(function () {

  /* ═══════════════════════════════════════════════
     KONFIGURACJA KOLORÓW
     ═══════════════════════════════════════════════ */
  var COLOR_RULES = [
    { keywords: ['dzieci', 'kids', 'academy'], color: 'red'    },
    { keywords: ['ocr'],                       color: 'green'  },
    { keywords: ['ninja'],                     color: 'blue'   },
    { keywords: ['cross'],                     color: 'orange' },
  ];

  function colorForTitle(title) {
    var t = String(title || '').toLowerCase();
    for (var i = 0; i < COLOR_RULES.length; i++) {
      var rule = COLOR_RULES[i];
      for (var j = 0; j < rule.keywords.length; j++) {
        if (t.indexOf(rule.keywords[j]) !== -1) return rule.color;
      }
    }
    return 'gray';
  }

  /* Kolory jako wartości hex/rgba — niezależne od pliku CSS */
  var DOT_COLORS = {
    red:    '#e05555',
    green:  '#4caf6e',
    blue:   '#4a90d9',
    orange: '#e8973a',
    gray:   'rgba(255,255,255,0.35)',
  };

  var STRIPE_COLORS = {
    red:    '#e05555',
    green:  '#4caf6e',
    blue:   '#4a90d9',
    orange: '#e8973a',
    gray:   'rgba(255,255,255,0.25)',
  };

  /* ═══════════════════════════════════════════════
     HELPERY DATY
     ═══════════════════════════════════════════════ */
  function pad2(n) { return (n < 10 ? '0' : '') + n; }

  function ymdFromDate(d) {
    return d.getFullYear() + '-' + pad2(d.getMonth() + 1) + '-' + pad2(d.getDate());
  }

  function dateFromYMD(ymd) {
    var m = String(ymd || '').match(/^(\d{4})-(\d{2})-(\d{2})$/);
    if (!m) return null;
    return new Date(parseInt(m[1], 10), parseInt(m[2], 10) - 1, parseInt(m[3], 10));
  }

  function monthLabelPL(d) {
    try { return d.toLocaleDateString('pl-PL', { month: 'long', year: 'numeric' }); }
    catch (e) { return d.getFullYear() + '-' + pad2(d.getMonth() + 1); }
  }

  function dayLabelPL(ymd) {
    var d = dateFromYMD(ymd);
    if (!d) return ymd;
    try {
      return d.toLocaleDateString('pl-PL', { weekday: 'long', day: 'numeric', month: 'long' });
    } catch (e) { return ymd; }
  }

  function lastDayOfMonth(d) {
    return new Date(d.getFullYear(), d.getMonth() + 1, 0);
  }

  function isoDow(d) { var n = d.getDay(); return n === 0 ? 7 : n; }

  function startOfWeekMonday(d) {
    var x = new Date(d.getFullYear(), d.getMonth(), d.getDate());
    x.setDate(x.getDate() - (isoDow(x) - 1));
    return x;
  }

  function endOfWeekSunday(d) {
    var x = new Date(d.getFullYear(), d.getMonth(), d.getDate());
    x.setDate(x.getDate() + (7 - isoDow(x)));
    return x;
  }

  function escHtml(s) {
    return String(s || '')
      .replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
      .replace(/"/g,'&quot;').replace(/'/g,'&#039;');
  }

  function sortByDateTime(a, b) {
    var ad = String(a.date || ''), bd = String(b.date || '');
    if (ad !== bd) return ad < bd ? -1 : 1;
    var as = String(a.start || ''), bs = String(b.start || '');
    return as < bs ? -1 : as > bs ? 1 : 0;
  }

  function buildMap(events) {
    var map = {};
    (events || []).forEach(function (e) {
      if (!e || !e.date) return;
      if (!map[e.date]) map[e.date] = [];
      map[e.date].push(e);
    });
    Object.keys(map).forEach(function (k) { map[k].sort(sortByDateTime); });
    return map;
  }

  /* ═══════════════════════════════════════════════
     FETCH
     ═══════════════════════════════════════════════ */
  function fetchMonth(url, nonce, startYMD, endYMD) {
    var u = url + '?start=' + encodeURIComponent(startYMD) + '&end=' + encodeURIComponent(endYMD);
    return fetch(u, {
      method: 'GET',
      credentials: 'include',
      headers: { 'X-WP-Nonce': nonce }
    }).then(function (r) { return r.json(); });
  }

  /* ═══════════════════════════════════════════════
     INLINE STYLES
     Wstrzykujemy przez JS — cache / Divi / minify
     nie mogą ich zablokować.
     ═══════════════════════════════════════════════ */
  function injectStyles() {
    if (document.getElementById('osmc-v2-styles')) return;

    /*
     * Strategia responsywności:
     *
     *  ≤ 400px  (telefon wąski)  → padding panelu 8px, gap 3px, komórka 36px, kropka 5px
     *  ≤ 520px  (telefon)        → padding 10px, gap 4px, komórka 42px, kropka 6px
     *  ≤ 768px  (mały tablet)    → padding 12px, gap 4px, komórka 48px, kropka 7px
     *   > 768px (desktop)        → padding 16px, gap 5px, komórka 52px, kropka 8px
     *
     * Kluczowa zasada: komórka dnia NIE ma stałego min-height —
     * używamy aspect-ratio 1/1.1 żeby proporcje były zawsze OK.
     * Numer dnia i kropki używają tylko jednostek % lub vw żeby
     * nie przekraczać kafelka niezależnie od szerokości ekranu.
     */

    var css = [

      /* ════════════════════════════════
         PANEL — wrapper kalendarza
         ════════════════════════════════ */
      '.osmc-panel{' +
        'position:relative!important;overflow:hidden!important;' +
        'background:rgba(7,12,24,.72)!important;' +
        'border:1px solid rgba(255,255,255,.08)!important;' +
        'border-radius:18px!important;' +
        'padding:16px!important;' +
        'backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);' +
        'box-sizing:border-box!important;}',

      /* ════════════════════════════════
         TOOLBAR
         ════════════════════════════════ */
      '.osmc-toolbar{' +
        'display:flex!important;gap:6px!important;' +
        'align-items:center!important;justify-content:space-between!important;' +
        'margin:0 0 10px!important;flex-wrap:nowrap!important;}',

      'button.osmc-btn{' +
        'border:1px solid rgba(255,255,255,.12)!important;' +
        'background:rgba(255,255,255,.10)!important;' +
        'color:#fff!important;border-radius:10px!important;' +
        'padding:8px 10px!important;' +
        'cursor:pointer!important;font-size:13px!important;' +
        'line-height:1!important;white-space:nowrap!important;' +
        'flex-shrink:0!important;' +
        'transition:background .15s!important;box-shadow:none!important;}',

      'button.osmc-btn:hover{background:rgba(255,255,255,.18)!important;}',

      '.osmc-month{' +
        'flex:1 1 auto!important;text-align:center!important;' +
        'font-weight:700!important;text-transform:capitalize!important;' +
        'color:#fff!important;font-size:14px!important;' +
        'min-width:0!important;overflow:hidden!important;' +
        'text-overflow:ellipsis!important;white-space:nowrap!important;}',

      /* ════════════════════════════════
         GRID
         ════════════════════════════════ */
      '.osmc-grid{' +
        'display:grid!important;' +
        'grid-template-columns:repeat(7,1fr)!important;' +
        'gap:5px!important;' +
        'margin-bottom:10px!important;}',

      '.osmc-dow{' +
        'text-align:center!important;font-size:10px!important;' +
        'color:rgba(255,255,255,.5)!important;' +
        'padding:2px 0 5px!important;font-weight:600!important;' +
        'letter-spacing:.02em!important;text-transform:uppercase!important;}',

      /* ════════════════════════════════
         KOMÓRKA DNIA — desktop bazowy
         ════════════════════════════════ */
      'button.osmc-day{' +
        'position:relative!important;' +
        'border:1px solid rgba(255,255,255,.07)!important;' +
        'background:rgba(255,255,255,.05)!important;' +
        'color:#fff!important;border-radius:10px!important;' +
        /* aspect-ratio zamiast min-height — proporcjonalne na każdym ekranie */
        'aspect-ratio:1/1.15!important;' +
        'width:100%!important;' +
        'padding:5px 4px 0!important;' +
        'cursor:pointer!important;text-align:left!important;' +
        'box-shadow:none!important;overflow:hidden!important;' +
        'display:flex!important;flex-direction:column!important;' +
        'justify-content:flex-start!important;' +
        'transition:background .12s!important;' +
        'box-sizing:border-box!important;}',

      'button.osmc-day:hover{background:rgba(255,255,255,.11)!important;}',

      'button.osmc-day.is-out{' +
        'background:rgba(255,255,255,.02)!important;' +
        'opacity:.35!important;pointer-events:none!important;}',

      'button.osmc-day.is-selected{' +
        'background:rgba(255,255,255,.13)!important;' +
        'outline:2px solid rgba(255,255,255,.35)!important;' +
        'outline-offset:-1px!important;}',

      'button.osmc-day.is-today .osmc-daynum{' +
        'background:rgba(255,255,255,.2)!important;' +
        'border-radius:5px!important;padding:1px 3px!important;}',

      /* Numer dnia — skaluje się z fontem */
      '.osmc-daynum{' +
        'font-weight:700!important;font-size:12px!important;' +
        'color:#fff!important;line-height:1.1!important;' +
        'display:inline-block!important;flex-shrink:0!important;}',

      /* ════════════════════════════════
         KROPKI — dół komórki
         ════════════════════════════════ */
      '.osmc-dots{' +
        'position:absolute!important;' +
        'bottom:3px!important;left:3px!important;right:3px!important;' +
        'display:flex!important;flex-wrap:wrap!important;gap:2px!important;' +
        'align-content:flex-end!important;}',

      /* ════════════════════════════════
         OVERLAY DNIA
         ════════════════════════════════ */
      '.osmc-day-overlay{' +
        'position:absolute!important;inset:0!important;' +
        'background:rgba(6,18,38,.97)!important;' +
        'border-radius:18px!important;' +
        'display:flex!important;flex-direction:column!important;' +
        'z-index:20!important;' +
        'transform:translateY(100%)!important;opacity:0!important;' +
        'transition:transform .3s cubic-bezier(.22,1,.36,1),opacity .22s ease!important;' +
        'pointer-events:none!important;}',

      '.osmc-day-overlay.is-open{' +
        'transform:translateY(0)!important;opacity:1!important;' +
        'pointer-events:auto!important;}',

      '.osmc-day-overlay-head{' +
        'display:flex!important;align-items:center!important;' +
        'justify-content:space-between!important;' +
        'padding:14px 14px 10px!important;' +
        'border-bottom:1px solid rgba(255,255,255,.08)!important;' +
        'flex-shrink:0!important;gap:8px!important;}',

      '.osmc-day-overlay-title{' +
        'font-size:15px!important;font-weight:700!important;' +
        'color:#fff!important;flex:1 1 auto!important;min-width:0!important;' +
        'text-transform:capitalize!important;}',

      'button.osmc-day-overlay-close{' +
        'width:38px!important;height:38px!important;' +
        'border-radius:10px!important;' +
        'border:1px solid rgba(255,255,255,.14)!important;' +
        'background:rgba(255,255,255,.07)!important;' +
        'color:#fff!important;font-size:20px!important;' +
        'cursor:pointer!important;' +
        'display:flex!important;align-items:center!important;' +
        'justify-content:center!important;' +
        'line-height:1!important;flex-shrink:0!important;' +
        'box-shadow:none!important;transition:background .12s!important;}',

      'button.osmc-day-overlay-close:hover{background:rgba(255,255,255,.14)!important;}',

      '.osmc-day-overlay-body{' +
        'flex:1 1 auto!important;overflow-y:auto!important;' +
        '-webkit-overflow-scrolling:touch!important;' +
        'padding:12px 14px!important;}',

      '.osmc-day-overlay .osmc-empty{' +
        'opacity:.7!important;font-size:14px!important;' +
        'color:#fff!important;padding:8px 0!important;}',

      '.osmc-day-overlay .osmc-items{' +
        'display:flex!important;flex-direction:column!important;gap:8px!important;}',

      '.osmc-day-overlay .osmc-item{' +
        'display:flex!important;gap:10px!important;align-items:flex-start!important;' +
        'background:rgba(255,255,255,.05)!important;' +
        'border:1px solid rgba(255,255,255,.07)!important;' +
        'border-left-width:3px!important;' +
        'border-radius:12px!important;padding:10px 12px!important;}',

      '.osmc-day-overlay .osmc-time{' +
        'font-weight:700!important;font-size:13px!important;' +
        'color:#fff!important;white-space:nowrap!important;' +
        'min-width:65px!important;flex-shrink:0!important;}',

      '.osmc-day-overlay .osmc-main{flex:1 1 auto!important;min-width:0!important;}',

      '.osmc-day-overlay .osmc-title{' +
        'font-weight:700!important;font-size:13px!important;color:#fff!important;}',

      '.osmc-day-overlay .osmc-sub{' +
        'font-size:12px!important;opacity:.7!important;' +
        'margin-top:3px!important;line-height:1.35!important;color:#fff!important;}',

      /* ════════════════════════════════
         RESPONSIVE — tablet ≤ 768px
         ════════════════════════════════ */
      '@media(max-width:768px){' +
        '.osmc-panel{padding:12px!important;border-radius:14px!important;}' +
        '.osmc-grid{gap:4px!important;}' +
        '.osmc-dow{font-size:9px!important;padding:2px 0 4px!important;}' +
        'button.osmc-day{border-radius:8px!important;padding:4px 3px 0!important;}' +
        '.osmc-daynum{font-size:11px!important;}' +
        '.osmc-dots{bottom:2px!important;left:2px!important;right:2px!important;gap:2px!important;}' +
        'button.osmc-btn{padding:7px 8px!important;font-size:12px!important;border-radius:9px!important;}' +
        '.osmc-month{font-size:13px!important;}' +
      '}',

      /* ════════════════════════════════
         RESPONSIVE — telefon ≤ 480px
         ════════════════════════════════ */
      '@media(max-width:480px){' +
        '.osmc-panel{padding:8px!important;border-radius:12px!important;}' +
        '.osmc-toolbar{gap:4px!important;margin-bottom:8px!important;}' +
        '.osmc-grid{gap:3px!important;margin-bottom:8px!important;}' +
        '.osmc-dow{font-size:8px!important;padding:1px 0 4px!important;letter-spacing:0!important;}' +
        /* Na telefonie: małe kółko zamiast prostokąta */
        'button.osmc-day{' +
          'border-radius:8px!important;' +
          'padding:3px 2px 0!important;' +
          'aspect-ratio:1/1.05!important;}' +
        '.osmc-daynum{font-size:10px!important;}' +
        /* Max 3 kropki na telefonie żeby nie wylewały */
        '.osmc-dots{bottom:2px!important;left:2px!important;right:2px!important;gap:1px!important;}' +
        /* Przyciski nawigacji — kwadratowe, tylko strzałka */
        'button.osmc-btn{' +
          'padding:6px 7px!important;font-size:11px!important;' +
          'border-radius:8px!important;}' +
        '.osmc-month{font-size:12px!important;}' +
        /* Overlay — padding mniejszy */
        '.osmc-day-overlay-head{padding:10px 10px 8px!important;}' +
        '.osmc-day-overlay-title{font-size:13px!important;}' +
        'button.osmc-day-overlay-close{width:34px!important;height:34px!important;font-size:18px!important;}' +
        '.osmc-day-overlay-body{padding:8px 10px!important;}' +
        '.osmc-day-overlay .osmc-item{padding:8px 10px!important;gap:8px!important;}' +
        '.osmc-day-overlay .osmc-time{font-size:12px!important;min-width:56px!important;}' +
        '.osmc-day-overlay .osmc-title{font-size:12px!important;}' +
        '.osmc-day-overlay .osmc-sub{font-size:11px!important;}' +
      '}',

      /* ════════════════════════════════
         RESPONSIVE — bardzo wąski ≤ 360px
         ════════════════════════════════ */
      '@media(max-width:360px){' +
        '.osmc-panel{padding:6px!important;}' +
        '.osmc-grid{gap:2px!important;}' +
        '.osmc-dow{font-size:7px!important;}' +
        'button.osmc-day{border-radius:6px!important;}' +
        '.osmc-daynum{font-size:9px!important;}' +
        'button.osmc-btn{padding:5px 6px!important;font-size:10px!important;}' +
        '.osmc-month{font-size:11px!important;}' +
      '}',

    ].join('\n');

    var el = document.createElement('style');
    el.id  = 'osmc-v2-styles';
    el.textContent = css;
    document.head.appendChild(el);
  }

  /* ═══════════════════════════════════════════════
     RENDER KALENDARZA
     ═══════════════════════════════════════════════ */
  function renderCalendar(root, state) {
    var todayYMD = ymdFromDate(new Date());
    var html = '';

    /* Toolbar */
    html += '<div class="osmc-toolbar">';
    html += '<button type="button" class="osmc-btn" data-action="prev">\u25C4</button>';
    html += '<div class="osmc-month">' + escHtml(monthLabelPL(state.monthDate)) + '</div>';
    html += '<button type="button" class="osmc-btn" data-action="today">Dzi\u015B</button>';
    html += '<button type="button" class="osmc-btn" data-action="next">\u25BA</button>';
    html += '</div>';

    /* Grid */
    html += '<div class="osmc-grid">';
    html += '<div class="osmc-dow">Pon</div>';
    html += '<div class="osmc-dow">Wto</div>';
    html += '<div class="osmc-dow">\u015Aro</div>';
    html += '<div class="osmc-dow">Czw</div>';
    html += '<div class="osmc-dow">Pi\u0105</div>';
    html += '<div class="osmc-dow">Sob</div>';
    html += '<div class="osmc-dow">Nd</div>';

    var first = new Date(state.monthDate.getFullYear(), state.monthDate.getMonth(), 1);
    var last  = lastDayOfMonth(state.monthDate);
    var cur   = startOfWeekMonday(first);
    var gend  = endOfWeekSunday(last);

    while (cur <= gend) {
      var ymd        = ymdFromDate(cur);
      var inMonth    = (cur.getMonth() === state.monthDate.getMonth());
      var isToday    = (ymd === todayYMD);
      var isSelected = (ymd === state.selectedYMD);
      var evList     = state.map[ymd] || [];

      var cls = 'osmc-day';
      if (!inMonth)   cls += ' is-out';
      if (isToday)    cls += ' is-today';
      if (isSelected) cls += ' is-selected';

      html += '<button type="button" class="' + cls + '" data-ymd="' + ymd + '">';
      html += '<div class="osmc-daynum">' + cur.getDate() + '</div>';

      /* Kolorowe kropki — max 3 (żeby nie wylewały z małych kafelków) */
      if (evList.length > 0) {
        html += '<div class="osmc-dots">';
        var shown = Math.min(evList.length, 3);
        for (var i = 0; i < shown; i++) {
          var dotCol = DOT_COLORS[colorForTitle(evList[i].title)] || DOT_COLORS.gray;
          /*
           * Rozmiar kropki przez clamp: min 4px, preferowany 1.8vw, max 8px
           * Dzięki temu na wąskich telefonach kropka się zmniejsza automatycznie.
           */
          html += '<span style="' +
            'display:inline-block;' +
            'width:clamp(4px,1.8vw,8px);height:clamp(4px,1.8vw,8px);' +
            'border-radius:50%;' +
            'background:' + dotCol + ';' +
            'opacity:.9;flex-shrink:0;' +
          '"></span>';
        }
        /* Jeśli jest więcej niż 3 zajęcia — pokaż +N */
        if (evList.length > 3) {
          html += '<span style="' +
            'display:inline-block;font-size:clamp(6px,1.4vw,9px);' +
            'color:rgba(255,255,255,.6);line-height:clamp(4px,1.8vw,8px);' +
            'flex-shrink:0;' +
          '">+' + (evList.length - 3) + '</span>';
        }
        html += '</div>';
      }

      html += '</button>';
      cur.setDate(cur.getDate() + 1);
    }

    html += '</div>'; /* /grid */

    /* Overlay dnia (początkowo ukryty) */
    html += '<div class="osmc-day-overlay" data-role="dayOverlay" aria-hidden="true">';
    html += '<div class="osmc-day-overlay-head">';
    html += '<div class="osmc-day-overlay-title" data-role="overlayTitle">Zaj\u0119cia</div>';
    html += '<button type="button" class="osmc-day-overlay-close" data-action="closeOverlay" aria-label="Zamknij">\u00D7</button>';
    html += '</div>';
    html += '<div class="osmc-day-overlay-body" data-role="overlayBody"></div>';
    html += '</div>';

    root.innerHTML = html;
  }

  /* ═══════════════════════════════════════════════
     ZAWARTOŚĆ OVERLAY DNIA
     ═══════════════════════════════════════════════ */
  function renderDayContent(ymd, events) {
    if (!events.length) {
      return '<div class="osmc-empty">Brak zaj\u0119\u0107 w tym dniu.</div>';
    }

    var html = '<div class="osmc-items">';
    events.forEach(function (e) {
      var stripe = STRIPE_COLORS[colorForTitle(e.title)] || STRIPE_COLORS.gray;

      var time = escHtml(e.start || '');
      if (e.end) time += '\u2013' + escHtml(e.end);

      var sub = '';
      if (e.coach)    sub += escHtml(e.coach);
      if (e.room)     sub += (sub ? ' \u00B7 ' : '') + escHtml(e.room);
      if (e.audience) sub += (sub ? ' \u00B7 ' : '') + escHtml(e.audience);
      if (e.level)    sub += (sub ? ' \u00B7 ' : '') + escHtml(e.level);

      /* border-left-color przez inline style — 100% pewność koloru */
      html += '<div class="osmc-item" style="border-left-color:' + stripe + '!important;">';
      html += '<div class="osmc-time">' + time + '</div>';
      html += '<div class="osmc-main">';
      html += '<div class="osmc-title">' + escHtml(e.title || '\u2014') + '</div>';
      if (sub) html += '<div class="osmc-sub">' + sub + '</div>';
      html += '</div>';
      html += '</div>';
    });
    html += '</div>';
    return html;
  }

  /* ═══════════════════════════════════════════════
     OVERLAY open / close
     ═══════════════════════════════════════════════ */
  function openDayOverlay(root, ymd, events) {
    var overlay = root.querySelector('[data-role="dayOverlay"]');
    var titleEl = root.querySelector('[data-role="overlayTitle"]');
    var bodyEl  = root.querySelector('[data-role="overlayBody"]');
    if (!overlay || !titleEl || !bodyEl) return;

    titleEl.textContent = dayLabelPL(ymd);
    bodyEl.innerHTML    = renderDayContent(ymd, events);
    bodyEl.scrollTop    = 0;

    overlay.classList.add('is-open');
    overlay.setAttribute('aria-hidden', 'false');
  }

  function closeDayOverlay(root) {
    var overlay = root.querySelector('[data-role="dayOverlay"]');
    if (!overlay) return;
    overlay.classList.remove('is-open');
    overlay.setAttribute('aria-hidden', 'true');
  }

  /* ═══════════════════════════════════════════════
     BIND ZDARZEŃ
     ═══════════════════════════════════════════════ */
  function bind(root, state, api) {
    root.addEventListener('click', function (ev) {
      var btn = ev.target && ev.target.closest ? ev.target.closest('button') : null;
      if (!btn) return;

      var action = btn.getAttribute('data-action');

      if (action === 'closeOverlay') { closeDayOverlay(root); return; }

      if (action === 'prev') {
        state.monthDate = new Date(state.monthDate.getFullYear(), state.monthDate.getMonth() - 1, 1);
        closeDayOverlay(root);
        api.loadMonth();
        return;
      }
      if (action === 'next') {
        state.monthDate = new Date(state.monthDate.getFullYear(), state.monthDate.getMonth() + 1, 1);
        closeDayOverlay(root);
        api.loadMonth();
        return;
      }
      if (action === 'today') {
        var now = new Date();
        state.monthDate   = new Date(now.getFullYear(), now.getMonth(), 1);
        state.selectedYMD = ymdFromDate(now);
        closeDayOverlay(root);
        api.loadMonth();
        return;
      }

      /* Kliknięcie dnia */
      var ymd = btn.getAttribute('data-ymd');
      if (ymd) {
        state.selectedYMD = ymd;

        /* Aktualizuj is-selected bez pełnego re-renderu */
        root.querySelectorAll('button.osmc-day').forEach(function (d) {
          d.classList.toggle('is-selected', d.getAttribute('data-ymd') === ymd);
        });

        openDayOverlay(root, ymd, state.map[ymd] || []);
      }
    });
  }

  /* ═══════════════════════════════════════════════
     INIT JEDNEGO KONTENERA
     ═══════════════════════════════════════════════ */
  function initOne(container) {
    var loading = container.querySelector('[data-role="loading"]');
    var error   = container.querySelector('[data-role="error"]');
    var root    = container.querySelector('[data-role="root"]');

    var cfg = window.osproMyClasses;
    if (!cfg || !cfg.url || !cfg.nonce) {
      if (loading) loading.style.display = 'none';
      if (error) {
        error.style.display = 'block';
        error.textContent   = 'Brak konfiguracji osproMyClasses.';
      }
      return;
    }

    var now = new Date();
    var state = {
      monthDate:   new Date(now.getFullYear(), now.getMonth(), 1),
      selectedYMD: ymdFromDate(now),
      events:      [],
      map:         {}
    };

    var api = {
      loadMonth: function () {
        if (loading) loading.style.display = 'block';
        if (error)   error.style.display   = 'none';
        if (root)    root.style.display    = 'none';

        var first    = new Date(state.monthDate.getFullYear(), state.monthDate.getMonth(), 1);
        var last     = lastDayOfMonth(state.monthDate);
        var startYMD = ymdFromDate(first);
        var endYMD   = ymdFromDate(last);

        fetchMonth(cfg.url, cfg.nonce, startYMD, endYMD)
          .then(function (d) {
            if (!d || d.ok !== true) throw new Error('Bad response');

            state.events = d.events || [];
            state.map    = buildMap(state.events);

            /* Jeśli zaznaczony dzień poza miesiącem → 1. dzień */
            var sel = dateFromYMD(state.selectedYMD);
            if (!sel ||
                sel.getFullYear() !== state.monthDate.getFullYear() ||
                sel.getMonth()    !== state.monthDate.getMonth()) {
              state.selectedYMD = startYMD;
            }

            renderCalendar(root, state);
            bind(root, state, api);

            if (loading) loading.style.display = 'none';
            if (root)    root.style.display    = 'block';
          })
          .catch(function (err) {
            console.error('[osmc] error:', err);
            if (loading) loading.style.display = 'none';
            if (error) {
              error.style.display = 'block';
              error.textContent   = 'B\u0142\u0105d \u0142adowania rezerwacji.';
            }
          });
      }
    };

    api.loadMonth();

    /* ── Nasłuchuj zapisu z grafiku zajęć ──────────────────────────
       schedule.js dispatchuje 'ospro:signed' po każdym udanym zapisie.
       Odbieramy event i przeładowujemy miesiąc żeby kropki/overlay
       zaktualizowały się automatycznie bez odświeżania strony.
    ─────────────────────────────────────────────────────────────── */
    window.addEventListener('ospro:signed', function(ev) {
      /* Małe opóźnienie żeby Airtable zdążył zapisać zanim odpytamy */
      setTimeout(function() {
        api.loadMonth();
      }, 800);
    });
  }

  /* ═══════════════════════════════════════════════
     START
     ═══════════════════════════════════════════════ */
  function init() {
    injectStyles(); /* Inline styles — cache-proof */

    var containers = Array.prototype.slice.call(
      document.querySelectorAll('[data-ospro-my-classes="1"]')
    );
    if (!containers.length) return;

    containers.forEach(function (wrap) {
      var panel = wrap.querySelector('.osmc-panel');
      if (!panel) return;
      initOne(panel);
    });
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }

})();