diff --git a/apps/web/src/components/sidebar.tsx b/apps/web/src/components/sidebar.tsx
new file mode 100644
index 0000000000..50cc98224e
--- /dev/null
+++ b/apps/web/src/components/sidebar.tsx
@@ -0,0 +1,520 @@
+import { Link, useRouterState } from "@tanstack/react-router";
+import {
+ BookOpen,
+ Building2,
+ ChevronRight,
+ FileText,
+ History,
+ LayoutTemplate,
+ Map,
+ Menu,
+ MessageCircle,
+ Newspaper,
+ X,
+} from "lucide-react";
+import type { LucideIcon } from "lucide-react";
+import { useEffect, useRef, useState } from "react";
+
+import { cn } from "@hypr/utils";
+
+import { SearchTrigger } from "@/components/search";
+import { getPlatformCTA, usePlatform } from "@/hooks/use-platform";
+
+const featuresList = [
+ { to: "/product/ai-notetaking", label: "AI Notetaking" },
+ { to: "/product/search", label: "Searchable Notes" },
+ { to: "/gallery?type=template", label: "Custom Templates" },
+ { to: "/product/markdown", label: "Markdown Files" },
+ { to: "/product/flexible-ai", label: "Flexible AI" },
+ { to: "/opensource", label: "Open Source" },
+];
+
+const solutionsList = [
+ { to: "/solution/knowledge-workers", label: "For Knowledge Workers" },
+ { to: "/enterprise", label: "For Enterprises" },
+ { to: "/product/api", label: "For Developers" },
+];
+
+const resourcesList: {
+ to: string;
+ label: string;
+ icon: LucideIcon;
+ external?: boolean;
+}[] = [
+ { to: "/blog/", label: "Blog", icon: FileText },
+ { to: "/docs/", label: "Documentation", icon: BookOpen },
+ {
+ to: "/gallery?type=template",
+ label: "Meeting Templates",
+ icon: LayoutTemplate,
+ },
+ { to: "/updates/", label: "Updates", icon: Newspaper },
+ { to: "/changelog/", label: "Changelog", icon: History },
+ { to: "/roadmap/", label: "Roadmap", icon: Map },
+ { to: "/company-handbook/", label: "Company Handbook", icon: Building2 },
+ {
+ to: "https://discord.gg/hyprnote",
+ label: "Community",
+ icon: MessageCircle,
+ external: true,
+ },
+];
+
+function CharLogo({ className }: { className?: string }) {
+ return (
+
+ );
+}
+
+const navLinks = [
+ { to: "/why-char/", label: "Why Char" },
+ { to: "/product/ai-notetaking/", label: "Product", hasSubmenu: true },
+ { to: "/docs/", label: "Resources", hasSubmenu: true },
+ { to: "/pricing/", label: "Pricing" },
+] as const;
+
+export function Sidebar() {
+ const [isMobileOpen, setIsMobileOpen] = useState(false);
+ const router = useRouterState();
+ const platform = usePlatform();
+ const platformCTA = getPlatformCTA(platform);
+ const pathname = router.location.pathname;
+
+ useEffect(() => {
+ setIsMobileOpen(false);
+ }, [pathname]);
+
+ useEffect(() => {
+ if (isMobileOpen) {
+ document.body.style.overflow = "hidden";
+ } else {
+ document.body.style.overflow = "";
+ }
+ return () => {
+ document.body.style.overflow = "";
+ };
+ }, [isMobileOpen]);
+
+ return (
+ <>
+
+
+ {isMobileOpen && (
+
setIsMobileOpen(false)}
+ />
+ )}
+
+
+
+ {/* Mobile slide-out sidebar */}
+
+ >
+ );
+}
+
+function MobileTopBar({
+ isMobileOpen,
+ setIsMobileOpen,
+}: {
+ isMobileOpen: boolean;
+ setIsMobileOpen: (open: boolean) => void;
+}) {
+ return (
+
+
+
+
+
+
+ );
+}
+
+function SidebarFlyout({
+ label,
+ to,
+ isActive,
+}: {
+ label: string;
+ to: string;
+ isActive: boolean;
+}) {
+ const [isOpen, setIsOpen] = useState(false);
+ const timeoutRef = useRef
| null>(null);
+
+ const open = () => {
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
+ setIsOpen(true);
+ };
+
+ const close = () => {
+ timeoutRef.current = setTimeout(() => setIsOpen(false), 150);
+ };
+
+ useEffect(() => {
+ return () => {
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
+ };
+ }, []);
+
+ return (
+
+
+ {label}
+
+
+
+ {isOpen && (
+
+
+ {label === "Product" &&
}
+ {label === "Resources" &&
}
+
+
+ )}
+
+ );
+}
+
+function ProductFlyoutContent() {
+ return (
+
+
+
+ Features
+
+
+ {featuresList.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+ Solutions
+
+
+ {solutionsList.map((item) => (
+
+ {item.label}
+
+ ))}
+
+ );
+}
+
+function ResourcesFlyoutContent() {
+ return (
+
+ {resourcesList.map((item) => {
+ const Icon = item.icon;
+ if (item.external) {
+ return (
+
+
+ {item.label}
+
+ );
+ }
+ return (
+
+
+ {item.label}
+
+ );
+ })}
+
+ );
+}
+
+function MobileSubmenu({
+ label,
+ isActive,
+}: {
+ label: string;
+ to: string;
+ isActive: boolean;
+}) {
+ const [isExpanded, setIsExpanded] = useState(false);
+
+ return (
+
+
+
+ {isExpanded && (
+
+ {label === "Product" && (
+ <>
+
+ Features
+
+ {featuresList.map((item) => (
+
+ {item.label}
+
+ ))}
+
+ Solutions
+
+ {solutionsList.map((item) => (
+
+ {item.label}
+
+ ))}
+ >
+ )}
+ {label === "Resources" && (
+ <>
+ {resourcesList.map((item) => {
+ const IconComp = item.icon;
+ if (item.external) {
+ return (
+
+
+ {item.label}
+
+ );
+ }
+ return (
+
+
+ {item.label}
+
+ );
+ })}
+ >
+ )}
+
+ )}
+
+ );
+}
+
+function SidebarCTA({
+ platformCTA,
+}: {
+ platformCTA: ReturnType;
+}) {
+ const baseClass =
+ "flex h-9 items-center justify-center rounded-lg bg-neutral-800 text-sm text-neutral-300 transition-colors hover:bg-neutral-700 hover:text-neutral-100";
+
+ if (platformCTA.action === "download") {
+ return (
+
+ {platformCTA.label}
+
+ );
+ }
+
+ return (
+
+ {platformCTA.label}
+
+ );
+}
diff --git a/apps/web/src/routes/_view/index.tsx b/apps/web/src/routes/_view/index.tsx
index 28e825d3c0..e1f2ed81f9 100644
--- a/apps/web/src/routes/_view/index.tsx
+++ b/apps/web/src/routes/_view/index.tsx
@@ -114,10 +114,7 @@ function Component() {
};
return (
-
+
-
+
-
-
+
+
{heroContent.title}
@@ -274,7 +271,7 @@ function HeroSection({
e.preventDefault();
form.handleSubmit();
}}
- className="w-full max-w-md"
+ className="w-full max-w-md text-left"
>
) : (
-
+
{heroCTA.subtextLink ? (
- {!isChoosePage &&
}
-
-
-
+ {/* Mobile top bar spacer */}
+
+
+ {/* Sidebar + content in a centered container */}
+
+ {!isChoosePage && }
+
+
+
+
+
{!isChoosePage &&
}
+
{isDocsPage && (
{isOpen && (
)}