<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Hotel Brochure | Room Gallery & Amenities</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #f4f1ea; /* warm paper-like background for flyer feel */
font-family: 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
line-height: 1.5;
padding: 2rem 1.5rem;
color: #2c2b28;
}
/* brochure container */
.brochure {
max-width: 1280px;
margin: 0 auto;
background: white;
border-radius: 28px;
box-shadow: 0 20px 35px -12px rgba(0, 0, 0, 0.15);
overflow: hidden;
padding: 2rem 2rem 2.5rem 2rem;
}
/* header */
.header {
text-align: center;
margin-bottom: 2.5rem;
border-bottom: 2px solid #e0cba0;
padding-bottom: 1.2rem;
}
.header h1 {
font-size: 2.8rem;
letter-spacing: -0.5px;
font-weight: 600;
color: #3e2a21;
font-family: 'Playfair Display', 'Times New Roman', serif;
}
.header p {
font-size: 1.1rem;
color: #6b5a4e;
margin-top: 0.5rem;
}
.tagline {
font-style: italic;
font-size: 0.95rem;
color: #9b7b5c;
}
/* room grid */
.rooms-grid {
display: flex;
flex-direction: column;
gap: 3rem;
}
/* individual room card */
.room-card {
background: #ffffff;
border-radius: 24px;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.05);
transition: transform 0.2s ease, box-shadow 0.2s ease;
overflow: hidden;
border: 1px solid #ede6db;
}
.room-card:hover {
box-shadow: 0 18px 30px -10px rgba(0, 0, 0, 0.12);
transform: translateY(-3px);
}
/* gallery area: main showcase + thumbnails */
.gallery {
padding: 1.5rem 1.5rem 0.8rem 1.5rem;
}
.main-image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
border-radius: 20px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transition: opacity 0.2s;
background: #f0ede8;
}
.thumbnail-strip {
display: flex;
gap: 0.8rem;
margin-top: 1rem;
flex-wrap: wrap;
justify-content: flex-start;
}
.thumbnail {
width: calc(25% - 0.6rem);
min-width: 70px;
aspect-ratio: 4 / 3;
object-fit: cover;
border-radius: 14px;
cursor: pointer;
border: 2px solid transparent;
transition: all 0.2s;
background: #f3efe8;
}
.thumbnail:hover {
transform: scale(0.98);
border-color: #c7ab7c;
opacity: 0.85;
}
.thumbnail.active {
border-color: #b48b4b;
box-shadow: 0 0 0 2px #f3e1bc;
}
/* room info */
.room-info {
padding: 0 1.5rem 1.8rem 1.5rem;
}
.room-title {
font-size: 1.9rem;
font-weight: 600;
font-family: 'Playfair Display', serif;
color: #3e2a21;
margin-bottom: 0.5rem;
border-left: 5px solid #dbb87c;
padding-left: 1rem;
}
.room-description {
color: #4a3e34;
margin: 0.75rem 0 1rem 0;
font-size: 1rem;
line-height: 1.5;
}
.amenities {
background: #faf7f0;
padding: 0.9rem 1.2rem;
border-radius: 24px;
margin-top: 0.8rem;
display: inline-block;
width: 100%;
}
.amenities h4 {
font-size: 1rem;
text-transform: uppercase;
letter-spacing: 1px;
color: #a77f44;
margin-bottom: 0.6rem;
font-weight: 600;
}
.amenities-list {
display: flex;
flex-wrap: wrap;
gap: 0.6rem 1.2rem;
list-style: none;
}
.amenities-list li {
font-size: 0.9rem;
background: white;
padding: 0.2rem 0.8rem;
border-radius: 40px;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
border: 1px solid #e5dbc9;
display: inline-flex;
align-items: center;
gap: 0.3rem;
}
.amenities-list li::before {
content: "✨";
font-size: 0.8rem;
}
/* print-friendly adjustments */
@media print {
body {
background: white;
padding: 0.2in;
}
.brochure {
box-shadow: none;
padding: 0.2in;
border-radius: 0;
}
.thumbnail-strip {
break-inside: avoid;
}
.room-card {
break-inside: avoid;
page-break-inside: avoid;
margin-bottom: 1.2rem;
}
.thumbnail:hover {
transform: none;
}
.rooms-grid {
gap: 1.5rem;
}
}
/* responsive */
@media (max-width: 750px) {
body {
padding: 1rem;
}
.brochure {
padding: 1.2rem;
}
.room-title {
font-size: 1.5rem;
}
.thumbnail {
width: calc(33% - 0.6rem);
}
.main-image {
aspect-ratio: 4 / 3;
}
}
@media (max-width: 550px) {
.thumbnail {
width: calc(50% - 0.5rem);
}
}
footer {
text-align: center;
margin-top: 2.8rem;
font-size: 0.8rem;
color: #bcae97;
border-top: 1px solid #ede3d4;
padding-top: 1.5rem;
}
.instruction-note {
background: #fef7e8;
border-radius: 20px;
padding: 1rem 1.5rem;
margin-bottom: 2rem;
font-size: 0.9rem;
border-left: 4px solid #e2bc7c;
}
.instruction-note code {
background: #e9e1d3;
padding: 0.2rem 0.4rem;
border-radius: 8px;
font-family: monospace;
font-size: 0.85rem;
}
button.edit-hint {
background: none;
border: none;
font-size: 0.8rem;
color: #b68b40;
cursor: pointer;
text-decoration: underline;
}
</style>
</head>
<body>
<div class="brochure">
<div class="header">
<h1>✦ Serene Haven Hotel ✦</h1>
<p>Luxury & comfort in every corner</p>
<div class="tagline">— your stay, reimagined —</div>
</div>
<!-- simple instruction box for user to replace images / data -->
<div class="instruction-note">
📸 <strong>How to add your own room images & descriptions:</strong><br>
• This brochure shows <strong>3–4 images per room</strong> (gallery + thumbnails).<br>
• To replace with <strong>your hotel photos</strong>: edit the <code>roomsData</code> array inside the <script> tag.<br>
• For each room, add <code>imageUrls: ["url1.jpg","url2.jpg","url3.jpg","url4.jpg"]</code> (use Imgur, Dropbox, or local paths).<br>
• Update <code>name</code>, <code>description</code>, and <code>amenities</code> list.<br>
• <strong>No coding needed?</strong> Just replace placeholder image URLs with your own image links — clustering is automatic per room.
</div>
<div class="rooms-grid" id="roomsGridContainer">
<!-- dynamically populated rooms with gallery clustering -->
</div>
<footer>
✨ Experience elegance — each room tells a story ✨<br>
<span style="font-size:0.7rem;"> brochure flyer • room clusters with 3-4 images per type</span>
</footer>
</div>
<script>
// ========================
// DATA MODEL: ROOM CLUSTERS (each room type has its own images)
// ========================
// Replace image URLs with your own hotel photos (upload to any image hosting and paste links)
// Each room can have 3 or 4 images – we show them as gallery + thumbnails.
const roomsData = [
{
id: "deluxe",
name: "🌿 Deluxe Garden View",
description: "Wake up to lush garden views and natural morning light. Spacious layout with a private balcony overlooking our tropical gardens. Perfect for relaxation and tranquility.",
amenities: ["King-size bed", "Rain shower", "Free Wi-Fi", "Mini bar", "Organic toiletries", "Smart TV"],
imageUrls: [
"https://picsum.photos/id/104/800/600", // garden/room vibe
"https://picsum.photos/id/106/800/600",
"https://picsum.photos/id/13/800/600",
"https://picsum.photos/id/29/800/600"
]
},
{
id: "ocean",
name: "🌊 Oceanfront Suite",
description: "Unobstructed ocean views from floor-to-ceiling windows. Elegant suite with separate living area and premium amenities for an unforgettable seaside escape.",
amenities: ["Panoramic sea view", "Jacuzzi tub", "Butler service", "Premium bedding", "Nespresso machine", "Sound system"],
imageUrls: [
"https://picsum.photos/id/22/800/600", // ocean wave style
"https://picsum.photos/id/23/800/600",
"https://picsum.photos/id/24/800/600",
"https://picsum.photos/id/96/800/600"
]
},
{
id: "executive",
name: "🏙️ Executive Skyline",
description: "Modern design, city skyline views, and workspace area. Ideal for business travelers or guests who love contemporary urban energy and high-end comfort.",
amenities: ["Work desk & ergonomic chair", "Express check-in", "Club lounge access", "Blackout curtains", "Evening turndown", "High-speed fiber"],
imageUrls: [
"https://picsum.photos/id/1/800/600",
"https://picsum.photos/id/20/800/600",
"https://picsum.photos/id/169/800/600"
// only 3 images for this room type — shows flexibility
]
},
{
id: "family",
name: "👪 Family Connecting Room",
description: "Two interconnecting rooms with extra space, bunk beds for kids, and thoughtful family amenities. Plenty of room for joy and togetherness.",
amenities: ["Kids' play corner", "Microwave & fridge", "Complimentary breakfast for children", "Baby cot available", "Board games", "Laundry service"],
imageUrls: [
"https://picsum.photos/id/91/800/600",
"https://picsum.photos/id/127/800/600",
"https://picsum.photos/id/77/800/600",
"https://picsum.photos/id/150/800/600"
]
}
];
// helper: render each room card with gallery + thumbnail cluster
function renderRooms() {
const container = document.getElementById('roomsGridContainer');
if (!container) return;
container.innerHTML = ''; // clear
roomsData.forEach((room, idx) => {
const images = room.imageUrls;
if (!images || images.length === 0) return;
// create card element
const card = document.createElement('div');
card.className = 'room-card';
card.setAttribute('data-room-id', room.id);
// gallery section
const galleryDiv = document.createElement('div');
galleryDiv.className = 'gallery';
// main image (first one initially)
const mainImg = document.createElement('img');
mainImg.className = 'main-image';
mainImg.alt = `${room.name} main view`;
mainImg.src = images[0];
// thumbnails container
const thumbStrip = document.createElement('div');
thumbStrip.className = 'thumbnail-strip';
// populate thumbnails
images.forEach((imgUrl, imgIndex) => {
const thumb = document.createElement('img');
thumb.src = imgUrl;
thumb.alt = `${room.name} view ${imgIndex+1}`;
thumb.className = 'thumbnail';
if (imgIndex === 0) thumb.classList.add('active');
// click on thumbnail -> swap main image
thumb.addEventListener('click', (e) => {
e.stopPropagation();
// update main image src
mainImg.src = imgUrl;
// update active class on thumbnails
const allThumbs = thumbStrip.querySelectorAll('.thumbnail');
allThumbs.forEach(t => t.classList.remove('active'));
thumb.classList.add('active');
});
thumbStrip.appendChild(thumb);
});
galleryDiv.appendChild(mainImg);
galleryDiv.appendChild(thumbStrip);
// Room info section
const infoDiv = document.createElement('div');
infoDiv.className = 'room-info';
const title = document.createElement('h2');
title.className = 'room-title';
title.innerText = room.name;
const desc = document.createElement('p');
desc.className = 'room-description';
desc.innerText = room.description;
// amenities block
const amenitiesDiv = document.createElement('div');
amenitiesDiv.className = 'amenities';
const amenitiesTitle = document.createElement('h4');
amenitiesTitle.innerText = '✧ room amenities ✧';
const amenitiesList = document.createElement('ul');
amenitiesList.className = 'amenities-list';
room.amenities.forEach(amenity => {
const li = document.createElement('li');
li.innerText = amenity;
amenitiesList.appendChild(li);
});
amenitiesDiv.appendChild(amenitiesTitle);
amenitiesDiv.appendChild(amenitiesList);
infoDiv.appendChild(title);
infoDiv.appendChild(desc);
infoDiv.appendChild(amenitiesDiv);
card.appendChild(galleryDiv);
card.appendChild(infoDiv);
container.appendChild(card);
});
// If no rooms, show friendly message
if (roomsData.length === 0) {
container.innerHTML = '<div style="padding:2rem; text-align:center;">✨ No room data yet. Add rooms in the <code>roomsData</code> array above. ✨</div>';
}
}
// optional: helper for user to understand how to replace images
// also we can add a small interactive hint in console.
console.log("🖼️ Hotel Brochure ready — To customize, edit 'roomsData' array. Each room accepts 3-4 image URLs. Clustering done automatically per room type.");
// render on load
document.addEventListener('DOMContentLoaded', renderRooms);
</script>
<!-- Additional style for smooth image loading & fallback -->
<style>
.main-image, .thumbnail {
background-color: #f2ede4;
transition: all 0.2s ease;
}
.thumbnail {
opacity: 0.9;
}
.thumbnail.active {
opacity: 1;
border: 2px solid #c9a86b;
transform: scale(0.97);
}
.amenities-list li::before {
content: "✓";
font-weight: bold;
color: #c9a86b;
margin-right: 5px;
}
.room-card {
background: #fffefc;
}
</style>
<!-- BONUS: instruction on how to use your own hosted images -->
<div style="display: none;">Customization Guide: change imageUrls inside roomsData to point to your hotel room images (3 or 4 each). The gallery and thumbnails auto-cluster.</div>
</body>
</html>