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

- -
-
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 @@