Move Projekt-KIQ-HP bahn/ -> dhive/ (dhive project); add steering files

This commit is contained in:
2026-06-30 21:15:59 +02:00
parent d9291f53e4
commit b0e452708a
124 changed files with 74 additions and 0 deletions
@@ -0,0 +1,66 @@
.bg-pattern-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: -10;
overflow: hidden;
background-color: var(--bg-color);
pointer-events: none;
}
.bg-scroll-container {
display: flex;
width: 4000px; /* 2 seamless blocks of 2000px width */
height: 2000px;
animation: endlessDrift 60s linear infinite;
opacity: 0.15; /* Subtle blend */
}
/*
One seamless block consists of 4 flipped versions of the original 1000px image,
making it a 2000px x 2000px perfectly seamless tile.
*/
.seamless-block {
width: 2000px;
height: 2000px;
display: flex;
flex-wrap: wrap;
flex-shrink: 0;
}
.bg-quadrant {
width: 1000px;
height: 1000px;
background-image: url('/logos/d_-hive-lines_1-1.svg');
background-size: 1000px 1000px;
background-repeat: no-repeat;
}
/* Mirroring to force seamless edge matching */
.top-left {
transform: scale(1, 1);
}
.top-right {
transform: scale(-1, 1);
}
.bottom-left {
transform: scale(1, -1);
}
.bottom-right {
transform: scale(-1, -1);
}
/* Move the entire double-block leftwards by the exact width of ONE block (2000px) */
@keyframes endlessDrift {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(-2000px, -500px); /* Move left 2000px, and up a bit for diagonal drift */
}
}
@@ -0,0 +1,34 @@
import React from 'react';
import './BackgroundPattern.css';
export default function BackgroundPattern() {
return (
<div className="bg-pattern-wrapper">
{/*
To create a perfectly seamless infinite tile from a non-seamless image,
we create a single massive seamless "block" by mirroring the image
in all 4 directions (Top-Left, Top-Right, Bottom-Left, Bottom-Right).
Then we duplicate this massive block to loop it.
*/}
<div className="bg-scroll-container">
{/* Block 1 */}
<div className="seamless-block">
<div className="bg-quadrant top-left"></div>
<div className="bg-quadrant top-right"></div>
<div className="bg-quadrant bottom-left"></div>
<div className="bg-quadrant bottom-right"></div>
</div>
{/* Block 2 (Duplicate for infinite seamless scroll) */}
<div className="seamless-block">
<div className="bg-quadrant top-left"></div>
<div className="bg-quadrant top-right"></div>
<div className="bg-quadrant bottom-left"></div>
<div className="bg-quadrant bottom-right"></div>
</div>
</div>
</div>
);
}
@@ -0,0 +1,125 @@
.dock-container {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
z-index: 1000;
display: flex;
justify-content: center;
}
.dock {
position: relative;
overflow: hidden;
/* background/border is now handled by pseudo-elements for the light effect */
padding: 8px 12px;
border-radius: 24px; /* Pill shape */
display: flex;
gap: 16px;
align-items: center;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5), 0 0 20px rgba(0, 255, 0, 0.05);
transition: all 0.3s ease;
z-index: 1;
}
/* The running light (rotating gradient) */
.dock::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 250%; /* Make it large enough to cover the pill shape while rotating */
height: 250%;
background: conic-gradient(transparent, transparent, transparent, rgba(0, 255, 0, 1));
transform: translate(-50%, -50%);
animation: rotateLight 4s linear infinite;
z-index: -2;
}
/* The inner mask that creates the border thickness */
.dock::after {
content: '';
position: absolute;
top: 1px;
left: 1px;
right: 1px;
bottom: 1px;
background: rgba(20, 20, 20, 0.9);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-radius: 23px; /* Matches inner curve */
z-index: -1;
}
@keyframes rotateLight {
0% { transform: translate(-50%, -50%) rotate(0deg); }
100% { transform: translate(-50%, -50%) rotate(360deg); }
}
.dock-item-wrapper {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
z-index: 2; /* Keep above the inner mask */
}
.dock-item {
position: relative;
width: 48px;
height: 48px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 16px;
color: var(--text-secondary);
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
text-decoration: none;
}
.dock-item:hover {
color: var(--text-primary);
background: rgba(255, 255, 255, 0.08);
transform: translateY(-8px) scale(1.15);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
}
.dock-item.active {
color: var(--accent-color);
background: rgba(0, 255, 0, 0.1);
}
.dock-item.active::after {
content: '';
position: absolute;
bottom: -4px;
width: 4px;
height: 4px;
border-radius: 50%;
background-color: var(--accent-color);
box-shadow: 0 0 10px var(--accent-color);
}
/* Tooltip */
.dock-tooltip {
position: absolute;
top: -45px;
background: rgba(10, 10, 10, 0.9);
color: #fff;
padding: 6px 12px;
border-radius: 8px;
font-size: 0.8rem;
font-weight: 500;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transform: translateY(10px);
transition: all 0.3s ease;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.dock-item:hover ~ .dock-tooltip,
.dock-item-wrapper:hover .dock-tooltip {
opacity: 1;
transform: translateY(0);
}
@@ -0,0 +1,49 @@
import React from 'react';
import { Link, useLocation } from 'react-router-dom';
import { Home, Lightbulb, FileText, Settings, Lock } from 'lucide-react';
import { useAuth } from '../contexts/AuthContext';
import './BottomDock.css';
export default function BottomDock() {
const location = useLocation();
const { session, role } = useAuth();
const publicItems = [
{ label: 'Home', path: '/', icon: <Home size={22} /> },
{ label: 'Use Cases', path: '/usecases', icon: <Lightbulb size={22} /> },
{ label: 'Zugang anfragen', path: '/request-access', icon: <FileText size={22} /> },
];
// Both investor and admin can see the locked area
const investorItems = session
? [{ label: 'Investor Area', path: '/restricted', icon: <Lock size={22} /> }]
: [];
// Only admin sees the admin panel
const adminItems = role === 'admin'
? [{ label: 'Admin', path: '/admin', icon: <Settings size={22} /> }]
: [];
const navItems = [...publicItems, ...investorItems, ...adminItems];
return (
<div className="dock-container">
<nav className="dock">
{navItems.map((item, index) => {
const isActive = location.pathname === item.path;
return (
<div className="dock-item-wrapper" key={index}>
<span className="dock-tooltip">{item.label}</span>
<Link
to={item.path}
className={`dock-item ${isActive ? 'active' : ''}`}
>
{item.icon}
</Link>
</div>
);
})}
</nav>
</div>
);
}
@@ -0,0 +1,68 @@
import React from 'react';
import './Marquee.css';
// Import logos via Vite asset pipeline for robustness
import arvosLogo from '../assets/logos/arvos.svg';
import estererLogo from '../assets/logos/esterer.svg';
import mtoLogo from '../assets/logos/Logo-MTO.png';
import beierLogo from '../assets/logos/Paul-Beier-GmbH-Logo-white.png';
import sikaLogo from '../assets/logos/sika-logo.png';
import hexagonLogo from '../assets/logos/hexagon-purus-logo-4aa648.svg';
export default function Customers() {
const customerLogos = [
{ src: arvosLogo, link: 'https://www.arvos-group.com/', styleType: 'white-bg' },
{ src: estererLogo, link: 'https://www.esterer.de/', styleType: 'white-bg' },
{ src: mtoLogo, link: 'https://www.mto-kassel.de/', styleType: 'original' },
{ src: beierLogo, link: 'https://www.paul-beier.de/', styleType: 'original' },
{ src: sikaLogo, link: 'https://www.sika.com/', styleType: 'original' },
{ src: hexagonLogo, link: 'https://www.hexagonpurus.com/', styleType: 'original' },
];
// Triple the array for smooth infinite scrolling
const marqueeItems = [...customerLogos, ...customerLogos, ...customerLogos];
return (
<section id="kunden" className="marquee-section">
<div className="section-header">
<h2 className="section-title" style={{fontSize: '2rem', marginBottom: '40px'}}>Unsere <span className="text-gradient">Kunden</span></h2>
</div>
<div className="marquee-fade-left"></div>
<div className="marquee-fade-right"></div>
<div className="marquee-container">
<div className="marquee-content">
{marqueeItems.map((item, index) => {
const isWhiteBg = item.styleType === 'white-bg';
const isMonochrome = item.styleType === 'monochrome';
return (
<a
href={item.link}
target="_blank"
rel="noopener noreferrer"
className={`marquee-item logo-item ${isWhiteBg ? 'logo-white-bg' : ''}`}
key={index}
>
<img
src={item.src}
alt="Kundenlogo"
style={{
height: '40px',
width: 'auto',
display: 'block',
filter: isMonochrome ? 'brightness(0) invert(1)' : 'none',
transition: 'transform 0.3s ease'
}}
onMouseOver={(e) => e.currentTarget.style.transform = 'scale(1.1)'}
onMouseOut={(e) => e.currentTarget.style.transform = 'scale(1)'}
/>
</a>
);
})}
</div>
</div>
</section>
);
}
@@ -0,0 +1,70 @@
.footer-section {
padding: 80px 0 40px 0;
background-color: var(--bg-color);
}
.cta-container {
margin-bottom: 80px;
}
.cta-content {
text-align: center;
padding: 80px 40px;
background: linear-gradient(145deg, rgba(25, 25, 25, 0.8), rgba(10, 10, 10, 0.9));
border: 1px solid rgba(16, 185, 129, 0.2);
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5), inset 0 0 0 1px rgba(255, 255, 255, 0.05);
}
.cta-content h2 {
font-size: 2.5rem;
margin-bottom: 20px;
color: #fff;
}
.cta-content p {
color: var(--text-secondary);
font-size: 1.2rem;
max-width: 600px;
margin: 0 auto 40px auto;
}
.footer-bottom {
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid rgba(255, 255, 255, 0.05);
padding-top: 40px;
}
.footer-logo {
font-size: 1.5rem;
font-weight: 800;
letter-spacing: -1px;
}
.footer-links {
display: flex;
gap: 24px;
}
.footer-links a {
color: var(--text-secondary);
font-weight: 400;
}
.footer-links a:hover {
color: #fff;
}
.footer-copyright {
color: var(--text-secondary);
font-size: 0.9rem;
}
@media (max-width: 768px) {
.footer-bottom {
flex-direction: column;
gap: 20px;
text-align: center;
}
}
@@ -0,0 +1,30 @@
import React from 'react';
import { Link } from 'react-router-dom';
import './Footer.css';
export default function Footer() {
return (
<footer id="kontakt" className="footer-section">
<div className="cta-container app-container">
<div className="cta-content glass-panel">
<h2>Ihr habt ein spannendes Projekt?</h2>
<p>Lasst uns gemeinsam herausfinden, wie wir eure Prozesse intelligenter machen können.</p>
<a href="mailto:KIQ@d-hive.de" className="primary-button">Jetzt Kontakt aufnehmen</a>
</div>
</div>
<div className="footer-bottom app-container">
<div className="footer-logo">
<img src="/logos/d-Hive_logo_transparent_crop.png" alt="d-HIVE" style={{ height: '50px', objectFit: 'contain' }} />
</div>
<div className="footer-links">
<Link to="/impressum">Impressum</Link>
<Link to="/datenschutz">Datenschutz</Link>
</div>
<div className="footer-copyright">
&copy; {new Date().getFullYear()} dHIVE. Alle Rechte vorbehalten.
</div>
</div>
</footer>
);
}
@@ -0,0 +1,32 @@
import React from 'react';
export default function Gefoerdert() {
return (
<section className="app-container" style={{ padding: '60px 20px', textAlign: 'center', borderTop: '1px solid rgba(255, 255, 255, 0.05)' }}>
<h3 style={{ fontSize: '1.2rem', color: 'var(--text-secondary)', marginBottom: '30px', fontWeight: '600', letterSpacing: '1px', textTransform: 'uppercase' }}>Gefördert durch</h3>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<a
href="https://digitales.hessen.de/foerderprogramme/distral"
target="_blank"
rel="noopener noreferrer"
style={{
display: 'inline-block',
background: '#ffffff',
padding: '15px 30px',
borderRadius: '16px',
transition: 'transform 0.3s ease',
textDecoration: 'none'
}}
onMouseOver={(e) => e.currentTarget.style.transform = 'scale(1.05)'}
onMouseOut={(e) => e.currentTarget.style.transform = 'scale(1)'}
>
<img
src="/logos/download-logo-distral-data.png"
alt="Gefördert durch distral"
style={{ maxHeight: '90px', maxWidth: '300px', objectFit: 'contain', display: 'block' }}
/>
</a>
</div>
</section>
);
}
@@ -0,0 +1,56 @@
.hero {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
text-align: center;
padding-top: 80px;
}
.hero-content {
position: relative;
z-index: 10;
max-width: 900px;
animation: fadeUp 1s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.hero h1 {
font-size: clamp(2.5rem, 6vw, 4.5rem);
line-height: 1.1;
margin-bottom: 24px;
}
.text-gradient {
background: var(--accent-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
display: inline-block;
}
.hero-subtext {
font-size: clamp(1.1rem, 2vw, 1.3rem);
color: var(--text-secondary);
max-width: 700px;
margin: 0 auto 40px auto;
line-height: 1.6;
}
.background-mesh {
position: absolute;
top: -20%;
left: 50%;
transform: translateX(-50%);
width: 1000px;
height: 600px;
background: radial-gradient(circle, rgba(16, 185, 129, 0.15) 0%, rgba(10, 10, 10, 0) 70%);
z-index: 1;
pointer-events: none;
}
@keyframes fadeUp {
0% { opacity: 0; transform: translateY(30px); }
100% { opacity: 1; transform: translateY(0); }
}
@@ -0,0 +1,24 @@
import React from 'react';
import './Hero.css';
export default function Hero() {
return (
<section className="hero">
<div className="hero-glow background-mesh"></div>
<div className="app-container hero-content">
<h1>
Das souveräne KI-Betriebssystem <br />
<span className="text-gradient">für den deutschen Mittelstand</span>
</h1>
<p className="hero-subtext">
Modernste KI-Anwendungen, souverän komplett aus Deutschland.
Völlig unabhängig von amerikanischen Hyperscalern oder chinesischem Big-Tech.
Höchste Performance trifft auf digitale Souveränität.
</p>
<div className="hero-actions">
<a href="/request-access" className="primary-button">Access Request</a>
</div>
</div>
</section>
);
}
@@ -0,0 +1,78 @@
.marquee-section {
position: relative;
width: 100%;
padding: 80px 0;
background-color: var(--bg-color); /* pure black */
overflow: hidden;
border-top: 1px solid rgba(255, 255, 255, 0.03);
border-bottom: 1px solid rgba(255, 255, 255, 0.03);
}
.marquee-fade-left, .marquee-fade-right {
position: absolute;
top: 0;
width: 15%;
height: 100%;
z-index: 2;
pointer-events: none;
}
.marquee-fade-left {
left: 0;
background: linear-gradient(to right, var(--bg-color), transparent);
}
.marquee-fade-right {
right: 0;
background: linear-gradient(to left, var(--bg-color), transparent);
}
.marquee-container {
width: 100%;
overflow: hidden;
}
.marquee-content {
display: flex;
width: fit-content;
animation: scroll 30s linear infinite;
align-items: center;
}
.marquee-item {
margin: 0 40px;
white-space: nowrap;
transition: transform 0.4s ease;
}
.logo-item img {
max-height: 60px;
max-width: 200px;
object-fit: contain;
transition: all 0.4s ease;
opacity: 0.8; /* Base opacity for all */
}
.logo-white-bg {
background: #ffffff;
padding: 10px 18px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
}
.logo-white-bg img {
opacity: 1 !important;
filter: none !important;
}
.marquee-item:hover img {
opacity: 1;
}
@keyframes scroll {
0% { transform: translateX(0); }
100% { transform: translateX(-33.33%); }
}
@@ -0,0 +1,28 @@
import React from 'react';
export default function Memberships() {
const membershipLogos = [
{ src: 'DE_KI-Verband-Logo_weiss-scaled.png', link: 'https://ki-verband.de/' },
{ src: 'GKS_Logo.jpg', link: 'https://gemeinsamklimaschuetzen.de/' },
{ src: 'science-park-kassel-logo.png', link: 'https://www.sciencepark-kassel.de/' }
];
return (
<section id="netzwerk" className="app-container" style={{ padding: '60px 20px', textAlign: 'center' }}>
<h2 style={{ fontSize: '2rem', marginBottom: '40px' }}>Mitgliedschaften & <span className="text-gradient">Partnerschaften</span></h2>
<div style={{ display: 'flex', justifyContent: 'center', gap: '60px', flexWrap: 'wrap', alignItems: 'center' }}>
{membershipLogos.map((item, index) => (
<a key={index} href={item.link} target="_blank" rel="noopener noreferrer" style={{ display: 'block' }}>
<img
src={`/logos/${item.src}`}
alt="Mitgliedschaft / Partner"
style={{ maxHeight: '80px', maxWidth: '200px', objectFit: 'contain', transition: 'transform 0.3s ease' }}
onMouseOver={(e) => e.currentTarget.style.transform = 'scale(1.1)'}
onMouseOut={(e) => e.currentTarget.style.transform = 'scale(1)'}
/>
</a>
))}
</div>
</section>
);
}
@@ -0,0 +1,116 @@
.navigation {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 1000;
transition: all 0.3s ease;
padding: 20px 0;
}
.navigation.scrolled {
padding: 15px 0;
border-bottom: 1px solid var(--glass-border);
border-radius: 0;
backdrop-filter: blur(12px);
background: rgba(10, 10, 10, 0.8);
}
.nav-container {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo-image {
height: 70px;
width: auto;
object-fit: contain;
}
/* Hamburger Menu */
.hamburger {
display: flex;
flex-direction: column;
gap: 6px;
background: transparent;
border: none;
cursor: pointer;
z-index: 1001; /* Above overlay */
padding: 10px;
}
.hamburger span {
display: block;
width: 30px;
height: 2px;
background-color: var(--text-primary);
transition: all 0.3s ease;
}
.hamburger.active span:nth-child(1) {
transform: translateY(8px) rotate(45deg);
}
.hamburger.active span:nth-child(2) {
opacity: 0;
}
.hamburger.active span:nth-child(3) {
transform: translateY(-8px) rotate(-45deg);
}
/* Overlay Navigation */
.nav-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
background: rgba(10, 10, 10, 0.95);
backdrop-filter: blur(15px);
-webkit-backdrop-filter: blur(15px);
z-index: 999; /* Below header */
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
visibility: hidden;
transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.nav-overlay.open {
opacity: 1;
visibility: visible;
}
.overlay-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 40px;
}
.overlay-link {
font-size: 3rem;
font-weight: 800;
color: var(--text-primary);
text-decoration: none;
opacity: 0;
transform: translateY(20px);
transition: all 0.3s ease;
}
.nav-overlay.open .overlay-link {
opacity: 1;
transform: translateY(0);
}
.nav-overlay.open .overlay-link:nth-child(1) { transition-delay: 0.1s; }
.nav-overlay.open .overlay-link:nth-child(2) { transition-delay: 0.2s; }
.nav-overlay.open .overlay-link:nth-child(3) { transition-delay: 0.3s; }
.nav-overlay.open .overlay-link:nth-child(4) { transition-delay: 0.4s; }
.overlay-link:hover {
color: var(--accent-color);
}
@@ -0,0 +1,57 @@
import React, { useEffect, useState } from 'react';
import { Link, useLocation } from 'react-router-dom';
import './Navigation.css';
export default function Navigation() {
const [scrolled, setScrolled] = useState(false);
const [isOverlayOpen, setIsOverlayOpen] = useState(false);
const location = useLocation();
useEffect(() => {
const handleScroll = () => {
setScrolled(window.scrollY > 50);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
// Close overlay on route change — use a layout effect to avoid setState-in-body lint
const prevPathRef = React.useRef(location.pathname);
React.useLayoutEffect(() => {
if (prevPathRef.current !== location.pathname) {
prevPathRef.current = location.pathname;
setIsOverlayOpen(false);
}
}, [location.pathname]);
return (
<>
<nav className={`navigation ${scrolled ? 'scrolled glass-panel' : ''}`}>
<div className="nav-container app-container">
<Link to="/" className="logo">
<img src="/logos/logo-dHIVE-2x.webp" alt="d-HIVE Logo" className="logo-image" />
</Link>
<button
className={`hamburger ${isOverlayOpen ? 'active' : ''}`}
onClick={() => setIsOverlayOpen(!isOverlayOpen)}
aria-label="Toggle menu"
>
<span></span>
<span></span>
<span></span>
</button>
</div>
</nav>
{/* Overlay Menu */}
<div className={`nav-overlay ${isOverlayOpen ? 'open' : ''}`}>
<div className="overlay-content">
<Link to="/" className="overlay-link" onClick={() => setIsOverlayOpen(false)}>Home</Link>
<Link to="/usecases" className="overlay-link" onClick={() => setIsOverlayOpen(false)}>Use Cases</Link>
<Link to="/request-access" className="overlay-link" onClick={() => setIsOverlayOpen(false)}>Request Access</Link>
</div>
</div>
</>
);
}
@@ -0,0 +1,61 @@
.platform-usp {
padding: 100px 0;
position: relative;
overflow: hidden;
}
.usp-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin-top: 60px;
}
.usp-card {
padding: 40px;
border-radius: 24px;
display: flex;
flex-direction: column;
transition: transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
background-size: cover;
background-position: center;
min-height: 380px;
border: 1px solid rgba(255, 255, 255, 0.1);
justify-content: flex-end; /* Align content to bottom for a modern look */
}
.usp-card:hover {
transform: translateY(-8px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5);
border-color: var(--accent-color);
}
.usp-card h3 {
font-size: 1.6rem;
margin-bottom: 20px;
color: #fff;
text-shadow: 0 2px 4px rgba(0,0,0,0.5);
}
.usp-card ul {
list-style: none;
padding: 0;
margin: 0;
}
.usp-card li {
color: #efefef; /* Slightly brighter for readability against dark overlay */
line-height: 1.6;
margin-bottom: 12px;
padding-left: 24px;
position: relative;
font-size: 0.95rem;
text-shadow: 0 1px 2px rgba(0,0,0,0.5);
}
.usp-card li::before {
content: '→';
position: absolute;
left: 0;
color: var(--accent-color);
}
@@ -0,0 +1,72 @@
import React from 'react';
import './PlatformUSP.css';
// Import background images
import devBg from '../assets/usp/developer.png';
import bizBg from '../assets/usp/business.png';
import sovBg from '../assets/usp/sovereignty.png';
const features = [
{
title: 'Für Entwickler: Speed & Participation',
points: [
'Maximale Liefergeschwindigkeit durch fertige Infrastruktur.',
'Direkte Beteiligung am Erfolg jedes Use-Cases (Shared Benefit).',
'Die besten Entwickler Deutschlands bündeln ihre Kraft.'
],
bgImage: devBg
},
{
title: 'Für Unternehmen: Time-to-Value',
points: [
'Use-Cases sofort sehen und für sich nutzbar machen.',
'Kein IT-Overhead, kein Setup-Stress.',
'Direkter Zugang zu High-End KI-Expertise.'
],
bgImage: bizBg
},
{
title: '100% Souveränität',
points: [
'Komplett Made in Germany.',
'Keine Abhängigkeit von US-Hyperscalern.',
'Kein chinesisches Big-Tech.',
'Sicherer Hafen für sensibelste Unternehmensdaten.'
],
bgImage: sovBg
}
];
export default function PlatformUSP() {
return (
<section className="platform-usp">
<div className="app-container">
<div className="section-header">
<h2 className="section-title">Die <span className="text-gradient">Plattform-Vision</span></h2>
<p className="section-subtitle">
Wo die besten Entwickler für die besten Unternehmen bauen und alle direkt profitieren.
</p>
</div>
<div className="usp-grid">
{features.map((f, i) => (
<div
key={i}
className="usp-card glass-panel"
style={{
backgroundImage: `linear-gradient(rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.85)), url(${f.bgImage})`
}}
>
<h3>{f.title}</h3>
<ul>
{f.points.map((p, j) => (
<li key={j}>{p}</li>
))}
</ul>
</div>
))}
</div>
</div>
</section>
);
}
@@ -0,0 +1,28 @@
import React from 'react';
import { Navigate, useLocation } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
/**
* ProtectedRoute: checks session via AuthContext.
* allowedRoles: array of roles that can access this route.
* If no session, redirects to /login.
* If role set but not allowed, redirects to /.
*/
export default function ProtectedRoute({ children, allowedRoles }) {
const { session, role, loading } = useAuth();
const location = useLocation();
if (loading) {
return <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh', color: '#00FF00', fontFamily: 'monospace' }}>Authentifizierung läuft...</div>;
}
if (!session) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
if (allowedRoles && !allowedRoles.includes(role)) {
return <Navigate to="/" replace />;
}
return children;
}
@@ -0,0 +1,16 @@
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
/**
* ScrollToTop component ensures that the page scroll position
* is reset to (0, 0) whenever the route changes.
*/
export default function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
@@ -0,0 +1,93 @@
.services-section {
padding: 120px 20px;
}
.section-header {
text-align: center;
max-width: 700px;
margin: 0 auto 60px auto;
}
.section-title {
font-size: 3rem;
margin-bottom: 20px;
}
.section-subtitle {
color: var(--text-secondary);
font-size: 1.1rem;
line-height: 1.6;
}
.bento-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
max-width: 1000px;
margin: 0 auto;
}
.bento-item {
position: relative;
border-radius: 24px;
padding: 40px;
display: flex;
flex-direction: column;
justify-content: flex-end;
min-height: 300px;
overflow: hidden;
transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
border: 1px solid rgba(255, 255, 255, 0.05);
}
.bento-item.span-2 {
grid-column: span 2;
}
.bento-content {
position: relative;
z-index: 2;
}
.bento-title {
font-size: 1.5rem;
margin-bottom: 12px;
color: #fff;
}
.bento-text {
color: var(--text-secondary);
line-height: 1.6;
font-size: 1rem;
margin: 0;
}
.bento-glow {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(circle at 100% 100%, rgba(16, 185, 129, 0.1), transparent 50%);
opacity: 0;
transition: opacity 0.4s ease;
z-index: 1;
}
.bento-item:hover {
transform: translateY(-8px);
border-color: rgba(16, 185, 129, 0.3);
}
.bento-item:hover .bento-glow {
opacity: 1;
}
@media (max-width: 768px) {
.bento-grid {
grid-template-columns: 1fr;
}
.bento-item.span-2 {
grid-column: span 1;
}
}
@@ -0,0 +1,61 @@
import React from 'react';
import './Services.css';
export default function Services() {
const services = [
{
title: 'Datenschutz & Souveränität',
desc: 'Wir garantieren 100% DSGVO- und AI-Act konforme KI-Lösungen ohne Lock-in durch US-Hyperscaler wie OpenAI oder Google.',
bg: '/images/automation_bg.png', // Temporary placeholder visuals
span: 'span-2'
},
{
title: 'Plug and Play KI',
desc: 'Kein Ressourcenmangel mehr. Out-of-the-box Infrastruktur für smarte Anwendungsfälle.',
bg: '/images/ai_bg.png',
span: ''
},
{
title: 'Netzwerk & Forschung',
desc: 'Partnerschaft zwischen Unternehmen und Universität Kassel für modernste Begleitung.',
bg: '/images/workshops_bg.png',
span: ''
},
{
title: 'Vorausschauende Wartung & Automatisierung',
desc: 'Nutzen Sie Custom GPTs für B2B: Von Predictive Maintenance bis komplexe Dokumentenverarbeitung out-of-the-box.',
bg: '/images/transformation_bg.png',
span: 'span-2'
}
];
return (
<section id="leistungen" className="services-section app-container">
<div className="section-header">
<h2 className="section-title">Unsere <span className="text-gradient">Lösung</span></h2>
<p className="section-subtitle">Project KIQ liefert die gesamte KI-Infrastruktur out-of-the-box. Radikaler Fokus auf den Use Case.</p>
</div>
<div className="bento-grid">
{services.map((service, index) => (
<div
key={index}
className={`bento-item glass-panel ${service.span}`}
style={{
backgroundImage: `linear-gradient(to bottom, rgba(10, 10, 10, 0.4), rgba(10, 10, 10, 0.95)), url(${service.bg})`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat'
}}
>
<div className="bento-content">
<h3 className="bento-title">{service.title}</h3>
<p className="bento-text">{service.desc}</p>
</div>
<div className="bento-glow"></div>
</div>
))}
</div>
</section>
);
}
@@ -0,0 +1,59 @@
.cube-section {
display: flex;
justify-content: center;
align-items: center;
padding: 100px 20px 150px 20px;
perspective: 1200px;
}
.cube-container {
width: 300px;
height: 300px;
position: relative;
transform-style: preserve-3d;
animation: spinCube 16s infinite cubic-bezier(0.25, 0.1, 0.25, 1);
}
.cube-face {
position: absolute;
width: 300px;
height: 300px;
background: rgba(15, 15, 15, 0.95); /* solid background prevents seeing through nicely */
border: 1px solid rgba(0, 255, 0, 0.2);
backdrop-filter: blur(10px);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
box-shadow: 0 0 40px rgba(0, 255, 0, 0.1) inset, 0 10px 40px rgba(0, 0, 0, 0.5);
backface-visibility: hidden; /* Hide faces turned away to prevent jumbled mess if translucent */
}
/* 3D Geometry */
.face-1 { transform: rotateY(0deg) translateZ(150px); }
.face-2 { transform: rotateY(90deg) translateZ(150px); }
.face-3 { transform: rotateY(180deg) translateZ(150px); }
.face-4 { transform: rotateY(-90deg) translateZ(150px); }
@keyframes spinCube {
0%, 15% { transform: rotateY(0deg); }
25%, 40% { transform: rotateY(-90deg); }
50%, 65% { transform: rotateY(-180deg); }
75%, 90% { transform: rotateY(-270deg); }
100% { transform: rotateY(-360deg); }
}
.stat-value {
font-size: 4rem;
font-weight: 800;
margin-bottom: 10px;
color: #fff;
filter: drop-shadow(0 0 15px rgba(0, 255, 0, 0.5));
}
.stat-label {
font-size: 1.5rem;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 2px;
}
@@ -0,0 +1,24 @@
import React from 'react';
import './Stats.css';
export default function Stats() {
const stats = [
{ value: '6', label: 'Pilotkunden', faceClass: 'face-1' },
{ value: '100%', label: 'DSGVO & AI-Act', faceClass: 'face-2' },
{ value: '6', label: 'Use Cases live', faceClass: 'face-3' },
{ value: 'EU', label: 'Hosting', faceClass: 'face-4' },
];
return (
<section className="cube-section app-container">
<div className="cube-container">
{stats.map((stat, i) => (
<div className={`cube-face ${stat.faceClass}`} key={i}>
<div className="stat-value text-gradient">{stat.value}</div>
<div className="stat-label">{stat.label}</div>
</div>
))}
</div>
</section>
);
}
@@ -0,0 +1,49 @@
import React from 'react';
export default function Team() {
return (
<section className="app-container" style={{ padding: '40px 20px 100px 20px' }}>
<div className="glass-panel" style={{ padding: '60px', borderRadius: '24px', border: '1px solid rgba(0, 255, 0, 0.1)' }}>
<h2 style={{ fontSize: '2rem', marginBottom: '20px', color: '#fff' }}>Das sind wir</h2>
<p style={{ color: 'var(--text-secondary)', lineHeight: '1.8', marginBottom: '30px', fontSize: '1.05rem', maxWidth: '800px' }}>
Unser Team setzt sich zusammen aus Problemlösern mit wissenschaftlichen Hintergründen in den Bereichen der Regelungs- und Elektrotechnik, Mechatronik, Maschinenbau, Informatik und Physik. Und wir alle haben uns in irgendeiner Form dem Generieren von Wissen durch das Erheben und Verarbeiten von Daten verschrieben.
<br/><br/>
Stellvertretend für unser Team stellen wir hier unsere Gründer vor: André unser KI, Leadership und Sensorik-Experte und Alex unser Mess-, Regelungs und Automatisierungs-Experte. Die beiden begleiten unsere Kunden und unsere internen und externen Mitarbeitenden durch die Projekte.
</p>
{/* Founders Grid */}
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(340px, 1fr))', gap: '40px', marginTop: '60px' }}>
{/* André */}
<div style={{ background: 'rgba(255,255,255,0.02)', padding: '40px', borderRadius: '20px', border: '1px solid rgba(255,255,255,0.05)' }}>
<h3 style={{ fontSize: '1.8rem', color: '#fff', marginBottom: '5px' }}>Dr. André Knie</h3>
<p style={{ color: 'var(--accent-color)', fontWeight: '600', marginBottom: '25px', fontSize: '1.1rem' }}>Mensch-Maschine-Moderator</p>
<div style={{ fontStyle: 'italic', color: '#ccc', borderLeft: '3px solid var(--accent-color)', paddingLeft: '15px', marginBottom: '20px', lineHeight: '1.7' }}>
Die Angst vor Technologie und Veränderung lähmt viele Organisationen und Menschen. Ich helfe dabei, diese Ängste zu überwinden, und gestalte Technologie sowie Change-Prozesse so, dass sie echten Mehrwert schaffen und Begeisterung auslösen."
</div>
<p style={{ color: 'var(--text-secondary)', lineHeight: '1.7', marginBottom: '15px' }}>
Als promovierter Physiker betrachte ich Systeme und Zusammenhänge aus einer einzigartigen Perspektive.
</p>
</div>
{/* Alex */}
<div style={{ background: 'rgba(255,255,255,0.02)', padding: '40px', borderRadius: '20px', border: '1px solid rgba(255,255,255,0.05)' }}>
<h3 style={{ fontSize: '1.8rem', color: '#fff', marginBottom: '5px' }}>Dr.-Ing. Alexander Schrodt</h3>
<p style={{ color: 'var(--accent-color)', fontWeight: '600', marginBottom: '25px', fontSize: '1.1rem' }}>Automatisierungs-Analyse-Architekt</p>
<div style={{ fontStyle: 'italic', color: '#ccc', borderLeft: '3px solid var(--accent-color)', paddingLeft: '15px', marginBottom: '20px', lineHeight: '1.7' }}>
„Ich führe die KI-Diskussion über Buzzwords hinaus: Für mich ist KI keine universelle Antwort, sondern ein hochspezialisiertes Werkzeug, um in technischen Systemen echten, messbaren Mehrwert zu schaffen."
</div>
<p style={{ color: 'var(--text-secondary)', lineHeight: '1.7', marginBottom: '15px' }}>
Mein Fundament als promovierter Regelungstechniker ist die Systemtheorie. Ich verstehe, wie technische Prozesse im Kern funktionieren, wie sie stabil designt und sicher betrieben werden.
</p>
</div>
</div>
</div>
</section>
);
}
@@ -0,0 +1,62 @@
.uct-section {
padding: 100px 20px;
}
.uct-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 20px;
margin-top: 48px;
}
.uct-card {
padding: 28px;
border-radius: 18px;
display: flex;
flex-direction: column;
gap: 12px;
transition: border-color 0.25s, transform 0.25s;
}
.uct-card:hover {
border-color: rgba(0, 255, 0, 0.25);
transform: translateY(-3px);
}
.uct-icon {
font-size: 1.8rem;
line-height: 1;
}
.uct-title {
font-size: 1.15rem;
color: #fff;
margin: 0;
}
.uct-summary {
color: var(--text-secondary);
font-size: 0.88rem;
line-height: 1.6;
margin: 0;
flex: 1;
}
.uct-status {
display: inline-block;
align-self: flex-start;
background: rgba(0,255,0,0.08);
border: 1px solid rgba(0,255,0,0.2);
color: var(--accent-color);
font-size: 0.73rem;
font-weight: 700;
padding: 3px 10px;
border-radius: 50px;
letter-spacing: 0.06em;
}
.uct-cta {
display: flex;
justify-content: center;
margin-top: 48px;
}
@@ -0,0 +1,34 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { USE_CASES } from '../data/useCases';
import './UseCasesTeaser.css';
export default function UseCasesTeaser() {
return (
<section className="uct-section app-container">
<div className="section-header">
<h2 className="section-title">Bewiesene <span className="text-gradient">Use Cases</span></h2>
<p className="section-subtitle">
Fünf KI-Agenten, bereits im Einsatz bei Praxispartnern aus der Region Kassel.
</p>
</div>
<div className="uct-grid">
{USE_CASES.map((uc) => (
<div key={uc.id} className="uct-card glass-panel">
<div className="uct-icon">{uc.icon}</div>
<h3 className="uct-title">{uc.title}</h3>
<p className="uct-summary">{uc.summary}</p>
<span className="uct-status"> Live</span>
</div>
))}
</div>
<div className="uct-cta">
<Link to="/usecases" className="primary-button">
Alle Use Cases ansehen
</Link>
</div>
</section>
);
}