← back to ethanfalik__ethanfa-web

Function bodies 14 total

All specs Real LLM only Function bodies
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={se
ComingSoonPage 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&apos;s watching?
        </h1>

        <div className="flex items-s
ProjectShowcasePage 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: "5
ContentRow 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-co
Modal 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={handleMouseL
NetflixNav 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 "