<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>SmartBoiler</title>
  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"
          crossorigin="anonymous"></script>
  <style>
    *{box-sizing:border-box;margin:0;padding:0}
    :root{
      --blue:#2b6cb0;--blue-l:#ebf8ff;--green:#276749;--green-l:#c6f6d5;
      --orange:#c05621;--orange-l:#feebc8;--red:#9b2c2c;--red-l:#fed7d7;
      --gray:#718096;--bg:#f0f4f8;--card:#fff;--border:#e2e8f0;
    }
    body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
         background:var(--bg);color:#2d3748}
    header{background:var(--blue);color:#fff;padding:.75rem 1.5rem;
           display:flex;align-items:center;gap:.75rem}
    header h1{font-size:1.2rem;font-weight:700}
    .dot{width:12px;height:12px;border-radius:50%;background:#a0aec0;flex-shrink:0}
    .dot.off{background:#fc8181}.dot.on_idle{background:#f6e05e}.dot.on_heating{background:#68d391}
    #ts{margin-left:auto;font-size:.78rem;opacity:.75}

    /* Tabs */
    .tabs{background:#1a4a8a;display:flex;padding:0 1rem}
    .tab-btn{color:rgba(255,255,255,.65);background:none;border:none;
             border-bottom:2px solid transparent;padding:.6rem 1.1rem;
             font-size:.88rem;cursor:pointer;transition:all .15s}
    .tab-btn:hover{color:#fff}
    .tab-btn.active{color:#fff;border-bottom-color:#90cdf4}

    /* Tab panes */
    .tab-pane{display:none}.tab-pane.active{display:block}

    /* Main grid */
    main{max-width:1400px;margin:1.25rem auto;padding:0 1rem;
         display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:1.25rem}

    /* Cards */
    .card{background:var(--card);border-radius:12px;padding:1.25rem;
          box-shadow:0 1px 4px rgba(0,0,0,.08)}
    .card h2{font-size:.78rem;color:var(--gray);text-transform:uppercase;
             letter-spacing:.06em;margin-bottom:.9rem}
    .big{font-size:2.4rem;font-weight:700;color:var(--blue);line-height:1}
    .unit{font-size:1rem;color:#a0aec0}
    .badge{display:inline-block;padding:.2rem .7rem;border-radius:999px;
           font-size:.78rem;font-weight:600;margin-top:.6rem}
    .badge.unavailable{background:#e2e8f0;color:#4a5568}
    .badge.off{background:var(--red-l);color:var(--red)}
    .badge.on_idle{background:#fefcbf;color:#744210}
    .badge.on_heating{background:var(--green-l);color:var(--green)}
    .row{display:flex;justify-content:space-between;padding:.3rem 0;
         border-bottom:1px solid #f7fafc;font-size:.88rem}
    .row:last-child{border-bottom:none}

    /* 24h grid */
    .hgrid{display:grid;grid-template-columns:repeat(24,1fr);gap:1px;margin-top:.5rem}
    .hcell{height:32px;border-radius:3px;display:flex;align-items:center;
           justify-content:center;font-size:.6rem;font-weight:600;color:#fff;cursor:default}
    .hcell.heat{background:#f6ad55}.hcell.pv{background:#68d391}
    .hcell.hdo{background:#fc8181}.hcell.idle{background:#e2e8f0;color:var(--gray)}
    .hcell.cal-vacation{background:#bee3f8;color:#2a69ac}
    .hcell.cal-off{background:#b2b2b2;color:#fff}
    .hcell.cal-boost{background:#d6bcfa;color:#44337a}

    /* Calendar events */
    .evt-list{margin-top:.5rem}
    .evt-row{display:flex;align-items:center;gap:.5rem;padding:.35rem 0;border-bottom:1px solid #f0f4f8;font-size:.84rem}
    .evt-row:last-child{border-bottom:none}
    .evt-dot{width:10px;height:10px;border-radius:50%;flex-shrink:0}
    .evt-info{flex:1;min-width:0}
    .evt-title{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
    .evt-time{font-size:.75rem;color:#718096}
    .evt-del{background:none;border:none;cursor:pointer;color:#fc8181;font-size:1rem;padding:0 .25rem}
    .btn-add{display:inline-block;margin-top:.75rem;padding:.4rem .9rem;background:#2b6cb0;color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:.85rem}
    .btn-add:hover{background:#2c5282}
    /* Modal */
    .overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:100;align-items:center;justify-content:center}
    .overlay.open{display:flex}
    .modal{background:#fff;border-radius:14px;padding:1.5rem;width:min(420px,92vw);box-shadow:0 8px 32px rgba(0,0,0,.15)}
    .modal h3{margin-bottom:1rem;font-size:1rem}
    .form-row{margin-bottom:.75rem}
    .form-row label{display:block;font-size:.8rem;color:#718096;margin-bottom:.25rem}
    .form-row input,.form-row select{width:100%;padding:.4rem .6rem;border:1px solid #e2e8f0;border-radius:6px;font-size:.9rem}
    .modal-btns{display:flex;gap:.5rem;justify-content:flex-end;margin-top:1rem}
    .btn-cancel{padding:.4rem .9rem;border:1px solid #e2e8f0;border-radius:8px;background:#fff;cursor:pointer;font-size:.85rem}
    .btn-save{padding:.4rem .9rem;background:#2b6cb0;color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:.85rem}

    /* Legend */
    .legend{display:flex;gap:.5rem;flex-wrap:wrap;margin-top:.6rem}
    .lbadge{font-size:.75rem;padding:.15rem .6rem;border-radius:6px;font-weight:600}

    /* Span helpers */
    .span2{grid-column:span 2}.span3{grid-column:span 3}
    .full{grid-column:1/-1}
    @media(max-width:700px){.span2,.span3,.full{grid-column:span 1}}

    /* Charts */
    canvas{max-height:220px}.chart-lg canvas{max-height:300px}

    /* HDO weekly grid */
    .wgrid{display:grid;grid-template-columns:60px repeat(24,1fr);gap:1px;margin-top:.5rem;font-size:.62rem}
    .wgrid .day-label{display:flex;align-items:center;color:var(--gray);padding-right:4px;font-weight:600}
    .wgrid .wcell{height:22px;border-radius:2px;background:#e2e8f0}
    .wgrid .wcell.blocked{background:#fc8181}
    .wgrid .hr-header{text-align:center;color:var(--gray);padding:.1rem 0}
    .wgrid .corner{} /* top-left empty */

    /* Predictor heatmap */
    .pheatmap{display:grid;grid-template-columns:60px repeat(24,1fr);gap:1px;margin-top:.5rem;font-size:.62rem}
    .pheatmap .day-lbl{display:flex;align-items:center;color:var(--gray);padding-right:4px;font-weight:600}
    .pheatmap .pcell{height:22px;border-radius:2px;cursor:default}
    .pheatmap .hr-hdr{text-align:center;color:var(--gray)}

    /* Settings */
    .settings-grid{display:grid;grid-template-columns:1fr 1fr;gap:.75rem}
    .s-field{display:flex;flex-direction:column;gap:.2rem}
    .s-field label{font-size:.78rem;color:var(--gray)}
    .s-field select{padding:.35rem .5rem;border:1px solid #e2e8f0;border-radius:6px;font-size:.85rem}

  </style>
</head>
<body>
<header>
  <div class="dot" id="status-dot"></div>
  <h1>SmartBoiler</h1>
  <span id="ts"></span>
</header>

<div class="tabs">
  <button class="tab-btn active" onclick="showTab('overview')">Overview</button>
  <button class="tab-btn" onclick="showTab('history')">History</button>
  <button class="tab-btn" onclick="showTab('predictor')">Predictor</button>
  <button class="tab-btn" onclick="showTab('hdo')">HDO</button>
  <button class="tab-btn" onclick="showTab('calendar')">Calendar</button>
  <a href="settings" style="margin-left:auto;color:rgba(255,255,255,.7);font-size:.82rem;
     padding:.6rem 1rem;text-decoration:none;align-self:stretch;display:flex;align-items:center"
     title="Edit configuration">⚙ Settings</a>
</div>

<!-- ── OVERVIEW TAB ─────────────────────────────────────────────────── -->
<div id="tab-overview" class="tab-pane active">
<main>

  <!-- Temperature card -->
  <div class="card">
    <h2>Water Temperature</h2>
    <div class="big" id="boiler-tmp">—<span class="unit">°C</span></div>
    <div class="badge" id="boiler-badge">—</div>
    <div style="margin-top:.9rem">
      <div class="row"><span>Set temp</span><strong id="set-tmp">—°C</strong></div>
      <div class="row"><span>Min temp</span><strong id="min-tmp">—°C</strong></div>
      <div class="row"><span>Last legionella</span><strong id="last-leg">—</strong></div>
    </div>
  </div>

  <!-- Today's plan -->
  <div class="card span2">
    <h2>Today's Heating Plan</h2>
    <div class="hgrid" id="plan-grid"></div>
    <div class="legend" style="margin-top:.75rem">
      <span class="lbadge" style="background:#f6ad55;color:#7b341e">Heat</span>
      <span class="lbadge" style="background:#68d391;color:#276749">PV</span>
      <span class="lbadge" style="background:#fc8181;color:#9b2c2c">HDO</span>
      <span class="lbadge" style="background:#e2e8f0;color:#718096">Idle</span>
      <span class="lbadge" style="background:#bee3f8;color:#2a69ac">Vacation</span>
      <span class="lbadge" style="background:#d6bcfa;color:#44337a">Boost</span>
    </div>
  </div>

  <!-- Consumption forecast chart -->
  <div class="card span2 chart-lg">
    <h2>Predicted Consumption (next 24 h)</h2>
    <canvas id="forecast-chart"></canvas>
  </div>

  <!-- System info -->
  <div class="card">
    <h2>System Info</h2>
    <div id="sys-info"></div>
  </div>

</main>
</div>

<!-- ── HISTORY TAB ──────────────────────────────────────────────────── -->
<div id="tab-history" class="tab-pane">
<main>
  <div class="card full">
    <h2>Consumption History &nbsp;
      <select id="hist-period" onchange="loadHistory()" style="font-size:.8rem;padding:.2rem .4rem;border:1px solid #e2e8f0;border-radius:5px">
        <option value="1d">Last 24 h</option>
        <option value="7d" selected>Last 7 days</option>
        <option value="30d">Last 30 days</option>
        <option value="90d">Last 90 days</option>
      </select>
    </h2>
    <canvas id="history-chart" style="max-height:320px"></canvas>
  </div>
  <div class="card full">
    <h2>Daily Summary</h2>
    <div id="daily-table"></div>
  </div>
  <div class="card full">
    <h2>Prediction Accuracy</h2>
    <canvas id="accuracy-chart" style="max-height:260px"></canvas>
  </div>
</main>
</div>

<!-- ── PREDICTOR TAB ────────────────────────────────────────────────── -->
<div id="tab-predictor" class="tab-pane">
<main>
  <div class="card full">
    <h2>Rolling Histogram — Predicted kWh / hour</h2>
    <div class="pheatmap" id="pred-heatmap"></div>
    <div style="margin-top:.5rem;font-size:.78rem;color:var(--gray)" id="pred-meta"></div>
  </div>
</main>
</div>

<!-- ── HDO TAB ──────────────────────────────────────────────────────── -->
<div id="tab-hdo" class="tab-pane">
<main>
  <div class="card full">
    <h2>HDO (Ripple Control) — Learned Weekly Pattern</h2>
    <div class="wgrid" id="hdo-grid"></div>
    <div style="margin-top:.75rem;font-size:.8rem;color:var(--gray)">
      Red = HDO expected to block relay. Learned from relay "unavailable" observations.
    </div>
  </div>
</main>
</div>

<!-- ── CALENDAR TAB ─────────────────────────────────────────────────── -->
<div id="tab-calendar" class="tab-pane">
<main>
  <div class="card full">
    <h2>Boiler Calendar Events</h2>
    <div class="evt-list" id="cal-list"></div>
    <button class="btn-add" onclick="openAddModal()">+ Add Event</button>
  </div>
</main>
</div>

<!-- Add Event Modal -->
<div class="overlay" id="add-modal">
  <div class="modal">
    <h3>New Boiler Event</h3>
    <div class="form-row">
      <label>Event type</label>
      <select id="m-type">
        <option value="vacation_min">Vacation — keep warm (min temp)</option>
        <option value="vacation_off">Vacation — boiler off</option>
        <option value="boost_max">Boost — heat to max</option>
        <option value="boost_temp">Boost — custom temp</option>
      </select>
    </div>
    <div class="form-row" id="m-target-row" style="display:none">
      <label>Target temperature (°C)</label>
      <input type="number" id="m-target" min="30" max="95" value="65">
    </div>
    <div class="form-row">
      <label>Start (YYYY-MM-DD HH:MM)</label>
      <input type="text" id="m-start" placeholder="2026-03-15 08:00">
    </div>
    <div class="form-row">
      <label>End (YYYY-MM-DD HH:MM)</label>
      <input type="text" id="m-end" placeholder="2026-03-22 20:00">
    </div>
    <div class="modal-btns">
      <button class="btn-cancel" onclick="closeAddModal()">Cancel</button>
      <button class="btn-save" onclick="saveEvent()">Save</button>
    </div>
  </div>
</div>

<script>
// ── Tabs ──────────────────────────────────────────────────────────────────
function showTab(name) {
  document.querySelectorAll('.tab-pane').forEach(p => p.classList.remove('active'));
  document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
  document.getElementById('tab-' + name).classList.add('active');
  event.target.classList.add('active');
  if (name === 'history')   loadHistory();
  if (name === 'predictor') loadPredictor();
  if (name === 'hdo')       loadHDO();
  if (name === 'calendar')  loadCalendar();
}

// ── Chart instances ───────────────────────────────────────────────────────
let forecastChart = null, historyChart = null, accuracyChart = null;

function destroyChart(ref) { if (ref) { try { ref.destroy(); } catch(e){} } return null; }

// ── Overview ──────────────────────────────────────────────────────────────
async function loadOverview() {
  try {
    const d = await fetch('api/status').then(r => r.json());
    document.getElementById('ts').textContent = new Date().toLocaleTimeString();

    const tmp = d.boiler_temp;
    document.getElementById('boiler-tmp').innerHTML =
      (tmp !== null && tmp !== undefined ? tmp.toFixed(1) : '—') + '<span class="unit">°C</span>';

    const st = d.boiler_status || 'unavailable';
    const dot = document.getElementById('status-dot');
    dot.className = 'dot ' + st;
    const badge = document.getElementById('boiler-badge');
    badge.className = 'badge ' + st;
    badge.textContent = {off:'Off', on_idle:'On — idle', on_heating:'On — heating', unavailable:'Unavailable'}[st] || st;

    document.getElementById('set-tmp').textContent = (d.set_tmp ?? '—') + '°C';
    document.getElementById('min-tmp').textContent = (d.min_tmp ?? '—') + '°C';
    document.getElementById('last-leg').textContent = d.last_legionella || '—';

    // Sys info
    const si = document.getElementById('sys-info');
    si.innerHTML = (d.sys_info || []).map(([k,v]) =>
      `<div class="row"><span>${k}</span><strong>${v}</strong></div>`).join('');

    // Plan grid
    const slots = d.plan_slots || [];
    const grid = document.getElementById('plan-grid');
    const now_h = new Date().getHours();
    grid.innerHTML = slots.map((s, i) => {
      let cls = 'idle', tip = s.label;
      if (s.hdo_blocked)    { cls = 'hdo';         tip += ' HDO'; }
      else if (s.pv_free)   { cls = s.heating ? 'pv' : 'idle'; tip += s.heating ? ' PV heat' : ''; }
      else if (s.heating)   { cls = 'heat';         tip += ' heat'; }
      if (s.calendar_mode === 'vacation_min' || s.calendar_mode === 'vacation_off')
        cls = 'cal-vacation';
      else if (s.calendar_mode === 'boost_max' || s.calendar_mode === 'boost_temp')
        cls = 'cal-boost';
      const active = i === now_h ? 'outline:2px solid #2b6cb0;' : '';
      return `<div class="hcell ${cls}" style="${active}" title="${tip}">${s.label.split(':')[0]}</div>`;
    }).join('');

    // Forecast chart
    const fc = d.forecast_24h || [];
    const labels = slots.length ? slots.map(s => s.label) : [...Array(24)].map((_,i) => i+':00');
    forecastChart = destroyChart(forecastChart);
    const ctx = document.getElementById('forecast-chart').getContext('2d');
    forecastChart = new Chart(ctx, {
      type: 'bar',
      data: {
        labels,
        datasets: [{
          label: 'Predicted kWh',
          data: fc,
          backgroundColor: 'rgba(43,108,176,.65)',
          borderRadius: 3,
        }]
      },
      options: { plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true } } }
    });

  } catch(e) { console.warn('loadOverview error', e); }
}

// ── History ───────────────────────────────────────────────────────────────
async function loadHistory() {
  const period = document.getElementById('hist-period')?.value || '7d';
  try {
    const d = await fetch('api/history?period=' + period).then(r => r.json());
    historyChart = destroyChart(historyChart);
    const ctx = document.getElementById('history-chart').getContext('2d');
    historyChart = new Chart(ctx, {
      type: 'bar',
      data: {
        labels: d.labels || [],
        datasets: [
          { label: 'Consumed kWh', data: d.consumption || [], backgroundColor: 'rgba(43,108,176,.7)', borderRadius:2, yAxisID:'y' },
          { label: 'Relay on (frac)', data: d.relay_on || [], type:'line', borderColor:'#f6ad55', borderWidth:2, pointRadius:0, yAxisID:'y2' },
        ]
      },
      options: {
        plugins: { legend: { position:'bottom' } },
        scales: {
          y:  { beginAtZero:true, title:{ display:true, text:'kWh' } },
          y2: { beginAtZero:true, max:1, position:'right', grid:{ drawOnChartArea:false }, title:{ display:true, text:'relay fraction' } }
        }
      }
    });

    // Daily table
    const tbl = document.getElementById('daily-table');
    const stats = d.daily_stats || [];
    if (!stats.length) { tbl.innerHTML = '<em style="color:var(--gray)">No data yet.</em>'; return; }
    tbl.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:.85rem">
      <thead><tr style="border-bottom:2px solid #e2e8f0">
        <th style="text-align:left;padding:.3rem .5rem">Date</th>
        <th style="text-align:right;padding:.3rem .5rem">kWh</th>
        <th style="text-align:right;padding:.3rem .5rem">Heating h</th>
        <th style="text-align:right;padding:.3rem .5rem">Avg W</th>
      </tr></thead>
      <tbody>${stats.map(r => `<tr style="border-bottom:1px solid #f7fafc">
        <td style="padding:.25rem .5rem">${r.date}</td>
        <td style="text-align:right;padding:.25rem .5rem">${r.consumption_kwh.toFixed(3)}</td>
        <td style="text-align:right;padding:.25rem .5rem">${r.heating_hours}</td>
        <td style="text-align:right;padding:.25rem .5rem">${r.avg_power_w.toFixed(0)}</td>
      </tr>`).join('')}</tbody></table>`;

    // Accuracy chart
    const acc = await fetch('api/accuracy').then(r => r.json());
    accuracyChart = destroyChart(accuracyChart);
    const ctx2 = document.getElementById('accuracy-chart').getContext('2d');
    const bh = acc.by_hour || [];
    accuracyChart = new Chart(ctx2, {
      type: 'bar',
      data: {
        labels: bh.map(h => h.hour + ':00'),
        datasets: [
          { label: 'Predicted', data: bh.map(h => h.predicted), backgroundColor:'rgba(43,108,176,.6)', borderRadius:2 },
          { label: 'Actual mean', data: bh.map(h => h.actual_mean), backgroundColor:'rgba(246,173,85,.6)', borderRadius:2 },
        ]
      },
      options: {
        plugins: { legend: { position:'bottom' },
          subtitle: { display: acc.mae !== null, text: `MAE=${acc.mae} kWh  Bias=${acc.bias} kWh` } },
        scales: { y: { beginAtZero:true, title:{ display:true, text:'kWh/h' } } }
      }
    });
  } catch(e) { console.warn('loadHistory error', e); }
}

// ── Predictor heatmap ─────────────────────────────────────────────────────
async function loadPredictor() {
  try {
    const d = await fetch('api/predictor').then(r => r.json());
    const hm = d.heatmap || {};
    const days = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
    const hours = [...Array(24)].map((_,i) => i);

    // Find max for colour scaling
    let maxVal = 0;
    days.forEach(day => hours.forEach(h => {
      const v = hm[day]?.[h]?.predicted || 0;
      if (v > maxVal) maxVal = v;
    }));

    const grid = document.getElementById('pred-heatmap');
    const corner = '<div></div>';
    const hrHeaders = hours.map(h => `<div class="hr-hdr">${h}</div>`).join('');
    const rows = days.map(day => {
      const cells = hours.map(h => {
        const v = hm[day]?.[String(h)]?.predicted || 0;
        const pct = maxVal > 0 ? v / maxVal : 0;
        const r = Math.round(255 - pct * 100);
        const g = Math.round(255 - pct * 60);
        const b = Math.round(255 - pct * 200);
        const bg = `rgb(${r},${g},${b})`;
        return `<div class="pcell" style="background:${bg}" title="${day} ${h}:00 — ${v.toFixed(4)} kWh"></div>`;
      }).join('');
      return `<div class="day-lbl">${day}</div>${cells}`;
    }).join('');
    grid.innerHTML = corner + hrHeaders + rows;

    const meta = document.getElementById('pred-meta');
    meta.textContent = `Quantile: ${d.quantile}  |  Total samples: ${d.total_samples}  |  Global fallback: ${(d.global_fallback||0).toFixed(4)} kWh`;
  } catch(e) { console.warn('loadPredictor error', e); }
}

// ── HDO grid ──────────────────────────────────────────────────────────────
async function loadHDO() {
  try {
    const d = await fetch('api/status').then(r => r.json());
    const sched = d.hdo_schedule || {};
    const days = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
    const hours = [...Array(24)].map((_,i)=>i);
    const grid = document.getElementById('hdo-grid');
    const corner = '<div class="corner"></div>';
    const hrHeaders = hours.map(h => `<div class="hr-header">${h}</div>`).join('');
    const rows = days.map(day => {
      const blocked = sched[day] || [];
      const cells = hours.map(h =>
        `<div class="wcell${blocked.includes(h) ? ' blocked':''}" title="${day} ${h}:00${blocked.includes(h)?' — HDO':''}"></div>`
      ).join('');
      return `<div class="day-label">${day}</div>${cells}`;
    }).join('');
    grid.innerHTML = corner + hrHeaders + rows;
  } catch(e) { console.warn('loadHDO error', e); }
}

// ── Calendar ──────────────────────────────────────────────────────────────
async function loadCalendar() {
  try {
    const evts = await fetch('api/calendar/events?days=14').then(r => r.json());
    const list = document.getElementById('cal-list');
    if (!evts.length) {
      list.innerHTML = '<em style="color:var(--gray)">No upcoming events.</em>';
      return;
    }
    const colorMap = {vacation_min:'#bee3f8', vacation_off:'#b2b2b2', boost_max:'#d6bcfa', boost_temp:'#d6bcfa'};
    list.innerHTML = evts.map(e => `
      <div class="evt-row">
        <div class="evt-dot" style="background:${colorMap[e.event_type]||'#e2e8f0'}"></div>
        <div class="evt-info">
          <div class="evt-title">${e.summary || e.event_type}</div>
          <div class="evt-time">${e.start} → ${e.end}</div>
        </div>
        <button class="evt-del" title="Delete" onclick="deleteEvent('${e.uid}')">✕</button>
      </div>`).join('');
  } catch(e) { console.warn('loadCalendar error', e); }
}

async function deleteEvent(uid) {
  if (!confirm('Delete this event?')) return;
  try {
    const r = await fetch('api/calendar/events/' + encodeURIComponent(uid), {method:'DELETE'}).then(r=>r.json());
    if (r.ok) loadCalendar();
    else alert('Failed: ' + (r.error || 'unknown error'));
  } catch(e) { alert('Request failed'); }
}

function openAddModal() {
  document.getElementById('add-modal').classList.add('open');
  document.getElementById('m-start').value = '';
  document.getElementById('m-end').value = '';
}
function closeAddModal() {
  document.getElementById('add-modal').classList.remove('open');
}
document.getElementById('m-type').addEventListener('change', function() {
  document.getElementById('m-target-row').style.display = this.value === 'boost_temp' ? '' : 'none';
});

async function saveEvent() {
  const body = {
    event_type: document.getElementById('m-type').value,
    start: document.getElementById('m-start').value.trim(),
    end:   document.getElementById('m-end').value.trim(),
  };
  if (body.event_type === 'boost_temp')
    body.target_temp = parseFloat(document.getElementById('m-target').value);
  try {
    const r = await fetch('api/calendar/events', {
      method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(body)
    }).then(r=>r.json());
    if (r.ok) { closeAddModal(); loadCalendar(); }
    else alert('Failed: ' + (r.error || 'unknown'));
  } catch(e) { alert('Request failed'); }
}

// ── Init ──────────────────────────────────────────────────────────────────
loadOverview();
setInterval(loadOverview, 60000);
setInterval(loadCalendar, 120000);  // refresh calendar every 2 min
setInterval(() => fetch('api/ping').catch(()=>{}), 55000);
</script>
</body>
</html>