polish: configurator visual redesign + copy cleanup
Some checks failed
Build & Push / build-and-push (push) Failing after 29s
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:
@@ -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
|
||||||
|
|||||||
@@ -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 (
|
return (
|
||||||
<div className={cn('flex gap-1.5', className)} role="progressbar" aria-valuenow={currentStep} aria-valuemin={1} aria-valuemax={3}>
|
<div
|
||||||
{([1, 2, 3] as const).map((step) => {
|
className={cn('flex items-center justify-center', className)}
|
||||||
const isActive = step <= currentStep;
|
role="progressbar"
|
||||||
return (
|
aria-valuenow={currentStep}
|
||||||
<div
|
aria-valuemin={1}
|
||||||
key={step}
|
aria-valuemax={3}
|
||||||
className="relative flex-1 h-1 rounded-full bg-outline-variant/40 overflow-hidden"
|
>
|
||||||
>
|
<div className="flex items-center gap-0 w-full max-w-xs">
|
||||||
<motion.div
|
{([1, 2, 3] as const).map((step) => {
|
||||||
className="absolute inset-0 rounded-full bg-gradient-to-r from-primary-dark to-primary"
|
const isComplete = step < currentStep;
|
||||||
initial={{ scaleX: 0 }}
|
const isActive = step === currentStep;
|
||||||
animate={{ scaleX: isActive ? 1 : 0 }}
|
const isUpcoming = step > currentStep;
|
||||||
transition={{
|
const showFilledLine = step < currentStep;
|
||||||
type: 'spring',
|
|
||||||
stiffness: 300,
|
return (
|
||||||
damping: 30,
|
<div key={step} className="flex items-center flex-1 last:flex-none">
|
||||||
delay: isActive ? (step - 1) * 0.05 : 0,
|
{/* Step circle */}
|
||||||
}}
|
<motion.div
|
||||||
style={{ transformOrigin: 'left' }}
|
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',
|
||||||
</div>
|
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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,130 +50,61 @@ 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
|
||||||
</div>
|
size={20}
|
||||||
|
strokeWidth={1.5}
|
||||||
<div className="flex-1 min-w-0">
|
|
||||||
<p
|
|
||||||
className={cn(
|
className={cn(
|
||||||
'text-sm font-semibold leading-tight mb-1 transition-colors duration-200',
|
'transition-colors duration-200',
|
||||||
selected ? 'text-primary-dark' : 'text-on-surface',
|
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}
|
</div>
|
||||||
</span>
|
{/* Check badge — appears on selection */}
|
||||||
<p className="text-xs text-outline mt-0.5">{description}</p>
|
<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
|
|
||||||
|
<p
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex-shrink-0 w-10 h-6 rounded-full relative transition-colors duration-300',
|
'text-sm font-semibold leading-tight mb-1 transition-colors duration-200',
|
||||||
enabled ? 'bg-primary' : 'bg-outline-variant',
|
selected ? 'text-primary-dark' : 'text-on-surface',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<motion.div
|
{title}
|
||||||
className="absolute top-0.5 w-5 h-5 rounded-full bg-white shadow-sm"
|
</p>
|
||||||
animate={{ x: enabled ? 18 : 2 }}
|
<p className="text-[11px] text-outline leading-relaxed">{description}</p>
|
||||||
transition={springTransition}
|
</motion.button>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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
|
<Chip active={formData.aiTypes.includes(aiId)} onClick={() => toggleAIType(aiId)}>
|
||||||
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>
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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>
|
||||||
`,
|
`,
|
||||||
|
|||||||
Reference in New Issue
Block a user