// p13b-categories.jsx — Category management screen (P13b).
//
// SHIPPING design = full-feature (Full variant), confirmed as the single screen:
//   · search input + 전체 N개 count (no 내/자동 tabs or sections)
//   · each row: drag-handle · CatIcon · label(ellipsis) · seed-icon+count · ⋯ menu · ›
//   · tap row → P03(all seeds) filtered to that category (from:'P13b')
//   · ⋯ menu = 카테고리 수정 / 카테고리 삭제 (no multi-select / merge / FAB)
//   · 우상단 + button creates · delete shows 4s undo toast
//   · screen states: default / empty / searchEmpty / loading / offline / error
//
// minimal / guided remain defined ONLY so the comparison canvas
// (categories-canvas.html) can still show the earlier explorations
// side-by-side. They are NOT reachable as app states anymore.
//   minimal  — flat list, swipe-style trailing × delete, tap label = inline rename.
//   guided   — sectioned (내 / 자동), per-row usage badge, ⋯ menu, undo toast.
//
// Local in-memory store so the prototype can demo adds/edits without persistence.
// (Real app would lift this to a server-backed store with optimistic UI.)
//
// Composed by ScreenCategories({state}) — Full is default; state now means a
// data/connection state (not a variant). URL:
//   ?screen=P13b                          → Full, default
//   ?screen=P13b&state=empty|searchEmpty|loading|offline|error → Full screen-states
//   ?screen=P13b&state=minimal|guided     → comparison-only variants (canvas)

// ──────────────────────────────────────────────────────────────
// Shared store: working copy of categories that survives across
// the 3 variants within one app session so users can see the
// same demo data while comparing.
// ──────────────────────────────────────────────────────────────
window.__cpCats = window.__cpCats || null;
function useCatStore() {
  const seed = window.__cpCats || window.MOCK_CATEGORIES.slice();
  const [cats, _setCats] = React.useState(seed);
  const setCats = (next) => { window.__cpCats = next; _setCats(next); };
  return [cats, setCats];
}

// ──────────────────────────────────────────────────────────────
// CatIcon — renders emoji OR a 2-char monogram tile if icon is null.
// Used everywhere a category needs a visual.
// ──────────────────────────────────────────────────────────────
function CatIcon({ icon, label, size = 36, accent }) {
  const T = window.T;
  const fg = accent || T.accent;
  const bg = T.accentSoft;
  if (icon) {
    return (
      <div style={{
        width: size, height: size, borderRadius: size * 0.32,
        background: bg,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontSize: size * 0.5, lineHeight: 1, flexShrink: 0,
      }}>{icon}</div>
    );
  }
  const mono = window.monogramOf(label);
  // Slightly smaller font for 2-char monograms
  const fontSize = mono.length >= 2 ? size * 0.36 : size * 0.5;
  return (
    <div style={{
      width: size, height: size, borderRadius: size * 0.32,
      background: bg, color: fg,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontSize: fontSize, lineHeight: 1, fontWeight: 800,
      letterSpacing: '-0.04em', flexShrink: 0,
    }}>{mono}</div>
  );
}

// ──────────────────────────────────────────────────────────────
// EmojiPickerInline — compact swatch grid used in NewCategoryForm.
// `value=null` shows the monogram preview tile as the selected option.
// ──────────────────────────────────────────────────────────────
function EmojiPickerInline({ value, onChange, label }) {
  const T = window.T;
  const palette = window.MOCK_CAT_EMOJI;
  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                    marginBottom: 8 }}>
        <div style={{ fontSize: 11.5, fontWeight: 700, color: T.ink3,
                      letterSpacing: '0.08em', textTransform: 'uppercase' }}>아이콘</div>
        <div style={{ fontSize: 11, color: T.ink3 }}>
          {value ? '탭하면 해제' : '안 고르면 글자로 표시돼요'}
        </div>
      </div>
      <div style={{
        display: 'grid',
        gridTemplateColumns: 'repeat(8, 1fr)',
        gap: 4,
      }}>
        {/* "None" tile = monogram preview */}
        <button onClick={() => onChange(null)} style={{
          aspectRatio: '1/1', borderRadius: 10,
          background: value === null ? T.accent : T.surface,
          color: value === null ? '#FFF' : T.ink2,
          border: value === null ? 'none' : `0.5px solid ${T.hairlineStrong}`,
          fontFamily: 'inherit', fontSize: 13, fontWeight: 700,
          letterSpacing: '-0.04em', cursor: 'pointer',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>{label?.trim() ? window.monogramOf(label) : '글자'}</button>
        {palette.map(e => {
          const on = e === value;
          return (
            <button key={e} onClick={() => onChange(on ? null : e)} style={{
              aspectRatio: '1/1', borderRadius: 10,
              background: on ? T.accentSoft : 'transparent',
              border: on ? `1.5px solid ${T.accent}` : `0.5px solid transparent`,
              fontFamily: 'inherit', fontSize: 18, lineHeight: 1,
              cursor: 'pointer',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}>{e}</button>
          );
        })}
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// NewCategoryForm — text + emoji picker + 추가 button.
// Used inside NewCategorySheet AND inline in the Minimal variant.
// ──────────────────────────────────────────────────────────────
function NewCategoryForm({ initial, onSubmit, onCancel, submitLabel = '추가' }) {
  const T = window.T;
  const [label, setLabel] = React.useState(initial?.label || '');
  const [icon, setIcon] = React.useState(initial?.icon ?? null);
  const can = label.trim().length > 0;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      {/* Live preview */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 12,
        background: T.surface, borderRadius: 14,
        border: `0.5px solid ${T.hairline}`,
        padding: 12,
      }}>
        <CatIcon icon={icon} label={label || '미리보기'} size={44}/>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 10.5, fontWeight: 700, color: T.ink3,
                        letterSpacing: '0.08em', textTransform: 'uppercase',
                        marginBottom: 3 }}>미리보기</div>
          <div style={{ fontSize: 15, fontWeight: 700, color: T.ink,
                        letterSpacing: '-0.015em',
                        overflow: 'hidden', textOverflow: 'ellipsis',
                        whiteSpace: 'nowrap' }}>
            {label.trim() || '카테고리 이름'}
          </div>
        </div>
      </div>

      {/* Label input */}
      <div>
        <div style={{ fontSize: 11.5, fontWeight: 700, color: T.ink3,
                      letterSpacing: '0.08em', textTransform: 'uppercase',
                      marginBottom: 8 }}>이름</div>
        <input
          value={label} onChange={e => setLabel(e.target.value.slice(0, 16))}
          placeholder="예: 도쿄 출장, 카페 탐방"
          autoFocus
          maxLength={16}
          style={{
            width: '100%', height: 48,
            background: T.surface, color: T.ink,
            border: `0.5px solid ${T.hairlineStrong}`,
            borderRadius: 12, padding: '0 14px',
            fontSize: 15, fontFamily: 'inherit',
            letterSpacing: '-0.01em', outline: 'none',
          }}
        />
        <div style={{ display: 'flex', justifyContent: 'space-between',
                      fontSize: 11, color: T.ink3, marginTop: 6 }}>
          <span>한글·영문·이모지 모두 가능</span>
          <span>{label.length}/16</span>
        </div>
      </div>

      <EmojiPickerInline value={icon} onChange={setIcon} label={label}/>

      <div style={{ display: 'flex', gap: 8, marginTop: 4 }}>
        {onCancel && (
          <Button kind="ghost" onClick={onCancel} style={{ flex: 1 }}>취소</Button>
        )}
        <Button kind="primary" disabled={!can}
                onClick={() => onSubmit({ label: label.trim(), icon })}
                style={{ flex: onCancel ? 2 : undefined, width: onCancel ? undefined : '100%' }}>
          {submitLabel}
        </Button>
      </div>
    </div>
  );
}

// Sheet wrapper for NewCategoryForm
function NewCategorySheet({ open, onClose, onCreate, initial, title }) {
  return (
    <BottomSheet open={open} onClose={onClose} title={title || '새 카테고리'}>
      <div style={{ padding: '4px 20px 24px' }}>
        <NewCategoryForm initial={initial} onCancel={onClose} onSubmit={(v) => {
          onCreate(v); onClose();
        }} submitLabel={initial ? '저장' : '추가'}/>
      </div>
    </BottomSheet>
  );
}

// Utility to create a new category with stable id
function makeNewCat(label, icon) {
  const id = 'u_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
  const today = new Date().toISOString().slice(0, 10);
  return { id, label, icon, kind: 'user', createdAt: today };
}

// ══════════════════════════════════════════════════════════════
// VARIANT A — Minimal
// ══════════════════════════════════════════════════════════════
// Flat list. One column. Tap label = inline rename. Trailing × = delete.
// FAB at bottom = new. No counts, no sections, no menus.
// ══════════════════════════════════════════════════════════════
function ScreenCategoriesMinimal({ onNavigate }) {
  const T = window.T;
  const [cats, setCats] = useCatStore();
  const [editingId, setEditingId] = React.useState(null);
  const [confirmDel, setConfirmDel] = React.useState(null); // cat or null
  const [showNew, setShowNew] = React.useState(false);

  const startRename = (id) => setEditingId(id);
  const commitRename = (id, newLabel) => {
    setCats(cats.map(c => c.id === id ? { ...c, label: newLabel.trim() || c.label } : c));
    setEditingId(null);
  };
  const delConfirmed = () => {
    setCats(cats.filter(c => c.id !== confirmDel.id));
    setConfirmDel(null);
  };
  const create = ({ label, icon }) => setCats([makeNewCat(label, icon), ...cats]);

  const usageOf = (id) => window.getCategoryUsage(id);

  return (
    <div style={{ position: 'absolute', inset: 0, background: T.bg,
                  display: 'flex', flexDirection: 'column' }}>
      <TopBar title="카테고리"
        leading={<IconButton icon={<Icons.Back size={22} color={T.ink}/>}
                             onClick={() => onNavigate('back')}/>}/>

      <div style={{ flex: 1, overflowY: 'auto', padding: '4px 16px 100px' }}>
        {cats.length === 0 ? (
          <EmptyState
            icon={<div style={{
              width: 64, height: 64, borderRadius: 18,
              background: T.accentSoft, display: 'flex',
              alignItems: 'center', justifyContent: 'center',
              fontSize: 30,
            }}>🏷</div>}
            title="아직 카테고리가 없어요"
            message="씨앗을 분류하면 나중에 찾기 쉬워요"
            action={<Button kind="primary" onClick={() => setShowNew(true)}
                            icon={<Icons.Plus size={16} color="#FFF"/>}>
              첫 카테고리 만들기
            </Button>}
          />
        ) : (
          <div style={{
            background: T.surface, borderRadius: 14,
            border: `0.5px solid ${T.hairline}`,
            overflow: 'hidden',
          }}>
            {cats.map((c, i) => {
              const isEditing = editingId === c.id;
              return (
                <div key={c.id} style={{
                  display: 'flex', alignItems: 'center', gap: 12,
                  padding: '10px 14px', minHeight: 60,
                  borderBottom: i === cats.length - 1 ? 'none' : `0.5px solid ${T.hairline}`,
                }}>
                  <CatIcon icon={c.icon} label={c.label} size={38}/>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    {isEditing ? (
                      <input
                        defaultValue={c.label}
                        autoFocus
                        onBlur={(e) => commitRename(c.id, e.target.value)}
                        onKeyDown={(e) => {
                          if (e.key === 'Enter')  { e.target.blur(); }
                          if (e.key === 'Escape') { setEditingId(null); }
                        }}
                        style={{
                          width: '100%', background: 'transparent',
                          border: `1.5px solid ${T.accent}`,
                          borderRadius: 8, padding: '6px 8px',
                          fontSize: 15, fontWeight: 600, color: T.ink,
                          letterSpacing: '-0.01em',
                          fontFamily: 'inherit', outline: 'none',
                        }}
                      />
                    ) : (
                      <button onClick={() => startRename(c.id)} style={{
                        background: 'transparent', border: 'none', padding: 0,
                        fontFamily: 'inherit', cursor: 'pointer',
                        textAlign: 'left', display: 'block', width: '100%',
                      }}>
                        <div style={{ fontSize: 15, fontWeight: 600, color: T.ink,
                                      letterSpacing: '-0.01em',
                                      overflow: 'hidden', textOverflow: 'ellipsis',
                                      whiteSpace: 'nowrap' }}>{c.label}</div>
                      </button>
                    )}
                  </div>
                  {!isEditing && (
                    <IconButton icon={<Icons.Close size={16} color={T.ink3}/>}
                                onClick={() => setConfirmDel(c)} size={32}/>
                  )}
                </div>
              );
            })}
          </div>
        )}
      </div>

      {/* FAB */}
      {cats.length > 0 && (
        <button onClick={() => setShowNew(true)} style={{
          position: 'absolute', right: 20, bottom: 28, zIndex: 10,
          width: 56, height: 56, borderRadius: 28,
          background: T.accent, border: 'none', cursor: 'pointer',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          boxShadow: '0 10px 24px rgba(139,111,71,0.4)',
        }}>
          <Icons.Plus size={24} color="#FFF" strokeWidth={2.2}/>
        </button>
      )}

      <NewCategorySheet open={showNew} onClose={() => setShowNew(false)} onCreate={create}/>

      <AlertModal
        open={!!confirmDel}
        title={confirmDel ? `"${confirmDel.label}" 삭제할까요?` : ''}
        message={confirmDel
          ? (usageOf(confirmDel.id) > 0
              ? `씨앗 ${usageOf(confirmDel.id)}개에서 분류가 사라져요. 씨앗 자체는 그대로예요.`
              : '되돌릴 수 없어요.')
          : ''}
        confirmLabel="삭제" danger
        onConfirm={delConfirmed}
        onCancel={() => setConfirmDel(null)}
      />
    </div>
  );
}

// ══════════════════════════════════════════════════════════════
// VARIANT B — Guided
// ══════════════════════════════════════════════════════════════
// Sectioned: "내 카테고리" + "자동 추천". Per-row usage badge.
// Inline rename, ⋯ menu (rename / 아이콘 / 삭제), undo toast.
// Header info card on first visit; sticky [+ 새로 만들기] in TopBar.
// ══════════════════════════════════════════════════════════════
function ScreenCategoriesGuided({ onNavigate }) {
  const T = window.T;
  const [cats, setCats] = useCatStore();
  const [showNew, setShowNew] = React.useState(false);
  const [editCat, setEditCat] = React.useState(null);   // for icon/full edit sheet
  const [menuFor, setMenuFor] = React.useState(null);   // bottom sheet ⋯ menu
  const [confirmDel, setConfirmDel] = React.useState(null);
  const [toast, setToast] = React.useState(null);       // { msg, undo? }
  const [renamingId, setRenamingId] = React.useState(null);

  React.useEffect(() => {
    if (!toast) return;
    const id = setTimeout(() => setToast(null), 4000);
    return () => clearTimeout(id);
  }, [toast]);

  const userCats = cats.filter(c => c.kind === 'user');
  const autoCats = cats.filter(c => c.kind === 'auto');
  const usageOf = (id) => window.getCategoryUsage(id);

  const create = ({ label, icon }) => {
    const c = makeNewCat(label, icon);
    setCats([c, ...cats]);
    setToast({ msg: `"${c.label}" 카테고리를 만들었어요` });
  };
  const updateCat = (id, patch) => {
    setCats(cats.map(c => c.id === id ? { ...c, ...patch } : c));
  };
  const doDelete = (cat) => {
    setCats(cats.filter(c => c.id !== cat.id));
    setConfirmDel(null);
    setMenuFor(null);
    // Allow undo within 4s
    setToast({
      msg: `"${cat.label}" 삭제됨`,
      undo: () => {
        setCats(prev => prev.find(c => c.id === cat.id) ? prev : [cat, ...prev]);
        setToast(null);
      },
    });
  };

  return (
    <div style={{ position: 'absolute', inset: 0, background: T.bg,
                  display: 'flex', flexDirection: 'column' }}>
      <TopBar title="카테고리"
        leading={<IconButton icon={<Icons.Back size={22} color={T.ink}/>}
                             onClick={() => onNavigate('back')}/>}
        trailing={
          <button onClick={() => setShowNew(true)} style={{
            background: 'transparent', border: 'none',
            color: T.accent, fontFamily: 'inherit',
            fontSize: 14, fontWeight: 700, padding: '6px 8px',
            cursor: 'pointer', letterSpacing: '-0.01em',
            display: 'inline-flex', alignItems: 'center', gap: 4, whiteSpace: 'nowrap',
          }}>
            <Icons.Plus size={15} color={T.accent} strokeWidth={2.2}/> 새로 만들기
          </button>
        }/>

      <div style={{ flex: 1, overflowY: 'auto', padding: '4px 16px 24px' }}>
        {/* Guidance card (only when there are no user cats) */}
        {userCats.length === 0 && cats.length > 0 && (
          <div style={{
            background: T.accentSoft,
            border: `0.5px solid ${T.hairlineStrong}`,
            borderRadius: 14, padding: '14px 16px',
            marginBottom: 18, display: 'flex', gap: 12,
          }}>
            <div style={{
              width: 32, height: 32, borderRadius: 10,
              background: T.accent, color: '#FFF', flexShrink: 0,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}>
              <Icons.Sparkle size={18} color="#FFF"/>
            </div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13.5, fontWeight: 700, color: T.ink,
                            letterSpacing: '-0.01em', marginBottom: 4 }}>
                AI가 새 씨앗을 자동으로 분류해요
              </div>
              <div style={{ fontSize: 12.5, color: T.ink2, lineHeight: 1.5 }}>
                마음에 안 들면 언제든 바꿀 수 있어요.<br/>
                여행·테마별로 직접 만들면 더 편해요.
              </div>
            </div>
          </div>
        )}

        {cats.length === 0 ? (
          <EmptyState
            icon={<div style={{ width: 110, height: 75 }}>
              <Hamster count={0} max={10} size={110}/>
            </div>}
            title="아직 카테고리가 없어요"
            message="첫 씨앗을 담으면 자동으로 분류가 생기거나, 직접 만들 수도 있어요"
            action={<Button kind="primary" onClick={() => setShowNew(true)}
                            icon={<Icons.Plus size={16} color="#FFF"/>}>
              카테고리 만들기
            </Button>}
          />
        ) : (
          <React.Fragment>
            {/* 내 카테고리 */}
            <GuidedSection
              title="내 카테고리"
              subtitle={userCats.length === 0 ? '아직 없어요 — 우측 상단 + 새로 만들기' : null}
              cats={userCats}
              onRename={(c) => { setRenamingId(c.id); setMenuFor(null); }}
              renamingId={renamingId}
              onCommitRename={(id, label) => { updateCat(id, { label }); setRenamingId(null); }}
              onCancelRename={() => setRenamingId(null)}
              onMenu={(c) => setMenuFor(c)}
              usageOf={usageOf}
            />
            {/* 자동 추천 */}
            <GuidedSection
              title="자동 추천"
              icon={<Icons.Sparkle size={12} color={T.ink3}/>}
              subtitle={`AI가 씨앗 내용을 보고 만든 분류 ${autoCats.length}개`}
              cats={autoCats}
              onRename={(c) => { setRenamingId(c.id); setMenuFor(null); }}
              renamingId={renamingId}
              onCommitRename={(id, label) => { updateCat(id, { label }); setRenamingId(null); }}
              onCancelRename={() => setRenamingId(null)}
              onMenu={(c) => setMenuFor(c)}
              usageOf={usageOf}
            />
          </React.Fragment>
        )}
      </div>

      {/* New category sheet */}
      <NewCategorySheet open={showNew} onClose={() => setShowNew(false)} onCreate={create}/>

      {/* Edit (icon + label) sheet */}
      <NewCategorySheet open={!!editCat} onClose={() => setEditCat(null)}
                        title="카테고리 편집"
                        initial={editCat}
                        onCreate={(v) => updateCat(editCat.id, v)}/>

      {/* ⋯ menu sheet */}
      <BottomSheet open={!!menuFor} onClose={() => setMenuFor(null)}>
        {menuFor && (
          <div style={{ padding: '6px 16px 22px' }}>
            <div style={{
              display: 'flex', alignItems: 'center', gap: 12,
              padding: '8px 4px 14px',
              borderBottom: `0.5px solid ${T.hairline}`,
              marginBottom: 10,
            }}>
              <CatIcon icon={menuFor.icon} label={menuFor.label} size={42}/>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 15, fontWeight: 700, color: T.ink,
                              letterSpacing: '-0.015em' }}>{menuFor.label}</div>
                <div style={{ fontSize: 12, color: T.ink3, marginTop: 2 }}>
                  씨앗 {usageOf(menuFor.id)}개 · {menuFor.kind === 'user' ? '내가 만듦' : 'AI 추천'}
                </div>
              </div>
            </div>
            <div style={{
              background: T.surface, borderRadius: 14,
              border: `0.5px solid ${T.hairline}`,
              overflow: 'hidden', marginBottom: 10,
            }}>
              <ListRow label="이름 바꾸기"
                       leading={<Icons.Edit size={20} color={T.ink2}/>}
                       trailing={<Icons.ChevronR size={18} color={T.ink3}/>}
                       onClick={() => { setRenamingId(menuFor.id); setMenuFor(null); }}/>
              <ListRow label="아이콘·이름 모두 편집"
                       leading={<Icons.Sparkle size={20} color={T.ink2}/>}
                       trailing={<Icons.ChevronR size={18} color={T.ink3}/>}
                       onClick={() => { setEditCat(menuFor); setMenuFor(null); }} last/>
            </div>
            <div style={{
              background: T.surface, borderRadius: 14,
              border: `0.5px solid ${T.hairline}`,
              overflow: 'hidden',
            }}>
              <ListRow label="삭제" danger
                       leading={<Icons.Trash size={20} color={T.danger}/>}
                       onClick={() => setConfirmDel(menuFor)} last/>
            </div>
          </div>
        )}
      </BottomSheet>

      <AlertModal
        open={!!confirmDel}
        title={confirmDel ? `"${confirmDel.label}" 삭제할까요?` : ''}
        message={confirmDel
          ? (usageOf(confirmDel.id) > 0
              ? `씨앗 ${usageOf(confirmDel.id)}개에서 이 분류가 사라져요. 씨앗 자체는 그대로예요.`
              : '되돌릴 수 없어요.')
          : ''}
        confirmLabel="삭제" danger
        onConfirm={() => doDelete(confirmDel)}
        onCancel={() => setConfirmDel(null)}
      />

      {toast && (
        <Toast message={toast.msg}
               action={toast.undo ? '되돌리기' : null}
               onAction={toast.undo}/>
      )}
    </div>
  );
}

function GuidedSection({ title, subtitle, icon, cats, onRename, renamingId,
                         onCommitRename, onCancelRename, onMenu, usageOf }) {
  const T = window.T;
  if (cats.length === 0 && !subtitle) return null;
  return (
    <div style={{ marginBottom: 22 }}>
      <div style={{ padding: '0 4px 8px',
                    display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontSize: 11.5, fontWeight: 700, color: T.ink3,
                       letterSpacing: '0.08em', textTransform: 'uppercase' }}>
          {title}
        </span>
        {icon}
        <span style={{ fontSize: 11.5, color: T.ink3, fontWeight: 600,
                       marginLeft: 'auto' }}>{cats.length}</span>
      </div>
      {cats.length === 0 ? (
        <div style={{
          padding: '14px 16px',
          background: T.surface, borderRadius: 14,
          border: `0.5px dashed ${T.hairlineStrong}`,
          fontSize: 12.5, color: T.ink3, lineHeight: 1.5, textAlign: 'center',
        }}>{subtitle}</div>
      ) : (
        <div style={{
          background: T.surface, borderRadius: 14,
          border: `0.5px solid ${T.hairline}`,
          overflow: 'hidden',
        }}>
          {cats.map((c, i) => {
            const isRenaming = renamingId === c.id;
            const n = usageOf(c.id);
            return (
              <div key={c.id} style={{
                display: 'flex', alignItems: 'center', gap: 12,
                padding: '10px 8px 10px 14px', minHeight: 60,
                borderBottom: i === cats.length - 1 ? 'none' : `0.5px solid ${T.hairline}`,
              }}>
                <CatIcon icon={c.icon} label={c.label} size={38}/>
                <div style={{ flex: 1, minWidth: 0 }}>
                  {isRenaming ? (
                    <input
                      defaultValue={c.label}
                      autoFocus maxLength={16}
                      onBlur={(e) => onCommitRename(c.id, e.target.value || c.label)}
                      onKeyDown={(e) => {
                        if (e.key === 'Enter') e.target.blur();
                        if (e.key === 'Escape') onCancelRename();
                      }}
                      style={{
                        width: '100%', background: 'transparent',
                        border: `1.5px solid ${T.accent}`,
                        borderRadius: 8, padding: '6px 8px',
                        fontSize: 14.5, fontWeight: 600, color: T.ink,
                        fontFamily: 'inherit', outline: 'none',
                      }}/>
                  ) : (
                    <button onClick={() => onRename(c)} style={{
                      background: 'transparent', border: 'none', padding: 0,
                      fontFamily: 'inherit', cursor: 'pointer',
                      textAlign: 'left', display: 'block', width: '100%',
                    }}>
                      <div style={{ fontSize: 14.5, fontWeight: 600, color: T.ink,
                                    letterSpacing: '-0.01em',
                                    overflow: 'hidden', textOverflow: 'ellipsis',
                                    whiteSpace: 'nowrap' }}>{c.label}</div>
                      <div style={{ fontSize: 11.5, color: T.ink3, marginTop: 2 }}>
                        {n === 0 ? '아직 쓰이지 않음' : `씨앗 ${n}개`}
                      </div>
                    </button>
                  )}
                </div>
                {!isRenaming && (
                  <IconButton icon={<Icons.More size={18} color={T.ink3}/>}
                              onClick={() => onMenu(c)} size={36}/>
                )}
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

// ══════════════════════════════════════════════════════════════
// VARIANT C — Full-feature
// ══════════════════════════════════════════════════════════════
// VARIANT C — Full-feature  ★ SHIPPING DESIGN ★
// Search input. 전체 N개 count (no 내/자동 split). Drag handle to reorder.
// Tap row → P03 filtered by this category. ⋯ menu = 수정 / 삭제.
// Each row: CatIcon · label(ellipsis) · 씨앗 icon+count · ⋯ · ›.
// screenState: default | empty | searchEmpty | loading | offline | error.
// ══════════════════════════════════════════════════════════════
function ScreenCategoriesFull({ onNavigate, screenState = 'default' }) {
  const T = window.T;
  const [cats, setCats] = useCatStore();
  const isLoading  = screenState === 'loading';
  const isError    = screenState === 'error';
  const isOffline  = screenState === 'offline';
  const forceEmpty = screenState === 'empty';
  const [q, setQ] = React.useState(screenState === 'searchEmpty' ? '운동' : '');
  const [showNew, setShowNew] = React.useState(false);
  const [editCat, setEditCat] = React.useState(null);
  const [menuFor, setMenuFor] = React.useState(null);
  const [confirmDel, setConfirmDel] = React.useState(null);
  const [toast, setToast] = React.useState(null);
  const [dragIdx, setDragIdx] = React.useState(null);
  const usageOf = (id) => window.getCategoryUsage(id);

  React.useEffect(() => {
    if (!toast) return;
    const id = setTimeout(() => setToast(null), 4000);
    return () => clearTimeout(id);
  }, [toast]);

  const displayCats = forceEmpty ? [] : cats;
  const filtered = displayCats.filter(c =>
    !q.trim() || c.label.toLowerCase().includes(q.toLowerCase())
  );

  const doDelete = (cat) => {
    setCats(cats.filter(c => c.id !== cat.id));
    setConfirmDel(null);
    setMenuFor(null);
    setToast({
      msg: `"${cat.label}" 삭제됨`,
      undo: () => {
        setCats(prev => prev.find(c => c.id === cat.id) ? prev : [cat, ...prev]);
        setToast(null);
      },
    });
  };

  // Drag reorder — simple HTML5 DnD on the filtered slice.
  const onDragStart = (idx) => setDragIdx(idx);
  const onDragOver  = (e) => { e.preventDefault(); };
  const onDrop = (idx) => {
    if (dragIdx === null || dragIdx === idx) { setDragIdx(null); return; }
    // Reorder within `cats` using ids from filtered view
    const fromId = filtered[dragIdx].id;
    const toId   = filtered[idx].id;
    const next = cats.slice();
    const fromPos = next.findIndex(c => c.id === fromId);
    const [moved] = next.splice(fromPos, 1);
    const toPos = next.findIndex(c => c.id === toId);
    next.splice(toPos, 0, moved);
    setCats(next);
    setDragIdx(null);
  };

  const create = ({ label, icon }) => {
    const c = makeNewCat(label, icon);
    setCats([c, ...cats]);
    setToast({ msg: `"${c.label}" 카테고리를 만들었어요` });
  };

  return (
    <div style={{ position: 'absolute', inset: 0, background: T.bg,
                  display: 'flex', flexDirection: 'column' }}>
      <TopBar
        title="카테고리"
        offline={isOffline}
        leading={<IconButton icon={<Icons.Back size={22} color={T.ink}/>} onClick={() => onNavigate('back')}/>}
        trailing={<IconButton icon={<Icons.Plus size={22} color={isOffline ? T.ink3 : T.accent} strokeWidth={2.2}/>}
                              disabled={isOffline}
                              onClick={() => !isOffline && setShowNew(true)}/>}
      />

      {isOffline && (
        <div style={{ padding: '0 16px 10px' }}>
          <div style={{
            background: T.dangerSoft, borderRadius: 12, padding: '11px 14px',
            fontSize: 12.5, color: T.danger, lineHeight: 1.5, fontWeight: 600,
          }}>
            오프라인이라 새 카테고리 추가·수정·정렬은 잠시 멈춰요. 목록은 그대로 볼 수 있어요
          </div>
        </div>
      )}

      <div style={{ padding: '0 16px 10px' }}>
        {/* Search */}
        <div style={{
          display: 'flex', alignItems: 'center', gap: 8,
          background: T.surface, border: `0.5px solid ${T.hairlineStrong}`,
          borderRadius: 12, padding: '0 12px', height: 40, marginBottom: 10,
        }}>
          <Icons.Search size={16} color={T.ink3}/>
          <input value={q} onChange={e => setQ(e.target.value)}
                 placeholder="카테고리 검색"
                 style={{
                   flex: 1, border: 'none', outline: 'none',
                   background: 'transparent', color: T.ink,
                   fontSize: 14, fontFamily: 'inherit',
                 }}/>
          {q && <IconButton icon={<Icons.Close size={14} color={T.ink3}/>}
                            onClick={() => setQ('')} size={26}/>}
        </div>
        {/* Total count */}
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 5, padding: '2px 2px 0', whiteSpace: 'nowrap' }}>
          <span style={{ fontSize: 13, fontWeight: 700, color: T.ink2, letterSpacing: '-0.01em', whiteSpace: 'nowrap' }}>전체</span>
          <span style={{ fontSize: 13, fontWeight: 700, color: T.accent, whiteSpace: 'nowrap' }}>{isLoading ? '—' : `${displayCats.length}개`}</span>
        </div>
      </div>

      <div style={{ flex: 1, overflowY: 'auto', padding: '6px 16px 28px' }}>
        {isLoading ? (
          <div style={{
            background: T.surface, borderRadius: 14,
            border: `0.5px solid ${T.hairline}`, overflow: 'hidden',
          }}>
            {[0,1,2,3,4].map(i => (
              <div key={i} style={{
                display: 'flex', alignItems: 'center', gap: 10,
                padding: '10px 6px 10px 6px', minHeight: 68,
                borderBottom: i === 4 ? 'none' : `0.5px solid ${T.hairline}`,
              }}>
                <div style={{ width: 22, flexShrink: 0 }}/>
                <div style={{ width: 38, height: 38, borderRadius: 11,
                              background: T.inkOverlaySoft, flexShrink: 0,
                              animation: 'cp-skeleton 1.2s ease-in-out infinite' }}/>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ width: `${55 - i*6}%`, height: 13, borderRadius: 6,
                                background: T.inkOverlaySoft,
                                animation: 'cp-skeleton 1.2s ease-in-out infinite' }}/>
                  <div style={{ width: 52, height: 11, borderRadius: 6, marginTop: 8,
                                background: T.inkOverlaySoft,
                                animation: 'cp-skeleton 1.2s ease-in-out infinite' }}/>
                </div>
              </div>
            ))}
          </div>
        ) : isError ? (
          <EmptyState
            icon={<div style={{
              width: 56, height: 56, borderRadius: 14,
              background: T.dangerSoft,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}><Icons.Warning size={24} color={T.danger}/></div>}
            title="카테고리를 불러오지 못했어요"
            message="잠깐 문제가 생겼어요. 잠시 후 다시 시도해주세요"
            action={<Button kind="soft" size="sm"
                            onClick={() => onNavigate('P13b')}>다시 시도</Button>}
          />
        ) : filtered.length === 0 ? (
          <EmptyState
            icon={<div style={{
              width: 56, height: 56, borderRadius: 14,
              background: T.inkOverlaySoft,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}>{q ? <Icons.Search size={24} color={T.ink3}/> : <Icons.Seed size={24} color={T.ink3}/>}</div>}
            title={q ? `"${q}"에 해당하는 카테고리가 없어요` : '아직 카테고리가 없어요'}
            message={q ? '검색어를 바꿔보세요' : '씨앗을 분류하면 나중에 찾기 쉬워요. + 으로 만들어보세요'}
            action={q ? null : (
              <Button kind="primary" size="sm" disabled={isOffline}
                      icon={<Icons.Plus size={15} color="#FFF"/>}
                      onClick={() => !isOffline && setShowNew(true)}>카테고리 만들기</Button>
            )}
          />
        ) : (
          <div style={{
            background: T.surface, borderRadius: 14,
            border: `0.5px solid ${T.hairline}`,
            overflow: 'hidden',
          }}>
            {filtered.map((c, i) => {
              const n = usageOf(c.id);
              return (
                <div key={c.id}
                  draggable={!isOffline}
                  onDragStart={() => onDragStart(i)}
                  onDragOver={onDragOver}
                  onDrop={() => onDrop(i)}
                  onClick={() => onNavigate('P03', { cat: c.id, catLabel: c.label, from: 'P13b' })}
                  style={{
                    display: 'flex', alignItems: 'center', gap: 10,
                    padding: '10px 6px 10px 6px', minHeight: 68,
                    borderBottom: i === filtered.length - 1 ? 'none' : `0.5px solid ${T.hairline}`,
                    cursor: 'pointer',
                    opacity: dragIdx === i ? 0.4 : 1,
                  }}>
                  {/* Drag handle */}
                  {!isOffline && (
                    <div
                      onClick={(e) => e.stopPropagation()}
                      style={{
                        width: 22, color: T.ink3, fontSize: 16, fontWeight: 800,
                        lineHeight: 0.6, letterSpacing: '-0.05em', flexShrink: 0,
                        textAlign: 'center', cursor: 'grab', userSelect: 'none',
                      }}>⋮⋮</div>
                  )}
                  <CatIcon icon={c.icon} label={c.label} size={38}/>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 14.5, fontWeight: 600, color: T.ink,
                                  letterSpacing: '-0.01em',
                                  overflow: 'hidden', textOverflow: 'ellipsis',
                                  whiteSpace: 'nowrap' }}>{c.label}</div>
                    {/* Seed count */}
                    <div style={{ display: 'flex', alignItems: 'center', gap: 4, marginTop: 5 }}>
                      <Icons.Seed size={13} color={n > 0 ? T.accent : T.ink3} filled={n > 0}/>
                      <span style={{ fontSize: 12.5, fontWeight: 700, lineHeight: 1,
                                     color: n > 0 ? T.ink2 : T.ink3, whiteSpace: 'nowrap' }}>
                        {n > 0 ? `씨앗 ${n}` : '비어 있음'}
                      </span>
                    </div>
                  </div>
                  {/* Edit / delete menu */}
                  {!isOffline && (
                    <IconButton icon={<Icons.More size={18} color={T.ink3}/>}
                                onClick={(e) => { e.stopPropagation(); setMenuFor(c); }}
                                size={36}/>
                  )}
                  <Icons.ChevronR size={16} color={T.ink3}/>
                </div>
              );
            })}
          </div>
        )}
        {filtered.length > 0 && (
          <div style={{ fontSize: 11.5, color: T.ink3, padding: '12px 4px 0',
                        lineHeight: 1.5, textAlign: 'center' }}>
            {isOffline
              ? '카테고리를 누르면 그 씨앗만 모아 볼 수 있어요'
              : '카테고리를 누르면 그 씨앗만 모아 볼 수 있어요 · 길게 눌러 순서 변경'}
          </div>
        )}
      </div>

      {/* New category sheet */}
      <NewCategorySheet open={showNew} onClose={() => setShowNew(false)} onCreate={create}/>

      {/* Edit (icon + label) sheet */}
      <NewCategorySheet open={!!editCat} onClose={() => setEditCat(null)}
                        title="카테고리 편집" initial={editCat}
                        onCreate={(v) => setCats(cats.map(c => c.id === editCat.id ? { ...c, ...v } : c))}/>

      {/* ⋯ menu sheet — 수정 / 삭제 */}
      <BottomSheet open={!!menuFor} onClose={() => setMenuFor(null)}>
        {menuFor && (
          <div style={{ padding: '6px 16px 22px' }}>
            <div style={{
              display: 'flex', alignItems: 'center', gap: 12,
              padding: '8px 4px 14px',
              borderBottom: `0.5px solid ${T.hairline}`,
              marginBottom: 10,
            }}>
              <CatIcon icon={menuFor.icon} label={menuFor.label} size={42}/>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 15, fontWeight: 700, color: T.ink,
                              letterSpacing: '-0.015em' }}>{menuFor.label}</div>
                <div style={{ fontSize: 12, color: T.ink3, marginTop: 2 }}>
                  씨앗 {usageOf(menuFor.id)}개
                </div>
              </div>
            </div>
            <div style={{
              background: T.surface, borderRadius: 14,
              border: `0.5px solid ${T.hairline}`,
              overflow: 'hidden', marginBottom: 10,
            }}>
              <ListRow label="카테고리 수정"
                       leading={<Icons.Edit size={20} color={T.ink2}/>}
                       trailing={<Icons.ChevronR size={18} color={T.ink3}/>}
                       onClick={() => { setEditCat(menuFor); setMenuFor(null); }} last/>
            </div>
            <div style={{
              background: T.surface, borderRadius: 14,
              border: `0.5px solid ${T.hairline}`,
              overflow: 'hidden',
            }}>
              <ListRow label="카테고리 삭제" danger
                       leading={<Icons.Trash size={20} color={T.danger}/>}
                       onClick={() => setConfirmDel(menuFor)} last/>
            </div>
          </div>
        )}
      </BottomSheet>

      <AlertModal
        open={!!confirmDel}
        title={confirmDel ? `"${confirmDel.label}" 삭제할까요?` : ''}
        message={confirmDel
          ? (usageOf(confirmDel.id) > 0
              ? `씨앗 ${usageOf(confirmDel.id)}개에서 이 분류가 사라져요. 씨앗 자체는 그대로예요.`
              : '되돌릴 수 없어요.')
          : ''}
        confirmLabel="삭제" danger
        onConfirm={() => doDelete(confirmDel)}
        onCancel={() => setConfirmDel(null)}
      />

      {toast && (
        <Toast message={toast.msg}
               action={toast.undo ? '되돌리기' : null}
               onAction={toast.undo}/>
      )}
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// Router — picks the variant based on `state` prop.
// Default: guided.
// ──────────────────────────────────────────────────────────────
function ScreenCategories({ state, onNavigate }) {
  switch (state) {
    case 'minimal': return <ScreenCategoriesMinimal onNavigate={onNavigate}/>;
    case 'guided':  return <ScreenCategoriesGuided onNavigate={onNavigate}/>;
    default:        return <ScreenCategoriesFull key={state || 'default'} onNavigate={onNavigate} screenState={state || 'default'}/>;
  }
}

Object.assign(window, {
  ScreenCategories, ScreenCategoriesMinimal,
  ScreenCategoriesGuided, ScreenCategoriesFull,
  CatIcon, NewCategoryForm, NewCategorySheet, makeNewCat,
});
