new creative section in services single

This commit is contained in:
haniyeroozmand
2026-05-10 11:15:08 +03:30
parent aecfe43d79
commit ae6bbcd78c
5 changed files with 497 additions and 68 deletions

26
package-lock.json generated
View File

@@ -10,6 +10,7 @@
"dependencies": {
"framer-motion": "^12.38.0",
"lucide-react": "^1.14.0",
"motion": "^12.0.6",
"next": "16.2.5",
"next-themes": "^0.4.6",
"react": "19.2.4",
@@ -1358,6 +1359,31 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/motion": {
"version": "12.38.0",
"resolved": "https://registry.npmjs.org/motion/-/motion-12.38.0.tgz",
"integrity": "sha512-uYfXzeHlgThchzwz5Te47dlv5JOUC7OB4rjJ/7XTUgtBZD8CchMN8qEJ4ZVsUmTyYA44zjV0fBwsiktRuFnn+w==",
"dependencies": {
"framer-motion": "^12.38.0",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/motion-dom": {
"version": "12.38.0",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz",

View File

@@ -10,6 +10,7 @@
"dependencies": {
"framer-motion": "^12.38.0",
"lucide-react": "^1.14.0",
"motion": "^12.0.6",
"next": "16.2.5",
"next-themes": "^0.4.6",
"react": "19.2.4",

View File

@@ -10,6 +10,9 @@ import {
Cloud,
Database,
Gauge,
HandCoins,
MousePointerClick,
BarChart3,
Headphones,
Rocket,
ShieldCheck,
@@ -18,6 +21,7 @@ import {
import { useI18n } from '../../../../i18n/provider';
import { serviceContent, type ServiceSlug } from './services.data';
import { useTheme } from 'next-themes';
import { NetworkNexus } from '../../../components/network-nexus';
function HeroIcon({ name }: { name: 'shield' | 'wp' | 'speed' | 'support' }) {
const className =
@@ -82,12 +86,12 @@ export default function ServiceDetailPage({ slug }: { slug: ServiceSlug }) {
const { locale } = useI18n();
const content = useMemo(() => serviceContent[slug][locale], [slug, locale]);
const dir = locale === 'en' ? 'ltr' : 'rtl';
const [openFaq, setOpenFaq] = useState<number>(0);
const { resolvedTheme } = useTheme();
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
const sectionMotion = {
initial: { opacity: 0, y: 24 },
whileInView: { opacity: 1, y: 0 },
@@ -304,6 +308,9 @@ export default function ServiceDetailPage({ slug }: { slug: ServiceSlug }) {
</div>
</motion.section>
{/* Network architecture (Core hub) */}
<NetworkNexus />
{/* Plans */}
<motion.section {...sectionMotion} className="max-w-7xl mx-auto px-6 lg:px-10 pt-24">
<div className="text-center mb-12">
@@ -358,78 +365,74 @@ export default function ServiceDetailPage({ slug }: { slug: ServiceSlug }) {
</div>
</motion.section>
{/* FAQ + Why */}
{/* FAQ (new layout) */}
<motion.section {...sectionMotion} className="max-w-7xl mx-auto px-6 lg:px-10 pt-24">
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6 items-stretch">
<div className="lg:col-span-7 rounded-3xl border border-[color:var(--border-10)] bg-[color:var(--glass-04)] p-6 lg:p-8">
<h2 className="text-xl lg:text-2xl font-bold mb-2">{content.faqTitle}</h2>
<p className="text-sm text-[color:var(--text-muted)] mb-6">{content.faqSubtitle}</p>
<div className="text-center mb-10">
<h2 className="text-2xl sm:text-3xl font-bold text-[color:var(--text-primary)]">
<span className="faq-title-bars">{content.faqTitle}</span>
</h2>
<p className="mt-3 text-sm sm:text-base text-[color:var(--text-muted)]">{content.faqSubtitle}</p>
</div>
<div className="space-y-3">
{content.faqs.map((f, idx) => {
const isOpen = openFaq === idx;
return (
<div
key={f.q}
className="rounded-2xl border border-[color:var(--border-10)] bg-[color:var(--glass-02)] overflow-hidden"
>
<button
type="button"
onClick={() => setOpenFaq(isOpen ? -1 : idx)}
className="w-full flex items-center justify-between gap-4 px-4 py-4 text-right en-text-left"
>
<span className="text-sm font-semibold">{f.q}</span>
<ChevronDown
className={[
'w-5 h-5 text-[color:var(--text-muted-2)] transition-transform',
isOpen ? 'rotate-180' : 'rotate-0',
].join(' ')}
/>
</button>
{isOpen ? (
<div className="px-4 pb-4 text-sm text-[color:var(--text-muted)] leading-relaxed">
{f.a}
</div>
) : null}
<div className="relative max-w-5xl mx-auto">
{/* Timeline spine (desktop) */}
<div className="hidden md:block absolute right-[132px] top-0 bottom-0 w-px bg-gradient-to-b from-transparent via-[#80c8f5]/35 to-transparent" />
<div className="space-y-4">
{content.faqs.map((f, idx) => {
const Icon = [HandCoins, Gauge, ShieldCheck, MousePointerClick, BarChart3][idx % 5];
const num = String(idx + 1).padStart(2, '0');
const glow = ['#80c8f5', '#a78bfa', '#84e1bc', '#60a5fa', '#22d3ee'][idx % 5];
return (
<div key={f.q} className="grid grid-cols-1 md:grid-cols-[1fr_220px] gap-5 items-stretch">
<div className="relative rounded-2xl overflow-hidden border border-[color:var(--border-10)] bg-[color:var(--glass-04)] backdrop-blur-md">
<div className="absolute inset-0 pointer-events-none opacity-70">
<div className="absolute inset-0 bg-gradient-to-b from-white/5 via-transparent to-transparent" />
<div className="absolute inset-y-0 right-0 w-px bg-gradient-to-b from-transparent via-white/10 to-transparent" />
</div>
<div className="relative px-6 py-5">
<div className="text-base sm:text-lg font-bold text-[color:var(--text-primary)] mb-2">{f.q}</div>
<div className="text-sm text-[color:var(--text-muted)] leading-relaxed">{f.a}</div>
</div>
</div>
);
})}
</div>
</div>
<div className="lg:col-span-5 rounded-3xl border border-[color:var(--border-10)] bg-[color:var(--glass-04)] p-6 lg:p-8 relative overflow-hidden">
<div className="absolute inset-0 opacity-70 pointer-events-none">
<div className="absolute -top-24 -right-24 w-72 h-72 bg-[#84e1bc]/10 blur-3xl rounded-full" />
<div className="absolute -bottom-24 -left-24 w-72 h-72 bg-[#80c8f5]/10 blur-3xl rounded-full" />
</div>
<div className="hidden md:flex items-center justify-end relative">
{/* Dot on spine */}
<span
className="absolute right-[132px] w-2 h-2 rounded-full"
style={{ backgroundColor: glow, boxShadow: `0 0 16px ${glow}aa` }}
/>
{/* Connector from dot to badge */}
<span
className="absolute right-[132px] h-px w-[44px]"
style={{
background: `linear-gradient(to left, ${glow}99, transparent)`,
}}
/>
<div className="relative">
<h3 className="text-xl lg:text-2xl font-bold mb-3">{content.whyTitle}</h3>
<p className="text-sm text-[color:var(--text-muted)] leading-relaxed mb-5">{content.whyText}</p>
<ul className="space-y-3 text-sm text-[color:var(--text-muted)]">
{content.whyBullets.map((b) => (
<li key={b} className="flex items-center gap-2">
<span className="w-5 h-5 rounded-full bg-[#84e1bc]/15 border border-[#84e1bc]/25 flex items-center justify-center">
<Check className="w-3.5 h-3.5 text-[#84e1bc]" />
</span>
<span className="en-text-left">{b}</span>
</li>
))}
</ul>
<div className="flex items-center gap-3 pr-2">
<div className="relative w-[66px] h-[66px] hex-clip gradient-border-anim bg-[color:var(--glass-04)]">
<div className="absolute inset-[1.5px] hex-clip bg-[color:var(--global-surface-bg-80)] border border-[color:var(--border-05)] flex items-center justify-center">
<Icon className="w-6 h-6" style={{ color: glow }} />
</div>
</div>
<div className="mt-8 relative aspect-[16/10] rounded-2xl overflow-hidden border border-[color:var(--border-10)] bg-white dark:bg-[color:var(--glass-02)]">
<Image
src={mounted && resolvedTheme === 'light' ? '/images/services-hero-light.png' : '/images/services-hero-dark.png'}
alt=""
fill
unoptimized
quality={100}
sizes="(min-width: 1024px) 35vw, 100vw"
className="object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/40 to-transparent dark:from-black/50" />
</div>
</div>
<span
className="h-px w-[18px]"
style={{
background: `linear-gradient(to left, ${glow}66, rgba(255,255,255,0.06))`,
}}
/>
<div className="w-[62px] h-[62px] rounded-2xl border border-[color:var(--border-10)] bg-[color:var(--glass-04)] backdrop-blur-md flex items-center justify-center text-[color:var(--text-primary)] font-bold text-lg">
{num}
</div>
</div>
</div>
</div>
);
})}
</div>
</div>
</motion.section>

View File

@@ -0,0 +1,295 @@
import React, { useState } from 'react';
import { motion, AnimatePresence } from 'motion/react';
import {
Server, Cloud, Shield, HardDrive, Globe, Cpu,
Activity, Lock, Zap
} from 'lucide-react';
const SERVICES = [
{
id: 'cloud',
title: 'هاست ابری اختصاصی',
desc: 'پایداری ۹۹.۹۹٪ با زیرساخت تمام ابری و هاردهای NVMe Enterprise',
icon: Cloud,
color: '#3b82f6',
x: 0,
y: -220,
stats: [
{ label: 'Uptime', value: '99.99%' },
{ label: 'Storage', value: 'NVMe SSD' }
]
},
{
id: 'dedicated',
title: 'سرور اختصاصی',
desc: 'بالاترین قدرت پردازشی با <20><>رورهای فیزیکی اختصاصی در بهترین دیتاسنترها',
icon: Server,
color: '#ef4444',
x: -300,
y: -100,
stats: [
{ label: 'CPU', value: 'Intel/AMD Dual' },
{ label: 'Network', value: '10 Gbps' }
]
},
{
id: 'vps',
title: 'سرور مجازی (VPS)',
desc: 'منابع کاملاً اختصاصی، کنترل کامل روت و تنوع سیستم‌عامل‌ها',
icon: HardDrive,
color: '#10b981',
x: 300,
y: -100,
stats: [
{ label: 'RAM', value: 'Up to 128GB' },
{ label: 'Root Access', value: 'Full' }
]
},
{
id: 'security',
title: 'امنیت و آنتی دیداس',
desc: 'محافظت هوشمند لایه ۷ در برابر بزرگترین حملات سایبری',
icon: Shield,
color: '#8b5cf6',
x: -220,
y: 160,
stats: [
{ label: 'DDoS Protection', value: 'Up to 2Tbps' },
{ label: 'WAF', value: 'Enabled' }
]
},
{
id: 'cdn',
title: 'شبکه توزیع محتوا (CDN)',
desc: 'بارگذاری در کسری از ثانیه با توزیع جهانی اطلاعات در سرورهای لبه',
icon: Globe,
color: '#f59e0b',
x: 220,
y: 160,
stats: [
{ label: 'Global PoPs', value: '150+' },
{ label: 'Latency', value: '< 20ms' }
]
},
];
export function NetworkNexus() {
const [activeService, setActiveService] = useState<string | null>(null);
return (
<section className="relative min-h-[120vh] flex flex-col items-center justify-center py-20 overflow-hidden bg-[#030014]" dir="rtl">
{/* Background Grid Pattern */}
<div
className="absolute inset-0 opacity-10"
style={{
backgroundImage: 'radial-gradient(#4f46e5 1px, transparent 1px)',
backgroundSize: '40px 40px',
backgroundPosition: '-19px -19px'
}}
/>
<div className="text-center mb-16 z-20">
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
whileInView={{ opacity: 1, scale: 1 }}
className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-indigo-500/10 border border-indigo-500/30 text-indigo-400 text-sm mb-6"
>
<Activity className="w-4 h-4" />
<span>زیرساخت ابری هوشمند و یکپارچه</span>
</motion.div>
<h2 className="text-4xl md:text-5xl font-bold mb-6 text-transparent bg-clip-text bg-gradient-to-r from-white via-indigo-200 to-gray-400">
معماری شبکـه و سرورهـا
</h2>
<p className="text-gray-400 text-lg max-w-2xl mx-auto leading-relaxed">
یک شبکه عظیم و به هم پیوسته از قدرتمندترین سرورهای جهان؛ پایداری بینظیر، امنیت لایهای و سرعت حداکثری برای کسبوکار شما.
</p>
</div>
<div className="relative flex items-center justify-center w-full max-w-[1000px] h-[600px] mt-10 scale-[0.6] sm:scale-75 md:scale-100 transition-transform duration-500">
{/* SVG Connections */}
<svg
className="absolute inset-0 w-full h-full pointer-events-none"
viewBox="-500 -300 1000 600"
preserveAspectRatio="xMidYMid meet"
>
<defs>
<radialGradient id="coreGlow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stopColor="rgba(99, 102, 241, 0.4)" />
<stop offset="100%" stopColor="rgba(99, 102, 241, 0)" />
</radialGradient>
</defs>
{/* Central Glow Effect */}
<circle cx="0" cy="0" r="150" fill="url(#coreGlow)" className="animate-pulse" />
{/* Static paths and animated data streams */}
{SERVICES.map((service) => {
// Cubic bezier curve path for a "cable" look
const pathD = `M 0 0 C ${service.x * 0.5} 0, ${service.x * 0.8} ${service.y * 0.8}, ${service.x} ${service.y}`;
const isActive = activeService === service.id;
const opacity = activeService ? (isActive ? 1 : 0.2) : 0.6;
return (
<g key={`path-${service.id}`} className="transition-opacity duration-300" style={{ opacity }}>
{/* Background cable */}
<path
d={pathD}
fill="none"
stroke="rgba(255,255,255,0.05)"
strokeWidth="2"
/>
{/* Glowing packet stream */}
<motion.path
d={pathD}
fill="none"
stroke={service.color}
strokeWidth="3"
strokeDasharray="4 30"
animate={{
strokeDashoffset: isActive ? [-100, 0] : [100, 0]
}}
transition={{
duration: isActive ? 1 : 3,
repeat: Infinity,
ease: "linear"
}}
style={{
filter: `drop-shadow(0 0 8px ${service.color})`
}}
/>
</g>
);
})}
</svg>
{/* Central Hub (Core Data Center) */}
<div
className="absolute z-30 flex flex-col items-center justify-center w-40 h-40 rounded-3xl bg-[#0a0a1a] border border-indigo-500/50 shadow-[0_0_80px_rgba(99,102,241,0.4)]"
style={{ transform: 'translate(-50%, -50%)', left: '50%', top: '50%' }}
>
<div className="absolute inset-0 rounded-3xl border-2 border-indigo-500/30 animate-ping opacity-20" />
<Cpu className="w-16 h-16 text-indigo-400 mb-2" />
<div className="flex gap-1 mb-2">
{[1,2,3].map(i => (
<motion.div
key={i}
animate={{ opacity: [0.3, 1, 0.3] }}
transition={{ duration: 1.5, repeat: Infinity, delay: i * 0.2 }}
className="w-2 h-2 rounded-full bg-indigo-400 shadow-[0_0_5px_#818cf8]"
/>
))}
</div>
<span className="font-bold text-white text-sm tracking-widest">CORE HUB</span>
</div>
{/* Service Nodes */}
{SERVICES.map((service) => {
const isActive = activeService === service.id;
const isFaded = activeService && !isActive;
return (
<div
key={`node-${service.id}`}
className="absolute z-40 transition-all duration-500"
style={{
transform: `translate(-50%, -50%)`,
left: `calc(50% + ${service.x}px)`,
top: `calc(50% + ${service.y}px)`,
opacity: isFaded ? 0.4 : 1,
scale: isActive ? 1.1 : (isFaded ? 0.9 : 1),
}}
onMouseEnter={() => setActiveService(service.id)}
onMouseLeave={() => setActiveService(null)}
>
<div
className="relative group cursor-pointer"
>
{/* Icon Container */}
<div
className="relative flex items-center justify-center w-20 h-20 rounded-2xl bg-[#0f0f29] border-2 transition-all duration-300 overflow-hidden"
style={{
borderColor: isActive ? service.color : 'rgba(255,255,255,0.1)',
boxShadow: isActive ? `0 0 30px ${service.color}60` : `0 0 15px rgba(0,0,0,0.5)`,
}}
>
{/* Hover gradient background inside the node */}
<div
className="absolute inset-0 opacity-0 group-hover:opacity-20 transition-opacity duration-300"
style={{ background: `radial-gradient(circle at center, ${service.color}, transparent)` }}
/>
<service.icon
size={32}
className="transition-colors duration-300"
style={{ color: isActive ? '#fff' : service.color }}
/>
</div>
{/* Floating Info Card (Shows on Hover/Active) */}
<AnimatePresence>
{isActive && (
<motion.div
initial={{ opacity: 0, y: 10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 5, scale: 0.95 }}
transition={{ duration: 0.2 }}
className="absolute top-24 left-1/2 -translate-x-1/2 w-[300px] p-5 rounded-2xl bg-[#13132f]/95 backdrop-blur-xl border border-white/10 shadow-2xl z-50 pointer-events-none"
>
<h4 className="text-xl font-bold mb-2 text-white flex items-center gap-2">
<span className="w-2 h-2 rounded-full" style={{ backgroundColor: service.color, boxShadow: `0 0 10px ${service.color}` }} />
{service.title}
</h4>
<p className="text-sm text-gray-400 mb-4 leading-relaxed">
{service.desc}
</p>
<div className="space-y-2 pt-3 border-t border-white/5">
{service.stats.map((stat, idx) => (
<div key={idx} className="flex justify-between items-center text-xs">
<span className="text-gray-500 font-mono">{stat.label}</span>
<span className="text-indigo-300 font-bold bg-indigo-500/10 px-2 py-1 rounded">
{stat.value}
</span>
</div>
))}
</div>
</motion.div>
)}
</AnimatePresence>
{/* Label (when not hovered) */}
<div
className={`absolute -bottom-8 left-1/2 -translate-x-1/2 whitespace-nowrap text-sm font-medium transition-opacity duration-300 ${isActive ? 'opacity-0' : 'opacity-100 text-gray-300'}`}
>
{service.title}
</div>
</div>
</div>
);
})}
</div>
{/* Decorative Server Rack Elements on the sides */}
<div className="hidden lg:flex absolute right-4 top-1/2 -translate-y-1/2 flex-col gap-2 opacity-20 pointer-events-none">
{[1,2,3,4,5].map(i => (
<div key={i} className="w-16 h-8 border border-white/20 rounded bg-white/5 flex items-center px-2 gap-1">
<div className="w-1 h-1 rounded-full bg-green-500 animate-pulse" style={{ animationDelay: `${i*0.3}s` }}/>
<div className="w-1 h-1 rounded-full bg-blue-500" />
</div>
))}
</div>
<div className="hidden lg:flex absolute left-4 top-1/2 -translate-y-1/2 flex-col gap-2 opacity-20 pointer-events-none">
{[1,2,3,4,5].map(i => (
<div key={i} className="w-16 h-8 border border-white/20 rounded bg-white/5 flex items-center justify-end px-2 gap-1">
<div className="w-1 h-1 rounded-full bg-green-500 animate-pulse" style={{ animationDelay: `${i*0.5}s` }}/>
<div className="w-1 h-1 rounded-full bg-blue-500" />
</div>
))}
</div>
</section>
);
}

View File

@@ -28,7 +28,7 @@
}
}
@keyframes rb-fade-in {
@keyframes rb-fade-in {
from {
opacity: 0;
}
@@ -93,6 +93,110 @@ html.light .glass-rotating-border::after {
filter: blur(16px);
}
/* Animated gradient border (FAQ cards / hex badges) */
@property --gb-angle {
syntax: '<angle>';
inherits: false;
initial-value: 0deg;
}
@keyframes gb-spin {
to {
--gb-angle: 360deg;
}
}
.gradient-border-anim {
position: relative;
}
.gradient-border-anim::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1.5px;
pointer-events: none;
background: conic-gradient(
from var(--gb-angle),
rgba(255, 255, 255, 0) 0deg,
rgba(128, 200, 245, 0.55) 70deg,
rgba(167, 139, 250, 0.50) 145deg,
rgba(132, 225, 188, 0.50) 225deg,
rgba(34, 211, 238, 0.45) 305deg,
rgba(255, 255, 255, 0) 360deg
);
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
filter: drop-shadow(0 0 18px rgba(128, 200, 245, 0.10));
animation: gb-spin 6.5s linear infinite;
}
.gradient-border-anim::after {
content: '';
position: absolute;
inset: -10px;
border-radius: inherit;
pointer-events: none;
background: conic-gradient(
from var(--gb-angle),
rgba(255, 255, 255, 0) 0deg,
rgba(128, 200, 245, 0.18) 90deg,
rgba(167, 139, 250, 0.14) 170deg,
rgba(132, 225, 188, 0.14) 250deg,
rgba(34, 211, 238, 0.12) 330deg,
rgba(255, 255, 255, 0) 360deg
);
filter: blur(18px);
opacity: 0.65;
animation: gb-spin 6.5s linear infinite;
}
html.light .gradient-border-anim::before {
filter: drop-shadow(0 0 14px rgba(2, 6, 23, 0.06));
}
.hex-clip {
clip-path: polygon(25% 6%, 75% 6%, 100% 50%, 75% 94%, 25% 94%, 0% 50%);
}
.faq-title-bars {
display: inline-flex;
align-items: center;
gap: 14px;
}
.faq-title-bars::before,
.faq-title-bars::after {
content: '';
width: 44px;
height: 10px;
background:
linear-gradient(90deg, rgba(128,200,245,0) 0%,
rgba(128,200,245,0) 22%,
rgba(167,139,250,0.85) 22%,
rgba(167,139,250,0.85) 34%,
rgba(128,200,245,0) 34%,
rgba(128,200,245,0) 44%,
rgba(128,200,245,0.85) 44%,
rgba(128,200,245,0.85) 56%,
rgba(128,200,245,0) 56%,
rgba(128,200,245,0) 66%,
rgba(96,165,250,0.85) 66%,
rgba(96,165,250,0.85) 78%,
rgba(128,200,245,0) 78%,
rgba(128,200,245,0) 100%);
-webkit-mask: linear-gradient(135deg, #000 0 0);
mask: linear-gradient(135deg, #000 0 0);
transform: skewX(-18deg);
opacity: 0.9;
}
.faq-title-bars::after {
transform: scaleX(-1) skewX(-18deg);
}
html.light .glass-rotating-border::before {
background: conic-gradient(
rgba(255, 255, 255, 0) 0deg 28deg,