diff --git a/package.json b/package.json
index b4052776..90eae8fc 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"next": "^15.1.8",
"react": "^19.0.0",
"react-dom": "^19.0.0",
+ "react-icons": "^5.6.0",
"reframe": ".",
"tailwind-merge": "^3.6.0",
"wasm-feature-detect": "^1.8.0"
diff --git a/src/app/globals.css b/src/app/globals.css
index 52b2929d..602f8484 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -36,26 +36,12 @@
--error-hover: #991b1b;
}
-/* ── High contrast mode tokens ── */
-[data-theme="high-contrast"] {
- --bg: #000000;
- --surface: #000000;
- --border: #FFFFFF;
- --text: #FFFFFF;
- --muted: #FFFFFF;
-
- --accent: #FFFF00;
- --accent-hover: #FFFF00;
-
- --error: #FF6666;
- --success: #66FF66;
-
- --film-600: #FF6666;
- --film-400: #66FF66;
-}
/* ── Base styles ── */
+html {
+ scroll-behavior: smooth;
+}
body {
background-color: var(--bg);
color: var(--text);
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 03ee0db9..71bc75c3 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -3,9 +3,8 @@ import { Bebas_Neue, Syne, DM_Sans } from "next/font/google";
import ErrorBoundary from "@/components/ErrorBoundary";
import "./globals.css";
import { ThemeProvider } from "@/components/ThemeProvider";
-import { ThemeToggle } from "@/components/ThemeToggle";
+import Header from "@/components/Header";
import ScrollToTop from "@/components/ScrollToTop";
-import BrandLogo from "@/components/BrandLogo";
export const metadata: Metadata = {
title: "Reframe — Resize, trim, and export videos in your browser",
@@ -47,7 +46,7 @@ export default function RootLayout({
children: React.ReactNode;
}) {
return (
-
+
@@ -87,16 +86,7 @@ export default function RootLayout({
-
+
{children}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 637feae3..5a52f5bf 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,24 +1,5 @@
-import VideoEditor from "@/components/VideoEditor";
-import Footer from "@/components/Footer";
+import LandingWrapper from "@/components/landing/LandingWrapper";
-export default function Home() {
- return (
- <>
-
- ⭐ Star on GitHub
-
-
-
-
-
-
-
- >
- );
+export default function Page() {
+ return ;
}
-
diff --git a/src/app/reframe/page.tsx b/src/app/reframe/page.tsx
new file mode 100644
index 00000000..e339f69e
--- /dev/null
+++ b/src/app/reframe/page.tsx
@@ -0,0 +1,23 @@
+import VideoEditor from "@/components/VideoEditor";
+import Footer from "@/components/Footer";
+
+export default function ReframePage() {
+ return (
+ <>
+
+ ⭐ Star on GitHub
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
new file mode 100644
index 00000000..8800bfd9
--- /dev/null
+++ b/src/components/Header.tsx
@@ -0,0 +1,27 @@
+"use client";
+
+import { usePathname } from "next/navigation";
+import BrandLogo from "@/components/BrandLogo";
+import { ThemeToggle } from "@/components/ThemeToggle";
+
+export default function Header() {
+ const pathname = usePathname();
+
+ // Hide the default editor header on the landing page
+ if (pathname === "/") {
+ return null;
+ }
+
+ return (
+
+ );
+}
diff --git a/src/components/ThemeProvider.tsx b/src/components/ThemeProvider.tsx
index 7c65cef3..23f6d688 100644
--- a/src/components/ThemeProvider.tsx
+++ b/src/components/ThemeProvider.tsx
@@ -9,7 +9,7 @@ import {
type ReactNode,
} from "react";
-type Theme = "light" | "dark" | "high-contrast";
+type Theme = "light" | "dark";
interface ThemeContextValue {
theme: Theme;
@@ -27,15 +27,15 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
// we just sync React state here so the toggle button shows the right icon.
useEffect(() => {
try {
- const stored = localStorage.getItem("theme") as Theme | null;
- if (stored === "light" || stored === "dark" || stored === "high-contrast") {
- setThemeState(stored);
- } else {
- const prefersDark = window.matchMedia(
- "(prefers-color-scheme: dark)"
- ).matches;
- setThemeState(prefersDark ? "dark" : "light");
- }
+ const stored = localStorage.getItem("theme") as Theme | null;
+ if (stored === "light" || stored === "dark") {
+ setThemeState(stored);
+ } else {
+ const prefersDark = window.matchMedia(
+ "(prefers-color-scheme: dark)"
+ ).matches;
+ setThemeState(prefersDark ? "dark" : "light");
+ }
} catch {
const prefersDark = window.matchMedia(
"(prefers-color-scheme: dark)"
@@ -62,14 +62,8 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
} else {
document.documentElement.classList.remove("dark");
}
- if (next === "high-contrast") {
- document.documentElement.setAttribute(
- "data-theme",
- "high-contrast"
- );
- } else {
+ // Always remove high-contrast data theme attribute
document.documentElement.removeAttribute("data-theme");
- }
if (persist) {
localStorage.setItem("theme", next);
}
@@ -78,13 +72,7 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
);
const toggleTheme = useCallback(() => {
- applyTheme(
- theme === "light"
- ? "dark"
- : theme === "dark"
- ? "high-contrast"
- : "light"
- );
+ applyTheme(theme === "light" ? "dark" : "light");
}, [theme, applyTheme]);
const setTheme = useCallback(
diff --git a/src/components/ThemeToggle.tsx b/src/components/ThemeToggle.tsx
index f8ceeb9f..4384dbe3 100644
--- a/src/components/ThemeToggle.tsx
+++ b/src/components/ThemeToggle.tsx
@@ -12,22 +12,10 @@ export function ThemeToggle() {
return (
) : (
- /* Moon icon — shown in dark/high contrast mode */
+ /* Moon icon — shown in dark mode */