/* 아리 — 작업(Tasks) · RON 디자인 언어
   · 칸반 / 리스트 뷰 (Asana 스타일)
   · 항목 클릭 → 우측 절반 드로어 · 자식 노드를 무한 중첩으로 추가 */
const { useState, useEffect, useRef } = React;
const TD = window.AriTasks;
const PPL = TD.people;
const PRJ = TD.projects;

/* ---------- 아이콘 ---------- */
const P = {
  spark: "M12 3l1.9 5.2L19 10l-5.1 1.8L12 17l-1.9-5.2L5 10l5.1-1.8z|M19 4v3|M5 17v3",
  grid: "M3 3h7v7H3z|M14 3h7v7h-7z|M14 14h7v7h-7z|M3 14h7v7H3z",
  route: "M6 19a3 3 0 1 0 0-6 3 3 0 0 0 0 6z|M18 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6z|M9 16h6a3 3 0 0 0 3-3V11",
  cal: "M8 2v4M16 2v4M3 10h18M5 4h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z",
  check: "M9 11l3 3L22 4|M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11",
  tick: "M20 6 9 17l-5-5",
  mail: "M22 7 13.03 12.7a1.94 1.94 0 0 1-2.06 0L2 7|M4 4h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z",
  heart: "M19 14c1.5-1.5 3-3.4 3-5.5A4.5 4.5 0 0 0 12 5.5 4.5 4.5 0 0 0 2 8.5c0 2.1 1.5 4 3 5.5l7 7z",
  list: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01",
  inbox: "M22 12h-6l-2 3h-4l-2-3H2|M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z",
  search: "M21 21l-4.34-4.34|M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16z",
  bell: "M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9|M10.3 21a1.94 1.94 0 0 0 3.4 0",
  sun: "M12 17a5 5 0 1 0 0-10 5 5 0 0 0 0 10z|M12 1v2M12 21v2M4.2 4.2l1.4 1.4M18.4 18.4l1.4 1.4M1 12h2M21 12h2M4.2 19.8l1.4-1.4M18.4 5.6l1.4-1.4",
  moon: "M21 12.8A9 9 0 1 1 11.2 3 7 7 0 0 0 21 12.8z",
  plus: "M12 5v14M5 12h14",
  x: "M18 6 6 18M6 6l12 12",
  more: "M5 12a1 1 0 1 0 2 0 1 1 0 0 0-2 0zM11 12a1 1 0 1 0 2 0 1 1 0 0 0-2 0zM17 12a1 1 0 1 0 2 0 1 1 0 0 0-2 0z",
  chev: "M9 6l6 6-6 6",
  star: "M12 2.5l2.9 5.9 6.5.9-4.7 4.6 1.1 6.5L12 18.8 6.2 21l1.1-6.5L2.6 9.8l6.5-.9z",
  flag: "M4 22V4M4 4s1.5-1 5-1 5 2 8 2 4-1 4-1v10s-1.5 1-4 1-5-2-8-2-5 1-5 1",
  zap: "M13 2 3 14h9l-1 8 10-12h-9z",
  back: "M19 12H5|M12 19l-7-7 7-7",
  user: "M20 21a8 8 0 0 0-16 0|M12 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z",
  file: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z|M14 2v6h6M9 13h6M9 17h4",
  branch: "M6 3v12|M18 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6zM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 6a3 3 0 1 0 0-6 3 3 0 0 0 0 6z|M15 12H9a3 3 0 0 0-3 3",
  trash: "M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",
  hash: "M4 9h16M4 15h16M10 3L8 21M16 3l-2 18",
  layers: "M12 2 2 7l10 5 10-5z|M2 17l10 5 10-5|M2 12l10 5 10-5",
};
function I({ d, w }) {
  return (
    <svg className="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor"
      strokeWidth={w || 1.9} strokeLinecap="round" strokeLinejoin="round">
      {P[d].split("|").map((s, i) => <path key={i} d={s} />)}
    </svg>
  );
}

const NAV = [
  { id: "dash", label: "대시보드", icon: "grid", href: "아리 대시보드 (RON).html" },
  { id: "journey", label: "여정", icon: "route", href: "아리 대시보드 (RON v2).html" },
  { id: "cal", label: "일정", icon: "cal", href: "#" },
  { id: "task", label: "작업", icon: "check", href: "#" },
  { id: "mail", label: "메일", icon: "mail", href: "#" },
  { id: "life", label: "라이프", icon: "heart", href: "#" },
];
const VIEWS = [
  { id: "kanban", label: "칸반", icon: "grid" },
  { id: "list", label: "리스트", icon: "list" },
];
const FILTERS = [
  { id: "all", icon: "inbox", label: "전체 작업" },
  { id: "active", icon: "zap", label: "진행 중" },
  { id: "star", icon: "star", label: "즐겨찾기" },
  { id: "done", icon: "check", label: "완료" },
];
const STATUS = {
  todo: { label: "할 일", c: "var(--faint)" },
  doing: { label: "진행 중", c: "var(--blue-d)" },
  review: { label: "검토", c: "var(--red)" },
  done: { label: "완료", c: "var(--teal)" },
};

/* ---------- localStorage ---------- */
const load = (k, fb) => { try { const v = localStorage.getItem("ariTR." + k); return v == null ? fb : JSON.parse(v); } catch (e) { return fb; } };
const save = (k, v) => { try { localStorage.setItem("ariTR." + k, JSON.stringify(v)); } catch (e) {} };

/* ---------- 트리 헬퍼 (불변) ---------- */
function findPath(nodes, id, trail) {
  trail = trail || [];
  for (const n of nodes) {
    const t = trail.concat(n);
    if (n.id === id) return t;
    if (n.children && n.children.length) { const r = findPath(n.children, id, t); if (r) return r; }
  }
  return null;
}
const getNode = (nodes, id) => { const p = findPath(nodes, id); return p ? p[p.length - 1] : null; };
function editTree(nodes, id, fn) {
  return nodes.map((n) => {
    if (n.id === id) return fn(n);
    if (n.children && n.children.length) return { ...n, children: editTree(n.children, id, fn) };
    return n;
  });
}
function removeFromTree(nodes, id) {
  return nodes.filter((n) => n.id !== id).map((n) =>
    n.children && n.children.length ? { ...n, children: removeFromTree(n.children, id) } : n);
}
const stat = (node) => { const c = node.children || []; return { total: c.length, done: c.filter((x) => x.status === "done").length }; };
const pct = (node) => { const s = stat(node); return s.total ? Math.round((s.done / s.total) * 100) : 0; };
const dueDay = (d) => (d ? parseInt(d.split("-")[1], 10) : 99);
const dueLabel = (d) => { if (!d) return "—"; const [m, x] = d.split("-"); return `${+m}/${+x}`; };
const TODAY = 9; // 6월 9일

function Av({ who }) { const p = PPL[who]; if (!p) return null; return <div className="av" style={{ background: p.c }} title={p.name}>{p.initial}</div>; }
function Ptag({ id }) { const p = PRJ[id]; if (!p) return null; return (<span className="ptag"><span className="pdot" style={{ background: p.color }} />{p.name}</span>); }

/* ===================== 칸반 ===================== */
function Card({ task, onOpen, onDrag, onDragEnd, dragging }) {
  const s = stat(task);
  const soon = task.status !== "done" && task.due && dueDay(task.due) <= TODAY + 1;
  return (
    <div className={"tcard" + (dragging ? " dragging" : "") + (task.status === "done" ? " done" : "")}
      draggable onDragStart={onDrag} onDragEnd={onDragEnd} onClick={() => onOpen(task.id)}>
      <div className="tcard-top">
        <Ptag id={task.project} />
        <button className="kmore" onClick={(e) => { e.stopPropagation(); onOpen(task.id); }} aria-label="열기"><I d="more" /></button>
      </div>
      <div className="tcard-title">{task.title}</div>
      {s.total > 0 && (
        <div className="miniprog">
          <span className="bar"><span style={{ width: pct(task) + "%" }} /></span>
          <span className="n"><I d="branch" /> {s.done}/{s.total}</span>
        </div>
      )}
      <div className="tcard-foot">
        <span className={"due" + (soon ? " soon" : "")}><I d="cal" /> {dueLabel(task.due)}</span>
        <span className={"prio prio-" + task.prio}><I d="flag" /> {task.prio}</span>
        <Av who={task.assignee} />
      </div>
    </div>
  );
}
function Kanban({ tasks, onOpen, onMove, onAdd }) {
  const [dragId, setDragId] = useState(null);
  const [over, setOver] = useState(null);
  const [addCol, setAddCol] = useState(null);
  const [text, setText] = useState("");
  const submit = (st) => { if (text.trim()) onAdd(st, text.trim()); setText(""); setAddCol(null); };
  return (
    <div className="kboard">
      {TD.columns.map((col) => {
        const items = tasks.filter((t) => t.status === col.id);
        return (
          <div key={col.id} className={"kcol" + (over === col.id ? " over" : "")}
            onDragOver={(e) => { e.preventDefault(); if (over !== col.id) setOver(col.id); }}
            onDragLeave={(e) => { if (e.currentTarget === e.target) setOver(null); }}
            onDrop={(e) => { e.preventDefault(); if (dragId) onMove(dragId, col.id); setDragId(null); setOver(null); }}>
            <div className="kcol-head">
              <span className="kdot" style={{ background: col.accent }} />
              <span className="kt">{col.label}</span>
              <span className="kn">{items.length}</span>
              <button className="kadd" onClick={() => { setAddCol(col.id); setText(""); }} aria-label="추가"><I d="plus" /></button>
            </div>
            <div className="kcol-body">
              {addCol === col.id && (
                <input className="sub-input" autoFocus value={text} placeholder="작업 입력 후 Enter…"
                  onChange={(e) => setText(e.target.value)}
                  onKeyDown={(e) => { if (e.key === "Enter") submit(col.id); if (e.key === "Escape") setAddCol(null); }}
                  onBlur={() => submit(col.id)} />
              )}
              {items.map((t) => (
                <Card key={t.id} task={t} onOpen={onOpen} dragging={dragId === t.id}
                  onDrag={(e) => { setDragId(t.id); e.dataTransfer.effectAllowed = "move"; }}
                  onDragEnd={() => { setDragId(null); setOver(null); }} />
              ))}
              {items.length === 0 && addCol !== col.id && <div className="kcol-empty">여기로 옮겨보세요</div>}
            </div>
          </div>
        );
      })}
    </div>
  );
}

/* ===================== 리스트 ===================== */
function ListView({ tasks, onOpen }) {
  const order = { todo: 0, doing: 1, review: 2, done: 3 };
  const sorted = [...tasks].sort((a, b) => order[a.status] - order[b.status] || dueDay(a.due) - dueDay(b.due));
  return (
    <div className="lst">
      <div className="lst-head">
        <div>작업</div>
        <div className="hide-sm">프로젝트</div>
        <div className="c">담당</div>
        <div>마감</div>
        <div className="hide-md">우선순위</div>
        <div className="hide-sm">하위</div>
      </div>
      {sorted.map((t) => {
        const s = stat(t), sm = STATUS[t.status];
        const soon = t.status !== "done" && t.due && dueDay(t.due) <= TODAY + 1;
        return (
          <div key={t.id} className={"lrow" + (t.status === "done" ? " done" : "")} onClick={() => onOpen(t.id)}>
            <div className="lt">
              <span className="lstatus" style={{ background: sm.c }} />
              <span className="lt-title">{t.title}{s.total > 0 && <span className="subcount">{s.done}/{s.total}</span>}</span>
            </div>
            <div className="hide-sm"><Ptag id={t.project} /></div>
            <div className="c"><Av who={t.assignee} /></div>
            <div><span className={"due" + (soon ? " soon" : "")}><I d="cal" /> {dueLabel(t.due)}</span></div>
            <div className="hide-md"><span className={"prio prio-" + t.prio}><I d="flag" /> {t.prio}</span></div>
            <div className="hide-sm">
              {s.total > 0
                ? <span className="miniprog"><span className="bar"><span style={{ width: pct(t) + "%" }} /></span></span>
                : <span style={{ color: "var(--faint)", fontFamily: "var(--font-mono)", fontSize: 12 }}>—</span>}
            </div>
          </div>
        );
      })}
    </div>
  );
}

/* ===================== 하위 작업 행 (재귀) ===================== */
function SubRow({ node, expanded, onExpand, onCheck, onFocus }) {
  const has = !!(node.children && node.children.length);
  const open = !!expanded[node.id];
  const s = stat(node);
  const done = node.status === "done";
  return (
    <>
      <div className={"subrow" + (done ? " done" : "")} onClick={() => onFocus(node.id)}>
        {has ? (
          <button className={"twist" + (open ? " open" : "")} onClick={(e) => { e.stopPropagation(); onExpand(node.id); }} aria-label="펼치기"><I d="chev" /></button>
        ) : <span className="twist-spacer" />}
        <div className={"scheck" + (done ? " done" : "")} onClick={(e) => { e.stopPropagation(); onCheck(node.id); }}><I d="tick" w="3" /></div>
        <span className="stitle">{node.title}</span>
        <div className="smeta">
          {has && <span className="schips"><I d="branch" /> {s.done}/{s.total}</span>}
          {node.due && <span className="schips"><I d="cal" /> {dueLabel(node.due)}</span>}
          <Av who={node.assignee} />
          <button className="sdrill" onClick={(e) => { e.stopPropagation(); onFocus(node.id); }} aria-label="열기"><I d="chev" /></button>
        </div>
      </div>
      {has && open && (
        <div className="subkids">
          {node.children.map((c) => (
            <SubRow key={c.id} node={c} expanded={expanded} onExpand={onExpand} onCheck={onCheck} onFocus={onFocus} />
          ))}
        </div>
      )}
    </>
  );
}

/* 자동 높이 제목 입력 */
function TitleInput({ value, done, onChange }) {
  const ref = useRef(null);
  useEffect(() => { const el = ref.current; if (el) { el.style.height = "auto"; el.style.height = el.scrollHeight + "px"; } }, [value]);
  return <textarea ref={ref} className={"dtitle" + (done ? " done" : "")} rows={1} value={value}
    onChange={(e) => onChange(e.target.value)} spellCheck={false} />;
}

/* ===================== 우측 드로어 ===================== */
function Drawer({ tasks, focusId, rootId, onFocus, onClose, onCheck, onAddChild, onField, onDelete }) {
  const [expanded, setExpanded] = useState({});
  const [adding, setAdding] = useState(false);
  const [subText, setSubText] = useState("");
  const [editDesc, setEditDesc] = useState(false);
  const [descText, setDescText] = useState("");
  const [menu, setMenu] = useState(false);

  const path = findPath(tasks, focusId) || [];
  const node = path[path.length - 1];
  useEffect(() => { setAdding(false); setEditDesc(false); setMenu(false); }, [focusId]);
  useEffect(() => {
    const h = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", h); return () => window.removeEventListener("keydown", h);
  }, [onClose]);
  if (!node) return null;

  // 루트 작업부터의 경로만 (보드 위로는 올라가지 않음)
  const rootIdx = path.findIndex((n) => n.id === rootId);
  const crumbs = rootIdx >= 0 ? path.slice(rootIdx) : path;
  const s = stat(node);
  const person = PPL[node.assignee];
  const sm = STATUS[node.status];
  const done = node.status === "done";
  const onExpand = (id) => setExpanded((e) => ({ ...e, [id]: !e[id] }));
  const submitSub = () => { if (subText.trim()) onAddChild(node.id, subText.trim()); setSubText(""); setAdding(false); };

  return (
    <>
      <div className="drawer-scrim" onClick={onClose} />
      <aside className="drawer" role="dialog" aria-label="작업 상세">
        {/* 헤더 — 브레드크럼 */}
        <div className="dh">
          <button className="dh-up" onClick={() => crumbs.length > 1 ? onFocus(crumbs[crumbs.length - 2].id) : onClose()} aria-label="위로"><I d="back" /></button>
          <div className="dh-crumbs">
            {crumbs.map((c, i) => (
              <React.Fragment key={c.id}>
                {i > 0 && <span className="csep"><I d="chev" /></span>}
                {i === crumbs.length - 1
                  ? <span className="cz cur">{c.title}</span>
                  : <button className="cz" onClick={() => onFocus(c.id)}>{c.title}</button>}
              </React.Fragment>
            ))}
          </div>
          <button className="dh-x" onClick={onClose} aria-label="닫기"><I d="x" /></button>
        </div>

        <div className="dbody">
          {/* 제목 + 완료 체크 */}
          <div className="dtitle-row">
            <div className={"dcheck" + (done ? " done" : "")} onClick={() => onCheck(node.id)} role="checkbox" aria-checked={done}><I d="tick" w="3" /></div>
            <TitleInput value={node.title} done={done} onChange={(v) => onField(node.id, { title: v })} />
          </div>

          {/* 메타 */}
          <div className="dmeta">
            <div className="dmi">
              <div className="ml"><I d="user" /> 담당자</div>
              <div className="dmv"><Av who={node.assignee} /> {person.name}{person.me ? " (나)" : ""}</div>
            </div>
            <div className="dmi">
              <div className="ml"><I d="cal" /> 마감일</div>
              <div className="dmv">{node.due ? `6월 ${dueDay(node.due)}일` : "미정"}</div>
            </div>
            <div className="dmi">
              <div className="ml"><I d="flag" /> 우선순위</div>
              <div className={"dmv prio-" + node.prio}><I d="flag" /> {node.prio}</div>
            </div>
            <div className="dmi">
              <div className="ml"><I d="list" /> 상태</div>
              <div className="status-wrap">
                <button className="status-pill" onClick={() => setMenu((o) => !o)}>
                  <span className="pdot" style={{ background: sm.c }} /> {sm.label} <I d="chev" />
                </button>
                {menu && (
                  <div className="status-menu">
                    {TD.columns.map((c) => (
                      <button key={c.id} onClick={() => { onField(node.id, { status: c.id }); setMenu(false); }}>
                        <span className="pdot" style={{ background: STATUS[c.id].c }} /> {c.label}
                      </button>
                    ))}
                  </div>
                )}
              </div>
            </div>
          </div>

          {/* 설명 */}
          <div className="dlabel"><I d="file" /> 설명<span className="dline" /></div>
          {editDesc ? (
            <textarea className="ddesc-edit" autoFocus value={descText}
              onChange={(e) => setDescText(e.target.value)}
              onBlur={() => { onField(node.id, { desc: descText }); setEditDesc(false); }} />
          ) : (
            <div className={"ddesc" + (node.desc ? "" : " empty")} onClick={() => { setDescText(node.desc || ""); setEditDesc(true); }}>
              {node.desc || "설명이 아직 없어요. 클릭해 작성하세요."}
            </div>
          )}

          {/* 하위 작업 (무한 중첩) */}
          <div className="dlabel"><I d="branch" /> 하위 작업 <span className="cnt">{s.done}/{s.total}</span><span className="dline" /></div>
          {s.total > 0 ? (
            <div className="subtree">
              {node.children.map((c) => (
                <SubRow key={c.id} node={c} expanded={expanded} onExpand={onExpand} onCheck={onCheck} onFocus={onFocus} />
              ))}
            </div>
          ) : !adding && (
            <div className="subempty"><I d="branch" /> 아직 하위 작업이 없어요</div>
          )}
          {adding ? (
            <input className="sub-input" autoFocus value={subText} placeholder="하위 작업 입력 후 Enter…"
              onChange={(e) => setSubText(e.target.value)}
              onKeyDown={(e) => { if (e.key === "Enter") submitSub(); if (e.key === "Escape") setAdding(false); }}
              onBlur={submitSub} />
          ) : (
            <button className="sub-add" onClick={() => setAdding(true)}><I d="plus" /> 하위 작업 추가</button>
          )}
        </div>

        {/* 푸터 */}
        <div className="dfoot">
          <button className="btn danger" onClick={() => onDelete(node.id)} aria-label="삭제"><I d="trash" /></button>
          {crumbs.length > 1 && (
            <button className="btn ghost" onClick={() => onFocus(crumbs[crumbs.length - 2].id)}><I d="back" /> 상위로</button>
          )}
          <button className="btn primary" onClick={onClose}><I d="check" /> 완료</button>
        </div>
      </aside>
    </>
  );
}

/* ===================== 앱 ===================== */
function App() {
  const [theme, setTheme] = useState(() => load("theme", "light"));
  const [view, setView] = useState(() => load("view", "kanban"));
  const [filter, setFilter] = useState(() => load("filter", "all"));
  const [projOrder, setProjOrder] = useState(() => {
    const stored = load("projects", null);
    if (stored) Object.keys(stored).forEach((k) => { PRJ[k] = stored[k]; });
    return load("projOrder", Object.keys(PRJ));
  });
  const [q, setQ] = useState("");
  const [tasks, setTasks] = useState(() => load("tasks", TD.tree));
  const [openRoot, setOpenRoot] = useState(null); // 드로어를 연 최상위 작업 id
  const [focusId, setFocusId] = useState(null);    // 현재 보고 있는 노드 id
  const nextId = useRef(1000);
  const ref = useRef(null);

  useEffect(() => { document.documentElement.setAttribute("data-theme", theme); save("theme", theme); }, [theme]);
  useEffect(() => save("view", view), [view]);
  useEffect(() => save("filter", filter), [filter]);
  useEffect(() => save("projOrder", projOrder), [projOrder]);
  useEffect(() => save("tasks", tasks), [tasks]);
  useEffect(() => { const el = ref.current; const id = requestAnimationFrame(() => el && el.classList.add("enter")); return () => cancelAnimationFrame(id); }, []);

  const uid = () => "u" + nextId.current++;
  const open = (id) => { setOpenRoot(id); setFocusId(id); };
  const close = () => { setOpenRoot(null); setFocusId(null); };

  const onCheck = (id) => setTasks((ts) => editTree(ts, id, (n) => ({ ...n, status: n.status === "done" ? "todo" : "done" })));
  const onField = (id, patch) => setTasks((ts) => editTree(ts, id, (n) => ({ ...n, ...patch })));
  const onAddChild = (parentId, title) => {
    const parent = getNode(tasks, parentId);
    const child = { id: uid(), title, status: "todo", assignee: parent ? parent.assignee : "jiwoo", due: "", prio: "보통", project: parent ? parent.project : "me", desc: "", star: false, children: [] };
    setTasks((ts) => editTree(ts, parentId, (n) => ({ ...n, children: [...(n.children || []), child] })));
  };
  const onMove = (id, status) => setTasks((ts) => ts.map((t) => (t.id === id ? { ...t, status } : t)));
  const onDelete = (id) => {
    if (id === openRoot) { setTasks((ts) => removeFromTree(ts, id)); close(); return; }
    const path = findPath(tasks, id);
    const parent = path && path.length > 1 ? path[path.length - 2] : null;
    setTasks((ts) => removeFromTree(ts, id));
    if (parent) setFocusId(parent.id);
  };
  const newTask = () => {
    const id = uid();
    const proj = filter.startsWith("prj:") ? filter.slice(4) : "me";
    const t = { id, title: "새 작업", status: "todo", assignee: "jiwoo", due: "", prio: "보통", project: proj, desc: "", star: false, children: [] };
    setTasks((ts) => [t, ...ts]);
    setView("kanban");
    setTimeout(() => open(id), 40);
  };
  const PRJ_COLORS = ["#ce6969", "#9c8bcb", "#5fa6a0", "#6286c9", "#c98a3e", "#5b8fb0", "#a86b9e"];
  const addProject = () => {
    const id = "p" + nextId.current++;
    PRJ[id] = { name: "새 프로젝트", color: PRJ_COLORS[projOrder.length % PRJ_COLORS.length] };
    save("projects", PRJ);
    setProjOrder((o) => [...o, id]);
    setFilter("prj:" + id);
  };
  const renameProject = (pid, name) => { if (PRJ[pid]) { PRJ[pid] = { ...PRJ[pid], name: name || "프로젝트" }; save("projects", PRJ); setProjOrder((o) => [...o]); } };

  // 최상위 필터 + 검색
  const match = (t) => {
    if (filter.startsWith("prj:")) return t.project === filter.slice(4);
    if (filter === "active") return t.status !== "done";
    if (filter === "done") return t.status === "done";
    if (filter === "star") return t.star;
    return true;
  };
  const shown = tasks.filter((t) => match(t) && (!q.trim() || t.title.toLowerCase().includes(q.trim().toLowerCase())));
  const activeCount = tasks.filter((t) => t.status !== "done").length;
  const doneCount = tasks.filter((t) => t.status === "done").length;
  const projCount = (pid) => tasks.filter((t) => t.project === pid && t.status !== "done").length;
  const isPrj = filter.startsWith("prj:");
  const curPrj = isPrj ? PRJ[filter.slice(4)] : null;
  const curFilter = FILTERS.find((f) => f.id === filter);
  const headTitle = isPrj ? (curPrj ? curPrj.name : "프로젝트") : (filter === "all" ? "작업" : curFilter.label);

  return (
    <div className="tpage app" ref={ref}>
      {/* 상단바 */}
      <header className="top">
        <div className="brand">
          <div className="brand-mark"><I d="spark" /></div>
          <span className="brand-name">아리</span>
        </div>
        <nav className="nav">
          {NAV.map((m) => (
            <a key={m.id} href={m.href} className={m.id === "task" ? "active" : ""}>
              <I d={m.icon} />{m.label}{m.id === "task" && <span className="badge">{activeCount}</span>}
            </a>
          ))}
        </nav>
        <div className="top-actions">
          <button className="circ" aria-label="검색"><I d="search" /></button>
          <button className="circ dot" aria-label="알림"><I d="bell" /></button>
          <button className="circ" aria-label="테마" onClick={() => setTheme(theme === "light" ? "dark" : "light")}><I d={theme === "light" ? "moon" : "sun"} /></button>
          <div className="avatar">{PPL.jiwoo.initial}</div>
        </div>
      </header>

      {/* 무대 */}
      <div className="stage">
        <aside className="tsidebar">
          <button className="sb-back" onClick={() => (window.location.href = "아리 대시보드 (RON v2).html")}>
            <I d="back" /> 대시보드
          </button>

          <div className="sb-label">보기</div>
          <div className="sb-sec">
            {FILTERS.map((f) => (
              <button key={f.id} className={"sb-item" + (filter === f.id ? " on" : "")} onClick={() => setFilter(f.id)}>
                <I d={f.icon} />
                <span className="sb-name">{f.label}</span>
                <span className="sb-count">
                  {f.id === "all" ? tasks.length : f.id === "active" ? activeCount : f.id === "done" ? doneCount : tasks.filter((t) => t.star).length}
                </span>
              </button>
            ))}
          </div>

          <div className="sb-divider" />

          <div className="sb-prj-head">
            <span className="sb-label">프로젝트</span>
            <button className="sb-addprj" onClick={addProject} aria-label="프로젝트 추가"><I d="plus" /></button>
          </div>
          <div className="sb-sec">
            {projOrder.map((pid) => {
              const p = PRJ[pid]; if (!p) return null;
              const key = "prj:" + pid;
              return (
                <button key={pid} className={"sb-item" + (filter === key ? " on" : "")} onClick={() => setFilter(key)}>
                  <span className="sb-dot" style={{ background: p.color }} />
                  <span className="sb-name">{p.name}</span>
                  <span className="sb-count">{projCount(pid)}</span>
                </button>
              );
            })}
          </div>
          <button className="sb-new" onClick={addProject}><I d="plus" /> 새 프로젝트</button>
        </aside>

        <main className="main">
          {/* 헤더 */}
          <div className="thead-row">
            <div className="thead-ic" style={curPrj ? { background: curPrj.color, color: "#fff" } : null}>
              <I d={isPrj ? "layers" : (curFilter ? curFilter.icon : "check")} />
            </div>
            <div className="thead-titles">
              {isPrj ? (
                <h1 key={filter} contentEditable suppressContentEditableWarning spellCheck={false}
                  onBlur={(e) => renameProject(filter.slice(4), e.target.textContent.trim())}
                  onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); e.target.blur(); } }}
                  style={{ outline: "none" }}>{headTitle}</h1>
              ) : <h1>{headTitle}</h1>}
              <p className="thead-sub">{shown.length}개 작업 · 진행 <b>{activeCount}</b> · 완료 <b>{doneCount}</b> · 항목을 열면 하위 작업을 무한히 만들 수 있어요</p>
            </div>
          </div>

          {/* 툴바 */}
          <div className="ttoolbar">
            <div className="view-seg">
              {VIEWS.map((v) => (
                <button key={v.id} className={view === v.id ? "on" : ""} onClick={() => setView(v.id)}><I d={v.icon} /> {v.label}</button>
              ))}
            </div>
            <div className="t-search"><I d="search" /><input value={q} onChange={(e) => setQ(e.target.value)} placeholder="작업 검색…" /></div>
            <button className="t-new" onClick={newTask}><I d="plus" /> 새 작업</button>
          </div>

          {/* 본문 */}
          {view === "kanban"
            ? <div className="panel" style={{ padding: "22px 20px" }}>
                <Kanban tasks={shown} onOpen={open} onMove={onMove} onAdd={(st, title) => {
                  const id = uid();
                  const proj = filter.startsWith("prj:") ? filter.slice(4) : "me";
                  setTasks((ts) => [{ id, title, status: st, assignee: "jiwoo", due: "", prio: "보통", project: proj, desc: "", star: false, children: [] }, ...ts]);
                }} />
              </div>
            : <div className="panel" style={{ padding: "20px" }}><ListView tasks={shown} onOpen={open} /></div>}
        </main>
      </div>

      {focusId && (
        <Drawer tasks={tasks} focusId={focusId} rootId={openRoot} onFocus={setFocusId} onClose={close}
          onCheck={onCheck} onAddChild={onAddChild} onField={onField} onDelete={onDelete} />
      )}
    </div>
  );
}

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