{"id":1432,"date":"2026-03-05T18:50:00","date_gmt":"2026-03-05T21:50:00","guid":{"rendered":"https:\/\/urbsaluga.com.br\/?page_id=1432"},"modified":"2026-03-19T12:00:05","modified_gmt":"2026-03-19T15:00:05","slug":"urbanonav","status":"publish","type":"page","link":"https:\/\/urbsaluga.com.br\/index.php\/urbanonav\/","title":{"rendered":"UrbanoNav"},"content":{"rendered":"<div id=\"urbnavG_40a3d2ac81\" class=\"urbnav-gwrap\" style=\"position:relative;width:100%;height:80vh;border:1px solid #e5e7eb;border-radius:12px;overflow:hidden;background:#fff;\">\n  <div id=\"urbnavMap_40a3d2ac81\" style=\"width:100%;height:100%;\"><\/div>\n\n  <div class=\"urbnav-user-fixed\" aria-hidden=\"true\">\n    <div class=\"urbnav-user-fixed-dot\"><\/div>\n  <\/div>\n\n  <div class=\"urbnav-top-right-tools\">\n    <button class=\"urbnav-tool btn-toggle-ui on\" title=\"Ocultar filtros e par\u00e2metros\">\u2630<\/button>\n    <button class=\"urbnav-tool btn-layers\" title=\"Camadas\">\u2637<\/button>\n    <button class=\"urbnav-tool btn-maptype\" title=\"Tipo de mapa\">\ud83d\uddfa<\/button>\n    <button class=\"urbnav-tool btn-north\" title=\"Voltar para o Norte\">N<\/button>\n    <button class=\"urbnav-tool btn-full\" title=\"Tela cheia\">\u26f6<\/button>\n  <\/div>\n\n  <button class=\"urbnav-exit\" type=\"button\" aria-label=\"Encerrar navega\u00e7\u00e3o\" title=\"Encerrar navega\u00e7\u00e3o\">\u2715<\/button>\n\n  <div class=\"urbnav-ui\">\n    <div class=\"urbnav-row\">\n      <div class=\"urbnav-brand\">URBANO Navigator<\/div>\n      <div class=\"urbnav-badges\">\n        <span class=\"urbnav-badge urbnav-gps\">GPS: aguardando\u2026<\/span>\n        <span class=\"urbnav-badge urbnav-rt\">Rota: \u2014<\/span>\n        <span class=\"urbnav-badge urbnav-imv-count\">Im\u00f3veis: 0<\/span>\n      <\/div>\n    <\/div>\n\n    <div class=\"urbnav-row\" style=\"gap:8px;\">\n      <input class=\"urbnav-dest\" type=\"text\" placeholder=\"Digite destino (Shopping, pr\u00e9dio comercial, bairro, CEP, endere\u00e7o, casa)...\" autocomplete=\"off\" \/>\n      <button class=\"urbnav-btn primary urbnav-go\">Tra\u00e7ar rota<\/button>\n      <button class=\"urbnav-btn urbnav-start\" disabled>Iniciar<\/button>\n      <button class=\"urbnav-btn urbnav-clear\">Limpar<\/button>\n    <\/div>\n\n    <div class=\"urbnav-place-card\" style=\"display:none;\"><\/div>\n\n    <div class=\"urbnav-row urbnav-subrow\">\n      <div class=\"urbnav-controls\">\n        <label class=\"urbnav-toggle\"><input type=\"checkbox\" class=\"urbnav-follow\" checked \/><span>Seguir<\/span><\/label>\n        <label class=\"urbnav-toggle\"><input type=\"checkbox\" class=\"urbnav-show-imv\" checked \/><span>Im\u00f3veis na rota<\/span><\/label>\n        <label class=\"urbnav-toggle\"><input type=\"checkbox\" class=\"urbnav-show-nearby\" \/><span>Im\u00f3veis ao redor<\/span><\/label>\n      <\/div>\n\n      <div class=\"urbnav-sliders\">\n        <div class=\"urbnav-sli\">\n          <span>Raio<\/span>\n          <input type=\"range\" class=\"urbnav-radius\" min=\"150\" max=\"3000\" step=\"50\" value=\"500\">\n          <span class=\"urbnav-radius-val\">500m<\/span>\n        <\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"urbnav-floating-panel urbnav-layer-panel\" style=\"display:none;\">\n    <div class=\"urbnav-panel-title\">Camadas<\/div>\n    <label class=\"urbnav-toggle full\"><input type=\"checkbox\" class=\"urbnav-traffic\"><span>Tr\u00e2nsito<\/span><\/label>\n    <label class=\"urbnav-toggle full\"><input type=\"checkbox\" class=\"urbnav-transit\"><span>Transporte<\/span><\/label>\n    <label class=\"urbnav-toggle full\"><input type=\"checkbox\" class=\"urbnav-bike\"><span>Ciclovias<\/span><\/label>\n  <\/div>\n\n  <div class=\"urbnav-floating-panel urbnav-maptype-panel\" style=\"display:none;\">\n    <div class=\"urbnav-panel-title\">Tipo de mapa<\/div>\n    <button class=\"urbnav-mini-btn\" data-maptype=\"roadmap\">Mapa<\/button>\n    <button class=\"urbnav-mini-btn\" data-maptype=\"satellite\">Sat\u00e9lite<\/button>\n    <button class=\"urbnav-mini-btn\" data-maptype=\"hybrid\">H\u00edbrido<\/button>\n    <button class=\"urbnav-mini-btn\" data-maptype=\"terrain\">Terreno<\/button>\n  <\/div>\n\n  <div class=\"urbnav-nav-ui\">\n    <div class=\"urbnav-nav-top\">\n      <div class=\"urbnav-nav-step-left\">\n        <div class=\"urbnav-nav-arrow\">\u2191<\/div>\n      <\/div>\n      <div class=\"urbnav-nav-step-main\">\n        <div class=\"urbnav-nav-step-dist\">\u2014<\/div>\n        <div class=\"urbnav-nav-step-road\">Continue<\/div>\n        <div class=\"urbnav-nav-step-lanes\">\u2b05\ufe0e \u2191 \u279c<\/div>\n      <\/div>\n    <\/div>\n\n    <div class=\"urbnav-nav-bottom\">\n      <button class=\"urbnav-nav-close-left\" title=\"Sair\">\u2715<\/button>\n\n      <div class=\"urbnav-nav-bottom-main\">\n        <div class=\"urbnav-nav-eta-big\">\u2014<\/div>\n        <div class=\"urbnav-nav-bottom-meta\">\n          <span class=\"urbnav-nav-dist\">\u2014<\/span>\n          <span class=\"urbnav-nav-dot\">\u2022<\/span>\n          <span class=\"urbnav-nav-arrival\">\u2014<\/span>\n        <\/div>\n      <\/div>\n\n      <div class=\"urbnav-nav-bottom-actions\">\n        <button class=\"urbnav-nav-center\" title=\"Recentrar\">\u2316<\/button>\n        <button class=\"urbnav-nav-mute\" title=\"Som\">\ud83d\udd0a<\/button>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<style>\n  .gm-style img, .gm-style iframe, .gm-style canvas, .gm-style svg { max-width:none !important; max-height:none !important; }\n  .gm-style * { box-sizing: content-box !important; }\n\n  .urbnav-gwrap.pseudo-fs{\n    position:fixed !important;\n    inset:0 !important;\n    width:100vw !important;\n    height:100vh !important;\n    z-index:999999 !important;\n    border-radius:0 !important;\n    border:0 !important;\n  }\n  body.urbnav-lock{ overflow:hidden !important; }\n\n  .urbnav-user-fixed{\n    position:absolute;\n    left:50%;\n    top:50%;\n    transform:translate(-50%,-50%);\n    z-index:12;\n    pointer-events:none;\n    width:36px;\n    height:36px;\n    display:flex;\n    align-items:center;\n    justify-content:center;\n  }\n  .urbnav-user-fixed-dot{\n    width:24px;\n    height:24px;\n    border-radius:999px;\n    background:#2563eb;\n    border:5px solid #ffffff;\n    box-shadow:0 0 0 2px rgba(37,99,235,.30), 0 10px 24px rgba(0,0,0,.16);\n  }\n\n  .urbnav-top-right-tools{\n    position:absolute;\n    right:12px;\n    top:138px;\n    z-index:9999;\n    display:flex;\n    flex-direction:column;\n    gap:10px;\n    pointer-events:auto;\n  }\n\n  .urbnav-tool{\n    width:46px;\n    height:46px;\n    border-radius:999px;\n    border:1px solid rgba(0,0,0,.14);\n    background:rgba(255,255,255,.96);\n    box-shadow:0 10px 26px rgba(0,0,0,.18);\n    font-weight:900;\n    cursor:pointer;\n    font-size:20px;\n    line-height:46px;\n    text-align:center;\n    padding:0;\n    user-select:none;\n  }\n  .urbnav-tool.on{ background:#0f172a;color:#fff;border-color:#0f172a }\n\n  .urbnav-ui{\n    position:absolute;\n    top:12px;\n    left:12px;\n    right:12px;\n    z-index:10;\n    background:rgba(255,255,255,.96);\n    border:1px solid rgba(0,0,0,.08);\n    border-radius:14px;\n    padding:10px 12px;\n    box-shadow:0 14px 34px rgba(0,0,0,.14);\n    backdrop-filter:blur(6px);\n  }\n  .urbnav-gwrap.ui-collapsed .urbnav-ui{ display:none !important; }\n  .urbnav-gwrap.ui-collapsed .urbnav-top-right-tools{ top:12px; }\n\n  .urbnav-row{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}\n  .urbnav-brand{font-weight:900;letter-spacing:.2px;color:#0f172a}\n  .urbnav-badges{display:flex;gap:8px;flex-wrap:wrap}\n  .urbnav-badge{\n    font-size:12px;\n    font-weight:800;\n    color:#0f172a;\n    background:#e2e8f0;\n    border:1px solid rgba(0,0,0,.06);\n    padding:4px 8px;\n    border-radius:999px;\n  }\n\n  .urbnav-dest{\n    flex:1;\n    min-width:260px;\n    padding:10px 12px !important;\n    border-radius:12px !important;\n    border:1px solid rgba(0,0,0,.12) !important;\n    font-size:14px;\n    outline:none;\n    background:#fff !important;\n    color:#0f172a !important;\n    min-height:42px !important;\n  }\n  .urbnav-btn{\n    padding:10px 12px !important;\n    border-radius:12px !important;\n    border:1px solid rgba(0,0,0,.12) !important;\n    background:#fff !important;\n    color:#0f172a !important;\n    font-weight:800 !important;\n    cursor:pointer;\n    min-height:42px !important;\n  }\n  .urbnav-btn.primary{\n    background:#2563eb !important;\n    color:#fff !important;\n    border-color:#2563eb !important;\n  }\n  .urbnav-btn[disabled]{opacity:.55 !important;cursor:not-allowed !important}\n\n  .urbnav-place-card{\n    margin-top:8px;\n    background:rgba(15,23,42,.06);\n    border:1px solid rgba(0,0,0,.08);\n    border-radius:12px;\n    padding:10px 12px;\n    font:13px\/1.35 Arial;\n    color:#0f172a;\n  }\n  .urbnav-place-card .n{font-weight:900;font-size:14px;margin-bottom:2px}\n  .urbnav-place-card .a{color:#334155;font-weight:700}\n  .urbnav-place-card .t{margin-top:6px;font-weight:900;color:#2563eb}\n  .urbnav-place-card .meta{margin-top:6px;display:flex;gap:8px;flex-wrap:wrap}\n  .urbnav-place-card .meta span{\n    display:inline-flex;\n    align-items:center;\n    gap:4px;\n    padding:4px 8px;\n    border-radius:999px;\n    background:#fff;\n    border:1px solid rgba(0,0,0,.08);\n    font-weight:800;\n    font-size:11px;\n    color:#0f172a;\n  }\n  .urbnav-place-card .meta a{ color:#0f172a; text-decoration:none; }\n  .urbnav-place-photo{\n    width:100%;\n    max-width:300px;\n    border-radius:10px;\n    display:block;\n    margin:0 0 8px 0;\n    border:1px solid rgba(0,0,0,.10);\n  }\n\n  .urbnav-subrow{margin-top:6px;padding-top:8px;border-top:1px dashed rgba(0,0,0,.12)}\n  .urbnav-controls{display:flex;gap:12px;flex-wrap:wrap}\n  .urbnav-toggle{display:flex;align-items:center;gap:6px;font-weight:800;font-size:12px;color:#0f172a}\n  .urbnav-toggle.full{ width:100%; margin:6px 0; }\n  .urbnav-sliders{display:flex;gap:10px;flex-wrap:wrap}\n  .urbnav-sli{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:800;color:#0f172a}\n  .urbnav-sli input[type=\"range\"]{width:160px}\n\n  .urbnav-floating-panel{\n    position:absolute;\n    right:70px;\n    top:140px;\n    z-index:9998;\n    width:210px;\n    background:rgba(255,255,255,.98);\n    border:1px solid rgba(0,0,0,.10);\n    border-radius:14px;\n    padding:12px;\n    box-shadow:0 14px 34px rgba(0,0,0,.16);\n  }\n  .urbnav-panel-title{\n    font-size:12px;\n    font-weight:900;\n    color:#0f172a;\n    margin-bottom:8px;\n    text-transform:uppercase;\n    letter-spacing:.5px;\n  }\n  .urbnav-mini-btn{\n    display:block;\n    width:100%;\n    margin:6px 0;\n    padding:10px 12px;\n    border-radius:10px;\n    border:1px solid rgba(0,0,0,.12);\n    background:#fff;\n    color:#0f172a;\n    font-weight:800;\n    cursor:pointer;\n    text-align:left;\n  }\n  .urbnav-mini-btn.active{\n    background:#0f172a;\n    color:#fff;\n    border-color:#0f172a;\n  }\n\n  .urbnav-exit{\n    position:absolute;\n    top:12px;\n    right:12px;\n    z-index:20;\n    width:42px;\n    height:42px;\n    border-radius:12px;\n    border:1px solid rgba(0,0,0,.12);\n    background:rgba(255,255,255,.92);\n    box-shadow:0 10px 26px rgba(0,0,0,.18);\n    font-weight:900;\n    font-size:20px;\n    line-height:42px;\n    text-align:center;\n    cursor:pointer;\n    display:none;\n  }\n\n  .urbnav-nav-ui{display:none}\n  .urbnav-gwrap.navmode .urbnav-ui{display:none !important}\n  .urbnav-gwrap.navmode .urbnav-exit{display:block}\n  .urbnav-gwrap.navmode .urbnav-nav-ui{display:block}\n  .urbnav-gwrap.navmode .urbnav-top-right-tools{top:72px}\n  .urbnav-gwrap.navmode .urbnav-floating-panel{top:74px}\n\n  .urbnav-nav-top{\n    position:absolute;\n    left:14px;\n    top:14px;\n    z-index:20;\n    width:min(420px, calc(100% - 90px));\n    min-height:92px;\n    background:linear-gradient(180deg,#0f7f59 0%, #116f4f 100%);\n    color:#fff;\n    border-radius:18px;\n    box-shadow:0 14px 34px rgba(0,0,0,.22);\n    display:flex;\n    align-items:flex-start;\n    overflow:hidden;\n  }\n  .urbnav-nav-step-left{\n    width:72px;\n    display:flex;\n    align-items:center;\n    justify-content:center;\n    padding-top:16px;\n    flex:0 0 72px;\n  }\n  .urbnav-nav-arrow{\n    font-size:34px;\n    font-weight:900;\n    line-height:1;\n  }\n  .urbnav-nav-step-main{\n    flex:1;\n    padding:12px 14px 10px 0;\n  }\n  .urbnav-nav-step-dist{\n    font-size:15px;\n    font-weight:800;\n    opacity:.95;\n    margin-bottom:2px;\n  }\n  .urbnav-nav-step-road{\n    font-size:26px;\n    font-weight:900;\n    line-height:1.05;\n    letter-spacing:.1px;\n  }\n  .urbnav-nav-step-lanes{\n    margin-top:10px;\n    padding-top:8px;\n    border-top:1px solid rgba(255,255,255,.18);\n    font-size:22px;\n    letter-spacing:8px;\n    opacity:.95;\n  }\n\n  .urbnav-nav-bottom{\n    position:absolute;\n    left:14px;\n    right:14px;\n    bottom:14px;\n    z-index:20;\n    min-height:80px;\n    background:rgba(255,255,255,.96);\n    border-radius:24px;\n    padding:12px 14px;\n    box-shadow:0 14px 34px rgba(0,0,0,.20);\n    display:flex;\n    align-items:center;\n    justify-content:space-between;\n    gap:12px;\n  }\n  .urbnav-nav-close-left{\n    width:50px;\n    height:50px;\n    border-radius:999px;\n    border:1px solid rgba(0,0,0,.14);\n    background:#fff;\n    color:#334155;\n    font-size:28px;\n    line-height:1;\n    cursor:pointer;\n    flex:0 0 50px;\n  }\n  .urbnav-nav-bottom-main{\n    flex:1;\n    min-width:0;\n    text-align:center;\n  }\n  .urbnav-nav-eta-big{\n    font-size:36px;\n    line-height:1;\n    font-weight:900;\n    color:#8b2e2e;\n    letter-spacing:-.4px;\n  }\n  .urbnav-nav-bottom-meta{\n    margin-top:6px;\n    display:flex;\n    align-items:center;\n    justify-content:center;\n    gap:10px;\n    color:#475569;\n    font-size:16px;\n    font-weight:800;\n  }\n  .urbnav-nav-dot{ opacity:.55; }\n  .urbnav-nav-bottom-actions{\n    display:flex;\n    align-items:center;\n    gap:8px;\n    flex:0 0 auto;\n  }\n  .urbnav-nav-center,.urbnav-nav-mute{\n    width:48px;height:48px;border-radius:999px;border:1px solid rgba(0,0,0,.12);\n    background:#fff;font-weight:900;cursor:pointer;font-size:20px;\n  }\n\n  .urbnav-card{\n    font:14px\/1.32 -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Arial,sans-serif;\n    width:300px;\n    max-width:300px;\n    min-width:300px;\n    position:relative;\n    overflow:visible;\n    padding:0;\n  }\n  .urbnav-card .imgwrap{\n    width:100%;\n    height:118px;\n    overflow:hidden;\n    border-radius:10px;\n    margin-bottom:8px;\n    border:1px solid rgba(0,0,0,.12);\n  }\n  .urbnav-card .imgwrap img{\n    width:100%;\n    height:100%;\n    object-fit:cover;\n    display:block;\n    transition:opacity .45s ease;\n  }\n  .urbnav-card .t{\n    font-weight:900;\n    font-size:15px;\n    margin:4px 0 6px;\n    color:#0f172a;\n    line-height:1.18;\n  }\n  .urbnav-card .row{\n    display:flex;\n    gap:8px;\n    margin:3px 0;\n    align-items:flex-start;\n  }\n  .urbnav-card .k{\n    min-width:74px;\n    font-weight:900;\n    font-size:10px;\n    text-transform:uppercase;\n    color:#475569;\n    letter-spacing:.35px;\n    padding-top:2px;\n  }\n  .urbnav-card .v{\n    font-weight:800;\n    color:#111827;\n    font-size:12px;\n    line-height:1.3;\n    word-break:break-word;\n  }\n  .urbnav-card-actions{\n    margin-top:10px;\n    display:flex;\n    justify-content:flex-end;\n    gap:8px;\n    align-items:center;\n  }\n  .urbnav-card-route,.urbnav-card-street{\n    width:42px;\n    height:42px;\n    border-radius:999px;\n    border:0;\n    cursor:pointer;\n    display:flex;\n    align-items:center;\n    justify-content:center;\n    font-size:20px;\n    font-weight:900;\n  }\n  .urbnav-card-route{\n    background:#2563eb;\n    color:#fff;\n    box-shadow:0 10px 22px rgba(37,99,235,.35);\n  }\n  .urbnav-card-street{\n    background:#fff;\n    color:#0f172a;\n    border:1px solid rgba(0,0,0,.12);\n  }\n\n  .urbnav-imv{\n    position:relative;\n    transform:translate(-50%,-100%);\n    pointer-events:auto;\n    user-select:none;\n  }\n  .urbnav-imv .pin{\n    position:absolute;\n    left:50%;\n    bottom:-2px;\n    width:14px;\n    height:14px;\n    transform:translateX(-50%) rotate(45deg);\n    background:hsl(20,90%,45%);\n    border-radius:3px;\n  }\n  .urbnav-imv .circle{\n    position:absolute;\n    left:50%;\n    top:0;\n    transform:translateX(-50%);\n    border-radius:999px;\n    border:3px solid hsl(20,90%,45%);\n    background:#fff;\n    overflow:hidden;\n    box-shadow:0 10px 26px rgba(0,0,0,.18);\n  }\n  .urbnav-imv .circle img{\n    width:100%;\n    height:100%;\n    object-fit:cover;\n    display:block;\n  }\n  .urbnav-imv .lbl{\n    position:absolute;\n    left:50%;\n    transform:translateX(-50%);\n    font-weight:900;\n    font-size:12px;\n    color:#0f172a;\n    background:rgba(255,255,255,.94);\n    border:1px solid rgba(0,0,0,.12);\n    border-radius:999px;\n    padding:2px 8px;\n    white-space:nowrap;\n    box-shadow:0 8px 18px rgba(0,0,0,.12);\n  }\n\n  @media (max-width: 768px){\n    .urbnav-ui{\n      left:8px;\n      right:8px;\n      top:8px;\n      padding:9px 10px;\n    }\n    .urbnav-top-right-tools{\n      right:8px;\n      top:142px;\n    }\n    .urbnav-gwrap.ui-collapsed .urbnav-top-right-tools{ top:8px; }\n    .urbnav-user-fixed{ width:30px; height:30px; }\n    .urbnav-user-fixed-dot{\n      width:20px;\n      height:20px;\n      border-width:4px;\n    }\n    .urbnav-floating-panel{\n      right:62px;\n      width:190px;\n      top:144px;\n    }\n    .urbnav-nav-top{\n      width:calc(100% - 86px);\n      min-height:84px;\n    }\n    .urbnav-nav-step-road{\n      font-size:22px;\n    }\n    .urbnav-nav-step-lanes{\n      font-size:18px;\n      letter-spacing:6px;\n    }\n    .urbnav-nav-eta-big{\n      font-size:30px;\n    }\n    .urbnav-card{\n      width:286px;\n      max-width:286px;\n      min-width:286px;\n    }\n    .urbnav-card .imgwrap{\n      height:108px;\n    }\n  }\n<\/style>\n\n<script>\n(function(){\n  const WRAP_ID   = \"urbnavG_40a3d2ac81\";\n  const MAP_DIV   = \"urbnavMap_40a3d2ac81\";\n  const AJAX_BASE = \"https:\\\/\\\/urbsaluga.com.br\\\/wp-admin\\\/admin-ajax.php\";\n  const NONCE     = \"4aec6dd6c3\";\n  const API_KEY   = \"AIzaSyCqoB9OUYYLskDpgIRZe8pz5-i5sDvUYCM\";\n  const MAP_ID    = \"2e5408e5f8b149a78f238f64\";\n  const EFFECTIVE_MAP_ID = MAP_ID || 'DEMO_MAP_ID';\n\n  if (!API_KEY) {\n    const w = document.getElementById(WRAP_ID);\n    if (w) w.innerHTML = '<div style=\"padding:16px;font:14px Arial;color:#111\"><b>Faltou a API KEY.<\/b><\/div>';\n    return;\n  }\n\n  if (!window.__urbnavGmapsLoader) {\n    window.__urbnavGmapsLoader = { loading: false, loaded: false, queue: [] };\n  }\n\n  function loadGoogleMapsOnce(initFn){\n    const loader = window.__urbnavGmapsLoader;\n    if (loader.loaded && window.google && google.maps) {\n      initFn();\n      return;\n    }\n\n    loader.queue.push(initFn);\n    if (loader.loading) return;\n    loader.loading = true;\n\n    const cb = '__urbnavGlobalInit';\n    window[cb] = function(){\n      loader.loaded = true;\n      loader.loading = false;\n      const q = loader.queue.slice();\n      loader.queue = [];\n      q.forEach(fn => { try { fn(); } catch(e){} });\n    };\n\n    if (!document.querySelector('script[data-urbnav-gmaps=\"1\"]')) {\n      const s = document.createElement('script');\n      s.async = true;\n      s.defer = true;\n      s.dataset.urbnavGmaps = \"1\";\n      s.src = 'https:\/\/maps.googleapis.com\/maps\/api\/js'\n        + '?key=' + encodeURIComponent(API_KEY)\n        + '&v=weekly'\n        + '&loading=async'\n        + '&libraries=geometry,places'\n        + '&callback=' + encodeURIComponent(cb);\n      document.head.appendChild(s);\n    } else if (window.google && google.maps) {\n      window[cb]();\n    }\n  }\n\n  loadGoogleMapsOnce(init);\n\n  async function init(){\n    const wrap = document.getElementById(WRAP_ID);\n    const mapEl= document.getElementById(MAP_DIV);\n    if (!wrap || !mapEl || !window.google || !google.maps) return;\n    if (wrap.dataset.urbnavInit === '1') return;\n    wrap.dataset.urbnavInit = '1';\n\n    const elGPS  = wrap.querySelector('.urbnav-gps');\n    const elRT   = wrap.querySelector('.urbnav-rt');\n    const elIMV  = wrap.querySelector('.urbnav-imv-count');\n    const elDest = wrap.querySelector('.urbnav-dest');\n    const btnGo  = wrap.querySelector('.urbnav-go');\n    const btnStart = wrap.querySelector('.urbnav-start');\n    const btnClr = wrap.querySelector('.urbnav-clear');\n    const btnExit= wrap.querySelector('.urbnav-exit');\n    const btnFS  = wrap.querySelector('.btn-full');\n    const btnToggleUI = wrap.querySelector('.btn-toggle-ui');\n    const btnLayers   = wrap.querySelector('.btn-layers');\n    const btnMapType  = wrap.querySelector('.btn-maptype');\n    const btnNorth    = wrap.querySelector('.btn-north');\n    const placeCard = wrap.querySelector('.urbnav-place-card');\n\n    const layerPanel = wrap.querySelector('.urbnav-layer-panel');\n    const mapTypePanel = wrap.querySelector('.urbnav-maptype-panel');\n\n    const chkFollow  = wrap.querySelector('.urbnav-follow');\n    const chkShowImv = wrap.querySelector('.urbnav-show-imv');\n    const chkShowNear= wrap.querySelector('.urbnav-show-nearby');\n    const rngRadius  = wrap.querySelector('.urbnav-radius');\n    const lblRadius  = wrap.querySelector('.urbnav-radius-val');\n\n    const chkTraffic = wrap.querySelector('.urbnav-traffic');\n    const chkTransit = wrap.querySelector('.urbnav-transit');\n    const chkBike    = wrap.querySelector('.urbnav-bike');\n    const mapTypeBtns = [...wrap.querySelectorAll('.urbnav-mini-btn[data-maptype]')];\n\n    const navBottomClose = wrap.querySelector('.urbnav-nav-close-left');\n    const navCenter  = wrap.querySelector('.urbnav-nav-center');\n    const navDist    = wrap.querySelector('.urbnav-nav-dist');\n    const navEtaBig  = wrap.querySelector('.urbnav-nav-eta-big');\n    const navArrival = wrap.querySelector('.urbnav-nav-arrival');\n    const navMute    = wrap.querySelector('.urbnav-nav-mute');\n\n    const navStepArrow = wrap.querySelector('.urbnav-nav-arrow');\n    const navStepDist  = wrap.querySelector('.urbnav-nav-step-dist');\n    const navStepRoad  = wrap.querySelector('.urbnav-nav-step-road');\n    const navStepLanes = wrap.querySelector('.urbnav-nav-step-lanes');\n\n    const IS_MOBILE = window.matchMedia('(max-width: 768px)').matches || ('ontouchstart' in window);\n\n    lblRadius.textContent = (rngRadius.value||'500') + 'm';\n    rngRadius.addEventListener('input', ()=> lblRadius.textContent = rngRadius.value + 'm');\n\n    const esc = (s)=> String(s ?? '').replace(\/&\/g,'&amp;').replace(\/<\/g,'&lt;').replace(\/>\/g,'&gt;').replace(\/\"\/g,'&quot;').replace(\/'\/g,'&#039;');\n    const nl2brEsc = (t)=> esc(t).replace(\/\\r?\\n\/g,'<br>');\n    const toBRL = (v)=>{\n      if (v === null || v === undefined || v === '') return '-';\n      let s = String(v).trim();\n      if (s.includes(',') && s.includes('.')) s = s.replace(\/\\.\/g, '').replace(',', '.');\n      else if (s.includes(',')) s = s.replace(',', '.');\n      const n = Number(s);\n      if (!isFinite(n)) return String(v);\n      return 'R$ ' + n.toLocaleString('pt-BR', { minimumFractionDigits: 0, maximumFractionDigits: 2 });\n    };\n    const fmtDist = (m)=> (m>=1000 ? (m\/1000).toFixed(1).replace('.',',')+' km' : Math.round(m)+' m');\n    const fmtEta = (sec)=>{\n      if(!isFinite(sec)||sec<=0) return '\u2014';\n      const min=Math.round(sec\/60);\n      if(min<60) return min+'min';\n      const h=Math.floor(min\/60), mm=min%60;\n      return h+'h '+mm+'m';\n    };\n    const secFromDurationString = (s)=>{\n      if (!s || typeof s !== 'string') return null;\n      const n = parseInt(s.replace('s',''), 10);\n      return Number.isFinite(n) ? n : null;\n    };\n    const norm=(d)=>{ d=Number(d); if(!isFinite(d)) return 0; d%=360; if(d<0)d+=360; return d; };\n    const nowClock = ()=>{\n      const d = new Date();\n      return d.toLocaleTimeString('pt-BR', { hour:'2-digit', minute:'2-digit' });\n    };\n\n    let AdvancedMarkerElement = null;\n    let PinElement = null;\n    try {\n      const markerLib = await google.maps.importLibrary('marker');\n      AdvancedMarkerElement = markerLib.AdvancedMarkerElement || null;\n      PinElement = markerLib.PinElement || null;\n    } catch(e) {}\n\n    const map = new google.maps.Map(mapEl, {\n      center: {lat:-16.701729031557978, lng:-49.27955304406354},\n      zoom: 16,\n      mapTypeControl: false,\n      fullscreenControl: false,\n      streetViewControl: false,\n      rotateControl: true,\n      zoomControl: true,\n      zoomControlOptions: { position: google.maps.ControlPosition.RIGHT_CENTER },\n      gestureHandling: 'greedy',\n      draggable: true,\n      scrollwheel: true,\n      disableDoubleClickZoom: false,\n      clickableIcons: false,\n      keyboardShortcuts: true,\n      heading: 0,\n      tilt: 0,\n      mapId: EFFECTIVE_MAP_ID,\n      mapTypeId: 'roadmap',\n      isFractionalZoomEnabled: true\n    });\n    map.setOptions({ draggableCursor: 'grab', draggingCursor: 'grabbing' });\n\n    const placesSvc = new google.maps.places.PlacesService(map);\n    const geocoder  = new google.maps.Geocoder();\n    const streetView = map.getStreetView();\n    const trafficLayer = new google.maps.TrafficLayer();\n    const transitLayer = new google.maps.TransitLayer();\n    const bikeLayer    = new google.maps.BicyclingLayer();\n\n    function updateUIToggleButton(){\n      const collapsed = wrap.classList.contains('ui-collapsed');\n      btnToggleUI.classList.toggle('on', !collapsed);\n      btnToggleUI.textContent = collapsed ? '\u2630' : '\u2715';\n      btnToggleUI.title = collapsed ? 'Reexibir filtros e par\u00e2metros' : 'Ocultar filtros e par\u00e2metros';\n    }\n\n    function hidePanels(){\n      layerPanel.style.display = 'none';\n      mapTypePanel.style.display = 'none';\n      btnLayers.classList.remove('on');\n      btnMapType.classList.remove('on');\n    }\n\n    function setLayer(layer, enabled){\n      layer.setMap(enabled ? map : null);\n    }\n\n    chkTraffic.addEventListener('change', ()=> setLayer(trafficLayer, chkTraffic.checked));\n    chkTransit.addEventListener('change', ()=> setLayer(transitLayer, chkTransit.checked));\n    chkBike.addEventListener('change', ()=> setLayer(bikeLayer, chkBike.checked));\n\n    mapTypeBtns.forEach(btn=>{\n      btn.addEventListener('click', ()=>{\n        mapTypeBtns.forEach(b=>b.classList.remove('active'));\n        btn.classList.add('active');\n        map.setMapTypeId(btn.dataset.maptype);\n      });\n    });\n    mapTypeBtns[0]?.classList.add('active');\n\n    btnLayers.addEventListener('click', ()=>{\n      const open = layerPanel.style.display !== 'none';\n      hidePanels();\n      if (!open) {\n        layerPanel.style.display = 'block';\n        btnLayers.classList.add('on');\n      }\n    });\n\n    btnMapType.addEventListener('click', ()=>{\n      const open = mapTypePanel.style.display !== 'none';\n      hidePanels();\n      if (!open) {\n        mapTypePanel.style.display = 'block';\n        btnMapType.classList.add('on');\n      }\n    });\n\n    wrap.addEventListener('click', (e)=>{\n      if (!e.target.closest('.urbnav-layer-panel') &&\n          !e.target.closest('.urbnav-maptype-panel') &&\n          !e.target.closest('.btn-layers') &&\n          !e.target.closest('.btn-maptype')) {\n        hidePanels();\n      }\n    });\n\n    function updateNorthButton(){\n      const h = map.getHeading();\n      const active = h && Math.abs(h) > 1;\n      btnNorth.classList.toggle('on', !!active);\n    }\n\n    btnNorth.addEventListener('click', ()=>{\n      state.targetHeading = 0;\n      state.currentHeading = 0;\n      state.lastStableHeading = 0;\n      if (state.lastPos) forceCenterOnUser(1000);\n      runProgrammaticCamera(()=>{\n        if (typeof map.moveCamera === 'function') {\n          map.moveCamera({\n            center: state.lastPos ? {lat:state.lastPos.lat(), lng:state.lastPos.lng()} : map.getCenter(),\n            heading: 0,\n            zoom: state.currentZoom,\n            tilt: state.currentTilt\n          });\n        } else {\n          map.setHeading(0);\n        }\n      });\n      updateNorthButton();\n    });\n\n    btnToggleUI.addEventListener('click', ()=>{\n      wrap.classList.toggle('ui-collapsed');\n      updateUIToggleButton();\n      setTimeout(forceReflow, 180);\n    });\n    updateUIToggleButton();\n\n    const forceReflow = () => {\n      try{\n        google.maps.event.trigger(map, 'resize');\n        const c = map.getCenter();\n        if (c) map.setCenter(c);\n      }catch(e){}\n    };\n    setTimeout(forceReflow, 150);\n    setTimeout(forceReflow, 600);\n    setTimeout(forceReflow, 1400);\n    window.addEventListener('resize', ()=> setTimeout(forceReflow, 150));\n    window.addEventListener('orientationchange', ()=> setTimeout(forceReflow, 250));\n\n    function inPseudoFS(){ return wrap.classList.contains('pseudo-fs'); }\n    function enterPseudoFS(){ document.body.classList.add('urbnav-lock'); wrap.classList.add('pseudo-fs'); btnFS.classList.add('on'); setTimeout(forceReflow, 250); }\n    function exitPseudoFS(){ document.body.classList.remove('urbnav-lock'); wrap.classList.remove('pseudo-fs'); btnFS.classList.remove('on'); setTimeout(forceReflow, 250); }\n    async function toggleFS(){\n      if (wrap.requestFullscreen) {\n        try{\n          if(!document.fullscreenElement){\n            await wrap.requestFullscreen();\n            btnFS.classList.add('on');\n            setTimeout(forceReflow, 250);\n            return;\n          }else{\n            await document.exitFullscreen();\n            btnFS.classList.remove('on');\n            setTimeout(forceReflow, 250);\n            return;\n          }\n        }catch(e){}\n      }\n      if(!inPseudoFS()) enterPseudoFS(); else exitPseudoFS();\n    }\n    btnFS.addEventListener('click', toggleFS);\n    document.addEventListener('fullscreenchange', ()=>{\n      if(document.fullscreenElement === wrap) btnFS.classList.add('on');\n      else if(!inPseudoFS()) btnFS.classList.remove('on');\n      setTimeout(forceReflow, 200);\n    });\n\n    let autocomplete = null;\n    let autocompletePlace = null;\n    let userIsDragging = false;\n    let geoWatchId = null;\n    let destinationMarker = null;\n    let radiusFetchTimer = null;\n\n    if (google.maps.places && google.maps.places.Autocomplete) {\n      autocomplete = new google.maps.places.Autocomplete(elDest, {\n        fields: ['geometry', 'formatted_address', 'name', 'place_id'],\n        componentRestrictions: { country: 'br' }\n      });\n      autocomplete.bindTo('bounds', map);\n\n      autocomplete.addListener('place_changed', async ()=>{\n        const p = autocomplete.getPlace();\n        autocompletePlace = p || null;\n\n        if (p && p.geometry && p.geometry.location) {\n          state.destLatLng = p.geometry.location;\n          setDestinationMarker(state.destLatLng, p.name || p.formatted_address || 'Destino');\n          let details = null;\n          if (p.place_id) details = await getPlaceDetailsLegacy(p.place_id);\n          showPlaceCard({\n            name: p.name || 'Destino',\n            address: p.formatted_address || elDest.value || '',\n            details\n          });\n        }\n      });\n    }\n\n    function showPlaceCard(info){\n      if(!info){ placeCard.style.display='none'; placeCard.innerHTML=''; return; }\n\n      const details = info.details || null;\n      const photo = details?.photoUrl ? `<img decoding=\"async\" class=\"urbnav-place-photo\" src=\"${esc(details.photoUrl)}\" alt=\"${esc(info.name || 'Destino')}\">` : '';\n      const metas = [];\n      if (details?.rating) metas.push(`<span>\u2b50 ${esc(details.rating)}<\/span>`);\n      if (details?.phone) metas.push(`<span>\ud83d\udcde ${esc(details.phone)}<\/span>`);\n      if (details?.openNow === true) metas.push(`<span>\ud83d\udfe2 Aberto agora<\/span>`);\n      if (details?.openNow === false) metas.push(`<span>\ud83d\udd34 Fechado agora<\/span>`);\n      if (details?.website) metas.push(`<span><a href=\"${esc(details.website)}\" target=\"_blank\" rel=\"noopener\">Site<\/a><\/span>`);\n      if (details?.mapsUrl) metas.push(`<span><a href=\"${esc(details.mapsUrl)}\" target=\"_blank\" rel=\"noopener\">Google Maps<\/a><\/span>`);\n\n      placeCard.style.display='block';\n      placeCard.innerHTML = `\n        ${photo}\n        <div class=\"n\">${esc(info.name || 'Destino')}<\/div>\n        <div class=\"a\">${esc(info.address || '')}<\/div>\n        <div class=\"t\">Pronto para tra\u00e7ar rota<\/div>\n        ${metas.length ? `<div class=\"meta\">${metas.join('')}<\/div>` : ''}\n      `;\n    }\n\n    function getPlaceDetailsLegacy(placeId){\n      return new Promise((resolve)=>{\n        if (!placeId || !placesSvc || !google.maps.places) {\n          resolve(null);\n          return;\n        }\n\n        placesSvc.getDetails({\n          placeId,\n          fields: [\n            'name',\n            'formatted_address',\n            'formatted_phone_number',\n            'opening_hours',\n            'photos',\n            'rating',\n            'website',\n            'url',\n            'geometry'\n          ]\n        }, (place, status)=>{\n          if (status !== google.maps.places.PlacesServiceStatus.OK || !place) {\n            resolve(null);\n            return;\n          }\n\n          let photoUrl = '';\n          try {\n            if (place.photos && place.photos[0]) {\n              photoUrl = place.photos[0].getUrl({ maxWidth: 900, maxHeight: 500 });\n            }\n          } catch(e) {}\n\n          resolve({\n            name: place.name || '',\n            address: place.formatted_address || '',\n            phone: place.formatted_phone_number || '',\n            rating: place.rating || null,\n            openNow: place.opening_hours && typeof place.opening_hours.open_now === 'boolean' ? place.opening_hours.open_now : null,\n            website: place.website || '',\n            mapsUrl: place.url || '',\n            photoUrl\n          });\n        });\n      });\n    }\n\n    function setDestinationMarker(latLng, label){\n      if (destinationMarker) {\n        try { destinationMarker.map = null; } catch(e) {}\n        try { destinationMarker.setMap && destinationMarker.setMap(null); } catch(e) {}\n      }\n\n      if (AdvancedMarkerElement && PinElement) {\n        const pin = new PinElement({\n          background: '#2563eb',\n          borderColor: '#0f172a',\n          glyphColor: '#ffffff',\n          scale: 1.15\n        });\n\n        destinationMarker = new AdvancedMarkerElement({\n          map,\n          position: latLng,\n          title: label || 'Destino',\n          content: pin.element\n        });\n      } else {\n        destinationMarker = new google.maps.Marker({\n          map,\n          position: latLng,\n          title: label || 'Destino'\n        });\n      }\n\n      state.destMarker = destinationMarker;\n    }\n\n    function openStreetViewAt(lat, lng){\n      if (!isFinite(lat) || !isFinite(lng)) return;\n      const ll = new google.maps.LatLng(lat, lng);\n      streetView.setPosition(ll);\n      streetView.setPov({ heading: 0, pitch: 0 });\n      streetView.setVisible(true);\n    }\n\n    const state = {\n      lastPos: null,\n      lastRawPos: null,\n      lastAccuracy: null,\n      currentHeading: 0,\n      targetHeading: 0,\n      currentZoom: 16,\n      targetZoom: 16,\n      currentTilt: 0,\n      targetTilt: 0,\n      cameraCurrentCenter: null,\n      cameraTargetCenter: null,\n      lastPosForBearing: null,\n      lastStableHeading: 0,\n      lastHeadingUpdateAt: 0,\n      destLatLng: null,\n      destMarker: null,\n      navMode: false,\n      routePolyline: null,\n      routePath: null,\n      routeReady: false,\n      routeDistanceMeters: null,\n      routeDurationSeconds: null,\n      lastRerouteAt: 0,\n      offRouteCount: 0,\n      previewLoaded: false,\n      cameraAnimating: false,\n      userInteractingUntil: 0,\n      programmaticCameraLock: false,\n      autoOpenCode: null,\n      autoOpenDistance: 100,\n      autoCloseDistance: 140,\n      hardCenteringUntil: 0,\n      lastSnapAt: 0,\n      lastSnapPoint: null,\n      lastMatrixAt: 0,\n      motionPrevFix: null,\n      motionLastFix: null,\n      motionSpeedMps: 0,\n      motionHeading: 0,\n      instructionText: 'Continue',\n      instructionDistanceM: null,\n      lastImvFetchAt: 0,\n      lastImvFetchPos: null,\n      effectiveRadius: Number(rngRadius.value || 500),\n      matrixCache: new Map(),\n      markerHashByCode: new Map(),\n      cameraLastUiAt: 0,\n      fetchBusy: false,\n      fetchPendingForce: false,\n      fetchPendingMatrixForce: false,\n      fetchSeq: 0,\n      lastAppliedFetchSeq: 0\n    };\n\n    function latLngToObj(ll){ return { lat: ll.lat(), lng: ll.lng() }; }\n    function lerp(a,b,t){ return a + (b-a)*t; }\n    function interpolateLatLng(a,b,t){ return { lat: lerp(a.lat, b.lat, t), lng: lerp(a.lng, b.lng, t) }; }\n    function shortestDelta(a,b){ return ((b-a+540)%360)-180; }\n\n    function isHardCentering(){ return Date.now() < state.hardCenteringUntil; }\n    function lockHardCenter(ms = 1200){ state.hardCenteringUntil = Date.now() + ms; }\n\n    function markUserInteracting(ms = 1800){\n      if (state.navMode && chkFollow.checked) return;\n      state.userInteractingUntil = Date.now() + ms;\n    }\n\n    function isUserInteracting(){\n      if (state.navMode && chkFollow.checked) return false;\n      return Date.now() < state.userInteractingUntil;\n    }\n\n    function runProgrammaticCamera(fn){\n      state.programmaticCameraLock = true;\n      try { fn(); }\n      finally { setTimeout(()=>{ state.programmaticCameraLock = false; }, 70); }\n    }\n\n    function forceCenterOnUser(ms = 1200){\n      if (!state.lastPos) return;\n      lockHardCenter(ms);\n      state.cameraTargetCenter = latLngToObj(state.lastPos);\n      state.cameraCurrentCenter = latLngToObj(state.lastPos);\n\n      runProgrammaticCamera(()=>{\n        if(typeof map.moveCamera === 'function'){\n          map.moveCamera({\n            center: state.cameraTargetCenter,\n            heading: state.currentHeading,\n            zoom: state.currentZoom,\n            tilt: state.currentTilt\n          });\n        } else {\n          map.setCenter(state.lastPos);\n          map.setHeading(state.currentHeading);\n          map.setZoom(state.currentZoom);\n          map.setTilt(state.currentTilt);\n        }\n      });\n    }\n\n    function predictUserPosition(){\n      if (!state.motionLastFix || !state.lastPos) return state.lastPos;\n\n      const ageMs = performance.now() - state.motionLastFix.t;\n      if (ageMs <= 0 || ageMs > 1200) return state.lastPos;\n      if (!isFinite(state.motionSpeedMps) || state.motionSpeedMps <= 1.2) return state.lastPos;\n\n      const meters = Math.min(state.motionSpeedMps * (ageMs \/ 1000) * 0.62, 8);\n      const heading = isFinite(state.motionHeading) ? state.motionHeading : state.currentHeading;\n      if (!isFinite(heading)) return state.lastPos;\n\n      try {\n        return google.maps.geometry.spherical.computeOffset(state.lastPos, meters, heading);\n      } catch(e) {\n        return state.lastPos;\n      }\n    }\n\n    function getRouteHeadingAhead(pos){\n      if(!state.routePath || state.routePath.length < 2 || !pos) return null;\n\n      let bestI = 0;\n      let bestD = Infinity;\n      const step = Math.max(1, Math.floor(state.routePath.length \/ 160));\n\n      for(let i=0;i<state.routePath.length;i+=step){\n        const d = google.maps.geometry.spherical.computeDistanceBetween(pos, state.routePath[i]);\n        if(d < bestD){ bestD = d; bestI = i; }\n      }\n\n      const aheadI = Math.min(bestI + 8, state.routePath.length - 1);\n      if (aheadI <= bestI) return null;\n\n      const a = state.routePath[bestI];\n      const b = state.routePath[aheadI];\n      return bearingFromTwoPoints(a, b);\n    }\n\n    function updateNavInstruction(){\n      if (!state.navMode || !state.lastPos || !state.routePath || state.routePath.length < 2) {\n        navStepDist.textContent = '\u2014';\n        navStepRoad.textContent = 'Continue';\n        navStepLanes.textContent = '\u2b05\ufe0e \u2191 \u279c';\n        navStepArrow.textContent = '\u2191';\n        return;\n      }\n\n      let bestI = 0;\n      let bestD = Infinity;\n      const step = Math.max(1, Math.floor(state.routePath.length \/ 160));\n\n      for(let i=0;i<state.routePath.length;i+=step){\n        const d = google.maps.geometry.spherical.computeDistanceBetween(state.lastPos, state.routePath[i]);\n        if(d < bestD){ bestD = d; bestI = i; }\n      }\n\n      const a = state.routePath[bestI];\n      const b = state.routePath[Math.min(bestI + 5, state.routePath.length - 1)];\n      const c = state.routePath[Math.min(bestI + 12, state.routePath.length - 1)];\n\n      const h1 = bearingFromTwoPoints(a, b);\n      const h2 = bearingFromTwoPoints(b, c);\n      const delta = shortestDelta(h1, h2);\n\n      let text = 'Continue';\n      let arrow = '\u2191';\n      let lanes = '\u2b05\ufe0e \u2191 \u279c';\n\n      if (delta > 20 && delta <= 55) {\n        text = 'Vire levemente \u00e0 direita';\n        arrow = '\u2197';\n        lanes = '\u2191 \u2197 \u279c';\n      } else if (delta > 55) {\n        text = 'Vire \u00e0 direita';\n        arrow = '\u2192';\n        lanes = '\u2191 \u279c';\n      } else if (delta < -20 && delta >= -55) {\n        text = 'Vire levemente \u00e0 esquerda';\n        arrow = '\u2196';\n        lanes = '\u2b05\ufe0e \u2196 \u2191';\n      } else if (delta < -55) {\n        text = 'Vire \u00e0 esquerda';\n        arrow = '\u2190';\n        lanes = '\u2b05\ufe0e \u2191';\n      }\n\n      state.instructionText = text;\n      state.instructionDistanceM = bestD;\n      navStepDist.textContent = fmtDist(bestD);\n      navStepRoad.textContent = text;\n      navStepLanes.textContent = lanes;\n      navStepArrow.textContent = arrow;\n    }\n\n    function updateBottomNavSummary(){\n      if (state.routeReady && state.destLatLng && state.lastPos) {\n        const d = google.maps.geometry.spherical.computeDistanceBetween(state.lastPos, state.destLatLng);\n        navDist.textContent = fmtDist(d);\n        navEtaBig.textContent = isFinite(state.routeDurationSeconds) ? fmtEta(state.routeDurationSeconds) : '\u2014';\n        navArrival.textContent = nowClock();\n      } else {\n        navDist.textContent = '\u2014';\n        navEtaBig.textContent = '\u2014';\n        navArrival.textContent = '\u2014';\n      }\n    }\n\n    function startCameraLoop(){\n      if(state.cameraAnimating) return;\n      state.cameraAnimating = true;\n\n      let lastFrameTs = 0;\n\n      function step(ts){\n        if(!state.cameraAnimating) return;\n\n        if (lastFrameTs && (ts - lastFrameTs) < 33) {\n          requestAnimationFrame(step);\n          return;\n        }\n        lastFrameTs = ts;\n\n        const centerRef = map.getCenter();\n        const currCenter = state.cameraCurrentCenter || (centerRef ? latLngToObj(centerRef) : null);\n        if (!currCenter) {\n          requestAnimationFrame(step);\n          return;\n        }\n\n        let targetCenterLL = state.lastPos;\n        if (state.navMode && chkFollow.checked && state.lastPos) {\n          targetCenterLL = predictUserPosition() || state.lastPos;\n        }\n\n        if (targetCenterLL) {\n          state.cameraTargetCenter = latLngToObj(targetCenterLL);\n        }\n\n        const tgtCenter = state.cameraTargetCenter || currCenter;\n        const followOn  = !!chkFollow.checked && (!isUserInteracting() || state.navMode || isHardCentering());\n\n        const nextCenter = followOn\n          ? interpolateLatLng(currCenter, tgtCenter, state.navMode ? 0.34 : 0.18)\n          : currCenter;\n\n        const dHead = shortestDelta(state.currentHeading, state.targetHeading);\n        let headStep = Math.abs(dHead) < 0.75 ? dHead : dHead * (state.navMode ? 0.16 : 0.10);\n        const maxHeadPerFrame = state.navMode ? 1.10 : 0.80;\n        if (headStep >  maxHeadPerFrame) headStep =  maxHeadPerFrame;\n        if (headStep < -maxHeadPerFrame) headStep = -maxHeadPerFrame;\n\n        const prevHeading = state.currentHeading;\n        const prevZoom = state.currentZoom;\n        const prevTilt = state.currentTilt;\n\n        state.currentHeading = (state.currentHeading + headStep + 360) % 360;\n        state.currentZoom = lerp(state.currentZoom, state.targetZoom, state.navMode ? 0.14 : 0.08);\n        state.currentTilt = lerp(state.currentTilt, state.targetTilt, state.navMode ? 0.12 : 0.08);\n\n        if (followOn) state.cameraCurrentCenter = nextCenter;\n\n        const centerDeltaLat = Math.abs((state.cameraCurrentCenter?.lat || 0) - currCenter.lat);\n        const centerDeltaLng = Math.abs((state.cameraCurrentCenter?.lng || 0) - currCenter.lng);\n\n        const shouldMoveCenter = centerDeltaLat > 0.0000016 || centerDeltaLng > 0.0000016;\n        const shouldMoveHeading = Math.abs(shortestDelta(prevHeading, state.currentHeading)) > 0.12;\n        const shouldMoveZoom = Math.abs(state.currentZoom - prevZoom) > 0.025;\n        const shouldMoveTilt = Math.abs(state.currentTilt - prevTilt) > 0.28;\n\n        if ((!isUserInteracting() || state.navMode || isHardCentering()) &&\n            (shouldMoveCenter || shouldMoveHeading || shouldMoveZoom || shouldMoveTilt)) {\n          runProgrammaticCamera(()=>{\n            if (typeof map.moveCamera === 'function') {\n              const payload = {\n                heading: state.currentHeading,\n                zoom: state.currentZoom,\n                tilt: state.currentTilt\n              };\n              if (followOn) payload.center = state.cameraCurrentCenter;\n              map.moveCamera(payload);\n            } else {\n              if (followOn) map.setCenter(state.cameraCurrentCenter);\n              map.setHeading(state.currentHeading);\n              map.setZoom(state.currentZoom);\n              map.setTilt(state.currentTilt);\n            }\n          });\n        }\n\n        const now = Date.now();\n        if (now - state.cameraLastUiAt > 240) {\n          state.cameraLastUiAt = now;\n          updateNorthButton();\n          updateNavInstruction();\n          updateBottomNavSummary();\n        }\n\n        requestAnimationFrame(step);\n      }\n\n      requestAnimationFrame(step);\n    }\n\n    async function geocodeAddress(q){\n      return new Promise((resolve)=>{\n        geocoder.geocode({\n          address: q,\n          region: 'BR',\n          componentRestrictions: { country: 'BR' }\n        }, (results, status)=>{\n          if(status === 'OK' && results && results[0]) resolve(results[0]);\n          else resolve(null);\n        });\n      });\n    }\n\n    async function placesTextSearch(q){\n      return new Promise((resolve)=>{\n        placesSvc.textSearch({ query: q, region: 'br' }, (results, status)=>{\n          if(status === google.maps.places.PlacesServiceStatus.OK && results && results[0]) resolve(results[0]);\n          else resolve(null);\n        });\n      });\n    }\n\n    async function resolveDestinationFromInput(){\n      if(state.destLatLng) return state.destLatLng;\n\n      const q = String(elDest.value || '').trim();\n      if(!q) return null;\n\n      if (autocompletePlace && autocompletePlace.geometry && autocompletePlace.geometry.location) {\n        state.destLatLng = autocompletePlace.geometry.location;\n        setDestinationMarker(state.destLatLng, autocompletePlace.name || autocompletePlace.formatted_address || 'Destino');\n        const details = autocompletePlace.place_id ? await getPlaceDetailsLegacy(autocompletePlace.place_id) : null;\n        showPlaceCard({\n          name: autocompletePlace.name || 'Destino',\n          address: autocompletePlace.formatted_address || q,\n          details\n        });\n        return state.destLatLng;\n      }\n\n      const m = q.match(\/^\\s*(-?\\d+(\\.\\d+)?)\\s*,\\s*(-?\\d+(\\.\\d+)?)\\s*$\/);\n      if(m){\n        const lat=+m[1], lng=+m[3];\n        if(isFinite(lat)&&isFinite(lng)){\n          const ll = new google.maps.LatLng(lat,lng);\n          state.destLatLng = ll;\n          setDestinationMarker(ll, 'Coordenadas');\n          showPlaceCard({ name:'Coordenadas', address:q, details:null });\n          return ll;\n        }\n      }\n\n      const query = q.replace(\/[^\\dA-Za-z\u00c0-\u00ff\\s,\\-\\\/]\/g, '').trim();\n\n      const placeResult = await placesTextSearch(query);\n      if (placeResult && placeResult.geometry && placeResult.geometry.location) {\n        state.destLatLng = placeResult.geometry.location;\n        setDestinationMarker(state.destLatLng, placeResult.name || query);\n        const details = placeResult.place_id ? await getPlaceDetailsLegacy(placeResult.place_id) : null;\n        showPlaceCard({\n          name: placeResult.name || query,\n          address: placeResult.formatted_address || placeResult.vicinity || query,\n          details\n        });\n        return state.destLatLng;\n      }\n\n      const geo = await geocodeAddress(query);\n      if (geo && geo.geometry && geo.geometry.location) {\n        state.destLatLng = geo.geometry.location;\n        setDestinationMarker(state.destLatLng, query);\n        showPlaceCard({ name: query, address: geo.formatted_address || query, details:null });\n        return state.destLatLng;\n      }\n\n      return null;\n    }\n\n    function bearingFromTwoPoints(a, b){\n      const lat1 = a.lat()*Math.PI\/180, lon1 = a.lng()*Math.PI\/180;\n      const lat2 = b.lat()*Math.PI\/180, lon2 = b.lng()*Math.PI\/180;\n      const y = Math.sin(lon2-lon1)*Math.cos(lat2);\n      const x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(lon2-lon1);\n      let brng = Math.atan2(y,x)*180\/Math.PI;\n      return (brng + 360) % 360;\n    }\n\n    function setNavMode(on){\n      state.navMode = !!on;\n      wrap.classList.toggle('navmode', state.navMode);\n\n      if(state.navMode){\n        state.targetTilt = 45;\n        state.targetZoom = IS_MOBILE ? 17.1 : 17.5;\n        chkFollow.checked = true;\n        if (state.lastPos) forceCenterOnUser(1000);\n      } else {\n        state.targetTilt = 0;\n        state.targetHeading = state.lastStableHeading || 0;\n        state.targetZoom = Math.max(16, map.getZoom() || 16);\n      }\n\n      setTimeout(forceReflow, 120);\n    }\n\n    function clearRoute(){\n      state.routeReady = false;\n      state.routePath = null;\n      state.routeDistanceMeters = null;\n      state.routeDurationSeconds = null;\n      if(state.routePolyline){ state.routePolyline.setMap(null); state.routePolyline = null; }\n      elRT.textContent = 'Rota: \u2014';\n      navDist.textContent = '\u2014';\n      navEtaBig.textContent = '\u2014';\n      navArrival.textContent = '\u2014';\n      navStepDist.textContent = '\u2014';\n      navStepRoad.textContent = 'Continue';\n      navStepLanes.textContent = '\u2b05\ufe0e \u2191 \u279c';\n      navStepArrow.textContent = '\u2191';\n    }\n\n    function drawRoute(path, skipFitBounds=false){\n      state.routePath = path;\n      if(state.routePolyline) state.routePolyline.setMap(null);\n\n      state.routePolyline = new google.maps.Polyline({\n        map,\n        path,\n        strokeColor:'#2563eb',\n        strokeOpacity:0.98,\n        strokeWeight:6,\n        zIndex:5\n      });\n\n      if (!skipFitBounds) {\n        const b = new google.maps.LatLngBounds();\n        path.forEach(p=>b.extend(p));\n\n        chkFollow.checked = false;\n        userIsDragging = false;\n        markUserInteracting(2200);\n\n        map.fitBounds(b, { top:80, right:80, bottom:110, left:80 });\n\n        state.cameraCurrentCenter = latLngToObj(map.getCenter());\n        state.cameraTargetCenter  = state.cameraCurrentCenter;\n        state.currentZoom = map.getZoom() || 16;\n        state.targetZoom  = state.currentZoom;\n\n        setTimeout(forceReflow, 140);\n      }\n    }\n\n    async function computeRouteRoutesREST(origin, destination){\n      const body = {\n        origin: { location: { latLng: { latitude: origin.lat(), longitude: origin.lng() } } },\n        destination: { location: { latLng: { latitude: destination.lat(), longitude: destination.lng() } } },\n        travelMode: \"DRIVE\",\n        routingPreference: \"TRAFFIC_AWARE\",\n        computeAlternativeRoutes: false\n      };\n\n      const resp = await fetch(\"https:\/\/routes.googleapis.com\/directions\/v2:computeRoutes\", {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application\/json\",\n          \"X-Goog-Api-Key\": API_KEY,\n          \"X-Goog-FieldMask\": \"routes.distanceMeters,routes.duration,routes.polyline.encodedPolyline\"\n        },\n        body: JSON.stringify(body)\n      });\n\n      if(!resp.ok) throw new Error(\"routes_rest_fail\");\n      const j = await resp.json();\n      const r = j?.routes?.[0];\n      const enc = r?.polyline?.encodedPolyline;\n      if(!enc) throw new Error(\"routes_rest_no_polyline\");\n\n      return {\n        path: google.maps.geometry.encoding.decodePath(enc),\n        distM: r?.distanceMeters ?? null,\n        durS: (typeof r?.duration === 'string' && r.duration.endsWith('s')) ? parseInt(r.duration,10) : null,\n        provider:'Google'\n      };\n    }\n\n    async function computeRouteOSRM(origin, destination){\n      const url = `https:\/\/router.project-osrm.org\/route\/v1\/driving\/${origin.lng()},${origin.lat()};${destination.lng()},${destination.lat()}?overview=full&geometries=geojson`;\n      const r = await fetch(url);\n      if(!r.ok) throw new Error('osrm_http');\n      const j = await r.json();\n      const coords = j?.routes?.[0]?.geometry?.coordinates;\n      if(!coords) throw new Error('osrm_no_route');\n\n      return {\n        path: coords.map(c => new google.maps.LatLng(c[1], c[0])),\n        distM:null,\n        durS:null,\n        provider:'OSRM'\n      };\n    }\n\n    async function computeRoute(origin, destination){\n      try { return await computeRouteRoutesREST(origin, destination); }\n      catch(e){ return await computeRouteOSRM(origin, destination); }\n    }\n\n    function matrixCacheKey(origin, items){\n      const olat = origin.lat().toFixed(3);\n      const olng = origin.lng().toFixed(3);\n      const ids = items.slice(0, 6).map(i => i.cod).join('|');\n      return `${olat}|${olng}|${ids}`;\n    }\n\n    async function computeRouteMatrixForItems(origin, items, force=false){\n      if (!origin || !Array.isArray(items) || !items.length) return items;\n\n      const top = items.slice(0, Math.min(items.length, 6));\n      const cacheKey = matrixCacheKey(origin, top);\n\n      if (state.matrixCache.has(cacheKey)) {\n        const cached = state.matrixCache.get(cacheKey);\n        const enriched = items.map(item => {\n          const match = cached.find(c => String(c.cod) === String(item.cod));\n          return match ? {...item, ...match} : item;\n        });\n        enriched.sort((a,b)=>{\n          const aEta = Number.isFinite(a.route_eta_sec) ? a.route_eta_sec : 1e15;\n          const bEta = Number.isFinite(b.route_eta_sec) ? b.route_eta_sec : 1e15;\n          if (aEta !== bEta) return aEta - bEta;\n          return (a.dist_m||0) - (b.dist_m||0);\n        });\n        return enriched;\n      }\n\n      const now = Date.now();\n      if (!force && (now - state.lastMatrixAt < 30000)) return items;\n      state.lastMatrixAt = now;\n\n      const body = {\n        origins: [{\n          waypoint: {\n            location: { latLng: { latitude: origin.lat(), longitude: origin.lng() } }\n          }\n        }],\n        destinations: top.map(d => ({\n          waypoint: {\n            location: { latLng: { latitude: Number(d.lat), longitude: Number(d.lng) } }\n          }\n        })),\n        travelMode: \"DRIVE\",\n        routingPreference: \"TRAFFIC_AWARE\"\n      };\n\n      try {\n        const resp = await fetch(\"https:\/\/routes.googleapis.com\/distanceMatrix\/v2:computeRouteMatrix\", {\n          method: \"POST\",\n          headers: {\n            \"Content-Type\": \"application\/json\",\n            \"X-Goog-Api-Key\": API_KEY,\n            \"X-Goog-FieldMask\": \"originIndex,destinationIndex,duration,distanceMeters,status,condition\"\n          },\n          body: JSON.stringify(body)\n        });\n\n        if (!resp.ok) return items;\n        const rows = await resp.json();\n        if (!Array.isArray(rows)) return items;\n\n        const cachedPayload = top.map(i => ({\n          cod: i.cod,\n          route_eta_sec: null,\n          route_dist_m: null,\n          route_condition: ''\n        }));\n\n        rows.forEach(row => {\n          const idx = Number(row.destinationIndex);\n          if (!Number.isFinite(idx) || !cachedPayload[idx]) return;\n          cachedPayload[idx].route_eta_sec = secFromDurationString(row.duration);\n          cachedPayload[idx].route_dist_m = Number.isFinite(row.distanceMeters) ? Number(row.distanceMeters) : null;\n          cachedPayload[idx].route_condition = row.condition || '';\n        });\n\n        state.matrixCache.set(cacheKey, cachedPayload);\n        if (state.matrixCache.size > 22) {\n          const firstKey = state.matrixCache.keys().next().value;\n          state.matrixCache.delete(firstKey);\n        }\n\n        const enriched = items.map(item => {\n          const match = cachedPayload.find(c => String(c.cod) === String(item.cod));\n          return match ? {...item, ...match} : item;\n        });\n\n        enriched.sort((a,b)=>{\n          const aEta = Number.isFinite(a.route_eta_sec) ? a.route_eta_sec : 1e15;\n          const bEta = Number.isFinite(b.route_eta_sec) ? b.route_eta_sec : 1e15;\n          if (aEta !== bEta) return aEta - bEta;\n          return (a.dist_m||0) - (b.dist_m||0);\n        });\n\n        return enriched;\n      } catch(e) {\n        return items;\n      }\n    }\n\n    async function snapPositionToRoad(rawLatLng){\n      if (!rawLatLng) return rawLatLng;\n\n      const now = Date.now();\n      const acc = Number(state.lastAccuracy || 999);\n\n      if (acc <= 14) return rawLatLng;\n      if (state.motionSpeedMps > 6.5) return rawLatLng;\n      if (!state.navMode) return rawLatLng;\n\n      const shouldThrottle = (now - state.lastSnapAt) < 9000;\n      const lastSnap = state.lastSnapPoint;\n      if (shouldThrottle && lastSnap) {\n        return new google.maps.LatLng(lastSnap.lat, lastSnap.lng);\n      }\n\n      try {\n        const params = new URLSearchParams();\n        params.set('action', 'urbano_nav_snap_road');\n        params.set('nonce', NONCE);\n        params.set('api_key', API_KEY);\n        params.set('lat', String(rawLatLng.lat()));\n        params.set('lng', String(rawLatLng.lng()));\n\n        const r = await fetch(AJAX_BASE, {\n          method: 'POST',\n          headers: { 'Content-Type': 'application\/x-www-form-urlencoded; charset=UTF-8' },\n          body: params.toString()\n        });\n\n        if (!r.ok) return rawLatLng;\n        const j = await r.json();\n        if (!j || !j.ok || !Number.isFinite(j.lat) || !Number.isFinite(j.lng)) return rawLatLng;\n\n        state.lastSnapAt = now;\n        state.lastSnapPoint = { lat: j.lat, lng: j.lng };\n        return new google.maps.LatLng(j.lat, j.lng);\n      } catch(e) {\n        return rawLatLng;\n      }\n    }\n\n    function distanceToRouteApproxMeters(pos){\n      if(!state.routePath || state.routePath.length < 2) return Infinity;\n      const n = state.routePath.length;\n      const step = Math.max(1, Math.floor(n \/ 150));\n      let best = Infinity;\n      for(let i=0;i<n;i+=step){\n        const d = google.maps.geometry.spherical.computeDistanceBetween(pos, state.routePath[i]);\n        if(d < best) best = d;\n        if(best < 30) break;\n      }\n      return best;\n    }\n\n    async function maybeReroute(){\n      if(!state.routeReady || !state.lastPos || !state.destLatLng) return;\n\n      const now = Date.now();\n      if(now - state.lastRerouteAt < 12000) return;\n\n      const off = distanceToRouteApproxMeters(state.lastPos);\n      if(off > 95) state.offRouteCount++;\n      else state.offRouteCount = 0;\n\n      if(state.offRouteCount >= 2){\n        state.lastRerouteAt = now;\n        state.offRouteCount = 0;\n        elRT.textContent = 'Rota: recalculando\u2026';\n        try{\n          const r = await computeRoute(state.lastPos, state.destLatLng);\n          drawRoute(r.path, true);\n          state.routeReady = true;\n          state.routeDistanceMeters = r.distM;\n          state.routeDurationSeconds = r.durS;\n          elRT.textContent = `Rota: ${r.provider}`;\n        }catch(e){\n          elRT.textContent = 'Rota: falha rec\u00e1lculo';\n        }\n      }\n    }\n\n    async function traceRouteToLatLng(latLng, labelText, placeInfo, skipFitBounds=false){\n      if(!state.lastPos || !latLng) return false;\n\n      btnStart.disabled = true;\n      clearRoute();\n      elRT.textContent = 'Rota: calculando\u2026';\n\n      try{\n        state.destLatLng = latLng;\n        autocompletePlace = null;\n\n        if (labelText) elDest.value = labelText;\n        setDestinationMarker(latLng, labelText || 'Destino');\n        if (placeInfo) showPlaceCard(placeInfo);\n\n        const r = await computeRoute(state.lastPos, latLng);\n        drawRoute(r.path, skipFitBounds);\n        state.routeReady = true;\n        state.routeDistanceMeters = r.distM;\n        state.routeDurationSeconds = r.durS;\n        elRT.textContent = `Rota: ${r.provider}`;\n        btnStart.disabled = false;\n\n        updateBottomNavSummary();\n        await scheduleFetch(true, true);\n        return true;\n      }catch(e){\n        state.routeReady = false;\n        elRT.textContent = 'Rota: falha';\n        return false;\n      }\n    }\n\n    async function traceAndStartNavigation(latLng, labelText, placeInfo){\n      const ok = await traceRouteToLatLng(latLng, labelText, placeInfo, true);\n      if (!ok) return;\n      setNavMode(true);\n\n      if(state.lastPos){\n        state.cameraCurrentCenter = latLngToObj(state.lastPos);\n        state.cameraTargetCenter = latLngToObj(state.lastPos);\n      }\n      state.targetTilt = 45;\n      state.targetZoom = IS_MOBILE ? 17.1 : 17.5;\n      chkFollow.checked = true;\n      userIsDragging = false;\n      state.userInteractingUntil = 0;\n      forceCenterOnUser(1000);\n\n      clearImoveis();\n      await scheduleFetch(true, true);\n    }\n\n    const info = new google.maps.InfoWindow({\n      minWidth: 300,\n      maxWidth: 320\n    });\n\n    const markersByCode = new Map();\n    const dataByCode = new Map();\n    const posByCode = new Map();\n    let autoOpenTimer = null;\n\n    function sizeFromDist(distM){\n      const minS=42, maxS=86;\n      let d=Number(distM); if(!isFinite(d)) d=500;\n      d=Math.max(0,Math.min(d,500));\n      const t=1-(d\/500);\n      return Math.round(minS+(maxS-minS)*t);\n    }\n\n    function buildImvDiv(d){\n      const dist = Number(d.dist_m);\n      const s = sizeFromDist(dist);\n      const foto = (Array.isArray(d.imgs) && d.imgs.length) ? d.imgs[0] : '';\n      const cod  = String(d.cod||'');\n      const eta  = Number.isFinite(d.route_eta_sec) ? ` \u2022 ${Math.max(1, Math.round(d.route_eta_sec\/60))}min` : '';\n      const lbl  = dist ? `${cod} \u2022 ${Math.round(dist)}m${eta}` : cod + eta;\n\n      const root = document.createElement('div');\n      root.className = 'urbnav-imv';\n\n      const circle = document.createElement('div');\n      circle.className = 'circle';\n      circle.style.width = s+'px';\n      circle.style.height= s+'px';\n\n      if (foto) {\n        const img = document.createElement('img');\n        img.src = foto;\n        img.alt = cod;\n        img.loading = 'lazy';\n        circle.appendChild(img);\n      } else {\n        const ph = document.createElement('div');\n        ph.style.width = '100%';\n        ph.style.height = '100%';\n        ph.style.display = 'flex';\n        ph.style.alignItems = 'center';\n        ph.style.justifyContent = 'center';\n        ph.style.fontWeight = '900';\n        ph.style.fontSize = '11px';\n        ph.style.color = '#475569';\n        ph.textContent = 'IM\u00d3VEL';\n        circle.appendChild(ph);\n      }\n\n      const pin = document.createElement('div');\n      pin.className = 'pin';\n\n      const lab = document.createElement('div');\n      lab.className = 'lbl';\n      lab.style.top = (s - 10) + 'px';\n      lab.textContent = lbl;\n\n      root.appendChild(circle);\n      root.appendChild(pin);\n      root.appendChild(lab);\n      return root;\n    }\n\n    function markerDataHash(d){\n      return JSON.stringify([\n        d.cod, d.nome, d.dist_m, d.route_eta_sec, d.route_dist_m,\n        d.lat, d.lng, (d.imgs && d.imgs[0]) ? d.imgs[0] : ''\n      ]);\n    }\n\n    function cardHtml(d, uid){\n      const pics = Array.isArray(d.imgs) ? d.imgs : [];\n      const first = pics.length ? pics[0] : '';\n      const img = first\n        ? `<div class=\"imgwrap\"><img decoding=\"async\" id=\"${uid}\" src=\"${first}\" style=\"opacity:1\"><\/div>`\n        : '';\n      const nome = esc(d.nome || 'Im\u00f3vel');\n      const cod  = esc(d.cod || '');\n      const dist = (d.dist_m!=null) ? `${Math.round(d.dist_m)} m` : '-';\n      const end  = nl2brEsc(d.end || '-');\n      const val  = esc(toBRL(d.valor));\n      const lat  = Number(d.lat);\n      const lng  = Number(d.lng);\n      const routeDist = Number.isFinite(d.route_dist_m) ? fmtDist(d.route_dist_m) : '-';\n      const routeEta  = Number.isFinite(d.route_eta_sec) ? fmtEta(d.route_eta_sec) : '-';\n\n      return `<div class=\"urbnav-card\">\n        ${img}\n        <div class=\"t\">${nome}<\/div>\n        <div class=\"row\"><div class=\"k\">C\u00f3digo<\/div><div class=\"v\">${cod}<\/div><\/div>\n        <div class=\"row\"><div class=\"k\">Dist\u00e2ncia<\/div><div class=\"v\">${esc(dist)}<\/div><\/div>\n        <div class=\"row\"><div class=\"k\">Rota<\/div><div class=\"v\">${esc(routeDist)}<\/div><\/div>\n        <div class=\"row\"><div class=\"k\">ETA<\/div><div class=\"v\">${esc(routeEta)}<\/div><\/div>\n        <div class=\"row\"><div class=\"k\">Endere\u00e7o<\/div><div class=\"v\">${end}<\/div><\/div>\n        <div class=\"row\"><div class=\"k\">Valor<\/div><div class=\"v\">${val}<\/div><\/div>\n        <div class=\"urbnav-card-actions\">\n          <button\n            type=\"button\"\n            class=\"urbnav-card-street\"\n            data-lat=\"${isFinite(lat) ? lat : ''}\"\n            data-lng=\"${isFinite(lng) ? lng : ''}\"\n            title=\"Street View\"\n            aria-label=\"Street View\">\ud83d\udc41<\/button>\n          <button\n            type=\"button\"\n            class=\"urbnav-card-route\"\n            data-cod=\"${esc(d.cod || '')}\"\n            data-lat=\"${isFinite(lat) ? lat : ''}\"\n            data-lng=\"${isFinite(lng) ? lng : ''}\"\n            title=\"Navegar para este im\u00f3vel\"\n            aria-label=\"Navegar para este im\u00f3vel\">\u279c<\/button>\n        <\/div>\n      <\/div>`;\n    }\n\n    function openImovelCard(d, pos, forceCode){\n      const cod = String(forceCode || d.cod || '');\n      if(!cod || !pos) return;\n\n      const uid='pic_'+cod.replace(\/[^a-z0-9]\/gi,'')+'_'+Math.floor(Math.random()*9999);\n\n      info.close();\n      info.setOptions({ minWidth: 300, maxWidth: 320 });\n      info.setContent(cardHtml(d,uid));\n      info.setPosition(pos);\n      info.open({ map });\n      state.autoOpenCode = cod;\n\n      const pics=Array.isArray(d.imgs)?d.imgs:[];\n      if(pics.length>=2){\n        setTimeout(()=>{\n          const imgEl=document.getElementById(uid);\n          if(!imgEl) return;\n          let idx=0;\n          const timer=setInterval(()=>{\n            if(!document.body.contains(imgEl)){ clearInterval(timer); return; }\n            imgEl.style.opacity=0;\n            setTimeout(()=>{\n              idx=(idx+1)%pics.length;\n              imgEl.src=pics[idx];\n              imgEl.style.opacity=1;\n            },200);\n          },3000);\n        },120);\n      }\n    }\n\n    info.addListener('domready', ()=>{\n      const iw = document.querySelector('.gm-style-iw');\n      if (!iw) return;\n\n      const btnRoute = iw.querySelector('.urbnav-card-route');\n      const btnStreet = iw.querySelector('.urbnav-card-street');\n\n      if (btnStreet) {\n        btnStreet.addEventListener('click', (e)=>{\n          e.preventDefault();\n          e.stopPropagation();\n          const lat = parseFloat(btnStreet.getAttribute('data-lat') || '');\n          const lng = parseFloat(btnStreet.getAttribute('data-lng') || '');\n          if (isFinite(lat) && isFinite(lng)) openStreetViewAt(lat, lng);\n        }, { once:true });\n      }\n\n      if (btnRoute) {\n        btnRoute.addEventListener('click', async (e)=>{\n          e.preventDefault();\n          e.stopPropagation();\n\n          const lat = parseFloat(btnRoute.getAttribute('data-lat') || '');\n          const lng = parseFloat(btnRoute.getAttribute('data-lng') || '');\n          const cod = btnRoute.getAttribute('data-cod') || '';\n          const d = dataByCode.get(cod);\n\n          if (!isFinite(lat) || !isFinite(lng) || !d) return;\n\n          const latLng = new google.maps.LatLng(lat, lng);\n\n          await traceAndStartNavigation(\n            latLng,\n            String(d.nome || d.end || d.cod || 'Destino'),\n            {\n              name: d.nome || d.cod || 'Im\u00f3vel',\n              address: d.end || '',\n              details: null\n            }\n          );\n        }, { once:true });\n      }\n    });\n\n    function closeAutoOpenIfAny(){\n      if(state.autoOpenCode){\n        info.close();\n        state.autoOpenCode = null;\n      }\n    }\n\n    function refreshAutoOpenCard(){\n      if(!state.lastPos || dataByCode.size === 0) return;\n\n      let bestCode = null;\n      let bestDist = Infinity;\n\n      dataByCode.forEach((d, cod)=>{\n        const pos = posByCode.get(cod);\n        if(!pos) return;\n        const dist = google.maps.geometry.spherical.computeDistanceBetween(state.lastPos, pos);\n        if (dist < bestDist) {\n          bestDist = dist;\n          bestCode = cod;\n        }\n      });\n\n      if (bestCode && bestDist <= state.autoOpenDistance) {\n        if (state.autoOpenCode !== bestCode) {\n          const d = dataByCode.get(bestCode);\n          const p = posByCode.get(bestCode);\n          if (d && p) openImovelCard(d, p, bestCode);\n        }\n      } else if (state.autoOpenCode && bestDist > state.autoCloseDistance) {\n        closeAutoOpenIfAny();\n      }\n    }\n\n    function scheduleAutoOpenRefresh(){\n      if (autoOpenTimer) clearTimeout(autoOpenTimer);\n      autoOpenTimer = setTimeout(refreshAutoOpenCard, 900);\n    }\n\n    function clearImoveis(){\n      markersByCode.forEach(entry=>{\n        try { entry.marker.map = null; } catch(e) {}\n        try { entry.marker.setMap && entry.marker.setMap(null); } catch(e) {}\n      });\n      markersByCode.clear();\n      dataByCode.clear();\n      posByCode.clear();\n      state.markerHashByCode.clear();\n      closeAutoOpenIfAny();\n      elIMV.textContent='Im\u00f3veis: 0';\n    }\n\n    function createAdvancedImovelMarker(pos, d, onClick){\n      const content = buildImvDiv(d);\n\n      if (AdvancedMarkerElement) {\n        const marker = new AdvancedMarkerElement({\n          map,\n          position: pos,\n          title: d.nome || d.cod || 'Im\u00f3vel',\n          content\n        });\n\n        content.addEventListener('click', (e)=>{\n          e.stopPropagation();\n          onClick();\n        });\n\n        return { marker, content };\n      }\n\n      const marker = new google.maps.Marker({\n        map,\n        position: pos,\n        title: d.nome || d.cod || 'Im\u00f3vel'\n      });\n\n      marker.addListener('click', onClick);\n      return { marker, content: null };\n    }\n\n    function upsertImoveis(items){\n      const incomingCodes = new Set(items.map(d => String(d.cod || '')));\n\n      markersByCode.forEach((entry, cod)=>{\n        if (!incomingCodes.has(cod)) {\n          try { entry.marker.map = null; } catch(e) {}\n          try { entry.marker.setMap && entry.marker.setMap(null); } catch(e) {}\n          markersByCode.delete(cod);\n          dataByCode.delete(cod);\n          posByCode.delete(cod);\n          state.markerHashByCode.delete(cod);\n          if (state.autoOpenCode === cod) closeAutoOpenIfAny();\n        }\n      });\n\n      for (const d of items){\n        const cod = String(d.cod || '');\n        if (!cod) continue;\n\n        const pos = new google.maps.LatLng(+d.lat, +d.lng);\n        if(!isFinite(pos.lat()) || !isFinite(pos.lng())) continue;\n\n        const hash = markerDataHash(d);\n        const existing = markersByCode.get(cod);\n\n        if (!existing) {\n          const onClick = ()=> openImovelCard(d, pos, cod);\n          const created = createAdvancedImovelMarker(pos, d, onClick);\n          markersByCode.set(cod, created);\n          dataByCode.set(cod, d);\n          posByCode.set(cod, pos);\n          state.markerHashByCode.set(cod, hash);\n          continue;\n        }\n\n        if (state.markerHashByCode.get(cod) !== hash) {\n          try { existing.marker.map = null; } catch(e) {}\n          try { existing.marker.setMap && existing.marker.setMap(null); } catch(e) {}\n          const onClick = ()=> openImovelCard(d, pos, cod);\n          const created = createAdvancedImovelMarker(pos, d, onClick);\n          markersByCode.set(cod, created);\n          state.markerHashByCode.set(cod, hash);\n        } else {\n          try {\n            if (existing.marker.position) existing.marker.position = pos;\n            else if (existing.marker.setPosition) existing.marker.setPosition(pos);\n          } catch(e) {}\n        }\n\n        dataByCode.set(cod, d);\n        posByCode.set(cod, pos);\n      }\n\n      elIMV.textContent='Im\u00f3veis: '+markersByCode.size;\n      scheduleAutoOpenRefresh();\n    }\n\n    function samplePointsAlongRoute(currentPos){\n      const MAX_POINTS=32, STEP_M=280, WINDOW_M=3600;\n      if(!state.routePath || state.routePath.length<2) return [{lat:currentPos.lat(),lng:currentPos.lng()}];\n\n      let bestI=0,bestD=Infinity;\n      const scanStep=Math.max(1, Math.floor(state.routePath.length\/200));\n\n      for(let i=0;i<state.routePath.length;i+=scanStep){\n        const d=google.maps.geometry.spherical.computeDistanceBetween(currentPos, state.routePath[i]);\n        if(d<bestD){bestD=d;bestI=i;}\n      }\n\n      const pts=[{lat:currentPos.lat(),lng:currentPos.lng()}];\n      let acc=0;\n      let lastEmit=currentPos;\n\n      for(let i=bestI;i<state.routePath.length && pts.length<MAX_POINTS;i++){\n        const p=state.routePath[i];\n        if(i>bestI) acc += google.maps.geometry.spherical.computeDistanceBetween(state.routePath[i-1], p);\n        const seg=google.maps.geometry.spherical.computeDistanceBetween(lastEmit, p);\n        if(seg>=STEP_M){ pts.push({lat:p.lat(),lng:p.lng()}); lastEmit=p; }\n        if(acc>=WINDOW_M) break;\n      }\n      return pts.slice(0,MAX_POINTS);\n    }\n\n    async function fetchImoveis(points, radius, isRadar){\n      const params=new URLSearchParams();\n      params.set('action', 'urbano_nav_imoveis');\n      params.set('nonce', NONCE);\n      params.set('radius', String(radius));\n      params.set('limit', String(isRadar ? 70 : 110));\n      params.set('mode', isRadar ? 'nearby' : 'route');\n      params.set('points', JSON.stringify(points));\n\n      const r=await fetch(AJAX_BASE, {\n        method: 'POST',\n        headers: { 'Content-Type': 'application\/x-www-form-urlencoded; charset=UTF-8' },\n        body: params.toString()\n      });\n      if(!r.ok) return [];\n      const j=await r.json();\n      if(!j || !j.ok || !Array.isArray(j.data)) return [];\n      return j.data;\n    }\n\n    function shouldFetchImoveis(force=false){\n      if (force) return true;\n      const now = Date.now();\n      if (!state.lastPos) return false;\n      if (!state.lastImvFetchPos) return true;\n      if (now - state.lastImvFetchAt > 15000) return true;\n\n      const moved = google.maps.geometry.spherical.computeDistanceBetween(state.lastImvFetchPos, state.lastPos);\n      if (state.navMode) return moved >= 150;\n      return moved >= 90;\n    }\n\n    async function scheduleFetch(force=false, matrixForce=false){\n      if (state.fetchBusy) {\n        state.fetchPendingForce = state.fetchPendingForce || !!force;\n        state.fetchPendingMatrixForce = state.fetchPendingMatrixForce || !!matrixForce;\n        return;\n      }\n\n      if (!shouldFetchImoveis(force)) return;\n      if (!state.lastPos && !map.getCenter()) return;\n\n      state.fetchBusy = true;\n      const mySeq = ++state.fetchSeq;\n\n      try {\n        const radius=Number(state.effectiveRadius || rngRadius.value || 500);\n        const origin = state.lastPos || map.getCenter();\n\n        state.lastImvFetchAt = Date.now();\n        if (state.lastPos) state.lastImvFetchPos = state.lastPos;\n\n        let items = [];\n\n        if(!state.navMode && !state.routeReady){\n          items = await fetchImoveis([{lat:origin.lat(),lng:origin.lng()}], radius, true);\n          if (matrixForce) {\n            items = await computeRouteMatrixForItems(origin, items, true);\n          }\n        } else if(state.navMode){\n          if(chkShowNear.checked && state.lastPos){\n            items = await fetchImoveis([{lat:state.lastPos.lat(),lng:state.lastPos.lng()}], radius, true);\n            items = await computeRouteMatrixForItems(state.lastPos, items, matrixForce);\n          } else if(chkShowImv.checked && state.routeReady && state.lastPos){\n            const pts = samplePointsAlongRoute(state.lastPos);\n            items = await fetchImoveis(pts, radius, false);\n            items = await computeRouteMatrixForItems(state.lastPos, items, matrixForce);\n          } else {\n            items = [];\n          }\n        } else if(chkShowNear.checked && state.lastPos){\n          items = await fetchImoveis([{lat:state.lastPos.lat(),lng:state.lastPos.lng()}], radius, true);\n          items = await computeRouteMatrixForItems(state.lastPos, items, matrixForce);\n        }\n\n        if (mySeq >= state.lastAppliedFetchSeq) {\n          state.lastAppliedFetchSeq = mySeq;\n          upsertImoveis(items);\n        }\n      } catch(e) {\n      } finally {\n        state.fetchBusy = false;\n        if (state.fetchPendingForce || state.fetchPendingMatrixForce) {\n          const nextForce = state.fetchPendingForce;\n          const nextMatrix = state.fetchPendingMatrixForce;\n          state.fetchPendingForce = false;\n          state.fetchPendingMatrixForce = false;\n          setTimeout(()=> scheduleFetch(nextForce, nextMatrix), 80);\n        }\n      }\n    }\n\n    function updateHeadingOnlyByMovement(){\n      if(!state.lastPos) return;\n\n      let target = null;\n\n      if (state.navMode && state.routeReady) {\n        target = getRouteHeadingAhead(state.lastPos);\n      }\n\n      if (target === null && state.lastPosForBearing && state.lastPos) {\n        const acc = Number(state.lastAccuracy || 0);\n        if (!(acc && acc > 35)) {\n          const moved = google.maps.geometry.spherical.computeDistanceBetween(state.lastPosForBearing, state.lastPos);\n          const minMove = IS_MOBILE ? 18 : 12;\n          if(moved >= minMove){\n            const now = Date.now();\n            const minInterval = IS_MOBILE ? 1400 : 700;\n            if(now - state.lastHeadingUpdateAt >= minInterval){\n              target = bearingFromTwoPoints(state.lastPosForBearing, state.lastPos);\n              state.lastHeadingUpdateAt = now;\n            }\n          }\n        }\n      }\n\n      if (target !== null && isFinite(target)) {\n        const delta = shortestDelta(state.lastStableHeading || 0, target);\n        if (!IS_MOBILE || Math.abs(delta) >= 7) {\n          state.lastStableHeading = norm(target);\n        }\n      }\n\n      state.targetHeading = state.lastStableHeading || 0;\n    }\n\n    let gpsBusy = false;\n    let pendingGeoPosition = null;\n\n    async function processGeoPosition(pos){\n      const rawLat = pos.coords.latitude;\n      const rawLng = pos.coords.longitude;\n      const acc = pos.coords.accuracy || 0;\n\n      const rawPos = new google.maps.LatLng(rawLat, rawLng);\n      state.lastRawPos = rawPos;\n      state.lastAccuracy = acc;\n\n      let effectivePos = rawPos;\n      if (acc > 14 && state.motionSpeedMps <= 6.5 && state.navMode) {\n        effectivePos = await snapPositionToRoad(rawPos);\n      }\n\n      const tNow = performance.now();\n\n      if (state.lastPos && !state.lastPosForBearing) {\n        state.lastPosForBearing = state.lastPos;\n      } else if (state.lastPos) {\n        const moved = google.maps.geometry.spherical.computeDistanceBetween(state.lastPos, effectivePos);\n        if (moved > 2.5) state.lastPosForBearing = state.lastPos;\n      }\n\n      state.lastPos = effectivePos;\n\n      if (state.motionLastFix) {\n        state.motionPrevFix = state.motionLastFix;\n      }\n      state.motionLastFix = { pos: effectivePos, t: tNow };\n\n      if (state.motionPrevFix && state.motionLastFix) {\n        const d = google.maps.geometry.spherical.computeDistanceBetween(state.motionPrevFix.pos, state.motionLastFix.pos);\n        const dt = (state.motionLastFix.t - state.motionPrevFix.t) \/ 1000;\n        if (dt > 0) {\n          state.motionSpeedMps = Math.min(d \/ dt, 35);\n          const h = bearingFromTwoPoints(state.motionPrevFix.pos, state.motionLastFix.pos);\n          if (isFinite(h)) state.motionHeading = h;\n        }\n      }\n\n      elGPS.textContent='GPS: ok \u2022 ' + (acc ? Math.round(acc)+'m' : '');\n\n      if(!state.previewLoaded){\n        state.previewLoaded = true;\n        state.cameraCurrentCenter = latLngToObj(state.lastPos);\n        state.cameraTargetCenter = latLngToObj(state.lastPos);\n        state.currentZoom = map.getZoom() || 16;\n        state.targetZoom = state.currentZoom;\n        state.currentTilt = 0;\n        state.targetTilt = 0;\n        state.currentHeading = 0;\n        state.targetHeading = 0;\n        startCameraLoop();\n        await scheduleFetch(true, true);\n      }\n\n      if (chkFollow.checked || state.navMode) {\n        state.cameraTargetCenter = latLngToObj(state.lastPos);\n      }\n\n      updateHeadingOnlyByMovement();\n\n      if(state.routeReady){\n        await maybeReroute();\n      }\n\n      await scheduleFetch(false, false);\n      scheduleAutoOpenRefresh();\n      updateBottomNavSummary();\n      updateNavInstruction();\n    }\n\n    geoWatchId = navigator.geolocation.watchPosition((pos)=>{\n      pendingGeoPosition = pos;\n      if (gpsBusy) return;\n\n      gpsBusy = true;\n\n      (async function runGeoQueue(){\n        while (pendingGeoPosition) {\n          const nextPos = pendingGeoPosition;\n          pendingGeoPosition = null;\n          try {\n            await processGeoPosition(nextPos);\n          } catch(e) {}\n        }\n        gpsBusy = false;\n      })();\n\n    }, ()=>{\n      elGPS.textContent='GPS: negado\/erro';\n    }, { enableHighAccuracy:true, maximumAge:1000, timeout:8000 });\n\n    map.addListener('click', (e)=>{\n      if(!e || !e.latLng) return;\n      autocompletePlace = null;\n      state.destLatLng = e.latLng;\n      setDestinationMarker(state.destLatLng, 'Destino no mapa');\n      elDest.value = `${e.latLng.lat().toFixed(6)}, ${e.latLng.lng().toFixed(6)}`;\n      showPlaceCard({ name:'Destino no mapa', address:elDest.value, details:null });\n    });\n\n    map.addListener('dragstart', ()=>{\n      userIsDragging = true;\n      if (state.navMode && chkFollow.checked) return;\n      chkFollow.checked = false;\n      markUserInteracting(1800);\n    });\n\n    map.addListener('drag', ()=>{\n      if (state.navMode && chkFollow.checked && state.lastPos) return;\n      markUserInteracting(1800);\n    });\n\n    map.addListener('zoom_changed', ()=>{\n      const z = map.getZoom();\n      if (typeof z === 'number' && isFinite(z)) {\n        state.currentZoom = z;\n        state.targetZoom = z;\n      }\n\n      if (state.programmaticCameraLock) return;\n      if (state.navMode && chkFollow.checked && state.lastPos) return;\n      markUserInteracting(1800);\n    });\n\n    map.addListener('tilt_changed', ()=>{\n      const t = map.getTilt();\n      if (typeof t === 'number' && isFinite(t)) {\n        state.currentTilt = t;\n        state.targetTilt = t;\n      }\n\n      if (state.programmaticCameraLock) return;\n      if (state.navMode && chkFollow.checked && state.lastPos) return;\n      markUserInteracting(1500);\n    });\n\n    map.addListener('heading_changed', ()=>{\n      const h = map.getHeading();\n      if (typeof h === 'number' && isFinite(h)) state.currentHeading = h;\n\n      if (state.programmaticCameraLock) return;\n      updateNorthButton();\n    });\n\n    map.addListener('idle', ()=>{\n      if (userIsDragging) setTimeout(()=>{ userIsDragging = false; }, 200);\n    });\n\n    elDest.addEventListener('input', ()=>{\n      state.destLatLng = null;\n      autocompletePlace = null;\n    });\n\n    elDest.addEventListener('keydown', (e)=>{\n      if(e.key === 'Enter'){\n        e.preventDefault();\n        btnGo.click();\n      }\n    });\n\n    btnGo.addEventListener('click', async ()=>{\n      if(!state.lastPos) return;\n\n      const ll = await resolveDestinationFromInput();\n      if(!ll) return;\n\n      await traceRouteToLatLng(\n        ll,\n        String(elDest.value || 'Destino'),\n        {\n          name: autocompletePlace?.name || elDest.value || 'Destino',\n          address: autocompletePlace?.formatted_address || placeCard.querySelector('.a')?.textContent || elDest.value || '',\n          details: autocompletePlace?.place_id ? await getPlaceDetailsLegacy(autocompletePlace.place_id) : null\n        }\n      );\n    });\n\n    btnStart.addEventListener('click', async ()=>{\n      if(!state.routeReady) return;\n      setNavMode(true);\n\n      if(state.lastPos){\n        state.cameraCurrentCenter = latLngToObj(state.lastPos);\n        state.cameraTargetCenter = latLngToObj(state.lastPos);\n      }\n\n      state.targetTilt = 45;\n      state.targetZoom = IS_MOBILE ? 17.1 : 17.5;\n      chkFollow.checked = true;\n      userIsDragging = false;\n      state.userInteractingUntil = 0;\n      forceCenterOnUser(1000);\n\n      clearImoveis();\n      await scheduleFetch(true, true);\n    });\n\n    navBottomClose.addEventListener('click', ()=>{\n      setNavMode(false);\n      clearImoveis();\n      scheduleFetch(true, true);\n    });\n\n    btnExit.addEventListener('click', ()=>{\n      setNavMode(false);\n      clearImoveis();\n      scheduleFetch(true, true);\n    });\n\n    navCenter.addEventListener('click', ()=>{\n      if(!state.lastPos) return;\n      userIsDragging = false;\n      state.userInteractingUntil = 0;\n      chkFollow.checked = true;\n      forceCenterOnUser(1200);\n    });\n\n    chkFollow.addEventListener('change', ()=>{\n      if(chkFollow.checked && state.lastPos){\n        userIsDragging = false;\n        state.userInteractingUntil = 0;\n        forceCenterOnUser(1200);\n      } else {\n        markUserInteracting(1800);\n      }\n    });\n\n    chkShowImv.addEventListener('change', ()=> scheduleFetch(true, false));\n    chkShowNear.addEventListener('change', ()=> scheduleFetch(true, false));\n\n    rngRadius.addEventListener('input', ()=>{\n      lblRadius.textContent = rngRadius.value + 'm';\n    });\n\n    rngRadius.addEventListener('change', ()=>{\n      if (radiusFetchTimer) clearTimeout(radiusFetchTimer);\n\n      radiusFetchTimer = setTimeout(()=>{\n        let raioAtual = Number(rngRadius.value || 500);\n        if (state.navMode && raioAtual > 1200) raioAtual = 1200;\n\n        state.effectiveRadius = raioAtual;\n        state.lastMatrixAt = 0;\n        state.lastImvFetchPos = null;\n\n        scheduleFetch(true, false);\n      }, 320);\n    });\n\n    btnClr.addEventListener('click', ()=>{\n      clearImoveis();\n      clearRoute();\n      info.close();\n      streetView.setVisible(false);\n      state.autoOpenCode = null;\n\n      if(state.destMarker){\n        try { state.destMarker.map = null; } catch(e) {}\n        try { state.destMarker.setMap && state.destMarker.setMap(null); } catch(e) {}\n        state.destMarker = null;\n      }\n\n      state.destLatLng = null;\n      autocompletePlace = null;\n\n      elDest.value = '';\n      showPlaceCard(null);\n\n      btnStart.disabled = true;\n      setNavMode(false);\n\n      state.previewLoaded = false;\n      state.cameraTargetCenter = null;\n      state.cameraCurrentCenter = null;\n      state.currentHeading = 0;\n      state.targetHeading = 0;\n      state.currentTilt = 0;\n      state.targetTilt = 0;\n      state.currentZoom = map.getZoom() || 16;\n      state.targetZoom = state.currentZoom;\n      state.lastRerouteAt = 0;\n      state.offRouteCount = 0;\n      state.lastStableHeading = 0;\n      state.lastHeadingUpdateAt = 0;\n      state.hardCenteringUntil = 0;\n      state.lastMatrixAt = 0;\n      state.motionPrevFix = null;\n      state.motionLastFix = null;\n      state.motionSpeedMps = 0;\n      state.motionHeading = 0;\n      state.lastImvFetchAt = 0;\n      state.lastImvFetchPos = null;\n      state.effectiveRadius = Number(rngRadius.value || 500);\n      state.matrixCache.clear();\n      state.fetchPendingForce = false;\n      state.fetchPendingMatrixForce = false;\n\n      if(state.lastPos){\n        state.cameraCurrentCenter = latLngToObj(state.lastPos);\n        state.cameraTargetCenter = latLngToObj(state.lastPos);\n      }\n\n      scheduleFetch(true, false);\n    });\n\n    navMute.addEventListener('click', ()=>{\n      navMute.classList.toggle('on');\n      navMute.textContent = navMute.classList.contains('on') ? '\ud83d\udd07' : '\ud83d\udd0a';\n    });\n\n    window.addEventListener('beforeunload', ()=>{\n      if (geoWatchId !== null) navigator.geolocation.clearWatch(geoWatchId);\n    });\n\n    updateBottomNavSummary();\n    updateNavInstruction();\n    updateNorthButton();\n  }\n})();\n<\/script>\n\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":3,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"page_full-width.php","meta":{"footnotes":""},"class_list":["post-1432","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/urbsaluga.com.br\/index.php\/wp-json\/wp\/v2\/pages\/1432","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/urbsaluga.com.br\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/urbsaluga.com.br\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/urbsaluga.com.br\/index.php\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/urbsaluga.com.br\/index.php\/wp-json\/wp\/v2\/comments?post=1432"}],"version-history":[{"count":0,"href":"https:\/\/urbsaluga.com.br\/index.php\/wp-json\/wp\/v2\/pages\/1432\/revisions"}],"wp:attachment":[{"href":"https:\/\/urbsaluga.com.br\/index.php\/wp-json\/wp\/v2\/media?parent=1432"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}