diff --git a/client/src/components/home/AboutSection.jsx b/client/src/components/home/AboutSection.jsx deleted file mode 100644 index 77fe50b..0000000 --- a/client/src/components/home/AboutSection.jsx +++ /dev/null @@ -1,126 +0,0 @@ -import { useRef, useEffect } from 'react'; -import anime from 'animejs'; -import FeatureCard from './FeatureCard'; -import styles from './AboutSection.module.css'; - -export default function AboutSection() { - const sectionRef = useRef(null); - const titleRef = useRef(null); - const introRef = useRef(null); - const cardsRef = useRef(null); - const hasAnimated = useRef(false); - - useEffect(() => { - const observer = new IntersectionObserver( - (entries) => { - entries.forEach((entry) => { - if (entry.isIntersecting && !hasAnimated.current) { - hasAnimated.current = true; - - // Animate title - anime({ - targets: titleRef.current, - translateY: [40, 0], - opacity: [0, 1], - duration: 800, - easing: 'easeOutExpo', - }); - - // Animate intro text - anime({ - targets: introRef.current, - translateY: [30, 0], - opacity: [0, 1], - duration: 800, - delay: 200, - easing: 'easeOutExpo', - }); - - // Cards will animate via CSS with stagger - anime({ - targets: cardsRef.current, - opacity: [0, 1], - duration: 600, - delay: 400, - easing: 'easeOutExpo', - }); - - observer.unobserve(entry.target); - } - }); - }, - { threshold: 0.2 } - ); - - if (sectionRef.current) { - observer.observe(sectionRef.current); - } - - return () => observer.disconnect(); - }, []); - - const features = [ - { - icon: '🔐', - title: 'Password Security', - description: 'Learn to create strong passwords and understand common vulnerabilities through interactive challenges.', - }, - { - icon: '🛡️', - title: 'Privacy Protection', - description: 'Master data protection techniques and learn how to keep your personal information safe online.', - }, - { - icon: '🎮', - title: 'Gesture Controls', - description: 'Navigate through challenges using hand gestures powered by MediaPipe technology.', - }, - { - icon: '🤖', - title: 'AI Companions', - description: 'Collect unique AI-generated robot companions to help you on your cybersecurity journey.', - }, - { - icon: '🏆', - title: 'Earn Rewards', - description: 'Gain points, coins, badges, and unlock new areas as you master security concepts.', - }, - { - icon: '🌐', - title: 'Immersive 3D World', - description: 'Explore cyberpunk environments built with Three.js in a visually stunning game world.', - }, - ]; - - return ( -
- {/* Forest Silhouette Background */} -
-
- -
-

- What is SafeForest? -

- -

- SafeForest is a digital forest sanctuary where cybersecurity education - meets immersive gameplay. Transform complex security concepts into engaging challenges - while exploring a world where technology and nature harmoniously blend. -

- -
- {features.map((feature, index) => ( - - ))} -
-
-
- ); -} diff --git a/client/src/components/home/AboutSection.module.css b/client/src/components/home/AboutSection.module.css deleted file mode 100644 index 7003885..0000000 --- a/client/src/components/home/AboutSection.module.css +++ /dev/null @@ -1,211 +0,0 @@ -/* ===== ABOUT SECTION - DIGITAL FOREST SANCTUARY ===== */ -/* Cyber-Forest Sanctuary Theme */ - -.aboutSection { - position: relative; - min-height: 100vh; - display: flex; - align-items: center; - justify-content: center; - padding: 120px 24px; - background: var(--neo-bg); - overflow: hidden; -} - -/* Forest Silhouette Background */ -.forestBackground { - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 300px; - background: linear-gradient( - to top, - rgba(0, 255, 159, 0.05) 0%, - transparent 100% - ); - opacity: 0.3; - z-index: 0; -} - -.forestBackground::before { - content: ''; - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 150px; - background: repeating-linear-gradient( - 90deg, - transparent, - transparent 40px, - rgba(0, 255, 159, 0.1) 40px, - rgba(0, 255, 159, 0.1) 42px - ); - clip-path: polygon( - 0% 100%, - 5% 60%, - 8% 100%, - 12% 50%, - 15% 100%, - 20% 40%, - 23% 100%, - 28% 55%, - 32% 100%, - 36% 45%, - 40% 100%, - 44% 60%, - 48% 100%, - 52% 50%, - 56% 100%, - 60% 40%, - 64% 100%, - 68% 55%, - 72% 100%, - 76% 45%, - 80% 100%, - 84% 60%, - 88% 100%, - 92% 50%, - 95% 100%, - 100% 100% - ); -} - -/* Circuit Pattern Overlay */ -.circuitPattern { - position: absolute; - inset: 0; - background-image: - linear-gradient(var(--neo-border) 1px, transparent 1px), - linear-gradient(90deg, var(--neo-border) 1px, transparent 1px); - background-size: 50px 50px; - opacity: 0.03; - z-index: 0; - animation: circuitMove 20s linear infinite; -} - -@keyframes circuitMove { - from { - background-position: 0 0, 0 0; - } - to { - background-position: 50px 50px, 50px 50px; - } -} - -/* Container */ -.container { - position: relative; - max-width: 1300px; - width: 100%; - margin: 0 auto; - z-index: 1; -} - -/* Section Title */ -.sectionTitle { - font-family: 'Orbitron', sans-serif; - font-size: clamp(42px, 6vw, 72px); - font-weight: 900; - color: var(--neo-text); - text-align: center; - margin: 0 0 32px; - letter-spacing: -1px; - text-transform: uppercase; - text-shadow: 0 4px 30px rgba(0, 0, 0, 0.3); -} - -.titleHighlight { - background: linear-gradient( - 135deg, - var(--forest-emerald), - var(--portal-cyan), - var(--portal-purple) - ); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - filter: drop-shadow(0 0 30px rgba(0, 255, 159, 0.5)); - font-weight: 900; -} - -/* Intro Text */ -.intro { - font-family: 'Fredoka', sans-serif; - font-size: clamp(16px, 2.2vw, 20px); - font-weight: 400; - color: var(--neo-text-dim); - text-align: center; - max-width: 900px; - margin: 0 auto 80px; - line-height: 1.9; - letter-spacing: 0.2px; -} - -.intro strong { - color: var(--neo-text); - font-weight: 600; - background: linear-gradient(135deg, var(--forest-emerald), var(--portal-cyan)); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -/* Features Grid */ -.featuresGrid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); - gap: 32px; - margin-top: 80px; - opacity: 0; -} - -/* Accessibility: Reduced Motion */ -@media (prefers-reduced-motion: reduce) { - .circuitPattern { - animation: none; - } -} - -/* Responsive Design */ -@media (max-width: 1024px) { - .featuresGrid { - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); - gap: 24px; - } -} - -@media (max-width: 768px) { - .aboutSection { - padding: 100px 20px; - } - - .intro { - margin-bottom: 60px; - } - - .featuresGrid { - grid-template-columns: 1fr; - gap: 20px; - margin-top: 60px; - } - - .forestBackground { - height: 200px; - } - - .forestBackground::before { - height: 100px; - } -} - -@media (max-width: 480px) { - .aboutSection { - padding: 80px 16px; - } - - .circuitPattern { - display: none; - } -} diff --git a/client/src/components/home/DemoSection.jsx b/client/src/components/home/DemoSection.jsx deleted file mode 100644 index 53ffe1a..0000000 --- a/client/src/components/home/DemoSection.jsx +++ /dev/null @@ -1,75 +0,0 @@ -import { useRef, useEffect } from 'react'; -import anime from 'animejs'; -import styles from './DemoSection.module.css'; - -export default function DemoSection() { - const sectionRef = useRef(null); - const videoRef = useRef(null); - const titleRef = useRef(null); - const hasAnimated = useRef(false); - - useEffect(() => { - // Intersection Observer for scroll animation - const observer = new IntersectionObserver( - (entries) => { - entries.forEach((entry) => { - if (entry.isIntersecting && !hasAnimated.current) { - hasAnimated.current = true; - - anime({ - targets: titleRef.current, - translateY: [40, 0], - opacity: [0, 1], - duration: 800, - easing: 'easeOutExpo', - }); - - anime({ - targets: videoRef.current, - translateY: [60, 0], - opacity: [0, 1], - duration: 1000, - delay: 200, - easing: 'easeOutExpo', - }); - - observer.unobserve(entry.target); - } - }); - }, - { threshold: 0.2 } - ); - - if (sectionRef.current) { - observer.observe(sectionRef.current); - } - - return () => observer.disconnect(); - }, []); - - return ( -
-
-

- See It In Action -

- -
- {/* Video placeholder - replace with actual video later */} -
-
- -
-

Demo Video Coming Soon

-
-
- -

- Watch how players navigate through cyberpunk environments, solve security - challenges using hand gestures, and earn rewards for mastering digital - safety skills. -

-
-
- ); -} diff --git a/client/src/components/home/DemoSection.module.css b/client/src/components/home/DemoSection.module.css deleted file mode 100644 index 093384f..0000000 --- a/client/src/components/home/DemoSection.module.css +++ /dev/null @@ -1,287 +0,0 @@ -/* ===== DEMO SECTION - TERMINAL/MONITOR FRAME ===== */ -/* Cyber-Forest Sanctuary Theme */ - -.demoSection { - position: relative; - min-height: 100vh; - display: flex; - align-items: center; - justify-content: center; - padding: 100px 24px; - background: var(--neo-bg); - overflow: hidden; -} - -/* Data Stream Background */ -.demoSection::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 100%; - background: repeating-linear-gradient( - 0deg, - transparent, - transparent 2px, - var(--neo-border) 2px, - var(--neo-border) 3px - ); - opacity: 0.02; - animation: dataStream 20s linear infinite; - z-index: 0; -} - -@keyframes dataStreamMove { - from { - background-position: 0 0; - } - to { - background-position: 0 100px; - } -} - -.container { - position: relative; - max-width: 1200px; - width: 100%; - margin: 0 auto; - z-index: 1; -} - -.sectionTitle { - font-family: 'Orbitron', sans-serif; - font-size: clamp(42px, 6vw, 68px); - font-weight: 900; - color: var(--neo-text); - text-align: center; - margin: 0 0 60px; - background: linear-gradient(135deg, var(--portal-cyan), var(--portal-purple)); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - letter-spacing: -0.8px; - text-transform: uppercase; - filter: drop-shadow(0 0 30px rgba(0, 242, 255, 0.3)); -} - -/* Terminal/Monitor Frame */ -.videoWrapper { - position: relative; - width: 100%; - max-width: 950px; - margin: 0 auto 48px; - border-radius: 24px; - overflow: visible; -} - -.videoWrapper::before { - content: ''; - position: absolute; - inset: -4px; - border-radius: 24px; - background: linear-gradient( - 135deg, - var(--portal-cyan), - var(--portal-purple), - var(--forest-emerald) - ); - background-size: 300% 300%; - animation: borderFlow 6s ease infinite; - z-index: -1; - filter: blur(8px); -} - -/* Terminal Header Bar */ -.videoWrapper::after { - content: '● ● ●'; - position: absolute; - top: -40px; - left: 20px; - font-size: 16px; - color: var(--neo-text-dim); - letter-spacing: 8px; - font-family: 'Orbitron', sans-serif; -} - -.videoPlaceholder { - position: relative; - width: 100%; - aspect-ratio: 16 / 9; - background: var(--glass-bg); - backdrop-filter: blur(var(--glass-blur)); - -webkit-backdrop-filter: blur(var(--glass-blur)); - border: 2px solid var(--neo-border); - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 24px; - border-radius: 20px; - overflow: hidden; - box-shadow: - 0 0 40px rgba(0, 242, 255, 0.2), - 0 20px 60px rgba(0, 0, 0, 0.3), - inset 0 0 40px rgba(0, 242, 255, 0.05); -} - -/* Scanline Effect */ -.videoPlaceholder::before { - content: ''; - position: absolute; - inset: 0; - background: repeating-linear-gradient( - 0deg, - rgba(0, 0, 0, 0.05), - rgba(0, 0, 0, 0.05) 2px, - transparent 2px, - transparent 4px - ); - pointer-events: none; - z-index: 2; -} - -/* Terminal Grid Background */ -.videoPlaceholder::after { - content: ''; - position: absolute; - inset: 0; - background-image: - linear-gradient(var(--neo-border) 1px, transparent 1px), - linear-gradient(90deg, var(--neo-border) 1px, transparent 1px); - background-size: 40px 40px; - opacity: 0.05; - pointer-events: none; -} - -.playButton { - position: relative; - width: 90px; - height: 90px; - border-radius: 50%; - background: linear-gradient(135deg, var(--portal-cyan), var(--portal-purple)); - display: grid; - place-items: center; - cursor: pointer; - transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); - z-index: 3; - box-shadow: - 0 0 40px rgba(0, 242, 255, 0.5), - 0 0 60px rgba(112, 0, 255, 0.4); -} - -.playButton::before { - content: ''; - position: absolute; - inset: -6px; - border-radius: 50%; - background: linear-gradient(135deg, var(--portal-cyan), var(--portal-purple)); - z-index: -1; - animation: portalPulse 3s ease-in-out infinite; - opacity: 0.6; -} - -.playButton:hover { - transform: scale(1.15); - box-shadow: - 0 0 60px rgba(0, 242, 255, 0.7), - 0 0 100px rgba(112, 0, 255, 0.6); -} - -.playIcon { - font-size: 36px; - color: white; - margin-left: 6px; - filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3)); -} - -.placeholderText { - font-family: 'Orbitron', sans-serif; - font-size: 24px; - font-weight: 700; - color: var(--neo-text); - margin: 0; - letter-spacing: 0.5px; - text-transform: uppercase; - position: relative; - z-index: 3; - text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3); -} - -.description { - font-family: 'Fredoka', sans-serif; - font-size: clamp(15px, 2vw, 19px); - font-weight: 400; - color: var(--neo-text-dim); - text-align: center; - max-width: 800px; - margin: 0 auto; - line-height: 1.9; - letter-spacing: 0.2px; -} - -/* Accessibility: Reduced Motion */ -@media (prefers-reduced-motion: reduce) { - .demoSection::before, - .videoWrapper::before, - .playButton::before { - animation: none !important; - } - - .playButton:hover { - transform: scale(1.08); - } -} - -/* Responsive Design */ -@media (max-width: 768px) { - .demoSection { - padding: 80px 20px; - } - - .sectionTitle { - margin-bottom: 48px; - } - - .videoWrapper::after { - top: -35px; - left: 16px; - font-size: 14px; - } - - .playButton { - width: 70px; - height: 70px; - } - - .playIcon { - font-size: 28px; - } - - .placeholderText { - font-size: 20px; - } -} - -@media (max-width: 480px) { - .demoSection { - padding: 60px 16px; - } - - .playButton { - width: 60px; - height: 60px; - } - - .playIcon { - font-size: 24px; - } - - .placeholderText { - font-size: 18px; - } - - .videoWrapper::after { - display: none; - } -} diff --git a/client/src/components/home/FeatureCard.jsx b/client/src/components/home/FeatureCard.jsx deleted file mode 100644 index fdfa51b..0000000 --- a/client/src/components/home/FeatureCard.jsx +++ /dev/null @@ -1,100 +0,0 @@ -import { useRef, useEffect } from 'react'; -import styles from './FeatureCard.module.css'; - -export default function FeatureCard({ icon, title, description, index }) { - const cardRef = useRef(null); - - useEffect(() => { - const card = cardRef.current; - if (!card) return; - - const handleMouseMove = (e) => { - const rect = card.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; - - card.style.setProperty('--mouse-x', `${x}px`); - card.style.setProperty('--mouse-y', `${y}px`); - }; - - card.addEventListener('mousemove', handleMouseMove); - return () => card.removeEventListener('mousemove', handleMouseMove); - }, []); - - // SVG Icon mapping - const getSvgIcon = () => { - switch (title) { - case 'Password Security': - return ( - - - - - ); - case 'Privacy Protection': - return ( - - - - ); - case 'Gesture Controls': - return ( - - - - - - - ); - case 'AI Companions': - return ( - - - - - - - - ); - case 'Earn Rewards': - return ( - - - - - ); - case 'Immersive 3D World': - return ( - - - - - - ); - default: - return {icon}; - } - }; - - return ( -
-
- -
-
-
-
-
-
- -
- {getSvgIcon()} -
- -

{title}

-

{description}

- -
-
- ); -} diff --git a/client/src/components/home/FeatureCard.module.css b/client/src/components/home/FeatureCard.module.css deleted file mode 100644 index 7edbd6d..0000000 --- a/client/src/components/home/FeatureCard.module.css +++ /dev/null @@ -1,280 +0,0 @@ -/* ===== HOLOGRAPHIC TERMINAL FEATURE CARD ===== */ -/* Cyber-Forest Sanctuary Theme */ - -.card { - position: relative; - padding: 36px 28px; - border-radius: 20px; - background: var(--glass-bg); - backdrop-filter: blur(var(--glass-blur)); - -webkit-backdrop-filter: blur(var(--glass-blur)); - border: 1px solid; - border-image: linear-gradient( - 135deg, - var(--portal-cyan), - var(--portal-purple), - transparent - ) 1; - transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); - overflow: hidden; - cursor: pointer; - --mouse-x: 50%; - --mouse-y: 50%; -} - -/* Animated Border Gradient */ -.card::before { - content: ''; - position: absolute; - inset: 0; - border-radius: 20px; - padding: 2px; - background: linear-gradient( - 135deg, - var(--portal-cyan), - var(--portal-purple), - var(--forest-emerald), - var(--portal-cyan) - ); - background-size: 300% 300%; - animation: borderFlow 4s ease infinite; - -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); - -webkit-mask-composite: xor; - mask-composite: exclude; - opacity: 0; - transition: opacity 0.4s; -} - -.card:hover::before { - opacity: 1; -} - -/* Scanning Line Animation */ -.scanLine { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 2px; - background: linear-gradient( - 90deg, - transparent, - var(--portal-cyan), - transparent - ); - box-shadow: 0 0 10px var(--portal-cyan); - transform: translateY(-100%); - opacity: 0; - transition: opacity 0.3s; -} - -.card:hover .scanLine { - animation: scan 2s linear infinite; - opacity: 1; -} - -/* Corner Brackets */ -.cornerBrackets div { - position: absolute; - width: 20px; - height: 20px; - border: 2px solid var(--security-shield); - opacity: 0; - transition: opacity 0.4s, transform 0.4s; -} - -.cornerTL { - top: 8px; - left: 8px; - border-right: none; - border-bottom: none; - transform: translate(-4px, -4px); -} - -.cornerTR { - top: 8px; - right: 8px; - border-left: none; - border-bottom: none; - transform: translate(4px, -4px); -} - -.cornerBL { - bottom: 8px; - left: 8px; - border-right: none; - border-top: none; - transform: translate(-4px, 4px); -} - -.cornerBR { - bottom: 8px; - right: 8px; - border-left: none; - border-top: none; - transform: translate(4px, 4px); -} - -.card:hover .cornerBrackets div { - opacity: 0.6; - transform: translate(0, 0); -} - -/* Icon Wrapper */ -.iconWrapper { - width: 64px; - height: 64px; - margin: 0 auto 20px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 16px; - background: linear-gradient(135deg, var(--portal-cyan), var(--portal-purple)); - padding: 16px; - transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); - position: relative; - z-index: 1; -} - -.iconWrapper svg { - width: 100%; - height: 100%; - color: white; - filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3)); - transition: transform 0.4s; -} - -.card:hover .iconWrapper { - transform: translateY(-8px) scale(1.1); - box-shadow: - 0 0 30px rgba(0, 242, 255, 0.5), - 0 20px 40px rgba(0, 0, 0, 0.3); -} - -.card:hover .iconWrapper svg { - transform: rotate(5deg); -} - -/* Title */ -.title { - font-family: 'Orbitron', sans-serif; - font-size: 22px; - font-weight: 700; - color: var(--neo-text); - margin: 0 0 16px; - letter-spacing: 0.5px; - text-align: center; - text-transform: uppercase; - position: relative; - z-index: 1; - transition: color 0.3s; -} - -.card:hover .title { - background: linear-gradient(135deg, var(--portal-cyan), var(--forest-emerald)); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -/* Description */ -.description { - font-family: 'Fredoka', sans-serif; - font-size: 15px; - font-weight: 400; - color: var(--neo-text-dim); - line-height: 1.7; - margin: 0; - text-align: center; - position: relative; - z-index: 1; -} - -/* Interactive Spotlight Effect */ -.spotlight { - position: absolute; - inset: 0; - background: radial-gradient( - 600px circle at var(--mouse-x) var(--mouse-y), - rgba(0, 242, 255, 0.1), - transparent 40% - ); - opacity: 0; - transition: opacity 0.3s; - pointer-events: none; -} - -.card:hover .spotlight { - opacity: 1; -} - -/* Hover Animation */ -.card:hover { - transform: translateY(-12px) scale(1.02); - box-shadow: - 0 0 40px rgba(0, 242, 255, 0.3), - 0 0 80px rgba(112, 0, 255, 0.2), - 0 30px 60px rgba(0, 0, 0, 0.3); -} - -/* Staggered Entrance Animation */ -@keyframes cardEntrance { - from { - opacity: 0; - transform: translateY(60px) scale(0.95); - } - to { - opacity: 1; - transform: translateY(0) scale(1); - } -} - -.card { - animation: cardEntrance 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) backwards; - animation-delay: calc(var(--index) * 0.1s); -} - -/* Accessibility: Reduced Motion */ -@media (prefers-reduced-motion: reduce) { - .card, - .scanLine, - .card::before, - .iconWrapper, - .iconWrapper svg { - animation: none !important; - transition-duration: 0.1s; - } - - .card:hover { - transform: translateY(-6px); - } - - .card:hover .iconWrapper { - transform: translateY(-4px) scale(1.05); - } - - .card:hover .iconWrapper svg { - transform: none; - } -} - -/* Responsive Design */ -@media (max-width: 768px) { - .card { - padding: 28px 24px; - } - - .iconWrapper { - width: 56px; - height: 56px; - margin-bottom: 16px; - } - - .title { - font-size: 20px; - } - - .description { - font-size: 14px; - } -} diff --git a/client/src/components/home/FeaturesShowcase.jsx b/client/src/components/home/FeaturesShowcase.jsx new file mode 100644 index 0000000..d5b4baf --- /dev/null +++ b/client/src/components/home/FeaturesShowcase.jsx @@ -0,0 +1,91 @@ +import { useRef, useEffect, useState } from 'react'; +import styles from './FeaturesShowcase.module.css'; + +export default function FeaturesShowcase() { + const sectionRef = useRef(null); + const [isVisible, setIsVisible] = useState(false); + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setIsVisible(true); + } + }, + { threshold: 0.1 } + ); + + if (sectionRef.current) { + observer.observe(sectionRef.current); + } + + return () => observer.disconnect(); + }, []); + + const features = [ + { + title: 'Password Security', + description: 'Learn to create unbreakable passwords through interactive challenges. Test password strength in real-time and discover common vulnerabilities.', + icon: '🔐', + color: 'var(--portal-cyan)', + }, + { + title: 'Privacy Protection', + description: 'Master data privacy concepts with hands-on scenarios. Understand tracking, cookies, and how to protect your digital footprint.', + icon: '🛡️', + color: 'var(--forest-emerald)', + }, + { + title: 'Gesture Controls', + description: 'Navigate through the cyber-forest using intuitive hand gestures powered by MediaPipe AI. Experience cutting-edge interaction technology.', + icon: '👋', + color: 'var(--portal-purple)', + }, + { + title: 'AI Companions', + description: 'Generate unique AI-powered companion dolls that guide you through challenges. Each companion is personalized to your learning journey.', + icon: '🤖', + color: 'var(--portal-pink)', + }, + ]; + + return ( +
+
+
+

Why SafeForest?

+

+ Immersive learning meets cutting-edge technology in a gamified cybersecurity adventure +

+
+ +
+ {features.map((feature, index) => ( +
+
+
+ {feature.icon} +
+
+ +
+

{feature.title}

+

{feature.description}

+
+
+ ))} +
+
+
+ ); +} diff --git a/client/src/components/home/FeaturesShowcase.module.css b/client/src/components/home/FeaturesShowcase.module.css new file mode 100644 index 0000000..8ca273a --- /dev/null +++ b/client/src/components/home/FeaturesShowcase.module.css @@ -0,0 +1,220 @@ +.featuresShowcase { + position: relative; + padding: var(--space-3xl) var(--space-md); + background: var(--section-bg-alt); + overflow: hidden; +} + +.container { + max-width: var(--container-xl); + margin: 0 auto; +} + +.header { + text-align: center; + margin-bottom: var(--space-3xl); + opacity: 0; + transform: translateY(30px); + transition: all 0.8s ease; +} + +.featuresShowcase.visible .header { + opacity: 1; + transform: translateY(0); +} + +.heading { + font-family: 'Orbitron', sans-serif; + font-size: clamp(36px, 5vw, 56px); + font-weight: 900; + color: var(--neo-text); + margin: 0 0 var(--space-md); + letter-spacing: -1px; + text-transform: uppercase; + background: linear-gradient(135deg, var(--neo-cyan), var(--neo-purple)); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.subheading { + font-family: 'Fredoka', sans-serif; + font-size: clamp(16px, 2vw, 20px); + font-weight: 500; + color: var(--neo-text-dim); + max-width: 700px; + margin: 0 auto; + line-height: 1.6; +} + +.featuresGrid { + display: flex; + flex-direction: column; + gap: var(--space-3xl); +} + +.feature { + display: flex; + gap: var(--space-2xl); + align-items: center; + opacity: 0; + transform: translateX(-50px); + transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.featuresShowcase.visible .feature { + opacity: 1; + transform: translateX(0); +} + +.feature.reverse { + flex-direction: row-reverse; + transform: translateX(50px); +} + +.featuresShowcase.visible .feature.reverse { + transform: translateX(0); +} + +.featureVisual { + flex: 1 1 400px; + display: flex; + justify-content: center; + align-items: center; + min-height: 300px; +} + +.iconWrapper { + position: relative; + width: 200px; + height: 200px; + display: flex; + align-items: center; + justify-content: center; + background: var(--glass-bg); + border: 2px solid var(--glass-border); + border-radius: 24px; + backdrop-filter: blur(var(--glass-blur)); + -webkit-backdrop-filter: blur(var(--glass-blur)); + transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); + box-shadow: var(--glass-shadow); +} + +.iconWrapper::before { + content: ''; + position: absolute; + inset: -2px; + border-radius: 24px; + padding: 2px; + background: linear-gradient(135deg, var(--feature-color), transparent); + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + opacity: 0; + transition: opacity 0.4s ease; +} + +.feature:hover .iconWrapper::before { + opacity: 1; +} + +.featureIcon { + font-size: 80px; + filter: drop-shadow(0 0 20px var(--feature-color)); + transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.feature:hover .iconWrapper { + transform: translateY(-8px) scale(1.05); + box-shadow: + 0 20px 60px rgba(0, 242, 255, 0.3), + 0 20px 60px rgba(112, 0, 255, 0.2); +} + +.feature:hover .featureIcon { + transform: scale(1.1) rotate(5deg); +} + +.featureContent { + flex: 1 1 400px; +} + +.featureTitle { + font-family: 'Orbitron', sans-serif; + font-size: clamp(24px, 3vw, 32px); + font-weight: 800; + color: var(--neo-text); + margin: 0 0 var(--space-md); + letter-spacing: -0.5px; +} + +.featureDescription { + font-family: 'Fredoka', sans-serif; + font-size: clamp(15px, 1.5vw, 18px); + font-weight: 400; + color: var(--neo-text-dim); + line-height: 1.8; + margin: 0; +} + +/* Responsive Design */ +@media (max-width: 1024px) { + .feature, + .feature.reverse { + flex-direction: column; + text-align: center; + gap: var(--space-xl); + } + + .featureVisual { + min-height: auto; + } + + .iconWrapper { + width: 160px; + height: 160px; + } + + .featureIcon { + font-size: 64px; + } +} + +@media (max-width: 768px) { + .featuresShowcase { + padding: var(--space-2xl) var(--space-md); + } + + .header { + margin-bottom: var(--space-2xl); + } + + .featuresGrid { + gap: var(--space-2xl); + } + + .iconWrapper { + width: 140px; + height: 140px; + } + + .featureIcon { + font-size: 56px; + } +} + +@media (prefers-reduced-motion: reduce) { + .feature { + transition: opacity 0.3s ease; + } + + .feature:hover .iconWrapper { + transform: scale(1.02); + } + + .feature:hover .featureIcon { + transform: scale(1.05); + } +} diff --git a/client/src/components/home/FinalCTA.jsx b/client/src/components/home/FinalCTA.jsx new file mode 100644 index 0000000..cd0b5be --- /dev/null +++ b/client/src/components/home/FinalCTA.jsx @@ -0,0 +1,84 @@ +import { useNavigate } from 'react-router-dom'; +import anime from 'animejs'; +import { useRef } from 'react'; +import styles from './FinalCTA.module.css'; + +export default function FinalCTA() { + const navigate = useNavigate(); + const primaryBtnRef = useRef(null); + const secondaryBtnRef = useRef(null); + + const handlePrimaryClick = () => { + anime({ + targets: primaryBtnRef.current, + scale: [1, 0.95, 1.05, 1], + duration: 400, + easing: 'easeInOutQuad', + complete: () => navigate('/login'), + }); + }; + + const handleSecondaryClick = () => { + anime({ + targets: secondaryBtnRef.current, + scale: [1, 0.95, 1.05, 1], + duration: 400, + easing: 'easeInOutQuad', + complete: () => { + // Scroll to demo section or show demo modal + const demoSection = document.getElementById('demo'); + if (demoSection) { + demoSection.scrollIntoView({ behavior: 'smooth' }); + } + }, + }); + }; + + return ( +
+
+ +
+
EXPLORE A NEW DIMENSION
+ +

+ Ready to unlock your{' '} + cybersecurity potential + {' '}and protect what matters most? +

+ +

+ Join thousands of learners mastering digital security through immersive gameplay. + Your journey to becoming a cyber-guardian starts here. +

+ +
+ + + +
+
+ + {/* Floating particles */} +
+ {[...Array(8)].map((_, i) => ( +
+ ))} +
+
+ ); +} diff --git a/client/src/components/home/FinalCTA.module.css b/client/src/components/home/FinalCTA.module.css new file mode 100644 index 0000000..0f4f43b --- /dev/null +++ b/client/src/components/home/FinalCTA.module.css @@ -0,0 +1,335 @@ +.finalCTA { + position: relative; + padding: var(--space-3xl) var(--space-md); + min-height: 600px; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + background: var(--neo-bg); +} + +.gradientBg { + position: absolute; + inset: 0; + background: linear-gradient( + 135deg, + rgba(0, 242, 255, 0.15) 0%, + rgba(112, 0, 255, 0.15) 50%, + rgba(0, 255, 159, 0.15) 100% + ); + opacity: 0.8; +} + +.gradientBg::before { + content: ''; + position: absolute; + inset: 0; + background: radial-gradient( + ellipse at center, + transparent 0%, + var(--neo-bg) 70% + ); +} + +.content { + position: relative; + z-index: 10; + text-align: center; + max-width: 900px; + padding: 0 var(--space-md); +} + +.eyebrow { + font-family: 'Orbitron', sans-serif; + font-size: 14px; + font-weight: 700; + letter-spacing: 3px; + text-transform: uppercase; + color: var(--portal-cyan); + margin-bottom: var(--space-md); + opacity: 0.9; + text-shadow: 0 0 20px rgba(0, 242, 255, 0.5); + animation: fadeInDown 0.8s ease; +} + +.headline { + font-family: 'Orbitron', sans-serif; + font-size: clamp(32px, 5vw, 56px); + font-weight: 900; + color: var(--neo-text); + margin: 0 0 var(--space-lg); + letter-spacing: -1px; + line-height: 1.2; + animation: fadeInUp 0.8s ease 0.2s backwards; +} + +.highlight { + background: linear-gradient(135deg, var(--neo-cyan), var(--neo-purple)); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.description { + font-family: 'Fredoka', sans-serif; + font-size: clamp(16px, 2vw, 20px); + font-weight: 500; + color: var(--neo-text-dim); + line-height: 1.7; + margin: 0 auto var(--space-2xl); + max-width: 700px; + animation: fadeInUp 0.8s ease 0.4s backwards; +} + +.ctaButtons { + display: flex; + justify-content: center; + gap: var(--space-md); + flex-wrap: wrap; + animation: fadeInUp 0.8s ease 0.6s backwards; +} + +.primaryButton { + position: relative; + display: inline-flex; + align-items: center; + gap: 12px; + padding: 0 48px; + height: 70px; + border-radius: 999px; + border: none; + background: linear-gradient(135deg, var(--portal-cyan), var(--portal-purple)); + color: white; + font-family: 'Orbitron', sans-serif; + font-size: 18px; + font-weight: 800; + cursor: pointer; + overflow: hidden; + letter-spacing: 0.5px; + text-transform: uppercase; + transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); + box-shadow: + 0 0 50px rgba(0, 242, 255, 0.5), + 0 0 70px rgba(112, 0, 255, 0.4), + 0 20px 40px rgba(0, 0, 0, 0.3); +} + +.primaryButton::before { + content: ''; + position: absolute; + inset: -4px; + border-radius: 999px; + background: linear-gradient(135deg, var(--portal-cyan), var(--portal-purple), var(--portal-pink)); + z-index: -1; + animation: glow 2s ease-in-out infinite; + opacity: 0.8; +} + +.primaryButton::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + border-radius: 50%; + background: rgba(255, 255, 255, 0.3); + transform: translate(-50%, -50%); + transition: width 0.6s, height 0.6s; +} + +.primaryButton:hover::after { + width: 400px; + height: 400px; +} + +.primaryButton:hover { + transform: translateY(-6px) scale(1.05); + box-shadow: + 0 0 70px rgba(0, 242, 255, 0.7), + 0 0 100px rgba(112, 0, 255, 0.6), + 0 30px 60px rgba(0, 0, 0, 0.4); +} + +.arrow { + font-size: 24px; + transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); + position: relative; + z-index: 1; +} + +.primaryButton:hover .arrow { + transform: translateX(8px); +} + +.secondaryButton { + position: relative; + display: inline-flex; + align-items: center; + gap: 10px; + padding: 0 40px; + height: 70px; + border-radius: 999px; + border: 2px solid var(--neo-cyan); + background: transparent; + color: var(--neo-text); + font-family: 'Orbitron', sans-serif; + font-size: 18px; + font-weight: 700; + cursor: pointer; + letter-spacing: 0.5px; + text-transform: uppercase; + transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); +} + +.secondaryButton:hover { + transform: translateY(-6px); + background: rgba(0, 242, 255, 0.1); + border-color: var(--neo-purple); + box-shadow: + 0 0 40px rgba(0, 242, 255, 0.4), + 0 20px 40px rgba(0, 0, 0, 0.2); +} + +.playIcon { + font-size: 14px; + transition: transform 0.3s ease; +} + +.secondaryButton:hover .playIcon { + transform: translateX(4px); +} + +.particles { + position: absolute; + inset: 0; + z-index: 1; + pointer-events: none; + overflow: hidden; +} + +.particle { + position: absolute; + width: 4px; + height: 4px; + background: var(--portal-cyan); + border-radius: 50%; + box-shadow: 0 0 10px currentColor; + animation: particleFloat 15s linear infinite; + opacity: 0; +} + +.particle:nth-child(1) { left: 10%; animation-delay: 0s; } +.particle:nth-child(2) { left: 20%; animation-delay: 2s; background: var(--forest-emerald); } +.particle:nth-child(3) { left: 35%; animation-delay: 4s; } +.particle:nth-child(4) { left: 50%; animation-delay: 1s; background: var(--portal-purple); } +.particle:nth-child(5) { left: 65%; animation-delay: 5s; } +.particle:nth-child(6) { left: 75%; animation-delay: 3s; background: var(--forest-emerald); } +.particle:nth-child(7) { left: 85%; animation-delay: 6s; } +.particle:nth-child(8) { left: 95%; animation-delay: 1.5s; background: var(--portal-purple); } + +@keyframes fadeInDown { + from { + opacity: 0; + transform: translateY(-30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes glow { + 0%, 100% { + opacity: 0.6; + } + 50% { + opacity: 1; + } +} + +@keyframes particleFloat { + 0% { + transform: translateY(100vh) translateX(0) rotate(0deg); + opacity: 0; + } + 10% { + opacity: 0.8; + } + 90% { + opacity: 0.8; + } + 100% { + transform: translateY(-100vh) translateX(50px) rotate(360deg); + opacity: 0; + } +} + +/* Responsive Design */ +@media (max-width: 768px) { + .finalCTA { + padding: var(--space-2xl) var(--space-md); + min-height: 500px; + } + + .ctaButtons { + flex-direction: column; + align-items: center; + } + + .primaryButton, + .secondaryButton { + width: 100%; + max-width: 320px; + } +} + +@media (max-width: 480px) { + .primaryButton, + .secondaryButton { + height: 60px; + font-size: 16px; + padding: 0 36px; + } +} + +@media (prefers-reduced-motion: reduce) { + .eyebrow, + .headline, + .description, + .ctaButtons { + animation: none; + } + + .primaryButton::before, + .particle { + animation: none !important; + } + + .primaryButton:hover { + transform: translateY(-3px) scale(1.02); + } + + .secondaryButton:hover { + transform: translateY(-3px); + } + + .primaryButton:hover .arrow, + .secondaryButton:hover .playIcon { + transform: translateX(4px); + } +} diff --git a/client/src/components/home/HeroSection.jsx b/client/src/components/home/HeroSection.jsx index 2771c9b..2dfa993 100644 --- a/client/src/components/home/HeroSection.jsx +++ b/client/src/components/home/HeroSection.jsx @@ -128,11 +128,13 @@ export default function HeroSection() { {/* Content Overlay */}
+
CYBERSECURITY EDUCATION
+

- Learn{' '} - Cybersecurity + Master{' '} + Digital Security
- Through Play + Through Immersive Play

@@ -145,6 +147,14 @@ export default function HeroSection() {

+ + {/* Scroll Down Indicator */} +
+
+
+
+ Scroll to explore +
); } diff --git a/client/src/components/home/HeroSection.module.css b/client/src/components/home/HeroSection.module.css index 7316721..607c2cb 100644 --- a/client/src/components/home/HeroSection.module.css +++ b/client/src/components/home/HeroSection.module.css @@ -34,7 +34,7 @@ z-index: 0; transition: opacity 0.5s ease; mix-blend-mode: screen; - opacity: 0.6; + opacity: 0.3; } .gradientWrapper canvas { @@ -111,6 +111,19 @@ width: 100%; } +/* Eyebrow Text */ +.eyebrow { + font-family: 'Orbitron', sans-serif; + font-size: 14px; + font-weight: 700; + letter-spacing: 3px; + text-transform: uppercase; + color: var(--portal-cyan); + margin-bottom: 24px; + opacity: 0.9; + text-shadow: 0 0 20px rgba(0, 242, 255, 0.5); +} + /* Main Title */ .title { font-family: 'Orbitron', sans-serif; @@ -162,7 +175,8 @@ display: inline-flex; align-items: center; gap: 14px; - padding: 20px 48px; + padding: 0 48px; + height: 60px; border-radius: 999px; border: none; background: linear-gradient(135deg, var(--portal-cyan), var(--portal-purple)); @@ -180,6 +194,7 @@ 0 0 40px rgba(0, 242, 255, 0.5), 0 0 60px rgba(112, 0, 255, 0.4), 0 20px 40px rgba(0, 0, 0, 0.3); + animation: ctaGlow 2s ease-in-out infinite; } /* Ripple Effect on Button */ @@ -263,12 +278,97 @@ .particle:nth-child(9) { left: 85%; animation-delay: 4.5s; } .particle:nth-child(10) { left: 95%; animation-delay: 1.5s; background: var(--forest-emerald); } +/* Scroll Down Indicator */ +.scrollIndicator { + position: absolute; + bottom: 40px; + left: 50%; + transform: translateX(-50%); + z-index: 10; + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + animation: scrollBounce 2s ease-in-out infinite; +} + +.scrollMouse { + width: 28px; + height: 44px; + border: 2px solid var(--portal-cyan); + border-radius: 14px; + position: relative; + box-shadow: 0 0 20px rgba(0, 242, 255, 0.4); +} + +.scrollWheel { + width: 4px; + height: 8px; + background: var(--portal-cyan); + border-radius: 2px; + position: absolute; + top: 8px; + left: 50%; + transform: translateX(-50%); + animation: scrollWheel 1.5s ease-in-out infinite; + box-shadow: 0 0 10px var(--portal-cyan); +} + +.scrollText { + font-family: 'Orbitron', sans-serif; + font-size: 12px; + font-weight: 600; + letter-spacing: 1.5px; + text-transform: uppercase; + color: var(--neo-text-dim); + opacity: 0.8; + text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3); +} + +@keyframes scrollBounce { + 0%, 100% { + transform: translateX(-50%) translateY(0); + } + 50% { + transform: translateX(-50%) translateY(-10px); + } +} + +@keyframes scrollWheel { + 0% { + opacity: 1; + top: 8px; + } + 100% { + opacity: 0; + top: 24px; + } +} + +@keyframes ctaGlow { + 0%, 100% { + box-shadow: + 0 0 40px rgba(0, 242, 255, 0.5), + 0 0 60px rgba(112, 0, 255, 0.4), + 0 20px 40px rgba(0, 0, 0, 0.3); + } + 50% { + box-shadow: + 0 0 60px rgba(0, 242, 255, 0.7), + 0 0 80px rgba(112, 0, 255, 0.6), + 0 20px 40px rgba(0, 0, 0, 0.3); + } +} + /* Accessibility: Reduced Motion */ @media (prefers-reduced-motion: reduce) { .floatingIcon, .glitch, + .ctaButton, .ctaButton::after, - .particle { + .particle, + .scrollIndicator, + .scrollWheel { animation: none !important; } @@ -317,11 +417,16 @@ .ctaButton { font-size: 16px; - padding: 16px 32px; + height: 52px; + padding: 0 32px; gap: 10px; } .particle { display: none; } + + .scrollIndicator { + display: none; + } } diff --git a/client/src/components/home/LearningModulesGrid.jsx b/client/src/components/home/LearningModulesGrid.jsx new file mode 100644 index 0000000..614705f --- /dev/null +++ b/client/src/components/home/LearningModulesGrid.jsx @@ -0,0 +1,93 @@ +import { useRef, useEffect, useState } from 'react'; +import ModuleCard from './ModuleCard'; +import styles from './LearningModulesGrid.module.css'; + +export default function LearningModulesGrid() { + const sectionRef = useRef(null); + const [isVisible, setIsVisible] = useState(false); + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setIsVisible(true); + } + }, + { threshold: 0.1 } + ); + + if (sectionRef.current) { + observer.observe(sectionRef.current); + } + + return () => observer.disconnect(); + }, []); + + const modules = [ + { + id: 1, + title: 'Password Room', + level: 'beginner', + icon: '🔐', + gradient: 'linear-gradient(135deg, #00f2ff, #0099cc)', + rating: 5, + players: 1200, + }, + { + id: 2, + title: 'Privacy Lab', + level: 'intermediate', + icon: '🛡️', + gradient: 'linear-gradient(135deg, #00ff9f, #00cc7f)', + rating: 5, + players: 850, + }, + { + id: 3, + title: 'Threat Arena', + level: 'advanced', + icon: '⚔️', + gradient: 'linear-gradient(135deg, #ff006e, #cc0056)', + rating: 4, + players: 620, + }, + { + id: 4, + title: 'Encryption Workshop', + level: 'intermediate', + icon: '🔑', + gradient: 'linear-gradient(135deg, #7000ff, #5500cc)', + rating: 5, + players: 940, + }, + ]; + + return ( +
+
+
+

Learning Paths

+

+ Choose your adventure through cybersecurity fundamentals +

+
+ +
+ {modules.map((module, index) => ( +
+ +
+ ))} +
+
+
+ ); +} diff --git a/client/src/components/home/LearningModulesGrid.module.css b/client/src/components/home/LearningModulesGrid.module.css new file mode 100644 index 0000000..c1ab4c1 --- /dev/null +++ b/client/src/components/home/LearningModulesGrid.module.css @@ -0,0 +1,93 @@ +.learningModules { + position: relative; + padding: var(--space-3xl) var(--space-md); + background: var(--neo-bg); + overflow: hidden; +} + +.container { + max-width: var(--container-xl); + margin: 0 auto; +} + +.header { + text-align: center; + margin-bottom: var(--space-2xl); + opacity: 0; + transform: translateY(30px); + transition: all 0.8s ease; +} + +.learningModules.visible .header { + opacity: 1; + transform: translateY(0); +} + +.heading { + font-family: 'Orbitron', sans-serif; + font-size: clamp(36px, 5vw, 56px); + font-weight: 900; + color: var(--neo-text); + margin: 0 0 var(--space-md); + letter-spacing: -1px; + text-transform: uppercase; + background: linear-gradient(135deg, var(--portal-cyan), var(--portal-purple), var(--forest-emerald)); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.subheading { + font-family: 'Fredoka', sans-serif; + font-size: clamp(16px, 2vw, 20px); + font-weight: 500; + color: var(--neo-text-dim); + max-width: 700px; + margin: 0 auto; + line-height: 1.6; +} + +.grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: var(--space-lg); +} + +.gridItem { + opacity: 0; + transform: translateY(30px); + transition: all 0.6s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.learningModules.visible .gridItem { + opacity: 1; + transform: translateY(0); +} + +/* Responsive Design */ +@media (max-width: 768px) { + .learningModules { + padding: var(--space-2xl) var(--space-md); + } + + .header { + margin-bottom: var(--space-xl); + } + + .grid { + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: var(--space-md); + } +} + +@media (max-width: 480px) { + .grid { + grid-template-columns: 1fr; + } +} + +@media (prefers-reduced-motion: reduce) { + .gridItem { + transition: opacity 0.3s ease; + } +} diff --git a/client/src/components/home/ModuleCard.jsx b/client/src/components/home/ModuleCard.jsx new file mode 100644 index 0000000..f28d34e --- /dev/null +++ b/client/src/components/home/ModuleCard.jsx @@ -0,0 +1,65 @@ +import { useNavigate } from 'react-router-dom'; +import styles from './ModuleCard.module.css'; + +export default function ModuleCard({ module }) { + const navigate = useNavigate(); + + const getBadgeColor = (level) => { + switch (level) { + case 'beginner': + return 'var(--badge-beginner)'; + case 'intermediate': + return 'var(--badge-intermediate)'; + case 'advanced': + return 'var(--badge-advanced)'; + default: + return 'var(--badge-beginner)'; + } + }; + + const handleStartModule = () => { + navigate('/login'); + }; + + return ( +
+
+ {module.level} +
+ +
+
+ {module.icon} +
+
+ +
+

{module.title}

+ +
+
+ {[...Array(5)].map((_, i) => ( + + ★ + + ))} +
+ +
+ 👥 + {module.players}+ +
+
+ + +
+
+ ); +} diff --git a/client/src/components/home/ModuleCard.module.css b/client/src/components/home/ModuleCard.module.css new file mode 100644 index 0000000..d8935ff --- /dev/null +++ b/client/src/components/home/ModuleCard.module.css @@ -0,0 +1,202 @@ +.moduleCard { + position: relative; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 16px; + overflow: hidden; + backdrop-filter: blur(var(--glass-blur)); + -webkit-backdrop-filter: blur(var(--glass-blur)); + box-shadow: var(--glass-shadow); + transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); + cursor: pointer; +} + +.moduleCard:hover { + transform: translateY(-8px); + box-shadow: + 0 20px 60px rgba(0, 242, 255, 0.3), + 0 20px 60px rgba(112, 0, 255, 0.2); + border-color: var(--neo-cyan); +} + +.badge { + position: absolute; + top: 16px; + right: 16px; + padding: 6px 12px; + border-radius: 20px; + background: var(--badge-color); + color: white; + font-family: 'Orbitron', sans-serif; + font-size: 11px; + font-weight: 700; + letter-spacing: 1px; + text-transform: uppercase; + z-index: 10; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); +} + +.imageWrapper { + position: relative; + width: 100%; + aspect-ratio: 16 / 9; + overflow: hidden; +} + +.imageGradient { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + transition: transform 0.4s ease; +} + +.moduleCard:hover .imageGradient { + transform: scale(1.1); +} + +.moduleIcon { + font-size: 64px; + filter: drop-shadow(0 0 20px rgba(0, 0, 0, 0.5)); +} + +.content { + padding: var(--space-md); +} + +.title { + font-family: 'Orbitron', sans-serif; + font-size: 20px; + font-weight: 800; + color: var(--neo-text); + margin: 0 0 var(--space-md); + letter-spacing: -0.5px; +} + +.meta { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: var(--space-md); +} + +.rating { + display: flex; + gap: 2px; +} + +.star { + color: var(--star-gold); + font-size: 16px; + transition: opacity 0.3s ease; +} + +.players { + display: flex; + align-items: center; + gap: 6px; + font-family: 'Fredoka', sans-serif; + font-size: 14px; + font-weight: 600; + color: var(--neo-text-dim); +} + +.playersIcon { + font-size: 16px; +} + +.playersCount { + opacity: 0.9; +} + +.ctaButton { + width: 100%; + padding: 12px 24px; + border-radius: 8px; + border: 2px solid transparent; + background: linear-gradient(135deg, var(--neo-cyan), var(--neo-purple)); + color: white; + font-family: 'Orbitron', sans-serif; + font-size: 14px; + font-weight: 700; + letter-spacing: 0.5px; + text-transform: uppercase; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +.ctaButton::before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + border-radius: 50%; + background: rgba(255, 255, 255, 0.3); + transform: translate(-50%, -50%); + transition: width 0.5s, height 0.5s; +} + +.ctaButton:hover::before { + width: 300px; + height: 300px; +} + +.ctaButton:hover { + transform: translateY(-2px); + box-shadow: + 0 8px 24px rgba(0, 242, 255, 0.4), + 0 8px 24px rgba(112, 0, 255, 0.3); +} + +.arrow { + font-size: 18px; + transition: transform 0.3s ease; + position: relative; + z-index: 1; +} + +.ctaButton:hover .arrow { + transform: translateX(4px); +} + +@media (max-width: 480px) { + .content { + padding: var(--space-sm); + } + + .title { + font-size: 18px; + } + + .ctaButton { + font-size: 13px; + padding: 10px 20px; + } +} + +@media (prefers-reduced-motion: reduce) { + .moduleCard:hover { + transform: translateY(-4px); + } + + .moduleCard:hover .imageGradient { + transform: scale(1.05); + } + + .ctaButton:hover { + transform: none; + } + + .ctaButton:hover .arrow { + transform: translateX(2px); + } +} diff --git a/client/src/components/home/StatsCounter.jsx b/client/src/components/home/StatsCounter.jsx new file mode 100644 index 0000000..355ce32 --- /dev/null +++ b/client/src/components/home/StatsCounter.jsx @@ -0,0 +1,79 @@ +import { useRef, useEffect, useState } from 'react'; +import anime from 'animejs'; +import styles from './StatsCounter.module.css'; + +export default function StatsCounter() { + const sectionRef = useRef(null); + const [hasAnimated, setHasAnimated] = useState(false); + const statsRefs = useRef([]); + + const stats = [ + { label: 'Active Players', value: 5000, suffix: '+', icon: '👥' }, + { label: 'Challenges Completed', value: 2500, suffix: '+', icon: '🎯' }, + { label: 'Learning Modules', value: 15, suffix: '', icon: '📚' }, + { label: 'Badges Earned', value: 10000, suffix: '+', icon: '🏆' }, + ]; + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting && !hasAnimated) { + setHasAnimated(true); + + // Animate each stat counter + statsRefs.current.forEach((ref, index) => { + if (ref) { + const stat = stats[index]; + const obj = { value: 0 }; + + anime({ + targets: obj, + value: stat.value, + duration: 2000, + delay: index * 100, + easing: 'easeOutExpo', + round: 1, + update: function() { + ref.textContent = obj.value.toLocaleString(); + } + }); + } + }); + + observer.disconnect(); + } + }, + { threshold: 0.3 } + ); + + if (sectionRef.current) { + observer.observe(sectionRef.current); + } + + return () => observer.disconnect(); + }, [hasAnimated]); + + return ( +
+
+
+ {stats.map((stat, index) => ( +
+
{stat.icon}
+
+ (statsRefs.current[index] = el)} + className={styles.number} + > + 0 + + {stat.suffix} +
+
{stat.label}
+
+ ))} +
+
+
+ ); +} diff --git a/client/src/components/home/StatsCounter.module.css b/client/src/components/home/StatsCounter.module.css new file mode 100644 index 0000000..2fe4132 --- /dev/null +++ b/client/src/components/home/StatsCounter.module.css @@ -0,0 +1,159 @@ +.statsCounter { + position: relative; + padding: var(--space-3xl) var(--space-md); + background: var(--section-bg-alt); + overflow: hidden; +} + +.statsCounter::before { + content: ''; + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); + width: 80%; + height: 1px; + background: linear-gradient( + 90deg, + transparent, + var(--neo-cyan), + var(--neo-purple), + transparent + ); + opacity: 0.5; +} + +.container { + max-width: var(--container-xl); + margin: 0 auto; +} + +.statsGrid { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: var(--space-xl); +} + +.statCard { + flex: 1 1 200px; + display: flex; + flex-direction: column; + align-items: center; + gap: var(--space-sm); + padding: var(--space-lg); + text-align: center; + position: relative; +} + +.statIcon { + font-size: 48px; + filter: drop-shadow(0 0 20px currentColor); + opacity: 0.9; + animation: float 3s ease-in-out infinite; +} + +.statCard:nth-child(1) .statIcon { + animation-delay: 0s; + color: var(--portal-cyan); +} + +.statCard:nth-child(2) .statIcon { + animation-delay: 0.5s; + color: var(--forest-emerald); +} + +.statCard:nth-child(3) .statIcon { + animation-delay: 1s; + color: var(--portal-purple); +} + +.statCard:nth-child(4) .statIcon { + animation-delay: 1.5s; + color: var(--star-gold); +} + +.statValue { + display: flex; + align-items: baseline; + gap: 4px; +} + +.number { + font-family: 'Orbitron', sans-serif; + font-size: clamp(40px, 6vw, 64px); + font-weight: 900; + background: linear-gradient(135deg, var(--neo-cyan), var(--neo-purple)); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + letter-spacing: -2px; + line-height: 1; +} + +.suffix { + font-family: 'Orbitron', sans-serif; + font-size: clamp(24px, 3vw, 32px); + font-weight: 700; + color: var(--neo-cyan); + opacity: 0.8; +} + +.statLabel { + font-family: 'Fredoka', sans-serif; + font-size: clamp(14px, 1.5vw, 18px); + font-weight: 600; + color: var(--neo-text-dim); + letter-spacing: 0.5px; + text-transform: uppercase; +} + +@keyframes float { + 0%, 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-10px); + } +} + +/* Responsive Design */ +@media (max-width: 768px) { + .statsCounter { + padding: var(--space-2xl) var(--space-md); + } + + .statsGrid { + gap: var(--space-lg); + } + + .statCard { + flex: 1 1 150px; + padding: var(--space-md); + } + + .statIcon { + font-size: 40px; + } +} + +@media (max-width: 480px) { + .statsGrid { + gap: var(--space-md); + } + + .statCard { + flex: 1 1 120px; + padding: var(--space-sm); + } + + .statIcon { + font-size: 32px; + } +} + +@media (prefers-reduced-motion: reduce) { + .statIcon { + animation: none !important; + } +} diff --git a/client/src/components/home/TestimonialCard.jsx b/client/src/components/home/TestimonialCard.jsx new file mode 100644 index 0000000..f4a6968 --- /dev/null +++ b/client/src/components/home/TestimonialCard.jsx @@ -0,0 +1,27 @@ +import styles from './TestimonialCard.module.css'; + +export default function TestimonialCard({ testimonial }) { + return ( +
+
+ {[...Array(5)].map((_, i) => ( + + ))} +
+ +
+ "{testimonial.quote}" +
+ +
+
+
{testimonial.avatar}
+
+
+
{testimonial.name}
+
{testimonial.title}
+
+
+
+ ); +} diff --git a/client/src/components/home/TestimonialCard.module.css b/client/src/components/home/TestimonialCard.module.css new file mode 100644 index 0000000..f3dcaa3 --- /dev/null +++ b/client/src/components/home/TestimonialCard.module.css @@ -0,0 +1,116 @@ +.testimonialCard { + position: relative; + padding: var(--space-lg); + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 16px; + backdrop-filter: blur(var(--glass-blur)); + -webkit-backdrop-filter: blur(var(--glass-blur)); + box-shadow: var(--glass-shadow); + transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); + display: flex; + flex-direction: column; + gap: var(--space-md); + min-height: 280px; +} + +.testimonialCard:hover { + transform: translateY(-6px); + box-shadow: + 0 20px 60px rgba(0, 242, 255, 0.25), + 0 20px 60px rgba(112, 0, 255, 0.15); + border-color: var(--neo-cyan); +} + +.rating { + display: flex; + gap: 4px; +} + +.star { + color: var(--star-gold); + font-size: 18px; + filter: drop-shadow(0 0 8px var(--star-gold)); +} + +.quote { + font-family: 'Fredoka', sans-serif; + font-size: clamp(15px, 1.5vw, 17px); + font-weight: 500; + color: var(--neo-text); + line-height: 1.7; + margin: 0; + flex: 1; + font-style: italic; +} + +.author { + display: flex; + align-items: center; + gap: var(--space-md); + padding-top: var(--space-sm); + border-top: 1px solid var(--neo-border); +} + +.avatarWrapper { + position: relative; + padding: 3px; + border-radius: 50%; + background: linear-gradient(135deg, var(--neo-cyan), var(--neo-purple)); +} + +.avatar { + width: 48px; + height: 48px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 24px; + background: var(--neo-bg); + border: 2px solid var(--neo-bg); +} + +.authorInfo { + flex: 1; +} + +.authorName { + font-family: 'Orbitron', sans-serif; + font-size: 15px; + font-weight: 700; + color: var(--neo-text); + margin-bottom: 2px; +} + +.authorTitle { + font-family: 'Fredoka', sans-serif; + font-size: 13px; + font-weight: 500; + color: var(--neo-text-dim); +} + +@media (max-width: 480px) { + .testimonialCard { + padding: var(--space-md); + min-height: 260px; + } + + .quote { + font-size: 15px; + } + + .authorName { + font-size: 14px; + } + + .authorTitle { + font-size: 12px; + } +} + +@media (prefers-reduced-motion: reduce) { + .testimonialCard:hover { + transform: translateY(-3px); + } +} diff --git a/client/src/components/home/TestimonialsSection.jsx b/client/src/components/home/TestimonialsSection.jsx new file mode 100644 index 0000000..f7870a2 --- /dev/null +++ b/client/src/components/home/TestimonialsSection.jsx @@ -0,0 +1,74 @@ +import { useRef, useEffect, useState } from 'react'; +import TestimonialCard from './TestimonialCard'; +import styles from './TestimonialsSection.module.css'; + +export default function TestimonialsSection() { + const sectionRef = useRef(null); + const [isVisible, setIsVisible] = useState(false); + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setIsVisible(true); + } + }, + { threshold: 0.1 } + ); + + if (sectionRef.current) { + observer.observe(sectionRef.current); + } + + return () => observer.disconnect(); + }, []); + + const testimonials = [ + { + quote: "SafeForest made learning cybersecurity actually fun! The gesture controls are incredible and the 3D environment keeps me engaged. I've learned more here in a week than months of traditional courses.", + avatar: '👨‍💻', + name: 'Alex Chen', + title: 'Computer Science Student', + }, + { + quote: "As a teacher, I'm always looking for innovative ways to engage students. SafeForest's gamified approach to security education is brilliant. My students are actually excited about learning password safety!", + avatar: '👩‍🏫', + name: 'Sarah Martinez', + title: 'High School Teacher', + }, + { + quote: "The AI companion feature is genius! Having a personalized guide through the challenges makes the learning experience feel unique. Plus, the forest-cyberpunk aesthetic is stunning.", + avatar: '👨‍🎓', + name: 'David Kim', + title: 'Cybersecurity Enthusiast', + }, + ]; + + return ( +
+
+
+

What Learners Say

+

+ Join thousands discovering cybersecurity through immersive gameplay +

+
+ +
+ {testimonials.map((testimonial, index) => ( +
+ +
+ ))} +
+
+
+ ); +} diff --git a/client/src/components/home/TestimonialsSection.module.css b/client/src/components/home/TestimonialsSection.module.css new file mode 100644 index 0000000..c934179 --- /dev/null +++ b/client/src/components/home/TestimonialsSection.module.css @@ -0,0 +1,109 @@ +.testimonialsSection { + position: relative; + padding: var(--space-3xl) var(--space-md); + background: var(--neo-bg); + overflow: hidden; +} + +.container { + max-width: var(--container-xl); + margin: 0 auto; +} + +.header { + text-align: center; + margin-bottom: var(--space-2xl); + opacity: 0; + transform: translateY(30px); + transition: all 0.8s ease; +} + +.testimonialsSection.visible .header { + opacity: 1; + transform: translateY(0); +} + +.heading { + font-family: 'Orbitron', sans-serif; + font-size: clamp(36px, 5vw, 56px); + font-weight: 900; + color: var(--neo-text); + margin: 0 0 var(--space-md); + letter-spacing: -1px; + text-transform: uppercase; + background: linear-gradient(135deg, var(--neo-cyan), var(--neo-purple)); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.subheading { + font-family: 'Fredoka', sans-serif; + font-size: clamp(16px, 2vw, 20px); + font-weight: 500; + color: var(--neo-text-dim); + max-width: 700px; + margin: 0 auto; + line-height: 1.6; +} + +.testimonialsGrid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); + gap: var(--space-lg); +} + +.gridItem { + opacity: 0; + transform: translateY(30px); + transition: all 0.6s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.testimonialsSection.visible .gridItem { + opacity: 1; + transform: translateY(0); +} + +/* Mobile: Horizontal Scroll */ +@media (max-width: 768px) { + .testimonialsSection { + padding: var(--space-2xl) var(--space-md); + } + + .header { + margin-bottom: var(--space-xl); + } + + .testimonialsGrid { + display: flex; + overflow-x: auto; + scroll-snap-type: x mandatory; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; + gap: var(--space-md); + padding: var(--space-sm) 0; + } + + .testimonialsGrid::-webkit-scrollbar { + display: none; + } + + .gridItem { + flex-shrink: 0; + scroll-snap-align: center; + width: 85vw; + max-width: 400px; + } +} + +@media (max-width: 480px) { + .gridItem { + width: 90vw; + } +} + +@media (prefers-reduced-motion: reduce) { + .gridItem { + transition: opacity 0.3s ease; + } +} diff --git a/client/src/components/home/TrustBadges.jsx b/client/src/components/home/TrustBadges.jsx new file mode 100644 index 0000000..f3d98bf --- /dev/null +++ b/client/src/components/home/TrustBadges.jsx @@ -0,0 +1,57 @@ +import { useRef, useEffect, useState } from 'react'; +import styles from './TrustBadges.module.css'; + +export default function TrustBadges() { + const containerRef = useRef(null); + const [isVisible, setIsVisible] = useState(false); + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setIsVisible(true); + observer.disconnect(); + } + }, + { threshold: 0.2 } + ); + + if (containerRef.current) { + observer.observe(containerRef.current); + } + + return () => observer.disconnect(); + }, []); + + const technologies = [ + { name: 'Three.js', icon: '🎮' }, + { name: 'MediaPipe', icon: '👋' }, + { name: 'React', icon: '⚛️' }, + { name: 'Google OAuth', icon: '🔐' }, + { name: 'MySQL', icon: '🗄️' }, + { name: 'OpenAI', icon: '🤖' }, + ]; + + return ( +
+
+

Built with industry-leading technologies

+
+ {technologies.map((tech, index) => ( +
+ {tech.icon} + {tech.name} +
+ ))} +
+
+
+ ); +} diff --git a/client/src/components/home/TrustBadges.module.css b/client/src/components/home/TrustBadges.module.css new file mode 100644 index 0000000..82cfefb --- /dev/null +++ b/client/src/components/home/TrustBadges.module.css @@ -0,0 +1,152 @@ +.trustBadges { + position: relative; + padding: var(--space-xl) var(--space-md); + background: var(--neo-bg); + overflow: hidden; +} + +.container { + max-width: var(--container-xl); + margin: 0 auto; + text-align: center; +} + +.label { + font-family: 'Orbitron', sans-serif; + font-size: 13px; + font-weight: 600; + letter-spacing: 2px; + text-transform: uppercase; + color: var(--neo-text-dim); + margin: 0 0 var(--space-lg); + opacity: 0; + transform: translateY(20px); + transition: all 0.6s ease; +} + +.trustBadges.visible .label { + opacity: 1; + transform: translateY(0); +} + +.logoGrid { + display: flex; + justify-content: center; + align-items: center; + gap: var(--space-lg); + flex-wrap: wrap; +} + +.logoCard { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--space-xs); + padding: var(--space-md); + border-radius: 12px; + background: var(--glass-bg); + border: 1px solid var(--glass-border); + backdrop-filter: blur(var(--glass-blur)); + -webkit-backdrop-filter: blur(var(--glass-blur)); + transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); + cursor: pointer; + min-width: 120px; + opacity: 0; + transform: translateY(30px); +} + +.trustBadges.visible .logoCard { + opacity: 1; + transform: translateY(0); + animation: fadeInUp 0.6s ease forwards; +} + +.logoIcon { + font-size: 40px; + filter: grayscale(100%); + opacity: 0.6; + transition: all 0.4s ease; +} + +.logoName { + font-family: 'Orbitron', sans-serif; + font-size: 12px; + font-weight: 600; + letter-spacing: 0.5px; + color: var(--neo-text-dim); + transition: color 0.4s ease; +} + +.logoCard:hover { + transform: translateY(-8px); + box-shadow: + 0 12px 40px rgba(0, 242, 255, 0.2), + 0 12px 40px rgba(112, 0, 255, 0.15); + border-color: var(--neo-cyan); +} + +.logoCard:hover .logoIcon { + filter: grayscale(0%); + opacity: 1; + transform: scale(1.1); +} + +.logoCard:hover .logoName { + color: var(--neo-cyan); +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Mobile: Horizontal Scroll */ +@media (max-width: 768px) { + .logoGrid { + flex-wrap: nowrap; + overflow-x: auto; + scroll-snap-type: x mandatory; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; + padding: var(--space-sm) 0; + justify-content: flex-start; + } + + .logoGrid::-webkit-scrollbar { + display: none; + } + + .logoCard { + flex-shrink: 0; + scroll-snap-align: center; + min-width: 100px; + } + + .logoIcon { + font-size: 32px; + } + + .logoName { + font-size: 11px; + } +} + +@media (prefers-reduced-motion: reduce) { + .logoCard { + animation: none !important; + } + + .logoCard:hover { + transform: translateY(-4px); + } + + .logoCard:hover .logoIcon { + transform: scale(1.05); + } +} diff --git a/client/src/components/layout/Footer.jsx b/client/src/components/layout/Footer.jsx index 12645cc..696ef1a 100644 --- a/client/src/components/layout/Footer.jsx +++ b/client/src/components/layout/Footer.jsx @@ -1,9 +1,18 @@ import { useNavigate } from 'react-router-dom'; +import { useState } from 'react'; import styles from './Footer.module.css'; export default function Footer() { const navigate = useNavigate(); const currentYear = new Date().getFullYear(); + const [email, setEmail] = useState(''); + + const handleNewsletterSubmit = (e) => { + e.preventDefault(); + // TODO: Implement newsletter signup + console.log('Newsletter signup:', email); + setEmail(''); + }; return (