diff --git a/surfsense_web/app/contact/page.tsx b/surfsense_web/app/contact/page.tsx
new file mode 100644
index 000000000..cb722ff77
--- /dev/null
+++ b/surfsense_web/app/contact/page.tsx
@@ -0,0 +1,12 @@
+import React from 'react'
+import { ContactFormGridWithDetails } from '@/components/contact/contact-form'
+
+const page = () => {
+ return (
+
+
+
+ )
+}
+
+export default page
\ No newline at end of file
diff --git a/surfsense_web/app/dashboard/[search_space_id]/chats/chats-client.tsx b/surfsense_web/app/dashboard/[search_space_id]/chats/chats-client.tsx
index 1222733d0..4177b451a 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/chats/chats-client.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/chats/chats-client.tsx
@@ -1,7 +1,7 @@
"use client";
import { format } from "date-fns";
-import { AnimatePresence, motion, type Variants } from "framer-motion";
+import { AnimatePresence, motion, type Variants } from "motion/react";
import {
Calendar,
CheckCircle,
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx
index fb803790b..2520307b7 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { format } from "date-fns";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { Calendar as CalendarIcon, Edit, Plus, RefreshCw, Trash2 } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useEffect, useState } from "react";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.tsx
index 83736ff71..b697acdc1 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/edit/page.tsx
@@ -1,6 +1,6 @@
"use client";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowLeft, Check, Loader2 } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useEffect } from "react";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/page.tsx
index 4152ff786..bf0362464 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useEffect, useState } from "react";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/airtable-connector/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/airtable-connector/page.tsx
index b787374ef..4455d0189 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/airtable-connector/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/airtable-connector/page.tsx
@@ -1,6 +1,6 @@
"use client";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowLeft, Check, ExternalLink, Loader2 } from "lucide-react";
import Link from "next/link";
import { useParams, useRouter } from "next/navigation";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/confluence-connector/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/confluence-connector/page.tsx
index 2eb767bf4..0da10f547 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/confluence-connector/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/confluence-connector/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/discord-connector/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/discord-connector/page.tsx
index 12ff1c790..8913c069a 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/discord-connector/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/discord-connector/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/github-connector/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/github-connector/page.tsx
index 4fc8e1f41..c0467baa9 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/github-connector/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/github-connector/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowLeft, Check, CircleAlert, Github, Info, ListChecks, Loader2 } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/google-calendar-connector/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/google-calendar-connector/page.tsx
index b04a5eddc..6c4dbbf81 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/google-calendar-connector/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/google-calendar-connector/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowLeft, Check, ExternalLink, Loader2 } from "lucide-react";
import Link from "next/link";
import { useParams, useRouter, useSearchParams } from "next/navigation";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/google-gmail-connector/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/google-gmail-connector/page.tsx
index 43123ec4b..b62f564c2 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/google-gmail-connector/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/google-gmail-connector/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowLeft, Check, ExternalLink, Loader2 } from "lucide-react";
import Link from "next/link";
import { useParams, useRouter, useSearchParams } from "next/navigation";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/jira-connector/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/jira-connector/page.tsx
index c7d4466b8..cfb812cfd 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/jira-connector/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/jira-connector/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/linear-connector/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/linear-connector/page.tsx
index 9e1581405..602585e5c 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/linear-connector/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/linear-connector/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/linkup-api/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/linkup-api/page.tsx
index 582898e0a..b033d2baa 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/linkup-api/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/linkup-api/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/luma-connector/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/luma-connector/page.tsx
index cc5bde60b..67af12347 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/luma-connector/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/luma-connector/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowLeft, Check, Key, Loader2 } from "lucide-react";
import Link from "next/link";
import { useParams, useRouter } from "next/navigation";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/notion-connector/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/notion-connector/page.tsx
index dfb66fe9f..6fc383042 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/notion-connector/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/notion-connector/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx
index d3cee106e..a8bb9cfe8 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx
@@ -6,7 +6,7 @@ import {
IconChevronDown,
IconChevronRight,
} from "@tabler/icons-react";
-import { AnimatePresence, motion, type Variants } from "framer-motion";
+import { AnimatePresence, motion, type Variants } from "motion/react";
import Link from "next/link";
import { useParams } from "next/navigation";
import { useState } from "react";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/serper-api/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/serper-api/page.tsx
index 667126e8e..79afeddeb 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/serper-api/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/serper-api/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/slack-connector/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/slack-connector/page.tsx
index 6e9802003..4f5e607e7 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/slack-connector/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/slack-connector/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/tavily-api/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/tavily-api/page.tsx
index e2321abcb..d312d46a5 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/tavily-api/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/tavily-api/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsFilters.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsFilters.tsx
index dd8791223..7b3d7beaf 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsFilters.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsFilters.tsx
@@ -1,6 +1,6 @@
"use client";
-import { AnimatePresence, motion, type Variants } from "framer-motion";
+import { AnimatePresence, motion, type Variants } from "motion/react";
import { CircleAlert, CircleX, Columns3, Filter, ListFilter, Trash } from "lucide-react";
import React, { useMemo, useRef } from "react";
import {
diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsTableShell.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsTableShell.tsx
index d9f9bb251..1317944b7 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsTableShell.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentsTableShell.tsx
@@ -1,6 +1,6 @@
"use client";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ChevronDown, ChevronUp, FileX } from "lucide-react";
import React from "react";
import { DocumentViewer } from "@/components/document-viewer";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/PaginationControls.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/PaginationControls.tsx
index 2e6f8f314..33d2f017a 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/PaginationControls.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/components/PaginationControls.tsx
@@ -1,6 +1,6 @@
"use client";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ChevronFirst, ChevronLast, ChevronLeft, ChevronRight } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx
index 4a69a7533..dfaee38a8 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx
@@ -1,6 +1,6 @@
"use client";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { useParams } from "next/navigation";
import { useEffect, useId, useMemo, useState } from "react";
import { toast } from "sonner";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/upload/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/upload/page.tsx
index efea74b49..438e10980 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/documents/upload/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/documents/upload/page.tsx
@@ -1,6 +1,6 @@
"use client";
-import { AnimatePresence, motion } from "framer-motion";
+import { AnimatePresence, motion } from "motion/react";
import { CheckCircle2, FileType, Info, Tag, Upload, X } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useCallback, useState } from "react";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/youtube/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/youtube/page.tsx
index 494420585..af1b702b7 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/documents/youtube/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/documents/youtube/page.tsx
@@ -2,7 +2,7 @@
import { IconBrandYoutube } from "@tabler/icons-react";
import { type Tag, TagInput } from "emblor";
-import { motion, type Variants } from "framer-motion";
+import { motion, type Variants } from "motion/react";
import { Loader2 } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
diff --git a/surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx
index c1c8fd132..e5c4351bf 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx
@@ -15,7 +15,7 @@ import {
useReactTable,
type VisibilityState,
} from "@tanstack/react-table";
-import { AnimatePresence, motion, type Variants } from "framer-motion";
+import { AnimatePresence, motion, type Variants } from "motion/react";
import {
Activity,
AlertCircle,
diff --git a/surfsense_web/app/dashboard/api-key/api-key-client.tsx b/surfsense_web/app/dashboard/api-key/api-key-client.tsx
index 97a36d8e6..97b478dc0 100644
--- a/surfsense_web/app/dashboard/api-key/api-key-client.tsx
+++ b/surfsense_web/app/dashboard/api-key/api-key-client.tsx
@@ -1,7 +1,7 @@
"use client";
import { IconCheck, IconCopy, IconKey } from "@tabler/icons-react";
-import { AnimatePresence, motion } from "framer-motion";
+import { AnimatePresence, motion } from "motion/react";
import { ArrowLeft } from "lucide-react";
import { useRouter } from "next/navigation";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
diff --git a/surfsense_web/app/dashboard/page.tsx b/surfsense_web/app/dashboard/page.tsx
index 484b95929..e0ac6b6cf 100644
--- a/surfsense_web/app/dashboard/page.tsx
+++ b/surfsense_web/app/dashboard/page.tsx
@@ -1,6 +1,6 @@
"use client";
-import { motion, type Variants } from "framer-motion";
+import { motion, type Variants } from "motion/react";
import { AlertCircle, Loader2, Plus, Search, Trash2 } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
diff --git a/surfsense_web/app/dashboard/searchspaces/page.tsx b/surfsense_web/app/dashboard/searchspaces/page.tsx
index b1f2f390e..598536c1b 100644
--- a/surfsense_web/app/dashboard/searchspaces/page.tsx
+++ b/surfsense_web/app/dashboard/searchspaces/page.tsx
@@ -1,6 +1,6 @@
"use client";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { useRouter } from "next/navigation";
import { toast } from "sonner";
import { SearchSpaceForm } from "@/components/search-space-form";
diff --git a/surfsense_web/app/login/GoogleLoginButton.tsx b/surfsense_web/app/login/GoogleLoginButton.tsx
index ab0e0ddf8..545f279e7 100644
--- a/surfsense_web/app/login/GoogleLoginButton.tsx
+++ b/surfsense_web/app/login/GoogleLoginButton.tsx
@@ -1,6 +1,6 @@
"use client";
import { IconBrandGoogleFilled } from "@tabler/icons-react";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { Logo } from "@/components/Logo";
import { AmbientBackground } from "./AmbientBackground";
diff --git a/surfsense_web/app/login/LocalLoginForm.tsx b/surfsense_web/app/login/LocalLoginForm.tsx
index b464bea98..a81b9d793 100644
--- a/surfsense_web/app/login/LocalLoginForm.tsx
+++ b/surfsense_web/app/login/LocalLoginForm.tsx
@@ -1,5 +1,5 @@
"use client";
-import { AnimatePresence, motion } from "framer-motion";
+import { AnimatePresence, motion } from "motion/react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
diff --git a/surfsense_web/app/login/page.tsx b/surfsense_web/app/login/page.tsx
index c9e9d9090..59ff16ee3 100644
--- a/surfsense_web/app/login/page.tsx
+++ b/surfsense_web/app/login/page.tsx
@@ -1,6 +1,6 @@
"use client";
-import { AnimatePresence, motion } from "framer-motion";
+import { AnimatePresence, motion } from "motion/react";
import { Loader2 } from "lucide-react";
import { useSearchParams } from "next/navigation";
import { Suspense, useEffect, useState } from "react";
diff --git a/surfsense_web/app/onboard/page.tsx b/surfsense_web/app/onboard/page.tsx
index 997b7e4c8..983b240c1 100644
--- a/surfsense_web/app/onboard/page.tsx
+++ b/surfsense_web/app/onboard/page.tsx
@@ -1,6 +1,6 @@
"use client";
-import { AnimatePresence, motion } from "framer-motion";
+import { AnimatePresence, motion } from "motion/react";
import { ArrowLeft, ArrowRight, Bot, CheckCircle, Sparkles } from "lucide-react";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
diff --git a/surfsense_web/app/pricing/page.tsx b/surfsense_web/app/pricing/page.tsx
new file mode 100644
index 000000000..6d71d6ff4
--- /dev/null
+++ b/surfsense_web/app/pricing/page.tsx
@@ -0,0 +1,12 @@
+import React from 'react'
+import PricingBasic from '@/components/pricing/pricing-section'
+
+const page = () => {
+ return (
+
+ )
+}
+
+export default page
\ No newline at end of file
diff --git a/surfsense_web/app/register/page.tsx b/surfsense_web/app/register/page.tsx
index f046e5cff..303d9a378 100644
--- a/surfsense_web/app/register/page.tsx
+++ b/surfsense_web/app/register/page.tsx
@@ -1,6 +1,6 @@
"use client";
-import { AnimatePresence, motion } from "framer-motion";
+import { AnimatePresence, motion } from "motion/react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
diff --git a/surfsense_web/components/Navbar.tsx b/surfsense_web/components/Navbar.tsx
index 8a38ca863..d1df0c927 100644
--- a/surfsense_web/components/Navbar.tsx
+++ b/surfsense_web/components/Navbar.tsx
@@ -1,6 +1,6 @@
"use client";
import { IconMenu2, IconUser, IconX } from "@tabler/icons-react";
-import { AnimatePresence, motion, useMotionValueEvent, useScroll } from "framer-motion";
+import { AnimatePresence, motion, useMotionValueEvent, useScroll } from "motion/react";
import Link from "next/link";
import { useRef, useState } from "react";
import { cn } from "@/lib/utils";
diff --git a/surfsense_web/components/chat/AnimatedEmptyState.tsx b/surfsense_web/components/chat/AnimatedEmptyState.tsx
index 992b75395..1ab8d5c84 100644
--- a/surfsense_web/components/chat/AnimatedEmptyState.tsx
+++ b/surfsense_web/components/chat/AnimatedEmptyState.tsx
@@ -1,6 +1,6 @@
"use client";
-import { useInView } from "framer-motion";
+import { useInView } from "motion/react";
import { Manrope } from "next/font/google";
import { useEffect, useMemo, useReducer, useRef } from "react";
import { RoughNotation, RoughNotationGroup } from "react-rough-notation";
diff --git a/surfsense_web/components/contact/contact-form.tsx b/surfsense_web/components/contact/contact-form.tsx
new file mode 100644
index 000000000..5af9bb783
--- /dev/null
+++ b/surfsense_web/components/contact/contact-form.tsx
@@ -0,0 +1,282 @@
+"use client";
+import React from "react";
+import { IconMailFilled } from "@tabler/icons-react";
+import { useId } from "react";
+import { cn } from "@/lib/utils";
+import Image from "next/image";
+import { motion } from "motion/react";
+import Link from "next/link";
+
+export function ContactFormGridWithDetails() {
+ return (
+
+
+
+
+
+
+
+
+ Contact us
+
+
+ We are always looking for ways to improve our products and services.
+ Contact us and let us know how we can help you.
+
+
+
+
+ rohan@surfsense.com
+
+
+ {/*
+
+ +1 (800) 123 XX21
+
*/}
+
+
+
+ https://cal.com/mod-surfsense
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const Pin = ({ className }: { className?: string }) => {
+ return (
+
+
+
+ We are here
+
+
+
+
+ <>
+
+
+
+ >
+
+
+ <>
+
+
+
+
+ >
+
+
+ );
+};
+
+export const FeatureIconContainer = ({
+ children,
+ className,
+}: {
+ children: React.ReactNode;
+ className?: string;
+}) => {
+ return (
+
+ );
+};
+
+export const Grid = ({
+ pattern,
+ size,
+}: {
+ pattern?: number[][];
+ size?: number;
+}) => {
+ const p = pattern ?? [
+ [9, 3],
+ [8, 5],
+ [10, 2],
+ [7, 4],
+ [9, 6],
+ ];
+ return (
+
+ );
+};
+
+export function GridPattern({ width, height, x, y, squares, ...props }: any) {
+ const patternId = useId();
+
+ return (
+
+ );
+}
diff --git a/surfsense_web/components/onboard/add-provider-step.tsx b/surfsense_web/components/onboard/add-provider-step.tsx
index af8d486e8..fc45df875 100644
--- a/surfsense_web/components/onboard/add-provider-step.tsx
+++ b/surfsense_web/components/onboard/add-provider-step.tsx
@@ -1,6 +1,6 @@
"use client";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { AlertCircle, Bot, Plus, Trash2 } from "lucide-react";
import { useState } from "react";
import { toast } from "sonner";
diff --git a/surfsense_web/components/onboard/assign-roles-step.tsx b/surfsense_web/components/onboard/assign-roles-step.tsx
index 2a7b25288..1e4aa2ae2 100644
--- a/surfsense_web/components/onboard/assign-roles-step.tsx
+++ b/surfsense_web/components/onboard/assign-roles-step.tsx
@@ -1,6 +1,6 @@
"use client";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { AlertCircle, Bot, Brain, CheckCircle, Zap } from "lucide-react";
import { useEffect, useState } from "react";
import { Alert, AlertDescription } from "@/components/ui/alert";
diff --git a/surfsense_web/components/onboard/completion-step.tsx b/surfsense_web/components/onboard/completion-step.tsx
index d23da492d..46787030b 100644
--- a/surfsense_web/components/onboard/completion-step.tsx
+++ b/surfsense_web/components/onboard/completion-step.tsx
@@ -1,6 +1,6 @@
"use client";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { ArrowRight, Bot, Brain, CheckCircle, Sparkles, Zap } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
diff --git a/surfsense_web/components/pricing.tsx b/surfsense_web/components/pricing.tsx
new file mode 100644
index 000000000..becb13c96
--- /dev/null
+++ b/surfsense_web/components/pricing.tsx
@@ -0,0 +1,212 @@
+"use client";
+
+import { buttonVariants } from "@/components/ui/button";
+import { Label } from "@/components/ui/label";
+import { Switch } from "@/components/ui/switch";
+import { useMediaQuery } from "@/hooks/use-media-query";
+import { cn } from "@/lib/utils";
+import { motion } from "framer-motion";
+import { Check, Star } from "lucide-react";
+import Link from "next/link";
+import { useState, useRef } from "react";
+import NumberFlow from "@number-flow/react";
+import confetti from "canvas-confetti";
+
+interface PricingPlan {
+ name: string;
+ price: string;
+ yearlyPrice: string;
+ period: string;
+ features: string[];
+ description: string;
+ buttonText: string;
+ href: string;
+ isPopular: boolean;
+}
+
+interface PricingProps {
+ plans: PricingPlan[];
+ title?: string;
+ description?: string;
+}
+
+export function Pricing({
+ plans,
+ title = "Simple, Transparent Pricing",
+ description = "Choose the plan that works for you\nAll plans include access to our platform, lead generation tools, and dedicated support.",
+}: PricingProps) {
+ const [isMonthly, setIsMonthly] = useState(true);
+ const isDesktop = useMediaQuery("(min-width: 768px)");
+ const switchRef = useRef(null);
+
+ const handleToggle = (checked: boolean) => {
+ setIsMonthly(!checked);
+ if (checked && switchRef.current) {
+ const rect = switchRef.current.getBoundingClientRect();
+ const x = rect.left + rect.width / 2;
+ const y = rect.top + rect.height / 2;
+
+ confetti({
+ particleCount: 50,
+ spread: 60,
+ origin: {
+ x: x / window.innerWidth,
+ y: y / window.innerHeight,
+ },
+ colors: [
+ "hsl(var(--primary))",
+ "hsl(var(--accent))",
+ "hsl(var(--secondary))",
+ "hsl(var(--muted))",
+ ],
+ ticks: 200,
+ gravity: 1.2,
+ decay: 0.94,
+ startVelocity: 30,
+ shapes: ["circle"],
+ });
+ }
+ };
+
+ return (
+
+
+
+ {title}
+
+
+ {description}
+
+
+
+
+
+
+ Annual billing (Save 20%)
+
+
+
+
+ {plans.map((plan, index) => (
+
+ {plan.isPopular && (
+
+
+
+ Popular
+
+
+ )}
+
+
+ {plan.name}
+
+
+
+
+
+ {plan.period !== "Next 3 months" && (
+
+ / {plan.period}
+
+ )}
+
+
+
+ {isMonthly ? "billed monthly" : "billed annually"}
+
+
+
+ {plan.features.map((feature, idx) => (
+ -
+
+ {feature}
+
+ ))}
+
+
+
+
+
+ {plan.buttonText}
+
+
+ {plan.description}
+
+
+
+ ))}
+
+
+ );
+}
diff --git a/surfsense_web/components/pricing/pricing-section.tsx b/surfsense_web/components/pricing/pricing-section.tsx
new file mode 100644
index 000000000..85c12bcd0
--- /dev/null
+++ b/surfsense_web/components/pricing/pricing-section.tsx
@@ -0,0 +1,74 @@
+"use client";
+
+import { Pricing } from "@/components/pricing";
+
+const demoPlans = [
+ {
+ name: "STARTER",
+ price: "50",
+ yearlyPrice: "40",
+ period: "per month",
+ features: [
+ "Up to 10 projects",
+ "Basic analytics",
+ "48-hour support response time",
+ "Limited API access",
+ "Community support",
+ ],
+ description: "Perfect for individuals and small projects",
+ buttonText: "Start Free Trial",
+ href: "/sign-up",
+ isPopular: false,
+ },
+ {
+ name: "PROFESSIONAL",
+ price: "99",
+ yearlyPrice: "79",
+ period: "per month",
+ features: [
+ "Unlimited projects",
+ "Advanced analytics",
+ "24-hour support response time",
+ "Full API access",
+ "Priority support",
+ "Team collaboration",
+ "Custom integrations",
+ ],
+ description: "Ideal for growing teams and businesses",
+ buttonText: "Get Started",
+ href: "/sign-up",
+ isPopular: true,
+ },
+ {
+ name: "ENTERPRISE",
+ price: "299",
+ yearlyPrice: "239",
+ period: "per month",
+ features: [
+ "Everything in Professional",
+ "Custom solutions",
+ "Dedicated account manager",
+ "1-hour support response time",
+ "SSO Authentication",
+ "Advanced security",
+ "Custom contracts",
+ "SLA agreement",
+ ],
+ description: "For large organizations with specific needs",
+ buttonText: "Contact Sales",
+ href: "/contact",
+ isPopular: false,
+ },
+];
+
+function PricingBasic() {
+ return (
+
+ );
+}
+
+export default PricingBasic;
diff --git a/surfsense_web/components/search-space-form.tsx b/surfsense_web/components/search-space-form.tsx
index 4516c912a..d87461f5b 100644
--- a/surfsense_web/components/search-space-form.tsx
+++ b/surfsense_web/components/search-space-form.tsx
@@ -1,7 +1,7 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
-import { motion, type Variants } from "framer-motion";
+import { motion, type Variants } from "motion/react";
import { MoveLeftIcon, Plus, Search, Trash2 } from "lucide-react";
import { useRouter } from "next/navigation";
import { useState } from "react";
diff --git a/surfsense_web/components/settings/llm-role-manager.tsx b/surfsense_web/components/settings/llm-role-manager.tsx
index d1e7ae98d..3b23c6fc5 100644
--- a/surfsense_web/components/settings/llm-role-manager.tsx
+++ b/surfsense_web/components/settings/llm-role-manager.tsx
@@ -1,6 +1,6 @@
"use client";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import {
AlertCircle,
Bot,
diff --git a/surfsense_web/components/settings/model-config-manager.tsx b/surfsense_web/components/settings/model-config-manager.tsx
index bc59532d1..c6ad7be1f 100644
--- a/surfsense_web/components/settings/model-config-manager.tsx
+++ b/surfsense_web/components/settings/model-config-manager.tsx
@@ -1,6 +1,6 @@
"use client";
-import { AnimatePresence, motion } from "framer-motion";
+import { AnimatePresence, motion } from "motion/react";
import {
AlertCircle,
Bot,
diff --git a/surfsense_web/components/theme/theme-toggle.tsx b/surfsense_web/components/theme/theme-toggle.tsx
index 587dd501a..a2c807907 100644
--- a/surfsense_web/components/theme/theme-toggle.tsx
+++ b/surfsense_web/components/theme/theme-toggle.tsx
@@ -1,6 +1,6 @@
"use client";
-import { motion } from "framer-motion";
+import { motion } from "motion/react";
import { MoonIcon, SunIcon } from "lucide-react";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
diff --git a/surfsense_web/components/ui/spotlight.tsx b/surfsense_web/components/ui/spotlight.tsx
index 67bb5c7c4..2258e8814 100644
--- a/surfsense_web/components/ui/spotlight.tsx
+++ b/surfsense_web/components/ui/spotlight.tsx
@@ -1,5 +1,5 @@
"use client";
-import { motion, type SpringOptions, useSpring, useTransform } from "framer-motion";
+import { motion, type SpringOptions, useSpring, useTransform } from "motion/react";
import { useCallback, useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
diff --git a/surfsense_web/components/ui/switch.tsx b/surfsense_web/components/ui/switch.tsx
new file mode 100644
index 000000000..8c464e46b
--- /dev/null
+++ b/surfsense_web/components/ui/switch.tsx
@@ -0,0 +1,29 @@
+"use client"
+
+import * as React from "react"
+import * as SwitchPrimitives from "@radix-ui/react-switch"
+
+import { cn } from "@/lib/utils"
+
+const Switch = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+Switch.displayName = SwitchPrimitives.Root.displayName
+
+export { Switch }
diff --git a/surfsense_web/components/ui/tilt.tsx b/surfsense_web/components/ui/tilt.tsx
index db0868cd5..985cff94b 100644
--- a/surfsense_web/components/ui/tilt.tsx
+++ b/surfsense_web/components/ui/tilt.tsx
@@ -8,7 +8,7 @@ import {
useMotionValue,
useSpring,
useTransform,
-} from "framer-motion";
+} from "motion/react";
import type React from "react";
import { useRef } from "react";
diff --git a/surfsense_web/hooks/use-media-query.ts b/surfsense_web/hooks/use-media-query.ts
new file mode 100644
index 000000000..af1cbe574
--- /dev/null
+++ b/surfsense_web/hooks/use-media-query.ts
@@ -0,0 +1,39 @@
+import { useEffect, useState } from "react";
+
+/**
+ * Custom hook that tracks if a media query matches
+ * @param query - The media query string to match (e.g., "(min-width: 768px)")
+ * @returns boolean - True if the media query matches, false otherwise
+ */
+export function useMediaQuery(query: string): boolean {
+ const [matches, setMatches] = useState(false);
+
+ useEffect(() => {
+ // Check if we're in the browser (handle SSR)
+ if (typeof window === "undefined") {
+ return;
+ }
+
+ const mediaQuery = window.matchMedia(query);
+
+ // Set initial value
+ setMatches(mediaQuery.matches);
+
+ // Create event listener
+ const handler = (event: MediaQueryListEvent) => {
+ setMatches(event.matches);
+ };
+
+ // Add event listener
+ mediaQuery.addEventListener("change", handler);
+
+ // Cleanup
+ return () => {
+ mediaQuery.removeEventListener("change", handler);
+ };
+ }, [query]);
+
+ return matches;
+}
+
+
diff --git a/surfsense_web/package.json b/surfsense_web/package.json
index 0ae413b34..36b63fad3 100644
--- a/surfsense_web/package.json
+++ b/surfsense_web/package.json
@@ -18,6 +18,7 @@
"@ai-sdk/react": "^1.2.12",
"@hookform/resolvers": "^4.1.3",
"@llamaindex/chat-ui": "^0.5.17",
+ "@number-flow/react": "^0.5.10",
"@radix-ui/react-accordion": "^1.2.11",
"@radix-ui/react-alert-dialog": "^1.1.14",
"@radix-ui/react-avatar": "^1.1.10",
@@ -32,6 +33,7 @@
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slider": "^1.3.5",
"@radix-ui/react-slot": "^1.2.3",
+ "@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.12",
"@radix-ui/react-toggle": "^1.1.9",
"@radix-ui/react-toggle-group": "^1.1.10",
@@ -41,16 +43,18 @@
"@types/mdx": "^2.0.13",
"@types/react-syntax-highlighter": "^15.5.13",
"ai": "^4.3.19",
+ "canvas-confetti": "^1.9.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"emblor": "^1.4.8",
- "framer-motion": "^12.23.9",
+ "framer-motion": "^12.23.22",
"fumadocs-core": "^15.6.6",
"fumadocs-mdx": "^11.7.1",
"fumadocs-ui": "^15.6.6",
"geist": "^1.4.2",
"lucide-react": "^0.477.0",
+ "motion": "^12.23.22",
"next": "^15.4.4",
"next-themes": "^0.4.6",
"react": "^19.1.0",
@@ -76,6 +80,7 @@
"@eslint/eslintrc": "^3.3.1",
"@tailwindcss/postcss": "^4.1.11",
"@tailwindcss/typography": "^0.5.16",
+ "@types/canvas-confetti": "^1.9.0",
"@types/node": "^20.19.9",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
diff --git a/surfsense_web/pnpm-lock.yaml b/surfsense_web/pnpm-lock.yaml
index 11f90611c..7d387d6fc 100644
--- a/surfsense_web/pnpm-lock.yaml
+++ b/surfsense_web/pnpm-lock.yaml
@@ -17,6 +17,9 @@ importers:
'@llamaindex/chat-ui':
specifier: ^0.5.17
version: 0.5.17(@babel/runtime@7.26.9)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.2)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.38.1)(@lezer/highlight@1.2.1)(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(codemirror@6.0.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(yjs@13.6.27)
+ '@number-flow/react':
+ specifier: ^0.5.10
+ version: 0.5.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-accordion':
specifier: ^1.2.11
version: 1.2.11(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -59,6 +62,9 @@ importers:
'@radix-ui/react-slot':
specifier: ^1.2.3
version: 1.2.3(@types/react@19.1.8)(react@19.1.0)
+ '@radix-ui/react-switch':
+ specifier: ^1.2.6
+ version: 1.2.6(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@radix-ui/react-tabs':
specifier: ^1.1.12
version: 1.1.12(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -86,6 +92,9 @@ importers:
ai:
specifier: ^4.3.19
version: 4.3.19(react@19.1.0)(zod@3.25.76)
+ canvas-confetti:
+ specifier: ^1.9.3
+ version: 1.9.3
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
@@ -99,8 +108,8 @@ importers:
specifier: ^1.4.8
version: 1.4.8(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
framer-motion:
- specifier: ^12.23.9
- version: 12.23.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ specifier: ^12.23.22
+ version: 12.23.22(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
fumadocs-core:
specifier: ^15.6.6
version: 15.6.6(@types/react@19.1.8)(next@15.4.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -116,6 +125,9 @@ importers:
lucide-react:
specifier: ^0.477.0
version: 0.477.0(react@19.1.0)
+ motion:
+ specifier: ^12.23.22
+ version: 12.23.22(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
next:
specifier: ^15.4.4
version: 15.4.4(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -186,6 +198,9 @@ importers:
'@tailwindcss/typography':
specifier: ^0.5.16
version: 0.5.16(tailwindcss@4.1.11)
+ '@types/canvas-confetti':
+ specifier: ^1.9.0
+ version: 1.9.0
'@types/node':
specifier: ^20.19.9
version: 20.19.9
@@ -1047,6 +1062,12 @@ packages:
resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==}
engines: {node: '>=12.4.0'}
+ '@number-flow/react@0.5.10':
+ resolution: {integrity: sha512-a8Wh5eNITn7Km4xbddAH7QH8eNmnduR6k34ER1hkHSGO4H2yU1DDnuAWLQM99vciGInFODemSc0tdxrXkJEpbA==}
+ peerDependencies:
+ react: ^18 || ^19
+ react-dom: ^18 || ^19
+
'@open-draft/deferred-promise@2.2.0':
resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==}
@@ -1073,6 +1094,9 @@ packages:
'@radix-ui/primitive@1.1.2':
resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==}
+ '@radix-ui/primitive@1.1.3':
+ resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
+
'@radix-ui/react-accordion@1.2.11':
resolution: {integrity: sha512-l3W5D54emV2ues7jjeG1xcyN7S3jnK3zE2zHqgn0CmMsy9lNJwmgcrmaxS+7ipw15FAivzKNzH3d5EcGoFKw0A==}
peerDependencies:
@@ -1702,6 +1726,19 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-switch@1.2.6':
+ resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-tabs@1.1.12':
resolution: {integrity: sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==}
peerDependencies:
@@ -2087,6 +2124,9 @@ packages:
resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==}
engines: {node: '>=12'}
+ '@types/canvas-confetti@1.9.0':
+ resolution: {integrity: sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg==}
+
'@types/debug@4.1.12':
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
@@ -2398,6 +2438,9 @@ packages:
caniuse-lite@1.0.30001706:
resolution: {integrity: sha512-3ZczoTApMAZwPKYWmwVbQMFpXBDds3/0VciVoUwPUbldlYyVLmRVuRs/PcUZtHpbLRpzzDvrvnFuREsGt6lUug==}
+ canvas-confetti@1.9.3:
+ resolution: {integrity: sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==}
+
canvas@3.1.2:
resolution: {integrity: sha512-Z/tzFAcBzoCvJlOSlCnoekh1Gu8YMn0J51+UAuXJAbW1Z6I9l2mZgdD7738MepoeeIcUdDtbMnOg6cC7GJxy/g==}
engines: {node: ^18.12.0 || >= 20.9.0}
@@ -2828,6 +2871,9 @@ packages:
jiti:
optional: true
+ esm-env@1.2.2:
+ resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==}
+
esniff@2.0.1:
resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==}
engines: {node: '>=0.10'}
@@ -2980,8 +3026,8 @@ packages:
resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
engines: {node: '>=0.4.x'}
- framer-motion@12.23.9:
- resolution: {integrity: sha512-TqEHXj8LWfQSKqfdr5Y4mYltYLw96deu6/K9kGDd+ysqRJPNwF9nb5mZcrLmybHbU7gcJ+HQar41U3UTGanbbQ==}
+ framer-motion@12.23.22:
+ resolution: {integrity: sha512-ZgGvdxXCw55ZYvhoZChTlG6pUuehecgvEAJz0BHoC5pQKW1EC5xf1Mul1ej5+ai+pVY0pylyFfdl45qnM1/GsA==}
peerDependencies:
'@emotion/is-prop-valid': '*'
react: ^18.0.0 || ^19.0.0
@@ -3968,12 +4014,26 @@ packages:
engines: {node: '>=10'}
hasBin: true
- motion-dom@12.23.9:
- resolution: {integrity: sha512-6Sv++iWS8XMFCgU1qwKj9l4xuC47Hp4+2jvPfyTXkqDg2tTzSgX6nWKD4kNFXk0k7llO59LZTPuJigza4A2K1A==}
+ motion-dom@12.23.21:
+ resolution: {integrity: sha512-5xDXx/AbhrfgsQmSE7YESMn4Dpo6x5/DTZ4Iyy4xqDvVHWvFVoV+V2Ri2S/ksx+D40wrZ7gPYiMWshkdoqNgNQ==}
motion-utils@12.23.6:
resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==}
+ motion@12.23.22:
+ resolution: {integrity: sha512-iSq6X9vLHbeYwmHvhK//+U74ROaPnZmBuy60XZzqNl0QtZkWfoZyMDHYnpKuWFv0sNMqHgED8aCXk94LCoQPGg==}
+ peerDependencies:
+ '@emotion/is-prop-valid': '*'
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@emotion/is-prop-valid':
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+
mri@1.2.0:
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
engines: {node: '>=4'}
@@ -4046,6 +4106,9 @@ packages:
resolution: {integrity: sha512-tt6PvKu4WyzPwWUzy/hvPFqn+uwXO0K1ZHka8az3NnrhWJDmSqI8ncWq0fkL0k/lmmi5tAC11FXwXuh0rFbt1A==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ number-flow@0.5.8:
+ resolution: {integrity: sha512-FPr1DumWyGi5Nucoug14bC6xEz70A1TnhgSHhKyfqjgji2SOTz+iLJxKtv37N5JyJbteGYCm6NQ9p1O4KZ7iiA==}
+
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -6198,6 +6261,13 @@ snapshots:
'@nolyfill/is-core-module@1.0.39': {}
+ '@number-flow/react@0.5.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ esm-env: 1.2.2
+ number-flow: 0.5.8
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+
'@open-draft/deferred-promise@2.2.0': {}
'@opentelemetry/api@1.9.0': {}
@@ -6218,6 +6288,8 @@ snapshots:
'@radix-ui/primitive@1.1.2': {}
+ '@radix-ui/primitive@1.1.3': {}
+
'@radix-ui/react-accordion@1.2.11(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
'@radix-ui/primitive': 1.1.2
@@ -6914,6 +6986,21 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.8
+ '@radix-ui/react-switch@1.2.6(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0)
+ '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.8)(react@19.1.0)
+ '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.8)(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.8
+ '@types/react-dom': 19.1.6(@types/react@19.1.8)
+
'@radix-ui/react-tabs@1.1.12(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
'@radix-ui/primitive': 1.1.2
@@ -7281,6 +7368,8 @@ snapshots:
'@tanstack/table-core@8.21.3': {}
+ '@types/canvas-confetti@1.9.0': {}
+
'@types/debug@4.1.12':
dependencies:
'@types/ms': 2.1.0
@@ -7655,6 +7744,8 @@ snapshots:
caniuse-lite@1.0.30001706: {}
+ canvas-confetti@1.9.3: {}
+
canvas@3.1.2:
dependencies:
node-addon-api: 7.1.1
@@ -8260,6 +8351,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ esm-env@1.2.2: {}
+
esniff@2.0.1:
dependencies:
d: 1.0.2
@@ -8442,9 +8535,9 @@ snapshots:
format@0.2.2: {}
- framer-motion@12.23.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ framer-motion@12.23.22(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
- motion-dom: 12.23.9
+ motion-dom: 12.23.21
motion-utils: 12.23.6
tslib: 2.8.1
optionalDependencies:
@@ -9972,12 +10065,20 @@ snapshots:
mkdirp@3.0.1: {}
- motion-dom@12.23.9:
+ motion-dom@12.23.21:
dependencies:
motion-utils: 12.23.6
motion-utils@12.23.6: {}
+ motion@12.23.22(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ dependencies:
+ framer-motion: 12.23.22(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ tslib: 2.8.1
+ optionalDependencies:
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+
mri@1.2.0: {}
ms@2.1.3: {}
@@ -10036,6 +10137,10 @@ snapshots:
npm-to-yarn@3.0.1: {}
+ number-flow@0.5.8:
+ dependencies:
+ esm-env: 1.2.2
+
object-assign@4.1.1: {}
object-inspect@1.13.4: {}
diff --git a/surfsense_web/public/contact/world.svg b/surfsense_web/public/contact/world.svg
new file mode 100644
index 000000000..84c8e242e
--- /dev/null
+++ b/surfsense_web/public/contact/world.svg
@@ -0,0 +1,977 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file