diff --git a/sites/hacklytics2027/app/page.tsx b/sites/hacklytics2027/app/page.tsx index 6aca16b3..51511a37 100644 --- a/sites/hacklytics2027/app/page.tsx +++ b/sites/hacklytics2027/app/page.tsx @@ -1,232 +1,181 @@ "use client"; -import React, { useState, useEffect } from "react"; +import React, { useEffect, useState } from "react"; import HomeSections from "@/components/HomeSections"; -import PixelGarden, { PixelGround } from "@/components/pixel/PixelGarden"; +import PixelGarden from "@/components/pixel/PixelGarden"; import PixelSprite from "@/components/pixel/PixelSprite"; -import { BLOOM, DAISY, SPROUT, TULIP } from "@/components/pixel/sprites"; +import { GLOBE } from "@/components/pixel/sprites"; import { INTEREST_URL } from "@/lib/links"; -// ─── Elegant Floral Background ───────────────────────────────────────────── -const FloralBackground = () => ( -
- {/* Subtle grid base */} -
- - {/* Center digital bloom (rotating lotus). - No backdrop-blur on the petals: they sit over a near-black backdrop, so - the blur produced nothing visible while forcing 20 full-screen readbacks - per frame while rotating. Gradients alone look identical here. */} -
- {Array.from({ length: 12 }).map((_, i) => ( -
- ))} -
+/** Hacklytics 2027 opens Feb 26, 00:00 America/New_York. */ +const EVENT_TZ = "America/New_York"; +const EVENT_LOCAL = "2027-02-26T00:00:00"; + +function zonedLocalToUtcMs(localIso: string, timeZone: string): number { + const [datePart, timePart = "00:00:00"] = localIso.split("T"); + const [year, month, day] = datePart.split("-").map(Number); + const [hour, minute, second] = timePart.split(":").map(Number); + const utcGuess = Date.UTC(year, month - 1, day, hour, minute, second); + + const dtf = new Intl.DateTimeFormat("en-US", { + timeZone, + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + hourCycle: "h23", + }); + + const readAsUtc = (ms: number) => { + const bag: Record = {}; + for (const { type, value } of dtf.formatToParts(new Date(ms))) { + if (type !== "literal") bag[type] = Number(value); + } + return Date.UTC(bag.year, bag.month - 1, bag.day, bag.hour, bag.minute, bag.second); + }; + + // Two refinements land on the correct offset around DST. + let utc = utcGuess + (utcGuess - readAsUtc(utcGuess)); + utc = utc + (utcGuess - readAsUtc(utc)); + return utc; +} - {/* Secondary Bloom */} -
- {Array.from({ length: 8 }).map((_, i) => ( -
- ))} -
+const TARGET_MS = zonedLocalToUtcMs(EVENT_LOCAL, EVENT_TZ); - {/* Ambient light fields — static radial gradients instead of pulsing - blur() layers, which re-rasterized a 50vw surface every frame. */} -
-
-
-); - -// ─── Countdown ──────────────────────────────────────────────────────────── -const Countdown: React.FC<{ targetDate: Date }> = ({ targetDate }) => { - const getTimeLeft = React.useCallback(() => { - const distance = targetDate.getTime() - Date.now(); - if (distance <= 0) return null; - return { - days: Math.floor(distance / (1000 * 60 * 60 * 24)), - hours: Math.floor((distance / (1000 * 60 * 60)) % 24), - minutes: Math.floor((distance / (1000 * 60)) % 60), - seconds: Math.floor((distance / 1000) % 60), - }; - }, [targetDate]); - - const [timeLeft, setTimeLeft] = useState<{ days: number; hours: number; minutes: number; seconds: number } | null>(null); - const [mounted, setMounted] = useState(false); +function splitRemaining(now: number) { + const distance = Math.max(0, TARGET_MS - now); + return { + days: Math.floor(distance / (1000 * 60 * 60 * 24)), + hours: Math.floor((distance / (1000 * 60 * 60)) % 24), + minutes: Math.floor((distance / (1000 * 60)) % 60), + seconds: Math.floor((distance / 1000) % 60), + }; +} + +const Countdown: React.FC = () => { + const [now, setNow] = useState(() => Date.now()); useEffect(() => { - setMounted(true); - setTimeLeft(getTimeLeft()); - const id = setInterval(() => setTimeLeft(getTimeLeft()), 1000); - return () => clearInterval(id); - }, [getTimeLeft]); + const tick = () => setNow(Date.now()); + tick(); + const id = window.setInterval(tick, 250); + return () => window.clearInterval(id); + }, []); - const fmt = (n?: number) => String(n ?? 0).padStart(2, "0"); + const time = splitRemaining(now); + const pad = (n: number) => String(n).padStart(2, "0"); const units = [ - { label: "Days", value: timeLeft?.days, color: "text-white" }, - { label: "Hours", value: timeLeft?.hours, color: "text-white/80" }, - { label: "Minutes", value: timeLeft?.minutes, color: "text-white/60" }, - { label: "Seconds", value: timeLeft?.seconds, color: "text-white/40" }, + { label: "DAYS", value: String(time.days), accent: true }, + { label: "HRS", value: pad(time.hours), accent: false }, + { label: "MIN", value: pad(time.minutes), accent: false }, + { label: "SEC", value: pad(time.seconds), accent: false }, ]; return ( -
- {units.map(({ label, value, color }) => ( -
- - {mounted ? fmt(value) : "00"} - - +
+
+ {units.map(({ label, value, accent }) => ( +
+ + {value} + +
+ ))} +
+
+
+ {units.map(({ label }) => ( + {label} -
- ))} + ))} +
); }; -// ─── Page ───────────────────────────────────────────────────────────────── export default function HomePage() { return ( -
- - - - +
{/* ── HERO ── */} -
+
+ - {/* Synthwave horizon grid */} -
-
+
- - {/* Required for the spin animation if not in tailwind config */} -