diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..288e979 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,55 @@ +# 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 based on the preferred package manager +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 +# Coolify will provide these, but we can set defaults +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 +# https://nextjs.org/docs/advanced-features/output-file-tracing +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 +# set hostname to localhost +ENV HOSTNAME="0.0.0.0" + +CMD ["node", "server.js"] diff --git a/app/about/page.tsx b/app/about/page.tsx new file mode 100644 index 0000000..c4a0006 --- /dev/null +++ b/app/about/page.tsx @@ -0,0 +1,287 @@ +'use client' + +import { useRef, useState, useEffect } from 'react' +import { motion, useScroll, useTransform } from 'framer-motion' +import Image from 'next/image' +import Link from 'next/link' + +const services = [ + { + title: 'Mimari Hizmetler', + description: 'A.N.Tarchitecture, proaktif entegre tasarım yaklaşımıyla kapsamlı bir profesyonel mimari hizmet yelpazesi sunar:', + list: [ + 'Proje Geliştirme: Saha analizi, potansiyel çalışma, sosyal kabul edilebilirlik stratejileri ve planlama', + 'Plan ve Şartnamelerin Tasarımı ve Üretimi.', + 'Proje Modelleme Yönetimi: Entegre BIM (Yapı Bilgi Modellemesi) tasarımı', + 'Düzenleyici Stratejiler: Yönetmelikler, saha çalışması, enerji ve güneş ışığı analizi', + 'Kentsel Tasarım: Master planlama, peyzaj tasarımı ve inşaat mühendisliği işleri', + '3D Görselleştirme: 3D perspektifler, modelleme ve animasyonlar', + 'İnşaat Hizmeti ve Yönetimi: İnşaat yönetimi ve saha denetimi' + ], + button: 'PROJELERİ GÖRÜNTÜLE', + image: 'https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?q=80&w=2070&auto=format&fit=crop' + }, + { + title: 'İç Mimari Hizmetleri', + description: 'A.N.Tarchitecture, sürdürülebilirlik, zarafet ve işlevselliği şirketinizin değerleriyle uyumlu akıllı düzenlerle birleştirerek, imajını ve işe alım kapasitesini artıran iç mimari konusunda derin bir uzmanlık sunar:', + list: [ + 'Aktif Müşteri Dinleme: Alan programlama ve planlama, bütçe analizi', + 'Konsept Geliştirme: Plan ve şartnamelerin üretimi, malzeme seçimi, entegre mobilya ve özel objelerin kavramsallaştırılması', + 'Entegre Tabela: Plan ve şartnamelerin modellenmesi ve üretimi', + '3D Görselleştirme: 3D perspektifler, modelleme ve animasyonlar' + ], + button: 'TÜM PROJELER', + image: 'https://images.unsplash.com/photo-1600210492486-724fe5c67fb0?q=80&w=2070&auto=format&fit=crop' + }, + { + title: 'Planlama ve Uzmanlık Hizmetleri', + description: 'A.N.Tarchitecture, bina planlama, programlama ve denetleme konularında, müşteri ihtiyaçlarını ve gereksinimlerini net bir şekilde tanımlamak için titiz ve sistematik yöntemler kullanarak çeşitli uzmanlıklar sunar.', + list: [ + 'Fonksiyonel ve Teknik Programların (FTP) Geliştirilmesi: Müdahale stratejileri, organizasyonun genel özellikleri, işlevsellik ve alanlar', + 'Fizibilite Çalışması ve Ön Konseptler: Teknik ve düzenleyici koşulların ve kısıtlamaların analizi', + 'Teknik Uzmanlık: Su yalıtım sorunlarının ve erken yaşlanmanın tespiti, yaşam döngüsü analizi' + ], + button: null, + image: 'https://images.unsplash.com/photo-1518780664697-55e3ad937233?q=80&w=2070&auto=format&fit=crop' + } +] + +const projectPhases = [ + { num: '00', title: 'ÖN KAVRAMSAL ÇALIŞMALAR', desc: 'Bir binanın kapsamını belirlemek için ihtiyaçlarını ve işlevlerini anlamaya ve belgelemeye yardımcı olan hazırlık aşaması.' }, + { num: '01', title: 'TASARIM AŞAMASI', desc: 'Bu aşama saha incelemesi, program incelemesi, ilk eskiz tasarımı ve ön bütçe analizini içerir.' }, + { num: '02', title: 'TASARIM GELİŞTİRME', desc: 'Eskizler onaylandıktan sonra, gerekli ön planları, kesitleri ve görünüşleri oluşturarak projeyi daha detaylı geliştiriyoruz.' }, + { num: '03', title: 'İNŞAAT BELGELERİ', desc: 'Bu inşaat için gerekli olan teknik planların gerçek geliştirilme aşamasıdır.' }, + { num: '04', title: 'İHALE', desc: 'Genel yüklenici tekliflerinin analizinde ve inşaat sözleşmesinin imzalanmasında müşteriye yardımcı oluyor ve danışmanlık yapıyoruz.' }, + { num: '05', title: 'SAHA DENETİMİ', desc: 'Projenin planlara sadık kalarak yürütülmesini sağlıyoruz. İnşaat sahasındaki öngörülemeyen sorunlara tasarımı uyarlamak için çözümler sunuyoruz.' } +] + +export default function AboutPage() { + const targetRef = useRef(null) + const { scrollY } = useScroll() + const [isAtBottom, setIsAtBottom] = useState(false) + + useEffect(() => { + const handleScroll = () => { + const windowHeight = window.innerHeight + const documentHeight = document.documentElement.scrollHeight + const scrollPosition = window.scrollY + windowHeight + setIsAtBottom(scrollPosition > documentHeight - 150) + } + window.addEventListener('scroll', handleScroll) + return () => window.removeEventListener('scroll', handleScroll) + }, []) + + const cargoFontSize = useTransform(scrollY, [0, 300], ["10vw", "4vw"]) + const archFontSize = useTransform(scrollY, [0, 300], ["8vw", "3vw"]) + const bottomPadding = useTransform(scrollY, [0, 300], ["2.5rem", "1rem"]) + + const { scrollYProgress } = useScroll({ + target: targetRef, + offset: ["start start", "end end"] + }) + + return ( +
+ + {/* SECTION 1: Intro */} +
+
+ +

+ Mimariyi, teknik disiplin ile yaratıcı vizyonun kusursuz dengesi olarak tanımlıyorum. Tasarım sürecimde hem iç hem de dış mekân projelerinde bütüncül bir yaklaşımı benimsiyor; projelerimi sadece birer yapı olarak değil, çevresiyle nefes alan yaşam alanları olarak tasarlıyorum. + + Projelerimin teknik omurgasını AutoCAD üzerinde detaylı plan ve kesit çalışmalarıyla oluştururken, 3D modelleme ve görselleştirme süreçlerinde sunduğum gerçekçi sunumlarla tasarıma derinlik kazandırıyorum. Konsept tasarımından uygulama detaylarına kadar her aşamada, mekânın işlevselliğini estetik bir değerle harmanlamayı amaçlıyorum.

+
+ + +

+ 2006 yılında Toronto'da CGBWstudio adıyla Charles-Bernard Gagnon tarafından kurulan ve 2008'de Quebec City'ye taşınan firma, felsefesi ve sürecin her aşamasında yüksek kaliteli profesyonel hizmet sunma taahhüdü sayesinde bölge pazarında öne çıkmıştır. Bu yaklaşım, firmanın yenilikçi projelerden oluşan bir portföy oluşturmasını sağlamıştır. 2013 yılında ekip, daha anlamlı bir isim olan A.N.Tarchitecture adını seçmiştir. +

+ +
+
+ Ayça Nur Turhan +
+
+ AYÇA NUR TURHAN KURUCU
MİMAR, OAQ +
+
+
+
+
+ + {/* SECTION 2: Services – Sequential Scroll Animation */} +
+
+
+ {services.map((service, index) => ( + + ))} +
+
+ + {/* Mobile Vertical Stack */} +
+ {services.map((service, index) => ( + + ))} +
+
+ + {/* SECTION 3: Project Phases */} +
+
+
+

Proje Aşamaları

+ + PROJENİZİ BAŞLATIN + +
+ +
+ {projectPhases.map((phase, idx) => ( +
+
{phase.num}
+
{phase.title}
+
{phase.desc}
+
+ ))} +
+
+
+
+ + {/* BOTTOM BRANDING */} + + + A.N.T + ARCHITECTURE + + + +
+ ) +} + +function ServiceCard({ service, index, scrollYProgress }: { service: any, index: number, scrollYProgress: any }) { + // Cards start visible but at different Y positions (staggered cascade) + // Card 0: y=0 (top), Card 1: y=200px lower, Card 2: y=400px lower + // As user scrolls, each card slides UP to y=0 to align with Card 0 + const initialOffsets = [0, 200, 400] + + // Sequential timing: Card 1 arrives first, Card 2 arrives after + const ranges: [number, number][] = [ + [0, 0.01], // Card 0: always in place + [0, 0.50], // Card 1: slides up during first half + [0.15, 0.85], // Card 2: starts after Card 1 is moving, arrives by 85% + ] + + const y = useTransform( + scrollYProgress, + ranges[index], + [initialOffsets[index], 0] + ) + + return ( + + {/* Content Area */} +
+

+ {service.title} +

+

+ {service.description} +

+
    + {service.list.map((item: string, i: number) => ( +
  • + + {item} +
  • + ))} +
+ {service.button && ( + + {service.button} + + )} +
+ + {/* Fixed Image at Bottom */} +
+ {service.title} +
+
+ ) +} + +function ServiceCardMobile({ service }: { service: any }) { + return ( +
+
+

{service.title}

+

{service.description}

+
    + {service.list.map((item: string, i: number) => ( +
  • + + {item} +
  • + ))} +
+ {service.button && ( + + {service.button} + + )} +
+
+ {service.title} +
+
+ ) +} diff --git a/app/contact/page.tsx b/app/contact/page.tsx new file mode 100644 index 0000000..edd94ff --- /dev/null +++ b/app/contact/page.tsx @@ -0,0 +1,68 @@ +'use client' + +import Link from 'next/link' + +const socialLinks = [ + { name: 'FACEBOOK', href: 'https://facebook.com' }, + { name: 'INSTAGRAM', href: 'https://instagram.com' }, + { name: 'LINKEDIN', href: 'https://linkedin.com' }, + { name: '***', href: '**' }, +] + +const offices = [ + { + num: '01', + name: 'Fethiye | Merkez Ofis', + address: 'Karagözler, 48300 Fethiye/Muğla', + phone: '0553 093 72 25' + } +] + +export default function ContactPage() { + + return ( +
+ +
+
+ {socialLinks.map((link) => ( + + {link.name} + + ))} +
+
+ +
+
+ {offices.map((office) => ( +
+
+ {office.num} +
+
+ {office.name} +
+
+ {office.address} +
+
+ {office.phone} +
+
+ ))} +
+
+ + + +
+ ) +} diff --git a/app/globals.css b/app/globals.css index a2dc41e..04c6f79 100644 --- a/app/globals.css +++ b/app/globals.css @@ -10,6 +10,8 @@ --color-foreground: var(--foreground); --font-sans: var(--font-geist-sans); --font-mono: var(--font-geist-mono); + --font-bebas: var(--font-bebas-neue); + --font-swiss: var(--font-oswald); } @media (prefers-color-scheme: dark) { @@ -22,5 +24,15 @@ body { background: var(--background); color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + font-family: var(--font-oswald), Arial, Helvetica, sans-serif; +} + +@layer utilities { + .no-scrollbar::-webkit-scrollbar { + display: none; + } + .no-scrollbar { + -ms-overflow-style: none; + scrollbar-width: none; + } } diff --git a/app/layout.tsx b/app/layout.tsx index 976eb90..72828bd 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; +import { Geist, Geist_Mono, Bebas_Neue, Oswald } from "next/font/google"; import "./globals.css"; +import LayoutContent from "@/components/LayoutContent"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -12,9 +13,20 @@ const geistMono = Geist_Mono({ subsets: ["latin"], }); +const bebasNeue = Bebas_Neue({ + variable: "--font-bebas-neue", + weight: "400", + subsets: ["latin"], +}); + +const oswald = Oswald({ + variable: "--font-oswald", + subsets: ["latin", "latin-ext"], +}); + export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "A.N.T ARCHITECTURE", + description: "Ayça Nur Turhan - Mimarlık ve Tasarım", }; export default function RootLayout({ @@ -24,10 +36,12 @@ export default function RootLayout({ }>) { return ( - {children} + + {children} + ); } diff --git a/app/page.tsx b/app/page.tsx index 3f36f7c..d6b4d90 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,65 +1,108 @@ -import Image from "next/image"; +'use client' + +import { useState, useEffect } from 'react' +import { motion, AnimatePresence } from 'framer-motion' +import ProjectSlider from '@/components/ProjectSlider' +import { projects } from '@/data/projects' export default function Home() { + const [isRevealed, setIsRevealed] = useState(false) + const [isAtCorners, setIsAtCorners] = useState(false) + + useEffect(() => { + const reveal = setTimeout(() => setIsRevealed(true), 500) + const move = setTimeout(() => setIsAtCorners(true), 2500) + + return () => { + clearTimeout(reveal) + clearTimeout(move) + } + }, []) + + const transition = { duration: 1.8, ease: [0.76, 0, 0.24, 1] } + return ( -
-
- Next.js logo -
-

- To get started, edit the page.tsx file. -

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -

-
-
- + +
+ -
-
- ); + + + + {isAtCorners && ( + + + + )} + + + + ) } diff --git a/app/projects/page.tsx b/app/projects/page.tsx new file mode 100644 index 0000000..e9b6873 --- /dev/null +++ b/app/projects/page.tsx @@ -0,0 +1,87 @@ +'use client' + +import { motion, useScroll, useTransform } from 'framer-motion' +import Image from 'next/image' +import { projects } from '@/data/projects' +import { useState, useEffect } from 'react' + +export default function ProjectsPage() { + const { scrollY } = useScroll() + const [isAtBottom, setIsAtBottom] = useState(false) + + useEffect(() => { + const handleScroll = () => { + const windowHeight = window.innerHeight + const documentHeight = document.documentElement.scrollHeight + const scrollPosition = window.scrollY + windowHeight + setIsAtBottom(scrollPosition > documentHeight - 100) + } + window.addEventListener('scroll', handleScroll) + return () => window.removeEventListener('scroll', handleScroll) + }, []) + + const cargoFontSize = useTransform(scrollY, [0, 300], ["10vw", "4vw"]) + const archFontSize = useTransform(scrollY, [0, 300], ["8vw", "3vw"]) + const bottomPadding = useTransform(scrollY, [0, 300], ["2.5rem", "1rem"]) + + return ( +
+
+ {projects.map((project, idx) => ( + +
+ {project.title} +
+
+ + {project.year} — {project.location} + +
+ + {project.title} + + +
+
+
+ ))} +
+ + + + + A.N.T + + + ARCHITECTURE + + + + +
+ ) +} diff --git a/components/Footer.tsx b/components/Footer.tsx new file mode 100644 index 0000000..52b143c --- /dev/null +++ b/components/Footer.tsx @@ -0,0 +1,35 @@ +import Link from 'next/link' + +export default function Footer() { + return ( + + ) +} diff --git a/components/LayoutContent.tsx b/components/LayoutContent.tsx new file mode 100644 index 0000000..98e2cb0 --- /dev/null +++ b/components/LayoutContent.tsx @@ -0,0 +1,20 @@ +'use client' + +import { usePathname } from "next/navigation"; +import Navbar from "@/components/Navbar"; +import Footer from "@/components/Footer"; + +export default function LayoutContent({ children }: { children: React.ReactNode }) { + const pathname = usePathname(); + const isHome = pathname === "/"; + + return ( + <> + +
+ {children} +
+ {!isHome &&