kh3_website/projects.html

1732 lines
60 KiB
HTML

<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>What - KH3</title>
<link rel="stylesheet" href="styles/main.css" />
<meta
name="description"
content="Explore KH3's portfolio of construction projects, fit-outs, and design developments across Ghana and West Africa."
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700;800;900&display=swap"
rel="stylesheet"
/>
</head>
<body class="bg-kh3-black text-white font-montserrat overflow-x-hidden">
<!-- Navigation Menu -->
<div
class="fixed left-0 top-0 h-full w-5/6 md:w-1/3 bg-kh3-black z-50 hidden shadow-2xl"
id="navMenu"
>
<div class="p-8 flex flex-col h-full">
<!-- Logo -->
<div class="flex items-center gap-3 mb-12">
<a
href="index.html"
class="flex items-center gap-3 hover:opacity-80 transition-opacity"
>
<img src="assets/images/kh3_logo.png" alt="KH3" class="w-12 h-12" />
<span class="text-white text-2xl font-bold tracking-wider"
>KH3</span
>
</a>
</div>
<!-- Navigation Links - Inverted C Layout -->
<nav class="flex-1 relative">
<div class="absolute inset-0 flex items-center">
<div class="relative ml-8">
<!-- Navigation items in inverted C shape -->
<div class="space-y-6">
<!-- HOME - Top -->
<a
href="index.html"
class="block text-white text-xl md:text-lg font-medium hover:text-kh3-red transition-colors ml-0 font-montserrat opacity-0 animate-fade-in-left"
style="
animation-delay: 0.05s;
animation-fill-mode: forwards;
animation-duration: 0.3s;
"
data-trans="crossfade"
>
<span class="md:hidden">HOME</span>
<span
class="hidden md:inline"
style="
animation-delay: 0.05s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>H</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.075s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>O</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.1s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>M</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.125s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>E</span
>
</a>
<!-- WHY - Slightly right -->
<a
href="about.html"
class="block text-white text-xl md:text-lg font-medium hover:text-kh3-red transition-colors ml-0 md:ml-6 font-montserrat opacity-0 animate-fade-in-left"
style="
animation-delay: 0.2s;
animation-fill-mode: forwards;
animation-duration: 0.3s;
"
data-trans="crossfade"
>
<span class="md:hidden">WHY</span>
<span
class="hidden md:inline"
style="
animation-delay: 0.2s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>W</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.225s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>H</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.25s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>Y</span
>
</a>
<!-- HOW - More right -->
<a
href="services.html"
class="block text-white text-xl md:text-lg font-medium hover:text-kh3-red transition-colors ml-0 md:ml-12 font-montserrat opacity-0 animate-fade-in-left"
style="
animation-delay: 0.35s;
animation-fill-mode: forwards;
animation-duration: 0.3s;
"
data-trans="crossfade"
>
<span class="md:hidden">HOW</span>
<span
class="hidden md:inline"
style="
animation-delay: 0.35s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>H</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.375s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>O</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.4s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>W</span
>
</a>
<!-- WHAT - Most right -->
<a
href="projects.html"
class="block text-white text-xl md:text-lg font-medium border-b border-white pb-1 ml-0 md:ml-20 font-montserrat opacity-0 animate-fade-in-left"
style="
animation-delay: 0.55s;
animation-fill-mode: forwards;
animation-duration: 0.3s;
"
>
<span class="md:hidden">WHAT</span>
<span
class="hidden md:inline"
style="
animation-delay: 0.55s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>W</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.575s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>H</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.6s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>A</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.625s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>T</span
>
</a>
<!-- WHO - New position -->
<a
href="who.html"
class="block text-white text-xl md:text-lg font-medium hover:text-kh3-red transition-colors ml-0 md:ml-20 font-montserrat opacity-0 animate-fade-in-left"
style="
animation-delay: 0.65s;
animation-fill-mode: forwards;
animation-duration: 0.3s;
"
data-trans="crossfade"
>
<span class="md:hidden">WHO</span>
<span
class="hidden md:inline"
style="
animation-delay: 0.65s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>W</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.675s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>H</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.7s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>O</span
>
</a>
<!-- CONTACTS - More right -->
<a
href="contact.html"
class="block text-white text-xl md:text-lg font-medium hover:text-kh3-red transition-colors ml-0 md:ml-12 font-montserrat opacity-0 animate-fade-in-left"
style="
animation-delay: 0.75s;
animation-fill-mode: forwards;
animation-duration: 0.3s;
"
data-trans="crossfade"
>
<span class="md:hidden">CONTACTS</span>
<span
class="hidden md:inline"
style="
animation-delay: 0.75s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>C</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.775s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>O</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.8s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>N</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.825s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>T</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.85s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>A</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.875s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>C</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.9s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>T</span
>
<span
class="hidden md:inline"
style="
animation-delay: 0.925s;
animation-fill-mode: forwards;
animation-duration: 0.15s;
"
>S</span
>
</a>
</div>
</div>
</div>
</nav>
</div>
</div>
<!-- Static Logo and Menu -->
<div class="fixed top-6 left-8 z-40">
<a
href="index.html"
class="flex items-center gap-3 hover:opacity-80 transition-opacity"
>
<img
src="assets/images/kh3_logo_dark.png"
alt="KH3"
class="w-12 h-12"
/>
<span
class="text-white text-2xl font-bold tracking-wider drop-shadow-lg"
>KH3</span
>
</a>
</div>
<!-- Menu Toggle with Transparent Background -->
<div class="fixed top-6 right-8 z-40">
<div
class="cursor-pointer bg-black/30 backdrop-blur-sm rounded-lg p-3"
id="menuToggle"
>
<div class="grid grid-cols-3 gap-1 w-6 h-6" id="menuGrid">
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
</div>
</div>
</div>
<!-- Main Content -->
<main class="pt-20">
<!-- Hero Section -->
<section class="relative bg-kh3-black hero-section">
<div class="grid grid-cols-1 md:grid-cols-2 min-h-[90vh]">
<!-- Left dark panel -->
<div class="relative bg-kh3-black flex items-center justify-center">
<!-- Vertical timeline with diamonds -->
<div
class="absolute left-12 top-8 bottom-16 flex flex-col items-center opacity-0 animate-fade-in-up"
style="animation-delay: 1200ms; animation-fill-mode: forwards"
>
<div class="w-px flex-1 bg-neutral-800"></div>
<div class="w-2.5 h-2.5 bg-white transform rotate-45"></div>
<div class="w-px flex-1 bg-neutral-800"></div>
<div class="w-2.5 h-2.5 bg-white transform rotate-45"></div>
<div class="w-px flex-1 bg-neutral-800"></div>
<div class="w-2.5 h-2.5 bg-white transform rotate-45"></div>
<div class="w-px flex-1 bg-neutral-800"></div>
</div>
<!-- Title with horizontal ruler -->
<div class="pl-28 pr-6 w-full relative overflow-visible z-30">
<div
class="flex items-center gap-6 opacity-0 animate-fade-in-up"
style="animation-delay: 1600ms; animation-fill-mode: forwards"
>
<div class="flex-1 h-px bg-white"></div>
<h1
class="whitespace-nowrap text-4xl md:text-6xl tracking-[0.2em] font-bold"
>
OUR WHAT
</h1>
<div class="flex-1 h-px bg-white"></div>
</div>
</div>
</div>
<!-- Right image panel -->
<div class="relative overflow-hidden pt-20 z-10">
<!-- Current image -->
<img
id="projectsCurrentImg"
src="assets/images/room.png"
alt="KH3 Projects"
class="w-full h-full object-cover opacity-0 absolute inset-0"
style="animation: diagMaskReveal 1500ms ease-out 300ms forwards"
/>
<!-- Next image (for transitions) -->
<img
id="projectsNextImg"
src="assets/images/chair.png"
alt="KH3 Projects"
class="w-full h-full object-cover opacity-0 absolute inset-0"
/>
<!-- Down arrow prompt -->
<button
id="projectsScrollArrow"
class="flex absolute bottom-10 left-6 items-center flex-col text-white/80 hover:text-white transition-colors opacity-0 animate-fade-in-up"
style="animation-delay: 1900ms; animation-fill-mode: forwards"
>
<span class="text-xs tracking-widest mb-2">SCROLL</span>
<svg
class="w-5 h-5 animate-arrowPulse"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 9l-7 7-7-7"
/>
</svg>
<div class="w-px h-8 bg-white/70"></div>
</button>
</div>
</div>
</section>
<!-- Projects Grid Section -->
<section class="py-32 bg-kh3-black">
<div class="container mx-auto px-8 max-w-7xl">
<!-- Category Filters -->
<div class="mb-16 text-center">
<div class="flex flex-wrap justify-center gap-4 mb-8">
<button
class="category-filter px-6 py-3 text-white border border-white/30 rounded-full hover:bg-white hover:text-kh3-black transition-all duration-300 active"
data-category="all"
>
ALL WHAT
</button>
<button
class="category-filter px-6 py-3 text-white border border-white/30 rounded-full hover:bg-white hover:text-kh3-black transition-all duration-300"
data-category="Corporate"
>
CORPORATE
</button>
<button
class="category-filter px-6 py-3 text-white border border-white/30 rounded-full hover:bg-white hover:text-kh3-black transition-all duration-300"
data-category="Residential"
>
RESIDENTIAL
</button>
<button
class="category-filter px-6 py-3 text-white border border-white/30 rounded-full hover:bg-white hover:text-kh3-black transition-all duration-300"
data-category="Retail"
>
RETAIL
</button>
<button
class="category-filter px-6 py-3 text-white border border-white/30 rounded-full hover:bg-white hover:text-kh3-black transition-all duration-300"
data-category="Hospitality"
>
HOSPITALITY
</button>
<button
class="category-filter px-6 py-3 text-white border border-white/30 rounded-full hover:bg-white hover:text-kh3-black transition-all duration-300"
data-category="Industrial"
>
INDUSTRIAL
</button>
</div>
</div>
<!-- Projects Grid -->
<div
id="projectsGrid"
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"
>
<!-- Project cards will be dynamically loaded here -->
</div>
</div>
</section>
</main>
<!-- Project Details Modal -->
<div
id="projectModal"
class="fixed inset-0 bg-black/90 backdrop-blur-sm z-50 hidden"
>
<div class="relative w-full h-full flex items-center justify-center p-4">
<!-- Modal Content -->
<div
id="modalContent"
class="bg-kh3-black border border-white/20 rounded-2xl max-w-6xl w-full max-h-[90vh] overflow-hidden transform scale-95 opacity-0 transition-all duration-500"
>
<!-- Modal Header -->
<div
class="flex items-center justify-between p-6 border-b border-white/10"
>
<div>
<h2
id="modalTitle"
class="text-2xl md:text-3xl font-bold text-white mb-2"
></h2>
<p
id="modalCategory"
class="text-kh3-red text-sm font-medium tracking-wider"
></p>
</div>
<button
id="closeModal"
class="text-white/70 hover:text-white transition-colors duration-300"
>
<svg
class="w-8 h-8"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
></path>
</svg>
</button>
</div>
<!-- Modal Body -->
<div class="flex flex-col lg:flex-row h-full">
<!-- Image Carousel Section -->
<div class="lg:w-2/3 relative group">
<div class="relative h-80 lg:h-full overflow-hidden">
<!-- Main Image -->
<img
id="modalMainImage"
src=""
alt=""
class="w-full h-full object-cover transition-all duration-500 cursor-pointer hover:scale-105"
onclick="enlargeImage()"
/>
<!-- Image Navigation -->
<div
class="absolute bottom-4 left-1/2 transform -translate-x-1/2 flex space-x-2"
>
<div id="imageDots" class="flex space-x-2">
<!-- Dots will be generated here -->
</div>
</div>
<!-- Previous/Next Buttons -->
<button
id="prevImage"
class="absolute left-4 top-1/2 transform -translate-y-1/2 bg-black/50 hover:bg-black/70 text-white p-2 rounded-full transition-all duration-300 opacity-0 group-hover:opacity-100"
>
<svg
class="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 19l-7-7 7-7"
></path>
</svg>
</button>
<button
id="nextImage"
class="absolute right-4 top-1/2 transform -translate-y-1/2 bg-black/50 hover:bg-black/70 text-white p-2 rounded-full transition-all duration-300 opacity-0 group-hover:opacity-100"
>
<svg
class="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
></path>
</svg>
</button>
<!-- Image Counter -->
<div
class="absolute top-4 right-4 bg-black/50 text-white px-3 py-1 rounded-full text-sm"
>
<span id="imageCounter">1</span> /
<span id="totalImages">1</span>
</div>
</div>
</div>
<!-- Project Details Section -->
<div
class="lg:w-1/3 p-6 overflow-y-auto"
style="max-height: calc(90vh - 120px)"
>
<div class="space-y-6">
<!-- Project Overview -->
<div>
<h3
class="text-lg font-semibold text-white mb-3 border-b border-white/20 pb-2"
>
PROJECT OVERVIEW
</h3>
<p
id="modalDescription"
class="text-white/80 text-sm leading-relaxed"
></p>
</div>
<!-- Project Details -->
<div>
<h3
class="text-lg font-semibold text-white mb-3 border-b border-white/20 pb-2"
>
PROJECT DETAILS
</h3>
<div class="space-y-3">
<div class="flex justify-between">
<span class="text-white/60 text-sm">Client:</span>
<span
id="modalClient"
class="text-white text-sm font-medium"
></span>
</div>
<div class="flex justify-between">
<span class="text-white/60 text-sm">Type of Work:</span>
<span
id="modalTypeOfWork"
class="text-white text-sm font-medium"
></span>
</div>
<div class="flex justify-between">
<span class="text-white/60 text-sm">Size:</span>
<span
id="modalSize"
class="text-white text-sm font-medium"
></span>
</div>
<div class="flex justify-between">
<span class="text-white/60 text-sm">Duration:</span>
<span
id="modalDuration"
class="text-white text-sm font-medium"
></span>
</div>
<div class="flex justify-between">
<span class="text-white/60 text-sm">Completion:</span>
<span
id="modalCompletionDate"
class="text-white text-sm font-medium"
></span>
</div>
<div class="flex justify-between">
<span class="text-white/60 text-sm">Location:</span>
<span
id="modalLocation"
class="text-white text-sm font-medium"
></span>
</div>
</div>
</div>
<!-- Project Highlights -->
<div>
<h3
class="text-lg font-semibold text-white mb-3 border-b border-white/20 pb-2"
>
HIGHLIGHTS
</h3>
<ul id="modalHighlights" class="space-y-2">
<!-- Highlights will be generated here -->
</ul>
</div>
<!-- Action Buttons -->
<div class="pt-4 border-t border-white/20">
<a
href="contact.html"
class="block w-full bg-kh3-red hover:bg-red-700 text-white py-3 px-6 rounded-lg font-medium transition-all duration-300 transform hover:scale-105 text-center"
data-trans="crossfade"
>
WORK WITH US
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Image Enlargement Modal -->
<div
id="imageEnlargeModal"
class="fixed inset-0 bg-black/95 backdrop-blur-sm z-[60] hidden"
>
<div
class="relative w-full h-full flex items-center justify-center p-4"
>
<div class="relative max-w-7xl max-h-full">
<!-- Enlarged Image -->
<img
id="enlargedImage"
src=""
alt=""
class="max-w-full max-h-full object-contain rounded-lg shadow-2xl"
/>
<!-- Close Button -->
<button
id="closeEnlargeModal"
class="absolute -top-4 -right-4 bg-kh3-red hover:bg-red-700 text-white p-3 rounded-full transition-all duration-300 transform hover:scale-110"
>
<svg
class="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
></path>
</svg>
</button>
<!-- Image Info -->
<div
class="absolute bottom-4 left-4 bg-black/70 text-white px-4 py-2 rounded-lg"
>
<span id="enlargedImageCounter">1</span> /
<span id="enlargedTotalImages">1</span>
</div>
<!-- Navigation Arrows -->
<button
id="enlargePrevImage"
class="absolute left-4 top-1/2 transform -translate-y-1/2 bg-black/50 hover:bg-black/70 text-white p-4 rounded-full transition-all duration-300"
>
<svg
class="w-8 h-8"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 19l-7-7 7-7"
></path>
</svg>
</button>
<button
id="enlargeNextImage"
class="absolute right-4 top-1/2 transform -translate-y-1/2 bg-black/50 hover:bg-black/70 text-white p-4 rounded-full transition-all duration-300"
>
<svg
class="w-8 h-8"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
></path>
</svg>
</button>
</div>
</div>
</div>
</div>
<style>
/* Project card animations */
@keyframes projectCardFloat {
0%,
100% {
transform: translateY(0px) scale(1);
}
50% {
transform: translateY(-8px) scale(1.02);
}
}
@keyframes projectImageZoom {
0% {
transform: scale(1);
}
100% {
transform: scale(1.1);
}
}
@keyframes projectOverlaySlide {
0% {
transform: translateY(100%);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
.project-card {
animation: projectCardFloat 6s ease-in-out infinite;
animation-delay: calc(var(--card-index) * 0.2s);
}
.project-card:hover {
animation-play-state: paused;
}
.project-card:hover .project-image {
animation: projectImageZoom 0.6s ease-out forwards;
}
.project-card:hover .project-overlay {
animation: projectOverlaySlide 0.4s ease-out forwards;
}
.project-overlay {
transform: translateY(100%);
opacity: 0;
}
/* Category filter animations */
.category-filter.active,
.category-filter.active:hover {
background: white !important;
color: #212829 !important;
border-color: white !important;
}
/* Diagonal mask reveal for hero image */
@keyframes diagMaskReveal {
0% {
opacity: 0;
clip-path: polygon(100% 0, 100% 0, 100% 100%, 100% 100%);
transform: scale(1.03);
}
55% {
opacity: 1;
clip-path: polygon(82% 0, 100% 0, 100% 100%, 64% 100%);
}
100% {
opacity: 1;
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
}
/* Arrow pulse animation */
@keyframes arrowPulse {
0%,
100% {
transform: translateY(0);
opacity: 0.7;
}
50% {
transform: translateY(10px);
opacity: 1;
}
}
.animate-arrowPulse {
animation: arrowPulse 2s ease-in-out infinite;
}
/* Fade in animations */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Slide transition animations for image carousel */
@keyframes slideOutLeft {
0% {
transform: translateX(0) scale(1);
opacity: 1;
}
100% {
transform: translateX(-100%) scale(0.8);
opacity: 0;
}
}
@keyframes slideOutRight {
0% {
transform: translateX(0) scale(1);
opacity: 1;
}
100% {
transform: translateX(100%) scale(0.8);
opacity: 0;
}
}
@keyframes zoomInFromLeft {
0% {
transform: translateX(-100%) scale(1.2);
opacity: 0;
}
100% {
transform: translateX(0) scale(1);
opacity: 1;
}
}
@keyframes zoomInFromRight {
0% {
transform: translateX(100%) scale(1.2);
opacity: 0;
}
100% {
transform: translateX(0) scale(1);
opacity: 1;
}
}
/* Modal animations */
@keyframes modalFadeIn {
from {
opacity: 0;
transform: scale(0.9) translateY(-20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
@keyframes modalFadeOut {
from {
opacity: 1;
transform: scale(1) translateY(0);
}
to {
opacity: 0;
transform: scale(0.9) translateY(-20px);
}
}
/* Image carousel hover effects */
.group:hover #prevImage,
.group:hover #nextImage {
opacity: 1 !important;
}
/* Modal content animations */
#modalContent {
animation: modalFadeIn 0.5s ease-out;
}
/* Highlight bullet animations */
#modalHighlights li {
opacity: 0;
transform: translateX(-20px);
animation: slideInLeft 0.5s ease-out forwards;
}
#modalHighlights li:nth-child(1) {
animation-delay: 0.1s;
}
#modalHighlights li:nth-child(2) {
animation-delay: 0.2s;
}
#modalHighlights li:nth-child(3) {
animation-delay: 0.3s;
}
#modalHighlights li:nth-child(4) {
animation-delay: 0.4s;
}
#modalHighlights li:nth-child(5) {
animation-delay: 0.5s;
}
@keyframes slideInLeft {
to {
opacity: 1;
transform: translateX(0);
}
}
/* Image dot hover effects */
#imageDots button:hover {
transform: scale(1.2);
background-color: #dc2626 !important;
}
/* Request quote button hover effect */
.bg-kh3-red:hover {
box-shadow: 0 0 20px rgba(220, 38, 38, 0.4);
}
/* Image enlargement modal animations */
@keyframes enlargeModalFadeIn {
from {
opacity: 0;
transform: scale(0.8);
}
to {
opacity: 1;
transform: scale(1);
}
}
#imageEnlargeModal:not(.hidden) {
animation: enlargeModalFadeIn 0.3s ease-out;
}
/* Enlarged image hover effects */
#enlargedImage {
transition: transform 0.3s ease-out;
}
#enlargedImage:hover {
transform: scale(1.02);
}
/* Enlarged modal navigation button hover effects */
#enlargePrevImage:hover,
#enlargeNextImage:hover {
transform: translateY(-50%) scale(1.1);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
/* Close button hover effect for enlarged modal */
#closeEnlargeModal:hover {
box-shadow: 0 0 20px rgba(220, 38, 38, 0.6);
}
/* Scrollbar styling for project details */
.overflow-y-auto::-webkit-scrollbar {
width: 6px;
}
.overflow-y-auto::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
}
.overflow-y-auto::-webkit-scrollbar-thumb {
background: rgba(220, 38, 38, 0.6);
border-radius: 3px;
}
.overflow-y-auto::-webkit-scrollbar-thumb:hover {
background: rgba(220, 38, 38, 0.8);
}
/* Page transition animations */
@keyframes staggerFadeOut {
0% {
opacity: 1;
transform: translateY(0) scale(1);
}
100% {
opacity: 0;
transform: translateY(-20px) scale(0.95);
}
}
/* Hero section class for targeting */
.hero-section {
/* This class will be added to the hero section for targeting */
}
</style>
<script src="js/main.js"></script>
<script>
// Projects page functionality
let projectsData = [];
let currentCategory = "all";
let currentProject = null;
let currentImageIndex = 0;
// Page transition functionality
function handlePageTransition(e) {
e.preventDefault();
const targetUrl = e.currentTarget.href;
const transitionType =
e.currentTarget.getAttribute("data-trans") || "crossfade";
// Get all elements to animate out
const elementsToAnimate = [
document.querySelector(".hero-section"),
document.querySelector("#projectsGrid"),
document.querySelector(".category-filter"),
document.querySelector("main"),
].filter((el) => el);
// Animate elements out in order
elementsToAnimate.forEach((element, index) => {
setTimeout(() => {
if (element) {
element.style.animation =
"staggerFadeOut 0.6s ease-in-out forwards";
}
}, index * 100);
});
// Navigate after animations complete
setTimeout(() => {
window.location.href = targetUrl;
}, elementsToAnimate.length * 100 + 600);
}
// Add event listeners for page transitions
document.addEventListener("DOMContentLoaded", function () {
const transitionLinks = document.querySelectorAll("[data-trans]");
transitionLinks.forEach((link) => {
link.addEventListener("click", handlePageTransition);
});
});
// Handle back/forward navigation
window.addEventListener("pageshow", function (event) {
if (event.persisted) {
// Page was loaded from cache (back/forward)
window.location.reload();
}
});
// Auto-scroll to projects grid after 3 seconds
setTimeout(() => {
// Scroll just slightly to hint at content below (1.1 viewport heights)
const targetScroll = window.innerHeight * 1.1;
// Create a smooth, gradual scroll animation
const startScroll = window.pageYOffset;
const distance = targetScroll - startScroll;
const duration = 2000; // 2 seconds for very smooth animation
let start = null;
function animateScroll(currentTime) {
if (start === null) start = currentTime;
const timeElapsed = currentTime - start;
const progress = Math.min(timeElapsed / duration, 1);
// Easing function for smooth deceleration
const easeOutQuart = 1 - Math.pow(1 - progress, 4);
window.scrollTo(0, startScroll + distance * easeOutQuart);
if (progress < 1) {
requestAnimationFrame(animateScroll);
}
}
requestAnimationFrame(animateScroll);
}, 3000);
// Load projects data
async function loadProjects() {
try {
const response = await fetch("data/projects.json");
projectsData = await response.json();
renderProjects();
} catch (error) {
console.error("Error loading projects:", error);
}
}
// Render projects
function renderProjects() {
const grid = document.getElementById("projectsGrid");
const filteredProjects =
currentCategory === "all"
? projectsData
: projectsData.filter(
(project) => project.category === currentCategory
);
grid.innerHTML = filteredProjects
.map(
(project, index) => `
<div class="project-card group relative overflow-hidden rounded-lg bg-kh3-grey/10 border border-white/10 cursor-pointer"
style="--card-index: ${index};"
onclick="openProjectDetails('${project.id}')">
<div class="relative h-64 overflow-hidden">
<img
src="${project.images[0]}"
alt="${project.name}"
class="project-image w-full h-full object-cover transition-all duration-600"
/>
<div class="project-overlay absolute inset-0 bg-black/80 flex items-center justify-center">
<div class="text-center">
<div class="w-12 h-12 bg-kh3-red rounded-full flex items-center justify-center mx-auto mb-4">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
</svg>
</div>
<p class="text-white font-medium">VIEW PROJECT</p>
</div>
</div>
</div>
<div class="p-6">
<div class="flex items-center justify-between mb-3">
<span class="text-kh3-red text-sm font-medium tracking-wider">${project.category}</span>
<span class="text-white/60 text-sm">${project.completionDate}</span>
</div>
<h3 class="text-xl font-bold text-white mb-2 group-hover:text-kh3-red transition-colors duration-300">
${project.name}
</h3>
<p class="text-white/70 text-sm mb-4 line-clamp-2">
${project.description}
</p>
<div class="flex items-center justify-between">
<span class="text-white/50 text-sm">${project.size}</span>
<span class="text-white/50 text-sm">${project.location}</span>
</div>
</div>
</div>
`
)
.join("");
}
// Modal functionality
function openProjectDetails(projectId) {
const project = projectsData.find((p) => p.id === projectId);
if (!project) return;
currentProject = project;
currentImageIndex = 0;
// Populate modal content
document.getElementById("modalTitle").textContent = project.name;
document.getElementById("modalCategory").textContent = project.category;
document.getElementById("modalDescription").textContent =
project.description;
document.getElementById("modalClient").textContent = project.client;
document.getElementById("modalTypeOfWork").textContent =
project.typeOfWork;
document.getElementById("modalSize").textContent = project.size;
document.getElementById("modalDuration").textContent = project.duration;
document.getElementById("modalCompletionDate").textContent =
project.completionDate;
document.getElementById("modalLocation").textContent = project.location;
// Populate highlights
const highlightsList = document.getElementById("modalHighlights");
highlightsList.innerHTML = project.highlights
.map(
(highlight) => `
<li class="flex items-start space-x-2">
<div class="w-1.5 h-1.5 bg-kh3-red rounded-full mt-2 flex-shrink-0"></div>
<span class="text-white/80 text-sm">${highlight}</span>
</li>
`
)
.join("");
// Set up image carousel
setupImageCarousel(project.images);
// Show modal with animation
const modal = document.getElementById("projectModal");
const modalContent = document.getElementById("modalContent");
modal.classList.remove("hidden");
document.body.style.overflow = "hidden";
// Trigger animation
setTimeout(() => {
modalContent.classList.remove("scale-95", "opacity-0");
modalContent.classList.add("scale-100", "opacity-100");
}, 10);
}
function closeModal() {
const modal = document.getElementById("projectModal");
const modalContent = document.getElementById("modalContent");
// Trigger close animation
modalContent.classList.remove("scale-100", "opacity-100");
modalContent.classList.add("scale-95", "opacity-0");
setTimeout(() => {
modal.classList.add("hidden");
document.body.style.overflow = "auto";
}, 500);
}
function setupImageCarousel(images) {
const mainImage = document.getElementById("modalMainImage");
const imageDots = document.getElementById("imageDots");
const imageCounter = document.getElementById("imageCounter");
const totalImages = document.getElementById("totalImages");
const prevButton = document.getElementById("prevImage");
const nextButton = document.getElementById("nextImage");
// Set initial image
mainImage.src = images[0];
mainImage.alt = currentProject.name;
totalImages.textContent = images.length;
// Generate dots
imageDots.innerHTML = images
.map(
(_, index) => `
<button
class="w-2 h-2 rounded-full transition-all duration-300 ${
index === 0 ? "bg-kh3-red" : "bg-white/30"
}"
onclick="goToImage(${index})"
></button>
`
)
.join("");
// Navigation functions
window.goToImage = function (index) {
currentImageIndex = index;
updateImageCarousel();
};
window.nextImage = function () {
currentImageIndex = (currentImageIndex + 1) % images.length;
updateImageCarousel();
};
window.prevImage = function () {
currentImageIndex =
currentImageIndex === 0 ? images.length - 1 : currentImageIndex - 1;
updateImageCarousel();
};
function updateImageCarousel() {
// Update main image with fade effect
mainImage.style.opacity = "0";
setTimeout(() => {
mainImage.src = images[currentImageIndex];
mainImage.style.opacity = "1";
}, 250);
// Update counter
imageCounter.textContent = currentImageIndex + 1;
// Update dots
const dots = imageDots.querySelectorAll("button");
dots.forEach((dot, index) => {
dot.classList.toggle("bg-kh3-red", index === currentImageIndex);
dot.classList.toggle("bg-white/30", index !== currentImageIndex);
});
}
// Event listeners
prevButton.onclick = window.prevImage;
nextButton.onclick = window.nextImage;
// Keyboard navigation
document.addEventListener("keydown", function (e) {
if (
!document
.getElementById("projectModal")
.classList.contains("hidden")
) {
if (e.key === "ArrowLeft") {
window.prevImage();
} else if (e.key === "ArrowRight") {
window.nextImage();
} else if (e.key === "Escape") {
closeModal();
}
}
});
}
// Category filter functionality
document.addEventListener("DOMContentLoaded", function () {
loadProjects();
// Category filter buttons
document.querySelectorAll(".category-filter").forEach((button) => {
button.addEventListener("click", function () {
// Remove active class from all buttons
document
.querySelectorAll(".category-filter")
.forEach((btn) => btn.classList.remove("active"));
// Add active class to clicked button
this.classList.add("active");
// Update current category and re-render
currentCategory = this.dataset.category;
renderProjects();
});
});
// Close modal functionality
document
.getElementById("closeModal")
.addEventListener("click", closeModal);
// Close modal when clicking outside
document
.getElementById("projectModal")
.addEventListener("click", function (e) {
if (e.target === this) {
closeModal();
}
});
// Menu toggle functionality
const menuToggle = document.getElementById("menuToggle");
const navMenu = document.getElementById("navMenu");
const menuGrid = document.getElementById("menuGrid");
if (menuToggle && navMenu && menuGrid) {
let isMenuOpen = false;
let closeBtnEl = null;
menuToggle.addEventListener("click", () => {
if (isMenuOpen) {
// Close menu
navMenu.classList.add("hidden");
menuGrid.innerHTML = `
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
`;
if (closeBtnEl) {
closeBtnEl.remove();
closeBtnEl = null;
}
isMenuOpen = false;
} else {
// Open menu
navMenu.classList.remove("hidden");
// Keep the grid icon as is, don't transform to X
menuGrid.innerHTML = `
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
`;
// Place red X close button at the bottom of the menu panel
closeBtnEl = document.createElement("button");
closeBtnEl.setAttribute("aria-label", "Close navigation");
closeBtnEl.className =
"absolute bottom-12 left-6 text-kh3-red hover:text-white transition-colors";
closeBtnEl.innerHTML = `
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
`;
navMenu.appendChild(closeBtnEl);
closeBtnEl.addEventListener("click", () => {
navMenu.classList.add("hidden");
if (closeBtnEl) {
closeBtnEl.remove();
closeBtnEl = null;
}
isMenuOpen = false;
});
isMenuOpen = true;
}
});
// Close menu when clicking outside
document.addEventListener("click", (e) => {
if (!menuToggle.contains(e.target) && !navMenu.contains(e.target)) {
navMenu.classList.add("hidden");
menuGrid.innerHTML = `
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
<span class="w-1 h-1 bg-white rounded-full"></span>
`;
if (closeBtnEl) {
closeBtnEl.remove();
closeBtnEl = null;
}
isMenuOpen = false;
}
});
}
});
// Projects page image carousel
const projectsImgs = [
"assets/images/room.png",
"assets/images/chair.png",
"assets/images/google.png",
];
const projectsCurrentImg = document.getElementById("projectsCurrentImg");
const projectsNextImg = document.getElementById("projectsNextImg");
if (projectsCurrentImg && projectsNextImg) {
let currentIndex = 0;
let isTransitioning = false;
const slideDirections = ["left", "right"];
let slideIndex = 0;
function performProjectsSlideZoomTransition() {
if (isTransitioning) return;
isTransitioning = true;
const nextIndex = (currentIndex + 1) % projectsImgs.length;
const slideDirection =
slideDirections[slideIndex % slideDirections.length];
slideIndex++;
projectsNextImg.src = projectsImgs[nextIndex];
projectsNextImg.style.opacity = "1";
if (slideDirection === "left") {
projectsCurrentImg.style.animation =
"slideOutLeft 1s ease-in-out forwards";
projectsNextImg.style.animation =
"zoomInFromRight 1s ease-in-out forwards";
} else {
projectsCurrentImg.style.animation =
"slideOutRight 1s ease-in-out forwards";
projectsNextImg.style.animation =
"zoomInFromLeft 1s ease-in-out forwards";
}
setTimeout(() => {
projectsCurrentImg.src = projectsImgs[nextIndex];
projectsCurrentImg.style.animation = "";
projectsCurrentImg.style.transform = "";
projectsCurrentImg.style.opacity = "1";
projectsNextImg.style.animation = "";
projectsNextImg.style.transform = "";
projectsNextImg.style.opacity = "0";
currentIndex = nextIndex;
isTransitioning = false;
}, 1000);
}
setTimeout(() => {
setInterval(performProjectsSlideZoomTransition, 10000);
}, 2000);
}
// Image enlargement functionality
function enlargeImage() {
if (!currentProject) return;
const enlargedImage = document.getElementById("enlargedImage");
const enlargedImageCounter = document.getElementById(
"enlargedImageCounter"
);
const enlargedTotalImages = document.getElementById(
"enlargedTotalImages"
);
const enlargePrevButton = document.getElementById("enlargePrevImage");
const enlargeNextButton = document.getElementById("enlargeNextImage");
const closeEnlargeModal = document.getElementById("closeEnlargeModal");
// Set initial image and counter
enlargedImage.src = currentProject.images[currentImageIndex];
enlargedImage.alt = currentProject.name;
enlargedImageCounter.textContent = currentImageIndex + 1;
enlargedTotalImages.textContent = currentProject.images.length;
// Navigation functions for enlarged image
window.goToEnlargedImage = function (index) {
currentImageIndex = index;
enlargedImageCounter.textContent = index + 1;
enlargedImage.src = currentProject.images[index];
// Update main modal image and dots
const modalMainImage = document.getElementById("modalMainImage");
const imageDots = document.getElementById("imageDots");
modalMainImage.src = currentProject.images[index];
modalMainImage.alt = currentProject.name;
// Update dots in main modal
const dots = imageDots.querySelectorAll("button");
dots.forEach((dot, dotIndex) => {
dot.classList.toggle("bg-kh3-red", dotIndex === index);
dot.classList.toggle("bg-white/30", dotIndex !== index);
});
// Update image counter in main modal
document.getElementById("imageCounter").textContent = index + 1;
};
window.enlargePrevImage = function () {
const newIndex =
currentImageIndex === 0
? currentProject.images.length - 1
: currentImageIndex - 1;
goToEnlargedImage(newIndex);
};
window.enlargeNextImage = function () {
const newIndex =
currentImageIndex === currentProject.images.length - 1
? 0
: currentImageIndex + 1;
goToEnlargedImage(newIndex);
};
window.closeEnlargeModal = function () {
document.getElementById("imageEnlargeModal").classList.add("hidden");
document.body.style.overflow = "auto";
};
// Event listeners for enlarged image
enlargePrevButton.onclick = window.enlargePrevImage;
enlargeNextButton.onclick = window.enlargeNextImage;
closeEnlargeModal.onclick = window.closeEnlargeModal;
// Close enlarged modal when clicking outside
document
.getElementById("imageEnlargeModal")
.addEventListener("click", function (e) {
if (e.target === this) {
window.closeEnlargeModal();
}
});
// Keyboard navigation for enlarged image
document.addEventListener("keydown", function (e) {
if (
!document
.getElementById("imageEnlargeModal")
.classList.contains("hidden")
) {
if (e.key === "ArrowLeft") {
window.enlargePrevImage();
} else if (e.key === "ArrowRight") {
window.enlargeNextImage();
} else if (e.key === "Escape") {
window.closeEnlargeModal();
}
}
});
document.getElementById("imageEnlargeModal").classList.remove("hidden");
document.body.style.overflow = "hidden";
}
</script>
</body>
</html>