From e568dbc080fe9e5fddfd33f6c26cfdec96eed93a Mon Sep 17 00:00:00 2001 From: Jana Marie Bantolino Date: Mon, 23 Mar 2026 17:25:37 +0800 Subject: [PATCH 01/17] feat: FFF page! --- app/student/fff/layout.tsx | 25 ++ app/student/fff/page.tsx | 445 +++++++++++++++++++++++ components/features/student/header.tsx | 4 +- components/shared/mobile-nav-wrapper.tsx | 1 + 4 files changed, 473 insertions(+), 2 deletions(-) create mode 100644 app/student/fff/layout.tsx create mode 100644 app/student/fff/page.tsx diff --git a/app/student/fff/layout.tsx b/app/student/fff/layout.tsx new file mode 100644 index 00000000..379d734a --- /dev/null +++ b/app/student/fff/layout.tsx @@ -0,0 +1,25 @@ +import type { Metadata } from "next"; + +const ogImageUrl = "https://fff-hub.com/opengraph.jpg"; + +export const metadata: Metadata = { + title: "BetterInternship x FFF: Startup Accelerator Intern", + description: + "Scout top AI-native builders, network deeply, and help scale the next startup accelerator.", + openGraph: { + images: [ + { + url: ogImageUrl, + width: 1200, + height: 630, + }, + ], + }, + twitter: { + images: [ogImageUrl], + }, +}; + +export default function FFFLayout({ children }: { children: React.ReactNode }) { + return <>{children}; +} diff --git a/app/student/fff/page.tsx b/app/student/fff/page.tsx new file mode 100644 index 00000000..8caff9b2 --- /dev/null +++ b/app/student/fff/page.tsx @@ -0,0 +1,445 @@ +"use client"; + +import { + type ChangeEvent, + type FormEvent, + useMemo, + useRef, + useState, +} from "react"; +import Link from "next/link"; +import Image from "next/image"; +import { ArrowUpRight, Loader2 } from "lucide-react"; +import { JetBrains_Mono, Space_Grotesk } from "next/font/google"; +import { Button } from "@/components/ui/button"; +import { Dialog, DialogContent } from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { cn } from "@/lib/utils"; + +const headingFont = Space_Grotesk({ + subsets: ["latin"], + weight: ["500", "700"], + variable: "--font-fff-heading", +}); + +const monoFont = JetBrains_Mono({ + subsets: ["latin"], + weight: ["400", "600"], + variable: "--font-fff-mono", +}); + +const RESPONSIBILITIES = [ + { + title: "Scout", + body: "Identify the top 1% of AI-native builders before anyone else.", + }, + { + title: "Network", + body: "Help build a founder community you would actually want to join.", + }, + { + title: "Scale", + body: "Solve problems that make the accelerator itself better over time.", + }, +]; + +const CHALLENGE_ITEMS = [ + { + title: "Scout Signal Early", + body: "Find AI-native builders before they become obvious. Prioritize quality of insight over quantity.", + }, + { + title: "Build Community Gravity", + body: "Design and run lightweight initiatives that attract serious founders and keep them engaged.", + }, + { + title: "Scale The Accelerator", + body: "Identify one bottleneck in the accelerator workflow and ship an improvement with measurable impact.", + }, +]; + +const SUBMISSION_REQUIREMENTS = [ + "Your scouting framework or thesis (short write-up or deck).", + "A sample list of high-signal builders you would prioritize.", + "A concrete idea you would implement to make the accelerator better.", +]; + +type FffSubmissionPayload = { + fullName: string; + email: string; + portfolioUrl: string; + linkedinUrl: string; + whyFit: string; +}; + +type FffSubmissionResponse = { + success: boolean; + message?: string; +}; + +const INITIAL_FORM_STATE: FffSubmissionPayload = { + fullName: "", + email: "", + portfolioUrl: "", + linkedinUrl: "", + whyFit: "", +}; + +export default function FFFPage() { + const [form, setForm] = useState(INITIAL_FORM_STATE); + const [isSubmitting, setIsSubmitting] = useState(false); + const [resultMessage, setResultMessage] = useState(""); + const [isError, setIsError] = useState(false); + const [submitModalOpen, setSubmitModalOpen] = useState(false); + + const challengeRef = useRef(null); + + const endpoint = useMemo(() => { + const base = process.env.NEXT_PUBLIC_API_URL?.replace(/\/$/, ""); + if (!base) return "/api/super-listings/fff-submission"; + return `${base}/super-listings/fff-submission`; + }, []); + + const updateField = + (field: keyof FffSubmissionPayload) => + ( + event: ChangeEvent | ChangeEvent, + ) => { + setForm((previous) => ({ ...previous, [field]: event.target.value })); + }; + + const handleSubmit = async (event: FormEvent) => { + event.preventDefault(); + setResultMessage(""); + setIsError(false); + setIsSubmitting(true); + + try { + const response = await fetch(endpoint, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(form), + }); + + const data = (await response.json()) as FffSubmissionResponse; + + if (!response.ok || !data.success) { + throw new Error(data.message || "Could not send your submission."); + } + + setForm(INITIAL_FORM_STATE); + setResultMessage( + "Submission sent. We also emailed a confirmation to your inbox.", + ); + } catch (error) { + setIsError(true); + setResultMessage( + error instanceof Error + ? error.message + : "Something went wrong while sending your submission.", + ); + } finally { + setIsSubmitting(false); + } + }; + + const openSubmitModal = () => { + setResultMessage(""); + setIsError(false); + setSubmitModalOpen(true); + }; + + const scrollToChallenge = () => { + challengeRef.current?.scrollIntoView({ + behavior: "smooth", + block: "start", + }); + }; + + return ( +
+
+
+ +
+
+
+ + BetterInternship + + + x + + + + FFF + + +

+ BetterInternship x FFF +

+
+ + + Founders for Founders + + +
+
+ +
+
+
+

+ Startup Accelerator Intern +

+

+ Scout. +
+ Network. +
+ Scale. +

+

+ s16vc and Ellipsis Ventures are building the successor to YC, + backed by founders and execs from Miro, Pitch, and WeTransfer. + This internship is for builders who move fast, spot signal early, + and can help compound leverage for the whole ecosystem. +

+
+ +
+ {RESPONSIBILITIES.map((item) => ( +
+
+
+

+ {item.title} +

+

+ {item.body} +

+
+
+ ))} +
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+

+ Challenge +

+

+ What You Need To Ship +

+
+ +
+
+ {CHALLENGE_ITEMS.map((item) => ( +
+
+
+

+ {item.title} +

+

+ {item.body} +

+
+
+ ))} +
+ +
+
+
+

+ Submission Requirements +

+
    + {SUBMISSION_REQUIREMENTS.map((item) => ( +
  • - {item}
  • + ))} +
+
+
+ +
+
+
+ +
+
+
+
+
+
+ + { + setSubmitModalOpen(open); + if (!open) { + setResultMessage(""); + setIsError(false); + } + }} + > + +
+
+
+ +
+

+ Candidate Submission +

+

+ Throw Your Hat In +

+

+ If this sounds like you, ship your pitch. +

+
+ +
+
void handleSubmit(e)} + > +
+ + +
+ +
+ + +
+ +