feat(marketing-website): add BreakPilot marketing website with CMP integration

Multi-page marketing website positioned as "Deterministic Regulatory Engineering Platform":
- 7 pages: Home, Plattform, CE-Prozess, Product Compliance, Architektur, Team, Preise
- Platform Bridge animation (adapted from pitch-deck USP slide)
- Cookie-Banner with consent-service integration (breakpilot-marketing site)
- DE/EN language toggle + Dark/Light theme
- Docker service on port 3014

[guardrail-change] PlatformBridgeSection.tsx added to loc-exceptions (816 LOC, SVG animation)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-10 22:41:00 +02:00
parent 937eca6b77
commit d13f4511cb
68 changed files with 11493 additions and 0 deletions
@@ -0,0 +1,53 @@
'use client'
import { useEffect, useRef, useState } from 'react'
interface AnimatedCounterProps {
value: string
className?: string
}
export default function AnimatedCounter({ value, className = '' }: AnimatedCounterProps) {
const [display, setDisplay] = useState('0')
const ref = useRef<HTMLSpanElement>(null)
const hasAnimated = useRef(false)
useEffect(() => {
if (hasAnimated.current) return
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting && !hasAnimated.current) {
hasAnimated.current = true
const numericMatch = value.match(/^([\d.]+)/)
if (!numericMatch) {
setDisplay(value)
return
}
const target = parseFloat(numericMatch[1])
const suffix = value.slice(numericMatch[1].length)
const isFloat = value.includes('.')
const duration = 1500
const start = performance.now()
const animate = (now: number) => {
const progress = Math.min((now - start) / duration, 1)
const eased = 1 - Math.pow(1 - progress, 3)
const current = target * eased
setDisplay(
(isFloat ? current.toFixed(1) : Math.floor(current).toLocaleString('de-DE')) + suffix
)
if (progress < 1) requestAnimationFrame(animate)
}
requestAnimationFrame(animate)
}
},
{ threshold: 0.5 }
)
if (ref.current) observer.observe(ref.current)
return () => observer.disconnect()
}, [value])
return <span ref={ref} className={className}>{display}</span>
}
@@ -0,0 +1,33 @@
'use client'
import { motion } from 'framer-motion'
interface CTAButtonProps {
children: React.ReactNode
variant?: 'primary' | 'ghost'
href?: string
className?: string
onClick?: () => void
}
export default function CTAButton({ children, variant = 'primary', href, className = '', onClick }: CTAButtonProps) {
const baseClass = 'inline-flex items-center gap-2 px-6 py-3 rounded-xl font-semibold text-sm transition-all duration-200'
const variantClass = variant === 'primary'
? 'bg-accent-electric text-white glow-blue hover:bg-blue-500'
: 'border border-white/[0.12] text-white/80 hover:bg-white/[0.06] hover:text-white'
const Component = href ? motion.a : motion.button
return (
<Component
href={href}
onClick={onClick}
whileHover={{ scale: 1.03 }}
whileTap={{ scale: 0.97 }}
className={`${baseClass} ${variantClass} ${className}`}
>
{children}
</Component>
)
}
@@ -0,0 +1,15 @@
import { Check, X, Minus } from 'lucide-react'
interface ComparisonCellProps {
value: boolean | 'partial'
}
export default function ComparisonCell({ value }: ComparisonCellProps) {
if (value === true) {
return <Check className="w-4 h-4 text-green-400 mx-auto" />
}
if (value === 'partial') {
return <Minus className="w-4 h-4 text-amber-400 mx-auto" />
}
return <X className="w-4 h-4 text-white/20 mx-auto" />
}
@@ -0,0 +1,41 @@
'use client'
import { motion } from 'framer-motion'
import { ReactNode } from 'react'
import { ANIMATION } from '@/lib/constants'
interface FadeInViewProps {
children: ReactNode
className?: string
delay?: number
direction?: 'up' | 'down' | 'left' | 'right' | 'none'
duration?: number
}
const directionMap = {
up: { y: 30 },
down: { y: -30 },
left: { x: 30 },
right: { x: -30 },
none: {},
}
export default function FadeInView({
children,
className = '',
delay = 0,
direction = 'up',
duration = ANIMATION.duration,
}: FadeInViewProps) {
return (
<motion.div
initial={{ opacity: 0, ...directionMap[direction] }}
whileInView={{ opacity: 1, x: 0, y: 0 }}
viewport={{ once: true, margin: '100px 0px -60px 0px' }}
transition={{ duration, delay, ease: ANIMATION.ease }}
className={className}
>
{children}
</motion.div>
)
}
@@ -0,0 +1,32 @@
'use client'
import { motion } from 'framer-motion'
import { ReactNode } from 'react'
import { ANIMATION } from '@/lib/constants'
interface GlassCardProps {
children: ReactNode
className?: string
delay?: number
hover?: boolean
}
export default function GlassCard({ children, className = '', delay = 0, hover = true }: GlassCardProps) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '100px 0px -60px 0px' }}
transition={{ duration: ANIMATION.duration, delay }}
whileHover={hover ? { scale: 1.02, backgroundColor: 'rgba(255, 255, 255, 0.10)' } : undefined}
className={`
bg-white/[0.06] backdrop-blur-xl
border border-white/[0.08] rounded-2xl
p-6 transition-colors duration-200
${className}
`}
>
{children}
</motion.div>
)
}
@@ -0,0 +1,18 @@
'use client'
import { ReactNode } from 'react'
interface GradientTextProps {
children: ReactNode
className?: string
variant?: 'default' | 'signal'
}
export default function GradientText({ children, className = '', variant = 'default' }: GradientTextProps) {
const gradientClass = variant === 'signal' ? 'gradient-text-signal' : 'gradient-text'
return (
<span className={`${gradientClass} ${className}`}>
{children}
</span>
)
}
@@ -0,0 +1,34 @@
'use client'
import { motion } from 'framer-motion'
import GradientText from './GradientText'
const ease = [0.22, 1, 0.36, 1] as const
interface PageHeaderProps {
tag: string
title: string
titleHighlight: string
subtitle: string
}
export default function PageHeader({ tag, title, titleHighlight, subtitle }: PageHeaderProps) {
return (
<div className="pt-32 pb-16 text-center">
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5, ease }}>
<p className="mono-label mb-4">{tag}</p>
</motion.div>
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5, delay: 0.1, ease }}>
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold mb-6">
{title}{' '}
<GradientText>{titleHighlight}</GradientText>
</h1>
</motion.div>
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5, delay: 0.2, ease }}>
<p className="text-white/50 text-lg max-w-3xl mx-auto">
{subtitle}
</p>
</motion.div>
</div>
)
}
@@ -0,0 +1,33 @@
'use client'
import FadeInView from './FadeInView'
import GradientText from './GradientText'
interface SectionHeadingProps {
tag: string
title: string
titleHighlight: string
subtitle: string
center?: boolean
}
export default function SectionHeading({ tag, title, titleHighlight, subtitle, center = true }: SectionHeadingProps) {
return (
<div className={`mb-16 ${center ? 'text-center' : ''}`}>
<FadeInView>
<p className="mono-label mb-4">{tag}</p>
</FadeInView>
<FadeInView delay={0.1}>
<h2 className="text-4xl md:text-5xl font-bold mb-6">
{title}{' '}
<GradientText>{titleHighlight}</GradientText>
</h2>
</FadeInView>
<FadeInView delay={0.2}>
<p className={`text-white/50 text-lg ${center ? 'max-w-3xl mx-auto' : 'max-w-3xl'}`}>
{subtitle}
</p>
</FadeInView>
</div>
)
}
@@ -0,0 +1,23 @@
interface StatusIndicatorProps {
label: string
status?: 'active' | 'warning' | 'error'
className?: string
}
const statusColors = {
active: 'bg-green-500',
warning: 'bg-amber-500',
error: 'bg-red-500',
}
export default function StatusIndicator({ label, status = 'active', className = '' }: StatusIndicatorProps) {
return (
<div className={`inline-flex items-center gap-2 ${className}`}>
<span className="relative flex h-2.5 w-2.5">
<span className={`animate-ping absolute inline-flex h-full w-full rounded-full ${statusColors[status]} opacity-75`} />
<span className={`relative inline-flex rounded-full h-2.5 w-2.5 ${statusColors[status]}`} />
</span>
<span className="font-mono text-xs text-white/50">{label}</span>
</div>
)
}
@@ -0,0 +1,18 @@
interface TechBadgeProps {
children: React.ReactNode
className?: string
}
export default function TechBadge({ children, className = '' }: TechBadgeProps) {
return (
<span className={`
inline-block px-3 py-1 rounded-md
font-mono text-xs
bg-white/[0.06] border border-white/[0.08]
text-white/60
${className}
`}>
{children}
</span>
)
}