feat: overhaul competence pages, unify brand colors, and polish site integrity
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 23s
Build & Deploy / 🧪 QA (push) Failing after 1m8s
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 1s
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 23s
Build & Deploy / 🧪 QA (push) Failing after 1m8s
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 1s
- Rewrote all 5 competence pages (Kabeltiefbau, Bohrtechnik, Glasfaser, Planung, Vermessung) with factual data from references - Unified brand colors: replaced toxic neon green with brand-aligned teal (#10a379) - Fixed ReferencesSlider injection in dynamic MDX pages - Enhanced Footer with icon parity, improved branding placement, and unified hover effects - Refined Header with 3D micro-animations and improved dropdown visibility - Fixed localized tracking API (rewrites for /stats/api/send) - Resolved ReferenceErrors in InteractiveGermanyMap and Header state management - Cleaned reference titles and truncated metadata in lists
This commit is contained in:
@@ -16,7 +16,7 @@ export function OGImageTemplate({
|
||||
mode = 'dark',
|
||||
}: OGImageTemplateProps) {
|
||||
const primaryBlue = '#001a4d';
|
||||
const accentGreen = '#82ed20';
|
||||
const accentGreen = '#10a379';
|
||||
const saturatedBlue = '#011dff';
|
||||
|
||||
const containerStyle: React.CSSProperties = {
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { MapPin, Factory, Zap, CheckCircle2 } from 'lucide-react';
|
||||
import { MapPin, Factory, Zap, CheckCircle2, ArrowUpRight } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { AnimatedGlossyBorder } from '@/components/ui/AnimatedGlossyBorder';
|
||||
import { Location, defaultLocations } from '@/lib/map-data';
|
||||
|
||||
interface Stat {
|
||||
@@ -42,7 +43,10 @@ export function InteractiveGermanyMap({
|
||||
|
||||
return (
|
||||
<div className={isHero ? "relative w-full" : "relative w-full max-w-7xl mx-auto py-16 md:py-24 px-4 sm:px-6 mt-16 md:mt-20"}>
|
||||
<div className={`bg-[#050B14] ${isHero ? 'pt-32 pb-16 md:pt-40 md:pb-24' : 'rounded-[2.5rem] md:rounded-[3.5rem] shadow-2xl border border-white/5'} overflow-visible relative`}>
|
||||
<div className={`bg-[#050B14] ${isHero ? 'pt-32 pb-16 md:pt-40 md:pb-24' : 'rounded-[2.5rem] md:rounded-[3.5rem] shadow-2xl border border-white/5'} overflow-visible relative group/map`}>
|
||||
{/* Animated Border Glow */}
|
||||
{!isHero && <AnimatedGlossyBorder opacity={0.7} className="z-30" />}
|
||||
|
||||
{/* Background Effects */}
|
||||
<div className={`absolute inset-0 bg-gradient-to-br from-[#050B14] via-[#0A1322] to-[#050B14] ${!isHero && 'rounded-[2.5rem] md:rounded-[3.5rem]'} overflow-hidden`} />
|
||||
<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 overflow-hidden" />
|
||||
@@ -91,7 +95,7 @@ export function InteractiveGermanyMap({
|
||||
{/* 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">
|
||||
<div className="absolute inset-0 opacity-[0.25] mix-blend-screen drop-shadow-2xl brightness-200">
|
||||
<Image
|
||||
src="/germany-map.svg"
|
||||
alt="Deutschlandkarte"
|
||||
|
||||
@@ -49,11 +49,11 @@ export const JobListingBlock = (props: JobListingBlockProps) => {
|
||||
title,
|
||||
showJobs = true,
|
||||
showFairs,
|
||||
fairsTitle = 'Nächste Messetermine 2026',
|
||||
fairsTitle = 'Nächste Messetermine',
|
||||
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' }
|
||||
{ name: 'Intersolar München', date: 'Termin folgt', type: 'Energie-Messe', location: 'Messe München' },
|
||||
{ name: 'Windenergietage Linstow', date: 'Termin folgt', type: 'Fachkongress', location: 'Linstow' },
|
||||
{ name: 'Kabelwerkstatt Wiesbaden', date: 'Termin folgt', type: 'Fachmesse', location: 'Wiesbaden' }
|
||||
],
|
||||
emptyStateMessage = 'Aktuell sind alle Positionen besetzt. Senden Sie uns gerne eine Initiativbewerbung!',
|
||||
emptyStateLinkText = 'Jetzt Kontakt aufnehmen',
|
||||
|
||||
@@ -81,7 +81,7 @@ export function ReferencesSlider(props: ReferencesSliderProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="py-24 bg-neutral-dark text-white relative overflow-hidden">
|
||||
<section id="referenzen" className="py-24 bg-neutral-dark text-white relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-neutral-dark via-neutral-900 to-neutral-dark z-0" />
|
||||
|
||||
<div className="container relative z-10 mb-12 flex flex-col md:flex-row justify-between items-end gap-6">
|
||||
@@ -129,11 +129,11 @@ export function ReferencesSlider(props: ReferencesSliderProps) {
|
||||
|
||||
<HoverShineOverlay />
|
||||
|
||||
<div className="absolute bottom-6 left-8 right-8 transform translate-y-4 group-hover:translate-y-0 transition-transform duration-500 pointer-events-none">
|
||||
<span className="inline-block py-1.5 px-4 rounded-full bg-primary/30 backdrop-blur-md text-white text-xs font-bold uppercase tracking-widest mb-3 border border-primary/50">
|
||||
<div className="absolute bottom-6 left-6 right-6 transform translate-y-4 group-hover:translate-y-0 transition-transform duration-500 pointer-events-none bg-black/60 backdrop-blur-sm p-4 rounded-xl border border-white/10">
|
||||
<span className="inline-block py-1 px-3 rounded-full bg-primary/20 text-white text-[10px] font-bold uppercase tracking-widest mb-2 border border-primary/30">
|
||||
{ref.category}
|
||||
</span>
|
||||
<h4 className="font-heading text-2xl md:text-3xl font-bold line-clamp-2">{ref.title}</h4>
|
||||
<h4 className="font-heading text-xl md:text-2xl font-bold leading-tight break-words">{ref.title}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { TransitionLink } from '@/components/ui/TransitionLink';
|
||||
import Image from 'next/image';
|
||||
import { useLocale } from 'next-intl';
|
||||
import { ShieldCheck, Leaf, Lock, Accessibility, Zap } from 'lucide-react';
|
||||
import { ShieldCheck, Leaf, Lock, Accessibility, Zap, Mail, Phone } from 'lucide-react';
|
||||
import { LanguageSwitcher } from './LanguageSwitcher';
|
||||
|
||||
interface CompanyInfo {
|
||||
@@ -21,7 +21,7 @@ export function Footer({ companyInfo }: FooterProps) {
|
||||
<footer className="bg-[#050B14] text-neutral-light py-10 md:py-16 relative overflow-hidden border-t border-white/5">
|
||||
{/* Subtle background tech grid */}
|
||||
<div className="absolute inset-0 bg-[linear-gradient(to_right,#8080800a_1px,transparent_1px),linear-gradient(to_bottom,#8080800a_1px,transparent_1px)] bg-[size:32px_32px] [mask-image:radial-gradient(ellipse_80%_50%_at_50%_0%,#000_70%,transparent_100%)] pointer-events-none" />
|
||||
|
||||
|
||||
{/* Top glowing edge */}
|
||||
<div className="absolute top-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-primary/40 to-transparent" />
|
||||
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-1/3 h-[200px] bg-primary/20 blur-[100px] rounded-full pointer-events-none opacity-50" />
|
||||
@@ -30,7 +30,7 @@ export function Footer({ companyInfo }: FooterProps) {
|
||||
{/* Brand Column Bento */}
|
||||
<div className="col-span-1 md:col-span-4 lg:col-span-5 flex flex-col justify-between bg-white/[0.02] border border-white/5 rounded-3xl p-6 md:p-8 lg:p-10 backdrop-blur-md relative group overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-white/[0.04] to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700" />
|
||||
|
||||
|
||||
<div className="space-y-4 md:space-y-6 relative z-10">
|
||||
<TransitionLink href={`/${locale}`} className="relative block h-12 md:h-14 w-40 md:w-48 mb-4 md:mb-6 group/logo cursor-pointer">
|
||||
<Image
|
||||
@@ -41,7 +41,7 @@ export function Footer({ companyInfo }: FooterProps) {
|
||||
/>
|
||||
</TransitionLink>
|
||||
<p className="text-neutral-400 text-base leading-relaxed max-w-sm">
|
||||
{locale === 'de'
|
||||
{locale === 'de'
|
||||
? 'Die Experten für Kabeltiefbau, Horizontalspülbohrungen und umfassende Infrastrukturprojekte. Qualität und Zuverlässigkeit aus Guben.'
|
||||
: 'Experts in cable construction, horizontal directional drilling, and comprehensive infrastructure projects. Quality and reliability from Guben.'}
|
||||
</p>
|
||||
@@ -58,16 +58,24 @@ export function Footer({ companyInfo }: FooterProps) {
|
||||
<p className="whitespace-pre-line font-medium text-neutral-200">{companyInfo.address}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3 md:gap-4">
|
||||
<a href={`mailto:${companyInfo.contactEmail}`} className="flex flex-col gap-2 p-4 rounded-2xl bg-white/[0.03] border border-white/[0.05] hover:bg-white/[0.08] hover:border-primary/30 transition-all group/contact cursor-pointer">
|
||||
<p className="text-xs text-neutral-400 uppercase tracking-wider font-heading">E-Mail</p>
|
||||
<a href={`mailto:${companyInfo.contactEmail}`} className="flex gap-4 p-4 rounded-2xl bg-white/[0.03] border border-white/[0.05] hover:bg-white/[0.08] hover:border-primary/30 transition-all group/contact cursor-pointer">
|
||||
<div className="w-10 h-10 rounded-full bg-primary/20 flex items-center justify-center shrink-0 group-hover/contact:bg-primary/30 transition-colors">
|
||||
<Mail className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="text-xs text-neutral-400 uppercase tracking-wider mb-1 font-heading">E-Mail</p>
|
||||
<p className="font-medium text-neutral-200 group-hover/contact:text-primary transition-colors truncate">{companyInfo.contactEmail}</p>
|
||||
</a>
|
||||
<a href={`tel:${companyInfo.contactPhone.replace(/\s+/g, '')}`} className="flex flex-col gap-2 p-4 rounded-2xl bg-white/[0.03] border border-white/[0.05] hover:bg-white/[0.08] hover:border-primary/30 transition-all group/contact cursor-pointer">
|
||||
<p className="text-xs text-neutral-400 uppercase tracking-wider font-heading">{locale === 'de' ? 'Telefon' : 'Phone'}</p>
|
||||
</div>
|
||||
</a>
|
||||
<a href={`tel:${companyInfo.contactPhone.replace(/\s+/g, '')}`} className="flex gap-4 p-4 rounded-2xl bg-white/[0.03] border border-white/[0.05] hover:bg-white/[0.08] hover:border-primary/30 transition-all group/contact cursor-pointer">
|
||||
<div className="w-10 h-10 rounded-full bg-primary/20 flex items-center justify-center shrink-0 group-hover/contact:bg-primary/30 transition-colors">
|
||||
<Phone className="w-5 h-5 text-primary" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="text-xs text-neutral-400 uppercase tracking-wider mb-1 font-heading">{locale === 'de' ? 'Telefon' : 'Phone'}</p>
|
||||
<p className="font-medium text-neutral-200 group-hover/contact:text-primary transition-colors truncate">{companyInfo.contactPhone}</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<div className="flex gap-4 p-4 rounded-2xl bg-white/[0.03] border border-white/[0.05] hover:bg-white/[0.05] transition-colors">
|
||||
<div className="w-10 h-10 rounded-full bg-primary/20 flex items-center justify-center shrink-0">
|
||||
<svg className="w-5 h-5 text-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
|
||||
@@ -86,7 +94,7 @@ export function Footer({ companyInfo }: FooterProps) {
|
||||
|
||||
{/* Navigation Bento Grid */}
|
||||
<div className="col-span-1 md:col-span-4 lg:col-span-7 grid grid-cols-1 sm:grid-cols-2 gap-4 md:gap-6 lg:gap-8">
|
||||
|
||||
|
||||
{/* Company Column */}
|
||||
<div className="bg-white/[0.02] border border-white/5 rounded-3xl p-6 md:p-8 lg:p-10 backdrop-blur-md relative group overflow-hidden flex flex-col">
|
||||
<div className="absolute inset-0 bg-gradient-to-bl from-white/[0.04] to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700 pointer-events-none" />
|
||||
@@ -129,7 +137,7 @@ export function Footer({ companyInfo }: FooterProps) {
|
||||
].map((link) => (
|
||||
<li key={link.href}>
|
||||
<TransitionLink href={link.href} className="group/link flex items-center text-neutral-400 hover:text-white transition-colors py-1 cursor-pointer">
|
||||
<span className="w-0 h-px bg-neutral-500 mr-0 group-hover/link:w-3 group-hover/link:mr-3 transition-all duration-300" />
|
||||
<span className="w-0 h-px bg-primary mr-0 group-hover/link:w-4 group-hover/link:mr-3 transition-all duration-300" />
|
||||
<span className="font-medium">{link.label}</span>
|
||||
</TransitionLink>
|
||||
</li>
|
||||
@@ -137,7 +145,7 @@ export function Footer({ companyInfo }: FooterProps) {
|
||||
</ul>
|
||||
|
||||
{/* Built Info */}
|
||||
|
||||
|
||||
{/* Legal Info removed from here */}
|
||||
</div>
|
||||
|
||||
@@ -148,31 +156,10 @@ export function Footer({ companyInfo }: FooterProps) {
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-4 md:gap-6 py-4 px-5 md:py-6 md:px-8 rounded-2xl bg-white/[0.02] border border-white/5 backdrop-blur-md text-center md:text-left">
|
||||
<div className="flex flex-col md:flex-row items-center gap-4 md:gap-6 h-full">
|
||||
<p className="text-neutral-400 text-xs font-medium m-0">© {new Date().getFullYear()} E-TIB GmbH. {locale === 'de' ? 'Alle Rechte vorbehalten.' : 'All rights reserved.'}</p>
|
||||
|
||||
<div className="hidden md:block w-px h-3 bg-neutral-800" />
|
||||
|
||||
<div className="flex items-center gap-3 text-neutral-400 text-xs">
|
||||
<a href="https://mintel.me" target="_blank" rel="noopener noreferrer" className="hover:text-neutral-200 transition-colors flex items-center gap-2">
|
||||
<span>Designed & Engineered by Mintel</span>
|
||||
</a>
|
||||
{process.env.NEXT_PUBLIC_APP_VERSION && (
|
||||
<>
|
||||
<span className="w-1 h-1 rounded-full bg-white/20" />
|
||||
<div className="flex items-center gap-2 opacity-60">
|
||||
<span className="font-mono text-[10px]">v{process.env.NEXT_PUBLIC_APP_VERSION.replace(/^v/, '')}</span>
|
||||
{process.env.NEXT_PUBLIC_TARGET && process.env.NEXT_PUBLIC_TARGET !== 'production' && (
|
||||
<span className="px-1.5 py-0.5 rounded-sm bg-white/10 text-[9px] uppercase tracking-wider">
|
||||
{process.env.NEXT_PUBLIC_TARGET}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-6">
|
||||
<LanguageSwitcher isSolidMode={false} />
|
||||
<LanguageSwitcher isSolidMode={false} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -183,6 +170,26 @@ export function Footer({ companyInfo }: FooterProps) {
|
||||
<div className="flex items-center gap-2"><Lock className="w-4 h-4 text-neutral-400" /><span>DSGVO Compliant</span></div>
|
||||
<div className="flex items-center gap-2"><Zap className="w-4 h-4 text-neutral-400" /><span>High Performance</span></div>
|
||||
</div>
|
||||
|
||||
{/* Mintel Design & Version */}
|
||||
<div className="mt-6 flex justify-center items-center gap-3 text-neutral-500 text-[10px] md:text-xs opacity-60 hover:opacity-100 transition-opacity">
|
||||
<a href="https://mintel.me" target="_blank" rel="noopener noreferrer" className="hover:text-neutral-300 transition-colors flex items-center gap-2">
|
||||
<span>Designed & Engineered by Marc Mintel</span>
|
||||
</a>
|
||||
{process.env.NEXT_PUBLIC_APP_VERSION && (
|
||||
<>
|
||||
<span className="w-1 h-1 rounded-full bg-white/20" />
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-mono text-[10px]">v{process.env.NEXT_PUBLIC_APP_VERSION.replace(/^v/, '')}</span>
|
||||
{process.env.NEXT_PUBLIC_TARGET && process.env.NEXT_PUBLIC_TARGET !== 'production' && (
|
||||
<span className="px-1.5 py-0.5 rounded-sm bg-white/10 text-[9px] uppercase tracking-wider">
|
||||
{process.env.NEXT_PUBLIC_TARGET}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
|
||||
@@ -20,6 +20,7 @@ interface HeaderProps {
|
||||
export function Header({ navLinks }: HeaderProps) {
|
||||
const [isScrolled, setIsScrolled] = React.useState(false);
|
||||
const [forceSolid, setForceSolid] = React.useState(false);
|
||||
const [headerVariant, setHeaderVariant] = React.useState<'capsule' | 'standard'>('capsule');
|
||||
const pathname = usePathname() || '/';
|
||||
|
||||
// Scroll bound shine effect (continuous looping as user scrolls)
|
||||
@@ -41,17 +42,18 @@ export function Header({ navLinks }: HeaderProps) {
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
|
||||
// Check for forced solid header (used by error pages, legal pages without hero, etc.)
|
||||
const checkSolid = () => {
|
||||
const checkHeaderState = () => {
|
||||
setForceSolid(document.body.getAttribute('data-force-solid-header') === 'true');
|
||||
setHeaderVariant((document.body.getAttribute('data-header-variant') as 'capsule' | 'standard') || 'capsule');
|
||||
};
|
||||
checkSolid();
|
||||
checkHeaderState();
|
||||
|
||||
// Set up observer for client-side navigation changes
|
||||
const observer = new MutationObserver(checkSolid);
|
||||
observer.observe(document.body, { attributes: true, attributeFilter: ['data-force-solid-header'] });
|
||||
const observer = new MutationObserver(checkHeaderState);
|
||||
observer.observe(document.body, { attributes: true, attributeFilter: ['data-force-solid-header', 'data-header-variant'] });
|
||||
|
||||
// Re-check when pathname changes
|
||||
checkSolid();
|
||||
checkHeaderState();
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
@@ -59,23 +61,26 @@ export function Header({ navLinks }: HeaderProps) {
|
||||
};
|
||||
}, [pathname]);
|
||||
|
||||
// Check for force solid initially
|
||||
|
||||
const isSolidMode = isScrolled || forceSolid;
|
||||
|
||||
const isSolidMode = isScrolled || forceSolid || headerVariant === 'standard';
|
||||
const isStandard = headerVariant === 'standard';
|
||||
|
||||
return (
|
||||
<>
|
||||
<header
|
||||
className={`fixed top-0 left-0 right-0 z-50 flex justify-center transition-all duration-700 ease-[cubic-bezier(0.16,1,0.3,1)] ${
|
||||
isSolidMode ? 'pt-4 px-4' : 'pt-0 px-0'
|
||||
isStandard
|
||||
? 'bg-[#050B14]/95 backdrop-blur-xl border-b border-white/10'
|
||||
: (isSolidMode ? 'pt-4 px-4' : 'pt-0 px-0')
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`relative 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'
|
||||
isStandard
|
||||
? 'py-4 px-6 md:px-8'
|
||||
: 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'
|
||||
}`}
|
||||
>
|
||||
{/* Scroll bound shine effect */}
|
||||
@@ -202,13 +207,17 @@ function NavItem({ link, currentLocale, pathname, isSolidMode }: { link: NavLink
|
||||
<AnimatePresence>
|
||||
{isHovered && link.children && (
|
||||
<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.2, ease: 'easeOut' }}
|
||||
className="absolute top-full left-1/2 -translate-x-1/2 pt-4 w-56"
|
||||
initial={{ opacity: 0, y: 10, scale: 0.95, rotateX: -10 }}
|
||||
animate={{ opacity: 1, y: 0, scale: 1, rotateX: 0 }}
|
||||
exit={{ opacity: 0, y: 10, scale: 0.95, rotateX: -10 }}
|
||||
transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
|
||||
style={{ perspective: 1000, transformOrigin: 'top center' }}
|
||||
className="absolute top-full left-1/2 -translate-x-1/2 pt-6 w-64 z-50"
|
||||
>
|
||||
<div className={`p-2 rounded-3xl shadow-[0_20px_40px_-15px_rgba(0,0,0,0.1)] border ${isSolidMode ? 'bg-white/95 border-neutral-200/60' : 'bg-[#050B14]/90 border-white/10'} backdrop-blur-xl flex flex-col gap-1`}>
|
||||
<div className={`p-2.5 rounded-3xl shadow-[0_30px_60px_-15px_rgba(0,0,0,0.3)] border ${isSolidMode ? 'bg-white/95 border-neutral-200/60' : 'bg-[#050B14]/95 border-white/[0.08] shadow-[0_0_40px_rgba(0,0,0,0.4)]'} backdrop-blur-2xl flex flex-col gap-1 relative overflow-hidden`}>
|
||||
{/* Top glowing accent line */}
|
||||
<div className="absolute top-0 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-primary to-transparent opacity-70" />
|
||||
|
||||
{link.children.map((child) => {
|
||||
const childUrl = child.url.startsWith('/') && !child.url.match(/^\/(en|de)/)
|
||||
? `/${currentLocale}${child.url}`
|
||||
@@ -218,13 +227,21 @@ function NavItem({ link, currentLocale, pathname, isSolidMode }: { link: NavLink
|
||||
<TransitionLink
|
||||
key={child.url}
|
||||
href={childUrl}
|
||||
className={`block px-4 py-3 rounded-2xl text-sm font-medium transition-all ${
|
||||
className={`group/dropitem relative block px-4 py-3.5 rounded-2xl text-sm font-medium transition-all duration-300 overflow-hidden ${
|
||||
isSolidMode
|
||||
? isChildActive ? 'bg-primary/10 text-primary' : 'text-neutral-600 hover:bg-neutral-100 hover:text-primary'
|
||||
: isChildActive ? 'bg-white/10 text-white' : 'text-neutral-300 hover:bg-white/5 hover:text-white'
|
||||
? isChildActive ? 'bg-primary/10 text-primary' : 'text-neutral-600 hover:text-primary hover:bg-neutral-50/80'
|
||||
: isChildActive ? 'bg-white/10 text-white' : 'text-neutral-300 hover:text-white hover:bg-white/[0.06]'
|
||||
}`}
|
||||
>
|
||||
{child.label}
|
||||
<div className="relative z-10 flex items-center justify-between">
|
||||
<span className="relative">
|
||||
{child.label}
|
||||
<span className={`absolute -bottom-0.5 left-0 w-0 h-0.5 rounded-full transition-all duration-300 ease-[cubic-bezier(0.16,1,0.3,1)] group-hover/dropitem:w-full ${isSolidMode ? 'bg-primary/40' : 'bg-primary/60'} ${isChildActive ? 'opacity-0' : 'opacity-100'}`} />
|
||||
</span>
|
||||
<svg className={`w-4 h-4 transition-all duration-300 ease-[cubic-bezier(0.16,1,0.3,1)] -translate-x-3 opacity-0 group-hover/dropitem:translate-x-0 group-hover/dropitem:opacity-100 ${isSolidMode ? 'text-primary' : 'text-white'}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</div>
|
||||
</TransitionLink>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user