GEARS
About the project
Green Explorers: Adults Reimagining Sustainability (G.E.A.R.S.) is a project funded by the Erasmus+ programme, which focuses on empowering adults with practical skills for sustainable living. The project responds to growing environmental challenges and the urgent need for accessible, high-quality education that helps individuals adopt an eco-friendly lifestyle.
The problem
Many adults across Europe lack access to practical, engaging, and flexible learning opportunities related to sustainability. Although awareness of environmental issues is increasing, there is still a gap in everyday knowledge and skills – such as waste reduction, sustainable consumer choices, or applying eco-friendly practices in daily life. This limits the ability of individuals to contribute to environmental protection and community resilience.
The solution
G.E.A.R.S. addresses this gap by transforming sustainability education into accessible and engaging learning experiences. The project is developing:
Interactive online learning modules on topics such as the 5R principles (Refuse, Reduce, Reuse, Recycle, Rot) and zero-waste living
Practical city games that allow participants to discover real-world sustainable initiatives
A creative comic that shares participants' journeys and inspires a wider audience
An accredited educational programme in line with the EU GreenComp framework
Community “sustainability cabinets” providing tools and resources for local engagement
Together, these activities combine digital learning with real-world experiences to foster lasting behavioural change and practical application of sustainability principles.
Target groups
The project primarily focuses on:
Adult learners (aged 21 – 60) interested in developing sustainable skills for personal and professional use
Educators and trainers who want to integrate sustainability into their teaching
Local community members who are keen to discover eco-friendly practices in their environment
Stakeholders and institutions supporting education, sustainability, and policy development
Special attention is given to making education accessible to people from both urban and rural areas, including those with fewer opportunities.
Impact
By combining education, creativity, and community engagement, G.E.A.R.S. aims to:
Increase awareness and adoption of sustainable practices
Improve access to flexible and high-quality adult education
Empower individuals with certified green competencies
Strengthen cooperation between organisations and communities across Europe
Ultimately, the project supports individuals in making informed, environmentally responsible decisions and contributes to building more sustainable communities!
One-day city game – Interactive map of local initiatives
This interactive map was created as part of the one-day city game and collects locations related to sustainable living, conscious consumption, and local eco-friendly practices. It includes points promoting repair, reuse, waste reduction, plant-based dining options, and support for local producers and services. The map encourages participants to explore the city through the lens of everyday activities that can support a more responsible and sustainable lifestyle. It is an educational tool that combines localness, practical experience, and the development of green skills in urban spaces.
#kosice-sustainability-map { margin: 0; padding: 0; font-family: Arial, sans-serif; color: #1f2937; } #kosice-sustainability-map * { box-sizing: border-box; } #kosice-sustainability-map .ksm-shell { display: flex; flex-direction: column; background: #f3f4f6; border-radius: 16px; overflow: hidden; } #kosice-sustainability-map .ksm-header { border-bottom: 1px solid #e5e7eb; background: #ffffff; padding: 20px 16px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); } #kosice-sustainability-map .ksm-header-inner { max-width: 1200px; margin: 0 auto; } #kosice-sustainability-map .ksm-eyebrow { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.2em; color: #047857; margin: 0 0 8px 0; } #kosice-sustainability-map .ksm-title { margin: 0; font-size: 30px; line-height: 1.2; font-weight: 700; color: #111827; } #kosice-sustainability-map .ksm-subtitle { margin: 10px 0 0 0; max-width: 760px; font-size: 16px; line-height: 1.6; color: #4b5563; } #kosice-sustainability-map .ksm-map-wrapper { position: relative; width: 100%; min-height: 650px; background: #dbeafe; box-shadow: 0 10px 20px rgba(0,0,0,0.08); } #kosice-sustainability-map .ksm-map { position: absolute; inset: 0; width: 100%; height: 100%; min-height: 650px; z-index: 1; } #kosice-sustainability-map .ksm-filter-control, #kosice-sustainability-map .ksm-funding-overlay { z-index: 1000; } #kosice-sustainability-map .ksm-filter-control { position: absolute; left: 50%; top: 16px; transform: translateX(-50%); width: auto; max-width: 96%; background: #ffffff; border: 1px solid #e5e7eb; border-radius: 12px; padding: 12px; box-shadow: 0 10px 25px rgba(0,0,0,0.12); pointer-events: auto; } #kosice-sustainability-map .ksm-filter-title { margin: 0 0 10px 0; padding-bottom: 8px; border-bottom: 1px solid #e5e7eb; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: #1f2937; } #kosice-sustainability-map .ksm-filters-list { display: flex; flex-wrap: nowrap; align-items: center; gap: 8px; overflow-x: auto; padding-bottom: 2px; } #kosice-sustainability-map .ksm-filter-btn { flex-shrink: 0; white-space: nowrap; border: none; border-radius: 999px; padding: 8px 14px; font-size: 12px; font-weight: 600; cursor: pointer; transition: 0.2s ease; text-align: center; } #kosice-sustainability-map .ksm-filter-btn.inactive { background: #f3f4f6; color: #4b5563; } #kosice-sustainability-map .ksm-filter-btn.inactive:hover { background: #e5e7eb; } #kosice-sustainability-map .ksm-filter-btn.active:hover { opacity: 0.9; } #kosice-sustainability-map .ksm-funding-overlay { position: absolute; bottom: 16px; left: 16px; max-width: 250px; background: #ffffff; border: 1px solid #e5e7eb; border-radius: 12px; padding: 12px; box-shadow: 0 10px 25px rgba(0,0,0,0.12); pointer-events: auto; } #kosice-sustainability-map .ksm-funding-overlay img { width: 100%; height: auto; max-height: 60px; object-fit: contain; display: block; } #kosice-sustainability-map .ksm-list-section { padding: 20px 16px; flex-grow: 1; } #kosice-sustainability-map .ksm-accordion-container { max-width: 1100px; margin: 0 auto; } #kosice-sustainability-map .ksm-category-heading { margin: 26px 0 14px; display: flex; align-items: center; gap: 10px; padding-bottom: 8px; border-bottom: 1px solid #d1d5db; font-size: 24px; font-weight: 700; color: #1f2937; } #kosice-sustainability-map .ksm-color-dot { width: 16px; height: 16px; border-radius: 4px; display: inline-block; flex: 0 0 auto; } #kosice-sustainability-map .ksm-accordion-item { background: #ffffff; border: 1px solid #e5e7eb; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.05); margin-bottom: 10px; overflow: hidden; } #kosice-sustainability-map .ksm-accordion-header { display: flex; justify-content: space-between; align-items: center; cursor: pointer; padding: 16px; gap: 16px; } #kosice-sustainability-map .ksm-accordion-header:hover { background: #f9fafb; } #kosice-sustainability-map .ksm-place-title { margin: 0; font-size: 18px; font-weight: 600; color: #1f2937; } #kosice-sustainability-map .ksm-place-subtitle { margin: 6px 0 0 0; font-size: 14px; color: #6b7280; } #kosice-sustainability-map .ksm-chevron { width: 20px; height: 20px; color: #6b7280; transition: transform 0.2s ease; flex: 0 0 auto; } #kosice-sustainability-map .ksm-chevron.rotated { transform: rotate(180deg); } #kosice-sustainability-map .ksm-accordion-content { max-height: 0; overflow: hidden; transition: max-height 0.3s ease-out; } #kosice-sustainability-map .ksm-accordion-content-inner { border-top: 1px solid #e5e7eb; padding: 20px 16px; background: #ffffff; } #kosice-sustainability-map .ksm-content-grid { display: flex; flex-direction: row; gap: 24px; } #kosice-sustainability-map .ksm-content-main { flex: 1; min-width: 0; } #kosice-sustainability-map .ksm-content-side { width: 38%; min-width: 280px; } #kosice-sustainability-map .ksm-badge { display: inline-block; margin-bottom: 14px; border-radius: 999px; padding: 6px 12px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: #ffffff; } #kosice-sustainability-map .ksm-description { font-size: 16px; line-height: 1.7; color: #374151; margin: 0; } #kosice-sustainability-map .ksm-side-box { padding-left: 24px; border-left: 1px solid #e5e7eb; } #kosice-sustainability-map .ksm-info-block { margin-bottom: 18px; } #kosice-sustainability-map .ksm-info-label { margin-bottom: 6px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: #6b7280; } #kosice-sustainability-map .ksm-info-value { font-size: 14px; line-height: 1.7; color: #374151; } #kosice-sustainability-map .ksm-info-value a { color: #2563eb; text-decoration: none; word-break: break-word; } #kosice-sustainability-map .ksm-info-value a:hover { text-decoration: underline; } #kosice-sustainability-map .leaflet-popup-content-wrapper { border-radius: 12px; } #kosice-sustainability-map .leaflet-popup-content { margin: 16px 16px 14px; } #kosice-sustainability-map .ksm-empty { text-align: center; padding: 30px 0; color: #6b7280; } @media (max-width: 767px) { #kosice-sustainability-map .ksm-title { font-size: 24px; } #kosice-sustainability-map .ksm-subtitle { font-size: 15px; } #kosice-sustainability-map .ksm-map-wrapper, #kosice-sustainability-map .ksm-map { min-height: 520px; } #kosice-sustainability-map .ksm-content-grid { flex-direction: column; } #kosice-sustainability-map .ksm-content-side { width: 100%; min-width: 0; } #kosice-sustainability-map .ksm-side-box { padding-left: 0; border-left: none; border-top: 1px solid #e5e7eb; padding-top: 18px; } #kosice-sustainability-map .ksm-funding-overlay { max-width: 180px; } }
Green Explorers
Interactive map of sustainable development in Košice
Click on a marker on the map or expand an item in the list to view details about the location.
Filter categories

(function () { const root = document.getElementById("kosice-sustainability-map"); if (!root) return; const rawPlacesData = [ { "Category": "Zero waste and eco shops", "Name": "Na Kôpke", "Description": "Na Kôpke is one of the pioneering zero-waste shops in Košice. Customers bring their own containers to buy food, toiletries, and household goods by weight, actively reducing single-use plastics in their daily lives. It is an ideal place for a conversation about packaging-free consumption and circular economy principles in a very practical, everyday context.", "Address": "Park Angelinum 13, 040 01 Košice", "Contact": "+421 950 377 787\nnakopke@gmail.com", "OpeningHours": "Mon–Fri: 10:00–13:00, 14:00–18:00\nSat: 10:00–13:00\nSun: closed", "Website": "nakopke.sk", "Lat": 48.7298683, "Lng": 21.2494567 }, { "Category": "Zero waste and eco shops", "Name": "Košická špajza", "Description": "Košická špajza is a packaging-free shop in the city centre where customers bring their own jars and containers for bulk food, oils, or cleaning products sold by weight. The shop shows that an alternative way of shopping without unnecessary packaging is fully possible even in an everyday urban environment, making it an excellent example for the topic of waste reduction.", "Address": "Poštová 14, 040 01 Košice", "Contact": "—", "OpeningHours": "Mon–Fri: 12:00–16:00\nSat–Sun: closed", "Website": "kosickaspajza.sk", "Lat": 48.7222117, "Lng": 21.2531173 }, { "Category": "Zero waste and eco shops", "Name": "Verdevana", "Description": "Verdevana is an eco shop focused on natural products with minimal packaging, from cosmetics to household cleaning products. It helps participants of the city quest understand how daily shopping decisions affect waste and carbon footprint, while also showing that sustainable alternatives are easily accessible even on the main street.", "Address": "Ludmanská 5, 040 01 Košice", "Contact": "+421 915 370 681\nludka@verdevana.sk", "OpeningHours": "Mon, Tue, Thu: 13:00–17:00\nWed: 11:00–18:00\nFri–Sun: closed", "Website": "verdevana.sk", "Lat": 48.7137793, "Lng": 21.2640169 }, { "Category": "Zero waste and eco shops", "Name": "Rezke", "Description": "Rezke is a unique clothing swap boutique where things are not bought or sold, but exchanged. Customers bring a piece of clothing they no longer wear and take away another, thus closing the cycle in the fashion industry. For the city quest, it is an excellent example of a sharing economy and extending the life cycle of products as an alternative to fast fashion.", "Address": "Mäsiarska 26, 040 01 Košice\n(also at Stanica Košice)", "Contact": "—", "OpeningHours": "Current schedule can be found on the website", "Website": "rezke.sk", "Lat": 48.7219339, "Lng": 21.2542848 }, { "Category": "Zero waste and eco shops", "Name": "Bylinkáreň & Čajovňa – Bioinštitút plus", "Description": "This herbalist shop and tea room offers medicinal herbs, natural products, and advice on plant-based health care. It connects traditional nature knowledge with a modern approach to sustainable self-care and encourages visitors to look beyond chemically burdened alternatives and rediscover locally grown plants.", "Address": "Hrnčiarska 20, 040 01 Košice", "Contact": "+421 918 475 640\n+421 55 625 52 07\nbylinkar@bylinkar.sk", "OpeningHours": "Mon–Fri: 09:00–17:00\nSat–Sun: closed", "Website": "bylinkar.sk", "Lat": 48.724249, "Lng": 21.2589403 }, { "Category": "Local and organic food", "Name": "Náš Dvor – Potraviny nie otraviny", "Description": "Náš Dvor sells honest Slovak food from small farmers and producers, without unnecessary additives and with an emphasis on short supply chains. The shop shows how local shopping reduces transport emissions and supports rural communities. For the city quest, it is a model example of responsible consumption in its most practical, everyday form.", "Address": "Hlavná 16, 040 01 Košice", "Contact": "+421 918 036 699\nkosice@potravinynieotraviny.sk", "OpeningHours": "Tue–Fri: 11:00–19:00\nSat: 08:00–14:00\nSun–Mon: closed", "Website": "potravinynieotraviny.sk", "Lat": 48.7188037, "Lng": 21.2579289 }, { "Category": "Local and organic food", "Name": "Dominikánske námestie Market", "Description": "This open-air market on Dominikánske námestie has existed in some form since the 1830s. Sellers offer fresh fruit, vegetables, honey, and flowers here, often grown by their own hands. The 'Gold Seller' label helps visitors identify verified local producers. The market is an excellent illustration of short food chains, seasonality, and the value of direct contact between producer and customer.", "Address": "Dominikánske námestie, 040 01 Košice", "Contact": "—", "OpeningHours": "Mon–Sat: mornings (approx. 06:00–12:00)\nSun: closed", "Website": "—", "Lat": 48.720647, "Lng": 21.255098 }, { "Category": "Parks and nature", "Name": "Mestský park Košice", "Description": "The city park in the heart of Košice is one of the oldest municipal green spaces in the region. It functions as the green lungs of the city, regulating temperature, absorbing CO₂, and supporting urban biodiversity. It is a natural starting point for a discussion about the role of green infrastructure in the fight against heat islands and the adaptation of cities to climate change.", "Address": "Mestský park, 040 01 Košice", "Contact": "—", "OpeningHours": "Open 24/7 (outdoors)", "Website": "—", "Lat": 48.7229604, "Lng": 21.2628203 }, { "Category": "Parks and nature", "Name": "Areál Anička", "Description": "Anička is a vast recreational area of approximately 50 hectares by the Hornád River, full of greenery and complemented by a mineral spring, mini-golf, mini zoo, workout park, and a summer swimming pool. Information boards about local birds and waste sorting are placed around the area. The site is ideal for exploring the relationship between the city and nature and how urban green spaces fulfill both ecological and social functions.", "Address": "Pod šiancom 5, 040 01 Košice – Sever", "Contact": "+421 918 523 304", "OpeningHours": "Daily: 09:00–18:00\n(seasonal changes possible)", "Website": "arealanicka.sk", "Lat": 48.7449643, "Lng": 21.2545401 }, { "Category": "Community spaces and culture", "Name": "Tabačka Kulturfabrik", "Description": "Tabačka Kulturfabrik is an independent cultural center located in a renovated former tobacco factory. It organizes exhibitions, concerts, and community events with an emphasis on participation and social inclusion. For the city quest, it is a demonstration of how the adaptive reuse of existing buildings can reduce the environmental footprint of cultural institutions while revitalizing city districts.", "Address": "Gorkého 2, 040 01 Košice", "Contact": "+421 905 545 986\ninfo@tabacka.sk", "OpeningHours": "Mon, Tue, Thu: 09:00–24:00\nWed, Fri: 09:00–01:00\nSat: 15:00–01:00\nSun: closed", "Website": "tabacka.sk", "Lat": 48.72903, "Lng": 21.2559594 }, { "Category": "Community spaces and culture", "Name": "Spoločenský pavilón – Community Garden", "Description": "The community garden at the Spoločenský pavilón is a participatory project in which city residents grow vegetables and herbs together. The garden strengthens social bonds, raises awareness about local food production, and brings a bit of biodiversity back to the urban landscape, thereby very tangibly connecting sustainability with building a community.", "Address": "Medická 6, 040 11 Košice", "Contact": "—", "OpeningHours": "Accessible during daylight (outdoors)", "Website": "—", "Lat": 48.7196527, "Lng": 21.2400015 }, { "Category": "Community spaces and culture", "Name": "Community Café KVP", "Description": "Community Café KVP in Drocárov Park was a city project that breathed new life into a neglected building in the KVP housing estate and turned it into a neighborhood center for coffee, events, and community meetings. For the city quest, it shows how the restoration of public space through community investments is an important part of sustainable and pleasant neighborhoods.", "Address": "Drocárov Park 3110/1, 040 23 Košice – KVP", "Contact": "—", "OpeningHours": "Mon: 14:00–20:00\nTue–Fri: 10:00–20:00\nSat–Sun: 15:00–20:00", "Website": "mckvp.sk", "Lat": 48.70976, "Lng": 21.2152521 }, { "Category": "Community spaces and culture", "Name": "Na Dlani Café and Wine Bar", "Description": "Na Dlani is a pleasant neighborhood café and wine bar on Hlinkova Street that prioritizes Slovak wines and local ingredients. In the context of the city quest, it serves as a quiet place to reflect on local gastronomy and the relationship between responsible drink choices and supporting regional producers.", "Address": "Hlinkova 9, 040 01 Košice – Sever", "Contact": "—", "OpeningHours": "Please check current opening hours directly at the establishment", "Website": "—", "Lat": 48.7395402, "Lng": 21.2522249 }, { "Category": "Green mobility", "Name": "Cyklokuchyňa Košice", "Description": "Cyklokuchyňa is a community bicycle repair workshop where cyclists can repair their bikes themselves with the help of shared tools and experienced volunteers. It promotes a culture of repair instead of replacement, reduces waste from discarded bicycles, and is a great example of a sharing economy in the field of sustainable urban transport.", "Address": "Čárskeho 7, 040 01 Košice", "Contact": "—", "OpeningHours": "Mon–Fri: 10:00–12:00, 13:00–18:00", "Website": "cke.sk", "Lat": 48.7389989, "Lng": 21.2540164 }, { "Category": "Green mobility", "Name": "ANTIK SmartWay – shared e-scooters and bikes", "Description": "ANTIK SmartWay is a Košice-based shared micromobility platform that offers bikes, e-bikes, and e-scooters via a mobile app. The stationless system allows for flexible movement around the city and can replace short car trips. Specifically, it shows how technology can reduce transport emissions and how shared mobility services contribute to a cleaner urban environment.", "Address": "Throughout the city (flexible zones in Košice)", "Contact": "+421 650 777 888\nsmartway@antik.sk", "OpeningHours": "24/7 via mobile app", "Website": "antiksmartway.sk", "Lat": 48.7211, "Lng": 21.2579 }, { "Category": "Ecological architecture", "Name": "Bastion Office Center", "Description": "The Bastion Office Center on Továrenská Street is only the second building in Košice with LEED Gold certification. It uses groundwater to flush toilets, cleans incoming air with plasma ionization filters, measures CO₂ in every room for automatic ventilation control, and uses LED lighting set with user comfort in mind. It is a living demonstration of how green building standards can significantly reduce the operational energy demands of a facility.", "Address": "Továrenská 4, 040 01 Košice", "Contact": "—", "OpeningHours": "Exterior freely accessible; interior: only during working days", "Website": "bastion-kosice.sk", "Lat": 48.7266457, "Lng": 21.2553168 } ]; const placesData = rawPlacesData.map((place, index) => ({ id: index + 1, category: place.Category, name: place.Name, description: place.Description, address: place.Address, contact: place.Contact, openingHours: place.OpeningHours, website: place.Website, lat: place.Lat, lng: place.Lng })); const CATEGORY_STYLES = { "Zero waste and eco shops": { color: "#16a34a", bgColor: "#16a34a", iconPath: "M18 4h-3V2h-2v2H9V2H7v2H4c-1.1 0-2 .9-2 2v13c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 15H4V10h14v9zm0-11H4V6h14v2zM9 13H7v4h2v-4zm4 0h-2v4h2v-4zm4 0h-2v4h2v-4z" }, "Local and organic food": { color: "#ea580c", bgColor: "#ea580c", iconPath: "M11 9H9V2H7v7H5V2H3v7c0 2.12 1.66 3.84 3.75 3.97V22h2.5v-9.03C11.34 12.84 13 11.12 13 9V2h-2v7zm5-3v8h2.5v8H21V2c-2.76 0-5 2.24-5 4z" }, "Parks and nature": { color: "#059669", bgColor: "#059669", iconPath: "M12 2 6 10h4l-3 5h3v7h4v-7h3l-3-5h4z" }, "Community spaces and culture": { color: "#7c3aed", bgColor: "#7c3aed", iconPath: "M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5s-3 1.34-3 3 1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5C15 14.17 10.33 13 8 13zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.98 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z" }, "Green mobility": { color: "#2563eb", bgColor: "#2563eb", iconPath: "M5.5 17A2.5 2.5 0 1 0 5.5 22 2.5 2.5 0 1 0 5.5 17zM18.5 17A2.5 2.5 0 1 0 18.5 22 2.5 2.5 0 1 0 18.5 17zM15.82 8l-2.69 3.5H9.5l1.96-3H15.82zM19 6h-3.79c-.63 0-1.22.3-1.59.8L10.2 11.2C9.89 11.68 9.35 12 8.77 12H7V9H5v3c0 1.1.9 2 2 2h1.77c1.24 0 2.4-.62 3.09-1.65L12.45 12h2.93l1.52 3.27C16.33 15.73 16 16.34 16 17c0 1.38 1.12 2.5 2.5 2.5S21 18.38 21 17c0-1.12-.73-2.06-1.74-2.39L17 10h2V6z" }, "Ecological architecture": { color: "#475569", bgColor: "#475569", iconPath: "M4 22h16v-2H4v2zm2-4h3V8H6v10zm5 0h3V2h-3v16zm5 0h3v-7h-3v7z" } }; const categories = [...new Set(placesData.map(place => place.category))]; let activeCategories = [...categories]; let markerObjects = {}; let map; let markersLayer; const accordionContainer = root.querySelector("#ksm-accordion-container"); const filtersContainer = root.querySelector("#ksm-filters-list"); function loadLeafletAssets() { return new Promise((resolve, reject) => { if (window.L) { resolve(); return; } const leafletCssId = "ksm-leaflet-css"; const leafletJsId = "ksm-leaflet-js"; if (!document.getElementById(leafletCssId)) { const link = document.createElement("link"); link.id = leafletCssId; link.rel = "stylesheet"; link.href = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"; document.head.appendChild(link); } const existingScript = document.getElementById(leafletJsId); if (existingScript) { existingScript.addEventListener("load", () => resolve()); existingScript.addEventListener("error", () => reject(new Error("Failed to load Leaflet JS."))); return; } const script = document.createElement("script"); script.id = leafletJsId; script.src = "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"; script.onload = () => resolve(); script.onerror = () => reject(new Error("Failed to load Leaflet JS.")); document.body.appendChild(script); }); } function getCategoryStyle(category) { return CATEGORY_STYLES[category] || { color: "#16a34a", bgColor: "#16a34a", iconPath: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z" }; } function escapeHtml(value) { return String(value) .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll("\"", """) .replaceAll("'", "'"); } function formatMultilineText(value) { return escapeHtml(value).replace(/\n/g, "<br>"); } function formatContact(contact) { if (!contact || contact.trim() === "" || contact.trim() === "—") { return "Contact details not available"; } return contact.split("\n").map(line => { const trimmed = line.trim(); if (trimmed.includes("@") && !trimmed.includes(" ")) { return `<a href="mailto:${escapeHtml(trimmed)}">${escapeHtml(trimmed)}</a>`; } return escapeHtml(trimmed); }).join("<br>"); } function formatWebsite(website) { if (!website || website.trim() === "" || website.trim() === "—") { return "Website not available"; } const normalized = website.startsWith("http") ? website : `https://${website}`; return `<a href="${normalized}" target="_blank" rel="noopener noreferrer">${escapeHtml(website)}</a>`; } function createSvgIcon(color, iconPath) { return L.divIcon({ className: "ksm-custom-pin", html: ` <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="48" height="48" style="filter: drop-shadow(2px 4px 6px rgba(0,0,0,0.3));"> <path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z" fill="${color}" /> <circle cx="12" cy="9" r="3.5" fill="rgba(255,255,255,0.18)"></circle> <path d="${iconPath}" fill="white" transform="translate(7, 4) scale(0.42)"></path> </svg> `, iconSize: [40, 40], iconAnchor: [20, 40], popupAnchor: [4, -35] }); } function getFilterButtonClass(isActive) { return `ksm-filter-btn ${isActive ? "active" : "inactive"}`; } function renderFilters() { filtersContainer.innerHTML = ""; categories.forEach(category => { const button = document.createElement("button"); const style = getCategoryStyle(category); const isActive = activeCategories.includes(category); button.textContent = category; button.className = getFilterButtonClass(isActive); if (isActive) { button.style.background = style.bgColor; button.style.color = "#ffffff"; } button.addEventListener("click", () => { const active = activeCategories.includes(category); activeCategories = active ? activeCategories.filter(item => item !== category) : [...activeCategories, category]; renderFilters(); renderMarkersAndList(); }); filtersContainer.appendChild(button); }); } function renderMarkersAndList() { markersLayer.clearLayers(); markerObjects = {}; accordionContainer.innerHTML = ""; const visibleMarkers = []; categories.forEach(category => { if (!activeCategories.includes(category)) return; const categoryPlaces = placesData.filter(place => place.category === category); if (!categoryPlaces.length) return; const categoryStyle = getCategoryStyle(category); const sectionHeader = document.createElement("h3"); sectionHeader.className = "ksm-category-heading"; sectionHeader.innerHTML = ` <span class="ksm-color-dot" style="background:${categoryStyle.bgColor}"></span> ${escapeHtml(category)} `; accordionContainer.appendChild(sectionHeader); categoryPlaces.forEach(place => { const style = getCategoryStyle(place.category); const icon = createSvgIcon(style.color, style.iconPath); const addressHtml = formatMultilineText(place.address); const hoursHtml = formatMultilineText(place.openingHours || "Opening hours not available"); const contactHtml = formatContact(place.contact); const websiteHtml = formatWebsite(place.website); const popupContent = ` <div style="font-family:Arial,sans-serif;min-width:240px;max-width:280px;"> <h3 style="margin:0 0 8px;font-size:18px;line-height:1.3;font-weight:700;color:#111827;">${escapeHtml(place.name)}</h3> <span style="display:inline-block;margin-bottom:12px;border-radius:6px;padding:4px 8px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;color:#fff;background:${style.bgColor};"> ${escapeHtml(place.category)} </span> <p style="margin:0 0 12px;font-size:14px;line-height:1.5;color:#374151;">${escapeHtml(place.description)}</p> <div style="margin-top:8px;padding-top:8px;border-top:1px solid #f3f4f6;font-size:12px;color:#4b5563;"> <div style="margin-bottom:8px;"><strong style="color:#1f2937;">Address:</strong><br>${addressHtml}</div> <div style="margin-bottom:8px;"><strong style="color:#1f2937;">Opening hours:</strong><br>${hoursHtml}</div> <div style="margin-bottom:8px;"><strong style="color:#1f2937;">Contact:</strong><br>${contactHtml}</div> <div><strong style="color:#1f2937;">Web:</strong><br>${websiteHtml}</div> </div> </div> `; const marker = L.marker([place.lat, place.lng], { icon }) .addTo(markersLayer) .bindPopup(popupContent); markerObjects[place.id] = marker; visibleMarkers.push(marker); const accordionItem = document.createElement("div"); accordionItem.className = "ksm-accordion-item"; accordionItem.innerHTML = ` <div class="ksm-accordion-header" data-id="${place.id}"> <div> <h4 class="ksm-place-title">${escapeHtml(place.name)}</h4> <p class="ksm-place-subtitle">${escapeHtml(place.address.split("\n")[0])}</p> </div> <svg class="ksm-chevron" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path> </svg> </div> <div class="ksm-accordion-content"> <div class="ksm-accordion-content-inner"> <div class="ksm-content-grid"> <div class="ksm-content-main"> <span class="ksm-badge" style="background:${style.bgColor};">${escapeHtml(place.category)}</span> <p class="ksm-description">${escapeHtml(place.description)}</p> </div> <div class="ksm-content-side"> <div class="ksm-side-box"> <div class="ksm-info-block"> <div class="ksm-info-label">Address</div> <div class="ksm-info-value">${addressHtml}</div> </div> <div class="ksm-info-block"> <div class="ksm-info-label">Opening hours</div> <div class="ksm-info-value">${hoursHtml}</div> </div> <div class="ksm-info-block"> <div class="ksm-info-label">Contact</div> <div class="ksm-info-value">${contactHtml}</div> </div> <div class="ksm-info-block" style="margin-bottom:0;"> <div class="ksm-info-label">Website</div> <div class="ksm-info-value">${websiteHtml}</div> </div> </div> </div> </div> </div> </div> `; accordionContainer.appendChild(accordionItem); }); }); if (!visibleMarkers.length) { accordionContainer.innerHTML = '<p class="ksm-empty">No places match the selected criteria.</p>'; map.setView([48.7209, 21.2578], 13); return; } const bounds = L.featureGroup(visibleMarkers).getBounds(); if (bounds.isValid()) { map.fitBounds(bounds.pad(0.18)); } } function bindAccordionEvents() { accordionContainer.addEventListener("click", function (event) { const header = event.target.closest(".ksm-accordion-header"); if (!header) return; const currentItem = header.parentElement; const content = currentItem.querySelector(".ksm-accordion-content"); const icon = header.querySelector(".ksm-chevron"); const placeId = header.dataset.id; const isOpen = Boolean(content.style.maxHeight); accordionContainer.querySelectorAll(".ksm-accordion-content").forEach(item => { item.style.maxHeight = null; }); accordionContainer.querySelectorAll(".ksm-chevron").forEach(item => { item.classList.remove("rotated"); }); if (!isOpen) { content.style.maxHeight = content.scrollHeight + "px"; icon.classList.add("rotated"); const marker = markerObjects[placeId]; if (marker) { map.flyTo(marker.getLatLng(), 15, { duration: 0.8 }); marker.openPopup(); } } }); } async function initMap() { try { await loadLeafletAssets(); map = L.map("ksm-map-canvas", { scrollWheelZoom: true }).setView([48.7209, 21.2578], 13); L.tileLayer("https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png", { subdomains: "abcd", maxZoom: 19, attribution: '© OpenStreetMap contributors © CARTO' }).addTo(map); markersLayer = L.layerGroup().addTo(map); renderFilters(); renderMarkersAndList(); bindAccordionEvents(); setTimeout(() => { map.invalidateSize(); }, 300); window.addEventListener("resize", function () { map.invalidateSize(); }); } catch (error) { root.innerHTML = '<div style="padding:20px;background:#fff3f3;border:1px solid #f5c2c2;border-radius:12px;color:#991b1b;">Map failed to load. Check if WordPress or a security plugin is blocking external scripts.</div>'; } } initMap(); })();

2024-2-PL01-KA210-ADU-000279352
