/* ============================================================
   beedle dataroom — admin modals (new section / drop docs / edit / branding)
   ============================================================ */
const ICON_CHOICES = ["compass", "cpu", "trend", "chart", "scale", "people", "shield", "heart", "building", "folder", "globe", "gauge"];
const COLOR_CHOICES = ["#15B4E7", "#0E97C6", "#1E9E6A", "#E0762B", "#7A57D1", "#EC2B3B", "#F3BB3A", "#42565F"];

// Inline "create a new visibility key" — registers it in the server catalog and
// hydrates the local KEYS so it appears in every picker + the member editor.
function NewKeyInline({ onAdded }) {
  const [adding, setAdding] = React.useState(false);
  const [name, setName] = React.useState("");
  const [busy, setBusy] = React.useState(false);
  const add = async () => {
    const slug = name.trim();
    if (!slug) return;
    setBusy(true);
    try {
      const r = await api.createKey({ key: slug, label: slug });
      if (window.setKeys) setKeys(r.keys);
      onAdded(r.key);
      setName(""); setAdding(false);
    } catch (e) { window.alert(e.message || T("Could not add key")); }
    finally { setBusy(false); }
  };
  if (!adding) return (
    <button type="button" className="btn btn-ghost" style={{ marginTop: 8 }} onClick={() => setAdding(true)}>
      <Icon name="plus" size={14} />{T("New key")}
    </button>
  );
  return (
    <div style={{ display: "flex", gap: 8, marginTop: 8 }}>
      <input className="input" autoFocus value={name} onChange={e => setName(e.target.value)}
        placeholder={T("e.g. forecast-seedround")} onKeyDown={e => { if (e.key === "Enter") add(); if (e.key === "Escape") { setAdding(false); setName(""); } }} style={{ flex: 1 }} />
      <button type="button" className="btn btn-primary" disabled={busy || !name.trim()} onClick={add}>{T("Add")}</button>
      <button type="button" className="btn btn-ghost" onClick={() => { setAdding(false); setName(""); }}>{T("Cancel")}</button>
    </div>
  );
}

function KeyPicker({ value, onChange }) {
  return (
    <div className="keypick">
      {Object.entries(contentKeys()).map(([k, meta]) => (
        <div key={k} className={"keyopt" + (value === k ? " on" : "")} onClick={() => onChange(k)}>
          <span className="radio" />
          <div style={{ flex: 1 }}>
            <div className="nm">
              <Icon name={k === "all" ? "unlock" : "lock"} size={14} style={{ color: k === "all" ? "var(--green)" : "var(--ink-3)" }} />
              {meta.label}
            </div>
            <div className="ds">{k === "all" ? T("Visible to every authenticated member") : T("Only members whose keyring holds this key")}</div>
          </div>
          {meta.tone && <span className={"pill pill-" + meta.tone}>{k}</span>}
        </div>
      ))}
      <NewKeyInline onAdded={onChange} />
    </div>
  );
}

function NewSectionModal({ onClose, onCreate }) {
  const [name, setName] = React.useState("");
  const [desc, setDesc] = React.useState("");
  const [icon, setIcon] = React.useState("folder");
  const [color, setColor] = React.useState("#15B4E7");
  const [key, setKey] = React.useState("all");
  return (
    <div className="scrim" onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div>
            <h2>{T("New section")}</h2>
            <p>{T("Create a folder in the data room and set who can see it.")}</p>
          </div>
          <button className="xbtn" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="modal-body scroll">
          <div className="form-grid">
            <div style={{ display: "flex", gap: 14, alignItems: "center" }}>
              <span className="sec-ico" style={{ width: 54, height: 54, borderRadius: 15, background: `linear-gradient(150deg,${color},${shade(color, -16)})` }}><Icon name={icon} size={26} sw={1.9} /></span>
              <div style={{ flex: 1 }}>
                <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>{T("Section name")}</label>
                <input className="input" value={name} onChange={e => setName(e.target.value)} placeholder={T("e.g. Customer References")} autoFocus />
              </div>
            </div>
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>{T("Description")}</label>
              <input className="input" value={desc} onChange={e => setDesc(e.target.value)} placeholder={T("One line about what lives here")} />
            </div>
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 9 }}>{T("Icon")}</label>
              <div className="iconpick">
                {ICON_CHOICES.map(ic => <button key={ic} className={icon === ic ? "on" : ""} onClick={() => setIcon(ic)}><Icon name={ic} size={19} /></button>)}
              </div>
            </div>
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 9 }}>{T("Accent")}</label>
              <div className="swatch-row">
                {COLOR_CHOICES.map(c => (
                  <button key={c} onClick={() => setColor(c)} style={{ width: 30, height: 30, borderRadius: 9, background: c, border: color === c ? "2px solid var(--ink)" : "2px solid transparent", boxShadow: color === c ? "0 0 0 2px #fff inset" : "none", cursor: "pointer" }} />
                ))}
              </div>
            </div>
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 9 }}>{T("Visibility key")}</label>
              <KeyPicker value={key} onChange={setKey} />
            </div>
          </div>
        </div>
        <div className="modal-foot">
          <div className="spacer" />
          <button className="btn btn-ghost" onClick={onClose}>{T("Cancel")}</button>
          <button className="btn btn-primary" disabled={!name.trim()} onClick={() => onCreate({ id: "sec_" + Date.now(), name: name.trim(), desc: desc.trim() || "—", icon, color, key })}>
            <Icon name="plus" size={16} />{T("Create section")}
          </button>
        </div>
      </div>
    </div>
  );
}

// classify a real File for the type badge (server re-derives authoritatively)
function fileType(file) {
  const mime = (file.type || "").toLowerCase();
  const ext = (file.name.split(".").pop() || "").toLowerCase();
  if (mime.includes("pdf") || ext === "pdf") return "pdf";
  if (mime.startsWith("image/") || ["png", "jpg", "jpeg", "gif", "webp", "svg"].includes(ext)) return "image";
  if (mime.startsWith("video/") || ["mp4", "mov", "webm", "m4v"].includes(ext)) return "video";
  if (mime.includes("presentation") || mime.includes("powerpoint") || ["ppt", "pptx"].includes(ext)) return "ppt";
  if (["html", "htm"].includes(ext)) return "html";
  return "file";
}
function fmtBytes(b) {
  if (b >= 1048576) return (b / 1048576).toFixed(1) + " MB";
  return Math.max(1, Math.round(b / 1024)) + " KB";
}

function AddDocsModal({ section, isAdmin, onClose, onUpload }) {
  const [drag, setDrag] = React.useState(false);
  const [staged, setStaged] = React.useState([]);
  const [busy, setBusy] = React.useState(false);
  const [, bumpKeys] = React.useState(0); // re-render the key <select>s when the catalog grows
  const inputRef = React.useRef(null);

  const addFiles = (fileList) => {
    const items = Array.from(fileList).map((file, i) => ({
      id: "stg_" + Date.now() + "_" + i + "_" + Math.round(Math.random() * 1e4),
      file,
      name: file.name.replace(/\.[a-z0-9]+$/i, ""),
      type: fileType(file),
      size: fmtBytes(file.size),
      key: section.key,
      downloadable: true,
    }));
    setStaged(s => [...s, ...items]);
  };
  const toggle = (id, field) => setStaged(s => s.map(x => x.id === id ? { ...x, [field]: !x[field] } : x));
  const setKey = (id, k) => setStaged(s => s.map(x => x.id === id ? { ...x, key: k } : x));
  const setName = (id, v) => setStaged(s => s.map(x => x.id === id ? { ...x, name: v } : x));
  const removeItem = (id) => setStaged(s => s.filter(x => x.id !== id));

  const submit = async () => {
    if (!staged.length) return;
    setBusy(true);
    try { await onUpload(staged); } catch (e) { /* App toasts the error */ } finally { setBusy(false); }
  };

  return (
    <div className="scrim" onClick={onClose}>
      <div className="modal" style={{ maxWidth: 580 }} onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div>
            <h2>{T("Add documents")}</h2>
            <p>{T("Uploading into")} <strong>{section.name}</strong>. {T("Set downloadable & key per file.")}</p>
          </div>
          <button className="xbtn" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="modal-body scroll">
          <input ref={inputRef} type="file" multiple style={{ display: "none" }}
            onChange={e => { addFiles(e.target.files); e.target.value = ""; }} />
          <div className={"dropzone" + (drag ? " drag" : "")}
            onDragOver={e => { e.preventDefault(); setDrag(true); }}
            onDragLeave={() => setDrag(false)}
            onDrop={e => { e.preventDefault(); setDrag(false); if (e.dataTransfer.files?.length) addFiles(e.dataTransfer.files); }}
            onClick={() => inputRef.current && inputRef.current.click()}>
            <div className="ic"><Icon name="upload" size={22} /></div>
            <div className="t">{T("Drag files here or click to browse")}</div>
            <div className="h">{T("PDF · images · HTML · video · slides — up to 120 MB each")}</div>
          </div>
          {isAdmin && <NewKeyInline onAdded={() => bumpKeys(v => v + 1)} />}
          {!isAdmin && staged.length > 0 && (
            <div style={{ fontSize: 12, color: "var(--ink-3)", margin: "4px 2px" }}>{T("Files inherit this section’s visibility key.")}</div>
          )}
          {staged.map(f => (
            <div key={f.id} className="stage-item">
              <DocType type={f.type} size="sm" />
              <div className="meta">
                <input className="input" value={f.name} onChange={e => setName(f.id, e.target.value)}
                  style={{ fontSize: 13, padding: "5px 8px", marginBottom: 5 }} />
                <div style={{ fontSize: 11.5, color: "var(--ink-3)" }}>{f.type.toUpperCase()} · {f.size}</div>
              </div>
              <div style={{ display: "flex", flexDirection: "column", gap: 7, alignItems: "flex-end" }}>
                <label className="opt" style={{ cursor: "pointer" }} onClick={() => toggle(f.id, "downloadable")}>
                  <span className={"sw" + (f.downloadable ? " on" : "")} style={{ width: 34, height: 20 }} />
                  {T("Downloadable")}
                </label>
                {isAdmin && (
                  <select value={f.key} onChange={e => setKey(f.id, e.target.value)} style={{ fontSize: 12, border: "1px solid var(--line)", borderRadius: 8, padding: "3px 6px", color: "var(--ink-2)", background: "#fff" }}>
                    {Object.entries(contentKeys()).map(([k, m]) => <option key={k} value={k}>{m.label}</option>)}
                  </select>
                )}
              </div>
              <button className="xbtn" style={{ width: 28, height: 28 }} title={T("Remove")} onClick={() => removeItem(f.id)}><Icon name="x" size={15} /></button>
            </div>
          ))}
        </div>
        <div className="modal-foot">
          <span style={{ fontSize: 13, color: "var(--ink-3)" }}>{staged.length !== 1 ? T("{n} files ready", { n: staged.length }) : T("1 file ready")}</span>
          <div className="spacer" />
          <button className="btn btn-ghost" onClick={onClose}>{T("Cancel")}</button>
          <button className="btn btn-primary" disabled={!staged.length || busy} onClick={submit}><Icon name="check" size={16} />{busy ? T("Uploading…") : staged.length === 1 ? T("Add document") : T("Add {n} documents", { n: staged.length })}</button>
        </div>
      </div>
    </div>
  );
}

function EditDocModal({ doc, isAdmin, onClose, onSave, onDelete, onEditContent }) {
  const [name, setName] = React.useState(doc.name);
  const [downloadable, setDownloadable] = React.useState(doc.downloadable);
  const [key, setKey] = React.useState(doc.key);
  const [by, setBy] = React.useState(doc.by || "");
  const isPage = doc.type === "html" && !doc.hasFile;   // an authored page has editable content
  return (
    <div className="scrim" onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div>
            <h2>{T("Document settings")}</h2>
            <p style={{ display: "flex", alignItems: "center", gap: 8 }}><DocType type={doc.type} size="sm" /> {doc.type.toUpperCase()} · {doc.size}</p>
          </div>
          <button className="xbtn" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="modal-body scroll">
          <div className="form-grid">
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>{T("Display name")}</label>
              <input className="input" value={name} onChange={e => setName(e.target.value)} />
            </div>
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>{T("Author")}</label>
              <input className="input" value={by} onChange={e => setBy(e.target.value)} placeholder={T("e.g. Dana Whitfield")} />
            </div>
            {isPage && onEditContent && (
              <div className="toggle" onClick={() => onEditContent({ ...doc, name: name.trim() || doc.name, downloadable, key, by: by.trim() || doc.by })} style={{ cursor: "pointer" }}>
                <div>
                  <div className="nm">{T("Page content")}</div>
                  <div className="ds">{T("Open the rich-text editor to write or paste the page.")}</div>
                </div>
                <span className="btn btn-ghost" style={{ pointerEvents: "none" }}><Icon name="edit" size={15} />{T("Edit content")}</span>
              </div>
            )}
            {isAdmin && (
              <div className="toggle" onClick={() => setDownloadable(d => !d)} style={{ cursor: "pointer" }}>
                <div>
                  <div className="nm">{T("Allow download")}</div>
                  <div className="ds">{T("Members can save a copy. Off = view-only in the room.")}</div>
                </div>
                <span className={"sw" + (downloadable ? " on" : "")} />
              </div>
            )}
            {isAdmin && (
              <div>
                <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 9 }}>{T("Visibility key")}</label>
                <KeyPicker value={key} onChange={setKey} />
              </div>
            )}
          </div>
        </div>
        <div className="modal-foot">
          <button className="btn btn-danger" onClick={() => onDelete(doc)}><Icon name="trash" size={16} />{T("Delete")}</button>
          <div className="spacer" />
          <button className="btn btn-ghost" onClick={onClose}>{T("Cancel")}</button>
          <button className="btn btn-primary" onClick={() => onSave({ ...doc, name: name.trim() || doc.name, downloadable, key, by: by.trim() || doc.by })}><Icon name="check" size={16} />{T("Save")}</button>
        </div>
      </div>
    </div>
  );
}

// Shared label style for the doc-authoring modals.
const FIELD_LBL = { display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 };

// Author/edit a data-room HTML page with the full rich-text editor (admin only).
// Reuses RichText in `full` mode: paste HTML, paste/insert graphics (uploaded via
// api.uploadAsset). Saved HTML is sanitised on the way out and again at render.
function EditContentModal({ doc, onClose, onSave }) {
  const [name, setName] = React.useState(doc.name);
  const [html, setHtml] = React.useState(doc.html || "");
  return (
    <div className="scrim rt-doc-scrim" onClick={onClose}>
      <div className="modal rt-doc-modal" onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div>
            <h2>{T("Edit page")}</h2>
            <p>{T("Rich HTML — paste formatted text and images. Admins only.")}</p>
          </div>
          <button className="xbtn" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="modal-body scroll">
          <label style={FIELD_LBL}>{T("Page title")}</label>
          <input className="input" value={name} onChange={e => setName(e.target.value)} style={{ marginBottom: 14 }} />
          <RichText full value={html} onChange={setHtml} uploadAsset={api.uploadAsset} minHeight="52vh"
            ariaLabel={T("Page content")} placeholder={T("Write or paste your page…")}
            onError={(e) => window.alert((e && e.message) || T("Image upload failed"))} />
        </div>
        <div className="modal-foot">
          <div className="spacer" style={{ flex: 1 }} />
          <button className="btn btn-ghost" onClick={onClose}>{T("Cancel")}</button>
          <button className="btn btn-primary" onClick={() => onSave({ ...doc, name: name.trim() || doc.name, html })}><Icon name="check" size={16} />{T("Save")}</button>
        </div>
      </div>
    </div>
  );
}

// Create a new file-less HTML page in the active section.
function NewHtmlDocModal({ section, isAdmin, onClose, onCreate }) {
  const [name, setName] = React.useState("");
  const [key, setKey] = React.useState((section && section.key) || "all");
  const [html, setHtml] = React.useState("");
  return (
    <div className="scrim rt-doc-scrim" onClick={onClose}>
      <div className="modal rt-doc-modal" onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div>
            <h2>{T("New page")}</h2>
            <p>{section ? T("Added to “{name}”", { name: section.name }) : ""}</p>
          </div>
          <button className="xbtn" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="modal-body scroll">
          <label style={FIELD_LBL}>{T("Page title")}</label>
          <input className="input" value={name} onChange={e => setName(e.target.value)} placeholder={T("e.g. Investment memo")} style={{ marginBottom: 14 }} />
          {isAdmin && (
            <React.Fragment>
              <label style={FIELD_LBL}>{T("Visibility key")}</label>
              <div style={{ marginBottom: 14 }}><KeyPicker value={key} onChange={setKey} /></div>
            </React.Fragment>
          )}
          <label style={FIELD_LBL}>{T("Content")}</label>
          <RichText full value={html} onChange={setHtml} uploadAsset={api.uploadAsset} minHeight="44vh"
            ariaLabel={T("Page content")} placeholder={T("Write or paste your page…")}
            onError={(e) => window.alert((e && e.message) || T("Image upload failed"))} />
        </div>
        <div className="modal-foot">
          <div className="spacer" style={{ flex: 1 }} />
          <button className="btn btn-ghost" onClick={onClose}>{T("Cancel")}</button>
          <button className="btn btn-primary" disabled={!name.trim()} onClick={() => onCreate({ name: name.trim(), key, html })}><Icon name="check" size={16} />{T("Create page")}</button>
        </div>
      </div>
    </div>
  );
}

const LOGO_MAX_BYTES = 512 * 1024; // ~512KB — kept small so it fits the JSON body.
const LOGO_TYPES = ["image/png", "image/svg+xml", "image/jpeg", "image/webp", "image/gif"];

function BrandingModal({ company, onClose, onSave }) {
  const [name, setName] = React.useState(company.name);
  const [desc, setDesc] = React.useState(company.description);
  const [accent, setAccent] = React.useState(company.colors.blue);
  const [logo, setLogo] = React.useState(company.logo || "");
  const [err, setErr] = React.useState("");
  const fileRef = React.useRef(null);

  // Sign-in screen copy. Prefill each field with the stored value, falling back to
  // the login page's built-in default so the admin edits the text they actually see.
  const loginDefs = loginTextDefaults();
  const storedLogin = company.login || {};
  const [loginText, setLoginText] = React.useState(() => {
    const init = {};
    for (const k of ["eyebrow", "heading", "subtitle", "quote", "quoteBy", "secureBadge", "regionBadge"]) {
      init[k] = storedLogin[k] && storedLogin[k].trim() ? storedLogin[k] : loginDefs[k];
    }
    return init;
  });
  const [stats, setStats] = React.useState(() =>
    (Array.isArray(storedLogin.stats) && storedLogin.stats.length ? storedLogin.stats : loginDefs.stats)
      .map(s => ({ k: s.k || "", v: s.v || "" })));
  const setLoginField = (k, v) => setLoginText(p => ({ ...p, [k]: v }));
  const setStat = (i, key, v) => setStats(p => p.map((s, j) => (j === i ? { ...s, [key]: v } : s)));

  function pickLogo(e) {
    const file = e.target.files && e.target.files[0];
    e.target.value = ""; // allow re-picking the same file
    if (!file) return;
    if (!LOGO_TYPES.includes(file.type)) { setErr(T("Use a PNG, SVG, JPEG, WebP or GIF image.")); return; }
    if (file.size > LOGO_MAX_BYTES) { setErr(T("Logo is too large (max 512KB).")); return; }
    const reader = new FileReader();
    reader.onload = () => { setLogo(reader.result); setErr(""); };
    reader.onerror = () => setErr(T("Could not read that file."));
    reader.readAsDataURL(file);
  }

  return (
    <div className="scrim" onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div>
            <h2>{T("Company branding")}</h2>
            <p>{T("Stored in")} <code style={{ fontSize: 12 }}>dataroom/company</code> — {T("shown across the room.")}</p>
          </div>
          <button className="xbtn" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="modal-body scroll">
          <div className="form-grid">
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 9 }}>{T("Logo")}</label>
              <div style={{ display: "flex", gap: 14, alignItems: "center" }}>
                <span className={"sb-logo" + (logo ? " has-img" : "")} style={{ width: 56, height: 56, borderRadius: 14, background: logo ? "#fff" : `linear-gradient(150deg,${accent},${shade(accent, -22)})` }}>
                  {logo ? <img src={logo} alt={T("Logo")} /> : <Icon name="heart" size={26} />}
                </span>
                <input ref={fileRef} type="file" accept={LOGO_TYPES.join(",")} style={{ display: "none" }} onChange={pickLogo} />
                <button className="btn btn-ghost" onClick={() => fileRef.current && fileRef.current.click()}><Icon name="upload" size={16} />{T("Upload logo")}</button>
                {logo && <button className="btn btn-ghost" onClick={() => setLogo("")}><Icon name="trash" size={16} />{T("Remove")}</button>}
                <span style={{ fontSize: 12, color: "var(--ink-3)" }}>PNG/SVG · transparent</span>
              </div>
              {err && <div style={{ fontSize: 12, color: "var(--red, #EC2B3B)", marginTop: 7 }}>{err}</div>}
            </div>
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>{T("Company name")}</label>
              <input className="input" value={name} onChange={e => setName(e.target.value)} />
            </div>
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>{T("Description")}</label>
              <textarea className="input" rows={3} style={{ resize: "vertical" }} value={desc} onChange={e => setDesc(e.target.value)} />
            </div>
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 9 }}>{T("Brand accent")}</label>
              <div className="swatch-row">
                {COLOR_CHOICES.map(c => (
                  <button key={c} onClick={() => setAccent(c)} style={{ width: 30, height: 30, borderRadius: 9, background: c, border: accent === c ? "2px solid var(--ink)" : "2px solid transparent", boxShadow: accent === c ? "0 0 0 2px #fff inset" : "none", cursor: "pointer" }} />
                ))}
              </div>
            </div>

            <div style={{ borderTop: "1px solid var(--line, #E7E2D8)", margin: "4px 0 2px" }} />
            <div>
              <div style={{ fontSize: 13, fontWeight: 700, color: "var(--ink)" }}>{T("Sign-in screen")}</div>
              <div style={{ fontSize: 12, color: "var(--ink-3)", marginTop: 3 }}>{T("The text on the login page. Leave a field blank to use the default.")}</div>
            </div>
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>{T("Eyebrow")}</label>
              <input className="input" value={loginText.eyebrow} placeholder={loginDefs.eyebrow} onChange={e => setLoginField("eyebrow", e.target.value)} />
            </div>
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>{T("Heading")}</label>
              <input className="input" value={loginText.heading} placeholder={loginDefs.heading} onChange={e => setLoginField("heading", e.target.value)} />
            </div>
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>{T("Subtitle")}</label>
              <textarea className="input" rows={2} style={{ resize: "vertical" }} value={loginText.subtitle} placeholder={loginDefs.subtitle} onChange={e => setLoginField("subtitle", e.target.value)} />
            </div>
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>{T("Sidebar quote")}</label>
              <textarea className="input" rows={2} style={{ resize: "vertical" }} value={loginText.quote} placeholder={loginDefs.quote} onChange={e => setLoginField("quote", e.target.value)} />
            </div>
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>{T("Quote attribution")}</label>
              <input className="input" value={loginText.quoteBy} placeholder={loginDefs.quoteBy} onChange={e => setLoginField("quoteBy", e.target.value)} />
            </div>
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>{T("Sidebar stats")}</label>
              <div style={{ display: "grid", gap: 8 }}>
                {stats.map((s, i) => (
                  <div key={i} style={{ display: "flex", gap: 8 }}>
                    <input className="input" style={{ flex: 1 }} value={s.k} placeholder={T("Label")} onChange={e => setStat(i, "k", e.target.value)} />
                    <input className="input" style={{ flex: 1 }} value={s.v} placeholder={T("Value")} onChange={e => setStat(i, "v", e.target.value)} />
                  </div>
                ))}
              </div>
            </div>
            <div style={{ display: "flex", gap: 18 }}>
              <div style={{ flex: 1 }}>
                <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>{T("Security badge")}</label>
                <input className="input" value={loginText.secureBadge} placeholder={loginDefs.secureBadge} onChange={e => setLoginField("secureBadge", e.target.value)} />
              </div>
              <div style={{ flex: 1 }}>
                <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>{T("Region badge")}</label>
                <input className="input" value={loginText.regionBadge} placeholder={loginDefs.regionBadge} onChange={e => setLoginField("regionBadge", e.target.value)} />
              </div>
            </div>
          </div>
        </div>
        <div className="modal-foot">
          <div className="spacer" />
          <button className="btn btn-ghost" onClick={onClose}>{T("Cancel")}</button>
          <button className="btn btn-primary" onClick={() => onSave({ name, description: desc, logo, colors: { blue: accent }, login: { ...loginText, stats } })}><Icon name="check" size={16} />{T("Save branding")}</button>
        </div>
      </div>
    </div>
  );
}

// Export / import the room's content collections as JSON (admin only).
// Export downloads a self-contained snapshot; import merges it back in (upsert,
// never deletes). Document file bytes are NOT included — only their metadata.
function DataModal({ onClose, onExport, onImport }) {
  const [busy, setBusy] = React.useState(false);
  const [staged, setStaged] = React.useState(null); // parsed { collections, ... }
  const [err, setErr] = React.useState("");
  const fileRef = React.useRef(null);

  const counts = staged && staged.collections
    ? ["config", "sections", "documents", "forecasts"]
        .map((k) => `${(staged.collections[k] || []).length} ${k}`)
        .join(" · ")
    : "";

  const doExport = async () => {
    setBusy(true);
    try { await onExport(); } catch (e) { /* App toasts */ } finally { setBusy(false); }
  };

  const pickFile = (e) => {
    const file = e.target.files && e.target.files[0];
    e.target.value = ""; // allow re-picking the same file
    if (!file) return;
    const reader = new FileReader();
    reader.onload = () => {
      try {
        const obj = JSON.parse(reader.result);
        if (!obj || typeof obj !== "object" || !obj.collections) throw new Error("bad");
        setStaged(obj); setErr("");
      } catch { setStaged(null); setErr(T("That doesn’t look like a data-room export file.")); }
    };
    reader.onerror = () => { setStaged(null); setErr(T("Could not read that file.")); };
    reader.readAsText(file);
  };

  const doImport = async () => {
    if (!staged) return;
    setBusy(true);
    try { await onImport(staged); } catch (e) { /* App toasts */ } finally { setBusy(false); }
  };

  return (
    <div className="scrim" onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div>
            <h2>{T("Export / import data")}</h2>
            <p>{T("Content collections as JSON — branding, sections, documents & saved models. User accounts and uploaded file contents are not included.")}</p>
          </div>
          <button className="xbtn" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="modal-body scroll">
          <div className="form-grid">
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>{T("Export")}</label>
              <div style={{ fontSize: 12.5, color: "var(--ink-3)", marginBottom: 9 }}>{T("Download a JSON snapshot of the current data room.")}</div>
              <button className="btn btn-ghost" disabled={busy} onClick={doExport}><Icon name="download" size={16} />{T("Download export")}</button>
            </div>
            <div>
              <label style={{ display: "block", fontSize: 13, fontWeight: 600, color: "var(--ink-2)", marginBottom: 7 }}>{T("Import")}</label>
              <div style={{ fontSize: 12.5, color: "var(--ink-3)", marginBottom: 9 }}>{T("Merge a JSON export back in. Existing records are updated and new ones added — nothing is deleted.")}</div>
              <input ref={fileRef} type="file" accept="application/json,.json" style={{ display: "none" }} onChange={pickFile} />
              <button className="btn btn-ghost" disabled={busy} onClick={() => fileRef.current && fileRef.current.click()}><Icon name="upload" size={16} />{T("Choose JSON file")}</button>
              {staged && <div style={{ fontSize: 12.5, color: "var(--ink-2)", marginTop: 9 }}>{T("Ready to import")}: {counts}</div>}
              {err && <div style={{ fontSize: 12, color: "var(--red, #EC2B3B)", marginTop: 9 }}>{err}</div>}
            </div>
          </div>
        </div>
        <div className="modal-foot">
          <div className="spacer" />
          <button className="btn btn-ghost" onClick={onClose}>{T("Close")}</button>
          <button className="btn btn-primary" disabled={!staged || busy} onClick={doImport}><Icon name="check" size={16} />{busy ? T("Importing…") : T("Import")}</button>
        </div>
      </div>
    </div>
  );
}

// ---- Request access (shown to a list-only member who clicks a locked item) ----
function RequestAccessModal({ itemType, item, wantMode, onClose, onSubmit }) {
  const [note, setNote] = React.useState("");
  const label = KEYS[item.key] ? KEYS[item.key].label : item.key;
  return (
    <div className="scrim" onClick={onClose}>
      <div className="modal" style={{ maxWidth: 480 }} onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div>
            <h2>{T("Request access?")}</h2>
            <p>{T("You can see “{name}” but not open it. Ask an admin to grant you access.", { name: item.name })}</p>
          </div>
          <button className="xbtn" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="modal-body scroll">
          <div className="form-grid">
            <div style={{ display: "flex", gap: 10, alignItems: "center", fontSize: 13, color: "var(--ink-2)" }}>
              <span className="pill"><Icon name="lock" size={11} />{label}</span>
              <span style={{ color: "var(--ink-3)" }}>{T("requesting")} <strong>{wantMode}</strong> {T("access")}</span>
            </div>
            <div>
              <label style={FIELD_LBL}>{T("Add a note (optional)")}</label>
              <textarea className="input" rows={3} style={{ resize: "vertical" }} value={note} onChange={e => setNote(e.target.value)} placeholder={T("Why you need access — the admin will see this.")} autoFocus />
            </div>
          </div>
        </div>
        <div className="modal-foot">
          <div className="spacer" />
          <button className="btn btn-ghost" onClick={onClose}>{T("Cancel")}</button>
          <button className="btn btn-primary" onClick={() => onSubmit({ itemType, item, wantMode, note: note.trim() })}><Icon name="key" size={16} />{T("Request access")}</button>
        </div>
      </div>
    </div>
  );
}

// Per-key mode selector — turns the KEYS catalog into a set of dotted grants.
function GrantEditor({ keyring, onChange }) {
  const map = {};
  for (const g of keyring || []) {
    const i = g.indexOf(".");
    map[i === -1 ? g : g.slice(0, i)] = i === -1 ? "view" : g.slice(i + 1);
  }
  const MODES = ["—", "list", "view", "tune", "edit"];
  const setMode = (k, m) => {
    const next = { ...map };
    if (m === "—") delete next[k]; else next[k] = m;
    onChange(Object.entries(next).map(([k, m]) => `${k}.${m}`));
  };
  return (
    <div>
      {Object.entries(KEYS).map(([k, meta]) => (
        <div key={k} style={{ display: "flex", alignItems: "center", gap: 10, padding: "8px 0", borderBottom: "1px solid var(--line)" }}>
          <div style={{ flex: 1, display: "flex", alignItems: "center", gap: 8 }}>
            <span style={{ fontSize: 13.5, fontWeight: 600 }}>{meta.label}</span>
            {meta.tone && <span className={"pill pill-" + meta.tone}>{k}</span>}
          </div>
          <select value={map[k] || "—"} onChange={e => setMode(k, e.target.value)} style={{ fontSize: 12, border: "1px solid var(--line)", borderRadius: 8, padding: "4px 7px", background: "#fff", color: "var(--ink-2)" }}>
            {MODES.map(m => <option key={m} value={m}>{m === "—" ? T("No access") : m}</option>)}
          </select>
        </div>
      ))}
      <NewKeyInline onAdded={(k) => setMode(k, "view")} />
    </div>
  );
}

// Members & access — edit each member's key grants and (optionally) their password.
function MembersModal({ seed, focusUserId, requestId, onClose, toast }) {
  const [users, setUsers] = React.useState(null);
  const [sel, setSel] = React.useState(null);
  const [pw, setPw] = React.useState("");
  const [busy, setBusy] = React.useState(false);

  function startEdit(u) {
    let keyring = (u.keyring || []).slice();
    if (seed && seed.key) {
      const i = keyring.findIndex(g => g.split(".")[0] === seed.key);
      const dotted = seed.key + "." + (seed.mode || "view");
      if (i >= 0) keyring[i] = dotted; else keyring.push(dotted);
    }
    setSel({ ...u, keyring }); setPw("");
  }

  // Start a blank draft member. Every authenticated member holds "all" at view by
  // default, so seed the keyring to match the server's default.
  function startNew() {
    setSel({ isNew: true, name: "", email: "", keyring: ["all.view"] }); setPw("");
  }

  React.useEffect(() => { (async () => {
    try {
      const list = await api.listUsers();
      setUsers(list);
      const initial = focusUserId ? list.find(u => String(u.id || u._id) === String(focusUserId)) : list[0];
      if (initial) startEdit(initial);
    } catch (e) { toast && toast(e.message || T("Could not load members"), { icon: "info" }); }
  })(); }, []);

  const save = async () => {
    if (!sel) return;
    if (sel.isNew) {
      const email = (sel.email || "").trim();
      if (!email) return toast && toast(T("An email is required"), { icon: "info" });
      if (!pw.trim()) return toast && toast(T("A password is required for a new member"), { icon: "info" });
      setBusy(true);
      try {
        const created = await api.createUser({ email, name: (sel.name || "").trim() || email, password: pw, keyring: sel.keyring });
        setUsers(us => [...(us || []), created]);
        startEdit(created);
        toast && toast(T("Member created"), { icon: "check", good: true });
      } catch (e) { toast && toast(e.message || T("Could not create member"), { icon: "info" }); }
      finally { setBusy(false); }
      return;
    }
    setBusy(true);
    try {
      const patch = { keyring: sel.keyring };
      if (pw.trim()) patch.password = pw;
      const updated = await api.updateUser(sel.id || sel._id, patch);
      setUsers(us => us.map(u => (u.id || u._id) === (updated.id || updated._id) ? updated : u));
      if (requestId) { try { await api.resolveAccessRequest(requestId); } catch (e) { /* non-fatal */ } }
      toast && toast(T("Member updated"), { icon: "check", good: true });
      setPw("");
    } catch (e) { toast && toast(e.message || T("Could not save member"), { icon: "info" }); }
    finally { setBusy(false); }
  };

  return (
    <div className="scrim" onClick={onClose}>
      <div className="modal" style={{ maxWidth: 680 }} onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div>
            <h2>{T("Members & access")}</h2>
            <p>{T("Grant each member a key at a mode — list, view, tune or edit. Set a password if needed.")}</p>
          </div>
          <button className="xbtn" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="modal-body scroll" style={{ display: "flex", gap: 16, minHeight: 320 }}>
          <div style={{ width: 200, borderRight: "1px solid var(--line)", paddingRight: 12, overflow: "auto" }}>
            <button className={"btn btn-ghost" + (sel && sel.isNew ? " sel" : "")} style={{ width: "100%", marginBottom: 8, justifyContent: "flex-start" }} onClick={startNew}>
              <Icon name="plus" size={15} />{T("New member")}
            </button>
            {!users ? <div style={{ fontSize: 13, color: "var(--ink-3)" }}>{T("Loading…")}</div> :
              users.map(u => {
                const id = u.id || u._id; const on = sel && (sel.id || sel._id) === id;
                return (
                  <button key={id} className={"menu-item" + (on ? " sel" : "")} style={{ width: "100%" }} onClick={() => startEdit(u)}>
                    <Avatar role={{ name: u.name, initials: u.initials, avatar: u.avatar, admin: u.dataroomAdmin }} size={28} />
                    <div style={{ flex: 1, minWidth: 0, textAlign: "left" }}>
                      <div className="nm" style={{ fontSize: 13 }}>{u.name}</div>
                      <div className="ds" style={{ fontSize: 11 }}>{u.dataroomAdmin ? T("Admin") : u.email}</div>
                    </div>
                  </button>
                );
              })}
          </div>
          <div style={{ flex: 1 }}>
            {!sel ? <div style={{ fontSize: 13, color: "var(--ink-3)" }}>{T("Select a member.")}</div> : sel.isNew ? (
              <div className="form-grid">
                <div style={{ fontSize: 14, fontWeight: 700 }}>{T("New member")}</div>
                <div>
                  <label style={FIELD_LBL}>{T("Email")}</label>
                  <input className="input" type="email" value={sel.email} onChange={e => setSel(s => ({ ...s, email: e.target.value }))} placeholder="name@company.com" autoComplete="off" />
                </div>
                <div>
                  <label style={FIELD_LBL}>{T("Name")}</label>
                  <input className="input" value={sel.name} onChange={e => setSel(s => ({ ...s, name: e.target.value }))} placeholder={T("Defaults to the email")} autoComplete="off" />
                </div>
                <div>
                  <label style={FIELD_LBL}>{T("Key grants")}</label>
                  <GrantEditor keyring={sel.keyring} onChange={(kr) => setSel(s => ({ ...s, keyring: kr }))} />
                </div>
                <div>
                  <label style={FIELD_LBL}>{T("Password")}</label>
                  <input className="input" type="password" value={pw} onChange={e => setPw(e.target.value)} placeholder={T("Required")} autoComplete="new-password" />
                </div>
              </div>
            ) : (
              <div className="form-grid">
                <div style={{ fontSize: 14, fontWeight: 700 }}>{sel.name} {sel.dataroomAdmin && <span className="pill pill-gold">{T("Admin")}</span>}</div>
                <div style={{ fontSize: 12, color: "var(--ink-3)", marginTop: -8 }}>{sel.email}</div>
                {sel.dataroomAdmin && <div style={{ fontSize: 12, color: "var(--ink-3)" }}>{T("Admins hold every key at edit — grants below don’t restrict them.")}</div>}
                <div>
                  <label style={FIELD_LBL}>{T("Key grants")}</label>
                  <GrantEditor keyring={sel.keyring} onChange={(kr) => setSel(s => ({ ...s, keyring: kr }))} />
                </div>
                <div>
                  <label style={FIELD_LBL}>{T("Set / change password")}</label>
                  <input className="input" type="password" value={pw} onChange={e => setPw(e.target.value)} placeholder={T("Leave blank to keep current password")} autoComplete="new-password" />
                </div>
              </div>
            )}
          </div>
        </div>
        <div className="modal-foot">
          <div className="spacer" />
          <button className="btn btn-ghost" onClick={onClose}>{T("Close")}</button>
          <button className="btn btn-primary" disabled={!sel || busy} onClick={save}><Icon name="check" size={16} />{busy ? T("Saving…") : sel && sel.isNew ? T("Create member") : T("Save member")}</button>
        </div>
      </div>
    </div>
  );
}

// Admin list of open access requests; Grant opens the member editor pre-seeded.
function AccessRequestsModal({ onClose, onGrant }) {
  const [rows, setRows] = React.useState(null);
  const [err, setErr] = React.useState("");
  const load = async () => { try { setRows(await api.listAccessRequests("open")); setErr(""); } catch (e) { setErr(e.message || T("Could not load requests")); } };
  React.useEffect(() => { load(); }, []);
  const dismiss = async (r) => { try { await api.resolveAccessRequest(r.id); load(); } catch (e) { /* ignore */ } };
  return (
    <div className="scrim" onClick={onClose}>
      <div className="modal" style={{ maxWidth: 640 }} onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div>
            <h2>{T("Access requests")}</h2>
            <p>{T("Members who asked to be granted a higher mode on an item.")}</p>
          </div>
          <button className="xbtn" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="modal-body scroll">
          {!rows ? <div style={{ fontSize: 13, color: "var(--ink-3)" }}>{T("Loading…")}</div> :
            rows.length === 0 ? (
              <div className="empty"><div className="ic"><Icon name="check" size={26} /></div><h3>{T("No open requests")}</h3><p>{T("You’re all caught up.")}</p></div>
            ) : rows.map(r => (
              <div key={r.id} className="stage-item" style={{ alignItems: "center" }}>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 13.5, fontWeight: 600 }}>{r.userName} <span style={{ color: "var(--ink-3)", fontWeight: 400 }}>{T("wants")} </span><span className="pill"><Icon name="lock" size={11} />{r.wantMode}</span></div>
                  <div style={{ fontSize: 12, color: "var(--ink-3)", marginTop: 3 }}>{r.itemType} · {r.itemName} · {(KEYS[r.key] ? KEYS[r.key].label : r.key)}{r.currentMode ? ` · ${T("now")}: ${r.currentMode}` : ""}</div>
                  {r.note && <div style={{ fontSize: 12, color: "var(--ink-2)", marginTop: 5, fontStyle: "italic" }}>“{r.note}”</div>}
                </div>
                <button className="btn btn-ghost" onClick={() => dismiss(r)}>{T("Dismiss")}</button>
                <button className="btn btn-primary" onClick={() => onGrant(r)}><Icon name="key" size={15} />{T("Grant")}</button>
              </div>
            ))}
          {err && <div style={{ fontSize: 12, color: "var(--red,#EC2B3B)", marginTop: 9 }}>{err}</div>}
        </div>
        <div className="modal-foot"><div className="spacer" /><button className="btn btn-ghost" onClick={onClose}>{T("Close")}</button></div>
      </div>
    </div>
  );
}

Object.assign(window, { NewSectionModal, AddDocsModal, EditDocModal, BrandingModal, DataModal, KeyPicker, RequestAccessModal, MembersModal, AccessRequestsModal });
