ALAMDIENG commited on
Commit
217a74c
·
1 Parent(s): 646ac62

revert: rollback to version before Anggaran & Decision Dashboard (commit 45fc0b7)

Browse files
Files changed (3) hide show
  1. frontend/app.js +15 -320
  2. frontend/index.html +123 -484
  3. frontend/style.css +3 -822
frontend/app.js CHANGED
@@ -117,34 +117,12 @@ const timelineList = document.getElementById("timeline-list");
117
  const hourlySection = document.getElementById("hourly-section");
118
  const hourlyGrid = document.getElementById("hourly-grid");
119
 
120
- // Decision Dashboard UI Elements
121
- const decLocationSelect = document.getElementById("dec-location-select");
122
- const decTotalVol = document.getElementById("dec-total-vol");
123
- const decVolStatus = document.getElementById("dec-vol-status");
124
- const decCostVal = document.getElementById("dec-cost-val");
125
- const decEfficiencyVal = document.getElementById("dec-efficiency-val");
126
- const decSavedVal = document.getElementById("dec-saved-val");
127
- const decWorkloadGrid = document.getElementById("dec-workload-grid");
128
- const decTruckLarge = document.getElementById("dec-truck-large");
129
- const decTruckSmall = document.getElementById("dec-truck-small");
130
- const decCriticalTitle = document.getElementById("dec-critical-title");
131
- const decCriticalList = document.getElementById("dec-critical-list");
132
-
133
- // Weather Modal UI Elements
134
- const weatherModal = document.getElementById("weather-modal");
135
- const modalWeatherLoc = document.getElementById("modal-weather-loc");
136
- const modalSumTotal = document.getElementById("modal-sum-total");
137
- const modalSumAvg = document.getElementById("modal-sum-avg");
138
- const modalSumBadge = document.getElementById("modal-sum-badge");
139
- const weatherModalList = document.getElementById("weather-modal-list");
140
-
141
  // State
142
  let selectedLocation = "Menteng";
143
  let rainValue = 0; // 0 means Auto (Open-Meteo)
144
  let map;
145
  let mapMarkers = {};
146
  let routeLine = null;
147
- let lastPredictionData = null;
148
 
149
  // ==========================================
150
  // SPA MULTIPAGE ROUTING
@@ -173,11 +151,8 @@ function switchPage(pageId) {
173
  loadAlertsFeed();
174
  } else if (pageId === "page-autopilot") {
175
  loadAutopilotFeed();
176
- } else if (pageId === "page-predictor") {
177
- if (map) setTimeout(() => { map.invalidateSize(); }, 200);
178
- if (lastPredictionData) {
179
- updateDecisionDashboard(lastPredictionData, selectedLocation);
180
- }
181
  }
182
  }
183
 
@@ -414,41 +389,38 @@ async function fetchLiveWeather(loc) {
414
  const precipList = dailyData.precipitation_sum || [];
415
  const precipToday = precipList[2] || 0;
416
 
417
- let cond = "Cerah Berawan";
418
- if (code === 0 && precipToday < 0.5) cond = "Cerah";
419
- else if (code <= 2 && precipToday < 0.5) cond = "Cerah Berawan";
420
- else if (code <= 3 && precipToday < 0.5) cond = "Berawan";
421
- else if (precipToday >= 20.0) cond = "Hujan Lebat";
422
- else if (precipToday >= 5.0) cond = "Hujan Sedang";
423
- else if (precipToday >= 0.5) cond = "Hujan Ringan";
424
- else cond = "Berawan";
425
 
426
  if (weatherForecastText) weatherForecastText.textContent = `${temp}°C - ${cond}`;
427
  if (weatherLocationText) weatherLocationText.textContent = `${loc} (${coord.city})`;
428
  if (weatherPrecip) weatherPrecip.textContent = `${precipToday.toFixed(1)} mm`;
429
 
430
  if (weatherAlert) {
431
- if (precipToday >= 50) {
432
- weatherAlert.textContent = "FLOOD DANGER 🔴";
433
- weatherAlert.className = "highlight text-red";
434
- } else if (precipToday >= 20) {
435
  weatherAlert.textContent = "HEAVY RAIN 🟡";
436
  weatherAlert.className = "highlight text-warning";
 
 
 
437
  } else {
438
- weatherAlert.textContent = "Normal / Aman";
439
  weatherAlert.className = "highlight";
440
  }
441
  }
442
  } catch (err) {
443
  console.warn("Weather fetch timed out/failed. Using fallback forecast.", err);
444
  // Instant Fallback Weather Data
445
- const fallbackTemp = 32.5 + Math.random() * 2.0;
446
  const fallbackPrecip = 0.0;
447
- if (weatherForecastText) weatherForecastText.textContent = `${fallbackTemp.toFixed(1)}°C - Cerah Berawan`;
448
  if (weatherLocationText) weatherLocationText.textContent = `${loc} (${coord.city})`;
449
  if (weatherPrecip) weatherPrecip.textContent = `${fallbackPrecip.toFixed(1)} mm`;
450
  if (weatherAlert) {
451
- weatherAlert.textContent = "Normal / Aman";
452
  weatherAlert.className = "highlight";
453
  }
454
  }
@@ -603,285 +575,8 @@ function updateDashboardData(data, confScore, message) {
603
  } else {
604
  if (hourlySection) hourlySection.style.display = "none";
605
  }
606
-
607
- // Save and update Decision Dashboard
608
- lastPredictionData = data;
609
- updateDecisionDashboard(data, selectedLocation);
610
- }
611
-
612
- // ==========================================
613
- // DECISION DASHBOARD (EXECUTIVE LOGISTICS & COST OPTIMIZER)
614
- // ==========================================
615
- function updateDecisionDashboard(data, location) {
616
- if (!data || !data.prediction_results || data.prediction_results.length === 0) return;
617
-
618
- const results = data.prediction_results;
619
- const weekResults = results.slice(0, 7);
620
- const totalVolume = weekResults.reduce((acc, curr) => acc + curr.total_volume_ton, 0);
621
- const totalTrucks = weekResults.reduce((acc, curr) => acc + curr.recommended_trucks, 0);
622
- const config = KECAMATAN_DATABASE[location] || KECAMATAN_DATABASE["Menteng"];
623
-
624
- // 1. Total Volume Prediksi
625
- if (decTotalVol) decTotalVol.textContent = totalVolume.toFixed(1);
626
- if (decVolStatus) decVolStatus.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#00F0FF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> Berhasil diprediksi (${weekResults.length} hari)`;
627
-
628
- // 2. Estimasi Biaya Operasional (Pergub DKI & Standar DLH: Rp 95.000/ton + SDM Rp 504.000/truk/hari)
629
- const costProactive = Math.round(((totalVolume * 95000) + (totalTrucks * 504000)) / 100000) * 100000;
630
- const costReactive = Math.round((costProactive * 2.048) / 100000) * 100000;
631
- const costSaved = costReactive - costProactive;
632
- const efficiencyPct = ((costSaved / costReactive) * 100).toFixed(1);
633
-
634
- if (decCostVal) decCostVal.textContent = `Rp ${costProactive.toLocaleString('id-ID')}`;
635
- if (decEfficiencyVal) decEfficiencyVal.textContent = `${efficiencyPct}%`;
636
- if (decSavedVal) decSavedVal.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#4ade80" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 18 13.5 8.5 8.5 13.5 1 6"/><polyline points="17 18 23 18 23 12"/></svg> Hemat Rp ${costSaved.toLocaleString('id-ID')} dibanding Reaktif`;
637
-
638
- // 3. Alokasi Armada Spesifik (Dump Truck 60% vs Pick-up 40% pemukiman padat)
639
- const dumpTruckUnits = Math.ceil(totalTrucks * 0.60);
640
- const pickupUnits = Math.ceil(totalTrucks * 0.40 * 1.75);
641
- if (decTruckLarge) decTruckLarge.textContent = dumpTruckUnits;
642
- if (decTruckSmall) decTruckSmall.textContent = pickupUnits;
643
-
644
- // 4. Peta Beban Kerja Harian (7 Hari Horizon)
645
- if (decWorkloadGrid) {
646
- decWorkloadGrid.innerHTML = "";
647
- const dayAbbrsIndo = ["MIN", "SEN", "SEL", "RAB", "KAM", "JUM", "SAB"];
648
-
649
- weekResults.forEach(day => {
650
- const dateObj = new Date(day.date);
651
- const dayAbbr = dayAbbrsIndo[dateObj.getDay()] || "HARI";
652
- const isCritical = day.risk_status === "CRITICAL" || day.total_volume_ton > (config.normal_avg * 1.18);
653
- const isWarning = day.risk_status === "WARNING";
654
-
655
- let badgeClass = "normal";
656
- if (isCritical) badgeClass = "peak-critical";
657
- else if (isWarning || dateObj.getDay() === 0 || dateObj.getDay() === 5 || dateObj.getDay() === 6) badgeClass = "warning";
658
-
659
- const card = document.createElement("div");
660
- card.className = `workload-day-card ${isCritical ? "critical" : ""}`;
661
-
662
- // Shift schedule: S1 & S2 always active, S3 active on peak/critical days
663
- const showShift3 = isCritical || day.total_volume_ton > (config.normal_avg * 1.10);
664
-
665
- card.innerHTML = `
666
- <div class="workload-day-badge ${badgeClass}">${dayAbbr}</div>
667
- <div class="workload-ton-val">${day.total_volume_ton.toFixed(1)}</div>
668
- <div class="workload-ton-label">TON</div>
669
- <div class="workload-trucks-count">
670
- <span>🚚</span> <span>${day.recommended_trucks}</span>
671
- </div>
672
- <div class="workload-shifts-list">
673
- <span class="workload-shift-pill s1">S1: 04:00-10:00</span>
674
- <span class="workload-shift-pill s2">S2: 10:00-16:00</span>
675
- ${showShift3 ? '<span class="workload-shift-pill s3">S3: 19:00-01:00</span>' : ''}
676
- </div>
677
- `;
678
- decWorkloadGrid.appendChild(card);
679
- });
680
- }
681
-
682
- // 5. Rencana Aksi Kritis
683
- const peakDay = weekResults.reduce((max, d) => d.total_volume_ton > max.total_volume_ton ? d : max, weekResults[0]);
684
- if (peakDay) {
685
- const peakDateObj = new Date(peakDay.date);
686
- const dayAbbrsIndo = ["MIN", "SEN", "SEL", "RAB", "KAM", "JUM", "SAB"];
687
- const peakDayName = dayAbbrsIndo[peakDateObj.getDay()] || "HARI";
688
-
689
- let situationTag = "BEBAN PUNCAK & KEPADATAN RUTIN";
690
- if (peakDay.event_info) {
691
- situationTag = peakDay.event_info.toUpperCase();
692
- } else if (peakDay.risk_status === "CRITICAL") {
693
- situationTag = "HUJAN LEBAT & BEBAN PUNCAK";
694
- }
695
-
696
- if (decCriticalTitle) {
697
- decCriticalTitle.textContent = `AKSI HARI ${peakDayName} (${situationTag})`;
698
- }
699
-
700
- if (decCriticalList) {
701
- decCriticalList.innerHTML = `
702
- <li>Aktifkan <strong>Shift 3 (19:00 - 01:00 WIB)</strong> untuk evakuasi cepat TPS permukiman ${location} guna mencegah penumpukan sampah malam.</li>
703
- <li>Prioritaskan dispatch <strong>${Math.ceil(peakDay.recommended_trucks * 0.60)} unit Dump Truck 15T</strong> via jalur bebas hambatan untuk mitigasi antrean gerbang timbang TPST Bantargebang.</li>
704
- <li>Siagakan <strong>pompa portabel penyedot lindi</strong> dan armada sapu bersih di titik TPS transfer point.</li>
705
- <li>Kerahkan <strong>${Math.ceil(peakDay.recommended_trucks * 0.40 * 1.75)} armada Pick-up</strong> untuk penyisiran lorong pemukiman padat sebelum jam sibuk warga (pukul 06:00 WIB).</li>
706
- `;
707
- }
708
- }
709
- }
710
-
711
- // ==========================================
712
- // 7-DAY WEATHER FORECAST MODAL CONTROLLER
713
- // ==========================================
714
- async function openWeatherModal(loc) {
715
- const targetLoc = loc || selectedLocation;
716
- const coord = KECAMATAN_DATABASE[targetLoc];
717
- if (!coord) return;
718
-
719
- if (modalWeatherLoc) modalWeatherLoc.textContent = targetLoc;
720
- if (weatherModal) {
721
- weatherModal.classList.add("open");
722
- }
723
-
724
- if (weatherModalList) {
725
- weatherModalList.innerHTML = '<div class="weather-modal-loading">Mengambil data cuaca 7 hari dari Open-Meteo API...</div>';
726
- }
727
-
728
- try {
729
- const url = `https://api.open-meteo.com/v1/forecast?latitude=${coord.coords[0]}&longitude=${coord.coords[1]}&daily=weathercode,temperature_2m_max,temperature_2m_min,precipitation_sum,precipitation_hours&timezone=Asia/Jakarta&forecast_days=7`;
730
-
731
- const timeoutPromise = new Promise((_, reject) =>
732
- setTimeout(() => reject(new Error("Timeout")), 2000)
733
- );
734
- const fetchPromise = fetch(url).then(res => {
735
- if (!res.ok) throw new Error("HTTP Error");
736
- return res.json();
737
- });
738
-
739
- const data = await Promise.race([fetchPromise, timeoutPromise]);
740
- const daily = data.daily || {};
741
- const dates = daily.time || [];
742
- const codes = daily.weathercode || [];
743
- const tempMax = daily.temperature_2m_max || [];
744
- const tempMin = daily.temperature_2m_min || [];
745
- const precipSum = daily.precipitation_sum || [];
746
- const precipHours = daily.precipitation_hours || [];
747
-
748
- let totalPrecip = 0;
749
- let totalHours = 0;
750
- precipSum.forEach(p => totalPrecip += (p || 0));
751
- precipHours.forEach(h => totalHours += (h || 0));
752
-
753
- const avgHr = totalHours > 0 ? (totalPrecip / totalHours) : (totalPrecip / 7);
754
-
755
- if (modalSumTotal) modalSumTotal.textContent = `${totalPrecip.toFixed(1)}mm`;
756
- if (modalSumAvg) modalSumAvg.textContent = `${avgHr.toFixed(1)}mm/hr`;
757
-
758
- // BMKG Weather Category badge
759
- if (modalSumBadge) {
760
- if (totalPrecip < 20.0) {
761
- modalSumBadge.textContent = "KERING";
762
- modalSumBadge.className = "weather-dryness-badge dry";
763
- } else if (totalPrecip < 50.0) {
764
- modalSumBadge.textContent = "LEMBAP";
765
- modalSumBadge.className = "weather-dryness-badge wet";
766
- } else {
767
- modalSumBadge.textContent = "HUJAN LEBAT";
768
- modalSumBadge.className = "weather-dryness-badge heavy";
769
- }
770
- }
771
-
772
- const dayNamesIndo = ["Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"];
773
-
774
- if (weatherModalList) {
775
- weatherModalList.innerHTML = "";
776
- dates.forEach((dStr, idx) => {
777
- const dateObj = new Date(dStr);
778
- const dayName = dayNamesIndo[dateObj.getDay()] || "Hari";
779
- const wCode = codes[idx] || 0;
780
- const pVal = precipSum[idx] || 0.0;
781
- const tMax = Math.round(tempMax[idx] || 33);
782
- const tMin = Math.round(tempMin[idx] || 25);
783
-
784
- let icon = "☀️";
785
- let condition = "Cerah";
786
-
787
- // BMKG Meteorological Standard Interpretation
788
- if (wCode >= 95 || (wCode >= 80 && pVal >= 20.0)) {
789
- icon = "⛈️";
790
- condition = "Badai Petir";
791
- } else if (pVal >= 20.0) {
792
- icon = "🌧️";
793
- condition = "Hujan Lebat";
794
- } else if (pVal >= 5.0) {
795
- icon = "🌧️";
796
- condition = "Hujan Sedang";
797
- } else if (pVal >= 1.5) {
798
- icon = "🌦️";
799
- condition = "Hujan Ringan";
800
- } else if (pVal >= 0.5) {
801
- icon = "⛅";
802
- condition = "Berawan";
803
- } else {
804
- // Under 0.5mm is non-rainy (Cerah / Cerah Berawan)
805
- if (wCode === 0) {
806
- icon = "☀️";
807
- condition = "Cerah";
808
- } else if (wCode <= 2) {
809
- icon = "🌤️";
810
- condition = "Cerah Berawan";
811
- } else {
812
- icon = "⛅";
813
- condition = "Berawan";
814
- }
815
- }
816
-
817
- const precipDisplay = pVal >= 0.5 ? `${pVal.toFixed(1)}mm` : '';
818
-
819
- const row = document.createElement("div");
820
- row.className = "weather-forecast-row";
821
- row.innerHTML = `
822
- <span class="w-row-day">${dayName}</span>
823
- <span class="w-row-icon">${icon}</span>
824
- <span class="w-row-cond">${condition}</span>
825
- <span class="w-row-precip">${precipDisplay}</span>
826
- <div class="w-row-temp">
827
- <span class="w-temp-max">${tMax}°</span>
828
- <span class="w-temp-min">${tMin}°</span>
829
- </div>
830
- `;
831
- weatherModalList.appendChild(row);
832
- });
833
- }
834
-
835
- } catch (err) {
836
- console.warn("Open-Meteo weather modal fallback:", err);
837
- // Fallback matching real Jakarta climate baseline
838
- const fallbackData = [
839
- { day: "Kam", icon: "🌦️", cond: "Hujan Ringan", precip: "1.3mm", max: 33, min: 25 },
840
- { day: "Jum", icon: "⛅", cond: "Berawan", precip: "", max: 35, min: 25 },
841
- { day: "Sab", icon: "🌤️", cond: "Cerah Berawan", precip: "", max: 36, min: 25 },
842
- { day: "Min", icon: "☀️", cond: "Cerah", precip: "", max: 34, min: 26 },
843
- { day: "Sen", icon: "🌦️", cond: "Hujan Ringan", precip: "1.5mm", max: 33, min: 26 },
844
- { day: "Sel", icon: "🌤️", cond: "Cerah Berawan", precip: "", max: 35, min: 26 },
845
- { day: "Rab", icon: "🌧️", cond: "Hujan Sedang", precip: "3.6mm", max: 35, min: 27 }
846
- ];
847
-
848
- if (modalSumTotal) modalSumTotal.textContent = "6.4mm";
849
- if (modalSumAvg) modalSumAvg.textContent = "0.9mm/hr";
850
- if (modalSumBadge) {
851
- modalSumBadge.textContent = "KERING";
852
- modalSumBadge.className = "weather-dryness-badge dry";
853
- }
854
-
855
- if (weatherModalList) {
856
- weatherModalList.innerHTML = "";
857
- fallbackData.forEach(item => {
858
- const row = document.createElement("div");
859
- row.className = "weather-forecast-row";
860
- row.innerHTML = `
861
- <span class="w-row-day">${item.day}</span>
862
- <span class="w-row-icon">${item.icon}</span>
863
- <span class="w-row-cond">${item.cond}</span>
864
- <span class="w-row-precip">${item.precip}</span>
865
- <div class="w-row-temp">
866
- <span class="w-temp-max">${item.max}°</span>
867
- <span class="w-temp-min">${item.min}°</span>
868
- </div>
869
- `;
870
- weatherModalList.appendChild(row);
871
- });
872
- }
873
- }
874
  }
875
 
876
- function closeWeatherModal() {
877
- if (weatherModal) {
878
- weatherModal.classList.remove("open");
879
- }
880
- }
881
-
882
- window.openWeatherModal = openWeatherModal;
883
- window.closeWeatherModal = closeWeatherModal;
884
-
885
  // Request CSV from Backend API and download it
886
  async function runExport() {
887
  if (!exportBtn) return;
 
117
  const hourlySection = document.getElementById("hourly-section");
118
  const hourlyGrid = document.getElementById("hourly-grid");
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  // State
121
  let selectedLocation = "Menteng";
122
  let rainValue = 0; // 0 means Auto (Open-Meteo)
123
  let map;
124
  let mapMarkers = {};
125
  let routeLine = null;
 
126
 
127
  // ==========================================
128
  // SPA MULTIPAGE ROUTING
 
151
  loadAlertsFeed();
152
  } else if (pageId === "page-autopilot") {
153
  loadAutopilotFeed();
154
+ } else if (pageId === "page-predictor" && map) {
155
+ setTimeout(() => { map.invalidateSize(); }, 200);
 
 
 
156
  }
157
  }
158
 
 
389
  const precipList = dailyData.precipitation_sum || [];
390
  const precipToday = precipList[2] || 0;
391
 
392
+ let cond = "Cloudy";
393
+ if (code === 0) cond = "Clear Sky";
394
+ else if (code > 0 && code < 4) cond = "Partly Cloudy";
395
+ else if (code >= 51 && code <= 67) cond = "Rainy";
396
+ else if (code >= 80 && code <= 82) cond = "Showers";
 
 
 
397
 
398
  if (weatherForecastText) weatherForecastText.textContent = `${temp}°C - ${cond}`;
399
  if (weatherLocationText) weatherLocationText.textContent = `${loc} (${coord.city})`;
400
  if (weatherPrecip) weatherPrecip.textContent = `${precipToday.toFixed(1)} mm`;
401
 
402
  if (weatherAlert) {
403
+ if (precipToday > 30) {
 
 
 
404
  weatherAlert.textContent = "HEAVY RAIN 🟡";
405
  weatherAlert.className = "highlight text-warning";
406
+ } else if (precipToday > 50) {
407
+ weatherAlert.textContent = "FLOOD DANGER 🔴";
408
+ weatherAlert.className = "highlight text-red";
409
  } else {
410
+ weatherAlert.textContent = "Normal conditions";
411
  weatherAlert.className = "highlight";
412
  }
413
  }
414
  } catch (err) {
415
  console.warn("Weather fetch timed out/failed. Using fallback forecast.", err);
416
  // Instant Fallback Weather Data
417
+ const fallbackTemp = 28.5 + Math.random() * 3.0;
418
  const fallbackPrecip = 0.0;
419
+ if (weatherForecastText) weatherForecastText.textContent = `${fallbackTemp.toFixed(1)}°C - Partly Cloudy`;
420
  if (weatherLocationText) weatherLocationText.textContent = `${loc} (${coord.city})`;
421
  if (weatherPrecip) weatherPrecip.textContent = `${fallbackPrecip.toFixed(1)} mm`;
422
  if (weatherAlert) {
423
+ weatherAlert.textContent = "Normal conditions";
424
  weatherAlert.className = "highlight";
425
  }
426
  }
 
575
  } else {
576
  if (hourlySection) hourlySection.style.display = "none";
577
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
578
  }
579
 
 
 
 
 
 
 
 
 
 
580
  // Request CSV from Backend API and download it
581
  async function runExport() {
582
  if (!exportBtn) return;
frontend/index.html CHANGED
@@ -1,18 +1,15 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
3
-
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
  <meta name="google-site-verification" content="Uv5ENuSBUKzeJte5ypBe6VGuYhEAZPqszpdCJPIawq4" />
8
  <title>Aeterna AI - #1 AI Prediksi Sampah Jakarta & DKI Jakarta | By Faril Putra Pratama</title>
9
-
10
  <!-- Primary SEO Meta Tags -->
11
  <meta name="title" content="Aeterna AI - #1 AI Prediksi Sampah Jakarta & DKI Jakarta | By Faril Putra Pratama">
12
- <meta name="description"
13
- content="Aeterna AI (aeternaai.biz.id) adalah platform AI Prediksi Sampah #1 untuk 44 Kecamatan DKI Jakarta (ai prediksi sampah jkt) dikembangkan oleh Faril Putra Pratama berbasis BPS Jumlah Jiwa & Amazon Chronos T5.">
14
- <meta name="keywords"
15
- content="ai prediksi sampah, ai prediksi sampah jkt, ai prediksi sampah jakarta, aeterna ai, aeterna ai jakarta, aeternaai.biz.id, Faril Putra Pratama, FARILtau72, Prediksi Sampah Jakarta, DLH DKI Jakarta, AI Persampahan, Amazon Chronos T5, BPS Jakarta Jumlah Jiwa, Smart City Jakarta, TPST Bantargebang">
16
  <meta name="author" content="Faril Putra Pratama">
17
  <meta name="developer" content="Faril Putra Pratama (@FARILtau72)">
18
  <meta name="github:repository" content="https://github.com/FARILtau72/Aeterna-Ai">
@@ -25,8 +22,7 @@
25
  <meta property="og:type" content="website">
26
  <meta property="og:url" content="https://www.aeternaai.biz.id/">
27
  <meta property="og:title" content="Aeterna AI (AI Prediksi Sampah JKT) by Faril Putra Pratama">
28
- <meta property="og:description"
29
- content="Platform AI Prediksi Sampah #1 untuk 44 Kecamatan DKI Jakarta dikembangkan oleh Faril Putra Pratama. GitHub: https://github.com/FARILtau72/Aeterna-Ai | LinkedIn: https://www.linkedin.com/in/faril-putra-pratama-81561a280/">
30
  <meta property="og:image" content="https://www.aeternaai.biz.id/static/assets/og_banner.png">
31
  <meta property="og:site_name" content="Aeterna AI">
32
  <meta property="og:locale" content="id_ID">
@@ -35,17 +31,14 @@
35
  <meta name="twitter:card" content="summary_large_image">
36
  <meta name="twitter:url" content="https://www.aeternaai.biz.id/">
37
  <meta name="twitter:title" content="Aeterna AI by Faril Putra Pratama - #1 Waste Intelligence Platform DKI Jakarta">
38
- <meta name="twitter:description"
39
- content="Sistem AI peramalan sampah & armada logistik 44 Kecamatan DKI Jakarta dikembangkan oleh Faril Putra Pratama.">
40
  <meta name="twitter:image" content="https://www.aeternaai.biz.id/static/assets/og_banner.png">
41
 
42
  <!-- GEO Meta Citations for LLMs (ChatGPT, Gemini, Claude, Perplexity) -->
43
- <meta name="citation_title"
44
- content="Aeterna AI: Spatial-Temporal Waste Generation Forecasting Across 44 Kecamatans in DKI Jakarta">
45
  <meta name="citation_author" content="Faril Putra Pratama (@FARILtau72)">
46
  <meta name="citation_publication_date" content="2026">
47
- <meta name="citation_technical_report_institution"
48
- content="Aeterna AI Labs & Faril Putra Pratama Official Portal (aeternaai.biz.id)">
49
 
50
  <!-- Geo-Location Meta Tags for Regional Indonesian Search Priority -->
51
  <meta name="geo.region" content="ID-JK" />
@@ -59,15 +52,11 @@
59
  <!-- Google Fonts -->
60
  <link rel="preconnect" href="https://fonts.googleapis.com">
61
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
62
- <link
63
- href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@300;400;700&display=swap"
64
- rel="stylesheet">
65
-
66
  <!-- Leaflet.js Map Library -->
67
- <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
68
- integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="" />
69
- <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
70
- integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
71
 
72
  <!-- Three.js 3D Library -->
73
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
@@ -127,7 +116,6 @@
127
  }
128
  </script>
129
  </head>
130
-
131
  <body>
132
  <!-- Background Canvas for Interactive Particle Rain -->
133
  <canvas id="rain-canvas"></canvas>
@@ -141,7 +129,7 @@
141
  <nav class="nav-links">
142
  <button class="nav-btn active" data-target="page-home" onclick="switchPage('page-home')">HOME</button>
143
  <button class="nav-btn" data-target="page-autopilot" onclick="switchPage('page-autopilot')">AI AUTOPILOT</button>
144
- <button class="nav-btn" data-target="page-predictor" onclick="switchPage('page-predictor')">SIMULATION & DECISION TOOL</button>
145
  <button class="nav-btn" data-target="page-news" onclick="switchPage('page-news')">NEWS FEED</button>
146
  <button class="nav-btn" data-target="page-alerts" onclick="switchPage('page-alerts')">REGIONAL ALERTS</button>
147
  <button class="nav-btn" data-target="page-education" onclick="switchPage('page-education')">WASTE EDUCATION</button>
@@ -157,8 +145,7 @@
157
  <section class="hero-section">
158
  <div class="hero-content">
159
  <h1 class="hero-title">NEXT-GEN WASTE FORECASTING</h1>
160
- <p class="hero-subtitle">Meningkatkan efisiensi tata kelola sampah DKI Jakarta hingga 98.28% dengan
161
- pemodelan spasial temporal real-time.</p>
162
  <div class="hero-actions">
163
  <button class="action-btn" onclick="switchPage('page-autopilot')">
164
  <span class="btn-text">OPEN AUTOPILOT</span>
@@ -192,114 +179,70 @@
192
  <div class="panel feature-card">
193
  <div class="feature-icon font-display">01</div>
194
  <h3 class="feature-name">Akurasi Validitas Tinggi (98.28%)</h3>
195
- <p class="feature-desc">Menggunakan arsitektur model Gradient Boosting Regressor (GBR) yang
196
- dioptimasi via GridSearchCV dengan metrik komparasi MAE, RMSE, dan MAPE secara berdampingan.</p>
197
  </div>
198
  <div class="panel feature-card">
199
  <div class="feature-icon font-display">02</div>
200
  <h3 class="feature-name">Integrasi Live Cuaca Open-Meteo</h3>
201
- <p class="feature-desc">API dinamis asinkron yang menarik data curah hujan live tingkat kecamatan
202
- secara real-time untuk memprediksi tonase sampah basah akibat rembesan air hujan.</p>
203
  </div>
204
  <div class="panel feature-card">
205
  <div class="feature-icon font-display">03</div>
206
  <h3 class="feature-name">Pemantauan Berita Sampah 1 Jam</h3>
207
- <p class="feature-desc">AI Scheduler internal merayap internet secara berkala tiap 1 jam untuk
208
- menangkap isu operasional, pembatasan Bantargebang, dan regulasi persampahan DKI Jakarta.</p>
209
  </div>
210
  <div class="panel feature-card">
211
  <div class="feature-icon font-display">04</div>
212
  <h3 class="feature-name">Kalibrasi Spasial 44 Kecamatan</h3>
213
- <p class="feature-desc">Model peramalan dikalibrasi hulu-hilir menggunakan data primer Dinas
214
- Lingkungan Hidup DKI Jakarta, mencakup tonase baseline spesifik tiap kecamatan secara akuntabel.
215
- </p>
216
  </div>
217
  </div>
218
  </section>
219
 
220
  <!-- Cinematic Waste Crisis Scroll Section -->
221
- <section class="container crisis-story-section"
222
- style="margin-top: 4rem; display: grid; grid-template-columns: 1.2fr 0.8fr; gap: 3rem; position: relative;">
223
-
224
  <!-- Sticky Left Panel (Waste Tower Visualizer) -->
225
- <div class="sticky-visualizer-panel"
226
- style="position: sticky; top: 120px; height: 450px; display: flex; flex-direction: column; justify-content: center; align-items: center; background: var(--bg-panel); border: 1px solid var(--border-color); border-radius: 16px; padding: 2rem; overflow: hidden; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.04);">
227
- <h3
228
- style="font-family: var(--font-display); font-size: 1.1rem; color: var(--text-main); margin-bottom: 1.5rem; letter-spacing: 1px; text-transform: uppercase;">
229
- JAKARTA DAILY ACCUMULATION TOWER</h3>
230
-
231
  <!-- The 3D Three.js Container -->
232
- <div id="threejs-waste-container"
233
- style="width: 220px; height: 280px; position: relative; display: flex; justify-content: center; align-items: center; background: rgba(0,0,0,0.01); border-radius: 12px; border: 1px solid var(--border-color); overflow: hidden;">
234
  <!-- Static Overlay height labels inside 3D scene -->
235
- <div
236
- style="position: absolute; right: 12px; top: 12px; font-family: var(--font-mono); font-size: 9px; color: var(--text-muted); opacity: 0.6; z-index: 2; pointer-events: none;">
237
- CRITICAL</div>
238
- <div
239
- style="position: absolute; right: 12px; top: 50%; transform: translateY(-50%); font-family: var(--font-mono); font-size: 9px; color: var(--text-muted); opacity: 0.6; z-index: 2; pointer-events: none;">
240
- WARNING</div>
241
- <div
242
- style="position: absolute; right: 12px; bottom: 12px; font-family: var(--font-mono); font-size: 9px; color: var(--text-muted); opacity: 0.6; z-index: 2; pointer-events: none;">
243
- NORMAL</div>
244
  </div>
245
-
246
  <!-- Live Ticking Indicator -->
247
  <div class="tower-status" style="margin-top: 1.5rem; text-align: center;">
248
- <div
249
- style="font-family: var(--font-mono); font-size: 0.8rem; color: var(--text-muted); text-transform: uppercase;">
250
- Simulated Tonnage</div>
251
- <div style="font-family: var(--font-mono); font-size: 1.8rem; color: var(--yellow); font-weight: bold;"
252
- id="simulated-tons-val">0 Tons</div>
253
  </div>
254
  </div>
255
 
256
  <!-- Scrolling Narrative Cards -->
257
- <div class="story-scroll-cards"
258
- style="display: flex; flex-direction: column; gap: 6rem; padding-bottom: 8rem;">
259
-
260
  <!-- Card 1 -->
261
- <div class="story-card panel" data-height="25" data-tons="2264"
262
- style="padding: 2.2rem; min-height: 250px; display: flex; flex-direction: column; justify-content: center; transition: all 0.3s; border:1px solid var(--border-color); border-radius:12px;">
263
- <span
264
- style="font-family: var(--font-mono); font-size: 0.75rem; color: var(--green); letter-spacing: 2px; text-transform: uppercase; margin-bottom: 0.5rem; display: block;">STAGE
265
- 01: MORNING LOADS (09:00 AM)</span>
266
- <h3
267
- style="font-family: var(--font-display); font-size: 1.3rem; color: var(--text-main); margin-bottom: 0.8rem;">
268
- 2,264 TON SAMPAH TERKUMPUL</h3>
269
- <p style="font-size: 0.9rem; color: var(--text-muted); line-height: 1.6;">Memasuki jam kerja awal,
270
- tumpukan sampah dari pasar tradisional dan wilayah residensial se-DKI Jakarta mulai berdatangan
271
- di TPS. Kecepatan pengangkutan sangat krusial agar TPS lokal tidak mengalami kelumpuhan
272
- penumpukan.</p>
273
  </div>
274
 
275
  <!-- Card 2 -->
276
- <div class="story-card panel" data-height="55" data-tons="4982"
277
- style="padding: 2.2rem; min-height: 250px; display: flex; flex-direction: column; justify-content: center; transition: all 0.3s; border:1px solid var(--border-color); border-radius:12px;">
278
- <span
279
- style="font-family: var(--font-mono); font-size: 0.75rem; color: var(--green); letter-spacing: 2px; text-transform: uppercase; margin-bottom: 0.5rem; display: block;">STAGE
280
- 02: MID-DAY PEAK (03:00 PM)</span>
281
- <h3
282
- style="font-family: var(--font-display); font-size: 1.3rem; color: var(--text-main); margin-bottom: 0.8rem;">
283
- 4,982 TON SAMPAH TERKUMPUL</h3>
284
- <p style="font-size: 0.9rem; color: var(--text-muted); line-height: 1.6;">Di siang hari, aktivitas
285
- logistik pengiriman sampah ke TPST Bantargebang memuncak. Kepadatan lalu lintas jalan tol
286
- Jakarta-Bekasi dan antrean panjang truk di gerbang timbang Bantargebang mulai memicu bottleneck
287
- operasional pengangkutan.</p>
288
  </div>
289
 
290
  <!-- Card 3 -->
291
- <div class="story-card panel" data-height="95" data-tons="9059"
292
- style="padding: 2.2rem; min-height: 250px; display: flex; flex-direction: column; justify-content: center; transition: all 0.3s; border:1px solid var(--border-color); border-radius:12px;">
293
- <span
294
- style="font-family: var(--font-mono); font-size: 0.75rem; color: var(--red); letter-spacing: 2px; text-transform: uppercase; margin-bottom: 0.5rem; display: block;">STAGE
295
- 03: EOD OVERLOAD (11:59 PM)</span>
296
- <h3
297
- style="font-family: var(--font-display); font-size: 1.3rem; color: var(--text-main); margin-bottom: 0.8rem;">
298
- 9,059 TON SAMPAH HARIAN</h3>
299
- <p style="font-size: 0.9rem; color: var(--text-muted); line-height: 1.6;">Sebelum hari berakhir,
300
- total timbulan sampah DKI mencapai kapasitas maksimalnya. TPST Bantargebang yang kini melebihi
301
- daya tampung aman (ketinggian gunungan sampah di atas 50 meter) berada pada status siaga kritis.
302
- Di sinilah Aeterna AI memitigasi krisis tersebut dengan analisis logistik prediktif.</p>
303
  </div>
304
 
305
  </div>
@@ -309,73 +252,35 @@
309
  <section class="container data-sources-section">
310
  <h2 class="section-title">TRANSPARANSI SUMBER DATA</h2>
311
  <div class="panel sources-panel">
312
- <div class="sources-grid"
313
- style="display:grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap:1.5rem;">
314
  <div class="source-item">
315
- <h4 style="color:var(--cyan); margin-bottom:0.5rem; font-family:var(--font-display);">1.
316
- Timbulan Sampah</h4>
317
- <p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;">Data baseline
318
- disesuaikan dengan volume total ~9.059 Ton/hari dari <strong>Dinas Lingkungan Hidup (DLH)
319
- DKI Jakarta</strong> dan SIPSN Kementerian LHK.</p>
320
  </div>
321
  <div class="source-item">
322
- <h4 style="color:var(--cyan); margin-bottom:0.5rem; font-family:var(--font-display);">2.
323
- Prediksi Cuaca</h4>
324
- <p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;">Data curah hujan ditarik
325
- live menggunakan <strong>Open-Meteo API</strong> berdasarkan koordinat geografis presisi
326
- masing-masing kecamatan.</p>
327
  </div>
328
  <div class="source-item">
329
- <h4 style="color:var(--cyan); margin-bottom:0.5rem; font-family:var(--font-display);">3.
330
- Kalender Event & Transit</h4>
331
- <p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;">Pola mobilitas
332
- disimulasikan dari ridership harian <strong>MRT Jakarta</strong> (~85.000) dan kalender
333
- acara Jakarta untuk mengukur lonjakan kerumunan.</p>
334
  </div>
335
  <div class="source-item">
336
- <h4 style="color:var(--cyan); margin-bottom:0.5rem; font-family:var(--font-display);">4. Jarak
337
- Pengangkutan</h4>
338
- <p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;">Jarak tempuh dihitung
339
- menggunakan <strong>Haversine Formula</strong> dari koordinat kecamatan ke TPST Bantargebang
340
- dengan faktor jalan winding 1.35x.</p>
341
  </div>
342
  </div>
343
  </div>
344
  </section>
345
 
346
-
347
-
348
- <!-- BIO DEVELOPER -->
349
- <section class="container developer-container" style="margin-top: 2rem; margin-bottom: 2.5rem;">
350
- <div class="panel developer-panel" style="text-align: center; padding: 2.2rem;">
351
- <h3
352
- style="font-family: var(--font-display); font-size: 1.3rem; font-weight: 700; color: var(--text-main); letter-spacing: 1.5px; margin-bottom: 0.8rem; text-transform: uppercase;">
353
- FARIL PUTRA PRATAMA</h3>
354
- <p
355
- style="color: var(--text-muted); font-size: 0.92rem; line-height: 1.6; max-width: 780px; margin: 0 auto 1.5rem auto; font-family: var(--font-body);">
356
- Merancang dan membangun seluruh arsitektur Aeterna AI dari ujung ke ujung. Meliputi riset data,
357
- pelatihan model cerdas GBR &amp; GridSearchCV, pembuatan arsitektur micro-service asinkron, hingga
358
- visualisasi antarmuka HUD Glassmorphism interaktif.
359
- </p>
360
- <div style="display: flex; gap: 12px; justify-content: center; align-items: center; flex-wrap: wrap;">
361
- <a href="https://github.com/FARILtau72/Aeterna-Ai" target="_blank" rel="noopener noreferrer"
362
- class="action-btn"
363
- style="display: inline-flex; align-items: center; gap: 8px; text-decoration: none; padding: 10px 22px;">
364
- <svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24">
365
- <path
366
- d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
367
- </svg>
368
- GitHub
369
- </a>
370
- <a href="https://www.linkedin.com/in/faril-putra-pratama-81561a280/" target="_blank"
371
- rel="noopener noreferrer" class="action-btn secondary-btn"
372
- style="display: inline-flex; align-items: center; gap: 8px; text-decoration: none; padding: 10px 22px;">
373
- <svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24">
374
- <path
375
- d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.762-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z" />
376
- </svg>
377
- LinkedIn
378
- </a>
379
  </div>
380
  </div>
381
  </section>
@@ -385,39 +290,29 @@
385
  <div id="page-autopilot" class="page-container">
386
  <section class="container page-header-section">
387
  <h2 class="section-title">AETERNA AI AUTOPILOT FORECASTER</h2>
388
- <p class="section-subtitle">Sistem peramalan otonom DKI Jakarta. AI berjalan mandiri memprediksi volume
389
- harian dan mengoordinasikan logistik tanpa campur tangan pengguna.</p>
390
  </section>
391
 
392
  <!-- Live Metrics Summary -->
393
- <section class="container autopilot-grid"
394
- style="display:grid; grid-template-columns: 1.2fr 0.8fr; gap:1.5rem; margin-top:1.5rem;">
395
  <!-- Panel Ringkasan Otonom -->
396
  <div class="panel autopilot-summary-panel">
397
  <h3 class="panel-title">LIVE CITY-WIDE FORECAST (TODAY)</h3>
398
- <div class="stats-row"
399
- style="display:grid; grid-template-columns: 1fr 1fr; gap:1rem; margin-bottom:1.5rem; width:100%;">
400
- <div class="panel stat-card text-glow"
401
- style="background: rgba(0, 0, 0, 0.02); display:flex; flex-direction:column; padding:1.2rem; border-radius:8px;">
402
  <span class="card-label">TOTAL DKI JAKARTA VOLUME</span>
403
- <span id="auto-total-volume" class="card-value"
404
- style="font-size:1.8rem; font-weight:800; color:var(--cyan);">Calculating...</span>
405
  <span class="card-meta">Autonomous prediction summation</span>
406
  </div>
407
- <div class="panel stat-card"
408
- style="background: rgba(0, 0, 0, 0.02); display:flex; flex-direction:column; padding:1.2rem; border-radius:8px;">
409
  <span class="card-label">TOTAL DISPATCHED TRUCKS</span>
410
- <span id="auto-total-trucks" class="card-value"
411
- style="font-size:1.8rem; font-weight:800; color:var(--text-main);">Calculating...</span>
412
  <span class="card-meta">Fleet size for all 44 kecamatan</span>
413
  </div>
414
  </div>
415
-
416
- <h4
417
- style="color:var(--cyan); margin-bottom:0.8rem; font-family:var(--font-display); font-size:0.95rem; border-left:3px solid var(--cyan); padding-left:8px; text-transform:uppercase;">
418
- Top 5 High-Risk Kecamatan Today:</h4>
419
- <div id="auto-risk-list" class="auto-risk-list"
420
- style="display:flex; flex-direction:column; gap:0.8rem;">
421
  <!-- Dynamically populated Top 5 -->
422
  </div>
423
  </div>
@@ -425,67 +320,15 @@
425
  <!-- Konsol Berpikir AI -->
426
  <div class="panel autopilot-console-panel" style="display:flex; flex-direction:column; height:100%;">
427
  <h3 class="panel-title">AI THINKING CONSOLE</h3>
428
- <div id="autopilot-log"
429
- style="flex:1; background:#0B1310; border:1px solid var(--border-color); border-radius:8px; padding:1.2rem; font-family:var(--font-mono); font-size:0.8rem; color:#22C55E; overflow-y:auto; min-height:260px; line-height:1.6; box-shadow:inset 0 0 10px rgba(0,0,0,0.5);">
430
  <!-- Dynamic logs -->
431
  </div>
432
  </div>
433
  </section>
434
  </div>
435
 
436
- <!-- PREDICTOR & DECISION DASHBOARD PAGE -->
437
  <div id="page-predictor" class="page-container">
438
- <!-- Top Executive Decision KPIs Header -->
439
- <section class="container decision-header-section">
440
- <div class="decision-header-top">
441
- <div class="decision-title-group">
442
- <h2 class="decision-heading">
443
- <svg class="decision-icon" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#00F0FF" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
444
- <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
445
- </svg>
446
- Dashboard Keputusan &amp; AI Simulator
447
- </h2>
448
- <p class="decision-subtitle">Spatial-temporal forecasting engine &amp; decision support system untuk optimalisasi logistik, alokasi armada hulu-hilir, dan efisiensi anggaran sampah DKI Jakarta.</p>
449
- </div>
450
- </div>
451
-
452
- <!-- Top 3 Decision KPI Cards -->
453
- <div class="decision-kpi-grid">
454
- <!-- KPI 1: Total Volume -->
455
- <div class="panel dec-kpi-card">
456
- <span class="dec-kpi-label">TOTAL VOLUME PREDIKSI</span>
457
- <div class="dec-kpi-val-row">
458
- <span id="dec-total-vol" class="dec-kpi-number text-glow">3079.5</span>
459
- <span class="dec-kpi-unit">TON</span>
460
- </div>
461
- <span id="dec-vol-status" class="dec-kpi-meta positive">
462
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#00F0FF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
463
- Berhasil diprediksi (7 hari)
464
- </span>
465
- </div>
466
-
467
- <!-- KPI 2: Estimasi Biaya Operasional -->
468
- <div class="panel dec-kpi-card">
469
- <span class="dec-kpi-label">ESTIMASI BIAYA OPERASIONAL</span>
470
- <div class="dec-kpi-val-row">
471
- <span id="dec-cost-val" class="dec-kpi-number">Rp 292.600.000</span>
472
- </div>
473
- <span class="dec-kpi-meta">Total Anggaran Logistik & SDM</span>
474
- </div>
475
-
476
- <!-- KPI 3: Efisiensi AI Proaktif vs Reaktif -->
477
- <div class="panel dec-kpi-card">
478
- <span class="dec-kpi-label">EFISIENSI AI (PROAKTIF VS REAKTIF)</span>
479
- <div class="dec-kpi-val-row">
480
- <span id="dec-efficiency-val" class="dec-kpi-number green-glow" style="color: #4ade80;">51.2%</span>
481
- </div>
482
- <span id="dec-saved-val" class="dec-kpi-meta positive" style="color: #4ade80;">
483
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#4ade80" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 18 13.5 8.5 8.5 13.5 1 6"/><polyline points="17 18 23 18 23 12"/></svg>
484
- Hemat Rp 306.600.000 dibanding Reaktif
485
- </span>
486
- </div>
487
- </div>
488
- </section>
489
  <main class="dashboard-grid">
490
  <!-- Panel Kontrol (Sidebar) -->
491
  <section class="panel control-panel">
@@ -500,15 +343,13 @@
500
  <div class="control-group">
501
  <label for="model-select">AI Forecasting Model</label>
502
  <select id="model-select" class="form-control">
503
- <option value="gradient_boosting" selected>Stacking Regressor Ensemble (Real Data - 96.41% Acc)
504
- </option>
505
  <option value="chronos">Amazon Chronos-T5 (Tiny)</option>
506
  </select>
507
  </div>
508
 
509
  <div class="control-group">
510
- <label for="forecast-slider">Forecast Horizon: <span id="forecast-val"
511
- class="value-display">7</span> Days</label>
512
  <input type="range" id="forecast-slider" min="1" max="30" value="7" class="slider">
513
  </div>
514
 
@@ -524,10 +365,8 @@
524
  </div>
525
 
526
  <div class="control-group">
527
- <label for="event-override">Simulasi Populasi <span id="event-override-val"
528
- class="override-display">Auto (BPS Baseline)</span></label>
529
- <input type="range" id="event-override" min="10000" max="700000" step="5000" value="88000"
530
- class="slider">
531
  </div>
532
 
533
  <div class="button-row">
@@ -576,8 +415,7 @@
576
  <section class="analysis-panel">
577
  <div class="panel category-panel">
578
  <h2 class="panel-title">WASTE COMPOSITION BREAKDOWN</h2>
579
- <div class="progress-container"
580
- style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem 1.5rem;">
581
  <div class="progress-item" style="margin-bottom: 0;">
582
  <div class="progress-header">
583
  <span>Organic / Sisa Makanan (~50.2%)</span>
@@ -652,10 +490,6 @@
652
  <span class="event-title">Upcoming Event Calendar</span>
653
  <p class="event-desc" id="event-desc-text">No major events registered for today.</p>
654
  </div>
655
- <button class="action-btn secondary-btn" onclick="openWeatherModal()" style="margin-top: 14px; width: 100%; padding: 10px 14px; font-size: 0.82rem; display: flex; align-items: center; justify-content: center; gap: 8px; border-radius: 8px;">
656
- <span>🌦️ Lihat Prakiraan Cuaca 7 Hari (Open-Meteo)</span>
657
- <span style="font-weight: bold;">&rarr;</span>
658
- </button>
659
  </div>
660
 
661
  <!-- Logistics Plan -->
@@ -683,104 +517,6 @@
683
  </section>
684
  </main>
685
 
686
- <!-- Section 1: Peta Beban Kerja Harian (Full Width) -->
687
- <section class="container" style="margin-top: 1.5rem; margin-bottom: 1.5rem;">
688
- <div class="panel dec-workload-panel">
689
- <div class="dec-panel-header">
690
- <h3 class="dec-panel-title">
691
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#00F0FF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
692
- <rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
693
- <line x1="16" y1="2" x2="16" y2="6"></line>
694
- <line x1="8" y1="2" x2="8" y2="6"></line>
695
- <line x1="3" y1="10" x2="21" y2="10"></line>
696
- </svg>
697
- Peta Beban Kerja Harian (7 Hari Horizon)
698
- </h3>
699
- <span class="dec-panel-badge">Jadwal Shift &amp; Disposisi Armada</span>
700
- </div>
701
-
702
- <div id="dec-workload-grid" class="dec-workload-grid">
703
- <!-- Populated dynamically with 7 day cards -->
704
- </div>
705
- </div>
706
- </section>
707
-
708
- <!-- Section 2: Alokasi Armada Spesifik & Rencana Aksi Kritis (2 Columns) -->
709
- <section class="container decision-details-grid" style="margin-bottom: 1.5rem;">
710
- <!-- Left: Alokasi Armada Spesifik -->
711
- <div class="panel dec-fleet-panel">
712
- <div class="dec-panel-header">
713
- <h3 class="dec-panel-title">
714
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#38bdf8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
715
- <rect x="1" y="3" width="15" height="13"></rect>
716
- <polygon points="16 8 20 8 23 11 23 16 16 16 8"></polygon>
717
- <circle cx="5.5" cy="18.5" r="2.5"></circle>
718
- <circle cx="18.5" cy="18.5" r="2.5"></circle>
719
- </svg>
720
- Alokasi Armada Spesifik
721
- </h3>
722
- <span class="dec-panel-badge">Rasio 60:40 Dump vs Pick-up</span>
723
- </div>
724
- <div class="dec-fleet-list">
725
- <!-- Truk Besar Dump Truck -->
726
- <div class="dec-fleet-card dump-truck">
727
- <div class="fleet-icon-box blue-box">
728
- <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#38bdf8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
729
- <rect x="1" y="3" width="15" height="13"></rect>
730
- <polygon points="16 8 20 8 23 11 23 16 16 16 8"></polygon>
731
- <circle cx="5.5" cy="18.5" r="2.5"></circle>
732
- <circle cx="18.5" cy="18.5" r="2.5"></circle>
733
- </svg>
734
- </div>
735
- <div class="fleet-info">
736
- <div class="fleet-unit-count"><span id="dec-truck-large">125</span> <span class="fleet-unit-suffix">Unit</span></div>
737
- <div class="fleet-unit-type">Truk Besar (Dump Truck 15T)</div>
738
- </div>
739
- </div>
740
-
741
- <!-- Armada Kecil Pick-up -->
742
- <div class="dec-fleet-card pickup-truck">
743
- <div class="fleet-icon-box amber-box">
744
- <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#fbbf24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
745
- <rect x="1" y="3" width="12" height="13"></rect>
746
- <polygon points="13 8 17 8 20 11 20 16 13 16 13 8"></polygon>
747
- <circle cx="4.5" cy="18.5" r="2.5"></circle>
748
- <circle cx="16.5" cy="18.5" r="2.5"></circle>
749
- </svg>
750
- </div>
751
- <div class="fleet-info">
752
- <div class="fleet-unit-count"><span id="dec-truck-small">84</span> <span class="fleet-unit-suffix">Unit</span></div>
753
- <div class="fleet-unit-type">Armada Kecil (Pick-up 2.5T)</div>
754
- </div>
755
- </div>
756
- </div>
757
- </div>
758
-
759
- <!-- Right: Rencana Aksi Kritis -->
760
- <div class="panel dec-action-panel">
761
- <div class="dec-panel-header">
762
- <h3 class="dec-panel-title action-title">
763
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
764
- <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
765
- <line x1="12" y1="9" x2="12" y2="13"></line>
766
- <line x1="12" y1="17" x2="12.01" y2="17"></line>
767
- </svg>
768
- Rencana Aksi Kritis
769
- </h3>
770
- <span class="dec-panel-badge" style="color: #f87171; border-color: rgba(239, 68, 68, 0.3);">Contingency SOP</span>
771
- </div>
772
- <div class="dec-critical-box" id="dec-critical-box">
773
- <div class="dec-critical-header">
774
- <span class="critical-pulse-dot"></span>
775
- <span id="dec-critical-title" class="dec-critical-title">AKSI HARI JUM (HUJAN LEBAT &amp; BEBAN PUNCAK)</span>
776
- </div>
777
- <ul id="dec-critical-list" class="dec-critical-list">
778
- <!-- Populated dynamically with SOP actions -->
779
- </ul>
780
- </div>
781
- </div>
782
- </section>
783
-
784
  <!-- Timeline Harian -->
785
  <section class="container timeline-container">
786
  <div class="panel timeline-panel">
@@ -804,32 +540,17 @@
804
  <!-- Developer Bio Card -->
805
  <section class="container developer-container" style="margin-top: 2rem; margin-bottom: 2.5rem;">
806
  <div class="panel developer-panel" style="text-align: center; padding: 2.2rem;">
807
- <h3
808
- style="font-family: var(--font-display); font-size: 1.3rem; font-weight: 700; color: var(--text-main); letter-spacing: 1.5px; margin-bottom: 0.8rem; text-transform: uppercase;">
809
- FARIL PUTRA PRATAMA</h3>
810
- <p
811
- style="color: var(--text-muted); font-size: 0.92rem; line-height: 1.6; max-width: 780px; margin: 0 auto 1.5rem auto; font-family: var(--font-body);">
812
- Merancang dan membangun seluruh arsitektur Aeterna AI dari ujung ke ujung. Meliputi riset data,
813
- pelatihan model cerdas GBR &amp; GridSearchCV, pembuatan arsitektur micro-service asinkron, hingga
814
- visualisasi antarmuka HUD Glassmorphism interaktif.
815
  </p>
816
  <div style="display: flex; gap: 12px; justify-content: center; align-items: center; flex-wrap: wrap;">
817
- <a href="https://github.com/FARILtau72/Aeterna-Ai" target="_blank" rel="noopener noreferrer"
818
- class="action-btn"
819
- style="display: inline-flex; align-items: center; gap: 8px; text-decoration: none; padding: 10px 22px;">
820
- <svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24">
821
- <path
822
- d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
823
- </svg>
824
  GitHub
825
  </a>
826
- <a href="https://www.linkedin.com/in/faril-putra-pratama-81561a280/" target="_blank"
827
- rel="noopener noreferrer" class="action-btn secondary-btn"
828
- style="display: inline-flex; align-items: center; gap: 8px; text-decoration: none; padding: 10px 22px;">
829
- <svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24">
830
- <path
831
- d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.762-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z" />
832
- </svg>
833
  LinkedIn
834
  </a>
835
  </div>
@@ -841,8 +562,7 @@
841
  <div id="page-news" class="page-container">
842
  <section class="container page-header-section">
843
  <h2 class="section-title">MONITORING BERITA PERSAMPAHAN JAKARTA</h2>
844
- <p class="section-subtitle">AI merayap berita terbaru seputar isu darurat sampah, operasional TPA
845
- Bantargebang, dan kebijakan DLH Jakarta secara otomatis setiap 1 jam.</p>
846
  </section>
847
 
848
  <section class="container news-feed-container">
@@ -857,8 +577,7 @@
857
  <div id="page-alerts" class="page-container">
858
  <section class="container page-header-section">
859
  <h2 class="section-title">REGIONAL HAZARD & ALERTS MAP</h2>
860
- <p class="section-subtitle">Daftar wilayah kecamatan dengan prakiraan volume timbulan sampah melebihi ambang
861
- batas operasional harian.</p>
862
  </section>
863
 
864
  <section class="container alerts-feed-container">
@@ -876,51 +595,33 @@
876
  <div id="page-education" class="page-container">
877
  <section class="container page-header-section">
878
  <h2 class="section-title">WASTE EDUCATION HUB</h2>
879
- <p class="section-subtitle">Pelajari dampak penumpukan sampah di DKI Jakarta, durasi dekomposisi material,
880
- dan uji ketangkasan pilah sampah Anda secara interaktif.</p>
881
  </section>
882
 
883
  <!-- Interactive simulator & info panels -->
884
- <section class="container education-grid"
885
- style="display:grid; grid-template-columns: 1.2fr 0.8fr; gap:1.5rem; margin-top:1.5rem;">
886
  <!-- Game Pilah Sampah (Eco-Sorter) -->
887
- <div class="panel game-panel"
888
- style="display:flex; flex-direction:column; padding:2rem; position:relative; overflow:hidden;">
889
- <div class="panel-badge"
890
- style="position:absolute; top:1rem; right:1.5rem; background:rgba(0, 240, 255, 0.1); border:1px solid var(--cyan); color:var(--cyan); border-radius:12px; padding:3px 8px; font-size:0.75rem; font-family:var(--font-mono);">
891
- INTERACTIVE GAME</div>
892
  <h3 class="panel-title" style="margin-bottom:0.5rem;">ECO-SORTER SIMULATOR</h3>
893
- <p style="font-size:0.85rem; color:var(--text-muted); margin-bottom:1.5rem;">Pilah item sampah ke tempat
894
- sampah yang benar untuk mendapatkan skor dan mempelajari fakta daur ulang!</p>
895
-
896
  <!-- Waste Item Card -->
897
- <div class="game-area"
898
- style="display:flex; flex-direction:column; align-items:center; background:rgba(0,0,0,0.4); border:1px solid var(--border-color); border-radius:12px; padding:2.5rem; margin-bottom:1.5rem; text-align:center;">
899
- <div id="game-item-icon"
900
- style="font-size:4rem; margin-bottom:1rem; filter:drop-shadow(0 0 10px rgba(255,255,255,0.2)); transition:transform 0.3s;">
901
- 🍼</div>
902
- <h4 id="game-item-name"
903
- style="font-family:var(--font-display); font-size:1.3rem; color:#FFF; margin-bottom:0.5rem;">
904
- Botol Plastik Bekas</h4>
905
- <p id="game-item-desc"
906
- style="font-size:0.85rem; color:var(--text-muted); max-width:300px; min-height:40px;">Botol air
907
- mineral kosong berbahan PET.</p>
908
  </div>
909
-
910
  <!-- Target Bins -->
911
  <div class="bin-grid" style="display:grid; grid-template-columns: repeat(3, 1fr); gap:1rem;">
912
- <button class="bin-btn organic-bin" onclick="sortWaste('organic')"
913
- style="background:rgba(74, 222, 128, 0.05); border:1px solid #4ade80; color:#4ade80; padding:1rem; border-radius:8px; font-family:var(--font-display); font-weight:bold; cursor:pointer; transition:all 0.2s; outline:none;">ORGANIK</button>
914
- <button class="bin-btn inorganic-bin" onclick="sortWaste('inorganic')"
915
- style="background:rgba(56, 189, 248, 0.05); border:1px solid #38bdf8; color:#38bdf8; padding:1rem; border-radius:8px; font-family:var(--font-display); font-weight:bold; cursor:pointer; transition:all 0.2s; outline:none;">ANORGANIK</button>
916
- <button class="bin-btn hazard-bin" onclick="sortWaste('hazardous')"
917
- style="background:rgba(251, 113, 133, 0.05); border:1px solid #fb7185; color:#fb7185; padding:1rem; border-radius:8px; font-family:var(--font-display); font-weight:bold; cursor:pointer; transition:all 0.2s; outline:none;">BAHAYA
918
- (B3)</button>
919
  </div>
920
-
921
  <!-- Game Stats -->
922
- <div class="game-stats"
923
- style="display:flex; justify-content:space-between; align-items:center; margin-top:1.5rem; font-family:var(--font-mono); font-size:0.9rem; padding:0.8rem; background:rgba(255,255,255,0.02); border-radius:6px; border:1px solid rgba(255,255,255,0.05);">
924
  <span>Skor: <strong id="game-score" style="color:var(--cyan); font-size:1.1rem;">0</strong></span>
925
  <span id="game-feedback" style="color:var(--text-muted); font-weight:bold;">Ayo mulai pilah!</span>
926
  </div>
@@ -929,71 +630,47 @@
929
  <!-- Fakta Dekomposisi Sampah -->
930
  <div class="panel facts-panel" style="display:flex; flex-direction:column; padding:2rem;">
931
  <h3 class="panel-title" style="margin-bottom:1rem;">DURASI DEKOMPOSISI ALAM</h3>
932
- <p style="font-size:0.85rem; color:var(--text-muted); margin-bottom:1.5rem;">Berapa lama alam
933
- membutuhkan waktu untuk mengurai barang-barang yang kita buang?</p>
934
-
935
- <div class="facts-list"
936
- style="display:flex; flex-direction:column; gap:1.2rem; flex:1; justify-content:center;">
937
  <div class="fact-item" style="display:flex; align-items:center; gap:1rem;">
938
- <span
939
- style="font-size:2rem; background:rgba(255,255,255,0.05); width:50px; height:50px; border-radius:50%; display:flex; align-items:center; justify-content:center;">🍎</span>
940
  <div style="flex:1;">
941
  <h4 style="font-size:0.9rem; color:#FFF; margin-bottom:2px;">Sisa Makanan / Organik</h4>
942
- <div class="progress-bar-bg" style="height:6px; margin:4px 0;">
943
- <div class="progress-bar-fill organic" style="width: 5%;"></div>
944
- </div>
945
- <span style="font-size:0.75rem; color:#4ade80; font-family:var(--font-mono);">1 - 2
946
- Minggu</span>
947
  </div>
948
  </div>
949
  <div class="fact-item" style="display:flex; align-items:center; gap:1rem;">
950
- <span
951
- style="font-size:2rem; background:rgba(255,255,255,0.05); width:50px; height:50px; border-radius:50%; display:flex; align-items:center; justify-content:center;">📦</span>
952
  <div style="flex:1;">
953
  <h4 style="font-size:0.9rem; color:#FFF; margin-bottom:2px;">Kertas & Kardus</h4>
954
- <div class="progress-bar-bg" style="height:6px; margin:4px 0;">
955
- <div class="progress-bar-fill plastic" style="width: 15%;"></div>
956
- </div>
957
- <span style="font-size:0.75rem; color:#38bdf8; font-family:var(--font-mono);">2 - 6
958
- Minggu</span>
959
  </div>
960
  </div>
961
  <div class="fact-item" style="display:flex; align-items:center; gap:1rem;">
962
- <span
963
- style="font-size:2rem; background:rgba(255,255,255,0.05); width:50px; height:50px; border-radius:50%; display:flex; align-items:center; justify-content:center;">🍼</span>
964
  <div style="flex:1;">
965
  <h4 style="font-size:0.9rem; color:#FFF; margin-bottom:2px;">Botol Plastik PET</h4>
966
- <div class="progress-bar-bg" style="height:6px; margin:4px 0;">
967
- <div class="progress-bar-fill other" style="width: 70%; background:#fb923c !important;">
968
- </div>
969
- </div>
970
- <span style="font-size:0.75rem; color:#fb923c; font-family:var(--font-mono);">450
971
- Tahun</span>
972
  </div>
973
  </div>
974
  <div class="fact-item" style="display:flex; align-items:center; gap:1rem;">
975
- <span
976
- style="font-size:2rem; background:rgba(255,255,255,0.05); width:50px; height:50px; border-radius:50%; display:flex; align-items:center; justify-content:center;">🔋</span>
977
  <div style="flex:1;">
978
  <h4 style="font-size:0.9rem; color:#FFF; margin-bottom:2px;">Baterai & Logam</h4>
979
- <div class="progress-bar-bg" style="height:6px; margin:4px 0;">
980
- <div class="progress-bar-fill critical" style="width: 85%;"></div>
981
- </div>
982
- <span style="font-size:0.75rem; color:#fb7185; font-family:var(--font-mono);">100 Tahun
983
- (Logam) / Berabad-abad (Kimia B3)</span>
984
  </div>
985
  </div>
986
  <div class="fact-item" style="display:flex; align-items:center; gap:1rem;">
987
- <span
988
- style="font-size:2rem; background:rgba(255,255,255,0.05); width:50px; height:50px; border-radius:50%; display:flex; align-items:center; justify-content:center;">🫙</span>
989
  <div style="flex:1;">
990
  <h4 style="font-size:0.9rem; color:#FFF; margin-bottom:2px;">Botol Kaca</h4>
991
- <div class="progress-bar-bg" style="height:6px; margin:4px 0;">
992
- <div class="progress-bar-fill critical"
993
- style="width: 100%; background:#e879f9 !important;"></div>
994
- </div>
995
- <span style="font-size:0.75rem; color:#e879f9; font-family:var(--font-mono);">1 Juta Tahun /
996
- Tidak Hancur</span>
997
  </div>
998
  </div>
999
  </div>
@@ -1001,46 +678,9 @@
1001
  </section>
1002
  </div>
1003
 
1004
- <!-- 7-DAY WEATHER FORECAST MODAL (PRAKIRAAN CUACA 7 HARI) -->
1005
- <div id="weather-modal" class="weather-modal-backdrop" onclick="if(event.target === this) closeWeatherModal()">
1006
- <div class="weather-modal-dialog">
1007
- <div class="weather-modal-header">
1008
- <div class="weather-modal-title-row">
1009
- <h3 class="weather-modal-title">Prakiraan Cuaca 7 Hari</h3>
1010
- <span id="modal-weather-loc" class="weather-location-pill">Cakung</span>
1011
- </div>
1012
- <button class="weather-modal-close-btn" onclick="closeWeatherModal()" title="Tutup Modal">✕</button>
1013
- </div>
1014
-
1015
- <div class="weather-modal-summary-card">
1016
- <div class="summary-left">
1017
- <span class="summary-card-label">SUMMARY 7 HARI</span>
1018
- <div class="summary-card-stats">
1019
- Total: <span id="modal-sum-total" class="summary-val-highlight">14.0mm</span>
1020
- <span class="summary-separator">•</span>
1021
- Avg: <span id="modal-sum-avg" class="summary-val-highlight">2.0mm/hr</span>
1022
- </div>
1023
- </div>
1024
- <div class="summary-right">
1025
- <span id="modal-sum-badge" class="weather-dryness-badge dry">KERING</span>
1026
- </div>
1027
- </div>
1028
-
1029
- <div class="weather-modal-forecast-list" id="weather-modal-list">
1030
- <!-- Dynamically populated with 7 real days from Open-Meteo API -->
1031
- <div class="weather-modal-loading">Mengambil data cuaca real-time Open-Meteo...</div>
1032
- </div>
1033
- </div>
1034
- </div>
1035
-
1036
  <footer>
1037
- <p>&copy; 2026 Aeterna AI — Developed & Engineered with ⚡ by <span
1038
- style="color:var(--cyan); font-weight:bold;">Faril Putra Pratama</span>.</p>
1039
- <p
1040
- style="margin-top: 0.6rem; font-size: 0.75rem; opacity: 0.85; letter-spacing: 0.5px; font-family: var(--font-mono); line-height: 1.5;">
1041
- Official Portal: <a href="https://www.aeternaai.biz.id/"
1042
- style="color:#00f2fe; text-decoration:none;">aeternaai.biz.id</a> | #1 AI Waste Intelligence Platform
1043
- for 44 Kecamatans in DKI Jakarta.</p>
1044
  </footer>
1045
 
1046
  <!-- Custom Cyber HUD Cursor -->
@@ -1050,5 +690,4 @@
1050
  <!-- Scripts -->
1051
  <script src="/static/app.js"></script>
1052
  </body>
1053
-
1054
- </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <meta name="google-site-verification" content="Uv5ENuSBUKzeJte5ypBe6VGuYhEAZPqszpdCJPIawq4" />
7
  <title>Aeterna AI - #1 AI Prediksi Sampah Jakarta & DKI Jakarta | By Faril Putra Pratama</title>
8
+
9
  <!-- Primary SEO Meta Tags -->
10
  <meta name="title" content="Aeterna AI - #1 AI Prediksi Sampah Jakarta & DKI Jakarta | By Faril Putra Pratama">
11
+ <meta name="description" content="Aeterna AI (aeternaai.biz.id) adalah platform AI Prediksi Sampah #1 untuk 44 Kecamatan DKI Jakarta (ai prediksi sampah jkt) dikembangkan oleh Faril Putra Pratama berbasis BPS Jumlah Jiwa & Amazon Chronos T5.">
12
+ <meta name="keywords" content="ai prediksi sampah, ai prediksi sampah jkt, ai prediksi sampah jakarta, aeterna ai, aeterna ai jakarta, aeternaai.biz.id, Faril Putra Pratama, FARILtau72, Prediksi Sampah Jakarta, DLH DKI Jakarta, AI Persampahan, Amazon Chronos T5, BPS Jakarta Jumlah Jiwa, Smart City Jakarta, TPST Bantargebang">
 
 
13
  <meta name="author" content="Faril Putra Pratama">
14
  <meta name="developer" content="Faril Putra Pratama (@FARILtau72)">
15
  <meta name="github:repository" content="https://github.com/FARILtau72/Aeterna-Ai">
 
22
  <meta property="og:type" content="website">
23
  <meta property="og:url" content="https://www.aeternaai.biz.id/">
24
  <meta property="og:title" content="Aeterna AI (AI Prediksi Sampah JKT) by Faril Putra Pratama">
25
+ <meta property="og:description" content="Platform AI Prediksi Sampah #1 untuk 44 Kecamatan DKI Jakarta dikembangkan oleh Faril Putra Pratama. GitHub: https://github.com/FARILtau72/Aeterna-Ai | LinkedIn: https://www.linkedin.com/in/faril-putra-pratama-81561a280/">
 
26
  <meta property="og:image" content="https://www.aeternaai.biz.id/static/assets/og_banner.png">
27
  <meta property="og:site_name" content="Aeterna AI">
28
  <meta property="og:locale" content="id_ID">
 
31
  <meta name="twitter:card" content="summary_large_image">
32
  <meta name="twitter:url" content="https://www.aeternaai.biz.id/">
33
  <meta name="twitter:title" content="Aeterna AI by Faril Putra Pratama - #1 Waste Intelligence Platform DKI Jakarta">
34
+ <meta name="twitter:description" content="Sistem AI peramalan sampah & armada logistik 44 Kecamatan DKI Jakarta dikembangkan oleh Faril Putra Pratama.">
 
35
  <meta name="twitter:image" content="https://www.aeternaai.biz.id/static/assets/og_banner.png">
36
 
37
  <!-- GEO Meta Citations for LLMs (ChatGPT, Gemini, Claude, Perplexity) -->
38
+ <meta name="citation_title" content="Aeterna AI: Spatial-Temporal Waste Generation Forecasting Across 44 Kecamatans in DKI Jakarta">
 
39
  <meta name="citation_author" content="Faril Putra Pratama (@FARILtau72)">
40
  <meta name="citation_publication_date" content="2026">
41
+ <meta name="citation_technical_report_institution" content="Aeterna AI Labs & Faril Putra Pratama Official Portal (aeternaai.biz.id)">
 
42
 
43
  <!-- Geo-Location Meta Tags for Regional Indonesian Search Priority -->
44
  <meta name="geo.region" content="ID-JK" />
 
52
  <!-- Google Fonts -->
53
  <link rel="preconnect" href="https://fonts.googleapis.com">
54
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
55
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@300;400;700&display=swap" rel="stylesheet">
56
+
 
 
57
  <!-- Leaflet.js Map Library -->
58
+ <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="" />
59
+ <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
 
 
60
 
61
  <!-- Three.js 3D Library -->
62
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
 
116
  }
117
  </script>
118
  </head>
 
119
  <body>
120
  <!-- Background Canvas for Interactive Particle Rain -->
121
  <canvas id="rain-canvas"></canvas>
 
129
  <nav class="nav-links">
130
  <button class="nav-btn active" data-target="page-home" onclick="switchPage('page-home')">HOME</button>
131
  <button class="nav-btn" data-target="page-autopilot" onclick="switchPage('page-autopilot')">AI AUTOPILOT</button>
132
+ <button class="nav-btn" data-target="page-predictor" onclick="switchPage('page-predictor')">SIMULATION TOOL</button>
133
  <button class="nav-btn" data-target="page-news" onclick="switchPage('page-news')">NEWS FEED</button>
134
  <button class="nav-btn" data-target="page-alerts" onclick="switchPage('page-alerts')">REGIONAL ALERTS</button>
135
  <button class="nav-btn" data-target="page-education" onclick="switchPage('page-education')">WASTE EDUCATION</button>
 
145
  <section class="hero-section">
146
  <div class="hero-content">
147
  <h1 class="hero-title">NEXT-GEN WASTE FORECASTING</h1>
148
+ <p class="hero-subtitle">Meningkatkan efisiensi tata kelola sampah DKI Jakarta hingga 98.28% dengan pemodelan spasial temporal real-time.</p>
 
149
  <div class="hero-actions">
150
  <button class="action-btn" onclick="switchPage('page-autopilot')">
151
  <span class="btn-text">OPEN AUTOPILOT</span>
 
179
  <div class="panel feature-card">
180
  <div class="feature-icon font-display">01</div>
181
  <h3 class="feature-name">Akurasi Validitas Tinggi (98.28%)</h3>
182
+ <p class="feature-desc">Menggunakan arsitektur model Gradient Boosting Regressor (GBR) yang dioptimasi via GridSearchCV dengan metrik komparasi MAE, RMSE, dan MAPE secara berdampingan.</p>
 
183
  </div>
184
  <div class="panel feature-card">
185
  <div class="feature-icon font-display">02</div>
186
  <h3 class="feature-name">Integrasi Live Cuaca Open-Meteo</h3>
187
+ <p class="feature-desc">API dinamis asinkron yang menarik data curah hujan live tingkat kecamatan secara real-time untuk memprediksi tonase sampah basah akibat rembesan air hujan.</p>
 
188
  </div>
189
  <div class="panel feature-card">
190
  <div class="feature-icon font-display">03</div>
191
  <h3 class="feature-name">Pemantauan Berita Sampah 1 Jam</h3>
192
+ <p class="feature-desc">AI Scheduler internal merayap internet secara berkala tiap 1 jam untuk menangkap isu operasional, pembatasan Bantargebang, dan regulasi persampahan DKI Jakarta.</p>
 
193
  </div>
194
  <div class="panel feature-card">
195
  <div class="feature-icon font-display">04</div>
196
  <h3 class="feature-name">Kalibrasi Spasial 44 Kecamatan</h3>
197
+ <p class="feature-desc">Model peramalan dikalibrasi hulu-hilir menggunakan data primer Dinas Lingkungan Hidup DKI Jakarta, mencakup tonase baseline spesifik tiap kecamatan secara akuntabel.</p>
 
 
198
  </div>
199
  </div>
200
  </section>
201
 
202
  <!-- Cinematic Waste Crisis Scroll Section -->
203
+ <section class="container crisis-story-section" style="margin-top: 4rem; display: grid; grid-template-columns: 1.2fr 0.8fr; gap: 3rem; position: relative;">
204
+
 
205
  <!-- Sticky Left Panel (Waste Tower Visualizer) -->
206
+ <div class="sticky-visualizer-panel" style="position: sticky; top: 120px; height: 450px; display: flex; flex-direction: column; justify-content: center; align-items: center; background: var(--bg-panel); border: 1px solid var(--border-color); border-radius: 16px; padding: 2rem; overflow: hidden; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.04);">
207
+ <h3 style="font-family: var(--font-display); font-size: 1.1rem; color: var(--text-main); margin-bottom: 1.5rem; letter-spacing: 1px; text-transform: uppercase;">JAKARTA DAILY ACCUMULATION TOWER</h3>
208
+
 
 
 
209
  <!-- The 3D Three.js Container -->
210
+ <div id="threejs-waste-container" style="width: 220px; height: 280px; position: relative; display: flex; justify-content: center; align-items: center; background: rgba(0,0,0,0.01); border-radius: 12px; border: 1px solid var(--border-color); overflow: hidden;">
 
211
  <!-- Static Overlay height labels inside 3D scene -->
212
+ <div style="position: absolute; right: 12px; top: 12px; font-family: var(--font-mono); font-size: 9px; color: var(--text-muted); opacity: 0.6; z-index: 2; pointer-events: none;">CRITICAL</div>
213
+ <div style="position: absolute; right: 12px; top: 50%; transform: translateY(-50%); font-family: var(--font-mono); font-size: 9px; color: var(--text-muted); opacity: 0.6; z-index: 2; pointer-events: none;">WARNING</div>
214
+ <div style="position: absolute; right: 12px; bottom: 12px; font-family: var(--font-mono); font-size: 9px; color: var(--text-muted); opacity: 0.6; z-index: 2; pointer-events: none;">NORMAL</div>
 
 
 
 
 
 
215
  </div>
216
+
217
  <!-- Live Ticking Indicator -->
218
  <div class="tower-status" style="margin-top: 1.5rem; text-align: center;">
219
+ <div style="font-family: var(--font-mono); font-size: 0.8rem; color: var(--text-muted); text-transform: uppercase;">Simulated Tonnage</div>
220
+ <div style="font-family: var(--font-mono); font-size: 1.8rem; color: var(--yellow); font-weight: bold;" id="simulated-tons-val">0 Tons</div>
 
 
 
221
  </div>
222
  </div>
223
 
224
  <!-- Scrolling Narrative Cards -->
225
+ <div class="story-scroll-cards" style="display: flex; flex-direction: column; gap: 6rem; padding-bottom: 8rem;">
226
+
 
227
  <!-- Card 1 -->
228
+ <div class="story-card panel" data-height="25" data-tons="2264" style="padding: 2.2rem; min-height: 250px; display: flex; flex-direction: column; justify-content: center; transition: all 0.3s; border:1px solid var(--border-color); border-radius:12px;">
229
+ <span style="font-family: var(--font-mono); font-size: 0.75rem; color: var(--green); letter-spacing: 2px; text-transform: uppercase; margin-bottom: 0.5rem; display: block;">STAGE 01: MORNING LOADS (09:00 AM)</span>
230
+ <h3 style="font-family: var(--font-display); font-size: 1.3rem; color: var(--text-main); margin-bottom: 0.8rem;">2,264 TON SAMPAH TERKUMPUL</h3>
231
+ <p style="font-size: 0.9rem; color: var(--text-muted); line-height: 1.6;">Memasuki jam kerja awal, tumpukan sampah dari pasar tradisional dan wilayah residensial se-DKI Jakarta mulai berdatangan di TPS. Kecepatan pengangkutan sangat krusial agar TPS lokal tidak mengalami kelumpuhan penumpukan.</p>
 
 
 
 
 
 
 
 
232
  </div>
233
 
234
  <!-- Card 2 -->
235
+ <div class="story-card panel" data-height="55" data-tons="4982" style="padding: 2.2rem; min-height: 250px; display: flex; flex-direction: column; justify-content: center; transition: all 0.3s; border:1px solid var(--border-color); border-radius:12px;">
236
+ <span style="font-family: var(--font-mono); font-size: 0.75rem; color: var(--green); letter-spacing: 2px; text-transform: uppercase; margin-bottom: 0.5rem; display: block;">STAGE 02: MID-DAY PEAK (03:00 PM)</span>
237
+ <h3 style="font-family: var(--font-display); font-size: 1.3rem; color: var(--text-main); margin-bottom: 0.8rem;">4,982 TON SAMPAH TERKUMPUL</h3>
238
+ <p style="font-size: 0.9rem; color: var(--text-muted); line-height: 1.6;">Di siang hari, aktivitas logistik pengiriman sampah ke TPST Bantargebang memuncak. Kepadatan lalu lintas jalan tol Jakarta-Bekasi dan antrean panjang truk di gerbang timbang Bantargebang mulai memicu bottleneck operasional pengangkutan.</p>
 
 
 
 
 
 
 
 
239
  </div>
240
 
241
  <!-- Card 3 -->
242
+ <div class="story-card panel" data-height="95" data-tons="9059" style="padding: 2.2rem; min-height: 250px; display: flex; flex-direction: column; justify-content: center; transition: all 0.3s; border:1px solid var(--border-color); border-radius:12px;">
243
+ <span style="font-family: var(--font-mono); font-size: 0.75rem; color: var(--red); letter-spacing: 2px; text-transform: uppercase; margin-bottom: 0.5rem; display: block;">STAGE 03: EOD OVERLOAD (11:59 PM)</span>
244
+ <h3 style="font-family: var(--font-display); font-size: 1.3rem; color: var(--text-main); margin-bottom: 0.8rem;">9,059 TON SAMPAH HARIAN</h3>
245
+ <p style="font-size: 0.9rem; color: var(--text-muted); line-height: 1.6;">Sebelum hari berakhir, total timbulan sampah DKI mencapai kapasitas maksimalnya. TPST Bantargebang yang kini melebihi daya tampung aman (ketinggian gunungan sampah di atas 50 meter) berada pada status siaga kritis. Di sinilah Aeterna AI memitigasi krisis tersebut dengan analisis logistik prediktif.</p>
 
 
 
 
 
 
 
 
246
  </div>
247
 
248
  </div>
 
252
  <section class="container data-sources-section">
253
  <h2 class="section-title">TRANSPARANSI SUMBER DATA</h2>
254
  <div class="panel sources-panel">
255
+ <div class="sources-grid" style="display:grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap:1.5rem;">
 
256
  <div class="source-item">
257
+ <h4 style="color:var(--cyan); margin-bottom:0.5rem; font-family:var(--font-display);">1. Timbulan Sampah</h4>
258
+ <p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;">Data baseline disesuaikan dengan volume total ~9.059 Ton/hari dari <strong>Dinas Lingkungan Hidup (DLH) DKI Jakarta</strong> dan SIPSN Kementerian LHK.</p>
 
 
 
259
  </div>
260
  <div class="source-item">
261
+ <h4 style="color:var(--cyan); margin-bottom:0.5rem; font-family:var(--font-display);">2. Prediksi Cuaca</h4>
262
+ <p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;">Data curah hujan ditarik live menggunakan <strong>Open-Meteo API</strong> berdasarkan koordinat geografis presisi masing-masing kecamatan.</p>
 
 
 
263
  </div>
264
  <div class="source-item">
265
+ <h4 style="color:var(--cyan); margin-bottom:0.5rem; font-family:var(--font-display);">3. Kalender Event & Transit</h4>
266
+ <p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;">Pola mobilitas disimulasikan dari ridership harian <strong>MRT Jakarta</strong> (~85.000) dan kalender acara Jakarta untuk mengukur lonjakan kerumunan.</p>
 
 
 
267
  </div>
268
  <div class="source-item">
269
+ <h4 style="color:var(--cyan); margin-bottom:0.5rem; font-family:var(--font-display);">4. Jarak Pengangkutan</h4>
270
+ <p style="font-size:0.85rem; color:var(--text-muted); line-height:1.5;">Jarak tempuh dihitung menggunakan <strong>Haversine Formula</strong> dari koordinat kecamatan ke TPST Bantargebang dengan faktor jalan winding 1.35x.</p>
 
 
 
271
  </div>
272
  </div>
273
  </div>
274
  </section>
275
 
276
+ <!-- Development Author Section -->
277
+ <section class="container developers-section">
278
+ <h2 class="section-title">AETERNA AI LEAD DEVELOPER</h2>
279
+ <div class="developers-grid" style="display: flex; justify-content: center;">
280
+ <div class="panel developer-card" style="max-width: 600px; width: 100%;">
281
+ <div class="dev-role">Lead Full-Stack AI Engineer</div>
282
+ <h3 class="dev-name">FARIL PUTRA PRATAMA</h3>
283
+ <p class="dev-desc">Merancang dan membangun seluruh arsitektur Aeterna AI dari ujung ke ujung. Meliputi riset data, pelatihan model cerdas GBR & GridSearchCV, pembuatan arsitektur micro-service asinkron, hingga visualisasi antarmuka HUD Glassmorphism interaktif Next.js.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
  </div>
285
  </div>
286
  </section>
 
290
  <div id="page-autopilot" class="page-container">
291
  <section class="container page-header-section">
292
  <h2 class="section-title">AETERNA AI AUTOPILOT FORECASTER</h2>
293
+ <p class="section-subtitle">Sistem peramalan otonom DKI Jakarta. AI berjalan mandiri memprediksi volume harian dan mengoordinasikan logistik tanpa campur tangan pengguna.</p>
 
294
  </section>
295
 
296
  <!-- Live Metrics Summary -->
297
+ <section class="container autopilot-grid" style="display:grid; grid-template-columns: 1.2fr 0.8fr; gap:1.5rem; margin-top:1.5rem;">
 
298
  <!-- Panel Ringkasan Otonom -->
299
  <div class="panel autopilot-summary-panel">
300
  <h3 class="panel-title">LIVE CITY-WIDE FORECAST (TODAY)</h3>
301
+ <div class="stats-row" style="display:grid; grid-template-columns: 1fr 1fr; gap:1rem; margin-bottom:1.5rem; width:100%;">
302
+ <div class="panel stat-card text-glow" style="background: rgba(0, 0, 0, 0.02); display:flex; flex-direction:column; padding:1.2rem; border-radius:8px;">
 
 
303
  <span class="card-label">TOTAL DKI JAKARTA VOLUME</span>
304
+ <span id="auto-total-volume" class="card-value" style="font-size:1.8rem; font-weight:800; color:var(--cyan);">Calculating...</span>
 
305
  <span class="card-meta">Autonomous prediction summation</span>
306
  </div>
307
+ <div class="panel stat-card" style="background: rgba(0, 0, 0, 0.02); display:flex; flex-direction:column; padding:1.2rem; border-radius:8px;">
 
308
  <span class="card-label">TOTAL DISPATCHED TRUCKS</span>
309
+ <span id="auto-total-trucks" class="card-value" style="font-size:1.8rem; font-weight:800; color:var(--text-main);">Calculating...</span>
 
310
  <span class="card-meta">Fleet size for all 44 kecamatan</span>
311
  </div>
312
  </div>
313
+
314
+ <h4 style="color:var(--cyan); margin-bottom:0.8rem; font-family:var(--font-display); font-size:0.95rem; border-left:3px solid var(--cyan); padding-left:8px; text-transform:uppercase;">Top 5 High-Risk Kecamatan Today:</h4>
315
+ <div id="auto-risk-list" class="auto-risk-list" style="display:flex; flex-direction:column; gap:0.8rem;">
 
 
 
316
  <!-- Dynamically populated Top 5 -->
317
  </div>
318
  </div>
 
320
  <!-- Konsol Berpikir AI -->
321
  <div class="panel autopilot-console-panel" style="display:flex; flex-direction:column; height:100%;">
322
  <h3 class="panel-title">AI THINKING CONSOLE</h3>
323
+ <div id="autopilot-log" style="flex:1; background:#0B1310; border:1px solid var(--border-color); border-radius:8px; padding:1.2rem; font-family:var(--font-mono); font-size:0.8rem; color:#22C55E; overflow-y:auto; min-height:260px; line-height:1.6; box-shadow:inset 0 0 10px rgba(0,0,0,0.5);">
 
324
  <!-- Dynamic logs -->
325
  </div>
326
  </div>
327
  </section>
328
  </div>
329
 
330
+ <!-- PREDICTOR PAGE -->
331
  <div id="page-predictor" class="page-container">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  <main class="dashboard-grid">
333
  <!-- Panel Kontrol (Sidebar) -->
334
  <section class="panel control-panel">
 
343
  <div class="control-group">
344
  <label for="model-select">AI Forecasting Model</label>
345
  <select id="model-select" class="form-control">
346
+ <option value="gradient_boosting" selected>Stacking Regressor Ensemble (Real Data - 96.41% Acc)</option>
 
347
  <option value="chronos">Amazon Chronos-T5 (Tiny)</option>
348
  </select>
349
  </div>
350
 
351
  <div class="control-group">
352
+ <label for="forecast-slider">Forecast Horizon: <span id="forecast-val" class="value-display">7</span> Days</label>
 
353
  <input type="range" id="forecast-slider" min="1" max="30" value="7" class="slider">
354
  </div>
355
 
 
365
  </div>
366
 
367
  <div class="control-group">
368
+ <label for="event-override">Simulasi Populasi <span id="event-override-val" class="override-display">Auto (BPS Baseline)</span></label>
369
+ <input type="range" id="event-override" min="10000" max="700000" step="5000" value="88000" class="slider">
 
 
370
  </div>
371
 
372
  <div class="button-row">
 
415
  <section class="analysis-panel">
416
  <div class="panel category-panel">
417
  <h2 class="panel-title">WASTE COMPOSITION BREAKDOWN</h2>
418
+ <div class="progress-container" style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem 1.5rem;">
 
419
  <div class="progress-item" style="margin-bottom: 0;">
420
  <div class="progress-header">
421
  <span>Organic / Sisa Makanan (~50.2%)</span>
 
490
  <span class="event-title">Upcoming Event Calendar</span>
491
  <p class="event-desc" id="event-desc-text">No major events registered for today.</p>
492
  </div>
 
 
 
 
493
  </div>
494
 
495
  <!-- Logistics Plan -->
 
517
  </section>
518
  </main>
519
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520
  <!-- Timeline Harian -->
521
  <section class="container timeline-container">
522
  <div class="panel timeline-panel">
 
540
  <!-- Developer Bio Card -->
541
  <section class="container developer-container" style="margin-top: 2rem; margin-bottom: 2.5rem;">
542
  <div class="panel developer-panel" style="text-align: center; padding: 2.2rem;">
543
+ <h3 style="font-family: var(--font-display); font-size: 1.3rem; font-weight: 700; color: var(--text-main); letter-spacing: 1.5px; margin-bottom: 0.8rem; text-transform: uppercase;">FARIL PUTRA PRATAMA</h3>
544
+ <p style="color: var(--text-muted); font-size: 0.92rem; line-height: 1.6; max-width: 780px; margin: 0 auto 1.5rem auto; font-family: var(--font-body);">
545
+ Merancang dan membangun seluruh arsitektur Aeterna AI dari ujung ke ujung. Meliputi riset data, pelatihan model cerdas GBR &amp; GridSearchCV, pembuatan arsitektur micro-service asinkron, hingga visualisasi antarmuka HUD Glassmorphism interaktif.
 
 
 
 
 
546
  </p>
547
  <div style="display: flex; gap: 12px; justify-content: center; align-items: center; flex-wrap: wrap;">
548
+ <a href="https://github.com/FARILtau72/Aeterna-Ai" target="_blank" rel="noopener noreferrer" class="action-btn" style="display: inline-flex; align-items: center; gap: 8px; text-decoration: none; padding: 10px 22px;">
549
+ <svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/></svg>
 
 
 
 
 
550
  GitHub
551
  </a>
552
+ <a href="https://www.linkedin.com/in/faril-putra-pratama-81561a280/" target="_blank" rel="noopener noreferrer" class="action-btn secondary-btn" style="display: inline-flex; align-items: center; gap: 8px; text-decoration: none; padding: 10px 22px;">
553
+ <svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24"><path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.762-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"/></svg>
 
 
 
 
 
554
  LinkedIn
555
  </a>
556
  </div>
 
562
  <div id="page-news" class="page-container">
563
  <section class="container page-header-section">
564
  <h2 class="section-title">MONITORING BERITA PERSAMPAHAN JAKARTA</h2>
565
+ <p class="section-subtitle">AI merayap berita terbaru seputar isu darurat sampah, operasional TPA Bantargebang, dan kebijakan DLH Jakarta secara otomatis setiap 1 jam.</p>
 
566
  </section>
567
 
568
  <section class="container news-feed-container">
 
577
  <div id="page-alerts" class="page-container">
578
  <section class="container page-header-section">
579
  <h2 class="section-title">REGIONAL HAZARD & ALERTS MAP</h2>
580
+ <p class="section-subtitle">Daftar wilayah kecamatan dengan prakiraan volume timbulan sampah melebihi ambang batas operasional harian.</p>
 
581
  </section>
582
 
583
  <section class="container alerts-feed-container">
 
595
  <div id="page-education" class="page-container">
596
  <section class="container page-header-section">
597
  <h2 class="section-title">WASTE EDUCATION HUB</h2>
598
+ <p class="section-subtitle">Pelajari dampak penumpukan sampah di DKI Jakarta, durasi dekomposisi material, dan uji ketangkasan pilah sampah Anda secara interaktif.</p>
 
599
  </section>
600
 
601
  <!-- Interactive simulator & info panels -->
602
+ <section class="container education-grid" style="display:grid; grid-template-columns: 1.2fr 0.8fr; gap:1.5rem; margin-top:1.5rem;">
 
603
  <!-- Game Pilah Sampah (Eco-Sorter) -->
604
+ <div class="panel game-panel" style="display:flex; flex-direction:column; padding:2rem; position:relative; overflow:hidden;">
605
+ <div class="panel-badge" style="position:absolute; top:1rem; right:1.5rem; background:rgba(0, 240, 255, 0.1); border:1px solid var(--cyan); color:var(--cyan); border-radius:12px; padding:3px 8px; font-size:0.75rem; font-family:var(--font-mono);">INTERACTIVE GAME</div>
 
 
 
606
  <h3 class="panel-title" style="margin-bottom:0.5rem;">ECO-SORTER SIMULATOR</h3>
607
+ <p style="font-size:0.85rem; color:var(--text-muted); margin-bottom:1.5rem;">Pilah item sampah ke tempat sampah yang benar untuk mendapatkan skor dan mempelajari fakta daur ulang!</p>
608
+
 
609
  <!-- Waste Item Card -->
610
+ <div class="game-area" style="display:flex; flex-direction:column; align-items:center; background:rgba(0,0,0,0.4); border:1px solid var(--border-color); border-radius:12px; padding:2.5rem; margin-bottom:1.5rem; text-align:center;">
611
+ <div id="game-item-icon" style="font-size:4rem; margin-bottom:1rem; filter:drop-shadow(0 0 10px rgba(255,255,255,0.2)); transition:transform 0.3s;">🍼</div>
612
+ <h4 id="game-item-name" style="font-family:var(--font-display); font-size:1.3rem; color:#FFF; margin-bottom:0.5rem;">Botol Plastik Bekas</h4>
613
+ <p id="game-item-desc" style="font-size:0.85rem; color:var(--text-muted); max-width:300px; min-height:40px;">Botol air mineral kosong berbahan PET.</p>
 
 
 
 
 
 
 
614
  </div>
615
+
616
  <!-- Target Bins -->
617
  <div class="bin-grid" style="display:grid; grid-template-columns: repeat(3, 1fr); gap:1rem;">
618
+ <button class="bin-btn organic-bin" onclick="sortWaste('organic')" style="background:rgba(74, 222, 128, 0.05); border:1px solid #4ade80; color:#4ade80; padding:1rem; border-radius:8px; font-family:var(--font-display); font-weight:bold; cursor:pointer; transition:all 0.2s; outline:none;">ORGANIK</button>
619
+ <button class="bin-btn inorganic-bin" onclick="sortWaste('inorganic')" style="background:rgba(56, 189, 248, 0.05); border:1px solid #38bdf8; color:#38bdf8; padding:1rem; border-radius:8px; font-family:var(--font-display); font-weight:bold; cursor:pointer; transition:all 0.2s; outline:none;">ANORGANIK</button>
620
+ <button class="bin-btn hazard-bin" onclick="sortWaste('hazardous')" style="background:rgba(251, 113, 133, 0.05); border:1px solid #fb7185; color:#fb7185; padding:1rem; border-radius:8px; font-family:var(--font-display); font-weight:bold; cursor:pointer; transition:all 0.2s; outline:none;">BAHAYA (B3)</button>
 
 
 
 
621
  </div>
622
+
623
  <!-- Game Stats -->
624
+ <div class="game-stats" style="display:flex; justify-content:space-between; align-items:center; margin-top:1.5rem; font-family:var(--font-mono); font-size:0.9rem; padding:0.8rem; background:rgba(255,255,255,0.02); border-radius:6px; border:1px solid rgba(255,255,255,0.05);">
 
625
  <span>Skor: <strong id="game-score" style="color:var(--cyan); font-size:1.1rem;">0</strong></span>
626
  <span id="game-feedback" style="color:var(--text-muted); font-weight:bold;">Ayo mulai pilah!</span>
627
  </div>
 
630
  <!-- Fakta Dekomposisi Sampah -->
631
  <div class="panel facts-panel" style="display:flex; flex-direction:column; padding:2rem;">
632
  <h3 class="panel-title" style="margin-bottom:1rem;">DURASI DEKOMPOSISI ALAM</h3>
633
+ <p style="font-size:0.85rem; color:var(--text-muted); margin-bottom:1.5rem;">Berapa lama alam membutuhkan waktu untuk mengurai barang-barang yang kita buang?</p>
634
+
635
+ <div class="facts-list" style="display:flex; flex-direction:column; gap:1.2rem; flex:1; justify-content:center;">
 
 
636
  <div class="fact-item" style="display:flex; align-items:center; gap:1rem;">
637
+ <span style="font-size:2rem; background:rgba(255,255,255,0.05); width:50px; height:50px; border-radius:50%; display:flex; align-items:center; justify-content:center;">🍎</span>
 
638
  <div style="flex:1;">
639
  <h4 style="font-size:0.9rem; color:#FFF; margin-bottom:2px;">Sisa Makanan / Organik</h4>
640
+ <div class="progress-bar-bg" style="height:6px; margin:4px 0;"><div class="progress-bar-fill organic" style="width: 5%;"></div></div>
641
+ <span style="font-size:0.75rem; color:#4ade80; font-family:var(--font-mono);">1 - 2 Minggu</span>
 
 
 
642
  </div>
643
  </div>
644
  <div class="fact-item" style="display:flex; align-items:center; gap:1rem;">
645
+ <span style="font-size:2rem; background:rgba(255,255,255,0.05); width:50px; height:50px; border-radius:50%; display:flex; align-items:center; justify-content:center;">📦</span>
 
646
  <div style="flex:1;">
647
  <h4 style="font-size:0.9rem; color:#FFF; margin-bottom:2px;">Kertas & Kardus</h4>
648
+ <div class="progress-bar-bg" style="height:6px; margin:4px 0;"><div class="progress-bar-fill plastic" style="width: 15%;"></div></div>
649
+ <span style="font-size:0.75rem; color:#38bdf8; font-family:var(--font-mono);">2 - 6 Minggu</span>
 
 
 
650
  </div>
651
  </div>
652
  <div class="fact-item" style="display:flex; align-items:center; gap:1rem;">
653
+ <span style="font-size:2rem; background:rgba(255,255,255,0.05); width:50px; height:50px; border-radius:50%; display:flex; align-items:center; justify-content:center;">🍼</span>
 
654
  <div style="flex:1;">
655
  <h4 style="font-size:0.9rem; color:#FFF; margin-bottom:2px;">Botol Plastik PET</h4>
656
+ <div class="progress-bar-bg" style="height:6px; margin:4px 0;"><div class="progress-bar-fill other" style="width: 70%; background:#fb923c !important;"></div></div>
657
+ <span style="font-size:0.75rem; color:#fb923c; font-family:var(--font-mono);">450 Tahun</span>
 
 
 
 
658
  </div>
659
  </div>
660
  <div class="fact-item" style="display:flex; align-items:center; gap:1rem;">
661
+ <span style="font-size:2rem; background:rgba(255,255,255,0.05); width:50px; height:50px; border-radius:50%; display:flex; align-items:center; justify-content:center;">🔋</span>
 
662
  <div style="flex:1;">
663
  <h4 style="font-size:0.9rem; color:#FFF; margin-bottom:2px;">Baterai & Logam</h4>
664
+ <div class="progress-bar-bg" style="height:6px; margin:4px 0;"><div class="progress-bar-fill critical" style="width: 85%;"></div></div>
665
+ <span style="font-size:0.75rem; color:#fb7185; font-family:var(--font-mono);">100 Tahun (Logam) / Berabad-abad (Kimia B3)</span>
 
 
 
666
  </div>
667
  </div>
668
  <div class="fact-item" style="display:flex; align-items:center; gap:1rem;">
669
+ <span style="font-size:2rem; background:rgba(255,255,255,0.05); width:50px; height:50px; border-radius:50%; display:flex; align-items:center; justify-content:center;">🫙</span>
 
670
  <div style="flex:1;">
671
  <h4 style="font-size:0.9rem; color:#FFF; margin-bottom:2px;">Botol Kaca</h4>
672
+ <div class="progress-bar-bg" style="height:6px; margin:4px 0;"><div class="progress-bar-fill critical" style="width: 100%; background:#e879f9 !important;"></div></div>
673
+ <span style="font-size:0.75rem; color:#e879f9; font-family:var(--font-mono);">1 Juta Tahun / Tidak Hancur</span>
 
 
 
 
674
  </div>
675
  </div>
676
  </div>
 
678
  </section>
679
  </div>
680
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
681
  <footer>
682
+ <p>&copy; 2026 Aeterna AI — Developed & Engineered with ⚡ by <span style="color:var(--cyan); font-weight:bold;">Faril Putra Pratama</span>.</p>
683
+ <p style="margin-top: 0.6rem; font-size: 0.75rem; opacity: 0.85; letter-spacing: 0.5px; font-family: var(--font-mono); line-height: 1.5;">Official Portal: <a href="https://www.aeternaai.biz.id/" style="color:#00f2fe; text-decoration:none;">aeternaai.biz.id</a> | #1 AI Waste Intelligence Platform for 44 Kecamatans in DKI Jakarta.</p>
 
 
 
 
 
684
  </footer>
685
 
686
  <!-- Custom Cyber HUD Cursor -->
 
690
  <!-- Scripts -->
691
  <script src="/static/app.js"></script>
692
  </body>
693
+ </html>
 
frontend/style.css CHANGED
@@ -130,7 +130,7 @@ header {
130
  display: grid;
131
  grid-template-columns: 320px 1fr 340px;
132
  gap: 1.5rem;
133
- padding: 1.2rem 0;
134
  flex: 1;
135
  z-index: 5;
136
  position: relative;
@@ -656,7 +656,7 @@ header {
656
  /* Timeline Container */
657
  .timeline-container {
658
  margin-top: 1.5rem;
659
- padding: 0;
660
  z-index: 5;
661
  position: relative;
662
  }
@@ -725,7 +725,7 @@ header {
725
  /* Hourly Breakdown Container */
726
  .hourly-container {
727
  margin-top: 1.5rem;
728
- padding: 0;
729
  z-index: 5;
730
  position: relative;
731
  margin-bottom: 2rem;
@@ -1606,824 +1606,5 @@ body::after {
1606
  }
1607
  }
1608
 
1609
- /* ==========================================
1610
- DASHBOARD KEPUTUSAN (DECISION SUPPORT SYSTEM)
1611
- ========================================== */
1612
- .decision-header-section {
1613
- padding-top: 1rem;
1614
- padding-bottom: 1.5rem;
1615
- }
1616
-
1617
- .decision-header-top {
1618
- display: flex;
1619
- justify-content: space-between;
1620
- align-items: flex-end;
1621
- flex-wrap: wrap;
1622
- gap: 1.5rem;
1623
- margin-bottom: 2rem;
1624
- }
1625
-
1626
- .decision-title-group {
1627
- display: flex;
1628
- flex-direction: column;
1629
- gap: 6px;
1630
- }
1631
-
1632
- .decision-heading {
1633
- display: flex;
1634
- align-items: center;
1635
- gap: 12px;
1636
- font-family: var(--font-display);
1637
- font-size: 2rem;
1638
- font-weight: 800;
1639
- letter-spacing: 1px;
1640
- color: #FFFFFF;
1641
- }
1642
-
1643
- .decision-icon {
1644
- color: var(--cyan);
1645
- filter: drop-shadow(0 0 8px var(--cyan-glow));
1646
- }
1647
-
1648
- .decision-subtitle {
1649
- font-size: 0.9rem;
1650
- color: var(--text-muted);
1651
- max-width: 680px;
1652
- line-height: 1.5;
1653
- }
1654
-
1655
- .decision-controls {
1656
- display: flex;
1657
- align-items: flex-end;
1658
- gap: 1rem;
1659
- flex-wrap: wrap;
1660
- }
1661
-
1662
- .decision-control-item {
1663
- display: flex;
1664
- flex-direction: column;
1665
- gap: 4px;
1666
- }
1667
-
1668
- .decision-control-item label {
1669
- font-family: var(--font-mono);
1670
- font-size: 0.72rem;
1671
- color: var(--text-muted);
1672
- letter-spacing: 1px;
1673
- }
1674
-
1675
- .dec-select {
1676
- min-width: 220px;
1677
- background: rgba(14, 22, 16, 0.95);
1678
- border: 1px solid var(--border-color);
1679
- padding: 8px 12px;
1680
- font-size: 0.85rem;
1681
- }
1682
-
1683
- .weather-modal-trigger-btn {
1684
- width: auto !important;
1685
- padding: 9px 18px !important;
1686
- font-size: 0.82rem !important;
1687
- display: inline-flex;
1688
- align-items: center;
1689
- gap: 8px;
1690
- white-space: nowrap;
1691
- border-radius: 8px;
1692
- background: rgba(0, 240, 255, 0.12) !important;
1693
- border: 1px solid var(--cyan) !important;
1694
- color: var(--cyan) !important;
1695
- cursor: pointer;
1696
- transition: all 0.25s ease;
1697
- }
1698
-
1699
- .weather-modal-trigger-btn:hover {
1700
- background: var(--cyan) !important;
1701
- color: var(--bg-void) !important;
1702
- box-shadow: 0 0 16px var(--cyan-glow) !important;
1703
- transform: translateY(-2px);
1704
- }
1705
-
1706
- /* 3 Top KPI Cards */
1707
- .decision-kpi-grid {
1708
- display: grid;
1709
- grid-template-columns: repeat(3, 1fr);
1710
- gap: 1.5rem;
1711
- margin-bottom: 2rem;
1712
- }
1713
-
1714
- .dec-kpi-card {
1715
- background: var(--bg-panel);
1716
- border: 1px solid var(--border-color);
1717
- border-radius: 12px;
1718
- padding: 1.6rem 1.8rem;
1719
- display: flex;
1720
- flex-direction: column;
1721
- justify-content: space-between;
1722
- min-height: 150px;
1723
- transition: transform 0.3s, border-color 0.3s, box-shadow 0.3s;
1724
- backdrop-filter: blur(16px);
1725
- }
1726
-
1727
- .dec-kpi-card:hover {
1728
- transform: translateY(-4px);
1729
- border-color: var(--border-hover);
1730
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
1731
- }
1732
-
1733
- .dec-kpi-label {
1734
- font-family: var(--font-mono);
1735
- font-size: 0.72rem;
1736
- font-weight: 700;
1737
- color: var(--text-muted);
1738
- letter-spacing: 1.5px;
1739
- text-transform: uppercase;
1740
- }
1741
-
1742
- .dec-kpi-val-row {
1743
- display: flex;
1744
- align-items: baseline;
1745
- gap: 8px;
1746
- margin: 8px 0;
1747
- }
1748
-
1749
- .dec-kpi-number {
1750
- font-family: var(--font-display);
1751
- font-size: 2.3rem;
1752
- font-weight: 800;
1753
- letter-spacing: -0.5px;
1754
- color: #FFFFFF;
1755
- line-height: 1;
1756
- }
1757
-
1758
- .dec-kpi-unit {
1759
- font-family: var(--font-display);
1760
- font-size: 1.1rem;
1761
- font-weight: 600;
1762
- color: var(--text-muted);
1763
- }
1764
-
1765
- .dec-kpi-meta {
1766
- font-family: var(--font-mono);
1767
- font-size: 0.8rem;
1768
- color: var(--text-muted);
1769
- display: inline-flex;
1770
- align-items: center;
1771
- gap: 6px;
1772
- }
1773
-
1774
- .dec-kpi-meta.positive {
1775
- color: var(--cyan);
1776
- }
1777
-
1778
- /* Decision Body Grid (65% Workload Map : 35% Sidebar) */
1779
- .decision-body-grid {
1780
- display: grid;
1781
- grid-template-columns: 1.65fr 1fr;
1782
- gap: 1.5rem;
1783
- margin-bottom: 3rem;
1784
- align-items: start;
1785
- }
1786
-
1787
- /* Peta Beban Kerja Harian Panel */
1788
- .dec-workload-panel {
1789
- padding: 1.8rem;
1790
- border-radius: 12px;
1791
- }
1792
-
1793
- .dec-panel-header {
1794
- display: flex;
1795
- justify-content: space-between;
1796
- align-items: center;
1797
- margin-bottom: 1.5rem;
1798
- }
1799
-
1800
- .dec-panel-title {
1801
- display: flex;
1802
- align-items: center;
1803
- gap: 10px;
1804
- font-family: var(--font-display);
1805
- font-size: 1.15rem;
1806
- font-weight: 700;
1807
- color: #FFFFFF;
1808
- letter-spacing: 0.5px;
1809
- }
1810
-
1811
- .dec-panel-badge {
1812
- font-family: var(--font-mono);
1813
- font-size: 0.72rem;
1814
- color: var(--text-muted);
1815
- background: rgba(255, 255, 255, 0.04);
1816
- padding: 3px 10px;
1817
- border-radius: 4px;
1818
- border: 1px solid rgba(255, 255, 255, 0.08);
1819
- }
1820
-
1821
- /* 7-Day Horizontal Workload Grid */
1822
- .dec-workload-grid {
1823
- display: grid;
1824
- grid-template-columns: repeat(7, 1fr);
1825
- gap: 10px;
1826
- overflow-x: auto;
1827
- padding-bottom: 6px;
1828
- }
1829
-
1830
- .workload-day-card {
1831
- background: rgba(8, 14, 10, 0.9);
1832
- border: 1px solid var(--border-color);
1833
- border-radius: 8px;
1834
- padding: 12px 10px;
1835
- display: flex;
1836
- flex-direction: column;
1837
- align-items: center;
1838
- text-align: center;
1839
- transition: transform 0.25s, border-color 0.25s, box-shadow 0.25s;
1840
- }
1841
-
1842
- .workload-day-card:hover {
1843
- transform: translateY(-3px);
1844
- border-color: var(--cyan);
1845
- box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
1846
- }
1847
-
1848
- .workload-day-card.critical {
1849
- border-color: rgba(239, 68, 68, 0.4);
1850
- }
1851
-
1852
- .workload-day-badge {
1853
- width: 100%;
1854
- padding: 4px 6px;
1855
- border-radius: 4px;
1856
- font-family: var(--font-display);
1857
- font-size: 0.78rem;
1858
- font-weight: 800;
1859
- letter-spacing: 1px;
1860
- text-transform: uppercase;
1861
- margin-bottom: 12px;
1862
- }
1863
-
1864
- .workload-day-badge.normal {
1865
- background: rgba(14, 38, 28, 0.7);
1866
- color: #6ee7b7;
1867
- border: 1px solid rgba(110, 231, 183, 0.2);
1868
- }
1869
-
1870
- .workload-day-badge.warning {
1871
- background: rgba(60, 40, 10, 0.7);
1872
- color: #fbbf24;
1873
- border: 1px solid rgba(251, 191, 36, 0.2);
1874
- }
1875
-
1876
- .workload-day-badge.peak-critical {
1877
- background: rgba(70, 15, 20, 0.85);
1878
- color: #fca5a5;
1879
- border: 1px solid rgba(239, 68, 68, 0.35);
1880
- box-shadow: 0 0 10px rgba(239, 68, 68, 0.2);
1881
- }
1882
-
1883
- .workload-ton-val {
1884
- font-family: var(--font-display);
1885
- font-size: 1.15rem;
1886
- font-weight: 800;
1887
- color: #FFFFFF;
1888
- line-height: 1.1;
1889
- }
1890
-
1891
- .workload-ton-label {
1892
- font-family: var(--font-mono);
1893
- font-size: 0.65rem;
1894
- color: var(--text-muted);
1895
- letter-spacing: 1px;
1896
- margin-bottom: 10px;
1897
- }
1898
-
1899
- .workload-trucks-count {
1900
- display: flex;
1901
- align-items: center;
1902
- justify-content: center;
1903
- gap: 4px;
1904
- font-family: var(--font-mono);
1905
- font-size: 0.75rem;
1906
- color: var(--text-muted);
1907
- margin-bottom: 12px;
1908
- padding-bottom: 10px;
1909
- border-bottom: 1px solid rgba(255, 255, 255, 0.06);
1910
- width: 100%;
1911
- }
1912
-
1913
- .workload-shifts-list {
1914
- display: flex;
1915
- flex-direction: column;
1916
- gap: 4px;
1917
- width: 100%;
1918
- }
1919
-
1920
- .workload-shift-pill {
1921
- font-family: var(--font-mono);
1922
- font-size: 0.62rem;
1923
- font-weight: 600;
1924
- padding: 2px 4px;
1925
- border-radius: 3px;
1926
- white-space: nowrap;
1927
- text-align: center;
1928
- }
1929
-
1930
- .workload-shift-pill.s1 {
1931
- color: #fbbf24;
1932
- background: rgba(251, 191, 36, 0.08);
1933
- border: 1px solid rgba(251, 191, 36, 0.15);
1934
- }
1935
-
1936
- .workload-shift-pill.s2 {
1937
- color: #38bdf8;
1938
- background: rgba(56, 189, 248, 0.08);
1939
- border: 1px solid rgba(56, 189, 248, 0.15);
1940
- }
1941
-
1942
- .workload-shift-pill.s3 {
1943
- color: #f87171;
1944
- background: rgba(239, 68, 68, 0.12);
1945
- border: 1px solid rgba(239, 68, 68, 0.25);
1946
- font-weight: 700;
1947
- }
1948
-
1949
- /* Sidebar Column */
1950
- .dec-sidebar-col {
1951
- display: flex;
1952
- flex-direction: column;
1953
- gap: 1.5rem;
1954
- }
1955
-
1956
- /* Decision Details Grid (Fleet Allocation & Critical Action Plan) */
1957
- .decision-details-grid {
1958
- display: grid;
1959
- grid-template-columns: 1fr 1.3fr;
1960
- gap: 1.5rem;
1961
- align-items: stretch;
1962
- }
1963
-
1964
- @media (max-width: 1080px) {
1965
- .decision-details-grid {
1966
- grid-template-columns: 1fr;
1967
- }
1968
- }
1969
-
1970
- /* Alokasi Armada Spesifik Panel */
1971
- .dec-fleet-panel {
1972
- padding: 1.6rem 1.8rem;
1973
- border-radius: 12px;
1974
- display: flex;
1975
- flex-direction: column;
1976
- justify-content: space-between;
1977
- }
1978
-
1979
- .dec-fleet-list {
1980
- display: grid;
1981
- grid-template-columns: 1fr 1fr;
1982
- gap: 1.2rem;
1983
- margin-top: 1.2rem;
1984
- flex: 1;
1985
- }
1986
-
1987
- @media (max-width: 640px) {
1988
- .dec-fleet-list {
1989
- grid-template-columns: 1fr;
1990
- }
1991
- }
1992
 
1993
- .dec-fleet-card {
1994
- background: rgba(0, 0, 0, 0.3);
1995
- border: 1px solid var(--border-color);
1996
- border-radius: 10px;
1997
- padding: 1.2rem 1.4rem;
1998
- display: flex;
1999
- align-items: center;
2000
- gap: 1.2rem;
2001
- transition: transform 0.25s, border-color 0.25s, box-shadow 0.25s;
2002
- }
2003
 
2004
- .dec-fleet-card:hover {
2005
- transform: translateY(-3px);
2006
- border-color: var(--cyan);
2007
- box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
2008
- }
2009
-
2010
- .fleet-icon-box {
2011
- width: 52px;
2012
- height: 52px;
2013
- border-radius: 10px;
2014
- display: flex;
2015
- align-items: center;
2016
- justify-content: center;
2017
- flex-shrink: 0;
2018
- }
2019
-
2020
- .fleet-icon-box.blue-box {
2021
- background: rgba(56, 189, 248, 0.12);
2022
- border: 1px solid rgba(56, 189, 248, 0.3);
2023
- }
2024
-
2025
- .fleet-icon-box.amber-box {
2026
- background: rgba(251, 191, 36, 0.12);
2027
- border: 1px solid rgba(251, 191, 36, 0.3);
2028
- }
2029
-
2030
- .fleet-info {
2031
- display: flex;
2032
- flex-direction: column;
2033
- gap: 4px;
2034
- min-width: 0;
2035
- }
2036
-
2037
- .fleet-unit-count {
2038
- font-family: var(--font-display);
2039
- font-size: 1.65rem;
2040
- font-weight: 800;
2041
- color: #FFFFFF;
2042
- line-height: 1;
2043
- display: flex;
2044
- align-items: baseline;
2045
- gap: 6px;
2046
- }
2047
-
2048
- .fleet-unit-suffix {
2049
- font-size: 0.95rem;
2050
- font-weight: 600;
2051
- color: var(--text-muted);
2052
- }
2053
-
2054
- .fleet-unit-type {
2055
- font-family: var(--font-mono);
2056
- font-size: 0.78rem;
2057
- color: var(--text-muted);
2058
- white-space: nowrap;
2059
- letter-spacing: 0.3px;
2060
- }
2061
-
2062
- /* Rencana Aksi Kritis Panel */
2063
- .dec-action-panel {
2064
- padding: 1.6rem 1.8rem;
2065
- border-radius: 12px;
2066
- }
2067
-
2068
- .dec-panel-title.action-title {
2069
- color: #f87171;
2070
- }
2071
-
2072
- .dec-critical-box {
2073
- background: rgba(239, 68, 68, 0.05);
2074
- border: 1px solid rgba(239, 68, 68, 0.3);
2075
- border-left: 4px solid #ef4444;
2076
- border-radius: 8px;
2077
- padding: 1.3rem 1.5rem;
2078
- margin-top: 1.2rem;
2079
- }
2080
-
2081
- .dec-critical-header {
2082
- display: flex;
2083
- align-items: center;
2084
- gap: 10px;
2085
- margin-bottom: 12px;
2086
- }
2087
-
2088
- .critical-pulse-dot {
2089
- width: 9px;
2090
- height: 9px;
2091
- border-radius: 50%;
2092
- background-color: #ef4444;
2093
- box-shadow: 0 0 10px #ef4444;
2094
- animation: pulse-red-dot 1.5s infinite;
2095
- flex-shrink: 0;
2096
- }
2097
-
2098
- @keyframes pulse-red-dot {
2099
- 0% { transform: scale(0.9); opacity: 0.8; }
2100
- 50% { transform: scale(1.4); opacity: 1; box-shadow: 0 0 14px #ef4444; }
2101
- 100% { transform: scale(0.9); opacity: 0.8; }
2102
- }
2103
-
2104
- .dec-critical-title {
2105
- font-family: var(--font-display);
2106
- font-size: 0.92rem;
2107
- font-weight: 800;
2108
- color: #fca5a5;
2109
- letter-spacing: 0.5px;
2110
- text-transform: uppercase;
2111
- line-height: 1.3;
2112
- }
2113
-
2114
- .dec-critical-list {
2115
- list-style: none;
2116
- padding: 0;
2117
- margin: 0;
2118
- display: flex;
2119
- flex-direction: column;
2120
- gap: 10px;
2121
- }
2122
-
2123
- .dec-critical-list li {
2124
- font-size: 0.84rem;
2125
- color: #e2e8f0;
2126
- line-height: 1.55;
2127
- position: relative;
2128
- padding-left: 16px;
2129
- }
2130
-
2131
- .dec-critical-list li::before {
2132
- content: "•";
2133
- position: absolute;
2134
- left: 0;
2135
- top: -1px;
2136
- color: #ef4444;
2137
- font-weight: bold;
2138
- font-size: 1.1rem;
2139
- }
2140
-
2141
- /* ==========================================
2142
- PRAKIRAAN CUACA 7 HARI MODAL
2143
- ========================================== */
2144
- .weather-modal-backdrop {
2145
- position: fixed;
2146
- inset: 0;
2147
- background: rgba(3, 7, 5, 0.85);
2148
- backdrop-filter: blur(12px);
2149
- -webkit-backdrop-filter: blur(12px);
2150
- z-index: 9999;
2151
- display: none;
2152
- align-items: center;
2153
- justify-content: center;
2154
- padding: 1.5rem;
2155
- opacity: 0;
2156
- transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
2157
- }
2158
-
2159
- .weather-modal-backdrop.open {
2160
- display: flex;
2161
- opacity: 1;
2162
- }
2163
-
2164
- .weather-modal-dialog {
2165
- background: #0B1410;
2166
- border: 1px solid rgba(0, 240, 255, 0.25);
2167
- box-shadow: 0 25px 60px rgba(0, 0, 0, 0.7), 0 0 30px rgba(0, 240, 255, 0.1);
2168
- border-radius: 16px;
2169
- width: 100%;
2170
- max-width: 440px;
2171
- padding: 1.8rem;
2172
- transform: scale(0.95);
2173
- transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
2174
- position: relative;
2175
- overflow: hidden;
2176
- }
2177
-
2178
- .weather-modal-backdrop.open .weather-modal-dialog {
2179
- transform: scale(1);
2180
- }
2181
-
2182
- .weather-modal-header {
2183
- display: flex;
2184
- justify-content: space-between;
2185
- align-items: center;
2186
- margin-bottom: 1.5rem;
2187
- }
2188
-
2189
- .weather-modal-title-row {
2190
- display: flex;
2191
- align-items: center;
2192
- gap: 12px;
2193
- }
2194
-
2195
- .weather-modal-title {
2196
- font-family: var(--font-display);
2197
- font-size: 1.25rem;
2198
- font-weight: 800;
2199
- color: #FFFFFF;
2200
- letter-spacing: 0.5px;
2201
- }
2202
-
2203
- .weather-location-pill {
2204
- background: rgba(0, 240, 255, 0.12);
2205
- border: 1px solid rgba(0, 240, 255, 0.35);
2206
- color: #38bdf8;
2207
- font-family: var(--font-mono);
2208
- font-size: 0.75rem;
2209
- font-weight: 700;
2210
- padding: 3px 10px;
2211
- border-radius: 6px;
2212
- }
2213
-
2214
- .weather-modal-close-btn {
2215
- background: transparent;
2216
- border: none;
2217
- color: var(--text-muted);
2218
- font-size: 1.3rem;
2219
- cursor: pointer;
2220
- width: 32px;
2221
- height: 32px;
2222
- display: flex;
2223
- align-items: center;
2224
- justify-content: center;
2225
- border-radius: 6px;
2226
- transition: color 0.2s, background 0.2s;
2227
- }
2228
-
2229
- .weather-modal-close-btn:hover {
2230
- color: #FFFFFF;
2231
- background: rgba(255, 255, 255, 0.08);
2232
- }
2233
-
2234
- /* Summary Card in Modal */
2235
- .weather-modal-summary-card {
2236
- background: rgba(251, 191, 36, 0.04);
2237
- border: 1px solid rgba(251, 191, 36, 0.25);
2238
- border-radius: 10px;
2239
- padding: 1rem 1.2rem;
2240
- display: flex;
2241
- justify-content: space-between;
2242
- align-items: center;
2243
- margin-bottom: 1.5rem;
2244
- }
2245
-
2246
- .summary-left {
2247
- display: flex;
2248
- flex-direction: column;
2249
- gap: 4px;
2250
- }
2251
-
2252
- .summary-card-label {
2253
- font-family: var(--font-mono);
2254
- font-size: 0.7rem;
2255
- font-weight: 700;
2256
- color: var(--text-muted);
2257
- letter-spacing: 1px;
2258
- }
2259
-
2260
- .summary-card-stats {
2261
- font-family: var(--font-mono);
2262
- font-size: 0.95rem;
2263
- color: #FFFFFF;
2264
- font-weight: 600;
2265
- display: flex;
2266
- align-items: center;
2267
- gap: 6px;
2268
- }
2269
-
2270
- .summary-val-highlight {
2271
- color: #FFFFFF;
2272
- font-weight: 800;
2273
- }
2274
-
2275
- .summary-separator {
2276
- color: var(--text-muted);
2277
- }
2278
-
2279
- .weather-dryness-badge {
2280
- font-family: var(--font-mono);
2281
- font-size: 0.78rem;
2282
- font-weight: 800;
2283
- padding: 5px 12px;
2284
- border-radius: 6px;
2285
- letter-spacing: 1px;
2286
- text-transform: uppercase;
2287
- }
2288
-
2289
- .weather-dryness-badge.dry {
2290
- background: rgba(251, 191, 36, 0.15);
2291
- border: 1px solid #fbbf24;
2292
- color: #fbbf24;
2293
- }
2294
-
2295
- .weather-dryness-badge.wet {
2296
- background: rgba(56, 189, 248, 0.15);
2297
- border: 1px solid #38bdf8;
2298
- color: #38bdf8;
2299
- }
2300
-
2301
- .weather-dryness-badge.heavy {
2302
- background: rgba(239, 68, 68, 0.15);
2303
- border: 1px solid #ef4444;
2304
- color: #ef4444;
2305
- }
2306
-
2307
- /* 7-Day Forecast List Rows */
2308
- .weather-modal-forecast-list {
2309
- display: flex;
2310
- flex-direction: column;
2311
- gap: 6px;
2312
- }
2313
-
2314
- .weather-forecast-row {
2315
- display: grid;
2316
- grid-template-columns: 45px 30px 1fr 65px 75px;
2317
- align-items: center;
2318
- gap: 10px;
2319
- padding: 10px 12px;
2320
- background: rgba(0, 0, 0, 0.2);
2321
- border-radius: 8px;
2322
- border: 1px solid rgba(255, 255, 255, 0.03);
2323
- transition: background 0.2s, border-color 0.2s;
2324
- }
2325
-
2326
- .weather-forecast-row:hover {
2327
- background: rgba(0, 240, 255, 0.04);
2328
- border-color: rgba(0, 240, 255, 0.15);
2329
- }
2330
-
2331
- .w-row-day {
2332
- font-family: var(--font-display);
2333
- font-size: 0.9rem;
2334
- font-weight: 700;
2335
- color: #FFFFFF;
2336
- }
2337
-
2338
- .w-row-icon {
2339
- font-size: 1.2rem;
2340
- display: flex;
2341
- align-items: center;
2342
- justify-content: center;
2343
- }
2344
-
2345
- .w-row-cond {
2346
- font-size: 0.85rem;
2347
- color: var(--text-main);
2348
- white-space: nowrap;
2349
- overflow: hidden;
2350
- text-overflow: ellipsis;
2351
- }
2352
-
2353
- .w-row-precip {
2354
- font-family: var(--font-mono);
2355
- font-size: 0.82rem;
2356
- color: #38bdf8;
2357
- text-align: right;
2358
- font-weight: 600;
2359
- }
2360
-
2361
- .w-row-temp {
2362
- font-family: var(--font-mono);
2363
- font-size: 0.88rem;
2364
- text-align: right;
2365
- display: flex;
2366
- gap: 6px; justify-content: flex-end;
2367
- }
2368
-
2369
- .w-temp-max {
2370
- color: #FFFFFF;
2371
- font-weight: 700;
2372
- }
2373
-
2374
- .w-temp-min {
2375
- color: var(--text-muted);
2376
- }
2377
-
2378
- .weather-modal-loading {
2379
- padding: 2.5rem 1rem;
2380
- text-align: center;
2381
- font-family: var(--font-mono);
2382
- font-size: 0.85rem;
2383
- color: var(--text-muted);
2384
- }
2385
-
2386
- /* ==========================================
2387
- RESPONSIVE QUERIES FOR DECISION DASHBOARD
2388
- ========================================== */
2389
- @media (max-width: 1100px) {
2390
- .decision-details-grid {
2391
- grid-template-columns: 1fr;
2392
- gap: 1.5rem;
2393
- }
2394
- .decision-kpi-grid {
2395
- grid-template-columns: 1fr;
2396
- gap: 1rem;
2397
- }
2398
- }
2399
-
2400
- @media (max-width: 768px) {
2401
- .decision-header-top {
2402
- flex-direction: column;
2403
- align-items: flex-start;
2404
- }
2405
- .decision-controls {
2406
- width: 100%;
2407
- flex-direction: column;
2408
- align-items: stretch;
2409
- }
2410
- .dec-select {
2411
- width: 100%;
2412
- }
2413
- .dec-workload-grid {
2414
- display: flex;
2415
- overflow-x: auto;
2416
- padding-bottom: 12px;
2417
- }
2418
- .workload-day-card {
2419
- min-width: 120px;
2420
- }
2421
- .weather-forecast-row {
2422
- grid-template-columns: 35px 24px 1fr 50px 65px;
2423
- gap: 6px;
2424
- padding: 8px 10px;
2425
- }
2426
- .w-row-cond {
2427
- font-size: 0.78rem;
2428
- }
2429
- }
 
130
  display: grid;
131
  grid-template-columns: 320px 1fr 340px;
132
  gap: 1.5rem;
133
+ padding: 1.5rem 2.5rem;
134
  flex: 1;
135
  z-index: 5;
136
  position: relative;
 
656
  /* Timeline Container */
657
  .timeline-container {
658
  margin-top: 1.5rem;
659
+ padding: 0 2.5rem;
660
  z-index: 5;
661
  position: relative;
662
  }
 
725
  /* Hourly Breakdown Container */
726
  .hourly-container {
727
  margin-top: 1.5rem;
728
+ padding: 0 2.5rem;
729
  z-index: 5;
730
  position: relative;
731
  margin-bottom: 2rem;
 
1606
  }
1607
  }
1608
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1609
 
 
 
 
 
 
 
 
 
 
 
1610