pages
Some checks failed
Build & Deploy Mintel Blog / build-and-deploy (push) Failing after 1m26s

This commit is contained in:
2026-01-31 10:09:12 +01:00
parent 03fa2ea4a9
commit cd2d267e48
15 changed files with 745 additions and 789 deletions

67
src/components/Button.tsx Normal file
View File

@@ -0,0 +1,67 @@
import * as React from 'react';
import { ArrowRight } from 'lucide-react';
import { motion } from 'framer-motion';
import Link from 'next/link';
interface ButtonProps {
href: string;
children: React.ReactNode;
variant?: 'primary' | 'outline';
className?: string;
showArrow?: boolean;
}
export const Button: React.FC<ButtonProps> = ({
href,
children,
variant = 'primary',
className = "",
showArrow = true
}) => {
const baseStyles = "inline-flex items-center gap-4 rounded-full font-bold uppercase tracking-widest transition-all duration-500 ease-[cubic-bezier(0.23,1,0.32,1)] group";
const variants = {
primary: "px-10 py-5 bg-slate-900 text-white hover:bg-slate-800 hover:-translate-y-1 hover:shadow-2xl hover:shadow-slate-900/20 text-sm",
outline: "px-8 py-4 border border-slate-200 bg-white text-slate-900 hover:border-slate-400 hover:bg-slate-50 hover:-translate-y-0.5 hover:shadow-xl hover:shadow-slate-100 text-sm"
};
const content = (
<>
{children}
{showArrow && <ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />}
</>
);
if (href.startsWith('#')) {
return (
<a href={href} className={`${baseStyles} ${variants[variant]} ${className}`}>
{content}
</a>
);
}
return (
<Link href={href} className={`${baseStyles} ${variants[variant]} ${className}`}>
{content}
</Link>
);
};
export const MotionButton: React.FC<ButtonProps> = ({
href,
children,
variant = 'primary',
className = "",
showArrow = true
}) => {
return (
<motion.div
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
<Button href={href} variant={variant} className={className} showArrow={showArrow}>
{children}
</Button>
</motion.div>
);
};

View File

@@ -8,7 +8,6 @@ interface LineProps {
delay?: number;
}
// ... existing components ...
export const HeroLines: React.FC<LineProps> = ({ className = "", delay = 0 }) => {
return (
<svg className={`absolute pointer-events-none ${className}`} viewBox="0 0 800 600" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -216,150 +215,7 @@ export const ComparisonLines: React.FC<LineProps> = ({ className = "", delay = 0
)
}
// --- New Connector Components ---
export const ConnectorStart: React.FC<LineProps> = ({ className = "", delay = 0 }) => {
return (
<svg className={`absolute pointer-events-none ${className}`} viewBox="0 0 100 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<motion.path
d="M 50 0 V 400"
stroke="currentColor"
strokeWidth="3"
className="text-slate-300"
initial={{ pathLength: 0 }}
whileInView={{ pathLength: 1 }}
viewport={{ once: true }}
transition={{ duration: 1.5, delay: delay }}
/>
<circle cx="50" cy="10" r="8" className="fill-slate-900" />
{/* Multiple Pulses */}
<motion.circle r="5" fill="currentColor" className="text-slate-900">
<animateMotion
dur="4s"
repeatCount="indefinite"
path="M 50 0 V 400"
/>
</motion.circle>
<motion.circle r="5" fill="currentColor" className="text-slate-900">
<animateMotion
dur="4s"
begin="2s"
repeatCount="indefinite"
path="M 50 0 V 400"
/>
</motion.circle>
</svg>
);
};
export const ConnectorBranch: React.FC<LineProps> = ({ className = "", delay = 0 }) => {
return (
<svg className={`absolute pointer-events-none ${className}`} viewBox="0 0 200 400" fill="none" xmlns="http://www.w3.org/2000/svg">
{/* Main vertical line */}
<motion.path
d="M 50 0 V 400"
stroke="currentColor"
strokeWidth="3"
className="text-slate-300"
initial={{ pathLength: 0 }}
whileInView={{ pathLength: 1 }}
viewport={{ once: true }}
transition={{ duration: 1.5, delay: delay }}
/>
{/* Branch */}
<motion.path
d="M 50 100 C 50 150, 100 150, 150 150 H 200"
stroke="currentColor"
strokeWidth="3"
className="text-slate-300"
initial={{ pathLength: 0 }}
whileInView={{ pathLength: 1 }}
viewport={{ once: true }}
transition={{ duration: 1.5, delay: delay + 0.5 }}
/>
{/* Pulses */}
<motion.circle r="5" fill="currentColor" className="text-slate-900">
<animateMotion
dur="4s"
repeatCount="indefinite"
path="M 50 0 V 400"
/>
</motion.circle>
<motion.circle r="5" fill="currentColor" className="text-slate-900">
<animateMotion
dur="3s"
repeatCount="indefinite"
path="M 50 100 C 50 150, 100 150, 150 150 H 200"
/>
</motion.circle>
</svg>
);
};
export const ConnectorSplit: React.FC<LineProps> = ({ className = "", delay = 0 }) => {
return (
<svg className={`absolute pointer-events-none ${className}`} viewBox="0 0 200 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<motion.path
d="M 50 0 V 50 C 50 100, 100 100, 150 100 H 200"
stroke="currentColor"
strokeWidth="3"
className="text-slate-300"
initial={{ pathLength: 0 }}
whileInView={{ pathLength: 1 }}
viewport={{ once: true }}
transition={{ duration: 1.5, delay: delay }}
/>
<motion.path
d="M 50 50 V 400"
stroke="currentColor"
strokeWidth="3"
className="text-slate-300"
initial={{ pathLength: 0 }}
whileInView={{ pathLength: 1 }}
viewport={{ once: true }}
transition={{ duration: 1.5, delay: delay }}
/>
<motion.circle r="5" fill="currentColor" className="text-slate-900">
<animateMotion
dur="4s"
repeatCount="indefinite"
path="M 50 0 V 400"
/>
</motion.circle>
<motion.circle r="5" fill="currentColor" className="text-slate-900">
<animateMotion
dur="3s"
repeatCount="indefinite"
path="M 50 0 V 50 C 50 100, 100 100, 150 100 H 200"
/>
</motion.circle>
</svg>
);
};
export const ConnectorEnd: React.FC<LineProps> = ({ className = "", delay = 0 }) => {
return (
<svg className={`absolute pointer-events-none ${className}`} viewBox="0 0 100 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<motion.path
d="M 50 0 V 200"
stroke="currentColor"
strokeWidth="3"
className="text-slate-300"
initial={{ pathLength: 0 }}
whileInView={{ pathLength: 1 }}
viewport={{ once: true }}
transition={{ duration: 1.5, delay: delay }}
/>
<circle cx="50" cy="200" r="8" className="fill-slate-900" />
<motion.circle r="5" fill="currentColor" className="text-slate-900">
<animateMotion
dur="2s"
repeatCount="indefinite"
path="M 50 0 V 200"
/>
</motion.circle>
</svg>
);
};
export const ConnectorStart: React.FC<LineProps> = ({ className = "", delay = 0 }) => null;
export const ConnectorBranch: React.FC<LineProps> = ({ className = "", delay = 0 }) => null;
export const ConnectorSplit: React.FC<LineProps> = ({ className = "", delay = 0 }) => null;
export const ConnectorEnd: React.FC<LineProps> = ({ className = "", delay = 0 }) => null;

View File

@@ -1,6 +1,7 @@
import * as React from 'react';
import { ArrowRight } from 'lucide-react';
import { Reveal } from '../Reveal';
import { Label, H3, LeadText } from '../Typography';
interface ComparisonRowProps {
negativeLabel: string;
@@ -23,12 +24,12 @@ export const ComparisonRow: React.FC<ComparisonRowProps> = ({
<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-1 p-8 md:p-10 bg-slate-50/50 rounded-2xl text-slate-400 border border-transparent w-full">
<div className="text-[10px] font-bold uppercase tracking-[0.2em] mb-4 line-through decoration-slate-200">
<Label className="mb-4 line-through decoration-slate-200">
{negativeLabel}
</div>
<div className="text-lg md:text-xl font-serif italic line-through decoration-slate-200 leading-snug">
</Label>
<LeadText className="line-through decoration-slate-200 leading-snug">
{negativeText}
</div>
</LeadText>
</div>
<div className="shrink-0">
@@ -36,12 +37,12 @@ export const ComparisonRow: React.FC<ComparisonRowProps> = ({
</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="text-[10px] font-bold uppercase tracking-[0.2em] text-slate-900 mb-4">
<Label className="text-slate-900 mb-4">
{positiveLabel}
</div>
<div className="text-2xl md:text-3xl font-bold text-slate-900 leading-tight tracking-tight">
</Label>
<H3 className="text-2xl md:text-3xl">
{positiveText}
</div>
</H3>
</div>
</div>
</Reveal>

View File

@@ -13,3 +13,4 @@ export * from './HeroMainIllustration';
export * from './ExperienceIllustration';
export * from './ResponsibilityIllustration';
export * from './ResultIllustration';
export * from './WebsitesDescriptive';

68
src/components/Layout.tsx Normal file
View File

@@ -0,0 +1,68 @@
import * as React from 'react';
import { cn } from '../utils/cn';
export const BackgroundGrid: React.FC = () => (
<div className="fixed inset-0 pointer-events-none -z-20 opacity-[0.01]" style={{
backgroundImage: 'linear-gradient(#0f172a 1px, transparent 1px), linear-gradient(90deg, #0f172a 1px, transparent 1px)',
backgroundSize: '60px 60px'
}} />
);
interface CardProps {
children: React.ReactNode;
className?: string;
variant?: 'white' | 'dark' | 'gray';
hover?: boolean;
padding?: 'none' | 'small' | 'normal' | 'large';
}
export const Card: React.FC<CardProps> = ({
children,
className = "",
variant = 'white',
hover = true,
padding = 'normal'
}) => {
const variants = {
white: 'bg-white border-slate-100 text-slate-900 shadow-sm',
dark: 'bg-slate-900 border-white/5 text-white shadow-xl',
gray: 'bg-slate-50/50 border-slate-100 text-slate-900'
};
const paddings = {
none: 'p-0',
small: 'p-6 md:p-8',
normal: 'p-8 md:p-10',
large: 'p-10 md:p-12'
};
return (
<div className={cn(
"rounded-2xl border h-full flex flex-col justify-between transition-all duration-500 ease-out",
variants[variant],
paddings[padding],
hover ? 'hover:border-slate-200 hover:shadow-md' : '',
className
)}>
{children}
</div>
);
};
export const Container: React.FC<{ children: React.ReactNode; className?: string; variant?: 'narrow' | 'normal' | 'wide' }> = ({
children,
className = "",
variant = 'normal'
}) => {
const variants = {
narrow: 'max-w-4xl',
normal: 'max-w-6xl',
wide: 'max-w-7xl'
};
return (
<div className={cn("mx-auto px-6 w-full", variants[variant], className)}>
{children}
</div>
);
};

View File

@@ -2,6 +2,8 @@ import * as React from 'react';
import { ArrowLeft } from 'lucide-react';
import Link from 'next/link';
import { Reveal } from './Reveal';
import { H1, LeadText } from './Typography';
import { cn } from '../utils/cn';
interface PageHeaderProps {
title: React.ReactNode;
@@ -11,18 +13,20 @@ interface PageHeaderProps {
label: string;
};
backgroundSymbol?: string;
className?: string;
}
export const PageHeader: React.FC<PageHeaderProps> = ({
title,
description,
backLink,
backgroundSymbol
backgroundSymbol,
className = ""
}) => {
return (
<section className="narrow-container relative">
<section className={cn("narrow-container relative pt-24 pb-16 md:pt-40 md:pb-24", className)}>
{backgroundSymbol && (
<div className="absolute -left-24 -top-24 text-[20rem] font-bold text-slate-50 select-none -z-10 opacity-50">
<div className="absolute -left-24 -top-12 text-[20rem] md:text-[24rem] font-bold text-slate-50 select-none -z-10 opacity-40 tracking-tighter leading-none">
{backgroundSymbol}
</div>
)}
@@ -30,24 +34,24 @@ export const PageHeader: React.FC<PageHeaderProps> = ({
{backLink && (
<Link
href={backLink.href}
className="inline-flex items-center gap-3 px-4 py-2 border border-slate-200 rounded-full text-slate-400 hover:text-slate-900 hover:border-slate-400 hover:bg-slate-50 mb-12 transition-all duration-500 ease-[cubic-bezier(0.23,1,0.32,1)] hover:-translate-y-0.5 hover:shadow-lg hover:shadow-slate-100 font-bold text-[10px] uppercase tracking-[0.3em] group"
className="inline-flex items-center gap-2 text-slate-400 hover:text-slate-900 mb-12 transition-colors font-bold text-[10px] uppercase tracking-[0.4em] group"
>
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-1 transition-transform" /> {backLink.label}
<ArrowLeft className="w-3 h-3 group-hover:-translate-x-1 transition-transform" /> {backLink.label}
</Link>
)}
<div className="space-y-16">
<div className="space-y-8 relative">
<Reveal>
<h1 className="text-6xl md:text-8xl font-bold text-slate-900 tracking-tighter leading-[0.95]">
<H1 className="max-w-4xl">
{title}
</h1>
</H1>
</Reveal>
{description && (
<Reveal delay={0.2}>
<p className="text-2xl md:text-3xl text-slate-500 font-serif italic leading-snug max-w-3xl">
<LeadText className="text-xl md:text-2xl text-slate-400 leading-relaxed max-w-2xl font-serif italic">
{description}
</p>
</LeadText>
</Reveal>
)}
</div>

View File

@@ -1,5 +1,7 @@
import * as React from 'react';
import { Reveal } from './Reveal';
import { Label } from './Typography';
import { cn } from '../utils/cn';
interface SectionProps {
number?: string;
@@ -9,7 +11,7 @@ interface SectionProps {
delay?: number;
variant?: 'white' | 'gray';
borderTop?: boolean;
connector?: React.ReactNode;
borderBottom?: boolean;
containerVariant?: 'narrow' | 'normal' | 'wide';
illustration?: React.ReactNode;
}
@@ -22,93 +24,59 @@ export const Section: React.FC<SectionProps> = ({
delay = 0,
variant = 'white',
borderTop = false,
connector,
borderBottom = false,
containerVariant = 'narrow',
illustration,
}) => {
const bgClass = variant === 'gray' ? 'bg-slate-50' : 'bg-white';
const borderClass = borderTop ? 'border-t border-slate-100' : '';
const bgClass = variant === 'gray' ? 'bg-slate-50/50' : 'bg-white';
const borderTopClass = borderTop ? 'border-t border-slate-100' : '';
const borderBottomClass = borderBottom ? 'border-b border-slate-100' : '';
const containerClass = containerVariant === 'wide' ? 'wide-container' : containerVariant === 'normal' ? 'container' : 'narrow-container';
// If no number and title, or if we want to force a simple layout, we could add a prop.
// But let's make it smart: if it's wide, maybe we want the title on top anyway?
// The user specifically asked to move it above for the configurator.
const isTopTitle = containerVariant === 'wide';
return (
<section className={`relative py-24 md:py-32 group ${bgClass} ${borderClass} ${className}`}>
<div className={containerClass}>
{isTopTitle ? (
<div className="space-y-16">
{(number || title) && (
<div className="flex flex-col md:flex-row md:items-end gap-6 md:gap-12">
{number && (
<Reveal delay={delay}>
<span className="block text-6xl md:text-8xl font-bold text-slate-100 leading-none select-none">
{number}
</span>
</Reveal>
)}
{title && (
<Reveal delay={delay + 0.1}>
<div className="flex items-center gap-3 mb-2 md:mb-4">
<div className="h-px w-6 bg-slate-900"></div>
<h2 className="text-xs font-bold uppercase tracking-[0.3em] text-slate-900">
{title}
</h2>
</div>
</Reveal>
)}
</div>
)}
<div>{children}</div>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-12 gap-12 md:gap-16">
{/* Sidebar: Number & Title */}
<div className="md:col-span-3 relative">
{/* Connector Line */}
{connector && (
<div className="absolute left-[2.5rem] top-0 bottom-0 w-24 hidden md:block -z-10 pointer-events-none">
{connector}
</div>
<section className={cn(
"relative py-24 md:py-40 group overflow-hidden",
bgClass,
borderTopClass,
borderBottomClass,
className
)}>
<div className={cn("relative z-10", containerClass)}>
<div className="grid grid-cols-1 md:grid-cols-12 gap-12 md:gap-24">
{/* Sidebar: Number & Title */}
<div className="md:col-span-3">
<div className="md:sticky md:top-40 space-y-8">
{number && (
<Reveal delay={delay}>
<span className="block text-7xl md:text-8xl font-bold text-slate-100 leading-none select-none tracking-tighter">
{number}
</span>
</Reveal>
)}
{title && (
<Reveal delay={delay + 0.1}>
<div className="flex items-center gap-4">
<Label className="text-slate-900 text-[10px] tracking-[0.4em]">
{title}
</Label>
</div>
</Reveal>
)}
{illustration && (
<Reveal delay={delay + 0.2}>
<div className="pt-8 opacity-100 group-hover:scale-105 transition-transform duration-1000 ease-out origin-left grayscale hover:grayscale-0">
{illustration}
</div>
</Reveal>
)}
<div className="md:sticky md:top-32 space-y-6">
{number && (
<Reveal delay={delay}>
<span className="block text-6xl md:text-8xl font-bold text-slate-100 leading-none select-none relative bg-white/0 backdrop-blur-[2px]">
{number}
</span>
</Reveal>
)}
{title && (
<Reveal delay={delay + 0.1}>
<div className="flex items-center gap-3">
<div className="h-px w-6 bg-slate-900"></div>
<h2 className="text-xs font-bold uppercase tracking-[0.3em] text-slate-900">
{title}
</h2>
</div>
</Reveal>
)}
{illustration && (
<Reveal delay={delay + 0.2}>
<div className="pt-12 opacity-100 group-hover:scale-110 transition-transform duration-700 origin-left">
{illustration}
</div>
</Reveal>
)}
</div>
</div>
{/* Main Content */}
<div className="md:col-span-9">
{children}
</div>
</div>
)}
{/* Main Content */}
<div className="md:col-span-9">
{children}
</div>
</div>
</div>
</section>
);

View File

@@ -0,0 +1,54 @@
import * as React from 'react';
interface TypographyProps {
children: React.ReactNode;
className?: string;
}
export const H1: React.FC<TypographyProps> = ({ children, className = "" }) => (
<h1 className={`text-5xl md:text-7xl font-bold text-slate-900 tracking-tighter leading-[1.1] ${className}`}>
{children}
</h1>
);
export const H2: React.FC<TypographyProps> = ({ children, className = "" }) => (
<h2 className={`text-3xl md:text-5xl font-bold text-slate-900 tracking-tighter leading-tight ${className}`}>
{children}
</h2>
);
export const H3: React.FC<TypographyProps> = ({ children, className = "" }) => (
<h3 className={`text-2xl md:text-4xl font-bold text-slate-900 tracking-tight leading-tight ${className}`}>
{children}
</h3>
);
export const H4: React.FC<TypographyProps> = ({ children, className = "" }) => (
<h4 className={`text-xl md:text-2xl font-bold text-slate-900 tracking-tight ${className}`}>
{children}
</h4>
);
export const LeadText: React.FC<TypographyProps> = ({ children, className = "" }) => (
<p className={`text-lg md:text-xl font-serif italic text-slate-500 leading-relaxed ${className}`}>
{children}
</p>
);
export const BodyText: React.FC<TypographyProps> = ({ children, className = "" }) => (
<p className={`text-base text-slate-500 leading-relaxed ${className}`}>
{children}
</p>
);
export const Label: React.FC<TypographyProps> = ({ children, className = "" }) => (
<span className={`text-[10px] font-bold uppercase tracking-[0.3em] text-slate-400 block ${className}`}>
{children}
</span>
);
export const MonoLabel: React.FC<TypographyProps> = ({ children, className = "" }) => (
<span className={`text-[10px] font-mono uppercase tracking-[0.2em] text-slate-400 block ${className}`}>
{children}
</span>
);

6
src/utils/cn.ts Normal file
View File

@@ -0,0 +1,6 @@
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}