prishasinghai commited on
Commit
66b4057
Β·
verified Β·
1 Parent(s): 10d34a0

Create index.html

Browse files
Files changed (1) hide show
  1. index.html +733 -19
index.html CHANGED
@@ -1,19 +1,733 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </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
+ <title>Silverstone British GP β€” Crowd Flow Optimiser</title>
7
+ <style>
8
+ /* ================================================================== */
9
+ /* 1. STYLING & DASHBOARD LAYOUT (CSS) */
10
+ /* ================================================================== */
11
+ * {
12
+ box-sizing: border-box;
13
+ margin: 0;
14
+ padding: 0;
15
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16
+ }
17
+
18
+ body {
19
+ background-color: #121212;
20
+ color: #ffffff;
21
+ padding: 20px;
22
+ }
23
+
24
+ header {
25
+ text-align: center;
26
+ margin-bottom: 20px;
27
+ }
28
+
29
+ header h1 {
30
+ color: #e10600; /* F1 Red Color */
31
+ font-size: 24px;
32
+ margin-bottom: 5px;
33
+ }
34
+
35
+ header p {
36
+ color: #aaa;
37
+ font-size: 14px;
38
+ }
39
+
40
+ /* Two-column responsive grid layout */
41
+ .dashboard {
42
+ display: flex;
43
+ flex-wrap: wrap;
44
+ gap: 20px;
45
+ max-width: 1450px;
46
+ margin: 0 auto;
47
+ }
48
+
49
+ /* Left Panel: Sliders & Controls */
50
+ .controls-panel {
51
+ flex: 1;
52
+ min-width: 320px;
53
+ background-color: #1e1e1e;
54
+ padding: 20px;
55
+ border-radius: 10px;
56
+ border: 1px solid #333;
57
+ }
58
+
59
+ .controls-panel h2 {
60
+ font-size: 18px;
61
+ margin-bottom: 15px;
62
+ color: #00cc66;
63
+ }
64
+
65
+ .slider-group {
66
+ margin-bottom: 15px;
67
+ }
68
+
69
+ .slider-group label {
70
+ display: block;
71
+ font-size: 13px;
72
+ margin-bottom: 6px;
73
+ color: #ddd;
74
+ }
75
+
76
+ .slider-group input[type="range"] {
77
+ width: 100%;
78
+ cursor: pointer;
79
+ }
80
+
81
+ .value-badge {
82
+ color: #e10600;
83
+ font-weight: bold;
84
+ }
85
+
86
+ /* Online Ordering Section inside Controls Panel */
87
+ .order-btn {
88
+ width: 100%;
89
+ padding: 12px;
90
+ background-color: #e10600;
91
+ color: white;
92
+ border: none;
93
+ border-radius: 6px;
94
+ font-weight: bold;
95
+ font-size: 14px;
96
+ cursor: pointer;
97
+ margin-top: 10px;
98
+ transition: background 0.2s;
99
+ }
100
+
101
+ .order-btn:hover {
102
+ background-color: #ff1e1e;
103
+ }
104
+
105
+ .active-orders-list {
106
+ margin-top: 15px;
107
+ max-height: 180px;
108
+ overflow-y: auto;
109
+ background: #141414;
110
+ padding: 10px;
111
+ border-radius: 6px;
112
+ border: 1px solid #333;
113
+ }
114
+
115
+ .order-card {
116
+ background: #252525;
117
+ padding: 8px;
118
+ margin-bottom: 8px;
119
+ border-radius: 4px;
120
+ border-left: 3px solid #f39c12;
121
+ font-size: 12px;
122
+ }
123
+
124
+ .order-card.ready {
125
+ border-left-color: #00cc66;
126
+ }
127
+
128
+ /* Modal Overlay */
129
+ .modal {
130
+ display: none;
131
+ position: fixed;
132
+ z-index: 1000;
133
+ left: 0;
134
+ top: 0;
135
+ width: 100%;
136
+ height: 100%;
137
+ background-color: rgba(0, 0, 0, 0.8);
138
+ justify-content: center;
139
+ align-items: center;
140
+ }
141
+
142
+ .modal-content {
143
+ background-color: #1e1e1e;
144
+ padding: 25px;
145
+ border-radius: 10px;
146
+ border: 1px solid #444;
147
+ width: 400px;
148
+ max-width: 90%;
149
+ }
150
+
151
+ .modal-content h3 {
152
+ color: #e10600;
153
+ margin-bottom: 15px;
154
+ }
155
+
156
+ .form-group {
157
+ margin-bottom: 12px;
158
+ }
159
+
160
+ .form-group label {
161
+ display: block;
162
+ font-size: 13px;
163
+ margin-bottom: 5px;
164
+ color: #ccc;
165
+ }
166
+
167
+ .form-group select, .form-group input {
168
+ width: 100%;
169
+ padding: 8px;
170
+ background: #2b2b2b;
171
+ border: 1px solid #444;
172
+ color: white;
173
+ border-radius: 4px;
174
+ }
175
+
176
+ .modal-actions {
177
+ display: flex;
178
+ gap: 10px;
179
+ margin-top: 20px;
180
+ }
181
+
182
+ .btn-submit {
183
+ flex: 1;
184
+ background: #00cc66;
185
+ color: black;
186
+ padding: 10px;
187
+ border: none;
188
+ border-radius: 4px;
189
+ font-weight: bold;
190
+ cursor: pointer;
191
+ }
192
+
193
+ .btn-cancel {
194
+ flex: 1;
195
+ background: #444;
196
+ color: white;
197
+ padding: 10px;
198
+ border: none;
199
+ border-radius: 4px;
200
+ cursor: pointer;
201
+ }
202
+
203
+ /* Right Panel: Interactive Canvas Map & Report */
204
+ .map-panel {
205
+ flex: 2;
206
+ min-width: 450px;
207
+ background-color: #1e1e1e;
208
+ padding: 20px;
209
+ border-radius: 10px;
210
+ border: 1px solid #333;
211
+ }
212
+
213
+ canvas {
214
+ width: 100%;
215
+ height: auto;
216
+ background-color: #181818;
217
+ border-radius: 8px;
218
+ border: 1px solid #444;
219
+ display: block;
220
+ }
221
+
222
+ /* Report Box Styling */
223
+ .report-box {
224
+ margin-top: 15px;
225
+ padding: 15px;
226
+ background-color: #252525;
227
+ border-radius: 8px;
228
+ border-left: 5px solid #00cc66;
229
+ font-size: 14px;
230
+ line-height: 1.5;
231
+ }
232
+
233
+ .report-box.alert {
234
+ border-left-color: #ff4444;
235
+ background-color: #2a1a1a;
236
+ }
237
+
238
+ .report-box h3 {
239
+ margin-bottom: 8px;
240
+ font-size: 16px;
241
+ }
242
+
243
+ .badge {
244
+ display: inline-block;
245
+ padding: 3px 8px;
246
+ border-radius: 4px;
247
+ font-size: 12px;
248
+ font-weight: bold;
249
+ }
250
+
251
+ .badge-red { background-color: #ff4444; color: white; }
252
+ .badge-green { background-color: #00cc66; color: black; }
253
+ .badge-purple { background-color: #a020f0; color: white; }
254
+ </style>
255
+ </head>
256
+ <body>
257
+
258
+ <header>
259
+ <h1>🏎️ SILVERSTONE BRITISH GP β€” CROWD FLOW OPTIMISER</h1>
260
+ <p>Real-time interconnected crowd routing, dynamic bottleneck propagation & online ordering integration</p>
261
+ </header>
262
+
263
+ <div class="dashboard">
264
+ <!-- ============================================================== -->
265
+ <!-- LEFT PANEL: USER SLIDER INPUTS & ONLINE ORDERING -->
266
+ <!-- ============================================================== -->
267
+ <div class="controls-panel">
268
+ <h2>βš™οΈ Grandstand Crowd Inputs</h2>
269
+
270
+ <div class="slider-group">
271
+ <label for="hamilton">Hamilton Straight GS: <span id="hamilton-val" class="value-badge">65</span> fans/min</label>
272
+ <input type="range" id="hamilton" min="10" max="100" value="65" oninput="updateSimulation()">
273
+ </div>
274
+
275
+ <div class="slider-group">
276
+ <label for="becketts">Becketts GS: <span id="becketts-val" class="value-badge">60</span> fans/min</label>
277
+ <input type="range" id="becketts" min="10" max="100" value="60" oninput="updateSimulation()">
278
+ </div>
279
+
280
+ <div class="slider-group">
281
+ <label for="stowe">Stowe GS: <span id="stowe-val" class="value-badge">40</span> fans/min</label>
282
+ <input type="range" id="stowe" min="10" max="100" value="40" oninput="updateSimulation()">
283
+ </div>
284
+
285
+ <div class="slider-group">
286
+ <label for="village">Village GS: <span id="village-val" class="value-badge">55</span> fans/min</label>
287
+ <input type="range" id="village" min="10" max="100" value="55" oninput="updateSimulation()">
288
+ </div>
289
+
290
+ <div class="slider-group">
291
+ <label for="club">Club GS: <span id="club-val" class="value-badge">50</span> fans/min</label>
292
+ <input type="range" id="club" min="10" max="100" value="50" oninput="updateSimulation()">
293
+ </div>
294
+
295
+ <hr style="border-color: #333; margin: 15px 0;">
296
+
297
+ <!-- ONLINE ORDERING SECTION -->
298
+ <h2 style="color: #f39c12;">πŸ” Click & Collect Food Express</h2>
299
+ <button class="order-btn" onclick="openOrderModal()">πŸ›’ Place Express Food/Merch Order</button>
300
+
301
+ <div style="margin-top: 15px;">
302
+ <span style="font-size: 13px; font-weight: bold; color: #aaa;">Active Mobile Orders:</span>
303
+ <div id="ordersContainer" class="active-orders-list">
304
+ <p style="color: #666; font-size: 12px; text-align: center;">No active mobile orders</p>
305
+ </div>
306
+ </div>
307
+
308
+ <hr style="border-color: #333; margin: 15px 0;">
309
+
310
+ <div style="font-size: 12px; color: #aaa; line-height: 1.4;">
311
+ <p><strong>πŸ’‘ Dynamic Flow Rules:</strong></p>
312
+ <p>β€’ <strong>Gate 1 Threshold:</strong> 50 fans/min</p>
313
+ <p>β€’ <strong>Gate 3 Threshold:</strong> 45 fans/min</p>
314
+ <p>β€’ Bottlenecks cause cascading <span class="badge badge-red">RED</span> corridor congestions.</p>
315
+ <p>β€’ Active rerouting renders complex multi-node <span class="badge badge-purple">PURPLE</span> Google Maps detours.</p>
316
+ </div>
317
+ </div>
318
+
319
+ <!-- ============================================================== -->
320
+ <!-- RIGHT PANEL: HTML5 CANVAS MAP & TEXT ANALYSIS REPORT -->
321
+ <!-- ============================================================== -->
322
+ <div class="map-panel">
323
+ <canvas id="silverstoneCanvas" width="900" height="520"></canvas>
324
+ <div id="reportBox" class="report-box">
325
+ <!-- Dynamic Text Report Injected Here -->
326
+ </div>
327
+ </div>
328
+ </div>
329
+
330
+ <!-- ONLINE ORDER MODAL DIALOG -->
331
+ <div id="orderModal" class="modal">
332
+ <div class="modal-content">
333
+ <h3>πŸ” Place Online Pickup Order</h3>
334
+ <div class="form-group">
335
+ <label for="pickupLocation">Select Pickup Location:</label>
336
+ <select id="pickupLocation">
337
+ <option value="Main Fan Zone">Main Fan Zone</option>
338
+ <option value="Becketts Food">Becketts Food</option>
339
+ <option value="Village Plaza">Village Plaza</option>
340
+ <option value="Club Eats">Club Eats</option>
341
+ </select>
342
+ </div>
343
+ <div class="form-group">
344
+ <label for="itemSelect">Select Express Menu Item:</label>
345
+ <select id="itemSelect">
346
+ <option value="F1 Burger & Chips Combo (Β£14.00)">F1 Burger & Chips Combo (Β£14.00)</option>
347
+ <option value="Woodfired Pizza Slices (Β£11.50)">Woodfired Pizza Slices (Β£11.50)</option>
348
+ <option value="Grid Soft Drink & Fries (Β£7.00)">Grid Soft Drink & Fries (Β£7.00)</option>
349
+ <option value="Silverstone Official Cap (Β£30.00)">Silverstone Official Cap (Β£30.00)</option>
350
+ </select>
351
+ </div>
352
+ <div class="form-group">
353
+ <label for="custName">Customer Name / Ticket ID:</label>
354
+ <input type="text" id="custName" placeholder="e.g. John - Row C" value="Seat 14B">
355
+ </div>
356
+ <div class="modal-actions">
357
+ <button class="btn-submit" onclick="submitOrder()">Confirm Order</button>
358
+ <button class="btn-cancel" onclick="closeOrderModal()">Cancel</button>
359
+ </div>
360
+ </div>
361
+ </div>
362
+
363
+ <!-- ================================================================== -->
364
+ <!-- 2. JAVASCRIPT SIMULATION & CANVAS DRAWING LOGIC -->
365
+ <!-- ================================================================== -->
366
+ <script>
367
+ // Gate Capacity Limits
368
+ const GATE_1_CAPACITY = 50;
369
+ const GATE_3_CAPACITY = 45;
370
+
371
+ // Active Orders State Management
372
+ let activeOrders = [];
373
+ let orderCounter = 101;
374
+
375
+ function updateSimulation() {
376
+ // 1. READ SLIDER VALUES FROM HTML INPUTS
377
+ const hamiltonCrowd = parseInt(document.getElementById('hamilton').value);
378
+ const beckettsCrowd = parseInt(document.getElementById('becketts').value);
379
+ const stoweCrowd = parseInt(document.getElementById('stowe').value);
380
+ const villageCrowd = parseInt(document.getElementById('village').value);
381
+ const clubCrowd = parseInt(document.getElementById('club').value);
382
+
383
+ // Update UI slider text badges
384
+ document.getElementById('hamilton-val').innerText = hamiltonCrowd;
385
+ document.getElementById('becketts-val').innerText = beckettsCrowd;
386
+ document.getElementById('stowe-val').innerText = stoweCrowd;
387
+ document.getElementById('village-val').innerText = villageCrowd;
388
+ document.getElementById('club-val').innerText = clubCrowd;
389
+
390
+ // 2. MATHEMATICAL TRAFFIC LOAD CALCULATIONS
391
+ const gate1Load = Math.round(hamiltonCrowd * 0.5 + beckettsCrowd * 0.3 + villageCrowd * 0.3);
392
+ const gate2Load = Math.round(hamiltonCrowd * 0.2 + clubCrowd * 0.4);
393
+ const gate3Load = Math.round(stoweCrowd * 0.6 + beckettsCrowd * 0.4);
394
+ const gate4Load = Math.round(villageCrowd * 0.4 + hamiltonCrowd * 0.2);
395
+
396
+ // Bottleneck flags
397
+ const isGate1Bottleneck = gate1Load > GATE_1_CAPACITY;
398
+ const isGate3Bottleneck = gate3Load > GATE_3_CAPACITY;
399
+ const excessGate1 = isGate1Bottleneck ? (gate1Load - GATE_1_CAPACITY) : 0;
400
+ const excessGate3 = isGate3Bottleneck ? (gate3Load - GATE_3_CAPACITY) : 0;
401
+
402
+ // 3. DRAW MAP ON HTML5 CANVAS
403
+ drawCanvasMap(
404
+ hamiltonCrowd, beckettsCrowd, stoweCrowd, villageCrowd, clubCrowd,
405
+ gate1Load, gate3Load, isGate1Bottleneck, isGate3Bottleneck
406
+ );
407
+
408
+ // 4. UPDATE TEXT REPORT
409
+ updateReport(gate1Load, gate2Load, gate3Load, gate4Load, isGate1Bottleneck, isGate3Bottleneck, excessGate1, excessGate3);
410
+ }
411
+
412
+ /* --- ONLINE ORDERING FUNCTIONS --- */
413
+ function openOrderModal() {
414
+ document.getElementById('orderModal').style.display = 'flex';
415
+ }
416
+
417
+ function closeOrderModal() {
418
+ document.getElementById('orderModal').style.display = 'none';
419
+ }
420
+
421
+ function submitOrder() {
422
+ const location = document.getElementById('pickupLocation').value;
423
+ const item = document.getElementById('itemSelect').value;
424
+ const customer = document.getElementById('custName').value || 'Guest';
425
+
426
+ const newOrder = {
427
+ id: `#ORD-${orderCounter++}`,
428
+ location: location,
429
+ item: item,
430
+ customer: customer,
431
+ status: 'Preparing'
432
+ };
433
+
434
+ activeOrders.push(newOrder);
435
+ closeOrderModal();
436
+ renderOrdersUI();
437
+ updateSimulation(); // Refresh Canvas Map Badges
438
+
439
+ // Automatically transition order status to Ready after 8 seconds
440
+ setTimeout(() => {
441
+ newOrder.status = 'Ready for Pickup';
442
+ renderOrdersUI();
443
+ updateSimulation();
444
+ }, 8000);
445
+ }
446
+
447
+ function completeOrder(orderId) {
448
+ activeOrders = activeOrders.filter(o => o.id !== orderId);
449
+ renderOrdersUI();
450
+ updateSimulation();
451
+ }
452
+
453
+ function renderOrdersUI() {
454
+ const container = document.getElementById('ordersContainer');
455
+ if (activeOrders.length === 0) {
456
+ container.innerHTML = `<p style="color: #666; font-size: 12px; text-align: center;">No active mobile orders</p>`;
457
+ return;
458
+ }
459
+
460
+ container.innerHTML = activeOrders.map(o => `
461
+ <div class="order-card ${o.status === 'Ready for Pickup' ? 'ready' : ''}">
462
+ <div style="display:flex; justify-space-between; font-weight:bold;">
463
+ <span>${o.id} - ${o.customer}</span>
464
+ <span style="color: ${o.status === 'Ready for Pickup' ? '#00cc66' : '#f39c12'}">${o.status}</span>
465
+ </div>
466
+ <div style="color: #aaa; margin-top:3px;">πŸ“ ${o.location}</div>
467
+ <div style="color: #ddd;">πŸ” ${o.item}</div>
468
+ ${o.status === 'Ready for Pickup' ? `<button onclick="completeOrder('${o.id}')" style="margin-top:5px; background:#00cc66; color:black; border:none; padding:2px 6px; border-radius:3px; cursor:pointer; font-size:10px; font-weight:bold;">Collect Order</button>` : ''}
469
+ </div>
470
+ `).join('');
471
+ }
472
+
473
+ function drawCanvasMap(hamilton, becketts, stowe, village, club, gate1Load, gate3Load, isGate1Bottleneck, isGate3Bottleneck) {
474
+ const canvas = document.getElementById('silverstoneCanvas');
475
+ const ctx = canvas.getContext('2d');
476
+
477
+ // Clear canvas background
478
+ ctx.fillStyle = '#181818';
479
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
480
+
481
+ // Map coordinate converter
482
+ function toCanvas(x, y) {
483
+ return {
484
+ x: x * 65 + 430,
485
+ y: -y * 48 + 260
486
+ };
487
+ }
488
+
489
+ // -------------------------------------------------------------
490
+ // A. DRAW F1 CIRCUIT TRACK
491
+ // -------------------------------------------------------------
492
+ const trackPoints = [
493
+ {x: -1, y: 0}, {x: 1, y: -2}, {x: 3, y: -3}, {x: 2, y: -4},
494
+ {x: -1, y: -3}, {x: -2, y: -1}, {x: -3, y: 1}, {x: -1, y: 3},
495
+ {x: 1, y: 4}, {x: 3, y: 4}, {x: 4, y: 2}, {x: 2, y: 0}, {x: -1, y: 0}
496
+ ];
497
+
498
+ // Outer track border
499
+ ctx.beginPath();
500
+ trackPoints.forEach((pt, idx) => {
501
+ const c = toCanvas(pt.x, pt.y);
502
+ if (idx === 0) ctx.moveTo(c.x, c.y);
503
+ else ctx.lineTo(c.x, c.y);
504
+ });
505
+ ctx.strokeStyle = '#333333';
506
+ ctx.lineWidth = 14;
507
+ ctx.stroke();
508
+
509
+ // Racing line
510
+ ctx.beginPath();
511
+ ctx.setLineDash([6, 6]);
512
+ trackPoints.forEach((pt, idx) => {
513
+ const c = toCanvas(pt.x, pt.y);
514
+ if (idx === 0) ctx.moveTo(c.x, c.y);
515
+ else ctx.lineTo(c.x, c.y);
516
+ });
517
+ ctx.strokeStyle = '#aaaaaa';
518
+ ctx.lineWidth = 1.5;
519
+ ctx.stroke();
520
+ ctx.setLineDash([]); // Reset dash
521
+
522
+ // -------------------------------------------------------------
523
+ // B. DEFINE MAP NODES (Grandstands, Food, Hubs, Gates)
524
+ // -------------------------------------------------------------
525
+ const nodes = {
526
+ // Grandstands
527
+ "Hamilton GS": { pos: toCanvas(-1.5, 0), type: "gs", label: `Hamilton (${hamilton})` },
528
+ "Becketts GS": { pos: toCanvas(1, 3.5), type: "gs", label: `Becketts (${becketts})` },
529
+ "Stowe GS": { pos: toCanvas(3.5, 2), type: "gs", label: `Stowe (${stowe})` },
530
+ "Village GS": { pos: toCanvas(-2.5, 1.5), type: "gs", label: `Village (${village})` },
531
+ "Club GS": { pos: toCanvas(1.5, -3), type: "gs", label: `Club (${club})` },
532
+
533
+ // Food Stalls & Arenas
534
+ "Main Fan Zone": { pos: toCanvas(-0.2, 0.8), type: "food", label: "πŸ” Main Fan Zone" },
535
+ "Becketts Food": { pos: toCanvas(1.8, 2.8), type: "food", label: "🌭 Becketts Food" },
536
+ "Village Plaza": { pos: toCanvas(-1.8, 2.2), type: "food", label: "πŸ• Village Plaza" },
537
+ "Club Eats": { pos: toCanvas(0.2, -2.2), type: "food", label: "🍦 Club Eats" },
538
+
539
+ // Intermediate Junction Hubs
540
+ "Arena Crossroad": { pos: toCanvas(-1.2, 0.8), type: "hub", label: "πŸ“ Arena Hub" },
541
+ "Becketts Interchange": { pos: toCanvas(0.5, 2.2), type: "hub", label: "πŸ“ Becketts Hub" },
542
+ "South Bypass": { pos: toCanvas(-0.8, -1.5), type: "hub", label: "πŸ“ South Hub" },
543
+
544
+ // Gates
545
+ "Main Gate 1": { pos: toCanvas(-3.5, 0), type: "gate", label: "πŸšͺ Main Gate 1" },
546
+ "South Gate 2": { pos: toCanvas(1, -4.2), type: "gate", label: "πŸšͺ South Gate 2" },
547
+ "East Gate 3": { pos: toCanvas(4.5, -1), type: "gate", label: "πŸšͺ East Gate 3" },
548
+ "West Gate 4": { pos: toCanvas(-4.0, 2.8), type: "gate", label: "πŸšͺ West Gate 4" }
549
+ };
550
+
551
+ // -------------------------------------------------------------
552
+ // C. INTERCONNECTED STANDARD PEDESTRIAN PATHS (GREEN / RED)
553
+ // -------------------------------------------------------------
554
+ const paths = [
555
+ // Network towards Gate 1 (Turns Red on Gate 1 Bottleneck)
556
+ { start: "Hamilton GS", end: "Arena Crossroad", color: isGate1Bottleneck ? "#ff4444" : "#00cc66" },
557
+ { start: "Arena Crossroad", end: "Main Fan Zone", color: isGate1Bottleneck ? "#ff4444" : "#00cc66" },
558
+ { start: "Main Fan Zone", end: "Main Gate 1", color: isGate1Bottleneck ? "#ff4444" : "#00cc66" },
559
+ { start: "Village GS", end: "Arena Crossroad", color: isGate1Bottleneck ? "#ff4444" : "#00cc66" },
560
+
561
+ // Network towards Becketts & Gate 3
562
+ { start: "Becketts GS", end: "Becketts Interchange", color: isGate3Bottleneck ? "#ff4444" : "#00cc66" },
563
+ { start: "Becketts Interchange", end: "Becketts Food", color: isGate3Bottleneck ? "#ff4444" : "#00cc66" },
564
+ { start: "Becketts Interchange", end: "Main Fan Zone", color: isGate1Bottleneck ? "#ff4444" : "#00cc66" },
565
+ { start: "Becketts Food", end: "Stowe GS", color: isGate3Bottleneck ? "#ff4444" : "#00cc66" },
566
+ { start: "Stowe GS", end: "East Gate 3", color: isGate3Bottleneck ? "#ff4444" : "#00cc66" },
567
+
568
+ // West & South Paths
569
+ { start: "Village GS", end: "Village Plaza", color: "#00cc66" },
570
+ { start: "Village Plaza", end: "West Gate 4", color: "#00cc66" },
571
+ { start: "Hamilton GS", end: "South Bypass", color: "#00cc66" },
572
+ { start: "South Bypass", end: "Club Eats", color: "#00cc66" },
573
+ { start: "Club GS", end: "Club Eats", color: "#00cc66" },
574
+ { start: "Club Eats", end: "South Gate 2", color: "#00cc66" }
575
+ ];
576
+
577
+ paths.forEach(p => {
578
+ ctx.beginPath();
579
+ ctx.moveTo(nodes[p.start].pos.x, nodes[p.start].pos.y);
580
+ ctx.lineTo(nodes[p.end].pos.x, nodes[p.end].pos.y);
581
+ ctx.strokeStyle = p.color;
582
+ ctx.lineWidth = 4;
583
+ ctx.stroke();
584
+ });
585
+
586
+ // -------------------------------------------------------------
587
+ // D. EXPANDED GOOGLE MAPS MULTI-NODE DETOURS (PURPLE)
588
+ // -------------------------------------------------------------
589
+ if (isGate1Bottleneck) {
590
+ // Multi-leg Detour 1: Fan Zone -> South Bypass -> Club Eats -> South Gate 2
591
+ ctx.beginPath();
592
+ ctx.setLineDash([8, 6]);
593
+ ctx.moveTo(nodes["Main Fan Zone"].pos.x, nodes["Main Fan Zone"].pos.y);
594
+ ctx.lineTo(nodes["South Bypass"].pos.x, nodes["South Bypass"].pos.y);
595
+ ctx.lineTo(nodes["Club Eats"].pos.x, nodes["Club Eats"].pos.y);
596
+ ctx.lineTo(nodes["South Gate 2"].pos.x, nodes["South Gate 2"].pos.y);
597
+ ctx.strokeStyle = "#a020f0";
598
+ ctx.lineWidth = 5;
599
+ ctx.stroke();
600
+
601
+ // Multi-leg Detour 2: Arena Hub -> Village Plaza -> West Gate 4
602
+ ctx.beginPath();
603
+ ctx.moveTo(nodes["Arena Crossroad"].pos.x, nodes["Arena Crossroad"].pos.y);
604
+ ctx.lineTo(nodes["Village Plaza"].pos.x, nodes["Village Plaza"].pos.y);
605
+ ctx.lineTo(nodes["West Gate 4"].pos.x, nodes["West Gate 4"].pos.y);
606
+ ctx.strokeStyle = "#a020f0";
607
+ ctx.lineWidth = 5;
608
+ ctx.stroke();
609
+ ctx.setLineDash([]);
610
+ }
611
+
612
+ if (isGate3Bottleneck) {
613
+ // Detour 3: Becketts Interchange -> Arena Hub -> South Bypass -> South Gate 2
614
+ ctx.beginPath();
615
+ ctx.setLineDash([8, 6]);
616
+ ctx.moveTo(nodes["Becketts Interchange"].pos.x, nodes["Becketts Interchange"].pos.y);
617
+ ctx.lineTo(nodes["Arena Crossroad"].pos.x, nodes["Arena Crossroad"].pos.y);
618
+ ctx.lineTo(nodes["South Bypass"].pos.x, nodes["South Bypass"].pos.y);
619
+ ctx.lineTo(nodes["South Gate 2"].pos.x, nodes["South Gate 2"].pos.y);
620
+ ctx.strokeStyle = "#a020f0";
621
+ ctx.lineWidth = 5;
622
+ ctx.stroke();
623
+ ctx.setLineDash([]);
624
+ }
625
+
626
+ // -------------------------------------------------------------
627
+ // E. DRAW NODE BOXES, LABELS & ORDER BADGES
628
+ // -------------------------------------------------------------
629
+ for (let key in nodes) {
630
+ const n = nodes[key];
631
+ ctx.font = "bold 11px Arial";
632
+ const textWidth = ctx.measureText(n.label).width;
633
+ const boxPadding = 7;
634
+
635
+ let boxColor = "#3498db"; // Blue for Grandstands
636
+ if (n.type === "food") boxColor = "#f39c12"; // Orange for Food
637
+ if (n.type === "hub") boxColor = "#7f8c8d"; // Grey for Hubs
638
+ if (n.type === "gate") {
639
+ if ((key === "Main Gate 1" && isGate1Bottleneck) || (key === "East Gate 3" && isGate3Bottleneck)) {
640
+ boxColor = "#ff4444";
641
+ } else {
642
+ boxColor = "#00cc66";
643
+ }
644
+ }
645
+
646
+ // Draw background rounded box
647
+ ctx.fillStyle = boxColor;
648
+ ctx.beginPath();
649
+ ctx.roundRect(
650
+ n.pos.x - textWidth / 2 - boxPadding,
651
+ n.pos.y - 12,
652
+ textWidth + boxPadding * 2,
653
+ 24,
654
+ 6
655
+ );
656
+ ctx.fill();
657
+ ctx.strokeStyle = "#ffffff";
658
+ ctx.lineWidth = 1;
659
+ ctx.stroke();
660
+
661
+ // Draw label text
662
+ ctx.fillStyle = "#ffffff";
663
+ ctx.textAlign = "center";
664
+ ctx.textBaseline = "middle";
665
+ ctx.fillText(n.label, n.pos.x, n.pos.y);
666
+
667
+ // DRAW PENDING ORDERS OVERLAY BADGE FOR FOOD STALLS
668
+ if (n.type === "food") {
669
+ const pendingCount = activeOrders.filter(o => o.location === key).length;
670
+ if (pendingCount > 0) {
671
+ ctx.fillStyle = "#e10600";
672
+ ctx.beginPath();
673
+ ctx.arc(n.pos.x + textWidth / 2 + 5, n.pos.y - 10, 10, 0, Math.PI * 2);
674
+ ctx.fill();
675
+ ctx.strokeStyle = "#ffffff";
676
+ ctx.lineWidth = 1.5;
677
+ ctx.stroke();
678
+
679
+ ctx.fillStyle = "#ffffff";
680
+ ctx.font = "bold 10px Arial";
681
+ ctx.fillText(pendingCount, n.pos.x + textWidth / 2 + 5, n.pos.y - 10);
682
+ }
683
+ }
684
+ }
685
+
686
+ // Draw Legend
687
+ ctx.font = "11px Arial";
688
+ ctx.fillStyle = "#ffffff";
689
+ ctx.textAlign = "left";
690
+ ctx.fillText("🟒 Clear Pathway", 20, 25);
691
+ ctx.fillText("πŸ”΄ Congested Network Segment", 140, 25);
692
+ ctx.fillText("🟣 Google Maps Active Multi-Leg Detour", 330, 25);
693
+ }
694
+
695
+ function updateReport(gate1, gate2, gate3, gate4, isG1Alert, isG3Alert, excess1, excess3) {
696
+ const reportDiv = document.getElementById('reportBox');
697
+
698
+ if (isG1Alert || isG3Alert) {
699
+ reportDiv.className = "report-box alert";
700
+ let alertsHtml = `<h3 style="color: #ff4444;">πŸ”΄ NETWORK BOTTLENECK ALERT ACTIVATED</h3>`;
701
+
702
+ if (isG1Alert) {
703
+ alertsHtml += `<p>β€’ <strong>Main Gate 1 Bottleneck:</strong> Load <code>${gate1}</code> fans/min (Max limit: ${GATE_1_CAPACITY}). Overflow: <code>${excess1}</code> fans.</p>`;
704
+ }
705
+ if (isG3Alert) {
706
+ alertsHtml += `<p>β€’ <strong>East Gate 3 Bottleneck:</strong> Load <code>${gate3}</code> fans/min (Max limit: ${GATE_3_CAPACITY}). Overflow: <code>${excess3}</code> fans.</p>`;
707
+ }
708
+
709
+ alertsHtml += `
710
+ <hr style="border-color: #552222; margin: 10px 0;">
711
+ <h4 style="color: #a020f0;">πŸ”„ Dynamic Rerouting Protocol:</h4>
712
+ <p>β€’ Activating interconnected <span class="badge badge-purple">PURPLE DETOURS</span> across <strong>Arena Hub</strong>, <strong>South Bypass</strong>, and <strong>Becketts Interchange</strong>.</p>
713
+ <p>β€’ Directing overflow pedestrian traffic to under-utilized exit points at <strong>South Gate 2</strong> and <strong>West Gate 4</strong>.</p>
714
+ `;
715
+ reportDiv.innerHTML = alertsHtml;
716
+ } else {
717
+ reportDiv.className = "report-box";
718
+ reportDiv.innerHTML = `
719
+ <h3 style="color: #00cc66;">🟒 ALL INTERCONNECTED PATHWAYS OPTIMAL</h3>
720
+ <p>β€’ <strong>Main Gate 1 Load:</strong> <code>${gate1}</code> / ${GATE_1_CAPACITY} fans/min (Safe)</p>
721
+ <p>β€’ <strong>South Gate 2 Load:</strong> <code>${gate2}</code> / 50 fans/min (Safe)</p>
722
+ <p>β€’ <strong>East Gate 3 Load:</strong> <code>${gate3}</code> / ${GATE_3_CAPACITY} fans/min (Safe)</p>
723
+ <p>β€’ <strong>West Gate 4 Load:</strong> <code>${gate4}</code> / 45 fans/min (Safe)</p>
724
+ <p style="margin-top: 5px; color: #aaa;"><em>Status: Grid flow is balanced across all junctions. No purple detour routing required.</em></p>
725
+ `;
726
+ }
727
+ }
728
+
729
+ // Initialize simulation on initial page load
730
+ window.onload = updateSimulation;
731
+ </script>
732
+ </body>
733
+ </html>