Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 75 additions & 58 deletions components/landing/FAQ/FAQ.jsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,58 @@
import { useState, useRef } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { useGSAP } from '@gsap/react';
import { useRetroSound } from '../hooks/useRetroSound';
import './FAQ.css';
import { useState, useRef } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { useGSAP } from "@gsap/react";
import { useRetroSound } from "../hooks/useRetroSound";
import "./FAQ.css";

gsap.registerPlugin(ScrollTrigger);

const faqsByCategory = {
General: [
{
q: 'Who can participate?',
a: 'PBCTF is open to students, professionals, and cybersecurity enthusiasts of all skill levels. Participants must be at least 18 years old or accompanied by a guardian.',
q: "Who can participate?",
a: "PBCTF is open to students, professionals, and cybersecurity enthusiasts of all skill levels.",
},
{
q: 'Do I need prior CTF experience?',
a: 'While prior experience is helpful, it is not mandatory. We offer challenges across multiple difficulty levels, making the competition accessible to beginners while still challenging for veterans.',
q: "Do I need prior CTF experience?",
a: "While prior experience is helpful, it is not mandatory. We offer challenges across multiple difficulty levels, making the competition accessible to beginners while still challenging for veterans.",
},
{
q: 'Will certificates be issued?',
a: 'Yes. All participants receive a certificate of participation. Winners receive special achievement certificates and trophies.',
q: "Will certificates be issued?",
a: "Yes. All participants receive a certificate of participation. Winners receive special achievement certificates and prizes.",
},
],
Teams: [
{
q: 'Can I participate individually?',
a: 'Teams of 2-4 members are required. If you do not have a team, join our Discord community to find teammates before registration closes.',
q: "Can I participate individually?",
a: "Yes, individual participation is allowed. You need to create a solo team in the dashboard",
},
{
q: "What is the maximum team size?",
a: "A team can consist of a maximum of Two members.",
},
],
Logistics: [
{
q: 'What should I bring?',
a: 'Bring your laptop, charger, any preferred peripherals, and a valid government-issued ID. All other equipment and resources will be provided at the venue.',
q: "What should I bring?",
a: "Bring your laptop, charger, any preferred peripherals, and a valid government-issued ID. All other equipment and resources will be provided at the venue.",
},
{
q: 'Is accommodation provided?',
a: 'Accommodation is not provided directly, but we have partnered with nearby hotels offering discounted rates for participants. Details will be shared after registration.',
q: "Is accommodation provided?",
a: "No accommodation is provided. Participants are responsible for their own lodging arrangements.",
},
],
Rules: [
{
q: 'How are winners selected?',
a: 'Teams are ranked based on total points earned by solving challenges. In case of a tie, the team that submitted solutions earliest wins.',
q: "How are winners selected?",
a: "Teams are ranked based on total points earned by solving challenges. In case of a tie, the team that submitted solutions earliest wins.",
},
{
q: 'What is the competition format?',
a: 'PBCTF is a jeopardy-style CTF with challenges across 8 categories. The competition runs for 24 continuous hours. Teams work together to solve as many challenges as possible.',
q: "What is the competition format?",
a: "PBCTF is a jeopardy-style CTF with challenges across 8 categories. The competition runs for 8 continuous hours. Teams work together to solve as many challenges as possible.",
},
]
],
};

const categories = Object.keys(faqsByCategory);
Expand All @@ -57,21 +61,25 @@ export default function FAQ() {
const [activeCategory, setActiveCategory] = useState(categories[0]);
const [openIndex, setOpenIndex] = useState(null);
const sectionRef = useRef(null);
const { playHover, playSelect, playWindowOpen, playWindowClose } = useRetroSound();
const { playHover, playSelect, playWindowOpen, playWindowClose } =
useRetroSound();

useGSAP(() => {
gsap.from('.faq__gameboy', {
opacity: 0,
y: 50,
duration: 0.8,
ease: 'power3.out',
scrollTrigger: {
trigger: sectionRef.current,
start: 'top 75%',
once: true,
},
});
}, { scope: sectionRef });
useGSAP(
() => {
gsap.from(".faq__gameboy", {
opacity: 0,
y: 50,
duration: 0.8,
ease: "power3.out",
scrollTrigger: {
trigger: sectionRef.current,
start: "top 75%",
once: true,
},
});
},
{ scope: sectionRef },
);

const toggle = (index) => {
setOpenIndex((prev) => {
Expand All @@ -97,11 +105,9 @@ export default function FAQ() {

<div className="faq__gameboy-wrapper">
<div className="faq__gameboy">

{/* Top Section: Screen */}
<div className="gameboy__top">
<div className="gameboy__screen-bezel">

<div className="gameboy__screen-header">
<div className="gameboy__power-light active"></div>
<span className="gameboy__brand">pbctf 5.0</span>
Expand All @@ -112,20 +118,24 @@ export default function FAQ() {
<div className="screen__category-title">
&gt; DIR: /{activeCategory.toUpperCase()}
</div>

<div className="faq__list">
{faqsByCategory[activeCategory].map((item, i) => {
const isOpen = openIndex === i;
return (
<div key={i} className={`faq__item ${isOpen ? 'is-open' : ''}`}>
<div
key={i}
className={`faq__item ${isOpen ? "is-open" : ""}`}
>
<button
className="faq__question"
onClick={() => toggle(i)}
onMouseEnter={playHover}
aria-expanded={isOpen}
>
<span className="faq__prompt">
<span className="faq__prompt-symbol">&gt;</span> {item.q}
<span className="faq__prompt-symbol">&gt;</span>{" "}
{item.q}
</span>
<motion.span
className="faq__chevron"
Expand All @@ -140,7 +150,7 @@ export default function FAQ() {
<motion.div
className="faq__answer-wrapper"
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.2 }}
>
Expand All @@ -156,7 +166,7 @@ export default function FAQ() {
</div>
</div>
</div>

{/* Middle Section: Logo */}
<div className="gameboy__middle">
<span className="gameboy__logo">POINT BLANK</span>
Expand All @@ -175,37 +185,45 @@ export default function FAQ() {
<div className="gameboy__action-buttons">
<div className="gameboy__button-row">
<div className="action-btn-wrapper">
<button
className={`gameboy__btn ${activeCategory === categories[0] ? 'active' : ''}`}
<button
className={`gameboy__btn ${activeCategory === categories[0] ? "active" : ""}`}
onMouseEnter={playHover}
onClick={() => handleCategoryChange(categories[0])}
>A</button>
>
A
</button>
<span className="gameboy__btn-label">{categories[0]}</span>
</div>
<div className="action-btn-wrapper btn-raised">
<button
className={`gameboy__btn ${activeCategory === categories[1] ? 'active' : ''}`}
<button
className={`gameboy__btn ${activeCategory === categories[1] ? "active" : ""}`}
onMouseEnter={playHover}
onClick={() => handleCategoryChange(categories[1])}
>B</button>
>
B
</button>
<span className="gameboy__btn-label">{categories[1]}</span>
</div>
</div>
<div className="gameboy__button-row">
<div className="action-btn-wrapper">
<button
className={`gameboy__btn ${activeCategory === categories[2] ? 'active' : ''}`}
<button
className={`gameboy__btn ${activeCategory === categories[2] ? "active" : ""}`}
onMouseEnter={playHover}
onClick={() => handleCategoryChange(categories[2])}
>X</button>
>
X
</button>
<span className="gameboy__btn-label">{categories[2]}</span>
</div>
<div className="action-btn-wrapper btn-raised">
<button
className={`gameboy__btn ${activeCategory === categories[3] ? 'active' : ''}`}
<button
className={`gameboy__btn ${activeCategory === categories[3] ? "active" : ""}`}
onMouseEnter={playHover}
onClick={() => handleCategoryChange(categories[3])}
>Y</button>
>
Y
</button>
<span className="gameboy__btn-label">{categories[3]}</span>
</div>
</div>
Expand All @@ -218,7 +236,6 @@ export default function FAQ() {
<div key={i} className="speaker-slit"></div>
))}
</div>

</div>
</div>
</div>
Expand Down
40 changes: 20 additions & 20 deletions components/landing/Sponsors/Sponsors.css
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
}

.pbctf-landing .sponsors__logo--paytm {
width: 80px;
width: 105px;
height: auto;
}

Expand Down Expand Up @@ -156,7 +156,7 @@
overflow: hidden;
-webkit-mask-image: linear-gradient(90deg, transparent 0%, black 10%, black 90%, transparent 100%);
mask-image: linear-gradient(90deg, transparent 0%, black 10%, black 90%, transparent 100%);
padding: 12px 0;
padding: 20px 0;
border-top: 1px solid rgba(0, 255, 136, 0.08);
border-bottom: 1px solid rgba(0, 255, 136, 0.08);
}
Expand All @@ -165,7 +165,7 @@
display: flex;
gap: var(--space-8);
width: max-content;
animation: ticker-scroll 28s linear infinite;
animation: ticker-scroll 38s linear infinite;
}

.pbctf-landing .sponsors__ticker-track:hover .sponsors__ticker-inner {
Expand All @@ -180,27 +180,27 @@
.pbctf-landing .sponsors__ticker-item {
display: flex;
align-items: center;
gap: 10px;
white-space: nowrap;
font-family: var(--font-mono);
font-size: 12px;
letter-spacing: 0.08em;
color: rgba(255, 255, 255, 0.35);
transition: color 0.3s ease;
flex-shrink: 0;
}

.pbctf-landing .sponsors__ticker-item:hover {
color: rgba(255, 255, 255, 0.7);
.pbctf-landing .sponsors__ticker-logo {
display: block;
height: 56px;
width: auto;
max-width: 220px;
object-fit: contain;
opacity: 1;
transition: opacity 0.3s ease, transform 0.3s ease;
}

.pbctf-landing .sponsors__ticker-dot {
display: inline-block;
width: 4px;
height: 4px;
border-radius: 50%;
background: #ffffff;
opacity: 0.5;
flex-shrink: 0;
.pbctf-landing .sponsors__ticker-item:hover .sponsors__ticker-logo {
opacity: 1;
transform: scale(1.08);
}

/* K7's brand colours clash with the dark theme — render it as a clean white (b&w) mark */
.pbctf-landing .sponsors__ticker-logo--k7 {
filter: brightness(0) invert(1);
}

/* CTA block */
Expand Down
Loading
Loading