This commit is contained in:
Kismet Hasanaj
2026-05-03 01:16:43 +02:00
parent c22d54abb0
commit 74a878bc62
5 changed files with 33 additions and 239 deletions
+23 -109
View File
@@ -9,94 +9,36 @@
//
// How this file is organised, top to bottom:
// 1. Imports + setup
// 2. Intro overlay (the animated logo shown on first visit)
// 3. Site shell (background gradient + ambient orbs + grid)
// 4. Header (logo + navigation)
// 5. Hero (big headline + buttons)
// 6. Services (three cards under "What we do")
// 7. How we engage (three cards under "Working with us")
// 8. Contact (the dark contact card)
// 9. Footer (company details + copyright)
// 2. Site shell (background gradient + ambient orbs + grid)
// 3. Header (logo + navigation)
// 4. Hero (big headline + buttons)
// 5. Services (three cards under "What we do")
// 6. How we engage (three cards under "Working with us")
// 7. Contact (the dark contact card)
// 8. Footer (company details + copyright)
//
// Each section is marked with a clear comment header you can search for.
// The "use client" line at the top tells Next.js this page runs in the
// browser (needed for the animated intro and the mouse-tracking glow).
// browser (needed for the mouse-tracking glow).
// =============================================================================
import Image from "next/image";
import Link from "next/link";
import { useEffect, useLayoutEffect, useMemo, useState } from "react";
import { useMemo, useState } from "react";
import { site } from "@/content";
import { openCookieBanner } from "@/components/CookieBanner";
// Type for the mouse-pointer position used to move the background glow.
type PointerState = { x: number; y: number };
const INTRO_SEEN_KEY = "novarix-intro-seen";
const INTRO_EVENT = "novarix:start-intro";
export default function HomePage() {
// ---------------------------------------------------------------------------
// STATE
// ---------------------------------------------------------------------------
// showIntro — true while the animated logo intro is playing.
// pointer — the mouse position (in % of page width/height). Used by the
// soft glow that follows the cursor in the background.
// pointer — the mouse position (in % of page width/height). Used by the
// soft glow that follows the cursor in the background.
// ---------------------------------------------------------------------------
const [showIntro, setShowIntro] = useState(false);
const [pointer, setPointer] = useState<PointerState>({ x: 50, y: 22 });
// ---------------------------------------------------------------------------
// INTRO OVERLAY EFFECT
// Plays the animated logo + wordmark the first time someone visits the
// site in this browser tab. The initial check runs in useLayoutEffect so
// the intro can mount before the first browser paint, avoiding a visible
// flash of the homepage underneath.
// ---------------------------------------------------------------------------
useLayoutEffect(() => {
function playIntro() {
setShowIntro(true);
try {
window.sessionStorage.setItem(INTRO_SEEN_KEY, "true");
} catch {
/* storage unavailable — silently ignore */
}
const timer = window.setTimeout(() => {
setShowIntro(false);
}, 3950);
return timer;
}
let timer: number | null = null;
try {
const introSeen = window.sessionStorage.getItem(INTRO_SEEN_KEY);
const consent = window.localStorage.getItem("novarix-cookie-consent");
const shouldPlayNow = consent === "ack" && !introSeen;
if (shouldPlayNow) {
timer = playIntro();
} else {
setShowIntro(false);
}
} catch {
setShowIntro(false);
}
function handleStartIntro() {
if (timer !== null) window.clearTimeout(timer);
timer = playIntro();
}
window.addEventListener(INTRO_EVENT, handleStartIntro);
return () => {
if (timer !== null) window.clearTimeout(timer);
window.removeEventListener(INTRO_EVENT, handleStartIntro);
};
}, []);
// ---------------------------------------------------------------------------
// BACKGROUND POSITION
// Translates the current mouse pointer into two CSS variables (--mx, --my)
@@ -113,44 +55,17 @@ export default function HomePage() {
);
return (
<>
{/* =====================================================================
INTRO OVERLAY
A single self-contained animated SVG of the Novarix wordmark,
drawn dead-centre in the viewport. The SVG itself contains all the
animation (strokes draw over 2s, fill in over the next 1s — see
/public/branding/animated_logo_intro.svg). Hidden after ~4s
(see the useEffect above + the CSS fade-out timing).
===================================================================== */}
{showIntro && (
<div className="intro-overlay" aria-hidden="true">
<div className="intro-backdrop" />
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src="/branding/animated_logo_intro.svg"
alt=""
className="intro-svg"
/>
</div>
)}
{/* =====================================================================
SITE SHELL
The <main> wraps the whole page. It tracks the mouse so the
background glow can follow the cursor, and applies the .site-shell
class (defined in globals.css) for the background gradient.
===================================================================== */}
<main
id="top"
className={`site-shell ${showIntro ? "intro-running" : ""}`}
style={backgroundStyle}
onMouseMove={(event) => {
const rect = event.currentTarget.getBoundingClientRect();
const x = ((event.clientX - rect.left) / rect.width) * 100;
const y = ((event.clientY - rect.top) / rect.height) * 100;
setPointer({ x, y });
}}
>
<main
id="top"
className="site-shell"
style={backgroundStyle}
onMouseMove={(event) => {
const rect = event.currentTarget.getBoundingClientRect();
const x = ((event.clientX - rect.left) / rect.width) * 100;
const y = ((event.clientY - rect.top) / rect.height) * 100;
setPointer({ x, y });
}}
>
{/* -------------------------------------------------------------------
AMBIENT LAYER
Two soft floating "orbs" and a faint grid behind everything.
@@ -512,7 +427,6 @@ export default function HomePage() {
</div>
</div>
</footer>
</main>
</>
</main>
);
}