diff --git a/src/app/page.tsx b/src/app/page.tsx index f534eb08..de220a06 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,58 +1,103 @@ -"use client"; +import dynamic from "next/dynamic"; +import type { Metadata } from "next"; +// Static imports for above-the-fold components import { Navbar } from "@/components/landing/Navbar"; import { Hero } from "@/components/landing/Hero"; -import { FourWays } from "@/components/landing/FourWays"; -import { ThreeSteps } from "@/components/landing/ThreeSteps"; -import { Comparison } from "@/components/landing/Comparison"; -import { Pricing } from "@/components/landing/Pricing"; -import { FinalCTA } from "@/components/landing/FinalCTA"; -import { Footer } from "@/components/landing/Footer"; -import { SEO } from "@/components/seo/SEO"; + +// Dynamic imports for below-the-fold components +const FourWays = dynamic( + () => import("@/components/landing/FourWays").then((mod) => mod.FourWays), + { ssr: true } +); + +const ThreeSteps = dynamic( + () => import("@/components/landing/ThreeSteps").then((mod) => mod.ThreeSteps), + { ssr: true } +); + +const Comparison = dynamic( + () => import("@/components/landing/Comparison").then((mod) => mod.Comparison), + { ssr: true } +); + +const Pricing = dynamic( + () => import("@/components/landing/Pricing").then((mod) => mod.Pricing), + { ssr: true } +); + +const FinalCTA = dynamic( + () => import("@/components/landing/FinalCTA").then((mod) => mod.FinalCTA), + { ssr: true } +); + +const Footer = dynamic( + () => import("@/components/landing/Footer").then((mod) => mod.Footer), + { ssr: true } +); + +// Next.js Metadata API for SEO (replaces client-side SEO component) +export const metadata: Metadata = { + title: "ThinkEx", + description: "Study and work with information effortlessly.", + keywords: "AI workspace, productivity, collaboration, artificial intelligence, workspace tools, ThinkEx, AI assistant, creative workspace", + authors: [{ name: "ThinkEx" }], + openGraph: { + title: "ThinkEx", + description: "Study and work with information effortlessly.", + url: "https://thinkex.app", + siteName: "ThinkEx", + locale: "en_US", + type: "website", + }, + twitter: { + card: "summary_large_image", + title: "ThinkEx", + description: "Study and work with information effortlessly.", + }, + alternates: { + canonical: "https://thinkex.app", + }, + robots: { + index: true, + follow: true, + }, +}; export default function LandingPage() { return ( - <> - +
+ {/* Grid Background (static) */}
- {/* Grid Background (static) */} + {/* Grid Pattern */}
- {/* Grid Pattern */} -
-
- - {/* Content */} -
- - - - - - - -
-
+ className="absolute inset-0" + style={{ + backgroundImage: ` + linear-gradient(to right, rgba(255, 255, 255, 0.08) 1px, transparent 1px), + linear-gradient(to bottom, rgba(255, 255, 255, 0.08) 1px, transparent 1px) + `, + backgroundSize: "50px 50px", + }} + /> +
+ + {/* Content */} +
+ + + + + + + +
- +
); } diff --git a/src/components/landing/BackgroundCard.tsx b/src/components/landing/BackgroundCard.tsx new file mode 100644 index 00000000..79c8993e --- /dev/null +++ b/src/components/landing/BackgroundCard.tsx @@ -0,0 +1,49 @@ +import type { CardColor } from "@/lib/workspace-state/colors"; +import { getCardColorCSS } from "@/lib/workspace-state/colors"; + +export interface BackgroundCardData { + top: string; + left: string; + width: string; + height: string; + color: CardColor; + rotation: number; +} + +interface BackgroundCardProps { + card: BackgroundCardData; + isDesktopOnly?: boolean; +} + +export function BackgroundCard({ card, isDesktopOnly = false }: BackgroundCardProps) { + return ( +
+ {/* Card content placeholder */} +
+
+
+
+
+
+
+
+ ); +} + +// Random card colors for background +export const cardColors: CardColor[] = [ + "#8B5CF6", "#3B82F6", "#10B981", "#F59E0B", "#EF4444", + "#EC4899", "#06B6D4", "#84CC16", "#F97316", "#6366F1", +]; diff --git a/src/components/landing/FinalCTA.tsx b/src/components/landing/FinalCTA.tsx index 5929f2cf..64802edc 100644 --- a/src/components/landing/FinalCTA.tsx +++ b/src/components/landing/FinalCTA.tsx @@ -2,17 +2,10 @@ import Link from "next/link"; import { Button } from "@/components/ui/button"; -import { getCardColorCSS } from "@/lib/workspace-state/colors"; -import type { CardColor } from "@/lib/workspace-state/colors"; - -// Random card colors for background -const cardColors: CardColor[] = [ - "#8B5CF6", "#3B82F6", "#10B981", "#F59E0B", "#EF4444", - "#EC4899", "#06B6D4", "#84CC16", "#F97316", "#6366F1", -]; +import { BackgroundCard, cardColors, type BackgroundCardData } from "./BackgroundCard"; // Random card positions and sizes (static - no parallax) -const backgroundCards = [ +const backgroundCards: BackgroundCardData[] = [ { top: "15%", left: "10%", width: "180px", height: "140px", color: cardColors[0], rotation: -3 }, { top: "30%", left: "75%", width: "200px", height: "160px", color: cardColors[1], rotation: 2 }, { top: "60%", left: "15%", width: "160px", height: "120px", color: cardColors[2], rotation: -2 }, @@ -20,38 +13,6 @@ const backgroundCards = [ { top: "20%", left: "50%", width: "170px", height: "130px", color: cardColors[4], rotation: 1 }, ]; -interface BackgroundCardProps { - card: typeof backgroundCards[0]; - isMobileOnly?: boolean; -} - -function BackgroundCard({ card, isMobileOnly = false }: BackgroundCardProps) { - return ( -
- {/* Card content placeholder */} -
-
-
-
-
-
-
-
- ); -} - export function FinalCTA() { return (
{/* Static Background Cards */} {backgroundCards.map((card, index) => { - const isMobileOnly = index >= 3; + const isDesktopOnly = index >= 3; return ( ); })} @@ -101,14 +62,15 @@ export function FinalCTA() {

- - - + +
diff --git a/src/components/landing/Footer.tsx b/src/components/landing/Footer.tsx index 6d9f8f07..a7b6607b 100644 --- a/src/components/landing/Footer.tsx +++ b/src/components/landing/Footer.tsx @@ -17,8 +17,8 @@ export function Footer() { alt="ThinkEx Logo" width={24} height={24} + loading="lazy" className="object-contain" - priority />
ThinkEx diff --git a/src/components/landing/FourWays.tsx b/src/components/landing/FourWays.tsx index 0a291a13..b9db7edf 100644 --- a/src/components/landing/FourWays.tsx +++ b/src/components/landing/FourWays.tsx @@ -95,14 +95,9 @@ export function FourWays() { className={`space-y-4 md:space-y-6 ${way.imageSide === "left" ? "md:col-start-2" : "" }`} > -
- - {index + 1} - -

- {way.title} -

-
+

+ {way.title} +

    {way.bullets.map((bullet, bulletIndex) => (
  • {bullet}
  • @@ -121,6 +116,8 @@ export function FourWays() { src={way.image} alt={way.title} fill + loading="lazy" + sizes="100vw" className="object-cover" style={ way.image === "/attachments.png" @@ -140,6 +137,8 @@ export function FourWays() { alt={way.title} width={800} height={600} + loading="lazy" + sizes="50vw" className="w-full h-full object-cover" style={ way.image === "/attachments.png" diff --git a/src/components/landing/Hero.tsx b/src/components/landing/Hero.tsx index 1362b815..895f5794 100644 --- a/src/components/landing/Hero.tsx +++ b/src/components/landing/Hero.tsx @@ -3,17 +3,10 @@ import Link from "next/link"; import Image from "next/image"; import { Button } from "@/components/ui/button"; -import { getCardColorCSS } from "@/lib/workspace-state/colors"; -import type { CardColor } from "@/lib/workspace-state/colors"; - -// Random card colors for background -const cardColors: CardColor[] = [ - "#8B5CF6", "#3B82F6", "#10B981", "#F59E0B", "#EF4444", - "#EC4899", "#06B6D4", "#84CC16", "#F97316", "#6366F1", -]; +import { BackgroundCard, cardColors, type BackgroundCardData } from "./BackgroundCard"; // Random card positions and sizes (static - no parallax) -const backgroundCards = [ +const backgroundCards: BackgroundCardData[] = [ { top: "10%", left: "5%", width: "180px", height: "140px", color: cardColors[0], rotation: -3 }, { top: "25%", left: "75%", width: "200px", height: "160px", color: cardColors[1], rotation: 2 }, { top: "50%", left: "5%", width: "160px", height: "120px", color: cardColors[2], rotation: -2 }, @@ -23,38 +16,6 @@ const backgroundCards = [ { top: "30%", left: "20%", width: "160px", height: "120px", color: cardColors[7], rotation: -2 }, ]; -interface BackgroundCardProps { - card: typeof backgroundCards[0]; - isMobileOnly?: boolean; -} - -function BackgroundCard({ card, isMobileOnly = false }: BackgroundCardProps) { - return ( -
    - {/* Card content placeholder */} -
    -
    -
    -
    -
    -
    -
    -
    - ); -} - export function Hero() { return (
    {/* Static Background Cards */} {backgroundCards.map((card, index) => { - const isMobileOnly = index >= 3; + const isDesktopOnly = index >= 3; return ( ); })} @@ -100,6 +61,7 @@ export function Hero() { width={140} height={28} className="h-6 md:h-7 w-auto" + priority /> Mokhtarzada Hatchery 2025 Cohort @@ -128,14 +90,15 @@ export function Hero() { {/* Get Started Button - Above Video */}
    - - - + +
    {/* Mobile Demo Image - Only visible on mobile */} @@ -146,6 +109,8 @@ export function Hero() { src="/demo.png" alt="ThinkEx Demo" fill + priority + sizes="100vw" className="object-cover" />
    @@ -154,12 +119,14 @@ export function Hero() { {/* Desktop Demo Image - Hidden on mobile */}
    -
    +
    ThinkEx Demo
    diff --git a/src/components/landing/Navbar.tsx b/src/components/landing/Navbar.tsx index 295779cc..a280a4be 100644 --- a/src/components/landing/Navbar.tsx +++ b/src/components/landing/Navbar.tsx @@ -34,11 +34,11 @@ export function Navbar() { handleScroll(); // Check on mount return () => window.removeEventListener("scroll", handleScroll); }, []); - + const userName = session?.user?.name || session?.user?.email || "User"; const userEmail = session?.user?.email || ""; const userImage = session?.user?.image || undefined; - + const getInitials = (name: string) => { if (name === "User") return "U"; return name @@ -63,238 +63,241 @@ export function Navbar() { transition={{ duration: 0.5 }} className="sticky top-0 z-50" > -
    - - {/* Logo/Brand */} - -
    - ThinkEx Logo -
    - ThinkEx - +
    + + {/* Logo/Brand */} + +
    + ThinkEx Logo +
    + ThinkEx + - {/* Desktop Navigation - Centered */} - - - {/* Desktop CTA */} -
    - {!isPending && !session && ( - <> + {/* Desktop Navigation - Centered */} +
    + {navLinks.map((link) => ( - - - { + e.preventDefault(); + const element = document.querySelector(link.href); + if (element) { + element.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + }} className="text-sm font-normal text-foreground/70 transition-colors hover:text-foreground cursor-pointer" > - Login - - + {link.label} + + ))} +
    + + {/* Desktop CTA */} +
    + {!isPending && !session && ( + <> + + + + + Login + - - - )} - {!isPending && session && ( - <> - + + )} + {!isPending && session && ( + <> - - - - - - -
    -

    {userName}

    - {userEmail && ( -

    {userEmail}

    - )} -
    - - setShowAccountModal(true)} - className="cursor-pointer" - > - Account Settings - - - signOut()} - className="cursor-pointer" - > - Sign Out - -
    -
    - - )} -
    - - {/* Mobile menu dropdown */} -
    - - - - - - {navLinks.map((link) => ( - { - e.preventDefault(); - const element = document.querySelector(link.href); - if (element) { - element.scrollIntoView({ behavior: 'smooth', block: 'start' }); - } - }} - className="cursor-pointer" - > - {link.label} - - ))} - {!isPending && !session && ( - <> - - - - - - - - + + + + +
    +

    {userName}

    + {userEmail && ( +

    {userEmail}

    + )} +
    + + setShowAccountModal(true)} className="cursor-pointer" > - Login - - - - posthog.capture('navbar-get-started-clicked', { location: 'mobile' })} + Account Settings + + + signOut()} className="cursor-pointer" > - Get Started - - - - )} - {!isPending && session && ( - <> - - - posthog.capture('navbar-dashboard-clicked', { location: 'mobile' })} - className="cursor-pointer" - > - Dashboard - - - -
    -

    {userName}

    - {userEmail && ( -

    {userEmail}

    - )} -
    - - setShowAccountModal(true)} - className="cursor-pointer" - > - Account Settings - - + Sign Out +
    +
    +
    + + )} +
    + + {/* Mobile menu dropdown */} +
    + + + + + + {navLinks.map((link) => ( signOut()} + key={link.href} + onClick={(e) => { + e.preventDefault(); + const element = document.querySelector(link.href); + if (element) { + element.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + }} className="cursor-pointer" > - Sign Out + {link.label} - - )} - - -
    - -
    - - - + ))} + {!isPending && !session && ( + <> + + + + + + + + + Login + + + + posthog.capture('navbar-get-started-clicked', { location: 'mobile' })} + className="cursor-pointer" + > + Get Started + + + + )} + {!isPending && session && ( + <> + + + posthog.capture('navbar-dashboard-clicked', { location: 'mobile' })} + className="cursor-pointer" + > + Dashboard + + + +
    +

    {userName}

    + {userEmail && ( +

    {userEmail}

    + )} +
    + + setShowAccountModal(true)} + className="cursor-pointer" + > + Account Settings + + + signOut()} + className="cursor-pointer" + > + Sign Out + + + )} + + +
    +
    +
    + + + ); } diff --git a/src/components/landing/Pricing.tsx b/src/components/landing/Pricing.tsx index 62c80b70..7aa0a469 100644 --- a/src/components/landing/Pricing.tsx +++ b/src/components/landing/Pricing.tsx @@ -158,18 +158,19 @@ export function Pricing() { ))}
- - - + +
))}
diff --git a/src/components/landing/ThreeSteps.tsx b/src/components/landing/ThreeSteps.tsx index 89ab9a79..a20c729d 100644 --- a/src/components/landing/ThreeSteps.tsx +++ b/src/components/landing/ThreeSteps.tsx @@ -71,6 +71,8 @@ export function ThreeSteps() { src={step.image} alt={step.title} fill + loading="lazy" + sizes="(max-width: 768px) 100vw, 33vw" className="object-cover" style={step.id === "step-2" ? { objectPosition: "left center" } : undefined} />