diff --git a/modules/rvote/mod.ts b/modules/rvote/mod.ts
index 6ce422f..2ded13c 100644
--- a/modules/rvote/mod.ts
+++ b/modules/rvote/mod.ts
@@ -506,17 +506,10 @@ routes.post("/api/proposals/:id/final-vote", async (c) => {
return c.json({ ok: true, tally });
});
-// ── Page routes ──
+// ── Demo page body (reused by / when space=demo, and /demo fallback) ──
-// Demo page — interactive polls with live sync
-routes.get("/demo", (c) => {
- return c.html(renderShell({
- title: "rVote Demo — Interactive Polls | rSpace",
- moduleId: "rvote",
- spaceSlug: "demo",
- modules: getModuleInfoList(),
- theme: "dark",
- body: `
+function renderDemoBody(): string {
+ return `
Interactive Demo
@@ -635,15 +628,33 @@ routes.get("/demo", (c) => {
-
`,
- scripts: ``,
- styles: `
`,
- }));
+
`;
+}
+
+// ── Page routes ──
+
+// Legacy /demo path — redirect to demo.rspace.online/rvote
+routes.get("/demo", (c) => {
+ return c.redirect("https://demo.rspace.online/rvote", 301);
});
-// Dashboard — full voting app with spaces, proposals, conviction voting
+// Main route — serves demo page when space=demo, dashboard otherwise
routes.get("/", (c) => {
const space = c.req.param("space") || "demo";
+
+ if (space === "demo") {
+ return c.html(renderShell({
+ title: "rVote Demo — Interactive Polls | rSpace",
+ moduleId: "rvote",
+ spaceSlug: "demo",
+ modules: getModuleInfoList(),
+ theme: "dark",
+ body: renderDemoBody(),
+ scripts: ``,
+ styles: `