polish: configurator visual redesign + copy cleanup
Some checks failed
Build & Push / build-and-push (push) Failing after 29s

- Redesigned progress bar: numbered circles with connectors, checkmarks on completion,
  line only fills up to current step, centered layout
- Redesigned service cards: horizontal 3-column grid, border-based selection state,
  check badges, staggered entrance animations
- Fixed card border clipping (ring → border)
- Fixed height stability (selection hint stays in DOM)
- Removed all em dashes, replaced with regular dashes
- Removed all Côte d'Azur / Riviera / France location references sitewide
- Philosophy quote attributed to "Matt Ciaccio, Founder"
- Footer: "American-founded. Serving clients worldwide."
- Email template: LetsBe Solutions LLC
- AI prompt: removed Côte d'Azur reference

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-26 18:44:37 +01:00
parent bbe5b6c67e
commit bdb664633d
7 changed files with 194 additions and 218 deletions

View File

@@ -84,7 +84,7 @@ async function generateBriefWithAI(body: ConfigureRequestBody): Promise<string>
const context = buildContext(body);
const displayName = body.name.split(' ')[0] || body.name;
const systemPrompt = `You are writing a project brief on behalf of LetsBe Solutions, a digital studio that builds custom websites, custom software, and private digital infrastructure. The company is American-founded and serves businesses on the Côte d'Azur and internationally.
const systemPrompt = `You are writing a project brief on behalf of LetsBe Solutions, a digital studio that builds custom websites, custom software, and private digital infrastructure. The company is American-founded and serves clients internationally.
Key facts about LetsBe:
- Every project is designed and coded from scratch — no templates, no page builders

View File

@@ -2,6 +2,7 @@
import { motion } from 'framer-motion';
import { cn } from '@/lib/utils';
import { Check } from 'lucide-react';
interface ProgressBarProps {
currentStep: 1 | 2 | 3;
@@ -9,31 +10,68 @@ interface ProgressBarProps {
className?: string;
}
const STEP_LABELS = ['01', '02', '03'];
export default function ProgressBar({ currentStep, className }: ProgressBarProps) {
return (
<div className={cn('flex gap-1.5', className)} role="progressbar" aria-valuenow={currentStep} aria-valuemin={1} aria-valuemax={3}>
{([1, 2, 3] as const).map((step) => {
const isActive = step <= currentStep;
return (
<div
key={step}
className="relative flex-1 h-1 rounded-full bg-outline-variant/40 overflow-hidden"
>
<motion.div
className="absolute inset-0 rounded-full bg-gradient-to-r from-primary-dark to-primary"
initial={{ scaleX: 0 }}
animate={{ scaleX: isActive ? 1 : 0 }}
transition={{
type: 'spring',
stiffness: 300,
damping: 30,
delay: isActive ? (step - 1) * 0.05 : 0,
}}
style={{ transformOrigin: 'left' }}
/>
</div>
);
})}
<div
className={cn('flex items-center justify-center', className)}
role="progressbar"
aria-valuenow={currentStep}
aria-valuemin={1}
aria-valuemax={3}
>
<div className="flex items-center gap-0 w-full max-w-xs">
{([1, 2, 3] as const).map((step) => {
const isComplete = step < currentStep;
const isActive = step === currentStep;
const isUpcoming = step > currentStep;
const showFilledLine = step < currentStep;
return (
<div key={step} className="flex items-center flex-1 last:flex-none">
{/* Step circle */}
<motion.div
className={cn(
'relative flex items-center justify-center w-7 h-7 rounded-full text-[11px] font-semibold flex-shrink-0 transition-colors duration-300',
isComplete && 'bg-primary text-white',
isActive && 'bg-primary-dark text-white shadow-[0_0_0_3px_rgba(91,164,217,0.15)]',
isUpcoming && 'bg-surface-low text-outline border border-outline-variant/40',
)}
>
{isComplete ? (
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: 'spring', stiffness: 400, damping: 20 }}
>
<Check size={13} strokeWidth={2.5} />
</motion.div>
) : (
<span>{STEP_LABELS[step - 1]}</span>
)}
</motion.div>
{/* Connector line — only between steps, fills only when step is complete */}
{step < 3 && (
<div className="flex-1 h-px mx-2 bg-outline-variant/25 relative overflow-hidden">
<motion.div
className="absolute inset-y-0 left-0 right-0 bg-primary"
initial={{ scaleX: 0 }}
animate={{ scaleX: showFilledLine ? 1 : 0 }}
transition={{
type: 'spring',
stiffness: 200,
damping: 25,
}}
style={{ transformOrigin: 'left' }}
/>
</div>
)}
</div>
);
})}
</div>
</div>
);
}

View File

@@ -27,7 +27,7 @@ const SERVICES: ServiceOption[] = [
const AI_TYPE_IDS = ['teammate', 'customer-facing', 'data-intelligence', 'notsure'] as const;
// ─── Service Card ────────────────────────────────────────────────────────────
// ─── Service Card (horizontal layout) ────────────────────────────────────────
function ServiceCard({
option,
@@ -35,12 +35,14 @@ function ServiceCard({
onToggle,
title,
description,
index,
}: {
option: ServiceOption;
selected: boolean;
onToggle: () => void;
title: string;
description: string;
index: number;
}) {
const Icon = option.icon;
@@ -48,130 +50,61 @@ function ServiceCard({
<motion.button
type="button"
onClick={onToggle}
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.06, duration: 0.35, ease: [0.16, 1, 0.3, 1] }}
whileTap={{ scale: 0.97 }}
className={cn(
'group relative w-full text-left rounded-2xl p-5 transition-all duration-200',
'group relative flex flex-col items-center text-center rounded-xl px-4 py-5 transition-all duration-200',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2',
selected
? 'bg-primary/6 border-2 border-primary/30 shadow-card'
: 'bg-surface-high border-2 border-transparent shadow-subtle hover:shadow-card hover:border-outline-variant/30',
? 'bg-primary/[0.06] border-2 border-primary/40 shadow-[0_4px_16px_rgba(0,100,148,0.1)]'
: 'bg-white border-2 border-outline-variant/20 hover:border-outline-variant/40 hover:shadow-[0_2px_12px_rgba(25,28,29,0.06)]',
)}
>
<div className="flex items-start gap-4">
{/* Icon + check badge */}
<div className="relative mb-2.5">
<div
className={cn(
'flex-shrink-0 w-11 h-11 rounded-xl flex items-center justify-center transition-colors duration-200',
selected
? 'bg-primary/15 text-primary-dark'
: 'bg-primary/8 text-primary group-hover:bg-primary/12',
'w-10 h-10 rounded-lg flex items-center justify-center transition-colors duration-200',
selected ? 'bg-primary/12' : 'bg-surface-low group-hover:bg-primary/6',
)}
>
<Icon size={20} strokeWidth={1.5} />
</div>
<div className="flex-1 min-w-0">
<p
<Icon
size={20}
strokeWidth={1.5}
className={cn(
'text-sm font-semibold leading-tight mb-1 transition-colors duration-200',
selected ? 'text-primary-dark' : 'text-on-surface',
'transition-colors duration-200',
selected ? 'text-primary-dark' : 'text-outline group-hover:text-primary',
)}
>
{title}
</p>
<p className="text-xs text-outline leading-relaxed">{description}</p>
</div>
<div className="flex-shrink-0 mt-0.5">
<motion.div
className={cn(
'w-5 h-5 rounded-full border-2 flex items-center justify-center',
selected ? 'border-primary bg-primary' : 'border-outline-variant bg-transparent',
)}
animate={
selected
? { scale: 1, borderColor: '#5BA4D9', backgroundColor: '#5BA4D9' }
: { scale: 1, borderColor: '#c2c7ce', backgroundColor: 'transparent' }
}
transition={springTransition}
>
<AnimatePresence>
{selected && (
<motion.div
initial={{ scale: 0, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0, opacity: 0 }}
transition={springTransition}
>
<Check size={11} strokeWidth={3} className="text-white" />
</motion.div>
)}
</AnimatePresence>
</motion.div>
</div>
</div>
</motion.button>
);
}
// ─── AI Toggle ───────────────────────────────────────────────────────────────
function AIToggle({
enabled,
onToggle,
label,
description,
}: {
enabled: boolean;
onToggle: () => void;
label: string;
description: string;
}) {
return (
<button
type="button"
onClick={onToggle}
className={cn(
'flex items-center gap-3 w-full text-left rounded-xl px-4 py-3',
'transition-all duration-200',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2',
enabled
? 'bg-primary/5 shadow-card'
: 'bg-surface-high shadow-subtle hover:shadow-card',
)}
>
<div className="flex-1 min-w-0">
<span
className={cn(
'text-sm font-semibold transition-colors duration-200 flex items-center gap-1.5',
enabled ? 'text-primary-dark' : 'text-on-surface',
)}
>
<Sparkles
size={14}
strokeWidth={1.75}
className={cn(
'flex-shrink-0 transition-colors duration-200',
enabled ? 'text-primary' : 'text-outline',
)}
aria-hidden="true"
/>
{label}
</span>
<p className="text-xs text-outline mt-0.5">{description}</p>
</div>
{/* Check badge — appears on selection */}
<AnimatePresence>
{selected && (
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
exit={{ scale: 0 }}
transition={springTransition}
className="absolute -top-1 -right-1 w-4 h-4 rounded-full bg-primary-dark flex items-center justify-center"
>
<Check size={10} strokeWidth={3} className="text-white" />
</motion.div>
)}
</AnimatePresence>
</div>
<div
<p
className={cn(
'flex-shrink-0 w-10 h-6 rounded-full relative transition-colors duration-300',
enabled ? 'bg-primary' : 'bg-outline-variant',
'text-sm font-semibold leading-tight mb-1 transition-colors duration-200',
selected ? 'text-primary-dark' : 'text-on-surface',
)}
>
<motion.div
className="absolute top-0.5 w-5 h-5 rounded-full bg-white shadow-sm"
animate={{ x: enabled ? 18 : 2 }}
transition={springTransition}
/>
</div>
</button>
{title}
</p>
<p className="text-[11px] text-outline leading-relaxed">{description}</p>
</motion.button>
);
}
@@ -209,19 +142,19 @@ export default function StepServices({ formData, setFormData, onNext }: StepProp
const canProceed = formData.services.length > 0;
return (
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-5">
<ProgressBar currentStep={1} totalSteps={3} />
<div>
<div className="text-center">
<h3 className="font-serif text-2xl font-semibold tracking-headline text-on-surface">
{t('step1.title')}
</h3>
<p className="mt-1 text-sm text-outline">{t('step1.subtitle')}</p>
</div>
{/* Service cards */}
<div className="flex flex-col gap-3">
{SERVICES.map((option) => (
{/* Service cards — horizontal grid */}
<div className="grid grid-cols-3 gap-3">
{SERVICES.map((option, index) => (
<ServiceCard
key={option.id}
option={option}
@@ -229,31 +162,49 @@ export default function StepServices({ formData, setFormData, onNext }: StepProp
onToggle={() => toggleService(option.id)}
title={t(option.titleKey)}
description={t(option.descriptionKey)}
index={index}
/>
))}
<AnimatePresence>
{formData.services.length === 0 && (
<motion.p
initial={{ opacity: 0, y: 4 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -4 }}
transition={{ duration: 0.2 }}
className="text-xs text-outline/60 text-center pt-1 select-none"
>
{t('selectService')}
</motion.p>
)}
</AnimatePresence>
</div>
{/* AI Toggle + Type Selection */}
<div className="flex flex-col gap-3">
<AIToggle
enabled={formData.aiEnabled}
onToggle={toggleAI}
label={t('aiToggle')}
description={t('aiDescription')}
/>
<p
className={cn(
'text-xs text-center select-none -mt-2 transition-opacity duration-200',
formData.services.length === 0 ? 'text-outline/50' : 'opacity-0',
)}
aria-hidden={formData.services.length > 0}
>
{t('selectService')}
</p>
{/* AI Enhancement */}
<div className="border-t border-dashed border-outline-variant/30 pt-4">
<button
type="button"
onClick={toggleAI}
className={cn(
'flex items-center gap-3 w-full text-left rounded-xl px-4 py-3',
'transition-all duration-200',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2',
formData.aiEnabled ? 'bg-primary/[0.04]' : 'hover:bg-surface-low',
)}
>
<div className={cn(
'flex-shrink-0 w-8 h-8 rounded-lg flex items-center justify-center transition-colors duration-200',
formData.aiEnabled ? 'bg-primary/12' : 'bg-surface-low',
)}>
<Sparkles size={16} strokeWidth={1.75} className={cn('transition-colors', formData.aiEnabled ? 'text-primary-dark' : 'text-outline')} />
</div>
<div className="flex-1 min-w-0">
<span className={cn('text-sm font-semibold transition-colors', formData.aiEnabled ? 'text-primary-dark' : 'text-on-surface')}>
{t('aiToggle')}
</span>
<p className="text-xs text-outline mt-0.5">{t('aiDescription')}</p>
</div>
<div className={cn('flex-shrink-0 w-11 h-6 rounded-full relative transition-colors duration-300', formData.aiEnabled ? 'bg-primary-dark' : 'bg-outline-variant/60')}>
<motion.div className="absolute top-[3px] w-[18px] h-[18px] rounded-full bg-white shadow-sm" animate={{ x: formData.aiEnabled ? 20 : 3 }} transition={springTransition} />
</div>
</button>
<AnimatePresence>
{formData.aiEnabled && (
@@ -264,29 +215,21 @@ export default function StepServices({ formData, setFormData, onNext }: StepProp
transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="overflow-hidden"
>
<div className="pt-1 flex flex-col gap-3">
<div className="pt-3 pb-1 flex flex-col gap-2.5">
<div className="flex flex-wrap gap-2">
{AI_TYPE_IDS.map((aiId, index) => (
<motion.div
key={aiId}
initial={{ opacity: 0, y: 8 }}
initial={{ opacity: 0, y: 6 }}
animate={{ opacity: 1, y: 0 }}
transition={{
delay: index * 0.06,
duration: 0.3,
ease: [0.16, 1, 0.3, 1],
}}
transition={{ delay: index * 0.05, duration: 0.25, ease: [0.16, 1, 0.3, 1] }}
>
<Chip
active={formData.aiTypes.includes(aiId)}
onClick={() => toggleAIType(aiId)}
>
<Chip active={formData.aiTypes.includes(aiId)} onClick={() => toggleAIType(aiId)}>
{t(`aiTypes.${aiId}.title`)}
</Chip>
</motion.div>
))}
</div>
<AnimatePresence mode="wait">
{formData.aiTypes.length > 0 && (
<motion.div
@@ -295,7 +238,7 @@ export default function StepServices({ formData, setFormData, onNext }: StepProp
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -4 }}
transition={{ duration: 0.2 }}
className="flex flex-col gap-1.5 px-1"
className="flex flex-col gap-1 px-1"
>
{formData.aiTypes.map((aiId) => (
<p key={aiId} className="text-xs text-outline leading-relaxed">
@@ -312,13 +255,8 @@ export default function StepServices({ formData, setFormData, onNext }: StepProp
</AnimatePresence>
</div>
<Button
variant="primary"
arrow
disabled={!canProceed}
onClick={onNext}
className="w-full"
>
{/* CTA */}
<Button variant="primary" arrow disabled={!canProceed} onClick={onNext} className="w-full">
{t('nextStep')}
</Button>
</div>

View File

@@ -131,7 +131,7 @@ export default function Configurator() {
transition={{ duration: 0.7, ease: [0.16, 1, 0.3, 1], delay: 0.1 }}
className="relative"
>
<div className="relative rounded-2xl bg-surface-high shadow-[0_20px_50px_rgba(25,28,29,0.08)] p-6 sm:p-8 overflow-hidden border border-outline-variant/20">
<div className="relative rounded-2xl bg-surface-high shadow-[0_20px_50px_rgba(25,28,29,0.08)] p-6 sm:p-8 border border-outline-variant/20">
{/* Top-edge accent line */}
<div
className="absolute top-0 left-6 right-6 h-[2px] rounded-full pointer-events-none"

View File

@@ -9,12 +9,12 @@
"bookCall": "Book a Call"
},
"hero": {
"title": "Websites, software, and infrastructure designed and built {accentWord} around you.",
"title": "Websites, software, and infrastructure - designed and built {accentWord} around you.",
"accentWord": "entirely",
"subtitle": "We design custom websites, build purpose-built software, and run private infrastructure that you own and control. One team, from first pixel to final deployment.",
"cta": "Start Your Project",
"ctaSecondary": "See Our Work",
"trust": "Trusted by businesses across the Riviera"
"trust": "Trusted by businesses worldwide"
},
"trustBar": {
"customBuilt": {
@@ -23,7 +23,7 @@
},
"privateInfra": {
"title": "You Own Everything",
"description": "Private servers, your data, your tools fully controlled and owned by you."
"description": "Private servers, your data, your tools - fully controlled and owned by you."
},
"aiPowered": {
"title": "One Team, End to End",
@@ -49,7 +49,7 @@
"title": "Private Infrastructure",
"features": ["Dedicated Servers", "Email & Cloud Storage", "Security & Encryption", "Monitoring & Support"]
},
"aiNarrative": "And when you're ready, we layer AI into everything from intelligent features in your software to automation that connects all your tools."
"aiNarrative": "And when you're ready, we layer AI into everything - from intelligent features in your software to automation that connects all your tools."
},
"configurator": {
"eyebrow": "Get Started",
@@ -69,7 +69,7 @@
},
"complete": {
"title": "Your project brief is ready",
"subtitle": "Check your inbox we've sent a detailed brief to {email}",
"subtitle": "Check your inbox - we've sent a detailed brief to {email}",
"bookTitle": "Book a Consultation",
"bookSubtitle": "30 minutes to discuss your brief with our team",
"bookCall": "Book a Call",
@@ -82,7 +82,7 @@
"services": {
"web": {
"title": "Web Design & Development",
"description": "Custom websites and web applications designed from scratch, built for performance, and optimized for search engines."
"description": "Custom websites and web applications - designed from scratch, built for performance, and optimized for search engines."
},
"systems": {
"title": "Custom Software",
@@ -90,7 +90,7 @@
},
"infrastructure": {
"title": "Private Infrastructure",
"description": "Dedicated servers with email, cloud storage, and business tools fully owned and controlled by you."
"description": "Dedicated servers with email, cloud storage, and business tools - fully owned and controlled by you."
}
},
"aiToggle": "Add AI Integration",
@@ -98,11 +98,11 @@
"aiTypes": {
"teammate": {
"title": "AI Teammate",
"description": "An internal AI assistant that helps your team work faster automates tasks, answers questions, connects your tools."
"description": "An internal AI assistant that helps your team work faster - automates tasks, answers questions, connects your tools."
},
"customer-facing": {
"title": "Customer-Facing AI",
"description": "AI features your clients interact with smart search, personalized recommendations, conversational interfaces."
"description": "AI features your clients interact with - smart search, personalized recommendations, conversational interfaces."
},
"data-intelligence": {
"title": "Data Intelligence",
@@ -110,7 +110,7 @@
},
"notsure": {
"title": "Not Sure Yet",
"description": "No problem we'll explore the best AI approach together during discovery."
"description": "No problem - we'll explore the best AI approach together during discovery."
}
},
"industries": {
@@ -167,7 +167,7 @@
},
"build": {
"title": "Design & Build",
"description": "Your project takes shape pixel by pixel, feature by feature."
"description": "Your project takes shape - pixel by pixel, feature by feature."
},
"launch": {
"title": "Launch & Support",
@@ -193,7 +193,7 @@
},
"portAmador": {
"title": "Port Amador",
"description": "Website and private digital infrastructure cloud storage, email, and file management for a premium marina.",
"description": "Website and private digital infrastructure - cloud storage, email, and file management for a premium marina.",
"tags": ["Website", "Infrastructure"]
}
},
@@ -211,10 +211,10 @@
"philosophy": {
"eyebrow": "Why It Matters",
"title": "Your tools should belong to you.",
"subtitle": "Most businesses rent their digital life from a dozen different platforms. We think there's a better way one where you own your data, control your infrastructure, and aren't locked into anyone's pricing page.",
"subtitle": "Most businesses rent their digital life from a dozen different platforms. We think there's a better way - one where you own your data, control your infrastructure, and aren't locked into anyone's pricing page.",
"ownership": {
"title": "Own Your Stack",
"description": "We move you off scattered SaaS subscriptions and onto private infrastructure servers, email, cloud storage, and tools that you control."
"description": "We move you off scattered SaaS subscriptions and onto private infrastructure - servers, email, cloud storage, and tools that you control."
},
"craftsmanship": {
"title": "No Shortcuts",
@@ -222,15 +222,15 @@
},
"oneTeam": {
"title": "One Relationship",
"description": "From the initial design through development to ongoing support one team that knows your business inside and out."
"description": "From the initial design through development to ongoing support - one team that knows your business inside and out."
},
"quote": "We build technology that works for your business not the other way around.",
"foundedLocation": "Based on the Côte d'Azur"
"quote": "We build technology that works for your business - not the other way around.",
"foundedLocation": "Matt Ciaccio, Founder"
},
"cta": {
"eyebrow": "Let's Talk",
"title": "Ready to build something?",
"subtitle": "Tell us what you need. No pitch decks, no pressure just a conversation about what's possible.",
"subtitle": "Tell us what you need. No pitch decks, no pressure - just a conversation about what's possible.",
"cta": "Start Your Project",
"configure": "Start Your Project",
"email": "hello@letsbe.biz",
@@ -238,7 +238,7 @@
},
"footer": {
"tagline": "Custom websites, software, and infrastructure for businesses that want to own their digital future.",
"location": "American-founded. Serving the Côte d'Azur and beyond.",
"location": "American-founded. Serving clients worldwide.",
"services": "Services",
"studio": "Studio",
"connect": "Connect",

View File

@@ -9,12 +9,12 @@
"bookCall": "Réserver un Appel"
},
"hero": {
"title": "Sites web, logiciels et infrastructure conçus et développés {accentWord} autour de vous.",
"title": "Sites web, logiciels et infrastructure - conçus et développés {accentWord} autour de vous.",
"accentWord": "entièrement",
"subtitle": "Nous concevons des sites web sur mesure, développons des logiciels dédiés et gérons une infrastructure privée qui vous appartient. Une seule équipe, du premier pixel au déploiement final.",
"cta": "Démarrer Votre Projet",
"ctaSecondary": "Voir Nos Réalisations",
"trust": "La confiance des entreprises de la Riviera"
"trust": "La confiance d'entreprises dans le monde entier"
},
"trustBar": {
"customBuilt": {
@@ -23,7 +23,7 @@
},
"privateInfra": {
"title": "Tout Vous Appartient",
"description": "Serveurs privés, vos données, vos outils entièrement contrôlés et détenus par vous."
"description": "Serveurs privés, vos données, vos outils - entièrement contrôlés et détenus par vous."
},
"aiPowered": {
"title": "Une Seule Équipe",
@@ -49,7 +49,7 @@
"title": "Infrastructure Privée",
"features": ["Serveurs Dédiés", "Email & Stockage Cloud", "Sécurité & Chiffrement", "Monitoring & Support"]
},
"aiNarrative": "Et quand vous êtes prêt, nous intégrons l'IA dans l'ensemble des fonctionnalités intelligentes dans vos logiciels à l'automatisation qui connecte tous vos outils."
"aiNarrative": "Et quand vous êtes prêt, nous intégrons l'IA dans l'ensemble - des fonctionnalités intelligentes dans vos logiciels à l'automatisation qui connecte tous vos outils."
},
"configurator": {
"eyebrow": "Démarrer",
@@ -69,7 +69,7 @@
},
"complete": {
"title": "Votre brief projet est prêt",
"subtitle": "Vérifiez votre boîte mail nous avons envoyé un brief détaillé à {email}",
"subtitle": "Vérifiez votre boîte mail - nous avons envoyé un brief détaillé à {email}",
"bookTitle": "Réservez une Consultation",
"bookSubtitle": "30 minutes pour discuter de votre brief avec notre équipe",
"bookCall": "Réserver un Appel",
@@ -82,7 +82,7 @@
"services": {
"web": {
"title": "Design & Développement Web",
"description": "Sites web et applications sur mesure conçus de zéro, optimisés pour la performance et le référencement."
"description": "Sites web et applications sur mesure - conçus de zéro, optimisés pour la performance et le référencement."
},
"systems": {
"title": "Logiciels Sur Mesure",
@@ -90,7 +90,7 @@
},
"infrastructure": {
"title": "Infrastructure Privée",
"description": "Serveurs dédiés avec email, stockage cloud et outils métier entièrement détenus et contrôlés par vous."
"description": "Serveurs dédiés avec email, stockage cloud et outils métier - entièrement détenus et contrôlés par vous."
}
},
"aiToggle": "Ajouter l'Intégration IA",
@@ -98,11 +98,11 @@
"aiTypes": {
"teammate": {
"title": "IA Coéquipier",
"description": "Un assistant IA interne qui aide votre équipe à travailler plus vite automatise les tâches, répond aux questions, connecte vos outils."
"description": "Un assistant IA interne qui aide votre équipe à travailler plus vite - automatise les tâches, répond aux questions, connecte vos outils."
},
"customer-facing": {
"title": "IA Client",
"description": "Des fonctionnalités IA avec lesquelles vos clients interagissent recherche intelligente, recommandations personnalisées, interfaces conversationnelles."
"description": "Des fonctionnalités IA avec lesquelles vos clients interagissent - recherche intelligente, recommandations personnalisées, interfaces conversationnelles."
},
"data-intelligence": {
"title": "Intelligence Données",
@@ -110,7 +110,7 @@
},
"notsure": {
"title": "Pas Encore Sûr",
"description": "Pas de problème nous explorerons ensemble la meilleure approche IA lors de la phase de découverte."
"description": "Pas de problème - nous explorerons ensemble la meilleure approche IA lors de la phase de découverte."
}
},
"industries": {
@@ -167,7 +167,7 @@
},
"build": {
"title": "Design & Construction",
"description": "Votre projet prend forme pixel par pixel, fonctionnalité par fonctionnalité."
"description": "Votre projet prend forme - pixel par pixel, fonctionnalité par fonctionnalité."
},
"launch": {
"title": "Lancement & Support",
@@ -193,7 +193,7 @@
},
"portAmador": {
"title": "Port Amador",
"description": "Site web et infrastructure digitale privée stockage cloud, email et gestion de fichiers pour une marina premium.",
"description": "Site web et infrastructure digitale privée - stockage cloud, email et gestion de fichiers pour une marina premium.",
"tags": ["Site Web", "Infrastructure"]
}
},
@@ -211,10 +211,10 @@
"philosophy": {
"eyebrow": "Pourquoi C'est Important",
"title": "Vos outils devraient vous appartenir.",
"subtitle": "La plupart des entreprises louent leur vie digitale à une dizaine de plateformes différentes. Nous pensons qu'il y a mieux un monde où vous possédez vos données, contrôlez votre infrastructure et n'êtes enfermé dans la grille tarifaire de personne.",
"subtitle": "La plupart des entreprises louent leur vie digitale à une dizaine de plateformes différentes. Nous pensons qu'il y a mieux - un monde où vous possédez vos données, contrôlez votre infrastructure et n'êtes enfermé dans la grille tarifaire de personne.",
"ownership": {
"title": "Maîtrisez Votre Stack",
"description": "Nous vous libérons des abonnements SaaS dispersés pour vous installer sur une infrastructure privée serveurs, email, stockage cloud et outils que vous contrôlez."
"description": "Nous vous libérons des abonnements SaaS dispersés pour vous installer sur une infrastructure privée - serveurs, email, stockage cloud et outils que vous contrôlez."
},
"craftsmanship": {
"title": "Pas de Raccourcis",
@@ -222,15 +222,15 @@
},
"oneTeam": {
"title": "Une Seule Relation",
"description": "Du design initial au développement jusqu'au support continu une seule équipe qui connaît votre activité de A à Z."
"description": "Du design initial au développement jusqu'au support continu - une seule équipe qui connaît votre activité de A à Z."
},
"quote": "Nous construisons la technologie qui travaille pour votre entreprise pas l'inverse.",
"foundedLocation": "Basé sur la Côte d'Azur"
"quote": "Nous construisons la technologie qui travaille pour votre entreprise - pas l'inverse.",
"foundedLocation": "Matt Ciaccio, Fondateur"
},
"cta": {
"eyebrow": "Parlons-en",
"title": "Prêt à construire quelque chose ?",
"subtitle": "Dites-nous ce dont vous avez besoin. Pas de slides, pas de pression juste une conversation sur ce qui est possible.",
"subtitle": "Dites-nous ce dont vous avez besoin. Pas de slides, pas de pression - juste une conversation sur ce qui est possible.",
"cta": "Démarrer Votre Projet",
"configure": "Démarrer Votre Projet",
"email": "hello@letsbe.biz",
@@ -238,7 +238,7 @@
},
"footer": {
"tagline": "Sites web, logiciels et infrastructure sur mesure pour les entreprises qui veulent maîtriser leur avenir digital.",
"location": "Fondé aux États-Unis. Au service de la Côte d'Azur et au-delà.",
"location": "Fondé aux États-Unis. Au service de clients dans le monde entier.",
"services": "Services",
"studio": "Studio",
"connect": "Contact",

View File

@@ -55,7 +55,7 @@ export async function sendBriefToClient({ to, name, brief }: SendBriefEmailOptio
</p>
</div>
<div style="border-top:1px solid #e5e7eb;padding:24px 0;font-size:12px;color:#999;text-align:center">
LetsBe. Digital Studio · Côte d'Azur, France
LetsBe Solutions LLC
</div>
</div>
`,