1736 lines
71 KiB
TypeScript
1736 lines
71 KiB
TypeScript
'use client'
|
||
import { motion, AnimatePresence, useScroll, useTransform, useMotionTemplate, useTime, MotionValue, useSpring } from "framer-motion";
|
||
|
||
import { useState, useCallback, useEffect } from "react"
|
||
import { Palette, Sparkles, Gauge, FlaskConical, TrendingUp, Users, Brain, CheckCircle2, Zap, CircleDot, XCircle } from "lucide-react";
|
||
// na górze pliku (masz już useEffect), dodaj:
|
||
import { createPortal } from "react-dom";
|
||
|
||
|
||
|
||
export default function Home() {
|
||
|
||
const [openIndex, setOpenIndex] = useState<number | null>(null)
|
||
|
||
const [activeSection, setActiveSection] = useState<string>("PrimeCode");
|
||
|
||
const [uiVariant, setUiVariant] = useState<"A" | "B" | "C">("A");
|
||
// A = Vibrant, B = Editorial, C = Dark
|
||
|
||
|
||
useEffect(() => {
|
||
const sections = document.querySelectorAll("section[id]");
|
||
const observer = new IntersectionObserver(
|
||
(entries) => {
|
||
entries.forEach((entry) => {
|
||
if (entry.isIntersecting) {
|
||
setActiveSection(entry.target.id);
|
||
}
|
||
});
|
||
},
|
||
{ rootMargin: "-50% 0px -50% 0px", threshold: 0 }
|
||
);
|
||
|
||
sections.forEach((section) => observer.observe(section));
|
||
return () => observer.disconnect();
|
||
}, []);
|
||
|
||
const navItems = [
|
||
{ id: "PrimeCode", label: "O nas" },
|
||
{ id: "Sytuacje", label: "Sytuacje" },
|
||
{ id: "Partner", label: "Potrzebujesz partnera" },
|
||
{ id: "Model", label: "Model Prime Code" },
|
||
{ id: "CoPotrafimy", label: "Co potrafimy?" },
|
||
{ id: "JakDzialamy", label: "Jak działamy?" },
|
||
{ id: "PomogliśmyCaseStudies", label: "Pomogliśmy" },
|
||
];
|
||
|
||
const caseStudies = [
|
||
{
|
||
title: "Firma kosmetyczna",
|
||
short: "Nowa strategia detaliczna i transformacja doświadczenia klientek w salonach.",
|
||
details: [
|
||
"Opracowanie nowej strategii detalicznej, integrującej sprzedaż z usługami.",
|
||
"Zaprojektowanie atmosfery sklepów oraz selekcja unikalnego asortymentu.",
|
||
"Transformacja komunikacji – od eksperckiej wiedzy do relacji opartej na empatii.",
|
||
"Redefinicja propozycji wartości – skupienie na doświadczeniu troski o zdrowie i piękno.",
|
||
"Stworzenie concept store’u jako pełnego doświadczenia marki.",
|
||
"Przejście z jednego formatu sklepu na trzy lepiej dopasowane formaty detaliczne.",
|
||
"Zmiana podejścia operacyjnego: od 4P do 7P, z naciskiem na doświadczenie klientki."
|
||
]
|
||
},
|
||
{
|
||
title: "Firma paliwowa",
|
||
short: "Integracja doświadczenia na stacjach z tożsamością marki i branding sensoryczny.",
|
||
details: [
|
||
"Integracja strategii marki ze strategią doświadczeń klienta.",
|
||
"Badania neuromarketingowe i ponad 100 rekomendacji optymalizacyjnych.",
|
||
"Wielowarstwowa propozycja wartości – od tankowania po rozbudowane doświadczenie marki.",
|
||
"Identyfikacja momentów prawdy i plan zarządzania nimi.",
|
||
"Mapa ścieżek klienta z instrukcją optymalizacji.",
|
||
"Projekt atmosfery stacji (kolorystyka, zapach, układ).",
|
||
"Kompleksowy branding sensoryczny angażujący wszystkie zmysły.",
|
||
"Standardy obsługi klienta odzwierciedlające wartości marki.",
|
||
"Mapa wdrożenia z harmonogramem i wytycznymi operacyjnymi."
|
||
]
|
||
},
|
||
{
|
||
title: "Firma technologiczna",
|
||
short: "Nowa architektura marki, system kompetencji i kultura klientocentryczna.",
|
||
details: [
|
||
"Zaprojektowanie struktury organizacyjnej i systemu kompetencji.",
|
||
"Redefinicja architektury marki (przejście z house of brands na branded house).",
|
||
"System zarządzania wartością klienta – poziom strategiczny, operacyjny i personalny.",
|
||
"„Playbooki” wdrażające pozycjonowanie do działań codziennych.",
|
||
"Usługi o wysokiej wartości dodanej, zmieniające postrzeganie firmy.",
|
||
"Model „high tech + high touch” łączący technologie z uważnością na klienta.",
|
||
"Strategia CRM wspierająca długofalowe relacje i wzrost."
|
||
]
|
||
}
|
||
]
|
||
|
||
// --- Wspólna treść dla wszystkich wariantów sekcji "DlaczegoIntegracji" ---
|
||
const why = {
|
||
p1: (
|
||
<>
|
||
<strong className="text-cyan-800">76% konsumentów oczekuje</strong>, że marka będzie
|
||
<strong className="text-cyan-800"> przewidywać ich potrzeby</strong>, nie tylko reagować
|
||
<span className="text-gray-500"> (Salesforce, 2023)</span>.
|
||
</>
|
||
),
|
||
p2: (
|
||
<>
|
||
Pokolenie Z <strong className="text-cyan-800">nie buduje lojalności przez reklamy</strong>.
|
||
Buduje ją przez <strong className="text-cyan-800">wartość, użyteczność i autentyczność</strong>.
|
||
<span className="font-semibold text-gray-800"> Aż 65% młodych konsumentów</span> deklaruje,
|
||
że lojalność wobec marki zależy od spójnych i realnych doświadczeń
|
||
<span className="text-gray-500"> (McKinsey, 2023)</span>.
|
||
</>
|
||
),
|
||
bulletsIntro: "Marka nie żyje w kampaniach. Żyje w:",
|
||
bullets: [
|
||
"chatbotach, landingach, mailach i pushach,",
|
||
"procesach sprzedażowych i onboardingowych,",
|
||
"sposobie, w jaki działa Twój model cenowy, UX i obsługa klienta.",
|
||
],
|
||
p3: (
|
||
<>
|
||
Jeśli to wszystko nie gra razem, marka <strong className="text-cyan-800">traci moc</strong>.
|
||
<br />
|
||
<strong className="text-cyan-800">I traci klientów.</strong>
|
||
<span className="text-gray-500"> Według badania PwC aż 32% klientów</span> porzuca markę po jednej
|
||
złej interakcji, nawet jeśli wcześniej byli zadowoleni.
|
||
</>
|
||
),
|
||
};
|
||
|
||
|
||
const [expandedIndex, setExpandedIndex] = useState<number | null>(null);
|
||
|
||
// === NOWE tiles (podmień całą dotychczasową definicję) ===
|
||
// === KAFELKI z jednolitym punktorowaniem ===
|
||
const tiles = [
|
||
{
|
||
title: "Brand Daily Scan™ – diagnoza w rytmie codzienności",
|
||
short: "Błyskawiczna analiza punktów styku, decyzji, procesów i sygnałów marki.",
|
||
output: "Mapa luk, insighty, benchmarki, rekomendacje.",
|
||
efekt:
|
||
"Mapa luk i niespójności, insighty z rzeczywistego użycia marki, benchmarki branżowe oraz rekomendacje naprawcze i wzmacniające.",
|
||
details: (
|
||
<>
|
||
<p >
|
||
Nie patrzymy na markę z dystansu. Wchodzimy w jej codzienne tętno.
|
||
Skupiamy się na tym, co naprawdę <strong>generuje doświadczenie klienta</strong>:
|
||
</p>
|
||
|
||
{/* stałe, eleganckie kropki */}
|
||
<ul className="mt-1 space-y-1">
|
||
<li className="relative pl-4 before:content-[''] before:absolute before:left-0 before:top-[0.55rem] before:w-1.5 before:h-1.5 before:rounded-full before:bg-cyan-600">
|
||
punkty styku (touchpointy),
|
||
</li>
|
||
<li className="relative pl-4 before:content-[''] before:absolute before:left-0 before:top-[0.55rem] before:w-1.5 before:h-1.5 before:rounded-full before:bg-cyan-600">
|
||
mikrodecyzje zespołów,
|
||
</li>
|
||
<li className="relative pl-4 before:content-[''] before:absolute before:left-0 before:top-[0.55rem] before:w-1.5 before:h-1.5 before:rounded-full before:bg-cyan-600">
|
||
automatyczne procesy i sygnały marki.
|
||
</li>
|
||
</ul>
|
||
</>
|
||
),
|
||
},
|
||
{
|
||
title: "Synchronizacja rytmu – faza pacingu",
|
||
short: "Zgrywamy Twoje działania z logiką marki. Automatyzujemy. Personalizujemy.",
|
||
output: "Roadmapa wdrożenia z KPI i zestawem eksperymentów.",
|
||
efekt:
|
||
"„Zgrane zegary” marki, marketingu, sprzedaży i obsługi; klarowne KPI oraz szybkie testy hipotez.",
|
||
details: (
|
||
<>
|
||
<p>
|
||
Integrujemy markę z codzienną dynamiką operacyjną. Nie robimy rewolucji —
|
||
<strong> synchronizujemy rytm</strong>:
|
||
</p>
|
||
|
||
{/* stałe, eleganckie kropki */}
|
||
<ul className="mt-1 space-y-1">
|
||
<li className="relative pl-4 before:content-[''] before:absolute before:left-0 before:top-[0.55rem] before:w-1.5 before:h-1.5 before:rounded-full before:bg-cyan-600">
|
||
automatyzujemy to, co można,
|
||
</li>
|
||
<li className="relative pl-4 before:content-[''] before:absolute before:left-0 before:top-[0.55rem] before:w-1.5 before:h-1.5 before:rounded-full before:bg-cyan-600">
|
||
personalizujemy to, co warto,
|
||
</li>
|
||
<li className="relative pl-4 before:content-[''] before:absolute before:left-0 before:top-[0.55rem] before:w-1.5 before:h-1.5 before:rounded-full before:bg-cyan-600">
|
||
urealniamy to, co obiecujesz jako marka.
|
||
</li>
|
||
</ul>
|
||
</>
|
||
),
|
||
},
|
||
{
|
||
title: "Wdrożenie i skalowanie",
|
||
short: "Marka w akcji: od call center i e-maili po pricing, CRM i fizyczną dostępność.",
|
||
output: "Efektywność, zaangażowanie, lojalność.",
|
||
efekt:
|
||
"Wzrost efektywności działań marketingowych, wyższe zaangażowanie użytkowników, zwiększenie lojalności i retencji — zmierzone.",
|
||
details: (
|
||
<>
|
||
<p >
|
||
Marka przestaje być tylko deklaracją. Zaczyna
|
||
<strong> działać w realnym czasie i miejscu</strong>:
|
||
</p>
|
||
|
||
{/* stałe, eleganckie kropki */}
|
||
<ul className="mt-1 space-y-1">
|
||
<li className="relative pl-4 before:content-[''] before:absolute before:left-0 before:top-[0.55rem] before:w-1.5 before:h-1.5 before:rounded-full before:bg-cyan-600">
|
||
w aplikacji i e-mailach,
|
||
</li>
|
||
<li className="relative pl-4 before:content-[''] before:absolute before:left-0 before:top-[0.55rem] before:w-1.5 before:h-1.5 before:rounded-full before:bg-cyan-600">
|
||
w polityce cenowej i mechanizmach CRM,
|
||
</li>
|
||
<li className="relative pl-4 before:content-[''] before:absolute before:left-0 before:top-[0.55rem] before:w-1.5 before:h-1.5 before:rounded-full before:bg-cyan-600">
|
||
w sklepie internetowym i na półce w punkcie sprzedaży.
|
||
</li>
|
||
</ul>
|
||
</>
|
||
),
|
||
},
|
||
];
|
||
|
||
|
||
const scrollToIntegration = useCallback(() => {
|
||
const section = document.getElementById('DlaczegoIntegracji')
|
||
section?.scrollIntoView({ behavior: 'smooth' })
|
||
}, [])
|
||
|
||
return (
|
||
|
||
|
||
|
||
<div className="relative isolate min-h-screen text-gray-900 overflow-hidden">
|
||
|
||
<Background variant={uiVariant} />
|
||
|
||
|
||
{/* Sticky Navbar */}
|
||
<nav className="fixed top-0 left-0 w-full z-50 bg-white/70 backdrop-blur-md shadow-sm border-b border-cyan-100">
|
||
<div className="max-w-7xl mx-auto px-4 flex justify-between items-center py-4">
|
||
|
||
<a href="#PrimeCode" className="flex items-center gap-3">
|
||
<img src="/LogoPrimeCode-transparent.png" alt="Prime Code" className="h-8 w-auto" />
|
||
|
||
<span className="sr-only">Prime Code — O nas</span>
|
||
</a>
|
||
|
||
<div className="md:hidden">
|
||
<input type="checkbox" id="menu-toggle" className="peer hidden" />
|
||
<label htmlFor="menu-toggle" className="cursor-pointer">
|
||
<svg className="h-8 w-8 text-gray-700" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" d="M4 6h16M4 12h16M4 18h16" />
|
||
</svg>
|
||
</label>
|
||
<ul className="hidden peer-checked:flex absolute top-full left-0 w-full flex-col bg-white shadow-lg">
|
||
{navItems.map((item) => (
|
||
<li key={item.id}>
|
||
<a
|
||
href={`#${item.id}`}
|
||
className={`block px-4 py-2 transition hover:text-cyan-700 ${activeSection === item.id ? "text-cyan-700 border-l-4 border-cyan-700" : ""
|
||
}`}
|
||
>
|
||
{item.label}
|
||
</a>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
<ul className="hidden md:flex gap-8 text-gray-700 font-semibold">
|
||
{navItems.map((item) => (
|
||
<li key={item.id}>
|
||
<a
|
||
href={`#${item.id}`}
|
||
className={`transition hover:text-cyan-700 ${activeSection === item.id ? "text-cyan-700 border-b-2 border-cyan-700 pb-1" : ""
|
||
}`}
|
||
>
|
||
{item.label}
|
||
</a>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
</nav>
|
||
|
||
|
||
|
||
|
||
{/* HERO - Nowoczesna, elegancka sekcja wprowadzająca */}
|
||
<section
|
||
id="PrimeCode"
|
||
className="relative flex flex-col justify-center items-center min-h-screen px-6 py-16 md:py-24 text-center"
|
||
>
|
||
|
||
|
||
{/* Dynamiczne tło
|
||
<div className="absolute inset-0">
|
||
<div className="absolute -top-40 -left-40 w-96 h-96 bg-cyan-300 opacity-30 rounded-full blur-3xl animate-blob"></div>
|
||
<div className="absolute -bottom-40 -right-40 w-[500px] h-[500px] bg-blue-300 opacity-30 rounded-full blur-3xl animate-blob animation-delay-2000"></div>
|
||
</div> */}
|
||
|
||
{/* Główna treść */}
|
||
<motion.h1
|
||
initial={{ opacity: 0, y: 60 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
transition={{ duration: 1 }}
|
||
className=" bottom-10 z-10 text-7xl md:text-8xl lg:text-9xl font-extrabold text-cyan-900 drop-shadow-xl"
|
||
>
|
||
Prime Code
|
||
</motion.h1>
|
||
|
||
<motion.h2
|
||
initial={{ opacity: 0, y: 40 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
transition={{ delay: 0.3, duration: 0.8 }}
|
||
className="mt-2 z-10 text-3xl md:text-4xl font-semibold text-cyan-700"
|
||
>
|
||
Integrujemy markę w codziennych działaniach
|
||
</motion.h2>
|
||
|
||
{/* Tagline */}
|
||
<motion.p
|
||
initial={{ opacity: 0, y: 20 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
transition={{ delay: 0.5, duration: 0.8 }}
|
||
className="mt-4 z-10 text-xl md:text-2xl text-cyan-600"
|
||
>
|
||
Marka, która działa codziennie. W czasie rzeczywistym. W każdym punkcie styku.
|
||
</motion.p>
|
||
|
||
{/* Dekoracyjna linia */}
|
||
<motion.div
|
||
initial={{ scaleX: 0 }}
|
||
animate={{ scaleX: 1 }}
|
||
transition={{ delay: 0.8, duration: 0.6 }}
|
||
className="mt-6 mb-6 w-32 h-1.5 rounded-full bg-gradient-to-r from-cyan-400 to-blue-500 origin-left z-10"
|
||
/>
|
||
|
||
<motion.p
|
||
initial={{ opacity: 0 }}
|
||
animate={{ opacity: 1 }}
|
||
transition={{ delay: 1.0, duration: 0.8 }}
|
||
className="z-10 max-w-3xl text-lg md:text-xl text-gray-700 leading-relaxed"
|
||
>
|
||
W Prime Code integrujemy markę z tym, co najważniejsze – realnymi działaniami, danymi,
|
||
technologią i zespołami. Nie tworzymy kampanii dla samej widoczności. Projektujemy
|
||
<strong className="font-semibold"> mechanizmy wzrostu</strong>, w których marka staje się fundamentem
|
||
operacyjnym: w komunikacji, sprzedaży, doświadczeniu klienta, polityce cenowej i kanałach dystrybucji.
|
||
</motion.p>
|
||
|
||
<motion.p
|
||
initial={{ opacity: 0 }}
|
||
animate={{ opacity: 1 }}
|
||
transition={{ delay: 1.2, duration: 0.8 }}
|
||
className="mt-4 z-10 max-w-3xl text-lg md:text-xl text-gray-700 leading-relaxed"
|
||
>
|
||
Bo marka <strong className="font-semibold">to nie tylko opowieść.</strong> To system. Aktywny,
|
||
skalowalny, zsynchronizowany z organizacją.
|
||
</motion.p>
|
||
|
||
|
||
|
||
{/* Strzałka zachęcająca do przewinięcia */}
|
||
<motion.div
|
||
onClick={scrollToIntegration}
|
||
animate={{ y: [0, 15, 0] }}
|
||
transition={{ repeat: Infinity, duration: 2, ease: 'easeInOut' }}
|
||
className="absolute bottom-8 z-10 text-cyan-700 cursor-pointer"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
className="h-10 w-10"
|
||
fill="none"
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
>
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||
</svg>
|
||
|
||
</motion.div>
|
||
|
||
</section>
|
||
|
||
|
||
|
||
|
||
|
||
{/* Dlaczego integracja marki to konieczność */}
|
||
{uiVariant === "C" ? (
|
||
// 👉 wariant C (timeline) w całości
|
||
<DlaczegoIntegracji_Timeline why={why} />
|
||
) : (
|
||
// 👉 warianty A i B – zostają bez zmian stylistycznych
|
||
<section
|
||
id="DlaczegoIntegracji"
|
||
className="relative flex flex-col justify-center items-center min-h-screen py-20 overflow-hidden"
|
||
>
|
||
<div className="relative max-w-5xl mx-auto px-6">
|
||
{/* Nagłówek wspólny */}
|
||
<motion.h2
|
||
initial={{ opacity: 0, y: 30 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.8 }}
|
||
className="text-4xl font-bold mb-4 text-center text-cyan-800"
|
||
>
|
||
Dlaczego integracja marki to konieczność?
|
||
</motion.h2>
|
||
<div className="mx-auto mb-10 h-1 w-20 bg-cyan-600" />
|
||
|
||
{/* === A === */}
|
||
{uiVariant === "A" && (
|
||
<motion.div
|
||
initial={{ opacity: 0, scale: 0.98 }}
|
||
whileInView={{ opacity: 1, scale: 1 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.6 }}
|
||
className="bg-white border border-cyan-100 rounded-none p-8 md:p-10 text-gray-800 space-y-6 shadow"
|
||
>
|
||
{/* Akapit 1 */}
|
||
<div className="grid grid-cols-[16px_1fr] gap-3 items-start">
|
||
<span className="mt-1 block w-2.5 h-2.5 rounded-full bg-cyan-600 shrink-0" aria-hidden="true" />
|
||
<p>{why.p1}</p>
|
||
</div>
|
||
{/* Akapit 2 */}
|
||
<div className="grid grid-cols-[16px_1fr] gap-3 items-start">
|
||
<span className="mt-1 block w-2.5 h-2.5 rounded-full bg-cyan-600 shrink-0" aria-hidden="true" />
|
||
<p>{why.p2}</p>
|
||
</div>
|
||
{/* Punkt nadrzędny + podpunkty */}
|
||
<div className="grid grid-cols-[16px_1fr] gap-3 items-start">
|
||
<span className="mt-1 block w-2.5 h-2.5 rounded-full bg-cyan-600 shrink-0" aria-hidden="true" />
|
||
<div>
|
||
<p>{why.bulletsIntro}</p>
|
||
<ul className="mt-2 space-y-2">
|
||
{why.bullets.map((txt, i) => (
|
||
<li key={i} className="grid grid-cols-[12px_1fr] gap-3 items-start">
|
||
<span className="mt-1 block w-2 h-2 rounded-full bg-cyan-500 shrink-0" aria-hidden="true" />
|
||
<span>{txt}</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
{/* Akapit 4 */}
|
||
<div className="grid grid-cols-[16px_1fr] gap-3 items-start">
|
||
<span className="mt-1 block w-2.5 h-2.5 rounded-full bg-cyan-600 shrink-0" aria-hidden="true" />
|
||
<p>{why.p3}</p>
|
||
</div>
|
||
</motion.div>
|
||
)}
|
||
|
||
{/* === B (Editorial) === */}
|
||
{uiVariant === "B" && (
|
||
<div className="space-y-6">
|
||
{/* Dwa callouty */}
|
||
<div className="grid md:grid-cols-2 gap-6">
|
||
{[why.p1, why.p2].map((node, i) => (
|
||
<motion.article
|
||
key={i}
|
||
initial={{ opacity: 0, y: 14 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.5, delay: i * 0.05 }}
|
||
className="relative bg-white/90 border border-stone-200 shadow-sm p-6"
|
||
>
|
||
<span className="absolute left-0 top-0 h-full w-1 bg-cyan-600" aria-hidden="true" />
|
||
<p className="text-stone-800">{node}</p>
|
||
</motion.article>
|
||
))}
|
||
</div>
|
||
|
||
{/* Lewa: „żyje w:”, Prawa: PwC */}
|
||
<div className="grid md:grid-cols-2 gap-6">
|
||
<motion.article
|
||
initial={{ opacity: 0, y: 14 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.5 }}
|
||
className="bg-white border border-cyan-100 p-6 shadow"
|
||
>
|
||
<p className="font-medium text-cyan-800">{why.bulletsIntro}</p>
|
||
<ul className="mt-3 space-y-2 text-gray-800">
|
||
{why.bullets.map((txt, i) => (
|
||
<li key={i} className="flex gap-3">
|
||
<span className="mt-2 w-2 h-2 rounded-full bg-cyan-500 shrink-0" />
|
||
<span>{txt}</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</motion.article>
|
||
<motion.article
|
||
initial={{ opacity: 0, y: 14 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.5, delay: 0.05 }}
|
||
className="bg-gradient-to-br from-cyan-50 to-blue-50 border border-cyan-100 p-6 shadow"
|
||
>
|
||
<p className="text-gray-800">{why.p3}</p>
|
||
</motion.article>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Strzałka do kolejnej sekcji */}
|
||
<motion.div
|
||
onClick={() => document.getElementById('Sytuacje')?.scrollIntoView({ behavior: 'smooth' })}
|
||
animate={{ y: [0, 15, 0] }}
|
||
transition={{ repeat: Infinity, duration: 2, ease: 'easeInOut' }}
|
||
className="absolute bottom-10 z-10 text-cyan-700 cursor-pointer"
|
||
aria-label="Przewiń do sekcji: Czy znasz te sytuacje?"
|
||
>
|
||
<svg xmlns="http://www.w3.org/2000/svg" className="h-10 w-10" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||
</svg>
|
||
</motion.div>
|
||
</section>
|
||
)}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
{/* Czy znasz te sytuacje? */}
|
||
<section id='Sytuacje' className="relative flex flex-col justify-center items-center min-h-screen py-24 overflow-hidden">
|
||
<div className="max-w-6xl mx-auto px-6">
|
||
{/* Dynamiczne tło */}
|
||
{/* <div className="absolute inset-0">
|
||
<div className="absolute -top-40 -left-40 w-96 h-96 bg-cyan-300 opacity-20 rounded-full blur-3xl animate-blob"></div>
|
||
<div className="absolute -bottom-40 -right-40 w-96 h-96 bg-blue-300 opacity-20 rounded-full blur-3xl animate-blob animation-delay-2000"></div>
|
||
</div> */}
|
||
<motion.h2
|
||
initial={{ opacity: 0, y: 30 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.8 }}
|
||
className="text-4xl font-bold mb-14 text-center text-cyan-700 drop-shadow"
|
||
>
|
||
Czy znasz te sytuacje?
|
||
</motion.h2>
|
||
|
||
<div className="grid md:grid-cols-2 gap-8">
|
||
{[
|
||
{
|
||
title: "1. Brak spójności w komunikacji",
|
||
text: "Różne działy (np. marketing, sprzedaż, obsługa klienta) komunikują się w różnym tonie i z odmiennym przekazem."
|
||
},
|
||
{
|
||
title: "2. Realizacja działań promocyjnych bez odniesienia do tożsamości marki",
|
||
text: "Firma wdraża modne działania (np. TikTok, influencerzy, AI copywriting) bez filtrowania ich przez tożsamość i wartości marki."
|
||
},
|
||
{
|
||
title: "3. Problemy w skalowaniu działań",
|
||
text: "Przy szybkim wzroście lub ekspansji różne zespoły zaczynają prowadzić działania niespójne z tożsamością marki."
|
||
},
|
||
{
|
||
title: "4. Zmiany w zespole / outsourcing marketingu",
|
||
text: "Nowe osoby lub agencje marketingowe nie rozumieją w pełni marki i działają według własnych schematów."
|
||
},
|
||
{
|
||
title: "5. Brak narzędzi lub procesów ułatwiających pracę z marką",
|
||
text: "Pracownicy nie mają dostępu do aktualnych wytycznych (brand plan, key visuals, archetypy, kroki milowe)."
|
||
},
|
||
{
|
||
title: "6. Brak mierzalnego modelu integracji marki",
|
||
text: "Firma nie ma sposobu, by sprawdzić, czy kampania, post czy oferta są zgodne z marką."
|
||
}
|
||
].map((item, i) => (
|
||
<motion.div
|
||
key={i}
|
||
initial={{ opacity: 0, y: 20 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ delay: i * 0.15, duration: 0.7, ease: 'easeOut' }}
|
||
className="p-6 bg-cyan-50/70 backdrop-blur-sm border border-cyan-100 rounded-none shadow hover:shadow-lg transition"
|
||
>
|
||
<h3 className="text-xl font-semibold text-cyan-700 mb-2">{item.title}</h3>
|
||
<p className="text-gray-700">{item.text}</p>
|
||
</motion.div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
{/* Strzałka do sekcji Partner */}
|
||
<motion.div
|
||
onClick={() => document.getElementById('Partner')?.scrollIntoView({ behavior: 'smooth' })}
|
||
animate={{ y: [0, 15, 0] }}
|
||
transition={{ repeat: Infinity, duration: 2, ease: 'easeInOut' }}
|
||
className="absolute bottom-10 z-10 text-cyan-700 cursor-pointer"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
className="h-10 w-10"
|
||
fill="none"
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
>
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||
</svg>
|
||
</motion.div>
|
||
</section>
|
||
|
||
|
||
{/* Potrzebujesz partnera, który... */}
|
||
<section
|
||
id="Partner"
|
||
className="relative flex flex-col justify-between items-center min-h-screen py-24 overflow-hidden"
|
||
>
|
||
{/* Tło dekoracyjne */}
|
||
{/* <div className="absolute -top-20 -left-20 w-72 h-72 bg-cyan-200 rounded-full blur-3xl opacity-30 animate-pulse" />
|
||
<div className="absolute -bottom-20 -right-20 w-96 h-96 bg-blue-200 rounded-full blur-3xl opacity-30 animate-pulse" /> */}
|
||
|
||
<div className="relative z-10 max-w-5xl mx-auto px-6 w-full flex-grow flex flex-col justify-center items-center">
|
||
<motion.h2
|
||
initial={{ opacity: 0, y: 30 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.8 }}
|
||
className="text-4xl font-bold mb-12 text-center text-cyan-800 drop-shadow"
|
||
>
|
||
Potrzebujesz partnera, który:
|
||
</motion.h2>
|
||
|
||
<motion.ul
|
||
initial={{ opacity: 0, y: 20 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.9, ease: 'easeOut' }}
|
||
className="space-y-6 text-lg leading-relaxed bg-white/70 backdrop-blur-lg border border-cyan-100 shadow-2xl rounded-none p-10 w-full"
|
||
>
|
||
{[
|
||
"myśli procesami, nie kanałami?",
|
||
"łączy branding z danymi, CX i automatyzacją?",
|
||
"projektuje mechanizmy wzrostu, a nie tylko „fajny content”?",
|
||
"wdraża AI w obsłudze marki, zamiast tylko mówić o trendach?",
|
||
"testuje i optymalizuje, zamiast zgadywać?"
|
||
].map((item, i) => (
|
||
<li key={i} className="flex items-start gap-4">
|
||
<span className="w-5 h-5 flex-shrink-0 rounded-full bg-cyan-500 mt-1" />
|
||
<span className="text-gray-800">
|
||
<strong>{item.split(" ")[0]}</strong> {item.replace(item.split(" ")[0], "")}
|
||
</span>
|
||
</li>
|
||
))}
|
||
</motion.ul>
|
||
|
||
{/* Nowa, wizualna dekoracja bez tekstu */}
|
||
<motion.div
|
||
initial={{ opacity: 0, scale: 0.8 }}
|
||
whileInView={{ opacity: 1, scale: 1 }}
|
||
viewport={{ once: true }}
|
||
transition={{ delay: 0.3, duration: 0.8 }}
|
||
className="mt-16 flex justify-center items-center space-x-6"
|
||
>
|
||
<div className="w-24 h-24 bg-cyan-200 rounded-full blur-2xl animate-blob animation-delay-2000" />
|
||
<div className="w-32 h-32 bg-blue-200 rounded-full blur-3xl animate-blob" />
|
||
<div className="w-20 h-20 bg-cyan-100 rounded-full blur-xl animate-blob animation-delay-1000" />
|
||
</motion.div>
|
||
</div>
|
||
|
||
{/* Animowany napis i strzałka przewijająca do Model */}
|
||
<motion.div
|
||
onClick={() => document.getElementById('Model')?.scrollIntoView({ behavior: 'smooth' })}
|
||
animate={{ y: [0, 10, 0] }}
|
||
transition={{ repeat: Infinity, duration: 2, ease: 'easeInOut' }}
|
||
className="absolute bottom-10 flex flex-col items-center text-cyan-700 cursor-pointer"
|
||
>
|
||
<span className="text-lg font-medium mb-2">Spójrz na model Prime Code</span>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
className="h-8 w-8"
|
||
fill="none"
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
>
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||
</svg>
|
||
</motion.div>
|
||
</section>
|
||
|
||
|
||
|
||
|
||
{/* Model Prime Code */}
|
||
<motion.section
|
||
id="Model"
|
||
initial={{ opacity: 0, y: 40 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.8, ease: "easeOut" }}
|
||
className="relative flex flex-col justify-center items-center min-h-screen pt-12 pb-32 overflow-hidden"
|
||
>
|
||
{/* Dekoracyjne tło */}
|
||
{/* <div className="absolute -top-20 -left-20 w-72 h-72 bg-cyan-200 rounded-full blur-3xl opacity-30 animate-pulse" />
|
||
<div className="absolute -bottom-20 -right-20 w-96 h-96 bg-blue-200 rounded-full blur-3xl opacity-30 animate-pulse" /> */}
|
||
|
||
<div className="relative z-10 max-w-6xl w-full mx-auto px-6 flex-grow flex flex-col justify-center items-center">
|
||
<h2 className="-mt-4 text-3xl font-bold mb-4 text-cyan-800 text-center">
|
||
Model Prime Code: marka jako system operacyjny
|
||
</h2>
|
||
<p className="-mt-2 text-gray-700 max-w-3xl mx-auto mb-12 text-center">
|
||
W klasycznym ujęciu marka to obietnica. W dzisiejszej rzeczywistości to{' '}
|
||
<strong>system działania</strong>. Prime Code traktuje markę jak{' '}
|
||
<strong>system operacyjny</strong> firmy: regulujący rytm decyzji,
|
||
komunikacji i doświadczeń klienta. Spójność przestaje być tylko
|
||
kwestią estetyki a staje się{' '}
|
||
<strong>mierzalnym źródłem przewagi</strong>.
|
||
</p>
|
||
|
||
{/* Siatka kafelków – bez shared layout na całej siatce */}
|
||
<div className="grid md:grid-cols-3 gap-6 lg:gap-8 items-start">
|
||
{tiles.map((tile, index) => (
|
||
<Tile
|
||
key={index}
|
||
open={expandedIndex === index}
|
||
onOpen={() => setExpandedIndex(index)}
|
||
onClose={() => setExpandedIndex(null)}
|
||
title={tile.title}
|
||
short={tile.short}
|
||
output={tile.output}
|
||
efekt={tile.efekt}
|
||
details={tile.details}
|
||
/>
|
||
))}
|
||
</div>
|
||
|
||
</div>
|
||
|
||
{/* Strzałka przewijania do następnej sekcji */}
|
||
<motion.div
|
||
onClick={() => document.getElementById('CoPotrafimy')?.scrollIntoView({ behavior: 'smooth' })}
|
||
animate={{ y: [0, 10, 0] }}
|
||
transition={{ repeat: Infinity, duration: 2, ease: 'easeInOut' }}
|
||
className="absolute bottom-10 flex flex-col items-center text-cyan-700 cursor-pointer"
|
||
>
|
||
<span className="text-lg font-medium mb-2">Zobacz Co potrafimy?</span>
|
||
<svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||
</svg>
|
||
</motion.div>
|
||
</motion.section>
|
||
|
||
|
||
|
||
|
||
{/* Co potrafimy? */}
|
||
<motion.section
|
||
id="CoPotrafimy"
|
||
initial={{ opacity: 0, y: 30 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.8 }}
|
||
className="relative flex flex-col justify-center items-center min-h-screen pt-12 pb-32 overflow-hidden"
|
||
>
|
||
|
||
{/* Główna zawartość wyśrodkowana pionowo */}
|
||
<div className="max-w-5xl w-full mx-auto px-6 flex-grow flex flex-col justify-center">
|
||
<h2 className="text-4xl font-bold mb-14 text-center text-cyan-800 drop-shadow">
|
||
Co potrafimy?
|
||
</h2>
|
||
|
||
<motion.ul
|
||
initial={{ opacity: 0, y: 20 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.9, ease: 'easeOut' }}
|
||
className="space-y-4 text-lg leading-relaxed bg-white/70 backdrop-blur-lg border border-cyan-100 shadow-2xl rounded-none p-10"
|
||
>
|
||
{[
|
||
"Projektujemy marki, które rosną dzięki danym i doświadczeniu użytkownika",
|
||
"Tworzymy systemy komunikacji, a nie tylko tone of voice",
|
||
"Zarządzamy marką tak jak innowacyjnym produktem lub usługą: zwinnie, testy, feedback, iteracje",
|
||
"Wdrażamy AI w miejscach, które mają znaczenie dla doświadczenia marki",
|
||
"Projektujemy onboarding, optymalizujemy punkty styku z klientem i mechanizmy wzrostu",
|
||
"Projektujemy szablony i piszemy playbooki dla marketingu, sprzedaży, HR i obsługi klienta z marką w centrum"
|
||
].map((item, i) => (
|
||
<li key={i} className="flex items-start gap-4">
|
||
<span className="w-2.5 h-2.5 mt-2 flex-shrink-0 rounded-full bg-cyan-600"></span>
|
||
<span className="text-gray-800">{item}</span>
|
||
</li>
|
||
))}
|
||
</motion.ul>
|
||
</div>
|
||
|
||
{/* Strzałka przewijania do następnej sekcji */}
|
||
<motion.div
|
||
onClick={() => document.getElementById('JakDzialamy')?.scrollIntoView({ behavior: 'smooth' })}
|
||
animate={{ y: [0, 10, 0] }}
|
||
transition={{ repeat: Infinity, duration: 2, ease: 'easeInOut' }}
|
||
className="absolute bottom-10 flex flex-col items-center text-cyan-700 cursor-pointer"
|
||
>
|
||
<span className="text-lg font-medium mb-2">Zobacz, jak to działa</span>
|
||
<svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||
</svg>
|
||
</motion.div>
|
||
</motion.section>
|
||
|
||
|
||
|
||
|
||
{/* Zobacz, jak to działa bez ryzyka */}
|
||
<motion.section
|
||
id="JakDzialamy"
|
||
initial={{ opacity: 0, y: 30 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.8 }}
|
||
className="relative flex flex-col justify-center items-center min-h-screen pt-12 pb-32 overflow-hidden"
|
||
>
|
||
{/* Dekoracje w tle */}
|
||
{/* <div className="absolute -top-24 -left-24 w-96 h-96 bg-cyan-200 rounded-full blur-3xl opacity-30 animate-pulse" />
|
||
<div className="absolute -bottom-24 -right-24 w-80 h-80 bg-blue-300 rounded-full blur-3xl opacity-30 animate-pulse" /> */}
|
||
|
||
{/* Główna zawartość */}
|
||
<div className="relative z-10 max-w-5xl w-full mx-auto px-6 flex-grow flex flex-col justify-center items-center text-center">
|
||
<motion.h2
|
||
initial={{ opacity: 0, y: 30 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.8 }}
|
||
className="text-4xl font-bold mb-6 text-cyan-800 drop-shadow"
|
||
>
|
||
Zobacz, jak to działa bez ryzyka
|
||
</motion.h2>
|
||
<motion.h3
|
||
initial={{ opacity: 0, y: 20 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ delay: 0.2, duration: 0.8 }}
|
||
className="text-2xl font-semibold mb-8 text-gray-800"
|
||
>
|
||
Brand Daily Test
|
||
</motion.h3>
|
||
<motion.p
|
||
initial={{ opacity: 0, y: 10 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ delay: 0.3, duration: 0.8 }}
|
||
className="text-lg text-gray-700 max-w-2xl mb-12"
|
||
>
|
||
W 7 dni pokażemy Ci realne luki, szanse i jeden test do wdrożenia od ręki.
|
||
</motion.p>
|
||
|
||
<div className="grid md:grid-cols-2 gap-8 w-full">
|
||
{[
|
||
"mini-audyt doświadczeń i danych",
|
||
"mapa punktów styku",
|
||
"insighty do poprawy spójności i skuteczności",
|
||
"1 eksperyment do wdrożenia natychmiast"
|
||
].map((item, i) => (
|
||
<motion.div
|
||
key={i}
|
||
initial={{ opacity: 0, y: 20 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ delay: i * 0.15, duration: 0.6 }}
|
||
className="flex items-start gap-4 bg-white/80 backdrop-blur-lg border border-cyan-100 rounded-none shadow-lg p-5 hover:shadow-xl transition h-full"
|
||
>
|
||
{/* kropka jak w sekcji "Potrzebujesz partnera" */}
|
||
<span
|
||
className="w-5 h-5 flex-shrink-0 rounded-full bg-cyan-500 mt-1"
|
||
aria-hidden="true"
|
||
/>
|
||
<span className="text-gray-800">{item}</span>
|
||
</motion.div>
|
||
))}
|
||
</div>
|
||
|
||
|
||
<motion.p
|
||
initial={{ opacity: 0, y: 20 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ delay: 0.5, duration: 0.8 }}
|
||
className="mt-12 text-lg text-gray-700"
|
||
>
|
||
<a href='#Kontakt' className="font-semibold underline decoration-cyan-600 underline-offset-4 hover:opacity-80">
|
||
Efekty zanim podejmiesz decyzję o dalszej współpracy.
|
||
</a>
|
||
</motion.p>
|
||
</div>
|
||
|
||
{/* Strzałka przewijania do następnej sekcji
|
||
<motion.div
|
||
onClick={() => document.getElementById('PomogliśmyCaseStudies')?.scrollIntoView({ behavior: 'smooth' })}
|
||
animate={{ y: [0, 10, 0] }}
|
||
transition={{ repeat: Infinity, duration: 2, ease: 'easeInOut' }}
|
||
className="absolute bottom-10 flex flex-col items-center text-cyan-700 cursor-pointer"
|
||
>
|
||
<span className="text-lg font-medium mb-2">Zobacz komu pomogliśmy</span>
|
||
<svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||
</svg>
|
||
</motion.div> */}
|
||
</motion.section>
|
||
|
||
|
||
|
||
|
||
{/* Połączona sekcja „Pomogliśmy” + „Case Studies” (STATYCZNIE) */}
|
||
<motion.section
|
||
id="PomogliśmyCaseStudies"
|
||
initial={{ opacity: 0, y: 30 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.8 }}
|
||
className="py-24 relative overflow-visible min-h-screen"
|
||
>
|
||
<div className="max-w-6xl mx-auto px-6 text-center flex flex-col h-full">
|
||
|
||
{/* Nagłówek „Pomogliśmy” */}
|
||
<h2 className="text-4xl font-bold mb-10 text-cyan-800">
|
||
Pomogliśmy
|
||
</h2>
|
||
|
||
{/* LISTA STATYCZNA — zamiast karuzel */}
|
||
<div className="space-y-6">
|
||
{/* wiersz 1 */}
|
||
<div className="grid gap-6 md:grid-cols-2">
|
||
{[
|
||
"Ponad 20 firmom farmaceutycznym",
|
||
"Ponad 20 firmom FMCG",
|
||
"Ponad 15 firmom B2B",
|
||
"10 firmom technologicznym i startupom",
|
||
].map((item, i) => (
|
||
<div
|
||
key={`row1-${i}`}
|
||
className="flex items-start gap-4 p-5 bg-white/85 backdrop-blur-md border border-cyan-100 rounded-none shadow-sm hover:shadow-md transition text-left"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
className="h-6 w-6 text-cyan-600 flex-shrink-0"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
strokeWidth={2}
|
||
aria-hidden="true"
|
||
>
|
||
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
||
</svg>
|
||
<p className="text-gray-800 text-base md:text-lg leading-relaxed text-left">
|
||
{item}
|
||
</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* wiersz 2 */}
|
||
<div className="grid gap-6 md:grid-cols-2">
|
||
{[
|
||
"4 bankom i 3 firmom ubezpieczeniowym",
|
||
"5 instytucjom edukacyjnym",
|
||
"Firmom z branży rozrywkowej, modowej, przemysłowej, wydawniczej, rolniczej i wielu innych",
|
||
"Globalnym centralom koncernów z USA, Kanady, Niemiec, Wielkiej Brytanii, Hiszpanii, Węgier i Polski",
|
||
].map((item, i) => (
|
||
<div
|
||
key={`row2-${i}`}
|
||
className="flex items-start gap-4 p-5 bg-white/85 backdrop-blur-md border border-cyan-100 rounded-none shadow-sm hover:shadow-md transition text-left"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
className="h-6 w-6 text-cyan-600 flex-shrink-0"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
strokeWidth={2}
|
||
aria-hidden="true"
|
||
>
|
||
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
||
</svg>
|
||
<p className="text-gray-800 text-base md:text-lg leading-relaxed text-left">
|
||
{item}
|
||
</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
|
||
{/* Case Studies — bez zmian, korzysta z Twojego komponentu CaseCard */}
|
||
<div className="max-w-6xl mx-auto px-6 mt-16">
|
||
<h3 className="text-3xl font-bold mb-10 text-cyan-800">
|
||
Case Studies
|
||
</h3>
|
||
|
||
<div className="grid md:grid-cols-3 gap-8 items-start">
|
||
{caseStudies.map((study, index) => (
|
||
<CaseCard
|
||
key={index}
|
||
index={index}
|
||
openIndex={openIndex}
|
||
setOpenIndex={setOpenIndex}
|
||
title={study.title}
|
||
short={study.short}
|
||
details={study.details}
|
||
/>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</motion.section>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
{/* KONTAKT – wyeksponowany baner */}
|
||
<section
|
||
id="Kontakt"
|
||
className="relative w-full bg-gradient-to-r from-cyan-700 via-cyan-600 to-blue-700 text-white py-14 md:py-20"
|
||
>
|
||
<div className="max-w-6xl mx-auto px-6 grid md:grid-cols-[1.1fr_0.9fr] gap-10 items-center">
|
||
{/* Tekst + CTA */}
|
||
<div>
|
||
<h2 className="text-3xl md:text-4xl font-extrabold tracking-tight">
|
||
Zacznij od eksperymentu
|
||
</h2>
|
||
<p className="mt-3 md:mt-4 text-lg text-cyan-100 max-w-xl">
|
||
Zobacz, co marka może zrobić – w 7 dni.
|
||
</p>
|
||
|
||
<div className="mt-8 flex flex-col sm:flex-row gap-4">
|
||
<a
|
||
href="mailto:primecode@primecode.pl"
|
||
className="inline-flex items-center justify-center px-6 h-12 bg-white text-cyan-700 font-semibold border border-white hover:opacity-90 transition rounded-none"
|
||
aria-label="Napisz maila do Prime Code"
|
||
>
|
||
{/* mail icon */}
|
||
<svg className="w-5 h-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||
<path strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
|
||
d="M4 4h16v16H4z M22 6l-10 7L2 6" />
|
||
</svg>
|
||
Napisz maila
|
||
</a>
|
||
<a
|
||
href="tel:+48500133609"
|
||
className="inline-flex items-center justify-center px-6 h-12 bg-transparent text-white font-semibold border border-white hover:bg-white/10 transition rounded-none"
|
||
aria-label="Zadzwoń do Prime Code"
|
||
>
|
||
{/* phone icon */}
|
||
<svg className="w-5 h-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||
<path strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
|
||
d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07
|
||
19.5 19.5 0 0 1-6-6A19.79 19.79 0 0 1 2.1 4.18
|
||
2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.98.36 1.93.69 2.84a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.24-1.26a2 2 0 0 1 2.11-.45c.91.33 1.86.56 2.84.69A2 2 0 0 1 22 16.92z" />
|
||
</svg>
|
||
Zadzwoń
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Karta z osobą kontaktową */}
|
||
<div className="bg-white text-gray-800 border border-cyan-100 shadow-xl p-6 md:p-7 rounded-none">
|
||
<div className="flex items-center gap-4">
|
||
<img
|
||
src="/jacek.jpg"
|
||
alt="Jacek Pogorzelski – Prime Code"
|
||
className="w-16 h-16 object-cover rounded-full"
|
||
/>
|
||
<div>
|
||
<a
|
||
href="https://jacekpogorzelski.pl"
|
||
className="text-lg font-semibold text-cyan-700 underline underline-offset-4"
|
||
>
|
||
Jacek Pogorzelski
|
||
</a>
|
||
<p className="text-sm text-gray-600 mt-0.5">Prime Code</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="mt-5 space-y-2">
|
||
<a href="mailto:primecode@primecode.pl" className="flex items-center gap-2 group">
|
||
<svg className="w-5 h-5 text-cyan-600" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||
<path strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
|
||
d="M4 4h16v16H4z M22 6l-10 7L2 6" />
|
||
</svg>
|
||
<span className="group-hover:underline">primecode@primecode.pl</span>
|
||
</a>
|
||
<a href="tel:+48500133609" className="flex items-center gap-2 group">
|
||
<svg className="w-5 h-5 text-cyan-600" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||
<path strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
|
||
d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07
|
||
19.5 19.5 0 0 1-6-6A19.79 19.79 0 0 1 2.1 4.18
|
||
2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.98.36 1.93.69 2.84a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.24-1.26a2 2 0 0 1 2.11-.45c.91.33 1.86.56 2.84.69A2 2 0 0 1 22 16.92z" />
|
||
</svg>
|
||
<span className="group-hover:underline">+48 500 133 609</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{/* FOOTER – prosty i równy */}
|
||
<footer className="bg-gradient-to-b from-cyan-100 to-blue-100 border-t border-cyan-200">
|
||
<div className="max-w-6xl mx-auto px-6 py-8">
|
||
<p className="text-center text-sm text-gray-500">© 2025 Prime Code</p>
|
||
</div>
|
||
</footer>
|
||
|
||
|
||
{/* Quick contact bar */}
|
||
<div
|
||
aria-label="Szybki kontakt"
|
||
className="fixed z-50 bottom-4 right-4 sm:right-6 flex items-center gap-3 bg-white/90 backdrop-blur-md border border-cyan-100 shadow-lg rounded-full px-3 py-2"
|
||
>
|
||
<img src="/jacek.jpg" alt="Jacek Pogorzelski" className="w-7 h-7 rounded-full object-cover" />
|
||
<a href="mailto:primecode@primecode.pl" className="text-sm text-cyan-700 hover:underline">Email</a>
|
||
<span className="text-gray-300">•</span>
|
||
<a href="tel:+48500133609" className="text-sm text-cyan-700 hover:underline">Zadzwoń</a>
|
||
</div>
|
||
|
||
{/* STYLE SWITCHER – pływający panel */}
|
||
<StyleSwitcher value={uiVariant} onChange={setUiVariant} />
|
||
|
||
</div>
|
||
)
|
||
}
|
||
|
||
function CaseCard({
|
||
index,
|
||
openIndex,
|
||
setOpenIndex,
|
||
title,
|
||
short,
|
||
details
|
||
}: {
|
||
index: number
|
||
openIndex: number | null
|
||
setOpenIndex: (i: number | null) => void
|
||
title: string
|
||
short: string
|
||
details: string[]
|
||
}) {
|
||
const isOpen = openIndex === index
|
||
|
||
return (
|
||
<motion.div
|
||
initial={{ opacity: 0, y: 20 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.6 }}
|
||
whileHover={{ scale: 1.03 }}
|
||
className="bg-white/80 backdrop-blur-lg shadow-xl border border-cyan-100 rounded-none
|
||
p-6 flex flex-col justify-between min-h-[220px] max-w-[350px] mx-auto transition-all duration-300"
|
||
>
|
||
<div>
|
||
<h3 className="text-2xl font-semibold text-cyan-700 mb-2">{title}</h3>
|
||
<p className="text-gray-700 mb-4">{short}</p>
|
||
</div>
|
||
|
||
<button
|
||
onClick={() => setOpenIndex(isOpen ? null : index)}
|
||
className="mt-auto text-cyan-600 font-semibold hover:underline flex items-center gap-2 focus:outline-none"
|
||
>
|
||
{isOpen ? 'Zwiń' : 'Pokaż szczegóły'}
|
||
<svg
|
||
className={`w-4 h-4 transition-transform ${isOpen ? 'rotate-180' : 'rotate-0'}`}
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
fill="none"
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
>
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||
</svg>
|
||
</button>
|
||
|
||
<AnimatePresence>
|
||
{isOpen && (
|
||
<motion.ul
|
||
key="expanded"
|
||
initial={{ opacity: 0, height: 0 }}
|
||
animate={{ opacity: 1, height: 'auto' }}
|
||
exit={{ opacity: 0, height: 0 }}
|
||
transition={{ duration: 0.3 }}
|
||
className="mt-4 list-disc pl-5 text-left text-gray-700 space-y-2 text-sm">
|
||
{details.map((d, idx) => (
|
||
<li key={idx}>{d}</li>
|
||
))}
|
||
</motion.ul>
|
||
)}
|
||
</AnimatePresence>
|
||
</motion.div>
|
||
)
|
||
}
|
||
|
||
function Tile({
|
||
open,
|
||
onOpen,
|
||
onClose,
|
||
title,
|
||
short,
|
||
output,
|
||
efekt,
|
||
details,
|
||
}: {
|
||
open: boolean;
|
||
onOpen: () => void;
|
||
onClose: () => void;
|
||
title: string;
|
||
short: string;
|
||
output: string;
|
||
efekt: string;
|
||
details: React.ReactNode;
|
||
}) {
|
||
return (
|
||
<article
|
||
className={
|
||
"flex flex-col rounded-none border border-cyan-100 bg-white/90 transition-shadow duration-300 " +
|
||
(open ? "shadow-xl ring-1 ring-cyan-100" : "shadow")
|
||
}
|
||
>
|
||
{/* Górna, widoczna część – równa wysokość przy stanie zamkniętym */}
|
||
<div className={`p-6 flex flex-col ${open ? "" : "min-h-[280px]"}`}>
|
||
<div className="flex justify-between items-start">
|
||
<h3 className="text-xl font-semibold text-cyan-700 mb-2">{title}</h3>
|
||
|
||
</div>
|
||
|
||
<p className="text-gray-700 mb-2 leading-relaxed text-justify">{short}</p>
|
||
|
||
<p className="text-sm text-gray-700 leading-[1.65]">
|
||
<span className="font-semibold text-cyan-800">Output:</span> {output}
|
||
</p>
|
||
|
||
|
||
{!open && (
|
||
<div className="mt-auto pt-4 border-t border-cyan-100">
|
||
<button
|
||
type="button"
|
||
onClick={onOpen}
|
||
aria-expanded={open}
|
||
className="text-cyan-600 font-semibold hover:underline cursor-pointer"
|
||
>
|
||
Zobacz więcej
|
||
</button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Rozwijana część – animujemy tylko wysokość tej sekcji */}
|
||
<motion.div
|
||
initial={false}
|
||
animate={open ? { height: "auto", opacity: 1 } : { height: 0, opacity: 0 }}
|
||
transition={{ duration: 0.35, ease: [0.22, 1, 0.36, 1] }}
|
||
style={{ overflow: "hidden", willChange: "height", position: "relative", top: "-10px" }}
|
||
>
|
||
<div className="px-6 pt-0 pb-6 text-sm text-gray-700">
|
||
{/* typografia: ten sam line-height i marginesy */}
|
||
<div className="text-justify hyphens-auto leading-[1.65] [&>p]:mb-2 [&_ul]:my-2 [&_li]:leading-[1.65]">
|
||
{details}
|
||
<p className="mt-2">
|
||
<span className="font-semibold text-cyan-800">Efekt:</span> {efekt}
|
||
</p>
|
||
</div>
|
||
|
||
<div className="mt-4 pt-4 border-t border-cyan-100">
|
||
<button
|
||
type="button"
|
||
onClick={onClose}
|
||
className="text-cyan-600 font-semibold hover:underline cursor-pointer"
|
||
>
|
||
Zwiń
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</motion.div>
|
||
|
||
|
||
|
||
</article>
|
||
);
|
||
}
|
||
|
||
// KOMPONENT PRZEŁĄCZNIKA (wrzuć pod innymi komponentami w tym pliku)
|
||
function StyleSwitcher({
|
||
value,
|
||
onChange,
|
||
}: {
|
||
value: "A" | "B" | "C";
|
||
onChange: (v: "A" | "B" | "C") => void;
|
||
}) {
|
||
const [mounted, setMounted] = useState(false);
|
||
useEffect(() => setMounted(true), []);
|
||
if (!mounted) return null;
|
||
|
||
return createPortal(
|
||
<div className="fixed bottom-24 right-4 sm:right-6 z-[9999] pointer-events-auto">
|
||
<div className="bg-white/95 backdrop-blur-md border border-cyan-100 shadow-xl rounded-xl px-3 py-2">
|
||
<div className="flex items-center gap-2">
|
||
<Palette className="w-4 h-4 text-cyan-700" />
|
||
<span className="text-[11px] uppercase tracking-wide text-gray-600">Wariant</span>
|
||
</div>
|
||
<div className="mt-2 grid grid-cols-3 gap-2">
|
||
{(["A", "B", "C"] as const).map((v) => (
|
||
<button
|
||
key={v}
|
||
onClick={() => onChange(v)}
|
||
className={[
|
||
"px-3 py-1.5 rounded-md text-xs font-semibold ring-1 transition",
|
||
value === v
|
||
? "bg-cyan-600 text-white ring-cyan-600"
|
||
: "bg-white text-gray-700 ring-gray-300 hover:bg-gray-50",
|
||
].join(" ")}
|
||
aria-pressed={value === v}
|
||
>
|
||
{v}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>,
|
||
document.body
|
||
);
|
||
}
|
||
|
||
|
||
function DotList({ items, className = "" }: { items: string[]; className?: string }) {
|
||
return (
|
||
<ul className={["mt-3 space-y-2", className].join(" ")}>
|
||
{items.map((txt, i) => (
|
||
<li key={i} className="grid grid-cols-[12px_1fr] gap-3 items-start">
|
||
{/* ta sama turkusowa kropka co w innych wypunktowaniach */}
|
||
<span
|
||
aria-hidden="true"
|
||
className="mt-[0.35rem] block w-2 h-2 rounded-full bg-cyan-600"
|
||
/>
|
||
<span className="text-gray-800">{txt}</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
);
|
||
}
|
||
|
||
|
||
|
||
function DlaczegoIntegracji_Timeline({
|
||
why,
|
||
}: {
|
||
why: {
|
||
p1: React.ReactNode;
|
||
p2: React.ReactNode;
|
||
bulletsIntro: string;
|
||
bullets: string[];
|
||
p3: React.ReactNode;
|
||
};
|
||
}) {
|
||
return (
|
||
<section
|
||
id="DlaczegoIntegracji"
|
||
className="relative flex flex-col justify-center items-center min-h-screen py-20 overflow-hidden"
|
||
>
|
||
<div className="relative max-w-6xl mx-auto px-6 w-full">
|
||
{/* Nagłówek */}
|
||
<motion.h2
|
||
initial={{ opacity: 0, y: 30 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.8 }}
|
||
className="text-4xl font-bold mb-4 text-center text-cyan-800"
|
||
>
|
||
Dlaczego integracja marki to konieczność?
|
||
</motion.h2>
|
||
<div className="mx-auto mb-10 h-1 w-20 bg-cyan-600" />
|
||
|
||
{/* Oś czasu */}
|
||
<div className="relative">
|
||
{/* pionowa linia */}
|
||
<div
|
||
aria-hidden="true"
|
||
className="hidden md:block absolute left-1/2 top-0 -translate-x-1/2 w-px h-full bg-gradient-to-b from-cyan-200 via-cyan-200/70 to-blue-200"
|
||
/>
|
||
|
||
{/* 01 (lewa) */}
|
||
<div className="md:grid md:grid-cols-[1fr_56px_1fr] md:items-center md:gap-8 mb-10">
|
||
<motion.div
|
||
initial={{ opacity: 0, x: -24 }}
|
||
whileInView={{ opacity: 1, x: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.5 }}
|
||
className="bg-white/85 backdrop-blur-md border border-cyan-100 shadow-md rounded-xl p-6 ring-1 ring-white/40"
|
||
>
|
||
<p className="text-gray-800">{why.p1}</p>
|
||
</motion.div>
|
||
<div className="hidden md:flex items-center justify-center">
|
||
<div className="relative w-14 h-14">
|
||
<div className="absolute inset-0 rounded-full bg-white shadow border border-cyan-100" />
|
||
<div className="absolute inset-1 rounded-full bg-gradient-to-b from-cyan-50 to-blue-50" />
|
||
<div className="relative w-14 h-14 flex items-center justify-center font-bold text-cyan-700">01</div>
|
||
</div>
|
||
</div>
|
||
<div className="md:block hidden" />
|
||
</div>
|
||
|
||
{/* 02 (prawa) */}
|
||
<div className="md:grid md:grid-cols-[1fr_56px_1fr] md:items-center md:gap-8 mb-10">
|
||
<div className="md:block hidden" />
|
||
<div className="hidden md:flex items-center justify-center">
|
||
<div className="relative w-14 h-14">
|
||
<div className="absolute inset-0 rounded-full bg-white shadow border border-cyan-100" />
|
||
<div className="absolute inset-1 rounded-full bg-gradient-to-b from-cyan-50 to-blue-50" />
|
||
<div className="relative w-14 h-14 flex items-center justify-center font-bold text-cyan-700">02</div>
|
||
</div>
|
||
</div>
|
||
<motion.div
|
||
initial={{ opacity: 0, x: 24 }}
|
||
whileInView={{ opacity: 1, x: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.5 }}
|
||
className="bg-white/85 backdrop-blur-md border border-cyan-100 shadow-md rounded-xl p-6 ring-1 ring-white/40"
|
||
>
|
||
<p className="text-gray-800">{why.p2}</p>
|
||
</motion.div>
|
||
</div>
|
||
|
||
{/* 03 (lewa) – chipy */}
|
||
<div className="md:grid md:grid-cols-[1fr_56px_1fr] md:items-center md:gap-8 mb-10">
|
||
<motion.div
|
||
initial={{ opacity: 0, x: -24 }}
|
||
whileInView={{ opacity: 1, x: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.5 }}
|
||
className="bg-white/85 backdrop-blur-md border border-cyan-100 shadow-md rounded-xl p-6 ring-1 ring-white/40"
|
||
>
|
||
<p className="text-gray-800 mb-3">
|
||
<strong className="text-cyan-800">{why.bulletsIntro}</strong>
|
||
</p>
|
||
<DotList items={[
|
||
"chatbotach, landingach, mailach i pushach,",
|
||
"procesach sprzedażowych i onboardingowych,",
|
||
"sposobie, w jaki działa Twój model cenowy, UX i obsługa klienta.",
|
||
]} />
|
||
|
||
|
||
</motion.div>
|
||
<div className="hidden md:flex items-center justify-center">
|
||
<div className="relative w-14 h-14">
|
||
<div className="absolute inset-0 rounded-full bg-white shadow border border-cyan-100" />
|
||
<div className="absolute inset-1 rounded-full bg-gradient-to-b from-cyan-50 to-blue-50" />
|
||
<div className="relative w-14 h-14 flex items-center justify-center font-bold text-cyan-700">03</div>
|
||
</div>
|
||
</div>
|
||
<div className="md:block hidden" />
|
||
</div>
|
||
|
||
{/* 04 (prawa) – callout */}
|
||
<div className="md:grid md:grid-cols-[1fr_56px_1fr] md:items-center md:gap-8">
|
||
<div className="md:block hidden" />
|
||
<div className="hidden md:flex items-center justify-center">
|
||
<div className="relative w-14 h-14">
|
||
<div className="absolute inset-0 rounded-full bg-white shadow border border-cyan-100" />
|
||
<div className="absolute inset-1 rounded-full bg-gradient-to-b from-cyan-50 to-blue-50" />
|
||
<div className="relative w-14 h-14 flex items-center justify-center font-bold text-cyan-700">04</div>
|
||
</div>
|
||
</div>
|
||
<motion.div
|
||
initial={{ opacity: 0, x: 24 }}
|
||
whileInView={{ opacity: 1, x: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.5 }}
|
||
className="rounded-xl p-6 bg-gradient-to-br from-white/90 to-cyan-50/70 border border-cyan-100 shadow-md"
|
||
>
|
||
<p className="text-gray-800">{why.p3}</p>
|
||
</motion.div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Strzałka do kolejnej sekcji */}
|
||
<motion.div
|
||
onClick={() => document.getElementById('Sytuacje')?.scrollIntoView({ behavior: 'smooth' })}
|
||
animate={{ y: [0, 15, 0] }}
|
||
transition={{ repeat: Infinity, duration: 2, ease: 'easeInOut' }}
|
||
className="absolute bottom-10 z-10 text-cyan-700 cursor-pointer"
|
||
aria-label="Przewiń do sekcji: Czy znasz te sytuacje?"
|
||
>
|
||
<svg xmlns="http://www.w3.org/2000/svg" className="h-10 w-10" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||
</svg>
|
||
</motion.div>
|
||
</section>
|
||
);
|
||
}
|
||
|
||
|
||
function Background({ variant }: { variant: "A" | "B" | "C" }) {
|
||
const [reduce, setReduce] = useState(false);
|
||
useEffect(() => {
|
||
setReduce(window.matchMedia("(prefers-reduced-motion: reduce)").matches);
|
||
}, []);
|
||
return (
|
||
<div className="fixed inset-0 -z-10 overflow-hidden pointer-events-none">
|
||
{/* WARSTWA BAZOWA – jak na początku strony */}
|
||
<div className="absolute inset-0 bg-gradient-to-br from-cyan-50 via-white to-blue-100" />
|
||
|
||
{/* === A — Aurora (oryginalna kolorystyka) === */}
|
||
{variant === "A" && (
|
||
<>
|
||
<motion.div
|
||
aria-hidden
|
||
className="absolute -top-20 -left-40 w-[900px] h-[900px] rounded-full blur-3xl
|
||
bg-gradient-to-tr from-cyan-200 via-blue-200 to-blue-300 opacity-30"
|
||
animate={
|
||
reduce ? {} : { x: [-10, 20, -10], y: [0, 20, 0], scale: [1, 1.05, 1] }
|
||
}
|
||
transition={{ duration: 20, repeat: Infinity, ease: "easeInOut" }}
|
||
/>
|
||
<motion.div
|
||
aria-hidden
|
||
className="absolute -bottom-32 -right-32 w-[900px] h-[900px] rounded-full blur-3xl
|
||
bg-gradient-to-tr from-blue-200 via-cyan-100 to-blue-300 opacity-25"
|
||
animate={
|
||
reduce ? {} : { x: [10, -20, 10], y: [0, -15, 0], scale: [1, 1.07, 1] }
|
||
}
|
||
transition={{ duration: 22, repeat: Infinity, ease: "easeInOut" }}
|
||
/>
|
||
<motion.div
|
||
aria-hidden
|
||
className="absolute top-[45%] left-1/2 -translate-x-1/2 w-[520px] h-[520px] rounded-full blur-2xl
|
||
bg-gradient-to-tr from-cyan-100 via-blue-100 to-blue-200 opacity-20"
|
||
animate={reduce ? {} : { scale: [1, 1.08, 1] }}
|
||
transition={{ duration: 24, repeat: Infinity, ease: "easeInOut" }}
|
||
/>
|
||
</>
|
||
)}
|
||
|
||
{/* === B — Editorial (pasy + shine) === */}
|
||
{variant === "B" && (
|
||
<>
|
||
<div
|
||
aria-hidden
|
||
className="absolute inset-0"
|
||
style={{
|
||
background:
|
||
"radial-gradient(1200px 600px at 50% -200px, rgba(59,130,246,.08), transparent 60%)",
|
||
}}
|
||
/>
|
||
<div
|
||
aria-hidden
|
||
className="absolute inset-0 opacity-[0.06]"
|
||
style={{
|
||
backgroundImage:
|
||
"repeating-linear-gradient(135deg, rgba(14,165,233,.14) 0, rgba(14,165,233,.14) 1px, transparent 1px, transparent 14px)",
|
||
}}
|
||
/>
|
||
{!reduce && (
|
||
<motion.div
|
||
aria-hidden
|
||
className="absolute inset-y-0 -left-1/3 w-1/3 bg-gradient-to-r from-transparent via-white/35 to-transparent"
|
||
animate={{ x: ["-120%", "120%"] }}
|
||
transition={{ duration: 22, repeat: Infinity, ease: "linear" }}
|
||
/>
|
||
)}
|
||
</>
|
||
)}
|
||
|
||
{/* === C — Kinetic Grid + SCROLL-REACTIVE AURORA === */}
|
||
{variant === "C" && (
|
||
<>
|
||
<ScrollToneBackground />
|
||
<ScrollToneDecor reduce={reduce} />
|
||
</>
|
||
)}
|
||
|
||
|
||
|
||
|
||
|
||
{/* Ziarno (delikatne) */}
|
||
<div
|
||
aria-hidden
|
||
className="absolute inset-0 opacity-[0.04]"
|
||
style={{
|
||
backgroundImage: "url('/textures/noise.png')",
|
||
backgroundSize: "200px 200px",
|
||
}}
|
||
/>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
type ToneOpts = { wobbleSpeed?: number; wobbleAmpDeg?: number };
|
||
|
||
// PASTELOWE, JESZCZE JAŚNIEJSZE ODCIENIE
|
||
// start: bardzo jasny błękit, end: bardzo jasny morelowy
|
||
const START = { h: 208, s: 78, l: 96 }; // hsl(208 78% 96%)
|
||
const END = { h: 24 + 360, s: 82, l: 94 }; // 24° + 360° => unikamy zieleni
|
||
|
||
function useBrandTone({ wobbleSpeed = 0.001, wobbleAmpDeg = 0.2 }: ToneOpts = {}) {
|
||
const { scrollYProgress } = useScroll();
|
||
const time = useTime() as MotionValue<number>;
|
||
|
||
// SINGLE SWEEP: 0 -> 1 tylko raz
|
||
const phase = useSpring(scrollYProgress, { stiffness: 35, damping: 22, mass: 0.9 });
|
||
|
||
// H, S, L płynnie z START -> END (hue idzie po zegarze 208 -> 384)
|
||
const hBase = useTransform(phase, [0, 1], [START.h, END.h]);
|
||
const sBase = useTransform(phase, [0, 1], [START.s, END.s]);
|
||
const lBase = useTransform(phase, [0, 1], [START.l, END.l]);
|
||
|
||
// Bardzo subtelny „oddech” hue, żeby tło żyło, ale bez mrygania
|
||
const wobbleRaw = useTransform(time, t => Math.sin(t * wobbleSpeed * 1000) * wobbleAmpDeg);
|
||
const wobble = useSpring(wobbleRaw, { stiffness: 60, damping: 20 });
|
||
|
||
// top = baza + leciutki wobble (ułamki stopnia)
|
||
const hTop = useTransform([hBase, wobble] as MotionValue<number>[], (vals: number[]) => {
|
||
const [h, w] = vals as [number, number];
|
||
return h + w; // hue może przekraczać 360° – CSS to normalizuje
|
||
});
|
||
const sTop = sBase;
|
||
const lTop = lBase;
|
||
|
||
// dolna warstwa: odrobinkę mniej nasycona i ciut ciemniejsza (wciąż pastel)
|
||
const hBot = useTransform(hTop, v => v - 4);
|
||
const sBot = useTransform(sTop, v => Math.max(65, v - 8));
|
||
const lBot = useTransform(lTop, v => Math.max(88, v - 4));
|
||
|
||
// dwa środki – zawsze kilka odcieni, ale „kolor przewodni” zmienia się tylko raz
|
||
const mix2 = (a: MotionValue<number>, b: MotionValue<number>, wa: number) =>
|
||
useTransform([a, b] as MotionValue<number>[], (vals: number[]) => {
|
||
const [x, y] = vals as [number, number];
|
||
return x * wa + y * (1 - wa);
|
||
});
|
||
|
||
const hMid1 = mix2(hTop, hBot, 0.65);
|
||
const sMid1 = mix2(sTop, sBot, 0.65);
|
||
const lMid1 = mix2(lTop, lBot, 0.65);
|
||
|
||
const hMid2 = useTransform([hTop, hBot, wobble] as MotionValue<number>[], (vals: number[]) => {
|
||
const [a, b, w] = vals as [number, number, number];
|
||
return a * 0.35 + b * 0.65 + w * 0.15;
|
||
});
|
||
const sMid2 = mix2(sTop, sBot, 0.35);
|
||
const lMid2 = mix2(lTop, lBot, 0.35);
|
||
|
||
// kolory jako CSS-zmienne (bez migotania)
|
||
const c1 = useMotionTemplate`hsl(${hTop} ${sTop}% ${lTop}%)`;
|
||
const c2 = useMotionTemplate`hsl(${hMid1} ${sMid1}% ${lMid1}%)`;
|
||
const c3 = useMotionTemplate`hsl(${hMid2} ${sMid2}% ${lMid2}%)`;
|
||
const c4 = useMotionTemplate`hsl(${hBot} ${sBot}% ${lBot}%)`;
|
||
|
||
const accent = useMotionTemplate`hsl(${hBot} ${sBot}% calc(${lBot} - 10%))`;
|
||
const accentSoft = useMotionTemplate`hsl(${hBot} ${sBot}% calc(${lBot} - 10%) / .45)`;
|
||
const gridTint = useMotionTemplate`hsl(${hTop} ${sTop}% 46% / .16)`;
|
||
|
||
// ⬇⬇⬇ DODAJ scrollYProgress do return
|
||
return { scrollYProgress, c1, c2, c3, c4, accent, accentSoft, gridTint };
|
||
}
|
||
|
||
function ScrollToneBackground() {
|
||
const { c1, c2, c3, c4 } = useBrandTone();
|
||
|
||
return (
|
||
<motion.div
|
||
aria-hidden
|
||
className="absolute inset-0 z-[1] pointer-events-none"
|
||
style={
|
||
{
|
||
["--c1" as any]: c1,
|
||
["--c2" as any]: c2,
|
||
["--c3" as any]: c3,
|
||
["--c4" as any]: c4,
|
||
background:
|
||
"linear-gradient(180deg, var(--c1) 0%, var(--c2) 45%, var(--c3) 75%, var(--c4) 100%)",
|
||
transform: "translateZ(0)",
|
||
contain: "paint",
|
||
} as React.CSSProperties
|
||
}
|
||
/>
|
||
);
|
||
}
|
||
|
||
function ScrollToneDecor({ reduce = false }: { reduce?: boolean }) {
|
||
const { scrollYProgress, accent, accentSoft, gridTint } = useBrandTone();
|
||
const time = useTime() as MotionValue<number>;
|
||
|
||
const x1 = useTransform(scrollYProgress, [0, 1], [15, 70]);
|
||
const y1 = useTransform(scrollYProgress, [0, 1], [18, 34]);
|
||
const x2 = useTransform(scrollYProgress, [0, 1], [85, 28]);
|
||
const y2 = useTransform(scrollYProgress, [0, 1], [78, 62]);
|
||
|
||
const wobRaw = useTransform(time, t => Math.sin(t * 0.0016) * 4);
|
||
const wob = useSpring(wobRaw, { stiffness: 60, damping: 18 });
|
||
|
||
const orb1 = useMotionTemplate`
|
||
radial-gradient(880px 880px at calc(${x1}% + ${wob}) ${y1}%,
|
||
${accentSoft}, transparent 60%)`;
|
||
const orb2 = useMotionTemplate`
|
||
radial-gradient(1040px 1040px at ${x2}% calc(${y2}% + ${wob}),
|
||
${accentSoft}, transparent 58%)`;
|
||
|
||
const shineX = useSpring(
|
||
useTransform(scrollYProgress, [0, 1], ["-130%", "130%"]),
|
||
{ stiffness: 60, damping: 20 }
|
||
);
|
||
const shineBg = useMotionTemplate`linear-gradient(90deg, transparent, ${accentSoft}, transparent)`;
|
||
|
||
const spin = useTransform(time, t => (t * 0.012) % 360);
|
||
const rings = useMotionTemplate`
|
||
conic-gradient(from 0deg,
|
||
transparent 0deg, ${accentSoft} 24deg, transparent 48deg,
|
||
${accentSoft} 72deg, transparent 96deg, ${accentSoft} 120deg, transparent 360deg)`;
|
||
|
||
const gridImg = useMotionTemplate`
|
||
repeating-linear-gradient(0deg, transparent, transparent 23px, ${gridTint} 24px),
|
||
repeating-linear-gradient(90deg, transparent, transparent 23px, ${gridTint} 24px)`;
|
||
|
||
return (
|
||
<>
|
||
<motion.div aria-hidden className="absolute inset-0 z-[2] pointer-events-none" style={{ backgroundImage: orb1 }} />
|
||
<motion.div aria-hidden className="absolute inset-0 z-[2] pointer-events-none" style={{ backgroundImage: orb2 }} />
|
||
|
||
{!reduce && (
|
||
<motion.div
|
||
aria-hidden
|
||
className="absolute top-0 bottom-0 -left-1/3 w-1/3 z-[3] pointer-events-none"
|
||
style={{ x: shineX, background: shineBg, opacity: 0.5 }}
|
||
/>
|
||
)}
|
||
|
||
<motion.div aria-hidden className="absolute inset-0 z-[2] pointer-events-none" style={{ background: rings, opacity: 0.08, rotate: spin }} />
|
||
<motion.div aria-hidden className="absolute inset-0 z-[2] opacity-[0.09] pointer-events-none" style={{ backgroundImage: gridImg }} />
|
||
</>
|
||
);
|
||
} |