openvibers/openvibe.tools Commits

Remove Google Fonts and use the OpenVibe house font stacks (#325)

OP OpenVibers committed 9 h ago · 7f15bd6c35 · 63 files, +144 −193

* Use the OpenVibe house font stacks instead of Google Fonts

* Restore the logo maker's fonts through openvibe.design

Changes

ddiff --git a/apps/audio/public/css/style.css b/apps/audio/public/css/style.css
iindex 3400ec9..1e12d82 100644
---- a/apps/audio/public/css/style.css
++++ b/apps/audio/public/css/style.css
@@@ -24,8 +24,8 @@
--radius-sm: 6px;
--radius-lg: 16px;
--shadow: 0 4px 24px rgba(0,0,0,.35);
-- --font: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
-- --font-mono: 'JetBrains Mono', 'Fira Code', monospace;
++ --font: Inter, system-ui, -apple-system, 'Segoe UI', sans-serif;
++ --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--transition: .2s ease;
--max-width: 860px;
}
ddiff --git a/apps/audio/public/index.html b/apps/audio/public/index.html
iindex 8189854..8232361 100644
---- a/apps/audio/public/index.html
++++ b/apps/audio/public/index.html
@@@ -10,8 +10,6 @@
<meta name="description" content="Convert, trim, merge, pitch-shift, speed-change, and process audio files online. Supports MP3, WAV, FLAC, OGG, M4A, OPUS, AAC, WMA, AIFF, and more.">
<meta property="og:title" content="Audio.OpenVibe — Online Audio Converter & Tools">
<meta property="og:type" content="website">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -19,7 +17,6 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
ddiff --git a/apps/audio/server/index.js b/apps/audio/server/index.js
iindex d2e9ef2..8d58b7c 100644
---- a/apps/audio/server/index.js
++++ b/apps/audio/server/index.js
@@@ -95,9 +95,9 @@ app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
-- scriptSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://openvibe.network", "https://static.cloudflareinsights.com"],
-- styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://fonts.gstatic.com"],
-- fontSrc: ["'self'", "https://fonts.gstatic.com"],
++ scriptSrc: ["'self'", "'unsafe-inline'", "https://openvibe.network", "https://static.cloudflareinsights.com"],
++ styleSrc: ["'self'", "'unsafe-inline'"],
++ fontSrc: ["'self'"],
imgSrc: ["'self'", "data:", "blob:", jobsRuntime.mediaOrigin()], // job result previews are 302s to Media
mediaSrc: ["'self'", "blob:", jobsRuntime.mediaOrigin()],
// openvibe.events: release notifications (release-watch's EventSource, openvibe-shared 2.13.0).
ddiff --git a/apps/docs/public/css/style.css b/apps/docs/public/css/style.css
iindex d162dd2..81efc3c 100644
---- a/apps/docs/public/css/style.css
++++ b/apps/docs/public/css/style.css
@@@ -30,7 +30,7 @@
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
-- font-family: 'Outfit', -apple-system, BlinkMacSystemFont, sans-serif;
++ font-family: Inter, system-ui, -apple-system, 'Segoe UI', sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
min-height: 100vh;
ddiff --git a/apps/docs/public/index.html b/apps/docs/public/index.html
iindex 1937e31..70adafc 100644
---- a/apps/docs/public/index.html
++++ b/apps/docs/public/index.html
@@@ -10,8 +10,6 @@
<meta name="description" content="Merge, split, compress, convert, rotate, reorder, watermark, and sign PDFs online. No sign-up required.">
<meta property="og:title" content="Docs.OpenVibe — Online PDF & Document Tools">
<meta property="og:type" content="website">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -19,7 +17,6 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
ddiff --git a/apps/docs/server/index.js b/apps/docs/server/index.js
iindex 15d2b7f..4f7b512 100644
---- a/apps/docs/server/index.js
++++ b/apps/docs/server/index.js
@@@ -112,9 +112,9 @@ app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
-- scriptSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://openvibe.network", "https://static.cloudflareinsights.com"],
-- styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://fonts.gstatic.com"],
-- fontSrc: ["'self'", "https://fonts.gstatic.com"],
++ scriptSrc: ["'self'", "'unsafe-inline'", "https://openvibe.network", "https://static.cloudflareinsights.com"],
++ styleSrc: ["'self'", "'unsafe-inline'"],
++ fontSrc: ["'self'"],
imgSrc: ["'self'", "data:", "blob:"],
// openvibe.events: release notifications (release-watch's EventSource, openvibe-shared 2.13.0).
// The navbar's Messages count asks Chat (openvibe-shared 4.130.0).
ddiff --git a/apps/food/public/css/style.css b/apps/food/public/css/style.css
iindex 0eefac0..a0f6fb8 100644
---- a/apps/food/public/css/style.css
++++ b/apps/food/public/css/style.css
@@@ -20,8 +20,8 @@
--purple: #9b59b6;
--orange: #e67e22;
--cyan: #06b6d4;
-- --font: 'Outfit', -apple-system, BlinkMacSystemFont, sans-serif;
-- --font-mono: 'JetBrains Mono', monospace;
++ --font: Inter, system-ui, -apple-system, 'Segoe UI', sans-serif;
++ --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--radius: 10px;
--shadow: 0 4px 24px rgba(0,0,0,0.4);
}
ddiff --git a/apps/food/public/index.html b/apps/food/public/index.html
iindex 69c2f1f..d5eb869 100644
---- a/apps/food/public/index.html
++++ b/apps/food/public/index.html
@@@ -22,8 +22,6 @@
<meta name="twitter:title" content="Food.OpenVibe — Budget Grocery & Meal Planning">
<meta name="twitter:description" content="Budget grocery finder, meal planning optimizer, food bank locator, and nutrition database for survival on a budget.">
<meta name="twitter:image" content="https://food.openvibe.tools/og.png">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -31,7 +29,6 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;600&family=Outfit:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css">
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/src/toastify.min.css">
ddiff --git a/apps/food/public/js/app.js b/apps/food/public/js/app.js
iindex 1276dab..4d436d6 100644
---- a/apps/food/public/js/app.js
++++ b/apps/food/public/js/app.js
@@@ -26,7 +26,7 @@
function toast(msg, type = 'info') {
const bg = { info: '#22c55e', error: '#ef4444', warn: '#f59e0b' };
Toastify({ text: msg, duration: 3000, gravity: 'bottom', position: 'right',
-- style: { background: bg[type] || bg.info, borderRadius: '10px', fontFamily: "'Outfit',sans-serif", fontSize: '12px', padding: '8px 16px' }
++ style: { background: bg[type] || bg.info, borderRadius: '10px', fontFamily: "Inter, system-ui, -apple-system, 'Segoe UI', sans-serif", fontSize: '12px', padding: '8px 16px' }
}).showToast();
}
ddiff --git a/apps/food/server/index.js b/apps/food/server/index.js
iindex 1139778..86c010d 100644
---- a/apps/food/server/index.js
++++ b/apps/food/server/index.js
@@@ -85,9 +85,9 @@ app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
-- scriptSrc: ["'self'", "'unsafe-inline'", 'cdn.jsdelivr.net', 'unpkg.com', 'fonts.googleapis.com', 'https://openvibe.network', "https://static.cloudflareinsights.com"],
-- styleSrc: ["'self'", "'unsafe-inline'", 'cdn.jsdelivr.net', 'unpkg.com', 'fonts.googleapis.com', 'fonts.gstatic.com'],
-- fontSrc: ["'self'", 'fonts.gstatic.com'],
++ scriptSrc: ["'self'", "'unsafe-inline'", 'cdn.jsdelivr.net', 'unpkg.com', 'https://openvibe.network', "https://static.cloudflareinsights.com"],
++ styleSrc: ["'self'", "'unsafe-inline'", 'cdn.jsdelivr.net', 'unpkg.com'],
++ fontSrc: ["'self'"],
imgSrc: ["'self'", 'data:', 'image.tmdb.org', '*.tile.openstreetmap.org', '*.basemaps.cartocdn.com', 'server.arcgisonline.com'],
// openvibe.events: release notifications (release-watch's EventSource, openvibe-shared 2.13.0).
// The navbar's Messages count asks Chat (openvibe-shared 4.130.0).
ddiff --git a/apps/gateway/public/audio.html b/apps/gateway/public/audio.html
iindex be2155c..936967e 100644
---- a/apps/gateway/public/audio.html
++++ b/apps/gateway/public/audio.html
@@@ -15,8 +15,6 @@
<meta property="og:type" content="website">
<meta property="og:url" content="https://audio.openvibe.tools">
<link rel="canonical" href="https://audio.openvibe.tools">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -24,7 +22,6 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<script src="/shared/theme-loader.js" defer></script>
<style>
:root {
@@@ -48,7 +45,7 @@
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { background: var(--bg-primary); scroll-behavior: smooth; }
-- body { min-height: 100vh; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; background: var(--bg-primary); color: var(--text-primary); line-height: 1.6; }
++ body { min-height: 100vh; font-family: Inter, system-ui, -apple-system, 'Segoe UI', sans-serif; background: var(--bg-primary); color: var(--text-primary); line-height: 1.6; }
.container { max-width: 1200px; margin: 0 auto; padding: 20px; }
ddiff --git a/apps/gateway/public/codes.html b/apps/gateway/public/codes.html
iindex b1f60ba..f04c84b 100644
---- a/apps/gateway/public/codes.html
++++ b/apps/gateway/public/codes.html
@@@ -33,7 +33,7 @@
--c-brd:var(--border,#1f2d47);--c-brd2:var(--border-light,#2c3d5c);
--c-ok:var(--success-text,#22c55e);--c-warn:var(--warning-text,#f59e0b);--c-bad:var(--danger-text,#f26262);
--c-shadow:var(--shadow,0 2px 12px rgba(0,0,0,.45));--c-shadow-lg:var(--shadow-lg,0 8px 32px rgba(0,0,0,.6));
-- --r:14px;--r-s:10px;--mono:'JetBrains Mono',ui-monospace,Menlo,Consolas,monospace;
++ --r:14px;--r-s:10px;--mono:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
/* the SEO block under the tool (server/seo/render.js) */
--tx:var(--c-tx);--tx2:var(--c-tx2);--tx3:var(--c-tx3);--bg2:var(--c-panel);--brd:var(--c-brd);--ac:var(--c-ac);
}
@@@ -220,9 +220,6 @@ table.ais td.v{font-family:var(--mono);word-break:break-all}
#print-sheet .lab span{font:7pt/1.1 ui-monospace,Menlo,monospace;color:#000;margin-top:1mm;text-align:center;word-break:break-all}
}
</style>
--<link rel="preconnect" href="https://fonts.googleapis.com">
--<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
--<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
</head>
<body>
<div id="navbar-mount"></div>
ddiff --git a/apps/gateway/public/dev.html b/apps/gateway/public/dev.html
iindex bf76d65..cd06373 100644
---- a/apps/gateway/public/dev.html
++++ b/apps/gateway/public/dev.html
@@@ -15,8 +15,6 @@
<meta property="og:type" content="website">
<meta property="og:url" content="https://dev.openvibe.tools">
<link rel="canonical" href="https://dev.openvibe.tools">
--<link rel="preconnect" href="https://fonts.googleapis.com">
--<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -24,12 +22,11 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
--<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root{--bg:var(--bg-primary,#0b0c12);--bg2:var(--bg-secondary,#12131c);--bg3:var(--bg-tertiary,#1a1b28);--card:var(--bg-card,rgba(22,24,36,.85));--tx:var(--text-primary,#f1f4fb);--tx2:var(--text-secondary,#a0a8c4);--tx3:var(--text-muted,#5e6580);--ac:var(--accent,#a78bfa);--acl:var(--accent-light,#c4b5fd);--brd:var(--border,rgba(255,255,255,.07));--r:16px}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{background:var(--bg)}
--body{min-height:100vh;font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--tx);-webkit-font-smoothing:antialiased}
++body{min-height:100vh;font-family:Inter, system-ui, -apple-system, 'Segoe UI', sans-serif;background:var(--bg);color:var(--tx);-webkit-font-smoothing:antialiased}
a{color:inherit;text-decoration:none}
.bg-glow{position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.bg-glow::before,.bg-glow::after{content:'';position:absolute;border-radius:50%;filter:blur(120px);opacity:.35;animation:gd 20s var(--ov-ease-in-out, ease-in-out) infinite alternate}
@@@ -45,13 +42,13 @@ a{color:inherit;text-decoration:none}
.mb{padding:7px 15px;border-radius:8px;background:var(--bg3);color:var(--tx2);font-size:.82rem;font-weight:600;cursor:pointer;border:1px solid var(--brd);transition:var(--ov-dur-2, .15s)}
.mb:hover{background:#252738;color:var(--tx)}.mb.on{background:var(--ac);color:#fff;border-color:var(--ac)}
.io{display:flex;gap:12px;margin-bottom:12px}
--.io textarea{flex:1;min-height:200px;padding:14px;background:var(--bg2);color:var(--tx);border:1px solid var(--brd);border-radius:var(--r);font-family:'JetBrains Mono',monospace;font-size:.85rem;resize:vertical;outline:none;transition:border var(--ov-dur-2, .2s)}
++.io textarea{flex:1;min-height:200px;padding:14px;background:var(--bg2);color:var(--tx);border:1px solid var(--brd);border-radius:var(--r);font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;font-size:.85rem;resize:vertical;outline:none;transition:border var(--ov-dur-2, .2s)}
.io textarea:focus{border-color:var(--ac)}.io textarea::placeholder{color:var(--tx3)}
.acts{display:flex;gap:8px;margin-bottom:14px;flex-wrap:wrap}
.ab{padding:8px 18px;border-radius:10px;font-size:.82rem;font-weight:600;cursor:pointer;transition:var(--ov-dur-2, .15s);border:none}
.ab-go{background:var(--ac);color:#fff}.ab-go:hover{filter:brightness(1.15)}
.ab-s{background:var(--bg3);color:var(--tx2);border:1px solid var(--brd)}.ab-s:hover{background:#252738;color:var(--tx)}
--#out{min-height:100px;padding:16px;background:var(--bg2);border:1px solid var(--brd);border-radius:var(--r);font-family:'JetBrains Mono',monospace;font-size:.85rem;white-space:pre-wrap;word-break:break-all;overflow-x:auto;line-height:1.6}
++#out{min-height:100px;padding:16px;background:var(--bg2);border:1px solid var(--brd);border-radius:var(--r);font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;font-size:.85rem;white-space:pre-wrap;word-break:break-all;overflow-x:auto;line-height:1.6}
#out:empty::before{content:'Output will appear here...';color:var(--tx3)}
#out .err{color:var(--danger-text, #f87171)}#out .ok{color:var(--success-text, #34d399)}
.dl{display:flex;font-size:.82rem;line-height:1.7}.dl .ln{width:34px;text-align:right;padding:0 6px;color:var(--tx3);user-select:none;flex-shrink:0}
@@@ -393,7 +390,7 @@ P.csv=(s,m)=>{
return{html:'<div style="overflow-x:auto"><table style="border-collapse:collapse;width:100%;font-size:.82rem">'+rows.map((r,ri)=>'<tr>'+r.map(c=>'<'+(ri?'td':'th')+' style="border:1px solid var(--brd);padding:6px 10px;text-align:left;'+(ri?'':'background:var(--bg3);font-weight:700')+'">'+H(c)+'</'+(ri?'td':'th')+'>').join('')+'</tr>').join('')+'</table></div>'};
};
P.sql=(s,m)=>DevEngine.sql(s,m);
--P.md=(s,m)=>{const h=DevEngine.md(s);if(m==='To HTML')return h;return{html:'<div style="line-height:1.8;font-family:Inter,sans-serif;font-size:.92rem">'+h+'</div>'}};
++P.md=(s,m)=>{const h=DevEngine.md(s);if(m==='To HTML')return h;return{html:'<div style="line-height:1.8;font-family:Inter,system-ui,-apple-system,Segoe UI,sans-serif;font-size:.92rem">'+h+'</div>'}};
P.html=(s,m)=>{
if(m==='Unescape'){const d=document.createElement('div');d.innerHTML=s;return d.textContent}
return DevEngine.html(s,m);
ddiff --git a/apps/gateway/public/img.html b/apps/gateway/public/img.html
iindex f09e959..6d5fad0 100644
---- a/apps/gateway/public/img.html
++++ b/apps/gateway/public/img.html
@@@ -15,8 +15,6 @@
<meta property="og:type" content="website">
<meta property="og:url" content="https://img.openvibe.tools">
<link rel="canonical" href="https://img.openvibe.tools">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -24,7 +22,6 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<script src="/shared/theme-loader.js" defer></script>
<style>
:root {
@@@ -48,7 +45,7 @@
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { background: var(--bg-primary); scroll-behavior: smooth; }
-- body { min-height: 100vh; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; background: var(--bg-primary); color: var(--text-primary); line-height: 1.6; }
++ body { min-height: 100vh; font-family: Inter, system-ui, -apple-system, 'Segoe UI', sans-serif; background: var(--bg-primary); color: var(--text-primary); line-height: 1.6; }
.container { max-width: 1200px; margin: 0 auto; padding: 20px; }
ddiff --git a/apps/gateway/public/net.html b/apps/gateway/public/net.html
iindex 3b1a224..bf4cb69 100644
---- a/apps/gateway/public/net.html
++++ b/apps/gateway/public/net.html
@@@ -15,8 +15,6 @@
<meta property="og:type" content="website">
<meta property="og:url" content="https://net.openvibe.tools">
<link rel="canonical" href="https://net.openvibe.tools">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -24,7 +22,6 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--bg-primary: #0b0c12;
@@@ -49,14 +46,14 @@
--shadow: 0 24px 64px rgba(0,0,0,0.4);
--radius: 16px;
--radius-sm: 10px;
-- --mono: 'JetBrains Mono', 'Fira Code', monospace;
++ --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { background: var(--bg-primary); scroll-behavior: smooth; }
body {
min-height: 100vh;
-- font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
++ font-family: Inter, system-ui, -apple-system, 'Segoe UI', sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
overflow-x: hidden;
@@@ -103,7 +100,7 @@
font-size: 15px; color: var(--text-primary); padding: 10px 0;
font-family: var(--mono);
}
-- .search-bar input::placeholder { color: var(--text-muted); font-family: 'Inter', sans-serif; }
++ .search-bar input::placeholder { color: var(--text-muted); font-family: Inter, system-ui, -apple-system, 'Segoe UI', sans-serif; }
.search-bar .btn-go {
padding: 10px 24px; border-radius: 10px; font-weight: 600; font-size: 14px;
background: var(--cyan); color: #fff; white-space: nowrap;
ddiff --git a/apps/gateway/server/index.js b/apps/gateway/server/index.js
iindex c538686..20acdb0 100644
---- a/apps/gateway/server/index.js
++++ b/apps/gateway/server/index.js
@@@ -151,9 +151,9 @@ app.use(helmet({
directives: {
defaultSrc: ["'self'"],
// 'wasm-unsafe-eval': the barcode scanner's ZXing reader is WebAssembly (codes.html), served from /vendor here.
-- scriptSrc: ["'self'", "'unsafe-inline'", "'wasm-unsafe-eval'", "https://openvibe.network", "https://openvibe.live", "cdn.jsdelivr.net", "fonts.googleapis.com", "https://static.cloudflareinsights.com"],
-- styleSrc: ["'self'", "'unsafe-inline'", "fonts.googleapis.com", "fonts.gstatic.com"],
-- fontSrc: ["'self'", "fonts.gstatic.com"],
++ scriptSrc: ["'self'", "'unsafe-inline'", "'wasm-unsafe-eval'", "https://openvibe.network", "https://openvibe.live", "cdn.jsdelivr.net", "https://static.cloudflareinsights.com"],
++ styleSrc: ["'self'", "'unsafe-inline'"],
++ fontSrc: ["'self'"],
imgSrc: ["'self'", "data:", "blob:", "https://openvibe.network", "https://openvibe.media"],
// openvibe.events: release notifications (release-watch's EventSource, openvibe-shared 2.13.0).
// The navbar's Messages count asks Chat (openvibe-shared 4.130.0).
ddiff --git a/apps/img/public/css/style.css b/apps/img/public/css/style.css
iindex ff6963d..d2e2774 100644
---- a/apps/img/public/css/style.css
++++ b/apps/img/public/css/style.css
@@@ -30,7 +30,7 @@
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
-- font-family: 'Outfit', -apple-system, BlinkMacSystemFont, sans-serif;
++ font-family: Inter, system-ui, -apple-system, 'Segoe UI', sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
min-height: 100vh;
@@@ -208,7 +208,7 @@ body {
border-radius: var(--radius-sm);
background: var(--bg-tertiary);
color: var(--text-secondary);
-- font-family: 'JetBrains Mono', monospace;
++ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 13px;
font-weight: 500;
cursor: pointer;
@@@ -259,7 +259,7 @@ body {
border-radius: var(--radius-sm);
background: var(--bg-tertiary);
color: var(--text-primary);
-- font-family: 'JetBrains Mono', monospace;
++ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 14px;
width: 120px;
outline: none;
ddiff --git a/apps/img/public/index.html b/apps/img/public/index.html
iindex a8925b4..7b7138d 100644
---- a/apps/img/public/index.html
++++ b/apps/img/public/index.html
@@@ -10,8 +10,6 @@
<meta name="description" content="Convert, compress, resize, and crop images online. Supports PNG, JPG, WebP, AVIF, HEIC, SVG, GIF, ICO, TIFF, BMP and more.">
<meta property="og:title" content="Img.OpenVibe — Online Image Converter & Tools">
<meta property="og:type" content="website">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -19,7 +17,6 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
ddiff --git a/apps/img/server/index.js b/apps/img/server/index.js
iindex 366be15..a85b303 100644
---- a/apps/img/server/index.js
++++ b/apps/img/server/index.js
@@@ -101,9 +101,9 @@ app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
-- scriptSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://openvibe.network", "https://static.cloudflareinsights.com"],
-- styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://fonts.gstatic.com"],
-- fontSrc: ["'self'", "https://fonts.gstatic.com"],
++ scriptSrc: ["'self'", "'unsafe-inline'", "https://openvibe.network", "https://static.cloudflareinsights.com"],
++ styleSrc: ["'self'", "'unsafe-inline'"],
++ fontSrc: ["'self'"],
imgSrc: ["'self'", "data:", "blob:", jobsRuntime.mediaOrigin()], // job result previews are 302s to Media
// openvibe.events: release notifications (release-watch's EventSource, openvibe-shared 2.13.0).
// The navbar's Messages count asks Chat (openvibe-shared 4.130.0).
ddiff --git a/apps/maps/public/css/style.css b/apps/maps/public/css/style.css
iindex 95a7ccd..10c595a 100644
---- a/apps/maps/public/css/style.css
++++ b/apps/maps/public/css/style.css
@@@ -21,8 +21,8 @@
--cyan: #06b6d4;
--orange: #fb923c;
--pink: #ec4899;
-- --font: 'Outfit', -apple-system, BlinkMacSystemFont, sans-serif;
-- --font-mono: 'JetBrains Mono', monospace;
++ --font: Inter, system-ui, -apple-system, 'Segoe UI', sans-serif;
++ --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--radius: 10px;
--shadow: 0 4px 24px rgba(0,0,0,0.4);
}
ddiff --git a/apps/maps/public/index.html b/apps/maps/public/index.html
iindex 9f86996..d65ed8e 100644
---- a/apps/maps/public/index.html
++++ b/apps/maps/public/index.html
@@@ -24,8 +24,6 @@
<meta name="twitter:image" content="https://maps.openvibe.tools/og.png">
<!-- Fonts -->
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -33,7 +31,6 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;600&family=Outfit:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<!-- Font Awesome 6 -->
ddiff --git a/apps/maps/public/js/app.js b/apps/maps/public/js/app.js
iindex 9e56ecf..0bbd4f3 100644
---- a/apps/maps/public/js/app.js
++++ b/apps/maps/public/js/app.js
@@@ -41,7 +41,7 @@
function toast(msg, type = 'info') {
const bg = { info: '#22c55e', success: '#059669', error: '#ef4444', warn: '#f59e0b', warning: '#f59e0b' };
Toastify({ text: msg, duration: 3500, gravity: 'bottom', position: 'right',
-- style: { background: bg[type] || bg.info, borderRadius: '10px', fontFamily: "'Outfit',sans-serif", fontSize: '12px', padding: '8px 16px' }
++ style: { background: bg[type] || bg.info, borderRadius: '10px', fontFamily: "Inter, system-ui, -apple-system, 'Segoe UI', sans-serif", fontSize: '12px', padding: '8px 16px' }
}).showToast();
}
@@@ -816,7 +816,7 @@
try {
state.charts.types = new Chart($('#stats-chart'), {
type:'doughnut', data:{ labels:labels.map(l=>l.charAt(0).toUpperCase()+l.slice(1)), datasets:[{data, backgroundColor:colors, borderWidth:0}] },
-- options:{responsive:true, plugins:{legend:{position:'bottom',labels:{color:'#94a3b8',font:{family:'Outfit',size:11}}}}}
++ options:{responsive:true, plugins:{legend:{position:'bottom',labels:{color:'#94a3b8',font:{family:'Inter, system-ui, -apple-system, Segoe UI, sans-serif',size:11}}}}}
});
} catch(e){}
@@@ -827,7 +827,7 @@
try {
state.charts.sources = new Chart($('#sources-chart'), {
type:'bar', data:{ labels:sl, datasets:[{label:'Locations',data:sd,backgroundColor:['#22c55e','#3b82f6','#f59e0b','#a855f7','#06b6d4','#ef4444','#ec4899','#64748b'],borderWidth:0,borderRadius:6}] },
-- options:{responsive:true, scales:{x:{ticks:{color:'#94a3b8',font:{family:'Outfit',size:9}},grid:{display:false}},y:{ticks:{color:'#94a3b8'},grid:{color:'rgba(34,197,94,0.06)'}}}, plugins:{legend:{display:false}}}
++ options:{responsive:true, scales:{x:{ticks:{color:'#94a3b8',font:{family:'Inter, system-ui, -apple-system, Segoe UI, sans-serif',size:9}},grid:{display:false}},y:{ticks:{color:'#94a3b8'},grid:{color:'rgba(34,197,94,0.06)'}}}, plugins:{legend:{display:false}}}
});
} catch(e){}
ddiff --git a/apps/maps/server/index.js b/apps/maps/server/index.js
iindex 54df888..7c7abf0 100644
---- a/apps/maps/server/index.js
++++ b/apps/maps/server/index.js
@@@ -79,12 +79,12 @@ app.use(helmet({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "unpkg.com", "cdn.jsdelivr.net", "static.cloudflareinsights.com", "https://openvibe.network"],
-- styleSrc: ["'self'", "'unsafe-inline'", "unpkg.com", "cdn.jsdelivr.net", "fonts.googleapis.com"],
++ styleSrc: ["'self'", "'unsafe-inline'", "unpkg.com", "cdn.jsdelivr.net"],
imgSrc: ["'self'", "data:", "blob:", "*.tile.openstreetmap.org", "*.basemaps.cartocdn.com", "server.arcgisonline.com", "image.tmdb.org", "*.wp.com", "https://openvibe.media"],
// openvibe.events: release notifications (release-watch's EventSource, openvibe-shared 2.13.0).
// The navbar's Messages count asks Chat (openvibe-shared 4.130.0).
connectSrc: ["'self'", "https://openvibe.chat", "nominatim.openstreetmap.org", "api.weather.gov", "api.open-meteo.com", "*.tile.openstreetmap.org", "*.basemaps.cartocdn.com", "server.arcgisonline.com", "https://openvibe.network", "https://openvibe.events", "https://cloudflareinsights.com"],
-- fontSrc: ["'self'", "fonts.gstatic.com", "cdn.jsdelivr.net"],
++ fontSrc: ["'self'", "cdn.jsdelivr.net"],
// The navbar checks Network's session in a hidden /sso/check frame on typed arrivals.
frameSrc: ["https://openvibe.network"],
scriptSrcAttr: ["'unsafe-inline'"],
ddiff --git a/apps/text/public/ascii.html b/apps/text/public/ascii.html
iindex 2a0ec62..2e6922d 100644
---- a/apps/text/public/ascii.html
++++ b/apps/text/public/ascii.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Convert text into large ASCII art banners using block characters. Perfect for code comments, READMEs, and chat.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
ddiff --git a/apps/text/public/badge.html b/apps/text/public/badge.html
iindex d75f9e4..b71a185 100644
---- a/apps/text/public/badge.html
++++ b/apps/text/public/badge.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Create badges, stickers, and labels with custom text, backgrounds, shapes, and colors. Download as PNG.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
<style>
ddiff --git a/apps/text/public/binary.html b/apps/text/public/binary.html
iindex 69c19f9..6ce4359 100644
---- a/apps/text/public/binary.html
++++ b/apps/text/public/binary.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Convert text to binary, hexadecimal, decimal, octal and back. See the raw bytes of your text.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
ddiff --git a/apps/text/public/bio.html b/apps/text/public/bio.html
iindex b6881b2..5745b5b 100644
---- a/apps/text/public/bio.html
++++ b/apps/text/public/bio.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Generate creative social media bios with fancy borders, symbols, and emoji. Perfect for Twitch, YouTube, Discord, Twitter.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
ddiff --git a/apps/text/public/braille.html b/apps/text/public/braille.html
iindex e933df2..15099da 100644
---- a/apps/text/public/braille.html
++++ b/apps/text/public/braille.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Convert text to Braille Unicode characters and back. Supports A–Z, 0–9, and common punctuation.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
ddiff --git a/apps/text/public/bubble.html b/apps/text/public/bubble.html
iindex 721e83c..09b0b7c 100644
---- a/apps/text/public/bubble.html
++++ b/apps/text/public/bubble.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Convert text into circled, negative circled, squared, negative squared, and regional indicator Unicode letters.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
ddiff --git a/apps/text/public/case.html b/apps/text/public/case.html
iindex cae28df..39cf8a0 100644
---- a/apps/text/public/case.html
++++ b/apps/text/public/case.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Convert text between UPPERCASE, lowercase, Title Case, camelCase, snake_case, kebab-case, PascalCase, sPoNgEbOb mocking case, and more.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
ddiff --git a/apps/text/public/clean.html b/apps/text/public/clean.html
iindex 686c2d0..c7076f6 100644
---- a/apps/text/public/clean.html
++++ b/apps/text/public/clean.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Clean up messy text: remove invisible characters, fix smart quotes, strip Zalgo diacritics, normalize spacing, remove HTML tags.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
ddiff --git a/apps/text/public/compare.html b/apps/text/public/compare.html
iindex e0e93e8..090c6f8 100644
---- a/apps/text/public/compare.html
++++ b/apps/text/public/compare.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Compare two texts side by side and see the differences highlighted. Paste your original and modified text to find what changed.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
<style>
ddiff --git a/apps/text/public/count.html b/apps/text/public/count.html
iindex deae433..f1eb01e 100644
---- a/apps/text/public/count.html
++++ b/apps/text/public/count.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Count characters, words, sentences, paragraphs, and bytes. Estimate reading time and speaking time.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
ddiff --git a/apps/text/public/escape.html b/apps/text/public/escape.html
iindex 64448e3..399c0af 100644
---- a/apps/text/public/escape.html
++++ b/apps/text/public/escape.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="HTML escape/unescape, URL encode/decode, JSON stringify/parse, Base64, and more.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
<style>
ddiff --git a/apps/text/public/fancy.html b/apps/text/public/fancy.html
iindex 35a2955..c290dfb 100644
---- a/apps/text/public/fancy.html
++++ b/apps/text/public/fancy.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Transform text into 30+ Unicode font styles: bold, italic, script, fraktur, double-struck, circled, squared, small caps, and more. Copy and paste anywhere.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
ddiff --git a/apps/text/public/ice.html b/apps/text/public/ice.html
iindex 0683187..b2c80ca 100644
---- a/apps/text/public/ice.html
++++ b/apps/text/public/ice.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="ICE raid alerts, know your rights resources, detention facility finder, emergency contacts, and preparedness tools for immigrant communities.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
<style>
ddiff --git a/apps/text/public/index.html b/apps/text/public/index.html
iindex 1195dbc..4644187 100644
---- a/apps/text/public/index.html
++++ b/apps/text/public/index.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Text tools: fancy Unicode fonts, Zalgo glitch text, ASCII art, symbols, kaomoji, case converters, bio generators, and more. Part of the OpenVibe Network.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
@@@ -186,7 +184,7 @@
<a href="https://wordmark.openvibe.tools" class="tool-card">
<div class="card-icon"><i class="fas fa-pen-nib"></i></div>
<h3>Wordmark Generator</h3>
-- <p>Design text-only logos with Google Fonts selections, colors, effects, and transparent backgrounds.</p>
++ <p>Design text-only logos with open font selections, colors, effects, and transparent backgrounds.</p>
<span class="card-domain">wordmark.openvibe.tools</span>
</a>
<a href="https://badge.openvibe.tools" class="tool-card">
ddiff --git a/apps/text/public/js/logo-engine.js b/apps/text/public/js/logo-engine.js
iindex 4cea45a..f8eb997 100644
---- a/apps/text/public/js/logo-engine.js
++++ b/apps/text/public/js/logo-engine.js
@@@ -1,7 +1,7 @@
// ═══════════════════════════════════════════════════════════════
// Logo.OpenVibe — Canvas-based Logo / Title / Badge Generation
// Text rendering with effects, transparent PNG/WebP export,
--// Google Fonts loading, preset templates. 100% client-side.
++// openvibe.design font loading, preset templates. 100% client-side.
// ═══════════════════════════════════════════════════════════════
(function (root) {
@@@ -152,7 +152,7 @@ const PRESETS = {
},
};
--// ── Google Fonts loader ──────────────────────────────────────
++// ── openvibe.design font loader ──────────────────────────────
const loadedFonts = new Set(['Arial', 'Arial Black', 'Courier New', 'Georgia', 'Impact', 'Times New Roman', 'Verdana']);
async function loadFont(fontName) {
@@@ -160,7 +160,7 @@ async function loadFont(fontName) {
try {
const link = document.createElement('link');
link.rel = 'stylesheet';
-- link.href = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(fontName)}:wght@400;700;900&display=swap`;
++ link.href = `https://openvibe.design/fonts/css2?family=${encodeURIComponent(fontName)}:wght@400;700;900&display=swap`;
document.head.appendChild(link);
await document.fonts.load(`48px "${fontName}"`);
loadedFonts.add(fontName);
@@@ -382,7 +382,7 @@ function download(canvas, filename = 'openvibe-logo.png', format = 'png') {
link.click();
}
--// ── Popular Google Fonts for logos ────────────────────────────
++// ── Popular open fonts for logos ─────────────────────────────
const POPULAR_FONTS = [
'Inter', 'Montserrat', 'Playfair Display', 'Oswald', 'Roboto', 'Lato',
'Raleway', 'Poppins', 'Merriweather', 'Source Sans 3', 'Nunito',
ddiff --git a/apps/text/public/json.html b/apps/text/public/json.html
iindex b88b849..116a67d 100644
---- a/apps/text/public/json.html
++++ b/apps/text/public/json.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Paste JSON to beautify, minify, and validate it instantly. Syntax highlighting, error detection, and tree view.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
<style>
ddiff --git a/apps/text/public/kaomoji.html b/apps/text/public/kaomoji.html
iindex fe3899e..edfcf31 100644
---- a/apps/text/public/kaomoji.html
++++ b/apps/text/public/kaomoji.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Browse and copy Japanese text emoticons (kaomoji) sorted by mood: happy, sad, angry, love, lenny, table flip, and more.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
ddiff --git a/apps/text/public/logo-hub.html b/apps/text/public/logo-hub.html
iindex 48c8810..140cd1b 100644
---- a/apps/text/public/logo-hub.html
++++ b/apps/text/public/logo-hub.html
@@@ -7,9 +7,7 @@
<link rel="icon" type="image/svg+xml" data-ov-icon="tools" 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%3Cmask%20id%3D%22ovh%22%20maskUnits%3D%22userSpaceOnUse%22%20x%3D%22-20%22%20y%3D%22-20%22%20width%3D%22140%22%20height%3D%22140%22%3E%3Crect%20x%3D%22-20%22%20y%3D%22-20%22%20width%3D%22140%22%20height%3D%22140%22%20fill%3D%22%23fff%22%2F%3E%3Cpath%20d%3D%22M70.59%2068.5L53.31%2031.5M70.59%2068.5L88.41%2031.5%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%2224%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fmask%3E%3ClinearGradient%20id%3D%22ovgo%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%223.09%22%20y1%3D%2223%22%20x2%3D%2257.09%22%20y2%3D%2277%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2387a6ff%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%234268d6%22%2F%3E%3C%2FlinearGradient%3E%3ClinearGradient%20id%3D%22ovgv%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%220%22%20y1%3D%2231.5%22%20x2%3D%220%22%20y2%3D%2268.5%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23ae8df9%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23784fd4%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2338bdf8%22%20stop-opacity%3D%22.3%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2338bdf8%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(0.58)%20translate(-50%20-50)%22%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2250%22%20rx%3D%2252%22%20ry%3D%2240%22%20fill%3D%22url(%23glow)%22%2F%3E%3Ccircle%20cx%3D%2230.09%22%20cy%3D%2250%22%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ovgo)%22%20stroke-width%3D%2218%22%20mask%3D%22url(%23ovh)%22%2F%3E%3Cpath%20d%3D%22M70.59%2068.5L53.31%2031.5M70.59%2068.5L88.41%2031.5%22%20fill%3D%22none%22%20stroke%3D%22url(%23ovgv)%22%20stroke-width%3D%2217%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
-- <meta name="description" content="Create logos, title cards, wordmarks, badges, stickers, watermarks, and thumbnails. Transparent PNG export, Google Fonts, presets. Browser-based.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
++ <meta name="description" content="Create logos, title cards, wordmarks, badges, stickers, watermarks, and thumbnails. Transparent PNG export, open fonts, presets. Browser-based.">
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
@@@ -27,7 +25,7 @@
<p class="hero-sub">Logo and design tools for streamers and creators. Title cards, wordmarks, badges, watermarks, thumbnails — all in your browser with transparent PNG export.</p>
<div class="hero-tags">
<span class="hero-tag">Transparent PNG</span>
-- <span class="hero-tag">Google Fonts</span>
++ <span class="hero-tag">Open Fonts</span>
<span class="hero-tag">No Watermarks</span>
<span class="hero-tag">Open source</span>
</div>
@@@ -65,7 +63,7 @@
<a href="https://wordmark.openvibe.tools" class="tool-card">
<div class="card-icon"><i class="fas fa-pen-nib"></i></div>
<h3>Wordmark Generator</h3>
-- <p>Design clean text-only logos. Pick from 40+ Google Fonts, adjust weight, spacing, colors. Perfect for branding.</p>
++ <p>Design clean text-only logos. Pick from 40+ open fonts, adjust weight, spacing, colors. Perfect for branding.</p>
<span class="card-domain">wordmark.openvibe.tools</span>
</a>
<a href="https://badge.openvibe.tools" class="tool-card">
ddiff --git a/apps/text/public/markdown.html b/apps/text/public/markdown.html
iindex 575d6bb..51ad436 100644
---- a/apps/text/public/markdown.html
++++ b/apps/text/public/markdown.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Write Markdown on the left, see rendered HTML on the right. Export as HTML.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
<style>
ddiff --git a/apps/text/public/morse.html b/apps/text/public/morse.html
iindex c4207b4..0ba603d 100644
---- a/apps/text/public/morse.html
++++ b/apps/text/public/morse.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Translate text to Morse code and back. Dots and dashes for your inner telegraph operator.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
ddiff --git a/apps/text/public/nickname.html b/apps/text/public/nickname.html
iindex 8b1cf77..c7417bd 100644
---- a/apps/text/public/nickname.html
++++ b/apps/text/public/nickname.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Generate unique gaming tags, usernames, and screen names in multiple styles.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
ddiff --git a/apps/text/public/peso.html b/apps/text/public/peso.html
iindex 3fd76d9..980a310 100644
---- a/apps/text/public/peso.html
++++ b/apps/text/public/peso.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Convert between Mexican Pesos (MXN) and US Dollars (USD) with live exchange rates. Fast, no signup.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
<style>
ddiff --git a/apps/text/public/reverse.html b/apps/text/public/reverse.html
iindex d125a59..9b46fd7 100644
---- a/apps/text/public/reverse.html
++++ b/apps/text/public/reverse.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Reverse text, flip it upside down, or mirror it. Full Unicode support with combining character awareness.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
ddiff --git a/apps/text/public/slang.html b/apps/text/public/slang.html
iindex 6c257b1..b80e457 100644
---- a/apps/text/public/slang.html
++++ b/apps/text/public/slang.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="The ultimate Mexican Spanish slang dictionary. Learn the real street Spanish — cabrón, chido, neta, güey and 100+ more with meanings, examples, and usage tips.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
<style>
ddiff --git a/apps/text/public/slug.html b/apps/text/public/slug.html
iindex a6de1ee..33108d6 100644
---- a/apps/text/public/slug.html
++++ b/apps/text/public/slug.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Convert titles into URL-friendly slugs. Strip diacritics, normalize whitespace, clean special characters.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
ddiff --git a/apps/text/public/sort.html b/apps/text/public/sort.html
iindex 45fbb81..1ac36bd 100644
---- a/apps/text/public/sort.html
++++ b/apps/text/public/sort.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Sort lines alphabetically, numerically, or randomly. Remove duplicates instantly.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
ddiff --git a/apps/text/public/spanish.html b/apps/text/public/spanish.html
iindex 6fafdec..0ac1f31 100644
---- a/apps/text/public/spanish.html
++++ b/apps/text/public/spanish.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Essential Spanish-English phrasebook with survival phrases, daily conversation, emergency expressions, and pronunciation tips.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
<style>
ddiff --git a/apps/text/public/symbols.html b/apps/text/public/symbols.html
iindex f57dba2..730301c 100644
---- a/apps/text/public/symbols.html
++++ b/apps/text/public/symbols.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Browse and click-to-copy arrows, stars, hearts, math symbols, currency, bullets, boxes, lines, and more special characters.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
</head>
ddiff --git a/apps/text/public/thumbnail.html b/apps/text/public/thumbnail.html
iindex 74ec29f..5c56eef 100644
---- a/apps/text/public/thumbnail.html
++++ b/apps/text/public/thumbnail.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Create stream and video thumbnails with bold text overlays. Upload a background, add text, download.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/shared/ov-upload.css">
<script src="/shared/theme-loader.js" defer></script>
ddiff --git a/apps/text/public/title.html b/apps/text/public/title.html
iindex c240890..4d640bd 100644
---- a/apps/text/public/title.html
++++ b/apps/text/public/title.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Generate stylish title cards with preset effects, custom fonts, colors, and transparent PNG export.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
<style>
ddiff --git a/apps/text/public/transparent.html b/apps/text/public/transparent.html
iindex 30937aa..cbcf59e 100644
---- a/apps/text/public/transparent.html
++++ b/apps/text/public/transparent.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Generate transparent text PNG images. Pick font, size, color — download with no background.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
<style>
ddiff --git a/apps/text/public/unicode.html b/apps/text/public/unicode.html
iindex 6288457..cbaef42 100644
---- a/apps/text/public/unicode.html
++++ b/apps/text/public/unicode.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Inspect text character by character: see Unicode code points, character names, byte counts. Detect invisible characters.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
<style>
ddiff --git a/apps/text/public/watermark.html b/apps/text/public/watermark.html
iindex 4faa3cd..21f040e 100644
---- a/apps/text/public/watermark.html
++++ b/apps/text/public/watermark.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Add text watermarks to images. Position, tile, adjust opacity and font. Download watermarked images.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/shared/ov-upload.css">
<script src="/shared/theme-loader.js" defer></script>
ddiff --git a/apps/text/public/wordmark.html b/apps/text/public/wordmark.html
iindex 702a4a9..78c5777 100644
---- a/apps/text/public/wordmark.html
++++ b/apps/text/public/wordmark.html
@@@ -7,9 +7,7 @@
<link rel="icon" type="image/svg+xml" data-ov-icon="tools" 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%3Cmask%20id%3D%22ovh%22%20maskUnits%3D%22userSpaceOnUse%22%20x%3D%22-20%22%20y%3D%22-20%22%20width%3D%22140%22%20height%3D%22140%22%3E%3Crect%20x%3D%22-20%22%20y%3D%22-20%22%20width%3D%22140%22%20height%3D%22140%22%20fill%3D%22%23fff%22%2F%3E%3Cpath%20d%3D%22M70.59%2068.5L53.31%2031.5M70.59%2068.5L88.41%2031.5%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%2224%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fmask%3E%3ClinearGradient%20id%3D%22ovgo%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%223.09%22%20y1%3D%2223%22%20x2%3D%2257.09%22%20y2%3D%2277%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2387a6ff%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%234268d6%22%2F%3E%3C%2FlinearGradient%3E%3ClinearGradient%20id%3D%22ovgv%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%220%22%20y1%3D%2231.5%22%20x2%3D%220%22%20y2%3D%2268.5%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23ae8df9%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23784fd4%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2338bdf8%22%20stop-opacity%3D%22.3%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2338bdf8%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(0.58)%20translate(-50%20-50)%22%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2250%22%20rx%3D%2252%22%20ry%3D%2240%22%20fill%3D%22url(%23glow)%22%2F%3E%3Ccircle%20cx%3D%2230.09%22%20cy%3D%2250%22%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ovgo)%22%20stroke-width%3D%2218%22%20mask%3D%22url(%23ovh)%22%2F%3E%3Cpath%20d%3D%22M70.59%2068.5L53.31%2031.5M70.59%2068.5L88.41%2031.5%22%20fill%3D%22none%22%20stroke%3D%22url(%23ovgv)%22%20stroke-width%3D%2217%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
-- <meta name="description" content="Create clean text-only wordmark logos with 46 Google Fonts, custom weight, spacing, and color.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
++ <meta name="description" content="Create clean text-only wordmark logos with 46 open fonts, custom weight, spacing, and color.">
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<script src="/shared/theme-loader.js" defer></script>
<style>
ddiff --git a/apps/text/public/zalgo.html b/apps/text/public/zalgo.html
iindex 99f4ec8..a819d07 100644
---- a/apps/text/public/zalgo.html
++++ b/apps/text/public/zalgo.html
@@@ -8,8 +8,6 @@
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
<meta name="description" content="Generate creepy Zalgo glitch text with adjustable intensity. Control above, middle, and below combining characters.">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -17,7 +15,7 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
++ <link href="https://openvibe.design/fonts/css2?family=Inter:wght@400;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/shared.css">
<link rel="stylesheet" href="/shared/kit.css">
<script src="/shared/theme-loader.js" defer></script>
ddiff --git a/apps/text/server/index.js b/apps/text/server/index.js
iindex 96e57dd..1583ed9 100644
---- a/apps/text/server/index.js
++++ b/apps/text/server/index.js
@@@ -64,9 +64,9 @@ app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
-- scriptSrc: ["'self'", "'unsafe-inline'", "cdn.jsdelivr.net", "fonts.googleapis.com", "https://openvibe.network", "https://static.cloudflareinsights.com"],
-- styleSrc: ["'self'", "'unsafe-inline'", "fonts.googleapis.com", "fonts.gstatic.com"],
-- fontSrc: ["'self'", "fonts.gstatic.com"],
++ scriptSrc: ["'self'", "'unsafe-inline'", "cdn.jsdelivr.net", "https://openvibe.network", "https://static.cloudflareinsights.com"],
++ styleSrc: ["'self'", "'unsafe-inline'", "https://openvibe.design"],
++ fontSrc: ["'self'", "https://openvibe.design"],
imgSrc: ["'self'", "data:", "blob:"],
// openvibe.events: release notifications (release-watch's EventSource, openvibe-shared 2.13.0).
// The navbar's Messages count asks Chat (openvibe-shared 4.130.0).
ddiff --git a/apps/yt/public/css/style.css b/apps/yt/public/css/style.css
iindex 828b9a8..ec075a9 100644
---- a/apps/yt/public/css/style.css
++++ b/apps/yt/public/css/style.css
@@@ -16,8 +16,8 @@
--radius-sm: 8px;
--radius-lg: 16px;
--shadow: 0 4px 24px rgba(0, 0, 0, 0.35);
-- --font: 'Outfit', sans-serif;
-- --font-mono: 'JetBrains Mono', monospace;
++ --font: Inter, system-ui, -apple-system, 'Segoe UI', sans-serif;
++ --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--max-w: 720px;
}
ddiff --git a/apps/yt/public/index.html b/apps/yt/public/index.html
iindex 6611163..d55be32 100644
---- a/apps/yt/public/index.html
++++ b/apps/yt/public/index.html
@@@ -7,8 +7,6 @@
<link rel="icon" type="image/svg+xml" data-ov-icon="tools" 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%3Cmask%20id%3D%22ovh%22%20maskUnits%3D%22userSpaceOnUse%22%20x%3D%22-20%22%20y%3D%22-20%22%20width%3D%22140%22%20height%3D%22140%22%3E%3Crect%20x%3D%22-20%22%20y%3D%22-20%22%20width%3D%22140%22%20height%3D%22140%22%20fill%3D%22%23fff%22%2F%3E%3Cpath%20d%3D%22M70.59%2068.5L53.31%2031.5M70.59%2068.5L88.41%2031.5%22%20fill%3D%22none%22%20stroke%3D%22%23000%22%20stroke-width%3D%2224%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fmask%3E%3ClinearGradient%20id%3D%22ovgo%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%223.09%22%20y1%3D%2223%22%20x2%3D%2257.09%22%20y2%3D%2277%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2387a6ff%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%234268d6%22%2F%3E%3C%2FlinearGradient%3E%3ClinearGradient%20id%3D%22ovgv%22%20gradientUnits%3D%22userSpaceOnUse%22%20x1%3D%220%22%20y1%3D%2231.5%22%20x2%3D%220%22%20y2%3D%2268.5%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23ae8df9%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23784fd4%22%2F%3E%3C%2FlinearGradient%3E%0A%3CradialGradient%20id%3D%22glow%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2338bdf8%22%20stop-opacity%3D%22.3%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%2338bdf8%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(0.58)%20translate(-50%20-50)%22%3E%3Cellipse%20cx%3D%2250%22%20cy%3D%2250%22%20rx%3D%2252%22%20ry%3D%2240%22%20fill%3D%22url(%23glow)%22%2F%3E%3Ccircle%20cx%3D%2230.09%22%20cy%3D%2250%22%20r%3D%2218%22%20fill%3D%22none%22%20stroke%3D%22url(%23ovgo)%22%20stroke-width%3D%2218%22%20mask%3D%22url(%23ovh)%22%2F%3E%3Cpath%20d%3D%22M70.59%2068.5L53.31%2031.5M70.59%2068.5L88.41%2031.5%22%20fill%3D%22none%22%20stroke%3D%22url(%23ovgv)%22%20stroke-width%3D%2217%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E">
<meta name="theme-color" content="#0d131d">
<meta name="color-scheme" content="dark light">
-- <link rel="preconnect" href="https://fonts.googleapis.com">
-- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Critical theme bootstrap (ov-theme-boot) — MUST stay inline and above the stylesheets.
Paints the cached theme synchronously so the default palette never flashes. Reads
localStorage only: no network, nothing to block on. theme-loader.js still runs after
@@@ -16,7 +14,6 @@
<script>
(function(){try{var raw=localStorage.getItem('ov_theme');if(!raw)return;var t=JSON.parse(raw),v=t&&t.variables;if(!v)return;var el=document.documentElement;for(var k in v)if(k.charAt(0)==='-')el.style.setProperty(k,v[k]);if(t.id)el.setAttribute('data-theme',t.id);}catch(_){}})();
</script>
-- <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/style.css">
<script src="/shared/ov-mark.js" defer></script>
</head>
ddiff --git a/apps/yt/server/index.js b/apps/yt/server/index.js
iindex d918bfa..a472bf6 100644
---- a/apps/yt/server/index.js
++++ b/apps/yt/server/index.js
@@@ -79,9 +79,9 @@ app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
-- scriptSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://openvibe.network", "https://static.cloudflareinsights.com"],
-- styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://fonts.gstatic.com"],
-- fontSrc: ["'self'", "https://fonts.gstatic.com"],
++ scriptSrc: ["'self'", "'unsafe-inline'", "https://openvibe.network", "https://static.cloudflareinsights.com"],
++ styleSrc: ["'self'", "'unsafe-inline'"],
++ fontSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https://i.ytimg.com", "https://img.youtube.com", "https://*.ggpht.com", "https://openvibe.network", "https://openvibe.media"],
// openvibe.events: release notifications (release-watch's EventSource, openvibe-shared 2.13.0).
connectSrc: ["'self'", "https://openvibe.network", "https://openvibe.chat", "https://openvibe.tools", "https://*.openvibe.tools", "https://cloudflareinsights.com", "https://openvibe.events"],
ddiff --git a/test/no-google-fonts.test.js b/test/no-google-fonts.test.js
nnew file mode 100644
iindex 0000000..a932fe5
---- /dev/null
++++ b/test/no-google-fonts.test.js
@@@ -0,0 +1,52 @@
++'use strict';
++// No Google Fonts (the owner, 2026-11-12: "make sure no remnants of font awesome or other libraries are still being
++// used rather than our openvibe.design systems"). Every app draws with the house stack — Inter/system-ui for text and
++// ui-monospace for code — and no page, stylesheet or script asks fonts.googleapis.com or fonts.gstatic.com for a face.
++// node test/no-google-fonts.test.js
++const assert = require('assert');
++const fs = require('fs');
++const path = require('path');
++const ROOT = path.join(__dirname, '..');
++
++const walk = (dir, ok, out = []) => {
++ if (!fs.existsSync(dir)) return out;
++ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
++ if (e.name === 'node_modules' || e.name === 'vendor' || e.name === 'data' || e.name.startsWith('.')) continue;
++ const p = path.join(dir, e.name);
++ if (e.isDirectory()) walk(p, ok, out); else if (ok.test(e.name)) out.push(p);
++ }
++ return out;
++};
++const rel = (p) => path.relative(ROOT, p);
++const GOOGLE_FONTS = /fonts\.(?:googleapis|gstatic)\.com/;
++
++const apps = path.join(ROOT, 'apps');
++const pages = walk(apps, /\.html$/);
++const styles = walk(apps, /\.css$/);
++const scripts = walk(apps, /\.js$/);
++
++for (const p of pages) {
++ assert.ok(!GOOGLE_FONTS.test(fs.readFileSync(p, 'utf8')), `${rel(p)} links Google Fonts`);
++}
++for (const p of styles) {
++ assert.ok(!GOOGLE_FONTS.test(fs.readFileSync(p, 'utf8')), `${rel(p)} imports Google Fonts`);
++}
++for (const p of scripts) {
++ assert.ok(!GOOGLE_FONTS.test(fs.readFileSync(p, 'utf8')), `${rel(p)} requests Google Fonts`);
++}
++
++// The logo maker keeps the 45 families it offered on main, now through openvibe.design's self-hosted CSS.
++const logo = fs.readFileSync(path.join(apps, 'text', 'public', 'js', 'logo-engine.js'), 'utf8');
++assert.match(logo, /https:\/\/openvibe\.design\/fonts\/css2\?family=/, 'the logo maker loads openvibe.design/fonts/css2');
++const list = logo.match(/const POPULAR_FONTS = \[([\s\S]*?)\];/);
++assert.ok(list, 'POPULAR_FONTS is a list');
++const families = [...list[1].matchAll(/'([^']+)'/g)].map((m) => m[1]);
++assert.strictEqual(families.length, 45, `the logo maker lists 45 families, not ${families.length}`);
++assert.match(logo, /font: 'Orbitron'/, 'a preset keeps its display face');
++
++// The text app's CSP lets the service in, and nothing of Google.
++const csp = fs.readFileSync(path.join(apps, 'text', 'server', 'index.js'), 'utf8');
++assert.match(csp, /styleSrc: \[[^\]]*"https:\/\/openvibe\.design"/, 'style-src allows openvibe.design');
++assert.match(csp, /fontSrc: \[[^\]]*"https:\/\/openvibe\.design"/, 'font-src allows openvibe.design');
++
++console.log(`no google fonts: ${pages.length} pages, ${styles.length} stylesheets, ${scripts.length} scripts clean; logo maker 45 families from openvibe.design`);