worldwander-site/index.html

2548 lines
No EOL
117 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WorldWander - 365 Days. 40 Countries. One Story.</title>
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400;1,700&family=Open+Sans:wght@300;400;600;700&family=Dancing+Script:wght@400;700&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<style>
/* ============================================
CSS RESET & BASE
============================================ */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; font-size: 16px; }
body {
font-family: 'Open Sans', sans-serif;
background: #F5F0E8;
color: #2C1810;
overflow-x: hidden;
line-height: 1.7;
}
/* ============================================
COLOR PALETTE VARIABLES
============================================ */
:root {
--dark-brown: #2C1810;
--forest-green: #4A6741;
--sandy-gold: #D4A574;
--cream: #F5F0E8;
--saddle-brown: #8B4513;
--deep-green: #2d3e28;
--warm-gold: #c49a6c;
--light-sage: #a8b99a;
}
/* ============================================
TYPOGRAPHY
============================================ */
h1, h2, h3, h4 { font-family: 'Playfair Display', serif; }
.journal-text { font-family: 'Dancing Script', cursive; }
/* ============================================
READING PROGRESS BAR
============================================ */
.reading-progress {
position: fixed;
top: 0;
left: 0;
width: 0%;
height: 3px;
background: linear-gradient(90deg, var(--sandy-gold), var(--saddle-brown));
z-index: 10001;
transition: width 0.1s linear;
}
.read-time-badge {
position: fixed;
top: 8px;
right: 80px;
z-index: 10000;
background: rgba(44,24,16,0.7);
color: var(--cream);
padding: 0.2rem 0.6rem;
border-radius: 12px;
font-size: 0.65rem;
letter-spacing: 1px;
text-transform: uppercase;
opacity: 0;
transition: opacity 0.4s;
pointer-events: none;
}
.read-time-badge.visible { opacity: 1; }
/* ============================================
SCROLL ANIMATIONS
============================================ */
.reveal {
opacity: 0;
transform: translateY(40px);
transition: opacity 0.9s ease, transform 0.9s ease;
}
.reveal.visible {
opacity: 1;
transform: translateY(0);
}
.reveal-left {
opacity: 0;
transform: translateX(-60px);
transition: opacity 0.9s ease, transform 0.9s ease;
}
.reveal-left.visible {
opacity: 1;
transform: translateX(0);
}
.reveal-right {
opacity: 0;
transform: translateX(60px);
transition: opacity 0.9s ease, transform 0.9s ease;
}
.reveal-right.visible {
opacity: 1;
transform: translateX(0);
}
/* ============================================
NAVIGATION BAR
============================================ */
.site-nav {
position: fixed;
top: 3px;
left: 0;
right: 0;
z-index: 1000;
background: rgba(44, 24, 16, 0.0);
backdrop-filter: blur(0px);
transition: background 0.4s ease, backdrop-filter 0.4s ease, box-shadow 0.4s ease;
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.site-nav.scrolled {
background: rgba(44, 24, 16, 0.92);
backdrop-filter: blur(12px);
box-shadow: 0 2px 20px rgba(0,0,0,0.3);
padding: 0.6rem 2rem;
}
.nav-logo {
font-family: 'Playfair Display', serif;
font-size: 1.4rem;
font-weight: 700;
color: #F5F0E8;
text-decoration: none;
letter-spacing: 2px;
}
.nav-links {
display: flex;
gap: 1.5rem;
list-style: none;
}
.nav-links a {
color: rgba(245, 240, 232, 0.85);
text-decoration: none;
font-size: 0.85rem;
font-weight: 600;
letter-spacing: 1.5px;
text-transform: uppercase;
transition: color 0.3s;
}
.nav-links a:hover { color: var(--sandy-gold); }
.mobile-menu-btn {
display: none;
background: none;
border: none;
color: var(--cream);
font-size: 1.5rem;
cursor: pointer;
}
@media (max-width: 768px) {
.nav-links { display: none; flex-direction: column; position: absolute; top: 100%; left: 0; right: 0; background: rgba(44,24,16,0.95); padding: 1rem 2rem; gap: 1rem; }
.nav-links.open { display: flex; }
.mobile-menu-btn { display: block; }
.read-time-badge { display: none; }
}
/* ============================================
HERO SECTION
============================================ */
.hero {
position: relative;
height: 100vh;
min-height: 600px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
overflow: hidden;
}
.hero-bg {
position: absolute;
inset: 0;
background: url('images/chile_andes.jpg') center center / cover no-repeat;
transform: scale(1.1);
transition: transform 0.1s linear;
}
.hero-overlay {
position: absolute;
inset: 0;
background: linear-gradient(
180deg,
rgba(44, 24, 16, 0.3) 0%,
rgba(44, 24, 16, 0.55) 50%,
rgba(44, 24, 16, 0.85) 100%
);
}
.hero-content {
position: relative;
z-index: 2;
color: var(--cream);
max-width: 800px;
padding: 0 2rem;
}
.hero-content h1 {
font-size: clamp(3rem, 8vw, 6rem);
font-weight: 900;
letter-spacing: 6px;
text-transform: uppercase;
margin-bottom: 0.5rem;
text-shadow: 0 4px 30px rgba(0,0,0,0.5);
}
.hero-content .subtitle {
font-family: 'Dancing Script', cursive;
font-size: clamp(1.2rem, 3vw, 2rem);
color: var(--sandy-gold);
margin-bottom: 1.5rem;
}
.hero-content .year-badge {
display: inline-block;
border: 2px solid var(--sandy-gold);
padding: 0.4rem 1.5rem;
font-size: 0.85rem;
letter-spacing: 3px;
text-transform: uppercase;
margin-bottom: 2rem;
}
.scroll-indicator {
position: absolute;
bottom: 2rem;
left: 50%;
transform: translateX(-50%);
z-index: 3;
color: var(--cream);
text-align: center;
animation: bounce 2s infinite;
cursor: pointer;
}
.scroll-indicator span {
display: block;
font-size: 0.7rem;
letter-spacing: 3px;
text-transform: uppercase;
margin-bottom: 0.5rem;
opacity: 0.7;
}
.scroll-indicator .arrow {
font-size: 2rem;
opacity: 0.7;
}
@keyframes bounce {
0%, 100% { transform: translateX(-50%) translateY(0); }
50% { transform: translateX(-50%) translateY(10px); }
}
/* ============================================
PROLOGUE / ORIGIN STORY
============================================ */
.prologue {
max-width: 800px;
margin: 0 auto;
padding: 8rem 2rem;
text-align: center;
}
.prologue .journal-ornament {
font-size: 2rem;
color: var(--sandy-gold);
margin-bottom: 2rem;
letter-spacing: 8px;
}
.prologue h2 {
font-size: clamp(2rem, 4vw, 3rem);
font-weight: 400;
font-style: italic;
color: var(--saddle-brown);
margin-bottom: 2rem;
line-height: 1.4;
}
.prologue .origin-text {
font-size: 1.1rem;
line-height: 2;
color: #5a4030;
margin-bottom: 1.5rem;
}
.prologue .journal-quote {
font-family: 'Dancing Script', cursive;
font-size: 1.6rem;
color: var(--forest-green);
margin: 2.5rem 0;
padding: 1.5rem 2rem;
border-left: 3px solid var(--sandy-gold);
}
.prologue .separator {
width: 60px;
height: 2px;
background: var(--sandy-gold);
margin: 3rem auto;
}
/* ============================================
3D GLOBE SECTION
============================================ */
.globe-section {
background: var(--dark-brown);
padding: 4rem 2rem;
position: relative;
overflow: hidden;
}
.globe-section h2 {
text-align: center;
color: var(--cream);
font-size: clamp(2rem, 4vw, 3rem);
margin-bottom: 0.3rem;
}
.globe-section .section-sub {
text-align: center;
font-family: 'Dancing Script', cursive;
color: var(--sandy-gold);
font-size: 1.3rem;
margin-bottom: 2rem;
}
#globe-container {
width: 100%;
height: 550px;
max-width: 900px;
margin: 0 auto;
cursor: grab;
position: relative;
}
#globe-container:active { cursor: grabbing; }
#globe-tooltip {
position: absolute;
background: rgba(44,24,16,0.9);
color: var(--sandy-gold);
padding: 0.4rem 0.8rem;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
letter-spacing: 1px;
pointer-events: none;
display: none;
z-index: 10;
white-space: nowrap;
border: 1px solid var(--sandy-gold);
}
@media (max-width: 768px) {
#globe-container { height: 380px; }
}
/* ============================================
JOURNEY TIMELINE
============================================ */
.timeline-section {
background: var(--dark-brown);
color: var(--cream);
padding: 6rem 2rem;
overflow: hidden;
}
.timeline-section h2 {
text-align: center;
font-size: clamp(2rem, 4vw, 3rem);
margin-bottom: 0.5rem;
}
.timeline-section .section-sub {
text-align: center;
font-family: 'Dancing Script', cursive;
color: var(--sandy-gold);
font-size: 1.3rem;
margin-bottom: 3rem;
}
.timeline-scroll {
display: flex;
gap: 0;
overflow-x: auto;
padding: 2rem 0;
scroll-snap-type: x proximity;
-webkit-overflow-scrolling: touch;
scrollbar-width: thin;
scrollbar-color: var(--sandy-gold) transparent;
}
.timeline-scroll::-webkit-scrollbar { height: 6px; }
.timeline-scroll::-webkit-scrollbar-track { background: transparent; }
.timeline-scroll::-webkit-scrollbar-thumb { background: var(--sandy-gold); border-radius: 3px; }
.timeline-stop {
flex: 0 0 auto;
width: 180px;
text-align: center;
scroll-snap-align: center;
position: relative;
cursor: pointer;
transition: transform 0.3s;
}
.timeline-stop:hover { transform: translateY(-5px); }
.timeline-stop::after {
content: '';
position: absolute;
top: 28px;
left: 50%;
width: 100%;
height: 2px;
background: var(--sandy-gold);
opacity: 0.3;
}
.timeline-stop:last-child::after { display: none; }
.timeline-dot {
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--sandy-gold);
margin: 0 auto 1rem;
position: relative;
z-index: 1;
box-shadow: 0 0 0 4px rgba(212,165,116,0.2);
transition: box-shadow 0.3s, transform 0.3s;
}
.timeline-stop:hover .timeline-dot {
box-shadow: 0 0 0 8px rgba(212,165,116,0.3);
transform: scale(1.3);
}
.timeline-stop img {
width: 90px;
height: 90px;
border-radius: 50%;
object-fit: cover;
border: 2px solid var(--sandy-gold);
margin-bottom: 0.7rem;
}
.timeline-stop .no-img {
width: 90px;
height: 90px;
border-radius: 50%;
margin: 0 auto 0.7rem;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
border: 2px solid var(--sandy-gold);
background: rgba(212,165,116,0.1);
}
.timeline-stop .country-name {
font-family: 'Playfair Display', serif;
font-size: 0.85rem;
font-weight: 700;
margin-bottom: 0.2rem;
}
.timeline-stop .dates {
font-size: 0.65rem;
opacity: 0.6;
letter-spacing: 1px;
text-transform: uppercase;
}
/* ============================================
PULL QUOTES
============================================ */
.pull-quote-section {
padding: 4rem 2rem;
max-width: 900px;
margin: 0 auto;
text-align: center;
}
.pull-quote {
position: relative;
padding: 2rem 2.5rem;
margin: 0 auto;
max-width: 750px;
}
.pull-quote::before {
content: '\201C';
font-family: 'Playfair Display', serif;
font-size: 6rem;
color: var(--sandy-gold);
opacity: 0.3;
position: absolute;
top: -1rem;
left: 0;
line-height: 1;
}
.pull-quote blockquote {
font-family: 'Playfair Display', serif;
font-size: clamp(1.3rem, 2.5vw, 1.8rem);
font-style: italic;
font-weight: 400;
color: var(--saddle-brown);
line-height: 1.6;
border-left: 4px solid var(--sandy-gold);
padding-left: 1.5rem;
text-align: left;
}
.pull-quote .quote-attr {
display: block;
margin-top: 1rem;
font-family: 'Open Sans', sans-serif;
font-size: 0.8rem;
font-style: normal;
color: var(--forest-green);
letter-spacing: 2px;
text-transform: uppercase;
text-align: left;
padding-left: 1.5rem;
}
/* ============================================
BEFORE/AFTER SLIDER
============================================ */
.ba-section {
padding: 4rem 2rem;
background: linear-gradient(180deg, var(--cream) 0%, #e8dfd1 50%, var(--cream) 100%);
}
.ba-section h2 {
text-align: center;
font-size: clamp(2rem, 4vw, 3rem);
color: var(--saddle-brown);
margin-bottom: 0.3rem;
}
.ba-section .section-sub {
text-align: center;
font-family: 'Dancing Script', cursive;
color: var(--forest-green);
font-size: 1.3rem;
margin-bottom: 2.5rem;
}
.ba-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 2rem;
max-width: 1200px;
margin: 0 auto;
}
.ba-slider {
position: relative;
overflow: hidden;
border-radius: 8px;
aspect-ratio: 4/3;
cursor: col-resize;
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
user-select: none;
-webkit-user-select: none;
}
.ba-slider img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.ba-slider .ba-after {
z-index: 1;
}
.ba-slider .ba-before {
z-index: 2;
clip-path: inset(0 50% 0 0);
}
.ba-slider .ba-handle {
position: absolute;
top: 0;
bottom: 0;
left: 50%;
width: 4px;
background: white;
z-index: 3;
transform: translateX(-50%);
box-shadow: 0 0 8px rgba(0,0,0,0.4);
}
.ba-slider .ba-handle::before,
.ba-slider .ba-handle::after {
content: '';
position: absolute;
top: 50%;
width: 0;
height: 0;
border-style: solid;
}
.ba-slider .ba-handle::before {
left: -10px;
transform: translateY(-50%);
border-width: 8px 8px 8px 0;
border-color: transparent white transparent transparent;
}
.ba-slider .ba-handle::after {
right: -10px;
transform: translateY(-50%);
border-width: 8px 0 8px 8px;
border-color: transparent transparent transparent white;
}
.ba-slider .ba-label {
position: absolute;
bottom: 0.8rem;
z-index: 4;
background: rgba(44,24,16,0.8);
color: var(--cream);
padding: 0.25rem 0.6rem;
border-radius: 3px;
font-size: 0.65rem;
font-weight: 700;
letter-spacing: 1px;
text-transform: uppercase;
}
.ba-slider .ba-label-left { left: 0.8rem; }
.ba-slider .ba-label-right { right: 0.8rem; }
.ba-slider-title {
text-align: center;
font-family: 'Playfair Display', serif;
font-size: 0.95rem;
color: var(--saddle-brown);
margin-top: 0.8rem;
}
/* ============================================
CHAPTER SECTIONS
============================================ */
.chapter {
position: relative;
overflow: hidden;
}
.chapter-hero {
position: relative;
height: 70vh;
min-height: 450px;
display: flex;
align-items: flex-end;
overflow: hidden;
}
.chapter-hero-bg {
position: absolute;
inset: 0;
background-size: cover;
background-position: center;
transition: transform 0.1s linear;
}
/* Ken Burns effect */
.chapter-hero-bg.ken-burns {
animation: kenburns 20s ease-in-out infinite alternate;
}
@keyframes kenburns {
0% { transform: scale(1.0) translate(0,0); }
100% { transform: scale(1.12) translate(-1%, -1%); }
}
.chapter-hero-overlay {
position: absolute;
inset: 0;
background: linear-gradient(
0deg,
rgba(44, 24, 16, 0.95) 0%,
rgba(44, 24, 16, 0.4) 40%,
rgba(44, 24, 16, 0.1) 100%
);
}
.chapter-hero-content {
position: relative;
z-index: 2;
padding: 3rem;
max-width: 900px;
color: var(--cream);
}
.chapter-number {
font-family: 'Dancing Script', cursive;
font-size: 1.1rem;
color: var(--sandy-gold);
letter-spacing: 3px;
margin-bottom: 0.3rem;
}
.chapter-title {
font-size: clamp(2.5rem, 5vw, 4rem);
font-weight: 900;
line-height: 1.1;
margin-bottom: 0.5rem;
text-shadow: 0 2px 20px rgba(0,0,0,0.5);
}
.chapter-tagline {
font-family: 'Dancing Script', cursive;
font-size: 1.4rem;
color: var(--sandy-gold);
opacity: 0.9;
}
/* Chapter Body */
.chapter-body {
max-width: 900px;
margin: 0 auto;
padding: 4rem 2rem;
}
.chapter-body .story-text {
font-size: 1.05rem;
line-height: 2;
color: #4a3828;
margin-bottom: 1.5rem;
text-align: justify;
}
.chapter-body .story-text:first-of-type::first-letter {
font-family: 'Playfair Display', serif;
font-size: 4rem;
float: left;
line-height: 1;
margin-right: 0.5rem;
margin-top: 0.1rem;
color: var(--saddle-brown);
font-weight: 700;
}
.chapter-body .story-date {
font-family: 'Playfair Display', serif;
font-weight: 700;
font-size: 1.1rem;
color: var(--saddle-brown);
margin: 2.5rem 0 1rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--sandy-gold);
display: inline-block;
}
/* Photo Gallery Grid within Chapter */
.chapter-gallery {
padding: 2rem;
max-width: 1400px;
margin: 0 auto;
}
.chapter-gallery h3 {
text-align: center;
font-size: 1.8rem;
margin-bottom: 0.3rem;
color: var(--saddle-brown);
}
.chapter-gallery .gallery-sub {
text-align: center;
font-family: 'Dancing Script', cursive;
color: var(--forest-green);
font-size: 1.1rem;
margin-bottom: 2rem;
}
.photo-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
}
.photo-card {
position: relative;
border-radius: 8px;
overflow: hidden;
cursor: pointer;
aspect-ratio: 4/3;
background: #e0d6c8;
box-shadow: 0 2px 12px rgba(0,0,0,0.1);
transition: transform 0.4s ease, box-shadow 0.4s ease;
}
.photo-card:hover {
transform: translateY(-5px) scale(1.02);
box-shadow: 0 8px 30px rgba(0,0,0,0.2);
}
.photo-card img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.6s ease;
}
.photo-card:hover img { transform: scale(1.08); }
.photo-card .caption {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 2rem 1rem 0.8rem;
background: linear-gradient(transparent, rgba(44,24,16,0.85));
color: var(--cream);
font-size: 0.85rem;
font-weight: 600;
transform: translateY(100%);
transition: transform 0.4s ease;
}
.photo-card:hover .caption { transform: translateY(0); }
.photo-card .caption .country-tag {
font-size: 0.65rem;
color: var(--sandy-gold);
text-transform: uppercase;
letter-spacing: 2px;
display: block;
margin-bottom: 0.2rem;
}
/* Chapter separator */
.chapter-divider {
text-align: center;
padding: 3rem 0;
}
.chapter-divider .ornament {
color: var(--sandy-gold);
font-size: 1.5rem;
letter-spacing: 8px;
}
/* Gradient placeholder for chapters without images */
.chapter-hero.no-image .chapter-hero-bg {
background: linear-gradient(135deg, var(--forest-green), var(--dark-brown), var(--saddle-brown));
}
/* ============================================
FULL GALLERY SECTION
============================================ */
.gallery-section {
background: var(--dark-brown);
padding: 6rem 2rem;
}
.gallery-section h2 {
text-align: center;
color: var(--cream);
font-size: clamp(2rem, 4vw, 3rem);
margin-bottom: 0.3rem;
}
.gallery-section .section-sub {
text-align: center;
font-family: 'Dancing Script', cursive;
color: var(--sandy-gold);
font-size: 1.3rem;
margin-bottom: 2rem;
}
.filter-buttons {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.5rem;
margin-bottom: 2.5rem;
padding: 0 1rem;
}
.filter-btn {
background: rgba(245, 240, 232, 0.08);
color: var(--cream);
border: 1px solid rgba(212,165,116,0.3);
padding: 0.5rem 1.2rem;
border-radius: 30px;
font-family: 'Open Sans', sans-serif;
font-size: 0.8rem;
font-weight: 600;
letter-spacing: 1px;
text-transform: uppercase;
cursor: pointer;
transition: all 0.3s ease;
}
.filter-btn:hover,
.filter-btn.active {
background: var(--sandy-gold);
color: var(--dark-brown);
border-color: var(--sandy-gold);
}
.masonry-grid {
max-width: 1400px;
margin: 0 auto;
columns: 4;
column-gap: 1rem;
}
@media (max-width: 1100px) { .masonry-grid { columns: 3; } }
@media (max-width: 768px) { .masonry-grid { columns: 2; } }
@media (max-width: 480px) { .masonry-grid { columns: 1; } }
.masonry-item {
break-inside: avoid;
margin-bottom: 1rem;
border-radius: 8px;
overflow: hidden;
position: relative;
cursor: pointer;
transition: transform 0.4s, opacity 0.5s;
}
.masonry-item.hidden {
display: none;
}
.masonry-item img {
width: 100%;
display: block;
transition: transform 0.5s;
}
.masonry-item:hover img { transform: scale(1.05); }
.masonry-item .m-caption {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 2.5rem 0.8rem 0.6rem;
background: linear-gradient(transparent, rgba(44,24,16,0.9));
color: var(--cream);
font-size: 0.8rem;
font-weight: 600;
opacity: 0;
transition: opacity 0.4s;
}
.masonry-item:hover .m-caption { opacity: 1; }
.masonry-item .m-caption .m-country {
font-size: 0.6rem;
color: var(--sandy-gold);
text-transform: uppercase;
letter-spacing: 2px;
}
/* ============================================
EPILOGUE
============================================ */
.epilogue {
text-align: center;
padding: 8rem 2rem;
background: linear-gradient(180deg, var(--cream) 0%, #e8dfd1 100%);
}
.epilogue h2 {
font-size: clamp(2rem, 4vw, 3rem);
font-style: italic;
color: var(--saddle-brown);
margin-bottom: 2rem;
}
.epilogue .closing-text {
max-width: 700px;
margin: 0 auto 2rem;
font-size: 1.1rem;
line-height: 2;
color: #5a4030;
}
.epilogue .journal-closing {
font-family: 'Dancing Script', cursive;
font-size: 1.8rem;
color: var(--forest-green);
margin: 2rem 0;
}
.epilogue .final-ornament {
color: var(--sandy-gold);
font-size: 1.5rem;
letter-spacing: 10px;
}
/* ============================================
LIGHTBOX
============================================ */
.lightbox {
display: none;
position: fixed;
inset: 0;
z-index: 9999;
background: rgba(0, 0, 0, 0.95);
align-items: center;
justify-content: center;
cursor: zoom-out;
flex-direction: column;
}
.lightbox.active {
display: flex;
}
.lightbox img {
max-width: 92vw;
max-height: 82vh;
object-fit: contain;
border-radius: 4px;
box-shadow: 0 0 60px rgba(0,0,0,0.5);
}
.lightbox-caption {
position: absolute;
bottom: 3.5rem;
left: 50%;
transform: translateX(-50%);
color: var(--cream);
font-family: 'Playfair Display', serif;
font-size: 1.1rem;
text-align: center;
text-shadow: 0 2px 10px rgba(0,0,0,0.8);
max-width: 80%;
}
.lightbox-close {
position: absolute;
top: 1.5rem;
right: 2rem;
color: var(--cream);
font-size: 2.5rem;
cursor: pointer;
background: none;
border: none;
opacity: 0.7;
transition: opacity 0.3s;
z-index: 10000;
}
.lightbox-close:hover { opacity: 1; }
.lightbox-nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
color: var(--cream);
font-size: 3rem;
cursor: pointer;
background: none;
border: none;
opacity: 0.5;
transition: opacity 0.3s;
padding: 1rem;
z-index: 10000;
}
.lightbox-nav:hover { opacity: 1; }
.lightbox-prev { left: 1rem; }
.lightbox-next { right: 1rem; }
.lightbox-toggle-original {
position: absolute;
bottom: 1rem;
left: 50%;
transform: translateX(-50%);
background: rgba(212,165,116,0.9);
color: var(--dark-brown);
border: none;
padding: 0.4rem 1rem;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 1px;
text-transform: uppercase;
cursor: pointer;
z-index: 10000;
display: none;
transition: background 0.3s;
}
.lightbox-toggle-original:hover { background: var(--cream); }
/* ============================================
SOUND TOGGLE
============================================ */
.sound-toggle {
position: fixed;
bottom: 2rem;
right: 2rem;
z-index: 999;
background: rgba(44,24,16,0.8);
border: 1px solid var(--sandy-gold);
color: var(--sandy-gold);
width: 44px;
height: 44px;
border-radius: 50%;
cursor: pointer;
font-size: 1.2rem;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
backdrop-filter: blur(8px);
}
.sound-toggle:hover {
background: var(--sandy-gold);
color: var(--dark-brown);
}
.sound-toggle.active {
background: var(--sandy-gold);
color: var(--dark-brown);
}
/* ============================================
FOOTER
============================================ */
footer {
background: var(--dark-brown);
color: rgba(245,240,232,0.5);
text-align: center;
padding: 2rem;
font-size: 0.8rem;
}
footer a { color: var(--sandy-gold); text-decoration: none; }
/* ============================================
UTILITY
============================================ */
.text-center { text-align: center; }
.mt-2 { margin-top: 2rem; }
/* Smooth section transitions */
.section-transition {
height: 100px;
background: linear-gradient(180deg, var(--cream) 0%, var(--dark-brown) 100%);
}
.section-transition-reverse {
height: 100px;
background: linear-gradient(180deg, var(--dark-brown) 0%, var(--cream) 100%);
}
/* Chapter alternating backgrounds */
.chapter:nth-child(even) .chapter-body {
background: rgba(74, 103, 65, 0.03);
}
/* Story read more toggle */
.story-expandable {
max-height: 600px;
overflow: hidden;
position: relative;
transition: max-height 0.8s ease;
}
.story-expandable.expanded {
max-height: none;
}
.story-expandable::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 120px;
background: linear-gradient(transparent, var(--cream));
pointer-events: none;
transition: opacity 0.5s;
}
.story-expandable.expanded::after {
opacity: 0;
}
.chapter:nth-child(even) .story-expandable::after {
background: linear-gradient(transparent, #f7f3ec);
}
.read-more-btn {
display: block;
margin: 1rem auto 0;
background: none;
border: 2px solid var(--saddle-brown);
color: var(--saddle-brown);
padding: 0.6rem 2rem;
font-family: 'Open Sans', sans-serif;
font-size: 0.85rem;
font-weight: 600;
letter-spacing: 1.5px;
text-transform: uppercase;
cursor: pointer;
border-radius: 30px;
transition: all 0.3s;
}
.read-more-btn:hover {
background: var(--saddle-brown);
color: var(--cream);
}
/* Stats bar */
.stats-bar {
display: flex;
justify-content: center;
gap: 4rem;
flex-wrap: wrap;
padding: 3rem 2rem;
background: var(--dark-brown);
color: var(--cream);
}
.stat-item {
text-align: center;
}
.stat-number {
font-family: 'Playfair Display', serif;
font-size: 3rem;
font-weight: 900;
color: var(--sandy-gold);
display: block;
}
.stat-label {
font-size: 0.75rem;
letter-spacing: 2px;
text-transform: uppercase;
opacity: 0.7;
}
</style>
</head>
<body>
<!-- ====== READING PROGRESS BAR ====== -->
<div class="reading-progress" id="readingProgress"></div>
<div class="read-time-badge" id="readTimeBadge"></div>
<!-- ====== NAVIGATION ====== -->
<nav class="site-nav" id="siteNav">
<a href="#hero" class="nav-logo">WorldWander</a>
<button class="mobile-menu-btn" id="mobileMenuBtn" aria-label="Menu">&#9776;</button>
<ul class="nav-links" id="navLinks">
<li><a href="#prologue">The Story</a></li>
<li><a href="#globe-section">Globe</a></li>
<li><a href="#journey">Journey</a></li>
<li><a href="#ch-argentina">Chapters</a></li>
<li><a href="#gallery">Gallery</a></li>
<li><a href="#epilogue">Epilogue</a></li>
</ul>
</nav>
<!-- ====== HERO ====== -->
<section class="hero" id="hero">
<div class="hero-bg" id="heroBg"></div>
<div class="hero-overlay"></div>
<div class="hero-content">
<div class="year-badge">February 1998 &mdash; February 1999</div>
<h1>WorldWander</h1>
<p class="subtitle">365 days. 40 countries. One story.</p>
</div>
<div class="scroll-indicator" onclick="document.getElementById('prologue').scrollIntoView({behavior:'smooth'})">
<span>Begin the journey</span>
<div class="arrow">&#8595;</div>
</div>
</section>
<!-- ====== PROLOGUE ====== -->
<section class="prologue" id="prologue">
<div class="journal-ornament reveal">&#10047; &bull; &#10047;</div>
<h2 class="reveal">The Origin Story</h2>
<div class="separator reveal"></div>
<p class="origin-text reveal">In 2000, Karen and I took a year off to travel the world and built the original WorldWander website. One day I checked the access logs and was surprised to see the amount of traffic we still get. These pages hold the stories we wrote along the way &mdash; dispatches from bus stations at 4 AM, journal entries scribbled in candlelit restaurants, and reflections penned beneath unfamiliar constellations.</p>
<p class="origin-text reveal">What started as a sabbatical became the defining adventure of our lives. We packed two backpacks, sold the car, quit our jobs, and stepped onto a plane bound for Venezuela. The plan was loose: head south through South America, cross to Asia, wind through the Middle East, and finish in Europe. The reality was infinitely more unpredictable, more beautiful, and more transformative than anything we could have planned.</p>
<div class="journal-quote reveal">"The travel books all state what the price should be from the airport to a particular destination. But sometimes it is a lot more efficient to see for yourself."</div>
<p class="origin-text reveal">What follows is the full, unabridged story of that year &mdash; every missed bus, every jaw-dropping vista, every $2 meal that tasted better than anything back home. Scroll on, and come along for the ride.</p>
</section>
<!-- ====== STATS BAR ====== -->
<div class="stats-bar" id="statsBar">
<div class="stat-item reveal"><span class="stat-number" data-target="365" data-suffix="">0</span><span class="stat-label">Days</span></div>
<div class="stat-item reveal"><span class="stat-number" data-target="40" data-suffix="+">0</span><span class="stat-label">Countries</span></div>
<div class="stat-item reveal"><span class="stat-number" data-target="6" data-suffix="">0</span><span class="stat-label">Continents</span></div>
<div class="stat-item reveal"><span class="stat-number" data-target="1" data-suffix="">0</span><span class="stat-label">Wedding</span></div>
</div>
<!-- ====== 3D GLOBE ====== -->
<section class="globe-section" id="globe-section">
<h2 class="reveal">The Journey Around the World</h2>
<p class="section-sub reveal">Click a pin to jump to that chapter</p>
<div id="globe-container">
<div id="globe-tooltip"></div>
</div>
</section>
<!-- ====== JOURNEY TIMELINE ====== -->
<section class="timeline-section" id="journey">
<h2 class="reveal">The Road Behind</h2>
<p class="section-sub reveal">Scroll to trace our path around the world</p>
<div class="timeline-scroll" id="timelineScroll">
</div>
</section>
<div class="section-transition-reverse"></div>
<!-- ====== BEFORE/AFTER SLIDER ====== -->
<section class="ba-section" id="before-after">
<h2 class="reveal">Then &amp; Now</h2>
<p class="section-sub reveal">Drag the slider to compare original 2000-era photos with AI restorations</p>
<div class="ba-grid" id="baGrid">
</div>
</section>
<div class="section-transition"></div>
<div class="section-transition-reverse"></div>
<!-- ====== CHAPTERS ====== -->
<div id="chapters-container">
</div>
<!-- ====== FULL GALLERY ====== -->
<div class="section-transition"></div>
<section class="gallery-section" id="gallery">
<h2 class="reveal">The Complete Collection</h2>
<p class="section-sub reveal">117 photographs from around the world</p>
<div class="filter-buttons" id="filterButtons">
</div>
<div class="masonry-grid" id="masonryGrid">
</div>
</section>
<!-- ====== EPILOGUE ====== -->
<div class="section-transition-reverse"></div>
<section class="epilogue" id="epilogue">
<div class="reveal">
<h2>The Journey Continues...</h2>
<div style="width:60px;height:2px;background:var(--sandy-gold);margin:2rem auto;"></div>
<p class="closing-text">A year of travel changed everything. We learned that the world is simultaneously smaller and grander than we ever imagined. That a bus station at 5 AM in Paraguay can teach you more about patience than any meditation retreat. That the best meals come from strangers who insist you sit down. That love, tested by 365 days of constant togetherness, only grows stronger.</p>
<p class="closing-text">Karen said yes to my proposal at the base of a waterfall in Venezuela. She said yes to every crazy bus ride, every questionable hotel room, every "let's just see what happens" detour. And the world said yes back to us &mdash; with Iguazu Falls at sunrise, Angel Falls through clearing clouds, the mosaics of Hagia Sophia catching afternoon light, and a mokoro gliding silently through the Okavango Delta.</p>
<p class="journal-closing">"We came away wanting to hold each other's hand a little more often and a little bit longer."</p>
<div class="final-ornament">&#10047; &bull; &#10047; &bull; &#10047;</div>
</div>
</section>
<!-- ====== FOOTER ====== -->
<footer>
<p>&copy; WorldWander 1998&ndash;2026 &bull; Karen &amp; Scooter &bull; <a href="#hero">Back to top</a></p>
</footer>
<!-- ====== LIGHTBOX ====== -->
<div class="lightbox" id="lightbox">
<button class="lightbox-close" id="lightboxClose">&times;</button>
<button class="lightbox-nav lightbox-prev" id="lightboxPrev">&#10094;</button>
<button class="lightbox-nav lightbox-next" id="lightboxNext">&#10095;</button>
<img id="lightboxImg" src="" alt="">
<div class="lightbox-caption" id="lightboxCaption"></div>
<button class="lightbox-toggle-original" id="lightboxToggleOriginal">View Original</button>
</div>
<!-- ====== SOUND TOGGLE ====== -->
<button class="sound-toggle" id="soundToggle" title="Toggle ambient sound">&#9835;</button>
<script>
/* ============================================================
DATA: All images and their AI-generated captions
============================================================ */
const AI_CAPTIONS = {
"argentina_arfall.jpg": "The thundering cascades of Iguazu Falls, viewed from the Argentine side, with lush subtropical forest framing the massive curtain of water",
"argentina_arfall1.jpg": "Another angle of Iguazu Falls showing the sheer power of the water plunging into the gorge below",
"argentina_barboat.jpg": "A boat on the waters near Bariloche in Argentina's Lake District, surrounded by Patagonian scenery",
"argentina_barchurch.jpg": "A church in the Bariloche area, with its distinctive Alpine-influenced architecture",
"argentina_barlake.jpg": "The pristine glacial lake near Bariloche, reflecting the Andes mountains",
"argentina_bird1.jpg": "A grey crowned crane at the Iguazu bird park, its golden crest fanning out",
"argentina_brazil_iguazu.jpg": "Iguazu Falls from the Brazilian side, offering a panoramic view of the Devil's Throat",
"argentina_buenes.jpg": "Driving into Buenos Aires on a hazy morning, the city skyline emerging",
"argentina_buenos_aires.jpg": "The streets of Buenos Aires, Argentina's vibrant capital city",
"argentina_dogwalk.jpg": "A leisurely dog walk through one of Buenos Aires' tree-lined neighborhoods",
"argentina_falls_walkway.jpg": "Walking the metal catwalk trails above the rushing waters at Iguazu Falls",
"argentina_government.jpg": "Plaza de Mayo in Buenos Aires with the Casa Rosada",
"argentina_iguazu_falls.jpg": "The iconic panorama of Iguazu Falls spanning the Argentina-Brazil border",
"argentina_macaw.jpg": "A blue-and-gold macaw at the Iguazu bird sanctuary, vivid plumage catching the light",
"argentina_park.jpg": "A peaceful park scene in Buenos Aires",
"argentina_tango.jpg": "Street tango dancers performing on the cobblestones of Buenos Aires",
"argentina_tango1.jpg": "Another moment from the Buenos Aires street tango, the woman's red dress swirling",
"argentina_telecom.jpg": "The Telecom building in Buenos Aires",
"argentina_toucan.jpg": "A toco toucan perched on a wooden railing near Iguazu, its enormous orange beak impossibly vivid",
"argentina_yestreaklaur.jpg": "A candid moment from the Argentina leg of the journey",
"bolivia_catamaran.jpg": "A traditional reed catamaran on Lake Titicaca, the world's highest navigable lake",
"bolivia_copacabana.jpg": "The town of Copacabana on the shores of Lake Titicaca",
"bolivia_lapaz.jpg": "The sprawling city of La Paz filling a canyon in the Altiplano",
"bolivia_sailing.jpg": "Sailing across the deep blue waters of Lake Titicaca",
"bolivia_sun_island.jpg": "Isla del Sol on Lake Titicaca, sacred to the Inca civilization",
"bolivia_sunisland.jpg": "The terraced hillsides of Isla del Sol rising from azure waters",
"botswana_delta_camp.jpg": "Our camp in the Okavango Delta, tents pitched under African trees",
"botswana_Deltacamp.jpg": "The bush camp in Botswana's Okavango Delta, far from civilization",
"botswana_Deltatruck.jpg": "The overland truck through the bush roads of the Okavango Delta",
"botswana_Elebullwatch.jpg": "A bull elephant watching us cautiously, ears spread wide",
"botswana_Elecharge.jpg": "An elephant striding through the grasslands of the Okavango Delta",
"botswana_elephant_charge.jpg": "A tense moment as an elephant moves toward our position",
"botswana_elephant_fire.jpg": "An elephant silhouetted against a bush fire in the Okavango Delta",
"botswana_Elerun.jpg": "An elephant on the move, dust rising from its massive footsteps",
"botswana_Elestop.jpg": "An elephant pausing to assess us before deciding to charge or retreat",
"botswana_hippo.jpg": "A hippo barely visible in the tall reeds at dusk",
"botswana_Hippobutt.jpg": "The rear end of a hippo disappearing into the tall grass",
"botswana_Inatree.jpg": "Wildlife spotted in a tree in the Okavango Delta",
"botswana_Lostshoes.jpg": "The infamous lost shoes incident in the Botswana bush",
"botswana_Ltksboat.jpg": "A boat navigating through the lily-covered channels",
"botswana_mokoro_ride.jpg": "Gliding through the Okavango Delta in a traditional mokoro dugout canoe",
"botswana_Mokorosride.jpg": "Loading up mokoro canoes in the papyrus-lined channels",
"botswana_Motorospole.jpg": "A mokoro guide using a long pole through shallow waters",
"botswana_nata_camp.jpg": "Camping near Nata on the edge of the Makgadikgadi salt pans",
"botswana_Natacamp.jpg": "Our campsite at Nata, under the vast African sky",
"botswana_Okatracking.jpg": "Tracking wildlife on foot through the Okavango Delta",
"botswana_Shoesrecovered.jpg": "The shoes recovered! A happy ending to the bush adventure",
"botswana_tornado.jpg": "A dust devil spinning across the dry Botswana plains",
"botswana_tracking.jpg": "Following animal tracks through the bush",
"cambodia_angkor_wat.jpg": "The majestic silhouette of Angkor Wat, the largest religious monument in the world",
"cambodia_angkor3.jpg": "Ancient stone towers of Angkor, draped in centuries of history",
"cambodia_buddha.jpg": "A serene Buddha statue at an Angkor temple complex",
"cambodia_scene.jpg": "A street scene from Cambodia, daily life near the temples",
"cambodia_wat.jpg": "The intricate stone carvings and towers of Angkor Wat's inner sanctuary",
"chile_andes.jpg": "The snow-capped peaks of the Andes mountains over the Chilean landscape",
"chile_camp.jpg": "Camping in the Chilean wilderness, dwarfed by surrounding mountains",
"chile_cattle.jpg": "Cattle herded along a rural Chilean road with the Andes as backdrop",
"chile_fish_market.jpg": "The bustling fish market in a Chilean coastal town",
"chile_flame.jpg": "A campfire blazing under the Chilean night sky",
"curacao_bridge.jpg": "The Queen Emma pontoon bridge in Willemstad, Curacao",
"curacao_cruise.jpg": "A cruise ship in the colorful harbor of Willemstad",
"curacao_market.jpg": "The floating market in Willemstad",
"curacao_proof.jpg": "Sampling the famous Blue Curacao liqueur at its birthplace",
"egypt_abusimbel.jpg": "The colossal facade of Abu Simbel carved into the cliff face",
"egypt_abusimbel1.jpg": "The four seated colossi of Ramesses II guarding Abu Simbel, each 66 feet tall",
"egypt_alexandria.jpg": "The Mediterranean waterfront of Alexandria",
"egypt_aswanfeluka.jpg": "A traditional felucca sailboat gliding on the Nile at Aswan",
"egypt_caironile.jpg": "The Nile River winding through Cairo",
"egypt_camelpyramid.jpg": "A camel resting near the Great Pyramids of Giza",
"egypt_edfu_temple.jpg": "The Temple of Edfu, dedicated to the falcon god Horus",
"egypt_pyramid.jpg": "The Great Pyramid of Giza, the last surviving Wonder of the Ancient World",
"egypt_sinai.jpg": "The rugged mountains of the Sinai Peninsula",
"greece_beachdog.jpg": "A beach dog lounging on a Greek island shore",
"greece_beachsleep.jpg": "Sleeping on the beach in Greece, backpacker life at its finest",
"greece_campbags.jpg": "Our backpacks at a Greek campsite, everything we owned for a year",
"greece_campbags_small.jpg": "Camping gear and backpacks at a Greek island campsite",
"greece_campers.jpg": "Fellow travelers on a Greek island, sharing stories and cheap wine",
"greece_campers_small.jpg": "The camping community on a Greek island beach",
"greece_colossus_color.jpg": "An artistic rendering of the Colossus of Rhodes",
"greece_coriethon.jpg": "The ancient ruins at Corinth",
"india_amber2.jpg": "The ornate walls of Amber Fort in Jaipur, Rajasthan",
"india_amber3.jpg": "Inside Amber Fort, intricate mirror work and carved marble",
"india_amberft.jpg": "The imposing facade of Amber Fort rising from the hillside",
"india_ambermonkey.jpg": "A monkey lounging on the ancient walls of Amber Fort",
"jordan_deadsea.jpg": "Floating effortlessly in the Dead Sea, the lowest point on Earth",
"jordan_jerash.jpg": "The remarkably preserved Roman ruins at Jerash",
"jordan_mosaic.jpg": "An ancient mosaic, each tiny tile placed by hand centuries ago",
"jordan_mountnebo.jpg": "The view from Mount Nebo, where Moses saw the Promised Land",
"jordan_petra_columns.jpg": "The carved stone columns deep inside Petra's ancient city",
"jordan_petra_inside.jpg": "Exploring the interior chambers of Petra, rose-red walls towering",
"jordan_petra_main.jpg": "The entrance to Petra through the narrow Siq canyon",
"jordan_petra_treasury.jpg": "Al-Khazneh, the Treasury at Petra, carved into rose-red sandstone",
"namibia_4wkaren.jpg": "Karen navigating a 4x4 through rugged Namibian terrain",
"namibia_rpaint3.jpg": "Ancient rock paintings by the San people",
"namibia_rpaint4.jpg": "Prehistoric rock art depicting animals and human figures",
"namibia_treeshower.jpg": "An improvised bush shower in the Namibian wilderness",
"namibia_whichway.jpg": "A fork in the dirt road somewhere in Namibia",
"namibia_whitetree.jpg": "A bleached white dead tree stark against the Namibian landscape",
"namibia_zebrapair.jpg": "A pair of zebras in Etosha National Park",
"nepal_abcmap.jpg": "A trail map for the Annapurna Base Camp trek",
"nepal_abcmap_small.jpg": "The Annapurna Base Camp trekking route map",
"nepal_abcmount.jpg": "A snow-covered Himalayan peak from the Annapurna trek",
"nepal_artsamples.jpg": "Traditional Nepalese thangka paintings",
"nepal_artsamples_small.jpg": "Nepalese traditional artwork on display",
"nepal_budaair.jpg": "Buddha Air, Nepal's airline with a spectacular approach",
"nepal_cobra.jpg": "A cobra displayed by a snake charmer in Kathmandu",
"nepal_cobra_small.jpg": "A snake charmer's cobra in Nepal",
"nepal_cobracharmer.jpg": "A Nepalese snake charmer performing in Durbar Square",
"nepal_cobracharmer_small.jpg": "The snake charmer at work in Kathmandu",
"singapore_computermall.jpg": "One of Singapore's massive electronics malls",
"singapore_dragonboat.jpg": "Dragon boat racing in Singapore's harbor",
"singapore_hooters.jpg": "Globalization in action, Singapore",
"singapore_orchard.jpg": "Orchard Road, Singapore's famous shopping boulevard",
"singapore_singaporecustoms.jpg": "Singapore customs, famously efficient and strict",
"singapore_singblues.jpg": "The Singapore Blues",
"singapore_singbuild1.jpg": "Singapore's stunning modern skyline",
"singapore_singbuild2.jpg": "Singapore's impressive architectural skyline along the waterfront",
"singapore_singchurch.jpg": "A colonial-era church amid Singapore's gleaming skyscrapers",
"singapore_singharbor.jpg": "Singapore's busy harbor, one of the world's busiest ports",
"southafrica_baboons.jpg": "Baboons at Cape Point, bold and unafraid of tourists",
"southafrica_capepoint.jpg": "The dramatic cliffs of Cape Point",
"southafrica_capesunset.jpg": "A fiery sunset over the Cape of Good Hope",
"southafrica_capetown.jpg": "Cape Town spread beneath Table Mountain",
"southafrica_elephant.jpg": "An elephant on safari in South Africa",
"southafrica_giraffe.jpg": "A giraffe browsing the treetops on safari",
"southafrica_penguins.jpg": "African penguins waddling along Boulders Beach",
"spain_alhambra.jpg": "The towers of the Alhambra fortress in Granada",
"spain_barcelona.jpg": "The vibrant streets of Barcelona",
"spain_elescorial.jpg": "El Escorial, the royal monastery near Madrid",
"spain_flamenco.jpg": "A passionate flamenco performance in Seville",
"spain_madrid_palace.jpg": "The Royal Palace of Madrid",
"spain_ronda_bridge.jpg": "The Puente Nuevo bridge spanning El Tajo gorge in Ronda",
"spain_segovia_aqueduct.jpg": "The Roman aqueduct of Segovia, 2,000 years old",
"spain_seville.jpg": "The city of Seville, heart of flamenco culture",
"thailand_ansac.jpg": "An ANZAC memorial in Thailand",
"thailand_ansac_small.jpg": "ANZAC memorial in Thailand",
"thailand_aonang.jpg": "The stunning beach at Ao Nang in Krabi with limestone karsts",
"thailand_aonang_small.jpg": "Ao Nang beach, Krabi, Thailand",
"thailand_birdcave.jpg": "A bird's nest cave in southern Thailand",
"thailand_birdcave_small.jpg": "Bird nest cave, southern Thailand",
"thailand_birdcave2.jpg": "Inside the bird cave, swiftlet nesting colonies on the ceiling",
"thailand_birdcave2_small.jpg": "Interior of the bird nest cave",
"thailand_dinopark.jpg": "The quirky Dino Park mini-golf in Phuket",
"thailand_dinopark_small.jpg": "Dino Park, Phuket",
"thailandn_bamboo1.jpg": "Bamboo forest trekking in northern Thailand",
"thailandn_bamboo1_small.jpg": "Bamboo forest, northern Thailand",
"thailandn_budacm.jpg": "A golden Buddha statue at a Chiang Mai temple",
"thailandn_budacm_small.jpg": "Buddha at Chiang Mai temple",
"thailandn_budagf.jpg": "An ornate Buddha figure gleaming in tropical light",
"turkey_bluemosque_dome.jpg": "The soaring interior dome of Istanbul's Blue Mosque",
"turkey_bodrum.jpg": "The whitewashed buildings of Bodrum on the Aegean Sea",
"turkey_cappadocia_valley.jpg": "The surreal fairy chimney landscape of Cappadocia",
"turkey_cappadocia.jpg": "The otherworldly cave dwellings of Cappadocia",
"turkey_cavehotel.jpg": "Our cave hotel room in Cappadocia",
"turkey_chora_mosaic.jpg": "The stunning Byzantine mosaics of the Chora Church",
"turkey_ephesus_library.jpg": "The Library of Celsus at Ephesus, a reconstructed two-story facade",
"turkey_underground_city.jpg": "Deep inside one of Cappadocia's underground cities",
"venezuela_angel_falls.jpg": "Angel Falls plunging 3,212 feet from Auyan-tepui, the world's highest waterfall",
"venezuela_arameru.jpg": "A flat-topped tepui rising from the Venezuelan jungle",
"venezuela_falls_boat.jpg": "Approaching Angel Falls by boat through the jungle",
"venezuela_tepui.jpg": "A massive tepui looming over the Gran Sabana",
"venezuela_two_falls.jpg": "Two waterfalls cascading down a tepui in Canaima National Park",
"venezuela_wuytepui.jpg": "The sheer cliff face of a tepui, clouds wrapping around its summit",
"zimbabwe_bridgezambazi.jpg": "The bridge over the Zambezi River near Victoria Falls",
"zimbabwe_bridgezambazi_small.jpg": "Zambezi bridge crossing",
"zimbabwe_bulawayocamp.jpg": "Camping in Bulawayo, Zimbabwe's second city",
"zimbabwe_bulawayocamp_small.jpg": "Our Bulawayo campsite",
"zimbabwe_bulawayocamp1.jpg": "Another view of the Bulawayo camping area",
"zimbabwe_bulawayocamp1_small.jpg": "Bulawayo camp scene",
"zimbabwe_curioshops.jpg": "Curio shops selling handcrafted souvenirs",
"zimbabwe_curioshops_small.jpg": "Roadside curio shops, Zimbabwe",
"zimbabwe_daverow.jpg": "Fellow traveler Dave on the road through Zimbabwe",
"zimbabwe_daverow_small.jpg": "Dave traveling through Zimbabwe",
"zimbabwe_emptybus.jpg": "An empty bus on a Zimbabwe highway",
"zimbabwe_greyhound.jpg": "The Greyhound bus through Zimbabwe",
"zimbabwe_gzimgate.jpg": "The entrance to Great Zimbabwe",
"zimbabwe_gzimhotel.jpg": "Lodging near the Great Zimbabwe ruins",
"zimbabwe_gziminside.jpg": "Inside the walls of Great Zimbabwe",
"zimbabwe_gzimwalk.jpg": "Walking through the Great Zimbabwe archaeological site",
"zimbabwe_gzimwall.jpg": "The iconic dry-stone walls of Great Zimbabwe",
"zimbabwe_gzimwall1.jpg": "The walls of Great Zimbabwe, standing strong after 800 years",
"zimbabwe_jamesongrave.jpg": "The grave of Leander Starr Jameson in the Matobo Hills",
"zimbabwe_kteeth.jpg": "An impressive set of teeth in the wild",
"zimbabwe_kwimpy.jpg": "Eating at Wimpy, the backpacker staple",
"zimbabwe_motomborock1.jpg": "The dramatic granite boulders of the Matobo Hills",
"zimbabwe_pizzainn.jpg": "Pizza Inn in Zimbabwe, familiar comfort food on the road",
"zimbabwe_purpletree.jpg": "A jacaranda tree in full purple bloom",
"zimbabwe_rhodesgrave.jpg": "Cecil Rhodes' grave atop World's View in the Matobo Hills",
"zimbabwe_sable.jpg": "A sable antelope, one of Africa's most magnificent creatures",
"zimbabwe_steamtrain.jpg": "A vintage steam train on colonial-era railway lines",
"zimbabwe_swater.jpg": "A water scene along the Zambezi"
};
const IMAGE_CAPTIONS = AI_CAPTIONS;
/* Before/After image pairs */
const BA_IMAGES = [
{ file: 'argentina_toucan.jpg', title: 'Toucan, Iguazu Bird Park' },
{ file: 'jordan_petra_treasury.jpg', title: 'The Treasury at Petra' },
{ file: 'egypt_abusimbel1.jpg', title: 'Abu Simbel Temple' },
{ file: 'cambodia_angkor_wat.jpg', title: 'Angkor Wat at Dawn' },
{ file: 'southafrica_penguins.jpg', title: 'Penguins, Boulders Beach' },
{ file: 'venezuela_angel_falls.jpg', title: 'Angel Falls' }
];
/* Images that have originals available */
const HAS_ORIGINAL = new Set(BA_IMAGES.map(b => b.file));
/* Pull quotes extracted from the travel stories */
const PULL_QUOTES = {
argentina: { text: "The International Bus Terminal in Ciudad Del Este is not something to put on your list of places to visit especially at 4:30 AM.", attr: "Argentina, March 1998" },
venezuela: { text: "I proposed to Karen in front of Hacha Falls in what I thought at the time was the prettiest and most romantic spot on earth. Karen was quick to respond with a very happy YES.", attr: "Venezuela, February 1998" },
jordan: { text: "We got up our courage and headed for Jordan. We were too close to miss the world wonder in Petra. It didn't help that the United States was bombing Iraq only four hours away.", attr: "Jordan, December 1998" },
egypt: { text: "Looking down the road and seeing the Great Pyramids of Giza on the horizon is surreal, you just can't quite believe that the image you have seen so many times is real.", attr: "Egypt, December 1998" },
botswana: { text: "When we see the guides bolt into a full run we knew what to do: Run! It looks like we are all extras in a Godzilla movie.", attr: "Botswana, September 1998" },
zimbabwe: { text: "In the middle of the Zambezi River we were now married in the Catholic Church. When Father Thomas asked if anyone objected, the hippos burst out in loud approval.", attr: "Zimbabwe, September 1998" },
nepal: { text: "The Kingdom of Nepal is one of the world's poorest countries by financial standards but we soon would learn that it is filled with riches beyond your wildest imagination.", attr: "Nepal, June 1998" },
greece: { text: "We dined on fresh bread, Tzatziki, Greek salad, watermelon and sipped retsina in an open air cafe. At that moment, life was good.", attr: "Greece, July 1998" },
spain: { text: "You could see grandparents and grandkids dancing in the street and it was after midnight. We had to work on getting onto Spanish time ASAP.", attr: "Spain, October 1998" },
curacao: { text: "Once the 100% status is reached, there is hooting and hollering from the crowd. It is an awesome sight \u2014 suddenly cold, dark, and you could see two planets in the sky.", attr: "Curacao, February 1998" },
cambodia: { text: "Angkor Temples are one of the seven modern wonders of the world. They are over 1,000 years old and too big and intact to be called ruins.", attr: "Cambodia, May 1998" },
india: { text: "If you look to the left you will see something so amazing that you will never forget it. If you look to the right you will see something so disgusting you will never forget it.", attr: "India, June 1998" },
turkey: { text: "The Grand Bazaar consists of 65 streets jammed with 4,000 shops making it the largest mall in the world.", attr: "Turkey, July 1998" },
chile: { text: "Sitting in the back corner of each little piece of paradise was a fire pit, running water and a covered picnic table with electricity. Things had finally started to click.", attr: "Chile, March 1998" },
southafrica: { text: "We all had too many desserts. Somehow we managed to walk out the restaurant just far enough until one-by-one we began collapsing on the perfectly manicured lawn.", attr: "South Africa, August 1998" },
bolivia: { text: "La Paz is a very high city, 12,000 feet, and is home to many of the world's highest records. The city itself is in a large hole surrounded by hills and is quite striking.", attr: "Bolivia, March 1998" }
};
/* Country metadata */
const COUNTRIES = {
venezuela: { name: 'Venezuela', tagline: 'Angel Falls & a Proposal', dates: 'February 1998', emoji: '\u{1F1FB}\u{1F1EA}', gradient: '' },
curacao: { name: 'Curacao', tagline: 'Solar Eclipse in the Dutch Antilles', dates: 'February 1998', emoji: '\u{1F1E8}\u{1F1FC}', gradient: '' },
argentina: { name: 'Argentina', tagline: 'Iguazu Falls, Buenos Aires & Tango', dates: 'March 1998', emoji: '\u{1F1E6}\u{1F1F7}', gradient: '' },
bolivia: { name: 'Bolivia', tagline: 'La Paz & Lake Titicaca', dates: 'March 1998', emoji: '\u{1F1E7}\u{1F1F4}', gradient: '' },
chile: { name: 'Chile', tagline: 'The Andes, Lakes & the Pan-American Highway', dates: 'March 1998', emoji: '\u{1F1E8}\u{1F1F1}', gradient: '' },
ecuador: { name: 'Ecuador & Galapagos', tagline: 'Taco Bell, the Equator & Darwin\'s Islands', dates: 'March 1998', emoji: '\u{1F1EA}\u{1F1E8}', gradient: 'linear-gradient(135deg, #2d5a3d, #1a3a4a, #4a6741)' },
egypt: { name: 'Egypt', tagline: 'Pyramids, the Nile & Abu Simbel', dates: 'December 1998', emoji: '\u{1F1EA}\u{1F1EC}', gradient: '' },
jordan: { name: 'Jordan', tagline: 'Petra, the Dead Sea & Ramadan', dates: 'December 1998', emoji: '\u{1F1EF}\u{1F1F4}', gradient: '' },
turkey: { name: 'Turkey', tagline: 'Istanbul, Ephesus & Cappadocia', dates: 'July 1998', emoji: '\u{1F1F9}\u{1F1F7}', gradient: '' },
greece: { name: 'Greece', tagline: 'Athens, the Islands & the Acropolis', dates: 'July 1998', emoji: '\u{1F1EC}\u{1F1F7}', gradient: '' },
spain: { name: 'Spain', tagline: 'Barcelona, Granada, Seville & Ronda', dates: 'October 1998', emoji: '\u{1F1EA}\u{1F1F8}', gradient: '' },
southafrica: { name: 'South Africa', tagline: 'Cape Town, Safari & Garden Route', dates: 'August 1998', emoji: '\u{1F1FF}\u{1F1E6}', gradient: '' },
botswana: { name: 'Botswana', tagline: 'Okavango Delta by Mokoro', dates: 'September 1998', emoji: '\u{1F1E7}\u{1F1FC}', gradient: '' },
zimbabwe: { name: 'Zimbabwe', tagline: 'Victoria Falls & the Wedding', dates: 'September 1998', emoji: '\u{1F1FF}\u{1F1FC}', gradient: 'linear-gradient(135deg, #4A6741, #8B4513, #2C1810)' },
namibia: { name: 'Namibia', tagline: 'Dunes, Rock Art & Etosha', dates: 'September 1998', emoji: '\u{1F1F3}\u{1F1E6}', gradient: '' },
cambodia: { name: 'Cambodia', tagline: 'Angkor Wat & the Killing Fields', dates: 'May 1998', emoji: '\u{1F1F0}\u{1F1ED}', gradient: '' },
nepal: { name: 'Nepal', tagline: 'Kathmandu & Himalayan Trekking', dates: 'June 1998', emoji: '\u{1F1F3}\u{1F1F5}', gradient: 'linear-gradient(135deg, #3d4a5a, #5a6741, #2C1810)' },
india: { name: 'India', tagline: 'Delhi, the Taj & Bangalore', dates: 'June 1998', emoji: '\u{1F1EE}\u{1F1F3}', gradient: 'linear-gradient(135deg, #8B4513, #D4A574, #4A6741)' },
thailand: { name: 'Thailand', tagline: 'Bangkok, Chiang Mai & the Islands', dates: 'May 1998', emoji: '\u{1F1F9}\u{1F1ED}', gradient: 'linear-gradient(135deg, #4A6741, #D4A574, #2C1810)' },
singapore: { name: 'Singapore', tagline: 'The Immaculate City-State', dates: 'May 1998', emoji: '\u{1F1F8}\u{1F1EC}', gradient: 'linear-gradient(135deg, #2C1810, #4A6741, #8B4513)' }
};
const CHAPTER_ORDER = [
'venezuela', 'curacao', 'argentina', 'bolivia', 'chile', 'ecuador',
'cambodia', 'nepal', 'india', 'thailand', 'singapore',
'greece', 'turkey', 'egypt', 'jordan',
'spain', 'southafrica', 'botswana', 'zimbabwe', 'namibia'
];
const CHAPTER_HEROES = {
venezuela: 'venezuela_angel_falls.jpg',
curacao: 'curacao_bridge.jpg',
argentina: 'argentina_iguazu_falls.jpg',
bolivia: 'bolivia_lapaz.jpg',
chile: 'chile_andes.jpg',
egypt: 'egypt_camelpyramid.jpg',
jordan: 'jordan_petra_treasury.jpg',
turkey: 'turkey_ephesus_library.jpg',
greece: 'greece_colossus_color.jpg',
spain: 'spain_alhambra.jpg',
southafrica: 'southafrica_capetown.jpg',
botswana: 'botswana_mokoro_ride.jpg',
cambodia: 'cambodia_angkor_wat.jpg',
namibia: 'namibia_zebrapair.jpg',
zimbabwe: 'zimbabwe_bridgezambazi.jpg'
};
const CHAPTER_STORIES = {
venezuela: `<p class="story-text">We arrived into the international Caracas Airport at 2:00 PM. Customs was very easy and our bags came up as we walked up to the baggage claim area. We exchanged $200 US and used the Tourist Information counter to make a hotel reservation. The people working the Tourist counter did not speak English so communicating was by trial and error.</p>
<p class="story-text">We are going to Canaima National Park, the sixth largest in the world, tomorrow so we set out to sign up for our tour package. We loaded up supplies and set out for Angel Falls via the Rio Carrao river, not sure if we would make it. The river was very low but our guides Jose and Jeime are masters at their work. The boat is carved out of a single tree and is over thirty feet long and powered by a Yamaha 48hp outboard motor. One guide sits on the front of the boat with a very large paddle for steering and signals to the other guide the location of oncoming rocks.</p>
<p class="story-date">Tuesday, February 17, 1998</p>
<p class="story-text">It rained throughout the night and when we got up the next morning it was very cloudy and we could not see Angel Falls because of all the clouds. Marlon, Karen and I set out on our 30 minute hike to the falls, hoping it would clear. When we reached our first vantage point the sun came out and the clouds opened and before us stood Angel Falls. Marlon could not believe our luck and asked us if we are usually this lucky and I told him that we have been very lucky in the past.</p>
<p class="story-text">The Tepuis on the way to Angel Falls are without compare to anything we have seen. Angel Falls is located at the top of a Tepuy and the water falls 980 meters or 3,280 feet which makes it the tallest waterfall in the world.</p>
<p class="story-text">For those of you reading this closely, I proposed to Karen in front of Hacha Falls on Sunday, February 15th, in what I thought at the time was the prettiest and most romantic spot on earth. Karen was surprised to hear those words come out of my mouth and she was quick to respond with a very happy YES. Our future looks very bright together filled with happiness, love and a little bit of luck thrown in when you least expect it.</p>`,
curacao: `<p class="story-text">Curacao is a Dutch Island in the Netherland Antilles and is the hub for all of ALM Airlines. We had an overnight layover scheduled and found out the island was in the 100% zone for a solar eclipse. This is a big event and people were visiting from all over the world. Some cruise packages had been booked for a couple of years! We just got lucky and asked to delay our flight by one more day.</p>
<p class="story-text">The best place to observe the eclipse was on the west side of the island called Westpoint. At this location the full eclipse would last 10 seconds longer, so of course we were going there and by city bus. Our quest for the real groupies took us on a hike through desert and a steep climb to the highest point at Westpunt. Once the 100% status is reached, there is hooting and hollering from the crowd. It is an awesome sight &mdash; suddenly cold, dark, and you could see two planets in the sky. Three minutes and twenty-three seconds later, it is over. It is impossible for video or cameras to capture the beauty and mystique of the ring of diamonds. You have to be there to understand.</p>`,
argentina: `<p class="story-text">The area of Iguazu falls is bordered by Argentina, Brazil and Paraguay which provides for some interesting multiple country visits in a single day. The International Bus Terminal in Ciudad Del Este, Paraguay is not something to put on your list of places to visit especially at 4:30 AM because that is when we arrived from Asuncion, Paraguay.</p>
<p class="story-date">March 18</p>
<p class="story-text">The travel books all recommended going to the Brazil side first to take in the true magnificence of the falls. Karen and I walked up and could not believe the size and magnitude of the falls. It was truly impressive. Every corner and observation point yielded an even larger view of the falls. The end of the walkway takes you into the middle of the river and at the edge of a large horseshoe shaped fall.</p>
<p class="story-text">The Argentinean side of the falls is definitely setup for tourists. They have put a lot of effort into building very nice walkways over the various waterfalls and down to the river's edge. It is a surreal feeling to stand a foot above the drop off point of a roaring waterfall. I don't think Disney could have built a nicer waterfall for tourists to enjoy.</p>
<p class="story-date">March 20 &mdash; Buenos Aires</p>
<p class="story-text">Argentina is a large country but over 80 percent of the population lives in Buenos Aires. Buenos Aires had prospered in the early 1900's as a port town and a major beef and agriculture exporter to Europe. The waterway in Puerto Moreno consisted of at least four miles of beautifully restored four story brick buildings. Each building in its day had been part of a large warehouse district. Now they served as the home to hundreds of restaurants, businesses and apartments.</p>
<p class="story-text">We then asked the cab driver to take us to the Tango district. The Tango district is known for the brightly colored buildings and all kinds of street vendors and locals dancing the Tango for a few extra bucks. The Argentina men developed the very complicated dance called the Tango, with lots of body parts touching and fancy footwork. I came away from Argentina wanting to hold Karen's hand a little more often and a little bit longer.</p>
<p class="story-date">March 23 &mdash; Bariloche</p>
<p class="story-text">Bariloche is an Alpine Ski town both tourist and business oriented. It is very quaint and situated on a large lake surrounded by mountains. The town is famous for its chocolate stores and every chocolate kitchen you walked by almost knocked you out from the overwhelming smell of fresh chocolate.</p>`,
bolivia: `<p class="story-text">The catamaran was modern and looked like a living room inside with lounge chairs and windows. A guide filled us in on the details of Lake Titicaca &mdash; the world's 11th largest, world's highest navigable, very deep, trout fishing, Inca ruins, etc. were the highlights. Basically, once you have seen 10 minutes of the lake you have experienced the next 5 hours of the cruise.</p>
<p class="story-text">La Paz is a very high city, 12,000 feet, and is home to many of the world's highest records. The city itself is in a large hole surrounded by hills and is quite striking. It is also safe to walk around the city day or night. Once again, we were arriving to a town with no lodging preferences so we followed another couple to the Gloria Hotel in downtown La Paz.</p>
<p class="story-text">We seem to have to learn our travel lessons repeatedly: secure a taxi price before getting in the car, look at the hotel room before paying for it, do not believe everything your guidebook tells you, and make sure you know the price of your phone calls.</p>`,
chile: `<p class="story-text">After one relaxing day in Bariloche, Argentina we are on the road, or rather on the bus to Osorno, Chile. The drive through the Lake Region of Argentina and over the mountains is beautiful even if we are sitting next to the bus toilet. The livelihood of Chile is its fruit and vegetable exports, so they take very seriously any little bugs entering their country.</p>
<p class="story-text">Karen was determined not to give our business to the siesta-minded Budget but we discovered that they had the best price in town. Five minutes later Karen and I had freedom for the first time in a month. We could go anywhere and do anything at our own leisure.</p>
<p class="story-text">We set out for Fruitillar, Chile only 45 minutes south and located on a beautiful lake with a snow-covered volcano in the background. The campground had thirty spots surrounded by huge trees. Each camp site was bordered by nicely trimmed hedges and rose bushes in full bloom. Sitting in the back corner of each little piece of paradise was a fire pit, running water and a covered picnic table with electricity. Things had finally started to click in Chile.</p>
<p class="story-text">The city of Puerto Montt is the end of the Pan American highway. We navigated our way to the fish market and began a very interesting tour. This was the oddest fish market I had ever been to &mdash; Abalone, Sea Cucumbers, Giant Barnacles and some sort of fish/eel looking thing everywhere you looked.</p>`,
ecuador: `<p class="story-text">After seeing an amazing 100% Solar Eclipse we are on our way to Quito, Ecuador and the Galapagos. Our flight took us through Caracas, an unplanned stop in Bogota, Colombia &mdash; where we accidentally ended up on the streets without going through customs &mdash; and finally to Quito.</p>
<p class="story-text">The Galapagos Islands are located on the Equator, 970 kilometers off Ecuador. There are six larger islands and 12 small islands. Our flight was into Baltra, where we met Jorge our guide, and the rest of our travel companions. There were 16 of us, mostly between the ages of 25 and 40, from Switzerland, Germany, Australia, Japan, Norway, Canada, Denmark, England and us.</p>
<p class="story-text">We had not done it yet for a year: here before my very eyes appeared a mirage. We could not believe it, the last thing we thought we would see for a year &mdash; yes they have TACO BELL in Ecuador. Karen and I are both addicted to Taco Bell and eat it at home at least twice a week. Dinner was great and it lifted our spirits.</p>`,
egypt: `<p class="story-text">From a world wonder standpoint our decision to include Egypt on our travels was an easy one. Arriving via Martin Air from Paris to Cairo with nowhere to stay past midnight. No problem, we've done this before. There are five world wonders here: Pyramids at Giza, Lighthouse in Alexandria, Suez Canal, High Dam in Aswan and Abu Simbel.</p>
<p class="story-text">The road to the pyramids was built in the 1860's. The ancient Greeks considered the Great Pyramids to be one of the original seven wonders of the world. Looking down the road and seeing the Great Pyramids of Giza on the horizon is surreal, you just can't quite believe that the image you have seen so many times is real. Our taxi driver felt we should see the Pyramids at sunset by camel. The pyramids were incredible especially by camel back.</p>
<p class="story-text">Let's talk world wonders. Abu Simbel could have made the list twice &mdash; once for when it was first constructed into a mountainside and again for the moving and reconstruction. Huge stone figures of Ramses sitting on the banks of Lake Nasser. The High Dam, completed in 1971, has 12 turbines that create all of Egypt's electrical power. The Dam also created the world's largest artificial lake, Lake Nasser.</p>
<p class="story-text">The views along the Nile are incredible surreal vistas of deep blue water, rich green crops mixed with the varied colors of the desert cliffs and sands. Karnak Temple is the granddaddy of temples, the biggest and most impressive temple in Egypt. The site was constantly embellished over 1,500 years.</p>`,
jordan: `<p class="story-text">Jordan is a kingdom ruled by the much-loved and respected King Hussein and Queen Noor. It is a predominantly Muslim country, although many religions mix and mingle. Conveniently, English is the second language making it easy to converse with the locals.</p>
<p class="story-text">Currently, we're in the town of Wadi Musa, 5 km from today's destination, Petra, a formerly lost city. 8th century BC Edomites started this ancient town on the trade route, then taken over in the 6th century BC by the Nabateans who carved temples and monuments out of the cliffs. It was home to 20-30,000 people. You may think you have seen it before, because it was featured as the lost city in the movie Indiana Jones and the Last Crusade.</p>
<p class="story-text">One of the most spectacular views is right at the start. A 1.5 km walk through a narrow gorge with a magnificently carved mausoleum at the end made out of rose-colored sandstone. As the Brits would say, "brilliant." The rest of the day took us to the 7,000 seat Roman theater, many huge tombs, and up to the Ad-Deir, the monastery.</p>
<p class="story-text">Leaving the city of Amman, we continued North to Jerash, one of the most extensive Roman cities still in existence. The triumphal arch, hippodrome, oval plaza, 4,000 seat south theater, temple of Zeus &mdash; this city is impressive. It is said that 90% of Jerash is still not excavated.</p>`,
turkey: `<p class="story-text">When you say the word "Turkey" what pops into your mind? For the typical American the response would most definitely be Thanksgiving. Turkey is actually located on both the European and Asian continents and has been the center of religion and politics for thousands of years. After our visit to Turkey I realized that stereotypes and too little knowledge can be a really bad thing.</p>
<p class="story-text">The ancient city of Ephesus was a major trade port to the Far East. What remains today are probably the best-preserved ruins of the ancient world. One of the most interesting features was running water delivered by an underground plumbing system. The Library of Celsus, located in the main square, is the most completely restored structure at Ephesus &mdash; a complete two-story facade with statues and the classic Roman look.</p>
<p class="story-text">The city of Istanbul, once known as Constantinople, has survived as the capital of three major empires: Roman, Byzantine and Ottoman. Istanbul bridges Asia and Europe. The Hagia Sophia Church is one of the most magnificent buildings ever constructed, both central church to Christianity and the major inspiration for mosques of Islam. Across a large park is the Blue Mosque, the highlight of the Islamic religion in Istanbul.</p>
<p class="story-text">The Grand Bazaar consists of 65 streets jammed with 4,000 shops making it the largest mall in the world. It has lots of atmosphere with crowded arched passageways and smiling vendors that know every line in the book.</p>`,
greece: `<p class="story-text">Our departure from India could not come soon enough. We had just spent a long and challenging five months traveling through South America and Asia. We were ready for the comforts of Europe. Arriving in Athens was chaotic but we found the Hotel Astor with a view of the Acropolis.</p>
<p class="story-text">The Acropolis is an incredible sight sitting on a high plateau surrounded by the modern city of Athens. There are several monuments on the Acropolis: Propylaia, Temple of Athena Nike, the Erechtheion, Porch of the Maidens and of course the Parthenon. Just below the Parthenon is the Theater of Dionysus, where Sophocles, Aristophanes, and Euripides first presented their immortal works.</p>
<p class="story-text">Our cruise ship schedule took us through the stunning Greek islands: Delos and Mykonos, Santorini, Heraklion in Crete, and Rhodes. It was refreshing to have the international flavor of our fellow travelers &mdash; everyone eager to meet, swap stories and gather travel advice.</p>
<p class="story-text">We had lunch in the small square at the center of town. We dined on fresh bread, Tzatziki, Greek salad, watermelon and sipped retsina in an open air cafe. My idea of a perfect afternoon and at that moment, life was good.</p>`,
spain: `<p class="story-text">We have become increasingly casual about our travel plans. I think the Eurail Pass has given us a new, higher level of flexibility. From Avignon, France we jumped on the first train going to Barcelona. It was Saturday night at 10:30, perfect timing for Spain's late nights. We walked the waterfront to Ramblas, the main drag, with incredible crowds of people strolling up this huge boulevard.</p>
<p class="story-text">Coming out of the Sagrada Familia subway station is shocking, seeing this fantasy church looming over you. The designer, Antoni Gaudi, worked on this creation for 40 years. It is like no other building in the world. If Salvador Dali had to design a church, it would look like Sagrada Familia.</p>
<p class="story-text">Granada's Alhambra was spectacular, and Ronda's Parador had the perfect location on the edge of the cliff with beautiful rooms and a balcony overlooking the gorge. Paradors are Spain's government-sponsored inns, most often in renovated castles, palaces or monasteries.</p>
<p class="story-text">Unlike at home, all ages of people were mixing and mingling in Spain. The older generation are not at home after eating the "early bird special." You could see grandparents and grandkids dancing in the street and it was after midnight. We had to work on getting onto Spanish time ASAP because at 1 AM we were tired just when everyone else was getting started.</p>`,
southafrica: `<p class="story-text">The Garden Route along the southern coast is spectacular and each time we told a local our next destination their eyes lit up with joy. Cape Town is stunning &mdash; Table Mountain looms over the city, and Chapman's Peak drive has to be one of the world's greatest scenic drives.</p>
<p class="story-text">The Boulders is home for a couple hundred Jackass Penguins. They walk back and forth playing in the water, minding the very ugly baby penguins and posing for pictures with the tourists. This will be the closest you will ever get to wild penguins.</p>
<p class="story-text">The Cape of Good Hope has a baboon problem. They run in large packs and will grab anything left around. Their target was an Official Nature Reserve truck &mdash; they jumped up on the truck with baboon hysteria and began to assert who was boss around here.</p>
<p class="story-text">The Stellenbosch wine region represents a long tradition of wine making started by Dutch settlers in 1679. The Boschendal Winery's buffet lunch at 85 Rand was beyond excellent, the wine superb. We all had too many desserts. Somehow we managed to walk out the restaurant just far enough until one-by-one we began collapsing on the perfectly manicured lawn. This is more popularly referred to as a food coma.</p>`,
botswana: `<p class="story-text">We did it, on the road by 6 AM heading for the Botswana border. It was cold and dark but we were on our way. We were treated to the sight of three lions lying in the road &mdash; a lioness and two cubs &mdash; and also spotted elephants and a vibrant pink sunrise.</p>
<p class="story-text">Our main event in Botswana was to visit the Okavango Delta, the natural highlight of the country. It is a river of grass similar to Florida's Everglades with the addition of hippos, elephants, lions, giraffes and crocs. It is the world's largest inland delta and part of a 1,300 kilometer river.</p>
<p class="story-text">We spotted a large bull elephant hanging out in the shade. We were probably 100 feet from him and everyone was taking pictures. If I was a big elephant trying to take a nap and a bunch of tourists kept making noise, you know what I would do &mdash; Charge! When we see the guides bolt into a full run we knew what to do: Run! Scooter captured the spectacle on video. It looks like we are all extras in a Godzilla movie.</p>
<p class="story-text">Back at camp, Dizzy was cooking baked potatoes, salad, and boerwors. It was so dark that without the campfire or flashlight you couldn't see two inches in front of your face. We heard wild animal noises all night. I am glad we only spent one night in the Delta but it was worth it. I have seen so many Discovery Channel specials on the Okavango Delta and now I know what it is like first hand.</p>`,
zimbabwe: `<p class="story-text">Next stop is Victoria Falls, Zimbabwe. We were very excited about Victoria Falls because Karen and I were going to try and get married! After eight months on the road you can get a little dirty. We checked in to the Elephant Hills Hotel and Karen signed up for the works at the day spa.</p>
<p class="story-text">Wedding day arrived and we checked in early to the Vic Falls Hotel. Up until now, we had been camping for the last twenty-six days. Our new accommodations cost $378 a night. A short drive to the Zambezi River at the top of Victoria Falls and we boarded our boat to Palm Island.</p>
<p class="story-text">Karen arrived in the lobby a little past 4:00 PM and she was a stunning bride. The dress was a perfect fit and a style that made Karen sparkle. I wasn't nervous and couldn't wait to be married. Instead, I was smiling and thinking about the next 80 years.</p>
<p class="story-text">The magistrate performed the legal ceremony and just like that, Karen and I were married. Father Thomas conducted a beautiful ceremony and the exchanging of rings. In the middle of the Zambezi River we were now married in the Catholic Church. Our special guests: two elephants and a group of loud hippos. When Father Thomas asked if anyone objected, the hippos burst out in loud approval. Karen and I are truly blessed.</p>`,
namibia: `<p class="story-text">Namibia offered us a completely different African experience. Karen navigated the 4x4 through rugged terrain past ancient rock paintings by the San people and bleached white dead trees in landscapes that felt like another planet entirely.</p>
<p class="story-text">Etosha National Park delivered incredible wildlife encounters &mdash; zebra pairs creating natural optical illusions with their stripes, and the vast salt pans stretching to the horizon under the enormous African sky.</p>`,
cambodia: `<p class="story-text">Several years ago I read an article in Traveler Magazine about Angkor, some ruins in Asia and it looked totally exotic. Cambodia has had a long history that has seen many changes in the last 800 years. The main attraction for a tourist is Angkor Wat, considered a modern wonder of the world even though it is over 800 years old.</p>
<p class="story-text">Angkor Temples are one of the seven modern wonders of the world. They are located in northwest Cambodia outside Siem Reap. They are over 1,000 years old and too big and intact to be called ruins. The whole area is 400 square kilometers with 100 monuments including 24 major temples.</p>
<p class="story-text">Angkor Thom means "great city" &mdash; a walled city built in the 12th century holding Bayon temple and elephant terrace. It is huge and awesome looking with Buddha faces everywhere. A short drive away is Ta Prohm, a temple overgrown by tremendous old banyan tree roots. This temple is my favorite &mdash; wild and mystical looking.</p>
<p class="story-text">Angkor Wat is the largest temple in the world. It is unlike the other temples in that it faces west and was built as a Hindu temple. Its symmetrical towers are on the Cambodian flag. We spent the next two hours roaming around and climbing to the top for the view.</p>`,
nepal: `<p class="story-text">It was time to head to the hottest place on earth, New Delhi, India, and we were not looking forward to it. New Delhi did not disappoint with its combination of heat and pollution, so we decided to leave for Nepal the next day. The Kingdom of Nepal is one of the world's poorest countries by financial standards but we soon would learn that it is filled with riches beyond your wildest imagination.</p>
<p class="story-text">The streets of Thamel in Kathmandu are narrow and crowded with souvenir shops, restaurants, guest houses and people everywhere. We were really liking Nepal. The people are very relaxed, friendly and happy. It was fun just walking down the streets taking it all in. This was surprising because we were into our fourth month of travel and it was refreshing to be excited about a new place.</p>
<p class="story-text">Most trekking in Nepal is called a Tea House trek. The goal is to walk to the base of mountains that climb to 24,000 feet. Mt. Everest is the granddaddy of all mountains and it only takes 14 days to walk to it and another 14 days to walk back. Along the way you stay in local village houses setup for trekkers. Food and lodging can be had for less than five dollars a day.</p>`,
india: `<p class="story-text">"If you look to the left you will see something so amazing that you will never forget it. If you look to the right you will see something so disgusting you will never forget it." India is not for the first time traveler.</p>
<p class="story-text">India is reported to have a population of over 935 million people. Everywhere you go people are in the streets living, eating, going to work. We entered the Hotel York through a dark, dank staircase. Karen took a shower, cried some more because the big metal window-door wouldn't close. This was a $50 hotel in the main downtown district of New Delhi. Welcome to India!</p>
<p class="story-text">India was filled with incredible sights &mdash; India Gate, Lotus Temple, Red Fort, and the Taj Mahal. But everywhere we drove, people were living on the side of the road surrounded by trash heaps being eaten by cows. It was a viscous recycling process. We were in India during the summer, which means it is hot and no foreigners. I spent most of my time at the tourist sites posing with families for pictures. Someone would approach and hand me their baby &mdash; they had simply never seen a white person before.</p>`,
thailand: `<p class="story-text">Thailand was a welcome change from the intensity of India. The country has a warmth and hospitality that is hard to match anywhere in the world. Bangkok is a bustling metropolis &mdash; a city of contrasts between ancient temples and modern skyscrapers, street food vendors and luxury hotels.</p>
<p class="story-text">We traveled north to Chiang Mai, exploring hill tribe villages and ancient temples surrounded by lush jungle. The night markets were a sensory overload of spices, silk, and silver. The southern islands provided our much-needed beach break with crystal clear waters and white sand stretching to the horizon.</p>`,
singapore: `<p class="story-text">Singapore is the most immaculate city-state we have ever visited. After the chaos of Southeast Asia, arriving in Singapore felt like stepping into the future. Everything works, everything is clean, and everything has a rule. They even ship all their laundry to Malaysia for washing and drying &mdash; Singapore will have nothing to do with being dirty.</p>
<p class="story-text">The city is a fascinating mix of Chinese, Malay, and Indian cultures, with world-class food available on every corner for just a few dollars. We spent our days exploring hawker centers, botanical gardens, and the waterfront, preparing ourselves mentally for the next leg of our journey to India.</p>`
};
/* Ambient sound regions */
const SOUND_REGIONS = {
venezuela: 'south-america', curacao: 'south-america', argentina: 'south-america',
bolivia: 'south-america', chile: 'south-america', ecuador: 'south-america',
egypt: 'middle-east', jordan: 'middle-east', turkey: 'middle-east',
greece: 'europe', spain: 'europe',
southafrica: 'africa', botswana: 'africa', zimbabwe: 'africa', namibia: 'africa',
nepal: 'asia', india: 'asia', cambodia: 'asia', thailand: 'asia', singapore: 'asia'
};
function getCountryImages(country) {
return Object.keys(IMAGE_CAPTIONS).filter(f => {
const prefix = f.split('_')[0];
if (country === 'thailand') return prefix === 'thailand' || prefix === 'thailandn';
return prefix === country;
});
}
function getCountry(filename) {
const prefix = filename.split('_')[0];
if (prefix === 'thailandn') return 'thailand';
return prefix;
}
/* ============================================================
BUILD BEFORE/AFTER SLIDERS
============================================================ */
function buildBeforeAfter() {
const grid = document.getElementById('baGrid');
let html = '';
BA_IMAGES.forEach(item => {
html += `<div>
<div class="ba-slider reveal" data-ba>
<img class="ba-after" src="images/${item.file}" alt="AI Restored" loading="lazy">
<img class="ba-before" src="images/original/${item.file}" alt="Original" loading="lazy">
<div class="ba-handle"></div>
<span class="ba-label ba-label-left">Original 2000</span>
<span class="ba-label ba-label-right">AI Restored 2026</span>
</div>
<div class="ba-slider-title">${item.title}</div>
</div>`;
});
grid.innerHTML = html;
// Slider interaction
document.querySelectorAll('[data-ba]').forEach(slider => {
let dragging = false;
const updateSlider = (x) => {
const rect = slider.getBoundingClientRect();
let pct = ((x - rect.left) / rect.width) * 100;
pct = Math.max(2, Math.min(98, pct));
slider.querySelector('.ba-before').style.clipPath = `inset(0 ${100 - pct}% 0 0)`;
slider.querySelector('.ba-handle').style.left = pct + '%';
};
slider.addEventListener('mousedown', e => { dragging = true; updateSlider(e.clientX); e.preventDefault(); });
slider.addEventListener('touchstart', e => { dragging = true; updateSlider(e.touches[0].clientX); }, { passive: true });
window.addEventListener('mousemove', e => { if (dragging) updateSlider(e.clientX); });
window.addEventListener('touchmove', e => { if (dragging) updateSlider(e.touches[0].clientX); }, { passive: true });
window.addEventListener('mouseup', () => { dragging = false; });
window.addEventListener('touchend', () => { dragging = false; });
});
}
/* ============================================================
BUILD TIMELINE
============================================================ */
function buildTimeline() {
const container = document.getElementById('timelineScroll');
const allStops = [
{ id: 'venezuela', name: 'Venezuela', img: 'venezuela_angel_falls.jpg', dates: 'Feb 98' },
{ id: 'trinidad', name: 'Trinidad', img: null, dates: 'Feb 98' },
{ id: 'curacao', name: 'Curacao', img: 'curacao_bridge.jpg', dates: 'Feb 98' },
{ id: 'ecuador', name: 'Ecuador', img: null, dates: 'Mar 98' },
{ id: 'galapagos', name: 'Galapagos', img: null, dates: 'Mar 98' },
{ id: 'peru', name: 'Peru', img: null, dates: 'Mar 98' },
{ id: 'bolivia', name: 'Bolivia', img: 'bolivia_lapaz.jpg', dates: 'Mar 98' },
{ id: 'paraguay', name: 'Paraguay', img: null, dates: 'Mar 98' },
{ id: 'argentina', name: 'Argentina', img: 'argentina_iguazu_falls.jpg', dates: 'Mar 98' },
{ id: 'chile', name: 'Chile', img: 'chile_andes.jpg', dates: 'Mar 98' },
{ id: 'hongkong', name: 'Hong Kong', img: null, dates: 'Apr 98' },
{ id: 'thailand', name: 'Thailand', img: null, dates: 'May 98' },
{ id: 'malaysia', name: 'Malaysia', img: null, dates: 'May 98' },
{ id: 'cambodia', name: 'Cambodia', img: 'cambodia_angkor_wat.jpg', dates: 'May 98' },
{ id: 'singapore', name: 'Singapore', img: null, dates: 'May 98' },
{ id: 'nepal', name: 'Nepal', img: null, dates: 'Jun 98' },
{ id: 'india', name: 'India', img: null, dates: 'Jun 98' },
{ id: 'greece', name: 'Greece', img: 'greece_colossus_color.jpg', dates: 'Jul 98' },
{ id: 'turkey', name: 'Turkey', img: 'turkey_ephesus_library.jpg', dates: 'Jul 98' },
{ id: 'southafrica', name: 'South Africa', img: 'southafrica_capetown.jpg', dates: 'Aug 98' },
{ id: 'namibia', name: 'Namibia', img: null, dates: 'Sep 98' },
{ id: 'botswana', name: 'Botswana', img: 'botswana_mokoro_ride.jpg', dates: 'Sep 98' },
{ id: 'zimbabwe', name: 'Zimbabwe', img: null, dates: 'Sep 98' },
{ id: 'germany', name: 'Germany', img: null, dates: 'Oct 98' },
{ id: 'netherlands', name: 'Netherlands', img: null, dates: 'Oct 98' },
{ id: 'spain', name: 'Spain', img: 'spain_barcelona.jpg', dates: 'Oct 98' },
{ id: 'portugal', name: 'Portugal', img: null, dates: 'Nov 98' },
{ id: 'france', name: 'France', img: null, dates: 'Nov 98' },
{ id: 'egypt', name: 'Egypt', img: 'egypt_pyramid.jpg', dates: 'Dec 98' },
{ id: 'jordan', name: 'Jordan', img: 'jordan_petra_treasury.jpg', dates: 'Dec 98' },
{ id: 'palestine', name: 'Palestine', img: null, dates: 'Dec 98' },
];
let html = '';
allStops.forEach(s => {
const chapterId = CHAPTER_ORDER.includes(s.id) ? `ch-${s.id}` : '';
const onclick = chapterId ? `onclick="document.getElementById('${chapterId}').scrollIntoView({behavior:'smooth'})"` : '';
html += `<div class="timeline-stop" ${onclick}>
<div class="timeline-dot"></div>
${s.img ? `<img src="images/${s.img}" alt="${s.name}" loading="lazy">` : `<div class="no-img">${s.name.charAt(0)}</div>`}
<div class="country-name">${s.name}</div>
<div class="dates">${s.dates}</div>
</div>`;
});
container.innerHTML = html;
}
/* ============================================================
BUILD CHAPTERS (with pull quotes)
============================================================ */
function buildChapters() {
const container = document.getElementById('chapters-container');
let html = '';
CHAPTER_ORDER.forEach((key, idx) => {
const c = COUNTRIES[key];
const images = getCountryImages(key);
const heroImg = CHAPTER_HEROES[key];
const hasImages = images.length > 0;
const story = CHAPTER_STORIES[key] || '<p class="story-text">Stories from this destination are part of the greater WorldWander narrative. Each place left its mark on our journey and in our hearts.</p>';
html += `<div class="chapter" id="ch-${key}" data-region="${SOUND_REGIONS[key] || ''}">`;
// Chapter Hero
if (hasImages && heroImg) {
html += `<div class="chapter-hero">
<div class="chapter-hero-bg ken-burns" style="background-image:url('images/${heroImg}')" data-parallax="chapter"></div>
<div class="chapter-hero-overlay"></div>
<div class="chapter-hero-content">
<div class="chapter-number">Chapter ${idx + 1}</div>
<h2 class="chapter-title">${c.name}</h2>
<p class="chapter-tagline">${c.tagline}</p>
</div>
</div>`;
} else {
html += `<div class="chapter-hero no-image">
<div class="chapter-hero-bg" style="background:${c.gradient || 'linear-gradient(135deg, #4A6741, #2C1810, #8B4513)'}" data-parallax="chapter"></div>
<div class="chapter-hero-overlay"></div>
<div class="chapter-hero-content">
<div class="chapter-number">Chapter ${idx + 1}</div>
<h2 class="chapter-title">${c.name}</h2>
<p class="chapter-tagline">${c.tagline}</p>
</div>
</div>`;
}
// Chapter Body with story
html += `<div class="chapter-body">
<div class="story-expandable" id="story-${key}">
${story}
</div>
<button class="read-more-btn" onclick="toggleStory('${key}')">Read full story</button>
</div>`;
// Photo gallery for this chapter
if (hasImages) {
html += `<div class="chapter-gallery">
<h3 class="reveal">${c.name} Gallery</h3>
<p class="gallery-sub reveal">${images.length} photographs</p>
<div class="photo-grid">`;
images.forEach(img => {
const caption = AI_CAPTIONS[img] || img;
html += `<div class="photo-card reveal" onclick="openLightbox('images/${img}', '${caption.replace(/'/g, "\\'")}')">
<img src="images/${img}" alt="${caption}" loading="lazy">
<div class="caption"><span class="country-tag">${c.name}</span>${caption}</div>
</div>`;
});
html += `</div></div>`;
}
// Pull quote after chapter (if available)
if (PULL_QUOTES[key]) {
const pq = PULL_QUOTES[key];
html += `<div class="pull-quote-section reveal">
<div class="pull-quote">
<blockquote>${pq.text}<span class="quote-attr">&mdash; ${pq.attr}</span></blockquote>
</div>
</div>`;
}
html += `<div class="chapter-divider"><div class="ornament">&#10047; &bull; &#10047;</div></div>`;
html += `</div><!-- end chapter -->`;
});
container.innerHTML = html;
}
/* ============================================================
BUILD FULL GALLERY
============================================================ */
function buildGallery() {
const btnContainer = document.getElementById('filterButtons');
const allCountries = [...new Set(Object.keys(IMAGE_CAPTIONS).map(getCountry))].sort();
let btnHtml = '<button class="filter-btn active" data-filter="all">All</button>';
const regionMap = {
'South America': ['argentina', 'bolivia', 'chile', 'venezuela', 'curacao'],
'Africa': ['botswana', 'southafrica', 'zimbabwe', 'namibia'],
'Middle East': ['egypt', 'jordan', 'turkey'],
'Europe': ['greece', 'spain'],
'Asia': ['cambodia', 'nepal', 'india', 'thailand', 'singapore']
};
Object.keys(regionMap).forEach(region => {
btnHtml += `<button class="filter-btn" data-filter="${regionMap[region].join(',')}">${region}</button>`;
});
allCountries.forEach(c => {
const name = c.charAt(0).toUpperCase() + c.slice(1);
btnHtml += `<button class="filter-btn" data-filter="${c}">${name}</button>`;
});
btnContainer.innerHTML = btnHtml;
const grid = document.getElementById('masonryGrid');
const allImages = Object.keys(IMAGE_CAPTIONS);
const shuffled = allImages.sort(() => Math.random() - 0.5);
let gridHtml = '';
shuffled.forEach(img => {
const country = getCountry(img);
const caption = AI_CAPTIONS[img] || '';
const countryName = country.charAt(0).toUpperCase() + country.slice(1);
gridHtml += `<div class="masonry-item" data-country="${country}" onclick="openLightbox('images/${img}', '${caption.replace(/'/g, "\\'")}')">
<img src="images/${img}" alt="${caption}" loading="lazy">
<div class="m-caption"><span class="m-country">${countryName}</span><br>${caption}</div>
</div>`;
});
grid.innerHTML = gridHtml;
btnContainer.addEventListener('click', e => {
if (!e.target.classList.contains('filter-btn')) return;
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
e.target.classList.add('active');
const filter = e.target.dataset.filter;
const items = document.querySelectorAll('.masonry-item');
if (filter === 'all') {
items.forEach(item => item.classList.remove('hidden'));
} else {
const countries = filter.split(',');
items.forEach(item => {
item.classList.toggle('hidden', !countries.includes(item.dataset.country));
});
}
});
}
/* ============================================================
STORY EXPAND/COLLAPSE
============================================================ */
function toggleStory(key) {
const el = document.getElementById('story-' + key);
const btn = el.parentElement.querySelector('.read-more-btn');
if (el.classList.contains('expanded')) {
el.classList.remove('expanded');
btn.textContent = 'Read full story';
} else {
el.classList.add('expanded');
btn.textContent = 'Show less';
}
}
/* ============================================================
LIGHTBOX (with original toggle)
============================================================ */
let currentLightboxImages = [];
let currentLightboxIdx = 0;
let showingOriginal = false;
function openLightbox(src, caption) {
const lb = document.getElementById('lightbox');
const img = document.getElementById('lightboxImg');
const cap = document.getElementById('lightboxCaption');
const toggleBtn = document.getElementById('lightboxToggleOriginal');
showingOriginal = false;
img.src = src;
cap.innerHTML = caption;
lb.classList.add('active');
document.body.style.overflow = 'hidden';
const filename = src.split('/').pop();
toggleBtn.style.display = HAS_ORIGINAL.has(filename) ? 'block' : 'none';
toggleBtn.textContent = 'View Original';
currentLightboxImages = [];
document.querySelectorAll('.photo-card img, .masonry-item:not(.hidden) img').forEach(i => {
currentLightboxImages.push({ src: i.src, caption: i.alt || '' });
});
currentLightboxIdx = currentLightboxImages.findIndex(i => i.src.includes(src.replace('images/', '')));
}
function closeLightbox() {
document.getElementById('lightbox').classList.remove('active');
document.body.style.overflow = '';
}
function navigateLightbox(dir) {
if (currentLightboxImages.length === 0) return;
currentLightboxIdx = (currentLightboxIdx + dir + currentLightboxImages.length) % currentLightboxImages.length;
const item = currentLightboxImages[currentLightboxIdx];
const img = document.getElementById('lightboxImg');
const toggleBtn = document.getElementById('lightboxToggleOriginal');
showingOriginal = false;
img.src = item.src;
document.getElementById('lightboxCaption').textContent = item.caption;
const filename = item.src.split('/').pop();
toggleBtn.style.display = HAS_ORIGINAL.has(filename) ? 'block' : 'none';
toggleBtn.textContent = 'View Original';
}
document.getElementById('lightboxToggleOriginal').addEventListener('click', e => {
e.stopPropagation();
const img = document.getElementById('lightboxImg');
const btn = document.getElementById('lightboxToggleOriginal');
const currentSrc = img.src;
const filename = currentSrc.split('/').pop();
if (showingOriginal) {
img.src = 'images/' + filename;
btn.textContent = 'View Original';
showingOriginal = false;
} else {
img.src = 'images/original/' + filename;
btn.textContent = 'View Restored';
showingOriginal = true;
}
});
document.getElementById('lightboxClose').addEventListener('click', closeLightbox);
document.getElementById('lightbox').addEventListener('click', e => {
if (e.target === e.currentTarget) closeLightbox();
});
document.getElementById('lightboxPrev').addEventListener('click', e => { e.stopPropagation(); navigateLightbox(-1); });
document.getElementById('lightboxNext').addEventListener('click', e => { e.stopPropagation(); navigateLightbox(1); });
document.addEventListener('keydown', e => {
if (!document.getElementById('lightbox').classList.contains('active')) return;
if (e.key === 'Escape') closeLightbox();
if (e.key === 'ArrowLeft') navigateLightbox(-1);
if (e.key === 'ArrowRight') navigateLightbox(1);
});
/* ============================================================
SCROLL ANIMATIONS (IntersectionObserver)
============================================================ */
function setupRevealAnimations() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
}, { threshold: 0.1, rootMargin: '0px 0px -50px 0px' });
document.querySelectorAll('.reveal, .reveal-left, .reveal-right').forEach(el => {
observer.observe(el);
});
}
/* ============================================================
STATS COUNTER ANIMATION
============================================================ */
function setupStatsCounter() {
let animated = false;
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !animated) {
animated = true;
document.querySelectorAll('.stat-number[data-target]').forEach(el => {
const target = parseInt(el.dataset.target);
const suffix = el.dataset.suffix || '';
const duration = 2000;
const start = performance.now();
const animate = (now) => {
const elapsed = now - start;
const progress = Math.min(elapsed / duration, 1);
const eased = 1 - Math.pow(1 - progress, 3);
el.textContent = Math.round(target * eased) + suffix;
if (progress < 1) requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
});
}
});
}, { threshold: 0.3 });
observer.observe(document.getElementById('statsBar'));
}
/* ============================================================
PARALLAX EFFECTS
============================================================ */
function setupParallax() {
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(() => {
const scrollY = window.scrollY;
const heroBg = document.getElementById('heroBg');
if (heroBg) {
const heroH = window.innerHeight;
if (scrollY < heroH) {
heroBg.style.transform = `scale(1.1) translateY(${scrollY * 0.3}px)`;
}
}
document.querySelectorAll('[data-parallax="chapter"]').forEach(bg => {
const rect = bg.parentElement.getBoundingClientRect();
if (rect.bottom > 0 && rect.top < window.innerHeight) {
const offset = (rect.top / window.innerHeight) * 40;
bg.style.transform = `translateY(${offset}px) scale(1.08)`;
}
});
ticking = false;
});
ticking = true;
}
});
}
/* ============================================================
READING PROGRESS BAR
============================================================ */
function setupReadingProgress() {
const bar = document.getElementById('readingProgress');
const badge = document.getElementById('readTimeBadge');
// Estimate read time
const text = document.body.innerText || '';
const wordCount = text.split(/\s+/).length;
const readMinutes = Math.ceil(wordCount / 200);
badge.textContent = `~${readMinutes} min read`;
window.addEventListener('scroll', () => {
const docH = document.documentElement.scrollHeight - window.innerHeight;
const progress = Math.min((window.scrollY / docH) * 100, 100);
bar.style.width = progress + '%';
badge.classList.toggle('visible', window.scrollY > 200);
});
}
/* ============================================================
NAV SCROLL EFFECT
============================================================ */
function setupNav() {
const nav = document.getElementById('siteNav');
window.addEventListener('scroll', () => {
nav.classList.toggle('scrolled', window.scrollY > 80);
});
document.getElementById('mobileMenuBtn').addEventListener('click', () => {
document.getElementById('navLinks').classList.toggle('open');
});
document.querySelectorAll('.nav-links a').forEach(a => {
a.addEventListener('click', () => {
document.getElementById('navLinks').classList.remove('open');
});
});
}
/* ============================================================
AMBIENT SOUND TOGGLE (placeholder UI)
============================================================ */
function setupSound() {
const btn = document.getElementById('soundToggle');
let audioCtx = null;
let soundEnabled = false;
let currentRegion = '';
let oscillator = null;
let gainNode = null;
// Create a subtle ambient tone per region (placeholder)
const regionFreqs = {
'south-america': [180, 220, 260],
'africa': [150, 200, 250],
'middle-east': [200, 267, 300],
'asia': [220, 330, 440],
'europe': [196, 247, 294]
};
function playRegion(region) {
if (!audioCtx || !soundEnabled || region === currentRegion) return;
stopSound();
currentRegion = region;
const freqs = regionFreqs[region] || regionFreqs['europe'];
gainNode = audioCtx.createGain();
gainNode.gain.value = 0.03;
gainNode.connect(audioCtx.destination);
// Create a soft ambient chord
freqs.forEach(f => {
const osc = audioCtx.createOscillator();
osc.type = 'sine';
osc.frequency.value = f;
osc.connect(gainNode);
osc.start();
});
}
function stopSound() {
if (gainNode) {
gainNode.disconnect();
gainNode = null;
}
currentRegion = '';
}
btn.addEventListener('click', () => {
soundEnabled = !soundEnabled;
btn.classList.toggle('active', soundEnabled);
if (soundEnabled) {
if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
// Find current region
const chapters = document.querySelectorAll('.chapter');
for (const ch of chapters) {
const rect = ch.getBoundingClientRect();
if (rect.top < window.innerHeight / 2 && rect.bottom > 0) {
playRegion(ch.dataset.region);
break;
}
}
} else {
stopSound();
}
});
// Update region on scroll
window.addEventListener('scroll', () => {
if (!soundEnabled) return;
const chapters = document.querySelectorAll('.chapter');
for (const ch of chapters) {
const rect = ch.getBoundingClientRect();
if (rect.top < window.innerHeight / 2 && rect.bottom > 0) {
const region = ch.dataset.region;
if (region && region !== currentRegion) playRegion(region);
break;
}
}
});
}
/* ============================================================
3D GLOBE with Three.js
============================================================ */
function setupGlobe() {
const container = document.getElementById('globe-container');
const tooltip = document.getElementById('globe-tooltip');
const width = container.clientWidth;
const height = container.clientHeight;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000);
camera.position.z = 3;
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(width, height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
container.appendChild(renderer.domElement);
// Earth sphere - procedural blue/green
const earthGeo = new THREE.SphereGeometry(1, 64, 64);
// Create a canvas texture for the earth
const texCanvas = document.createElement('canvas');
texCanvas.width = 1024;
texCanvas.height = 512;
const ctx = texCanvas.getContext('2d');
// Ocean blue
ctx.fillStyle = '#1a4a7a';
ctx.fillRect(0, 0, 1024, 512);
// Simplified continent shapes (lat/lon to UV)
function drawContinent(points, color) {
ctx.fillStyle = color;
ctx.beginPath();
points.forEach((p, i) => {
const x = ((p[1] + 180) / 360) * 1024;
const y = ((90 - p[0]) / 180) * 512;
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
});
ctx.closePath();
ctx.fill();
}
const landColor = '#3a6b35';
const landColor2 = '#4a7a45';
// North America
drawContinent([[60,-130],[70,-100],[65,-60],[50,-55],[30,-80],[25,-100],[30,-115],[48,-125]], landColor);
// South America
drawContinent([[12,-75],[5,-60],[-5,-35],[-15,-40],[-35,-55],[-55,-68],[-50,-75],[-20,-70],[-5,-80],[5,-77]], landColor2);
// Europe
drawContinent([[70,10],[72,30],[55,40],[45,30],[36,0],[43,-10],[48,-5],[55,10]], landColor);
// Africa
drawContinent([[35,-5],[30,32],[10,42],[0,42],[-10,40],[-35,20],[-35,18],[-25,15],[5,-10],[10,-17],[20,-17],[30,-10]], landColor2);
// Asia
drawContinent([[70,40],[70,140],[50,140],[35,130],[20,107],[10,105],[0,100],[5,80],[25,65],[30,50],[40,40],[55,40]], landColor);
// Australia
drawContinent([[-12,130],[-15,140],[-25,150],[-35,150],[-35,137],[-32,115],[-20,115],[-12,130]], landColor2);
// India
drawContinent([[30,70],[25,90],[8,77],[15,73]], landColor);
const earthTex = new THREE.CanvasTexture(texCanvas);
const earthMat = new THREE.MeshPhongMaterial({
map: earthTex,
specular: 0x222222,
shininess: 20
});
const earth = new THREE.Mesh(earthGeo, earthMat);
scene.add(earth);
// Atmosphere glow
const atmosGeo = new THREE.SphereGeometry(1.03, 64, 64);
const atmosMat = new THREE.MeshPhongMaterial({
color: 0x4488ff,
transparent: true,
opacity: 0.08,
side: THREE.BackSide
});
scene.add(new THREE.Mesh(atmosGeo, atmosMat));
// Lights
const ambientLight = new THREE.AmbientLight(0x444444);
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.9);
dirLight.position.set(5, 3, 5);
scene.add(dirLight);
// Destination data (lat, lon, name, chapter)
const destinations = [
{ lat: -34.6, lon: -58.4, name: 'Argentina', chapter: 'ch-argentina' },
{ lat: -16.5, lon: -68.15, name: 'Bolivia', chapter: 'ch-bolivia' },
{ lat: -33.4, lon: -70.6, name: 'Chile', chapter: 'ch-chile' },
{ lat: 8.0, lon: -66.0, name: 'Venezuela', chapter: 'ch-venezuela' },
{ lat: -0.2, lon: -78.5, name: 'Ecuador', chapter: 'ch-ecuador' },
{ lat: 12.2, lon: -68.9, name: 'Curacao', chapter: 'ch-curacao' },
{ lat: 30.0, lon: 31.2, name: 'Egypt', chapter: 'ch-egypt' },
{ lat: 30.3, lon: 35.7, name: 'Jordan', chapter: 'ch-jordan' },
{ lat: 39.0, lon: 35.2, name: 'Turkey', chapter: 'ch-turkey' },
{ lat: 37.9, lon: 23.7, name: 'Greece', chapter: 'ch-greece' },
{ lat: 40.4, lon: -3.7, name: 'Spain', chapter: 'ch-spain' },
{ lat: -33.9, lon: 18.4, name: 'South Africa', chapter: 'ch-southafrica' },
{ lat: -20.0, lon: 23.0, name: 'Botswana', chapter: 'ch-botswana' },
{ lat: -17.8, lon: 25.8, name: 'Zimbabwe', chapter: 'ch-zimbabwe' },
{ lat: -22.0, lon: 17.0, name: 'Namibia', chapter: 'ch-namibia' },
{ lat: 27.7, lon: 85.3, name: 'Nepal', chapter: 'ch-nepal' },
{ lat: 28.6, lon: 77.2, name: 'India', chapter: 'ch-india' },
{ lat: 13.4, lon: 103.8, name: 'Cambodia', chapter: 'ch-cambodia' },
{ lat: 13.7, lon: 100.5, name: 'Thailand', chapter: 'ch-thailand' },
{ lat: 1.3, lon: 103.8, name: 'Singapore', chapter: 'ch-singapore' },
];
// Convert lat/lon to 3D position
function latLonToVec3(lat, lon, r) {
const phi = (90 - lat) * (Math.PI / 180);
const theta = (lon + 180) * (Math.PI / 180);
return new THREE.Vector3(
-(r * Math.sin(phi) * Math.cos(theta)),
r * Math.cos(phi),
r * Math.sin(phi) * Math.sin(theta)
);
}
// Create pins
const pinGroup = new THREE.Group();
const pins = [];
const pinGeo = new THREE.SphereGeometry(0.018, 8, 8);
const pinMat = new THREE.MeshBasicMaterial({ color: 0xD4A574 });
destinations.forEach(dest => {
const pos = latLonToVec3(dest.lat, dest.lon, 1.02);
const pin = new THREE.Mesh(pinGeo, pinMat.clone());
pin.position.copy(pos);
pin.userData = { name: dest.name, chapter: dest.chapter };
pinGroup.add(pin);
pins.push(pin);
// Stem
const stemGeo = new THREE.CylinderGeometry(0.003, 0.003, 0.04, 4);
const stemMat = new THREE.MeshBasicMaterial({ color: 0xD4A574 });
const stem = new THREE.Mesh(stemGeo, stemMat);
const surfacePos = latLonToVec3(dest.lat, dest.lon, 1.0);
stem.position.copy(surfacePos).lerp(pos, 0.5);
stem.lookAt(pos);
stem.rotateX(Math.PI / 2);
pinGroup.add(stem);
});
scene.add(pinGroup);
// Animated arc flight paths
const routeOrder = [
{ lat: -34.6, lon: -58.4 }, // Argentina
{ lat: -16.5, lon: -68.15 }, // Bolivia
{ lat: -33.4, lon: -70.6 }, // Chile
{ lat: 8.0, lon: -66.0 }, // Venezuela
{ lat: -0.2, lon: -78.5 }, // Ecuador
{ lat: 12.2, lon: -68.9 }, // Curacao
{ lat: 30.0, lon: 31.2 }, // Egypt
{ lat: 30.3, lon: 35.7 }, // Jordan
{ lat: 39.0, lon: 35.2 }, // Turkey
{ lat: 37.9, lon: 23.7 }, // Greece
{ lat: 40.4, lon: -3.7 }, // Spain
{ lat: -33.9, lon: 18.4 }, // South Africa
{ lat: -20.0, lon: 23.0 }, // Botswana
{ lat: -17.8, lon: 25.8 }, // Zimbabwe
{ lat: -22.0, lon: 17.0 }, // Namibia
{ lat: 27.7, lon: 85.3 }, // Nepal
{ lat: 28.6, lon: 77.2 }, // India
{ lat: 13.4, lon: 103.8 }, // Cambodia
{ lat: 13.7, lon: 100.5 }, // Thailand
{ lat: 1.3, lon: 103.8 }, // Singapore
{ lat: 27.7, lon: 85.3 }, // Nepal (return)
];
for (let i = 0; i < routeOrder.length - 1; i++) {
const start = latLonToVec3(routeOrder[i].lat, routeOrder[i].lon, 1.0);
const end = latLonToVec3(routeOrder[i+1].lat, routeOrder[i+1].lon, 1.0);
const mid = start.clone().lerp(end, 0.5);
const dist = start.distanceTo(end);
mid.normalize().multiplyScalar(1.0 + dist * 0.25);
const curve = new THREE.QuadraticBezierCurve3(start, mid, end);
const points = curve.getPoints(40);
const lineGeo = new THREE.BufferGeometry().setFromPoints(points);
const lineMat = new THREE.LineBasicMaterial({ color: 0xD4A574, transparent: true, opacity: 0.4 });
scene.add(new THREE.Line(lineGeo, lineMat));
}
// OrbitControls (manual implementation)
let isDragging = false;
let prevMouse = { x: 0, y: 0 };
let rotationVel = { x: 0, y: 0 };
let autoRotate = true;
let hovered = false;
container.addEventListener('mousedown', e => {
isDragging = true;
prevMouse = { x: e.clientX, y: e.clientY };
autoRotate = false;
});
container.addEventListener('touchstart', e => {
isDragging = true;
prevMouse = { x: e.touches[0].clientX, y: e.touches[0].clientY };
autoRotate = false;
}, { passive: true });
window.addEventListener('mousemove', e => {
if (isDragging) {
const dx = e.clientX - prevMouse.x;
const dy = e.clientY - prevMouse.y;
rotationVel.x = dy * 0.005;
rotationVel.y = dx * 0.005;
earth.rotation.y += dx * 0.005;
earth.rotation.x += dy * 0.005;
earth.rotation.x = Math.max(-1.2, Math.min(1.2, earth.rotation.x));
pinGroup.rotation.y = earth.rotation.y;
pinGroup.rotation.x = earth.rotation.x;
prevMouse = { x: e.clientX, y: e.clientY };
}
// Raycast for tooltip
handleHover(e.clientX, e.clientY);
});
window.addEventListener('touchmove', e => {
if (isDragging) {
const dx = e.touches[0].clientX - prevMouse.x;
const dy = e.touches[0].clientY - prevMouse.y;
earth.rotation.y += dx * 0.005;
earth.rotation.x += dy * 0.005;
earth.rotation.x = Math.max(-1.2, Math.min(1.2, earth.rotation.x));
pinGroup.rotation.y = earth.rotation.y;
pinGroup.rotation.x = earth.rotation.x;
prevMouse = { x: e.touches[0].clientX, y: e.touches[0].clientY };
}
}, { passive: true });
window.addEventListener('mouseup', () => {
isDragging = false;
setTimeout(() => { autoRotate = true; }, 3000);
});
window.addEventListener('touchend', () => {
isDragging = false;
setTimeout(() => { autoRotate = true; }, 3000);
});
container.addEventListener('mouseenter', () => { hovered = true; });
container.addEventListener('mouseleave', () => {
hovered = false;
tooltip.style.display = 'none';
});
// Raycaster for pin hover/click
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function handleHover(clientX, clientY) {
const rect = container.getBoundingClientRect();
mouse.x = ((clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = -((clientY - rect.top) / rect.height) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(pins);
if (intersects.length > 0) {
const pin = intersects[0].object;
tooltip.textContent = pin.userData.name;
tooltip.style.display = 'block';
tooltip.style.left = (clientX - rect.left + 15) + 'px';
tooltip.style.top = (clientY - rect.top - 15) + 'px';
container.style.cursor = 'pointer';
pin.material.color.setHex(0xF5F0E8);
pin.scale.setScalar(1.5);
} else {
tooltip.style.display = 'none';
container.style.cursor = 'grab';
pins.forEach(p => { p.material.color.setHex(0xD4A574); p.scale.setScalar(1); });
}
}
container.addEventListener('click', e => {
const rect = container.getBoundingClientRect();
mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(pins);
if (intersects.length > 0) {
const chapter = intersects[0].object.userData.chapter;
const el = document.getElementById(chapter);
if (el) el.scrollIntoView({ behavior: 'smooth' });
}
});
// Animation loop
function animate() {
requestAnimationFrame(animate);
if (autoRotate && !hovered) {
earth.rotation.y += 0.002;
pinGroup.rotation.y = earth.rotation.y;
pinGroup.rotation.x = earth.rotation.x;
}
// Sync scenes for arcs
scene.children.forEach(child => {
if (child.isLine) {
child.rotation.y = earth.rotation.y;
child.rotation.x = earth.rotation.x;
}
});
renderer.render(scene, camera);
}
animate();
// Resize handler
window.addEventListener('resize', () => {
const w = container.clientWidth;
const h = container.clientHeight;
camera.aspect = w / h;
camera.updateProjectionMatrix();
renderer.setSize(w, h);
});
}
/* ============================================================
KEN BURNS - pause on not visible
============================================================ */
function setupKenBurns() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
const bg = entry.target;
if (entry.isIntersecting) {
bg.style.animationPlayState = 'running';
} else {
bg.style.animationPlayState = 'paused';
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.ken-burns').forEach(el => observer.observe(el));
}
/* ============================================================
INIT
============================================================ */
document.addEventListener('DOMContentLoaded', () => {
buildTimeline();
buildChapters();
buildBeforeAfter();
buildGallery();
setupRevealAnimations();
setupStatsCounter();
setupParallax();
setupNav();
setupReadingProgress();
setupSound();
setupKenBurns();
// Delay globe init slightly so container has size
setTimeout(setupGlobe, 100);
});
</script>
</body>
</html>