{"id":187,"date":"2026-08-13T00:05:08","date_gmt":"2026-08-13T10:05:08","guid":{"rendered":"https:\/\/mana-corp.com\/?page_id=187"},"modified":"2026-08-13T00:05:36","modified_gmt":"2026-08-13T10:05:36","slug":"elementor-187","status":"publish","type":"page","link":"http:\/\/mana-corp.com\/?page_id=187","title":{"rendered":"Elementor #187"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-post\" data-elementor-id=\"187\" class=\"elementor elementor-187\">\n\t\t\t\t<div class=\"elementor-element elementor-element-30dc998 e-flex e-con-boxed e-con e-parent\" data-id=\"30dc998\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-fa1f0ad elementor-widget elementor-widget-html\" data-id=\"fa1f0ad\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover\">\n<title>HexMarket<\/title>\n<link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/maplibre-gl@^5\/dist\/maplibre-gl.css\">\n<style>\n*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent;touch-action:manipulation}\n:root{--a:#17E3A6;--a2:#FF9F45;--d:#FF5C72;--h:#17E3A6;--bg:#0A0D10;--p:#12171B;--p2:#1a2025;--b:rgba(255,255,255,.1);--t:#E9EDEC;--td:#8B979A;--tilt:0}\nhtml,body{height:100%;width:100%;overflow:hidden;background:var(--bg);color:var(--t);font-family:system-ui,sans-serif;-webkit-user-select:none;user-select:none}\nbody{position:fixed;inset:0}\n@media (prefers-reduced-motion:reduce){*{animation-duration:.001s!important;transition-duration:.001s!important}}\n\n#map{position:absolute;inset:0;z-index:1}\n#map:fullscreen,#map:-webkit-full-screen{width:100vw;height:100vh}\n\n.bar{position:fixed;top:0;left:0;right:0;z-index:100;padding:max(10px,env(safe-area-inset-top)) 10px 6px;display:flex;gap:8px;align-items:center;pointer-events:none}\n.logo{display:flex;align-items:center;gap:6px;background:rgba(18,23,27,.95);backdrop-filter:blur(8px);border:1px solid var(--b);padding:7px 11px;border-radius:99px;pointer-events:auto;font-weight:700;font-size:13px}\n.logo svg{width:15px;height:15px;color:var(--a)}\n.ib{width:36px;height:36px;border-radius:50%;background:rgba(18,23,27,.95);backdrop-filter:blur(8px);border:1px solid var(--b);display:flex;align-items:center;justify-content:center;pointer-events:auto;cursor:pointer;color:var(--t);transition:transform .12s,border-color .15s}\n.ib:active{transform:scale(.9)}\n.ib svg{width:17px;height:17px}\n.pb{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--a),#0FBF8A);border:none;pointer-events:auto;cursor:pointer;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:13px;color:#04140F;margin-left:auto;transition:transform .12s}\n.pb:active{transform:scale(.9)}\n\n.cs{position:fixed;top:max(52px,calc(env(safe-area-inset-top) + 44px));left:0;right:0;z-index:90;padding:6px 10px;display:flex;gap:12px;overflow-x:auto;scrollbar-width:none;pointer-events:auto}\n.cs::-webkit-scrollbar{display:none}\n.ci{flex-shrink:0;display:flex;flex-direction:column;align-items:center;gap:3px;cursor:pointer}\n.ci:active{opacity:.7}\n.ca{width:46px;height:46px;border-radius:50%;background:var(--p2);border:2px solid var(--b);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:15px;color:var(--a);position:relative}\n.ca::after{content:'';position:absolute;bottom:0;right:0;width:10px;height:10px;border-radius:50%;background:var(--a);border:2px solid var(--bg)}\n.cn{font-size:9px;color:var(--td);max-width:50px;text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n\n.fs{position:fixed;top:max(108px,calc(env(safe-area-inset-top) + 100px));left:0;right:0;z-index:85;padding:5px 10px;display:flex;gap:7px;overflow-x:auto;scrollbar-width:none;pointer-events:auto}\n.fs::-webkit-scrollbar{display:none}\n.fc{flex-shrink:0;padding:7px 12px;border-radius:99px;background:rgba(18,23,27,.95);backdrop-filter:blur(8px);border:1px solid var(--b);font-size:11px;font-weight:600;color:var(--td);cursor:pointer;white-space:nowrap;transition:background .15s,color .15s,border-color .15s}\n.fc:active{transform:scale(.95)}\n.fc.on{background:var(--a);color:#04140F;border-color:var(--a)}\n\n.hud{position:fixed;z-index:80;left:10px;bottom:calc(72px + env(safe-area-inset-bottom,0px));background:rgba(18,23,27,.95);backdrop-filter:blur(8px);border:1px solid var(--b);border-radius:10px;padding:7px 9px;display:flex;align-items:center;gap:7px}\n.hud .dot{width:7px;height:7px;border-radius:50%;background:var(--a);box-shadow:0 0 0 3px rgba(23,227,166,.2);transition:background .2s}\n.hud .dot.stale{background:var(--td);box-shadow:0 0 0 3px rgba(139,151,154,.15)}\n.hud .l1{font-size:8px;color:var(--td);text-transform:uppercase}\n.hud .l2{font-size:10px;font-weight:600;font-family:monospace}\n.hud .l3{font-size:8px;color:var(--a);font-family:monospace}\n\n.tb{position:fixed;right:10px;z-index:80;bottom:calc(72px + env(safe-area-inset-bottom,0px));display:flex;flex-direction:column;gap:9px}\n.gt{display:flex;flex-direction:column;background:rgba(18,23,27,.95);backdrop-filter:blur(8px);border:1px solid var(--b);border-radius:14px;padding:3px;gap:2px}\n.gt button{width:34px;height:28px;border:none;background:transparent;color:var(--td);border-radius:11px;font-size:13px;cursor:pointer;display:flex;align-items:center;justify-content:center}\n.gt button.on{background:var(--a);color:#04140F}\n.gt button:active{transform:scale(.9)}\n.tg{width:40px;height:40px;border-radius:50%;background:rgba(18,23,27,.95);backdrop-filter:blur(8px);border:2px solid var(--b);display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--td);transition:border-color .15s,color .15s}\n.tg:active{transform:scale(.9)}\n.tg.on{border-color:var(--a);color:var(--a);background:rgba(23,227,166,.12)}\n.tg svg{width:18px;height:18px}\n.fab{width:50px;height:50px;border-radius:50%;border:none;cursor:pointer;background:linear-gradient(145deg,var(--a),#0FBF8A);color:#04140F;display:flex;align-items:center;justify-content:center;box-shadow:0 6px 20px rgba(23,227,166,.4);transition:transform .12s}\n.fab:active{transform:scale(.9)}\n.fab svg{width:22px;height:22px}\n\n\/* Self marker \u2014 subtly grows with map tilt too *\/\n.sd{width:18px;height:18px;display:flex;align-items:center;justify-content:center}\n.sd i{width:12px;height:12px;border-radius:50%;background:var(--a);border:2px solid var(--bg);box-shadow:0 0 0 3px rgba(23,227,166,.3);position:relative;display:block;transition:background .3s,box-shadow .3s,transform .12s linear;transform:scale(calc(1 + var(--tilt) * 0.008))}\n.sd i::after{content:'';position:absolute;inset:-3px;border-radius:50%;background:var(--a);opacity:.4;animation:pulse 2s ease-out infinite}\n@keyframes pulse{0%{transform:scale(1);opacity:.4}100%{transform:scale(2.5);opacity:0}}\n\n\/* Listing markers \u2014 grow along a vertical stem whenever the map is tilted *\/\n.mc{position:relative;display:flex;flex-direction:column;align-items:center;cursor:pointer}\n.md{width:32px;height:32px;border-radius:50%;background:rgba(18,23,27,.95);border:2px solid var(--h);display:flex;align-items:center;justify-content:center;font-size:14px;transition:transform .12s linear,border-color .2s;transform:scale(calc(1 + var(--tilt) * 0.005));box-shadow:0 2px 8px rgba(0,0,0,.35)}\n.md:active{transform:scale(.85)}\n.md.sell{border-color:var(--a)}.md.buy{border-color:var(--a2)}.md.share{border-color:#7CA8FF}.md.business{border-color:#A855F7}\n.stem{width:2px;height:calc(4px + var(--tilt) * 1.2px);background:linear-gradient(to bottom,var(--h),transparent);margin-top:1px;border-radius:1px;transition:height .12s linear;opacity:.9}\n.gdot{width:4px;height:4px;border-radius:50%;background:var(--h);opacity:.55;margin-top:-1px}\n\n\/* Marker popup (custom, anchored to the marker so it rises with it) *\/\n.mp{position:absolute;bottom:calc(100% + 8px);left:50%;transform:translateX(-50%) scale(.9);background:rgba(18,23,27,.98);backdrop-filter:blur(8px);border:1px solid var(--b);border-radius:12px;padding:9px;min-width:180px;max-width:220px;opacity:0;pointer-events:none;transition:all .15s;z-index:200}\n.mp.show{opacity:1;pointer-events:auto;transform:translateX(-50%) scale(1)}\n.mp::after{content:'';position:absolute;top:100%;left:50%;transform:translateX(-50%);border:5px solid transparent;border-top-color:rgba(18,23,27,.98)}\n.ph{display:flex;align-items:center;gap:7px;margin-bottom:6px}\n.pa{width:28px;height:28px;border-radius:50%;background:linear-gradient(135deg,var(--a),#0FBF8A);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:11px;color:#04140F;flex-shrink:0}\n.pi{flex:1;min-width:0}\n.pn{font-weight:600;font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.px{font-size:8px;color:var(--td);font-family:monospace}\n.pt{font-size:7px;padding:2px 5px;border-radius:99px;font-weight:700;text-transform:uppercase;flex-shrink:0}\n.pt.sell{background:rgba(23,227,166,.15);color:var(--a)}.pt.buy{background:rgba(255,159,69,.15);color:var(--a2)}.pt.share{background:rgba(124,168,255,.15);color:#7CA8FF}.pt.business{background:rgba(168,85,247,.15);color:#A855F7}\n.pT{font-size:11px;font-weight:600;margin-bottom:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.pm{font-size:9px;color:var(--td);display:flex;justify-content:space-between;margin-bottom:5px}\n.pp{font-weight:700;color:var(--a)}\n.pe{font-size:9px;color:var(--a);margin-bottom:6px}\n.pA{display:flex;gap:5px}\n.pB{flex:1;padding:6px;border-radius:7px;border:none;font-weight:600;font-size:9px;cursor:pointer}\n.pB:active{transform:scale(.95)}\n.pB.pr2{background:var(--a);color:#04140F}.pB.sc{background:rgba(255,255,255,.1);color:var(--t)}\n\n.fn{position:fixed;left:0;right:0;bottom:0;z-index:100;background:rgba(18,23,27,.98);backdrop-filter:blur(8px);border-top:1px solid var(--b);padding:7px 0 max(7px,env(safe-area-inset-bottom));display:flex;justify-content:space-around}\n.ni{display:flex;flex-direction:column;align-items:center;gap:2px;padding:5px 12px;border-radius:10px;cursor:pointer;color:var(--td);position:relative;transition:color .15s}\n.ni:active{transform:scale(.9)}\n.ni.on{color:var(--a)}\n.ni.on::before{content:'';position:absolute;top:-7px;left:50%;transform:translateX(-50%);width:14px;height:2px;border-radius:1px;background:var(--a)}\n.ni svg{width:20px;height:20px}\n.ni span{font-size:8px;font-weight:600}\n.nb{position:absolute;top:-2px;right:2px;background:var(--d);color:#fff;font-size:7px;font-weight:700;min-width:12px;height:12px;border-radius:6px;display:flex;align-items:center;justify-content:center;padding:0 2px}\n\n.sh{position:fixed;left:0;right:0;bottom:0;z-index:95;background:rgba(18,23,27,.98);backdrop-filter:blur(8px);border-radius:18px 18px 0 0;border-top:1px solid var(--b);transform:translateY(100%);transition:transform .25s cubic-bezier(.4,0,.2,1);max-height:60vh;display:flex;flex-direction:column;padding-bottom:calc(65px + env(safe-area-inset-bottom,0px))}\n.sh.open{transform:translateY(0)}\n.hw{padding:8px 0 5px;display:flex;justify-content:center;cursor:grab}\n.hw i{width:32px;height:3px;border-radius:2px;background:rgba(255,255,255,.2);display:block}\n.sb{overflow-y:auto;padding:6px 14px 16px;flex:1;-webkit-overflow-scrolling:touch}\n.st{font-size:10px;text-transform:uppercase;letter-spacing:.5px;color:var(--td);margin:3px 0 8px}\n.cd{display:flex;gap:10px;align-items:center;padding:10px;border-radius:10px;background:rgba(255,255,255,.04);border:1px solid var(--b);margin-bottom:7px;cursor:pointer;transition:background .12s}\n.cd:active{background:rgba(255,255,255,.07)}\n.cd .th{width:44px;height:44px;border-radius:9px;flex-shrink:0;background:rgba(255,255,255,.05);display:flex;align-items:center;justify-content:center;font-size:18px}\n.cd .mt{flex:1;min-width:0}\n.cd .t1{font-weight:600;font-size:12px;display:flex;align-items:center;gap:5px}\n.cd .t2{font-size:10px;color:var(--td);margin-top:1px}\n.ch{font-size:8px;padding:2px 5px;border-radius:99px;font-weight:700;text-transform:uppercase}\n.ch.sell{background:rgba(23,227,166,.15);color:var(--a)}.ch.buy{background:rgba(255,159,69,.15);color:var(--a2)}.ch.share{background:rgba(124,168,255,.15);color:#7CA8FF}.ch.business{background:rgba(168,85,247,.15);color:#A855F7}\n.pr{font-weight:700;font-size:12px;color:var(--a)}\n.em{padding:30px 8px;text-align:center;color:var(--td);font-size:12px}\n.em .big{font-size:28px;margin-bottom:6px}\n.cr{display:flex;gap:10px;align-items:center;padding:10px 0;border-bottom:1px solid var(--b);cursor:pointer;position:relative}\n.cr:active{opacity:.7}\n.av{width:38px;height:38px;border-radius:50%;background:var(--p2);border:1px solid var(--b);display:flex;align-items:center;justify-content:center;font-weight:700;color:var(--a);flex-shrink:0}\n.cr .mt{flex:1;min-width:0}\n.cr .nm{font-weight:600;font-size:12px}\n.cr .ls{font-size:10px;color:var(--td);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.cr .tm{font-size:9px;color:var(--td)}\n.cr .dl{position:absolute;right:0;top:50%;transform:translateY(-50%);width:28px;height:28px;border-radius:50%;background:rgba(255,92,114,.15);border:none;color:var(--d);cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:12px}\n.cr .dl:active{transform:translateY(-50%) scale(.9)}\n\n.ov{position:fixed;inset:0;z-index:500;background:rgba(0,0,0,.6);backdrop-filter:blur(3px);display:flex;align-items:flex-end;opacity:0;pointer-events:none;transition:opacity .15s}\n.ov.show{opacity:1;pointer-events:auto}\n.mo{background:var(--p);width:100%;border-radius:18px 18px 0 0;max-height:85vh;overflow-y:auto;padding:14px 16px max(18px,env(safe-area-inset-bottom));transform:translateY(20px);transition:transform .18s cubic-bezier(.4,0,.2,1);-webkit-overflow-scrolling:touch}\n.ov.show .mo{transform:translateY(0)}\n.mh{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}\n.mh h2{font-size:15px;margin:0}\n.cx{width:30px;height:30px;border-radius:50%;background:rgba(255,255,255,.1);border:none;color:var(--t);cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:14px}\n.cx:active{transform:scale(.9)}\nlabel{font-size:9px;text-transform:uppercase;letter-spacing:.5px;color:var(--td);display:block;margin:12px 0 5px}\ninput[type=text],input[type=number],textarea{width:100%;background:rgba(255,255,255,.05);border:1px solid var(--b);color:var(--t);padding:11px 12px;border-radius:10px;font-size:13px;font-family:inherit}\ninput:focus,textarea:focus{outline:none;border-color:var(--a)}\ntextarea{resize:none;min-height:50px}\n.sg{display:flex;gap:5px;flex-wrap:wrap}\n.sg button{flex:1;min-width:60px;padding:9px 6px;border-radius:9px;border:1px solid var(--b);background:rgba(255,255,255,.05);color:var(--td);font-weight:600;font-size:11px;cursor:pointer}\n.sg button:active{transform:scale(.95)}\n.sg button.on{background:var(--a);color:#04140F;border-color:var(--a)}\n.ig{display:grid;grid-template-columns:repeat(3,1fr);gap:6px}\n.ic{padding:8px 5px;border-radius:9px;border:1px solid var(--b);background:rgba(255,255,255,.05);font-size:9px;font-weight:600;color:var(--td);cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:2px;text-align:center}\n.ic:active{transform:scale(.95)}\n.ic .ii{font-size:14px}\n.ic.on{background:var(--a);border-color:var(--a);color:#04140F}\n.cR{display:flex;gap:8px;flex-wrap:wrap}\n.cS{width:32px;height:32px;border-radius:50%;cursor:pointer;border:2px solid transparent}\n.cS:active{transform:scale(.9)}\n.cS.on{border-color:var(--t);transform:scale(1.1)}\n.tR{display:flex;align-items:center;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--b)}\n.tR label{margin:0;font-size:12px;color:var(--t);text-transform:none;letter-spacing:0}\n.tS{width:44px;height:22px;border-radius:11px;background:rgba(255,255,255,.1);border:1px solid var(--b);cursor:pointer;position:relative}\n.tS::after{content:'';position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--td);transition:all .15s}\n.tS.on{background:var(--a);border-color:var(--a)}\n.tS.on::after{left:24px;background:#04140F}\n.bp{width:100%;padding:13px;border-radius:11px;border:none;background:var(--a);color:#04140F;font-weight:700;font-size:13px;cursor:pointer;margin-top:14px}\n.bp:active{transform:scale(.98)}\n.bp:disabled{opacity:.5}\n.bs{width:100%;padding:13px;border-radius:11px;border:1px solid var(--b);background:rgba(255,255,255,.05);color:var(--t);font-weight:700;font-size:13px;cursor:pointer;margin-top:7px}\n.bs:active{transform:scale(.98)}\n.rR{display:flex;align-items:center;gap:8px}\ninput[type=range]{flex:1;accent-color:var(--a);height:3px}\n\n.fsc{position:fixed;inset:0;z-index:600;background:var(--bg);display:flex;flex-direction:column;transform:translateX(100%);transition:transform .2s cubic-bezier(.4,0,.2,1)}\n.fsc.show{transform:translateX(0)}\n.fH{display:flex;align-items:center;gap:9px;padding:max(10px,env(safe-area-inset-top)) 12px 10px;border-bottom:1px solid var(--b);background:var(--p)}\n.fH .nm{font-weight:700;font-size:13px}\n.fH .sub{font-size:9px;color:var(--td)}\n.fB{flex:1;overflow-y:auto;padding:12px;-webkit-overflow-scrolling:touch}\n.msg{max-width:75%;padding:9px 11px;border-radius:13px;margin-bottom:7px;font-size:12px;line-height:1.4;position:relative;animation:rise .18s ease-out}\n@keyframes rise{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}\n.msg.me{margin-left:auto;background:var(--a);color:#04140F;border-bottom-right-radius:3px}\n.msg.them{background:rgba(255,255,255,.07);border:1px solid var(--b);border-bottom-left-radius:3px}\n.msg.sys{margin:8px auto;text-align:center;max-width:90%;background:transparent;color:var(--td);font-size:10px}\n.msg .mt{font-size:8px;opacity:.6;margin-top:2px}\n.msg .ma{position:absolute;top:50%;right:-26px;transform:translateY(-50%);opacity:0;transition:opacity .1s}\n.msg:hover .ma,.msg:active .ma{opacity:1}\n.mb{width:22px;height:22px;border-radius:50%;background:var(--p2);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:10px}\n.mb:active{transform:scale(.9)}\n.fI{display:flex;gap:7px;padding:9px 11px max(9px,env(safe-area-inset-bottom));border-top:1px solid var(--b);background:var(--p)}\n.fI input{flex:1;background:rgba(255,255,255,.05);border:1px solid var(--b);color:var(--t);padding:9px 12px;border-radius:18px;font-size:13px}\n.fI input:focus{outline:none;border-color:var(--a)}\n.sB,.cB{width:40px;height:40px;border-radius:50%;border:none;background:var(--a);color:#04140F;display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0}\n.sB:active,.cB:active{transform:scale(.9)}\n.cB{background:rgba(255,255,255,.1);color:var(--t);border:1px solid var(--b)}\n\n.cSc{position:fixed;inset:0;z-index:700;background:#000;display:none;flex-direction:column}\n.cSc.show{display:flex}\n.cSc video{width:100%;height:100%;object-fit:cover;position:absolute;inset:0}\n#lv{position:absolute;width:80px;height:110px;right:12px;top:max(12px,env(safe-area-inset-top));border-radius:10px;border:2px solid rgba(255,255,255,.3);object-fit:cover;z-index:2}\n.cSt{position:absolute;top:max(12px,env(safe-area-inset-top));left:12px;z-index:2;color:#fff;font-size:11px;background:rgba(0,0,0,.5);padding:5px 10px;border-radius:99px}\n.cC{position:absolute;bottom:max(25px,env(safe-area-inset-bottom));left:0;right:0;display:flex;justify-content:center;gap:14px;z-index:2}\n.cC button{width:50px;height:50px;border-radius:50%;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,.2);color:#fff;backdrop-filter:blur(8px)}\n.cC button:active{transform:scale(.9)}\n.cC button.end{background:var(--d)}\n.cC button.acc{background:var(--a);color:#04140F}\n.icBanner{position:absolute;top:40%;left:0;right:0;text-align:center;color:#fff;z-index:2}\n.icBanner .av{width:64px;height:64px;font-size:22px;margin:0 auto 10px;border:2px solid rgba(255,255,255,.3)}\n.icBanner .nm{font-size:16px;font-weight:700;margin-bottom:3px}\n.icBanner .st2{font-size:11px;color:rgba(255,255,255,.6)}\n\n.tt{position:fixed;left:50%;top:max(55px,env(safe-area-inset-top));transform:translate(-50%,-15px);background:rgba(18,23,27,.95);backdrop-filter:blur(8px);border:1px solid var(--b);padding:8px 14px;border-radius:99px;font-size:11px;z-index:1000;opacity:0;transition:all .15s;pointer-events:none;max-width:85vw;text-align:center}\n.tt.show{opacity:1;transform:translate(-50%,0)}\n\n\/* Bottom carousel *\/\n.bc{position:fixed;left:0;right:0;bottom:calc(58px + env(safe-area-inset-bottom,0px));z-index:82;padding:0 8px 6px;display:flex;gap:7px;overflow-x:auto;scrollbar-width:none;pointer-events:none}\n.bc::-webkit-scrollbar{display:none}\n.bc.show{pointer-events:auto}\n.bcc{flex-shrink:0;width:112px;background:rgba(18,23,27,.96);backdrop-filter:blur(8px);border:1px solid var(--b);border-radius:11px;padding:7px 8px;cursor:pointer;transition:border-color .15s,transform .12s;scroll-snap-align:start}\n.bcc:active{transform:scale(.95)}\n.bcc.sel{border-color:var(--a)}\n.bcc .ic{font-size:16px;margin-bottom:3px}\n.bcc .tt2{font-size:10px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.bcc .pp2{font-size:9px;color:var(--a);font-weight:700;margin-top:2px}\n\n\/* Globe\/hollow atmosphere backdrop *\/\nbody.hollow #map{background:radial-gradient(ellipse at 50% 40%,#0d1a2b 0%,#050709 70%);filter:brightness(.5) contrast(1.35) saturate(.6)}\n\n\/* Boost pill in listing form *\/\n.bx{display:flex;align-items:center;justify-content:space-between;padding:10px;border-radius:10px;background:rgba(255,159,69,.08);border:1px solid rgba(255,159,69,.3);margin-top:12px}\n.bx .bl{font-size:11px;font-weight:600;color:var(--a2)}\n.bx .bd{font-size:9px;color:var(--td);margin-top:1px}\n\n\/* Pricing cards *\/\n.pc{border:1px solid var(--b);border-radius:12px;padding:11px;margin-bottom:8px;cursor:pointer;position:relative}\n.pc.sel{border-color:var(--a);background:rgba(23,227,166,.06)}\n.pc .pn2{font-weight:700;font-size:13px;display:flex;justify-content:space-between;align-items:center}\n.pc .pr3{color:var(--a);font-size:12px;font-weight:700}\n.pc .pf{font-size:10px;color:var(--td);margin-top:5px;line-height:1.5}\n.pc .cur{position:absolute;top:8px;right:8px;font-size:8px;background:var(--a);color:#04140F;padding:2px 6px;border-radius:99px;font-weight:700}\n\n\/* Analytics dashboard *\/\n.ag{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:12px}\n.ak{background:rgba(255,255,255,.04);border:1px solid var(--b);border-radius:10px;padding:10px}\n.ak .av2{font-size:18px;font-weight:700;font-family:monospace}\n.ak .al{font-size:9px;color:var(--td);text-transform:uppercase;margin-top:2px}\n.hm{height:120px;border-radius:10px;background:linear-gradient(135deg,rgba(23,227,166,.15),rgba(255,159,69,.1));border:1px solid var(--b);display:flex;align-items:center;justify-content:center;font-size:10px;color:var(--td);margin-bottom:12px;position:relative;overflow:hidden}\n.lockov{position:absolute;inset:0;background:rgba(10,13,16,.9);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;text-align:center;padding:20px;z-index:5}\n.lockov .lk{font-size:26px}\n.lockov p{font-size:11px;color:var(--td);max-width:220px}\n\n.maplibregl-ctrl-attrib,.maplibregl-ctrl-logo,.maplibregl-popup{display:none!important}\n<\/style>\n<\/head>\n<body>\n\n<div id=\"map\"><\/div>\n\n<div class=\"bar\">\n<div class=\"logo\"><svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 1 22 7v10l-10 6-10-6V7z\"\/><\/svg>Hex<\/div>\n<div class=\"ib\" id=\"themeBtn\" aria-label=\"Toggle theme\"><svg id=\"tI\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z\"\/><\/svg><\/div>\n<div class=\"ib\" id=\"mockBtn\" aria-label=\"Toggle demo data\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"\/><path d=\"M12 6v6l4 2\"\/><\/svg><\/div>\n<button class=\"pb\" id=\"profBtn\" aria-label=\"Profile\">?<\/button>\n<\/div>\n\n<div class=\"cs\" id=\"cScroll\"><\/div>\n\n<div class=\"fs\" id=\"fScroll\">\n<div class=\"fc on\" data-f=\"all\">All<\/div>\n<div class=\"fc\" data-f=\"sell\">\ud83d\udcb0 Sell<\/div>\n<div class=\"fc\" data-f=\"buy\">\ud83d\uded2 Buy<\/div>\n<div class=\"fc\" data-f=\"share\">\ud83c\udf81 Share<\/div>\n<div class=\"fc\" data-f=\"business\">\ud83c\udfea Biz<\/div>\n<div class=\"fc\" data-f=\"dist\">\ud83d\udccd <span id=\"dL\">&lt;1km<\/span><\/div>\n<div class=\"fc\" data-f=\"price\">\ud83d\udcb5 <span id=\"pL\">Any<\/span><\/div>\n<\/div>\n\n<div class=\"hud\"><div class=\"dot\" id=\"hDot\"><\/div><div><div class=\"l1\">hex \u00b7 r<span id=\"hR\">9<\/span><\/div><div class=\"l2\" id=\"hH\">...<\/div><\/div><\/div>\n\n<div class=\"tb\">\n<div class=\"gt\" id=\"gToggle\">\n<button class=\"on\" data-g=\"globe\" aria-label=\"Globe view\">\ud83c\udf10<\/button>\n<button data-g=\"flat\" aria-label=\"Flat view\">\u25ad<\/button>\n<button data-g=\"hollow\" aria-label=\"Hollow data-sphere view\">\u25cc<\/button>\n<\/div>\n<div class=\"ib\" id=\"locBtn\" aria-label=\"Center on me\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"3\"\/><path d=\"M12 2v3M12 19v3M2 12h3M19 12h3\"\/><\/svg><\/div>\n<div class=\"ib\" id=\"layBtn\" aria-label=\"Hex grid settings\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polygon points=\"12 2 22 8.5 22 15.5 12 22 2 15.5 2 8.5 12 2\"\/><\/svg><\/div>\n<div class=\"ib\" id=\"fsBtn\" aria-label=\"Fullscreen\"><svg id=\"fI\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M8 3H5a2 2 0 0 0-2 2v3M16 3h3a2 2 0 0 1 2 2v3M21 16v3a2 2 0 0 1-2 2h-3M8 21H5a2 2 0 0 1-2-2v-3\"\/><\/svg><\/div>\n<div class=\"tg\" id=\"rtBtn\" aria-label=\"Toggle route\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"6\" cy=\"19\" r=\"3\"\/><path d=\"M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15\"\/><circle cx=\"18\" cy=\"5\" r=\"3\"\/><\/svg><\/div>\n<div class=\"tg\" id=\"tkBtn\" aria-label=\"Toggle live tracking\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 22s-8-4.5-8-11.8A8 8 0 0 1 12 2a8 8 0 0 1 8 8.2c0 7.3-8 11.8-8 11.8z\"\/><circle cx=\"12\" cy=\"10\" r=\"3\"\/><\/svg><\/div>\n<button class=\"fab\" id=\"sellBtn\" aria-label=\"New listing\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><path d=\"M12 5v14M5 12h14\"\/><\/svg><\/button>\n<\/div>\n\n<div class=\"bc\" id=\"carousel\"><\/div>\n\n<div class=\"fn\">\n<div class=\"ni on\" data-t=\"nearby\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polygon points=\"12 2 22 8.5 22 15.5 12 22 2 15.5 2 8.5 12 2\"\/><\/svg><span>Nearby<\/span><\/div>\n<div class=\"ni\" data-t=\"messages\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M21 11.5a8.38 8.38 0 0 1-8.5 8.5H3l3-3.5A8.4 8.4 0 0 1 21 11.5Z\"\/><\/svg><span>Msgs<\/span><span class=\"nb\" id=\"mB\" style=\"display:none\">0<\/span><\/div>\n<div class=\"ni\" data-t=\"hex\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5\"\/><\/svg><span>Hex<\/span><\/div>\n<div class=\"ni\" data-t=\"profile\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2\"\/><circle cx=\"12\" cy=\"7\" r=\"4\"\/><\/svg><span>Me<\/span><\/div>\n<\/div>\n\n<div class=\"sh\" id=\"sheet\"><div class=\"hw\" id=\"sH\"><i><\/i><\/div><div class=\"sb\" id=\"sB\"><div class=\"st\">Nearby<\/div><div class=\"em\"><div class=\"big\">\u2b21<\/div>Locating...<\/div><\/div><\/div>\n\n<!-- Profile Modal -->\n<div class=\"ov\" id=\"profOv\"><div class=\"mo\"><div class=\"mh\"><h2>Profile<\/h2><button class=\"cx\" data-c=\"profOv\">\u2715<\/button><\/div>\n<label>Name<\/label><input type=\"text\" id=\"pN\" placeholder=\"Your name\" maxlength=\"24\">\n<label>Type<\/label><div class=\"sg\" id=\"atS\"><button class=\"on\" data-v=\"individual\">\ud83d\udc64 Indiv<\/button><button data-v=\"pro\">\u2b50 Pro<\/button><\/div>\n<label>Intents \u2014 what do you want to see?<\/label><div class=\"ig\" id=\"iG\">\n<div class=\"ic on\" data-b=\"1\"><span class=\"ii\">\ud83d\uded2<\/span>Buy<\/div><div class=\"ic on\" data-b=\"2\"><span class=\"ii\">\ud83d\udcb0<\/span>Sell<\/div><div class=\"ic\" data-b=\"4\"><span class=\"ii\">\ud83c\udf81<\/span>Share<\/div>\n<div class=\"ic\" data-b=\"8\"><span class=\"ii\">\ud83c\udfea<\/span>Biz<\/div><div class=\"ic\" data-b=\"16\"><span class=\"ii\">\ud83d\udeb4<\/span>Deliver<\/div><div class=\"ic\" data-b=\"32\"><span class=\"ii\">\ud83d\udd27<\/span>Service<\/div>\n<\/div>\n<label>Theme<\/label><div class=\"cR\" id=\"tC\"><div class=\"cS on\" data-c=\"#17E3A6\" style=\"background:#17E3A6\"><\/div><div class=\"cS\" data-c=\"#FF9F45\" style=\"background:#FF9F45\"><\/div><div class=\"cS\" data-c=\"#7CA8FF\" style=\"background:#7CA8FF\"><\/div><div class=\"cS\" data-c=\"#A855F7\" style=\"background:#A855F7\"><\/div><div class=\"cS\" data-c=\"#FF5C72\" style=\"background:#FF5C72\"><\/div><\/div>\n<label>Hex Color<\/label><div class=\"cR\" id=\"hC\"><div class=\"cS on\" data-c=\"#17E3A6\" style=\"background:#17E3A6\"><\/div><div class=\"cS\" data-c=\"#3B82F6\" style=\"background:#3B82F6\"><\/div><div class=\"cS\" data-c=\"#8B5CF6\" style=\"background:#8B5CF6\"><\/div><div class=\"cS\" data-c=\"#EC4899\" style=\"background:#EC4899\"><\/div><\/div>\n<div class=\"tR\"><label>Demo listings<\/label><div class=\"tS on\" id=\"mT\"><\/div><\/div>\n<div class=\"tR\"><label>High-accuracy GPS<\/label><div class=\"tS on\" id=\"gT\"><\/div><\/div>\n<label>Subscription <span style=\"text-transform:none;font-weight:400\">(local only \u2014 no payment)<\/span><\/label>\n<div id=\"planCards\"><\/div>\n<button class=\"bs\" id=\"openBoostBtn\">\ud83d\ude80 Ad Boost & Analytics<\/button>\n<button class=\"bp\" id=\"svP\">Save<\/button><\/div><\/div>\n\n<!-- Hex Modal -->\n<div class=\"ov\" id=\"hexOv\"><div class=\"mo\"><div class=\"mh\"><h2>Hex Grid<\/h2><button class=\"cx\" data-c=\"hexOv\">\u2715<\/button><\/div>\n<label>Resolution: <span id=\"rV\">9<\/span> (~<span id=\"rS\">0.1<\/span> km\u00b2)<\/label>\n<div class=\"rR\"><span style=\"font-size:10px;color:var(--td)\">5<\/span><input type=\"range\" id=\"rR\" min=\"5\" max=\"12\" value=\"9\"><span style=\"font-size:10px;color:var(--td)\">12<\/span><\/div>\n<label>Search ring: <span id=\"rgV\">2<\/span> (also sets what listings load)<\/label>\n<div class=\"rR\"><span style=\"font-size:10px;color:var(--td)\">1<\/span><input type=\"range\" id=\"rgR\" min=\"1\" max=\"6\" value=\"2\"><span style=\"font-size:10px;color:var(--td)\">6<\/span><\/div>\n<label>Layers<\/label><div class=\"sg\" id=\"lS\"><button class=\"on\" data-l=\"cur\">Current<\/button><button class=\"on\" data-l=\"ring\">Ring<\/button><button data-l=\"inner\">Inner<\/button><\/div>\n<button class=\"bp\" id=\"svH\">Apply<\/button><\/div><\/div>\n\n<!-- Distance Modal -->\n<div class=\"ov\" id=\"distOv\"><div class=\"mo\"><div class=\"mh\"><h2>Distance<\/h2><button class=\"cx\" data-c=\"distOv\">\u2715<\/button><\/div>\n<label>Max: <span id=\"dV\">1.0<\/span> km<\/label>\n<div class=\"rR\"><span style=\"font-size:10px;color:var(--td)\">0.1<\/span><input type=\"range\" id=\"dR\" min=\"0.1\" max=\"5\" step=\"0.1\" value=\"1\"><span style=\"font-size:10px;color:var(--td)\">5<\/span><\/div>\n<button class=\"bp\" id=\"svD\">Apply<\/button><\/div><\/div>\n\n<!-- Price Modal -->\n<div class=\"ov\" id=\"prOv\"><div class=\"mo\"><div class=\"mh\"><h2>Price<\/h2><button class=\"cx\" data-c=\"prOv\">\u2715<\/button><\/div>\n<div class=\"sg\" id=\"pS\"><button class=\"on\" data-p=\"any\">Any<\/button><button data-p=\"free\">Free<\/button><button data-p=\"u25\">&lt;$25<\/button><button data-p=\"u50\">&lt;$50<\/button><button data-p=\"o50\">$50+<\/button><\/div>\n<button class=\"bp\" id=\"svPr\">Apply<\/button><\/div><\/div>\n\n<!-- Listing Modal -->\n<div class=\"ov\" id=\"listOv\"><div class=\"mo\"><div class=\"mh\"><h2>New Listing<\/h2><button class=\"cx\" data-c=\"listOv\">\u2715<\/button><\/div>\n<div class=\"sg\" id=\"tyS\"><button class=\"on\" data-v=\"sell\">Sell<\/button><button data-v=\"buy\">Buy<\/button><button data-v=\"share\">Share<\/button><button data-v=\"business\">Biz<\/button><\/div>\n<label>Title<\/label><input type=\"text\" id=\"lT\" placeholder=\"What?\" maxlength=\"60\">\n<label>Details<\/label><textarea id=\"lD\" placeholder=\"Desc...\" maxlength=\"280\"><\/textarea>\n<label>Price (0=free)<\/label><input type=\"number\" id=\"lP\" placeholder=\"0\" inputmode=\"decimal\" min=\"0\" step=\"0.01\">\n<label>Category<\/label><div class=\"sg\" id=\"caS\"><button class=\"on\" data-v=\"\ud83d\udecd\ufe0f\">Goods<\/button><button data-v=\"\ud83c\udf5c\">Food<\/button><button data-v=\"\ud83e\uddf0\">Service<\/button><button data-v=\"\ud83d\udeb2\">Delivery<\/button><\/div>\n<div class=\"bx\" id=\"boostPill\"><div><div class=\"bl\">\ud83d\ude80 Boost this listing<\/div><div class=\"bd\" id=\"boostSub\">Pro plan required \u2014 reach more of the hex grid<\/div><\/div><div class=\"tS\" id=\"boostT\"><\/div><\/div>\n<button class=\"bp\" id=\"pubBtn\">Publish<\/button><\/div><\/div>\n\n<!-- Boost\/Analytics Screen -->\n<div class=\"fsc\" id=\"boostSc\">\n<div class=\"fH\"><button class=\"ib\" id=\"baBk\" style=\"background:rgba(255,255,255,.1)\" aria-label=\"Back\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M15 18l-6-6 6-6\"\/><\/svg><\/button><div style=\"flex:1\"><div class=\"nm\">Ad Boost & Analytics<\/div><div class=\"sub\" id=\"baPlanLbl\">Free plan<\/div><\/div><\/div>\n<div class=\"fB\">\n<div class=\"sg\" id=\"baTabs\" style=\"margin-bottom:14px\"><button class=\"on\" data-bt=\"boost\">Boost<\/button><button data-bt=\"analytics\">Analytics<\/button><\/div>\n\n<div id=\"baBoost\">\n<label>Billing model<\/label>\n<div class=\"sg\" id=\"bmS\"><button class=\"on\" data-v=\"CPC\">CPC<\/button><button data-v=\"CPM\">CPM<\/button><button data-v=\"CPR\">CPR<\/button><\/div>\n<label>Daily budget cap ($)<\/label><input type=\"number\" id=\"bBudget\" placeholder=\"10\" min=\"1\" value=\"10\">\n<label>H3 target ring (from your hex)<\/label>\n<div class=\"rR\"><span style=\"font-size:10px;color:var(--td)\">1<\/span><input type=\"range\" id=\"bRing\" min=\"1\" max=\"6\" value=\"2\"><span style=\"font-size:10px;color:var(--td)\">6<\/span><\/div>\n<div class=\"tR\"><label>Create native poll with this boost<\/label><div class=\"tS\" id=\"bPoll\"><\/div><\/div>\n<input type=\"text\" id=\"bPollQ\" placeholder=\"Poll question (optional)\" style=\"margin-top:8px;display:none\">\n<button class=\"bp\" id=\"startBoost\">Launch Boost<\/button>\n<div class=\"st\" style=\"margin-top:16px\">Active boosts<\/div>\n<div id=\"boostList\"><div class=\"em\"><div class=\"big\">\ud83d\ude80<\/div>No active boosts<\/div><\/div>\n<\/div>\n\n<div id=\"baAnalytics\" style=\"display:none;position:relative\">\n<div class=\"ag\">\n<div class=\"ak\"><div class=\"av2\" id=\"mImp\">0<\/div><div class=\"al\">Impressions<\/div><\/div>\n<div class=\"ak\"><div class=\"av2\" id=\"mClk\">0<\/div><div class=\"al\">Clicks<\/div><\/div>\n<div class=\"ak\"><div class=\"av2\" id=\"mReach\">0<\/div><div class=\"al\">Reach (hexes)<\/div><\/div>\n<div class=\"ak\"><div class=\"av2\" id=\"mVotes\">0<\/div><div class=\"al\">Poll votes<\/div><\/div>\n<\/div>\n<div class=\"hm\" id=\"heatBox\">Hex-grid heatmap overlay (globe)<\/div>\n<label>Spend so far<\/label><div class=\"av2\" id=\"mSpend\" style=\"margin-bottom:10px\">$0.00<\/div>\n<label>ROI calculator \u2014 est. value per conversion ($)<\/label><input type=\"number\" id=\"roiVal\" placeholder=\"25\" value=\"25\">\n<div class=\"ak\" style=\"margin-top:8px\"><div class=\"av2\" id=\"mRoi\">0%<\/div><div class=\"al\">Estimated ROI<\/div><\/div>\n<div class=\"lockov\" id=\"anaLock\" style=\"display:none\"><div class=\"lk\">\ud83d\udd12<\/div><p>Analytics dashboard is a Pro feature. Upgrade in Profile \u2192 Subscription.<\/p><\/div>\n<\/div>\n<\/div>\n<\/div>\n\n<!-- Detail Modal -->\n<div class=\"ov\" id=\"detOv\"><div class=\"mo\" id=\"detM\"><\/div><\/div>\n\n<!-- Chat Screen -->\n<div class=\"fsc\" id=\"chatSc\">\n<div class=\"fH\"><button class=\"ib\" id=\"chBk\" style=\"background:rgba(255,255,255,.1)\" aria-label=\"Back\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M15 18l-6-6 6-6\"\/><\/svg><\/button><div class=\"av\" id=\"chAv\">?<\/div><div style=\"flex:1\"><div class=\"nm\" id=\"chNm\">\u2014<\/div><div class=\"sub\" id=\"chId\" style=\"font-family:monospace\">\u2014<\/div><\/div><button class=\"cB\" id=\"vcBtn\" aria-label=\"Video call\"><svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M23 7l-7 5 7 5V7Z\"\/><rect x=\"1\" y=\"5\" width=\"15\" height=\"14\" rx=\"2\"\/><\/svg><\/button><\/div>\n<div class=\"fB\" id=\"chBd\"><\/div>\n<div class=\"fI\"><input type=\"text\" id=\"chIn\" placeholder=\"Message...\" maxlength=\"500\"><button class=\"sB\" id=\"chSnd\" aria-label=\"Send\"><svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M22 2 11 13M22 2l-7 20-4-9-9-4 20-7Z\"\/><\/svg><\/button><\/div>\n<\/div>\n\n<!-- Call Screen -->\n<div class=\"cSc\" id=\"callSc\"><video id=\"rv\" autoplay playsinline><\/video><video id=\"lv\" autoplay playsinline muted><\/video><div class=\"cSt\" id=\"cSt\">Calling...<\/div>\n<div class=\"icBanner\" id=\"icBanner\" style=\"display:none\"><div class=\"av\" id=\"icAv\">?<\/div><div class=\"nm\" id=\"icNm\">\u2014<\/div><div class=\"st2\">Incoming video call<\/div><\/div>\n<div class=\"cC\" id=\"cC\"><button id=\"mtBtn\" aria-label=\"Mute\"><svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3Z\"\/><path d=\"M19 10v2a7 7 0 0 1-14 0v-2M12 19v4\"\/><\/svg><\/button><button class=\"end\" id=\"ecBtn\" aria-label=\"End call\"><svg width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 9c-2.5 0-4.8.6-6.8 1.7a1.7 1.7 0 0 0-.5 2.5l1.6 2.1c.4.5 1.1.7 1.7.4.8-.4 1.7-.7 2.6-.9.4-.1.7-.4.7-.8v-1.6c0-.4.3-.8.7-.9 1.3-.3 2.7-.3 4 0 .4.1.7.5.7.9V14c0 .4.3.7.7.8.9.2 1.8.5 2.6.9.6.3 1.3.1 1.7-.4l1.6-2.1a1.7 1.7 0 0 0-.5-2.5C16.8 9.6 14.5 9 12 9Z\"\/><\/svg><\/button><\/div>\n<\/div>\n\n<div class=\"tt\" id=\"toast\"><\/div>\n\n<script src=\"https:\/\/unpkg.com\/maplibre-gl@^5\/dist\/maplibre-gl.js\"><\/script>\n<script src=\"https:\/\/unpkg.com\/h3-js@4.1.0\/dist\/h3-js.umd.js\"><\/script>\n<script src=\"https:\/\/unpkg.com\/peerjs@1.5.2\/dist\/peerjs.min.js\"><\/script>\n<script>\n'use strict';\n\/\/ ================= utils =================\nconst $=s=>document.querySelector(s),$$=s=>[...document.querySelectorAll(s)];\nconst uid=()=>'x'+Math.random().toString(36).slice(2,9);\nconst toast=(m,t=2000)=>{const e=$('#toast');e.textContent=m;e.classList.add('show');clearTimeout(toast._);toast._=setTimeout(()=>e.classList.remove('show'),t)};\nconst hap=()=>navigator.vibrate?.(8);\nconst esc=s=>(s||'').replace(\/[&<>\"]\/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[m]));\nconst debounce=(fn,ms)=>{let h;return(...a)=>{clearTimeout(h);h=setTimeout(()=>fn(...a),ms)}};\nconst emptyFC=()=>({type:'FeatureCollection',features:[]});\nfunction haversine(a,b,c,d){const R=6371,dLa=(c-a)*Math.PI\/180,dLn=(d-b)*Math.PI\/180,x=Math.sin(dLa\/2)**2+Math.cos(a*Math.PI\/180)*Math.cos(c*Math.PI\/180)*Math.sin(dLn\/2)**2;return R*2*Math.atan2(Math.sqrt(x),Math.sqrt(1-x))}\n\n\/\/ ================= storage: raw IndexedDB (no external DB library) =================\nlet _idb=null;\nfunction idbReq(req){return new Promise((res,rej)=>{req.onsuccess=()=>res(req.result);req.onerror=()=>rej(req.error)})}\nfunction openDB(){\n  return new Promise((resolve,reject)=>{\n    const req=indexedDB.open('HX',2);\n    req.onupgradeneeded=e=>{\n      const d=e.target.result;\n      if(!d.objectStoreNames.contains('l')){const s=d.createObjectStore('l',{keyPath:'id'});s.createIndex('h','h');s.createIndex('mock','mock')}\n      if(!d.objectStoreNames.contains('c')){d.createObjectStore('c',{keyPath:'p'})}\n      if(!d.objectStoreNames.contains('m')){const s=d.createObjectStore('m',{keyPath:'i',autoIncrement:true});s.createIndex('c','c')}\n      if(!d.objectStoreNames.contains('kv')){d.createObjectStore('kv',{keyPath:'k'})}\n      if(!d.objectStoreNames.contains('bo')){d.createObjectStore('bo',{keyPath:'id'})}\n    };\n    req.onsuccess=e=>{_idb=e.target.result;resolve(_idb)};\n    req.onerror=e=>reject(e.target.error);\n  });\n}\nfunction store(name,mode='readonly'){return _idb.transaction(name,mode).objectStore(name)}\n\nconst db={\n  l:{\n    get:id=>idbReq(store('l').get(id)),\n    put:obj=>idbReq(store('l','readwrite').put(obj)),\n    countMock:()=>idbReq(store('l').index('mock').count(IDBKeyRange.only(1))),\n    \/\/ union query across a small ring of hex-index keys \u2014 avoids a full table scan as data grows\n    byHexRing:ring=>Promise.all(ring.map(h=>idbReq(store('l').index('h').getAll(h)))).then(arrs=>arrs.flat())\n  },\n  c:{\n    get:p=>idbReq(store('c').get(p)),\n    put:obj=>idbReq(store('c','readwrite').put(obj)),\n    delete:p=>idbReq(store('c','readwrite').delete(p)),\n    all:()=>idbReq(store('c').getAll())\n  },\n  m:{\n    add:obj=>idbReq(store('m','readwrite').add(obj)),\n    byConversation:c=>idbReq(store('m').index('c').getAll(c)).then(arr=>arr.sort((a,b)=>a.ts-b.ts)),\n    deleteByConvo:c=>new Promise(res=>{\n      const req=store('m','readwrite').index('c').openCursor(IDBKeyRange.only(c));\n      req.onsuccess=e=>{const cur=e.target.result;if(cur){cur.delete();cur.continue()}else res()};\n      req.onerror=()=>res();\n    }),\n    deleteOne:(c,ts)=>new Promise(res=>{\n      const req=store('m','readwrite').index('c').openCursor(IDBKeyRange.only(c));\n      req.onsuccess=e=>{const cur=e.target.result;if(cur){if(cur.value.ts===ts)cur.delete();cur.continue()}else res()};\n      req.onerror=()=>res();\n    })\n  },\n  kv:{\n    get:k=>idbReq(store('kv').get(k)),\n    put:obj=>idbReq(store('kv','readwrite').put(obj))\n  },\n  bo:{\n    all:()=>idbReq(store('bo').getAll()),\n    put:obj=>idbReq(store('bo','readwrite').put(obj))\n  }\n};\n\n\/\/ ---- premium tiers (client-side gates only \u2014 no payment flow) ----\nconst PLANS={\n  free:{label:'Free',price:0,maxRes:6,boost:false,analytics:false,polls:false,video:false,desc:'Basic globe\/map view \u00b7 1 active intent \u00b7 hex res 6 only'},\n  pro:{label:'Pro',price:199,maxRes:12,boost:true,analytics:true,polls:true,video:true,desc:'Unlimited intents \u00b7 all hex res (2\u201312) \u00b7 full ad boost \u00b7 analytics + heatmap \u00b7 polls \u00b7 1:1 video'},\n  enterprise:{label:'Enterprise',price:4999,maxRes:12,boost:true,analytics:true,polls:true,video:true,desc:'Custom H3 zones \u00b7 1:many broadcast \u00b7 priority routing \u00b7 SLA privacy \u00b7 API access'}\n};\nconst plan=()=>PLANS[M.plan||'free'];\n\nconst DEFAULT_M={id:'hx_'+uid(),name:'Neighbor',type:'individual',flags:3,res:9,tc:'#17E3A6',hc:'#17E3A6',ho:{rr:2,ly:['cur','ring']},fi:{t:'all',d:1,p:'any'},mock:1,gps:1,rt:0,tk:0,plan:'free',gview:'globe'};\nlet M=null, th='dark';\nasync function loadState(){\n  const [pr,tr]=await Promise.all([db.kv.get('profile'),db.kv.get('theme')]);\n  M=pr?{...DEFAULT_M,...pr.v,ho:{...DEFAULT_M.ho,...(pr.v.ho||{})},fi:{...DEFAULT_M.fi,...(pr.v.fi||{})}}:{...DEFAULT_M};\n  th=tr?tr.v:'dark';\n}\nconst sv=()=>db.kv.put({k:'profile',v:M});\nconst svTheme=()=>db.kv.put({k:'theme',v:th});\n\n\/\/ ================= Kalman filter for GPS smoothing =================\nlet K={i:0};\nconst kalman=(la,ln,ac,t)=>{ac=Math.max(ac||10,3);if(!K.i){K={i:1,lat:la,lng:ln,v:ac*ac,t};return{lat:la,lng:ln}}const dt=Math.max((t-K.t)\/1e3,.001);K.t=t;K.v+=dt*.25;const k=K.v\/(K.v+ac*ac);K.lat+=k*(la-K.lat);K.lng+=k*(ln-K.lng);K.v*=1-k;return{lat:K.lat,lng:K.lng}};\n\nconst STY={light:'https:\/\/basemaps.cartocdn.com\/gl\/positron-gl-style\/style.json',dark:'https:\/\/basemaps.cartocdn.com\/gl\/dark-matter-gl-style\/style.json'};\nconst RZ={5:8,6:10,7:12,8:13,9:15,10:16,11:17,12:18};\nconst RS={5:'250',6:'36',7:'5.1',8:'0.74',9:'0.105',10:'0.015',11:'0.002',12:'0.0003'};\nconst DEFLOC=[{lat:40.7128,lng:-74.006},{lat:34.0522,lng:-118.2437},{lat:51.5074,lng:-0.1278}];\nconst MARKER_CAP=150; \/\/ keeps DOM marker count fast even in a dense hex\n\n\/\/ ================= routing (OSRM, with haversine fallback) =================\nasync function getRoute(from,to){\n  try{\n    const r=await fetch(`https:\/\/router.project-osrm.org\/route\/v1\/foot\/${from[1]},${from[0]};${to[1]},${to[0]}?overview=full&geometries=geojson`,{signal:AbortSignal.timeout(6000)});\n    if(!r.ok)return null;\n    const d=await r.json();\n    return d.routes?.[0]?{coords:d.routes[0].geometry.coordinates,dist:(d.routes[0].distance\/1000).toFixed(2),dur:Math.round(d.routes[0].duration\/60)}:null;\n  }catch(e){return null}\n}\nasync function drawRouteTo(la,ln,onDone){\n  const route=await getRoute([lastLL.lat,lastLL.lng],[la,ln]);\n  let coords,label;\n  if(route){coords=route.coords;label=`\ud83d\udeb6 ${route.dist}km \u00b7 ${route.dur}min walk`}\n  else{const dist=haversine(lastLL.lat,lastLL.lng,la,ln);coords=[[lastLL.lng,lastLL.lat],[ln,la]];label=`\ud83d\udeb6 ~${dist.toFixed(2)}km \u00b7 ~${Math.max(1,~~(dist\/5*60))}min`}\n  M.rt=1;$('#rtBtn').classList.add('on');\n  map.getSource('rt').setData({type:'Feature',geometry:{type:'LineString',coordinates:coords}});\n  const bounds=coords.reduce((b,c)=>{b[0][0]=Math.min(b[0][0],c[0]);b[0][1]=Math.min(b[0][1],c[1]);b[1][0]=Math.max(b[1][0],c[0]);b[1][1]=Math.max(b[1][1],c[1]);return b},[[Infinity,Infinity],[-Infinity,-Infinity]]);\n  map.fitBounds(bounds,{padding:60,duration:500,essential:true});\n  onDone?.(label);\n}\n\n\/\/ ================= geolocation =================\n\/\/ Explicitly requests the browser's native permission prompt via getCurrentPosition.\n\/\/ Resolves {precise:true} on grant, {precise:false} on IP fallback, or a {denied:true} marker\n\/\/ so the caller can tell the user exactly why they're seeing an approximate location.\nasync function getLocation(){\n  return new Promise(resolve=>{\n    if(!navigator.geolocation){resolve(null);return}\n    navigator.geolocation.getCurrentPosition(\n      p=>resolve({lat:p.coords.latitude,lng:p.coords.longitude,acc:p.coords.accuracy,precise:true}),\n      async err=>{\n        const denied=err && err.code===1; \/\/ PERMISSION_DENIED\n        try{\n          const r=await fetch('https:\/\/ipapi.co\/json\/',{signal:AbortSignal.timeout(3000)});\n          const d=await r.json();\n          if(d.latitude)resolve({lat:d.latitude,lng:d.longitude,acc:5000,precise:false,denied});\n          else resolve(denied?{denied:true}:null);\n        }catch(e){resolve(denied?{denied:true}:null)}\n      },\n      {enableHighAccuracy:true,timeout:10000,maximumAge:0}\n    );\n  });\n}\n\nasync function checkLocationPermission(){\n  try{\n    if(!navigator.permissions?.query)return null;\n    const st=await navigator.permissions.query({name:'geolocation'});\n    return st.state; \/\/ 'granted' | 'denied' | 'prompt'\n  }catch(e){return null}\n}\n\n\/\/ ================= app state =================\nlet map,selfMarker=null,lastLL=null,curHex=null,tab='nearby';\nlet peer=null,conns={},curChat=null,curChatName='';\nlet lastHexFC={cur:emptyFC(),ring:emptyFC(),inner:emptyFC()};\nlet gpsStale=false, staleTimer=null;\nconst markerCache=new Map(); \/\/ id -> {marker, el, l}\n\nasync function initApp(){\n  await openDB();\n  await loadState();\n  applyC();\n  $('#profBtn').textContent=M.name[0].toUpperCase();\n  $('#mockBtn').style.opacity=M.mock?1:.45;\n  $('#rtBtn').classList.toggle('on',!!M.rt);\n  $('#tkBtn').classList.toggle('on',!!M.tk);\n  $$('#gToggle button').forEach(b=>b.classList.toggle('on',b.dataset.g===(M.gview||'globe')));\n\n  \/\/ Render the globe immediately \u2014 no spinner, no waiting on geolocation.\n  \/\/ Starts on a world view; flies in once (and if) a location is granted.\n  map=new maplibregl.Map({\n    container:'map',style:STY[th],center:[0,20],zoom:1.3,\n    attributionControl:false,fadeDuration:200,maxPitch:85\n  });\n\n  const el=document.createElement('div');\n  el.className='sd';el.innerHTML='<i><\/i>';\n  selfMarker=new maplibregl.Marker({element:el,anchor:'center'});\n\n  map.on('load',()=>{\n    addAllLayers();\n    try{map.setProjection({type:M.gview==='flat'?'mercator':'globe'})}catch(e){}\n    applyAtmosphere();\n    document.body.classList.toggle('hollow',M.gview==='hollow');\n    setupUI();\n    locateAndSettle();\n  });\n  map.on('style.load',()=>{\n    addAllLayers();\n    try{map.setProjection({type:M.gview==='flat'?'mercator':'globe'})}catch(e){}\n    applyAtmosphere();\n    map.getSource('hc')?.setData(lastHexFC.cur);\n    map.getSource('hr')?.setData(lastHexFC.ring);\n    map.getSource('hi')?.setData(lastHexFC.inner);\n  });\n  map.on('click',closeAllPopups);\n\n  \/\/ ---- tilt\/pitch gesture -> markers grow along their vertical stem ----\n  \/\/ fires continuously while the user two-finger-drags (or ctrl-drags) to tilt the map\n  map.on('pitch',()=>{\n    const p=Math.max(0,Math.min(map.getPitch(),70));\n    document.documentElement.style.setProperty('--tilt',p);\n  });\n}\n\n\/\/ Runs after the globe is already visible. Requests geolocation (triggering the\n\/\/ browser's native permission prompt if not yet decided), then flies to the result.\nasync function locateAndSettle(){\n  const perm=await checkLocationPermission();\n  if(perm==='denied')toast('Location permission is blocked \u2014 enable it in your browser\/site settings for accurate results',4000);\n\n  let loc=await getLocation();\n  if(!loc||(!loc.lat&&!loc.denied)){\/* nothing usable *\/}\n  if(loc?.denied&&!loc.lat)toast('Location denied \u2014 using an approximate area instead',3000);\n  if(!loc||!loc.lat){loc=DEFLOC[Math.floor(Math.random()*DEFLOC.length)];loc.precise=false}\n  else if(loc.precise===false&&!loc.denied)toast('Using approximate location \u2014 enable GPS for accuracy',3000);\n\n  lastLL={lat:loc.lat,lng:loc.lng};\n  curHex=h3.latLngToCell(loc.lat,loc.lng,M.res);\n  $('#hH').textContent=curHex.slice(-8);\n  $('#hR').textContent=M.res;\n\n  selfMarker.setLngLat([loc.lng,loc.lat]).addTo(map);\n  map.flyTo({center:[loc.lng,loc.lat],zoom:RZ[M.res]||15,duration:2200,curve:1.4,essential:true});\n\n  renderHex();\n  refresh();\n  renderContacts();\n  startWatch();\n}\n\nfunction startWatch(){\n  if(!navigator.geolocation)return;\n  navigator.geolocation.watchPosition(\n    p=>{markFresh();updatePos(p.coords.latitude,p.coords.longitude,p.coords.accuracy,p.timestamp)},\n    ()=>markStale(),\n    {enableHighAccuracy:!!M.gps,maximumAge:1000,timeout:10000}\n  );\n}\nfunction markFresh(){gpsStale=false;$('#hDot').classList.remove('stale');clearTimeout(staleTimer);staleTimer=setTimeout(markStale,15000)}\nfunction markStale(){gpsStale=true;$('#hDot').classList.add('stale')}\n\nconst updatePos=(la,ln,ac,t)=>{\n  const f=kalman(la,ln,ac,t||Date.now());\n  lastLL=f;\n  const h=h3.latLngToCell(f.lat,f.lng,M.res);\n  const changed=h!==curHex;\n  curHex=h;\n  selfMarker?.setLngLat([f.lng,f.lat]);\n  $('#hH').textContent=h.slice(-8);\n  if(M.tk)map.easeTo({center:[f.lng,f.lat],duration:400});\n  if(changed){renderHex();refresh();renderContacts()}\n};\n\n\/\/ ================= 3D globe entry \/ atmosphere =================\nfunction applyAtmosphere(){\n  try{\n    map.setSky({\n      'sky-color':'#0d1a2b','sky-horizon-blend':.5,\n      'horizon-color':th==='dark'?'#1a2a3a':'#c9dceb','horizon-fog-blend':.55,\n      'fog-color':th==='dark'?'#0A0D10':'#e8eef2','fog-ground-blend':.6\n    });\n  }catch(e){\/* MapLibre build without sky\/fog support \u2014 globe projection still applies *\/}\n}\n\nfunction setGView(mode){\n  M.gview=mode;sv();\n  $$('#gToggle button').forEach(b=>b.classList.toggle('on',b.dataset.g===mode));\n  document.body.classList.toggle('hollow',mode==='hollow');\n  try{map.setProjection({type:mode==='flat'?'mercator':'globe'})}catch(e){}\n  \/\/ camera settles toward an overview curve on entry, then eases back to the local hex on flat\/hollow\n  if(mode==='flat'&&lastLL){\n    map.flyTo({center:[lastLL.lng,lastLL.lat],zoom:RZ[M.res]||15,pitch:0,duration:900,curve:1.3,essential:true});\n  }else if(lastLL){\n    map.flyTo({center:[lastLL.lng,lastLL.lat],zoom:Math.max(2,(RZ[M.res]||15)-1),duration:900,curve:1.3,essential:true});\n  }\n}\n\n\/\/ ================= hex layers (h3 -> geojson) =================\nfunction hPoly(h){const b=h3.cellToBoundary(h,true);b.push(b[0]);return{type:'Feature',properties:{},geometry:{type:'Polygon',coordinates:[b]}}}\n\nfunction addAllLayers(){\n  if(!map.getSource('hc')){\n    map.addSource('hc',{type:'geojson',data:emptyFC()});\n    map.addLayer({id:'hcf',type:'fill',source:'hc',paint:{'fill-color':M.hc,'fill-opacity':.15}});\n    map.addLayer({id:'hcl',type:'line',source:'hc',paint:{'line-color':M.hc,'line-width':2}});\n  }\n  if(!map.getSource('hr')){\n    map.addSource('hr',{type:'geojson',data:emptyFC()});\n    map.addLayer({id:'hrl',type:'line',source:'hr',paint:{'line-color':M.hc,'line-width':1,'line-dasharray':[2,2]}});\n  }\n  if(!map.getSource('hi')){\n    map.addSource('hi',{type:'geojson',data:emptyFC()});\n    map.addLayer({id:'hil',type:'line',source:'hi',paint:{'line-color':M.hc,'line-width':.5,'line-opacity':.4}});\n  }\n  if(!map.getSource('rt')){\n    map.addSource('rt',{type:'geojson',data:emptyFC()});\n    map.addLayer({id:'rtl',type:'line',source:'rt',paint:{'line-color':M.tc,'line-width':4,'line-opacity':.85}});\n  }\n}\n\nfunction renderHex(){\n  if(!curHex||!map.isStyleLoaded())return;\n  try{\n    map.setPaintProperty('hcf','fill-color',M.hc);\n    map.setPaintProperty('hcl','line-color',M.hc);\n    map.setPaintProperty('hrl','line-color',M.hc);\n    map.setPaintProperty('hil','line-color',M.hc);\n    const ly=M.ho.ly||['cur','ring'];\n    lastHexFC.cur=ly.includes('cur')?{type:'FeatureCollection',features:[hPoly(curHex)]}:emptyFC();\n    lastHexFC.ring=(ly.includes('ring')&&M.ho.rr>0)?{type:'FeatureCollection',features:h3.gridDisk(curHex,M.ho.rr).filter(h=>h!==curHex).map(hPoly)}:emptyFC();\n    lastHexFC.inner=ly.includes('inner')?{type:'FeatureCollection',features:h3.cellToChildren(curHex,Math.min(M.res+2,15)).map(hPoly)}:emptyFC();\n    map.getSource('hc').setData(lastHexFC.cur);\n    map.getSource('hr').setData(lastHexFC.ring);\n    map.getSource('hi').setData(lastHexFC.inner);\n  }catch(e){}\n}\n\n\/\/ ================= mock\/demo data =================\nconst MU=[{id:'d_sarah',n:'Sarah',la:40.7135,ln:-74.0055},{id:'d_mike',n:'Mike',la:40.712,ln:-74.007},{id:'d_emma',n:'Emma',la:40.7145,ln:-74.0045},{id:'d_james',n:'James',la:40.711,ln:-74.008}];\nconst MI=[{t:'Sourdough starter',p:0,ca:'\ud83c\udf5c',ty:'share',pe:'d_emma'},{t:'Vintage camera',p:45,ca:'\ud83d\udecd\ufe0f',ty:'sell',pe:'d_sarah'},{t:'Need bike pump',p:0,ca:'\ud83d\udecd\ufe0f',ty:'buy',pe:'d_mike'},{t:'Coffee delivery',p:5,ca:'\ud83d\udeb2',ty:'sell',pe:'d_james'},{t:'Ceramics studio',p:0,ca:'\ud83c\udfea',ty:'business',pe:'d_emma'}];\n\nasync function seedMock(){\n  if(!M.mock||!curHex)return;\n  const existing=await db.l.countMock();\n  if(existing>0)return;\n  const ring=h3.gridDisk(curHex,2);\n  for(const i of MI){\n    const h=ring[Math.floor(Math.random()*ring.length)];\n    const[la,ln]=h3.cellToLatLng(h);\n    await db.l.put({id:uid(),h,...i,la,ln,ts:Date.now()-Math.random()*36e5,de:'Demo listing',mock:1});\n  }\n}\n\n\/\/ ================= filtering =================\n\/\/ A user's own intent flags describe what they want to SEE, not what they're posting.\n\/\/ buy(1) -> see 'sell' posts, sell(2) -> see 'buy' requests, share(4) -> share, business(8) -> business.\nconst INTENT_BIT={sell:1,buy:2,share:4,business:8};\nfunction matchIntent(ty,fl){if(!fl)return true;return(fl & (INTENT_BIT[ty]||0))!==0}\n\nfunction filterItems(items){\n  let f=items;\n  if(!M.mock)f=f.filter(l=>!l.mock);\n  if(M.fi.t!=='all')f=f.filter(l=>l.ty===M.fi.t);\n  f=f.filter(l=>matchIntent(l.ty,M.flags));\n  if(lastLL&&M.fi.d<5)f=f.filter(l=>haversine(lastLL.lat,lastLL.lng,l.la,l.ln)<=M.fi.d);\n  if(M.fi.p!=='any')f=f.filter(l=>{\n    if(M.fi.p==='free')return l.p===0;\n    if(M.fi.p==='u25')return l.p<25;\n    if(M.fi.p==='u50')return l.p<50;\n    if(M.fi.p==='o50')return l.p>=50;\n    return true;\n  });\n  return f;\n}\n\n\/\/ ================= listings: indexed ring query, not a full table scan =================\nconst refresh=debounce(async()=>{\n  if(!curHex)return;\n  await seedMock();\n  const dataRadius=Math.max(2,M.ho.rr||2);\n  const ring=h3.gridDisk(curHex,dataRadius);\n  const all=await db.l.byHexRing(ring);\n  const near=filterItems(all).sort((a,b)=>b.ts-a.ts);\n  renderList(near);\n  renderMarkers(near);\n  renderCarousel(near);\n},120);\n\nfunction tAgo(ts){const s=(Date.now()-ts)\/1e3;return s<60?'now':s<3600?~~(s\/60)+'m':s<86400?~~(s\/3600)+'h':~~(s\/86400)+'d'}\n\nfunction renderList(items){\n  const b=$('#sB');\n  if(tab!=='nearby')return;\n  if(!items.length){b.innerHTML='<div class=\"st\">Nearby<\/div><div class=\"em\"><div class=\"big\">\u2b21<\/div>No items here yet \u2014 be the first to post<\/div>';return}\n  const shown=items.slice(0,100);\n  b.innerHTML='<div class=\"st\">Nearby \u00b7 '+items.length+'<\/div>'+shown.map(l=>\n    '<div class=\"cd\" data-id=\"'+l.id+'\"><div class=\"th\">'+l.ca+'<\/div><div class=\"mt\"><div class=\"t1\">'+esc(l.t)+' <span class=\"ch '+l.ty+'\">'+l.ty+'<\/span><\/div><div class=\"t2\">'+esc(l.pe.replace('d_',''))+' \u00b7 '+tAgo(l.ts)+'<\/div><\/div><div class=\"pr\">'+(l.p>0?'$'+l.p:'free')+'<\/div><\/div>'\n  ).join('');\n  $$('.cd',b).forEach(c=>c.onclick=()=>openDetail(c.dataset.id));\n}\n\n\/\/ ---- DOM markers, diffed by id so we don't tear down + rebuild every refresh ----\nfunction closeAllPopups(){$$('.mp.show').forEach(p=>p.classList.remove('show'))}\n\nfunction buildMarkerEl(l){\n  const w=document.createElement('div');w.className='mc';\n  const name=esc(l.pe.replace('d_',''));\n  w.innerHTML=`<div class=\"mp\"><div class=\"ph\"><div class=\"pa\">${(name[0]||'?').toUpperCase()}<\/div><div class=\"pi\"><div class=\"pn\">${name}<\/div><div class=\"px\">${esc(l.h).slice(-6)}<\/div><\/div><span class=\"pt ${l.ty}\">${l.ty}<\/span><\/div>\n    <div class=\"pT\">${esc(l.t)}<\/div>\n    <div class=\"pm\"><span>${tAgo(l.ts)}<\/span><span class=\"pp\">${l.p>0?'$'+l.p:'free'}<\/span><\/div>\n    <div class=\"pe\">\ud83d\udccd Tap route for ETA<\/div>\n    <div class=\"pA\"><button class=\"pB sc\" data-rt>\ud83d\uddfa\ufe0f Route<\/button><button class=\"pB pr2\" data-vw>View<\/button><\/div><\/div>\n    <div class=\"md ${l.ty}\">${l.ca}<\/div>\n    <div class=\"stem\"><\/div>\n    <div class=\"gdot\"><\/div>`;\n  return w;\n}\n\nfunction bindMarkerEvents(entry){\n  const {el}=entry;\n  const dot=el.querySelector('.md'), pop=el.querySelector('.mp');\n  dot.onclick=ev=>{\n    ev.stopPropagation();hap();\n    const isOpen=pop.classList.contains('show');\n    closeAllPopups();\n    if(!isOpen)pop.classList.add('show');\n  };\n  el.querySelector('[data-vw]').onclick=ev=>{ev.stopPropagation();closeAllPopups();openDetail(entry.l.id)};\n  el.querySelector('[data-rt]').onclick=async ev=>{\n    ev.stopPropagation();\n    const etaEl=pop.querySelector('.pe');etaEl.textContent='\u23f3 Calculating...';\n    await drawRouteTo(entry.l.la,entry.l.ln,label=>etaEl.textContent=label);\n    closeAllPopups();\n  };\n}\n\nfunction renderMarkers(items){\n  const capped=items.slice(0,MARKER_CAP);\n  const seen=new Set();\n  capped.forEach(l=>{\n    seen.add(l.id);\n    let entry=markerCache.get(l.id);\n    if(!entry){\n      const el=buildMarkerEl(l);\n      const marker=new maplibregl.Marker({element:el,anchor:'bottom'}).setLngLat([l.ln,l.la]).addTo(map);\n      entry={marker,el,l};\n      markerCache.set(l.id,entry);\n      bindMarkerEvents(entry);\n    }else{\n      entry.l=l;\n    }\n  });\n  for(const [id,entry] of markerCache){\n    if(!seen.has(id)){entry.marker.remove();markerCache.delete(id)}\n  }\n  if(!M.rt)try{map.getSource('rt').setData(emptyFC())}catch(e){}\n}\n\n\/\/ ================= bottom carousel =================\nfunction renderCarousel(items){\n  const c=$('#carousel');\n  if(!items.length){c.classList.remove('show');c.innerHTML='';return}\n  c.classList.add('show');\n  const shown=items.slice(0,30);\n  c.innerHTML=shown.map(l=>`<div class=\"bcc\" data-id=\"${l.id}\"><div class=\"ic\">${l.ca}<\/div><div class=\"tt2\">${esc(l.t)}<\/div><div class=\"pp2\">${l.p>0?'$'+l.p:'free'}<\/div><\/div>`).join('');\n  $$('.bcc',c).forEach(card=>card.onclick=()=>{\n    hap();$$('.bcc',c).forEach(x=>x.classList.remove('sel'));card.classList.add('sel');\n    const l=shown.find(x=>x.id===card.dataset.id);if(!l)return;\n    map.flyTo({center:[l.ln,l.la],zoom:Math.max(RZ[M.res]||15,16),duration:600,essential:true});\n    const entry=markerCache.get(l.id);\n    if(entry){closeAllPopups();entry.el.querySelector('.mp')?.classList.add('show')}\n  });\n}\n\n\/\/ ================= contacts =================\nfunction renderContacts(){\n  const c=$('#cScroll');\n  if(!M.mock||!curHex){c.innerHTML='';return}\n  const cs=MU.filter(u=>h3.gridDistance(curHex,h3.latLngToCell(u.la,u.ln,M.res))<=2);\n  c.innerHTML=cs.map(u=>`<div class=\"ci\" data-id=\"${u.id}\" data-n=\"${esc(u.n)}\"><div class=\"ca\">${u.n[0]}<\/div><div class=\"cn\">${esc(u.n)}<\/div><\/div>`).join('');\n  $$('.ci',c).forEach(e=>e.onclick=()=>{hap();openChat(e.dataset.id,e.dataset.n)});\n}\n\n\/\/ ================= detail sheet =================\nasync function openDetail(id){\n  const l=await db.l.get(id);if(!l)return;\n  $('#detM').innerHTML=`<div class=\"mh\"><h2>${esc(l.t)}<\/h2><button class=\"cx\" data-c=\"detOv\">\u2715<\/button><\/div>\n  <div style=\"font-size:44px;text-align:center;padding:18px;background:rgba(255,255,255,.04);border-radius:10px;margin-bottom:10px\">${l.ca}<\/div>\n  <div style=\"display:flex;gap:7px;margin-bottom:10px\"><span class=\"ch ${l.ty}\">${l.ty}<\/span><span class=\"pr\" style=\"font-size:16px\">${l.p>0?'$'+l.p:'free'}<\/span><\/div>\n  <p style=\"font-size:13px;line-height:1.5\">${esc(l.de||'No details')}<\/p>\n  <div class=\"pe\" id=\"detEta\" style=\"margin:10px 0;font-size:12px\">\ud83d\udccd Tap route for ETA<\/div>\n  <button class=\"bp\" id=\"msgB\">\ud83d\udcac Message<\/button>\n  <button class=\"bs\" id=\"rtB\">\ud83d\uddfa\ufe0f Get Route & ETA<\/button>`;\n\n  $('#msgB').onclick=()=>{closeOv('detOv');openChat(l.pe,l.pe.replace('d_',''))};\n  $('#rtB').onclick=async()=>{\n    hap();const etaEl=$('#detEta');etaEl.textContent='\u23f3 Calculating...';\n    await drawRouteTo(l.la,l.ln,label=>etaEl.textContent=label);\n    closeOv('detOv');\n  };\n  $$('[data-c]',$('#detM')).forEach(b=>b.onclick=()=>closeOv(b.dataset.c));\n  openOv('detOv');\n}\n\nfunction openOv(id){$('#'+id).classList.add('show')}\nfunction closeOv(id){$('#'+id).classList.remove('show')}\n\n\/\/ ================= UI setup =================\nfunction setupUI(){\n  $('#themeBtn').onclick=()=>{\n    hap();th=th==='dark'?'light':'dark';svTheme();map.setStyle(STY[th]);\n    $('#tI').innerHTML=th==='dark'?'<path d=\"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z\"\/>':'<circle cx=\"12\" cy=\"12\" r=\"4\"\/><path d=\"M12 2v2M12 20v2M4 12H2M22 12h-2\"\/>';\n  };\n\n  const isFs=()=>document.fullscreenElement===$('#map')||document.webkitFullscreenElement===$('#map');\n  const updFsIcon=()=>$('#fI').innerHTML=isFs()?'<path d=\"M9 3v4a2 2 0 0 1-2 2H3M15 3v4a2 2 0 0 0 2 2h4M21 15h-4a2 2 0 0 0-2 2v4M3 15h4a2 2 0 0 1 2 2v4\"\/>':'<path d=\"M8 3H5a2 2 0 0 0-2 2v3M16 3h3a2 2 0 0 1 2 2v3M21 16v3a2 2 0 0 1-2 2h-3M8 21H5a2 2 0 0 1-2-2v-3\"\/>';\n  $('#fsBtn').onclick=()=>{\n    hap();const mapEl=$('#map');\n    if(!isFs())(mapEl.requestFullscreen||mapEl.webkitRequestFullscreen)?.call(mapEl);\n    else (document.exitFullscreen||document.webkitExitFullscreen)?.call(document);\n  };\n  document.addEventListener('fullscreenchange',()=>{updFsIcon();setTimeout(()=>map.resize(),100)});\n  document.addEventListener('webkitfullscreenchange',()=>{updFsIcon();setTimeout(()=>map.resize(),100)});\n\n  $('#locBtn').onclick=()=>{hap();lastLL&&map.flyTo({center:[lastLL.lng,lastLL.lat],zoom:RZ[M.res]||15,duration:450,essential:true})};\n\n  $('#gToggle').onclick=e=>{const b=e.target.closest('button');if(!b)return;hap();setGView(b.dataset.g)};\n\n  $$('.ov').forEach(o=>o.onclick=e=>{if(e.target===o)closeOv(o.id)});\n  $$('[data-c]').forEach(b=>b.onclick=()=>closeOv(b.dataset.c));\n\n  const sheet=$('#sheet');let shOpen=false;\n  const setSh=v=>{shOpen=v;sheet.classList.toggle('open',v)};\n\n  $$('.ni').forEach(i=>i.onclick=()=>{\n    hap();const t=i.dataset.t;\n    $$('.ni').forEach(x=>x.classList.remove('on'));i.classList.add('on');\n    tab=t;\n    if(t==='profile')openProf();\n    else if(t==='hex')openHex();\n    else{setSh(true);if(t==='nearby')refresh();else if(t==='messages')renderMsgs()}\n  });\n\n  (()=>{const h=$('#sH');let sy=0,dr=false;\n    h.addEventListener('touchstart',e=>{dr=true;sy=e.touches[0].clientY;sheet.style.transition='none'},{passive:true});\n    h.addEventListener('touchmove',e=>{if(dr)sheet.style.transform=`translateY(${Math.max(0,e.touches[0].clientY-sy)}px)`},{passive:true});\n    h.addEventListener('touchend',e=>{if(!dr)return;dr=false;sheet.style.transition='';setSh(e.changedTouches[0].clientY-sy<-25);sheet.style.transform=''});\n    h.onclick=()=>{hap();setSh(!shOpen)};\n  })();\n\n  function openProf(){\n    $('#pN').value=M.name;\n    $$('#atS button').forEach(b=>b.classList.toggle('on',b.dataset.v===M.type));\n    $$('.ic').forEach(c=>c.classList.toggle('on',(M.flags&+c.dataset.b)!==0));\n    $$('#tC .cS').forEach(s=>s.classList.toggle('on',s.dataset.c===M.tc));\n    $$('#hC .cS').forEach(s=>s.classList.toggle('on',s.dataset.c===M.hc));\n    $('#mT').classList.toggle('on',!!M.mock);\n    $('#gT').classList.toggle('on',!!M.gps);\n    renderPlanCards();\n    updBoostPill();\n    openOv('profOv');\n  }\n\n  function renderPlanCards(){\n    $('#planCards').innerHTML=Object.entries(PLANS).map(([k,p])=>\n      `<div class=\"pc ${M.plan===k?'sel':''}\" data-plan=\"${k}\">${M.plan===k?'<span class=\"cur\">Current<\/span>':''}<div class=\"pn2\">${p.label}<span class=\"pr3\">${p.price?'$'+p.price+'\/mo':'$0'}<\/span><\/div><div class=\"pf\">${p.desc}<\/div><\/div>`\n    ).join('');\n    $$('.pc',$('#planCards')).forEach(c=>c.onclick=async()=>{\n      hap();M.plan=c.dataset.plan;await sv();renderPlanCards();updBoostPill();toast('Plan set to '+plan().label+' (local only, no payment)');\n    });\n  }\n\n  function updBoostPill(){\n    const on=!!(M.pendingBoost);\n    $('#boostT').classList.toggle('on',on&&plan().boost);\n    $('#boostSub').textContent=plan().boost?'Reach more of the hex grid with a paid boost':'Pro plan required \u2014 reach more of the hex grid';\n  }\n  $('#boostT').onclick=function(){\n    if(!plan().boost){hap();toast('Upgrade to Pro for Ad Boost');return}\n    hap();this.classList.toggle('on');M.pendingBoost=this.classList.contains('on');\n  };\n  $('#openBoostBtn').onclick=()=>{hap();closeOv('profOv');openBoostScreen()};\n\n  $$('.ic').forEach(c=>c.onclick=()=>{hap();c.classList.toggle('on')});\n  $('#atS').onclick=e=>{const b=e.target.closest('button');if(!b)return;hap();$$('#atS button').forEach(x=>x.classList.remove('on'));b.classList.add('on')};\n  $$('#tC .cS').forEach(s=>s.onclick=()=>{hap();$$('#tC .cS').forEach(x=>x.classList.remove('on'));s.classList.add('on');M.tc=s.dataset.c;applyC();sv()});\n  $$('#hC .cS').forEach(s=>s.onclick=()=>{hap();$$('#hC .cS').forEach(x=>x.classList.remove('on'));s.classList.add('on');M.hc=s.dataset.c;applyC();sv();renderHex()});\n  $('#mT').onclick=function(){hap();this.classList.toggle('on')};\n  $('#gT').onclick=function(){hap();this.classList.toggle('on')};\n  $('#svP').onclick=async()=>{\n    hap();M.name=$('#pN').value.trim()||'Neighbor';M.type=$('#atS .on')?.dataset.v||'individual';\n    M.flags=$$('.ic.on').reduce((a,c)=>a|+c.dataset.b,0);\n    const mockWasOn=!!M.mock;\n    M.mock=$('#mT').classList.contains('on')?1:0;M.gps=$('#gT').classList.contains('on')?1:0;\n    await sv();closeOv('profOv');$('#profBtn').textContent=M.name[0].toUpperCase();\n    $('#mockBtn').style.opacity=M.mock?1:.45;\n    toast('Saved');refresh();renderContacts();\n    if(M.mock&&!mockWasOn)seedMock().then(refresh);\n  };\n  $('#profBtn').onclick=()=>{hap();openProf()};\n\n  function openHex(){\n    $('#rR').max=plan().maxRes;\n    if(M.res>plan().maxRes){M.res=plan().maxRes;sv()}\n    $('#rR').value=M.res;$('#rV').textContent=M.res;$('#rS').textContent=RS[M.res];\n    $('#rgR').value=M.ho.rr;$('#rgV').textContent=M.ho.rr;\n    $$('#lS button').forEach(b=>b.classList.toggle('on',(M.ho.ly||[]).includes(b.dataset.l)));\n    openOv('hexOv');\n  }\n  $('#rR').oninput=e=>{$('#rV').textContent=e.target.value;$('#rS').textContent=RS[e.target.value]};\n  $('#rgR').oninput=e=>$('#rgV').textContent=e.target.value;\n  $('#lS').onclick=e=>{const b=e.target.closest('button');if(b){hap();b.classList.toggle('on')}};\n  $('#svH').onclick=async()=>{\n    hap();M.res=Math.min(+$('#rR').value,plan().maxRes);M.ho.rr=+$('#rgR').value;M.ho.ly=$$('#lS .on').map(b=>b.dataset.l);\n    await sv();closeOv('hexOv');\n    if(lastLL){curHex=h3.latLngToCell(lastLL.lat,lastLL.lng,M.res);$('#hR').textContent=M.res;$('#hH').textContent=curHex.slice(-8);renderHex();refresh()}\n    toast('Updated');\n  };\n  $('#layBtn').onclick=()=>{hap();openHex()};\n\n  $$('.fc').forEach(c=>c.onclick=()=>{\n    hap();const f=c.dataset.f;\n    if(f==='dist'){$('#dR').value=M.fi.d;$('#dV').textContent=M.fi.d.toFixed(1);openOv('distOv')}\n    else if(f==='price'){$$('#pS button').forEach(b=>b.classList.toggle('on',b.dataset.p===M.fi.p));openOv('prOv')}\n    else{$$('.fc').forEach(x=>x.classList.remove('on'));c.classList.add('on');M.fi.t=f;sv();refresh()}\n  });\n  $('#dR').oninput=e=>$('#dV').textContent=(+e.target.value).toFixed(1);\n  $('#svD').onclick=()=>{hap();M.fi.d=+$('#dR').value;$('#dL').textContent='<'+M.fi.d.toFixed(1)+'km';sv();closeOv('distOv');refresh()};\n  $('#pS').onclick=e=>{const b=e.target.closest('button');if(!b)return;hap();$$('#pS button').forEach(x=>x.classList.remove('on'));b.classList.add('on')};\n  $('#svPr').onclick=()=>{\n    hap();M.fi.p=$('#pS .on')?.dataset.p||'any';\n    const lb={any:'Any',free:'Free',u25:'<$25',u50:'<$50',o50:'$50+'};\n    $('#pL').textContent=lb[M.fi.p];sv();closeOv('prOv');refresh();\n  };\n\n  $('#rtBtn').onclick=function(){hap();M.rt=M.rt?0:1;this.classList.toggle('on',!!M.rt);if(!M.rt)try{map.getSource('rt').setData(emptyFC())}catch(e){}sv();toast(M.rt?'Route on':'Route off')};\n  $('#tkBtn').onclick=function(){hap();M.tk=M.tk?0:1;this.classList.toggle('on',!!M.tk);sv();toast(M.tk?'Live tracking on':'Live tracking off')};\n  $('#mockBtn').onclick=async()=>{hap();M.mock=M.mock?0:1;await sv();$('#mockBtn').style.opacity=M.mock?1:.45;toast(M.mock?'Demo data on':'Demo data off');if(M.mock)await seedMock();refresh();renderContacts()};\n\n  \/\/ ---- new listing ----\n  let nTy='sell',nCa='\ud83d\udecd\ufe0f';\n  $('#sellBtn').onclick=()=>{hap();openOv('listOv')};\n  $('#tyS').onclick=e=>{const b=e.target.closest('button');if(!b)return;hap();$$('#tyS button').forEach(x=>x.classList.remove('on'));b.classList.add('on');nTy=b.dataset.v};\n  $('#caS').onclick=e=>{const b=e.target.closest('button');if(!b)return;hap();$$('#caS button').forEach(x=>x.classList.remove('on'));b.classList.add('on');nCa=b.dataset.v};\n  $('#pubBtn').onclick=async()=>{\n    hap();const t=$('#lT').value.trim();\n    if(!t){toast('Add a title');return}\n    if(!curHex){toast('Waiting for location\u2026');return}\n    const price=Math.max(0,+$('#lP').value||0);\n    const[la,ln]=h3.cellToLatLng(curHex);\n    await db.l.put({id:uid(),h:curHex,ty:nTy,ca:nCa,t,de:$('#lD').value.trim(),p:price,pe:M.id,ts:Date.now(),la,ln,mock:0});\n    if(M.pendingBoost&&plan().boost){\n      await db.bo.put({id:uid(),bm:'CPC',budget:10,ring:M.ho.rr||2,poll:false,pollQ:'',ts:Date.now(),imp:0,clk:0,spend:0});\n      toast('Published + boosted!');\n    }else toast('Published!');\n    M.pendingBoost=false;$('#boostT').classList.remove('on');\n    $('#lT').value='';$('#lD').value='';$('#lP').value='';\n    closeOv('listOv');refresh();\n  };\n\n  \/\/ ---- chat \/ peer ----\n  function connectPeer(){\n    try{\n      peer=new Peer(M.id,{debug:0});\n      peer.on('connection',c=>{\n        conns[c.peer]=c;\n        c.on('data',async d=>{\n          if(d?.ty==='msg'){\n            await storeMsg(c.peer,d.tx,false,d.n);\n            if(curChat===c.peer)addMsg(d.tx,false,Date.now());\n            else{updBadge();toast('New message')}\n            if(tab==='messages')renderMsgs();\n          }\n        });\n      });\n      peer.on('call',call=>{\n        pendingCall=call;\n        $('#icAv').textContent=(call.peer.replace('d_','')[0]||'?').toUpperCase();\n        $('#icNm').textContent=call.peer.replace('d_','');\n        $('#icBanner').style.display='';\n        $('#cSt').style.display='none';\n        $('#cC').innerHTML=`<button class=\"acc\" id=\"accBtn\" aria-label=\"Accept\">\u2713<\/button><button class=\"end\" id=\"decBtn\" aria-label=\"Decline\">\u2715<\/button>`;\n        $('#callSc').classList.add('show');\n        $('#accBtn').onclick=async()=>{\n          hap();\n          try{\n            lStr=await navigator.mediaDevices.getUserMedia({video:{facingMode:'user'},audio:true});\n            $('#lv').srcObject=lStr;$('#icBanner').style.display='none';$('#cSt').style.display='';$('#cSt').textContent='Connected';\n            $('#cC').innerHTML=`<button id=\"mtBtn\" aria-label=\"Mute\"><svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3Z\"\/><path d=\"M19 10v2a7 7 0 0 1-14 0v-2M12 19v4\"\/><\/svg><\/button><button class=\"end\" id=\"ecBtn\" aria-label=\"End call\"><svg width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 9c-2.5 0-4.8.6-6.8 1.7a1.7 1.7 0 0 0-.5 2.5l1.6 2.1c.4.5 1.1.7 1.7.4.8-.4 1.7-.7 2.6-.9.4-.1.7-.4.7-.8v-1.6c0-.4.3-.8.7-.9 1.3-.3 2.7-.3 4 0 .4.1.7.5.7.9V14c0 .4.3.7.7.8.9.2 1.8.5 2.6.9.6.3 1.3.1 1.7-.4l1.6-2.1a1.7 1.7 0 0 0-.5-2.5C16.8 9.6 14.5 9 12 9Z\"\/><\/svg><\/button>`;\n            bindCallButtons();\n            pendingCall.answer(lStr);\n            cCall=pendingCall;\n            cCall.on('stream',s=>{$('#rv').srcObject=s});\n            cCall.on('close',endCall);\n            pendingCall=null;\n          }catch(e){toast('Camera\/mic needed');endCall()}\n        };\n        $('#decBtn').onclick=()=>{hap();pendingCall?.close();pendingCall=null;endCall()};\n      });\n      peer.on('error',e=>{console.log('peer error',e.type);if(e.type==='unavailable-id')toast('Reconnecting\u2026')});\n      peer.on('disconnected',()=>{try{peer.reconnect()}catch(e){}});\n    }catch(e){}\n  }\n  connectPeer();\n\n  async function storeMsg(ci,tx,me,peerName){\n    await db.m.add({c:ci,tx,me,ts:Date.now()});\n    let c=await db.c.get(ci);\n    const now=Date.now();\n    if(!c)c={p:ci,n:peerName||ci.replace('d_',''),ts:now,ls:tx,ur:0};\n    else{c.ts=now;c.ls=tx}\n    if(!me&&curChat!==ci)c.ur=(c.ur||0)+1;\n    await db.c.put(c);\n  }\n\n  async function updBadge(){\n    const cs=await db.c.all(),t=cs.reduce((a,c)=>a+(c.ur||0),0),b=$('#mB');\n    if(t>0){b.style.display='flex';b.textContent=t>9?'9+':t}else b.style.display='none';\n  }\n  updBadge();\n\n  async function openChat(p,n){\n    hap();curChat=p;curChatName=n;\n    let c=await db.c.get(p);\n    if(!c){c={p,n,ts:Date.now(),ls:'',ur:0};await db.c.put(c)}\n    else if(c.ur){c.ur=0;await db.c.put(c)}\n    updBadge();\n    $('#chNm').textContent=n;$('#chAv').textContent=n[0].toUpperCase();$('#chId').textContent=p;\n    $('#chBd').innerHTML='';\n    const ms=await db.m.byConversation(p);\n    ms.forEach(m=>addMsg(m.tx,m.me,m.ts));\n    if(!ms.length)addMsg(p.startsWith('d_')?'Demo contact \u2014 replies are simulated':'Say hello!',null,null,true);\n    $('#chatSc').classList.add('show');\n    if(!p.startsWith('d_')&&peer&&!conns[p]){\n      try{const cn=peer.connect(p);conns[p]=cn;cn.on('data',async d=>{if(d?.ty==='msg'){await storeMsg(p,d.tx,false,d.n);if(curChat===p)addMsg(d.tx,false,Date.now())}})}catch(e){}\n    }\n  }\n\n  function addMsg(tx,me,ts,sys=false){\n    const d=document.createElement('div');\n    d.className='msg '+(sys?'sys':me?'me':'them');\n    d.innerHTML=esc(tx)+(ts?`<div class=\"mt\">${new Date(ts).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'})}<\/div>`:'');\n    if(!sys&&ts){\n      const a=document.createElement('div');a.className='ma';\n      a.innerHTML='<button class=\"mb\">\ud83d\uddd1<\/button>';\n      a.querySelector('.mb').onclick=async ev=>{ev.stopPropagation();hap();await db.m.deleteOne(curChat,ts);d.remove();toast('Deleted')};\n      d.appendChild(a);\n    }\n    $('#chBd').appendChild(d);$('#chBd').scrollTop=$('#chBd').scrollHeight;\n  }\n\n  $('#chBk').onclick=()=>{hap();$('#chatSc').classList.remove('show');curChat=null;if(tab==='messages')renderMsgs()};\n\n  async function sendMsg(){\n    const i=$('#chIn'),tx=i.value.trim();\n    if(!tx||!curChat)return;\n    hap();i.value='';addMsg(tx,true,Date.now());await storeMsg(curChat,tx,true);\n    if(curChat.startsWith('d_')){\n      setTimeout(async()=>{\n        const r=['Sounds good!','Available!','Sure!','\ud83d\udc4d','Can do!'][~~(Math.random()*5)];\n        addMsg(r,false,Date.now());await storeMsg(curChat,r,false,curChatName);if(tab==='messages')renderMsgs();\n      },400+Math.random()*400);\n    }else if(conns[curChat])try{conns[curChat].send({ty:'msg',tx,n:M.name})}catch(e){}\n    if(tab==='messages')renderMsgs();\n  }\n  $('#chSnd').onclick=sendMsg;\n  $('#chIn').onkeydown=e=>{if(e.key==='Enter')sendMsg()};\n\n  async function renderMsgs(){\n    const cs=(await db.c.all()).sort((a,b)=>b.ts-a.ts),b=$('#sB');\n    if(tab!=='messages')return;\n    if(!cs.length){b.innerHTML='<div class=\"st\">Messages<\/div><div class=\"em\"><div class=\"big\">\ud83d\udcac<\/div>No conversations yet<\/div>';return}\n    b.innerHTML='<div class=\"st\">Messages<\/div>'+cs.map(c=>\n      `<div class=\"cr\" data-p=\"${c.p}\" data-n=\"${esc(c.n||c.p.replace('d_',''))}\"><div class=\"av\">${(c.n||'?')[0].toUpperCase()}<\/div><div class=\"mt\"><div class=\"nm\">${esc(c.n||c.p.replace('d_',''))}${c.ur?` <span class=\"nb\" style=\"position:static;display:inline-flex\">${c.ur}<\/span>`:''}<\/div><div class=\"ls\">${esc(c.ls||'No messages yet')}<\/div><\/div><div class=\"tm\">${tAgo(c.ts)}<\/div><button class=\"dl\" data-del=\"${c.p}\">\u2715<\/button><\/div>`\n    ).join('');\n    $$('.cr',b).forEach(r=>{\n      r.onclick=e=>{if(e.target.closest('.dl'))return;openChat(r.dataset.p,r.dataset.n)};\n      r.querySelector('.dl').onclick=async e=>{e.stopPropagation();hap();await db.c.delete(r.dataset.p);await db.m.deleteByConvo(r.dataset.p);r.remove();toast('Deleted');updBadge()};\n    });\n  }\n\n  \/\/ ---- video call ----\n  let lStr=null,cCall=null,pendingCall=null;\n  function bindCallButtons(){\n    $('#mtBtn').onclick=()=>{if(!lStr)return;hap();const t=lStr.getAudioTracks()[0];if(t){t.enabled=!t.enabled;toast(t.enabled?'Unmuted':'Muted')}};\n    $('#ecBtn').onclick=endCall;\n  }\n  bindCallButtons();\n\n  $('#vcBtn').onclick=async()=>{\n    if(!curChat||curChat.startsWith('d_')){toast('Video calls need a real contact');return}\n    try{\n      lStr=await navigator.mediaDevices.getUserMedia({video:{facingMode:'user'},audio:true});\n      $('#lv').srcObject=lStr;$('#callSc').classList.add('show');\n      $('#cSt').style.display='';$('#cSt').textContent='Calling...';$('#icBanner').style.display='none';\n      cCall=peer.call(curChat,lStr);\n      cCall.on('stream',s=>{$('#rv').srcObject=s;$('#cSt').textContent='Connected'});\n      cCall.on('close',endCall);\n    }catch(e){toast('Camera\/mic access needed')}\n  };\n  function endCall(){\n    if(cCall){try{cCall.close()}catch(e){}cCall=null}\n    if(pendingCall){try{pendingCall.close()}catch(e){}pendingCall=null}\n    if(lStr){lStr.getTracks().forEach(t=>t.stop());lStr=null}\n    $('#rv').srcObject=null;$('#lv').srcObject=null;\n    $('#icBanner').style.display='none';\n    $('#callSc').classList.remove('show');\n  }\n}\n\n\/\/ ================= Ad Boost & Analytics (simulated, client-side\/local only) =================\nlet anaTimer=null,curBoostForm={bm:'CPC'};\nfunction openBoostScreen(){\n  hap();\n  $('#baPlanLbl').textContent=plan().label+' plan';\n  $$('#baTabs button').forEach(b=>b.classList.toggle('on',b.dataset.bt==='boost'));\n  $('#baBoost').style.display='';$('#baAnalytics').style.display='none';\n  renderBoostList();\n  $('#boostSc').classList.add('show');\n}\n$('#baBk').onclick=()=>{hap();$('#boostSc').classList.remove('show');stopAnalyticsSim()};\n$('#baTabs').onclick=e=>{\n  const b=e.target.closest('button');if(!b)return;hap();\n  $$('#baTabs button').forEach(x=>x.classList.remove('on'));b.classList.add('on');\n  const t=b.dataset.bt;\n  $('#baBoost').style.display=t==='boost'?'':'none';\n  $('#baAnalytics').style.display=t==='analytics'?'':'none';\n  $('#anaLock').style.display=(t==='analytics'&&!plan().analytics)?'flex':'none';\n  if(t==='analytics'){if(plan().analytics)startAnalyticsSim();else stopAnalyticsSim()}\n  else stopAnalyticsSim();\n};\n$('#bmS').onclick=e=>{const b=e.target.closest('button');if(!b)return;hap();$$('#bmS button').forEach(x=>x.classList.remove('on'));b.classList.add('on');curBoostForm.bm=b.dataset.v};\n$('#bPoll').onclick=function(){hap();this.classList.toggle('on');$('#bPollQ').style.display=this.classList.contains('on')?'':'none'};\n\nasync function renderBoostList(){\n  const list=$('#boostList');\n  const bs=(await db.bo.all()).sort((a,b)=>b.ts-a.ts);\n  if(!bs.length){list.innerHTML='<div class=\"em\"><div class=\"big\">\ud83d\ude80<\/div>No active boosts<\/div>';return}\n  list.innerHTML=bs.map(b=>`<div class=\"cd\"><div class=\"th\">${b.bm==='CPC'?'\ud83d\udc46':b.bm==='CPM'?'\ud83d\udc41\ufe0f':'\ud83c\udfaf'}<\/div><div class=\"mt\"><div class=\"t1\">${b.bm} \u00b7 $${b.budget}\/day<\/div><div class=\"t2\">Ring ${b.ring} \u00b7 ${b.poll?'poll: '+esc(b.pollQ||'\u2014'):'no poll'}<\/div><\/div><div class=\"pr\">${tAgo(b.ts)}<\/div><\/div>`).join('');\n}\n\n$('#startBoost').onclick=async()=>{\n  if(!plan().boost){hap();toast('Upgrade to Pro to launch boosts');return}\n  hap();\n  const budget=Math.max(1,+$('#bBudget').value||10);\n  const ring=+$('#bRing').value;\n  const poll=$('#bPoll').classList.contains('on');\n  const pollQ=$('#bPollQ').value.trim();\n  await db.bo.put({id:uid(),bm:curBoostForm.bm,budget,ring,poll,pollQ,ts:Date.now(),imp:0,clk:0,spend:0});\n  toast('Boost launched (simulated)');\n  renderBoostList();\n};\n\nfunction startAnalyticsSim(){\n  stopAnalyticsSim();\n  let imp=0,clk=0,votes=0;\n  const reachHexes=curHex?h3.gridDisk(curHex,M.ho.rr||2).length:0;\n  const tick=()=>{\n    imp+=Math.round(8+Math.random()*40);\n    if(Math.random()<.3)clk+=1;\n    if(Math.random()<.08)votes+=1;\n    const cpx=curBoostForm.bm==='CPM'?imp\/1000*1.2:curBoostForm.bm==='CPC'?clk*.35:votes*.9;\n    $('#mImp').textContent=imp.toLocaleString();\n    $('#mClk').textContent=clk.toLocaleString();\n    $('#mReach').textContent=reachHexes;\n    $('#mVotes').textContent=votes;\n    $('#mSpend').textContent='$'+cpx.toFixed(2);\n    const roiVal=Math.max(0,+$('#roiVal').value||25);\n    const roi=cpx>0?(((clk*.12)*roiVal-cpx)\/cpx*100):0;\n    $('#mRoi').textContent=(roi>=0?'+':'')+roi.toFixed(0)+'%';\n    paintHeat(imp);\n  };\n  tick();\n  anaTimer=setInterval(tick,1400);\n  $('#roiVal').oninput=tick;\n}\nfunction stopAnalyticsSim(){\n  if(anaTimer){clearInterval(anaTimer);anaTimer=null}\n  try{map.setPaintProperty('hrl','line-opacity',1);map.setPaintProperty('hcf','fill-opacity',.15)}catch(e){}\n}\n\n\/\/ colors the ring hex layer by simulated intensity as a lightweight heat overlay on the globe\nfunction paintHeat(intensity){\n  try{\n    const t=Math.min(1,intensity\/2000);\n    map.setPaintProperty('hrl','line-opacity',.3+t*.7);\n    map.setPaintProperty('hcf','fill-opacity',.1+t*.35);\n    $('#heatBox').textContent=`Hex-grid heatmap \u00b7 intensity ${(t*100).toFixed(0)}% of ring capacity`;\n  }catch(e){}\n}\n\nfunction applyC(){document.documentElement.style.setProperty('--a',M.tc);document.documentElement.style.setProperty('--h',M.hc)}\n\n\/\/ Start the app\ninitApp();\n<\/script>\n<\/body>\n<\/html>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>HexMarket Hex ? All \ud83d\udcb0 Sell \ud83d\uded2 Buy \ud83c\udf81 Share \ud83c\udfea Biz \ud83d\udccd &lt;1km \ud83d\udcb5 Any hex \u00b7 r9&#8230; \ud83c\udf10 \u25ad \u25cc Nearby Msgs0 Hex Me Nearby\u2b21Locating&#8230; Profile \u2715 Name Type\ud83d\udc64 Indiv\u2b50 Pro Intents \u2014 what do you want to see? \ud83d\uded2Buy\ud83d\udcb0Sell\ud83c\udf81Share \ud83c\udfeaBiz\ud83d\udeb4Deliver\ud83d\udd27Service Theme Hex Color Demo listings High-accuracy GPS Subscription (local only \u2014 no [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-187","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"http:\/\/mana-corp.com\/index.php?rest_route=\/wp\/v2\/pages\/187","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/mana-corp.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"http:\/\/mana-corp.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"http:\/\/mana-corp.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/mana-corp.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=187"}],"version-history":[{"count":4,"href":"http:\/\/mana-corp.com\/index.php?rest_route=\/wp\/v2\/pages\/187\/revisions"}],"predecessor-version":[{"id":191,"href":"http:\/\/mana-corp.com\/index.php?rest_route=\/wp\/v2\/pages\/187\/revisions\/191"}],"wp:attachment":[{"href":"http:\/\/mana-corp.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=187"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}