// Full-page "create" flows: NewProjectPage, InviteMemberPage, NewApiKeyPage,
// NewSshKeyPage, UploadFilePage. Each renders inside the main content area
// with a back link to the listing page.

// ---------- Shared "create page" wrapper ----------
const CreatePage = ({ icon, title, subtitle, backTo, backLabel, children, footer }) => (
  <div className="hx-screen hx-create">
    <button className="hx-back" onClick={() => window.__nav(backTo)}>
      <IconArrowL size={14}/> Back to {backLabel}
    </button>
    <header className="hx-sh">
      <div className="hx-sh-left">
        <div className="hx-sh-title">{icon}<h1>{title}</h1></div>
        {subtitle && <div className="hx-sh-sub">{subtitle}</div>}
      </div>
    </header>
    <Card>{children}</Card>
    <div className="hx-create-foot">{footer}</div>
    <style>{`
      .hx-create { max-width: 760px; }
      .hx-back {
        display: inline-flex; align-items: center; gap: 6px;
        background: transparent; border: none; cursor: pointer;
        color: var(--muted); font: inherit; font-size: 13px; padding: 4px 0;
        margin-bottom: 4px; align-self: flex-start;
        white-space: nowrap;
      }
      .hx-back:hover { color: var(--ink); }
      .hx-create-foot { display: flex; gap: 10px; justify-content: flex-end; }
      @media (max-width: 640px) {
        .hx-create-foot { flex-direction: column-reverse; }
        .hx-create-foot > button { width: 100%; justify-content: center; }
      }
    `}</style>
  </div>
);

// ============================================================
// New Project
// ============================================================
const NewProjectPage = ({ data, setData }) => {
  const [name, setName] = React.useState("");
  const [desc, setDesc] = React.useState("");
  const [defaultModel, setDefaultModel] = React.useState("helix-7b-instruct");
  const [makeCurrent, setMakeCurrent] = React.useState(true);
  const slugify = (s) => s.toLowerCase().trim().replace(/[^a-z0-9-]+/g, "-").replace(/^-|-$/g, "");
  const slug = slugify(name) || "new-project";

  const onCreate = () => {
    setData(d => ({
      ...d,
      projects: [...d.projects, { id: "p"+Math.random().toString(36).slice(2,6), name: slug, created: "Just now", cost: 0, collab: 1 }],
      currentProject: makeCurrent ? slug : d.currentProject,
      projectOptions: [...d.projectOptions, slug],
    }));
    window.__toast(`Project "${slug}" created`);
    window.__nav("projects");
  };

  return (
    <CreatePage icon={<IconFolder size={20}/>} title="New project"
      subtitle="Projects isolate API keys, members, and spend tracking."
      backTo="projects" backLabel="Project List"
      footer={<>
        <Btn kind="secondary" onClick={() => window.__nav("projects")}>Cancel</Btn>
        <Btn kind="primary" disabled={!name.trim()} onClick={onCreate}>Create project</Btn>
      </>}>
      <FormField label="Project name" hint={name ? <>Resource slug: <code style={{fontFamily:"var(--mono)",fontSize:12}}>{slug}</code></> : "Lowercase letters, numbers, and dashes."}>
        <Input value={name} onChange={e=>setName(e.target.value)} placeholder="e.g. prod-inference"/>
      </FormField>
      <FormField label="Description" hint="Optional — shown on the project list and in invites.">
        <textarea className="hx-ta" value={desc} onChange={e=>setDesc(e.target.value)} rows={3} placeholder="What is this project for?"/>
      </FormField>
      <FormField label="Default model" hint="Used when API calls omit the model parameter.">
        <Input value={defaultModel} onChange={e=>setDefaultModel(e.target.value)}/>
      </FormField>
      <div className="hx-toggle-row">
        <Toggle checked={makeCurrent} onChange={setMakeCurrent}/>
        <div>
          <div style={{fontSize:13.5,fontWeight:500}}>Switch to this project</div>
          <div style={{fontSize:12.5,color:"var(--muted)"}}>Make the new project active immediately after creation.</div>
        </div>
      </div>
      <CreatePageStyles/>
    </CreatePage>
  );
};

// ============================================================
// Invite member — CredAxis roles + live client (tenant) list
// ============================================================
const CREDAXIS_INVITE_ROLES = [
  { id: "analyst",      title: "Analyst",      icon: IconChart,
    desc: "Create and view assessments. Cannot manage users or approve evidence." },
  { id: "viewer",       title: "Viewer",        icon: IconUsers,
    desc: "Read-only access to assessments and reports." },
  { id: "tenant_admin", title: "Client Admin",  icon: IconSettings,
    desc: "Manage users and assessments within the assigned client organization." },
  { id: "consultant",   title: "Consultant",    icon: IconUser,
    desc: "Run AI tasks, trigger evidence collection, and manage assigned clients." },
];

const InviteMemberPage = ({ data, setData }) => {
  const [emails, setEmails]   = React.useState("");
  const [role, setRole]       = React.useState("analyst");
  const [clientId, setClientId] = React.useState("");
  const [clients, setClients] = React.useState([]);
  const [loadingClients, setLoadingClients] = React.useState(true);
  const [sending, setSending] = React.useState(false);

  React.useEffect(() => {
    api.call("listTenants")
      .then(rows => setClients(Array.isArray(rows) ? rows : (rows.items ?? [])))
      .catch(() => setClients([]))
      .finally(() => setLoadingClients(false));
  }, []);

  const emailList = emails.split(/[\s,]+/).map(s => s.trim()).filter(s => s.includes("@"));
  const needsClient = role !== "consultant";
  const canSubmit = emailList.length > 0 && (!needsClient || clientId);

  const onInvite = async () => {
    setSending(true);
    let ok = 0, fail = 0;
    for (const email of emailList) {
      try {
        await api.call("inviteUser", {
          email,
          full_name: email.split("@")[0],
          role,
          tenant_id: clientId || null,
        });
        ok++;
      } catch {
        fail++;
      }
    }
    setSending(false);
    if (ok > 0) window.__toast(`${ok} invite${ok === 1 ? "" : "s"} sent`);
    if (fail > 0) window.__toast(`${fail} invite${fail === 1 ? "" : "s"} failed`, "error");
    if (ok > 0) window.__nav("members");
  };

  const selectedClient = clients.find(c => c.id === clientId);

  return (
    <CreatePage icon={<IconUsers size={20}/>} title="Invite member"
      subtitle="Invite teammates to your organization. They'll get an email link to join."
      backTo="members" backLabel="Members"
      footer={<>
        <Btn kind="secondary" onClick={() => window.__nav("members")}>Cancel</Btn>
        <Btn kind="primary" disabled={!canSubmit || sending} onClick={onInvite}>
          {sending ? "Sending…" : emailList.length > 1 ? `Send ${emailList.length} invites` : "Send invite"}
        </Btn>
      </>}>

      <FormField label="Email addresses"
        hint={emailList.length > 0
          ? `${emailList.length} valid email${emailList.length === 1 ? "" : "s"} detected. Separate multiple with commas or spaces.`
          : "Paste one or more emails, separated by commas or spaces."}>
        <textarea className="hx-ta" value={emails} onChange={e => setEmails(e.target.value)}
          rows={2} placeholder="alice@example.com, bob@example.com"/>
      </FormField>

      <FormField label="Role">
        <div className="hx-role-cards">
          {CREDAXIS_INVITE_ROLES.map(r => {
            const IconC = r.icon;
            const active = role === r.id;
            return (
              <label key={r.id} className={`hx-role-card ${active ? "on" : ""}`}>
                <input type="radio" name="role" checked={active} onChange={() => setRole(r.id)}/>
                <span className="hx-role-icon"><IconC size={16}/></span>
                <span className="hx-role-meta">
                  <span className="hx-role-name">{r.title}</span>
                  <span className="hx-role-desc">{r.desc}</span>
                </span>
                <span className="hx-role-radio" aria-hidden="true">
                  <span className="hx-role-radio-dot"/>
                </span>
              </label>
            );
          })}
        </div>
      </FormField>

      <FormField label="Client access"
        hint={role === "consultant"
          ? "Consultants have platform-wide access. Optionally restrict to one client."
          : "Select the client this member will be scoped to."}>
        <select className="hx-select" value={clientId} onChange={e => setClientId(e.target.value)}
          disabled={loadingClients}>
          <option value="">
            {loadingClients ? "Loading clients…" : role === "consultant" ? "All clients (platform-wide)" : "— Select a client —"}
          </option>
          {clients.map(c => (
            <option key={c.id} value={c.id}>{c.name}</option>
          ))}
        </select>
        {needsClient && !clientId && emailList.length > 0 && (
          <div style={{ fontSize: 12, color: "var(--bad)", marginTop: 4 }}>
            A client is required for the {role.replace("_", " ")} role.
          </div>
        )}
        {selectedClient && (
          <div style={{ fontSize: 12, color: "var(--muted)", marginTop: 4 }}>
            This member will have access to all assessments and companies under <strong>{selectedClient.name}</strong>.
          </div>
        )}
      </FormField>

      <CreatePageStyles/>
    </CreatePage>
  );
};

// ============================================================
// New API Key
// ============================================================
const NewApiKeyPage = ({ data, setData }) => {
  const [name, setName] = React.useState("");
  const [env, setEnv] = React.useState("live");
  const [project, setProject] = React.useState(data.currentProject);
  const [scopes, setScopes] = React.useState({ inference: true, files: false, finetune: false, billing: false });
  const [created, setCreated] = React.useState(null);

  const onCreate = () => {
    const key = `hx-${env}-${Math.random().toString(36).slice(2,12)}${Math.random().toString(36).slice(2,12)}`;
    setData(d => ({...d, apiKeys: [
      { id:"k"+Math.random().toString(36).slice(2,6), name, key, created: "Just now", lastUsed: "—", revealed: true }, ...d.apiKeys
    ]}));
    setCreated(key);
  };

  if (created) {
    return (
      <CreatePage icon={<IconKey size={20}/>} title="API key created"
        subtitle="Copy this key now — it won't be shown in full again."
        backTo="api-keys" backLabel="API Keys"
        footer={<Btn kind="primary" onClick={() => window.__nav("api-keys")}>Done</Btn>}>
        <div className="hx-key-result">
          <Pill tone="good">✓ Key ready</Pill>
          <div className="hx-key-box">
            <span className="hx-key-text">{created}</span>
            <Btn kind="secondary" size="sm" icon={<IconCopy size={13}/>}
              onClick={() => { navigator.clipboard.writeText(created); window.__toast("Key copied to clipboard"); }}>Copy</Btn>
          </div>
          <Banner tone="warn" icon={<IconInfo size={16}/>}>
            Store this key in a secret manager. Never commit it to source control.
          </Banner>
        </div>
        <CreatePageStyles/>
      </CreatePage>
    );
  }

  return (
    <CreatePage icon={<IconKey size={20}/>} title="New API key"
      subtitle={`API keys authenticate your applications when calling ${window.APP_CONFIG.brand.productName}.`}
      backTo="api-keys" backLabel="API Keys"
      footer={<>
        <Btn kind="secondary" onClick={() => window.__nav("api-keys")}>Cancel</Btn>
        <Btn kind="primary" disabled={!name.trim()} onClick={onCreate}>Create key</Btn>
      </>}>
      <FormField label="Name" hint="A descriptive label so you can identify this key later.">
        <Input value={name} onChange={e=>setName(e.target.value)} placeholder="e.g. prod-server, ci-pipeline"/>
      </FormField>

      <FormField label="Environment">
        <div className="hx-env-grid">
          {[
            { v: "live", label: "Live", desc: "Production traffic. Billed to your account." },
            { v: "test", label: "Test", desc: "Sandboxed. Free. Rate-limited." },
          ].map(opt => (
            <button key={opt.v} type="button" className={`hx-env-card ${env===opt.v ? "on" : ""}`} onClick={() => setEnv(opt.v)}>
              <div className="hx-env-h">
                <span className="hx-env-label">{opt.label}</span>
                <span className={`hx-env-tag ${opt.v==="live"?"live":"test"}`}>{opt.v.toUpperCase()}</span>
              </div>
              <div className="hx-env-desc">{opt.desc}</div>
            </button>
          ))}
        </div>
      </FormField>

      <FormField label="Project" hint="Spend, rate limits, and usage roll up to this project.">
        <select className="hx-select" value={project} onChange={e=>setProject(e.target.value)}>
          {data.projectOptions.map(p => <option key={p}>{p}</option>)}
        </select>
      </FormField>

      <FormField label="Scopes" hint="Restrict what this key can do.">
        <div className="hx-scope-list">
          {[
            { id: "inference", label: "Inference", desc: "Call models with completions and chat" },
            { id: "files",     label: "Files",     desc: "Upload and manage training files" },
            { id: "finetune",  label: "Fine-tune", desc: "Create and manage finetune jobs" },
            { id: "billing",   label: "Billing",   desc: "Read invoices and spend (read-only)" },
          ].map(s => (
            <label key={s.id} className="hx-scope-row">
              <input type="checkbox" checked={scopes[s.id]} onChange={e => setScopes({...scopes, [s.id]: e.target.checked})}/>
              <div>
                <div style={{fontSize:13.5,fontWeight:500}}>{s.label}</div>
                <div style={{fontSize:12.5,color:"var(--muted)"}}>{s.desc}</div>
              </div>
            </label>
          ))}
        </div>
      </FormField>

      <CreatePageStyles/>
    </CreatePage>
  );
};

// ============================================================
// New SSH Key
// ============================================================
const NewSshKeyPage = ({ data, setData }) => {
  const [label, setLabel] = React.useState("");
  const [pub, setPub] = React.useState("");
  const valid = pub.trim().startsWith("ssh-") && pub.trim().split(/\s+/).length >= 2;
  const onCreate = () => {
    const fp = "SHA256:" + Array.from({length:16}, () => Math.floor(Math.random()*256).toString(16).padStart(2,"0")).join(":");
    setData(d => ({...d, sshKeys: [...d.sshKeys, { id:"s"+Math.random().toString(36).slice(2,6), label, fp, added: "Just now" }]}));
    window.__toast(`SSH key "${label}" added`);
    window.__nav("ssh-key");
  };

  return (
    <CreatePage icon={<IconKey size={20}/>} title="Add SSH key"
      subtitle="Authorize a public key for GPU cluster access."
      backTo="ssh-key" backLabel="SSH Keys"
      footer={<>
        <Btn kind="secondary" onClick={() => window.__nav("ssh-key")}>Cancel</Btn>
        <Btn kind="primary" disabled={!label.trim() || !valid} onClick={onCreate}>Add SSH key</Btn>
      </>}>
      <FormField label="Label" hint="A name for this key (usually the machine it's on).">
        <Input value={label} onChange={e=>setLabel(e.target.value)} placeholder="e.g. work-macbook"/>
      </FormField>
      <FormField label="Public key" hint={pub && !valid ? <span style={{color:"var(--bad)"}}>Doesn't look like a valid public key. Should start with "ssh-rsa", "ssh-ed25519", etc.</span> : "Paste the contents of your .pub file. Begins with 'ssh-rsa' or 'ssh-ed25519'."}>
        <textarea className="hx-ta hx-ta-mono" value={pub} onChange={e=>setPub(e.target.value)} rows={6} placeholder="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... user@host"/>
      </FormField>
      <details className="hx-help-details">
        <summary>How do I get my public key?</summary>
        <div style={{padding: "8px 0", fontSize: 13, color: "var(--ink-2)", lineHeight: 1.6}}>
          <p style={{margin: "4px 0"}}>On macOS or Linux, run:</p>
          <pre className="hx-code">cat ~/.ssh/id_ed25519.pub</pre>
          <p style={{margin: "4px 0"}}>If the file doesn't exist, generate one with:</p>
          <pre className="hx-code">ssh-keygen -t ed25519 -C "your-email@example.com"</pre>
        </div>
      </details>
      <CreatePageStyles/>
    </CreatePage>
  );
};

// ============================================================
// Upload File — with real file input + progress
// ============================================================
const UploadFilePage = ({ data, setData }) => {
  const [files, setFiles] = React.useState([]);     // [{file, progress, done, error}]
  const [purpose, setPurpose] = React.useState("fine-tune");
  const inputRef = React.useRef();
  const [dragOver, setDragOver] = React.useState(false);

  const fmt = (n) => n < 1024 ? `${n} B` : n < 1024*1024 ? `${(n/1024).toFixed(1)} KB` : n < 1024*1024*1024 ? `${(n/(1024*1024)).toFixed(1)} MB` : `${(n/(1024*1024*1024)).toFixed(2)} GB`;

  const addFiles = (fileList) => {
    const arr = Array.from(fileList);
    setFiles(prev => [...prev, ...arr.map(file => ({ file, progress: 0, done: false }))]);
    // Simulate upload progress
    arr.forEach((file, j) => {
      const start = files.length + j;
      const interval = setInterval(() => {
        setFiles(prev => {
          const copy = [...prev];
          const t = copy[start];
          if (!t || t.done) { clearInterval(interval); return prev; }
          const next = Math.min(100, t.progress + Math.random()*18 + 4);
          copy[start] = { ...t, progress: next, done: next >= 100 };
          if (next >= 100) clearInterval(interval);
          return copy;
        });
      }, 220);
    });
  };

  const onDrop = (e) => {
    e.preventDefault(); setDragOver(false);
    if (e.dataTransfer.files?.length) addFiles(e.dataTransfer.files);
  };

  const allDone = files.length > 0 && files.every(f => f.done);

  const onCommit = () => {
    const purposeLabel = ({ "fine-tune": "fine-tune", "evaluation": "evaluation", "batch": "batch", "reference": "reference" })[purpose];
    const today = new Date().toLocaleDateString("en-US", { month:"short", day:"2-digit", year:"numeric" });
    const newRows = files.map(f => ({
      id: "f"+Math.random().toString(36).slice(2,6),
      name: f.file.name,
      purpose: purposeLabel,
      size: fmt(f.file.size),
      created: today,
    }));
    setData(d => ({...d, files: [...newRows, ...d.files]}));
    window.__toast(`${files.length} file${files.length===1?"":"s"} uploaded`);
    window.__nav("files");
  };

  return (
    <CreatePage icon={<IconUpload size={20}/>} title="Upload file"
      subtitle="Upload training data, evaluation sets, or batch input files."
      backTo="files" backLabel="Files"
      footer={<>
        <Btn kind="secondary" onClick={() => window.__nav("files")}>Cancel</Btn>
        <Btn kind="primary" disabled={!allDone} onClick={onCommit}>
          {files.length === 0 ? "Upload" : allDone ? `Add ${files.length} file${files.length===1?"":"s"}` : "Uploading…"}
        </Btn>
      </>}>
      <FormField label="Purpose" hint="How this file will be used.">
        <select className="hx-select" value={purpose} onChange={e=>setPurpose(e.target.value)}>
          <option value="fine-tune">Fine-tuning data</option>
          <option value="evaluation">Evaluation set</option>
          <option value="batch">Batch inference input</option>
          <option value="reference">Reference / docs</option>
        </select>
      </FormField>

      <FormField label="Files" hint="JSONL, TXT, or CSV. Up to 5 GB per file.">
        <div
          className={`hx-drop ${dragOver ? "on" : ""}`}
          onDragOver={(e)=>{e.preventDefault(); setDragOver(true);}}
          onDragLeave={()=>setDragOver(false)}
          onDrop={onDrop}
          onClick={() => inputRef.current?.click()}
          role="button" tabIndex={0}
          onKeyDown={(e) => (e.key === "Enter" || e.key === " ") && inputRef.current?.click()}
        >
          <IconUpload size={26} className="hx-drop-icon"/>
          <div className="hx-drop-title">Drop files here, or <span className="hx-link">browse</span></div>
          <div className="hx-drop-hint">JSONL · TXT · CSV — up to 5 GB</div>
          <input ref={inputRef} type="file" multiple style={{ display: "none" }}
            onChange={e => e.target.files && addFiles(e.target.files)} />
        </div>
      </FormField>

      {files.length > 0 && (
        <div className="hx-upload-list">
          {files.map((f, i) => (
            <div key={i} className="hx-upload-row">
              <IconFile size={16} style={{color:"var(--muted)",flexShrink:0}}/>
              <div className="hx-upload-meta">
                <div className="hx-upload-name">{f.file.name}</div>
                <div className="hx-upload-sub">
                  <span>{fmt(f.file.size)}</span>
                  <span>·</span>
                  {f.done ? <span style={{color:"var(--good)"}}>✓ Uploaded</span> : <span>{Math.round(f.progress)}%</span>}
                </div>
                <div className="hx-upload-bar"><div className="hx-upload-bar-fill" style={{width: f.progress + "%"}}/></div>
              </div>
              <button className="hx-icon-btn" onClick={(e) => { e.stopPropagation(); setFiles(prev => prev.filter((_, j) => j !== i)); }} aria-label="Remove">
                <IconClose size={14}/>
              </button>
            </div>
          ))}
        </div>
      )}

      <CreatePageStyles/>
    </CreatePage>
  );
};

// ---------- Shared form/page styles ----------
const CreatePageStyles = () => (
  <style>{`
    .hx-ta { width: 100%; font: inherit; padding: 10px 12px; border: 1px solid var(--line); border-radius: 8px; resize: vertical; background: var(--panel); color: var(--ink); font-size: 14px; }
    .hx-ta:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 18%, transparent); }
    .hx-ta-mono { font-family: var(--mono); font-size: 12.5px; line-height: 1.5; }
    .hx-select { width: 100%; height: var(--row); padding: 0 10px; font: inherit; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); color: var(--ink); }
    .hx-select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 18%, transparent); }

    .hx-toggle-row { display: flex; gap: 12px; align-items: center; padding: 12px 14px; background: var(--panel-2); border: 1px solid var(--line); border-radius: 10px; margin-top: 8px; }

    /* Role cards */
    .hx-role-cards { display: flex; flex-direction: column; gap: 8px; }
    .hx-role-card {
      display: grid;
      grid-template-columns: auto 1fr auto;
      gap: 14px; align-items: flex-start;
      padding: 14px; cursor: pointer;
      background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
      transition: border-color .12s, background .12s;
    }
    .hx-role-card:hover { border-color: color-mix(in oklab, var(--ink) 30%, transparent); }
    .hx-role-card.on { border-color: var(--accent); background: var(--accent-soft); }
    .hx-role-card input { position: absolute; opacity: 0; pointer-events: none; }
    .hx-role-icon {
      width: 34px; height: 34px; border-radius: 8px;
      background: var(--panel-2); border: 1px solid var(--line);
      display: inline-flex; align-items: center; justify-content: center; color: var(--ink-2);
      flex-shrink: 0;
    }
    .hx-role-card.on .hx-role-icon { background: var(--panel); color: var(--accent); border-color: color-mix(in oklab, var(--accent) 35%, transparent); }
    .hx-role-meta { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
    .hx-role-name { font-size: 14px; font-weight: 600; color: var(--ink); }
    .hx-role-desc { font-size: 13px; color: var(--muted); line-height: 1.5; }
    .hx-role-radio {
      width: 18px; height: 18px; border-radius: 50%;
      border: 1.5px solid var(--line);
      display: inline-flex; align-items: center; justify-content: center;
      flex-shrink: 0; margin-top: 8px;
      background: var(--panel);
    }
    .hx-role-card.on .hx-role-radio { border-color: var(--accent); background: var(--accent); }
    .hx-role-radio-dot { width: 6px; height: 6px; border-radius: 50%; background: white; opacity: 0; }
    .hx-role-card.on .hx-role-radio-dot { opacity: 1; }

    /* Env cards (for API key) */
    .hx-env-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
    @media (max-width: 640px) { .hx-env-grid { grid-template-columns: 1fr; } }
    .hx-env-card {
      text-align: left; padding: 14px; cursor: pointer;
      background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
      font: inherit; color: var(--ink);
    }
    .hx-env-card:hover { border-color: color-mix(in oklab, var(--ink) 30%, transparent); }
    .hx-env-card.on { border-color: var(--accent); background: var(--accent-soft); }
    .hx-env-h { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; }
    .hx-env-label { font-size: 14px; font-weight: 600; }
    .hx-env-tag { font-family: var(--mono); font-size: 10.5px; padding: 2px 6px; border-radius: 4px; font-weight: 500; }
    .hx-env-tag.live { background: #fee9e0; color: #b04a14; }
    .hx-env-tag.test { background: var(--line-2); color: var(--muted); }
    .hx-env-desc { font-size: 13px; color: var(--muted); line-height: 1.4; }

    /* Scope list */
    .hx-scope-list { display: flex; flex-direction: column; gap: 4px; }
    .hx-scope-row { display: flex; gap: 12px; align-items: flex-start; padding: 10px; border-radius: 8px; cursor: pointer; }
    .hx-scope-row:hover { background: var(--hover); }
    .hx-scope-row input { margin-top: 3px; accent-color: var(--accent); }

    /* SSH help */
    .hx-help-details { margin-top: 4px; }
    .hx-help-details summary { color: var(--muted); cursor: pointer; font-size: 13px; user-select: none; padding: 4px 0; }
    .hx-help-details summary:hover { color: var(--ink-2); }
    .hx-code { background: var(--panel-2); border: 1px solid var(--line); border-radius: 6px; padding: 8px 10px; font-family: var(--mono); font-size: 12.5px; margin: 4px 0; overflow-x: auto; }

    /* API key result */
    .hx-key-result { display: flex; flex-direction: column; gap: 14px; }
    .hx-key-box { display: flex; gap: 10px; align-items: center; padding: 12px 14px; border: 1px solid var(--line); border-radius: 10px; background: var(--panel-2); font-family: var(--mono); font-size: 13px; flex-wrap: wrap; }
    .hx-key-text { flex: 1; min-width: 0; word-break: break-all; }

    /* File drop zone */
    .hx-drop {
      display: flex; flex-direction: column; gap: 6px; align-items: center; justify-content: center;
      padding: 36px 20px; cursor: pointer;
      border: 1.5px dashed var(--line); border-radius: 12px;
      background: var(--panel-2);
      transition: border-color .12s, background .12s;
    }
    .hx-drop:hover, .hx-drop.on {
      border-color: var(--accent);
      background: var(--accent-soft);
    }
    .hx-drop-icon { color: var(--muted); }
    .hx-drop:hover .hx-drop-icon, .hx-drop.on .hx-drop-icon { color: var(--accent); }
    .hx-drop-title { font-size: 14px; font-weight: 500; color: var(--ink); }
    .hx-drop-hint { font-size: 12.5px; color: var(--muted); }

    .hx-upload-list { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
    .hx-upload-row {
      display: flex; gap: 12px; align-items: center;
      padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px;
      background: var(--panel-2);
    }
    .hx-upload-meta { flex: 1; min-width: 0; }
    .hx-upload-name { font-size: 13.5px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .hx-upload-sub { font-size: 12px; color: var(--muted); display: flex; gap: 6px; margin-top: 2px; }
    .hx-upload-bar { height: 4px; background: var(--line); border-radius: 2px; overflow: hidden; margin-top: 6px; }
    .hx-upload-bar-fill { height: 100%; background: var(--accent); transition: width .2s ease; }
  `}</style>
);

Object.assign(window, {
  NewProjectPage, InviteMemberPage, NewApiKeyPage, NewSshKeyPage, UploadFilePage,
});
