openvibers/openvibe.sites Commits

Unknown paths answer 404 on every Sites domain

OP OpenVibers committed 24 Sept 2026 · 508863cfa0 · 35 files, +699 −18

Every vhost ended in `try_files $uri $uri.html $uri/ /index.html`, so any path (/llms.txt,
/.well-known/openvibe, /release.json, typos) answered 200 with the front page: a soft 404 on all 16
domains (audit §32.1).

Each domain now gets dist/<domain>/404.html (noindex, links the front page and openvibe.network), and
the generated vhost ends in `=404` with `error_page 404 /404.html` (the page itself is internal, so
/404.html also answers 404). /terms, /privacy and /dmca still resolve through $uri.html.

Checked in nginx 1.28.3 (the host's version) with the generated vhosts: / /terms /status.json
/robots.txt /sitemap.xml 200; /nope, /deep/path/x.png, /llms.txt, /404.html 404 with the page;
realtime.openvibe.network/sitemap.xml 404; nginx -t passes.

deploy.sh installs the changed vhosts and reloads nginx after `nginx -t`, as before.

Changes

ddiff --git a/README.md b/README.md
iindex 504b274..118dde9 100644
---- a/README.md
++++ b/README.md
@@@ -28,6 +28,10 @@ subdomain of a live service says it is not routed yet; only a repository with no
"charter only". The build reads the committed snapshot, so it is reproducible; `npm test` fails
locally when a sibling's `STATUS.json` stage or code flag no longer matches it.
++Every domain also gets a `404.html`: its vhost answers any path that is not a file in
++`dist/<domain>/` with status 404 and that page (`try_files … =404` + `error_page 404 /404.html`),
++never the front page.
++
Deploy (host): the repo lives at `/opt/openvibe.sites`; `deploy/scripts/deploy.sh` runs `npm ci`, rebuilds, installs
the vhosts into `/etc/nginx/sites-available`, enables them and reloads nginx. Each domain has a
Let's Encrypt wildcard certificate (`certbot --dns-cloudflare`, see the host's renewal configs).
ddiff --git a/build.js b/build.js
iindex c27ff49..bcebe39 100644
---- a/build.js
++++ b/build.js
@@@ -275,7 +275,48 @@ function robots(site) { return `User-agent: *\nAllow: /\nDisallow: /auth/\n${sit
function sitemap(site) { return `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n <url><loc>https://${site.domain}/</loc><lastmod>${today}</lastmod><changefreq>weekly</changefreq><priority>1.0</priority></url>\n</urlset>\n`; }
function manifest(site) { return JSON.stringify(require('openvibe-shared/app-icon').manifest({ site: 'network', name: site.name, shortName: site.name.split('.').pop(), description: site.tagline, iconBase: `${NET.networkUrl}/assets` }), null, 2) + '\n'; }
--/** nginx vhost: static root, wildcard cert, www → apex, long cache for the immutable bits. */
++/** 404.html: what nginx answers (with status 404) for any path that is not a file in dist/<domain>/. */
++function notFound(site) {
++ const home = `https://${site.domain}/`;
++ return `<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · ${esc(site.name)}</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="${esc(site.accent)}">
++${require('openvibe-shared/app-icon').headTags({ site: 'network' }).split('\n')[0]}
++${require('openvibe-shared/app-icon').CRITICAL}
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:${esc(site.accent)}}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>${esc(site.domain)} has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="${home}">${esc(site.name)}</a>
++ <a href="${NET.networkUrl}/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
++`;
++}
++
++/** nginx vhost: static root, wildcard cert, www → apex, long cache for the immutable bits, real 404s. */
function vhost(site) {
const d = site.domain;
// Subdomains of a zone (e.g. events.openvibe.network) use the zone's wildcard cert and have no www.
@@@ -327,9 +368,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\\.(?!well-known) { deny all; }
@@@ -360,6 +405,7 @@ function build() {
const out = {};
for (const site of catalog.sites) {
out[`${site.domain}/index.html`] = page(site);
++ out[`${site.domain}/404.html`] = notFound(site);
out[`${site.domain}/robots.txt`] = robots(site);
if (!site.kind) out[`${site.domain}/sitemap.xml`] = sitemap(site);
out[`${site.domain}/manifest.webmanifest`] = manifest(site);
ddiff --git a/deploy/nginx/admin.openvibe.network.conf b/deploy/nginx/admin.openvibe.network.conf
iindex 0a221b9..bd8e5ac 100644
---- a/deploy/nginx/admin.openvibe.network.conf
++++ b/deploy/nginx/admin.openvibe.network.conf
@@@ -34,9 +34,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\.(?!well-known) { deny all; }
ddiff --git a/deploy/nginx/ai.openvibe.network.conf b/deploy/nginx/ai.openvibe.network.conf
iindex 6cefdde..00acd8a 100644
---- a/deploy/nginx/ai.openvibe.network.conf
++++ b/deploy/nginx/ai.openvibe.network.conf
@@@ -34,9 +34,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\.(?!well-known) { deny all; }
ddiff --git a/deploy/nginx/api.openvibe.network.conf b/deploy/nginx/api.openvibe.network.conf
iindex c154bb3..1819459 100644
---- a/deploy/nginx/api.openvibe.network.conf
++++ b/deploy/nginx/api.openvibe.network.conf
@@@ -34,9 +34,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\.(?!well-known) { deny all; }
ddiff --git a/deploy/nginx/auth.openvibe.network.conf b/deploy/nginx/auth.openvibe.network.conf
iindex 351746d..56648c8 100644
---- a/deploy/nginx/auth.openvibe.network.conf
++++ b/deploy/nginx/auth.openvibe.network.conf
@@@ -34,9 +34,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\.(?!well-known) { deny all; }
ddiff --git a/deploy/nginx/openre.stream.conf b/deploy/nginx/openre.stream.conf
iindex 878e363..3a027f2 100644
---- a/deploy/nginx/openre.stream.conf
++++ b/deploy/nginx/openre.stream.conf
@@@ -44,9 +44,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\.(?!well-known) { deny all; }
ddiff --git a/deploy/nginx/openvibe.chat.conf b/deploy/nginx/openvibe.chat.conf
iindex 32e607a..80614c6 100644
---- a/deploy/nginx/openvibe.chat.conf
++++ b/deploy/nginx/openvibe.chat.conf
@@@ -44,9 +44,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\.(?!well-known) { deny all; }
ddiff --git a/deploy/nginx/openvibe.coupons.conf b/deploy/nginx/openvibe.coupons.conf
iindex aa8089b..def1e6d 100644
---- a/deploy/nginx/openvibe.coupons.conf
++++ b/deploy/nginx/openvibe.coupons.conf
@@@ -44,9 +44,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\.(?!well-known) { deny all; }
ddiff --git a/deploy/nginx/openvibe.deals.conf b/deploy/nginx/openvibe.deals.conf
iindex f18bc81..4064c60 100644
---- a/deploy/nginx/openvibe.deals.conf
++++ b/deploy/nginx/openvibe.deals.conf
@@@ -44,9 +44,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\.(?!well-known) { deny all; }
ddiff --git a/deploy/nginx/openvibe.host.conf b/deploy/nginx/openvibe.host.conf
iindex 2332df8..d3ec19a 100644
---- a/deploy/nginx/openvibe.host.conf
++++ b/deploy/nginx/openvibe.host.conf
@@@ -44,9 +44,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\.(?!well-known) { deny all; }
ddiff --git a/deploy/nginx/openvibe.news.conf b/deploy/nginx/openvibe.news.conf
iindex 0a483b5..e6bd4cd 100644
---- a/deploy/nginx/openvibe.news.conf
++++ b/deploy/nginx/openvibe.news.conf
@@@ -44,9 +44,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\.(?!well-known) { deny all; }
ddiff --git a/deploy/nginx/openvibe.reviews.conf b/deploy/nginx/openvibe.reviews.conf
iindex d35da01..516499f 100644
---- a/deploy/nginx/openvibe.reviews.conf
++++ b/deploy/nginx/openvibe.reviews.conf
@@@ -44,9 +44,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\.(?!well-known) { deny all; }
ddiff --git a/deploy/nginx/openvibe.tips.conf b/deploy/nginx/openvibe.tips.conf
iindex 7c3628e..bae8816 100644
---- a/deploy/nginx/openvibe.tips.conf
++++ b/deploy/nginx/openvibe.tips.conf
@@@ -44,9 +44,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\.(?!well-known) { deny all; }
ddiff --git a/deploy/nginx/openvibe.trade.conf b/deploy/nginx/openvibe.trade.conf
iindex edb0bcb..642f34b 100644
---- a/deploy/nginx/openvibe.trade.conf
++++ b/deploy/nginx/openvibe.trade.conf
@@@ -44,9 +44,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\.(?!well-known) { deny all; }
ddiff --git a/deploy/nginx/openvibe.vip.conf b/deploy/nginx/openvibe.vip.conf
iindex 2f5cf86..153c73e 100644
---- a/deploy/nginx/openvibe.vip.conf
++++ b/deploy/nginx/openvibe.vip.conf
@@@ -44,9 +44,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\.(?!well-known) { deny all; }
ddiff --git a/deploy/nginx/realtime.openvibe.network.conf b/deploy/nginx/realtime.openvibe.network.conf
iindex 530e2d9..44b09ed 100644
---- a/deploy/nginx/realtime.openvibe.network.conf
++++ b/deploy/nginx/realtime.openvibe.network.conf
@@@ -34,9 +34,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\.(?!well-known) { deny all; }
ddiff --git a/deploy/nginx/themes.openvibe.network.conf b/deploy/nginx/themes.openvibe.network.conf
iindex 62580a8..b41128f 100644
---- a/deploy/nginx/themes.openvibe.network.conf
++++ b/deploy/nginx/themes.openvibe.network.conf
@@@ -34,9 +34,13 @@ server {
location = /manifest.webmanifest { types { application/manifest+json webmanifest; } add_header Cache-Control "public, max-age=86400"; }
location = /status.json { add_header Cache-Control "public, max-age=600"; add_header Access-Control-Allow-Origin "*"; }
++ # Only the files in dist/ exist; anything else is a real 404 with a page, never the front page.
++ error_page 404 /404.html;
++ location = /404.html { internal; }
++
location / {
add_header Cache-Control "public, max-age=600";
-- try_files $uri $uri.html $uri/ /index.html;
++ try_files $uri $uri.html $uri/ =404;
}
location ~ /\.(?!well-known) { deny all; }
ddiff --git a/dist/admin.openvibe.network/404.html b/dist/admin.openvibe.network/404.html
nnew file mode 100644
iindex 0000000..aaaf1ba
---- /dev/null
++++ b/dist/admin.openvibe.network/404.html
@@@ -0,0 +1,35 @@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · admin.openvibe.network</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="#f59e0b">
++<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
++<style id="ov-critical">html{background:#0a0f1c;color:#e6edf7;color-scheme:dark}</style><script>(function(){try{var r=localStorage.getItem('ov_theme');if(!r)return;var t=JSON.parse(r),v=t&&t.variables;if(!v)return;var e=document.documentElement;for(var k in v)if(k.charAt(0)==='-')e.style.setProperty(k,v[k]);if(v['--bg-primary'])e.style.background=v['--bg-primary'];if(v['--text-primary'])e.style.color=v['--text-primary'];if(v['--color-scheme']==='light'||v['--color-scheme']==='dark')e.style.colorScheme=v['--color-scheme'];if(t.id)e.setAttribute('data-theme',t.id)}catch(_){}})();</script>
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:#f59e0b}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>admin.openvibe.network has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="https://admin.openvibe.network/">admin.openvibe.network</a>
++ <a href="https://openvibe.network/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
ddiff --git a/dist/ai.openvibe.network/404.html b/dist/ai.openvibe.network/404.html
nnew file mode 100644
iindex 0000000..81790d2
---- /dev/null
++++ b/dist/ai.openvibe.network/404.html
@@@ -0,0 +1,35 @@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · ai.openvibe.network</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="#c084fc">
++<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
++<style id="ov-critical">html{background:#0a0f1c;color:#e6edf7;color-scheme:dark}</style><script>(function(){try{var r=localStorage.getItem('ov_theme');if(!r)return;var t=JSON.parse(r),v=t&&t.variables;if(!v)return;var e=document.documentElement;for(var k in v)if(k.charAt(0)==='-')e.style.setProperty(k,v[k]);if(v['--bg-primary'])e.style.background=v['--bg-primary'];if(v['--text-primary'])e.style.color=v['--text-primary'];if(v['--color-scheme']==='light'||v['--color-scheme']==='dark')e.style.colorScheme=v['--color-scheme'];if(t.id)e.setAttribute('data-theme',t.id)}catch(_){}})();</script>
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:#c084fc}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>ai.openvibe.network has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="https://ai.openvibe.network/">ai.openvibe.network</a>
++ <a href="https://openvibe.network/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
ddiff --git a/dist/api.openvibe.network/404.html b/dist/api.openvibe.network/404.html
nnew file mode 100644
iindex 0000000..28b052f
---- /dev/null
++++ b/dist/api.openvibe.network/404.html
@@@ -0,0 +1,35 @@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · api.openvibe.network</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="#22d3ee">
++<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
++<style id="ov-critical">html{background:#0a0f1c;color:#e6edf7;color-scheme:dark}</style><script>(function(){try{var r=localStorage.getItem('ov_theme');if(!r)return;var t=JSON.parse(r),v=t&&t.variables;if(!v)return;var e=document.documentElement;for(var k in v)if(k.charAt(0)==='-')e.style.setProperty(k,v[k]);if(v['--bg-primary'])e.style.background=v['--bg-primary'];if(v['--text-primary'])e.style.color=v['--text-primary'];if(v['--color-scheme']==='light'||v['--color-scheme']==='dark')e.style.colorScheme=v['--color-scheme'];if(t.id)e.setAttribute('data-theme',t.id)}catch(_){}})();</script>
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:#22d3ee}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>api.openvibe.network has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="https://api.openvibe.network/">api.openvibe.network</a>
++ <a href="https://openvibe.network/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
ddiff --git a/dist/auth.openvibe.network/404.html b/dist/auth.openvibe.network/404.html
nnew file mode 100644
iindex 0000000..5059156
---- /dev/null
++++ b/dist/auth.openvibe.network/404.html
@@@ -0,0 +1,35 @@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · auth.openvibe.network</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="#60a5fa">
++<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
++<style id="ov-critical">html{background:#0a0f1c;color:#e6edf7;color-scheme:dark}</style><script>(function(){try{var r=localStorage.getItem('ov_theme');if(!r)return;var t=JSON.parse(r),v=t&&t.variables;if(!v)return;var e=document.documentElement;for(var k in v)if(k.charAt(0)==='-')e.style.setProperty(k,v[k]);if(v['--bg-primary'])e.style.background=v['--bg-primary'];if(v['--text-primary'])e.style.color=v['--text-primary'];if(v['--color-scheme']==='light'||v['--color-scheme']==='dark')e.style.colorScheme=v['--color-scheme'];if(t.id)e.setAttribute('data-theme',t.id)}catch(_){}})();</script>
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:#60a5fa}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>auth.openvibe.network has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="https://auth.openvibe.network/">auth.openvibe.network</a>
++ <a href="https://openvibe.network/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
ddiff --git a/dist/openre.stream/404.html b/dist/openre.stream/404.html
nnew file mode 100644
iindex 0000000..210bcae
---- /dev/null
++++ b/dist/openre.stream/404.html
@@@ -0,0 +1,35 @@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · OpenRe.Stream</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="#ef4444">
++<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
++<style id="ov-critical">html{background:#0a0f1c;color:#e6edf7;color-scheme:dark}</style><script>(function(){try{var r=localStorage.getItem('ov_theme');if(!r)return;var t=JSON.parse(r),v=t&&t.variables;if(!v)return;var e=document.documentElement;for(var k in v)if(k.charAt(0)==='-')e.style.setProperty(k,v[k]);if(v['--bg-primary'])e.style.background=v['--bg-primary'];if(v['--text-primary'])e.style.color=v['--text-primary'];if(v['--color-scheme']==='light'||v['--color-scheme']==='dark')e.style.colorScheme=v['--color-scheme'];if(t.id)e.setAttribute('data-theme',t.id)}catch(_){}})();</script>
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:#ef4444}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>openre.stream has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="https://openre.stream/">OpenRe.Stream</a>
++ <a href="https://openvibe.network/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
ddiff --git a/dist/openvibe.chat/404.html b/dist/openvibe.chat/404.html
nnew file mode 100644
iindex 0000000..149bdfe
---- /dev/null
++++ b/dist/openvibe.chat/404.html
@@@ -0,0 +1,35 @@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · OpenVibe.Chat</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="#a78bfa">
++<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
++<style id="ov-critical">html{background:#0a0f1c;color:#e6edf7;color-scheme:dark}</style><script>(function(){try{var r=localStorage.getItem('ov_theme');if(!r)return;var t=JSON.parse(r),v=t&&t.variables;if(!v)return;var e=document.documentElement;for(var k in v)if(k.charAt(0)==='-')e.style.setProperty(k,v[k]);if(v['--bg-primary'])e.style.background=v['--bg-primary'];if(v['--text-primary'])e.style.color=v['--text-primary'];if(v['--color-scheme']==='light'||v['--color-scheme']==='dark')e.style.colorScheme=v['--color-scheme'];if(t.id)e.setAttribute('data-theme',t.id)}catch(_){}})();</script>
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:#a78bfa}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>openvibe.chat has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="https://openvibe.chat/">OpenVibe.Chat</a>
++ <a href="https://openvibe.network/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
ddiff --git a/dist/openvibe.coupons/404.html b/dist/openvibe.coupons/404.html
nnew file mode 100644
iindex 0000000..fc5b10f
---- /dev/null
++++ b/dist/openvibe.coupons/404.html
@@@ -0,0 +1,35 @@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · OpenVibe.Coupons</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="#fbbf24">
++<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
++<style id="ov-critical">html{background:#0a0f1c;color:#e6edf7;color-scheme:dark}</style><script>(function(){try{var r=localStorage.getItem('ov_theme');if(!r)return;var t=JSON.parse(r),v=t&&t.variables;if(!v)return;var e=document.documentElement;for(var k in v)if(k.charAt(0)==='-')e.style.setProperty(k,v[k]);if(v['--bg-primary'])e.style.background=v['--bg-primary'];if(v['--text-primary'])e.style.color=v['--text-primary'];if(v['--color-scheme']==='light'||v['--color-scheme']==='dark')e.style.colorScheme=v['--color-scheme'];if(t.id)e.setAttribute('data-theme',t.id)}catch(_){}})();</script>
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:#fbbf24}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>openvibe.coupons has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="https://openvibe.coupons/">OpenVibe.Coupons</a>
++ <a href="https://openvibe.network/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
ddiff --git a/dist/openvibe.deals/404.html b/dist/openvibe.deals/404.html
nnew file mode 100644
iindex 0000000..1e4ef3f
---- /dev/null
++++ b/dist/openvibe.deals/404.html
@@@ -0,0 +1,35 @@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · OpenVibe.Deals</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="#f97316">
++<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
++<style id="ov-critical">html{background:#0a0f1c;color:#e6edf7;color-scheme:dark}</style><script>(function(){try{var r=localStorage.getItem('ov_theme');if(!r)return;var t=JSON.parse(r),v=t&&t.variables;if(!v)return;var e=document.documentElement;for(var k in v)if(k.charAt(0)==='-')e.style.setProperty(k,v[k]);if(v['--bg-primary'])e.style.background=v['--bg-primary'];if(v['--text-primary'])e.style.color=v['--text-primary'];if(v['--color-scheme']==='light'||v['--color-scheme']==='dark')e.style.colorScheme=v['--color-scheme'];if(t.id)e.setAttribute('data-theme',t.id)}catch(_){}})();</script>
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:#f97316}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>openvibe.deals has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="https://openvibe.deals/">OpenVibe.Deals</a>
++ <a href="https://openvibe.network/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
ddiff --git a/dist/openvibe.host/404.html b/dist/openvibe.host/404.html
nnew file mode 100644
iindex 0000000..9380a78
---- /dev/null
++++ b/dist/openvibe.host/404.html
@@@ -0,0 +1,35 @@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · OpenVibe.Host</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="#94a3b8">
++<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
++<style id="ov-critical">html{background:#0a0f1c;color:#e6edf7;color-scheme:dark}</style><script>(function(){try{var r=localStorage.getItem('ov_theme');if(!r)return;var t=JSON.parse(r),v=t&&t.variables;if(!v)return;var e=document.documentElement;for(var k in v)if(k.charAt(0)==='-')e.style.setProperty(k,v[k]);if(v['--bg-primary'])e.style.background=v['--bg-primary'];if(v['--text-primary'])e.style.color=v['--text-primary'];if(v['--color-scheme']==='light'||v['--color-scheme']==='dark')e.style.colorScheme=v['--color-scheme'];if(t.id)e.setAttribute('data-theme',t.id)}catch(_){}})();</script>
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:#94a3b8}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>openvibe.host has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="https://openvibe.host/">OpenVibe.Host</a>
++ <a href="https://openvibe.network/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
ddiff --git a/dist/openvibe.news/404.html b/dist/openvibe.news/404.html
nnew file mode 100644
iindex 0000000..30993ca
---- /dev/null
++++ b/dist/openvibe.news/404.html
@@@ -0,0 +1,35 @@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · OpenVibe.News</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="#fb923c">
++<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
++<style id="ov-critical">html{background:#0a0f1c;color:#e6edf7;color-scheme:dark}</style><script>(function(){try{var r=localStorage.getItem('ov_theme');if(!r)return;var t=JSON.parse(r),v=t&&t.variables;if(!v)return;var e=document.documentElement;for(var k in v)if(k.charAt(0)==='-')e.style.setProperty(k,v[k]);if(v['--bg-primary'])e.style.background=v['--bg-primary'];if(v['--text-primary'])e.style.color=v['--text-primary'];if(v['--color-scheme']==='light'||v['--color-scheme']==='dark')e.style.colorScheme=v['--color-scheme'];if(t.id)e.setAttribute('data-theme',t.id)}catch(_){}})();</script>
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:#fb923c}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>openvibe.news has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="https://openvibe.news/">OpenVibe.News</a>
++ <a href="https://openvibe.network/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
ddiff --git a/dist/openvibe.reviews/404.html b/dist/openvibe.reviews/404.html
nnew file mode 100644
iindex 0000000..5d2f4ef
---- /dev/null
++++ b/dist/openvibe.reviews/404.html
@@@ -0,0 +1,35 @@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · OpenVibe.Reviews</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="#facc15">
++<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
++<style id="ov-critical">html{background:#0a0f1c;color:#e6edf7;color-scheme:dark}</style><script>(function(){try{var r=localStorage.getItem('ov_theme');if(!r)return;var t=JSON.parse(r),v=t&&t.variables;if(!v)return;var e=document.documentElement;for(var k in v)if(k.charAt(0)==='-')e.style.setProperty(k,v[k]);if(v['--bg-primary'])e.style.background=v['--bg-primary'];if(v['--text-primary'])e.style.color=v['--text-primary'];if(v['--color-scheme']==='light'||v['--color-scheme']==='dark')e.style.colorScheme=v['--color-scheme'];if(t.id)e.setAttribute('data-theme',t.id)}catch(_){}})();</script>
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:#facc15}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>openvibe.reviews has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="https://openvibe.reviews/">OpenVibe.Reviews</a>
++ <a href="https://openvibe.network/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
ddiff --git a/dist/openvibe.tips/404.html b/dist/openvibe.tips/404.html
nnew file mode 100644
iindex 0000000..5cfeb03
---- /dev/null
++++ b/dist/openvibe.tips/404.html
@@@ -0,0 +1,35 @@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · OpenVibe.Tips</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="#4ade80">
++<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
++<style id="ov-critical">html{background:#0a0f1c;color:#e6edf7;color-scheme:dark}</style><script>(function(){try{var r=localStorage.getItem('ov_theme');if(!r)return;var t=JSON.parse(r),v=t&&t.variables;if(!v)return;var e=document.documentElement;for(var k in v)if(k.charAt(0)==='-')e.style.setProperty(k,v[k]);if(v['--bg-primary'])e.style.background=v['--bg-primary'];if(v['--text-primary'])e.style.color=v['--text-primary'];if(v['--color-scheme']==='light'||v['--color-scheme']==='dark')e.style.colorScheme=v['--color-scheme'];if(t.id)e.setAttribute('data-theme',t.id)}catch(_){}})();</script>
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:#4ade80}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>openvibe.tips has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="https://openvibe.tips/">OpenVibe.Tips</a>
++ <a href="https://openvibe.network/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
ddiff --git a/dist/openvibe.trade/404.html b/dist/openvibe.trade/404.html
nnew file mode 100644
iindex 0000000..75715a1
---- /dev/null
++++ b/dist/openvibe.trade/404.html
@@@ -0,0 +1,35 @@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · OpenVibe.Trade</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="#60a5fa">
++<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
++<style id="ov-critical">html{background:#0a0f1c;color:#e6edf7;color-scheme:dark}</style><script>(function(){try{var r=localStorage.getItem('ov_theme');if(!r)return;var t=JSON.parse(r),v=t&&t.variables;if(!v)return;var e=document.documentElement;for(var k in v)if(k.charAt(0)==='-')e.style.setProperty(k,v[k]);if(v['--bg-primary'])e.style.background=v['--bg-primary'];if(v['--text-primary'])e.style.color=v['--text-primary'];if(v['--color-scheme']==='light'||v['--color-scheme']==='dark')e.style.colorScheme=v['--color-scheme'];if(t.id)e.setAttribute('data-theme',t.id)}catch(_){}})();</script>
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:#60a5fa}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>openvibe.trade has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="https://openvibe.trade/">OpenVibe.Trade</a>
++ <a href="https://openvibe.network/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
ddiff --git a/dist/openvibe.vip/404.html b/dist/openvibe.vip/404.html
nnew file mode 100644
iindex 0000000..ce24ec4
---- /dev/null
++++ b/dist/openvibe.vip/404.html
@@@ -0,0 +1,35 @@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · OpenVibe.VIP</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="#e879f9">
++<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
++<style id="ov-critical">html{background:#0a0f1c;color:#e6edf7;color-scheme:dark}</style><script>(function(){try{var r=localStorage.getItem('ov_theme');if(!r)return;var t=JSON.parse(r),v=t&&t.variables;if(!v)return;var e=document.documentElement;for(var k in v)if(k.charAt(0)==='-')e.style.setProperty(k,v[k]);if(v['--bg-primary'])e.style.background=v['--bg-primary'];if(v['--text-primary'])e.style.color=v['--text-primary'];if(v['--color-scheme']==='light'||v['--color-scheme']==='dark')e.style.colorScheme=v['--color-scheme'];if(t.id)e.setAttribute('data-theme',t.id)}catch(_){}})();</script>
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:#e879f9}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>openvibe.vip has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="https://openvibe.vip/">OpenVibe.VIP</a>
++ <a href="https://openvibe.network/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
ddiff --git a/dist/realtime.openvibe.network/404.html b/dist/realtime.openvibe.network/404.html
nnew file mode 100644
iindex 0000000..742f5b3
---- /dev/null
++++ b/dist/realtime.openvibe.network/404.html
@@@ -0,0 +1,35 @@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · realtime.openvibe.network</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="#34d399">
++<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
++<style id="ov-critical">html{background:#0a0f1c;color:#e6edf7;color-scheme:dark}</style><script>(function(){try{var r=localStorage.getItem('ov_theme');if(!r)return;var t=JSON.parse(r),v=t&&t.variables;if(!v)return;var e=document.documentElement;for(var k in v)if(k.charAt(0)==='-')e.style.setProperty(k,v[k]);if(v['--bg-primary'])e.style.background=v['--bg-primary'];if(v['--text-primary'])e.style.color=v['--text-primary'];if(v['--color-scheme']==='light'||v['--color-scheme']==='dark')e.style.colorScheme=v['--color-scheme'];if(t.id)e.setAttribute('data-theme',t.id)}catch(_){}})();</script>
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:#34d399}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>realtime.openvibe.network has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="https://realtime.openvibe.network/">realtime.openvibe.network</a>
++ <a href="https://openvibe.network/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
ddiff --git a/dist/themes.openvibe.network/404.html b/dist/themes.openvibe.network/404.html
nnew file mode 100644
iindex 0000000..8af6c98
---- /dev/null
++++ b/dist/themes.openvibe.network/404.html
@@@ -0,0 +1,35 @@
++<!DOCTYPE html>
++<html lang="en">
++<head>
++<meta charset="utf-8">
++<meta name="viewport" content="width=device-width, initial-scale=1">
++<title>Not found · themes.openvibe.network</title>
++<meta name="robots" content="noindex">
++<meta name="theme-color" content="#a78bfa">
++<link rel="icon" type="image/svg+xml" data-ov-icon="network" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-32%20-32%2064%2064%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22ring%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%232563eb%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%22.34%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2360a5fa%22%20stop-opacity%3D%220%22%2F%3E%3C%2FradialGradient%3E%0A%3CradialGradient%20id%3D%22bg%22%20cx%3D%22.5%22%20cy%3D%22.38%22%20r%3D%22.8%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2316223a%22%2F%3E%3Cstop%20offset%3D%22.6%22%20stop-color%3D%22%230a0f1c%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23060912%22%2F%3E%3C%2FradialGradient%3E%3C%2Fdefs%3E%3Crect%20x%3D%22-32%22%20y%3D%22-32%22%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2215%22%20fill%3D%22url(%23bg)%22%2F%3E%3Cg%20transform%3D%22scale(1.22)%22%3E%0A%3Ccircle%20r%3D%2221%22%20fill%3D%22url(%23glow)%22%2F%3E%0A%3Ccircle%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224%22%20opacity%3D%22.38%22%2F%3E%0A%3Cpath%20d%3D%22M0%2C-18%20A18%2C18%200%200%2C1%2017.4%2C4.6%22%20fill%3D%22none%22%20stroke%3D%22url(%23ring)%22%20stroke-width%3D%224.4%22%20stroke-linecap%3D%22round%22%2F%3E%0A%3Cpath%20d%3D%22M-9.5%2C-7%20L0%2C10%20L9.5%2C-7%22%20fill%3D%22none%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%0A%3Ccircle%20cx%3D%2217.4%22%20cy%3D%224.6%22%20r%3D%223.2%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
++<style id="ov-critical">html{background:#0a0f1c;color:#e6edf7;color-scheme:dark}</style><script>(function(){try{var r=localStorage.getItem('ov_theme');if(!r)return;var t=JSON.parse(r),v=t&&t.variables;if(!v)return;var e=document.documentElement;for(var k in v)if(k.charAt(0)==='-')e.style.setProperty(k,v[k]);if(v['--bg-primary'])e.style.background=v['--bg-primary'];if(v['--text-primary'])e.style.color=v['--text-primary'];if(v['--color-scheme']==='light'||v['--color-scheme']==='dark')e.style.colorScheme=v['--color-scheme'];if(t.id)e.setAttribute('data-theme',t.id)}catch(_){}})();</script>
++<style>
++*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
++:root{--bg-primary:#0a0f1c;--bg-secondary:#101828;--border:#1f2d47;--text-primary:#e6edf7;--text-secondary:#96a7c2;--site:#a78bfa}
++body{background:var(--bg-primary);color:var(--text-primary);font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;min-height:100vh;display:grid;place-items:center;padding:24px 16px}
++main{max-width:560px;text-align:center}
++.code{font-size:14px;font-weight:700;letter-spacing:.2em;color:var(--site)}
++h1{font-size:clamp(28px,5vw,40px);font-weight:800;letter-spacing:-.8px;margin:10px 0 12px}
++p{color:var(--text-secondary);font-size:15px;line-height:1.65}
++.links{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:24px}
++a{display:inline-block;padding:11px 18px;border-radius:12px;font-size:14px;font-weight:600;text-decoration:none;border:1px solid var(--border);color:var(--text-primary);background:var(--bg-secondary)}
++a:first-child{background:var(--site);border-color:var(--site);color:#0b0d10}
++</style>
++</head>
++<body>
++<main>
++ <p class="code">404</p>
++ <h1>This page does not exist</h1>
++ <p>themes.openvibe.network has one page for now. The address you followed is not part of it.</p>
++ <div class="links">
++ <a href="https://themes.openvibe.network/">themes.openvibe.network</a>
++ <a href="https://openvibe.network/">OpenVibe.Network</a>
++ </div>
++</main>
++</body>
++</html>
ddiff --git a/test/build.test.js b/test/build.test.js
iindex 165b54f..c5e8ab7 100644
---- a/test/build.test.js
++++ b/test/build.test.js
@@@ -53,6 +53,13 @@ for (const site of catalog.sites) {
const conf = read(`deploy/nginx/${d}.conf`);
assert.ok(conf.includes(`server_name ${d}`), `${d}: vhost server_name`);
assert.ok(conf.includes(`root /opt/openvibe.sites/dist/${d};`), `${d}: vhost root`);
++ // Unknown paths are real 404s with a page (no soft 404: the front page is never the fallback).
++ const nf = read(`dist/${d}/404.html`);
++ assert.match(nf, /<meta name="robots" content="noindex">/, `${d}: 404 page is noindex`);
++ assert.ok(nf.includes(`href="https://${d}/"`), `${d}: 404 page links the front page`);
++ assert.ok(conf.includes('error_page 404 /404.html;'), `${d}: vhost answers 404 with /404.html`);
++ assert.ok(conf.includes('try_files $uri $uri.html $uri/ =404;'), `${d}: vhost try_files ends in =404`);
++ assert.doesNotMatch(conf, /try_files[^;]*\/index\.html;/, `${d}: vhost never falls back to the front page`);
}
// Labels come from facts (facts.json: each repository's STATUS.json + the Network registry), never a