{"id":2183,"date":"2026-01-22T09:35:12","date_gmt":"2026-01-22T09:35:12","guid":{"rendered":"https:\/\/dromosfestival.it\/map\/"},"modified":"2026-06-12T10:29:02","modified_gmt":"2026-06-12T10:29:02","slug":"map","status":"publish","type":"page","link":"https:\/\/dromosfestival.it\/en\/festival\/map\/","title":{"rendered":"Map"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"2183\" class=\"elementor elementor-2183 elementor-110\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-60cfece e-con-full e-flex e-con e-parent\" data-id=\"60cfece\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-a4c50a6 elementor-widget elementor-widget-html\" data-id=\"a4c50a6\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div id=\"dromos-map-wrapper\">\r\n<div id=\"map-consent-placeholder\">\r\n  <div class=\"map-consent-box\">\r\n    <p>To view the map, you must accept marketing cookies.<br> Use the site's consent management button to update your preferences.<\/p>\r\n  <\/div>\r\n<\/div>\r\n<div id=\"map-search-ui\">\r\n  <div id=\"map-search-bar\">\r\n    <span class=\"material-symbols-outlined map-search-icon\">search<\/span>\r\n    <input id=\"map-search-input\" type=\"search\" placeholder=\"Search locations or artists...\">\r\n  <\/div>\r\n\r\n  <div id=\"map-search-results\"><\/div>\r\n\r\n  <div id=\"map-search-filters\"><\/div>\r\n<\/div>\r\n  <div id=\"map\"><\/div>\r\n\r\n  <div id=\"map-panel\" aria-live=\"polite\" aria-label=\"Scheda location\">\r\n    <button id=\"panel-close\" type=\"button\" aria-label=\"Chiudi\">\u00d7<\/button>\r\n\r\n    <!-- Handle visibile su mobile -->\r\n    <div id=\"panel-handle\" class=\"panel-handle\" aria-hidden=\"true\"><\/div>\r\n\r\n<div id=\"panel-hero\">\r\n  <div class=\"panel-hero-mobile-thumb\" aria-hidden=\"true\"><\/div>\r\n\r\n  <div class=\"panel-hero-text\">\r\n    <div id=\"panel-title\">Select a location<\/div>\r\n    <div class=\"panel-subtitle\">DROMOS FESTIVAL<\/div>\r\n  <\/div>\r\n\r\n  <div class=\"panel-hero-actions\">\r\n    <button id=\"panel-info-toggle\" class=\"panel-info-btn\" type=\"button\" aria-label=\"Apri info\" aria-expanded=\"false\">\r\n INFO\r\n      <span class=\"panel-info-btn__chev\" aria-hidden=\"true\">\u25be<\/span>\r\n    <\/button>\r\n  <\/div>\r\n<\/div>\r\n\r\n    <!-- INFO MENU (popup ancorato) -->\r\n    <div id=\"panel-info-menu\" class=\"panel-info-menu\" aria-hidden=\"true\" data-max-title=\"\">\r\n      <div class=\"panel-info-menu__header\">Active information and services<\/div>\r\n      <div id=\"panel-info-list\" class=\"panel-info-menu__list\"><\/div>\r\n      <div class=\"panel-info-menu__footer\">\r\n *service information may change up to one day before the event date\r\n      <\/div>\r\n    <\/div>\r\n\r\n    <div class=\"panel-chips\"><\/div>\r\n\r\n    <div id=\"panel-address\">Click on a marker to discover the program.<\/div>\r\n\r\n    <div class=\"panel-events\"><\/div>\r\n\r\n    <div class=\"panel-cta\">\r\n      <a id=\"panel-link\" href=\"#\" target=\"_blank\" rel=\"noopener\">Go to location \u2192<\/a>\r\n    <\/div>\r\n\r\n<div class=\"panel-actions\">\r\n   <a id=\"btn-gmaps\" href=\"#\" target=\"_blank\" rel=\"noopener\" class=\"map-btn\">\r\n     <svg class=\"map-btn__icon\" height=\"24px\" viewbox=\"0 -960 960 960\" width=\"24px\"><path d=\"m600-120-240-84-186 72q-20 8-37-4.5T120-170v-560q0-13 7.5-23t20.5-15l212-72 240 84 186-72q20-8 37 4.5t17 33.5v560q0 13-7.5 23T812-192l-212 72Zm-40-98v-468l-160-56v468l160 56Zm80 0 120-40v-474l-120 46v468Zm-440-10 120-46v-468l-120 40v474Zm440-458v468-468Zm-320-56v468-468Z\"><\/path><\/svg>\r\n    <span>Google Maps<\/span>\r\n  <\/a>\r\n\r\n  <a id=\"btn-amaps\" href=\"#\" target=\"_blank\" rel=\"noopener\" class=\"map-btn\">\r\n<svg class=\"map-btn__icon\" height=\"24px\" viewbox=\"0 -960 960 960\" width=\"24px\" fill=\"#e3e3e3\"><path d=\"m200-120-40-40 320-720 320 720-40 40-280-120-280 120Zm84-124 196-84 196 84-196-440-196 440Zm196-84Z\"><\/path><\/svg>\r\n    <span>Apple Maps<\/span>\r\n  <\/a> \r\n<\/div>\r\n  <\/div>\r\n<\/div>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-2abef28 elementor-widget elementor-widget-html\" data-id=\"2abef28\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<script>\n document.addEventListener(\"DOMContentLoaded\", () => {\n  let mapInitialized = false;\n  const wrapper = document.getElementById(\"dromos-map-wrapper\");\n\n  function getCookie(name) {\n    const match = document.cookie.match(\n      new RegExp('(?:^|; )' + name.replace(\/([.$?*|{}()[\\]\\\\\/+^])\/g, '\\\\$1') + '=([^;]*)')\n    );\n    return match ? decodeURIComponent(match[1]) : null;\n  }\n\n  function hasMapConsent() {\n    const marketing = getCookie(\"cmplz_marketing\");\n    return marketing === \"allow\";\n  }\n\n  function setConsentState() {\n    if (!wrapper) return;\n\n    if (hasMapConsent()) {\n      wrapper.classList.remove(\"map-consent-blocked\");\n      wrapper.classList.add(\"map-consent-ready\");\n    } else {\n      wrapper.classList.remove(\"map-consent-ready\");\n      wrapper.classList.add(\"map-consent-blocked\");\n    }\n  }\n\n  async function initMap() {\n    if (mapInitialized) return;\n    mapInitialized = true;\n\n    const map = L.map(\"map\", { zoomControl: false }).setView([39.903, 8.593], 9);\n\n    L.tileLayer(\"https:\/\/{s}.basemaps.cartocdn.com\/light_all\/{z}\/{x}\/{y}{r}.png\", {\n      attribution: '&copy; OpenStreetMap &copy; CARTO',\n      maxZoom: 19\n    }).addTo(map);\n\n    L.control.zoom({ position: \"bottomright\" }).addTo(map);\n\n    const markerIcon = L.divIcon({\n      className: \"\",\n      html: '<div class=\"dromos-marker\"><\/div>',\n      iconSize: [14, 14],\n      iconAnchor: [7, 7]\n    });\n\n    \/\/ ---- SEARCH refs ----\n    const searchInput = document.getElementById(\"map-search-input\");\n    const searchResults = document.getElementById(\"map-search-results\");\n    const searchFilters = document.getElementById(\"map-search-filters\");\n\n    \/\/ ---- PANEL refs ----\n    const panel = document.getElementById(\"map-panel\");\n    const panelClose = document.getElementById(\"panel-close\");\n    const panelHandle = document.getElementById(\"panel-handle\");\n\n    const panelTitle = document.getElementById(\"panel-title\");\n    const panelAddress = document.getElementById(\"panel-address\");\n    const panelSubtitle = document.querySelector(\".panel-subtitle\");\n    const panelChips = document.querySelector(\".panel-chips\");\n    const panelEvents = document.querySelector(\".panel-events\");\n    const panelLink = document.getElementById(\"panel-link\");\n    const panelCtaWrap = document.querySelector(\".panel-cta\");\n    const btnGmaps = document.getElementById(\"btn-gmaps\");\n    const btnAmaps = document.getElementById(\"btn-amaps\");\n\n    const panelHero = document.getElementById(\"panel-hero\");\n\n    \/\/ INFO MENU refs\n    const infoToggle = document.getElementById(\"panel-info-toggle\");\n    const infoMenu = document.getElementById(\"panel-info-menu\");\n    const infoList = document.getElementById(\"panel-info-list\");\n\n    const isMobile = () => window.matchMedia(\"(max-width: 768px)\").matches;\n\n    const openPanel = () => {\n      panel?.classList.add(\"open\");\n      panel?.classList.remove(\"is-hidden-mobile\");\n    };\n\n    const closePanel = () => {\n      panel?.classList.remove(\"open\");\n      if (isMobile()) {\n        panel?.classList.add(\"is-hidden-mobile\");\n      }\n    };\n\n    const expandPanel = () => panel?.classList.add(\"expanded\");\n    const collapsePanel = () => panel?.classList.remove(\"expanded\");\n\n    let activeFilter = \"all\";\n    let activeLocationId = null;\n    let eventsLoaded = false;\n    const availableFilters = new Map();\n\n    const decodeHtml = (s) => {\n      const el = document.createElement(\"textarea\");\n      el.innerHTML = s || \"\";\n      return el.value;\n    };\n\n    const escapeAttr = (s) =>\n      String(s || \"\")\n        .replace(\/&\/g, \"&amp;\")\n        .replace(\/\"\/g, \"&quot;\")\n        .replace(\/<\/g, \"&lt;\")\n        .replace(\/>\/g, \"&gt;\");\n\n    const escapeCssUrl = (s) =>\n      String(s || \"\")\n        .replace(\/\"\/g, '\\\\\"')\n        .replace(\/'\/g, \"\\'\")\n        .replace(\/\\n\/g, \"\")\n        .replace(\/\\r\/g, \"\");\n\n    const normalizeSearchString = (s) =>\n      String(s || \"\")\n        .toLowerCase()\n        .normalize(\"NFD\")\n        .replace(\/[\\u0300-\\u036f]\/g, \"\")\n        .replace(\/[^a-z0-9\\s]\/g, \" \")\n        .replace(\/\\s+\/g, \" \")\n        .trim();\n\n    const slugify = (s) =>\n      String(s || \"\")\n        .toLowerCase()\n        .normalize(\"NFD\")\n        .replace(\/[\\u0300-\\u036f]\/g, \"\")\n        .replace(\/[^a-z0-9]+\/g, \"-\")\n        .replace(\/^-+|-+$\/g, \"\");\n\n    const getFilterIcon = (slug, label = \"\") => {\n      const key = `${slug} ${label}`.toLowerCase();\n\n      if (key.includes(\"music\") || key.includes(\"musica\") || key.includes(\"concerto\")) return \"music_note\";\n      if (key.includes(\"art\") || key.includes(\"arte\") || key.includes(\"mostra\") || key.includes(\"exhibition\")) return \"palette\";\n      if (key.includes(\"talk\") || key.includes(\"incontro\") || key.includes(\"dialogo\") || key.includes(\"conferenza\")) return \"mic\";\n      if (key.includes(\"cinema\") || key.includes(\"film\")) return \"movie\";\n      if (key.includes(\"teatro\") || key.includes(\"performance\")) return \"theater_comedy\";\n      if (key.includes(\"letter\") || key.includes(\"libri\") || key.includes(\"book\")) return \"menu_book\";\n      if (key.includes(\"workshop\") || key.includes(\"laboratorio\") || key.includes(\"laboratori\")) return \"construction\";\n\n      return \"apps\";\n    };\n\n    const getEventTypeTerms = (ev) => {\n      const embeddedTerms = ev?._embedded?.[\"wp:term\"] || [];\n      const out = [];\n\n      embeddedTerms.forEach((group) => {\n        if (!Array.isArray(group)) return;\n\n        group.forEach((term) => {\n          if (!term) return;\n\n          const taxonomy = String(term.taxonomy || \"\");\n          const slug = String(term.slug || \"\");\n          const name = decodeHtml(term.name || \"\");\n\n          if (\n            taxonomy === \"tipologie_evento\" ||\n            taxonomy === \"tipologia-evento\" ||\n            taxonomy === \"tipologia_evento\"\n          ) {\n            out.push({\n              slug: slug || slugify(name),\n              name\n            });\n          }\n        });\n      });\n\n      return out;\n    };\n\n    const PANEL_CHIP_LABELS = {\n      music: \"Music\",\n      musica: \"Music\",\n      concerto: \"Music\",\n      concerti: \"Music\",\n      live: \"Music\",\n      talk: \"Talk\",\n      talks: \"Talk\",\n      conferenza: \"Talk\",\n      conferenze: \"Talk\",\n      incontro: \"Talk\",\n      incontri: \"Talk\",\n      mostra: \"Mostra\",\n      mostre: \"Mostra\",\n      exhibition: \"Mostra\",\n      exhibitions: \"Mostra\",\n      workshop: \"Workshop\",\n      laboratorio: \"Workshop\",\n      laboratori: \"Workshop\",\n      cinema: \"Cinema\",\n      film: \"Cinema\",\n      teatro: \"Teatro\",\n      performance: \"Performance\",\n      libro: \"Libri\",\n      libri: \"Libri\",\n      letteratura: \"Libri\"\n    };\n\n    const getPanelChipLabel = (term) => {\n      const slug = String(term?.slug || \"\").toLowerCase();\n      const name = String(term?.name || \"\").toLowerCase();\n\n      return (\n        PANEL_CHIP_LABELS[slug] ||\n        PANEL_CHIP_LABELS[name] ||\n        decodeHtml(term?.name || \"Evento\")\n      );\n    };\n\n    const getLocationEventTypes = (events = []) => {\n      const mapTerms = new Map();\n\n      events.forEach((ev) => {\n        const terms = getEventTypeTerms(ev);\n\n        terms.forEach((term) => {\n          const key = term.slug || slugify(term.name);\n\n          if (!mapTerms.has(key)) {\n            mapTerms.set(key, {\n              slug: key,\n              label: getPanelChipLabel(term)\n            });\n          }\n        });\n      });\n\n      return Array.from(mapTerms.values());\n    };\n\n    const renderPanelChips = (events = []) => {\n      if (!panelChips) return;\n\n      const chips = getLocationEventTypes(events);\n\n      if (!chips.length) {\n        panelChips.style.display = \"none\";\n        panelChips.innerHTML = \"\";\n        return;\n      }\n\n      panelChips.style.display = \"flex\";\n      panelChips.innerHTML = chips.map((chip) => `\n        <span class=\"panel-chip panel-chip--${escapeAttr(chip.slug)}\">\n          ${escapeAttr(chip.label)}\n        <\/span>\n      `).join(\"\");\n    };\n\n    const itemMatchesFilter = (item) => {\n      if (activeFilter === \"all\") return true;\n\n      if (item.type === \"location\") {\n        return Array.isArray(item.filterSlugs) && item.filterSlugs.includes(activeFilter);\n      }\n\n      if (item.type === \"event\") {\n        return Array.isArray(item.filterSlugs) && item.filterSlugs.includes(activeFilter);\n      }\n\n      return true;\n    };\n\n    const renderFilterChips = () => {\n      if (!searchFilters) return;\n\n      const filters = [\n        {\n          slug: \"all\",\n          label: \"ALL\",\n          icon: \"link\"\n        },\n        ...Array.from(availableFilters.values()).sort((a, b) =>\n          a.label.localeCompare(b.label, \"it\", { sensitivity: \"base\" })\n        )\n      ];\n\n      searchFilters.innerHTML = filters.map((filter) => `\n        <button type=\"button\" class=\"map-filter-chip${filter.slug === activeFilter ? \" is-active\" : \"\"}\" data-filter=\"${escapeAttr(filter.slug)}\" aria-pressed=\"${filter.slug === activeFilter ? \"true\" : \"false\"}\">\n          <span class=\"material-symbols-outlined\" aria-hidden=\"true\">${filter.icon}<\/span>\n          <span>${escapeAttr(filter.label)}<\/span>\n        <\/button>\n      `).join(\"\");\n    };\n\n    const setActiveFilter = (slug) => {\n      activeFilter = slug || \"all\";\n      renderFilterChips();\n      updateSearch();\n    };\n\n    const INFO_SERVIZI_MAP = {\n      merch: { title: \"Merchandising\", icon: \"shopping_bag\" },\n      food: { title: \"Food\", icon: \"fork_spoon\" },\n      bar: { title: \"Bar\", icon: \"local_bar\" },\n      wc: { title: \"WC\", icon: \"wc\" },\n      park: { title: \"Park\", icon: \"local_parking\" },\n\n      abbigliamento_comodo: { title: \"Dresscode\", subtitle: \"Comodo\", icon: \"apparel\" },\n      abbigliamento_elegante: { title: \"Dresscode\", subtitle: \"Elegante\", icon: \"apparel\" },\n      abbigliamento_casual: { title: \"Dresscode\", subtitle: \"Casual\", icon: \"apparel\" }\n    };\n\n    const normalizeInfoKey = (s) =>\n      String(s || \"\")\n        .trim()\n        .toLowerCase()\n        .replace(\/\u00e0\/g, \"a\")\n        .replace(\/\u00e8|\u00e9\/g, \"e\")\n        .replace(\/\u00ec\/g, \"i\")\n        .replace(\/\u00f2\/g, \"o\")\n        .replace(\/\u00f9\/g, \"u\")\n        .replace(\/[^a-z0-9]+\/g, \"_\")\n        .replace(\/^_+|_+$\/g, \"\");\n\n    const closeInfoMenu = () => {\n      panel?.classList.remove(\"info-open\");\n      infoMenu?.setAttribute(\"aria-hidden\", \"true\");\n      infoToggle?.setAttribute(\"aria-expanded\", \"false\");\n    };\n\n    const openInfoMenu = () => {\n      if (!panel || !infoMenu || !infoToggle) return;\n\n      const btnRect = infoToggle.getBoundingClientRect();\n      const panelRect = panel.getBoundingClientRect();\n\n      const top = (btnRect.bottom - panelRect.top) + 10;\n      const right = Math.max(16, (panelRect.right - btnRect.right));\n\n      infoMenu.style.top = `${top}px`;\n      infoMenu.style.right = `${right}px`;\n      infoMenu.style.left = \"auto\";\n\n      panel.classList.add(\"info-open\");\n      infoMenu.setAttribute(\"aria-hidden\", \"false\");\n      infoToggle.setAttribute(\"aria-expanded\", \"true\");\n    };\n\n    const toggleInfoMenu = () => {\n      const isOpen = panel?.classList.contains(\"info-open\");\n      if (isOpen) closeInfoMenu();\n      else openInfoMenu();\n    };\n\n    infoToggle?.addEventListener(\"click\", (e) => {\n      e.stopPropagation();\n      toggleInfoMenu();\n    });\n\n    document.addEventListener(\"click\", (e) => {\n      if (!panel?.classList.contains(\"info-open\")) return;\n      const t = e.target;\n      const clickedToggle = infoToggle?.contains(t);\n      const clickedMenu = infoMenu?.contains(t);\n      if (!clickedToggle && !clickedMenu) closeInfoMenu();\n    });\n\n    document.addEventListener(\"keydown\", (e) => {\n      if (e.key === \"Escape\") {\n        closeInfoMenu();\n        closeSearchResults();\n      }\n    });\n\n    window.addEventListener(\"resize\", () => {\n      if (panel?.classList.contains(\"info-open\")) openInfoMenu();\n    });\n\n    const renderInfoMenu = (rawValues) => {\n      if (!infoList || !infoToggle) return;\n\n      const arr = Array.isArray(rawValues) ? rawValues : (rawValues ? [rawValues] : []);\n      const keys = arr.map(normalizeInfoKey);\n      const items = keys.map((k) => INFO_SERVIZI_MAP[k]).filter(Boolean);\n\n      if (!items.length) {\n        infoToggle.classList.add(\"is-hidden\");\n        infoList.innerHTML = \"\";\n        closeInfoMenu();\n        return;\n      }\n\n      infoToggle.classList.remove(\"is-hidden\");\n\n      const maxTitle = items.map((it) => it.title || \"\").sort((a, b) => b.length - a.length)[0] || \"\";\n      infoMenu?.setAttribute(\"data-max-title\", maxTitle);\n\n      infoList.innerHTML = items.map((it) => {\n        const title = decodeHtml(it.title || \"\");\n        const subtitle = decodeHtml(it.subtitle || \"\");\n        const iconName = decodeHtml(it.icon || \"info\");\n\n        return `\n          <div class=\"panel-info-item\" tabindex=\"0\">\n            <div class=\"panel-info-icon\" aria-hidden=\"true\">\n              <span class=\"material-symbols-outlined\" aria-hidden=\"true\">${iconName}<\/span>\n            <\/div>\n            <div class=\"panel-info-text\">\n              <div class=\"panel-info-title\">${title}<\/div>\n              ${subtitle ? `<div class=\"panel-info-subtitle\">${subtitle}<\/div>` : ``}\n            <\/div>\n          <\/div>\n        `;\n      }).join(\"\");\n    };\n\n    const formatAcfDate = (yyyymmdd) => {\n      if (!yyyymmdd || String(yyyymmdd).length !== 8) return \"\";\n      const y = String(yyyymmdd).slice(0, 4);\n      const m = String(yyyymmdd).slice(4, 6);\n      const d = String(yyyymmdd).slice(6, 8);\n      const months = [\"GEN\", \"FEB\", \"MAR\", \"APR\", \"MAG\", \"GIU\", \"LUG\", \"AGO\", \"SET\", \"OTT\", \"NOV\", \"DIC\"];\n      return `${d} ${months[parseInt(m, 10) - 1]} ${y}`;\n    };\n\n    const formatTime = (hhmmss) => (hhmmss ? String(hhmmss).slice(0, 5) : \"\");\n\n    const getEventThumbUrl = (ev) => {\n      const embeddedMedia = ev?._embedded?.[\"wp:featuredmedia\"]?.[0];\n      if (embeddedMedia) {\n        return (\n          embeddedMedia?.media_details?.sizes?.medium?.source_url ||\n          embeddedMedia?.media_details?.sizes?.medium_large?.source_url ||\n          embeddedMedia?.media_details?.sizes?.thumbnail?.source_url ||\n          embeddedMedia?.source_url ||\n          \"\"\n        );\n      }\n\n      if (typeof ev?.acf?.immagine_evento === \"string\") return ev.acf.immagine_evento;\n      if (ev?.acf?.immagine_evento?.url) return ev.acf.immagine_evento.url;\n\n      return \"\";\n    };\n\n    const getEventContextLabel = (ev) => {\n      const embeddedTerms = ev?._embedded?.[\"wp:term\"] || [];\n\n      for (const group of embeddedTerms) {\n        if (!Array.isArray(group)) continue;\n\n        const found = group.find((term) => {\n          const taxonomy = term?.taxonomy || \"\";\n          const slug = term?.slug || \"\";\n          return taxonomy === \"contesto\" || slug === \"festival\" || slug === \"extra-festival\";\n        });\n\n        if (found?.name) return decodeHtml(found.name);\n      }\n\n      if (ev?.acf?.contesto_label) {\n        return decodeHtml(ev.acf.contesto_label);\n      }\n\n      return \"\";\n    };\n\n    const renderEventCard = (ev) => {\n      const date = formatAcfDate(ev?.acf?.data);\n      const time = formatTime(ev?.acf?.ora);\n      const title = decodeHtml(ev?.title?.rendered || \"\");\n      const link = ev?.link || \"#\";\n      const linkTicket = ev?.acf?.link_biglietteria || \"#\";\n      const thumb = getEventThumbUrl(ev);\n      const eventId = ev?.id ? String(ev.id) : \"\";\n      const contextLabel = getEventContextLabel(ev);\n\n      return `\n        <div class=\"panel-event\" data-event-id=\"${escapeAttr(eventId)}\">\n          <a href=\"${link}\" class=\"panel-event-thumb-link\" aria-label=\"${escapeAttr(title)}\">\n            <div class=\"panel-event-thumb${thumb ? \"\" : \" panel-event-thumb--placeholder\"}\" ${thumb ? `style=\"background-image:url('${escapeCssUrl(thumb)}');\"` : \"\"} aria-hidden=\"true\"><\/div>\n          <\/a>\n\n          <div class=\"panel-event-main\">\n            <a href=\"${link}\" class=\"panel-event-date-link\">\n              <div class=\"panel-event-date\">${date}${time ? `, ${time}` : \"\"}<\/div>\n            <\/a>\n\n            ${contextLabel ? `<div class=\"panel-event-context\">${contextLabel}<\/div>` : \"\"}\n\n            <a href=\"${link}\" class=\"panel-event-link\">\n              <div class=\"panel-event-title\">${title}<\/div>\n            <\/a>\n          <\/div>\n\n          <a class=\"panel-event-btn\" href=\"${linkTicket}\" aria-label=\"Acquista biglietto\">\n            <svg class=\"icon-ticket\" viewBox=\"0 -960 960 960\" aria-hidden=\"true\">\n              <path d=\"M480-280q17 0 28.5-11.5T520-320q0-17-11.5-28.5T480-360q-17 0-28.5 11.5T440-320q0 17 11.5 28.5T480-280Zm0-160q17 0 28.5-11.5T520-480q0-17-11.5-28.5T480-520q-17 0-28.5 11.5T440-480q0 17 11.5 28.5T480-440Zm0-160q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm320 440H160q-33 0-56.5-23.5T80-240v-160q33 0 56.5-23.5T160-480q0-33-23.5-56.5T80-560v-160q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v160q-33 0-56.5 23.5T800-480q0 33 23.5 56.5T880-400v160q0 33-23.5 56.5T800-160Zm0-80v-102q-37-22-58.5-58.5T720-480q0-43 21.5-79.5T800-618v-102H160v102q37 22 58.5 58.5T240-480q0 43-21.5 79.5T160-342v102h640Z\"\/>\n            <\/svg>\n          <\/a>\n        <\/div>\n      `;\n    };\n\n    const highlightEventCard = (eventId) => {\n      if (!eventId || !panelEvents) return;\n\n      const el = panelEvents.querySelector(`.panel-event[data-event-id=\"${CSS.escape(String(eventId))}\"]`);\n      if (!el) return;\n\n      el.scrollIntoView({ behavior: \"smooth\", block: \"nearest\" });\n      el.classList.add(\"is-highlighted\");\n\n      setTimeout(() => {\n        el.classList.remove(\"is-highlighted\");\n      }, 1800);\n    };\n\n    let heroTimer = null;\n    let heroIndex = 0;\n\n    const stopHeroSlideshow = () => {\n      if (heroTimer) clearInterval(heroTimer);\n      heroTimer = null;\n      heroIndex = 0;\n\n      if (!panelHero) return;\n\n      panelHero.classList.remove(\"is-a\", \"is-b\");\n      panelHero.style.setProperty(\"--heroA\", \"none\");\n      panelHero.style.setProperty(\"--heroB\", \"none\");\n      panelHero.style.setProperty(\"--heroThumb\", \"none\");\n    };\n\n    const preloadImages = (urls = []) =>\n      Promise.all(\n        urls.map((url) => new Promise((resolve) => {\n          const img = new Image();\n          img.onload = () => resolve(url);\n          img.onerror = () => resolve(null);\n          img.src = url;\n        }))\n      ).then((res) => res.filter(Boolean));\n\n    const extractGalleryItems = (gallery) => {\n      if (!Array.isArray(gallery)) return [];\n      return gallery.map((item) => {\n        if (!item) return null;\n        if (typeof item === \"string\") return item;\n        if (typeof item === \"number\") return item;\n        return item.url || item.source_url || item.sizes?.large || item.sizes?.medium_large || item.sizes?.full || null;\n      }).filter(Boolean);\n    };\n\n    const resolveMediaIdsToUrls = async (items = []) => {\n      const ids = items.map((x) => {\n        if (typeof x === \"number\") return x;\n        const n = parseInt(String(x), 10);\n        return Number.isFinite(n) ? n : null;\n      }).filter(Boolean);\n\n      if (!ids.length) return [];\n\n      try {\n        const res = await fetch(`\/wp-json\/wp\/v2\/media?include=${ids.join(\",\")}&per_page=100`);\n        const media = await res.json();\n        return (media || []).map((m) => m?.source_url).filter(Boolean);\n      } catch (e) {\n        return [];\n      }\n    };\n\n    const startHeroSlideshow = async (gallery) => {\n      if (!panelHero) return;\n      stopHeroSlideshow();\n\n      let items = extractGalleryItems(gallery);\n      const hasIds = items.some((x) => typeof x === \"number\" || \/^\\d+$\/.test(String(x)));\n      let urls = hasIds ? await resolveMediaIdsToUrls(items) : items.filter((x) => typeof x === \"string\");\n      urls = await preloadImages(urls);\n\n      if (!urls.length) return;\n\n      panelHero.style.setProperty(\"--heroThumb\", `url(\"${escapeCssUrl(urls[0])}\")`);\n\n      if (isMobile()) {\n        return;\n      }\n\n      if (urls.length === 1) {\n        panelHero.style.setProperty(\"--heroA\", `url(\"${escapeCssUrl(urls[0])}\")`);\n        panelHero.style.setProperty(\"--heroB\", `url(\"${escapeCssUrl(urls[0])}\")`);\n        panelHero.classList.add(\"is-a\");\n        return;\n      }\n\n      heroIndex = 1;\n      panelHero.style.setProperty(\"--heroA\", `url(\"${escapeCssUrl(urls[0])}\")`);\n      panelHero.style.setProperty(\"--heroB\", `url(\"${escapeCssUrl(urls[1])}\")`);\n      panelHero.classList.add(\"is-a\");\n\n      let showingA = true;\n      heroTimer = setInterval(() => {\n        heroIndex = (heroIndex + 1) % urls.length;\n        const nextUrl = urls[heroIndex];\n\n        if (showingA) {\n          panelHero.style.setProperty(\"--heroB\", `url(\"${escapeCssUrl(nextUrl)}\")`);\n          panelHero.classList.remove(\"is-a\");\n          panelHero.classList.add(\"is-b\");\n        } else {\n          panelHero.style.setProperty(\"--heroA\", `url(\"${escapeCssUrl(nextUrl)}\")`);\n          panelHero.classList.remove(\"is-b\");\n          panelHero.classList.add(\"is-a\");\n        }\n\n        showingA = !showingA;\n      }, 3500);\n    };\n\n    let sheetState = \"hidden\";\n    let currentY = 0;\n    let dragging = false;\n    let startY = 0;\n    let startSheetY = 0;\n    \n    const getSnapPoints = () => {\n      const headerOffset = 8;\n      const availableHeight = window.innerHeight - headerOffset;\n\n      return {\n        hidden: availableHeight + 24,\n        collapsed: Math.round(availableHeight * 0.84),\n        peek: Math.round(availableHeight * 0.73),\n        mid: Math.round(availableHeight * 0.52),\n        expanded: 0\n      };\n    };\n\n    const applySheetY = (y, animate = true) => {\n      if (!panel || !isMobile()) return;\n\n      panel.style.transition = animate\n        ? \"transform 0.28s cubic-bezier(.4,0,.2,1)\"\n        : \"none\";\n\n      panel.style.transform = `translateY(${y}px)`;\n      currentY = y;\n    };\n\n    const snapTo = (state, animate = true) => {\n      if (!isMobile() || !panel) return;\n\n      const sp = getSnapPoints();\n      sheetState = state;\n\n      if (state === \"expanded\") applySheetY(sp.expanded, animate);\n      else if (state === \"mid\") applySheetY(sp.mid, animate);\n      else if (state === \"peek\") applySheetY(sp.peek, animate);\n      else if (state === \"collapsed\") applySheetY(sp.collapsed, animate);\n      else applySheetY(sp.hidden, animate);\n\n      if (state === \"hidden\") {\n        panel.classList.add(\"is-hidden-mobile\");\n      } else {\n        panel.classList.remove(\"is-hidden-mobile\");\n        panel.classList.add(\"open\");\n      }\n    };\n\n    const initSheet = () => {\n      if (!panel) return;\n\n      if (!isMobile()) {\n        panel.style.transform = \"\";\n        panel.style.transition = \"\";\n        panel.classList.remove(\"is-hidden-mobile\");\n        return;\n      }\n\n      snapTo(\"hidden\", false);\n    };\n\n    const getNearestState = (y) => {\n      const sp = getSnapPoints();\n      const distances = [\n        { state: \"expanded\", value: Math.abs(y - sp.expanded) },\n        { state: \"mid\", value: Math.abs(y - sp.mid) },\n        { state: \"peek\", value: Math.abs(y - sp.peek) },\n        { state: \"collapsed\", value: Math.abs(y - sp.collapsed) },\n        { state: \"hidden\", value: Math.abs(y - sp.hidden) }\n      ];\n\n      distances.sort((a, b) => a.value - b.value);\n      return distances[0].state;\n    };\n\n    const dragZone = panelHero;\n    const dragZone2 = panelHandle;\n\n    const onPointerDown = (e) => {\n      if (!isMobile() || !panel) return;\n\n      const t = e.target;\n      if (infoToggle?.contains(t) || infoMenu?.contains(t)) return;\n\n      dragging = true;\n      startY = e.clientY;\n      startSheetY = currentY;\n\n      closeInfoMenu();\n\n      map.dragging.disable();\n      map.scrollWheelZoom.disable();\n      map.doubleClickZoom.disable();\n      map.touchZoom.disable();\n\n      try {\n        e.target.setPointerCapture?.(e.pointerId);\n      } catch (_) {}\n\n      panel.style.transition = \"none\";\n    };\n\n    const onPointerMove = (e) => {\n      if (!dragging || !isMobile()) return;\n\n      const dy = e.clientY - startY;\n      const sp = getSnapPoints();\n      const next = Math.min(Math.max(startSheetY + dy, sp.expanded), sp.hidden);\n\n      applySheetY(next, false);\n    };\n\n    const onPointerUp = () => {\n      if (!dragging || !isMobile()) return;\n\n      dragging = false;\n\n      map.dragging.enable();\n      map.scrollWheelZoom.enable();\n      map.doubleClickZoom.enable();\n      map.touchZoom.enable();\n\n      const nextState = getNearestState(currentY);\n      snapTo(nextState, true);\n    };\n\n    [dragZone, dragZone2].forEach((el) => {\n      if (!el) return;\n      el.style.touchAction = \"none\";\n      el.addEventListener(\"pointerdown\", onPointerDown);\n    });\n\n    window.addEventListener(\"pointermove\", onPointerMove);\n    window.addEventListener(\"pointerup\", onPointerUp);\n    window.addEventListener(\"pointercancel\", onPointerUp);\n\n    panelHandle?.addEventListener(\"click\", () => {\n      if (!isMobile()) return;\n\n      if (sheetState === \"collapsed\") snapTo(\"peek\", true);\n      else if (sheetState === \"peek\") snapTo(\"mid\", true);\n      else if (sheetState === \"mid\") snapTo(\"expanded\", true);\n      else if (sheetState === \"expanded\") snapTo(\"peek\", true);\n      else snapTo(\"peek\", true);\n    });\n\n    window.addEventListener(\"resize\", () => {\n      setTimeout(() => {\n        map.invalidateSize();\n      }, 100);\n\n      if (!isMobile()) {\n        panel?.classList.remove(\"is-hidden-mobile\");\n        panel.style.transform = \"\";\n        panel.style.transition = \"\";\n        return;\n      }\n\n      if (sheetState === \"hidden\") snapTo(\"hidden\", false);\n      else if (sheetState === \"collapsed\") snapTo(\"collapsed\", false);\n      else if (sheetState === \"peek\") snapTo(\"peek\", false);\n      else if (sheetState === \"mid\") snapTo(\"mid\", false);\n      else snapTo(\"expanded\", false);\n\n      if (panel?.classList.contains(\"info-open\")) openInfoMenu();\n    });\n\n    initSheet();\n\n    function setPanelPlaceholder() {\n      activeLocationId = null;\n      stopHeroSlideshow();\n      closeInfoMenu();\n      collapsePanel();\n\n      if (panelTitle) panelTitle.textContent = \"Seleziona una location\";\n      if (panelSubtitle) panelSubtitle.textContent = \"DROMOS FESTIVAL\";\n      if (panelAddress) panelAddress.textContent = \"Clicca su un marker per scoprire la programmazione.\";\n      if (panelHero) panelHero.style.setProperty(\"--heroThumb\", \"none\");\n\n      if (panelChips) {\n        panelChips.style.display = \"none\";\n        panelChips.innerHTML = \"\";\n      }\n\n      if (panelEvents) panelEvents.innerHTML = \"\";\n      if (panelCtaWrap) panelCtaWrap.style.display = \"none\";\n      if (panelLink) panelLink.href = \"#\";\n\n      renderInfoMenu([]);\n\n      if (btnGmaps) {\n        btnGmaps.href = \"#\";\n        btnGmaps.setAttribute(\"aria-disabled\", \"true\");\n        btnGmaps.style.pointerEvents = \"none\";\n        btnGmaps.style.opacity = \"0.5\";\n      }\n\n      if (btnAmaps) {\n        btnAmaps.href = \"#\";\n        btnAmaps.setAttribute(\"aria-disabled\", \"true\");\n        btnAmaps.style.pointerEvents = \"none\";\n        btnAmaps.style.opacity = \"0.5\";\n      }\n\n      if (isMobile()) {\n        snapTo(\"hidden\", true);\n        closePanel();\n      } else {\n        openPanel();\n      }\n    }\n\n    function setPanelLocation({ title, address, lat, lng, link, events = [], gallery = [], info_e_servizi = [], locationId = null }) {\n      activeLocationId = locationId ? String(locationId) : null;\n\n      expandPanel();\n      closeInfoMenu();\n\n      if (panelTitle) panelTitle.innerHTML = title || \"Location\";\n      if (panelSubtitle) panelSubtitle.textContent = `Dromos x ${title || \"Location\"}`;\n      if (panelAddress) panelAddress.textContent = address || \"\";\n\n      renderPanelChips(events);\n\n      if (panelEvents) {\n        if (!events.length) {\n          panelEvents.innerHTML = eventsLoaded\n            ? `<div style=\"opacity:.7; padding:8px 4px;\">Nessun evento in programma.<\/div>`\n            : `<div style=\"opacity:.7; padding:8px 4px;\">Caricamento eventi...<\/div>`;\n        } else {\n          const sorted = [...events].sort((a, b) => {\n            const ad = (a?.acf?.data || \"\") + (a?.acf?.ora || \"\");\n            const bd = (b?.acf?.data || \"\") + (b?.acf?.ora || \"\");\n            return ad.localeCompare(bd);\n          });\n          panelEvents.innerHTML = sorted.map(renderEventCard).join(\"\");\n        }\n      }\n\n      if (panelCtaWrap) panelCtaWrap.style.display = \"flex\";\n      if (panelLink) panelLink.href = link || \"#\";\n\n      const q = `${lat},${lng}`;\n\n      if (btnGmaps) {\n        btnGmaps.href = `https:\/\/www.google.com\/maps\/search\/?api=1&query=${encodeURIComponent(q)}`;\n        btnGmaps.removeAttribute(\"aria-disabled\");\n        btnGmaps.style.pointerEvents = \"\";\n        btnGmaps.style.opacity = \"\";\n      }\n\n      if (btnAmaps) {\n        btnAmaps.href = `https:\/\/maps.apple.com\/?q=${encodeURIComponent(q)}`;\n        btnAmaps.removeAttribute(\"aria-disabled\");\n        btnAmaps.style.pointerEvents = \"\";\n        btnAmaps.style.opacity = \"\";\n      }\n\n      startHeroSlideshow(gallery);\n      renderInfoMenu(info_e_servizi);\n\n      openPanel();\n\n      if (isMobile()) {\n        snapTo(\"peek\", true);\n      }\n    }\n\n    panelClose?.addEventListener(\"click\", () => {\n      stopHeroSlideshow();\n      closeInfoMenu();\n      collapsePanel();\n\n      if (isMobile()) {\n        snapTo(\"hidden\", true);\n        setTimeout(() => {\n          setPanelPlaceholder();\n        }, 220);\n      } else {\n        setTimeout(() => {\n          setPanelPlaceholder();\n        }, 220);\n      }\n    });\n\n    setPanelPlaceholder();\n\n    console.time(\"DROMOS LOCATIONS\");\n\n    const locRes = await fetch(\"\/wp-json\/wp\/v2\/locations?per_page=100\");\n    const locations = await locRes.json();\n\n    console.timeEnd(\"DROMOS LOCATIONS\");\n\n    let eventi = [];\n    const eventsByLocationId = new Map();\n\n    const bounds = [];\n    const markersByLocationId = new Map();\n    const locationDataById = new Map();\n    const searchableItems = [];\n\n    const setMarkerVisible = (marker, visible) => {\n      if (!marker) return;\n      marker.setOpacity(visible ? 1 : 0);\n\n      const el = marker.getElement?.();\n      if (el) {\n        el.style.pointerEvents = visible ? \"\" : \"none\";\n      }\n    };\n\n    const showAllMarkers = () => {\n      markersByLocationId.forEach((marker) => setMarkerVisible(marker, true));\n    };\n\n    const closeSearchResults = () => {\n      if (searchResults) {\n        searchResults.innerHTML = \"\";\n        searchResults.classList.remove(\"is-open\");\n      }\n    };\n\n    const renderSearchResults = (results) => {\n      if (!searchResults) return;\n\n      if (!results.length) {\n        searchResults.innerHTML = `<div class=\"map-search-result is-empty\">Nessun risultato<\/div>`;\n        searchResults.classList.add(\"is-open\");\n        return;\n      }\n\n      searchResults.innerHTML = results.map((item) => {\n        const label = escapeAttr(item.label || \"\");\n        const sub = escapeAttr(item.subLabel || \"\");\n        const type = escapeAttr(item.type || \"\");\n        return `\n          <button class=\"map-search-result\" type=\"button\" data-type=\"${type}\" data-location-id=\"${escapeAttr(item.locationId)}\" ${item.eventId ? `data-event-id=\"${escapeAttr(item.eventId)}\"` : \"\"}>\n            <span class=\"map-search-result__main\">${label}<\/span>\n            ${sub ? `<span class=\"map-search-result__sub\">${sub}<\/span>` : \"\"}\n            <span class=\"map-search-result__type\">${type === \"event\" ? \"EVENT\" : \"LOCATION\"}<\/span>\n          <\/button>\n        `;\n      }).join(\"\");\n\n      searchResults.classList.add(\"is-open\");\n    };\n\n    const openLocationFromSearch = ({ locationId, eventId = null }) => {\n      const locData = locationDataById.get(String(locationId));\n      if (!locData) return;\n\n      const { lat, lng } = locData;\n      const targetZoom = Math.max(map.getZoom(), 13);\n\n      map.flyTo([lat, lng], targetZoom, { duration: 0.7 });\n\n      setPanelLocation(locData);\n      searchInput?.blur();\n\n      if (eventId) {\n        setTimeout(() => highlightEventCard(eventId), 250);\n      }\n\n      closeSearchResults();\n    };\n\n    const updateSearch = () => {\n      const q = normalizeSearchString(searchInput?.value || \"\");\n\n      const matches = searchableItems.filter((item) => {\n        const matchesText = !q || item.searchText.includes(q);\n        const matchesFilter = itemMatchesFilter(item);\n        return matchesText && matchesFilter;\n      });\n\n      const uniqueLocationIds = [...new Set(matches.map((item) => item.locationId))];\n\n      if (!q && activeFilter === \"all\") {\n        showAllMarkers();\n        closeSearchResults();\n        return;\n      }\n\n      markersByLocationId.forEach((marker, locId) => {\n        setMarkerVisible(marker, uniqueLocationIds.includes(String(locId)));\n      });\n\n      if (!q) {\n        closeSearchResults();\n        return;\n      }\n\n      const results = matches.slice(0, 8);\n      renderSearchResults(results);\n    };\n\n    locations.forEach((loc) => {\n      const pos = loc?.acf?.posizione;\n      if (!pos || !pos.lat || !pos.lng) return;\n\n      const lat = parseFloat(pos.lat);\n      const lng = parseFloat(pos.lng);\n\n      const title = decodeHtml(loc?.title?.rendered || loc.slug);\n      const link = loc?.link || \"#\";\n      const address = loc?.acf?.indirizzo || pos.address || \"\";\n      const locId = String(loc?.id);\n\n      const gallery = loc?.acf?.galleria || [];\n      const info_e_servizi = loc?.acf?.info_e_servizi || [];\n      const locationEvents = eventsByLocationId.get(locId) || [];\n\n      const marker = L.marker([lat, lng], { icon: markerIcon }).addTo(map);\n\n      marker.bindTooltip(title, {\n        permanent: false,\n        direction: \"top\",\n        offset: [0, -12],\n        opacity: 1,\n        sticky: true,\n        className: \"dromos-location-tooltip\"\n      });\n\n      const locData = {\n        title,\n        address,\n        lat,\n        lng,\n        link,\n        events: locationEvents,\n        gallery,\n        info_e_servizi,\n        marker,\n        locationId: locId\n      };\n\n      markersByLocationId.set(locId, marker);\n      locationDataById.set(locId, locData);\n\n      marker.on(\"click\", () => {\n        const targetZoom = Math.max(map.getZoom(), 13);\n        map.flyTo([lat, lng], targetZoom, { duration: 0.7 });\n        setPanelLocation(locData);\n      });\n\n      const locationFilterSlugs = [...new Set(\n        locationEvents.flatMap((ev) => getEventTypeTerms(ev).map((term) => term.slug))\n      )];\n\n      searchableItems.push({\n        type: \"location\",\n        locationId: locId,\n        label: title,\n        subLabel: address,\n        filterSlugs: locationFilterSlugs,\n        searchText: normalizeSearchString(`${title} ${address}`)\n      });\n\n      bounds.push([lat, lng]);\n    });\n\n    renderFilterChips();\n\n    if (bounds.length) {\n      map.fitBounds(bounds, { padding: [60, 60] });\n\n      const getPanelOffsetX = () => {\n        const w = window.innerWidth;\n        if (w <= 768) return 0;\n        if (w < 1024) return 180;\n        return 260;\n      };\n\n      setTimeout(() => {\n        map.panBy([getPanelOffsetX(), 0], { animate: false });\n      }, 0);\n    }\n\n    async function loadEventsForMap() {\n      console.time(\"DROMOS EVENTS\");\n\n      try {\n        const evRes = await fetch(\"\/wp-json\/wp\/v2\/eventi?per_page=100&_embed\");\n        eventi = await evRes.json();\n\n        console.timeEnd(\"DROMOS EVENTS\");\n\n        eventsByLocationId.clear();\n        availableFilters.clear();\n\n        const locationSearchItems = searchableItems.filter((item) => item.type === \"location\");\n        searchableItems.splice(0, searchableItems.length, ...locationSearchItems);\n\n        (eventi || []).forEach((ev) => {\n          const locIds = ev?.acf?.location || [];\n          if (!Array.isArray(locIds)) return;\n\n          locIds.forEach((id) => {\n            const key = String(id);\n            if (!eventsByLocationId.has(key)) eventsByLocationId.set(key, []);\n            eventsByLocationId.get(key).push(ev);\n          });\n        });\n\n        locationDataById.forEach((locData, locId) => {\n          const locationEvents = eventsByLocationId.get(String(locId)) || [];\n          locData.events = locationEvents;\n\n          const locationFilterSlugs = [...new Set(\n            locationEvents.flatMap((ev) => getEventTypeTerms(ev).map((term) => term.slug))\n          )];\n\n          const locationItem = searchableItems.find(\n            (item) => item.type === \"location\" && item.locationId === String(locId)\n          );\n\n          if (locationItem) {\n            locationItem.filterSlugs = locationFilterSlugs;\n          }\n\n          locationEvents.forEach((ev) => {\n            const evTitle = decodeHtml(ev?.title?.rendered || \"\");\n            const evDate = formatAcfDate(ev?.acf?.data);\n            const evTime = formatTime(ev?.acf?.ora);\n            const artists = Array.isArray(ev?.acf?.artisti) ? ev.acf.artisti.join(\" \") : \"\";\n            const contesto = getEventContextLabel(ev);\n            const typeTerms = getEventTypeTerms(ev);\n\n            typeTerms.forEach((term) => {\n              if (!availableFilters.has(term.slug)) {\n                availableFilters.set(term.slug, {\n                  slug: term.slug,\n                  label: term.name.toUpperCase(),\n                  icon: getFilterIcon(term.slug, term.name)\n                });\n              }\n            });\n\n            searchableItems.push({\n              type: \"event\",\n              locationId: String(locId),\n              eventId: String(ev?.id || \"\"),\n              label: evTitle,\n              subLabel: [locData.title, evDate, evTime, contesto].filter(Boolean).join(\" \u2022 \"),\n              filterSlugs: typeTerms.map((t) => t.slug),\n              filterLabels: typeTerms.map((t) => t.name),\n              searchText: normalizeSearchString(\n                `${evTitle} ${locData.title} ${locData.address} ${artists} ${evDate} ${evTime} ${contesto} ${typeTerms.map((t) => t.name).join(\" \")}`\n              )\n            });\n          });\n        });\n\n        eventsLoaded = true;\n\n        renderFilterChips();\n        updateSearch();\n\n        if (activeLocationId) {\n          const activeLocData = locationDataById.get(String(activeLocationId));\n          if (activeLocData) {\n            setPanelLocation(activeLocData);\n          }\n        }\n      } catch (error) {\n        eventsLoaded = true;\n        console.error(\"Errore caricamento eventi mappa:\", error);\n\n        if (activeLocationId) {\n          const activeLocData = locationDataById.get(String(activeLocationId));\n          if (activeLocData) {\n            setPanelLocation(activeLocData);\n          }\n        }\n      }\n    }\n\n    loadEventsForMap();\n\n    searchInput?.addEventListener(\"input\", updateSearch);\n\n    searchInput?.addEventListener(\"focus\", () => {\n      if ((searchInput.value || \"\").trim()) updateSearch();\n    });\n\n    searchFilters?.addEventListener(\"click\", (e) => {\n      const btn = e.target.closest(\".map-filter-chip\");\n      if (!btn) return;\n\n      setActiveFilter(btn.dataset.filter || \"all\");\n    });\n\n    searchResults?.addEventListener(\"click\", (e) => {\n      const btn = e.target.closest(\".map-search-result\");\n      if (!btn) return;\n\n      openLocationFromSearch({\n        locationId: btn.dataset.locationId,\n        eventId: btn.dataset.eventId || null\n      });\n    });\n\n    document.addEventListener(\"click\", (e) => {\n      const t = e.target;\n      const clickedInput = searchInput?.contains?.(t) || t === searchInput;\n      const clickedResults = searchResults?.contains?.(t);\n      const clickedFilters = searchFilters?.contains?.(t);\n\n      if (!clickedInput && !clickedResults && !clickedFilters) {\n        closeSearchResults();\n      }\n    });\n\n    searchInput?.addEventListener(\"keydown\", (e) => {\n      if (e.key === \"Enter\") {\n        e.preventDefault();\n        const first = searchResults?.querySelector(\".map-search-result[data-location-id]\");\n        if (first) {\n          openLocationFromSearch({\n            locationId: first.dataset.locationId,\n            eventId: first.dataset.eventId || null\n          });\n        }\n      }\n    });\n  }\n\n  function maybeInitMap() {\n    setConsentState();\n\n    if (hasMapConsent()) {\n      initMap();\n    }\n  }\n\n  maybeInitMap();\n});<\/script>\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>To view the map, you must accept marketing cookies. Use the site&#8217;s consent management button to update your preferences. search \u00d7 Select a location DROMOS FESTIVAL INFO \u25be Active information and services *service information may change up to one day before the event date Click on a marker to discover the program. Go to location [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"parent":2213,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"footnotes":""},"in-evidenza":[24],"class_list":["post-2183","page","type-page","status-publish","hentry","in-evidenza-home-ticker"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Map - Dromos Festival<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/dromosfestival.it\/en\/festival\/map\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Map - Dromos Festival\" \/>\n<meta property=\"og:description\" content=\"To view the map, you must accept marketing cookies. Use the site&#8217;s consent management button to update your preferences. search \u00d7 Select a location DROMOS FESTIVAL INFO \u25be Active information and services *service information may change up to one day before the event date Click on a marker to discover the program. Go to location [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/dromosfestival.it\/en\/festival\/map\/\" \/>\n<meta property=\"og:site_name\" content=\"Dromos Festival\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/dromosfestivalsardegna\/\" \/>\n<meta property=\"article:modified_time\" content=\"2026-06-12T10:29:02+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"15 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/dromosfestival.it\\\/en\\\/festival\\\/map\\\/\",\"url\":\"https:\\\/\\\/dromosfestival.it\\\/en\\\/festival\\\/map\\\/\",\"name\":\"Map - Dromos Festival\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/dromosfestival.it\\\/en\\\/#website\"},\"datePublished\":\"2026-01-22T09:35:12+00:00\",\"dateModified\":\"2026-06-12T10:29:02+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/dromosfestival.it\\\/en\\\/festival\\\/map\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/dromosfestival.it\\\/en\\\/festival\\\/map\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/dromosfestival.it\\\/en\\\/festival\\\/map\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/dromosfestival.it\\\/en\\\/home\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Festival\",\"item\":\"https:\\\/\\\/dromosfestival.it\\\/en\\\/festival\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Map\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/dromosfestival.it\\\/en\\\/#website\",\"url\":\"https:\\\/\\\/dromosfestival.it\\\/en\\\/\",\"name\":\"Dromos Festival\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/dromosfestival.it\\\/en\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/dromosfestival.it\\\/en\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/dromosfestival.it\\\/en\\\/#organization\",\"name\":\"Dromos Festival\",\"url\":\"https:\\\/\\\/dromosfestival.it\\\/en\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/dromosfestival.it\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/dromosfestival.it\\\/wp-content\\\/uploads\\\/2025\\\/12\\\/DROMOS_2026_LOGO.svg\",\"contentUrl\":\"https:\\\/\\\/dromosfestival.it\\\/wp-content\\\/uploads\\\/2025\\\/12\\\/DROMOS_2026_LOGO.svg\",\"width\":1864,\"height\":716,\"caption\":\"Dromos Festival\"},\"image\":{\"@id\":\"https:\\\/\\\/dromosfestival.it\\\/en\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/dromosfestivalsardegna\\\/\",\"https:\\\/\\\/www.instagram.com\\\/dromosfestival\\\/\",\"https:\\\/\\\/www.youtube.com\\\/@DromosFestivalOristano\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Map - Dromos Festival","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/dromosfestival.it\/en\/festival\/map\/","og_locale":"en_US","og_type":"article","og_title":"Map - Dromos Festival","og_description":"To view the map, you must accept marketing cookies. Use the site&#8217;s consent management button to update your preferences. search \u00d7 Select a location DROMOS FESTIVAL INFO \u25be Active information and services *service information may change up to one day before the event date Click on a marker to discover the program. Go to location [&hellip;]","og_url":"https:\/\/dromosfestival.it\/en\/festival\/map\/","og_site_name":"Dromos Festival","article_publisher":"https:\/\/www.facebook.com\/dromosfestivalsardegna\/","article_modified_time":"2026-06-12T10:29:02+00:00","twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"15 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/dromosfestival.it\/en\/festival\/map\/","url":"https:\/\/dromosfestival.it\/en\/festival\/map\/","name":"Map - Dromos Festival","isPartOf":{"@id":"https:\/\/dromosfestival.it\/en\/#website"},"datePublished":"2026-01-22T09:35:12+00:00","dateModified":"2026-06-12T10:29:02+00:00","breadcrumb":{"@id":"https:\/\/dromosfestival.it\/en\/festival\/map\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/dromosfestival.it\/en\/festival\/map\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/dromosfestival.it\/en\/festival\/map\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/dromosfestival.it\/en\/home\/"},{"@type":"ListItem","position":2,"name":"Festival","item":"https:\/\/dromosfestival.it\/en\/festival\/"},{"@type":"ListItem","position":3,"name":"Map"}]},{"@type":"WebSite","@id":"https:\/\/dromosfestival.it\/en\/#website","url":"https:\/\/dromosfestival.it\/en\/","name":"Dromos Festival","description":"","publisher":{"@id":"https:\/\/dromosfestival.it\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/dromosfestival.it\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/dromosfestival.it\/en\/#organization","name":"Dromos Festival","url":"https:\/\/dromosfestival.it\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/dromosfestival.it\/en\/#\/schema\/logo\/image\/","url":"https:\/\/dromosfestival.it\/wp-content\/uploads\/2025\/12\/DROMOS_2026_LOGO.svg","contentUrl":"https:\/\/dromosfestival.it\/wp-content\/uploads\/2025\/12\/DROMOS_2026_LOGO.svg","width":1864,"height":716,"caption":"Dromos Festival"},"image":{"@id":"https:\/\/dromosfestival.it\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/dromosfestivalsardegna\/","https:\/\/www.instagram.com\/dromosfestival\/","https:\/\/www.youtube.com\/@DromosFestivalOristano"]}]}},"_links":{"self":[{"href":"https:\/\/dromosfestival.it\/en\/wp-json\/wp\/v2\/pages\/2183","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dromosfestival.it\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/dromosfestival.it\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/dromosfestival.it\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/dromosfestival.it\/en\/wp-json\/wp\/v2\/comments?post=2183"}],"version-history":[{"count":5,"href":"https:\/\/dromosfestival.it\/en\/wp-json\/wp\/v2\/pages\/2183\/revisions"}],"predecessor-version":[{"id":2782,"href":"https:\/\/dromosfestival.it\/en\/wp-json\/wp\/v2\/pages\/2183\/revisions\/2782"}],"up":[{"embeddable":true,"href":"https:\/\/dromosfestival.it\/en\/wp-json\/wp\/v2\/pages\/2213"}],"wp:attachment":[{"href":"https:\/\/dromosfestival.it\/en\/wp-json\/wp\/v2\/media?parent=2183"}],"wp:term":[{"taxonomy":"in-evidenza","embeddable":true,"href":"https:\/\/dromosfestival.it\/en\/wp-json\/wp\/v2\/in-evidenza?post=2183"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}