Bilingual (ES/EN) site for VanguardDevs, a boutique product studio founded by
Jesus O., positioned on fintech / insurtech / lending: MVPs, multi-tenant SaaS
and embedded infrastructure (widgets, SDKs, partner APIs) for clients in the
USA, UK and LATAM. Built with Next.js App Router. The original static design
lives in design-reference/. Copy voice is fully impersonal — no first person
("I" or "we") anywhere; VanguardDevs or the product is the subject.
Landing page (/es, /en): Hero → Case studies (#cases) → Process + packages
(#process) → Contact (#contact). Primary CTA is WhatsApp on /es; on /en
it switches to a booking link once BOOKING_URL is filled in. No forms.
components/Industries.tsx is built and compiles but is intentionally not
mounted; re-add it to app/[lang]/page.tsx (and its #industries link to
Header.tsx) to bring the section back.
Pending [PLACEHOLDER]s before launch (search the codebase for PLACEHOLDER):
the Calendly/booking URL (BOOKING_URL in lib/site.ts, which gates the English
primary CTA), two client testimonials, two package prices, the Akomo case body
and the lending industry line (lib/dictionaries.ts).
Also pending: the real GA4 measurement ID (NEXT_PUBLIC_GA_ID in .env.example),
the legal entity name and jurisdiction backing the privacy policy, a
dedicated DPO/privacy contact (may reuse CONTACT_EMAIL or need its own),
the GA4 and cookie-consent record retention periods, and the privacy
policy's effective/last-updated date — all in the draft copy under
lib/dictionaries.ts's privacy key (see docs/plans/analytics-cookie-consent.md).
npm install
npm run dev # http://localhost:3000
npm run build # production build (fully static pages)app/[lang]/— one statically generated page per locale (/es,/en). Spanish is the default/x-default locale per brand guidelines.middleware.ts— 307-redirects any unprefixed path to its locale version (/→/es,/privacy→/es/privacy) based onAccept-Language(Spanish wins on ambiguity)./es/*and/en/*pass through; the matcher skips Next internals and anything with a file extension, which is what keepsrobots.txt,sitemap.xmland/publicassets out of it.lib/dictionaries.ts— all copy for both languages, typed. Edit content here.app/globals.css— the brand system (Fog / Obsidian / Voltage Violet), built on hairline rules and generous whitespace, with contrast carried by type scale rather than heavy borders. All colour goes through semantic tokens; body text must clear 4.5:1 on both--paperand the--tinthover surface. The site has no dark mode.lib/fonts.ts— self-hosted fonts (Anton, Archivo, IBM Plex Mono) vianext/font— no external font requests, zero layout shift. Shared by the[lang]layout and the root 404 fallback (see below).- 404s: a single file,
app/global-not-found.tsx(Next'sexperimental.globalNotFound), renderingcomponents/NotFound.tsx(glitch/terminal-styled body, shown bilingually since a not-found page can't access the locale). It is the one place outside[lang]that renders its own<html>/<body>, hard-coded tolang="es"— this app has no rootapp/layout.tsx, and a plainapp/not-found.tsxwould demand one; see CLAUDE.md for why the alternatives cost more than they're worth.
- Server-rendered content per language at its own URL — no client-side language toggle, so crawlers index both versions.
hreflangalternates +x-defaultin both<head>andsitemap.xml.- Per-locale canonical URLs, titles, descriptions, Open Graph + Twitter cards.
- JSON-LD structured data:
ProfessionalService(with service offers and areaServed),WebSite,WebPage. sitemap.xmlandrobots.txtgenerated from code (app/sitemap.ts,app/robots.ts).- Per-locale Open Graph image generated at build (
app/[lang]/opengraph-image.tsx). - Titles and descriptions carry the positioning keywords (fintech, insurtech, multi-tenant SaaS, MVP).
- Static generation (SSG) for both pages → fast TTFB and Core Web Vitals; zero client-side JS required for any content.
- Deploy (Vercel is the zero-config path) and point
vanguarddevs.comat it. - Verify the domain in Google Search Console and submit
sitemap.xml. - Set up a Google Business Profile (helps for "desarrollo de software Venezuela" style local queries) and link it to the site.
- Keep publishing: the fastest ranking lever for a new domain is content —
consider a
/notesor case-study section fed by the Instagram "Field notes" pillar (P2 in the brand guide). Deferred by the owner for now. - Collect the two client testimonials — the render path is already wired, so
they only need pasting into the
testimonialfields inlib/dictionaries.ts.