ALAMDIENG commited on
Commit
bf204bf
·
1 Parent(s): ea21466

feat: retheme to professional Deep Eco-Green and add cinematic scrolling waste accumulation story

Browse files
Files changed (3) hide show
  1. frontend/app.js +81 -0
  2. frontend/index.html +55 -0
  3. frontend/style.css +37 -15
frontend/app.js CHANGED
@@ -1054,4 +1054,85 @@ window.sortWaste = sortWaste;
1054
  // Initialize the game automatically
1055
  document.addEventListener("DOMContentLoaded", () => {
1056
  loadNextWasteItem();
 
1057
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1054
  // Initialize the game automatically
1055
  document.addEventListener("DOMContentLoaded", () => {
1056
  loadNextWasteItem();
1057
+ initCrisisStoryScroller();
1058
  });
1059
+
1060
+ // ==========================================
1061
+ // CINEMATIC CRISIS STORYTELLING SCROLLER
1062
+ // ==========================================
1063
+ function initCrisisStoryScroller() {
1064
+ const storyCards = document.querySelectorAll(".story-card");
1065
+ const wasteFill = document.getElementById("waste-pile-fill");
1066
+ const tonsVal = document.getElementById("simulated-tons-val");
1067
+
1068
+ if (!storyCards.length || !wasteFill || !tonsVal) return;
1069
+
1070
+ // Use IntersectionObserver to detect which card is currently active/visible in the center of screen
1071
+ const observerOptions = {
1072
+ root: null,
1073
+ rootMargin: "-25% 0px -40% 0px", // Focus on the middle of the screen
1074
+ threshold: 0.1
1075
+ };
1076
+
1077
+ const observer = new IntersectionObserver((entries) => {
1078
+ entries.forEach(entry => {
1079
+ if (entry.isIntersecting) {
1080
+ // Highlight active card
1081
+ storyCards.forEach(c => {
1082
+ c.style.borderColor = "var(--border-color)";
1083
+ c.style.background = "rgba(8,18,14,0.6)";
1084
+ c.style.boxShadow = "none";
1085
+ });
1086
+ entry.target.style.borderColor = "var(--cyan)";
1087
+ entry.target.style.background = "rgba(16, 185, 129, 0.08)";
1088
+ entry.target.style.boxShadow = "0 0 15px rgba(16, 185, 129, 0.1)";
1089
+
1090
+ // Get parameters
1091
+ const targetHeight = entry.target.getAttribute("data-height");
1092
+ const targetTons = entry.target.getAttribute("data-tons");
1093
+
1094
+ // Set fill height and text counter
1095
+ wasteFill.style.height = `${targetHeight}%`;
1096
+
1097
+ // If it is the last critical card, change color to red
1098
+ if (targetHeight === "95") {
1099
+ wasteFill.style.background = "linear-gradient(180deg, var(--red) 0%, #7f1d1d 100%)";
1100
+ wasteFill.style.boxShadow = "0 0 20px rgba(239, 68, 68, 0.4)";
1101
+ } else {
1102
+ wasteFill.style.background = "linear-gradient(180deg, var(--yellow) 0%, #78350F 100%)";
1103
+ wasteFill.style.boxShadow = "0 0 20px rgba(245, 158, 11, 0.3)";
1104
+ }
1105
+
1106
+ // Animate tons text value counter
1107
+ animateTonsCounter(parseInt(tonsVal.textContent.replace(/,/g, "")), parseInt(targetTons));
1108
+ }
1109
+ });
1110
+ }, observerOptions);
1111
+
1112
+ storyCards.forEach(card => observer.observe(card));
1113
+ }
1114
+
1115
+ function animateTonsCounter(start, end) {
1116
+ const tonsVal = document.getElementById("simulated-tons-val");
1117
+ if (!tonsVal) return;
1118
+
1119
+ const duration = 800; // ms
1120
+ const startTime = performance.now();
1121
+
1122
+ function update(now) {
1123
+ const elapsed = now - startTime;
1124
+ const progress = Math.min(elapsed / duration, 1);
1125
+
1126
+ // Ease out quadratic
1127
+ const easeProgress = progress * (2 - progress);
1128
+ const current = Math.round(start + (end - start) * easeProgress);
1129
+
1130
+ tonsVal.textContent = `${current.toLocaleString('en-US')} Tons`;
1131
+
1132
+ if (progress < 1) {
1133
+ requestAnimationFrame(update);
1134
+ }
1135
+ }
1136
+
1137
+ requestAnimationFrame(update);
1138
+ }
frontend/index.html CHANGED
@@ -196,6 +196,61 @@
196
  </div>
197
  </section>
198
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  <!-- Sumber Data & Akuntabilitas -->
200
  <section class="container data-sources-section">
201
  <h2 class="section-title">TRANSPARANSI SUMBER DATA</h2>
 
196
  </div>
197
  </section>
198
 
199
+ <!-- Cinematic Waste Crisis Scroll Section -->
200
+ <section class="container crisis-story-section" style="margin-top: 4rem; display: grid; grid-template-columns: 1.2fr 0.8fr; gap: 3rem; position: relative;">
201
+
202
+ <!-- Sticky Left Panel (Waste Tower Visualizer) -->
203
+ <div class="sticky-visualizer-panel" style="position: sticky; top: 120px; height: 450px; display: flex; flex-direction: column; justify-content: center; align-items: center; background: rgba(5, 12, 10, 0.4); border: 1px solid var(--border-color); border-radius: 16px; padding: 2rem; overflow: hidden; box-shadow: 0 0 30px rgba(16, 185, 129, 0.05);">
204
+ <h3 style="font-family: var(--font-display); font-size: 1.1rem; color: #FFF; margin-bottom: 1.5rem; letter-spacing: 1px; text-transform: uppercase;">JAKARTA DAILY ACCUMULATION TOWER</h3>
205
+
206
+ <!-- The Wireframe Tower -->
207
+ <div class="waste-tower-outline" style="position: relative; width: 140px; height: 260px; border: 2px dashed rgba(16, 185, 129, 0.3); border-radius: 12px 12px 0 0; background: rgba(255,255,255,0.01); overflow: hidden; display: flex; align-items: flex-end;">
208
+ <!-- Raising Waste Fill -->
209
+ <div id="waste-pile-fill" style="width: 100%; height: 10%; background: linear-gradient(180deg, var(--yellow) 0%, #78350F 100%); opacity: 0.85; transition: height 0.6s cubic-bezier(0.25, 0.8, 0.25, 1); position: relative; box-shadow: 0 0 20px rgba(245, 158, 11, 0.3);">
210
+ <!-- Bubbles/Particles inside fill -->
211
+ <div class="waste-steam" style="position: absolute; top: -10px; left: 0; width: 100%; height: 12px; background: rgba(245, 158, 11, 0.6); filter: blur(4px); animation: steam-pulse 1.5s infinite alternate; pointer-events: none;"></div>
212
+ </div>
213
+
214
+ <!-- Height Markers -->
215
+ <div style="position: absolute; right: 10px; top: 10px; font-family: var(--font-mono); font-size: 10px; color: rgba(255,255,255,0.3); pointer-events: none;">CRITICAL</div>
216
+ <div style="position: absolute; right: 10px; top: 120px; font-family: var(--font-mono); font-size: 10px; color: rgba(255,255,255,0.3); pointer-events: none;">WARNING</div>
217
+ <div style="position: absolute; right: 10px; top: 230px; font-family: var(--font-mono); font-size: 10px; color: rgba(255,255,255,0.3); pointer-events: none;">NORMAL</div>
218
+ </div>
219
+
220
+ <!-- Live Ticking Indicator -->
221
+ <div class="tower-status" style="margin-top: 1.5rem; text-align: center;">
222
+ <div style="font-family: var(--font-mono); font-size: 0.8rem; color: var(--text-muted); text-transform: uppercase;">Simulated Tonnage</div>
223
+ <div style="font-family: var(--font-mono); font-size: 1.8rem; color: var(--yellow); font-weight: bold; text-shadow: 0 0 10px rgba(245,158,11,0.2);" id="simulated-tons-val">905 Tons</div>
224
+ </div>
225
+ </div>
226
+
227
+ <!-- Scrolling Narrative Cards -->
228
+ <div class="story-scroll-cards" style="display: flex; flex-direction: column; gap: 6rem; padding-bottom: 8rem;">
229
+
230
+ <!-- Card 1 -->
231
+ <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; background: rgba(8,18,14,0.6); border:1px solid var(--border-color); border-radius:12px;">
232
+ <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>
233
+ <h3 style="font-family: var(--font-display); font-size: 1.3rem; color: #FFF; margin-bottom: 0.8rem;">2.264 TON SAMPAH TERKUMPUL</h3>
234
+ <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>
235
+ </div>
236
+
237
+ <!-- Card 2 -->
238
+ <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; background: rgba(8,18,14,0.6); border:1px solid var(--border-color); border-radius:12px;">
239
+ <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>
240
+ <h3 style="font-family: var(--font-display); font-size: 1.3rem; color: #FFF; margin-bottom: 0.8rem;">4.982 TON SAMPAH TERKUMPUL</h3>
241
+ <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>
242
+ </div>
243
+
244
+ <!-- Card 3 -->
245
+ <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; background: rgba(8,18,14,0.6); border:1px solid var(--border-color); border-radius:12px;">
246
+ <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>
247
+ <h3 style="font-family: var(--font-display); font-size: 1.3rem; color: #FFF; margin-bottom: 0.8rem;">9.059 TON SAMPAH HARIAN</h3>
248
+ <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>
249
+ </div>
250
+
251
+ </div>
252
+ </section>
253
+
254
  <!-- Sumber Data & Akuntabilitas -->
255
  <section class="container data-sources-section">
256
  <h2 class="section-title">TRANSPARANSI SUMBER DATA</h2>
frontend/style.css CHANGED
@@ -1,22 +1,22 @@
1
  /* CSS Variables for Theming */
2
  :root {
3
- --bg-void: #02040a;
4
- --bg-panel: rgba(6, 10, 22, 0.75);
5
- --border-color: rgba(0, 240, 255, 0.12);
6
- --border-hover: rgba(0, 240, 255, 0.3);
7
 
8
- --text-main: #E1E3E8;
9
- --text-muted: #8c93a3;
10
 
11
- /* Neon Colors */
12
- --cyan: #00F0FF;
13
- --cyan-glow: rgba(0, 240, 255, 0.45);
14
- --green: #39FF14;
15
- --green-glow: rgba(57, 255, 20, 0.35);
16
- --yellow: #FFE600;
17
- --yellow-glow: rgba(255, 230, 0, 0.35);
18
- --red: #FF0055;
19
- --red-glow: rgba(255, 0, 85, 0.45);
20
 
21
  /* Fonts */
22
  --font-display: 'Outfit', 'Space Grotesk', system-ui, sans-serif;
@@ -1573,5 +1573,27 @@ body::after {
1573
  }
1574
  }
1575
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1576
 
1577
 
 
1
  /* CSS Variables for Theming */
2
  :root {
3
+ --bg-void: #030806;
4
+ --bg-panel: rgba(8, 18, 14, 0.85);
5
+ --border-color: rgba(16, 185, 129, 0.15);
6
+ --border-hover: rgba(16, 185, 129, 0.4);
7
 
8
+ --text-main: #E2E8F0;
9
+ --text-muted: #94A3B8;
10
 
11
+ /* Professional Eco-Urban Colors */
12
+ --cyan: #10B981; /* Eco Emerald Green (Primary) */
13
+ --cyan-glow: rgba(16, 185, 129, 0.4);
14
+ --green: #34D399; /* Bright Mint Green */
15
+ --green-glow: rgba(52, 211, 153, 0.3);
16
+ --yellow: #F59E0B; /* Amber Orange (Warning) */
17
+ --yellow-glow: rgba(245, 158, 11, 0.3);
18
+ --red: #EF4444; /* Alert Coral Red (Critical) */
19
+ --red-glow: rgba(239, 68, 68, 0.4);
20
 
21
  /* Fonts */
22
  --font-display: 'Outfit', 'Space Grotesk', system-ui, sans-serif;
 
1573
  }
1574
  }
1575
 
1576
+ @keyframes steam-pulse {
1577
+ 0% { opacity: 0.3; transform: scaleY(0.9); }
1578
+ 100% { opacity: 0.75; transform: scaleY(1.1); }
1579
+ }
1580
+
1581
+ @media (max-width: 992px) {
1582
+ .crisis-story-section {
1583
+ grid-template-columns: 1fr !important;
1584
+ gap: 2rem !important;
1585
+ }
1586
+ .sticky-visualizer-panel {
1587
+ position: relative !important;
1588
+ top: 0 !important;
1589
+ height: auto !important;
1590
+ padding: 1.5rem !important;
1591
+ }
1592
+ .story-scroll-cards {
1593
+ gap: 3rem !important;
1594
+ padding-bottom: 2rem !important;
1595
+ }
1596
+ }
1597
+
1598
 
1599