Refine homepage and restore hero
This commit is contained in:
+189
-103
@@ -1,27 +1,36 @@
|
||||
import type { Metadata } from "next";
|
||||
import {
|
||||
ArrowRight,
|
||||
BadgeCheck,
|
||||
Blocks,
|
||||
BriefcaseBusiness,
|
||||
ChartNoAxesCombined,
|
||||
Globe,
|
||||
Package2,
|
||||
Search,
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { getLocale, getTranslations } from "next-intl/server";
|
||||
|
||||
import { Container } from "@/components/layout/container";
|
||||
import { HomeHero } from "@/components/layout/home-hero";
|
||||
import { StackedMarqueeSection, type MarqueeRow } from "@/components/layout/stacked-marquee-section";
|
||||
import { MotionFade } from "@/components/motion-fade";
|
||||
import { getMarqueeSettings, getSiteSettings, splitMarqueeRowItems } from "@/lib/app-config";
|
||||
import { BentoGridSection } from "@/components/home/bento-grid-section";
|
||||
import { CapabilitiesSection } from "@/components/home/capabilities-section";
|
||||
import { ContactCtaSection } from "@/components/home/contact-cta-section";
|
||||
import { MarqueeSection } from "@/components/home/marquee-section";
|
||||
import { ProcessSection } from "@/components/home/process-section";
|
||||
import { ProjectsSection } from "@/components/home/projects-section";
|
||||
import type {
|
||||
BentoSectionCopy,
|
||||
CapabilitiesSectionCopy,
|
||||
ContactCtaSectionCopy,
|
||||
MarqueeSectionCopy,
|
||||
ProcessSectionCopy,
|
||||
ProjectCardCopy,
|
||||
ProjectsSectionCopy,
|
||||
} from "@/components/home/types";
|
||||
import { buildLocalizedMetadata } from "@/lib/metadata";
|
||||
import { AppCard } from "@/components/ui/app-card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { CardContent } from "@/components/ui/card";
|
||||
import { getLocalizedPath, resolveLocale } from "@/lib/locale";
|
||||
import {
|
||||
getMarqueeSettings,
|
||||
getSiteSettings,
|
||||
splitMarqueeRowItems,
|
||||
} from "@/lib/app-config";
|
||||
import {
|
||||
getLocalizedValue,
|
||||
getPublishedPortfolioProjects,
|
||||
} from "@/lib/portfolio";
|
||||
import { type MarqueeRow } from "@/components/layout/stacked-marquee-section";
|
||||
|
||||
type HomePageProps = {
|
||||
params: Promise<{
|
||||
@@ -29,29 +38,22 @@ type HomePageProps = {
|
||||
}>;
|
||||
};
|
||||
|
||||
const serviceIcons = [
|
||||
BriefcaseBusiness,
|
||||
Globe,
|
||||
Package2,
|
||||
Blocks,
|
||||
Search,
|
||||
ChartNoAxesCombined,
|
||||
BadgeCheck,
|
||||
];
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function generateMetadata({ params }: HomePageProps): Promise<Metadata> {
|
||||
await params;
|
||||
const siteSettings = await getSiteSettings();
|
||||
const localeKey = resolveLocale(await getLocale().catch(() => siteSettings.defaultLocale), siteSettings.defaultLocale);
|
||||
const localeKey = resolveLocale(
|
||||
await getLocale().catch(() => siteSettings.defaultLocale),
|
||||
siteSettings.defaultLocale,
|
||||
);
|
||||
const t = await getTranslations({ locale: localeKey, namespace: "homepage" });
|
||||
|
||||
return await buildLocalizedMetadata({
|
||||
locale: localeKey,
|
||||
pathname: "/",
|
||||
title: siteSettings.locales[localeKey].siteName,
|
||||
description: t("heroText"),
|
||||
title: t("meta.title"),
|
||||
description: t("meta.description"),
|
||||
applyTitleTemplate: false,
|
||||
});
|
||||
}
|
||||
@@ -59,100 +61,184 @@ export async function generateMetadata({ params }: HomePageProps): Promise<Metad
|
||||
export default async function HomePage({ params }: HomePageProps) {
|
||||
await params;
|
||||
const siteSettings = await getSiteSettings();
|
||||
const localeKey = resolveLocale(await getLocale().catch(() => siteSettings.defaultLocale), siteSettings.defaultLocale);
|
||||
const [t, marqueeSettings] = await Promise.all([
|
||||
const localeKey = resolveLocale(
|
||||
await getLocale().catch(() => siteSettings.defaultLocale),
|
||||
siteSettings.defaultLocale,
|
||||
);
|
||||
|
||||
const [t, marqueeSettings, publishedProjects] = await Promise.all([
|
||||
getTranslations({ locale: localeKey, namespace: "homepage" }),
|
||||
getMarqueeSettings(),
|
||||
getPublishedPortfolioProjects(),
|
||||
]);
|
||||
const serviceItems = [
|
||||
t("serviceBrand"),
|
||||
t("serviceWebsite"),
|
||||
t("serviceStore"),
|
||||
t("serviceWordPress"),
|
||||
t("serviceSeo"),
|
||||
t("serviceTracking"),
|
||||
t("serviceSystem"),
|
||||
];
|
||||
|
||||
const bentoCopy: BentoSectionCopy = {
|
||||
eyebrow: t("bento.eyebrow"),
|
||||
title: t("bento.title"),
|
||||
description: t("bento.description"),
|
||||
about: {
|
||||
eyebrow: t("bento.about.eyebrow"),
|
||||
title: t("bento.about.title"),
|
||||
description: t("bento.about.description"),
|
||||
points: t.raw("bento.about.points") as string[],
|
||||
},
|
||||
coreStack: {
|
||||
eyebrow: t("bento.coreStack.eyebrow"),
|
||||
title: t("bento.coreStack.title"),
|
||||
items: t.raw("bento.coreStack.items") as string[],
|
||||
},
|
||||
availability: {
|
||||
eyebrow: t("bento.availability.eyebrow"),
|
||||
title: t("bento.availability.title"),
|
||||
description: t("bento.availability.description"),
|
||||
status: t("bento.availability.status"),
|
||||
},
|
||||
currentFocus: {
|
||||
eyebrow: t("bento.currentFocus.eyebrow"),
|
||||
title: t("bento.currentFocus.title"),
|
||||
items: t.raw("bento.currentFocus.items") as string[],
|
||||
},
|
||||
contact: {
|
||||
eyebrow: t("bento.contact.eyebrow"),
|
||||
title: t("bento.contact.title"),
|
||||
description: t("bento.contact.description"),
|
||||
emailLabel: t("bento.contact.emailLabel"),
|
||||
emailValue: t("bento.contact.emailValue"),
|
||||
},
|
||||
highlights: {
|
||||
eyebrow: t("bento.highlights.eyebrow"),
|
||||
title: t("bento.highlights.title"),
|
||||
items: t.raw("bento.highlights.items") as Array<{ value: string; label: string }>,
|
||||
},
|
||||
};
|
||||
|
||||
const marqueeCopy: MarqueeSectionCopy = {
|
||||
eyebrow: t("marquee.eyebrow"),
|
||||
title: t("marquee.title"),
|
||||
description: t("marquee.description"),
|
||||
};
|
||||
|
||||
const projectsCopy: ProjectsSectionCopy = {
|
||||
eyebrow: t("projects.eyebrow"),
|
||||
title: t("projects.title"),
|
||||
description: t("projects.description"),
|
||||
stackLabel: t("projects.stackLabel"),
|
||||
fallbackItems: t.raw("projects.fallbackItems") as ProjectsSectionCopy["fallbackItems"],
|
||||
};
|
||||
|
||||
const capabilitiesCopy: CapabilitiesSectionCopy = {
|
||||
eyebrow: t("capabilities.eyebrow"),
|
||||
title: t("capabilities.title"),
|
||||
description: t("capabilities.description"),
|
||||
items: t.raw("capabilities.items") as CapabilitiesSectionCopy["items"],
|
||||
};
|
||||
|
||||
const processCopy: ProcessSectionCopy = {
|
||||
eyebrow: t("process.eyebrow"),
|
||||
title: t("process.title"),
|
||||
description: t("process.description"),
|
||||
steps: t.raw("process.steps") as ProcessSectionCopy["steps"],
|
||||
};
|
||||
|
||||
const contactCtaCopy: ContactCtaSectionCopy = {
|
||||
eyebrow: t("contactCta.eyebrow"),
|
||||
title: t("contactCta.title"),
|
||||
description: t("contactCta.description"),
|
||||
contactCta: t("contactCta.contactCta"),
|
||||
githubCta: t("contactCta.githubCta"),
|
||||
emailLabel: t("contactCta.emailLabel"),
|
||||
emailValue: t("contactCta.emailValue"),
|
||||
availabilityLabel: t("contactCta.availabilityLabel"),
|
||||
availabilityValue: t("contactCta.availabilityValue"),
|
||||
githubHref: t("contactCta.githubHref"),
|
||||
};
|
||||
|
||||
const marqueeLocale = marqueeSettings.locales[localeKey];
|
||||
const marqueeRows: MarqueeRow[] = [
|
||||
const marqueeRows = [
|
||||
{ direction: "left", duration: 126, items: splitMarqueeRowItems(marqueeLocale.row1) },
|
||||
{ direction: "right", duration: 220, items: splitMarqueeRowItems(marqueeLocale.row2) },
|
||||
{ direction: "left", duration: 168, items: splitMarqueeRowItems(marqueeLocale.row3) },
|
||||
{ direction: "right", duration: 144, items: splitMarqueeRowItems(marqueeLocale.row4) },
|
||||
];
|
||||
] satisfies MarqueeRow[];
|
||||
|
||||
const portfolioHref = getLocalizedPath(localeKey, "/portfolio", siteSettings.defaultLocale);
|
||||
const contactHref = getLocalizedPath(localeKey, "/contact", siteSettings.defaultLocale);
|
||||
const stackSets = t.raw("projects.stackSets") as string[][];
|
||||
|
||||
const selectedProjects = [
|
||||
...publishedProjects.filter((project) => project.isFeatured),
|
||||
...publishedProjects.filter((project) => !project.isFeatured),
|
||||
].slice(0, 3);
|
||||
|
||||
const projectCards: ProjectCardCopy[] = selectedProjects.map((project, index) => ({
|
||||
title: getLocalizedValue(project.title, localeKey),
|
||||
summary: getLocalizedValue(project.summary, localeKey),
|
||||
stack: stackSets[index] ?? stackSets[stackSets.length - 1] ?? [],
|
||||
href: getLocalizedPath(
|
||||
localeKey,
|
||||
`/portfolio/${project.slug}`,
|
||||
siteSettings.defaultLocale,
|
||||
),
|
||||
cta: t("projects.cta"),
|
||||
meta: [
|
||||
getLocalizedValue(project.category.name, localeKey),
|
||||
getLocalizedValue(project.serviceLabel, localeKey),
|
||||
String(project.projectYear),
|
||||
],
|
||||
featured: index === 0,
|
||||
}));
|
||||
|
||||
const fallbackProjectCards: ProjectCardCopy[] = projectsCopy.fallbackItems.map((item) => ({
|
||||
title: item.title,
|
||||
summary: item.summary,
|
||||
stack: item.stack,
|
||||
href: item.href === "/portfolio" ? portfolioHref : item.href === "/contact" ? contactHref : item.href,
|
||||
cta: t("projects.cta"),
|
||||
meta: [t("projects.fallbackMeta")],
|
||||
}));
|
||||
|
||||
const mergedProjectCards = [
|
||||
...projectCards,
|
||||
...fallbackProjectCards.slice(0, Math.max(0, 3 - projectCards.length)),
|
||||
].slice(0, 3);
|
||||
|
||||
return (
|
||||
<>
|
||||
<HomeHero
|
||||
locale={localeKey}
|
||||
kicker={t("heroKicker")}
|
||||
title={t("heroTitle")}
|
||||
description={t("heroText")}
|
||||
kicker={t("heroVisual.kicker")}
|
||||
title={t("heroVisual.title")}
|
||||
description={t("heroVisual.description")}
|
||||
/>
|
||||
|
||||
<Container id="home-content" className="relative z-10 -mt-6 flex scroll-mt-28 flex-col gap-section pb-12 lg:-mt-8 lg:pb-16">
|
||||
<MotionFade delay={0.05}>
|
||||
<AppCard className="overflow-hidden">
|
||||
<CardContent className="p-6 lg:p-10">
|
||||
<div className="grid gap-8 lg:grid-cols-[minmax(0,1.05fr)_minmax(0,1fr)] lg:items-start">
|
||||
<div>
|
||||
<p className="text-sm font-medium tracking-[0.08em] text-foreground/56">
|
||||
{t("servicesEyebrow")}
|
||||
</p>
|
||||
<h2 className="mt-4 max-w-[12ch] text-balance text-4xl font-semibold leading-[0.92] tracking-[-0.06em] text-foreground sm:text-5xl lg:text-6xl">
|
||||
{t("servicesTitle")}
|
||||
</h2>
|
||||
<p className="mt-5 max-w-[34rem] text-sm leading-7 text-muted-foreground sm:text-base">
|
||||
{t("servicesText")}
|
||||
</p>
|
||||
<div className="mt-8 flex flex-wrap gap-3">
|
||||
<Button asChild size="lg" className="min-w-[11rem]">
|
||||
<Link href={getLocalizedPath(localeKey, "/contact", siteSettings.defaultLocale)}>
|
||||
{t("servicesPrimaryCta")}
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
<Button
|
||||
asChild
|
||||
size="lg"
|
||||
variant="outline"
|
||||
className="min-w-[11rem] border-border/80 bg-background/70 hover:bg-background"
|
||||
<Container
|
||||
id="home-content"
|
||||
className="relative z-10 flex max-w-7xl flex-col gap-16 pb-14 pt-6 sm:gap-20 sm:pb-16 sm:pt-8 lg:gap-24 lg:pb-20 lg:pt-10"
|
||||
>
|
||||
<Link href={getLocalizedPath(localeKey, "/portfolio", siteSettings.defaultLocale)}>
|
||||
{t("servicesSecondaryCta")}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<MotionFade>
|
||||
<BentoGridSection copy={bentoCopy} />
|
||||
</MotionFade>
|
||||
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
{serviceItems.map((item, index) => {
|
||||
const Icon = serviceIcons[index];
|
||||
<MotionFade delay={0.06}>
|
||||
<MarqueeSection copy={marqueeCopy} rows={marqueeRows} />
|
||||
</MotionFade>
|
||||
|
||||
return (
|
||||
<AppCard
|
||||
key={item}
|
||||
layer="single"
|
||||
interactive
|
||||
className="h-full"
|
||||
>
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-[linear-gradient(135deg,hsl(var(--hero-left)/0.42),hsl(var(--hero-right)/0.26))]">
|
||||
<Icon className="h-5 w-5 text-foreground/78" />
|
||||
</div>
|
||||
<p className="mt-4 text-sm font-medium leading-6 text-foreground/88 sm:text-base">
|
||||
{item}
|
||||
</p>
|
||||
</AppCard>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
<MotionFade delay={0.08}>
|
||||
<ProjectsSection copy={projectsCopy} items={mergedProjectCards} />
|
||||
</MotionFade>
|
||||
|
||||
<MotionFade delay={0.1}>
|
||||
<CapabilitiesSection copy={capabilitiesCopy} />
|
||||
</MotionFade>
|
||||
|
||||
<MotionFade delay={0.12}>
|
||||
<ProcessSection copy={processCopy} />
|
||||
</MotionFade>
|
||||
|
||||
<MotionFade delay={0.14}>
|
||||
<ContactCtaSection copy={contactCtaCopy} contactHref={contactHref} />
|
||||
</MotionFade>
|
||||
</Container>
|
||||
|
||||
<StackedMarqueeSection rows={marqueeRows} className="relative z-10 mt-2 pb-10 sm:pb-12 lg:pb-14" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import { AppCard } from "@/components/ui/app-card";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type BentoCardProps = {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
icon?: ReactNode;
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
export function BentoCard({
|
||||
eyebrow,
|
||||
title,
|
||||
description,
|
||||
icon,
|
||||
className,
|
||||
children,
|
||||
}: BentoCardProps) {
|
||||
return (
|
||||
<AppCard
|
||||
layer="single"
|
||||
interactive
|
||||
className={cn(
|
||||
"group h-full border-border/80 bg-surface-2 transition-all duration-300 hover:-translate-y-1 hover:border-border-strong hover:bg-background/80 hover:shadow-lg",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="flex h-full flex-col gap-6 p-6">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="space-y-3">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground">
|
||||
{eyebrow}
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-2xl font-semibold tracking-[-0.05em] text-foreground transition-colors duration-300 group-hover:text-foreground/95">
|
||||
{title}
|
||||
</h3>
|
||||
{description ? (
|
||||
<p className="text-sm leading-7 text-muted-foreground">{description}</p>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{icon ? (
|
||||
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-nested border border-border/70 bg-background text-foreground transition-all duration-300 group-hover:-translate-y-0.5 group-hover:border-border-strong group-hover:shadow-sm">
|
||||
{icon}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
</AppCard>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
import {
|
||||
Activity,
|
||||
BriefcaseBusiness,
|
||||
Layers3,
|
||||
Mail,
|
||||
Sparkles,
|
||||
TimerReset,
|
||||
} from "lucide-react";
|
||||
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { SectionHeading } from "./section-heading";
|
||||
import { BentoCard } from "./bento-card";
|
||||
import type { BentoSectionCopy } from "./types";
|
||||
|
||||
type BentoGridSectionProps = {
|
||||
copy: BentoSectionCopy;
|
||||
};
|
||||
|
||||
export function BentoGridSection({ copy }: BentoGridSectionProps) {
|
||||
return (
|
||||
<section className="space-y-8">
|
||||
<SectionHeading
|
||||
eyebrow={copy.eyebrow}
|
||||
title={copy.title}
|
||||
description={copy.description}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4 xl:gap-6">
|
||||
<BentoCard
|
||||
eyebrow={copy.about.eyebrow}
|
||||
title={copy.about.title}
|
||||
description={copy.about.description}
|
||||
icon={<BriefcaseBusiness className="h-5 w-5" />}
|
||||
className="xl:col-span-2 xl:row-span-2"
|
||||
>
|
||||
<ul className="grid gap-3 sm:grid-cols-2">
|
||||
{copy.about.points.map((item) => (
|
||||
<li
|
||||
key={item}
|
||||
className="rounded-nested border border-border/70 bg-background px-4 py-3 text-sm text-foreground/88"
|
||||
>
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</BentoCard>
|
||||
|
||||
<BentoCard
|
||||
eyebrow={copy.coreStack.eyebrow}
|
||||
title={copy.coreStack.title}
|
||||
icon={<Layers3 className="h-5 w-5" />}
|
||||
>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{copy.coreStack.items.map((item) => (
|
||||
<Badge key={item} variant="outline" className="border-border/80 bg-background">
|
||||
{item}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</BentoCard>
|
||||
|
||||
<BentoCard
|
||||
eyebrow={copy.availability.eyebrow}
|
||||
title={copy.availability.title}
|
||||
description={copy.availability.description}
|
||||
icon={<Activity className="h-5 w-5" />}
|
||||
>
|
||||
<Badge variant="success" className="w-fit">
|
||||
{copy.availability.status}
|
||||
</Badge>
|
||||
</BentoCard>
|
||||
|
||||
<BentoCard
|
||||
eyebrow={copy.currentFocus.eyebrow}
|
||||
title={copy.currentFocus.title}
|
||||
icon={<Sparkles className="h-5 w-5" />}
|
||||
>
|
||||
<ul className="space-y-3 text-sm text-foreground/88">
|
||||
{copy.currentFocus.items.map((item) => (
|
||||
<li key={item} className="rounded-nested border border-border/70 bg-background px-4 py-3">
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</BentoCard>
|
||||
|
||||
<BentoCard
|
||||
eyebrow={copy.contact.eyebrow}
|
||||
title={copy.contact.title}
|
||||
description={copy.contact.description}
|
||||
icon={<Mail className="h-5 w-5" />}
|
||||
>
|
||||
<div className="rounded-nested border border-border/70 bg-background px-4 py-4">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground">
|
||||
{copy.contact.emailLabel}
|
||||
</p>
|
||||
<p className="mt-2 text-base font-medium text-foreground">
|
||||
{copy.contact.emailValue}
|
||||
</p>
|
||||
</div>
|
||||
</BentoCard>
|
||||
|
||||
<BentoCard
|
||||
eyebrow={copy.highlights.eyebrow}
|
||||
title={copy.highlights.title}
|
||||
icon={<TimerReset className="h-5 w-5" />}
|
||||
>
|
||||
<div className="grid gap-3 sm:grid-cols-3 xl:grid-cols-1">
|
||||
{copy.highlights.items.map((item) => (
|
||||
<div
|
||||
key={item.label}
|
||||
className="rounded-nested border border-border/70 bg-background px-4 py-4"
|
||||
>
|
||||
<p className="text-2xl font-semibold tracking-[-0.05em] text-foreground">
|
||||
{item.value}
|
||||
</p>
|
||||
<p className="mt-1 text-sm text-muted-foreground">{item.label}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</BentoCard>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { Bot, Database, Globe, ServerCog } from "lucide-react";
|
||||
|
||||
import { AppCard } from "@/components/ui/app-card";
|
||||
import { SectionHeading } from "./section-heading";
|
||||
import type { CapabilitiesSectionCopy } from "./types";
|
||||
|
||||
type CapabilitiesSectionProps = {
|
||||
copy: CapabilitiesSectionCopy;
|
||||
};
|
||||
|
||||
const capabilityIcons = [Globe, Database, ServerCog, Bot];
|
||||
|
||||
export function CapabilitiesSection({ copy }: CapabilitiesSectionProps) {
|
||||
return (
|
||||
<section className="space-y-8">
|
||||
<SectionHeading
|
||||
eyebrow={copy.eyebrow}
|
||||
title={copy.title}
|
||||
description={copy.description}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4 xl:gap-6">
|
||||
{copy.items.map((item, index) => {
|
||||
const Icon = capabilityIcons[index] ?? Globe;
|
||||
|
||||
return (
|
||||
<AppCard
|
||||
key={item.title}
|
||||
layer="single"
|
||||
interactive
|
||||
className="group border-border/80 bg-surface-2 transition-all duration-300 hover:-translate-y-1 hover:border-border-strong hover:shadow-panel"
|
||||
>
|
||||
<div className="flex h-full flex-col gap-5 p-6">
|
||||
<div className="flex h-11 w-11 items-center justify-center rounded-nested border border-border/70 bg-background text-foreground transition-transform duration-300 group-hover:-translate-y-0.5">
|
||||
<Icon className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-xl font-semibold tracking-[-0.04em] text-foreground">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-sm leading-7 text-muted-foreground">{item.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</AppCard>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import { ArrowUpRight, Mail } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { AppCard } from "@/components/ui/app-card";
|
||||
import type { ContactCtaSectionCopy } from "./types";
|
||||
|
||||
type ContactCtaSectionProps = {
|
||||
copy: ContactCtaSectionCopy;
|
||||
contactHref: string;
|
||||
};
|
||||
|
||||
export function ContactCtaSection({
|
||||
copy,
|
||||
contactHref,
|
||||
}: ContactCtaSectionProps) {
|
||||
return (
|
||||
<section>
|
||||
<AppCard className="overflow-hidden">
|
||||
<div className="grid gap-6 rounded-nested border border-border/70 bg-background p-6 sm:p-8 lg:grid-cols-[minmax(0,1fr)_auto] lg:items-end lg:p-10">
|
||||
<div className="space-y-4">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.16em] text-muted-foreground">
|
||||
{copy.eyebrow}
|
||||
</p>
|
||||
<h2 className="max-w-3xl text-balance text-3xl font-semibold tracking-[-0.05em] text-foreground sm:text-4xl lg:text-5xl">
|
||||
{copy.title}
|
||||
</h2>
|
||||
<p className="max-w-2xl text-sm leading-7 text-muted-foreground sm:text-base">
|
||||
{copy.description}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap gap-6 text-sm">
|
||||
<div className="space-y-1">
|
||||
<p className="font-semibold text-foreground">{copy.emailLabel}</p>
|
||||
<a href={`mailto:${copy.emailValue}`} className="text-muted-foreground hover:text-foreground">
|
||||
{copy.emailValue}
|
||||
</a>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<p className="font-semibold text-foreground">{copy.availabilityLabel}</p>
|
||||
<p className="text-muted-foreground">{copy.availabilityValue}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-3 lg:justify-end">
|
||||
<Button asChild size="lg">
|
||||
<Link href={contactHref}>
|
||||
{copy.contactCta}
|
||||
<Mail className="h-4 w-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
<Button asChild size="lg" variant="outline">
|
||||
<a href={copy.githubHref} target="_blank" rel="noreferrer">
|
||||
{copy.githubCta}
|
||||
<ArrowUpRight className="h-4 w-4" />
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</AppCard>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import { ArrowRight, MapPin, Sparkles } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { AppCard } from "@/components/ui/app-card";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { HeroCopy } from "./types";
|
||||
|
||||
type HeroSectionProps = {
|
||||
copy: HeroCopy;
|
||||
contactHref: string;
|
||||
portfolioHref: string;
|
||||
};
|
||||
|
||||
export function HeroSection({
|
||||
copy,
|
||||
contactHref,
|
||||
portfolioHref,
|
||||
}: HeroSectionProps) {
|
||||
return (
|
||||
<section className="relative pt-8 sm:pt-12 lg:pt-16">
|
||||
<div className="grid gap-6 lg:grid-cols-[minmax(0,1.15fr)_minmax(320px,0.85fr)] lg:items-end">
|
||||
<div className="space-y-8">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="w-fit border-border/80 bg-background/80 text-foreground shadow-xs"
|
||||
>
|
||||
<span className="mr-2 h-2 w-2 rounded-full bg-status-success" aria-hidden="true" />
|
||||
{copy.availability}
|
||||
</Badge>
|
||||
|
||||
<div className="space-y-5">
|
||||
<p className="text-sm font-semibold uppercase tracking-[0.18em] text-muted-foreground">
|
||||
{copy.name}
|
||||
</p>
|
||||
<h1 className="max-w-4xl text-balance text-5xl font-semibold tracking-[-0.07em] text-foreground sm:text-6xl lg:text-7xl">
|
||||
{copy.headline}
|
||||
</h1>
|
||||
<p className="max-w-2xl text-base leading-8 text-muted-foreground sm:text-lg">
|
||||
{copy.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Button asChild size="lg" className="shadow-sm">
|
||||
<Link href={contactHref}>
|
||||
{copy.primaryCta}
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
<Button
|
||||
asChild
|
||||
size="lg"
|
||||
variant="outline"
|
||||
className="border-border/80 bg-background/70 hover:border-border-strong hover:bg-background"
|
||||
>
|
||||
<Link href={portfolioHref}>{copy.secondaryCta}</Link>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{copy.highlights.map((item) => (
|
||||
<Badge
|
||||
key={item}
|
||||
variant="secondary"
|
||||
className="bg-secondary/70 text-foreground shadow-none"
|
||||
>
|
||||
{item}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AppCard className="overflow-hidden">
|
||||
<div className="grid gap-3">
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<MetricCard label={copy.roleLabel} value={copy.roleValue} />
|
||||
<MetricCard label={copy.locationLabel} value={copy.locationValue} icon={MapPin} />
|
||||
</div>
|
||||
<MetricCard label={copy.focusLabel} value={copy.focusValue} icon={Sparkles} strong />
|
||||
</div>
|
||||
</AppCard>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
type MetricCardProps = {
|
||||
label: string;
|
||||
value: string;
|
||||
strong?: boolean;
|
||||
icon?: typeof MapPin;
|
||||
};
|
||||
|
||||
function MetricCard({ label, value, strong = false, icon: Icon }: MetricCardProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-nested border border-border/70 bg-background p-5 transition-all duration-300",
|
||||
strong && "bg-surface-2",
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground">
|
||||
{Icon ? <Icon className="h-3.5 w-3.5" /> : null}
|
||||
<span>{label}</span>
|
||||
</div>
|
||||
<p className="mt-3 text-xl font-semibold tracking-[-0.04em] text-foreground">
|
||||
{value}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { StackedMarqueeSection, type MarqueeRow } from "@/components/layout/stacked-marquee-section";
|
||||
import { AppCard } from "@/components/ui/app-card";
|
||||
import { SectionHeading } from "./section-heading";
|
||||
import type { MarqueeSectionCopy } from "./types";
|
||||
|
||||
type MarqueeSectionProps = {
|
||||
copy: MarqueeSectionCopy;
|
||||
rows: MarqueeRow[];
|
||||
};
|
||||
|
||||
export function MarqueeSection({ copy, rows }: MarqueeSectionProps) {
|
||||
return (
|
||||
<section className="space-y-8 overflow-hidden">
|
||||
<SectionHeading
|
||||
eyebrow={copy.eyebrow}
|
||||
title={copy.title}
|
||||
description={copy.description}
|
||||
/>
|
||||
|
||||
<AppCard className="overflow-hidden">
|
||||
<div className="rounded-nested border border-border/70 bg-background py-6">
|
||||
<StackedMarqueeSection rows={rows} className="py-0" />
|
||||
</div>
|
||||
</AppCard>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { AppCard } from "@/components/ui/app-card";
|
||||
import { SectionHeading } from "./section-heading";
|
||||
import type { ProcessSectionCopy } from "./types";
|
||||
|
||||
type ProcessSectionProps = {
|
||||
copy: ProcessSectionCopy;
|
||||
};
|
||||
|
||||
export function ProcessSection({ copy }: ProcessSectionProps) {
|
||||
return (
|
||||
<section className="space-y-8">
|
||||
<SectionHeading
|
||||
eyebrow={copy.eyebrow}
|
||||
title={copy.title}
|
||||
description={copy.description}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4 xl:gap-6">
|
||||
{copy.steps.map((step, index) => (
|
||||
<AppCard
|
||||
key={step.title}
|
||||
layer="single"
|
||||
interactive
|
||||
className="group border-border/80 bg-surface-2 transition-all duration-300 hover:-translate-y-1 hover:border-border-strong hover:shadow-panel"
|
||||
>
|
||||
<div className="flex h-full flex-col gap-5 p-6">
|
||||
<p className="text-sm font-semibold tracking-[0.18em] text-muted-foreground">
|
||||
{String(index + 1).padStart(2, "0")}
|
||||
</p>
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-2xl font-semibold tracking-[-0.05em] text-foreground">
|
||||
{step.title}
|
||||
</h3>
|
||||
<p className="text-sm leading-7 text-muted-foreground">{step.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</AppCard>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { ArrowUpRight } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { AppCard } from "@/components/ui/app-card";
|
||||
import { SectionHeading } from "./section-heading";
|
||||
import type { ProjectCardCopy, ProjectsSectionCopy } from "./types";
|
||||
|
||||
type ProjectsSectionProps = {
|
||||
copy: ProjectsSectionCopy;
|
||||
items: ProjectCardCopy[];
|
||||
};
|
||||
|
||||
export function ProjectsSection({ copy, items }: ProjectsSectionProps) {
|
||||
return (
|
||||
<section className="space-y-10">
|
||||
<SectionHeading
|
||||
eyebrow={copy.eyebrow}
|
||||
title={copy.title}
|
||||
description={copy.description}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 lg:grid-cols-3 xl:gap-6">
|
||||
{items.map((item, index) => (
|
||||
<AppCard
|
||||
key={`${item.title}-${index}`}
|
||||
layer="single"
|
||||
interactive
|
||||
className={[
|
||||
"group h-full border-border/80 bg-surface-2 transition-all duration-300 hover:-translate-y-1 hover:border-border-strong hover:bg-background/80 hover:shadow-lg",
|
||||
index === 0 ? "lg:col-span-2" : "",
|
||||
].join(" ")}
|
||||
>
|
||||
<div className="flex h-full flex-col gap-6 p-6">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{item.meta.map((metaItem) => (
|
||||
<Badge
|
||||
key={metaItem}
|
||||
variant="outline"
|
||||
className="border-border/80 bg-background text-muted-foreground transition-colors duration-300 group-hover:border-border-strong group-hover:text-foreground/80"
|
||||
>
|
||||
{metaItem}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-2xl font-semibold tracking-[-0.05em] text-foreground transition-colors duration-300 group-hover:text-foreground/95">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-sm leading-7 text-muted-foreground">{item.summary}</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground">
|
||||
{copy.stackLabel}
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{item.stack.map((stackItem) => (
|
||||
<Badge
|
||||
key={stackItem}
|
||||
variant="secondary"
|
||||
className="bg-secondary/70 text-foreground transition-colors duration-300 group-hover:bg-secondary"
|
||||
>
|
||||
{stackItem}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-auto">
|
||||
<Link
|
||||
href={item.href}
|
||||
className="inline-flex items-center gap-2 text-sm font-medium text-foreground transition-all duration-200 group-hover:translate-x-0.5"
|
||||
>
|
||||
{item.cta}
|
||||
<ArrowUpRight className="h-4 w-4 transition-transform duration-200 group-hover:-translate-y-0.5 group-hover:translate-x-0.5" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</AppCard>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type SectionHeadingProps = {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
description: string;
|
||||
align?: "left" | "center";
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function SectionHeading({
|
||||
eyebrow,
|
||||
title,
|
||||
description,
|
||||
align = "left",
|
||||
className,
|
||||
}: SectionHeadingProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"space-y-4",
|
||||
align === "center" && "mx-auto max-w-3xl text-center",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.16em] text-muted-foreground">
|
||||
{eyebrow}
|
||||
</p>
|
||||
<h2 className="max-w-4xl text-balance text-3xl font-semibold tracking-[-0.05em] text-foreground sm:text-4xl lg:text-5xl">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="max-w-3xl text-sm leading-7 text-muted-foreground sm:text-[1.02rem]">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
export type HeroCopy = {
|
||||
availability: string;
|
||||
name: string;
|
||||
headline: string;
|
||||
description: string;
|
||||
primaryCta: string;
|
||||
secondaryCta: string;
|
||||
locationLabel: string;
|
||||
locationValue: string;
|
||||
roleLabel: string;
|
||||
roleValue: string;
|
||||
focusLabel: string;
|
||||
focusValue: string;
|
||||
highlights: string[];
|
||||
};
|
||||
|
||||
export type BentoSectionCopy = {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
description: string;
|
||||
about: {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
description: string;
|
||||
points: string[];
|
||||
};
|
||||
coreStack: {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
items: string[];
|
||||
};
|
||||
availability: {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
description: string;
|
||||
status: string;
|
||||
};
|
||||
currentFocus: {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
items: string[];
|
||||
};
|
||||
contact: {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
description: string;
|
||||
emailLabel: string;
|
||||
emailValue: string;
|
||||
};
|
||||
highlights: {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
items: Array<{
|
||||
value: string;
|
||||
label: string;
|
||||
}>;
|
||||
};
|
||||
};
|
||||
|
||||
export type MarqueeSectionCopy = {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
export type ProjectCardCopy = {
|
||||
title: string;
|
||||
summary: string;
|
||||
stack: string[];
|
||||
href: string;
|
||||
cta: string;
|
||||
meta: string[];
|
||||
featured?: boolean;
|
||||
};
|
||||
|
||||
export type ProjectsSectionCopy = {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
description: string;
|
||||
stackLabel: string;
|
||||
fallbackItems: Array<{
|
||||
title: string;
|
||||
summary: string;
|
||||
stack: string[];
|
||||
href: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
export type CapabilitiesSectionCopy = {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
description: string;
|
||||
items: Array<{
|
||||
title: string;
|
||||
description: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
export type ProcessSectionCopy = {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
description: string;
|
||||
steps: Array<{
|
||||
title: string;
|
||||
description: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
export type ContactCtaSectionCopy = {
|
||||
eyebrow: string;
|
||||
title: string;
|
||||
description: string;
|
||||
contactCta: string;
|
||||
githubCta: string;
|
||||
emailLabel: string;
|
||||
emailValue: string;
|
||||
availabilityLabel: string;
|
||||
availabilityValue: string;
|
||||
githubHref: string;
|
||||
};
|
||||
+218
-17
@@ -36,23 +36,224 @@
|
||||
"secondaryCta": "العودة للرئيسية"
|
||||
},
|
||||
"homepage": {
|
||||
"heroKicker": "مرحبااا، أنا محمد",
|
||||
"heroTitle": "مطوّر واجهات ويب\nومصمّم جرافيك",
|
||||
"heroText": "بشتغل على حلول رقمية لمشاكل حقيقية\nبتطلع ممتازة وبتشتغل بكفاءة.",
|
||||
"servicesEyebrow": "شو فيني قدّم",
|
||||
"servicesTitle": "حلول رقمية مصممة حسب شغلك، مو قوالب جاهزة.",
|
||||
"servicesText": "من الهوية والموقع، للمتجر والسيو والتراكنغ، لحد الأنظمة الخاصة الداخلية. الفكرة إنو كل شيء يطلع مرتب، مفهوم، وقابل للنمو.",
|
||||
"servicesPrimaryCta": "احكيلي عن مشروعك",
|
||||
"servicesSecondaryCta": "شوف شغلي",
|
||||
"serviceBrand": "بناء براند واضح ومرتب.",
|
||||
"serviceWebsite": "موقع إلكتروني يليق بشغلك.",
|
||||
"serviceStore": "متجر أسهل بالعرض والبيع.",
|
||||
"serviceWordPress": "إضافة WordPress خاصة فيك.",
|
||||
"serviceSeo": "تحسين الظهور بمحركات البحث.",
|
||||
"serviceTracking": "تراكنغ واضح وفهم لحركة الزوار.",
|
||||
"serviceSystem": "سستم خاص لشركتك أو لفريقك.",
|
||||
"toPortfolio": "عرض الأعمال",
|
||||
"toContact": "تواصل معي"
|
||||
"meta": {
|
||||
"title": "معرض أعمال مطور Full-stack",
|
||||
"description": "منتجات رقمية حديثة وأنظمة قابلة للتوسع وتجارب متعددة اللغات مبنية بعقلية منتج واضحة."
|
||||
},
|
||||
"heroVisual": {
|
||||
"kicker": "مرحبا، أنا محمد",
|
||||
"title": "مطوّر واجهات ويب\nومصمّم جرافيك",
|
||||
"description": "أبني حلول رقمية لمشاكل حقيقية\nتبدو قوية وتعمل بكفاءة."
|
||||
},
|
||||
"hero": {
|
||||
"availability": "متاح لمشاريع مختارة",
|
||||
"name": "محمد فراواتي",
|
||||
"headline": "منتجات Full-stack تجمع بين تنفيذ Frontend قوي وأنظمة Backend موثوقة.",
|
||||
"description": "أصمم وأبني تجارب ويب بمستوى عالي لفرق تحتاج وضوح وسرعة وتنفيذ جاهز للإنتاج من أول الفكرة حتى الإطلاق.",
|
||||
"primaryCta": "ابدأ مشروعك",
|
||||
"secondaryCta": "شوف الأعمال المختارة",
|
||||
"locationLabel": "الموقع",
|
||||
"locationValue": "برلين، ألمانيا",
|
||||
"roleLabel": "الدور",
|
||||
"roleValue": "مطور Full-stack",
|
||||
"focusLabel": "التركيز الحالي",
|
||||
"focusValue": "منتجات عالية الأداء وأتمتة وعمل منصات قابلة للصيانة.",
|
||||
"highlights": [
|
||||
"تسليم بثلاث لغات",
|
||||
"تنفيذ بعقلية منتج",
|
||||
"شريك تقني لفرق جاهزة للإطلاق"
|
||||
]
|
||||
},
|
||||
"bento": {
|
||||
"eyebrow": "لمحات",
|
||||
"title": "مبني لفرق تهتم بالجودة والسرعة والقرارات التقنية النظيفة.",
|
||||
"description": "تنظيم الصفحة يعكس أسلوب شغلي: وضوح أولاً، تنفيذ قوي ثانياً، ومن دون تعقيد زائد بالنص.",
|
||||
"about": {
|
||||
"eyebrow": "عني",
|
||||
"title": "أشتغل بين المنتج والهندسة والتنفيذ.",
|
||||
"description": "شغلي يجمع بين التفكير المنتج والتنفيذ العملي، حتى يحصل المؤسسون والوكالات والفرق التقنية على مطور قادر ينقل المشروع من المعمارية للتفاصيل النهائية بدون ما يضيع المستوى.",
|
||||
"points": [
|
||||
"أنظمة Frontend مع انضباط UX واضح",
|
||||
"خدمات Backend وربط أنظمة لسير عمل حقيقي",
|
||||
"تواصل واضح مع أصحاب القرار التقني وغير التقني",
|
||||
"تسليم مع قابلية صيانة محسوبة من أول يوم"
|
||||
]
|
||||
},
|
||||
"coreStack": {
|
||||
"eyebrow": "Core stack",
|
||||
"title": "أدواتي اليومية",
|
||||
"items": [
|
||||
"Next.js",
|
||||
"TypeScript",
|
||||
"React",
|
||||
"Node.js",
|
||||
"PostgreSQL",
|
||||
"Tailwind CSS",
|
||||
"Prisma",
|
||||
"Docker"
|
||||
]
|
||||
},
|
||||
"availability": {
|
||||
"eyebrow": "التوفر",
|
||||
"title": "جاهز لتعاونات مركزة",
|
||||
"description": "أفضل مناسبة لستارت أب ووكالات وفرق تريد شخصاً تقنياً واحداً يدير التنفيذ من البداية للنهاية.",
|
||||
"status": "فرص جديدة متاحة"
|
||||
},
|
||||
"currentFocus": {
|
||||
"eyebrow": "التركيز الحالي",
|
||||
"title": "أكثر شيء أعمل عليه حالياً",
|
||||
"items": [
|
||||
"مواقع تسويقية بمستوى UX قريب من المنتجات",
|
||||
"لوحات تحكم وأدوات داخلية للإدارة",
|
||||
"مسارات أتمتة للمحتوى والعمليات"
|
||||
]
|
||||
},
|
||||
"contact": {
|
||||
"eyebrow": "تواصل سريع",
|
||||
"title": "أقصر طريق للوصول إلي",
|
||||
"description": "إذا كان الطلب واضحاً من الآن، أرسل السياق والمدة الزمنية وأرجع لك بالخطوة الصحيحة مباشرة.",
|
||||
"emailLabel": "البريد الإلكتروني",
|
||||
"emailValue": "hello@moh-sass.dev"
|
||||
},
|
||||
"highlights": {
|
||||
"eyebrow": "لمحات",
|
||||
"title": "مبادئ العمل",
|
||||
"items": [
|
||||
{
|
||||
"value": "3",
|
||||
"label": "لغات مدعومة"
|
||||
},
|
||||
{
|
||||
"value": "E2E",
|
||||
"label": "ملكية تسليم كاملة"
|
||||
},
|
||||
{
|
||||
"value": "24/7",
|
||||
"label": "عقلية إنتاج"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"marquee": {
|
||||
"eyebrow": "الأدوات المعتمدة",
|
||||
"title": "Stack عملي مختار على أساس الصيانة والسرعة والقيمة الطويلة المدى.",
|
||||
"description": "قسم Marquee يستخدم إعدادات الموقع الموجودة مسبقاً حتى تبقى الصفحة متناسقة مع الأدوات والقدرات التي تريد إبرازها."
|
||||
},
|
||||
"projects": {
|
||||
"eyebrow": "مشاريع مختارة",
|
||||
"title": "ثلاثة أمثلة على نوع الشغل الذي أحب أن أطلقه.",
|
||||
"description": "الأعمال المختارة تجمع بين جودة واجهة قوية وبنية تقنية تبقى ثابتة بعد الإطلاق.",
|
||||
"stackLabel": "Stack",
|
||||
"cta": "فتح المشروع",
|
||||
"fallbackMeta": "عمل مختار",
|
||||
"stackSets": [
|
||||
[
|
||||
"Next.js",
|
||||
"TypeScript",
|
||||
"Design Systems"
|
||||
],
|
||||
[
|
||||
"APIs",
|
||||
"PostgreSQL",
|
||||
"Automation"
|
||||
],
|
||||
[
|
||||
"Tailwind CSS",
|
||||
"Prisma",
|
||||
"Infrastructure"
|
||||
]
|
||||
],
|
||||
"fallbackItems": [
|
||||
{
|
||||
"title": "إعادة بناء منصة منتج",
|
||||
"summary": "تحديث عملي لمنصة خدمات مع UX أوضح ومعمارية أقوى ودورات تسليم أسرع.",
|
||||
"stack": [
|
||||
"Next.js",
|
||||
"TypeScript",
|
||||
"PostgreSQL"
|
||||
],
|
||||
"href": "/portfolio"
|
||||
},
|
||||
{
|
||||
"title": "لوحة عمليات داخلية",
|
||||
"summary": "أداة داخلية تركز على تسريع القرار وتنظيف سير العمل وتقليل التنسيق اليدوي بين الفرق.",
|
||||
"stack": [
|
||||
"React",
|
||||
"Node.js",
|
||||
"Automation"
|
||||
],
|
||||
"href": "/portfolio"
|
||||
},
|
||||
{
|
||||
"title": "نظام مواقع للإطلاقات",
|
||||
"summary": "أساس قابل لإعادة الاستخدام لإطلاق مواقع بسرعة بدون التنازل عن الجودة التقنية أو التحكم بالمحتوى.",
|
||||
"stack": [
|
||||
"Tailwind CSS",
|
||||
"CMS",
|
||||
"Performance"
|
||||
],
|
||||
"href": "/contact"
|
||||
}
|
||||
]
|
||||
},
|
||||
"capabilities": {
|
||||
"eyebrow": "القدرات",
|
||||
"title": "تغطية تقنية من الطرف للطرف بدون خسارة تركيز المنتج.",
|
||||
"description": "كل مجال هنا مبني على شغل إنتاجي فعلي، وليس مجرد تسميات خدمات عامة.",
|
||||
"items": [
|
||||
{
|
||||
"title": "Frontend Engineering",
|
||||
"description": "واجهات Responsive وأنظمة تصميم وتفاصيل تفاعل ومعمارية Components قابلة للتوسع."
|
||||
},
|
||||
{
|
||||
"title": "Backend Systems",
|
||||
"description": "APIs منظمة وربط أنظمة ونمذجة بيانات ومنطق تطبيقي مصمم لتسليم يمكن الاعتماد عليه."
|
||||
},
|
||||
{
|
||||
"title": "DevOps & Infrastructure",
|
||||
"description": "نشر وبيئات وتشغيل ومراقبة أساسية وقرارات Infrastructure تقلل العبء التشغيلي."
|
||||
},
|
||||
{
|
||||
"title": "Automation & AI Workflows",
|
||||
"description": "طبقات أتمتة عملية تقلل العمل اليدوي وتربط بين المنتج والمحتوى والعمليات."
|
||||
}
|
||||
]
|
||||
},
|
||||
"process": {
|
||||
"eyebrow": "آلية العمل",
|
||||
"title": "مسار مختصر يبقي المشروع متقدماً.",
|
||||
"description": "العملية مقصودة أن تكون بسيطة: نقلل الغموض مبكراً، نثبت القرارات التقنية، ثم نطلق بانضباط.",
|
||||
"steps": [
|
||||
{
|
||||
"title": "Discover",
|
||||
"description": "نحدد الأهداف والقيود والجمهور والنتيجة الحقيقية المطلوبة من المنتج."
|
||||
},
|
||||
{
|
||||
"title": "Architect",
|
||||
"description": "نرسم شكل النظام وحدوده التقنية وبنية المحتوى ومسار التنفيذ قبل الدخول في البناء الثقيل."
|
||||
},
|
||||
{
|
||||
"title": "Build",
|
||||
"description": "ننّفذ مع عناية قوية بالواجهة ومنطق تطبيق نظيف وبنية قابلة للصيانة عبر كامل الـ stack."
|
||||
},
|
||||
{
|
||||
"title": "Ship",
|
||||
"description": "نطلق بثقة ونتأكد من النتيجة ونترك المنتج بحالة مستقرة للمرحلة التالية."
|
||||
}
|
||||
]
|
||||
},
|
||||
"contactCta": {
|
||||
"eyebrow": "ابدأ الحديث",
|
||||
"title": "إذا المشروع يحتاج تنفيذ قوي، أقدر أدفعه للأمام.",
|
||||
"description": "هات الملخص أو الاختناق الحالي أو الاتجاه العام. أقدر أساعد في ضبط النطاق وتصميم النظام وبناء المنتج.",
|
||||
"contactCta": "تواصل معي",
|
||||
"githubCta": "GitHub",
|
||||
"emailLabel": "البريد الإلكتروني",
|
||||
"emailValue": "hello@moh-sass.dev",
|
||||
"availabilityLabel": "التوفر",
|
||||
"availabilityValue": "مفتوح لشغل منتجات مركز",
|
||||
"githubHref": "https://github.com/mohfarawati"
|
||||
}
|
||||
},
|
||||
"portfolioPage": {
|
||||
"title": "الأعمال",
|
||||
|
||||
+218
-17
@@ -36,23 +36,224 @@
|
||||
"secondaryCta": "Zur Startseite"
|
||||
},
|
||||
"homepage": {
|
||||
"heroKicker": "Hi, ich bin Moh",
|
||||
"heroTitle": "frontend\ndeveloper\n& designer",
|
||||
"heroText": "Ich entwickle digitale Lösungen für echte Probleme,\ndie gut aussehen und sauber funktionieren.",
|
||||
"servicesEyebrow": "Leistungen",
|
||||
"servicesTitle": "Digitale Arbeit, die sich an deinem Geschäft orientiert und nicht an Vorlagen.",
|
||||
"servicesText": "Von Markenaufbau und Websites bis zu Shops, SEO, Tracking, WordPress Plugins und internen Systemen geht es immer um dasselbe: mehr Klarheit, bessere Wirkung und eine saubere Grundlage für Wachstum.",
|
||||
"servicesPrimaryCta": "Erzähl mir von deinem Projekt",
|
||||
"servicesSecondaryCta": "Meine Arbeiten ansehen",
|
||||
"serviceBrand": "Markenaufbau mit klarer Richtung.",
|
||||
"serviceWebsite": "Websites mit sauberer Struktur.",
|
||||
"serviceStore": "Shops, die klarer verkaufen.",
|
||||
"serviceWordPress": "Eigene WordPress Plugins.",
|
||||
"serviceSeo": "Mehr Sichtbarkeit in Suchmaschinen.",
|
||||
"serviceTracking": "Tracking und bessere Auswertung.",
|
||||
"serviceSystem": "Interne Systeme für Teams und Firmen.",
|
||||
"toPortfolio": "Portfolio ansehen",
|
||||
"toContact": "Kontakt aufnehmen"
|
||||
"meta": {
|
||||
"title": "Portfolio fuer Full-stack Entwicklung",
|
||||
"description": "Moderne digitale Produkte, skalierbare Systeme und mehrsprachige Erlebnisse mit starkem Produktfokus."
|
||||
},
|
||||
"heroVisual": {
|
||||
"kicker": "Hi, ich bin Moh",
|
||||
"title": "frontend\ndeveloper\n& designer",
|
||||
"description": "Ich entwickle digitale Loesungen fuer echte Probleme,\ndie gut aussehen und sauber funktionieren."
|
||||
},
|
||||
"hero": {
|
||||
"availability": "Verfuegbar fuer ausgewaehlte Projekte",
|
||||
"name": "Moh Farawati",
|
||||
"headline": "Full-stack Produkte mit starker Frontend Umsetzung und verlaesslichen Backend Systemen.",
|
||||
"description": "Ich konzipiere und entwickle hochwertige Web-Erlebnisse fuer Teams, die Klarheit, Geschwindigkeit und produktionsreife Umsetzung von der Strategie bis zum Launch brauchen.",
|
||||
"primaryCta": "Projekt starten",
|
||||
"secondaryCta": "Ausgewaehlte Arbeiten",
|
||||
"locationLabel": "Standort",
|
||||
"locationValue": "Berlin, Deutschland",
|
||||
"roleLabel": "Rolle",
|
||||
"roleValue": "Full-stack Entwickler",
|
||||
"focusLabel": "Aktueller Fokus",
|
||||
"focusValue": "Performante Produkte, Automatisierung und wartbare Plattformarbeit.",
|
||||
"highlights": [
|
||||
"3-sprachige Umsetzung",
|
||||
"Produktorientierte Ausfuehrung",
|
||||
"Technischer Partner fuer launchbereite Teams"
|
||||
]
|
||||
},
|
||||
"bento": {
|
||||
"eyebrow": "Highlights",
|
||||
"title": "Gebaut fuer Teams, die Qualitaet, Tempo und saubere technische Entscheidungen ernst nehmen.",
|
||||
"description": "Die Startseite ist so aufgebaut, wie ich arbeite: erst Klarheit, dann starke Umsetzung, ohne unnoetige Komplexitaet dazwischen.",
|
||||
"about": {
|
||||
"eyebrow": "Ueber mich",
|
||||
"title": "Ich arbeite ueber Produkt, Engineering und Delivery hinweg.",
|
||||
"description": "Meine Arbeit verbindet Produktdenken mit direkter Umsetzung. So bekommen Gruender, Agenturen und technische Teams einen Entwickler, der von Architektur bis zum finalen Detail liefern kann, ohne an Qualitaet zu verlieren.",
|
||||
"points": [
|
||||
"Frontend Systeme mit klarer UX Disziplin",
|
||||
"Backend Services und Integrationen fuer reale Prozesse",
|
||||
"Klare Kommunikation mit technischen und nicht-technischen Stakeholdern",
|
||||
"Lieferung mit Blick auf Wartbarkeit ab dem ersten Tag"
|
||||
]
|
||||
},
|
||||
"coreStack": {
|
||||
"eyebrow": "Core stack",
|
||||
"title": "Taegliche Werkzeuge",
|
||||
"items": [
|
||||
"Next.js",
|
||||
"TypeScript",
|
||||
"React",
|
||||
"Node.js",
|
||||
"PostgreSQL",
|
||||
"Tailwind CSS",
|
||||
"Prisma",
|
||||
"Docker"
|
||||
]
|
||||
},
|
||||
"availability": {
|
||||
"eyebrow": "Verfuegbarkeit",
|
||||
"title": "Offen fuer fokussierte Zusammenarbeit",
|
||||
"description": "Am besten passend fuer Startups, Agenturen und Teams, die einen technischen Owner suchen, der die Umsetzung Ende zu Ende treiben kann.",
|
||||
"status": "Neue Slots offen"
|
||||
},
|
||||
"currentFocus": {
|
||||
"eyebrow": "Aktueller Fokus",
|
||||
"title": "Woran ich gerade am meisten baue",
|
||||
"items": [
|
||||
"Marketing Websites mit Produktniveau in der UX",
|
||||
"Admin Tools und interne Dashboards",
|
||||
"Automatisierungsablaeufe fuer Content und Operations"
|
||||
]
|
||||
},
|
||||
"contact": {
|
||||
"eyebrow": "Schneller Kontakt",
|
||||
"title": "Direkter Weg zu mir",
|
||||
"description": "Wenn das Briefing schon klar ist, schick Kontext und Zeitrahmen und ich melde mich mit dem richtigen naechsten Schritt.",
|
||||
"emailLabel": "E-Mail",
|
||||
"emailValue": "hello@moh-sass.dev"
|
||||
},
|
||||
"highlights": {
|
||||
"eyebrow": "Highlights",
|
||||
"title": "Arbeitsprinzipien",
|
||||
"items": [
|
||||
{
|
||||
"value": "3",
|
||||
"label": "Unterstuetzte Sprachen"
|
||||
},
|
||||
{
|
||||
"value": "E2E",
|
||||
"label": "Delivery Ownership"
|
||||
},
|
||||
{
|
||||
"value": "24/7",
|
||||
"label": "Produktionsdenken"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"marquee": {
|
||||
"eyebrow": "Werkzeuge",
|
||||
"title": "Ein Arbeits-Stack, ausgewaehlt fuer Wartbarkeit, Geschwindigkeit und langfristigen Hebel.",
|
||||
"description": "Das Marquee nutzt die bestehende Site-Konfiguration, damit die Startseite mit den hervorgehobenen Tools und Kompetenzen konsistent bleibt."
|
||||
},
|
||||
"projects": {
|
||||
"eyebrow": "Ausgewaehlte Projekte",
|
||||
"title": "Drei Beispiele fuer die Art von Arbeit, die ich gerne ausliefere.",
|
||||
"description": "Ausgewaehlte Projekte verbinden starke Interface-Qualitaet mit technischer Struktur, die auch nach dem Launch stabil bleibt.",
|
||||
"stackLabel": "Stack",
|
||||
"cta": "Projekt oeffnen",
|
||||
"fallbackMeta": "Kuratiertes Projekt",
|
||||
"stackSets": [
|
||||
[
|
||||
"Next.js",
|
||||
"TypeScript",
|
||||
"Design Systems"
|
||||
],
|
||||
[
|
||||
"APIs",
|
||||
"PostgreSQL",
|
||||
"Automation"
|
||||
],
|
||||
[
|
||||
"Tailwind CSS",
|
||||
"Prisma",
|
||||
"Infrastructure"
|
||||
]
|
||||
],
|
||||
"fallbackItems": [
|
||||
{
|
||||
"title": "Produktplattform Relaunch",
|
||||
"summary": "Eine produktionsreife Erneuerung fuer eine Service-Plattform mit klarerer UX, besserer Architektur und schnelleren Lieferzyklen.",
|
||||
"stack": [
|
||||
"Next.js",
|
||||
"TypeScript",
|
||||
"PostgreSQL"
|
||||
],
|
||||
"href": "/portfolio"
|
||||
},
|
||||
{
|
||||
"title": "Operations Dashboard",
|
||||
"summary": "Interne Tools mit Fokus auf schnellere Entscheidungen, sauberere Workflows und weniger manuelle Abstimmung im Team.",
|
||||
"stack": [
|
||||
"React",
|
||||
"Node.js",
|
||||
"Automation"
|
||||
],
|
||||
"href": "/portfolio"
|
||||
},
|
||||
{
|
||||
"title": "Launch Website System",
|
||||
"summary": "Eine wiederverwendbare Website Grundlage fuer schnelle Launches ohne Abstriche bei technischer Qualitaet oder redaktioneller Kontrolle.",
|
||||
"stack": [
|
||||
"Tailwind CSS",
|
||||
"CMS",
|
||||
"Performance"
|
||||
],
|
||||
"href": "/contact"
|
||||
}
|
||||
]
|
||||
},
|
||||
"capabilities": {
|
||||
"eyebrow": "Capabilities",
|
||||
"title": "Technische Abdeckung von Ende zu Ende ohne den Produktfokus zu verlieren.",
|
||||
"description": "Jeder Bereich ist um echte Produktionsarbeit aufgebaut und nicht um generische Service-Begriffe.",
|
||||
"items": [
|
||||
{
|
||||
"title": "Frontend Engineering",
|
||||
"description": "Responsive Interfaces, Design Systems, Interaktionsqualitaet und Komponentenarchitektur fuer skalierbare Produkte."
|
||||
},
|
||||
{
|
||||
"title": "Backend Systems",
|
||||
"description": "Strukturierte APIs, Integrationen, Datenmodelle und Applikationslogik fuer verlaessliche Umsetzung."
|
||||
},
|
||||
{
|
||||
"title": "DevOps & Infrastructure",
|
||||
"description": "Deployments, Umgebungen, grundlegende Observability und Infrastrukturentscheidungen mit weniger operativer Reibung."
|
||||
},
|
||||
{
|
||||
"title": "Automation & AI Workflows",
|
||||
"description": "Praktische Automatisierungsebenen, die manuelle Arbeit reduzieren und Produkt, Content und Operations verbinden."
|
||||
}
|
||||
]
|
||||
},
|
||||
"process": {
|
||||
"eyebrow": "Prozess",
|
||||
"title": "Ein kompakter Ablauf, der Projekte in Bewegung haelt.",
|
||||
"description": "Der Prozess ist absichtlich einfach: Unklarheit frueh reduzieren, solide technische Entscheidungen treffen und diszipliniert ausliefern.",
|
||||
"steps": [
|
||||
{
|
||||
"title": "Discover",
|
||||
"description": "Ziele, Grenzen, Zielgruppe und das eigentliche Ergebnis klaeren, das das Produkt erreichen soll."
|
||||
},
|
||||
{
|
||||
"title": "Architect",
|
||||
"description": "Systemform, technische Grenzen, Inhaltsstruktur und Lieferweg definieren, bevor die schwere Umsetzung startet."
|
||||
},
|
||||
{
|
||||
"title": "Build",
|
||||
"description": "Mit starker UI Qualitaet, sauberer Applikationslogik und wartbarer Struktur ueber den gesamten Stack umsetzen."
|
||||
},
|
||||
{
|
||||
"title": "Ship",
|
||||
"description": "Sicher launchen, das Ergebnis validieren und das Produkt stabil fuer die naechste Phase hinterlassen."
|
||||
}
|
||||
]
|
||||
},
|
||||
"contactCta": {
|
||||
"eyebrow": "Gespraech starten",
|
||||
"title": "Wenn das Projekt starke Umsetzung braucht, kann ich es voranbringen.",
|
||||
"description": "Bring das Briefing, den Engpass oder die grobe Richtung mit. Ich kann Scope schaerfen, das System entwerfen und das Produkt bauen.",
|
||||
"contactCta": "Kontakt aufnehmen",
|
||||
"githubCta": "GitHub",
|
||||
"emailLabel": "E-Mail",
|
||||
"emailValue": "hello@moh-sass.dev",
|
||||
"availabilityLabel": "Verfuegbarkeit",
|
||||
"availabilityValue": "Offen fuer fokussierte Produktarbeit",
|
||||
"githubHref": "https://github.com/mohfarawati"
|
||||
}
|
||||
},
|
||||
"portfolioPage": {
|
||||
"title": "Portfolio",
|
||||
|
||||
+218
-17
@@ -36,23 +36,224 @@
|
||||
"secondaryCta": "Back to homepage"
|
||||
},
|
||||
"homepage": {
|
||||
"heroKicker": "Hi, I am Moh",
|
||||
"heroTitle": "frontend\ndeveloper\n& designer",
|
||||
"heroText": "I craft digital solutions for human problems\nthat look good and work well.",
|
||||
"servicesEyebrow": "Capabilities",
|
||||
"servicesTitle": "Digital work shaped around what your business actually needs.",
|
||||
"servicesText": "From brand direction and websites to stores, SEO, tracking, WordPress plugins, and internal systems, the goal is the same: clearer structure, better performance, and room to grow.",
|
||||
"servicesPrimaryCta": "Tell me about your project",
|
||||
"servicesSecondaryCta": "See my work",
|
||||
"serviceBrand": "Brand systems and visual direction.",
|
||||
"serviceWebsite": "Business websites with clear structure.",
|
||||
"serviceStore": "Stores built for easier selling.",
|
||||
"serviceWordPress": "Custom WordPress plugin development.",
|
||||
"serviceSeo": "Search visibility and technical SEO.",
|
||||
"serviceTracking": "Tracking setup and user insights.",
|
||||
"serviceSystem": "Custom internal systems for teams.",
|
||||
"toPortfolio": "View portfolio",
|
||||
"toContact": "Contact me"
|
||||
"meta": {
|
||||
"title": "Full-stack developer portfolio",
|
||||
"description": "Modern digital products, scalable systems, and multilingual experiences built with product focus."
|
||||
},
|
||||
"heroVisual": {
|
||||
"kicker": "Hi, I am Moh",
|
||||
"title": "frontend\ndeveloper\n& designer",
|
||||
"description": "I craft digital solutions for human problems\nthat look good and work well."
|
||||
},
|
||||
"hero": {
|
||||
"availability": "Available for selected projects",
|
||||
"name": "Moh Farawati",
|
||||
"headline": "Full-stack products with sharp frontend execution and reliable backend systems.",
|
||||
"description": "I design and build premium web experiences for teams that need clarity, speed, and production-ready engineering from strategy to launch.",
|
||||
"primaryCta": "Start a project",
|
||||
"secondaryCta": "View selected work",
|
||||
"locationLabel": "Base",
|
||||
"locationValue": "Berlin, Germany",
|
||||
"roleLabel": "Role",
|
||||
"roleValue": "Full-stack developer",
|
||||
"focusLabel": "Current focus",
|
||||
"focusValue": "High-performance products, automation, and maintainable platform work.",
|
||||
"highlights": [
|
||||
"3-language delivery",
|
||||
"Product-minded execution",
|
||||
"Technical partner for launch-ready teams"
|
||||
]
|
||||
},
|
||||
"bento": {
|
||||
"eyebrow": "Highlights",
|
||||
"title": "Built for teams that care about quality, speed, and clean technical decisions.",
|
||||
"description": "The homepage is structured around the way I work: strategy first, strong execution second, and no wasted surface area in between.",
|
||||
"about": {
|
||||
"eyebrow": "About me",
|
||||
"title": "I work across product, engineering, and delivery.",
|
||||
"description": "My work combines product thinking with hands-on implementation, so founders, agencies, and technical teams get a developer who can move from architecture to shipped detail without losing quality.",
|
||||
"points": [
|
||||
"Frontend systems with strong UX discipline",
|
||||
"Backend services and integrations for real workflows",
|
||||
"Clear communication with technical and non-technical stakeholders",
|
||||
"Shipping with maintainability in mind from day one"
|
||||
]
|
||||
},
|
||||
"coreStack": {
|
||||
"eyebrow": "Core stack",
|
||||
"title": "Daily tools",
|
||||
"items": [
|
||||
"Next.js",
|
||||
"TypeScript",
|
||||
"React",
|
||||
"Node.js",
|
||||
"PostgreSQL",
|
||||
"Tailwind CSS",
|
||||
"Prisma",
|
||||
"Docker"
|
||||
]
|
||||
},
|
||||
"availability": {
|
||||
"eyebrow": "Availability",
|
||||
"title": "Open for focused collaborations",
|
||||
"description": "Best fit for startups, agencies, and teams that want one technical owner who can drive implementation end to end.",
|
||||
"status": "New slots open"
|
||||
},
|
||||
"currentFocus": {
|
||||
"eyebrow": "Current focus",
|
||||
"title": "What I am building most",
|
||||
"items": [
|
||||
"Marketing sites with product-grade UX",
|
||||
"Admin tooling and internal dashboards",
|
||||
"Automation flows around content and operations"
|
||||
]
|
||||
},
|
||||
"contact": {
|
||||
"eyebrow": "Quick contact",
|
||||
"title": "Short path to reach me",
|
||||
"description": "If the brief is already clear, send the context and timeline and I will reply with the right next step.",
|
||||
"emailLabel": "Email",
|
||||
"emailValue": "hello@moh-sass.dev"
|
||||
},
|
||||
"highlights": {
|
||||
"eyebrow": "Highlights",
|
||||
"title": "Working principles",
|
||||
"items": [
|
||||
{
|
||||
"value": "3",
|
||||
"label": "Supported languages"
|
||||
},
|
||||
{
|
||||
"value": "E2E",
|
||||
"label": "Delivery ownership"
|
||||
},
|
||||
{
|
||||
"value": "24/7",
|
||||
"label": "Production mindset"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"marquee": {
|
||||
"eyebrow": "Trusted tools",
|
||||
"title": "A working stack chosen for maintainability, speed, and long-term leverage.",
|
||||
"description": "The marquee pulls from the existing site configuration so the homepage stays aligned with the tools and capabilities you want to highlight."
|
||||
},
|
||||
"projects": {
|
||||
"eyebrow": "Selected projects",
|
||||
"title": "Three examples of the kind of work I like to ship.",
|
||||
"description": "Featured work combines strong interface quality with technical structure that stays stable after launch.",
|
||||
"stackLabel": "Stack",
|
||||
"cta": "Open project",
|
||||
"fallbackMeta": "Curated work",
|
||||
"stackSets": [
|
||||
[
|
||||
"Next.js",
|
||||
"TypeScript",
|
||||
"Design Systems"
|
||||
],
|
||||
[
|
||||
"APIs",
|
||||
"PostgreSQL",
|
||||
"Automation"
|
||||
],
|
||||
[
|
||||
"Tailwind CSS",
|
||||
"Prisma",
|
||||
"Infrastructure"
|
||||
]
|
||||
],
|
||||
"fallbackItems": [
|
||||
{
|
||||
"title": "Product platform overhaul",
|
||||
"summary": "A production-minded refresh for a service platform with clearer UX, stronger architecture, and faster delivery cycles.",
|
||||
"stack": [
|
||||
"Next.js",
|
||||
"TypeScript",
|
||||
"PostgreSQL"
|
||||
],
|
||||
"href": "/portfolio"
|
||||
},
|
||||
{
|
||||
"title": "Operations dashboard",
|
||||
"summary": "Internal tooling focused on decision speed, cleaner workflows, and less manual coordination across teams.",
|
||||
"stack": [
|
||||
"React",
|
||||
"Node.js",
|
||||
"Automation"
|
||||
],
|
||||
"href": "/portfolio"
|
||||
},
|
||||
{
|
||||
"title": "Launch website system",
|
||||
"summary": "A reusable website foundation for rapid launches without compromising technical quality or editorial control.",
|
||||
"stack": [
|
||||
"Tailwind CSS",
|
||||
"CMS",
|
||||
"Performance"
|
||||
],
|
||||
"href": "/contact"
|
||||
}
|
||||
]
|
||||
},
|
||||
"capabilities": {
|
||||
"eyebrow": "Capabilities",
|
||||
"title": "End-to-end technical coverage without losing product focus.",
|
||||
"description": "Each capability area is shaped around production work, not generic service labels.",
|
||||
"items": [
|
||||
{
|
||||
"title": "Frontend Engineering",
|
||||
"description": "Responsive interfaces, design systems, interaction polish, and component architecture built for scale."
|
||||
},
|
||||
{
|
||||
"title": "Backend Systems",
|
||||
"description": "Structured APIs, integrations, data modeling, and application logic designed for dependable delivery."
|
||||
},
|
||||
{
|
||||
"title": "DevOps & Infrastructure",
|
||||
"description": "Deployments, environments, observability basics, and infrastructure decisions that reduce operational drag."
|
||||
},
|
||||
{
|
||||
"title": "Automation & AI Workflows",
|
||||
"description": "Practical automation layers that remove manual work and connect product, content, and operational tasks."
|
||||
}
|
||||
]
|
||||
},
|
||||
"process": {
|
||||
"eyebrow": "Process",
|
||||
"title": "A compact workflow that keeps projects moving.",
|
||||
"description": "The process is intentionally simple: reduce ambiguity early, make sound technical decisions, and ship with discipline.",
|
||||
"steps": [
|
||||
{
|
||||
"title": "Discover",
|
||||
"description": "Clarify goals, constraints, audience, and the real outcome the product needs to drive."
|
||||
},
|
||||
{
|
||||
"title": "Architect",
|
||||
"description": "Define the system shape, technical boundaries, content structure, and delivery path before heavy implementation."
|
||||
},
|
||||
{
|
||||
"title": "Build",
|
||||
"description": "Implement with strong UI detail, clean application logic, and maintainable structure across the stack."
|
||||
},
|
||||
{
|
||||
"title": "Ship",
|
||||
"description": "Launch with confidence, validate the result, and leave the product in a stable state for the next phase."
|
||||
}
|
||||
]
|
||||
},
|
||||
"contactCta": {
|
||||
"eyebrow": "Start the conversation",
|
||||
"title": "If the project needs strong execution, I can help move it forward.",
|
||||
"description": "Bring the brief, the bottleneck, or the rough direction. I can help shape the scope, design the system, and build the product.",
|
||||
"contactCta": "Contact me",
|
||||
"githubCta": "GitHub",
|
||||
"emailLabel": "Email",
|
||||
"emailValue": "hello@moh-sass.dev",
|
||||
"availabilityLabel": "Availability",
|
||||
"availabilityValue": "Open for focused product work",
|
||||
"githubHref": "https://github.com/mohfarawati"
|
||||
}
|
||||
},
|
||||
"portfolioPage": {
|
||||
"title": "Portfolio",
|
||||
|
||||
Reference in New Issue
Block a user