Skip to content
Merged
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
11 changes: 11 additions & 0 deletions apps/landing/app/coming-soon/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react'
import Link from 'next/link'

export default function ComingSoon() {
return (
<div className="flex items-center justify-center h-screen flex-col gap-4">
<h1 className="text-4xl font-bold font-serif">Coming Soon!</h1>
<Link href="/" className="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 hover:underline transition-all duration-300">Back to Home</Link>
</div>
)
}
14 changes: 14 additions & 0 deletions apps/landing/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react'
import Link from 'next/link'

export default function NotFound() {
return (
<div
className="flex items-center justify-center h-screen flex-col gap-4"
>
<h1 className="text-4xl font-bold font-serif">404 - Page Not Found</h1>
<p className="text-lg font-medium font-serif">The page you are looking for does not exist.</p>
<Link href="/" className="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 hover:underline transition-all duration-300">Back to Home</Link>
</div>
)
}
41 changes: 11 additions & 30 deletions apps/landing/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,20 @@
import React from "react";
import ThemeToggler from "../components/theme/theme-toggler";

import Link from "next/link";
import { Button } from "@repo/ui";
import Image from "next/image";
import { Header } from "../components/landing/header/header";
import { Home } from "../components/landing/home/home";
import { Features } from "../components/landing/features/features";
import { FAQ } from "../components/landing/faq/faq";
import { Footer } from "../components/landing/footer/footer";

export default function App() {
return (
<div className="flex flex-col min-h-screen bg-background">
{/* Header */}
<header className="flex items-center justify-between px-8 py-4 border-b border-border">
<div className="flex items-center gap-2">
<Image src="/logo.png" alt="CoBrain Logo" width={24} height={24} />
<span className="text-h4-bold text-foreground">CoBrain</span>
</div>
<div className="flex items-center gap-4">
<ThemeToggler />
<Link href="/signin">
<Button variant="texted">Sign In</Button>
</Link>
<Link href="/signup">
<Button variant="contained">Sign Up</Button>
</Link>
</div>
</header>

{/* Main Content */}
<main className="flex flex-1 flex-col items-center justify-center gap-4 p-8">
<h1 className="text-h1 text-center text-foreground font-serif">
Welcome to CoBrain
</h1>
<p className="text-body-medium text-center text-muted-foreground">
CoBrain is a platform for mental health and wellness.
</p>
<Header />
<main>
<Home />
<Features />
<FAQ />
</main>
<Footer />
</div>
);
}
6 changes: 4 additions & 2 deletions apps/landing/components/auth/auth-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ export function AuthCard({
<div className="hidden w-2/5 lg:block">
<div className="relative h-full w-full">
<Image
fill
src={imageSrc}
alt="Auth Background"
className="h-full w-full object-cover"
width={1000}
height={1000}
className="h-full w-full object-contain"
priority
/>
{imageOverlay && (
<div className="absolute top-6 left-6 z-10">{imageOverlay}</div>
Expand Down
101 changes: 101 additions & 0 deletions apps/landing/components/landing/faq/accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
"use client";

import { useState } from "react";
import { motion, AnimatePresence } from "motion/react";
import { cn } from "@repo/ui";

interface AccordionItemProps {
question: string;
answer: string;
isOpen: boolean;
onClick: () => void;
}

const AccordionItem = ({
question,
answer,
isOpen,
onClick,
}: AccordionItemProps) => {
return (
<div
className={cn(
"group border border-border/50 rounded-2xl bg-surface/40 overflow-hidden transition-all duration-300",
isOpen
? "border-border-strong bg-surface shadow-sm"
: "hover:border-border-strong hover:bg-surface-hover/60",
)}
>
<button
className="flex w-full items-center justify-between px-8 py-5 text-left transition-colors"
onClick={onClick}
>
<span className="text-lg font-medium text-foreground font-serif group-hover:text-primary transition-colors">
{question}
</span>
<span
className={cn(
"ml-6 flex h-6 w-6 items-center justify-center text-muted-foreground transition-transform duration-300 ease-out",
isOpen && "rotate-180 text-foreground",
)}
>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 9L12 15L18 9"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</span>
</button>
<AnimatePresence initial={false}>
{isOpen && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.3, ease: [0.04, 0.62, 0.23, 0.98] }}
>
<div className="px-8 pb-6 pt-0 text-base leading-relaxed text-muted-foreground max-w-2xl">
{answer}
</div>
</motion.div>
)}
</AnimatePresence>
</div>
);
};

interface AccordionProps {
items: { question: string; answer: string }[];
}

export const Accordion = ({ items }: AccordionProps) => {
const [openIndex, setOpenIndex] = useState<number | null>(null);

const handleItemClick = (index: number) => {
setOpenIndex(openIndex === index ? null : index);
};

return (
<div className="flex flex-col gap-4">
{items.map((item, index) => (
<AccordionItem
key={index}
question={item.question}
answer={item.answer}
isOpen={openIndex === index}
onClick={() => handleItemClick(index)}
/>
))}
</div>
);
};
28 changes: 28 additions & 0 deletions apps/landing/components/landing/faq/faq.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Capsule } from "@repo/ui";
import { Accordion } from "./accordion";
import content from "../../../content/landing.json";

export const FAQ = () => {
return (
<section
id="faq"
className="py-24 px-6 sm:py-32 lg:px-8 bg-background relative overflow-hidden"
>
{/* Subtle background decoration */}
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-full h-full bg-[radial-gradient(circle_at_center,var(--color-primary)_0%,transparent_70%)] opacity-[0.03] pointer-events-none" />

<div className="mx-auto max-w-3xl relative z-10">
<div className="flex flex-col items-center text-center mb-16">
<Capsule text={content.faq.capsule_title} className="mb-6" />
<h2 className="text-h2 italic text-foreground mb-4">
{content.faq.title}
</h2>
<p className="text-lg text-muted-foreground max-w-xl leading-relaxed">
{content.faq.description}
</p>
</div>
<Accordion items={content.faq.items} />
</div>
</section>
);
};
51 changes: 51 additions & 0 deletions apps/landing/components/landing/features/features.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Capsule } from "@repo/ui";
import { cn } from "@repo/ui";
import content from "../../../content/landing.json";

export const Features = () => {
return (
<section id="features" className="py-24 sm:py-32 bg-secondary/10">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto max-w-2xl lg:text-center">
<div className="flex justify-center mb-4">
<Capsule text={content.features.section_title} />
</div>
<h3 className="mt-2 text-h3 italic text-foreground">
{content.features.main_title}
</h3>
<p className="ml-2 mt-3 text-md leading-8 text-muted-foreground">
{content.features.description}
</p>
</div>
<div className="mx-auto mt-16 max-w-2xl sm:mt-20 lg:mt-24 lg:max-w-none">
<div className="grid grid-cols-1 gap-4 lg:grid-cols-3 lg:grid-rows-2">
{content.features.list.map((feature, idx) => (
<div
key={feature.name}
className={cn(
"flex flex-col justify-between border border-border-muted rounded-2xl p-6 hover:border-border-strong transition-colors bg-surface-muted",
idx === 0
? "lg:col-span-2"
: idx === 1
? "lg:col-span-1"
: idx === 2
? "lg:col-span-1"
: "lg:col-span-2",
)}
>
<div>
<dt className="text-xl font-semibold leading-7 text-foreground font-serif">
{feature.name}
</dt>
<dd className="mt-2 text-sm leading-7 text-muted-foreground">
{feature.description}
</dd>
</div>
</div>
))}
</div>
</div>
</div>
</section>
);
};
70 changes: 70 additions & 0 deletions apps/landing/components/landing/footer/footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import Link from "next/link";
import Image from "next/image";
import { Button } from "@repo/ui";
import { logo } from "@assets";
import content from "../../../content/landing.json";

export const Footer = () => {
return (
<footer className="bg-background border-t border-border mt-20">
<div className="mx-auto max-w-7xl px-6 py-12 md:py-16 lg:px-8">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-12 mb-16">
{/* Logo & Info */}
<div className="lg:col-span-2 flex flex-col gap-3">
<div className="flex items-center gap-2">
<Image
src={logo}
alt="CoBrain Logo"
width={32}
height={32}
className="w-8 h-8 rounded-full"
/>
<span className="text-2xl font-serif font-bold text-foreground">
CoBrain
</span>
</div>
<p className="text-muted-foreground text-base max-w-sm leading-relaxed">
{content.footer.description}
</p>
<Link href="/coming-soon" className="inline-block">
<Button variant="contained">Get Started</Button>
</Link>
</div>

{/* Spacer for desktop */}
<div className="hidden lg:block"></div>

{/* Socials Column */}
<div className="flex flex-col gap-2">
<h3 className="text-foreground font-semibold text-md">Socials</h3>
<ul className="flex flex-col gap-1">
{content.footer.socials.map((social) => (
<li key={social.label}>
<a
href={social.href}
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground hover:text-foreground transition-colors text-sm hover:underline"
>
{social.label}
</a>
</li>
))}
</ul>
</div>
</div>

{/* Bottom Bar */}
<div className="pt-8 border-t border-border flex flex-col md:flex-row justify-between items-center gap-4">
<p className="text-muted-foreground text-sm">
{content.footer.all_rights_reserved} &copy;{" "}
{new Date().getFullYear()} CoBrain
</p>
<p className="text-muted-foreground text-sm font-medium">
{content.footer.copyright}
</p>
</div>
</div>
</footer>
);
};
Loading
Loading