landing page: scrollable home with 'How amerc works' (0.30.0)
- tavern hero now scrolls into a product section: 3 feature cards (browse/publish/deliver) with CTAs, a 'three ways to use an agent' row (chatbox/web-terminal/reverse-proxy), + footer - scroll cue under the hero; explains the product to first-time visitors
This commit is contained in:
parent
7b3ee02de6
commit
4bcad5f9c7
@ -7,7 +7,7 @@
|
||||
name="description"
|
||||
content="amerc is an agent mercenary layer for embedding, bringing, and hosting agents across vertical software boundaries."
|
||||
/>
|
||||
<meta name="version" content="0.29.0-footer" />
|
||||
<meta name="version" content="0.30.0-landing" />
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "amerc-site",
|
||||
"version": "0.29.0-footer",
|
||||
"version": "0.30.0-landing",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@ -6,7 +6,7 @@ import Mansion from './Mansion.jsx';
|
||||
import TavernGame from './TavernGame.jsx';
|
||||
import { api } from './api.js';
|
||||
|
||||
const RELEASE = '0.29.0-footer';
|
||||
const RELEASE = '0.30.0-landing';
|
||||
|
||||
const NAV = [
|
||||
{ id: 'home', label: 'Tavern' },
|
||||
@ -71,24 +71,66 @@ function Home({ setRoute }) {
|
||||
useEffect(() => { api('/agents/stats').then(setStats).catch(() => {}); }, []);
|
||||
return (
|
||||
<div className="gm-home">
|
||||
<div className="gm-stage">
|
||||
<TavernGame onHotspot={setRoute} />
|
||||
</div>
|
||||
<div className="gm-caption">
|
||||
<h1>Hire agents like mercenaries.</h1>
|
||||
<p>Walk into the tavern — talk to the <b>screen</b> to browse staff, or step to the floor to open <b>your booth</b>.</p>
|
||||
{stats && (
|
||||
<div className="hero-live">
|
||||
<span><b>{stats.classes}</b> classes</span>
|
||||
<span><b>{stats.online}</b> online</span>
|
||||
<span><b>{stats.sessions}</b> sessions</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="gm-hero">
|
||||
<div className="gm-stage"><TavernGame onHotspot={setRoute} /></div>
|
||||
<div className="gm-caption">
|
||||
<h1>Hire agents like mercenaries.</h1>
|
||||
<p>Walk into the tavern — talk to the <b>screen</b> to browse staff, or step to the floor to open <b>your booth</b>.</p>
|
||||
{stats && (
|
||||
<div className="hero-live">
|
||||
<span><b>{stats.classes}</b> classes</span>
|
||||
<span><b>{stats.online}</b> online</span>
|
||||
<span><b>{stats.sessions}</b> sessions</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="gm-scrollcue" onClick={() => document.querySelector('.hf')?.scrollIntoView({ behavior: 'smooth' })}>how it works ↓</div>
|
||||
</div>
|
||||
</div>
|
||||
<HomeFeatures setRoute={setRoute} />
|
||||
<Footer setRoute={setRoute} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const HF_STEPS = [
|
||||
{ icon: '🍺', title: 'Browse & hire', body: 'Walk the tavern floor and pick a mercenary by skill and tag. See runtime, online status, and what each agent can do.', cta: ['Browse Agents', 'agents'] },
|
||||
{ icon: '🗡️', title: 'Publish & run', body: 'Register your own agent class in My Booth and run instances — your broker brings the runtime. No infrastructure to manage.', cta: ['Open My Booth', 'booth'] },
|
||||
{ icon: '🔌', title: 'Deliver anywhere', body: 'Serve users as an embedded chatbox, drive a live web terminal, or expose a local port to the internet via Showcase.', cta: ['The Mansion', 'mansion'] },
|
||||
];
|
||||
const HF_USES = [
|
||||
{ icon: '💬', title: 'Chatbox', body: 'Drop one <script> tag on any site; the agent appears as a chat widget with your page skills injected.' },
|
||||
{ icon: '🖥️', title: 'Web terminal', body: 'Open a relayed PTY to the agent’s terminal — watch and drive it right from the browser.' },
|
||||
{ icon: '🌐', title: 'Reverse proxy', body: 'Map a local port to your own subdomain — your service goes live on the internet through your broker.' },
|
||||
];
|
||||
function HomeFeatures({ setRoute }) {
|
||||
return (
|
||||
<section className="hf">
|
||||
<h2 className="hf-title">How <span>amerc</span> works</h2>
|
||||
<p className="hf-lead">An agent mercenary layer for embedding, bringing, and hosting AI agents across vertical software boundaries.</p>
|
||||
<div className="hf-grid">
|
||||
{HF_STEPS.map((s, i) => (
|
||||
<div key={i} className="hf-card">
|
||||
<span className="hf-ico">{s.icon}</span>
|
||||
<h3>{s.title}</h3>
|
||||
<p>{s.body}</p>
|
||||
<button className="hf-link" onClick={() => setRoute(s.cta[1])}>{s.cta[0]} →</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<h3 className="hf-sub">Three ways to use an agent</h3>
|
||||
<div className="hf-uses">
|
||||
{HF_USES.map((u, i) => (
|
||||
<div key={i} className="hf-use"><span className="hf-ico-sm">{u.icon}</span><div><strong>{u.title}</strong><p>{u.body}</p></div></div>
|
||||
))}
|
||||
</div>
|
||||
<div className="hf-cta">
|
||||
<button className="px-action" onClick={() => setRoute('agents')}>Browse Agents</button>
|
||||
<button className="px-action" onClick={() => setRoute('booth')}>Publish an Agent</button>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function Footer({ setRoute }) {
|
||||
const [ok, setOk] = useState(null);
|
||||
const [stats, setStats] = useState(null);
|
||||
|
||||
@ -1110,3 +1110,29 @@ button {
|
||||
.ft-col a:hover, .ft-col button:hover { color: #27d8ff; }
|
||||
.ft-base { max-width: 1080px; margin: 24px auto 0; padding-top: 14px; border-top: 1px solid #140d22; font-size: 11px; color: #5f4f7a; text-align: center; }
|
||||
@media (max-width: 680px) { .ft-cols { grid-template-columns: 1fr 1fr; } .ft-brand { grid-column: 1 / -1; } }
|
||||
|
||||
/* 0.30 — landing: scrollable home with "how it works" */
|
||||
.gm-home { display: block; overflow-y: auto; overflow-x: hidden; padding: 0; scroll-behavior: smooth; }
|
||||
.gm-hero { min-height: calc(100vh - 58px); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; padding: 14px 12px; }
|
||||
.gm-scrollcue { margin-top: 8px; font-family: Georgia, serif; font-size: 12px; color: #9a86b8; cursor: pointer; opacity: 0.8; animation: hf-bob 2.2s ease-in-out infinite; }
|
||||
.gm-scrollcue:hover { color: #27d8ff; }
|
||||
@keyframes hf-bob { 0%,100% { transform: translateY(0); } 50% { transform: translateY(4px); } }
|
||||
.hf { max-width: 1040px; margin: 0 auto; padding: 50px 22px 30px; font-family: Inter, sans-serif; }
|
||||
.hf-title { text-align: center; font-family: Georgia, serif; font-size: clamp(22px, 3vw, 32px); color: #fff; margin: 0 0 8px; }
|
||||
.hf-title span { color: #f3d27a; }
|
||||
.hf-lead { text-align: center; color: #b9a6cf; font-size: 14px; max-width: 620px; margin: 0 auto 34px; line-height: 1.7; }
|
||||
.hf-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
|
||||
.hf-card { padding: 22px; border-radius: 16px; background: linear-gradient(165deg, #15102a, #0e0a18); border: 1px solid #271c3a; transition: transform .14s, border-color .14s; }
|
||||
.hf-card:hover { transform: translateY(-4px); border-color: #5a3fa0; }
|
||||
.hf-ico { font-size: 30px; display: inline-block; margin-bottom: 10px; filter: drop-shadow(0 4px 10px rgba(120,90,200,0.4)); }
|
||||
.hf-card h3 { margin: 0 0 8px; color: #fff; font-size: 17px; }
|
||||
.hf-card p { margin: 0 0 14px; color: #b9a6cf; font-size: 13px; line-height: 1.6; }
|
||||
.hf-link { background: none; color: #27d8ff; cursor: pointer; font: inherit; font-size: 13px; padding: 0; font-weight: 600; }
|
||||
.hf-link:hover { color: #7fe9ff; }
|
||||
.hf-sub { text-align: center; font-family: Georgia, serif; font-size: 19px; color: #f3d27a; margin: 44px 0 18px; }
|
||||
.hf-uses { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
|
||||
.hf-use { display: flex; gap: 12px; padding: 16px; border-radius: 12px; background: #0e0a18; border: 1px solid #221934; }
|
||||
.hf-ico-sm { font-size: 22px; flex: none; }
|
||||
.hf-use strong { color: #fff; font-size: 14px; } .hf-use p { margin: 4px 0 0; color: #9a86b8; font-size: 12px; line-height: 1.5; }
|
||||
.hf-cta { display: flex; gap: 12px; justify-content: center; margin: 38px 0 0; }
|
||||
@media (max-width: 760px) { .hf-grid, .hf-uses { grid-template-columns: 1fr; } }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user