{"id":1536,"date":"2026-08-12T09:19:51","date_gmt":"2026-08-12T09:19:51","guid":{"rendered":"https:\/\/villasrivero.com\/?page_id=1536"},"modified":"2026-08-12T11:18:51","modified_gmt":"2026-08-12T11:18:51","slug":"dashboard-era-del-centeno","status":"publish","type":"page","link":"http:\/\/villasrivero.com\/en\/dashboard-era-del-centeno\/","title":{"rendered":"Dashboard Era del centeno"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"1536\" class=\"elementor elementor-1536\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-65049e3 e-flex e-con-boxed e-con e-parent\" data-id=\"65049e3\" 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-ea19808 elementor-widget elementor-widget-html\" data-id=\"ea19808\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"es\"><head><meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>Era del Centeno &middot; Revenue Dashboard<\/title>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Sorts+Mill+Goudy:ital@0;1&family=Montserrat:wght@300;400;500;600;700&display=swap\" rel=\"stylesheet\">\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/Chart.js\/4.4.1\/chart.umd.min.js\"><\/script>\n<style>\n:root{--sea:#4ABCC5;--cadet:#242B59;--tan:#D1B38C;--black:#0E0E0E;\n--bg:#F7F6F3;--card:#FFF;--line:#E3E1DC;--mut:#6E6B65;--up:#2F9E6D;--up-soft:#EAF7F0;--dn:#B87355;--dn-soft:#F8EEE9;}\n*{box-sizing:border-box;margin:0;padding:0}\nbody{font-family:Montserrat,sans-serif;background:var(--bg);color:var(--black);-webkit-font-smoothing:antialiased;font-size:15px}\n.wrap{max-width:1340px;margin:0 auto;padding:0 28px}\nheader{background:var(--cadet);color:#fff;padding:32px 0 28px;border-bottom:3px solid var(--tan)}\n.hd{display:flex;justify-content:space-between;align-items:flex-end;gap:24px;flex-wrap:wrap}\n.brand{font-family:'Sorts Mill Goudy',serif;font-style:italic;font-size:34px;line-height:1}\n.brand span{color:var(--sea)}\n.sub{font-size:10.5px;letter-spacing:2.6px;text-transform:uppercase;color:var(--tan);margin-top:9px;font-weight:500}\n.per{text-align:right;font-size:11px;color:#B9BFD6;line-height:1.75}.per b{color:#fff;font-size:13px;font-weight:600}\nnav{background:var(--card);border-bottom:1px solid var(--line);position:sticky;top:0;z-index:30}\n.nv{display:flex;gap:2px;overflow-x:auto}\n.nv button{background:none;border:0;padding:15px 18px;font-family:Montserrat;font-size:12px;font-weight:500;\nletter-spacing:.7px;color:var(--mut);cursor:pointer;white-space:nowrap;border-bottom:2px solid transparent}\n.nv button:hover{color:var(--cadet)}\n.nv button.on{color:var(--cadet);border-bottom-color:var(--sea);font-weight:600}\nmain{padding:26px 0 60px}.pane{display:none}.pane.on{display:block}\n.eyebrow{font-size:10px;letter-spacing:2.4px;text-transform:uppercase;color:var(--mut);font-weight:600;margin-bottom:6px}\nh2{font-family:'Sorts Mill Goudy',serif;font-size:25px;font-weight:400;margin-bottom:4px;color:var(--cadet)}\n.lead{color:var(--mut);font-size:13px;margin-bottom:18px;max-width:820px;line-height:1.6}\n.filt{background:var(--card);border:1px solid var(--line);border-radius:3px;padding:15px 18px;margin-bottom:18px;\ndisplay:flex;gap:24px;align-items:flex-start;flex-wrap:wrap}\n.fg{display:flex;flex-direction:column;gap:7px}\n.fg>span{font-size:9.5px;letter-spacing:1.5px;text-transform:uppercase;color:var(--mut);font-weight:600}\n.seg{display:flex;border:1px solid var(--line);border-radius:2px;overflow:hidden;flex-wrap:wrap}\n.seg button{background:#fff;border:0;padding:8px 14px;font-family:Montserrat;font-size:12px;color:var(--mut);\ncursor:pointer;border-right:1px solid var(--line);white-space:nowrap}\n.seg button:last-child{border-right:0}\n.seg button.on{background:var(--cadet);color:#fff;font-weight:600}\n.fg .cnt{font-size:11px;color:var(--mut)}\n.picker{position:relative}\n.picker>button{background:#fff;border:1px solid var(--line);border-radius:2px;padding:8px 14px;\nfont-family:Montserrat;font-size:12px;cursor:pointer;min-width:190px;text-align:left}\n.pop{display:none;position:absolute;top:calc(100% + 5px);left:0;z-index:40;background:#fff;border:1px solid var(--line);\nborder-radius:3px;box-shadow:0 8px 26px rgba(0,0,0,.13);width:340px;max-height:380px;overflow:auto;padding:9px}\n.pop.on{display:block}\n.pop .act{display:flex;gap:6px;padding:4px 4px 9px;border-bottom:1px solid var(--line);margin-bottom:6px}\n.pop .act button{flex:1;background:#F4F3F0;border:0;padding:6px;font-size:11px;font-family:Montserrat;cursor:pointer;\nborder-radius:2px;color:var(--cadet);font-weight:500}\n.pop label{display:flex;gap:8px;align-items:center;padding:5px 6px;font-size:12.5px;cursor:pointer;border-radius:2px}\n.pop label:hover{background:#FAFAF8}.pop input{accent-color:var(--sea)}\n.pbar{background:var(--cadet);color:#fff;border-radius:3px;padding:13px 18px;margin-bottom:18px;\ndisplay:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap}\n.pbar .pl{font-size:10px;letter-spacing:1.6px;text-transform:uppercase;color:var(--tan);font-weight:600;margin-bottom:3px}\n.pbar .pv{font-family:'Sorts Mill Goudy',serif;font-size:19px}\n.pbar .pn{font-size:11.5px;color:#B9BFD6;max-width:520px;line-height:1.55;text-align:right}\n.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(186px,1fr));gap:13px;margin-bottom:20px}\n.kpi{background:var(--card);border:1px solid var(--line);border-radius:3px;padding:18px 17px;position:relative;overflow:hidden}\n.kpi:before{content:'';position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--sea)}\n.kpi.t:before{background:var(--tan)}\n.kpi .l{font-size:9.5px;letter-spacing:1.5px;text-transform:uppercase;color:var(--mut);font-weight:600;margin-bottom:9px}\n.kpi .v{font-family:'Sorts Mill Goudy',serif;font-size:29px;line-height:1;color:var(--cadet)}\n.kpi .v small{font-size:15px;color:var(--mut)}\n.kpi .cmp{font-size:11px;color:var(--mut);margin-top:7px;display:flex;gap:7px;align-items:baseline;flex-wrap:wrap}\n.delta{font-weight:700;font-size:11.5px;display:inline-flex;align-items:center;padding:3px 8px;border-radius:999px;line-height:1.35;white-space:nowrap}.delta-positive{color:#23865F !important;background:#E6F5ED !important;border:1px solid #C8E8D8}.delta-negative{color:#B36B49 !important;background:#FAECE5 !important;border:1px solid #EECFC0}.delta-neutral{color:#6E6B65 !important;background:#F0EFEC !important;border:1px solid #E1DED8}\n.card{background:var(--card);border:1px solid var(--line);border-radius:3px;padding:21px;margin-bottom:17px}\n.card h3{font-family:'Sorts Mill Goudy',serif;font-size:17px;font-weight:400;color:var(--cadet)}\n.card .cs{font-size:11.5px;color:var(--mut);margin-bottom:15px}\n.g2{display:grid;grid-template-columns:1fr 1fr;gap:17px}\n@media(max-width:920px){.g2{grid-template-columns:1fr}}\n.ch{position:relative;height:290px}.ch.sm{height:235px}\ntable{width:100%;border-collapse:collapse;font-size:13px}\nth{text-align:left;padding:9px;font-size:9.5px;letter-spacing:1.1px;text-transform:uppercase;color:var(--mut);\nfont-weight:600;border-bottom:1.5px solid var(--line);cursor:pointer;white-space:nowrap;user-select:none}\nth:hover{color:var(--cadet)}th.num,td.num{text-align:right}\ntd{padding:9px;border-bottom:1px solid #F0EEEA}\ntbody tr:hover{background:#FAFAF8}td.nm{font-weight:500}\ntr.fut td{color:#9B978F;font-style:italic}\nthead tr.grp th{border-bottom:0;padding-bottom:3px;color:var(--cadet);text-align:center;cursor:default}\ntd.sp,th.sp{border-left:1px solid var(--line)}\n.bar{height:4px;background:#EDEBE6;border-radius:2px;overflow:hidden;margin-top:5px;min-width:54px}\n.bar i{display:block;height:100%;background:var(--sea)}\n.pill{display:inline-block;padding:2px 8px;border-radius:2px;font-size:10px;font-weight:600}\n.pill.a{background:#E8F6F7;color:#1F7A80}.pill.b{background:#EDEEF5;color:var(--cadet)}\n.pill.c{background:#FAF3E9;color:#8A6A3C}.pill.g{background:#F0EFEC;color:#7A766F}\nselect{font-family:Montserrat;font-size:13px;padding:9px 12px;border:1px solid var(--line);border-radius:2px;\nbackground:#fff;min-width:250px;cursor:pointer}\n.ctl{display:flex;gap:10px;align-items:center;margin-bottom:17px;flex-wrap:wrap}\n.ctl label{font-size:10px;letter-spacing:1.3px;text-transform:uppercase;color:var(--mut);font-weight:600}\n.note{background:#FAF7F1;border-left:3px solid var(--tan);padding:12px 15px;font-size:12px;color:#5C5852;\nline-height:1.65;margin-bottom:17px;border-radius:0 2px 2px 0}.note b{color:var(--cadet)}\n.note.w{background:var(--dn-soft);border-left-color:var(--dn)}\nfooter{border-top:1px solid var(--line);padding:22px 0;font-size:11px;color:var(--mut)}\n.fl{display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;width:100%}\n.fl .lg{font-family:'Sorts Mill Goudy',serif;font-style:italic;font-size:15px;color:var(--cadet)}\n.chips{display:flex;flex-wrap:wrap;gap:5px;margin-top:4px}\n.chip{background:#F4F3F0;border-radius:2px;padding:3px 8px;font-size:11px;color:var(--mut)}\n.brk{margin-top:32px;padding-top:24px;border-top:2px solid var(--line)}\n.brkh{display:flex;justify-content:space-between;align-items:flex-end;gap:18px;flex-wrap:wrap;margin-bottom:18px}\n.bhead{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:15px}\n.bhead .dot{width:9px;height:9px;border-radius:50%;background:var(--sea);align-self:center}\n.bhead .bn{font-family:'Sorts Mill Goudy',serif;font-size:23px;color:var(--cadet)}\n.bhead .by{font-size:12.5px;color:var(--mut)}\n.bhead .bm{font-size:11.5px;color:var(--mut);margin-left:auto}\n.bk{display:grid;grid-template-columns:repeat(auto-fit,minmax(196px,1fr));gap:14px;margin-bottom:17px}\n.bkc{background:var(--card);border:1px solid var(--line);border-radius:3px;padding:17px 17px 15px}\n.bkc .l{font-size:9.5px;letter-spacing:1.5px;text-transform:uppercase;color:var(--mut);font-weight:600;margin-bottom:13px}\n.bkc .y1,.bkc .y2{display:flex;flex-direction:column;gap:1px;margin-bottom:9px}\n.bkc .y1 span,.bkc .y2 span{font-size:10.5px;color:var(--mut)}\n.bkc .y1 b{font-size:19px;font-weight:400;color:var(--mut);font-family:'Sorts Mill Goudy',serif}\n.bkc .y2 b{font-size:27px;font-weight:400;color:var(--cadet);font-family:'Sorts Mill Goudy',serif;line-height:1.1}\n.bkc .dl{padding-top:11px;border-top:1px solid #F0EEEA;display:flex;flex-direction:column;gap:2px;margin-bottom:11px}\n.bkc .dl .ar{font-size:10px;color:var(--mut)}\n.bkc .mb{display:flex;align-items:center;gap:7px;margin-top:5px}\n.bkc .mb span{font-size:9.5px;color:var(--mut);width:26px;flex-shrink:0}\n.bkc .mb i{display:block;height:4px;border-radius:2px;min-width:2px}\n.empty{background:var(--card);border:1px solid var(--line);border-radius:3px;padding:42px;text-align:center;color:var(--mut);font-size:13px}\n.ins{background:var(--card);border:1px solid var(--line);border-radius:3px;padding:20px 22px;margin-bottom:17px}\n.ins h4{font-family:'Sorts Mill Goudy',serif;font-size:16px;color:var(--cadet);font-weight:400;margin-bottom:11px}\n.ins ul{list-style:none;display:flex;flex-direction:column;gap:9px}\n.ins li{font-size:13px;line-height:1.6;padding-left:15px;position:relative;color:#454239}\n.ins li:before{content:'';position:absolute;left:0;top:7px;width:5px;height:5px;border-radius:50%;background:var(--sea)}\n.ins li.w:before{background:var(--dn)}.ins li.g:before{background:var(--up)}\n.ins li b{color:var(--cadet)}\n.diag h4{display:flex;align-items:center;gap:9px}\n.diag .tag{font-size:9px;letter-spacing:1px;text-transform:uppercase;font-weight:700;padding:2px 7px;border-radius:2px}\n.diag .tag.ok{background:var(--up-soft);color:var(--up)}\n.diag .tag.no{background:var(--dn-soft);color:var(--dn)}\n.wf{display:flex;flex-direction:column;gap:0;margin:14px 0 4px}\n.wf .row{display:grid;grid-template-columns:1fr auto auto;gap:12px;align-items:center;padding:9px 0;border-bottom:1px solid #F0EEEA;font-size:13px}\n.wf .row:last-child{border-bottom:0}\n.wf .row.tot{font-weight:600;color:var(--cadet);border-top:1.5px solid var(--line);border-bottom:0}\n.wf .lbl{color:#454239}.wf .lbl small{display:block;color:var(--mut);font-size:11px;font-weight:400;margin-top:1px}\n.wf .amt{font-family:'Sorts Mill Goudy',serif;font-size:15px;text-align:right;min-width:82px}\n.wf .amt.u{color:var(--up)}.wf .amt.d{color:var(--dn)}\n.wf .pctm{font-size:11px;color:var(--mut);text-align:right;min-width:44px}\n.hyp{margin-top:16px;padding-top:15px;border-top:1px dashed var(--line)}\n.hyp .ht{font-size:10px;letter-spacing:1.5px;text-transform:uppercase;color:var(--mut);font-weight:600;margin-bottom:10px}\n.hyp ul{gap:11px}\n.hyp li{font-size:12.5px;color:#5C5852}\n.hyp li .q{display:block;margin-top:3px;color:var(--cadet);font-style:italic}\n.subT{font-size:10px;letter-spacing:1.5px;text-transform:uppercase;color:var(--mut);font-weight:600;margin:16px 0 8px}\n.canx{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-top:6px}\n@media(max-width:640px){.canx{grid-template-columns:1fr}}\n.cbx{border:1px solid var(--line);border-radius:2px;padding:11px 13px;font-size:12px}\n.cbx .cy{font-size:10px;letter-spacing:1px;text-transform:uppercase;color:var(--mut);font-weight:600;margin-bottom:7px}\n.cbx .cl{display:flex;justify-content:space-between;padding:2px 0;color:#454239}\n.cbx .cl b{color:var(--cadet);font-weight:600}\n@media(max-width:640px){.wrap{padding:0 15px}.brand{font-size:26px}.kpi .v{font-size:25px}\n.per,.pbar .pn{text-align:left}.bhead .bm{margin-left:0;width:100%}}\n<\/style><\/head><body>\n<header><div class=\"wrap\"><div class=\"hd\">\n<div><div class=\"brand\">Villas <span>Rivero<\/span><\/div><div class=\"sub\">Revenue Dashboard<\/div><\/div>\n<div class=\"per\"><b id=\"hPer\"><\/b><br><span id=\"hSel\"><\/span><br>Actualizado <span id=\"hGen\"><\/span><\/div>\n<\/div><\/div><\/header>\n<nav><div class=\"wrap\"><div class=\"nv\">\n<button class=\"on\" data-p=\"resumen\">Resumen<\/button>\n<button data-p=\"periodos\">Comparativa de periodos<\/button>\n<button data-p=\"ranking\">Ranking<\/button>\n<button data-p=\"canales\">Canales<\/button>\n<button data-p=\"inventario\">Inventario<\/button>\n<\/div><\/div><\/nav>\n<main><div class=\"wrap\">\n\n<div class=\"filt\">\n<div class=\"fg\"><span>Periodo<\/span><div class=\"seg\" id=\"segP\">\n<button data-v=\"ytd\" class=\"on\">YTD<\/button><button data-v=\"q1\">Q1<\/button><button data-v=\"q2\">Q2<\/button>\n<button data-v=\"q3\">Q3<\/button><button data-v=\"q4\">Q4<\/button><button data-v=\"s1\">S1<\/button>\n<button data-v=\"s2\">S2<\/button><button data-v=\"anio\">A&ntilde;o completo<\/button>\n<\/div><\/div>\n<div class=\"fg\"><span>Vista<\/span><div class=\"seg\" id=\"segC\">\n<button data-v=\"comp\" class=\"on\">Era del Centeno<\/button><button data-v=\"todos\">\u00danico activo<\/button>\n<button data-v=\"manual\">Manual<\/button><\/div><div class=\"cnt\" id=\"cntC\"><\/div><\/div>\n<div class=\"fg\"><span>Alojamiento<\/span><div class=\"picker\">\n<button id=\"pkBtn\">Seleccionar&hellip;<\/button>\n<div class=\"pop\" id=\"pkPop\"><div class=\"act\">\n<button id=\"pkAll\">Todos<\/button><button id=\"pkNone\">Ninguno<\/button>\n<button id=\"pkComp\">Comparables<\/button><button id=\"pkOasis\">Era del Centeno<\/button>\n<\/div><div id=\"pkList\"><\/div><\/div><\/div><\/div>\n<\/div>\n\n<div class=\"pbar\"><div><div class=\"pl\" id=\"pbL\"><\/div><div class=\"pv\" id=\"pbV\"><\/div><\/div>\n<div class=\"pn\" id=\"pbN\"><\/div><\/div>\n\n<section class=\"pane on\" id=\"resumen\">\n<div class=\"eyebrow\">Comparativa<\/div><h2>Resumen ejecutivo<\/h2>\n<p class=\"lead\">KPIs del periodo seleccionado, comparados entre ambos a&ntilde;os.<\/p>\n<div class=\"kpis\" id=\"kA\"><\/div>\n<div class=\"ins\" id=\"insBox\"><\/div>\n<div class=\"ins\" id=\"diagBox\"><\/div>\n<div class=\"g2\">\n<div class=\"card\"><h3>Revenue mensual<\/h3><div class=\"cs\">Meses del periodo destacados; el resto atenuado<\/div><div class=\"ch\"><canvas id=\"a1\"><\/canvas><\/div><\/div>\n<div class=\"card\"><h3>Ocupaci&oacute;n mensual<\/h3><div class=\"cs\">Porcentaje sobre disponibilidad real<\/div><div class=\"ch\"><canvas id=\"a2\"><\/canvas><\/div><\/div>\n<\/div>\n<div class=\"card\"><h3>Tabla mensual<\/h3><div class=\"cs\">Detalle mes a mes con total del periodo<\/div>\n<div style=\"overflow-x:auto\"><table id=\"tA\"><\/table><\/div><\/div>\n<div class=\"brk\"><div class=\"brkh\">\n<div><div class=\"eyebrow\">Desglose<\/div><h2>Detalle por alojamiento<\/h2>\n<p class=\"lead\" id=\"brkS\" style=\"margin-bottom:0\"><\/p><\/div>\n<select id=\"selB\"><\/select><\/div>\n<div id=\"brkBox\"><\/div><\/div>\n<\/section>\n\n<section class=\"pane\" id=\"periodos\">\n<div class=\"eyebrow\">Claridad<\/div><h2>Comparativa de periodos<\/h2>\n<p class=\"lead\">Todos los periodos en una sola tabla, indicando cu&aacute;les son comparables y cu&aacute;les no.<\/p>\n<div class=\"note\" id=\"cmpNota\"><\/div>\n<div class=\"card\"><h3>Todos los periodos<\/h3><div class=\"cs\">El periodo activo aparece resaltado<\/div>\n<div style=\"overflow-x:auto\"><table id=\"tCmp\"><\/table><\/div><\/div>\n<div class=\"card\"><h3>Revenue por periodo<\/h3><div class=\"cs\">Las barras claras corresponden a periodos a&uacute;n en curso<\/div><div class=\"ch\"><canvas id=\"cmp1\"><\/canvas><\/div><\/div>\n<\/section>\n\n<section class=\"pane\" id=\"ranking\">\n<div class=\"eyebrow\">Comparativa<\/div><h2>Ranking de alojamientos<\/h2>\n<p class=\"lead\">Rendimiento individual en el periodo seleccionado. Ordena por cualquier columna.<\/p>\n<div class=\"card\"><div style=\"overflow-x:auto\"><table id=\"tR\"><\/table><\/div><\/div>\n<\/section>\n\n<section class=\"pane\" id=\"canales\">\n<div class=\"eyebrow\">Distribuci&oacute;n<\/div><h2>An&aacute;lisis por canal<\/h2>\n<p class=\"lead\">Origen de los ingresos en el periodo seleccionado.<\/p>\n<div class=\"note\" id=\"canNota\"><\/div>\n<div class=\"g2\">\n<div class=\"card\"><h3>Revenue por canal<\/h3><div class=\"cs\">Comparativa entre a&ntilde;os<\/div><div class=\"ch\"><canvas id=\"n1\"><\/canvas><\/div><\/div>\n<div class=\"card\"><h3>Reparto<\/h3><div class=\"cs\">Peso de cada canal<\/div><div class=\"ch\"><canvas id=\"n2\"><\/canvas><\/div><\/div>\n<\/div>\n<div class=\"card\"><h3>Detalle por canal<\/h3><div class=\"cs\">Revenue, peso y precio medio por noche<\/div>\n<div style=\"overflow-x:auto\"><table id=\"tN\"><\/table><\/div><\/div>\n<\/section>\n\n<section class=\"pane\" id=\"inventario\">\n<div class=\"eyebrow\">Cartera<\/div><h2>Inventario operativo<\/h2>\n<p class=\"lead\">Alojamientos con actividad comercial mes a mes.<\/p>\n<div class=\"card\"><h3>Alojamientos activos<\/h3><div class=\"cs\">Unidades con reservas cada mes<\/div><div class=\"ch\"><canvas id=\"i1\"><\/canvas><\/div><\/div>\n<div class=\"card\"><h3>Disponibilidad por mes<\/h3><div class=\"cs\">Selecciona a&ntilde;o y mes<\/div>\n<div class=\"ctl\"><select id=\"selIY\"><\/select><select id=\"selIM\"><\/select><\/div>\n<div id=\"boxI\"><\/div><\/div>\n<\/section>\n\n<\/div><\/main>\n<footer><div class=\"wrap\"><div class=\"fl\"><div class=\"lg\">Villas Rivero<\/div>\n<div>Era del Centeno &middot; Datos de gesti&oacute;n propia &middot; <span id=\"fGen\"><\/span><\/div><\/div><\/div><\/footer>\n<script>\nconst D={\"detalle\": [{\"nombre\": \"Era del Centeno\", \"grupo_to\": false, \"2025\": {\"mensual\": {\"1\": {\"rev\": 251, \"noches\": 1, \"res\": 1, \"dias\": 31.0}, \"2\": {\"rev\": 2101, \"noches\": 14, \"res\": 2, \"dias\": 28.0}, \"3\": {\"rev\": 0, \"noches\": 0, \"res\": 0, \"dias\": null}, \"4\": {\"rev\": 787, \"noches\": 4, \"res\": 1, \"dias\": 30.0}, \"5\": {\"rev\": 851, \"noches\": 6, \"res\": 2, \"dias\": 31.0}, \"6\": {\"rev\": 241, \"noches\": 3, \"res\": 1, \"dias\": 30.0}, \"7\": {\"rev\": 1067, \"noches\": 6, \"res\": 2, \"dias\": 31.0}, \"8\": {\"rev\": 1852, \"noches\": 17, \"res\": 4, \"dias\": 31.0}, \"9\": {\"rev\": 289, \"noches\": 3, \"res\": 1, \"dias\": 30.0}, \"10\": {\"rev\": 0, \"noches\": 0, \"res\": 0, \"dias\": null}, \"11\": {\"rev\": 1411, \"noches\": 15, \"res\": 2, \"dias\": 30.0}, \"12\": {\"rev\": 1625, \"noches\": 9, \"res\": 2, \"dias\": 31.0}}, \"canal_mes\": {\"1\": {\"Sin canal informado\": 251}, \"2\": {\"Sin canal informado\": 2101}, \"4\": {\"Sin canal informado\": 787}, \"5\": {\"Booking\": 306, \"Sin canal informado\": 545}, \"6\": {\"Booking\": 241}, \"7\": {\"Booking\": 1067}, \"8\": {\"Booking\": 1559, \"Sin canal informado\": 293}, \"9\": {\"Sin canal informado\": 289}, \"11\": {\"Airbnb\": 569, \"Booking\": 843}, \"12\": {\"Airbnb\": 327, \"Booking\": 1298}}, \"canal_noches\": {\"1\": {\"Sin canal informado\": 1}, \"2\": {\"Sin canal informado\": 14}, \"4\": {\"Sin canal informado\": 4}, \"5\": {\"Booking\": 3, \"Sin canal informado\": 3}, \"6\": {\"Booking\": 3}, \"7\": {\"Booking\": 6}, \"8\": {\"Booking\": 14, \"Sin canal informado\": 3}, \"9\": {\"Sin canal informado\": 3}, \"11\": {\"Airbnb\": 7, \"Booking\": 8}, \"12\": {\"Airbnb\": 2, \"Booking\": 7}}}, \"2026\": {\"mensual\": {\"1\": {\"rev\": 655, \"noches\": 4, \"res\": 1, \"dias\": 31.0}, \"2\": {\"rev\": 1176, \"noches\": 14, \"res\": 3, \"dias\": 28.0}, \"3\": {\"rev\": 790, \"noches\": 10, \"res\": 3, \"dias\": 31.0}, \"4\": {\"rev\": 1304, \"noches\": 10, \"res\": 3, \"dias\": 30.0}, \"5\": {\"rev\": 827, \"noches\": 15, \"res\": 4, \"dias\": 31.0}, \"6\": {\"rev\": 917, \"noches\": 12, \"res\": 3, \"dias\": 30.0}, \"7\": {\"rev\": 1538, \"noches\": 20, \"res\": 3, \"dias\": 31.0}, \"8\": {\"rev\": 1403, \"noches\": 10, \"res\": 2, \"dias\": 31.0}, \"9\": {\"rev\": 467, \"noches\": 6, \"res\": 1, \"dias\": 30.0}, \"10\": {\"rev\": 0, \"noches\": 0, \"res\": 0, \"dias\": null}, \"11\": {\"rev\": 0, \"noches\": 0, \"res\": 0, \"dias\": null}, \"12\": {\"rev\": 0, \"noches\": 0, \"res\": 0, \"dias\": null}}, \"canal_mes\": {\"1\": {\"Airbnb\": 655}, \"2\": {\"Airbnb\": 569, \"Booking\": 607}, \"3\": {\"Airbnb\": 790}, \"4\": {\"Booking\": 1304}, \"5\": {\"Airbnb\": 312, \"Booking\": 343, \"Directo\": 172}, \"6\": {\"Booking\": 917}, \"7\": {\"Booking\": 1538}, \"8\": {\"Booking\": 895, \"Motor propio\": 508}, \"9\": {\"Airbnb\": 467}}, \"canal_noches\": {\"1\": {\"Airbnb\": 4}, \"2\": {\"Airbnb\": 7, \"Booking\": 7}, \"3\": {\"Airbnb\": 10}, \"4\": {\"Booking\": 10}, \"5\": {\"Airbnb\": 7, \"Booking\": 5, \"Directo\": 3}, \"6\": {\"Booking\": 12}, \"7\": {\"Booking\": 20}, \"8\": {\"Booking\": 6, \"Motor propio\": 4}, \"9\": {\"Airbnb\": 6}}}}], \"comparables\": [\"Era del Centeno\"], \"todos\": [\"Era del Centeno\"], \"meta\": {\"y1\": 2025, \"y2\": 2026, \"mes_cerrado\": 6, \"generado\": \"24\/07\/2026\", \"hoy\": \"2026-07-24\"}};\nconst Y1=D.meta.y1,Y2=D.meta.y2,S1=String(Y1),S2=String(Y2),MC=D.meta.mes_cerrado;\nconst C={sea:'#4ABCC5',cadet:'#242B59',tan:'#D1B38C',mut:'#6E6B65',up:'#23865F',dn:'#B36B49'};\nconst PAL=[C.sea,C.cadet,C.tan,'#7A9AA8','#A8B4C8','#C9BBA4'];\nconst nf=n=>new Intl.NumberFormat('es-ES',{maximumFractionDigits:0}).format(n);\nconst eu=n=>nf(n)+' \\u20AC';\nconst MN=['ene','feb','mar','abr','may','jun','jul','ago','sep','oct','nov','dic'];\nconst MF=['Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'];\nChart.defaults.font.family='Montserrat';Chart.defaults.font.size=11;Chart.defaults.color=C.mut;\nconst GRID={grid:{color:'#F0EEEA'},border:{display:false}},NOGRID={grid:{display:false},border:{display:false}};\nconst CH={};\nfunction mk(id,cfg){if(CH[id])CH[id].destroy();const e=document.getElementById(id);if(e)CH[id]=new Chart(e,cfg);}\nfunction dh(a,b,inv){\n if(!b)return'';\n const p=(a\/b-1)*100;\n let cls='delta-neutral';\n if(Math.abs(p)>=0.05){\n   const positive=inv?p<0:p>0;\n   cls=positive?'delta-positive':'delta-negative';\n }\n const sign=p>0?'+':'';\n return `<span class=\"delta ${cls}\">${sign}${p.toFixed(1)}%<\/span>`;\n}\n\n\/\/ \u2500\u2500 periodos \u2500\u2500\nconst PER={\n ytd:{lab:'YTD comparable',ms:Array.from({length:MC},(_,i)=>i+1),\n      nota:`Compara enero\\u2013${MF[MC-1].toLowerCase()} de ambos a\\u00F1os. Es la \\u00FAnica comparativa en igualdad de condiciones: mismo n\\u00FAmero de meses, ambos cerrados.`},\n q1:{lab:'Q1',ms:[1,2,3],nota:'Primer trimestre: enero, febrero y marzo. Ambos a\\u00F1os cerrados.'},\n q2:{lab:'Q2',ms:[4,5,6],nota:'Segundo trimestre: abril, mayo y junio. Ambos a\\u00F1os cerrados.'},\n q3:{lab:'Q3',ms:[7,8,9],nota:'Tercer trimestre. En '+Y2+' a\\u00FAn en curso: incluye reservas confirmadas pendientes de estancia.'},\n q4:{lab:'Q4',ms:[10,11,12],nota:'Cuarto trimestre. En '+Y2+' es previsi\\u00F3n basada en reservas ya confirmadas.'},\n s1:{lab:'S1',ms:[1,2,3,4,5,6],nota:'Primer semestre completo. Ambos a\\u00F1os cerrados: comparativa fiable.'},\n s2:{lab:'S2',ms:[7,8,9,10,11,12],nota:'Segundo semestre. En '+Y2+' a\\u00FAn por completar: las cifras seguir\\u00E1n subiendo.'},\n anio:{lab:'A\\u00F1o completo',ms:Array.from({length:12},(_,i)=>i+1),\n      nota:`Cuidado al leer: ${Y1} est\\u00E1 cerrado y de ${Y2} solo han transcurrido ${MC} meses. La ca\\u00EDda aparente no es real, es un a\\u00F1o incompleto frente a uno completo.`}\n};\nlet P='ytd', SEL=new Set(D.comparables), MODE='comp';\nconst LIST=D.detalle.map(d=>d.nombre);\nconst ms=()=>PER[P].ms;\nconst cerrado=()=>ms().every(m=>m<=MC);\n\n\/\/ \u2500\u2500 agregacion \u2500\u2500\nfunction sel(){return D.detalle.filter(d=>SEL.has(d.nombre));}\nfunction agg(y,mm,list){\n let rev=0,noc=0,res=0,dias=0,act=new Set();\n (list||sel()).forEach(d=>{mm.forEach(m=>{const v=d[String(y)].mensual[m];if(!v)return;\n  rev+=v.rev;noc+=v.noches;res+=v.res;if(v.dias)dias+=v.dias;if(v.noches>0)act.add(d.nombre);});});\n return{rev,noc,res,dias,adr:noc?rev\/noc:0,occ:dias?noc\/dias*100:0,revpar:dias?rev\/dias:0,\n  aloj:act.size,est:res?noc\/res:0};}\nfunction aggAloj(d,y,mm){\n let rev=0,noc=0,res=0,dias=0,mes=0;\n mm.forEach(m=>{const v=d[String(y)].mensual[m];if(!v)return;\n  rev+=v.rev;noc+=v.noches;res+=v.res;if(v.dias)dias+=v.dias;if(v.noches>0)mes++;});\n return{rev,noc,res,dias,mes,adr:noc?rev\/noc:0,occ:dias?noc\/dias*100:0,revpar:dias?rev\/dias:0};}\nfunction canales(y,mm,list){\n const o={};(list||sel()).forEach(d=>{mm.forEach(m=>{\n  const cm=d[String(y)].canal_mes[m];if(!cm)return;\n  Object.entries(cm).forEach(([k,v])=>o[k]=(o[k]||0)+v);});});\n return o;}\nfunction canalNoches(y,mm,list){\n const o={};(list||sel()).forEach(d=>{mm.forEach(m=>{\n  const cm=d[String(y)].canal_noches[m];if(!cm)return;\n  Object.entries(cm).forEach(([k,v])=>o[k]=(o[k]||0)+v);});});\n return o;}\n\n\/\/ \u2500\u2500 UI selecci\u00f3n \u2500\u2500\ndocument.getElementById('pkList').innerHTML=LIST.map(n=>\n`<label><input type=\"checkbox\" value=\"${n}\"><span>${n}<\/span><\/label>`).join('');\nfunction syncPick(){document.querySelectorAll('#pkList input').forEach(i=>i.checked=SEL.has(i.value));\ndocument.getElementById('pkBtn').textContent=SEL.size+' de '+LIST.length+' seleccionados';}\ndocument.getElementById('pkBtn').onclick=e=>{e.stopPropagation();document.getElementById('pkPop').classList.toggle('on')};\ndocument.addEventListener('click',e=>{if(!e.target.closest('.picker'))document.getElementById('pkPop').classList.remove('on')});\ndocument.getElementById('pkList').onchange=e=>{\n e.target.checked?SEL.add(e.target.value):SEL.delete(e.target.value);setMode('manual');render();};\ndocument.getElementById('pkAll').onclick=()=>{SEL=new Set(LIST);setMode('todos');syncPick();render();};\ndocument.getElementById('pkNone').onclick=()=>{SEL=new Set();setMode('manual');syncPick();render();};\ndocument.getElementById('pkComp').onclick=()=>{SEL=new Set(D.comparables);setMode('comp');syncPick();render();};\ndocument.getElementById('pkOasis').onclick=()=>{SEL=new Set(['Era del Centeno']);setMode('manual');syncPick();render();};\nfunction setMode(m){MODE=m;document.querySelectorAll('#segC button').forEach(b=>b.classList.toggle('on',b.dataset.v===m));}\ndocument.querySelectorAll('#segC button').forEach(b=>b.onclick=()=>{\n const v=b.dataset.v;if(v==='comp')SEL=new Set(D.comparables);else if(v==='todos')SEL=new Set(LIST);\n setMode(v);syncPick();render();});\ndocument.querySelectorAll('#segP button').forEach(b=>b.onclick=()=>{\n P=b.dataset.v;document.querySelectorAll('#segP button').forEach(x=>x.classList.toggle('on',x===b));render();});\n\ndocument.getElementById('hGen').textContent=D.meta.generado;\ndocument.getElementById('fGen').textContent=D.meta.generado;\n\n\/\/ \u2500\u2500 render principal \u2500\u2500\nfunction render(){\nconst mm=ms(),A=agg(Y1,mm),B=agg(Y2,mm);\ndocument.getElementById('cntC').textContent='Era del Centeno';\ndocument.getElementById('hPer').textContent=PER[P].lab+' \\u00B7 '+Y1+' vs '+Y2;\ndocument.getElementById('hSel').textContent=SEL.size+' alojamientos \\u00B7 '+\n (MODE==='comp'?'solo comparables':MODE==='todos'?'cartera completa':'selecci\\u00F3n manual');\nconst rango=mm.length===12?'enero \\u2013 diciembre':MF[mm[0]-1]+' \\u2013 '+MF[mm[mm.length-1]-1];\ndocument.getElementById('pbV').textContent=PER[P].lab+'  \\u00B7  '+rango;\ndocument.getElementById('pbN').textContent=PER[P].nota;\ndocument.getElementById('pbL').textContent=cerrado()?'Periodo cerrado \\u00B7 comparativa fiable':'Periodo en curso \\u00B7 leer con cautela';\n\nconst K=[['Revenue',eu(B.rev),eu(A.rev),B.rev,A.rev,0],\n['Ocupaci\\u00F3n',B.occ.toFixed(1)+'<small>%<\/small>',A.occ.toFixed(1)+'%',B.occ,A.occ,1],\n['ADR',eu(B.adr),eu(A.adr),B.adr,A.adr,0],\n['RevPAR',eu(B.revpar),eu(A.revpar),B.revpar,A.revpar,1],\n['Noches',nf(B.noc),nf(A.noc),B.noc,A.noc,0],\n['Reservas',nf(B.res),nf(A.res),B.res,A.res,1],\n['Alojamientos',B.aloj,A.aloj,B.aloj,A.aloj,0],\n['Estancia media',B.est.toFixed(1)+'<small> n<\/small>',A.est.toFixed(1)+' n',B.est,A.est,1]];\ndocument.getElementById('kA').innerHTML=K.map(k=>\n`<div class=\"kpi ${k[5]?'t':''}\"><div class=\"l\">${k[0]}<\/div><div class=\"v\">${k[1]}<\/div>\n<div class=\"cmp\"><span>${Y1}: ${k[2]}<\/span>${dh(k[3],k[4])}<\/div><\/div>`).join('');\n\n\/\/ serie mensual (siempre 12 meses, resalta periodo)\nconst s1=MN.map((_,i)=>{let r=0;sel().forEach(d=>{const v=d[S1].mensual[i+1];if(v)r+=v.rev});return r;});\nconst s2=MN.map((_,i)=>{let r=0;sel().forEach(d=>{const v=d[S2].mensual[i+1];if(v)r+=v.rev});return r;});\nconst inP=MN.map((_,i)=>mm.includes(i+1));\nmk('a1',{type:'bar',data:{labels:MN,datasets:[\n{label:S1,data:s1,backgroundColor:inP.map(x=>x?C.tan:'#EDE7DC'),borderRadius:2},\n{label:S2,data:s2,backgroundColor:inP.map(x=>x?C.sea:'#DCEEF0'),borderRadius:2}]},\noptions:{maintainAspectRatio:false,plugins:{legend:{position:'bottom',labels:{boxWidth:10,padding:13}},\ntooltip:{callbacks:{label:x=>x.dataset.label+' '+eu(x.raw),\nafterLabel:x=>inP[x.dataIndex]?'':'fuera del periodo'}}},\nscales:{y:{...GRID,ticks:{callback:v=>nf(v\/1000)+'k'}},x:NOGRID}}});\n\nconst o1=MN.map((_,i)=>{const a=agg(Y1,[i+1]);return +a.occ.toFixed(1)});\nconst o2=MN.map((_,i)=>{const a=agg(Y2,[i+1]);return +a.occ.toFixed(1)});\nmk('a2',{type:'line',data:{labels:MN,datasets:[\n{label:S1,data:o1,borderColor:C.tan,tension:.35,pointRadius:3,borderWidth:2},\n{label:S2,data:o2,borderColor:C.cadet,tension:.35,pointRadius:3,borderWidth:2}]},\noptions:{maintainAspectRatio:false,plugins:{legend:{position:'bottom',labels:{boxWidth:10,padding:13}},\ntooltip:{callbacks:{label:x=>x.dataset.label+' '+x.raw+'%'}}},\nscales:{y:{...GRID,ticks:{callback:v=>v+'%'}},x:NOGRID}}});\n\n\/\/ tabla mensual\ndocument.getElementById('tA').innerHTML=\n`<thead><tr class=\"grp\"><th><\/th><th colspan=\"2\">${Y1}<\/th><th colspan=\"2\" class=\"sp\">${Y2}<\/th><th colspan=\"2\" class=\"sp\">Variaci\\u00F3n<\/th><\/tr>\n<tr><th>Mes<\/th><th class=\"num\">Revenue<\/th><th class=\"num\">Ocup.<\/th><th class=\"num sp\">Revenue<\/th>\n<th class=\"num\">Ocup.<\/th><th class=\"num sp\">Revenue<\/th><th class=\"num\">Ocup.<\/th><\/tr><\/thead><tbody>`+\nMN.map((_,i)=>{const fu=i+1>MC;\nreturn `<tr class=\"${inP[i]?'':'fut'}\"><td class=\"nm\">${MF[i]}${fu?' <span class=\"pill c\">en curso<\/span>':''}${inP[i]?'':' <span class=\"pill g\">fuera<\/span>'}<\/td>\n<td class=\"num\">${eu(s1[i])}<\/td><td class=\"num\">${o1[i]}%<\/td>\n<td class=\"num sp\">${eu(s2[i])}<\/td><td class=\"num\">${o2[i]}%<\/td>\n<td class=\"num sp\">${dh(s2[i],s1[i])}<\/td><td class=\"num\">${dh(o2[i],o1[i])}<\/td><\/tr>`}).join('')+\n`<tr style=\"background:#F4F3F0;font-weight:600\"><td class=\"nm\">${PER[P].lab}<\/td>\n<td class=\"num\">${eu(A.rev)}<\/td><td class=\"num\">${A.occ.toFixed(1)}%<\/td>\n<td class=\"num sp\">${eu(B.rev)}<\/td><td class=\"num\">${B.occ.toFixed(1)}%<\/td>\n<td class=\"num sp\">${dh(B.rev,A.rev)}<\/td><td class=\"num\">${dh(B.occ,A.occ)}<\/td><\/tr><\/tbody>`;\n\nrenderIns(A,B,mm);renderDiag(A,B,mm);renderBrk();renderCmp();renderRank();renderCan();renderInv();}\n\n\/\/ \u2500\u2500 lectura automatica (solo desde datos propios) \u2500\u2500\nfunction renderIns(A,B,mm){\nconst L=[];\nconst dRev=A.rev?(B.rev\/A.rev-1)*100:0, dNoc=A.noc?(B.noc\/A.noc-1)*100:0, dAdr=A.adr?(B.adr\/A.adr-1)*100:0;\nif(A.rev){\n const efV=A.adr*(B.noc-A.noc), efP=(B.adr-A.adr)*B.noc;\n L.push([`El revenue del periodo pasa de <b>${eu(A.rev)}<\/b> a <b>${eu(B.rev)}<\/b> (${dRev>0?'+':''}${dRev.toFixed(1)}%). Descompuesto: <b>${eu(efV)}<\/b> vienen de la variaci\\u00F3n de volumen y <b>${eu(efP)}<\/b> de la variaci\\u00F3n de precio medio.`,\n   dRev>0?'g':'w']);\n}\nif(Math.abs(dAdr)>3&&Math.abs(dNoc)>3&&Math.sign(dAdr)!==Math.sign(dNoc)){\n L.push([dNoc>0\n  ?`Se vende <b>m\\u00E1s volumen a menor precio<\/b>: noches ${dNoc>0?'+':''}${dNoc.toFixed(1)}% con ADR ${dAdr.toFixed(1)}%. Es la se\\u00F1al t\\u00EDpica de una estrategia orientada a ocupaci\\u00F3n.`\n  :`Se vende <b>menos volumen a mayor precio<\/b>: noches ${dNoc.toFixed(1)}% con ADR +${dAdr.toFixed(1)}%. Se ha sostenido tarifa a costa de ocupaci\\u00F3n.`,'']);\n}\n\/\/ mejor y peor mes del periodo\nconst per=mm.map(m=>({m,a:agg(Y1,[m]).rev,b:agg(Y2,[m]).rev})).filter(x=>x.a||x.b);\nif(per.length>1){\n const conD=per.filter(x=>x.a>0).map(x=>({...x,d:(x.b\/x.a-1)*100}));\n if(conD.length){\n  const mx=conD.reduce((p,c)=>c.d>p.d?c:p), mn=conD.reduce((p,c)=>c.d<p.d?c:p);\n  L.push([`Mejor evoluci\\u00F3n en <b>${MF[mx.m-1]}<\/b> (${mx.d>0?'+':''}${mx.d.toFixed(1)}%, ${eu(mx.a)} \\u2192 ${eu(mx.b)}). La m\\u00E1s d\\u00E9bil en <b>${MF[mn.m-1]}<\/b> (${mn.d>0?'+':''}${mn.d.toFixed(1)}%, ${eu(mn.a)} \\u2192 ${eu(mn.b)}).`,'']);\n }\n}\n\/\/ estacionalidad: meses bajo la media\nconst occM=mm.map(m=>({m,o:agg(Y2,[m]).occ})).filter(x=>x.o>0);\nif(occM.length>2){\n const med=occM.reduce((s,x)=>s+x.o,0)\/occM.length;\n const bajos=occM.filter(x=>x.o<med*0.8).map(x=>MF[x.m-1]);\n if(bajos.length)L.push([`Meses por debajo del 80% de la ocupaci\\u00F3n media del periodo (${med.toFixed(1)}%): <b>${bajos.join(', ')}<\/b>. Es donde queda margen de mejora.`,'w']);\n}\n\/\/ canales\nconst c1=canales(Y1,mm),c2=canales(Y2,mm);\nconst t1=Object.values(c1).reduce((a,b)=>a+b,0),t2=Object.values(c2).reduce((a,b)=>a+b,0);\nif(c1['Turoperador']||c2['Turoperador']){\n const a=c1['Turoperador']||0,b=c2['Turoperador']||0;\n L.push([`El turoperador aporta <b>${eu(b)}<\/b> (${t2?(b\/t2*100).toFixed(1):0}% del total) frente a <b>${eu(a)}<\/b> (${t1?(a\/t1*100).toFixed(1):0}%) el a\\u00F1o anterior. ${b>=a*0.9&&t2>t1?'Se mantiene en euros: su menor peso relativo se debe a que el resto de canales ha crecido, no a que haya bajado.':''}`,'']);\n const nA=canalNoches(Y1,mm),nB=canalNoches(Y2,mm);\n const adrTO=nB['Turoperador']?b\/nB['Turoperador']:0;\n const otras=['Airbnb','Booking'].reduce((s,k)=>s+(c2[k]||0),0);\n const nOtras=['Airbnb','Booking'].reduce((s,k)=>s+(nB[k]||0),0);\n if(adrTO&&nOtras){const adrO=otras\/nOtras;\n  L.push([`ADR por canal en ${Y2}: turoperador <b>${eu(adrTO)}<\/b> por noche frente a <b>${eu(adrO)}<\/b> en canal abierto. Diferencia de <b>${eu(adrO-adrTO)}<\/b> por noche vendida.`,adrO>adrTO?'g':'w']);}\n}\nif(c1['Sin canal informado']){\n L.push([`En ${Y1} hay <b>${eu(c1['Sin canal informado'])}<\/b> sin canal de origen registrado (reservas migradas al PMS). No se comparan con ${Y2} por no ser un canal real.`,'']);\n}\ndocument.getElementById('insBox').innerHTML=\n`<h4>Lectura del periodo<\/h4><ul>`+L.map(x=>`<li class=\"${x[1]}\">${x[0]}<\/li>`).join('')+`<\/ul>`;}\n\n\n\/\/ \u2500\u2500 diagn\u00f3stico de ADR: por qu\u00e9 se mueve el precio medio \u2500\u2500\nfunction canalRN(y,mm,list){ \/\/ {canal:{rev,noc}}\n const o={};(list||sel()).forEach(d=>{mm.forEach(m=>{\n  const cm=d[String(y)].canal_mes[m]||{}, cn=d[String(y)].canal_noches[m]||{};\n  Object.keys({...cm,...cn}).forEach(k=>{o[k]=o[k]||{rev:0,noc:0};\n   o[k].rev+=cm[k]||0;o[k].noc+=cn[k]||0;});});});\n return o;}\n\nfunction renderDiag(A,B,mm){\nconst box=document.getElementById('diagBox');\nconst dAdr=A.adr?(B.adr-A.adr):0;\n\/\/ fiabilidad: peso de \"Sin canal informado\" en a\u00f1o base\nconst rn1=canalRN(Y1,mm), rn2=canalRN(Y2,mm);\nconst noc1=Object.values(rn1).reduce((s,x)=>s+x.noc,0);\nconst sc=(rn1['Sin canal informado']||{noc:0}).noc;\nconst contam=noc1?sc\/noc1*100:0;\nconst fiable=contam<15 && A.adr>0 && B.adr>0;\n\nlet html=`<div class=\"diag\"><h4>Diagn\u00f3stico del precio medio (ADR)\n<span class=\"tag ${fiable?'ok':'no'}\">${fiable?'an\u00e1lisis fiable':'no fiable en esta selecci\u00f3n'}<\/span><\/h4>`;\n\nif(!fiable){\n html+=`<p style=\"font-size:13px;line-height:1.6;color:#5C5852\">\n No se puede descomponer la variaci\u00f3n de ADR con garant\u00edas en esta selecci\u00f3n: el\n <b>${contam.toFixed(0)}%<\/b> de las noches de ${Y1} son reservas migradas al PMS sin canal de\n origen (\"Sin canal informado\"). El a\u00f1o base est\u00e1 contaminado y cualquier reparto por canal\n ser\u00eda falso. <b>El diagn\u00f3stico de ADR solo es fiable en Oasis y Jardines del Cura<\/b>, que\n tienen canal real en ambos a\u00f1os. Usa el bot\u00f3n <b>Oasis+JdC<\/b> del selector para activarlo.<\/p><\/div>`;\n box.innerHTML=html;return;\n}\n\n\/\/ ADR por canal cada a\u00f1o\nconst adrc=(rn)=>Object.fromEntries(Object.entries(rn).map(([k,v])=>[k,v.noc?v.rev\/v.noc:0]));\nconst ac1=adrc(rn1), ac2=adrc(rn2);\nconst tn1=noc1, tn2=Object.values(rn2).reduce((s,x)=>s+x.noc,0);\nconst w1=Object.fromEntries(Object.entries(rn1).map(([k,v])=>[k,tn1?v.noc\/tn1:0]));\nconst w2=Object.fromEntries(Object.entries(rn2).map(([k,v])=>[k,tn2?v.noc\/tn2:0]));\nconst cs=[...new Set([...Object.keys(rn1),...Object.keys(rn2)])];\n\n\/\/ Descomposici\u00f3n shift-share:\n\/\/ efecto MIX  = \u03a3 (w2-w1) * adr1_canal   (cambi\u00f3 la composici\u00f3n, a precios viejos)\n\/\/ efecto TARIFA = \u03a3 w2 * (adr2-adr1)_canal (mismo canal, cambi\u00f3 su precio)\nlet efMix=0, efTar=0;\ncs.forEach(k=>{const a1=ac1[k]||0,a2=ac2[k]||0;\n efMix += ((w2[k]||0)-(w1[k]||0))*a1;\n efTar += (w2[k]||0)*(a2-a1);});\n\/\/ estancia\nconst est1=A.est,est2=B.est;\n\nconst f=v=>`${v>=0?'+':''}${nf(Math.round(v))} \\u20AC`;\nconst cls=v=>v>=0?'u':'d';\nhtml+=`<p style=\"font-size:13px;color:#5C5852;line-height:1.55\">\nEl ADR pasa de <b>${eu(A.adr)}<\/b> a <b>${eu(B.adr)}<\/b> (${f(dAdr)} por noche).\nEsta es la parte que s\u00ed se puede explicar con los datos:<\/p>\n<div class=\"wf\">\n<div class=\"row\"><div class=\"lbl\">Cambio de <b>mix de canal<\/b><small>Vender por canales m\u00e1s caros o m\u00e1s baratos, sin tocar tarifas<\/small><\/div>\n<div class=\"amt ${cls(efMix)}\">${f(efMix)}<\/div><div class=\"pctm\">${dAdr?Math.round(efMix\/dAdr*100):0}%<\/div><\/div>\n<div class=\"row\"><div class=\"lbl\">Cambio de <b>tarifa dentro de cada canal<\/b><small>Mismo canal cobrando distinto: aqu\u00ed s\u00ed hay decisi\u00f3n de precio<\/small><\/div>\n<div class=\"amt ${cls(efTar)}\">${f(efTar)}<\/div><div class=\"pctm\">${dAdr?Math.round(efTar\/dAdr*100):0}%<\/div><\/div>\n<div class=\"row tot\"><div class=\"lbl\">Variaci\u00f3n total de ADR<\/div>\n<div class=\"amt ${cls(dAdr)}\">${f(dAdr)}<\/div><div class=\"pctm\">100%<\/div><\/div>\n<\/div>`;\n\n\/\/ tabla de canales\nhtml+=`<div class=\"subT\">ADR y peso por canal<\/div><div class=\"canx\">`;\n[[Y1,ac1,w1,rn1],[Y2,ac2,w2,rn2]].forEach(([y,ac,w,rn])=>{\n const ord=cs.filter(k=>rn[k]&&rn[k].noc).sort((a,b)=>(w[b]||0)-(w[a]||0));\n html+=`<div class=\"cbx\"><div class=\"cy\">${y}<\/div>`+\n  ord.map(k=>`<div class=\"cl\"><span>${k}<\/span><b>${eu(ac[k])} \\u00B7 ${Math.round((w[k]||0)*100)}%<\/b><\/div>`).join('')+`<\/div>`;});\nhtml+=`<\/div>`;\n\n\/\/ \u2500\u2500 hip\u00f3tesis inferidas \u2500\u2500\nconst H=[];\nif(Math.abs(efMix)>Math.abs(efTar)*1.3){\n H.push([`El movimiento del ADR es <b>principalmente de mix de canal<\/b> (${Math.round(efMix\/dAdr*100)}% del total), no de tarifa. `,\n  dAdr>0?`Has sustituido canal barato por canal m\u00e1s caro: es efecto comercial, no de pricing.`\n        :`Ha ganado peso un canal m\u00e1s barato. Pregunta a revenue si es deliberado o hay que reequilibrar.`,\n  `\u00bfEl cambio de mix de canal fue una decisi\u00f3n o consecuencia de d\u00f3nde entr\u00f3 la demanda?`]);\n}\nif(Math.abs(efTar)>Math.abs(dAdr)*0.35){\n const to=cs.filter(k=>ac2[k]&&ac1[k]).map(k=>({k,dif:ac2[k]-ac1[k],w:w2[k]||0}))\n   .filter(x=>x.w>0.05).sort((a,b)=>Math.abs(b.dif*b.w)-Math.abs(a.dif*a.w));\n const peor=to[0];\n H.push([`Hay <b>efecto tarifa real<\/b> (${Math.round(efTar\/dAdr*100)}% del cambio): dentro del mismo canal se cobra distinto. `,\n  peor?`El canal que m\u00e1s pesa en esto es <b>${peor.k}<\/b> (${peor.dif>0?'+':''}${Math.round(peor.dif)} \\u20AC por noche).`:'',\n  `\u00bfPor qu\u00e9 cambi\u00f3 la tarifa en ${peor?peor.k:'los canales principales'}? \u00bfDecisi\u00f3n de pricing, presi\u00f3n de mercado o ajuste de \u00faltima hora?`]);\n}\nif(est2>est1*1.08){\n H.push([`La <b>estancia media sube<\/b> de ${est1.toFixed(1)} a ${est2.toFixed(1)} noches. `,\n  `Estancias m\u00e1s largas suelen llevar descuento por semana o mes, lo que baja el ADR aunque llene mejor.`,\n  `\u00bfSe est\u00e1n aplicando descuentos por larga estancia? \u00bfCompensan en ocupaci\u00f3n lo que restan en ADR?`]);\n}else if(est2<est1*0.92){\n H.push([`La <b>estancia media baja<\/b> de ${est1.toFixed(1)} a ${est2.toFixed(1)} noches. `,\n  `Estancias m\u00e1s cortas y m\u00e1s rotaci\u00f3n pueden subir ADR pero aumentan limpieza y huecos.`,\n  `\u00bfInteresa esta rotaci\u00f3n m\u00e1s alta o conviene incentivar estancias largas?`]);\n}\n\/\/ lo que no se puede ver\nH.push([`<b>Lo que estos datos no pueden ver:<\/b> `,\n `si el mercado (competencia, evento, temporada) movi\u00f3 los precios del destino, y con qu\u00e9 antelaci\u00f3n se reserva (booking window). Eso requiere datos de mercado y la fecha de creaci\u00f3n de cada reserva.`,\n `\u00bfC\u00f3mo se movi\u00f3 nuestro ADR frente al del mercado en este periodo? \u00bfY la antelaci\u00f3n media de reserva?`]);\n\nhtml+=`<div class=\"hyp\"><div class=\"ht\">Hip\u00f3tesis e interpretaci\u00f3n (inferido, a validar con revenue)<\/div><ul>`+\n H.map(x=>`<li>${x[0]}${x[1]}<span class=\"q\">Pregunta a tu equipo: ${x[2]}<\/span><\/li>`).join('')+\n `<\/ul><\/div><\/div>`;\nbox.innerHTML=html;}\n\n\/\/ \u2500\u2500 comparativa de periodos \u2500\u2500\nfunction renderCmp(){\nconst rows=Object.entries(PER).map(([k,v])=>{\n const A=agg(Y1,v.ms),B=agg(Y2,v.ms);\n return{k,lab:v.lab,cerr:v.ms.every(m=>m<=MC),A,B};});\ndocument.getElementById('tCmp').innerHTML=\n`<thead><tr class=\"grp\"><th><\/th><th><\/th><th colspan=\"2\">${Y1}<\/th><th colspan=\"2\" class=\"sp\">${Y2}<\/th><th colspan=\"3\" class=\"sp\">Variaci\\u00F3n<\/th><\/tr>\n<tr><th>Periodo<\/th><th>Estado<\/th><th class=\"num\">Revenue<\/th><th class=\"num\">Ocup.<\/th>\n<th class=\"num sp\">Revenue<\/th><th class=\"num\">Ocup.<\/th>\n<th class=\"num sp\">Revenue<\/th><th class=\"num\">Ocup.<\/th><th class=\"num\">ADR<\/th><\/tr><\/thead><tbody>`+\nrows.map(r=>`<tr${r.k===P?' style=\"background:#F4F3F0;font-weight:600\"':''}>\n<td class=\"nm\">${r.lab}<\/td>\n<td><span class=\"pill ${r.cerr?'a':'c'}\">${r.cerr?'cerrado':'en curso'}<\/span><\/td>\n<td class=\"num\">${eu(r.A.rev)}<\/td><td class=\"num\">${r.A.occ.toFixed(1)}%<\/td>\n<td class=\"num sp\">${eu(r.B.rev)}<\/td><td class=\"num\">${r.B.occ.toFixed(1)}%<\/td>\n<td class=\"num sp\">${dh(r.B.rev,r.A.rev)}<\/td><td class=\"num\">${dh(r.B.occ,r.A.occ)}<\/td>\n<td class=\"num\">${dh(r.B.adr,r.A.adr)}<\/td><\/tr>`).join('')+'<\/tbody>';\nconst cerr=rows.filter(r=>r.cerr),curso=rows.filter(r=>!r.cerr);\ndocument.getElementById('cmpNota').innerHTML=\n`<b>Por qu\\u00E9 esta tabla importa.<\/b> Los periodos marcados como cerrados comparan meses completos en ambos a\\u00F1os: son los \\u00FAnicos v\\u00E1lidos para juzgar rendimiento. Los marcados en curso incluyen meses de ${Y2} a\\u00FAn sin completar, por lo que su variaci\\u00F3n ser\\u00E1 negativa por construcci\\u00F3n y no refleja una ca\\u00EDda real.`;\nmk('cmp1',{type:'bar',data:{labels:rows.map(r=>r.lab),datasets:[\n{label:S1,data:rows.map(r=>r.A.rev),backgroundColor:C.tan,borderRadius:2},\n{label:S2,data:rows.map(r=>r.B.rev),backgroundColor:rows.map(r=>r.cerr?C.sea:'#DCEEF0'),borderRadius:2}]},\noptions:{maintainAspectRatio:false,plugins:{legend:{position:'bottom',labels:{boxWidth:10,padding:13}},\ntooltip:{callbacks:{label:x=>x.dataset.label+' '+eu(x.raw),\nafterLabel:x=>rows[x.dataIndex].cerr?'periodo cerrado':'periodo en curso'}}},\nscales:{y:{...GRID,ticks:{callback:v=>nf(v\/1000)+'k'}},x:NOGRID}}});}\n\n\/\/ \u2500\u2500 desglose individual \u2500\u2500\nfunction renderBrk(){\nconst s=sel(),sl=document.getElementById('selB'),prev=sl.value;\ndocument.getElementById('brkS').textContent=s.length\n ?`KPIs individuales del periodo seleccionado, comparados entre ${Y1} y ${Y2}.`\n :'Selecciona al menos un alojamiento arriba.';\nsl.innerHTML=[...s].sort((a,b)=>aggAloj(b,Y2,ms()).rev-aggAloj(a,Y2,ms()).rev)\n .map(d=>`<option value=\"${d.nombre}\">${d.nombre}<\/option>`).join('');\nif(s.some(d=>d.nombre===prev))sl.value=prev;\nsl.onchange=drawBrk;drawBrk();}\n\nfunction drawBrk(){\nconst box=document.getElementById('brkBox'),n=document.getElementById('selB').value;\nconst d=D.detalle.find(x=>x.nombre===n),mm=ms();\nif(!d){box.innerHTML='<div class=\"empty\">No hay alojamientos seleccionados.<\/div>';return;}\nconst a=aggAloj(d,Y1,mm),b=aggAloj(d,Y2,mm);\nconst K=[['Revenue',eu(a.rev),eu(b.rev),b.rev,a.rev],\n['Reservas',a.res,b.res,b.res,a.res],\n['ADR',eu(a.adr),eu(b.adr),b.adr,a.adr],\n['Noches',nf(a.noc),nf(b.noc),b.noc,a.noc],\n['Ocupaci\\u00F3n',a.occ.toFixed(1)+'%',b.occ.toFixed(1)+'%',b.occ,a.occ]];\nconst mx=K.map(k=>Math.max(k[3],k[4])||1);\nbox.innerHTML=`<div class=\"bhead\"><span class=\"dot\"><\/span><span class=\"bn\">${d.nombre}<\/span>\n<span class=\"by\">${PER[P].lab} \\u00B7 ${Y1} vs ${Y2}<\/span>\n<span class=\"bm\">${b.mes} meses con actividad en ${Y2}${d.grupo_to?' \\u00B7 con turoperador':''}<\/span><\/div>\n<div class=\"bk\">`+K.map((k,i)=>`<div class=\"bkc\"><div class=\"l\">${k[0]}<\/div>\n<div class=\"y1\"><span>${Y1}<\/span><b>${k[1]}<\/b><\/div>\n<div class=\"y2\"><span>${Y2}<\/span><b>${k[2]}<\/b><\/div>\n<div class=\"dl\">${dh(k[3],k[4])}<span class=\"ar\">${Y1} \\u2192 ${Y2}<\/span><\/div>\n<div class=\"mb\"><span>${Y1}<\/span><i style=\"width:${k[4]\/mx[i]*100}%;background:var(--tan)\"><\/i><\/div>\n<div class=\"mb\"><span>${Y2}<\/span><i style=\"width:${k[3]\/mx[i]*100}%;background:var(--sea)\"><\/i><\/div>\n<\/div>`).join('')+`<\/div>\n<div class=\"g2\">\n<div class=\"card\"><h3>Revenue mensual \\u00B7 ${d.nombre}<\/h3><div class=\"cs\">${Y1} vs ${Y2} \\u00B7 meses del periodo destacados<\/div><div class=\"ch\"><canvas id=\"b1\"><\/canvas><\/div><\/div>\n<div class=\"card\"><h3>Canales \\u00B7 ${PER[P].lab}<\/h3><div class=\"cs\">Origen de los ingresos de esta unidad<\/div><div class=\"ch\"><canvas id=\"b2\"><\/canvas><\/div><\/div><\/div>`;\nconst inP=MN.map((_,i)=>mm.includes(i+1));\nmk('b1',{type:'bar',data:{labels:MN,datasets:[\n{label:S1,data:MN.map((_,i)=>{const v=d[S1].mensual[i+1];return v?v.rev:0}),\n backgroundColor:inP.map(x=>x?C.tan:'#EDE7DC'),borderRadius:2},\n{label:S2,data:MN.map((_,i)=>{const v=d[S2].mensual[i+1];return v?v.rev:0}),\n backgroundColor:inP.map(x=>x?C.sea:'#DCEEF0'),borderRadius:2}]},\noptions:{maintainAspectRatio:false,plugins:{legend:{position:'bottom',labels:{boxWidth:10,padding:13}},\ntooltip:{callbacks:{label:x=>x.dataset.label+' '+eu(x.raw)}}},\nscales:{y:{...GRID,ticks:{callback:v=>nf(v)+' \\u20AC'}},x:NOGRID}}});\nconst cb=canales(Y2,mm,[d]),ck=Object.keys(cb);\nmk('b2',{type:'doughnut',data:{labels:ck.length?ck:['Sin datos'],\ndatasets:[{data:ck.length?ck.map(k=>cb[k]):[1],backgroundColor:PAL,borderWidth:2,borderColor:'#fff'}]},\noptions:{maintainAspectRatio:false,cutout:'58%',plugins:{legend:{position:'bottom',labels:{boxWidth:10,padding:12}},\ntooltip:{callbacks:{label:x=>x.label+' '+eu(x.raw)}}}}});}\n\n\/\/ \u2500\u2500 ranking \u2500\u2500\nlet rk='rev2',rd=-1;\nfunction renderRank(){\nconst mm=ms();\nconst rows=sel().map(d=>{const a=aggAloj(d,Y1,mm),b=aggAloj(d,Y2,mm);\n return{n:d.nombre,rev1:a.rev,rev2:b.rev,occ1:a.occ,occ2:b.occ,adr1:a.adr,adr2:b.adr,\n  noc2:b.noc,res2:b.res,m2:b.mes,to:d.grupo_to};});\nconst mxr=Math.max(...rows.map(r=>r.rev2),1);\nrows.sort((a,b)=>typeof a[rk]==='string'?rd*a[rk].localeCompare(b[rk]):rd*(a[rk]-b[rk]));\nconst CO=[['n','Alojamiento',0],['rev1',Y1,1],['rev2',Y2,1],['occ2','Ocup.',1],\n['adr2','ADR',1],['noc2','Noches',1],['res2','Reservas',1],['m2','Meses',1]];\ndocument.getElementById('tR').innerHTML='<thead><tr>'+CO.map(c=>\n`<th class=\"${c[2]?'num':''}\" data-k=\"${c[0]}\">${c[1]}${rk===c[0]?(rd<0?' \\u2193':' \\u2191'):''}<\/th>`).join('')+'<\/tr><\/thead><tbody>'+\nrows.map(r=>`<tr><td class=\"nm\">${r.n}${r.to?' <span class=\"pill g\">TO<\/span>':''}\n<div class=\"bar\"><i style=\"width:${r.rev2\/mxr*100}%\"><\/i><\/div><\/td>\n<td class=\"num\">${eu(r.rev1)}<\/td><td class=\"num\">${eu(r.rev2)} ${dh(r.rev2,r.rev1)}<\/td>\n<td class=\"num\">${r.occ2.toFixed(1)}% ${dh(r.occ2,r.occ1)}<\/td><td class=\"num\">${eu(r.adr2)}<\/td>\n<td class=\"num\">${nf(r.noc2)}<\/td><td class=\"num\">${r.res2}<\/td><td class=\"num\">${r.m2}<\/td><\/tr>`).join('')+'<\/tbody>';\ndocument.querySelectorAll('#tR th').forEach(t=>t.onclick=()=>{\n const k=t.dataset.k;if(rk===k)rd=-rd;else{rk=k;rd=k==='n'?1:-1;}renderRank();});}\n\n\/\/ \u2500\u2500 canales \u2500\u2500\nfunction renderCan(){\nconst mm=ms(),c1=canales(Y1,mm),c2=canales(Y2,mm),n1=canalNoches(Y1,mm),n2=canalNoches(Y2,mm);\nconst ks=[...new Set([...Object.keys(c1),...Object.keys(c2)])].sort((a,b)=>(c2[b]||0)-(c2[a]||0));\nconst t1=Object.values(c1).reduce((a,b)=>a+b,0),t2=Object.values(c2).reduce((a,b)=>a+b,0);\nmk('n1',{type:'bar',data:{labels:ks,datasets:[\n{label:S1,data:ks.map(k=>c1[k]||0),backgroundColor:C.tan,borderRadius:2},\n{label:S2,data:ks.map(k=>c2[k]||0),backgroundColor:C.sea,borderRadius:2}]},\noptions:{maintainAspectRatio:false,plugins:{legend:{position:'bottom',labels:{boxWidth:10,padding:13}},\ntooltip:{callbacks:{label:x=>x.dataset.label+' '+eu(x.raw)}}},\nscales:{y:{...GRID,ticks:{callback:v=>nf(v\/1000)+'k'}},x:NOGRID}}});\nconst k2=ks.filter(k=>c2[k]);\nmk('n2',{type:'doughnut',data:{labels:k2,datasets:[{data:k2.map(k=>c2[k]),\nbackgroundColor:PAL,borderWidth:2,borderColor:'#fff'}]},\noptions:{maintainAspectRatio:false,cutout:'58%',plugins:{legend:{position:'bottom',labels:{boxWidth:10,padding:12}},\ntooltip:{callbacks:{label:x=>x.label+' '+eu(x.raw)+' ('+(c2[x.label]\/t2*100).toFixed(1)+'%)'}}}}});\ndocument.getElementById('tN').innerHTML=\n`<thead><tr class=\"grp\"><th><\/th><th colspan=\"3\">${Y1}<\/th><th colspan=\"3\" class=\"sp\">${Y2}<\/th><\/tr>\n<tr><th>Canal<\/th><th class=\"num\">Revenue<\/th><th class=\"num\">%<\/th><th class=\"num\">ADR<\/th>\n<th class=\"num sp\">Revenue<\/th><th class=\"num\">%<\/th><th class=\"num\">ADR<\/th><\/tr><\/thead><tbody>`+\nks.map((k,i)=>{const a=c1[k]||0,b=c2[k]||0,na=n1[k]||0,nb=n2[k]||0;\nreturn `<tr><td class=\"nm\"><span class=\"pill ${k==='Turoperador'?'c':k==='Sin canal informado'?'g':['a','b','a','b'][i%4]}\">${k}<\/span><\/td>\n<td class=\"num\">${eu(a)}<\/td><td class=\"num\">${t1?(a\/t1*100).toFixed(1):0}%<\/td><td class=\"num\">${na?eu(a\/na):'\\u2014'}<\/td>\n<td class=\"num sp\">${eu(b)}<\/td><td class=\"num\">${t2?(b\/t2*100).toFixed(1):0}%<\/td><td class=\"num\">${nb?eu(b\/nb):'\\u2014'}<\/td><\/tr>`}).join('')+'<\/tbody>';\nconst hayTO=c1['Turoperador']||c2['Turoperador'], haySC=c1['Sin canal informado'];\ndocument.getElementById('canNota').innerHTML=\n(hayTO?`<b>Turoperador.<\/b> Las reservas de turoperador se registran en el PMS como FrontOffice y aqu\\u00ED aparecen etiquetadas como tal. Afecta \\u00FAnicamente a Oasis y Jardines del Cura. `:'')+\n(haySC?`<b>Sin canal informado.<\/b> Corresponde a reservas cargadas al PMS durante la migraci\\u00F3n desde el gestor anterior, sin canal de origen. No es un canal comercial y no debe compararse con ${Y2}.`:'');}\n\n\/\/ \u2500\u2500 inventario \u2500\u2500\nfunction renderInv(){\nconst mm=ms();\nconst s1=MN.map((_,i)=>sel().filter(d=>{const v=d[S1].mensual[i+1];return v&&v.noches>0}).length);\nconst s2=MN.map((_,i)=>sel().filter(d=>{const v=d[S2].mensual[i+1];return v&&v.noches>0}).length);\nmk('i1',{type:'line',data:{labels:MN,datasets:[\n{label:S1,data:s1,borderColor:C.tan,backgroundColor:'rgba(209,179,140,.16)',fill:true,stepped:true,pointRadius:0,borderWidth:2.5},\n{label:S2,data:s2,borderColor:C.cadet,backgroundColor:'rgba(36,43,89,.07)',fill:true,stepped:true,pointRadius:0,borderWidth:2.5}]},\noptions:{maintainAspectRatio:false,plugins:{legend:{position:'bottom',labels:{boxWidth:10,padding:13}},\ntooltip:{callbacks:{label:x=>x.dataset.label+' \\u00B7 '+x.raw+' alojamientos'}}},\nscales:{y:{...GRID,beginAtZero:true},x:NOGRID}}});\ndrawInv();}\ndocument.getElementById('selIY').innerHTML=[Y1,Y2].map(y=>`<option>${y}<\/option>`).join('');\ndocument.getElementById('selIY').value=Y2;\ndocument.getElementById('selIM').innerHTML=MF.map((m,i)=>`<option value=\"${i+1}\">${m}<\/option>`).join('');\ndocument.getElementById('selIM').value=Math.min(MC,12);\ndocument.getElementById('selIY').onchange=drawInv;\ndocument.getElementById('selIM').onchange=drawInv;\nfunction drawInv(){\nconst y=document.getElementById('selIY').value,m=+document.getElementById('selIM').value;\nconst l=sel().filter(d=>{const v=d[String(y)].mensual[m];return v&&v.noches>0}).map(d=>d.nombre).sort();\ndocument.getElementById('boxI').innerHTML=l.length\n?`<div style=\"font-size:12px;color:var(--mut);margin-bottom:7px\">${l.length} alojamientos con actividad en ${MF[m-1]} ${y}<\/div>\n<div class=\"chips\">${l.map(n=>`<span class=\"chip\">${n}<\/span>`).join('')}<\/div>`\n:'<div style=\"color:var(--mut);font-size:13px\">Sin actividad registrada.<\/div>';}\n\ndocument.querySelectorAll('.nv button').forEach(b=>b.onclick=()=>{\ndocument.querySelectorAll('.nv button').forEach(x=>x.classList.remove('on'));\ndocument.querySelectorAll('.pane').forEach(x=>x.classList.remove('on'));\nb.classList.add('on');document.getElementById(b.dataset.p).classList.add('on');\nwindow.scrollTo({top:0,behavior:'smooth'});});\n\nsyncPick();render();\n<\/script><\/body><\/html>\t\t\t\t<\/div>\n\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>Era del Centeno &middot; Revenue Dashboard Villas RiveroRevenue Dashboard Actualizado Resumen Comparativa de periodos Ranking Canales Inventario Periodo YTDQ1Q2 Q3Q4S1 S2A&ntilde;o completo Vista Era del Centeno\u00danico activo Manual Alojamiento Seleccionar&hellip; TodosNinguno ComparablesEra del Centeno Comparativa Resumen ejecutivo KPIs del periodo seleccionado, comparados entre ambos a&ntilde;os. Revenue mensual Meses del periodo destacados; el resto atenuado Ocupaci&oacute;n [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-1536","page","type-page","status-publish","hentry"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Dashboard Era del centeno - Villas Rivero<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/villasrivero.com\/en\/dashboard-era-del-centeno\/\" \/>\n<meta property=\"og:locale\" content=\"en_GB\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Dashboard Era del centeno - Villas Rivero\" \/>\n<meta property=\"og:description\" content=\"Era del Centeno &middot; Revenue Dashboard Villas RiveroRevenue Dashboard Actualizado Resumen Comparativa de periodos Ranking Canales Inventario Periodo YTDQ1Q2 Q3Q4S1 S2A&ntilde;o completo Vista Era del Centeno\u00danico activo Manual Alojamiento Seleccionar&hellip; TodosNinguno ComparablesEra del Centeno Comparativa Resumen ejecutivo KPIs del periodo seleccionado, comparados entre ambos a&ntilde;os. Revenue mensual Meses del periodo destacados; el resto atenuado Ocupaci&oacute;n [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/villasrivero.com\/en\/dashboard-era-del-centeno\/\" \/>\n<meta property=\"og:site_name\" content=\"Villas Rivero\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-12T11:18:51+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Estimated reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/villasrivero.com\\\/dashboard-era-del-centeno\\\/\",\"url\":\"https:\\\/\\\/villasrivero.com\\\/dashboard-era-del-centeno\\\/\",\"name\":\"Dashboard Era del centeno - Villas Rivero\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/villasrivero.com\\\/#website\"},\"datePublished\":\"2026-08-12T09:19:51+00:00\",\"dateModified\":\"2026-08-12T11:18:51+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/villasrivero.com\\\/dashboard-era-del-centeno\\\/#breadcrumb\"},\"inLanguage\":\"en-GB\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/villasrivero.com\\\/dashboard-era-del-centeno\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/villasrivero.com\\\/dashboard-era-del-centeno\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Portada\",\"item\":\"https:\\\/\\\/villasrivero.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Dashboard Era del centeno\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/villasrivero.com\\\/#website\",\"url\":\"https:\\\/\\\/villasrivero.com\\\/\",\"name\":\"Villas Rivero\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/villasrivero.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-GB\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Dashboard Era del centeno - Villas Rivero","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/villasrivero.com\/en\/dashboard-era-del-centeno\/","og_locale":"en_GB","og_type":"article","og_title":"Dashboard Era del centeno - Villas Rivero","og_description":"Era del Centeno &middot; Revenue Dashboard Villas RiveroRevenue Dashboard Actualizado Resumen Comparativa de periodos Ranking Canales Inventario Periodo YTDQ1Q2 Q3Q4S1 S2A&ntilde;o completo Vista Era del Centeno\u00danico activo Manual Alojamiento Seleccionar&hellip; TodosNinguno ComparablesEra del Centeno Comparativa Resumen ejecutivo KPIs del periodo seleccionado, comparados entre ambos a&ntilde;os. Revenue mensual Meses del periodo destacados; el resto atenuado Ocupaci&oacute;n [&hellip;]","og_url":"https:\/\/villasrivero.com\/en\/dashboard-era-del-centeno\/","og_site_name":"Villas Rivero","article_modified_time":"2026-08-12T11:18:51+00:00","twitter_card":"summary_large_image","twitter_misc":{"Estimated reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/villasrivero.com\/dashboard-era-del-centeno\/","url":"https:\/\/villasrivero.com\/dashboard-era-del-centeno\/","name":"Dashboard Era del centeno - Villas Rivero","isPartOf":{"@id":"https:\/\/villasrivero.com\/#website"},"datePublished":"2026-08-12T09:19:51+00:00","dateModified":"2026-08-12T11:18:51+00:00","breadcrumb":{"@id":"https:\/\/villasrivero.com\/dashboard-era-del-centeno\/#breadcrumb"},"inLanguage":"en-GB","potentialAction":[{"@type":"ReadAction","target":["https:\/\/villasrivero.com\/dashboard-era-del-centeno\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/villasrivero.com\/dashboard-era-del-centeno\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Portada","item":"https:\/\/villasrivero.com\/"},{"@type":"ListItem","position":2,"name":"Dashboard Era del centeno"}]},{"@type":"WebSite","@id":"https:\/\/villasrivero.com\/#website","url":"https:\/\/villasrivero.com\/","name":"Villas Rivero","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/villasrivero.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-GB"}]}},"_links":{"self":[{"href":"http:\/\/villasrivero.com\/en\/wp-json\/wp\/v2\/pages\/1536","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/villasrivero.com\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"http:\/\/villasrivero.com\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"http:\/\/villasrivero.com\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/villasrivero.com\/en\/wp-json\/wp\/v2\/comments?post=1536"}],"version-history":[{"count":10,"href":"http:\/\/villasrivero.com\/en\/wp-json\/wp\/v2\/pages\/1536\/revisions"}],"predecessor-version":[{"id":1546,"href":"http:\/\/villasrivero.com\/en\/wp-json\/wp\/v2\/pages\/1536\/revisions\/1546"}],"wp:attachment":[{"href":"http:\/\/villasrivero.com\/en\/wp-json\/wp\/v2\/media?parent=1536"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}