// ============================================
// Screens: Login, Menu, Staff, Work, Dept, Role, AuditLog
// ============================================

// Brand mark — generic lightning bolt (Saetta = lightning in Italian)
const BrandMark = ({ size = 22 }) => (
  <span className="brand-mark" style={{ width: size, height: size }}>
    <svg width={Math.round(size * 0.55)} height={Math.round(size * 0.6)} viewBox="0 0 11 14" fill="none">
      <path d="M6.5 0L0 8h3.5L2 14 11 5H7L8.5 0z" fill="currentColor" />
    </svg>
  </span>
);

// ============================================
// LoginScreen — MS Entra ID OIDC風
// ============================================
const LoginScreen = ({ onLogin }) => {
  const [loading, setLoading] = useState(false);
  const click = () => {
    setLoading(true);
    setTimeout(() => { onLogin(); }, 1100);
  };

  return (
    <div className="login-page">
      <div className="login-single">
        <div className="login-brand-top">
          <BrandMark size={40} />
        </div>

        <div className="login-card">
          <div style={{ textAlign: "center" }}>
            <h1 className="login-title">社内システム<br />共通マスター登録</h1>
            <p className="login-subtitle">
              組織アカウントでサインインしてください
            </p>
          </div>

          <button className="ms-button" onClick={click} disabled={loading} data-loading={loading || undefined}>
            {loading ? (
              <>
                <span className="spinner" />
                <span>Microsoft Entra ID で認証中…</span>
              </>
            ) : (
              <>
                <span className="ms-logo"><span /><span /><span /><span /></span>
                <span>Microsoft アカウントでサインイン</span>
              </>
            )}
          </button>

          <div className="login-divider">SSO 経由 — OpenID Connect</div>

          <div style={{ background: "var(--bg-subtle)", border: "1px solid var(--border)", borderRadius: 6, padding: 12, fontSize: 11.5, color: "var(--text-muted)", lineHeight: 1.6 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 4, color: "var(--text)" }}>
              <Icon name="info" size={13} />
              <strong>テナント: saetta.onmicrosoft.com</strong>
            </div>
            アクセスには組織アカウントが必要です。<br />
            お持ちでない場合は管理本部までお問い合わせください。
          </div>
        </div>

        <div className="login-meta">
          <span>© 2026 Saetta, Inc.</span>
          <a href="#">利用規約</a>
          <a href="#">プライバシー</a>
          <a href="#">ヘルプ</a>
        </div>
      </div>
    </div>
  );
};

// ============================================
// Sidebar nav
// ============================================
const Sidebar = ({ current, onNav, collapsed, currentUser, role, counts }) => {
  const NavItem = ({ id, label, icon, count }) => (
    <button className="nav-item" data-active={current === id ? "true" : undefined} onClick={() => onNav(id)}>
      <span className="nav-item-icon"><Icon name={icon} size={15} /></span>
      <span className="nav-item-label">{label}</span>
      {count != null && <span className="nav-item-count">{count}</span>}
    </button>
  );
  return (
    <aside className="sidebar">
      <div className="sidebar-brand">
        <BrandMark size={22} />
        <span className="brand-name">共通マスター</span>
      </div>

      <div className="sidebar-section">マスター</div>
      <div className="sidebar-nav">
        <NavItem id="menu"   label="ダッシュボード" icon="grid" />
        <NavItem id="staff"  label="スタッフ"       icon="users"    count={counts.staff} />
        <NavItem id="work"   label="作品"           icon="film"     count={counts.work} />
        <NavItem id="client" label="取引先"         icon="briefcase" count={counts.client} />
        <NavItem id="dept"   label="部署"           icon="building" count={counts.dept} />
        <NavItem id="role"   label="職種"           icon="tag"      count={counts.role} />
      </div>

      <div className="sidebar-section">システム設定</div>
      <div className="sidebar-nav">
        {role === "admin" && <NavItem id="users" label="アクセスユーザー" icon="shield" count={counts.users} />}
        <NavItem id="audit" label="変更履歴" icon="history" />
      </div>

      <div className="sidebar-footer">
        <div className="sidebar-user">
          <Avatar id={currentUser.id} name={initialsFor(currentUser)} />
          <div className="sidebar-user-info">
            <div className="sidebar-user-name">{currentUser.lastName} {currentUser.firstName}</div>
            <div className="sidebar-user-role">{role === "admin" ? "管理者" : role === "editor" ? "編集者" : "閲覧者"}</div>
          </div>
        </div>
      </div>
    </aside>
  );
};

// ============================================
// TopBar
// ============================================
const TopBar = ({ breadcrumb, actions, role, onRoleChange, onLogout }) => (
  <div className="topbar">
    <div className="breadcrumb">
      {breadcrumb.map((b, i) => (
        <React.Fragment key={i}>
          {i > 0 && <span className="crumb-sep"><Icon name="chevron-right" size={11} /></span>}
          <span className={i === breadcrumb.length - 1 ? "crumb-current" : undefined}>{b}</span>
        </React.Fragment>
      ))}
    </div>
    <div className="topbar-actions">
      {actions}
      <span className="toolbar-divider" />
      <DropdownMenu
        align="right"
        trigger={
          <span className="role-pill" data-role={role}>
            <span className="role-dot" />
            <span>{role === "admin" ? "管理者" : role === "editor" ? "編集者" : "閲覧者"}</span>
            <Icon name="chevron-down" size={10} />
          </span>
        }
        items={[
          { type: "label", label: "ロール切替（デモ用）" },
          { label: "管理者",   icon: "shield", onClick: () => onRoleChange("admin") },
          { label: "編集者",   icon: "edit",   onClick: () => onRoleChange("editor") },
          { label: "閲覧者",   icon: "lock",   onClick: () => onRoleChange("viewer") },
          { type: "divider" },
          { label: "サインアウト", icon: "logout", onClick: onLogout, danger: true },
        ]}
      />
    </div>
  </div>
);

// ============================================
// Menu / Dashboard
// ============================================
const MenuScreen = ({ onNav, staff, works, depts, roles, clients, log, currentUser, role }) => {
  const inProd = works.filter(w => w.status === "in_production").length;
  const planning = works.filter(w => w.status === "planning").length;
  const lastUpdated = log[0]?.ts.slice(0, 10) ?? "—";
  const todayChanges = log.filter(l => l.ts.startsWith("2026-05-18")).length;

  return (
    <div style={{ maxWidth: 1100 }}>
      <div style={{ marginBottom: 24 }}>
        <h1 style={{ fontSize: 22, fontWeight: 600, letterSpacing: "-0.02em", margin: 0 }}>
          こんにちは、{currentUser.lastName}さん
        </h1>
        <p style={{ color: "var(--text-subtle)", fontSize: 13, margin: "4px 0 0" }}>
          社内システム共通マスターの管理画面です。本日 {todayChanges} 件の更新があります。
        </p>
      </div>

      <div className="menu-grid">
        <button className="menu-card" onClick={() => onNav("staff")}>
          <div className="menu-card-head">
            <div className="menu-card-icon"><Icon name="users" size={18} /></div>
            <div style={{ flex: 1 }}>
              <h3 className="menu-card-title">スタッフマスター</h3>
              <p className="menu-card-desc">社員・契約スタッフの基本情報、所属、職種を管理</p>
            </div>
          </div>
          <div className="menu-card-stats">
            <div className="stat"><div className="stat-v">{staff.length}</div><div className="stat-l">登録人数</div></div>
            <div className="stat"><div className="stat-v">{staff.filter(s => s.status === "active").length}</div><div className="stat-l">在籍</div></div>
            <div className="stat"><div className="stat-v">{staff.filter(s => s.status === "leave").length}</div><div className="stat-l">休職</div></div>
          </div>
          <div className="menu-card-foot">
            <span>最終更新: {lastUpdated}</span>
            <span style={{ color: "var(--accent)", display: "inline-flex", alignItems: "center", gap: 4 }}>
              開く <Icon name="chevron-right" size={11} />
            </span>
          </div>
        </button>

        <button className="menu-card" onClick={() => onNav("work")}>
          <div className="menu-card-head">
            <div className="menu-card-icon"><Icon name="film" size={18} /></div>
            <div style={{ flex: 1 }}>
              <h3 className="menu-card-title">作品マスター</h3>
              <p className="menu-card-desc">制作中・完了済の作品コード、クライアントを管理</p>
            </div>
          </div>
          <div className="menu-card-stats">
            <div className="stat"><div className="stat-v">{works.length}</div><div className="stat-l">登録作品</div></div>
            <div className="stat"><div className="stat-v">{inProd}</div><div className="stat-l">制作中</div></div>
            <div className="stat"><div className="stat-v">{planning}</div><div className="stat-l">企画</div></div>
          </div>
          <div className="menu-card-foot">
            <span>最終更新: 2026-05-18</span>
            <span style={{ color: "var(--accent)", display: "inline-flex", alignItems: "center", gap: 4 }}>
              開く <Icon name="chevron-right" size={11} />
            </span>
          </div>
        </button>

        <button className="menu-card" onClick={() => onNav("client")}>
          <div className="menu-card-head">
            <div className="menu-card-icon"><Icon name="briefcase" size={18} /></div>
            <div style={{ flex: 1 }}>
              <h3 className="menu-card-title">取引先マスター</h3>
              <p className="menu-card-desc">作品クライアントの会社情報・担当窓口を管理</p>
            </div>
          </div>
          <div className="menu-card-stats">
            <div className="stat"><div className="stat-v">{clients.length}</div><div className="stat-l">登録社</div></div>
            <div className="stat"><div className="stat-v">{clients.filter(c => c.status === "active").length}</div><div className="stat-l">取引中</div></div>
          </div>
          <div className="menu-card-foot">
            <span>最終更新: 2026-05-17</span>
            <span style={{ color: "var(--accent)", display: "inline-flex", alignItems: "center", gap: 4 }}>
              開く <Icon name="chevron-right" size={11} />
            </span>
          </div>
        </button>

        <button className="menu-card" onClick={() => onNav("dept")}>
          <div className="menu-card-head">
            <div className="menu-card-icon"><Icon name="building" size={18} /></div>
            <div style={{ flex: 1 }}>
              <h3 className="menu-card-title">部署マスター</h3>
              <p className="menu-card-desc">組織の部署コードと所属長を管理</p>
            </div>
          </div>
          <div className="menu-card-stats">
            <div className="stat"><div className="stat-v">{depts.length}</div><div className="stat-l">部署数</div></div>
            <div className="stat"><div className="stat-v">{staff.length}</div><div className="stat-l">総人員</div></div>
          </div>
          <div className="menu-card-foot">
            <span>最終更新: 2026-05-17</span>
            <span style={{ color: "var(--accent)", display: "inline-flex", alignItems: "center", gap: 4 }}>
              開く <Icon name="chevron-right" size={11} />
            </span>
          </div>
        </button>

        <button className="menu-card" onClick={() => onNav("role")}>
          <div className="menu-card-head">
            <div className="menu-card-icon"><Icon name="tag" size={18} /></div>
            <div style={{ flex: 1 }}>
              <h3 className="menu-card-title">職種マスター</h3>
              <p className="menu-card-desc">職種コードと役職レベルを管理</p>
            </div>
          </div>
          <div className="menu-card-stats">
            <div className="stat"><div className="stat-v">{roles.length}</div><div className="stat-l">職種数</div></div>
            <div className="stat"><div className="stat-v">{new Set(roles.map(r => r.category)).size}</div><div className="stat-l">カテゴリ</div></div>
          </div>
          <div className="menu-card-foot">
            <span>最終更新: 2026-05-16</span>
            <span style={{ color: "var(--accent)", display: "inline-flex", alignItems: "center", gap: 4 }}>
              開く <Icon name="chevron-right" size={11} />
            </span>
          </div>
        </button>
      </div>

      <div style={{ marginTop: 32 }}>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 12 }}>
          <h2 style={{ fontSize: 13, fontWeight: 600, margin: 0, color: "var(--text-muted)", textTransform: "uppercase", letterSpacing: "0.06em" }}>
            最近の変更
          </h2>
          <button className="btn" data-variant="ghost" data-size="sm" onClick={() => onNav("audit")}>
            すべて見る <Icon name="chevron-right" size={11} />
          </button>
        </div>
        <div style={{ maxWidth: 880 }}>
          <RecentActivityList items={log.slice(0, 5)} />
        </div>
      </div>
    </div>
  );
};

const ENTITY_LABEL = {
  staff: "スタッフ", work: "作品", department: "部署", role: "職種", user: "ユーザー", client: "取引先",
};
const ENTITY_ICON = {
  staff: "users", work: "film", department: "building", role: "tag", user: "shield", client: "briefcase",
};

const RecentActivityList = ({ items }) => (
  <div style={{ background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 8, overflow: "hidden" }}>
    {items.map((l, i) => {
      const user = STAFF_BY_ID[l.user];
      const userName = user ? `${user.lastName} ${user.firstName}` : l.user;
      return (
        <div key={l.id} style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 14px", borderTop: i > 0 ? "1px solid var(--border)" : "none" }}>
          <Icon name={ENTITY_ICON[l.entity]} size={14} style={{ color: "var(--text-subtle)" }} />
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 12.5, lineHeight: 1.4 }}>
              <Badge tone={l.action === "create" ? "success" : l.action === "delete" ? "danger" : "info"}>
                {l.action === "create" ? "新規" : l.action === "delete" ? "削除" : "更新"}
              </Badge>{" "}
              <span style={{ color: "var(--text-muted)" }}>{ENTITY_LABEL[l.entity]}</span>{" "}
              <span style={{ fontFamily: "var(--font-mono)", fontSize: 11.5, color: "var(--text-subtle)" }}>{l.entityId}</span>{" "}
              <span>{l.after !== "—" && `→ ${l.after}`}</span>
            </div>
            <div style={{ fontSize: 11, color: "var(--text-faint)" }}>{userName} · {l.note || (l.field !== "—" ? `${l.field} を変更` : "—")}</div>
          </div>
          <div style={{ fontSize: 11, color: "var(--text-faint)", fontFamily: "var(--font-mono)" }}>{l.ts.slice(5, 16)}</div>
        </div>
      );
    })}
  </div>
);

// ============================================
// STAFF SCREEN
// ============================================
const StaffScreen = ({ staff, setStaff, depts, roles, role, addLog, openImport, onOpenDetail, externalEditing, clearExternalEditing }) => {
  const toast = useToast();
  const [q, setQ] = useState("");
  const [deptFilter, setDeptFilter] = useState("");
  const [roleFilter, setRoleFilter] = useState("");
  const [statusFilter, setStatusFilter] = useState("");
  const [sort, setSort] = useState({ key: "code", dir: "asc" });
  const [page, setPage] = useState(1);
  const [pageSize, setPageSize] = useState(25);
  const [selected, setSelected] = useState(new Set());
  const [editing, setEditing] = useState(null); // staff object or "new"
  const [confirmDelete, setConfirmDelete] = useState(null); // staff or {bulk: true}
  const canEdit = role !== "viewer";

  // Accept external edit trigger (from detail drawer in App)
  useEffect(() => {
    if (externalEditing) { setEditing(externalEditing); clearExternalEditing?.(); }
  }, [externalEditing]);

  const filtered = useMemo(() => {
    let r = staff;
    if (q) {
      const ql = q.toLowerCase();
      r = r.filter(s =>
        (`${s.lastName}${s.firstName}`).includes(q) ||
        (`${s.lastKana}${s.firstKana}`).includes(q) ||
        s.email.toLowerCase().includes(ql) ||
        s.code.toLowerCase().includes(ql) ||
        (s.roles || []).some(rid => ROLE_BY_ID[rid]?.name.includes(q))
      );
    }
    if (deptFilter)   r = r.filter(s => s.dept === deptFilter);
    if (roleFilter)   r = r.filter(s => (s.roles || []).includes(roleFilter));
    if (statusFilter) r = r.filter(s => s.status === statusFilter);

    r = [...r].sort((a, b) => {
      const dir = sort.dir === "asc" ? 1 : -1;
      const k = sort.key;
      let av, bv;
      if (k === "name")  { av = `${a.lastKana}${a.firstKana}`; bv = `${b.lastKana}${b.firstKana}`; }
      else if (k === "roles") { av = ROLE_BY_ID[(a.roles||[])[0]]?.name ?? ""; bv = ROLE_BY_ID[(b.roles||[])[0]]?.name ?? ""; }
      else { av = a[k] ?? ""; bv = b[k] ?? ""; }
      if (av < bv) return -1 * dir;
      if (av > bv) return  1 * dir;
      return 0;
    });
    return r;
  }, [staff, q, deptFilter, roleFilter, statusFilter, sort]);

  const paged = filtered.slice((page - 1) * pageSize, page * pageSize);
  useEffect(() => { setPage(1); }, [q, deptFilter, roleFilter, statusFilter]);

  const columns = [
    { key: "code",   label: "社員番号", width: 90,  sortable: true,
      render: r => <span className="mono">{r.code}</span> },
    { key: "name",   label: "氏名",     width: 200, sortable: true,
      render: r => <AvatarName staff={r} /> },
    { key: "dept",   label: "所属部署", width: 140, sortable: true,
      render: r => DEPT_BY_ID[r.dept]?.name ?? <span className="muted">—</span> },
    { key: "roles",  label: "職種", minWidth: 240, sortable: true,
      render: r => <RoleTags roleIds={r.roles} max={3} /> },
    { key: "email",  label: "メール",   minWidth: 200, sortable: true,
      render: r => <span style={{ color: "var(--text-muted)" }}>{r.email}</span> },
    { key: "status", label: "状態", width: 80, sortable: true,
      render: r => <Badge tone={STAFF_STATUS[r.status].tone} dot>{STAFF_STATUS[r.status].label}</Badge> },
    { key: "joined", label: "入社日", width: 100, sortable: true,
      render: r => <span className="mono">{r.joined}</span> },
  ];

  const handleSave = (data) => {
    if (data.id) {
      setStaff(staff.map(s => s.id === data.id ? data : s));
      addLog({ entity: "staff", entityId: data.id, action: "update", field: "—", before: "—", after: `${data.lastName} ${data.firstName}`, note: "編集" });
      toast(`${data.lastName} ${data.firstName} を更新しました`);
    } else {
      const next = { ...data, id: `S${String(staff.length + 31).padStart(3, "0")}` };
      setStaff([...staff, next]);
      addLog({ entity: "staff", entityId: next.id, action: "create", field: "—", before: "—", after: `${next.lastName} ${next.firstName}`, note: "新規スタッフ登録" });
      toast(`${next.lastName} ${next.firstName} を登録しました`);
    }
    setEditing(null);
  };

  const handleDelete = () => {
    if (confirmDelete?.bulk) {
      const ids = [...selected];
      setStaff(staff.filter(s => !selected.has(s.id)));
      ids.forEach(id => {
        const t = STAFF_BY_ID[id];
        addLog({ entity: "staff", entityId: id, action: "delete", field: "—", before: t ? `${t.lastName} ${t.firstName}` : id, after: "—", note: "一括削除" });
      });
      toast(`${ids.length}件を削除しました`);
      setSelected(new Set());
    } else if (confirmDelete) {
      setStaff(staff.filter(s => s.id !== confirmDelete.id));
      addLog({ entity: "staff", entityId: confirmDelete.id, action: "delete", field: "—", before: `${confirmDelete.lastName} ${confirmDelete.firstName}`, after: "—", note: "削除" });
      toast(`${confirmDelete.lastName} ${confirmDelete.firstName} を削除しました`);
    }
    setConfirmDelete(null);
  };

  const exportCSV = () => {
    downloadCSV("staff.csv", [
      { label: "社員番号", value: r => r.code },
      { label: "姓",       value: r => r.lastName },
      { label: "名",       value: r => r.firstName },
      { label: "姓カナ",   value: r => r.lastKana },
      { label: "名カナ",   value: r => r.firstKana },
      { label: "メール",   value: r => r.email },
      { label: "所属部署", value: r => DEPT_BY_ID[r.dept]?.name ?? "" },
      { label: "職種",     value: r => (r.roles || []).map(id => ROLE_BY_ID[id]?.name).filter(Boolean).join(" / ") },
      { label: "状態",     value: r => STAFF_STATUS[r.status].label },
      { label: "入社日",   value: r => r.joined },
      { label: "備考",     value: r => r.note },
    ], filtered);
    toast(`スタッフ${filtered.length}件をエクスポートしました`);
  };

  return (
    <>
      <div className="list-header">
        <div className="list-title-row">
          <h1 className="list-title">スタッフ</h1>
          <Badge>{filtered.length} 件</Badge>
          <div style={{ flex: 1 }} />
          <Button icon="upload"   onClick={openImport} disabled={!canEdit}>CSVインポート</Button>
          <Button icon="download" onClick={exportCSV}>CSVエクスポート</Button>
          <Button icon="plus" variant="accent" onClick={() => setEditing("new")} disabled={!canEdit}>新規スタッフ</Button>
        </div>

        <div className="list-toolbar">
          <div className="grow">
            <SearchInput value={q} onChange={setQ} placeholder="氏名・カナ・社員番号・メールで検索…" />
          </div>
          <div style={{ width: 160 }}>
            <Select
              value={deptFilter}
              onChange={setDeptFilter}
              placeholder="所属部署（すべて）"
              options={depts.map(d => ({ value: d.id, label: d.name }))}
            />
          </div>
          <div style={{ width: 160 }}>
            <Select
              value={roleFilter}
              onChange={setRoleFilter}
              placeholder="職種（すべて）"
              options={roles.map(r => ({ value: r.id, label: r.name }))}
            />
          </div>
          <div style={{ width: 130 }}>
            <Select
              value={statusFilter}
              onChange={setStatusFilter}
              placeholder="状態（すべて）"
              options={[
                { value: "active",   label: "在籍" },
                { value: "leave",    label: "休職" },
                { value: "inactive", label: "退職" },
              ]}
            />
          </div>
          {(q || deptFilter || roleFilter || statusFilter) && (
            <Button variant="ghost" size="sm" onClick={() => { setQ(""); setDeptFilter(""); setRoleFilter(""); setStatusFilter(""); }}>
              フィルター解除
            </Button>
          )}
        </div>

        {selected.size > 0 && (
          <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "6px 10px", background: "var(--accent-soft)", border: "1px solid var(--accent-border)", borderRadius: 6, fontSize: 12 }}>
            <span style={{ color: "var(--accent)", fontWeight: 500 }}>{selected.size}件を選択中</span>
            <span style={{ flex: 1 }} />
            <Button size="sm" variant="ghost" onClick={() => setSelected(new Set())}>解除</Button>
            <Button size="sm" icon="trash" variant="danger" onClick={() => setConfirmDelete({ bulk: true })} disabled={!canEdit}>削除</Button>
          </div>
        )}
      </div>

      <DataTable
        columns={columns}
        rows={paged}
        sort={sort}
        onSortChange={setSort}
        selected={selected}
        onSelectedChange={setSelected}
        onRowClick={(r) => onOpenDetail ? onOpenDetail(r.id) : (canEdit ? setEditing(r) : null)}
        rowActions={(r) => (
          <>
            <IconButton icon="edit"  title="編集" onClick={() => canEdit && setEditing(r)} />
            <IconButton icon="trash" title="削除" onClick={() => canEdit && setConfirmDelete(r)} />
          </>
        )}
      />

      <Pagination
        page={page} pageSize={pageSize} total={filtered.length}
        onPageChange={setPage} onPageSizeChange={setPageSize}
      />

      {editing && (
        <StaffEditModal
          staff={editing === "new" ? null : editing}
          depts={depts} roles={roles}
          onClose={() => setEditing(null)}
          onSave={handleSave}
        />
      )}
      <ConfirmDialog
        open={!!confirmDelete}
        title="スタッフを削除"
        message={confirmDelete?.bulk
          ? `選択中の ${selected.size} 件のスタッフを削除します。この操作は取り消せません。`
          : `「${confirmDelete?.lastName} ${confirmDelete?.firstName}」を削除します。この操作は取り消せません。`}
        onCancel={() => setConfirmDelete(null)}
        onConfirm={handleDelete}
      />
    </>
  );
};

// ============================================
// Staff Edit Modal
// ============================================
const StaffEditModal = ({ staff, depts, roles, onClose, onSave }) => {
  const [form, setForm] = useState(staff ?? {
    code: "", lastName: "", firstName: "", lastKana: "", firstKana: "",
    email: "", dept: depts[0]?.id || "", roles: [],
    status: "active", joined: new Date().toISOString().slice(0, 10), note: "",
  });
  const [errors, setErrors] = useState({});
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const handleSubmit = () => {
    const e = {};
    if (!form.lastName)  e.lastName = "必須項目です";
    if (!form.firstName) e.firstName = "必須項目です";
    if (!form.code)      e.code = "必須項目です";
    if (!form.email)     e.email = "必須項目です";
    else if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(form.email)) e.email = "メールアドレス形式が不正です";
    if (!form.roles || form.roles.length === 0) e.roles = "1つ以上の職種を選択してください";
    setErrors(e);
    if (Object.keys(e).length) return;
    onSave(form);
  };

  return (
    <Modal
      open
      onClose={onClose}
      size="lg"
      title={staff ? `スタッフ編集 · ${staff.lastName} ${staff.firstName}` : "新規スタッフ登録"}
      subtitle={staff ? `社員番号 ${staff.code}` : "氏名・所属を入力してください"}
      footer={
        <>
          <span className="grow text-xs faint">
            <Icon name="info" size={12} /> 変更はEntra IDの組織情報には反映されません
          </span>
          <Button onClick={onClose}>キャンセル</Button>
          <Button variant="accent" onClick={handleSubmit}>{staff ? "保存" : "登録"}</Button>
        </>
      }
    >
      {/* Photo + identity */}
      <div style={{ display: "flex", gap: 20, marginBottom: 18 }}>
        <div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 8 }}>
          <div className="photo-stripes" style={{ width: 88, height: 88, borderRadius: 8 }}>
            顔写真
          </div>
          <Button size="sm" variant="ghost" icon="upload">アップロード</Button>
        </div>
        <div style={{ flex: 1 }}>
          <div className="form-grid">
            <Field label="社員番号" required error={errors.code}>
              <Input value={form.code} onChange={v => set("code", v)} placeholder="A0001" />
            </Field>
            <Field label="状態">
              <Select
                value={form.status} onChange={v => set("status", v)}
                options={[
                  { value: "active",   label: "在籍" },
                  { value: "leave",    label: "休職" },
                  { value: "inactive", label: "退職" },
                ]}
              />
            </Field>
          </div>
        </div>
      </div>

      <div className="form-grid">
        <div className="form-section-title">基本情報</div>
        <Field label="姓" required error={errors.lastName}>
          <Input value={form.lastName}  onChange={v => set("lastName", v)} placeholder="山田" />
        </Field>
        <Field label="名" required error={errors.firstName}>
          <Input value={form.firstName} onChange={v => set("firstName", v)} placeholder="太郎" />
        </Field>
        <Field label="姓（カナ）">
          <Input value={form.lastKana}  onChange={v => set("lastKana", v)} placeholder="ヤマダ" />
        </Field>
        <Field label="名（カナ）">
          <Input value={form.firstKana} onChange={v => set("firstKana", v)} placeholder="タロウ" />
        </Field>
        <Field label="メールアドレス" required error={errors.email} span={2}>
          <Input value={form.email} onChange={v => set("email", v)} placeholder="name@adfproject.jp" />
        </Field>

        <div className="form-section-title">所属・職種</div>
        <Field label="所属部署" hint="組織上の所属（1つ）">
          <Select
            value={form.dept} onChange={v => set("dept", v)}
            options={depts.map(d => ({ value: d.id, label: d.name }))}
          />
        </Field>
        <Field label="入社日">
          <Input type="date" value={form.joined} onChange={v => set("joined", v)} />
        </Field>
        <Field
          label="職種"
          required
          error={errors.roles}
          hint="兼任可能。職種マスターに登録された区分から選択（経理・営業など管理職種を含む）"
          span={2}
        >
          <RolePicker value={form.roles} onChange={v => set("roles", v)} roles={roles} />
        </Field>
        <Field label="(参考) Entra ID" hint="サインインアカウント" span={2}>
          <Input value={form.email} readOnly />
        </Field>

        <Field label="備考" span={2}>
          <Textarea value={form.note} onChange={v => set("note", v)} rows={2} placeholder="任意" />
        </Field>
      </div>
    </Modal>
  );
};

// ============================================
// WORK SCREEN
// ============================================
const WorkScreen = ({ works, setWorks, staff, role, addLog, onOpenDetail, externalEditing, clearExternalEditing }) => {
  const toast = useToast();
  const [q, setQ] = useState("");
  const [statusFilter, setStatusFilter] = useState("");
  const [typeFilter, setTypeFilter] = useState("");
  const [sort, setSort] = useState({ key: "code", dir: "desc" });
  const [page, setPage] = useState(1);
  const [pageSize, setPageSize] = useState(25);
  const [selected, setSelected] = useState(new Set());
  const [editing, setEditing] = useState(null);
  const [confirmDelete, setConfirmDelete] = useState(null);
  const canEdit = role !== "viewer";

  useEffect(() => {
    if (externalEditing) { setEditing(externalEditing); clearExternalEditing?.(); }
  }, [externalEditing]);

  const filtered = useMemo(() => {
    let r = works;
    if (q) {
      const ql = q.toLowerCase();
      r = r.filter(w =>
        w.title.includes(q) || w.titleEn?.toLowerCase().includes(ql) ||
        w.code.toLowerCase().includes(ql) ||
        (CLIENT_BY_ID[w.clientId]?.name || "").includes(q)
      );
    }
    if (statusFilter) r = r.filter(w => w.status === statusFilter);
    if (typeFilter)   r = r.filter(w => w.type === typeFilter);

    r = [...r].sort((a, b) => {
      const dir = sort.dir === "asc" ? 1 : -1;
      const av = a[sort.key] ?? ""; const bv = b[sort.key] ?? "";
      if (av < bv) return -1 * dir;
      if (av > bv) return  1 * dir;
      return 0;
    });
    return r;
  }, [works, q, statusFilter, typeFilter, sort]);

  const paged = filtered.slice((page - 1) * pageSize, page * pageSize);
  useEffect(() => { setPage(1); }, [q, statusFilter, typeFilter]);

  const columns = [
    { key: "code", label: "作品コード", width: 130, sortable: true,
      render: r => <span className="mono">{r.code}</span> },
    { key: "title", label: "作品名", minWidth: 240, sortable: true,
      render: r => (
        <span style={{ display: "inline-flex", alignItems: "center", gap: 8 }}>
          <span style={{ width: 28, height: 18, background: "var(--bg-subtle)", borderRadius: 3, border: "1px solid var(--border)", display: "inline-grid", placeItems: "center", fontSize: 9, color: "var(--text-faint)", fontFamily: "var(--font-mono)" }}>
            KV
          </span>
          <span style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
            <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{r.title}</span>
            <span style={{ fontSize: 10.5, color: "var(--text-faint)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{r.titleEn}</span>
          </span>
        </span>
      )
    },
    { key: "clientId", label: "クライアント", width: 200, sortable: true,
      render: r => {
        const c = CLIENT_BY_ID[r.clientId];
        if (!c) return <span className="muted">—</span>;
        return (
          <span style={{ display: "inline-flex", alignItems: "center", gap: 6, minWidth: 0 }}>
            <span className="mono text-xs faint" style={{ width: 38, flexShrink: 0 }}>{c.code}</span>
            <span style={{ color: "var(--text-muted)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{c.name}</span>
          </span>
        );
      }
    },
    { key: "type", label: "形式", width: 70, sortable: true,
      render: r => <Tag>{r.type}</Tag> },
    { key: "episodes", label: "話数", width: 60, sortable: true, align: "right",
      render: r => <span className="mono">{r.episodes}</span> },
    { key: "producer", label: "P", width: 130,
      render: r => {
        const s = STAFF_BY_ID[r.producer];
        if (!s) return <span className="muted">—</span>;
        return (
          <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
            <Avatar id={s.id} name={initialsFor(s)} size="sm" />
            <span style={{ fontSize: 12 }}>{s.lastName} {s.firstName}</span>
          </span>
        );
      }
    },
    { key: "director", label: "監督", width: 130,
      render: r => {
        const s = STAFF_BY_ID[r.director];
        if (!s) return <span className="muted">{r.director || "—"}</span>;
        return (
          <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
            <Avatar id={s.id} name={initialsFor(s)} size="sm" />
            <span style={{ fontSize: 12 }}>{s.lastName} {s.firstName}</span>
          </span>
        );
      }
    },
    { key: "status", label: "ステータス", width: 110, sortable: true,
      render: r => <Badge tone={WORK_STATUS[r.status].tone} dot>{WORK_STATUS[r.status].label}</Badge> },
    { key: "startDate", label: "期間", width: 180, sortable: true,
      render: r => <span className="mono">{r.startDate} → {r.endDate}</span> },
    { key: "budget", label: "予算", width: 80, sortable: true, align: "right",
      render: r => <span className="mono">¥{formatYen(r.budget)}</span> },
  ];

  const handleSave = (data) => {
    if (data.id) {
      setWorks(works.map(w => w.id === data.id ? data : w));
      addLog({ entity: "work", entityId: data.id, action: "update", field: "—", before: "—", after: data.title, note: "編集" });
      toast(`${data.title} を更新しました`);
    } else {
      const next = { ...data, id: `W${String(works.length + 13).padStart(3, "0")}` };
      setWorks([...works, next]);
      addLog({ entity: "work", entityId: next.id, action: "create", field: "—", before: "—", after: next.title, note: "新規作品登録" });
      toast(`${next.title} を登録しました`);
    }
    setEditing(null);
  };

  const handleDelete = () => {
    if (confirmDelete?.bulk) {
      const ids = [...selected];
      setWorks(works.filter(w => !selected.has(w.id)));
      ids.forEach(id => addLog({ entity: "work", entityId: id, action: "delete", field: "—", before: WORK_BY_ID[id]?.title || id, after: "—", note: "一括削除" }));
      toast(`${ids.length}件を削除しました`);
      setSelected(new Set());
    } else if (confirmDelete) {
      setWorks(works.filter(w => w.id !== confirmDelete.id));
      addLog({ entity: "work", entityId: confirmDelete.id, action: "delete", field: "—", before: confirmDelete.title, after: "—", note: "削除" });
      toast(`${confirmDelete.title} を削除しました`);
    }
    setConfirmDelete(null);
  };

  const exportCSV = () => {
    downloadCSV("works.csv", [
      { label: "作品コード", value: r => r.code },
      { label: "作品名",     value: r => r.title },
      { label: "英題",       value: r => r.titleEn },
      { label: "クライアント", value: r => CLIENT_BY_ID[r.clientId]?.name || "" },
      { label: "クライアントコード", value: r => CLIENT_BY_ID[r.clientId]?.code || "" },
      { label: "形式",       value: r => r.type },
      { label: "話数",       value: r => r.episodes },
      { label: "ステータス", value: r => WORK_STATUS[r.status].label },
      { label: "開始日",     value: r => r.startDate },
      { label: "終了日",     value: r => r.endDate },
      { label: "予算",       value: r => r.budget },
      { label: "プロデューサー", value: r => { const s = STAFF_BY_ID[r.producer]; return s ? `${s.lastName} ${s.firstName}` : ""; } },
      { label: "監督",       value: r => { const s = STAFF_BY_ID[r.director]; return s ? `${s.lastName} ${s.firstName}` : r.director; } },
    ], filtered);
    toast(`作品${filtered.length}件をエクスポートしました`);
  };

  return (
    <>
      <div className="list-header">
        <div className="list-title-row">
          <h1 className="list-title">作品</h1>
          <Badge>{filtered.length} 件</Badge>
          <div style={{ flex: 1 }} />
          <Button icon="download" onClick={exportCSV}>CSVエクスポート</Button>
          <Button icon="plus" variant="accent" onClick={() => setEditing("new")} disabled={!canEdit}>新規作品</Button>
        </div>

        <div className="list-toolbar">
          <div className="grow">
            <SearchInput value={q} onChange={setQ} placeholder="作品名・コード・クライアントで検索…" />
          </div>
          <div style={{ width: 160 }}>
            <Select
              value={statusFilter} onChange={setStatusFilter} placeholder="ステータス（すべて）"
              options={Object.entries(WORK_STATUS).map(([k, v]) => ({ value: k, label: v.label }))}
            />
          </div>
          <div style={{ width: 130 }}>
            <Select
              value={typeFilter} onChange={setTypeFilter} placeholder="形式（すべて）"
              options={["TV", "Movie", "OVA", "ONA", "CM", "Other"].map(v => ({ value: v, label: v }))}
            />
          </div>
          {(q || statusFilter || typeFilter) && (
            <Button variant="ghost" size="sm" onClick={() => { setQ(""); setStatusFilter(""); setTypeFilter(""); }}>
              フィルター解除
            </Button>
          )}
        </div>

        {selected.size > 0 && (
          <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "6px 10px", background: "var(--accent-soft)", border: "1px solid var(--accent-border)", borderRadius: 6, fontSize: 12 }}>
            <span style={{ color: "var(--accent)", fontWeight: 500 }}>{selected.size}件を選択中</span>
            <span style={{ flex: 1 }} />
            <Button size="sm" variant="ghost" onClick={() => setSelected(new Set())}>解除</Button>
            <Button size="sm" icon="trash" variant="danger" onClick={() => setConfirmDelete({ bulk: true })} disabled={!canEdit}>削除</Button>
          </div>
        )}
      </div>

      <DataTable
        columns={columns}
        rows={paged}
        sort={sort}
        onSortChange={setSort}
        selected={selected}
        onSelectedChange={setSelected}
        onRowClick={(r) => onOpenDetail ? onOpenDetail(r.id) : (canEdit ? setEditing(r) : null)}
        rowActions={(r) => (
          <>
            <IconButton icon="edit"  title="編集" onClick={() => canEdit && setEditing(r)} />
            <IconButton icon="trash" title="削除" onClick={() => canEdit && setConfirmDelete(r)} />
          </>
        )}
      />

      <Pagination
        page={page} pageSize={pageSize} total={filtered.length}
        onPageChange={setPage} onPageSizeChange={setPageSize}
      />

      {editing && (
        <WorkEditModal
          work={editing === "new" ? null : editing}
          staff={staff}
          onClose={() => setEditing(null)}
          onSave={handleSave}
        />
      )}
      <ConfirmDialog
        open={!!confirmDelete}
        title="作品を削除"
        message={confirmDelete?.bulk
          ? `選択中の ${selected.size} 件の作品を削除します。この操作は取り消せません。`
          : `「${confirmDelete?.title}」を削除します。この操作は取り消せません。`}
        onCancel={() => setConfirmDelete(null)}
        onConfirm={handleDelete}
      />
    </>
  );
};

const WorkEditModal = ({ work, staff, onClose, onSave }) => {
  const [form, setForm] = useState(work ?? {
    code: `ADF-${new Date().getFullYear()}-XX`, title: "", titleEn: "",
    clientId: "", producer: staff[0]?.id || "", director: staff[0]?.id || "",
    status: "planning", type: "TV", episodes: 12,
    startDate: "", endDate: "", budget: 0, tags: [],
  });
  const [tagInput, setTagInput] = useState("");
  const [errors, setErrors] = useState({});
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const addTag = () => {
    const t = tagInput.trim();
    if (t && !form.tags.includes(t)) set("tags", [...form.tags, t]);
    setTagInput("");
  };

  const submit = () => {
    const e = {};
    if (!form.code)     e.code = "必須項目です";
    if (!form.title)    e.title = "必須項目です";
    if (!form.clientId) e.clientId = "取引先を選択してください";
    setErrors(e);
    if (Object.keys(e).length) return;
    onSave({ ...form, budget: Number(form.budget) || 0, episodes: Number(form.episodes) || 1 });
  };

  return (
    <Modal
      open
      onClose={onClose}
      size="lg"
      title={work ? `作品編集 · ${work.title}` : "新規作品登録"}
      subtitle={work ? `作品コード ${work.code}` : "作品コード・クライアントを入力してください"}
      footer={
        <>
          <span className="grow" />
          <Button onClick={onClose}>キャンセル</Button>
          <Button variant="accent" onClick={submit}>{work ? "保存" : "登録"}</Button>
        </>
      }
    >
      <div className="form-grid">
        <div className="form-section-title">基本情報</div>
        <Field label="作品コード" required error={errors.code}>
          <Input value={form.code} onChange={v => set("code", v.toUpperCase())} placeholder="ADF-2025-01" />
        </Field>
        <Field label="ステータス">
          <Select value={form.status} onChange={v => set("status", v)}
            options={Object.entries(WORK_STATUS).map(([k, v]) => ({ value: k, label: v.label }))} />
        </Field>
        <Field label="作品名 (邦題)" required error={errors.title} span={2}>
          <Input value={form.title} onChange={v => set("title", v)} placeholder="作品タイトル" />
        </Field>
        <Field label="作品名 (英題)" span={2}>
          <Input value={form.titleEn} onChange={v => set("titleEn", v)} placeholder="English Title" />
        </Field>
        <Field label="取引先" required error={errors.clientId} span={2}
          hint="取引先マスターに未登録の場合は、取引先画面で先に追加してください">
          <ComboBox
            value={form.clientId}
            onChange={v => set("clientId", v)}
            placeholder="取引先を選択..."
            options={CLIENTS.filter(c => c.status === "active" || c.id === form.clientId).map(c => ({
              value: c.id,
              label: c.name,
              sub: `${c.code} · ${CLIENT_TYPES[c.type]?.label || c.type}${c.contactPerson && c.contactPerson !== "—" ? ` · 担当: ${c.contactPerson}` : ""}`,
              searchKey: `${c.name} ${c.nameKana} ${c.code} ${c.contactPerson || ""}`,
              leading: (
                <span className="mono text-xs" style={{ width: 38, color: "var(--text-faint)", flexShrink: 0 }}>{c.code}</span>
              ),
            }))}
          />
        </Field>

        <div className="form-section-title">区分・規模</div>
        <Field label="形式">
          <Select value={form.type} onChange={v => set("type", v)}
            options={["TV", "Movie", "OVA", "ONA", "CM", "Other"].map(v => ({ value: v, label: v }))} />
        </Field>
        <Field label="話数">
          <Input type="number" value={form.episodes} onChange={v => set("episodes", v)} />
        </Field>
        <Field label="開始日">
          <Input type="date" value={form.startDate} onChange={v => set("startDate", v)} />
        </Field>
        <Field label="終了日">
          <Input type="date" value={form.endDate} onChange={v => set("endDate", v)} />
        </Field>
        <Field label="予算 (円)" span={2}>
          <Input type="number" value={form.budget} onChange={v => set("budget", v)} placeholder="0" />
        </Field>

        <div className="form-section-title">担当</div>
        <Field label="プロデューサー">
          <ComboBox
            value={form.producer}
            onChange={v => set("producer", v)}
            placeholder="プロデューサーを選択..."
            options={staff.filter(s => s.status !== "inactive").map(s => ({
              value: s.id,
              label: `${s.lastName} ${s.firstName}`,
              sub: `${s.code} · ${DEPT_BY_ID[s.dept]?.name || ""}${(s.roles || []).map(rid => ROLE_BY_ID[rid]?.name).filter(Boolean).slice(0, 2).join(" / ") ? " · " + (s.roles || []).map(rid => ROLE_BY_ID[rid]?.name).filter(Boolean).slice(0, 2).join(" / ") : ""}`,
              searchKey: `${s.lastName} ${s.firstName} ${s.lastKana} ${s.firstKana} ${s.code}`,
              leading: <Avatar id={s.id} name={initialsFor(s)} size="sm" />,
            }))}
          />
        </Field>
        <Field label="監督">
          <ComboBox
            value={form.director}
            onChange={v => set("director", v)}
            placeholder="監督を選択..."
            clearable
            options={staff.filter(s => s.status !== "inactive").map(s => ({
              value: s.id,
              label: `${s.lastName} ${s.firstName}`,
              sub: `${s.code} · ${DEPT_BY_ID[s.dept]?.name || ""}`,
              searchKey: `${s.lastName} ${s.firstName} ${s.lastKana} ${s.firstKana} ${s.code}`,
              leading: <Avatar id={s.id} name={initialsFor(s)} size="sm" />,
            }))}
          />
        </Field>

        <div className="form-section-title">タグ</div>
        <Field span={2}>
          <div style={{ display: "flex", flexWrap: "wrap", gap: 4, padding: 6, border: "1px solid var(--border)", borderRadius: 4, background: "var(--surface)", minHeight: 32 }}>
            {form.tags.map(t => (
              <button key={t} type="button" className="tag" style={{ cursor: "pointer" }} onClick={() => set("tags", form.tags.filter(x => x !== t))}>
                {t} <Icon name="close" size={10} style={{ marginLeft: 2 }} />
              </button>
            ))}
            <input
              style={{ border: "none", outline: "none", flex: 1, minWidth: 100, padding: "2px 4px", fontSize: 12.5, background: "transparent" }}
              value={tagInput}
              onChange={e => setTagInput(e.target.value)}
              onKeyDown={e => { if (e.key === "Enter") { e.preventDefault(); addTag(); } }}
              placeholder={form.tags.length === 0 ? "タグを入力（例: ファンタジー, 続編）" : "+ 追加"}
            />
          </div>
        </Field>
      </div>
    </Modal>
  );
};

// ============================================
// CLIENT SCREEN (取引先)
// ============================================
const ClientScreen = ({ clients, setClients, works, role, addLog }) => {
  const toast = useToast();
  const [q, setQ] = useState("");
  const [typeFilter, setTypeFilter] = useState("");
  const [statusFilter, setStatusFilter] = useState("");
  const [sort, setSort] = useState({ key: "code", dir: "asc" });
  const [editing, setEditing] = useState(null);
  const [confirmDelete, setConfirmDelete] = useState(null);
  const canEdit = role !== "viewer";

  const filtered = useMemo(() => {
    let r = clients;
    if (q) {
      const ql = q.toLowerCase();
      r = r.filter(c =>
        c.name.includes(q) || c.nameKana?.includes(q) ||
        c.code.toLowerCase().includes(ql) ||
        (c.contactPerson || "").includes(q)
      );
    }
    if (typeFilter)   r = r.filter(c => c.type === typeFilter);
    if (statusFilter) r = r.filter(c => c.status === statusFilter);
    return [...r].sort((a, b) => {
      const dir = sort.dir === "asc" ? 1 : -1;
      const av = a[sort.key] ?? ""; const bv = b[sort.key] ?? "";
      if (av < bv) return -1 * dir;
      if (av > bv) return  1 * dir;
      return 0;
    });
  }, [clients, q, typeFilter, statusFilter, sort]);

  const worksCount = (cid) => works.filter(w => w.clientId === cid).length;
  const activeWorksCount = (cid) => works.filter(w => w.clientId === cid && (w.status === "in_production" || w.status === "post_production")).length;

  const handleSave = (data) => {
    if (data.id) {
      setClients(clients.map(c => c.id === data.id ? data : c));
      addLog({ entity: "client", entityId: data.id, action: "update", field: "—", before: "—", after: data.name, note: "編集" });
      toast(`${data.name} を更新しました`);
    } else {
      const next = { ...data, id: `CL${String(clients.length + 1).padStart(2, "0")}`, createdAt: new Date().toISOString().slice(0, 10) };
      setClients([...clients, next]);
      addLog({ entity: "client", entityId: next.id, action: "create", field: "—", before: "—", after: next.name, note: "新規取引先" });
      toast(`${next.name} を登録しました`);
    }
    setEditing(null);
  };

  const handleDelete = () => {
    const c = confirmDelete;
    const cnt = worksCount(c.id);
    if (cnt > 0) {
      toast(`${cnt}件の作品が紐付いているため削除できません。先に作品の取引先を変更してください`, { icon: "warning", duration: 4000 });
      setConfirmDelete(null);
      return;
    }
    setClients(clients.filter(x => x.id !== c.id));
    addLog({ entity: "client", entityId: c.id, action: "delete", field: "—", before: c.name, after: "—", note: "削除" });
    toast(`${c.name} を削除しました`);
    setConfirmDelete(null);
  };

  const exportCSV = () => {
    downloadCSV("clients.csv", [
      { label: "コード",     value: r => r.code },
      { label: "取引先名",   value: r => r.name },
      { label: "カナ",       value: r => r.nameKana },
      { label: "種別",       value: r => CLIENT_TYPES[r.type]?.label || r.type },
      { label: "担当窓口",   value: r => r.contactPerson },
      { label: "メール",     value: r => r.contactEmail },
      { label: "電話",       value: r => r.contactPhone },
      { label: "状態",       value: r => CLIENT_STATUS[r.status].label },
      { label: "作品数",     value: r => worksCount(r.id) },
      { label: "備考",       value: r => r.note },
    ], filtered);
    toast(`取引先${filtered.length}件をエクスポートしました`);
  };

  const columns = [
    { key: "code", label: "コード", width: 80, sortable: true,
      render: r => <span className="mono">{r.code}</span> },
    { key: "name", label: "取引先名", minWidth: 220, sortable: true,
      render: r => (
        <span style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
          <span style={{ fontSize: 12.5, fontWeight: 500 }}>{r.name}</span>
          <span style={{ fontSize: 10.5, color: "var(--text-faint)" }}>{r.nameKana}</span>
        </span>
      )
    },
    { key: "type", label: "種別", width: 130, sortable: true,
      render: r => <Badge tone={CLIENT_TYPES[r.type]?.tone}>{CLIENT_TYPES[r.type]?.label || r.type}</Badge> },
    { key: "contactPerson", label: "担当窓口", width: 130, sortable: true,
      render: r => r.contactPerson === "—" ? <span className="muted">—</span> : <span>{r.contactPerson}</span> },
    { key: "contactEmail", label: "メール", minWidth: 200,
      render: r => r.contactEmail === "—"
        ? <span className="muted">—</span>
        : <span style={{ color: "var(--text-muted)", fontFamily: "var(--font-mono)", fontSize: 11.5 }}>{r.contactEmail}</span>
    },
    { key: "works", label: "作品", width: 100, align: "right",
      render: r => {
        const total = worksCount(r.id);
        const active = activeWorksCount(r.id);
        return (
          <span style={{ display: "inline-flex", alignItems: "center", gap: 4 }}>
            <span className="mono">{total}件</span>
            {active > 0 && (
              <span className="badge" data-tone="info" style={{ fontSize: 10 }}>
                {active}進行
              </span>
            )}
          </span>
        );
      }
    },
    { key: "status", label: "状態", width: 80, sortable: true,
      render: r => <Badge tone={CLIENT_STATUS[r.status].tone} dot>{CLIENT_STATUS[r.status].label}</Badge>
    },
  ];

  return (
    <>
      <div className="list-header">
        <div className="list-title-row">
          <h1 className="list-title">取引先</h1>
          <Badge>{filtered.length} 件</Badge>
          <div style={{ flex: 1 }} />
          <Button icon="download" onClick={exportCSV}>CSVエクスポート</Button>
          <Button icon="plus" variant="accent" onClick={() => setEditing("new")} disabled={!canEdit}>新規取引先</Button>
        </div>
        <div className="list-toolbar">
          <div className="grow">
            <SearchInput value={q} onChange={setQ} placeholder="取引先名・コード・担当窓口で検索…" />
          </div>
          <div style={{ width: 180 }}>
            <Select value={typeFilter} onChange={setTypeFilter} placeholder="種別（すべて）"
              options={Object.entries(CLIENT_TYPES).map(([k, v]) => ({ value: k, label: v.label }))} />
          </div>
          <div style={{ width: 130 }}>
            <Select value={statusFilter} onChange={setStatusFilter} placeholder="状態（すべて）"
              options={Object.entries(CLIENT_STATUS).map(([k, v]) => ({ value: k, label: v.label }))} />
          </div>
          {(q || typeFilter || statusFilter) && (
            <Button variant="ghost" size="sm" onClick={() => { setQ(""); setTypeFilter(""); setStatusFilter(""); }}>
              フィルター解除
            </Button>
          )}
        </div>
      </div>

      <DataTable
        columns={columns} rows={filtered}
        sort={sort} onSortChange={setSort}
        onRowClick={(r) => canEdit ? setEditing(r) : null}
        rowActions={(r) => (
          <>
            <IconButton icon="edit"  onClick={() => canEdit && setEditing(r)} title="編集" />
            <IconButton icon="trash" onClick={() => canEdit && setConfirmDelete(r)} title="削除" />
          </>
        )}
      />

      {editing && (
        <Modal
          open onClose={() => setEditing(null)}
          size="lg"
          title={editing === "new" ? "新規取引先登録" : `取引先編集 · ${editing.name}`}
        >
          <ClientForm
            initial={editing === "new" ? null : editing}
            onSubmit={handleSave}
            onClose={() => setEditing(null)}
          />
        </Modal>
      )}

      <ConfirmDialog
        open={!!confirmDelete}
        title="取引先を削除"
        message={
          worksCount(confirmDelete?.id || "") > 0
            ? `「${confirmDelete?.name}」には${worksCount(confirmDelete?.id)}件の作品が紐付いています。先に作品の取引先を変更してください。`
            : `「${confirmDelete?.name}」を削除します。この操作は取り消せません。`
        }
        onCancel={() => setConfirmDelete(null)}
        onConfirm={handleDelete}
      />
    </>
  );
};

const ClientForm = ({ initial, onSubmit, onClose }) => {
  const [form, setForm] = useState(initial ?? {
    code: "", name: "", nameKana: "", type: "production",
    contactPerson: "", contactEmail: "", contactPhone: "",
    status: "active", note: "",
  });
  const [errors, setErrors] = useState({});
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const submit = () => {
    const e = {};
    if (!form.code) e.code = "必須項目です";
    if (!form.name) e.name = "必須項目です";
    if (form.contactEmail && form.contactEmail !== "—" && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(form.contactEmail)) {
      e.contactEmail = "メールアドレス形式が不正です";
    }
    setErrors(e);
    if (Object.keys(e).length) return;
    onSubmit(form);
  };

  return (
    <>
      <div className="form-grid">
        <div className="form-section-title">基本情報</div>
        <Field label="取引先コード" required error={errors.code}>
          <Input value={form.code} onChange={v => set("code", v.toUpperCase())} placeholder="TAS" />
        </Field>
        <Field label="状態">
          <Select value={form.status} onChange={v => set("status", v)}
            options={Object.entries(CLIENT_STATUS).map(([k, v]) => ({ value: k, label: v.label }))} />
        </Field>
        <Field label="取引先名" required error={errors.name} span={2}>
          <Input value={form.name} onChange={v => set("name", v)} placeholder="東京アニメーションスタジオ" />
        </Field>
        <Field label="カナ" span={2}>
          <Input value={form.nameKana} onChange={v => set("nameKana", v)} placeholder="トウキョウアニメーションスタジオ" />
        </Field>
        <Field label="種別" span={2}>
          <Select value={form.type} onChange={v => set("type", v)}
            options={Object.entries(CLIENT_TYPES).map(([k, v]) => ({ value: k, label: v.label }))} />
        </Field>

        <div className="form-section-title">連絡先</div>
        <Field label="担当窓口" span={2}>
          <Input value={form.contactPerson} onChange={v => set("contactPerson", v)} placeholder="氏名" />
        </Field>
        <Field label="メールアドレス" error={errors.contactEmail}>
          <Input value={form.contactEmail} onChange={v => set("contactEmail", v)} placeholder="contact@example.jp" />
        </Field>
        <Field label="電話番号">
          <Input value={form.contactPhone} onChange={v => set("contactPhone", v)} placeholder="03-XXXX-XXXX" />
        </Field>

        <Field label="備考" span={2}>
          <Textarea value={form.note} onChange={v => set("note", v)} rows={2} placeholder="任意" />
        </Field>
      </div>
      <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 18, paddingTop: 14, borderTop: "1px solid var(--border)" }}>
        <Button onClick={onClose}>キャンセル</Button>
        <Button variant="accent" onClick={submit}>{initial ? "保存" : "登録"}</Button>
      </div>
    </>
  );
};

// ============================================
// DEPT SCREEN (light)
// ============================================
const DeptScreen = ({ depts, setDepts, staff, role, addLog }) => {
  const toast = useToast();
  const [q, setQ] = useState("");
  const [editing, setEditing] = useState(null);
  const [confirmDelete, setConfirmDelete] = useState(null);
  const canEdit = role !== "viewer";

  const filtered = useMemo(() =>
    depts.filter(d => !q || d.name.includes(q) || d.code.toLowerCase().includes(q.toLowerCase()) || d.nameEn?.toLowerCase().includes(q.toLowerCase()))
  , [depts, q]);

  const columns = [
    { key: "code", label: "コード", width: 80, render: r => <span className="mono">{r.code}</span> },
    { key: "name", label: "部署名", width: 200, render: r => <strong>{r.name}</strong> },
    { key: "nameEn", label: "英名", width: 160, render: r => <span className="muted">{r.nameEn}</span> },
    { key: "manager", label: "部署長", width: 180,
      render: r => {
        const s = STAFF_BY_ID[r.manager];
        return s ? <AvatarName staff={s} sub={null} /> : <span className="muted">—</span>;
      }
    },
    { key: "members", label: "所属人数", width: 90, align: "right",
      render: r => <span className="mono">{staff.filter(s => s.dept === r.id).length}名</span> },
    { key: "note", label: "備考", minWidth: 200, render: r => <span className="muted">{r.note}</span> },
    { key: "createdAt", label: "作成日", width: 110, render: r => <span className="mono">{r.createdAt}</span> },
  ];

  const handleSave = (data) => {
    if (data.id) {
      setDepts(depts.map(d => d.id === data.id ? data : d));
      addLog({ entity: "department", entityId: data.id, action: "update", field: "—", before: "—", after: data.name, note: "編集" });
      toast(`${data.name} を更新しました`);
    } else {
      const next = { ...data, id: `D${String(depts.length + 1).padStart(3, "0")}`, createdAt: new Date().toISOString().slice(0, 10), members: 0 };
      setDepts([...depts, next]);
      addLog({ entity: "department", entityId: next.id, action: "create", field: "—", before: "—", after: next.name, note: "新規部署" });
      toast(`${next.name} を登録しました`);
    }
    setEditing(null);
  };
  const handleDelete = () => {
    setDepts(depts.filter(d => d.id !== confirmDelete.id));
    addLog({ entity: "department", entityId: confirmDelete.id, action: "delete", field: "—", before: confirmDelete.name, after: "—", note: "削除" });
    toast(`${confirmDelete.name} を削除しました`);
    setConfirmDelete(null);
  };

  return (
    <>
      <div className="list-header">
        <div className="list-title-row">
          <h1 className="list-title">部署</h1>
          <Badge>{filtered.length} 件</Badge>
          <div style={{ flex: 1 }} />
          <Button icon="plus" variant="accent" onClick={() => setEditing("new")} disabled={!canEdit}>新規部署</Button>
        </div>
        <div className="list-toolbar">
          <div style={{ width: 300 }}>
            <SearchInput value={q} onChange={setQ} placeholder="部署名・コードで検索…" />
          </div>
        </div>
      </div>

      <DataTable
        columns={columns} rows={filtered}
        onRowClick={(r) => canEdit ? setEditing(r) : null}
        rowActions={(r) => (
          <>
            <IconButton icon="edit"  onClick={() => canEdit && setEditing(r)} title="編集" />
            <IconButton icon="trash" onClick={() => canEdit && setConfirmDelete(r)} title="削除" />
          </>
        )}
      />

      {editing && (
        <Modal
          open onClose={() => setEditing(null)}
          title={editing === "new" ? "新規部署登録" : `部署編集 · ${editing.name}`}
          footer={<DeptFormFooter onClose={() => setEditing(null)} />}
        >
          <DeptForm
            initial={editing === "new" ? null : editing}
            staff={staff}
            onSubmit={handleSave}
            onClose={() => setEditing(null)}
          />
        </Modal>
      )}

      <ConfirmDialog
        open={!!confirmDelete}
        title="部署を削除"
        message={`「${confirmDelete?.name}」を削除します。所属スタッフの再配置を必ず行ってください。`}
        onCancel={() => setConfirmDelete(null)}
        onConfirm={handleDelete}
      />
    </>
  );
};

const DeptFormFooter = () => null; // placeholder; form has its own footer via portal? We'll inline.

const DeptForm = ({ initial, staff, onSubmit, onClose }) => {
  const [form, setForm] = useState(initial ?? { code: "", name: "", nameEn: "", manager: "", note: "" });
  const [errors, setErrors] = useState({});
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const submit = () => {
    const e = {};
    if (!form.code) e.code = "必須項目です";
    if (!form.name) e.name = "必須項目です";
    setErrors(e);
    if (Object.keys(e).length) return;
    onSubmit(form);
  };

  return (
    <>
      <div className="form-grid">
        <Field label="部署コード" required error={errors.code}>
          <Input value={form.code} onChange={v => set("code", v.toUpperCase())} placeholder="PRD" />
        </Field>
        <Field label="部署名" required error={errors.name}>
          <Input value={form.name} onChange={v => set("name", v)} placeholder="プロダクション部" />
        </Field>
        <Field label="英名" span={2}>
          <Input value={form.nameEn} onChange={v => set("nameEn", v)} placeholder="Production" />
        </Field>
        <Field label="部署長" span={2}>
          <ComboBox
            value={form.manager}
            onChange={v => set("manager", v)}
            placeholder="未設定（あとから設定可）"
            clearable
            options={staff.filter(s => s.status !== "inactive").map(s => ({
              value: s.id,
              label: `${s.lastName} ${s.firstName}`,
              sub: `${s.code} · ${DEPT_BY_ID[s.dept]?.name || ""}`,
              searchKey: `${s.lastName} ${s.firstName} ${s.lastKana} ${s.firstKana} ${s.code}`,
              leading: <Avatar id={s.id} name={initialsFor(s)} size="sm" />,
            }))}
          />
        </Field>
        <Field label="備考" span={2}>
          <Textarea value={form.note} onChange={v => set("note", v)} rows={2} />
        </Field>
      </div>
      <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 18, paddingTop: 14, borderTop: "1px solid var(--border)" }}>
        <Button onClick={onClose}>キャンセル</Button>
        <Button variant="accent" onClick={submit}>{initial ? "保存" : "登録"}</Button>
      </div>
    </>
  );
};

// ============================================
// ROLE SCREEN (light)
// ============================================
const RoleScreen = ({ roles, setRoles, staff, role, addLog, categories, setCategories }) => {
  const toast = useToast();
  const [q, setQ] = useState("");
  const [catFilter, setCatFilter] = useState("");
  const [editing, setEditing] = useState(null);
  const [confirmDelete, setConfirmDelete] = useState(null);
  const [catManagerOpen, setCatManagerOpen] = useState(false);
  const canEdit = role !== "viewer";

  const catNames = useMemo(() => [...categories].sort((a, b) => a.order - b.order).map(c => c.name), [categories]);

  const filtered = useMemo(() => {
    let r = roles;
    if (q) r = r.filter(x => x.name.includes(q) || x.code.toLowerCase().includes(q.toLowerCase()));
    if (catFilter) r = r.filter(x => x.category === catFilter);
    // sort by category order then name
    const orderMap = Object.fromEntries(categories.map(c => [c.name, c.order]));
    return [...r].sort((a, b) => {
      const ao = orderMap[a.category] ?? 999;
      const bo = orderMap[b.category] ?? 999;
      if (ao !== bo) return ao - bo;
      return a.code.localeCompare(b.code);
    });
  }, [roles, q, catFilter, categories]);

  const columns = [
    { key: "code", label: "コード", width: 80, render: r => <span className="mono">{r.code}</span> },
    { key: "name", label: "職種名", width: 200, render: r => <strong>{r.name}</strong> },
    { key: "category", label: "カテゴリ", width: 140,
      render: r => {
        const cat = categories.find(c => c.name === r.category);
        return (
          <span
            className="tag"
            style={{ cursor: "pointer" }}
            title={cat?.description || r.category}
            onClick={(e) => { e.stopPropagation(); setCatFilter(r.category); }}
          >
            {r.category}
          </span>
        );
      }
    },
    { key: "level", label: "レベル", width: 100,
      render: r => (
        <span style={{ display: "inline-flex", alignItems: "center", gap: 4 }}>
          {[1,2,3,4,5].map(i => (
            <span key={i} style={{ width: 6, height: 6, borderRadius: 50, background: i <= r.level ? "var(--accent)" : "var(--border-strong)" }} />
          ))}
          <span className="mono text-xs faint" style={{ marginLeft: 4 }}>L{r.level}</span>
        </span>
      )
    },
    { key: "count", label: "登録人数", width: 90, align: "right",
      render: r => <span className="mono">{staff.filter(s => (s.roles || []).includes(r.id)).length}名</span> },
    { key: "createdAt", label: "作成日", width: 110, render: r => <span className="mono">{r.createdAt}</span> },
  ];

  const handleSave = (data) => {
    if (data.id) {
      setRoles(roles.map(r => r.id === data.id ? data : r));
      addLog({ entity: "role", entityId: data.id, action: "update", field: "—", before: "—", after: data.name, note: "編集" });
      toast(`${data.name} を更新しました`);
    } else {
      const next = { ...data, id: `R${String(roles.length + 1).padStart(3, "0")}`, createdAt: new Date().toISOString().slice(0, 10), count: 0, level: Number(data.level) };
      setRoles([...roles, next]);
      addLog({ entity: "role", entityId: next.id, action: "create", field: "—", before: "—", after: next.name, note: "新規職種" });
      toast(`${next.name} を登録しました`);
    }
    setEditing(null);
  };
  const handleDelete = () => {
    setRoles(roles.filter(r => r.id !== confirmDelete.id));
    addLog({ entity: "role", entityId: confirmDelete.id, action: "delete", field: "—", before: confirmDelete.name, after: "—", note: "削除" });
    toast(`${confirmDelete.name} を削除しました`);
    setConfirmDelete(null);
  };

  return (
    <>
      <div className="list-header">
        <div className="list-title-row">
          <h1 className="list-title">職種</h1>
          <Badge>{filtered.length} 件</Badge>
          <span className="muted text-xs">/ {categories.length}カテゴリ</span>
          <div style={{ flex: 1 }} />
          <Button icon="grid" onClick={() => setCatManagerOpen(true)} disabled={!canEdit}>カテゴリ管理</Button>
          <Button icon="plus" variant="accent" onClick={() => setEditing("new")} disabled={!canEdit}>新規職種</Button>
        </div>
        <div className="list-toolbar">
          <div style={{ width: 280 }}>
            <SearchInput value={q} onChange={setQ} placeholder="職種名・コードで検索…" />
          </div>
          <div style={{ width: 200 }}>
            <Select value={catFilter} onChange={setCatFilter} placeholder="カテゴリ（すべて）"
              options={catNames.map(c => ({ value: c, label: c }))} />
          </div>
          {(q || catFilter) && (
            <Button variant="ghost" size="sm" onClick={() => { setQ(""); setCatFilter(""); }}>
              フィルター解除
            </Button>
          )}
        </div>
      </div>

      <DataTable
        columns={columns} rows={filtered}
        onRowClick={(r) => canEdit ? setEditing(r) : null}
        rowActions={(r) => (
          <>
            <IconButton icon="edit"  onClick={() => canEdit && setEditing(r)} title="編集" />
            <IconButton icon="trash" onClick={() => canEdit && setConfirmDelete(r)} title="削除" />
          </>
        )}
      />

      {editing && (
        <Modal
          open onClose={() => setEditing(null)}
          title={editing === "new" ? "新規職種登録" : `職種編集 · ${editing.name}`}
        >
          <RoleForm
            initial={editing === "new" ? null : editing}
            categories={catNames}
            onSubmit={handleSave}
            onClose={() => setEditing(null)}
          />
        </Modal>
      )}

      {catManagerOpen && (
        <CategoryManagerModal
          categories={categories}
          setCategories={setCategories}
          roles={roles}
          setRoles={setRoles}
          addLog={addLog}
          onClose={() => setCatManagerOpen(false)}
        />
      )}

      <ConfirmDialog
        open={!!confirmDelete}
        title="職種を削除"
        message={`「${confirmDelete?.name}」を削除します。この職種が割り当てられているスタッフは未設定になります。`}
        onCancel={() => setConfirmDelete(null)}
        onConfirm={handleDelete}
      />
    </>
  );
};

const RoleForm = ({ initial, categories, onSubmit, onClose }) => {
  const [form, setForm] = useState(initial ?? { code: "", name: "", category: categories[0] || "", level: 3 });
  const [errors, setErrors] = useState({});
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const submit = () => {
    const e = {};
    if (!form.code) e.code = "必須項目です";
    if (!form.name) e.name = "必須項目です";
    setErrors(e);
    if (Object.keys(e).length) return;
    onSubmit(form);
  };
  return (
    <>
      <div className="form-grid">
        <Field label="職種コード" required error={errors.code}>
          <Input value={form.code} onChange={v => set("code", v.toUpperCase())} placeholder="KGA" />
        </Field>
        <Field label="職種名" required error={errors.name}>
          <Input value={form.name} onChange={v => set("name", v)} placeholder="原画" />
        </Field>
        <Field label="カテゴリ">
          <Select value={form.category} onChange={v => set("category", v)}
            options={categories.map(c => ({ value: c, label: c }))} />
        </Field>
        <Field label="レベル (1=新人 / 5=リード)">
          <Select value={String(form.level)} onChange={v => set("level", Number(v))}
            options={[1,2,3,4,5].map(n => ({ value: String(n), label: `L${n}` }))} />
        </Field>
      </div>
      <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 18, paddingTop: 14, borderTop: "1px solid var(--border)" }}>
        <Button onClick={onClose}>キャンセル</Button>
        <Button variant="accent" onClick={submit}>{initial ? "保存" : "登録"}</Button>
      </div>
    </>
  );
};

// ============================================
// CATEGORY MANAGER MODAL (manage 職種カテゴリ)
// ============================================
const CategoryManagerModal = ({ categories, setCategories, roles, setRoles, addLog, onClose }) => {
  const toast = useToast();
  const [editing, setEditing] = useState(null); // category being edited inline
  const [draft, setDraft] = useState({ name: "", description: "" });
  const [adding, setAdding] = useState(false);
  const [newCat, setNewCat] = useState({ name: "", description: "" });
  const [confirmDelete, setConfirmDelete] = useState(null);

  const sorted = useMemo(() => [...categories].sort((a, b) => a.order - b.order), [categories]);
  const roleCount = (catName) => roles.filter(r => r.category === catName).length;

  const startEdit = (c) => {
    setEditing(c.id);
    setDraft({ name: c.name, description: c.description || "" });
  };
  const cancelEdit = () => {
    setEditing(null);
    setDraft({ name: "", description: "" });
  };
  const saveEdit = (c) => {
    const trimmed = draft.name.trim();
    if (!trimmed) { toast("カテゴリ名を入力してください", { icon: "warning" }); return; }
    if (categories.some(x => x.id !== c.id && x.name === trimmed)) {
      toast("同名のカテゴリが既に存在します", { icon: "warning" });
      return;
    }
    const renamed = trimmed !== c.name;
    setCategories(categories.map(x => x.id === c.id ? { ...x, name: trimmed, description: draft.description.trim() } : x));
    if (renamed) {
      // cascade rename to all roles using the old name
      setRoles(roles.map(r => r.category === c.name ? { ...r, category: trimmed } : r));
      addLog({ entity: "role", entityId: c.id, action: "update", field: "category.name", before: c.name, after: trimmed, note: `${roleCount(c.name)}件の職種に反映` });
      toast(`「${c.name}」を「${trimmed}」に変更しました`);
    } else {
      toast(`「${trimmed}」を更新しました`);
    }
    cancelEdit();
  };

  const addCategory = () => {
    const trimmed = newCat.name.trim();
    if (!trimmed) { toast("カテゴリ名を入力してください", { icon: "warning" }); return; }
    if (categories.some(x => x.name === trimmed)) {
      toast("同名のカテゴリが既に存在します", { icon: "warning" });
      return;
    }
    const nextOrder = Math.max(0, ...categories.map(c => c.order)) + 1;
    const nextId = `C${String(categories.length + 1).padStart(3, "0")}`;
    const created = { id: nextId, name: trimmed, description: newCat.description.trim(), order: nextOrder, createdAt: new Date().toISOString().slice(0, 10) };
    setCategories([...categories, created]);
    addLog({ entity: "role", entityId: nextId, action: "create", field: "category", before: "—", after: trimmed, note: "新規カテゴリ" });
    toast(`カテゴリ「${trimmed}」を追加しました`);
    setNewCat({ name: "", description: "" });
    setAdding(false);
  };

  const handleDelete = () => {
    const c = confirmDelete;
    const cnt = roleCount(c.name);
    if (cnt > 0) {
      toast(`このカテゴリには${cnt}件の職種が紐付いています。先に職種のカテゴリを変更してください`, { icon: "warning", duration: 4000 });
      setConfirmDelete(null);
      return;
    }
    setCategories(categories.filter(x => x.id !== c.id));
    addLog({ entity: "role", entityId: c.id, action: "delete", field: "category", before: c.name, after: "—", note: "カテゴリ削除" });
    toast(`カテゴリ「${c.name}」を削除しました`);
    setConfirmDelete(null);
  };

  const move = (c, dir) => {
    const idx = sorted.findIndex(x => x.id === c.id);
    const swapIdx = idx + dir;
    if (swapIdx < 0 || swapIdx >= sorted.length) return;
    const other = sorted[swapIdx];
    setCategories(categories.map(x => {
      if (x.id === c.id) return { ...x, order: other.order };
      if (x.id === other.id) return { ...x, order: c.order };
      return x;
    }));
  };

  return (
    <>
      <Modal
        open
        onClose={onClose}
        size="lg"
        title="カテゴリ管理"
        subtitle="職種マスターのカテゴリを追加・編集・並び替え"
        footer={
          <>
            <span className="grow text-xs faint">
              <Icon name="info" size={12} /> 名称変更は紐付く全ての職種に自動反映されます
            </span>
            <Button onClick={onClose}>閉じる</Button>
          </>
        }
      >
        <div style={{ border: "1px solid var(--border)", borderRadius: 6, overflow: "hidden", background: "var(--surface)" }}>
          {sorted.map((c, i) => {
            const isEditing = editing === c.id;
            const cnt = roleCount(c.name);
            return (
              <div
                key={c.id}
                style={{
                  display: "grid",
                  gridTemplateColumns: "26px 1fr 80px 100px",
                  alignItems: "center",
                  gap: 12,
                  padding: "10px 14px",
                  borderTop: i > 0 ? "1px solid var(--border)" : "none",
                  background: isEditing ? "var(--accent-soft)" : undefined,
                }}
              >
                {/* Order controls */}
                <div style={{ display: "flex", flexDirection: "column", gap: 1 }}>
                  <button
                    className="icon-btn"
                    style={{ width: 16, height: 14, padding: 0 }}
                    onClick={() => move(c, -1)} disabled={i === 0} title="上へ"
                  ><Icon name="chevron-down" size={9} style={{ transform: "rotate(180deg)" }} /></button>
                  <button
                    className="icon-btn"
                    style={{ width: 16, height: 14, padding: 0 }}
                    onClick={() => move(c, 1)} disabled={i === sorted.length - 1} title="下へ"
                  ><Icon name="chevron-down" size={9} /></button>
                </div>

                {/* Name + description */}
                <div style={{ minWidth: 0 }}>
                  {isEditing ? (
                    <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
                      <Input
                        value={draft.name} onChange={v => setDraft(d => ({ ...d, name: v }))}
                        placeholder="カテゴリ名"
                        autoFocus
                        onKeyDown={e => { if (e.key === "Enter") saveEdit(c); if (e.key === "Escape") cancelEdit(); }}
                      />
                      <Input
                        value={draft.description} onChange={v => setDraft(d => ({ ...d, description: v }))}
                        placeholder="説明（任意）"
                      />
                    </div>
                  ) : (
                    <>
                      <div style={{ fontSize: 13, fontWeight: 500, display: "flex", alignItems: "center", gap: 6 }}>
                        {c.name}
                        <span className="mono text-xs faint">{c.id}</span>
                      </div>
                      {c.description && <div className="text-xs muted" style={{ marginTop: 2 }}>{c.description}</div>}
                    </>
                  )}
                </div>

                {/* Role count */}
                <div style={{ textAlign: "right", fontSize: 11.5 }}>
                  <span className="mono">{cnt}</span>
                  <span className="faint text-xs"> 職種</span>
                </div>

                {/* Actions */}
                <div style={{ display: "flex", justifyContent: "flex-end", gap: 2 }}>
                  {isEditing ? (
                    <>
                      <Button size="sm" onClick={cancelEdit}>キャンセル</Button>
                      <Button size="sm" variant="accent" onClick={() => saveEdit(c)}>保存</Button>
                    </>
                  ) : (
                    <>
                      <IconButton icon="edit"  onClick={() => startEdit(c)} title="編集" />
                      <IconButton icon="trash" onClick={() => setConfirmDelete(c)} title="削除" />
                    </>
                  )}
                </div>
              </div>
            );
          })}

          {/* Add new row */}
          {adding ? (
            <div style={{
              display: "grid", gridTemplateColumns: "26px 1fr 80px 100px", gap: 12,
              padding: "10px 14px", borderTop: "1px solid var(--border)",
              background: "var(--bg-subtle)",
            }}>
              <div />
              <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
                <Input
                  value={newCat.name} onChange={v => setNewCat(c => ({ ...c, name: v }))}
                  placeholder="新しいカテゴリ名" autoFocus
                  onKeyDown={e => { if (e.key === "Enter") addCategory(); if (e.key === "Escape") { setAdding(false); setNewCat({ name: "", description: "" }); } }}
                />
                <Input
                  value={newCat.description} onChange={v => setNewCat(c => ({ ...c, description: v }))}
                  placeholder="説明（任意）"
                />
              </div>
              <div />
              <div style={{ display: "flex", justifyContent: "flex-end", gap: 2 }}>
                <Button size="sm" onClick={() => { setAdding(false); setNewCat({ name: "", description: "" }); }}>キャンセル</Button>
                <Button size="sm" variant="accent" onClick={addCategory}>追加</Button>
              </div>
            </div>
          ) : (
            <button
              className="dropdown-item"
              style={{
                width: "100%", padding: "10px 14px", borderTop: "1px solid var(--border)",
                color: "var(--text-muted)", borderRadius: 0,
              }}
              onClick={() => setAdding(true)}
            >
              <Icon name="plus" size={13} />
              <span>カテゴリを追加</span>
            </button>
          )}
        </div>

        <div style={{ marginTop: 14, fontSize: 11.5, color: "var(--text-subtle)", lineHeight: 1.6 }}>
          <strong style={{ color: "var(--text-muted)" }}>使い方:</strong> カテゴリ名を変更すると、そのカテゴリに属するすべての職種に自動で反映されます。
          職種が紐付いているカテゴリは削除できません。先に職種の所属カテゴリを変更してから削除してください。
        </div>
      </Modal>

      <ConfirmDialog
        open={!!confirmDelete}
        title="カテゴリを削除"
        message={`カテゴリ「${confirmDelete?.name}」を削除します。${roleCount(confirmDelete?.name || "") > 0 ? `このカテゴリには${roleCount(confirmDelete?.name)}件の職種が紐付いているため、削除できません。` : "この操作は取り消せません。"}`}
        onCancel={() => setConfirmDelete(null)}
        onConfirm={handleDelete}
      />
    </>
  );
};

// ============================================
// ACCESS USERS SCREEN — admin only
// ============================================
const AccessUsersScreen = ({ users, setUsers, staff, role, addLog }) => {
  const toast = useToast();
  const [q, setQ] = useState("");
  const [roleFilter, setRoleFilter] = useState("");
  const [statusFilter, setStatusFilter] = useState("");
  const [sort, setSort] = useState({ key: "lastLogin", dir: "desc" });
  const [inviteOpen, setInviteOpen] = useState(false);
  const [confirmAction, setConfirmAction] = useState(null);
  const isAdmin = role === "admin";

  const filtered = useMemo(() => {
    let r = users;
    if (q) {
      const ql = q.toLowerCase();
      r = r.filter(u => {
        const s = u.staffId ? STAFF_BY_ID[u.staffId] : null;
        const name = s ? `${s.lastName}${s.firstName}${s.lastKana}${s.firstKana}` : "";
        return u.email.toLowerCase().includes(ql) || name.includes(q);
      });
    }
    if (roleFilter)   r = r.filter(u => u.role === roleFilter);
    if (statusFilter) r = r.filter(u => u.status === statusFilter);
    return [...r].sort((a, b) => {
      const dir = sort.dir === "asc" ? 1 : -1;
      const av = a[sort.key] ?? ""; const bv = b[sort.key] ?? "";
      if (av < bv) return -1 * dir;
      if (av > bv) return  1 * dir;
      return 0;
    });
  }, [users, q, roleFilter, statusFilter, sort]);

  const counts = useMemo(() => ({
    total:   users.length,
    active:  users.filter(u => u.status === "active").length,
    admin:   users.filter(u => u.role === "admin" && u.status === "active").length,
    pending: users.filter(u => u.status === "pending").length,
  }), [users]);

  const changeRole = (user, newRole) => {
    if (!isAdmin) return;
    if (user.role === newRole) return;
    const oldLabel = USER_ROLES[user.role].label;
    const newLabel = USER_ROLES[newRole].label;
    setUsers(users.map(u => u.id === user.id ? { ...u, role: newRole } : u));
    addLog({ entity: "user", entityId: user.id, action: "update", field: "role", before: oldLabel, after: newLabel, note: `${user.email}` });
    toast(`${user.email} のロールを ${newLabel} に変更しました`);
  };

  const toggleStatus = (user) => {
    if (!isAdmin) return;
    const newStatus = user.status === "active" ? "disabled" : "active";
    setUsers(users.map(u => u.id === user.id ? { ...u, status: newStatus } : u));
    addLog({ entity: "user", entityId: user.id, action: "update", field: "status", before: USER_STATUS[user.status].label, after: USER_STATUS[newStatus].label, note: `${user.email}` });
    toast(`${user.email} を${newStatus === "active" ? "有効化" : "無効化"}しました`);
    setConfirmAction(null);
  };

  const resendInvite = (user) => {
    addLog({ entity: "user", entityId: user.id, action: "update", field: "—", before: "—", after: "—", note: `${user.email} に招待メール再送` });
    toast(`${user.email} に招待メールを再送しました`);
  };

  const inviteUser = (data) => {
    const next = {
      id: `U${String(users.length + 1).padStart(3, "0")}`,
      staffId: data.staffId || null,
      email: data.email,
      role: data.role,
      status: "pending",
      lastLogin: null,
      invitedAt: new Date().toISOString().slice(0, 10),
      invitedBy: "U001",
      note: data.note || "",
    };
    setUsers([next, ...users]);
    addLog({ entity: "user", entityId: next.id, action: "create", field: "—", before: "—", after: data.email, note: `${USER_ROLES[data.role].label} として招待` });
    toast(`${data.email} に招待メールを送信しました`);
    setInviteOpen(false);
  };

  const columns = [
    { key: "name", label: "氏名・所属", minWidth: 240,
      render: u => {
        const s = u.staffId ? STAFF_BY_ID[u.staffId] : null;
        if (!s) return <span className="muted text-sm">{u.email}</span>;
        const dept = DEPT_BY_ID[s.dept];
        return (
          <span style={{ display: "inline-flex", alignItems: "center", gap: 8 }}>
            <Avatar id={s.id} name={initialsFor(s)} />
            <span style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
              <span style={{ fontSize: 12.5, fontWeight: 500 }}>{s.lastName} {s.firstName}</span>
              <span style={{ fontSize: 10.5, color: "var(--text-faint)" }}>{dept?.name || "—"}{u.note ? ` · ${u.note}` : ""}</span>
            </span>
          </span>
        );
      }
    },
    { key: "email", label: "メール (Entra ID)", minWidth: 240, sortable: true,
      render: u => <span style={{ color: "var(--text-muted)", fontFamily: "var(--font-mono)", fontSize: 11.5 }}>{u.email}</span> },
    { key: "role", label: "ロール", width: 130, sortable: true,
      render: u => isAdmin && u.status !== "pending" ? (
        <select
          className="input"
          style={{ height: 24, padding: "0 22px 0 8px", fontSize: 11.5, width: "100%", background: "var(--accent-soft)", color: "var(--accent)", borderColor: "var(--accent-border)" }}
          value={u.role}
          onClick={e => e.stopPropagation()}
          onChange={e => changeRole(u, e.target.value)}
        >
          {Object.entries(USER_ROLES).map(([k, v]) => (
            <option key={k} value={k}>{v.label}</option>
          ))}
        </select>
      ) : (
        <Badge tone={USER_ROLES[u.role].tone}>{USER_ROLES[u.role].label}</Badge>
      )
    },
    { key: "status", label: "状態", width: 90, sortable: true,
      render: u => <Badge tone={USER_STATUS[u.status].tone} dot>{USER_STATUS[u.status].label}</Badge>
    },
    { key: "lastLogin", label: "最終ログイン", width: 160, sortable: true,
      render: u => u.lastLogin
        ? <span className="mono" style={{ fontSize: 11.5 }}>{u.lastLogin}</span>
        : <span className="muted text-xs">未ログイン</span>
    },
    { key: "invitedAt", label: "招待日", width: 100, sortable: true,
      render: u => <span className="mono">{u.invitedAt}</span>
    },
  ];

  return (
    <>
      <div className="list-header">
        <div className="list-title-row">
          <h1 className="list-title">アクセスユーザー</h1>
          <Badge>{counts.total} 件</Badge>
          <div style={{ flex: 1 }} />
          <Button icon="plus" variant="accent" onClick={() => setInviteOpen(true)} disabled={!isAdmin}>
            ユーザーを招待
          </Button>
        </div>

        {/* Stats row */}
        <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 12, maxWidth: 540 }}>
          {[
            { label: "有効ユーザー", value: counts.active, sub: `全${counts.total}人中` },
            { label: "管理者", value: counts.admin, sub: "管理者ロール" },
            { label: "招待中", value: counts.pending, sub: "未ログイン" },
          ].map((s) => (
            <div key={s.label} style={{ background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 6, padding: "10px 12px" }}>
              <div className="text-xs faint">{s.label}</div>
              <div style={{ fontSize: 20, fontWeight: 600, letterSpacing: "-0.02em", marginTop: 2, lineHeight: 1.1 }}>{s.value}</div>
              <div className="text-xs muted" style={{ marginTop: 2 }}>{s.sub}</div>
            </div>
          ))}
        </div>

        <div className="list-toolbar">
          <div className="grow">
            <SearchInput value={q} onChange={setQ} placeholder="氏名・メールで検索…" />
          </div>
          <div style={{ width: 150 }}>
            <Select value={roleFilter} onChange={setRoleFilter} placeholder="ロール（すべて）"
              options={Object.entries(USER_ROLES).map(([k, v]) => ({ value: k, label: v.label }))} />
          </div>
          <div style={{ width: 130 }}>
            <Select value={statusFilter} onChange={setStatusFilter} placeholder="状態（すべて）"
              options={Object.entries(USER_STATUS).map(([k, v]) => ({ value: k, label: v.label }))} />
          </div>
          {(q || roleFilter || statusFilter) && (
            <Button variant="ghost" size="sm" onClick={() => { setQ(""); setRoleFilter(""); setStatusFilter(""); }}>
              フィルター解除
            </Button>
          )}
        </div>

        {!isAdmin && (
          <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "8px 12px", background: "var(--warning-soft)", border: "1px solid transparent", borderRadius: 6, fontSize: 12, color: "var(--warning)" }}>
            <Icon name="lock" size={13} />
            <span>この画面は管理者ロールのみ編集できます。閲覧専用モードです。</span>
          </div>
        )}
      </div>

      <DataTable
        columns={columns}
        rows={filtered}
        sort={sort}
        onSortChange={setSort}
        rowActions={(u) => (
          <DropdownMenu
            align="right"
            trigger={<IconButton icon="more" title="操作" />}
            items={[
              ...(u.status === "pending"
                ? [{ label: "招待を再送", icon: "upload", onClick: () => resendInvite(u) }]
                : []
              ),
              ...(u.status === "active"
                ? [{ label: "無効化",  icon: "lock", danger: true, onClick: () => setConfirmAction({ type: "disable", user: u }) }]
                : [{ label: "有効化",  icon: "check", onClick: () => toggleStatus(u) }]
              ),
              { type: "divider" },
              { label: "Entra IDで開く", icon: "shield", onClick: () => toast("Entra ID 管理ポータルを開きます（モック）") },
            ].filter(Boolean)}
          />
        )}
      />

      <p className="text-xs muted" style={{ marginTop: 16, lineHeight: 1.7 }}>
        <Icon name="info" size={11} />{" "}
        ロール変更は即時反映されます。退職スタッフのアクセス権は無効化のみ行い、レコード自体は監査のため残します。
        Entra ID側でアカウントが削除された場合、次回ログイン時に該当ユーザーは自動的に無効化されます。
      </p>

      {inviteOpen && (
        <InviteUserModal
          staff={staff}
          existingUsers={users}
          onClose={() => setInviteOpen(false)}
          onInvite={inviteUser}
        />
      )}

      <ConfirmDialog
        open={confirmAction?.type === "disable"}
        title="アクセスを無効化"
        message={`${confirmAction?.user?.email} のシステムアクセスを無効化します。次回のログイン試行は拒否されますが、ユーザー記録自体は監査のため残ります。後から有効化することもできます。`}
        confirmLabel="無効化"
        onCancel={() => setConfirmAction(null)}
        onConfirm={() => toggleStatus(confirmAction.user)}
      />
    </>
  );
};

// Invite user modal — スタッフから選択のみ
const InviteUserModal = ({ staff, existingUsers, onClose, onInvite }) => {
  const [staffId, setStaffId] = useState("");
  const [role, setRole] = useState("editor");
  const [errors, setErrors] = useState({});

  const eligibleStaff = useMemo(() =>
    staff.filter(s => s.status === "active" && !existingUsers.some(u => u.staffId === s.id))
  , [staff, existingUsers]);

  const submit = () => {
    const e = {};
    if (!staffId) e.staffId = "スタッフを選択してください";
    setErrors(e);
    if (Object.keys(e).length) return;
    const s = STAFF_BY_ID[staffId];
    onInvite({ staffId, email: s.email, role, note: "" });
  };

  return (
    <Modal
      open
      onClose={onClose}
      title="ユーザーを招待"
      subtitle="スタッフマスターから選択してアクセス権を付与します"
      footer={
        <>
          <span className="grow text-xs faint">
            <Icon name="info" size={12} /> 招待後、本人がサインインするまで「招待中」状態
          </span>
          <Button onClick={onClose}>キャンセル</Button>
          <Button variant="accent" onClick={submit}>招待を送信</Button>
        </>
      }
    >
      <div className="form-grid">
        <Field label="スタッフ" required error={errors.staffId} span={2}
          hint={`現在 ${eligibleStaff.length} 名のスタッフが招待可能（既にユーザー登録済みのスタッフは除外）`}>
          <ComboBox
            value={staffId}
            onChange={setStaffId}
            placeholder="スタッフを検索・選択..."
            options={eligibleStaff.map(s => ({
              value: s.id,
              label: `${s.lastName} ${s.firstName}`,
              sub: `${s.code} · ${DEPT_BY_ID[s.dept]?.name || ""} · ${s.email}`,
              searchKey: `${s.lastName} ${s.firstName} ${s.lastKana} ${s.firstKana} ${s.code} ${s.email}`,
              leading: <Avatar id={s.id} name={initialsFor(s)} size="sm" />,
            }))}
          />
        </Field>

        {staffId && (
          <Field label="送信先メールアドレス" span={2} hint="スタッフマスターのメールアドレスに招待を送信します">
            <Input value={STAFF_BY_ID[staffId]?.email || ""} readOnly />
          </Field>
        )}

        <div className="form-section-title">付与するロール</div>
        <Field span={2}>
          <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
            {Object.entries(USER_ROLES).map(([k, v]) => (
              <label
                key={k}
                style={{
                  display: "flex", alignItems: "center", gap: 10,
                  padding: "10px 12px",
                  border: `1px solid ${role === k ? "var(--accent)" : "var(--border)"}`,
                  borderRadius: 6,
                  background: role === k ? "var(--accent-soft)" : "var(--surface)",
                  cursor: "pointer",
                }}
              >
                <input
                  type="radio"
                  checked={role === k}
                  onChange={() => setRole(k)}
                  style={{ accentColor: "var(--accent)" }}
                />
                <span style={{ flex: 1 }}>
                  <span style={{ fontSize: 13, fontWeight: 500, display: "block" }}>{v.label}</span>
                  <span style={{ fontSize: 11.5, color: "var(--text-subtle)" }}>{v.description}</span>
                </span>
              </label>
            ))}
          </div>
        </Field>
      </div>
    </Modal>
  );
};

// ============================================
// AUDIT LOG SCREEN
// ============================================
const AuditLogScreen = ({ log }) => {
  const [q, setQ] = useState("");
  const [entityFilter, setEntityFilter] = useState("");
  const [actionFilter, setActionFilter] = useState("");

  const filtered = useMemo(() => {
    let r = log;
    if (entityFilter) r = r.filter(l => l.entity === entityFilter);
    if (actionFilter) r = r.filter(l => l.action === actionFilter);
    if (q) {
      const ql = q.toLowerCase();
      r = r.filter(l =>
        l.entityId.toLowerCase().includes(ql) ||
        l.after.toLowerCase().includes(ql) ||
        l.before.toLowerCase().includes(ql) ||
        l.note.toLowerCase().includes(ql)
      );
    }
    return r;
  }, [log, q, entityFilter, actionFilter]);

  const columns = [
    { key: "ts", label: "日時", width: 150, render: r => <span className="mono">{r.ts}</span> },
    { key: "user", label: "実行者", width: 160,
      render: r => {
        const s = STAFF_BY_ID[r.user];
        return s ? <AvatarName staff={s} sub={null} /> : <span className="mono">{r.user}</span>;
      }
    },
    { key: "entity", label: "対象", width: 100,
      render: r => (
        <span style={{ display: "inline-flex", alignItems: "center", gap: 6, color: "var(--text-muted)" }}>
          <Icon name={ENTITY_ICON[r.entity]} size={13} />
          {ENTITY_LABEL[r.entity]}
        </span>
      )
    },
    { key: "entityId", label: "ID", width: 80, render: r => <span className="mono">{r.entityId}</span> },
    { key: "action", label: "操作", width: 80,
      render: r => <Badge tone={r.action === "create" ? "success" : r.action === "delete" ? "danger" : "info"}>
        {r.action === "create" ? "作成" : r.action === "delete" ? "削除" : "更新"}
      </Badge> },
    { key: "field", label: "項目", width: 100, render: r => <span className="muted">{r.field}</span> },
    { key: "before", label: "変更前", minWidth: 140, render: r => <span className="muted mono">{r.before}</span> },
    { key: "after", label: "変更後", minWidth: 140, render: r => r.after },
    { key: "note", label: "備考", minWidth: 140, render: r => <span className="muted">{r.note}</span> },
  ];

  return (
    <>
      <div className="list-header">
        <div className="list-title-row">
          <h1 className="list-title">変更履歴</h1>
          <Badge>{filtered.length} 件</Badge>
          <div style={{ flex: 1 }} />
          <Button icon="download" onClick={() => downloadCSV("audit_log.csv", columns.filter(c => c.key !== "user").concat([{ key: "user", label: "実行者", value: r => r.user }]).map(c => ({ label: c.label, value: c.value || (r => r[c.key]) })), filtered)}>
            CSVエクスポート
          </Button>
        </div>
        <div className="list-toolbar">
          <div className="grow">
            <SearchInput value={q} onChange={setQ} placeholder="ID・内容・備考で検索…" />
          </div>
          <div style={{ width: 160 }}>
            <Select value={entityFilter} onChange={setEntityFilter} placeholder="対象（すべて）"
              options={Object.entries(ENTITY_LABEL).map(([k, v]) => ({ value: k, label: v }))} />
          </div>
          <div style={{ width: 130 }}>
            <Select value={actionFilter} onChange={setActionFilter} placeholder="操作（すべて）"
              options={[{ value: "create", label: "作成" }, { value: "update", label: "更新" }, { value: "delete", label: "削除" }]} />
          </div>
        </div>
      </div>

      <DataTable columns={columns} rows={filtered} />
    </>
  );
};

// ============================================
// CSV Import modal (mock)
// ============================================
const CSVImportModal = ({ open, onClose, onImport }) => {
  const [stage, setStage] = useState("drop"); // drop | preview | done
  const [file, setFile] = useState(null);
  useEffect(() => { if (open) { setStage("drop"); setFile(null); } }, [open]);

  const onFile = (f) => {
    setFile(f);
    setTimeout(() => setStage("preview"), 300);
  };
  const doImport = () => {
    setStage("done");
    setTimeout(() => { onImport(); onClose(); }, 600);
  };

  return (
    <Modal
      open={open} onClose={onClose}
      title="スタッフCSVインポート"
      subtitle="UTF-8 (BOM可) / 1行目をヘッダーとして読み込みます"
      footer={
        stage === "preview"
          ? <>
              <span className="grow text-sm muted">3件中3件が新規追加されます</span>
              <Button onClick={onClose}>キャンセル</Button>
              <Button variant="accent" onClick={doImport}>インポート実行</Button>
            </>
          : <><span className="grow" /><Button onClick={onClose}>閉じる</Button></>
      }
    >
      {stage === "drop" && (
        <div
          style={{ border: "2px dashed var(--border-strong)", borderRadius: 8, padding: 36, textAlign: "center", color: "var(--text-subtle)" }}
          onDragOver={e => e.preventDefault()}
          onDrop={e => { e.preventDefault(); const f = e.dataTransfer.files?.[0]; if (f) onFile(f); }}
        >
          <Icon name="upload" size={28} style={{ marginBottom: 8, color: "var(--text-faint)" }} />
          <div style={{ fontSize: 13, color: "var(--text)" }}>CSVファイルをドラッグ&ドロップ</div>
          <div style={{ fontSize: 11.5, marginTop: 4 }}>または <a href="#" style={{ color: "var(--accent)" }} onClick={(e) => { e.preventDefault(); onFile({ name: "staff_import.csv" }); }}>ファイルを選択</a></div>
          <div style={{ marginTop: 14, fontSize: 11, color: "var(--text-faint)" }}>
            <a href="#" style={{ color: "var(--accent)" }}>テンプレートをダウンロード</a>
          </div>
        </div>
      )}
      {stage === "preview" && (
        <>
          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 12, fontSize: 12.5 }}>
            <Icon name="check" size={14} style={{ color: "var(--success)" }} />
            <strong>{file?.name}</strong>
            <span className="muted">· 3行を検出</span>
          </div>
          <div className="table-wrap" style={{ fontSize: 11.5 }}>
            <table className="data-table">
              <thead>
                <tr><th>社員番号</th><th>姓</th><th>名</th><th>メール</th><th>部署</th><th>状態</th></tr>
              </thead>
              <tbody>
                <tr><td className="mono">A0031</td><td>新井</td><td>真由</td><td>arai.m@adfproject.jp</td><td>制作進行</td><td><Badge tone="success">新規</Badge></td></tr>
                <tr><td className="mono">A0032</td><td>西村</td><td>誠</td><td>nishimura.m@adfproject.jp</td><td>原画</td><td><Badge tone="success">新規</Badge></td></tr>
                <tr><td className="mono">A0033</td><td>大野</td><td>颯太</td><td>ono.s@adfproject.jp</td><td>動画</td><td><Badge tone="success">新規</Badge></td></tr>
              </tbody>
            </table>
          </div>
        </>
      )}
      {stage === "done" && (
        <div style={{ textAlign: "center", padding: 24 }}>
          <Icon name="check" size={32} style={{ color: "var(--success)" }} />
          <div style={{ fontSize: 13, marginTop: 8 }}>インポートが完了しました</div>
        </div>
      )}
    </Modal>
  );
};

Object.assign(window, {
  LoginScreen, Sidebar, TopBar, MenuScreen,
  StaffScreen, WorkScreen, DeptScreen, RoleScreen, ClientScreen,
  AccessUsersScreen, AuditLogScreen, CSVImportModal, CategoryManagerModal,
});
