Files
breakpilot-core/pitch-deck/components/slides/ArchitectureSlide.tsx
Benjamin Admin 92c86ec6ba [split-required] [guardrail-change] Enforce 500 LOC budget across all services
Install LOC guardrails (check-loc.sh, architecture.md, pre-commit hook)
and split all 44 files exceeding 500 LOC into domain-focused modules:

- consent-service (Go): models, handlers, services, database splits
- backend-core (Python): security_api, rbac_api, pdf_service, auth splits
- admin-core (TypeScript): 5 page.tsx + sidebar extractions
- pitch-deck (TypeScript): 6 slides, 3 UI components, engine.ts splits
- voice-service (Python): enhanced_task_orchestrator split

Result: 0 violations, 36 exempted (pipeline, tests, pure-data files).
Go build verified clean. No behavior changes — pure structural splits.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 00:09:30 +02:00

255 lines
12 KiB
TypeScript

'use client'
import { useState, Fragment } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { Language } from '@/lib/types'
import { t } from '@/lib/i18n'
import GradientText from '../ui/GradientText'
import FadeInView from '../ui/FadeInView'
import { X, Users, Lock, Server, BadgeCheck } from 'lucide-react'
import { type NodeId, type NodeDef, getNodes, LAYERS } from './ArchitectureSlide.data'
import { CSS_KF, MONO, useIsLight, LayerConnector, LayerSlab } from './ArchitectureSlide.parts'
interface ArchitectureSlideProps { lang: Language }
// ── Main slide ────────────────────────────────────────────────────────────────
export default function ArchitectureSlide({ lang }: ArchitectureSlideProps) {
const i = t(lang)
const de = lang === 'de'
const isLight = useIsLight()
const allNodes = getNodes(de)
const nodeMap = Object.fromEntries(allNodes.map(n => [n.id, n])) as Record<NodeId, NodeDef>
const [activeId, setActiveId] = useState<NodeId | null>(null)
function toggle(id: NodeId) { setActiveId(prev => prev === id ? null : id) }
const active = activeId ? nodeMap[activeId] : null
const tenants = de
? ['Mandant A', 'Mandant B', 'Mandant C', 'Mandant N…']
: ['Namespace A', 'Namespace B', 'Namespace C', 'Namespace N…']
const layerLabels = de
? ['01 · Anwendung', '02 · Gateway', '03 · Infrastruktur']
: ['01 · Application', '02 · Gateway', '03 · Infrastructure']
const layerSublabels = de
? ['Benutzeroberflächen', 'Routing & Guardrails', 'Compute & Daten']
: ['User-facing services', 'Routing & guardrails', 'Compute & data']
return (
<div className="space-y-3">
<style>{CSS_KF}</style>
<FadeInView className="text-center mb-3">
<p className="text-[10px] font-mono text-indigo-400/50 uppercase tracking-widest mb-1.5">
{de ? 'Anhang' : 'Appendix'}
</p>
<h2 className="text-3xl md:text-4xl font-bold mb-1.5">
<GradientText>{i.annex.architecture.title}</GradientText>
</h2>
<p className="text-xs text-white/35">
{de ? 'Klicke auf eine Station für Details' : 'Click any node to explore'}
</p>
</FadeInView>
<FadeInView delay={0.15}>
<div className="flex items-center justify-center gap-2 flex-wrap mb-3 px-[4%]">
<Users className="w-3 h-3 text-white/25 flex-shrink-0" />
<span className="text-[9px] font-mono text-white/25 uppercase tracking-widest mr-1">
{de ? 'Kundenmandanten' : 'Customer Namespaces'}
</span>
{tenants.map(tn => (
<span key={tn} className="text-[9px] px-2 py-0.5 rounded-full border border-white/[0.08] bg-white/[0.03] text-white/35 font-mono">
{tn}
</span>
))}
</div>
{/* ── Main canvas ── */}
<div style={{
position: 'relative',
background: isLight
? 'linear-gradient(180deg, #f0f4ff 0%, #eef2ff 50%, #f0f4ff 100%)'
: 'linear-gradient(180deg, #0a0618 0%, #140a28 50%, #1a0f34 100%)',
borderRadius: 16, overflow: 'hidden',
padding: '22px 16px 20px',
fontFamily: '"Inter", system-ui, -apple-system, sans-serif',
WebkitFontSmoothing: 'antialiased',
MozOsxFontSmoothing: 'grayscale',
} as React.CSSProperties}>
{/* Ambient glows */}
{!isLight && (
<>
<div style={{
position: 'absolute', top: -80, left: '25%',
width: 400, height: 400, borderRadius: '50%',
background: 'radial-gradient(circle, rgba(167,139,250,.2), transparent 65%)',
filter: 'blur(50px)', pointerEvents: 'none',
}} />
<div style={{
position: 'absolute', bottom: -100, right: '15%',
width: 500, height: 500, borderRadius: '50%',
background: 'radial-gradient(circle, rgba(139,92,246,.15), transparent 65%)',
filter: 'blur(50px)', pointerEvents: 'none',
}} />
</>
)}
{/* Slabs + connectors */}
<div style={{
display: 'flex', flexDirection: 'column', alignItems: 'center',
position: 'relative', zIndex: 1,
perspective: '2000px', perspectiveOrigin: '50% 0%',
}}>
{LAYERS.map((layer, li) => {
const nodes = layer.nodeIds.map(id => nodeMap[id])
return (
<Fragment key={layer.id}>
<LayerSlab
label={layerLabels[li]}
sublabel={layerSublabels[li]}
nodes={nodes}
tint={layer.tint}
depth={layer.depth}
selectedId={activeId}
onSelect={toggle}
isLight={isLight}
/>
{li < LAYERS.length - 1 && <LayerConnector tint={layer.tint} />}
</Fragment>
)
})}
</div>
{/* Footer badges */}
<div style={{
display: 'flex', justifyContent: 'center', gap: 8,
flexWrap: 'wrap', marginTop: 20, position: 'relative', zIndex: 1,
}}>
{([
{ Icon: Lock, label: de ? 'Kein US-Anbieter · 100% DSGVO' : 'No US providers · 100% GDPR' },
{ Icon: Server, label: de ? 'BSI-zertifiziertes Rechenzentrum' : 'BSI-certified data center' },
{ Icon: BadgeCheck, label: de ? 'EU-souveräne Inferenz' : 'EU-sovereign inference' },
] as { Icon: React.ElementType; label: string }[]).map(({ Icon, label }) => (
<div key={label} style={{
display: 'flex', alignItems: 'center', gap: 6,
padding: '5px 11px', borderRadius: 99,
background: isLight ? '#ffffff' : 'rgba(10,6,24,.82)',
border: `1px solid ${isLight ? 'rgba(0,0,0,.1)' : 'rgba(167,139,250,.28)'}`,
fontSize: 10.5,
color: isLight ? '#64748b' : 'rgba(236,233,247,.7)',
whiteSpace: 'nowrap',
boxShadow: isLight ? '0 1px 3px rgba(0,0,0,.06)' : 'none',
}}>
<Icon style={{ width: 12, height: 12, color: '#a78bfa' }} />
{label}
</div>
))}
</div>
{/* Detail panel */}
<AnimatePresence>
{active && (
<motion.div
initial={{ y: '100%' }}
animate={{ y: 0 }}
exit={{ y: '100%' }}
transition={{ duration: 0.3, ease: [0.2, 0.7, 0.2, 1] }}
style={{
position: 'absolute', left: 0, right: 0, bottom: 0,
background: isLight ? 'rgba(255,255,255,.98)' : 'rgba(15,10,31,.97)',
borderTop: `1px solid ${active.color}${isLight ? '30' : '40'}`,
zIndex: 50,
padding: '18px 24px 20px',
boxShadow: isLight ? '0 -8px 30px rgba(0,0,0,.08)' : '0 -20px 60px rgba(0,0,0,.55)',
}}
>
<div style={{ maxWidth: 900, margin: '0 auto' }}>
<div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16, marginBottom: 14 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<div style={{
width: 38, height: 38, borderRadius: 11, flexShrink: 0,
background: `linear-gradient(135deg, ${active.color}3a, ${active.color}10)`,
border: `1px solid ${active.color}66`,
display: 'flex', alignItems: 'center', justifyContent: 'center',
color: active.color,
}}>
<active.icon style={{ width: 19, height: 19 }} />
</div>
<div>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
<span style={{ fontSize: 15, fontWeight: 600, color: isLight ? '#1a1a2e' : '#f5f3fc', letterSpacing: -0.2 }}>
{active.title}
</span>
<span style={{
fontSize: 9, padding: '2px 7px', borderRadius: 4,
background: `${active.color}18`, color: active.color,
border: `1px solid ${active.color}40`,
letterSpacing: 0.8, textTransform: 'uppercase' as const, fontWeight: 600,
}}>
{active.tier === 'product' ? (de ? 'Anwendung' : 'Application') :
active.tier === 'proxy' ? 'Gateway' :
(de ? 'Inferenz' : 'Inference')}
</span>
</div>
<div style={{ fontSize: 11.5, color: isLight ? '#64748b' : 'rgba(236,233,247,.5)', marginTop: 2 }}>
{active.subtitle}
</div>
</div>
</div>
<button
onClick={() => setActiveId(null)}
style={{
background: 'transparent',
border: `1px solid ${isLight ? 'rgba(0,0,0,.15)' : 'rgba(167,139,250,.25)'}`,
color: isLight ? '#64748b' : 'rgba(236,233,247,.5)',
width: 28, height: 28, borderRadius: 14,
cursor: 'pointer', display: 'flex',
alignItems: 'center', justifyContent: 'center', flexShrink: 0,
}}
>
<X style={{ width: 13, height: 13 }} />
</button>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20 }}>
<div>
<div style={{ fontSize: 8.5, letterSpacing: 1.5, textTransform: 'uppercase' as const, color: isLight ? '#94a3b8' : 'rgba(236,233,247,.32)', marginBottom: 7, fontWeight: 600 }}>
{de ? 'Stack' : 'Tech Stack'}
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
{active.tech.map(tk => (
<span key={tk} style={{
...MONO,
fontSize: 10, padding: '3px 8px', borderRadius: 5,
background: isLight ? '#f1f5f9' : 'rgba(255,255,255,.05)',
border: `1px solid ${isLight ? 'rgba(0,0,0,.1)' : 'rgba(255,255,255,.1)'}`,
color: isLight ? '#334155' : 'rgba(236,233,247,.65)',
}}>{tk}</span>
))}
</div>
</div>
<div>
<div style={{ fontSize: 8.5, letterSpacing: 1.5, textTransform: 'uppercase' as const, color: isLight ? '#94a3b8' : 'rgba(236,233,247,.32)', marginBottom: 7, fontWeight: 600 }}>
{de ? 'Funktionen' : 'Capabilities'}
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
{active.services.map(s => (
<div key={s.name} style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>
<div style={{ width: 3, height: 3, borderRadius: '50%', background: active.color, opacity: 0.7, flexShrink: 0, marginTop: 6 }} />
<span style={{ fontSize: 11.5, fontWeight: 600, color: isLight ? '#1a1a2e' : 'rgba(245,243,252,.82)' }}>{s.name}</span>
<span style={{ fontSize: 10, color: isLight ? '#64748b' : 'rgba(236,233,247,.38)' }}>{s.desc}</span>
</div>
))}
</div>
</div>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</FadeInView>
</div>
)
}