{"id":14,"date":"2026-10-27T17:20:08","date_gmt":"2026-10-27T17:20:08","guid":{"rendered":"https:\/\/magicafm.mx\/wordpress\/?page_id=14"},"modified":"2026-10-27T18:14:11","modified_gmt":"2026-10-27T18:14:11","slug":"espectaculos","status":"publish","type":"page","link":"https:\/\/magicafm.mx\/?page_id=14","title":{"rendered":"Espect\u00e1culos"},"content":{"rendered":"\n<!-- HTML para la p\u00e1gina de Espect\u00e1culos de M\u00e1gica 100.1 FM.\n     Si WordPress est\u00e1 en otra direcci\u00f3n, escr\u00edbela en data-wordpress-url.\n     Ejemplo: data-wordpress-url=\"https:\/\/tudominio.com\/wordpress\/\" -->\n\n<section\n  id=\"magica-esp\"\n  data-category=\"espectaculos,farandula\"\n  data-api-root=\"\"\n  data-wordpress-url=\"\"\n  aria-label=\"Noticias de espect\u00e1culos de M\u00e1gica\">\n\n  <header class=\"me-heading\">\n    <div>\n      <p class=\"me-eyebrow\">EL LADO M\u00c1S M\u00c1GICO DEL ENTRETENIMIENTO<\/p>\n      <h2>Espect\u00e1culos<span>.<\/span><\/h2>\n      <p class=\"me-intro\">\n        M\u00fasica, cine y las historias que dan de qu\u00e9 hablar.\n      <\/p>\n    <\/div>\n\n    <span class=\"me-station\">\n      M\u00c1GICA\n      <b>100.1 FM<\/b>\n      <small>Tan diferente como t\u00fa.<\/small>\n    <\/span>\n  <\/header>\n\n  <p class=\"me-status\" data-status role=\"status\" aria-live=\"polite\">\n    Cargando las noticias de espect\u00e1culos\u2026\n  <\/p>\n\n  <div class=\"me-error\" data-error hidden>\n    <p><\/p>\n    <button type=\"button\" data-retry>Volver a intentar<\/button>\n  <\/div>\n\n  <section\n    class=\"me-featured\"\n    data-featured\n    aria-labelledby=\"me-latest-title\"\n    hidden>\n\n    <div class=\"me-section-title\">\n      <h3 id=\"me-latest-title\">\n        <span class=\"me-dot\"><\/span>\n        Lo \u00faltimo en espect\u00e1culos\n      <\/h3>\n      <span>ACTUALIDAD \u00b7 M\u00c1GICA<\/span>\n    <\/div>\n\n    <div\n      class=\"me-carousel\"\n      data-carousel\n      role=\"region\"\n      aria-roledescription=\"carrusel\"\n      aria-label=\"\u00daltimas noticias de espect\u00e1culos\">\n\n      <div class=\"me-slides\" data-slides><\/div>\n\n      <div class=\"me-carousel-bar\" data-carousel-controls>\n        <div class=\"me-arrows\">\n          <button type=\"button\" data-prev\n            aria-label=\"Noticia anterior\">\u2190<\/button>\n          <button type=\"button\" data-next\n            aria-label=\"Noticia siguiente\">\u2192<\/button>\n        <\/div>\n\n        <div class=\"me-dots\" data-dots\n          aria-label=\"Seleccionar noticia\"><\/div>\n\n        <button\n          class=\"me-pause\"\n          type=\"button\"\n          data-pause\n          aria-pressed=\"false\">\n          Pausar\n        <\/button>\n      <\/div>\n    <\/div>\n  <\/section>\n\n  <section\n    class=\"me-archive\"\n    id=\"me-archivo\"\n    aria-labelledby=\"me-archive-title\"\n    hidden>\n\n    <div class=\"me-archive-heading\">\n      <div>\n        <p class=\"me-eyebrow\">QUE NO SE TE PASE NINGUNA<\/p>\n        <h3 id=\"me-archive-title\" tabindex=\"-1\">\n          Todas las noticias\n        <\/h3>\n      <\/div>\n      <p class=\"me-count\" data-count><\/p>\n    <\/div>\n\n    <div class=\"me-grid\" data-grid><\/div>\n\n    <nav\n      class=\"me-pagination\"\n      data-pagination\n      aria-label=\"P\u00e1ginas de noticias de espect\u00e1culos\">\n    <\/nav>\n\n    <p class=\"me-page-info\" data-page-info><\/p>\n  <\/section>\n\n  <noscript>\n    <p>Activa JavaScript para consultar las noticias de espect\u00e1culos.<\/p>\n  <\/noscript>\n<\/section>\n\n<style>\n#magica-esp{\n  --me-pink:#ce00b8;\n  --me-yellow:#f4ff00;\n  --me-ink:#211427;\n  --me-muted:#746b79;\n  --me-line:#e8dfeb;\n  box-sizing:border-box;\n  max-width:1200px;\n  width:100%;\n  margin:0 auto;\n  padding:30px 20px 52px;\n  color:var(--me-ink);\n  font:16px\/1.6 Arial,Helvetica,sans-serif;\n  text-align:left;\n}\n#magica-esp *,\n#magica-esp *::before,\n#magica-esp *::after{\n  box-sizing:border-box;\n}\n#magica-esp [hidden]{\n  display:none!important;\n}\n#magica-esp h2,\n#magica-esp h3,\n#magica-esp p{\n  margin:0;\n}\n#magica-esp a{\n  color:inherit;\n  text-decoration:none;\n}\n#magica-esp button{\n  font:inherit;\n  cursor:pointer;\n  min-width:0;\n}\n#magica-esp a:focus-visible,\n#magica-esp button:focus-visible{\n  outline:3px solid var(--me-pink);\n  outline-offset:4px;\n}\n#magica-esp .me-heading{\n  display:flex;\n  justify-content:space-between;\n  align-items:center;\n  gap:25px;\n  margin-bottom:30px;\n}\n#magica-esp .me-eyebrow{\n  color:var(--me-pink);\n  font-size:11px;\n  letter-spacing:.13em;\n  font-weight:800;\n  line-height:1.5;\n  margin-bottom:8px;\n}\n#magica-esp .me-heading h2{\n  font:900 clamp(34px,5.3vw,62px)\/1.05 \"Arial Black\",Arial,sans-serif;\n  letter-spacing:-.055em;\n  color:var(--me-ink);\n}\n#magica-esp .me-heading h2 span{\n  color:var(--me-pink);\n}\n#magica-esp .me-intro{\n  color:var(--me-muted);\n  font-size:15px;\n  margin-top:13px;\n}\n#magica-esp .me-station{\n  flex-shrink:0;\n  background:linear-gradient(135deg,#bd00ad,#610b89);\n  color:white;\n  transform:rotate(-3deg);\n  border-radius:12px;\n  padding:17px 23px;\n  font-size:16px;\n  font-weight:900;\n  line-height:1.2;\n  box-shadow:5px 5px 0 var(--me-yellow);\n}\n#magica-esp .me-station b{\n  display:block;\n  font-size:25px;\n  color:var(--me-yellow);\n}\n#magica-esp .me-station small{\n  display:block;\n  font-size:10px;\n  letter-spacing:.025em;\n  font-weight:400;\n  margin-top:7px;\n}\n#magica-esp .me-section-title{\n  display:flex;\n  justify-content:space-between;\n  align-items:center;\n  gap:15px;\n  margin-bottom:13px;\n}\n#magica-esp .me-section-title h3{\n  display:flex;\n  align-items:center;\n  gap:9px;\n  font-size:17px;\n  font-weight:800;\n}\n#magica-esp .me-section-title>span{\n  font-size:10px;\n  letter-spacing:.09em;\n  color:var(--me-muted);\n  font-weight:700;\n}\n#magica-esp .me-dot{\n  width:8px;\n  height:8px;\n  flex:none;\n  border-radius:50%;\n  background:var(--me-pink);\n  box-shadow:0 0 0 4px #ce00b812;\n}\n#magica-esp .me-carousel{\n  overflow:hidden;\n  border:1px solid var(--me-line);\n  border-radius:18px;\n  background:white;\n  box-shadow:0 12px 35px #300b3c09;\n}\n#magica-esp .me-slides{\n  position:relative;\n  background:#361242;\n}\n#magica-esp .me-slide{\n  position:relative;\n  min-height:445px;\n  animation:me-appear .35s ease-out;\n}\n#magica-esp .me-slide-link{\n  position:relative;\n  display:flex;\n  align-items:end;\n  min-height:445px;\n  isolation:isolate;\n  overflow:hidden;\n}\n#magica-esp .me-media{\n  position:relative;\n  overflow:hidden;\n  background:linear-gradient(135deg,#c600b8,#581078 70%,#25122f);\n  aspect-ratio:16\/9;\n}\n#magica-esp .me-media::before{\n  content:\"M\u00c1GICA 100.1\";\n  position:absolute;\n  inset:0;\n  display:flex;\n  align-items:center;\n  justify-content:center;\n  color:#ffffff33;\n  font-weight:900;\n  font-size:clamp(25px,4vw,56px);\n  letter-spacing:-.04em;\n}\n#magica-esp .me-media img{\n  position:absolute;\n  inset:0;\n  display:block;\n  width:100%;\n  height:100%;\n  object-fit:cover;\n  transition:transform .35s;\n}\n#magica-esp .me-slide .me-media{\n  position:absolute;\n  inset:0;\n  aspect-ratio:auto;\n  z-index:-2;\n}\n#magica-esp .me-slide-link::after{\n  content:\"\";\n  position:absolute;\n  inset:0;\n  background:linear-gradient(\n    180deg,\n    #15051e05 5%,\n    #15051e40 30%,\n    #16051eeb 100%\n  );\n  z-index:-1;\n}\n#magica-esp .me-slide-copy{\n  padding:35px 38px;\n  max-width:920px;\n  color:white;\n}\n#magica-esp .me-tag{\n  display:inline-block;\n  padding:6px 10px;\n  border-radius:5px;\n  background:var(--me-yellow);\n  color:#201324;\n  font-size:10px;\n  font-weight:900;\n  letter-spacing:.08em;\n}\n#magica-esp .me-slide h3{\n  font:900 clamp(25px,3.5vw,42px)\/1.12 Arial,sans-serif;\n  letter-spacing:-.035em;\n  margin:13px 0 15px;\n  color:white;\n  text-wrap:balance;\n}\n#magica-esp .me-slide time{\n  font-size:12px;\n  color:#f1e6f1;\n}\n#magica-esp .me-slide-cta{\n  display:block;\n  margin-top:17px;\n  font-size:13px;\n  font-weight:800;\n  color:var(--me-yellow);\n}\n#magica-esp .me-carousel-bar{\n  display:flex;\n  align-items:center;\n  justify-content:space-between;\n  gap:12px;\n  padding:10px 18px;\n  background:white;\n}\n#magica-esp .me-arrows{\n  display:flex;\n  gap:8px;\n}\n#magica-esp .me-arrows button{\n  width:44px;\n  height:44px;\n  padding:0;\n  border:1px solid var(--me-line);\n  border-radius:50%;\n  background:white;\n  color:var(--me-ink);\n  font-size:21px;\n  line-height:1;\n}\n#magica-esp .me-arrows button:hover{\n  background:var(--me-yellow);\n  border-color:var(--me-yellow);\n}\n#magica-esp .me-dots{\n  display:flex;\n  gap:0;\n  justify-content:center;\n}\n#magica-esp .me-dots button{\n  position:relative;\n  width:34px;\n  min-height:44px;\n  padding:0;\n  border:0;\n  background:transparent;\n}\n#magica-esp .me-dots button::before{\n  content:\"\";\n  position:absolute;\n  width:7px;\n  height:7px;\n  top:18px;\n  left:13px;\n  border-radius:10px;\n  background:#d8cadc;\n  transition:background .2s;\n}\n#magica-esp .me-dots button[aria-current=true]::before{\n  background:var(--me-pink);\n  width:19px;\n  left:7px;\n}\n#magica-esp .me-pause{\n  border:0;\n  border-radius:5px;\n  background:#f5edf7;\n  padding:8px 12px;\n  min-height:44px;\n  font-size:12px;\n  font-weight:700;\n  color:var(--me-ink);\n}\n#magica-esp .me-archive{\n  margin-top:42px;\n  scroll-margin-top:120px;\n}\n#magica-esp .me-archive-heading{\n  display:flex;\n  justify-content:space-between;\n  align-items:end;\n  gap:20px;\n  margin-bottom:24px;\n  padding-top:21px;\n  border-top:1px solid var(--me-line);\n}\n#magica-esp .me-archive h3#me-archive-title{\n  font-size:31px;\n  line-height:1.15;\n  font-weight:900;\n  letter-spacing:-.04em;\n  outline:none;\n}\n#magica-esp .me-count{\n  color:var(--me-muted);\n  font-size:12px;\n  text-align:right;\n}\n#magica-esp .me-grid{\n  display:grid;\n  grid-template-columns:repeat(2,minmax(0,1fr));\n  gap:25px;\n}\n#magica-esp .me-card{\n  min-width:0;\n  overflow:hidden;\n  border:1px solid var(--me-line);\n  border-radius:14px;\n  background:white;\n  transition:box-shadow .2s,transform .2s;\n}\n#magica-esp .me-card:hover{\n  transform:translateY(-3px);\n  box-shadow:0 12px 25px #300b3c0d;\n}\n#magica-esp .me-card>a{\n  display:block;\n}\n#magica-esp .me-card:hover .me-media img{\n  transform:scale(1.035);\n}\n#magica-esp .me-card-copy{\n  padding:21px 23px 23px;\n}\n#magica-esp .me-card-meta{\n  display:flex;\n  flex-wrap:wrap;\n  justify-content:space-between;\n  align-items:center;\n  gap:6px 10px;\n  font-size:11px;\n  color:var(--me-muted);\n  margin-bottom:12px;\n}\n#magica-esp .me-card-meta span{\n  font-weight:800;\n  text-transform:uppercase;\n  letter-spacing:.04em;\n  color:var(--me-pink);\n}\n#magica-esp .me-card h3{\n  font-size:23px;\n  font-weight:800;\n  line-height:1.22;\n  letter-spacing:-.025em;\n  color:var(--me-ink);\n}\n#magica-esp .me-card p{\n  font-size:14px;\n  color:var(--me-muted);\n  line-height:1.65;\n  margin-top:12px;\n  display:-webkit-box;\n  -webkit-box-orient:vertical;\n  -webkit-line-clamp:2;\n  overflow:hidden;\n}\n#magica-esp .me-read{\n  display:inline-block;\n  color:var(--me-pink);\n  font-size:13px;\n  font-weight:800;\n  margin-top:17px;\n}\n#magica-esp .me-pagination{\n  display:flex;\n  justify-content:center;\n  align-items:center;\n  gap:8px;\n  flex-wrap:wrap;\n  margin-top:35px;\n}\n#magica-esp .me-pagination a{\n  display:inline-flex;\n  align-items:center;\n  justify-content:center;\n  min-width:44px;\n  min-height:44px;\n  padding:9px 13px;\n  border:1px solid #ded0e3;\n  border-radius:7px;\n  background:white;\n  color:var(--me-ink);\n  font-size:14px;\n  font-weight:700;\n}\n#magica-esp .me-pagination a:hover{\n  border-color:var(--me-pink);\n  color:var(--me-pink);\n}\n#magica-esp .me-pagination a[aria-current=page]{\n  background:var(--me-yellow);\n  border-color:var(--me-yellow);\n  color:#211427;\n}\n#magica-esp .me-pagination .me-ellipsis{\n  padding:8px 4px;\n  color:var(--me-muted);\n}\n#magica-esp .me-page-info{\n  text-align:center;\n  color:var(--me-muted);\n  font-size:12px;\n  margin-top:15px;\n}\n#magica-esp .me-status,\n#magica-esp .me-error{\n  padding:25px;\n  border-radius:12px;\n  border:1px solid var(--me-line);\n  background:#faf4fb;\n  margin-bottom:22px;\n  font-size:14px;\n  color:var(--me-muted);\n}\n#magica-esp .me-error button{\n  margin-top:13px;\n  padding:10px 17px;\n  border:0;\n  border-radius:6px;\n  color:white;\n  background:var(--me-pink);\n  font-weight:700;\n}\n#magica-esp .me-archive[aria-busy=true] .me-grid{\n  opacity:.5;\n  pointer-events:none;\n}\n@keyframes me-appear{\n  from{opacity:.4}\n  to{opacity:1}\n}\n@media(max-width:760px){\n  #magica-esp{padding:23px 12px 40px}\n  #magica-esp .me-station{padding:13px 15px;font-size:12px}\n  #magica-esp .me-station b{font-size:20px}\n  #magica-esp .me-heading{gap:17px}\n  #magica-esp .me-heading h2{font-size:43px}\n  #magica-esp .me-eyebrow{font-size:10px}\n  #magica-esp .me-slide,\n  #magica-esp .me-slide-link{min-height:370px}\n  #magica-esp .me-slide-copy{padding:27px}\n  #magica-esp .me-grid{gap:18px}\n  #magica-esp .me-card-copy{padding:18px}\n  #magica-esp .me-card h3{font-size:21px}\n}\n@media(max-width:560px){\n  #magica-esp .me-station{display:none}\n  #magica-esp .me-heading h2{\n    font-size:clamp(32px,10.8vw,42px);\n  }\n  #magica-esp .me-intro{font-size:14px}\n  #magica-esp .me-section-title>span{display:none}\n  #magica-esp .me-section-title h3{font-size:15px}\n  #magica-esp .me-slide,\n  #magica-esp .me-slide-link{min-height:360px}\n  #magica-esp .me-slide-copy{padding:24px 21px}\n  #magica-esp .me-slide h3{font-size:28px}\n  #magica-esp .me-carousel-bar{\n    display:grid;\n    grid-template-columns:1fr auto;\n    gap:1px 8px;\n    padding:9px 12px;\n  }\n  #magica-esp .me-dots{\n    grid-column:1\/-1;\n    grid-row:2;\n  }\n  #magica-esp .me-pause{\n    grid-column:2;\n    grid-row:1;\n    padding:8px;\n    font-size:11px;\n  }\n  #magica-esp .me-dots button{width:27px}\n  #magica-esp .me-dots button::before{left:10px}\n  #magica-esp .me-dots button[aria-current=true]::before{\n    left:4px;\n  }\n  #magica-esp .me-arrows{gap:5px}\n  #magica-esp .me-arrows button{width:42px}\n  #magica-esp .me-grid{\n    grid-template-columns:1fr;\n    gap:20px;\n  }\n  #magica-esp .me-archive-heading{display:block}\n  #magica-esp .me-count{\n    text-align:left;\n    margin-top:10px;\n  }\n  #magica-esp .me-card h3{font-size:24px}\n  #magica-esp .me-pagination{gap:6px}\n  #magica-esp .me-pagination a{padding-inline:11px}\n  #magica-esp .me-archive{scroll-margin-top:95px}\n}\n@media(prefers-reduced-motion:reduce){\n  #magica-esp *,\n  #magica-esp *::before{\n    animation:none!important;\n    transition:none!important;\n  }\n}\n<\/style>\n\n<script>\n(() => {\n  function initialize() {\n    'use strict';\n\n    const root = document.getElementById('magica-esp');\n    if (!root || root.dataset.ready) return;\n    root.dataset.ready = 'true';\n\n    const $ = selector => root.querySelector(selector);\n    const perPage = 10;\n    const pageKey = 'magica_esp_p';\n    const cache = new Map();\n\n    let api = '';\n    let category = null;\n    let current = 1;\n    let totalPages = 1;\n    let total = null;\n    let slides = [];\n    let slideIndex = 0;\n\n    let auto = !matchMedia(\n      '(prefers-reduced-motion: reduce)'\n    ).matches;\n\n    let hover = false;\n    let timer;\n    let controller;\n    let requestId = 0;\n\n    let source = 'rest';\n    let feedHome = '';\n    let feedSize = 0;\n    let exactPages = true;\n\n    const feedCache = new Map();\n    const feedStats = { total: null };\n\n    const plain = html => {\n      const template = document.createElement('template');\n      template.innerHTML = html || '';\n\n      return (template.content.textContent || '')\n        .replace(\/\\s+\/g, ' ')\n        .trim();\n    };\n\n    const safe = value => {\n      if (typeof value !== 'string' || !value.trim()) {\n        return '';\n      }\n\n      try {\n        const url = new URL(value, location.href);\n        return \/^https?:$\/.test(url.protocol) ? url.href : '';\n      } catch {\n        return '';\n      }\n    };\n\n    const escape = value => String(value ?? '').replace(\n      \/[&<>\"']\/g,\n      character => ({\n        '&': '&amp;',\n        '<': '&lt;',\n        '>': '&gt;',\n        '\"': '&quot;',\n        \"'\": '&#39;'\n      }[character])\n    );\n\n    const names = (\n      root.dataset.category || 'espectaculos,farandula'\n    ).split(',').map(value => value.trim()).filter(Boolean);\n\n    const normalize = value => plain(value)\n      .normalize('NFD')\n      .replace(\/[\\u0300-\\u036f]\/g, '')\n      .toLowerCase();\n\n    const restLink = document.querySelector(\n      'link[rel=\"https:\/\/api.w.org\/\"]'\n    )?.href || window.wpApiSettings?.root || '';\n\n    const rssLink = document.querySelector(\n      'link[rel=\"alternate\"][type=\"application\/rss+xml\"]'\n    )?.href || '';\n\n    function requestedPage() {\n      const value = Number(\n        new URL(location.href).searchParams.get(pageKey)\n      );\n\n      return Number.isSafeInteger(value) && value > 0\n        ? value\n        : 1;\n    }\n\n    function pageHref(page) {\n      const url = new URL(location.href);\n\n      if (page === 1) {\n        url.searchParams.delete(pageKey);\n      } else {\n        url.searchParams.set(pageKey, page);\n      }\n\n      url.hash = 'me-archivo';\n      return url.href;\n    }\n\n    function endpoint(base, path, parameters = {}) {\n      const url = new URL(base, location.href);\n\n      if (url.searchParams.has('rest_route')) {\n        url.searchParams.set(\n          'rest_route',\n          '\/wp\/v2\/' + path\n        );\n      } else {\n        url.pathname = url.pathname.replace(\/\\\/?$\/, '\/') +\n          'wp\/v2\/' + path;\n      }\n\n      Object.entries(parameters).forEach(([key, value]) => {\n        url.searchParams.set(key, value);\n      });\n\n      return url.href;\n    }\n\n    async function json(url, signal) {\n      const response = await fetch(url, {\n        signal,\n        credentials: 'same-origin',\n        headers: { Accept: 'application\/json' }\n      });\n\n      if (!response.ok) {\n        throw new Error('HTTP ' + response.status);\n      }\n\n      const data = await response.json();\n\n      if (!Array.isArray(data)) {\n        throw new Error('Respuesta inv\u00e1lida');\n      }\n\n      return { response, data };\n    }\n\n    async function text(url, signal) {\n      const response = await fetch(url, {\n        signal,\n        credentials: 'same-origin'\n      });\n\n      if (!response.ok) {\n        const error = new Error('HTTP ' + response.status);\n        error.status = response.status;\n        throw error;\n      }\n\n      return response.text();\n    }\n\n    function homeFrom(value) {\n      const url = new URL(value, location.href);\n      url.hash = '';\n      url.search = '';\n\n      url.pathname = url.pathname\n        .replace(\/\\\/wp-json(?:\\\/.*)?$\/, '\/')\n        .replace(\/\\\/feed\\\/?$\/, '\/');\n\n      return url.href;\n    }\n\n    function feedUrl(home, page, feed = true) {\n      const url = new URL(home);\n      url.search = '';\n      url.searchParams.set('category_name', names.join(','));\n\n      if (feed) {\n        url.searchParams.set('feed', 'rss2');\n      }\n\n      if (page > 1) {\n        url.searchParams.set('paged', page);\n      }\n\n      return url.href;\n    }\n\n    async function discover() {\n      const manual = root.dataset.apiRoot;\n      const home = root.dataset.wordpressUrl;\n      const preferred = manual || restLink;\n\n      const bases = preferred ? [preferred] : [\n        home\n          ? new URL(\n              'wp-json\/',\n              home.replace(\/\\\/?$\/, '\/')\n            ).href\n          : new URL('\/wp-json\/', location.origin).href,\n\n        new URL(\n          '\/?rest_route=\/',\n          home || location.origin\n        ).href,\n\n        new URL(\n          '\/wordpress\/index.php?rest_route=\/',\n          location.origin\n        ).href\n      ];\n\n      for (const base of [...new Set(bases)]) {\n        const abort = new AbortController();\n\n        const timeout = setTimeout(\n          () => abort.abort(),\n          6000\n        );\n\n        try {\n          let { data } = await json(\n            endpoint(base, 'categories', {\n              slug: names.join(','),\n              per_page: 100,\n              _fields: 'id,name,slug,link'\n            }),\n            abort.signal\n          );\n\n          if (!data.length) {\n            const all = await json(\n              endpoint(base, 'categories', {\n                per_page: 100,\n                _fields: 'id,name,slug,link'\n              }),\n              abort.signal\n            );\n\n            data = all.data.filter(term => names.some(\n              name =>\n                normalize(term.name) === normalize(name) ||\n                term.slug === name\n            ));\n          }\n\n          if (data.length) {\n            api = base;\n\n            category = {\n              id: data.map(term => term.id).join(',')\n            };\n\n            return;\n          }\n        } catch {\n        } finally {\n          clearTimeout(timeout);\n        }\n      }\n\n      await enableFeed();\n    }\n\n    async function readFeed(page, signal) {\n      if (feedCache.has(page)) {\n        return feedCache.get(page);\n      }\n\n      let xml;\n\n      try {\n        xml = await text(\n          feedUrl(feedHome, page),\n          signal\n        );\n      } catch (error) {\n        if (page > 1 && error.status === 404) {\n          feedCache.set(page, []);\n          return [];\n        }\n\n        throw error;\n      }\n\n      const documentXML = new DOMParser().parseFromString(\n        xml,\n        'text\/xml'\n      );\n\n      const channel = documentXML.querySelector('channel');\n\n      if (\n        documentXML.querySelector('parsererror') ||\n        !channel\n      ) {\n        throw new Error('Feed no disponible');\n      }\n\n      const generator = channel.querySelector(\n        'generator'\n      )?.textContent || '';\n\n      if (\n        generator &&\n        !generator.toLowerCase().includes('wordpress')\n      ) {\n        throw new Error('No es el feed de WordPress');\n      }\n\n      const posts = Array.from(\n        channel.querySelectorAll('item')\n      ).map((item, index) => {\n        const value = tag =>\n          item.getElementsByTagName(tag)[0]?.textContent || '';\n\n        const html = value('content:encoded') ||\n          value('description');\n\n        const content = new DOMParser().parseFromString(\n          html,\n          'text\/html'\n        );\n\n        const enclosure = item.querySelector(\n          'enclosure[type^=\"image\/\"]'\n        );\n\n        const media =\n          item.getElementsByTagName('media:content')[0] ||\n          item.getElementsByTagName('media:thumbnail')[0];\n\n        const image = safe(\n          enclosure?.getAttribute('url') ||\n          media?.getAttribute('url') ||\n          content.querySelector('img')?.getAttribute('src') ||\n          ''\n        );\n\n        const published = new Date(value('pubDate'));\n\n        return {\n          id: value('guid') || page + '-' + index,\n          link: value('link'),\n          title: {\n            rendered: value('title')\n          },\n          excerpt: {\n            rendered: value('description')\n          },\n          date_gmt: Number.isNaN(published.getTime())\n            ? ''\n            : published.toISOString().replace(\/Z$\/, ''),\n          _embedded: {\n            'wp:featuredmedia': image\n              ? [{\n                  source_url: image,\n                  alt_text: content.querySelector('img')?.alt || ''\n                }]\n              : []\n          }\n        };\n      });\n\n      feedCache.set(page, posts);\n      return posts;\n    }\n\n    function archiveInfo(html) {\n      const doc = new DOMParser().parseFromString(\n        html,\n        'text\/html'\n      );\n\n      const template = doc.querySelector(\n        'main .wp-block-post-template'\n      ) || doc.querySelector('.wp-block-post-template');\n\n      const count = template\n        ? Array.from(template.children).filter(\n            child => child.matches('li,.wp-block-post')\n          ).length\n        : doc.querySelectorAll(\n            'main article[id^=\"post-\"],' +\n            '#primary article[id^=\"post-\"]'\n          ).length;\n\n      let last = 1;\n      let lastUrl = '';\n\n      doc.querySelectorAll(\n        '.wp-block-query-pagination a,' +\n        '.pagination a,' +\n        '.nav-links a'\n      ).forEach(link => {\n        let number = Number(link.textContent.trim());\n        let url;\n\n        try {\n          url = new URL(\n            link.getAttribute('href'),\n            feedHome\n          );\n        } catch {\n          return;\n        }\n\n        for (const [key, value] of url.searchParams) {\n          if (\n            key === 'paged' ||\n            \/^query-\\d+-page$\/.test(key)\n          ) {\n            number = Math.max(\n              number || 0,\n              Number(value) || 0\n            );\n          }\n        }\n\n        const path = url.pathname.match(\n          \/\\\/page\\\/(\\d+)\\\/?\/\n        );\n\n        if (path) {\n          number = Math.max(\n            number || 0,\n            Number(path[1])\n          );\n        }\n\n        if (number > last) {\n          last = number;\n          lastUrl = url.href;\n        }\n      });\n\n      return { count, last, lastUrl };\n    }\n\n    async function enableFeed(signal) {\n      const custom = root.dataset.wordpressUrl;\n\n      const homes = [\n        custom,\n        restLink ? homeFrom(restLink) : '',\n        rssLink ? homeFrom(rssLink) : '',\n        location.protocol === 'file:'\n          ? ''\n          : location.origin + '\/',\n        location.protocol === 'file:'\n          ? ''\n          : location.origin + '\/wordpress\/index.php'\n      ].filter(Boolean);\n\n      let found = false;\n\n      for (const home of [...new Set(homes)]) {\n        if (signal?.aborted) {\n          throw new DOMException('Cancelado', 'AbortError');\n        }\n\n        feedHome = home;\n        feedCache.clear();\n\n        try {\n          const posts = await readFeed(1, signal);\n          feedSize = Math.max(1, posts.length);\n          found = true;\n          break;\n        } catch (error) {\n          if (signal?.aborted) throw error;\n        }\n      }\n\n      if (!found) {\n        throw new Error('WordPress no accesible');\n      }\n\n      source = 'rss';\n      category = { id: '' };\n      exactPages = false;\n\n      try {\n        const first = archiveInfo(\n          await text(\n            feedUrl(feedHome, 1, false),\n            signal\n          )\n        );\n\n        if (\n          first.last > 1 &&\n          first.count &&\n          first.lastUrl\n        ) {\n          const last = archiveInfo(\n            await text(first.lastUrl, signal)\n          );\n\n          if (last.count) {\n            feedStats.total =\n              (first.last - 1) * first.count + last.count;\n\n            exactPages = true;\n          }\n        }\n      } catch (error) {\n        if (signal?.aborted) throw error;\n      }\n    }\n\n    async function feedPage(page, signal) {\n      const offset = (page - 1) * perPage;\n\n      const start = Math.floor(\n        offset \/ feedSize\n      ) + 1;\n\n      const end = Math.floor(\n        (offset + perPage - 1) \/ feedSize\n      ) + 1;\n\n      const groups = await Promise.all(\n        Array.from(\n          { length: end - start + 1 },\n          (_, index) => readFeed(\n            start + index,\n            signal\n          )\n        )\n      );\n\n      const posts = groups.flat().slice(\n        offset % feedSize,\n        offset % feedSize + perPage\n      );\n\n      if (feedStats.total !== null) {\n        exactPages = true;\n\n        return {\n          posts,\n          total: feedStats.total,\n          pages: Math.max(\n            1,\n            Math.ceil(feedStats.total \/ perPage)\n          )\n        };\n      }\n\n      const nextPosition = offset + perPage;\n\n      const nextFeed = await readFeed(\n        Math.floor(nextPosition \/ feedSize) + 1,\n        signal\n      );\n\n      const more = !!nextFeed[\n        nextPosition % feedSize\n      ];\n\n      exactPages = !more;\n\n      if (!more) {\n        feedStats.total = offset + posts.length;\n      }\n\n      return {\n        posts,\n        total: more ? null : offset + posts.length,\n        pages: more ? page + 1 : Math.max(1, page)\n      };\n    }\n\n    async function fetchPage(page, signal) {\n      if (source === 'rss') {\n        return feedPage(page, signal);\n      }\n\n      try {\n        const { response, data } = await json(\n          endpoint(api, 'posts', {\n            categories: category.id,\n            per_page: perPage,\n            page,\n            orderby: 'date',\n            order: 'desc',\n            status: 'publish',\n            _embed: 'wp:featuredmedia',\n            _fields:\n              'id,date,date_gmt,link,title,excerpt,' +\n              'featured_media,_links,_embedded'\n          }),\n          signal\n        );\n\n        const count = response.headers.get(\n          'X-WP-Total'\n        );\n\n        const pages = response.headers.get(\n          'X-WP-TotalPages'\n        );\n\n        exactPages = pages !== null;\n\n        return {\n          posts: data,\n          total: count !== null ? Number(count) : null,\n          pages: pages !== null\n            ? Math.max(1, Number(pages))\n            : page + (data.length === perPage ? 1 : 0)\n        };\n      } catch (error) {\n        if (signal.aborted) throw error;\n\n        await enableFeed(signal);\n        cache.clear();\n\n        return feedPage(page, signal);\n      }\n    }\n\n    function date(post) {\n      const iso = post.date_gmt\n        ? post.date_gmt.replace(\/Z$\/, '') + 'Z'\n        : post.date;\n\n      const parsed = new Date(iso);\n\n      if (Number.isNaN(parsed.getTime())) {\n        return '';\n      }\n\n      const formatted = parsed.toLocaleDateString(\n        'es-MX',\n        {\n          day: 'numeric',\n          month: 'short',\n          year: 'numeric',\n          timeZone: 'America\/Chihuahua'\n        }\n      );\n\n      return '<time datetime=\"' +\n        escape(iso) + '\">' +\n        escape(formatted) +\n        '<\/time>';\n    }\n\n    function media(post, hero = false) {\n      const image = post._embedded?.[\n        'wp:featuredmedia'\n      ]?.[0];\n\n      const sizes = image?.media_details?.sizes || {};\n\n      const src = safe(\n        (hero\n          ? sizes.large?.source_url\n          : sizes.medium_large?.source_url\n        ) || image?.source_url || ''\n      );\n\n      const holder = document.createElement('div');\n      holder.className = 'me-media';\n\n      if (src) {\n        const img = document.createElement('img');\n\n        img.src = src;\n        img.alt = plain(image?.alt_text) ||\n          plain(post.title?.rendered);\n\n        img.loading = hero ? 'eager' : 'lazy';\n        img.decoding = 'async';\n        img.width = 1000;\n        img.height = 563;\n\n        holder.append(img);\n      }\n\n      return holder.outerHTML;\n    }\n\n    function checkImages(holder) {\n      holder.querySelectorAll('img').forEach(img => {\n        img.addEventListener('error', () => {\n          img.hidden = true;\n        }, { once: true });\n      });\n    }\n\n    function showSlide(index, manual = false) {\n      if (!slides.length) {\n        updateTimer();\n        return;\n      }\n\n      slideIndex = (\n        index + slides.length\n      ) % slides.length;\n\n      $('[data-slides]').querySelectorAll(\n        '.me-slide'\n      ).forEach((slide, position) => {\n        slide.hidden = position !== slideIndex;\n      });\n\n      $('[data-dots]').querySelectorAll(\n        'button'\n      ).forEach((button, position) => {\n        button.setAttribute(\n          'aria-current',\n          String(position === slideIndex)\n        );\n      });\n\n      if (manual) auto = false;\n      updateTimer();\n    }\n\n    function updateTimer() {\n      clearInterval(timer);\n\n      $('[data-pause]').textContent = auto\n        ? 'Pausar'\n        : 'Reproducir';\n\n      $('[data-pause]').setAttribute(\n        'aria-pressed',\n        String(!auto)\n      );\n\n      if (\n        auto &&\n        !hover &&\n        !document.hidden &&\n        slides.length > 1 &&\n        root.isConnected\n      ) {\n        timer = setInterval(() => {\n          if (root.isConnected) {\n            showSlide(slideIndex + 1);\n          } else {\n            clearInterval(timer);\n          }\n        }, 6500);\n      }\n    }\n\n    function renderCarousel(posts) {\n      slides = posts.slice(0, 4);\n\n      $('[data-featured]').hidden = !slides.length;\n\n      $('[data-slides]').innerHTML = slides.map(\n        (post, index) => {\n          const title = plain(post.title?.rendered) ||\n            'Noticia de espect\u00e1culos';\n\n          return '<div class=\"me-slide\" role=\"group\"' +\n            ' aria-roledescription=\"diapositiva\"' +\n            ' aria-label=\"' + (index + 1) +\n            ' de ' + slides.length + '\"' +\n            (index ? ' hidden' : '') + '>' +\n            '<a class=\"me-slide-link\" href=\"' +\n            escape(safe(post.link)) + '\">' +\n            media(post, true) +\n            '<div class=\"me-slide-copy\">' +\n            '<span class=\"me-tag\">' +\n            'ESPECT\u00c1CULOS \u00b7 LO M\u00c1S RECIENTE<\/span>' +\n            '<h3>' + escape(title) + '<\/h3>' +\n            date(post) +\n            '<span class=\"me-slide-cta\">' +\n            'Leer nota completa \u2192<\/span>' +\n            '<\/div><\/a><\/div>';\n        }\n      ).join('');\n\n      $('[data-dots]').replaceChildren();\n\n      slides.forEach((post, index) => {\n        const button = document.createElement('button');\n        button.type = 'button';\n\n        button.setAttribute(\n          'aria-label',\n          'Ver noticia ' + (index + 1) +\n          ' de ' + slides.length\n        );\n\n        button.addEventListener(\n          'click',\n          () => showSlide(index, true)\n        );\n\n        $('[data-dots]').append(button);\n      });\n\n      $('[data-carousel-controls]').hidden =\n        slides.length < 2;\n\n      checkImages($('[data-slides]'));\n      showSlide(0);\n    }\n\n    function renderGrid(posts) {\n      $('[data-grid]').innerHTML = posts.map(post => {\n        const title = plain(post.title?.rendered) ||\n          'Noticia de espect\u00e1culos';\n\n        const excerpt = post.excerpt?.rendered\n          ? '<p>' +\n            escape(\n              plain(post.excerpt.rendered).slice(0, 190)\n            ) +\n            '<\/p>'\n          : '';\n\n        return '<article class=\"me-card\">' +\n          '<a href=\"' + escape(safe(post.link)) + '\">' +\n          media(post) +\n          '<div class=\"me-card-copy\">' +\n          '<div class=\"me-card-meta\">' +\n          '<span>Espect\u00e1culos<\/span>' +\n          date(post) +\n          '<\/div>' +\n          '<h3>' + escape(title) + '<\/h3>' +\n          excerpt +\n          '<span class=\"me-read\">' +\n          'Leer nota completa \u2192<\/span>' +\n          '<\/div><\/a><\/article>';\n      }).join('');\n\n      checkImages($('[data-grid]'));\n\n      const first = (current - 1) * perPage + 1;\n      const last = first + posts.length - 1;\n\n      $('[data-count]').textContent = total === null\n        ? posts.length + ' noticias en esta p\u00e1gina'\n        : 'Mostrando ' + first + '\u2013' + last +\n          ' de ' + total + ' noticias';\n\n      $('[data-page-info]').textContent =\n        'P\u00e1gina ' + current +\n        (exactPages ? ' de ' + totalPages : '');\n\n      renderPagination();\n    }\n\n    function renderPagination() {\n      const navigation = $('[data-pagination]');\n      navigation.replaceChildren();\n      navigation.hidden = totalPages < 2;\n\n      function link(page, label) {\n        const anchor = document.createElement('a');\n\n        anchor.href = pageHref(page);\n        anchor.dataset.page = page;\n        anchor.textContent = label || page;\n\n        anchor.setAttribute(\n          'aria-label',\n          label || 'P\u00e1gina ' + page\n        );\n\n        if (!label) {\n          if (page === current) {\n            anchor.setAttribute(\n              'aria-current',\n              'page'\n            );\n          }\n        }\n\n        navigation.append(anchor);\n      }\n\n      if (current > 1) {\n        link(current - 1, '\u2190 Anterior');\n      }\n\n      const numbers = [...new Set([\n        1,\n        current - 1,\n        current,\n        current + 1,\n        totalPages\n      ])].filter(\n        number => number > 0 && number <= totalPages\n      ).sort((a, b) => a - b);\n\n      let previous = 0;\n\n      numbers.forEach(number => {\n        if (previous && number - previous > 1) {\n          const ellipsis = document.createElement('span');\n\n          ellipsis.className = 'me-ellipsis';\n          ellipsis.textContent = '\u2026';\n          ellipsis.setAttribute('aria-hidden', 'true');\n\n          navigation.append(ellipsis);\n        }\n\n        link(number);\n        previous = number;\n      });\n\n      if (current < totalPages) {\n        link(current + 1, 'Siguiente \u2192');\n      }\n    }\n\n    async function loadPage(\n      page,\n      { push = false, scroll = false } = {}\n    ) {\n      const id = ++requestId;\n\n      controller?.abort();\n      controller = new AbortController();\n\n      const activeController = controller;\n\n      const timeout = setTimeout(\n        () => activeController.abort(),\n        25000\n      );\n\n      $('[data-error]').hidden = true;\n\n      $('.me-archive').setAttribute(\n        'aria-busy',\n        'true'\n      );\n\n      $('[data-status]').textContent =\n        'Cargando noticias\u2026';\n\n      $('[data-status]').hidden = false;\n\n      try {\n        let result = cache.get(page);\n\n        if (!result) {\n          result = await fetchPage(\n            page,\n            activeController.signal\n          );\n\n          cache.set(page, result);\n\n          if (cache.size > 6) {\n            cache.delete(\n              [...cache.keys()].find(\n                number => number !== 1 && number !== page\n              )\n            );\n          }\n        }\n\n        if (id !== requestId) return;\n\n        if (\n          source === 'rss' &&\n          feedStats.total !== null\n        ) {\n          result.total = feedStats.total;\n\n          result.pages = Math.max(\n            1,\n            Math.ceil(feedStats.total \/ perPage)\n          );\n\n          exactPages = true;\n        }\n\n        current = page;\n        total = result.total;\n        totalPages = result.pages;\n\n        if (page === 1) {\n          renderCarousel(result.posts);\n        }\n\n        if (!result.posts.length) {\n          $('[data-status]').textContent =\n            'Muy pronto encontrar\u00e1s aqu\u00ed las noticias ' +\n            'de espect\u00e1culos.';\n\n          $('[data-featured]').hidden = true;\n          $('.me-archive').hidden = true;\n          return;\n        }\n\n        renderGrid(result.posts);\n\n        $('.me-archive').hidden = false;\n        $('[data-status]').hidden = true;\n\n        if (push) {\n          history.pushState(\n            { magicaEspectaculos: current },\n            '',\n            pageHref(current)\n          );\n        }\n\n        if (scroll) {\n          $('#me-archivo').scrollIntoView({\n            behavior: 'instant',\n            block: 'start'\n          });\n\n          $('#me-archive-title').focus({\n            preventScroll: true\n          });\n        }\n      } catch (error) {\n        if (id !== requestId) return;\n\n        $('[data-status]').hidden = true;\n        $('[data-error]').hidden = false;\n\n        $('[data-error] p').textContent =\n          'No se pudieron cargar las noticias en este ' +\n          'momento. Intenta nuevamente.';\n      } finally {\n        clearTimeout(timeout);\n\n        if (id === requestId) {\n          $('.me-archive').setAttribute(\n            'aria-busy',\n            'false'\n          );\n        }\n      }\n    }\n\n    async function start() {\n      $('[data-error]').hidden = true;\n      $('[data-status]').hidden = false;\n\n      try {\n        await discover();\n\n        if (!category) {\n          $('[data-status]').textContent =\n            'Muy pronto encontrar\u00e1s aqu\u00ed las noticias ' +\n            'de espect\u00e1culos.';\n          return;\n        }\n\n        const requested = requestedPage();\n\n        await loadPage(1);\n\n        if (requested > 1) {\n          await loadPage(\n            exactPages\n              ? Math.min(requested, totalPages)\n              : requested\n          );\n        }\n      } catch {\n        $('[data-status]').hidden = true;\n        $('[data-error]').hidden = false;\n\n        $('[data-error] p').textContent =\n          'No se pudieron cargar las noticias en este ' +\n          'momento. Intenta nuevamente.';\n      }\n    }\n\n    $('[data-prev]').addEventListener(\n      'click',\n      () => showSlide(slideIndex - 1, true)\n    );\n\n    $('[data-next]').addEventListener(\n      'click',\n      () => showSlide(slideIndex + 1, true)\n    );\n\n    $('[data-pause]').addEventListener('click', () => {\n      auto = !auto;\n      updateTimer();\n    });\n\n    $('[data-carousel]').addEventListener(\n      'mouseenter',\n      () => {\n        hover = true;\n        updateTimer();\n      }\n    );\n\n    $('[data-carousel]').addEventListener(\n      'mouseleave',\n      () => {\n        hover = false;\n        updateTimer();\n      }\n    );\n\n    $('[data-carousel]').addEventListener(\n      'focusin',\n      event => {\n        if (event.target !== $('[data-pause]')) {\n          auto = false;\n          updateTimer();\n        }\n      }\n    );\n\n    document.addEventListener(\n      'visibilitychange',\n      updateTimer\n    );\n\n    $('[data-pagination]').addEventListener(\n      'click',\n      event => {\n        const anchor = event.target.closest(\n          'a[data-page]'\n        );\n\n        if (\n          !anchor ||\n          event.button !== 0 ||\n          event.ctrlKey ||\n          event.metaKey ||\n          event.shiftKey ||\n          event.altKey\n        ) return;\n\n        event.preventDefault();\n\n        if (Number(anchor.dataset.page) !== current) {\n          loadPage(\n            Number(anchor.dataset.page),\n            {\n              push: true,\n              scroll: true\n            }\n          );\n        }\n      }\n    );\n\n    $('[data-retry]').addEventListener(\n      'click',\n      () => category ? loadPage(current) : start()\n    );\n\n    window.addEventListener('popstate', () => {\n      if (category) {\n        loadPage(\n          exactPages\n            ? Math.min(requestedPage(), totalPages)\n            : requestedPage(),\n          { scroll: true }\n        );\n      }\n    });\n\n    if (\n      location.protocol === 'file:' &&\n      !root.dataset.wordpressUrl\n    ) {\n      $('[data-status]').textContent =\n        'Pega este HTML en el widget HTML de Elementor ' +\n        'o en HTML personalizado de WordPress para ' +\n        'cargar tus entradas.';\n    } else {\n      start();\n    }\n  }\n\n  if (document.readyState === 'loading') {\n    document.addEventListener(\n      'DOMContentLoaded',\n      initialize,\n      { once: true }\n    );\n  } else {\n    initialize();\n  }\n})();\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>EL LADO M\u00c1S M\u00c1GICO DEL ENTRETENIMIENTO Espect\u00e1culos. M\u00fasica, cine y las historias que dan de qu\u00e9 hablar. M\u00c1GICA 100.1 FM Tan diferente como t\u00fa. Cargando las noticias de espect\u00e1culos\u2026 Volver a intentar Lo \u00faltimo en espect\u00e1culos ACTUALIDAD \u00b7 M\u00c1GICA \u2190 \u2192 Pausar QUE NO SE TE PASE NINGUNA Todas las noticias Activa JavaScript para consultar [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-14","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/magicafm.mx\/index.php?rest_route=\/wp\/v2\/pages\/14","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/magicafm.mx\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/magicafm.mx\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/magicafm.mx\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/magicafm.mx\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=14"}],"version-history":[{"count":2,"href":"https:\/\/magicafm.mx\/index.php?rest_route=\/wp\/v2\/pages\/14\/revisions"}],"predecessor-version":[{"id":24,"href":"https:\/\/magicafm.mx\/index.php?rest_route=\/wp\/v2\/pages\/14\/revisions\/24"}],"wp:attachment":[{"href":"https:\/\/magicafm.mx\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=14"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}