Optimize images for faster mobile loading
- Host images locally instead of loading from external inwardtravel.com - Convert PNGs to WebP format (89% size reduction on teotihuacan) - Add JPEG fallbacks for older browsers - Implement lazy loading for below-fold images - Add WebP feature detection - Preload critical hero image - Total payload reduced from 2.3MB to ~600KB Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
|
@ -2,6 +2,7 @@ FROM nginx:alpine
|
|||
|
||||
# Copy static files
|
||||
COPY index.html /usr/share/nginx/html/
|
||||
COPY images/ /usr/share/nginx/html/images/
|
||||
|
||||
# Custom nginx config for SPA
|
||||
RUN echo 'server { \
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 258 KiB |
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 158 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 46 KiB |
205
index.html
|
|
@ -8,6 +8,11 @@
|
|||
<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=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;1,400&family=Inter:wght@300;400;500&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Preload critical hero image -->
|
||||
<link rel="preload" as="image" href="images/hero-mountain.webp" type="image/webp">
|
||||
<link rel="preload" as="image" href="images/hero-mountain.jpg" type="image/jpeg">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--color-bg: #0a0a0a;
|
||||
|
|
@ -48,14 +53,29 @@
|
|||
text-align: center;
|
||||
padding: 2rem;
|
||||
position: relative;
|
||||
background:
|
||||
linear-gradient(to bottom, rgba(10, 10, 10, 0.3) 0%, rgba(10, 10, 10, 0.6) 50%, rgba(10, 10, 10, 0.95) 100%),
|
||||
url('https://www.inwardtravel.com/content/image/251215-124345/acommodation-mountain-view-huautla-de-jimenez-variants/image-1759-main_adventure_image/acommodation-mountain-view-huautla-de-jimenez.jpeg');
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
.hero-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
.webp .hero-bg {
|
||||
background-image:
|
||||
linear-gradient(to bottom, rgba(10, 10, 10, 0.3) 0%, rgba(10, 10, 10, 0.6) 50%, rgba(10, 10, 10, 0.95) 100%),
|
||||
url('images/hero-mountain.webp');
|
||||
}
|
||||
|
||||
.no-webp .hero-bg {
|
||||
background-image:
|
||||
linear-gradient(to bottom, rgba(10, 10, 10, 0.3) 0%, rgba(10, 10, 10, 0.6) 50%, rgba(10, 10, 10, 0.95) 100%),
|
||||
url('images/hero-mountain.jpg');
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
max-width: 900px;
|
||||
position: relative;
|
||||
|
|
@ -164,11 +184,12 @@
|
|||
50% { transform: translateX(-50%) translateY(8px); }
|
||||
}
|
||||
|
||||
/* Full-width image break */
|
||||
/* Full-width image break - lazy loaded */
|
||||
.image-break {
|
||||
width: 100%;
|
||||
height: 50vh;
|
||||
min-height: 300px;
|
||||
background-color: var(--color-bg-alt);
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-attachment: fixed;
|
||||
|
|
@ -182,20 +203,19 @@
|
|||
background: linear-gradient(to bottom, var(--color-bg) 0%, transparent 15%, transparent 85%, var(--color-bg) 100%);
|
||||
}
|
||||
|
||||
.image-break-teotihuacan {
|
||||
background-image: url('https://www.inwardtravel.com/content/image/251216-165925/teotihuacan-mexico-temple-variants/itinerary-33-adventure_itinerary_image/teotihuacan-mexico-temple.png');
|
||||
/* Lazy load backgrounds - only load when visible */
|
||||
.webp .image-break-teotihuacan.loaded {
|
||||
background-image: url('images/teotihuacan.webp');
|
||||
}
|
||||
.no-webp .image-break-teotihuacan.loaded {
|
||||
background-image: url('images/teotihuacan.jpg');
|
||||
}
|
||||
|
||||
.image-break-ceremony {
|
||||
background-image: url('https://www.inwardtravel.com/content/image/251216-203848/mushroom-mazatec-tradition-curandera-mexico-variants/itinerary-35-adventure_itinerary_image_mobile/mushroom-mazatec-tradition-curandera-mexico.png');
|
||||
.webp .image-break-sabina.loaded {
|
||||
background-image: url('images/sabina-art.webp');
|
||||
}
|
||||
|
||||
.image-break-waterfall {
|
||||
background-image: url('https://www.inwardtravel.com/content/image/251217-210223/oaxaca-tropical-waterfall-bridge-variants/itinerary-40-adventure_itinerary_image_mobile/oaxaca-tropical-waterfall-bridge.jpeg');
|
||||
}
|
||||
|
||||
.image-break-sabina {
|
||||
background-image: url('https://www.inwardtravel.com/content/image/251215-125710/mexican-art-maria-sabina-mushrooms-variants/itinerary-37-adventure_itinerary_image_mobile/mexican-art-maria-sabina-mushrooms.jpeg');
|
||||
.no-webp .image-break-sabina.loaded {
|
||||
background-image: url('images/sabina-art.jpg');
|
||||
}
|
||||
|
||||
/* Section Styles */
|
||||
|
|
@ -257,6 +277,7 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
.journey-card-bg {
|
||||
|
|
@ -387,14 +408,34 @@
|
|||
text-align: center;
|
||||
padding: 8rem 2rem;
|
||||
position: relative;
|
||||
background:
|
||||
linear-gradient(to bottom, rgba(10, 10, 10, 0.9) 0%, rgba(10, 10, 10, 0.7) 50%, rgba(10, 10, 10, 0.95) 100%),
|
||||
url('https://www.inwardtravel.com/content/image/251215-125850/mountain-view-with-flowerts-huautla-de-jimenez-variants/itinerary-41-adventure_itinerary_image_mobile/mountain-view-with-flowerts-huautla-de-jimenez.jpeg');
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
.cta-section-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
.webp .cta-section-bg.loaded {
|
||||
background-image:
|
||||
linear-gradient(to bottom, rgba(10, 10, 10, 0.9) 0%, rgba(10, 10, 10, 0.7) 50%, rgba(10, 10, 10, 0.95) 100%),
|
||||
url('images/cta-mountain.webp');
|
||||
}
|
||||
|
||||
.no-webp .cta-section-bg.loaded {
|
||||
background-image:
|
||||
linear-gradient(to bottom, rgba(10, 10, 10, 0.9) 0%, rgba(10, 10, 10, 0.7) 50%, rgba(10, 10, 10, 0.95) 100%),
|
||||
url('images/cta-mountain.jpg');
|
||||
}
|
||||
|
||||
.cta-section-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.cta-section h2 {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
|
@ -471,6 +512,7 @@
|
|||
|
||||
.image-gallery-item {
|
||||
aspect-ratio: 1;
|
||||
background-color: var(--color-bg-alt);
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
position: relative;
|
||||
|
|
@ -490,19 +532,16 @@
|
|||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.hero {
|
||||
.hero-bg,
|
||||
.image-break,
|
||||
.cta-section-bg {
|
||||
background-attachment: scroll;
|
||||
}
|
||||
|
||||
.image-break {
|
||||
background-attachment: scroll;
|
||||
height: 40vh;
|
||||
}
|
||||
|
||||
.cta-section {
|
||||
background-attachment: scroll;
|
||||
}
|
||||
|
||||
.image-gallery {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
|
@ -529,8 +568,46 @@
|
|||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- WebP detection and lazy loading -->
|
||||
<script>
|
||||
// Detect WebP support
|
||||
(function() {
|
||||
var webp = new Image();
|
||||
webp.onload = webp.onerror = function() {
|
||||
document.documentElement.classList.add(webp.height === 1 ? 'webp' : 'no-webp');
|
||||
};
|
||||
webp.src = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA=';
|
||||
})();
|
||||
|
||||
// Lazy load background images
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var lazyBgs = document.querySelectorAll('[data-lazy-bg]');
|
||||
|
||||
if ('IntersectionObserver' in window) {
|
||||
var observer = new IntersectionObserver(function(entries) {
|
||||
entries.forEach(function(entry) {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('loaded');
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, { rootMargin: '200px' });
|
||||
|
||||
lazyBgs.forEach(function(el) {
|
||||
observer.observe(el);
|
||||
});
|
||||
} else {
|
||||
// Fallback: load all immediately
|
||||
lazyBgs.forEach(function(el) {
|
||||
el.classList.add('loaded');
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Hero -->
|
||||
<section class="hero">
|
||||
<div class="hero-bg"></div>
|
||||
<div class="hero-content">
|
||||
<p class="hero-tagline">Inward Travel</p>
|
||||
<h1>Journey to the Heart of <em>Sacred Mushrooms</em> in Oaxaca</h1>
|
||||
|
|
@ -580,7 +657,7 @@
|
|||
</section>
|
||||
|
||||
<!-- Image Break: Teotihuacan -->
|
||||
<div class="image-break image-break-teotihuacan"></div>
|
||||
<div class="image-break image-break-teotihuacan" data-lazy-bg></div>
|
||||
|
||||
<!-- Journey Overview -->
|
||||
<div class="journey">
|
||||
|
|
@ -588,29 +665,29 @@
|
|||
<p class="section-label">Your Journey</p>
|
||||
<h2>9 Days of Ceremony & Connection</h2>
|
||||
<div class="journey-grid">
|
||||
<div class="journey-card">
|
||||
<div class="journey-card-bg" style="background-image: url('https://www.inwardtravel.com/content/image/251216-165925/teotihuacan-mexico-temple-variants/itinerary-33-adventure_itinerary_image/teotihuacan-mexico-temple.png')"></div>
|
||||
<div class="journey-card" data-lazy-bg>
|
||||
<div class="journey-card-bg" data-bg-webp="images/teotihuacan.webp" data-bg-jpg="images/teotihuacan.jpg"></div>
|
||||
<div class="journey-card-content">
|
||||
<h3>Arrival & Teotihuacán</h3>
|
||||
<p>Begin in Mexico City with an exploration of the ancient temple complex, setting intentions before journeying into the mountains.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="journey-card">
|
||||
<div class="journey-card-bg" style="background-image: url('https://www.inwardtravel.com/content/image/251216-203848/mushroom-mazatec-tradition-curandera-mexico-variants/itinerary-35-adventure_itinerary_image_mobile/mushroom-mazatec-tradition-curandera-mexico.png')"></div>
|
||||
<div class="journey-card" data-lazy-bg>
|
||||
<div class="journey-card-bg" data-bg-webp="images/ceremony.webp" data-bg-jpg="images/ceremony.jpg"></div>
|
||||
<div class="journey-card-content">
|
||||
<h3>Three Sacred Ceremonies</h3>
|
||||
<p>Experience traditional Mazatec mushroom ceremonies led by Magdalena or Eugenia Casimiro in the village of Huautla de Jiménez.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="journey-card">
|
||||
<div class="journey-card-bg" style="background-image: url('https://www.inwardtravel.com/content/image/251217-210223/oaxaca-tropical-waterfall-bridge-variants/itinerary-40-adventure_itinerary_image_mobile/oaxaca-tropical-waterfall-bridge.jpeg')"></div>
|
||||
<div class="journey-card" data-lazy-bg>
|
||||
<div class="journey-card-bg" data-bg-webp="images/waterfall.webp" data-bg-jpg="images/waterfall.jpg"></div>
|
||||
<div class="journey-card-content">
|
||||
<h3>Integration & Exploration</h3>
|
||||
<p>Daily yoga, breathwork, hikes to waterfalls, a traditional temazcal ceremony, and visits to María Sabina's sacred sites.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="journey-card">
|
||||
<div class="journey-card-bg" style="background-image: url('https://www.inwardtravel.com/content/image/251215-124551/huautla-de-imenez-place-of-worship-mountain-view-variants/itinerary-36-adventure_itinerary_image_mobile/huautla-de-imenez-place-of-worship-mountain-view.jpeg')"></div>
|
||||
<div class="journey-card" data-lazy-bg>
|
||||
<div class="journey-card-bg" data-bg-webp="images/community.webp" data-bg-jpg="images/community.jpg"></div>
|
||||
<div class="journey-card-content">
|
||||
<h3>Community & Connection</h3>
|
||||
<p>Shared meals, integration circles, and meaningful conversations with fellow travelers in an intimate group setting.</p>
|
||||
|
|
@ -622,9 +699,9 @@
|
|||
|
||||
<!-- Image Gallery -->
|
||||
<div class="image-gallery">
|
||||
<div class="image-gallery-item" style="background-image: url('https://www.inwardtravel.com/content/image/251215-125710/mexican-art-maria-sabina-mushrooms-variants/itinerary-37-adventure_itinerary_image_mobile/mexican-art-maria-sabina-mushrooms.jpeg')"></div>
|
||||
<div class="image-gallery-item" style="background-image: url('https://www.inwardtravel.com/content/image/251216-211822/temazcal-ceremony-mexico-variants/itinerary-38-adventure_itinerary_image_mobile/temazcal-ceremony-mexico.png')"></div>
|
||||
<div class="image-gallery-item" style="background-image: url('https://www.inwardtravel.com/content/image/251215-130709/paiting-with-poem-from-maria-sabina-variants/itinerary-39-adventure_itinerary_image_mobile/paiting-with-poem-from-maria-sabina.jpeg')"></div>
|
||||
<div class="image-gallery-item" data-lazy-bg data-bg-webp="images/sabina-art.webp" data-bg-jpg="images/sabina-art.jpg"></div>
|
||||
<div class="image-gallery-item" data-lazy-bg data-bg-webp="images/temazcal.webp" data-bg-jpg="images/temazcal.jpg"></div>
|
||||
<div class="image-gallery-item" data-lazy-bg data-bg-webp="images/sabina-poem.webp" data-bg-jpg="images/sabina-poem.jpg"></div>
|
||||
</div>
|
||||
|
||||
<!-- Lineage -->
|
||||
|
|
@ -648,8 +725,8 @@
|
|||
</p>
|
||||
</section>
|
||||
|
||||
<!-- Image Break: Ceremony -->
|
||||
<div class="image-break image-break-sabina"></div>
|
||||
<!-- Image Break: Sabina Art -->
|
||||
<div class="image-break image-break-sabina" data-lazy-bg></div>
|
||||
|
||||
<!-- What's Included -->
|
||||
<div class="included">
|
||||
|
|
@ -688,14 +765,17 @@
|
|||
|
||||
<!-- CTA -->
|
||||
<section class="cta-section">
|
||||
<h2>Begin Your Journey</h2>
|
||||
<p>
|
||||
Join us for this rare opportunity to experience authentic Mazatec mushroom ceremonies in the mountains of Oaxaca.
|
||||
</p>
|
||||
<a href="https://www.inwardtravel.com/registration/mexico-mushrooms-9-days/trip" class="cta-button">Reserve Your Spot</a>
|
||||
<p class="contact-email">
|
||||
Questions? Reach out at <a href="mailto:info@inwardtravel.com">info@inwardtravel.com</a>
|
||||
</p>
|
||||
<div class="cta-section-bg" data-lazy-bg></div>
|
||||
<div class="cta-section-content">
|
||||
<h2>Begin Your Journey</h2>
|
||||
<p>
|
||||
Join us for this rare opportunity to experience authentic Mazatec mushroom ceremonies in the mountains of Oaxaca.
|
||||
</p>
|
||||
<a href="https://www.inwardtravel.com/registration/mexico-mushrooms-9-days/trip" class="cta-button">Reserve Your Spot</a>
|
||||
<p class="contact-email">
|
||||
Questions? Reach out at <a href="mailto:info@inwardtravel.com">info@inwardtravel.com</a>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
|
|
@ -708,5 +788,38 @@
|
|||
</div>
|
||||
<p class="copyright">© 2025 Inward Travel. All rights reserved.</p>
|
||||
</footer>
|
||||
|
||||
<!-- Enhanced lazy loading for inline background images -->
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var isWebP = document.documentElement.classList.contains('webp');
|
||||
var lazyItems = document.querySelectorAll('[data-bg-webp]');
|
||||
|
||||
if ('IntersectionObserver' in window) {
|
||||
var imgObserver = new IntersectionObserver(function(entries) {
|
||||
entries.forEach(function(entry) {
|
||||
if (entry.isIntersecting) {
|
||||
var el = entry.target;
|
||||
var src = isWebP ? el.dataset.bgWebp : el.dataset.bgJpg;
|
||||
if (src) {
|
||||
el.style.backgroundImage = 'url(' + src + ')';
|
||||
}
|
||||
imgObserver.unobserve(el);
|
||||
}
|
||||
});
|
||||
}, { rootMargin: '200px' });
|
||||
|
||||
lazyItems.forEach(function(el) {
|
||||
imgObserver.observe(el);
|
||||
});
|
||||
} else {
|
||||
// Fallback
|
||||
lazyItems.forEach(function(el) {
|
||||
var src = isWebP ? el.dataset.bgWebp : el.dataset.bgJpg;
|
||||
if (src) el.style.backgroundImage = 'url(' + src + ')';
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||