From 7012f0fdd2a7e51ace081b1f0c86e817ddb3cb1b Mon Sep 17 00:00:00 2001 From: mifi Date: Tue, 10 Mar 2026 00:51:18 +0000 Subject: [PATCH] feature/services-pages (#7) Reviewed-on: https://git.mifi.dev/mifi-ventures/landing/pulls/7 Co-authored-by: mifi Co-committed-by: mifi --- .dockerignore | 8 + .husky/pre-commit | 1 + AGENTS.md | 18 +- README.md | 7 +- logos/avatar-playground-2.af | Bin 0 -> 23350 bytes logos/avatar-playground.af | Bin 0 -> 22552 bytes logos/exports/mifi-avatar.png | Bin 0 -> 14451 bytes logos/exports/nifi-avatar-reverse.png | Bin 0 -> 8549 bytes package.json | 32 +- playwright.config.ts | 7 +- pnpm-lock.yaml | 1427 +++++++---------- scripts/410-paths.mjs | 12 + scripts/beasties.mjs | 47 + scripts/copy-410-paths.mjs | 11 +- scripts/critters.mjs | 71 - scripts/generate-sitemap.mjs | 99 ++ scripts/minify-static-js.mjs | 2 +- scripts/run-e2e-in-docker.sh | 27 + scripts/run-e2e.sh | 19 + src/app.css | 55 + src/lib/components/Breadcrumbs.svelte | 61 + src/lib/components/EngagementsDl.svelte | 37 + src/lib/components/FAQ.svelte | 51 + src/lib/components/Footer.svelte | 24 +- src/lib/components/Hero.svelte | 91 +- src/lib/components/HowWeWork.svelte | 14 - src/lib/components/Navigation.svelte | 89 +- src/lib/components/ScheduleSection.svelte | 95 +- src/lib/components/ServiceSection.svelte | 82 + src/lib/components/ServicesCardGrid.svelte | 115 ++ src/lib/components/TOC.svelte | 58 + src/lib/components/WhatWeDo.svelte | 14 - src/lib/components/WhoGrid.svelte | 59 + .../{ => home}/EngagementsSection.svelte | 2 +- .../{ => home}/ExperienceSection.svelte | 2 +- src/lib/components/home/Hero.svelte | 101 ++ src/lib/components/home/HowWeWork.svelte | 14 + .../{ => home}/ImpactSection.svelte | 2 +- src/lib/components/home/WhatWeDo.svelte | 25 + src/lib/data/content.ts | 26 - src/lib/data/home-meta.ts | 9 - src/lib/data/home/content.ts | 27 + src/lib/data/{ => home}/engagements.ts | 0 src/lib/data/{ => home}/experience.ts | 0 src/lib/data/home/home-meta.ts | 9 + src/lib/data/{ => home}/json-ld.ts | 28 +- src/lib/data/home/navigation.ts | 22 + src/lib/data/home/services.ts | 26 + src/lib/data/privacy-policy.ts | 194 +++ .../content.ts | 187 +++ .../faq.ts | 36 + .../content.ts | 230 +++ .../faq.ts | 40 + src/lib/data/services/landing/content.ts | 103 ++ .../mvp-architecture-and-launch/content.ts | 195 +++ .../mvp-architecture-and-launch/faq.ts | 40 + .../stage-aligned-infrastructure/content.ts | 185 +++ .../stage-aligned-infrastructure/faq.ts | 36 + src/lib/data/terms-of-service.ts | 99 ++ src/lib/types/faq.ts | 6 + src/lib/types/service-page.ts | 132 ++ src/routes/+layout.svelte | 4 +- src/routes/+page.svelte | 30 +- src/routes/+page.ts | 2 +- src/routes/privacy-policy/+page.svelte | 171 ++ src/routes/privacy-policy/+page.ts | 28 + src/routes/services/+page.svelte | 44 + src/routes/services/+page.ts | 34 + .../+page.svelte | 37 + .../+page.ts | 50 + .../+page.svelte | 37 + .../+page.ts | 50 + .../mvp-architecture-and-launch/+page.svelte | 37 + .../mvp-architecture-and-launch/+page.ts | 50 + .../stage-aligned-infrastructure/+page.svelte | 37 + .../stage-aligned-infrastructure/+page.ts | 50 + src/routes/terms-of-service/+page.svelte | 139 ++ src/routes/terms-of-service/+page.ts | 28 + static/sitemap.xml | 9 - svelte.config.js | 4 + tests/visual.spec.ts | 58 + .../home-chromium-desktop-linux.png | Bin 0 -> 551011 bytes .../home-chromium-linux.png | Bin 591189 -> 0 bytes .../home-chromium-mobile-linux.png | Bin 0 -> 561251 bytes .../privacy-policy-chromium-desktop-linux.png | Bin 0 -> 561750 bytes .../privacy-policy-chromium-mobile-linux.png | Bin 0 -> 557209 bytes .../services-chromium-desktop-linux.png | Bin 0 -> 389900 bytes .../services-chromium-mobile-linux.png | Bin 0 -> 384393 bytes ...arly-stage-saas-chromium-desktop-linux.png | Bin 0 -> 808347 bytes ...early-stage-saas-chromium-mobile-linux.png | Bin 0 -> 813125 bytes ...ture-consultant-chromium-desktop-linux.png | Bin 0 -> 933118 bytes ...cture-consultant-chromium-mobile-linux.png | Bin 0 -> 940580 bytes ...ture-and-launch-chromium-desktop-linux.png | Bin 0 -> 703090 bytes ...cture-and-launch-chromium-mobile-linux.png | Bin 0 -> 688258 bytes ...-infrastructure-chromium-desktop-linux.png | Bin 0 -> 890873 bytes ...d-infrastructure-chromium-mobile-linux.png | Bin 0 -> 889397 bytes ...erms-of-service-chromium-desktop-linux.png | Bin 0 -> 359325 bytes ...terms-of-service-chromium-mobile-linux.png | Bin 0 -> 359520 bytes 98 files changed, 4119 insertions(+), 1189 deletions(-) create mode 100644 .dockerignore create mode 100644 .husky/pre-commit create mode 100644 logos/avatar-playground-2.af create mode 100644 logos/avatar-playground.af create mode 100644 logos/exports/mifi-avatar.png create mode 100644 logos/exports/nifi-avatar-reverse.png create mode 100644 scripts/410-paths.mjs create mode 100644 scripts/beasties.mjs delete mode 100644 scripts/critters.mjs create mode 100644 scripts/generate-sitemap.mjs create mode 100755 scripts/run-e2e-in-docker.sh create mode 100755 scripts/run-e2e.sh create mode 100644 src/lib/components/Breadcrumbs.svelte create mode 100644 src/lib/components/EngagementsDl.svelte create mode 100644 src/lib/components/FAQ.svelte delete mode 100644 src/lib/components/HowWeWork.svelte create mode 100644 src/lib/components/ServiceSection.svelte create mode 100644 src/lib/components/ServicesCardGrid.svelte create mode 100644 src/lib/components/TOC.svelte delete mode 100644 src/lib/components/WhatWeDo.svelte create mode 100644 src/lib/components/WhoGrid.svelte rename src/lib/components/{ => home}/EngagementsSection.svelte (96%) rename src/lib/components/{ => home}/ExperienceSection.svelte (99%) create mode 100644 src/lib/components/home/Hero.svelte create mode 100644 src/lib/components/home/HowWeWork.svelte rename src/lib/components/{ => home}/ImpactSection.svelte (86%) create mode 100644 src/lib/components/home/WhatWeDo.svelte delete mode 100644 src/lib/data/content.ts delete mode 100644 src/lib/data/home-meta.ts create mode 100644 src/lib/data/home/content.ts rename src/lib/data/{ => home}/engagements.ts (100%) rename src/lib/data/{ => home}/experience.ts (100%) create mode 100644 src/lib/data/home/home-meta.ts rename src/lib/data/{ => home}/json-ld.ts (79%) create mode 100644 src/lib/data/home/navigation.ts create mode 100644 src/lib/data/home/services.ts create mode 100644 src/lib/data/privacy-policy.ts create mode 100644 src/lib/data/services/fractional-cto-for-early-stage-saas/content.ts create mode 100644 src/lib/data/services/fractional-cto-for-early-stage-saas/faq.ts create mode 100644 src/lib/data/services/hands-on-saas-architecture-consultant/content.ts create mode 100644 src/lib/data/services/hands-on-saas-architecture-consultant/faq.ts create mode 100644 src/lib/data/services/landing/content.ts create mode 100644 src/lib/data/services/mvp-architecture-and-launch/content.ts create mode 100644 src/lib/data/services/mvp-architecture-and-launch/faq.ts create mode 100644 src/lib/data/services/stage-aligned-infrastructure/content.ts create mode 100644 src/lib/data/services/stage-aligned-infrastructure/faq.ts create mode 100644 src/lib/data/terms-of-service.ts create mode 100644 src/lib/types/faq.ts create mode 100644 src/lib/types/service-page.ts create mode 100644 src/routes/privacy-policy/+page.svelte create mode 100644 src/routes/privacy-policy/+page.ts create mode 100644 src/routes/services/+page.svelte create mode 100644 src/routes/services/+page.ts create mode 100644 src/routes/services/fractional-cto-for-early-stage-saas/+page.svelte create mode 100644 src/routes/services/fractional-cto-for-early-stage-saas/+page.ts create mode 100644 src/routes/services/hands-on-saas-architecture-consultant/+page.svelte create mode 100644 src/routes/services/hands-on-saas-architecture-consultant/+page.ts create mode 100644 src/routes/services/mvp-architecture-and-launch/+page.svelte create mode 100644 src/routes/services/mvp-architecture-and-launch/+page.ts create mode 100644 src/routes/services/stage-aligned-infrastructure/+page.svelte create mode 100644 src/routes/services/stage-aligned-infrastructure/+page.ts create mode 100644 src/routes/terms-of-service/+page.svelte create mode 100644 src/routes/terms-of-service/+page.ts delete mode 100644 static/sitemap.xml create mode 100644 tests/visual.spec.ts-snapshots/home-chromium-desktop-linux.png delete mode 100644 tests/visual.spec.ts-snapshots/home-chromium-linux.png create mode 100644 tests/visual.spec.ts-snapshots/home-chromium-mobile-linux.png create mode 100644 tests/visual.spec.ts-snapshots/privacy-policy-chromium-desktop-linux.png create mode 100644 tests/visual.spec.ts-snapshots/privacy-policy-chromium-mobile-linux.png create mode 100644 tests/visual.spec.ts-snapshots/services-chromium-desktop-linux.png create mode 100644 tests/visual.spec.ts-snapshots/services-chromium-mobile-linux.png create mode 100644 tests/visual.spec.ts-snapshots/services-fractional-cto-for-early-stage-saas-chromium-desktop-linux.png create mode 100644 tests/visual.spec.ts-snapshots/services-fractional-cto-for-early-stage-saas-chromium-mobile-linux.png create mode 100644 tests/visual.spec.ts-snapshots/services-hands-on-saas-architecture-consultant-chromium-desktop-linux.png create mode 100644 tests/visual.spec.ts-snapshots/services-hands-on-saas-architecture-consultant-chromium-mobile-linux.png create mode 100644 tests/visual.spec.ts-snapshots/services-mvp-architecture-and-launch-chromium-desktop-linux.png create mode 100644 tests/visual.spec.ts-snapshots/services-mvp-architecture-and-launch-chromium-mobile-linux.png create mode 100644 tests/visual.spec.ts-snapshots/services-stage-aligned-infrastructure-chromium-desktop-linux.png create mode 100644 tests/visual.spec.ts-snapshots/services-stage-aligned-infrastructure-chromium-mobile-linux.png create mode 100644 tests/visual.spec.ts-snapshots/terms-of-service-chromium-desktop-linux.png create mode 100644 tests/visual.spec.ts-snapshots/terms-of-service-chromium-mobile-linux.png diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..9b2a6a0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +# Preview-only 410 path artifacts (copy-410-paths.mjs). +# Deploy uses pnpm run build (no copy step); nginx serves 410 via error_page. +# These dirs exist only when running build-preview for local serve dist. +dist/pt +dist/feed +dist/2024 +dist/category +dist/comments diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..075dc6e --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +pnpm run test:ci diff --git a/AGENTS.md b/AGENTS.md index 3ea0d38..ea81e5d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,7 +10,7 @@ This file helps LLM agents work in this repo without introducing anti-patterns. ## Stack and architecture - **Framework**: SvelteKit with **adapter-static**. All routes are prerendered; there is no client-side router or hydration (`csr: false` in `src/routes/+layout.ts`). -- **Build**: `pnpm run build` = `vite build` → `node scripts/critters.mjs` → `node scripts/minify-static-js.mjs` → `node scripts/copy-410-paths.mjs`. Output is `dist/` (static files only). +- **Build**: `pnpm run build` = `vite build` → `node scripts/beasties.mjs` → `node scripts/generate-sitemap.mjs` → `node scripts/minify-static-js.mjs`. Output is `dist/` (static files only). Deploy uses this; no 410 path copies. For local preview with 410 URLs working, use `pnpm run build-preview` (adds `copy-410-paths.mjs`). The 410 path dirs are in `.dockerignore` so they are never included in the image. - **Runtime**: nginx serves `dist/` (mounted as `/usr/share/nginx/html` in the container). No Node at runtime. - **Theming**: CSS only. Light/dark follows **system preference** via `@media (prefers-color-scheme: dark)` in `src/app.css`. There is no JS theme toggle or `data-theme`; do not add one unless explicitly requested. - **Fonts**: **Local only.** Inter and Fraunces are served from `static/assets/fonts/` (e.g. `inter-v20-latin-*.woff2`, `fraunces-v38-latin-*.woff2`). Preloads are in `src/routes/+layout.svelte`. Do not add Google Fonts or other external font URLs for the main site or error pages. @@ -27,21 +27,21 @@ This file helps LLM agents work in this repo without introducing anti-patterns. | `src/lib/data/*.ts` | Content and meta (home-meta, content, experience, engagements, json-ld). Edit here for copy or SEO. | | `src/lib/seo.ts` | SEO defaults (baseUrl, theme colors, etc.) and `mergeMeta()`. | | `static/` | Copied as-is into `dist/` by SvelteKit. Favicon, robots.txt, fonts, images, **404.html**, **410.html**, and **assets/error-pages.css** live here. | -| `scripts/critters.mjs` | Post-build: inlines critical CSS into **every** `dist/*.html` (including 404 and 410). Resolves stylesheet URLs relative to `dist/`. | +| `scripts/beasties.mjs` | Post-build: inlines critical CSS into **every** `dist/*.html` (including 404 and 410). Resolves stylesheet URLs relative to `dist/`. | | `scripts/minify-static-js.mjs` | Post-build: minifies JS in `dist/assets/`. | -| `scripts/copy-410-paths.mjs` | Post-build: copies `410.html` to each 410 URL path as `index.html` so static preview (e.g. `serve dist`) shows the 410 page at those URLs; nginx still returns 410 via explicit location blocks. | +| `scripts/copy-410-paths.mjs` | Run by `build-preview` only: copies `410.html` to each 410 URL path as `index.html` so static preview (e.g. `serve dist`) shows the 410 page at those URLs. Production uses `build` (no copy); nginx returns 410 via explicit location blocks and `error_page 410 /410.html`. | | `nginx.conf` | Serves static files; `try_files $uri $uri/ /index.html` for SPA-style fallback; `error_page 404 /404.html` and `error_page 410 /410.html` for custom error pages. | ## Static error pages (404, 410) - **Files**: `static/404.html`, `static/410.html`. They are **standalone HTML** (not Svelte). Do not convert them to Svelte routes. - **Styling**: Both link to **one** shared stylesheet: ``. All error-page CSS lives in **`static/assets/error-pages.css`** (theme variables for light/dark, local `@font-face` for Inter/Fraunces, layout for `.error-page`). Do not duplicate theme tokens or add inline ` diff --git a/src/lib/components/EngagementsDl.svelte b/src/lib/components/EngagementsDl.svelte new file mode 100644 index 0000000..5810e8a --- /dev/null +++ b/src/lib/components/EngagementsDl.svelte @@ -0,0 +1,37 @@ + + +
+
+

{heading}

+ {#if intro} +

{intro}

+ {/if} +
+ {#each items as item} +
{item.term}
+
{item.definition}
+ {/each} +
+ {#if outro} +

{outro}

+ {/if} +
+
diff --git a/src/lib/components/FAQ.svelte b/src/lib/components/FAQ.svelte new file mode 100644 index 0000000..99e5432 --- /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..1238201 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..14b9b45 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,8 +92,10 @@ 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%; } 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..9561c63 100644 --- a/src/lib/components/Navigation.svelte +++ b/src/lib/components/Navigation.svelte @@ -1,5 +1,23 @@