Function bodies 14 total
AboutPage function · typescript · L15-L308 (294 LOC)app/about/page.tsx
export default function AboutPage() {
const router = useRouter();
return (
<div className="min-h-screen" style={{ background: "#141414" }}>
<NetflixNav />
{/* Hero / Billboard-style header */}
<div
className="relative w-full flex items-end"
style={{
height: "78vh",
minHeight: 460,
background: `
radial-gradient(ellipse at 65% 35%, rgba(0,64,135,0.35) 0%, transparent 60%),
linear-gradient(135deg, rgba(0,48,100,0.3) 0%, #0a0a0a 55%)
`,
}}
>
{/* Abstract background pattern */}
<div className="absolute inset-0 overflow-hidden pointer-events-none">
{/* Decorative concentric rings */}
{[300, 520, 740, 960].map((size, i) => (
<div
key={i}
className="absolute rounded-full border"
style={{
width: size,
height: size,
right: -size * 0.2,
BrowsePage function · typescript · L11-L133 (123 LOC)app/browse/page.tsx
export default function BrowsePage() {
const [selectedProject, setSelectedProject] = useState<Project | null>(null);
const [searchQuery, setSearchQuery] = useState("");
const featuredProject = projects.find((p) => p.featured) ?? projects[0];
// Filter projects based on search query
const filteredProjects = useMemo(() => {
if (!searchQuery.trim()) return null;
const q = searchQuery.toLowerCase();
return projects.filter(
(p) =>
p.title.toLowerCase().includes(q) ||
p.tagline.toLowerCase().includes(q) ||
p.description.toLowerCase().includes(q) ||
p.genre.some((g) => g.toLowerCase().includes(q)) ||
p.tech.some((t) => t.toLowerCase().includes(q)) ||
p.type.toLowerCase().includes(q)
);
}, [searchQuery]);
const isSearching = Boolean(searchQuery.trim());
return (
<div
className="min-h-screen"
style={{ background: "#141414" }}
>
<NetflixNav onSearch={setSearchQuery} searchQuery={seComingSoonPage function · typescript · L43-L251 (209 LOC)app/coming-soon/[project]/page.tsx
export default async function ComingSoonPage({ params }: PageProps) {
const { project: projectId } = await params;
const project = getProjectById(projectId);
if (!project || !comingSoonData[projectId]) {
notFound();
}
const data = comingSoonData[projectId];
return (
<div
className="min-h-screen flex flex-col"
style={{ background: "#141414" }}
>
{/* Nav */}
<nav
className="flex items-center justify-between px-12 py-6"
style={{
background:
"linear-gradient(180deg, rgba(0,0,0,0.6) 0%, transparent 100%)",
}}
>
<Link href="https://ethanfa.com">
<span
style={{
color: "#E50914",
fontFamily: "'Bebas Neue', Impact, sans-serif",
fontSize: 30,
letterSpacing: "-0.01em",
}}
>
ethan
</span>
</Link>
<Link
href="https://ethanfa.com/browse"
RootLayout function · typescript · L12-L34 (23 LOC)app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossOrigin="anonymous"
/>
<link
href="https://fonts.googleapis.com/css2?family=Bebas+Neue&display=swap"
rel="stylesheet"
/>
</head>
<body className="bg-[#141414] text-white antialiased">{children}</body>
</html>
);
}Intro function · typescript · L20-L143 (124 LOC)app/page.tsx
export default function Intro() {
const router = useRouter();
const wrapRef = useRef<HTMLDivElement>(null);
useEffect(() => {
// ── Initial state ────────────────────────────────────────────────────────
LETTERS.forEach(({ id }) => {
// Base starts squished to zero height (grows upward on reveal)
gsap.set(`#base-${id}`, { scaleY: 0, transformOrigin: "50% 100%" });
// Shadow starts fully opaque
gsap.set(`#shadow-${id}`, { opacity: 1 });
});
const tl = gsap.timeline({
onComplete: () => router.push("/profiles"),
});
// ── Per-letter animation (staggered) ────────────────────────────────────
// Mirrors the CodePen: base scales in, shadow fades out
LETTERS.forEach(({ id }, i) => {
const t = i * 0.16;
// Base letter grows upward from baseline
tl.to(
`#base-${id}`,
{ scaleY: 1, duration: 0.24, ease: "power3.out", transformOrigin: "50% 100%" },
t,
);
// Shadow gradient ProfilesPage function · typescript · L45-L105 (61 LOC)app/profiles/page.tsx
export default function ProfilesPage() {
const router = useRouter();
const handleClick = (profile: (typeof profiles)[0]) => {
if (profile.newTab) window.open(profile.href, "_blank");
else router.push(profile.href);
};
return (
<div
className="min-h-screen flex flex-col items-center justify-center"
style={{ background: "#141414" }}
>
{/* Logo */}
<div className="absolute top-8 left-12">
<span
style={{
color: "#E50914",
fontFamily: "'Bebas Neue', Impact, sans-serif",
fontSize: 36,
letterSpacing: "-0.01em",
}}
>
ethan
</span>
</div>
<div className="flex flex-col items-center gap-14 animate-fadeIn">
<h1
className="text-white font-light"
style={{ fontSize: "clamp(26px, 3.5vw, 48px)", letterSpacing: "0.01em" }}
>
Who's watching?
</h1>
<div className="flex items-sProjectShowcasePage function · typescript · L47-L256 (210 LOC)app/project/[id]/page.tsx
export default async function ProjectShowcasePage({ params }: PageProps) {
const { id } = await params;
const project = getProjectById(id);
const showcase = projectShowcaseData[id];
if (!project || !showcase) {
notFound();
}
return (
<div className="min-h-screen" style={{ background: "#141414" }}>
{/* Nav */}
<nav
className="flex items-center justify-between px-12 py-6"
style={{
background: "linear-gradient(180deg, rgba(0,0,0,0.6) 0%, transparent 100%)",
}}
>
<Link href="https://ethanfa.com">
<span
style={{
color: "#E50914",
fontFamily: "'Bebas Neue', Impact, sans-serif",
fontSize: 30,
}}
>
ethan
</span>
</Link>
<Link
href="https://ethanfa.com/browse"
className="text-sm text-[#e5e5e5] hover:text-white transition-colors"
>
← All Projects
<Repobility — the code-quality scanner for AI-generated software · https://repobility.com
Billboard function · typescript · L10-L142 (133 LOC)components/Billboard.tsx
export default function Billboard({ project, onMoreInfo }: BillboardProps) {
const handlePlay = () => {
if (project.status === "finished") {
window.open(
project.liveUrl || `https://${project.subdomain}.ethanfa.com`,
"_blank"
);
} else {
onMoreInfo(project);
}
};
return (
<div
className="relative w-full flex items-center"
style={{ height: "82vh", minHeight: 480 }}
>
{/* Background gradient based on project accent */}
<div
className="absolute inset-0"
style={{
background: `
radial-gradient(ellipse at 70% 40%, ${project.thumbnail.accent}22 0%, transparent 60%),
linear-gradient(135deg, ${project.thumbnail.accent}18 0%, #0a0a0a 55%)
`,
}}
/>
{/* Giant icon in background */}
<div
className="absolute right-0 top-0 bottom-0 flex items-center justify-end overflow-hidden pointer-events-none"
style={{ width: "5ContentRow function · typescript · L16-L121 (106 LOC)components/ContentRow.tsx
export default function ContentRow({ title, projects, onOpenModal }: ContentRowProps) {
const [page, setPage] = useState(0);
const [isTransitioning, setIsTransitioning] = useState(false);
const rowRef = useRef<HTMLDivElement>(null);
const totalPages = Math.ceil(projects.length / CARDS_PER_PAGE);
const visibleProjects = projects.slice(
page * CARDS_PER_PAGE,
(page + 1) * CARDS_PER_PAGE
);
const paginate = (direction: "left" | "right") => {
if (isTransitioning) return;
setIsTransitioning(true);
setPage((prev) => {
if (direction === "right") return (prev + 1) % totalPages;
return (prev - 1 + totalPages) % totalPages;
});
setTimeout(() => setIsTransitioning(false), 350);
};
if (projects.length === 0) return null;
return (
<div className="relative mb-2 group/row" style={{ paddingBottom: "3rem" }}>
{/* Row title */}
<h2
className="text-white font-bold mb-2 cursor-pointer hover:text-[#e5e5e5] transition-coModal function · typescript · L12-L232 (221 LOC)components/Modal.tsx
export default function Modal({ project, onClose }: ModalProps) {
// Lock body scroll
useEffect(() => {
document.body.style.overflow = "hidden";
return () => {
document.body.style.overflow = "";
};
}, []);
// Close on escape
useEffect(() => {
const handler = (e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
};
window.addEventListener("keydown", handler);
return () => window.removeEventListener("keydown", handler);
}, [onClose]);
const handleLaunch = () => {
window.open(
project.status === "finished"
? project.liveUrl || `https://${project.subdomain}.ethanfa.com`
: `https://${project.subdomain}.ethanfa.com`,
"_blank"
);
};
return (
<div
className="modal-backdrop"
onClick={(e) => e.target === e.currentTarget && onClose()}
>
<div
className="relative w-full max-w-3xl mx-4 overflow-y-auto animate-slideUp"
style={{
background: "#181818"ProjectIcon function · typescript · L15-L31 (17 LOC)components/MovieCard.tsx
function ProjectIcon({ icon, size }: { icon: string; size: number }) {
return (
<span
style={{
fontSize: size,
fontFamily: "'Bebas Neue', Impact, sans-serif",
fontWeight: 900,
color: "rgba(255,255,255,0.85)",
letterSpacing: icon.length > 2 ? "0.05em" : "-0.02em",
lineHeight: 1,
userSelect: "none",
}}
>
{icon}
</span>
);
}MovieCard function · typescript · L33-L164 (132 LOC)components/MovieCard.tsx
export default function MovieCard({ project, onOpenModal, cardIndex, totalCards }: MovieCardProps) {
const [expanded, setExpanded] = useState(false);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const handleMouseEnter = () => {
timeoutRef.current = setTimeout(() => setExpanded(true), 420);
};
const handleMouseLeave = () => {
if (timeoutRef.current) clearTimeout(timeoutRef.current);
setExpanded(false);
};
const handleLaunch = (e: React.MouseEvent) => {
e.stopPropagation();
window.open(
project.status === "finished"
? project.liveUrl || `https://${project.subdomain}.ethanfa.com`
: `https://${project.subdomain}.ethanfa.com`,
"_blank"
);
};
const isFirst = cardIndex === 0;
const isLast = cardIndex === totalCards - 1;
return (
<div
className="relative flex-none"
style={{ width: "calc(16.666% - 4px)", minWidth: 180 }}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLNetflixNav function · typescript · L12-L130 (119 LOC)components/NetflixNav.tsx
export default function NetflixNav({ onSearch, searchQuery = "" }: NetflixNavProps) {
const router = useRouter();
const [scrolled, setScrolled] = useState(false);
const [searchOpen, setSearchOpen] = useState(false);
const [inputValue, setInputValue] = useState(searchQuery);
useEffect(() => {
const handleScroll = () => setScrolled(window.scrollY > 20);
window.addEventListener("scroll", handleScroll, { passive: true });
return () => window.removeEventListener("scroll", handleScroll);
}, []);
useEffect(() => {
setInputValue(searchQuery);
}, [searchQuery]);
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value);
onSearch?.(e.target.value);
};
const openSearch = () => {
setSearchOpen(true);
setTimeout(() => document.getElementById("nav-search-input")?.focus(), 50);
};
const closeSearch = () => {
setSearchOpen(false);
setInputValue("");
onSearch?.("");
};
return (
proxy function · typescript · L5-L49 (45 LOC)proxy.ts
export function proxy(request: NextRequest) {
const hostname = request.headers.get("host") || "";
// Extract subdomain: "calc01.ethanfa.com" → "calc01"
// Works in prod (calc01.ethanfa.com) and local dev (calc01.localhost:3000)
const hostParts = hostname.replace(":3000", "").split(".");
const isSubdomain =
(hostParts.length === 3 && hostParts[1] === "ethanfa") ||
(hostParts.length === 2 && hostParts[1] === "localhost");
if (!isSubdomain) return NextResponse.next();
const subdomain = hostParts[0];
switch (subdomain) {
case "calc01":
// Redirect to the live Cloudflare Workers deployment
return NextResponse.redirect(CALC01_URL);
case "rustplusplus":
// Showcase page hosted within this app
return NextResponse.rewrite(
new URL("/project/rustplusplus", request.url)
);
case "emoji":
// Showcase page
return NextResponse.rewrite(
new URL("/project/emoji-reactor", request.url)
);
case "