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 context = buildContext(body);
const displayName = body.name.split(' ')[0] || body.name; 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: Key facts about LetsBe:
- Every project is designed and coded from scratch — no templates, no page builders - 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 { motion } from 'framer-motion';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { Check } from 'lucide-react';
interface ProgressBarProps { interface ProgressBarProps {
currentStep: 1 | 2 | 3; currentStep: 1 | 2 | 3;
@@ -9,31 +10,68 @@ interface ProgressBarProps {
className?: string; className?: string;
} }
const STEP_LABELS = ['01', '02', '03'];
export default function ProgressBar({ currentStep, className }: ProgressBarProps) { 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 ( return (
<div <div
key={step} className={cn('flex items-center justify-center', className)}
className="relative flex-1 h-1 rounded-full bg-outline-variant/40 overflow-hidden" 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 <motion.div
className="absolute inset-0 rounded-full bg-gradient-to-r from-primary-dark to-primary" 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 }} initial={{ scaleX: 0 }}
animate={{ scaleX: isActive ? 1 : 0 }} animate={{ scaleX: showFilledLine ? 1 : 0 }}
transition={{ transition={{
type: 'spring', type: 'spring',
stiffness: 300, stiffness: 200,
damping: 30, damping: 25,
delay: isActive ? (step - 1) * 0.05 : 0,
}} }}
style={{ transformOrigin: 'left' }} style={{ transformOrigin: 'left' }}
/> />
</div> </div>
)}
</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; const AI_TYPE_IDS = ['teammate', 'customer-facing', 'data-intelligence', 'notsure'] as const;
// ─── Service Card ──────────────────────────────────────────────────────────── // ─── Service Card (horizontal layout) ────────────────────────────────────────
function ServiceCard({ function ServiceCard({
option, option,
@@ -35,12 +35,14 @@ function ServiceCard({
onToggle, onToggle,
title, title,
description, description,
index,
}: { }: {
option: ServiceOption; option: ServiceOption;
selected: boolean; selected: boolean;
onToggle: () => void; onToggle: () => void;
title: string; title: string;
description: string; description: string;
index: number;
}) { }) {
const Icon = option.icon; const Icon = option.icon;
@@ -48,28 +50,51 @@ function ServiceCard({
<motion.button <motion.button
type="button" type="button"
onClick={onToggle} 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 }} whileTap={{ scale: 0.97 }}
className={cn( 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', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2',
selected selected
? 'bg-primary/6 border-2 border-primary/30 shadow-card' ? 'bg-primary/[0.06] border-2 border-primary/40 shadow-[0_4px_16px_rgba(0,100,148,0.1)]'
: 'bg-surface-high border-2 border-transparent shadow-subtle hover:shadow-card hover:border-outline-variant/30', : '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 <div
className={cn( className={cn(
'flex-shrink-0 w-11 h-11 rounded-xl flex items-center justify-center transition-colors duration-200', 'w-10 h-10 rounded-lg flex items-center justify-center transition-colors duration-200',
selected selected ? 'bg-primary/12' : 'bg-surface-low group-hover:bg-primary/6',
? 'bg-primary/15 text-primary-dark'
: 'bg-primary/8 text-primary group-hover:bg-primary/12',
)} )}
> >
<Icon size={20} strokeWidth={1.5} /> <Icon
size={20}
strokeWidth={1.5}
className={cn(
'transition-colors duration-200',
selected ? 'text-primary-dark' : 'text-outline group-hover:text-primary',
)}
/>
</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>
<div className="flex-1 min-w-0">
<p <p
className={cn( className={cn(
'text-sm font-semibold leading-tight mb-1 transition-colors duration-200', 'text-sm font-semibold leading-tight mb-1 transition-colors duration-200',
@@ -78,103 +103,11 @@ function ServiceCard({
> >
{title} {title}
</p> </p>
<p className="text-xs text-outline leading-relaxed">{description}</p> <p className="text-[11px] 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> </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>
<div
className={cn(
'flex-shrink-0 w-10 h-6 rounded-full relative transition-colors duration-300',
enabled ? 'bg-primary' : 'bg-outline-variant',
)}
>
<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>
);
}
// ─── Main Component ────────────────────────────────────────────────────────── // ─── Main Component ──────────────────────────────────────────────────────────
export default function StepServices({ formData, setFormData, onNext }: StepProps) { export default function StepServices({ formData, setFormData, onNext }: StepProps) {
@@ -209,19 +142,19 @@ export default function StepServices({ formData, setFormData, onNext }: StepProp
const canProceed = formData.services.length > 0; const canProceed = formData.services.length > 0;
return ( return (
<div className="flex flex-col gap-6"> <div className="flex flex-col gap-5">
<ProgressBar currentStep={1} totalSteps={3} /> <ProgressBar currentStep={1} totalSteps={3} />
<div> <div className="text-center">
<h3 className="font-serif text-2xl font-semibold tracking-headline text-on-surface"> <h3 className="font-serif text-2xl font-semibold tracking-headline text-on-surface">
{t('step1.title')} {t('step1.title')}
</h3> </h3>
<p className="mt-1 text-sm text-outline">{t('step1.subtitle')}</p> <p className="mt-1 text-sm text-outline">{t('step1.subtitle')}</p>
</div> </div>
{/* Service cards */} {/* Service cards — horizontal grid */}
<div className="flex flex-col gap-3"> <div className="grid grid-cols-3 gap-3">
{SERVICES.map((option) => ( {SERVICES.map((option, index) => (
<ServiceCard <ServiceCard
key={option.id} key={option.id}
option={option} option={option}
@@ -229,31 +162,49 @@ export default function StepServices({ formData, setFormData, onNext }: StepProp
onToggle={() => toggleService(option.id)} onToggle={() => toggleService(option.id)}
title={t(option.titleKey)} title={t(option.titleKey)}
description={t(option.descriptionKey)} 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> </div>
{/* AI Toggle + Type Selection */} <p
<div className="flex flex-col gap-3"> className={cn(
<AIToggle 'text-xs text-center select-none -mt-2 transition-opacity duration-200',
enabled={formData.aiEnabled} formData.services.length === 0 ? 'text-outline/50' : 'opacity-0',
onToggle={toggleAI} )}
label={t('aiToggle')} aria-hidden={formData.services.length > 0}
description={t('aiDescription')} >
/> {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> <AnimatePresence>
{formData.aiEnabled && ( {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] }} transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="overflow-hidden" 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"> <div className="flex flex-wrap gap-2">
{AI_TYPE_IDS.map((aiId, index) => ( {AI_TYPE_IDS.map((aiId, index) => (
<motion.div <motion.div
key={aiId} key={aiId}
initial={{ opacity: 0, y: 8 }} initial={{ opacity: 0, y: 6 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ transition={{ delay: index * 0.05, duration: 0.25, ease: [0.16, 1, 0.3, 1] }}
delay: index * 0.06,
duration: 0.3,
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`)} {t(`aiTypes.${aiId}.title`)}
</Chip> </Chip>
</motion.div> </motion.div>
))} ))}
</div> </div>
<AnimatePresence mode="wait"> <AnimatePresence mode="wait">
{formData.aiTypes.length > 0 && ( {formData.aiTypes.length > 0 && (
<motion.div <motion.div
@@ -295,7 +238,7 @@ export default function StepServices({ formData, setFormData, onNext }: StepProp
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -4 }} exit={{ opacity: 0, y: -4 }}
transition={{ duration: 0.2 }} 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) => ( {formData.aiTypes.map((aiId) => (
<p key={aiId} className="text-xs text-outline leading-relaxed"> <p key={aiId} className="text-xs text-outline leading-relaxed">
@@ -312,13 +255,8 @@ export default function StepServices({ formData, setFormData, onNext }: StepProp
</AnimatePresence> </AnimatePresence>
</div> </div>
<Button {/* CTA */}
variant="primary" <Button variant="primary" arrow disabled={!canProceed} onClick={onNext} className="w-full">
arrow
disabled={!canProceed}
onClick={onNext}
className="w-full"
>
{t('nextStep')} {t('nextStep')}
</Button> </Button>
</div> </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 }} transition={{ duration: 0.7, ease: [0.16, 1, 0.3, 1], delay: 0.1 }}
className="relative" 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 */} {/* Top-edge accent line */}
<div <div
className="absolute top-0 left-6 right-6 h-[2px] rounded-full pointer-events-none" 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" "bookCall": "Book a Call"
}, },
"hero": { "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", "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.", "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", "cta": "Start Your Project",
"ctaSecondary": "See Our Work", "ctaSecondary": "See Our Work",
"trust": "Trusted by businesses across the Riviera" "trust": "Trusted by businesses worldwide"
}, },
"trustBar": { "trustBar": {
"customBuilt": { "customBuilt": {
@@ -23,7 +23,7 @@
}, },
"privateInfra": { "privateInfra": {
"title": "You Own Everything", "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": { "aiPowered": {
"title": "One Team, End to End", "title": "One Team, End to End",
@@ -49,7 +49,7 @@
"title": "Private Infrastructure", "title": "Private Infrastructure",
"features": ["Dedicated Servers", "Email & Cloud Storage", "Security & Encryption", "Monitoring & Support"] "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": { "configurator": {
"eyebrow": "Get Started", "eyebrow": "Get Started",
@@ -69,7 +69,7 @@
}, },
"complete": { "complete": {
"title": "Your project brief is ready", "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", "bookTitle": "Book a Consultation",
"bookSubtitle": "30 minutes to discuss your brief with our team", "bookSubtitle": "30 minutes to discuss your brief with our team",
"bookCall": "Book a Call", "bookCall": "Book a Call",
@@ -82,7 +82,7 @@
"services": { "services": {
"web": { "web": {
"title": "Web Design & Development", "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": { "systems": {
"title": "Custom Software", "title": "Custom Software",
@@ -90,7 +90,7 @@
}, },
"infrastructure": { "infrastructure": {
"title": "Private 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", "aiToggle": "Add AI Integration",
@@ -98,11 +98,11 @@
"aiTypes": { "aiTypes": {
"teammate": { "teammate": {
"title": "AI 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": { "customer-facing": {
"title": "Customer-Facing AI", "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": { "data-intelligence": {
"title": "Data Intelligence", "title": "Data Intelligence",
@@ -110,7 +110,7 @@
}, },
"notsure": { "notsure": {
"title": "Not Sure Yet", "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": { "industries": {
@@ -167,7 +167,7 @@
}, },
"build": { "build": {
"title": "Design & 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": { "launch": {
"title": "Launch & Support", "title": "Launch & Support",
@@ -193,7 +193,7 @@
}, },
"portAmador": { "portAmador": {
"title": "Port Amador", "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"] "tags": ["Website", "Infrastructure"]
} }
}, },
@@ -211,10 +211,10 @@
"philosophy": { "philosophy": {
"eyebrow": "Why It Matters", "eyebrow": "Why It Matters",
"title": "Your tools should belong to you.", "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": { "ownership": {
"title": "Own Your Stack", "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": { "craftsmanship": {
"title": "No Shortcuts", "title": "No Shortcuts",
@@ -222,15 +222,15 @@
}, },
"oneTeam": { "oneTeam": {
"title": "One Relationship", "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.", "quote": "We build technology that works for your business - not the other way around.",
"foundedLocation": "Based on the Côte d'Azur" "foundedLocation": "Matt Ciaccio, Founder"
}, },
"cta": { "cta": {
"eyebrow": "Let's Talk", "eyebrow": "Let's Talk",
"title": "Ready to build something?", "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", "cta": "Start Your Project",
"configure": "Start Your Project", "configure": "Start Your Project",
"email": "hello@letsbe.biz", "email": "hello@letsbe.biz",
@@ -238,7 +238,7 @@
}, },
"footer": { "footer": {
"tagline": "Custom websites, software, and infrastructure for businesses that want to own their digital future.", "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", "services": "Services",
"studio": "Studio", "studio": "Studio",
"connect": "Connect", "connect": "Connect",

View File

@@ -9,12 +9,12 @@
"bookCall": "Réserver un Appel" "bookCall": "Réserver un Appel"
}, },
"hero": { "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", "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.", "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", "cta": "Démarrer Votre Projet",
"ctaSecondary": "Voir Nos Réalisations", "ctaSecondary": "Voir Nos Réalisations",
"trust": "La confiance des entreprises de la Riviera" "trust": "La confiance d'entreprises dans le monde entier"
}, },
"trustBar": { "trustBar": {
"customBuilt": { "customBuilt": {
@@ -23,7 +23,7 @@
}, },
"privateInfra": { "privateInfra": {
"title": "Tout Vous Appartient", "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": { "aiPowered": {
"title": "Une Seule Équipe", "title": "Une Seule Équipe",
@@ -49,7 +49,7 @@
"title": "Infrastructure Privée", "title": "Infrastructure Privée",
"features": ["Serveurs Dédiés", "Email & Stockage Cloud", "Sécurité & Chiffrement", "Monitoring & Support"] "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": { "configurator": {
"eyebrow": "Démarrer", "eyebrow": "Démarrer",
@@ -69,7 +69,7 @@
}, },
"complete": { "complete": {
"title": "Votre brief projet est prêt", "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", "bookTitle": "Réservez une Consultation",
"bookSubtitle": "30 minutes pour discuter de votre brief avec notre équipe", "bookSubtitle": "30 minutes pour discuter de votre brief avec notre équipe",
"bookCall": "Réserver un Appel", "bookCall": "Réserver un Appel",
@@ -82,7 +82,7 @@
"services": { "services": {
"web": { "web": {
"title": "Design & Développement 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": { "systems": {
"title": "Logiciels Sur Mesure", "title": "Logiciels Sur Mesure",
@@ -90,7 +90,7 @@
}, },
"infrastructure": { "infrastructure": {
"title": "Infrastructure Privée", "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", "aiToggle": "Ajouter l'Intégration IA",
@@ -98,11 +98,11 @@
"aiTypes": { "aiTypes": {
"teammate": { "teammate": {
"title": "IA Coéquipier", "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": { "customer-facing": {
"title": "IA Client", "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": { "data-intelligence": {
"title": "Intelligence Données", "title": "Intelligence Données",
@@ -110,7 +110,7 @@
}, },
"notsure": { "notsure": {
"title": "Pas Encore Sûr", "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": { "industries": {
@@ -167,7 +167,7 @@
}, },
"build": { "build": {
"title": "Design & Construction", "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": { "launch": {
"title": "Lancement & Support", "title": "Lancement & Support",
@@ -193,7 +193,7 @@
}, },
"portAmador": { "portAmador": {
"title": "Port Amador", "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"] "tags": ["Site Web", "Infrastructure"]
} }
}, },
@@ -211,10 +211,10 @@
"philosophy": { "philosophy": {
"eyebrow": "Pourquoi C'est Important", "eyebrow": "Pourquoi C'est Important",
"title": "Vos outils devraient vous appartenir.", "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": { "ownership": {
"title": "Maîtrisez Votre Stack", "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": { "craftsmanship": {
"title": "Pas de Raccourcis", "title": "Pas de Raccourcis",
@@ -222,15 +222,15 @@
}, },
"oneTeam": { "oneTeam": {
"title": "Une Seule Relation", "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.", "quote": "Nous construisons la technologie qui travaille pour votre entreprise - pas l'inverse.",
"foundedLocation": "Basé sur la Côte d'Azur" "foundedLocation": "Matt Ciaccio, Fondateur"
}, },
"cta": { "cta": {
"eyebrow": "Parlons-en", "eyebrow": "Parlons-en",
"title": "Prêt à construire quelque chose ?", "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", "cta": "Démarrer Votre Projet",
"configure": "Démarrer Votre Projet", "configure": "Démarrer Votre Projet",
"email": "hello@letsbe.biz", "email": "hello@letsbe.biz",
@@ -238,7 +238,7 @@
}, },
"footer": { "footer": {
"tagline": "Sites web, logiciels et infrastructure sur mesure pour les entreprises qui veulent maîtriser leur avenir digital.", "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", "services": "Services",
"studio": "Studio", "studio": "Studio",
"connect": "Contact", "connect": "Contact",

View File

@@ -55,7 +55,7 @@ export async function sendBriefToClient({ to, name, brief }: SendBriefEmailOptio
</p> </p>
</div> </div>
<div style="border-top:1px solid #e5e7eb;padding:24px 0;font-size:12px;color:#999;text-align:center"> <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>
</div> </div>
`, `,