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
8 changes: 7 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ export const metadata: Metadata = {
"PBCTF 5.0 is a Capture the Flag competition that brings together hackers, security enthusiasts, and problem solvers across web exploitation, reverse engineering, cryptography, forensics, and more. Race the clock, capture the flags, sharpen your edge.",
viewport: "width=device-width, initial-scale=1",
themeColor: "#050507",
icons: { icon: "/images/pbctf-logo.svg" },
icons: {
icon: [
{ url: "/favicon.ico", sizes: "any" },
{ url: "/icon.png", type: "image/png", sizes: "32x32" },
],
apple: "/apple-touch-icon.png",
},
};

export default function RootLayout({
Expand Down
6 changes: 4 additions & 2 deletions components/landing/AboutPointBlank/AboutPointBlank.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { useGSAP } from '@gsap/react';
import { GridScan } from '../GridScan/GridScan';
import { useRetroSound } from '../hooks/useRetroSound';
import './AboutPointBlank.css';

gsap.registerPlugin(ScrollTrigger);

export default function AboutPointBlank() {
const sectionRef = useRef(null);
const [isVideoPlaying, setIsVideoPlaying] = useState(false);
const { playHover, playWindowOpen, playWindowClose } = useRetroSound();

useGSAP(() => {
const content = sectionRef.current.querySelector('.about__content');
Expand Down Expand Up @@ -56,15 +58,15 @@ export default function AboutPointBlank() {
<p>
Our mission is to foster a culture of continuous learning, hands-on hacking, and collaborative problem-solving. Whether you are a seasoned hacker or a curious beginner, Point Blank provides the perfect environment to sharpen your skills, compete at the highest level, and push the boundaries of cybersecurity.
</p>
<button className="about__play-btn--monitor" onClick={() => setIsVideoPlaying(true)} aria-label="Play Video" title="Watch Video">
<button className="about__play-btn--monitor" onClick={() => { playWindowOpen(); setIsVideoPlaying(true); }} onMouseEnter={playHover} aria-label="Play Video" title="Watch Video">
<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16">
<path d="M8 5v14l11-7z" />
</svg>
</button>
</>
) : (
<div className="about__video-container">
<button className="about__close-btn" onClick={() => setIsVideoPlaying(false)} aria-label="Close Video">
<button className="about__close-btn" onClick={() => { playWindowClose(); setIsVideoPlaying(false); }} onMouseEnter={playHover} aria-label="Close Video">
</button>
<div className="about__video-wrapper">
Expand Down
9 changes: 6 additions & 3 deletions components/landing/Categories/Categories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ 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 './Categories.css';

gsap.registerPlugin(ScrollTrigger);
Expand Down Expand Up @@ -179,6 +180,7 @@ function DifficultyBar({ level, accent }) {
export default function Categories() {
const sectionRef = useRef(null);
const [activeId, setActiveId] = useState(CATEGORIES[0].id);
const { playHover, playSelect } = useRetroSound();

useEffect(() => {
const interval = setInterval(() => {
Expand Down Expand Up @@ -228,10 +230,11 @@ export default function Categories() {
</div>
<div className="categories__nav-list">
{CATEGORIES.map((cat) => (
<button
key={cat.id}
<button
key={cat.id}
className={`categories__nav-item ${activeId === cat.id ? 'active' : ''}`}
onClick={() => setActiveId(cat.id)}
onMouseEnter={playHover}
onClick={() => { playSelect(); setActiveId(cat.id); }}
>
<span className="categories__nav-title">{cat.title}</span>
{activeId === cat.id && <span className="categories__nav-indicator">█</span>}
Expand Down
17 changes: 16 additions & 1 deletion components/landing/FAQ/FAQ.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ 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);
Expand Down Expand Up @@ -56,6 +57,7 @@ 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();

useGSAP(() => {
gsap.from('.faq__gameboy', {
Expand All @@ -72,10 +74,18 @@ export default function FAQ() {
}, { scope: sectionRef });

const toggle = (index) => {
setOpenIndex((prev) => (prev === index ? null : index));
setOpenIndex((prev) => {
if (prev === index) {
playWindowClose();
return null;
}
playWindowOpen();
return index;
});
};

const handleCategoryChange = (category) => {
playSelect();
setActiveCategory(category);
setOpenIndex(null); // reset open accordion when switching
};
Expand Down Expand Up @@ -111,6 +121,7 @@ export default function FAQ() {
<button
className="faq__question"
onClick={() => toggle(i)}
onMouseEnter={playHover}
aria-expanded={isOpen}
>
<span className="faq__prompt">
Expand Down Expand Up @@ -166,13 +177,15 @@ export default function FAQ() {
<div className="action-btn-wrapper">
<button
className={`gameboy__btn ${activeCategory === categories[0] ? 'active' : ''}`}
onMouseEnter={playHover}
onClick={() => handleCategoryChange(categories[0])}
>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' : ''}`}
onMouseEnter={playHover}
onClick={() => handleCategoryChange(categories[1])}
>B</button>
<span className="gameboy__btn-label">{categories[1]}</span>
Expand All @@ -182,13 +195,15 @@ export default function FAQ() {
<div className="action-btn-wrapper">
<button
className={`gameboy__btn ${activeCategory === categories[2] ? 'active' : ''}`}
onMouseEnter={playHover}
onClick={() => handleCategoryChange(categories[2])}
>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' : ''}`}
onMouseEnter={playHover}
onClick={() => handleCategoryChange(categories[3])}
>Y</button>
<span className="gameboy__btn-label">{categories[3]}</span>
Expand Down
10 changes: 9 additions & 1 deletion components/landing/FinalCTA/FinalCTA.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { useRef } from 'react';
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { useGSAP } from '@gsap/react';
import { useRetroSound } from '../hooks/useRetroSound';
import './FinalCTA.css';

gsap.registerPlugin(ScrollTrigger);

export default function FinalCTA() {
const sectionRef = useRef(null);
const { playHover, playClick } = useRetroSound();

useGSAP(() => {
const headline = sectionRef.current.querySelector('.final-cta__headline');
Expand Down Expand Up @@ -39,7 +41,13 @@ export default function FinalCTA() {
compete against some of the brightest minds in cybersecurity.
</p>
<div className="final-cta__actions">
<a href="/register" className="btn btn--primary" id="cta-register-now">
<a
href="/register"
className="btn btn--primary"
id="cta-register-now"
onMouseEnter={playHover}
onClick={playClick}
>
Register Now
</a>

Expand Down
5 changes: 5 additions & 0 deletions components/landing/Footer/Footer.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useRetroSound } from '../hooks/useRetroSound';
import './Footer.css';

/* Minimal inline SVG icons — 24×24, stroke-only */
Expand Down Expand Up @@ -47,6 +48,8 @@ const SOCIALS = [
];

export default function Footer() {
const { playHover, playClick } = useRetroSound();

return (
<footer id="footer" className="footer">
<div className="footer__bottom-content">
Expand Down Expand Up @@ -74,6 +77,8 @@ export default function Footer() {
target="_blank"
rel="noopener noreferrer"
aria-label={s.label}
onMouseEnter={playHover}
onClick={playClick}
>
{s.icon}
</a>
Expand Down
44 changes: 44 additions & 0 deletions components/landing/Header/Header.css
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,50 @@
transform: translateX(0);
}

/* --- Right-side controls --- */
.pbctf-landing .header__actions {
display: flex;
align-items: center;
gap: var(--space-3);
}

/* --- Sound toggle --- */
.pbctf-landing .header__sound-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
flex-shrink: 0;
color: var(--muted);
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: var(--radius-sm);
cursor: pointer;
transition: color var(--duration-normal) var(--ease-out),
background var(--duration-normal) var(--ease-out),
border-color var(--duration-normal) var(--ease-out),
box-shadow var(--duration-normal) var(--ease-out);
}

.pbctf-landing .header__sound-toggle svg {
width: 18px;
height: 18px;
}

.pbctf-landing .header__sound-toggle:hover {
color: var(--primary);
background: rgba(0, 255, 136, 0.08);
border-color: rgba(0, 255, 136, 0.4);
box-shadow: 0 0 15px rgba(0, 255, 136, 0.15);
}

/* Lit accent when sound is enabled */
.pbctf-landing .header__sound-toggle[aria-pressed="true"] {
color: var(--primary);
border-color: rgba(0, 255, 136, 0.25);
}

/* --- CTA --- */
.pbctf-landing .btn.header__cta {
display: none;
Expand Down
Loading
Loading