feat: implement industrial optimizations, hybrid dev workflow, and simplified reveal animations
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 11s
Build & Deploy / 🧪 QA (push) Successful in 4m5s
Build & Deploy / 🏗️ Build (push) Successful in 9m26s
Build & Deploy / 🚀 Deploy (push) Successful in 25s
Build & Deploy / 🩺 Health Check (push) Failing after 12s
Build & Deploy / 🔔 Notify (push) Successful in 2s

This commit is contained in:
2026-02-13 15:23:36 +01:00
parent 066ccb4f4d
commit cfda0daef9
12 changed files with 299 additions and 165 deletions

View File

@@ -48,14 +48,14 @@ export default function AboutPage() {
<div className="absolute inset-0 -m-8 border border-slate-100 rounded-full animate-[spin_30s_linear_infinite] opacity-50" /> <div className="absolute inset-0 -m-8 border border-slate-100 rounded-full animate-[spin_30s_linear_infinite] opacity-50" />
<div className="absolute inset-0 -m-4 border border-slate-200 rounded-full animate-[spin_20s_linear_infinite_reverse] opacity-30" /> <div className="absolute inset-0 -m-4 border border-slate-200 rounded-full animate-[spin_20s_linear_infinite_reverse] opacity-30" />
<div className="relative w-32 h-32 md:w-40 md:h-40 rounded-full overflow-hidden border border-slate-200 shadow-xl bg-white p-1 group"> <div className="relative w-40 h-40 rounded-full overflow-hidden border border-slate-200 shadow-xl bg-white p-1 group">
<div className="w-full h-full rounded-full overflow-hidden"> <div className="w-full h-full rounded-full overflow-hidden relative aspect-square">
<Image <Image
src="/header.webp" src="https://img.infra.mintel.me/unsafe/rs:fill:800:800/plain/https://picsum.photos/seed/marc/800/800"
alt="Marc Mintel" alt="Marc Mintel"
width={160} fill
height={160} className="object-cover grayscale transition-all duration-1000 ease-in-out scale-110 group-hover:scale-100 group-hover:grayscale-0"
className="w-full h-full object-cover grayscale transition-all duration-1000 ease-in-out scale-110 group-hover:scale-100 group-hover:grayscale-0" unoptimized
/> />
</div> </div>
</div> </div>

View File

@@ -25,33 +25,7 @@ import {
Layers, Layers,
} from "lucide-react"; } from "lucide-react";
/** import { Marker } from "../../../src/components/Marker";
* TECHNICAL MARKER COMPONENT
* Implements the "hand-drawn marker" effect from STYLEGUIDE.md
* Updated: Only yellow marker as requested.
*/
const Marker: React.FC<{ children: React.ReactNode; delay?: number }> = ({
children,
delay = 0,
}) => {
return (
<span className="relative inline-block px-1">
<motion.span
initial={{ scaleX: 0, opacity: 0 }}
whileInView={{ scaleX: 1, opacity: 1 }}
viewport={{ once: true }}
transition={{
duration: 1.2,
delay: delay + 0.1,
ease: [0.23, 1, 0.32, 1],
}}
className="absolute inset-0 z-[-1] -skew-x-6 rotate-[-1deg] translate-y-1 transform-gpu bg-[rgba(255,235,59,0.95)] origin-left"
aria-hidden="true"
/>
{children}
</span>
);
};
export default function KLZCablesCaseStudy() { export default function KLZCablesCaseStudy() {
const { scrollYProgress } = useScroll(); const { scrollYProgress } = useScroll();
@@ -170,9 +144,9 @@ export default function KLZCablesCaseStudy() {
<BodyText className="text-xl text-slate-600 leading-relaxed"> <BodyText className="text-xl text-slate-600 leading-relaxed">
Ich habe die KLZ-Architektur radikal auf einen entkoppelten Ich habe die KLZ-Architektur radikal auf einen entkoppelten
High-Performance-Stack umgestellt. WordPress fungiert hier High-Performance-Stack umgestellt. WordPress fungiert hier
nicht als CMS-Baukasten, sondern als{" "} nicht als CMS-Baukasten, sondern speichert alle technischen
<Marker delay={0.3}>Headless JSON-Provider</Marker>. Durch die Attribute in einer zentralen relationalen Instanz. Durch die
Implementierung nativer PHP-Microservices und den Verzicht auf Implementierung nativer PHP-Services und den Verzicht auf
volatile Drittanbieter-Plugins wurde ein System geschaffen, volatile Drittanbieter-Plugins wurde ein System geschaffen,
das keine technologischen Überraschungen zulässt.{" "} das keine technologischen Überraschungen zulässt.{" "}
<Marker delay={0.5}>Stability by Design.</Marker> <Marker delay={0.5}>Stability by Design.</Marker>
@@ -316,7 +290,6 @@ export default function KLZCablesCaseStudy() {
</div> </div>
</Section> </Section>
{/* --- SECTION 03: COMMERCE --- */}
<Section <Section
number="03" number="03"
title="Katalog-Architektur" title="Katalog-Architektur"
@@ -338,7 +311,7 @@ export default function KLZCablesCaseStudy() {
<motion.div <motion.div
whileHover={{ scale: 1.01 }} whileHover={{ scale: 1.01 }}
transition={{ type: "spring", stiffness: 200, damping: 20 }} transition={{ type: "spring", stiffness: 200, damping: 20 }}
className="relative h-[650px] w-full overflow-visible group" className="relative h-[650px] w-full overflow-hidden group"
> >
<IframeSection <IframeSection
src="/showcase/klz-cables.com/which-cables-for-wind-power-differences-from-low-to-extra-high-voltage-explained-2.html" src="/showcase/klz-cables.com/which-cables-for-wind-power-differences-from-low-to-extra-high-voltage-explained-2.html"
@@ -467,8 +440,9 @@ export default function KLZCablesCaseStudy() {
title="Lead Engineering" title="Lead Engineering"
variant="white" variant="white"
containerVariant="wide" containerVariant="wide"
className="!pb-32"
> >
<div className="grid grid-cols-1 lg:grid-cols-12 gap-16 items-center"> <div className="grid grid-cols-1 lg:grid-cols-12 gap-16 lg:gap-24 items-center">
<div className="lg:col-span-7"> <div className="lg:col-span-7">
<Reveal direction="left" scale={0.98} blur> <Reveal direction="left" scale={0.98} blur>
<motion.div <motion.div
@@ -569,9 +543,10 @@ export default function KLZCablesCaseStudy() {
<MotionButton <MotionButton
href="/contact" href="/contact"
variant="outline" variant="outline"
showArrow={false}
className="w-full py-8 text-lg group border-2 border-slate-900 rounded-full bg-white hover:bg-slate-900 hover:text-white transition-all duration-700" className="w-full py-8 text-lg group border-2 border-slate-900 rounded-full bg-white hover:bg-slate-900 hover:text-white transition-all duration-700"
> >
System-Analyse anfragen Architektur-Audit anfragen
<ArrowRight className="inline-block ml-4 w-6 h-6 group-hover:translate-x-4 transition-transform duration-700" /> <ArrowRight className="inline-block ml-4 w-6 h-6 group-hover:translate-x-4 transition-transform duration-700" />
</MotionButton> </MotionButton>
</Reveal> </Reveal>

View File

@@ -36,7 +36,7 @@ export default function CaseStudiesPage() {
but a static image or a styled div is more standard for a card. */} but a static image or a styled div is more standard for a card. */}
<div className="absolute inset-0 flex items-center justify-center bg-[#0117bf] transition-transform duration-700 group-hover:scale-105 p-12"> <div className="absolute inset-0 flex items-center justify-center bg-[#0117bf] transition-transform duration-700 group-hover:scale-105 p-12">
<Image <Image
src="/showcase/klz-cables/assets/img/white_logo_transparent_background.svg" src="/showcase/klz-cables.com/assets/klz-cables.com/wp-content/uploads/2024/11/white_logo_transparent_background.svg"
alt="KLZ Cables Logo" alt="KLZ Cables Logo"
width={200} width={200}
height={200} height={200}

View File

@@ -1,32 +1,36 @@
import * as React from 'react'; import * as React from "react";
import { Reveal } from '../../src/components/Reveal'; import { Reveal } from "../../src/components/Reveal";
import { PageHeader } from '../../src/components/PageHeader'; import { PageHeader } from "../../src/components/PageHeader";
import { Section } from '../../src/components/Section'; import { Section } from "../../src/components/Section";
import { ContactForm } from '../../src/components/ContactForm'; import { ContactForm } from "../../src/components/ContactForm";
export default function ContactPage() { export default function ContactPage() {
return ( return (
<div className="flex flex-col gap-12 py-12 md:py-24"> <div className="flex flex-col gap-12 py-12 md:py-24">
<PageHeader <PageHeader
title={<>Projekt <br /><span className="text-slate-200">konfigurieren.</span></>} title={
<>
Projekt <br />
<span className="text-slate-200">konfigurieren.</span>
</>
}
description="Nutzen Sie den Konfigurator für eine erste Einschätzung oder schreiben Sie mir direkt eine Email." description="Nutzen Sie den Konfigurator für eine erste Einschätzung oder schreiben Sie mir direkt eine Email."
backLink={{ href: '/', label: 'Zurück' }} backLink={{ href: "/", label: "Zurück" }}
backgroundSymbol="?" backgroundSymbol="?"
/> />
<Section number="01" title="Konfigurator" containerVariant="wide" className="!py-12"> <Section title="Konfigurator" containerVariant="wide" className="!py-12">
<ContactForm /> <ContactForm />
</Section> </Section>
<Section number="02" title="Direkt" className="!py-12"> <Section title="Direkt" className="!py-12">
<div className="grid grid-cols-1 gap-24"> <div className="grid grid-cols-1 gap-24">
<Reveal delay={0.4}> <Reveal delay={0.4}>
<div className="space-y-8"> <div className="space-y-8">
<a <a href="mailto:marc@mintel.me" className="group block space-y-2">
href="mailto:marc@mintel.me" <span className="text-xs font-bold uppercase tracking-widest text-slate-300 group-hover:text-slate-900 transition-colors">
className="group block space-y-2" Email
> </span>
<span className="text-xs font-bold uppercase tracking-widest text-slate-300 group-hover:text-slate-900 transition-colors">Email</span>
<p className="text-3xl md:text-6xl font-bold text-slate-900 border-b border-slate-100 group-hover:border-slate-900 transition-all duration-500 pb-6 tracking-tighter"> <p className="text-3xl md:text-6xl font-bold text-slate-900 border-b border-slate-100 group-hover:border-slate-900 transition-all duration-500 pb-6 tracking-tighter">
marc@mintel.me marc@mintel.me
</p> </p>

View File

@@ -9,18 +9,25 @@ import {
ConceptWebsite, ConceptWebsite,
DifferenceIllustration, DifferenceIllustration,
HeroArchitecture, HeroArchitecture,
HeroMainIllustration HeroMainIllustration,
} from '../src/components/Landing'; } from "../src/components/Landing";
import { Reveal } from '../src/components/Reveal'; import { Reveal } from "../src/components/Reveal";
import { Section } from '../src/components/Section'; import { Marker } from "../src/components/Marker";
import { H1, H3, LeadText, BodyText, MonoLabel, Label } from '../src/components/Typography'; import { Section } from "../src/components/Section";
import { BackgroundGrid, Card, Container } from '../src/components/Layout'; import {
import { Button } from '../src/components/Button'; H1,
H3,
LeadText,
BodyText,
MonoLabel,
Label,
} from "../src/components/Typography";
import { BackgroundGrid, Card, Container } from "../src/components/Layout";
import { Button } from "../src/components/Button";
export default function LandingPage() { export default function LandingPage() {
return ( return (
<div className="flex flex-col bg-white overflow-hidden relative"> <div className="flex flex-col bg-white overflow-hidden relative">
<BackgroundGrid /> <BackgroundGrid />
{/* Hero Section */} {/* Hero Section */}
@@ -33,7 +40,9 @@ export default function LandingPage() {
<div className="space-y-8"> <div className="space-y-8">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="w-8 h-px bg-slate-900"></div> <div className="w-8 h-px bg-slate-900"></div>
<MonoLabel className="text-slate-900">Digital Architect</MonoLabel> <MonoLabel className="text-slate-900">
Digital Architect
</MonoLabel>
</div> </div>
<H1 className="text-6xl md:text-8xl"> <H1 className="text-6xl md:text-8xl">
Websites <br /> Websites <br />
@@ -54,7 +63,10 @@ export default function LandingPage() {
<HeroArchitecture className="w-full h-full" /> <HeroArchitecture className="w-full h-full" />
</div> </div>
<Reveal delay={0.2} className="w-full h-full flex items-center justify-center"> <Reveal
delay={0.2}
className="w-full h-full flex items-center justify-center"
>
<div className="relative w-full h-full flex items-center justify-center pointer-events-none"> <div className="relative w-full h-full flex items-center justify-center pointer-events-none">
<HeroMainIllustration className="w-full h-full scale-110 md:scale-125 origin-center" /> <HeroMainIllustration className="w-full h-full scale-110 md:scale-125 origin-center" />
</div> </div>
@@ -65,16 +77,14 @@ export default function LandingPage() {
</section> </section>
{/* Section 02: The Promise */} {/* Section 02: The Promise */}
<Section <Section number="02" title="Das Versprechen" borderTop>
number="02"
title="Das Versprechen"
borderTop
>
<div className="space-y-16 relative"> <div className="space-y-16 relative">
<Reveal> <Reveal>
<H3 className="max-w-3xl"> <H3 className="max-w-3xl">
Schluss mit aufgeblähten Prozessen. <br /> Schluss mit aufgeblähten Prozessen. <br />
<span className="text-slate-200">Ich reduziere auf das Wesentliche.</span> <span className="text-slate-200">
Ich reduziere auf das Wesentliche.
</span>
</H3> </H3>
</Reveal> </Reveal>
@@ -86,10 +96,22 @@ export default function LandingPage() {
</div> </div>
<ul className="space-y-6"> <ul className="space-y-6">
{[ {[
{ text: 'Direkte Kommunikation ohne Umwege', icon: <ConceptCommunication className="w-12 h-12" /> }, {
{ text: 'Schnelle Prototypen statt langer Konzepte', icon: <ConceptPrototyping className="w-12 h-12" /> }, text: "Direkte Kommunikation ohne Umwege",
{ text: 'Sauberer Code, der auch morgen noch läuft', icon: <ConceptCode className="w-12 h-12" /> }, icon: <ConceptCommunication className="w-12 h-12" />,
{ text: 'Fixpreise für volle Budgetsicherheit', icon: <ConceptPrice className="w-12 h-12" /> } },
{
text: "Schnelle Prototypen statt langer Konzepte",
icon: <ConceptPrototyping className="w-12 h-12" />,
},
{
text: "Sauberer Code, der auch morgen noch läuft",
icon: <ConceptCode className="w-12 h-12" />,
},
{
text: "Fixpreise für volle Budgetsicherheit",
icon: <ConceptPrice className="w-12 h-12" />,
},
].map((item, i) => ( ].map((item, i) => (
<li key={i} className="flex items-center gap-6 group"> <li key={i} className="flex items-center gap-6 group">
<div className="shrink-0 transition-transform duration-500 group-hover:scale-110"> <div className="shrink-0 transition-transform duration-500 group-hover:scale-110">
@@ -109,14 +131,19 @@ export default function LandingPage() {
</div> </div>
<ul className="space-y-4"> <ul className="space-y-4">
{[ {[
'Endlose Workshops ohne Ergebnis', "Endlose Workshops ohne Ergebnis",
'PowerPoint-Schlachten', "PowerPoint-Schlachten",
'Outsourcing an Billig-Anbieter', "Outsourcing an Billig-Anbieter",
'Wartungsverträge mit versteckten Kosten' "Wartungsverträge mit versteckten Kosten",
].map((item, i) => ( ].map((item, i) => (
<li key={i} className="flex items-start gap-3 decoration-slate-200 line-through"> <li
key={i}
className="flex items-start gap-3 decoration-slate-200 line-through"
>
<span className="w-1.5 h-1.5 bg-slate-200 rounded-full mt-2.5 shrink-0"></span> <span className="w-1.5 h-1.5 bg-slate-200 rounded-full mt-2.5 shrink-0"></span>
<LeadText className="text-slate-400 text-lg">{item}</LeadText> <LeadText className="text-slate-400 text-lg">
{item}
</LeadText>
</li> </li>
))} ))}
</ul> </ul>
@@ -127,17 +154,15 @@ export default function LandingPage() {
</Section> </Section>
{/* Section 03: The Difference */} {/* Section 03: The Difference */}
<Section <Section number="03" title="Der Unterschied" variant="white" borderTop>
number="03"
title="Der Unterschied"
variant="white"
borderTop
>
<div className="space-y-16 relative"> <div className="space-y-16 relative">
<div className="flex flex-col md:flex-row gap-12 items-center"> <div className="flex flex-col md:flex-row gap-12 items-center">
<Reveal className="flex-1"> <Reveal className="flex-1">
<LeadText className="text-2xl md:text-3xl leading-tight max-w-2xl relative z-10 text-slate-400"> <LeadText className="text-2xl md:text-3xl leading-tight max-w-2xl relative z-10 text-slate-400">
Ich arbeite nicht gegen die Zeit, sondern <span className="text-slate-900">für das Ergebnis.</span> Mein Fokus liegt auf der Umsetzung, nicht auf der Verwaltung von Prozessen. Ich arbeite nicht gegen die Zeit, sondern{" "}
<span className="text-slate-900">für das Ergebnis.</span> Mein
Fokus liegt auf der Umsetzung, nicht auf der Verwaltung von
Prozessen.
</LeadText> </LeadText>
</Reveal> </Reveal>
<Reveal delay={0.2} className="w-full md:w-72 shrink-0"> <Reveal delay={0.2} className="w-full md:w-72 shrink-0">
@@ -168,11 +193,7 @@ export default function LandingPage() {
</Section> </Section>
{/* Section 04: Target Group */} {/* Section 04: Target Group */}
<Section <Section number="04" title="Zielgruppe" borderTop>
number="04"
title="Zielgruppe"
borderTop
>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 relative z-10"> <div className="grid grid-cols-1 md:grid-cols-2 gap-8 relative z-10">
<Reveal> <Reveal>
<Card variant="dark" padding="normal" className="group"> <Card variant="dark" padding="normal" className="group">
@@ -180,13 +201,19 @@ export default function LandingPage() {
<div className="w-16 h-16 bg-white/5 rounded-xl flex items-center justify-center border border-white/10"> <div className="w-16 h-16 bg-white/5 rounded-xl flex items-center justify-center border border-white/10">
<ConceptPrice className="w-8 h-8" /> <ConceptPrice className="w-8 h-8" />
</div> </div>
<H3 className="text-white text-3xl">Unternehmer & <br />Geschäftsführer</H3> <H3 className="text-white text-3xl">
Unternehmer & <br />
Geschäftsführer
</H3>
<LeadText className="text-slate-400 text-lg"> <LeadText className="text-slate-400 text-lg">
"Ich brauche eine Lösung, die funktioniert. Ich habe keine Zeit für technische Details." "Ich brauche eine Lösung, die funktioniert. Ich habe keine
Zeit für technische Details."
</LeadText> </LeadText>
</div> </div>
<div className="pt-8 border-t border-white/5 mt-8"> <div className="pt-8 border-t border-white/5 mt-8">
<Label className="group-hover:text-white transition-colors">Perfekt für Sie</Label> <Label className="group-hover:text-white transition-colors">
Perfekt für Sie
</Label>
</div> </div>
</Card> </Card>
</Reveal> </Reveal>
@@ -196,13 +223,19 @@ export default function LandingPage() {
<div className="w-16 h-16 bg-slate-50 border border-slate-100 rounded-xl flex items-center justify-center"> <div className="w-16 h-16 bg-slate-50 border border-slate-100 rounded-xl flex items-center justify-center">
<ConceptWebsite className="w-8 h-8" /> <ConceptWebsite className="w-8 h-8" />
</div> </div>
<H3 className="text-3xl">Marketing & <br />Vertrieb</H3> <H3 className="text-3xl">
Marketing & <br />
Vertrieb
</H3>
<LeadText className="text-slate-400 text-lg"> <LeadText className="text-slate-400 text-lg">
"Wir brauchen Landingpages und Tools, um unsere Ziele zu erreichen. Schnell und zuverlässig." "Wir brauchen Landingpages und Tools, um unsere Ziele zu
erreichen. Schnell und zuverlässig."
</LeadText> </LeadText>
</div> </div>
<div className="pt-8 border-t border-slate-50 mt-8"> <div className="pt-8 border-t border-slate-50 mt-8">
<Label className="group-hover:text-slate-900 transition-colors">Perfekt für Sie</Label> <Label className="group-hover:text-slate-900 transition-colors">
Perfekt für Sie
</Label>
</div> </div>
</Card> </Card>
</Reveal> </Reveal>
@@ -210,12 +243,7 @@ export default function LandingPage() {
</Section> </Section>
{/* Section 05: Services */} {/* Section 05: Services */}
<Section <Section number="05" title="Leistungen" variant="gray" borderTop>
number="05"
title="Leistungen"
variant="gray"
borderTop
>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 relative z-20"> <div className="grid grid-cols-1 md:grid-cols-3 gap-8 relative z-20">
<Reveal delay={0.1}> <Reveal delay={0.1}>
<Card variant="white" padding="small" className="group"> <Card variant="white" padding="small" className="group">
@@ -225,10 +253,14 @@ export default function LandingPage() {
<div className="space-y-4"> <div className="space-y-4">
<H3 className="text-2xl">Websites</H3> <H3 className="text-2xl">Websites</H3>
<BodyText> <BodyText>
High-Performance Websites. Maßgeschneiderte Architektur statt Baukasten. High-Performance Websites. Maßgeschneiderte Architektur statt
Baukasten.
</BodyText> </BodyText>
<div className="pt-4"> <div className="pt-4">
<a href="/websites" className="text-[10px] font-bold uppercase tracking-[0.4em] text-slate-900 border-b border-slate-100 pb-1 hover:border-slate-900 transition-all"> <a
href="/websites"
className="text-[10px] font-bold uppercase tracking-[0.4em] text-slate-900 border-b border-slate-100 pb-1 hover:border-slate-900 transition-all"
>
Details Details
</a> </a>
</div> </div>
@@ -237,14 +269,19 @@ export default function LandingPage() {
</Reveal> </Reveal>
<Reveal delay={0.3}> <Reveal delay={0.3}>
<Card variant="white" padding="small" className="group mt-8 md:mt-0"> <Card
variant="white"
padding="small"
className="group mt-8 md:mt-0"
>
<div className="w-16 h-16 bg-slate-50 rounded-xl flex items-center justify-center mb-8 group-hover:scale-110 transition-transform duration-500"> <div className="w-16 h-16 bg-slate-50 rounded-xl flex items-center justify-center mb-8 group-hover:scale-110 transition-transform duration-500">
<ConceptSystem className="w-8 h-8" /> <ConceptSystem className="w-8 h-8" />
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
<H3 className="text-2xl">Systeme</H3> <H3 className="text-2xl">Systeme</H3>
<BodyText> <BodyText>
Web-Applikationen, Portale, interne Tools. Wenn Standard an Grenzen stößt. Web-Applikationen, Portale, interne Tools. Wenn Standard an
Grenzen stößt.
</BodyText> </BodyText>
</div> </div>
</Card> </Card>
@@ -258,7 +295,8 @@ export default function LandingPage() {
<div className="space-y-4"> <div className="space-y-4">
<H3 className="text-2xl">Automatisierung</H3> <H3 className="text-2xl">Automatisierung</H3>
<BodyText> <BodyText>
Verbindung von Tools, automatische Prozesse, Daten-Synchronisation. Verbindung von Tools, automatische Prozesse,
Daten-Synchronisation.
</BodyText> </BodyText>
</div> </div>
</Card> </Card>
@@ -267,11 +305,7 @@ export default function LandingPage() {
</Section> </Section>
{/* Section 06: Contact */} {/* Section 06: Contact */}
<Section <Section number="06" title="Kontakt" borderTop>
number="06"
title="Kontakt"
borderTop
>
<div className="relative py-12" id="contact"> <div className="relative py-12" id="contact">
<Reveal> <Reveal>
<div className="space-y-16"> <div className="space-y-16">
@@ -283,7 +317,8 @@ export default function LandingPage() {
<div className="flex flex-col md:flex-row gap-16 items-start relative z-10"> <div className="flex flex-col md:flex-row gap-16 items-start relative z-10">
<div className="space-y-8 flex-1"> <div className="space-y-8 flex-1">
<LeadText className="text-2xl md:text-3xl text-slate-400"> <LeadText className="text-2xl md:text-3xl text-slate-400">
Schreiben Sie mir kurz, worum es geht. Ich melde mich innerhalb von <span className="text-slate-900">24 Stunden</span>. Schreiben Sie mir kurz, worum es geht. Ich melde mich{" "}
<span className="text-slate-900">zeitnah</span> bei Ihnen.
</LeadText> </LeadText>
<div className="pt-4"> <div className="pt-4">
<a <a
@@ -301,7 +336,9 @@ export default function LandingPage() {
<Label className="text-slate-900">Verfügbarkeit</Label> <Label className="text-slate-900">Verfügbarkeit</Label>
</div> </div>
<BodyText className="text-base leading-snug"> <BodyText className="text-base leading-snug">
Aktuell nehme ich Projekte für <span className="font-bold text-slate-900">Q2 2026</span> an. Aktuell nehme ich Projekte für{" "}
<span className="font-bold text-slate-900">Q2 2026</span>{" "}
an.
</BodyText> </BodyText>
</div> </div>
</div> </div>

BIN
apps/web/public/marc-mintel.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -537,8 +537,8 @@ export function ContactForm({
Anfrage gesendet! Anfrage gesendet!
</h2> </h2>
<p className="text-slate-400 text-2xl max-w-2xl mx-auto leading-relaxed"> <p className="text-slate-400 text-2xl max-w-2xl mx-auto leading-relaxed">
Vielen Dank, {state.name.split(" ")[0]}. Ich melde mich innerhalb Vielen Dank, {state.name.split(" ")[0]}. Ich melde mich zeitnah bei
von 24 Stunden bei Ihnen. Ihnen.
</p> </p>
</div> </div>
<button <button

View File

@@ -1,7 +1,7 @@
import * as React from 'react'; import * as React from "react";
import { ArrowRight } from 'lucide-react'; import { ArrowRight } from "lucide-react";
import { Reveal } from '../Reveal'; import { Reveal } from "../Reveal";
import { Label, H3, LeadText } from '../Typography'; import { Label, H3, LeadText } from "../Typography";
interface ComparisonRowProps { interface ComparisonRowProps {
negativeLabel: string; negativeLabel: string;
@@ -22,27 +22,27 @@ export const ComparisonRow: React.FC<ComparisonRowProps> = ({
}) => { }) => {
return ( return (
<Reveal delay={delay}> <Reveal delay={delay}>
<div className={`flex flex-col ${reverse ? 'md:flex-row-reverse' : 'md:flex-row'} gap-8 md:gap-12 items-center`}> <div
className={`flex flex-col ${reverse ? "md:flex-row-reverse" : "md:flex-row"} gap-8 md:gap-12 items-center`}
>
<div className="flex-1 p-8 md:p-10 bg-slate-50/50 rounded-2xl text-slate-400 border border-transparent w-full"> <div className="flex-1 p-8 md:p-10 bg-slate-50/50 rounded-2xl text-slate-400 border border-transparent w-full">
<Label className="mb-4 line-through decoration-slate-200"> <Label className="mb-4 line-through decoration-red-500">
{negativeLabel} {negativeLabel}
</Label> </Label>
<LeadText className="line-through decoration-slate-200 leading-snug"> <LeadText className="line-through decoration-red-500 leading-snug">
{negativeText} {negativeText}
</LeadText> </LeadText>
</div> </div>
<div className="shrink-0"> <div className="shrink-0">
<ArrowRight className={`w-6 h-6 text-slate-200 hidden md:block ${reverse ? 'rotate-180' : ''}`} /> <ArrowRight
className={`w-6 h-6 text-slate-200 hidden md:block ${reverse ? "rotate-180" : ""}`}
/>
</div> </div>
<div className="flex-1 p-8 md:p-10 border border-slate-100 rounded-2xl bg-white hover:border-slate-200 transition-all duration-500 hover:shadow-xl hover:shadow-slate-100/50 w-full"> <div className="flex-1 p-8 md:p-10 border border-slate-100 rounded-2xl bg-white hover:border-slate-200 transition-all duration-500 hover:shadow-xl hover:shadow-slate-100/50 w-full">
<Label className="text-slate-900 mb-4"> <Label className="text-slate-900 mb-4">{positiveLabel}</Label>
{positiveLabel} <H3 className="text-2xl md:text-3xl">{positiveText}</H3>
</Label>
<H3 className="text-2xl md:text-3xl">
{positiveText}
</H3>
</div> </div>
</div> </div>
</Reveal> </Reveal>

View File

@@ -0,0 +1,42 @@
"use client";
import React from "react";
import { motion } from "framer-motion";
/**
* TECHNICAL MARKER COMPONENT
* Implements the "hand-drawn marker" effect.
* Animates in when entering the viewport.
*/
interface MarkerProps {
children: React.ReactNode;
delay?: number;
className?: string;
color?: string;
}
export const Marker: React.FC<MarkerProps> = ({
children,
delay = 0,
className = "",
color = "rgba(255,235,59,0.95)",
}) => {
return (
<span className={`relative inline-block px-1 ${className}`}>
<motion.span
initial={{ scaleX: 0, opacity: 0 }}
whileInView={{ scaleX: 1, opacity: 1 }}
viewport={{ once: true, margin: "-5%" }}
transition={{
duration: 1.2,
delay: delay + 0.1,
ease: [0.23, 1, 0.32, 1],
}}
className="absolute inset-0 z-[-1] -skew-x-6 rotate-[-1deg] translate-y-1 transform-gpu origin-left"
style={{ backgroundColor: color }}
aria-hidden="true"
/>
{children}
</span>
);
};

View File

@@ -1,26 +1,26 @@
'use client'; "use client";
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef } from "react";
import { motion, useInView, useAnimation, Variants } from 'framer-motion'; import { motion, useInView, useAnimation, Variants } from "framer-motion";
interface RevealProps { interface RevealProps {
children: React.ReactNode; children: React.ReactNode;
width?: 'fit-content' | '100%'; width?: "fit-content" | "100%";
delay?: number; delay?: number;
className?: string; className?: string;
direction?: 'up' | 'down' | 'left' | 'right' | 'none'; direction?: "up" | "down" | "left" | "right" | "none";
scale?: number; scale?: number;
blur?: boolean; blur?: boolean;
} }
export const Reveal: React.FC<RevealProps> = ({ export const Reveal: React.FC<RevealProps> = ({
children, children,
width = 'fit-content', width = "fit-content",
delay = 0.25, delay = 0.25,
className = "", className = "",
direction = 'up', direction = "up",
scale = 1, scale = 1,
blur = false blur = false,
}) => { }) => {
const ref = useRef(null); const ref = useRef(null);
const isInView = useInView(ref, { once: true, margin: "-10%" }); const isInView = useInView(ref, { once: true, margin: "-10%" });
@@ -32,31 +32,18 @@ export const Reveal: React.FC<RevealProps> = ({
} }
}, [isInView, mainControls]); }, [isInView, mainControls]);
const getDirectionOffset = () => {
switch (direction) {
case 'up': return { y: 20 };
case 'down': return { y: -20 };
case 'left': return { x: 20 };
case 'right': return { x: -20 };
default: return {};
}
};
const variants: Variants = { const variants: Variants = {
hidden: { hidden: {
opacity: 0, opacity: 0,
...getDirectionOffset(), y: direction === "up" ? 10 : direction === "down" ? -10 : 0,
scale: scale !== 1 ? scale : 0.99, x: direction === "left" ? 10 : direction === "right" ? -10 : 0,
rotateX: direction === 'up' ? 2 : direction === 'down' ? -2 : 0, scale: scale !== 1 ? scale : 1,
rotateY: direction === 'left' ? -2 : direction === 'right' ? 2 : 0,
}, },
visible: { visible: {
opacity: 1, opacity: 1,
y: 0, y: 0,
x: 0, x: 0,
scale: 1, scale: 1,
rotateX: 0,
rotateY: 0,
}, },
}; };
@@ -66,7 +53,6 @@ export const Reveal: React.FC<RevealProps> = ({
style={{ style={{
position: "relative", position: "relative",
width, width,
perspective: "1000px"
}} }}
className={className} className={className}
> >
@@ -76,13 +62,13 @@ export const Reveal: React.FC<RevealProps> = ({
animate={mainControls} animate={mainControls}
style={{ transformStyle: "preserve-3d" }} style={{ transformStyle: "preserve-3d" }}
transition={{ transition={{
duration: 0.8, duration: 0.4,
delay: delay, delay: delay,
type: "spring", type: "spring",
stiffness: 70, stiffness: 260,
damping: 24, damping: 20,
mass: 1, mass: 1,
opacity: { duration: 0.5, ease: [0.16, 1, 0.3, 1] } opacity: { duration: 0.3, ease: [0.16, 1, 0.3, 1] },
}} }}
> >
{children} {children}

90
docker-compose.dev.yml Normal file
View File

@@ -0,0 +1,90 @@
services:
app:
# Lighweight proxy to bridge Traefik to local host (HMR support)
image: alpine/socat
restart: always
command: tcp-listen:3000,fork,reuseaddr tcp:host.docker.internal:3000
extra_hosts:
- "host.docker.internal:host-gateway"
networks:
- default
- infra
labels:
- "traefik.enable=true"
- 'traefik.http.routers.${PROJECT_NAME:-mintel-me}-web.rule=${TRAEFIK_HOST_RULE:-Host("${TRAEFIK_HOST:-mintel.localhost}")}'
- "traefik.http.routers.${PROJECT_NAME:-mintel-me}-web.entrypoints=web"
- 'traefik.http.routers.${PROJECT_NAME:-mintel-me}.rule=${TRAEFIK_HOST_RULE:-Host("${TRAEFIK_HOST:-mintel.localhost}")}'
- "traefik.http.routers.${PROJECT_NAME:-mintel-me}.entrypoints=${TRAEFIK_ENTRYPOINT:-web}"
- "traefik.http.services.${PROJECT_NAME:-mintel-me}.loadbalancer.server.port=3000"
- "traefik.docker.network=infra"
gatekeeper:
profiles: ["gatekeeper"]
image: registry.infra.mintel.me/mintel/gatekeeper:v1.7.12
container_name: ${PROJECT_NAME:-mintel-me}-gatekeeper
restart: always
networks:
infra:
aliases:
- ${PROJECT_NAME:-mintel-me}-gatekeeper
env_file:
- .env
environment:
PORT: 3000
labels:
- "traefik.enable=true"
- "traefik.http.services.${PROJECT_NAME:-mintel-me}-gatekeeper.loadbalancer.server.port=3000"
- "traefik.docker.network=infra"
directus:
image: registry.infra.mintel.me/mintel/directus:latest
restart: always
networks:
- default
- infra
env_file:
- .env
environment:
KEY: ${DIRECTUS_KEY}
SECRET: ${DIRECTUS_SECRET}
DB_CLIENT: "pg"
DB_HOST: "directus-db"
DB_PORT: "5432"
DB_DATABASE: ${DIRECTUS_DB_NAME:-directus}
DB_USER: ${DIRECTUS_DB_USER:-directus}
DB_PASSWORD: ${DIRECTUS_DB_PASSWORD:-directus}
PUBLIC_URL: ${DIRECTUS_URL:-https://cms.mintel.me}
volumes:
- ./directus/uploads:/directus/uploads
- ./directus/extensions:/directus/extensions
- ./directus/schema:/directus/schema
- ./directus/migrations:/directus/migrations
labels:
- "traefik.enable=true"
- 'traefik.http.routers.${PROJECT_NAME:-mintel-me}-directus.rule=${TRAEFIK_DIRECTUS_RULE:-Host("${DIRECTUS_HOST:-cms.mintel.localhost}")}'
- "traefik.http.routers.${PROJECT_NAME:-mintel-me}-directus.entrypoints=web"
- "traefik.http.services.${PROJECT_NAME:-mintel-me}-directus.loadbalancer.server.port=8055"
- "traefik.docker.network=infra"
directus-db:
image: postgres:15-alpine
restart: always
networks:
- default
env_file:
- .env
environment:
POSTGRES_DB: ${DIRECTUS_DB_NAME:-directus}
POSTGRES_USER: ${DIRECTUS_DB_USER:-directus}
POSTGRES_PASSWORD: ${DIRECTUS_DB_PASSWORD:-directus}
volumes:
- directus-db-data:/var/lib/postgresql/data
networks:
default:
name: ${PROJECT_NAME:-mintel-me}-internal
infra:
external: true
volumes:
directus-db-data:

View File

@@ -4,7 +4,7 @@
"type": "module", "type": "module",
"packageManager": "pnpm@10.18.3", "packageManager": "pnpm@10.18.3",
"scripts": { "scripts": {
"dev": "docker network create infra 2>/dev/null || true && echo '\\n🚀 Development Environment Starting...\\n\\n📱 App: http://mintel.localhost\\n🗄 CMS: http://cms.mintel.localhost/admin\\n🚦 Traefik: http://localhost:8080\\n\\n(Press Ctrl+C to stop)\\n' && docker-compose down --remove-orphans && docker-compose up app directus directus-db gatekeeper", "dev": "docker network create infra 2>/dev/null || true && echo '\\n🚀 Development Environment Starting...\\n\\n📱 App: http://mintel.localhost\\n🗄 CMS: http://cms.mintel.localhost/admin\\n🚦 Traefik: http://localhost:8080\\n\\n' && docker-compose -f docker-compose.dev.yml down --remove-orphans && (docker-compose -f docker-compose.dev.yml up app directus directus-db gatekeeper & pnpm -r dev)",
"dev:local": "pnpm -r dev", "dev:local": "pnpm -r dev",
"build": "pnpm -r build", "build": "pnpm -r build",
"start": "pnpm -r start", "start": "pnpm -r start",