feat: add project files
This commit is contained in:
+53
@@ -0,0 +1,53 @@
|
|||||||
|
# 1. Base image
|
||||||
|
FROM node:20-alpine AS base
|
||||||
|
|
||||||
|
# 2. Dependencies
|
||||||
|
FROM base AS deps
|
||||||
|
RUN apk add --no-cache libc6-compat
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
COPY package.json package-lock.json* ./
|
||||||
|
RUN npm ci --legacy-peer-deps
|
||||||
|
|
||||||
|
|
||||||
|
# 3. Builder
|
||||||
|
FROM base AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Environment variables must be present at build time for Next.js
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# 4. Runner
|
||||||
|
FROM base AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
|
||||||
|
# Set the correct permission for prerender cache
|
||||||
|
RUN mkdir .next
|
||||||
|
RUN chown nextjs:nodejs .next
|
||||||
|
|
||||||
|
# Automatically leverage output traces to reduce image size
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
ENV PORT=3000
|
||||||
|
ENV HOSTNAME="0.0.0.0"
|
||||||
|
|
||||||
|
# Start the server
|
||||||
|
CMD ["node", "server.js"]
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import { Playfair_Display, Lato } from "next/font/google";
|
||||||
|
import "../globals.css";
|
||||||
|
|
||||||
|
const playfair = Playfair_Display({
|
||||||
|
variable: "--font-playfair",
|
||||||
|
subsets: ["latin"],
|
||||||
|
});
|
||||||
|
|
||||||
|
const lato = Lato({
|
||||||
|
variable: "--font-lato",
|
||||||
|
subsets: ["latin"],
|
||||||
|
weight: ["300", "400", "700", "900"],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Moy Beach Akyaka | Lüks & Huzur",
|
||||||
|
description: "Akyaka'nın kalbinde doğayla iç içe lüks bir beach, restoran ve otel deneyimi.",
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function generateStaticParams() {
|
||||||
|
return [{ lang: 'tr' }, { lang: 'en' }];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
params,
|
||||||
|
}: Readonly<{
|
||||||
|
children: React.ReactNode;
|
||||||
|
params: { lang: string };
|
||||||
|
}>) {
|
||||||
|
return (
|
||||||
|
<html lang={params.lang} className="scroll-smooth">
|
||||||
|
<body className={`${lato.variable} ${playfair.variable} antialiased`}>
|
||||||
|
{children}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import Header from "@/components/Header";
|
||||||
|
import Hero from "@/components/Hero";
|
||||||
|
import About from "@/components/About";
|
||||||
|
import Services from "@/components/Services";
|
||||||
|
import Gallery from "@/components/Gallery";
|
||||||
|
import InstagramFeed from "@/components/InstagramFeed";
|
||||||
|
import Contact from "@/components/Contact";
|
||||||
|
import Footer from "@/components/Footer";
|
||||||
|
import { getDictionary } from "../dictionaries";
|
||||||
|
|
||||||
|
export default async function Home({ params: { lang } }: { params: { lang: string } }) {
|
||||||
|
const dict = await getDictionary(lang);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="min-h-screen bg-brand-white">
|
||||||
|
<Header dict={dict} lang={lang} />
|
||||||
|
<Hero dict={dict} />
|
||||||
|
<About dict={dict} />
|
||||||
|
<Services dict={dict} />
|
||||||
|
<Gallery dict={dict} />
|
||||||
|
<InstagramFeed dict={dict} />
|
||||||
|
<Contact dict={dict} />
|
||||||
|
<Footer dict={dict} />
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import 'server-only';
|
||||||
|
|
||||||
|
const dictionaries: Record<string, () => Promise<any>> = {
|
||||||
|
tr: () => import('../dictionaries/tr.json').then((module) => module.default),
|
||||||
|
en: () => import('../dictionaries/en.json').then((module) => module.default),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDictionary = async (locale: string) => {
|
||||||
|
if (typeof dictionaries[locale] === 'function') {
|
||||||
|
return dictionaries[locale]();
|
||||||
|
}
|
||||||
|
return dictionaries['tr'](); // fallback
|
||||||
|
};
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 15 KiB |
+54
-14
@@ -1,26 +1,66 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
:root {
|
|
||||||
--background: #ffffff;
|
|
||||||
--foreground: #171717;
|
|
||||||
}
|
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
--color-background: var(--background);
|
--color-sand: #E3D9C6;
|
||||||
--color-foreground: var(--foreground);
|
--color-sand-light: #F4EFEB;
|
||||||
--font-sans: var(--font-geist-sans);
|
--color-sand-dark: #C2B49A;
|
||||||
--font-mono: var(--font-geist-mono);
|
|
||||||
|
--color-sea-blue: #2A4B7C;
|
||||||
|
--color-sea-light: #4A6B9C;
|
||||||
|
--color-sea-dark: #1A2B4C;
|
||||||
|
|
||||||
|
--color-coral: #FF6B6B;
|
||||||
|
--color-coral-light: #FF8B8B;
|
||||||
|
--color-coral-dark: #D94A4A;
|
||||||
|
|
||||||
|
--color-brand-white: #FFFFFF;
|
||||||
|
--color-brand-black: #171717;
|
||||||
|
|
||||||
|
--font-serif: var(--font-playfair);
|
||||||
|
--font-sans: var(--font-lato);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
:root {
|
||||||
--background: #0a0a0a;
|
--background: #FFFFFF;
|
||||||
--foreground: #ededed;
|
--foreground: #171717;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: var(--font-sans), sans-serif;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-family: var(--font-serif), serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Shimmer animation for CTA buttons */
|
||||||
|
@keyframes shimmer {
|
||||||
|
0% { transform: translateX(-150%); }
|
||||||
|
100% { transform: translateX(150%); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-shimmer {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-shimmer::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
transparent 0%,
|
||||||
|
rgba(255, 255, 255, 0.22) 50%,
|
||||||
|
transparent 100%
|
||||||
|
);
|
||||||
|
transform: translateX(-150%);
|
||||||
|
animation: shimmer 2.8s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
import type { Metadata } from "next";
|
|
||||||
import { Geist, Geist_Mono } from "next/font/google";
|
|
||||||
import "./globals.css";
|
|
||||||
|
|
||||||
const geistSans = Geist({
|
|
||||||
variable: "--font-geist-sans",
|
|
||||||
subsets: ["latin"],
|
|
||||||
});
|
|
||||||
|
|
||||||
const geistMono = Geist_Mono({
|
|
||||||
variable: "--font-geist-mono",
|
|
||||||
subsets: ["latin"],
|
|
||||||
});
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "Create Next App",
|
|
||||||
description: "Generated by create next app",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function RootLayout({
|
|
||||||
children,
|
|
||||||
}: Readonly<{
|
|
||||||
children: React.ReactNode;
|
|
||||||
}>) {
|
|
||||||
return (
|
|
||||||
<html
|
|
||||||
lang="en"
|
|
||||||
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
|
|
||||||
>
|
|
||||||
<body className="min-h-full flex flex-col">{children}</body>
|
|
||||||
</html>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import Image from "next/image";
|
|
||||||
|
|
||||||
export default function Home() {
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black">
|
|
||||||
<main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
|
|
||||||
<Image
|
|
||||||
className="dark:invert"
|
|
||||||
src="/next.svg"
|
|
||||||
alt="Next.js logo"
|
|
||||||
width={100}
|
|
||||||
height={20}
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
|
|
||||||
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
|
|
||||||
To get started, edit the page.tsx file.
|
|
||||||
</h1>
|
|
||||||
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
|
|
||||||
Looking for a starting point or more instructions? Head over to{" "}
|
|
||||||
<a
|
|
||||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
|
||||||
>
|
|
||||||
Templates
|
|
||||||
</a>{" "}
|
|
||||||
or the{" "}
|
|
||||||
<a
|
|
||||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
|
||||||
>
|
|
||||||
Learning
|
|
||||||
</a>{" "}
|
|
||||||
center.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
|
|
||||||
<a
|
|
||||||
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
|
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
className="dark:invert"
|
|
||||||
src="/vercel.svg"
|
|
||||||
alt="Vercel logomark"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
Deploy Now
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
|
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Documentation
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
export default function About({ dict }: { dict: any }) {
|
||||||
|
const stats = [
|
||||||
|
{ value: "5+", label: dict.about.stats.years },
|
||||||
|
{ value: "10K+", label: dict.about.stats.guests },
|
||||||
|
{ value: "4.8★", label: dict.about.stats.rating },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section id="about" className="py-24 bg-sand-light text-sea-dark overflow-hidden">
|
||||||
|
<div className="container mx-auto px-6 max-w-6xl">
|
||||||
|
<div className="grid md:grid-cols-2 gap-16 items-center">
|
||||||
|
|
||||||
|
{/* Text side */}
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, x: -50 }}
|
||||||
|
whileInView={{ opacity: 1, x: 0 }}
|
||||||
|
viewport={{ once: true, margin: "-100px" }}
|
||||||
|
transition={{ duration: 0.8 }}
|
||||||
|
className="space-y-6"
|
||||||
|
>
|
||||||
|
{/* Label with decorative lines */}
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="w-8 h-px bg-coral" />
|
||||||
|
<h2 className="text-sm font-bold tracking-widest uppercase text-coral">{dict.about.title}</h2>
|
||||||
|
<span className="w-8 h-px bg-coral" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 className="text-4xl md:text-5xl font-serif leading-tight" dangerouslySetInnerHTML={{ __html: dict.about.heading }}></h3>
|
||||||
|
|
||||||
|
<p className="text-base opacity-75 leading-relaxed">
|
||||||
|
{dict.about.p1}
|
||||||
|
</p>
|
||||||
|
<p className="text-base opacity-75 leading-relaxed">
|
||||||
|
{dict.about.p2}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Stats row */}
|
||||||
|
<div className="grid grid-cols-3 gap-6 pt-6 border-t border-sand-dark/40">
|
||||||
|
{stats.map((stat) => (
|
||||||
|
<div key={stat.label} className="text-center">
|
||||||
|
<div className="text-2xl md:text-3xl font-serif font-bold text-sea-dark">{stat.value}</div>
|
||||||
|
<div className="text-xs text-gray-500 uppercase tracking-wide mt-1">{stat.label}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
{/* Image side */}
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, x: 50 }}
|
||||||
|
whileInView={{ opacity: 1, x: 0 }}
|
||||||
|
viewport={{ once: true, margin: "-100px" }}
|
||||||
|
transition={{ duration: 0.8, delay: 0.2 }}
|
||||||
|
className="relative h-[520px] w-full"
|
||||||
|
>
|
||||||
|
{/* Shadow shape behind */}
|
||||||
|
<div className="absolute inset-0 bg-sand-dark/25 rounded-t-full rounded-br-full transform translate-x-5 translate-y-5" />
|
||||||
|
{/* Image clipped to shape */}
|
||||||
|
<div className="absolute inset-0 rounded-t-full rounded-br-full overflow-hidden shadow-2xl">
|
||||||
|
<Image
|
||||||
|
src="/moy-beach.jpg"
|
||||||
|
alt="Moy Beach Atmosphere"
|
||||||
|
fill
|
||||||
|
className="object-cover"
|
||||||
|
sizes="(max-width: 768px) 100vw, 50vw"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { MapPin, Phone, Mail, Clock } from "lucide-react";
|
||||||
|
|
||||||
|
export default function Contact({ dict }: { dict: any }) {
|
||||||
|
return (
|
||||||
|
<section id="contact" className="py-24 bg-sand-light text-sea-dark relative">
|
||||||
|
<div className="container mx-auto px-6 max-w-7xl">
|
||||||
|
<div className="text-center mb-16">
|
||||||
|
<h2 className="text-sm font-bold tracking-widest uppercase text-coral mb-4">{dict.contact.title}</h2>
|
||||||
|
<h3 className="text-4xl md:text-5xl font-serif">{dict.contact.heading}</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid lg:grid-cols-2 gap-12 items-start">
|
||||||
|
|
||||||
|
{/* İletişim Bilgileri */}
|
||||||
|
<div className="space-y-8">
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<div className="w-12 h-12 bg-white rounded-full flex items-center justify-center shrink-0 shadow-md">
|
||||||
|
<MapPin className="text-coral w-6 h-6" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="font-bold text-xl mb-2 font-serif">{dict.contact.address.title}</h4>
|
||||||
|
<p className="text-gray-600 leading-relaxed">
|
||||||
|
{dict.contact.address.text1}<br />
|
||||||
|
{dict.contact.address.text2}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<div className="w-12 h-12 bg-white rounded-full flex items-center justify-center shrink-0 shadow-md">
|
||||||
|
<Phone className="text-coral w-6 h-6" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="font-bold text-xl mb-2 font-serif">{dict.contact.phone}</h4>
|
||||||
|
<p className="text-gray-600">
|
||||||
|
<a href="tel:+905344656235" className="hover:text-coral transition-colors">+90 534 465 62 35</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<div className="w-12 h-12 bg-white rounded-full flex items-center justify-center shrink-0 shadow-md">
|
||||||
|
<Mail className="text-coral w-6 h-6" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="font-bold text-xl mb-2 font-serif">{dict.contact.email}</h4>
|
||||||
|
<p className="text-gray-600">
|
||||||
|
<a href="mailto:info@moybeachakyaka.com" className="hover:text-coral transition-colors">info@moybeachakyaka.com</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<div className="w-12 h-12 bg-white rounded-full flex items-center justify-center shrink-0 shadow-md">
|
||||||
|
<Clock className="text-coral w-6 h-6" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="font-bold text-xl mb-2 font-serif">{dict.contact.hours.title}</h4>
|
||||||
|
<p className="text-gray-600">
|
||||||
|
{dict.contact.hours.text}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Google Maps Embed */}
|
||||||
|
<div className="w-full h-[400px] lg:h-full min-h-[400px] bg-gray-200 rounded-2xl overflow-hidden shadow-lg border-4 border-white">
|
||||||
|
<iframe
|
||||||
|
src="https://maps.google.com/maps?hl=tr&gl=TR&cid=17585469489776666725&output=embed"
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
style={{ border: 0 }}
|
||||||
|
allowFullScreen={false}
|
||||||
|
loading="lazy"
|
||||||
|
referrerPolicy="no-referrer-when-downgrade"
|
||||||
|
title="Moy Beach Konum"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
const socials = [
|
||||||
|
{
|
||||||
|
label: "Instagram",
|
||||||
|
href: "https://instagram.com/moybeachakyaka",
|
||||||
|
icon: (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-5 h-5">
|
||||||
|
<rect x="2" y="2" width="20" height="20" rx="5" ry="5" />
|
||||||
|
<circle cx="12" cy="12" r="4" />
|
||||||
|
<circle cx="17.5" cy="6.5" r="0.5" fill="currentColor" stroke="none" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Facebook",
|
||||||
|
href: "https://facebook.com/moybeachakyaka",
|
||||||
|
icon: (
|
||||||
|
<svg viewBox="0 0 24 24" fill="currentColor" className="w-5 h-5">
|
||||||
|
<path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "YouTube",
|
||||||
|
href: "https://youtube.com/@moybeach",
|
||||||
|
icon: (
|
||||||
|
<svg viewBox="0 0 24 24" fill="currentColor" className="w-5 h-5">
|
||||||
|
<path d="M22.54 6.42a2.78 2.78 0 0 0-1.95-1.96C18.88 4 12 4 12 4s-6.88 0-8.59.46A2.78 2.78 0 0 0 1.46 6.42 29 29 0 0 0 1 12a29 29 0 0 0 .46 5.58 2.78 2.78 0 0 0 1.95 1.96C5.12 20 12 20 12 20s6.88 0 8.59-.46a2.78 2.78 0 0 0 1.96-1.96A29 29 0 0 0 23 12a29 29 0 0 0-.46-5.58z" />
|
||||||
|
<polygon points="9.75 15.02 15.5 12 9.75 8.98 9.75 15.02" fill="white" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function Footer({ dict }: { dict: any }) {
|
||||||
|
const links = [
|
||||||
|
{ label: dict.nav.about, href: "#about" },
|
||||||
|
{ label: dict.nav.services, href: "#services" },
|
||||||
|
{ label: dict.nav.gallery, href: "#gallery" },
|
||||||
|
{ label: dict.nav.contact, href: "#contact" },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<footer className="relative bg-sea-dark text-sand-light overflow-hidden">
|
||||||
|
|
||||||
|
{/* Large decorative brand name — background texture */}
|
||||||
|
<div className="absolute inset-x-0 top-0 flex items-start justify-center pointer-events-none select-none overflow-hidden">
|
||||||
|
<span className="text-[clamp(6rem,20vw,18rem)] font-serif text-white/[0.03] leading-none tracking-tight whitespace-nowrap">
|
||||||
|
MOY BEACH
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative z-10">
|
||||||
|
|
||||||
|
{/* Top section */}
|
||||||
|
<div className="container mx-auto px-6 max-w-6xl pt-20 pb-14">
|
||||||
|
<div className="grid md:grid-cols-3 gap-12 items-start">
|
||||||
|
|
||||||
|
{/* Brand column */}
|
||||||
|
<div className="md:col-span-1">
|
||||||
|
<p className="text-2xl md:text-3xl font-serif text-white tracking-wide mb-3">MOY BEACH</p>
|
||||||
|
<p className="text-xs uppercase tracking-[0.3em] text-coral mb-6">Akyaka · Muğla</p>
|
||||||
|
<p className="text-sm text-sand-light/60 leading-relaxed mb-8">
|
||||||
|
{dict.footer.desc}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Social icons */}
|
||||||
|
<div className="flex gap-3">
|
||||||
|
{socials.map((s) => (
|
||||||
|
<a
|
||||||
|
key={s.label}
|
||||||
|
href={s.href}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
aria-label={s.label}
|
||||||
|
className="w-10 h-10 rounded-full border border-white/10 flex items-center justify-center text-sand-light/60 hover:text-white hover:border-coral hover:bg-coral/10 transition-all duration-300"
|
||||||
|
>
|
||||||
|
{s.icon}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Navigation column */}
|
||||||
|
<div>
|
||||||
|
<h4 className="text-[11px] uppercase tracking-[0.25em] text-sand-dark mb-5">{dict.footer.pages}</h4>
|
||||||
|
<ul className="space-y-3">
|
||||||
|
{links.map((l) => (
|
||||||
|
<li key={l.label}>
|
||||||
|
<a
|
||||||
|
href={l.href}
|
||||||
|
className="text-sm text-sand-light/65 hover:text-white transition-colors duration-200 flex items-center gap-2 group"
|
||||||
|
>
|
||||||
|
<span className="w-0 group-hover:w-3 h-px bg-coral transition-all duration-300 shrink-0" />
|
||||||
|
{l.label}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contact column */}
|
||||||
|
<div>
|
||||||
|
<h4 className="text-[11px] uppercase tracking-[0.25em] text-sand-dark mb-5">{dict.footer.contact}</h4>
|
||||||
|
<ul className="space-y-4 text-sm text-sand-light/65">
|
||||||
|
<li className="leading-relaxed">
|
||||||
|
{dict.contact.address.text1}<br />
|
||||||
|
{dict.contact.address.text2}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="tel:+905344656235" className="hover:text-white transition-colors">
|
||||||
|
+90 534 465 62 35
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="mailto:info@moybeachakyaka.com" className="hover:text-white transition-colors">
|
||||||
|
info@moybeachakyaka.com
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li className="text-sand-light/45">{dict.contact.hours.text}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Divider */}
|
||||||
|
<div className="border-t border-white/[0.07]" />
|
||||||
|
|
||||||
|
{/* Bottom bar */}
|
||||||
|
<div className="container mx-auto px-6 max-w-6xl py-6 flex flex-col sm:flex-row justify-between items-center gap-3 text-[11px] text-sand-light/35">
|
||||||
|
<p>© {new Date().getFullYear()} {dict.footer.rights}</p>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<span>{dict.footer.created_by}</span>
|
||||||
|
<a
|
||||||
|
href="https://ayris.tech"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="hover:text-sand-light/70 transition-colors underline underline-offset-4"
|
||||||
|
>
|
||||||
|
ayris.tech
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Floating WhatsApp Button */}
|
||||||
|
<a
|
||||||
|
href="https://wa.me/905344656235"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="fixed bottom-6 right-6 z-50 bg-[#25D366] text-white p-3.5 rounded-full shadow-xl hover:scale-110 transition-transform"
|
||||||
|
aria-label="WhatsApp ile iletişime geçin"
|
||||||
|
>
|
||||||
|
<svg viewBox="0 0 24 24" className="w-6 h-6 fill-current">
|
||||||
|
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51a12.8 12.8 0 0 0-.57-.01c-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 0 1-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 0 1-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 0 1 2.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0 0 12.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 0 0 5.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 0 0-3.48-8.413Z" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
|
import { X, ZoomIn } from "lucide-react";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
const photos = [
|
||||||
|
{ id: 1, categoryKey: "beach", url: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?auto=format&fit=crop&q=80&w=1200" },
|
||||||
|
{ id: 2, categoryKey: "rooms", url: "https://images.unsplash.com/photo-1611892440504-42a792e24d32?auto=format&fit=crop&q=80&w=1200" },
|
||||||
|
{ id: 3, categoryKey: "restaurant", url: "https://images.unsplash.com/photo-1414235077428-338989a2e8c0?auto=format&fit=crop&q=80&w=1200" },
|
||||||
|
{ id: 4, categoryKey: "beach", url: "https://images.unsplash.com/photo-1519046904884-53103b34b206?auto=format&fit=crop&q=80&w=1200" },
|
||||||
|
{ id: 5, categoryKey: "events", url: "https://images.unsplash.com/photo-1511285560929-80b456fea0bc?auto=format&fit=crop&q=80&w=1200" },
|
||||||
|
{ id: 6, categoryKey: "restaurant", url: "https://images.unsplash.com/photo-1555396273-367ea4eb4db5?auto=format&fit=crop&q=80&w=1200" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function Gallery({ dict }: { dict: any }) {
|
||||||
|
const categories = [
|
||||||
|
{ key: "all", label: dict.gallery.categories.all },
|
||||||
|
{ key: "beach", label: dict.gallery.categories.beach },
|
||||||
|
{ key: "restaurant", label: dict.gallery.categories.restaurant },
|
||||||
|
{ key: "rooms", label: dict.gallery.categories.rooms },
|
||||||
|
{ key: "events", label: dict.gallery.categories.events }
|
||||||
|
];
|
||||||
|
|
||||||
|
const [activeCategoryKey, setActiveCategoryKey] = useState("all");
|
||||||
|
const [selectedImage, setSelectedImage] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const filteredPhotos =
|
||||||
|
activeCategoryKey === "all"
|
||||||
|
? photos
|
||||||
|
: photos.filter((p) => p.categoryKey === activeCategoryKey);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section id="gallery" className="py-24 bg-sand-light text-sea-dark">
|
||||||
|
<div className="container mx-auto px-6 max-w-7xl">
|
||||||
|
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<div className="flex items-center justify-center gap-3 mb-4">
|
||||||
|
<span className="w-8 h-px bg-coral" />
|
||||||
|
<h2 className="text-sm font-bold tracking-widest uppercase text-coral">{dict.gallery.title}</h2>
|
||||||
|
<span className="w-8 h-px bg-coral" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-4xl md:text-5xl font-serif mb-10">{dict.gallery.heading}</h3>
|
||||||
|
|
||||||
|
{/* Category filters */}
|
||||||
|
<div className="flex flex-wrap justify-center gap-3 mb-12">
|
||||||
|
{categories.map((cat) => (
|
||||||
|
<button
|
||||||
|
key={cat.key}
|
||||||
|
onClick={() => setActiveCategoryKey(cat.key)}
|
||||||
|
className={`px-5 py-2 rounded-full text-xs tracking-widest uppercase font-medium transition-all duration-300 ${
|
||||||
|
activeCategoryKey === cat.key
|
||||||
|
? "bg-sea-dark text-white shadow-md"
|
||||||
|
: "bg-white text-sea-dark border border-sand-dark/30 hover:border-sea-dark hover:bg-sea-dark/5"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{cat.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<motion.div layout className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||||
|
<AnimatePresence mode="popLayout">
|
||||||
|
{filteredPhotos.map((photo) => (
|
||||||
|
<motion.div
|
||||||
|
key={photo.id}
|
||||||
|
layout
|
||||||
|
initial={{ opacity: 0, scale: 0.92 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
exit={{ opacity: 0, scale: 0.92 }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
className="relative h-72 cursor-pointer overflow-hidden rounded-2xl group shadow-sm hover:shadow-lg hover:shadow-sea-dark/10 transition-shadow duration-500"
|
||||||
|
onClick={() => setSelectedImage(photo.url)}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={photo.url}
|
||||||
|
alt={dict.gallery.categories[photo.categoryKey]}
|
||||||
|
fill
|
||||||
|
className="object-cover transform group-hover:scale-108 transition-transform duration-700"
|
||||||
|
sizes="(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
||||||
|
style={{ transform: "scale(1)", transition: "transform 700ms" }}
|
||||||
|
/>
|
||||||
|
{/* Hover overlay */}
|
||||||
|
<div className="absolute inset-0 bg-sea-dark/40 opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-10 flex items-center justify-center">
|
||||||
|
<div className="flex flex-col items-center gap-2 text-white">
|
||||||
|
<ZoomIn size={28} />
|
||||||
|
<span className="text-[10px] uppercase tracking-[0.25em]">{dict.gallery.categories[photo.categoryKey]}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</AnimatePresence>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Lightbox */}
|
||||||
|
<AnimatePresence>
|
||||||
|
{selectedImage && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
className="fixed inset-0 z-[100] bg-black/92 flex items-center justify-center p-4"
|
||||||
|
onClick={() => setSelectedImage(null)}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="absolute top-6 right-6 text-white/70 hover:text-white transition-colors bg-white/10 hover:bg-white/20 p-2.5 rounded-full backdrop-blur-sm"
|
||||||
|
onClick={() => setSelectedImage(null)}
|
||||||
|
aria-label="Kapat"
|
||||||
|
>
|
||||||
|
<X size={20} />
|
||||||
|
</button>
|
||||||
|
<motion.div
|
||||||
|
initial={{ scale: 0.88, opacity: 0 }}
|
||||||
|
animate={{ scale: 1, opacity: 1 }}
|
||||||
|
exit={{ scale: 0.88, opacity: 0 }}
|
||||||
|
transition={{ duration: 0.25, ease: [0.22, 1, 0.36, 1] }}
|
||||||
|
className="relative max-w-4xl max-h-[88vh] w-full h-full"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={selectedImage}
|
||||||
|
alt="Büyütülmüş Görsel"
|
||||||
|
fill
|
||||||
|
className="object-contain rounded-lg"
|
||||||
|
sizes="100vw"
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { Menu, X } from "lucide-react";
|
||||||
|
|
||||||
|
export default function Header({ dict, lang }: { dict: any, lang: string }) {
|
||||||
|
const [isScrolled, setIsScrolled] = useState(false);
|
||||||
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleScroll = () => {
|
||||||
|
setIsScrolled(window.scrollY > 50);
|
||||||
|
};
|
||||||
|
window.addEventListener("scroll", handleScroll);
|
||||||
|
return () => window.removeEventListener("scroll", handleScroll);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const navLinks = [
|
||||||
|
{ name: dict.nav.about, href: "#about" },
|
||||||
|
{ name: dict.nav.services, href: "#services" },
|
||||||
|
{ name: dict.nav.gallery, href: "#gallery" },
|
||||||
|
{ name: dict.nav.contact, href: "#contact" },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header
|
||||||
|
className={`fixed top-0 w-full z-50 transition-all duration-300 ${
|
||||||
|
isScrolled ? "bg-white/95 backdrop-blur-md shadow-sm py-4" : "bg-transparent py-6"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="container mx-auto px-4 md:px-6 flex justify-between items-center">
|
||||||
|
<a href="#" className="flex items-center">
|
||||||
|
<img
|
||||||
|
src="/logo.png"
|
||||||
|
alt="Moy Beach Logo"
|
||||||
|
className={`h-12 w-auto transition-all duration-300 ${!isScrolled ? "brightness-0 invert opacity-90" : ""}`}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{/* Desktop Nav */}
|
||||||
|
<nav className="hidden md:flex items-center space-x-8">
|
||||||
|
{navLinks.map((link) => (
|
||||||
|
<a
|
||||||
|
key={link.name}
|
||||||
|
href={link.href}
|
||||||
|
className={`text-sm tracking-wide hover:text-coral transition-colors ${
|
||||||
|
isScrolled ? "text-sea-dark" : "text-white"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{link.name}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
<div className={`flex items-center gap-2 text-sm font-medium ${isScrolled ? "text-sea-dark" : "text-white"}`}>
|
||||||
|
<a href="/tr" className={`cursor-pointer hover:text-coral ${lang === 'tr' ? 'opacity-100 font-bold' : 'opacity-70'}`}>TR</a>
|
||||||
|
<span className="opacity-50">/</span>
|
||||||
|
<a href="/en" className={`cursor-pointer hover:text-coral ${lang === 'en' ? 'opacity-100 font-bold' : 'opacity-70'}`}>EN</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* Mobile Menu Toggle */}
|
||||||
|
<button
|
||||||
|
className="md:hidden"
|
||||||
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||||
|
>
|
||||||
|
{mobileMenuOpen ? (
|
||||||
|
<X className={isScrolled ? "text-sea-dark" : "text-white"} />
|
||||||
|
) : (
|
||||||
|
<Menu className={isScrolled ? "text-sea-dark" : "text-white"} />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Nav */}
|
||||||
|
{mobileMenuOpen && (
|
||||||
|
<div className="md:hidden absolute top-full left-0 w-full bg-white shadow-xl flex flex-col py-4 px-6 space-y-4">
|
||||||
|
{navLinks.map((link) => (
|
||||||
|
<a
|
||||||
|
key={link.name}
|
||||||
|
href={link.href}
|
||||||
|
className="text-sea-dark text-lg py-2 border-b border-gray-100"
|
||||||
|
onClick={() => setMobileMenuOpen(false)}
|
||||||
|
>
|
||||||
|
{link.name}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
<div className="flex gap-4 pt-2">
|
||||||
|
<a href="/tr" className={`font-medium cursor-pointer ${lang === 'tr' ? 'text-sea-dark font-bold' : 'text-gray-400'}`}>TR</a>
|
||||||
|
<a href="/en" className={`font-medium cursor-pointer ${lang === 'en' ? 'text-sea-dark font-bold' : 'text-gray-400'}`}>EN</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { ChevronDown } from "lucide-react";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
export default function Hero({ dict }: { dict: any }) {
|
||||||
|
const words = ["MOY", "BEACH"];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="relative h-screen w-full flex items-center justify-center overflow-hidden">
|
||||||
|
|
||||||
|
{/* Background Image */}
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
<Image
|
||||||
|
src="https://images.unsplash.com/photo-1590523277543-a94d2e4eb00b?auto=format&fit=crop&q=85&w=2400"
|
||||||
|
alt="Moy Beach Akyaka"
|
||||||
|
fill
|
||||||
|
className="object-cover object-center"
|
||||||
|
priority
|
||||||
|
sizes="100vw"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Multi-layer gradient for depth */}
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-b from-sea-dark/55 via-black/15 to-sea-dark/70 z-10" />
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-r from-sea-dark/20 via-transparent to-sea-dark/20 z-10" />
|
||||||
|
|
||||||
|
{/* Main Content */}
|
||||||
|
<div className="relative z-20 text-center px-4 max-w-5xl mx-auto">
|
||||||
|
|
||||||
|
{/* Location badge */}
|
||||||
|
<motion.p
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
transition={{ duration: 1.4, delay: 0.1 }}
|
||||||
|
className="text-sand/60 text-[11px] md:text-xs uppercase tracking-[0.45em] mb-6 font-light"
|
||||||
|
>
|
||||||
|
Akyaka · Muğla · Türkiye
|
||||||
|
</motion.p>
|
||||||
|
|
||||||
|
{/* Decorative line */}
|
||||||
|
<motion.div
|
||||||
|
initial={{ scaleX: 0 }}
|
||||||
|
animate={{ scaleX: 1 }}
|
||||||
|
transition={{ duration: 0.7, delay: 0.25 }}
|
||||||
|
className="w-12 h-px bg-coral mx-auto mb-8 origin-center"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Main Title — word-by-word reveal */}
|
||||||
|
<h1 className="text-[4.5rem] md:text-[8rem] lg:text-[11rem] font-serif text-white leading-none tracking-tight mb-6">
|
||||||
|
{words.map((word, i) => (
|
||||||
|
<span key={word} className="inline-block overflow-hidden">
|
||||||
|
<motion.span
|
||||||
|
initial={{ y: 110, opacity: 0 }}
|
||||||
|
animate={{ y: 0, opacity: 1 }}
|
||||||
|
transition={{
|
||||||
|
duration: 0.9,
|
||||||
|
delay: 0.4 + i * 0.2,
|
||||||
|
ease: [0.22, 1, 0.36, 1],
|
||||||
|
}}
|
||||||
|
className="inline-block"
|
||||||
|
>
|
||||||
|
{word}
|
||||||
|
</motion.span>
|
||||||
|
{i === 0 && (
|
||||||
|
<span className="inline-block w-4 md:w-8 lg:w-12" />
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
{/* Subtitle */}
|
||||||
|
<motion.p
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ duration: 1, delay: 1.0 }}
|
||||||
|
className="text-lg md:text-2xl text-sand-light font-light tracking-wide mb-12 drop-shadow-md"
|
||||||
|
>
|
||||||
|
{dict.hero.subtitle}
|
||||||
|
</motion.p>
|
||||||
|
|
||||||
|
{/* CTA Buttons */}
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ duration: 0.8, delay: 1.35 }}
|
||||||
|
className="flex flex-wrap items-center justify-center gap-4"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="#about"
|
||||||
|
className="border border-white/50 hover:border-white text-white px-8 py-3.5 rounded-full text-sm tracking-widest uppercase font-medium transition-all hover:bg-white/10 backdrop-blur-sm"
|
||||||
|
>
|
||||||
|
{dict.hero.explore}
|
||||||
|
</a>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Scroll Indicator — direct child of section, correctly positioned */}
|
||||||
|
<motion.a
|
||||||
|
href="#about"
|
||||||
|
className="absolute bottom-20 left-1/2 -translate-x-1/2 z-20 text-white flex flex-col items-center gap-2 cursor-pointer"
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 0.65 }}
|
||||||
|
transition={{ duration: 1, delay: 2.2 }}
|
||||||
|
whileHover={{ opacity: 1 }}
|
||||||
|
aria-label="Aşağı kaydır"
|
||||||
|
>
|
||||||
|
<span className="text-[9px] uppercase tracking-[0.35em] font-light">Kaydır</span>
|
||||||
|
<motion.div
|
||||||
|
animate={{ y: [0, 7, 0] }}
|
||||||
|
transition={{
|
||||||
|
duration: 1.6,
|
||||||
|
repeat: Infinity,
|
||||||
|
repeatType: "loop",
|
||||||
|
ease: "easeInOut",
|
||||||
|
delay: 2.6,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ChevronDown size={18} />
|
||||||
|
</motion.div>
|
||||||
|
</motion.a>
|
||||||
|
|
||||||
|
{/* Wave divider into About section */}
|
||||||
|
<div className="absolute bottom-0 left-0 right-0 z-20 leading-none pointer-events-none">
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 1440 56"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
preserveAspectRatio="none"
|
||||||
|
className="w-full h-14 block"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M0,28 C360,56 720,0 1080,28 C1260,42 1380,14 1440,28 L1440,56 L0,56 Z"
|
||||||
|
fill="#F4EFEB"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
const reels = [
|
||||||
|
{ id: 1, url: "https://www.instagram.com/reel/C97iqFqoSyl/embed" },
|
||||||
|
{ id: 2, url: "https://www.instagram.com/reel/C-slzOcIQB3/embed" },
|
||||||
|
{ id: 3, url: "https://www.instagram.com/reel/DLM8ng6MOKf/embed" },
|
||||||
|
{ id: 4, url: "https://www.instagram.com/reel/DX-BDErM5qd/embed" },
|
||||||
|
];
|
||||||
|
|
||||||
|
function InstagramIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.5} className="w-4 h-4">
|
||||||
|
<rect x="2" y="2" width="20" height="20" rx="5" ry="5" />
|
||||||
|
<circle cx="12" cy="12" r="4" />
|
||||||
|
<circle cx="17.5" cy="6.5" r="0.8" fill="currentColor" stroke="none" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function InstagramFeed({ dict }: { dict: any }) {
|
||||||
|
return (
|
||||||
|
<section className="py-24 bg-sand-light text-sea-dark overflow-hidden">
|
||||||
|
<div className="container mx-auto px-6 max-w-7xl">
|
||||||
|
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center mb-14">
|
||||||
|
<div className="flex items-center justify-center gap-3 mb-4">
|
||||||
|
<span className="w-8 h-px bg-coral" />
|
||||||
|
<h2 className="text-sm font-bold tracking-widest uppercase text-coral flex items-center gap-2">
|
||||||
|
<InstagramIcon />
|
||||||
|
{dict.social.title}
|
||||||
|
</h2>
|
||||||
|
<span className="w-8 h-px bg-coral" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 className="text-4xl md:text-5xl font-serif mb-5">{dict.social.heading}</h3>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="https://www.instagram.com/moy_akyaka/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="inline-flex items-center gap-2 text-sm text-sea-dark/60 hover:text-coral transition-colors tracking-wide"
|
||||||
|
>
|
||||||
|
<InstagramIcon />
|
||||||
|
@moy_akyaka
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Reels grid */}
|
||||||
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6">
|
||||||
|
{reels.map((reel) => (
|
||||||
|
<div
|
||||||
|
key={reel.id}
|
||||||
|
className="group relative bg-white rounded-2xl overflow-hidden shadow-sm hover:shadow-xl hover:shadow-sea-dark/10 transition-all duration-500 hover:-translate-y-1"
|
||||||
|
>
|
||||||
|
{/* Thin coral top border accent */}
|
||||||
|
<div className="absolute top-0 left-0 right-0 h-[2px] bg-gradient-to-r from-coral/0 via-coral to-coral/0 opacity-0 group-hover:opacity-100 transition-opacity duration-500 z-10" />
|
||||||
|
|
||||||
|
<iframe
|
||||||
|
src={reel.url}
|
||||||
|
className="w-full h-[520px] md:h-[580px]"
|
||||||
|
style={{ border: "none", display: "block" }}
|
||||||
|
scrolling="no"
|
||||||
|
frameBorder="0"
|
||||||
|
allow="encrypted-media"
|
||||||
|
title={`Instagram Reel ${reel.id}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* CTA */}
|
||||||
|
<div className="text-center mt-12">
|
||||||
|
<a
|
||||||
|
href="https://www.instagram.com/moy_akyaka/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="btn-shimmer inline-flex items-center gap-2.5 bg-sea-dark hover:bg-sea-blue text-white px-8 py-3.5 rounded-full text-sm tracking-widest uppercase font-medium transition-colors shadow-md hover:shadow-lg"
|
||||||
|
>
|
||||||
|
<InstagramIcon />
|
||||||
|
{dict.social.button}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { Calendar, Users, Coffee } from "lucide-react";
|
||||||
|
|
||||||
|
export default function Reservation() {
|
||||||
|
return (
|
||||||
|
<section id="reservation" className="py-24 bg-white relative overflow-hidden">
|
||||||
|
{/* Decorative background blobs */}
|
||||||
|
<div className="absolute top-0 right-0 w-[500px] h-[500px] bg-sand-light rounded-full blur-3xl opacity-60 -translate-y-1/2 translate-x-1/3 pointer-events-none" />
|
||||||
|
<div className="absolute bottom-0 left-0 w-[380px] h-[380px] bg-coral-light/15 rounded-full blur-3xl opacity-50 translate-y-1/2 -translate-x-1/3 pointer-events-none" />
|
||||||
|
|
||||||
|
<div className="container mx-auto px-6 max-w-5xl relative z-10">
|
||||||
|
<div className="bg-sea-dark rounded-[2.5rem] p-8 md:p-14 shadow-2xl overflow-hidden relative">
|
||||||
|
{/* Inner glow */}
|
||||||
|
<div className="absolute top-0 right-0 w-72 h-72 bg-sea-light rounded-full blur-3xl opacity-25 -translate-y-1/2 translate-x-1/2 pointer-events-none" />
|
||||||
|
|
||||||
|
<div className="text-center mb-12 relative z-10 text-white">
|
||||||
|
<div className="flex items-center justify-center gap-3 mb-4">
|
||||||
|
<span className="w-8 h-px bg-coral/70" />
|
||||||
|
<h2 className="text-sm font-bold tracking-widest uppercase text-coral">Hemen Yerinizi Ayırtın</h2>
|
||||||
|
<span className="w-8 h-px bg-coral/70" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-4xl md:text-5xl font-serif mb-4">Rezervasyon Talebi</h3>
|
||||||
|
<p className="text-sand-light/80 max-w-2xl mx-auto text-sm leading-relaxed">
|
||||||
|
Unutulmaz bir Ege deneyimi için hemen formumuzu doldurun, ekibimiz en kısa sürede sizinle iletişime geçsin.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<motion.form
|
||||||
|
initial={{ opacity: 0, y: 30 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
transition={{ duration: 0.6 }}
|
||||||
|
className="relative z-10 bg-white p-7 md:p-10 rounded-2xl shadow-xl max-w-4xl mx-auto"
|
||||||
|
onSubmit={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
|
<div className="grid md:grid-cols-3 gap-5 mb-6">
|
||||||
|
|
||||||
|
{/* Date */}
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<label className="text-xs font-semibold text-gray-600 uppercase tracking-wide flex items-center gap-1.5">
|
||||||
|
<Calendar size={13} className="text-coral" /> Tarih
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:outline-none focus:ring-2 focus:ring-coral/40 focus:border-coral transition-colors text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Guests */}
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<label className="text-xs font-semibold text-gray-600 uppercase tracking-wide flex items-center gap-1.5">
|
||||||
|
<Users size={13} className="text-coral" /> Kişi Sayısı
|
||||||
|
</label>
|
||||||
|
<select className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:outline-none focus:ring-2 focus:ring-coral/40 focus:border-coral transition-colors bg-white text-sm">
|
||||||
|
<option>1 Kişi</option>
|
||||||
|
<option>2 Kişi</option>
|
||||||
|
<option>3 Kişi</option>
|
||||||
|
<option>4 Kişi</option>
|
||||||
|
<option>5+ Kişi</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Service type */}
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<label className="text-xs font-semibold text-gray-600 uppercase tracking-wide flex items-center gap-1.5">
|
||||||
|
<Coffee size={13} className="text-coral" /> Hizmet Tipi
|
||||||
|
</label>
|
||||||
|
<select className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:outline-none focus:ring-2 focus:ring-coral/40 focus:border-coral transition-colors bg-white text-sm">
|
||||||
|
<option>Beach Club (Şezlong/Loca)</option>
|
||||||
|
<option>Restoran Akşam Yemeği</option>
|
||||||
|
<option>Konaklama</option>
|
||||||
|
<option>Özel Etkinlik / Organizasyon</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-2 gap-5 mb-7">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Adınız Soyadınız"
|
||||||
|
className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:outline-none focus:ring-2 focus:ring-coral/40 focus:border-coral transition-colors text-sm"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="tel"
|
||||||
|
placeholder="Telefon Numaranız"
|
||||||
|
className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:outline-none focus:ring-2 focus:ring-coral/40 focus:border-coral transition-colors text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Shimmer submit button */}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn-shimmer w-full bg-coral hover:bg-coral-dark text-white font-medium py-4 rounded-xl transition-colors shadow-lg hover:shadow-xl text-sm tracking-wide uppercase"
|
||||||
|
>
|
||||||
|
Rezervasyon Talebi Gönder
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<p className="text-[11px] text-center text-gray-400 mt-4">
|
||||||
|
Bu form ön rezervasyon talebidir. Kesin onay için ekibimiz sizinle iletişime geçecektir.
|
||||||
|
</p>
|
||||||
|
</motion.form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { Waves, UtensilsCrossed, Hotel, CalendarHeart } from "lucide-react";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
export default function Services({ dict }: { dict: any }) {
|
||||||
|
const services = [
|
||||||
|
{
|
||||||
|
title: dict.services.items.beach.title,
|
||||||
|
description: dict.services.items.beach.description,
|
||||||
|
icon: Waves,
|
||||||
|
image: "https://images.unsplash.com/photo-1519046904884-53103b34b206?auto=format&fit=crop&q=80&w=800",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: dict.services.items.restaurant.title,
|
||||||
|
description: dict.services.items.restaurant.description,
|
||||||
|
icon: UtensilsCrossed,
|
||||||
|
image: "https://images.unsplash.com/photo-1544148103-0773bf10d330?auto=format&fit=crop&q=80&w=800",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: dict.services.items.hotel.title,
|
||||||
|
description: dict.services.items.hotel.description,
|
||||||
|
icon: Hotel,
|
||||||
|
image: "https://images.unsplash.com/photo-1582719478250-c89cae4dc85b?auto=format&fit=crop&q=80&w=800",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: dict.services.items.events.title,
|
||||||
|
description: dict.services.items.events.description,
|
||||||
|
icon: CalendarHeart,
|
||||||
|
image: "https://images.unsplash.com/photo-1511285560929-80b456fea0bc?auto=format&fit=crop&q=80&w=800",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section id="services" className="py-24 bg-white text-sea-dark">
|
||||||
|
<div className="container mx-auto px-6 max-w-7xl">
|
||||||
|
|
||||||
|
<div className="text-center mb-16">
|
||||||
|
<div className="flex items-center justify-center gap-3 mb-4">
|
||||||
|
<span className="w-8 h-px bg-coral" />
|
||||||
|
<h2 className="text-sm font-bold tracking-widest uppercase text-coral">{dict.services.title}</h2>
|
||||||
|
<span className="w-8 h-px bg-coral" />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-4xl md:text-5xl font-serif">{dict.services.heading}</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||||
|
{services.map((service, index) => (
|
||||||
|
<motion.div
|
||||||
|
key={service.title}
|
||||||
|
initial={{ opacity: 0, y: 30 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
viewport={{ once: true, margin: "-50px" }}
|
||||||
|
transition={{ duration: 0.6, delay: index * 0.1 }}
|
||||||
|
className="group cursor-pointer"
|
||||||
|
>
|
||||||
|
{/* Image card with gradient border on hover */}
|
||||||
|
<div className="relative mb-6">
|
||||||
|
{/* Border beam — visible on hover */}
|
||||||
|
<div className="absolute -inset-[1px] rounded-2xl bg-gradient-to-br from-coral/0 to-coral/0 group-hover:from-coral/50 group-hover:to-sea-light/40 transition-all duration-500 opacity-0 group-hover:opacity-100 z-0" />
|
||||||
|
|
||||||
|
<div className="relative h-80 overflow-hidden rounded-2xl shadow-md group-hover:shadow-lg group-hover:shadow-coral/15 transition-shadow duration-500 z-10">
|
||||||
|
<div className="absolute inset-0 bg-sea-dark/20 group-hover:bg-transparent transition-colors duration-500 z-10" />
|
||||||
|
|
||||||
|
<Image
|
||||||
|
src={service.image}
|
||||||
|
alt={service.title}
|
||||||
|
fill
|
||||||
|
className="object-cover transform group-hover:scale-110 transition-transform duration-700"
|
||||||
|
sizes="(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 25vw"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Icon badge */}
|
||||||
|
<div className="absolute top-4 left-4 z-20 bg-white/90 backdrop-blur-sm p-3 rounded-full shadow-lg ring-2 ring-coral/10 group-hover:ring-coral/50 transition-all duration-300">
|
||||||
|
<service.icon className="w-5 h-5 text-coral" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4 className="text-xl font-serif mb-2 group-hover:text-coral transition-colors duration-300">
|
||||||
|
{service.title}
|
||||||
|
</h4>
|
||||||
|
<p className="text-sm text-gray-500 leading-relaxed">{service.description}</p>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { Star, Quote, Play } from "lucide-react";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
const testimonials = [
|
||||||
|
{
|
||||||
|
name: "Ayşe Y.",
|
||||||
|
platform: "Google Yorumu",
|
||||||
|
text: "Ege'de gördüğüm en kaliteli mekanlardan biri. Yemekler harika, denizi zaten muhteşem. Özellikle kokteyllerini denemelisiniz.",
|
||||||
|
rating: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "David M.",
|
||||||
|
platform: "TripAdvisor",
|
||||||
|
text: "Mükemmel bir deneyimdi. Ailece geldik ve çocuklar da biz de çok eğlendik. Personel çok ilgili ve güler yüzlüydü.",
|
||||||
|
rating: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Caner K.",
|
||||||
|
platform: "Google Yorumu",
|
||||||
|
text: "Harika bir atmosfer. Akşam yemeği için tercih ettik, gün batımı manzarası eşliğinde unutulmaz bir anı oldu.",
|
||||||
|
rating: 4,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function VideoTestimonials() {
|
||||||
|
return (
|
||||||
|
<section className="bg-sea-dark text-white overflow-hidden relative">
|
||||||
|
<div className="grid lg:grid-cols-2">
|
||||||
|
|
||||||
|
{/* Video / Image Panel */}
|
||||||
|
<div className="relative h-[50vh] lg:h-auto min-h-[500px] overflow-hidden">
|
||||||
|
<Image
|
||||||
|
src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?auto=format&fit=crop&q=80&w=1200"
|
||||||
|
alt="Moy Beach Video"
|
||||||
|
fill
|
||||||
|
className="object-cover"
|
||||||
|
sizes="(max-width: 1024px) 100vw, 50vw"
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 bg-sea-dark/50 z-10" />
|
||||||
|
|
||||||
|
{/* Play button overlay */}
|
||||||
|
<div className="absolute inset-0 z-20 flex flex-col items-center justify-center gap-6 p-8 text-center">
|
||||||
|
<a
|
||||||
|
href="https://www.youtube.com/@moybeach"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="group w-20 h-20 bg-white/15 hover:bg-white/25 border-2 border-white/60 hover:border-white rounded-full flex items-center justify-center transition-all duration-300 hover:scale-110 backdrop-blur-sm"
|
||||||
|
aria-label="Videoyu izle"
|
||||||
|
>
|
||||||
|
<Play className="w-7 h-7 text-white ml-1 group-hover:scale-110 transition-transform" fill="white" />
|
||||||
|
</a>
|
||||||
|
<p className="text-sand/80 text-sm tracking-wide">Moy Beach'i Keşfedin</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Testimonials Panel */}
|
||||||
|
<div className="py-20 px-8 lg:px-14 flex flex-col justify-center">
|
||||||
|
<div className="flex items-center gap-3 mb-4">
|
||||||
|
<span className="w-8 h-px bg-coral" />
|
||||||
|
<h2 className="text-sm font-bold tracking-widest uppercase text-coral">Referanslar</h2>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-4xl md:text-5xl font-serif mb-10">Misafirlerimiz<br />Ne Diyor?</h3>
|
||||||
|
|
||||||
|
<div className="space-y-6">
|
||||||
|
{testimonials.map((testimonial, index) => (
|
||||||
|
<motion.div
|
||||||
|
key={index}
|
||||||
|
initial={{ opacity: 0, x: 40 }}
|
||||||
|
whileInView={{ opacity: 1, x: 0 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
transition={{ duration: 0.6, delay: index * 0.15 }}
|
||||||
|
className="bg-white/6 hover:bg-white/10 p-6 rounded-2xl relative border border-white/5 hover:border-white/10 transition-colors duration-300"
|
||||||
|
>
|
||||||
|
<Quote className="absolute top-5 right-6 text-coral/20 w-10 h-10" />
|
||||||
|
|
||||||
|
{/* Stars */}
|
||||||
|
<div className="flex text-coral mb-3">
|
||||||
|
{[...Array(5)].map((_, i) => (
|
||||||
|
<Star
|
||||||
|
key={i}
|
||||||
|
className={`w-3.5 h-3.5 ${i < testimonial.rating ? "fill-current" : "opacity-25"}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-base italic text-sand-light/90 mb-5 leading-relaxed">
|
||||||
|
"{testimonial.text}"
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="w-9 h-9 bg-coral rounded-full flex items-center justify-center font-bold text-sm shrink-0">
|
||||||
|
{testimonial.name.charAt(0)}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-sm">{testimonial.name}</p>
|
||||||
|
<p className="text-xs text-gray-400">{testimonial.platform}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Aggregate rating */}
|
||||||
|
<div className="mt-8 flex items-center gap-4 pt-6 border-t border-white/10">
|
||||||
|
<div className="flex items-baseline gap-1">
|
||||||
|
<span className="text-3xl font-bold font-serif">4.8</span>
|
||||||
|
<span className="text-coral text-lg">/ 5</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-gray-400 leading-snug">
|
||||||
|
Google & TripAdvisor değerlendirmelerine göre
|
||||||
|
<br />
|
||||||
|
<span className="text-sand/60">(150+ Yorum)</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
{
|
||||||
|
"nav": {
|
||||||
|
"about": "About",
|
||||||
|
"services": "Services",
|
||||||
|
"gallery": "Gallery",
|
||||||
|
"contact": "Contact"
|
||||||
|
},
|
||||||
|
"hero": {
|
||||||
|
"subtitle": "An Escape in the Most Beautiful Corner of the Aegean",
|
||||||
|
"explore": "Explore"
|
||||||
|
},
|
||||||
|
"about": {
|
||||||
|
"title": "About Us",
|
||||||
|
"heading": "Where Nature<br />Meets Luxury",
|
||||||
|
"p1": "Moy Beach Akyaka is designed to offer a nature-friendly and luxurious experience in the fascinating atmosphere of the Aegean. Brought to life with Moy Group quality, this special venue promises its guests peace, flavor, and entertainment beyond an ordinary beach.",
|
||||||
|
"p2": "While cooling off in the crystal-clear waters during the day, you can embark on a gastronomic journey in the evenings with special delicacies prepared by our chefs.",
|
||||||
|
"stats": {
|
||||||
|
"years": "Years of Experience",
|
||||||
|
"guests": "Happy Guests",
|
||||||
|
"rating": "Average Rating"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"services": {
|
||||||
|
"title": "Privileges",
|
||||||
|
"heading": "Our Services",
|
||||||
|
"items": {
|
||||||
|
"beach": {
|
||||||
|
"title": "Beach Club",
|
||||||
|
"description": "Enjoy the sun and the sea all day long on our private sunbeds and cabanas."
|
||||||
|
},
|
||||||
|
"restaurant": {
|
||||||
|
"title": "Restaurant & Bar",
|
||||||
|
"description": "Meet our selected delicacies from Mediterranean cuisine and signature cocktails."
|
||||||
|
},
|
||||||
|
"hotel": {
|
||||||
|
"title": "Boutique Hotel",
|
||||||
|
"description": "Wake up in our rooms carefully designed for your comfort."
|
||||||
|
},
|
||||||
|
"events": {
|
||||||
|
"title": "Special Events",
|
||||||
|
"description": "Catch the rhythm with unforgettable parties and live DJ performances."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gallery": {
|
||||||
|
"title": "Atmosphere",
|
||||||
|
"heading": "Gallery",
|
||||||
|
"categories": {
|
||||||
|
"all": "All",
|
||||||
|
"beach": "Beach",
|
||||||
|
"restaurant": "Restaurant",
|
||||||
|
"rooms": "Rooms",
|
||||||
|
"events": "Events"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"social": {
|
||||||
|
"title": "Social Media",
|
||||||
|
"heading": "We are on Instagram",
|
||||||
|
"button": "View All Reels"
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"title": "Contact",
|
||||||
|
"heading": "Get in Touch",
|
||||||
|
"address": {
|
||||||
|
"title": "Address",
|
||||||
|
"text1": "Akyaka, Ataturk 1. Cd No:86",
|
||||||
|
"text2": "48640 Ula/Mugla"
|
||||||
|
},
|
||||||
|
"phone": "Phone",
|
||||||
|
"email": "Email",
|
||||||
|
"hours": {
|
||||||
|
"title": "Working Hours",
|
||||||
|
"text": "Everyday: 09:00 - 02:00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"desc": "A unique escape where the cool waters of the Aegean meet luxury. With the assurance of Moy Group.",
|
||||||
|
"pages": "Pages",
|
||||||
|
"contact": "Contact",
|
||||||
|
"rights": "Moy Beach Akyaka. All rights reserved.",
|
||||||
|
"created_by": "Created by"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
{
|
||||||
|
"nav": {
|
||||||
|
"about": "Hakkımızda",
|
||||||
|
"services": "Hizmetler",
|
||||||
|
"gallery": "Galeri",
|
||||||
|
"contact": "İletişim"
|
||||||
|
},
|
||||||
|
"hero": {
|
||||||
|
"subtitle": "Ege'nin En Güzel Köşesinde Bir Kaçış",
|
||||||
|
"explore": "Keşfet"
|
||||||
|
},
|
||||||
|
"about": {
|
||||||
|
"title": "Hakkımızda",
|
||||||
|
"heading": "Doğanın Lüksle<br />Buluştuğu Nokta",
|
||||||
|
"p1": "Moy Beach Akyaka, Ege'nin büyüleyici atmosferinde, doğaya saygılı ve lüks bir deneyim sunmak için tasarlandı. Moy Group kalitesiyle hayata geçen bu özel mekan, sıradan bir plajın ötesinde, misafirlerine huzur, lezzet ve eğlenceyi bir arada vadediyor.",
|
||||||
|
"p2": "Gündüzleri kristal berraklığındaki sularda serinlerken, akşamları şeflerimizin hazırladığı özel lezzetlerle gastronomi yolculuğuna çıkabilirsiniz.",
|
||||||
|
"stats": {
|
||||||
|
"years": "Yıl Deneyim",
|
||||||
|
"guests": "Mutlu Misafir",
|
||||||
|
"rating": "Ortalama Puan"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"services": {
|
||||||
|
"title": "Ayrıcalıklar",
|
||||||
|
"heading": "Hizmetlerimiz",
|
||||||
|
"items": {
|
||||||
|
"beach": {
|
||||||
|
"title": "Beach Club",
|
||||||
|
"description": "Özel şezlong ve localarımızda gün boyu güneşin ve denizin tadını çıkarın."
|
||||||
|
},
|
||||||
|
"restaurant": {
|
||||||
|
"title": "Restoran & Bar",
|
||||||
|
"description": "Akdeniz mutfağından seçme lezzetler ve imza kokteyllerimizle tanışın."
|
||||||
|
},
|
||||||
|
"hotel": {
|
||||||
|
"title": "Boutique Otel",
|
||||||
|
"description": "Konforunuz için özenle tasarlanmış odalarımızda uyanın."
|
||||||
|
},
|
||||||
|
"events": {
|
||||||
|
"title": "Özel Etkinlikler",
|
||||||
|
"description": "Unutulmaz partiler ve canlı DJ performanslarıyla ritmi yakalayın."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gallery": {
|
||||||
|
"title": "Atmosfer",
|
||||||
|
"heading": "Galeri",
|
||||||
|
"categories": {
|
||||||
|
"all": "Tümü",
|
||||||
|
"beach": "Plaj",
|
||||||
|
"restaurant": "Restoran",
|
||||||
|
"rooms": "Odalar",
|
||||||
|
"events": "Etkinlikler"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"social": {
|
||||||
|
"title": "Sosyal Medya",
|
||||||
|
"heading": "Instagram'da Biz",
|
||||||
|
"button": "Tüm Reels'ları Gör"
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"title": "İletişim",
|
||||||
|
"heading": "Bize Ulaşın",
|
||||||
|
"address": {
|
||||||
|
"title": "Adres",
|
||||||
|
"text1": "Akyaka, Atatürk 1. Cd No:86",
|
||||||
|
"text2": "48640 Ula/Muğla"
|
||||||
|
},
|
||||||
|
"phone": "Telefon",
|
||||||
|
"email": "E-Posta",
|
||||||
|
"hours": {
|
||||||
|
"title": "Çalışma Saatleri",
|
||||||
|
"text": "Her Gün: 09:00 - 02:00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"desc": "Ege'nin serin sularıyla lüksün buluştuğu eşsiz bir kaçış. Moy Group güvencesiyle.",
|
||||||
|
"pages": "Sayfalar",
|
||||||
|
"contact": "İletişim",
|
||||||
|
"rights": "Moy Beach Akyaka. Tüm hakları saklıdır.",
|
||||||
|
"created_by": "Created by"
|
||||||
|
}
|
||||||
|
}
|
||||||
+120
@@ -0,0 +1,120 @@
|
|||||||
|
# 🌊 Moy Beach Akyaka — Web Sitesi Yenileme Prompt'u
|
||||||
|
|
||||||
|
## Mevcut Site Analizi
|
||||||
|
|
||||||
|
**URL:** https://www.moybeachakyaka.com/
|
||||||
|
**Konsept:** Beach & Hotel — Akyaka, Muğla
|
||||||
|
**Mevcut Durum:**
|
||||||
|
- Tek sayfalık (one-page) bir yapı
|
||||||
|
- Sadece 1 navigasyon öğesi: "KURUMSAL" (dış bağlantı)
|
||||||
|
- Hero bölümü: tam ekran fotoğraf + "MOY BEACH" yazısı
|
||||||
|
- İçerik: yalnızca fotoğraf galerisi (6 görsel) + YouTube video + Google Harita
|
||||||
|
- Footer: Eagle-Themes tasarımı, minimal bilgi
|
||||||
|
- Metin içeriği neredeyse yok; SEO değeri çok düşük
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Yenileme Prompt'u
|
||||||
|
|
||||||
|
Sen deneyimli bir web tasarımcısı ve UX stratejistisisin.
|
||||||
|
**Moy Beach Akyaka** için mevcut web sitesini, lüks bir Ege sahil deneyimini yansıtacak şekilde baştan tasarla.
|
||||||
|
|
||||||
|
### 🎯 Hedef Kitle
|
||||||
|
- Yerli ve yabancı turistler (yaz sezonu odaklı)
|
||||||
|
- Rezervasyon yapmak isteyen bireyler ve aileler
|
||||||
|
- Düğün, özel etkinlik ve kurumsal organizasyon arayan gruplar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🗂️ Sayfa Yapısı ve İçerik
|
||||||
|
|
||||||
|
#### 1. Header / Navigasyon
|
||||||
|
- Sticky (sayfaya yapışık) navbar
|
||||||
|
- Logo sol tarafta, menü sağ tarafta
|
||||||
|
- Menü öğeleri: **Hakkımızda | Hizmetler | Galeri | Etkinlikler | İletişim | Rezervasyon**
|
||||||
|
- Dil seçeneği: TR / EN
|
||||||
|
- "Rezervasyon Yap" butonu — belirgin CTA (örnek: altın sarısı/turuncu)
|
||||||
|
|
||||||
|
#### 2. Hero Bölümü
|
||||||
|
- Tam ekran video veya yüksek çözünürlüklü slider (mevcut fotoğraflar kullanılabilir)
|
||||||
|
- Üzerine yerleştirilmiş kısa slogan: *"Ege'nin En Güzel Köşesinde Bir Kaçış"*
|
||||||
|
- Alt kısımda kaydırma yönlendirici ok animasyonu
|
||||||
|
|
||||||
|
#### 3. Hakkımızda
|
||||||
|
- Moy Beach'in hikayesi, felsefesi ve Moy Group bağlantısı
|
||||||
|
- Öne çıkan özellikler: konum, atmosfer, deneyim kalitesi
|
||||||
|
- Kısa metin + yan yana görsel
|
||||||
|
|
||||||
|
#### 4. Hizmetler / Deneyimler
|
||||||
|
- Beach Club (şezlong, şemsiye, bar)
|
||||||
|
- Restoran & Yemek (menü bağlantısı)
|
||||||
|
- Otel / Konaklama seçenekleri
|
||||||
|
- Özel etkinlikler & organizasyon
|
||||||
|
- Her hizmet için ikon, başlık ve kısa açıklama kartı
|
||||||
|
|
||||||
|
#### 5. Galeri
|
||||||
|
- Masonry veya grid düzeninde filtrelenebilir fotoğraf galerisi
|
||||||
|
- Kategoriler: Plaj | Restoran | Odalar | Etkinlikler
|
||||||
|
- Lightbox ile tam ekran görüntüleme
|
||||||
|
|
||||||
|
#### 6. Video Bölümü
|
||||||
|
- Mevcut YouTube videosu tam ekran arka plan veya embed olarak
|
||||||
|
- Otomatik oynatma (sessiz), kullanıcı sesi açabilir
|
||||||
|
|
||||||
|
#### 7. Yorumlar / Referanslar
|
||||||
|
- Google veya TripAdvisor yorumlarından seçmeler (şu an 3.3 ⭐ — iyileştirme notu ekle)
|
||||||
|
- Misafir fotoğrafları ile birlikte alıntı kutuları
|
||||||
|
|
||||||
|
#### 8. Konum & İletişim
|
||||||
|
- Mevcut Google Harita embed'i korunabilir
|
||||||
|
- Adres: Akyaka, Atatürk 1. Cd No:88, 48640 Ula/Muğla
|
||||||
|
- Telefon, e-posta, sosyal medya ikonları
|
||||||
|
- İletişim formu (ad, e-posta, mesaj, tarih seçici)
|
||||||
|
|
||||||
|
#### 9. Rezervasyon Bölümü
|
||||||
|
- Tarih seçimi (giriş/çıkış)
|
||||||
|
- Kişi sayısı
|
||||||
|
- Hizmet tipi seçimi
|
||||||
|
- "Rezervasyon Talebi Gönder" CTA butonu
|
||||||
|
|
||||||
|
#### 10. Footer
|
||||||
|
- Logo + kısa açıklama
|
||||||
|
- Hızlı bağlantılar
|
||||||
|
- Sosyal medya (Instagram, Facebook, YouTube)
|
||||||
|
- KVKK / Gizlilik Politikası bağlantıları
|
||||||
|
- © 2026 Moy Beach Akyaka
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🎨 Tasarım Rehberi
|
||||||
|
|
||||||
|
| Özellik | Öneri |
|
||||||
|
|---|---|
|
||||||
|
| **Renk Paleti** | Kum beji, deniz mavisi, mercan/turuncu vurgu, beyaz |
|
||||||
|
| **Tipografi** | Serif başlık (örn. Playfair Display) + sans-serif gövde (örn. Lato) |
|
||||||
|
| **Duygu** | Lüks ama samimi, doğal, rahatlatıcı |
|
||||||
|
| **Animasyonlar** | Scroll-triggered fade-in, parallax efekti hero'da |
|
||||||
|
| **Görseller** | Mevcut profesyonel fotoğraflar korunmalı, yeni çekimler eklenebilir |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ⚙️ Teknik Gereksinimler
|
||||||
|
|
||||||
|
- **Responsive** tasarım: mobil öncelikli (mobile-first)
|
||||||
|
- **Sayfa hızı:** Core Web Vitals optimizasyonu (LCP < 2.5s)
|
||||||
|
- **SEO:** Meta etiketleri, schema.org yapılandırması (Hotel, Restaurant, LocalBusiness)
|
||||||
|
- **CMS:** WordPress veya Webflow (içerik kolayca güncellenebilmeli)
|
||||||
|
- **SSL** zorunlu
|
||||||
|
- **Google Analytics 4** entegrasyonu
|
||||||
|
- **WhatsApp** hızlı iletişim butonu (sağ alt köşe sabit)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🚀 Öncelik Sırası (MVP)
|
||||||
|
|
||||||
|
1. Hero + Navigasyon
|
||||||
|
2. Hizmetler kartları
|
||||||
|
3. Galeri
|
||||||
|
4. İletişim & Harita
|
||||||
|
5. Rezervasyon formu
|
||||||
|
6. Çok dilli destek (TR/EN)
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import type { NextRequest } from "next/server";
|
||||||
|
|
||||||
|
const locales = ["tr", "en"];
|
||||||
|
const defaultLocale = "tr";
|
||||||
|
|
||||||
|
export function middleware(request: NextRequest) {
|
||||||
|
// Check if there is any supported locale in the pathname
|
||||||
|
const { pathname } = request.nextUrl;
|
||||||
|
|
||||||
|
const pathnameHasLocale = locales.some(
|
||||||
|
(locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pathnameHasLocale) return;
|
||||||
|
|
||||||
|
// If no locale found, redirect to default locale
|
||||||
|
request.nextUrl.pathname = `/${defaultLocale}${pathname}`;
|
||||||
|
return NextResponse.redirect(request.nextUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
matcher: [
|
||||||
|
// Skip all internal paths (_next) and static files
|
||||||
|
'/((?!api|_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
|
||||||
|
],
|
||||||
|
};
|
||||||
Generated
+53
@@ -8,6 +8,8 @@
|
|||||||
"name": "moybeach",
|
"name": "moybeach",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"framer-motion": "^12.40.0",
|
||||||
|
"lucide-react": "^1.17.0",
|
||||||
"next": "16.2.7",
|
"next": "16.2.7",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"react-dom": "19.2.4"
|
"react-dom": "19.2.4"
|
||||||
@@ -3762,6 +3764,33 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/framer-motion": {
|
||||||
|
"version": "12.40.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.40.0.tgz",
|
||||||
|
"integrity": "sha512-uaBd3qC1v3KQqBEjwTUd183K6PbS+j0yR9w9VmEOLWA/tnUcSn8Xa3uck7t4dgpDoUss8xQTcj8W2L07lrnLFg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-dom": "^12.40.0",
|
||||||
|
"motion-utils": "^12.39.0",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/is-prop-valid": "*",
|
||||||
|
"react": "^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/is-prop-valid": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
@@ -5032,6 +5061,15 @@
|
|||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lucide-react": {
|
||||||
|
"version": "1.17.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.17.0.tgz",
|
||||||
|
"integrity": "sha512-9FA9evdox/JQL5PT57fdA1x/yg8T7knJ98+zjTL3UfKza6pflQUUh3XtaQIHKvnsJw1lmsEyHVlt5jchYxOQ5w==",
|
||||||
|
"license": "ISC",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/magic-string": {
|
"node_modules/magic-string": {
|
||||||
"version": "0.30.21",
|
"version": "0.30.21",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||||
@@ -5099,6 +5137,21 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/motion-dom": {
|
||||||
|
"version": "12.40.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.40.0.tgz",
|
||||||
|
"integrity": "sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-utils": "^12.39.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/motion-utils": {
|
||||||
|
"version": "12.39.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz",
|
||||||
|
"integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
"lint": "eslint"
|
"lint": "eslint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"framer-motion": "^12.40.0",
|
||||||
|
"lucide-react": "^1.17.0",
|
||||||
"next": "16.2.7",
|
"next": "16.2.7",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"react-dom": "19.2.4"
|
"react-dom": "19.2.4"
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
Reference in New Issue
Block a user