diff --git a/website/public/favicon.svg b/website/public/favicon.svg
index ded5165..21ac0b2 100644
--- a/website/public/favicon.svg
+++ b/website/public/favicon.svg
@@ -1,4 +1,11 @@
diff --git a/website/scripts/generate-logos.mjs b/website/scripts/generate-logos.mjs
new file mode 100644
index 0000000..3da5933
--- /dev/null
+++ b/website/scripts/generate-logos.mjs
@@ -0,0 +1,157 @@
+/**
+ * Regenerate ShieldedShell logo SVGs (header, hero mark, favicon).
+ * Requires: npm install fontkit opentype.js (dev-only, run from repo root).
+ */
+import fs from 'node:fs';
+import https from 'node:https';
+import path from 'node:path';
+import { createRequire } from 'node:module';
+
+const require = createRequire(import.meta.url);
+const fontkit = require('fontkit');
+
+const root = path.resolve(import.meta.dirname, '..');
+const assetsDir = path.join(root, 'src', 'assets');
+const publicDir = path.join(root, 'public');
+
+const FONTS = {
+ jakarta:
+ 'https://fonts.gstatic.com/s/plusjakartasans/v12/LDIbaomQNQcsA88c7O9yZ4KMCoOg4IA6-91aHEjcWuA_d0nNSg.ttf',
+ mono: 'https://fonts.gstatic.com/s/jetbrainsmono/v24/tDbY2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8L6tjPQ.ttf',
+};
+
+function fetch(url) {
+ return new Promise((resolve, reject) => {
+ https
+ .get(url, (res) => {
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
+ fetch(res.headers.location).then(resolve).catch(reject);
+ return;
+ }
+ const chunks = [];
+ res.on('data', (chunk) => chunks.push(chunk));
+ res.on('end', () => resolve(Buffer.concat(chunks)));
+ })
+ .on('error', reject);
+ });
+}
+
+function pathFor(font, text, x, y, size) {
+ const run = font.layout(text);
+ const scale = size / font.unitsPerEm;
+ let cx = x;
+ const parts = [];
+ for (let i = 0; i < run.glyphs.length; i++) {
+ const glyph = run.glyphs[i];
+ const pos = run.positions[i];
+ if (glyph.path) {
+ const p = glyph.path
+ .scale(scale)
+ .translate(cx + (pos?.xOffset || 0) * scale, y + (pos?.yOffset || 0) * scale);
+ parts.push(p.toSVG());
+ }
+ cx += (pos?.xAdvance || glyph.advanceWidth) * scale;
+ }
+ return { d: parts.join(' '), width: cx - x };
+}
+
+function logomark(theme) {
+ const isLight = theme === 'light';
+ const outer = isLight ? '#0f766e' : '#14b8a6';
+ const outerMid = isLight ? '#115e59' : '#0d9488';
+ const inner = isLight ? '#ecfdf5' : '#042f2e';
+ const prompt = isLight ? '#ecfdf5' : '#ccfbf1';
+ const ring = isLight ? 'rgba(255,255,255,0.35)' : 'rgba(0,0,0,0.22)';
+
+ return `
+
+
+
+
+
+
+
+
+
+
+
+ `.trim();
+}
+
+function wordmark(theme, jakarta, mono) {
+ const isLight = theme === 'light';
+ const shieldedColor = isLight ? '#0f172a' : '#f8fafc';
+ const shellColor = isLight ? '#0f766e' : '#2dd4bf';
+ const shieldedSize = 15.5;
+ const shellSize = 14.5;
+ const x = 44;
+ const y = 23.5;
+ const shielded = pathFor(jakarta, 'Shielded', x, y, shieldedSize);
+ const shell = pathFor(mono, 'Shell', x + shielded.width + 2, y, shellSize);
+ const width = Math.ceil(x + shielded.width + 2 + shell.width + 4);
+
+ return {
+ width,
+ svg: `
+
+
+ `.trim(),
+ };
+}
+
+function horizontalLogo(theme, jakarta, mono) {
+ const mark = logomark(theme);
+ const text = wordmark(theme, jakarta, mono);
+ return `
+`;
+}
+
+function squareMark(theme) {
+ const mark = logomark(theme);
+ return `
+`;
+}
+
+function favicon() {
+ return `
+`;
+}
+
+const [jakartaBuf, monoBuf] = await Promise.all([
+ fetch(FONTS.jakarta),
+ fetch(FONTS.mono),
+]);
+const jakarta = fontkit.create(jakartaBuf);
+const mono = fontkit.create(monoBuf);
+
+fs.mkdirSync(assetsDir, { recursive: true });
+fs.mkdirSync(publicDir, { recursive: true });
+
+const files = [
+ [path.join(assetsDir, 'logo-light.svg'), horizontalLogo('light', jakarta, mono)],
+ [path.join(assetsDir, 'logo-dark.svg'), horizontalLogo('dark', jakarta, mono)],
+ [path.join(assetsDir, 'logo-mark-light.svg'), squareMark('light')],
+ [path.join(assetsDir, 'logo-mark-dark.svg'), squareMark('dark')],
+ [path.join(publicDir, 'favicon.svg'), favicon()],
+];
+
+for (const [file, content] of files) {
+ fs.writeFileSync(file, content.trim() + '\n');
+}
+
+console.log('Wrote', files.length, 'logo files');
diff --git a/website/src/assets/logo-dark.svg b/website/src/assets/logo-dark.svg
index 9f912aa..fdf95c8 100644
--- a/website/src/assets/logo-dark.svg
+++ b/website/src/assets/logo-dark.svg
@@ -1,5 +1,15 @@
-