fix(ui): resolve annotator overlap, slider images, subcompany rounding and copy updates
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 1m16s
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🧪 QA (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
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 1m16s
Build & Deploy / 🏗️ Build (push) Has been cancelled
Build & Deploy / 🧪 QA (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
This commit is contained in:
@@ -239,7 +239,7 @@ export function Annotator({ assets = [], onSubmit }: AnnotatorProps) {
|
||||
return (
|
||||
<div className="annotator-ui-ignore">
|
||||
{/* 1. Global Toolbar */}
|
||||
<div className="fixed bottom-24 right-4 z-[9999] flex flex-col items-end gap-2">
|
||||
<div className="fixed bottom-36 md:bottom-24 right-4 z-[10000] flex flex-col items-end gap-2">
|
||||
<div className="bg-black/80 backdrop-blur-xl border border-white/10 p-1.5 rounded-2xl shadow-2xl flex flex-col items-center gap-1">
|
||||
<button
|
||||
onClick={() => setIsActive(!isActive)}
|
||||
|
||||
@@ -118,8 +118,8 @@ export function ReferencesSlider(props: ReferencesSliderProps) {
|
||||
className={`select-none flex gap-6 overflow-x-auto pb-8 pt-4 px-6 md:px-[max(2rem,calc((100vw-1280px)/2))] lg:px-[max(2.5rem,calc((100vw-1280px)/2))] [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden ${isDragging ? 'cursor-grabbing snap-none' : 'cursor-grab snap-x snap-mandatory'}`}
|
||||
>
|
||||
{references.map((ref, i) => {
|
||||
const imgSrc = (ref.image && typeof ref.image === 'object' && ref.image.url)
|
||||
? ref.image.url
|
||||
const imgSrc = ref.image
|
||||
? (typeof ref.image === 'string' ? ref.image : ref.image.url)
|
||||
: fallbacks[i % fallbacks.length];
|
||||
|
||||
return (
|
||||
@@ -129,11 +129,17 @@ export function ReferencesSlider(props: ReferencesSliderProps) {
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: i * 0.1, duration: 0.6, ease: "easeOut" }}
|
||||
className="flex-shrink-0 w-[320px] md:w-[480px] snap-center group pointer-events-auto"
|
||||
className="flex-shrink-0 w-[320px] md:w-[480px] snap-start group pointer-events-auto"
|
||||
>
|
||||
<div
|
||||
<Link
|
||||
href={`/${locale}/referenzen/${ref.slug}`}
|
||||
onClick={(e) => {
|
||||
if (dragDistanceRef.current > 5) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}}
|
||||
data-testid="reference-tile"
|
||||
className="block relative select-none aspect-[16/10] bg-neutral-800 rounded-2xl overflow-hidden mb-5 border border-white/5 shadow-2xl"
|
||||
className="block relative select-none aspect-[16/10] bg-neutral-800 rounded-2xl overflow-hidden mb-5 border border-white/5 shadow-2xl cursor-pointer"
|
||||
>
|
||||
<Image
|
||||
draggable={false}
|
||||
|
||||
@@ -228,7 +228,7 @@ export function SubCompanyTiles(props: SubCompanyTilesProps) {
|
||||
</>
|
||||
);
|
||||
|
||||
const sharedClass = `group relative overflow-hidden aspect-[4/3] flex flex-col justify-center items-center shadow-xl transition-all duration-500 bg-neutral-dark block w-full h-full sm:rounded-xl select-none ${
|
||||
const sharedClass = `group relative overflow-hidden aspect-[4/3] flex flex-col justify-center items-center shadow-xl transition-all duration-500 bg-neutral-dark block w-full h-full rounded-2xl select-none ${
|
||||
isCurrent
|
||||
? 'border border-white/5 hover:shadow-2xl hover:-translate-y-1 hover:border-white/10 cursor-pointer'
|
||||
: 'border border-white/5 hover:shadow-2xl hover:-translate-y-1 hover:border-white/10 cursor-pointer'
|
||||
|
||||
@@ -157,81 +157,10 @@ export function Header({ navLinks }: HeaderProps) {
|
||||
<span className="absolute top-0 -left-[150%] h-[200%] w-[150%] bg-gradient-to-r from-transparent via-white/30 to-transparent skew-x-[-20deg] group-hover/cta:left-[100%] transition-all duration-1000 ease-[cubic-bezier(0.16,1,0.3,1)] z-30 pointer-events-none" />
|
||||
</TransitionLink>
|
||||
|
||||
{/* Hamburger Menu (Mobile Only) */}
|
||||
<button
|
||||
className={`md:hidden ml-2 p-2 transition-colors focus:outline-none ${isSolidMode && !isMobileMenuOpen ? 'text-text-primary hover:text-primary' : 'text-white hover:text-primary z-[60] relative'}`}
|
||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
||||
aria-label="Toggle Menu"
|
||||
>
|
||||
<div className="w-6 flex flex-col gap-[6px] relative items-center justify-center">
|
||||
<span className={`w-full h-[2px] rounded-full bg-current transform transition-all duration-300 ease-in-out origin-center ${isMobileMenuOpen ? 'rotate-45 translate-y-[8px]' : ''}`} />
|
||||
<span className={`w-full h-[2px] rounded-full bg-current transition-all duration-300 ease-in-out ${isMobileMenuOpen ? 'opacity-0 translate-x-3' : 'opacity-100'}`} />
|
||||
<span className={`w-full h-[2px] rounded-full bg-current transform transition-all duration-300 ease-in-out origin-center ${isMobileMenuOpen ? '-rotate-45 -translate-y-[8px]' : ''}`} />
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Mobile Menu Overlay */}
|
||||
<AnimatePresence>
|
||||
{isMobileMenuOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -20 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="fixed inset-0 z-[40] bg-[#050B14]/95 backdrop-blur-xl md:hidden pt-24 pb-24 px-6 overflow-y-auto"
|
||||
>
|
||||
<nav className="flex flex-col gap-6 mt-8">
|
||||
{navLinks && navLinks.length > 0 && navLinks.map((link) => {
|
||||
const mappedUrl = link.url.startsWith('/') && !link.url.match(/^\/(en|de)/)
|
||||
? `/${currentLocale}${link.url}`
|
||||
: link.url;
|
||||
const isActive = pathname === mappedUrl || (mappedUrl !== `/${currentLocale}` && pathname?.startsWith(`${mappedUrl}/`));
|
||||
|
||||
return (
|
||||
<div key={link.url} className="flex flex-col">
|
||||
<TransitionLink
|
||||
href={mappedUrl}
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
className={`text-2xl font-bold uppercase tracking-widest transition-colors ${isActive ? 'text-primary' : 'text-white'}`}
|
||||
>
|
||||
{link.label}
|
||||
</TransitionLink>
|
||||
|
||||
{link.children && (
|
||||
<div className="flex flex-col gap-3 mt-3 pl-4 border-l-2 border-white/10">
|
||||
{link.children.map((child, idx) => {
|
||||
if (child.isGroupLabel) {
|
||||
return <div key={idx} className="text-sm font-bold text-neutral-500 uppercase tracking-widest mt-2">{child.label}</div>;
|
||||
}
|
||||
const childUrl = child.url.startsWith('/') && !child.url.match(/^\/(en|de)/)
|
||||
? `/${currentLocale}${child.url}`
|
||||
: child.url;
|
||||
const isChildActive = pathname === childUrl;
|
||||
|
||||
return (
|
||||
<TransitionLink
|
||||
key={child.url}
|
||||
href={childUrl}
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
className={`text-lg font-medium transition-colors ${isChildActive ? 'text-primary' : 'text-white/70'}`}
|
||||
>
|
||||
{child.label}
|
||||
</TransitionLink>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,34 +18,48 @@ interface MobileBottomNavProps {
|
||||
|
||||
export function MobileBottomNav({ navLinks, currentLocale }: MobileBottomNavProps) {
|
||||
const pathname = usePathname() || '/';
|
||||
const [isFlyoutOpen, setIsFlyoutOpen] = React.useState(false);
|
||||
|
||||
// Construct our bottom nav items based on the passed links
|
||||
// We need to gather all links that should go into the flyout.
|
||||
// The user wants 'alle navigationspunkte daraus' (from the old burger menu).
|
||||
// Old menu had: Kompetenzen, Über uns (Firma, Team, Zertifikate, Standorte), Referenzen, Karriere, Messen.
|
||||
|
||||
// Construct our bottom nav items
|
||||
const items = React.useMemo(() => {
|
||||
const homeUrl = `/${currentLocale}`;
|
||||
|
||||
// Find the specific links from the provided navLinks (which might be translated)
|
||||
const competenciesUrl = navLinks[0]?.url || `/${currentLocale}/kompetenzen`;
|
||||
const aboutUrl = navLinks[1]?.url || `/${currentLocale}/ueber-uns`;
|
||||
const referencesUrl = navLinks[2]?.url || `/${currentLocale}/referenzen`;
|
||||
const contactUrl = `/${currentLocale}/${currentLocale === 'de' ? 'kontakt' : 'contact'}`;
|
||||
|
||||
return [
|
||||
{
|
||||
id: 'home',
|
||||
label: currentLocale === 'de' ? 'Start' : 'Home',
|
||||
url: homeUrl,
|
||||
icon: <Home className="w-5 h-5 md:w-6 md:h-6" strokeWidth={2.5} />,
|
||||
exactMatch: true
|
||||
},
|
||||
{
|
||||
id: 'kompetenzen',
|
||||
label: navLinks[0]?.label || (currentLocale === 'de' ? 'Kompetenzen' : 'Competencies'),
|
||||
url: competenciesUrl,
|
||||
icon: <Briefcase className="w-5 h-5 md:w-6 md:h-6" strokeWidth={2.5} />
|
||||
},
|
||||
{
|
||||
label: navLinks[1]?.label || (currentLocale === 'de' ? 'Über uns' : 'About Us'),
|
||||
url: aboutUrl,
|
||||
icon: <Users className="w-5 h-5 md:w-6 md:h-6" strokeWidth={2.5} />
|
||||
id: 'referenzen',
|
||||
label: navLinks[2]?.label || (currentLocale === 'de' ? 'Referenzen' : 'References'),
|
||||
url: referencesUrl,
|
||||
icon: <Users className="w-5 h-5 md:w-6 md:h-6" strokeWidth={2.5} /> // Using Users for now, wait we need an image or star icon?
|
||||
},
|
||||
{
|
||||
id: 'menu',
|
||||
label: navLinks[1]?.label || (currentLocale === 'de' ? 'Über uns' : 'About Us'),
|
||||
url: '#', // Triggers flyout
|
||||
icon: <svg className="w-5 h-5 md:w-6 md:h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}><path strokeLinecap="round" strokeLinejoin="round" d="M4 6h16M4 12h16m-7 6h7" /></svg>,
|
||||
isFlyoutTrigger: true
|
||||
},
|
||||
{
|
||||
id: 'kontakt',
|
||||
label: currentLocale === 'de' ? 'Kontakt' : 'Contact',
|
||||
url: contactUrl,
|
||||
icon: <Mail className="w-5 h-5 md:w-6 md:h-6" strokeWidth={2.5} />
|
||||
@@ -53,55 +67,143 @@ export function MobileBottomNav({ navLinks, currentLocale }: MobileBottomNavProp
|
||||
];
|
||||
}, [currentLocale, navLinks]);
|
||||
|
||||
return (
|
||||
<div className="md:hidden fixed bottom-0 left-0 right-0 z-[9999] px-4 pb-[calc(env(safe-area-inset-bottom)+1rem)] pt-4 pointer-events-none flex justify-center">
|
||||
<nav className="flex justify-between items-center bg-white/70 backdrop-blur-3xl border border-white/60 shadow-[0_8px_32px_rgba(0,0,0,0.12)] rounded-3xl p-1.5 pointer-events-auto w-full max-w-[400px]">
|
||||
{items.map((item) => {
|
||||
// Determine if active
|
||||
const mappedUrl = item.url.startsWith('/') && !item.url.match(/^\/(en|de)/)
|
||||
? `/${currentLocale}${item.url}`
|
||||
: item.url;
|
||||
|
||||
const isActive = item.exactMatch
|
||||
? pathname === mappedUrl
|
||||
: (pathname === mappedUrl || (mappedUrl !== `/${currentLocale}` && pathname?.startsWith(`${mappedUrl}/`)));
|
||||
// Use a star or folder for references? Let's use standard Briefcase for Kompetenzen, and maybe a Layers or Image icon for Referenzen.
|
||||
// We'll replace the Users icon with something better if possible, but Users is fine for now, wait, Users is Über uns!
|
||||
// I will fix the icon in the render.
|
||||
|
||||
return (
|
||||
<TransitionLink
|
||||
key={item.label}
|
||||
href={mappedUrl}
|
||||
className={`relative flex flex-col items-center justify-center flex-1 h-[60px] transition-colors duration-300 select-none ${
|
||||
isActive ? 'text-white' : 'text-neutral-500 hover:text-neutral-900'
|
||||
}`}
|
||||
style={{ WebkitTapHighlightColor: 'transparent', touchAction: 'manipulation' }}
|
||||
>
|
||||
{isActive && (
|
||||
<motion.div
|
||||
layoutId="mobile-nav-active-bg"
|
||||
className="absolute inset-0 bg-primary rounded-2xl shadow-md"
|
||||
transition={{ type: 'spring', stiffness: 400, damping: 25 }}
|
||||
/>
|
||||
)}
|
||||
// Gather flyout links:
|
||||
const flyoutLinks = React.useMemo(() => {
|
||||
// We want to put Über uns children, Karriere, and Messen in here.
|
||||
const aboutNode = navLinks[1];
|
||||
const karriereNode = navLinks[3];
|
||||
const messenNode = navLinks[4];
|
||||
|
||||
let links: {label: string, url: string}[] = [];
|
||||
if (aboutNode?.children) {
|
||||
links.push(...aboutNode.children.map(c => ({ label: c.label, url: c.url })));
|
||||
}
|
||||
if (karriereNode) links.push({ label: karriereNode.label, url: karriereNode.url });
|
||||
if (messenNode) links.push({ label: messenNode.label, url: messenNode.url });
|
||||
return links;
|
||||
}, [navLinks]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="md:hidden fixed bottom-0 left-0 right-0 z-[9999] px-4 pb-[calc(env(safe-area-inset-bottom)+1rem)] pt-4 pointer-events-none flex justify-center">
|
||||
<nav className="flex justify-between items-center bg-white/80 backdrop-blur-3xl border border-white/60 shadow-[0_8px_32px_rgba(0,0,0,0.12)] rounded-3xl p-1.5 pointer-events-auto w-full max-w-[400px]">
|
||||
{items.map((item) => {
|
||||
// Determine if active
|
||||
const mappedUrl = item.url.startsWith('/') && !item.url.match(/^\/(en|de)/)
|
||||
? `/${currentLocale}${item.url}`
|
||||
: item.url;
|
||||
|
||||
<div className="relative flex flex-col items-center justify-center z-10 w-full h-full">
|
||||
<motion.div
|
||||
whileTap={{ scale: 0.8 }}
|
||||
animate={{ y: isActive ? -2 : 0 }}
|
||||
transition={{ type: 'spring', stiffness: 400, damping: 17 }}
|
||||
>
|
||||
{item.icon}
|
||||
</motion.div>
|
||||
<motion.span
|
||||
animate={{ opacity: isActive ? 1 : 0.7, y: isActive ? 0 : 2 }}
|
||||
className="text-[10px] font-bold mt-1 tracking-wide"
|
||||
>
|
||||
{item.label}
|
||||
</motion.span>
|
||||
let isActive = false;
|
||||
if (item.isFlyoutTrigger) {
|
||||
isActive = isFlyoutOpen;
|
||||
} else {
|
||||
isActive = item.exactMatch
|
||||
? pathname === mappedUrl
|
||||
: (pathname === mappedUrl || (mappedUrl !== `/${currentLocale}` && pathname?.startsWith(`${mappedUrl}/`)));
|
||||
}
|
||||
|
||||
// Fix icon for referenzen
|
||||
let ItemIcon = item.icon;
|
||||
if (item.id === 'referenzen') {
|
||||
ItemIcon = <svg className="w-5 h-5 md:w-6 md:h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}><path strokeLinecap="round" strokeLinejoin="round" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /></svg>;
|
||||
}
|
||||
|
||||
const LinkWrapper = item.isFlyoutTrigger ? 'button' : TransitionLink;
|
||||
const linkProps = item.isFlyoutTrigger
|
||||
? { onClick: () => setIsFlyoutOpen(!isFlyoutOpen), className: "w-full focus:outline-none" }
|
||||
: { href: mappedUrl };
|
||||
|
||||
return (
|
||||
<LinkWrapper
|
||||
key={item.label}
|
||||
{...linkProps}
|
||||
className={`relative flex flex-col items-center justify-center flex-1 h-[60px] transition-colors duration-300 select-none ${
|
||||
isActive ? 'text-white' : 'text-neutral-500 hover:text-neutral-900'
|
||||
} ${item.isFlyoutTrigger ? 'focus:outline-none' : ''}`}
|
||||
style={{ WebkitTapHighlightColor: 'transparent', touchAction: 'manipulation' }}
|
||||
>
|
||||
{isActive && (
|
||||
<motion.div
|
||||
layoutId="mobile-nav-active-bg"
|
||||
className="absolute inset-0 bg-primary rounded-2xl shadow-md"
|
||||
transition={{ type: 'spring', stiffness: 400, damping: 25 }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="relative flex flex-col items-center justify-center z-10 w-full h-full">
|
||||
<motion.div
|
||||
whileTap={{ scale: 0.8 }}
|
||||
animate={{ y: isActive ? -2 : 0 }}
|
||||
transition={{ type: 'spring', stiffness: 400, damping: 17 }}
|
||||
>
|
||||
{ItemIcon}
|
||||
</motion.div>
|
||||
<motion.span
|
||||
animate={{ opacity: isActive ? 1 : 0.7, y: isActive ? 0 : 2 }}
|
||||
className="text-[10px] font-bold mt-1 tracking-wide"
|
||||
>
|
||||
{item.label}
|
||||
</motion.span>
|
||||
</div>
|
||||
</LinkWrapper>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Flyout Menu (Bottom Sheet) */}
|
||||
<AnimatePresence>
|
||||
{isFlyoutOpen && (
|
||||
<>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="fixed inset-0 bg-black/40 backdrop-blur-sm z-[9997] md:hidden"
|
||||
onClick={() => setIsFlyoutOpen(false)}
|
||||
/>
|
||||
<motion.div
|
||||
initial={{ y: '100%', opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
exit={{ y: '100%', opacity: 0 }}
|
||||
transition={{ type: 'spring', stiffness: 300, damping: 30 }}
|
||||
className="fixed bottom-0 left-0 right-0 z-[9998] bg-white rounded-t-3xl pt-6 pb-[calc(env(safe-area-inset-bottom)+6rem)] px-6 md:hidden shadow-[0_-20px_40px_rgba(0,0,0,0.1)] border-t border-neutral-100"
|
||||
>
|
||||
<div className="w-12 h-1.5 bg-neutral-200 rounded-full mx-auto mb-6" />
|
||||
<h3 className="text-sm font-bold uppercase tracking-widest text-neutral-400 mb-4 px-2">{currentLocale === 'de' ? 'Über uns & Mehr' : 'About us & More'}</h3>
|
||||
<div className="flex flex-col gap-1">
|
||||
{flyoutLinks.map((link, i) => {
|
||||
const childUrl = link.url.startsWith('/') && !link.url.match(/^\/(en|de)/)
|
||||
? `/${currentLocale}${link.url}`
|
||||
: link.url;
|
||||
const isChildActive = pathname === childUrl;
|
||||
|
||||
return (
|
||||
<TransitionLink
|
||||
key={i}
|
||||
href={childUrl}
|
||||
onClick={() => setIsFlyoutOpen(false)}
|
||||
className={`px-4 py-3.5 rounded-2xl text-base font-medium transition-all flex items-center justify-between ${
|
||||
isChildActive ? 'bg-primary/10 text-primary' : 'text-neutral-700 hover:bg-neutral-50'
|
||||
}`}
|
||||
>
|
||||
{link.label}
|
||||
<svg className={`w-4 h-4 ${isChildActive ? 'text-primary' : 'text-neutral-400'}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</TransitionLink>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</TransitionLink>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -79,8 +79,8 @@ description: "Die E-TIB GmbH ist Ihr zuverlässiger Partner für komplexe Kabelt
|
||||
<div className="text-sm font-bold text-text-secondary uppercase tracking-wider">Mitarbeitende</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-4xl font-black text-primary mb-1 tracking-tighter">100+</div>
|
||||
<div className="text-sm font-bold text-text-secondary uppercase tracking-wider">Kunden</div>
|
||||
<div className="text-4xl font-black text-primary mb-1 tracking-tighter">200+</div>
|
||||
<div className="text-sm font-bold text-text-secondary uppercase tracking-wider">Projekte</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -79,8 +79,8 @@ description: "E-TIB GmbH is your reliable partner for complex cable routes, hori
|
||||
<div className="text-sm font-bold text-text-secondary uppercase tracking-wider">Employees</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-4xl font-black text-primary mb-1 tracking-tighter">100+</div>
|
||||
<div className="text-sm font-bold text-text-secondary uppercase tracking-wider">Clients</div>
|
||||
<div className="text-4xl font-black text-primary mb-1 tracking-tighter">200+</div>
|
||||
<div className="text-sm font-bold text-text-secondary uppercase tracking-wider">Projects</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user