diff --git a/logos/avatar-playground-2.af b/logos/avatar-playground-2.af
new file mode 100644
index 0000000..236df22
Binary files /dev/null and b/logos/avatar-playground-2.af differ
diff --git a/logos/avatar-playground.af b/logos/avatar-playground.af
new file mode 100644
index 0000000..cbb2cdf
Binary files /dev/null and b/logos/avatar-playground.af differ
diff --git a/logos/exports/mifi-avatar.png b/logos/exports/mifi-avatar.png
new file mode 100644
index 0000000..5c69bda
Binary files /dev/null and b/logos/exports/mifi-avatar.png differ
diff --git a/logos/exports/nifi-avatar-reverse.png b/logos/exports/nifi-avatar-reverse.png
new file mode 100644
index 0000000..53cbcab
Binary files /dev/null and b/logos/exports/nifi-avatar-reverse.png differ
diff --git a/package.json b/package.json
index 50955e2..e1244fc 100644
--- a/package.json
+++ b/package.json
@@ -3,11 +3,11 @@
"version": "2.0.0",
"private": true,
"repository": "https://git.mifi.dev/mifi-ventures/landing.git",
- "packageManager": "pnpm@10.30.0+sha512.2b5753de015d480eeb88f5b5b61e0051f05b4301808a82ec8b840c9d2adf7748eb352c83f5c1593ca703ff1017295bc3fdd3119abb9686efc96b9fcb18200937",
+ "packageManager": "pnpm@10.31.0+sha512.e3927388bfaa8078ceb79b748ffc1e8274e84d75163e67bc22e06c0d3aed43dd153151cbf11d7f8301ff4acb98c68bdc5cadf6989532801ffafe3b3e4a63c268",
"description": "mifi Ventures landing site — SvelteKit static build with critical CSS inlining",
"type": "module",
"scripts": {
- "build": "vite build && node scripts/critters.mjs && node scripts/minify-static-js.mjs && node scripts/copy-410-paths.mjs",
+ "build": "vite build && node scripts/critters.mjs && node scripts/generate-sitemap.mjs && node scripts/minify-static-js.mjs && node scripts/copy-410-paths.mjs",
"dev": "vite dev",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
diff --git a/scripts/410-paths.mjs b/scripts/410-paths.mjs
new file mode 100644
index 0000000..77fde06
--- /dev/null
+++ b/scripts/410-paths.mjs
@@ -0,0 +1,12 @@
+/**
+ * URL paths that return 410 Gone. Shared by copy-410-paths.mjs and generate-sitemap.mjs.
+ * Keep in sync with nginx.conf location blocks.
+ */
+export const PATHS = [
+ '2024/02/18/hello-world',
+ 'pt',
+ 'feed',
+ 'category/uncategorized/feed',
+ 'category/uncategorized',
+ 'comments/feed',
+];
diff --git a/scripts/copy-410-paths.mjs b/scripts/copy-410-paths.mjs
index c9daf95..288c3ec 100644
--- a/scripts/copy-410-paths.mjs
+++ b/scripts/copy-410-paths.mjs
@@ -9,19 +9,12 @@ import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
+import { PATHS } from './410-paths.mjs';
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const DIST = path.join(__dirname, '..', 'dist');
const SOURCE = path.join(DIST, '410.html');
-const PATHS = [
- '2024/02/18/hello-world',
- 'pt',
- 'feed',
- 'category/uncategorized/feed',
- 'category/uncategorized',
- 'comments/feed',
-];
-
function main() {
if (!fs.existsSync(SOURCE)) {
console.error('dist/410.html not found. Run build first.');
diff --git a/scripts/generate-sitemap.mjs b/scripts/generate-sitemap.mjs
new file mode 100644
index 0000000..55a84ab
--- /dev/null
+++ b/scripts/generate-sitemap.mjs
@@ -0,0 +1,99 @@
+#!/usr/bin/env node
+/**
+ * Post-build: generate sitemap.xml from prerendered pages in dist/.
+ * Scans for index.html (root and under each path), excludes 410 paths.
+ * Run after vite build and critters, before copy-410-paths so 410 dirs don't exist yet.
+ */
+
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+import { PATHS as PATHS_410 } from './410-paths.mjs';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const DIST = path.join(__dirname, '..', 'dist');
+
+/** Must match src/lib/seo.ts SEO_DEFAULTS.baseUrl */
+const BASE_URL = 'https://mifi.ventures';
+
+const EXCLUDE_FILES = new Set(['404.html', '410.html']);
+const excludeSet = new Set(PATHS_410);
+
+/**
+ * adapter-static emits path.html or path/index.html. Walk dist and collect
+ * every .html that represents a page (exclude 404/410 and 410-gone paths).
+ */
+function findPages(dir, basePath = '') {
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
+ const pages = [];
+
+ for (const e of entries) {
+ const rel = basePath ? `${basePath}/${e.name}` : e.name;
+
+ if (e.isDirectory()) {
+ const indexPath = path.join(dir, e.name, 'index.html');
+ if (fs.existsSync(indexPath)) {
+ if (!excludeSet.has(rel)) {
+ pages.push({ path: rel, indexPath });
+ }
+ } else {
+ pages.push(...findPages(path.join(dir, e.name), rel));
+ }
+ } else if (e.name.endsWith('.html') && !EXCLUDE_FILES.has(e.name)) {
+ const urlPath = e.name === 'index.html'
+ ? basePath
+ : (basePath ? `${basePath}/${e.name.slice(0, -5)}` : e.name.slice(0, -5));
+ if (!excludeSet.has(urlPath)) {
+ pages.push({ path: urlPath, indexPath: path.join(dir, e.name) });
+ }
+ }
+ }
+
+ return pages;
+}
+
+function escapeXml(s) {
+ return s
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+}
+
+function main() {
+ const indexHtml = path.join(DIST, 'index.html');
+ if (!fs.existsSync(indexHtml)) {
+ console.error('dist/index.html not found. Run build first.');
+ process.exit(1);
+ }
+
+ const pages = findPages(DIST).sort((a, b) => {
+ if (a.path === '') return -1;
+ if (b.path === '') return 1;
+ return a.path.localeCompare(b.path);
+ });
+ const urlElements = [];
+
+ for (const { path: pagePath, indexPath } of pages) {
+ const loc = pagePath ? `${BASE_URL}/${pagePath}` : BASE_URL;
+ const stat = fs.statSync(indexPath);
+ const lastmod = stat.mtime.toISOString().slice(0, 10);
+
+ urlElements.push(
+ ` \n ${escapeXml(loc)}\n ${lastmod}\n `
+ );
+ }
+
+ const xml = `
+
+${urlElements.join('\n')}
+
+`;
+
+ fs.writeFileSync(path.join(DIST, 'sitemap.xml'), xml, 'utf8');
+ console.log('✓ sitemap.xml generated with', pages.length, 'URLs.');
+}
+
+main();
diff --git a/src/app.css b/src/app.css
index e75a1c8..9837b25 100644
--- a/src/app.css
+++ b/src/app.css
@@ -323,11 +323,20 @@ a {
Layout Containers
======================================== */
+/* Avoid in-page anchor targets sitting under sticky nav */
+[id] {
+ scroll-margin-top: 6rem;
+}
+
.container {
width: 100%;
max-width: var(--max-width);
margin: 0 auto;
padding: 0 var(--space-md);
+
+ &.container--narrow {
+ max-width: var(--max-narrow-width);
+ }
}
.section {
diff --git a/src/lib/components/FAQ.svelte b/src/lib/components/FAQ.svelte
new file mode 100644
index 0000000..a4b29a5
--- /dev/null
+++ b/src/lib/components/FAQ.svelte
@@ -0,0 +1,51 @@
+
+
+
+
+
{title}
+
+ {#each faqList as { question, answer }, index (index)}
+ - {question}
+ - {answer}
+ {/each}
+
+
+
+
+
diff --git a/src/lib/components/Footer.svelte b/src/lib/components/Footer.svelte
index f6f2e97..982f383 100644
--- a/src/lib/components/Footer.svelte
+++ b/src/lib/components/Footer.svelte
@@ -7,9 +7,9 @@
@@ -56,6 +72,10 @@
max-width: 100%;
}
+ .footer-links--wrap {
+ flex-wrap: wrap;
+ }
+
.link {
display: flex;
align-items: center;
diff --git a/src/lib/components/Hero.svelte b/src/lib/components/Hero.svelte
index 346115f..0f64b76 100644
--- a/src/lib/components/Hero.svelte
+++ b/src/lib/components/Hero.svelte
@@ -1,41 +1,58 @@
@@ -46,26 +63,20 @@
text-align: center;
background-color: var(--color-bg);
border-bottom: 1px solid var(--color-border);
-
- @media (max-width: 768px) {
- padding: var(--space-xxl) 0 var(--space-xl) 0;
- }
-
- @media (max-width: 480px) {
- padding: var(--space-xl) 0 var(--space-lg) 0;
- }
}
- .headline {
+ .title {
margin-bottom: var(--space-lg);
font-family: var(--font-family-heading);
- font-size: var(--font-size-xl);
- font-weight: var(--font-weight-semibold);
- color: var(--color-text);
- letter-spacing: -0.02em;
+ font-size: var(--font-size-xxl);
+ font-weight: var(--font-weight-bold);
+ letter-spacing: -0.03em;
+ max-width: var(--max-text-width);
+ margin-left: auto;
+ margin-right: auto;
}
- .subhead {
+ .subtitle {
max-width: var(--max-narrow-width);
margin: 0 auto var(--space-xl) auto;
font-size: var(--font-size-large);
@@ -81,10 +92,12 @@
justify-content: center;
align-items: center;
margin-top: var(--space-lg);
+ }
- @media (max-width: 768px) {
+ @media (max-width: 768px) {
+ .cta-group {
flex-direction: column;
width: 100%;
}
}
-
+
\ No newline at end of file
diff --git a/src/lib/components/HowWeWork.svelte b/src/lib/components/HowWeWork.svelte
deleted file mode 100644
index b0b6114..0000000
--- a/src/lib/components/HowWeWork.svelte
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
How We Work
-
- {#each howWeWorkItems as item (item)}
- - {item}
- {/each}
-
-
-
diff --git a/src/lib/components/Navigation.svelte b/src/lib/components/Navigation.svelte
index bd0231b..5163690 100644
--- a/src/lib/components/Navigation.svelte
+++ b/src/lib/components/Navigation.svelte
@@ -1,5 +1,21 @@