"use client"; import { IconPointerFilled } from "@tabler/icons-react"; import { Check, X } from "lucide-react"; import { motion, useInView } from "motion/react"; import { useRef, useState } from "react"; import { Badge } from "@/components/ui/badge"; import { Separator } from "@/components/ui/separator"; import { cn } from "@/lib/utils"; const cards = [ { title: "Unlimited & Self-Hosted", description: "No caps on sources, notebooks, or file sizes. Deploy on your own infra and your data never leaves your control.", skeleton: , }, { title: "100+ LLMs, Zero Lock-in", description: "Swap between 100+ LLMs via OpenAI spec and LiteLLM, or run fully private with vLLM, Ollama, and more.", skeleton: , }, { title: "Real-Time Multiplayer", description: "RBAC with Owner, Admin, Editor, and Viewer roles plus real-time chat and comment threads. Built for teams.", skeleton: , }, ]; export function WhySurfSense() { return (

Why SurfSense

Everything NotebookLM should have been

Open source. No data limits. No vendor lock-in. Built for teams that care about privacy and flexibility.

{cards.map((card) => ( ))}
); } function UnlimitedSkeleton({ className }: { className?: string }) { const ref = useRef(null); const isInView = useInView(ref, { once: true, margin: "-50px" }); const items = [ { label: "Sources", notebookLm: "50-600", surfSense: "Unlimited", icon: "📄" }, { label: "Notebooks", notebookLm: "100-500", surfSense: "Unlimited", icon: "📓" }, { label: "File size", notebookLm: "200 MB", surfSense: "No limit", icon: "📦" }, { label: "Self-host", notebookLm: "No", surfSense: "Yes", icon: "🏠" }, ]; return (
{items.map((item, index) => ( {item.icon} {item.label}
{item.notebookLm} {item.surfSense}
))}
); } function LLMFlexibilitySkeleton({ className }: { className?: string }) { const ref = useRef(null); const isInView = useInView(ref, { once: true, margin: "-50px" }); const [selected, setSelected] = useState(0); const models = [ { name: "GPT-4o", provider: "OpenAI", color: "bg-green-500" }, { name: "Claude 4", provider: "Anthropic", color: "bg-orange-500" }, { name: "Gemini 2.5", provider: "Google", color: "bg-blue-500" }, { name: "Llama 4", provider: "Local", color: "bg-purple-500" }, { name: "DeepSeek R1", provider: "DeepSeek", color: "bg-cyan-500" }, ]; return (
{models.map((model, index) => ( setSelected(index)} initial={{ opacity: 0, x: 12 }} animate={isInView ? { opacity: 1, x: 0 } : {}} transition={{ duration: 0.3, delay: 0.1 + index * 0.1 }} className={cn( "flex w-full cursor-pointer items-center gap-2 rounded-lg px-2.5 py-1.5 text-left transition-all", selected === index ? "bg-background shadow-sm ring-1 ring-border" : "hover:bg-accent" )} >

{model.name}

{model.provider}

{selected === index && ( )} ))}
); } function MultiplayerSkeleton({ className }: { className?: string }) { const ref = useRef(null); const isInView = useInView(ref, { once: true, margin: "-50px" }); const collaborators = [ { id: 1, name: "Alice", role: "Editor", color: "#3b82f6", path: [ { x: 15, y: 10 }, { x: 80, y: 40 }, { x: 40, y: 80 }, { x: 15, y: 10 }, ], }, { id: 2, name: "Bob", role: "Viewer", color: "#10b981", path: [ { x: 115, y: 70 }, { x: 55, y: 20 }, { x: 95, y: 50 }, { x: 115, y: 70 }, ], }, ]; const codeLines = [ { indent: 0, width: "60%", color: "bg-chart-4/60" }, { indent: 1, width: "75%", color: "bg-muted-foreground/20" }, { indent: 1, width: "50%", color: "bg-chart-1/60" }, { indent: 2, width: "80%", color: "bg-muted-foreground/20" }, { indent: 2, width: "45%", color: "bg-chart-2/60" }, { indent: 1, width: "30%", color: "bg-muted-foreground/20" }, { indent: 0, width: "20%", color: "bg-chart-4/60" }, ]; return (
{codeLines.map((line, index) => (
))} {collaborators.map((collaborator, index) => ( p.x), y: collaborator.path.map((p) => p.y), } : {} } transition={{ opacity: { duration: 0.3, delay: 0.5 + index * 0.2 }, x: { duration: 6, delay: 0.5 + index * 0.3, repeat: Infinity, ease: "easeInOut", }, y: { duration: 6, delay: 0.5 + index * 0.3, repeat: Infinity, ease: "easeInOut", }, }} >
{collaborator.name[0]}
{collaborator.name} {collaborator.role}
))}
); } function FeatureCard({ title, description, skeleton, }: { title: string; description: string; skeleton: React.ReactNode; }) { return (
{skeleton}

{title}

{description}

); } const comparisonRows: { feature: string; notebookLm: string | boolean; surfSense: string | boolean; }[] = [ { feature: "Sources per Notebook", notebookLm: "50-600", surfSense: "Unlimited", }, { feature: "LLM Support", notebookLm: "Gemini only", surfSense: "100+ LLMs", }, { feature: "Self-Hostable", notebookLm: false, surfSense: true, }, { feature: "Open Source", notebookLm: false, surfSense: true, }, { feature: "External Connectors", notebookLm: "Limited", surfSense: "27+", }, { feature: "Desktop App", notebookLm: false, surfSense: true, }, { feature: "Agentic Architecture", notebookLm: false, surfSense: true, }, { feature: "AI File Sorting", notebookLm: false, surfSense: true, }, ]; function ComparisonStrip() { const ref = useRef(null); const isInView = useInView(ref, { once: true, margin: "-80px" }); return (
Feature NotebookLM SurfSense
{comparisonRows.map((row, index) => (
{row.feature} {typeof row.notebookLm === "boolean" ? ( row.notebookLm ? ( ) : ( ) ) : ( {row.notebookLm} )} {typeof row.surfSense === "boolean" ? ( row.surfSense ? ( ) : ( ) ) : ( {row.surfSense} )}
{index !== comparisonRows.length - 1 && }
))}
); }