{"id":18064,"date":"2025-12-02T16:37:51","date_gmt":"2025-12-02T15:37:51","guid":{"rendered":"https:\/\/management-kolloquium.de\/?page_id=18064"},"modified":"2026-10-21T12:03:29","modified_gmt":"2026-10-21T10:03:29","slug":"fotos-mmk-heilbronn-2025","status":"publish","type":"page","link":"https:\/\/management-kolloquium.de\/en\/fotos-mmk-heilbronn-2025\/","title":{"rendered":"Fotos MMK Heilbronn 2025"},"content":{"rendered":"\n    <section  class=\"w-full max-w-boxed mx-auto md:p-wb-xxs grid grid-cols-12 justify-center items-start md:!pt-[50px] overflow-hidden \">\n\n\n        <div class=\"w-[calc(100%-20px)] ml-[10px] md:ml-0 mt-[-80px] md:mt-0 md:w-full col-span-12 md:col-span-7 bg-dunkelblau rounded-sm px-wb-s py-wb-m md:p-wb-m flex flex-col items-start gap-wb-xs relative z-10 order-last md:order-first\">\n\n            <svg class=\"absolute top-[-20px]  right-[-30px]\" width=\"205\" height=\"56\" viewBox=\"0 0 205 56\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                <path class=\"horizontal-reveal\" d=\"M135.833 27.6406L113.434 6.89515L90.9905 27.637L68.4922 6.8916L46.0673 27.637L23.6204 6.91645L3.60119 25.9727L0 22.4312L23.5654 0.00012207L46.0637 20.7668L68.4849 0.0249626L90.9868 20.7704L113.437 0.021414L135.837 20.7668L158.284 0.0249626L180.73 20.7704L201.457 1.66089L205 5.25568L180.727 27.637L158.284 6.8916L135.833 27.6406Z\" fill=\"#eaff6e\" \/>\n                <path class=\"horizontal-reveal\" d=\"M135.833 55.2811L113.434 34.5357L90.9905 55.2775L68.4922 34.5321L46.0673 55.2775L23.6204 34.5569L3.60119 53.6132L0 50.0717L23.5654 27.6406L46.0637 48.4073L68.4849 27.6655L90.9868 48.4109L113.437 27.6619L135.837 48.4073L158.284 27.6655L180.73 48.4109L201.457 29.3014L205 32.8962L180.727 55.2775L158.284 34.5321L135.833 55.2811Z\" fill=\"#eaff6e\" \/>\n            <\/svg>\n\n\n            <h2 class=\"text-weiss h4-tag\">Heilbronn 25 &#8211; Your MMK Moments to Share<\/h2>\n            <div class=\"text-weiss flex flex-col items-start gap-wb-xxs mt-wb-xxs\">\n\n                <p data-start=\"109\" data-end=\"299\">All photos are available for free download. Important: please use the download function \u2014 it\u2019s the only way to keep the image quality intact. We\u2019re happy if you tag the MMK on <span style=\"text-decoration: underline;\"><a href=\"https:\/\/www.linkedin.com\/feed\/\" target=\"_blank\" rel=\"noopener\">LinkedIn<\/a><\/span> and <a href=\"https:\/\/www.instagram.com\/mmk.muenchen\/\" target=\"_blank\" rel=\"noopener\"><span style=\"text-decoration: underline;\">Instagram<\/span><\/a> when sharing.<br \/>\nThanks for making the community visible!<\/p>\n            <\/div>\n\n            \n\n        <\/div>\n\n\n        <div class=\"col-span-12 md:col-span-5 md:mt-[-50px]  md:ml-[-250px]\">\n\n            <img decoding=\"async\" class=\"w-full h-[300px] md:h-[450px] object-cover rounded-[500px] \" src=\"https:\/\/management-kolloquium.de\/wp-content\/uploads\/2025\/12\/MMK-Heilbronn-Fotos.jpg\" alt=\"MMK Heilbronn Fotos\" \/>\n        <\/div>\n\n\n    <\/section>\n\n\n\n\n\n\n    <div class=\"max-w-[1166px] mx-auto spacer-block block s \"><\/div>\n\n\n\n\n\n\n<section\n    class=\" w-full max-w-boxed mx-auto px-wb-xs flex items-start space-between gap-[20px] flex-wrap\"\n  data-gallery-block=\"gallery-block-block_984200e6a35e1e87a43c2bf61b27d557-1\"\n  data-folder=\"MMK-Heilbronn\"\n  data-ajax=\"https:\/\/management-kolloquium.de\/wp-admin\/admin-ajax.php?action=get_gallery_images\"\n>\n  <style>\n    .gallery-load-more { justify-self: center; }\n\n    \/* Block-lokal: hidden immer zuverl\u00e4ssig *\/\n    [data-gallery-block=\"gallery-block-block_984200e6a35e1e87a43c2bf61b27d557-1\"] .hidden { display: none !important; }\n\n    \/* Spinner Loader *\/\n    [data-gallery-block=\"gallery-block-block_984200e6a35e1e87a43c2bf61b27d557-1\"] .gallery-loader {\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      padding: 40px 0;\n    }\n    [data-gallery-block=\"gallery-block-block_984200e6a35e1e87a43c2bf61b27d557-1\"] .gallery-spinner {\n      width: 48px;\n      height: 48px;\n      border: 4px solid rgba(0,0,0,0.12);\n      border-top-color: #152644;\n      border-radius: 50%;\n      animation: gallery-spin 0.9s linear infinite;\n    }\n    @keyframes gallery-spin { to { transform: rotate(360deg); } }\n  <\/style>\n\n  <div class=\"w-full md:w-[250px] max-w-full bg-gelb text-dunkelblau rounded-sm p-wb-ss flex flex-col items-start gap-[20px]\">\n\n    <input\n      type=\"text\"\n      placeholder=\"Search...\"\n      class=\"speaker-search-input gallery-search-input w-full px-2 py-wb-xxs rounded-sm\"\n    \/>\n\n    <h3 class=\"h6-tag\">Filter<\/h3>\n\n          <div>\n        <p class=\"font-semibold text-left\">Aula<\/p>\n        <ul class=\"flex flex-col items-start gap-wb-xxxs list-none p-0 mt-[5px]\">\n          <li class=\"relative nobefore\">\n            <label class=\"font-medium\">\n              <input\n                type=\"radio\"\n                name=\"filter-group-gallery-block-block_984200e6a35e1e87a43c2bf61b27d557-1-0\"\n                class=\"filter-radio text-[16px] font-semibold appearance-none\"\n                value=\"\"\n                checked\n              >\n              All            <\/label>\n          <\/li>\n\n                      <li class=\"relative nobefore\">\n              <label class=\"font-medium\">\n                <input\n                  type=\"radio\"\n                  name=\"filter-group-gallery-block-block_984200e6a35e1e87a43c2bf61b27d557-1-0\"\n                  class=\"filter-radio text-[16px] font-semibold appearance-none\"\n                  value=\"Aula\"\n                >\n                Aula              <\/label>\n            <\/li>\n                      <li class=\"relative nobefore\">\n              <label class=\"font-medium\">\n                <input\n                  type=\"radio\"\n                  name=\"filter-group-gallery-block-block_984200e6a35e1e87a43c2bf61b27d557-1-0\"\n                  class=\"filter-radio text-[16px] font-semibold appearance-none\"\n                  value=\"Masterclass\"\n                >\n                Masterclass              <\/label>\n            <\/li>\n                      <li class=\"relative nobefore\">\n              <label class=\"font-medium\">\n                <input\n                  type=\"radio\"\n                  name=\"filter-group-gallery-block-block_984200e6a35e1e87a43c2bf61b27d557-1-0\"\n                  class=\"filter-radio text-[16px] font-semibold appearance-none\"\n                  value=\"Foyer\"\n                >\n                Foyer              <\/label>\n            <\/li>\n                      <li class=\"relative nobefore\">\n              <label class=\"font-medium\">\n                <input\n                  type=\"radio\"\n                  name=\"filter-group-gallery-block-block_984200e6a35e1e87a43c2bf61b27d557-1-0\"\n                  class=\"filter-radio text-[16px] font-semibold appearance-none\"\n                  value=\"Speakers Dinner\"\n                >\n                Speakers Dinner              <\/label>\n            <\/li>\n                  <\/ul>\n      <\/div>\n    \n  <\/div>\n\n  <div class=\"flex-1\">\n\n    <div class=\"gallery-grid columns-2 md:columns-3 gap-wb-xs space-y-wb-xs\"><\/div>\n\n    <button\n      type=\"button\"\n      class=\"gallery-load-more main-button mt-wb-ss hidden load-more-images-button\"\n    >\n      <svg class=\"zacken-button\" width=\"77\" height=\"10\" viewBox=\"0 0 77 10\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n        <path d=\"M51.0202 10L42.6068 2.49454L34.1769 9.99872L25.7264 2.49326L17.3033 9.99872L8.87206 2.50225L1.35264 9.39659L0 8.11529L8.8514 0L17.302 7.51316L25.7236 0.00898703L34.1755 7.51444L42.6082 0.00770317L51.0216 7.51316L59.4529 0.00898703L67.8841 7.51444L75.6694 0.600847L77 1.9014L67.8828 9.99872L59.4529 2.49326L51.0202 10Z\" fill=\"#152644\"><\/path>\n      <\/svg>\n      <span class=\"main-button-text\">\n        load more      <\/span>\n    <\/button>\n\n    <!-- Loader -->\n    <div class=\"gallery-loader hidden\" aria-hidden=\"true\">\n      <div class=\"gallery-spinner\"><\/div>\n    <\/div>\n\n    <div class=\"gallery-no-results hidden text-center text-dunkelblau font-semibold rounded-sm bg-lila p-wb-s\">\n      <p class=\"text-weiss\">Nothing found&#8230;<\/p>\n    <\/div>\n\n    <!-- Eigene Fehlermeldung, damit API-Fehler nicht wie \"keine Treffer\" aussehen -->\n    <div class=\"gallery-error hidden text-center text-dunkelblau font-semibold rounded-sm bg-lila p-wb-s\">\n      <p class=\"text-weiss\">Images could not be loaded. Please try again later.<\/p>\n    <\/div>\n\n  <\/div>\n<\/section>\n\n<script>\n(function () {\n  const LIMIT_INITIAL = 30; \/\/ best-of\n  const LIMIT_FULL    = 60; \/\/ full \/ filter\n\n  const FANCYBOX_OPTIONS = {\n    infinite: false,\n    dragToClose: true,\n    Carousel: { infinite: false },\n    Thumbs: false,\n    Toolbar: { display: { left: [], middle: [], right: ['close'] } }\n  };\n\n  \/\/ Fancybox arbeitet mit Event-Delegation -> nur einmal binden\n  function ensureFancybox() {\n    if (window.__mmkGalleryFancyboxBound) return;\n    if (typeof Fancybox === 'undefined') return;\n    Fancybox.bind('[data-fancybox]', FANCYBOX_OPTIONS);\n    window.__mmkGalleryFancyboxBound = true;\n  }\n\n  function initGallery(block) {\n    \/\/ Guard: jeden Block nur einmal initialisieren\n    if (block.dataset.galleryInit === '1') return;\n    block.dataset.galleryInit = '1';\n\n    const grid        = block.querySelector('.gallery-grid');\n    const loader      = block.querySelector('.gallery-loader');\n    const radios      = block.querySelectorAll('.filter-radio');\n    const searchInput = block.querySelector('.gallery-search-input');\n    const noResults   = block.querySelector('.gallery-no-results');\n    const errorBox    = block.querySelector('.gallery-error');\n    const loadMoreBtn = block.querySelector('.gallery-load-more');\n    const folder      = block.dataset.folder;\n    const ajaxUrl     = block.dataset.ajax;\n\n    if (!grid || !folder || !ajaxUrl) return;\n\n    const state = {\n      reqId: 0,               \/\/ nur die neueste Anfrage darf rendern\n      controller: null,       \/\/ zum Abbrechen veralteter Requests\n      queryType: 'initial',   \/\/ initial | browse | filter | search\n      cursor: null,\n      seen: new Set(),\n      debounce: null\n    };\n\n    function toggle(el, show) {\n      if (el) el.classList.toggle('hidden', !show);\n    }\n\n    function resetGrid() {\n      grid.innerHTML = '';\n      state.seen = new Set();\n      state.cursor = null;\n    }\n\n    \/\/ Startet eine neue Anfrage und macht alle \u00e4lteren ung\u00fcltig\n    function startRequest() {\n      state.reqId++;\n      if (state.controller) state.controller.abort();\n      state.controller = new AbortController();\n\n      toggle(loader, true);\n      toggle(loadMoreBtn, false);\n      toggle(noResults, false);\n      toggle(errorBox, false);\n\n      return { id: state.reqId, signal: state.controller.signal };\n    }\n\n    function isCurrent(id) {\n      return id === state.reqId;\n    }\n\n    function finish(id, { hasMore = false, failed = false } = {}) {\n      if (!isCurrent(id)) return;\n      toggle(loader, false);\n      toggle(loadMoreBtn, hasMore && !failed);\n      if (failed) {\n        toggle(errorBox, true);\n      } else if (grid.children.length === 0) {\n        toggle(noResults, true);\n      }\n    }\n\n    function handleError(id, e) {\n      if (e && e.name === 'AbortError') return; \/\/ bewusst abgebrochen\n      console.error('Gallery API error:', e && e.detail ? e.detail : e);\n      finish(id, { failed: true });\n    }\n\n    function resetFiltersToAll() {\n      radios.forEach(r => { if (r.value === '') r.checked = true; });\n    }\n\n    function getActiveTags() {\n      const tags = [];\n      radios.forEach(rb => {\n        if (rb.checked && rb.value.trim() !== '') tags.push(rb.value.trim());\n      });\n      return tags;\n    }\n\n    async function fetchImages(body, signal) {\n      const res = await fetch(ajaxUrl, {\n        method: 'POST',\n        headers: { 'Content-Type': 'application\/json' },\n        body: JSON.stringify(Object.assign({ folder }, body)),\n        signal\n      });\n\n      let json;\n      try {\n        json = await res.json();\n      } catch (e) {\n        throw new Error('Invalid response (HTTP ' + res.status + ')');\n      }\n\n      if (!json || !json.success) {\n        const err = new Error('API error');\n        err.detail = json ? json.data : null;\n        throw err;\n      }\n\n      return {\n        images: json.data.images || [],\n        nextCursor: json.data.next_cursor || null\n      };\n    }\n\n    function render(images) {\n      images.forEach(img => {\n        if (!img || !img.public_id) return;\n        if (state.seen.has(img.public_id)) return;\n        state.seen.add(img.public_id);\n\n        const card = document.createElement('div');\n        card.className = 'card';\n\n        const wrap = document.createElement('div');\n        wrap.className = 'relative group bg-neutral-200 animate-pulse overflow-hidden rounded-sm';\n\n        const link = document.createElement('a');\n        link.href = img.full_url;\n        link.setAttribute('data-fancybox', img.public_id);\n\n        const image = document.createElement('img');\n        image.src = img.thumb_url;\n        image.alt = '';\n        image.loading = 'lazy';\n        image.className = 'w-full h-auto opacity-0 transition-opacity duration-300 rounded-sm';\n        const done = () => {\n          image.style.opacity = '1';\n          wrap.classList.remove('animate-pulse');\n        };\n        image.addEventListener('load', done);\n        image.addEventListener('error', done);\n\n        const download = document.createElement('a');\n        download.href = img.download_url;\n        download.target = '_blank';\n        download.rel = 'noopener';\n        download.className = 'absolute top-2 right-2 bg-gelb text-dunkelblau font-semibold text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition';\n        download.textContent = 'Download';\n\n        link.appendChild(image);\n        wrap.appendChild(link);\n        wrap.appendChild(download);\n        card.appendChild(wrap);\n        grid.appendChild(card);\n      });\n\n      ensureFancybox();\n    }\n\n    \/\/ ---------- Loads ----------\n\n    \/\/ Best-of beim Seitenaufruf\n    async function loadInitial() {\n      state.queryType = 'initial';\n      resetGrid();\n      const { id, signal } = startRequest();\n\n      try {\n        const { images } = await fetchImages({ mode: 'initial', limit: LIMIT_INITIAL }, signal);\n        if (!isCurrent(id)) return;\n\n        \/\/ Kein best-of-Ordner bzw. leer -> direkt alle Bilder laden statt \"Nothing found\"\n        if (images.length === 0) {\n          state.queryType = 'browse';\n          loadFull();\n          return;\n        }\n\n        render(images);\n        finish(id, { hasMore: true }); \/\/ \"load more\" startet den full browse\n      } catch (e) {\n        handleError(id, e);\n      }\n    }\n\n    \/\/ Full browse oder Filter (paginiert)\n    async function loadFull({ reset = false } = {}) {\n      if (reset) resetGrid();\n      if (state.queryType === 'initial') state.queryType = 'browse';\n\n      const tags = state.queryType === 'filter' ? getActiveTags() : [];\n      const { id, signal } = startRequest();\n\n      try {\n        const { images, nextCursor } = await fetchImages({\n          mode: 'full',\n          cursor: state.cursor,\n          limit: LIMIT_FULL,\n          tags\n        }, signal);\n        if (!isCurrent(id)) return;\n\n        render(images);\n        state.cursor = nextCursor;\n        finish(id, { hasMore: !!nextCursor });\n      } catch (e) {\n        handleError(id, e);\n      }\n    }\n\n    \/\/ Suche: ein einziger Request, Server filtert, kein \"load more\"\n    async function loadSearch(term) {\n      state.queryType = 'search';\n      resetFiltersToAll();\n      resetGrid();\n      const { id, signal } = startRequest();\n\n      try {\n        const { images } = await fetchImages({ mode: 'search', search: term }, signal);\n        if (!isCurrent(id)) return;\n\n        render(images);\n        finish(id, { hasMore: false });\n      } catch (e) {\n        handleError(id, e);\n      }\n    }\n\n    \/\/ ---------- Events ----------\n\n    \/\/ Filter: Suche leeren, dann Filter oder \"All\" (= full browse)\n    radios.forEach(rb => rb.addEventListener('change', () => {\n      if (searchInput) searchInput.value = '';\n      clearTimeout(state.debounce);\n      state.queryType = getActiveTags().length ? 'filter' : 'browse';\n      loadFull({ reset: true });\n    }));\n\n    \/\/ Suche: Filter zur\u00fccksetzen, alle Treffer laden\n    if (searchInput) {\n      searchInput.addEventListener('input', () => {\n        clearTimeout(state.debounce);\n        state.debounce = setTimeout(() => {\n          const term = searchInput.value.trim();\n          if (term) {\n            loadSearch(term);\n          } else {\n            resetFiltersToAll();\n            loadInitial();\n          }\n        }, 350);\n      });\n    }\n\n    \/\/ Load more: im best-of startet es den full browse, sonst n\u00e4chste Seite\n    if (loadMoreBtn) {\n      loadMoreBtn.addEventListener('click', () => {\n        if (state.queryType === 'search') return;\n        loadFull();\n      });\n    }\n\n    \/\/ ---------- Init ----------\n    loadInitial();\n  }\n\n  function boot() {\n    document.querySelectorAll('[data-gallery-block]').forEach(initGallery);\n  }\n\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', boot);\n  } else {\n    boot();\n  }\n})();\n<\/script>\n\n\n\n\n\n    <div class=\"max-w-[1166px] mx-auto spacer-block block l \"><\/div>\n\n\n\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":19,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-18064","page","type-page","status-publish","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/management-kolloquium.de\/en\/wp-json\/wp\/v2\/pages\/18064","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/management-kolloquium.de\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/management-kolloquium.de\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/management-kolloquium.de\/en\/wp-json\/wp\/v2\/users\/19"}],"replies":[{"embeddable":true,"href":"https:\/\/management-kolloquium.de\/en\/wp-json\/wp\/v2\/comments?post=18064"}],"version-history":[{"count":14,"href":"https:\/\/management-kolloquium.de\/en\/wp-json\/wp\/v2\/pages\/18064\/revisions"}],"predecessor-version":[{"id":22177,"href":"https:\/\/management-kolloquium.de\/en\/wp-json\/wp\/v2\/pages\/18064\/revisions\/22177"}],"wp:attachment":[{"href":"https:\/\/management-kolloquium.de\/en\/wp-json\/wp\/v2\/media?parent=18064"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}