<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Dashboard Monitoring</title>
  <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🍈</text></svg>">
  <link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=DM+Sans:wght@400;500;600&display=swap" rel="stylesheet">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@latest/tabler-icons.min.css">
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/hammerjs@2.0.8/hammer.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@1.2.1/dist/chartjs-plugin-zoom.min.js"></script>
  <style>
    :root {
      --green-dark: #1a5c2a; --green-mid: #2d8a45; --green-light: #4caf68;
      --green-pale: #e8f5ec; --bg: #f0f4f0; --card: #ffffff;
      --text: #1a2e1e; --muted: #6b7c6e; --border: #d4e4d8;
    }
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: 'DM Sans', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; }
    nav { background: var(--green-dark); padding: 0 20px; min-height: 48px; display: flex; align-items: center; justify-content: space-between; border-bottom: 3px solid var(--green-light); flex-wrap: wrap; gap: 4px; }
    nav .brand { font-family: 'Space Mono', monospace; font-size: 15px; font-weight: 700; color: #fff; letter-spacing: 1px; white-space: nowrap; }
    nav .brand span { color: var(--green-light); }
    nav .nav-right { display: flex; gap: 12px; font-size: 12px; color: rgba(255,255,255,0.7); font-family: 'Space Mono', monospace; align-items: center; flex-wrap: wrap; }
    nav .status-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: #4ade80; margin-right: 5px; animation: blink 1.5s infinite; }
    @keyframes blink { 0%,100%{opacity:1} 50%{opacity:.3} }
    .tab-menu { background: #fff; border-bottom: 2px solid var(--border); display: flex; padding: 0 12px; overflow-x: auto; }
    .tab-menu::-webkit-scrollbar { display: none; }
    .tab-btn { padding: 10px 16px; font-size: 13px; font-weight: 600; color: var(--muted); border: none; background: none; cursor: pointer; border-bottom: 3px solid transparent; margin-bottom: -2px; transition: all .2s; font-family: 'DM Sans', sans-serif; white-space: nowrap; }
    .tab-btn:hover { color: var(--green-mid); }
    .tab-btn.active { color: var(--green-dark); border-bottom-color: var(--green-mid); }
    .tab-content { display: none; }
    .tab-content.active { display: block; }
    .container { max-width: 1100px; margin: 18px auto; padding: 0 16px; display: grid; grid-template-columns: 1fr 300px; gap: 16px; align-items: start; }
    .left-col { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
    .card { background: var(--card); border-radius: 10px; border: 1px solid var(--border); overflow: hidden; }
    .card-header { padding: 10px 14px; border-bottom: 1px solid var(--border); font-size: 12px; font-weight: 600; color: var(--muted); letter-spacing: .5px; text-transform: uppercase; background: var(--green-pale); display: flex; align-items: center; gap: 6px; }
    .main-image-wrap { position: relative; background: #111; aspect-ratio: 16/9; max-height: 340px; overflow: hidden; width: 100%; }
    .main-image-wrap img { width: 100%; height: 100%; object-fit: cover; display: block; opacity: .95; }
    .offline-overlay { display: none; position: absolute; top:0; left:0; right:0; bottom:0; background: #111; align-items: center; justify-content: center; flex-direction: column; gap: 10px; z-index: 3; }
    .offline-overlay.show { display: flex; }
    .offline-overlay svg { opacity: .3; }
    .offline-overlay span { font-family: 'Space Mono', monospace; font-size: 12px; color: rgba(255,255,255,.4); letter-spacing: 1px; }
    .img-overlay { position: absolute; bottom:0; left:0; right:0; background: linear-gradient(transparent, rgba(0,0,0,.8)); padding: 16px 12px 10px; z-index: 4; }
    .live-badge { color: #fff; font-size: 10px; font-family: 'Space Mono', monospace; font-weight: 700; padding: 2px 7px; border-radius: 4px; letter-spacing: 1px; margin-right: 8px; }
    .img-meta { font-family: 'Space Mono', monospace; font-size: 10px; color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,.5); line-height: 1.8; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
    .img-meta .val { color: var(--green-light); font-weight: 700; }
    .stream-status { position: absolute; top: 8px; right: 10px; font-size: 10px; font-family: 'Space Mono', monospace; color: #fff; background: rgba(0,0,0,0.55); padding: 2px 8px; border-radius: 4px; z-index: 5; }
    .btn-capture { display: inline-flex; align-items: center; gap: 5px; background: var(--green-mid); color: #fff; border: none; border-radius: 6px; padding: 5px 12px; font-size: 11px; font-weight: 600; cursor: pointer; font-family: 'DM Sans', sans-serif; transition: background .2s; }
    .btn-capture:hover { background: var(--green-dark); }
    .btn-capture:disabled { background: #9ca3af; cursor: not-allowed; }
    .timelapse-strip { display: flex; gap: 8px; padding: 12px 14px; overflow-x: auto; min-height: 90px; align-items: center; }
    .timelapse-strip::-webkit-scrollbar { height: 4px; }
    .timelapse-strip::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
    .right-col { display: flex; flex-direction: column; gap: 16px; }
    .gauges-row { display: flex; justify-content: space-around; padding: 14px 12px 12px; gap: 6px; }
    .gauge-wrap { display: flex; flex-direction: column; align-items: center; gap: 6px; }
    .gauge-label { font-size: 10px; color: var(--muted); font-weight: 600; text-align: center; }
    .gauge-svg { width: 80px; height: 80px; transform: rotate(-90deg); }
    .gauge-svg circle { fill: none; stroke-width: 8; stroke-linecap: round; }
    .gauge-bg { stroke: #e9ecef; }
    .gauge-val-text { font-family: 'DM Sans', sans-serif; font-size: 12px; font-weight: 700; fill: var(--text); dominant-baseline: middle; text-anchor: middle; }
    .gauge-sub { font-size: 9px; fill: var(--muted); dominant-baseline: middle; text-anchor: middle; }
    .chart-wrap { padding: 14px 14px 6px; height: 200px; }
    .split-charts { display: flex; flex-direction: column; gap: 0; padding: 10px 14px 14px; }
    .split-chart-item { display: flex; flex-direction: column; gap: 4px; padding: 12px 0; border-bottom: 1px solid var(--border); }
    .split-chart-item:last-child { border-bottom: none; }
    .split-chart-label { font-size: 11px; font-weight: 700; letter-spacing: .4px; font-family: 'Space Mono', monospace; }
    .split-chart-wrap { width: 100%; height: 110px; position: relative; }
    .chart-filter { display: flex; gap: 4px; padding: 8px 14px 0; justify-content: flex-end; }
    .chart-filter-btn { padding: 3px 10px; font-size: 11px; font-weight: 600; border-radius: 20px; border: 1px solid var(--border); background: #fff; color: var(--muted); cursor: pointer; font-family: 'Space Mono', monospace; transition: all .15s; }
    .chart-filter-btn:hover { border-color: var(--green-mid); color: var(--green-mid); }
    .chart-filter-btn.active { background: var(--green-dark); color: #fff; border-color: var(--green-dark); }
    .period-tabs { display: flex; justify-content: center; gap: 0; border-top: 1px solid var(--border); margin-top: 6px; }
    .period-tab { flex: 1; padding: 7px 0; font-size: 12px; font-weight: 700; font-family: 'Space Mono', monospace; border: none; background: none; color: var(--muted); cursor: pointer; transition: color .15s; letter-spacing: .5px; }
    .period-tab:hover { color: var(--green-dark); }
    .period-tab.active { color: var(--green-mid); border-bottom: 2px solid var(--green-mid); }
    .zoom-hint { font-size: 10px; color: var(--muted); text-align: center; padding: 4px 0 2px; font-family: 'Space Mono', monospace; }
    .btn-reset-zoom { display: block; margin: 4px auto 0; padding: 2px 12px; font-size: 10px; font-weight: 600; font-family: 'Space Mono', monospace; background: var(--green-pale); border: 1px solid var(--border); border-radius: 20px; color: var(--green-dark); cursor: pointer; transition: all .15s; }
    .btn-reset-zoom:hover { background: var(--green-mid); color: #fff; border-color: var(--green-mid); }    .chart-legend { display: flex; gap: 14px; padding: 6px 14px 12px; flex-wrap: wrap; justify-content: center; }
    .legend-item { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--muted); cursor: pointer; user-select: none; transition: opacity .2s; }
    .legend-item:hover { opacity: .7; }
    .legend-dot { width: 28px; height: 4px; border-radius: 2px; flex-shrink: 0; }
    .riwayat-chart-inner { height: 300px; padding: 14px 14px 6px; }
    .sensor-table { width: 100%; border-collapse: collapse; font-size: 12px; }
    .sensor-table td { padding: 7px 14px; border-bottom: 1px solid var(--border); }
    .sensor-table tr:last-child td { border-bottom: none; }
    .sensor-table .key { color: var(--muted); font-weight: 500; }
    .sensor-table .val-cell { font-family: 'Space Mono', monospace; font-weight: 700; text-align: right; }
    .badge-ok     { background: #dcfce7; color: #16a34a; border-radius: 4px; padding: 2px 8px; font-size: 10px; font-weight: 700; }
    .badge-warn   { background: #fef9c3; color: #ca8a04; border-radius: 4px; padding: 2px 8px; font-size: 10px; font-weight: 700; }
    .badge-danger { background: #fee2e2; color: #dc2626; border-radius: 4px; padding: 2px 8px; font-size: 10px; font-weight: 700; }
    .badge-info   { background: #dbeafe; color: #1d4ed8; border-radius: 4px; padding: 2px 8px; font-size: 10px; font-weight: 700; }
    .badge-pump-on   { background: #dcfce7; color: #14532d; border-radius: 4px; padding: 2px 8px; font-size: 10px; font-weight: 700; border: 1px solid #86efac; }
    .badge-pump-off  { background: #f3f4f6; color: #4b5563; border-radius: 4px; padding: 2px 8px; font-size: 10px; font-weight: 700; border: 1px solid #d1d5db; }
    .badge-fan-on    { background: #e0f2fe; color: #075985; border-radius: 4px; padding: 2px 8px; font-size: 10px; font-weight: 700; border: 1px solid #7dd3fc; }
    .badge-fan-off   { background: #f3f4f6; color: #4b5563; border-radius: 4px; padding: 2px 8px; font-size: 10px; font-weight: 700; border: 1px solid #d1d5db; }
    .pompa-banner { margin: 0 14px 12px; border-radius: 8px; padding: 10px 14px; display: flex; align-items: center; gap: 10px; font-size: 13px; font-weight: 700; font-family: 'Space Mono', monospace; transition: all .4s; border: 2px solid transparent; }
    .pompa-banner.on  { background: #dcfce7; border-color: #86efac; color: #14532d; }
    .pompa-banner.off { background: #f3f4f6; border-color: #d1d5db; color: #4b5563; }
    .pompa-banner .pompa-icon { font-size: 22px; line-height: 1; }
    .pompa-banner .pompa-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
    .pompa-banner.on  .pompa-dot { background: #16a34a; box-shadow: 0 0 0 0 rgba(22,163,74,.6); animation: pump-pulse 1.2s infinite; }
    .pompa-banner.off .pompa-dot { background: #9ca3af; }
    @keyframes pump-pulse { 0%{box-shadow:0 0 0 0 rgba(22,163,74,.6)} 70%{box-shadow:0 0 0 8px rgba(22,163,74,0)} 100%{box-shadow:0 0 0 0 rgba(22,163,74,0)} }
    .pompa-banner .pompa-label { flex: 1; }
    .pompa-banner .pompa-since { font-size: 10px; font-weight: 400; color: var(--muted); margin-top: 2px; }
    .pump-cam-badge { position: absolute; top: 8px; left: 10px; font-size: 11px; font-family: 'Space Mono', monospace; font-weight: 700; padding: 3px 10px; border-radius: 5px; z-index: 5; transition: all .3s; display: none; }
    .pump-cam-badge.on { display: block; background: rgba(22,163,74,0.85); color: #fff; animation: cam-badge-blink 1.5s infinite; }
    @keyframes cam-badge-blink { 0%,100%{opacity:1} 50%{opacity:.6} }
    .riwayat-wrap { max-width: 1100px; margin: 18px auto; padding: 0 16px; }
    .riwayat-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; flex-wrap: wrap; gap: 8px; }
    .riwayat-header h2 { font-size: 15px; font-weight: 700; color: var(--text); }
    .btn-hapus-toggle { background: #fee2e2; color: #dc2626; border: 1px solid #fca5a5; padding: 8px 16px; border-radius: 6px; font-size: 12px; font-weight: 600; cursor: pointer; }
    .btn-hapus-toggle:hover { background: #fecaca; }
    .btn-hapus-all { background: #dc2626; color: #fff; border: none; padding: 8px 16px; border-radius: 6px; font-size: 12px; font-weight: 600; cursor: pointer; }
    .btn-hapus-all:hover { background: #b91c1c; }
    .riwayat-section { background: #fff; border-radius: 10px; border: 1px solid var(--border); overflow: hidden; margin-bottom: 16px; }
    .riwayat-section-header { padding: 10px 14px; border-bottom: 1px solid var(--border); font-size: 12px; font-weight: 600; color: var(--muted); letter-spacing: .5px; text-transform: uppercase; background: var(--green-pale); }
    .foto-histori-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 10px; padding: 12px 14px; }
    .foto-histori-item { border-radius: 8px; overflow: hidden; border: 1px solid var(--border); cursor: pointer; transition: transform .2s, box-shadow .2s; position: relative; }
    .foto-histori-item:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,.12); }
    .foto-histori-item img { width: 100%; height: 110px; object-fit: cover; display: block; }
    .foto-histori-label { padding: 4px 8px; font-size: 10px; font-family: 'Space Mono', monospace; color: var(--muted); background: #fff; }
    .btn-hapus-foto-item { position: absolute; top: 5px; right: 5px; background: rgba(220,38,38,0.85); color: #fff; border: none; border-radius: 50%; width: 22px; height: 22px; font-size: 11px; cursor: pointer; display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity .2s; z-index: 2; line-height: 1; }
    .foto-histori-item:hover .btn-hapus-foto-item { opacity: 1; }
    .foto-skeleton { border-radius: 8px; background: #e8f5ec; height: 140px; border: 1px solid var(--border); display: flex; align-items: center; justify-content: center; animation: pulse 1.5s infinite; }
    @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.5} }
    .riwayat-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; width: 100%; }
    .riwayat-table { width: 100%; border-collapse: collapse; font-size: 12px; min-width: 600px; }
    .riwayat-table th { background: var(--green-pale); padding: 10px 14px; text-align: left; font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .5px; border-bottom: 1px solid var(--border); }
    .riwayat-table td { padding: 9px 14px; border-bottom: 1px solid var(--border); font-family: 'Space Mono', monospace; font-size: 11px; }
    .riwayat-table tr:last-child td { border-bottom: none; }
    .riwayat-table tr:hover td { background: var(--green-pale); }
    .loading-text { text-align: center; padding: 30px; color: var(--muted); font-size: 13px; }
    .foto-count-badge { background: var(--green-pale); color: var(--green-dark); border-radius: 20px; padding: 3px 12px; font-size: 11px; font-weight: 600; }
    .pump-on-row td  { background: #f0fdf4 !important; }
    .pump-off-row td { background: #fafafa !important; }
    .modal-backdrop { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 999; align-items: center; justify-content: center; }
    .modal-backdrop.show { display: flex; }
    .modal-box { background: #fff; border-radius: 12px; padding: 24px; width: 90%; max-width: 400px; }
    .modal-box h3 { font-size: 15px; font-weight: 700; color: #1a2e1e; margin-bottom: 6px; }
    .modal-box p { font-size: 12px; color: var(--muted); margin-bottom: 18px; }
    .modal-box label { font-size: 11px; color: var(--muted); display: block; margin-bottom: 4px; }
    .modal-box input[type=date] { width: 100%; padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 13px; font-family: 'DM Sans', sans-serif; margin-bottom: 12px; }
    .modal-footer { display: flex; gap: 8px; justify-content: flex-end; margin-top: 8px; flex-wrap: wrap; }
    .btn-batal { background: none; border: 1px solid var(--border); padding: 8px 16px; border-radius: 6px; font-size: 12px; cursor: pointer; color: var(--muted); }
    .btn-danger { background: #dc2626; color: #fff; border: none; padding: 8px 16px; border-radius: 6px; font-size: 12px; font-weight: 600; cursor: pointer; }
    .btn-danger:hover { background: #b91c1c; }
    .divider { border: none; border-top: 1px solid var(--border); margin: 14px 0; }
    #modal-foto-viewer .modal-inner { background: #111; border-radius: 12px; padding: 16px; width: 420px; max-width: 92vw; display: flex; flex-direction: column; align-items: center; gap: 10px; position: relative; }
    #modal-foto-viewer img { width: 100%; max-width: 388px; max-height: 300px; border-radius: 8px; object-fit: contain; }
    #modal-foto-viewer .close-btn { position: absolute; top: 10px; right: 12px; background: rgba(255,255,255,0.15); border: none; color: #fff; font-size: 18px; cursor: pointer; border-radius: 50%; width: 30px; height: 30px; line-height: 30px; text-align: center; }
    #modal-foto-viewer .foto-waktu { color: rgba(255,255,255,0.7); font-family: 'Space Mono', monospace; font-size: 11px; margin: 0; }
    #modal-foto-viewer .btn-download { background: var(--green-mid); color: #fff; padding: 6px 16px; border-radius: 6px; font-size: 12px; font-weight: 600; border: none; cursor: pointer; }
    #modal-foto-viewer .btn-download:hover { background: var(--green-dark); }
    #modal-foto-viewer .btn-hapus-satu { background: #dc2626; color: #fff; padding: 6px 16px; border-radius: 6px; font-size: 12px; font-weight: 600; border: none; cursor: pointer; }
    #modal-foto-viewer .btn-hapus-satu:hover { background: #b91c1c; }
    #toast { position: fixed; bottom: 20px; right: 20px; z-index: 9999; background: #1a2e1e; color: #fff; padding: 10px 18px; border-radius: 8px; font-size: 13px; font-family: 'DM Sans', sans-serif; box-shadow: 0 4px 12px rgba(0,0,0,0.3); opacity: 0; transition: opacity 0.3s; pointer-events: none; max-width: 320px; }
    #toast.show { opacity: 1; pointer-events: auto; }
    @media (max-width: 900px) {
      .container { grid-template-columns: 1fr; }
      .right-col { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
    }
    @media (max-width: 640px) {
      nav { padding: 6px 10px; height: auto; flex-wrap: wrap; gap: 4px; }
      nav .brand { font-size: 13px; }
      nav .nav-right { font-size: 10px; gap: 6px; flex-wrap: wrap; }
      nav .nav-right > span:first-child { order: -1; }
      .cam-id { display: none; }
      #clock { display: none; }
      .tab-btn { padding: 8px 10px; font-size: 11px; }
      .container { padding: 0 10px; margin: 12px auto; gap: 12px; }
      .main-image-wrap { max-height: 220px; }
      .img-meta { font-size: 9px; gap: 4px; }
      .live-badge { font-size: 9px; padding: 1px 5px; }
      .stream-status { font-size: 9px; padding: 2px 6px; }
      .right-col { display: flex; flex-direction: column; gap: 12px; }
      .gauges-row { padding: 12px 8px 10px; gap: 2px; }
      .gauge-svg { width: 72px; height: 72px; }
      .gauge-label { font-size: 9px; }
      .sensor-table td { padding: 6px 10px; font-size: 11px; }
      .timelapse-strip { padding: 10px; gap: 6px; }
      .riwayat-wrap { padding: 0 10px; margin: 12px auto; }
      .riwayat-header { flex-direction: column; align-items: flex-start; gap: 6px; }
      .riwayat-header h2 { font-size: 13px; }
      .btn-hapus-toggle, .btn-hapus-all { padding: 6px 12px; font-size: 11px; }
      .chart-wrap { height: 160px; padding: 10px 10px 4px; }
      .riwayat-chart-inner { height: 190px; padding: 10px 10px 4px; }
      .chart-legend { gap: 10px; padding: 4px 10px 10px; justify-content: center; }
      .legend-dot { width: 20px; }
      .legend-item { font-size: 10px; }
      .split-chart-wrap { height: 75px; }
      .split-charts { padding: 8px 10px 10px; }
      .riwayat-table th, .riwayat-table td { padding: 7px 8px; font-size: 10px; white-space: nowrap; }
      .foto-histori-grid { grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); gap: 8px; padding: 10px; }
      .foto-histori-item img { height: 90px; }
      .foto-histori-label { font-size: 9px; padding: 3px 6px; }
      .modal-box { padding: 18px 16px; }
      .modal-footer { flex-direction: column; }
      .modal-footer button { width: 100%; }
    }
    @media (max-width: 400px) {
      .gauges-row { justify-content: space-between; }
      .gauge-svg { width: 62px; height: 62px; }
      .tab-btn { padding: 7px 8px; font-size: 10px; }
      nav .nav-right > span:nth-child(3) { display: none; }
    }
    #suhu-alert-modal .modal-box { border: 2px solid #dc2626; box-shadow: 0 0 0 4px rgba(220,38,38,0.12); }
    #suhu-alert-modal h3 { display: flex; align-items: center; gap: 8px; }
    .suhu-alert-icon-wrap { display: flex; justify-content: center; margin-bottom: 10px; }
    .suhu-alert-icon-wrap span { font-size: 40px; animation: suhu-shake 0.6s ease-in-out infinite; display: inline-block; }
    @keyframes suhu-shake { 0%,100%{transform:rotate(-6deg)} 50%{transform:rotate(6deg)} }
  </style>
</head>
<body>

<div id="hapus-modal" class="modal-backdrop">
  <div class="modal-box">
    <h3>🗑️ Hapus Data Sensor</h3>
    <p>Pilih rentang tanggal yang ingin dihapus, atau hapus semua data sekaligus.</p>
    <label>Dari Tanggal</label><input type="date" id="hapus-dari">
    <label>Sampai Tanggal</label><input type="date" id="hapus-sampai">
    <div class="modal-footer">
      <button class="btn-batal" onclick="closeModal('hapus-modal')">Batal</button>
      <button class="btn-danger" onclick="hapusData()">Hapus Rentang</button>
    </div>
    <hr class="divider">
    <p style="margin-bottom:0;font-size:11px;color:#dc2626;">⚠️ Hapus semua data riwayat sensor secara permanen.</p>
    <div class="modal-footer" style="margin-top:10px;">
      <button class="btn-danger" onclick="hapusSemua()" style="background:#7f1d1d;">🗑️ Hapus Semua Data</button>
    </div>
  </div>
</div>

<div id="hapus-foto-modal" class="modal-backdrop">
  <div class="modal-box">
    <h3>🗑️ Hapus Riwayat Foto</h3>
    <p style="color:#dc2626;font-size:12px;margin-bottom:20px;">⚠️ Semua riwayat foto akan dihapus secara permanen. TIDAK BISA DIKEMBALIKAN.</p>
    <div class="modal-footer">
      <button class="btn-batal" onclick="closeModal('hapus-foto-modal')">Batal</button>
      <button class="btn-danger" onclick="hapusFotoSemua()">Ya, Hapus Semua Foto</button>
    </div>
  </div>
</div>

<div id="modal-foto-viewer" class="modal-backdrop" onclick="if(event.target===this) tutupModalFoto()">
  <div class="modal-inner">
    <button class="close-btn" onclick="tutupModalFoto()">✕</button>
    <img id="modal-foto-img" src="" alt="foto tanaman">
    <p id="modal-foto-waktu" class="foto-waktu"></p>
    <div style="display:flex;gap:8px;margin-top:2px;">
      <button class="btn-download" onclick="downloadFoto()">⬇️ Download</button>
      <button class="btn-hapus-satu" onclick="hapusFotoSatu()">🗑️ Hapus</button>
    </div>
  </div>
</div>

<div id="suhu-alert-modal" class="modal-backdrop">
  <div class="modal-box">
    <div class="suhu-alert-icon-wrap"><span>🔥</span></div>
    <h3 style="color:#dc2626;justify-content:center;">⚠️ Peringatan Suhu Tinggi!</h3>
    <p id="suhu-alert-text" style="color:#1a2e1e;font-size:13px;line-height:1.7;text-align:center;">
      Suhu telah mencapai batas maksimal.
    </p>
    <div class="modal-footer" style="justify-content:center;">
      <button class="btn-batal" onclick="closeModal('suhu-alert-modal')">Tutup</button>
      <button class="btn-danger" style="background:#0369a1;" onclick="toggleFanRelay();closeModal('suhu-alert-modal')">🌀 Nyalakan Fan</button>
    </div>
  </div>
</div>

<div id="toast"></div>

<nav>
  <div class="brand"><span>HIDROGENZ</span>&nbsp;FARM</div>
  <div class="nav-right">
    <span><span class="status-dot" id="nav-dot"></span><span id="nav-live-text">CONNECTING</span></span>
    <span class="cam-id">CAM_ID: MELON01</span>
    <span id="clock">--:--:--</span>
    <span style="border-left:1px solid rgba(255,255,255,0.3);padding-left:12px;">
      Halo, {{ auth()->user()->name ?? 'Guest' }}
    </span>
    <form method="POST" action="{{ route('logout') }}" style="display:inline;">
      @csrf
      <button type="submit" style="background:none;border:none;color:rgba(255,255,255,0.7);cursor:pointer;font-family:'Space Mono',monospace;font-size:12px;text-decoration:underline;padding:0;">Logout</button>
    </form>
  </div>
</nav>

<div class="tab-menu">
  <button class="tab-btn active" onclick="switchTab(this,'dashboard')">📊 Dashboard</button>
  <button class="tab-btn" onclick="switchTab(this,'riwayat')">📋 Riwayat Data</button>
  <button class="tab-btn" onclick="switchTab(this,'foto')">📸 Riwayat Foto</button>
</div>

<div id="tab-dashboard" class="tab-content active">
  <div class="container">
  <div class="left-col">
      <div class="card">
        <div class="card-header" style="justify-content:space-between;">
          <span>📹 Live Camera</span>
          <button class="btn-capture" id="btn-ambil-foto" onclick="ambilFotoManual()">
            <i class="ti ti-camera"></i> Ambil Foto
          </button>
        </div>
        <div class="main-image-wrap">
          <img id="mainImg" alt="Live Stream" style="width:100%;height:100%;object-fit:cover;display:block;">
          <div class="offline-overlay" id="offline-overlay">
            <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="1.5">
              <path d="M15.6 11.6L22 7v10l-6.4-4.5v-1zM4 5h9a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2z"/>
              <line x1="2" y1="2" x2="22" y2="22" stroke="#ef4444" stroke-width="2"/>
            </svg>
            <span>STREAM OFFLINE</span>
          </div>
          <span class="stream-status" id="stream-status">● CONNECTING...</span>
          <span class="pump-cam-badge" id="pump-cam-badge">💧 POMPA ON</span>
          <div class="img-overlay">
            <div class="img-meta">
              <span class="live-badge" id="live-badge" style="background:#f59e0b;">CONNECTING</span>
              <span>TEMP: <span class="val" id="overlay-suhu">{{ $latest->suhu ?? '-' }}°C</span></span>
              <span>HUM: <span class="val" id="overlay-kelembapan">{{ $latest->kelembapan ?? '-' }}%</span></span>
              <span>SOIL: <span class="val" id="overlay-soil">{{ $latest->soil ?? '-' }}%</span></span>
              <span>DATE: <span class="val" id="overlay-date">{{ $latest->created_at ?? 'n/a' }}</span></span>
            </div>
          </div>
        </div>
      </div>
      <div class="card">
        <div class="card-header">⏱ Time-lapse Progress</div>
        <div class="timelapse-strip" id="timelapse-strip">
          <p style="color:var(--muted);font-size:12px;padding:10px;">Memuat foto...</p>
        </div>
      </div>
    </div>
    <div class="right-col">
      <div class="card">
        <div class="card-header">🌡 Sensor Status</div>
        <div class="gauges-row">
          @php
            $suhu=$latest->suhu??0; $kelembapan=$latest->kelembapan??0; $soil=$latest->soil??0;
            $circumf=201;
            $offsetSuhu=round($circumf*(1-min($suhu,100)/100));
            $offsetHum=round($circumf*(1-min($kelembapan,100)/100));
            $offsetSoil=round($circumf*(1-min($soil,100)/100));
          @endphp
          <div class="gauge-wrap">
            <svg class="gauge-svg" viewBox="0 0 80 80">
              <circle class="gauge-bg" cx="40" cy="40" r="32" stroke-dasharray="{{ $circumf }}" stroke-dashoffset="0"/>
              <circle id="gauge-suhu-circle" cx="40" cy="40" r="32" fill="none" stroke-width="8" stroke-linecap="round" stroke="#e53e3e" stroke-dasharray="{{ $circumf }}" stroke-dashoffset="{{ $offsetSuhu }}"/>
              <g transform="rotate(90,40,40)"><text id="gauge-suhu-text" class="gauge-val-text" x="40" y="37">{{ $suhu }}°</text><text class="gauge-sub" x="40" y="51">Temp</text></g>
            </svg>
            <div class="gauge-label">Temperature</div>
          </div>
          <div class="gauge-wrap">
            <svg class="gauge-svg" viewBox="0 0 80 80">
              <circle class="gauge-bg" cx="40" cy="40" r="32" stroke-dasharray="{{ $circumf }}" stroke-dashoffset="0"/>
              <circle id="gauge-hum-circle" cx="40" cy="40" r="32" fill="none" stroke-width="8" stroke-linecap="round" stroke="#00b5a3" stroke-dasharray="{{ $circumf }}" stroke-dashoffset="{{ $offsetHum }}"/>
              <g transform="rotate(90,40,40)"><text id="gauge-hum-text" class="gauge-val-text" x="40" y="37">{{ $kelembapan }}%</text><text class="gauge-sub" x="40" y="51">Humid</text></g>
            </svg>
            <div class="gauge-label">Humidity</div>
          </div>
          <div class="gauge-wrap">
            <svg class="gauge-svg" viewBox="0 0 80 80">
              <circle class="gauge-bg" cx="40" cy="40" r="32" stroke-dasharray="{{ $circumf }}" stroke-dashoffset="0"/>
              <circle id="gauge-soil-circle" cx="40" cy="40" r="32" fill="none" stroke-width="8" stroke-linecap="round" stroke="#2563eb" stroke-dasharray="{{ $circumf }}" stroke-dashoffset="{{ $offsetSoil }}"/>
              <g transform="rotate(90,40,40)"><text id="gauge-soil-text" class="gauge-val-text" x="40" y="37">{{ $soil }}%</text><text class="gauge-sub" x="40" y="51">Soil</text></g>
            </svg>
            <div class="gauge-label">Soil Moisture</div>
          </div>
        </div>
        <table class="sensor-table">
          <tr><td class="key">Suhu</td><td class="val-cell" id="val-suhu">{{ $suhu }} °C</td><td><span id="badge-suhu" class="badge-ok">NORMAL</span></td></tr>
          <tr><td class="key">Kelembapan</td><td class="val-cell" id="val-kelembapan">{{ $kelembapan }} %</td><td><span id="badge-kelembapan" class="badge-ok">NORMAL</span></td></tr>
          <tr><td class="key">Soil Moisture</td><td class="val-cell" id="val-soil">{{ $soil }} %</td><td><span id="badge-soil" class="badge-ok">IDEAL</span></td></tr>
          <tr>
            <td class="key">Status Pompa</td>
            <td class="val-cell" id="val-pompa">-</td>
            <td><span id="badge-pompa" onclick="togglePompaRelay()" style="cursor:pointer;" title="Klik untuk toggle ON/OFF manual">-</span></td>
          </tr>
          <tr>
            <td class="key">Status Fan</td>
            <td class="val-cell" id="val-fan">-</td>
            <td><span id="badge-fan" onclick="toggleFanRelay()" style="cursor:pointer;" title="Klik untuk toggle ON/OFF manual">-</span></td>
          </tr>
        </table>
      </div>
      <div class="card">
        <div class="card-header">
          <span>📈 Time-series Chart</span>
        </div>
        <div class="split-charts">
          <div class="split-chart-item">
            <div class="split-chart-label" style="color:#e53e3e;">🌡 Suhu (°C)</div>
            <div class="split-chart-wrap"><canvas id="chartSuhu"></canvas></div>
            <div class="zoom-hint">🔍 Scroll/pinch zoom · drag geser</div>
            <div class="period-tabs">
              <button class="period-tab" onclick="filterSplitChart('hari',this)">1D</button>
              <button class="period-tab" onclick="filterSplitChart('minggu',this)">1W</button>
              <button class="period-tab" onclick="filterSplitChart('bulan',this)">1M</button>
              <button class="period-tab active" onclick="filterSplitChart('semua',this)">ALL</button>
              <button class="period-tab" onclick="resetZoomAll()">↺</button>
            </div>
          </div>
          <div class="split-chart-item">
            <div class="split-chart-label" style="color:#00b5a3;">💧 Kelembapan (%)</div>
            <div class="split-chart-wrap"><canvas id="chartKelembapan"></canvas></div>
            <div class="zoom-hint">🔍 Scroll/pinch zoom · drag geser</div>
            <div class="period-tabs">
              <button class="period-tab" onclick="filterSplitChart('hari',this)">1D</button>
              <button class="period-tab" onclick="filterSplitChart('minggu',this)">1W</button>
              <button class="period-tab" onclick="filterSplitChart('bulan',this)">1M</button>
              <button class="period-tab active" onclick="filterSplitChart('semua',this)">ALL</button>
              <button class="period-tab" onclick="resetZoomAll()">↺</button>
            </div>
          </div>
          <div class="split-chart-item">
            <div class="split-chart-label" style="color:#2563eb;">🌱 Soil Moisture (%)</div>
            <div class="split-chart-wrap"><canvas id="chartSoil" height="90"></canvas></div>
            <div class="zoom-hint">🔍 Scroll/pinch zoom · drag geser</div>
            <div class="period-tabs">
              <button class="period-tab" onclick="filterSplitChart('hari',this)">1D</button>
              <button class="period-tab" onclick="filterSplitChart('minggu',this)">1W</button>
              <button class="period-tab" onclick="filterSplitChart('bulan',this)">1M</button>
              <button class="period-tab active" onclick="filterSplitChart('semua',this)">ALL</button>
              <button class="period-tab" onclick="resetZoomAll()">↺</button>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

<div id="tab-riwayat" class="tab-content">
  <div class="riwayat-wrap">
    <div class="riwayat-header">
      <h2>📋 Riwayat Data Sensor</h2>
      <button class="btn-hapus-toggle" onclick="openModal('hapus-modal')">🗑️ Hapus Data</button>
    </div>
    <div class="riwayat-section">
      <div class="riwayat-section-header" style="display:flex;justify-content:space-between;align-items:center;">
        <span>📈 Grafik Riwayat Sensor</span>
        <div class="chart-filter" style="padding:0;">
          <button class="chart-filter-btn active" onclick="filterRiwayatChart('hari',this)">Hari</button>
          <button class="chart-filter-btn" onclick="filterRiwayatChart('minggu',this)">Minggu</button>
          <button class="chart-filter-btn" onclick="filterRiwayatChart('bulan',this)">Bulan</button>
        </div>
      </div>
      <div class="riwayat-chart-inner"><canvas id="riwayatChart"></canvas></div>
    </div>
    <div class="riwayat-section">
      <div class="riwayat-section-header">📊 Tabel Data Sensor, Status Pompa &amp; Fan</div>
      <div class="riwayat-table-wrap">
        <table class="riwayat-table">
          <thead>
            <tr>
              <th>No</th><th>Waktu</th><th>Suhu (°C)</th><th>Kelembapan (%)</th>
              <th>Soil (%)</th><th>St. Suhu</th><th>St. Kelembapan</th><th>St. Soil</th><th>Pompa</th><th>Fan</th>
            </tr>
          </thead>
          <tbody id="riwayat-tbody"><tr><td colspan="10" class="loading-text">Memuat data...</td></tr></tbody>
        </table>
      </div>
    </div>
  </div>
</div>

<div id="tab-foto" class="tab-content">
  <div class="riwayat-wrap">
    <div class="riwayat-header">
      <h2>📸 Riwayat Foto</h2>
      <div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
        <span class="foto-count-badge" id="foto-count">0 foto</span>
        <button class="btn-hapus-all" onclick="openModal('hapus-foto-modal')">🗑️ Hapus Semua Foto</button>
      </div>
    </div>
    <div class="riwayat-section">
      <div class="riwayat-section-header">Histori Foto Tanaman</div>
      <div class="foto-histori-grid" id="foto-grid">
        <p style="color:var(--muted);font-size:13px;padding:10px;">Memuat foto...</p>
      </div>
    </div>
  </div>
</div>

<script>
  const ESP32_IP      = '192.168.110.246';
  const ESP32_STREAM  = 'http://' + ESP32_IP;
  const ESP32_CAPTURE = 'http://' + ESP32_IP + ':81/capture';

  // ── Ambang batas soil moisture (pompa) ──
  // soil <= 40  -> pompa ON
  // soil >= 60  -> pompa OFF
  // 40 < soil < 60 -> pertahankan status terakhir (hysteresis)
  const SOIL_MULAI_SIRAM = 40;
  const SOIL_STOP_SIRAM  = 60;

  function tick() { document.getElementById('clock').textContent = new Date().toLocaleTimeString('id-ID'); }
  tick(); setInterval(tick, 1000);
  function openModal(id)  { document.getElementById(id).classList.add('show'); }
  function closeModal(id) { document.getElementById(id).classList.remove('show'); }

  let _toastTimer = null;
  function showToast(msg, durasi=3000) {
    const t = document.getElementById('toast');
    t.textContent = msg;
    t.className = 'show';
    if(_toastTimer) clearTimeout(_toastTimer);
    _toastTimer = setTimeout(() => t.classList.remove('show'), durasi);
  }
  function switchTab(btn, tab) {
    document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active'));
    document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
    document.getElementById('tab-'+tab).classList.add('active');
    btn.classList.add('active');
  }

  function getBadgeSuhu(v) {
    v = parseFloat(v);
    if (isNaN(v)) return '<span class="badge-warn">-</span>';
    if (v < 25)   return '<span class="badge-warn">DINGIN</span>';
    if (v <= 32)  return '<span class="badge-ok">NORMAL</span>';
    return '<span class="badge-danger">PANAS</span>';
  }
  function getBadgeKelembapan(v) {
    v = parseFloat(v);
    if (isNaN(v)) return '<span class="badge-warn">-</span>';
    if (v < 60)   return '<span class="badge-warn">RENDAH</span>';
    if (v <= 80)  return '<span class="badge-ok">NORMAL</span>';
    return '<span class="badge-warn">TINGGI</span>';
  }
  function getBadgeSoil(v) {
    v = parseFloat(v);
    if (isNaN(v))              return '<span class="badge-warn">-</span>';
    if (v <= SOIL_MULAI_SIRAM) return '<span class="badge-danger">KERING</span>';
    if (v < SOIL_STOP_SIRAM)   return '<span class="badge-ok">IDEAL</span>';
    return '<span class="badge-info">BASAH</span>';
  }
  function getBadgePompa(pompa) {
    if (pompa === null || pompa === undefined) return '<span class="badge-pump-off">⭕ OFF</span>';
    const p = pompa.toString().toUpperCase().trim();
    if (p === '1' || p === 'ON' || p === 'TRUE' || p.includes('ON')) return '<span class="badge-pump-on">💧 ON</span>';
    return '<span class="badge-pump-off">⭕ OFF</span>';
  }
  function getBadgeFan(fan) {
    if (fan === null || fan === undefined) return '<span class="badge-fan-off">⭕ OFF</span>';
    const f = fan.toString().toUpperCase().trim();
    if (f === '1' || f === 'ON' || f === 'TRUE' || f.includes('ON')) return '<span class="badge-fan-on">🌀 ON</span>';
    return '<span class="badge-fan-off">⭕ OFF</span>';
  }

  const mainImg=document.getElementById('mainImg'),
        offlineOverlay=document.getElementById('offline-overlay'),
        streamStatus=document.getElementById('stream-status'),
        liveBadge=document.getElementById('live-badge'),
        navDot=document.getElementById('nav-dot'),
        navText=document.getElementById('nav-live-text');
  let streamReconnectTimer=null, streamLoadTimeout=null;

  function setStreamState(state) {
    if (state==='live') {
      offlineOverlay.classList.remove('show'); streamStatus.textContent='● LIVE'; streamStatus.style.color='#4ade80';
      liveBadge.textContent='LIVE'; liveBadge.style.background='#ef4444'; navDot.style.background='#4ade80'; navText.textContent='LIVE';
    } else if (state==='offline') {
      offlineOverlay.classList.add('show'); streamStatus.textContent='● OFFLINE'; streamStatus.style.color='#ef4444';
      liveBadge.textContent='OFFLINE'; liveBadge.style.background='#6b7280'; navDot.style.background='#ef4444'; navText.textContent='OFFLINE';
    } else {
      offlineOverlay.classList.remove('show'); streamStatus.textContent='● CONNECTING...'; streamStatus.style.color='#fff';
      liveBadge.textContent='CONNECTING'; liveBadge.style.background='#f59e0b'; navDot.style.background='#f59e0b'; navText.textContent='CONNECTING';
    }
  }
  function startStream() {
    if (streamReconnectTimer) { clearTimeout(streamReconnectTimer); streamReconnectTimer=null; }
    if (streamLoadTimeout)    { clearTimeout(streamLoadTimeout);    streamLoadTimeout=null; }
    setStreamState('connecting');
    mainImg.src = ESP32_STREAM+'/';
    streamLoadTimeout = setTimeout(() => {
      if (navText.textContent!=='LIVE') { setStreamState('offline'); streamReconnectTimer=setTimeout(startStream,5000); }
    }, 10000);
  }
  mainImg.onload  = () => { setStreamState('live');    if (streamLoadTimeout){clearTimeout(streamLoadTimeout);streamLoadTimeout=null;} };
  mainImg.onerror = () => { setStreamState('offline'); if (streamLoadTimeout){clearTimeout(streamLoadTimeout);streamLoadTimeout=null;} streamReconnectTimer=setTimeout(startStream,5000); };
  function setMain(src) { mainImg.src=src; offlineOverlay.classList.remove('show'); setTimeout(()=>startStream(),10000); }
  startStream();

  async function ambilFotoManual() {
    const btn = document.getElementById('btn-ambil-foto');
    if (!btn || btn.disabled) return;
    const originalHtml = btn.innerHTML;
    btn.disabled = true;
    btn.innerHTML = '<i class="ti ti-loader-2"></i> Mengambil...';
    try {
      const res = await fetch(ESP32_CAPTURE, { cache: 'no-store' });
      if (!res.ok) throw new Error('Kamera tidak merespon (HTTP '+res.status+')');
      const blob = await res.blob();
      if (!blob || blob.size < 100) throw new Error('Data gambar kosong/rusak');

      const b64Raw = await new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload  = () => resolve(reader.result.split(',')[1]);
        reader.onerror = () => reject(new Error('Gagal membaca gambar'));
        reader.readAsDataURL(blob);
      });

      const now = new Date();
      const pad = x => String(x).padStart(2,'0');
      const waktu = now.getFullYear()+'-'+pad(now.getMonth()+1)+'-'+pad(now.getDate())+' '+pad(now.getHours())+':'+pad(now.getMinutes())+':'+pad(now.getSeconds());

      const suhuTxt = (document.getElementById('overlay-suhu')?.textContent || '').replace('°C','').trim();
      const humTxt  = (document.getElementById('overlay-kelembapan')?.textContent || '').replace('%','').trim();
      const soilTxt = (document.getElementById('overlay-soil')?.textContent || '').replace('%','').trim();
      const suhu       = (suhuTxt && suhuTxt !== '-') ? suhuTxt : null;
      const kelembapan = (humTxt  && humTxt  !== '-') ? humTxt  : null;
      const soil        = (soilTxt && soilTxt !== '-') ? soilTxt : null;

      const b64wm = await tambahWatermarkAsync(b64Raw, waktu, suhu, kelembapan, soil);

      setMain('data:image/jpeg;base64,'+b64wm);

      if (typeof window._simpanFotoCallback === 'function') {
        await window._simpanFotoCallback({
          image: b64wm,
          timestamp: now.getTime(),
          waktu,
          suhu, kelembapan, soil
        });
      } else {
        showToast('✅ Foto diambil (belum tersambung ke penyimpanan)');
      }
    } catch (e) {
      showToast('❌ Gagal ambil foto: '+e.message, 4000);
    } finally {
      btn.disabled = false;
      btn.innerHTML = originalHtml;
    }
  }

  function gambarWatermark(ctx, W, H, tglStr, senStr) {
    const fs=Math.max(11,Math.round(W*0.022)), lh=Math.round(fs*1.6), px=Math.round(fs*0.6), py=Math.round(fs*0.5);
    ctx.font='bold '+fs+'px "Courier New",Courier,monospace';
    const bw=Math.max(ctx.measureText(tglStr).width,ctx.measureText(senStr).width)+px*2, bh=lh*2+py*2;
    ctx.fillStyle='rgba(0,0,0,0.72)'; ctx.fillRect(0,H-bh,bw,bh);
    ctx.font='bold '+fs+'px "Courier New",Courier,monospace'; ctx.textBaseline='top';
    ctx.fillStyle='#00ff41'; ctx.fillText(tglStr,px,H-bh+py);
    let cx=px, y2=H-bh+py+lh;
    (senStr.match(/(T:[^\s]+|H:[^\s]+|S:[^\s]+)/g)||[]).forEach((seg,i,arr)=>{
      const sp=i<arr.length-1?' ':'';
      if (seg.startsWith('T:'))      { ctx.fillStyle='#fff'; ctx.fillText('T:',cx,y2); cx+=ctx.measureText('T:').width; ctx.fillStyle='#ff4d4d'; const v=seg.slice(2)+sp; ctx.fillText(v,cx,y2); cx+=ctx.measureText(v).width; }
      else if (seg.startsWith('H:')) { ctx.fillStyle='#fff'; ctx.fillText('H:',cx,y2); cx+=ctx.measureText('H:').width; ctx.fillStyle='#00ffff'; const v=seg.slice(2)+sp; ctx.fillText(v,cx,y2); cx+=ctx.measureText(v).width; }
      else if (seg.startsWith('S:')) { ctx.fillStyle='#fff'; ctx.fillText('S:',cx,y2); cx+=ctx.measureText('S:').width; ctx.fillStyle='#fff'; ctx.fillText(seg.slice(2),cx,y2); }
    });
  }

  function tambahWatermarkAsync(b64, waktuStr, suhu, kelembapan, soil) {
    return new Promise(resolve => {
      try {
        const img = new Image(), timer = setTimeout(() => resolve(b64), 8000);
        img.onload = () => {
          clearTimeout(timer);
          try {
            const TARGET_W=640, oriW=img.naturalWidth||320, oriH=img.naturalHeight||240;
            const scale=TARGET_W/oriW, cv=document.createElement('canvas');
            cv.width=TARGET_W; cv.height=Math.round(oriH*scale);
            const ctx=cv.getContext('2d');
            ctx.drawImage(img,0,0,cv.width,cv.height);
            const tgl=(waktuStr&&waktuStr.length>=19)?waktuStr.substring(0,19):(()=>{
              const n=new Date(),p=x=>String(x).padStart(2,'0');
              return n.getFullYear()+'-'+p(n.getMonth()+1)+'-'+p(n.getDate())+' '+p(n.getHours())+':'+p(n.getMinutes())+':'+p(n.getSeconds());
            })();
            const s  = (suhu       != null && String(suhu)       !== 'null') ? String(suhu)       : '-';
            const h  = (kelembapan != null && String(kelembapan) !== 'null') ? String(kelembapan) : '-';
            const so = (soil       != null && String(soil)       !== 'null') ? String(soil)       : '-';
            gambarWatermark(ctx,cv.width,cv.height,tgl,'T:'+s+'C H:'+h+'% S:'+so+'%');
            resolve(cv.toDataURL('image/jpeg',0.88).split(',')[1]);
          } catch(e) { resolve(b64); }
        };
        img.onerror=()=>{ clearTimeout(timer); resolve(b64); };
        img.src='data:image/jpeg;base64,'+b64;
      } catch(e) { resolve(b64); }
    });
  }

  const timeseries = @json($timeseries);

  window.sharedChartOptions = {
    responsive: true,
    maintainAspectRatio: false,
    animation: { duration: 700, easing: 'easeInOutQuart' },
    interaction: { mode: 'index', intersect: false },
    plugins: {
      legend: { display: false },
      tooltip: {
        backgroundColor: 'rgba(26,44,30,0.92)', titleColor: '#a3c4a8', bodyColor: '#fff',
        padding: 10, cornerRadius: 8,
        titleFont: { family: "'Space Mono', monospace", size: 10 },
        bodyFont:  { family: "'DM Sans', sans-serif", size: 12 },
        callbacks: {
          title: items => '⏱ ' + items[0].label,
          label: ctx => {
            const u = {'Suhu':'°C','Kelembapan':'%','Soil Moisture':'%'};
            const ic= {'Suhu':'🌡','Kelembapan':'💧','Soil Moisture':'🌱'};
            const n = ctx.dataset.label;
            return '  '+(ic[n]||'')+' '+n+': '+ctx.parsed.y+(u[n]||'');
          }
        }
      }
    },
    scales: {
      x: { ticks:{display:false}, grid:{display:false}, border:{display:false} },
      y: { grid:{color:'#e8f5ec'}, ticks:{font:{size:9},color:'#6b7c6e',padding:4}, border:{display:false} }
    }
  };

  function filterByPeriod(arr, period) {
    const now = new Date();
    return arr.filter(t => {
      if (!t.t) return false;
      const d = new Date(t.t);
      if (isNaN(d)) return false;
      if (period === 'hari')   return d.toDateString() === now.toDateString();
      if (period === 'minggu') return (now - d) / 86400000 <= 7;
      if (period === 'bulan')  return d.getMonth() === now.getMonth() && d.getFullYear() === now.getFullYear();
      return true;
    });
  }

  // ── Agregasi: 'hari'=per jam, 'minggu'/'bulan'=per tanggal (dd/mm), 'semua'=per tanggal lengkap (dd/mm/yy) ──
  function agregasi(arr, period) {
    if (!arr.length) return { labels:[], suhu:[], kelembapan:[], soil:[] };
    const groups = {};
    arr.forEach(t => {
      const d = new Date(t.t);
      let key;
      if (period === 'hari')   key = d.getHours().toString().padStart(2,'0') + ':00';
      if (period === 'minggu') key = d.toLocaleDateString('id-ID',{weekday:'short',day:'2-digit',month:'2-digit'});
      if (period === 'bulan')  key = d.toLocaleDateString('id-ID',{day:'2-digit',month:'2-digit'});
      if (period === 'semua')  key = d.toLocaleDateString('id-ID',{day:'2-digit',month:'2-digit',year:'2-digit'});
      if (!groups[key]) groups[key] = {suhu:[],kelembapan:[],soil:[]};
      if (t.suhu != null)       groups[key].suhu.push(parseFloat(t.suhu));
      if (t.kelembapan != null) groups[key].kelembapan.push(parseFloat(t.kelembapan));
      if (t.soil != null)       groups[key].soil.push(parseFloat(t.soil));
    });
    const avg = a => a.length ? +(a.reduce((x,y)=>x+y,0)/a.length).toFixed(1) : null;
    const keys = Object.keys(groups);
    return { labels:keys, suhu:keys.map(k=>avg(groups[k].suhu)), kelembapan:keys.map(k=>avg(groups[k].kelembapan)), soil:keys.map(k=>avg(groups[k].soil)) };
  }

  function buatSplitChart(canvasId, label, initLabels, initData, borderColor, bgColor) {
    return new Chart(document.getElementById(canvasId).getContext('2d'), {
      type: 'line',
      data: {
        labels: initLabels,
        datasets: [{ label, data: initData, borderColor, backgroundColor: bgColor,
          tension:0.4, pointRadius:1, pointHoverRadius:4, fill:true, borderWidth:1.5 }]
      },
      options: {
        responsive: true, maintainAspectRatio: true,
        animation: { duration:700, easing:'easeInOutQuart' },
        interaction: { mode:'index', intersect:false },
        plugins: {
          legend: { display:false },
          tooltip: {
            backgroundColor:'rgba(26,44,30,0.92)', titleColor:'#a3c4a8', bodyColor:'#fff',
            padding:10, cornerRadius:8,
            titleFont:{ family:"'Space Mono', monospace", size:10 },
            bodyFont: { family:"'DM Sans', sans-serif", size:12 },
            callbacks: {
              title: items => '⏱ ' + items[0].label,
              label: ctx => {
                const u={'Suhu':'°C','Kelembapan':'%','Soil Moisture':'%'};
                const ic={'Suhu':'🌡','Kelembapan':'💧','Soil Moisture':'🌱'};
                const n=ctx.dataset.label;
                return '  '+(ic[n]||'')+' '+n+': '+ctx.parsed.y+(u[n]||'');
              }
            }
          },
          zoom: {
            pan:  { enabled:true, mode:'x' },
            zoom: {
              wheel:   { enabled:true },
              pinch:   { enabled:true },
              mode:    'x',
            }
          }
        },
        scales: {
          x: {
            ticks: { font:{size:9, family:"'Space Mono', monospace"}, color:'#6b7c6e', maxTicksLimit:8, maxRotation:0 },
            grid:  { color:'#f0f4f0' },
            border:{ display:false }
          },
          y: {
            grid:  { color:'#e8f5ec' },
            ticks: { font:{size:9}, color:'#6b7c6e', padding:4, maxTicksLimit:4 },
            border:{ display:false }
          }
        }
      }
    });
  }

  const chartSuhu       = buatSplitChart('chartSuhu',       'Suhu',          [], [],  '#e53e3e', 'rgba(229,62,62,.12)');
  const chartKelembapan = buatSplitChart('chartKelembapan', 'Kelembapan',    [], [],  '#00b5a3', 'rgba(0,181,163,.12)');
  const chartSoilMini   = buatSplitChart('chartSoil',       'Soil Moisture', [], [],  '#2563eb', 'rgba(37,99,235,.10)');

  window.resetZoomAll = function() {
    chartSuhu.resetZoom();
    chartKelembapan.resetZoom();
    chartSoilMini.resetZoom();
  };

  let _splitPeriod = 'semua';

  window.filterSplitChart = function(period, btn) {
    _splitPeriod = period;
    document.querySelectorAll('.period-tab').forEach(b => {
      if (b.textContent === '↺') return;
      b.classList.toggle('active', b.textContent === btn.textContent);
    });
    updateSplitChartsFromFirebase();
  };

  // ── Semua periode (termasuk 'semua') selalu diagregasi biar chart halus, bukan menumpuk data mentah ──
  window.updateSplitChartsFromFirebase = function() {
    if (!window._historyCache || !window._historyCache.length) return;

    const source = window._historyCache.map(d => ({
      t: d.created_at || d.updated_at || '',
      suhu: d.suhu, kelembapan: d.kelembapan, soil: d.soil
    }));

    const filtered = _splitPeriod === 'semua' ? source : filterByPeriod(source, _splitPeriod);
    const agg      = agregasi(filtered, _splitPeriod);

    const noData = !agg.labels.length;
    const labels = noData ? [] : agg.labels;
    const dSuhu  = noData ? [] : agg.suhu;
    const dHum   = noData ? [] : agg.kelembapan;
    const dSoil  = noData ? [] : agg.soil;

    if (typeof chartSuhu !== 'undefined') {
      chartSuhu.data.labels = labels;
      chartSuhu.data.datasets[0].data = dSuhu;
      chartSuhu.update('active');
      chartSuhu.resetZoom();
    }
    if (typeof chartKelembapan !== 'undefined') {
      chartKelembapan.data.labels = labels;
      chartKelembapan.data.datasets[0].data = dHum;
      chartKelembapan.update('active');
      chartKelembapan.resetZoom();
    }
    if (typeof chartSoilMini !== 'undefined') {
      chartSoilMini.data.labels = labels;
      chartSoilMini.data.datasets[0].data = dSoil;
      chartSoilMini.update('active');
      chartSoilMini.resetZoom();
    }
  };

  window.allFotoSrc=[]; window._fotoViewerIdx=-1;
  window.bukaModalFoto=function(idx){
    const item=window.allFotoSrc[idx]; if(!item)return;
    window._fotoViewerIdx=idx;
    document.getElementById('modal-foto-img').src=item.src;
    document.getElementById('modal-foto-waktu').textContent=item.waktu;
    document.getElementById('modal-foto-viewer').classList.add('show');
  };
  window.tutupModalFoto=function(){
    document.getElementById('modal-foto-viewer').classList.remove('show');
    setTimeout(()=>{document.getElementById('modal-foto-img').src='';},300);
    window._fotoViewerIdx=-1;
  };
  window.downloadFoto=function(){
    const img=document.getElementById('modal-foto-img');
    const w=document.getElementById('modal-foto-waktu').textContent;
    const a=document.createElement('a'); a.href=img.src; a.download='foto-melon-'+w.replace(/[/:, ]/g,'-')+'.jpg'; a.click();
  };
</script>

<script type="module">
  import { initializeApp }                                from "https://www.gstatic.com/firebasejs/10.0.0/firebase-app.js";
  import { getDatabase, ref, onValue, remove, push, set } from "https://www.gstatic.com/firebasejs/10.0.0/firebase-database.js";

  const app=initializeApp({
    apiKey:"AIzaSyCmIIEjajnv7m95T4gMoUUMCwFcf2qwClw",
    databaseURL:"https://monitoring-tanaman-d2cd2-default-rtdb.firebaseio.com",
    projectId:"monitoring-tanaman-d2cd2",
  });
  const db=getDatabase(app), circumf=201;

  let relayValue   = -1;
  let _historyCache  = null;
  let _fotoSnapshot  = null;
  let _historyReady  = false;
  let _fotoReady     = false;
  let riwayatChart   = null;

  let _suhuAlertOn      = false;
  const SUHU_BATAS_MAKS = 30;

  // ── Ambang batas suhu (fan) ──
  // suhu >= 30 -> fan ON
  // suhu <= 28 -> fan OFF
  // 28 < suhu < 30 -> pertahankan status terakhir (hysteresis)
  const SUHU_FAN_ON  = 30;
  const SUHU_FAN_OFF = 28;

  // ── Konfigurasi Telegram ──
  const TELEGRAM_BOT_TOKEN = '8782805447:AAHGVQ8a6gsc-Wfs_LW58e0zHjsSJVTeA80';
  const TELEGRAM_CHAT_ID   = '8474879260';

  window.kirimTelegramAlert = async function(suhu) {
  const pesan = '🔥 *PERINGATAN SUHU TINGGI!*\n\n' +
                '🌡 Suhu sensor saat ini: *' + suhu + '°C*\n' +
                '⚠️ Suhu sudah terlalu tinggi!\n\n' +
                '📅 ' + new Date().toLocaleString('id-ID');
  try {
    await fetch('https://api.telegram.org/bot' + TELEGRAM_BOT_TOKEN + '/sendMessage', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        chat_id: TELEGRAM_CHAT_ID,
        text: pesan,
        parse_mode: 'Markdown'
      })
    });
  } catch (e) {
    console.error('Gagal kirim notifikasi Telegram:', e);
  }
}

  function cariSensorTerdekat(waktuStr, field) {
    const cache = window._historyCache || _historyCache;
    if (!cache || !cache.length || !waktuStr) return null;
    const target = new Date(waktuStr).getTime();
    if (isNaN(target)) return null;
    let best = null, bestDiff = Infinity;
    for (const d of cache) {
      const diff = Math.abs(new Date(d.created_at).getTime() - target);
      if (diff < bestDiff) { bestDiff = diff; best = d; }
    }
    return best ? (best[field] ?? null) : null;
  }

  window._simpanFotoCallback = function(p) {
    return push(ref(db,'/foto'),{
      image:p.image, timestamp:p.timestamp, waktu:p.waktu,
      suhu:p.suhu??null, kelembapan:p.kelembapan??null, soil:p.soil??null
    }).then(()=>showToast('✅ Foto berhasil disimpan!',3000));
  };

  window.hapusFotoSatu=async function(){
    const item=window.allFotoSrc[window._fotoViewerIdx];
    if(!item||!item.firebaseKey){alert('Key tidak ditemukan!');return;}
    if(!confirm('Yakin hapus foto ini? TIDAK BISA DIKEMBALIKAN.'))return;
    try{ await remove(ref(db,'/foto/'+item.firebaseKey)); showToast('✅ Foto dihapus!'); tutupModalFoto(); }
    catch(e){ alert('❌ Gagal: '+e.message); }
  };

  window.hapusFotoGrid=async function(event,firebaseKey){
    event.stopPropagation();
    if(!confirm('Yakin hapus foto ini? TIDAK BISA DIKEMBALIKAN.'))return;
    try{ await remove(ref(db,'/foto/'+firebaseKey)); showToast('✅ Foto dihapus!'); }
    catch(e){ alert('❌ Gagal: '+e.message); }
  };

  window.hapusFotoSemua=async function(){
    if(!confirm('⚠️ YAKIN hapus SEMUA foto? Ini TIDAK BISA DIKEMBALIKAN!'))return;
    if(!confirm('Konfirmasi sekali lagi — HAPUS SEMUA FOTO PERMANEN?'))return;
    try{ await remove(ref(db,'/foto')); showToast('✅ Semua foto dihapus!'); closeModal('hapus-foto-modal'); }
    catch(e){ alert('❌ Gagal: '+e.message); }
  };

  function parseEntries(data) {
    const allKeys=Object.keys(data);
    const hasPushKeys=allKeys.some(k=>k.startsWith('-'));
    if(!hasPushKeys&&allKeys.includes('image')&&typeof data.image==='string'&&data.image.length>100){
      return [{key:'legacy-flat',image:data.image,timestamp:data.timestamp||Date.now(),waktu:data.waktu||'',suhu:data.suhu??null,kelembapan:data.kelembapan??null,soil:data.soil??null}];
    }
    return Object.entries(data)
      .map(([key,foto])=>{
        if(typeof foto!=='object'||foto===null)return null;
        if(!foto.image||typeof foto.image!=='string'||foto.image.length<100)return null;
        return {key,...foto};
      })
      .filter(Boolean)
      .sort((a,b)=>{
        const wa=a.waktu||'',wb=b.waktu||'';
        if(wa&&wb)return wb.localeCompare(wa);
        const ta=a.timestamp<1e10?a.timestamp*1000:(a.timestamp||0);
        const tb=b.timestamp<1e10?b.timestamp*1000:(b.timestamp||0);
        return tb-ta;
      });
  }

  function renderFotoItem(i,src,waktu,firebaseKey){
    window.allFotoSrc[i]={src,waktu,firebaseKey};
    const sk=document.getElementById('foto-skeleton-'+i); if(!sk)return;
    const div=document.createElement('div');
    div.className='foto-histori-item'; div.onclick=()=>bukaModalFoto(i);
    div.innerHTML='<img src="'+src+'" title="'+waktu+'" alt="foto" loading="lazy"><div class="foto-histori-label">'+waktu+'</div><button class="btn-hapus-foto-item" onclick="hapusFotoGrid(event,\''+firebaseKey+'\')" title="Hapus foto ini">✕</button>';
    sk.replaceWith(div);
  }

  async function renderSemuaFoto(data) {
    const grid=document.getElementById('foto-grid');
    const countBadge=document.getElementById('foto-count');
    const strip=document.getElementById('timelapse-strip');
    if(!data){
      grid.innerHTML='<p style="color:var(--muted);font-size:13px;padding:10px;">Belum ada foto tersimpan</p>';
      strip.innerHTML='<p style="color:var(--muted);font-size:12px;padding:10px;">Belum ada foto</p>';
      countBadge.textContent='0 foto'; window.allFotoSrc=[]; return;
    }
    const entries=parseEntries(data);
    countBadge.textContent=entries.length+' foto';
    window.allFotoSrc=new Array(entries.length);
    grid.innerHTML=entries.map((_,i)=>'<div id="foto-skeleton-'+i+'" class="foto-skeleton"><span style="font-size:20px">⏳</span></div>').join('');
    strip.innerHTML=entries.map((foto,i)=>{
      const ts=foto.timestamp<1e10?foto.timestamp*1000:(foto.timestamp||Date.now());
      const src='data:image/jpeg;base64,'+foto.image;
      const wkt=foto.waktu?foto.waktu.substring(5,16):new Date(ts).toLocaleString('id-ID',{day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'});
      return '<div style="flex-shrink:0;position:relative;" id="strip-'+i+'"><img src="'+src+'" onclick="setMain(\''+src+'\')" style="width:80px;height:58px;object-fit:cover;border-radius:6px;border:2px solid '+(i===0?'var(--green-mid)':'transparent')+';cursor:pointer;" loading="lazy"><span style="position:absolute;bottom:3px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,.55);color:#fff;font-size:9px;font-family:\'Space Mono\',monospace;padding:1px 5px;border-radius:3px;white-space:nowrap;">'+wkt+'</span></div>';
    }).join('')||'<p style="color:var(--muted);font-size:12px;padding:10px;">Belum ada foto</p>';

    for(let i=0;i<entries.length;i++){
      const foto=entries[i];
      const ts=foto.timestamp<1e10?foto.timestamp*1000:(foto.timestamp||Date.now());
      const waktu=foto.waktu||new Date(ts).toLocaleString('id-ID');

      const suhuFoto = cariSensorTerdekat(foto.waktu, 'suhu');
      const humFoto  = cariSensorTerdekat(foto.waktu, 'kelembapan');
      const soilFoto = cariSensorTerdekat(foto.waktu, 'soil');

      let b64wm=foto.image;
      try{ b64wm=await tambahWatermarkAsync(foto.image, foto.waktu||'', suhuFoto, humFoto, soilFoto); }
      catch(e){ console.warn('[WM] foto ke-'+i+' gagal watermark'); }
      const src='data:image/jpeg;base64,'+b64wm;
      renderFotoItem(i,src,waktu,foto.key);
      const si=document.getElementById('strip-'+i);
      if(si){ const img=si.querySelector('img'); if(img) img.src=src; }
    }
  }

  onValue(ref(db,'/sensor/history'),(snap)=>{
    const raw=snap.val();
    if(!raw){ window._historyCache=[]; }
    else {
      window._historyCache = Object.values(raw)
        .filter(d => d.created_at || d.updated_at)
        .map(d => ({...d, created_at: d.created_at || d.updated_at}))
        .sort((a,b) => new Date(a.created_at) - new Date(b.created_at));
    }
    _historyReady = true;

    if (typeof window.updateSplitChartsFromFirebase === 'function') {
      window.updateSplitChartsFromFirebase();
    }

    if(!raw){
      document.getElementById('riwayat-tbody').innerHTML='<tr><td colspan="10" class="loading-text">Belum ada data</td></tr>';
    } else {
      const data=Object.values(raw).sort((a,b)=>new Date(b.created_at||b.updated_at||0)-new Date(a.created_at||a.updated_at||0));
      document.getElementById('riwayat-tbody').innerHTML=data.map((d,i)=>{
        const pompaRaw=d.pompa;
        const pompaStr=(pompaRaw!==null&&pompaRaw!==undefined)?pompaRaw.toString().toUpperCase().trim():'';
        const isOn=(pompaStr==='1'||pompaStr==='ON'||pompaStr.includes('ON'));
        const rc=isOn?'pump-on-row':'pump-off-row';
        return '<tr class="'+rc+'"><td>'+(i+1)+'</td><td>'+(d.created_at||d.updated_at||'-')+'</td><td>'+(d.suhu??'-')+'</td><td>'+(d.kelembapan??'-')+'</td><td>'+(d.soil??'-')+'</td><td>'+getBadgeSuhu(d.suhu)+'</td><td>'+getBadgeKelembapan(d.kelembapan)+'</td><td>'+getBadgeSoil(d.soil)+'</td><td>'+getBadgePompa(d.pompa)+'</td><td>'+getBadgeFan(d.fan)+'</td></tr>';
      }).join('');
      const asc=[...data].reverse().slice(-50);
      const rl=asc.map(d=>(d.created_at||d.updated_at||'').slice(0,16)||'-');

      const mkDatasets = (src) => [
        {label:'Suhu (°C)',        data:src.map(d=>d.suhu),       borderColor:'#e53e3e', backgroundColor:'rgba(229,62,62,.13)',  tension:0.4, pointRadius:3, pointHoverRadius:6, fill:true, borderWidth:2.5},
        {label:'Kelembapan (%)',   data:src.map(d=>d.kelembapan), borderColor:'#00b5a3', backgroundColor:'rgba(0,181,163,.13)',  tension:0.4, pointRadius:3, pointHoverRadius:6, fill:true, borderWidth:2.5},
        {label:'Soil Moisture (%)',data:src.map(d=>d.soil),       borderColor:'#2563eb', backgroundColor:'rgba(37,99,235,.10)',  tension:0.4, pointRadius:3, pointHoverRadius:6, fill:true, borderWidth:2.5},
      ];

      const riwayatOpts = {
        responsive:true, maintainAspectRatio:false,
        animation:{ duration:700, easing:'easeInOutQuart' },
        interaction:{ mode:'index', intersect:false },
        plugins:{
          legend:{
            display:true,
            position:'bottom',
            labels:{ color:'#6b7c6e', font:{size:11, family:"'DM Sans', sans-serif"}, usePointStyle:true, pointStyleWidth:20, padding:20 }
          },
          tooltip:{
            backgroundColor:'rgba(26,44,30,0.92)', titleColor:'#a3c4a8', bodyColor:'#fff',
            padding:10, cornerRadius:8,
            titleFont:{ family:"'Space Mono', monospace", size:10 },
            bodyFont:{ family:"'DM Sans', sans-serif", size:12 },
            callbacks:{
              title: items => '⏱ '+items[0].label,
              label: ctx => {
                const ic={'Suhu (°C)':'🌡','Kelembapan (%)':'💧','Soil Moisture (%)':'🌱'};
                return '  '+(ic[ctx.dataset.label]||'')+' '+ctx.dataset.label+': '+ctx.parsed.y;
              }
            }
          }
        },
        scales:{
          x:{
            ticks:{ font:{size:9, family:"'Space Mono', monospace"}, color:'#6b7c6e', maxTicksLimit:10, maxRotation:0 },
            grid:{ color:'#f0f4f0' }, border:{ display:false }
          },
          y:{
            grid:{ color:'#e8f5ec' },
            ticks:{ font:{size:9}, color:'#6b7c6e', padding:4 },
            border:{ display:false }
          }
        }
      };

      if(riwayatChart){
        riwayatChart.data.labels=rl;
        riwayatChart.data.datasets[0].data=asc.map(d=>d.suhu);
        riwayatChart.data.datasets[1].data=asc.map(d=>d.kelembapan);
        riwayatChart.data.datasets[2].data=asc.map(d=>d.soil);
        riwayatChart.update('active');
      } else {
        riwayatChart=new Chart(document.getElementById('riwayatChart').getContext('2d'),{
          type:'line', data:{ labels:rl, datasets:mkDatasets(asc) }, options:riwayatOpts
        });
      }

      window.filterRiwayatChart = function(period, btn) {
        btn.closest('.chart-filter').querySelectorAll('.chart-filter-btn').forEach(b=>b.classList.remove('active'));
        btn.classList.add('active');

        const now = new Date();
        const allData = Object.values(raw).sort((a,b)=>new Date(a.created_at||a.updated_at||0)-new Date(b.created_at||b.updated_at||0));

        const filtered = allData.filter(d => {
          const t = new Date(d.created_at||d.updated_at||0);
          if (period==='hari')   return t.toDateString()===now.toDateString();
          if (period==='minggu') return (now-t)/86400000<=7;
          if (period==='bulan')  return t.getMonth()===now.getMonth()&&t.getFullYear()===now.getFullYear();
          return true;
        });

        if (period==='hari') {
          const grp={};
          filtered.forEach(d=>{
            const h=new Date(d.created_at||d.updated_at).getHours().toString().padStart(2,'0')+':00';
            if(!grp[h])grp[h]={s:[],k:[],so:[]};
            if(d.suhu!=null)       grp[h].s.push(+d.suhu);
            if(d.kelembapan!=null) grp[h].k.push(+d.kelembapan);
            if(d.soil!=null)       grp[h].so.push(+d.soil);
          });
          const avg=a=>a.length?+(a.reduce((x,y)=>x+y,0)/a.length).toFixed(1):null;
          const keys=Object.keys(grp);
          riwayatChart.data.labels=keys;
          riwayatChart.data.datasets[0].data=keys.map(k=>avg(grp[k].s));
          riwayatChart.data.datasets[1].data=keys.map(k=>avg(grp[k].k));
          riwayatChart.data.datasets[2].data=keys.map(k=>avg(grp[k].so));
        } else if (period==='minggu'||period==='bulan') {
          const grp={};
          filtered.forEach(d=>{
            const key=new Date(d.created_at||d.updated_at).toLocaleDateString('id-ID',{day:'2-digit',month:'2-digit'});
            if(!grp[key])grp[key]={s:[],k:[],so:[]};
            if(d.suhu!=null)       grp[key].s.push(+d.suhu);
            if(d.kelembapan!=null) grp[key].k.push(+d.kelembapan);
            if(d.soil!=null)       grp[key].so.push(+d.soil);
          });
          const avg=a=>a.length?+(a.reduce((x,y)=>x+y,0)/a.length).toFixed(1):null;
          const keys=Object.keys(grp);
          riwayatChart.data.labels=keys;
          riwayatChart.data.datasets[0].data=keys.map(k=>avg(grp[k].s));
          riwayatChart.data.datasets[1].data=keys.map(k=>avg(grp[k].k));
          riwayatChart.data.datasets[2].data=keys.map(k=>avg(grp[k].so));
        } else {
          riwayatChart.data.labels=filtered.map(d=>(d.created_at||d.updated_at||'').slice(0,16));
          riwayatChart.data.datasets[0].data=filtered.map(d=>d.suhu);
          riwayatChart.data.datasets[1].data=filtered.map(d=>d.kelembapan);
          riwayatChart.data.datasets[2].data=filtered.map(d=>d.soil);
        }
        riwayatChart.update('active');
      };

      window.toggleRiwayatDataset = function(idx) {
        if(!riwayatChart) return;
        const meta = riwayatChart.getDatasetMeta(idx);
        meta.hidden = !meta.hidden;
        riwayatChart.update();
        const items = document.querySelectorAll('#tab-riwayat .legend-item');
        if(items[idx]) items[idx].style.opacity = meta.hidden ? '0.35' : '1';
      };
    }

    if(_fotoReady && _fotoSnapshot !== null){
      renderSemuaFoto(_fotoSnapshot);
    }
  });

  onValue(ref(db,'/foto'), async (snapshot)=>{
    _fotoSnapshot = snapshot.val();
    _fotoReady = true;
    if(!_historyReady) return;
    renderSemuaFoto(_fotoSnapshot);
  });

  onValue(ref(db,'/relay'),(snap)=>{
    const val=snap.val();
    relayValue=(val===null||val===undefined)?0:Number(val);
    const isOn=(relayValue===1);
    const valPompa=document.getElementById('val-pompa');
    const badgePompa=document.getElementById('badge-pompa');
    const camBadge=document.getElementById('pump-cam-badge');
    if(isOn){
      valPompa.textContent='ON';
      badgePompa.innerHTML='<span class="badge-pump-on">💧 ON</span>';
      camBadge.className='pump-cam-badge on';
    } else {
      valPompa.textContent='OFF';
      badgePompa.innerHTML='<span class="badge-pump-off">⭕ OFF</span>';
      camBadge.className='pump-cam-badge';
    }
  });

  window.togglePompaRelay=async function(){
    const next=(relayValue===1)?0:1;
    try{
      await set(ref(db,'/relay'),next);
      showToast(next===1?'✅ Pompa diset ON (manual)':'✅ Pompa diset OFF (manual)');
    }catch(e){
      showToast('❌ Gagal: '+e.message,4000);
    }
  };

  let fanValue = 0;

  onValue(ref(db,'/fan'),(snap)=>{
    const val=snap.val();
    fanValue=(val===null||val===undefined)?0:Number(val);
    const isOn=(fanValue===1);
    const valFan=document.getElementById('val-fan');
    const badgeFan=document.getElementById('badge-fan');
    if(isOn){
      valFan.textContent='ON';
      badgeFan.innerHTML='<span class="badge-fan-on">🌀 ON</span>';
    } else {
      valFan.textContent='OFF';
      badgeFan.innerHTML='<span class="badge-fan-off">⭕ OFF</span>';
    }
  });

  window.toggleFanRelay=async function(){
    const next=(fanValue===1)?0:1;
    try{
      await set(ref(db,'/fan'),next);
      showToast(next===1?'✅ Fan diset ON (manual)':'✅ Fan diset OFF (manual)');
    }catch(e){
      showToast('❌ Gagal: '+e.message,4000);
    }
  };

  onValue(ref(db,'/sensor/latest'),(snapshot)=>{
    const data=snapshot.val(); if(!data)return;
    const suhu=parseFloat(data.suhu)||0, kelembapan=parseFloat(data.kelembapan)||0, soil=parseFloat(data.soil)||0;
    const updatedAt=data.updated_at??data.created_at??'n/a';
    document.getElementById('overlay-suhu').textContent=suhu+'°C';
    document.getElementById('overlay-kelembapan').textContent=kelembapan+'%';
    document.getElementById('overlay-soil').textContent=soil+'%';
    document.getElementById('overlay-date').textContent=updatedAt;
    document.getElementById('val-suhu').textContent=suhu+' °C';
    document.getElementById('val-kelembapan').textContent=kelembapan+' %';
    document.getElementById('val-soil').textContent=soil+' %';
    document.getElementById('gauge-suhu-circle').setAttribute('stroke-dashoffset',Math.round(circumf*(1-Math.min(suhu,100)/100)));
    document.getElementById('gauge-suhu-text').textContent=suhu+'°';
    document.getElementById('gauge-hum-circle').setAttribute('stroke-dashoffset',Math.round(circumf*(1-Math.min(kelembapan,100)/100)));
    document.getElementById('gauge-hum-text').textContent=kelembapan+'%';
    document.getElementById('gauge-soil-circle').setAttribute('stroke-dashoffset',Math.round(circumf*(1-Math.min(soil,100)/100)));
    document.getElementById('gauge-soil-text').textContent=soil+'%';
    const bs=document.getElementById('badge-suhu');
    if(suhu<25){bs.className='badge-warn';bs.textContent='DINGIN';}
    else if(suhu<=32){bs.className='badge-ok';bs.textContent='NORMAL';}
    else{bs.className='badge-danger';bs.textContent='PANAS';}
    const bh=document.getElementById('badge-kelembapan');
    if(kelembapan<60){bh.className='badge-warn';bh.textContent='RENDAH';}
    else if(kelembapan<=80){bh.className='badge-ok';bh.textContent='NORMAL';}
    else{bh.className='badge-warn';bh.textContent='TINGGI';}
    const bso=document.getElementById('badge-soil');
    if(soil<=SOIL_MULAI_SIRAM){bso.className='badge-danger';bso.textContent='KERING';}
    else if(soil<SOIL_STOP_SIRAM){bso.className='badge-ok';bso.textContent='IDEAL';}
    else{bso.className='badge-info';bso.textContent='BASAH';}

    // ── Auto kontrol POMPA berdasarkan soil moisture (dengan hysteresis) ──
    if (soil <= SOIL_MULAI_SIRAM && relayValue !== 1) {
      set(ref(db,'/relay'), 1).catch(e=>console.error('Gagal auto ON pompa:', e));
    } else if (soil >= SOIL_STOP_SIRAM && relayValue !== 0) {
      set(ref(db,'/relay'), 0).catch(e=>console.error('Gagal auto OFF pompa:', e));
    }

    // ── Auto kontrol FAN berdasarkan suhu (dengan hysteresis) ──
    if (suhu >= SUHU_FAN_ON && fanValue !== 1) {
      set(ref(db,'/fan'), 1).catch(e=>console.error('Gagal auto ON fan:', e));
    } else if (suhu <= SUHU_FAN_OFF && fanValue !== 0) {
      set(ref(db,'/fan'), 0).catch(e=>console.error('Gagal auto OFF fan:', e));
    }

    if (suhu >= SUHU_BATAS_MAKS) {
      if (!_suhuAlertOn) {
        _suhuAlertOn = true;
        const teksAlert = document.getElementById('suhu-alert-text');
        if (teksAlert) {
          teksAlert.innerHTML =
            '⚠️ <b>Suhu telah mencapai ' + suhu + '°C</b>, melebihi batas aman (' + SUHU_BATAS_MAKS + '°C)';
        }
        openModal('suhu-alert-modal');
        showToast('🔥 Suhu tinggi terdeteksi: ' + suhu + '°C!', 5000);
        kirimTelegramAlert(suhu);
      }
    } else {
      _suhuAlertOn = false;
    }
  });

  window.hapusData=async function(){
    const dari=document.getElementById('hapus-dari').value, sampai=document.getElementById('hapus-sampai').value;
    if(!dari||!sampai){alert('Pilih tanggal dulu!');return;}
    if(!confirm('Hapus data '+dari+' s/d '+sampai+'?'))return;
    const snap=await new Promise(res=>onValue(ref(db,'/sensor/history'),res,{onlyOnce:true}));
    const raw=snap.val(); if(!raw){alert('Tidak ada data!');return;}
    const keys=Object.entries(raw).filter(([,v])=>{const t=(v.created_at||v.updated_at||'').slice(0,10);return t&&t>=dari&&t<=sampai;}).map(([k])=>k);
    if(!keys.length){alert('Tidak ada data di rentang itu.');return;}
    await Promise.all(keys.map(k=>remove(ref(db,'/sensor/history/'+k))));
    alert('✅ '+keys.length+' data dihapus!'); closeModal('hapus-modal');
  };

  window.hapusSemua=async function(){
    if(!confirm('Hapus SEMUA data sensor?'))return;
    await remove(ref(db,'/sensor/history'));
    alert('✅ Semua data sensor dihapus!'); closeModal('hapus-modal');
  };
</script>
</body>
</html>