fix(pitch-deck): resolve merge conflict in AIPipelineSlide — keep updated version
All checks were successful
Build pitch-deck / build-push-deploy (push) Successful in 1m0s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 34s
CI / test-python-voice (push) Successful in 30s
CI / test-bqas (push) Successful in 31s
All checks were successful
Build pitch-deck / build-push-deploy (push) Successful in 1m0s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 34s
CI / test-python-voice (push) Successful in 30s
CI / test-bqas (push) Successful in 31s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,6 @@ import {
|
||||
Activity,
|
||||
Shield,
|
||||
Cpu,
|
||||
MessageSquare,
|
||||
Eye,
|
||||
Gauge,
|
||||
Network,
|
||||
|
||||
@@ -277,7 +277,7 @@ const PRICING_COMPARISON: CompetitorPricing[] = [
|
||||
{
|
||||
name: 'ComplAI',
|
||||
flag: '🇩🇪',
|
||||
model: 'Cloud (BSI DE / OVH FR)',
|
||||
model: 'Cloud (BSI DE / FR)',
|
||||
publicPricing: true,
|
||||
tiers: [
|
||||
{ name: { de: 'Startup/<10', en: 'Startup/<10' }, price: 'ab €300/mo', annual: 'ab €3.600/yr', notes: { de: '14-Tage-Test, Kreditkarte', en: '14-day trial, credit card' } },
|
||||
|
||||
@@ -38,8 +38,8 @@ export default function EngineeringSlide({ lang }: EngineeringSlideProps) {
|
||||
},
|
||||
{
|
||||
value: '10',
|
||||
label: de ? 'Docker Container' : 'Docker Containers',
|
||||
sub: de ? 'Coolify → Hetzner (amd64)' : 'Coolify → Hetzner (amd64)',
|
||||
label: de ? 'Services' : 'Services',
|
||||
sub: de ? 'orca → Hetzner (amd64)' : 'orca → Hetzner (amd64)',
|
||||
color: 'text-emerald-400',
|
||||
borderColor: 'border-emerald-500/30',
|
||||
},
|
||||
@@ -51,9 +51,9 @@ export default function EngineeringSlide({ lang }: EngineeringSlideProps) {
|
||||
borderColor: 'border-purple-500/30',
|
||||
},
|
||||
{
|
||||
value: '14',
|
||||
label: 'Dockerfiles',
|
||||
sub: de ? 'Vollstaendig containerisiert' : 'Fully containerized',
|
||||
value: '5',
|
||||
label: de ? 'Infra-Komponenten' : 'Infra Components',
|
||||
sub: 'orca (Rust) · infisical · pg · qdrant',
|
||||
color: 'text-amber-400',
|
||||
borderColor: 'border-amber-500/30',
|
||||
},
|
||||
@@ -69,17 +69,17 @@ export default function EngineeringSlide({ lang }: EngineeringSlideProps) {
|
||||
{
|
||||
icon: GitBranch,
|
||||
label: 'Gitea + Actions',
|
||||
desc: de ? 'Self-hosted Git + CI/CD · Lint → Tests → Validierung' : 'Self-hosted Git + CI/CD · Lint → Tests → Validation',
|
||||
desc: de ? 'Self-hosted Git + CI/CD · Lint → Tests → Image-Build' : 'Self-hosted Git + CI/CD · Lint → Tests → Image build',
|
||||
},
|
||||
{
|
||||
icon: Workflow,
|
||||
label: 'Coolify',
|
||||
desc: de ? 'Auto-Deploy bei Push · Docker Compose auf Hetzner · Health Checks' : 'Auto-deploy on push · Docker Compose on Hetzner · Health checks',
|
||||
label: 'orca',
|
||||
desc: de ? 'Single-Binary Orchestrator (Rust) · Webhook-Deploy · Auto-TLS · Raft' : 'Single-binary orchestrator (Rust) · Webhook deploys · Auto-TLS · Raft',
|
||||
},
|
||||
{
|
||||
icon: Container,
|
||||
label: 'Docker Compose',
|
||||
desc: de ? 'arm64 → amd64 Build-Pipeline · Multi-Stage Builds' : 'arm64 → amd64 build pipeline · Multi-stage builds',
|
||||
label: 'Private Registry',
|
||||
desc: de ? 'registry.meghsakha.com · Signed Images · Tag pro Commit (:SHA + :latest)' : 'registry.meghsakha.com · Signed images · Per-commit tags (:SHA + :latest)',
|
||||
},
|
||||
{
|
||||
icon: ShieldCheck,
|
||||
@@ -88,13 +88,13 @@ export default function EngineeringSlide({ lang }: EngineeringSlideProps) {
|
||||
},
|
||||
{
|
||||
icon: Database,
|
||||
label: 'HashiCorp Vault',
|
||||
desc: de ? 'Secrets Management · Auto-Rotation · PKI' : 'Secrets Management · Auto-Rotation · PKI',
|
||||
label: 'Infisical',
|
||||
desc: de ? 'Secrets Management · Rotation · RBAC · End-to-End verschlüsselt' : 'Secrets Management · Rotation · RBAC · End-to-end encrypted',
|
||||
},
|
||||
{
|
||||
icon: Server,
|
||||
label: de ? 'EU-Cloud Infrastruktur' : 'EU Cloud Infrastructure',
|
||||
desc: de ? 'Hetzner · SysEleven (BSI) · OVH · PostgreSQL · Qdrant' : 'Hetzner · SysEleven (BSI) · OVH · PostgreSQL · Qdrant',
|
||||
desc: de ? 'Hetzner · SysEleven (BSI) · PostgreSQL · Qdrant' : 'Hetzner · SysEleven (BSI) · PostgreSQL · Qdrant',
|
||||
},
|
||||
]
|
||||
|
||||
@@ -120,8 +120,8 @@ export default function EngineeringSlide({ lang }: EngineeringSlideProps) {
|
||||
color: 'text-emerald-400',
|
||||
dotColor: 'bg-emerald-400',
|
||||
services: de
|
||||
? ['PostgreSQL 17 (Hetzner)', 'Qdrant Vector DB', 'DSMS/IPFS Node + Gateway', 'MinIO Object Storage']
|
||||
: ['PostgreSQL 17 (Hetzner)', 'Qdrant Vector DB', 'DSMS/IPFS Node + Gateway', 'MinIO Object Storage'],
|
||||
? ['orca (Rust) Orchestrator', 'Infisical Secrets', 'PostgreSQL 17 (Hetzner)', 'Qdrant Vector DB', 'DSMS/IPFS Node + Gateway', 'Private Registry']
|
||||
: ['orca (Rust) Orchestrator', 'Infisical Secrets', 'PostgreSQL 17 (Hetzner)', 'Qdrant Vector DB', 'DSMS/IPFS Node + Gateway', 'Private Registry'],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -261,8 +261,8 @@ export default function EngineeringSlide({ lang }: EngineeringSlideProps) {
|
||||
<div className="mt-3 pt-3 border-t border-white/5">
|
||||
<p className="text-[10px] text-white/20 text-center">
|
||||
{de
|
||||
? '100% EU-Cloud · Hetzner + SysEleven (BSI) + OVH · Keine US-Anbieter · Volle Datenkontrolle'
|
||||
: '100% EU Cloud · Hetzner + SysEleven (BSI) + OVH · No US Providers · Full Data Control'}
|
||||
? '100% EU-Cloud · Hetzner + SysEleven (BSI) · Keine US-Anbieter · Volle Datenkontrolle'
|
||||
: '100% EU Cloud · Hetzner + SysEleven (BSI) · No US Providers · Full Data Control'}
|
||||
</p>
|
||||
</div>
|
||||
</GlassCard>
|
||||
|
||||
@@ -179,7 +179,7 @@ export default function ExecutiveSummarySlide({ lang, data }: ExecutiveSummarySl
|
||||
<li><strong>SAST + DAST + SBOM</strong> ${de ? '\\u2014 Vollumf\\u00e4ngliche Sicherheitstests bei jeder Code-\\u00c4nderung' : '\\u2014 Full security testing on every code change'}</li>
|
||||
<li><strong>${de ? 'KI-gest\\u00fctztes Pentesting' : 'AI-powered Pentesting'}</strong> ${de ? '\\u2014 Kontinuierlich statt einmal im Jahr' : '\\u2014 Continuous instead of once a year'}</li>
|
||||
<li><strong>CE-Software-Risikobeurteilung</strong> ${de ? '\\u2014 F\\u00fcr Maschinenverordnung und Produktsicherheit' : '\\u2014 For Machinery Regulation and product safety'}</li>
|
||||
<li><strong>Jira-Integration</strong> ${de ? '\\u2014 Findings als Tickets mit Implementierungsvorschl\\u00e4gen' : '\\u2014 Findings as tickets with implementation suggestions'}</li>
|
||||
<li><strong>Issue-Tracker-Integration</strong> ${de ? '\\u2014 Findings als Tickets mit Implementierungsvorschl\\u00e4gen' : '\\u2014 Findings as tickets with implementation suggestions'}</li>
|
||||
<li><strong>Audit-Trail</strong> ${de ? '\\u2014 L\\u00fcckenloser Nachweis von Erkennung bis Behebung' : '\\u2014 Complete evidence from detection to remediation'}</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -191,7 +191,7 @@ export default function ExecutiveSummarySlide({ lang, data }: ExecutiveSummarySl
|
||||
<li><strong>Audit Manager</strong> ${de ? '\\u2014 Abweichungen End-to-End: Rollen, Stichtage, Eskalation' : '\\u2014 Deviations end-to-end: roles, deadlines, escalation'}</li>
|
||||
<li><strong>Compliance LLM</strong> ${de ? '\\u2014 GPT f\\u00fcr Text und Audio, sicher in der EU gehostet' : '\\u2014 GPT for text and audio, securely hosted in EU'}</li>
|
||||
<li><strong>Academy</strong> ${de ? '\\u2014 Online-Schulungen f\\u00fcr GF und Mitarbeiter' : '\\u2014 Online training for management and employees'}</li>
|
||||
<li><strong>${de ? 'BSI-Cloud DE / OVH FR' : 'BSI Cloud DE / OVH FR'}</strong> ${de ? '\\u2014 Keine US-SaaS, Jitsi, Matrix, volle Integration' : '\\u2014 No US SaaS, Jitsi, Matrix, full integration'}</li>
|
||||
<li><strong>${de ? 'BSI-Cloud DE / FR' : 'BSI Cloud DE / FR'}</strong> ${de ? '\\u2014 Keine US-SaaS, Jitsi, Matrix, volle Integration' : '\\u2014 No US SaaS, Jitsi, Matrix, full integration'}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -208,7 +208,7 @@ export default function ExecutiveSummarySlide({ lang, data }: ExecutiveSummarySl
|
||||
<div class="card bottom-card">
|
||||
<div class="section-title">${de ? 'Gesch\\u00e4ftsmodell' : 'Business Model'}</div>
|
||||
<ul>
|
||||
<li><strong>SaaS Cloud</strong> ${de ? '\\u2014 BSI DE / OVH FR, mitarbeiterbasiert' : '\\u2014 BSI DE / OVH FR, employee-based'}</li>
|
||||
<li><strong>SaaS Cloud</strong> ${de ? '\\u2014 BSI DE / FR, mitarbeiterbasiert' : '\\u2014 BSI DE / FR, employee-based'}</li>
|
||||
<li><strong>${de ? 'Modular w\\u00e4hlbar' : 'Modular choice'}</strong> ${de ? '\\u2014 Einzelne Module oder Full Compliance' : '\\u2014 Single modules or full compliance'}</li>
|
||||
<li><strong>${de ? 'ROI ab Tag 1' : 'ROI from day 1'}</strong> ${de ? '\\u2014 Kunde spart 50.000+ EUR/Jahr' : '\\u2014 Customer saves EUR 50,000+/year'}</li>
|
||||
</ul>
|
||||
@@ -416,7 +416,7 @@ export default function ExecutiveSummarySlide({ lang, data }: ExecutiveSummarySl
|
||||
de ? 'Audit Manager — Abweichungen End-to-End mit Eskalation' : 'Audit Manager — deviations end-to-end with escalation',
|
||||
de ? 'Compliance LLM — GPT für Text und Audio, EU-gehostet' : 'Compliance LLM — GPT for text and audio, EU-hosted',
|
||||
de ? 'Academy — Online-Schulungen für GF und Mitarbeiter' : 'Academy — online training for management and employees',
|
||||
de ? 'BSI-Cloud DE / OVH FR' : 'BSI Cloud DE / OVH FR',
|
||||
de ? 'BSI-Cloud DE / FR' : 'BSI Cloud DE / FR',
|
||||
].map((item, idx) => (
|
||||
<p key={idx} className="text-xs text-white/60 pl-3 relative">
|
||||
<span className="absolute left-0 top-1 w-1.5 h-1.5 rounded-full bg-cyan-400/60" />
|
||||
|
||||
@@ -109,7 +109,7 @@ export default function ProductSlide({ lang }: ProductSlideProps) {
|
||||
<p className="text-[10px] text-white/50 leading-relaxed">{i.product.cloudDesc}</p>
|
||||
<div className="flex gap-2 mt-2">
|
||||
<span className="text-[9px] bg-blue-500/15 text-blue-300 px-2 py-0.5 rounded-full">BSI DE</span>
|
||||
<span className="text-[9px] bg-blue-500/15 text-blue-300 px-2 py-0.5 rounded-full">OVH FR</span>
|
||||
<span className="text-[9px] bg-blue-500/15 text-blue-300 px-2 py-0.5 rounded-full">FR</span>
|
||||
<span className="text-[9px] bg-blue-500/15 text-blue-300 px-2 py-0.5 rounded-full">{de ? 'Fix oder flexibel' : 'Fixed or flexible'}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { Language, PitchTeamMember } from '@/lib/types'
|
||||
import { t } from '@/lib/i18n'
|
||||
import { User, Linkedin } from 'lucide-react'
|
||||
import { User, Linkedin, Github } from 'lucide-react'
|
||||
import GradientText from '../ui/GradientText'
|
||||
import FadeInView from '../ui/FadeInView'
|
||||
import Image from 'next/image'
|
||||
@@ -13,89 +13,109 @@ interface TeamSlideProps {
|
||||
team: PitchTeamMember[]
|
||||
}
|
||||
|
||||
function equityDisplay(pct: number | string | null | undefined): string {
|
||||
const n = Number(pct)
|
||||
if (!Number.isFinite(n)) return '—'
|
||||
return Number.isInteger(n) ? `${n}%` : `${n.toFixed(1)}%`
|
||||
}
|
||||
|
||||
function detectProfileLink(url: string | null | undefined): { icon: typeof Linkedin | typeof Github; label: string } | null {
|
||||
if (!url) return null
|
||||
if (url.includes('github.com')) return { icon: Github, label: 'GitHub' }
|
||||
if (url.includes('linkedin.com')) return { icon: Linkedin, label: 'LinkedIn' }
|
||||
return { icon: Linkedin, label: 'Profile' }
|
||||
}
|
||||
|
||||
export default function TeamSlide({ lang, team }: TeamSlideProps) {
|
||||
const i = t(lang)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FadeInView className="text-center mb-12">
|
||||
<FadeInView className="text-center mb-8">
|
||||
<h2 className="text-4xl md:text-5xl font-bold mb-3">
|
||||
<GradientText>{i.team.title}</GradientText>
|
||||
</h2>
|
||||
<p className="text-lg text-white/50 max-w-2xl mx-auto">{i.team.subtitle}</p>
|
||||
</FadeInView>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-8 max-w-4xl mx-auto">
|
||||
{team.map((member, idx) => (
|
||||
<motion.div
|
||||
key={member.id}
|
||||
initial={{ opacity: 0, x: idx === 0 ? -40 : 40 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.3 + idx * 0.2, duration: 0.6 }}
|
||||
className="bg-white/[0.08] backdrop-blur-xl border border-white/10 rounded-3xl p-8"
|
||||
>
|
||||
<div className="flex items-start gap-5">
|
||||
{/* Avatar — Foto oder Fallback */}
|
||||
{member.photo_url ? (
|
||||
<div className="w-20 h-20 rounded-2xl overflow-hidden shrink-0 shadow-lg">
|
||||
<Image
|
||||
src={member.photo_url}
|
||||
alt={member.name}
|
||||
width={80}
|
||||
height={80}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-20 h-20 rounded-2xl bg-gradient-to-br from-indigo-500 to-purple-600
|
||||
flex items-center justify-center shrink-0 shadow-lg">
|
||||
<User className="w-10 h-10 text-white" />
|
||||
</div>
|
||||
)}
|
||||
<div className="grid md:grid-cols-2 gap-6 max-w-5xl mx-auto items-stretch">
|
||||
{team.map((member, idx) => {
|
||||
const link = detectProfileLink(member.linkedin_url)
|
||||
const LinkIcon = link?.icon
|
||||
return (
|
||||
<motion.div
|
||||
key={member.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.2 + idx * 0.15, duration: 0.5 }}
|
||||
className="bg-white/[0.04] backdrop-blur-xl border border-white/[0.08] rounded-2xl p-6 flex flex-col hover:border-indigo-500/20 transition-colors"
|
||||
>
|
||||
{/* Header: avatar + name + role */}
|
||||
<div className="flex items-center gap-4 mb-5">
|
||||
{member.photo_url ? (
|
||||
<div className="w-16 h-16 rounded-2xl overflow-hidden shrink-0 shadow-lg">
|
||||
<Image
|
||||
src={member.photo_url}
|
||||
alt={member.name}
|
||||
width={64}
|
||||
height={64}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center shrink-0 shadow-lg shadow-indigo-500/20">
|
||||
<User className="w-8 h-8 text-white" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h3 className="text-xl font-bold text-white">{member.name}</h3>
|
||||
{member.linkedin_url && (
|
||||
<a
|
||||
href={member.linkedin_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="p-1 text-white/30 hover:text-[#0A66C2] transition-colors"
|
||||
title="LinkedIn"
|
||||
>
|
||||
<Linkedin className="w-4 h-4" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-indigo-400 text-sm font-medium mb-3">
|
||||
{lang === 'de' ? member.role_de : member.role_en}
|
||||
</p>
|
||||
<p className="text-sm text-white/50 leading-relaxed mb-4">
|
||||
{lang === 'de' ? member.bio_de : member.bio_en}
|
||||
</p>
|
||||
|
||||
{/* Equity */}
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<span className="text-xs text-white/40">{i.team.equity}:</span>
|
||||
<span className="text-sm font-bold text-white">{member.equity_pct}%</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-2 mb-0.5 flex-wrap">
|
||||
<h3 className="text-xl font-bold text-white truncate">{member.name}</h3>
|
||||
{link && LinkIcon && (
|
||||
<a
|
||||
href={member.linkedin_url!}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-white/30 hover:text-indigo-300 transition-colors"
|
||||
title={link.label}
|
||||
>
|
||||
<LinkIcon className="w-4 h-4" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-indigo-400 text-sm font-medium">
|
||||
{lang === 'de' ? member.role_de : member.role_en}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Expertise Tags */}
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{/* Equity pill in top-right */}
|
||||
<div className="text-right shrink-0">
|
||||
<div className="text-[10px] uppercase tracking-wider text-white/30">{i.team.equity}</div>
|
||||
<div className="text-base font-bold text-white tabular-nums">{equityDisplay(member.equity_pct)}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bio */}
|
||||
<p className="text-sm text-white/60 leading-relaxed mb-5 flex-1">
|
||||
{lang === 'de' ? member.bio_de : member.bio_en}
|
||||
</p>
|
||||
|
||||
{/* Expertise tags */}
|
||||
{(member.expertise || []).length > 0 && (
|
||||
<div className="flex flex-wrap gap-1.5 pt-4 border-t border-white/[0.06]">
|
||||
{(member.expertise || []).map((skill, sidx) => (
|
||||
<span
|
||||
key={sidx}
|
||||
className="text-xs px-2.5 py-1 rounded-full bg-indigo-500/10 text-indigo-300 border border-indigo-500/20"
|
||||
className="text-[11px] px-2.5 py-1 rounded-full bg-indigo-500/10 text-indigo-300 border border-indigo-500/20"
|
||||
>
|
||||
{skill}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
)}
|
||||
</motion.div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user