// screen_files.jsx — File Room (knowledge base, uploads, staging, provenance) — merged from office FilesPage
(function () {
  const { useState, useEffect } = React;
  const h = React.createElement;
  window.Screens = window.Screens || {};
  const { Card, CardHead, Badge, RiskBadge, Button, KPI, Avatar, PageHeader, Segmented, Toggle, ProgressBar, EmptyHint, FutureStep, Modal, Section, Icon } = window;
  const { RoleIcon, RoleDot, InlineIcon, StatusIcon, Chip, levelChip, caveatLine, Seg3, AUDIT_TONE } = window;

  // English short labels per office role id (role.short is 繁中 data — would leak under EN; C2/C3 DISPLAY map).
  const SHORT_EN = { clerk: "Admin Assistant", cs: "Customer Support Assistant", sales: "Sales Assistant", social: "Marketing Assistant", acct: "Accounting Assistant" };
  // file.kind is 繁中 DATA (價目表/FAQ/範本/條款/單據). LOGIC keeps 繁中; DISPLAY maps to English so
  // the dict (English-keyed) localizes it across EN/繁/简 (C3). Unknown kinds pass through.
  const KIND_EN = { "價目表": "Price List", "FAQ": "FAQ", "範本": "Template", "條款": "Terms", "單據": "Receipt", "名片": "Business Card", "術語表": "Glossary", "報表": "Report" };
  const kindEn = (k) => KIND_EN[k] || k;
  // Seed data keeps a few short Chinese date/status values as persisted contracts.
  // Convert them to English source strings before rendering so the shared i18n
  // runtime can translate each label in either direction.
  const dateEn = (d) => ({ "今天": "Today", "昨天": "Yesterday", "6 月 9 日": "June 9" })[d] || d;
  const RISK_EN = { "高": "High", "中": "Medium", "低": "Low", High: "High", Medium: "Medium", Low: "Low" };
  const TASK_STAGE_EN = {
    Intake: "New",
    Planning: "Plan",
    Running: "In progress",
    "Needs Approval": "Needs approval",
    Completed: "Done",
  };
  const RUN_STATUS_EN = {
    "awaiting-approval": "Waiting for approval",
    awaiting_approval: "Waiting for approval",
    "pending-approval": "Waiting for approval",
    buffering: "Waiting to send · can undo",
    sent: "Sent",
    filed: "Saved",
    recalled: "Undone",
    rejected: "Returned",
    nosource: "Stopped — file missing",
    "source-blocked": "Stopped — file missing",
    source_gap_blocked: "Stopped — file missing",
    running: "Working",
  };
  const CITATION_EN = {
    "宏圖五金 報價單 QT-2026-0612（今天）": "Quote QT-2026-0612 (today)",
    "刻字服務私訊回覆（昨天）": "Engraving service message reply (yesterday)",
    "618 優惠貼文價格核對（6 月 8 日）": "618 promotion price check (June 8)",
    "利安食品 保養查詢回覆（今天 08:42）": "Warranty reply (today 08:42)",
    "最低訂購量 FAQ 回覆（昨天）": "Minimum-order reply (yesterday)",
    "蒸焗爐使用查詢（6 月 9 日）": "Oven use reply (June 9)",
    "618 貼文保冷聲稱核對（6 月 11 日）": "618 cooling claim check (June 11)",
    "代理分銷合約風險比對（6 月 5 日）": "Distributor contract risk check (June 5)",
    "報價單 QT-2026-0612 條款（今天）": "Quote QT-2026-0612 terms (today)",
    "發票付款條件引用（示範）": "Invoice payment terms (sample)",
    "報價單 QT-2026-0612 運費（今天）": "Quote QT-2026-0612 delivery fee (today)",
    "屋苑會所提案初稿結構（6 月 7 日）": "Clubhouse proposal outline (June 7)",
    "保養條款中英對照（示範）": "Warranty terms translation (sample)",
    "6 月應付帳款核對（今天）": "June payable accounts check (today)",
    "送貨記錄歸檔（示範）": "Delivery record saved (sample)",
    "展會名片轉客戶記錄（6 月 9 日）": "Business cards added to customer list (June 9)",
    "報價單 QT-2026-0612 套用（今天）": "Quote QT-2026-0612 template (today)",
    "退貨查詢回覆（昨天）": "Return request reply (yesterday)",
    "618 退換貨聲稱核對（6 月 11 日）": "618 return policy check (June 11)",
    "上週銷售數據整理（今天 09:00）": "Weekly sales table (today 09:00)",
  };

  function formatCopy(key, values) {
    if (window.I18N && window.I18N.format) return window.I18N.format(key, values);
    return String(key).replace(/\{([A-Za-z0-9_]+)\}/g, (match, name) => Object.prototype.hasOwnProperty.call(values || {}, name) ? String(values[name]) : match);
  }
  function timeParts(value) {
    return String(value || "").split(/\s*·\s*/).filter(Boolean).map((part) => {
      if (part === "剛剛") return "Just now";
      if (part === "夜間排程") return "Overnight schedule";
      let match = part.match(/^今天\s+([0-2]?\d:[0-5]\d)$/);
      if (match) return "Today " + match[1];
      match = part.match(/^昨天\s+([0-2]?\d:[0-5]\d)$/);
      if (match) return "Yesterday " + match[1];
      return dateEn(part);
    });
  }
  function citationDisplay(value, index) {
    const source = String(value || "");
    if (CITATION_EN[source]) return CITATION_EN[source];
    if (!/[\u3400-\u9fff]/.test(source)) return source;
    return formatCopy("Usage record {count}", { count: Number(index || 0) + 1 });
  }
  function folderPathNode(value) {
    const labels = { "單據": "Receipts", "銷售": "Sales", "名片": "Business Cards" };
    const parts = String(value || "").split(/\s*[／/]\s*/).filter(Boolean).map((part) => labels[part] || part);
    return h("span", null, parts.map((part, index) => h(React.Fragment, { key: index }, index ? h("span", { "aria-hidden": "true" }, " / ") : null, h("span", null, part))));
  }
  function metaParts(parts) {
    const clean = (parts || []).filter((part) => part !== null && part !== undefined && part !== "");
    return clean.map((part, index) => h(React.Fragment, { key: index },
      index ? h("span", { "aria-hidden": "true" }, " · ") : null,
      h("span", null, part)));
  }

  const CANNED_UPLOADS = [
    { name: "WhatsApp Image 2026-06-12.jpg", from: "Mock upload · just now", source: "upload", suggestedName: "2026-06_Maxim Tableware_damage-photo_01.jpg", suggestedFolder: "Receipts/2026/06", targetFolder: "receipts" },
    { name: "quote_final_v2(3).pdf", from: "Mock upload · just now", source: "upload", suggestedName: "2026-06_Voyage Education_gift-box-quote_QT-0613.pdf", suggestedFolder: "Sales/Quotes", targetFolder: "quotes" },
  ];
  let uploadCursor = 0;

  function uploadSizeLabel(bytes) {
    if (!bytes && bytes !== 0) return "—";
    if (bytes < 1024) return bytes + " B";
    if (bytes < 1024 * 1024) return Math.round(bytes / 1024) + " KB";
    return (bytes / (1024 * 1024)).toFixed(bytes < 10 * 1024 * 1024 ? 1 : 0) + " MB";
  }
  function uploadExt(name) {
    const m = String(name || "").match(/(\.[A-Za-z0-9]{1,8})$/);
    return m ? m[1].toLowerCase() : "";
  }
  function uploadBase(name) {
    return String(name || "uploaded-document")
      .replace(/(\.[A-Za-z0-9]{1,8})$/, "")
      .replace(/[\\/:*?"<>|]+/g, " ")
      .trim()
      .replace(/\s+/g, "-")
      .replace(/[-_]{2,}/g, "-")
      .replace(/^-+|-+$/g, "") || "uploaded-document";
  }
  function classifyUpload(file) {
    const raw = String((file && file.name) || "uploaded-document");
    const name = raw.toLowerCase();
    if (/faq|policy|support|refund|return|退換|退货|退貨|退款|保養|warranty/.test(name)) return { kind: "FAQ", folder: "faq", label: "Knowledge & FAQ", tags: ["support", "rule"], usedBy: ["cs", "social"] };
    if (/contract|agreement|terms|條款|条款|合約|合同/.test(name)) return { kind: "條款", folder: "contracts", label: "Contracts", tags: ["legal"], usedBy: ["clerk", "acct"] };
    if (/template|proposal|範本|模板|提案/.test(name)) return { kind: "範本", folder: "templates", label: "Templates", tags: ["template"], usedBy: ["clerk", "sales"] };
    if (/business.?card|name.?card|名片/.test(name)) return { kind: "名片", folder: "cards", label: "Sales/Business Cards", tags: ["leads"], usedBy: ["sales"] };
    if (/quote|price|pricing|catalog|價目|价格|價格|報價|报价|運費|shipping/.test(name)) return { kind: "價目表", folder: "quotes", label: "Sales/Quotes", tags: ["pricing"], usedBy: ["sales", "cs"] };
    return { kind: "單據", folder: "receipts", label: "Receipts/2026/07", tags: ["upload"], usedBy: ["clerk", "acct"] };
  }
  function droppedUploadItem(file, index) {
    const cls = classifyUpload(file);
    const original = String((file && file.name) || "uploaded-document");
    const ext = uploadExt(original) || ".pdf";
    const base = uploadBase(original);
    return {
      id: "drop-" + Date.now().toString(36) + "-" + index + "-" + Math.floor(Math.random() * 1e5).toString(36),
      name: original,
      from: "Browser drop · just now",
      source: "upload",
      suggestedName: "2026-07_" + base + ext,
      suggestedFolder: cls.label,
      targetFolder: cls.folder,
      kind: cls.kind,
      size: uploadSizeLabel(file && file.size),
      tags: cls.tags.slice(),
      usedBy: cls.usedBy.slice(),
      citedBy: [],
      refs: 0,
      origin: "browser-drop",
      note: "Added to Files for review.",
    };
  }

  // view-state: own v1 guard, NOT office state — never bumps dataVersion.
  const VIEW_KEY = "emplix-files-view-v1";
  function loadView() {
    try { const v = JSON.parse(localStorage.getItem(VIEW_KEY)); if (v && v.v === 1) return v; } catch (e) {}
    return { v: 1, scope: { t: "all" }, group: "folder", sort: "cited", grid: false };
  }
  function routePart(v) { return encodeURIComponent(String(v || "")); }
  function decodeRoutePart(v) {
    try { return decodeURIComponent(String(v || "")); } catch (e) { return String(v || ""); }
  }
  function fileScopeArg(scope) {
    if (!scope || scope.t === "all") return null;
    if ((scope.t === "folder" || scope.t === "source" || scope.t === "tag" || scope.t === "smart") && scope.id) {
      return scope.t + "/" + routePart(scope.id);
    }
    return null;
  }
  function parseFileArg(arg, files, folders, office) {
    if (!arg) return { kind: "list", scope: null, file: null };
    const directFile = (files || []).find((f) => f.id === decodeRoutePart(arg)) || null;
    if (directFile) return { kind: "file", scope: null, file: directFile };
    const parts = String(arg).split("/");
    const head = parts.shift();
    const rest = decodeRoutePart(parts.join("/"));
    const fileById = (id) => (files || []).find((f) => f.id === id) || null;
    const folderById = (id) => (folders || []).find((fo) => fo.id === id) || null;
    if (head === "all") return { kind: "list", scope: { t: "all" }, file: null };
    if (head === "file") {
      const file = fileById(rest);
      return file ? { kind: "file", scope: null, file } : { kind: "invalid" };
    }
    if (head === "folder") {
      return folderById(rest) ? { kind: "scope", scope: { t: "folder", id: rest }, file: null } : { kind: "invalid" };
    }
    if (head === "source") {
      const connectors = (office && office.connectors) || [];
      const hasSource = connectors.some((c) => c.id === rest) || (files || []).some((f) => f.source === rest) || !!SRC_NAME[rest];
      return hasSource ? { kind: "scope", scope: { t: "source", id: rest }, file: null } : { kind: "invalid" };
    }
    if (head === "tag") {
      return rest ? { kind: "scope", scope: { t: "tag", id: rest }, file: null } : { kind: "invalid" };
    }
    if (head === "smart") {
      return rest === "pending" || rest === "recent" ? { kind: "scope", scope: { t: "smart", id: rest }, file: null } : { kind: "invalid" };
    }
    const file = fileById(decodeRoutePart(arg));
    return file ? { kind: "file", scope: null, file } : { kind: "invalid" };
  }
  const SRC_NAME = { upload: "Upload", manual: "Manual" };
  function parseSize(s) { if (!s) return 0; const m = String(s).match(/([\d.]+)\s*(KB|MB|GB|B)?/i); if (!m) return 0; const n = parseFloat(m[1]) || 0; const u = (m[2] || "B").toUpperCase(); return n * ({ B: 1, KB: 1e3, MB: 1e6, GB: 1e9 }[u] || 1); }
  function displaySize(s) {
    const bytes = parseSize(s);
    if (!bytes) return "Size not shown";
    if (bytes < 1024 * 1024) return "Small file";
    if (bytes < 5 * 1024 * 1024) return "Medium file";
    return "Large file";
  }
  function childIds(folders, id) { const out = [id]; folders.forEach((fo) => { if (fo.parent === id) out.push(fo.id); }); return out; }
  function inScope(f, scope, folders) {
    if (!scope || scope.t === "all") return true;
    if (scope.t === "folder") return childIds(folders, scope.id).indexOf(f.folder) >= 0;
    if (scope.t === "source") return f.source === scope.id;
    if (scope.t === "tag") return (f.tags || []).indexOf(scope.id) >= 0;
    return true;
  }
  function sortFiles(arr, key) {
    const cmp = {
      name: (a, b) => a.name.localeCompare(b.name),
      cited: (a, b) => (b.refs || 0) - (a.refs || 0),
      new: (a, b) => (a.uploaded < b.uploaded ? 1 : a.uploaded > b.uploaded ? -1 : 0),
      size: (a, b) => parseSize(b.size) - parseSize(a.size),
    }[key] || ((a, b) => (b.refs || 0) - (a.refs || 0));
    return arr.slice().sort(cmp);
  }
  function srcName(office, id) { const c = (office.connectors || []).find((x) => x.id === id); return (c && c.name) || SRC_NAME[id] || id || "Upload"; }
  function groupFiles(arr, key, office) {
    if (key === "none") return [{ key: "all", label: null, items: arr }];
    const map = {}, order = [];
    const push = (k, label, f) => { if (!map[k]) { map[k] = { key: k, label: label, items: [] }; order.push(k); } map[k].items.push(f); };
    arr.forEach((f) => {
      if (key === "type") { const t = kindEn(f.kind); push(t, t, f); }
      else if (key === "source") { push(f.source || "upload", srcName(office, f.source || "upload"), f); }
      else if (key === "employee") { const u = f.usedBy || []; if (!u.length) push("_none", "Unassigned", f); else u.forEach((id) => push(id, SHORT_EN[id] || id, f)); }
      else { const fo = (office.folders || []).find((x) => x.id === f.folder); push(f.folder || "_un", (fo && fo.name) || "No folder", f); }
    });
    return order.map((k) => map[k]);
  }
  function srcTone(c) { if (!c) return "var(--text-faint)"; if (c.status !== "已連接") return "var(--border-2)"; return c.health === "ok" ? "var(--green)" : "var(--amber)"; }
  function srcLogo(id) { const B = window.BRANDS || {}; const b = B[id]; return b ? h("span", { className: "fr-srclogo", style: { background: b.bg } }, b.svg()) : null; }

  function Files({ ctx }) {
    const office = ctx.office;
    const folders = office.folders || [];
    const files = office.files || [];
    const sourceApprovalRequired = !ctx.settings || ctx.settings.requireSourceWriteApproval !== false;
    const [view, setViewState] = useState(loadView);
    const [q, setQ] = useState("");
    const [prog, setProg] = useState(-1);
    const [dragOver, setDragOver] = useState(false);
    const [pop, setPop] = useState(null);          // citation popover { n:fileId, x, y }
	    const [menu, setMenu] = useState(null);         // row menu { id:fileId, x, y, sub }
	    const [renaming, setRenaming] = useState(null); // fileId being renamed inline
	    const [traceRunId, setTraceRunId] = useState(null);
	    const [fileBackArg, setFileBackArg] = useState(null);
	    const [folderModal, setFolderModal] = useState(false);
	    const [folderName, setFolderName] = useState("");
	    const [folderParentId, setFolderParentId] = useState("");
	    const [folderError, setFolderError] = useState("");
	    const [renameModalId, setRenameModalId] = useState(null);
	    const [renameValue, setRenameValue] = useState("");
	    const [renameError, setRenameError] = useState("");
	    const setView = (patch) => { const nv = Object.assign({}, view, patch); setViewState(nv); try { localStorage.setItem(VIEW_KEY, JSON.stringify(nv)); } catch (e) {} };
	    const setScope = (scope, opts) => {
	      const nextScope = scope || { t: "all" };
      setView({ scope: nextScope });
      if (opts && opts.route === false) return;
      setTraceRunId(null);
      setPop(null);
      setMenu(null);
      if (q) setQ("");
      ctx.openPage("files", fileScopeArg(nextScope));
    };
    const startMockUpload = () => { if (ctx.can("addUpload") && prog < 0) setProg(0); };
    const handleDrop = (e) => {
      e.preventDefault(); e.stopPropagation(); setDragOver(false);
      if (!ctx.can("addUpload") || prog >= 0) return;
      const dropped = Array.from(((e.dataTransfer || {}).files) || []).filter(Boolean).slice(0, 12);
      if (!dropped.length) { startMockUpload(); return; }
      let lastId = null;
      dropped.forEach((file, index) => {
        const item = droppedUploadItem(file, index);
        lastId = ctx.addUpload(item) || item.id;
      });
      if (lastId && ctx.setHighlight) ctx.setHighlight(lastId);
      setScope(sourceApprovalRequired ? { t: "smart", id: "pending" } : { t: "smart", id: "recent" });
      const toastKey = sourceApprovalRequired
        ? (dropped.length === 1 ? "Added {count} file for review." : "Added {count} files for review.")
        : (dropped.length === 1 ? "Added {count} file." : "Added {count} files.");
      ctx.showToast(formatCopy(toastKey, { count: dropped.length }));
    };
    const handleDragOver = (e) => {
      e.preventDefault(); e.stopPropagation();
      if (ctx.can("addUpload") && prog < 0) setDragOver(true);
    };

    useEffect(() => { if (prog < 0 || prog >= 100) return; const t = setTimeout(() => setProg((p) => Math.min(100, p + 5)), 80); return () => clearTimeout(t); }, [prog]);
    useEffect(() => { if (prog !== 100) return;
      const canned = CANNED_UPLOADS[uploadCursor % CANNED_UPLOADS.length]; uploadCursor += 1;
      ctx.addUpload(Object.assign({ id: "up" + Date.now() }, canned));
      ctx.showToast(sourceApprovalRequired ? "Added for review. The Admin Assistant suggested a name and folder." : "Added to Files. File approval is off."); setProg(-1);
      setScope(sourceApprovalRequired ? { t: "smart", id: "pending" } : { t: "smart", id: "recent" });
    }, [prog]);
    useEffect(() => { if (!pop && !menu) return; const close = () => { setPop(null); setMenu(null); }; document.addEventListener("click", close); return () => document.removeEventListener("click", close); }, [pop, menu]);
    useEffect(() => {
      const fileTarget = ctx.highlight && files.find((f) => f.id === ctx.highlight);
      const stagingTarget = ctx.highlight && (office.staging || []).find((st) => st.id === ctx.highlight);
      if (!fileTarget && !stagingTarget) return;
      if (fileTarget && ctx.pageArg !== fileTarget.id) ctx.openPage("files", fileTarget.id);
      if (stagingTarget && ctx.pageArg) ctx.openPage("files");
      setTraceRunId(null);
      setScope(fileTarget ? { t: "all" } : { t: "smart", id: "pending" }, fileTarget ? { route: false } : null);
      if (q) setQ("");
      const focus = setTimeout(() => {
        const attr = fileTarget ? "data-file-id" : "data-staging-id";
        const selector = '[' + attr + '="' + String(ctx.highlight).replace(/"/g, '\\"') + '"]';
        const el = document.querySelector(selector);
        if (el && el.scrollIntoView) el.scrollIntoView({ block: "center", behavior: "smooth" });
      }, 80);
      const clear = setTimeout(() => { if (ctx.setHighlight) ctx.setHighlight(null); }, 4000);
      return () => { clearTimeout(focus); clearTimeout(clear); };
    }, [ctx.highlight]);
    const fileRoute = parseFileArg(ctx.pageArg, files, folders, office);
    const routeScope = fileRoute.scope || null;
    const scope = routeScope || view.scope || { t: "all" };
    const isSmart = scope.t === "smart";
    const canOrg = ctx.can("organizeFiles");
    const countIn = (folderId) => files.filter((f) => childIds(folders, folderId).indexOf(f.folder) >= 0).length;
    const topFolders = folders.filter((fo) => !fo.parent);
    const drives = (office.connectors || []).filter((c) => c.defaultFolder);
    const selectedFile = fileRoute.kind === "file" ? fileRoute.file : null;
    const selectedFileId = selectedFile ? selectedFile.id : null;
    useEffect(() => {
      if (!routeScope) return;
      const current = view.scope || { t: "all" };
      if (current.t !== routeScope.t || current.id !== routeScope.id) setView({ scope: routeScope });
    }, [ctx.pageArg, routeScope && routeScope.t, routeScope && routeScope.id]);
    useEffect(() => {
      if (ctx.pageArg && fileRoute.kind === "invalid") {
        setView({ scope: { t: "all" } });
        ctx.openPage("files");
        return;
      }
      if (!selectedFile) return;
      if ((scope.t === "smart" || !inScope(selectedFile, scope, folders))) setScope({ t: "all" }, { route: false });
      if (q) setQ("");
    }, [ctx.pageArg, files.length, folders.length]);
    function openFileId(fileId, traceId) {
      if (!fileId) return;
      setFileBackArg(fileScopeArg(scope));
      setTraceRunId(traceId || null);
      setPop(null);
      setMenu(null);
      ctx.openPage("files", fileId);
    }
    function openFile(f) {
      if (!f) return;
      openFileId(f.id);
    }
    function fileKeyOpen(e, f) {
      if (e.key === "Enter" || e.key === " ") {
        e.preventDefault();
        openFile(f);
      }
    }

    const ql = q.trim().toLowerCase();
    let visible = files.filter((f) => inScope(f, scope, folders));
    if (ql) visible = visible.filter((f) => (f.name + " " + (f.tags || []).join(" ") + " " + kindEn(f.kind)).toLowerCase().indexOf(ql) >= 0);
    const groups = groupFiles(sortFiles(visible, view.sort), view.group, office);

    // ---- left rail ----
    const railItem = (key, active, onClick, icoEl, label, countEl, extraClass) =>
      h("button", { key: key, className: "fr-item" + (active ? " on" : "") + (extraClass ? " " + extraClass : ""), onClick: onClick },
        icoEl ? h("span", { className: "fr-ico" }, icoEl) : null, h("span", { className: "grow" }, label), countEl || null);

    const rail = h("div", { className: "fr-rail" },
      h("div", { className: "fr-railzone" },
	        h("div", { className: "fr-grp" }, h("span", null, "Files")),
        railItem("all", scope.t === "all", () => setScope({ t: "all" }), h(Icon, { name: "folder", size: 14 }), "All files", h("span", { className: "fr-count" }, "" + files.length)),
        railItem("pending", isSmart && scope.id === "pending", () => setScope({ t: "smart", id: "pending" }), h(Icon, { name: "inbox", size: 14 }), "Needs review", office.staging.length ? h("span", { className: "fr-count" }, "" + office.staging.length) : null),
        railItem("recent", isSmart && scope.id === "recent", () => setScope({ t: "smart", id: "recent" }), h(Icon, { name: "clock", size: 14 }), "Recent", office.recentFiled.length ? h("span", { className: "fr-count" }, "" + office.recentFiled.length) : null)),
      h("div", { className: "fr-railzone" },
	        h("div", { className: "fr-grp" }, h("span", { className: "grow" }, "Folders"),
	          h("button", { className: "fr-item fr-add", style: { width: "auto", padding: "2px 6px" }, disabled: !canOrg, title: canOrg ? "" : ctx.denyReason("organizeFiles"),
	            "data-file-action": "new-folder",
	            onClick: (e) => { e.stopPropagation(); if (!canOrg) return; openFolderModal(scope.t === "folder" ? scope.id : ""); } }, "+ New")),
	        topFolders.map((fo) => h(React.Fragment, { key: fo.id },
	          railItem(fo.id, scope.t === "folder" && scope.id === fo.id, () => setScope({ t: "folder", id: fo.id }), h(Icon, { name: "folder", size: 14 }), fo.name, h("span", { className: "fr-count" }, "" + countIn(fo.id))),
          folders.filter((c) => c.parent === fo.id).map((c) =>
            railItem(c.id, scope.t === "folder" && scope.id === c.id, () => setScope({ t: "folder", id: c.id }), null, c.name, h("span", { className: "fr-count" }, "" + countIn(c.id)), "child"))))),
      h("div", { className: "fr-railzone" },
        h("div", { className: "fr-grp" }, h("span", { className: "grow" }, "Connected Apps")),
        drives.map((c) => h("button", { key: c.id, className: "fr-item" + (scope.t === "source" && scope.id === c.id ? " on" : ""), onClick: () => setScope({ t: "source", id: c.id }) },
          srcLogo(c.id) || h("span", { className: "fr-ico" }, h(Icon, { name: "folder", size: 14 })),
          h("span", { className: "grow" }, c.name),
          h("span", { className: "fr-srcdot", style: { background: srcTone(c) }, title: c.status === "已連接" ? (c.health === "ok" ? "Connected" : "Needs re-authorization") : "Not connected" }))),
        h("button", { className: "fr-item fr-add", onClick: () => ctx.openPage("connectors") }, h("span", { className: "fr-ico" }, h(Icon, { name: "plus", size: 14 })), h("span", { className: "grow" }, "Add an app"))));

    // ---- breadcrumb ----
    let crumb;
    if (scope.t === "folder") { const fo = folders.find((x) => x.id === scope.id); const par = fo && fo.parent && folders.find((x) => x.id === fo.parent); crumb = h("span", { className: "fr-crumb" }, h("span", null, "Folders"), h(Icon, { name: "chevRight", size: 12 }), par ? h("span", null, par.name) : null, par ? h(Icon, { name: "chevRight", size: 12 }) : null, h("b", null, (fo && fo.name) || "—")); }
    else if (scope.t === "source") { const c = (office.connectors || []).find((x) => x.id === scope.id); crumb = h("span", { className: "fr-crumb" }, h("span", null, "App"), h(Icon, { name: "chevRight", size: 12 }), h("b", null, (c && c.name) || scope.id)); }
    else if (scope.t === "tag") crumb = h("span", { className: "fr-crumb" }, h("span", null, "Tag"), h(Icon, { name: "chevRight", size: 12 }), h("b", null, "#", h("span", null, scope.id)));
    else if (isSmart && scope.id === "pending") crumb = h("span", { className: "fr-crumb" }, h("b", null, "Needs review"));
    else if (isSmart && scope.id === "recent") crumb = h("span", { className: "fr-crumb" }, h("b", null, "Recent"));
    else crumb = h("span", { className: "fr-crumb" }, h("b", null, "All files"));

    // ---- toolbar (file-list scopes only) ----
    const toolbar = h("div", { className: "fr-toolbar", "data-file-toolbar": "true", "data-file-query": q, "data-file-group": view.group, "data-file-sort": view.sort, "data-file-grid": view.grid ? "true" : "false" },
      h("label", { className: "fr-search" }, h(Icon, { name: "search", size: 14 }), h("input", { placeholder: "Search files", value: q, "aria-label": "Search files", "data-file-search": "true", onChange: (e) => setQ(e.target.value) })),
      h(Segmented, { value: view.group, onChange: (v) => setView({ group: v }), options: [{ value: "folder", label: "Folder" }, { value: "type", label: "Type" }, { value: "source", label: "App" }, { value: "employee", label: "Employee" }, { value: "none", label: "None" }].map((opt) => Object.assign({}, opt, { attrs: { "data-file-group-option": opt.value, "data-file-group-active": view.group === opt.value ? "true" : "false" } })) }),
      h("select", { className: "fr-sort", value: view.sort, "aria-label": "Sort files", "data-file-sort-select": "true", onChange: (e) => setView({ sort: e.target.value }) },
	        [["cited", "Most used"], ["new", "Newest"], ["name", "Name"], ["size", "Size"]].map((o) => h("option", { key: o[0], value: o[0] }, o[1]))),
      h(Button, { sm: true, variant: "ghost", icon: view.grid ? "menu" : "grid", "data-file-view-toggle": "true", "data-file-view-mode": view.grid ? "grid" : "list", onClick: () => setView({ grid: !view.grid }) }, view.grid ? "List" : "Grid"),
      h(Button, { sm: true, variant: "primary", icon: "upload", "data-file-action": "upload", disabled: !ctx.can("addUpload") || prog >= 0, title: ctx.can("addUpload") ? "" : ctx.denyReason("addUpload"), onClick: () => { if (ctx.can("addUpload") && prog < 0) setProg(0); } }, prog >= 0 ? "Uploading…" : "Upload"));

    // ---- shared cell builders ----
	    function citedBtn(f) {
	      const runs = relatedRuns(f);
	      const citations = (f.refs > 0 && f.citedBy && f.citedBy.length ? f.citedBy.map(citationDisplay) : ["Not used yet"]);
	      return h("div", { className: "pop-wrap" },
        h("button", { className: "btn btn-sm", "data-file-citations": "true", "data-file-citation-file-id": f.id, "aria-expanded": pop && pop.n === f.id ? "true" : "false", onClick: (e) => { e.stopPropagation(); const rc = e.currentTarget.getBoundingClientRect(); setMenu(null); setPop(pop && pop.n === f.id ? null : { n: f.id, x: rc.left, y: rc.bottom }); } }, h("span", null, (f.refs || 0) + "× "), h(Icon, { name: "chevDown", size: 12 })),
        pop && pop.n === f.id && h("div", { className: "popover", "data-file-citation-popover": f.id, style: { position: "fixed", top: pop.y + 6, left: Math.max(8, Math.min(pop.x, (window.innerWidth || 400) - 280)) }, onClick: (e) => e.stopPropagation() },
	          h("div", { className: "popover-t" }, "Usage history"),
	          h("div", { className: "fr-provenance-list" }, citations.map((c, i) => h("button", { key: i, type: "button", className: "fr-cite-row", "data-file-citation-row": f.id, "data-citation-index": i, onClick: () => openFile(f) }, h("b", null, c), h("span", null, "Open file details")))),
	          runs.slice(0, 3).map((r) => h("div", { key: r.id, className: "fr-provenance-row", style: { marginTop: 8 } },
	            h("b", null, r.title || r.id),
	            h("span", null, metaParts([SHORT_EN[r.roleId] || "Employee", r.trigger && r.trigger.label ? r.trigger.label : "Work history"])),
	            h("div", { className: "fr-inspector-actions" },
	              r.workOrderId && inspectorButton("Task", () => ctx.openPage("workorder", r.workOrderId), "clipboard"),
	              inspectorButton("Work history", () => { openFile(f); setTraceRunId(r.id); }, "workflow"),
	              r.auditId && inspectorButton("History", () => ctx.openPage("audit-event", r.auditId), "scroll"))))));
	    }
    const tagChips = (f) => (f.tags || []).map((t) => h("button", { key: t, className: "fr-tag", title: "Filter by tag", "data-file-tag": f.id, "data-file-tag-value": t, onClick: (e) => { e.stopPropagation(); setScope({ t: "tag", id: t }); } }, "#", h("span", null, t)));
	    const usedByRow = (f) => h("div", { className: "row gap6 wrap" }, (f.usedBy || []).map((id) => h("span", { key: id, className: "row gap4", style: { fontSize: 12 } }, h(window.Face, { role: office.roleById[id], size: 18, variant: "head" }), h("span", null, SHORT_EN[id] || id))));
		    const srcBadge = (f) => h("span", { className: "fr-srcbadge" }, srcLogo(f.source) || h(Icon, { name: "upload", size: 12 }), h("span", null, srcName(office, f.source)));
	    const folderLabel = (id) => ((folders || []).find((x) => x.id === id) || {}).name || "No folder";
		    function folderPathLabel(fo) {
		      if (!fo) return "Top level";
		      const parent = fo.parent && folders.find((x) => x.id === fo.parent);
		      return parent ? parent.name + " / " + fo.name : fo.name;
		    }
		    function openFolderModal(parentId) {
		      setFolderName("");
		      setFolderParentId(parentId || "");
		      setFolderError("");
		      setFolderModal(true);
		      setPop(null);
		      setMenu(null);
		    }
		    function closeFolderModal() {
		      setFolderModal(false);
		      setFolderName("");
		      setFolderParentId("");
		      setFolderError("");
		    }
		    function createFolderFromModal() {
		      const name = folderName.trim();
		      if (!canOrg) { setFolderError(ctx.denyReason("organizeFiles")); return; }
		      if (!name) { setFolderError("Enter a folder name."); return; }
		      const id = ctx.addFolder(name, folderParentId || null);
		      closeFolderModal();
		      if (id) setScope({ t: "folder", id });
		    }
		    function openRenameModal(f) {
		      if (!f || !canOrg) return;
		      setRenameModalId(f.id);
		      setRenameValue(f.name || "");
		      setRenameError("");
		      setPop(null);
		      setMenu(null);
		    }
		    function closeRenameModal() {
		      setRenameModalId(null);
		      setRenameValue("");
		      setRenameError("");
		    }
		    function saveRenameModal() {
		      const file = renameModalId && files.find((x) => x.id === renameModalId);
		      const name = renameValue.trim();
		      if (!canOrg) { setRenameError(ctx.denyReason("organizeFiles")); return; }
		      if (!file) { setRenameError("This file is no longer available."); return; }
		      if (!name) { setRenameError("Enter a file name."); return; }
		      ctx.renameFile(file.id, name);
		      closeRenameModal();
		    }
		    const plainFileName = (name) => String(name || "").replace(/\.[^.]+$/, "");
		    function textMentionsFile(text, f) {
	      const hay = String(text || "");
	      const name = String(f && f.name || "");
	      const base = plainFileName(name);
	      return !!hay && !!name && (hay.indexOf(name) >= 0 || (!!base && hay.indexOf(base) >= 0));
	    }
	    function sourceMatchesFile(f, src) {
	      if (!f || !src) return false;
	      const label = src.file || src.name || src.title || "";
	      return src.fileId === f.id || label === f.name || textMentionsFile(label, f);
	    }
	    function runUsesFile(f, run) {
	      if (!f || !run) return false;
	      if (run.fileId === f.id) return true;
	      if ((run.sources || []).some((src) => sourceMatchesFile(f, src))) return true;
	      return (run.steps || []).some((s) => s.fileId === f.id || sourceMatchesFile(f, s) || sourceMatchesFile(f, s.source));
	    }
	    function workOrderById(id) {
	      const board = office.workOrders || {};
	      let hit = null;
	      Object.keys(board).forEach((col) => {
	        if (hit) return;
	        (board[col] || []).forEach((wo) => { if (!hit && wo.id === id) hit = Object.assign({ col }, wo); });
	      });
	      return hit;
	    }
	    function relatedRuns(f) {
	      return (office.runs || []).filter((r) => runUsesFile(f, r));
	    }
	    function relatedApprovals(f, runs) {
	      const runIds = new Set((runs || []).map((r) => r.id));
	      return (office.inbox || []).filter((item) =>
	        item.sourceFileId === f.id ||
	        textMentionsFile(item.source, f) ||
	        textMentionsFile(item.sourceSnippet, f) ||
	        (item.runId && runIds.has(item.runId)) ||
	        (item.evidence || []).some((ev) => textMentionsFile(ev.source, f) || textMentionsFile(ev.title, f)));
	    }
	    function relatedRecent(f) {
	      return (office.recentFiled || []).filter((rf) => rf.fileId === f.id || rf.newName === f.name || rf.oldName === f.name);
	    }
	    function relatedAudits(f, runs) {
	      const auditIds = new Set((runs || []).map((r) => r.auditId).filter(Boolean));
	      return (office.auditLog || []).filter((a) => auditIds.has(a.id) || textMentionsFile(a.title, f) || textMentionsFile(a.note, f));
	    }
	    function liveApprovalId(id) {
	      return id && (office.inbox || []).some((item) => item.id === id) ? id : null;
	    }
	    function runStatusText(run) {
	      return RUN_STATUS_EN[run && run.status] || "Task";
	    }
	    function taskStageText(column) {
	      return TASK_STAGE_EN[column] || (column ? "Task" : "");
	    }
	    function riskText(risk) {
	      return RISK_EN[risk] || (risk ? "Risk" : "");
	    }
	    function openApprovalOrAudit(id, run) {
	      const live = liveApprovalId(id);
	      if (live) {
	        if (ctx.setHighlight) ctx.setHighlight(live);
	        ctx.openPage("approvals");
	        return;
	      }
	      if (run && run.auditId) {
	        ctx.openPage("audit-event", run.auditId);
	        return;
	      }
	      ctx.showToast("This item is complete and no longer in My Desk.");
	    }
    function inspectorButton(label, onClick, icon, attrs) {
      return h("button", Object.assign({ type: "button", className: "ac-chip", onClick }, attrs || {}), icon && h(Icon, { name: icon, size: 12 }), h("span", null, label));
    }
	    function sectionBlock(title, children) {
	      return h("div", null, h("div", { className: "fr-grp", style: { margin: "0 0 7px" } }, title), children);
	    }
    const menuBtn = (f) => h("button", { className: "btn btn-sm", title: "More", "aria-label": "More actions for " + f.name, "data-file-menu-button": "true", "data-file-menu-file-id": f.id, "aria-expanded": menu && menu.id === f.id ? "true" : "false", onClick: (e) => { e.stopPropagation(); const rc = e.currentTarget.getBoundingClientRect(); setPop(null); setMenu(menu && menu.id === f.id ? null : { id: f.id, x: rc.right, y: rc.bottom, sub: null }); } }, h(Icon, { name: "more", size: 15 }));

	    function fileRow(f) {
	      const fo = folders.find((x) => x.id === f.folder);
	      const highlighted = ctx.highlight === f.id;
	      const selected = selectedFileId === f.id;
	      return h("div", { className: "fr-row" + (highlighted ? " onboard-hl" : "") + (selected ? " selected" : ""), "data-file-id": f.id, "data-file-folder": f.folder || "", "data-file-selected": selected ? "true" : "false", key: f.id, role: "button", tabIndex: 0, "aria-label": "Open file details for " + f.name, "aria-current": selected ? "true" : undefined, onClick: () => openFile(f), onKeyDown: (e) => fileKeyOpen(e, f) },
        h("div", { style: { minWidth: 0 } },
          renaming === f.id
            ? h("input", { className: "fr-rename-in", defaultValue: f.name, autoFocus: true, "aria-label": "Rename file", "data-file-rename-input": f.id, onClick: (e) => e.stopPropagation(),
                onKeyDown: (e) => { if (e.key === "Enter") { ctx.renameFile(f.id, e.target.value.trim()); setRenaming(null); } if (e.key === "Escape") setRenaming(null); },
                onBlur: (e) => { ctx.renameFile(f.id, e.target.value.trim()); setRenaming(null); } })
            : h("div", { className: "fr-name" }, f.name),
	          h("div", { className: "fr-meta" },
	            h(Chip, { tone: "gray" }, kindEn(f.kind)), h("span", null, displaySize(f.size)), h("span", null, "·"), h("span", null, dateEn(f.uploaded)),
	            fo ? h(React.Fragment, null, h("span", null, "·"), h("span", null, fo.name)) : null, srcBadge(f), h("span", { className: "fr-tags" }, tagChips(f)))),
        h("div", { className: "fr-rowactions" }, usedByRow(f), citedBtn(f), menuBtn(f)));
    }
	    function fileCard(f) {
	      const fo = folders.find((x) => x.id === f.folder);
	      const highlighted = ctx.highlight === f.id;
	      const selected = selectedFileId === f.id;
	      return h("div", { className: "fr-card" + (highlighted ? " onboard-hl" : "") + (selected ? " selected" : ""), "data-file-id": f.id, "data-file-folder": f.folder || "", "data-file-selected": selected ? "true" : "false", key: f.id, role: "button", tabIndex: 0, "aria-label": "Open file details for " + f.name, "aria-current": selected ? "true" : undefined, onClick: () => openFile(f), onKeyDown: (e) => fileKeyOpen(e, f) },
	        h("div", { className: "row gap8", style: { alignItems: "center" } }, srcLogo(f.source) || h(Icon, { name: "file", size: 16 }), h(Chip, { tone: "gray" }, kindEn(f.kind)), h("span", { className: "grow" }), menuBtn(f)),
	        h("div", { className: "fr-name", style: { fontSize: 13 } }, f.name),
	        h("div", { className: "fr-meta" }, h("span", null, displaySize(f.size)), h("span", null, "·"), h("span", null, dateEn(f.uploaded)), fo ? h(React.Fragment, null, h("span", null, "·"), h("span", null, fo.name)) : null),
	        h("div", { className: "fr-tags" }, tagChips(f)),
	        h("div", { className: "row gap8", style: { justifyContent: "space-between", alignItems: "center", marginTop: 2 } },
	          h("div", { className: "row gap6" }, (f.usedBy || []).slice(0, 4).map((id) => h(window.Face, { key: id, role: office.roleById[id], size: 18, variant: "head" }))), citedBtn(f)));
	    }
    function fileInspector(f) {
      const conn = (office.connectors || []).find((c) => c.id === f.source);
      const runs = relatedRuns(f);
      const approvals = relatedApprovals(f, runs);
      const recent = relatedRecent(f);
      const audits = relatedAudits(f, runs);
	      const citations = (f.citedBy && f.citedBy.length ? f.citedBy.map(citationDisplay) : []);
      const roleScopeButtons = (office.roles || []).map((r) => {
        const scoped = (f.usedBy || []).indexOf(r.id) >= 0;
        const label = SHORT_EN[r.id] || r.short || r.id;
        return h("button", { key: r.id, type: "button", className: "ac-chip", disabled: !canOrg, title: canOrg ? (scoped ? "Remove from " : "Assign to ") + label : ctx.denyReason("organizeFiles"),
          "data-file-employee-scope": f.id,
          "data-role-id": r.id,
          "data-file-employee-active": scoped ? "true" : "false",
          onClick: () => ctx.toggleFileEmployee(f.id, r.id),
          style: scoped ? { borderColor: "var(--border-glow)", background: "var(--blue-soft)", color: "var(--blue-deep)" } : null },
          h(window.Face, { role: r, size: 18, variant: "head" }),
          h("span", null, label),
          scoped && h(Icon, { name: "check", size: 12 }));
      });
      const field = (label, value) => h("div", { className: "fr-inspector-field" }, h("b", null, label), h("span", null, value || "—"));
		      const openLiveSource = (src) => {
		        if (src && src.fileId && ctx.setHighlight) ctx.setHighlight(src.fileId);
		        if (src && src.fileId) openFileId(src.fileId);
		        else ctx.openPage("files");
		      };
	      const runRow = (r) => {
	        const liveAp = liveApprovalId(r.inboxId);
	        const wo = r.workOrderId ? workOrderById(r.workOrderId) : null;
	        return h("div", { key: r.id, className: "fr-provenance-row" },
	          h("b", null, r.title || r.id),
	          h("span", null, metaParts([r.id, runStatusText(r), SHORT_EN[r.roleId] || "Employee", wo && taskStageText(wo.col)])),
	          h("div", { className: "fr-inspector-actions" },
	            r.workOrderId && inspectorButton("Task", () => ctx.openPage("workorder", r.workOrderId), "clipboard"),
	            liveAp && inspectorButton("My Desk", () => { if (ctx.setHighlight) ctx.setHighlight(liveAp); ctx.openPage("approvals"); }, "stamp"),
	            r.auditId && inspectorButton("History", () => ctx.openPage("audit-event", r.auditId), "scroll"),
	            inspectorButton(traceRunId === r.id ? "Hide history" : "Show history", () => setTraceRunId(traceRunId === r.id ? null : r.id), "workflow")),
	          traceRunId === r.id && h(window.RunTrace, { run: r,
	            onOpenSource: openLiveSource,
	            onOpenInbox: (id) => openApprovalOrAudit(id, r) }));
	      };
	      const approvalRow = (item) => h("div", { key: item.id, className: "fr-provenance-row" },
	        h("b", null, item.title || item.id),
	        h("span", null, metaParts([item.id, item.skill || "Task", riskText(item.risk)].concat(timeParts(item.time)))),
	        h("div", { className: "fr-inspector-actions" },
	          inspectorButton("Open in My Desk", () => { if (ctx.setHighlight) ctx.setHighlight(item.id); ctx.openPage("approvals"); }, "stamp"),
	          item.workOrderId && inspectorButton("Task", () => ctx.openPage("workorder", item.workOrderId), "clipboard"),
	          item.runId && inspectorButton("History", () => setTraceRunId(item.runId), "workflow")));
	      const recentRow = (rf) => {
	        const run = rf.originRunId ? (office.runs || []).find((r) => r.id === rf.originRunId) : null;
	        const auditId = rf.auditId || (run && run.auditId);
	        return h("div", { key: rf.id, className: "fr-provenance-row" },
	          h("b", null, rf.newName || f.name),
	          h("span", null, metaParts([rf.oldName && "from " + rf.oldName, rf.folder && folderPathNode(rf.folder), dateEn(rf.date), rf.daysLeft != null ? rf.daysLeft + " days to restore" : null])),
	          h("div", { className: "fr-inspector-actions" },
	            rf.workOrderId && inspectorButton("Task", () => ctx.openPage("workorder", rf.workOrderId), "clipboard"),
	            run && inspectorButton("History", () => setTraceRunId(run.id), "workflow"),
	            auditId && inspectorButton("History record", () => ctx.openPage("audit-event", auditId), "scroll"),
	            inspectorButton("Restore", () => ctx.restoreFiled(rf.id), "refresh", { disabled: !ctx.can("restoreFiled"), title: ctx.can("restoreFiled") ? "" : ctx.denyReason("restoreFiled"), "data-recent-action": "restore", "data-recent-filed-id": rf.id })));
	      };
	      return h("aside", { className: "fr-inspector", "aria-label": "File details", "data-file-inspector-id": f.id },
	        h("div", { className: "fr-inspector-head" },
	          h("span", { className: "fr-inspector-ico" }, h(Icon, { name: "file", size: 17 })),
	          h("div", { className: "fr-inspector-title" },
	            h("b", null, f.name),
	            h("span", null, metaParts([kindEn(f.kind), folderLabel(f.folder), srcName(office, f.source)]))),
          h("button", { type: "button", className: "btn btn-sm", title: "Close inspector", "data-file-inspector-close": f.id, onClick: () => { const backArg = fileBackArg; setTraceRunId(null); setPop(null); setMenu(null); setFileBackArg(null); ctx.openPage("files", backArg); } }, h(Icon, { name: "x", size: 14 }))),
	        h("div", { className: "fr-inspector-actions" },
	          inspectorButton("Rename", () => openRenameModal(f), "edit", { disabled: !canOrg, title: canOrg ? "" : ctx.denyReason("organizeFiles"), "data-file-inspector-action": "rename", "data-file-inspector-file-id": f.id }),
	          conn && inspectorButton("App settings", () => ctx.openPage("connector", conn.id), "link", { "data-file-inspector-action": "connector", "data-connector-id": conn.id }),
	          inspectorButton("Usage history", () => setPop({ n: f.id, x: Math.max(12, (window.innerWidth || 420) - 330), y: 120 }), "search", { "data-file-inspector-action": "citations", "data-file-inspector-file-id": f.id })),
	        h("div", { className: "fr-inspector-grid" },
	          field("File size", displaySize(f.size)),
	          field("Uploaded", dateEn(f.uploaded)),
	          field("Times used", "" + (f.refs || 0)),
	          field("From", srcName(office, f.source)),
	          f.originalName && field("Original", f.originalName),
	          f.suggestedName && field("Suggested", f.suggestedName)),
	        (f.tags || []).length ? sectionBlock("Tags", h("div", { className: "fr-tags" }, tagChips(f))) : null,
	        sectionBlock("Available to",
          h("div", { className: "fr-inspector-actions" }, roleScopeButtons)),
	        conn && sectionBlock("Connected app",
	          h("div", { className: "fr-provenance-row" },
	            h("b", null, conn.name),
	            h("span", null, metaParts([conn.status === "已連接" ? "Connected" : "Not connected", conn.health === "ok" ? "Healthy" : (conn.health === "reauth" ? "Reconnect to resume file updates." : "File updates need attention."), conn.defaultFolder && "Saves to " + folderLabel(conn.defaultFolder)])),
	            h("div", { className: "fr-inspector-actions" }, inspectorButton("Open app", () => ctx.openPage("connector", conn.id), "link")))),
	        sectionBlock("Usage history",
	          citations.length
	            ? h("div", { className: "fr-provenance-list" }, citations.map((c, i) => h("button", { key: i, type: "button", className: "fr-cite-row", onClick: () => setTraceRunId(runs[0] ? runs[0].id : null) }, h("b", null, c), h("span", null, runs[0] ? "Show related work" : "Usage saved with this file"))))
	            : h("div", { className: "fr-empty", style: { padding: 16 } }, "This file has not been used yet.")),
	        approvals.length ? sectionBlock("Items in My Desk", h("div", { className: "fr-provenance-list" }, approvals.map(approvalRow))) : null,
	        runs.length ? sectionBlock("Related tasks", h("div", { className: "fr-provenance-list" }, runs.map(runRow))) : null,
	        audits.length ? sectionBlock("History", h("div", { className: "fr-provenance-list" }, audits.slice(0, 5).map((a) => h("div", { key: a.id, className: "fr-provenance-row" }, h("b", null, a.title || a.action || a.id), h("span", null, metaParts([a.id].concat(timeParts(a.time), [a.action]))), h("div", { className: "fr-inspector-actions" }, inspectorButton("Open history", () => ctx.openPage("audit-event", a.id), "scroll")))))) : null,
	        recent.length ? sectionBlock("Recently added", h("div", { className: "fr-provenance-list" }, recent.map(recentRow))) : null,
	        !runs.length && !approvals.length && !audits.length && !recent.length && h("div", { className: "fr-empty", style: { padding: 16 } }, "This file has not been used yet."));
	    }

	    // ---- row context menu (fixed position) ----
	    const menuEl = menu && (function () {
	      const f = files.find((x) => x.id === menu.id); if (!f) return null;
	      const left = Math.max(8, Math.min(menu.x - 200, (window.innerWidth || 400) - 222));
      let inner;
      if (menu.sub === "move") {
        inner = [h("div", { className: "fr-menu-h", key: "h" }, "Move to folder"),
          folders.map((fo) => h("button", { key: fo.id, disabled: !canOrg, title: canOrg ? "" : ctx.denyReason("organizeFiles"), "data-file-menu-action": "move-to-folder", "data-file-menu-file-id": f.id, "data-folder-id": fo.id, onClick: (e) => { e.stopPropagation(); ctx.moveFile(f.id, fo.id); setMenu(null); } }, h(Icon, { name: "folder", size: 14 }), h("span", null, fo.parent ? "— " + fo.name : fo.name)))];
      } else if (menu.sub === "tag") {
        inner = [h("div", { className: "fr-menu-h", key: "h" }, "Tags"),
          h("div", { key: "cur", style: { display: "flex", flexWrap: "wrap", gap: 4, padding: "2px 10px 6px" } }, (f.tags || []).length ? (f.tags || []).map((t) => h("button", { key: t, className: "fr-tag", disabled: !canOrg, title: canOrg ? "Remove tag" : ctx.denyReason("organizeFiles"), "data-file-menu-action": "remove-tag", "data-file-menu-file-id": f.id, "data-file-tag-value": t, onClick: (e) => { e.stopPropagation(); if (!canOrg) return; ctx.untagFile(f.id, t); } }, "#", h("span", null, t), " ×")) : h("span", { style: { fontSize: 12, color: "var(--text-faint)" } }, "No tags")),
          h("input", { key: "in", className: "fr-tag-in", placeholder: "Add tag, press Enter", disabled: !canOrg, title: canOrg ? "" : ctx.denyReason("organizeFiles"), "aria-label": "Add file tag", "data-file-menu-tag-input": f.id, onClick: (e) => e.stopPropagation(), onKeyDown: (e) => { if (e.key === "Enter" && e.target.value.trim()) { ctx.tagFile(f.id, e.target.value.trim().replace(/^#/, "")); e.target.value = ""; } } })];
      } else {
        inner = [
	        h("button", { key: "open", "data-file-menu-action": "open", "data-file-menu-file-id": f.id, onClick: (e) => { e.stopPropagation(); openFile(f); } }, h(Icon, { name: "eye", size: 14 }), h("span", null, "Open details")),
          h("button", { key: "mv", disabled: !canOrg, title: canOrg ? "" : ctx.denyReason("organizeFiles"), "data-file-menu-action": "move", "data-file-menu-file-id": f.id, onClick: (e) => { e.stopPropagation(); setMenu(Object.assign({}, menu, { sub: "move" })); } }, h(Icon, { name: "folder", size: 14 }), h("span", null, "Move to folder")),
          h("button", { key: "rn", disabled: !canOrg, title: canOrg ? "" : ctx.denyReason("organizeFiles"), "data-file-menu-action": "rename", "data-file-menu-file-id": f.id, onClick: (e) => { e.stopPropagation(); setRenaming(f.id); setMenu(null); } }, h(Icon, { name: "edit", size: 14 }), h("span", null, "Rename")),
          h("button", { key: "tg", disabled: !canOrg, title: canOrg ? "" : ctx.denyReason("organizeFiles"), "data-file-menu-action": "tags", "data-file-menu-file-id": f.id, onClick: (e) => { e.stopPropagation(); setMenu(Object.assign({}, menu, { sub: "tag" })); } }, h(Icon, { name: "tag", size: 14 }), h("span", null, "Tags")),
          h("div", { key: "sep", className: "fr-menu-sep" }),
	        h("button", { key: "ct", "data-file-menu-action": "citations", "data-file-menu-file-id": f.id, onClick: (e) => { e.stopPropagation(); const rc = e.currentTarget.getBoundingClientRect(); setMenu(null); setPop({ n: f.id, x: rc.left, y: rc.bottom }); } }, h(Icon, { name: "search", size: 14 }), h("span", null, "Open usage history")),
        ];
	      }
	      return h("div", { className: "fr-menu", "data-file-menu-open": f.id, "data-file-menu-sub": menu.sub || "root", style: { left: left, top: menu.y + 6 }, onClick: (e) => e.stopPropagation() }, inner);
	    })();
	    const renameModalFile = renameModalId ? files.find((f) => f.id === renameModalId) : null;
	    const folderModalEl = folderModal && h(Modal, { title: "New folder", sub: "Add a folder for company files.", icon: "folder", width: 520, onClose: closeFolderModal, "data-file-folder-modal": "true",
	      footer: [
	        h(Button, { key: "cancel", variant: "ghost", "data-file-folder-cancel": "true", onClick: closeFolderModal }, "Cancel"),
	        h(Button, { key: "create", variant: "primary", icon: "plus", disabled: !folderName.trim(), "data-file-folder-create": "true", onClick: createFolderFromModal }, "Create folder"),
	      ] },
	      h("div", { style: { display: "grid", gap: 14 } },
	        h("label", { style: { display: "grid", gap: 6, fontSize: 12.5, fontWeight: 700, color: "var(--text-dim)" } },
	          h("span", null, "Folder name"),
	          h("input", { className: "field", autoFocus: true, value: folderName, placeholder: "e.g. Vendor contracts", "data-file-folder-name": "true",
	            onChange: (e) => { setFolderName(e.target.value); setFolderError(""); },
	            onKeyDown: (e) => { if (e.key === "Enter") createFolderFromModal(); if (e.key === "Escape") closeFolderModal(); } })),
	        h("label", { style: { display: "grid", gap: 6, fontSize: 12.5, fontWeight: 700, color: "var(--text-dim)" } },
	          h("span", null, "Parent folder"),
	          h("select", { className: "field", value: folderParentId, "data-file-folder-parent": "true", onChange: (e) => setFolderParentId(e.target.value) },
	            h("option", { value: "" }, "Top level"),
	            folders.map((fo) => h("option", { key: fo.id, value: fo.id }, folderPathLabel(fo))))),
	        folderError && h("div", { style: { color: "var(--red)", fontSize: 12.5 }, "data-file-folder-error": "true" }, folderError)));
	    const renameModalEl = renameModalFile && h(Modal, { title: "Rename file", sub: renameModalFile.name, icon: "edit", width: 520, onClose: closeRenameModal, "data-file-rename-modal": renameModalFile.id,
	      footer: [
	        h(Button, { key: "cancel", variant: "ghost", "data-file-rename-cancel": renameModalFile.id, onClick: closeRenameModal }, "Cancel"),
	        h(Button, { key: "save", variant: "primary", icon: "check", disabled: !renameValue.trim(), "data-file-rename-save": renameModalFile.id, onClick: saveRenameModal }, "Save name"),
	      ] },
	      h("div", { style: { display: "grid", gap: 12 } },
	        h("label", { style: { display: "grid", gap: 6, fontSize: 12.5, fontWeight: 700, color: "var(--text-dim)" } },
	          h("span", null, "File name"),
	          h("input", { className: "field", autoFocus: true, value: renameValue, "data-file-rename-modal-input": renameModalFile.id,
	            onChange: (e) => { setRenameValue(e.target.value); setRenameError(""); },
	            onKeyDown: (e) => { if (e.key === "Enter") saveRenameModal(); if (e.key === "Escape") closeRenameModal(); } })),
	        renameError && h("div", { style: { color: "var(--red)", fontSize: 12.5 }, "data-file-rename-error": renameModalFile.id }, renameError)));

	    // ---- body ----
    const dropzone = h("div", { className: "dropzone" + (dragOver ? " dragover" : "") + (ctx.can("addUpload") ? "" : " disabled"),
      role: "button",
      "aria-disabled": ctx.can("addUpload") ? "false" : "true",
      tabIndex: ctx.can("addUpload") ? 0 : -1,
      "data-upload-dropzone": "file-room",
      "data-file-upload-disabled": ctx.can("addUpload") ? "false" : "true",
      title: ctx.can("addUpload") ? "" : ctx.denyReason("addUpload"),
      style: ctx.can("addUpload") ? null : { opacity: 0.55, cursor: "not-allowed" },
      onClick: startMockUpload,
      onKeyDown: (e) => { if ((e.key === "Enter" || e.key === " ") && ctx.can("addUpload")) { e.preventDefault(); startMockUpload(); } },
      onDragEnter: handleDragOver,
      onDragOver: handleDragOver,
      onDragLeave: (e) => { e.preventDefault(); e.stopPropagation(); setDragOver(false); },
      onDrop: handleDrop },
      h(InlineIcon, { name: "upload" }),
	      h("b", null, prog >= 0 ? "Uploading…" : dragOver ? (sourceApprovalRequired ? "Drop files for review" : "Drop files to add them") : "Drop in 3–5 company documents"),
	      prog >= 0
	        ? h("div", { style: { maxWidth: 320, margin: "10px auto 0" } }, h(ProgressBar, { value: prog, tone: "blue", height: 6 }))
		        : h("p", { style: { fontSize: 13, color: "var(--text-faint)" } }, ctx.can("addUpload") ? (sourceApprovalRequired ? "Price list · FAQ · contract (drag files here or click to add a sample)" : "File approval is off. New files have a 30-day undo.") : ctx.denyReason("addUpload")));

	    const stagingCard = (st) => h(Card, { key: st.id, className: ctx.highlight === st.id ? "onboard-hl" : "", "data-staging-id": st.id, style: { padding: 16, marginBottom: 10 } },
	      h("div", { className: "row gap10 wrap", style: { fontSize: 13 } },
        h("div", { className: "grow" }, h("b", null, st.name), h("span", { style: { marginLeft: 8, fontSize: 12, color: "var(--text-faint)" } }, st.from),
	          h("div", { style: { fontSize: 12.5, marginTop: 3, color: "var(--text-faint)" } }, h("span", null, "Suggested name: "), h("b", { style: { color: "var(--text)" } }, st.suggestedName), h("span", { "aria-hidden": "true" }, " · "), h("span", null, "Folder: "), h("span", null, st.suggestedFolder))),
	        h(Button, { variant: "primary", sm: true, "data-staging-action": "approve-suggested", "data-staging-id": st.id, disabled: !ctx.can("approveStaging"), title: ctx.can("approveStaging") ? "" : ctx.denyReason("approveStaging"),
	          onClick: () => { const fileId = ctx.approveStaging(st.id, true); ctx.showToast(formatCopy("Saved: {name}", { name: st.suggestedName })); if (fileId) openFileId(fileId); } }, "Use suggestion"),
	        h(Button, { variant: "ghost", sm: true, "data-staging-action": "approve-original", "data-staging-id": st.id, disabled: !ctx.can("approveStaging"), title: ctx.can("approveStaging") ? "" : ctx.denyReason("approveStaging"),
	          onClick: () => { const fileId = ctx.approveStaging(st.id, false); ctx.showToast("Saved with the original name."); if (fileId) openFileId(fileId); } }, "Keep original name")));

    let body;
    if (isSmart && scope.id === "pending") {
      body = h("div", null, dropzone,
        office.staging.length === 0
		          ? h("div", { className: "fr-empty" }, sourceApprovalRequired ? "No files need review. New files from connected apps appear here." : "No files need review. File approval is off, so new files are added right away.")
          : office.staging.map(stagingCard));
    } else if (isSmart && scope.id === "recent") {
      body = office.recentFiled.length === 0
	        ? h("div", { className: "fr-empty" }, "No recently added files. You can restore renamed files for 30 days.")
	        : h(Card, { style: { padding: 18 } },
	            office.recentFiled.map((rf) => {
	              const originRun = rf.originRunId ? (office.runs || []).find((r) => r.id === rf.originRunId) : null;
	              const auditId = rf.auditId || (originRun && originRun.auditId) || null;
	              const liveApprovalId = rf.approvalId && (office.inbox || []).some((item) => item.id === rf.approvalId) ? rf.approvalId : null;
	              return h("div", { key: rf.id, className: "row gap8 wrap", "data-recent-filed-id": rf.id, "data-recent-file-id": rf.fileId || "", style: { padding: "9px 0", borderBottom: "1px solid var(--border)", fontSize: 13 } },
	                h("span", { style: { color: "var(--text-faint)", textDecoration: "line-through" } }, rf.oldName),
	                h(Icon, { name: "arrowRight", size: 12 }), h("b", null, rf.newName),
	                h("span", { style: { fontSize: 12, color: "var(--text-faint)" } }, h("span", null, rf.folder), h("span", null, " · "), h("span", null, dateEn(rf.date))),
	                h("span", { className: "grow" }),
                rf.fileId && h(Button, { variant: "ghost", sm: true, "data-recent-action": "open-file", "data-recent-filed-id": rf.id, onClick: () => { if (ctx.setHighlight) ctx.setHighlight(rf.fileId); setScope({ t: "all" }, { route: false }); openFileId(rf.fileId, rf.originRunId || null); } }, "Open file"),
	                rf.workOrderId && h(Button, { variant: "ghost", sm: true, "data-recent-action": "open-work-order", "data-recent-filed-id": rf.id, onClick: () => ctx.openPage("workorder", rf.workOrderId) }, rf.workOrderId),
	                liveApprovalId && h(Button, { variant: "ghost", sm: true, "data-recent-action": "open-approval", "data-recent-filed-id": rf.id, onClick: () => { if (ctx.setHighlight) ctx.setHighlight(liveApprovalId); ctx.openPage("approvals"); } }, "Open approval"),
	                auditId && h(Button, { variant: "ghost", sm: true, "data-recent-action": "open-audit", "data-recent-filed-id": rf.id, onClick: () => ctx.openPage("audit-event", auditId) }, "Open history"),
	                h(Button, { variant: "ghost", sm: true, disabled: !ctx.can("restoreFiled"), title: ctx.can("restoreFiled") ? "" : ctx.denyReason("restoreFiled"),
	                  "data-recent-action": "restore", "data-recent-filed-id": rf.id, onClick: () => ctx.restoreFiled(rf.id) }, h("span", null, "Restore ("), h("span", null, "" + rf.daysLeft), h("span", null, " days left)")));
	            }),
	            h("p", { style: { fontSize: 12, marginTop: 10, color: "var(--text-faint)" } }, "Original names are kept for 30 days, so you can restore them."));
    } else {
      body = visible.length === 0
        ? h("div", { className: "fr-empty" }, ql ? "No files match “" + q + "”." : "No files in this view yet.")
        : h("div", null, groups.map((g) => h("div", { key: g.key, style: { marginBottom: 14 } },
            g.label ? h("div", { className: "fr-group-h", "data-file-group-heading": g.key, "data-file-group-label": g.label, "data-file-group-count": String(g.items.length) }, h("span", null, g.label), h("span", { className: "fr-count" }, "" + g.items.length)) : null,
            view.grid ? h("div", { className: "fr-grid" }, g.items.map(fileCard)) : h("div", { className: "fr-list" }, g.items.map(fileRow)))));
    }

    return h("div", null,
	      h(PageHeader, { eyebrow: "Company", icon: "folder", title: "Files",
	        sub: "Add the files employees can use. Each draft names the file used." }),
	      h("div", { className: "fileroom" + (selectedFile ? " has-inspector" : "") }, rail,
	        h("div", { className: "fr-main" },
	          h("div", { className: "fr-bar" }, h("span", { className: "grow" }, crumb),
	            !isSmart ? h("span", { className: "fr-count" }, visible.length + " file" + (visible.length === 1 ? "" : "s")) : null),
	          !isSmart ? toolbar : null,
	          prog >= 0 && !isSmart ? h("div", null, h(ProgressBar, { value: prog, tone: "blue", height: 4 })) : null,
	          body,
	          h("div", { className: "rule-banner", style: { marginTop: 4 } },
	            h("div", { className: "row gap8" }, h(Icon, { name: "file", size: 15 }),
              h("span", null, "Drafts name the file used. Organizing here does not change the original file in your drive.")))),
		        selectedFile && fileInspector(selectedFile)),
		      menuEl,
		      folderModalEl,
		      renameModalEl);
	  }

  window.Screens.files = Files;
})();
