diff --git a/apps/marketing/src/components/GitHubStarsButton.tsx b/apps/marketing/src/components/GitHubStarsButton.tsx
index 4f1dba0..5d9b35b 100644
--- a/apps/marketing/src/components/GitHubStarsButton.tsx
+++ b/apps/marketing/src/components/GitHubStarsButton.tsx
@@ -1,6 +1,6 @@
import { Button } from "@usesend/ui/src/button";
-const REPO = "unsend-dev/unsend";
+const REPO = "usesend/usesend";
const REPO_URL = `https://github.com/${REPO}`;
const API_URL = `https://api.github.com/repos/${REPO}`;
const REVALIDATE_SECONDS = 60 * 60 * 24 * 7; // 7 days
diff --git a/apps/marketing/src/components/PricingCalculator.tsx b/apps/marketing/src/components/PricingCalculator.tsx
new file mode 100644
index 0000000..9f2b338
--- /dev/null
+++ b/apps/marketing/src/components/PricingCalculator.tsx
@@ -0,0 +1,177 @@
+"use client";
+
+import React from "react";
+
+type SliderProps = {
+ label: string;
+ value: number;
+ onChange: (v: number) => void;
+ min?: number;
+ max?: number;
+ step?: number;
+ suffix?: string;
+};
+
+function Slider({
+ label,
+ value,
+ onChange,
+ min = 0,
+ max = 100000,
+ step = 500,
+ suffix = "",
+}: SliderProps) {
+ const id = React.useId();
+ const [dragging, setDragging] = React.useState(false);
+ const percent = Math.max(
+ 0,
+ Math.min(100, ((value - min) / (max - min)) * 100)
+ );
+
+ React.useEffect(() => {
+ if (!dragging) return;
+ const stop = () => setDragging(false);
+ window.addEventListener("mouseup", stop);
+ window.addEventListener("touchend", stop);
+ window.addEventListener("pointerup", stop);
+ return () => {
+ window.removeEventListener("mouseup", stop);
+ window.removeEventListener("touchend", stop);
+ window.removeEventListener("pointerup", stop);
+ };
+ }, [dragging]);
+
+ return (
+
+
+
+
+ {value.toLocaleString()} {suffix}
+
+
+
+
onChange(Number(e.target.value))}
+ onMouseDown={() => setDragging(true)}
+ onTouchStart={() => setDragging(true)}
+ onPointerDown={() => setDragging(true)}
+ className="w-full accent-primary"
+ aria-label={label}
+ aria-valuetext={`${value.toLocaleString()} ${suffix}`}
+ />
+ {dragging && (
+
+
+ {value.toLocaleString()} {suffix}
+
+
+ )}
+
+
+ );
+}
+
+export function PricingCalculator() {
+ // Rates from pricing copy
+ const MARKETING_RATE = 0.001; // $ per marketing email
+ const TRANSACTIONAL_RATE = 0.0004; // $ per transactional email
+ const MINIMUM_SPEND = 10; // $ minimum monthly spend
+
+ // Defaults chosen to total $10: 8000*$0.001 + 5000*$0.0004 = 10
+ const [marketing, setMarketing] = React.useState
(5000);
+ const [transactional, setTransactional] = React.useState(12500);
+
+ const marketingCost = marketing * MARKETING_RATE;
+ const transactionalCost = transactional * TRANSACTIONAL_RATE;
+ const subtotal = marketingCost + transactionalCost;
+ const totalDue = Math.max(subtotal, MINIMUM_SPEND);
+
+ return (
+
+
+
+
+
+
+ Pricing Calculator
+
+
+ Drag the sliders to estimate your monthly cost.
+
+
+
+
+
+
+
+
+
+
+
Marketing
+
+ ${marketingCost.toFixed(2)}
+
+
+ @ ${MARKETING_RATE.toFixed(4)} each
+
+
+
+
+ Transactional
+
+
+ ${transactionalCost.toFixed(2)}
+
+
+ @ ${TRANSACTIONAL_RATE.toFixed(4)} each
+
+
+
+
+ Estimated Total
+
+
+ ${totalDue.toFixed(2)}
+
+
+ {subtotal < MINIMUM_SPEND
+ ? "Minimum $10 applies"
+ : "before taxes"}
+
+
+
+
+
+
+
+ );
+}
+
+export default PricingCalculator;
diff --git a/apps/marketing/src/components/SiteFooter.tsx b/apps/marketing/src/components/SiteFooter.tsx
new file mode 100644
index 0000000..eb40cac
--- /dev/null
+++ b/apps/marketing/src/components/SiteFooter.tsx
@@ -0,0 +1,90 @@
+import Image from "next/image";
+import Link from "next/link";
+import { StatusBadge } from "~/components/StatusBadge";
+
+const REPO = "usesend/usesend";
+const REPO_URL = `https://github.com/${REPO}`;
+const APP_URL = "https://app.usesend.com";
+
+export function SiteFooter() {
+ return (
+
+ );
+}
+
diff --git a/apps/marketing/src/components/StatusBadge.tsx b/apps/marketing/src/components/StatusBadge.tsx
new file mode 100644
index 0000000..e7650ae
--- /dev/null
+++ b/apps/marketing/src/components/StatusBadge.tsx
@@ -0,0 +1,131 @@
+"use client";
+
+import { useEffect, useMemo, useState } from "react";
+
+type StatusState = "operational" | "degraded" | "down" | "unknown";
+
+// Best-effort fetcher for Uptime Kuma public status page JSON.
+// Falls back gracefully if the endpoint or CORS is not available.
+async function fetchUptimeStatus(baseUrl: string): Promise {
+ const candidates = [
+ "/api/status-page/heartbeat/default", // specific uptime-kuma status page slug
+ ];
+
+ for (const path of candidates) {
+ try {
+ const res = await fetch(baseUrl.replace(/\/$/, "") + path, {
+ // Always fetch from the browser; avoid caching too aggressively
+ cache: "no-store",
+ });
+ if (!res.ok) continue;
+ const data: any = await res.json().catch(() => null);
+ if (!data) continue;
+
+ // Heuristics across possible Kuma payloads
+ // 1) overallStatus or status fields
+ const overall = (data.overallStatus || data.status || "")
+ .toString()
+ .toLowerCase();
+ if (
+ overall.includes("up") ||
+ overall.includes("ok") ||
+ overall.includes("oper")
+ )
+ return "operational";
+ if (overall.includes("degrad") || overall.includes("partial"))
+ return "degraded";
+ if (
+ overall.includes("down") ||
+ overall.includes("outage") ||
+ overall.includes("incident")
+ )
+ return "down";
+
+ // 2) heartbeat style: if any monitor is down
+ if (Array.isArray(data.monitors) && Array.isArray(data.heartbeatList)) {
+ // If any lastHeartbeatStatus === 0 (down) => down
+ const isAnyDown = Object.values(data.heartbeatList).some(
+ (arr: any[]) =>
+ Array.isArray(arr) && arr.some((hb) => hb?.status === 0)
+ );
+ if (isAnyDown) return "down";
+ return "operational";
+ }
+
+ // 3) Generic boolean hints
+ if (typeof data.allUp === "boolean")
+ return data.allUp ? "operational" : "down";
+
+ // Unknown but successful response
+ return "unknown";
+ } catch {
+ // Try next candidate
+ continue;
+ }
+ }
+ return "unknown";
+}
+
+export function StatusBadge({
+ baseUrl = "https://status.usesend.com",
+}: {
+ baseUrl?: string;
+}) {
+ const [status, setStatus] = useState("unknown");
+
+ useEffect(() => {
+ let mounted = true;
+ const load = async () => {
+ const s = await fetchUptimeStatus(baseUrl);
+ if (mounted) setStatus(s);
+ };
+ load();
+ const id = setInterval(load, 60_000); // refresh every 60s
+ return () => {
+ mounted = false;
+ clearInterval(id);
+ };
+ }, [baseUrl]);
+
+ const dotClass = useMemo(() => {
+ switch (status) {
+ case "operational":
+ return "bg-green";
+ case "degraded":
+ return "bg-yellow";
+ case "down":
+ return "bg-red";
+ default:
+ return "bg-muted-foreground";
+ }
+ }, [status]);
+
+ const label = useMemo(() => {
+ switch (status) {
+ case "operational":
+ return "operational";
+ case "degraded":
+ return "degraded";
+ case "down":
+ return "outage";
+ default:
+ return "unknown";
+ }
+ }, [status]);
+
+ return (
+
+
+ {label}
+
+ );
+}
diff --git a/apps/marketing/src/components/TopNav.tsx b/apps/marketing/src/components/TopNav.tsx
index 1e1d1fa..9d9d11c 100644
--- a/apps/marketing/src/components/TopNav.tsx
+++ b/apps/marketing/src/components/TopNav.tsx
@@ -6,7 +6,7 @@ import { usePathname } from "next/navigation";
import { useState } from "react";
import { Button } from "@usesend/ui/src/button";
-const REPO = "unsend-dev/unsend";
+const REPO = "usesend/usesend";
const REPO_URL = `https://github.com/${REPO}`;
const APP_URL = "https://app.usesend.com";
diff --git a/packages/ui/styles/globals.css b/packages/ui/styles/globals.css
index ac26ca1..b7fa2f4 100644
--- a/packages/ui/styles/globals.css
+++ b/packages/ui/styles/globals.css
@@ -9,7 +9,7 @@
--foreground: 234 16% 35%;
--card: 0 0% 100%;
- --card-foreground: 222.2 84% 4.9%;
+ --card-foreground: 234 16% 35%;
--popover: 220 2% 96%;
--popover-foreground: 234 16% 35%;
@@ -71,7 +71,7 @@
--foreground: 226 64% 88%;
--card: 222.2 84% 4.9%;
- --card-foreground: 210 40% 98%;
+ --card-foreground: 226 64% 88%;
--popover: 240 21% 15%;
--popover-foreground: 226 64% 88%;