- AgentCard extracted; Browse and My Legion render the identical card;
Legion cards open the same ClassModal (instances + Use + chat)
- class properties: managed (amerc-managed, loads amerc skills),
terminal_mode new|shared per session, skills_reload for shared
terminals — publish form, API create/patch, badges in the modal
- kebab coordination: broker spec delivered via TerminalMan 155; kebab
already heartbeats instance 2 with the skills array (status lost ->
stabilizing); monitor armed for online
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- src/Chat.jsx: long-poll chat panel — skills-loaded chips, file/image
send with streaming progress bar, expiring download links for received
files, per-session chatlog with resume; no iframe, no external relay
- UsePanel: chatbox tab now uses the native panel (web-pty still rides
the relay for now); Legion gains a 'My chatlog' card
- verified END-TO-END IN PRODUCTION via throwaway account + live broker:
browser sent, broker echoed, skills rendered, chatlog listed; 2MB file
relayed through nginx md5-identical with no server caching; all test
data purged after
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- chat_messages table: persistent per-session chatlog (file kinds store
placeholder only); session open/close events logged
- GET/POST /agents/sessions/:id/messages — browser (cookie) and broker
(accesskey) both speak plain HTTP; ?wait=1 long-polls up to 25s
- GET /agents/sessions — my chatlog list with last message + counts
- POST /agents/sessions/:id/files + GET /agents/files/:token — streamed
rendezvous relay, both directions; bytes never touch disk or DB;
single-use token, 5-min expiry, Content-Length passthrough for progress
- heartbeat now accepts skills[] (shown in chat) and returns activeSessions
- nginx /api: request buffering off, 512m bodies, 360s timeouts (deployed)
Tested locally end-to-end: chat both ways, 3MB relay both directions
(md5-identical, zero server caching), long-poll wake, auth rejection.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
audited the Booth as a new publisher: the onboarding is good, but the publish
form was placeholder-only — and Kind (codex) + Launch command (codex --yolo)
come pre-filled, so their placeholders never show, leaving those two technical
fields with no visible label.
- added a persistent label above every field (Class name, Tags, Description,
Kind, Visibility, Launch command) + clearer example placeholders
- a one-line hint explains the two technical fields
- verified live (throwaway account, cleaned up): all six labels render, form
reads cleanly
- changelog entry (0.99) added with the bump
the agent-native callout's API peek showed a curl line + the JSON, but felt
static. made it feel like a real API client:
- after each call it shows the response status and timing (green dot, '200 OK
· 47 ms') measured with performance.now() — the visitor's actual request time
- a '↻ run' button re-fetches the current endpoint (running… -> new status)
- endpoint tabs still switch (stats / roster / activity), all read-only & no-auth
- verified live: status '200 OK · 683 ms', run re-fetches (running… -> 200 OK),
green dot on success
- changelog entry (0.98) added with the bump
the join form used confusing labels at the exact moment a visitor commits:
- the email field was labelled 'Guild handle email' (it is just an email)
- the password field was 'Access sigil'
- the login button was 'Enter Booth' (logging in does not go to the Booth)
- the card eyebrow was 'OPEN BOOTH' (publisher-biased; you might sign in to browse)
now: Email / Password, a clear 'Log in' button, and a neutral 'ONE ACCOUNT'
eyebrow that reinforces the single sign-on. theme stays in the heading
('Join the tavern', 'Welcome, mercenary') and the benefit points.
verified live: login shows Email/Password -> Log in; signup shows
Email/Display name/Password -> Create Account.
on common laptops (1366x768, 1280x720, 1440x900) the tavern canvas reserved
only 210px for the topbar + caption, so the tagline + CTAs landed at the very
bottom edge and the live stats sat ~40px BELOW the fold — a visitor had to
scroll to see the value prop and the action buttons.
- reserve 290px instead of 210, so the canvas is sized to leave room for the
full caption; the tavern is still the large, vivid centrepiece (478px tall
at 768, down from 558)
- desktop-only: the canvas is width-capped (94vw) on phones, so mobile is
untouched
- verified across all three laptop sizes: CTA ~80px clearance, stats ~40px
clearance (was 40px below the fold)
- added the matching changelog entry (0.96) alongside the bump
the in-app CHANGELOG array stopped at v0.89 while the badge (which links to it)
read v0.94 — five shipped versions missing: linkable agents, mobile docs nav,
the live API peek, full-text docs search, and the key 'try it' curl.
- added entries 0.90-0.95 so the changelog's newest version matches RELEASE
- verified live: badge v0.95, changelog tops out at v0.95 (37 entries)
(also saved a project note to add a CHANGELOG entry alongside each version bump —
this has now drifted 5 versions behind twice.)
minting an agent key showed the key once with no hint of what to do with it —
you had to go dig through the docs to make your first call. great API products
hand you a working example right there.
- after minting, show a ready-to-run curl with YOUR key inlined, hitting a real
read-only endpoint (GET /agents/classes), plus a one-click copy and a link to
the API reference
- completes the get-key -> call-the-API loop and reinforces the agent-native pitch
- verified live (throwaway account, cleaned up after): the curl renders with the
real minted key, copy button + api-reference link present
the docs search only matched titles/folders, so searching a term that lives in
a doc's body (e.g. 'heartbeat') returned nothing — you had to already know
which doc to open.
- search now matches title AND body across all docs; results show the title
plus a context snippet with the query highlighted (<mark>)
- doc bodies are fetched once on first search and cached (invalidated when the
doc list changes); in-flight searches cancel on a new query
- clicking a result opens the doc (and updates the URL); clearing returns the
folder tree
- a11y: the homepage API peek used role=tablist/tab without tabpanels (an
incomplete ARIA pattern) — switched to a labeled button group with aria-pressed
- verified live: 'heartbeat' -> 3 body matches with highlighted snippets,
click opens Connect-your-broker, clear restores the tree
the callout claimed 'drive the whole platform over JSON' but only said it.
now it shows it: a compact terminal with tabs for the public read-only
endpoints (stats / roster / activity) that fetches the LIVE response and
pretty-prints it with JSON syntax highlighting.
- proves the agent-native differentiator with real data, not a mockup
- read-only public endpoints, no auth; switching tabs updates the curl line
and refetches; cancels in-flight on unmount
- small self-contained jsonHl() highlighter (keys/strings/numbers/bools),
output escaped before highlighting so it's injection-safe
- verified live: stats shows {classes:1,online:1,...}, roster shows
Kebab Webagent, 9 key + 7 number spans highlighted, tabs refetch
a mobile audit (no overflow anywhere; agent modal + on-this-page TOC both fine)
turned up one weak spot: on phones the doc tree was a cramped 210px scroll area
pinned above every page, and it never scrolled the active doc into view — so
you couldn't tell which doc you were reading or reach half of them easily.
- the sidebar now collapses to a compact bar showing the CURRENT doc + a
chevron; tap to reveal the full tree (active doc highlighted), tap a doc to
navigate and auto-collapse
- desktop is byte-for-byte unchanged: the collapsible wrapper is display:contents
off-mobile, so the tree stays a normal flex child (sidebar still 280px)
- verified live: mobile collapsed shows 'API reference', expands to 8 docs,
selecting Quickstart navigates + collapses + relabels; desktop toggle hidden,
tree intact
opening an agent only lived in component state, so you couldn't link to one,
and a refresh dropped the open agent — inconsistent with the now-shareable
docs. each agent modal now has a URL: amerc.ai/#/agents/<slug>.
- the hash router keys off the first path segment, so #/agents/<slug> still
routes to Browse while exposing the slug as a sub-path (backward compatible)
- opening a card pushes #/agents/<slug>; loading that URL opens the modal;
closing returns to #/agents; browser back/forward open & close it
- verified live: click updates URL + opens, Escape closes to #/agents, a fresh
deep-link opens 'Kebab Webagent', back closes
also recorded a project memory: sandbox->amerc network path is slow (~0.65s
TLS) but the server is fast (~13ms loopback) — don't trust sandbox-measured
latency or Lighthouse-from-sandbox as the real user experience.
the changelog page stopped at v0.51 while the site shipped to v0.88 — so the
version badge (which links to the changelog) advertised ~37 versions that the
'trail' never showed, directly undercutting its 'ships continuously' line.
reconstructed the real history from git and added 17 curated milestones
(0.54 -> 0.89): the agent-native/WebMCP work, the accessibility & caching
sweeps, instant paint, the OG share card, docs routing + section nav, the
error boundary, mobile nav, lean build, and more.
- verified live: 31 entries, newest v0.89, matches the v0.89 badge
builds on per-doc routing: long docs (API reference has 6 sections, Roadmap 4)
are now navigable and section-shareable.
- each h2/h3 gets a slug id + a hover '#' anchor; URL form is
#/<doc-slug>/<section-slug> (e.g. #/api-reference/keys-self-service)
- an 'On this page' TOC renders for docs with >=3 headings; click or deep-link
scrolls to the section; browser back/forward still work
- decorations (anchors, ids, code copy buttons, syntax highlighting) are now
baked into the memoized HTML string instead of mutated in post-render DOM
WHY: the post-render mutation approach was being wiped. setToc() inside the
decorate effect triggered a re-render that re-applied dangerouslySetInnerHTML,
resetting the article's children and dropping every decoration, with the
effect never re-running (deps unchanged). Doing it at the string level makes
the markup self-contained and immune to re-injection. Copy buttons use event
delegation; anchors are real links (work without JS).
- verified live: 6/6 heading ids + anchors on API reference, TOC navigates,
3 copy buttons + highlighting on Connect-your-broker, deep-links land and
scroll to the section, TOC hidden on short docs
the docs site kept the selected document only in React state, so you couldn't
bookmark, share, or refresh-into a specific doc, and browser back/forward did
nothing — a real gap for a developer docs site people want to link into.
- each doc now has a slug URL: docs.amerc.ai/#/connect-your-broker
- selecting a doc pushes the slug to the URL (history.pushState)
- loading a #/<slug> URL lands directly on that doc; unknown/empty falls back
to Quickstart as before
- hashchange + popstate listeners make browser back/forward switch docs
- the browser tab title now reflects the open doc (<title> · Docs · amerc)
- verified live on docs.amerc.ai: click updates URL+view+title, deep-links
land correctly, back/forward navigate between docs
sharing amerc.ai in Slack/Discord/X/etc. showed a raw pixel-art tavern
screenshot — distinctive but cluttered at thumbnail size and missing the
wordmark and tagline. replaced it with a designed 1200x630 OG card:
gem + 'amerc' wordmark, the 'Hire agents like mercenaries.' tagline, a
one-line description, amerc.ai, and the three delivery modes — on the
brand's dark/cyan/gold palette over a deeply dimmed tavern backdrop.
- rendered headless at 1200x630 (Page.captureScreenshot, jpeg q88, 58KB)
- point og:image + twitter:image at /og-card.jpg, fix width/height to 1200x630
- verified live: meta tags resolve, image serves 200 image/jpeg
the <div id=root> was empty until the JS bundle downloaded and React mounted,
so the page was blank for ~2.4s (FCP). put the branded loading screen (pulsing
gem + 'amerc') directly in the static HTML, with its critical CSS inlined in
<head>, so it paints as soon as the CSS loads instead of waiting for JS.
- React's createRoot replaces #root on mount; the Suspense fallback renders the
identical markup, so the static->React handoff is seamless (no flicker)
- inlined .app-loading critical CSS mirrors styles.css exactly + dark body bg
so there's no white flash during load
- verified live: FCP 2.4s -> 1.4s, performance 86 -> 90, TBT 50 -> 0ms;
app mounts cleanly, static loader replaced, 0 console errors
measured the LIVE site with Lighthouse (best-practices 100, seo 100,
agentic-browsing 100; a11y 98, perf 86) and fixed two real flags:
- heading-order: the footer column labels were <h5> following an <h3>
(skipping h4) — changed to <h4> so the page heading order is sequential;
a11y 98 -> 100
- caching: every static asset was served with no cache lifetime (0s).
added nginx cache locations on the amerc.ai vhost (server-side, not in repo):
/assets/ -> 1y immutable (content-hashed chunks), /scene2d/ -> 30d;
index.html stays uncached so it always points at the latest assets.
repeat visitors now serve JS/CSS/images from cache. cache-insight -> PASS
- verified live: a11y 100, heading-order + cache-insight PASS, cache headers
present on assets and absent on index.html
- amerc is genuinely agent-native now (JSON API + self-service keys + WebMCP
browser tools + llms.txt) but the home page never said so
- add a concise callout after the session preview: 'Agent-native by design —
mint an API key and drive it over JSON, get callable WebMCP tools on the
page, read llms.txt for the map. Agents can hire agents.'
- a real, differentiating positioning point, not filler; cyan-accented card,
scroll-reveal, links to /llms.txt, stacks on mobile
- verified live: callout renders and reveals
- llms.txt: add a 'Browser tools (WebMCP)' section listing the four callable
tools (amerc_browse_agents / platform_stats / get_agent / open) so agents
reading the file know they can act on the tavern, not just read about it
- footer: add a 'For agents ↗' link to /llms.txt so humans can discover
amerc's agent-facing guide (keys, JSON API, WebMCP)
- verified live: llms.txt WebMCP section served, footer link in the bundle
amerc is the agent mercenary tavern — so expose it to AI agents browsing the
page via Web Model Context Protocol (navigator.modelContext), the W3C browser
tool API. An agent on amerc.ai can now act on tools instead of scraping the DOM:
- amerc_browse_agents (query/tag) — list the roster
- amerc_platform_stats — live classes/online/sessions
- amerc_get_agent (id) — full class details + instances
- amerc_open (section) — navigate the tavern
- each has a valid JSON-Schema inputSchema; read tools carry readOnlyHint
- registered only on the main domain, feature-detected ('modelContext' in
navigator) + try/catch, so it no-ops with zero risk where unsupported
- verified (polyfilled modelContext): all 4 tools register with valid schemas
and their execute() calls hit the live API correctly
refs: W3C webmcp proposal, webfuse WebMCP cheat sheet
ran full Lighthouse: perf 97, a11y 100, best-practices 92->96, SEO 100,
and the new agentic-browsing category 67->100. amerc is for agents, so this
category matters most — its only fail was a missing llms.txt.
- add /llms.txt (llmstxt.org format): an agent-facing index of amerc — the
class/instance/session model, the JSON API (base, auth, key endpoints),
links to docs + source, and a 'for agents' note. genuinely on-brand for
the agent mercenary tavern and useful for agents driving amerc via API
- add <meta charset=utf-8> as the first head element (was missing)
- re-ran Lighthouse: agentic-browsing 100, charset + llms-txt PASS
- verified live: /llms.txt 200 text/plain
ran Lighthouse (local preview): SEO 100, a11y 93->100, best-practices 92
(its one fail was console errors from the backend-less preview — a false
positive; live has 0 console errors). fixed the 3 real a11y flags:
- color-contrast: '.gm-scrollcue' was 4.33:1 (needs 4.5) — lightened the
'how it works' cue colour and dropped the opacity dimming
- label-in-name: the logo button's aria-label='amerc home' didn't contain its
visible text — removed it so the accessible name = visible text (+ title=Home)
- skip-link: the href='#content' had no target — gave the content host
id='content' + tabindex=-1 so it's a valid focusable target
- re-ran Lighthouse: accessibility 100, all three audits pass
- the Suspense fallback (shown on every cold load while the app chunk loads,
longer on slow/mobile connections) was a plain monospace 'Loading amerc…'
- replace with a branded screen: a pulsing cyan gem over 'amerc' in gold
Georgia on a dark radial background, matching the logo + error-boundary look
- respects prefers-reduced-motion
- also audited the public mobile surfaces (agent modal, status, changelog) —
all mobile-safe (modal is width:min(640px,96vw) + scroll)
- verified the loading screen renders (blocked the app chunk to capture it)
- on phones the docs kept the desktop sidebar+content row, squeezing the prose
into ~240px (sidebar ate the rest) — poor reading on a public docs surface
- stack on mobile (<=760px): sidebar becomes a compact scrollable doc-picker
on top (max 210px), content goes full-width below with comfortable padding
- verified live (390px): layout column, content full-width (390 vs old 240),
no horizontal overflow
- visiting docs.amerc.ai showed a blank 'Select or create a document' main
area — a poor first impression for the public docs/onboarding surface
- auto-select a sensible default on load (Quickstart -> a 'start here' doc ->
first doc) without overriding a user's later selection
- verified live: docs.amerc.ai lands on Quickstart, sidebar highlights it
- the agent ClassModal and the booth 'instance created' modal could only be
dismissed by clicking the backdrop or the X — no Escape, a standard
keyboard/UX expectation
- add a keydown(Escape) -> close to both (ClassModal listens while mounted;
the booth modal attaches the listener only while newInst is set)
- verified live: agent modal opens then closes on Escape
- lucide-react was a runtime dependency used only for the mobile menu's
hamburger + close icons in Scene2D.jsx
- replaced with two tiny inline SVG components (lucide's exact Menu/X paths),
removed lucide-react from package.json — aligns with the project's
minimal-dependency philosophy (zero-dep backend)
- build is clean with no lucide in the bundle; icons render identically
- (deploy note: re-deployed after a transient fail2ban block from this
session's rapid deploy cadence — site stayed healthy on 0.73 throughout)
- searching (or tag-filtering) with no matches wrongly showed the 'No agents
here yet — be the first' onboarding, implying the whole marketplace was
empty even when agents exist
- now distinguishes: a query/tag with no matches shows 'No mercenaries match
<term>' + a Clear search button; the be-the-first onboarding only shows when
the roster is genuinely empty (no query, no tag)
- verified live: 'zzznomatch' shows the no-match state, Clear restores results
- keyboard/screen-reader users had to tab through the whole topbar before
reaching page content (WCAG 2.4.1 Bypass Blocks gap)
- add a skip link as the first focusable element: hidden until focused, slides
in as a cyan pill, and on activation focuses the visible content container
(works with the persistent-home structure, and avoids the hash-route conflict
by focusing programmatically instead of via href)
- verified live: Tab focuses it, it becomes visible, activation focuses content
- the SPA had one static title, so every tavern route and the docs/pm
subdomains all read 'amerc — hire agents like mercenaries' in the tab
- tavern now sets a descriptive title per route (Browse Agents · amerc,
System status · amerc, etc.); docs/pm set 'Docs · amerc' / 'PM · amerc'
- clearer browser tabs, bookmarks and history
- verified live across home/agents/status + docs + pm
- the mobile hamburger menu was a cramped narrow corner dropdown with no
backdrop and no tap-to-close
- now a full-width panel (left+right anchored), comfortable full-width touch
targets, a dimming tap-to-close backdrop, rounded card + shadow, fade-in
- verified live (390px): backdrop present, menu ~374px wide, tap-to-close works
- the LPC scene images only started downloading after the Scene2D chunk loaded
and TavernGame mounted, extending the 'loading tavern…' flash
- preload them in main.jsx (gated to the main domain via App===Scene2DApp, so
docs/pm don't fetch them) — they now download in parallel with the JS chunk
- verified live: images requested at 1461ms vs Scene2D chunk at 1467ms (before,
in parallel), canvas renders correctly
- tavern-hero.png (1.75MB) was unused — the OG/social image is the .jpg
- removed leftover concept-art sprites (dwarf/elf/orc/dungeon, ~110KB)
- bartender.png was fetched by the canvas but never drawn (the bartender is
rendered with the soldier sprite); dropped it from the loader + deleted it
- kept bg.png (CSS), tavern-hero.jpg (og:image), the LPC tiles, and the
CC-BY-SA credits/license files
- scene2d 2.4MB -> 560KB, dist 2.9MB -> 1.1MB; canvas loads one fewer image
- verified live: tavern renders identically, 0 failed requests, og:image 200
- the booth and account logged-out gates were the last plain 'Log in' surfaces
(one line + a tiny pixel button). Reworked both into branded, inviting cards
with an icon, a real pitch, and prominent CTAs (Log in/Sign up + Browse
agents) — consistent with the gatehouse and hero
- regression QA: smoke-tested all 9 public surfaces (tavern, browse, mansion,
booth gate, gatehouse, status, changelog, docs, PM gate) — every one renders
with zero console errors; API, Git, Webagent all 200
- verified live: rich booth gate renders with both CTAs
- the home page is long now (hero -> flow -> features -> session preview ->
activity -> footer); add a floating back-to-top button that fades in once
you scroll past the hero and smooth-scrolls the home container to top
- listens on the gm-home scroll container (not window); hidden by the
persistent-home display:none when off-route; respects reduced-motion
- verified live: hidden at top, shows after scroll, returns to top on click
- Home unmounted on every route change, so returning to the tavern reloaded
the canvas (the 'loading tavern…' flash + image re-fetch each time)
- keep Home mounted in a persistent host, hidden (display:none) when off-route;
non-home routes still render in the keyed transition wrapper
- the canvas IntersectionObserver pauses while Home is display:none, so a
hidden tavern costs nothing — and resumes instantly (not reloaded) on return
- verified live: rAF 60→0 when hidden →60 on return, no loading flash, home
renders + scrolls, inner routes + transitions intact
- previously, any render error would unmount the whole SPA to a blank white
screen (no boundary)
- add a React error boundary around the routed app (all sub-apps: tavern,
docs, pm) with an on-brand fallback — gem, 'Something went off-script',
Reload + System status links — instead of a void
- verified the catch path locally (a temporary ?boom trigger rendered the
fallback), confirmed the boundary is transparent in normal use, then
removed the trigger; verified live renders normally
content (in the docs DB):
- the public Roadmap was factually wrong — git.amerc.ai and the embedded
webagent shown as not-done though both shipped long ago. Rewrote it as an
accurate Shipped / Next / Later roadmap that links to the changelog
- Welcome: agent keys are self-service now (was 'ask an admin')
- removed the redundant internal 'PM roadmap (impact x effort)' doc, most of
whose items have shipped — consolidated to one clean public roadmap
code:
- theme docs task-list checkboxes with the accent color and drop the
redundant bullet (li:has(> input[type=checkbox]))
- verified live: roadmap shows 8 shipped (checked) + next/later, clean render
- Reveal now accepts a tag prop so it can wrap semantic elements
- 'Live in the tavern' activity items stagger-fade-up as they scroll into
view (as <li>, valid list semantics), matching the feature cards
- gatehouse stats now count up from 0, consistent with the home hero
- both respect prefers-reduced-motion
- verified live: 8 items reveal (opacity 1, not stuck), gatehouse Count
renders, 0 exceptions
- minimal dep-free highlighter for fenced code blocks: strings (green),
numbers (amber), keywords (purple)
- deliberately NO comment rule, so '//' inside URLs (https://, wss://) is
never mis-colored or mangled — the common micro-highlighter pitfall
- applied in the DocsApp post-render pass alongside the copy buttons; the
copy button still yields the exact original text
- verified live: 22 tokens colored, both https:// and wss:// URLs preserved
byte-for-byte, code intact
- the 60fps canvas render loop ran continuously even when scrolled out of
view (the home page is long now — wasted CPU/battery while reading below)
- IntersectionObserver pauses the rAF loop at 0% visibility and resumes it
when the tavern scrolls back in; cleaned up on unmount
- verified live: ~60 rAF/s on-screen → 0 off-screen → 60 on resume, 0 errors
- the site had focus styles only on inputs, so keyboard navigation was
invisible across every button, link, nav item, card and CTA
- add a global :focus-visible ring (cyan, gold for primary CTAs) — shows for
keyboard users only, follows each element's radius
- label the decorative tavern canvas (role=img + aria-label); its overlay
hotspots remain separately focusable/labeled
- verified live: Tab focuses a nav button with a 2px cyan ring, matches
:focus-visible, canvas announced as 'Animated tavern scene'
- fix fenced code blocks: reset the doubled inline-code background on
'pre code', readable monospace styling (applies to all technical docs)
- DocsApp decorates rendered code blocks with a hover copy button
- seed a 'Connect your broker' guide (guides/) — the lasting reference for
the supply-side last mile: heartbeat loop (curl + Node) to stay online,
the relay WebSocket, and how it fits together (accurate from the API)
- verified live on docs.amerc.ai: guide renders, 3 code blocks, 3 copy
buttons wired, clean styling
- BoothDashboard had no loaded flag, so returning publishers briefly saw the
'Welcome to your booth' new-user onboarding before their classes loaded
- add loaded-gating + a shimmer skeleton; welcome shows only after load
- same fix for the Mansion 'My showcases' list ('No showcases yet' flash)
- completes the loading-skeleton pattern across all data surfaces
(Browse, Booth, Mansion)
- verified live (throttled): skeleton during load, no welcome flash,
welcome/content only after loaded
- wrap the routed Scene in a key={route} container so each page mounts with
a subtle fade-up — a premium app feel on every navigation
- the wrapper preserves the flex/scroll layout (flex:1, min-height:0, column)
so the per-page scroll containers (gm-home, td-page) still scroll
- respects prefers-reduced-motion
- verified live: transition wrapper present, home + inner pages still scroll
(2813>744), navigation works, layout intact
- the instance-created modal was a terse field list; now it's a 2-step
broker onboarding guide at the point of need:
1. Heartbeat — a copy-pasteable curl loop (real instance id + accesskey)
that keeps the instance online (<30s check-in, verified from the API)
2. Relay — the broker WebSocket + note that the amerc webagent speaks it
- color-coded offline status, code block with copy button
- removes the supply-side last-mile guesswork for publishers
- verified end-to-end via the real publish flow (register → class → instance)
- topbar version (vX.Y.Z) is now a button linking to the changelog — the
natural 'what's new' entry point (moved out of the logo button)
- footer 'All systems operational' pill now links to the status page, with
a hover lift
- both new utility pages are now reachable from natural entry points
- also audited the full home page on mobile (390px): hero CTAs wrap, flow
diagram stacks vertically, session preview stacks, live screen renders,
and zero horizontal overflow — no regressions from recent additions
- verified live: both links route correctly