{"id":438,"date":"2026-09-13T08:05:43","date_gmt":"2026-09-13T08:05:43","guid":{"rendered":"https:\/\/musicandme.es\/?page_id=438"},"modified":"2026-10-13T19:25:51","modified_gmt":"2026-10-13T19:25:51","slug":"class-calendar","status":"publish","type":"page","link":"https:\/\/musicandme.es\/es\/class-calendar\/","title":{"rendered":"Calendario"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"438\" class=\"elementor elementor-438\">\n\t\t\t\t<div class=\"elementor-element elementor-element-4baed58 e-flex e-con-boxed e-con e-parent\" data-id=\"4baed58\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-cbd9b59 elementor-widget elementor-widget-html\" data-id=\"cbd9b59\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<nav class=\"nav\">\n  <div class=\"nav-inner\">\n    <a href=\"\/\" class=\"brand\">\n      <img decoding=\"async\" src=\"https:\/\/musicandme.es\/wp-content\/uploads\/2026\/09\/logo-nav.png\" alt=\"Music & Me\">\n    <\/a>\n    <div class=\"nav-links\">\n      <a class=\"nav-link\" href=\"\/\">Home<\/a>\n      <a class=\"nav-link\" href=\"\/our-venues\/\">Our Venues<\/a>\n      <a class=\"nav-link\" href=\"\/classes-passes\/\">Shop<\/a>\n      <a class=\"nav-link\" href=\"\/class-calendar\/\" aria-current=\"page\">Calendar<\/a>\n    <\/div>\n    <div class=\"nav-right\">\n      <div class=\"lang-switch\" role=\"group\" aria-label=\"Language \/ Idioma\">\n        <button type=\"button\" class=\"active\">EN<\/button>\n        <button type=\"button\">ES<\/button>\n      <\/div>\n      <a class=\"cart-btn\" href=\"\/basket\/\" aria-label=\"View cart, 0 items\">\n        <span aria-hidden=\"true\">\ud83d\uded2<\/span><span class=\"cart-count\">0<\/span>\n      <\/a>\n      <a class=\"btn btn-trial btn-sm\" href=\"#book-trial\">Book a Trial<\/a>\n      <button class=\"burger\" aria-label=\"Open menu\">\u2630<\/button>\n    <\/div>\n  <\/div>\n<\/nav>\n<section>\n  <div class=\"wrap\">\n    <div class=\"section-head\">\n      <p class=\"plan-kicker\">CLASS SCHEDULE<\/p>\n      <h2>Find a class near you<\/h2>\n      <p>Browse our weekly schedule across all six venues. Tap a highlighted day to see what's on.<\/p>\n    <\/div>\n\n    <div class=\"cal-filter-row\" id=\"calFilters\"><\/div>\n\n    <div class=\"cal-page-grid\">\n      <div class=\"cal-page-card\">\n        <div class=\"cal-page-head\">\n          <button id=\"calPrev\" type=\"button\" aria-label=\"Previous month\">&lsaquo;<\/button>\n          <strong id=\"calMonthLabel\">Month Year<\/strong>\n          <button id=\"calNext\" type=\"button\" aria-label=\"Next month\">&rsaquo;<\/button>\n        <\/div>\n        <div class=\"cal-page-dow\">\n          <div>Mon<\/div><div>Tue<\/div><div>Wed<\/div><div>Thu<\/div><div>Fri<\/div><div>Sat<\/div><div>Sun<\/div>\n        <\/div>\n        <div class=\"cal-page-days\" id=\"calDays\"><\/div>\n        <div class=\"cal-page-legend\">\n          <span><span class=\"legend-swatch sky\"><\/span>Class<\/span>\n          <span><span class=\"legend-swatch popup\"><\/span>Pop-up class<\/span>\n          <span><span class=\"legend-ring\"><\/span>Today<\/span>\n        <\/div>\n      <\/div>\n\n      <div class=\"cal-page-detail\" id=\"calDetail\">\n        <h3>Select a day<\/h3>\n        <p class=\"detail-date\">&nbsp;<\/p>\n        <div class=\"detail-empty\">Click a highlighted day on the calendar to see classes running that day.<\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/section>\n\n<script>\n(function(){\n  const VENUES = [\"Plaza de Espa\u00f1a\", \"Ciudad Jard\u00edn\", \"Barrio Salamanca\", \"El Viso\", \"Chamart\u00edn\", \"Chamberi\"];\n  const DOW_MON_FIRST = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];\n  const LANG = (document.documentElement.lang || 'en').slice(0, 2) === 'es' ? 'es' : 'en';\n  const L = (en, es) => (LANG === 'es' ? es : en);\n  const VENUE_LABELS = { 'Ciudad Jard\u00edn': 'Parque de Berl\u00edn' };\n  const vName = (v) => VENUE_LABELS[v] || (LANG === 'es' && v === 'Chamberi' ? 'Chamber\u00ed' : v);\n  const HOL_ES = { 'Winter Holidays': 'Vacaciones de Navidad', 'Easter Week': 'Semana Santa' };\n  const holLabel = (s) => (LANG === 'es' && HOL_ES[s]) || s;\n  const MONTH_NAMES = LANG === 'es' ? ['Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'] : ['January','February','March','April','May','June','July','August','September','October','November','December'];\n\n  const today = new Date();\n  const todayKey = fmtKey(today);\n\n  let state = {\n    year: today.getFullYear(),\n    month: today.getMonth(), \/\/ 0-indexed\n    venue: 'All',\n    selectedDate: null,\n    eventsByDay: {}\n  };\n\n  function fmtKey(d){\n    return d.getFullYear() + '-' + String(d.getMonth()+1).padStart(2,'0') + '-' + String(d.getDate()).padStart(2,'0');\n  }\n  const VENUE_INFO = {\n    'Plaza de Espa\u00f1a': { name: 'Color\u00edn Colorado', address: 'C\/ Fomento 34' },\n    'Ciudad Jard\u00edn': { name: 'Parque de Berl\u00edn', address: L('Concha Espina park entrance', 'Entrada por Concha Espina') },\n    'Barrio Salamanca': { name: 'Atelier House', address: 'C\/ Castello 70B' },\n    'El Viso': { name: 'Little &amp; Latte', address: 'C\/ Principe de Vergara 205' },\n    'Chamart\u00edn': { name: 'Osito &amp; Mandarina', address: 'C\/ Nicaragua 14' },\n    'Chamberi': { name: 'Museum of Mini', address: 'C\/ Palafox 20' }\n  };\n  \/\/ Monthly pop-up: first Friday of each month from September to June (no January).\n  const POPUP_START = '2026-10-01';\n  const POPUP_END = '2027-06-30';\n  function getPopup(key){\n    if (key < POPUP_START || key > POPUP_END) return null;\n    const parts = key.split('-').map(Number);\n    if (parts[1] === 1 || parts[2] > 7) return null;\n    return new Date(parts[0], parts[1] - 1, parts[2]).getDay() === 5 ? { label: 'Pop-up class' } : null;\n  }\n  const HOLIDAYS = [\n    { start: '2026-12-21', end: '2027-01-10', label: 'Winter Holidays' },\n    { start: '2027-03-20', end: '2027-03-28', label: 'Easter Week' }\n  ];\n  function getHoliday(key){\n    return HOLIDAYS.find(function(h){ return key >= h.start && key <= h.end; });\n  }\n  function pad2(n){ return String(n).padStart(2,'0'); }\n\n  function buildFilters(){\n    const container = document.getElementById('calFilters');\n    const options = ['All'].concat(VENUES);\n    container.innerHTML = options.map(v =>\n      `<button type=\"button\" class=\"cal-filter-btn${v===state.venue?' active':''}\" data-venue=\"${v}\">${v === 'All' ? L('All', 'Todas') : vName(v)}<\/button>`\n    ).join('');\n    container.querySelectorAll('.cal-filter-btn').forEach(btn => {\n      btn.addEventListener('click', () => {\n        state.venue = btn.getAttribute('data-venue');\n        buildFilters();\n        renderGrid();\n        if (state.selectedDate) renderDetail(state.selectedDate);\n      });\n    });\n  }\n\n  async function fetchMonth(year, month){\n    const start = year + '-' + pad2(month+1) + '-01';\n    const lastDay = new Date(year, month+1, 0).getDate();\n    const end = year + '-' + pad2(month+1) + '-' + pad2(lastDay);\n    try {\n      const res = await fetch('\/wp-json\/mm\/v1\/events?start=' + start + '&end=' + end, {cache:'no-store'});\n      const data = await res.json();\n      const byDay = {};\n      (Array.isArray(data) ? data : []).forEach(ev => {\n        const d = new Date(ev.start_ts * 1000);\n        const key = d.getUTCFullYear() + '-' + pad2(d.getUTCMonth() + 1) + '-' + pad2(d.getUTCDate());\n        if (!byDay[key]) byDay[key] = [];\n        byDay[key].push(ev);\n      });\n      Object.values(byDay).forEach(list => list.sort((a,b) => a.start_ts - b.start_ts));\n      return byDay;\n    } catch(e){\n      return {};\n    }\n  }\n\n  function filteredEvents(list){\n    if (state.venue === 'All') return list;\n    return list.filter(e => e.venue === state.venue);\n  }\n\n  async function renderGrid(){\n    document.getElementById('calMonthLabel').textContent = MONTH_NAMES[state.month] + ' ' + state.year;\n    state.eventsByDay = await fetchMonth(state.year, state.month);\n\n    const daysContainer = document.getElementById('calDays');\n    daysContainer.innerHTML = '';\n\n    const firstOfMonth = new Date(state.year, state.month, 1);\n    let startWeekday = firstOfMonth.getDay(); \/\/ 0=Sun\n    startWeekday = (startWeekday + 6) % 7; \/\/ convert to Mon=0..Sun=6\n    const daysInMonth = new Date(state.year, state.month+1, 0).getDate();\n\n    for (let i=0; i<startWeekday; i++){\n      const empty = document.createElement('div');\n      empty.className = 'cal-page-day empty';\n      daysContainer.appendChild(empty);\n    }\n\n    for (let day=1; day<=daysInMonth; day++){\n      const dateObj = new Date(state.year, state.month, day);\n      const key = fmtKey(dateObj);\n      const allDayEvents = state.eventsByDay[key] || [];\n      const dayEvents = filteredEvents(allDayEvents);\n      const holiday = getHoliday(key);\n      const popup = holiday ? null : getPopup(key);\n\n      const cell = document.createElement('div');\n      let cls = 'cal-page-day';\n      if (key === todayKey) cls += ' today';\n      if (dayEvents.length > 0 || holiday || popup) cls += ' clickable';\n      if (dayEvents.length > 0 && !holiday) cls += ' has-class';\n      if (popup) cls += ' has-popup';\n      if (holiday) cls += ' holiday';\n      if (state.selectedDate === key) cls += ' selected';\n      cell.className = cls;\n\n      const num = document.createElement('div');\n      num.className = 'cal-page-daynum';\n      num.textContent = day;\n      cell.appendChild(num);\n\n      if (holiday){\n        const chip = document.createElement('div');\n        chip.className = 'cal-page-chip chip-holiday';\n        chip.textContent = holLabel(holiday.label);\n        cell.appendChild(chip);\n      } else {\n        if (popup){\n          const pchip = document.createElement('div');\n          pchip.className = 'cal-page-chip chip-popup';\n          pchip.textContent = L('Pop-up class', 'Clase pop-up');\n          cell.appendChild(pchip);\n        }\n        const maxChips = 2;\n        dayEvents.slice(0, maxChips).forEach(ev => {\n          const chip = document.createElement('div');\n          chip.className = 'cal-page-chip chip-class';\n          chip.textContent = vName(ev.venue) + ' \\u00b7 ' + ev.type;\n          cell.appendChild(chip);\n        });\n        if (dayEvents.length > maxChips){\n          const more = document.createElement('div');\n          more.className = 'cal-page-more';\n          more.textContent = '+' + (dayEvents.length - maxChips) + L(' more', ' m\u00e1s');\n          cell.appendChild(more);\n        }\n      }\n\n      if (dayEvents.length > 0 || holiday || popup){\n        cell.addEventListener('click', () => {\n          state.selectedDate = key;\n          renderGrid();\n          renderDetail(key);\n        });\n      }\n\n      daysContainer.appendChild(cell);\n    }\n  }\n\n  function renderDetail(dateKey){\n    const detail = document.getElementById('calDetail');\n    const [y,m,d] = dateKey.split('-').map(Number);\n    const dateObj = new Date(y, m-1, d);\n    const dateLabel = dateObj.toLocaleDateString(LANG === 'es' ? 'es-ES' : 'en-GB', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });\n\n    const allEvents = state.eventsByDay[dateKey] || [];\n    const events = filteredEvents(allEvents);\n\n    let html = '<h3>' + L('Classes', 'Clases') + '<\/h3><p class=\"detail-date\">' + dateLabel + '<\/p>';\n\n    const holiday = getHoliday(dateKey);\n    if (holiday){\n      html += '<div class=\"detail-empty\">' + holLabel(holiday.label) + L(' \\u2014 no classes.', ' \\u2014 sin clases.') + '<\/div>';\n    } else {\n      const popup = getPopup(dateKey);\n      if (events.length === 0 && !popup){\n        html += '<div class=\"detail-empty\">' + L('No classes on this day', 'No hay clases este d\u00eda') + (state.venue !== 'All' ? L(' at ', ' en ') + vName(state.venue) : '') + '.<\/div>';\n      }\n      events.forEach(ev => {\n        const info = VENUE_INFO[ev.venue] || { name: ev.venue, address: '' };\n        html += '<div class=\"detail-entry\">' +\n          '<div class=\"entry-kind\">' + ev.type + '<\/div>' +\n          '<div class=\"entry-title\">' + info.name + '<\/div>' +\n          (info.address ? '<div class=\"entry-meta\">' + info.address + '<\/div>' : '') +\n          '<div class=\"entry-meta\">' + ev.start_time + ' &ndash; ' + ev.end_time + '<\/div>' +\n          '<\/div>';\n      });\n      if (popup){\n        html += '<div class=\"detail-entry popup\">' +\n          '<div class=\"entry-kind\">' + L('Monthly pop-up', 'Pop-up mensual') + '<\/div>' +\n          '<div class=\"entry-title\">' + L('Free Pop-Up Class', 'Clase pop-up gratuita') + '<\/div>' +\n          '<div class=\"entry-meta\">' + L('One free class, every age group welcome, at a surprise location across Madrid.', 'Una clase gratuita, para todas las edades, en un lugar sorpresa de Madrid.') + '<\/div>' +\n          '<div class=\"entry-meta\">' + L('Location announced a few days before on Instagram: ', 'La ubicaci\u00f3n se anuncia unos d\u00edas antes en Instagram: ') + '<a href=\"https:\/\/www.instagram.com\/musicandme.madrid\/\" target=\"_blank\" rel=\"noopener\">@musicandme.madrid<\/a><\/div>' +\n          '<\/div>';\n      }\n    }\n    detail.innerHTML = html;\n  }\n\n  document.getElementById('calPrev').addEventListener('click', () => {\n    state.month--;\n    if (state.month < 0){ state.month = 11; state.year--; }\n    renderGrid();\n  });\n  document.getElementById('calNext').addEventListener('click', () => {\n    state.month++;\n    if (state.month > 11){ state.month = 0; state.year++; }\n    renderGrid();\n  });\n\n  buildFilters();\n  renderGrid();\n})();\n<\/script>\n\n<footer>\n  <div class=\"wrap footer-grid\">\n    <div>\n      <img decoding=\"async\" class=\"footer-logo\" src=\"https:\/\/musicandme.es\/wp-content\/uploads\/2026\/09\/logo-footer.png\" alt=\"Music & Me - English Music Club\">\n      <p style=\"max-width:260px;\">English Music for Little Learners. Founded in Madrid, Spain.<\/p>\n    <\/div>\n    <div>\n      <h5>Explore<\/h5>\n      <a href=\"\/\">Home<\/a>\n      <a href=\"\/our-venues\/\">Our Venues<\/a>\n      <a href=\"\/classes-passes\/\">Shop<\/a>\n      <a href=\"\/class-calendar\/\">Calendar<\/a>\n    <\/div>\n    <div>\n      <h5>Contact<\/h5>\n      <a href=\"mailto:info@musicandme.es\">info@musicandme.es<\/a>\n      <a href=\"tel:+34685068053\" aria-label=\"WhatsApp (+34) 685 068 053\" style=\"display:flex;align-items:center;gap:8px;\"><svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z\"\/><\/svg><span>(+34) 685 068 053<\/span><\/a>\n      <a href=\"https:\/\/www.instagram.com\/musicandme.madrid\/\" target=\"_blank\" rel=\"noopener\" aria-label=\"Instagram @musicandme.madrid\" style=\"display:flex;align-items:center;gap:8px;margin-top:6px;\">\n        <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"2\" y=\"2\" width=\"20\" height=\"20\" rx=\"5\"><\/rect><path d=\"M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z\"><\/path><line x1=\"17.5\" y1=\"6.5\" x2=\"17.5\" y2=\"6.5\"><\/line><\/svg><span>@musicandme.madrid<\/span><\/a>\n    <\/div>\n    <div>\n      <h5>Join the band<\/h5>\n      <p>Music tips and info from our creatives - straight to your inbox<\/p>\n      <form class=\"newsletter-row\">\n        <label for=\"newsletterEmail\" class=\"visually-hidden\">Your email<\/label>\n        <input id=\"newsletterEmail\" type=\"email\" placeholder=\"Your email\" required>\n        <button class=\"btn btn-white btn-sm\" type=\"submit\">Join<\/button>\n      <\/form>\n    <\/div>\n    <\/div>\n  <div class=\"foot-bottom\">&copy; 2026 Music & Me - All rights reserved.<\/div>\n    <div class=\"foot-bottom\" style=\"margin-top:6px;\"><a href=\"\/privacy-policy\/\">Privacy Policy<\/a> &middot; <a href=\"\/refunds-returns\/\">Refunds &amp; Returns<\/a> &middot; <a href=\"\/attendance\/\">Attendance<\/a><\/div>\n    <div class=\"foot-bottom\" style=\"margin-top:6px;\">In partnership with <a href=\"https:\/\/littleandlatte.com\/\" target=\"_blank\" rel=\"noopener\">Little & Latte<\/a>, <a href=\"https:\/\/www.atelierhouse.es\/\" target=\"_blank\" rel=\"noopener\">Atelier House<\/a>, <a href=\"https:\/\/museumofmini.com\/?srsltid=AfmBOorWKTUVGx1Ggkehl1XRIzY9Ta38YTiDbee5dyJsja1Gry-K9tNS\" target=\"_blank\" rel=\"noopener\">Museum of Mini<\/a>, Color\u00edn Colorado & Osito & Mandarina.<\/div>\n<\/footer>\t\t\t\t<\/div>\n\t\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>Home Our Venues Shop Calendar EN ES \ud83d\uded20 Book a Trial \u2630 CLASS SCHEDULE Find a class near you Browse our weekly schedule across all six venues. Tap a highlighted day to see what&#8217;s on. &lsaquo; Month Year &rsaquo; MonTueWedThuFriSatSun Class Pop-up class Today Select a day &nbsp; Click a highlighted day on the calendar [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-438","page","type-page","status-publish","hentry"],"_hostinger_reach_plugin_has_subscription_block":false,"_hostinger_reach_plugin_is_elementor":false,"_links":{"self":[{"href":"https:\/\/musicandme.es\/es\/wp-json\/wp\/v2\/pages\/438","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/musicandme.es\/es\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/musicandme.es\/es\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/musicandme.es\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/musicandme.es\/es\/wp-json\/wp\/v2\/comments?post=438"}],"version-history":[{"count":56,"href":"https:\/\/musicandme.es\/es\/wp-json\/wp\/v2\/pages\/438\/revisions"}],"predecessor-version":[{"id":1227,"href":"https:\/\/musicandme.es\/es\/wp-json\/wp\/v2\/pages\/438\/revisions\/1227"}],"wp:attachment":[{"href":"https:\/\/musicandme.es\/es\/wp-json\/wp\/v2\/media?parent=438"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}