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
14 changes: 14 additions & 0 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,15 @@ import OperationTimeline from '../src/components/landing/OperationTimeline';
import Footer from '../src/components/shared/Footer';
import FloatingContactButton from '@/src/components/landing/FloatingContactForm';
import LandingChampionshipWorks from '../src/components/landing/HowItWorks';
import RegistrationEndingPopup from '../src/components/landing/RegistrationEndingPopup';
export default function LandingPage() {
const [showRegPopup, setShowRegPopup] = useState(false);

useEffect(() => {
const timer = setTimeout(() => setShowRegPopup(true), 3000);
return () => clearTimeout(timer);
}, []);

return (
<div className="bg-obsidian min-h-screen text-ghost-white relative overflow-x-hidden" id="landing-page">
{/* Blurred Satellite Background */}
Expand Down Expand Up @@ -72,6 +80,12 @@ export default function LandingPage() {

{/* Floating Contact Button */}
<FloatingContactButton />

{/* Registration Ending Popup */}
<RegistrationEndingPopup
isOpen={showRegPopup}
onClose={() => setShowRegPopup(false)}
/>
</div>
);
}
6 changes: 3 additions & 3 deletions src/components/landing/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,8 @@ const LandingHeader = () => {
<div className="flex flex-col w-full max-w-4xl mt-12 md:mt-20 sm:mt-8">
{/* Content Box with Border */}

<SectionCard className="mb-0 w-full !p-5 md:!py-10 md:!px-8" paddingClassName="p-0">
<h1 className="text-[28px] sm:text-4xl md:text-5xl lg:text-6xl font-black font-heading mb-4 text-ghost-white text-left break-words leading-tight">
<SectionCard className="mb-4 w-full !p-5 md:!py-10 md:!px-8" paddingClassName="p-0">
<h1 className="text-[clamp(1.4rem,8vw,3.2rem)] font-black font-heading mb-6 text-ghost-white text-left break-words leading-tight">
<span className="block mb-2">
<GlitchText
text="India's Cybersecurity Championship"
Expand All @@ -341,7 +341,7 @@ const LandingHeader = () => {
</div>
</SectionCard>

<div className="grid grid-cols-1 md:grid-cols-2 gap-4 lg:gap-6 justify-center w-full mt-6 md:mt-8">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 lg:gap-6 justify-center w-full mt-8 md:mt-12">
<Link href="https://register.inctf.in" target="_blank" rel="noopener noreferrer" className="plain-link w-full">
<motion.button
initial={{ opacity: 0, y: 20, boxShadow: "4px 4px 0 rgba(255,255,255,0.5)" }}
Expand Down
165 changes: 165 additions & 0 deletions src/components/landing/RegistrationEndingPopup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import React, { useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import gsap from 'gsap';
import { X } from 'lucide-react';

interface RegistrationEndingPopupProps {
isOpen: boolean;
onClose: () => void;
}

const RegistrationEndingPopup: React.FC<RegistrationEndingPopupProps> = ({ isOpen, onClose }) => {
const overlayRef = useRef<HTMLDivElement>(null);
const modalRef = useRef<HTMLDivElement>(null);
const progressBarRef = useRef<HTMLDivElement>(null);
const [mounted, setMounted] = useState(false);

useEffect(() => {
setMounted(true);
}, []);

useEffect(() => {
if (isOpen && mounted) {
const tl = gsap.timeline();

tl.to(overlayRef.current, {
opacity: 1,
duration: 0.4,
ease: 'power2.out',
});

tl.fromTo(modalRef.current,
{ scaleY: 0, scaleX: 0.05, opacity: 0 },
{ scaleY: 1, opacity: 1, duration: 0.3, ease: 'power2.inOut' }
)
.to(modalRef.current, {
scaleX: 1,
duration: 0.3,
ease: 'back.out(1.2)'
});

if (progressBarRef.current) {
gsap.fromTo(progressBarRef.current,
{ width: '100%' },
{ width: '0%', duration: 25, ease: 'none', delay: 0.6 }
);
}

const timer = setTimeout(() => {
handleClose();
}, 25600);

return () => {
clearTimeout(timer);
tl.kill();
};
}
}, [isOpen, mounted]);

const handleClose = () => {
const tl = gsap.timeline({ onComplete: onClose });
tl.to(modalRef.current, { scale: 0.85, opacity: 0, y: 30, duration: 0.3, ease: 'power2.in' })
.to(overlayRef.current, { opacity: 0, duration: 0.3 }, '<');
};

if (!isOpen || !mounted) return null;

return createPortal(
<div className="fixed inset-0 z-[9999] flex items-center justify-center p-4">
<div
ref={overlayRef}
className="absolute inset-0 bg-[#020610]/90 opacity-0"
onClick={handleClose}
/>

<div
ref={modalRef}
className="relative z-10 w-full max-w-[500px] opacity-0"
>
<div
className="relative"
style={{
border: '3px solid #FF0000',
boxShadow:
'0 0 18px rgba(244,63,94,0.35), 0 0 60px rgba(244,63,94,0.08), inset 0 0 18px rgba(244,63,94,0.08)',
background: 'transparent',
}}
>
<div
className="relative overflow-y-auto overflow-x-hidden p-5 pt-8 md:p-8 md:pt-10 max-h-[75svh] scrollbar-thin scrollbar-thumb-red-500/30 scrollbar-track-transparent"
style={{
margin: '4px',
border: '1px solid rgba(255,0,0,0.25)',
background: '#060a16',
}}
>
{/* Timer bar */}
<div className="absolute top-0 left-0 right-0 h-[2px]">
<div
ref={progressBarRef}
className="h-full"
style={{ background: '#FF0000', boxShadow: '0 0 10px rgba(255,0,0,0.7)' }}
/>
</div>

{/* Close button */}
<button
onClick={handleClose}
className="absolute top-3 right-3 z-20 p-2 rounded-md text-red-500 border border-red-500/60 bg-red-500/10 hover:bg-red-600 hover:text-white hover:border-red-600 hover:shadow-[0_0_20px_rgba(255,0,0,0.9)] hover:scale-110 transition-all duration-200"
>
<X size={20} />
</button>

{/* Content */}
<div className="relative z-10 text-center flex flex-col items-center mt-6">
<h2 className="font-heading text-2xl md:text-3xl font-bold text-[#e2e8f0] mb-2">
Registration Ends Soon!
</h2>

<p className="font-mono text-[#94a3b8] text-sm md:text-base mb-6 max-w-sm">
Last chance to secure your spot and compete in the CTF.
</p>

<div className="flex items-center gap-3 font-mono text-[#FF0000] text-base md:text-lg mb-6"
style={{ textShadow: '0 0 10px rgba(255,0,0,0.3)' }}
>
<span className="text-2xl">📅</span>
<span className="font-bold">Qualifiers: 20th–21st June</span>
</div>

<a
href="https://register.inctf.in"
target="_blank"
rel="noopener noreferrer"
className="inline-block font-heading text-base md:text-lg font-semibold text-white bg-gradient-to-br from-red-900 to-red-800 border-2 border-white/60 px-8 py-3 rounded-sm hover:shadow-[0_0_30px_rgba(244,63,94,0.6)] hover:border-white transition-all duration-200"
>
Sign Up Now 🚀
</a>
</div>

{/* Corner brackets */}
<div className="absolute top-0 left-0">
<div className="absolute top-0 left-0 w-8 h-[4px] bg-[#FF0000]" style={{ boxShadow: '0 0 8px rgba(255,0,0,0.6)' }} />
<div className="absolute top-0 left-0 h-8 w-[4px] bg-[#FF0000]" style={{ boxShadow: '0 0 8px rgba(255,0,0,0.6)' }} />
</div>
<div className="absolute top-0 right-0">
<div className="absolute top-0 right-0 w-8 h-[4px] bg-[#FF0000]" style={{ boxShadow: '0 0 8px rgba(255,0,0,0.6)' }} />
<div className="absolute top-0 right-0 h-8 w-[4px] bg-[#FF0000]" style={{ boxShadow: '0 0 8px rgba(255,0,0,0.6)' }} />
</div>
<div className="absolute bottom-0 left-0">
<div className="absolute bottom-0 left-0 w-8 h-[4px] bg-[#FF0000]" style={{ boxShadow: '0 0 8px rgba(255,0,0,0.6)' }} />
<div className="absolute bottom-0 left-0 h-8 w-[4px] bg-[#FF0000]" style={{ boxShadow: '0 0 8px rgba(255,0,0,0.6)' }} />
</div>
<div className="absolute bottom-0 right-0">
<div className="absolute bottom-0 right-0 w-8 h-[4px] bg-[#FF0000]" style={{ boxShadow: '0 0 8px rgba(255,0,0,0.6)' }} />
<div className="absolute bottom-0 right-0 h-8 w-[4px] bg-[#FF0000]" style={{ boxShadow: '0 0 8px rgba(255,0,0,0.6)' }} />
</div>
</div>
</div>
</div>
</div>,
document.body
);
};

export default RegistrationEndingPopup;
78 changes: 27 additions & 51 deletions src/components/shared/GlitchText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,28 +62,22 @@ const GlitchText = ({ text, className = "", delay = 0, strikethrough = false, tr
}, [started, text, triggerOnView]);

return (
<span ref={ref} className={`relative inline ${className}`}>
<span ref={ref} className={`relative inline-block ${className}`}>
<style jsx>{`
@keyframes glitch {
2%, 64% { transform: translate(2px, 0) skew(0deg); }
4%, 60% { transform: translate(-2px, 0) skew(0deg); }
62% { transform: translate(0, 0) skew(5deg); }
@keyframes glitchSlow {
0%, 90%, 100% { transform: translate(0.04em, 0); }
92% { transform: translate(0.08em, -0.01em); }
94% { transform: translate(0.02em, 0.01em); }
}
@keyframes glitchTop {
2%, 64% { transform: translate(2px, -2px); }
4%, 60% { transform: translate(-2px, 2px); }
62% { transform: translate(13px, -1px) skew(-13deg); }
}
@keyframes glitchBottom {
2%, 64% { transform: translate(-2px, 0); }
4%, 60% { transform: translate(-2px, 0); }
62% { transform: translate(-22px, 5px) skew(21deg); }
@keyframes glitchSlowBottom {
0%, 90%, 100% { transform: translate(-0.04em, 0); }
92% { transform: translate(-0.08em, 0.01em); }
94% { transform: translate(-0.02em, -0.01em); }
}

.glitch-wrapper {
position: relative;
display: inline;
animation: glitch 1s linear infinite;
display: inline-block;
}

.glitch-wrapper::before,
Expand All @@ -94,57 +88,39 @@ const GlitchText = ({ text, className = "", delay = 0, strikethrough = false, tr
top: 0;
width: 100%;
height: 100%;
z-index: -1;
pointer-events: none;
opacity: 1;
}

.glitch-wrapper::before {
color: #ff0000;
animation: glitchTop 1s linear infinite;
clip-path: polygon(0 0, 100% 0, 100% 33%, 0 33%);
-webkit-clip-path: polygon(0 0, 100% 0, 100% 33%, 0 33%);
animation: glitchSlow 8s ease-in-out infinite;
}

.glitch-wrapper::after {
color: #00ffff;
animation: glitchBottom 1.5s linear infinite;
clip-path: polygon(0 67%, 100% 67%, 100% 100%, 0 100%);
-webkit-clip-path: polygon(0 67%, 100% 67%, 100% 100%, 0 100%);
animation: glitchSlowBottom 8s ease-in-out infinite;
}

.glitch-main {
position: relative;
z-index: 1;
color: inherit; /* Inherit color to allow customization */
text-shadow:
1px 1px 0 rgba(0, 255, 255, 0.8),
2px 2px 0 rgba(0, 255, 255, 0.7),
3px 3px 0 rgba(0, 255, 255, 0.6),
4px 4px 0 rgba(0, 200, 200, 0.5),
5px 5px 0 rgba(0, 150, 150, 0.4),
6px 6px 0 rgba(255, 0, 0, 0.3),
7px 7px 0 rgba(255, 0, 0, 0.2),
8px 8px 20px rgba(0, 0, 0, 0.5);
transform: skewY(-2deg);
color: inherit;
/* No shadow, no blur - pure clarity */
}
`}</style>

{/* Invisible spacer to reserve layout space */}
<span className="opacity-0 pointer-events-none select-none" aria-hidden="true">
{text}
</span>

{/* Overlay for the actual effect */}
<span className="absolute top-0 left-0 w-full">
{!typingComplete ? (
<span className="glitch-main">
{displayText}
<span className="animate-pulse">|</span>
</span>
) : (
<span className="glitch-wrapper" data-text={text}>
<span className="glitch-main">{text}</span>
</span>
)}
</span>
{!typingComplete ? (
<span className="glitch-main">
{displayText}
<span className="animate-pulse">|</span>
</span>
) : (
<span className="glitch-wrapper" data-text={text}>
<span className="glitch-main">{text}</span>
</span>
)}

{/* Strikethrough Lines */}
{strikethrough && (
Expand Down
Loading