import { useEffect, useRef, useState, type FormEvent } from "react"; import { Link, useNavigate } from "react-router-dom"; import SkyCanvas from "../components/SkyCanvas"; import FeatureShowcase from "../components/FeatureShowcase"; import PricingTiers from "../components/PricingTiers"; import "../styles/showcase.css"; import { PublicFooter, PublicNav } from "../components/PublicChrome"; import { blogPosts } from "../content/blogPosts"; import { useAuth } from "../hooks/useAuth"; import { ArrowRightIcon, ConstellationIcon, HelmetIcon, LaunchWindowIcon, LaunchpadIcon, OrbitIcon, RocketIcon, SignalIcon, TelescopeIcon, } from "../components/icons/Icons"; export const PENDING_SOURCE_KEY = "ns_pending_source"; // Bump after re-rendering public/demo.mp4 so browsers and the CDN fetch the new file. const DEMO_VERSION = 5; const FEATURES = [ { icon: TelescopeIcon, label: "Research chat", title: "Ask your archive anything", body: "Every answer cites the exact passage it came from. If you never wrote it, Notestack says so.", }, { icon: ConstellationIcon, label: "Mind Constellation", title: "Fly through your ideas", body: "Open a notebook as a galaxy of topics. Switch the shape, dive in and see the posts behind every star.", }, { icon: SignalIcon, label: "Audio overview", title: "Two hosts, your ideas", body: "Turn a post or a whole notebook into a natural two host conversation, voiced by ElevenLabs.", }, { icon: LaunchWindowIcon, label: "Video", title: "Narrated videos you can edit", body: "Pick a post and a template, then edit every scene with a live preview. Landscape for YouTube, vertical for Reels.", }, { icon: RocketIcon, label: "Launch Kit", title: "One post, every platform", body: "Threads, LinkedIn, Substack Notes, hooks, quote cards and an SEO pack in one click.", }, { icon: HelmetIcon, label: "Voices", title: "Sounds like you", body: "Notestack learns your tone from your posts, and you can describe, pick or clone a voice for narration.", }, { icon: OrbitIcon, label: "Memory", title: "Remembers who you write for", body: "Short notes about your audience and style, learned from your chats and yours to edit anytime.", }, { icon: LaunchpadIcon, label: "Launchpad", title: "Schedule the countdown", body: "Plan every launch on one calendar and resurface evergreen posts when they matter again.", }, ]; const STEPS = [ { n: "01", title: "Point it at your writing", body: "Any blog, newsletter or site URL, or upload markdown, text, HTML and PDF files." }, { n: "02", title: "Watch them come into orbit", body: "Posts are cleaned, indexed and embedded in minutes." }, { n: "03", title: "Launch", body: "Ask, listen, render and publish, all grounded in what you wrote." }, ]; /** The narrated demo. Browsers only autoplay muted, so a button turns the captain's log on. */ function DemoVideo() { const ref = useRef(null); const [muted, setMuted] = useState(true); const toggle = () => { const v = ref.current; if (!v) return; if (muted) { v.currentTime = 0; // start the narration from the top v.muted = false; void v.play(); } else { v.muted = true; } setMuted(!muted); }; return (
Mission log · 00:46
); } /** Sections drift in out of the depth as they reach the viewport, like planets on approach. */ function useArrivals() { useEffect(() => { const els = Array.from(document.querySelectorAll(".arrive")); if (!("IntersectionObserver" in window) || window.matchMedia("(prefers-reduced-motion: reduce)").matches) { els.forEach((el) => el.classList.add("arrived")); return; } const io = new IntersectionObserver( (entries) => { for (const e of entries) { if (e.isIntersecting) { e.target.classList.add("arrived"); io.unobserve(e.target); } } }, { threshold: 0.35, rootMargin: "0px 0px -12% 0px" }, ); els.forEach((el) => io.observe(el)); return () => io.disconnect(); }, []); } export default function Landing() { const [url, setUrl] = useState(""); const navigate = useNavigate(); const { user } = useAuth(); useArrivals(); const onSubmit = (e: FormEvent) => { e.preventDefault(); // Always go to sign in / sign up. A pasted URL rides along and starts connecting after signup // (Welcome picks it up); signed in writers go straight to Mission Control, which does the same. const value = url.trim(); if (value) { try { sessionStorage.setItem(PENDING_SOURCE_KEY, value); } catch { /* ignore */ } } navigate(user ? "/app" : "/auth?mode=signup"); }; return (

The ultimate NotebookLM alternative {" "} for writers, bloggers and newsletters

Your knowledge,
in orbit.

Notestack turns your blog, newsletter, site or markdown files into a research notebook you can question, podcasts you can publish and launch kits for every platform. Every word traced back to what you actually wrote.

setUrl(e.target.value)} autoComplete="url" inputMode="url" />

Works with Substack, Ghost, WordPress, Medium, any RSS feed or site, and markdown files.{" "} Upload files instead

What you get

Everything your writing can become

{FEATURES.map(({ icon: Icon, label, title, body }, i) => (
{label}

{title}

{body}

))}

The tour

Seven stops across your archive

How it works

Three steps to liftoff

    {STEPS.map((s, i) => (
  1. {s.n}

    {s.title}

    {s.body}

  2. ))}

Pricing

Pick your trajectory

From the blog

Field notes

{blogPosts.slice(0, 3).map((p, i) => ( {p.category} · {p.readTime}

{p.title}

{p.description}

))}

Clear skies ahead.

Paste your URL above or start with an empty notebook.

Start free
); }