diff --git a/index.html b/index.html
index 5880668..8edfef4 100644
--- a/index.html
+++ b/index.html
@@ -4,7 +4,7 @@
-
+
diff --git a/package.json b/package.json
index 2926609..36c7d05 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "amerc-site",
- "version": "0.63.0-docsaccuracy",
+ "version": "0.64.0-resilience",
"private": true,
"type": "module",
"scripts": {
diff --git a/src/Scene2D.jsx b/src/Scene2D.jsx
index f6e4513..5c085bd 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.63.0-docsaccuracy';
+const RELEASE = '0.64.0-resilience';
const NAV = [
{ id: 'home', label: 'Tavern' },
diff --git a/src/main.jsx b/src/main.jsx
index 2b1bd53..43c96ca 100644
--- a/src/main.jsx
+++ b/src/main.jsx
@@ -12,11 +12,37 @@ let App = Scene2DApp;
if (host.startsWith('docs.')) App = DocsApp;
else if (host.startsWith('pm.')) App = PmApp;
+// Catch render errors so a single bad component can't blank the whole app.
+class ErrorBoundary extends React.Component {
+ constructor(props) { super(props); this.state = { error: null }; }
+ static getDerivedStateFromError(error) { return { error }; }
+ render() {
+ if (this.state.error) {
+ return (
+
+
+
+
Something went off-script.
+
The tavern hit an unexpected snag. A reload usually sorts it out.
+
+
+
+ );
+ }
+ return this.props.children;
+ }
+}
+
createRoot(document.getElementById('root')).render(
- Loading amerc…}>
-
-
+
+ Loading amerc…}>
+
+
+
,
);
diff --git a/src/styles.css b/src/styles.css
index 5619c50..67d2228 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -1435,3 +1435,14 @@ button {
/* themed task-list checkboxes in docs (0.63) */
.docs-render input[type="checkbox"] { accent-color: var(--accent); width: 14px; height: 14px; vertical-align: -2px; margin-right: 6px; }
.docs-render li:has(> input[type="checkbox"]) { list-style: none; margin-left: -20px; }
+
+/* error boundary fallback (0.64) */
+.eb { position: fixed; inset: 0; display: grid; place-items: center; background: radial-gradient(circle at 50% 28%, #160d22, #07060c 70%); padding: 24px; }
+.eb-card { max-width: 420px; text-align: center; background: linear-gradient(180deg, #160f24, #110b1d); border: 1px solid #2a1f3e; border-radius: 16px; padding: 32px 28px; box-shadow: 0 20px 60px rgba(0,0,0,0.5); font-family: Georgia, "Times New Roman", serif; }
+.eb-gem { width: 26px; height: 26px; display: inline-block; background: linear-gradient(135deg, #5fe6ff, #1f86c4); transform: rotate(45deg); box-shadow: 0 0 0 3px #07273a, 0 0 24px rgba(39,216,255,0.5); margin-bottom: 20px; }
+.eb-card h1 { color: #f3d27a; font-size: 22px; margin: 0 0 10px; }
+.eb-card p { color: #b6a7d4; font-size: 14px; line-height: 1.6; margin: 0 0 22px; }
+.eb-actions { display: flex; gap: 12px; justify-content: center; align-items: center; }
+.eb-actions button { font-family: Georgia, serif; font-size: 14px; padding: 9px 22px; border-radius: 10px; border: none; cursor: pointer; background: linear-gradient(180deg, #f3d27a, #d9a948); color: #2a1303; font-weight: 600; }
+.eb-actions a { font-family: Georgia, serif; font-size: 14px; color: #cfe6ff; text-decoration: none; }
+.eb-actions a:hover { color: #7fe9ff; }