Compare commits

...

6 commits

Author SHA1 Message Date
8c42edd511 refactor: standardize sizes array formatting in products.json for improved consistency and readability
All checks were successful
continuous-integration/drone/push Build is passing
2025-09-17 20:59:50 -07:00
a648abe2d0 refactor: streamline sizes array formatting in products.json for consistency and readability 2025-09-17 20:52:22 -07:00
18cf5c8ed3 Implement image enlargement modal and enhance product detail functionality
- Replaced static quantity controls with a modal for image enlargement on product detail and catalog pages.
- Added event listeners for image clicks to trigger modal display with enlarged images.
- Updated product detail page to dynamically load images and descriptions, improving user experience.
- Refactored JavaScript to streamline image handling and modal interactions.
- Enhanced CSS for modal styling and transitions, ensuring a smooth user experience.
2025-09-17 20:52:03 -07:00
6caa3ee6c4 Fix galleryPairs for all 88 products to ensure at least 2 images
- Updated products 1-88 to have minimum 2 images in galleryPairs array
- Fixed empty galleryPairs by copying first 2 images from images array
- Fixed single-image galleryPairs by adding first image from images array
- Ensures carousel functionality works properly for all products
- Special case: Product 84 (Sphere) only had 1 image, copied it twice
2025-09-17 19:56:17 -07:00
d84c6196d1 refactor: update products.json to enhance product galleries with new image pairs for various training tables and screens 2025-09-17 19:43:10 -07:00
7295bcba7e refactor: enhance product detail page with dynamic content loading and improved gallery functionality
- Updated product-detail.html to replace static content with dynamic placeholders for images, title, description, sizes, and colors.
- Modified main.js to implement dynamic loading of product details, including title, description, images, sizes, and colors.
- Improved gallery functionality to support a carousel view for product images, including navigation buttons.
- Adjusted CSS to add new transition durations and easing functions for smoother animations.
2025-09-17 19:40:13 -07:00
7 changed files with 1620 additions and 1566 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 KiB

File diff suppressed because it is too large Load diff

View file

@ -164,7 +164,7 @@
<!-- Background Image --> <!-- Background Image -->
<div class="absolute inset-0 w-full h-full"> <div class="absolute inset-0 w-full h-full">
<img <img
src="assets/images/potty.jpg" src="assets/images/prod-catalog.jpg"
alt="Product catalog background" alt="Product catalog background"
class="w-full h-full object-cover object-center" class="w-full h-full object-cover object-center"
style="filter: blur(3px)" style="filter: blur(3px)"

View file

@ -194,71 +194,33 @@
<div <div
class="flex flex-row md:flex-col gap-3 md:gap-4 w-full md:w-32 overflow-x-auto md:overflow-visible" class="flex flex-row md:flex-col gap-3 md:gap-4 w-full md:w-32 overflow-x-auto md:overflow-visible"
> >
<div <!-- Thumbnail images will be populated dynamically -->
class="w-32 h-32 bg-floral-white rounded-lg overflow-hidden cursor-pointer"
>
<img
src="assets/images/outdoor_sofa_set.png"
alt="Outdoor sofa set"
class="w-full h-full object-cover"
/>
</div>
<div
class="w-32 h-32 bg-floral-white rounded-lg overflow-hidden cursor-pointer"
>
<img
src="assets/images/outdoor_sofa_set.png"
alt="Outdoor sofa set 2"
class="w-full h-full object-cover"
/>
</div>
<div
class="w-32 h-32 bg-floral-white rounded-lg overflow-hidden cursor-pointer"
>
<img
src="assets/images/outdoor_sofa_set.png"
alt="Stuart sofa"
class="w-full h-full object-cover"
/>
</div>
<div
class="w-32 h-32 bg-floral-white rounded-lg overflow-hidden cursor-pointer"
>
<img
src="assets/images/outdoor_sofa_set.png"
alt="Maya sofa three seater"
class="w-full h-full object-cover"
/>
</div>
</div> </div>
<!-- Main Product Image --> <!-- Main Product Image -->
<div <div
class="w-full h-80 md:w-[500px] md:h-[500px] bg-floral-white rounded-lg overflow-hidden" class="w-full h-80 md:w-[500px] md:h-[500px] bg-floral-white rounded-lg overflow-hidden"
> >
<img <!-- Main product image will be populated dynamically -->
src="assets/images/asgaard_sofa.png"
alt="Asgaard sofa"
class="w-full h-full object-cover"
/>
</div> </div>
</div> </div>
<!-- Right Section - Product Details --> <!-- Right Section - Product Details -->
<div class="w-full md:w-[500px]"> <div class="w-full md:w-[500px]">
<!-- Product Title --> <!-- Product Title -->
<h1 class="font-playfair font-normal text-4xl text-black mb-8"> <h1
Asgaard sofa class="font-playfair font-normal text-4xl text-black mb-8"
id="product-title"
>
<!-- Product title will be populated dynamically -->
</h1> </h1>
<!-- Product Description --> <!-- Product Description -->
<p <p
class="font-playfair font-normal text-sm text-black mb-12 max-w-md" class="font-playfair font-normal text-sm text-black mb-12 max-w-md"
id="product-description"
> >
Setting the bar as one of the loudest speakers in its class, the <!-- Product description will be populated dynamically -->
Kilburn is a compact, stout-hearted hero with a well-balanced
audio which boasts a clear midrange and extended highs for a
sound.
</p> </p>
<!-- Size Options --> <!-- Size Options -->
@ -269,36 +231,7 @@
Size Size
</h3> </h3>
<div class="grid grid-cols-6 gap-2" id="size-buttons-container"> <div class="grid grid-cols-6 gap-2" id="size-buttons-container">
<button <!-- Size buttons will be populated dynamically -->
class="w-8 h-8 bg-uc-gold text-white font-playfair font-normal text-sm rounded flex items-center justify-center size-button"
>
L
</button>
<button
class="w-8 h-8 bg-floral-white text-black font-playfair font-normal text-sm rounded flex items-center justify-center size-button"
>
XL
</button>
<button
class="w-8 h-8 bg-floral-white text-black font-playfair font-normal text-sm rounded flex items-center justify-center size-button"
>
XS
</button>
<button
class="w-8 h-8 bg-floral-white text-black font-playfair font-normal text-sm rounded flex items-center justify-center size-button"
>
S
</button>
<button
class="w-8 h-8 bg-floral-white text-black font-playfair font-normal text-sm rounded flex items-center justify-center size-button"
>
M
</button>
<button
class="w-8 h-8 bg-floral-white text-black font-playfair font-normal text-sm rounded flex items-center justify-center size-button"
>
2XL
</button>
</div> </div>
</div> </div>
@ -309,42 +242,13 @@
> >
Color Color
</h3> </h3>
<div class="flex gap-4"> <div class="flex gap-4" id="color-buttons-container">
<button <!-- Color buttons will be populated dynamically -->
class="w-8 h-8 bg-purple-500 rounded-full border-2 border-black"
></button>
<button class="w-8 h-8 bg-black rounded-full"></button>
<button class="w-8 h-8 bg-uc-gold rounded-full"></button>
</div> </div>
</div> </div>
<!-- Quantity and Action Buttons --> <!-- Quantity and Action Buttons -->
<div class="flex flex-col md:flex-row gap-4 md:gap-6 mb-8"> <div class="flex flex-col md:flex-row gap-4 md:gap-6 mb-8">
<!-- Quantity Selector -->
<div
class="inline-flex items-center justify-between w-[180px] h-[64px] min-h-[64px] bg-white border border-quick-silver rounded-[15px] px-4 box-border shadow-sm hover:shadow-md transition-all duration-200"
>
<button
id="qty-decr"
aria-label="Decrease quantity"
class="font-playfair font-light text-[20px] leading-none text-black w-8 h-8 flex items-center justify-center rounded-lg hover:bg-light-bg transition-colors cursor-pointer"
>
-
</button>
<span
id="qty-value"
class="font-playfair font-light text-[20px] leading-none text-black"
>1</span
>
<button
id="qty-incr"
aria-label="Increase quantity"
class="font-playfair font-light text-[20px] leading-none text-black w-8 h-8 flex items-center justify-center rounded-lg hover:bg-light-bg transition-colors cursor-pointer"
>
+
</button>
</div>
<!-- Action Buttons --> <!-- Action Buttons -->
<a <a
href="contact.html" href="contact.html"
@ -365,62 +269,7 @@
<!-- Product Metadata --> <!-- Product Metadata -->
<div class="space-y-4"> <div class="space-y-4">
<div class="flex items-center"> <!-- Product metadata will be populated dynamically -->
<span
class="font-playfair font-normal text-base text-quick-silver w-20"
>Model No.</span
>
<span
class="font-playfair font-medium text-base text-quick-silver mx-2"
>:</span
>
<span
class="font-playfair font-normal text-base text-quick-silver"
>SS001</span
>
</div>
<div class="flex items-center">
<span
class="font-playfair font-normal text-base text-quick-silver w-20"
>Category</span
>
<span
class="font-playfair font-medium text-base text-quick-silver mx-2"
>:</span
>
<span
class="font-playfair font-normal text-base text-quick-silver"
>Sofas</span
>
</div>
<div class="flex items-center">
<span
class="font-playfair font-normal text-base text-quick-silver w-20"
>Tags</span
>
<span
class="font-playfair font-medium text-base text-quick-silver mx-2"
>:</span
>
<span
class="font-playfair font-normal text-base text-quick-silver"
>Sofa, Chair, Home, Shop</span
>
</div>
<div class="flex items-center">
<span
class="font-playfair font-normal text-base text-quick-silver w-20"
>Dimension</span
>
<span
class="font-playfair font-medium text-base text-quick-silver mx-2"
>:</span
>
<span
class="font-playfair font-normal text-base text-quick-silver"
>180cm x 85cm x 75cm</span
>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -439,45 +288,61 @@
</div> </div>
<!-- Copy --> <!-- Copy -->
<div class="max-w-5xl mx-auto space-y-6"> <div
<p class="max-w-5xl mx-auto space-y-6"
class="font-playfair text-base leading-6 text-[#333333] text-justify" id="product-description-content"
> >
Embodying the raw, wayward spirit of rock ‘n’ roll, the Kilburn <!-- Product description content will be populated dynamically -->
portable active stereo speaker takes the unmistakable look and
sound of Marshall, unplugs the chords, and takes the show on the
road.
</p>
<p
class="font-playfair text-base leading-6 text-[#333333] text-justify"
>
Weighing in under 7 pounds, the Kilburn is a lightweight piece of
vintage styled engineering. Setting the bar as one of the loudest
speakers in its class, the Kilburn is a compact, stout-hearted
hero with a well-balanced audio which boasts a clear midrange and
extended highs for a sound that is both articulate and pronounced.
The analogue knobs allow you to fine tune the controls to your
personal preferences while the guitar-influenced leather strap
enables easy and stylish travel.
</p>
</div> </div>
<!-- Images --> <!-- Images -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 mt-10"> <div class="relative mt-10">
<div class="rounded-[10px] bg-floral-white p-6"> <div
<img class="grid grid-cols-1 md:grid-cols-2 gap-8"
src="assets/images/asgaard_sofa.png" id="product-gallery-images"
alt="Sofa variant left" >
class="w-full h-80 object-contain mx-auto" <!-- Product gallery images will be populated dynamically -->
/>
</div>
<div class="rounded-[10px] bg-floral-white p-6">
<img
src="assets/images/asgaard_sofa.png"
alt="Sofa variant right"
class="w-full h-80 object-contain mx-auto"
/>
</div> </div>
<!-- Back Arrow -->
<button
id="gallery-back-btn"
class="absolute top-1/2 left-0 transform -translate-y-1/2 bg-white border border-gray-300 rounded-full p-3 shadow-lg hover:shadow-xl transition-all duration-200 opacity-0 pointer-events-none"
style="display: none"
>
<svg
class="w-6 h-6 text-gray-600"
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>
<!-- Forward Arrow -->
<button
id="gallery-next-btn"
class="absolute top-1/2 right-0 transform -translate-y-1/2 bg-white border border-gray-300 rounded-full p-3 shadow-lg hover:shadow-xl transition-all duration-200 opacity-0 pointer-events-none"
style="display: none"
>
<svg
class="w-6 h-6 text-gray-600"
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>
</section> </section>
@ -648,6 +513,45 @@
</div> </div>
</footer> </footer>
<!-- Image Enlargement Modal -->
<div
id="image-modal"
class="fixed inset-0 bg-black bg-opacity-90 z-50 hidden flex items-center justify-center p-4 transition-opacity duration-300"
>
<div
class="relative max-w-7xl max-h-full w-full h-full flex items-center justify-center"
>
<!-- Close Button -->
<button
id="modal-close-btn"
class="absolute top-4 right-4 z-10 bg-white bg-opacity-20 hover:bg-opacity-30 text-white rounded-full p-3 transition-all duration-200 backdrop-blur-sm"
aria-label="Close modal"
>
<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>
<!-- Enlarged Image -->
<img
id="modal-image"
src=""
alt=""
class="w-[95vw] h-[95vh] object-contain rounded-lg shadow-2xl transform transition-transform duration-300 scale-95"
/>
</div>
</div>
<script src="scripts/main.js?v=3.5"></script> <script src="scripts/main.js?v=3.5"></script>
</body> </body>
</html> </html>

View file

@ -55,27 +55,6 @@ function initSite() {
window.addEventListener("scroll", updateActiveLink); window.addEventListener("scroll", updateActiveLink);
updateActiveLink(); updateActiveLink();
// Quantity controls on product detail page
(function initQuantityControls() {
const decr = document.getElementById("qty-decr");
const incr = document.getElementById("qty-incr");
const valueEl = document.getElementById("qty-value");
if (!decr || !incr || !valueEl) return; // Not on product detail page
function parseQty() {
const n = parseInt(valueEl.textContent || "1", 10);
return Number.isFinite(n) && n > 0 ? n : 1;
}
decr.addEventListener("click", () => {
const next = Math.max(1, parseQty() - 1);
valueEl.textContent = String(next);
});
incr.addEventListener("click", () => {
const next = parseQty() + 1;
valueEl.textContent = String(next);
});
})();
// Related products (dynamic) // Related products (dynamic)
(async function initRelated() { (async function initRelated() {
const grid = document.getElementById("related-grid"); const grid = document.getElementById("related-grid");
@ -222,342 +201,530 @@ function initSite() {
}); });
}); });
} }
}
// Product detail page functionality // Image enlargement modal functionality (global)
(async function initProductDetail() { function addImageEnlargementListeners() {
console.log("Product detail script running..."); // Target both main product image AND gallery carousel images
const mainProductImage = document.querySelector(
".w-full.h-80.md\\:w-\\[500px\\].md\\:h-\\[500px\\] img[data-enlarge-src]"
);
const galleryImages = document.querySelectorAll(
"#product-gallery-images img[data-enlarge-src]"
);
const modal = document.getElementById("image-modal");
const modalImage = document.getElementById("modal-image");
const modalCloseBtn = document.getElementById("modal-close-btn");
// Check if we're on the product detail page console.log("Adding image enlargement listeners...");
const productTitle = document.querySelector("h1"); console.log("Found main product image:", !!mainProductImage);
if (!productTitle) { console.log("Found gallery images:", galleryImages.length);
console.log("No h1 found, not on product detail page"); console.log("Modal elements:", {
modal: !!modal,
modalImage: !!modalImage,
modalCloseBtn: !!modalCloseBtn,
});
if (!modal || !modalImage || !modalCloseBtn) {
console.log("Modal elements not found, skipping image enlargement setup");
return;
}
// Add click listener to main product image
if (mainProductImage) {
console.log(
`Adding click listener to main product image:`,
mainProductImage.src
);
mainProductImage.removeEventListener("click", handleImageClick);
mainProductImage.addEventListener("click", handleImageClick);
} else {
console.log("Main product image not found");
}
// Add click listeners to gallery carousel images
galleryImages.forEach((img, index) => {
console.log(
`Adding click listener to gallery image ${index + 1}:`,
img.src
);
img.removeEventListener("click", handleImageClick);
img.addEventListener("click", handleImageClick);
});
function handleImageClick(e) {
console.log("Image clicked!", this.src);
const imageSrc = this.getAttribute("data-enlarge-src");
const imageAlt = this.getAttribute("alt");
console.log("Opening modal with image:", imageSrc);
modalImage.src = imageSrc;
modalImage.alt = imageAlt;
// Show modal with animation
modal.classList.remove("hidden");
document.body.style.overflow = "hidden"; // Prevent background scrolling
// Trigger animation after a brief delay
setTimeout(() => {
modalImage.classList.remove("scale-95");
modalImage.classList.add("scale-100");
}, 10);
}
// Close modal functionality
function closeModal() {
// Animate out
modalImage.classList.remove("scale-100");
modalImage.classList.add("scale-95");
// Hide modal after animation
setTimeout(() => {
modal.classList.add("hidden");
document.body.style.overflow = ""; // Restore scrolling
}, 300);
}
modalCloseBtn.addEventListener("click", closeModal);
// Close modal when clicking outside the image
modal.addEventListener("click", function (e) {
if (e.target === modal) {
closeModal();
}
});
// Close modal with Escape key
document.addEventListener("keydown", function (e) {
if (e.key === "Escape" && !modal.classList.contains("hidden")) {
closeModal();
}
});
}
// Thumbnail click functionality (global)
function addThumbnailClickListeners() {
const thumbnails = document.querySelectorAll("[data-thumbnail-src]");
const mainImageContainer = document.querySelector(
".w-full.h-80.md\\:w-\\[500px\\].md\\:h-\\[500px\\]"
);
console.log("Adding thumbnail click listeners...");
console.log("Found thumbnails:", thumbnails.length);
console.log("Main image container:", !!mainImageContainer);
if (!mainImageContainer) {
console.log("Main image container not found");
return;
}
thumbnails.forEach((thumbnail, index) => {
thumbnail.addEventListener("click", function () {
const imageSrc = this.getAttribute("data-thumbnail-src");
console.log(`Thumbnail ${index + 1} clicked, switching to:`, imageSrc);
// Update the main product image
const mainImg = mainImageContainer.querySelector("img");
if (mainImg) {
mainImg.src = imageSrc;
mainImg.setAttribute("data-enlarge-src", imageSrc);
console.log("Main image updated to:", imageSrc);
}
});
});
}
// Product detail page functionality
async function initProductDetail() {
console.log("Product detail script running...");
console.log("Current URL:", window.location.href);
// Check if we're on the product detail page
const productTitle = document.querySelector("h1");
if (!productTitle) {
console.log("No h1 found, not on product detail page");
return;
}
console.log("Found h1 element:", productTitle);
// Get product ID from URL
const urlParams = new URLSearchParams(window.location.search);
const productId = parseInt(urlParams.get("id"));
console.log("Product ID from URL:", productId);
if (!productId) {
console.log("No product ID found in URL");
return;
}
try {
// Load product data
const response = await fetch("data/products.json");
const data = await response.json();
const product = data.products.find((p) => p.id === productId);
if (!product) {
console.error("Product not found:", productId);
return; return;
} }
// Get product ID from URL console.log("Loading product:", product);
const urlParams = new URLSearchParams(window.location.search);
const productId = parseInt(urlParams.get("id"));
console.log("Product ID from URL:", productId);
if (!productId) { // Update page title
console.log("No product ID found in URL"); document.title = `${product.name} - KHY`;
return;
// Update the "Product Details" title with the actual product name
const productDetailsTitle = document.getElementById(
"product-details-title"
);
if (productDetailsTitle) {
productDetailsTitle.textContent = product.name;
} }
try { // Update product title (the main product title, not the breadcrumb)
// Load product data const productTitle = document.getElementById("product-title");
const response = await fetch("data/products.json"); console.log("Product title element:", productTitle);
const data = await response.json(); if (productTitle) {
const product = data.products.find((p) => p.id === productId); productTitle.textContent = product.name;
console.log("Updated product title to:", product.name);
}
if (!product) { // Update product description
console.error("Product not found:", productId); const productDescription = document.getElementById("product-description");
console.log("Product description element:", productDescription);
if (productDescription) {
productDescription.textContent = product.description;
console.log("Updated product description to:", product.description);
}
// Update main product image
const mainImageContainer = document.querySelector(
".w-full.h-80.md\\:w-\\[500px\\].md\\:h-\\[500px\\]"
);
console.log("Main image container:", mainImageContainer);
if (mainImageContainer) {
mainImageContainer.innerHTML = `
<img
src="${product.image}"
alt="${product.alt || product.name}"
class="w-full h-full object-cover cursor-pointer hover:opacity-90 transition-opacity"
data-enlarge-src="${product.image}"
/>
`;
console.log("Updated main image to:", product.image);
// Add image enlargement functionality to the main product image
setTimeout(() => {
addImageEnlargementListeners();
}, 100);
}
// Update thumbnail images
const thumbnailContainer = document.querySelector(
".flex.flex-row.md\\:flex-col.gap-3.md\\:gap-4"
);
console.log("Thumbnail container:", thumbnailContainer);
if (thumbnailContainer && product.images) {
thumbnailContainer.innerHTML = product.images
.slice(0, 4)
.map(
(img, index) => `
<div class="w-32 h-32 bg-floral-white rounded-lg overflow-hidden cursor-pointer hover:opacity-80 transition-opacity" data-thumbnail-src="${img}">
<img
src="${img}"
alt="${product.name} ${index + 1}"
class="w-full h-full object-cover"
/>
</div>
`
)
.join("");
console.log("Updated thumbnails with", product.images.length, "images");
// Add click event listeners to thumbnails
addThumbnailClickListeners();
}
// Update size options
const sizeContainer = document.getElementById("size-buttons-container");
console.log("Size container:", sizeContainer);
if (sizeContainer && product.sizes) {
sizeContainer.innerHTML = product.sizes
.map(
(size, index) => `
<button
class="w-8 h-8 ${
index === 0
? "bg-uc-gold text-white"
: "bg-floral-white text-black"
} font-playfair font-normal text-sm rounded flex items-center justify-center size-button"
>
${size}
</button>
`
)
.join("");
console.log("Updated size options with", product.sizes.length, "sizes");
}
// Update color options
const colorContainer = document.getElementById("color-buttons-container");
console.log("Color container:", colorContainer);
if (colorContainer && product.colors) {
colorContainer.innerHTML = product.colors
.map(
(color, index) => `
<button
class="w-8 h-8 rounded-full ${
index === 0 ? "border-2 border-black" : ""
}"
style="background-color: ${color}"
></button>
`
)
.join("");
console.log(
"Updated color options with",
product.colors.length,
"colors"
);
}
// Update product metadata - find by text content
const allSpans = document.querySelectorAll("span");
// Find and update Model No.
const modelNoLabel = Array.from(allSpans).find(
(span) => span.textContent === "Model No."
);
if (modelNoLabel && product.modelNo) {
const modelNoValue =
modelNoLabel.parentElement.querySelector("span:last-child");
if (modelNoValue) {
modelNoValue.textContent = product.modelNo;
}
}
// Find and update Category
const categoryLabel = Array.from(allSpans).find(
(span) => span.textContent === "Category"
);
if (categoryLabel && product.category) {
const categoryValue =
categoryLabel.parentElement.querySelector("span:last-child");
if (categoryValue) {
categoryValue.textContent =
product.category.charAt(0).toUpperCase() + product.category.slice(1);
}
}
// Find and update Dimensions
const dimensionsLabel = Array.from(allSpans).find(
(span) => span.textContent === "Dimension"
);
console.log("Dimensions label found:", dimensionsLabel);
console.log("Product dimensions:", product.dimensions);
if (dimensionsLabel && product.dimensions) {
const dimensionsValue =
dimensionsLabel.parentElement.querySelector("span:last-child");
console.log("Dimensions value element:", dimensionsValue);
if (dimensionsValue) {
dimensionsValue.textContent = product.dimensions;
console.log("Updated dimensions to:", product.dimensions);
}
}
// Update description content
const descriptionContainer = document.getElementById(
"product-description-content"
);
console.log("Description content container:", descriptionContainer);
if (descriptionContainer && product.descriptionLong) {
descriptionContainer.innerHTML = product.descriptionLong
.map(
(paragraph) => `
<p class="font-playfair text-base leading-6 text-[#333333] text-justify">
${paragraph}
</p>
`
)
.join("");
console.log(
"Updated description content with",
product.descriptionLong.length,
"paragraphs"
);
}
// Update gallery images with carousel
const galleryContainer = document.getElementById("product-gallery-images");
const galleryBackBtn = document.getElementById("gallery-back-btn");
const galleryNextBtn = document.getElementById("gallery-next-btn");
console.log("Gallery container:", galleryContainer);
console.log("Product galleryPairs:", product.galleryPairs);
if (galleryContainer && product.galleryPairs) {
// galleryPairs is now a simple array of image paths
const allGalleryImages = product.galleryPairs;
console.log("All gallery images:", allGalleryImages);
// Store gallery images for carousel
window.currentGalleryImages = allGalleryImages;
window.currentGalleryIndex = 0;
console.log("About to call renderGalleryCarousel...");
// Render initial gallery view
renderGalleryCarousel();
// Show navigation arrows for looped carousel (always show if more than 2 images)
if (allGalleryImages.length > 2) {
galleryBackBtn.style.display = "block";
galleryNextBtn.style.display = "block";
} else {
galleryBackBtn.style.display = "none";
galleryNextBtn.style.display = "none";
}
console.log(
"Gallery carousel:",
allGalleryImages.length,
"gallery images total (sliding)"
);
}
function renderGalleryCarousel() {
console.log("renderGalleryCarousel called");
console.log("window.currentGalleryImages:", window.currentGalleryImages);
console.log("galleryContainer:", galleryContainer);
if (!window.currentGalleryImages || !galleryContainer) {
console.log(
"Early return: missing currentGalleryImages or galleryContainer"
);
return; return;
} }
console.log("Loading product:", product); console.log(
"Rendering gallery carousel with",
// Update page title window.currentGalleryImages.length,
document.title = `${product.name} - KHY`; "images"
// Update the "Product Details" title with the actual product name
const productDetailsTitle = document.getElementById(
"product-details-title"
); );
if (productDetailsTitle) {
productDetailsTitle.textContent = product.name; // Handle single image vs multiple images
if (window.currentGalleryImages.length === 1) {
// Single image - show only one image, hide arrows
const image1 = window.currentGalleryImages[0];
console.log("Rendering single image:", image1);
galleryContainer.innerHTML = `
<div class="rounded-[10px] bg-floral-white p-6 transition-all duration-1500 ease-out cursor-pointer hover:opacity-90" id="gallery-image-1">
<img
src="${image1}"
alt="${product.name} gallery"
class="w-full h-80 object-contain mx-auto"
data-enlarge-src="${image1}"
/>
</div>
`;
// Hide arrows for single image
galleryBackBtn.style.display = "none";
galleryNextBtn.style.display = "none";
} else {
// Multiple images - show 2 images with carousel
const image1 = window.currentGalleryImages[window.currentGalleryIndex];
const image2Index =
(window.currentGalleryIndex + 1) % window.currentGalleryImages.length;
const image2 = window.currentGalleryImages[image2Index];
console.log("Rendering multiple images:", image1, image2);
galleryContainer.innerHTML = `
<div class="rounded-[10px] bg-floral-white p-6 transition-all duration-1500 ease-out cursor-pointer hover:opacity-90" id="gallery-image-1">
<img
src="${image1}"
alt="${product.name} gallery"
class="w-full h-80 object-contain mx-auto"
data-enlarge-src="${image1}"
/>
</div>
<div class="rounded-[10px] bg-floral-white p-6 transition-all duration-1500 ease-out cursor-pointer hover:opacity-90" id="gallery-image-2">
<img
src="${image2}"
alt="${product.name} gallery"
class="w-full h-80 object-contain mx-auto"
data-enlarge-src="${image2}"
/>
</div>
`;
// Show arrows for multiple images
galleryBackBtn.style.display = "block";
galleryBackBtn.style.opacity = "1";
galleryBackBtn.style.pointerEvents = "auto";
galleryNextBtn.style.display = "block";
galleryNextBtn.style.opacity = "1";
galleryNextBtn.style.pointerEvents = "auto";
} }
// Update product title (the main product title, not the breadcrumb) // Add click event listeners to gallery images for enlargement
const allH1s = document.querySelectorAll("h1"); console.log("About to call addImageEnlargementListeners for gallery...");
console.log("All H1 elements:", allH1s); addImageEnlargementListeners();
if (allH1s.length > 1) { }
const mainProductTitle = allH1s[1]; // The second h1 should be the product title
console.log("Main product title element:", mainProductTitle);
if (mainProductTitle) {
mainProductTitle.textContent = product.name;
}
}
// Update product description - find by text content // Add click handlers for looped gallery carousel navigation
const allParagraphs = document.querySelectorAll("p"); if (galleryBackBtn) {
console.log("All paragraphs:", allParagraphs); galleryBackBtn.addEventListener("click", function () {
const descriptionEl = Array.from(allParagraphs).find( // Left arrow moves forward: if at last position, go to 0
(p) => window.currentGalleryIndex =
p.textContent && (window.currentGalleryIndex + 1) % window.currentGalleryImages.length;
p.textContent.includes( renderGalleryCarousel();
"Setting the bar as one of the loudest speakers" });
) }
if (galleryNextBtn) {
galleryNextBtn.addEventListener("click", function () {
// Right arrow moves backward: if at 0, go to last position
window.currentGalleryIndex =
window.currentGalleryIndex === 0
? window.currentGalleryImages.length - 1
: window.currentGalleryIndex - 1;
renderGalleryCarousel();
});
}
// Update related products section
const relatedGrid = document.getElementById("related-grid");
if (relatedGrid && product.category) {
console.log("Current product category:", product.category);
console.log(
"All products:",
data.products.map((p) => ({
id: p.id,
name: p.name,
category: p.category,
}))
); );
console.log("Description element:", descriptionEl);
if (descriptionEl) {
descriptionEl.textContent = product.description;
}
// Update main product image - find by alt text // Filter related products by category (excluding current product)
const allImages = document.querySelectorAll("img"); const allRelatedProducts = data.products.filter(
console.log("All images:", allImages); (p) => p.category === product.category && p.id !== product.id
const mainImage = Array.from(allImages).find(
(img) => img.alt && img.alt.includes("Asgaard sofa")
); );
console.log("Main image element:", mainImage);
if (mainImage) {
mainImage.src = product.image;
mainImage.alt = product.alt || product.name;
}
// Update thumbnail images - find by alt text console.log("All related products found:", allRelatedProducts);
if (product.images && product.images.length > 0) {
const allImages = document.querySelectorAll("img");
const thumbnails = Array.from(allImages).filter(
(img) =>
img.alt &&
(img.alt.includes("Outdoor sofa set") ||
img.alt.includes("Stuart sofa") ||
img.alt.includes("Maya sofa"))
);
console.log("Thumbnail images found:", thumbnails);
product.images.forEach((imageSrc, index) => {
if (thumbnails[index]) {
thumbnails[index].src = imageSrc;
thumbnails[index].alt = product.alt || product.name;
}
});
}
// Update size options - use the specific size-button class let currentPage = 1;
if (product.sizes) { const pageSize = 4;
const sizeButtons = document.querySelectorAll(".size-button");
console.log("Size buttons found:", sizeButtons);
// Hide all size buttons first function renderRelatedProducts() {
sizeButtons.forEach((button) => { const start = 0;
button.style.display = "none"; const end = currentPage * pageSize;
}); const productsToShow = allRelatedProducts.slice(start, end);
// Show and update only the buttons we need relatedGrid.innerHTML = productsToShow
product.sizes.forEach((size, index) => { .map(
if (sizeButtons[index]) { (p) => `
sizeButtons[index].style.display = "flex";
sizeButtons[index].textContent = size;
sizeButtons[index].setAttribute("data-size", size);
// Set selected size
if (size === product.selectedSize) {
sizeButtons[index].classList.remove(
"bg-floral-white",
"text-black"
);
sizeButtons[index].classList.add("bg-uc-gold", "text-white");
sizeButtons[index].classList.add("selected");
} else {
sizeButtons[index].classList.remove(
"bg-uc-gold",
"text-white",
"selected"
);
sizeButtons[index].classList.add("bg-floral-white", "text-black");
}
}
});
// Add click event listeners for size buttons
sizeButtons.forEach((button) => {
button.addEventListener("click", function () {
// Remove selected state from all size buttons
sizeButtons.forEach((btn) => {
btn.classList.remove("bg-uc-gold", "text-white", "selected");
btn.classList.add("bg-floral-white", "text-black");
});
// Add selected state to clicked button
this.classList.remove("bg-floral-white", "text-black");
this.classList.add("bg-uc-gold", "text-white", "selected");
});
});
}
// Update color options - find only the rounded color buttons, not size buttons
if (product.colors) {
const allButtons = document.querySelectorAll("button");
const colorButtons = Array.from(allButtons).filter(
(button) =>
button.classList.contains("w-8") &&
button.classList.contains("h-8") &&
button.classList.contains("rounded-full") &&
!button.textContent // Color buttons should not have text content
);
console.log("Color buttons found:", colorButtons);
product.colors.forEach((color, index) => {
if (colorButtons[index]) {
colorButtons[index].style.backgroundColor = color.value;
colorButtons[index].setAttribute(
"data-color",
color.name || color.value
);
// Set selected color
if (color.selected) {
colorButtons[index].classList.add(
"border-2",
"border-black",
"selected"
);
} else {
colorButtons[index].classList.remove(
"border-2",
"border-black",
"selected"
);
}
}
});
// Add click event listeners for color buttons
colorButtons.forEach((button) => {
button.addEventListener("click", function () {
// Remove selected state from all color buttons
colorButtons.forEach((btn) => {
btn.classList.remove("border-2", "border-black", "selected");
});
// Add selected state to clicked button
this.classList.add("border-2", "border-black", "selected");
});
});
}
// Update product metadata - find by text content
const allSpans = document.querySelectorAll("span");
// Find and update Model No.
const modelNoLabel = Array.from(allSpans).find(
(span) => span.textContent === "Model No."
);
if (modelNoLabel && product.modelNo) {
const modelNoValue =
modelNoLabel.parentElement.querySelector("span:last-child");
if (modelNoValue) {
modelNoValue.textContent = product.modelNo;
}
}
// Find and update Category
const categoryLabel = Array.from(allSpans).find(
(span) => span.textContent === "Category"
);
if (categoryLabel && product.category) {
const categoryValue =
categoryLabel.parentElement.querySelector("span:last-child");
if (categoryValue) {
categoryValue.textContent =
product.category.charAt(0).toUpperCase() +
product.category.slice(1);
}
}
// Find and update Tags
const tagsLabel = Array.from(allSpans).find(
(span) => span.textContent === "Tags"
);
if (tagsLabel && product.tags) {
const tagsValue =
tagsLabel.parentElement.querySelector("span:last-child");
if (tagsValue) {
tagsValue.textContent = product.tags.join(", ");
}
}
// Find and update Dimensions
const dimensionsLabel = Array.from(allSpans).find(
(span) => span.textContent === "Dimension"
);
console.log("Dimensions label found:", dimensionsLabel);
console.log("Product dimensions:", product.dimensions);
if (dimensionsLabel && product.dimensions) {
const dimensionsValue =
dimensionsLabel.parentElement.querySelector("span:last-child");
console.log("Dimensions value element:", dimensionsValue);
if (dimensionsValue) {
dimensionsValue.textContent = product.dimensions;
console.log("Updated dimensions to:", product.dimensions);
}
}
// Update description content
if (product.descriptionLong) {
const descriptionContainer = document.querySelector(
".max-w-5xl.mx-auto.space-y-6"
);
if (descriptionContainer) {
const descriptionParagraphs =
descriptionContainer.querySelectorAll("p");
console.log("Description paragraphs found:", descriptionParagraphs);
// Update each paragraph with the product's description content
product.descriptionLong.forEach((paragraph, index) => {
if (descriptionParagraphs[index]) {
descriptionParagraphs[index].textContent = paragraph;
}
});
}
}
// Update gallery images
if (product.galleryPairs) {
// Find gallery images by alt text
const allImages = document.querySelectorAll("img");
const galleryImages = Array.from(allImages).filter(
(img) =>
img.alt &&
(img.alt.includes("Sofa variant left") ||
img.alt.includes("Sofa variant right"))
);
console.log("Gallery images found:", galleryImages);
product.galleryPairs.forEach((pair, index) => {
if (galleryImages[index * 2]) {
galleryImages[index * 2].src = pair.left;
galleryImages[index * 2].alt = product.alt || product.name;
}
if (galleryImages[index * 2 + 1]) {
galleryImages[index * 2 + 1].src = pair.right;
galleryImages[index * 2 + 1].alt = product.alt || product.name;
}
});
}
// Update related products section
const relatedGrid = document.getElementById("related-grid");
if (relatedGrid && product.category) {
console.log("Current product category:", product.category);
console.log(
"All products:",
data.products.map((p) => ({
id: p.id,
name: p.name,
category: p.category,
}))
);
// Filter related products by category (excluding current product)
const allRelatedProducts = data.products.filter(
(p) => p.category === product.category && p.id !== product.id
);
console.log("All related products found:", allRelatedProducts);
let currentPage = 1;
const pageSize = 4;
function renderRelatedProducts() {
const start = 0;
const end = currentPage * pageSize;
const productsToShow = allRelatedProducts.slice(start, end);
relatedGrid.innerHTML = productsToShow
.map(
(p) => `
<a href="product-detail.html?id=${p.id}" class="block"> <a href="product-detail.html?id=${p.id}" class="block">
<div class="rounded-lg overflow-hidden bg-white shadow-sm hover:shadow-lg transition-shadow"> <div class="rounded-lg overflow-hidden bg-white shadow-sm hover:shadow-lg transition-shadow">
<div class="h-72 ${ <div class="h-72 ${
@ -566,8 +733,8 @@ function initSite() {
: "bg-white" : "bg-white"
} flex items-center justify-center"> } flex items-center justify-center">
<img src="${p.image}" alt="${ <img src="${p.image}" alt="${
p.alt || p.name p.alt || p.name
}" class="w-full h-full object-cover" /> }" class="w-full h-full object-cover" />
</div> </div>
<div class="bg-light-bg p-6"> <div class="bg-light-bg p-6">
<h3 class="font-poppins font-semibold text-2xl text-[#3A3A3A] mb-0">${ <h3 class="font-poppins font-semibold text-2xl text-[#3A3A3A] mb-0">${
@ -577,38 +744,37 @@ function initSite() {
</div> </div>
</a> </a>
` `
) )
.join(""); .join("");
// Handle "Show More" button visibility // Handle "Show More" button visibility
const showMoreBtn = document.getElementById("related-show-more");
if (showMoreBtn) {
const hasMore = allRelatedProducts.length > end;
showMoreBtn.style.display = hasMore ? "inline-flex" : "none";
showMoreBtn.classList.add(
"inline-flex",
"items-center",
"justify-center"
);
}
}
// Add event listener for "Show More" button
const showMoreBtn = document.getElementById("related-show-more"); const showMoreBtn = document.getElementById("related-show-more");
if (showMoreBtn) { if (showMoreBtn) {
showMoreBtn.addEventListener("click", () => { const hasMore = allRelatedProducts.length > end;
currentPage += 1; showMoreBtn.style.display = hasMore ? "inline-flex" : "none";
renderRelatedProducts(); showMoreBtn.classList.add(
}); "inline-flex",
"items-center",
"justify-center"
);
} }
// Initial render
renderRelatedProducts();
} }
} catch (error) {
console.error("Error loading product data:", error); // Add event listener for "Show More" button
const showMoreBtn = document.getElementById("related-show-more");
if (showMoreBtn) {
showMoreBtn.addEventListener("click", () => {
currentPage += 1;
renderRelatedProducts();
});
}
// Initial render
renderRelatedProducts();
} }
})(); } catch (error) {
console.error("Error loading product data:", error);
}
} }
// Product Comparison Page Functionality // Product Comparison Page Functionality
@ -1001,6 +1167,8 @@ if (window.location.pathname.includes("product-detail.html")) {
"Product detail page detected, initializing compare functionality immediately" "Product detail page detected, initializing compare functionality immediately"
); );
initProductDetailCompare(); initProductDetailCompare();
// Also initialize product detail loading
initProductDetail();
} }
// Also try to initialize on DOMContentLoaded // Also try to initialize on DOMContentLoaded
@ -1009,6 +1177,8 @@ document.addEventListener("DOMContentLoaded", function () {
if (window.location.pathname.includes("product-detail.html")) { if (window.location.pathname.includes("product-detail.html")) {
console.log("Product detail page detected in DOMContentLoaded"); console.log("Product detail page detected in DOMContentLoaded");
initProductDetailCompare(); initProductDetailCompare();
// Also initialize product detail loading
initProductDetail();
} }
}); });

View file

@ -123,6 +123,11 @@ class ProductManager {
.join(""); .join("");
this.updateResultsCount(); this.updateResultsCount();
this.updatePagination(); this.updatePagination();
// Re-add image enlargement listeners after products are rendered
setTimeout(() => {
addImageEnlargementListeners();
}, 50);
} }
// Create individual product card HTML // Create individual product card HTML
@ -140,7 +145,8 @@ class ProductManager {
<img <img
src="${product.image}" src="${product.image}"
alt="${product.alt}" alt="${product.alt}"
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300 cursor-pointer"
data-enlarge-src="${product.image}"
/> />
<!-- Hover Overlay --> <!-- Hover Overlay -->
<div class="absolute inset-0 bg-dark-charcoal bg-opacity-70 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center"> <div class="absolute inset-0 bg-dark-charcoal bg-opacity-70 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center">
@ -454,7 +460,76 @@ class ProductManager {
// Initialize product manager // Initialize product manager
const productManager = new ProductManager(); const productManager = new ProductManager();
// Image enlargement modal functionality
function addImageEnlargementListeners() {
const productImages = document.querySelectorAll(
"#product-grid img[data-enlarge-src]"
);
const modal = document.getElementById("image-modal");
const modalImage = document.getElementById("modal-image");
const modalCloseBtn = document.getElementById("modal-close-btn");
if (!modal || !modalImage || !modalCloseBtn) return;
productImages.forEach((img) => {
img.addEventListener("click", function (e) {
e.preventDefault(); // Prevent any default behavior
e.stopPropagation(); // Stop event bubbling to parent elements
const imageSrc = this.getAttribute("data-enlarge-src");
const imageAlt = this.getAttribute("alt");
modalImage.src = imageSrc;
modalImage.alt = imageAlt;
// Show modal with animation
modal.classList.remove("hidden");
document.body.style.overflow = "hidden"; // Prevent background scrolling
// Trigger animation after a brief delay
setTimeout(() => {
modalImage.classList.remove("scale-95");
modalImage.classList.add("scale-100");
}, 10);
});
});
// Close modal functionality
function closeModal() {
// Animate out
modalImage.classList.remove("scale-100");
modalImage.classList.add("scale-95");
// Hide modal after animation
setTimeout(() => {
modal.classList.add("hidden");
document.body.style.overflow = ""; // Restore scrolling
}, 300);
}
modalCloseBtn.addEventListener("click", closeModal);
// Close modal when clicking outside the image
modal.addEventListener("click", function (e) {
if (e.target === modal) {
closeModal();
}
});
// Close modal with Escape key
document.addEventListener("keydown", function (e) {
if (e.key === "Escape" && !modal.classList.contains("hidden")) {
closeModal();
}
});
}
// Load products when DOM is ready // Load products when DOM is ready
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
productManager.loadProducts(); productManager.loadProducts();
// Add image enlargement listeners after products are loaded
setTimeout(() => {
addImageEnlargementListeners();
}, 100);
}); });

View file

@ -673,6 +673,10 @@ video {
top: 20rem; top: 20rem;
} }
.top-4 {
top: 1rem;
}
.z-10 { .z-10 {
z-index: 10; z-index: 10;
} }
@ -779,10 +783,6 @@ video {
margin-top: 2.5rem; margin-top: 2.5rem;
} }
.mt-2 {
margin-top: 0.5rem;
}
.mt-20 { .mt-20 {
margin-top: 5rem; margin-top: 5rem;
} }
@ -791,10 +791,18 @@ video {
margin-top: 0.75rem; margin-top: 0.75rem;
} }
.mt-12 {
margin-top: 3rem;
}
.mt-6 { .mt-6 {
margin-top: 1.5rem; margin-top: 1.5rem;
} }
.mt-2 {
margin-top: 0.5rem;
}
.box-border { .box-border {
box-sizing: border-box; box-sizing: border-box;
} }
@ -883,22 +891,22 @@ video {
height: 24rem; height: 24rem;
} }
.h-\[22vh\] { .h-\[145px\] {
height: 22vh; height: 145px;
} }
.h-\[29vh\] { .h-\[301px\] {
height: 29vh; height: 301px;
}
.h-\[446px\] {
height: 446px;
} }
.h-\[64px\] { .h-\[64px\] {
height: 64px; height: 64px;
} }
.h-\[6vh\] {
height: 6vh;
}
.h-auto { .h-auto {
height: auto; height: auto;
} }
@ -915,6 +923,54 @@ video {
height: 100vh; height: 100vh;
} }
.h-\[320px\] {
height: 320px;
}
.h-\[220px\] {
height: 220px;
}
.h-\[100px\] {
height: 100px;
}
.h-\[25vh\] {
height: 25vh;
}
.h-\[18vh\] {
height: 18vh;
}
.h-\[7vh\] {
height: 7vh;
}
.h-\[30vh\] {
height: 30vh;
}
.h-\[22vh\] {
height: 22vh;
}
.h-\[8vh\] {
height: 8vh;
}
.h-\[29vh\] {
height: 29vh;
}
.h-\[21vh\] {
height: 21vh;
}
.h-\[6vh\] {
height: 6vh;
}
.h-\[31vh\] { .h-\[31vh\] {
height: 31vh; height: 31vh;
} }
@ -927,8 +983,12 @@ video {
height: 32vh; height: 32vh;
} }
.h-\[25vh\] { .h-\[95vh\] {
height: 25vh; height: 95vh;
}
.max-h-full {
max-height: 100%;
} }
.min-h-\[64px\] { .min-h-\[64px\] {
@ -1015,6 +1075,10 @@ video {
width: 100%; width: 100%;
} }
.w-\[95vw\] {
width: 95vw;
}
.max-w-2xl { .max-w-2xl {
max-width: 42rem; max-width: 42rem;
} }
@ -1047,6 +1111,14 @@ video {
max-width: 20rem; max-width: 20rem;
} }
.max-w-4xl {
max-width: 56rem;
}
.max-w-full {
max-width: 100%;
}
.flex-1 { .flex-1 {
flex: 1 1 0%; flex: 1 1 0%;
} }
@ -1070,6 +1142,18 @@ video {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
} }
.scale-95 {
--tw-scale-x: .95;
--tw-scale-y: .95;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.scale-100 {
--tw-scale-x: 1;
--tw-scale-y: 1;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.transform { .transform {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
} }
@ -1164,14 +1248,14 @@ video {
gap: 1.5rem; gap: 1.5rem;
} }
.gap-7 {
gap: 1.75rem;
}
.gap-8 { .gap-8 {
gap: 2rem; gap: 2rem;
} }
.gap-7 {
gap: 1.75rem;
}
.space-x-10 > :not([hidden]) ~ :not([hidden]) { .space-x-10 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0; --tw-space-x-reverse: 0;
margin-right: calc(2.5rem * var(--tw-space-x-reverse)); margin-right: calc(2.5rem * var(--tw-space-x-reverse));
@ -1208,6 +1292,12 @@ video {
margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); margin-bottom: calc(0.25rem * var(--tw-space-y-reverse));
} }
.space-y-16 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(4rem * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(4rem * var(--tw-space-y-reverse));
}
.space-y-2 > :not([hidden]) ~ :not([hidden]) { .space-y-2 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0; --tw-space-y-reverse: 0;
margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse)));
@ -1442,6 +1532,14 @@ video {
--tw-bg-opacity: 0.7; --tw-bg-opacity: 0.7;
} }
.bg-opacity-20 {
--tw-bg-opacity: 0.2;
}
.bg-opacity-90 {
--tw-bg-opacity: 0.9;
}
.object-contain { .object-contain {
-o-object-fit: contain; -o-object-fit: contain;
object-fit: contain; object-fit: contain;
@ -1590,6 +1688,10 @@ video {
padding-right: 1.5rem; padding-right: 1.5rem;
} }
.pt-0 {
padding-top: 0px;
}
.pt-12 { .pt-12 {
padding-top: 3rem; padding-top: 3rem;
} }
@ -1860,6 +1962,12 @@ video {
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
} }
.shadow-2xl {
--tw-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
--tw-shadow-colored: 0 25px 50px -12px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.blur { .blur {
--tw-blur: blur(8px); --tw-blur: blur(8px);
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
@ -1874,6 +1982,11 @@ video {
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
} }
.backdrop-blur-sm {
--tw-backdrop-blur: blur(4px);
backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
}
.transition { .transition {
transition-property: color, background-color, border-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-text-decoration-color; transition-property: color, background-color, border-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-text-decoration-color;
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
@ -1922,10 +2035,22 @@ video {
transition-duration: 300ms; transition-duration: 300ms;
} }
.duration-500 {
transition-duration: 500ms;
}
.duration-1000 {
transition-duration: 1000ms;
}
.ease-in-out { .ease-in-out {
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
} }
.ease-out {
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
}
.hover\:-translate-y-0\.5:hover { .hover\:-translate-y-0\.5:hover {
--tw-translate-y: -0.125rem; --tw-translate-y: -0.125rem;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
@ -1956,6 +2081,10 @@ video {
--tw-bg-opacity: 0.9; --tw-bg-opacity: 0.9;
} }
.hover\:bg-opacity-30:hover {
--tw-bg-opacity: 0.3;
}
.hover\:text-black:hover { .hover\:text-black:hover {
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(0 0 0 / var(--tw-text-opacity, 1)); color: rgb(0 0 0 / var(--tw-text-opacity, 1));
@ -1985,6 +2114,10 @@ video {
opacity: 0.8; opacity: 0.8;
} }
.hover\:opacity-90:hover {
opacity: 0.9;
}
.hover\:shadow-lg:hover { .hover\:shadow-lg:hover {
--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
@ -2257,18 +2390,90 @@ video {
margin-left: 2rem; margin-left: 2rem;
} }
.lg\:mt-16 {
margin-top: 4rem;
}
.lg\:mt-8 { .lg\:mt-8 {
margin-top: 2rem; margin-top: 2rem;
} }
.lg\:h-\[70vh\] {
height: 70vh;
}
.lg\:h-\[90vh\] {
height: 90vh;
}
.lg\:h-\[50vh\] {
height: 50vh;
}
.lg\:h-\[600px\] {
height: 600px;
}
.lg\:h-\[500px\] {
height: 500px;
}
.lg\:h-\[85vh\] {
height: 85vh;
}
.lg\:h-\[60vh\] {
height: 60vh;
}
.lg\:h-\[65vh\] { .lg\:h-\[65vh\] {
height: 65vh; height: 65vh;
} }
.lg\:min-h-screen {
min-height: 100vh;
}
.lg\:min-h-\[90vh\] {
min-height: 90vh;
}
.lg\:min-h-\[100vh\] {
min-height: 100vh;
}
.lg\:min-h-\[150vh\] {
min-height: 150vh;
}
.lg\:min-h-\[900px\] {
min-height: 900px;
}
.lg\:min-h-\[800px\] {
min-height: 800px;
}
.lg\:min-h-\[700px\] {
min-height: 700px;
}
.lg\:min-h-\[600px\] {
min-height: 600px;
}
.lg\:min-h-\[60vh\] {
min-height: 60vh;
}
.lg\:min-h-\[65vh\] { .lg\:min-h-\[65vh\] {
min-height: 65vh; min-height: 65vh;
} }
.lg\:min-h-\[80vh\] {
min-height: 80vh;
}
.lg\:w-36 { .lg\:w-36 {
width: 9rem; width: 9rem;
} }
@ -2305,6 +2510,18 @@ video {
gap: 2rem; gap: 2rem;
} }
.lg\:space-y-10 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(2.5rem * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(2.5rem * var(--tw-space-y-reverse));
}
.lg\:space-y-6 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
}
.lg\:space-y-4 > :not([hidden]) ~ :not([hidden]) { .lg\:space-y-4 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0; --tw-space-y-reverse: 0;
margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
@ -2328,6 +2545,11 @@ video {
padding-top: 12rem; padding-top: 12rem;
} }
.lg\:text-2xl {
font-size: 1.5rem;
line-height: 2rem;
}
.lg\:text-3xl { .lg\:text-3xl {
font-size: 1.875rem; font-size: 1.875rem;
line-height: 2.25rem; line-height: 2.25rem;
@ -2348,6 +2570,11 @@ video {
line-height: 1.75rem; line-height: 1.75rem;
} }
.lg\:text-lg {
font-size: 1.125rem;
line-height: 1.75rem;
}
.lg\:leading-snug { .lg\:leading-snug {
line-height: 1.375; line-height: 1.375;
} }