0
TOOLS#28f63a72
Iceland 2026 3D Trip Map
@Owner·deposited 5d ago·updated 4d ago·28 views
TOOLS#28f63a72
Iceland 2026 3D Trip Map
OW
@Owner
28Views
0Comments
0Forks
0Saves
SHARE · REMIX
Iceland 2026 3D Trip Map — a HTML tools widget by @Owner.
CONTROLS
No comments yet. Be the first!
✦ Remix with AI
SDK in this widget
- raw fetch(): Raw fetch() detected. Prefer vibes.fetch for proxied requests.
Generated prompt
You are helping me modify a vibe-coded widget from itjustvibes.com.
[VIBE CODE: "Iceland 2026 3D Trip Map" by @Owner]
Source: https://itjustvibes.com/Owner/iceland-2026-3d-trip-map
Type: HTML
--- SOURCE CODE ---
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Iceland 2026 3D Trip Map</title>
<!-- GENERATED by vibes-map/build_vibes_map.py from vibes-map/template.html. Edit the template, not this file. See vibes-map/README.md. -->
<style>
:root{--bg:#0b0d10;--glass:rgba(22,25,31,.88);--line:rgba(255,255,255,.12);--fg:#f2f4f7;--mut:#a3acb9;--acc:#0a84ff;--r:16px;--sheet:150px;color-scheme:dark}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
[hidden]{display:none!important}
html,body{margin:0;height:100%;background:var(--bg);color:var(--fg);font:15px/1.35 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,system-ui,sans-serif;overflow:hidden;overscroll-behavior:none}
button,select,input{font:inherit;color:inherit}
button{cursor:pointer}
a{color:var(--acc)}
#map{position:absolute;inset:0}
gmp-map-3d{display:block;width:100%;height:100%}
.glass{background:var(--glass);-webkit-backdrop-filter:blur(18px) saturate(1.4);backdrop-filter:blur(18px) saturate(1.4);border:1px solid var(--line);box-shadow:0 8px 30px rgba(0,0,0,.35)}
/* side panel (desktop) / bottom sheet (phone) */
#panel{position:absolute;z-index:3;left:12px;top:12px;bottom:36px;width:350px;border-radius:var(--r);display:flex;flex-direction:column;overflow:hidden}
#grab{display:none}
#ph{padding:12px 12px 6px;border-bottom:1px solid var(--line)}
.row{display:flex;align-items:center;gap:8px}
.ttl{flex:1;min-width:0}
#pt{font-size:17px;font-weight:650;margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#ps{margin:1px 0 0;color:var(--mut);font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.icon{width:36px;height:36px;flex:none;border:0;border-radius:50%;background:rgba(255,255,255,.1);font-size:24px;line-height:1;display:grid;place-items:center;padding:0 0 3px}
.switch{display:flex;align-items:center;gap:6px;font-size:13px;color:var(--mut);cursor:pointer;flex:none}
.switch input{appearance:none;-webkit-appearance:none;width:40px;height:24px;margin:0;border-radius:12px;background:rgba(255,255,255,.22);position:relative;cursor:pointer;transition:background .2s}
.switch input::after{content:"";position:absolute;top:2px;left:2px;width:20px;height:20px;border-radius:50%;background:#fff;transition:transform .2s;box-shadow:0 1px 3px rgba(0,0,0,.4)}
.switch input:checked{background:#30d158}
.switch input:checked::after{transform:translateX(16px)}
#chips{display:flex;gap:6px;overflow-x:auto;padding:10px 0 4px;scrollbar-width:none}
#chips::-webkit-scrollbar{display:none}
.chip{flex:none;min-width:46px;height:48px;padding:0 8px;border-radius:12px;border:1px solid var(--line);background:rgba(255,255,255,.06);display:flex;flex-direction:column;align-items:center;justify-content:center;position:relative}
.chip b{font-size:15px;line-height:1.1}
.chip small{font-size:10px;color:var(--mut)}
.chip i{position:absolute;left:10px;right:10px;bottom:4px;height:3px;border-radius:2px;background:var(--c)}
.chip[aria-selected=true]{background:var(--acc);border-color:var(--acc)}
.chip[aria-selected=true] small{color:#fff}
#list{flex:1;overflow-y:auto;overscroll-behavior:contain;padding:4px 0 12px}
#list ol{list-style:none;margin:0;padding:0}
.sec{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--mut);padding:12px 14px 4px}
.hint{color:var(--mut);font-size:13px;padding:8px 14px;margin:0}
.day,.it-h{all:unset;box-sizing:border-box;display:flex;gap:10px;align-items:flex-start;width:100%;min-height:48px;padding:10px 14px;cursor:pointer}
.day:hover,.it-h:hover,.day:focus-visible,.it-h:focus-visible{background:rgba(255,255,255,.07)}
.bar{width:4px;align-self:stretch;border-radius:2px;flex:none}
.nm{flex:1;min-width:0;padding-top:3px}
.dt{display:block;font-weight:600}
.da,.dm{display:block;color:var(--mut);font-size:13px}
.da{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.tm{width:48px;flex:none;padding-top:3px;font-size:12px;color:var(--mut);font-variant-numeric:tabular-nums}
.tm b{display:block;font-size:14px;color:var(--fg);font-weight:600}
.dot{width:28px;height:28px;flex:none;border-radius:50%;border:2px solid #fff;display:grid;place-items:center;font-size:12px;font-weight:700}
.it.alert .nm{color:#ff6961}
.it.open{background:rgba(255,255,255,.07)}
.it-d{padding:0 14px 12px 100px;font-size:14px;color:var(--mut)}
.it-d p{margin:0 0 8px}
.gm{display:inline-flex;align-items:center;min-height:36px;padding:0 14px;border-radius:18px;background:var(--acc);color:#fff;font-weight:600;font-size:14px;text-decoration:none}
#pf{padding:6px 14px;font-size:11px;color:var(--mut);border-top:1px solid var(--line)}
/* tour pill, top of the map */
#tour{position:absolute;z-index:3;top:12px;left:calc(374px + (100% - 374px) / 2);transform:translateX(-50%);display:flex;align-items:center;gap:4px;padding:4px;border-radius:24px;white-space:nowrap}
.pb{height:36px;border:0;border-radius:18px;background:transparent;padding:0 14px;font-weight:600;font-size:14px}
.pb:hover{background:rgba(255,255,255,.1)}
.pb.pri{background:var(--acc);color:#fff}
#speed{height:36px;border:0;border-radius:18px;background:rgba(255,255,255,.1);padding:0 8px}
/* map buttons on the right edge, clear of the platform's bottom-right corner button */
#fabs{position:absolute;z-index:3;right:12px;top:50%;transform:translateY(-50%);display:flex;flex-direction:column;align-items:flex-end;gap:10px}
.zg{display:flex;flex-direction:column;border-radius:22px;overflow:hidden}
.fab{width:44px;height:44px;border:0;background:transparent;font-size:24px;display:grid;place-items:center}
.zg .fab+.fab{border-top:1px solid var(--line)}
.fab:hover,#recenter:hover{background:rgba(255,255,255,.12)}
#recenter{height:44px;padding:0 14px;border-radius:22px;display:flex;align-items:center;gap:6px;font-weight:600;font-size:14px}
#msg{position:absolute;inset:0;z-index:5;display:grid;place-items:center;overflow:auto;padding:16px;background:var(--bg)}
#msg .card{max-width:600px;padding:18px 22px;border-radius:var(--r)}
#msg h2{margin:0 0 8px;font-size:18px}
#msg ol{padding-left:20px}
#msg li{margin:4px 0}
.m{color:var(--mut);font-size:13px;margin:3px 0}
.b{background:rgba(255,255,255,.12);border:1px solid var(--line);border-radius:10px;padding:8px 14px}
code{background:rgba(255,255,255,.1);padding:1px 4px;border-radius:4px;word-break:break-all}
#keyForm{display:flex;flex-wrap:wrap;gap:8px;margin:12px 0}
#keyIn{flex:1;min-width:200px;background:#1b2129;border:1px solid var(--line);border-radius:10px;padding:10px}
#keyForm label{flex-basis:100%;display:flex;gap:6px;align-items:center;font-size:13px;color:var(--mut)}
@media (max-width:759px){
#panel{left:0;right:0;top:auto;bottom:0;width:auto;height:var(--sheet);border-radius:18px 18px 0 0;border-bottom:0;transition:height .25s ease}
#panel.drag{transition:none}
#grab{display:flex;justify-content:center;align-items:center;height:22px;flex:none;cursor:grab;touch-action:none}
#grab span{width:40px;height:5px;border-radius:3px;background:rgba(255,255,255,.35)}
#ph{padding-top:2px}
#list{padding-bottom:64px} /* last row can scroll above the platform's corner button */
#pf{display:none}
#tour{left:50%;top:10px}
#fabs{top:auto;transform:none;right:10px;bottom:calc(var(--sheet) + 12px)}
#fabs.low{display:none}
}
</style>
</head>
<body>
<div id="map"></div>
<aside id="panel" class="glass" aria-label="Trip itinerary">
<div id="grab" role="button" aria-label="Resize list"><span></span></div>
<header id="ph">
<div class="row">
<button id="back" class="icon" aria-label="All days" hidden>‹</button>
<div class="ttl"><h1 id="pt">Iceland 2026</h1><p id="ps"></p></div>
<label class="switch"><input type="checkbox" id="gems" role="switch"> Gems</label>
</div>
<div id="chips" role="tablist" aria-label="Days"></div>
</header>
<div id="list"></div>
<footer id="pf"><span id="engine"></span> · arrow keys change day</footer>
</aside>
<div id="tour" class="glass">
<button class="pb pri" id="bDay" hidden>▶ Fly this day</button>
<button class="pb" id="bTrip">✈ Fly the trip</button>
<button class="pb pri" id="bPause" hidden>❚❚ Pause</button>
<select id="speed" aria-label="Tour speed"><option value="0.5">0.5×</option><option value="1" selected>1×</option><option value="2">2×</option><option value="4">4×</option></select>
<button class="pb" id="bStop" aria-label="Stop tour" hidden>✕</button>
</div>
<div id="fabs">
<div class="zg glass"><button class="fab" id="zin" aria-label="Zoom in">+</button><button class="fab" id="zout" aria-label="Zoom out">−</button></div>
<button class="glass" id="recenter" title="Snap back to the planned route"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="7"/><circle cx="12" cy="12" r="2.5" fill="currentColor"/><path d="M12 1v4M12 19v4M1 12h4M19 12h4"/></svg>Route</button>
</div>
<div id="msg"><div class="card glass"><p class="m">Loading…</p></div></div>
<script>
const TRIP = {"days":[{"n":1,"t":"Tue Sep 15","a":"Travel (Atlanta → Reykjavik via MSP)","c":"#556b2f","l":"","p":[[33.6403,-84.44463,1,0,"18:30–20:30","Arrive ATL 14:30 EDT / 18:30 Iceland — check bags, security",""]],"h":null,"g":[]},{"n":2,"t":"Wed Sep 16","a":"Reykjavik","c":"#008cff","l":"ajreKhkaiCAsFmB?AwMwBR{A{EmRm@iD{PfHjhA~GnJFnIrFAsF@GoIkEoCyBwK}Im|ADwp@bOgk@xW{q@fhAkiB~[}x@h`@aq@tGaV`Hon@t_@awI`]{eFA_kBeKylDaEqq@wTusBgs@siDyu@owEiY}vB}g@y|C}JuaAgYu~Dew@uiG{G}[qOo\\gIa]mWgaF_^upBqNsuBgUseAaMq~@iRgt@aRwjDuw@gpEaaAybLoIcPmc@sIygBhe@gI{X}Qy^aP{j@uj@gt@yJc\\gKul@uH}NgKkA_`@|W_JcHsL}d@sJ_LkkAtKiY}LoRz`ClAn{@gBn`@kUlv@wn@~{AmO|LgDqAQhQbHfq@sC~~@aK|^kTlFcF~HcLbw@_Dnb@{JnYwCpi@vCqi@zJoYlDmd@tKeu@bF_IhSiErKi[tB{Xt@gm@yG_n@x@uPmK_g@nHu]hQorArBzDhBeOhJzE|@uGcIqM~@gIuJiGy@lDoC_EsB|WuAd@pCs@pBbMzSf`@{Ex[cImKo@bGiB_BsAdEhK|GbAsJ~Zlc@bc@qlAtFcg@aAshAhRwvBfYjHbiAeL|JbK|C`TiA~HzAjlA_AnSCziCyL\\j@~Nk@_OxL]bAk~CyCezBqvA|LiY}LoRz`ChAvcAsErc@cIbVgE_I_TfOyOgHaFn\\cImKo@bG","p":[[63.99662,-22.62318,1,0,"07:00–07:30","Land KEF; immigration + bags",""],[63.99662,-22.62318,2,0,"07:30–08:00","Duty-free liquor pickup, collect luggage",""],[63.99806,-22.63028,3,0,"08:10–08:35","Rental pickup, paperwork","BOOKED OTB05W (no email)"],[64.16501,-22.02198,4,0,"09:30–10:00","Grótta Lighthouse",""],[64.14557,-21.93395,5,0,"10:10–10:40","Grocery stop (snacks, water)",""],[64.15025,-21.93228,6,0,"12:20–14:50","Guided food tour, meet at Harpa Concert Hall","BOOKED WRE-T138641934"],[64.14859,-21.9428,7,2,"16:00–17:00","Check into Reykjavik Treasure B&B, shower, rest","BOOKED 6713374611"],[64.11649,-21.94629,8,0,"18:00–21:00","Sky Lagoon (7-step ritual)","BOOKED T-1022854"],[64.14859,-21.9428,9,0,"21:15–22:00","Dinner downtown, return to B&B",""]],"h":[1,64.14859,-21.9428,"Reykjavik Treasure B&B","BOOKED 6713374611"],"g":[[64.14819,-21.93789,"Bæjarins Beztu Pylsur","iconic hot dog stand · detour on route, walk 5 min, total 10 min, ~ISK 700 · fits after 10:00"],[64.14202,-21.92655,"Hallgrímskirkja tower","church + city view · detour 10 min walk, walk 5 min, total 30-45 min, ISK 1200 · fits after 10:00"],[64.12925,-21.91887,"Perlan","museum/viewing deck · detour 10 min (API not run), walk n/a, total 60-90 min, ISK ~4900 · fits after 15:00"],[64.12859,-21.91967,"Öskjuhlíð forest trails","short forest walk · detour same as Perlan, walk 20-30 min, total 20-30 min, free · fits after 15:00"]]},{"n":3,"t":"Thu Sep 17","a":"Golden Circle","c":"#ffc800","l":"{_pfKhu|dCiB_BsAdEhK|GbAsJt[|b@lb@alA~DsUz@oXeB{t@bAwTfcA}dLfD_l@dAeeA{A}rGlCwGvh@yZtl@e@nQsMhKoT|Bm\\|XsqBxb@ewB~DivA|ImkAh~@{_Fvp@ykElEqf@tLopCr|@auJbGckBLipBjDoo@pJwf@jlAuvEfi@{fA|Lw^dI{d@~JigAdGgYxJuW|j@g|@rJsd@jBi^[m_BvBsjAi@_gDcUqqE_@e~@|F{}@dd@qvCbIw|@fNsaAxAo\\_@oS{DeVcIgL{OgE}AaHXwMbK_PdFuV~OwIjReTbGbCdRjp@jJiDvGcoA`U_bBp`As`F`u@wmAr~@y_C|_@ur@zJq^|Esc@zBspDnDekBxEchApW{zDch@c}@yj@qfBeLeOoY_IiRkVqKiDim@eKq]tAs^yGuRjBob@e^qSqr@sGch@kWab@gg@w{Ba\\av@km@idCgLeYmt@slAqGaPrAiFsAhFbEtKzv@~pAdMj[`m@vcCfZlq@dW~hAcrA~_AwSfDgbAlq@qWdDc[eT_PWyJrGex@|cAmc@v@wXvGip@hv@ma@y@gIzOeG|s@yKva@o~@beAeb@lu@kMb@oh@wd@uo@PiHcJwIkVoJcE}IlGwNjj@iJxN{bFnwBcMwAkKyHiYoi@cLcLavAsXeLtXyg@`t@_tA`tAsKrDwm@iNoEqF}MkpAcAo]}CaMmIuH}W[ia@_ZoQe@kTqJcEnAgNzVsQvI}CzIoA|]oH`U]vl@gE~Y}Sp^gPpg@eWn\\_Pxf@mTxN}Jvp@c@xv@|Lh`Az~@vmC~NxQbQhc@d\\d_@hJ|Wh@ic@mDgHlDfHYjb@yJ_Wi]{`@_Psa@mPkTm}@ekC{Lg~@Tuu@zIuo@jEgJpPqIrOif@zRkU`Vqq@rP_XjFm\\\\wl@lIaYtAa^lYyTbLgTlk@`KpV~Tp_@|BdJpG~E~ObAn]|MjpAhF|Fhk@zMdOkFbrAirAvj@mx@hOch@zEim@@{b@uFa~B@i`AzCcaAhS{gD`BoqAqEwqAsUa}BkHahBuMgqBgAqsB_KulBbC}aBu@sa@_Faj@eOqt@sDo]oOgnCcMkz@oAqb@l@cMqIa^eg@{d@oaA{cBiV}i@m]ocAkGet@iBm_A|@ueAx]_aDdGoTdLcRdJuGzWcErIcW|A}h@bGsn@{@sPyKoj@gAyRxEqmBoBus@_GyWg[sc@wn@qxAgDkWs@abAwM|EcQkNeCzGuXqUyIdPeC@qOgYsKxBwMcKwDc[aMmXl@eCm@dCdJ|PjIjd@~KjI`JcDbQpZdCAxIeP|WhUxCsGfQjNvM}E_A_{Br|Abe@hg@KnL_QtM}z@zK{a@nw@a}AxQc@tZl_@vKdGlc@kFb[{\\zMsDd^~GwBwWr@awC}Cak@cKqb@vGi\\KjG~DfLsFnVuz@cvC}JmqA_Kum@g`@gy@mjBglCmIc\\_Va}Amd@kqAcIkNyjAyb@a}@cfAiLsFiIn@{SfTi_A`gCik@r~@wSum@qp@eu@cdBc`AyLgMuJc]w_@miCwFoOgEiCfEhCmE{V}KopCxEwcA`Kku@fB}[d_@srKGmqAeEq[uw@kkBeKgn@mNa_@gVmHa^ag@`^`g@zVrIhMl\\dKpn@vu@jgBlFjVl@v{AaXzuH|GvFpNpg@hHxJxc@ZdTy^bUkSf\\mp@jFkE~cAbInMoDtNbC`[kSbTjFfEoSaUs]yDmj@{UukAp@kTzGsEzoA`r@ta@z^zJd]h^vi@jn@`aCfdA|sBhh@`]~kA|jAx^~Sx~@rC|_A|p@~_@wGpK|BhVl\\vQzb@jb@po@zMft@vLnY~|AlfAhM_OhMo@lXcTFqRlFmLlWfFjn@MfZxJpo@d_ApKrFd`A{n@peAy\\jOvI|y@xjAhf@ti@jLj^zRbsAjYldAvj@dtFhV~_AtHnmAtTvs@tYb|Abc@vwGzOhyAzKrn@~IvTlq@~n@nWth@`lAdiB`o@jg@|`A|yCx[tyAhe@pzAha@nqB~ArVd@bnAbBve@bg@h|CvDnd@hB`r@|BxHpA^nAy^zDeR~x@wfBhQ}Plg@kPla@g_@bScWbT}h@xPm{@jn@yjGrHiXpO{Ul[cPtH_Pdp@owE|SotBtbC_`LbBkQjAwnAzEm]`jBudDp[cs@vPiRbYcJxKaTfDi@mCkCbAc@jB}HkAhKwOnXcYbJfi@`cAcCbQ","p":[[64.14859,-21.9428,1,0,"07:00–07:40","Breakfast at B&B","included"],[64.04128,-20.88515,2,0,"09:00–09:35","Kerið Crater rim walk",""],[64.25564,-21.1364,3,0,"10:15–11:45","Þingvellir: Almannagjá rift walk, Öxarárfoss",""],[64.26219,-20.52119,4,0,"12:40–13:25","Brúarfoss short hike to the blue falls",""],[64.1775,-20.44489,5,0,"14:00–15:30","Friðheimar tomato-greenhouse lunch, 4 people","BOOKED 8733110"],[64.31271,-20.30072,6,0,"15:50–16:45","Geysir / Strokkur eruptions",""],[64.32707,-20.11995,7,0,"17:00–17:50","Gullfoss upper and lower viewpoints",""],[63.83504,-20.40166,8,1,"19:00–19:15","Fuel at N1 Hella",""],[63.83416,-20.39995,9,0,"19:45–20:45","Dinner at Lava 47 (pizza), Hella",""],[63.83511,-20.42159,10,2,"20:50–21:30","Self check-in, settle in","BOOKED VWTIFYO (no email)"]],"h":[2,63.83511,-20.42159,"Aurora Igloo Hella","BOOKED VWTIFYO"],"g":[[64.22507,-20.33675,"Faxi Waterfall","wide, quiet waterfall with salmon ladder on Route 35 · detour 5 min (on route Friðheimar→Geysir), walk 5 min, total 15 min, free (parking ISK 700) ·…"],[64.1374,-20.3097,"Secret Lagoon Flúðir","oldest swimming pool in Iceland, natural hot spring · detour 30 min from Friðheimar, would replace Geysir/Gullfoss if chosen, walk none, total 60-90…"]]},{"n":4,"t":"Fri Sep 18","a":"Westman Islands","c":"#32cd32","l":"iyrdKnrs{Bh@iMxAyBgi@acAfO{A}AeAxKaExKaTxCG|DcO|h@wxD~Qqx@|d@gw@l^s~@j`@{q@~fAkcCdd@upAfz@ayAbw@cgAjlAqaDzz@w}A~rB|O|`A{L`mB{mBnt@yqAvI_Uf~AyzG~c@}kDvJe]`qAifDtkAqiFfT{j@dRw[f`BibBrTpq@p^bUp_@lw@lWbWr{AtOj~A|`@hlAjHtV|LpVj[nQlc@rJba@tQxsAjPz{@r^~bAbQbSrWlE~Ea[f^dEx~Cp|Ef`CdcHlQ~`@hmD|dEnIzdAeDlh@d@nUjM~j@~Fnw@vEjEu@cKv@Bw@qSdH_JxGiAzJzKrDia@zPg@gA{IfAzItFKg@_NeDsHaUcOwItCoFeIiCgKQ_WlKeQp@eInOfAbMhb@cMib@oOgAq@dImKdQn@d\\jH~NhMoAjSrNpEfOHjX~Ft\\mFdEyNf^dEbS|VxNzM`b@lJhIzGxPhScCvm@md@`Ncj@|ZoNnUbKxCzc@|G{LlGfLc@vRjCzEmCeFl@uQ}GgMuGbMkCac@aV}KyZnN_Ira@mDzGmh@x`@uVvGuHeQkJ{HcNwb@cVwLqF}UuCp@wAe_@oJmUuHmGeMhGqCgOTmUcCw@bCv@QpVvEvW_DRc@gDdH_JhJa@]xMcIr@iBFyAn[sFgAwGe|@kM_k@e@oUdDmh@oI{dAetDuoEykCmzHy~Cq|Eg^eEmB|OhChDA{G{@~OwYoEyMiKgLuSk]{fAkc@csC{Rel@{Ui^eb@aTmgA_Go}Au`@s{AuOmWcWq_@mw@q^cUkTgu@sQzX{mAblAyTl`@qOha@ooAxrF{wAzvDaKbj@e]~pCu{AjrG}KhYyr@~nAamBzmB{}@|LwtBcQqLtRl@`DcDpH","p":[[63.83875,-20.40743,1,0,"07:14–08:00","Breakfast at American School Bus Cafe",""],[63.44434,-20.27304,2,0,"10:45–11:20","Herjólfur ferry to Vestmannaeyjar","BOOKED 2472067"],[63.44307,-20.27134,3,0,"12:00–13:00","Harbor boat tour, Tangagata 7","BOOKED GTI-93722749"],[63.44307,-20.27134,4,0,"13:02–14:00","Lunch in the harbor area",""],[63.43492,-20.26027,5,0,"14:00–15:00","Visit Eldheimar Volcano Museum",""],[63.43205,-20.24943,6,0,"15:05–15:30","Eldfell Volcano (1973 eruption cone, short look)",""],[63.3999,-20.28804,7,0,"15:40–16:00","Stórhöfði Cape (puffins & coastal views)",""],[63.44375,-20.26102,8,0,"16:10–16:30","Sprangan & Skansinn cliffs",""],[63.4429,-20.26926,9,0,"16:34–17:00","Beluga Whale Sanctuary",""],[63.44157,-20.269,10,0,"17:30–18:30","Brothers Brewery",""],[63.53058,-20.11684,11,0,"19:30–20:05","Herjólfur ferry to Landeyjahöfn","BOOKED 2472067"],[63.75181,-20.23802,12,2,"20:30–20:45","Check in Hotel Loa Hvolsvöllur (24/7 check-in)","BOOKED 6427214977"],[63.75181,-20.23802,13,0,"20:45–21:30","Dinner in Hvolsvöllur (Eldstó or hotel bar)",""]],"h":[3,63.75181,-20.23802,"Hotel Loa Hvolsvöllur","BOOKED 6427214977"],"g":[[63.43205,-20.24943,"Eldfell volcano full hike","1973 eruption cone, full summit hike · detour on route, in town, walk 45-60 min RT, total 60 min, free · fits after 15:05 (replaces the trimmed 25 mi…"],[63.4429,-20.26926,"Sæheimar Aquarium","small aquarium, puffin rescue · detour on route in town, walk n/a, total 30-45 min, ISK ~2000 · fits after 13:02"],[63.44206,-20.29725,"Herjólfsdalur valley","scenic crater valley · detour 5 min, walk 20-30 min, total 20-30 min, free · fits after 17:00"]]},{"n":5,"t":"Sat Sep 19","a":"South Coast (Hótel Lóa Hvolsvöllur → Hótel Jökulsárlón)","c":"#ba55d3","l":"epbdKhuozBbDqHm@aDpLuRtqB`Q|`A{L`mB{mBvx@kyAvdBgiH~c@}kDvJe]`qAifDtkAqiFfT{j@dRw[nyAyyAh[{w@`^s_B}Lm\\yTe\\bD}TbMdHFmIGlIcMeHcD|TjUf]jLj[z[c_AhlAsaCtFkQbhAc|FtJetA`gBiaMhHsbAhIurB`cA_xF`LuvC~Ikk@nDaaAdJonAUgv@vGa_A~Am}@Hms@kDgjAtDahApl@qkCbpA}hKHaWoDg~@iAiqBhUsqEyWi]aM~r@wStDhIu@hDoS|GlCfI}f@xWh]pZwrGd|AetIxoAurKb^ofB~GsrAzx@mwJ`v@afCxUoeAdp@oyA`lBghBbKa\\|Ce_@vH{Jdd@d@hGgj@bBcm@~Ns\\mDer@nAeVnCeHrQsJ|JkOv]}bCdHsq@zIBbHt]cHu]{IC_g@zuD{J`OsQrJyCdIeAdUlDdr@_Or\\cBbm@yE`g@}BtCm_@sB_KbHg@cYmCuTuv@mmDcXcb@kHiVyAqQeBwgBaNq|@gGw_AgKuf@_KmaB~Bav@jKqRsG__@Gsq@vHlA|k@hr@pPzKh\\zBtr@or@jZaCt_@mQ|JvCvaAqDIpEbD`AcDaAHqEwaApD}JwCu_@lQsYnBat@bs@u[}BuP}Koi@cp@oMyCjAqd@bMw^hNuGbWzCzKeC|PqQds@ioAn\\@nOkI`T}b@pTw_AvAvAwAwAhCca@yGgjGaNubEmYimDo~@iyDoIwv@mAo}AxByiFkCahBcHseB|ZuC~LpBf[yI`m@t@nRkIzC{N{CzNoRjIam@u@g[xI_MqB}ZtCwYk{EqNskFeLizB{OqlBo_@}uCi\\ogB}x@mqD}g@mdDgUgiAkXg~@eq@}bB{u@i{Bsc@edAqa@mu@wb@{m@oh@ek@wy@iq@em@qy@s_Bw~@cMaA}rBlc@u}@jw@_b@zTu^nFse@iD}WeK}a@a]ooD{pEit@gd@khAeZu`@u\\sYsi@oT_v@sqAe|Hcq@yfC{jGsqZq_@qkAmgA{~EqWsg@mKs]gOze@iVjNtFbz@mBvYb@hk@{Dju@vB`UaOfhAbO_iA{B_ShEg|@q@ed@nBuYuFw{@lUeMbPag@lKr]vW`h@~}@jaE`DMaDLe~@yaEqWsg@k\\_rAcJuz@}BoiBkDgZkZmn@_c@c`B{VeYgT}oAcGoJ{IwCsc@|PkjCcnEeH{Vg`@e|Bg^i}@{LaK{_@g@wMgLqD_KgE}s@ka@cuDgC{q@cKat@cUczC_Dc~AfMuiAzBdA{BeAhBsuA_VwpAmMee@cf@imAcnC_pLyJoSqQiPeoAu]_bAkhBiqBagAePiO}JeUeH{Z_Ny}Am[o|BoGooAkEajEeTa{AyRuq@eLehAqGawDpH{uLuAkvBhB}d@fLkrAfyBuaSXap@aR{hIuG}y@y{@gpGcjB_cKoJox@ug@kpKsCghImEmoDpAmnDyCci@kKyf@gOoWcqC_jBgHcPgEod@bA}k@dEiYbkBeeFnGk[zHsw@d`@qy@bPcPznBqWfMgJbIgOfUsoAfEwcApc@ogDdH_W~]uc@z`Ac]~_@kQvE{GdRioB~jA_tHnHyRxX{^nU_r@bJqInXaFnQgKxk@cz@j@saAtJ_iAeLouB\\gp@qOwx@_K{Og[oSsLuQko@agBkOmo@aIql@kpAwnMmc@_kBquA}~J_FcSkR_SwcEau@{J}FseDoaHef@mz@__AemAeRme@afBasKyH{n@gDoq@_MuqJ}\\szD}I}l@cIg[cRce@_pA{cCgcAg|CoNwJwcDoiAsUiPmXo_@kcAqrBfGaWpFxK","p":[[63.75181,-20.23802,1,0,"07:00–07:45","Breakfast at Hótel Lóa","included"],[63.61562,-19.98857,2,0,"08:20–09:20","Seljalandsfoss + Gljúfrabúi",""],[63.53205,-19.51137,3,0,"09:50–11:00","Skógafoss + Kvernufoss",""],[63.40468,-19.11855,4,0,"11:30–12:15","Dyrhólaey viewpoints",""],[63.40574,-19.07162,5,0,"12:40–13:15","Reynisfjara black sand beach",""],[63.41764,-19.00278,6,0,"13:30–14:15","Lunch at Black Crust Pizzeria, Vík",""],[63.41698,-18.76456,7,0,"14:35–14:50","Hjörleifshöfði \"Yoda Cave\" roadside stop",""],[63.77128,-18.17182,8,0,"15:40–16:15","Fjaðrárgljúfur canyon",""],[63.74587,-18.16104,9,0,"16:30–16:40","Eldhraun lava field viewpoint",""],[63.84924,-17.86126,10,0,"17:00–17:20","Dverghamrar basalt columns",""],[64.12164,-16.06884,11,0,"19:00–20:00","Dinner at Restaurant Gunna á Leiti (hotel)","BOOKED (confirmed via email)"]],"h":[4,64.12164,-16.06884,"Hótel Jökulsárlón – Glacier Lagoon Hotel","conf 6905691749"],"g":[]},{"n":6,"t":"Sun Sep 20","a":"Southeast (Hótel Jökulsárlón → Höfn)","c":"#ffff00","l":"axjfK`jaaBcAeHmDsBgG`WjcAprBlXn_@rUhPvcDniAnNvJfcAf|Cb_Bt`DbMpd@vG|a@jFjInBnLkDsPoDgEwG}a@cMqd@c_Bu`DgcAg|CoNwJwcDoiAsUiPmXo_@kcAqrBfGaWpFxKcAeHmDsBgG`WjcAprBlXn_@rUhPvcDniAnNvJfcAf|Cb_Bt`DbMpd@bInh@z]|_EzLxpJfDnq@xHzn@`fB`sKdRle@~~@dmAdf@lz@reDnaHzJ|FvcE`u@jR~RfFhThuAv}Jlc@~jBjpAvnM`Ipl@zRrw@zk@z~AzL|Qpc@`^nHpTdIbg@Sjr@dLnuBwJdjA@nu@eAzLgp@x{@qn@lUaIlOwMjd@g`@tm@eJhb@{aAtuGeRhoBwEzG}^xP}aAt]c[`_@aKr[ac@~dD}DjbAmVhtAwHlNgMfJ{nBpWuJjHse@haAcM|hAoT|s@rD{OgBkBfBjBzNac@bM}hAre@iaAtJkHznBqWfMgJbIgOfUsoAfEwcAbi@ixDra@{j@z`Ac]~_@kQvE{GdRioBzgAikHrKo[xX{^nU_r@tL}Jdc@iLh`@cl@~LaJ~BsNBqy@`JuaAJ_T}KygBp@_k@sJcp@_McZy^cWsLuQko@agBcR_z@uwAwqNmc@_kBquA}~J_FcSkR_SwcEau@{J}FseDoaHef@mz@{e@{o@sQhd@rF|k@`GlVaGmVsF}k@rQid@i_@_h@sRmq@m~Ak{JyH{n@gDoq@_MuqJ}\\szDqPe`AsUcn@_pA{cCgcAg|CoNwJwcDoiAsUiPqd@es@a}AybDsC}`@mB_hAhAoYrJkn@@oSuNesAkFy{AcOqqAcf@kmB_x@awAmc@aoA_kBkdHwVcfCaByWwAgcB`Fs~@CuWaCmV_Oig@aQq{@kEui@rCceAz\\ctCRqj@sE{_@mY{bAcKoQmb@sc@{UpGoJuGix@yeBoRmp@eVy]yY}}@a`Ay`CaLmHsb@YkTqSqYmH_Tse@c]a~FohA_eTgKceA}P}`Aiy@ghCmPwo@aFnAsEmQg_@yv@}S{`Aex@ioEaIwUy`@eb@_M{CqJ`JsXnf@uGrA}PoEio@n[yQoRaPe`@}`@ql@iy@su@cxCwsQmp@f_Dyq@nq@aVx_AmHzCsOiBi@fKh@gKrOhBtHeDxUo_Ahk@ig@nH_Qlm@mwCcGmi@_A{_@zBk_@dKw[l`AeeAfnA_dBjWmIr[[zR_KxXu@bH}GjOqb@|IiHvIn@rYjRtQaHnn@_qAvRuh@pUibApnAwuCfIiBnOvIng@bx@dF`]|HdApPrTddACla@rSjQ`UjS_d@zIdH{IeHkS~c@{QqUia@gSydAIqOaT}HeAmDkYy`@up@|@aC","p":[[64.04433,-16.17766,1,0,"06:10–06:45","Diamond Beach — catch the ~06:30 low tide / sunrise",""],[64.12164,-16.06884,"!",3,"07:00","ALERT: breakfast NOT included per booking",""],[64.12164,-16.06884,2,0,"07:00–07:45","Breakfast at Hótel Jökulsárlón","pay if added (€28/pp)"],[63.99063,-16.8965,3,0,"09:00–13:00","Glacier Discovery Tour, Local Guide of Vatnajökull","BOOKED LGV-T140513453"],[63.99059,-16.89224,4,0,"13:00–13:45","Lunch at Freysnes station",""],[64.01861,-16.385,5,0,"14:20–15:15","Fjallsárlón glacier lagoon",""],[64.39688,-15.34398,6,0,"16:20–17:15","Hoffell Hot Tubs soak",""],[64.25022,-15.20407,7,0,"19:00–20:30","Langoustine dinner in Höfn harbor (Pakkhús, Nailed It, or Hafnarbúðin)","per v6 — book ahead, no confirmation on…"]],"h":[5,64.28506,-15.18531,"Aurora Cabins","conf 5813826970"],"g":[]},{"n":7,"t":"Mon Sep 21","a":"East (Höfn → Vestrahorn/Djúpivogur/Seyðisfjörður → Wilderness Center)","c":"#4682b4","l":"qtjgKl{t{A}@`CkLwN}NgErDk_DxEcw@eS}zCkB{n@`CgfAdFwo@k@}q@`M}xB|\\soBzDmu@jCeNbGoO|]kc@lYqH~GwKjN}Grj@iyBlZ{d@xCuWQuNPtNyCtWsZde@uNtp@{N|_@yHnb@oYpXoYrH}]nc@kJzXuExy@oAlBxDew@s@mZ{CaVwG}LqPmLo`@qImQqWgj@mhCmcA_sDsVklBySw}@eEkMqTeYi]w]ce@gKuQkNkeAkd@ou@dA}V|G_YqLqp@jDaJjKoSzh@qPzB{r@et@kx@wl@ybBihCwIsU}U_jA~Ioy@|Ti|@rCos@wUm}@{OmtAqJkuCsNu`BrA}KzP{GlM_OzH_VjDgWjFeoBaByjAdAylAkBeVgPkw@eGeOmQuPoPgn@qMcbA{P}rBkUifAmAge@fLwaAf`@avAxGml@pCoq@vDaNnMyPdJoY`PmyA~v@_dDtCw]_@g]jAiS`DsOjNwYfF{WRqUmDoPy[aPiOK_\\nI}_@aK_\\ce@m_@m|@mKkw@wPok@{FcIyIzAoYf\\_FhNmM`AoG`HcXiMup@Qgb@{s@sJcE{KqN}OaIyf@`AsIkDcGmIeR{i@mZi^cSka@gN}FgPrOcQ|z@sh@haAsSvy@o^bc@qp@~hBwLt}@aWvgAiQllCmIpVa\\d]aQh^{Idj@kIdkAgHjSyT`LeI}B{`AyxAmQkn@iGcLuv@mB{XoKcM}Pge@ykA{Kia@cIci@DgVbIgw@vEa`@vGiSh@qM}Gof@cEsx@cPm`AiJobCy@iyAqN}lB{@wlAdD_s@UcY_IchAcJcW{\\g`@eWpCuSaFmFpBu`@nh@cJle@yj@`gAsAhPRdi@}Cdd@yFlo@kIlb@iF|p@k`@d_AcVd|B}GfNy\\fSaMaLwMgb@{BuTCud@{RqbArEmoBzEmr@cL{jAmAadAaE{v@xAuu@cCwg@xAih@_Ash@fIckDjTsrBf@}X}@q^wK_pAwAk{@_Hmh@cEoGkGW`AgQq@oQ|B{YaC}I}@fHiCyD`C_KhCia@rFeGrK_@|DeQkBjJdKhf@zCto@yA`T{Qjy@kC{RqEcK_HoAwDjEkc@znAoHdd@iRzn@eApb@uGdU_Od`@q`A~gBaXn\\kPf~@mq@t|@_Vzc@q_@lPaTnRei@}Co_@tVyY~z@ka@ne@aIrUcRn[iUxq@aTfiAuHdHmCrMiAlPf@l`@mEdd@if@h}@}TjPkMAyOmS{Sor@kImGge@xYqSp\\qVf~@aJlk@aGhcAmU||@gPffA{CjDsJ{KgH|A}NfIcRzZUnL`F~{@gNvgAaMjg@Kd]mGha@dCfb@gJvXm@lg@cK`TfCbjAoAfCgEyBeDdJfE`l@aDnz@sGzFjDtLdCth@}Fpg@iJ|NsDng@wKlXwD~@qJkMwE\\}BrJQzUeWb\\yUhIkMna@uIrHwVhDgG|LmOXeWxTuP`AqN_DoG}HeG|AmImH}j@dUio@yZuRoYkIxFsDmIiGqCsH_WuLUcRmK_DcNgOwRqPqHyS}t@sYhG_IiHyCaMXkVkEoF{Zx`@ug@v]y_@pFmm@aMcWyL_\\[qg@k\\w\\nDu_@aQkeAuWc]o\\w[wC{YuWkLkB}RaY{`@hAgpAaiAmaB_C}s@i~@}L`@ul@`W}b@f[wSzCyVwLae@{n@oa@uNy_@qc@iNuH{e@Xel@s^{PgCiv@nI{ZbLuy@_GmJwGo]{k@kh@mo@y]e{AwWqc@mH_f@wYahAyGga@}GsLyHwCySnGgSGgr@wV}c@`GySEoPoFinA|@elAqoBwWmFmOqOupAgu@ed@iw@k^ya@gZeQ}jAyuAsqAgqBoc@_nAqO}t@kYyl@hAqO{@k]zKwa@~AgZiOeNuNej@_TcJiGuZkQsBn@m_@mAat@`Ag]nFk`@xQgm@f@ma@qC_T_HwPqt@aeALmJbJeNd@_MgEwSoM_SSoEbx@y]~BaGe@{R{[_i@dFiC|R{a@v@ylA`Fkk@|Z{pAl^}{Dda@ypC~_@qcAz^ga@fIoTlOklArC{f@~@slAdMq`BpGaXrN{P`CqM]wMyBiGeUyHkJe_@_@eKhPstAtOmc@pDus@gEuCiKnKjFai@_XpKkFgCmFqe@{KeOqD{OU{[pEqXqBieAkOmRgJe\\_E_VyAqn@sFcX}g@{u@_`@}XPgM_Gij@yVoq@{Wsb@zWrb@dV|o@xGhm@WxKv`@tYdg@bu@rFbXxApn@~D~UfJd\\dLfJzB~JvBhy@oFra@dAha@dPbZrGtg@`^sIaFxi@`HqJfGUoB|q@gPhf@_QjyAlK~h@rUlInB`GXjMqChNsOdSeFtU_NbiBc@fdAsChf@aOxjAuF|Q{a@fe@i`@~dAga@rqCc^dzDwZdpAaFjk@k@dkAkSpc@cFfCzZ~f@dAzToD|Gsv@~\\RlErMhSfEfUsAzNiI|K@~Hvu@zfAbJj[z@pV{@lRyQfm@}Fzc@UfoBfQlBbH|[hS`ItNdj@hOdNcBtZwKha@Jzg@g@pDi_@uNoLvEiv@v|@y^naA`Iba@vNtVl\\p~AhOpYvN`|AdR`m@pRfVte@|rAli@joArSd^vQ~{@tw@nrA|M`u@nIh`BhHxj@nTvw@vYto@dN`x@dRrm@hTr]|QvIlg@lo@hgAxfBfl@zm@rQp[fb@xbBvLh`Bhm@nlB|f@n_@lEnJ~[n|@h^tyAjn@r|Az[dh@fRvt@p_@pn@ru@~jBjV~v@fq@lhAdYjYjVbn@nOfn@bz@hkB`{@jkD~TpiArCfYvH`\\|}A~qEdf@~z@tVnaBrYfx@f{@j{@|U~]tUzI~NlY`ZnWh_@lw@zHng@lBh_@~HjXtLpz@nOd`@hFb_@re@hxBhKhQf\\baAvIdv@`Rzx@Dlc@bJp`Az_@nlArH|^lo@hpErVbtAfDtH","p":[[64.28506,-15.18531,1,0,"07:00–07:35","Wake, pack, self-catered breakfast",""],[64.25502,-14.99355,2,0,"08:15–09:15","Vestrahorn & Stokksnes Sand Dunes",""],[64.66178,-14.29477,3,0,"10:50–11:30","Djúpivogur stone eggs (Eggin í Gleðivík)",""],[64.65634,-14.28288,4,0,"11:35–12:30","Lunch at Við Voginn",""],[65.26071,-14.40786,"!",3,"14:09–14:14","ALERT: check Route 93/Fjarðarheiði status",""],[65.2399,-14.05758,5,0,"14:44–14:59","Gufufoss roadside stop",""],[65.26795,-13.98166,6,0,"15:04–16:04","Tvísöngur sound sculpture",""],[65.07338,-14.88049,7,0,"16:59–17:44","Litlanesfoss walk (lower falls only, not full Hengifoss)",""],[64.96457,-15.15332,8,0,"19:00–20:30","Dinner at Wilderness Center","BOOKED (confirmed, meal selection made)"]],"h":[6,64.96457,-15.15332,"Wilderness Center / Óbyggðasetur Íslands","conf 5201059517"],"g":[]},{"n":8,"t":"Tue Sep 22","a":"Wilderness Center → Stuðlagil (east) → Dettifoss/Selfoss → Hverir → Dimmuborgir → Mývatn…","c":"#00ff7f","l":"ymokK`co{AqDqIiVgsAmo@ipEsH}^{_@olAcJq`AEmc@yPit@_Kwz@i\\eaAkKmQoe@cxBiFc_@oOe`@uLqz@aIoXqBg`@uHmf@i`@wx@aYeVaOoYsUyI}U_^_v@ks@{Wyn@eIg[oSuvA_`@gn@cdBw~Emc@eaCc~@irD}w@afBiMwi@sWsp@yU_U}q@sgAcVcv@ow@aoBma@gr@mQas@i\\_i@}m@y{Ak^yyA}[k|@mEoJig@a`@}l@}kBeL_~Agb@adBqRe]ui@oj@oiAsiBmg@mo@qTuLyQ_[aRim@eNax@wYuo@oTww@eIup@sHmzA}Mau@ox@ctA}Pkz@sSe^mi@koAue@}rAyRuVqReo@cNoyAqOcZm\\{~AuNgV{Hu`@m|@|~@}_@pt@aHlXyE`s@cHnOeYzHiTlWqPhCoXzOsgBrdBw[nf@o~@hd@co@jl@cOn`@_Nx_AcSt`@oxAfZe_@fB}q@xk@af@oC{TdEg[dTwXv_@qIbYmOjmAsFvp@oB`m@uFh]_GvL}K`Hup@hCeI~EqEhMgC|V|Cdp@xGjh@vHxRdKrHd[zFdd@td@`Pz[bGlUfPlgApT|d@xRfu@tSfh@h}@jiE|Yvv@zHha@`Exl@b@r`A~PvdDrWfoCpY`pEx^rsDxQ~}C|NpeDfYzyCvAtkBrLlfA|JpyD~Fdy@dwBhxP`Ij\\h\\|t@r]p`B`SnmAjO|}BvNx{@d[dsAdXd{@lFrHja@fS|]pe@pk@xL`u@ve@pKdMfe@lRla@xKdf@?|`Az~@be@|Kzm@lZrd@fp@jf@bbA||@pz@dk@bU`Zh]lMhGnM~SjD{WlPYUwS`GrD`Xjw@`WxFhR~]|Ib^}Ic^iR_^kWoGeWav@sGgETvSmPXkDzWoM_TmMiGaZi]ek@cU}|@qz@id@__Ayj@ov@wi@iWce@}KcbAm_Aqe@N{`@wKge@mRmz@wo@wr@_Q}]qe@of@aYq[{aAcZipAj^dzCzChw@tKreAbGpyB|@vyCjDtl@fLd|@`Ap^}BdvAsI~uBcRxrA}InnAiJxf@qc@jz@yc@h|Aeh@rq@{OlZabAzzCiVve@}S`Qqq@dI{X~PyYhf@s`@ffAqZh\\oqBxt@i_A`Kiz@xAof@jSeSfXwTrl@ed@fpCo\\daAkTxZiP`M}v@nLaTfNgXrh@uNhs@sCdl@vAlr@bFna@dOln@lDzg@_@f_@mFt|@~An_BsHv|Ar@tZnKhoAt@bn@eEjz@e\\l_CiI~xBsPjhC_@dcAtJ|wBaFfuCj@d~A|Czc@nOxi@zF`e@p@v\\iAtlAdBzc@rEj\\xRhv@tC|c@`BvkAjCl^~VttAzLbpAtPxaAlC`h@|@v|AvOd}@tErg@bAfb@w@bh@qOfrAmAnfAwGhf@iJvQsNjFoh@wWebA}HkYcLuNx@eyAtgBkvAlu@q|@nn@_n@bH}e@|^cn@_H}zAd]{a@pb@}k@juAo[nlB{_@b`AcN|f@wb@`iCa~@jkCqLhs@uR~qB_Lvm@sMtd@cyAfbDqYz^ap@nf@}HvTiFxd@_Q{@eGuK}iAc`EeEwy@cHy^eG{EcF~Fu]jrBaFrNkjCtfBwfBf{BuHfWaMxnAaHpO}QhQuEnZdBzsAj_@veCzE`f@gHzaBaNjwG{Fl\\_JzT{r@~RkcAxF_Q`SmtBlJwYrV{e@cc@gZeAmLzJw[biAgHnKcLlDcUeKcRxAciAdb@uoAuEs|@{Xkg@v[m]`i@}Bz[mLxh@}GtNfMti@|IwBrIzD{IyDkI`CqMaj@|GuNlLyh@|B{[~\\qh@|i@e]nz@xYtoAtEbiAeb@bRyAbUdKbLmDfHoKb]ykA`KeHhWPxh@vd@vYsVltBmJfNaRbfAyGnu@sVfKe\\`CeRfNiyG~G}vAiDgg@}`@{lCmBgjAhCs^h\\wc@|N}tAbHwTvfBg{BjjCufB`FsNt]krBfF_G`GzExHtb@jAbg@zBvOdkArbEdEdG~Pz@Ql`@vEx\\~Xf[xG|PzBpVMxr@tDheA}Azk@qFrb@geBxaIcMz}BesAzkQqLxxCqCf|DgQn`DaA|fEkMb{CzAxmByAbmAfAz]rXj_BnElhA~Fzn@lZnvA~Hzm@lRd}EfAbjAeCfxDmWh`CoCdeCzBn`Bd[|hGdQjSeQkSxBtn@mLdVwGv{@lDlVdT`P|DvV~KlRfCbXy@bsCdCftC}Df_CbHxAzU|m@tR`E~Q{L|T`Jl[Z|ToRfYwBxqA~jAlBmSrMqIhFoN`AcV`Q{EtE}SuE|SaQzEaAbViFnNsMpImBlS_qAsjAiY`BaWdSaZg@}TaJ_RzLuRaE{U}m@cHyAzDezB}BglCH}bBvUwa@`\\hBa\\iBwUva@I|bB~BdgC}Df_CbHxAzU|m@hUdEpQqL`U`J~AjU","p":[[64.96457,-15.15332,1,0,"07:30–08:30","Breakfast at Wilderness Center",""],[65.17537,-15.26903,2,0,"10:05–12:20","Stuðlagil Canyon east — walk in and out (45 min each way) + picnic lu…",""],[65.81605,-16.38732,3,0,"14:05–15:20","Dettifoss west viewpoint + short walk to Selfoss",""],[65.64091,-16.80931,4,0,"16:20–17:05","Hverir Námafjall geothermal area",""],[65.59122,-16.91228,5,0,"17:25–18:25","Dimmuborgir lava-pillar labyrinth (Kirkjuhringur/Church Circle loop)",""],[65.63089,-16.84761,6,0,"18:35–19:35","Mývatn Nature Baths (rebranded \"Earth Lagoon Mývatn\")","per v6 — confirm booked"],[65.62414,-16.9228,7,2,"19:45–20:00","Check in Vogafjós Farm Resort","BOOKED 5750735839"],[65.62414,-16.9228,8,0,"20:00–21:00","Dinner at Vogafjós Cowshed Café",""]],"h":[7,65.62414,-16.9228,"Vogafjós Farm Resort","conf 5750735839 PIN 5573"],"g":[[65.16352,-15.3096,"Stuðlagil Canyon west (Grund)","Shorter, easier canyon viewpoint · detour Alt to east side, same detour, walk 20-30 min RT, total 30 min, free · fits after 08:30 (use instead of eas…"],[65.62636,-16.88301,"Grjótagjá","Small lava cave (GoT cave) · detour ~5 min from Dimmuborgir, walk 10 min, total 10-15 min, free · fits after 17:25"],[65.72078,-16.79412,"Leirhnjúkur","Steaming lava field, less crowded · detour ~15 min off Rte 1, walk ~1 h RT, total 45-60 min, free · fits after 17:25 (trades against Dimmuborgir/bath…"],[65.57892,-16.95602,"Höfði","Lava pillars, birch-wood peninsula · detour ~5 min from Reykjahlíð, walk 30-40 min loop, total 30-40 min, free · fits after 21:30 (aurora pull-off)"]]},{"n":9,"t":"Wed Sep 23","a":"Vogafjós → Húsavík (whale watching, lunch, church, GeoSea, grocery) → Goðafoss → Svartabo…","c":"#ff00ff","l":"mfpoKhghfB_BkUaUaJqQpLiUeEoYkq@_^`QkHnh@yw@|uBkRhfCmQfwAoi@plCudBpyByk@v^_lEz{Huu@xdD{Yhg@_~AjgFuwAbfCu}AjoF}F`WyOlnA_fArnCeS~lA}c@l}@wgAhm@en@to@ym@l`A_c@jIyiA`t@ea@vKw~@xu@uRjZoMvIa{AhHyxAwc@{`AwOsaAyt@y_D|gAi{@|qAuG~Rep@pqGqFzRcQhWaDpN_YsTkl@qNw[`AuYjIqPwBybA}wAubBer@cYmXqa@gFyZk_@mGrEm^y[}J{AwOvKFoCoDbIwAeIgHpFpAxm@wLjVsH~BTjTUkTrH_CvLkVqAym@|L}IzKgHdY|Djb@|XhYv]l^jDd[pYtbBdr@xbA|wApPvBht@yKt^lGda@pQvHtMtEbSx\\n~DzJd`@b~Avu@||BviCbs@lZ~wAyOnc@iTbVaRvpCkpDlgBgq@pmAiQvKrA`z@jn@zl@~aApVvUtaBj^p|@sRjX_UpQeZlgA}j@vx@gp@`hB}Fho@rBhOmH`HwLzK|UzRzN`FdPnAxQ[nSoQvp@aAlSt@`RxEbRvUdQ|JrSzOrqArKpd@nPvWjm@hTjWtVzWnk@rVjdAxMh`@fSf]zc@pf@vGvOzFnXvCdb@aAldC~IrEjCdKkCeK_JsEeYd`AwT~iBmi@vsDoNr|Bed@ks@uRyt@oJuQot@a\\ee@w_@ga@uGmt@rFidBuu@edBiSwm@fBcmBs_Awn@mr@ie@yr@eg@wM_mA{n@qh@qGqUaKs\\`KhC`W","p":[[65.62414,-16.9228,1,0,"07:00–07:45","Breakfast at Vogafjós",""],[66.04617,-17.34372,2,0,"08:45–09:00","Check in, Silent Whale Watching (Hafnarstétt 9)","BOOKED (no email)"],[66.04617,-17.34372,3,0,"09:30–12:30","Silent Whale Watching tour (North Sailing, 3 h)","BOOKED (no email)"],[66.04617,-17.34372,4,0,"12:30–13:00","Lunch at Gamli Baukur or Salka Fish & Chips + walk past Húsavíkurkirk…",""],[66.05219,-17.36052,5,0,"13:05–14:35","GeoSea Sea Baths",""],[66.0467,-17.34321,6,0,"14:40–15:10","Krambúðin grocery top-up",""],[65.68278,-17.55019,7,0,"15:50–16:35","Goðafoss waterfall",""],[65.84788,-17.54611,8,2,"16:55–17:10","Check in Svartaborg villa","BOOKED 6132456281"],[65.84788,-17.54611,9,0,"17:30–18:30","Dinner, self-catered",""]],"h":[8,65.84788,-17.54611,"Svartaborg villa","conf 6132456281 PIN 0036"],"g":[[66.04689,-17.34457,"Húsavík Whale Museum","Whale-skeleton museum · detour on route, in town, walk n/a, total 1-1.5 h, ~2,900-3,500 ISK · fits after 12:30 (trades against lunch/church time)"],[66.04365,-17.33771,"Skrúðgarðurinn (botanic garden)","Small town park · detour on route, in town, walk 15 min, total 15-20 min, free · fits after 12:30"],[66.16667,-17.13333,"Tjörnes cliffs / fossil beach","Coastal cliffs, fossil layers · detour ~20-25 min north, walk 20-30 min, total 30 min, free · fits after 14:35 (tide-dependent; competes with grocery…"],[66.01777,-16.50554,"Ásbyrgi canyon","Horseshoe forested canyon · detour ~35-40 min NE, walk 30-60 min, total 45 min-1.5 h, free · fits after 14:35 (alt to Goðafoss, likely too far to als…"]]},{"n":10,"t":"Thu Sep 24","a":"Svartaborg → Akureyri → Kolugljúfur → Hvítserkur → Hvammstangi → Borgarnes → Hraunsnef, B…","c":"#ff8000","l":"e|{pKv}ajBiCaWr\\aKpSnJpj@bH~lAzn@dg@vM`JhIdX~e@xp@|t@nmBx_Anm@mB`dBhSvfB`v@~q@_Gp]tEzh@va@nt@`\\~IlPdS`v@dd@js@mAljBwI`eDkEnj@{Mp{@g@xfAmIlv@{Cpy@eS`kAq@ft@nE~gAOt\\g^rhCmG|uBiZptAmb@rhDw^xlAyS`wBwIng@qb@dfBiDzg@lG`lBxBdQhGfQbo@j^nI_@hUiO`LUtMrLrHbSp_@|lDjoB`dM~`@pgDrIdNrNtFfxBqy@lLbIlFnQtDx]`@b[iIxyAApy@_c@t`@k`@wBaXjV{Sz`AhBbUpANmDwVoQbk@aWhtAu_@zlCqH`_@kJvVyqBt~C{bB`qAk`@~v@oT|dAmAz}@}Clm@`IbjB|AlpApGho@f^hjB~ZjeAfzA``Exq@j|CxTlu@b{@hgBvxArqDz^j`@~OhFnZRpTpOzv@buAj\\bhA|jAvaBtnAfpCfr@tw@nu@jrAlVrP~^nLrlAdcApg@~rAvk@|~@hRdLbVj@lWlKh~@jGr_@tMhb@na@t{@f_Bby@vk@|MhYzS~|@rNp`@pSdZ|k@na@xc@t~@|b@rh@vK`YbX|cAxz@ppArl@l~A~MhVvCvO|Bpg@nM|n@hEhg@{ArcAtAlx@}AhnFtLbmDjHjhAlFdkBbJruAuNbhGd@xf@dOfoDmCt}@l@je@|QdeB~PrjC~Gzk@dDffAbDn_@|Hf`@hXbw@vGlu@jNtr@vLjzAdWvdAfKxl@jWb_A`Vd|AhDzt@OthC_C~aAwR`jCeJ~lCmUbnCyLzy@gSx_Agq@psByv@voCgn@bvA_w@~vBa^vj@mg@`_@}~@zJsd@~^al@fPemAtkAe`AjgAoWjl@kkAhsA{Lvd@yBls@|U|lEx@pw@aErs@g]xpC{@vk@~Cb}@v]oZ|FdBdErJdDrXhGbtBkDre@wUpl@gB`W^vRlGlZf`AzjBn`@j_@~H~RfRltAjOzt@`NfiBxVf~AtRhoBq@hb@qDnf@gUdsA_A~|@bI|}DgAflAfBfe@fF`_@tIvWh`@jg@~Ejc@a@nXuFbp@aIbqEd@`[~Flh@f@tY_CxWuOre@cO||@kp@`|BkBjPlLf{E{@rk@jD|g@ZdpAuBfPq{@jgDm[vzAuqArbD_~@tjDa^pcAgUt}@sg@z{CuSfuDcGhn@eYlwAeQnpAgf@baB_UhiBgNv_@}Zj]gNti@yUz[of@jkA_iAfqAge@zx@oj@vb@}Ylp@wj@p~@cl@`oCgKb}Ae_@xpB}B`i@nBnz@nJ|h@jXnb@zAhL~A~w@rBzLrOn[pg@|k@bj@dUtkAdFjpAjf@~yA`_A~u@n[|y@bk@f`Bpz@hp@bg@z^lCzr@lYpo@_^rq@~O`a@wLdV{Qtm@{HjLhHnFvQtp@n~DdA`YgBdg@XdXzRbhAjG|Rb\\|j@dh@luBh_@laAdGd]lIlqA|Ex]zc@roA~_@jzFhlA|}Fn{@|bCbk@peAlTrVje@pOfNvNrHzRfTv_Ahn@zuApuAotClFLr^j^rZhNbOJlb@}[|[l\\zRvFnl@cQ|^mAnJeKpL_[rSm@zTmTfQd@lSdPrMaS`DMrc@{eCzJqS_K|Sg[n`BcF~YqDji@_LhI_Rb]ce@lnAsa@blBuJnq@zBzd@aEbmAiK_d@qQke@}[ia@o[eIayAnKyk@sh@yd@gRad@g]qQ~qA{Vfs@ik@lYglAyNyPtx@dD|TrO|\\pAtP_ArSq[l{A}l@g]{|@[wKnEkZ`a@yd@tD}r@l[irBwU{N_MoC}H_MgiB}N{a@qYya@_fA_`AyLqWim@q[em@bE_HjKmMdj@gWbQ}jCgY{TnBo~@}Gsl@}Nkt@h@qc@_VqL[kDsMbEoYy@kCv@nC_E~YhD~LtNz@`^~Sxw@Gxn@fOf|@pGlSqBjlChYnWqQdMwi@~GkK`p@uC|g@vWhO|YxfA|`At[je@`Lp]hMjjBjT~UnqBhUjr@k[bd@eDl`@me@taAgAbfAnm@|f@~I`XxBdk@iEjXvMfOx@~TwLfh@eChs@|NvxAfoAdVtn@~|ArnBjYpl@lKnI{@vTxAzXph@v_CfGbb@xB`d@pArtCy@d`@_[~yEa[fgCDfi@jFbc@kMbQyVbKgNxMuh@|iAiTtUuiA~gBci@`r@ib@~s@ErVzAjEiCtGhCuG{AkEDsVhb@_t@|n@{y@zcAe`BhTuUbf@kfAdLyMl[uNjMcQhQxl@vWp\\pRxEzi@sK|K~Dpt@hlAxQjj@nRljAvsAx~Fd[ffBv_@nvAtq@jwFlGnXlPza@fWneAlNfcAtm@dzArMbSfO|H|gA_Oha@vF`X}BhQkL`lAmtAx`A{Obk@wRleA}n@bs@}Bpj@|Dzl@{N`~A\\|e@hJhTbUjKvYf\\zwAtK|ThO`NnjAbCxo@bY|e@tJnUxThKxBv]_Rt\\iFz]oVvo@wBbLbFdNfQtKrYjLbi@pGdMz{@h\\jJwGpXeh@lReQxf@yHtgB{j@|b@{`@~t@}oAlXcX~|@gVf^nOnN_Aru@ahArK{HrR`F`m@zy@r[rLfTwCpf@yc@zZ}A~r@{\\xbA|f@rMnWhWrjAn]jlCfPvm@|L`cAfZvv@dPhcAvXpw@dUxvB|Cpw@zHj\\dv@bl@daApUty@tp@`\\|Nfb@`a@fg@vKpd@bt@|L~a@fJnk@hI|XxHhr@tRls@`F`[bA|ZuBtvAqJ|bAq@va@vBr[fNlt@rElzBzQxvA~JpxAfh@rgClu@foBhOfk@dP``Adg@dy@fOtc@jEd`@jG`eCfDra@`k@d|CnVldAzVfw@tGrKz]rHrKnKzO`}@bN`Xnb@lmAvLxgAfGlWnKjSr]`Tx{@`zB`LzMfg@dWzd@~v@jRnTrp@t_@dY~Xx_@~n@|Kjb@tL~Up`@hf@lc@dXtRbZh[py@|VbXp]hn@jv@dfA`I|[d]`hD~Kh[f\\lb@bFlMdFx^l@ns@vB`S|n@fwAbTp[nDhRdCvi@hGnZ`LrOdjAnn@`KjQzRjl@nb@do@xMhb@lZb{ArQhp@hSj_@n`@bL`QfTh_@~gA~XlnArf@beBpg@drAzc@|o@nd@j_@nUbGvY~c@te@xh@tOmBbIbDlD{OrU{DIbGqG?pA`CqAaCxGCR{HsV~EqDvMyXl@cf@ai@kZwd@{TkFod@k_@{c@}o@qg@erAei@_lBoWgjAg^ieAcR}Tia@cMmQu]sQip@wWgsA}P_k@ab@kn@{Rkl@aKkQejAon@aLsO}EuSyDar@{DgRoScZsn@cvAeCuUi@_r@eFy^cFmM{Zi`@kMm]e]ahDaI}[kv@efAs^so@oVuWuZux@uRcZmc@eXq`@if@uL_V}Kkb@g\\wj@gZc[ax@wf@iw@qkAem@q]}~@q~Bwf@qa@sJi^sJcaAkG_TqEkClB`Df@yByX_y@uOa[yHej@uGqT_JyH{]sHaCtP","p":[[65.68771,-18.10496,1,0,"07:55–08:30","Breakfast in Akureyri (Bakaríið við Brúna)",""],[65.33338,-20.5713,2,0,"10:50–11:20","Kolugljúfur Canyon & Kolufossar",""],[65.60638,-20.63524,3,0,"12:05–12:50","Hvítserkur viewing platform",""],[65.39543,-20.94775,4,0,"13:50–14:30","Lunch at Sjávarborg, Hvammstangi",""],[65.39543,-20.94775,5,1,"14:30–14:45","Fuel: N1 self-service Hvammstangi (fill the tank)",""],[64.54111,-21.90932,6,0,"16:20–17:05","Grocery stop: Bónus Borgarnes",""],[64.77141,-21.53136,7,0,"17:35–18:35","Grábrók crater stairs (optional)",""],[64.78761,-21.50729,8,2,"18:55–19:10","Check in Hraunsnef Country Hotel","per v8 — no confirmation on file"],[64.78761,-21.50729,9,0,"19:10–20:10","Dinner at Hraunsnef restaurant (farm-to-table)",""],[64.78761,-21.50729,10,0,"20:10–20:55","Hraunsnef geothermal hot tubs",""]],"h":[9,64.78761,-21.50729,"Hraunsnef Country Hotel",""],"g":[]},{"n":11,"t":"Fri Sep 25","a":"Snæfellsnes loop (Hraunsnef → Kirkjufell → Saxhóll → Djúpalónssandur → Arnarstapi → Búðir…","c":"#00ffff","l":"szljKvsgbC`CuPz]rHrKnKzO`}@bN`Xnb@lmAvLxgAfGlWnKjSvSbIzH|I|~@p~Bdm@p]jt@phAv[~Wv[rNdY~Xx_@~n@|Kjb@tL~Up`@hf@lc@dXtRbZh[py@|VbXp]hn@jv@dfA`I|[d]`hD~Kh[f\\lb@bFlMdFx^l@ns@vB`S|n@fwAbTp[nDhRdCvi@hGnZ`LrOffAhk@rKbNfVxr@`_@ni@`KbWr`@~kBl[zcArNjO`^pJ|QbYv[baA~XlnArf@beBhd@tlAbg@lu@nd@j_@fPnGZd~C_FfVol@zoAki@~gBgh@veAcGnXiCni@eD`QuHtIwRfByJ~QoHzf@_Czm@wq@bnCqKt_AsP|aAeE~KaIdDcTiD_VtT_[fG_d@~u@gM`[cNdLqYaAo]jFwb@_YeOuCmPbDgh@h]qhAfJ}TaBqOnMmn@brAujAxtCcFb\\{CfjA_Jvp@mK~]kP`Vsf@dYwKnMiz@dkCyj@dj@sXnj@}Nhg@a[b{AkLhpA}Rrw@eY`z@axA`kDaKnDmjCoAqrCqZmJdAo~Al`ByJlVia@f|AoWhtBuXtuAiNzbAdAnmAs@jwAtCvbAsG|iBv@bbB|GlnArMdeA`f@b}@r]|gAvb@nv@hRheBzC~u@gBbXaRt|@qKxgB}wAnmL{@hd@jEpaEMtrAoJl{AoVh_AgGtk@UlsAaJnfDl@jo@lEfgAot@pbBy{@r~@{Yzn@eWnjAsPjeBmYv_A_XjpAoQrl@uRdgAsS`d@sh@zi@c]nlAgW|[kYro@sUrWcS~e@_Vt]_]`t@{WdToj@lN}b@yb@il@oWcX_Ski@aE{@jr@mEvo@wFdZ_Sln@oHtn@s@`u@dDld@fn@l`EIz_AiMlw@oBjZt@xnA~Bzk@`Lrh@fm@vfAlE`UfAlUSxWgIhq@yMvaCaCb}Aa]`aEoCvt@pBfn@fKnj@bTfl@dOtShVvNlL|SzGx_@xFxdAxh@fgCbKjSfY|Ttz@zZhPhk@|G~s@nG|fBcAvhA{Qdb@mBl\\nQbjBhA|j@iArZmHdj@uSjn@{H|g@sX|R}FvO|FwOrX}RsVljB{Ltl@k`@jxFgSrbA}Epd@{AxcAiKpwBnAfxByH~hBx@jo@iJhr@xDvw@~A`Kr\\pz@fh@nk@|~@rs@tWf_@b\\|z@dYfqAnVbqBz_@twBbh@~kA~i@pcAzArZeFf{AjKdoAvBxn@y@fo@cKfj@uAl[rBnaAkBnp@vCjl@Yh\\}H~bAeIp\\iLhP{Ff`AaPdlAeJf~BHfp@}IvaCwFr^yOvh@ex@vjB{F~WwDfc@uCvfBjR|xBlAzgArNnoCnGjm@lWlr@jNn~@|jAn`GxEtOrGlH|}@V`NsPlJw`@zTseB~LaoBdDgMta@cv@|c@x@bQpNpQgBzH|D`Wht@yP~\\xP_]xv@l`B|Tzw@nx@nrBjG|HbJnAfGeDva@yl@rlBahEhaCouDpVc|@bR{|@nWo_@dL`d@lMhVrThCzJlk@rN`QpGzXqG{XsNaQgKel@gTqBeMyUmLqd@~Ygj@hZ}jA~VcnAf[efAhZk~A`Vyp@xWcpCRua@kJkjBwE_e@geBywJiU}j@sFoj@tRif@lPwApFyFjYmfAbB_U}@aj@e@mIsFe@h@iBi@hBdGjBnAto@aBrWqYtfAkFpFmPvAuRhf@{Bw]{@or@sM_c@eJu~AzDy{@uBaY|DoIjDZkD[{CbHlA|`@uDhu@}DyL_FwB{i@`Wmo@tEmoAax@mXu^{Vg}@iJadAkXstBgk@}jBkC}g@}X}hBob@_yDkO}zCxEqjCpGcwArTocAtCyWBwUcEcWqX}a@gEmPoHe{@}OwdA`e@oV|J}x@pFoEn@eg@xNeWrCvDsCwDyNdWo@dg@qFnE}J|x@ae@nVaHg`@iJqFsFlG_W{q@mD{WoCgp@_B_sAdZu_JjIco@pn@g{C`E_[dEkyAk@gi@hCun@TmyAbWc}AnJi~BfT}aCbRi_B|F_jB`Hsu@j@}{@tFa`@x[}z@|DgTAyWeLi~@iAc[rFi}GtHojBwDoxAaKeq@iNkl@ga@ch@ok@aaAiWtAcHqDwReZqTao@cEeTyKqbCJmS~BwPzXmm@zFcc@k@uyAcNsjBxS_mDkAgR_N}o@eEug@`NcyCuJelAv@wsAiGamBmLiv@mm@mfBiJof@aK}yCc@s{@pIetC\\}wAfGuk@nVo_AxImpAb@o}AmE_cE|@{b@tvAwfLxLqnB`Ru|@fBcXsDwz@qQq`Byc@ux@q]ogAmb@eu@qE{RaIys@mGgdAcBcw@l@_qAlFsrAsCk~@p@}yA{@_qAxPwiAzUikAnWitBdb@k~A`Uec@|jAclAjQuF`tCrZ|jCnAnLgGlmB_|ExU__AjLipA`[c{A|Nig@rXoj@|j@oj@rl@ipBxMm[pt@_h@vNqUzJk]vIuo@zCgjAbFc\\tjAytCds@sxA`KaGtTbBzbAmH|m@c_@lPcDdOtC`_@`Xpe@yEdUlArL_Jvs@gtA~ZgG`VuT`ThDhIsD|DqKrP}aApKu_Avq@cnC~B{m@nH{f@xJ_RvRgBzHcJ~CsPhCoi@bGoXbi@sgAfi@ugBbp@iyAlBu]KmnCjdAbpAtOmBbIbDlD{OtQcBbIiIvp@cuBrKcOhMqCZySnK{c@|C}`@yCmsAUgrAuByc@oa@cvEqC{gAcJihBeGo]aWkv@yHw{@uDqcAcFeW}JkJuWdAmPiMe]`B_IqHeGsOiPuyAqJuwAwIod@_Vms@wIid@iHoaBkDyZeKoZo\\o]wNoe@oE}f@Kmf@|A{W|Mq~@jBwa@]cj@aGmb@yz@m{Bej@ot@sx@iaBmJ_^wRtPqI|@wNdaA{MtW{g@lwBkHtgB^ndBeHr`@","p":[[64.78761,-21.50729,1,0,"07:30–08:15","Breakfast at Hraunsnef, check out",""],[64.94167,-23.30694,2,0,"10:05–10:50","Kirkjufell Mountain & Kirkjufellsfoss","Free"],[64.85095,-23.92458,3,0,"11:35–12:20","Saxhóll crater stairs","Free"],[64.75229,-23.90052,4,0,"12:35–13:50","Djúpalónssandur black pebble beach","Free"],[64.73253,-23.78461,5,0,"14:00–14:25","Lóndrangar basalt pinnacles","Free"],[64.75179,-23.6456,6,0,"14:50–15:30","Lunch at Fjöruhúsið Café, Hellnar",""],[64.76431,-23.62487,7,0,"15:40–16:55","Arnarstapi coastal walk to Gatklettur arch","Free"],[64.82169,-23.38403,8,0,"17:15–17:45","Búðakirkja (black church)","Free"],[64.6276,-21.62431,9,2,"19:45–20:00","Check in The Hvítá Inn","per v8 — no confirmation on file"],[64.6276,-21.62431,10,0,"20:00–20:50","Dinner at The Hvítá Inn",""]],"h":[10,64.6276,-21.62431,"The Hvítá Inn",""],"g":[[64.8638,-24.0391,"Svörtuloft lighthouse","Orange lighthouse, cliffs · detour ~10 min gravel detour, walk 10 min, total 20 min, Free · fits after 12:20 (after Saxhóll)"],[64.74789,-23.81825,"Vatnshellir Cave","Lava tube guided tour · detour ~15 min detour, walk tour, total 45 min, ISK ~6,500 pp · fits after 13:50 (after Djúpalónssandur)"],[64.85776,-22.36734,"Gerðuberg","Hexagonal basalt wall · detour ~10–15 min off Rte 54, walk 10 min, total 20 min, Free · fits after 18:00 (on the drive to the Hvítá Inn, ~40 min in)"]]},{"n":12,"t":"Sat Sep 26","a":"Hvítá Inn → Hraunfossar → Víðgelmir → Hafnarfjörður → Seltún → ASKA Vellir → Blue Lagoon","c":"#8b008b","l":"yqmiKnm~bCdHs`@]{gBhHidB|g@owBxMsWvNeaAnJsAxQ_PoI}_AeR_qAmBg`AkC}^_Led@qRcWcDuN_@mTdDi\\|@eg@gCaZ{fAi|Bgb@mn@qy@y{As^gzAk_@igCwG}KgW_PyKac@sAa]`AamAcQsqBzOgbGdLgfCuD}wAqEeb@iSqi@km@gj@me@uqAwI{LsIoEwp@XuOoJ_K}S}DuQwMauA{DmcAd@ceHwG}jA^wsEdVkpHsDu_AoAc`CuGa_CDs}@qD_u@tFuwA_C}P~B|PyFprAxDd|@Sfs@~GjgCC`t@eIcBuWgUaBocBiMo_A{Q{mBk@gh@yLmi@qHm}AkPctAgFcV{LsWko@evBo^_fBmLilAeTyrAsb@ycBwMwuAfHk_AoAqFDiYoKqVuA_m@cGuq@sMs}@oAkb@`AsDaArCpAtc@pMh}@bGtq@tA~l@xKbYOvVnApFeHv_AtMjuAhc@bfB~RhnA`MboAn]rbBjeAthDvPfvAxGnyAdMfk@j@fh@fSnuB|Kzw@`BncB|XbV|GfAxA`^E~k@rDt_AaVdjHc@|yE~GhtAWtpHnP`yB|Gl^fPtX|InDtp@YnO|Krj@tyA`r@bq@xKrZ|EfYrFxlB_LxaC{OfbGfQfsBeAlkAlCxe@bJnZbWxObHxL~^lfCp]bxAl|@t`Br`@`l@tfA|{BfC`Z}@jg@eDb\\d@dU|C|MpRbW~Kdd@vBdW`C~gAdR~pA|JreA~HhXzUba@zb@baAzg@lp@r~@zdCbEjb@Gxk@sDfh@eKbo@iBta@`@v`@xE`e@lLn`@d^f`@zKb]tCdXhHnaBvIhd@~Uls@vInd@fKv{ArOruAdGrO~HpHl[kBbS|M`XeAxHpH|FtXxDrdAtHtz@~W~x@tE|W~JhnBnDvnAn`@bnExBlh@@jcA`D`|AmD|b@_Kpa@{@zSrZvMv[bFzNvRbL|c@`RjuB~^~y@~kA|yB`jCn_CnOGtrAoYfjDu\\`Qo\\f[ksA|Kwv@lLqb@pR}\\de@c_@rZmi@xP_oApLyuC~L}qAbGmV|j@wc@fHbCnXnZpKInIcH~_@krAfb@ydAv_@_sAbOa[lMcHpLlCtUd[b`@t[`Wfe@~v@bqCdYjmAtP`lAfWd_Ad^|j@dHlTfTn~A~Hf[hHlJz@oOaBsSqSgi@{D}Wc@gj@tC}`@`uCiqHdNkAlYrd@~[vAx\\s\\b\\sJny@_Jt_@gOzy@M`MeI`b@_o@tu@oh@hQ}V|Umx@tm@eiD`_@mpApHm_@bBgZQwWaRahDp@{XlCsRrOeYlYJnLiHb[yv@zT_x@|k@wr@h_@uQvYiEfNzGtSh_@lDzd@pO`j@\\v_@oBb^hC|h@|FjVbR`Zxf@~fBpY~c@te@vfAnm@|`@hRdSrKzVxZfjA`Gzd@xAn\\zBpiGaElwAcCjTsCi@b@qGhd@{Ld`@aTzYjFjOhPlKlg@fIxcB`Hh^tNbb@df@hv@vSzNzd@nG~Qh]nQvy@~OfjAjElxA|Exb@nh@wJx@tOqApn@dCrq@dUdMvFxZ}Fa\\{PsLbCcAgGxAdUdM`El[pl@fRbFtPxNveBbEnDhS{Q~D~b@nYwNzN}PbMeg@nI}QlzCkhE~GcAlyE`fA`VkJv[ug@pMm[dGwBrJtTbEd^fGrO~XpVdh@~s@rI|~@nO`i@xXpb@vE~SxNrO`KhTpDwAtEnD|EaDfGlLjNb@lAjYpBlI|uAx{DfHjKxfAjv@~Tli@_D|G~C}GiRcf@oiAuy@gHkKqvAi}DkDib@kNc@gGmLkFdDyEwDiCjBmLsUcNyNyEcT}Wy`@iPuj@sI}~@eh@_t@_YqVwGmQsDk\\sJuTqGjCmLtYo\\xh@aVjJm_FqeAa~C~jEcVpt@}E}GqAoKkWjM|FoBxD}Bw[pLxC~b@qMzOiO|GeEdNtFnVlRdeB|b@fzBlKbt@lR|jDbQzn@lMj`AfUfeA|Op~BfTfcAfHvi@fT|rErGl]nWvn@n[p`Ch`@|_DbYd_E|K`dAle@tpCnZj|Bp|@rpFz^r`BnOj|@zTv`CjFf}AkBfYpg@oEhr@zp@hKhD|dBcMt{@q[z|@wIbjBjHtZjZvI_DlMyR`QgAc@rXkCdRt@vUy@pN`CzPErPvIoAdB_I`CtEaDrn@`Dsn@aCuEeB~HwInADsPaC{Px@wNu@qUjCeRb@sXuQvAyLhRwI~CwYaZyhB{Ie_ArIqz@b\\_fBzMiKiDso@io@c]x@yDaI{IyqBiT_{B_w@ozDyu@owEiY}vB}g@y|C}JuaAgVgmDuNuoAyl@ysEmHcYmPk^_Ga\\sReeEuGul@sXswAkMkpBmV{jAaMq~@wPkm@mMqiCkFam@qn@{fDsJsu@eFao@?cYvDsIxWuReDmc@zT_K","p":[[64.6276,-21.62431,1,0,"07:00–07:35","Breakfast at Hvítá Inn, check out",""],[64.7029,-20.97717,2,0,"08:10–09:05","Hraunfossar & Barnafoss waterfalls","Free"],[64.75313,-20.80185,3,0,"10:00–12:00","Víðgelmir lava cave tour","per v8 — not confirmed (10:00 slot)"],[64.06522,-21.95581,4,0,"14:20–15:05","Lunch at Fjörukráin (Viking Village), Hafnarfjörður",""],[63.89573,-22.05221,5,0,"15:35–16:35","Seltún geothermal area boardwalks","parking ISK 750 (camera)"],[64.04316,-21.98188,6,2,"17:10–17:30","Check in ASKA Vellir, unpack","per v9 — booked per Zach, no confirmati…"],[64.04316,-21.98188,7,0,"17:30–19:00","Dinner — TBD (before or after the Blue Lagoon)",""],[63.88072,-22.4473,8,0,"20:00–21:30","Blue Lagoon evening soak","per v9 — timed ticket needed, not confi…"]],"h":[11,64.04316,-21.98188,"ASKA Vellir, Hafnarfjörður",""],"g":[]},{"n":13,"t":"Sun Sep 27","a":"ASKA Vellir → KEF → fly home","c":"#556b2f","l":"cm{eKbfdeCxD}Bw[pLxC~b@qMzOiO|GeEdNtFnVlRdeB|b@fzBlKbt@lR|jDbQzn@lMj`AfUfeA|Op~BfTfcAfHvi@fT|rErGl]nWvn@n[p`Ch`@|_DbYd_E|K`dAle@tpCnZj|Bp|@rpFnk@tlCvV|aCbO`mEJdjBi\\t_Fq`@v{IcEDo@pK|AtA}AuAn@qKrCpDuB|]oJ|e@cd@dt@oJb]qNxZac@pm@mi@xaAsb@prAr@dr@|Il|AlCxLvDlBFnIrFAsF@GoIfGkIv@sSlC_E?qFdA?@rF","p":[[64.04316,-21.98188,1,2,"06:45–07:15","Check out of ASKA Vellir",""],[63.97253,-22.54339,2,1,"07:40–07:55","Refuel rental car (diesel)",""],[63.99806,-22.63028,3,0,"08:10–08:25","Return rental car","BOOKED OTB05W (no email)"],[63.99662,-22.62318,4,0,"08:30–09:10","Delta check-in and bag drop","BOOKED GZZZ8X"],[63.99662,-22.62318,5,0,"09:10–09:50","Security and passport control",""],[63.99662,-22.62318,6,0,"09:50–10:45","Airside breakfast",""]],"h":null,"g":[]}]};
const EMBEDDED_KEY = "AIzaSyCd38wDuESBzp6oN-ySUTB1ErpJMj93b9Q"; // "" in the repo copy; set only in the --share upload copy (Decision 35)
const KEY_NAME = 'GOOGLE_MAPS_BROWSER_KEY';
const CESIUM = 'https://cdn.jsdelivr.net/npm/cesium@1.145.0/Build/Cesium';
const RED = '#ff3b30', HOTEL = '#2b3440';
const $ = s => document.querySelector(s);
const esc = s => String(s ?? '').replace(/[&<>"]/g, c => ({'&': '&', '<': '<', '>': '>', '"': '"'}[c]));
const gmaps = (lat, lng) => `https://www.google.com/maps/search/?api=1&query=${lat},${lng}`;
const ink = c => (parseInt(c.slice(1, 3), 16) * 299 + parseInt(c.slice(3, 5), 16) * 587 + parseInt(c.slice(5, 7), 16) * 114) / 1000 > 150 ? '#111111' : '#ffffff';
const sleep = ms => new Promise(r => setTimeout(r, ms));
const km = (a, b) => 111.2 * Math.hypot(a.lat - b.lat, (a.lng - b.lng) * Math.cos(a.lat * Math.PI / 180));
const bearing = (a, b) => (Math.atan2((b.lng - a.lng) * Math.cos(a.lat * Math.PI / 180), b.lat - a.lat) * 180 / Math.PI + 360) % 360;
function decode(s) {
const out = []; let i = 0, lat = 0, lng = 0;
while (i < s.length) {
for (const k of [0, 1]) {
let r = 0, sh = 0, b;
do { b = s.charCodeAt(i++) - 63; r |= (b & 31) << sh; sh += 5; } while (b >= 32);
const d = r & 1 ? ~(r >> 1) : r >> 1;
if (k) lng += d; else lat += d;
}
out.push({lat: lat / 1e5, lng: lng / 1e5});
}
return out;
}
function camFor(pts, tilt = 55) {
const lats = pts.map(p => p.lat), lngs = pts.map(p => p.lng);
const c = {lat: (Math.max(...lats) + Math.min(...lats)) / 2, lng: (Math.max(...lngs) + Math.min(...lngs)) / 2};
const span = Math.max(km({lat: Math.min(...lats), lng: c.lng}, {lat: Math.max(...lats), lng: c.lng}),
km({lat: c.lat, lng: Math.min(...lngs)}, {lat: c.lat, lng: Math.max(...lngs)}), 1.5);
return {...c, range: span * 1000 * 2.2, tilt, heading: 0};
}
// ---- data ----
const DAYS = TRIP.days.map(d => {
const path = d.l ? decode(d.l) : [];
const pts = path.length ? path : d.p.map(p => ({lat: p[0], lng: p[1]}));
return {...d, path, cam: camFor(pts), pins: [], gemPins: [], line: null};
});
const OVER = camFor(DAYS.flatMap(d => d.path), 45);
// ---- messages ----
function show(html) { $('#msg').hidden = false; $('#msg').innerHTML = `<div class="card glass">${html}</div>`; }
function status(t) { $('#engine').textContent = t; }
const SAVED_KEY = 'google-maps-key'; // per-user widget state (vibes.save), never in this file
const V = window.vibes || {};
const PASTE = `<h2>Paste the map key</h2>
<p>This map shows Google's photorealistic 3D tiles and needs the Google Maps API key shared with the family.</p>
<form id="keyForm">
<input id="keyIn" type="password" autocomplete="off" spellcheck="false" placeholder="Google Maps API key" aria-label="Google Maps API key">
<button class="b">Load map</button>
<label><input type="checkbox" id="keyRem" checked> Remember on my It Just Vibes account</label>
</form>
<p class="m">The key is sent only to Google and, if remembered, to your own saved data for this widget. It is not part of the widget file.</p>`;
async function getKey() {
if (EMBEDDED_KEY) return EMBEDDED_KEY;
if (typeof V.browserKey === 'function') { // platform browser secret, if the runner ever offers one
try { const k = await V.browserKey(KEY_NAME); if (typeof k === 'string' && k.trim()) return k.trim(); } catch (e) {}
}
try { const k = await V.load?.(SAVED_KEY); if (typeof k === 'string' && k.trim()) return k.trim(); } catch (e) {}
return new Promise(resolve => {
show(PASTE);
$('#keyIn').focus();
$('#keyForm').onsubmit = e => {
e.preventDefault();
const k = $('#keyIn').value.trim();
if (!k) return;
if ($('#keyRem').checked) Promise.resolve().then(() => V.save?.(SAVED_KEY, k)).catch(() => {});
resolve(k);
};
});
}
async function referrerInfo() {
let policy = document.querySelector('meta[name=referrer]')?.content || '';
if (!policy) {
try {
const r = await fetch(location.href, {method: 'HEAD', cache: 'no-store'});
policy = r.headers.get('referrer-policy') || '';
} catch (e) { policy = 'unreadable (' + e.message + ')'; }
}
const last = policy.split(',').map(s => s.trim()).filter(Boolean).pop() || '';
const none = /^(no-referrer|same-origin)$/.test(last);
let docRef = '(empty)';
try { if (document.referrer) docRef = new URL(document.referrer).origin; } catch (e) {}
return {policy: policy || 'not set (browser default strict-origin-when-cross-origin)', none, docRef};
}
let authShown = false;
async function authError(detail) {
if (authShown) return;
authShown = true;
stopTour();
const r = await referrerInfo();
show(`<h2>Google refused the map key</h2>
<p>${esc(detail)}</p>
<p class="m">Page origin (location.origin): <code>${esc(location.origin)}</code><br>
Referrer-Policy: <code>${esc(r.policy)}</code> → ${r.none
? '<b>Google receives no Referer from this page, so a key with website restrictions is always refused.</b>'
: `Google receives <code>${esc(location.origin)}/</code> as the Referer.`}<br>
document.referrer: <code>${esc(r.docRef)}</code></p>
<ol>
<li>${r.none ? 'Key → Application restrictions: <b>None</b> (API restrictions are fine).' : `Key website restriction includes <code>${esc(location.origin)}/*</code>.`}</li>
<li>Key API restriction allows <b>Maps JavaScript API</b> and <b>Map Tiles API</b>, and both are enabled on the project.</li>
<li>Billing is enabled on the Google Cloud project.</li>
<li>The pasted key is the whole key (39 characters, no spaces).</li>
</ol>
<p><button class="b" id="forget">Forget saved key</button> <span class="m" id="forgotMsg"></span></p>
<p class="m">The browser console shows Google's error code (RefererNotAllowedMapError, ApiNotActivatedMapError, InvalidKeyMapError, BillingNotEnabledMapError).</p>`);
$('#forget').onclick = async () => {
try { await V.delete?.(SAVED_KEY); $('#forgotMsg').textContent = 'Removed. Reload the widget to paste a key again.'; }
catch (e) { $('#forgotMsg').textContent = 'Could not remove: ' + (e?.message || e); }
};
}
window.gm_authFailure = () => authError('gm_authFailure: Google Maps rejected the key for this page.');
// Map3DElement does not always call gm_authFailure; Google still logs the code (e.g. InvalidKeyMapError).
const logError = console.error;
console.error = (...a) => {
logError.apply(console, a);
const m = String(a[0]).match(/Google Maps JavaScript API error: (\w+MapError)/);
if (m) authError(`Google Maps error ${m[1]}: the key was refused for this page.`);
};
// ---- engine: Google Maps 3D (primary) ----
function loadGoogle(key) {
// Google's dynamic bootstrap loader, verbatim (key passed in at runtime, never stored).
(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({key, v: 'weekly'});
}
async function googleEngine(key) {
loadGoogle(key);
const {Map3DElement, Marker3DInteractiveElement, Polyline3DElement} = await google.maps.importLibrary('maps3d');
const {PinElement} = await google.maps.importLibrary('marker');
if (!Map3DElement || !Marker3DInteractiveElement || !Polyline3DElement) throw Error('maps3d library has no 3D elements');
const cam = c => ({center: {lat: c.lat, lng: c.lng, altitude: 0}, range: c.range, tilt: c.tilt, heading: c.heading || 0});
const map = new Map3DElement();
Object.assign(map, cam(OVER), {mode: 'HYBRID', defaultUIHidden: true, gestureHandling: 'GREEDY'}); // own controls; one-finger pan in the iframe
$('#map').append(map);
await new Promise((ok, fail) => {
map.addEventListener('gmp-steadychange', ok, {once: true});
map.addEventListener('gmp-error', e => fail(Error('Map3DElement gmp-error ' + (e.error?.message || ''))), {once: true});
setTimeout(ok, 15000); // slow network: keep the Google map rather than double-loading Cesium
});
let pend = null;
const anim = (start, ms) => new Promise(res => {
const done = () => { map.removeEventListener('gmp-animationend', done); if (pend === done) pend = null; res(); };
pend = done;
map.addEventListener('gmp-animationend', done);
start(Math.max(50, ms));
});
return {
name: 'Google Maps 3D (Map3DElement)',
pin(p, glyph, color, title, onClick, z) {
const m = new Marker3DInteractiveElement();
Object.assign(m, {position: {lat: p[0], lng: p[1]}, title, drawsWhenOccluded: true, zIndex: z});
const pe = new PinElement({background: color, borderColor: '#ffffff', glyphColor: ink(color)});
if ('glyphText' in pe) pe.glyphText = String(glyph); else pe.glyph = String(glyph);
m.append(pe);
m.addEventListener('gmp-click', onClick);
return {show(v) { if (v && !m.isConnected) map.append(m); else if (!v && m.isConnected) m.remove(); }};
},
line(path, color) {
const l = new Polyline3DElement();
Object.assign(l, {path, strokeColor: color, strokeWidth: 5, altitudeMode: 'CLAMP_TO_GROUND', drawsOccludedSegments: true});
map.append(l);
return {style(s) { l.strokeColor = s === 'dim' ? color + '59' : color; l.strokeWidth = s === 'hi' ? 9 : s === 'dim' ? 3 : 5; l.zIndex = s === 'hi' ? 2 : 1; }};
},
fly: (c, ms) => anim(d => map.flyCameraTo({endCamera: cam(c), durationMillis: d}), ms),
around: (c, ms) => anim(d => map.flyCameraAround({camera: cam(c), durationMillis: d, repeatCount: 1}), ms),
zoom(f) {
const c = map.center;
return anim(d => map.flyCameraTo({endCamera: {center: {lat: c.lat, lng: c.lng, altitude: c.altitude || 0}, range: map.range * f, tilt: map.tilt, heading: map.heading}, durationMillis: d}), 350);
},
stop() { map.stopCameraAnimation(); pend?.(); },
};
}
// ---- engine: CesiumJS + Google Photorealistic 3D Tiles (fallback) ----
async function cesiumEngine(key) {
window.CESIUM_BASE_URL = CESIUM + '/';
const css = Object.assign(document.createElement('link'), {rel: 'stylesheet', href: CESIUM + '/Widgets/widgets.css'});
document.head.append(css);
await new Promise((ok, fail) => document.head.append(Object.assign(document.createElement('script'),
{src: CESIUM + '/Cesium.js', onload: ok, onerror: () => fail(Error('CesiumJS could not load from jsDelivr'))})));
const C = window.Cesium;
$('#map').replaceChildren();
const viewer = new C.Viewer($('#map'), {globe: false, baseLayer: false, baseLayerPicker: false, geocoder: false,
homeButton: false, sceneModePicker: false, navigationHelpButton: false, animation: false, timeline: false,
fullscreenButton: false, infoBox: false, selectionIndicator: false});
viewer.scene.primitives.add(await C.createGooglePhotorealistic3DTileset({key, onlyUsingWithGoogleGeocoder: true}));
new C.ScreenSpaceEventHandler(viewer.scene.canvas).setInputAction(ev => viewer.scene.pick(ev.position)?.id?._click?.(),
C.ScreenSpaceEventType.LEFT_CLICK);
const at = c => C.Cartesian3.fromDegrees(c.lng, c.lat, 60);
const hpr = (c, turn = 0) => new C.HeadingPitchRange(C.Math.toRadians((c.heading || 0) + turn), C.Math.toRadians(c.tilt - 90), c.range);
const ground = C.HeightReference.CLAMP_TO_GROUND;
let pend = null;
const eng = {
name: 'CesiumJS 1.145.0 + Google Photorealistic 3D Tiles',
pin(p, glyph, color, title, onClick) {
const e = viewer.entities.add({position: C.Cartesian3.fromDegrees(p[1], p[0]), name: title, show: false,
point: {pixelSize: 20, color: C.Color.fromCssColorString(color), outlineColor: C.Color.WHITE, outlineWidth: 2, heightReference: ground, disableDepthTestDistance: Infinity},
label: {text: String(glyph), font: 'bold 11px sans-serif', fillColor: C.Color.fromCssColorString(ink(color)), heightReference: ground, disableDepthTestDistance: Infinity,
horizontalOrigin: C.HorizontalOrigin.CENTER, verticalOrigin: C.VerticalOrigin.CENTER}});
e._click = onClick;
return {show(v) { e.show = v; }};
},
line(path, color) {
const col = C.Color.fromCssColorString(color);
const e = viewer.entities.add({polyline: {positions: C.Cartesian3.fromDegreesArray(path.flatMap(p => [p.lng, p.lat])), width: 5, clampToGround: true, material: col}});
return {style(s) { e.polyline.material = s === 'dim' ? col.withAlpha(0.35) : col; e.polyline.width = s === 'hi' ? 9 : s === 'dim' ? 3 : 5; }};
},
fly: (c, ms) => new Promise(res => {
pend = res;
viewer.camera.flyToBoundingSphere(new C.BoundingSphere(at(c), 1), {offset: hpr(c), duration: ms / 1000, complete: res, cancel: res});
}),
around: (c, ms) => new Promise(res => {
const t0 = performance.now();
pend = res;
const tick = now => {
if (pend !== res) return;
const k = Math.min(1, (now - t0) / ms);
viewer.camera.lookAt(at(c), hpr(c, 360 * k));
if (k < 1) requestAnimationFrame(tick);
else { viewer.camera.lookAtTransform(C.Matrix4.IDENTITY); pend = null; res(); }
};
requestAnimationFrame(tick);
}),
zoom(f) { const h = viewer.camera.positionCartographic.height; f < 1 ? viewer.camera.zoomIn(h * (1 - f)) : viewer.camera.zoomOut(h * (f - 1)); },
stop() { viewer.camera.cancelFlight(); viewer.camera.lookAtTransform(C.Matrix4.IDENTITY); const p = pend; pend = null; p?.(); },
};
viewer.camera.flyToBoundingSphere(new C.BoundingSphere(at(OVER), 1), {offset: hpr(OVER), duration: 0});
return eng;
}
// ---- viewer ----
let E = null, cur = -1, openIdx = -1, run = 0, steps = [], idx = 0, paused = false;
const mobile = () => matchMedia('(max-width: 759px)').matches;
function buildItems() { // one list row per stop / ALERT / hotel / gem; the map pin is attached in buildMap
DAYS.forEach(d => {
d.items = d.p.map(([lat, lng, glyph, kind, time, what, ref]) => ({kind: kind === 3 ? 'alert' : 'stop', lat, lng,
glyph: kind === 1 ? '⛽' : kind === 2 ? '⌂' : glyph, color: kind === 3 ? RED : d.c, time, title: what, ref}));
if (d.h) {
const [num, lat, lng, name, ref] = d.h;
d.items.push({kind: 'hotel', lat, lng, glyph: num, color: HOTEL, time: 'Night ' + num, title: name, ref});
}
d.g.forEach(([lat, lng, name, note]) => d.items.push({kind: 'gem', lat, lng, glyph: '◆', color: d.c, time: '', title: name, ref: note}));
});
}
function buildMap() {
DAYS.forEach((d, k) => {
if (d.path.length) d.line = E.line(d.path, d.c);
d.items.forEach((it, i) => {
it.pin = E.pin([it.lat, it.lng], it.glyph, it.color, it.title, () => { pauseTour(); focusItem(k, i); },
{gem: 1, stop: 2, alert: 3, hotel: 4}[it.kind]);
if (it.kind === 'hotel') it.pin.show(true); // hotel pins 1-11 always visible
});
});
}
function renderChips() {
$('#chips').innerHTML = '<button class="chip" role="tab" data-k="-1"><b>All</b><small>days</small></button>'
+ DAYS.map((d, k) => `<button class="chip" role="tab" data-k="${k}" style="--c:${d.c}" aria-label="Day ${d.n}, ${esc(d.t)}"><b>${d.n}</b><small>${esc(d.t.split(' ')[0])}</small><i></i></button>`).join('');
}
function renderHeader() {
const d = DAYS[cur];
$('#back').hidden = !d;
$('#pt').textContent = d ? `Day ${d.n} · ${d.t}` : 'Iceland 2026';
$('#ps').textContent = d ? d.a : 'Sep 15–27 · 13 days · pick a day';
document.querySelectorAll('.chip').forEach(c => c.setAttribute('aria-selected', +c.dataset.k === cur));
document.querySelector(`.chip[data-k="${cur}"]`)?.scrollIntoView({block: 'nearest', inline: 'center'});
}
function renderList() {
const L = $('#list');
if (cur < 0) {
L.innerHTML = DAYS.map((d, k) => `<button class="day" data-k="${k}"><span class="bar" style="background:${d.c}"></span><span class="nm">`
+ `<span class="dt">Day ${d.n} · ${esc(d.t)}</span><span class="da">${esc(d.a)}</span>`
+ `<span class="dm">${((n) => n + (n === 1 ? ' stop' : ' stops'))(d.items.filter(x => x.kind === 'stop').length)}${d.h ? ' · ⌂ ' + esc(d.h[3]) : ''}</span></span></button>`).join('');
return;
}
const d = DAYS[cur], rows = d.items.map((it, i) => [it, i]);
const row = ([it, i]) => {
const [a, b] = String(it.time).split('–'), open = i === openIdx;
return `<li class="it ${it.kind}${open ? ' open' : ''}"><button class="it-h" data-i="${i}" aria-expanded="${open}">`
+ `<span class="tm"><b>${esc(a)}</b>${esc(b || '')}</span><span class="dot" style="background:${it.color};color:${ink(it.color)}">${esc(it.glyph)}</span>`
+ `<span class="nm">${esc(it.title)}</span></button>`
+ (open ? `<div class="it-d">${it.ref ? `<p>${esc(it.ref)}</p>` : ''}<a class="gm" href="${gmaps(it.lat, it.lng)}" target="_blank" rel="noopener">Open in Google Maps</a></div>` : '')
+ '</li>';
};
const plan = rows.filter(([it]) => it.kind !== 'gem'), gems = rows.filter(([it]) => it.kind === 'gem');
L.innerHTML = `<div class="sec">Plan</div><ol>${plan.map(row).join('')}</ol>`
+ (!gems.length ? '' : $('#gems').checked ? `<div class="sec">Optional gems</div><ol>${gems.map(row).join('')}</ol>`
: `<p class="hint">${gems.length} optional gem${gems.length > 1 ? 's' : ''} for this day: turn on Gems to see them.</p>`);
L.querySelector('.it.open')?.scrollIntoView({block: 'nearest'});
}
function select(i, fly = true) {
cur = i;
openIdx = -1;
const gems = $('#gems').checked;
DAYS.forEach((d, k) => {
d.line?.style(i < 0 ? 'norm' : k === i ? 'hi' : 'dim');
d.items.forEach(it => {
if (it.kind !== 'hotel') it.pin.show(it.kind === 'gem' ? gems && (i < 0 || k === i) : k === i);
});
});
renderHeader();
renderList();
$('#list').scrollTop = 0;
ui();
if (fly) E.fly(i < 0 ? OVER : DAYS[i].cam, 3000);
}
function focusItem(k, i, fly = true) {
if (cur !== k) select(k, false);
openIdx = i;
renderList();
raiseSheet();
const it = DAYS[k].items[i];
if (fly) E.fly({lat: it.lat, lng: it.lng, range: 2500, tilt: 60, heading: 0}, 1800);
}
// ---- bottom sheet (phones) ----
const sheetH = () => $('#panel').getBoundingClientRect().height;
function setSheet(h, snap) {
if (snap) h = [150, innerHeight * 0.5, innerHeight * 0.88].reduce((a, b) => Math.abs(b - h) < Math.abs(a - h) ? b : a);
h = Math.max(120, Math.min(innerHeight * 0.92, h));
document.documentElement.style.setProperty('--sheet', Math.round(h) + 'px');
$('#fabs').classList.toggle('low', h > innerHeight * 0.6);
}
function raiseSheet() { if (mobile() && sheetH() < innerHeight * 0.45) setSheet(innerHeight * 0.5); }
function wireSheet() {
const g = $('#grab');
g.addEventListener('pointerdown', e => {
const y0 = e.clientY, h0 = sheetH();
let moved = false;
$('#panel').classList.add('drag');
g.setPointerCapture(e.pointerId);
const move = ev => { if (Math.abs(ev.clientY - y0) > 4) moved = true; setSheet(h0 - (ev.clientY - y0)); };
const up = () => {
g.removeEventListener('pointermove', move);
$('#panel').classList.remove('drag');
setSheet(moved ? sheetH() : h0 < innerHeight * 0.4 ? innerHeight * 0.5 : 150, true);
};
g.addEventListener('pointermove', move);
g.addEventListener('pointerup', up, {once: true});
g.addEventListener('pointercancel', up, {once: true});
});
addEventListener('resize', () => mobile() && setSheet(sheetH(), true));
}
// ---- tours ----
const speed = () => +$('#speed').value;
function ui() {
const on = steps.length > 0;
$('#bDay').hidden = on || cur < 0;
$('#bTrip').hidden = on;
$('#bPause').hidden = $('#bStop').hidden = !on;
$('#bPause').textContent = paused ? '▶ Resume' : '❚❚ Pause';
}
async function play() {
const g = ++run;
paused = false;
ui();
while (idx < steps.length) {
const s = steps[idx];
s.pre?.();
await (s.wait ? sleep(s.wait / speed()) : s.around ? E.around(s.cam, s.ms / speed()) : E.fly(s.cam, s.ms / speed()));
if (g !== run) return;
s.post?.();
idx++;
}
steps = [];
ui();
}
function startTour(list) { stopTour(); steps = list; idx = 0; play(); }
function stopTour() { run++; steps = []; idx = 0; paused = false; E?.stop(); ui(); }
function togglePause() {
if (!steps.length) return;
if (paused) play(); else { paused = true; run++; E.stop(); ui(); }
}
function pauseTour() { if (steps.length && !paused) togglePause(); }
function dayTour(k) {
const d = DAYS[k], out = [{pre: () => select(k, false), cam: d.cam, ms: 3000}];
let prev = null;
d.items.forEach((p, i) => {
if (p.kind === 'gem' || (prev && p.lat === prev.lat && p.lng === prev.lng)) return;
out.push({cam: {lat: p.lat, lng: p.lng, range: 1800, tilt: 65, heading: prev ? bearing(prev, p) : 0},
ms: Math.min(9000, Math.max(2500, (prev ? km(prev, p) : 0) * 60)), post: () => focusItem(k, i, false)}, {wait: 2200});
prev = p;
});
if (d.path.length) out.push({pre: () => { openIdx = -1; renderList(); }, cam: d.cam, ms: 3000}, {around: true, cam: d.cam, ms: 9000});
return out;
}
const tripTour = () => DAYS.slice(1).flatMap((d, j) => [ // Days 2-13; Day 1 is the Atlanta departure
{pre: () => select(j + 1, false), cam: d.cam, ms: 4000}, {around: true, cam: d.cam, ms: 9000}]);
function wire() {
$('#chips').addEventListener('click', e => { const c = e.target.closest('.chip'); if (c) { stopTour(); select(+c.dataset.k); if (cur >= 0) raiseSheet(); } });
$('#back').onclick = () => { stopTour(); select(-1); };
$('#list').addEventListener('click', e => {
const day = e.target.closest('.day');
if (day) { stopTour(); select(+day.dataset.k); raiseSheet(); return; }
const h = e.target.closest('.it-h');
if (!h) return;
pauseTour();
if (+h.dataset.i === openIdx) { openIdx = -1; renderList(); } else focusItem(cur, +h.dataset.i);
});
$('#gems').onchange = () => { const o = openIdx; select(cur, false); openIdx = o; renderList(); };
$('#bTrip').onclick = () => startTour(tripTour());
$('#bDay').onclick = () => cur >= 0 && startTour(dayTour(cur));
$('#bPause').onclick = togglePause;
$('#bStop').onclick = stopTour;
$('#zin').onclick = () => { pauseTour(); E.zoom(0.5); };
$('#zout').onclick = () => { pauseTour(); E.zoom(2); };
$('#recenter').onclick = () => { pauseTour(); E.stop(); E.fly(cur < 0 ? OVER : DAYS[cur].cam, 1500); };
$('#map').addEventListener('pointerdown', pauseTour); // grabbing the map pauses a tour instead of fighting it
$('.ttl').addEventListener('click', () => mobile() && setSheet(sheetH() < innerHeight * 0.4 ? innerHeight * 0.5 : 150, true));
document.addEventListener('keydown', e => {
if (e.target.closest?.('select,input') || !E) return;
const step = {ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1}[e.key];
if (step) { e.preventDefault(); stopTour(); select(Math.max(0, Math.min(DAYS.length - 1, cur + step))); }
else if (e.key === 'Escape') { openIdx = -1; renderList(); }
else if (e.key === ' ' && steps.length) { e.preventDefault(); togglePause(); }
});
wireSheet();
}
async function start() {
buildItems();
renderChips();
renderHeader();
renderList();
if (typeof V.onReady === 'function') await Promise.race([new Promise(r => V.onReady(r)), sleep(10000)]);
const key = await getKey();
show('<p class="m">Loading photorealistic 3D map…</p>');
let note = '';
try {
if (location.hash === '#cesium') throw Error('#cesium forces the fallback');
E = await googleEngine(key);
} catch (e) {
note = e.message;
if (authShown) return;
try { E = await cesiumEngine(key); }
catch (e2) { return authError(`Google Maps 3D failed (${note}) and the CesiumJS fallback failed (${e2?.message || e2}).`); }
}
if (authShown) return;
$('#msg').hidden = true;
status(E.name + (note ? ' · fallback: ' + note : ''));
buildMap();
wire();
select(-1, false);
}
start();
</script>
</body>
</html>
```
[REQUESTED CHANGES]
(no specific request — apply your best judgment)
--- HOW TO RESPOND (READ FIRST) ---
Before writing any code, follow this exact process:
1. **ANALYZE** the widget source code provided above and identify:
a. Which Vibes SDK features it already uses (vibes.save, vibes.load, vibes.shared.join, etc.)
b. Which SDK features would genuinely benefit THIS specific widget — tailored to what it does, not a dump of everything available.
2. **PRESENT A NUMBERED LIST** covering:
- SDK features currently active in this widget
- New SDK features that would concretely improve this widget (be specific: why this widget, what it enables)
3. **WAIT** — do not write any code yet. Reply with your analysis and numbered list, then stop and ask the user which numbered items they want.
4. **IMPLEMENT ONLY** the items the user confirms, plus any explicit change they requested. Do not add unrequested features.
**IMPORTANT — Shared state room names:**
If you add vibes.shared.join(), do NOT use a hardcoded string literal as the room name (e.g. vibes.shared.join("lobby")) unless the user explicitly wants ALL viewers to share one single global state. A hardcoded room name means every person who visits this widget reads and writes the same shared state — it is a global room. For per-user or per-session isolation, derive the room name from a variable (e.g. a user ID, session token, or random value). When in doubt, use vibes.save/vibes.load for per-user persistence instead.
--- VIBES SDK CONTEXT ---
## Vibes SDK Reference
You are building an HTML widget for It Just Vibes (itjustvibes.com). The Vibes SDK is auto-injected — do NOT add a script tag. Just use `window.vibes` (or just `vibes`).
### Setup
Wrap your startup code in `vibes.onReady`:
```js
vibes.onReady(async () => {
const saved = await vibes.load("myKey");
// your widget logic here
});
```
### State (Per-User Persistence)
Every user gets their own isolated state per widget. All methods return Promises.
| Method | Description |
|--------|-------------|
| `await vibes.save(key, value)` | Save JSON-serializable data |
| `await vibes.load(key)` | Load saved data (returns `null` if not found) |
| `await vibes.delete(key)` | Delete a saved key |
| `await vibes.listKeys()` | Get array of all saved key names |
**Key rules:**
- Keys are strings, max 64 characters, alphanumeric + dashes/underscores
- Values must be JSON-serializable (objects, arrays, strings, numbers, booleans)
- Max 100KB per value, 500KB per widget per user, 5MB per user total
- Max 100 keys per widget per user
### Fetch Proxy
Use direct `fetch()` for APIs with permissive CORS headers (`Access-Control-Allow-Origin: *`). Use `vibes.fetch` for APIs without CORS headers (the proxy handles cross-origin requests):
```js
const resp = await vibes.fetch("https://api.example.com/data", {
method: "GET", // GET, POST, PUT, DELETE
headers: {}, // optional headers
body: null, // optional body (string)
timeout: null // optional timeout in ms
});
const data = await resp.json(); // or resp.text()
console.log(resp.status, resp.ok);
```
### API Keys (never put a key in your code)
The widget owner stores keys in **Manage → Secrets**. Never hard-code an API key — anyone can read widget source.
**Server keys** (OpenAI, Stripe secret, etc.): pass the secret's name as `useSecret`. The proxy adds the key to the request; your code never sees it. The owner must also add the API's domain under Allowed Domains.
```js
const resp = await vibes.fetch("https://api.openai.com/v1/models", { useSecret: "OPENAI_KEY" });
```
**Browser keys** (keys built to be public, e.g. a Google Maps JavaScript API key): some libraries need the key in the page. Read it at runtime — it resolves `null` if the owner hasn't set it, so always handle that case:
```js
vibes.onReady(async () => {
const key = typeof vibes.browserKey === "function" ? await vibes.browserKey("GOOGLE_MAPS_BROWSER_KEY") : null;
if (!key) { showSetupMessage(); return; } // e.g. "Owner: add a Browser secret in Manage → Secrets"
loadGoogleMaps(key);
});
```
For website-restricted keys (Google Maps), the owner restricts the key to `https://run.itjustvibes.com/*` and turns on **Manage → Settings → Send origin as referrer**. Browser keys are visible to every viewer; never use `browserKey` for a server key. Forks don't get the owner's keys.
### Multiplayer (Shared State)
Real-time shared state across all users viewing the same widget.
```js
// Join a room (call once at startup)
await vibes.shared.join("lobby", { persistent: true });
// Set shared state (broadcasts to all users)
await vibes.shared.set("score", { player1: 10, player2: 7 });
// Read shared state (synchronous, returns last known value)
const score = vibes.shared.get("score");
// Listen for changes to a specific key
vibes.shared.onChange("score", (newValue) => {
console.log("Score updated:", newValue);
});
// Listen for any shared state change
vibes.shared.onAny((key, value) => {
console.log(key, "changed to", value);
});
// Get number of connected users
const count = await vibes.shared.getUserCount();
// Leave room
vibes.shared.leave();
// Clear all shared state for this room
await vibes.shared.clear();
```
**Shared state options:**
- `{ persistent: true }` — state survives page reloads (stored server-side)
- Default room name is "__default__" if omitted
### Agent-Accessible State (vibes.ai.*)
State written with the standard `vibes.save` / `vibes.load` methods is private to each user and is **NOT readable by AI agents**. To share state with an AI agent (via the MCP connector), use the `vibes.ai` sub-namespace:
| Method | Description |
|--------|-------------|
| `await vibes.ai.setState(key, value)` | Write agent-readable state. Key is stored internally as `ai/<key>`. |
| `await vibes.ai.getState(key)` | Read agent-readable state. Returns `null` if key absent. |
| `await vibes.ai.listKeys()` | List all agent-readable keys (without the `ai/` prefix). |
**Important rules:**
- Regular `vibes.save()` / `vibes.load()` is **NOT agent-accessible** — use `vibes.ai.*` for state you want agents to read.
- The widget **owner** must enable agent access in **Manage → Agent tab** before any agent can read or write `vibes.ai.*` state.
- Keys are auto-prefixed to `ai/` internally; you supply just the short key (e.g. `'context'`).
```js
vibes.onReady(async () => {
// Write state an AI agent can later read
await vibes.ai.setState('context', { currentLevel: 3, score: 1500 });
// Read it back (same auto-prefix applies)
const ctx = await vibes.ai.getState('context');
// List all agent-accessible keys for this widget
const keys = await vibes.ai.listKeys(); // e.g. ['context']
});
```
### Rules
1. **Do NOT use localStorage, sessionStorage, or window.storage** — they are blocked or undefined in the sandbox. Use `vibes.save`/`vibes.load` instead.
2. **Do NOT add a script tag to import the SDK** — it is auto-injected.
3. **Wrap startup code in `vibes.onReady()`** — the SDK may not be ready immediately.
4. **Await all SDK calls** — every method (except `vibes.shared.get`) returns a Promise.
5. **External JS libraries are allowed and encouraged** — load them via a `<script src>` tag from a reputable CDN (jsDelivr, unpkg, or cdnjs) with a pinned version, or inline the library source. The only exception: do NOT add a script tag for the Vibes SDK itself — it is auto-injected (see Rule 2).
6. **Keep total code under 100KB** — that is the default widget size limit (your account limit may be higher).
### Rate Limits
| Operation | Limit |
|-----------|-------|
| Writes (save + delete combined) | 30/min |
| Reads (load) | 60/min |
| List keys | 30/min |
| Fetch proxy | Rate limited per widget |
### Error Handling
All SDK methods can reject. Wrap in try/catch:
```js
try {
await vibes.save("key", value);
} catch (err) {
console.error("Save failed:", err.message);
}
```
Fetch proxy errors include `err.code`: `"RATE_LIMITED"`, `"BLOCKED"`, `"FETCH_ERROR"`, and `"SECRET_NOT_FOUND"` (unknown or paused `useSecret` name). `vibes.browserKey` rejects only with `"RATE_LIMITED"` or `"FETCH_ERROR"`.
### Data Export
Export rows of data as CSV or Excel directly from your widget.
```js
// Register dataset for the platform's "Export data" button
// AND optionally trigger an immediate download
vibes.exportData(rows, { filename: 'results.csv' })
// rows: Array of arrays (each inner array is one row; first row = headers)
// options.filename: sets the download filename and format (csv or xlsx)
// options.directDownload: false to only register without downloading (default: true)
```
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `filename` | string | `'data.csv'` | Download filename; extension determines format (`.csv` or `.xlsx`) |
| `directDownload` | boolean | `true` | Trigger immediate browser download in addition to registering the dataset |
**Notes:**
- First call registers the dataset so the widget chrome shows an "Export data" button
- Use `.csv` extension for CSV, `.xlsx` for Excel
- CSV injection safety is automatic (formula-starting cells prefixed with `'`)
- Data stays in the browser — no server round-trip
--- FORK & RESUBMIT INSTRUCTION ---
Return the complete, self-contained HTML file with all changes applied.
It should be ready to paste into itjustvibes.com/submit to create a new fork.
Include this comment at the top of the output:
/* Forked from: https://itjustvibes.com/Owner/iceland-2026-3d-trip-map */