diff --git a/app/cookies/page.tsx b/app/cookies/page.tsx index 9813e4eb..0c341521 100644 --- a/app/cookies/page.tsx +++ b/app/cookies/page.tsx @@ -78,8 +78,8 @@ export default function CookiePolicyPage() { What we store

- We use two small browser storage entries — both first-party, - both confined to your browser, neither shared with anyone: + We use one small browser storage entry. It is first-party, + confined to your browser, and never shared with anyone:

@@ -96,22 +96,6 @@ export default function CookiePolicyPage() { every page. Persists until you clear your browser data.

- -
-

- novarix-intro-seen -

-

- - Preference. - {" "} - Stored in sessionStorage only when you accept - cookies. Lets us skip the animated logo intro for the rest - of your browsing session. Cleared automatically when you - close the browser tab. If you decline cookies, this is - never set and the intro may play again on a fresh tab. -

-
diff --git a/app/globals.css b/app/globals.css index cf77b524..65b57a42 100644 --- a/app/globals.css +++ b/app/globals.css @@ -229,79 +229,7 @@ } /* --------------------------------------------------------------------------- - 6. Intro overlay - The first-visit animated SVG wordmark. The SVG itself runs a 3-second - stroke-then-fill animation (defined inside - /public/branding/animated_logo_intro.svg). The overlay begins fading once - the wordmark finishes drawing, while the page underneath eases in. Total - intro length is matched in page.tsx's setTimeout (3950ms). - - When .intro-running is on the
element, the page content - underneath is gently softened so it can reveal itself without a hard cut. - --------------------------------------------------------------------------- */ -.intro-overlay { - position: fixed; - inset: 0; - z-index: 2000; - display: grid; - place-items: center; /* dead-centre the SVG in the viewport */ - animation: intro-fade-out 900ms cubic-bezier(0.22, 1, 0.36, 1) 3.05s forwards; - will-change: opacity; -} - -.intro-backdrop { - position: absolute; - inset: 0; - background: - radial-gradient( - circle at 50% 32%, - oklch(0.62 0.16 240 / 0.08), - transparent 30% - ), - var(--bg); -} - -.intro-svg { - position: relative; /* sits above .intro-backdrop in the stacking order */ - display: block; - width: min(70vw, 900px); - max-height: 70vh; - height: auto; - object-fit: contain; -} - -.site-header, -main > section, -main > footer { - transition: - opacity 900ms cubic-bezier(0.22, 1, 0.36, 1), - transform 900ms cubic-bezier(0.22, 1, 0.36, 1), - filter 900ms cubic-bezier(0.22, 1, 0.36, 1); - will-change: opacity, transform, filter; -} - -.intro-running .site-header, -.intro-running main > section, -.intro-running main > footer { - opacity: 0.16; - transform: translateY(10px) scale(0.99); - filter: blur(8px); -} - -@media (max-width: 720px) { - .intro-svg { - width: min(86vw, 560px); - } -} - -@media (max-width: 480px) { - .intro-svg { - width: 92vw; - } -} - -/* --------------------------------------------------------------------------- - 7. Animations — keyframes used by the orbs and the intro overlay above. + 6. Animations — keyframes used by the ambient orbs above. --------------------------------------------------------------------------- */ @keyframes drift { 0%, @@ -313,39 +241,17 @@ main > footer { } } -@keyframes intro-wordmark { - 0% { - opacity: 0; - transform: translateY(110%) scale(0.98); - } - 100% { - opacity: 1; - transform: translateY(0) scale(1); - } -} - -@keyframes intro-fade-out { - to { - opacity: 0; - visibility: hidden; - pointer-events: none; - } -} - /* --------------------------------------------------------------------------- - 8. Reduced motion + 7. Reduced motion Respects the user's OS-level "reduce motion" preference by disabling - the orb drift, the intro animation, and any transition/animation - durations across the site. + the orb drift and any transition/animation durations across the site. --------------------------------------------------------------------------- */ @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } - .ambient-orb, - .intro-overlay, - .intro-wordmark { + .ambient-orb { animation: none !important; } diff --git a/app/page.tsx b/app/page.tsx index 1d947041..a4c15adf 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -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({ 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 && ( - -
- + ); } diff --git a/components/CookieBanner.tsx b/components/CookieBanner.tsx index 75fb16ad..0204c2e1 100644 --- a/components/CookieBanner.tsx +++ b/components/CookieBanner.tsx @@ -10,11 +10,9 @@ // Behaviour: // - On first load, if no consent decision is stored, the banner appears. // - "ACK" stores `novarix-cookie-consent = "ack"` in localStorage and the -// banner closes. The site then behaves as before (intro animation -// remembers it has played using sessionStorage). +// banner closes. // - "RST" stores `novarix-cookie-consent = "rst"` in localStorage and the -// banner closes. We also clear the intro-seen flag so we don't keep any -// non-consent storage around. +// banner closes. // - The footer "Cookie preferences" link dispatches a window event that // re-opens this banner so users can change their mind. // @@ -26,9 +24,7 @@ import { useEffect, useLayoutEffect, useState } from "react"; import { site } from "@/content"; const CONSENT_KEY = "novarix-cookie-consent"; -const INTRO_SEEN_KEY = "novarix-intro-seen"; const REOPEN_EVENT = "novarix:open-cookie-banner"; -const INTRO_EVENT = "novarix:start-intro"; type Consent = "unknown" | "ack" | "rst"; @@ -61,12 +57,6 @@ export default function CookieBanner() { function decide(choice: "ack" | "rst") { try { window.localStorage.setItem(CONSENT_KEY, choice); - if (choice === "rst") { - // Honour the rejection by clearing any non-consent storage. - window.sessionStorage.removeItem(INTRO_SEEN_KEY); - } else { - window.dispatchEvent(new Event(INTRO_EVENT)); - } } catch { /* storage unavailable — nothing to do */ } diff --git a/content.ts b/content.ts index cd8750b5..1ef96b76 100644 --- a/content.ts +++ b/content.ts @@ -153,9 +153,9 @@ export const site = { // --------------------------------------------------------------------------- cookies: { // Short message shown in the banner. Keep it honest — this site does not - // currently use any tracking cookies, only one preference for the intro. + // currently use any tracking cookies, only one preference for consent. message: - "Heads up — this site stores one tiny browser preference to remember you’ve seen the intro animation. We don’t run analytics, advertising, or third-party tracking.", + "Heads up — this site stores one tiny browser preference to remember your cookie choice. We don’t run analytics, advertising, or third-party tracking.", // The two buttons. Labels are small jokes for network folk; subtitles // and aria-labels make them clear to everyone else.