Skip to content
Open
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
Binary file added public/JUST-GREY.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/JUST-YELLOW.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/duck.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/fish.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,503 changes: 1,503 additions & 0 deletions public/illusion-lab.html

Large diffs are not rendered by default.

19 changes: 12 additions & 7 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

body {
font-family: var(--font-base), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
overflow-x: hidden;
width: 100%;
}

code,
Expand Down Expand Up @@ -47,9 +49,11 @@ samp {
}

#typing {
font-size: 1.8rem;
font-weight: 500;
/* Removed modern styles like letter-spacing, min-height, etc. */
font-size: 2.5rem;
font-weight: 600;
position: relative;
z-index: 20;
color: white;
}

#email {
Expand All @@ -58,14 +62,15 @@ samp {
font-size: 1.3rem;
font-weight: 100;
opacity: 0;
color: black; /* Legacy assumed black text on white, ensuring visibility */
}

#caption {
font-size: 2.5rem;
font-weight: 500;
opacity: 0;
color: black;
position: relative;
z-index: 20;
color: white;
}

.fadeInAnimation {
Expand Down Expand Up @@ -93,13 +98,13 @@ samp {
}

.cursor {
border-right: 3px solid black;
border-right: 3px solid white;
animation: blink 1s step-end infinite;
}

@keyframes blink {
from, to { border-color: transparent; }
50% { border-color: black; }
50% { border-color: white; }
}

/* Media queries for responsive adjustments - EXACTLY matching legacy */
Expand Down
29 changes: 5 additions & 24 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import type { Metadata, Viewport } from "next";
import Link from "next/link";
import "./globals.css";
import Image from "next/image";
import { IBM_Plex_Mono, Inconsolata } from "next/font/google";
import ScrollToTop from "@/components/ScrollToTop";
import EscherBackground from "@/components/EscherBackground";
import NavBar from "@/components/NavBar";

const plexMono = IBM_Plex_Mono({
subsets: ["latin"],
Expand Down Expand Up @@ -50,27 +48,10 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
<body className={`${plexMono.variable} ${inconsolata.variable} text-[rgb(10,10,10)] bg-white flex flex-col items-center text-center min-h-screen m-0 p-0 overflow-x-hidden`}>
<EscherBackground />
<header className="relative w-full px-4 py-4 sm:px-8 sm:py-6 flex justify-between items-center box-border z-50 bg-transparent">
<div className="logo-container">
<Link href="/" className="flex items-center gap-2 sm:gap-3 no-underline text-inherit font-bold text-lg sm:text-xl transition-opacity duration-200 hover:opacity-80">
<Image src="/logo.png" alt="Nucleus AI Logo" width={40} height={40} className="invert w-8 h-8 sm:w-10 sm:h-10" />
<span className="tracking-tight hidden sm:inline">Nucleus AI</span>
{(process.env.NODE_ENV === "development" || process.env.NEXT_PUBLIC_ENV === "dev") && (
<span className="text-[0.7rem] sm:text-[0.8rem] bg-[#ff4444] text-white px-1.5 py-0.5 rounded ml-2 font-bold align-middle">
DEV
</span>
)}
</Link>
</div>
<nav className="flex gap-4 sm:gap-8">
<Link href="/" className="no-underline text-[#555] font-medium text-sm sm:text-base transition-colors duration-200 hover:text-black">Home</Link>
<Link href="/blog" className="no-underline text-[#555] font-medium text-sm sm:text-base transition-colors duration-200 hover:text-black">Blogs</Link>
</nav>
</header>
<main className="flex-1 flex flex-col w-full">{children}</main>
<div id="email" className="fixed bottom-[30px] text-base text-[#666] font-normal opacity-0 tracking-widest">contact@withnucleus.ai</div>
<body className={`${plexMono.variable} ${inconsolata.variable} text-black bg-white flex flex-col items-center text-center min-h-screen w-full m-0 p-0 overflow-x-hidden`}>
<NavBar />
<main className="flex-1 flex mt-20 flex-col w-full relative z-10">{children}</main>
<div id="email" className="fixed bottom-[30px] text-base text-black/50 font-normal opacity-0 tracking-widest z-10">contact@withnucleus.ai</div>
<ScrollToTop />
</body>
</html>
Expand Down
3 changes: 3 additions & 0 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import ScrollDownButton from "@/components/ScrollDownButton";
import BlogSection from "@/components/BlogSection";
import { getPosts } from "@/lib/posts";

import EscherBirdsBackground from "@/components/EscherBirdsBackground";

export default async function Home() {
const posts = await getPosts();
const recentPosts = posts.slice(0, 3);

return (
<main className="w-full">
<section className="min-h-[calc(100svh-6rem)] relative flex flex-col justify-center items-center w-full overflow-hidden">
<EscherBirdsBackground />
<Typewriter />
<ScrollDownButton />
</section>
Expand Down
51 changes: 51 additions & 0 deletions src/components/EscherBirdsBackground.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"use client";
import { useEffect, useState } from "react";

export default function EscherBirdsBackground() {
const [opacity, setOpacity] = useState(1);

useEffect(() => {
let rafId: number;

const handleScroll = () => {
// Fade out over the first 80% of the viewport
const fadePoint = window.innerHeight * 0.8;
// Use a slightly eased curve for smoother visual disappearance
const scrollY = window.scrollY;
const rawOpacity = 1 - (scrollY / fadePoint);
const newOpacity = Math.max(0, Math.min(1, rawOpacity));

setOpacity(newOpacity);
};

const onScroll = () => {
cancelAnimationFrame(rafId);
rafId = requestAnimationFrame(handleScroll);
};

// Initial check
handleScroll();

window.addEventListener('scroll', onScroll, { passive: true });
return () => {
window.removeEventListener('scroll', onScroll);
cancelAnimationFrame(rafId);
};
}, []);

return (
<div
className="fixed inset-0 w-full h-full z-0 will-change-opacity"
style={{
opacity,
pointerEvents: opacity > 0.05 ? 'auto' : 'none'
}}
>
<iframe
src="/illusion-lab.html"
className="w-full h-full border-none"
title="Tessellation Background"
/>
</div>
);
}
73 changes: 73 additions & 0 deletions src/components/NavBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"use client";
import { useEffect, useState } from "react";
import { usePathname } from "next/navigation";
import Link from "next/link";
import Image from "next/image";

export default function NavBar() {
const pathname = usePathname();
const isHomePage = pathname === "/";
const [isScrolled, setIsScrolled] = useState(false);

useEffect(() => {
if (!isHomePage) return;

const handleScroll = () => {
// Change color when background fades out significantly
// User suggested around 300px, or when fade anim starts.
// The background fades out continuously from 0 to 0.8vh.
// By 300px (on desktop ~1080p), opacity is ~0.65.
// Let's use a threshold where the background is light enough.
// Actually, the background is black? "below bg is whit".
// Ah, the illusions are on a black background? No, `globals.css` body is bg-white.
// But the illusion iframe has a dark setting?
// "Bird A (Yellow) and Bird B (Grey)".
// The user surely sees a dark background initially if they want white text.
// And "below bg is whit" implies the content below the hero is white.
// So we switch to black text when we scroll down.
setIsScrolled(window.scrollY > 300);
};

window.addEventListener("scroll", handleScroll, { passive: true });
return () => window.removeEventListener("scroll", handleScroll);
}, [isHomePage]);

// Determine if we should use dark mode (white text)
// Dark mode is active ONLY on Home Page AND NOT Scrolled.
const useDarkMode = isHomePage && !isScrolled;

// Color classes based on state
const textColor = useDarkMode ? "text-white" : "text-black";
const linkColor = useDarkMode ? "text-white/80" : "text-black/60";
const linkHoverColor = useDarkMode ? "hover:text-white" : "hover:text-black";
const borderColor = useDarkMode ? "border-white/20" : "border-black/5";
const bgColor = useDarkMode ? "bg-white/10" : "bg-white/50"; // More opaque helper when scrolled? Or keep same?

return (
<div className="w-full fixed p-3 z-50">
<header className={`w-full top-0 px-4 py-2 rounded-full shadow-sm sm:px-8 sm:py-3 flex justify-between items-center box-border z-50 pointer-events-none backdrop-blur-md transition-all duration-300 ${bgColor} border ${borderColor}`}>
<div className="logo-container pointer-events-auto">
<Link href="/" className={`flex items-center gap-2 sm:gap-3 no-underline ${textColor} font-bold text-lg sm:text-xl transition-colors duration-300 hover:opacity-80`}>
<Image
src="/logo.png"
alt="Nucleus AI Logo"
width={40}
height={40}
className={`w-8 h-8 sm:w-10 sm:h-10 transition-all duration-300 ${!useDarkMode ? "invert" : ""}`}
/>
<span className="tracking-tight hidden sm:inline">Nucleus AI</span>
{(process.env.NODE_ENV === "development" || process.env.NEXT_PUBLIC_ENV === "dev") && (
<span className="text-[0.7rem] sm:text-[0.8rem] bg-[#ff4444] text-white px-1.5 py-0.5 rounded ml-2 font-bold align-middle">
DEV
</span>
)}
</Link>
</div>
<nav className="flex gap-4 sm:gap-8 pointer-events-auto">
<Link href="/" className={`no-underline ${linkColor} font-medium text-sm sm:text-base transition-colors duration-300 ${linkHoverColor}`}>Home</Link>
<Link href="/blog" className={`no-underline ${linkColor} font-medium text-sm sm:text-base transition-colors duration-300 ${linkHoverColor}`}>Blogs</Link>
</nav>
</header>
</div>
);
}
4 changes: 2 additions & 2 deletions src/components/ScrollDownButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ export default function ScrollDownButton() {
return (
<button
onClick={scrollToBlogs}
className="absolute bottom-8 animate-bounce p-2 rounded-full hover:bg-black/5 dark:hover:bg-white/10 transition-colors"
className="absolute bottom-12 animate-bounce p-4 rounded-full border border-white/20 bg-white/10 backdrop-blur-md shadow-[0_0_15px_rgba(255,255,255,0.1)] hover:bg-white/20 transition-all duration-300"
aria-label="Scroll down to blogs"
>
<ChevronDown className="w-8 h-8 text-gray-600 dark:text-gray-300" />
<ChevronDown className="w-6 h-6 text-white" />
</button>
);
}
4 changes: 2 additions & 2 deletions src/components/Typewriter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ export default function Typewriter() {
}, []);

return (
<>
<div className="max-w-[80%] w-full mx-auto flex flex-col items-center">
<div id="typing" style={fontSize ? { fontSize } : undefined} className={text === "NUCLEUS." ? "final-text" : ""}>
<span id="text" dangerouslySetInnerHTML={{ __html: text }}></span>
<span className="cursor"></span>
Expand All @@ -203,6 +203,6 @@ export default function Typewriter() {
General Intelligence
</span>
</div>
</>
</div>
);
}