gatehouse: split join page with value prop (0.42.0)
- replace the lonely floating login form with a two-column 'Join the tavern' page - left: pitch — headline, three benefit rows (hire/publish/deliver) with icon tiles, cross-app SSO note, live stats - right: the auth board (login/signup) in a clean static card - responsive: stacks form-first on mobile (<=860px) - verified live on desktop + mobile
This commit is contained in:
parent
03b5f631b5
commit
76ba8dcbb4
@ -4,7 +4,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="theme-color" content="#0e0a14" />
|
||||
<meta name="description" content="amerc is the agent mercenary tavern — hire, run, publish, and remotely deliver AI agents across software boundaries. No infrastructure, just skills." />
|
||||
<meta name="version" content="0.41.0-mansion" />
|
||||
<meta name="version" content="0.42.0-gatehouse" />
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="icon" href="/app-192.png" type="image/png" sizes="192x192" />
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
|
||||
@ -7,7 +7,7 @@ import Account from './Account.jsx';
|
||||
import TavernGame from './TavernGame.jsx';
|
||||
import { api } from './api.js';
|
||||
|
||||
const RELEASE = '0.41.0-mansion';
|
||||
const RELEASE = '0.42.0-gatehouse';
|
||||
|
||||
const NAV = [
|
||||
{ id: 'home', label: 'Tavern' },
|
||||
@ -189,6 +189,44 @@ function Footer({ setRoute }) {
|
||||
);
|
||||
}
|
||||
|
||||
const GATE_BENEFITS = [
|
||||
{ icon: '🍺', title: 'Hire agents like mercenaries', body: 'Browse the roster and put a skilled agent to work in seconds.' },
|
||||
{ icon: '🗡️', title: 'Publish & run your own', body: 'Register an agent class — your broker brings the runtime, no servers to rent.' },
|
||||
{ icon: '🔌', title: 'Deliver anywhere', body: 'Embed a chatbox, drive a live web terminal, or expose a local port to the internet.' },
|
||||
];
|
||||
function Gatehouse({ auth, setRoute }) {
|
||||
const [stats, setStats] = useState(null);
|
||||
useEffect(() => { api('/agents/stats').then(setStats).catch(() => {}); }, []);
|
||||
return (
|
||||
<div className="gate">
|
||||
<div className="gate-pitch">
|
||||
<span className="gate-tag">⛩ THE GATEHOUSE</span>
|
||||
<h2>Join the tavern.</h2>
|
||||
<p className="gate-lead">One amerc account opens every door — hire mercenaries, run your own, and deliver them across software boundaries. No infrastructure, just skills.</p>
|
||||
<ul className="gate-benefits">
|
||||
{GATE_BENEFITS.map((b) => (
|
||||
<li key={b.title}><span className="gate-bi" aria-hidden="true">{b.icon}</span><div><strong>{b.title}</strong><p>{b.body}</p></div></li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="gate-sso"><span>🔓</span> One sign-in works across <b>tavern</b>, <b>docs</b>, <b>PM</b> and <b>git</b>.</div>
|
||||
{stats && (
|
||||
<div className="gate-live">
|
||||
<span><b>{stats.classes}</b> classes</span><i />
|
||||
<span><b>{stats.online}</b> online</span><i />
|
||||
<span><b>{stats.sessions}</b> sessions</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="gate-form">
|
||||
<div className="px-board gate-board">
|
||||
<header className="px-board-head"><span className="px-board-tag">OPEN BOOTH</span><h2>Welcome, mercenary</h2><p>Log in, or create your account in seconds.</p></header>
|
||||
<AuthPanel auth={auth} onDone={() => setRoute('booth')} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Page({ children, setRoute }) { return <div className="td-page"><div className="td-page-inner">{children}</div><Footer setRoute={setRoute} /></div>; }
|
||||
|
||||
function Scene({ route, setRoute, auth }) {
|
||||
@ -197,10 +235,7 @@ function Scene({ route, setRoute, auth }) {
|
||||
if (route === 'mansion') return <Page setRoute={setRoute}><Mansion auth={auth} setRoute={setRoute} /></Page>;
|
||||
if (route === 'booth') return <Page setRoute={setRoute}><BoothDashboard auth={auth} setRoute={setRoute} /></Page>;
|
||||
if (route === 'account') return <Page setRoute={setRoute}><Account auth={auth} setRoute={setRoute} /></Page>;
|
||||
if (route === 'signin') return <Page setRoute={setRoute}><div className="td-signin"><div className="px-board" style={{ position: 'static', transform: 'none', width: 'min(380px,92vw)', margin: '0 auto' }}>
|
||||
<header className="px-board-head"><span className="px-board-tag">GATEHOUSE</span><h2>Open Booth</h2><p>One amerc account across tavern, docs, PM and git.</p></header>
|
||||
<AuthPanel auth={auth} onDone={() => setRoute('booth')} />
|
||||
</div></div></Page>;
|
||||
if (route === 'signin') return <Page setRoute={setRoute}><Gatehouse auth={auth} setRoute={setRoute} /></Page>;
|
||||
if (route === 'admin') return <Page setRoute={setRoute}><div className="px-board" style={{ position: 'static', transform: 'none', width: 'min(720px,94vw)', margin: '0 auto' }}>
|
||||
<header className="px-board-head"><span className="px-board-tag">BACKDOOR</span><h2>Quartermaster</h2><p>Tenant, key, session and risk controls.</p></header>
|
||||
<AdminConsole auth={auth} />
|
||||
|
||||
@ -1218,3 +1218,27 @@ button {
|
||||
.gm-say { position: absolute; bottom: 102%; left: 50%; transform: translateX(-50%); width: max-content; max-width: 200px; padding: 8px 11px; font-family: Georgia, serif; font-size: 12px; line-height: 1.5; color: #1c1206; background: #fff0d7; border: 2px solid #3a2410; border-radius: 8px; box-shadow: 0 6px 16px rgba(0,0,0,0.5); z-index: 8; text-align: left; }
|
||||
.gm-say strong { display: block; color: #7a3d0c; font-size: 11px; margin-bottom: 3px; }
|
||||
.gm-say::after { content: ''; position: absolute; top: 100%; left: 50%; transform: translateX(-50%); border: 7px solid transparent; border-top-color: #3a2410; }
|
||||
|
||||
/* gatehouse — split join page (0.42 vividness) */
|
||||
.gate { display: grid; grid-template-columns: 1.05fr 0.95fr; gap: 32px; align-items: center; max-width: 1000px; margin: 0 auto; padding: 20px 0 40px; }
|
||||
.gate-tag { display: inline-block; font-family: Inter, sans-serif; font-size: 11px; letter-spacing: 2px; color: var(--gold); margin-bottom: 10px; }
|
||||
.gate-pitch h2 { font-size: 34px; line-height: 1.1; margin: 0 0 12px; color: #fff; text-shadow: 2px 2px 0 rgba(0,0,0,0.4); }
|
||||
.gate-lead { font-size: 14px; line-height: 1.7; color: #b6a7d4; margin: 0 0 22px; max-width: 52ch; }
|
||||
.gate-benefits { list-style: none; margin: 0 0 20px; padding: 0; display: flex; flex-direction: column; gap: 14px; }
|
||||
.gate-benefits li { display: flex; gap: 13px; align-items: flex-start; }
|
||||
.gate-bi { font-size: 22px; line-height: 1.2; flex: 0 0 auto; width: 40px; height: 40px; display: grid; place-items: center; border-radius: 11px;
|
||||
background: radial-gradient(120% 120% at 30% 20%, #2a1a3e, #160d22); border: 1px solid #34254e; box-shadow: inset 0 1px 0 rgba(255,255,255,0.05); }
|
||||
.gate-benefits strong { display: block; color: #f0e9ff; font-size: 14.5px; margin-bottom: 2px; }
|
||||
.gate-benefits p { margin: 0; font-size: 12.5px; line-height: 1.55; color: #9d8fc0; }
|
||||
.gate-sso { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: #cfe6ff; background: rgba(39,216,255,0.07); border: 1px solid rgba(39,216,255,0.18);
|
||||
border-radius: 10px; padding: 9px 13px; margin-bottom: 16px; }
|
||||
.gate-sso b { color: #7fe9ff; font-weight: 600; }
|
||||
.gate-live { display: flex; align-items: center; gap: 12px; font-size: 13px; color: #b6a7d4; }
|
||||
.gate-live b { color: #fff; font-size: 16px; } .gate-live i { width: 4px; height: 4px; border-radius: 50%; background: #4a3a68; }
|
||||
.gate-form { display: flex; justify-content: center; }
|
||||
.gate-board { position: static !important; transform: none !important; left: auto; top: auto; width: 100%; max-width: 400px; margin: 0; animation: none; }
|
||||
@media (max-width: 860px) {
|
||||
.gate { grid-template-columns: 1fr; gap: 24px; max-width: 460px; }
|
||||
.gate-form { order: -1; }
|
||||
.gate-pitch h2 { font-size: 28px; }
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user