rajkhanke commited on
Commit
d190b3d
Β·
verified Β·
1 Parent(s): 6ce1456

Create templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +1364 -0
templates/index.html ADDED
@@ -0,0 +1,1364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ <title>Water Footprint Calculator - Agricultural Products</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
9
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
10
+ <style>
11
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
12
+
13
+ * { font-family: 'Inter', sans-serif; }
14
+
15
+ body {
16
+ background: linear-gradient(135deg, #f0fff4 0%, #c6f6d5 100%); /* Light green base */
17
+ background-size: 400% 400%;
18
+ animation: gradientShift 18s ease infinite; /* Slightly slower animation */
19
+ min-height: 100vh;
20
+ color: #2D3748; /* Dark text color */
21
+ }
22
+
23
+ @keyframes gradientShift {
24
+ 0% { background-position: 0% 50%; }
25
+ 50% { background-position: 100% 50%; }
26
+ 100% { background-position: 0% 50%; }
27
+ }
28
+
29
+ .glass-effect {
30
+ background: rgba(255, 255, 255, 0.85); /* More opaque white */
31
+ backdrop-filter: blur(12px);
32
+ -webkit-backdrop-filter: blur(12px);
33
+ border: 1px solid rgba(255, 255, 255, 0.9); /* Very light border */
34
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.08); /* Soft shadow */
35
+ }
36
+
37
+ .hover-3d {
38
+ transform-style: preserve-3d;
39
+ transition: transform 0.5s ease-out;
40
+ }
41
+
42
+ .hover-3d:hover {
43
+ transform: perspective(1000px) rotateX(1deg) rotateY(1deg); /* Minimal rotation */
44
+ }
45
+
46
+ .shine-effect {
47
+ position: relative;
48
+ overflow: hidden;
49
+ }
50
+
51
+ .shine-effect::before {
52
+ content: '';
53
+ position: absolute;
54
+ top: 0;
55
+ left: -120%; /* Start further left */
56
+ width: 60%; /* Wider shine */
57
+ height: 100%;
58
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent); /* More prominent shine */
59
+ animation: shine 4s infinite ease-in-out; /* Slower shine */
60
+ }
61
+
62
+ @keyframes shine {
63
+ 0% { left: -120%; }
64
+ 60% { left: 120%; } /* Go further right */
65
+ 100% { left: 120%; }
66
+ }
67
+
68
+
69
+ .float-animation {
70
+ animation: float 7s ease-in-out infinite; /* Slower float */
71
+ }
72
+
73
+ @keyframes float {
74
+ 0%, 100% { transform: translateY(0px); }
75
+ 50% { transform: translateY(-6px); } /* Slightly more float */
76
+ }
77
+
78
+ .drop-shadow-custom {
79
+ filter: drop-shadow(0 6px 12px rgba(74, 222, 128, 0.3)); /* Softer green shadow */
80
+ }
81
+
82
+
83
+ .pulse-glow {
84
+ animation: pulseGlow 2.5s ease-in-out infinite alternate; /* Slower pulse */
85
+ }
86
+
87
+ @keyframes pulseGlow {
88
+ from { box-shadow: 0 0 12px rgba(74, 222, 128, 0.7); } /* Stronger green glow */
89
+ to { box-shadow: 0 0 20px rgba(74, 222, 128, 1); }
90
+ }
91
+
92
+ .loading-dots::after {
93
+ content: '';
94
+ display: inline-block;
95
+ animation: loadingDots 1.5s infinite steps(3, end);
96
+ }
97
+
98
+ @keyframes loadingDots {
99
+ 0%, 20% { content: ''; }
100
+ 40% { content: '.'; }
101
+ 60% { content: '..'; }
102
+ 80%, 100% { content: '...'; }
103
+ }
104
+
105
+ /* Load-in Animations */
106
+ .slide-in {
107
+ animation: slideIn 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; /* Smoother curve */
108
+ opacity: 0;
109
+ }
110
+
111
+ @keyframes slideIn {
112
+ from { transform: translateY(20px); opacity: 0; }
113
+ to { transform: translateY(0); opacity: 1; }
114
+ }
115
+
116
+ .scale-in {
117
+ animation: scaleIn 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; /* Smoother curve */
118
+ opacity: 0;
119
+ }
120
+
121
+ @keyframes scaleIn {
122
+ from { transform: scale(0.98); opacity: 0; }
123
+ to { transform: scale(1); opacity: 1; }
124
+ }
125
+
126
+ .fade-in {
127
+ animation: fadeIn 0.7s ease-out forwards;
128
+ opacity: 0;
129
+ }
130
+ @keyframes fadeIn {
131
+ from { opacity: 0; }
132
+ to { opacity: 1; }
133
+ }
134
+
135
+ /* Hover Effects */
136
+ .hover-lift {
137
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
138
+ }
139
+ .hover-lift:hover {
140
+ transform: translateY(-5px);
141
+ box-shadow: 0 12px 25px rgba(0, 0, 0, 0.1); /* Increased hover shadow */
142
+ }
143
+
144
+
145
+ .upload-area {
146
+ border: 3px dashed #68D391; /* Lighter Green */
147
+ transition: all 0.3s ease;
148
+ }
149
+
150
+ .upload-area.has-image {
151
+ border-style: solid;
152
+ border-color: #38A169; /* Darker Green */
153
+ background: rgba(74, 222, 128, 0.08); /* Light greenish background */
154
+ display: flex;
155
+ flex-direction: column;
156
+ align-items: center;
157
+ justify-content: center;
158
+ padding: 2rem; /* Slightly adjusted padding when image is present */
159
+ }
160
+
161
+
162
+ .upload-area:hover {
163
+ border-color: #38A169;
164
+ background: rgba(74, 222, 128, 0.05);
165
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05); /* Subtle shadow on hover */
166
+ }
167
+
168
+ .upload-area.dragover {
169
+ border-color: #2F855A; /* Even Darker Green */
170
+ background: rgba(74, 222, 128, 0.12);
171
+ box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
172
+ }
173
+
174
+ .upload-area img {
175
+ max-width: 180px; /* Slightly larger preview */
176
+ max-height: 180px;
177
+ object-fit: cover;
178
+ border-radius: 0.75rem; /* More rounded corners */
179
+ margin-bottom: 1.5rem; /* More space below image */
180
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
181
+ transition: transform 0.3s ease;
182
+ }
183
+
184
+ .upload-area img:hover {
185
+ transform: scale(1.05); /* Subtle zoom on image hover */
186
+ }
187
+
188
+ .upload-area.has-image .upload-area-content {
189
+ text-align: center;
190
+ }
191
+
192
+
193
+ .metric-card {
194
+ background: rgba(255, 255, 255, 0.9); /* Very light card background */
195
+ border: 1px solid rgba(255, 255, 255, 0.95); /* Almost invisible border */
196
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05); /* Soft initial shadow */
197
+ color: #2D3748;
198
+ }
199
+
200
+ .metric-card h3, .metric-card h4 { color: #2D3748; }
201
+ .metric-card p { color: #4A5568; }
202
+
203
+ /* Apply hover-lift to metric-card */
204
+ .metric-card.hover-lift:hover {
205
+ transform: translateY(-5px);
206
+ box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1); /* More pronounced hover shadow */
207
+ }
208
+
209
+ .tab-button {
210
+ transition: all 0.3s ease;
211
+ border: 1px solid transparent;
212
+ background-color: rgba(255, 255, 255, 0.6); /* Lighter inactive button */
213
+ color: #4A5568;
214
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); /* Subtle shadow on inactive */
215
+ }
216
+
217
+ .tab-button.active {
218
+ background: linear-gradient(45deg, #48BB78, #81E6D9); /* Green to Teal gradient */
219
+ color: white;
220
+ border-color: #81E6D9;
221
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); /* Stronger shadow for active */
222
+ transform: translateY(-2px); /* Slight lift on active */
223
+ }
224
+
225
+ .tab-button:not(.active):hover {
226
+ background-color: rgba(255, 255, 255, 0.9); /* Lighter hover for inactive */
227
+ border-color: rgba(0, 0, 0, 0.05);
228
+ box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1);
229
+ }
230
+
231
+ /* Chart Styling */
232
+ canvas {
233
+ background: rgba(255, 255, 255, 1);
234
+ border-radius: 0.75rem;
235
+ padding: 1.5rem;
236
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
237
+ }
238
+
239
+ /* Custom Colors for elements on LIGHT background */
240
+ .text-primary { color: #38A169; } /* Dark Green */
241
+ .text-secondary { color: #319795; } /* Dark Teal */
242
+ .text-accent { color: #D69E2E; } /* Orange-Yellow */
243
+
244
+ /* Severity Colors */
245
+ .severity-low { color: #38A169; } /* Dark Green */
246
+ .severity-medium { color: #D69E2E; } /* Orange-Yellow */
247
+ .severity-high { color: #C53030; } /* Darker Red */
248
+ .severity-very-high { color: #9B2C2C; } /* Even Darker Red */
249
+ .severity-unknown { color: #718096; } /* Gray */
250
+
251
+
252
+ .bg-card { background: rgba(255, 255, 255, 0.9); }
253
+ .bg-chart { background: rgba(255, 255, 255, 1); }
254
+
255
+ /* Table Styling for Comparisons */
256
+ .comparison-table {
257
+ width: 100%;
258
+ border-collapse: collapse;
259
+ margin-top: 1.5rem;
260
+ color: #2D3748;
261
+ }
262
+
263
+ .comparison-table th, .comparison-table td {
264
+ padding: 0.9rem; /* Slightly more padding */
265
+ text-align: left;
266
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06); /* Very light border */
267
+ }
268
+
269
+ .comparison-table th {
270
+ background-color: rgba(0, 0, 0, 0.03); /* Very light header background */
271
+ color: #2D3748;
272
+ font-weight: 600;
273
+ text-transform: uppercase;
274
+ font-size: 0.9rem;
275
+ }
276
+
277
+ .comparison-table td {
278
+ color: #4A5568;
279
+ }
280
+
281
+ .comparison-table tbody tr {
282
+ transition: background-color 0.2s ease;
283
+ }
284
+
285
+ .comparison-table tbody tr:hover {
286
+ background-color: rgba(0, 0, 0, 0.05); /* Slightly darker hover */
287
+ }
288
+
289
+ /* Water Definitions Section */
290
+ .water-definitions {
291
+ background: rgba(255, 255, 255, 0.9);
292
+ border: 1px solid rgba(255, 255, 255, 0.95);
293
+ border-radius: 0.75rem;
294
+ padding: 1.5rem;
295
+ margin-top: 2rem;
296
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
297
+ color: #2D3748;
298
+ }
299
+
300
+ .water-definitions h3 {
301
+ color: #2F855A;
302
+ margin-bottom: 1.2rem; /* More space below heading */
303
+ font-weight: 700;
304
+ font-size: 1.6rem;
305
+ }
306
+
307
+ .water-definitions dl {
308
+ display: grid;
309
+ grid-template-columns: auto 1fr;
310
+ gap: 1.2rem; /* Slightly more gap */
311
+ }
312
+
313
+ .water-definitions dt {
314
+ font-weight: 600;
315
+ color: #38A169;
316
+ }
317
+
318
+ .water-definitions dd {
319
+ color: #4A5568;
320
+ margin-left: 0;
321
+ }
322
+
323
+ /* Hover for list items in tabs */
324
+ .tab-content-list-item {
325
+ transition: background-color 0.2s ease;
326
+ }
327
+ .tab-content-list-item:hover {
328
+ background-color: rgba(0, 0, 0, 0.03); /* Light hover background */
329
+ }
330
+
331
+ </style>
332
+ </head>
333
+ <body>
334
+ <!-- Header -->
335
+ <header class="glass-effect p-6 mb-12 slide-in">
336
+ <div class="container mx-auto">
337
+ <div class="flex flex-col sm:flex-row items-center justify-center space-y-4 sm:space-y-0 sm:space-x-4">
338
+ <div class="text-5xl text-green-500 drop-shadow-custom">πŸ’§</div>
339
+ <h1 class="text-3xl sm:text-4xl font-bold text-gray-800 drop-shadow-lg text-center">Water Footprint Calculator</h1>
340
+ <div class="text-5xl text-green-400 float-animation">🌱</div>
341
+ </div>
342
+ <p class="text-center text-gray-600 mt-3 text-base sm:text-lg">Discover the water impact of agricultural products</p>
343
+ </div>
344
+ </header>
345
+
346
+ <div class="container mx-auto px-4 max-w-6xl">
347
+ <!-- Upload Section -->
348
+ <div class="glass-effect rounded-2xl p-8 mb-12 shine-effect hover-3d origin-center hover-lift" id="uploadContainer"> <!-- Added hover-lift -->
349
+ <h2 class="text-2xl sm:text-3xl font-semibold text-gray-800 mb-8 text-center">
350
+ <i class="fas fa-camera mr-3 text-primary"></i>Upload Product Image
351
+ </h2>
352
+
353
+ <div class="upload-area rounded-xl p-10 text-center cursor-pointer transition duration-300 ease-in-out flex flex-col items-center justify-center" id="uploadArea">
354
+ <img id="imagePreview" src="" alt="Image Preview" class="hidden rounded-md mb-4">
355
+ <div class="upload-area-content">
356
+ <div class="text-6xl sm:text-7xl mb-6">πŸ“Έ</div>
357
+ <p class="text-xl sm:text-2xl text-green-700 font-medium mb-3">Drop your image here or click to browse</p>
358
+ <p class="text-green-600 text-base sm:text-lg">Supports JPG, PNG, WebP formats</p>
359
+ </div>
360
+ <input type="file" id="imageInput" accept="image/*" class="hidden">
361
+ </div>
362
+
363
+ <button
364
+ id="analyzeBtn"
365
+ class="w-full mt-8 bg-gradient-to-r from-green-500 to-teal-500 text-white py-4 sm:py-5 px-6 sm:px-8 rounded-xl font-bold text-lg sm:text-xl hover:from-green-600 hover:to-teal-600 transform hover:scale-105 transition-all duration-300 pulse-glow disabled:opacity-40 disabled:cursor-not-allowed"
366
+ disabled
367
+ >
368
+ <i class="fas fa-chart-line mr-3"></i>
369
+ Analyze Water Footprint
370
+ </button>
371
+ </div>
372
+
373
+ <!-- Loading Section -->
374
+ <div id="loadingSection" class="hidden glass-effect rounded-2xl p-8 mb-12 text-center scale-in">
375
+ <div class="text-5xl sm:text-6xl mb-6 text-green-500 pulse-glow">πŸ’§</div> <!-- Added pulse-glow to icon -->
376
+ <h3 class="text-2xl sm:text-3xl font-semibold text-gray-800 mb-4">
377
+ <span id="loadingMessage">Analyzing Image</span><span class="loading-dots"></span>
378
+ </h3>
379
+ <p id="loadingSubMessage" class="text-gray-600 text-base sm:text-lg">Processing and calculating environmental impact...</p>
380
+ <div class="mt-8">
381
+ <div class="w-full bg-gray-300 rounded-full h-3">
382
+ <div class="bg-gradient-to-r from-green-500 to-teal-500 h-3 rounded-full animate-pulse" style="width: 80%"></div>
383
+ </div>
384
+ </div>
385
+ </div>
386
+
387
+ <!-- Error Section -->
388
+ <div id="errorSection" class="hidden glass-effect rounded-2xl p-8 mb-12 text-center scale-in bg-red-100 border border-red-400 text-red-800">
389
+ <div class="text-5xl sm:text-6xl mb-6 text-red-500">❌</div>
390
+ <h3 class="text-2xl sm:text-3xl font-semibold text-red-800 mb-4">Analysis Failed</h3>
391
+ <p id="errorMessage" class="text-red-700 text-base sm:text-lg mb-4">An unexpected error occurred during analysis.</p>
392
+ <p id="fallbackMessage" class="text-yellow-800 text-sm italic hidden"></p>
393
+ </div>
394
+
395
+
396
+ <!-- Results Section -->
397
+ <div id="resultsSection" class="hidden">
398
+ <!-- Product Identification -->
399
+ <div class="glass-effect rounded-2xl p-8 mb-12 slide-in hover-lift" style="animation-delay: 0.2s;"> <!-- Added hover-lift -->
400
+ <h2 class="text-2xl sm:text-3xl font-bold text-gray-800 mb-6">
401
+ <i class="fas fa-search mr-3 text-primary"></i>Product Identification
402
+ </h2>
403
+ <div id="productInfo" class="grid md:grid-cols-2 gap-8">
404
+ <!-- Product info will be populated here -->
405
+ </div>
406
+ </div>
407
+
408
+ <!-- Water Footprint Overview -->
409
+ <div class="glass-effect rounded-2xl p-8 mb-12 scale-in hover-lift" style="animation-delay: 0.4s;"> <!-- Added hover-lift -->
410
+ <h2 class="text-2xl sm:text-3xl font-bold text-gray-800 mb-6">
411
+ <i class="fas fa-tint mr-3 text-green-600"></i>Water Footprint Analysis
412
+ </h2>
413
+ <div id="overallSeverity" class="mb-8 text-center p-4 rounded-lg border border-gray-200 bg-white shadow-sm"> <!-- Lighter border -->
414
+ <h3 class="text-xl font-semibold text-gray-800 mb-2">Overall Water Footprint Severity:</h3>
415
+ <p class="text-3xl font-bold severity-unknown" id="severityText">Calculating...</p>
416
+ </div>
417
+
418
+ <div id="waterFootprintMetrics" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-10">
419
+ <!-- Metrics will be populated here -->
420
+ </div>
421
+
422
+ <!-- Water Definitions Section -->
423
+ <div id="waterDefinitions" class="water-definitions hidden">
424
+ <!-- Definitions populated here by JS -->
425
+ </div>
426
+
427
+
428
+ <div class="grid md:grid-cols-2 gap-8 mt-8">
429
+ <div class="bg-chart rounded-xl p-6 shadow-lg">
430
+ <canvas id="waterBreakdownChart"></canvas>
431
+ </div>
432
+ <div class="bg-chart rounded-xl p-6 shadow-lg">
433
+ <canvas id="regionalComparisonChart"></canvas>
434
+ </div>
435
+ </div>
436
+ </div>
437
+
438
+ <!-- Detailed Analysis Tabs -->
439
+ <div class="glass-effect rounded-2xl p-8 mb-12 slide-in hover-lift" style="animation-delay: 0.6s;"> <!-- Added hover-lift -->
440
+ <h2 class="text-2xl sm:text-3xl font-bold text-gray-800 mb-6">
441
+ <i class="fas fa-chart-pie mr-3 text-teal-700"></i>Detailed Analysis
442
+ </h2>
443
+ <div class="flex flex-wrap gap-3 mb-8" id="tabButtons">
444
+ <button class="tab-button px-4 py-2 sm:px-6 sm:py-3 rounded-lg font-medium active" data-tab="environmental">
445
+ <i class="fas fa-leaf mr-2 text-green-700"></i>Environmental Impact
446
+ </button>
447
+ <button class="tab-button px-4 py-2 sm:px-6 sm:py-3 rounded-lg font-medium" data-tab="production">
448
+ <i class="fas fa-industry mr-2 text-gray-600"></i>Production Insights
449
+ </button>
450
+ <button class="tab-button px-4 py-2 sm:px-6 sm:py-3 rounded-lg font-medium" data-tab="comparisons">
451
+ <i class="fas fa-chart-bar mr-2 text-orange-700"></i>Comparisons
452
+ </button>
453
+ <button class="tab-button px-4 py-2 sm:px-6 sm:py-3 rounded-lg font-medium" data-tab="recommendations">
454
+ <i class="fas fa-lightbulb mr-2 text-purple-700"></i>Recommendations
455
+ </button>
456
+ <button class="tab-button px-4 py-2 sm:px-6 sm:py-3 rounded-lg font-medium" data-tab="more-metrics">
457
+ <i class="fas fa-balance-scale mr-2 text-cyan-700"></i>More Metrics
458
+ </button>
459
+ </div>
460
+
461
+ <div id="tabContent" class="fade-in">
462
+ <!-- Tab content will be populated here -->
463
+ </div>
464
+ </div>
465
+
466
+ <!-- Fun Facts Section -->
467
+ <div class="glass-effect rounded-2xl p-8 mb-12 scale-in hover-lift" style="animation-delay: 0.8s;"> <!-- Added hover-lift -->
468
+ <h2 class="text-2xl sm:text-3xl font-bold text-gray-800 mb-6">
469
+ <i class="fas fa-star mr-3 text-yellow-600"></i>Interesting Facts
470
+ </h2>
471
+ <div id="funFacts" class="grid md:grid-cols-2 gap-6">
472
+ <!-- Facts will be populated here -->
473
+ </div>
474
+ </div>
475
+ </div>
476
+ </div>
477
+
478
+ <!-- Footer removed -->
479
+
480
+ <script>
481
+ let uploadedImage = null;
482
+ let currentImageFile = null;
483
+ let currentCharts = {}; // Store chart instances
484
+
485
+ // DOM Elements
486
+ const uploadContainer = document.getElementById('uploadContainer'); // New
487
+ const uploadArea = document.getElementById('uploadArea');
488
+ const imageInput = document.getElementById('imageInput');
489
+ const imagePreview = document.getElementById('imagePreview');
490
+ const uploadAreaContent = uploadArea.querySelector('.upload-area-content');
491
+ const analyzeBtn = document.getElementById('analyzeBtn');
492
+ const loadingSection = document.getElementById('loadingSection');
493
+ const loadingMessage = document.getElementById('loadingMessage');
494
+ const loadingSubMessage = document.getElementById('loadingSubMessage');
495
+ const resultsSection = document.getElementById('resultsSection');
496
+ const errorSection = document.getElementById('errorSection');
497
+ const errorMessage = document.getElementById('errorMessage');
498
+ const fallbackMessage = document.getElementById('fallbackMessage');
499
+ const severityText = document.getElementById('severityText');
500
+ const waterDefinitionsSection = document.getElementById('waterDefinitions');
501
+
502
+
503
+ // Upload functionality
504
+ uploadArea.addEventListener('click', (e) => {
505
+ // Only trigger input click if not clicking on the image itself when previewing
506
+ if (e.target !== imagePreview && !uploadArea.classList.contains('has-image')) { // Check if it's the preview or if there's no image
507
+ imageInput.click();
508
+ } else if (e.target === imagePreview && uploadArea.classList.contains('has-image')) {
509
+ // If clicking the image preview itself, also trigger input click to allow changing image
510
+ imageInput.click();
511
+ } else if (e.target !== imagePreview && uploadArea.classList.contains('has-image')) {
512
+ // If clicking the text area when an image is present, also trigger input click
513
+ imageInput.click();
514
+ }
515
+ });
516
+ uploadArea.addEventListener('dragover', handleDragOver);
517
+ uploadArea.addEventListener('dragleave', handleDragLeave);
518
+ uploadArea.addEventListener('drop', handleDrop);
519
+ imageInput.addEventListener('change', handleFileSelect);
520
+
521
+ function handleDragOver(e) {
522
+ e.preventDefault();
523
+ uploadArea.classList.add('dragover');
524
+ }
525
+
526
+ function handleDragLeave(e) {
527
+ e.preventDefault();
528
+ uploadArea.classList.remove('dragover');
529
+ }
530
+
531
+ function handleDrop(e) {
532
+ e.preventDefault();
533
+ uploadArea.classList.remove('dragover');
534
+ const files = e.dataTransfer.files;
535
+ if (files.length > 0) {
536
+ handleFile(files[0]);
537
+ }
538
+ }
539
+
540
+ function handleFileSelect(e) {
541
+ const file = e.target.files[0];
542
+ if (file) {
543
+ handleFile(file);
544
+ }
545
+ }
546
+
547
+ function handleFile(file) {
548
+ if (!file.type.startsWith('image/')) {
549
+ alert('Please select a valid image file.');
550
+ return;
551
+ }
552
+
553
+ currentImageFile = file;
554
+ const reader = new FileReader();
555
+ reader.onload = function(e) {
556
+ uploadedImage = e.target.result;
557
+ updateUploadArea(file.name, uploadedImage);
558
+ analyzeBtn.disabled = false;
559
+ };
560
+ reader.readAsDataURL(file);
561
+ }
562
+
563
+ function updateUploadArea(fileName, imageUrl) {
564
+ uploadArea.classList.remove('dragover');
565
+ uploadArea.classList.add('has-image');
566
+ uploadArea.classList.remove('shine-effect'); // Remove animation after upload
567
+ uploadContainer.classList.remove('shine-effect'); // Remove from container too
568
+
569
+ // Display image preview
570
+ imagePreview.src = imageUrl;
571
+ imagePreview.classList.remove('hidden');
572
+
573
+ // Update text content
574
+ uploadAreaContent.innerHTML = `
575
+ <div class="text-xl sm:text-2xl text-green-700 font-medium mb-2"><i class="fas fa-check-circle text-green-500 mr-2"></i>${fileName}</div>
576
+ <p class="text-green-600 text-base sm:text-lg">Image uploaded successfully! Click area or image to change.</p>
577
+ `;
578
+
579
+ // Arrange elements: image first, then content
580
+ uploadArea.insertBefore(imagePreview, uploadAreaContent);
581
+ }
582
+
583
+ function resetUploadArea() {
584
+ uploadedImage = null;
585
+ currentImageFile = null;
586
+ analyzeBtn.disabled = true;
587
+ analyzeBtn.innerHTML = '<i class="fas fa-chart-line mr-3"></i> Analyze Water Footprint';
588
+
589
+ uploadArea.classList.remove('has-image');
590
+ uploadArea.classList.add('shine-effect'); // Add shine back to area
591
+ uploadContainer.classList.add('shine-effect'); // Add shine back to container
592
+
593
+ if (imagePreview.parentNode === uploadArea) {
594
+ imagePreview.classList.add('hidden'); // Hide it
595
+ imagePreview.src = ''; // Clear the image source
596
+ // Remove the image from the parent DOM temporarily if needed, or just hide it well
597
+ // Let's just hide and clear src, re-inserting seems overly complex.
598
+ }
599
+
600
+
601
+ uploadAreaContent.innerHTML = `
602
+ <div class="text-6xl sm:text-7xl mb-6">πŸ“Έ</div>
603
+ <p class="text-xl sm:text-2xl text-green-700 font-medium mb-3">Drop your image here or click to browse</p>
604
+ <p class="text-green-600 text-base sm:text-lg">Supports JPG, PNG, WebP formats</p>
605
+ `;
606
+ imageInput.value = null; // Clear file input value
607
+ // Ensure content is correctly positioned - maybe just remove image and let content take over
608
+ if (imagePreview.parentNode === uploadArea) {
609
+ uploadArea.removeChild(imagePreview); // Remove the image element
610
+ }
611
+ // Add it back hidden so the next upload can insert it again
612
+ uploadArea.appendChild(imagePreview);
613
+ imagePreview.classList.add('hidden'); // Ensure it's hidden initially
614
+
615
+ }
616
+
617
+
618
+ // Analysis functionality
619
+ analyzeBtn.addEventListener('click', analyzeImage);
620
+
621
+ const loadingMessages = [
622
+ "Detecting product...",
623
+ "Estimating water usage...",
624
+ "Analyzing environmental impact...",
625
+ "Comparing metrics...",
626
+ "Generating recommendations...",
627
+ "Finalizing report..."
628
+ ];
629
+ let messageIndex = 0;
630
+ let loadingInterval;
631
+
632
+ function startLoadingAnimation() {
633
+ loadingMessage.textContent = loadingMessages[messageIndex];
634
+ loadingSubMessage.textContent = "Processing your image and calculating environmental impact...";
635
+
636
+ loadingInterval = setInterval(() => {
637
+ messageIndex = (messageIndex + 1) % loadingMessages.length;
638
+ loadingMessage.textContent = loadingMessages[messageIndex];
639
+ }, 2000);
640
+ }
641
+
642
+ function stopLoadingAnimation() {
643
+ clearInterval(loadingInterval);
644
+ messageIndex = 0;
645
+ }
646
+
647
+
648
+ async function analyzeImage() {
649
+ if (!uploadedImage) return;
650
+
651
+ // Hide previous results/errors, show loading
652
+ resultsSection.classList.add('hidden');
653
+ errorSection.classList.add('hidden');
654
+ loadingSection.classList.remove('hidden');
655
+ startLoadingAnimation();
656
+
657
+ analyzeBtn.disabled = true;
658
+ analyzeBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Analyzing...';
659
+
660
+
661
+ try {
662
+ const response = await fetch('/analyze', {
663
+ method: 'POST',
664
+ headers: {
665
+ 'Content-Type': 'application/json',
666
+ },
667
+ body: JSON.stringify({
668
+ image: uploadedImage
669
+ })
670
+ });
671
+
672
+ const result = await response.json();
673
+ stopLoadingAnimation();
674
+
675
+ if (response.ok && result.success) { // Check both HTTP status and internal success flag
676
+ displayResults(result.data);
677
+ loadingSection.classList.add('hidden');
678
+ resultsSection.classList.remove('hidden');
679
+ } else {
680
+ // Handle non-200 HTTP responses or API success: False
681
+ const errorReason = result.error || `HTTP Error: ${response.status}`;
682
+ console.error('Analysis API returned failure:', errorReason, result);
683
+
684
+ loadingSection.classList.add('hidden');
685
+ errorSection.classList.remove('hidden');
686
+ errorMessage.textContent = errorReason;
687
+
688
+ // If partial data is present, display it and show a fallback message
689
+ if (result.data && (Object.keys(result.data).length > 0 || (result.data.raw_analysis_text_fallback))) {
690
+ fallbackMessage.textContent = "Partial data might be available below. Raw AI output included for reference.";
691
+ fallbackMessage.classList.remove('hidden');
692
+ displayResults(result.data); // Try displaying partial data
693
+ resultsSection.classList.remove('hidden'); // Show results section even with partial data
694
+ } else {
695
+ // No usable data fallback
696
+ fallbackMessage.classList.add('hidden');
697
+ // Clear any potentially partial results data display from a previous run
698
+ clearResultsDisplay();
699
+ }
700
+ }
701
+ } catch (error) {
702
+ // Handle network errors or other unexpected exceptions
703
+ console.error('Error during analysis fetch:', error);
704
+ stopLoadingAnimation();
705
+ loadingSection.classList.add('hidden');
706
+ errorSection.classList.remove('hidden');
707
+ errorMessage.textContent = 'Network error or unexpected issue: ' + error.message;
708
+ fallbackMessage.classList.add('hidden');
709
+ // Clear any potentially partial results data display
710
+ clearResultsDisplay();
711
+ } finally {
712
+ analyzeBtn.disabled = false;
713
+ analyzeBtn.innerHTML = '<i class="fas fa-chart-line mr-2"></i>Analyze Water Footprint';
714
+ }
715
+ }
716
+
717
+ function clearResultsDisplay() {
718
+ document.getElementById('productInfo').innerHTML = '';
719
+ document.getElementById('waterFootprintMetrics').innerHTML = '';
720
+ document.getElementById('waterDefinitions').innerHTML = '';
721
+ document.getElementById('tabContent').innerHTML = '';
722
+ document.getElementById('funFacts').innerHTML = '';
723
+ document.getElementById('overallSeverity').querySelector('#severityText').textContent = 'N/A'; // Reset severity text
724
+ document.getElementById('overallSeverity').querySelector('#severityText').className = 'text-3xl font-bold severity-unknown'; // Reset severity styling
725
+
726
+ // Destroy charts explicitly and replace canvases with placeholders
727
+ const waterBreakdownChartContainer = document.getElementById('waterBreakdownChart').closest('.bg-chart');
728
+ if (currentCharts['waterBreakdownChart']) {
729
+ currentCharts['waterBreakdownChart'].destroy();
730
+ currentCharts['waterBreakdownChart'] = null;
731
+ }
732
+ if (waterBreakdownChartContainer) waterBreakdownChartContainer.innerHTML = '<canvas id="waterBreakdownChart"></canvas><p class="text-center text-gray-600 mt-4">Chart data not available.</p>'; // Add message
733
+
734
+ const regionalComparisonChartContainer = document.getElementById('regionalComparisonChart').closest('.bg-chart');
735
+ if (currentCharts['regionalComparisonChart']) {
736
+ currentCharts['regionalComparisonChart'].destroy();
737
+ currentCharts['regionalComparisonChart'] = null;
738
+ }
739
+ if (regionalComparisonChartContainer) regionalComparisonChartContainer.innerHTML = '<canvas id="regionalComparisonChart"></canvas><p class="text-center text-gray-600 mt-4">Chart data not available.</p>'; // Add message
740
+
741
+ // Hide the definitions section
742
+ document.getElementById('waterDefinitions').classList.add('hidden');
743
+ }
744
+
745
+
746
+ function displayResults(data) {
747
+ // Destroy previous charts if they exist
748
+ for (const chartId in currentCharts) {
749
+ if (currentCharts[chartId]) {
750
+ currentCharts[chartId].destroy();
751
+ currentCharts[chartId] = null;
752
+ }
753
+ }
754
+
755
+ displayProductInfo(data.product_identification);
756
+ displayOverallSeverity(data.overall_severity);
757
+ displayWaterFootprint(data.water_footprint);
758
+ displayWaterDefinitions(data.definitions);
759
+ setupTabs(data);
760
+ displayFunFacts(data.interesting_facts || []);
761
+
762
+
763
+ // Ensure canvas containers exist and are ready before creating charts
764
+ const waterBreakdownChartElement = document.getElementById('waterBreakdownChart');
765
+ const regionalComparisonChartElement = document.getElementById('regionalComparisonChart');
766
+
767
+ // Replace placeholder message with clean canvas if data exists
768
+ if (data.water_breakdown_chart && waterBreakdownChartElement && waterBreakdownChartElement.closest('.bg-chart').querySelector('p')) {
769
+ waterBreakdownChartElement.closest('.bg-chart').innerHTML = '<canvas id="waterBreakdownChart"></canvas>';
770
+ }
771
+ if (data.regional_comparison_chart && regionalComparisonChartElement && regionalComparisonChartElement.closest('.bg-chart').querySelector('p')) {
772
+ regionalComparisonChartElement.closest('.bg-chart').innerHTML = '<canvas id="regionalComparisonChart"></canvas>';
773
+ }
774
+
775
+
776
+ // Create charts AFTER the results section is shown and potential partial data handled
777
+ setTimeout(() => {
778
+ // Re-get the canvas elements in case they were replaced
779
+ const newWaterBreakdownCtx = document.getElementById('waterBreakdownChart')?.getContext('2d');
780
+ const newRegionalComparisonCtx = document.getElementById('regionalComparisonChart')?.getContext('2d');
781
+
782
+ if (data.water_breakdown_chart && newWaterBreakdownCtx) {
783
+ createWaterBreakdownChart(data.water_breakdown_chart, newWaterBreakdownCtx);
784
+ } else if (newWaterBreakdownCtx?.closest('.bg-chart')) {
785
+ // If element exists but no data, show message
786
+ newWaterBreakdownCtx.closest('.bg-chart').innerHTML = '<canvas id="waterBreakdownChart"></canvas><p class="text-center text-gray-600 mt-4">Chart data not available.</p>';
787
+ }
788
+
789
+ if (data.regional_comparison_chart && newRegionalComparisonCtx) {
790
+ createRegionalComparisonChart(data.regional_comparison_chart, newRegionalComparisonCtx);
791
+ } else if (newRegionalComparisonCtx?.closest('.bg-chart')) {
792
+ // If element exists but no data, show message
793
+ newRegionalComparisonCtx.closest('.bg-chart').innerHTML = '<canvas id="regionalComparisonChart"></canvas><p class="text-center text-gray-600 mt-4">Chart data not available.</p>';
794
+ }
795
+
796
+ }, 100); // Small delay
797
+
798
+ // Trigger animations for results sections
799
+ document.querySelectorAll('#resultsSection > div').forEach((el, index) => {
800
+ el.classList.remove('slide-in', 'scale-in'); // Reset if already present
801
+ void el.offsetWidth; // Trigger reflow
802
+ // Re-add classes to re-trigger animation
803
+ if (index % 2 === 0) el.classList.add('slide-in');
804
+ else el.classList.add('scale-in');
805
+ el.style.animationDelay = `${0.2 + index * 0.15}s`; // Slightly faster delay
806
+ });
807
+ }
808
+
809
+ function displayOverallSeverity(severity) {
810
+ const severityElement = document.getElementById('severityText');
811
+ let severityClass = 'severity-unknown';
812
+
813
+ severityElement.textContent = severity || 'Unknown';
814
+ severityElement.classList.remove('severity-low', 'severity-medium', 'severity-high', 'severity-very-high', 'severity-unknown');
815
+
816
+ if (severity) {
817
+ const lowerSeverity = severity.toLowerCase();
818
+ if (lowerSeverity.includes('very high')) {
819
+ severityClass = 'severity-very-high';
820
+ } else if (lowerSeverity.includes('high')) {
821
+ severityClass = 'severity-high';
822
+ } else if (lowerSeverity.includes('medium')) {
823
+ severityClass = 'severity-medium';
824
+ } else if (lowerSeverity.includes('low')) {
825
+ severityClass = 'severity-low';
826
+ }
827
+ }
828
+ severityElement.classList.add(severityClass);
829
+ }
830
+
831
+
832
+ function displayProductInfo(productInfo) {
833
+ const container = document.getElementById('productInfo');
834
+ if (!productInfo || Object.keys(productInfo).length === 0 || (!productInfo.detected_product && !productInfo.category)) {
835
+ container.innerHTML = '<p class="text-gray-600 col-span-2">Product identification data not available.</p>';
836
+ return;
837
+ }
838
+ container.innerHTML = `
839
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
840
+ <h3 class="text-xl font-semibold mb-4">
841
+ <i class="fas fa-seedling mr-2 text-primary"></i>Detected Product
842
+ </h3>
843
+ <p class="text-3xl font-bold text-primary">${productInfo?.detected_product || 'Unknown'}</p>
844
+ <p class="text-gray-600 mt-2 text-sm">Confidence: ${productInfo?.confidence || 'N/A'}</p>
845
+ </div>
846
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
847
+ <h3 class="text-xl font-semibold mb-4">
848
+ <i class="fas fa-tags mr-2 text-accent"></i>Category
849
+ </h3>
850
+ <p class="text-2xl font-bold text-secondary">${productInfo?.category || 'Agricultural Product'}</p>
851
+ <p class="text-gray-600 mt-2 text-sm">${productInfo?.scientific_name || 'Classification'}</p>
852
+ </div>
853
+ `;
854
+ }
855
+
856
+ function displayWaterFootprint(footprint) {
857
+ const container = document.getElementById('waterFootprintMetrics');
858
+ if (!footprint || Object.keys(footprint).length === 0 || (!footprint.total_footprint && !footprint.green_water && !footprint.blue_water && !footprint.grey_water)) {
859
+ container.innerHTML = '<p class="text-gray-600 col-span-4">Water footprint data not available.</p>';
860
+ return;
861
+ }
862
+ container.innerHTML = `
863
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
864
+ <h3 class="text-lg font-semibold mb-2">
865
+ <i class="fas fa-tint mr-2 text-green-600"></i>Total Footprint
866
+ </h3>
867
+ <p class="text-2xl font-bold text-green-600">${footprint?.total_footprint || 'N/A'}</p>
868
+ <p class="text-sm text-gray-600 mt-1">Global Avg: ${footprint?.global_average || 'N/A'}</p>
869
+ </div>
870
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
871
+ <h3 class="text-lg font-semibold mb-2">
872
+ <i class="fas fa-cloud-rain mr-2 text-green-700"></i>Green Water
873
+ </h3>
874
+ <p class="text-2xl font-bold text-green-700">${footprint?.green_water || 'N/A'}</p>
875
+ </div>
876
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
877
+ <h3 class="text-lg font-semibold mb-2">
878
+ <i class="fas fa-water mr-2 text-blue-500"></i>Blue Water
879
+ </h3>
880
+ <p class="text-2xl font-bold text-blue-500">${footprint?.blue_water || 'N/A'}</p>
881
+ </div>
882
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
883
+ <h3 class="text-lg font-semibold mb-2">
884
+ <i class="fas fa-flask mr-2 text-purple-600"></i>Grey Water
885
+ </h3>
886
+ <p class="text-2xl font-bold text-purple-600">${footprint?.grey_water || 'N/A'}</p>
887
+ </div>
888
+ `;
889
+ }
890
+
891
+ function displayWaterDefinitions(definitions) {
892
+ const container = document.getElementById('waterDefinitions');
893
+ if (!definitions || Object.keys(definitions).length === 0 || (!definitions.green_water && !definitions.blue_water && !definitions.grey_water)) {
894
+ container.classList.add('hidden');
895
+ container.innerHTML = '';
896
+ return;
897
+ }
898
+ container.classList.remove('hidden');
899
+
900
+ container.innerHTML = `
901
+ <h3>Understanding Your Water Footprint</h3>
902
+ <dl>
903
+ <dt><i class="fas fa-cloud-rain mr-2 text-green-700"></i>Green Water:</dt>
904
+ <dd>${definitions.green_water || 'The volume of rainwater that is stored as soil moisture and evaporates from the soil and from plants.'}</dd>
905
+
906
+ <dt><i class="fas fa-water mr-2 text-blue-500"></i>Blue Water:</dt>
907
+ <dd>${definitions.blue_water || 'The volume of surface and groundwater required (evaporated or incorporated into a product).'}</dd>
908
+
909
+ <dt><i class="fas fa-flask mr-2 text-purple-600"></i>Grey Water:</dt>
910
+ <dd>${definitions.grey_water || 'The volume of freshwater required to assimilate the load of pollutants based on existing ambient water quality standards.'}</dd>
911
+ </dl>
912
+ `;
913
+ }
914
+
915
+
916
+ function createWaterBreakdownChart(data, ctx) {
917
+ const backgroundColors = data.colors || ['#48BB78', '#4299E1', '#A78BFA']; // Green, Blue, Purple
918
+ // Ensure values are numbers for the chart
919
+ const values = (data.values || []).map(v => parseFloat(v)).filter(v => !isNaN(v));
920
+
921
+ if (currentCharts['waterBreakdownChart']) currentCharts['waterBreakdownChart'].destroy();
922
+ currentCharts['waterBreakdownChart'] = new Chart(ctx, {
923
+ type: 'doughnut',
924
+ data: {
925
+ labels: data.labels || ['Green Water', 'Blue Water', 'Grey Water'],
926
+ datasets: [{
927
+ data: values.length === 3 ? values : [0, 0, 0], // Use parsed values, default to zero
928
+ backgroundColor: backgroundColors,
929
+ borderColor: '#ffffff',
930
+ borderWidth: 2
931
+ }]
932
+ },
933
+ options: {
934
+ responsive: true,
935
+ plugins: {
936
+ title: {
937
+ display: true,
938
+ text: 'Water Footprint Breakdown (%)',
939
+ color: '#2D3748',
940
+ font: { size: 18, weight: 'bold' }
941
+ },
942
+ legend: {
943
+ position: 'bottom',
944
+ labels: { color: '#4A5568', font: { size: 12 } }
945
+ },
946
+ tooltip: {
947
+ callbacks: {
948
+ label: function(tooltipItem) {
949
+ const value = parseFloat(tooltipItem.raw);
950
+ if (isNaN(value)) return tooltipItem.label + ': N/A';
951
+ return tooltipItem.label + ': ' + value.toFixed(1) + '%';
952
+ }
953
+ }
954
+ }
955
+ }
956
+ }
957
+ });
958
+ }
959
+
960
+ function createRegionalComparisonChart(data, ctx) {
961
+ if (currentCharts['regionalComparisonChart']) currentCharts['regionalComparisonChart'].destroy();
962
+
963
+ const barColors = ['#48BB78', '#D69E2E', '#4FD1C5', '#ECC94B']; // Green, Orange, Teal, Yellow
964
+ // Ensure values are numbers for the chart
965
+ const waterUsage = (data.water_usage || []).map(v => parseFloat(v)).filter(v => !isNaN(v));
966
+
967
+
968
+ currentCharts['regionalComparisonChart'] = new Chart(ctx, {
969
+ type: 'bar',
970
+ data: {
971
+ labels: data.regions || ['Global Average', 'Arid Regions', 'Temperate', 'Tropical'],
972
+ datasets: [{
973
+ label: 'Water Usage (L/kg)',
974
+ data: waterUsage.length === 4 ? waterUsage : [0, 0, 0, 0], // Use parsed values, default to zero
975
+ backgroundColor: barColors,
976
+ borderColor: '#ffffff',
977
+ borderWidth: 1
978
+ }]
979
+ },
980
+ options: {
981
+ responsive: true,
982
+ plugins: {
983
+ title: {
984
+ display: true,
985
+ text: 'Regional Water Usage Comparison (L/kg)',
986
+ color: '#2D3748',
987
+ font: { size: 18, weight: 'bold' }
988
+ },
989
+ legend: { display: false }
990
+ },
991
+ scales: {
992
+ y: {
993
+ beginAtZero: true,
994
+ title: {
995
+ display: true,
996
+ text: 'Liters per kg',
997
+ color: '#4A5568',
998
+ font: { size: 14 }
999
+ },
1000
+ ticks: { color: '#4A5568' },
1001
+ grid: { color: 'rgba(0, 0, 0, 0.1)' }
1002
+ },
1003
+ x: {
1004
+ ticks: { color: '#4A5568' },
1005
+ grid: { color: 'rgba(0, 0, 0, 0.1)' }
1006
+ }
1007
+ }
1008
+ }
1009
+ });
1010
+ }
1011
+
1012
+
1013
+ function setupTabs(data) {
1014
+ const tabButtons = document.querySelectorAll('#tabButtons .tab-button');
1015
+ const tabContent = document.getElementById('tabContent');
1016
+
1017
+ tabContent.innerHTML = ''; // Clear previous content
1018
+
1019
+ // Add event listeners to cloned buttons to prevent duplicates
1020
+ tabButtons.forEach(button => {
1021
+ const newButton = button.cloneNode(true);
1022
+ button.parentNode.replaceChild(newButton, button);
1023
+
1024
+ newButton.addEventListener('click', () => {
1025
+ document.querySelectorAll('#tabButtons .tab-button').forEach(btn => {
1026
+ btn.classList.remove('active', 'bg-gradient-to-r', 'from-green-500', 'to-teal-500', 'text-white', 'transform', 'translate-y-[-2px]');
1027
+ btn.classList.add('bg-white', 'bg-opacity-60', 'text-gray-600');
1028
+ });
1029
+
1030
+ newButton.classList.add('active', 'bg-gradient-to-r', 'from-green-500', 'to-teal-500', 'text-white', 'transform', 'translate-y-[-2px]');
1031
+ newButton.classList.remove('bg-white', 'bg-opacity-60', 'text-gray-600');
1032
+
1033
+ const tabType = newButton.getAttribute('data-tab');
1034
+ displayTabContent(tabType, data);
1035
+ });
1036
+ });
1037
+
1038
+
1039
+ // Display initial tab content and set active state
1040
+ const firstButton = document.querySelector('#tabButtons .tab-button');
1041
+ if (firstButton) {
1042
+ firstButton.click();
1043
+ } else {
1044
+ container.innerHTML = '<p class="text-gray-600">Select a tab to view details.</p>';
1045
+ }
1046
+ }
1047
+
1048
+ function displayTabContent(tabType, data) {
1049
+ const container = document.getElementById('tabContent');
1050
+ container.innerHTML = '';
1051
+
1052
+ let contentHTML = '';
1053
+ switch(tabType) {
1054
+ case 'environmental':
1055
+ contentHTML = createEnvironmentalTab(data.environmental_impact);
1056
+ break;
1057
+ case 'production':
1058
+ contentHTML = createProductionTab(data.production_insights);
1059
+ break;
1060
+ case 'comparisons':
1061
+ contentHTML = createComparisonsTab(data.comparisons);
1062
+ break;
1063
+ case 'recommendations':
1064
+ contentHTML = createRecommendationsTab(data.recommendations);
1065
+ break;
1066
+ case 'more-metrics':
1067
+ contentHTML = createMoreMetricsTab(data.impact_metrics);
1068
+ break;
1069
+ default:
1070
+ contentHTML = '<p class="text-gray-600">Select a tab to view details.</p>';
1071
+ break;
1072
+ }
1073
+ container.innerHTML = contentHTML;
1074
+ container.classList.remove('fade-in');
1075
+ void container.offsetWidth;
1076
+ container.classList.add('fade-in');
1077
+
1078
+ // Apply hover class to list items if they exist
1079
+ container.querySelectorAll('.tab-content-list-item').forEach(item => {
1080
+ item.classList.add('hover-lift'); // Use the general hover-lift class
1081
+ });
1082
+ }
1083
+
1084
+ function createEnvironmentalTab(impact) {
1085
+ if (!impact || Object.keys(impact).length === 0 || (!impact.severity_score && !impact.impact_category && !impact.sustainability_rating && !impact.carbon_footprint && !impact.land_use)) {
1086
+ return '<p class="text-gray-600">Environmental impact data not available.</p>';
1087
+ }
1088
+
1089
+ return `
1090
+ <div class="grid md:grid-cols-2 gap-6">
1091
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
1092
+ <h4 class="text-lg font-semibold mb-4">Impact Assessment</h4>
1093
+ <div class="space-y-3 text-gray-700">
1094
+ <div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
1095
+ <span class="text-gray-600">Severity Score:</span>
1096
+ <span class="font-bold text-primary">${impact.severity_score || 'N/A'}</span>
1097
+ </div>
1098
+ <div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
1099
+ <span class="text-gray-600">Impact Category:</span>
1100
+ <span class="font-bold text-secondary">${impact.impact_category || 'N/A'}</span>
1101
+ </div>
1102
+ <div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
1103
+ <span class="text-gray-600">Sustainability Rating:</span>
1104
+ <span class="font-bold text-accent">${impact.sustainability_rating || 'N/A'}</span>
1105
+ </div>
1106
+ </div>
1107
+ </div>
1108
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
1109
+ <h4 class="text-lg font-semibold mb-4">Carbon & Land Impact</h4>
1110
+ <div class="space-y-3 text-gray-700">
1111
+ <div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
1112
+ <span class="text-gray-600">Carbon Footprint:</span>
1113
+ <span class="font-bold text-gray-700">${impact.carbon_footprint || 'N/A'}</span>
1114
+ </div>
1115
+ <div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
1116
+ <span class="text-gray-600">Land Use:</span>
1117
+ <span class="font-bold text-gray-700">${impact.land_use || 'N/A'}</span>
1118
+ </div>
1119
+ </div>
1120
+ </div>
1121
+ </div>
1122
+ `;
1123
+ }
1124
+
1125
+ function createProductionTab(insights) {
1126
+ if (!insights || Object.keys(insights).length === 0 || (!insights.growing_season && !insights.water_efficiency && !insights.irrigation_dependency && !insights.climate_sensitivity && (insights.seasonal_availability?.length === 0 || !insights.seasonal_availability))) {
1127
+ return '<p class="text-gray-600">Production insights not available.</p>';
1128
+ }
1129
+
1130
+ return `
1131
+ <div class="grid md:grid-cols-2 gap-6">
1132
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
1133
+ <h4 class="text-lg font-semibold mb-4">Production Details</h4>
1134
+ <div class="space-y-3 text-gray-700">
1135
+ <div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
1136
+ <span class="text-gray-600">Growing Season:</span>
1137
+ <span class="font-bold">${insights.growing_season || 'N/A'}</span>
1138
+ </div>
1139
+ <div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
1140
+ <span class="text-gray-600">Water Efficiency:</span>
1141
+ <span class="font-bold text-secondary">${insights.water_efficiency || 'N/A'}</span>
1142
+ </div>
1143
+ <div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
1144
+ <span class="text-gray-600">Irrigation Dependency:</span>
1145
+ <span class="font-bold">${insights.irrigation_dependency || 'N/A'}</span>
1146
+ </div>
1147
+ </div>
1148
+ </div>
1149
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
1150
+ <h4 class="text-lg font-semibold mb-4">Climate Factors</h4>
1151
+ <div class="space-y-3 text-gray-700">
1152
+ <div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
1153
+ <span class="text-gray-600">Climate Sensitivity:</span>
1154
+ <span class="font-bold">${insights.climate_sensitivity || 'N/A'}</span>
1155
+ </div>
1156
+ <div class="tab-content-list-item p-2 -mx-2 rounded-md">
1157
+ <span class="text-gray-600">Seasonal Availability:</span>
1158
+ <div class="mt-2 flex flex-wrap gap-2">
1159
+ ${(insights.seasonal_availability || []).map(month =>
1160
+ `<span class="bg-gray-200 text-gray-700 px-2 py-1 rounded text-sm">${month}</span>`
1161
+ ).join('')}
1162
+ ${(insights.seasonal_availability || []).length === 0 ? '<span class="text-gray-500 text-sm">N/A</span>' : ''}
1163
+ </div>
1164
+ </div>
1165
+ </div>
1166
+ </div>
1167
+ </div>
1168
+ `;
1169
+ }
1170
+
1171
+ function createComparisonsTab(comparisons) {
1172
+ if (!comparisons || (comparisons.vs_similar_products?.length === 0 && comparisons.vs_alternatives?.length === 0)) {
1173
+ return '<p class="text-gray-600">Comparison data not available.</p>';
1174
+ }
1175
+
1176
+ return `
1177
+ <div class="space-y-8">
1178
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift overflow-x-auto">
1179
+ <h4 class="text-lg font-semibold mb-4">
1180
+ <i class="fas fa-chart-line mr-2 text-green-600"></i>Similar Products Comparison
1181
+ </h4>
1182
+ ${(comparisons.vs_similar_products && comparisons.vs_similar_products.length > 0) ? `
1183
+ <table class="comparison-table">
1184
+ <thead>
1185
+ <tr>
1186
+ <th>Product</th>
1187
+ <th>Water Footprint</th>
1188
+ <th>Difference</th>
1189
+ </tr>
1190
+ </thead>
1191
+ <tbody>
1192
+ ${comparisons.vs_similar_products.map(product => `
1193
+ <tr>
1194
+ <td class="font-medium text-gray-800">${product.product || 'Unknown'}</td>
1195
+ <td>${product.water_footprint || 'N/A'}</td>
1196
+ <td class="${product.difference?.includes('-') ? 'text-green-600' : product.difference ? 'text-red-600' : 'text-gray-600'}">
1197
+ ${product.difference || 'N/A'}
1198
+ </td>
1199
+ </tr>
1200
+ `).join('')}
1201
+ </tbody>
1202
+ </table>
1203
+ ` : '<p class="text-gray-600">No similar products comparison available.</p>'}
1204
+ </div>
1205
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
1206
+ <h4 class="text-lg font-semibold mb-4">
1207
+ <i class="fas fa-leaf mr-2 text-green-700"></i>Sustainable Alternatives
1208
+ </h4>
1209
+ <div class="space-y-3">
1210
+ ${(comparisons.vs_alternatives || []).map(alt => `
1211
+ <div class="p-3 bg-gray-100 rounded-lg tab-content-list-item hover-lift">
1212
+ <div class="font-medium text-green-700">${alt.alternative || 'Unknown'}</div>
1213
+ <div class="text-sm text-gray-700 mt-1">Saves: ${alt.water_savings || 'N/A'}</div>
1214
+ <div class="text-sm text-gray-600 mt-1">${alt.benefit || ''}</div>
1215
+ </div>
1216
+ `).join('')}
1217
+ ${(comparisons.vs_alternatives || []).length === 0 ? '<p class="text-gray-600">No sustainable alternatives suggested.</p>' : ''}
1218
+ </div>
1219
+ </div>
1220
+ </div>
1221
+ `;
1222
+ }
1223
+
1224
+ function createRecommendationsTab(recommendations) {
1225
+ if (!recommendations || (recommendations.consumer_tips?.length === 0 && recommendations.sustainable_practices?.length === 0 && recommendations.water_conservation?.length === 0 && !recommendations.seasonal_buying)) {
1226
+ return '<p class="text-gray-600">Recommendations not available.</p>';
1227
+ }
1228
+
1229
+ return `
1230
+ <div class="grid md:grid-cols-2 gap-6">
1231
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
1232
+ <h4 class="text-lg font-semibold mb-4">
1233
+ <i class="fas fa-user mr-2 text-purple-700"></i>Consumer Tips
1234
+ </h4>
1235
+ <ul class="space-y-3">
1236
+ ${(recommendations.consumer_tips || []).map(tip =>
1237
+ `<li class="flex items-start space-x-3 tab-content-list-item p-2 -mx-2 rounded-md">
1238
+ <i class="fas fa-check-circle text-green-600 mt-1 text-lg"></i>
1239
+ <span class="text-gray-700 leading-relaxed">${tip}</span>
1240
+ </li>`
1241
+ ).join('')}
1242
+ ${(recommendations.consumer_tips || []).length === 0 ? '<p class="text-gray-600">No consumer tips available.</p>' : ''}
1243
+ </ul>
1244
+ </div>
1245
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
1246
+ <h4 class="text-lg font-semibold mb-4">
1247
+ <i class="fas fa-recycle mr-2 text-green-600"></i>Sustainable Practices (Production)
1248
+ </h4>
1249
+ <ul class="space-y-3">
1250
+ ${(recommendations.sustainable_practices || []).map(practice =>
1251
+ `<li class="flex items-start space-x-3 tab-content-list-item p-2 -mx-2 rounded-md">
1252
+ <i class="fas fa-leaf text-teal-700 mt-1 text-lg"></i>
1253
+ <span class="text-gray-700 leading-relaxed">${practice}</span>
1254
+ </li>`
1255
+ ).join('')}
1256
+ ${(recommendations.sustainable_practices || []).length === 0 ? '<p class="text-gray-600">No sustainable production practices suggested.</p>' : ''}
1257
+ </ul>
1258
+ </div>
1259
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
1260
+ <h4 class="text-lg font-semibold mb-4">
1261
+ <i class="fas fa-tint mr-2 text-cyan-700"></i>Water Conservation Methods
1262
+ </h4>
1263
+ <ul class="space-y-3">
1264
+ ${(recommendations.water_conservation || []).map(method =>
1265
+ `<li class="flex items-start space-x-3 tab-content-list-item p-2 -mx-2 rounded-md">
1266
+ <i class="fas fa-water text-blue-600 mt-1 text-lg"></i>
1267
+ <span class="text-gray-700 leading-relaxed">${method}</span>
1268
+ </li>`
1269
+ ).join('')}
1270
+ ${(recommendations.water_conservation || []).length === 0 ? '<p class="text-gray-600">No water conservation methods suggested.</p>' : ''}
1271
+ </ul>
1272
+ </div>
1273
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
1274
+ <h4 class="text-lg font-semibold mb-4">
1275
+ <i class="fas fa-calendar-alt mr-2 text-orange-600"></i>Seasonal Buying
1276
+ </h4>
1277
+ <p class="text-gray-700 leading-relaxed">
1278
+ <strong class="text-gray-800">Best time to buy:</strong> ${recommendations.seasonal_buying || 'Information not available'}
1279
+ </p>
1280
+ </div>
1281
+ </div>
1282
+ `;
1283
+ }
1284
+
1285
+ function createMoreMetricsTab(metrics) {
1286
+ if (!metrics || Object.keys(metrics).length === 0 || (!metrics.water_stress_contribution && !metrics.biodiversity_impact && !metrics.soil_health_impact && !metrics.economic_water_cost)) {
1287
+ return '<p class="text-gray-600">Additional metrics data not available.</p>';
1288
+ }
1289
+
1290
+ return `
1291
+ <div class="grid md:grid-cols-2 gap-6">
1292
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
1293
+ <h4 class="text-lg font-semibold mb-4">
1294
+ <i class="fas fa-balance-scale mr-2 text-cyan-700"></i>Advanced Metrics
1295
+ </h4>
1296
+ <div class="space-y-3 text-gray-700">
1297
+ <div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
1298
+ <span class="text-gray-600">Water Stress Contribution:</span>
1299
+ <span class="font-bold">${metrics.water_stress_contribution || 'N/A'}</span>
1300
+ </div>
1301
+ <div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
1302
+ <span class="text-gray-600">Biodiversity Impact:</span>
1303
+ <span class="font-bold">${metrics.biodiversity_impact || 'N/A'}</span>
1304
+ </div>
1305
+ <div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
1306
+ <span class="text-gray-600">Soil Health Impact:</span>
1307
+ <span class="font-bold">${metrics.soil_health_impact || 'N/A'}</span>
1308
+ </div>
1309
+ <div class="flex justify-between tab-content-list-item p-2 -mx-2 rounded-md">
1310
+ <span class="text-gray-600">Economic Water Cost:</span>
1311
+ <span class="font-bold">${metrics.economic_water_cost || 'N/A'}</span>
1312
+ </div>
1313
+ </div>
1314
+ </div>
1315
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
1316
+ <h4 class="text-lg font-semibold mb-4">
1317
+ <i class="fas fa-info-circle mr-2 text-green-600"></i>About These Metrics
1318
+ </h4>
1319
+ <p class="text-gray-600 text-sm leading-relaxed">
1320
+ These metrics provide a deeper look into the environmental and economic context of the product's water usage. Water stress contribution relates to the scarcity of water in the production region. Biodiversity and soil health impacts consider broader ecological effects. Economic cost is an estimate of the water's value. Data availability may vary.
1321
+ </p>
1322
+ </div>
1323
+ </div>
1324
+ `;
1325
+ }
1326
+
1327
+
1328
+ function displayFunFacts(facts) {
1329
+ const container = document.getElementById('funFacts');
1330
+ if (!facts || facts.length === 0) {
1331
+ container.innerHTML = '<p class="text-gray-600 col-span-2">No interesting facts available.</p>';
1332
+ return;
1333
+ }
1334
+ container.innerHTML = facts.map((fact, index) => `
1335
+ <div class="bg-card rounded-xl p-6 metric-card hover-lift">
1336
+ <div class="flex items-start space-x-4">
1337
+ <div class="text-3xl">${['πŸ’‘', '🌍', 'πŸ“Š', 'πŸ”¬', 'πŸ’§', '🌱', '🍎', 'πŸ₯•'][index % 8]}</div>
1338
+ <p class="text-gray-700 leading-relaxed text-sm">${fact}</p>
1339
+ </div>
1340
+ </div>
1341
+ `).join('');
1342
+ }
1343
+
1344
+ // Initialize page animations
1345
+ document.addEventListener('DOMContentLoaded', function() {
1346
+ document.querySelector('header').style.opacity = 0;
1347
+ document.getElementById('uploadContainer').style.opacity = 0;
1348
+
1349
+ setTimeout(() => {
1350
+ document.querySelector('header').style.opacity = 1;
1351
+ document.querySelector('header').classList.add('slide-in');
1352
+ }, 200);
1353
+
1354
+ setTimeout(() => {
1355
+ document.getElementById('uploadContainer').style.opacity = 1;
1356
+ document.getElementById('uploadContainer').classList.add('scale-in');
1357
+ }, 400);
1358
+ });
1359
+
1360
+ // Reset on load for clean state
1361
+ resetUploadArea();
1362
+ </script>
1363
+ </body>
1364
+ </html>