chore: stabilize apps/web (lint, build, typecheck fixes)
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 6s
Build & Deploy / 🧪 QA (push) Failing after 1m27s
Build & Deploy / 🏗️ Build (push) Failing after 1m31s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s

This commit is contained in:
2026-02-11 11:56:13 +01:00
parent 8ba81809b0
commit ecea90dc91
50 changed files with 5596 additions and 3456 deletions

View File

@@ -1,16 +1,25 @@
'use client';
"use client";
import * as React from 'react';
import { motion } from 'framer-motion';
/* eslint-disable no-unused-vars */
import * as React from "react";
import { motion } from "framer-motion";
interface LineProps {
className?: string;
delay?: number;
}
export const HeroLines: React.FC<LineProps> = ({ className = "", delay = 0 }) => {
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">
<svg
className={`absolute pointer-events-none ${className}`}
viewBox="0 0 800 600"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<motion.path
d="M-100 300 C 100 300, 200 100, 400 100 C 600 100, 700 500, 900 500"
stroke="currentColor"
@@ -31,42 +40,70 @@ export const HeroLines: React.FC<LineProps> = ({ className = "", delay = 0 }) =>
animate={{ pathLength: 1, opacity: 1 }}
transition={{ duration: 2.5, delay: delay + 0.2, ease: "easeInOut" }}
/>
{/* Animated Pulses */}
<motion.circle r="3" fill="currentColor" className="text-slate-300">
<animateMotion
dur="6s"
repeatCount="indefinite"
path="M-100 300 C 100 300, 200 100, 400 100 C 600 100, 700 500, 900 500"
dur="6s"
repeatCount="indefinite"
path="M-100 300 C 100 300, 200 100, 400 100 C 600 100, 700 500, 900 500"
/>
</motion.circle>
<motion.circle r="3" fill="currentColor" className="text-slate-200">
<animateMotion
dur="8s"
repeatCount="indefinite"
path="M-100 350 C 100 350, 200 150, 400 150 C 600 150, 700 550, 900 550"
dur="8s"
repeatCount="indefinite"
path="M-100 350 C 100 350, 200 150, 400 150 C 600 150, 700 550, 900 550"
/>
</motion.circle>
{/* Nodes */}
<motion.circle cx="400" cy="100" r="4" className="fill-slate-200"
initial={{ scale: 0 }} animate={{ scale: 1 }} transition={{ delay: delay + 1, duration: 0.5 }} />
<motion.circle cx="400" cy="150" r="4" className="fill-slate-100"
initial={{ scale: 0 }} animate={{ scale: 1 }} transition={{ delay: delay + 1.2, duration: 0.5 }} />
<motion.circle
cx="400"
cy="100"
r="4"
className="fill-slate-200"
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ delay: delay + 1, duration: 0.5 }}
/>
<motion.circle
cx="400"
cy="150"
r="4"
className="fill-slate-100"
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ delay: delay + 1.2, duration: 0.5 }}
/>
</svg>
);
};
export const GridLines: React.FC<LineProps> = ({ className = "", delay = 0 }) => {
export const GridLines: React.FC<LineProps> = ({
className = "",
delay = 0,
}) => {
return (
<svg className={`absolute pointer-events-none ${className}`} viewBox="0 0 400 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
className={`absolute pointer-events-none ${className}`}
viewBox="0 0 400 400"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="currentColor" strokeWidth="0.5" className="text-slate-100" />
<path
d="M 40 0 L 0 0 0 40"
fill="none"
stroke="currentColor"
strokeWidth="0.5"
className="text-slate-100"
/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#grid)" />
{/* Highlighted Path */}
<motion.path
d="M 40 40 L 120 40 L 120 120 L 200 120"
@@ -80,22 +117,54 @@ export const GridLines: React.FC<LineProps> = ({ className = "", delay = 0 }) =>
viewport={{ once: true }}
transition={{ duration: 1.5, delay: delay }}
/>
{/* Active Cells */}
<motion.rect x="120" y="40" width="40" height="40" className="fill-slate-50"
initial={{ opacity: 0 }} animate={{ opacity: [0, 0.5, 0] }} transition={{ duration: 3, repeat: Infinity, repeatDelay: 2 }} />
<motion.rect x="160" y="80" width="40" height="40" className="fill-slate-50"
initial={{ opacity: 0 }} animate={{ opacity: [0, 0.5, 0] }} transition={{ duration: 4, repeat: Infinity, repeatDelay: 1 }} />
<motion.circle cx="200" cy="120" r="3" className="fill-slate-400"
initial={{ scale: 0 }} whileInView={{ scale: 1 }} viewport={{ once: true }} transition={{ delay: delay + 1.5 }} />
{/* Active Cells */}
<motion.rect
x="120"
y="40"
width="40"
height="40"
className="fill-slate-50"
initial={{ opacity: 0 }}
animate={{ opacity: [0, 0.5, 0] }}
transition={{ duration: 3, repeat: Infinity, repeatDelay: 2 }}
/>
<motion.rect
x="160"
y="80"
width="40"
height="40"
className="fill-slate-50"
initial={{ opacity: 0 }}
animate={{ opacity: [0, 0.5, 0] }}
transition={{ duration: 4, repeat: Infinity, repeatDelay: 1 }}
/>
<motion.circle
cx="200"
cy="120"
r="3"
className="fill-slate-400"
initial={{ scale: 0 }}
whileInView={{ scale: 1 }}
viewport={{ once: true }}
transition={{ delay: delay + 1.5 }}
/>
</svg>
);
};
export const FlowLines: React.FC<LineProps> = ({ className = "", delay = 0 }) => {
export const FlowLines: React.FC<LineProps> = ({
className = "",
delay = 0,
}) => {
return (
<svg className={`absolute pointer-events-none ${className}`} viewBox="0 0 600 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
className={`absolute pointer-events-none ${className}`}
viewBox="0 0 600 200"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<motion.path
d="M 0 100 H 100 C 150 100, 150 50, 200 50 H 300"
stroke="currentColor"
@@ -116,45 +185,122 @@ export const FlowLines: React.FC<LineProps> = ({ className = "", delay = 0 }) =>
viewport={{ once: true }}
transition={{ duration: 1.5, delay: delay + 0.2 }}
/>
{/* Pulse */}
<motion.circle r="2" fill="currentColor" className="text-slate-400">
<animateMotion
dur="4s"
repeatCount="indefinite"
path="M 0 100 H 100 C 150 100, 150 50, 200 50 H 300"
<animateMotion
dur="4s"
repeatCount="indefinite"
path="M 0 100 H 100 C 150 100, 150 50, 200 50 H 300"
/>
</motion.circle>
<motion.rect x="300" y="30" width="80" height="40" rx="8" className="stroke-slate-300 fill-white" strokeWidth="1"
initial={{ opacity: 0, x: 280 }} whileInView={{ opacity: 1, x: 300 }} viewport={{ once: true }} transition={{ delay: delay + 1 }} />
<motion.rect x="300" y="130" width="80" height="40" rx="8" className="stroke-slate-300 fill-white" strokeWidth="1"
initial={{ opacity: 0, x: 280 }} whileInView={{ opacity: 1, x: 300 }} viewport={{ once: true }} transition={{ delay: delay + 1.2 }} />
<motion.rect
x="300"
y="30"
width="80"
height="40"
rx="8"
className="stroke-slate-300 fill-white"
strokeWidth="1"
initial={{ opacity: 0, x: 280 }}
whileInView={{ opacity: 1, x: 300 }}
viewport={{ once: true }}
transition={{ delay: delay + 1 }}
/>
<motion.rect
x="300"
y="130"
width="80"
height="40"
rx="8"
className="stroke-slate-300 fill-white"
strokeWidth="1"
initial={{ opacity: 0, x: 280 }}
whileInView={{ opacity: 1, x: 300 }}
viewport={{ once: true }}
transition={{ delay: delay + 1.2 }}
/>
</svg>
);
};
export const CirclePattern: React.FC<LineProps> = ({ className = "", delay = 0 }) => {
return (
<svg className={`absolute pointer-events-none ${className}`} viewBox="0 0 400 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<motion.circle cx="200" cy="200" r="100" stroke="currentColor" strokeWidth="1" className="text-slate-100"
initial={{ scale: 0.8, opacity: 0 }} whileInView={{ scale: 1, opacity: 1 }} viewport={{ once: true }} transition={{ duration: 1, delay: delay }} />
<motion.circle cx="200" cy="200" r="150" stroke="currentColor" strokeWidth="1" className="text-slate-50"
initial={{ scale: 0.8, opacity: 0 }} whileInView={{ scale: 1, opacity: 1 }} viewport={{ once: true }} transition={{ duration: 1, delay: delay + 0.2 }} />
<motion.circle cx="200" cy="200" r="50" stroke="currentColor" strokeWidth="1" className="text-slate-200"
initial={{ scale: 0.8, opacity: 0 }} whileInView={{ scale: 1, opacity: 1 }} viewport={{ once: true }} transition={{ duration: 1, delay: delay + 0.4 }} />
{/* Rotating Ring */}
<motion.circle cx="200" cy="200" r="120" stroke="currentColor" strokeWidth="1" strokeDasharray="10 10" className="text-slate-200"
animate={{ rotate: 360 }} transition={{ duration: 20, repeat: Infinity, ease: "linear" }} />
</svg>
)
}
export const ServicesFlow: React.FC<LineProps> = ({ className = "", delay = 0 }) => {
export const CirclePattern: React.FC<LineProps> = ({
className = "",
delay = 0,
}) => {
return (
<svg className={`absolute pointer-events-none ${className}`} viewBox="0 0 1000 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
className={`absolute pointer-events-none ${className}`}
viewBox="0 0 400 400"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<motion.circle
cx="200"
cy="200"
r="100"
stroke="currentColor"
strokeWidth="1"
className="text-slate-100"
initial={{ scale: 0.8, opacity: 0 }}
whileInView={{ scale: 1, opacity: 1 }}
viewport={{ once: true }}
transition={{ duration: 1, delay: delay }}
/>
<motion.circle
cx="200"
cy="200"
r="150"
stroke="currentColor"
strokeWidth="1"
className="text-slate-50"
initial={{ scale: 0.8, opacity: 0 }}
whileInView={{ scale: 1, opacity: 1 }}
viewport={{ once: true }}
transition={{ duration: 1, delay: delay + 0.2 }}
/>
<motion.circle
cx="200"
cy="200"
r="50"
stroke="currentColor"
strokeWidth="1"
className="text-slate-200"
initial={{ scale: 0.8, opacity: 0 }}
whileInView={{ scale: 1, opacity: 1 }}
viewport={{ once: true }}
transition={{ duration: 1, delay: delay + 0.4 }}
/>
{/* Rotating Ring */}
<motion.circle
cx="200"
cy="200"
r="120"
stroke="currentColor"
strokeWidth="1"
strokeDasharray="10 10"
className="text-slate-200"
animate={{ rotate: 360 }}
transition={{ duration: 20, repeat: Infinity, ease: "linear" }}
/>
</svg>
);
};
export const ServicesFlow: React.FC<LineProps> = ({
className = "",
delay = 0,
}) => {
return (
<svg
className={`absolute pointer-events-none ${className}`}
viewBox="0 0 1000 200"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
{/* Path connecting the 3 steps */}
<motion.path
d="M 100 100 L 900 100"
@@ -167,55 +313,59 @@ export const ServicesFlow: React.FC<LineProps> = ({ className = "", delay = 0 })
viewport={{ once: true }}
transition={{ duration: 2, delay: delay }}
/>
{/* Animated pulse moving along the line */}
<motion.circle r="5" fill="currentColor" className="text-slate-900">
<animateMotion
dur="3s"
repeatCount="indefinite"
path="M 100 100 L 900 100"
dur="3s"
repeatCount="indefinite"
path="M 100 100 L 900 100"
/>
</motion.circle>
{/* Second pulse with delay */}
<motion.circle r="5" fill="currentColor" className="text-slate-900">
<animateMotion
dur="3s"
begin="1.5s"
repeatCount="indefinite"
path="M 100 100 L 900 100"
dur="3s"
begin="1.5s"
repeatCount="indefinite"
path="M 100 100 L 900 100"
/>
</motion.circle>
</svg>
);
};
export const ComparisonLines: 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"
strokeDasharray="4 4"
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>
</svg>
)
}
export const ComparisonLines: 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"
strokeDasharray="4 4"
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>
</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;
export const ConnectorStart: React.FC<LineProps> = (_props) => null;
export const ConnectorBranch: React.FC<LineProps> = (_props) => null;
export const ConnectorSplit: React.FC<LineProps> = (_props) => null;
export const ConnectorEnd: React.FC<LineProps> = (_props) => null;

View File

@@ -1,11 +1,21 @@
'use client';
"use client";
import * as React from 'react';
import { motion } from 'framer-motion';
import { IllustrationProps } from './types';
/* eslint-disable no-unused-vars */
export const ConceptAutomation: React.FC<IllustrationProps> = ({ className = "", delay = 0 }) => (
<svg className={className} viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
import * as React from "react";
import { motion } from "framer-motion";
import { IllustrationProps } from "./types";
export const ConceptAutomation: React.FC<IllustrationProps> = ({
className = "",
delay: _delay = 0,
}) => (
<svg
className={className}
viewBox="0 0 120 120"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<motion.g
animate={{ rotate: 360 }}
transition={{ duration: 10, repeat: Infinity, ease: "linear" }}
@@ -22,7 +32,9 @@ export const ConceptAutomation: React.FC<IllustrationProps> = ({ className = "",
</motion.g>
<motion.path
d="M 10 60 H 110"
stroke="currentColor" strokeWidth="1" className="text-slate-300"
stroke="currentColor"
strokeWidth="1"
className="text-slate-300"
strokeDasharray="4 4"
animate={{ strokeDashoffset: [0, -20] }}
transition={{ duration: 2, repeat: Infinity, ease: "linear" }}

View File

@@ -1,22 +1,37 @@
'use client';
"use client";
import * as React from 'react';
import { motion } from 'framer-motion';
import { IllustrationProps } from './types';
/* eslint-disable no-unused-vars */
export const ConceptMessy: React.FC<IllustrationProps> = ({ className = "", delay = 0 }) => (
<svg className={className} viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
import * as React from "react";
import { motion } from "framer-motion";
import { IllustrationProps } from "./types";
export const ConceptMessy: React.FC<IllustrationProps> = ({
className = "",
delay: _delay = 0,
}) => (
<svg
className={className}
viewBox="0 0 120 120"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<motion.path
d="M 20 60 C 30 20, 40 100, 50 60 C 60 20, 70 100, 80 60 C 90 20, 100 100, 110 60"
stroke="currentColor" strokeWidth="1" className="text-slate-500"
stroke="currentColor"
strokeWidth="1"
className="text-slate-500"
animate={{ strokeDashoffset: [0, 20] }}
strokeDasharray="4 4"
transition={{ duration: 5, repeat: Infinity, ease: "linear" }}
/>
<motion.path
d="M 20 40 L 100 80 M 20 80 L 100 40"
stroke="currentColor" strokeWidth="1" className="text-slate-200 opacity-50"
animate={{ opacity: [0.2, 0.5, 0.2] }} transition={{ duration: 3, repeat: Infinity }}
stroke="currentColor"
strokeWidth="1"
className="text-slate-200 opacity-50"
animate={{ opacity: [0.2, 0.5, 0.2] }}
transition={{ duration: 3, repeat: Infinity }}
/>
</svg>
);

View File

@@ -1,27 +1,50 @@
'use client';
"use client";
import * as React from 'react';
import { motion } from 'framer-motion';
import { IllustrationProps } from './types';
/* eslint-disable no-unused-vars */
export const ConceptSystem: React.FC<IllustrationProps> = ({ className = "", delay = 0 }) => (
<svg className={className} viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<motion.circle cx="60" cy="60" r="15" className="fill-slate-900"
animate={{ scale: [1, 1.1, 1] }} transition={{ duration: 4, repeat: Infinity }} />
import * as React from "react";
import { motion } from "framer-motion";
import { IllustrationProps } from "./types";
export const ConceptSystem: React.FC<IllustrationProps> = ({
className = "",
delay: _delay = 0,
}) => (
<svg
className={className}
viewBox="0 0 120 120"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<motion.circle
cx="60"
cy="60"
r="15"
className="fill-slate-900"
animate={{ scale: [1, 1.1, 1] }}
transition={{ duration: 4, repeat: Infinity }}
/>
{[0, 72, 144, 216, 288].map((angle, i) => {
const x = 60 + Math.cos((angle * Math.PI) / 180) * 40;
const y = 60 + Math.sin((angle * Math.PI) / 180) * 40;
return (
<React.Fragment key={i}>
<motion.line
x1="60" y1="60" x2={x} y2={y}
stroke="currentColor" strokeWidth="1" className="text-slate-400"
x1="60"
y1="60"
x2={x}
y2={y}
stroke="currentColor"
strokeWidth="1"
className="text-slate-400"
animate={{ strokeDashoffset: [0, 10] }}
strokeDasharray="2 2"
transition={{ duration: 2, repeat: Infinity, ease: "linear" }}
/>
<motion.circle
cx={x} cy={y} r="6"
cx={x}
cy={y}
r="6"
className="fill-white stroke-slate-300"
animate={{ scale: [1, 1.2, 1] }}
transition={{ duration: 3, repeat: Infinity, delay: i * 0.4 }}

View File

@@ -1,23 +1,43 @@
'use client';
"use client";
import * as React from 'react';
import { motion } from 'framer-motion';
import { IllustrationProps } from './types';
/* eslint-disable no-unused-vars */
export const ConceptTarget: React.FC<IllustrationProps> = ({ className = "", delay = 0 }) => (
<svg className={className} viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
import * as React from "react";
import { motion } from "framer-motion";
import { IllustrationProps } from "./types";
export const ConceptTarget: React.FC<IllustrationProps> = ({
className = "",
delay: _delay = 0,
}) => (
<svg
className={className}
viewBox="0 0 120 120"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<motion.circle
cx="60" cy="60" r="50"
stroke="currentColor" strokeWidth="1" className="text-slate-300"
cx="60"
cy="60"
r="50"
stroke="currentColor"
strokeWidth="1"
className="text-slate-300"
animate={{ scale: [1, 1.05, 1] }}
transition={{ duration: 4, repeat: Infinity }}
/>
<motion.circle
cx="60" cy="60" r="30"
stroke="currentColor" strokeWidth="1" className="text-slate-400"
cx="60"
cy="60"
r="30"
stroke="currentColor"
strokeWidth="1"
className="text-slate-400"
/>
<motion.circle
cx="60" cy="60" r="10"
cx="60"
cy="60"
r="10"
className="fill-slate-900"
animate={{ opacity: [0.5, 1, 0.5] }}
transition={{ duration: 2, repeat: Infinity }}

View File

@@ -1,14 +1,37 @@
'use client';
"use client";
import * as React from 'react';
import { motion } from 'framer-motion';
import { IllustrationProps } from './types';
/* eslint-disable no-unused-vars */
export const ConceptWebsite: React.FC<IllustrationProps> = ({ className = "", delay = 0 }) => (
<svg className={className} viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="20" width="100" height="80" rx="4" stroke="currentColor" strokeWidth="1" className="text-slate-400" />
import * as React from "react";
import { motion } from "framer-motion";
import { IllustrationProps } from "./types";
export const ConceptWebsite: React.FC<IllustrationProps> = ({
className = "",
delay: _delay = 0,
}) => (
<svg
className={className}
viewBox="0 0 120 120"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="10"
y="20"
width="100"
height="80"
rx="4"
stroke="currentColor"
strokeWidth="1"
className="text-slate-400"
/>
<motion.rect
x="20" y="35" width="80" height="15" rx="2"
x="20"
y="35"
width="80"
height="15"
rx="2"
className="fill-slate-200"
animate={{ opacity: [0.3, 0.6, 0.3] }}
transition={{ duration: 3, repeat: Infinity }}
@@ -17,8 +40,22 @@ export const ConceptWebsite: React.FC<IllustrationProps> = ({ className = "", de
animate={{ y: [0, 10, 0] }}
transition={{ duration: 4, repeat: Infinity }}
>
<rect x="20" y="55" width="35" height="35" rx="2" className="fill-slate-300" />
<rect x="65" y="55" width="35" height="35" rx="2" className="fill-slate-300" />
<rect
x="20"
y="55"
width="35"
height="35"
rx="2"
className="fill-slate-300"
/>
<rect
x="65"
y="55"
width="35"
height="35"
rx="2"
className="fill-slate-300"
/>
</motion.g>
</svg>
);

View File

@@ -1,29 +1,56 @@
'use client';
"use client";
import * as React from 'react';
import { motion } from 'framer-motion';
import { IllustrationProps } from './types';
/* eslint-disable no-unused-vars */
export const HeroArchitecture: React.FC<IllustrationProps> = ({ className = "", delay = 0 }) => (
<svg className={className} viewBox="0 0 400 300" fill="none" xmlns="http://www.w3.org/2000/svg">
<motion.rect x="170" y="120" width="60" height="60" rx="8" className="stroke-slate-900 fill-white" strokeWidth="2"
animate={{ scale: [1, 1.05, 1] }} transition={{ duration: 4, repeat: Infinity }} />
import * as React from "react";
import { motion } from "framer-motion";
import { IllustrationProps } from "./types";
export const HeroArchitecture: React.FC<IllustrationProps> = ({
className = "",
delay: _delay = 0,
}) => (
<svg
className={className}
viewBox="0 0 400 300"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<motion.rect
x="170"
y="120"
width="60"
height="60"
rx="8"
className="stroke-slate-900 fill-white"
strokeWidth="2"
animate={{ scale: [1, 1.05, 1] }}
transition={{ duration: 4, repeat: Infinity }}
/>
{[
{ x: 80, y: 60 }, { x: 320, y: 60 },
{ x: 80, y: 240 }, { x: 320, y: 240 }
{ x: 80, y: 60 },
{ x: 320, y: 60 },
{ x: 80, y: 240 },
{ x: 320, y: 240 },
].map((node, i) => (
<React.Fragment key={i}>
<motion.path
d={`M 200 150 L ${node.x} ${node.y}`}
stroke="currentColor" strokeWidth="1" className="text-slate-400"
animate={{ strokeDashoffset: [0, -10] }} strokeDasharray="4 4"
stroke="currentColor"
strokeWidth="1"
className="text-slate-400"
animate={{ strokeDashoffset: [0, -10] }}
strokeDasharray="4 4"
transition={{ duration: 2, repeat: Infinity, ease: "linear" }}
/>
<motion.circle
cx={node.x} cy={node.y} r="12"
cx={node.x}
cy={node.y}
r="12"
className="fill-white stroke-slate-300"
strokeWidth="1"
animate={{ scale: [1, 1.1, 1] }} transition={{ duration: 3, repeat: Infinity, delay: i * 0.5 }}
animate={{ scale: [1, 1.1, 1] }}
transition={{ duration: 3, repeat: Infinity, delay: i * 0.5 }}
/>
</React.Fragment>
))}

View File

@@ -1,18 +1,28 @@
'use client';
"use client";
import * as React from 'react';
import { motion } from 'framer-motion';
import { IllustrationProps } from './types';
/* eslint-disable no-unused-vars */
export const HeroMainIllustration: React.FC<IllustrationProps> = ({ className = "", delay = 0 }) => {
import * as React from "react";
import { motion } from "framer-motion";
import { IllustrationProps } from "./types";
export const HeroMainIllustration: React.FC<IllustrationProps> = ({
className = "",
delay: _delay = 0,
}) => {
return (
<svg className={className} viewBox="0 0 800 700" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
className={className}
viewBox="0 0 800 700"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
{/* Matrix-style Binary Rain Background */}
<g className="opacity-[0.08]">
{Array.from({ length: 20 }).map((_, col) => {
const colX = 20 + col * 40;
const speed = 8 + (col % 6);
const startDelay = (col % 5);
const startDelay = col % 5;
return (
<motion.g
key={`rain-col-${col}`}
@@ -33,7 +43,7 @@ export const HeroMainIllustration: React.FC<IllustrationProps> = ({ className =
className="fill-slate-900 font-mono"
style={{ fontSize: 12 }}
>
{(col + row) % 2 === 0 ? '1' : '0'}
{(col + row) % 2 === 0 ? "1" : "0"}
</text>
))}
</motion.g>
@@ -46,35 +56,141 @@ export const HeroMainIllustration: React.FC<IllustrationProps> = ({ className =
animate={{ y: [0, 8, 0] }}
transition={{ duration: 5, repeat: Infinity, ease: "easeInOut" }}
>
<rect x="150" y="500" width="500" height="30" rx="4" className="fill-slate-100 stroke-slate-300" strokeWidth="1" />
<rect x="170" y="510" width="460" height="10" rx="2" className="fill-slate-200" />
<rect
x="150"
y="500"
width="500"
height="30"
rx="4"
className="fill-slate-100 stroke-slate-300"
strokeWidth="1"
/>
<rect
x="170"
y="510"
width="460"
height="10"
rx="2"
className="fill-slate-200"
/>
{/* Binary on base */}
<text x="180" y="518" className="fill-slate-400 font-mono" style={{ fontSize: 8 }}>01010101010101010101010101010101010101</text>
<text
x="180"
y="518"
className="fill-slate-400 font-mono"
style={{ fontSize: 8 }}
>
01010101010101010101010101010101010101
</text>
</motion.g>
{/* Layer 2: Server/Database Layer */}
<motion.g
animate={{ y: [0, 6, 0] }}
transition={{ duration: 5, repeat: Infinity, ease: "easeInOut", delay: 0.3 }}
transition={{
duration: 5,
repeat: Infinity,
ease: "easeInOut",
delay: 0.3,
}}
>
{/* Left Server Block */}
<g transform="translate(200, 400)">
<rect x="0" y="0" width="120" height="80" rx="6" className="fill-white stroke-slate-900" strokeWidth="2" />
<rect x="10" y="10" width="100" height="15" rx="2" className="fill-slate-100" />
<rect x="10" y="30" width="80" height="10" rx="2" className="fill-slate-200" />
<rect x="10" y="45" width="60" height="10" rx="2" className="fill-slate-200" />
<rect
x="0"
y="0"
width="120"
height="80"
rx="6"
className="fill-white stroke-slate-900"
strokeWidth="2"
/>
<rect
x="10"
y="10"
width="100"
height="15"
rx="2"
className="fill-slate-100"
/>
<rect
x="10"
y="30"
width="80"
height="10"
rx="2"
className="fill-slate-200"
/>
<rect
x="10"
y="45"
width="60"
height="10"
rx="2"
className="fill-slate-200"
/>
<circle cx="100" cy="65" r="5" className="fill-slate-900" />
<text x="15" y="20" className="fill-slate-500 font-mono" style={{ fontSize: 8 }}>SERVER</text>
<text
x="15"
y="20"
className="fill-slate-500 font-mono"
style={{ fontSize: 8 }}
>
SERVER
</text>
</g>
{/* Right Database Block */}
<g transform="translate(480, 400)">
<rect x="0" y="0" width="120" height="80" rx="6" className="fill-white stroke-slate-900" strokeWidth="2" />
<rect x="10" y="10" width="100" height="15" rx="2" className="fill-slate-100" />
<rect x="10" y="30" width="100" height="8" rx="2" className="fill-slate-200" />
<rect x="10" y="42" width="100" height="8" rx="2" className="fill-slate-200" />
<rect x="10" y="54" width="100" height="8" rx="2" className="fill-slate-200" />
<text x="15" y="20" className="fill-slate-500 font-mono" style={{ fontSize: 8 }}>DATABASE</text>
<rect
x="0"
y="0"
width="120"
height="80"
rx="6"
className="fill-white stroke-slate-900"
strokeWidth="2"
/>
<rect
x="10"
y="10"
width="100"
height="15"
rx="2"
className="fill-slate-100"
/>
<rect
x="10"
y="30"
width="100"
height="8"
rx="2"
className="fill-slate-200"
/>
<rect
x="10"
y="42"
width="100"
height="8"
rx="2"
className="fill-slate-200"
/>
<rect
x="10"
y="54"
width="100"
height="8"
rx="2"
className="fill-slate-200"
/>
<text
x="15"
y="20"
className="fill-slate-500 font-mono"
style={{ fontSize: 8 }}
>
DATABASE
</text>
</g>
{/* Connection Lines */}
@@ -92,56 +208,227 @@ export const HeroMainIllustration: React.FC<IllustrationProps> = ({ className =
{/* Layer 3: Browser/Website */}
<motion.g
animate={{ y: [0, 4, 0] }}
transition={{ duration: 5, repeat: Infinity, ease: "easeInOut", delay: 0.6 }}
transition={{
duration: 5,
repeat: Infinity,
ease: "easeInOut",
delay: 0.6,
}}
>
{/* Browser Window */}
<rect x="180" y="100" width="440" height="280" rx="8" className="fill-white stroke-slate-900" strokeWidth="2" />
<rect
x="180"
y="100"
width="440"
height="280"
rx="8"
className="fill-white stroke-slate-900"
strokeWidth="2"
/>
{/* Browser Chrome */}
<rect x="180" y="100" width="440" height="30" rx="8" className="fill-slate-900" />
<rect x="180" y="120" width="440" height="10" className="fill-slate-900" />
<rect
x="180"
y="100"
width="440"
height="30"
rx="8"
className="fill-slate-900"
/>
<rect
x="180"
y="120"
width="440"
height="10"
className="fill-slate-900"
/>
{/* Browser Dots */}
<circle cx="200" cy="115" r="5" className="fill-slate-600" />
<circle cx="218" cy="115" r="5" className="fill-slate-600" />
<circle cx="236" cy="115" r="5" className="fill-slate-600" />
{/* Address Bar */}
<rect x="260" y="108" width="200" height="14" rx="3" className="fill-slate-700" />
<rect
x="260"
y="108"
width="200"
height="14"
rx="3"
className="fill-slate-700"
/>
{/* Website Content */}
<g transform="translate(200, 150)">
{/* Navigation */}
<rect x="0" y="0" width="400" height="20" className="fill-slate-50" />
<rect x="10" y="5" width="60" height="10" rx="2" className="fill-slate-900" />
<rect x="280" y="5" width="30" height="10" rx="2" className="fill-slate-300" />
<rect x="320" y="5" width="30" height="10" rx="2" className="fill-slate-300" />
<rect x="360" y="5" width="30" height="10" rx="2" className="fill-slate-300" />
<rect
x="10"
y="5"
width="60"
height="10"
rx="2"
className="fill-slate-900"
/>
<rect
x="280"
y="5"
width="30"
height="10"
rx="2"
className="fill-slate-300"
/>
<rect
x="320"
y="5"
width="30"
height="10"
rx="2"
className="fill-slate-300"
/>
<rect
x="360"
y="5"
width="30"
height="10"
rx="2"
className="fill-slate-300"
/>
{/* Hero Section */}
<rect x="0" y="30" width="400" height="100" className="fill-slate-100" />
<rect x="20" y="50" width="180" height="16" rx="2" className="fill-slate-900" />
<rect x="20" y="72" width="140" height="10" rx="2" className="fill-slate-400" />
<rect x="20" y="88" width="100" height="10" rx="2" className="fill-slate-400" />
<rect x="20" y="108" width="80" height="16" rx="4" className="fill-slate-900" />
<rect
x="0"
y="30"
width="400"
height="100"
className="fill-slate-100"
/>
<rect
x="20"
y="50"
width="180"
height="16"
rx="2"
className="fill-slate-900"
/>
<rect
x="20"
y="72"
width="140"
height="10"
rx="2"
className="fill-slate-400"
/>
<rect
x="20"
y="88"
width="100"
height="10"
rx="2"
className="fill-slate-400"
/>
<rect
x="20"
y="108"
width="80"
height="16"
rx="4"
className="fill-slate-900"
/>
{/* Hero Image Placeholder */}
<rect x="240" y="40" width="140" height="80" rx="4" className="fill-slate-200" />
<path d="M 280 80 L 310 60 L 340 80 L 310 100 Z" className="fill-slate-300" />
<rect
x="240"
y="40"
width="140"
height="80"
rx="4"
className="fill-slate-200"
/>
<path
d="M 280 80 L 310 60 L 340 80 L 310 100 Z"
className="fill-slate-300"
/>
{/* Cards Section */}
<g transform="translate(0, 140)">
<rect x="0" y="0" width="125" height="70" rx="4" className="fill-slate-50 stroke-slate-200" strokeWidth="1" />
<rect x="10" y="10" width="105" height="30" rx="2" className="fill-slate-200" />
<rect x="10" y="48" width="80" height="8" rx="2" className="fill-slate-300" />
<rect x="137" y="0" width="125" height="70" rx="4" className="fill-slate-50 stroke-slate-200" strokeWidth="1" />
<rect x="147" y="10" width="105" height="30" rx="2" className="fill-slate-200" />
<rect x="147" y="48" width="80" height="8" rx="2" className="fill-slate-300" />
<rect x="274" y="0" width="125" height="70" rx="4" className="fill-slate-50 stroke-slate-200" strokeWidth="1" />
<rect x="284" y="10" width="105" height="30" rx="2" className="fill-slate-200" />
<rect x="284" y="48" width="80" height="8" rx="2" className="fill-slate-300" />
<rect
x="0"
y="0"
width="125"
height="70"
rx="4"
className="fill-slate-50 stroke-slate-200"
strokeWidth="1"
/>
<rect
x="10"
y="10"
width="105"
height="30"
rx="2"
className="fill-slate-200"
/>
<rect
x="10"
y="48"
width="80"
height="8"
rx="2"
className="fill-slate-300"
/>
<rect
x="137"
y="0"
width="125"
height="70"
rx="4"
className="fill-slate-50 stroke-slate-200"
strokeWidth="1"
/>
<rect
x="147"
y="10"
width="105"
height="30"
rx="2"
className="fill-slate-200"
/>
<rect
x="147"
y="48"
width="80"
height="8"
rx="2"
className="fill-slate-300"
/>
<rect
x="274"
y="0"
width="125"
height="70"
rx="4"
className="fill-slate-50 stroke-slate-200"
strokeWidth="1"
/>
<rect
x="284"
y="10"
width="105"
height="30"
rx="2"
className="fill-slate-200"
/>
<rect
x="284"
y="48"
width="80"
height="8"
rx="2"
className="fill-slate-300"
/>
</g>
</g>
</motion.g>
@@ -176,7 +463,6 @@ export const HeroMainIllustration: React.FC<IllustrationProps> = ({ className =
transition={{ duration: 0.8, repeat: Infinity, ease: "linear" }}
/>
</motion.g>
</svg>
);
};

View File

@@ -1,6 +1,8 @@
'use client';
"use client";
import * as React from 'react';
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable no-unused-vars */
import * as React from "react";
export interface IllustrationProps {
className?: string;