-
-
-
- {localeOptions.map((targetLocale) => (
-
- ))}
-
-
-
{t("badge")}
-
-
-
+
-
+
{t("titleStart")}{" "}
{t("titleAccent")}{" "}
{t("titleEnd")}
@@ -116,7 +74,7 @@ export default async function ComingSoonPage({
{t("description")}
-
+
{t("note")}
diff --git a/app/globals.css b/app/globals.css
index 7f25cc3..ec0dee6 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -40,6 +40,9 @@
--brand-primary: 214 84% 42%;
--brand-secondary: 191 78% 42%;
+ --ambient-primary-alpha: 0.18;
+ --ambient-secondary-alpha: 0.14;
+ --ambient-grid-alpha: 0.4;
--radius-surface: 0rem;
--radius-nested: 0rem;
@@ -111,6 +114,9 @@
--brand-primary: 205 88% 64%;
--brand-secondary: 190 76% 57%;
+ --ambient-primary-alpha: 0.22;
+ --ambient-secondary-alpha: 0.18;
+ --ambient-grid-alpha: 0.32;
--shadow-xs: 0 1px 2px hsl(220 50% 2% / 0.42);
--shadow-sm: 0 14px 30px -20px hsl(220 50% 2% / 0.48);
@@ -145,7 +151,24 @@ html.dark {
}
body {
- @apply min-h-screen bg-background text-foreground antialiased;
+ @apply min-h-screen text-foreground antialiased;
+ background-color: hsl(var(--background));
+ background-image:
+ radial-gradient(
+ circle at top,
+ hsl(var(--brand-primary) / var(--ambient-primary-alpha)),
+ transparent 34%
+ ),
+ radial-gradient(
+ circle at bottom right,
+ hsl(var(--brand-secondary) / var(--ambient-secondary-alpha)),
+ transparent 30%
+ ),
+ linear-gradient(hsl(var(--border) / var(--ambient-grid-alpha)) 1px, transparent 1px),
+ linear-gradient(90deg, hsl(var(--border) / var(--ambient-grid-alpha)) 1px, transparent 1px);
+ background-size: auto, auto, 24px 24px, 24px 24px;
+ background-position: center top, right bottom, center center, center center;
+ background-attachment: fixed;
}
a {
@@ -187,4 +210,18 @@ html.dark {
background-size: 24px 24px;
background-position: center center;
}
+
+ .surface-ambient {
+ background-image:
+ radial-gradient(
+ circle at top,
+ hsl(var(--brand-primary) / calc(var(--ambient-primary-alpha) + 0.04)),
+ transparent 36%
+ ),
+ radial-gradient(
+ circle at bottom right,
+ hsl(var(--brand-secondary) / calc(var(--ambient-secondary-alpha) + 0.03)),
+ transparent 32%
+ );
+ }
}
diff --git a/components/layout/animated-logo.tsx b/components/layout/animated-logo.tsx
new file mode 100644
index 0000000..dcd8038
--- /dev/null
+++ b/components/layout/animated-logo.tsx
@@ -0,0 +1,83 @@
+"use client";
+
+import { motion, useMotionTemplate, useMotionValue, useSpring } from "framer-motion";
+import Image from "next/image";
+
+export function AnimatedLogo() {
+ const rotateX = useMotionValue(0);
+ const rotateY = useMotionValue(0);
+ const springX = useSpring(rotateX, { stiffness: 180, damping: 18 });
+ const springY = useSpring(rotateY, { stiffness: 180, damping: 18 });
+ const glowX = useMotionTemplate`${springY}px`;
+ const glowY = useMotionTemplate`${springX}px`;
+
+ function handlePointerMove(event: React.PointerEvent
) {
+ const rect = event.currentTarget.getBoundingClientRect();
+ const offsetX = event.clientX - rect.left - rect.width / 2;
+ const offsetY = event.clientY - rect.top - rect.height / 2;
+
+ rotateX.set((-offsetY / rect.height) * 14);
+ rotateY.set((offsetX / rect.width) * 14);
+ }
+
+ function handlePointerLeave() {
+ rotateX.set(0);
+ rotateY.set(0);
+ }
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/components/layout/floating-preferences.tsx b/components/layout/floating-preferences.tsx
new file mode 100644
index 0000000..4271707
--- /dev/null
+++ b/components/layout/floating-preferences.tsx
@@ -0,0 +1,28 @@
+"use client";
+
+import { useTranslations } from "next-intl";
+
+import { LocaleToggle } from "@/components/layout/locale-toggle";
+import { ThemeToggle } from "@/components/theme-toggle";
+import { cn } from "@/lib/utils";
+
+type FloatingPreferencesProps = {
+ locale: string;
+ className?: string;
+};
+
+export function FloatingPreferences({
+ locale,
+ className,
+}: FloatingPreferencesProps) {
+ const t = useTranslations("navigation");
+
+ return (
+
+ );
+}
diff --git a/components/layout/locale-toggle.tsx b/components/layout/locale-toggle.tsx
index 7250fc7..5d161b0 100644
--- a/components/layout/locale-toggle.tsx
+++ b/components/layout/locale-toggle.tsx
@@ -1,8 +1,15 @@
"use client";
+import { Languages } from "lucide-react";
import { usePathname } from "next/navigation";
import { Button } from "@/components/ui/button";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
import { AppLocale, getLocalizedPath, stripLocalePrefix } from "@/lib/locale";
type LocaleToggleProps = {
@@ -16,20 +23,32 @@ export function LocaleToggle({ locale }: LocaleToggleProps) {
const currentLocale = (["de", "en", "ar"].includes(locale) ? locale : "de") as AppLocale;
return (
-
+
+
+ {locales.map((targetLocale) => (
+
+
+ {targetLocale.toUpperCase()}
+ {targetLocale === currentLocale ? (
+ Active
+ ) : null}
+
+
+ ))}
+
+
);
}
diff --git a/messages/ar.json b/messages/ar.json
index 63ebbfc..b708679 100644
--- a/messages/ar.json
+++ b/messages/ar.json
@@ -14,12 +14,12 @@
"copyright": "© {year} moh-sass. جميع الحقوق محفوظة."
},
"comingSoon": {
- "badge": "Coming Soon",
- "titleStart": "شيء",
- "titleAccent": "جديد",
- "titleEnd": "في الطريق.",
- "description": "الموقع حالياً في وضع الصيانة. عم أنهي المحتوى واللمسات الأخيرة قبل الإطلاق.",
- "note": "شكراً على صبرك. قريباً ستنطلق نسخة أوضح وأسرع وأقوى."
+ "badge": "قريباً",
+ "titleStart": "تجربة",
+ "titleAccent": "أقوى",
+ "titleEnd": "قيد الإطلاق.",
+ "description": "نعمل حالياً على تحديث الموقع وتحسين التفاصيل البصرية والمحتوى لنقدّم تجربة أوضح، أسرع، وأكثر احترافية.",
+ "note": "شكراً لصبرك. النسخة الجديدة ستصل قريباً بهوية أنضج وحضور أدق."
},
"homepage": {
"heroKicker": "Digital Studio",
diff --git a/messages/de.json b/messages/de.json
index 7bf200d..35b9962 100644
--- a/messages/de.json
+++ b/messages/de.json
@@ -15,11 +15,11 @@
},
"comingSoon": {
"badge": "Coming Soon",
- "titleStart": "Etwas",
- "titleAccent": "Neues",
- "titleEnd": "ist auf dem Weg.",
- "description": "Meine Website ist aktuell im Wartungsmodus. Ich finalisiere Inhalte und den letzten Feinschliff vor dem Launch.",
- "note": "Danke fuer deine Geduld. Bald geht eine klarere, schnellere und staerkere Version live."
+ "titleStart": "Ein praeziserer",
+ "titleAccent": "digitaler Auftritt",
+ "titleEnd": "entsteht gerade.",
+ "description": "Die Website wird aktuell in Inhalt, Gestaltung und Gesamtwirkung verfeinert, damit der Launch klarer, hochwertiger und stimmiger wird.",
+ "note": "Danke fuer deine Geduld. Die naechste Version geht bald mit mehr Klarheit, Tempo und Charakter live."
},
"homepage": {
"heroKicker": "Digital Studio",
diff --git a/messages/en.json b/messages/en.json
index 50d9ec6..1462253 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -15,11 +15,11 @@
},
"comingSoon": {
"badge": "Coming Soon",
- "titleStart": "Something",
- "titleAccent": "new",
- "titleEnd": "is on the way.",
- "description": "My website is currently in maintenance mode. I am finalizing content and polish before launch.",
- "note": "Thank you for your patience. A cleaner, faster, and stronger version is launching soon."
+ "titleStart": "A sharper",
+ "titleAccent": "digital presence",
+ "titleEnd": "is taking shape.",
+ "description": "The site is being refined with stronger storytelling, cleaner visuals, and a more polished experience before launch.",
+ "note": "Thank you for your patience. The next version is arriving soon with more clarity, speed, and intent."
},
"homepage": {
"heroKicker": "Digital Studio",