Balkan Alliance for Local Media is a regional network of small, independent, community, and minority media from Kosovo, Bosnia and Herzegovina, and Serbia.

About Us

We are part of the same landscape described in the Call for Change – media outlets working under pressure, often with limited means, but with a strong sense of responsibility toward the people and places we serve. Our collaboration is grounded in ethics, transparency, and solidarity, and in a shared commitment to journalism that protects the public interest, strengthens communities, and helps people across the region hear and understand one another again.

Call for Change

Small, independent, local, community, and minority media in Kosovo, Bosnia and Herzegovina, and Serbia face the same reality: political pressure, unstable funding, and donor systems that often fail to reflect what local journalism truly needs. These outlets are often the last places where people from divided communities still hear one another. They report from overlooked towns and villages with limited resources and deep commitment to public service.

Stories

The Call

(function() { console.log(“BALM Gallery Slider: Infinite Loop Loaded v1.0”); var container = document.getElementById(‘balm-slider-696fb20b94cd9’); if (!container) return; var slides = container.querySelector(‘.sv-slides’); var originalSlideElements = container.querySelectorAll(‘.sv-slide’); // Clone first and last slides for infinite loop var firstSlide = originalSlideElements[0]; var lastSlide = originalSlideElements[originalSlideElements.length – 1]; var cloneFirst = firstSlide.cloneNode(true); var cloneLast = lastSlide.cloneNode(true); // Add clones to DOM slides.appendChild(cloneFirst); slides.insertBefore(cloneLast, firstSlide); // Re-query slides to include clones var allSlides = container.querySelectorAll(‘.sv-slide’); var slideCount = allSlides.length; // Now N + 2 var prevBtn = container.querySelector(‘.sv-prev’); var nextBtn = container.querySelector(‘.sv-next’); // Start at index 1 because index 0 is the clone of the last slide var currentIndex = 1; var isTransitioning = false; // Initialize position slides.style.transform = ‘translateX(-100%)’; function updateSlide(index) { if (isTransitioning) return; currentIndex = index; slides.style.transition = ‘transform 0.5s ease’; slides.style.transform = ‘translateX(-‘ + (currentIndex * 100) + ‘%)’; isTransitioning = true; } // Jump to a specific index without animation (teleport) function jumpToSlide(index) { slides.style.transition = ‘none’; currentIndex = index; slides.style.transform = ‘translateX(-‘ + (currentIndex * 100) + ‘%)’; } slides.addEventListener(‘transitionend’, function() { isTransitioning = false; if (currentIndex === 0) { // We are at the cloned last slide, jump to real last slide jumpToSlide(slideCount – 2); } if (currentIndex === slideCount – 1) { // We are at the cloned first slide, jump to real first slide jumpToSlide(1); } }); prevBtn.addEventListener(‘click’, function(e) { e.preventDefault(); if (isTransitioning) return; updateSlide(currentIndex – 1); }); nextBtn.addEventListener(‘click’, function(e) { e.preventDefault(); if (isTransitioning) return; updateSlide(currentIndex + 1); }); // Touch Support var touchstartX = 0; var touchstartY = 0; var touchcurrentX = 0; var touchcurrentY = 0; var isDragging = false; container.addEventListener(‘touchstart’, function(e) { if (isTransitioning) return; isDragging = true; touchstartX = e.changedTouches[0].screenX; touchstartY = e.changedTouches[0].screenY; touchcurrentX = touchstartX; slides.style.transition = ‘none’; }, {passive: true}); container.addEventListener(‘touchmove’, function(e) { if (!isDragging) return; touchcurrentX = e.changedTouches[0].screenX; touchcurrentY = e.changedTouches[0].screenY; var diffX = touchcurrentX – touchstartX; var diffY = touchcurrentY – touchstartY; // Allow vertical scroll if (Math.abs(diffY) > Math.abs(diffX)) return; if (e.cancelable) e.preventDefault(); var sliderWidth = container.offsetWidth; var movePercent = (diffX / sliderWidth) * 100; // The base is determined by the current index (1-based now) var currentBase = -(currentIndex * 100); slides.style.transform = ‘translateX(‘ + (currentBase + movePercent) + ‘%)’; }, {passive: false}); container.addEventListener(‘touchend’, function(e) { if (!isDragging) return; isDragging = false; var diff = touchcurrentX – touchstartX; var threshold = 50; if (Math.abs(diff) > threshold) { if (diff > 0) updateSlide(currentIndex – 1); else updateSlide(currentIndex + 1); } else { updateSlide(currentIndex); } }); })();
Read More

Where’s Our Logo?

(function() { console.log(“BALM Gallery Slider: Infinite Loop Loaded v1.0”); var container = document.getElementById(‘balm-slider-696fb20b95806’); if (!container) return; var slides = container.querySelector(‘.sv-slides’); var originalSlideElements = container.querySelectorAll(‘.sv-slide’); // Clone first and last slides for infinite loop var firstSlide = originalSlideElements[0]; var lastSlide = originalSlideElements[originalSlideElements.length – 1]; var cloneFirst = firstSlide.cloneNode(true); var cloneLast = lastSlide.cloneNode(true); // Add clones to DOM slides.appendChild(cloneFirst); slides.insertBefore(cloneLast, firstSlide); // Re-query slides to include clones var allSlides = container.querySelectorAll(‘.sv-slide’); var slideCount = allSlides.length; // Now N + 2 var prevBtn = container.querySelector(‘.sv-prev’); var nextBtn = container.querySelector(‘.sv-next’); // Start at index 1 because index 0 is the clone of the last slide var currentIndex = 1; var isTransitioning = false; // Initialize position slides.style.transform = ‘translateX(-100%)’; function updateSlide(index) { if (isTransitioning) return; currentIndex = index; slides.style.transition = ‘transform 0.5s ease’; slides.style.transform = ‘translateX(-‘ + (currentIndex * 100) + ‘%)’; isTransitioning = true; } // Jump to a specific index without animation (teleport) function jumpToSlide(index) { slides.style.transition = ‘none’; currentIndex = index; slides.style.transform = ‘translateX(-‘ + (currentIndex * 100) + ‘%)’; } slides.addEventListener(‘transitionend’, function() { isTransitioning = false; if (currentIndex === 0) { // We are at the cloned last slide, jump to real last slide jumpToSlide(slideCount – 2); } if (currentIndex === slideCount – 1) { // We are at the cloned first slide, jump to real first slide jumpToSlide(1); } }); prevBtn.addEventListener(‘click’, function(e) { e.preventDefault(); if (isTransitioning) return; updateSlide(currentIndex – 1); }); nextBtn.addEventListener(‘click’, function(e) { e.preventDefault(); if (isTransitioning) return; updateSlide(currentIndex + 1); }); // Touch Support var touchstartX = 0; var touchstartY = 0; var touchcurrentX = 0; var touchcurrentY = 0; var isDragging = false; container.addEventListener(‘touchstart’, function(e) { if (isTransitioning) return; isDragging = true; touchstartX = e.changedTouches[0].screenX; touchstartY = e.changedTouches[0].screenY; touchcurrentX = touchstartX; slides.style.transition = ‘none’; }, {passive: true}); container.addEventListener(‘touchmove’, function(e) { if (!isDragging) return; touchcurrentX = e.changedTouches[0].screenX; touchcurrentY = e.changedTouches[0].screenY; var diffX = touchcurrentX – touchstartX; var diffY = touchcurrentY – touchstartY; // Allow vertical scroll if (Math.abs(diffY) > Math.abs(diffX)) return; if (e.cancelable) e.preventDefault(); var sliderWidth = container.offsetWidth; var movePercent = (diffX / sliderWidth) * 100; // The base is determined by the current index (1-based now) var currentBase = -(currentIndex * 100); slides.style.transform = ‘translateX(‘ + (currentBase + movePercent) + ‘%)’; }, {passive: false}); container.addEventListener(‘touchend’, function(e) { if (!isDragging) return; isDragging = false; var diff = touchcurrentX – touchstartX; var threshold = 50; if (Math.abs(diff) > threshold) { if (diff > 0) updateSlide(currentIndex – 1); else updateSlide(currentIndex + 1); } else { updateSlide(currentIndex); } }); })();
Read More

Trust Over Perfection

(function() { console.log(“BALM Gallery Slider: Infinite Loop Loaded v1.0”); var container = document.getElementById(‘balm-slider-696fb20b96ab5’); if (!container) return; var slides = container.querySelector(‘.sv-slides’); var originalSlideElements = container.querySelectorAll(‘.sv-slide’); // Clone first and last slides for infinite loop var firstSlide = originalSlideElements[0]; var lastSlide = originalSlideElements[originalSlideElements.length – 1]; var cloneFirst = firstSlide.cloneNode(true); var cloneLast = lastSlide.cloneNode(true); // Add clones to DOM slides.appendChild(cloneFirst); slides.insertBefore(cloneLast, firstSlide); // Re-query slides to include clones var allSlides = container.querySelectorAll(‘.sv-slide’); var slideCount = allSlides.length; // Now N + 2 var prevBtn = container.querySelector(‘.sv-prev’); var nextBtn = container.querySelector(‘.sv-next’); // Start at index 1 because index 0 is the clone of the last slide var currentIndex = 1; var isTransitioning = false; // Initialize position slides.style.transform = ‘translateX(-100%)’; function updateSlide(index) { if (isTransitioning) return; currentIndex = index; slides.style.transition = ‘transform 0.5s ease’; slides.style.transform = ‘translateX(-‘ + (currentIndex * 100) + ‘%)’; isTransitioning = true; } // Jump to a specific index without animation (teleport) function jumpToSlide(index) { slides.style.transition = ‘none’; currentIndex = index; slides.style.transform = ‘translateX(-‘ + (currentIndex * 100) + ‘%)’; } slides.addEventListener(‘transitionend’, function() { isTransitioning = false; if (currentIndex === 0) { // We are at the cloned last slide, jump to real last slide jumpToSlide(slideCount – 2); } if (currentIndex === slideCount – 1) { // We are at the cloned first slide, jump to real first slide jumpToSlide(1); } }); prevBtn.addEventListener(‘click’, function(e) { e.preventDefault(); if (isTransitioning) return; updateSlide(currentIndex – 1); }); nextBtn.addEventListener(‘click’, function(e) { e.preventDefault(); if (isTransitioning) return; updateSlide(currentIndex + 1); }); // Touch Support var touchstartX = 0; var touchstartY = 0; var touchcurrentX = 0; var touchcurrentY = 0; var isDragging = false; container.addEventListener(‘touchstart’, function(e) { if (isTransitioning) return; isDragging = true; touchstartX = e.changedTouches[0].screenX; touchstartY = e.changedTouches[0].screenY; touchcurrentX = touchstartX; slides.style.transition = ‘none’; }, {passive: true}); container.addEventListener(‘touchmove’, function(e) { if (!isDragging) return; touchcurrentX = e.changedTouches[0].screenX; touchcurrentY = e.changedTouches[0].screenY; var diffX = touchcurrentX – touchstartX; var diffY = touchcurrentY – touchstartY; // Allow vertical scroll if (Math.abs(diffY) > Math.abs(diffX)) return; if (e.cancelable) e.preventDefault(); var sliderWidth = container.offsetWidth; var movePercent = (diffX / sliderWidth) * 100; // The base is determined by the current index (1-based now) var currentBase = -(currentIndex * 100); slides.style.transform = ‘translateX(‘ + (currentBase + movePercent) + ‘%)’; }, {passive: false}); container.addEventListener(‘touchend’, function(e) { if (!isDragging) return; isDragging = false; var diff = touchcurrentX – touchstartX; var threshold = 50; if (Math.abs(diff) > threshold) { if (diff > 0) updateSlide(currentIndex – 1); else updateSlide(currentIndex + 1); } else { updateSlide(currentIndex); } }); })();
Read More

News

Balkan Alliance for Local Media launches, issues Call for Change

November 13, 2025 – A unique regional network, the Balkan Alliance for Local Media (BALM), launched today. At the moment, BALM unites 27 independent local, community, and minority media outlets from Kosovo, Bosnia and Herzegovina, and Serbia.

Contact Us