fix: update portfolio branding to "Killian' DAL-CIN" across all documentation and components

- Corrected the name in various files including CLAUDE.md, README.md, and configuration files to reflect the updated branding.
- Ensured consistency in the use of the new name throughout the project, enhancing brand identity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-08 19:54:46 +02:00
parent 9779e4e133
commit c8dac9ac88
45 changed files with 750 additions and 665 deletions
+47 -51
View File
@@ -65,42 +65,45 @@ const approachCards = computed(() => [
<template>
<div>
<!-- Hero Section -->
<section class="pt-16 pb-20 px-4 bg-gray-50 dark:bg-gray-900/30">
<div class="max-w-4xl mx-auto text-center">
<span class="text-sm font-semibold text-brand-500 dark:text-brand-400 uppercase tracking-wider">About</span>
<h1 class="text-4xl sm:text-5xl font-bold mt-2 mb-6 text-gray-900 dark:text-white">
<section class="relative pt-20 pb-20 px-4 sm:px-6 lg:px-8 overflow-hidden">
<div class="absolute inset-0 bg-gray-50/80 dark:bg-gray-900/40" aria-hidden="true" />
<div class="absolute top-0 right-0 w-[600px] h-[600px] bg-brand-500/5 dark:bg-brand-500/8 rounded-full blur-3xl -translate-y-1/2 translate-x-1/4 pointer-events-none" aria-hidden="true" />
<div class="relative z-10 max-w-4xl mx-auto text-center">
<span class="font-mono text-sm text-brand-500 dark:text-brand-400 tracking-wider">// about</span>
<h1 class="text-4xl sm:text-5xl lg:text-6xl font-bold mt-3 mb-6 bg-gradient-to-r from-gray-900 via-gray-800 to-gray-600 dark:from-white dark:via-gray-200 dark:to-gray-500 bg-clip-text text-transparent">
{{ t('about.title') }}
</h1>
<p class="text-xl text-gray-500 dark:text-gray-400 mb-10 max-w-2xl mx-auto">
<p class="text-xl text-gray-500 dark:text-gray-400 mb-12 max-w-2xl mx-auto leading-relaxed">
{{ t('about.subtitle') }}
</p>
<div class="space-y-4 text-lg text-gray-600 dark:text-gray-400 max-w-3xl mx-auto leading-relaxed">
<p>{{ t('about.intro.content') }}</p>
<p>{{ t('about.experience.content') }}</p>
<div class="max-w-3xl mx-auto space-y-6">
<p class="text-lg text-gray-600 dark:text-gray-400 leading-relaxed rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/60 dark:bg-gray-900/40 backdrop-blur-sm p-6 sm:p-8 text-left">{{ t('about.intro.content') }}</p>
<p class="text-lg text-gray-600 dark:text-gray-400 leading-relaxed rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/60 dark:bg-gray-900/40 backdrop-blur-sm p-6 sm:p-8 text-left">{{ t('about.experience.content') }}</p>
</div>
</div>
</section>
<!-- Skills Section -->
<section class="py-20 md:py-28 px-4">
<section class="py-24 md:py-32 px-4 sm:px-6 lg:px-8">
<div class="max-w-7xl mx-auto">
<div class="text-center mb-14">
<span class="text-sm font-semibold text-brand-500 dark:text-brand-400 uppercase tracking-wider">Tech Stack</span>
<h2 class="text-3xl sm:text-4xl font-bold mt-2 text-gray-900 dark:text-white">{{ t('about.skills.title') }}</h2>
<p class="text-lg text-gray-500 dark:text-gray-400 mt-3 max-w-2xl mx-auto">
<div class="text-center mb-16">
<span class="font-mono text-sm text-brand-500 dark:text-brand-400 tracking-wider">// tech-stack</span>
<h2 class="text-3xl sm:text-4xl lg:text-5xl font-bold mt-3 bg-gradient-to-r from-gray-900 via-gray-800 to-gray-600 dark:from-white dark:via-gray-200 dark:to-gray-500 bg-clip-text text-transparent">{{ t('about.skills.title') }}</h2>
<p class="text-lg text-gray-500 dark:text-gray-400 mt-4 max-w-2xl mx-auto leading-relaxed">
{{ t('about.subtitle') }}
</p>
</div>
<!-- Tech Categories Grid -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<!-- Tech Categories Bento Grid -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-5 mb-5">
<div
v-for="category in techCategories"
:key="category.key"
class="rounded-2xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-6 sm:p-8"
class="group rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/80 dark:bg-gray-900/60 backdrop-blur-sm p-7 sm:p-8 transition-all duration-300 hover:border-brand-500/30 hover:shadow-lg hover:shadow-brand-500/5"
>
<div class="flex items-center gap-3 mb-6">
<div class="w-10 h-10 rounded-xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center">
<div class="w-10 h-10 rounded-xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center transition-all duration-300 group-hover:bg-brand-500/20 group-hover:scale-110">
<UIcon :name="category.icon" class="text-xl text-brand-600 dark:text-brand-400" />
</div>
<h3 class="text-xl font-bold text-gray-900 dark:text-white">{{ category.title }}</h3>
@@ -117,9 +120,9 @@ const approachCards = computed(() => [
</div>
<!-- Operating Systems -->
<div class="rounded-2xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-6 sm:p-8">
<div class="group rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/80 dark:bg-gray-900/60 backdrop-blur-sm p-7 sm:p-8 transition-all duration-300 hover:border-brand-500/30 hover:shadow-lg hover:shadow-brand-500/5">
<div class="flex items-center gap-3 mb-6">
<div class="w-10 h-10 rounded-xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center">
<div class="w-10 h-10 rounded-xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center transition-all duration-300 group-hover:bg-brand-500/20 group-hover:scale-110">
<UIcon name="i-lucide-monitor" class="text-xl text-brand-600 dark:text-brand-400" />
</div>
<h3 class="text-xl font-bold text-gray-900 dark:text-white">{{ t('about.skills.systems') }}</h3>
@@ -137,28 +140,34 @@ const approachCards = computed(() => [
</section>
<!-- Approach Section -->
<section class="py-20 md:py-28 px-4 bg-gray-50 dark:bg-gray-900/30">
<div class="max-w-7xl mx-auto">
<div class="text-center mb-14">
<span class="text-sm font-semibold text-brand-500 dark:text-brand-400 uppercase tracking-wider">Methodology</span>
<h2 class="text-3xl sm:text-4xl font-bold mt-2 text-gray-900 dark:text-white">{{ t('about.approach.title') }}</h2>
<p class="text-lg text-gray-500 dark:text-gray-400 mt-3 max-w-2xl mx-auto">
<section class="relative py-24 md:py-32 px-4 sm:px-6 lg:px-8 overflow-hidden">
<div class="absolute inset-0 bg-gray-50/80 dark:bg-gray-900/40" aria-hidden="true" />
<div class="absolute bottom-0 left-0 w-[500px] h-[500px] bg-brand-500/5 dark:bg-brand-500/8 rounded-full blur-3xl translate-y-1/2 -translate-x-1/4 pointer-events-none" aria-hidden="true" />
<div class="relative z-10 max-w-7xl mx-auto">
<div class="text-center mb-16">
<span class="font-mono text-sm text-brand-500 dark:text-brand-400 tracking-wider">// methodology</span>
<h2 class="text-3xl sm:text-4xl lg:text-5xl font-bold mt-3 bg-gradient-to-r from-gray-900 via-gray-800 to-gray-600 dark:from-white dark:via-gray-200 dark:to-gray-500 bg-clip-text text-transparent">{{ t('about.approach.title') }}</h2>
<p class="text-lg text-gray-500 dark:text-gray-400 mt-4 max-w-2xl mx-auto leading-relaxed">
{{ t('about.approach.subtitle') }}
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
<div
v-for="(card, index) in approachCards"
:key="index"
class="group rounded-2xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-6 sm:p-8 transition-all duration-300 hover:border-brand-500/30 hover:shadow-lg hover:shadow-brand-500/5"
class="group rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/80 dark:bg-gray-900/60 backdrop-blur-sm p-7 sm:p-8 transition-all duration-300 hover:border-brand-500/40 hover:shadow-xl hover:shadow-brand-500/10 hover:-translate-y-1"
>
<div class="flex items-start gap-4">
<div class="w-12 h-12 rounded-xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center shrink-0 transition-colors group-hover:bg-brand-500/20">
<!-- Hover glow -->
<div class="absolute inset-0 rounded-2xl bg-gradient-to-br from-brand-500/0 to-emerald-500/0 group-hover:from-brand-500/5 group-hover:to-emerald-500/5 transition-all duration-500 pointer-events-none" aria-hidden="true" />
<div class="relative z-10 flex items-start gap-4">
<div class="w-12 h-12 rounded-xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center shrink-0 transition-all duration-300 group-hover:bg-brand-500/20 group-hover:scale-110">
<UIcon :name="card.icon" class="text-xl text-brand-600 dark:text-brand-400" />
</div>
<div>
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-2">{{ card.title }}</h3>
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-2 group-hover:text-brand-600 dark:group-hover:text-brand-400 transition-colors">{{ card.title }}</h3>
<p class="text-gray-500 dark:text-gray-400 leading-relaxed">{{ card.description }}</p>
</div>
</div>
@@ -168,26 +177,13 @@ const approachCards = computed(() => [
</section>
<!-- CTA Section -->
<section class="py-20 md:py-28 px-4">
<div class="max-w-5xl mx-auto">
<div class="relative overflow-hidden rounded-3xl bg-gradient-to-br from-brand-600 via-brand-500 to-emerald-500 px-8 py-16 sm:px-16 sm:py-20 text-center">
<div class="absolute top-0 left-0 w-72 h-72 bg-white/10 rounded-full -translate-x-1/2 -translate-y-1/2 blur-2xl" aria-hidden="true" />
<div class="absolute bottom-0 right-0 w-96 h-96 bg-black/10 rounded-full translate-x-1/3 translate-y-1/3 blur-2xl" aria-hidden="true" />
<div class="relative z-10">
<h2 class="text-3xl sm:text-4xl font-bold text-white mb-4">{{ t('about.cta.title') }}</h2>
<p class="text-lg text-white/80 mb-10 max-w-2xl mx-auto">{{ t('about.cta.description') }}</p>
<div class="flex flex-wrap justify-center gap-4">
<UButton :to="localePath('/contact')" size="xl" color="white" class="font-semibold">
{{ t('about.cta.button') }}
</UButton>
<UButton :to="localePath('/projects')" size="xl" variant="outline" color="white" class="font-semibold">
{{ t('home.cta.viewProjects') }}
</UButton>
</div>
</div>
</div>
</div>
</section>
<CTASection
:title="t('about.cta.title')"
:subtitle="t('about.cta.description')"
:primary-text="t('about.cta.button')"
primary-to="/contact"
:secondary-text="t('home.cta.viewProjects')"
secondary-to="/projects"
/>
</div>
</template>
+62 -57
View File
@@ -18,43 +18,49 @@ useSeoMeta({
<template>
<div>
<!-- Hero Section -->
<section class="pt-16 pb-12 px-4 bg-gray-50 dark:bg-gray-900/30">
<div class="max-w-4xl mx-auto text-center">
<span class="text-sm font-semibold text-brand-500 dark:text-brand-400 uppercase tracking-wider">Contact</span>
<h1 class="text-4xl sm:text-5xl font-bold mt-2 mb-6 text-gray-900 dark:text-white">
<section class="relative pt-20 pb-16 px-4 sm:px-6 lg:px-8 overflow-hidden">
<div class="absolute inset-0 bg-gray-50/80 dark:bg-gray-900/40" aria-hidden="true" />
<div class="absolute top-0 left-1/2 -translate-x-1/2 w-[800px] h-[400px] bg-brand-500/5 dark:bg-brand-500/8 rounded-full blur-3xl pointer-events-none" aria-hidden="true" />
<div class="relative z-10 max-w-4xl mx-auto text-center">
<span class="font-mono text-sm text-brand-500 dark:text-brand-400 tracking-wider">// contact</span>
<h1 class="text-4xl sm:text-5xl lg:text-6xl font-bold mt-3 mb-6 bg-gradient-to-r from-gray-900 via-gray-800 to-gray-600 dark:from-white dark:via-gray-200 dark:to-gray-500 bg-clip-text text-transparent">
{{ t('contact.title') }}
</h1>
<p class="text-xl text-gray-500 dark:text-gray-400 mb-10 max-w-2xl mx-auto">
<p class="text-xl text-gray-500 dark:text-gray-400 mb-12 max-w-2xl mx-auto leading-relaxed">
{{ t('contact.subtitle') }}
</p>
<!-- Stats -->
<div class="flex flex-wrap justify-center gap-10">
<div class="flex flex-wrap justify-center gap-8 sm:gap-12">
<div class="text-center">
<div class="text-3xl font-extrabold text-brand-500 dark:text-brand-400">24-48h</div>
<div class="text-sm text-gray-500 dark:text-gray-400 mt-1">{{ t('contact.stats.responseTime') }}</div>
<div class="text-3xl sm:text-4xl font-black bg-gradient-to-b from-brand-400 to-brand-600 bg-clip-text text-transparent">24-48h</div>
<div class="text-sm text-gray-500 dark:text-gray-400 mt-2 font-medium">{{ t('contact.stats.responseTime') }}</div>
</div>
<div class="w-px bg-gray-200 dark:bg-gray-800 hidden sm:block" />
<div class="w-px bg-gradient-to-b from-transparent via-gray-300 dark:via-gray-700 to-transparent hidden sm:block" />
<div class="text-center">
<div class="text-3xl font-extrabold text-brand-500 dark:text-brand-400">100%</div>
<div class="text-sm text-gray-500 dark:text-gray-400 mt-1">{{ t('contact.stats.satisfaction') }}</div>
<div class="text-3xl sm:text-4xl font-black bg-gradient-to-b from-brand-400 to-brand-600 bg-clip-text text-transparent">100%</div>
<div class="text-sm text-gray-500 dark:text-gray-400 mt-2 font-medium">{{ t('contact.stats.satisfaction') }}</div>
</div>
<div class="w-px bg-gray-200 dark:bg-gray-800 hidden sm:block" />
<div class="w-px bg-gradient-to-b from-transparent via-gray-300 dark:via-gray-700 to-transparent hidden sm:block" />
<div class="text-center">
<div class="text-3xl font-extrabold text-brand-500 dark:text-brand-400">Remote</div>
<div class="text-sm text-gray-500 dark:text-gray-400 mt-1">{{ t('contact.stats.collaboration') }}</div>
<div class="text-3xl sm:text-4xl font-black bg-gradient-to-b from-brand-400 to-brand-600 bg-clip-text text-transparent">Remote</div>
<div class="text-sm text-gray-500 dark:text-gray-400 mt-2 font-medium">{{ t('contact.stats.collaboration') }}</div>
</div>
</div>
</div>
</section>
<!-- Two Column Layout -->
<section class="py-16 md:py-20 px-4">
<div class="max-w-6xl mx-auto grid grid-cols-1 lg:grid-cols-5 gap-10 lg:gap-16">
<section class="py-16 md:py-24 px-4 sm:px-6 lg:px-8">
<div class="max-w-6xl mx-auto grid grid-cols-1 lg:grid-cols-5 gap-8 lg:gap-12">
<!-- Left: Contact Form (wider) -->
<div class="lg:col-span-3">
<div class="rounded-2xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-6 sm:p-8">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-6">{{ t('contact.form.title') }}</h2>
<div class="rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/80 dark:bg-gray-900/60 backdrop-blur-sm p-7 sm:p-8">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-6 flex items-center gap-3">
<div class="w-1 h-6 rounded-full bg-brand-500" />
{{ t('contact.form.title') }}
</h2>
<ContactForm />
</div>
</div>
@@ -62,53 +68,50 @@ useSeoMeta({
<!-- Right: Contact Info + Social -->
<div class="lg:col-span-2 flex flex-col gap-6">
<!-- Contact Info -->
<div class="rounded-2xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-6 sm:p-8">
<h2 class="text-xl font-bold text-gray-900 dark:text-white mb-6">{{ t('contact.quickContact') }}</h2>
<div class="flex flex-col gap-5">
<div class="rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/80 dark:bg-gray-900/60 backdrop-blur-sm p-7 sm:p-8">
<h2 class="text-xl font-bold text-gray-900 dark:text-white mb-6 flex items-center gap-3">
<div class="w-1 h-5 rounded-full bg-brand-500" />
{{ t('contact.quickContact') }}
</h2>
<div class="flex flex-col gap-4">
<a
:href="`mailto:${siteConfig.contact.email}`"
class="flex items-center gap-4 group"
class="flex items-center gap-4 p-3 rounded-xl border border-transparent hover:border-brand-500/20 hover:bg-brand-500/5 transition-all duration-200 group"
>
<div class="w-10 h-10 rounded-xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center shrink-0 transition-colors group-hover:bg-brand-500/20">
<div class="w-10 h-10 rounded-xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center shrink-0 transition-all duration-300 group-hover:bg-brand-500/20 group-hover:scale-110">
<UIcon name="i-lucide-mail" class="text-brand-600 dark:text-brand-400" />
</div>
<span class="text-gray-600 dark:text-gray-300 group-hover:text-brand-500 transition-colors">{{ siteConfig.contact.email }}</span>
<span class="text-gray-600 dark:text-gray-300 group-hover:text-brand-500 transition-colors font-medium">{{ siteConfig.contact.email }}</span>
</a>
<a
:href="`tel:${siteConfig.contact.phone.replace(/\s/g, '')}`"
class="flex items-center gap-4 group"
>
<div class="w-10 h-10 rounded-xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center shrink-0 transition-colors group-hover:bg-brand-500/20">
<UIcon name="i-lucide-phone" class="text-brand-600 dark:text-brand-400" />
</div>
<span class="text-gray-600 dark:text-gray-300 group-hover:text-brand-500 transition-colors">{{ siteConfig.contact.phone }}</span>
</a>
<div class="flex items-center gap-4">
<div class="flex items-center gap-4 p-3">
<div class="w-10 h-10 rounded-xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center shrink-0">
<UIcon name="i-lucide-map-pin" class="text-brand-600 dark:text-brand-400" />
</div>
<span class="text-gray-600 dark:text-gray-300">{{ siteConfig.contact.location }}</span>
<span class="text-gray-600 dark:text-gray-300 font-medium">{{ siteConfig.contact.location }}</span>
</div>
</div>
</div>
<!-- Social Links -->
<div class="rounded-2xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-6 sm:p-8">
<h2 class="text-xl font-bold text-gray-900 dark:text-white mb-6">{{ t('contact.findMeOn') }}</h2>
<div class="flex flex-col gap-3">
<div class="rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/80 dark:bg-gray-900/60 backdrop-blur-sm p-7 sm:p-8">
<h2 class="text-xl font-bold text-gray-900 dark:text-white mb-6 flex items-center gap-3">
<div class="w-1 h-5 rounded-full bg-brand-500" />
{{ t('contact.findMeOn') }}
</h2>
<div class="flex flex-col gap-2">
<a
v-for="social in siteConfig.social.filter(s => s.icon !== 'i-lucide-mail')"
:key="social.name"
:href="social.url"
target="_blank"
rel="noopener noreferrer"
class="flex items-center gap-4 p-3 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors group"
class="flex items-center gap-4 p-3 rounded-xl border border-transparent hover:border-brand-500/20 hover:bg-brand-500/5 transition-all duration-200 group"
>
<div class="w-10 h-10 rounded-xl bg-gray-100 dark:bg-gray-800 flex items-center justify-center shrink-0 group-hover:bg-brand-500/10 transition-colors">
<div class="w-10 h-10 rounded-xl bg-gray-100 dark:bg-gray-800/80 border border-gray-200/50 dark:border-gray-700/30 flex items-center justify-center shrink-0 group-hover:bg-brand-500/10 group-hover:border-brand-500/20 transition-all duration-300">
<UIcon :name="social.icon" class="text-gray-500 dark:text-gray-400 group-hover:text-brand-500 transition-colors" />
</div>
<span class="text-gray-700 dark:text-gray-300 font-medium group-hover:text-brand-500 transition-colors">{{ social.name }}</span>
<UIcon name="i-lucide-external-link" class="text-xs text-gray-400 dark:text-gray-600 ml-auto" />
<UIcon name="i-lucide-external-link" class="text-xs text-gray-400 dark:text-gray-600 ml-auto group-hover:text-brand-400 transition-colors" />
</a>
</div>
</div>
@@ -117,38 +120,40 @@ useSeoMeta({
</section>
<!-- FAQ Info Cards -->
<section class="py-16 md:py-20 px-4 bg-gray-50 dark:bg-gray-900/30">
<div class="max-w-6xl mx-auto">
<div class="text-center mb-14">
<span class="text-sm font-semibold text-brand-500 dark:text-brand-400 uppercase tracking-wider">Info</span>
<h2 class="text-3xl sm:text-4xl font-bold mt-2 text-gray-900 dark:text-white">{{ t('contact.faq.title') }}</h2>
<p class="text-lg text-gray-500 dark:text-gray-400 mt-3 max-w-2xl mx-auto">
<section class="relative py-20 md:py-28 px-4 sm:px-6 lg:px-8 overflow-hidden">
<div class="absolute inset-0 bg-gray-50/80 dark:bg-gray-900/40" aria-hidden="true" />
<div class="relative z-10 max-w-6xl mx-auto">
<div class="text-center mb-16">
<span class="font-mono text-sm text-brand-500 dark:text-brand-400 tracking-wider">// info</span>
<h2 class="text-3xl sm:text-4xl lg:text-5xl font-bold mt-3 bg-gradient-to-r from-gray-900 via-gray-800 to-gray-600 dark:from-white dark:via-gray-200 dark:to-gray-500 bg-clip-text text-transparent">{{ t('contact.faq.title') }}</h2>
<p class="text-lg text-gray-500 dark:text-gray-400 mt-4 max-w-2xl mx-auto leading-relaxed">
{{ t('contact.faq.subtitle') }}
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="rounded-2xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-8 text-center group hover:border-brand-500/30 transition-all duration-300">
<div class="w-14 h-14 rounded-2xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center mx-auto mb-5 group-hover:bg-brand-500/20 transition-colors">
<div class="grid grid-cols-1 md:grid-cols-3 gap-5">
<div class="group rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/80 dark:bg-gray-900/60 backdrop-blur-sm p-8 text-center transition-all duration-300 hover:border-brand-500/40 hover:shadow-xl hover:shadow-brand-500/10 hover:-translate-y-1">
<div class="w-14 h-14 rounded-2xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center mx-auto mb-5 transition-all duration-300 group-hover:bg-brand-500/20 group-hover:scale-110">
<UIcon name="i-lucide-clock" class="text-2xl text-brand-600 dark:text-brand-400" />
</div>
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-2">{{ t('contact.faq.responseTime.title') }}</h3>
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-3 group-hover:text-brand-600 dark:group-hover:text-brand-400 transition-colors">{{ t('contact.faq.responseTime.title') }}</h3>
<p class="text-gray-500 dark:text-gray-400 text-sm leading-relaxed">{{ t('contact.faq.responseTime.description') }}</p>
</div>
<div class="rounded-2xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-8 text-center group hover:border-brand-500/30 transition-all duration-300">
<div class="w-14 h-14 rounded-2xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center mx-auto mb-5 group-hover:bg-brand-500/20 transition-colors">
<div class="group rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/80 dark:bg-gray-900/60 backdrop-blur-sm p-8 text-center transition-all duration-300 hover:border-brand-500/40 hover:shadow-xl hover:shadow-brand-500/10 hover:-translate-y-1">
<div class="w-14 h-14 rounded-2xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center mx-auto mb-5 transition-all duration-300 group-hover:bg-brand-500/20 group-hover:scale-110">
<UIcon name="i-lucide-building" class="text-2xl text-brand-600 dark:text-brand-400" />
</div>
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-2">{{ t('contact.faq.projectTypes.title') }}</h3>
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-3 group-hover:text-brand-600 dark:group-hover:text-brand-400 transition-colors">{{ t('contact.faq.projectTypes.title') }}</h3>
<p class="text-gray-500 dark:text-gray-400 text-sm leading-relaxed">{{ t('contact.faq.projectTypes.description') }}</p>
</div>
<div class="rounded-2xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-8 text-center group hover:border-brand-500/30 transition-all duration-300">
<div class="w-14 h-14 rounded-2xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center mx-auto mb-5 group-hover:bg-brand-500/20 transition-colors">
<div class="group rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/80 dark:bg-gray-900/60 backdrop-blur-sm p-8 text-center transition-all duration-300 hover:border-brand-500/40 hover:shadow-xl hover:shadow-brand-500/10 hover:-translate-y-1">
<div class="w-14 h-14 rounded-2xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center mx-auto mb-5 transition-all duration-300 group-hover:bg-brand-500/20 group-hover:scale-110">
<UIcon name="i-lucide-users" class="text-2xl text-brand-600 dark:text-brand-400" />
</div>
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-2">{{ t('contact.faq.collaboration.title') }}</h3>
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-3 group-hover:text-brand-600 dark:group-hover:text-brand-400 transition-colors">{{ t('contact.faq.collaboration.title') }}</h3>
<p class="text-gray-500 dark:text-gray-400 text-sm leading-relaxed">{{ t('contact.faq.collaboration.description') }}</p>
</div>
</div>
+35 -47
View File
@@ -33,21 +33,24 @@ const heroStats = computed(() => [
<template>
<div>
<!-- Hero Section -->
<section class="pt-16 pb-16 px-4 bg-gray-50 dark:bg-gray-900/30">
<div class="max-w-4xl mx-auto text-center">
<span class="text-sm font-semibold text-brand-500 dark:text-brand-400 uppercase tracking-wider">Fiverr</span>
<h1 class="text-4xl sm:text-5xl font-bold mt-2 mb-6 text-gray-900 dark:text-white">
<section class="relative pt-20 pb-16 px-4 sm:px-6 lg:px-8 overflow-hidden">
<div class="absolute inset-0 bg-gray-50/80 dark:bg-gray-900/40" aria-hidden="true" />
<div class="absolute top-0 right-0 w-[600px] h-[600px] bg-brand-500/5 dark:bg-brand-500/8 rounded-full blur-3xl -translate-y-1/2 translate-x-1/4 pointer-events-none" aria-hidden="true" />
<div class="relative z-10 max-w-4xl mx-auto text-center">
<span class="font-mono text-sm text-brand-500 dark:text-brand-400 tracking-wider">// fiverr</span>
<h1 class="text-4xl sm:text-5xl lg:text-6xl font-bold mt-3 mb-6 bg-gradient-to-r from-gray-900 via-gray-800 to-gray-600 dark:from-white dark:via-gray-200 dark:to-gray-500 bg-clip-text text-transparent">
{{ t('fiverr.title') }}
</h1>
<p class="text-xl text-gray-500 dark:text-gray-400 mb-10 max-w-2xl mx-auto">
<p class="text-xl text-gray-500 dark:text-gray-400 mb-12 max-w-2xl mx-auto leading-relaxed">
{{ t('fiverr.subtitle') }}
</p>
<!-- Stats -->
<div class="flex flex-wrap justify-center gap-10 mb-10">
<div class="flex flex-wrap justify-center gap-8 sm:gap-12 mb-12">
<div v-for="stat in heroStats" :key="stat.label" class="text-center">
<div class="text-4xl font-extrabold text-brand-500 dark:text-brand-400">{{ stat.number }}</div>
<div class="text-sm text-gray-500 dark:text-gray-400 mt-1">{{ stat.label }}</div>
<div class="text-4xl sm:text-5xl font-black bg-gradient-to-b from-brand-400 to-brand-600 bg-clip-text text-transparent">{{ stat.number }}</div>
<div class="text-sm text-gray-500 dark:text-gray-400 mt-2 font-medium">{{ stat.label }}</div>
</div>
</div>
@@ -65,19 +68,19 @@ const heroStats = computed(() => [
</section>
<!-- Services Section -->
<section class="py-20 md:py-28 px-4">
<section class="py-24 md:py-32 px-4 sm:px-6 lg:px-8">
<div class="max-w-7xl mx-auto">
<div class="text-center mb-14">
<span class="text-sm font-semibold text-brand-500 dark:text-brand-400 uppercase tracking-wider">Services</span>
<h2 class="text-3xl sm:text-4xl font-bold mt-2 text-gray-900 dark:text-white">{{ t('fiverr.services.title') }}</h2>
<p class="text-lg text-gray-500 dark:text-gray-400 mt-3">{{ t('fiverr.services.subtitle') }}</p>
<div class="text-center mb-16">
<span class="font-mono text-sm text-brand-500 dark:text-brand-400 tracking-wider">// services</span>
<h2 class="text-3xl sm:text-4xl lg:text-5xl font-bold mt-3 bg-gradient-to-r from-gray-900 via-gray-800 to-gray-600 dark:from-white dark:via-gray-200 dark:to-gray-500 bg-clip-text text-transparent">{{ t('fiverr.services.title') }}</h2>
<p class="text-lg text-gray-500 dark:text-gray-400 mt-4 leading-relaxed">{{ t('fiverr.services.subtitle') }}</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 lg:gap-8">
<div class="grid grid-cols-1 md:grid-cols-2 gap-5 lg:gap-6">
<div
v-for="service in services"
:key="service.id"
class="group rounded-2xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 overflow-hidden transition-all duration-300 hover:border-brand-500/30 hover:shadow-xl hover:shadow-brand-500/5 hover:-translate-y-1"
class="group rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/80 dark:bg-gray-900/60 backdrop-blur-sm overflow-hidden transition-all duration-300 hover:border-brand-500/40 hover:shadow-xl hover:shadow-brand-500/10 hover:-translate-y-1.5"
>
<!-- Service Image -->
<div class="relative overflow-hidden">
@@ -87,10 +90,10 @@ const heroStats = computed(() => [
class="w-full h-52 object-cover transition-transform duration-500 group-hover:scale-105"
loading="lazy"
/>
<div class="absolute inset-0 bg-gradient-to-t from-black/40 via-transparent to-transparent" />
<div class="absolute inset-0 bg-gradient-to-t from-black/60 via-black/20 to-transparent" />
<!-- Price badge overlay -->
<div class="absolute bottom-3 left-3">
<span class="px-3 py-1.5 rounded-lg bg-brand-500 text-white text-sm font-bold shadow-lg">
<span class="px-3 py-1.5 rounded-lg bg-brand-500 text-white text-sm font-bold shadow-lg backdrop-blur-sm">
{{ t('fiverr.pricing.startingAt') }} {{ service.price }}
</span>
</div>
@@ -98,8 +101,8 @@ const heroStats = computed(() => [
<div class="absolute top-3 right-3">
<span
:class="service.url !== '#'
? 'bg-green-500/90 text-white'
: 'bg-yellow-500/90 text-white'"
? 'bg-green-500/90 text-white backdrop-blur-sm'
: 'bg-yellow-500/90 text-white backdrop-blur-sm'"
class="px-2.5 py-1 rounded-lg text-xs font-semibold shadow-lg"
>
{{ service.url !== '#' ? t('fiverr.services.available') : t('fiverr.services.comingSoon') }}
@@ -108,11 +111,11 @@ const heroStats = computed(() => [
</div>
<!-- Content -->
<div class="p-6">
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-2 group-hover:text-brand-500 transition-colors">
<div class="p-6 sm:p-7">
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-3 group-hover:text-brand-600 dark:group-hover:text-brand-400 transition-colors">
{{ t(`fiverr.serviceData.${service.id}.title`) }}
</h3>
<p class="text-gray-500 dark:text-gray-400 mb-5 leading-relaxed">
<p class="text-gray-500 dark:text-gray-400 mb-6 leading-relaxed">
{{ t(`fiverr.serviceData.${service.id}.description`) }}
</p>
@@ -142,7 +145,7 @@ const heroStats = computed(() => [
</section>
<!-- FAQ Section -->
<div class="bg-gray-50 dark:bg-gray-900/30">
<div class="relative bg-gray-50/50 dark:bg-gray-900/20">
<FAQSection
:faqs="homeFAQs"
:title="t('fiverr.faq.title')"
@@ -151,29 +154,14 @@ const heroStats = computed(() => [
</div>
<!-- CTA Section -->
<section class="py-20 md:py-28 px-4">
<div class="max-w-5xl mx-auto">
<div class="relative overflow-hidden rounded-3xl bg-gradient-to-br from-brand-600 via-brand-500 to-emerald-500 px-8 py-16 sm:px-16 sm:py-20 text-center">
<div class="absolute top-0 left-0 w-72 h-72 bg-white/10 rounded-full -translate-x-1/2 -translate-y-1/2 blur-2xl" aria-hidden="true" />
<div class="absolute bottom-0 right-0 w-96 h-96 bg-black/10 rounded-full translate-x-1/3 translate-y-1/3 blur-2xl" aria-hidden="true" />
<div class="relative z-10">
<h2 class="text-3xl sm:text-4xl font-bold text-white mb-4">{{ t('fiverr.cta.title') }}</h2>
<p class="text-lg text-white/80 mb-10 max-w-2xl mx-auto">{{ t('fiverr.cta.subtitle') }}</p>
<UButton
:to="siteConfig.fiverr.profileUrl"
target="_blank"
external
size="xl"
color="white"
trailing-icon="i-lucide-external-link"
class="font-semibold"
>
{{ t('fiverr.cta.button') }}
</UButton>
</div>
</div>
</div>
</section>
<CTASection
:title="t('fiverr.cta.title')"
:subtitle="t('fiverr.cta.subtitle')"
:primary-text="t('fiverr.cta.button')"
:primary-to="siteConfig.fiverr.profileUrl"
:secondary-text="t('fiverr.profileCta')"
secondary-to="/contact"
external
/>
</div>
</template>
+9 -13
View File
@@ -23,7 +23,7 @@ useHead({
'@graph': [
{
'@type': 'Person',
name: 'Killian Dalcin',
name: "Killian' DAL-CIN",
url: 'https://killiandalcin.fr',
jobTitle: 'Developpeur Full Stack Freelance',
email: 'contact@killiandalcin.fr',
@@ -35,14 +35,14 @@ useHead({
},
{
'@type': 'ProfessionalService',
name: 'Killian Dalcin - Developpeur Full Stack',
name: "Killian' DAL-CIN - Developpeur Full Stack",
url: 'https://killiandalcin.fr',
logo: 'https://killiandalcin.fr/images/logo.webp',
priceRange: '$$$',
areaServed: 'Worldwide',
logo: 'https://killiandalcin.fr/images/logo.webp',
priceRange: '$$$',
areaServed: 'Worldwide',
},
],
}),
}),
},
],
})
@@ -54,7 +54,7 @@ useHead({
<HeroSection />
<!-- Featured Projects Section -->
<div class="bg-gray-50 dark:bg-gray-900/30">
<div class="relative bg-gray-50/50 dark:bg-gray-900/20">
<FeaturedProjectsSection />
</div>
@@ -62,16 +62,12 @@ useHead({
<ServicesSection />
<!-- Testimonials Section -->
<div class="bg-gray-50 dark:bg-gray-900/30">
<div class="relative bg-gray-50/50 dark:bg-gray-900/20">
<TestimonialsSection />
</div>
<!-- FAQ Section -->
<FAQSection
:faqs="homeFAQs"
:title="t('faq.title')"
:subtitle="t('faq.subtitle')"
/>
<FAQSection :faqs="homeFAQs" :title="t('faq.title')" :subtitle="t('faq.subtitle')" />
<!-- CTA Section -->
<CTASection />
+117 -99
View File
@@ -30,107 +30,113 @@ useSeoMeta({
<template>
<div v-if="project">
<!-- Back navigation -->
<div class="bg-gray-50 dark:bg-gray-900/30 border-b border-gray-200 dark:border-gray-800">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
<UButton
variant="ghost"
icon="i-lucide-arrow-left"
to="/projects"
size="sm"
class="text-gray-500 hover:text-gray-900 dark:hover:text-white"
>
{{ t('projects.projectDetail.backToProjects') }}
</UButton>
</div>
</div>
<!-- Full-width hero image -->
<section class="relative overflow-hidden">
<!-- Hero image with overlay -->
<div class="relative h-[40vh] sm:h-[50vh] lg:h-[60vh]">
<NuxtImg
v-if="project.image"
:src="project.image"
:alt="project.title"
class="w-full h-full object-cover"
format="webp"
loading="eager"
/>
<!-- Gradient overlay -->
<div class="absolute inset-0 bg-gradient-to-t from-white via-white/40 to-transparent dark:from-gray-950 dark:via-gray-950/40 dark:to-transparent" />
<div class="absolute inset-0 bg-gradient-to-r from-white/60 to-transparent dark:from-gray-950/60 dark:to-transparent" />
<!-- Hero section -->
<section class="bg-gray-50 dark:bg-gray-900/30 pb-16 pt-8">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-10 lg:gap-16 items-center">
<!-- Project Image -->
<div class="rounded-2xl overflow-hidden border border-gray-200 dark:border-gray-800 shadow-lg">
<NuxtImg
v-if="project.image"
:src="project.image"
:alt="project.title"
class="w-full h-auto object-cover"
format="webp"
loading="lazy"
/>
</div>
<!-- Back button (floating) -->
<div class="absolute top-6 left-4 sm:left-6 lg:left-8 z-20">
<UButton
variant="solid"
color="neutral"
icon="i-lucide-arrow-left"
to="/projects"
size="sm"
class="shadow-lg backdrop-blur-sm"
>
{{ t('projects.projectDetail.backToProjects') }}
</UButton>
</div>
<!-- Project Info -->
<div class="flex flex-col justify-center space-y-6">
<div class="flex items-center gap-3">
<!-- Title overlay at bottom -->
<div class="absolute bottom-0 left-0 right-0 z-10 px-4 sm:px-6 lg:px-8 pb-10">
<div class="max-w-7xl mx-auto">
<div class="flex items-center gap-3 mb-4">
<UBadge v-if="project.category" variant="subtle" size="md">{{ project.category }}</UBadge>
<span v-if="project.date" class="text-sm text-gray-400 dark:text-gray-500 font-medium">{{ project.date }}</span>
</div>
<h1 class="text-3xl sm:text-4xl font-bold text-gray-900 dark:text-white">{{ project.title }}</h1>
<p class="text-lg text-gray-500 dark:text-gray-400 leading-relaxed">{{ project.description }}</p>
<!-- CTA Buttons -->
<div class="flex flex-wrap gap-3 pt-2">
<UButton
v-if="project.demoUrl"
:to="project.demoUrl"
target="_blank"
icon="i-lucide-external-link"
size="lg"
class="font-semibold"
>
{{ t('projects.projectDetail.viewDemo') }}
</UButton>
<UButton
v-if="project.githubUrl"
:to="project.githubUrl"
target="_blank"
variant="soft"
icon="i-lucide-github"
size="lg"
>
{{ t('projects.projectDetail.sourceCode') }}
</UButton>
<UButton
v-for="button in project.buttons"
:key="button.title"
:to="button.link"
target="_blank"
variant="outline"
icon="i-lucide-external-link"
size="lg"
>
{{ button.title }}
</UButton>
<span v-if="project.date" class="text-sm text-gray-500 dark:text-gray-400 font-mono">{{ project.date }}</span>
</div>
<h1 class="text-3xl sm:text-4xl lg:text-5xl font-bold text-gray-900 dark:text-white max-w-3xl tracking-tight">{{ project.title }}</h1>
</div>
</div>
</div>
</section>
<!-- Content -->
<section class="py-16 px-4">
<!-- Content area -->
<section class="py-12 md:py-16 px-4 sm:px-6 lg:px-8">
<div class="max-w-7xl mx-auto">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-10 lg:gap-16">
<!-- Main Content -->
<div class="lg:col-span-2 space-y-16">
<!-- About -->
<div class="lg:col-span-2 space-y-14">
<!-- Description -->
<div>
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-5">{{ t('projects.projectDetail.aboutProject') }}</h2>
<p class="text-lg sm:text-xl text-gray-600 dark:text-gray-300 leading-relaxed mb-8">{{ project.description }}</p>
<!-- CTA Buttons -->
<div class="flex flex-wrap gap-3">
<UButton
v-if="project.demoUrl"
:to="project.demoUrl"
target="_blank"
icon="i-lucide-external-link"
size="lg"
class="font-semibold"
>
{{ t('projects.projectDetail.viewDemo') }}
</UButton>
<UButton
v-if="project.githubUrl"
:to="project.githubUrl"
target="_blank"
variant="soft"
icon="i-lucide-github"
size="lg"
>
{{ t('projects.projectDetail.sourceCode') }}
</UButton>
<UButton
v-for="button in project.buttons"
:key="button.title"
:to="button.link"
target="_blank"
variant="outline"
icon="i-lucide-external-link"
size="lg"
>
{{ button.title }}
</UButton>
</div>
</div>
<!-- About -->
<div class="rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/60 dark:bg-gray-900/40 backdrop-blur-sm p-7 sm:p-8">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-5 flex items-center gap-3">
<div class="w-1 h-6 rounded-full bg-brand-500" />
{{ t('projects.projectDetail.aboutProject') }}
</h2>
<p class="text-gray-500 dark:text-gray-400 leading-relaxed text-lg">
{{ project.longDescription || project.description }}
</p>
<!-- Features -->
<div v-if="project.features" class="mt-8">
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-4">{{ t('projects.projectDetail.keyFeatures') }}</h3>
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-5">{{ t('projects.projectDetail.keyFeatures') }}</h3>
<ul class="space-y-3">
<li v-for="feature in project.features" :key="feature" class="flex items-start gap-3">
<div class="w-6 h-6 rounded-full bg-brand-500/10 flex items-center justify-center shrink-0 mt-0.5">
<li v-for="feature in project.features" :key="feature" class="flex items-start gap-3 group">
<div class="w-6 h-6 rounded-lg bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center shrink-0 mt-0.5 group-hover:bg-brand-500/20 transition-colors">
<UIcon name="i-lucide-check" class="text-brand-500 w-3.5 h-3.5" />
</div>
<span class="text-gray-600 dark:text-gray-300">{{ feature }}</span>
@@ -140,27 +146,33 @@ useSeoMeta({
</div>
<!-- Technologies -->
<div v-if="project.technologies.length">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-5">{{ t('projects.projectDetail.technologiesUsed') }}</h2>
<div v-if="project.technologies.length" class="rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/60 dark:bg-gray-900/40 backdrop-blur-sm p-7 sm:p-8">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-5 flex items-center gap-3">
<div class="w-1 h-6 rounded-full bg-brand-500" />
{{ t('projects.projectDetail.technologiesUsed') }}
</h2>
<div class="flex flex-wrap gap-2">
<TechBadge v-for="tech in project.technologies" :key="tech" :tech="tech" />
</div>
</div>
<!-- Gallery Thumbnails -->
<div v-if="project.gallery?.length">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-5">{{ t('projects.projectDetail.gallery') }}</h2>
<div class="grid grid-cols-2 sm:grid-cols-3 gap-4">
<div v-if="project.gallery?.length" class="rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/60 dark:bg-gray-900/40 backdrop-blur-sm p-7 sm:p-8">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-5 flex items-center gap-3">
<div class="w-1 h-6 rounded-full bg-brand-500" />
{{ t('projects.projectDetail.gallery') }}
</h2>
<div class="grid grid-cols-2 sm:grid-cols-3 gap-3">
<button
v-for="(image, index) in project.gallery"
:key="index"
class="relative rounded-xl overflow-hidden group cursor-pointer border border-gray-200 dark:border-gray-800"
class="relative rounded-xl overflow-hidden group cursor-pointer border border-gray-200/80 dark:border-gray-800/50 aspect-video"
@click="galleryRef?.openGallery(index)"
>
<NuxtImg
:src="image"
:alt="`${project.title} - Image ${index + 1}`"
class="w-full h-32 object-cover transition-transform duration-300 group-hover:scale-105"
class="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
loading="lazy"
format="webp"
/>
@@ -173,17 +185,20 @@ useSeoMeta({
</div>
<!-- Sidebar -->
<aside class="space-y-6">
<aside class="sticky top-24 space-y-6">
<!-- Project Info Card -->
<div class="rounded-2xl border border-gray-200 dark:border-gray-800 bg-gray-50 dark:bg-gray-900 p-6 sticky top-24">
<h3 class="font-bold text-gray-900 dark:text-white mb-5">{{ t('projects.projectDetail.projectInfo') }}</h3>
<div class="rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/60 dark:bg-gray-900/40 backdrop-blur-sm p-6">
<h3 class="font-bold text-gray-900 dark:text-white mb-5 flex items-center gap-2">
<UIcon name="i-lucide-info" class="text-brand-500 w-4 h-4" />
{{ t('projects.projectDetail.projectInfo') }}
</h3>
<div class="space-y-4 text-sm">
<div v-if="project.date" class="flex justify-between items-center py-2 border-b border-gray-200 dark:border-gray-800">
<div v-if="project.date" class="flex justify-between items-center py-3 border-b border-gray-200/60 dark:border-gray-800/40">
<span class="text-gray-500 dark:text-gray-400">{{ t('projects.projectDetail.date') }}</span>
<span class="font-semibold text-gray-900 dark:text-white">{{ project.date }}</span>
<span class="font-semibold text-gray-900 dark:text-white font-mono text-xs">{{ project.date }}</span>
</div>
<div v-if="project.category" class="flex justify-between items-center py-2">
<div v-if="project.category" class="flex justify-between items-center py-3">
<span class="text-gray-500 dark:text-gray-400">{{ t('projects.projectDetail.category') }}</span>
<UBadge variant="subtle" size="xs">{{ project.category }}</UBadge>
</div>
@@ -191,14 +206,17 @@ useSeoMeta({
</div>
<!-- Related Projects -->
<div v-if="relatedProjects.length > 0" class="rounded-2xl border border-gray-200 dark:border-gray-800 bg-gray-50 dark:bg-gray-900 p-6">
<h3 class="font-bold text-gray-900 dark:text-white mb-5">{{ t('projects.projectDetail.relatedProjects') }}</h3>
<div class="space-y-4">
<div v-if="relatedProjects.length > 0" class="rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/60 dark:bg-gray-900/40 backdrop-blur-sm p-6">
<h3 class="font-bold text-gray-900 dark:text-white mb-5 flex items-center gap-2">
<UIcon name="i-lucide-layers" class="text-brand-500 w-4 h-4" />
{{ t('projects.projectDetail.relatedProjects') }}
</h3>
<div class="space-y-3">
<NuxtLink
v-for="related in relatedProjects"
:key="related.id"
:to="`/project/${related.id}`"
class="flex gap-3 p-3 rounded-xl hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors group"
class="flex gap-3 p-3 rounded-xl border border-transparent hover:border-brand-500/20 hover:bg-brand-500/5 transition-all duration-200 group"
>
<NuxtImg
v-if="related.image"
@@ -211,7 +229,7 @@ useSeoMeta({
/>
<div class="min-w-0">
<p class="font-semibold text-sm text-gray-900 dark:text-white truncate group-hover:text-brand-500 transition-colors">{{ related.title }}</p>
<p class="text-xs text-gray-500 dark:text-gray-400 line-clamp-2 mt-0.5">{{ related.description }}</p>
<p class="text-xs text-gray-500 dark:text-gray-400 line-clamp-2 mt-1">{{ related.description }}</p>
</div>
</NuxtLink>
</div>
+25 -22
View File
@@ -53,37 +53,40 @@ function resetFilters() {
<template>
<div>
<!-- Hero -->
<section class="pt-16 pb-12 px-4 bg-gray-50 dark:bg-gray-900/30">
<div class="max-w-7xl mx-auto text-center">
<span class="text-sm font-semibold text-brand-500 dark:text-brand-400 uppercase tracking-wider">Portfolio</span>
<h1 class="text-4xl sm:text-5xl font-bold mt-2 mb-4 text-gray-900 dark:text-white">{{ t('projects.title') }}</h1>
<p class="text-lg text-gray-500 dark:text-gray-400 max-w-2xl mx-auto">{{ t('projects.subtitle') }}</p>
<section class="relative pt-20 pb-16 px-4 sm:px-6 lg:px-8 overflow-hidden">
<div class="absolute inset-0 bg-gray-50/80 dark:bg-gray-900/40" aria-hidden="true" />
<div class="absolute top-0 left-1/2 -translate-x-1/2 w-[800px] h-[400px] bg-brand-500/5 dark:bg-brand-500/8 rounded-full blur-3xl pointer-events-none" aria-hidden="true" />
<div class="relative z-10 max-w-7xl mx-auto text-center">
<span class="font-mono text-sm text-brand-500 dark:text-brand-400 tracking-wider">// portfolio</span>
<h1 class="text-4xl sm:text-5xl lg:text-6xl font-bold mt-3 mb-5 bg-gradient-to-r from-gray-900 via-gray-800 to-gray-600 dark:from-white dark:via-gray-200 dark:to-gray-500 bg-clip-text text-transparent">{{ t('projects.title') }}</h1>
<p class="text-lg sm:text-xl text-gray-500 dark:text-gray-400 max-w-2xl mx-auto leading-relaxed">{{ t('projects.subtitle') }}</p>
<!-- Stats -->
<div class="flex justify-center gap-10 mt-10">
<div class="flex justify-center gap-8 sm:gap-12 mt-12">
<div class="text-center">
<p class="text-3xl font-extrabold text-brand-500 dark:text-brand-400">{{ totalProjects }}</p>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">{{ t('nav.projects') }}</p>
<p class="text-3xl sm:text-4xl font-black bg-gradient-to-b from-brand-400 to-brand-600 bg-clip-text text-transparent">{{ totalProjects }}</p>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-2 font-medium">{{ t('nav.projects') }}</p>
</div>
<div class="w-px bg-gray-200 dark:bg-gray-800" />
<div class="w-px bg-gradient-to-b from-transparent via-gray-300 dark:via-gray-700 to-transparent" />
<div class="text-center">
<p class="text-3xl font-extrabold text-brand-500 dark:text-brand-400">{{ featuredCount }}</p>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">{{ t('home.featuredProjects.title') }}</p>
<p class="text-3xl sm:text-4xl font-black bg-gradient-to-b from-brand-400 to-brand-600 bg-clip-text text-transparent">{{ featuredCount }}</p>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-2 font-medium">{{ t('home.featuredProjects.title') }}</p>
</div>
<div class="w-px bg-gray-200 dark:bg-gray-800" />
<div class="w-px bg-gradient-to-b from-transparent via-gray-300 dark:via-gray-700 to-transparent" />
<div class="text-center">
<p class="text-3xl font-extrabold text-brand-500 dark:text-brand-400">{{ categories.length - 1 }}</p>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">{{ t('projects.categories.all') }}</p>
<p class="text-3xl sm:text-4xl font-black bg-gradient-to-b from-brand-400 to-brand-600 bg-clip-text text-transparent">{{ categories.length - 1 }}</p>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-2 font-medium">{{ t('projects.categories.all') }}</p>
</div>
</div>
</div>
</section>
<!-- Filters & Grid -->
<section class="py-12 px-4">
<section class="py-16 md:py-20 px-4 sm:px-6 lg:px-8">
<div class="max-w-7xl mx-auto">
<!-- Filter bar -->
<div class="flex flex-col sm:flex-row gap-4 items-start sm:items-center mb-10 p-4 rounded-2xl bg-gray-50 dark:bg-gray-900/50 border border-gray-200 dark:border-gray-800">
<div class="flex flex-col sm:flex-row gap-4 items-start sm:items-center mb-12 p-4 sm:p-5 rounded-2xl border border-gray-200/80 dark:border-gray-800/50 bg-white/60 dark:bg-gray-900/40 backdrop-blur-sm">
<UInput
v-model="searchQuery"
icon="i-lucide-search"
@@ -108,18 +111,18 @@ function resetFilters() {
</div>
<!-- Projects Grid -->
<div v-if="filteredProjects.length > 0" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 lg:gap-8">
<div v-if="filteredProjects.length > 0" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 lg:gap-6">
<ProjectCard v-for="project in filteredProjects" :key="project.id" :project="project" />
</div>
<!-- Empty State -->
<div v-else class="text-center py-24">
<div class="w-16 h-16 mx-auto mb-6 rounded-2xl bg-gray-100 dark:bg-gray-800 flex items-center justify-center">
<div v-else class="text-center py-32">
<div class="w-16 h-16 mx-auto mb-6 rounded-2xl bg-gray-100 dark:bg-gray-800/60 border border-gray-200/80 dark:border-gray-700/30 flex items-center justify-center">
<UIcon name="i-lucide-search-x" class="text-2xl text-gray-400" />
</div>
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-2">{{ t('projects.noResults.title') }}</h3>
<p class="text-gray-500 dark:text-gray-400 mb-8 max-w-md mx-auto">{{ t('projects.noResults.description') }}</p>
<UButton @click="resetFilters" variant="soft" size="md">
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-3">{{ t('projects.noResults.title') }}</h3>
<p class="text-gray-500 dark:text-gray-400 mb-8 max-w-md mx-auto leading-relaxed">{{ t('projects.noResults.description') }}</p>
<UButton @click="resetFilters" variant="soft" size="md" icon="i-lucide-rotate-ccw">
{{ t('common.reset') }}
</UButton>
</div>