feat: deep SEO optimization — metadata, OG tags, sitemap, structured data, GA4
All checks were successful
Build & Push / build-and-push (push) Successful in 1m28s

- Add metadataBase and localized generateMetadata to all pages (EN/FR)
- Add Open Graph and Twitter card defaults with branded OG image
- Add canonical URLs and hreflang alternates on every page
- Create robots.ts (allow all, block /admin/ and /api/)
- Create sitemap.ts with all routes x 2 locales and hreflang
- Add Organization, WebSite, and CreativeWork JSON-LD structured data
- Add GA4 (G-LD348WF8EM) with Consent Mode v2 (default denied for EEA)
- Add llms.txt for AI discoverability
- Add production nginx config for letsbe.biz

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-07 20:38:52 -04:00
parent 57faddc051
commit 518f86265e
15 changed files with 600 additions and 14 deletions

View File

@@ -24,3 +24,6 @@ NEXT_PUBLIC_CALCOM_URL=https://cal.letsbe.biz
# ── Site URL ──
NEXT_PUBLIC_SITE_URL=https://staging.letsbe.biz
# ── Google Analytics ──
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX

View File

@@ -0,0 +1,151 @@
# SEO Optimization — Design Spec
**Date:** 2026-04-07
**Domain:** letsbe.biz
**Stack:** Next.js 16 App Router, next-intl (en/fr, `as-needed` prefix), Payload CMS
## Current State
The site has a single `metadata` export in the layout with a generic title/description. No robots.txt, no sitemap, no OG tags, no hreflang, no structured data, no analytics. The services page has a static English-only metadata export. Case study pages and the homepage have no metadata at all.
## 1. Metadata Foundation
### metadataBase
Add `metadataBase: new URL('https://letsbe.biz')` to `src/app/(frontend)/[locale]/layout.tsx`. Required for OG images and canonical URLs to resolve as absolute URLs.
### i18n SEO keys
Add a `meta` section to both `en.json` and `fr.json`:
```json
{
"meta": {
"home": { "title": "...", "description": "..." },
"about": { "title": "...", "description": "..." },
"services": { "title": "...", "description": "..." },
"work": {
"monaco-ocean": { "title": "...", "description": "..." },
"port-nimara": { "title": "...", "description": "..." },
"port-amador": { "title": "...", "description": "..." }
}
}
}
```
### Per-page generateMetadata
Convert every page to use `generateMetadata({ params })`:
- Read locale from params
- Call `getTranslations({ locale, namespace: 'meta' })` for localized title/description
- Set `alternates.canonical` (e.g., `https://letsbe.biz/about` for en, `https://letsbe.biz/fr/about` for fr)
- Set `alternates.languages` for hreflang:
- `en` → unprefixed path
- `fr``/fr/` prefixed path
- `x-default` → unprefixed (English)
### Layout metadata
Convert the layout's static `metadata` to `generateMetadata()` so it can set locale-aware defaults, `metadataBase`, and default OG/Twitter config.
## 2. Social Sharing (Open Graph / Twitter)
### Default OG in layout
```ts
openGraph: {
type: 'website',
siteName: 'LetsBe.',
locale: locale === 'fr' ? 'fr_FR' : 'en_US',
images: [{ url: '/images/og-default.png', width: 1200, height: 630 }],
}
twitter: {
card: 'summary_large_image',
}
```
### OG image
Create a 1200x630 OG default image using sharp — the logo centered on a branded background (the site's surface color with the blue accent). Place at `public/images/og-default.png`.
### Per-page OG
Case study pages override with their hero image. Other pages use the default.
## 3. Crawl Infrastructure
### robots.ts
`src/app/robots.ts`:
- Allow all user agents
- Disallow `/admin/`, `/api/`
- Sitemap: `https://letsbe.biz/sitemap.xml`
### sitemap.ts
`src/app/sitemap.ts`:
- Static routes: `/`, `/about`, `/services`
- Dynamic routes: `/work/monaco-ocean`, `/work/port-nimara`, `/work/port-amador`
- Each entry includes both locale variants in `alternates.languages`
- `lastModified` set to build date
- `changeFrequency` and `priority` set appropriately
## 4. Structured Data (JSON-LD)
Inject via `<script type="application/ld+json">` in layout and page components.
### Organization (layout)
```json
{
"@type": "Organization",
"name": "LetsBe.",
"url": "https://letsbe.biz",
"logo": "https://letsbe.biz/images/letsbe-logo-short.png",
"sameAs": ["linkedin-url", "x-url"]
}
```
### WebSite (homepage)
```json
{
"@type": "WebSite",
"name": "LetsBe.",
"url": "https://letsbe.biz"
}
```
### Service (services page)
One `Service` entry per pillar (Design, Software, Infrastructure).
### CreativeWork (case study pages)
Per-project with title, description, image, creator → Organization.
## 5. Google Analytics + Consent Mode
### GA4 integration
- Measurement ID: `G-LD348WF8EM`
- Use Next.js `@next/third-parties/google` GoogleAnalytics component (or `next/script` if the package isn't available)
- Place in the layout, render only in production (`process.env.NODE_ENV === 'production'`)
### Consent Mode v2
Set default consent state before GA loads:
```js
gtag('consent', 'default', {
analytics_storage: 'denied',
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
wait_for_update: 500,
});
```
This lets GA4 run in cookieless mode (modeled conversions, basic pageviews) without requiring a cookie banner. A full consent banner can be added later to unlock full measurement.
### Environment variable
`NEXT_PUBLIC_GA_ID=G-LD348WF8EM` in `.env` / `.env.production`.
## Files to Create/Modify
| File | Action |
|------|--------|
| `src/app/(frontend)/[locale]/layout.tsx` | Convert to generateMetadata, add OG, add JSON-LD Organization |
| `src/app/(frontend)/[locale]/page.tsx` | Add generateMetadata, add JSON-LD WebSite |
| `src/app/(frontend)/[locale]/about/page.tsx` | Add generateMetadata |
| `src/app/(frontend)/[locale]/services/page.tsx` | Replace static metadata with generateMetadata, add JSON-LD Service |
| `src/app/(frontend)/[locale]/work/[slug]/page.tsx` | Add generateMetadata, add JSON-LD CreativeWork |
| `src/i18n/messages/en.json` | Add `meta` section |
| `src/i18n/messages/fr.json` | Add `meta` section |
| `src/app/robots.ts` | Create |
| `src/app/sitemap.ts` | Create |
| `public/images/og-default.png` | Create (generated via sharp) |
| `src/components/analytics/GoogleAnalytics.tsx` | Create (GA4 + consent mode) |
| `.env.production` | Add `NEXT_PUBLIC_GA_ID` |

60
nginx/letsbe.biz.conf Normal file
View File

@@ -0,0 +1,60 @@
# Redirect www → non-www
server {
listen 80;
listen [::]:80;
server_name www.letsbe.biz;
# Certbot will upgrade this to https after cert is issued
return 301 http://letsbe.biz$request_uri;
}
# Main site
server {
listen 80;
listen [::]:80;
server_name letsbe.biz;
# Certbot will add SSL config after:
# sudo certbot --nginx -d letsbe.biz -d www.letsbe.biz
location / {
proxy_pass http://127.0.0.1:6974;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_read_timeout 86400;
}
# Static assets — long cache
location /_next/static/ {
proxy_pass http://127.0.0.1:6974;
proxy_cache_valid 200 365d;
add_header Cache-Control "public, max-age=31536000, immutable";
}
# Public assets
location /images/ {
proxy_pass http://127.0.0.1:6974;
proxy_cache_valid 200 30d;
add_header Cache-Control "public, max-age=2592000";
}
# Payload media uploads
location /media/ {
proxy_pass http://127.0.0.1:6974;
proxy_cache_valid 200 30d;
add_header Cache-Control "public, max-age=2592000";
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
client_max_body_size 50M;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

31
public/llms.txt Normal file
View File

@@ -0,0 +1,31 @@
# LetsBe.
> Bespoke digital studio — custom web design, software, AI integration, and private infrastructure.
LetsBe. is a digital studio founded by Matt Ciaccio, serving clients worldwide from the Côte d'Azur, France. We design and build custom websites, purpose-built software, and manage private digital infrastructure — all under one roof.
## Services
- **Web Design & Development**: Custom websites and web applications designed from scratch, built for performance and SEO.
- **Software & Platforms**: CRMs, management tools, dashboards, booking systems, and API integrations built around your workflow.
- **Hosting & Infrastructure**: Dedicated servers, email, cloud storage, security, and monitoring — fully owned by the client.
- **AI Integration**: Intelligent features, automation, and data intelligence woven into websites and software.
## Selected Work
- [Monaco Ocean Protection Challenge](/work/monaco-ocean): AI-powered judging and analytics platform for a Mediterranean conservation event.
- [Port Nimara](/work/port-nimara): Custom website and CRM for marina lead management and operations.
- [Port Amador](/work/port-amador): Website and private digital infrastructure for a premium marina.
## Key Pages
- [Home](https://letsbe.biz)
- [Services](https://letsbe.biz/services)
- [About](https://letsbe.biz/about)
- [Start a Project](https://letsbe.biz/#configure)
## Contact
- Email: hello@letsbe.biz
- LinkedIn: https://www.linkedin.com/company/letsbe-digital
- X: https://x.com/letsbe_digital

View File

@@ -1,11 +1,40 @@
import Image from 'next/image';
import { setRequestLocale } from 'next-intl/server';
import type { Metadata } from 'next';
import { getTranslations, setRequestLocale } from 'next-intl/server';
import { Shield, PenTool, Users } from 'lucide-react';
import ScrollReveal from '@/components/ui/ScrollReveal';
import Button from '@/components/ui/Button';
import CornerBracket from '@/components/icons/CornerBracket';
import { routing } from '@/i18n/routing';
const BASE_URL = 'https://letsbe.biz';
// ─── Metadata ─────────────────────────────────────────────────────────────────
type PageProps = {
params: Promise<{ locale: string }>;
};
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'meta.about' });
const path = locale === 'en' ? '/about' : `/${locale}/about`;
return {
title: t('title'),
description: t('description'),
alternates: {
canonical: `${BASE_URL}${path}`,
languages: {
'en': `${BASE_URL}/about`,
'fr': `${BASE_URL}/fr/about`,
'x-default': `${BASE_URL}/about`,
},
},
};
}
// ─── Static Generation ────────────────────────────────────────────────────────
export function generateStaticParams() {

View File

@@ -1,28 +1,79 @@
import type { Metadata } from 'next'
import Script from 'next/script'
import { NextIntlClientProvider } from 'next-intl'
import { getMessages, setRequestLocale } from 'next-intl/server'
import { getMessages, getTranslations, setRequestLocale } from 'next-intl/server'
import { notFound } from 'next/navigation'
import { routing } from '@/i18n/routing'
import Nav from '@/components/layout/Nav'
import Footer from '@/components/layout/Footer'
import GoogleAnalytics from '@/components/analytics/GoogleAnalytics'
import '@/styles/globals.css'
export const metadata: Metadata = {
title: 'LetsBe. | Web Design, AI & Digital Infrastructure Studio',
description:
'Custom web design, purpose-built software, AI integration, and private infrastructure — designed, built, and managed by one dedicated team.',
}
const BASE_URL = 'https://letsbe.biz'
type Props = {
children: React.ReactNode
params: Promise<{ locale: string }>
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale } = await params
const t = await getTranslations({ locale, namespace: 'meta' })
return {
metadataBase: new URL(BASE_URL),
title: {
default: t('home.title'),
template: '%s',
},
description: t('home.description'),
openGraph: {
type: 'website',
siteName: t('siteName'),
locale: locale === 'fr' ? 'fr_FR' : 'en_US',
images: [{ url: '/images/og-default.png', width: 1200, height: 630 }],
},
twitter: {
card: 'summary_large_image',
},
robots: {
index: true,
follow: true,
},
alternates: {
canonical: BASE_URL,
languages: {
'en': BASE_URL,
'fr': `${BASE_URL}/fr`,
'x-default': BASE_URL,
},
},
}
}
export function generateStaticParams() {
return routing.locales.map((locale) => ({ locale }))
}
// ─── JSON-LD: Organization ───────────────────────────────────────────────────
const organizationJsonLd = {
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'LetsBe.',
url: BASE_URL,
logo: `${BASE_URL}/images/letsbe-logo-short.png`,
sameAs: [
'https://www.linkedin.com/company/letsbe-digital',
'https://x.com/letsbe_digital',
],
contactPoint: {
'@type': 'ContactPoint',
email: 'hello@letsbe.biz',
contactType: 'customer service',
},
}
export default async function LocaleLayout({ children, params }: Props) {
const { locale } = await params
@@ -38,6 +89,10 @@ export default async function LocaleLayout({ children, params }: Props) {
<head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationJsonLd) }}
/>
{process.env.NODE_ENV === 'development' && (
<Script
src="//unpkg.com/react-grab/dist/index.global.js"
@@ -47,6 +102,7 @@ export default async function LocaleLayout({ children, params }: Props) {
)}
</head>
<body className="font-sans text-on-surface bg-surface antialiased">
<GoogleAnalytics />
<NextIntlClientProvider messages={messages}>
<Nav />
{children}

View File

@@ -1,4 +1,5 @@
import { setRequestLocale } from 'next-intl/server'
import type { Metadata } from 'next'
import { getTranslations, setRequestLocale } from 'next-intl/server'
import Hero from '@/components/sections/Hero'
import TrustBar from '@/components/sections/TrustBar'
import ServicesOverview from '@/components/sections/ServicesOverview'
@@ -9,16 +10,49 @@ import Configurator from '@/components/sections/Configurator'
import Discovery from '@/components/sections/Discovery'
import CTABanner from '@/components/sections/CTABanner'
const BASE_URL = 'https://letsbe.biz'
type Props = {
params: Promise<{ locale: string }>
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale } = await params
const t = await getTranslations({ locale, namespace: 'meta.home' })
const path = locale === 'en' ? '' : `/${locale}`
return {
title: t('title'),
description: t('description'),
alternates: {
canonical: `${BASE_URL}${path}`,
languages: {
'en': BASE_URL,
'fr': `${BASE_URL}/fr`,
'x-default': BASE_URL,
},
},
}
}
const websiteJsonLd = {
'@context': 'https://schema.org',
'@type': 'WebSite',
name: 'LetsBe.',
url: BASE_URL,
}
export default async function HomePage({ params }: Props) {
const { locale } = await params
setRequestLocale(locale)
return (
<main>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(websiteJsonLd) }}
/>
<Hero />
<TrustBar />
<ServicesOverview />

View File

@@ -1,19 +1,39 @@
import { setRequestLocale } from 'next-intl/server';
import type { Metadata } from 'next';
import { getTranslations, setRequestLocale } from 'next-intl/server';
import ServicesHero from '@/components/sections/services/ServicesHero';
import ServicePillar from '@/components/sections/services/ServicePillar';
import AILayer from '@/components/sections/services/AILayer';
import ServicesCTA from '@/components/sections/services/ServicesCTA';
// Icon names passed as strings — resolved in client component
const BASE_URL = 'https://letsbe.biz';
// ─── Metadata ─────────────────────────────────────────────────────────────────
export const metadata: Metadata = {
title: 'Services | LetsBe. — Bespoke Digital Studio',
description:
'Custom web design, purpose-built software, AI automation, and private infrastructure — three pillars of digital excellence under one roof.',
type PageProps = {
params: Promise<{ locale: string }>;
};
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'meta.services' });
const path = locale === 'en' ? '/services' : `/${locale}/services`;
return {
title: t('title'),
description: t('description'),
alternates: {
canonical: `${BASE_URL}${path}`,
languages: {
'en': `${BASE_URL}/services`,
'fr': `${BASE_URL}/fr/services`,
'x-default': `${BASE_URL}/services`,
},
},
};
}
// ─── Service data ──────────────────────────────────────────────────────────────
export const SERVICE_PILLARS = [

View File

@@ -1,12 +1,15 @@
import Image from 'next/image';
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { setRequestLocale } from 'next-intl/server';
import { getTranslations, setRequestLocale } from 'next-intl/server';
import { routing } from '@/i18n/routing';
import ScrollReveal from '@/components/ui/ScrollReveal';
import Button from '@/components/ui/Button';
import CornerBracket from '@/components/icons/CornerBracket';
import Chip from '@/components/ui/Chip';
const BASE_URL = 'https://letsbe.biz';
// ─── Types ────────────────────────────────────────────────────────────────────
interface Project {
@@ -69,6 +72,38 @@ const PROJECTS: Record<string, Project> = {
},
};
// ─── Metadata ─────────────────────────────────────────────────────────────────
type PageProps = {
params: Promise<{ locale: string; slug: string }>;
};
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { locale, slug } = await params;
const project = PROJECTS[slug];
if (!project) return {};
const t = await getTranslations({ locale, namespace: 'meta.work' });
const path = locale === 'en' ? `/work/${slug}` : `/${locale}/work/${slug}`;
return {
title: t(`${slug}.title` as any),
description: t(`${slug}.description` as any),
openGraph: {
images: [{ url: project.image }],
},
alternates: {
canonical: `${BASE_URL}${path}`,
languages: {
'en': `${BASE_URL}/work/${slug}`,
'fr': `${BASE_URL}/fr/work/${slug}`,
'x-default': `${BASE_URL}/work/${slug}`,
},
},
};
}
// ─── Static Generation ────────────────────────────────────────────────────────
export function generateStaticParams() {
@@ -124,8 +159,25 @@ export default async function CaseStudyPage({ params }: Props) {
const project = PROJECTS[slug];
if (!project) notFound();
const caseStudyJsonLd = {
'@context': 'https://schema.org',
'@type': 'CreativeWork',
name: project.title,
description: project.description,
image: `${BASE_URL}${project.image}`,
creator: {
'@type': 'Organization',
name: 'LetsBe.',
url: BASE_URL,
},
};
return (
<main>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(caseStudyJsonLd) }}
/>
{/* ── Hero ── */}
<section className="relative min-h-[420px] md:min-h-[480px] flex items-end overflow-hidden">

14
src/app/robots.ts Normal file
View File

@@ -0,0 +1,14 @@
import type { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
return {
rules: [
{
userAgent: '*',
allow: '/',
disallow: ['/admin/', '/api/'],
},
],
sitemap: 'https://letsbe.biz/sitemap.xml',
}
}

38
src/app/sitemap.ts Normal file
View File

@@ -0,0 +1,38 @@
import type { MetadataRoute } from 'next'
const BASE_URL = 'https://letsbe.biz'
const STATIC_ROUTES = ['', '/about', '/services']
const PROJECT_SLUGS = ['monaco-ocean', 'port-nimara', 'port-amador']
export default function sitemap(): MetadataRoute.Sitemap {
const now = new Date()
const staticEntries: MetadataRoute.Sitemap = STATIC_ROUTES.map((route) => ({
url: `${BASE_URL}${route}`,
lastModified: now,
changeFrequency: route === '' ? 'weekly' : 'monthly',
priority: route === '' ? 1.0 : 0.8,
alternates: {
languages: {
en: `${BASE_URL}${route}`,
fr: `${BASE_URL}/fr${route}`,
},
},
}))
const projectEntries: MetadataRoute.Sitemap = PROJECT_SLUGS.map((slug) => ({
url: `${BASE_URL}/work/${slug}`,
lastModified: now,
changeFrequency: 'monthly' as const,
priority: 0.7,
alternates: {
languages: {
en: `${BASE_URL}/work/${slug}`,
fr: `${BASE_URL}/fr/work/${slug}`,
},
},
}))
return [...staticEntries, ...projectEntries]
}

View File

@@ -0,0 +1,40 @@
import Script from 'next/script'
const GA_ID = process.env.NEXT_PUBLIC_GA_ID
export default function GoogleAnalytics() {
if (!GA_ID || process.env.NODE_ENV !== 'production') return null
return (
<>
{/* Consent Mode v2 — default to denied for EEA compliance */}
<Script id="gtag-consent" strategy="beforeInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
analytics_storage: 'denied',
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
wait_for_update: 500,
});
`}
</Script>
{/* Google tag (gtag.js) */}
<Script
src={`https://www.googletagmanager.com/gtag/js?id=${GA_ID}`}
strategy="afterInteractive"
/>
<Script id="gtag-init" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GA_ID}');
`}
</Script>
</>
)
}

View File

@@ -1,4 +1,33 @@
{
"meta": {
"siteName": "LetsBe.",
"home": {
"title": "LetsBe. | Custom Web Design, Software & Digital Infrastructure",
"description": "Bespoke websites, purpose-built software, AI integration, and private infrastructure — designed, built, and managed by one dedicated team."
},
"about": {
"title": "About LetsBe. | Our Story & Approach",
"description": "An American-founded digital studio building custom websites, software, and platforms for businesses that care about quality."
},
"services": {
"title": "Services | LetsBe. — Web Design, Software & Infrastructure",
"description": "Custom web design, purpose-built software, AI automation, and private infrastructure — three pillars of digital excellence under one roof."
},
"work": {
"monaco-ocean": {
"title": "Monaco Ocean Protection Challenge | LetsBe.",
"description": "AI-powered judging and analytics platform for one of the Mediterranean's leading conservation events."
},
"port-nimara": {
"title": "Port Nimara — Maritime Digital Hub | LetsBe.",
"description": "Custom website and full CRM for lead management, berth assignment, and marina operations."
},
"port-amador": {
"title": "Port Amador — Premium Nautical Experience | LetsBe.",
"description": "Website and private digital infrastructure for a premium marina — cloud storage, email, and file management."
}
}
},
"nav": {
"services": "Services",
"configure": "Get Started",

View File

@@ -1,4 +1,33 @@
{
"meta": {
"siteName": "LetsBe.",
"home": {
"title": "LetsBe. | Design Web Sur Mesure, Logiciels & Infrastructure Digitale",
"description": "Sites web sur mesure, logiciels dédiés, intégration IA et infrastructure privée — conçus, développés et gérés par une seule équipe."
},
"about": {
"title": "À Propos de LetsBe. | Notre Histoire & Approche",
"description": "Un studio digital fondé aux États-Unis, créant des sites web, logiciels et plateformes sur mesure pour les entreprises exigeantes."
},
"services": {
"title": "Services | LetsBe. — Design Web, Logiciels & Infrastructure",
"description": "Design web sur mesure, logiciels dédiés, automatisation IA et infrastructure privée — trois piliers d'excellence digitale sous un même toit."
},
"work": {
"monaco-ocean": {
"title": "Monaco Ocean Protection Challenge | LetsBe.",
"description": "Plateforme de jugement et d'analyse propulsée par l'IA pour l'un des événements de conservation majeurs de la Méditerranée."
},
"port-nimara": {
"title": "Port Nimara — Hub Digital Maritime | LetsBe.",
"description": "Site web sur mesure et CRM complet pour la gestion des prospects, l'attribution des places et les opérations de la marina."
},
"port-amador": {
"title": "Port Amador — Expérience Nautique Premium | LetsBe.",
"description": "Site web et infrastructure digitale privée pour une marina premium — stockage cloud, email et gestion de fichiers."
}
}
},
"nav": {
"services": "Services",
"configure": "Démarrer",