Compare commits

...

8 Commits

Author SHA1 Message Date
56012fd0d6 feat(ui): modernize company timeline with icons and giant typography
Some checks failed
Build & Deploy / 🔍 Prepare (push) Has been cancelled
Build & Deploy / 🧪 QA (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
CI - Lint, Typecheck & Test / quality-assurance (pull_request) Successful in 6m6s
2026-05-13 11:46:40 +02:00
220e03bed4 feat(ui): redesign germany map to modern dark split layout 2026-05-13 11:44:53 +02:00
1c317db747 fix(ui): add explicit cursor-pointer to core interactive elements to bypass tailwind v4 layer conflicts 2026-05-13 11:23:11 +02:00
33f64272d7 fix: restore hover states and cursor-pointer for all tiles
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 1m11s
Build & Deploy / 🧪 QA (push) Failing after 1m2s
Build & Deploy / 🏗️ Build (push) Has been skipped
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 3s
CI - Lint, Typecheck & Test / quality-assurance (pull_request) Successful in 4m25s
2026-05-13 11:13:34 +02:00
0b837e3376 fix: subcompany tiles layout and active states for 4 items
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 48s
Build & Deploy / 🧪 QA (push) Successful in 2m23s
CI - Lint, Typecheck & Test / quality-assurance (pull_request) Successful in 3m41s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
2026-05-13 11:08:31 +02:00
c4cc98a392 fix: align content with briefing and remove legacy data
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 43s
Build & Deploy / 🧪 QA (push) Successful in 2m11s
CI - Lint, Typecheck & Test / quality-assurance (pull_request) Successful in 4m39s
Build & Deploy / 🏗️ Build (push) Successful in 3m26s
Build & Deploy / 🚀 Deploy (push) Successful in 47s
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-05-13 10:49:48 +02:00
2cb0b50193 feat(ui): add AnimatedGlossyBorder and refine industrial interactions
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 39s
Build & Deploy / 🧪 QA (push) Successful in 1m44s
Build & Deploy / 🏗️ Build (push) Successful in 3m23s
Build & Deploy / 🚀 Deploy (push) Successful in 47s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 1m2s
Build & Deploy / 🔔 Notify (push) Successful in 2s
2026-05-13 10:23:47 +02:00
7d6a52e1c6 feat: unified container widths and added magnetic mouse pull to corporate background arcs
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 34s
Build & Deploy / 🧪 QA (push) Successful in 1m20s
Build & Deploy / 🏗️ Build (push) Successful in 2m59s
Build & Deploy / 🚀 Deploy (push) Successful in 43s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 1m0s
Build & Deploy / 🔔 Notify (push) Successful in 1s
2026-05-12 19:08:23 +02:00
28 changed files with 1038 additions and 117 deletions

10
.npmrc
View File

@@ -1,7 +1,7 @@
@mintel:registry=https://npm.infra.mintel.me
//npm.infra.mintel.me/:_authToken=YTJlYzk0ZDk1MzRiMTRmNmQxZWNkMTBkNmFhNTg4Y2I6MWVkMjdjNzE3NDVkMzhmMmQ4MDEyODEwNmJjZmQyYTkwMjY3M2VjZA==
//npm.infra.mintel.me/:always-auth=true
//git.infra.mintel.me/api/packages/mmintel/npm/:_authToken=YTJlYzk0ZDk1MzRiMTRmNmQxZWNkMTBkNmFhNTg4Y2I6MWVkMjdjNzE3NDVkMzhmMmQ4MDEyODEwNmJjZmQyYTkwMjY3M2VjZA==
@mintel:registry=https://git.infra.mintel.me/api/packages/mmintel/npm
//git.infra.mintel.me/api/packages/mmintel/npm/:_authToken=3b9e7c4da038461efd0c6fd006d432d2ace5cb14
//git.infra.mintel.me/api/packages/mmintel/npm/:always-auth=true
//registry.infra.mintel.me/api/packages/mmintel/npm/:_authToken=YTJlYzk0ZDk1MzRiMTRmNmQxZWNkMTBkNmFhNTg4Y2I6MWVkMjdjNzE3NDVkMzhmMmQ4MDEyODEwNmJjZmQyYTkwMjY3M2VjZA==
//registry.infra.mintel.me/api/packages/mmintel/npm/:_authToken=3b9e7c4da038461efd0c6fd006d432d2ace5cb14
//registry.infra.mintel.me/api/packages/mmintel/npm/:always-auth=true
//npm.infra.mintel.me/:_authToken=3b9e7c4da038461efd0c6fd006d432d2ace5cb14
//npm.infra.mintel.me/:always-auth=true

View File

@@ -19,6 +19,7 @@ import { JobListingBlock } from '@/components/blocks/JobListingBlock';
import { CallToAction } from '@/components/blocks/CallToAction';
import { ServiceDetailGrid } from '@/components/blocks/ServiceDetailGrid';
import { BenefitGrid } from '@/components/blocks/BenefitGrid';
import { InteractiveGermanyMap } from '@/components/blocks/InteractiveGermanyMap';
import { Button } from '@/components/ui/Button';
@@ -32,6 +33,7 @@ const mdxComponents = {
CallToAction,
ServiceDetailGrid,
BenefitGrid,
InteractiveGermanyMap,
Button,
Heading,
};
@@ -131,7 +133,7 @@ export default async function Page(props: { params: Promise<{ locale: string; sl
<Badge variant="accent" className="mb-4 md:mb-6">
{t('badge')}
</Badge>
<Heading level={1} className="text-white mb-0">
<Heading level={1} variant="white" className="mb-0">
{pageData.frontmatter.title}
</Heading>
</div>
@@ -139,7 +141,7 @@ export default async function Page(props: { params: Promise<{ locale: string; sl
</section>
{/* Main Content Area */}
<div className="container mx-auto px-4 py-16 md:py-24">
<Container className="py-16 md:py-24">
<div className="max-w-4xl mx-auto">
{/* Excerpt/Lead paragraph if available */}
{pageData.frontmatter.excerpt && (
@@ -180,7 +182,7 @@ export default async function Page(props: { params: Promise<{ locale: string; sl
</div>
</div>
</div>
</div>
</Container>
</div>
);
}

View File

@@ -18,6 +18,7 @@ import { cookies } from 'next/headers';
import { TransitionProvider } from '@/components/providers/TransitionProvider';
import { PageTransitionShutter } from '@/components/providers/PageTransitionShutter';
import { InitialLoader } from '@/components/providers/InitialLoader';
import { CorporateBackground } from '@/components/decorations/CorporateBackground';
const inter = Inter({
subsets: ['latin'],
@@ -175,9 +176,10 @@ export default async function Layout(props: {
{/* Preload critical hero video */}
<link rel="preload" as="video" href="/assets/videos/web/hero-bohrung.mp4" type="video/mp4" />
</head>
<body className="flex flex-col min-h-screen font-sans antialiased overflow-x-hidden selection:bg-primary/90 selection:text-white">
<body className="relative flex flex-col min-h-screen font-sans antialiased overflow-x-hidden selection:bg-primary/90 selection:text-white">
<NextIntlClientProvider messages={clientMessages} locale={safeLocale}>
<TransitionProvider>
<CorporateBackground />
<InitialLoader shouldShowLoader={!hasSeenLoader} />
<PageTransitionShutter />
<SkipLink />

19
app/[locale]/template.tsx Normal file
View File

@@ -0,0 +1,19 @@
'use client';
import { motion } from 'framer-motion';
export default function Template({ children }: { children: React.ReactNode }) {
return (
<motion.div
initial={{ opacity: 0, y: 20, filter: 'blur(10px)' }}
animate={{ opacity: 1, y: 0, filter: 'blur(0px)' }}
transition={{
duration: 0.7,
ease: [0.16, 1, 0.3, 1],
}}
className="w-full h-full"
>
{children}
</motion.div>
);
}

View File

@@ -15,6 +15,17 @@
.hide-scrollbar::-webkit-scrollbar {
display: none; /* Chrome, Safari and Opera */
}
@keyframes bg-pulse-15 {
0%, 90%, 100% { opacity: 0.04; }
95% { opacity: 0.10; filter: brightness(1.2); }
}
@keyframes bg-pulse-20 {
0%, 90%, 100% { opacity: 0.07; }
95% { opacity: 0.14; filter: brightness(1.2); }
}
.animate-bg-pulse-15 { animation: bg-pulse-15 12s infinite ease-in-out; }
.animate-bg-pulse-20 { animation: bg-pulse-20 12s infinite ease-in-out; }
}
/* Base Styles */

View File

@@ -3,6 +3,7 @@
import * as React from 'react';
import { motion, Variants } from 'framer-motion';
import { HoverShineOverlay } from '@/components/ui/HoverShineOverlay';
import { LogoArcs } from '@/components/ui/LogoArcs';
export interface BenefitItem {
id: string;
@@ -100,13 +101,32 @@ export function BenefitGrid({ badge, title, description, benefits }: BenefitGrid
whileInView="visible"
viewport={{ once: true, margin: "-50px" }}
>
{benefits.map((benefit) => (
<motion.div
key={benefit.id}
variants={itemVariants}
className="group relative overflow-hidden bg-neutral-50/50 border border-neutral-200/60 rounded-3xl p-8 transition-all duration-500 hover:bg-white hover:border-primary/30 hover:shadow-[0_20px_40px_-15px_rgba(0,0,0,0.05)] hover:-translate-y-1"
>
<HoverShineOverlay shineColor="via-primary/5" />
{benefits.map((benefit, index) => {
const bgPositionClasses = [
"-bottom-[30%] -right-[30%]",
"-top-[30%] -left-[30%]",
"-bottom-[30%] -left-[30%]"
];
const animationClasses = [
"animate-[spin_60s_linear_infinite]",
"animate-[spin_80s_linear_infinite]",
"animate-[spin_100s_linear_infinite]"
];
const positionClass = bgPositionClasses[index % bgPositionClasses.length];
const animationClass = animationClasses[index % animationClasses.length];
return (
<motion.div
key={benefit.id}
variants={itemVariants}
className="group relative overflow-hidden bg-neutral-50/50 border border-neutral-200/60 rounded-3xl p-8 transition-all duration-500 hover:bg-white hover:border-primary/30 hover:shadow-[0_20px_40px_-15px_rgba(0,0,0,0.05)] hover:-translate-y-1"
>
<HoverShineOverlay shineColor="via-primary/5" />
{/* Subtle Background Logo Arcs */}
<div className={`absolute ${positionClass} w-[120%] h-[120%] z-0 opacity-0 group-hover:opacity-[0.03] transition-opacity duration-1000 pointer-events-none mix-blend-multiply`}>
<LogoArcs className={`w-full h-full text-primary ${animationClass}`} />
</div>
<div className="relative z-10 flex items-start gap-4">
<div className="relative shrink-0 w-12 h-12 flex items-center justify-center bg-primary/10 text-primary rounded-xl transition-all duration-500 group-hover:bg-primary group-hover:text-white group-hover:scale-110 group-hover:-rotate-3">
@@ -125,7 +145,8 @@ export function BenefitGrid({ badge, title, description, benefits }: BenefitGrid
</div>
</div>
</motion.div>
))}
);
})}
</motion.div>
</div>
</section>

View File

@@ -2,34 +2,45 @@
import * as React from 'react';
import { motion, useScroll, useTransform } from 'framer-motion';
import { Building2, Compass, Layers, Drill, ArrowDownToLine, Wrench } from 'lucide-react';
const defaultMilestones = [
{
date: '2015',
title: 'Gründung der E-TIB GmbH',
desc: 'Gründung im brandenburgischen Guben mit Fokus auf Tiefbauarbeiten und Kabelmontagen.',
date: '16.12.2015',
year: '2015',
title: 'Gründung E-TIB GmbH',
desc: 'Ausführung elektrischer Infrastrukturprojekte, Kabelbau und Horizontalspülbohrungen.',
icon: Building2,
},
{
date: 'Wachstum',
title: 'Erweiterung der Kompetenzen',
desc: 'Konsequenter Ausbau der Leistungen um Horizontalspülbohrungen und Kabelpflugarbeiten.',
date: '04.02.2019',
year: '2019',
title: 'Gründung E-TIB Ingenieurgesellschaft',
desc: 'Genehmigungs- und Ausführungsplanung, komplexe Querungen sowie Netzanschlussplanung.',
icon: Compass,
},
{
date: 'Unternehmensgruppe',
title: 'Zusammenschluss',
desc: 'Bündelung der Fachbereiche mit der NEMO GmbH und der E-TIB Ingenieurgesellschaft mbH.',
date: '14.11.2019',
year: '2019',
title: 'Gründung E-TIB Verwaltung GmbH',
desc: 'Zentrale Dienste, Erwerb, Vermietung, Verpachtung und Verwaltung von Immobilien und Maschinen.',
icon: Layers,
},
{
date: 'Heute',
title: 'Die E-TIB Gruppe',
desc: 'Ein starker Verbund mit über 50 Mitarbeitern für bundesweite Infrastrukturprojekte.',
date: '21.10.2025',
year: '2025',
title: 'Gründung E-TIB Bohrtechnik GmbH',
desc: 'Spezialisierung auf präzise Horizontalspülbohrungen in allen Bodenklassen.',
icon: ArrowDownToLine,
},
];
interface Milestone {
date: string;
year: string;
title: string;
desc: string;
icon: React.ElementType;
}
interface CompanyTimelineProps {
@@ -52,48 +63,84 @@ export function CompanyTimeline({
const lineHeight = useTransform(scrollYProgress, [0, 1], ["0%", "100%"]);
return (
<section className="py-24 bg-white relative">
<div className="container max-w-4xl">
<div className="text-center mb-16">
<h2 className="text-primary font-bold tracking-wider uppercase text-sm mb-3">{badge}</h2>
<h3 className="font-heading text-4xl font-extrabold text-neutral-dark">{title}</h3>
<section className="py-24 md:py-32 bg-[#FAFAFA] relative overflow-hidden">
{/* Background Decor */}
<div className="absolute top-0 right-0 w-[600px] h-[600px] bg-primary/5 rounded-full blur-[100px] pointer-events-none -translate-y-1/2 translate-x-1/3" />
<div className="absolute bottom-0 left-0 w-[800px] h-[800px] bg-neutral-200/40 rounded-full blur-[120px] pointer-events-none translate-y-1/3 -translate-x-1/3" />
<div className="container max-w-5xl relative z-10">
<div className="text-center mb-24">
<div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-white border border-neutral-200 text-neutral-dark text-xs font-bold uppercase tracking-wider mb-6 shadow-sm">
<span className="w-2 h-2 rounded-full bg-primary animate-pulse" />
{badge}
</div>
<h3 className="font-heading text-4xl md:text-5xl lg:text-6xl font-extrabold text-neutral-dark tracking-tight">
{title}
</h3>
</div>
<div ref={containerRef} className="relative">
{/* Central Line */}
<div className="absolute left-4 md:left-1/2 top-0 bottom-0 w-1 bg-neutral-200 -translate-x-1/2 rounded-full overflow-hidden">
{/* Central Line Background */}
<div className="absolute left-[38px] md:left-1/2 top-0 bottom-0 w-1.5 bg-neutral-200 -translate-x-1/2 rounded-full" />
{/* Animated Central Line */}
<div className="absolute left-[38px] md:left-1/2 top-0 bottom-0 w-1.5 -translate-x-1/2 rounded-full overflow-hidden">
<motion.div
className="w-full bg-primary"
className="w-full bg-primary drop-shadow-[0_0_8px_rgba(130,237,32,0.8)]"
style={{ height: lineHeight }}
/>
</div>
<div className="space-y-12 md:space-y-0">
<div className="space-y-16 md:space-y-24">
{milestones.map((milestone, i) => {
const isEven = i % 2 === 0;
// isEven (0, 2): Left side on desktop
// !isEven (1, 3): Right side on desktop
const Icon = milestone.icon;
return (
<motion.div
key={i}
initial={{ opacity: 0, y: 30 }}
initial={{ opacity: 0, y: 40 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6 }}
className="relative flex flex-col w-full"
transition={{ duration: 0.7, ease: "easeOut" }}
className="relative flex flex-col md:flex-row items-center w-full group"
>
{/* Timeline Dot */}
<div className="absolute left-4 md:left-1/2 top-8 md:top-1/2 w-6 h-6 rounded-full bg-white border-4 border-primary z-10 -translate-x-1/2 md:-translate-y-1/2 shadow-lg" />
{/* Timeline Dot with Pulse */}
<div className="absolute left-[38px] md:left-1/2 top-0 md:top-1/2 w-8 h-8 rounded-full bg-white border-[5px] border-neutral-200 group-hover:border-primary transition-colors duration-500 z-20 -translate-x-1/2 md:-translate-y-1/2 shadow-sm flex items-center justify-center">
<div className="w-2 h-2 rounded-full bg-neutral-300 group-hover:bg-primary transition-colors duration-500" />
</div>
{/* Content Box */}
<div className={`w-full md:w-[calc(50%-2.5rem)] pl-16 md:pl-0 ${isEven ? 'md:mr-auto md:text-right' : 'md:ml-auto md:text-left'}`}>
<div className="bg-neutral-50 p-6 md:p-8 rounded-2xl shadow-sm border border-neutral-100 hover:shadow-md transition-shadow relative">
<span className="inline-block py-1.5 px-4 rounded-full bg-primary/10 text-primary font-bold text-sm mb-4">
{milestone.date}
</span>
<h4 className="font-heading font-bold text-2xl md:text-3xl mb-3 text-neutral-dark leading-tight">{milestone.title}</h4>
<p className="text-text-secondary leading-relaxed">{milestone.desc}</p>
{/* Content Box Wrapper */}
<div className={`w-full md:w-1/2 pl-24 md:pl-0 ${isEven ? 'md:pr-16 md:text-right' : 'md:pl-16 md:ml-auto md:text-left'}`}>
{/* The Card */}
<div className="relative bg-white p-8 md:p-10 rounded-[2rem] shadow-[0_8px_30px_rgb(0,0,0,0.04)] border border-neutral-100 hover:shadow-[0_20px_40px_rgb(0,0,0,0.08)] hover:border-primary/20 transition-all duration-500 overflow-hidden transform hover:-translate-y-1">
{/* Giant Typography Background Year */}
<div className={`absolute select-none pointer-events-none text-[140px] leading-none font-black text-neutral-50 z-0 ${isEven ? '-left-8' : '-right-8'} -bottom-10 opacity-70 group-hover:scale-110 transition-transform duration-700`}>
{milestone.year}
</div>
<div className="relative z-10">
{/* Header Row */}
<div className={`flex items-center gap-4 mb-6 ${isEven ? 'md:flex-row-reverse' : ''}`}>
<div className="w-12 h-12 rounded-2xl bg-primary/10 text-primary flex items-center justify-center shrink-0 group-hover:bg-primary group-hover:text-neutral-dark transition-colors duration-500">
<Icon className="w-6 h-6" />
</div>
<span className="inline-block py-1.5 px-4 rounded-full bg-neutral-100 text-neutral-600 font-bold text-sm tracking-wide">
{milestone.date}
</span>
</div>
<h4 className="font-heading font-extrabold text-2xl md:text-3xl mb-4 text-neutral-dark leading-tight group-hover:text-primary transition-colors duration-300">
{milestone.title}
</h4>
<p className="text-neutral-500 text-lg leading-relaxed">
{milestone.desc}
</p>
</div>
</div>
</div>
</motion.div>
);

View File

@@ -6,6 +6,9 @@ import { motion } from 'framer-motion';
import { useState, useEffect } from 'react';
import { Button } from '@/components/ui/Button';
import { HoverShineOverlay } from '@/components/ui/HoverShineOverlay';
import { AnimatedGlossyBorder } from '@/components/ui/AnimatedGlossyBorder';
import { Container } from '@/components/ui/Container';
import { LogoArcs } from '@/components/ui/LogoArcs';
interface CompetenceBentoGridProps {
badge?: string;
@@ -58,7 +61,7 @@ export function CompetenceBentoGrid(props: CompetenceBentoGridProps) {
if (!isMounted) {
return (
<section className="py-24 bg-neutral text-neutral-dark overflow-hidden relative">
<div className="container">
<Container>
<div className="flex flex-col md:flex-row justify-between items-end mb-12">
<div className="max-w-2xl">
<h2 className="text-primary font-bold tracking-wider uppercase text-sm mb-3">{badge}</h2>
@@ -68,20 +71,20 @@ export function CompetenceBentoGrid(props: CompetenceBentoGridProps) {
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-4 auto-rows-[220px]">
{/* Simplified static grid */}
</div>
</div>
</Container>
</section>
);
}
return (
<section className="py-24 bg-neutral text-neutral-dark relative overflow-hidden">
<div className="container relative z-10 px-4">
<div className="flex flex-col md:flex-row justify-between items-end mb-12">
<Container className="relative z-10 px-0 md:px-12 lg:px-16">
<div className="flex flex-col md:flex-row justify-between items-end mb-12 px-4 md:px-0">
<div className="max-w-2xl">
<h2 className="text-primary font-bold tracking-wider uppercase text-sm mb-3">{badge}</h2>
<h3 className="font-heading text-4xl md:text-5xl font-extrabold text-neutral-dark mb-4">{title}</h3>
</div>
<Button href={ctaHref} variant="ghost" className="mt-6 md:mt-0 bg-primary/5 hover:bg-primary/10">
<Button href={ctaHref} variant="ghost" className="mt-6 md:mt-0 bg-primary/5 hover:bg-primary/10 mx-4 md:mx-0">
{ctaLabel}
<svg xmlns="http://www.w3.org/2000/svg" className="w-5 h-5 ml-2 transform group-hover:translate-x-1 transition-transform" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>
</Button>
@@ -111,7 +114,14 @@ export function CompetenceBentoGrid(props: CompetenceBentoGridProps) {
className={gridClasses}
>
<div className="absolute top-0 right-0 w-32 h-32 bg-primary/30 blur-3xl rounded-full -mr-10 -mt-10 group-hover:bg-primary/50 transition-colors duration-500" />
{/* Subtle Background Logo Arcs */}
<div className={`absolute -bottom-[30%] -right-[30%] w-[120%] h-[120%] z-0 opacity-10 group-hover:opacity-20 transition-opacity duration-1000 pointer-events-none mix-blend-overlay`}>
<LogoArcs className={`w-full h-full text-white animate-[spin_60s_linear_infinite]`} />
</div>
<HoverShineOverlay shineColor="via-white/30" />
<AnimatedGlossyBorder color="white" className="opacity-0 group-hover:opacity-100 transition-opacity duration-700" borderWidth={1} />
<h4 className="font-bold text-xl md:text-2xl relative z-10 leading-snug">{item.title}</h4>
{item.description && <p className="text-white/60 text-sm mt-3 relative z-10">{item.description}</p>}
</motion.div>
@@ -146,6 +156,12 @@ export function CompetenceBentoGrid(props: CompetenceBentoGridProps) {
)}
<HoverShineOverlay />
<AnimatedGlossyBorder color="white" className="opacity-0 group-hover:opacity-100 transition-opacity duration-700" borderWidth={1} />
{/* Subtle Background Logo Arcs */}
<div className="absolute -top-[30%] -left-[30%] w-[120%] h-[120%] z-0 opacity-0 group-hover:opacity-[0.05] transition-opacity duration-1000 pointer-events-none mix-blend-overlay">
<LogoArcs className={`w-full h-full text-white animate-[spin_80s_linear_infinite]`} />
</div>
<div className="relative z-10 p-8 flex flex-col justify-end h-full text-white">
{item.tag && (
@@ -166,7 +182,7 @@ export function CompetenceBentoGrid(props: CompetenceBentoGridProps) {
);
})}
</div>
</div>
</Container>
</section>
);
}

View File

@@ -55,6 +55,7 @@ export function HeroVideo(props: HeroVideoProps) {
{videoUrl && (
<video
key={videoUrl}
className="absolute inset-0 w-full h-full object-cover z-1 pointer-events-none filter contrast-125 saturate-110 brightness-90"
src={videoUrl}
autoPlay

View File

@@ -0,0 +1,252 @@
'use client';
import React, { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { MapPin, CheckCircle2, Factory, Zap } from 'lucide-react';
import Image from 'next/image';
interface Location {
id: string;
name: string;
type: 'hq' | 'branch' | 'project';
x: number; // percentage from left
y: number; // percentage from top
description?: string;
details?: string[];
}
const locations: Location[] = [
{
id: 'guben',
name: 'Guben (Hauptsitz)',
type: 'hq',
x: 85, // approx East (adjusted to be inside map)
y: 48, // approx Middle
description: 'E-TIB GmbH Holding & Bohrtechnik GmbH',
details: ['Zentrale Steuerung', 'Kabelleitungstiefbau', 'Maschinenpark'],
},
{
id: 'buelstedt',
name: 'Bülstedt (Ingenieurgesellschaft)',
type: 'branch',
x: 37, // approx North-West
y: 25, // approx North
description: 'E-TIB Ingenieurgesellschaft mbH',
details: ['Planung & Projektierung', 'Vermessung', 'Dokumentation'],
},
// Dummy Projects
{
id: 'project-1',
name: '110kV Trasse München',
type: 'project',
x: 58,
y: 85,
description: 'Horizontalspülbohrung & Netzanbindung',
},
{
id: 'project-2',
name: 'Solarpark Hamburg',
type: 'project',
x: 45,
y: 18,
description: 'Komplexe Querung unter Gewässer',
},
{
id: 'project-3',
name: 'Windpark NRW',
type: 'project',
x: 20,
y: 50,
description: 'Kabelpflugarbeiten & LWL',
},
{
id: 'project-4',
name: 'Trassenausbau Leipzig',
type: 'project',
x: 65,
y: 55,
description: 'Infrastrukturausbau',
}
];
export function InteractiveGermanyMap() {
const [activeLocation, setActiveLocation] = useState<Location | null>(null);
const hq = locations.find((l) => l.type === 'hq');
const branch = locations.find((l) => l.type === 'branch');
const projects = locations.filter((l) => l.type === 'project');
return (
<div className="relative w-full max-w-7xl mx-auto py-16 md:py-32 px-4 sm:px-6">
<div className="bg-[#050B14] rounded-[2.5rem] md:rounded-[3.5rem] overflow-hidden relative shadow-2xl border border-white/5">
{/* Background Effects */}
<div className="absolute inset-0 bg-gradient-to-br from-[#050B14] via-[#0A1322] to-[#050B14]" />
<div className="absolute top-0 right-0 w-[800px] h-[800px] bg-primary/5 rounded-full blur-[120px] pointer-events-none translate-x-1/3 -translate-y-1/3" />
<div className="relative z-10 grid grid-cols-1 lg:grid-cols-12 gap-12 items-center min-h-[600px] p-8 md:p-16 lg:p-20">
{/* Content Left */}
<div className="lg:col-span-5 text-white">
<div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-primary/10 border border-primary/20 text-primary text-xs font-bold uppercase tracking-wider mb-8">
<MapPin className="w-4 h-4" />
<span>Einsatzgebiete</span>
</div>
<h3 className="font-heading text-4xl md:text-5xl lg:text-6xl font-extrabold mb-6 leading-[1.1] text-transparent bg-clip-text bg-gradient-to-r from-white to-white/70">
Deutschlandweit<br/>für Sie im Einsatz.
</h3>
<p className="text-white/60 text-lg mb-12 leading-relaxed">
Von unseren strategischen Standorten in Guben und Bülstedt steuern und realisieren wir komplexe Infrastrukturprojekte im gesamten Bundesgebiet.
</p>
{/* Industrial Stats Grid */}
<div className="grid grid-cols-2 gap-4">
<div className="bg-white/5 border border-white/10 rounded-2xl p-6 backdrop-blur-md relative overflow-hidden group">
<div className="absolute inset-0 bg-gradient-to-br from-primary/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
<div className="relative z-10">
<div className="text-4xl font-extrabold text-white mb-1 flex items-baseline gap-1">
100<span className="text-xl text-primary">%</span>
</div>
<div className="text-sm text-white/50 font-medium">Überregionale Reichweite</div>
</div>
</div>
<div className="bg-white/5 border border-white/10 rounded-2xl p-6 backdrop-blur-md relative overflow-hidden group">
<div className="absolute inset-0 bg-gradient-to-br from-primary/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
<div className="relative z-10">
<div className="text-4xl font-extrabold text-white mb-1 flex items-baseline gap-1">
2<span className="text-xl text-primary">+</span>
</div>
<div className="text-sm text-white/50 font-medium">Operative Standorte</div>
</div>
</div>
</div>
</div>
{/* Map Right */}
<div className="lg:col-span-7 relative flex items-center justify-center lg:justify-end mt-12 lg:mt-0">
{/* Map Container - Enforce strict aspect ratio matching the SVG (1024x1024) */}
<div className="relative w-full max-w-[600px] aspect-square z-10">
{/* Map SVG */}
<div className="absolute inset-0 opacity-[0.15] mix-blend-screen drop-shadow-2xl brightness-150">
<Image
src="/germany-map.svg"
alt="Deutschlandkarte"
fill
className="object-cover"
priority
/>
</div>
{/* Connections (Lines) */}
<svg className="absolute inset-0 w-full h-full pointer-events-none z-10">
{hq && branch && (
<motion.line
x1={`${hq.x}%`}
y1={`${hq.y}%`}
x2={`${branch.x}%`}
y2={`${branch.y}%`}
stroke="rgba(130,237,32,0.6)"
strokeWidth="2"
strokeDasharray="6 6"
initial={{ pathLength: 0, opacity: 0 }}
animate={{ pathLength: 1, opacity: 1 }}
transition={{ duration: 2, ease: "easeOut" }}
/>
)}
{hq && projects.map((proj, idx) => (
<motion.line
key={proj.id}
x1={`${hq.x}%`}
y1={`${hq.y}%`}
x2={`${proj.x}%`}
y2={`${proj.y}%`}
stroke="rgba(130,237,32,0.3)"
strokeWidth="1.5"
initial={{ pathLength: 0, opacity: 0 }}
animate={{ pathLength: 1, opacity: 1 }}
transition={{ duration: 1.5, delay: 0.5 + idx * 0.2 }}
/>
))}
</svg>
{/* Location Pins */}
{locations.map((loc, idx) => {
const isHQ = loc.type === 'hq';
const isBranch = loc.type === 'branch';
const isActive = activeLocation?.id === loc.id;
return (
<div
key={loc.id}
className="absolute transform -translate-x-1/2 -translate-y-1/2 z-20 group cursor-pointer w-16 h-16 flex items-center justify-center"
style={{ left: `${loc.x}%`, top: `${loc.y}%` }}
onMouseEnter={() => setActiveLocation(loc)}
onMouseLeave={() => setActiveLocation(null)}
>
{/* Ping Animation for HQ / Branch */}
{(isHQ || isBranch) && (
<div className="absolute inset-0 m-auto w-6 h-6 rounded-full animate-ping bg-primary/50 scale-150" />
)}
{/* Marker Dot */}
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: 'spring', delay: idx * 0.1 }}
className={`relative flex items-center justify-center rounded-full shadow-[0_0_20px_rgba(130,237,32,0.4)] transition-transform duration-300 ${
isActive ? 'scale-125 z-30 ring-4 ring-primary/30' : 'scale-100'
} ${
isHQ || isBranch
? 'w-6 h-6 bg-primary text-[#050B14]'
: 'w-3 h-3 bg-white/90 ring-2 ring-primary'
}`}
>
{(isHQ || isBranch) && <MapPin className="w-3.5 h-3.5" />}
</motion.div>
{/* Tooltip */}
<AnimatePresence>
{isActive && (
<motion.div
initial={{ opacity: 0, y: 10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 10, scale: 0.95 }}
transition={{ duration: 0.15 }}
className="absolute left-1/2 -translate-x-1/2 bottom-full mb-3 w-56 bg-white text-neutral-dark p-5 rounded-2xl shadow-2xl z-50 pointer-events-none border border-neutral-100"
>
<div className="flex items-center gap-2 mb-2">
{isHQ ? <Factory className="w-4 h-4 text-primary" /> : <Zap className="w-4 h-4 text-primary" />}
<span className="font-extrabold text-sm leading-tight text-neutral-dark">{loc.name}</span>
</div>
<p className="text-xs text-neutral-500 mt-2 font-medium">{loc.description}</p>
{loc.details && (
<ul className="mt-4 space-y-2">
{loc.details.map((detail, i) => (
<li key={i} className="text-[11px] text-neutral-dark flex items-center gap-2 font-semibold">
<div className="w-4 h-4 rounded-full bg-primary/10 flex items-center justify-center shrink-0">
<CheckCircle2 className="w-3 h-3 text-primary" />
</div>
{detail}
</li>
))}
</ul>
)}
{/* Arrow Down */}
<div className="absolute left-1/2 -bottom-2 -translate-x-1/2 border-8 border-transparent border-t-white drop-shadow-sm" />
</motion.div>
)}
</AnimatePresence>
</div>
);
})}
</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -4,6 +4,7 @@ import * as React from 'react';
import { motion, Variants } from 'framer-motion';
import Image from 'next/image';
import { HoverShineOverlay } from '@/components/ui/HoverShineOverlay';
import { AnimatedGlossyBorder } from '@/components/ui/AnimatedGlossyBorder';
interface SubCompanyTilesProps {
badge?: string;
@@ -49,6 +50,7 @@ const itemVariants: Variants = {
};
import { usePathname } from 'next/navigation';
import { LogoArcs } from '@/components/ui/LogoArcs';
export function SubCompanyTiles(props: SubCompanyTilesProps) {
const { data } = props;
@@ -76,21 +78,39 @@ export function SubCompanyTiles(props: SubCompanyTilesProps) {
)}
<motion.div
className="grid grid-cols-1 md:grid-cols-3 gap-6"
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"
variants={containerVariants}
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: "-50px" }}
>
{companiesData.map((company: any, index: number) => {
const isCurrent = !company.url;
const CardWrapper = isCurrent ? 'div' : 'a';
const wrapperProps = !isCurrent ? { href: company.url, target: "_blank", rel: "noopener noreferrer" } : {};
const isCurrent = company.title.toUpperCase() === 'E-TIB GMBH';
const hasUrl = !!company.url;
const CardWrapper = hasUrl ? 'a' : 'div';
const wrapperProps = hasUrl ? { href: company.url, target: "_blank", rel: "noopener noreferrer" } : {};
// Generate logo representation based on title
const isEtib = company.title.includes('E-TIB');
const isIng = company.title.includes('Ingenieur');
const isVerwaltung = company.title.includes('Verwaltung');
const isBohrtechnik = company.title.includes('Bohrtechnik');
const isNemo = company.title.includes('NEMO');
const bgPositionClasses = [
"-bottom-[30%] -right-[30%]",
"-top-[30%] -left-[30%]",
"-bottom-[30%] -left-[30%]",
"-top-[30%] -right-[30%]"
];
const animationClasses = [
"animate-[spin_60s_linear_infinite]",
"animate-[spin_80s_linear_infinite]",
"animate-[spin_100s_linear_infinite]",
"animate-[spin_120s_linear_infinite]"
];
const positionClass = bgPositionClasses[index % bgPositionClasses.length];
const animationClass = animationClasses[index % animationClasses.length];
return (
<motion.div
@@ -126,10 +146,22 @@ export function SubCompanyTiles(props: SubCompanyTilesProps) {
? 'bg-gradient-to-t from-[#0a192f]/90 via-[#0a192f]/60 to-[#0a192f]/40'
: 'bg-gradient-to-t from-[#0a192f]/90 via-[#0a192f]/60 to-[#0a192f]/40 group-hover:from-primary/90 group-hover:to-primary/20'
}`} />
{/* Subtle Background Logo Arcs */}
<div className={`absolute ${positionClass} w-[120%] h-[120%] z-10 opacity-[0.02] group-hover:opacity-[0.08] transition-opacity duration-1000 pointer-events-none mix-blend-overlay`}>
<LogoArcs className={`w-full h-full text-white ${animationClass}`} />
</div>
{/* Premium Shine Sweep Effect (Industrial Reflection) */}
<HoverShineOverlay shineColor="via-white/30" />
{/* Glossy Animated Border */}
<AnimatedGlossyBorder
color={isCurrent ? "primary" : "white"}
className={`transition-opacity duration-700 ${isCurrent ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'}`}
borderWidth={1}
/>
{/* Current Site Indicator Badge */}
{isCurrent && (
<div className="absolute top-6 right-6 z-20">
@@ -159,10 +191,20 @@ export function SubCompanyTiles(props: SubCompanyTilesProps) {
/>
</div>
{isIng && (
<div className="text-white/80 tracking-[0.3em] text-xs font-semibold uppercase mt-1">
<div className="text-white/80 tracking-[0.2em] text-[10px] sm:text-xs font-semibold uppercase mt-1 text-center">
Ingenieurgesellschaft
</div>
)}
{isVerwaltung && (
<div className="text-white/80 tracking-[0.2em] text-[10px] sm:text-xs font-semibold uppercase mt-1 text-center">
Verwaltung GmbH
</div>
)}
{isBohrtechnik && (
<div className="text-white/80 tracking-[0.2em] text-[10px] sm:text-xs font-semibold uppercase mt-1 text-center">
Bohrtechnik GmbH
</div>
)}
</div>
) : isNemo ? (
<div className="flex flex-col items-center">

View File

@@ -0,0 +1,188 @@
'use client';
import React, { useRef, useEffect } from 'react';
import { motion, useScroll, useTransform, useSpring } from 'framer-motion';
import { LogoArcs } from '@/components/ui/LogoArcs';
function MagneticRing({
children,
className,
pullStrength = 30, // How strong the pull is (higher = moves closer to mouse)
pullRadius = 600 // Distance from center at which it starts pulling
}: {
children: React.ReactNode,
className?: string,
pullStrength?: number,
pullRadius?: number
}) {
const ref = useRef<HTMLDivElement>(null);
// Spring physics for extremely smooth, organic movement
const x = useSpring(0, { stiffness: 40, damping: 25, mass: 1 });
const y = useSpring(0, { stiffness: 40, damping: 25, mass: 1 });
useEffect(() => {
// Only run on client with mouse
if (window.matchMedia("(pointer: coarse)").matches) return;
const handleMouseMove = (e: MouseEvent) => {
if (!ref.current) return;
const rect = ref.current.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
const distX = e.clientX - centerX;
const distY = e.clientY - centerY;
const distance = Math.sqrt(distX * Math.pow(distX, 2) + Math.pow(distY, 2)); // Fix: correct pythagoras
// Calculate actual distance correctly
const actualDistance = Math.sqrt(distX * distX + distY * distY);
if (actualDistance < pullRadius) {
// Exponential falloff for natural magnetic feel
const pullFactor = Math.pow(1 - (actualDistance / pullRadius), 2);
// Target offset (fraction of the distance based on strength)
x.set(distX * pullFactor * (pullStrength / 100));
y.set(distY * pullFactor * (pullStrength / 100));
} else {
// Return to origin smoothly
x.set(0);
y.set(0);
}
};
window.addEventListener('mousemove', handleMouseMove);
return () => window.removeEventListener('mousemove', handleMouseMove);
}, [x, y, pullRadius, pullStrength]);
return (
<motion.div
ref={ref}
style={{ x, y }}
className={className}
>
{children}
</motion.div>
);
}
export function CorporateBackground() {
const { scrollYProgress } = useScroll();
// Stronger parallax offsets for visible movement
const y1 = useTransform(scrollYProgress, [0, 1], [0, -400]);
const y2 = useTransform(scrollYProgress, [0, 1], [0, 500]);
const y3 = useTransform(scrollYProgress, [0, 1], [0, -600]);
const y4 = useTransform(scrollYProgress, [0, 1], [0, 350]);
const y5 = useTransform(scrollYProgress, [0, 1], [0, -500]);
return (
<div className="absolute inset-0 z-[50] overflow-hidden pointer-events-none" aria-hidden="true">
{/* 1. Top Right - Medium, rotating moderately */}
<motion.div
style={{
y: y1,
animationDelay: '0s',
maskImage: 'radial-gradient(circle at center, black 10%, transparent 60%)',
WebkitMaskImage: 'radial-gradient(circle at center, black 10%, transparent 60%)'
}}
className="absolute top-[0%] right-0 translate-x-[40%] w-[400px] h-[400px] md:w-[700px] md:h-[700px] animate-bg-pulse-20"
>
<MagneticRing pullRadius={700} pullStrength={40} className="w-full h-full">
<motion.div
animate={{ rotate: 360 }}
transition={{ duration: 40, repeat: Infinity, ease: "linear" }}
className="w-full h-full text-primary"
>
<LogoArcs className="w-full h-full" />
</motion.div>
</MagneticRing>
</motion.div>
{/* 2. Quarter way down left - Smaller, rotating fast reverse */}
<motion.div
style={{
y: y2,
animationDelay: '3s',
maskImage: 'radial-gradient(circle at center, black 15%, transparent 65%)',
WebkitMaskImage: 'radial-gradient(circle at center, black 15%, transparent 65%)'
}}
className="absolute top-[20%] left-0 -translate-x-[40%] w-[300px] h-[300px] md:w-[500px] md:h-[500px] animate-bg-pulse-15"
>
<MagneticRing pullRadius={500} pullStrength={50} className="w-full h-full">
<motion.div
animate={{ rotate: -360 }}
transition={{ duration: 30, repeat: Infinity, ease: "linear" }}
className="w-full h-full text-secondary"
>
<LogoArcs className="w-full h-full -rotate-45" />
</motion.div>
</MagneticRing>
</motion.div>
{/* 3. Halfway down right - Medium, rotating slow */}
<motion.div
style={{
y: y3,
animationDelay: '6s',
maskImage: 'radial-gradient(circle at center, black 10%, transparent 60%)',
WebkitMaskImage: 'radial-gradient(circle at center, black 10%, transparent 60%)'
}}
className="absolute top-[45%] right-0 translate-x-[30%] w-[350px] h-[350px] md:w-[600px] md:h-[600px] animate-bg-pulse-15"
>
<MagneticRing pullRadius={600} pullStrength={35} className="w-full h-full">
<motion.div
animate={{ rotate: 360 }}
transition={{ duration: 60, repeat: Infinity, ease: "linear" }}
className="w-full h-full text-primary"
>
<LogoArcs className="w-full h-full rotate-90" />
</motion.div>
</MagneticRing>
</motion.div>
{/* 4. Bottom left - Larger, rotating moderately */}
<motion.div
style={{
y: y4,
animationDelay: '9s',
maskImage: 'radial-gradient(circle at center, black 5%, transparent 60%)',
WebkitMaskImage: 'radial-gradient(circle at center, black 5%, transparent 60%)'
}}
className="absolute top-[70%] left-0 -translate-x-[40%] w-[500px] h-[500px] md:w-[800px] md:h-[800px] animate-bg-pulse-20"
>
<MagneticRing pullRadius={800} pullStrength={30} className="w-full h-full">
<motion.div
animate={{ rotate: -360 }}
transition={{ duration: 50, repeat: Infinity, ease: "linear" }}
className="w-full h-full text-secondary"
>
<LogoArcs className="w-full h-full rotate-180" />
</motion.div>
</MagneticRing>
</motion.div>
{/* 5. Deep bottom right - Smallest, very fast */}
<motion.div
style={{
y: y5,
animationDelay: '12s',
maskImage: 'radial-gradient(circle at center, black 20%, transparent 70%)',
WebkitMaskImage: 'radial-gradient(circle at center, black 20%, transparent 70%)'
}}
className="absolute top-[88%] right-0 translate-x-[50%] w-[250px] h-[250px] md:w-[400px] md:h-[400px] animate-bg-pulse-15"
>
<MagneticRing pullRadius={400} pullStrength={60} className="w-full h-full">
<motion.div
animate={{ rotate: 360 }}
transition={{ duration: 25, repeat: Infinity, ease: "linear" }}
className="w-full h-full text-primary"
>
<LogoArcs className="w-full h-full -rotate-12" />
</motion.div>
</MagneticRing>
</motion.div>
</div>
);
}

View File

@@ -71,7 +71,7 @@ export function Header({ navLinks }: HeaderProps) {
}`}
>
<div
className={`relative overflow-hidden w-full max-w-[1400px] mx-auto transition-all duration-700 ease-[cubic-bezier(0.16,1,0.3,1)] flex items-center justify-between ${
className={`relative overflow-hidden w-full max-w-7xl mx-auto transition-all duration-700 ease-[cubic-bezier(0.16,1,0.3,1)] flex items-center justify-between ${
isSolidMode
? 'rounded-full bg-white/50 backdrop-blur-xl shadow-[0_8px_32px_rgba(0,0,0,0.08)] border border-white/40 py-2.5 px-6 md:px-8'
: 'rounded-none bg-transparent py-6 px-4 md:px-8 border border-transparent'
@@ -125,7 +125,7 @@ export function Header({ navLinks }: HeaderProps) {
<TransitionLink
key={link.url}
href={mappedUrl}
className={`relative font-bold text-xs lg:text-sm uppercase tracking-widest transition-colors duration-300 group py-2 ${
className={`relative font-bold text-xs lg:text-sm uppercase tracking-widest transition-colors duration-300 group py-2 select-none ${
isSolidMode
? (isActive ? 'text-primary' : 'text-text-primary hover:text-primary')
: (isActive ? 'text-white' : 'text-white/80 hover:text-white')
@@ -162,7 +162,7 @@ export function Header({ navLinks }: HeaderProps) {
{/* Contact Button Desktop Only */}
<TransitionLink
href={`/${currentLocale}/${currentLocale === 'de' ? 'kontakt' : 'contact'}`}
className={`relative hidden md:inline-flex ml-2 px-7 py-2.5 rounded-full font-bold text-xs lg:text-sm uppercase tracking-widest transition-all duration-500 overflow-hidden group/cta isolate ${
className={`relative hidden md:inline-flex ml-2 px-7 py-2.5 rounded-full font-bold text-xs lg:text-sm uppercase tracking-widest transition-all duration-500 overflow-hidden group/cta isolate select-none ${
isSolidMode
? 'bg-primary text-white shadow-lg shadow-primary/20 hover:shadow-primary/40 hover:-translate-y-0.5 border border-transparent'
: 'bg-white/10 text-white backdrop-blur-md border border-white/30 hover:bg-white/20 hover:-translate-y-0.5 hover:shadow-[0_0_20px_rgba(255,255,255,0.2)]'

View File

@@ -33,7 +33,7 @@ export function LanguageSwitcher({ mobile = false, isSolidMode = false }: Langua
key={loc.code}
href={getSwitchedUrl(loc.code)}
transitionMessage={currentLocale === loc.code ? undefined : `LANG_SWITCH:${currentLocObj.flag}:${loc.flag}`}
className={`group relative overflow-hidden flex items-center gap-3 px-5 py-3 rounded-xl text-lg font-semibold transition-all duration-300 ${
className={`group relative overflow-hidden flex items-center gap-3 px-5 py-3 rounded-xl text-lg font-semibold transition-all duration-300 select-none ${
currentLocale === loc.code
? 'bg-primary/10 text-primary border border-primary/20'
: 'text-text-secondary hover:bg-neutral-50 border border-transparent'
@@ -83,7 +83,7 @@ export function LanguageSwitcher({ mobile = false, isSolidMode = false }: Langua
key={loc.code}
href={getSwitchedUrl(loc.code)}
transitionMessage={isActive ? undefined : `LANG_SWITCH:${currentLocObj.flag}:${loc.flag}`}
className={`relative z-10 flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-bold uppercase tracking-wider transition-all duration-300 ${linkClass}`}
className={`relative z-10 flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-bold uppercase tracking-wider transition-all duration-300 select-none ${linkClass}`}
aria-label={`Switch to ${loc.fullLabel}`}
>
<span className="text-sm" aria-hidden="true">{loc.flag}</span>

View File

@@ -72,7 +72,7 @@ export function MobileBottomNav({ navLinks, currentLocale }: MobileBottomNavProp
<TransitionLink
key={item.label}
href={mappedUrl}
className={`relative flex flex-col items-center justify-center w-full min-h-[44px] py-1 transition-colors duration-300 ${
className={`relative flex flex-col items-center justify-center w-full min-h-[44px] py-1 transition-colors duration-300 select-none ${
isActive ? 'text-primary' : 'text-neutral-500 hover:text-neutral-900'
}`}
>

View File

@@ -1,7 +1,7 @@
'use client';
import React, { createContext, useContext, useState, useCallback, ReactNode } from 'react';
import { useRouter } from 'next/navigation';
import React, { createContext, useContext, useState, useCallback, ReactNode, useEffect } from 'react';
import { useRouter, usePathname, useSearchParams } from 'next/navigation';
interface TransitionContextType {
isTransitioning: boolean;
@@ -15,13 +15,39 @@ export function TransitionProvider({ children }: { children: ReactNode }) {
const [isTransitioning, setIsTransitioning] = useState(false);
const [transitionMessage, setTransitionMessage] = useState<string | null>(null);
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
// We use a ref to track transition state without triggering the route change effect when it turns true
const isTransitioningRef = React.useRef(isTransitioning);
useEffect(() => {
isTransitioningRef.current = isTransitioning;
}, [isTransitioning]);
// Watch for route changes to complete the transition
useEffect(() => {
if (isTransitioningRef.current) {
// The route has actually changed in the browser, or at least the RSC payload arrived
const timer = setTimeout(() => {
setIsTransitioning(false);
setTimeout(() => setTransitionMessage(null), 300);
}, 100);
return () => clearTimeout(timer);
}
}, [pathname, searchParams]); // Run when pathname or search parameters change
const startTransition = useCallback((href: string, message: string | null = null) => {
// Wenn wir bereits navigieren, nichts tun
if (isTransitioning) return;
// Normalize paths to avoid false mismatches (e.g. /de vs /de/)
const currentPath = typeof window !== 'undefined' ? window.location.pathname.replace(/\/$/, '') || '/' : '';
const targetPath = href.split('?')[0].replace(/\/$/, '') || '/';
const currentSearch = typeof window !== 'undefined' ? window.location.search : '';
const targetSearch = href.includes('?') ? '?' + href.split('?')[1] : '';
// Aktuelle URL prüfen, falls es dieselbe ist, nicht navigieren
if (typeof window !== 'undefined' && window.location.pathname === href) {
if (currentPath === targetPath && currentSearch === targetSearch) {
// Optional: Wir könnten weich nach oben scrollen
window.scrollTo({ top: 0, behavior: 'smooth' });
return;
@@ -34,13 +60,16 @@ export function TransitionProvider({ children }: { children: ReactNode }) {
setTimeout(() => {
router.push(href);
// Den Shutter wieder einfahren lassen, nachdem die Seite gewechselt hat
// Next.js Route-Changes sind fast sofort da, aber wir geben dem Cache kurz Zeit
// Fallback: If for some reason the route doesn't change after 5 seconds, open the shutter anyway
setTimeout(() => {
setIsTransitioning(false);
// Message etwas verzögert zurücksetzen, damit es während dem Ausblenden nicht verschwindet
setTimeout(() => setTransitionMessage(null), 300);
}, 300);
setIsTransitioning((current) => {
if (current) {
setTimeout(() => setTransitionMessage(null), 300);
return false;
}
return current;
});
}, 5000);
}, 700);
}, [isTransitioning, router]);

View File

@@ -0,0 +1,46 @@
import React from 'react';
import { cn } from './utils';
interface AnimatedGlossyBorderProps {
className?: string;
borderWidth?: 1 | 2;
opacity?: number;
color?: 'white' | 'primary';
}
export function AnimatedGlossyBorder({
className,
borderWidth = 1,
color = 'white'
}: AnimatedGlossyBorderProps) {
const isWhite = color === 'white';
const glowColor = isWhite ? 'rgba(255,255,255,0.6)' : 'rgba(17,124,97,0.6)';
const gradientColors = isWhite
? 'transparent 0%, rgba(255,255,255,0.1) 15%, rgba(255,255,255,0.85) 25%, rgba(255,255,255,0.1) 35%, transparent 50%, transparent 50%, rgba(255,255,255,0.1) 65%, rgba(255,255,255,0.85) 75%, rgba(255,255,255,0.1) 85%, transparent 100%'
: 'transparent 0%, rgba(17,124,97,0.1) 15%, rgba(17,124,97,0.85) 25%, rgba(17,124,97,0.1) 35%, transparent 50%, transparent 50%, rgba(17,124,97,0.1) 65%, rgba(17,124,97,0.85) 75%, rgba(17,124,97,0.1) 85%, transparent 100%';
return (
<div
className={cn('absolute inset-0 pointer-events-none rounded-[inherit] z-20 mix-blend-overlay', className)}
style={{ filter: `drop-shadow(0 0 6px ${glowColor}) drop-shadow(0 0 2px ${glowColor})` }}
>
<div
className="absolute inset-0 rounded-[inherit]"
style={{
border: `${borderWidth}px solid transparent`,
WebkitMask: 'linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0)',
WebkitMaskComposite: 'xor',
maskComposite: 'exclude'
}}
>
<div
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[250%] aspect-square animate-[spin_8s_linear_infinite]"
style={{
background: `conic-gradient(from 0deg, ${gradientColors})`
}}
/>
</div>
</div>
);
}

View File

@@ -10,7 +10,7 @@ export function Badge({ children, className, variant = 'primary' }: { children:
};
return (
<span className={cn('inline-flex items-center px-3 py-1 rounded-full text-xs font-bold uppercase tracking-wider', variants[variant], className)}>
<span className={cn('inline-flex items-center px-3 py-1 rounded-full text-xs font-bold uppercase tracking-wider select-none', variants[variant], className)}>
{children}
</span>
);

View File

@@ -1,5 +1,6 @@
import React from 'react';
import { TransitionLink } from '@/components/ui/TransitionLink';
import { AnimatedGlossyBorder } from '@/components/ui/AnimatedGlossyBorder';
import { cn } from './utils';
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
@@ -19,7 +20,7 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
}
export const buttonBaseStyles =
'inline-flex items-center justify-center whitespace-nowrap rounded-full font-semibold transition-all duration-500 ease-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none active:scale-95 hover:-translate-y-1 hover:scale-[1.02] relative overflow-hidden group/btn isolate';
'cursor-pointer inline-flex items-center justify-center whitespace-nowrap rounded-full font-semibold transition-all duration-500 ease-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none active:scale-95 hover:-translate-y-1 hover:scale-[1.02] relative overflow-hidden group/btn isolate select-none';
export const buttonVariantsMap = {
primary: 'bg-primary text-white shadow-md hover:shadow-primary/30 hover:shadow-2xl',
@@ -58,13 +59,23 @@ export function getButtonClasses(variant: keyof typeof buttonVariantsMap = 'prim
}
export function ButtonOverlay({ variant = 'primary' }: { variant?: keyof typeof buttonVariantsMap }) {
const isWhiteBg = variant === 'white' || variant === 'outline' || variant === 'ghost';
return (
<span
className={cn(
'absolute top-0 -left-[150%] h-[200%] w-[150%] bg-gradient-to-r from-transparent to-transparent skew-x-[-20deg] group-hover/btn:left-[100%] transition-all duration-1000 ease-[cubic-bezier(0.16,1,0.3,1)] z-30 pointer-events-none',
buttonShineColorsMap[variant]
<>
<span
className={cn(
'absolute top-0 -left-[150%] h-[200%] w-[150%] bg-gradient-to-r from-transparent to-transparent skew-x-[-20deg] group-hover/btn:left-[100%] transition-all duration-1000 ease-[cubic-bezier(0.16,1,0.3,1)] z-30 pointer-events-none',
buttonShineColorsMap[variant]
)}
/>
{!isWhiteBg && (
<AnimatedGlossyBorder
color="white"
className="opacity-40 group-hover/btn:opacity-100 transition-opacity duration-700"
/>
)}
/>
</>
);
}

View File

@@ -9,7 +9,7 @@ export function Label({ className, ...props }: LabelProps) {
return (
<label
className={cn(
'text-[10px] md:text-xs font-extrabold text-primary uppercase tracking-widest',
'text-[10px] md:text-xs font-extrabold text-primary uppercase tracking-widest select-none',
className
)}
{...props}

View File

@@ -0,0 +1,20 @@
import React from 'react';
// Reusable SVG component that precisely mimics the overlapping, sweeping arcs of the E-TIB logo.
// The circles are offset along a diagonal to bunch together at the bottom-left and spread apart at the top-right.
export const LogoArcs = ({ className }: { className?: string }) => (
<svg
className={className}
viewBox="0 0 200 200"
fill="none"
xmlns="http://www.w3.org/2000/svg"
stroke="currentColor"
>
{/* Outer ring */}
<circle cx="100" cy="100" r="90" strokeWidth="1.5" />
{/* Middle thicker ring */}
<circle cx="92" cy="108" r="78" strokeWidth="3.5" />
{/* Inner ring */}
<circle cx="84" cy="116" r="66" strokeWidth="1.5" />
</svg>
);

View File

@@ -3,6 +3,7 @@
import React from 'react';
import Link, { LinkProps } from 'next/link';
import { useTransition } from '@/components/providers/TransitionProvider';
import { cn } from './utils';
interface TransitionLinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, keyof LinkProps>, LinkProps {
children: React.ReactNode;
@@ -31,7 +32,7 @@ export function TransitionLink({ children, href, onClick, transitionMessage, ...
};
return (
<Link href={href} onClick={handleClick} {...props}>
<Link href={href} onClick={handleClick} className={cn('cursor-pointer', props.className)} {...props}>
{children}
</Link>
);

View File

@@ -30,11 +30,16 @@ description: Willkommen bei E-TIB GmbH
backgroundImage: "/assets/photos/DSC01137.JPG"
},
{
title: "NEMO GmbH",
description: "Der Spezialist für Planung und Umsetzung von nachhaltigen Energieprojekten - Mit Fokus auf Solarenergie.",
icon: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z",
url: "https://www.nemo-gmbh.de/",
title: "E-TIB Verwaltung GmbH",
description: "Zentrale Dienste, Einkauf und Finanzen sowie Verwaltung von Immobilien, Grundstücken, Maschinen und Geräten.",
icon: "M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4",
backgroundImage: "/assets/photos/DJI_0048.JPG"
},
{
title: "E-TIB Bohrtechnik GmbH",
description: "Präzise Horizontalspülbohrungen in allen Bodenklassen für eine oberflächenschonende und grabenlose Leitungsverlegung.",
icon: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z",
backgroundImage: "/assets/photos/DSC08653.JPG"
}
]}
/>

View File

@@ -24,12 +24,12 @@ E-Mail: d.joseph@e-tib.com
Eintragung im Handelsregister.
Registergericht: Amtsgericht Cottbus
Registernummer: HRB 14781 CB
Registernummer: HRB 12403 CB
## Umsatzsteuer-ID
Umsatzsteuer-Identifikationsnummer gemäß § 27 a Umsatzsteuergesetz:
DE332345678 (Beispiel)
DE304799919
## Redaktionell verantwortlich

View File

@@ -70,11 +70,11 @@ layout: "fullBleed"
icon: "energy",
lists: [
[
"Kabel- und Rohrgräben",
"Kabelbau",
"Kabelverlegung (0,4kV - 110kV)",
"Kabelmontagen (0,4kV - 30kV)",
"Elektromontagen bis 110 kV",
"Errichtung von Transformatorstationen",
"Erdungsanlagen & Straßenbeleuchtung"
"Wartung & Störungsdienst"
]
]
},
@@ -84,22 +84,22 @@ layout: "fullBleed"
icon: "fiber",
lists: [
[
"Glasfaser-Kabelmontagen",
"Herstellung von Leerrohrtrassen",
"Einziehen/Einblasen von LWL-Kabeln",
"Stellen von Multifunktionsgehäusen",
"LWL-Kabelmontagen"
"Stellen von Multifunktionsgehäusen"
]
]
},
{
id: "hdd",
title: "Grabenlose Verlegung",
title: "Bohrtechnik & Kabelpflug",
icon: "hdd",
lists: [
[
"Horizontalspülbohrverfahren (HDD)",
"Max. Bohrlänge: 250m",
"Max. Durchmesser: 400er Rohr",
"Horizontalspülbohrungen",
"Kabelpflugarbeiten",
"Komplexe Querung (Bahn, Autobahn, Gewässer)",
"Erdrakete (bis 15m / 160er Rohr)"
]
]
@@ -110,9 +110,9 @@ layout: "fullBleed"
icon: "planning",
lists: [
[
"Struktur- & Ausführungsplanung",
"Genehmigungs- und Ausführungsplanung",
"Elektro- und Netzanschlussplanung",
"Ausschreibung & Bauüberwachung",
"Zustimmungsplanung",
"Machbarkeitsstudien & Projektprüfung"
]
]
@@ -124,12 +124,12 @@ layout: "fullBleed"
fullWidth: true,
lists: [
[
"Absteckung und Einmessung von Leitungstrassen",
"Digitale Bestandsdokumentation (GIS)"
"Vermessung & Dokumentation",
"Absteckung und Einmessung von Leitungstrassen"
],
[
"360° Foto-/Video-Erfassung des Baufeldes",
"Erstellung von Datenbanken zur Projektabrechnung"
"Digitale Bestandsdokumentation (GIS)",
"360° Foto-/Video-Erfassung des Baufeldes"
]
]
}

View File

@@ -15,7 +15,15 @@ layout: "fullBleed"
/>
<div id="termine">
<JobListingBlock showFairs={true} showJobs={false} />
<JobListingBlock
showFairs={true}
showJobs={false}
fairs={[
{ name: 'Intersolar München', date: '19. - 21. Juni 2026', type: 'Energie-Messe', location: 'Messe München', booth: 'Halle A3, Stand 110', url: 'https://www.intersolar.de' },
{ name: 'Windenergietage Linstow', date: '04. - 06. November 2026', type: 'Fachkongress', location: 'Van der Valk Resort Linstow', booth: 'Stand 42', url: 'https://windenergietage.de' },
{ name: 'Kabelwerkstatt Wiesbaden', date: '02. - 03. Dezember 2026', type: 'Fachmesse', location: 'RheinMain CongressCenter', booth: 'Halle 1, Stand B20' }
]}
/>
</div>
<CallToAction />

View File

@@ -56,6 +56,8 @@ layout: "fullBleed"
<CompanyTimeline />
<InteractiveGermanyMap />
<div id="struktur">
<HomeSubCompanyTiles
badge="Struktur"
@@ -75,13 +77,18 @@ layout: "fullBleed"
url: "https://www.etib-ing.com/",
backgroundImage: "/assets/photos/DSC01137.JPG"
},
{
title: "NEMO GmbH",
description: "Der Spezialist für Planung und Umsetzung von nachhaltigen Energieprojekten - Mit Fokus auf Solarenergie.",
icon: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z",
url: "https://www.nemo-gmbh.de/",
backgroundImage: "/assets/photos/DJI_0048.JPG"
}
{
title: "E-TIB Verwaltung GmbH",
description: "Zentrale Dienste, Einkauf und Finanzen sowie Verwaltung von Immobilien, Grundstücken, Maschinen und Geräten.",
icon: "M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4",
backgroundImage: "/assets/photos/DJI_0048.JPG"
},
{
title: "E-TIB Bohrtechnik GmbH",
description: "Präzise Horizontalspülbohrungen in allen Bodenklassen für eine oberflächenschonende und grabenlose Leitungsverlegung.",
icon: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z",
backgroundImage: "/assets/photos/DSC08653.JPG"
}
]}
/>
</div>

193
public/germany-map.svg Normal file
View File

@@ -0,0 +1,193 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg class="w-full h-full text-primary opacity-20 drop-shadow-2xl" version="1.0" xmlns="http://www.w3.org/2000/svg"
width="1024.000000pt" height="1024.000000pt" viewBox="0 0 1024.000000 1024.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.10, written by Peter Selinger 2001-2011
</metadata>
<g transform="translate(0.000000,1024.000000) scale(0.100000,-0.100000)"
fill="#2A303C" stroke="none">
<path d="M3047 10193 c-74 -128 -89 -173 -90 -273 -2 -79 1 -95 13 -95 12 0
16 16 18 72 4 89 15 102 72 83 34 -11 45 -11 77 3 55 23 143 33 143 15 0 -7
18 -35 40 -63 22 -27 40 -55 40 -62 0 -6 18 -33 40 -60 21 -26 51 -62 65 -79
14 -18 25 -44 25 -58 0 -22 4 -26 28 -26 17 0 37 -9 49 -22 22 -25 47 -68 41
-73 -2 -1 -29 -10 -61 -19 -45 -13 -57 -21 -53 -32 3 -9 6 -20 6 -25 0 -5 20
-9 44 -9 33 0 52 7 81 30 l39 31 13 -32 c16 -40 17 -39 -5 -39 -10 0 -39 -13
-65 -30 -46 -29 -52 -30 -166 -30 -104 0 -120 -2 -136 -20 -17 -18 -16 -19 11
-26 42 -10 46 -14 33 -30 -6 -8 -26 -14 -45 -14 -25 0 -34 -5 -40 -22 -5 -18
2 -29 37 -59 l44 -37 29 21 c17 12 49 22 75 24 25 1 69 4 96 6 28 1 56 7 64
11 10 7 12 5 6 -5 -18 -31 -38 -48 -64 -54 -21 -5 -29 -16 -43 -58 -16 -50
-15 -53 5 -82 11 -16 21 -31 22 -33 1 -1 28 1 60 6 l57 9 15 -32 c11 -23 14
-46 9 -79 -8 -50 -18 -55 -71 -32 -32 13 -75 10 -75 -6 0 -19 50 -94 85 -128
32 -31 43 -35 88 -36 139 -1 220 -16 286 -52 58 -32 66 -41 92 -98 24 -53 36
-67 72 -85 50 -26 53 -30 61 -80 5 -32 13 -42 59 -68 41 -24 169 -61 210 -61
4 0 7 -10 7 -21 0 -18 -4 -20 -27 -15 -16 3 -53 10 -83 15 -30 5 -81 21 -112
35 -47 21 -61 33 -79 68 -12 24 -50 75 -84 113 -34 39 -74 87 -90 108 -57 77
-167 107 -265 72 -46 -16 -176 -19 -268 -6 -40 6 -61 15 -87 41 -31 30 -36 32
-62 21 -56 -23 -79 -55 -127 -172 -25 -63 -46 -119 -46 -124 0 -5 17 -38 39
-74 55 -94 57 -106 27 -141 -15 -18 -27 -49 -31 -78 -4 -26 -9 -56 -12 -67 -6
-20 -6 -20 -14 0 -15 39 -9 103 12 140 12 19 19 41 16 48 -3 8 -32 23 -64 35
-33 12 -70 33 -83 47 -17 18 -34 25 -60 25 -31 0 -38 -5 -52 -33 -8 -17 -19
-47 -22 -65 -8 -32 -7 -32 32 -32 43 0 52 -10 52 -59 0 -18 -12 -38 -33 -59
-43 -42 -78 -42 -107 -2 -14 20 -30 30 -48 30 -37 1 -62 22 -62 53 0 20 6 27
29 32 16 3 36 14 45 24 15 16 14 22 -14 77 -24 48 -36 62 -65 73 -33 11 -36
15 -30 41 5 25 2 31 -23 44 -24 13 -49 13 -158 5 -71 -5 -169 -19 -217 -29
-63 -14 -94 -17 -107 -10 -26 14 -81 12 -179 -5 -89 -15 -128 -36 -201 -102
-41 -36 -49 -53 -27 -53 6 0 18 -4 26 -9 11 -7 10 -14 -4 -39 -16 -26 -21 -28
-36 -19 -15 9 -22 8 -33 -3 -17 -19 -56 -136 -56 -171 0 -42 20 -51 112 -49
147 4 142 8 108 -87 -11 -32 -20 -70 -20 -86 0 -17 -5 -47 -12 -69 -9 -29 -9
-41 0 -50 8 -8 12 -49 12 -114 l0 -101 -54 -84 c-47 -73 -58 -100 -82 -205
l-29 -122 -53 -5 c-30 -3 -65 -2 -79 4 -43 16 -113 11 -149 -12 -38 -23 -44
-51 -16 -82 9 -11 12 -17 6 -14 -7 4 -22 -1 -35 -12 -21 -17 -22 -21 -11 -59
9 -29 23 -47 48 -62 31 -20 46 -22 142 -19 l107 4 42 -45 42 -46 -30 -49 c-27
-45 -29 -56 -25 -108 6 -56 4 -60 -25 -85 -17 -15 -72 -50 -122 -77 -72 -39
-96 -58 -109 -85 -24 -50 -22 -66 17 -117 19 -25 35 -49 35 -52 0 -15 -155
-62 -282 -87 -131 -25 -138 -28 -138 -50 0 -13 -3 -24 -6 -24 -3 0 -54 21
-112 47 -80 36 -107 44 -110 34 -9 -28 -40 -48 -83 -55 -24 -4 -60 -11 -80
-16 l-36 -10 14 -62 c10 -44 25 -75 54 -109 29 -35 39 -56 39 -82 0 -29 7 -41
43 -70 97 -80 93 -73 101 -185 l8 -101 -82 -121 c-45 -67 -80 -123 -78 -125 2
-2 23 0 48 5 25 5 46 7 48 5 2 -2 -8 -26 -22 -53 -22 -43 -36 -56 -101 -92
-63 -35 -82 -41 -111 -36 l-34 7 0 -54 0 -55 63 -17 c57 -15 66 -21 90 -61 15
-25 27 -47 27 -50 0 -4 -13 -23 -30 -44 -29 -37 -36 -71 -24 -117 5 -18 14
-23 56 -25 l50 -3 23 -54 c20 -49 56 -83 110 -105 6 -2 -10 -21 -34 -43 l-43
-38 18 -38 c16 -33 22 -37 56 -37 27 0 46 -8 69 -28 l31 -28 -12 -57 -13 -58
33 -34 34 -35 -33 0 c-24 0 -50 -13 -95 -46 -33 -26 -67 -49 -73 -51 -7 -3
-22 -37 -34 -77 -31 -102 -22 -200 25 -293 30 -60 43 -75 112 -122 51 -36 96
-59 132 -67 30 -7 58 -16 62 -21 4 -4 15 -32 23 -62 l16 -55 -85 -79 -84 -79
0 -79 0 -79 31 0 c22 0 52 -15 100 -51 66 -49 69 -53 69 -93 0 -35 6 -48 36
-77 20 -18 49 -62 65 -95 l28 -62 76 -25 75 -26 0 50 c0 27 5 49 10 49 6 0 45
-10 86 -22 82 -24 94 -37 94 -101 l1 -32 25 29 26 29 63 -11 c87 -17 100 -15
145 13 l40 25 65 -17 c59 -16 66 -20 79 -54 15 -36 20 -39 108 -66 90 -28 97
-29 214 -20 l121 9 51 -36 c46 -34 58 -37 136 -43 47 -3 86 -7 86 -9 0 -1 -33
-59 -72 -127 -71 -121 -77 -129 -193 -227 -66 -57 -126 -109 -133 -118 -7 -9
-12 -45 -12 -88 0 -64 -6 -85 -55 -204 -30 -73 -71 -168 -91 -211 -23 -52 -42
-116 -54 -187 l-18 -107 24 -41 23 -41 -44 -112 c-40 -98 -45 -120 -45 -188 0
-55 6 -94 21 -133 11 -30 22 -56 23 -58 2 -1 19 5 39 13 l37 15 0 -45 c0 -25
4 -45 9 -45 10 0 19 3 106 38 l51 20 57 -24 c51 -22 67 -24 132 -18 66 6 77
10 106 40 30 31 38 34 90 34 51 0 60 -3 82 -30 l25 -30 119 16 c182 24 167 16
159 77 -4 28 -9 53 -11 55 -2 2 -20 -5 -42 -16 -70 -36 -78 -36 -95 0 -21 46
-5 87 52 132 37 30 53 36 93 36 41 0 51 -4 76 -32 27 -31 28 -35 16 -65 -12
-29 -12 -32 8 -37 12 -3 41 -6 65 -6 l43 0 -7 35 c-4 22 -2 35 4 35 6 0 17 3
25 6 11 4 20 -9 32 -48 10 -29 18 -54 19 -55 1 -1 30 6 64 16 l61 19 83 -24
c46 -13 108 -24 139 -24 54 0 59 -3 189 -95 l133 -95 68 0 c68 0 68 0 110 45
23 25 45 45 49 45 4 0 13 -11 20 -25 11 -21 21 -25 59 -25 75 0 113 -16 166
-70 l49 -50 -12 -47 c-15 -56 -12 -60 71 -69 31 -4 57 -11 57 -16 0 -5 -7 -25
-15 -45 -8 -20 -13 -38 -11 -40 7 -7 168 24 175 34 3 5 28 41 54 80 45 65 48
73 39 105 -10 35 -5 138 7 138 3 -1 26 -5 51 -10 28 -5 59 -5 83 1 35 9 43 7
88 -22 l50 -32 81 22 c44 12 81 21 82 19 1 -2 12 -41 25 -88 12 -47 26 -89 31
-94 13 -13 147 8 208 32 56 23 54 24 66 -30 l6 -28 76 53 c48 33 80 62 86 80
10 26 13 27 76 27 37 0 72 4 77 8 6 4 24 28 40 54 30 47 31 48 85 49 30 0 152
6 270 12 240 13 222 6 206 88 -7 34 -5 38 27 58 19 11 37 21 41 21 3 0 6 -16
6 -35 0 -19 1 -35 3 -35 1 0 34 9 72 20 38 11 77 20 87 20 9 0 28 -20 42 -45
25 -42 29 -45 71 -45 24 0 65 9 92 21 39 17 59 19 109 14 l62 -6 7 -56 c5 -38
17 -72 38 -104 35 -52 52 -59 143 -59 l54 0 0 48 c0 41 5 53 30 77 33 32 35
42 19 98 -12 43 -40 60 -120 71 l-46 7 40 77 40 78 -23 57 c-18 44 -34 63 -70
88 -40 28 -49 39 -56 78 -8 40 -13 47 -39 51 -30 5 -30 6 -27 60 3 46 10 62
44 102 37 44 44 48 84 48 38 0 51 6 88 40 40 37 52 42 145 59 109 20 91 9 247
146 33 29 35 34 41 115 5 74 10 90 36 122 l29 36 71 -39 c119 -66 113 -64 152
-43 20 10 49 36 66 57 l31 38 -23 61 -22 61 24 69 24 69 -32 29 c-18 17 -38
39 -45 49 -8 11 -29 23 -48 28 -27 7 -36 17 -48 50 -13 33 -22 43 -52 52 -51
15 -71 14 -85 -6 -11 -15 -16 -14 -57 12 -40 25 -47 35 -57 81 -10 45 -20 60
-61 94 -42 35 -51 39 -74 31 -22 -9 -28 -6 -50 22 -14 18 -31 38 -38 44 -81
78 -142 149 -140 163 1 8 -18 30 -43 47 -45 33 -92 42 -112 22 -17 -17 -112
28 -119 57 -4 14 -23 33 -47 46 -43 24 -54 46 -56 110 -1 32 -5 37 -30 43 -26
6 -31 12 -36 49 -8 49 -12 57 -40 72 -14 8 -20 22 -20 45 -1 31 -5 35 -42 48
-38 14 -73 52 -73 80 0 6 14 17 30 26 24 13 30 22 30 49 0 27 5 35 23 40 23 5
74 92 62 105 -4 3 -21 11 -38 17 -26 9 -31 16 -29 38 3 26 -4 30 -40 27 -12
-2 -18 5 -18 17 0 13 -18 29 -59 51 -32 17 -66 31 -75 31 -9 0 -16 8 -16 20 0
12 -14 29 -35 42 -29 19 -34 27 -30 47 10 39 -12 77 -56 96 -40 18 -52 48 -25
58 9 4 12 14 9 25 -7 29 35 41 54 16 7 -10 13 -24 13 -31 0 -7 8 -13 18 -13
22 0 72 -62 72 -89 0 -14 7 -21 19 -21 15 0 22 12 31 51 10 41 23 61 69 106
131 128 140 133 239 133 51 0 73 5 92 20 14 11 32 20 41 20 8 0 19 4 25 10 6
6 25 -1 54 -20 35 -23 52 -29 75 -24 39 8 85 52 85 80 0 21 6 23 81 30 75 6
81 9 90 33 6 14 20 36 31 48 16 17 25 20 45 12 28 -10 43 -5 43 17 0 8 5 14
11 14 7 0 18 8 25 17 12 17 14 17 36 -3 34 -31 69 -36 96 -14 12 10 22 27 22
37 0 10 4 23 9 29 5 5 11 18 13 28 3 22 88 48 125 39 12 -3 32 2 43 10 16 11
37 14 75 10 33 -4 55 -1 59 6 4 6 16 11 26 11 14 0 19 6 17 22 -3 32 52 61
123 64 32 1 68 9 81 17 13 9 43 20 66 26 23 7 50 22 61 35 15 19 32 25 86 31
56 6 69 11 76 29 10 28 6 33 -34 40 -22 4 -37 14 -41 26 -4 11 -17 23 -31 26
-29 7 -28 5 -7 49 9 19 24 35 32 35 29 0 91 -21 91 -30 0 -13 47 -13 72 1 16
9 24 7 42 -10 12 -12 29 -21 37 -21 8 0 20 -9 27 -20 10 -16 9 -27 -3 -57 -17
-41 -13 -53 15 -35 37 23 49 16 39 -25 -5 -21 -9 -41 -9 -44 0 -13 108 -59
137 -59 36 0 63 27 63 63 0 19 11 33 40 54 28 20 40 35 40 53 0 14 10 41 21
60 12 19 41 102 65 185 48 163 49 187 8 242 -14 18 -24 45 -24 60 2 56 -8 115
-21 128 -8 8 -61 31 -119 53 -119 45 -125 53 -90 123 27 53 21 100 -17 124
-44 29 -73 66 -73 93 0 14 -12 38 -26 53 -20 21 -25 34 -20 53 3 14 13 26 21
26 8 0 15 4 15 8 0 4 16 25 35 45 36 38 39 48 36 107 -1 19 5 49 13 65 25 47
20 72 -15 91 -27 13 -30 19 -24 42 3 15 8 58 10 96 l4 68 -60 27 c-46 20 -59
31 -59 47 0 12 -10 47 -21 78 -22 56 -20 106 3 106 7 0 25 12 40 27 29 27 36
61 18 79 -7 7 -7 21 1 43 17 49 -1 72 -93 117 -60 29 -92 52 -126 92 -25 29
-62 60 -81 68 -20 8 -52 30 -71 48 -21 21 -50 36 -73 40 -37 7 -38 9 -31 34 5
15 9 55 9 90 l0 63 75 45 c41 25 87 57 103 72 32 31 51 92 43 140 -4 26 2 46
25 87 29 51 30 55 15 84 -9 16 -16 42 -16 57 0 45 -51 218 -71 240 -14 15 -19
35 -19 75 0 33 -6 63 -15 75 -8 10 -15 33 -15 50 0 34 -16 54 -42 54 -25 0
-23 13 4 25 30 13 34 41 9 55 -16 8 -28 7 -53 -6 -61 -31 -165 -8 -273 61 -22
14 -52 31 -67 37 -41 18 -36 50 15 92 l42 35 -69 66 c-51 48 -63 65 -48 65 11
0 25 7 32 15 21 25 43 19 36 -10 -5 -20 -3 -25 12 -25 25 0 42 19 42 47 0 15
6 23 18 23 24 0 62 -28 62 -45 0 -7 10 -15 23 -17 18 -2 22 -9 22 -38 0 -31
-3 -35 -23 -33 -13 2 -32 -2 -42 -7 -18 -10 -19 -8 -14 20 6 28 5 30 -24 30
-40 0 -46 -11 -26 -53 24 -51 13 -81 -33 -93 -42 -10 -55 -37 -26 -52 20 -11
132 3 148 19 5 5 52 9 105 9 l97 0 0 46 c0 43 -3 48 -56 92 -30 26 -75 66
-100 88 -32 29 -59 44 -87 48 -49 9 -105 52 -128 101 -13 26 -23 35 -42 35
-17 0 -28 -7 -31 -20 -3 -11 -13 -20 -22 -20 -24 0 -33 11 -21 25 23 28 -20
22 -66 -9 -35 -23 -64 -35 -102 -38 -62 -6 -67 -10 -52 -38 10 -19 9 -21 -11
-18 -15 2 -26 14 -33 36 -6 18 -17 37 -24 41 -8 4 -10 15 -6 26 7 16 3 17 -34
11 -33 -5 -42 -3 -42 8 0 8 -6 19 -13 23 -8 4 -22 20 -32 36 -11 18 -26 27
-43 27 -14 0 -33 7 -42 15 -9 8 -34 18 -56 23 -33 7 -39 12 -39 34 0 14 -8 35
-18 46 -12 13 -15 22 -8 27 18 11 12 35 -8 35 -50 0 -60 51 -20 95 10 12 22
35 25 52 3 18 15 40 25 49 11 9 19 28 19 41 0 12 5 23 10 23 6 0 10 9 10 21 0
15 -5 20 -22 17 -17 -2 -24 -12 -29 -38 -3 -19 -11 -46 -19 -60 -8 -14 -16
-41 -20 -60 -7 -45 -19 -47 -220 -49 -129 -1 -166 3 -218 19 -106 33 -116 30
-166 -48 -24 -37 -54 -88 -66 -112 -18 -39 -33 -51 -97 -85 -44 -23 -92 -58
-113 -82 -35 -40 -41 -43 -88 -43 -27 0 -92 -9 -145 -20 -73 -15 -111 -17
-166 -11 -71 7 -72 7 -111 -26 -31 -26 -43 -47 -56 -95 l-17 -60 -48 6 c-56 8
-111 -11 -128 -42 -15 -28 -14 -32 9 -32 10 0 30 -5 44 -11 21 -10 23 -15 14
-35 -13 -28 -17 -29 -43 -11 -11 7 -35 19 -53 27 -29 11 -34 11 -45 -4 -19
-26 -67 -5 -67 29 0 18 -7 26 -24 31 -14 3 -27 12 -30 20 -7 19 -66 18 -155
-2 -58 -13 -76 -22 -93 -45 -27 -36 -34 -36 -50 -1 -9 19 -27 31 -62 42 -65
21 -83 52 -53 95 11 16 21 31 22 32 1 1 18 -2 39 -8 36 -10 39 -8 79 28 23 21
70 56 105 78 l64 40 -6 65 c-3 36 -8 77 -12 91 -4 19 1 34 22 59 l29 33 -61
-6 c-34 -4 -75 -2 -93 3 -26 7 -34 6 -44 -11 -6 -10 -20 -19 -31 -19 -10 0
-41 -16 -67 -35 -69 -49 -112 -48 -163 4 -31 32 -49 41 -99 51 -34 6 -77 20
-97 30 -67 34 -116 42 -176 29 -51 -12 -56 -16 -85 -66 -31 -53 -52 -66 -63
-38 -5 15 12 55 24 55 3 0 11 4 19 9 10 7 11 13 1 30 -9 17 -9 26 0 36 9 11 5
18 -22 35 -38 24 -80 22 -212 -11 -64 -16 -81 -18 -87 -7 -17 26 -8 37 39 52
26 9 66 34 90 57 39 37 42 44 42 92 0 46 -3 55 -32 79 l-33 27 27 1 c34 0 44
16 20 29 -10 6 -26 30 -36 54 -10 28 -26 48 -42 56 -23 10 -26 9 -35 -19 -9
-26 -15 -29 -37 -24 -15 3 -34 15 -43 25 -10 10 -59 34 -110 52 -72 25 -95 38
-99 54 -5 21 -9 20 -85 -27 -81 -49 -94 -50 -95 -6 0 17 -4 16 -32 -11 -18
-17 -48 -34 -66 -38 -30 -6 -37 -3 -65 27 -28 30 -47 38 -142 63 -138 35 -205
45 -227 33 -12 -6 -26 -4 -42 7 -22 14 -28 15 -52 2 -24 -12 -35 -12 -82 0
-50 13 -56 13 -64 -1 -7 -13 -29 -16 -117 -16 -94 0 -112 3 -135 21 -21 17
-26 29 -26 65 0 39 3 46 28 55 45 18 54 24 50 32 -2 5 5 13 16 19 15 8 16 12
5 19 -35 22 -64 10 -92 -38z"/>
<path d="M3145 9843 c-17 -2 -37 -13 -44 -25 -21 -33 14 -60 85 -66 51 -4 59
-2 74 18 12 16 15 31 11 49 -6 23 -13 26 -52 27 -24 1 -58 -1 -74 -3z"/>
<path d="M2992 9757 c-28 -30 -28 -48 1 -75 27 -26 59 -28 78 -6 11 13 10 17
-4 25 -9 6 -25 26 -35 45 l-18 35 -22 -24z"/>
<path d="M7960 9741 c-76 -7 -130 -38 -130 -75 0 -13 -9 -31 -20 -41 -29 -27
-16 -31 19 -6 20 14 28 27 24 37 -6 16 12 34 35 34 19 0 14 -25 -8 -45 -11
-10 -20 -29 -20 -42 0 -20 -5 -23 -41 -23 -38 0 -41 -2 -35 -22 3 -13 6 -26 6
-30 0 -5 14 -8 30 -8 17 0 30 -4 30 -10 0 -5 -15 -10 -34 -10 -25 0 -40 -7
-55 -26 -25 -32 -26 -44 -6 -44 8 0 15 -4 15 -10 0 -5 15 -10 34 -10 22 0 36
-6 40 -16 13 -34 6 -41 -44 -48 -46 -6 -50 -9 -50 -34 0 -19 10 -35 35 -55 19
-15 35 -34 35 -42 0 -8 8 -15 18 -15 9 0 50 -14 91 -30 83 -34 94 -36 110 -16
9 10 9 20 0 39 -14 31 3 67 31 67 11 0 20 7 20 15 0 9 9 15 24 15 14 0 37 5
52 10 20 8 32 7 50 -5 19 -12 27 -13 44 -3 29 19 50 -4 27 -30 -16 -17 -15
-19 18 -34 35 -15 35 -15 35 9 0 13 12 35 26 48 15 14 22 25 17 25 -5 0 -26
16 -46 35 -28 27 -44 35 -72 35 -32 0 -38 4 -51 35 -24 58 -19 74 31 96 56 25
71 62 36 90 -19 16 -35 17 -119 12 l-98 -6 -32 33 c-37 37 -37 49 1 68 30 15
37 43 10 40 -10 0 -47 -4 -83 -7z"/>
<path d="M3325 9562 c-27 -8 -52 -16 -54 -18 -9 -8 31 -54 48 -54 31 0 63 33
59 61 l-3 25 -50 -14z"/>
<path d="M3140 9558 c0 -7 5 -20 10 -28 8 -12 10 -11 10 7 0 12 -4 25 -10 28
-5 3 -10 0 -10 -7z"/>
<path d="M5687 9532 c-19 -20 -27 -39 -27 -65 0 -40 22 -59 43 -38 17 17 37
13 37 -9 0 -10 8 -23 18 -28 19 -10 202 -13 201 -3 0 3 -17 33 -37 65 -35 56
-41 60 -97 76 -114 32 -109 32 -138 2z"/>
<path d="M3110 9465 c0 -19 27 -55 42 -55 20 0 23 12 8 41 -9 16 -50 28 -50
14z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB