diff --git a/src/app/globals.css b/src/app/globals.css
index 05b35fb..fd58d32 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -140,6 +140,51 @@ body {
-webkit-font-smoothing: antialiased;
}
+/*
+ * Apply the user's stored theme preference on first paint using only CSS.
+ * `:root.dark` is set by ThemeSync after mount; the explicit `:root.dark`
+ * selector below the `@media` block wins over the media query when the
+ * user has made a stored choice (applied before paint by the ThemeSync
+ * effect running synchronously after hydration).
+ *
+ * This file deliberately does not contain any
- );
+ // Intentionally empty: theme is applied client-side via ThemeSync.
+ return null;
}
\ No newline at end of file
diff --git a/src/components/theme/theme-sync.tsx b/src/components/theme/theme-sync.tsx
new file mode 100644
index 0000000..d98492a
--- /dev/null
+++ b/src/components/theme/theme-sync.tsx
@@ -0,0 +1,35 @@
+"use client";
+
+import { useEffect } from "react";
+
+/**
+ * Applies the user's stored theme preference as soon as React mounts.
+ *
+ * The first paint already uses the OS preference (handled in globals.css via
+ * `@media (prefers-color-scheme: dark)`), so this is purely the override for
+ * the rare user who picked the opposite of their OS default. The brief flash
+ * is intentional — it avoids the only path Next.js 16 + React 19 leave for
+ * a pre-paint inline script: a `