fix: consent mode v2 compliance + visual enhancements across sections
Some checks failed
Build & Push / build-and-push (push) Failing after 52s
Some checks failed
Build & Push / build-and-push (push) Failing after 52s
Google Consent Mode v2: region-specific defaults (granted globally, denied for EEA/UK), update all 4 consent types on accept/decline, add url_passthrough and ads_data_redaction for better measurement. Visual: unified 48px grid texture across all light sections, animated constellation SVG in Process section, radial glow on Philosophy, removed broken SVG connector lines and unused imports. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -31,3 +31,6 @@ public/media/
|
|||||||
|
|
||||||
# superpowers
|
# superpowers
|
||||||
.superpowers/
|
.superpowers/
|
||||||
|
|
||||||
|
# private credentials
|
||||||
|
private/
|
||||||
|
|||||||
27
package-lock.json
generated
27
package-lock.json
generated
@@ -9,7 +9,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@calcom/embed-react": "^1.5.3",
|
"@calcom/embed-react": "^1.5.3",
|
||||||
"@google/genai": "^1.46.0",
|
"@google/genai": "^1.48.0",
|
||||||
"@payloadcms/db-postgres": "^3.80.0",
|
"@payloadcms/db-postgres": "^3.80.0",
|
||||||
"@payloadcms/next": "^3.80.0",
|
"@payloadcms/next": "^3.80.0",
|
||||||
"@payloadcms/richtext-lexical": "^3.80.0",
|
"@payloadcms/richtext-lexical": "^3.80.0",
|
||||||
@@ -1435,9 +1435,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@google/genai": {
|
"node_modules/@google/genai": {
|
||||||
"version": "1.46.0",
|
"version": "1.48.0",
|
||||||
"resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.46.0.tgz",
|
"resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.48.0.tgz",
|
||||||
"integrity": "sha512-ewPMN5JkKfgU5/kdco9ZhXBHDPhVqZpMQqIFQhwsHLf8kyZfx1cNpw1pHo1eV6PGEW7EhIBFi3aYZraFndAXqg==",
|
"integrity": "sha512-plonYK4ML2PrxsRD9SeqmFt76eREWkQdPCglOA6aYDzL1AAbE+7PUnT54SvpWGfws13L0AZEqGSpL7+1IPnTxQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"google-auth-library": "^10.3.0",
|
"google-auth-library": "^10.3.0",
|
||||||
@@ -3633,14 +3633,6 @@
|
|||||||
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
|
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/trusted-types": {
|
|
||||||
"version": "2.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
|
||||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"peer": true
|
|
||||||
},
|
|
||||||
"node_modules/@types/unist": {
|
"node_modules/@types/unist": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
||||||
@@ -6714,17 +6706,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/next-intl/node_modules/@swc/helpers": {
|
|
||||||
"version": "0.5.20",
|
|
||||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.20.tgz",
|
|
||||||
"integrity": "sha512-2egEBHUMasdypIzrprsu8g+OEVd7Vp2MM3a2eVlM/cyFYto0nGz5BX5BTgh/ShZZI9ed+ozEq+Ngt+rgmUs8tw==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"optional": true,
|
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/next/node_modules/postcss": {
|
"node_modules/next/node_modules/postcss": {
|
||||||
"version": "8.4.31",
|
"version": "8.4.31",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@calcom/embed-react": "^1.5.3",
|
"@calcom/embed-react": "^1.5.3",
|
||||||
"@google/genai": "^1.46.0",
|
"@google/genai": "^1.48.0",
|
||||||
"@payloadcms/db-postgres": "^3.80.0",
|
"@payloadcms/db-postgres": "^3.80.0",
|
||||||
"@payloadcms/next": "^3.80.0",
|
"@payloadcms/next": "^3.80.0",
|
||||||
"@payloadcms/richtext-lexical": "^3.80.0",
|
"@payloadcms/richtext-lexical": "^3.80.0",
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ function updateConsent(state: ConsentState) {
|
|||||||
if (typeof window !== 'undefined' && typeof window.gtag === 'function') {
|
if (typeof window !== 'undefined' && typeof window.gtag === 'function') {
|
||||||
window.gtag('consent', 'update', {
|
window.gtag('consent', 'update', {
|
||||||
analytics_storage: state,
|
analytics_storage: state,
|
||||||
|
ad_storage: state,
|
||||||
|
ad_user_data: state,
|
||||||
|
ad_personalization: state,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,18 +7,33 @@ export default function GoogleAnalytics() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Consent Mode v2 — default to denied for EEA compliance */}
|
{/* Consent Mode v2 — region-specific defaults */}
|
||||||
<Script id="gtag-consent" strategy="beforeInteractive">
|
<Script id="gtag-consent" strategy="beforeInteractive">
|
||||||
{`
|
{`
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
function gtag(){dataLayer.push(arguments);}
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
|
||||||
|
// Default: granted for regions without consent requirements
|
||||||
|
gtag('consent', 'default', {
|
||||||
|
analytics_storage: 'granted',
|
||||||
|
ad_storage: 'granted',
|
||||||
|
ad_user_data: 'granted',
|
||||||
|
ad_personalization: 'granted',
|
||||||
|
});
|
||||||
|
|
||||||
|
// EEA + UK: denied until user consents (GDPR)
|
||||||
gtag('consent', 'default', {
|
gtag('consent', 'default', {
|
||||||
analytics_storage: 'denied',
|
analytics_storage: 'denied',
|
||||||
ad_storage: 'denied',
|
ad_storage: 'denied',
|
||||||
ad_user_data: 'denied',
|
ad_user_data: 'denied',
|
||||||
ad_personalization: 'denied',
|
ad_personalization: 'denied',
|
||||||
wait_for_update: 500,
|
wait_for_update: 500,
|
||||||
|
region: ['AT','BE','BG','CY','CZ','DE','DK','EE','ES','FI','FR','GR','HR','HU','IE','IT','LT','LU','LV','MT','NL','PL','PT','RO','SE','SI','SK','IS','LI','NO','GB'],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Improve measurement when consent is denied
|
||||||
|
gtag('set', 'url_passthrough', true);
|
||||||
|
gtag('set', 'ads_data_redaction', true);
|
||||||
`}
|
`}
|
||||||
</Script>
|
</Script>
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,16 @@ export default function Configurator() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="configure" className="relative bg-surface py-24 overflow-hidden">
|
<section
|
||||||
|
id="configure"
|
||||||
|
className="relative bg-surface py-24 overflow-hidden"
|
||||||
|
style={{
|
||||||
|
backgroundImage: [
|
||||||
|
'repeating-linear-gradient(0deg, rgba(25,28,29,0.02) 0px, rgba(25,28,29,0.02) 1px, transparent 1px, transparent 48px)',
|
||||||
|
'repeating-linear-gradient(90deg, rgba(25,28,29,0.02) 0px, rgba(25,28,29,0.02) 1px, transparent 1px, transparent 48px)',
|
||||||
|
].join(', '),
|
||||||
|
}}
|
||||||
|
>
|
||||||
{/* Subtle diagonal accent line */}
|
{/* Subtle diagonal accent line */}
|
||||||
<div
|
<div
|
||||||
className="absolute top-0 left-0 right-0 h-px pointer-events-none"
|
className="absolute top-0 left-0 right-0 h-px pointer-events-none"
|
||||||
|
|||||||
@@ -57,7 +57,16 @@ export default function Discovery() {
|
|||||||
if (!voiceSupported) return null;
|
if (!voiceSupported) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="discover" className="relative bg-surface-high py-24 overflow-hidden">
|
<section
|
||||||
|
id="discover"
|
||||||
|
className="relative bg-surface-high py-24 overflow-hidden"
|
||||||
|
style={{
|
||||||
|
backgroundImage: [
|
||||||
|
'repeating-linear-gradient(0deg, rgba(25,28,29,0.02) 0px, rgba(25,28,29,0.02) 1px, transparent 1px, transparent 48px)',
|
||||||
|
'repeating-linear-gradient(90deg, rgba(25,28,29,0.02) 0px, rgba(25,28,29,0.02) 1px, transparent 1px, transparent 48px)',
|
||||||
|
].join(', '),
|
||||||
|
}}
|
||||||
|
>
|
||||||
{/* Top accent line */}
|
{/* Top accent line */}
|
||||||
<div
|
<div
|
||||||
className="absolute top-0 left-0 right-0 h-px pointer-events-none"
|
className="absolute top-0 left-0 right-0 h-px pointer-events-none"
|
||||||
|
|||||||
@@ -131,7 +131,24 @@ export default function Philosophy() {
|
|||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="about" className="bg-surface py-20">
|
<section
|
||||||
|
id="about"
|
||||||
|
className="relative bg-surface py-20 overflow-hidden"
|
||||||
|
style={{
|
||||||
|
backgroundImage: [
|
||||||
|
'repeating-linear-gradient(0deg, rgba(25,28,29,0.02) 0px, rgba(25,28,29,0.02) 1px, transparent 1px, transparent 48px)',
|
||||||
|
'repeating-linear-gradient(90deg, rgba(25,28,29,0.02) 0px, rgba(25,28,29,0.02) 1px, transparent 1px, transparent 48px)',
|
||||||
|
].join(', '),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Ambient radial glow */}
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 pointer-events-none"
|
||||||
|
style={{
|
||||||
|
background: 'radial-gradient(ellipse 80% 60% at 20% 50%, rgba(91,164,217,0.04) 0%, transparent 70%)',
|
||||||
|
}}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
<div className="container mx-auto px-6">
|
<div className="container mx-auto px-6">
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-16 items-start">
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-16 items-start">
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
import { motion, type Variants } from 'framer-motion';
|
import { motion, type Variants } from 'framer-motion';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import type { LucideIcon } from 'lucide-react';
|
|
||||||
import { Search, LayoutDashboard, PenTool, Rocket } from 'lucide-react';
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import {
|
import {
|
||||||
staggerContainerWide,
|
staggerContainerWide,
|
||||||
@@ -17,16 +15,15 @@ import SectionHeader from '@/components/ui/SectionHeader';
|
|||||||
interface Step {
|
interface Step {
|
||||||
numeral: string;
|
numeral: string;
|
||||||
key: 'discovery' | 'strategy' | 'build' | 'launch';
|
key: 'discovery' | 'strategy' | 'build' | 'launch';
|
||||||
Icon: LucideIcon;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Data ─────────────────────────────────────────────────────────────────────
|
// ─── Data ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const STEPS: Step[] = [
|
const STEPS: Step[] = [
|
||||||
{ numeral: '01', key: 'discovery', Icon: Search },
|
{ numeral: '01', key: 'discovery' },
|
||||||
{ numeral: '02', key: 'strategy', Icon: LayoutDashboard },
|
{ numeral: '02', key: 'strategy' },
|
||||||
{ numeral: '03', key: 'build', Icon: PenTool },
|
{ numeral: '03', key: 'build' },
|
||||||
{ numeral: '04', key: 'launch', Icon: Rocket },
|
{ numeral: '04', key: 'launch' },
|
||||||
];
|
];
|
||||||
|
|
||||||
// ─── Variants ─────────────────────────────────────────────────────────────────
|
// ─── Variants ─────────────────────────────────────────────────────────────────
|
||||||
@@ -44,9 +41,134 @@ const numeralScaleVariants: Variants = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ─── Animated blueprint network (desktop only) ───────────────────────────────
|
||||||
|
|
||||||
|
function FlowNetwork() {
|
||||||
|
// Radial constellation — center hub with curved spokes to outer nodes
|
||||||
|
const cx = 130, cy = 100; // center
|
||||||
|
const nodes = [
|
||||||
|
{ x: 50, y: 30 }, // top-left
|
||||||
|
{ x: 210, y: 25 }, // top-right
|
||||||
|
{ x: 240, y: 110 }, // right
|
||||||
|
{ x: 195, y: 180 }, // bottom-right
|
||||||
|
{ x: 55, y: 170 }, // bottom-left
|
||||||
|
{ x: 20, y: 95 }, // left
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="hidden lg:block relative mt-8 w-full h-52" aria-hidden="true">
|
||||||
|
<style>{`
|
||||||
|
@keyframes flow-dash { to { stroke-dashoffset: -40; } }
|
||||||
|
@keyframes flow-dash-slow { to { stroke-dashoffset: -40; } }
|
||||||
|
@keyframes node-pulse {
|
||||||
|
0%, 100% { opacity: 0.4; }
|
||||||
|
50% { opacity: 1; }
|
||||||
|
}
|
||||||
|
@keyframes orbit-a {
|
||||||
|
from { transform: rotate(0deg) translateX(28px) rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg) translateX(28px) rotate(-360deg); }
|
||||||
|
}
|
||||||
|
@keyframes orbit-b {
|
||||||
|
from { transform: rotate(120deg) translateX(22px) rotate(-120deg); }
|
||||||
|
to { transform: rotate(480deg) translateX(22px) rotate(-480deg); }
|
||||||
|
}
|
||||||
|
@keyframes orbit-c {
|
||||||
|
from { transform: rotate(240deg) translateX(35px) rotate(-240deg); }
|
||||||
|
to { transform: rotate(-120deg) translateX(35px) rotate(120deg); }
|
||||||
|
}
|
||||||
|
@keyframes ring-breathe {
|
||||||
|
0%, 100% { opacity: 0.06; }
|
||||||
|
50% { opacity: 0.14; }
|
||||||
|
}
|
||||||
|
.process-flow { animation: flow-dash 2.2s linear infinite; }
|
||||||
|
.process-flow-slow { animation: flow-dash-slow 3.5s linear infinite; }
|
||||||
|
.process-pulse { animation: node-pulse 2.8s ease-in-out infinite; }
|
||||||
|
.process-orbit-a { animation: orbit-a 8s linear infinite; }
|
||||||
|
.process-orbit-b { animation: orbit-b 11s linear infinite; }
|
||||||
|
.process-orbit-c { animation: orbit-c 14s linear infinite; }
|
||||||
|
.process-ring { animation: ring-breathe 4s ease-in-out infinite; }
|
||||||
|
.process-ring-lg { animation: ring-breathe 5.5s ease-in-out infinite; }
|
||||||
|
`}</style>
|
||||||
|
<svg className="w-full h-full" viewBox="0 0 260 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
{/* Breathing rings */}
|
||||||
|
<circle cx={cx} cy={cy} r="24" stroke="rgba(91,164,217,0.1)" strokeWidth="1" fill="none" className="process-ring" />
|
||||||
|
<circle cx={cx} cy={cy} r="42" stroke="rgba(91,164,217,0.07)" strokeWidth="1" fill="none" className="process-ring-lg" />
|
||||||
|
<circle cx={cx} cy={cy} r="65" stroke="rgba(91,164,217,0.04)" strokeWidth="0.8" fill="none" strokeDasharray="3 6" />
|
||||||
|
|
||||||
|
{/* Curved spokes from center to each outer node */}
|
||||||
|
{nodes.map((n, i) => {
|
||||||
|
const mx = cx + (n.x - cx) * 0.5 + (i % 2 === 0 ? 15 : -15);
|
||||||
|
const my = cy + (n.y - cy) * 0.5 + (i % 2 === 0 ? -12 : 12);
|
||||||
|
return (
|
||||||
|
<path
|
||||||
|
key={i}
|
||||||
|
d={`M ${cx} ${cy} Q ${mx} ${my} ${n.x} ${n.y}`}
|
||||||
|
stroke="rgba(91,164,217,0.18)"
|
||||||
|
strokeWidth="1.2"
|
||||||
|
strokeDasharray="5 4"
|
||||||
|
strokeLinecap="round"
|
||||||
|
className={i % 2 === 0 ? 'process-flow' : 'process-flow-slow'}
|
||||||
|
style={{ animationDelay: `${i * 0.3}s` }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
{/* Faint arcs connecting adjacent outer nodes */}
|
||||||
|
{nodes.map((n, i) => {
|
||||||
|
const next = nodes[(i + 1) % nodes.length];
|
||||||
|
const mx = cx + (n.x + next.x - 2 * cx) * 0.15;
|
||||||
|
const my = cy + (n.y + next.y - 2 * cy) * 0.15;
|
||||||
|
return (
|
||||||
|
<path
|
||||||
|
key={`arc-${i}`}
|
||||||
|
d={`M ${n.x} ${n.y} Q ${mx} ${my} ${next.x} ${next.y}`}
|
||||||
|
stroke="rgba(91,164,217,0.08)"
|
||||||
|
strokeWidth="0.8"
|
||||||
|
strokeDasharray="3 5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
{/* Outer nodes — pulsing */}
|
||||||
|
{nodes.map((n, i) => (
|
||||||
|
<circle
|
||||||
|
key={`node-${i}`}
|
||||||
|
cx={n.x}
|
||||||
|
cy={n.y}
|
||||||
|
r="3"
|
||||||
|
fill="#5BA4D9"
|
||||||
|
className="process-pulse"
|
||||||
|
style={{ animationDelay: `${i * 0.45}s` }}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Center hub */}
|
||||||
|
<circle cx={cx} cy={cy} r="5" fill="#5BA4D9" opacity="0.85" />
|
||||||
|
<circle cx={cx} cy={cy} r="2" fill="#fff" opacity="0.9" />
|
||||||
|
|
||||||
|
{/* Orbiting particles */}
|
||||||
|
<g style={{ transformOrigin: `${cx}px ${cy}px` }}>
|
||||||
|
<circle cx={cx} cy={cy} r="2" fill="#5BA4D9" opacity="0.7" className="process-orbit-a" />
|
||||||
|
</g>
|
||||||
|
<g style={{ transformOrigin: `${cx}px ${cy}px` }}>
|
||||||
|
<circle cx={cx} cy={cy} r="1.5" fill="rgba(91,164,217,0.5)" className="process-orbit-b" />
|
||||||
|
</g>
|
||||||
|
<g style={{ transformOrigin: `${cx}px ${cy}px` }}>
|
||||||
|
<circle cx={cx} cy={cy} r="1.5" fill="rgba(91,164,217,0.35)" className="process-orbit-c" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
{/* Corner brackets — architectural detail */}
|
||||||
|
<path d="M 14 25 L 14 20 L 19 20" stroke="rgba(28,43,58,0.12)" strokeWidth="0.8" fill="none" />
|
||||||
|
<path d="M 246 175 L 246 180 L 241 180" stroke="rgba(28,43,58,0.12)" strokeWidth="0.8" fill="none" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Sub-components ───────────────────────────────────────────────────────────
|
// ─── Sub-components ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function StepCard({ numeral, stepKey, Icon }: { numeral: string; stepKey: string; Icon: LucideIcon }) {
|
function StepCard({ numeral, stepKey }: { numeral: string; stepKey: string }) {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const title = t(`process.steps.${stepKey}.title`);
|
const title = t(`process.steps.${stepKey}.title`);
|
||||||
const description = t(`process.steps.${stepKey}.description`);
|
const description = t(`process.steps.${stepKey}.description`);
|
||||||
@@ -94,7 +216,16 @@ export default function Process() {
|
|||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="process" className="bg-surface-low py-20">
|
<section
|
||||||
|
id="process"
|
||||||
|
className="relative bg-surface py-20"
|
||||||
|
style={{
|
||||||
|
backgroundImage: [
|
||||||
|
'repeating-linear-gradient(0deg, rgba(25,28,29,0.02) 0px, rgba(25,28,29,0.02) 1px, transparent 1px, transparent 48px)',
|
||||||
|
'repeating-linear-gradient(90deg, rgba(25,28,29,0.02) 0px, rgba(25,28,29,0.02) 1px, transparent 1px, transparent 48px)',
|
||||||
|
].join(', '),
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className="container mx-auto px-6">
|
<div className="container mx-auto px-6">
|
||||||
|
|
||||||
{/*
|
{/*
|
||||||
@@ -121,47 +252,11 @@ export default function Process() {
|
|||||||
align="left"
|
align="left"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<FlowNetwork />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Steps column ── */}
|
{/* ── Steps column ── */}
|
||||||
<div className="lg:col-span-3">
|
<div className="lg:col-span-3">
|
||||||
<div className="relative">
|
|
||||||
{/* Dashed connector line — visible on sm+ grid layouts only */}
|
|
||||||
<div
|
|
||||||
className="hidden sm:block absolute inset-0 pointer-events-none"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="w-full h-full"
|
|
||||||
preserveAspectRatio="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
{/* Horizontal dashes across the middle gap */}
|
|
||||||
<line
|
|
||||||
x1="50%" y1="50%"
|
|
||||||
x2="50%" y2="50%"
|
|
||||||
stroke="rgba(91,164,217,0.18)"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeDasharray="4 6"
|
|
||||||
/>
|
|
||||||
{/* Vertical dashes down the centre gap */}
|
|
||||||
<line
|
|
||||||
x1="0%" y1="50%"
|
|
||||||
x2="100%" y2="50%"
|
|
||||||
stroke="rgba(91,164,217,0.18)"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeDasharray="4 6"
|
|
||||||
/>
|
|
||||||
<line
|
|
||||||
x1="50%" y1="0%"
|
|
||||||
x2="50%" y2="100%"
|
|
||||||
stroke="rgba(91,164,217,0.18)"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeDasharray="4 6"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
variants={staggerContainerWide}
|
variants={staggerContainerWide}
|
||||||
initial="hidden"
|
initial="hidden"
|
||||||
@@ -170,11 +265,10 @@ export default function Process() {
|
|||||||
className="grid grid-cols-1 sm:grid-cols-2 gap-5"
|
className="grid grid-cols-1 sm:grid-cols-2 gap-5"
|
||||||
>
|
>
|
||||||
{STEPS.map((step) => (
|
{STEPS.map((step) => (
|
||||||
<StepCard key={step.key} numeral={step.numeral} stepKey={step.key} Icon={step.Icon} />
|
<StepCard key={step.key} numeral={step.numeral} stepKey={step.key} />
|
||||||
))}
|
))}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -286,7 +286,16 @@ export default function SelectedWorks() {
|
|||||||
const secondaryProjects = PROJECTS.filter((p) => !p.featured);
|
const secondaryProjects = PROJECTS.filter((p) => !p.featured);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="work" className="bg-surface-low py-20">
|
<section
|
||||||
|
id="work"
|
||||||
|
className="bg-surface-low py-20"
|
||||||
|
style={{
|
||||||
|
backgroundImage: [
|
||||||
|
'repeating-linear-gradient(0deg, rgba(25,28,29,0.02) 0px, rgba(25,28,29,0.02) 1px, transparent 1px, transparent 48px)',
|
||||||
|
'repeating-linear-gradient(90deg, rgba(25,28,29,0.02) 0px, rgba(25,28,29,0.02) 1px, transparent 1px, transparent 48px)',
|
||||||
|
].join(', '),
|
||||||
|
}}
|
||||||
|
>
|
||||||
<style>{`
|
<style>{`
|
||||||
@keyframes dashed-drift {
|
@keyframes dashed-drift {
|
||||||
0% { background-position: 0 0, 100% 0, 100% 100%, 0 100%; }
|
0% { background-position: 0 0, 100% 0, 100% 100%, 0 100%; }
|
||||||
|
|||||||
@@ -112,6 +112,12 @@ export default function TrustBar() {
|
|||||||
<section
|
<section
|
||||||
aria-label="Trust indicators"
|
aria-label="Trust indicators"
|
||||||
className="relative bg-surface-low py-12"
|
className="relative bg-surface-low py-12"
|
||||||
|
style={{
|
||||||
|
backgroundImage: [
|
||||||
|
'repeating-linear-gradient(0deg, rgba(25,28,29,0.02) 0px, rgba(25,28,29,0.02) 1px, transparent 1px, transparent 48px)',
|
||||||
|
'repeating-linear-gradient(90deg, rgba(25,28,29,0.02) 0px, rgba(25,28,29,0.02) 1px, transparent 1px, transparent 48px)',
|
||||||
|
].join(', '),
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{/* Gradient bridge — blends hero into this section */}
|
{/* Gradient bridge — blends hero into this section */}
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user