feat(landing): Add landing Nuxt page

This commit is contained in:
2026-01-03 10:19:39 +01:00
parent 0803b59f0f
commit b3311155c7
28 changed files with 13620 additions and 0 deletions

24
landing/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist
# Node dependencies
node_modules
# Logs
logs
*.log
# Misc
.DS_Store
.fleet
.idea
# Local env files
.env
.env.*
!.env.example

1
landing/.npmrc Normal file
View File

@@ -0,0 +1 @@
shamefully-hoist=true

1
landing/.nvmrc Normal file
View File

@@ -0,0 +1 @@
22

7
landing/.prettierignore Normal file
View File

@@ -0,0 +1,7 @@
# Ignore artifacts:
build
coverage
.nuxt
.output
.api-client
pnpm-lock.yaml

6
landing/.prettierrc Normal file
View File

@@ -0,0 +1,6 @@
{
"trailingComma": "none",
"semi": false,
"singleQuote": true,
"printWidth": 120
}

75
landing/README.md Normal file
View File

@@ -0,0 +1,75 @@
# Nuxt Minimal Starter
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
## Setup
Make sure to install dependencies:
```bash
# npm
npm install
# pnpm
pnpm install
# yarn
yarn install
# bun
bun install
```
## Development Server
Start the development server on `http://localhost:3000`:
```bash
# npm
npm run dev
# pnpm
pnpm dev
# yarn
yarn dev
# bun
bun run dev
```
## Production
Build the application for production:
```bash
# npm
npm run build
# pnpm
pnpm build
# yarn
yarn build
# bun
bun run build
```
Locally preview production build:
```bash
# npm
npm run preview
# pnpm
pnpm preview
# yarn
yarn preview
# bun
bun run preview
```
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.

98
landing/app/app.config.ts Normal file
View File

@@ -0,0 +1,98 @@
export default defineAppConfig({
ui: {
// Primary color for the app - Teal
colors: {
primary: 'primary',
neutral: 'slate',
success: 'success',
warning: 'warning',
error: 'red'
},
// Button customizations
button: {
defaultVariants: {
size: 'lg'
}
},
// Card customizations with enhanced hover states
card: {
slots: {
root: 'rounded-2xl overflow-hidden transition-all duration-300'
}
},
// Page Hero customizations for landing page
pageHero: {
slots: {
root: 'relative isolate overflow-hidden',
title: 'font-heading text-5xl sm:text-6xl lg:text-7xl text-pretty tracking-tight font-bold text-highlighted',
description: 'text-lg sm:text-xl text-muted max-w-3xl'
}
},
// Page Section customizations
pageSection: {
slots: {
title: 'font-heading text-3xl sm:text-4xl lg:text-5xl text-pretty tracking-tight font-bold text-highlighted',
description: 'text-base sm:text-lg text-muted'
}
},
// Page Feature customizations
pageFeature: {
slots: {
title: 'text-lg font-semibold text-highlighted',
description: 'text-base text-muted'
}
},
// Page Card customizations
pageCard: {
slots: {
root: 'rounded-2xl transition-all duration-300',
title: 'text-lg font-semibold text-highlighted',
description: 'text-base text-muted'
}
},
// Page CTA customizations
pageCTA: {
slots: {
title: 'font-heading text-3xl sm:text-4xl text-pretty tracking-tight font-bold',
description: 'text-base sm:text-lg'
}
},
// Header customizations - transparent with blur
header: {
slots: {
root: 'bg-white/80 dark:bg-gray-950/80 backdrop-blur-xl border-b border-gray-200/50 dark:border-gray-800/50',
title: 'font-heading text-xl font-bold'
}
},
// Footer customizations
footer: {
slots: {
root: 'border-t border-gray-200 dark:border-gray-800'
}
},
// Input customizations for newsletter form
input: {
defaultVariants: {
size: 'lg'
}
},
// Accordion customizations
accordion: {
slots: {
trigger: 'text-base font-medium',
content: 'text-base text-muted'
}
}
}
})

222
landing/app/app.vue Normal file
View File

@@ -0,0 +1,222 @@
<template>
<UApp>
<!-- Skip link for accessibility -->
<a href="#main-content" class="skip-link"> Zum Hauptinhalt springen </a>
<NuxtRouteAnnouncer />
<!-- Header with transparent background and blur on scroll -->
<UHeader
:title="'LegalConsentHub'"
:ui="{
root: [
'fixed top-0 left-0 right-0 z-50 transition-all duration-300',
isScrolled
? 'bg-white/90 dark:bg-gray-950/90 backdrop-blur-xl shadow-sm border-b border-gray-200/50 dark:border-gray-800/50'
: 'bg-transparent border-transparent'
].join(' ')
}"
>
<template #title>
<NuxtLink to="/" class="flex items-center gap-3 group">
<div
class="w-10 h-10 rounded-xl bg-gradient-to-br from-primary-500 to-cyan-500 flex items-center justify-center shadow-lg shadow-primary-500/25 group-hover:shadow-primary-500/40 transition-shadow"
>
<UIcon name="i-lucide-scale" class="w-5 h-5 text-white" />
</div>
<span
class="font-heading text-xl font-bold bg-gradient-to-r from-primary-600 to-cyan-600 bg-clip-text text-transparent"
>
LegalConsentHub
</span>
</NuxtLink>
</template>
<UNavigationMenu
:items="navigationItems"
:ui="{
link: 'text-gray-700 dark:text-gray-200 hover:text-primary-600 dark:hover:text-primary-400 font-medium transition-colors'
}"
/>
<template #right>
<UColorModeButton
:ui="{
base: 'text-gray-600 dark:text-gray-300 hover:text-primary-600 dark:hover:text-primary-400'
}"
/>
<UButton
to="#newsletter"
class="btn-gradient px-5 py-2.5 rounded-xl font-semibold shadow-lg shadow-primary-500/25 hover:shadow-primary-500/40 transition-shadow"
>
<span>Informiert bleiben</span>
</UButton>
</template>
<template #body>
<UNavigationMenu
:items="navigationItems"
orientation="vertical"
class="-mx-2.5"
:ui="{
link: 'text-gray-700 dark:text-gray-200 hover:text-primary-600 dark:hover:text-primary-400 font-medium'
}"
/>
<div class="mt-6 pt-6 border-t border-gray-200 dark:border-gray-800">
<UButton to="#newsletter" size="lg" block class="btn-gradient rounded-xl font-semibold">
Informiert bleiben
</UButton>
</div>
</template>
</UHeader>
<!-- Main Content -->
<UMain id="main-content">
<NuxtPage />
</UMain>
<!-- Footer -->
<footer class="bg-gray-50 dark:bg-gray-900 border-t border-gray-200 dark:border-gray-800">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 lg:py-16">
<div class="grid grid-cols-1 md:grid-cols-4 gap-8 lg:gap-12">
<!-- Brand column -->
<div class="md:col-span-2">
<NuxtLink to="/" class="flex items-center gap-3 mb-4">
<div
class="w-10 h-10 rounded-xl bg-gradient-to-br from-primary-500 to-cyan-500 flex items-center justify-center"
>
<UIcon name="i-lucide-scale" class="w-5 h-5 text-white" />
</div>
<span class="font-heading text-xl font-bold text-gray-900 dark:text-white"> LegalConsentHub </span>
</NuxtLink>
<p class="text-gray-600 dark:text-gray-400 max-w-sm mb-6">
Digitale Mitbestimmung für IT- und KI-Systeme. Strukturierte Prozesse, revisionssichere Dokumentation.
</p>
<div class="flex items-center gap-3">
<UButton
icon="i-lucide-linkedin"
color="neutral"
variant="ghost"
to="https://linkedin.com"
target="_blank"
aria-label="LinkedIn"
class="hover:text-primary-600 dark:hover:text-primary-400"
/>
<UButton
icon="i-lucide-mail"
color="neutral"
variant="ghost"
to="mailto:info@legalconsenthub.de"
aria-label="E-Mail"
class="hover:text-primary-600 dark:hover:text-primary-400"
/>
</div>
</div>
<!-- Links column -->
<div>
<h4 class="font-heading font-bold text-gray-900 dark:text-white mb-4">Navigation</h4>
<ul class="space-y-3">
<li v-for="item in navigationItems" :key="item.label">
<NuxtLink
:to="item.to"
class="text-gray-600 dark:text-gray-400 hover:text-primary-600 dark:hover:text-primary-400 transition-colors"
>
{{ item.label }}
</NuxtLink>
</li>
</ul>
</div>
<!-- Legal column -->
<div>
<h4 class="font-heading font-bold text-gray-900 dark:text-white mb-4">Rechtliches</h4>
<ul class="space-y-3">
<li v-for="item in footerLinks" :key="item.label">
<NuxtLink
:to="item.to"
class="text-gray-600 dark:text-gray-400 hover:text-primary-600 dark:hover:text-primary-400 transition-colors"
>
{{ item.label }}
</NuxtLink>
</li>
</ul>
</div>
</div>
<!-- Bottom bar -->
<div class="mt-12 pt-8 border-t border-gray-200 dark:border-gray-800">
<div class="flex flex-col sm:flex-row justify-between items-center gap-4">
<p class="text-sm text-gray-500 dark:text-gray-400">
© {{ new Date().getFullYear() }} LegalConsentHub. Alle Rechte vorbehalten.
</p>
<div class="flex items-center gap-6 text-sm text-gray-500 dark:text-gray-400">
<div class="flex items-center gap-2">
<UIcon name="i-lucide-server" class="w-4 h-4" />
<span>Hosted in Germany</span>
</div>
<div class="flex items-center gap-2">
<UIcon name="i-lucide-shield-check" class="w-4 h-4" />
<span>DSGVO-konform</span>
</div>
</div>
</div>
</div>
</div>
</footer>
</UApp>
</template>
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
// Track scroll position for header styling
const isScrolled = ref(false)
onMounted(() => {
const handleScroll = () => {
isScrolled.value = window.scrollY > 50
}
window.addEventListener('scroll', handleScroll, { passive: true })
handleScroll() // Check initial position
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll)
})
})
const navigationItems = computed<NavigationMenuItem[]>(() => [
{
label: 'Für Betriebsräte',
to: '#betriebsraete'
},
{
label: 'Für Unternehmen',
to: '#unternehmen'
},
{
label: 'Features',
to: '#features'
},
{
label: 'Kontakt',
to: '#kontakt'
}
])
const footerLinks = computed<NavigationMenuItem[]>(() => [
{
label: 'Impressum',
to: '/impressum'
},
{
label: 'Datenschutz',
to: '/datenschutz'
},
{
label: 'AGB',
to: '/agb'
}
])
</script>

View File

@@ -0,0 +1,820 @@
@import 'tailwindcss';
@import '@nuxt/ui';
/* Theme configuration with new teal/cyan/violet palette */
@theme {
/* Typography - Bricolage Grotesque for headings, DM Sans for body */
--font-sans: 'DM Sans', system-ui, sans-serif;
--font-heading: 'Bricolage Grotesque', system-ui, sans-serif;
/* Primary color - Teal/Cyan gradient base */
--color-primary-50: #f0fdfa;
--color-primary-100: #ccfbf1;
--color-primary-200: #99f6e4;
--color-primary-300: #5eead4;
--color-primary-400: #2dd4bf;
--color-primary-500: #14b8a6;
--color-primary-600: #0d9488;
--color-primary-700: #0f766e;
--color-primary-800: #115e59;
--color-primary-900: #134e4a;
--color-primary-950: #042f2e;
/* Accent color - Violet for highlights */
--color-accent-50: #f5f3ff;
--color-accent-100: #ede9fe;
--color-accent-200: #ddd6fe;
--color-accent-300: #c4b5fd;
--color-accent-400: #a78bfa;
--color-accent-500: #8b5cf6;
--color-accent-600: #7c3aed;
--color-accent-700: #6d28d9;
--color-accent-800: #5b21b6;
--color-accent-900: #4c1d95;
--color-accent-950: #2e1065;
/* Cyan for gradient effects */
--color-cyan-50: #ecfeff;
--color-cyan-100: #cffafe;
--color-cyan-200: #a5f3fc;
--color-cyan-300: #67e8f9;
--color-cyan-400: #22d3ee;
--color-cyan-500: #06b6d4;
--color-cyan-600: #0891b2;
--color-cyan-700: #0e7490;
--color-cyan-800: #155e75;
--color-cyan-900: #164e63;
--color-cyan-950: #083344;
/* Success color - Emerald */
--color-success-50: #ecfdf5;
--color-success-100: #d1fae5;
--color-success-200: #a7f3d0;
--color-success-300: #6ee7b7;
--color-success-400: #34d399;
--color-success-500: #10b981;
--color-success-600: #059669;
--color-success-700: #047857;
--color-success-800: #065f46;
--color-success-900: #064e3b;
--color-success-950: #022c22;
/* Warning color - Amber */
--color-warning-50: #fffbeb;
--color-warning-100: #fef3c7;
--color-warning-200: #fde68a;
--color-warning-300: #fcd34d;
--color-warning-400: #fbbf24;
--color-warning-500: #f59e0b;
--color-warning-600: #d97706;
--color-warning-700: #b45309;
--color-warning-800: #92400e;
--color-warning-900: #78350f;
--color-warning-950: #451a03;
/* Animation durations */
--animate-duration-slow: 1s;
--animate-duration-normal: 0.5s;
--animate-duration-fast: 0.2s;
}
/* Heading styles with Bricolage Grotesque */
.font-heading {
font-family: var(--font-heading);
}
h1,
h2,
h3 {
font-family: var(--font-heading);
}
/* Gradient text effect */
.gradient-text {
background: linear-gradient(
135deg,
var(--color-primary-500) 0%,
var(--color-cyan-500) 50%,
var(--color-accent-500) 100%
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.gradient-text-teal {
background: linear-gradient(135deg, var(--color-primary-600) 0%, var(--color-cyan-500) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* ============================================
ANIMATIONS
============================================ */
/* Fade in up animation */
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Fade in animation */
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* Float animation for orbs */
@keyframes orb-float {
0%,
100% {
transform: translate(0, 0) scale(1);
}
25% {
transform: translate(10px, -20px) scale(1.05);
}
50% {
transform: translate(-5px, -10px) scale(0.95);
}
75% {
transform: translate(-15px, -25px) scale(1.02);
}
}
/* Slower float for large elements - more noticeable movement */
@keyframes float-slow {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-15px);
}
}
/* Fast float for small elements */
@keyframes float-fast {
0%,
100% {
transform: translateY(0) rotate(0deg);
}
50% {
transform: translateY(-10px) rotate(2deg);
}
}
/* Gradient rotation for backgrounds */
@keyframes gradient-rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* Shimmer effect */
@keyframes shimmer {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}
/* Pulse glow effect */
@keyframes pulse-glow {
0%,
100% {
opacity: 0.5;
transform: scale(1);
}
50% {
opacity: 0.8;
transform: scale(1.05);
}
}
/* Particle drift */
@keyframes particle-drift {
0% {
transform: translate(0, 0) rotate(0deg);
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
transform: translate(var(--drift-x, 100px), var(--drift-y, -100px)) rotate(360deg);
opacity: 0;
}
}
/* Scale in animation */
@keyframes scale-in {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
/* Border gradient animation */
@keyframes border-dance {
0%,
100% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
}
/* Count up number animation */
@keyframes count-pulse {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.02);
}
}
/* Tilt hover animation */
@keyframes tilt-hover {
0%,
100% {
transform: perspective(1000px) rotateX(0deg) rotateY(0deg);
}
25% {
transform: perspective(1000px) rotateX(2deg) rotateY(-2deg);
}
75% {
transform: perspective(1000px) rotateX(-2deg) rotateY(2deg);
}
}
/* ============================================
ANIMATION UTILITY CLASSES
============================================ */
.animate-fade-in-up {
animation: fade-in-up 0.7s ease-out forwards;
}
.animate-fade-in {
animation: fade-in 0.5s ease-out forwards;
}
.animate-orb-float {
animation: orb-float 20s ease-in-out infinite;
}
.animate-float-slow {
animation: float-slow 4s ease-in-out infinite;
}
.animate-float-fast {
animation: float-fast 4s ease-in-out infinite;
}
.animate-gradient-rotate {
animation: gradient-rotate 20s linear infinite;
}
.animate-shimmer {
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
background-size: 200% 100%;
animation: shimmer 2s infinite;
}
.animate-pulse-glow {
animation: pulse-glow 3s ease-in-out infinite;
}
.animate-scale-in {
animation: scale-in 0.6s ease-out forwards;
}
.animate-border-dance {
background-size: 200% 200%;
animation: border-dance 4s ease infinite;
}
.animate-tilt-hover {
animation: tilt-hover 6s ease-in-out infinite;
}
/* Staggered animation delays */
.delay-75 {
animation-delay: 75ms;
}
.delay-100 {
animation-delay: 100ms;
}
.delay-150 {
animation-delay: 150ms;
}
.delay-200 {
animation-delay: 200ms;
}
.delay-300 {
animation-delay: 300ms;
}
.delay-400 {
animation-delay: 400ms;
}
.delay-500 {
animation-delay: 500ms;
}
.delay-600 {
animation-delay: 600ms;
}
.delay-700 {
animation-delay: 700ms;
}
.delay-800 {
animation-delay: 800ms;
}
.delay-1000 {
animation-delay: 1000ms;
}
/* Initial state for animated elements */
.animate-on-scroll {
opacity: 0;
}
.animate-on-scroll.is-visible {
animation: fade-in-up 0.7s ease-out forwards;
}
/* ============================================
REDUCED MOTION SUPPORT
============================================ */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
.animate-orb-float,
.animate-float-slow,
.animate-float-fast,
.animate-gradient-rotate,
.animate-shimmer,
.animate-pulse-glow,
.animate-border-dance,
.animate-tilt-hover {
animation: none;
}
}
/* ============================================
GRADIENT BACKGROUNDS
============================================ */
/* Light hero gradient with orbs */
.hero-gradient-light {
background: linear-gradient(180deg, #ffffff 0%, #f0fdfa 50%, #ecfeff 100%);
}
/* Dark hero gradient */
.dark .hero-gradient-light {
background: linear-gradient(180deg, #0a0a0a 0%, #042f2e 50%, #083344 100%);
}
/* Gradient orb styles */
.gradient-orb {
position: absolute;
border-radius: 50%;
filter: blur(80px);
opacity: 0.6;
}
.gradient-orb-teal {
background: linear-gradient(135deg, var(--color-primary-400), var(--color-cyan-400));
}
.gradient-orb-violet {
background: linear-gradient(135deg, var(--color-accent-400), var(--color-accent-600));
}
.gradient-orb-cyan {
background: linear-gradient(135deg, var(--color-cyan-300), var(--color-primary-400));
}
.dark .gradient-orb {
opacity: 0.3;
}
/* Mesh gradient for CTAs */
.mesh-gradient-cta {
background:
radial-gradient(at 40% 20%, var(--color-primary-500) 0px, transparent 50%),
radial-gradient(at 80% 0%, var(--color-cyan-400) 0px, transparent 50%),
radial-gradient(at 0% 50%, var(--color-accent-500) 0px, transparent 50%),
radial-gradient(at 80% 50%, var(--color-primary-400) 0px, transparent 50%),
radial-gradient(at 0% 100%, var(--color-cyan-500) 0px, transparent 50%),
linear-gradient(135deg, var(--color-primary-600) 0%, var(--color-accent-600) 100%);
}
/* ============================================
GLASS MORPHISM
============================================ */
.glass {
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.3);
}
.dark .glass {
background: rgba(0, 0, 0, 0.4);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.glass-subtle {
background: rgba(255, 255, 255, 0.5);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.dark .glass-subtle {
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.05);
}
/* ============================================
CARD EFFECTS
============================================ */
/* Card with gradient border on hover */
.card-gradient-border {
position: relative;
background: white;
border-radius: 1rem;
transition: all 0.3s ease;
}
.card-gradient-border::before {
content: '';
position: absolute;
inset: -2px;
border-radius: inherit;
background: linear-gradient(135deg, var(--color-primary-400), var(--color-cyan-400), var(--color-accent-400));
opacity: 0;
transition: opacity 0.3s ease;
z-index: -1;
}
.card-gradient-border:hover::before {
opacity: 1;
}
.dark .card-gradient-border {
background: #1a1a1a;
}
/* Card hover lift effect - subtle version without clipping issues */
.card-hover-lift {
transition:
transform 0.3s ease,
box-shadow 0.3s ease;
}
.card-hover-lift:hover {
transform: translateY(-4px);
box-shadow:
0 10px 25px -5px rgba(0, 0, 0, 0.1),
0 8px 10px -6px rgba(0, 0, 0, 0.1);
}
/* Spotlight effect for cards */
.card-spotlight {
position: relative;
overflow: hidden;
}
.card-spotlight::after {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(
600px circle at var(--mouse-x, 50%) var(--mouse-y, 50%),
rgba(20, 184, 166, 0.1),
transparent 40%
);
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
}
.card-spotlight:hover::after {
opacity: 1;
}
/* 3D tilt effect */
.card-3d {
transform-style: preserve-3d;
perspective: 1000px;
}
.card-3d-inner {
transition: transform 0.3s ease;
transform-style: preserve-3d;
}
.card-3d:hover .card-3d-inner {
transform: rotateX(5deg) rotateY(-5deg);
}
/* ============================================
BUTTON STYLES
============================================ */
/* Gradient button */
.btn-gradient {
background: linear-gradient(135deg, var(--color-primary-500) 0%, var(--color-cyan-500) 100%);
color: white;
border: none;
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
.btn-gradient::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(135deg, var(--color-cyan-500) 0%, var(--color-accent-500) 100%);
opacity: 0;
transition: opacity 0.3s ease;
}
.btn-gradient:hover::before {
opacity: 1;
}
.btn-gradient span {
position: relative;
z-index: 1;
}
/* Outline gradient button */
.btn-outline-gradient {
position: relative;
background: transparent;
border: 2px solid transparent;
background-image:
linear-gradient(white, white), linear-gradient(135deg, var(--color-primary-500), var(--color-cyan-500));
background-origin: border-box;
background-clip: padding-box, border-box;
transition: all 0.3s ease;
}
.dark .btn-outline-gradient {
background-image:
linear-gradient(#1a1a1a, #1a1a1a), linear-gradient(135deg, var(--color-primary-500), var(--color-cyan-500));
}
.btn-outline-gradient:hover {
background-image:
linear-gradient(var(--color-primary-50), var(--color-primary-50)),
linear-gradient(135deg, var(--color-primary-500), var(--color-cyan-500));
}
.dark .btn-outline-gradient:hover {
background-image:
linear-gradient(var(--color-primary-950), var(--color-primary-950)),
linear-gradient(135deg, var(--color-primary-500), var(--color-cyan-500));
}
/* ============================================
DECORATIVE ELEMENTS
============================================ */
/* Dots pattern */
.dots-pattern {
background-image: radial-gradient(var(--color-primary-200) 1px, transparent 1px);
background-size: 24px 24px;
}
.dark .dots-pattern {
background-image: radial-gradient(var(--color-primary-800) 1px, transparent 1px);
}
/* Grid pattern */
.grid-pattern {
background-image:
linear-gradient(to right, var(--color-primary-100) 1px, transparent 1px),
linear-gradient(to bottom, var(--color-primary-100) 1px, transparent 1px);
background-size: 40px 40px;
}
.dark .grid-pattern {
background-image:
linear-gradient(to right, var(--color-primary-900) 1px, transparent 1px),
linear-gradient(to bottom, var(--color-primary-900) 1px, transparent 1px);
}
/* Section divider with gradient */
.section-divider {
height: 1px;
background: linear-gradient(90deg, transparent, var(--color-primary-300), var(--color-cyan-300), transparent);
}
.dark .section-divider {
background: linear-gradient(90deg, transparent, var(--color-primary-700), var(--color-cyan-700), transparent);
}
/* Gradient line accent */
.gradient-line {
height: 4px;
border-radius: 2px;
background: linear-gradient(90deg, var(--color-primary-500), var(--color-cyan-500), var(--color-accent-500));
}
/* ============================================
ACCESSIBILITY
============================================ */
/* Focus styles */
:focus-visible {
outline: 2px solid var(--color-primary-500);
outline-offset: 2px;
}
/* Skip link */
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: var(--color-primary-600);
color: white;
padding: 8px 16px;
z-index: 100;
transition: top 0.3s;
border-radius: 0 0 8px 0;
}
.skip-link:focus {
top: 0;
}
/* ============================================
PARTICLES (for newsletter section)
============================================ */
.particle {
position: absolute;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--color-primary-400);
opacity: 0;
animation: particle-drift 8s ease-in-out infinite;
}
.particle:nth-child(1) {
--drift-x: 80px;
--drift-y: -120px;
animation-delay: 0s;
left: 10%;
top: 80%;
}
.particle:nth-child(2) {
--drift-x: -60px;
--drift-y: -100px;
animation-delay: 1s;
left: 20%;
top: 70%;
}
.particle:nth-child(3) {
--drift-x: 100px;
--drift-y: -80px;
animation-delay: 2s;
left: 80%;
top: 90%;
}
.particle:nth-child(4) {
--drift-x: -80px;
--drift-y: -140px;
animation-delay: 3s;
left: 70%;
top: 75%;
}
.particle:nth-child(5) {
--drift-x: 60px;
--drift-y: -90px;
animation-delay: 4s;
left: 30%;
top: 85%;
}
.particle:nth-child(6) {
--drift-x: -100px;
--drift-y: -110px;
animation-delay: 5s;
left: 90%;
top: 80%;
}
.particle:nth-child(7) {
--drift-x: 40px;
--drift-y: -130px;
animation-delay: 6s;
left: 50%;
top: 95%;
}
.particle:nth-child(8) {
--drift-x: -40px;
--drift-y: -70px;
animation-delay: 7s;
left: 40%;
top: 70%;
}
/* Particle color variations */
.particle:nth-child(odd) {
background: var(--color-cyan-400);
}
.particle:nth-child(3n) {
background: var(--color-accent-400);
width: 8px;
height: 8px;
}
/* ============================================
BENTO GRID
============================================ */
.bento-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: minmax(200px, auto);
gap: 1.5rem;
}
.bento-item-large {
grid-column: span 2;
grid-row: span 2;
}
.bento-item-tall {
grid-row: span 2;
}
.bento-item-wide {
grid-column: span 2;
}
@media (max-width: 1024px) {
.bento-grid {
grid-template-columns: repeat(2, 1fr);
}
.bento-item-large {
grid-column: span 2;
grid-row: span 1;
}
}
@media (max-width: 640px) {
.bento-grid {
grid-template-columns: 1fr;
}
.bento-item-large,
.bento-item-wide {
grid-column: span 1;
}
.bento-item-tall {
grid-row: span 1;
}
}

View File

@@ -0,0 +1,123 @@
<template>
<UPageSection
:ui="{
root: 'py-16 lg:py-20 bg-gray-50 dark:bg-gray-900/50'
}"
>
<template #title>
<div class="flex flex-col items-center text-center mb-4">
<span
class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-success-100 dark:bg-success-900/30 text-success-700 dark:text-success-300 text-sm font-medium mb-4"
>
<UIcon name="i-lucide-shield" class="w-4 h-4" />
Vertrauen & Sicherheit
</span>
<h2
class="font-heading text-3xl sm:text-4xl lg:text-5xl text-pretty tracking-tight font-bold text-gray-900 dark:text-white"
>
Weitere <span class="gradient-text">Vorteile</span>
</h2>
</div>
</template>
<template #description>
<p class="text-center text-lg text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">
Alles aus einer Hand ohne Drittanbieter, mit Serverstandort in Deutschland.
</p>
</template>
<!-- Icon grid with hover animations -->
<div class="mt-12 grid sm:grid-cols-2 lg:grid-cols-3 gap-6">
<div
v-for="(feature, index) in features"
:key="index"
class="group animate-fade-in-up"
:style="{ animationDelay: `${index * 100}ms` }"
>
<div
class="h-full p-6 rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 hover:border-transparent transition-all relative overflow-hidden"
>
<!-- Gradient border on hover -->
<div
class="absolute inset-0 rounded-2xl bg-gradient-to-br from-primary-500 via-cyan-500 to-accent-500 opacity-0 group-hover:opacity-100 transition-opacity -z-10"
/>
<div class="absolute inset-[2px] rounded-[14px] bg-white dark:bg-gray-900 -z-10" />
<div class="flex items-start gap-4">
<!-- Icon with gradient background -->
<div class="relative shrink-0">
<div
class="w-14 h-14 rounded-2xl bg-gradient-to-br from-primary-100 to-cyan-100 dark:from-primary-900/50 dark:to-cyan-900/50 flex items-center justify-center group-hover:scale-110 transition-transform"
>
<UIcon :name="feature.icon" class="w-7 h-7 text-primary-600 dark:text-primary-400" />
</div>
<!-- Animated ring on hover -->
<div
class="absolute inset-0 rounded-2xl ring-2 ring-primary-500/0 group-hover:ring-primary-500/50 transition-all scale-100 group-hover:scale-125 opacity-0 group-hover:opacity-100"
/>
</div>
<div class="flex-1">
<h3
class="font-heading text-lg font-bold text-gray-900 dark:text-white mb-2 group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors"
>
{{ feature.title }}
</h3>
<p class="text-sm text-gray-600 dark:text-gray-300 leading-relaxed">
{{ feature.description }}
</p>
</div>
</div>
</div>
</div>
</div>
<!-- Bottom CTA -->
<div class="mt-12 text-center">
<p class="text-gray-500 dark:text-gray-400 mb-4">Haben Sie Fragen zu unseren Sicherheitsstandards?</p>
<UButton to="#kontakt" variant="outline" class="btn-outline-gradient">
<UIcon name="i-lucide-message-circle" class="w-4 h-4 mr-2" />
Kontakt aufnehmen
</UButton>
</div>
</UPageSection>
</template>
<script setup lang="ts">
const features = [
{
icon: 'i-lucide-puzzle',
title: 'Keine Zusatztools erforderlich',
description:
'Keine externen Zusatzlösungen für Prozess, Abstimmung und Dokumentenerstellung alles läuft in einem System.'
},
{
icon: 'i-lucide-server',
title: 'Hosting & Backups in Deutschland',
description:
'Betrieb und Datensicherung erfolgen in Deutschland transparente Datenflüsse ohne unerwartete Auslandsverarbeitung.'
},
{
icon: 'i-lucide-lock',
title: 'Verschlüsselte Übertragung',
description:
'Alle Verbindungen sind per TLS/HTTPS verschlüsselt, damit Inhalte und Zugangsdaten beim Transport geschützt sind.'
},
{
icon: 'i-lucide-sparkles',
title: 'Einfach zu bedienen',
description: 'Nutzerfreundliche, selbsterklärende Oberfläche mit geführten Eingaben reduziert Schulungsaufwand.'
},
{
icon: 'i-lucide-settings',
title: 'Hohe Anpassbarkeit',
description:
'Templates, Felder und Prozessschritte lassen sich auf Organisation und Abläufe konkret anpassen (auf Anfrage).'
},
{
icon: 'i-lucide-headphones',
title: 'Persönlicher Support',
description: 'Bei Fragen steht Ihnen unser Support-Team zur Verfügung schnell, kompetent und auf Deutsch.'
}
]
</script>

View File

@@ -0,0 +1,101 @@
<template>
<div class="absolute inset-0 overflow-hidden pointer-events-none" :class="variant">
<!-- Animated gradient orbs -->
<div
v-for="(orb, index) in orbs"
:key="index"
class="absolute rounded-full blur-3xl animate-float"
:class="orb.color"
:style="{
width: orb.size,
height: orb.size,
top: orb.top,
left: orb.left,
right: orb.right,
bottom: orb.bottom,
animationDelay: `${index * 2}s`,
animationDuration: `${6 + index * 2}s`
}"
/>
<!-- Grid pattern -->
<div v-if="showGrid" class="absolute inset-0 dots-pattern opacity-20" />
<!-- Animated lines (for tech feel) -->
<svg v-if="showLines" class="absolute inset-0 w-full h-full opacity-10" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="lineGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" class="[stop-color:var(--color-primary-500)]" stop-opacity="0" />
<stop offset="50%" class="[stop-color:var(--color-primary-500)]" stop-opacity="1" />
<stop offset="100%" class="[stop-color:var(--color-primary-500)]" stop-opacity="0" />
</linearGradient>
</defs>
<!-- Horizontal animated lines -->
<g v-for="i in 3" :key="`h-${i}`">
<line
x1="0"
:y1="`${20 + i * 25}%`"
x2="100%"
:y2="`${20 + i * 25}%`"
stroke="url(#lineGradient)"
stroke-width="1"
class="animate-pulse-soft"
:style="{ animationDelay: `${i * 0.5}s` }"
/>
</g>
</svg>
<!-- Floating particles -->
<div v-if="showParticles" class="absolute inset-0">
<div
v-for="i in 12"
:key="`particle-${i}`"
class="absolute w-1 h-1 rounded-full bg-primary-400/30 animate-float"
:style="{
top: `${Math.random() * 100}%`,
left: `${Math.random() * 100}%`,
animationDelay: `${Math.random() * 5}s`,
animationDuration: `${4 + Math.random() * 4}s`
}"
/>
</div>
</div>
</template>
<script setup lang="ts">
interface Props {
variant?: 'hero' | 'section' | 'cta'
showGrid?: boolean
showLines?: boolean
showParticles?: boolean
}
const props = withDefaults(defineProps<Props>(), {
variant: 'section',
showGrid: true,
showLines: false,
showParticles: false
})
const orbs = computed(() => {
switch (props.variant) {
case 'hero':
return [
{ size: '400px', top: '-100px', left: '-100px', color: 'bg-primary-500/20' },
{ size: '300px', top: '200px', right: '-50px', color: 'bg-success-500/15' },
{ size: '250px', bottom: '-50px', left: '30%', color: 'bg-primary-400/15' }
]
case 'cta':
return [
{ size: '300px', top: '-50px', right: '-100px', color: 'bg-white/10' },
{ size: '200px', bottom: '-50px', left: '-50px', color: 'bg-white/5' }
]
default:
return [
{ size: '200px', top: '10%', left: '-50px', color: 'bg-primary-500/10' },
{ size: '150px', bottom: '20%', right: '-30px', color: 'bg-success-500/10' }
]
}
})
</script>

View File

@@ -0,0 +1,185 @@
<template>
<UPageSection
id="unternehmen"
:ui="{
root: 'scroll-mt-20 py-16 lg:py-20 bg-gray-50 dark:bg-gray-900/50'
}"
>
<template #title>
<div class="flex flex-col items-center text-center mb-4">
<span
class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-accent-100 dark:bg-accent-900/30 text-accent-700 dark:text-accent-300 text-sm font-medium mb-4"
>
<UIcon name="i-lucide-building-2" class="w-4 h-4" />
Für Unternehmen
</span>
<h2
class="font-heading text-3xl sm:text-4xl lg:text-5xl text-pretty tracking-tight font-bold text-gray-900 dark:text-white"
>
Vorteile für <span class="gradient-text">Unternehmen</span>
</h2>
</div>
</template>
<template #description>
<p class="text-center text-lg text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">
Schneller zur abgestimmten Einführung von IT- und KI-Systemen mit planbaren Timelines.
</p>
</template>
<!-- Comparison view -->
<div class="mt-12 grid lg:grid-cols-2 gap-8 lg:gap-12">
<!-- Pain points column -->
<div class="space-y-6">
<div class="flex items-center gap-3 mb-6">
<div
class="w-12 h-12 rounded-2xl bg-gradient-to-br from-warning-500 to-orange-500 flex items-center justify-center shadow-lg shadow-warning-500/25"
>
<UIcon name="i-lucide-x" class="w-6 h-6 text-white" />
</div>
<div>
<h3 class="font-heading text-xl font-bold text-gray-900 dark:text-white">Weg von</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">Typische Herausforderungen</p>
</div>
</div>
<div class="space-y-3">
<div
v-for="(point, index) in painPoints"
:key="index"
class="group flex gap-4 p-4 rounded-xl bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 hover:border-warning-300 dark:hover:border-warning-600 transition-all animate-fade-in-up"
:style="{ animationDelay: `${index * 75}ms` }"
>
<div
class="mt-0.5 w-8 h-8 rounded-lg bg-warning-100 dark:bg-warning-900/30 flex items-center justify-center shrink-0 group-hover:scale-110 transition-transform"
>
<UIcon :name="point.icon" class="w-4 h-4 text-warning-600 dark:text-warning-400" />
</div>
<p class="text-sm text-gray-700 dark:text-gray-300 leading-relaxed">{{ point.text }}</p>
</div>
</div>
</div>
<!-- Benefits column -->
<div class="space-y-6">
<div class="flex items-center gap-3 mb-6">
<div
class="w-12 h-12 rounded-2xl bg-gradient-to-br from-success-500 to-emerald-500 flex items-center justify-center shadow-lg shadow-success-500/25"
>
<UIcon name="i-lucide-check" class="w-6 h-6 text-white" />
</div>
<div>
<h3 class="font-heading text-xl font-bold text-gray-900 dark:text-white">Hin zu</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">Mit LegalConsentHub</p>
</div>
</div>
<div class="space-y-3">
<div
v-for="(benefit, index) in benefits"
:key="index"
class="group flex gap-4 p-4 rounded-xl bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 hover:border-success-300 dark:hover:border-success-600 transition-all animate-fade-in-up"
:style="{ animationDelay: `${index * 75 + 100}ms` }"
>
<div
class="mt-0.5 w-8 h-8 rounded-lg bg-success-100 dark:bg-success-900/30 flex items-center justify-center shrink-0 group-hover:scale-110 transition-transform"
>
<UIcon :name="benefit.icon" class="w-4 h-4 text-success-600 dark:text-success-400" />
</div>
<p class="text-sm text-gray-700 dark:text-gray-300 leading-relaxed">{{ benefit.text }}</p>
</div>
</div>
</div>
</div>
<!-- Highlights section -->
<div class="mt-12">
<div class="section-divider mb-8" />
<h3 class="font-heading text-2xl sm:text-3xl text-gray-900 dark:text-white text-center mb-10">
So wirkt es in der <span class="gradient-text">Praxis</span>
</h3>
<div class="grid sm:grid-cols-3 gap-6">
<div
v-for="(highlight, index) in highlights"
:key="index"
class="group animate-fade-in-up"
:style="{ animationDelay: `${index * 150}ms` }"
>
<div
class="h-full p-6 rounded-2xl bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 hover:shadow-xl hover:shadow-accent-500/10 hover:border-accent-200 dark:hover:border-accent-700 transition-all"
>
<div
class="w-12 h-12 rounded-2xl bg-gradient-to-br from-accent-500 to-violet-500 flex items-center justify-center mb-4 group-hover:scale-110 transition-transform shadow-lg shadow-accent-500/25"
>
<UIcon :name="highlight.icon" class="w-6 h-6 text-white" />
</div>
<h4 class="font-heading text-lg font-bold text-gray-900 dark:text-white mb-2">{{ highlight.title }}</h4>
<p class="text-sm text-gray-600 dark:text-gray-300">{{ highlight.description }}</p>
</div>
</div>
</div>
</div>
</UPageSection>
</template>
<script setup lang="ts">
const painPoints = [
{
icon: 'i-lucide-refresh-cw',
text: 'Unkalkulierbare Schleifen im Mitbestimmungsverfahren (fehlende Infos, Nachforderungen)'
},
{ icon: 'i-lucide-ban', text: 'Verzögerungen, Projektstopps und Last-Minute-Änderungen kurz vor Go-live' },
{ icon: 'i-lucide-files', text: 'Hoher Koordinationsaufwand (E-Mail/Excel/Word) und Versionschaos' },
{
icon: 'i-lucide-puzzle',
text: 'Komplexität und Reibung durch uneinheitliche Prozesse und unterschiedliche Erwartungshaltungen'
},
{ icon: 'i-lucide-help-circle', text: 'Unklarer Änderungsumfang bei Updates/Modulerweiterungen' },
{ icon: 'i-lucide-hourglass', text: 'Verlust wertvoller Zeit und begrenzter Ressourcen' },
{ icon: 'i-lucide-wallet', text: 'Vermeidbare Kosten (Einigungsstelle, externe Beratung, Eskalationen)' }
]
const benefits = [
{
icon: 'i-lucide-calendar-check',
text: 'Planbare, schnellere Verfahren durch Standardisierung & klare Prozessschritte'
},
{
icon: 'i-lucide-users',
text: 'Bessere Zusammenarbeit mit BR/PR durch Transparenz und nachvollziehbare Dokumentation'
},
{
icon: 'i-lucide-gauge',
text: 'Risikobasierter Ansatz: Schnellverfahren für Standard-/Low-Risk-Systeme, Fokus auf High-Risk'
},
{
icon: 'i-lucide-thumbs-up',
text: 'Höhere Einigungswahrscheinlichkeit, weil die Verhandlungsgrundlage strukturiert ist'
},
{
icon: 'i-lucide-file-check-2',
text: 'Qualitativ konsistente, robuste Betriebs-/Dienstvereinbarungen (auch bei Änderungen updatefähig)'
},
{ icon: 'i-lucide-sparkles', text: 'Entlastung von Fachbereichen/IT/HR mehr Zeit für Wertschöpfung' }
]
const highlights = [
{
icon: 'i-lucide-repeat',
title: 'Weniger Iterationen',
description: 'Unterlagen sind vollständig und vergleichbar Nachforderungen sinken.'
},
{
icon: 'i-lucide-rocket',
title: 'Schneller zur Freigabe',
description: 'Klare Schritte statt „Hauruck" und kurzfristiger Umplanungen.'
},
{
icon: 'i-lucide-refresh-cw',
title: 'Change-ready',
description:
'Updates, neue Module oder neue Auswertungen werden strukturiert nachgezogen, ohne jedes Mal neu zu starten.'
}
]
</script>

View File

@@ -0,0 +1,209 @@
<template>
<UPageSection
id="betriebsraete"
:ui="{
root: 'scroll-mt-20 py-16 lg:py-20'
}"
>
<template #title>
<div class="flex flex-col items-center text-center mb-4">
<span
class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary-100 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300 text-sm font-medium mb-4"
>
<UIcon name="i-lucide-users" class="w-4 h-4" />
Für Betriebsräte
</span>
<h2
class="font-heading text-3xl sm:text-4xl lg:text-5xl text-pretty tracking-tight font-bold text-gray-900 dark:text-white"
>
Vorteile für <span class="gradient-text">Betriebsräte</span>
</h2>
</div>
</template>
<template #description>
<p class="text-center text-lg text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">
Weg von Informationsjagd und Blackbox-Systemen hin zu Transparenz und belastbaren Vereinbarungen.
</p>
</template>
<!-- Toggle between pain points and benefits -->
<div class="mt-12">
<!-- Tab switcher -->
<div class="flex justify-center mb-10">
<div class="inline-flex p-1.5 rounded-2xl glass">
<button
:class="[
'px-6 py-3 rounded-xl text-sm font-semibold transition-all duration-300',
activeTab === 'pain'
? 'bg-gradient-to-r from-warning-500 to-warning-600 text-white shadow-lg'
: 'text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white'
]"
@click="activeTab = 'pain'"
>
<UIcon name="i-lucide-x-circle" class="w-4 h-4 inline mr-2" />
Weg von
</button>
<button
:class="[
'px-6 py-3 rounded-xl text-sm font-semibold transition-all duration-300',
activeTab === 'benefit'
? 'bg-gradient-to-r from-success-500 to-success-600 text-white shadow-lg'
: 'text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white'
]"
@click="activeTab = 'benefit'"
>
<UIcon name="i-lucide-check-circle" class="w-4 h-4 inline mr-2" />
Hin zu
</button>
</div>
</div>
<!-- Cards carousel -->
<div class="relative">
<!-- Pain points cards -->
<Transition
enter-active-class="transition-all duration-500 ease-out"
enter-from-class="opacity-0 translate-x-8"
enter-to-class="opacity-100 translate-x-0"
leave-active-class="transition-all duration-300 ease-in absolute inset-0"
leave-from-class="opacity-100 translate-x-0"
leave-to-class="opacity-0 -translate-x-8"
>
<div v-if="activeTab === 'pain'" class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-5">
<div v-for="(point, index) in painPoints" :key="index" class="group">
<div
class="h-full p-5 rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 hover:border-warning-300 dark:hover:border-warning-700 shadow-sm hover:shadow-lg transition-all duration-300"
>
<div
class="w-10 h-10 rounded-xl bg-warning-100 dark:bg-warning-900/30 flex items-center justify-center mb-4 group-hover:scale-110 transition-transform"
>
<UIcon :name="point.icon" class="w-5 h-5 text-warning-600 dark:text-warning-400" />
</div>
<p class="text-sm text-gray-700 dark:text-gray-300 leading-relaxed">{{ point.text }}</p>
</div>
</div>
</div>
</Transition>
<!-- Benefits cards -->
<Transition
enter-active-class="transition-all duration-500 ease-out"
enter-from-class="opacity-0 translate-x-8"
enter-to-class="opacity-100 translate-x-0"
leave-active-class="transition-all duration-300 ease-in absolute inset-0"
leave-from-class="opacity-100 translate-x-0"
leave-to-class="opacity-0 -translate-x-8"
>
<div v-if="activeTab === 'benefit'" class="grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-5">
<div v-for="(benefit, index) in benefits" :key="index" class="group">
<div
class="h-full p-5 rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 hover:border-success-300 dark:hover:border-success-700 shadow-sm hover:shadow-lg transition-all duration-300"
>
<div
class="w-10 h-10 rounded-xl bg-success-100 dark:bg-success-900/30 flex items-center justify-center mb-4 group-hover:scale-110 transition-transform"
>
<UIcon :name="benefit.icon" class="w-5 h-5 text-success-600 dark:text-success-400" />
</div>
<p class="text-sm text-gray-700 dark:text-gray-300 leading-relaxed">{{ benefit.text }}</p>
</div>
</div>
</div>
</Transition>
</div>
</div>
<!-- Highlights section -->
<div class="mt-12">
<div class="section-divider mb-8" />
<h3 class="font-heading text-2xl sm:text-3xl text-gray-900 dark:text-white text-center mb-10">
So hilft es im <span class="gradient-text">Alltag</span>
</h3>
<div class="grid sm:grid-cols-3 gap-6">
<div
v-for="(highlight, index) in highlights"
:key="index"
class="group animate-fade-in-up"
:style="{ animationDelay: `${index * 150}ms` }"
>
<div
class="h-full p-6 rounded-2xl bg-gradient-to-br from-primary-50 to-cyan-50 dark:from-primary-950/50 dark:to-cyan-950/50 border border-primary-100 dark:border-primary-900 hover:shadow-xl hover:shadow-primary-500/10 transition-all"
>
<div
class="w-12 h-12 rounded-2xl bg-gradient-to-br from-primary-500 to-cyan-500 flex items-center justify-center mb-4 group-hover:scale-110 transition-transform shadow-lg shadow-primary-500/25"
>
<UIcon :name="highlight.icon" class="w-6 h-6 text-white" />
</div>
<h4 class="font-heading text-lg font-bold text-gray-900 dark:text-white mb-2">{{ highlight.title }}</h4>
<p class="text-sm text-gray-600 dark:text-gray-300">{{ highlight.description }}</p>
</div>
</div>
</div>
</div>
</UPageSection>
</template>
<script setup lang="ts">
const activeTab = ref<'pain' | 'benefit'>('benefit')
const painPoints = [
{
icon: 'i-lucide-search',
text: 'Informationen zu IT-/KI-Systemen mühsam zusammensuchen (E-Mails, PDFs, PowerPoint, Excel-Chaos)'
},
{ icon: 'i-lucide-git-branch', text: 'Medienbrüche, Versionschaos und wiederkehrende Rückfragen' },
{ icon: 'i-lucide-timer', text: 'Ad-hoc-Systemeinführungen unter Zeitdruck („Hauruck" statt Prozess)' },
{ icon: 'i-lucide-brain', text: 'Überforderung durch Menge und Komplexität der Systeme' },
{ icon: 'i-lucide-eye-off', text: 'Blackbox bei Funktionen, Auswertungen, Schnittstellen und Änderungen' },
{ icon: 'i-lucide-file-question', text: 'Verhandlungen ohne belastbare Datenbasis' },
{ icon: 'i-lucide-calendar-x', text: 'Heute geregelt, morgen veraltet' }
]
const benefits = [
{
icon: 'i-lucide-layout-list',
text: 'Strukturierte, vollständige Informationsgrundlage (einheitlich je System/Verarbeitung)'
},
{ icon: 'i-lucide-route', text: 'Klarer Mitbestimmungsprozess mit bewährten Schritten und Zuständigkeiten' },
{
icon: 'i-lucide-file-check',
text: 'Betriebs-/Dienstvereinbarungen auf belastbarer Grundlage schneller, konsistenter, leicht aktualisierbar'
},
{
icon: 'i-lucide-scan-eye',
text: 'Transparenz über Systemfähigkeiten inkl. möglicher Leistungs-/Verhaltenskontrolle'
},
{
icon: 'i-lucide-git-compare',
text: 'Vergleichbarkeit über viele Systeme hinweg durch Standardisierung und einheitliche Templates'
},
{ icon: 'i-lucide-clock', text: 'Große Zeitersparnis gegenüber konventionellem Verfahren' },
{
icon: 'i-lucide-shield-check',
text: 'Schutzmaßnahmen konsequent dokumentiert und pro Auswertung/Verarbeitung nachvollziehbar'
},
{
icon: 'i-lucide-heart-handshake',
text: 'Spürbare Entlastung: mehr Zeit für andere Mitbestimmungsthemen und kontinuierlichen Arbeitnehmerschutz'
}
]
const highlights = [
{
icon: 'i-lucide-zap',
title: 'Schneller prüfen',
description: 'Relevante Funktionen und Risiken sind auf einen Blick sichtbar.'
},
{
icon: 'i-lucide-handshake',
title: 'Besser verhandeln',
description: 'Klare, konsistente Unterlagen statt Interpretationsspielräumen.'
},
{
icon: 'i-lucide-shield',
title: 'Nachhaltig absichern',
description: 'Änderungen und neue Auswertungen lassen sich strukturiert nachziehen.'
}
]
</script>

View File

@@ -0,0 +1,175 @@
<template>
<section id="kontakt" class="relative py-16 lg:py-20 overflow-hidden scroll-mt-20">
<!-- Mesh gradient background -->
<div class="absolute inset-0 mesh-gradient-cta" />
<!-- Animated gradient overlay -->
<div class="absolute inset-0 bg-gradient-to-br from-primary-600/90 via-cyan-600/90 to-accent-600/90" />
<!-- Grid pattern -->
<div class="absolute inset-0 grid-pattern opacity-10" />
<!-- Floating elements -->
<div class="absolute inset-0 overflow-hidden pointer-events-none">
<div class="absolute top-10 left-10 w-20 h-20 rounded-full bg-white/10 animate-float-slow" />
<div
class="absolute bottom-20 right-20 w-32 h-32 rounded-full bg-white/5 animate-float-slow"
style="animation-delay: 2s"
/>
<div
class="absolute top-1/2 left-1/4 w-16 h-16 rounded-full bg-white/10 animate-float-fast"
style="animation-delay: 1s"
/>
</div>
<div class="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid lg:grid-cols-2 gap-12 lg:gap-16 items-center">
<!-- Left: Content -->
<div class="text-center lg:text-left">
<span
class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-white/20 backdrop-blur-sm text-white text-sm font-medium mb-6"
>
<UIcon name="i-lucide-users" class="w-4 h-4" />
Expertennetzwerk
</span>
<h2 class="font-heading text-3xl sm:text-4xl lg:text-5xl font-bold text-white mb-6 animate-fade-in-up">
Externer Sachverstand direkt aus dem Verfahren
</h2>
<p class="text-lg text-white/90 mb-8 animate-fade-in-up" style="animation-delay: 100ms">
Wenn Betriebsparteien bei einzelnen Fragen nicht weiterkommen, kann optional externer Sachverstand direkt im
System angefragt werden. Anfragen, Rückfragen und Ergebnisse bleiben nachvollziehbar dokumentiert.
</p>
<!-- CTA buttons -->
<div
class="flex flex-wrap justify-center lg:justify-start gap-4 animate-fade-in-up"
style="animation-delay: 200ms"
>
<UButton
to="mailto:info@legalconsenthub.de"
size="xl"
class="bg-white text-primary-700 hover:bg-white/90 px-8 py-4 text-lg font-semibold rounded-xl shadow-lg"
>
Kontakt aufnehmen
<UIcon name="i-lucide-arrow-right" class="w-5 h-5 ml-2" />
</UButton>
<UButton
to="#features"
size="xl"
class="bg-white/10 text-white border-2 border-white/30 hover:bg-white/20 px-8 py-4 text-lg font-semibold rounded-xl backdrop-blur-sm"
>
Mehr erfahren
</UButton>
</div>
</div>
<!-- Right: Expert cards with connection lines -->
<div class="flex justify-center animate-scale-in" style="animation-delay: 300ms">
<div class="relative">
<!-- Connection lines SVG -->
<svg class="absolute inset-0 w-full h-full pointer-events-none" viewBox="0 0 400 300">
<defs>
<linearGradient id="lineGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color: rgba(255, 255, 255, 0.3)" />
<stop offset="50%" style="stop-color: rgba(255, 255, 255, 0.6)" />
<stop offset="100%" style="stop-color: rgba(255, 255, 255, 0.3)" />
</linearGradient>
</defs>
<!-- Animated connection lines -->
<path
d="M200,150 L100,80"
stroke="url(#lineGradient)"
stroke-width="2"
fill="none"
stroke-dasharray="5,5"
class="animate-pulse"
/>
<path
d="M200,150 L300,80"
stroke="url(#lineGradient)"
stroke-width="2"
fill="none"
stroke-dasharray="5,5"
class="animate-pulse"
style="animation-delay: 0.5s"
/>
<path
d="M200,150 L200,250"
stroke="url(#lineGradient)"
stroke-width="2"
fill="none"
stroke-dasharray="5,5"
class="animate-pulse"
style="animation-delay: 1s"
/>
</svg>
<!-- Center hub -->
<div class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-10">
<div
class="w-20 h-20 rounded-full bg-white/20 backdrop-blur-sm border-2 border-white/40 flex items-center justify-center animate-pulse-glow"
>
<UIcon name="i-lucide-link" class="w-8 h-8 text-white" />
</div>
</div>
<!-- Expert cards -->
<div class="grid grid-cols-2 gap-6 w-80 sm:w-96">
<!-- Legal expert card -->
<div class="col-span-1 animate-float-slow">
<div
class="bg-white/15 backdrop-blur-md rounded-2xl p-5 border border-white/20 hover:bg-white/20 transition-all"
>
<div class="w-12 h-12 rounded-xl bg-white/20 flex items-center justify-center mb-4">
<UIcon name="i-lucide-scale" class="w-6 h-6 text-white" />
</div>
<h4 class="font-heading font-bold text-white mb-1">Arbeitsrecht</h4>
<p class="text-sm text-white/70">Fachanwälte für Arbeitsrecht</p>
</div>
</div>
<!-- Technical expert card -->
<div class="col-span-1 animate-float-slow" style="animation-delay: 1s">
<div
class="bg-white/15 backdrop-blur-md rounded-2xl p-5 border border-white/20 hover:bg-white/20 transition-all"
>
<div class="w-12 h-12 rounded-xl bg-white/20 flex items-center justify-center mb-4">
<UIcon name="i-lucide-cpu" class="w-6 h-6 text-white" />
</div>
<h4 class="font-heading font-bold text-white mb-1">Technik</h4>
<p class="text-sm text-white/70">IT-Sachverständige</p>
</div>
</div>
<!-- Spacer for center positioning -->
<div class="col-span-2 h-24" />
<!-- Process integration card -->
<div class="col-span-2 animate-float-slow" style="animation-delay: 2s">
<div
class="bg-white/15 backdrop-blur-md rounded-2xl p-5 border border-white/20 hover:bg-white/20 transition-all"
>
<div class="flex items-center gap-4">
<div class="w-12 h-12 rounded-xl bg-white/20 flex items-center justify-center shrink-0">
<UIcon name="i-lucide-file-check" class="w-6 h-6 text-white" />
</div>
<div>
<h4 class="font-heading font-bold text-white mb-1">Direkt im Verfahren</h4>
<p class="text-sm text-white/70">Dokumentiert & nachvollziehbar</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</template>
<script setup lang="ts">
// No additional script needed
</script>

View File

@@ -0,0 +1,242 @@
<template>
<UPageSection
id="features"
:ui="{
root: 'scroll-mt-20 py-16 lg:py-20'
}"
>
<template #title>
<div class="flex flex-col items-center text-center mb-4">
<span
class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-cyan-100 dark:bg-cyan-900/30 text-cyan-700 dark:text-cyan-300 text-sm font-medium mb-4"
>
<UIcon name="i-lucide-sparkles" class="w-4 h-4" />
Features
</span>
<h2
class="font-heading text-3xl sm:text-4xl lg:text-5xl text-pretty tracking-tight font-bold text-gray-900 dark:text-white"
>
Features, die Mitbestimmung <span class="gradient-text">effizient</span> machen
</h2>
</div>
</template>
<template #description>
<p class="text-center text-lg text-gray-600 dark:text-gray-300 max-w-3xl mx-auto">
Eine strukturierte Eingabelogik, klare Prozesse und revisionssichere Dokumentation damit IT-/KI-Systeme
schneller bewertet, abgestimmt und sauber vereinbart werden können.
</p>
</template>
<!-- Bento Grid -->
<div ref="gridRef" class="mt-12 bento-grid">
<!-- Large featured card -->
<div class="bento-item-large group card-spotlight animate-fade-in-up" @mousemove="handleMouseMove">
<div
class="h-full p-8 rounded-3xl bg-gradient-to-br from-primary-500 to-cyan-500 text-white relative overflow-hidden"
>
<!-- Background pattern -->
<div class="absolute inset-0 opacity-10">
<div class="absolute inset-0 grid-pattern" />
</div>
<div class="relative z-10">
<div
class="w-14 h-14 rounded-2xl bg-white/20 backdrop-blur-sm flex items-center justify-center mb-6 group-hover:scale-110 transition-transform"
>
<UIcon name="i-lucide-route" class="w-7 h-7 text-white" />
</div>
<h3 class="font-heading text-2xl sm:text-3xl font-bold mb-4">Geführter Mitbestimmungsprozess</h3>
<p class="text-white/90 text-lg leading-relaxed mb-6">
Vorgegebene, erweiterbare Eingabeparameter strukturieren den gesamten Ablauf von der Systembeschreibung
bis zur Vereinbarung.
</p>
<!-- Visual element -->
<div class="flex items-center gap-3 mt-8">
<div class="flex -space-x-2">
<div class="w-8 h-8 rounded-full bg-white/30 flex items-center justify-center text-xs font-bold">1</div>
<div class="w-8 h-8 rounded-full bg-white/30 flex items-center justify-center text-xs font-bold">2</div>
<div class="w-8 h-8 rounded-full bg-white/30 flex items-center justify-center text-xs font-bold">3</div>
<div class="w-8 h-8 rounded-full bg-white/30 flex items-center justify-center text-xs font-bold">4</div>
</div>
<span class="text-sm text-white/80">Strukturierte Prozessschritte</span>
</div>
</div>
</div>
</div>
<!-- Regular cards -->
<div
v-for="(feature, index) in features.slice(1, 5)"
:key="index"
class="group card-spotlight animate-fade-in-up"
:style="{ animationDelay: `${(index + 1) * 100}ms` }"
@mousemove="handleMouseMove"
>
<div
class="h-full p-6 rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 hover:border-primary-300 dark:hover:border-primary-700 transition-all hover:shadow-xl hover:shadow-primary-500/10"
>
<div
class="w-12 h-12 rounded-xl bg-gradient-to-br from-primary-100 to-cyan-100 dark:from-primary-900/50 dark:to-cyan-900/50 flex items-center justify-center mb-4 group-hover:scale-110 transition-transform"
>
<UIcon :name="feature.icon" class="w-6 h-6 text-primary-600 dark:text-primary-400" />
</div>
<h3 class="font-heading text-lg font-bold text-gray-900 dark:text-white mb-2">{{ feature.title }}</h3>
<p class="text-sm text-gray-600 dark:text-gray-300 leading-relaxed">{{ feature.description }}</p>
</div>
</div>
<!-- Tall card -->
<div
class="bento-item-tall group card-spotlight animate-fade-in-up"
style="animation-delay: 500ms"
@mousemove="handleMouseMove"
>
<div
class="h-full p-6 rounded-2xl bg-gradient-to-b from-accent-50 to-violet-50 dark:from-accent-950/50 dark:to-violet-950/50 border border-accent-200 dark:border-accent-800 hover:shadow-xl hover:shadow-accent-500/10 transition-all"
>
<div
class="w-12 h-12 rounded-xl bg-gradient-to-br from-accent-500 to-violet-500 flex items-center justify-center mb-4 group-hover:scale-110 transition-transform shadow-lg shadow-accent-500/25"
>
<UIcon name="i-lucide-history" class="w-6 h-6 text-white" />
</div>
<h3 class="font-heading text-lg font-bold text-gray-900 dark:text-white mb-2">Versionierung & Audit-Trail</h3>
<p class="text-sm text-gray-600 dark:text-gray-300 leading-relaxed mb-6">
Änderungen werden versioniert und lückenlos nachvollziehbar dokumentiert inklusive Historie und Vergleich.
</p>
<!-- Visual timeline -->
<div class="space-y-3 mt-auto">
<div class="flex items-center gap-3">
<div class="w-2 h-2 rounded-full bg-accent-500" />
<div class="flex-1 h-px bg-accent-200 dark:bg-accent-800" />
<span class="text-xs text-accent-600 dark:text-accent-400">v1.0</span>
</div>
<div class="flex items-center gap-3">
<div class="w-2 h-2 rounded-full bg-accent-500" />
<div class="flex-1 h-px bg-accent-200 dark:bg-accent-800" />
<span class="text-xs text-accent-600 dark:text-accent-400">v1.1</span>
</div>
<div class="flex items-center gap-3">
<div class="w-3 h-3 rounded-full bg-accent-500 ring-4 ring-accent-200 dark:ring-accent-800" />
<div class="flex-1 h-px bg-accent-300 dark:bg-accent-700" />
<span class="text-xs font-semibold text-accent-700 dark:text-accent-300">v2.0</span>
</div>
</div>
</div>
</div>
<!-- Wide card -->
<div
class="bento-item-wide group card-spotlight animate-fade-in-up"
style="animation-delay: 600ms"
@mousemove="handleMouseMove"
>
<div
class="h-full p-6 rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 hover:border-primary-300 dark:hover:border-primary-700 transition-all hover:shadow-xl hover:shadow-primary-500/10"
>
<div class="flex flex-col sm:flex-row sm:items-start gap-6">
<div
class="w-12 h-12 rounded-xl bg-gradient-to-br from-primary-100 to-cyan-100 dark:from-primary-900/50 dark:to-cyan-900/50 flex items-center justify-center shrink-0 group-hover:scale-110 transition-transform"
>
<UIcon name="i-lucide-file-text" class="w-6 h-6 text-primary-600 dark:text-primary-400" />
</div>
<div>
<h3 class="font-heading text-lg font-bold text-gray-900 dark:text-white mb-2">
Automatische BV-/DV-Generierung
</h3>
<p class="text-sm text-gray-600 dark:text-gray-300 leading-relaxed">
Nach Abschluss der Eingaben erzeugt das System strukturierte, übersichtliche und signaturbereite
Betriebs-/Dienstvereinbarungen.
</p>
</div>
</div>
</div>
</div>
<!-- Remaining cards -->
<div
v-for="(feature, index) in features.slice(5)"
:key="index + 5"
class="group card-spotlight animate-fade-in-up"
:style="{ animationDelay: `${(index + 7) * 100}ms` }"
@mousemove="handleMouseMove"
>
<div
class="h-full p-6 rounded-2xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 hover:border-primary-300 dark:hover:border-primary-700 transition-all hover:shadow-xl hover:shadow-primary-500/10"
>
<div
class="w-12 h-12 rounded-xl bg-gradient-to-br from-primary-100 to-cyan-100 dark:from-primary-900/50 dark:to-cyan-900/50 flex items-center justify-center mb-4 group-hover:scale-110 transition-transform"
>
<UIcon :name="feature.icon" class="w-6 h-6 text-primary-600 dark:text-primary-400" />
</div>
<h3 class="font-heading text-lg font-bold text-gray-900 dark:text-white mb-2">{{ feature.title }}</h3>
<p class="text-sm text-gray-600 dark:text-gray-300 leading-relaxed">{{ feature.description }}</p>
</div>
</div>
</div>
</UPageSection>
</template>
<script setup lang="ts">
const gridRef = ref<HTMLElement | null>(null)
// Spotlight effect handler
function handleMouseMove(event: MouseEvent) {
const card = event.currentTarget as HTMLElement
const rect = card.getBoundingClientRect()
const x = ((event.clientX - rect.left) / rect.width) * 100
const y = ((event.clientY - rect.top) / rect.height) * 100
card.style.setProperty('--mouse-x', `${x}%`)
card.style.setProperty('--mouse-y', `${y}%`)
}
const features = [
{
icon: 'i-lucide-route',
title: 'Geführter Mitbestimmungsprozess',
description:
'Vorgegebene, erweiterbare Eingabeparameter strukturieren den gesamten Ablauf von der Systembeschreibung bis zur Vereinbarung.'
},
{
icon: 'i-lucide-shield-alert',
title: 'Risikobasierter Assistent',
description:
'Ein regelbasierter, risikoorientierter Assistent passt die Eingabemaske an Komplexität, Umfang und Potenziale zur Leistungs-/Verhaltensauswertung an.'
},
{
icon: 'i-lucide-gauge',
title: 'Ampelsystem & Direktfeedback',
description:
'Kritikalität wird transparent bewertet und direkt beim Ausfüllen zurückgespielt für schnelle Orientierung und weniger Schleifen.'
},
{
icon: 'i-lucide-message-square',
title: 'Zusammenarbeit per Kommentaren',
description: 'Inline-Kommentare ermöglichen Abstimmung direkt an einzelnen Inhalten ohne Medienbrüche.'
},
{
icon: 'i-lucide-history',
title: 'Versionierung & Audit-Trail',
description:
'Änderungen werden versioniert und lückenlos nachvollziehbar dokumentiert inklusive Historie und Vergleich.'
},
{
icon: 'i-lucide-pen-tool',
title: 'Signaturfähig mit QES (eIDAS)',
description: 'Optional rechtsverbindliche Unterzeichnung per qualifizierter elektronischer Signatur nach eIDAS.'
},
{
icon: 'i-lucide-bell',
title: 'Benachrichtigungen & News-Center',
description: 'Aktuell bleiben über Status-Updates, Änderungen und Aufgaben per Nachrichten-Center und E-Mail.'
},
{
icon: 'i-lucide-shield-check',
title: 'SSO & Governance',
description:
'SSO-Integration via SAML oder OIDC sowie ein umfangreich konfigurierbares Rollen- und Berechtigungskonzept.'
}
]
</script>

View File

@@ -0,0 +1,210 @@
<template>
<UPageSection
:ui="{
root: 'py-16 lg:py-20 scroll-mt-20'
}"
>
<div class="grid lg:grid-cols-2 gap-12 lg:gap-16 items-center">
<!-- Left: Content -->
<div class="order-2 lg:order-1">
<span
class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-accent-100 dark:bg-accent-900/30 text-accent-700 dark:text-accent-300 text-sm font-medium mb-6"
>
<UIcon name="i-lucide-file-text" class="w-4 h-4" />
Optional verfügbar
</span>
<h2
class="font-heading text-3xl sm:text-4xl lg:text-5xl text-pretty tracking-tight font-bold text-gray-900 dark:text-white mb-6"
>
Rahmenbetriebsvereinbarung <span class="gradient-text">IT/KI</span>
</h2>
<p class="text-lg text-gray-600 dark:text-gray-300 mb-8">
Für den Einstieg stellen wir auf Nachfrage eine optionale Rahmenbetriebs-/Rahmendienstvereinbarung für IT- und
KI-Systeme bereit. Sie ist auf die Struktur des Tools zugeschnitten und enthält praxiserprobte
Regelungsbausteine.
</p>
<!-- Animated checklist -->
<ul class="space-y-4 mb-8">
<li
v-for="(point, index) in bulletPoints"
:key="index"
class="flex gap-4 animate-fade-in-up"
:style="{ animationDelay: `${index * 100}ms` }"
>
<div
class="mt-1 w-6 h-6 rounded-full bg-gradient-to-br from-primary-500 to-cyan-500 flex items-center justify-center shrink-0"
>
<UIcon name="i-lucide-check" class="w-3.5 h-3.5 text-white" />
</div>
<p class="text-gray-700 dark:text-gray-300">{{ point }}</p>
</li>
</ul>
<!-- Price info -->
<div
class="p-4 rounded-xl bg-gradient-to-r from-primary-50 to-cyan-50 dark:from-primary-950/50 dark:to-cyan-950/50 border border-primary-200 dark:border-primary-800"
>
<div class="flex items-center gap-3">
<div
class="w-10 h-10 rounded-xl bg-gradient-to-br from-primary-500 to-cyan-500 flex items-center justify-center"
>
<UIcon name="i-lucide-tag" class="w-5 h-5 text-white" />
</div>
<div>
<p class="text-sm text-gray-500 dark:text-gray-400">Preis</p>
<p class="font-heading text-lg font-bold text-gray-900 dark:text-white">Auf Anfrage</p>
</div>
</div>
</div>
<!-- CTA -->
<div class="mt-8">
<UButton
to="#kontakt"
size="xl"
class="btn-gradient px-8 py-4 text-lg font-semibold rounded-xl shadow-lg shadow-primary-500/25"
>
<span>Mehr erfahren</span>
<UIcon name="i-lucide-arrow-right" class="w-5 h-5 ml-2" />
</UButton>
</div>
</div>
<!-- Right: 3D Document Preview -->
<div class="order-1 lg:order-2 flex justify-center">
<div class="relative animate-scale-in">
<!-- 3D Document with tilt effect -->
<div
class="relative transform-gpu transition-transform duration-500 hover:rotate-0"
style="transform: perspective(1000px) rotateY(-8deg) rotateX(5deg)"
@mouseenter="isHovered = true"
@mouseleave="isHovered = false"
>
<!-- Shadow layer -->
<div
class="absolute inset-0 bg-gray-900/20 dark:bg-black/40 blur-2xl rounded-3xl translate-x-4 translate-y-4"
/>
<!-- Document card -->
<div
class="relative bg-white dark:bg-gray-900 rounded-2xl border border-gray-200 dark:border-gray-700 shadow-2xl overflow-hidden max-w-sm"
>
<!-- Document header -->
<div class="bg-gradient-to-r from-primary-600 to-cyan-600 px-6 py-5">
<div class="flex items-center gap-4">
<div class="w-12 h-12 rounded-xl bg-white/20 backdrop-blur-sm flex items-center justify-center">
<UIcon name="i-lucide-file-text" class="w-6 h-6 text-white" />
</div>
<div>
<h4 class="font-heading font-bold text-white text-lg">Rahmen-BV IT/KI</h4>
<p class="text-sm text-white/80">Betriebsvereinbarung Vorlage</p>
</div>
</div>
</div>
<!-- Document content preview -->
<div class="p-6">
<!-- Table of contents -->
<div class="space-y-4">
<div class="text-xs uppercase tracking-wider text-gray-500 dark:text-gray-400 font-semibold">
Inhaltsverzeichnis
</div>
<!-- Section items with staggered animation -->
<div
v-for="(section, index) in documentSections"
:key="index"
class="group flex items-center gap-3 p-3 rounded-xl bg-gray-50 dark:bg-gray-800 hover:bg-primary-50 dark:hover:bg-primary-900/20 transition-colors cursor-pointer"
:class="{ 'animate-fade-in-up': isHovered }"
:style="{ animationDelay: `${index * 100}ms` }"
>
<div
class="w-8 h-8 rounded-lg bg-gradient-to-br from-primary-100 to-cyan-100 dark:from-primary-900/50 dark:to-cyan-900/50 flex items-center justify-center text-sm font-bold text-primary-600 dark:text-primary-400 group-hover:scale-110 transition-transform"
>
{{ index + 1 }}
</div>
<div class="flex-1 min-w-0">
<div class="text-sm font-medium text-gray-900 dark:text-white truncate">{{ section.title }}</div>
<div class="text-xs text-gray-500 dark:text-gray-400 truncate">{{ section.description }}</div>
</div>
<UIcon
:name="section.icon"
class="w-4 h-4 text-gray-400 group-hover:text-primary-500 transition-colors"
/>
</div>
</div>
<!-- Footer info -->
<div class="mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<UIcon name="i-lucide-layers" class="w-4 h-4 text-gray-400" />
<span class="text-xs text-gray-500 dark:text-gray-400">12 Regelungsbausteine</span>
</div>
<UBadge color="success" variant="soft" size="sm">
<UIcon name="i-lucide-check-circle" class="w-3 h-3 mr-1" />
Praxiserprobt
</UBadge>
</div>
</div>
</div>
</div>
<!-- Floating badge -->
<div class="absolute -top-4 -right-4 z-10 animate-float-fast">
<div
class="bg-gradient-to-r from-accent-500 to-violet-500 text-white px-4 py-2 rounded-full shadow-lg text-sm font-semibold flex items-center gap-2"
>
<UIcon name="i-lucide-download" class="w-4 h-4" />
Auf Anfrage
</div>
</div>
</div>
<!-- Decorative elements -->
<div class="absolute -top-8 -left-8 w-32 h-32 bg-primary-400/20 rounded-full blur-3xl animate-pulse-glow" />
<div
class="absolute -bottom-8 -right-8 w-40 h-40 bg-accent-400/20 rounded-full blur-3xl animate-pulse-glow"
style="animation-delay: 1s"
/>
</div>
</div>
</div>
</UPageSection>
</template>
<script setup lang="ts">
const isHovered = ref(false)
const bulletPoints = [
'Vorlage für eine Rahmen-BV/DV zu IT- und KI-Systemen',
'Auf die Tool-Logik und Dokumentationsstruktur zugeschnitten',
'Enthält praxiserprobte Regelungsbausteine als Startpunkt für systembezogene Vereinbarungen'
]
const documentSections = [
{
title: 'Geltungsbereich',
description: 'Definition der betroffenen Systeme',
icon: 'i-lucide-target'
},
{
title: 'Mitbestimmungsverfahren',
description: 'Prozessschritte und Zuständigkeiten',
icon: 'i-lucide-git-branch'
},
{
title: 'Datenschutz & Kontrolle',
description: 'Schutzmaßnahmen und Auswertungen',
icon: 'i-lucide-shield'
},
{
title: 'Änderungsmanagement',
description: 'Updates und Erweiterungen',
icon: 'i-lucide-refresh-cw'
}
]
</script>

View File

@@ -0,0 +1,263 @@
<template>
<section class="relative min-h-screen flex items-center justify-center overflow-hidden hero-gradient-light">
<!-- Animated gradient orbs -->
<div class="absolute inset-0 overflow-hidden pointer-events-none">
<!-- Large teal orb -->
<div
class="gradient-orb gradient-orb-teal w-[600px] h-[600px] -top-40 -left-40 animate-orb-float"
style="animation-delay: 0s"
/>
<!-- Violet orb -->
<div
class="gradient-orb gradient-orb-violet w-[500px] h-[500px] top-1/4 -right-32 animate-orb-float"
style="animation-delay: 3s"
/>
<!-- Cyan orb -->
<div
class="gradient-orb gradient-orb-cyan w-[400px] h-[400px] bottom-0 left-1/4 animate-orb-float"
style="animation-delay: 6s"
/>
<!-- Small accent orb -->
<div
class="gradient-orb gradient-orb-violet w-[200px] h-[200px] top-1/2 left-10 animate-orb-float opacity-40"
style="animation-delay: 9s"
/>
<!-- Grid pattern overlay -->
<div class="absolute inset-0 grid-pattern opacity-30" />
</div>
<!-- Content -->
<div class="relative z-10 w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-20 lg:py-32">
<div class="text-center max-w-4xl mx-auto">
<!-- Badge -->
<div class="animate-fade-in-up mb-8">
<span
class="inline-flex items-center gap-2 px-4 py-2 rounded-full glass text-sm font-medium text-gray-700 dark:text-gray-200"
>
<span class="relative flex h-2 w-2">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary-400 opacity-75" />
<span class="relative inline-flex rounded-full h-2 w-2 bg-primary-500" />
</span>
Jetzt verfügbar
</span>
</div>
<!-- Title with gradient text -->
<h1 class="font-heading text-4xl sm:text-5xl md:text-6xl lg:text-7xl tracking-tight mb-6">
<span class="animate-fade-in-up block text-gray-900 dark:text-white" style="animation-delay: 100ms">
Digitale Mitbestimmung
</span>
<span class="animate-fade-in-up block gradient-text" style="animation-delay: 200ms">
für IT- und KI-Systeme
</span>
</h1>
<!-- Description -->
<p
class="animate-fade-in-up text-lg sm:text-xl text-gray-600 dark:text-gray-300 max-w-2xl mx-auto mb-10"
style="animation-delay: 300ms"
>
{{ description }}
</p>
<!-- CTA Buttons -->
<div class="flex flex-wrap justify-center gap-4 animate-fade-in-up" style="animation-delay: 400ms">
<UButton
size="xl"
to="#kontakt"
trailing-icon="i-lucide-arrow-right"
class="btn-gradient px-8 py-4 text-lg font-semibold rounded-xl shadow-lg shadow-primary-500/25 hover:shadow-xl hover:shadow-primary-500/30 transition-all"
>
<span>Demo anfragen</span>
</UButton>
<UButton
size="xl"
to="#features"
variant="outline"
class="btn-outline-gradient px-8 py-4 text-lg font-semibold rounded-xl"
>
Features entdecken
</UButton>
</div>
</div>
<!-- Floating product preview cards -->
<div class="mt-16 lg:mt-24 relative animate-scale-in" style="animation-delay: 600ms">
<div class="flex flex-col lg:flex-row items-center justify-center gap-6 lg:gap-8">
<!-- Left card - tilted -->
<div class="card-3d w-full max-w-xs lg:-mr-8 lg:mt-12 order-2 lg:order-1">
<div
class="card-3d-inner glass rounded-2xl p-5 shadow-xl transform lg:-rotate-6 hover:rotate-0 transition-transform duration-500"
>
<div class="flex items-center gap-3 mb-4">
<div
class="w-10 h-10 rounded-xl bg-gradient-to-br from-warning-400 to-warning-500 flex items-center justify-center"
>
<UIcon name="i-lucide-alert-triangle" class="w-5 h-5 text-white" />
</div>
<div>
<p class="font-semibold text-gray-900 dark:text-white text-sm">Risikoprüfung</p>
<p class="text-xs text-gray-500 dark:text-gray-400">3 Punkte offen</p>
</div>
</div>
<div class="space-y-2">
<div class="flex items-center gap-2">
<div class="w-4 h-4 rounded bg-warning-100 dark:bg-warning-900/50 flex items-center justify-center">
<UIcon name="i-lucide-clock" class="w-2.5 h-2.5 text-warning-600" />
</div>
<span class="text-xs text-gray-600 dark:text-gray-300">Datenschutz-Folgenabschätzung</span>
</div>
<div class="flex items-center gap-2">
<div class="w-4 h-4 rounded bg-warning-100 dark:bg-warning-900/50 flex items-center justify-center">
<UIcon name="i-lucide-clock" class="w-2.5 h-2.5 text-warning-600" />
</div>
<span class="text-xs text-gray-600 dark:text-gray-300">Leistungskontrolle prüfen</span>
</div>
<div class="flex items-center gap-2">
<div class="w-4 h-4 rounded bg-success-100 dark:bg-success-900/50 flex items-center justify-center">
<UIcon name="i-lucide-check" class="w-2.5 h-2.5 text-success-600" />
</div>
<span class="text-xs text-gray-600 dark:text-gray-300">Zugriffsrechte definiert</span>
</div>
</div>
</div>
</div>
<!-- Center card - main -->
<div class="w-full max-w-md z-10 order-1 lg:order-2">
<div class="glass rounded-2xl overflow-hidden shadow-2xl border border-white/50 dark:border-white/10">
<!-- Mock header -->
<div
class="flex items-center justify-between px-5 py-4 bg-white/50 dark:bg-gray-900/50 border-b border-gray-200/50 dark:border-gray-700/50"
>
<div class="flex items-center gap-3">
<div
class="w-8 h-8 rounded-lg bg-gradient-to-br from-primary-500 to-cyan-500 flex items-center justify-center"
>
<UIcon name="i-lucide-file-text" class="w-4 h-4 text-white" />
</div>
<div>
<p class="font-semibold text-gray-900 dark:text-white text-sm">Microsoft 365</p>
<p class="text-xs text-gray-500 dark:text-gray-400">Betriebsvereinbarung</p>
</div>
</div>
<UBadge color="primary" variant="soft" size="sm">In Bearbeitung</UBadge>
</div>
<!-- Mock content -->
<div class="p-5 space-y-4">
<!-- Progress section -->
<div>
<div class="flex justify-between text-sm mb-2">
<span class="text-gray-600 dark:text-gray-300 font-medium">Fortschritt</span>
<span class="text-primary-600 dark:text-primary-400 font-semibold">67%</span>
</div>
<div class="h-2 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden">
<div class="h-full w-[67%] bg-gradient-to-r from-primary-500 to-cyan-500 rounded-full" />
</div>
</div>
<!-- Status items -->
<div class="grid grid-cols-2 gap-3">
<div
class="flex items-center gap-2 p-3 rounded-xl bg-success-50 dark:bg-success-900/20 border border-success-200 dark:border-success-800"
>
<UIcon name="i-lucide-check-circle" class="w-5 h-5 text-success-500" />
<div>
<p class="text-xs font-medium text-success-700 dark:text-success-300">8 Abschnitte</p>
<p class="text-xs text-success-600 dark:text-success-400">abgeschlossen</p>
</div>
</div>
<div
class="flex items-center gap-2 p-3 rounded-xl bg-primary-50 dark:bg-primary-900/20 border border-primary-200 dark:border-primary-800"
>
<UIcon name="i-lucide-message-square" class="w-5 h-5 text-primary-500" />
<div>
<p class="text-xs font-medium text-primary-700 dark:text-primary-300">4 Kommentare</p>
<p class="text-xs text-primary-600 dark:text-primary-400">neu</p>
</div>
</div>
</div>
<!-- Action button -->
<button
class="w-full py-3 px-4 rounded-xl bg-gradient-to-r from-primary-500 to-cyan-500 text-white font-semibold text-sm hover:from-primary-600 hover:to-cyan-600 transition-all shadow-lg shadow-primary-500/25"
>
Weiter bearbeiten
</button>
</div>
</div>
</div>
<!-- Right card - tilted -->
<div class="card-3d w-full max-w-xs lg:-ml-8 lg:mt-12 order-3">
<div
class="card-3d-inner glass rounded-2xl p-5 shadow-xl transform lg:rotate-6 hover:rotate-0 transition-transform duration-500"
>
<div class="flex items-center gap-3 mb-4">
<div
class="w-10 h-10 rounded-xl bg-gradient-to-br from-success-400 to-success-500 flex items-center justify-center"
>
<UIcon name="i-lucide-check-circle" class="w-5 h-5 text-white" />
</div>
<div>
<p class="font-semibold text-gray-900 dark:text-white text-sm">Abgeschlossen</p>
<p class="text-xs text-gray-500 dark:text-gray-400">Letzte Woche</p>
</div>
</div>
<div class="space-y-2">
<div class="flex items-center justify-between p-2 rounded-lg bg-success-50 dark:bg-success-900/30">
<span class="text-xs text-gray-700 dark:text-gray-200">SAP S/4HANA</span>
<UIcon name="i-lucide-check" class="w-4 h-4 text-success-500" />
</div>
<div class="flex items-center justify-between p-2 rounded-lg bg-success-50 dark:bg-success-900/30">
<span class="text-xs text-gray-700 dark:text-gray-200">Workday HCM</span>
<UIcon name="i-lucide-check" class="w-4 h-4 text-success-500" />
</div>
<div class="flex items-center justify-between p-2 rounded-lg bg-success-50 dark:bg-success-900/30">
<span class="text-xs text-gray-700 dark:text-gray-200">Salesforce CRM</span>
<UIcon name="i-lucide-check" class="w-4 h-4 text-success-500" />
</div>
</div>
</div>
</div>
</div>
<!-- Floating notification - positioned above all cards with higher z-index -->
<div
class="absolute top-0 right-8 lg:right-16 xl:right-24 z-20 animate-float-slow hidden lg:block"
style="animation-delay: 1s"
>
<div class="glass rounded-xl px-4 py-3 shadow-lg flex items-center gap-3">
<div
class="w-8 h-8 rounded-full bg-gradient-to-br from-success-400 to-success-500 flex items-center justify-center"
>
<UIcon name="i-lucide-sparkles" class="w-4 h-4 text-white" />
</div>
<div>
<p class="text-sm font-semibold text-gray-900 dark:text-white">BV erstellt!</p>
<p class="text-xs text-gray-500 dark:text-gray-400">Bereit zur Unterschrift</p>
</div>
</div>
</div>
</div>
</div>
<!-- Scroll indicator -->
<div class="absolute bottom-8 left-1/2 -translate-x-1/2 animate-bounce">
<a
href="#betriebsraete"
class="flex items-center justify-center w-12 h-12 rounded-full glass text-gray-600 dark:text-gray-300 hover:bg-white/80 dark:hover:bg-gray-800/80 transition-colors"
aria-label="Nach unten scrollen"
>
<UIcon name="i-lucide-chevron-down" class="w-6 h-6" />
</a>
</div>
</section>
</template>
<script setup lang="ts">
const description =
'Struktur statt Hauruck: Alle relevanten Informationen an einem Ort, klare Prozessschritte und nachvollziehbare Dokumentation damit Mitbestimmung schneller, belastbarer und dauerhaft updatefähig wird.'
</script>

View File

@@ -0,0 +1,187 @@
<template>
<section id="newsletter" class="relative py-16 lg:py-20 overflow-hidden scroll-mt-20">
<!-- Background with gradient and particles -->
<div
class="absolute inset-0 bg-gradient-to-br from-primary-50 via-cyan-50 to-accent-50 dark:from-gray-950 dark:via-primary-950/30 dark:to-accent-950/30"
/>
<!-- Animated particles -->
<div class="absolute inset-0 overflow-hidden pointer-events-none">
<div class="particle" />
<div class="particle" />
<div class="particle" />
<div class="particle" />
<div class="particle" />
<div class="particle" />
<div class="particle" />
<div class="particle" />
</div>
<!-- Gradient orbs -->
<div class="absolute top-0 left-1/4 w-96 h-96 bg-primary-400/20 rounded-full blur-3xl animate-orb-float" />
<div
class="absolute bottom-0 right-1/4 w-80 h-80 bg-accent-400/20 rounded-full blur-3xl animate-orb-float"
style="animation-delay: 3s"
/>
<div class="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<!-- Glass card -->
<div class="max-w-2xl mx-auto">
<div class="glass rounded-3xl p-8 sm:p-12 shadow-2xl">
<!-- Icon -->
<div class="flex justify-center mb-6">
<div
class="w-16 h-16 rounded-2xl bg-gradient-to-br from-primary-500 to-cyan-500 flex items-center justify-center animate-float-slow shadow-lg shadow-primary-500/25"
>
<UIcon name="i-lucide-mail" class="w-8 h-8 text-white" />
</div>
</div>
<!-- Title -->
<h2
class="font-heading text-3xl sm:text-4xl font-bold text-center text-gray-900 dark:text-white mb-4 animate-fade-in-up"
>
Bleiben Sie <span class="gradient-text">informiert</span>
</h2>
<!-- Description -->
<p
class="text-center text-lg text-gray-600 dark:text-gray-300 mb-8 animate-fade-in-up"
style="animation-delay: 100ms"
>
Erhalten Sie Updates zur Entwicklung von LegalConsentHub und seien Sie unter den Ersten, die von neuen
Funktionen erfahren.
</p>
<!-- Form -->
<UForm
:state="formState"
:schema="schema"
class="animate-fade-in-up"
style="animation-delay: 200ms"
@submit="onSubmit"
>
<div class="flex flex-col sm:flex-row gap-3">
<UFormField name="email" class="flex-1">
<UInput
v-model="formState.email"
type="email"
placeholder="Ihre E-Mail-Adresse"
size="xl"
:disabled="isLoading || isSuccess"
:ui="{
root: 'w-full',
base: 'rounded-xl bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-700 focus:ring-2 focus:ring-primary-500 focus:border-transparent'
}"
>
<template #leading>
<UIcon name="i-lucide-mail" class="w-5 h-5 text-gray-400" />
</template>
</UInput>
</UFormField>
<UButton
type="submit"
size="xl"
:loading="isLoading"
:disabled="isSuccess"
class="btn-gradient px-8 rounded-xl font-semibold shadow-lg shadow-primary-500/25 whitespace-nowrap"
>
<template v-if="isSuccess">
<UIcon name="i-lucide-check" class="w-5 h-5 mr-2" />
<span>Angemeldet!</span>
</template>
<template v-else>
<span>Anmelden</span>
</template>
</UButton>
</div>
</UForm>
<!-- Success message with animation -->
<Transition
enter-active-class="transition-all duration-500 ease-out"
enter-from-class="opacity-0 scale-95 translate-y-4"
enter-to-class="opacity-100 scale-100 translate-y-0"
leave-active-class="transition-all duration-300 ease-in"
leave-from-class="opacity-100 scale-100 translate-y-0"
leave-to-class="opacity-0 scale-95 translate-y-4"
>
<div
v-if="isSuccess"
class="mt-6 p-4 rounded-xl bg-success-100 dark:bg-success-900/30 border border-success-200 dark:border-success-800"
>
<div class="flex items-center justify-center gap-3 text-success-700 dark:text-success-300">
<div class="w-8 h-8 rounded-full bg-success-500 flex items-center justify-center">
<UIcon name="i-lucide-check" class="w-5 h-5 text-white" />
</div>
<span class="font-medium">Vielen Dank! Wir halten Sie auf dem Laufenden.</span>
</div>
</div>
</Transition>
<!-- Privacy note -->
<p
class="mt-6 text-sm text-center text-gray-500 dark:text-gray-400 animate-fade-in-up"
style="animation-delay: 300ms"
>
Mit der Anmeldung stimmen Sie unserer
<NuxtLink to="/datenschutz" class="text-primary-600 dark:text-primary-400 hover:underline font-medium">
Datenschutzerklärung
</NuxtLink>
zu. Wir versenden keinen Spam.
</p>
<!-- Trust indicators -->
<div
class="mt-8 flex flex-wrap justify-center gap-6 text-sm animate-fade-in-up"
style="animation-delay: 400ms"
>
<div class="flex items-center gap-2 text-gray-600 dark:text-gray-300">
<div class="w-6 h-6 rounded-full bg-success-100 dark:bg-success-900/30 flex items-center justify-center">
<UIcon name="i-lucide-shield-check" class="w-3.5 h-3.5 text-success-600 dark:text-success-400" />
</div>
<span>DSGVO-konform</span>
</div>
<div class="flex items-center gap-2 text-gray-600 dark:text-gray-300">
<div class="w-6 h-6 rounded-full bg-success-100 dark:bg-success-900/30 flex items-center justify-center">
<UIcon name="i-lucide-lock" class="w-3.5 h-3.5 text-success-600 dark:text-success-400" />
</div>
<span>Verschlüsselt</span>
</div>
<div class="flex items-center gap-2 text-gray-600 dark:text-gray-300">
<div class="w-6 h-6 rounded-full bg-success-100 dark:bg-success-900/30 flex items-center justify-center">
<UIcon name="i-lucide-x-circle" class="w-3.5 h-3.5 text-success-600 dark:text-success-400" />
</div>
<span>Kein Spam</span>
</div>
</div>
</div>
</div>
</div>
</section>
</template>
<script setup lang="ts">
import { z } from 'zod'
import type { FormSubmitEvent } from '@nuxt/ui'
const { isLoading, isSuccess, submitEmail } = useNewsletterSignup()
const schema = z.object({
email: z
.string()
.min(1, 'Bitte geben Sie eine E-Mail-Adresse ein')
.email('Bitte geben Sie eine gültige E-Mail-Adresse ein')
})
type Schema = z.output<typeof schema>
const formState = reactive<Partial<Schema>>({
email: ''
})
async function onSubmit(event: FormSubmitEvent<Schema>) {
await submitEmail(event.data.email)
}
</script>

View File

@@ -0,0 +1,122 @@
<template>
<section
class="py-10 lg:py-12 bg-gradient-to-r from-primary-50 via-cyan-50 to-accent-50 dark:from-primary-950/30 dark:via-cyan-950/30 dark:to-accent-950/30"
>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<!-- Stats grid -->
<div class="grid grid-cols-2 lg:grid-cols-4 gap-8 lg:gap-12">
<div
v-for="(stat, index) in stats"
:key="index"
class="text-center animate-fade-in-up"
:style="{ animationDelay: `${index * 100}ms` }"
>
<div class="relative inline-block mb-3">
<!-- Animated number -->
<span ref="counterRefs" class="font-heading text-4xl sm:text-5xl lg:text-6xl font-bold gradient-text">
{{ animatedValues[index] }}{{ stat.suffix }}
</span>
<!-- Decorative glow -->
<div class="absolute -inset-4 bg-primary-400/20 blur-2xl rounded-full -z-10 animate-pulse-glow" />
</div>
<p class="text-sm sm:text-base text-gray-600 dark:text-gray-300 font-medium">
{{ stat.label }}
</p>
</div>
</div>
<!-- Trust badges -->
<div class="mt-10 pt-8 border-t border-gray-200 dark:border-gray-800">
<p class="text-center text-sm text-gray-500 dark:text-gray-400 mb-8">Vertrauen & Sicherheit</p>
<div class="flex flex-wrap justify-center items-center gap-8 lg:gap-12">
<div
v-for="(badge, index) in trustBadges"
:key="index"
class="flex items-center gap-3 px-4 py-2 rounded-full bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 shadow-sm hover:shadow-md transition-shadow animate-fade-in-up"
:style="{ animationDelay: `${(index + 4) * 100}ms` }"
>
<div
class="w-8 h-8 rounded-full bg-gradient-to-br from-primary-100 to-cyan-100 dark:from-primary-900/50 dark:to-cyan-900/50 flex items-center justify-center"
>
<UIcon :name="badge.icon" class="w-4 h-4 text-primary-600 dark:text-primary-400" />
</div>
<span class="text-sm font-medium text-gray-700 dark:text-gray-200">{{ badge.label }}</span>
</div>
</div>
</div>
</div>
</section>
</template>
<script setup lang="ts">
const stats = [
{ value: 70, suffix: '%', label: 'Zeitersparnis im Verfahren' },
{ value: 100, suffix: '%', label: 'Revisionssicher dokumentiert' },
{ value: 24, suffix: '/7', label: 'Verfügbarkeit' },
{ value: 0, suffix: '', label: 'Medienbrüche' }
]
const trustBadges = [
{ icon: 'i-lucide-shield-check', label: 'DSGVO-konform' },
{ icon: 'i-lucide-server', label: 'Hosting in Deutschland' },
{ icon: 'i-lucide-lock', label: 'Ende-zu-Ende verschlüsselt' },
{ icon: 'i-lucide-key', label: 'SSO-fähig' }
]
// Animated counter values
const animatedValues = ref(stats.map(() => 0))
const hasAnimated = ref(false)
// Counter animation function
function animateCounter(index: number, target: number, duration: number = 2000) {
const start = 0
const startTime = performance.now()
function update(currentTime: number) {
const elapsed = currentTime - startTime
const progress = Math.min(elapsed / duration, 1)
// Easing function (ease-out cubic)
const easeOut = 1 - Math.pow(1 - progress, 3)
animatedValues.value[index] = Math.round(start + (target - start) * easeOut)
if (progress < 1) {
requestAnimationFrame(update)
}
}
requestAnimationFrame(update)
}
// Start animation when section is visible
onMounted(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting && !hasAnimated.value) {
hasAnimated.value = true
stats.forEach((stat, index) => {
setTimeout(() => {
animateCounter(index, stat.value)
}, index * 200)
})
}
})
},
{ threshold: 0.3 }
)
const section = document.querySelector('section')
if (section) {
observer.observe(section)
}
onUnmounted(() => {
observer.disconnect()
})
})
</script>

View File

@@ -0,0 +1,43 @@
export function useNewsletterSignup() {
const isLoading = ref(false)
const isSuccess = ref(false)
const error = ref<string | null>(null)
const submitEmail = async (_email: string) => {
isLoading.value = true
error.value = null
try {
// Simulate API call - replace with actual newsletter service integration
await new Promise((resolve) => setTimeout(resolve, 1500))
// TODO: Integrate with external newsletter service (e.g., Mailchimp, ConvertKit, etc.)
// Example:
// await $fetch('/api/newsletter/subscribe', {
// method: 'POST',
// body: { email }
// })
isSuccess.value = true
} catch (e: unknown) {
error.value = e instanceof Error ? e.message : 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.'
throw e
} finally {
isLoading.value = false
}
}
const reset = () => {
isLoading.value = false
isSuccess.value = false
error.value = null
}
return {
isLoading,
isSuccess,
error,
submitEmail,
reset
}
}

View File

@@ -0,0 +1,87 @@
<template>
<div class="landing-page">
<!-- Hero Section -->
<LandingHeroSection />
<!-- Stats Section (Social Proof) -->
<LandingStatsSection />
<!-- Benefits for Works Councils -->
<LandingBenefitsWorksCouncil />
<!-- Benefits for Companies -->
<LandingBenefitsCompany />
<!-- Features Grid (Bento Layout) -->
<LandingFeaturesGrid />
<!-- Additional Features / Trust Signals -->
<LandingAdditionalFeatures />
<!-- Framework Agreement Section -->
<LandingFrameworkAgreement />
<!-- Newsletter Signup (Glass-morphism) -->
<LandingNewsletterSignup />
<!-- Expert Access CTA (Gradient Mesh) -->
<LandingExpertAccess />
</div>
</template>
<script setup lang="ts">
// SEO Meta
useSeoMeta({
title: 'LegalConsentHub - Digitale Mitbestimmung für IT- und KI-Systeme',
description:
'Struktur statt Hauruck: Alle relevanten Informationen an einem Ort, klare Prozessschritte und nachvollziehbare Dokumentation damit Mitbestimmung schneller, belastbarer und dauerhaft updatefähig wird.',
ogTitle: 'LegalConsentHub - Digitale Mitbestimmung für IT- und KI-Systeme',
ogDescription: 'Strukturierte IT-Mitbestimmung mit klaren Prozessen und revisionssicherer Dokumentation.',
ogImage: '/og-image.png',
ogType: 'website',
twitterCard: 'summary_large_image'
})
// Structured data for SEO
useHead({
script: [
{
type: 'application/ld+json',
innerHTML: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
name: 'LegalConsentHub',
applicationCategory: 'BusinessApplication',
operatingSystem: 'Web',
description:
'Digitale Mitbestimmung für IT- und KI-Systeme. Strukturierte Eingabelogik, klare Prozesse und revisionssichere Dokumentation.',
offers: {
'@type': 'Offer',
price: '0',
priceCurrency: 'EUR',
description: 'Kontaktieren Sie uns für Preisdetails'
},
featureList: [
'Geführter Mitbestimmungsprozess',
'Risikobasierter Assistent',
'Versionierung & Audit-Trail',
'Automatische BV-/DV-Generierung',
'SSO & Governance'
]
})
}
]
})
</script>
<style scoped>
/* Smooth scrolling for anchor links */
.landing-page {
scroll-behavior: smooth;
}
/* Add padding to account for fixed header */
.landing-page > :deep(section:first-child) {
padding-top: 0;
}
</style>

15
landing/eslint.config.mjs Normal file
View File

@@ -0,0 +1,15 @@
// @ts-check
import withNuxt from './.nuxt/eslint.config.mjs'
export default withNuxt({
rules: {
'vue/html-self-closing': [
'error',
{
html: {
void: 'any'
}
}
]
}
})

42
landing/nuxt.config.ts Normal file
View File

@@ -0,0 +1,42 @@
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
modules: ['@nuxt/ui', '@nuxt/eslint', '@nuxt/fonts'],
css: ['~/assets/css/main.css'],
devtools: { enabled: true },
// SSR enabled by default for performance
ssr: true,
// Font configuration - Bricolage Grotesque for headings, DM Sans for body
fonts: {
families: [
{
name: 'Bricolage Grotesque',
provider: 'google',
weights: [400, 500, 600, 700, 800]
},
{
name: 'DM Sans',
provider: 'google',
weights: [400, 500, 600, 700]
}
]
},
// App configuration
app: {
head: {
htmlAttrs: {
lang: 'de'
},
title: 'LegalConsentHub - Digitale Mitbestimmung für IT- und KI-Systeme',
meta: [
{
name: 'description',
content:
'Struktur statt Hauruck: Alle relevanten Informationen an einem Ort, klare Prozessschritte und nachvollziehbare Dokumentation für IT-Mitbestimmung.'
}
]
}
}
})

36
landing/package.json Normal file
View File

@@ -0,0 +1,36 @@
{
"name": "nuxt-app",
"type": "module",
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",
"format": "prettier . --write",
"type-check": "nuxi typecheck",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"check": "pnpm run lint && pnpm run type-check && pnpm run format && pnpm run test"
},
"dependencies": {
"@nuxt/eslint": "1.12.1",
"@nuxt/fonts": "^0.12.1",
"@nuxt/ui": "4.3.0",
"eslint": "9.39.2",
"nuxt": "4.2.2",
"vue": "3.5.26",
"vue-router": "4.6.4",
"zod": "^4.3.4"
},
"devDependencies": {
"prettier": "^3.7.4",
"typescript": "5.9.3"
},
"volta": {
"node": "22.16.0",
"pnpm": "10.11.0"
},
"packageManager": "pnpm@10.13.1+sha512.37ebf1a5c7a30d5fabe0c5df44ee8da4c965ca0c5af3dbab28c3a1681b70a256218d05c81c9c0dcf767ef6b8551eb5b960042b9ed4300c59242336377e01cfad"
}

10306
landing/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

BIN
landing/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,2 @@
User-Agent: *
Disallow:

18
landing/tsconfig.json Normal file
View File

@@ -0,0 +1,18 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"files": [],
"references": [
{
"path": "./.nuxt/tsconfig.app.json"
},
{
"path": "./.nuxt/tsconfig.server.json"
},
{
"path": "./.nuxt/tsconfig.shared.json"
},
{
"path": "./.nuxt/tsconfig.node.json"
}
]
}