/* Shared primitives for Scality Solutions */ const { useState, useEffect, useRef, useMemo, useCallback, useLayoutEffect } = React; // ---------- Lucide icon helper ---------- // Inline SVG icon paths (lucide-style, MIT). Only the ones we use. const ICON_PATHS = { TrendingUp: '', UserPlus: '', Calendar: '', Zap: '', ShieldCheck: '', Star: '', Code2: '', Layers: '', Workflow: '', ArrowRight: '', ArrowLeft: '', ChevronDown: '', ChevronLeft: '', ChevronRight: '', Check: '', CheckCircle2: '', Menu: '', X: '', Mail: '', Phone: '', FileText: '', Linkedin: '', Instagram: '', Facebook: '', User: '', LayoutDashboard: '', Users: '', Settings: '', Bell: '', Search: '', Home: '', ClipboardList: '', CalendarDays: '', Smartphone: '', WifiOff: '', Lock: '', Wifi: '', Battery: '', Signal: '', MapPin: '', Clock: '', Plus: '', }; function makeIcon(name) { return function Icon({ size = 18, className = "", strokeWidth = 1.8, style = {}, fill }) { const path = ICON_PATHS[name]; if (!path) return ; return ( ); }; } const TrendingUp = makeIcon("TrendingUp"); const UserPlus = makeIcon("UserPlus"); const Calendar = makeIcon("Calendar"); const Zap = makeIcon("Zap"); const ShieldCheck = makeIcon("ShieldCheck"); const Star = makeIcon("Star"); const Code2 = makeIcon("Code2"); const Layers = makeIcon("Layers"); const Workflow = makeIcon("Workflow"); const ArrowRight = makeIcon("ArrowRight"); const ArrowLeft = makeIcon("ArrowLeft"); const ChevronDown = makeIcon("ChevronDown"); const ChevronLeft = makeIcon("ChevronLeft"); const ChevronRight = makeIcon("ChevronRight"); const Check = makeIcon("Check"); const CheckCircle2 = makeIcon("CheckCircle2"); const Menu = makeIcon("Menu"); const X = makeIcon("X"); const Mail = makeIcon("Mail"); const Phone = makeIcon("Phone"); const FileText = makeIcon("FileText"); const Linkedin = makeIcon("Linkedin"); const Instagram = makeIcon("Instagram"); const Facebook = makeIcon("Facebook"); const User = makeIcon("User"); const LayoutDashboard = makeIcon("LayoutDashboard"); const Briefcase = makeIcon("Briefcase"); const Users = makeIcon("Users"); const Settings = makeIcon("Settings"); const Bell = makeIcon("Bell"); const Search = makeIcon("Search"); const Home = makeIcon("Home"); const ClipboardList = makeIcon("ClipboardList"); const CalendarDays = makeIcon("CalendarDays"); const Smartphone = makeIcon("Smartphone"); const WifiOff = makeIcon("WifiOff"); const Lock = makeIcon("Lock"); const Wifi = makeIcon("Wifi"); const Battery = makeIcon("Battery"); const Signal = makeIcon("Signal"); const MapPin = makeIcon("MapPin"); const Clock = makeIcon("Clock"); const Globe = makeIcon("Globe"); const Cog = makeIcon("Cog"); const Target = makeIcon("Target"); const Plus = makeIcon("Plus"); const Minus = makeIcon("Minus"); const Award = makeIcon("Award"); // ---------- Reveal hook ---------- function useReveal(threshold = 0.1) { const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver( (entries) => { entries.forEach((e) => { if (e.isIntersecting) { el.classList.add("in"); io.unobserve(el); } }); }, { threshold, rootMargin: "0px 0px -10% 0px" } ); io.observe(el); return () => io.disconnect(); }, [threshold]); return ref; } // ---------- Buttons ---------- function PrimaryButton({ children, href = "#kontakt", onClick, className = "", magnet = true, size = "md" }) { const ref = useRef(null); useEffect(() => { if (!magnet) return; const el = ref.current; if (!el) return; const onMove = (e) => { const r = el.getBoundingClientRect(); const cx = r.left + r.width / 2; const cy = r.top + r.height / 2; const dx = (e.clientX - cx) / (r.width / 2); const dy = (e.clientY - cy) / (r.height / 2); const max = 8; el.style.transform = `translate(${Math.max(-1,Math.min(1,dx))*max}px, ${Math.max(-1,Math.min(1,dy))*max}px) scale(1.02)`; }; const onLeave = () => { el.style.transform = ""; }; const region = el; region.addEventListener("mousemove", onMove); region.addEventListener("mouseleave", onLeave); return () => { region.removeEventListener("mousemove", onMove); region.removeEventListener("mouseleave", onLeave); }; }, [magnet]); const sizes = { sm: "px-4 py-2 text-sm", md: "px-6 py-3 text-[15px]", lg: "px-7 py-4 text-base", }; return ( {children} ); } function GhostButton({ children, href = "#projekte", onClick, className = "", size = "md" }) { const sizes = { sm: "px-4 py-2 text-sm", md: "px-6 py-3 text-[15px]", lg: "px-7 py-4 text-base", }; return ( {children} ); } // ---------- Stats count-up ---------- function CountUp({ to, suffix = "", prefix = "", duration = 1600, decimals = 0 }) { const [val, setVal] = useState(0); const ref = useRef(null); const startedRef = useRef(false); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting && !startedRef.current) { startedRef.current = true; const start = performance.now(); const tick = (t) => { const p = Math.min(1, (t - start) / duration); // easeOutCubic const e2 = 1 - Math.pow(1 - p, 3); setVal(to * e2); if (p < 1) requestAnimationFrame(tick); else setVal(to); }; requestAnimationFrame(tick); } }); }, { threshold: 0.4 }); io.observe(el); return () => io.disconnect(); }, [to, duration]); const formatted = decimals ? val.toFixed(decimals) : Math.round(val).toString(); return {prefix}{formatted}{suffix}; } // expose Object.assign(window, { TrendingUp, UserPlus, Calendar, Zap, ShieldCheck, Star, Code2, Layers, Workflow, ArrowRight, ArrowLeft, ChevronDown, ChevronLeft, ChevronRight, Check, CheckCircle2, Menu, X, Mail, Phone, FileText, Linkedin, Instagram, Facebook, User, LayoutDashboard, Briefcase, Users, Settings, Bell, Search, Home, ClipboardList, CalendarDays, Smartphone, WifiOff, Lock, Wifi, Battery, Signal, MapPin, Clock, Globe, Cog, Target, Plus, Minus, Award, useReveal, PrimaryButton, GhostButton, CountUp, makeIcon, });