diff --git a/index.html b/index.html
index c5eb0e7..2d693ec 100644
--- a/index.html
+++ b/index.html
@@ -4,7 +4,7 @@
-
+
diff --git a/package.json b/package.json
index 00c8069..adbeba6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "amerc-site",
- "version": "0.50.0-hero",
+ "version": "0.51.0-skel",
"private": true,
"type": "module",
"scripts": {
diff --git a/src/AgentPlatform.jsx b/src/AgentPlatform.jsx
index aeb91eb..061e49c 100644
--- a/src/AgentPlatform.jsx
+++ b/src/AgentPlatform.jsx
@@ -15,6 +15,7 @@ export function AgentBrowse({ auth, setRoute }) {
const [q, setQ] = useState('');
const [open, setOpen] = useState(null);
const [err, setErr] = useState('');
+ const [loaded, setLoaded] = useState(false);
const load = useCallback(async () => {
setErr('');
@@ -22,7 +23,7 @@ export function AgentBrowse({ auth, setRoute }) {
const [c, s] = await Promise.all([api(`/agents/classes${tag ? `?tag=${encodeURIComponent(tag)}` : ''}`), api('/agents/stats')]);
setClasses((c.classes || []).filter((x) => !q || (x.name + x.tags.join() + x.description).toLowerCase().includes(q.toLowerCase())));
setTags(s.tags || {});
- } catch (e) { setErr(e.message); }
+ } catch (e) { setErr(e.message); } finally { setLoaded(true); }
}, [tag, q]);
useEffect(() => { load(); }, [load]);
@@ -43,7 +44,14 @@ export function AgentBrowse({ auth, setRoute }) {
{err &&
{err}
}
- {classes.map((c) => (
+ {!loaded && Array.from({ length: 3 }).map((_, i) => (
+
+ ))}
+ {loaded && classes.map((c) => (
))}
- {classes.length > 0 && (
+ {loaded && classes.length > 0 && (
)}
- {!classes.length && (
+ {loaded && !classes.length && (
No agents here yet — be the first.
diff --git a/src/Scene2D.jsx b/src/Scene2D.jsx
index 44d804a..df100ee 100644
--- a/src/Scene2D.jsx
+++ b/src/Scene2D.jsx
@@ -7,7 +7,7 @@ import Account from './Account.jsx';
import TavernGame from './TavernGame.jsx';
import { api } from './api.js';
-const RELEASE = '0.50.0-hero';
+const RELEASE = '0.51.0-skel';
const NAV = [
{ id: 'home', label: 'Tavern' },
diff --git a/src/styles.css b/src/styles.css
index 683631e..f2f95dd 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -1356,3 +1356,16 @@ button {
.gm-cta-primary:hover { transform: translateY(-2px); box-shadow: 0 10px 26px rgba(243,210,122,0.42); }
.gm-cta-secondary { background: rgba(18,12,28,0.66); color: #ecdfc6; box-shadow: inset 0 0 0 1.5px rgba(243,210,122,0.5); backdrop-filter: blur(2px); }
.gm-cta-secondary:hover { background: rgba(42,30,58,0.85); color: #fff; transform: translateY(-2px); }
+
+/* browse loading skeletons (0.51) */
+.ap-skel { pointer-events: none; }
+.ap-skel-head { display: flex; align-items: center; gap: 11px; }
+.sk-av, .sk-tt i, .sk-p, .ap-skel-foot i { background: linear-gradient(90deg, #1a1330 25%, #261b42 37%, #1a1330 63%); background-size: 400% 100%; animation: sk-shimmer 1.4s ease infinite; border-radius: 6px; display: block; }
+.sk-av { width: 38px; height: 38px; border-radius: 10px; flex: 0 0 auto; }
+.sk-tt { display: flex; flex-direction: column; gap: 6px; flex: 1; }
+.sk-tt i { height: 11px; width: 60%; } .sk-tt i:last-child { width: 35%; height: 9px; }
+.sk-p { height: 9px; width: 100%; margin-top: 12px; } .sk-p.sk-short { width: 70%; margin-top: 7px; }
+.ap-skel-foot { display: flex; justify-content: space-between; margin-top: 14px; }
+.ap-skel-foot i { height: 9px; width: 30%; }
+@keyframes sk-shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }
+@media (prefers-reduced-motion: reduce) { .sk-av, .sk-tt i, .sk-p, .ap-skel-foot i { animation: none; } }