{"id":192,"date":"2026-08-13T09:10:23","date_gmt":"2026-08-13T19:10:23","guid":{"rendered":"https:\/\/mana-corp.com\/?page_id=192"},"modified":"2026-08-13T12:40:11","modified_gmt":"2026-08-13T22:40:11","slug":"elementor-192","status":"publish","type":"page","link":"http:\/\/mana-corp.com\/?page_id=192","title":{"rendered":"Elementor #192"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-post\" data-elementor-id=\"192\" class=\"elementor elementor-192\">\n\t\t\t\t<div class=\"elementor-element elementor-element-ebcdace e-flex e-con-boxed e-con e-parent\" data-id=\"ebcdace\" 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-2c564ba elementor-widget elementor-widget-html\" data-id=\"2c564ba\" 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 Pro<\/title>\n<link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/maplibre-gl@4.7.1\/dist\/maplibre-gl.css\">\n<script src=\"https:\/\/unpkg.com\/maplibre-gl@4.7.1\/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<style>\n:root {\n  --c-pri: #10b981; \/* Emerald 500 *\/\n  --c-pri-dim: rgba(16, 185, 129, 0.15);\n  --c-sec: #f59e0b; \/* Amber 500 *\/\n  --c-acc: #ec4899; \/* Pink 500 *\/\n  --c-bg: #0f172a;   \/* Slate 900 *\/\n  --c-surf: #1e293b; \/* Slate 800 *\/\n  --c-surf-hi: #334155; \/* Slate 700 *\/\n  --c-txt: #f8fafc;  \/* Slate 50 *\/\n  --c-txt-mut: #94a3b8; \/* Slate 400 *\/\n  --c-bdr: rgba(255,255,255,0.08);\n  --safe-t: env(safe-area-inset-top, 0px);\n  --safe-b: env(safe-area-inset-bottom, 0px);\n  --nav-h: 64px;\n  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);\n}\n\n* { margin:0; padding:0; box-sizing:border-box; -webkit-tap-highlight-color:transparent; touch-action:manipulation; }\nhtml, body { height:100%; width:100%; overflow:hidden; background:var(--c-bg); color:var(--c-txt); font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif; user-select:none; }\nbody { position:fixed; inset:0; display:flex; flex-direction:column; }\n\n\/* --- Map & Layers --- *\/\n#map { position:absolute; inset:0; z-index:1; }\n.maplibregl-ctrl-attrib, .maplibregl-ctrl-logo { display:none !important; }\n\n\/* --- Header --- *\/\n.header { position:fixed; top:0; left:0; right:0; z-index:100; padding:calc(var(--safe-t) + 12px) 16px 12px; display:flex; align-items:center; gap:12px; pointer-events:none; background:linear-gradient(to bottom, rgba(15,23,42,0.9), transparent); }\n.brand { display:flex; align-items:center; gap:8px; background:rgba(30,41,59,0.85); backdrop-filter:blur(12px); border:1px solid var(--c-bdr); padding:8px 14px; border-radius:24px; pointer-events:auto; font-weight:800; letter-spacing:-0.02em; font-size:15px; box-shadow:0 4px 12px rgba(0,0,0,0.2); }\n.brand svg { width:18px; height:18px; color:var(--c-pri); }\n.h-actions { margin-left:auto; display:flex; gap:8px; pointer-events:auto; }\n.icon-btn { width:40px; height:40px; border-radius:50%; background:rgba(30,41,59,0.85); backdrop-filter:blur(12px); border:1px solid var(--c-bdr); display:flex; align-items:center; justify-content:center; color:var(--c-txt); cursor:pointer; transition:transform 0.15s var(--ease), background 0.2s; box-shadow:0 4px 12px rgba(0,0,0,0.15); }\n.icon-btn:active { transform:scale(0.92); }\n.icon-btn svg { width:20px; height:20px; }\n\n\/* --- HUD --- *\/\n.hud { position:fixed; left:16px; bottom:calc(var(--nav-h) + var(--safe-b) + 16px); z-index:90; background:rgba(30,41,59,0.9); backdrop-filter:blur(12px); border:1px solid var(--c-bdr); border-radius:12px; padding:10px 14px; display:flex; align-items:center; gap:10px; pointer-events:auto; box-shadow:0 8px 24px rgba(0,0,0,0.25); transition:opacity 0.3s; }\n.hud-dot { width:8px; height:8px; border-radius:50%; background:var(--c-pri); box-shadow:0 0 0 4px var(--c-pri-dim); animation:pulse 2s infinite; }\n@keyframes pulse { 0% { box-shadow:0 0 0 0 var(--c-pri-dim); } 70% { box-shadow:0 0 0 8px rgba(16,185,129,0); } 100% { box-shadow:0 0 0 0 rgba(16,185,129,0); } }\n.hud-info { display:flex; flex-direction:column; }\n.hud-label { font-size:9px; text-transform:uppercase; letter-spacing:0.08em; color:var(--c-txt-mut); font-weight:700; }\n.hud-val { font-size:12px; font-family:\"SF Mono\",Monaco,Consolas,monospace; font-weight:600; color:var(--c-pri); }\n\n\/* --- Bottom Nav --- *\/\n.nav { position:fixed; bottom:0; left:0; right:0; z-index:100; height:calc(var(--nav-h) + var(--safe-b)); padding-bottom:var(--safe-b); background:rgba(30,41,59,0.95); backdrop-filter:blur(16px); border-top:1px solid var(--c-bdr); display:flex; justify-content:space-around; align-items:flex-start; }\n.nav-item { flex:1; height:var(--nav-h); display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px; color:var(--c-txt-mut); cursor:pointer; position:relative; transition:color 0.2s; }\n.nav-item.active { color:var(--c-pri); }\n.nav-item.active::before { content:''; position:absolute; top:0; width:32px; height:2px; background:var(--c-pri); border-radius:0 0 2px 2px; }\n.nav-item svg { width:24px; height:24px; stroke-width:2; }\n.nav-item span { font-size:10px; font-weight:600; }\n.fab-container { position:relative; top:-24px; }\n.fab { width:56px; height:56px; border-radius:50%; background:linear-gradient(135deg, var(--c-pri), #059669); border:none; display:flex; align-items:center; justify-content:center; color:#fff; box-shadow:0 8px 24px rgba(16,185,129,0.4); cursor:pointer; transition:transform 0.2s var(--ease); }\n.fab:active { transform:scale(0.9) rotate(90deg); }\n.fab svg { width:28px; height:28px; stroke-width:2.5; }\n\n\/* --- Sheets & Modals --- *\/\n.sheet { position:fixed; left:0; right:0; bottom:0; z-index:95; background:var(--c-surf); border-radius:24px 24px 0 0; border-top:1px solid var(--c-bdr); transform:translateY(100%); transition:transform 0.35s var(--ease); max-height:75vh; display:flex; flex-direction:column; padding-bottom:calc(var(--nav-h) + var(--safe-b)); }\n.sheet.open { transform:translateY(0); }\n.sheet-handle { width:100%; padding:12px 0 8px; display:flex; justify-content:center; cursor:grab; }\n.sheet-handle::after { content:''; width:40px; height:4px; border-radius:2px; background:rgba(255,255,255,0.15); }\n.sheet-content { flex:1; overflow-y:auto; padding:0 16px 24px; -webkit-overflow-scrolling:touch; }\n.section-title { font-size:11px; text-transform:uppercase; letter-spacing:0.1em; color:var(--c-txt-mut); font-weight:700; margin:16px 0 12px; display:flex; align-items:center; justify-content:space-between; }\n\n\/* --- Cards --- *\/\n.card { display:flex; gap:14px; padding:14px; border-radius:16px; background:rgba(255,255,255,0.03); border:1px solid var(--c-bdr); margin-bottom:10px; cursor:pointer; transition:background 0.15s, transform 0.15s; position:relative; overflow:hidden; }\n.card:active { background:rgba(255,255,255,0.06); transform:scale(0.98); }\n.card-icon { width:48px; height:48px; border-radius:12px; background:rgba(255,255,255,0.05); display:flex; align-items:center; justify-content:center; font-size:22px; flex-shrink:0; }\n.card-body { flex:1; min-width:0; display:flex; flex-direction:column; justify-content:center; }\n.card-title { font-size:14px; font-weight:700; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; margin-bottom:2px; display:flex; align-items:center; gap:6px; }\n.card-meta { font-size:11px; color:var(--c-txt-mut); display:flex; align-items:center; gap:6px; }\n.badge { font-size:8px; font-weight:800; text-transform:uppercase; padding:2px 6px; border-radius:6px; letter-spacing:0.05em; }\n.badge.sell { background:var(--c-pri-dim); color:var(--c-pri); }\n.badge.buy { background:rgba(245,158,11,0.15); color:var(--c-sec); }\n.badge.share { background:rgba(236,72,153,0.15); color:var(--c-acc); }\n.card-price { font-size:15px; font-weight:800; color:var(--c-pri); margin-left:auto; align-self:center; }\n\n\/* --- Markers --- *\/\n.marker-wrap { position:relative; display:flex; flex-direction:column; align-items:center; cursor:pointer; transition:transform 0.2s var(--ease); }\n.marker-wrap:hover { transform:translateY(-4px); z-index:10 !important; }\n.marker-pin { width:36px; height:36px; border-radius:50% 50% 50% 0; background:var(--c-surf); border:2px solid var(--c-pri); transform:rotate(-45deg); display:flex; align-items:center; justify-content:center; box-shadow:0 4px 12px rgba(0,0,0,0.3); position:relative; }\n.marker-pin span { transform:rotate(45deg); font-size:16px; }\n.marker-stem { width:2px; height:12px; background:linear-gradient(to bottom, var(--c-pri), transparent); margin-top:-2px; opacity:0.8; }\n.marker-popup { position:absolute; bottom:calc(100% + 12px); left:50%; transform:translateX(-50%) scale(0.9); background:rgba(30,41,59,0.98); backdrop-filter:blur(12px); border:1px solid var(--c-bdr); border-radius:14px; padding:12px; width:200px; opacity:0; pointer-events:none; transition:all 0.2s var(--ease); z-index:20; box-shadow:0 12px 32px rgba(0,0,0,0.4); }\n.marker-popup.show { opacity:1; pointer-events:auto; transform:translateX(-50%) scale(1); }\n.marker-popup::after { content:''; position:absolute; top:100%; left:50%; transform:translateX(-50%); border:6px solid transparent; border-top-color:rgba(30,41,59,0.98); }\n\n\/* --- Fullscreen Screens --- *\/\n.screen { position:fixed; inset:0; z-index:200; background:var(--c-bg); display:flex; flex-direction:column; transform:translateX(100%); transition:transform 0.3s var(--ease); }\n.screen.active { transform:translateX(0); }\n.screen-header { padding:calc(var(--safe-t) + 12px) 16px 12px; border-bottom:1px solid var(--c-bdr); display:flex; align-items:center; gap:12px; background:var(--c-surf); }\n.screen-title { font-size:17px; font-weight:800; flex:1; }\n.screen-body { flex:1; overflow-y:auto; padding:16px; -webkit-overflow-scrolling:touch; }\n\n\/* --- Chat --- *\/\n.msg-row { display:flex; margin-bottom:12px; max-width:85%; animation:msgIn 0.25s var(--ease); }\n@keyframes msgIn { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:translateY(0); } }\n.msg-row.me { margin-left:auto; }\n.msg-bubble { padding:10px 14px; border-radius:16px; font-size:14px; line-height:1.45; position:relative; word-break:break-word; }\n.msg-row.them .msg-bubble { background:var(--c-surf-hi); border-bottom-left-radius:4px; }\n.msg-row.me .msg-bubble { background:var(--c-pri); color:#fff; border-bottom-right-radius:4px; }\n.msg-time { font-size:9px; opacity:0.6; margin-top:4px; text-align:right; }\n.chat-input-bar { padding:12px 16px calc(var(--safe-b) + 12px); border-top:1px solid var(--c-bdr); background:var(--c-surf); display:flex; gap:10px; align-items:center; }\n.chat-input { flex:1; background:rgba(255,255,255,0.06); border:1px solid var(--c-bdr); border-radius:24px; padding:12px 18px; color:var(--c-txt); font-size:15px; outline:none; transition:border-color 0.2s; }\n.chat-input:focus { border-color:var(--c-pri); }\n.send-btn { width:44px; height:44px; border-radius:50%; background:var(--c-pri); border:none; display:flex; align-items:center; justify-content:center; color:#fff; cursor:pointer; flex-shrink:0; transition:transform 0.15s; }\n.send-btn:active { transform:scale(0.9); }\n\n\/* --- Forms --- *\/\n.form-group { margin-bottom:18px; }\n.form-label { display:block; font-size:11px; text-transform:uppercase; letter-spacing:0.08em; color:var(--c-txt-mut); font-weight:700; margin-bottom:8px; }\n.form-input { width:100%; background:rgba(255,255,255,0.05); border:1px solid var(--c-bdr); border-radius:12px; padding:14px 16px; color:var(--c-txt); font-size:15px; outline:none; transition:border-color 0.2s; font-family:inherit; }\n.form-input:focus { border-color:var(--c-pri); }\ntextarea.form-input { resize:none; min-height:80px; }\n.chip-group { display:flex; flex-wrap:wrap; gap:8px; }\n.chip { padding:10px 16px; border-radius:10px; border:1px solid var(--c-bdr); background:rgba(255,255,255,0.03); font-size:13px; font-weight:600; color:var(--c-txt-mut); cursor:pointer; transition:all 0.15s; }\n.chip.active { background:var(--c-pri); border-color:var(--c-pri); color:#fff; }\n.btn-primary { width:100%; padding:16px; border-radius:14px; border:none; background:var(--c-pri); color:#fff; font-size:15px; font-weight:800; cursor:pointer; margin-top:8px; transition:transform 0.15s, opacity 0.2s; }\n.btn-primary:active { transform:scale(0.98); }\n.btn-primary:disabled { opacity:0.5; pointer-events:none; }\n\n\/* --- Toast --- *\/\n.toast { position:fixed; top:calc(var(--safe-t) + 16px); left:50%; transform:translateX(-50%) translateY(-20px); background:var(--c-surf-hi); border:1px solid var(--c-bdr); padding:10px 20px; border-radius:99px; font-size:13px; font-weight:600; z-index:9999; opacity:0; pointer-events:none; transition:all 0.25s var(--ease); box-shadow:0 8px 24px rgba(0,0,0,0.3); white-space:nowrap; }\n.toast.show { opacity:1; transform:translateX(-50%) translateY(0); }\n\n\/* --- Loader --- *\/\n.loader { position:fixed; inset:0; z-index:9999; background:var(--c-bg); display:flex; flex-direction:column; align-items:center; justify-content:center; gap:20px; transition:opacity 0.4s; }\n.loader.hide { opacity:0; pointer-events:none; }\n.spinner { width:44px; height:44px; border:3px solid var(--c-bdr); border-top-color:var(--c-pri); border-radius:50%; animation:spin 0.8s linear infinite; }\n@keyframes spin { to { transform:rotate(360deg); } }\n.loader-text { color:var(--c-txt-mut); font-size:14px; font-weight:600; }\n\n\/* --- Empty State --- *\/\n.empty { text-align:center; padding:48px 24px; color:var(--c-txt-mut); }\n.empty-icon { font-size:48px; margin-bottom:12px; opacity:0.5; }\n.empty-text { font-size:14px; line-height:1.5; }\n\n\/* --- Video Call --- *\/\n.call-screen { position:fixed; inset:0; z-index:300; background:#000; display:none; flex-direction:column; }\n.call-screen.active { display:flex; }\n.remote-video { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }\n.local-video { position:absolute; top:calc(var(--safe-t) + 16px); right:16px; width:100px; height:140px; border-radius:12px; border:2px solid rgba(255,255,255,0.2); object-fit:cover; z-index:2; }\n.call-overlay { position:absolute; bottom:0; left:0; right:0; padding:32px 24px calc(var(--safe-b) + 32px); display:flex; justify-content:center; gap:20px; background:linear-gradient(to top, rgba(0,0,0,0.8), transparent); z-index:3; }\n.call-btn { width:56px; height:56px; border-radius:50%; border:none; display:flex; align-items:center; justify-content:center; color:#fff; cursor:pointer; backdrop-filter:blur(8px); transition:transform 0.15s; }\n.call-btn:active { transform:scale(0.9); }\n.call-btn.end { background:#ef4444; }\n.call-btn.mute { background:rgba(255,255,255,0.2); }\n.call-info { position:absolute; top:calc(var(--safe-t) + 16px); left:16px; z-index:2; color:#fff; }\n.call-name { font-size:18px; font-weight:800; text-shadow:0 2px 8px rgba(0,0,0,0.5); }\n.call-status { font-size:12px; opacity:0.8; margin-top:2px; }\n.incoming-banner { position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); text-align:center; z-index:4; color:#fff; }\n.incoming-avatar { width:80px; height:80px; border-radius:50%; border:3px solid rgba(255,255,255,0.3); margin:0 auto 16px; display:flex; align-items:center; justify-content:center; font-size:28px; font-weight:800; background:rgba(255,255,255,0.1); backdrop-filter:blur(8px); }\n.incoming-actions { display:flex; gap:16px; justify-content:center; margin-top:24px; }\n.incoming-btn { width:64px; height:64px; border-radius:50%; border:none; display:flex; align-items:center; justify-content:center; color:#fff; cursor:pointer; font-size:24px; }\n.incoming-btn.accept { background:var(--c-pri); }\n.incoming-btn.decline { background:#ef4444; }\n<\/style>\n<\/head>\n<body>\n\n<!-- Loader -->\n<div class=\"loader\" id=\"loader\">\n  <div class=\"spinner\"><\/div>\n  <div class=\"loader-text\" id=\"loadTxt\">Initializing HexGrid...<\/div>\n<\/div>\n\n<!-- Toast -->\n<div class=\"toast\" id=\"toast\"><\/div>\n\n<!-- Map -->\n<div id=\"map\"><\/div>\n\n<!-- Header -->\n<div class=\"header\">\n  <div class=\"brand\">\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><path d=\"M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5\"\/><\/svg>\n    HexMarket\n  <\/div>\n  <div class=\"h-actions\">\n    <div class=\"icon-btn\" id=\"themeBtn\" aria-label=\"Toggle theme\">\n      <svg 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>\n    <\/div>\n    <div class=\"icon-btn\" id=\"locBtn\" aria-label=\"Recenter\">\n      <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>\n    <\/div>\n  <\/div>\n<\/div>\n\n<!-- HUD -->\n<div class=\"hud\" id=\"hud\">\n  <div class=\"hud-dot\" id=\"hudDot\"><\/div>\n  <div class=\"hud-info\">\n    <div class=\"hud-label\">Active Hex<\/div>\n    <div class=\"hud-val\" id=\"hudHex\">--------<\/div>\n  <\/div>\n<\/div>\n\n<!-- Bottom Sheet -->\n<div class=\"sheet\" id=\"sheet\">\n  <div class=\"sheet-handle\" id=\"sheetHandle\"><\/div>\n  <div class=\"sheet-content\" id=\"sheetContent\">\n    <div class=\"empty\">\n      <div class=\"empty-icon\">\u2b21<\/div>\n      <div class=\"empty-text\">Select a tab to explore<\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<!-- Bottom Nav -->\n<div class=\"nav\">\n  <div class=\"nav-item active\" data-tab=\"nearby\">\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"><polygon points=\"12 2 22 8.5 22 15.5 12 22 2 15.5 2 8.5 12 2\"\/><\/svg>\n    <span>Nearby<\/span>\n  <\/div>\n  <div class=\"nav-item\" data-tab=\"messages\">\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"><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>\n    <span>Chat<\/span>\n  <\/div>\n  <div class=\"fab-container\">\n    <button class=\"fab\" id=\"createBtn\" aria-label=\"Create Listing\">\n      <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"><path d=\"M12 5v14M5 12h14\"\/><\/svg>\n    <\/button>\n  <\/div>\n  <div class=\"nav-item\" data-tab=\"hex\">\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"><path d=\"M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5\"\/><\/svg>\n    <span>Grid<\/span>\n  <\/div>\n  <div class=\"nav-item\" data-tab=\"profile\">\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"><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>\n    <span>Me<\/span>\n  <\/div>\n<\/div>\n\n<!-- Create Screen -->\n<div class=\"screen\" id=\"createScreen\">\n  <div class=\"screen-header\">\n    <div class=\"icon-btn\" id=\"createBack\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M15 18l-6-6 6-6\"\/><\/svg><\/div>\n    <div class=\"screen-title\">New Listing<\/div>\n  <\/div>\n  <div class=\"screen-body\">\n    <div class=\"form-group\">\n      <label class=\"form-label\">Type<\/label>\n      <div class=\"chip-group\" id=\"typeChips\">\n        <div class=\"chip active\" data-v=\"sell\">\ud83d\udcb0 Sell<\/div>\n        <div class=\"chip\" data-v=\"buy\">\ud83d\uded2 Buy<\/div>\n        <div class=\"chip\" data-v=\"share\">\ud83c\udf81 Share<\/div>\n        <div class=\"chip\" data-v=\"biz\">\ud83c\udfea Biz<\/div>\n      <\/div>\n    <\/div>\n    <div class=\"form-group\">\n      <label class=\"form-label\">Title<\/label>\n      <input type=\"text\" class=\"form-input\" id=\"createTitle\" placeholder=\"What are you offering?\" maxlength=\"60\">\n    <\/div>\n    <div class=\"form-group\">\n      <label class=\"form-label\">Description<\/label>\n      <textarea class=\"form-input\" id=\"createDesc\" placeholder=\"Details, condition, etc.\" maxlength=\"300\"><\/textarea>\n    <\/div>\n    <div class=\"form-group\">\n      <label class=\"form-label\">Price ($)<\/label>\n      <input type=\"number\" class=\"form-input\" id=\"createPrice\" placeholder=\"0 for free\" min=\"0\" step=\"0.01\">\n    <\/div>\n    <div class=\"form-group\">\n      <label class=\"form-label\">Category<\/label>\n      <div class=\"chip-group\" id=\"catChips\">\n        <div class=\"chip active\" data-v=\"\ud83d\udecd\ufe0f\">Goods<\/div>\n        <div class=\"chip\" data-v=\"\ud83c\udf5c\">Food<\/div>\n        <div class=\"chip\" data-v=\"\ud83e\uddf0\">Service<\/div>\n        <div class=\"chip\" data-v=\"\ud83d\udeb2\">Transport<\/div>\n        <div class=\"chip\" data-v=\"\ud83c\udfa8\">Art<\/div>\n      <\/div>\n    <\/div>\n    <button class=\"btn-primary\" id=\"publishBtn\">Publish to HexGrid<\/button>\n  <\/div>\n<\/div>\n\n<!-- Chat Screen -->\n<div class=\"screen\" id=\"chatScreen\">\n  <div class=\"screen-header\">\n    <div class=\"icon-btn\" id=\"chatBack\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M15 18l-6-6 6-6\"\/><\/svg><\/div>\n    <div style=\"flex:1\">\n      <div class=\"screen-title\" id=\"chatName\">User<\/div>\n      <div style=\"font-size:10px;color:var(--c-txt-mut);font-family:monospace\" id=\"chatId\">---<\/div>\n    <\/div>\n    <div class=\"icon-btn\" id=\"callBtn\"><svg 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><\/div>\n  <\/div>\n  <div class=\"screen-body\" id=\"chatMessages\" style=\"display:flex;flex-direction:column;padding-bottom:80px\"><\/div>\n  <div class=\"chat-input-bar\">\n    <input type=\"text\" class=\"chat-input\" id=\"chatInput\" placeholder=\"Message...\" maxlength=\"500\">\n    <button class=\"send-btn\" id=\"sendBtn\"><svg width=\"20\" height=\"20\" 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>\n  <\/div>\n<\/div>\n\n<!-- Profile \/ Settings Screen -->\n<div class=\"screen\" id=\"profileScreen\">\n  <div class=\"screen-header\">\n    <div class=\"icon-btn\" id=\"profileBack\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M15 18l-6-6 6-6\"\/><\/svg><\/div>\n    <div class=\"screen-title\">Profile & Settings<\/div>\n  <\/div>\n  <div class=\"screen-body\">\n    <div class=\"form-group\">\n      <label class=\"form-label\">Display Name<\/label>\n      <input type=\"text\" class=\"form-input\" id=\"profName\" placeholder=\"Your name\" maxlength=\"24\">\n    <\/div>\n    <div class=\"form-group\">\n      <label class=\"form-label\">Hex Resolution<\/label>\n      <div style=\"display:flex;align-items:center;gap:12px\">\n        <input type=\"range\" id=\"resRange\" min=\"6\" max=\"11\" value=\"9\" style=\"flex:1;accent-color:var(--c-pri)\">\n        <span id=\"resVal\" style=\"font-size:13px;font-weight:700;color:var(--c-pri);min-width:24px\">9<\/span>\n      <\/div>\n      <div style=\"font-size:10px;color:var(--c-txt-mut);margin-top:6px\" id=\"resDesc\">~0.1 km\u00b2 per hex<\/div>\n    <\/div>\n    <div class=\"form-group\">\n      <label class=\"form-label\">Demo Mode<\/label>\n      <div class=\"chip-group\" id=\"demoToggle\">\n        <div class=\"chip active\" data-v=\"1\">On<\/div>\n        <div class=\"chip\" data-v=\"0\">Off<\/div>\n      <\/div>\n      <div style=\"font-size:10px;color:var(--c-txt-mut);margin-top:6px\">Generates mock listings & contacts nearby<\/div>\n    <\/div>\n    <button class=\"btn-primary\" id=\"saveProfBtn\">Save Changes<\/button>\n    <div style=\"margin-top:24px;padding-top:16px;border-top:1px solid var(--c-bdr);text-align:center\">\n      <div style=\"font-size:10px;color:var(--c-txt-mut);font-family:monospace\" id=\"profPeerId\">Peer ID: ---<\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<!-- Video Call Overlay -->\n<div class=\"call-screen\" id=\"callScreen\">\n  <video class=\"remote-video\" id=\"remoteVideo\" autoplay playsinline><\/video>\n  <video class=\"local-video\" id=\"localVideo\" autoplay playsinline muted><\/video>\n  <div class=\"call-info\">\n    <div class=\"call-name\" id=\"callName\">---<\/div>\n    <div class=\"call-status\" id=\"callStatus\">Connecting...<\/div>\n  <\/div>\n  <div class=\"incoming-banner\" id=\"incomingBanner\" style=\"display:none\">\n    <div class=\"incoming-avatar\" id=\"incAvatar\">?<\/div>\n    <div style=\"font-size:18px;font-weight:800;margin-bottom:4px\" id=\"incName\">---<\/div>\n    <div style=\"font-size:12px;opacity:0.7\">Incoming video call<\/div>\n    <div class=\"incoming-actions\">\n      <button class=\"incoming-btn decline\" id=\"incDecline\">\u2715<\/button>\n      <button class=\"incoming-btn accept\" id=\"incAccept\">\u2713<\/button>\n    <\/div>\n  <\/div>\n  <div class=\"call-overlay\" id=\"callControls\">\n    <button class=\"call-btn mute\" id=\"muteBtn\"><svg width=\"24\" height=\"24\" 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>\n    <button class=\"call-btn end\" id=\"endCallBtn\"><svg width=\"28\" height=\"28\" 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  <\/div>\n<\/div>\n\n<script>\n\/\/ ==================== UTILITIES ====================\nconst $ = s => document.querySelector(s);\nconst $$ = s => [...document.querySelectorAll(s)];\nconst uid = () => 'hx_' + Math.random().toString(36).slice(2, 10);\nconst esc = s => (s || '').replace(\/[&<>\"']\/g, m => ({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[m]));\nconst haversine = (la1, ln1, la2, ln2) => {\n  const R = 6371, dLa = (la2-la1)*Math.PI\/180, dLn = (ln2-ln1)*Math.PI\/180;\n  const a = Math.sin(dLa\/2)**2 + Math.cos(la1*Math.PI\/180)*Math.cos(la2*Math.PI\/180)*Math.sin(dLn\/2)**2;\n  return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));\n};\nconst timeAgo = ts => {\n  const s = (Date.now() - ts) \/ 1000;\n  if (s < 60) return 'now';\n  if (s < 3600) return ~~(s\/60) + 'm';\n  if (s < 86400) return ~~(s\/3600) + 'h';\n  return ~~(s\/86400) + 'd';\n};\nconst toast = (msg, dur = 2500) => {\n  const el = $('#toast');\n  el.textContent = msg;\n  el.classList.add('show');\n  clearTimeout(toast._t);\n  toast._t = setTimeout(() => el.classList.remove('show'), dur);\n};\nconst hap = () => navigator.vibrate?.(10);\n\n\/\/ ==================== INDEXED DB ====================\nlet _db = null;\nconst dbReq = req => 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('HexMarketDB', 2);\n    req.onupgradeneeded = e => {\n      const db = e.target.result;\n      if (!db.objectStoreNames.contains('listings')) {\n        const s = db.createObjectStore('listings', { keyPath: 'id' });\n        s.createIndex('hex', 'hex');\n        s.createIndex('mock', 'mock');\n      }\n      if (!db.objectStoreNames.contains('conversations')) db.createObjectStore('conversations', { keyPath: 'peerId' });\n      if (!db.objectStoreNames.contains('messages')) {\n        const s = db.createObjectStore('messages', { keyPath: 'id', autoIncrement: true });\n        s.createIndex('peerId', 'peerId');\n      }\n      if (!db.objectStoreNames.contains('settings')) db.createObjectStore('settings', { keyPath: 'key' });\n    };\n    req.onsuccess = e => { _db = e.target.result; resolve(_db); };\n    req.onerror = e => reject(e.target.error);\n  });\n}\nconst store = (name, mode = 'readonly') => _db.transaction(name, mode).objectStore(name);\nconst db = {\n  listings: {\n    put: obj => dbReq(store('listings', 'readwrite').put(obj)),\n    getByHex: hexes => Promise.all(hexes.map(h => dbReq(store('listings').index('hex').getAll(h)))).then(a => a.flat()),\n    countMock: () => dbReq(store('listings').index('mock').count(IDBKeyRange.only(1))),\n    deleteExpired: () => new Promise(res => {\n      const req = store('listings', 'readwrite').openCursor();\n      req.onsuccess = e => { const c = e.target.result; if (c) { if (c.value.expires && c.value.expires < Date.now()) c.delete(); c.continue(); } else res(); };\n    })\n  },\n  convos: {\n    get: id => dbReq(store('conversations').get(id)),\n    put: obj => dbReq(store('conversations', 'readwrite').put(obj)),\n    all: () => dbReq(store('conversations').getAll()),\n    delete: id => dbReq(store('conversations', 'readwrite').delete(id))\n  },\n  msgs: {\n    add: obj => dbReq(store('messages', 'readwrite').add(obj)),\n    getByPeer: pid => dbReq(store('messages').index('peerId').getAll(pid)).then(a => a.sort((x,y) => x.ts - y.ts)),\n    deleteByPeer: pid => new Promise(res => {\n      const req = store('messages', 'readwrite').index('peerId').openCursor(IDBKeyRange.only(pid));\n      req.onsuccess = e => { const c = e.target.result; if (c) { c.delete(); c.continue(); } else res(); };\n    })\n  },\n  settings: {\n    get: k => dbReq(store('settings').get(k)),\n    put: (k, v) => dbReq(store('settings', 'readwrite').put({ key: k, value: v }))\n  }\n};\n\n\/\/ ==================== STATE ====================\nconst RES_SIZES = { 6:'36km\u00b2', 7:'5.1km\u00b2', 8:'0.74km\u00b2', 9:'0.105km\u00b2', 10:'0.015km\u00b2', 11:'0.002km\u00b2' };\nconst DEFAULT_SETTINGS = { name: 'Explorer', res: 9, demo: true, theme: 'dark' };\nlet S = { ...DEFAULT_SETTINGS };\nlet map, selfMarker, lastPos = null, curHex = null, activeTab = 'nearby';\nlet peer = null, connections = {}, activeChatPeer = null, localStream = null, activeCall = null, pendingCall = null;\nlet markerCache = new Map();\nlet gpsStale = false, staleTimer = null;\nlet kalmanState = null;\n\n\/\/ ==================== KALMAN FILTER ====================\nfunction kalmanFilter(lat, lng, accuracy) {\n  accuracy = Math.max(accuracy || 10, 3);\n  if (!kalmanState) { kalmanState = { lat, lng, variance: accuracy * accuracy }; return { lat, lng }; }\n  kalmanState.variance += 0.25;\n  const k = kalmanState.variance \/ (kalmanState.variance + accuracy * accuracy);\n  kalmanState.lat += k * (lat - kalmanState.lat);\n  kalmanState.lng += k * (lng - kalmanState.lng);\n  kalmanState.variance *= (1 - k);\n  return { lat: kalmanState.lat, lng: kalmanState.lng };\n}\n\n\/\/ ==================== GEOLOCATION ====================\nasync function getLocation() {\n  $('#loadTxt').textContent = 'Acquiring position...';\n  return new Promise(resolve => {\n    if (!navigator.geolocation) return resolve(null);\n    navigator.geolocation.getCurrentPosition(\n      p => resolve({ lat: p.coords.latitude, lng: p.coords.longitude, acc: p.coords.accuracy }),\n      () => {\n        \/\/ Fallback to IP geolocation\n        fetch('https:\/\/ipapi.co\/json\/', { signal: AbortSignal.timeout(3000) })\n          .then(r => r.json()).then(d => d.latitude ? resolve({ lat: d.latitude, lng: d.longitude, acc: 5000 }) : resolve(null))\n          .catch(() => resolve(null));\n      },\n      { enableHighAccuracy: true, timeout: 10000, maximumAge: 0 }\n    );\n  });\n}\n\nfunction startWatch() {\n  if (!navigator.geolocation) return;\n  navigator.geolocation.watchPosition(\n    p => { markFresh(); updatePosition(p.coords.latitude, p.coords.longitude, p.coords.accuracy); },\n    () => markStale(),\n    { enableHighAccuracy: true, maximumAge: 2000, timeout: 10000 }\n  );\n}\n\nfunction markFresh() { gpsStale = false; $('#hudDot').style.background = 'var(--c-pri)'; $('#hudDot').style.animation = 'pulse 2s infinite'; clearTimeout(staleTimer); staleTimer = setTimeout(markStale, 15000); }\nfunction markStale() { gpsStale = true; $('#hudDot').style.background = 'var(--c-txt-mut)'; $('#hudDot').style.animation = 'none'; }\n\nfunction updatePosition(lat, lng, acc) {\n  const filtered = kalmanFilter(lat, lng, acc);\n  lastPos = filtered;\n  const newHex = h3.latLngToCell(filtered.lat, filtered.lng, S.res);\n  const changed = newHex !== curHex;\n  curHex = newHex;\n  selfMarker?.setLngLat([filtered.lng, filtered.lat]);\n  $('#hudHex').textContent = curHex.slice(-8);\n  if (changed) { renderHexLayers(); refreshListings(); }\n}\n\n\/\/ ==================== MAP ====================\nconst STYLES = {\n  dark: 'https:\/\/basemaps.cartocdn.com\/gl\/dark-matter-gl-style\/style.json',\n  light: 'https:\/\/basemaps.cartocdn.com\/gl\/positron-gl-style\/style.json'\n};\nconst ZOOM_LEVELS = { 6:8, 7:10, 8:12, 9:14, 10:16, 11:17 };\n\nfunction hexFeature(hex) {\n  const boundary = h3.cellToBoundary(hex, true);\n  boundary.push(boundary[0]);\n  return { type: 'Feature', geometry: { type: 'Polygon', coordinates: [boundary] }, properties: {} };\n}\nfunction emptyFC() { return { type: 'FeatureCollection', features: [] }; }\n\nfunction initMap(lat, lng) {\n  map = new maplibregl.Map({\n    container: 'map', style: STYLES[S.theme], center: [lng, lat], zoom: ZOOM_LEVELS[S.res] || 14,\n    attributionControl: false, fadeDuration: 150\n  });\n\n  const selfEl = document.createElement('div');\n  selfEl.style.cssText = 'width:16px;height:16px;border-radius:50%;background:var(--c-pri);border:3px solid #fff;box-shadow:0 0 0 4px var(--c-pri-dim),0 4px 12px rgba(0,0,0,0.3);';\n  selfMarker = new maplibregl.Marker({ element: selfEl, anchor: 'center' }).setLngLat([lng, lat]).addTo(map);\n\n  map.on('load', () => {\n    addHexLayers();\n    renderHexLayers();\n    refreshListings();\n    startWatch();\n    connectPeer();\n    $('#loader').classList.add('hide');\n    setTimeout(() => $('#loader')?.remove(), 400);\n  });\n\n  map.on('click', () => closeAllPopups());\n}\n\nfunction addHexLayers() {\n  ['hexFill', 'hexLine', 'hexRing'].forEach(id => { if (map.getLayer(id)) map.removeLayer(id); });\n  ['hexSrc', 'ringSrc'].forEach(id => { if (map.getSource(id)) map.removeSource(id); });\n\n  map.addSource('hexSrc', { type: 'geojson', data: emptyFC() });\n  map.addSource('ringSrc', { type: 'geojson', data: emptyFC() });\n\n  map.addLayer({ id: 'hexFill', type: 'fill', source: 'hexSrc', paint: { 'fill-color': '#10b981', 'fill-opacity': 0.12 } });\n  map.addLayer({ id: 'hexLine', type: 'line', source: 'hexSrc', paint: { 'line-color': '#10b981', 'line-width': 2 } });\n  map.addLayer({ id: 'hexRing', type: 'line', source: 'ringSrc', paint: { 'line-color': '#10b981', 'line-width': 1, 'line-dasharray': [3, 3], 'line-opacity': 0.5 } });\n}\n\nfunction renderHexLayers() {\n  if (!curHex || !map.isStyleLoaded()) return;\n  try {\n    map.getSource('hexSrc').setData({ type: 'FeatureCollection', features: [hexFeature(curHex)] });\n    const ring = h3.gridDisk(curHex, 2).filter(h => h !== curHex);\n    map.getSource('ringSrc').setData({ type: 'FeatureCollection', features: ring.map(hexFeature) });\n  } catch (e) {}\n}\n\n\/\/ ==================== LISTINGS ====================\nconst MOCK_USERS = [\n  { id: 'demo_sarah', name: 'Sarah', lat: 40.7135, lng: -74.0055 },\n  { id: 'demo_mike', name: 'Mike', lat: 40.712, lng: -74.007 },\n  { id: 'demo_emma', name: 'Emma', lat: 40.7145, lng: -74.0045 },\n  { id: 'demo_james', name: 'James', lat: 40.711, lng: -74.008 }\n];\nconst MOCK_ITEMS = [\n  { title: 'Vintage Camera', price: 45, cat: '\ud83d\udecd\ufe0f', type: 'sell', peer: 'demo_sarah' },\n  { title: 'Need Bike Pump', price: 0, cat: '\ud83d\udeb2', type: 'buy', peer: 'demo_mike' },\n  { title: 'Fresh Coffee', price: 5, cat: '\ud83c\udf5c', type: 'sell', peer: 'demo_james' },\n  { title: 'Art Studio Tour', price: 0, cat: '\ud83c\udfa8', type: 'share', peer: 'demo_emma' }\n];\n\nasync function seedMockData() {\n  if (!S.demo || !curHex) return;\n  const count = await db.listings.countMock();\n  if (count > 0) return;\n  const ring = h3.gridDisk(curHex, 2);\n  for (const item of MOCK_ITEMS) {\n    const hex = ring[Math.floor(Math.random() * ring.length)];\n    const [lat, lng] = h3.cellToLatLng(hex);\n    await db.listings.put({\n      id: uid(), hex, ...item, lat, lng,\n      desc: 'Demo listing for testing', mock: true,\n      ts: Date.now() - Math.random() * 3600000,\n      expires: Date.now() + 86400000\n    });\n  }\n}\n\nasync function refreshListings() {\n  if (!curHex) return;\n  await seedMockData();\n  const ring = h3.gridDisk(curHex, 2);\n  const all = await db.listings.getByHex(ring);\n  const now = Date.now();\n  const valid = all.filter(l => (!l.expires || l.expires > now) && (!l.mock || S.demo));\n  valid.sort((a, b) => b.ts - a.ts);\n  renderSheetList(valid);\n  renderMapMarkers(valid);\n}\n\nfunction renderSheetList(items) {\n  const container = $('#sheetContent');\n  if (activeTab !== 'nearby') return;\n  if (!items.length) {\n    container.innerHTML = '<div class=\"section-title\">Nearby Listings<\/div><div class=\"empty\"><div class=\"empty-icon\">\u2b21<\/div><div class=\"empty-text\">No listings in this hex.<br>Be the first to post!<\/div><\/div>';\n    return;\n  }\n  container.innerHTML = `<div class=\"section-title\"><span>Nearby<\/span><span style=\"color:var(--c-pri)\">${items.length}<\/span><\/div>` +\n    items.map(l => `\n      <div class=\"card\" data-id=\"${l.id}\">\n        <div class=\"card-icon\">${l.cat}<\/div>\n        <div class=\"card-body\">\n          <div class=\"card-title\">${esc(l.title)} <span class=\"badge ${l.type}\">${l.type}<\/span><\/div>\n          <div class=\"card-meta\">${esc(l.peer.replace('demo_',''))} \u00b7 ${timeAgo(l.ts)}<\/div>\n        <\/div>\n        <div class=\"card-price\">${l.price > 0 ? '$'+l.price : 'Free'}<\/div>\n      <\/div>\n    `).join('');\n  $$('.card', container).forEach(c => c.onclick = () => openListingDetail(c.dataset.id));\n}\n\nfunction closeAllPopups() { $$('.marker-popup.show').forEach(p => p.classList.remove('show')); }\n\nfunction renderMapMarkers(items) {\n  const seen = new Set();\n  items.slice(0, 80).forEach(l => {\n    seen.add(l.id);\n    let entry = markerCache.get(l.id);\n    if (!entry) {\n      const el = document.createElement('div');\n      el.className = 'marker-wrap';\n      el.innerHTML = `\n        <div class=\"marker-popup\">\n          <div style=\"font-weight:700;font-size:13px;margin-bottom:4px\">${esc(l.title)}<\/div>\n          <div style=\"font-size:11px;color:var(--c-txt-mut);margin-bottom:8px\">${esc(l.peer.replace('demo_',''))} \u00b7 ${l.price > 0 ? '$'+l.price : 'Free'}<\/div>\n          <div style=\"display:flex;gap:6px\">\n            <button style=\"flex:1;padding:8px;border-radius:8px;border:none;background:var(--c-pri);color:#fff;font-weight:700;font-size:11px;cursor:pointer\" data-action=\"view\">View<\/button>\n            <button style=\"flex:1;padding:8px;border-radius:8px;border:1px solid var(--c-bdr);background:transparent;color:var(--c-txt);font-weight:700;font-size:11px;cursor:pointer\" data-action=\"route\">Route<\/button>\n          <\/div>\n        <\/div>\n        <div class=\"marker-pin\"><span>${l.cat}<\/span><\/div>\n        <div class=\"marker-stem\"><\/div>\n      `;\n      const marker = new maplibregl.Marker({ element: el, anchor: 'bottom' }).setLngLat([l.lng, l.lat]).addTo(map);\n      entry = { marker, el, listing: l };\n      markerCache.set(l.id, entry);\n\n      el.querySelector('.marker-pin').onclick = e => { e.stopPropagation(); hap(); closeAllPopups(); el.querySelector('.marker-popup').classList.add('show'); };\n      el.querySelector('[data-action=\"view\"]').onclick = e => { e.stopPropagation(); closeAllPopups(); openListingDetail(l.id); };\n      el.querySelector('[data-action=\"route\"]').onclick = async e => {\n        e.stopPropagation(); closeAllPopups();\n        if (lastPos) {\n          toast('\ud83d\uddfa\ufe0f Routing...');\n          const dist = haversine(lastPos.lat, lastPos.lng, l.lat, l.lng);\n          toast(`\ud83d\udccd ~${dist.toFixed(2)}km away`);\n        }\n      };\n    } else {\n      entry.listing = l;\n      entry.marker.setLngLat([l.lng, l.lat]);\n    }\n  });\n  for (const [id, entry] of markerCache) {\n    if (!seen.has(id)) { entry.marker.remove(); markerCache.delete(id); }\n  }\n}\n\nasync function openListingDetail(id) {\n  const l = await db.listings.getByHex([curHex]).then(all => all.find(x => x.id === id));\n  if (!l) return;\n  \/\/ Reuse create screen as detail view (simplified)\n  toast(`${l.title} \u2014 ${l.price > 0 ? '$'+l.price : 'Free'}`);\n  openChatWith(l.peer, l.peer.replace('demo_', ''));\n}\n\n\/\/ ==================== CHAT & P2P ====================\nfunction connectPeer() {\n  try {\n    peer = new Peer(S.peerId || undefined, { debug: 0 });\n    peer.on('open', id => {\n      S.peerId = id;\n      db.settings.put('peerId', id);\n      $('#profPeerId').textContent = 'Peer ID: ' + id.slice(0, 16) + '...';\n    });\n    peer.on('connection', conn => {\n      connections[conn.peer] = conn;\n      conn.on('data', async data => {\n        if (data?.type === 'msg') {\n          await saveMessage(conn.peer, data.text, false);\n          if (activeChatPeer === conn.peer) appendMessage(data.text, false, Date.now());\n          else { toast(`\ud83d\udcac New message from ${data.name || conn.peer.slice(0,8)}`); updateUnread(); }\n          if (activeTab === 'messages') renderMessagesList();\n        }\n      });\n    });\n    peer.on('call', call => {\n      pendingCall = call;\n      $('#incName').textContent = call.peer.replace('demo_', '').slice(0, 12);\n      $('#incAvatar').textContent = (call.peer[0] || '?').toUpperCase();\n      $('#incomingBanner').style.display = '';\n      $('#callStatus').style.display = 'none';\n      $('#callControls').style.display = 'none';\n      $('#callScreen').classList.add('active');\n    });\n    peer.on('error', e => { console.error('Peer error:', e); toast('Connection error'); });\n    peer.on('disconnected', () => toast('Disconnected. Retrying...'));\n  } catch (e) { console.error('PeerJS init failed:', e); toast('P2P unavailable'); }\n}\n\nasync function saveMessage(peerId, text, isMe) {\n  await db.msgs.add({ peerId, text, me: isMe, ts: Date.now() });\n  let convo = await db.convos.get(peerId);\n  if (!convo) convo = { peerId, name: peerId.replace('demo_', ''), ts: Date.now(), lastMsg: text, unread: 0 };\n  else { convo.ts = Date.now(); convo.lastMsg = text; }\n  if (!isMe && activeChatPeer !== peerId) convo.unread = (convo.unread || 0) + 1;\n  await db.convos.put(convo);\n}\n\nasync function updateUnread() {\n  const convos = await db.convos.all();\n  const total = convos.reduce((sum, c) => sum + (c.unread || 0), 0);\n  \/\/ Could show badge on nav item\n}\n\nasync function openChatWith(peerId, name) {\n  hap();\n  activeChatPeer = peerId;\n  let convo = await db.convos.get(peerId);\n  if (!convo) { convo = { peerId, name, ts: Date.now(), lastMsg: '', unread: 0 }; await db.convos.put(convo); }\n  else if (convo.unread) { convo.unread = 0; await db.convos.put(convo); }\n  updateUnread();\n\n  $('#chatName').textContent = name || peerId.replace('demo_', '');\n  $('#chatId').textContent = peerId.slice(0, 16);\n  $('#chatMessages').innerHTML = '';\n\n  const msgs = await db.msgs.getByPeer(peerId);\n  msgs.forEach(m => appendMessage(m.text, m.me, m.ts));\n  if (!msgs.length) appendMessage(peerId.startsWith('demo_') ? '\ud83d\udc4b Demo contact \u2014 say hello!' : 'Start the conversation', null, null, true);\n\n  $('#chatScreen').classList.add('active');\n\n  \/\/ Establish P2P connection if not demo\n  if (!peerId.startsWith('demo_') && peer && !connections[peerId]) {\n    try {\n      const conn = peer.connect(peerId);\n      connections[peerId] = conn;\n      conn.on('data', async data => {\n        if (data?.type === 'msg') {\n          await saveMessage(peerId, data.text, false);\n          if (activeChatPeer === peerId) appendMessage(data.text, false, Date.now());\n        }\n      });\n    } catch (e) {}\n  }\n}\n\nfunction appendMessage(text, isMe, ts, isSystem = false) {\n  const row = document.createElement('div');\n  row.className = `msg-row ${isSystem ? '' : isMe ? 'me' : 'them'}`;\n  if (isSystem) {\n    row.style.cssText = 'align-self:center;max-width:90%;margin:12px 0';\n    row.innerHTML = `<div style=\"text-align:center;font-size:11px;color:var(--c-txt-mut);padding:6px 12px;background:rgba(255,255,255,0.04);border-radius:99px\">${esc(text)}<\/div>`;\n  } else {\n    row.innerHTML = `<div class=\"msg-bubble\">${esc(text)}${ts ? `<div class=\"msg-time\">${new Date(ts).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'})}<\/div>` : ''}<\/div>`;\n  }\n  $('#chatMessages').appendChild(row);\n  $('#chatMessages').scrollTop = $('#chatMessages').scrollHeight;\n}\n\nasync function sendMessage() {\n  const input = $('#chatInput');\n  const text = input.value.trim();\n  if (!text || !activeChatPeer) return;\n  hap();\n  input.value = '';\n  appendMessage(text, true, Date.now());\n  await saveMessage(activeChatPeer, text, true);\n\n  if (activeChatPeer.startsWith('demo_')) {\n    setTimeout(async () => {\n      const replies = ['Sounds great! \ud83d\udc4d', 'Sure thing!', 'Let me check...', 'Interesting! Tell me more.', '\ud83c\udf89 Awesome!'];\n      const reply = replies[Math.floor(Math.random() * replies.length)];\n      appendMessage(reply, false, Date.now());\n      await saveMessage(activeChatPeer, reply, false);\n    }, 500 + Math.random() * 800);\n  } else if (connections[activeChatPeer]) {\n    try { connections[activeChatPeer].send({ type: 'msg', text, name: S.name }); } catch (e) {}\n  }\n}\n\nasync function renderMessagesList() {\n  const container = $('#sheetContent');\n  if (activeTab !== 'messages') return;\n  const convos = (await db.convos.all()).sort((a, b) => b.ts - a.ts);\n  if (!convos.length) {\n    container.innerHTML = '<div class=\"section-title\">Messages<\/div><div class=\"empty\"><div class=\"empty-icon\">\ud83d\udcac<\/div><div class=\"empty-text\">No conversations yet.<br>Tap a listing to start chatting!<\/div><\/div>';\n    return;\n  }\n  container.innerHTML = '<div class=\"section-title\">Conversations<\/div>' + convos.map(c => `\n    <div class=\"card\" data-peer=\"${c.peerId}\" data-name=\"${esc(c.name || c.peerId.replace('demo_',''))}\">\n      <div class=\"card-icon\" style=\"font-size:18px;font-weight:800;color:var(--c-pri)\">${(c.name || '?')[0].toUpperCase()}<\/div>\n      <div class=\"card-body\">\n        <div class=\"card-title\">${esc(c.name || c.peerId.replace('demo_',''))} ${c.unread ? `<span class=\"badge sell\" style=\"margin-left:auto\">${c.unread}<\/span>` : ''}<\/div>\n        <div class=\"card-meta\">${esc(c.lastMsg || 'No messages')} \u00b7 ${timeAgo(c.ts)}<\/div>\n      <\/div>\n    <\/div>\n  `).join('');\n  $$('.card', container).forEach(c => c.onclick = () => openChatWith(c.dataset.peer, c.dataset.name));\n}\n\n\/\/ ==================== VIDEO CALL ====================\nasync function startCall() {\n  if (!activeChatPeer || activeChatPeer.startsWith('demo_')) { toast('Cannot call demo contacts'); return; }\n  try {\n    localStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'user' }, audio: true });\n    $('#localVideo').srcObject = localStream;\n    $('#callScreen').classList.add('active');\n    $('#callName').textContent = $('#chatName').textContent;\n    $('#callStatus').textContent = 'Calling...';\n    $('#callStatus').style.display = '';\n    $('#callControls').style.display = '';\n    $('#incomingBanner').style.display = 'none';\n    activeCall = peer.call(activeChatPeer, localStream);\n    activeCall.on('stream', s => { $('#remoteVideo').srcObject = s; $('#callStatus').textContent = 'Connected'; });\n    activeCall.on('close', endCall);\n  } catch (e) { toast('Camera\/microphone access required'); endCall(); }\n}\n\nasync function answerCall() {\n  if (!pendingCall) return;\n  hap();\n  try {\n    localStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'user' }, audio: true });\n    $('#localVideo').srcObject = localStream;\n    $('#incomingBanner').style.display = 'none';\n    $('#callStatus').style.display = '';\n    $('#callStatus').textContent = 'Connected';\n    $('#callControls').style.display = '';\n    pendingCall.answer(localStream);\n    activeCall = pendingCall;\n    activeCall.on('stream', s => { $('#remoteVideo').srcObject = s; });\n    activeCall.on('close', endCall);\n    pendingCall = null;\n  } catch (e) { toast('Camera\/microphone access required'); endCall(); }\n}\n\nfunction endCall() {\n  if (activeCall) { try { activeCall.close(); } catch(e){} activeCall = null; }\n  if (pendingCall) { try { pendingCall.close(); } catch(e){} pendingCall = null; }\n  if (localStream) { localStream.getTracks().forEach(t => t.stop()); localStream = null; }\n  $('#remoteVideo').srcObject = null;\n  $('#localVideo').srcObject = null;\n  $('#callScreen').classList.remove('active');\n}\n\n\/\/ ==================== UI BINDINGS ====================\nasync function loadSettings() {\n  await openDB();\n  await db.listings.deleteExpired();\n  const saved = await db.settings.get('settings');\n  if (saved) S = { ...S, ...saved.value };\n  const peerId = await db.settings.get('peerId');\n  if (peerId) S.peerId = peerId.value;\n  if (!localStorage.getItem('hx_id')) localStorage.setItem('hx_id', uid());\n}\n\nasync function saveSettings() {\n  await db.settings.put('settings', S);\n}\n\nfunction setupUI() {\n  \/\/ Theme toggle\n  $('#themeBtn').onclick = () => {\n    hap();\n    S.theme = S.theme === 'dark' ? 'light' : 'dark';\n    saveSettings();\n    map.setStyle(STYLES[S.theme]);\n    $('#themeBtn svg').innerHTML = S.theme === 'dark'\n      ? '<path d=\"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z\"\/>'\n      : '<circle cx=\"12\" cy=\"12\" r=\"4\"\/><path d=\"M12 2v2M12 20v2M4 12H2M22 12h-2\"\/>';\n  };\n\n  \/\/ Recenter\n  $('#locBtn').onclick = () => { hap(); if (lastPos) map.flyTo({ center: [lastPos.lng, lastPos.lat], zoom: ZOOM_LEVELS[S.res] || 14, duration: 500 }); };\n\n  \/\/ Tab navigation\n  $$('.nav-item').forEach(item => {\n    item.onclick = () => {\n      hap();\n      const tab = item.dataset.tab;\n      $$('.nav-item').forEach(n => n.classList.remove('active'));\n      item.classList.add('active');\n      activeTab = tab;\n\n      if (tab === 'profile') { openProfileScreen(); return; }\n      if (tab === 'hex') { toast(`Hex Res: ${S.res} (${RES_SIZES[S.res]})`); return; }\n\n      $('#sheet').classList.add('open');\n      if (tab === 'nearby') refreshListings();\n      else if (tab === 'messages') renderMessagesList();\n    };\n  });\n\n  \/\/ Sheet drag-to-close\n  (() => {\n    const handle = $('#sheetHandle');\n    const sheet = $('#sheet');\n    let startY = 0, dragging = false;\n    handle.addEventListener('touchstart', e => { dragging = true; startY = e.touches[0].clientY; sheet.style.transition = 'none'; }, { passive: true });\n    handle.addEventListener('touchmove', e => { if (dragging) sheet.style.transform = `translateY(${Math.max(0, e.touches[0].clientY - startY)}px)`; }, { passive: true });\n    handle.addEventListener('touchend', e => {\n      if (!dragging) return; dragging = false; sheet.style.transition = '';\n      const shouldClose = (e.changedTouches[0].clientY - startY) > 80;\n      sheet.style.transform = '';\n      if (shouldClose) sheet.classList.remove('open');\n    });\n    handle.onclick = () => { hap(); sheet.classList.toggle('open'); };\n  })();\n\n  \/\/ Create listing\n  let createType = 'sell', createCat = '\ud83d\udecd\ufe0f';\n  $('#createBtn').onclick = () => { hap(); $('#createScreen').classList.add('active'); };\n  $('#createBack').onclick = () => { hap(); $('#createScreen').classList.remove('active'); };\n  $('#typeChips').onclick = e => { const chip = e.target.closest('.chip'); if (!chip) return; hap(); $$('#typeChips .chip').forEach(c => c.classList.remove('active')); chip.classList.add('active'); createType = chip.dataset.v; };\n  $('#catChips').onclick = e => { const chip = e.target.closest('.chip'); if (!chip) return; hap(); $$('#catChips .chip').forEach(c => c.classList.remove('active')); chip.classList.add('active'); createCat = chip.dataset.v; };\n  $('#publishBtn').onclick = async () => {\n    hap();\n    const title = $('#createTitle').value.trim();\n    if (!title) { toast('Please add a title'); return; }\n    if (!curHex) { toast('Waiting for location...'); return; }\n    const price = Math.max(0, parseFloat($('#createPrice').value) || 0);\n    const [lat, lng] = h3.cellToLatLng(curHex);\n    await db.listings.put({\n      id: uid(), hex: curHex, type: createType, cat: createCat,\n      title, desc: $('#createDesc').value.trim(), price,\n      peer: S.peerId || localStorage.getItem('hx_id'),\n      lat, lng, mock: false, ts: Date.now(), expires: Date.now() + 86400000\n    });\n    $('#createTitle').value = ''; $('#createDesc').value = ''; $('#createPrice').value = '';\n    $('#createScreen').classList.remove('active');\n    toast('\u2705 Published to HexGrid!');\n    refreshListings();\n  };\n\n  \/\/ Chat\n  $('#chatBack').onclick = () => { hap(); $('#chatScreen').classList.remove('active'); activeChatPeer = null; if (activeTab === 'messages') renderMessagesList(); };\n  $('#sendBtn').onclick = sendMessage;\n  $('#chatInput').onkeydown = e => { if (e.key === 'Enter') sendMessage(); };\n  $('#callBtn').onclick = startCall;\n\n  \/\/ Video call controls\n  $('#muteBtn').onclick = () => {\n    if (!localStream) return; hap();\n    const track = localStream.getAudioTracks()[0];\n    if (track) { track.enabled = !track.enabled; toast(track.enabled ? 'Unmuted' : 'Muted'); }\n  };\n  $('#endCallBtn').onclick = endCall;\n  $('#incAccept').onclick = answerCall;\n  $('#incDecline').onclick = () => { hap(); pendingCall?.close(); pendingCall = null; endCall(); };\n\n  \/\/ Profile\n  $('#resRange').oninput = e => { $('#resVal').textContent = e.target.value; $('#resDesc').textContent = '~' + RES_SIZES[e.target.value]; };\n  $('#demoToggle').onclick = e => { const chip = e.target.closest('.chip'); if (!chip) return; hap(); $$('#demoToggle .chip').forEach(c => c.classList.remove('active')); chip.classList.add('active'); };\n  $('#saveProfBtn').onclick = async () => {\n    hap();\n    S.name = $('#profName').value.trim() || 'Explorer';\n    const newRes = parseInt($('#resRange').value);\n    const demoOn = $('#demoToggle .chip.active')?.dataset.v === '1';\n    const resChanged = newRes !== S.res;\n    S.res = newRes;\n    S.demo = demoOn;\n    await saveSettings();\n    $('#profileScreen').classList.remove('active');\n    toast('Settings saved');\n    if (resChanged && lastPos) {\n      curHex = h3.latLngToCell(lastPos.lat, lastPos.lng, S.res);\n      $('#hudHex').textContent = curHex.slice(-8);\n      map.setZoom(ZOOM_LEVELS[S.res] || 14);\n      renderHexLayers();\n      refreshListings();\n    }\n    if (S.demo) { await seedMockData(); refreshListings(); }\n  };\n}\n\nfunction openProfileScreen() {\n  $('#profName').value = S.name;\n  $('#resRange').value = S.res;\n  $('#resVal').textContent = S.res;\n  $('#resDesc').textContent = '~' + RES_SIZES[S.res];\n  $$('#demoToggle .chip').forEach(c => c.classList.toggle('active', c.dataset.v === (S.demo ? '1' : '0')));\n  $('#profPeerId').textContent = 'Peer ID: ' + (S.peerId ? S.peerId.slice(0, 16) + '...' : 'connecting...');\n  $('#profileScreen').classList.add('active');\n}\n$('#profileBack').onclick = () => { hap(); $('#profileScreen').classList.remove('active'); };\n\n\/\/ ==================== INIT ====================\nasync function init() {\n  await loadSettings();\n  setupUI();\n\n  let loc = await getLocation();\n  if (!loc) {\n    loc = { lat: 40.7128, lng: -74.006, acc: 5000 }; \/\/ NYC fallback\n    toast('Using default location', 3000);\n  }\n  lastPos = { lat: loc.lat, lng: loc.lng };\n  curHex = h3.latLngToCell(loc.lat, loc.lng, S.res);\n  $('#hudHex').textContent = curHex.slice(-8);\n\n  initMap(loc.lat, loc.lng);\n}\n\ninit();\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 Pro Initializing HexGrid&#8230; HexMarket Active Hex &#8212;&#8212;&#8211; \u2b21 Select a tab to explore Nearby Chat Grid Me New Listing Type \ud83d\udcb0 Sell \ud83d\uded2 Buy \ud83c\udf81 Share \ud83c\udfea Biz Title Description Price ($) Category Goods Food Service Transport Art Publish to HexGrid User &#8212; Profile &#038; Settings Display Name Hex Resolution 9 ~0.1 km\u00b2 per [&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-192","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"http:\/\/mana-corp.com\/index.php?rest_route=\/wp\/v2\/pages\/192","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=192"}],"version-history":[{"count":7,"href":"http:\/\/mana-corp.com\/index.php?rest_route=\/wp\/v2\/pages\/192\/revisions"}],"predecessor-version":[{"id":199,"href":"http:\/\/mana-corp.com\/index.php?rest_route=\/wp\/v2\/pages\/192\/revisions\/199"}],"wp:attachment":[{"href":"http:\/\/mana-corp.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=192"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}