251 lines
10 KiB
TypeScript
251 lines
10 KiB
TypeScript
import { useNavigate } from "react-router-dom";
|
||
import { ArrowRight } from "lucide-react";
|
||
import React, { useState, useEffect, useRef } from "react";
|
||
import { useTheme } from "next-themes";
|
||
import Silk from "@/components/Silk";
|
||
import CountUp from "@/components/CountUp";
|
||
|
||
const SPARKLE_SVG = (
|
||
<svg className="btn-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden>
|
||
<path
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
d="M9.813 15.904 9 18.75l-.813-2.846a4.5 4.5 0 0 0-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 0 0 3.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 0 0 3.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 0 0-3.09 3.09ZM18.259 8.715 18 9.75l-.259-1.035a3.375 3.375 0 0 0-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 0 0 2.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 0 0 2.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 0 0-2.456 2.456ZM16.894 20.567 16.5 21.75l-.394-1.183a2.25 2.25 0 0 0-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 0 0 1.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 0 0 1.423 1.423l1.183.394-1.183.394a2.25 2.25 0 0 0-1.423 1.423Z"
|
||
/>
|
||
</svg>
|
||
);
|
||
|
||
function DemoButtonLetters({ text }: { text: string }) {
|
||
// #region agent log
|
||
const chars = text.split("");
|
||
const spaceIndex = chars.findIndex((c) => c === " ");
|
||
const lastIndex = chars.length - 1;
|
||
const lastChar = chars[lastIndex];
|
||
fetch("http://127.0.0.1:7244/ingest/72f53105-0a54-4d4c-a295-fb93aa72afcc", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({
|
||
location: "Hero.tsx:DemoButtonLetters",
|
||
message: "Letter split for button text",
|
||
data: { text, len: chars.length, spaceIndex, spaceChar: spaceIndex >= 0 ? chars[spaceIndex] : null, lastIndex, lastChar },
|
||
timestamp: Date.now(),
|
||
sessionId: "debug-session",
|
||
hypothesisId: "A,C",
|
||
}),
|
||
}).catch(() => {});
|
||
// #endregion
|
||
return (
|
||
<>
|
||
{chars.map((char, i) => (
|
||
<span key={i} className={char === " " ? "btn-letter btn-letter-space" : "btn-letter"}>
|
||
{char}
|
||
</span>
|
||
))}
|
||
</>
|
||
);
|
||
}
|
||
|
||
const FOUNDING_DATE = new Date("2026-01-25"); // Samstag, 25. Januar 2026
|
||
|
||
const Hero = () => {
|
||
const navigate = useNavigate();
|
||
const { resolvedTheme } = useTheme();
|
||
const [companyAge, setCompanyAge] = useState("");
|
||
const secondBtnRef = useRef<HTMLButtonElement>(null);
|
||
|
||
useEffect(() => {
|
||
const el = secondBtnRef.current;
|
||
if (!el) return;
|
||
const firstTxtWrapper = el.querySelector(".txt-wrapper");
|
||
const letters = firstTxtWrapper ? firstTxtWrapper.querySelectorAll(".btn-letter") : [];
|
||
const spaceIdx = 8;
|
||
const lastIdx = 16;
|
||
const wSpace = letters[spaceIdx]?.getBoundingClientRect?.()?.width ?? -1;
|
||
const wLast = letters[lastIdx]?.getBoundingClientRect?.()?.width ?? -1;
|
||
fetch("http://127.0.0.1:7244/ingest/72f53105-0a54-4d4c-a295-fb93aa72afcc", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({
|
||
location: "Hero.tsx:useEffect:measure",
|
||
message: "Measured btn-letter widths (space + last)",
|
||
data: { letterCount: letters.length, wSpace, wLast, spaceIdx, lastIdx },
|
||
timestamp: Date.now(),
|
||
sessionId: "debug-session",
|
||
runId: "post-fix",
|
||
hypothesisId: "B,D,E",
|
||
}),
|
||
}).catch(() => {});
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
const calculateAge = () => {
|
||
const now = new Date();
|
||
const diff = now.getTime() - FOUNDING_DATE.getTime();
|
||
|
||
const totalSeconds = Math.floor(diff / 1000);
|
||
const days = Math.floor(totalSeconds / (60 * 60 * 24));
|
||
const hours = Math.floor((totalSeconds % (60 * 60 * 24)) / (60 * 60));
|
||
|
||
const years = Math.floor(days / 365);
|
||
const remainingDays = days % 365;
|
||
|
||
if (years > 0) {
|
||
setCompanyAge(`${years}J ${remainingDays}T ${hours}h`);
|
||
} else {
|
||
setCompanyAge(`${days}T ${hours}h`);
|
||
}
|
||
};
|
||
|
||
calculateAge();
|
||
const interval = setInterval(calculateAge, 60 * 60 * 1000); // Update every hour (only days/hours shown)
|
||
|
||
return () => clearInterval(interval);
|
||
}, []);
|
||
|
||
// ── Silk-Hintergrund Farben ──
|
||
const isDark = resolvedTheme === "dark";
|
||
// Silk: Hauptfarbe (Wellenspitzen)
|
||
const silkColor = isDark ? "#6a6a6a" : "#ffffff";
|
||
// Silk: Zweite Farbe (Wellentäler)
|
||
const silkColor2 = isDark ? "#000000" : "#c0c0c0";
|
||
// Silk: Rausch-Intensität
|
||
const silkNoise = isDark ? 4 : 1.5;
|
||
|
||
return (
|
||
<section className="relative min-h-screen flex flex-col justify-center overflow-hidden pt-20">
|
||
{/* Silk animated background */}
|
||
<div className="absolute inset-0 z-0 w-full h-full">
|
||
<Silk
|
||
speed={3}
|
||
scale={0.5}
|
||
color={silkColor}
|
||
color2={silkColor2}
|
||
noiseIntensity={silkNoise}
|
||
rotation={0}
|
||
/>
|
||
</div>
|
||
|
||
<div className="container mx-auto px-6 relative z-10">
|
||
<div className="max-w-6xl">
|
||
{/* Label */}
|
||
<div className="label-tag mb-8 animate-fade-in" style={{ animationDelay: '0.1s' }}>
|
||
Webentwicklung & Design
|
||
</div>
|
||
|
||
{/* Main Heading - Large Uppercase */}
|
||
<h1 className="text-5xl sm:text-6xl md:text-7xl lg:text-8xl xl:text-9xl font-display font-medium text-foreground mb-6 leading-[0.95] tracking-tighter uppercase animate-slide-up" style={{ animationDelay: '0.2s' }}>
|
||
Wir digitalisieren<br />
|
||
<span className="text-muted-foreground">Ihr Unternehmen</span>
|
||
</h1>
|
||
|
||
{/* Subheadline */}
|
||
<p className="text-xl md:text-2xl text-foreground/90 max-w-3xl mb-6 font-medium animate-fade-in" style={{ animationDelay: '0.3s' }}>
|
||
Wir digitalisieren, automatisieren und vernetzen Ihre gesamte Firma in einem einzigen System – damit Ihr Unternehmen wachsen kann, ohne dass Sie mehr arbeiten müssen.
|
||
</p>
|
||
|
||
{/* CTA Buttons */}
|
||
<div className="flex flex-col sm:flex-row flex-nowrap items-stretch sm:items-center gap-3 sm:gap-4 mb-6 animate-fade-in" style={{ animationDelay: '0.5s' }}>
|
||
<div className="btn-wrapper shrink-0 w-full sm:w-auto">
|
||
<button
|
||
type="button"
|
||
className="btn btn-primary w-full sm:w-auto justify-center"
|
||
onClick={() => navigate("/kontakt")}
|
||
aria-label="Kostenlose Potenzialanalyse sichern"
|
||
>
|
||
<ArrowRight className="btn-icon" size={24} strokeWidth={2} aria-hidden />
|
||
<div className="txt-wrapper">
|
||
<span className="txt-width-helper" aria-hidden="true">
|
||
<DemoButtonLetters text="Kostenlose Potenzialanalyse sichern" />
|
||
</span>
|
||
<div className="txt-1">
|
||
<DemoButtonLetters text="Kostenlose Potenzialanalyse sichern" />
|
||
</div>
|
||
<div className="txt-2">
|
||
<DemoButtonLetters text="Wird weitergeleitet..." />
|
||
</div>
|
||
</div>
|
||
</button>
|
||
</div>
|
||
<div className="btn-wrapper w-full sm:w-auto">
|
||
<button
|
||
ref={secondBtnRef}
|
||
type="button"
|
||
className="btn w-full sm:w-auto justify-center"
|
||
onClick={() => {
|
||
const el = document.getElementById("projects");
|
||
if (el) el.scrollIntoView({ behavior: "smooth" });
|
||
else navigate("/#projects");
|
||
}}
|
||
aria-label="Projekte ansehen"
|
||
>
|
||
{SPARKLE_SVG}
|
||
<div className="txt-wrapper">
|
||
<span className="txt-width-helper" aria-hidden="true">
|
||
<DemoButtonLetters text="Projekte ansehen" />
|
||
</span>
|
||
<div className="txt-1">
|
||
<DemoButtonLetters text="Projekte ansehen" />
|
||
</div>
|
||
<div className="txt-2">
|
||
<DemoButtonLetters text="Wird geladen..." />
|
||
</div>
|
||
</div>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Trust Line */}
|
||
<p className="text-sm text-muted-foreground/70 uppercase tracking-wider animate-fade-in" style={{ animationDelay: '0.55s' }}>
|
||
Für Unternehmen, die nicht stehen bleiben wollen.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Bottom Stats Row */}
|
||
<div className="container mx-auto px-6 mt-auto pb-12 pt-20 relative z-10">
|
||
<div className="divider mb-12" />
|
||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 md:gap-12">
|
||
<div className="animate-fade-in" style={{ animationDelay: '0.6s' }}>
|
||
<div className="stat-number text-4xl md:text-5xl text-foreground mb-2">
|
||
<CountUp
|
||
from={0}
|
||
to={10}
|
||
direction="up"
|
||
duration={1.2}
|
||
className="count-up-text"
|
||
startWhen={true}
|
||
suffix="+"
|
||
/>
|
||
</div>
|
||
<div className="label-tag">Projekte</div>
|
||
</div>
|
||
<div className="animate-fade-in" style={{ animationDelay: '0.7s' }}>
|
||
<div className="stat-number text-4xl md:text-5xl text-foreground mb-2">{companyAge}</div>
|
||
<div className="label-tag">Am Markt</div>
|
||
</div>
|
||
<div className="animate-fade-in" style={{ animationDelay: '0.8s' }}>
|
||
<div className="stat-number text-4xl md:text-5xl text-foreground mb-2">
|
||
<CountUp
|
||
from={0}
|
||
to={99.9}
|
||
direction="up"
|
||
duration={1.5}
|
||
className="count-up-text"
|
||
startWhen={true}
|
||
suffix="%"
|
||
/>
|
||
</div>
|
||
<div className="label-tag">Systemverfügbarkeit</div>
|
||
</div>
|
||
<div className="animate-fade-in" style={{ animationDelay: '0.9s' }}>
|
||
<div className="stat-number text-4xl md:text-5xl text-foreground mb-2">🇩🇪</div>
|
||
<div className="label-tag">Serverstandort in Deutschland</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
};
|
||
|
||
export default Hero;
|