Skip to content

Commit 7fb78f7

Browse files
authored
Merge branch 'pointblank-club:staging' into staging
2 parents 423e035 + 74b4417 commit 7fb78f7

21 files changed

Lines changed: 938 additions & 54 deletions

File tree

.github/workflows/sync-fork.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
uses: actions/checkout@v4
1919
with:
2020
fetch-depth: 0 # Fetch all history for proper merging
21-
token: ${{ secrets.GITHUB_TOKEN }}
21+
token: ${{ secrets.PAT_TOKEN }}
2222

2323
- name: Configure Git
2424
run: |
@@ -27,7 +27,7 @@ jobs:
2727
2828
- name: Add upstream remote
2929
run: |
30-
UPSTREAM_REPO="https://github.com/pbdsce/Zenith.git"
30+
UPSTREAM_REPO="https://github.com/pointblank-club/pbctf.git"
3131
3232
# Check if upstream remote already exists
3333
if git remote | grep -q "^upstream$"; then

app/layout.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ export const metadata: Metadata = {
1515
"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.",
1616
viewport: "width=device-width, initial-scale=1",
1717
themeColor: "#050507",
18-
icons: { icon: "/images/pbctf-logo.svg" },
18+
icons: {
19+
icon: [
20+
{ url: "/favicon.ico", sizes: "any" },
21+
{ url: "/icon.png", type: "image/png", sizes: "32x32" },
22+
],
23+
apple: "/apple-touch-icon.png",
24+
},
1925
};
2026

2127
export default function RootLayout({

components/landing/AboutPointBlank/AboutPointBlank.jsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import { gsap } from 'gsap';
33
import { ScrollTrigger } from 'gsap/ScrollTrigger';
44
import { useGSAP } from '@gsap/react';
55
import { GridScan } from '../GridScan/GridScan';
6+
import { useRetroSound } from '../hooks/useRetroSound';
67
import './AboutPointBlank.css';
78

89
gsap.registerPlugin(ScrollTrigger);
910

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

1416
useGSAP(() => {
1517
const content = sectionRef.current.querySelector('.about__content');
@@ -56,15 +58,15 @@ export default function AboutPointBlank() {
5658
<p>
5759
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.
5860
</p>
59-
<button className="about__play-btn--monitor" onClick={() => setIsVideoPlaying(true)} aria-label="Play Video" title="Watch Video">
61+
<button className="about__play-btn--monitor" onClick={() => { playWindowOpen(); setIsVideoPlaying(true); }} onMouseEnter={playHover} aria-label="Play Video" title="Watch Video">
6062
<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16">
6163
<path d="M8 5v14l11-7z" />
6264
</svg>
6365
</button>
6466
</>
6567
) : (
6668
<div className="about__video-container">
67-
<button className="about__close-btn" onClick={() => setIsVideoPlaying(false)} aria-label="Close Video">
69+
<button className="about__close-btn" onClick={() => { playWindowClose(); setIsVideoPlaying(false); }} onMouseEnter={playHover} aria-label="Close Video">
6870
6971
</button>
7072
<div className="about__video-wrapper">

components/landing/Categories/Categories.jsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { motion, AnimatePresence } from 'framer-motion';
33
import { gsap } from 'gsap';
44
import { ScrollTrigger } from 'gsap/ScrollTrigger';
55
import { useGSAP } from '@gsap/react';
6+
import { useRetroSound } from '../hooks/useRetroSound';
67
import './Categories.css';
78

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

183185
useEffect(() => {
184186
const interval = setInterval(() => {
@@ -228,10 +230,11 @@ export default function Categories() {
228230
</div>
229231
<div className="categories__nav-list">
230232
{CATEGORIES.map((cat) => (
231-
<button
232-
key={cat.id}
233+
<button
234+
key={cat.id}
233235
className={`categories__nav-item ${activeId === cat.id ? 'active' : ''}`}
234-
onClick={() => setActiveId(cat.id)}
236+
onMouseEnter={playHover}
237+
onClick={() => { playSelect(); setActiveId(cat.id); }}
235238
>
236239
<span className="categories__nav-title">{cat.title}</span>
237240
{activeId === cat.id && <span className="categories__nav-indicator"></span>}

components/landing/FAQ/FAQ.jsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { motion, AnimatePresence } from 'framer-motion';
33
import { gsap } from 'gsap';
44
import { ScrollTrigger } from 'gsap/ScrollTrigger';
55
import { useGSAP } from '@gsap/react';
6+
import { useRetroSound } from '../hooks/useRetroSound';
67
import './FAQ.css';
78

89
gsap.registerPlugin(ScrollTrigger);
@@ -56,6 +57,7 @@ export default function FAQ() {
5657
const [activeCategory, setActiveCategory] = useState(categories[0]);
5758
const [openIndex, setOpenIndex] = useState(null);
5859
const sectionRef = useRef(null);
60+
const { playHover, playSelect, playWindowOpen, playWindowClose } = useRetroSound();
5961

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

7476
const toggle = (index) => {
75-
setOpenIndex((prev) => (prev === index ? null : index));
77+
setOpenIndex((prev) => {
78+
if (prev === index) {
79+
playWindowClose();
80+
return null;
81+
}
82+
playWindowOpen();
83+
return index;
84+
});
7685
};
7786

7887
const handleCategoryChange = (category) => {
88+
playSelect();
7989
setActiveCategory(category);
8090
setOpenIndex(null); // reset open accordion when switching
8191
};
@@ -111,6 +121,7 @@ export default function FAQ() {
111121
<button
112122
className="faq__question"
113123
onClick={() => toggle(i)}
124+
onMouseEnter={playHover}
114125
aria-expanded={isOpen}
115126
>
116127
<span className="faq__prompt">
@@ -166,13 +177,15 @@ export default function FAQ() {
166177
<div className="action-btn-wrapper">
167178
<button
168179
className={`gameboy__btn ${activeCategory === categories[0] ? 'active' : ''}`}
180+
onMouseEnter={playHover}
169181
onClick={() => handleCategoryChange(categories[0])}
170182
>A</button>
171183
<span className="gameboy__btn-label">{categories[0]}</span>
172184
</div>
173185
<div className="action-btn-wrapper btn-raised">
174186
<button
175187
className={`gameboy__btn ${activeCategory === categories[1] ? 'active' : ''}`}
188+
onMouseEnter={playHover}
176189
onClick={() => handleCategoryChange(categories[1])}
177190
>B</button>
178191
<span className="gameboy__btn-label">{categories[1]}</span>
@@ -182,13 +195,15 @@ export default function FAQ() {
182195
<div className="action-btn-wrapper">
183196
<button
184197
className={`gameboy__btn ${activeCategory === categories[2] ? 'active' : ''}`}
198+
onMouseEnter={playHover}
185199
onClick={() => handleCategoryChange(categories[2])}
186200
>X</button>
187201
<span className="gameboy__btn-label">{categories[2]}</span>
188202
</div>
189203
<div className="action-btn-wrapper btn-raised">
190204
<button
191205
className={`gameboy__btn ${activeCategory === categories[3] ? 'active' : ''}`}
206+
onMouseEnter={playHover}
192207
onClick={() => handleCategoryChange(categories[3])}
193208
>Y</button>
194209
<span className="gameboy__btn-label">{categories[3]}</span>

components/landing/FinalCTA/FinalCTA.jsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ import { useRef } from 'react';
22
import { gsap } from 'gsap';
33
import { ScrollTrigger } from 'gsap/ScrollTrigger';
44
import { useGSAP } from '@gsap/react';
5+
import { useRetroSound } from '../hooks/useRetroSound';
56
import './FinalCTA.css';
67

78
gsap.registerPlugin(ScrollTrigger);
89

910
export default function FinalCTA() {
1011
const sectionRef = useRef(null);
12+
const { playHover, playClick } = useRetroSound();
1113

1214
useGSAP(() => {
1315
const headline = sectionRef.current.querySelector('.final-cta__headline');
@@ -39,7 +41,13 @@ export default function FinalCTA() {
3941
compete against some of the brightest minds in cybersecurity.
4042
</p>
4143
<div className="final-cta__actions">
42-
<a href="/register" className="btn btn--primary" id="cta-register-now">
44+
<a
45+
href="/register"
46+
className="btn btn--primary"
47+
id="cta-register-now"
48+
onMouseEnter={playHover}
49+
onClick={playClick}
50+
>
4351
Register Now
4452
</a>
4553

components/landing/Footer/Footer.jsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useRetroSound } from '../hooks/useRetroSound';
12
import './Footer.css';
23

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

4950
export default function Footer() {
51+
const { playHover, playClick } = useRetroSound();
52+
5053
return (
5154
<footer id="footer" className="footer">
5255
<div className="footer__bottom-content">
@@ -74,6 +77,8 @@ export default function Footer() {
7477
target="_blank"
7578
rel="noopener noreferrer"
7679
aria-label={s.label}
80+
onMouseEnter={playHover}
81+
onClick={playClick}
7782
>
7883
{s.icon}
7984
</a>

components/landing/Header/Header.css

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,50 @@
118118
transform: translateX(0);
119119
}
120120

121+
/* --- Right-side controls --- */
122+
.pbctf-landing .header__actions {
123+
display: flex;
124+
align-items: center;
125+
gap: var(--space-3);
126+
}
127+
128+
/* --- Sound toggle --- */
129+
.pbctf-landing .header__sound-toggle {
130+
display: inline-flex;
131+
align-items: center;
132+
justify-content: center;
133+
width: 36px;
134+
height: 36px;
135+
flex-shrink: 0;
136+
color: var(--muted);
137+
background: rgba(255, 255, 255, 0.03);
138+
border: 1px solid rgba(255, 255, 255, 0.06);
139+
border-radius: var(--radius-sm);
140+
cursor: pointer;
141+
transition: color var(--duration-normal) var(--ease-out),
142+
background var(--duration-normal) var(--ease-out),
143+
border-color var(--duration-normal) var(--ease-out),
144+
box-shadow var(--duration-normal) var(--ease-out);
145+
}
146+
147+
.pbctf-landing .header__sound-toggle svg {
148+
width: 18px;
149+
height: 18px;
150+
}
151+
152+
.pbctf-landing .header__sound-toggle:hover {
153+
color: var(--primary);
154+
background: rgba(0, 255, 136, 0.08);
155+
border-color: rgba(0, 255, 136, 0.4);
156+
box-shadow: 0 0 15px rgba(0, 255, 136, 0.15);
157+
}
158+
159+
/* Lit accent when sound is enabled */
160+
.pbctf-landing .header__sound-toggle[aria-pressed="true"] {
161+
color: var(--primary);
162+
border-color: rgba(0, 255, 136, 0.25);
163+
}
164+
121165
/* --- CTA --- */
122166
.pbctf-landing .btn.header__cta {
123167
display: none;

0 commit comments

Comments
 (0)