diff --git a/.npmrc b/.npmrc
index b0b0f7051..c7c69bd37 100644
--- a/.npmrc
+++ b/.npmrc
@@ -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
diff --git a/app/[locale]/template.tsx b/app/[locale]/template.tsx
new file mode 100644
index 000000000..4d690b91a
--- /dev/null
+++ b/app/[locale]/template.tsx
@@ -0,0 +1,19 @@
+'use client';
+
+import { motion } from 'framer-motion';
+
+export default function Template({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/components/blocks/BenefitGrid.tsx b/components/blocks/BenefitGrid.tsx
index 0c76ad2ab..dd2cee38f 100644
--- a/components/blocks/BenefitGrid.tsx
+++ b/components/blocks/BenefitGrid.tsx
@@ -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) => (
-
-
+ {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 (
+
+
+
+ {/* Subtle Background Logo Arcs */}
+
+
+
@@ -125,7 +145,8 @@ export function BenefitGrid({ badge, title, description, benefits }: BenefitGrid
- ))}
+ );
+ })}
diff --git a/components/blocks/CompetenceBentoGrid.tsx b/components/blocks/CompetenceBentoGrid.tsx
index 340711adf..acc9dcb3c 100644
--- a/components/blocks/CompetenceBentoGrid.tsx
+++ b/components/blocks/CompetenceBentoGrid.tsx
@@ -6,8 +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;
@@ -113,7 +114,14 @@ export function CompetenceBentoGrid(props: CompetenceBentoGridProps) {
className={gridClasses}
>
+
+ {/* Subtle Background Logo Arcs */}
+
+
+
+
+
{item.title}
{item.description && {item.description}
}
@@ -148,6 +156,12 @@ export function CompetenceBentoGrid(props: CompetenceBentoGridProps) {
)}
+
+
+ {/* Subtle Background Logo Arcs */}
+
+
+
{item.tag && (
diff --git a/components/blocks/SubCompanyTiles.tsx b/components/blocks/SubCompanyTiles.tsx
index b141e4a0f..eea21755a 100644
--- a/components/blocks/SubCompanyTiles.tsx
+++ b/components/blocks/SubCompanyTiles.tsx
@@ -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;
@@ -91,6 +93,19 @@ export function SubCompanyTiles(props: SubCompanyTilesProps) {
const isEtib = company.title.includes('E-TIB');
const isIng = company.title.includes('Ingenieur');
const isNemo = company.title.includes('NEMO');
+
+ 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 (
+
+ {/* Subtle Background Logo Arcs */}
+
+
+
{/* Premium Shine Sweep Effect (Industrial Reflection) */}
+ {/* Glossy Animated Border */}
+
+
{/* Current Site Indicator Badge */}
{isCurrent && (
diff --git a/components/decorations/CorporateBackground.tsx b/components/decorations/CorporateBackground.tsx
index 581d9e09e..e02583822 100644
--- a/components/decorations/CorporateBackground.tsx
+++ b/components/decorations/CorporateBackground.tsx
@@ -2,24 +2,7 @@
import React, { useRef, useEffect } from 'react';
import { motion, useScroll, useTransform, useSpring } from 'framer-motion';
-
-// Reusable SVG component that precisely mimics the overlapping, sweeping arcs of the E-TIB logo.
-const LogoArcs = ({ className }: { className?: string }) => (
-
-);
+import { LogoArcs } from '@/components/ui/LogoArcs';
function MagneticRing({
children,
diff --git a/components/layout/Header.tsx b/components/layout/Header.tsx
index 45c42c321..b0e9fee03 100644
--- a/components/layout/Header.tsx
+++ b/components/layout/Header.tsx
@@ -125,7 +125,7 @@ export function Header({ navLinks }: HeaderProps) {
{loc.flag}
diff --git a/components/layout/MobileBottomNav.tsx b/components/layout/MobileBottomNav.tsx
index d136056e2..b4bd50ddd 100644
--- a/components/layout/MobileBottomNav.tsx
+++ b/components/layout/MobileBottomNav.tsx
@@ -72,7 +72,7 @@ export function MobileBottomNav({ navLinks, currentLocale }: MobileBottomNavProp
diff --git a/components/providers/TransitionProvider.tsx b/components/providers/TransitionProvider.tsx
index 095d993db..7ceeee085 100644
--- a/components/providers/TransitionProvider.tsx
+++ b/components/providers/TransitionProvider.tsx
@@ -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(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]);
diff --git a/components/ui/AnimatedGlossyBorder.tsx b/components/ui/AnimatedGlossyBorder.tsx
new file mode 100644
index 000000000..f9be55dca
--- /dev/null
+++ b/components/ui/AnimatedGlossyBorder.tsx
@@ -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 (
+
+ );
+}
diff --git a/components/ui/Badge.tsx b/components/ui/Badge.tsx
index 8d8f22f54..874ffc1cc 100644
--- a/components/ui/Badge.tsx
+++ b/components/ui/Badge.tsx
@@ -10,7 +10,7 @@ export function Badge({ children, className, variant = 'primary' }: { children:
};
return (
-
+
{children}
);
diff --git a/components/ui/Button.tsx b/components/ui/Button.tsx
index f592d045f..c9aee0631 100644
--- a/components/ui/Button.tsx
+++ b/components/ui/Button.tsx
@@ -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 {
@@ -19,7 +20,7 @@ export interface ButtonProps extends React.ButtonHTMLAttributes
+
+ {!isWhiteBg && (
+
)}
- />
+ >
);
}
diff --git a/components/ui/Label.tsx b/components/ui/Label.tsx
index 82cf925dd..540c90b78 100644
--- a/components/ui/Label.tsx
+++ b/components/ui/Label.tsx
@@ -9,7 +9,7 @@ export function Label({ className, ...props }: LabelProps) {
return (