Files
Bonchellon 3954082e8d v0.3.2 — commit/submit progress, cancel, thumbnails, waitlist landing+admin
Client:
- Commit: batch `p4 reopen` so huge changelists (63k+) no longer blow the
  Windows command-line limit; live "N / total" progress + spinner during commit.
- Submit dialog: white arrow, "N of total" + % bar, running file log,
  Cancel button (aborts pre-commit; changelist stays pending).
- Disk scan: streaming reconcile with a live "found N files" counter + Cancel.
- Thumbnails: only decode browser-renderable image formats; glyph fallback
  (+ onError) instead of a broken-image icon for bmp/tga/dds/etc.

Landing + waitlist:
- waitlist-server: Express + SQLite email collector (dedup) with a secure
  admin panel (scrypt auth, rate-limited login, signed cookies) and CSV export.
- landing form now POSTs signups to the waitlist API.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 20:40:53 +03:00

877 lines
62 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Exbyte Depot — Perforce for Unreal teams, finally pleasant</title>
<meta name="description" content="Exbyte Depot is a fast native Perforce client built for Unreal Engine teams: 3D asset previews, one-click exclusive locks, offline work, and live teammate activity. All the power of Helix Core, none of the P4V pain." />
<style>
:root{
--bg:#0a0a0f; --bg-2:#0d0d14; --panel:#12121b; --panel-2:#16161f; --elevated:#1a1a24;
--line:rgba(255,255,255,.08); --line-2:rgba(255,255,255,.05);
--txt:#ececf4; --muted:#a2a2b4; --faint:#6d6d80;
--acc:#7c6ef6; --acc-2:#9d93ff; --acc-deep:#5b4ee0;
--ok:#3ecf8e; --warn:#e8b04b; --del:#f2637e;
--maxw:1180px;
--font:"Segoe UI Variable Display","Segoe UI",system-ui,-apple-system,Roboto,"Helvetica Neue",Arial,sans-serif;
--mono:"Cascadia Code","JetBrains Mono",ui-monospace,"SF Mono",Consolas,monospace;
--shadow:0 40px 120px -40px rgba(0,0,0,.8);
}
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{
font-family:var(--font);background:var(--bg);color:var(--txt);
-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
line-height:1.6;overflow-x:hidden;
}
/* ambient background */
body::before{
content:"";position:fixed;inset:0;z-index:-2;pointer-events:none;
background:
radial-gradient(900px 640px at 78% -6%,rgba(124,110,246,.22),transparent 60%),
radial-gradient(760px 620px at 6% 8%,rgba(91,78,224,.16),transparent 58%),
var(--bg);
}
body::after{
content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;opacity:.5;
background-image:
linear-gradient(rgba(255,255,255,.020) 1px,transparent 1px),
linear-gradient(90deg,rgba(255,255,255,.020) 1px,transparent 1px);
background-size:46px 46px;
-webkit-mask-image:radial-gradient(1200px 800px at 60% 0%,#000 30%,transparent 78%);
mask-image:radial-gradient(1200px 800px at 60% 0%,#000 30%,transparent 78%);
}
a{color:inherit;text-decoration:none}
.tlink{color:var(--acc-2);text-decoration:underline;text-underline-offset:2px;text-decoration-color:rgba(157,147,255,.45);transition:.15s}
.tlink:hover{text-decoration-color:var(--acc-2)}
/* "works with your Perforce server" reassurance band */
.clarify{display:flex;align-items:center;justify-content:center;gap:12px;flex-wrap:wrap;text-align:center;
max-width:760px;margin:26px auto 0;padding:14px 20px;border-radius:14px;
background:var(--panel);border:1px solid var(--line);color:var(--muted);font-size:14px;line-height:1.55}
.clarify svg{width:18px;height:18px;color:var(--acc-2);flex:0 0 auto}
.clarify b{color:var(--txt);font-weight:600}
.wrap{width:100%;max-width:var(--maxw);margin:0 auto;padding:0 24px}
.eyebrow{display:inline-flex;align-items:center;gap:8px;font-size:12.5px;font-weight:600;letter-spacing:.08em;
text-transform:uppercase;color:var(--acc-2);background:rgba(124,110,246,.10);border:1px solid rgba(124,110,246,.28);
padding:6px 13px;border-radius:100px}
.eyebrow .dot{width:6px;height:6px;border-radius:50%;background:var(--acc-2);box-shadow:0 0 10px var(--acc-2)}
h1,h2,h3{line-height:1.08;letter-spacing:-.022em;font-weight:700;text-wrap:balance}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;font-family:inherit;font-size:15px;
font-weight:600;cursor:pointer;border-radius:12px;padding:13px 22px;border:1px solid transparent;transition:.18s ease;white-space:nowrap}
.btn.primary{color:#fff;background:linear-gradient(135deg,var(--acc-2),var(--acc-deep));
box-shadow:0 14px 34px -14px var(--acc),inset 0 1px 0 rgba(255,255,255,.22)}
.btn.primary:hover{transform:translateY(-1px);box-shadow:0 20px 44px -14px var(--acc),inset 0 1px 0 rgba(255,255,255,.3)}
.btn.ghost{color:var(--txt);background:rgba(255,255,255,.04);border-color:var(--line)}
.btn.ghost:hover{background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.18)}
.btn svg{width:17px;height:17px}
:focus-visible{outline:2px solid var(--acc-2);outline-offset:2px;border-radius:8px}
/* ---------- nav ---------- */
header.nav{position:sticky;top:0;z-index:50;backdrop-filter:blur(14px);
background:linear-gradient(180deg,rgba(10,10,15,.86),rgba(10,10,15,.55));border-bottom:1px solid var(--line-2)}
.nav-in{display:flex;align-items:center;gap:20px;height:66px}
.brand{display:flex;align-items:center;gap:11px;font-weight:700;font-size:16.5px;letter-spacing:-.01em}
.brand .mark{width:34px;height:34px;border-radius:9px;display:grid;place-items:center;color:#fff;
background:linear-gradient(140deg,var(--acc),var(--acc-deep));box-shadow:0 8px 22px -8px var(--acc),inset 0 1px 0 rgba(255,255,255,.35)}
.brand .mark svg{width:19px;height:19px}
.brand small{color:var(--faint);font-weight:500}
.nav-links{display:flex;gap:26px;margin-left:18px}
.nav-links a{font-size:14.5px;color:var(--muted);font-weight:500;transition:.15s}
.nav-links a:hover{color:var(--txt)}
.nav-cta{margin-left:auto;display:flex;align-items:center;gap:12px}
/* ---------- hero ---------- */
.hero{padding:74px 0 40px;text-align:center}
.hero h1{font-size:clamp(38px,6vw,68px);margin:22px auto 0;max-width:16ch}
.hero h1 .g{background:linear-gradient(120deg,var(--acc-2),#c9c2ff 60%,var(--acc));-webkit-background-clip:text;background-clip:text;color:transparent}
.hero p.sub{font-size:clamp(16px,2vw,19px);color:var(--muted);max-width:60ch;margin:22px auto 0;line-height:1.62}
.waitlist{margin:34px auto 0;max-width:520px}
.wl-form{display:flex;gap:10px;background:var(--panel);border:1px solid var(--line);border-radius:15px;padding:7px;
box-shadow:var(--shadow)}
.wl-form input{flex:1;min-width:0;background:transparent;border:none;outline:none;color:var(--txt);font-family:inherit;
font-size:15px;padding:12px 14px}
.wl-form input::placeholder{color:var(--faint)}
.wl-note{margin-top:12px;font-size:13px;color:var(--faint);display:flex;align-items:center;justify-content:center;gap:8px;flex-wrap:wrap}
.wl-note b{color:var(--muted);font-weight:600}
.wl-ok{display:none;align-items:center;gap:12px;justify-content:center;background:rgba(62,207,142,.10);
border:1px solid rgba(62,207,142,.32);color:#8ff0c4;border-radius:15px;padding:18px 20px;font-size:15px;box-shadow:var(--shadow)}
.wl-ok.show{display:flex}
.wl-ok svg{width:22px;height:22px;flex:0 0 auto}
.avatars{display:flex;justify-content:center;margin-top:22px;align-items:center;gap:12px;color:var(--faint);font-size:13.5px}
.stack{display:flex}
.stack span{width:28px;height:28px;border-radius:50%;border:2px solid var(--bg);margin-left:-8px;
display:grid;place-items:center;font-size:11px;font-weight:700;color:#fff}
/* ---------- app mock ---------- */
.mock-shell{margin:56px 0 0;width:min(1280px,95vw);max-width:1280px;perspective:1800px;
position:relative;left:50%;transform:translateX(-50%)}
.mock{border-radius:16px;overflow:hidden;border:1px solid var(--line);background:var(--panel);
box-shadow:var(--shadow),0 0 0 1px rgba(124,110,246,.10);transform:rotateX(2.5deg);transform-origin:center top}
.m-title{display:flex;align-items:center;gap:12px;height:46px;padding:0 14px;background:var(--panel-2);border-bottom:1px solid var(--line)}
.m-title .mark{width:22px;height:22px;border-radius:6px;display:grid;place-items:center;color:#fff;background:linear-gradient(140deg,var(--acc),var(--acc-deep))}
.m-title .mark svg{width:13px;height:13px}
.m-title .t{font-size:13px;font-weight:600}.m-title .t span{color:var(--faint);font-weight:500}
.m-menu{display:flex;gap:16px;margin-left:10px;font-size:12.5px;color:var(--muted)}
.m-pill{margin-left:auto;display:flex;align-items:center;gap:7px;font-size:11.5px;color:var(--muted);
background:rgba(62,207,142,.10);border:1px solid rgba(62,207,142,.28);padding:4px 11px;border-radius:100px}
.m-pill .d{width:6px;height:6px;border-radius:50%;background:var(--ok);box-shadow:0 0 8px var(--ok)}
.m-body{display:grid;grid-template-columns:340px 1fr;min-height:376px}
.m-left{border-right:1px solid var(--line);display:flex;flex-direction:column}
.m-tabs{display:flex;gap:20px;padding:14px 16px 0;font-size:13px;color:var(--faint)}
.m-tabs .on{color:var(--txt);font-weight:600;position:relative;padding-bottom:12px}
.m-tabs .on::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;background:var(--acc);border-radius:2px}
.m-tabs .badge{background:rgba(255,255,255,.08);color:var(--muted);border-radius:8px;padding:0 7px;font-size:11px;margin-left:5px}
.m-list{padding:12px 12px 0;display:flex;flex-direction:column;gap:7px}
.m-file{display:flex;align-items:center;gap:10px;padding:8px 9px;border-radius:10px}
.m-file:hover{background:rgba(255,255,255,.03)}
.m-file.sel{background:rgba(124,110,246,.12);box-shadow:inset 0 0 0 1px rgba(124,110,246,.25)}
.m-chk{width:16px;height:16px;border-radius:5px;background:linear-gradient(135deg,var(--acc-2),var(--acc-deep));display:grid;place-items:center;flex:0 0 auto}
.m-chk svg{width:10px;height:10px;color:#fff}
.m-ico{width:26px;height:26px;border-radius:7px;background:var(--panel-2);border:1px solid var(--line);display:grid;place-items:center;color:var(--acc-2);flex:0 0 auto}
.m-ico svg{width:14px;height:14px}
.m-fmeta{min-width:0}
.m-fmeta .n{font-size:12.5px;font-weight:600;white-space:nowrap}
.m-fmeta .p{font-size:10.5px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:180px}
.m-plus{margin-left:auto;color:var(--ok)}
.m-commit{margin-top:auto;padding:14px;border-top:1px solid var(--line);display:flex;flex-direction:column;gap:9px}
.m-input{background:var(--panel-2);border:1px solid var(--line);border-radius:9px;padding:9px 11px;font-size:12px;color:var(--muted)}
.m-submit{background:linear-gradient(135deg,var(--acc-2),var(--acc-deep));color:#fff;border-radius:10px;padding:11px;
font-size:12.5px;font-weight:600;text-align:center;display:flex;align-items:center;justify-content:center;gap:7px}
.m-stage{position:relative;display:grid;place-items:center;
background:
radial-gradient(520px 380px at 50% 38%,rgba(124,110,246,.12),transparent 62%),
repeating-linear-gradient(0deg,transparent,transparent 31px,rgba(255,255,255,.02) 31px,rgba(255,255,255,.02) 32px),
repeating-linear-gradient(90deg,transparent,transparent 31px,rgba(255,255,255,.02) 31px,rgba(255,255,255,.02) 32px),
var(--bg-2)}
.m-stage .lab{position:absolute;top:14px;left:14px;font-size:11px;color:var(--muted);background:rgba(0,0,0,.4);
border:1px solid var(--line);border-radius:100px;padding:5px 11px;display:flex;align-items:center;gap:7px}
.m-cube{width:120px;height:120px;color:var(--acc-2);filter:drop-shadow(0 20px 40px rgba(124,110,246,.4));animation:float 5s ease-in-out infinite}
@keyframes float{0%,100%{transform:translateY(0) rotate(-4deg)}50%{transform:translateY(-12px) rotate(4deg)}}
.m-load{position:absolute;bottom:56px;left:50%;transform:translateX(-50%);
background:linear-gradient(135deg,var(--acc-2),var(--acc-deep));color:#fff;border-radius:10px;padding:10px 18px;
font-size:12.5px;font-weight:600;display:flex;align-items:center;gap:8px;box-shadow:0 14px 30px -12px var(--acc)}
/* titlebar extras */
.m-title .m-actions{display:flex;align-items:center;gap:6px;margin-left:14px}
.m-ib{width:26px;height:26px;border-radius:7px;display:grid;place-items:center;color:var(--muted);background:rgba(255,255,255,.04);border:1px solid var(--line-2)}
.m-ib svg{width:14px;height:14px}
.m-wc{display:flex;gap:9px;margin-left:12px}
.m-wc i{width:12px;height:12px;border-radius:50%;display:block}
.m-wc .r{background:#ff5f57}.m-wc .y{background:#febc2e}.m-wc .g{background:#28c840}
/* second header row: workspace / working folder / get latest */
.m-head{display:grid;grid-template-columns:1fr 1fr 190px;border-bottom:1px solid var(--line)}
.m-zone{display:flex;align-items:center;gap:11px;padding:12px 16px;border-right:1px solid var(--line);min-width:0}
.m-zone .zi{width:30px;height:30px;border-radius:8px;display:grid;place-items:center;color:var(--acc-2);background:rgba(124,110,246,.10);border:1px solid rgba(124,110,246,.2);flex:0 0 auto}
.m-zone .zi svg{width:16px;height:16px}
.m-zone .zm{min-width:0}
.m-zone .zm .l{font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
.m-zone .zm .v{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.m-zone .cv{margin-left:auto;color:var(--faint);flex:0 0 auto}
.m-zone.get{border-right:none;cursor:default}
.m-zone.get .zi{color:var(--ok);background:rgba(62,207,142,.10);border-color:rgba(62,207,142,.22)}
.m-zone.get .v{color:var(--txt)}.m-zone.get .l{color:var(--faint)}
/* left column bits */
.m-filter{margin:12px 12px 4px;display:flex;align-items:center;gap:8px;background:var(--panel-2);border:1px solid var(--line);border-radius:9px;padding:8px 11px;color:var(--faint);font-size:12px}
.m-filter svg{width:14px;height:14px}
.m-selhead{display:flex;align-items:center;gap:9px;padding:8px 16px 6px;font-size:11.5px;color:var(--muted)}
.m-selhead .sc{width:15px;height:15px;border-radius:5px;background:linear-gradient(135deg,var(--acc-2),var(--acc-deep));display:grid;place-items:center;flex:0 0 auto}
.m-selhead .sc svg{width:9px;height:9px;color:#fff}
.m-selhead .rf{margin-left:auto;display:flex;align-items:center;gap:5px;color:var(--muted);background:rgba(255,255,255,.04);border:1px solid var(--line);border-radius:7px;padding:4px 9px;font-size:11px}
.m-selhead .rf svg{width:12px;height:12px}
.m-stat{width:18px;height:18px;border-radius:5px;display:grid;place-items:center;font-size:10px;font-weight:700;flex:0 0 auto}
.m-stat.a{color:var(--ok);background:rgba(62,207,142,.14)}
.m-plus{margin-left:auto}
.ai-btn{width:34px;height:34px;flex:0 0 auto;border-radius:9px;display:grid;place-items:center;color:var(--acc-2);background:rgba(124,110,246,.10);border:1px solid rgba(124,110,246,.24)}
.ai-btn svg{width:16px;height:16px}
.m-summrow{display:flex;gap:8px;align-items:stretch}
.m-summrow .m-input{flex:1}
/* right: diff head + meta bar */
.m-right{display:flex;flex-direction:column;min-width:0}
.m-diffhead{display:flex;align-items:center;gap:11px;padding:12px 16px;border-bottom:1px solid var(--line)}
.m-diffhead .ttl{min-width:0}
.m-diffhead .ttl .n{font-size:13.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.m-diffhead .ttl .n span{color:var(--faint);font-weight:500}
.m-diffhead .ttl .mm{font-size:11px;color:var(--faint)}
.m-diffhead .mrev{color:var(--acc-2)}
.m-revert{margin-left:auto;display:flex;align-items:center;gap:6px;font-size:12px;color:var(--muted);background:rgba(255,255,255,.04);border:1px solid var(--line);border-radius:8px;padding:6px 12px;flex:0 0 auto}
.m-revert svg{width:13px;height:13px}
.m-stage{flex:1;position:relative}
.m-metabar{border-top:1px solid var(--line);padding:12px 18px;display:grid;grid-template-columns:repeat(4,1fr);gap:10px;background:rgba(0,0,0,.16)}
.m-metabar .mi .k{font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}
.m-metabar .mi .v{font-size:13px;font-weight:600;margin-top:3px}
.m-metabar .mi .v.acc{color:var(--acc-2)}
/* bottom status bar */
.m-status{display:flex;align-items:center;gap:9px;height:34px;padding:0 14px;border-top:1px solid var(--line);background:var(--panel-2);font-size:11px;color:var(--faint)}
.m-status .cmd{font-family:var(--mono);color:var(--muted)}
.m-status .ok{color:var(--ok)}
.m-status .chips{margin-left:auto;display:flex;gap:7px}
.m-status .chips b{display:flex;align-items:center;gap:5px;font-weight:500;color:var(--muted);background:rgba(255,255,255,.04);border:1px solid var(--line-2);border-radius:7px;padding:3px 9px}
.m-status .chips b svg{width:12px;height:12px}
@media (max-width:900px){
.m-head{grid-template-columns:1fr}
.m-zone{border-right:none;border-bottom:1px solid var(--line)}
.m-zone.get{display:none}
.m-metabar{grid-template-columns:1fr 1fr}
}
/* ---- interactive mock: hover / active / press ---- */
.m-menu span,.m-file,.m-tabs span,.m-submit,.m-load,.m-zone.get,.m-ib,.m-selhead .rf,
.m-revert,.m-chk,.ai-btn,.m-status .chips b,.m-wc i{cursor:pointer}
.m-menu span{padding:3px 6px;border-radius:6px;transition:.14s}
.m-menu span:hover{background:rgba(255,255,255,.06);color:var(--txt)}
.m-tabs span:hover{color:var(--txt)}
.m-ib{transition:.14s}.m-ib:hover{color:var(--txt);background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.16)}
.m-wc i{transition:.14s}.m-wc i:hover{filter:brightness(1.25)}
.m-file{transition:background .14s,box-shadow .14s;user-select:none}
.m-file:active{transform:translateY(.5px)}
.m-chk{transition:.14s}
.m-file.off .m-chk{background:transparent;border:1.5px solid var(--faint);box-shadow:none}
.m-file.off .m-chk svg{display:none}
.m-selhead .rf,.m-revert{transition:.14s}
.m-selhead .rf:hover,.m-revert:hover{color:var(--txt);border-color:rgba(255,255,255,.2);background:rgba(255,255,255,.07)}
.m-selhead .rf.spin svg{animation:mspin .7s linear infinite}
.ai-btn{transition:.14s}.ai-btn:hover{background:rgba(124,110,246,.18);border-color:rgba(124,110,246,.4)}
.m-zone.get{transition:.14s;border-radius:0}.m-zone.get:hover{background:rgba(62,207,142,.06)}
.m-zone.get.spin .zi svg{animation:mspin .7s linear infinite}
.m-submit,.m-load{transition:transform .12s,box-shadow .14s,filter .14s}
.m-submit:hover,.m-load:hover{filter:brightness(1.08)}
.m-submit:active,.m-load:active{transform:scale(.98)}
.m-load:hover{transform:translateX(-50%) scale(1.03)}
.m-load:active{transform:translateX(-50%) scale(.97)}
.m-status .chips b{transition:.14s}.m-status .chips b:hover{color:var(--txt);border-color:var(--line)}
@keyframes mspin{to{transform:rotate(360deg)}}
.m-spinner{width:14px;height:14px;border:2px solid rgba(255,255,255,.4);border-top-color:#fff;border-radius:50%;
animation:mspin .7s linear infinite;display:inline-block}
/* preview variants */
.pv{position:absolute;inset:0;display:grid;place-items:center}
.pv[hidden]{display:none}
.m-cube.loaded{animation:mrotate 7s linear infinite}
@keyframes mrotate{to{transform:rotateY(360deg)}}
.pv-mesh{perspective:900px}
.tex-swatch{width:min(62%,300px);aspect-ratio:1;border-radius:14px;box-shadow:0 24px 60px -24px #000;border:1px solid var(--line);
background:
repeating-conic-gradient(from 45deg,#2a2733 0deg 90deg,#211f2a 90deg 180deg) 0 0/26px 26px,
radial-gradient(120% 120% at 30% 20%,rgba(124,110,246,.25),transparent 60%)}
.tex-cap{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);font-size:11px;color:var(--muted);
background:rgba(0,0,0,.4);border:1px solid var(--line);border-radius:100px;padding:5px 12px}
.pv-code{align-items:stretch;justify-items:stretch;padding:0}
.codeblk{width:100%;height:100%;overflow:hidden;font-family:var(--mono);font-size:12px;line-height:1.75;
padding:16px 18px;color:#c7c7d6;text-align:left;background:var(--bg-2)}
.codeblk .ln{display:block;white-space:pre}
.codeblk .kw{color:#9d93ff}.codeblk .ty{color:#5cc9c0}.codeblk .fn{color:#e8b04b}.codeblk .cm{color:#5f5f72;font-style:italic}.codeblk .st{color:#8ff0c4}
/* only one body panel visible at a time (beats inline/class display) */
.m-panel[hidden]{display:none!important}
/* history list (History tab) */
.m-hist{padding:10px 12px;display:flex;flex-direction:column;gap:4px;overflow:auto}
.m-hrow{display:flex;align-items:center;gap:11px;padding:9px 10px;border-radius:10px;cursor:pointer;transition:.14s}
.m-hrow:hover{background:rgba(255,255,255,.04)}
.m-hav{width:26px;height:26px;border-radius:50%;flex:0 0 auto;display:grid;place-items:center;font-size:10px;font-weight:700;color:#fff}
.m-hb{min-width:0;flex:1}
.m-hb .d{font-size:12.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.m-hb .mt{font-size:10.5px;color:var(--faint)}
.m-hsz{font-size:10.5px;font-weight:600;color:var(--muted);background:var(--panel-2);border:1px solid var(--line);border-radius:6px;padding:1px 7px;white-space:nowrap}
/* ---------- logos / trust ---------- */
.trust{padding:44px 0 8px;text-align:center}
.trust p{font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint);margin-bottom:20px}
.trust-row{display:flex;flex-wrap:wrap;justify-content:center;gap:14px}
.chip{display:inline-flex;align-items:center;gap:8px;font-size:13.5px;color:var(--muted);font-weight:500;
background:var(--panel);border:1px solid var(--line);border-radius:100px;padding:9px 15px}
.chip svg{width:15px;height:15px;color:var(--acc-2)}
/* ---------- section headers ---------- */
section{padding:86px 0}
.sec-head{max-width:720px;margin:0 auto 46px;text-align:center}
.sec-head h2{font-size:clamp(28px,4vw,42px)}
.sec-head p{color:var(--muted);font-size:17px;margin-top:14px;line-height:1.6}
/* ---------- features grid ---------- */
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
.card{background:linear-gradient(180deg,var(--panel),var(--bg-2));border:1px solid var(--line);border-radius:16px;
padding:26px 24px;transition:.2s ease;position:relative;overflow:hidden}
.card:hover{border-color:rgba(124,110,246,.4);transform:translateY(-3px)}
.card:hover::before{opacity:1}
.card::before{content:"";position:absolute;inset:0;opacity:0;transition:.25s;pointer-events:none;
background:radial-gradient(340px 180px at 20% 0%,rgba(124,110,246,.12),transparent 70%)}
.card .ic{width:44px;height:44px;border-radius:12px;display:grid;place-items:center;color:var(--acc-2);
background:rgba(124,110,246,.10);border:1px solid rgba(124,110,246,.24);margin-bottom:18px}
.card .ic svg{width:22px;height:22px}
.card h3{font-size:18px;letter-spacing:-.01em}
.card p{color:var(--muted);font-size:14.5px;margin-top:9px;line-height:1.6}
.card .tag{position:absolute;top:20px;right:20px;font-size:10px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
color:var(--acc-2);background:rgba(124,110,246,.12);border:1px solid rgba(124,110,246,.28);border-radius:7px;padding:3px 7px}
/* ---------- spotlight (why not p4v) ---------- */
.split{display:grid;grid-template-columns:1fr 1fr;gap:56px;align-items:center}
.split h2{font-size:clamp(26px,3.4vw,38px)}
.split .lead{color:var(--muted);font-size:16.5px;margin-top:16px}
.comp{display:flex;flex-direction:column;gap:10px;margin-top:26px}
.crow{display:flex;align-items:flex-start;gap:13px;padding:14px 16px;border-radius:13px;background:var(--panel);border:1px solid var(--line)}
.crow .ck{width:22px;height:22px;border-radius:7px;display:grid;place-items:center;flex:0 0 auto;margin-top:1px}
.crow.good .ck{background:rgba(62,207,142,.14);color:var(--ok)}
.crow .ck svg{width:13px;height:13px}
.crow b{font-size:14.5px}.crow span{color:var(--muted);font-size:13.5px;display:block;margin-top:2px}
.panel-visual{background:linear-gradient(180deg,var(--panel),var(--bg-2));border:1px solid var(--line);border-radius:18px;
padding:22px;box-shadow:var(--shadow)}
.lock-item{display:flex;align-items:center;gap:12px;padding:12px;border-radius:11px;background:var(--panel-2);border:1px solid var(--line);margin-bottom:9px}
.lock-item:last-child{margin-bottom:0}
.lock-badge{width:30px;height:30px;border-radius:8px;display:grid;place-items:center;flex:0 0 auto}
.lock-badge.red{background:rgba(242,99,126,.12);color:var(--del)}
.lock-badge.gr{background:rgba(62,207,142,.12);color:var(--ok)}
.lock-badge svg{width:15px;height:15px}
.lock-item .li-b{min-width:0}
.lock-item .li-b .n{font-size:13.5px;font-weight:600}
.lock-item .li-b .p{font-size:11.5px;color:var(--faint)}
.lock-item .who{margin-left:auto;font-size:11.5px;color:var(--muted);white-space:nowrap}
/* ---------- tech strip ---------- */
.tech{display:grid;grid-template-columns:repeat(4,1fr);gap:18px;text-align:center}
.tech .t{padding:24px 16px;border-radius:14px;background:var(--panel);border:1px solid var(--line)}
.tech .t .k{font-size:30px;font-weight:800;letter-spacing:-.02em;
background:linear-gradient(120deg,var(--acc-2),#cfc9ff);-webkit-background-clip:text;background-clip:text;color:transparent}
.tech .t .v{color:var(--muted);font-size:13px;margin-top:6px}
/* ---------- final CTA ---------- */
.cta{position:relative;text-align:center;border-radius:26px;padding:64px 28px;overflow:hidden;
background:linear-gradient(180deg,rgba(124,110,246,.14),rgba(91,78,224,.05));border:1px solid rgba(124,110,246,.28)}
.cta::before{content:"";position:absolute;inset:0;z-index:-1;
background:radial-gradient(600px 300px at 50% 0%,rgba(124,110,246,.25),transparent 70%)}
.cta h2{font-size:clamp(28px,4vw,44px)}
.cta p{color:var(--muted);font-size:17px;margin:16px auto 30px;max-width:52ch}
.cta .waitlist{margin-top:0}
/* ---------- footer ---------- */
footer{border-top:1px solid var(--line-2);padding:40px 0;margin-top:20px}
.foot{display:flex;align-items:center;gap:16px;flex-wrap:wrap;color:var(--faint);font-size:13.5px}
.foot .brand{font-size:15px}
.foot .fl{margin-left:auto;display:flex;gap:22px}
.foot a:hover{color:var(--muted)}
@media (max-width:900px){
.nav-links{display:none}
.m-body{grid-template-columns:1fr}
.m-left{border-right:none;border-bottom:1px solid var(--line)}
.m-stage{min-height:260px}
.grid{grid-template-columns:1fr 1fr}
.split{grid-template-columns:1fr;gap:34px}
.tech{grid-template-columns:1fr 1fr}
}
@media (max-width:560px){
.grid{grid-template-columns:1fr}
.wl-form{flex-direction:column}
.wl-form .btn{width:100%}
.hero{padding:48px 0 30px}
.mock{transform:none}
}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
</style>
</head>
<body>
<!-- ===================== NAV ===================== -->
<header class="nav">
<div class="wrap nav-in">
<a class="brand" href="#top">
<span class="mark"><svg viewBox="0 0 24 24" fill="none"><path d="M12 2 3 7v10l9 5 9-5V7l-9-5Z" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/><path d="m3 7 9 5 9-5M12 12v10" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/></svg></span>
Exbyte&nbsp;Depot <small>— Perforce</small>
</a>
<nav class="nav-links">
<a href="#features">Features</a>
<a href="#why">Why not P4V</a>
<a href="#tech">Under the hood</a>
</nav>
<div class="nav-cta">
<a class="btn ghost" href="#waitlist">Join the waitlist</a>
</div>
</div>
</header>
<a id="top"></a>
<!-- ===================== HERO ===================== -->
<section class="hero">
<div class="wrap">
<span class="eyebrow"><span class="dot"></span> A desktop client for Perforce® · by Exbyte Studios</span>
<h1>The <a class="g" href="https://www.perforce.com/products/helix-core" target="_blank" rel="noopener">Perforce</a> client your <span class="g">Unreal team</span> actually wants.</h1>
<p class="sub">Exbyte Depot is a fast, native desktop <b style="color:var(--txt)">client for <a class="tlink" href="https://www.perforce.com/products/helix-core" target="_blank" rel="noopener">Perforce Helix Core</a></b> — real 3D asset previews, one-click exclusive locks, offline work, and live teammate activity. All the power of your Perforce server, none of the P4V pain.</p>
<div class="clarify">
<svg viewBox="0 0 24 24" fill="none"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10Z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><path d="m9 12 2 2 4-4" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>
<span><b>It's a client, not a replacement.</b> Exbyte Depot connects to your existing <a class="tlink" href="https://www.perforce.com/" target="_blank" rel="noopener">Perforce</a> server through Perforce's own <span style="font-family:var(--mono);font-size:13px">p4</span> command-line — your server, history and workflow stay exactly as they are. We don't clone Perforce or reverse-engineer anything; we just give you a nicer window into it.</span>
</div>
<div class="waitlist" id="waitlist">
<form class="wl-form" id="wlForm" novalidate>
<input id="wlEmail" type="email" inputmode="email" autocomplete="email" placeholder="you@studio.com" aria-label="Email address" required />
<button class="btn primary" type="submit">
Join the waitlist
<svg viewBox="0 0 24 24" fill="none"><path d="M5 12h14m-6-6 6 6-6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
</form>
<div class="wl-ok" id="wlOk">
<svg viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="1.7"/><path d="m8.5 12 2.5 2.5 4.5-5" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/></svg>
<span id="wlOkMsg">You're on the list. We'll email you the moment early access opens.</span>
</div>
<div class="wl-note"><b>Windows</b> · early access · no spam, one launch email · <b id="wlCount"></b> on the list</div>
</div>
<!-- ============ APP MOCK ============ -->
<div class="mock-shell">
<div class="mock">
<!-- titlebar -->
<div class="m-title">
<span class="mark"><svg viewBox="0 0 24 24" fill="none"><path d="M12 2 3 7v10l9 5 9-5V7l-9-5Z" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/></svg></span>
<span class="t">Exbyte Depot <span>— Perforce</span></span>
<span class="m-menu"><span>File</span><span>Connection</span><span>Actions</span><span>Window</span><span>Tools</span><span>Help</span></span>
<span class="m-pill"><span class="d"></span> helix.auroragames.dev:1666</span>
<span class="m-actions">
<span class="m-ib"><svg viewBox="0 0 24 24" fill="none"><path d="M16 20v-1a4 4 0 0 0-4-4H7a4 4 0 0 0-4 4v1M9.5 11a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7Zm11 9v-1a4 4 0 0 0-3-3.9M16 4.1a4 4 0 0 1 0 7.8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></span>
<span class="m-ib"><svg viewBox="0 0 24 24" fill="none"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/></svg></span>
</span>
<span class="m-wc"><i class="g"></i><i class="y"></i><i class="r"></i></span>
</div>
<!-- workspace / working folder / get latest -->
<div class="m-head">
<div class="m-zone">
<span class="zi"><svg viewBox="0 0 24 24" fill="none"><rect x="3" y="4" width="18" height="13" rx="2" stroke="currentColor" stroke-width="1.6"/><path d="M8 20h8m-4-3v3" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></span>
<span class="zm"><span class="l">Workspace</span><span class="v">kestrel_WS_02</span></span>
<span class="cv"><svg viewBox="0 0 24 24" width="14" height="14" fill="none"><path d="m6 9 6 6 6-6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
</div>
<div class="m-zone">
<span class="zi"><svg viewBox="0 0 24 24" fill="none"><path d="M6 3v12a3 3 0 0 0 3 3h6M6 3a3 3 0 1 0 0 6M6 3a3 3 0 1 1 0 6m9 9a3 3 0 1 0 6 0 3 3 0 0 0-6 0Z" stroke="currentColor" stroke-width="1.6"/></svg></span>
<span class="zm"><span class="l">Working folder</span><span class="v">//depot/Aurora_Road</span></span>
<span class="cv"><svg viewBox="0 0 24 24" width="14" height="14" fill="none"><path d="m6 9 6 6 6-6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
</div>
<div class="m-zone get">
<span class="zi"><svg viewBox="0 0 24 24" fill="none"><path d="M21 12a9 9 0 1 1-3-6.7M21 4v4h-4" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
<span class="zm"><span class="v">Get Latest</span><span class="l">pull from server</span></span>
</div>
</div>
<!-- body -->
<div class="m-body">
<div class="m-left">
<div class="m-tabs"><span class="on" data-tab="changes">Changes <span class="badge">3</span></span><span data-tab="history">History</span></div>
<!-- CHANGES -->
<div class="m-panel" data-panel="changes" style="display:flex;flex-direction:column;flex:1;min-height:0">
<div class="m-filter"><svg viewBox="0 0 24 24" fill="none"><circle cx="11" cy="11" r="7" stroke="currentColor" stroke-width="1.7"/><path d="m20 20-3.5-3.5" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></svg> Filter 3 files…</div>
<div class="m-selhead">
<span class="sc"><svg viewBox="0 0 24 24" fill="none"><path d="m5 12 4 4 10-10" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
<span id="mSelTxt">3 of 3 selected</span>
<span class="rf" id="mRefresh"><svg viewBox="0 0 24 24" fill="none"><path d="M21 12a9 9 0 1 1-3-6.7M21 4v4h-4" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg> Refresh</span>
</div>
<div class="m-list" id="mList" style="flex:1;min-height:0;overflow:auto">
<div class="m-file sel" data-name="SM_Guardrail.uasset" data-dir="— //depot/Aurora_Road/Content/Meshes/" data-meta="add · #1 · binary" data-kind="mesh" data-type="binary +l" data-prev="3D mesh" data-lab="3D preview">
<span class="m-chk"><svg viewBox="0 0 24 24" fill="none"><path d="m5 12 4 4 10-10" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
<span class="m-ico"><svg viewBox="0 0 24 24" fill="none"><path d="M12 2 3 7v10l9 5 9-5V7l-9-5Z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/></svg></span>
<span class="m-fmeta"><span class="n">SM_Guardrail.uasset</span><span class="p">//depot/…/Content/Meshes/</span></span>
<span class="m-stat a">+</span>
</div>
<div class="m-file" data-name="T_Asphalt_D.uasset" data-dir="— //depot/Aurora_Road/Content/Textures/" data-meta="add · #1 · binary" data-kind="tex" data-type="binary" data-prev="2048 × 2048" data-lab="Image">
<span class="m-chk"><svg viewBox="0 0 24 24" fill="none"><path d="m5 12 4 4 10-10" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
<span class="m-ico"><svg viewBox="0 0 24 24" fill="none"><rect x="3" y="3" width="18" height="18" rx="2" stroke="currentColor" stroke-width="1.6"/><path d="m3 16 5-5 4 4 3-3 6 6" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/></svg></span>
<span class="m-fmeta"><span class="n">T_Asphalt_D.uasset</span><span class="p">//depot/…/Content/Textures/</span></span>
<span class="m-stat a">+</span>
</div>
<div class="m-file" data-name="RoadSampler.cpp" data-dir="— //depot/Aurora_Road/Source/Aurora/" data-meta="edit · #7 → #8 · text" data-kind="code" data-type="text · C++" data-prev="312 lines" data-lab="Source">
<span class="m-chk"><svg viewBox="0 0 24 24" fill="none"><path d="m5 12 4 4 10-10" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
<span class="m-ico"><svg viewBox="0 0 24 24" fill="none"><path d="M8 3H5a2 2 0 0 0-2 2v3m0 8v3a2 2 0 0 0 2 2h3m8-18h3a2 2 0 0 1 2 2v3m0 8v3a2 2 0 0 1-2 2h-3M9 9l6 6m0-6-6 6" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></span>
<span class="m-fmeta"><span class="n">RoadSampler.cpp</span><span class="p">//depot/…/Source/Aurora/</span></span>
<span class="m-stat a">+</span>
</div>
</div>
<div class="m-commit">
<div class="m-summrow">
<div class="m-input">Add guardrail mesh + asphalt texture for road kit</div>
<div class="ai-btn" title="Draft with AI"><svg viewBox="0 0 24 24" fill="none"><path d="M12 3v3m0 12v3M5 12H2m20 0h-3M6 6l2 2m8 8 2 2m0-12-2 2M8 16l-2 2" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/><circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="1.6"/></svg></div>
</div>
<div class="m-submit" id="mCommit"><svg viewBox="0 0 24 24" width="14" height="14" fill="none"><path d="m5 12 4 4 10-10" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg> <span id="mCommitTxt">Commit changelist · 3 files</span></div>
</div>
</div>
<!-- HISTORY -->
<div class="m-panel m-hist" data-panel="history" hidden>
<div class="m-hrow"><span class="m-hav" style="background:#7c6ef6">VD</span><span class="m-hb"><span class="d">road guardrails + collision</span><span class="mt">#34 · voss_dev · 2h ago</span></span><span class="m-hsz">146 KB</span></div>
<div class="m-hrow"><span class="m-hav" style="background:#e8b04b">MA</span><span class="m-hb"><span class="d">asphalt material pass</span><span class="mt">#33 · mira_art · 5h ago</span></span><span class="m-hsz">172 KB</span></div>
<div class="m-hrow"><span class="m-hav" style="background:#3ecf8e">NK</span><span class="m-hb"><span class="d">early car handling setup</span><span class="mt">#31 · novak_dev · yesterday</span></span><span class="m-hsz">21.3 KB</span></div>
<div class="m-hrow"><span class="m-hav" style="background:#f2637e">KS</span><span class="m-hb"><span class="d">new road master material</span><span class="mt">#28 · kestrel · 2 days ago</span></span><span class="m-hsz">6.4 KB</span></div>
<div class="m-hrow"><span class="m-hav" style="background:#5cc9c0">KS</span><span class="m-hb"><span class="d">Project Init</span><span class="mt">#18 · kestrel · last week</span></span><span class="m-hsz">34.1 GB</span></div>
</div>
</div>
<div class="m-right">
<div class="m-diffhead">
<span class="m-stat a" id="mStat" style="width:24px;height:24px;font-size:13px">+</span>
<span class="ttl"><span class="n"><span id="mName">SM_Guardrail.uasset</span> <span id="mDir">— //depot/Aurora_Road/Content/Meshes/</span></span><span class="mm" id="mMeta">add · #1 · binary</span></span>
<span class="m-revert"><svg viewBox="0 0 24 24" fill="none"><path d="M9 14 4 9l5-5M4 9h11a5 5 0 0 1 0 10h-3" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg> Revert</span>
</div>
<div class="m-stage" id="mStage">
<span class="lab" id="mLab"><svg viewBox="0 0 24 24" width="13" height="13" fill="none"><path d="M12 2 3 7v10l9 5 9-5V7l-9-5Z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/></svg> <span id="mLabTxt">3D preview</span></span>
<div class="pv pv-mesh" data-pv="mesh">
<svg class="m-cube" id="mCube" viewBox="0 0 24 24" fill="none"><path d="M12 2 3 7v10l9 5 9-5V7l-9-5Z" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/><path d="m3 7 9 5 9-5M12 12v10" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/></svg>
<span class="m-load" id="mLoad"><svg viewBox="0 0 24 24" width="14" height="14" fill="none"><path d="M12 2 3 7v10l9 5 9-5V7l-9-5Z" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/></svg> <span id="mLoadTxt">Load 3D model</span></span>
</div>
<div class="pv pv-tex" data-pv="tex" hidden>
<div class="tex-swatch"></div>
<span class="tex-cap">2048 × 2048 · BC7 · sRGB</span>
</div>
<div class="pv pv-code" data-pv="code" hidden>
<div class="codeblk"><span class="ln"><span class="cm">// M5 — deterministic POI placement over the road graph</span></span><span class="ln"><span class="kw">void</span> <span class="ty">URoadSampler</span>::<span class="fn">Build</span>(<span class="kw">const</span> <span class="ty">FRoadConfig</span>&amp; Cfg)</span><span class="ln">{</span><span class="ln"> Graph = <span class="fn">NewObject</span>&lt;<span class="ty">URoadGraph</span>&gt;(<span class="kw">this</span>);</span><span class="ln"> Graph-&gt;<span class="fn">Initialize</span>(Cfg);</span><span class="ln"> Graph-&gt;<span class="fn">EnsureGeneratedAround</span>(<span class="ty">INDEX_NONE</span>, <span class="st">0.0</span>);</span><span class="ln"> Pois = <span class="fn">NewObject</span>&lt;<span class="ty">UPoiRegistry</span>&gt;(<span class="kw">this</span>);</span><span class="ln">}</span></div>
</div>
</div>
<div class="m-metabar">
<div class="mi"><div class="k">Type</div><div class="v" id="mvType">binary +l</div></div>
<div class="mi"><div class="k">Preview</div><div class="v" id="mvPrev">3D mesh</div></div>
<div class="mi"><div class="k">Revision</div><div class="v acc">#1</div></div>
<div class="mi"><div class="k">Change</div><div class="v">default</div></div>
</div>
</div>
</div>
<!-- status bar -->
<div class="m-status">
<span class="ok"></span><span class="cmd">p4 opened -c default //depot/Aurora_Road/… {3 files}</span>
<span class="chips">
<b><svg viewBox="0 0 24 24" fill="none"><path d="M12 2 3 7v10l9 5 9-5V7l-9-5Z" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/></svg> Unreal</b>
<b><svg viewBox="0 0 24 24" fill="none"><path d="M4 5h16v14H4z" stroke="currentColor" stroke-width="1.6"/><path d="m7 9 2.5 2.5L7 14m5 0h4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg> Terminal</b>
<b><svg viewBox="0 0 24 24" fill="none"><path d="M4 6h16M4 12h16M4 18h10" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></svg> Log</b>
</span>
</div>
</div>
</div>
</div>
</section>
<!-- ===================== TRUST ===================== -->
<div class="trust wrap">
<p>Purpose-built for game studios on Perforce</p>
<div class="trust-row">
<span class="chip"><svg viewBox="0 0 24 24" fill="none"><path d="M12 2 3 7v10l9 5 9-5V7l-9-5Z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/></svg> Unreal Engine 4 &amp; 5</span>
<a class="chip" href="https://www.perforce.com/products/helix-core" target="_blank" rel="noopener"><svg viewBox="0 0 24 24" fill="none"><rect x="3" y="4" width="18" height="14" rx="2" stroke="currentColor" stroke-width="1.6"/><path d="M8 20h8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg> Powered by Perforce Helix Core</a>
<span class="chip"><svg viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="1.6"/><path d="M3 12h18M12 3c2.5 2.5 2.5 15 0 18M12 3c-2.5 2.5-2.5 15 0 18" stroke="currentColor" stroke-width="1.4"/></svg> Radmin VPN friendly</span>
<span class="chip"><svg viewBox="0 0 24 24" fill="none"><path d="M4 7h16M4 12h16M4 17h10" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></svg> 5 languages</span>
</div>
</div>
<!-- ===================== FEATURES ===================== -->
<section id="features">
<div class="wrap">
<div class="sec-head">
<span class="eyebrow"><span class="dot"></span> Everything, in one native app</span>
<h2 style="margin-top:18px">Built for the way Unreal teams actually work</h2>
<p>Perforce is the industry standard for a reason. Exbyte Depot keeps every bit of its power and finally makes it feel good to use.</p>
</div>
<div class="grid">
<div class="card">
<span class="tag">Signature</span>
<div class="ic"><svg viewBox="0 0 24 24" fill="none"><path d="M12 2 3 7v10l9 5 9-5V7l-9-5Z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><path d="m3 7 9 5 9-5M12 12v10" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/></svg></div>
<h3>Real 3D asset preview</h3>
<p>Rotate the actual mesh inside a <code>.uasset</code> right in the app — plus embedded thumbnails, image, audio and video previews. See what you're committing before you commit it.</p>
</div>
<div class="card">
<div class="ic"><svg viewBox="0 0 24 24" fill="none"><rect x="5" y="11" width="14" height="9" rx="2" stroke="currentColor" stroke-width="1.6"/><path d="M8 11V8a4 4 0 0 1 8 0v3" stroke="currentColor" stroke-width="1.6"/></svg></div>
<h3>Exclusive locks, one click</h3>
<p>Binary <code>.uasset</code> files can't be merged. Take an exclusive lock so only you edit a mesh or material at a time — and see everyone else's locks live. No more overwritten work.</p>
</div>
<div class="card">
<div class="ic"><svg viewBox="0 0 24 24" fill="none"><path d="M4 12a8 8 0 0 1 8-8 8 8 0 0 1 8 8m0 0-3-3m3 3 3-3M20 12a8 8 0 0 1-8 8 8 8 0 0 1-8-8m0 0 3 3m-3-3-3 3" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<h3>Work offline, reconcile later</h3>
<p>On a plane or the VPN's down? Keep editing. Exbyte Depot scans your workspace and reconciles adds, edits and deletes into a changelist when you're back — just like P4V, but nicer.</p>
</div>
<div class="card">
<div class="ic"><svg viewBox="0 0 24 24" fill="none"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><path d="M13.7 21a2 2 0 0 1-3.4 0" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></div>
<h3>Live teammate activity</h3>
<p>Lives in your system tray and sends a native notification the moment a teammate submits — with their message. A banner tells you when you're behind, one click to Get Latest.</p>
</div>
<div class="card">
<span class="tag">AI</span>
<div class="ic"><svg viewBox="0 0 24 24" fill="none"><path d="M12 3v3m0 12v3M5 12H2m20 0h-3M6 6l2 2m8 8 2 2m0-12-2 2M8 16l-2 2" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/><circle cx="12" cy="12" r="3.2" stroke="currentColor" stroke-width="1.6"/></svg></div>
<h3>AI commit summaries</h3>
<p>Draft a clear changelist message from your selected files, and get a plain-English summary of what any source file does — in your interface language.</p>
</div>
<div class="card">
<div class="ic"><svg viewBox="0 0 24 24" fill="none"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7Z" stroke="currentColor" stroke-width="1.6"/></svg></div>
<h3>Files &amp; Sizes explorer</h3>
<p>Browse the Depot and your Workspace as a tree and see exactly how much every file and folder weighs — heaviest first. Find what's bloating your repo in seconds.</p>
</div>
<div class="card">
<div class="ic"><svg viewBox="0 0 24 24" fill="none"><path d="m9 8-4 4 4 4m6-8 4 4-4 4" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<h3>Built-in code editor</h3>
<p>Fix a header or a config right before commit — no external IDE needed. Full syntax highlighting, line numbers, and a proper editor, not a grey textbox.</p>
</div>
<div class="card">
<div class="ic"><svg viewBox="0 0 24 24" fill="none"><path d="M6 3v12a3 3 0 0 0 3 3h6M6 3a3 3 0 1 0 0 6M6 3a3 3 0 1 1 0 6m9 9a3 3 0 1 0 6 0 3 3 0 0 0-6 0Z" stroke="currentColor" stroke-width="1.6"/></svg></div>
<h3>The full Perforce toolbox</h3>
<p>Labels, streams, integrate / merge / copy, jobs, file history with blame &amp; diff, clean, <code>.p4ignore</code>, get-revision, shelve/unshelve — the depth is all there when you need it.</p>
</div>
<div class="card">
<div class="ic"><svg viewBox="0 0 24 24" fill="none"><path d="M21 12a9 9 0 1 1-9-9c2.5 0 4.5 1 6 2.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/><path d="m17 4 1 3-3 1" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<h3>Signed auto-updates</h3>
<p>Ships as a signed installer with a built-in updater. New versions land in place — the whole team stays current without chasing downloads.</p>
</div>
</div>
</div>
</section>
<!-- ===================== WHY NOT P4V ===================== -->
<section id="why" style="background:linear-gradient(180deg,transparent,rgba(124,110,246,.04),transparent)">
<div class="wrap split">
<div>
<span class="eyebrow"><span class="dot"></span> Why switch</span>
<h2 style="margin-top:18px">P4V is powerful. It's also from another era.</h2>
<p class="lead">We're not replacing your <a class="tlink" href="https://www.perforce.com/" target="_blank" rel="noopener">Perforce</a> server — we're replacing the client you look at it through. Exbyte Depot keeps every bit of Perforce power your studio depends on and rebuilds the experience around how artists and engineers really work: visual, fast, and forgiving.</p>
<div class="comp">
<div class="crow good"><span class="ck"><svg viewBox="0 0 24 24" fill="none"><path d="m5 12 4 4 10-10" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg></span><div><b>See your assets, not filenames</b><span>Live 3D meshes, thumbnails, audio &amp; video — right where you review a changelist.</span></div></div>
<div class="crow good"><span class="ck"><svg viewBox="0 0 24 24" fill="none"><path d="m5 12 4 4 10-10" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg></span><div><b>Commit locally, submit when ready</b><span>A GitHub-Desktop-style flow on top of Perforce — revertable local commits, then push.</span></div></div>
<div class="crow good"><span class="ck"><svg viewBox="0 0 24 24" fill="none"><path d="m5 12 4 4 10-10" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg></span><div><b>Never lose work to a binary conflict</b><span>Exclusive locks on <code>.uasset</code> are one click, and everyone's locks are visible.</span></div></div>
<div class="crow good"><span class="ck"><svg viewBox="0 0 24 24" fill="none"><path d="m5 12 4 4 10-10" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg></span><div><b>Know what the team is doing</b><span>Tray notifications on every submit and a clear "you're behind" nudge.</span></div></div>
</div>
</div>
<div class="panel-visual">
<div style="font-size:12px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);margin-bottom:14px;display:flex;align-items:center;gap:8px"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" style="color:var(--acc-2)"><rect x="5" y="11" width="14" height="9" rx="2" stroke="currentColor" stroke-width="1.6"/><path d="M8 11V8a4 4 0 0 1 8 0v3" stroke="currentColor" stroke-width="1.6"/></svg> File Locks</div>
<div class="lock-item"><span class="lock-badge red"><svg viewBox="0 0 24 24" fill="none"><rect x="5" y="11" width="14" height="9" rx="2" stroke="currentColor" stroke-width="1.7"/><path d="M8 11V8a4 4 0 0 1 8 0v3" stroke="currentColor" stroke-width="1.7"/></svg></span><div class="li-b"><div class="n">SM_Hero_Character.uasset</div><div class="p">//depot/Char/Content/Meshes/</div></div><span class="who">🔒 mira_art</span></div>
<div class="lock-item"><span class="lock-badge red"><svg viewBox="0 0 24 24" fill="none"><rect x="5" y="11" width="14" height="9" rx="2" stroke="currentColor" stroke-width="1.7"/><path d="M8 11V8a4 4 0 0 1 8 0v3" stroke="currentColor" stroke-width="1.7"/></svg></span><div class="li-b"><div class="n">M_Road_Master.uasset</div><div class="p">//depot/Road/Content/Materials/</div></div><span class="who">🔒 you</span></div>
<div class="lock-item"><span class="lock-badge gr"><svg viewBox="0 0 24 24" fill="none"><rect x="5" y="11" width="14" height="9" rx="2" stroke="currentColor" stroke-width="1.7"/><path d="M8 11V8a4 4 0 0 1 8 0v3" stroke="currentColor" stroke-width="1.7"/></svg></span><div class="li-b"><div class="n">BP_GameMode.uasset</div><div class="p">//depot/Core/Content/Blueprints/</div></div><span class="who">available</span></div>
<div style="margin-top:16px;padding:13px 15px;border-radius:11px;background:rgba(124,110,246,.08);border:1px solid rgba(124,110,246,.22);font-size:13px;color:var(--acc-2);display:flex;gap:10px;align-items:center">
<svg viewBox="0 0 24 24" width="18" height="18" fill="none"><path d="M12 2 3 7v10l9 5 9-5V7l-9-5Z" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/></svg>
voss_dev just submitted <b style="color:#fff;margin:0 4px">#34</b> — you're 3 files behind.
</div>
</div>
</div>
</section>
<!-- ===================== TECH ===================== -->
<section id="tech">
<div class="wrap">
<div class="sec-head">
<span class="eyebrow"><span class="dot"></span> Under the hood</span>
<h2 style="margin-top:18px">Native, fast, and quietly powerful</h2>
<p>No Electron bloat. A tiny signed installer, instant startup, and a UI that stays out of your way.</p>
</div>
<div class="tech">
<div class="t"><div class="k">Rust</div><div class="v">Native Tauri backend</div></div>
<div class="t"><div class="k">~4 MB</div><div class="v">Installer, not 200</div></div>
<div class="t"><div class="k">5</div><div class="v">Languages built in</div></div>
<div class="t"><div class="k">0</div><div class="v">Servers to configure</div></div>
</div>
</div>
</section>
<!-- ===================== FINAL CTA ===================== -->
<section style="padding-top:20px">
<div class="wrap">
<div class="cta">
<h2>Get early access to Exbyte Depot</h2>
<p>We're onboarding studios in waves. Drop your email and we'll send your download the moment your spot opens.</p>
<div class="waitlist">
<form class="wl-form" id="wlForm2" novalidate>
<input id="wlEmail2" type="email" inputmode="email" autocomplete="email" placeholder="you@studio.com" aria-label="Email address" required />
<button class="btn primary" type="submit">Reserve my spot
<svg viewBox="0 0 24 24" fill="none"><path d="M5 12h14m-6-6 6 6-6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
</form>
<div class="wl-ok" id="wlOk2">
<svg viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="1.7"/><path d="m8.5 12 2.5 2.5 4.5-5" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"/></svg>
<span>You're on the list. Talk soon.</span>
</div>
</div>
</div>
</div>
</section>
<!-- ===================== FOOTER ===================== -->
<footer>
<div class="wrap foot">
<a class="brand" href="#top">
<span class="mark"><svg viewBox="0 0 24 24" fill="none"><path d="M12 2 3 7v10l9 5 9-5V7l-9-5Z" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/></svg></span>
Exbyte&nbsp;Depot
</a>
<span class="fl" style="margin-left:0">
<a href="#features">Features</a>
<a href="#waitlist">Waitlist</a>
<a class="tlink" href="https://www.perforce.com/" target="_blank" rel="noopener">Perforce.com</a>
</span>
<span style="flex-basis:100%;color:var(--faint);font-size:12.5px;line-height:1.6;margin-top:6px">
© <span id="yr"></span> Exbyte Studios. Exbyte Depot is an independent desktop <b style="color:var(--muted);font-weight:600">client for Perforce Helix Core</b> and requires a Perforce server. It is not affiliated with, endorsed by, or sponsored by Perforce Software, Inc. “Perforce”, “Helix Core” and “P4V” are trademarks of <a class="tlink" href="https://www.perforce.com/" target="_blank" rel="noopener">Perforce Software, Inc.</a>, used here only to describe compatibility.
</span>
</div>
</footer>
<script>
(function(){
"use strict";
var KEY = "exd_waitlist";
function list(){ try { return JSON.parse(localStorage.getItem(KEY) || "[]"); } catch(e){ return []; } }
function save(a){ try { localStorage.setItem(KEY, JSON.stringify(a)); } catch(e){} }
// Signups POST to the waitlist server (../waitlist-server). The server dedups
// and stores them; localStorage is kept as a mirror for the social-proof
// counter and as an offline fallback if the page is opened without a backend.
var API = "/api/waitlist";
function submitToWaitlist(email){
var a = list();
if (a.indexOf(email) === -1) a.push(email);
save(a);
return fetch(API, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: email, source: "landing", website: "" })
})
.then(function(r){ return r.ok ? r.json() : null; })
.catch(function(){ return null; }); // never block the UX on a network error
}
var BASE = 1240; // shown alongside real signups for social proof
function renderCount(){
var el = document.getElementById("wlCount");
if (el) el.textContent = (BASE + list().length).toLocaleString() + " studios";
}
renderCount();
document.getElementById("yr").textContent = new Date().getFullYear();
function valid(v){ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v); }
function wire(formId, emailId, okId){
var form = document.getElementById(formId);
if (!form) return;
form.addEventListener("submit", function(e){
e.preventDefault();
var input = document.getElementById(emailId);
var email = (input.value || "").trim().toLowerCase();
if (!valid(email)){
input.focus();
input.style.boxShadow = "0 0 0 2px var(--del)";
setTimeout(function(){ input.style.boxShadow = ""; }, 1400);
return;
}
var already = list().indexOf(email) !== -1;
submitToWaitlist(email).then(function(){
form.style.display = "none";
var ok = document.getElementById(okId);
if (ok){
ok.classList.add("show");
if (okId === "wlOk"){
var msg = document.getElementById("wlOkMsg");
if (msg) msg.textContent = already
? "You're already on the list — we've got you."
: "You're on the list. We'll email you the moment early access opens.";
}
}
renderCount();
});
});
}
wire("wlForm", "wlEmail", "wlOk");
wire("wlForm2", "wlEmail2", "wlOk2");
})();
/* ===== interactive app-window mock ===== */
(function(){
"use strict";
var mock = document.querySelector(".mock");
if (!mock) return;
var $ = function(s){ return mock.querySelector(s); };
var $$ = function(s){ return Array.prototype.slice.call(mock.querySelectorAll(s)); };
/* --- tab switching (Changes / History) --- */
$$(".m-tabs span").forEach(function(tab){
tab.addEventListener("click", function(){
var name = tab.getAttribute("data-tab");
$$(".m-tabs span").forEach(function(t){ t.classList.toggle("on", t === tab); });
$$(".m-panel").forEach(function(p){ p.hidden = p.getAttribute("data-panel") !== name; });
});
});
/* --- file selection swaps the right-hand preview --- */
function selectFile(f){
$$(".m-file").forEach(function(x){ x.classList.toggle("sel", x === f); });
var d = f.dataset;
$("#mName").textContent = d.name;
$("#mDir").textContent = d.dir;
$("#mMeta").textContent = d.meta;
$("#mvType").textContent = d.type;
$("#mvPrev").textContent = d.prev;
$("#mLabTxt").textContent = d.lab;
$$(".pv").forEach(function(pv){ pv.hidden = pv.getAttribute("data-pv") !== d.kind; });
}
$$(".m-file").forEach(function(f){
f.addEventListener("click", function(e){
if (e.target.closest(".m-chk")) return; // checkbox handled separately
selectFile(f);
});
});
/* --- checkboxes toggle the selected count + commit button --- */
function refreshCount(){
var on = $$(".m-file:not(.off)").length;
$("#mSelTxt").textContent = on + " of 3 selected";
$("#mCommitTxt").textContent = on ? ("Commit changelist · " + on + (on === 1 ? " file" : " files")) : "Select files to commit";
$("#mCommit").style.opacity = on ? "1" : ".55";
}
$$(".m-file .m-chk").forEach(function(chk){
chk.addEventListener("click", function(e){
e.stopPropagation();
chk.closest(".m-file").classList.toggle("off");
refreshCount();
});
});
/* --- little press / spin feedback --- */
function pressSpin(el, ms){
if (!el || el.classList.contains("spin")) return;
el.classList.add("spin");
setTimeout(function(){ el.classList.remove("spin"); }, ms || 900);
}
var rf = $("#mRefresh"); if (rf) rf.addEventListener("click", function(){ pressSpin(rf, 800); });
var get = $(".m-zone.get"); if (get) get.addEventListener("click", function(){ pressSpin(get, 1100); });
/* --- Load 3D model → spinner, then a slowly spinning mesh --- */
var load = $("#mLoad"), cube = $("#mCube"), loaded = false;
if (load){
load.addEventListener("click", function(){
if (loaded){ return; }
var txt = $("#mLoadTxt");
load.innerHTML = '<span class="m-spinner"></span> Rendering…';
setTimeout(function(){
loaded = true;
cube.classList.add("loaded");
load.innerHTML = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none"><path d="m5 12 4 4 10-10" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg> Loaded';
load.style.opacity = ".7";
var pv = $("#mvPrev"); if (pv && pv.textContent.indexOf("3D") === 0) pv.textContent = "3D mesh · loaded";
}, 950);
});
}
/* --- Commit button press --- */
var commit = $("#mCommit");
if (commit){
commit.addEventListener("click", function(){
if ($$(".m-file:not(.off)").length === 0) return;
var t = $("#mCommitTxt"), old = t.textContent;
commit.style.transform = "scale(.98)";
t.textContent = "Committing…";
setTimeout(function(){
commit.style.transform = "";
t.textContent = "✓ Changelist created locally";
setTimeout(function(){ t.textContent = old; }, 1500);
}, 700);
});
}
})();
</script>
</body>
</html>