<style data-inlined-from="assets/css/oloropa-immersive-sitewide.css">
/* Oloropa sitewide immersive layer. Additive, scoped, and public-page safe. */
:root{
  --olx-green:#1F3A2D;
  --olx-deep:#07120d;
  --olx-gold:#C6A24A;
  --olx-ivory:#fff8eb;
  --olx-ink:#17231c;
  --olx-line:rgba(31,58,45,.14);
  --olx-shadow:0 28px 90px rgba(7,24,16,.18);
}
body[data-olx-tone="atlas"]{--olx-green:#173d2d;--olx-gold:#C6A24A}
body[data-olx-tone="coast"]{--olx-green:#12433c;--olx-gold:#d7bb72}
body[data-olx-tone="story"]{--olx-green:#173527;--olx-gold:#C6A24A}
body[data-olx-tone="brief"]{--olx-green:#1F3A2D;--olx-gold:#e0b963}
body[data-olx-tone="guard"]{--olx-green:#10281d;--olx-gold:#efc96b}
html{scroll-behavior:smooth}
body.olx-immersive-ready{--olx-progress:0;--olx-mouse-x:50%;--olx-mouse-y:30%}
body.olx-immersive-ready::before{
  content:"";position:fixed;left:0;top:0;z-index:2147482500;width:100%;height:3px;
  transform:scaleX(var(--olx-progress));transform-origin:left center;
  background:linear-gradient(90deg,transparent,var(--olx-gold),#fff0bd,var(--olx-gold),transparent);
  box-shadow:0 0 22px rgba(198,162,74,.42);pointer-events:none;
}
body.olx-immersive-ready::after{
  content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;
  background:radial-gradient(circle at var(--olx-mouse-x) var(--olx-mouse-y),rgba(198,162,74,.10),transparent 24rem);
}
.olx-reveal{opacity:0;transform:translateY(22px);filter:blur(5px);transition:opacity .7s ease,transform .7s ease,filter .7s ease}
.olx-reveal.is-visible{opacity:1;transform:translateY(0);filter:blur(0)}
.olx-media-awake img,.olx-media-awake video{transition:transform 1.1s cubic-bezier(.22,1,.36,1),filter 1.1s cubic-bezier(.22,1,.36,1)}
.olx-media-awake:hover img,.olx-media-awake:hover video{transform:scale(1.045);filter:saturate(1.08) contrast(1.03)}
.olx-command-dock{
  position:fixed;left:50%;bottom:18px;z-index:2147482400;display:flex;align-items:center;gap:.55rem;
  max-width:calc(100vw - 24px);padding:.48rem;border:1px solid rgba(255,248,235,.16);border-radius:999px;
  color:var(--olx-ivory);background:linear-gradient(135deg,rgba(7,25,17,.86),rgba(31,58,45,.78));
  box-shadow:0 22px 80px rgba(0,0,0,.28);backdrop-filter:blur(18px);transform:translateX(-50%);
}
.olx-command-dock a,.olx-command-dock button{
  display:inline-flex;align-items:center;justify-content:center;gap:.45rem;min-height:38px;padding:0 .85rem;
  border:0;border-radius:999px;background:rgba(255,248,235,.08);color:var(--olx-ivory);
  font:inherit;font-size:.78rem;font-weight:900;text-decoration:none;white-space:nowrap;cursor:pointer;
}
.olx-command-dock a:first-child{background:linear-gradient(135deg,var(--olx-ivory),#f1da9b);color:#173024}
.olx-command-dock button[aria-pressed="true"]{background:rgba(198,162,74,.22)}
.olx-brief-toast{
  position:fixed;right:18px;bottom:78px;z-index:2147482401;max-width:min(360px,calc(100vw - 36px));
  padding:.9rem 1rem;border:1px solid rgba(31,58,45,.12);border-radius:18px;background:var(--olx-ivory);
  color:var(--olx-green);box-shadow:var(--olx-shadow);font-weight:850;opacity:0;transform:translateY(12px);
  pointer-events:none;transition:opacity .28s ease,transform .28s ease;
}
.olx-brief-toast.is-visible{opacity:1;transform:translateY(0)}
.olx-booking-modal{
  position:fixed;inset:0;z-index:2147482600;display:none;align-items:center;justify-content:center;
  padding:18px;background:rgba(3,10,7,.66);backdrop-filter:blur(12px);
}
.olx-booking-modal.is-open{display:flex}
.olx-booking-dialog{
  width:min(860px,100%);max-height:min(820px,calc(100vh - 36px));overflow:auto;border-radius:28px;
  border:1px solid rgba(255,248,235,.18);background:linear-gradient(145deg,#fff8eb,#f0dfb7);
  color:var(--olx-green);box-shadow:0 36px 120px rgba(0,0,0,.38);
}
.olx-booking-head{
  position:sticky;top:0;z-index:2;display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;
  padding:1.15rem 1.2rem;border-bottom:1px solid rgba(31,58,45,.12);
  background:linear-gradient(145deg,rgba(255,248,235,.96),rgba(240,223,183,.94));backdrop-filter:blur(16px);
}
.olx-booking-head span{display:block;color:#8b6a25;font-size:.68rem;font-weight:950;letter-spacing:.18em;text-transform:uppercase}
.olx-booking-head strong{display:block;margin:.22rem 0;font-family:Georgia,"Times New Roman",serif;font-size:clamp(1.75rem,3vw,2.65rem);line-height:.95;color:var(--olx-green)}
.olx-booking-head p{margin:0;color:rgba(31,58,45,.68);line-height:1.55}
.olx-booking-close{flex:0 0 auto;border:1px solid rgba(31,58,45,.14);border-radius:999px;background:#fff;color:var(--olx-green);width:40px;height:40px;font-size:1.25rem;cursor:pointer}
.olx-booking-form{padding:1.15rem 1.2rem 1.25rem}
.olx-booking-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.82rem}
.olx-booking-field{display:grid;gap:.36rem}
.olx-booking-field.full{grid-column:1/-1}
.olx-booking-field label{font-size:.72rem;font-weight:950;letter-spacing:.12em;text-transform:uppercase;color:rgba(31,58,45,.72)}
.olx-booking-field input,.olx-booking-field select,.olx-booking-field textarea{
  width:100%;border:1px solid rgba(31,58,45,.16);border-radius:16px;background:rgba(255,255,255,.72);
  color:var(--olx-green);font:inherit;font-weight:750;min-height:48px;padding:.82rem .9rem;outline:none;
}
.olx-booking-field textarea{min-height:118px;resize:vertical;line-height:1.5}
.olx-booking-field :is(input,select,textarea):focus{border-color:var(--olx-gold);box-shadow:0 0 0 4px rgba(198,162,74,.18);background:#fff}
.olx-booking-status{min-height:22px;margin:.9rem 0 0;color:rgba(31,58,45,.72);font-weight:850}
.olx-booking-actions{display:flex;align-items:center;justify-content:space-between;gap:.75rem;flex-wrap:wrap;margin-top:1rem}
.olx-booking-actions button{
  border:0;border-radius:999px;min-height:46px;padding:0 1rem;font:inherit;font-weight:950;cursor:pointer;
}
.olx-booking-actions button[type="submit"]{background:linear-gradient(135deg,var(--olx-green),#0e271b);color:var(--olx-ivory);box-shadow:0 16px 45px rgba(31,58,45,.22)}
.olx-booking-actions button[type="button"]{background:rgba(31,58,45,.08);color:var(--olx-green)}
.olx-booking-actions button:disabled{opacity:.62;cursor:wait}
body.olx-booking-lock{overflow:hidden}
.olx-page-guide{
  position:fixed;right:18px;top:calc(var(--ol19-header-height,76px) + 34px);z-index:2147482300;
  width:min(340px,calc(100vw - 36px));padding:1rem;border:1px solid rgba(255,248,235,.16);border-radius:24px;
  color:var(--olx-ivory);background:linear-gradient(135deg,rgba(7,25,17,.74),rgba(31,58,45,.62));
  box-shadow:0 24px 80px rgba(0,0,0,.22);backdrop-filter:blur(18px);transform:translateY(0);transition:opacity .3s ease,transform .3s ease;
}
.olx-page-guide.is-minimized{opacity:.78;transform:translateY(calc(-100% + 52px))}
.olx-page-guide-head{display:flex;align-items:flex-start;justify-content:space-between;gap:.8rem}
.olx-page-guide span{display:block;color:var(--olx-gold);font-size:.66rem;font-weight:900;letter-spacing:.18em;text-transform:uppercase}
.olx-page-guide strong{display:block;margin:.35rem 0;font-family:Georgia,"Times New Roman",serif;font-size:1.55rem;line-height:1}
.olx-page-guide p{margin:0;color:rgba(255,248,235,.72);font-size:.92rem;line-height:1.62}
.olx-page-guide button{border:1px solid rgba(255,248,235,.16);border-radius:999px;background:rgba(255,248,235,.08);color:var(--olx-ivory);width:34px;height:34px;cursor:pointer}
.olx-page-actions{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:.9rem}
.olx-page-actions a{display:inline-flex;align-items:center;justify-content:center;min-height:34px;padding:0 .75rem;border-radius:999px;background:rgba(255,248,235,.10);color:var(--olx-ivory);font-size:.72rem;font-weight:900;text-decoration:none}
.olx-page-actions a:first-child{background:linear-gradient(135deg,var(--olx-ivory),#f1da9b);color:#173024}
.olx-section-pips{display:flex;gap:.32rem;margin-top:.9rem}
.olx-section-pips i{display:block;width:100%;height:4px;border-radius:999px;background:rgba(255,248,235,.16);overflow:hidden}
.olx-section-pips i.is-active{background:var(--olx-gold)}
body[data-oloropa-page="index.php"] .olx-story-rail{
  position:fixed;left:18px;top:50%;z-index:1200;display:grid;gap:.5rem;transform:translateY(-50%);
}
body[data-oloropa-page="index.php"] .olx-story-rail button{
  display:grid;grid-template-columns:24px minmax(0,1fr);align-items:center;gap:.55rem;width:168px;min-height:38px;
  border:1px solid rgba(255,248,235,.14);border-radius:999px;background:rgba(7,24,16,.42);color:rgba(255,248,235,.74);
  backdrop-filter:blur(14px);font-size:.72rem;font-weight:900;text-align:left;cursor:pointer;
}
body[data-oloropa-page="index.php"] .olx-story-rail i{display:grid;place-items:center;width:24px;height:24px;border-radius:999px;background:rgba(255,248,235,.10);font-style:normal;color:var(--olx-gold)}
body[data-oloropa-page="index.php"] .olx-story-rail button.is-active{background:rgba(255,248,235,.94);color:var(--olx-green);box-shadow:0 18px 55px rgba(0,0,0,.24)}
body[data-oloropa-page="index.php"] .olx-story-caption{
  position:fixed;right:18px;top:50%;z-index:1200;width:min(330px,calc(100vw - 36px));padding:1rem;
  border:1px solid rgba(255,248,235,.16);border-radius:24px;background:rgba(7,24,16,.58);color:var(--olx-ivory);
  backdrop-filter:blur(18px);box-shadow:0 22px 80px rgba(0,0,0,.24);transform:translateY(-50%);
}
body[data-oloropa-page="index.php"] .olx-story-caption span{display:block;color:var(--olx-gold);font-size:.68rem;font-weight:900;letter-spacing:.18em;text-transform:uppercase}
body[data-oloropa-page="index.php"] .olx-story-caption strong{display:block;margin:.35rem 0;font-family:Georgia,"Times New Roman",serif;font-size:1.6rem;line-height:1}
body[data-oloropa-page="index.php"] .olx-story-caption p{margin:0;color:rgba(255,248,235,.72);line-height:1.6}
@media(max-width:980px){
  .olx-command-dock{left:12px;right:12px;bottom:12px;transform:none;justify-content:space-between;border-radius:22px}
  .olx-command-dock a,.olx-command-dock button{flex:1;padding:0 .6rem;font-size:.72rem}
  .olx-page-guide{display:none}
  body[data-oloropa-page="index.php"] .olx-story-rail,body[data-oloropa-page="index.php"] .olx-story-caption{display:none}
  .olx-booking-dialog{border-radius:22px}
  .olx-booking-grid{grid-template-columns:1fr}
}
@media(prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .olx-reveal,.olx-media-awake img,.olx-media-awake video,*{transition:none!important;animation:none!important}
}
.olx-booking-options {
  border: 1px solid rgba(217, 181, 96, 0.22);
  border-radius: 22px;
  background: rgba(255, 255, 255, 0.07);
  padding: 0.9rem;
  margin-bottom: 1rem;
}

.olx-booking-options-head {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  align-items: start;
  color: rgba(255, 248, 230, 0.72);
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-weight: 800;
}

.olx-booking-options-head strong {
  color: #ffe8a8;
  text-align: right;
  max-width: 52%;
}

.olx-booking-option-tabs {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.4rem;
  margin: 0.85rem 0;
}

.olx-booking-option-tabs button {
  border: 1px solid rgba(217, 181, 96, 0.18);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 248, 230, 0.78);
  padding: 0.62rem 0.55rem;
  font-weight: 900;
  cursor: pointer;
}

.olx-booking-option-tabs button.is-active {
  background: linear-gradient(135deg, #d8b45f, #f2d98f);
  color: #15241b;
}

.olx-booking-option-list {
  display: grid;
  gap: 0.55rem;
  max-height: 19rem;
  overflow: auto;
  padding-right: 0.2rem;
}

.olx-booking-option {
  display: grid;
  grid-template-columns: 74px 1fr;
  gap: 0.75rem;
  width: 100%;
  border: 1px solid rgba(217, 181, 96, 0.18);
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.08);
  color: #fff8e6;
  padding: 0.52rem;
  text-align: left;
  cursor: pointer;
}

.olx-booking-option.is-selected {
  border-color: rgba(242, 217, 143, 0.78);
  background: rgba(242, 217, 143, 0.16);
}

.olx-booking-option-media {
  display: block;
  min-height: 74px;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.12) center/cover no-repeat;
}

.olx-booking-option-copy {
  min-width: 0;
  display: grid;
  gap: 0.18rem;
}

.olx-booking-option-copy span,
.olx-booking-option-copy small {
  color: rgba(255, 248, 230, 0.62);
}

.olx-booking-option-copy span {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.11em;
  font-weight: 900;
}

.olx-booking-option-copy strong {
  color: #fff8e6;
  font-size: 0.95rem;
  line-height: 1.18;
}

.olx-booking-option-copy small {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.35;
}

.olx-booking-option-copy em {
  color: #ffe8a8;
  font-style: normal;
  font-weight: 900;
  font-size: 0.8rem;
}

.olx-booking-option-empty {
  margin: 0;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.07);
  color: rgba(255, 248, 230, 0.68);
  padding: 0.8rem;
  font-weight: 700;
}

.olx-booking-itinerary-head {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin: 0.85rem 0 0.5rem;
  color: rgba(255, 248, 230, 0.66);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.11em;
  font-weight: 900;
}

.olx-booking-itinerary-head strong {
  color: #ffe8a8;
  text-align: right;
}

.olx-booking-itinerary-days {
  display: grid;
  gap: 0.45rem;
}

.olx-booking-itinerary-days article {
  display: grid;
  grid-template-columns: 4.4rem 1fr;
  gap: 0.45rem 0.65rem;
  border: 1px solid rgba(217, 181, 96, 0.15);
  border-radius: 15px;
  background: rgba(255, 255, 255, 0.055);
  padding: 0.62rem;
}

.olx-booking-itinerary-days b {
  color: #ffe8a8;
  font-size: 0.78rem;
}

.olx-booking-itinerary-days span {
  color: #fff8e6;
  font-weight: 900;
  line-height: 1.2;
}

.olx-booking-itinerary-days small {
  grid-column: 2;
  color: rgba(255, 248, 230, 0.64);
  line-height: 1.35;
}

.olx-serenity-live-rail {
  width: min(1180px, calc(100% - 32px));
  margin: 28px auto;
  border: 1px solid rgba(198, 162, 74, 0.24);
  border-radius: 26px;
  background: rgba(255, 250, 240, 0.9);
  color: #17231d;
  padding: 20px;
  box-shadow: 0 24px 70px rgba(31, 58, 45, 0.12);
}

.olx-serenity-live-head {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 14px;
}

.olx-serenity-live-head span {
  color: #8a6f2e;
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-weight: 900;
}

.olx-serenity-live-head strong {
  color: #1f3a2d;
  font-family: Georgia, serif;
  font-size: clamp(1.4rem, 3vw, 2.1rem);
}

.olx-serenity-live-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 12px;
}

.olx-serenity-live-grid article {
  overflow: hidden;
  border: 1px solid rgba(198, 162, 74, 0.18);
  border-radius: 20px;
  background: #fff;
}

.olx-serenity-live-grid article > span {
  display: block;
  height: 145px;
  background: #173326 center/cover no-repeat;
}

.olx-serenity-live-grid article > div {
  padding: 13px;
}

.olx-serenity-live-grid small {
  display: block;
  color: #8a6f2e;
  font-size: 0.72rem;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

.olx-serenity-live-grid b {
  display: block;
  margin-top: 5px;
  color: #1f3a2d;
  font-size: 1.02rem;
}

.olx-serenity-live-grid p {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  min-height: 2.7em;
  overflow: hidden;
  color: #667067;
  line-height: 1.35;
}

.olx-serenity-live-grid button {
  border: 0;
  border-radius: 999px;
  background: #1f3a2d;
  color: white;
  padding: 10px 13px;
  font-weight: 900;
  cursor: pointer;
}

@media (max-width: 560px) {
  .olx-booking-option-tabs {
    grid-template-columns: 1fr;
  }

  .olx-booking-option {
    grid-template-columns: 58px 1fr;
  }

  .olx-booking-option-media {
    min-height: 58px;
  }
}

/* Phase: advanced Oloropa booking/itinerary builder */
.olx-builder-progress{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:.45rem;margin:.85rem 0 1rem}.olx-builder-progress button{border:1px solid rgba(198,162,74,.28);background:#fff;border-radius:999px;padding:.45rem .55rem;display:flex;align-items:center;justify-content:center;gap:.4rem;font-size:.72rem;font-weight:900;color:#123529}.olx-builder-progress button b{width:1.35rem;height:1.35rem;border-radius:999px;background:#F4E8D0;display:grid;place-items:center}.olx-builder-progress button.is-active{background:#123529;color:#fff;border-color:#123529}.olx-builder-progress button.is-active b{background:#C6A24A;color:#123529}.olx-step-page{display:none;border:1px solid rgba(198,162,74,.22);background:linear-gradient(180deg,#fff,#fffaf0);border-radius:24px;padding:1rem;margin-top:1rem}.olx-step-page.is-active{display:block;animation:olxStepIn .28s ease}.olx-step-page h3{margin:0 0 .35rem;color:#123529;font-size:1.05rem}.olx-step-page p{margin:0 0 .9rem;color:#657168;font-size:.88rem}.olx-step-controls{display:flex;justify-content:space-between;gap:.75rem;margin-top:.85rem}.olx-step-controls button{border:1px solid rgba(198,162,74,.32);border-radius:999px;background:#fff;padding:.75rem 1rem;font-weight:900;color:#123529}.olx-step-controls button:last-child{background:#123529;color:#fff}.olx-step-controls button:disabled{opacity:.45}.olx-package-morph-strip{position:absolute;left:.35rem;right:.35rem;bottom:.35rem;display:flex;gap:.25rem}.olx-package-morph-strip i{height:28px;flex:1;border-radius:10px;background-size:cover;background-position:center;border:1px solid rgba(255,255,255,.72);box-shadow:0 4px 12px rgba(0,0,0,.18);animation:olxMiniMorph 7s ease-in-out infinite}.olx-route-order{display:block!important;margin-top:.35rem;color:#123529!important;font-weight:800}.olx-package-morph-show{position:relative;height:170px;border-radius:24px;overflow:hidden;margin:.9rem 0;background:#123529;isolation:isolate}.olx-package-morph-show figure{position:absolute;inset:0;margin:0;background-size:cover;background-position:center;opacity:0;transform:scale(1.06);animation:olxPackageMorph 24s infinite}.olx-package-morph-show figure:nth-child(1){animation-delay:0s}.olx-package-morph-show figure:nth-child(2){animation-delay:3s}.olx-package-morph-show figure:nth-child(3){animation-delay:6s}.olx-package-morph-show figure:nth-child(4){animation-delay:9s}.olx-package-morph-show figure:nth-child(5){animation-delay:12s}.olx-package-morph-show figure:nth-child(6){animation-delay:15s}.olx-package-morph-show figure:nth-child(7){animation-delay:18s}.olx-package-morph-show figure:nth-child(8){animation-delay:21s}.olx-package-morph-show figcaption{position:absolute;left:1rem;right:1rem;bottom:1rem;border-radius:999px;background:rgba(18,53,41,.78);color:#fff;padding:.5rem .8rem;font-size:.76rem;font-weight:900;backdrop-filter:blur(10px)}.olx-availability-calendar{border:1px solid rgba(198,162,74,.24);background:#fff;border-radius:24px;padding:1rem;margin-top:1rem}.olx-availability-calendar b{display:block;color:#123529}.olx-availability-calendar small{color:#657168}.olx-date-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(82px,1fr));gap:.45rem;margin-top:.8rem}.olx-date-chip{border:1px solid rgba(198,162,74,.26);border-radius:16px;background:#fffaf0;padding:.55rem .45rem;text-align:center;color:#123529}.olx-date-chip b{font-size:.78rem}.olx-date-chip span{display:block;font-size:.62rem;text-transform:uppercase;letter-spacing:.08em;color:#657168}.olx-date-chip.available,.olx-date-chip.open{background:#ecfdf5;border-color:#86efac}.olx-date-chip.blocked,.olx-date-chip.closed{background:#fff1f2;border-color:#fecdd3}.olx-date-chip.is-selected{outline:3px solid rgba(198,162,74,.35);background:#123529;color:#fff}.olx-date-chip.is-selected span,.olx-date-chip.is-selected b{color:#fff}@keyframes olxStepIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}@keyframes olxMiniMorph{0%,100%{transform:scale(.96);opacity:.72}40%{transform:scale(1.04);opacity:1}}@keyframes olxPackageMorph{0%,11%{opacity:0;transform:scale(1.08) translateX(2%)}14%,25%{opacity:1;transform:scale(1) translateX(0)}31%,100%{opacity:0;transform:scale(1.06) translateX(-2%)}}@media(max-width:640px){.olx-builder-progress{grid-template-columns:repeat(2,minmax(0,1fr))}.olx-package-morph-show{height:130px}.olx-date-grid{grid-template-columns:repeat(3,minmax(0,1fr))}}

/* Safari Brief builder account handoff + cost lens */
.olx-email-lock-note{display:block;margin-top:.35rem;color:rgba(31,58,45,.58);font-size:.72rem;line-height:1.5;font-weight:750;text-transform:none;letter-spacing:0}
.olx-cost-estimate{margin-top:1rem;border:1px solid rgba(198,162,74,.34);border-radius:24px;background:linear-gradient(135deg,rgba(31,58,45,.96),rgba(15,39,27,.92));color:#fff8e6;padding:1rem;box-shadow:0 18px 50px rgba(31,58,45,.18)}
.olx-cost-estimate span{display:block;color:#e8d5a0;font-size:.68rem;font-weight:950;letter-spacing:.16em;text-transform:uppercase}
.olx-cost-estimate strong{display:block;margin:.25rem 0;font-family:Georgia,'Times New Roman',serif;font-size:clamp(1.65rem,3vw,2.3rem);line-height:1;color:#fff}
.olx-cost-estimate small{display:block;color:rgba(255,248,230,.72);line-height:1.55;font-weight:750}
.olx-booking-status .olx-brief-success-title{display:block;color:#123529;font-size:1rem;font-weight:950;margin-bottom:.2rem}
.olx-booking-status .olx-brief-success-actions{display:flex;flex-wrap:wrap;gap:.55rem;margin-top:.75rem}
.olx-booking-status .olx-brief-success-actions a,.olx-booking-status .olx-brief-success-actions button{display:inline-flex;align-items:center;justify-content:center;min-height:40px;border:0;border-radius:999px;padding:0 .9rem;text-decoration:none;font:inherit;font-size:.78rem;font-weight:950;cursor:pointer}
.olx-booking-status .olx-brief-success-actions a{background:#123529;color:#fff8e6}.olx-booking-status .olx-brief-success-actions button{background:rgba(31,58,45,.08);color:#123529}
@media(max-width:640px){.olx-booking-status .olx-brief-success-actions a,.olx-booking-status .olx-brief-success-actions button{width:100%}.olx-cost-estimate{border-radius:20px}}

</style>
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <sitemap><loc>https://www.oloropasafaris.com/sitemap-pages.php</loc></sitemap>
  <sitemap><loc>https://www.oloropasafaris.com/sitemap-destinations.php</loc></sitemap>
  <sitemap><loc>https://www.oloropasafaris.com/sitemap-packages.php</loc></sitemap>
  <sitemap><loc>https://www.oloropasafaris.com/sitemap-homes.php</loc></sitemap>
  <sitemap><loc>https://www.oloropasafaris.com/sitemap-stories.php</loc></sitemap>
</sitemapindex>

<script>document.documentElement.dataset.olPublicRoot="/";</script>
<script data-inlined-from="assets/js/oloropa-immersive-sitewide.js">
(() => {
  'use strict';
  if (window.__OLOROPA_IMMERSIVE_SITEWIDE__) return;
  window.__OLOROPA_IMMERSIVE_SITEWIDE__ = true;

  const doc = document;
  const body = doc.body;
  if (!body) return;
  const reduceMotion = typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;
  body.classList.add('olx-immersive-ready');

  const $ = (sel, base = doc) => base.querySelector(sel);
  const $$ = (sel, base = doc) => [...base.querySelectorAll(sel)];
  const rawPageName = (body.dataset.oloropaPage || location.pathname.split('/').pop() || 'index.php').toLowerCase();
  const pageName = normalizePage(rawPageName);
  const pageMeta = getPageMeta(pageName);
  body.dataset.olxTone = pageMeta.tone;
  body.dataset.olxTailoredPage = pageName;
  if (!body.dataset.oloropaPage) body.dataset.oloropaPage = pageName;

  function olxPublicUrl(path) {
    const cleanPath = String(path || '').replace(/^\/+/, '');
    const explicitRoot = (body.dataset.olPublicRoot || doc.documentElement.dataset.olPublicRoot || '').trim();
    if (explicitRoot) return explicitRoot.replace(/\/?$/, '/') + cleanPath;
    const parts = location.pathname.split('/').filter(Boolean);
    const sectionIndex = parts.findIndex(part => /^(admin|api|user|package|destination|home|story)$/i.test(part));
    if (sectionIndex >= 0) {
      const baseParts = parts.slice(0, sectionIndex);
      return '/' + (baseParts.length ? baseParts.join('/') + '/' : '') + cleanPath;
    }
    return cleanPath;
  }

  function normalizePage(name) {
    const cleaned = (name || 'index.php').split('?')[0].split('#')[0].toLowerCase();
    const aliases = {
      'index.html': 'index.php',
      'home.php': 'index.php',
      'destinations.php': 'destination.php',
      'destinations.html': 'destination.php',
      'destination.html': 'destination.php',
      'packages.html': 'packages.php',
      'services.html': 'services.php',
      'about.html': 'about.php',
      'contact.html': 'contact.php',
      'booking.html': 'booking.php',
      'stories.html': 'stories.php',
      'blog.php': 'stories.php',
      'story.php': 'view_story.php',
      'package.php': 'view_package.php',
      'serenity-homes.php': 'serenity-homes.html'
    };
    if (/serenity-home-\d+\.html/.test(cleaned) || cleaned.includes('view_home')) return 'serenity-home';
    if (cleaned.includes('view_package')) return 'view_package.php';
    if (cleaned.includes('view_destination')) return 'view_destination.php';
    if (cleaned.includes('view_story')) return 'view_story.php';
    return aliases[cleaned] || cleaned || 'index.php';
  }

  function getPageMeta(key) {
    const meta = {
      'index.php': {
        tone: 'story',
        eyebrow: 'Homepage story',
        title: 'A journey told in chapters.',
        copy: 'Move from first atmosphere to route fit, destination proof, and a private handoff.',
        primary: 'Start story brief',
        primaryHref: 'booking.php?context=homepage-story',
        secondary: 'Read stories',
        secondaryHref: 'stories.php'
      },
      'destination.php': {
        tone: 'atlas',
        eyebrow: 'Destination atlas',
        title: 'Choose by landscape, then route.',
        copy: 'Scan the places by mood, wildlife, coast, timing, and how they connect into a journey.',
        primary: 'Shape destination brief',
        primaryHref: 'booking.php?context=destination-atlas',
        secondary: 'Compare journeys',
        secondaryHref: 'packages.php'
      },
      'packages.php': {
        tone: 'atlas',
        eyebrow: 'Journey matcher',
        title: 'Compare routes by feeling and pace.',
        copy: 'Use the package page as a route board: wildlife intensity, privacy, coast recovery, and stay style.',
        primary: 'Save route brief',
        primaryHref: 'booking.php?context=package-matcher',
        secondary: 'Open atlas',
        secondaryHref: 'destination.php'
      },
      'services.php': {
        tone: 'brief',
        eyebrow: 'Planning studio',
        title: 'Turn scattered wishes into a route.',
        copy: 'This page frames what Oloropa designs: movement, stays, timing, privacy, and responsible handoff.',
        primary: 'Start planning',
        primaryHref: 'booking.php?context=planning-studio',
        secondary: 'Method',
        secondaryHref: 'about.php'
      },
      'about.php': {
        tone: 'brief',
        eyebrow: 'Method ledger',
        title: 'See how the journey gets shaped.',
        copy: 'Follow the planning method from local route intelligence to low-pressure private design.',
        primary: 'Use this method',
        primaryHref: 'booking.php?context=method-ledger',
        secondary: 'Services',
        secondaryHref: 'services.php'
      },
      'stories.php': {
        tone: 'story',
        eyebrow: 'Field notes',
        title: 'Read the route before you request it.',
        copy: 'Filter destination essays, planning logic, and stay inspiration into a stronger private brief.',
        primary: 'Brief from a story',
        primaryHref: 'booking.php?context=stories-field-notes',
        secondary: 'Open atlas',
        secondaryHref: 'destination.php'
      },
      'view_story.php': {
        tone: 'story',
        eyebrow: 'Story detail',
        title: 'Carry this idea into a real route.',
        copy: 'Use the article as route evidence: destination mood, season, pace, and the decision it suggests.',
        primary: 'Plan from story',
        primaryHref: 'booking.php?context=story-detail',
        secondary: 'More stories',
        secondaryHref: 'stories.php'
      },
      'view_package.php': {
        tone: 'atlas',
        eyebrow: 'Route detail',
        title: 'Test whether this journey fits.',
        copy: 'Read the sequence, comfort layer, and pace before turning the route into a private quote.',
        primary: 'Request this route',
        primaryHref: 'booking.php?context=package-detail',
        secondary: 'Compare routes',
        secondaryHref: 'packages.php'
      },
      'view_destination.php': {
        tone: 'atlas',
        eyebrow: 'Place detail',
        title: 'Anchor the route in one landscape.',
        copy: 'Use wildlife, stay, transfer, and season cues to decide whether this destination belongs in the journey.',
        primary: 'Plan this place',
        primaryHref: 'booking.php?context=destination-detail',
        secondary: 'All destinations',
        secondaryHref: 'destination.php'
      },
      'booking.php': {
        tone: 'brief',
        eyebrow: 'Private brief',
        title: 'Build one clean signal.',
        copy: 'Gather destination, pace, dates, people, stay style, and constraints into a useful planning brief.',
        primary: 'Continue brief',
        primaryHref: '#support-framework',
        secondary: 'Compare first',
        secondaryHref: 'packages.php'
      },
      'contact.php': {
        tone: 'brief',
        eyebrow: 'Concierge router',
        title: 'Send the right message to the right place.',
        copy: 'Route questions, private planning, and follow-up notes without losing the journey context.',
        primary: 'Open contact',
        primaryHref: '#support-framework',
        secondary: 'Start brief',
        secondaryHref: 'booking.php?context=contact-router'
      },
      'serenity-homes.html': {
        tone: 'coast',
        eyebrow: 'Private stays',
        title: 'Choose the stay that changes the pace.',
        copy: 'Read the home collection as a recovery layer for coast, family, privacy, and longer-stay journeys.',
        primary: 'Plan with a stay',
        primaryHref: 'booking.php?context=serenity-homes',
        secondary: 'Safari routes',
        secondaryHref: 'packages.php'
      },
      'serenity-home': {
        tone: 'coast',
        eyebrow: 'Stay detail',
        title: 'Fit the home to the journey.',
        copy: 'Use layout, privacy, location, and recovery rhythm to decide whether this stay belongs in the route.',
        primary: 'Request this stay',
        primaryHref: 'booking.php?context=serenity-home-detail',
        secondary: 'All stays',
        secondaryHref: 'serenity-homes.html'
      },
      'login.php': {
        tone: 'guard',
        eyebrow: 'Account access',
        title: 'Return to your private planning space.',
        copy: 'Sign in when you are ready to continue a brief, booking, or saved route conversation.',
        primary: 'Start new brief',
        primaryHref: 'booking.php?context=login-handoff',
        secondary: 'Home',
        secondaryHref: 'index.php'
      },
      'register.php': {
        tone: 'guard',
        eyebrow: 'Account setup',
        title: 'Create space for the journey details.',
        copy: 'Use an account when the route needs saved preferences, follow-up, and a quieter handoff.',
        primary: 'Start brief',
        primaryHref: 'booking.php?context=register-handoff',
        secondary: 'Home',
        secondaryHref: 'index.php'
      },
      'payment-success.php': {
        tone: 'guard',
        eyebrow: 'Payment received',
        title: 'The journey handoff can continue.',
        copy: 'Use this moment to return to the route or contact the team with any remaining details.',
        primary: 'Contact concierge',
        primaryHref: 'contact.php?reason=payment-success',
        secondary: 'Home',
        secondaryHref: 'index.php'
      },
      'payment-cancelled.php': {
        tone: 'guard',
        eyebrow: 'Payment paused',
        title: 'Nothing is lost; the route can be resumed.',
        copy: 'Return to the brief or ask the concierge team to help with the next step.',
        primary: 'Resume brief',
        primaryHref: 'booking.php?context=payment-paused',
        secondary: 'Contact',
        secondaryHref: 'contact.php?reason=payment-paused'
      },
      '404.php': {
        tone: 'guard',
        eyebrow: 'Route guard',
        title: 'The path is not on the map.',
        copy: 'Use the home gateway or concierge route if the page you expected has moved.',
        primary: 'Return home',
        primaryHref: 'index.php',
        secondary: 'Ask concierge',
        secondaryHref: 'contact.php?reason=route-help'
      },
      '500.php': {
        tone: 'guard',
        eyebrow: 'Route guard',
        title: 'The server paused the handoff.',
        copy: 'Try again from the home gateway or ask the concierge team to help complete the route.',
        primary: 'Return home',
        primaryHref: 'index.php',
        secondary: 'Ask concierge',
        secondaryHref: 'contact.php?reason=server-help'
      }
    };
    return meta[key] || {
      tone: 'brief',
      eyebrow: 'Oloropa page',
      title: 'Keep the journey context close.',
      copy: 'Use this page as one part of the private East Africa planning path.',
      primary: 'Start private brief',
      primaryHref: `booking.php?context=${encodeURIComponent(key || 'public-page')}`,
      secondary: 'Home',
      secondaryHref: 'index.php'
    };
  }

  function toast(message) {
    let node = $('.olx-brief-toast');
    if (!node) {
      node = doc.createElement('div');
      node.className = 'olx-brief-toast';
      node.setAttribute('role', 'status');
      node.setAttribute('aria-live', 'polite');
      body.appendChild(node);
    }
    node.textContent = message;
    node.classList.add('is-visible');
    clearTimeout(node._t);
    node._t = setTimeout(() => node.classList.remove('is-visible'), 2200);
  }

  function saveContext(patch) {
    try {
      const key = 'oloropaPhase11LeadContext';
      const old = JSON.parse(localStorage.getItem(key) || '{}');
      localStorage.setItem(key, JSON.stringify({ ...old, ...patch, updatedAt: new Date().toISOString() }));
      toast('Saved to your private brief.');
    } catch (_) {
      toast('Brief context noted for this page.');
    }
  }

  function bookingContextFromUrl(href) {
    try {
      const url = new URL(href, location.href);
      return url.searchParams.get('context') || url.searchParams.get('source') || pageName.replace(/\.(php|html)$/i, '');
    } catch (_) {
      return pageName.replace(/\.(php|html)$/i, '');
    }
  }

  function ensureBookingOptionField(form, name) {
    let field = form.querySelector(`input[name="${name}"]`);
    if (!field) {
      field = doc.createElement('input');
      field.type = 'hidden';
      field.name = name;
      form.appendChild(field);
    }
    return field;
  }

  function labelForBookingType(type) {
    if (type === 'package') return 'Routes';
    if (type === 'destination') return 'Locations';
    if (type === 'home') return 'Serenity stays';
    return 'Options';
  }

  function renderBookingOptions(panel, options, activeType = 'package') {
    const list = panel.querySelector('[data-olx-booking-option-list]');
    const tabs = panel.querySelectorAll('[data-olx-booking-option-tab]');
    tabs.forEach(tab => tab.classList.toggle('is-active', tab.dataset.olxBookingOptionTab === activeType));
    if (!list) return;
    const items = options[activeType] || [];
    if (!items.length) {
      list.innerHTML = `<p class="olx-booking-option-empty">No ${labelForBookingType(activeType).toLowerCase()} are published for booking yet.</p>`;
      return;
    }
    list.innerHTML = items.map(item => {
      const gallery = Array.isArray(item.gallery) && item.gallery.length ? item.gallery : (item.image ? [{src:item.image, caption:item.label || ''}] : []);
      const galleryStrip = gallery.slice(0, 4).map((g, idx) => `<i style="background-image:url('${String(g.src || '').replace(/'/g, '%27')}')" title="${String(g.caption || item.label || '').replace(/"/g, '&quot;')}"></i>`).join('');
      const days = Array.isArray(item.route_days) ? item.route_days : [];
      return `
      <button type="button" class="olx-booking-option ${activeType === 'package' ? 'is-route-morph' : ''}" data-olx-booking-option="${item.source_type}" data-olx-booking-option-id="${item.source_id}" data-olx-booking-option-label="${String(item.label || '').replace(/"/g, '&quot;')}">
        <span class="olx-booking-option-media" style="background-image:url('${item.image || ''}')"><span class="olx-package-morph-strip">${galleryStrip}</span></span>
        <span class="olx-booking-option-copy">
          <span>${item.eyebrow || labelForBookingType(activeType)}</span>
          <strong>${item.label || 'Private Oloropa brief'}</strong>
          <small>${item.summary || ''}</small>
          ${days.length ? `<small class="olx-route-order">${days.slice(0,5).map(d => d.location || d.title || ('Day '+d.day_number)).filter(Boolean).join(' → ')}</small>` : ''}
          ${item.price ? `<em>${item.price}</em>` : ''}
        </span>
      </button>`;
    }).join('');
  }

  function renderBookingItineraryPreview(panel, itinerary) {
    const node = panel.querySelector('[data-olx-booking-itinerary-preview]');
    if (!node) return;
    const source = itinerary.source || {};
    const days = Array.isArray(itinerary.days) ? itinerary.days.slice(0, 4) : [];
    if (!source.title && !days.length) {
      node.innerHTML = '';
      return;
    }
    const gallery = Array.isArray(itinerary.gallery) ? itinerary.gallery : (Array.isArray(source.gallery) ? source.gallery : []);
    const availability = Array.isArray(itinerary.availability) ? itinerary.availability : (Array.isArray(source.availability) ? source.availability : []);
    const calendar = availability.slice(0, 21).map(slot => `<button type="button" class="olx-date-chip ${String(slot.status || '').toLowerCase()}" data-date="${slot.date || ''}"><b>${slot.date ? new Date(slot.date + 'T00:00:00').toLocaleDateString(undefined,{month:'short',day:'numeric'}) : 'Date'}</b><span>${slot.status || 'request'}</span></button>`).join('');
    node.innerHTML = `
      <div class="olx-booking-itinerary-head">
        <span>Live itinerary preview</span>
        <strong>${source.title || 'Custom Oloropa route'}</strong>
      </div>
      ${gallery.length ? `<div class="olx-package-morph-show" aria-label="Package route image slideshow">${gallery.slice(0,8).map((g,i)=>`<figure style="--i:${i};background-image:url('${String(g.src || '').replace(/'/g,'%27')}')"><figcaption>${g.day ? 'Day '+g.day+' · ' : ''}${g.caption || source.title || 'Route image'}</figcaption></figure>`).join('')}</div>` : ''}
      <div class="olx-booking-itinerary-days">
        ${days.length ? days.map(day => `
          <article>
            <b>Day ${day.day_number || ''}</b>
            <span>${day.title || day.location || 'Route chapter'}</span>
            <small>${day.location ? day.location + ' · ' : ''}${day.drive_time || ''}</small>
            <small>${day.description || ''}</small>
          </article>
        `).join('') : '<p class="olx-booking-option-empty">This brief will be shaped by the safari desk after submission.</p>'}
      </div>
      <div class="olx-availability-calendar"><div><b>Availability lens</b><small>${availability.length ? 'Choose a preferred available day or request custom dates.' : 'Admin-controlled availability calendar can be enabled for this route.'}</small></div><div class="olx-date-grid">${calendar || '<span class="olx-booking-option-empty">No fixed dates published yet — request custom dates.</span>'}</div></div>
    `;
    node.querySelectorAll('[data-date]').forEach(btn => btn.addEventListener('click', () => {
      const start = document.getElementById('olx-booking-start');
      if (start && btn.dataset.date) { start.value = btn.dataset.date; start.dispatchEvent(new Event('input', {bubbles:true})); }
      node.querySelectorAll('[data-date]').forEach(x => x.classList.remove('is-selected'));
      btn.classList.add('is-selected');
    }));
  }

  async function enhanceBookingModalOptions(modal) {
    const form = modal.querySelector('form');
    if (!form || form.dataset.olxOptionsEnhanced === '1') return;
    form.dataset.olxOptionsEnhanced = '1';
    ensureBookingOptionField(form, 'source_type');
    ensureBookingOptionField(form, 'source_id');
    ensureBookingOptionField(form, 'booking_context');
    ensureBookingOptionField(form, 'builder_stage');
    ensureBookingOptionField(form, 'selected_route_media');

    const panel = doc.createElement('section');
    panel.className = 'olx-booking-options';
    panel.innerHTML = `
      <div class="olx-booking-options-head">
        <span>Step 1 · Choose journey foundation</span>
        <strong data-olx-booking-selection-label>Private route brief</strong>
      </div>
      <div class="olx-builder-progress" data-olx-builder-progress>
        <button type="button" class="is-active" data-step-jump="0"><b>1</b><span>Route</span></button>
        <button type="button" data-step-jump="1"><b>2</b><span>Dates</span></button>
        <button type="button" data-step-jump="2"><b>3</b><span>Guests</span></button>
        <button type="button" data-step-jump="3"><b>4</b><span>Confirm</span></button>
      </div>
      <div class="olx-booking-option-tabs" role="tablist" aria-label="Booking type">
        <button type="button" class="is-active" data-olx-booking-option-tab="package">Routes</button>
        <button type="button" data-olx-booking-option-tab="destination">Locations</button>
        <button type="button" data-olx-booking-option-tab="home">Serenity stays</button>
      </div>
      <div class="olx-booking-option-list" data-olx-booking-option-list>
        <p class="olx-booking-option-empty">Loading live route and location options...</p>
      </div>
      <div data-olx-booking-itinerary-preview></div>
    `;
    const firstField = form.querySelector('label, .olx-booking-field, .olx-booking-grid, textarea');
    if (firstField) form.insertBefore(panel, firstField);
    else form.prepend(panel);

    const state = { activeType: 'package', options: { package: [], destination: [], home: [] } };
    panel.querySelectorAll('[data-olx-booking-option-tab]').forEach(tab => {
      tab.addEventListener('click', () => {
        state.activeType = tab.dataset.olxBookingOptionTab || 'package';
        renderBookingOptions(panel, state.options, state.activeType);
      });
    });
    panel.addEventListener('click', event => {
      const option = event.target.closest('[data-olx-booking-option]');
      if (!option) return;
      panel.querySelectorAll('[data-olx-booking-option]').forEach(item => item.classList.remove('is-selected'));
      option.classList.add('is-selected');
      const sourceType = option.dataset.olxBookingOption || '';
      const sourceId = option.dataset.olxBookingOptionId || '';
      const label = option.dataset.olxBookingOptionLabel || 'Private Oloropa brief';
      ensureBookingOptionField(form, 'source_type').value = sourceType;
      ensureBookingOptionField(form, 'source_id').value = sourceId;
      ensureBookingOptionField(form, 'booking_context').value = label;
      ensureBookingOptionField(form, 'selected_route_media').value = option.querySelector('.olx-booking-option-media')?.style.backgroundImage || '';
      const selection = panel.querySelector('[data-olx-booking-selection-label]');
      if (selection) selection.textContent = label;
      modal.dataset.olxContext = label;
      fetch(olxPublicUrl(`api/itinerary-brief.php?source_type=${encodeURIComponent(sourceType)}&source_id=${encodeURIComponent(sourceId)}`), { credentials: 'same-origin' })
        .then(response => response.json())
        .then(payload => renderBookingItineraryPreview(panel, payload.data || {}))
        .catch(() => renderBookingItineraryPreview(panel, { source: { title: label }, days: [] }));
    });

    try {
      const response = await fetch(olxPublicUrl('api/booking-options.php'), { credentials: 'same-origin' });
      const payload = await response.json().catch(() => ({}));
      const data = payload.data || {};
      state.options = {
        package: Array.isArray(data.packages) ? data.packages : [],
        destination: Array.isArray(data.destinations) ? data.destinations : [],
        home: Array.isArray(data.homes) ? data.homes : []
      };
      renderBookingOptions(panel, state.options, state.activeType);
    } catch (error) {
      const list = panel.querySelector('[data-olx-booking-option-list]');
      if (list) list.innerHTML = '<p class="olx-booking-option-empty">Live options are offline. You can still send the brief manually.</p>';
    }
  }



  function olxEscapeHtml(value) {
    return String(value == null ? '' : value).replace(/[&<>"']/g, ch => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[ch]));
  }

  function olxBudgetBase(value) {
    const budget = String(value || '').toLowerCase();
    if (budget.includes('ultra')) return 5200;
    if (budget.includes('luxury')) return 3200;
    if (budget.includes('mid')) return 1900;
    if (budget.includes('value')) return 1200;
    return 1500;
  }

  function olxTripNights(start, end) {
    if (!start || !end) return 1;
    const a = new Date(String(start) + 'T00:00:00');
    const b = new Date(String(end) + 'T00:00:00');
    if (Number.isNaN(a.getTime()) || Number.isNaN(b.getTime()) || b <= a) return 1;
    return Math.max(1, Math.round((b - a) / 86400000));
  }

  function estimateOloropaCost(data) {
    const guests = Math.max(1, parseInt(data.guest_count || '1', 10) || 1);
    const nights = olxTripNights(data.travel_start_date, data.travel_end_date);
    const base = olxBudgetBase(data.budget_range);
    const itineraryFactor = Math.min(1.85, Math.max(1, nights / 5));
    return Math.round(base * guests * itineraryFactor);
  }

  function formatOloropaCurrency(amount, currency = 'USD') {
    try {
      return new Intl.NumberFormat(undefined, { style: 'currency', currency, maximumFractionDigits: 0 }).format(Number(amount || 0));
    } catch (error) {
      return `${currency} ${Math.round(Number(amount || 0)).toLocaleString()}`;
    }
  }

  function refreshBookingEstimate(modal) {
    const form = modal?.querySelector('[data-olx-booking-form]');
    const estimate = modal?.querySelector('[data-olx-cost-estimate]');
    if (!form || !estimate) return;
    const data = Object.fromEntries(new FormData(form).entries());
    const total = estimateOloropaCost(data);
    const guests = Math.max(1, parseInt(data.guest_count || '1', 10) || 1);
    const nights = olxTripNights(data.travel_start_date, data.travel_end_date);
    const range = data.budget_range ? String(data.budget_range).replace(/-/g, ' ') : 'planning range';
    estimate.innerHTML = `<span>Estimated planning lens</span><strong>${formatOloropaCurrency(total, data.currency || 'USD')}</strong><small>${guests} guest${guests === 1 ? '' : 's'} · ${nights} night${nights === 1 ? '' : 's'} · ${olxEscapeHtml(range)}. Final quote is confirmed by the safari desk.</small>`;
  }

  function jumpBookingStep(modal, target) {
    const progress = Array.from(modal.querySelectorAll('[data-step-jump]'));
    const button = progress.find(item => Number(item.dataset.stepJump) === Number(target));
    if (button) button.click();
  }

  function buildOnboardingUrl(data, payload) {
    const serverUrl = payload?.data?.onboarding_url || payload?.data?.redirect_url || '';
    if (serverUrl) return serverUrl;
    const params = new URLSearchParams();
    if (data.email) params.set('brief_email', data.email);
    if (payload?.data?.request_code || payload?.request_code) params.set('quote', payload.data?.request_code || payload.request_code);
    if (payload?.data?.booking_code) params.set('booking', payload.data.booking_code);
    params.set('next', 'user/my-bookings.php');
    params.set('lock_email', '1');
    return olxPublicUrl('register.php') + '?' + params.toString();
  }

  function setBookingStatusHtml(status, html) {
    if (!status) return;
    status.innerHTML = html;
  }

  function initBookingStepper(modal) {
    if (!modal || modal.dataset.olxStepper === '1') return;
    modal.dataset.olxStepper = '1';
    let step = 0;
    const pages = () => Array.from(modal.querySelectorAll('[data-step]'));
    const progress = () => Array.from(modal.querySelectorAll('[data-step-jump]'));
    const hiddenStage = () => modal.querySelector('input[name="builder_stage"]');
    function show(next) {
      const max = Math.max(0, pages().length - 1);
      step = Math.max(0, Math.min(max, Number(next) || 0));
      pages().forEach(p => p.classList.toggle('is-active', Number(p.dataset.step) === step));
      progress().forEach(p => p.classList.toggle('is-active', Number(p.dataset.stepJump) === step));
      const prev = modal.querySelector('[data-olx-step-prev]');
      const nextBtn = modal.querySelector('[data-olx-step-next]');
      if (prev) prev.disabled = step === 0;
      if (nextBtn) nextBtn.textContent = step === max ? 'Review details' : 'Continue';
      const stage = hiddenStage(); if (stage) stage.value = ['route','dates','travellers','confirm'][step] || String(step);
    }
    modal.addEventListener('click', event => {
      const jump = event.target.closest('[data-step-jump]');
      if (jump) show(jump.dataset.stepJump);
      if (event.target.closest('[data-olx-step-next]')) show(step + 1);
      if (event.target.closest('[data-olx-step-prev]')) show(step - 1);
    });
    show(0);
  }

  function createBookingModal() {
    let modal = $('.olx-booking-modal');
    if (modal) {
      enhanceBookingModalOptions(modal);
      return modal;
    }
    modal = doc.createElement('div');
    modal.className = 'olx-booking-modal';
    modal.setAttribute('role', 'dialog');
    modal.setAttribute('aria-modal', 'true');
    modal.setAttribute('aria-labelledby', 'olx-booking-title');
    modal.innerHTML = `
      <div class="olx-booking-dialog">
        <div class="olx-booking-head">
          <div>
            <span data-olx-booking-context>Private brief</span>
            <strong id="olx-booking-title">Start a private safari brief</strong>
            <p>Share the essentials. The planning team can shape destinations, packages, stays, dates, and guest needs from here.</p>
          </div>
          <button class="olx-booking-close" type="button" aria-label="Close booking brief" data-olx-booking-close>&times;</button>
        </div>
        <form class="olx-booking-form" data-olx-booking-form>
          <input type="hidden" name="source_page" value="">
          <input type="hidden" name="source_type" value="sitewide_modal">
          <div class="olx-step-pages" data-olx-step-pages>
            <section class="olx-step-page is-active" data-step="0"><h3>Step 1 · Route style</h3><p>Choose a package, destination, or stay above. The route preview and image morph board will update instantly.</p><div class="olx-booking-field full"><label for="olx-booking-message">Journey notes</label><textarea id="olx-booking-message" name="message" placeholder="Destinations, pace, accessibility needs, children, special dates, or anything already decided."></textarea></div></section>
            <section class="olx-step-page" data-step="1"><h3>Step 2 · Dates & availability</h3><div class="olx-booking-grid"><div class="olx-booking-field"><label for="olx-booking-start">Start date</label><input id="olx-booking-start" name="travel_start_date" type="date"></div><div class="olx-booking-field"><label for="olx-booking-end">End date</label><input id="olx-booking-end" name="travel_end_date" type="date"></div><div class="olx-booking-field"><label for="olx-booking-flexibility">Date flexibility</label><select id="olx-booking-flexibility" name="date_flexibility"><option value="exact">Exact dates</option><option value="few-days">Flexible by a few days</option><option value="month">Any time in selected month</option></select></div><div class="olx-booking-field"><label for="olx-booking-season">Preferred season</label><select id="olx-booking-season" name="preferred_season"><option value="">Use route recommendation</option><option>Great Migration</option><option>Green season</option><option>Beach extension</option><option>Festive season</option></select></div></div></section>
            <section class="olx-step-page" data-step="2"><h3>Step 3 · Travellers & budget</h3><div class="olx-booking-grid"><div class="olx-booking-field"><label for="olx-booking-guests">Guests</label><input id="olx-booking-guests" name="guest_count" type="number" min="1" value="2"></div><div class="olx-booking-field"><label for="olx-booking-budget">Budget range</label><select id="olx-booking-budget" name="budget_range"><option value="">Not sure yet</option><option value="value">Value conscious</option><option value="mid-range">Mid-range comfort</option><option value="luxury">Luxury private</option><option value="ultra-luxury">Ultra luxury</option></select></div><div class="olx-booking-field"><label for="olx-booking-privacy">Privacy level</label><select id="olx-booking-privacy" name="privacy_level"><option>Private safari</option><option>Small group</option><option>Family-only guide</option><option>Honeymoon privacy</option></select></div><div class="olx-booking-field"><label for="olx-booking-pace">Route pace</label><select id="olx-booking-pace" name="route_pace"><option>Balanced</option><option>Slow luxury</option><option>Wildlife intensive</option><option>Active adventure</option></select></div></div></section>
            <section class="olx-step-page" data-step="3"><h3>Step 4 · Contact & confirmation</h3><div class="olx-booking-grid"><div class="olx-booking-field"><label for="olx-booking-name">Full name</label><input id="olx-booking-name" name="full_name" autocomplete="name" required></div><div class="olx-booking-field"><label for="olx-booking-email">Brief / account email</label><input id="olx-booking-email" name="email" type="email" autocomplete="email" required><small class="olx-email-lock-note">This exact email will open your traveller portal. You can edit it here before sending; the create-account page will lock it for accuracy and offer an edit-email option.</small></div><div class="olx-booking-field"><label for="olx-booking-phone">Phone</label><input id="olx-booking-phone" name="phone" autocomplete="tel"></div><div class="olx-booking-field"><label for="olx-booking-contact">Contact method</label><select id="olx-booking-contact" name="preferred_contact_method"><option value="email">Email</option><option value="whatsapp">WhatsApp</option><option value="phone">Phone call</option></select></div></div><div class="olx-cost-estimate" data-olx-cost-estimate><span>Estimated planning lens</span><strong>Choose dates and budget</strong><small>The safari desk will confirm the final quote after reviewing route, season, stays, and availability.</small></div></section>
          </div>
          <div class="olx-step-controls"><button type="button" data-olx-step-prev>Back</button><button type="button" data-olx-step-next>Continue</button></div>
          <p class="olx-booking-status" data-olx-booking-status></p>
          <div class="olx-booking-actions">
            <button type="button" data-olx-booking-close>Keep browsing</button>
            <button type="submit">Send private brief</button>
          </div>
        </form>
      </div>
    `;
    body.appendChild(modal);
    modal.addEventListener('input', () => refreshBookingEstimate(modal));
    modal.addEventListener('change', () => refreshBookingEstimate(modal));
    refreshBookingEstimate(modal);

    const close = () => {
      modal.classList.remove('is-open');
      body.classList.remove('olx-booking-lock');
      const trigger = modal._trigger;
      if (trigger && typeof trigger.focus === 'function') trigger.focus({ preventScroll: true });
    };
    modal.addEventListener('click', (event) => {
      if (event.target === modal || event.target.closest('[data-olx-booking-close]')) close();
    });
    doc.addEventListener('keydown', (event) => {
      if (event.key === 'Escape' && modal.classList.contains('is-open')) close();
    });
    $('[data-olx-booking-form]', modal)?.addEventListener('submit', async (event) => {
      event.preventDefault();
      const form = event.currentTarget;
      const status = $('[data-olx-booking-status]', modal);
      const submit = form.querySelector('button[type="submit"]');
      const data = Object.fromEntries(new FormData(form).entries());
      data.source_page = data.source_page || `${location.pathname}${location.search}`;
      data.message = [data.message, modal.dataset.olxContext ? `Context: ${modal.dataset.olxContext}` : ''].filter(Boolean).join('\n\n');
      if (data.source_type === 'package' && data.source_id) data.package_id = data.source_id;
      if (data.source_type === 'destination' && data.source_id) data.destination_id = data.source_id;
      if (data.source_type === 'home' && data.source_id) data.home_id = data.source_id;
      data.currency = data.currency || 'USD';
      data.estimated_cost = String(estimateOloropaCost(data));
      data.portal_next = 'user/my-bookings.php';
      if (status) status.textContent = 'Sending your private brief and preparing your traveller portal handoff...';
      if (submit) submit.disabled = true;
      try {
        const response = await fetch(olxPublicUrl('api/quote-submit.php'), {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(data)
        });
        const payload = await response.json().catch(() => ({}));
        if (!response.ok || payload.ok === false) throw new Error(payload.message || 'Could not send the brief yet.');
        saveContext({ lastQuoteRequest: payload.data?.request_code || payload.request_code || 'submitted', lastQuotePage: pageName, lastBriefEmail: data.email || '' });
        const onboardingUrl = buildOnboardingUrl(data, payload);
        let cancelledRedirect = false;
        setBookingStatusHtml(status, `<span class="olx-brief-success-title">Brief received.</span><span>Your account email is locked to <b>${olxEscapeHtml(data.email || '')}</b> so your booking status and calculated cost appear in the right traveller portal.</span><span class="olx-brief-success-actions"><a href="${olxEscapeHtml(onboardingUrl)}">Create account with this email</a><button type="button" data-olx-edit-brief-email>Edit email first</button></span>`);
        status?.querySelector('[data-olx-edit-brief-email]')?.addEventListener('click', () => {
          cancelledRedirect = true;
          if (submit) submit.disabled = false;
          jumpBookingStep(modal, 3);
          const email = $('#olx-booking-email', modal);
          if (email) email.focus({ preventScroll: false });
        });
        setTimeout(() => { if (!cancelledRedirect && modal.classList.contains('is-open')) window.location.href = onboardingUrl; }, 1800);
      } catch (error) {
        if (status) status.textContent = error.message || 'Could not send the brief yet. Please try again.';
        if (submit) submit.disabled = false;
      }
    });
    initBookingStepper(modal);
    enhanceBookingModalOptions(modal);
    return modal;
  }

  function openBookingModal(context = {}, trigger = null) {
    const modal = createBookingModal();
    const label = context.label || context.context || pageMeta.eyebrow || 'Private brief';
    const hiddenSource = modal.querySelector('input[name="source_page"]');
    const title = $('#olx-booking-title', modal);
    const contextNode = $('[data-olx-booking-context]', modal);
    modal._trigger = trigger;
    modal.dataset.olxContext = label;
    if (hiddenSource) hiddenSource.value = `${location.pathname}${location.search}`;
    if (contextNode) contextNode.textContent = label.replace(/[-_]/g, ' ');
    if (title) title.textContent = context.title || pageMeta.primary || 'Start a private safari brief';
    modal.classList.add('is-open');
    body.classList.add('olx-booking-lock');
    setTimeout(() => $('#olx-booking-name', modal)?.focus({ preventScroll: true }), 30);
  }

  function isSerenityRoute() {
    const path = location.pathname.toLowerCase();
    return pageName.includes('serenity') || path.includes('serenity-home') || path.includes('/home/');
  }

  function renderSerenityLiveRail(data) {
    const homes = Array.isArray(data) ? data : (data ? [data] : []);
    if (!homes.length || doc.querySelector('[data-olx-serenity-live-rail]')) return;
    const host = $('main') || body;
    const rail = doc.createElement('section');
    rail.dataset.olxSerenityLiveRail = '1';
    rail.className = 'olx-serenity-live-rail';
    rail.innerHTML = `
      <div class="olx-serenity-live-head">
        <span>Live Serenity inventory</span>
        <strong>${homes.length === 1 ? homes[0].title : `${homes.length} stays available`}</strong>
      </div>
      <div class="olx-serenity-live-grid">
        ${homes.slice(0, 6).map(home => `
          <article>
            <span style="background-image:url('${home.hero_media || 'assets/media/stays/bnb-1-1.jpg'}')"></span>
            <div>
              <small>${home.location || 'Kenya Coast'} / ${home.price_label || 'Stay brief required'}</small>
              <b>${home.title || 'Serenity Home'}</b>
              <p>${home.summary || ''}</p>
              <button type="button" data-ol-open-booking-modal data-ol-booking-context="${String(home.title || 'Serenity stay').replace(/"/g, '&quot;')}">Request this stay</button>
            </div>
          </article>
        `).join('')}
      </div>
    `;
    host.appendChild(rail);
  }

  async function hydrateSerenityPages() {
    if (!isSerenityRoute()) return;
    try {
      const clean = location.pathname.split('/').filter(Boolean);
      const homeIndex = clean.findIndex(part => part.toLowerCase() === 'home');
      const slug = homeIndex >= 0 && clean[homeIndex + 1] ? clean[homeIndex + 1] : '';
      const url = slug ? olxPublicUrl(`api/serenity-homes-live.php?slug=${encodeURIComponent(slug)}`) : olxPublicUrl('api/serenity-homes-live.php');
      const response = await fetch(url, { credentials: 'same-origin' });
      const payload = await response.json().catch(() => ({}));
      if (!response.ok || payload.ok === false) return;
      window.OLOROPA_SERENITY_HOMES_LIVE = payload.data;
      renderSerenityLiveRail(payload.data);
    } catch (error) {}
  }

  function progress() {
    const max = doc.documentElement.scrollHeight - innerHeight;
    body.style.setProperty('--olx-progress', max > 0 ? Math.min(1, Math.max(0, scrollY / max)).toFixed(4) : 0);
  }
  addEventListener('scroll', progress, { passive: true });
  addEventListener('resize', progress, { passive: true });
  progress();

  if (!reduceMotion) {
    doc.addEventListener('pointermove', (event) => {
      body.style.setProperty('--olx-mouse-x', `${event.clientX}px`);
      body.style.setProperty('--olx-mouse-y', `${event.clientY}px`);
    }, { passive: true });
  }

  const revealTargets = $$('main > section, main > article, main > aside, .ol-recovery-story-card, .p11-card, .pk-card, .pd-card, .ix-card, .sp-card, .cct-tool-stage').filter(Boolean);
  revealTargets.forEach((node) => node.classList.add('olx-reveal'));
  if ('IntersectionObserver' in window) {
    const io = new IntersectionObserver((entries) => {
      entries.forEach((entry) => entry.target.classList.toggle('is-visible', entry.isIntersecting));
    }, { threshold: .12, rootMargin: '0px 0px -8% 0px' });
    revealTargets.forEach((node) => io.observe(node));
  } else {
    revealTargets.forEach((node) => node.classList.add('is-visible'));
  }

  $$('figure, article, .p11-card, .pk-card, .pd-card, .ix-intent-card, .ol-recovery-story-card').forEach((node) => {
    if (node.querySelector('img,video')) node.classList.add('olx-media-awake');
  });

  function createDock() {
    if ($('.olx-command-dock')) return;
    const dock = doc.createElement('div');
    dock.className = 'olx-command-dock';
    dock.innerHTML = `
      <a href="${pageMeta.primaryHref}">${pageMeta.primary}</a>
      <button type="button" data-olx-save-page>Save page</button>
      <button type="button" data-olx-top>Top</button>
    `;
    body.appendChild(dock);
    $('[data-olx-save-page]', dock)?.addEventListener('click', () => saveContext({
      savedPage: pageName,
      savedPageUrl: location.href,
      savedPageIntent: pageMeta.eyebrow
    }));
    $('[data-olx-top]', dock)?.addEventListener('click', () => scrollTo({ top: 0, behavior: reduceMotion ? 'auto' : 'smooth' }));
  }
  createDock();

  function createPageGuide() {
    if ($('.olx-page-guide')) return;
    const sections = $$('main > section, main > article, main > aside, section[id], article[id]')
      .filter((node) => node.offsetParent !== null)
      .slice(0, 6);
    const guide = doc.createElement('aside');
    guide.className = 'olx-page-guide';
    guide.setAttribute('aria-label', `${pageMeta.eyebrow} guide`);
    guide.innerHTML = `
      <div class="olx-page-guide-head">
        <div>
          <span data-olx-guide-eyebrow>${pageMeta.eyebrow}</span>
          <strong data-olx-guide-title>${pageMeta.title}</strong>
        </div>
        <button type="button" aria-label="Minimize page guide" aria-expanded="true" data-olx-minimize-guide>-</button>
      </div>
      <p data-olx-guide-copy>${pageMeta.copy}</p>
      <div class="olx-page-actions">
        <a href="${pageMeta.primaryHref}">${pageMeta.primary}</a>
        <a href="${pageMeta.secondaryHref}">${pageMeta.secondary}</a>
      </div>
      <div class="olx-section-pips" aria-hidden="true">${sections.map((_, index) => `<i class="${index === 0 ? 'is-active' : ''}"></i>`).join('')}</div>
    `;
    body.appendChild(guide);

    const toggle = $('[data-olx-minimize-guide]', guide);
    toggle?.addEventListener('click', () => {
      const isMinimized = guide.classList.toggle('is-minimized');
      toggle.setAttribute('aria-expanded', String(!isMinimized));
      toggle.textContent = isMinimized ? '+' : '-';
    });

    if (!sections.length || !('IntersectionObserver' in window)) return;
    const pips = $$('.olx-section-pips i', guide);
    const guideTitle = $('[data-olx-guide-title]', guide);
    const guideCopy = $('[data-olx-guide-copy]', guide);
    const io = new IntersectionObserver((entries) => {
      const visible = entries.filter((entry) => entry.isIntersecting).sort((a, b) => b.intersectionRatio - a.intersectionRatio)[0];
      if (!visible) return;
      const index = sections.indexOf(visible.target);
      pips.forEach((pip, pipIndex) => pip.classList.toggle('is-active', pipIndex === index));
      const heading = visible.target.querySelector('h1,h2,h3');
      const paragraph = visible.target.querySelector('p');
      if (heading && guideTitle) guideTitle.textContent = heading.textContent.trim().slice(0, 72);
      if (paragraph && guideCopy) guideCopy.textContent = paragraph.textContent.trim().slice(0, 138);
    }, { threshold: [.22, .48, .72], rootMargin: '-12% 0px -48% 0px' });
    sections.forEach((section) => io.observe(section));
  }
  createPageGuide();

  doc.addEventListener('click', (event) => {
    const trigger = event.target.closest('[data-ol-open-booking-modal],a[href*="booking.php"],a[href*="booking.html"],a[href*="context=package"],a[href*="context=destination"],a[href*="context=google-route-map"],[data-ol-phase1-cta="brief"]');
    if (!trigger || trigger.hasAttribute('data-ol-booking-full-page')) return;
    if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || trigger.target === '_blank') return;
    const href = trigger.getAttribute('href') || '';
    const isBookingHref = /booking\.(php|html)/i.test(href) || trigger.hasAttribute('data-ol-open-booking-modal') || trigger.dataset.olPhase1Cta === 'brief';
    if (!isBookingHref) return;
    event.preventDefault();
    openBookingModal({
      context: trigger.dataset.olBookingContext || bookingContextFromUrl(href),
      title: trigger.textContent.trim() || pageMeta.primary
    }, trigger);
  });

  doc.addEventListener('click', (event) => {
    const target = event.target.closest('[data-ix-context],[data-crm-context],[data-save-route]');
    if (!target) return;
    let patch = {};
    try { patch = JSON.parse(target.dataset.ixContext || target.dataset.crmContext || '{}'); } catch (_) {}
    if (target.dataset.saveRoute) patch.routeInterest = target.dataset.saveRoute;
    if (Object.keys(patch).length) saveContext(patch);
  });

  function indexStorytelling() {
    if (pageName !== 'index.php') return;
    const sections = [
      ['Arrival', '.ix-hero', 'A private journey begins with atmosphere, not a form.'],
      ['Intent', '#live-command', 'Choose the feeling first, then let the route follow.'],
      ['Match', '#route-match', 'The best safari is a fit between pace, place, and people.'],
      ['Proof', '#destination-scroll, #destination-atlas, #packages', 'Landscape, stay, and movement become one route story.'],
      ['Handoff', '#final-decision, footer', 'The last step is a quiet private brief, not pressure.']
    ];
    const available = sections.map(([label, selector, copy], index) => {
      const node = $(selector.split(',')[0]) || $(selector);
      return node ? { label, selector, copy, node, index } : null;
    }).filter(Boolean);
    if (!available.length || $('.olx-story-rail')) return;
    const rail = doc.createElement('nav');
    rail.className = 'olx-story-rail';
    rail.setAttribute('aria-label', 'Homepage story chapters');
    rail.innerHTML = available.map((item) => `<button type="button" data-story-index="${item.index}"><i>${item.index + 1}</i><span>${item.label}</span></button>`).join('');
    const caption = doc.createElement('aside');
    caption.className = 'olx-story-caption';
    caption.setAttribute('aria-live', 'polite');
    caption.innerHTML = '<span>Oloropa story</span><strong>Arrival</strong><p>A private journey begins with atmosphere, not a form.</p>';
    body.append(rail, caption);
    const buttons = $$('button', rail);
    buttons.forEach((button) => {
      button.addEventListener('click', () => {
        const item = available.find((entry) => String(entry.index) === button.dataset.storyIndex);
        if (item) item.node.scrollIntoView({ behavior: reduceMotion ? 'auto' : 'smooth', block: 'start' });
      });
    });
    const setActive = (item) => {
      buttons.forEach((button) => button.classList.toggle('is-active', button.dataset.storyIndex === String(item.index)));
      caption.innerHTML = `<span>Oloropa story</span><strong>${item.label}</strong><p>${item.copy}</p>`;
      body.dataset.olxStoryChapter = item.label.toLowerCase();
    };
    const io = new IntersectionObserver((entries) => {
      const visible = entries.filter((entry) => entry.isIntersecting).sort((a, b) => b.intersectionRatio - a.intersectionRatio)[0];
      if (!visible) return;
      const item = available.find((entry) => entry.node === visible.target);
      if (item) setActive(item);
    }, { threshold: [.2, .45, .7] });
    available.forEach((item) => io.observe(item.node));
    setActive(available[0]);
  }
  indexStorytelling();
  window.OloropaBookingModal = {
    open: openBookingModal,
    create: createBookingModal
  };
  hydrateSerenityPages();
})();

/* Oloropa location pages — automatic hero video + ambient sound handoff */
(function () {
  'use strict';
  if (window.__OLOROPA_LOCATION_AUTO_MEDIA_ACTIVE__) return;
  window.__OLOROPA_LOCATION_AUTO_MEDIA_ACTIVE__ = true;

  const doc = document;
  const body = doc.body || doc.documentElement;
  const reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  const path = (location.pathname || '').toLowerCase();
  const isLocationLikePage = () => {
    if (/destination|view_destination|serenity-home|serenity_homes|location/.test(path)) return true;
    if (body && /destination|location|sensory|atlas|serenity/i.test((body.dataset && Object.values(body.dataset).join(' ')) || '')) return true;
    return Boolean(doc.querySelector('.p13-hero,.p8-hero,.ol14-hero,.p12-location-hero,[data-ol21-phase6-hero-media="assigned"],[data-ol-location-auto-media]'));
  };
  if (!isLocationLikePage()) return;

  const fallbackMedia = {
    'masai-mara': {
      video: 'assets/media/cinematic/masai-mara-hero.mp4',
      audio: 'assets/media/cinematic/savannah-ambient.mp3',
      poster: 'assets/media/cinematic/photos/golden grass migration theatre Masai Mara.avif',
      label: 'Mara Dawn · birds, grass, distant wind'
    },
    'amboseli': {
      video: 'assets/media/cinematic/amboseli.mp4',
      audio: 'assets/media/cinematic/amboseli-audio.mp3',
      poster: 'assets/media/cinematic/photos/Kilimanjaro elephant procession Amboseli.avif',
      label: 'Amboseli Dawn · elephant grass, breeze, distant calls'
    },
    'samburu': {
      video: 'assets/media/cinematic/samburu.mp4',
      audio: 'assets/media/cinematic/savannah-ambient.mp3',
      poster: 'assets/media/cinematic/photos/riverbend sunrise Samburu.avif',
      label: 'Samburu River · wind, sand, birds'
    },
    'lake-nakuru': {
      video: 'assets/media/cinematic/lake-nakuru.mp4',
      audio: 'assets/media/cinematic/savannah-ambient.mp3',
      poster: 'assets/media/cinematic/photos/Rift lake mirror flamingo light Lake Nakuru.avif',
      label: 'Rift Lake · birds, wind, soft water'
    },
    'nairobi-national-park': {
      video: 'assets/media/cinematic/nairobi-national-park.mp4',
      audio: 'assets/media/cinematic/savannah-ambient.mp3',
      poster: 'assets/media/cinematic/photos/skyline-meets-savannah frame Nairobi National Park.avif',
      label: 'Nairobi Edge · birds, traffic hush, grass'
    },
    'tsavo-east': {
      video: 'assets/media/cinematic/tsavo-east.mp4',
      audio: 'assets/media/cinematic/savannah-ambient.mp3',
      poster: 'assets/media/cinematic/photos/red-earth wide Tsavo East.avif',
      label: 'Tsavo Wind · red earth, heat, distance'
    },
    'diani-beach': {
      video: 'assets/media/cinematic/diani-beach.mp4',
      audio: 'assets/media/cinematic/coast-ambient.mp3',
      poster: 'assets/media/cinematic/photos/palm shadow white sand Diani Beach.avif',
      label: 'Diani Tide · reef, palms, soft surf'
    },
    'watamu-marine-park': {
      video: 'assets/media/cinematic/watamu.mp4',
      audio: 'assets/media/cinematic/coast-ambient.mp3',
      poster: 'assets/media/cinematic/photos/reef aerial Watamu.avif',
      label: 'Watamu Reef · tide, wind, reef birds'
    },
    'shimba-hills': {
      video: 'assets/media/cinematic/shimba-hills.mp4',
      audio: 'assets/media/cinematic/savannah-ambient.mp3',
      poster: 'assets/media/cinematic/photos/waterfall mist trail Shimba Hills.avif',
      label: 'Shimba Forest · mist, birds, leaves'
    },
    'serenity': {
      video: 'assets/media/cinematic/serenity-home.mp4',
      audio: 'assets/media/cinematic/coast-ambient.mp3',
      poster: 'assets/media/stays/bnb-1-1.jpg',
      label: 'Private Stay Ambience · coast, air, quiet pool light'
    },
    'atlas': {
      video: 'assets/media/cinematic/amboseli.mp4',
      audio: 'assets/media/cinematic/savannah-ambient.mp3',
      poster: 'assets/media/cinematic/photos/Kilimanjaro elephant procession Amboseli.avif',
      label: 'Kenya Atlas · wind, birds, low field air'
    }
  };

  function normalizeKey(input) {
    const value = String(input || '').toLowerCase().trim();
    if (!value) return '';
    if (value.includes('masai') || value.includes('mara')) return 'masai-mara';
    if (value.includes('amboseli')) return 'amboseli';
    if (value.includes('samburu')) return 'samburu';
    if (value.includes('nakuru')) return 'lake-nakuru';
    if (value.includes('nairobi')) return 'nairobi-national-park';
    if (value.includes('tsavo')) return 'tsavo-east';
    if (value.includes('diani')) return 'diani-beach';
    if (value.includes('watamu')) return 'watamu-marine-park';
    if (value.includes('shimba')) return 'shimba-hills';
    if (value.includes('serenity') || value.includes('home') || value.includes('stay')) return 'serenity';
    if (value.includes('destination') || value.includes('atlas')) return 'atlas';
    return value.replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
  }

  function datasetValue(node, keys) {
    if (!node || !node.dataset) return '';
    for (const key of keys) {
      if (node.dataset[key]) return node.dataset[key];
    }
    return '';
  }

  function cssUrlToValue(value) {
    const raw = String(value || '').trim();
    const match = raw.match(/url\((['"]?)(.*?)\1\)/i);
    return match ? match[2] : '';
  }

  function firstMeaningful(values) {
    for (const value of values) {
      const text = String(value || '').trim();
      if (text && text !== 'none' && text !== 'null' && text !== 'undefined') return text;
    }
    return '';
  }

  function pageKey(hero) {
    const dataText = [
      datasetValue(hero, ['ol21P6PrimaryDestination', 'ol21P5DestinationKey', 'ol21P4DestinationKey', 'p13World', 'destination']),
      datasetValue(body, ['olDestinationKey', 'ol21P6PrimaryDestination', 'ol21P5DestinationKey', 'ol21P4DestinationKey', 'olPhase5DestinationName', 'olSensoryDestination', 'oloropaPage']),
      doc.querySelector('main[data-p13-world]')?.dataset.p13World,
      doc.querySelector('main[data-destination]')?.dataset.destination,
      doc.querySelector('h1')?.textContent,
      path
    ].join(' ');
    const normalized = normalizeKey(dataText);
    return fallbackMedia[normalized] ? normalized : (path.includes('destination') ? 'atlas' : normalized);
  }

  function resolveMedia(hero) {
    const key = pageKey(hero);
    const fallback = fallbackMedia[key] || fallbackMedia.atlas;
    const main = doc.querySelector('main[data-sound],main[data-p13-world]');
    const computedPoster = cssUrlToValue(getComputedStyle(hero).getPropertyValue('--p13-hero')) || cssUrlToValue(getComputedStyle(main || hero).getPropertyValue('--p13-hero'));
    const video = firstMeaningful([
      datasetValue(hero, ['ol21P6HeroVideo', 'ol21P5HeroVideo', 'ol21P4HeroVideo', 'routeVideo', 'heroVideo']),
      datasetValue(body, ['ol21P6HeroVideo', 'ol21P5HeroVideo', 'ol21P4HeroVideo', 'olSensoryVideo']),
      fallback.video
    ]);
    const audio = firstMeaningful([
      datasetValue(hero, ['ol21P6HeroSound', 'ol21P5HeroSound', 'ol21P4HeroSound', 'routeAudio', 'heroSound']),
      datasetValue(main, ['sound']),
      datasetValue(body, ['olSensoryAudio', 'olPhase5Audio', 'ol21P6HeroSound', 'ol21P5HeroSound', 'ol21P4HeroSound']),
      fallback.audio
    ]);
    const poster = firstMeaningful([
      datasetValue(hero, ['ol21P6HeroPoster', 'ol21P5HeroPoster', 'ol21P4HeroPoster', 'heroPoster']),
      computedPoster,
      hero.querySelector('video')?.getAttribute('poster'),
      doc.querySelector('meta[property="og:image"]')?.getAttribute('content'),
      fallback.poster
    ]);
    const label = firstMeaningful([
      datasetValue(hero, ['ol21P6SoundLabel', 'ol21P5SoundLabel', 'ol21P4SoundLabel', 'routeSoundLabel', 'soundLabel']),
      datasetValue(body, ['ol21P6SoundLabel', 'ol21P5SoundLabel', 'ol21P4SoundLabel']),
      fallback.label,
      'Oloropa location ambience'
    ]);
    return { key, video, audio, poster, label };
  }

  function ensureStyles() {
    if (doc.getElementById('oloropa-location-auto-media-css')) return;
    const style = doc.createElement('style');
    style.id = 'oloropa-location-auto-media-css';
    style.textContent = `
      .ol-location-auto-media-ready video[data-ol-location-auto-video]{opacity:1;transition:opacity .9s ease,filter .9s ease,transform 1.6s cubic-bezier(.19,1,.22,1)}
      :is(.story-hero,.sp-hero,.cct-hero,.pk4-hero,.pd5-hero,.p6-hero,.sh7-hero) > video[data-ol-location-auto-video]{position:absolute!important;inset:0!important;width:100%!important;height:100%!important;object-fit:cover!important;z-index:0!important}
      .ol-location-auto-media-ready.is-location-video-playing video[data-ol-location-auto-video]{filter:saturate(1.17) contrast(1.08) brightness(.82)!important;transform:scale(1.015)!important}
      .ol-es-sound-dock{position:fixed;right:clamp(14px,2vw,26px);bottom:clamp(14px,2vw,26px);z-index:1200;display:flex;align-items:center;gap:.55rem;border:1px solid rgba(255,255,255,.18);border-radius:999px;background:rgba(9,30,21,.86);color:#fff;backdrop-filter:blur(18px);box-shadow:0 20px 60px rgba(0,0,0,.28);padding:.55rem .75rem;font:800 11px/1 Inter,system-ui,sans-serif;letter-spacing:.08em;text-transform:uppercase;max-width:min(360px,calc(100vw - 28px))}
      .ol-es-sound-dock button{display:grid;place-items:center;width:34px;height:34px;border:0;border-radius:999px;background:linear-gradient(135deg,#C6A24A,#F2D994);color:#10251B;cursor:pointer;font-weight:950;box-shadow:0 10px 25px rgba(198,162,74,.24)}
      .ol-es-sound-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:rgba(255,255,255,.86);max-width:180px}
      .ol-es-sound-wave{height:22px;display:flex!important;align-items:center;gap:3px}
      .ol-es-sound-wave i{display:block;width:3px;height:45%;border-radius:999px;background:rgba(198,162,74,.72);animation:olEsWave 1.1s ease-in-out infinite;opacity:.85}
      .ol-es-sound-wave i:nth-child(2){animation-delay:.12s;height:70%}.ol-es-sound-wave i:nth-child(3){animation-delay:.24s;height:95%}.ol-es-sound-wave i:nth-child(4){animation-delay:.36s;height:62%}.ol-es-sound-wave i:nth-child(5){animation-delay:.48s;height:38%}
      .ol-es-sound-dock[data-state="muted"] .ol-es-sound-wave i,.ol-es-sound-dock[data-ol-es-muted="true"] .ol-es-sound-wave i{animation:none;opacity:.36}
      @keyframes olEsWave{50%{transform:scaleY(.35);opacity:.44}}
      .ol-es-sound-dock[data-state="playing"] .ol-es-sound-wave i{background:#C6A24A;box-shadow:0 0 12px rgba(198,162,74,.32)}
      .ol-es-sound-dock[data-state="blocked"] span::after{content:' · tap anywhere';color:rgba(242,217,148,.9)}
      @media(max-width:720px){.ol-es-sound-dock{right:14px;bottom:18px;justify-content:flex-start}.ol-es-sound-dock span{max-width:calc(100vw - 112px)}}
    `;
    doc.head.appendChild(style);
  }

  function ensureVideo(hero, media) {
    if (!media.video || reducedMotion) return null;
    let video = hero.querySelector(':scope > video, .p13-hero-video, .p13-immersive-bg-video, .p8-hero-media video, .p11-hero-video video, .p12-bg-video, video[data-ol-location-auto-video]');
    if (!video) {
      video = doc.createElement('video');
      video.className = hero.classList.contains('p8-hero') ? 'p8-hero-video' : 'p13-hero-video p13-immersive-bg-video';
      video.setAttribute('aria-hidden', 'true');
      video.muted = true;
      hero.insertBefore(video, hero.firstChild);
    }
    video.dataset.olLocationAutoVideo = 'true';
    video.muted = true;
    video.loop = true;
    video.autoplay = true;
    video.playsInline = true;
    video.defaultMuted = true;
    video.setAttribute('muted', '');
    video.setAttribute('loop', '');
    video.setAttribute('autoplay', '');
    video.setAttribute('playsinline', '');
    video.setAttribute('webkit-playsinline', '');
    video.setAttribute('preload', 'auto');
    if (media.poster) video.setAttribute('poster', media.poster);
    let source = video.querySelector('source');
    if (!source) {
      source = doc.createElement('source');
      source.type = 'video/mp4';
      video.appendChild(source);
    }
    if (source.getAttribute('src') !== media.video) {
      source.setAttribute('src', media.video);
      try { video.load(); } catch (_) {}
    }
    video.addEventListener('playing', () => hero.classList.add('is-location-video-playing'), { passive: true });
    video.addEventListener('pause', () => hero.classList.remove('is-location-video-playing'), { passive: true });
    video.addEventListener('error', () => {
      if (media.poster) hero.style.setProperty('--p13-hero', `url('${media.poster}')`);
      hero.classList.add('is-location-video-fallback');
    }, { passive: true });
    return video;
  }

  const audioState = {
    audio: null,
    src: '',
    label: '',
    playing: false,
    userMuted: localStorage.getItem('oloropa.location.ambient.muted') === 'true'
  };

  function ensureChip(media) {
    doc.querySelectorAll('.sp-ambient,.p8-sound-panel,[data-sp-ambient],.ol-location-auto-audio-chip').forEach((node) => {
      if (!node.classList.contains('ol-es-sound-dock')) node.remove();
    });
    let chip = doc.querySelector('.ol-es-sound-dock');
    if (!chip) {
      chip = doc.createElement('aside');
      chip.className = 'ol-es-sound-dock';
      chip.setAttribute('data-ol-es-muted', 'true');
      chip.setAttribute('aria-live', 'polite');
      chip.innerHTML = '<button type="button" aria-pressed="false" aria-label="Enable Oloropa soundscape">♪</button><span class="ol-es-sound-wave" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i></span><span class="ol-es-sound-label">Muted · Coast Wind</span>';
      doc.body.appendChild(chip);
      chip.querySelector('button').addEventListener('click', () => {
        if (audioState.playing) muteAudio(true); else {
          audioState.userMuted = false;
          localStorage.setItem('oloropa.location.ambient.muted', 'false');
          startAudio(true);
        }
      });
    }
    chip.querySelector('.ol-es-sound-label').textContent = audioState.userMuted ? `Muted · ${media.label || 'Coast Wind'}` : `Auto ready · ${media.label || 'Coast Wind'}`;
    return chip;
  }

  function fadeAudio(target) {
    if (!audioState.audio) return;
    const start = Number(audioState.audio.volume || 0);
    const delta = target - start;
    let tick = 0;
    const steps = 22;
    const run = () => {
      tick += 1;
      if (!audioState.audio) return;
      audioState.audio.volume = Math.max(0, Math.min(.28, start + delta * (tick / steps)));
      if (tick < steps) requestAnimationFrame(run);
    };
    run();
  }

  function syncSoundControls(state, label) {
    const playing = state === 'playing';
    doc.querySelectorAll('[data-p13-sound-toggle],[data-p11-ambient-toggle],[data-ol-phase1-soundscape],[data-ol21-p6-sound-button]').forEach((button) => {
      button.setAttribute('aria-pressed', playing ? 'true' : 'false');
      if (button.matches('[data-p13-sound-toggle]')) button.textContent = playing ? 'Ambience on' : 'Start ambience';
      else if (button.matches('[data-p11-ambient-toggle]')) button.textContent = playing ? 'Ambience on' : 'Start ambience';
      const nested = button.querySelector('[data-ol-phase1-sound-label],.ol-p1-soundscape-label,span');
      if (nested) nested.textContent = playing ? `Playing · ${label}` : `Auto ready · ${label}`;
    });
    doc.querySelectorAll('[data-p13-sound-label]').forEach((node) => {
      node.textContent = playing ? `Playing · ${label}` : `Auto ready · ${label}`;
    });
    const chip = doc.querySelector('.ol-es-sound-dock');
    if (chip) {
      chip.dataset.state = state;
      chip.setAttribute('data-ol-es-muted', playing ? 'false' : 'true');
      const btn = chip.querySelector('button');
      if (btn) {
        btn.setAttribute('aria-pressed', playing ? 'true' : 'false');
        btn.setAttribute('aria-label', playing ? 'Mute Oloropa soundscape' : 'Enable Oloropa soundscape');
        btn.textContent = playing ? '×' : '♪';
      }
      const text = chip.querySelector('.ol-es-sound-label');
      if (text) text.textContent = state === 'blocked' ? `Tap to enable · ${label || 'Coast Wind'}` : (playing ? `Playing · ${label || 'Coast Wind'}` : `Muted · ${label || 'Coast Wind'}`);
    }
  }

  function setupAudio(media) {
    if (!media.audio || media.audio === 'none') return;
    audioState.src = media.audio;
    audioState.label = media.label;
    ensureChip(media);
    if (!audioState.audio) {
      audioState.audio = new Audio();
      audioState.audio.loop = true;
      audioState.audio.preload = 'auto';
      audioState.audio.volume = 0;
      audioState.audio.addEventListener('play', () => {
        audioState.playing = true;
        audioState.userMuted = false;
        localStorage.setItem('oloropa.location.ambient.muted', 'false');
        syncSoundControls('playing', audioState.label);
      });
      audioState.audio.addEventListener('pause', () => {
        audioState.playing = false;
        if (!audioState.userMuted) syncSoundControls('ready', audioState.label);
      });
    }
    if (!audioState.audio.src || audioState.audio.src.indexOf(media.audio) === -1) {
      audioState.audio.src = media.audio;
      audioState.audio.volume = 0;
    }
    syncSoundControls(audioState.userMuted ? 'muted' : 'ready', media.label);
  }

  function startAudio(fromUserGesture) {
    if (!audioState.audio || audioState.userMuted) return Promise.resolve(false);
    audioState.audio.muted = false;
    return audioState.audio.play().then(() => {
      localStorage.setItem('oloropa.location.ambient.autoplay', 'true');
      fadeAudio(fromUserGesture ? .22 : .16);
      syncSoundControls('playing', audioState.label);
      return true;
    }).catch(() => {
      syncSoundControls('blocked', audioState.label);
      return false;
    });
  }

  function muteAudio(byUser) {
    if (!audioState.audio) return;
    audioState.userMuted = Boolean(byUser);
    if (byUser) localStorage.setItem('oloropa.location.ambient.muted', 'true');
    fadeAudio(0);
    setTimeout(() => { try { audioState.audio.pause(); } catch (_) {} }, 220);
    syncSoundControls('muted', audioState.label);
  }

  function unlockAudioOnInteraction() {
    const events = ['pointerdown', 'touchstart', 'keydown'];
    const unlock = () => {
      if (!audioState.audio || audioState.userMuted || audioState.playing) return;
      startAudio(true).then((ok) => {
        if (ok) events.forEach((name) => doc.removeEventListener(name, unlock, true));
      });
    };
    events.forEach((name) => doc.addEventListener(name, unlock, { capture: true, passive: true }));
  }

  function bindExistingSoundButtons() {
    doc.addEventListener('click', (event) => {
      const button = event.target.closest('[data-p13-sound-toggle],[data-p11-ambient-toggle],[data-ol-phase1-soundscape]');
      if (!button) return;
      event.preventDefault();
      if (audioState.playing) muteAudio(true); else {
        audioState.userMuted = false;
        localStorage.setItem('oloropa.location.ambient.muted', 'false');
        startAudio(true);
      }
    }, true);
  }

  function autoplayExistingMedia() {
    doc.querySelectorAll('video[autoplay],video[data-ol-hero-video],video[data-ol-autoplay],.story-media video,.p8-hero video,.sp-hero video,.cct-hero video').forEach((video) => {
      try {
        video.muted = true; video.defaultMuted = true; video.loop = video.loop || video.hasAttribute('loop'); video.playsInline = true;
        video.setAttribute('muted',''); video.setAttribute('playsinline',''); video.setAttribute('webkit-playsinline','');
        if (!video.hasAttribute('preload')) video.setAttribute('preload','auto');
        video.play().catch(() => {});
      } catch (_) {}
    });
  }

  function run() {
    ensureStyles();
    autoplayExistingMedia();
    const heroes = Array.from(doc.querySelectorAll('.p13-hero,.p8-hero,.ol14-hero,.p12-location-hero,.sp-hero,.cct-hero,.story-hero,.pk4-hero,.pd5-hero,.p6-hero,.sh7-hero,[data-ol21-phase6-hero-media="assigned"],[data-ol-location-auto-media]'));
    if (!heroes.length) return;
    const prepared = heroes.map((hero) => {
      const media = resolveMedia(hero);
      hero.classList.add('ol-location-auto-media-ready');
      hero.dataset.olLocationAutoMedia = 'active';
      hero.dataset.olLocationAutoVideo = media.video || '';
      hero.dataset.olLocationAutoAudio = media.audio || '';
      hero.dataset.olLocationAutoSoundLabel = media.label || '';
      const video = ensureVideo(hero, media);
      setupAudio(media);
      if (video && !reducedMotion) {
        const attempt = () => video.play().catch(() => {});
        attempt();
        setTimeout(attempt, 260);
      }
      return { hero, video, media };
    }).filter((item) => item.video);

    if ('IntersectionObserver' in window && prepared.length) {
      const observer = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
          const item = prepared.find((candidate) => candidate.hero === entry.target);
          if (!item || !item.video) return;
          if (entry.isIntersecting && entry.intersectionRatio > .2 && !reducedMotion) {
            item.video.play().catch(() => {});
          } else {
            item.video.pause();
          }
        });
      }, { threshold: [0, .2, .55] });
      prepared.forEach((item) => observer.observe(item.hero));
    }

    bindExistingSoundButtons();
    if (!audioState.userMuted) {
      startAudio(false).then((ok) => { if (!ok) unlockAudioOnInteraction(); });
    } else {
      unlockAudioOnInteraction();
    }
  }

  if (doc.readyState === 'loading') doc.addEventListener('DOMContentLoaded', run, { once: true });
  else run();
})();

</script>