diff --git a/apps/web/src/components/faq/FaqSection.tsx b/apps/web/src/components/faq/FaqSection.tsx
index f46b3931..63231f04 100644
--- a/apps/web/src/components/faq/FaqSection.tsx
+++ b/apps/web/src/components/faq/FaqSection.tsx
@@ -38,24 +38,26 @@ export function FaqSection() {
className="w-[30px] lg:w-[50px] absolute right-0 top-0"
/>
+
-
- {faqs.map((faq, index) => (
-
-
- {faq.question}
-
-
- {faq.answer}
-
-
- ))}
-
-
+
+ {faqs.map((faq, index) => (
+
+
+ {faq.question}
+
+
+
+ {faq.answer}
+
+
+ ))}
+
+
);
diff --git a/apps/web/src/components/landing-sections/navbar.tsx b/apps/web/src/components/landing-sections/navbar.tsx
index 580b943d..8f356303 100644
--- a/apps/web/src/components/landing-sections/navbar.tsx
+++ b/apps/web/src/components/landing-sections/navbar.tsx
@@ -1,5 +1,5 @@
"use client";
-import React, { useState } from "react";
+import React, { useState, useEffect } from "react";
import PrimaryButton from "../ui/custom-button";
import { motion, useScroll, useMotionValueEvent } from "framer-motion";
import Image from "next/image";
@@ -15,6 +15,7 @@ const Navbar = () => {
const isPricingPage = pathname === "/pricing";
const [showNavbar, setShowNavbar] = useState(isPricingPage ? true : false);
const [isOpen, setIsOpen] = useState(false);
+ const [activeSection, setActiveSection] = useState("");
const { trackButtonClick, trackLinkClick } = useAnalytics();
const handleGetStartedClick = (location: "navbar" | "mobile_menu") => {
@@ -30,18 +31,19 @@ const Navbar = () => {
);
};
- React.useEffect(() => {
+ // Close mobile menu on Escape
+ useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === "Escape" && isOpen) {
setIsOpen(false);
(document.activeElement as HTMLElement)?.blur();
}
};
-
document.addEventListener("keydown", handleEscape);
return () => document.removeEventListener("keydown", handleEscape);
}, [isOpen]);
+ // Show navbar when scrolling down (except on Pricing page)
useMotionValueEvent(scrollYProgress, "change", (latest) => {
if (!isPricingPage) {
setShowNavbar(latest > 0);
@@ -53,23 +55,67 @@ const Navbar = () => {
{ name: "Features", href: "/#features" },
{ name: "Demo", href: "/#demo" },
{ name: "How it works", href: "/#HIW" },
+ { name: "FAQ", href: "/#faq" },
{ name: "Stats", href: "/#Stats" },
{ name: "Contact", href: "/#Contact" },
- { name: "FAQ", href: "/#faq" },
+
];
+
+// scroll spy to highlight active section
+useEffect(() => {
+ const sectionIds = links
+ .filter((link) => link.href.startsWith("/#"))
+ .map((link) => link.href.replace("/#", ""));
+
+ const handleIntersect = (entries: IntersectionObserverEntry[]) => {
+ // Filter for intersecting sections
+ const visibleEntries = entries.filter(entry => entry.isIntersecting);
+
+ if (visibleEntries.length > 0) {
+ // Find the section with highest visibility
+ let mostVisibleEntry = visibleEntries[0];
+
+ for (const entry of visibleEntries) {
+ if (entry.intersectionRatio > mostVisibleEntry.intersectionRatio) {
+ mostVisibleEntry = entry;
+ }
+ }
+
+ // Update active section if target has an id
+ if (mostVisibleEntry.target.id) {
+ setActiveSection(mostVisibleEntry.target.id);
+ }
+ }
+ };
+
+ const observer = new IntersectionObserver(handleIntersect, {
+ root: null,
+ rootMargin: "0px",
+ threshold: 0.5,
+ });
+
+ sectionIds.forEach((id) => {
+ const el = document.getElementById(id);
+ if (el) observer.observe(el);
+ });
+
+ return () => observer.disconnect();
+}, [links]);
+
return (
+ {/* Left: Logo + Menu */}
+
+ {/* Center: Desktop Links */}
{links.map((link, index) => {
- const isActive = pathname === link.href;
+ const isActive =
+ pathname === link.href ||
+ (link.href.startsWith("/#") &&
+ activeSection === link.href.replace("/#", ""));
+
return (
{link.name}
+
+ {isActive && (
+
+)}
);
})}
+
+ {/* Right: Buttons */}
{
+
+ {/* Mobile Menu */}
{isOpen && (
{
);
};
-export default Navbar;
+export default Navbar;
\ No newline at end of file