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
12 changes: 10 additions & 2 deletions app/(pages)/(hackers)/(hub)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use client';

import PrizeTracks from '@pages/(hackers)/_components/PrizeTracks/PrizeTracks';
import BeginnersSection from '@pages/(hackers)/_components/HomeHacking/BeginnersSection';
import Discord from '@pages/(hackers)/_components/StayUpToDate/Discord';
import Footer from '@components/Footer/Footer';
import HeroMVP from '../_components/HomeHacking/HeroMVP';
import ClientTimeProtectedDisplay from '@pages/_components/TimeProtectedDisplay/ClientTimeProtectedDisplay';
import TableNumberCheckin from '@pages/(hackers)/_components/TableNumberCheckin/TableNumberCheckin';
import TableNumberContextProvider from '@pages/_contexts/TableNumberContext';
Expand All @@ -11,13 +12,20 @@ import ScheduleSneakPeek from '@pages/(hackers)/_components/HomeHacking/Schedule
import HeroJudging from '../_components/HomeJudging/HeroJudging';
import HackerChoiceAward from '../_components/HomeJudging/HackersChoiceAwards';
import HeroWaiting from '../_components/HomeJudging/HeroWaiting';
import HeroHacking from '../_components/HomeHacking/HeroHacking';
import { useRollout } from '@pages/_hooks/useRollout';

export default function Page() {
const { rolloutRes, loading } = useRollout('hacking-starts');
const rolloutTime = rolloutRes?.ok
? rolloutRes.body?.rollout_time
: undefined;

return (
<main id="home">
<TableNumberContextProvider>
<ClientTimeProtectedDisplay featureId="hero-hacking">
<HeroMVP />
<HeroHacking rolloutTime={rolloutTime} loading={loading} />
<ScheduleSneakPeek />
<BeginnersSection />
<MDHelp />
Expand Down
275 changes: 275 additions & 0 deletions app/(pages)/(hackers)/_components/HomeHacking/HeroHacking.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
'use client';

import Image from 'next/image';
import { GoArrowRight } from 'react-icons/go';

import ClientTimeProtectedDisplay from '@pages/_components/TimeProtectedDisplay/ClientTimeProtectedDisplay';
import Countdown from './_components/Countdown';

interface HeroHackingProps {
rolloutTime?: number;
loading?: boolean;
}

const ONE_DAY_MS = 24 * 60 * 60 * 1000;

export default function HeroHacking({
rolloutTime,
loading,
}: HeroHackingProps) {
const countdownTarget = rolloutTime ? rolloutTime + ONE_DAY_MS : undefined;

return (
<div className="w-full h-screen p-4 md:p-10">
<div className="relative w-full min-h-[86vh] overflow-hidden">
{/* content wrapper */}
<div className="relative z-10 w-full">
{/* Main grid area */}
<div className="mx-auto h-[90vh] min-h-0 flex flex-col justify-center gap-3 md:grid md:grid-rows-[minmax(1,1fr)] md:gap-5">
{/* Countdown bar */}
<div
className="relative flex flex-col justify-center rounded-[20px] md:rounded-[32px] bg-white/25 backdrop-blur-xl px-6 md:px-10 py-8 md:py-10 overflow-hidden"
style={{
background:
'linear-gradient(172deg, #46D8E9 43.03%, #76DEEB 63.28%, #FCFCD1 112.36%)',
}}
>
{/* background */}
<Image
src="/Hero/Clouds.svg"
alt="Background"
fill
className="object-cover pointer-events-none select-none -z-10"
/>

<div className="flex items-start justify-between gap-2">
{/* Right: countdown (big) */}
<div className="flex-1 flex justify-center">
<div className="text-white">
<ClientTimeProtectedDisplay
featureId="hacking-starts"
fallback={<Countdown />}
>
{countdownTarget !== undefined && (
<Countdown countdownTarget={countdownTarget} />
)}
</ClientTimeProtectedDisplay>

{loading && (
<div className="mt-2 text-white/90 text-sm text-right">
loading…
</div>
)}
</div>
</div>
</div>
</div>
{/* Everything below countdown must fit remaining height */}
<div className="relative min-h-0">
{/* Decorative stars */}
<Image
src="/hackers/hero/StarLeft.svg"
alt=""
width={120}
height={120}
className="
pointer-events-none select-none
absolute z-20
-left-2 top-[35%] md:top-[50%]
w-[56px] sm:w-[70px] md:w-[84px] lg:w-[96px]
rotate-[-8deg]
"
/>

<Image
src="/hackers/hero/StarRight.svg"
alt=""
width={120}
height={120}
className="
pointer-events-none select-none
absolute z-20
-right-2 top-[10%]
w-[56px] sm:w-[70px] md:w-[84px] lg:w-[96px]
rotate-[10deg]
"
/>
{/* ===================== MOBILE (2x2 animals) ===================== */}
<div className="md:hidden h-full flex flex-col justify-center">
{/* 2x2 animals */}
<div className="grid grid-cols-2 gap-2">
{/* Cow */}
<div className="relative rounded-[20px] bg-[#93F5F3] overflow-hidden flex items-end justify-center">
<Image
src="/hackers/hero/PeepingCow.svg"
alt="Peeking cow"
width={520}
height={320}
className="w-[85%] h-auto pointer-events-none select-none"
/>
</div>

{/* Duck */}
<div className="relative rounded-[20px] bg-[#FFF3B6] overflow-hidden flex items-end justify-center">
<Image
src="/hackers/hero/PeepingDuck.svg"
alt="Peeking duck"
width={520}
height={320}
className="w-[100%] h-auto pointer-events-none select-none"
/>
</div>

{/* Frog */}
<div className="relative rounded-[20px] bg-[#E9FBBA] overflow-hidden flex items-end justify-center">
<Image
src="/hackers/hero/PeepingFrog.svg"
alt="Peeking frog"
width={520}
height={320}
className="w-[65%] h-auto pointer-events-none select-none"
/>
</div>

{/* Bunny */}
<div className="relative rounded-[20px] bg-[#FFD2D2] overflow-hidden flex items-end justify-center">
<Image
src="/hackers/hero/PeepingBunny.svg"
alt="Peeking bunny"
width={520}
height={320}
className="w-[85%] h-auto pointer-events-none select-none"
/>
</div>
</div>

{/* Submit pill under grid on mobile */}
<a
href="https://hackdavis-2026.devpost.com/"
target="_blank"
rel="noopener noreferrer"
className="mt-2 group relative rounded-[1000px] bg-white/25 backdrop-blur-xl min-h-[110px] flex items-center justify-center overflow-hidden"
style={{
background:
'linear-gradient(172deg, #46D8E9 43.03%, #76DEEB 63.28%, #FCFCD1 112.36%)',
}}
>
{/* background */}
<Image
src="/Hero/Clouds.svg"
alt="Background"
fill
className="object-cover pointer-events-none select-none -z-10 overflow-hidden"
/>
<div className="absolute inset-0 opacity-70 pointer-events-none bg-gradient-to-r from-white/0 via-white/10 to-white/0" />
<div className="relative flex items-center gap-2">
<span className="text-white text-4xl md:text-5xl font-extrabold tracking-wide drop-shadow-[0_6px_14px_rgba(0,0,0,0.18)]">
SUBMIT!
</span>
<GoArrowRight className="text-white text-4xl md:text-5xl drop-shadow-[0_6px_14px_rgba(0,0,0,0.18)] transition-transform duration-300 group-hover:translate-x-1" />
</div>
</a>
</div>

{/* ===================== DESKTOP (dynamically fits remaining height) ===================== */}
<div className="hidden md:grid min-h-0 h-full grid-rows-[minmax(0,1fr)_minmax(0,1fr)] gap-5">
{/* Row 1: 3 tiles */}
<div className="min-h-0 grid grid-cols-[50fr_30fr_20fr] gap-5">
{/* Cow */}
<div className="min-h-0 relative rounded-[28px] bg-[#93F5F3] backdrop-blur-xl overflow-hidden flex items-end justify-center">
<Image
src="/hackers/hero/PeepingCow.svg"
alt="Peeking cow"
width={520}
height={320}
className="w-[55%] max-h-full h-auto pointer-events-none select-none"
/>
</div>

{/* Duck */}
<div className="min-h-0 relative rounded-[28px] bg-[#FFF3B6] overflow-hidden flex items-end justify-center">
<Image
src="/hackers/hero/PeepingDuck.svg"
alt="Peeking duck"
width={520}
height={320}
className="w-[100%] max-h-full h-auto pointer-events-none select-none"
/>
</div>

{/* Frog */}
<div className="min-h-0 relative rounded-[28px] bg-[#E9FBBA] backdrop-blur-xl overflow-hidden flex items-end justify-center">
<Image
src="/hackers/hero/PeepingFrog.svg"
alt="Peeking frog"
width={520}
height={320}
className="w-[90%] max-h-full h-auto pointer-events-none select-none"
/>
</div>
</div>

{/* Row 2: bunny + submit pill */}
<div className="min-h-0 grid grid-cols-[1fr_2fr] gap-5 items-stretch">
{/* Bunny */}
<div className="min-h-0 relative rounded-[28px] bg-[#FFD2D2] backdrop-blur-xl overflow-hidden flex items-end justify-center">
<Image
src="/hackers/hero/PeepingBunny.svg"
alt="Peeking bunny"
width={520}
height={320}
className="w-[85%] max-h-full h-auto pointer-events-none select-none"
/>
</div>

{/* Submit pill */}
<a
href="https://hackdavis-2026.devpost.com/"
target="_blank"
rel="noopener noreferrer"
className="group relative rounded-full bg-white/25 backdrop-blur-xl flex items-center justify-center overflow-hidden"
style={{
background:
'linear-gradient(172deg, #46D8E9 43.03%, #76DEEB 63.28%, #FCFCD1 112.36%)',
}}
>
{/* background */}
<Image
src="/Hero/Clouds.svg"
alt="Background"
fill
className="object-cover pointer-events-none select-none -z-10"
/>

<div className="absolute inset-0 opacity-70 pointer-events-none bg-gradient-to-r from-white/0 via-white/10 to-white/0" />

<div className="relative flex items-center gap-6">
<span
className="text-white font-medium tracking-[4.601px]"
style={{
fontSize: 'clamp(28px, 6vw, 90px)',
textShadow: '0 0 31.729px rgba(255, 255, 255, 0.30)',
}}
>
SUBMIT
</span>

<GoArrowRight
className="text-white transition-transform duration-300 group-hover:translate-x-3"
style={{
fontSize: 'clamp(40px, 5vw, 120px)',
textShadow: '0 0 31.729px rgba(255, 255, 255, 0.30)',
}}
/>
</div>
</a>
</div>
</div>
</div>
</div>
{/* end main grid area */}
</div>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
'use client';

import { useState, useEffect } from 'react';

interface CountdownProps {
countdownTarget?: number;
}

const HACKING_ENDS_TIME = new Date('2026-05-10T11:00:00-07:00').getTime(); // May 10, 2026 at 11:00 AM PDT

export default function Countdown({
countdownTarget = HACKING_ENDS_TIME,
}: CountdownProps) {
const [timeLeft, setTimeLeft] = useState({
hours: 24,
minutes: 0,
seconds: 0,
});

useEffect(() => {
const calculateTimeLeft = () => {
const now = Date.now();
const difference = countdownTarget - now;

if (difference <= 0) {
return { hours: 0, minutes: 0, seconds: 0 };
}

if (difference > 24 * 60 * 60 * 1000) {
return { hours: 24, minutes: 0, seconds: 0 };
}

return {
hours: Math.floor(difference / (1000 * 60 * 60)),
minutes: Math.floor((difference / (1000 * 60)) % 60),
seconds: Math.floor((difference / 1000) % 60),
};
};

setTimeLeft(calculateTimeLeft());

const timer = setInterval(() => {
setTimeLeft(calculateTimeLeft());
}, 1000);

return () => clearInterval(timer);
}, [countdownTarget]);

const timerStyle = {
color: '#FFF',
fontFamily: 'DM Mono',
fontSize: 'clamp(36px, 12vw, 230.034px)',
fontStyle: 'normal',
fontWeight: 500,
lineHeight: 'normal',
letterSpacing: '4.601px',
textShadow: '0 0 31.729px rgba(255, 255, 255, 0.30)',
};

return (
<div className="flex flex-col items-end text-white">
{/* BIG TIMER */}
<div className="flex items-end gap-3 md:gap-6" style={timerStyle}>
<span className="leading-none">
{timeLeft.hours.toString().padStart(2, '0')}
</span>

<span className="leading-none">:</span>

<span className="leading-none">
{timeLeft.minutes.toString().padStart(2, '0')}
</span>

<span className="leading-none">:</span>

<span className="leading-none">
{timeLeft.seconds.toString().padStart(2, '0')}
</span>
</div>

{/* LABEL ROW */}
{/* <div className="flex gap-8 md:gap-14 mt-3 text-xs md:text-sm font-semibold tracking-widest opacity-90">
<span>HOURS</span>
<span>MINUTES</span>
<span>SECONDS</span>
</div> */}
</div>
);
}
Loading