diff --git a/app/[locale]/(site)/about/page.tsx b/app/[locale]/(site)/about/page.tsx index a5ecc29..2fc8838 100644 --- a/app/[locale]/(site)/about/page.tsx +++ b/app/[locale]/(site)/about/page.tsx @@ -1,14 +1,29 @@ import type { Metadata } from "next"; -import { Compass, Layers3, Users } from "lucide-react"; +import { + ArrowRight, + Blocks, + BriefcaseBusiness, + Compass, + Layers3, + MessageSquareMore, + Sparkles, + Workflow, +} from "lucide-react"; +import Image from "next/image"; +import Link from "next/link"; import { getTranslations } from "next-intl/server"; import { Container } from "@/components/layout/container"; import { PageHero } from "@/components/layout/page-hero"; +import { SiteLogo } from "@/components/layout/site-logo"; import { MotionFade } from "@/components/motion-fade"; -import { buildLocalizedMetadata } from "@/lib/metadata"; -import { resolveLocale } from "@/lib/locale"; import { AppCard } from "@/components/ui/app-card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; import { CardContent } from "@/components/ui/card"; +import { getSiteSettings, getSiteSettingsMediaBindings } from "@/lib/app-config"; +import { getLocalizedPath, resolveLocale } from "@/lib/locale"; +import { buildLocalizedMetadata } from "@/lib/metadata"; type AboutPageProps = { params: { @@ -16,6 +31,8 @@ type AboutPageProps = { }; }; +export const dynamic = "force-dynamic"; + export async function generateMetadata({ params: { locale }, }: AboutPageProps): Promise { @@ -32,7 +49,68 @@ export async function generateMetadata({ export default async function AboutPage({ params: { locale } }: AboutPageProps) { const localeKey = resolveLocale(locale); - const t = await getTranslations({ locale: localeKey, namespace: "aboutPage" }); + const [t, siteSettings, mediaBindings] = await Promise.all([ + getTranslations({ locale: localeKey, namespace: "aboutPage" }), + getSiteSettings(), + getSiteSettingsMediaBindings(), + ]); + const siteName = siteSettings.locales[localeKey].siteName; + + const valueItems = [ + { + icon: Compass, + title: t("valueA"), + text: t("valueAText"), + }, + { + icon: Layers3, + title: t("valueB"), + text: t("valueBText"), + }, + { + icon: MessageSquareMore, + title: t("valueC"), + text: t("valueCText"), + }, + ]; + + const processItems = [ + { + icon: Sparkles, + title: t("processOne"), + text: t("processOneText"), + }, + { + icon: Blocks, + title: t("processTwo"), + text: t("processTwoText"), + }, + { + icon: Workflow, + title: t("processThree"), + text: t("processThreeText"), + }, + ]; + + const statItems = [ + { + value: t("statOneValue"), + label: t("statOneLabel"), + text: t("statOneText"), + }, + { + value: t("statTwoValue"), + label: t("statTwoLabel"), + text: t("statTwoText"), + }, + { + value: t("statThreeValue"), + label: t("statThreeLabel"), + text: t("statThreeText"), + }, + ]; + + const highlightItems = [t("highlightOne"), t("highlightTwo"), t("highlightThree")]; return ( <> @@ -43,70 +121,222 @@ export default async function AboutPage({ params: { locale } }: AboutPageProps) description={t("intro")} /> - + - - -

- {t("valuesTitle")} -

-
- {[ - { - icon: Compass, - title: t("valueA"), - text: t("valueAText"), - }, - { - icon: Layers3, - title: t("valueB"), - text: t("valueBText"), - }, - { - icon: Users, - title: t("valueC"), - text: t("valueCText"), - }, - ].map((item) => { +
+ + + + {t("storyEyebrow")} + + +

+ {t("storyTitle")} +

+ +
+
+

+ {t("storyTextOne")} +

+

+ {t("storyTextTwo")} +

+
+ +
+ {highlightItems.map((item) => ( + + {item} + + ))} +
+
+ +
+ + +
+
+
+ + + +
+ + +
+
+

+ {t("logoEyebrow")} +

+

+ {t("logoText")} +

+
+ +
+
+
+ + +
+ {t("galleryAltOne")} +
+
+ + + +
+ + {t("availabilityLabel")} +
+

+ {t("availabilityText")} +

+
+
+ + +
+ {t("galleryAltTwo")} +
+
+
+
+
+
+ + + +
+ + + + {t("valuesEyebrow")} + +

+ {t("valuesTitle")} +

+

+ {t("valuesIntro")} +

+
+
+ + {valueItems.map((item) => { + const Icon = item.icon; + + return ( + + +
+ +
+

{item.title}

+

{item.text}

+
+
+ ); + })} +
+
+ + +
+ + + + {t("processEyebrow")} + +

+ {t("processTitle")} +

+

+ {t("processIntro")} +

+ +
+ {statItems.map((item) => ( + +

+ {item.value} +

+

+ {item.label} +

+

{item.text}

+
+ ))} +
+
+
+ +
+ {processItems.map((item, index) => { const Icon = item.icon; + const isWide = index === 0; return ( - - - -

- {item.title} -

-

{item.text}

+ + +
+ + 0{index + 1} + + +
+

{item.title}

+

+ {item.text} +

); })}
- - - - - - - -

- {t("processTitle")} -

-
    - {[t("processOne"), t("processTwo"), t("processThree")].map((step, index) => ( - - - - {index + 1} - -

    {step}

    -
    -
    - ))} -
-
-
+
diff --git a/app/[locale]/(site)/page.tsx b/app/[locale]/(site)/page.tsx index 38b75cd..0ab6a51 100644 --- a/app/[locale]/(site)/page.tsx +++ b/app/[locale]/(site)/page.tsx @@ -133,9 +133,11 @@ export default async function HomePage({ params: { locale } }: HomePageProps) { const Icon = serviceIcons[index]; return ( -
@@ -143,7 +145,7 @@ export default async function HomePage({ params: { locale } }: HomePageProps) {

{item}

-
+ ); })}
diff --git a/components/ui/app-card.tsx b/components/ui/app-card.tsx index f496761..a8f35bc 100644 --- a/components/ui/app-card.tsx +++ b/components/ui/app-card.tsx @@ -5,29 +5,78 @@ import { Card } from "@/components/ui/card"; import { cn } from "@/lib/utils"; const appCardVariants = cva( - "rounded-surface transition-colors", + "rounded-surface border p-2.5 transition-colors", + { + variants: { + level: { + 1: "border-border/80 bg-surface-2 text-card-foreground shadow-card", + 2: "border-border/80 bg-surface-2 text-foreground shadow-xs", + 3: "border-border/80 bg-surface-2 text-foreground shadow-panel", + inverse: "border-transparent bg-foreground/92 text-background shadow-card", + }, + interactive: { + true: "hover:bg-accent/30 hover:text-accent-foreground", + false: "", + }, + }, + defaultVariants: { + level: 1, + interactive: false, + }, +}); + +const appCardInnerVariants = cva( + "h-full rounded-nested border p-4 transition-colors", + { + variants: { + level: { + 1: "border-border/70 bg-background dark:bg-surface-1", + 2: "border-border/70 bg-background dark:bg-surface-1", + 3: "border-border/70 bg-background dark:bg-surface-1", + inverse: "border border-background/10 bg-background/10 text-inherit", + }, + padding: { + none: "p-0", + sm: "p-4", + md: "p-6", + lg: "p-8", + }, + interactive: { + true: "hover:bg-accent/40 hover:text-accent-foreground", + false: "", + }, + }, + defaultVariants: { + level: 1, + padding: "sm", + interactive: false, + }, +}); + +const appCardSingleLayerVariants = cva( + "rounded-nested border transition-colors", { variants: { level: { - 1: "border border-border/80 bg-card text-card-foreground shadow-card", - 2: "border border-input bg-background text-foreground shadow-xs", - 3: "border border-border/80 bg-surface-2 text-foreground shadow-panel", - inverse: "border-transparent bg-foreground text-background shadow-card", + 1: "border-border/70 bg-background dark:bg-surface-1", + 2: "border-border/70 bg-background dark:bg-surface-1", + 3: "border-border/70 bg-background dark:bg-surface-1", + inverse: "border border-background/10 bg-background/10 text-inherit", }, padding: { - none: "", + none: "p-0", sm: "p-4", md: "p-6", lg: "p-8", }, interactive: { - true: "hover:bg-accent/30 hover:text-accent-foreground", + true: "hover:bg-accent/40 hover:text-accent-foreground", false: "", }, }, defaultVariants: { level: 1, - padding: "none", + padding: "sm", interactive: false, }, }, @@ -35,23 +84,58 @@ const appCardVariants = cva( export interface AppCardProps extends React.HTMLAttributes, - VariantProps {} + VariantProps, + VariantProps { + layer?: "double" | "single"; +} const AppCard = React.forwardRef( - ({ className, level, padding, interactive, ...props }, ref) => ( - - ), + ({ className, level, padding, interactive, layer = "double", children, ...props }, ref) => { + if (layer === "single") { + return ( + + {children} + + ); + } + + return ( + +
+ {children} +
+
+ ); + }, ); AppCard.displayName = "AppCard"; diff --git a/components/ui/ui-kit-showcase.tsx b/components/ui/ui-kit-showcase.tsx index 7455779..38a6869 100644 --- a/components/ui/ui-kit-showcase.tsx +++ b/components/ui/ui-kit-showcase.tsx @@ -78,6 +78,8 @@ export function UiKitShowcase({ localeKey }: UiKitShowcaseProps) { nestedContent: "Verschachtelte Inhalte nutzen den zweiten Radius und eine weichere Oberflaeche.", nestedSurface: "Verschachtelte Ebene", nestedSurfaceText: "Mehr Tiefe ohne einen neuen Component Stil zu erfinden.", + singleLayerCard: "Single Layer Card", + singleLayerCardText: "Fuer kompakte Kennzahlen und kleine Infobloecke ohne zweite innere Schale.", interactiveOuterCard: "Interaktive aeussere Card", hoverText: "Das Hover Verhalten bleibt an dieselbe Oberflaechenebene gebunden.", inverseOuterCard: "Inverse aeussere Card", @@ -175,6 +177,8 @@ export function UiKitShowcase({ localeKey }: UiKitShowcaseProps) { nestedContent: "Nested content uses the secondary radius and a softer surface.", nestedSurface: "Nested surface", nestedSurfaceText: "Deeper emphasis without inventing a new component style.", + singleLayerCard: "Single layer card", + singleLayerCardText: "For compact metrics and small info blocks without a second inner shell.", interactiveOuterCard: "Interactive outer card", hoverText: "Hover behavior stays attached to the same surface level.", inverseOuterCard: "Inverse outer card", @@ -298,7 +302,19 @@ export function UiKitShowcase({ localeKey }: UiKitShowcaseProps) { -
+
+ + +

{copy.singleLayerCard}

+

06+

+

+ {copy.surfaceLevel} +

+

+ {copy.singleLayerCardText} +

+
+

{copy.interactiveOuterCard}

diff --git a/messages/ar.json b/messages/ar.json index 335ab9f..5427373 100644 --- a/messages/ar.json +++ b/messages/ar.json @@ -66,18 +66,49 @@ "title": "من أنا", "heroBadge": "خلّيني عرّفك عليّي", "heroTitle": "معلومات عني", - "intro": "أبني تجارب رقمية واضحة للعلامات والمنتجات والفرق.", + "intro": "أبني تجارب رقمية واضحة للعلامات والمنتجات والفرق، مع تركيز على الهوية والواجهة والبنية القابلة للتوسع.", + "storyEyebrow": "نبذة سريعة", + "storyTitle": "أحوّل الأفكار المبعثرة إلى حضور رقمي مرتب وواضح.", + "storyTextOne": "هذا النص مؤقت حالياً. الفكرة هنا إن الصفحة تعطي انطباع جاهز: مين أنا، كيف أشتغل، وكيف أقدر أبني تجربة متماسكة من أول الفكرة لآخر الإطلاق.", + "storyTextTwo": "حالياً وضعت محتوى وهمي قريب من النبرة النهائية، حتى يصير أسهل استبداله لاحقاً بمحتوى فعلي بدون الرجوع لتعديل التصميم من جديد.", + "highlightOne": "هوية أوضح ومسار استخدام أنظف.", + "highlightTwo": "واجهة منظمة ومحتوى أسهل بالتبديل.", + "highlightThree": "بنية مناسبة للتطوير المستقبلي.", + "primaryCta": "تواصل معي", + "secondaryCta": "شوف معرض الأعمال", + "logoEyebrow": "الهوية الحالية", + "logoText": "الشعار مأخوذ من إعدادات الموقع حتى تبقى الصفحة متطابقة مع الهوية الحالية.", + "galleryAltOne": "صورة وهمية رئيسية لقسم معلومات عني", + "galleryAltTwo": "صورة وهمية ثانوية لقسم معلومات عني", + "availabilityLabel": "الوضع الحالي", + "availabilityText": "متاح حالياً لاستقبال مشاريع جديدة، وهذه الجملة أيضاً placeholder قابل للتعديل.", + "statOneValue": "06+", + "statOneLabel": "سنوات تقريبية", + "statOneText": "رقم تجريبي ليوضح مكان الخبرة أو عدد السنوات بعد تعبئة المحتوى الحقيقي.", + "statTwoValue": "24", + "statTwoLabel": "مشاريع مختارة", + "statTwoText": "مكان مخصص لعدد المشاريع أو العملاء أو الإطلاقات حسب الصياغة النهائية.", + "statThreeValue": "03", + "statThreeLabel": "مسارات خدمة", + "statThreeText": "يمكن تحويل هذا الرقم لاحقاً إلى تخصصات أو منتجات أو مجالات عمل رئيسية.", + "valuesEyebrow": "طريقة الشغل", "valuesTitle": "كيف أعمل", + "valuesIntro": "هذا القسم صار جاهز لعرض المبادئ الأساسية بشكل سريع وواضح بدل فقرة نصية طويلة.", "valueA": "الاستراتيجية أولاً", - "valueAText": "كل مشروع يبدأ بهدف واضح ونطاق وأولويات محددة.", + "valueAText": "أبدأ بتحديد الهدف، الجمهور، وحدود النطاق حتى ما يتحول التنفيذ إلى تخمين.", "valueB": "أنظمة نظيفة", - "valueBText": "أعتمد على مكونات قابلة للصيانة وبنية واضحة.", + "valueBText": "أبني مكونات قابلة للصيانة والتوسعة حتى يظل المشروع مرتب بعد الإطلاق.", "valueC": "تعاون قريب", - "valueCText": "دورات قصيرة مع ملاحظات مباشرة طوال التنفيذ.", + "valueCText": "أفضل دورات قصيرة وملاحظات مباشرة حتى يظل القرار واضحاً والنتيجة أدق.", + "processEyebrow": "من البداية للنهاية", "processTitle": "آلية العمل", + "processIntro": "قسم مرتب يشرح الرحلة بثلاث خطوات سريعة، وبإمكانك لاحقاً تعديله حسب طريقتك الفعلية.", "processOne": "اكتشاف وتحديد الهدف", + "processOneText": "نجمع الصورة العامة، نحدد الأولويات، ونرتب المطلوب قبل أي قرار بصري أو تقني.", "processTwo": "تصميم ونمذجة أولية", - "processThree": "بناء واختبار وإطلاق" + "processTwoText": "أصيغ الاتجاه العام، أبني تصور واجهات، وأجهز أساس بصري يسهّل بقية التنفيذ.", + "processThree": "بناء واختبار وإطلاق", + "processThreeText": "بعد تثبيت الاتجاه، يتحول العمل إلى تنفيذ منظم، مراجعة سريعة، ثم إطلاق جاهز." }, "contactPage": { "title": "تواصل", diff --git a/messages/de.json b/messages/de.json index 46b4eb6..8e98179 100644 --- a/messages/de.json +++ b/messages/de.json @@ -66,18 +66,49 @@ "title": "Über mich", "heroBadge": "Mehr über mich", "heroTitle": "Über mich", - "intro": "Ich baue klare digitale Erlebnisse für Marken, Produkte und Teams.", + "intro": "Ich baue klare digitale Erlebnisse für Marken, Produkte und Teams, mit Fokus auf Identität, Interface Qualität und skalierbare Struktur.", + "storyEyebrow": "Kurzprofil", + "storyTitle": "Ich verwandle verstreute Ideen in einen klaren und strukturierten digitalen Auftritt.", + "storyTextOne": "Das ist vorerst Platzhaltertext. Ziel ist eine Seite, die fast fertig wirkt: wer ich bin, wie ich arbeite und wie aus einer Idee ein stimmiger Launch wird.", + "storyTextTwo": "Der aktuelle Inhalt ist bewusst nah an einer finalen Tonalität, damit echter Text später ohne neues visuelles Rework eingesetzt werden kann.", + "highlightOne": "Klarere Identität und sauberer Nutzerfluss.", + "highlightTwo": "Geordnete Oberfläche und leicht austauschbarer Content.", + "highlightThree": "Struktur vorbereitet für spätere Weiterentwicklung.", + "primaryCta": "Kontakt aufnehmen", + "secondaryCta": "Portfolio ansehen", + "logoEyebrow": "Aktuelle Identität", + "logoText": "Das Logo wird aus den Site Settings geladen, damit die Seite mit der aktuellen Markenbasis übereinstimmt.", + "galleryAltOne": "Primäres Platzhalterbild für die Über mich Seite", + "galleryAltTwo": "Sekundäres Platzhalterbild für die Über mich Seite", + "availabilityLabel": "Aktueller Status", + "availabilityText": "Aktuell offen für neue Projekte, und auch diese Zeile ist als Platzhalter für später gedacht.", + "statOneValue": "06+", + "statOneLabel": "Jahre grob", + "statOneText": "Ein Platzhalter für Erfahrung oder Jahre in der Praxis, sobald die echten Inhalte feststehen.", + "statTwoValue": "24", + "statTwoLabel": "Ausgewählte Projekte", + "statTwoText": "Ein flexibler Platz für Projekte, Kunden, Launches oder einen später stärkeren Nachweis.", + "statThreeValue": "03", + "statThreeLabel": "Leistungsfelder", + "statThreeText": "Später kann das in Spezialisierungen, Produktlinien oder Kernbereiche umgewandelt werden.", + "valuesEyebrow": "Arbeitsweise", "valuesTitle": "Wie ich arbeite", + "valuesIntro": "Dieser Bereich ist jetzt so aufgebaut, dass die Grundprinzipien schnell erfassbar sind statt in einem langen Absatz zu verschwinden.", "valueA": "Strategie zuerst", - "valueAText": "Jedes Projekt startet mit Zielbild, Scope und Prioritäten.", + "valueAText": "Ich beginne mit Ziel, Zielgruppe und Scope, damit die Umsetzung nicht in Vermutungen abgleitet.", "valueB": "Saubere Systeme", - "valueBText": "Ich setze auf wartbare Komponenten und klare Strukturen.", + "valueBText": "Ich baue wartbare Komponenten und ein klares System, das nach dem Launch weiter wachsen kann.", "valueC": "Enge Zusammenarbeit", - "valueCText": "Kurze Schleifen mit direktem Feedback im gesamten Ablauf.", + "valueCText": "Ich arbeite lieber in kurzen Schleifen mit direktem Feedback, damit Entscheidungen klar bleiben.", + "processEyebrow": "Von Start bis Launch", "processTitle": "Mein Ablauf", + "processIntro": "Ein kompakter Drei Schritte Ablauf, der sich später mit deinem echten Prozess ersetzen lässt ohne den Bereich neu zu gestalten.", "processOne": "Discovery und Zieldefinition", + "processOneText": "Wir ordnen das Gesamtbild, priorisieren die Anforderungen und klären das Ziel vor der visuellen oder technischen Arbeit.", "processTwo": "Design und Prototyping", - "processThree": "Build, Test und Launch" + "processTwoText": "Ich forme die Richtung, entwickle erste Interface Ideen und setze ein visuelles System für die Umsetzung auf.", + "processThree": "Build, Test und Launch", + "processThreeText": "Sobald die Richtung steht, geht die Arbeit in strukturierte Umsetzung, Review und Launch Vorbereitung über." }, "contactPage": { "title": "Kontakt", diff --git a/messages/en.json b/messages/en.json index 2c89917..d13717b 100644 --- a/messages/en.json +++ b/messages/en.json @@ -66,18 +66,49 @@ "title": "About", "heroBadge": "A bit more about me", "heroTitle": "About me", - "intro": "I build clear digital experiences for brands, products, and teams.", + "intro": "I build clear digital experiences for brands, products, and teams, with focus on identity, interface quality, and scalable structure.", + "storyEyebrow": "Quick overview", + "storyTitle": "I turn scattered ideas into a clear and structured digital presence.", + "storyTextOne": "This is placeholder copy for now. The goal is to make the page feel nearly production-ready: who I am, how I work, and how I shape a cohesive experience from concept to launch.", + "storyTextTwo": "The current content is intentionally close to the final tone, so replacing it later with real copy should not require another visual rewrite.", + "highlightOne": "Sharper identity and cleaner user flow.", + "highlightTwo": "Organized interface and easy-to-update content.", + "highlightThree": "Structure prepared for future iteration.", + "primaryCta": "Contact me", + "secondaryCta": "View portfolio", + "logoEyebrow": "Current identity", + "logoText": "The logo is pulled from site settings so the page stays aligned with the current brand setup.", + "galleryAltOne": "Primary placeholder visual for the about page", + "galleryAltTwo": "Secondary placeholder visual for the about page", + "availabilityLabel": "Current status", + "availabilityText": "Available for new projects at the moment, and this line is also a placeholder ready for replacement.", + "statOneValue": "06+", + "statOneLabel": "Approx. years", + "statOneText": "A placeholder metric for experience or years in practice once the real content is ready.", + "statTwoValue": "24", + "statTwoLabel": "Selected projects", + "statTwoText": "A flexible slot for projects, clients, launches, or any stronger proof point later on.", + "statThreeValue": "03", + "statThreeLabel": "Service tracks", + "statThreeText": "This can later become specialties, product lines, or core work streams.", + "valuesEyebrow": "Work style", "valuesTitle": "How I work", + "valuesIntro": "This section is now structured to present core principles quickly instead of relying on one long paragraph.", "valueA": "Strategy first", - "valueAText": "Each project starts with goals, scope, and priorities.", + "valueAText": "I start by clarifying goals, audience, and scope so execution does not drift into guesswork.", "valueB": "Clean systems", - "valueBText": "I rely on maintainable components and clear structure.", + "valueBText": "I build maintainable components and a clear system that can evolve after launch.", "valueC": "Close collaboration", - "valueCText": "Short loops with direct feedback across the full process.", + "valueCText": "I prefer short loops and direct feedback so decisions stay sharp and momentum stays high.", + "processEyebrow": "From start to launch", "processTitle": "My process", + "processIntro": "A compact three-step flow that can later be replaced with your exact process without redesigning the section.", "processOne": "Discovery and goal definition", + "processOneText": "We frame the full picture, align priorities, and define the target before visual or technical work starts.", "processTwo": "Design and prototyping", - "processThree": "Build, test, and launch" + "processTwoText": "I shape the direction, build interface concepts, and establish a visual system that supports implementation.", + "processThree": "Build, test, and launch", + "processThreeText": "Once the direction is locked, the work moves into structured implementation, review, and launch readiness." }, "contactPage": { "title": "Contact",