srinetwork commited on
Commit
de2a9ce
·
verified ·
1 Parent(s): d0ff9bd

Create public/index.html

Browse files
Files changed (1) hide show
  1. public/index.html +1174 -0
public/index.html ADDED
@@ -0,0 +1,1174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="si">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
+ <title>Online Carrom - Multiplayer Game</title>
7
+ <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
8
+ <style>
9
+ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;800&display=swap');
10
+
11
+ * {
12
+ margin: 0;
13
+ padding: 0;
14
+ box-sizing: border-box;
15
+ -webkit-tap-highlight-color: transparent;
16
+ user-select: none;
17
+ -webkit-user-select: none;
18
+ }
19
+
20
+ body {
21
+ font-family: 'Poppins', sans-serif;
22
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
23
+ min-height: 100vh;
24
+ display: flex;
25
+ flex-direction: column;
26
+ align-items: center;
27
+ color: white;
28
+ overflow-x: hidden;
29
+ position: relative;
30
+ touch-action: manipulation;
31
+ }
32
+
33
+ .game-header {
34
+ text-align: center;
35
+ padding: 12px 10px;
36
+ width: 100%;
37
+ background: rgba(0,0,0,0.3);
38
+ backdrop-filter: blur(10px);
39
+ position: sticky;
40
+ top: 0;
41
+ z-index: 100;
42
+ }
43
+
44
+ .game-header h1 {
45
+ font-size: clamp(1.3rem, 4vw, 2rem);
46
+ background: linear-gradient(45deg, #f39c12, #e74c3c);
47
+ -webkit-background-clip: text;
48
+ -webkit-text-fill-color: transparent;
49
+ text-shadow: 0 0 30px rgba(243, 156, 18, 0.5);
50
+ }
51
+
52
+ .connection-status {
53
+ display: inline-flex;
54
+ align-items: center;
55
+ gap: 8px;
56
+ margin-top: 6px;
57
+ padding: 4px 10px;
58
+ background: rgba(255,255,255,0.1);
59
+ border-radius: 20px;
60
+ font-size: 0.8rem;
61
+ }
62
+
63
+ .status-dot {
64
+ width: 8px;
65
+ height: 8px;
66
+ border-radius: 50%;
67
+ background: #e74c3c;
68
+ animation: pulse 2s infinite;
69
+ }
70
+
71
+ .status-dot.connected {
72
+ background: #2ecc71;
73
+ }
74
+
75
+ @keyframes pulse {
76
+ 0%, 100% { opacity: 1; }
77
+ 50% { opacity: 0.5; }
78
+ }
79
+
80
+ .main-container {
81
+ display: flex;
82
+ flex-wrap: wrap;
83
+ justify-content: center;
84
+ gap: 15px;
85
+ padding: 10px;
86
+ max-width: 1400px;
87
+ width: 100%;
88
+ flex: 1;
89
+ }
90
+
91
+ .game-section {
92
+ display: flex;
93
+ flex-direction: column;
94
+ align-items: center;
95
+ width: 100%;
96
+ max-width: 550px;
97
+ }
98
+
99
+ .board-wrapper {
100
+ position: relative;
101
+ width: 100%;
102
+ max-width: 550px;
103
+ padding: 8px;
104
+ }
105
+
106
+ .carrom-board {
107
+ width: 100%;
108
+ aspect-ratio: 1;
109
+ background: linear-gradient(145deg, #8B4513, #A0522D);
110
+ border-radius: 20px;
111
+ position: relative;
112
+ box-shadow:
113
+ 0 20px 60px rgba(0,0,0,0.5),
114
+ inset 0 0 100px rgba(0,0,0,0.3),
115
+ 0 0 0 10px #654321,
116
+ 0 0 0 14px #3d2817;
117
+ overflow: hidden;
118
+ }
119
+
120
+ .board-inner {
121
+ position: absolute;
122
+ top: 35px;
123
+ left: 35px;
124
+ right: 35px;
125
+ bottom: 35px;
126
+ background: linear-gradient(145deg, #DEB887, #F5DEB3);
127
+ border-radius: 10px;
128
+ box-shadow: inset 0 0 20px rgba(0,0,0,0.2);
129
+ }
130
+
131
+ .center-circle {
132
+ position: absolute;
133
+ top: 50%;
134
+ left: 50%;
135
+ transform: translate(-50%, -50%);
136
+ width: 140px;
137
+ height: 140px;
138
+ border: 2px solid rgba(139, 69, 19, 0.4);
139
+ border-radius: 50%;
140
+ }
141
+
142
+ .center-red-circle {
143
+ position: absolute;
144
+ top: 50%;
145
+ left: 50%;
146
+ transform: translate(-50%, -50%);
147
+ width: 16px;
148
+ height: 16px;
149
+ background: #e74c3c;
150
+ border-radius: 50%;
151
+ }
152
+
153
+ .pocket {
154
+ position: absolute;
155
+ width: 44px;
156
+ height: 44px;
157
+ background: #1a1a1a;
158
+ border-radius: 50%;
159
+ box-shadow: inset 0 0 10px rgba(0,0,0,0.8);
160
+ z-index: 10;
161
+ }
162
+
163
+ .pocket.tl { top: 12px; left: 12px; }
164
+ .pocket.tr { top: 12px; right: 12px; }
165
+ .pocket.bl { bottom: 12px; left: 12px; }
166
+ .pocket.br { bottom: 12px; right: 12px; }
167
+
168
+ canvas {
169
+ position: absolute;
170
+ top: 35px;
171
+ left: 35px;
172
+ width: calc(100% - 70px);
173
+ height: calc(100% - 70px);
174
+ border-radius: 10px;
175
+ cursor: crosshair;
176
+ touch-action: none;
177
+ }
178
+
179
+ .striker-bar {
180
+ position: absolute;
181
+ left: 35px;
182
+ right: 35px;
183
+ height: 36px;
184
+ background: rgba(0,0,0,0.15);
185
+ border-radius: 18px;
186
+ z-index: 20;
187
+ display: none;
188
+ border: 2px dashed rgba(139, 69, 19, 0.5);
189
+ }
190
+
191
+ .striker-bar.top { top: 45px; }
192
+ .striker-bar.bottom { bottom: 45px; }
193
+
194
+ .striker-bar.active {
195
+ display: block;
196
+ background: rgba(46, 204, 113, 0.15);
197
+ border-color: rgba(46, 204, 113, 0.4);
198
+ }
199
+
200
+ .striker-handle {
201
+ position: absolute;
202
+ width: 32px;
203
+ height: 32px;
204
+ background: linear-gradient(145deg, #e74c3c, #c0392b);
205
+ border-radius: 50%;
206
+ top: 50%;
207
+ transform: translateY(-50%);
208
+ cursor: grab;
209
+ box-shadow: 0 2px 8px rgba(0,0,0,0.4);
210
+ border: 2px solid white;
211
+ z-index: 25;
212
+ transition: transform 0.1s;
213
+ }
214
+
215
+ .striker-handle:active {
216
+ cursor: grabbing;
217
+ transform: translateY(-50%) scale(1.2);
218
+ }
219
+
220
+ .player-info {
221
+ display: flex;
222
+ gap: 10px;
223
+ margin-top: 12px;
224
+ flex-wrap: wrap;
225
+ justify-content: center;
226
+ width: 100%;
227
+ }
228
+
229
+ .player-card {
230
+ background: rgba(255,255,255,0.1);
231
+ padding: 10px 16px;
232
+ border-radius: 12px;
233
+ text-align: center;
234
+ min-width: 110px;
235
+ flex: 1;
236
+ max-width: 180px;
237
+ transition: all 0.3s;
238
+ border: 2px solid transparent;
239
+ }
240
+
241
+ .player-card.active {
242
+ border-color: #f39c12;
243
+ background: rgba(243, 156, 18, 0.2);
244
+ transform: scale(1.02);
245
+ box-shadow: 0 0 20px rgba(243, 156, 18, 0.3);
246
+ }
247
+
248
+ .player-card.winner {
249
+ border-color: #2ecc71;
250
+ background: rgba(46, 204, 113, 0.2);
251
+ animation: winnerPulse 1s infinite;
252
+ }
253
+
254
+ @keyframes winnerPulse {
255
+ 0%, 100% { transform: scale(1); }
256
+ 50% { transform: scale(1.05); }
257
+ }
258
+
259
+ .player-name {
260
+ font-weight: 600;
261
+ font-size: clamp(0.85rem, 3vw, 1rem);
262
+ margin-bottom: 4px;
263
+ word-break: break-word;
264
+ }
265
+
266
+ .player-color {
267
+ width: 16px;
268
+ height: 16px;
269
+ border-radius: 50%;
270
+ display: inline-block;
271
+ margin: 2px;
272
+ border: 2px solid white;
273
+ }
274
+
275
+ .score {
276
+ font-size: clamp(1.1rem, 4vw, 1.4rem);
277
+ font-weight: 800;
278
+ color: #f39c12;
279
+ }
280
+
281
+ .controls-section {
282
+ background: rgba(255,255,255,0.05);
283
+ padding: 15px;
284
+ border-radius: 16px;
285
+ width: 100%;
286
+ max-width: 380px;
287
+ }
288
+
289
+ .room-controls {
290
+ margin-bottom: 15px;
291
+ }
292
+
293
+ .input-group {
294
+ margin-bottom: 12px;
295
+ }
296
+
297
+ .input-group label {
298
+ display: block;
299
+ margin-bottom: 5px;
300
+ font-size: 0.85rem;
301
+ opacity: 0.9;
302
+ font-weight: 500;
303
+ }
304
+
305
+ .input-group input {
306
+ width: 100%;
307
+ padding: 12px;
308
+ border: none;
309
+ border-radius: 10px;
310
+ background: rgba(255,255,255,0.15);
311
+ color: white;
312
+ font-size: 1rem;
313
+ outline: none;
314
+ transition: all 0.3s;
315
+ font-family: inherit;
316
+ }
317
+
318
+ .input-group input::placeholder {
319
+ color: rgba(255,255,255,0.5);
320
+ }
321
+
322
+ .input-group input:focus {
323
+ background: rgba(255,255,255,0.25);
324
+ box-shadow: 0 0 15px rgba(243, 156, 18, 0.3);
325
+ }
326
+
327
+ .btn {
328
+ width: 100%;
329
+ padding: 12px;
330
+ border: none;
331
+ border-radius: 10px;
332
+ font-size: 0.95rem;
333
+ font-weight: 600;
334
+ cursor: pointer;
335
+ transition: all 0.3s;
336
+ margin-bottom: 8px;
337
+ text-transform: uppercase;
338
+ letter-spacing: 1px;
339
+ font-family: inherit;
340
+ }
341
+
342
+ .btn-primary {
343
+ background: linear-gradient(45deg, #f39c12, #e74c3c);
344
+ color: white;
345
+ }
346
+
347
+ .btn-secondary {
348
+ background: rgba(255,255,255,0.1);
349
+ color: white;
350
+ border: 2px solid rgba(255,255,255,0.2);
351
+ }
352
+
353
+ .btn:hover {
354
+ transform: translateY(-2px);
355
+ box-shadow: 0 8px 25px rgba(0,0,0,0.3);
356
+ }
357
+
358
+ .btn:active {
359
+ transform: translateY(0);
360
+ }
361
+
362
+ .btn:disabled {
363
+ opacity: 0.5;
364
+ cursor: not-allowed;
365
+ transform: none;
366
+ }
367
+
368
+ .game-log {
369
+ background: rgba(0,0,0,0.3);
370
+ border-radius: 10px;
371
+ padding: 12px;
372
+ height: 150px;
373
+ overflow-y: auto;
374
+ margin-top: 12px;
375
+ }
376
+
377
+ .game-log h3 {
378
+ margin-bottom: 8px;
379
+ color: #f39c12;
380
+ font-size: 0.9rem;
381
+ }
382
+
383
+ .log-entry {
384
+ padding: 6px;
385
+ margin-bottom: 4px;
386
+ background: rgba(255,255,255,0.08);
387
+ border-radius: 5px;
388
+ font-size: 0.8rem;
389
+ animation: slideIn 0.3s;
390
+ }
391
+
392
+ @keyframes slideIn {
393
+ from { opacity: 0; transform: translateX(-10px); }
394
+ to { opacity: 1; transform: translateX(0); }
395
+ }
396
+
397
+ .turn-indicator {
398
+ text-align: center;
399
+ margin-top: 12px;
400
+ padding: 10px;
401
+ background: rgba(243, 156, 18, 0.2);
402
+ border-radius: 10px;
403
+ font-weight: 600;
404
+ font-size: 0.9rem;
405
+ transition: all 0.3s;
406
+ }
407
+
408
+ .waiting-overlay {
409
+ position: fixed;
410
+ top: 0;
411
+ left: 0;
412
+ width: 100%;
413
+ height: 100%;
414
+ background: rgba(0,0,0,0.95);
415
+ display: flex;
416
+ flex-direction: column;
417
+ justify-content: center;
418
+ align-items: center;
419
+ z-index: 1000;
420
+ backdrop-filter: blur(10px);
421
+ padding: 20px;
422
+ }
423
+
424
+ .waiting-overlay.hidden {
425
+ display: none;
426
+ }
427
+
428
+ .loader {
429
+ width: 50px;
430
+ height: 50px;
431
+ border: 3px solid rgba(243, 156, 18, 0.3);
432
+ border-top-color: #f39c12;
433
+ border-radius: 50%;
434
+ animation: spin 1s linear infinite;
435
+ margin-bottom: 15px;
436
+ }
437
+
438
+ @keyframes spin {
439
+ to { transform: rotate(360deg); }
440
+ }
441
+
442
+ .waiting-overlay h2 {
443
+ font-size: clamp(1.2rem, 4vw, 1.6rem);
444
+ margin-bottom: 8px;
445
+ text-align: center;
446
+ }
447
+
448
+ .waiting-overlay p {
449
+ opacity: 0.8;
450
+ text-align: center;
451
+ font-size: clamp(0.85rem, 3vw, 0.95rem);
452
+ }
453
+
454
+ .error-message {
455
+ color: #e74c3c;
456
+ text-align: center;
457
+ margin-top: 12px;
458
+ padding: 10px;
459
+ background: rgba(231, 76, 60, 0.2);
460
+ border-radius: 8px;
461
+ display: none;
462
+ max-width: 380px;
463
+ width: 100%;
464
+ font-size: 0.9rem;
465
+ }
466
+
467
+ .error-message.show {
468
+ display: block;
469
+ }
470
+
471
+ .rules-section {
472
+ margin-top: 12px;
473
+ padding: 12px;
474
+ background: rgba(255,255,255,0.05);
475
+ border-radius: 10px;
476
+ font-size: 0.8rem;
477
+ }
478
+
479
+ .rules-section h4 {
480
+ color: #f39c12;
481
+ margin-bottom: 8px;
482
+ font-size: 0.9rem;
483
+ }
484
+
485
+ .rules-section ul {
486
+ list-style: none;
487
+ padding-left: 0;
488
+ }
489
+
490
+ .rules-section li {
491
+ padding: 3px 0;
492
+ padding-left: 16px;
493
+ position: relative;
494
+ }
495
+
496
+ .rules-section li:before {
497
+ content: "▸";
498
+ position: absolute;
499
+ left: 0;
500
+ color: #f39c12;
501
+ }
502
+
503
+ .room-code-display {
504
+ background: rgba(243, 156, 18, 0.2);
505
+ padding: 10px;
506
+ border-radius: 8px;
507
+ text-align: center;
508
+ margin-top: 10px;
509
+ border: 1px dashed #f39c12;
510
+ }
511
+
512
+ .room-code-display span {
513
+ font-weight: 800;
514
+ font-size: 1.3rem;
515
+ color: #f39c12;
516
+ letter-spacing: 2px;
517
+ }
518
+
519
+ @media (max-width: 768px) {
520
+ .main-container { padding: 8px; gap: 10px; }
521
+ .carrom-board {
522
+ border-radius: 15px;
523
+ box-shadow: 0 10px 30px rgba(0,0,0,0.5), inset 0 0 50px rgba(0,0,0,0.3), 0 0 0 8px #654321, 0 0 0 12px #3d2817;
524
+ }
525
+ .board-inner { top: 25px; left: 25px; right: 25px; bottom: 25px; }
526
+ canvas { top: 25px; left: 25px; width: calc(100% - 50px); height: calc(100% - 50px); }
527
+ .pocket { width: 32px; height: 32px; }
528
+ .pocket.tl { top: 8px; left: 8px; }
529
+ .pocket.tr { top: 8px; right: 8px; }
530
+ .pocket.bl { bottom: 8px; left: 8px; }
531
+ .pocket.br { bottom: 8px; right: 8px; }
532
+ .center-circle { width: 90px; height: 90px; }
533
+ .controls-section { padding: 12px; }
534
+ .player-card { padding: 8px 12px; min-width: 90px; }
535
+ .striker-bar { left: 25px; right: 25px; height: 30px; }
536
+ .striker-handle { width: 28px; height: 28px; }
537
+ }
538
+
539
+ @media (max-width: 480px) {
540
+ .game-header { padding: 8px; }
541
+ .input-group input { padding: 10px; font-size: 16px; }
542
+ .btn { padding: 10px; }
543
+ }
544
+ </style>
545
+ </head>
546
+ <body>
547
+ <div class="game-header">
548
+ <h1>🏆 ONLINE CARROM 🏆</h1>
549
+ <div class="connection-status">
550
+ <span class="status-dot" id="statusDot"></span>
551
+ <span id="statusText">Connecting...</span>
552
+ </div>
553
+ </div>
554
+
555
+ <div class="waiting-overlay" id="waitingOverlay">
556
+ <div class="loader" id="loader"></div>
557
+ <h2 id="waitingText">Connecting to Server...</h2>
558
+ <p id="waitingSubtext">Please wait</p>
559
+ <div class="error-message" id="errorMessage"></div>
560
+
561
+ <div class="room-controls" id="roomControls" style="display: none; width: 100%; max-width: 380px; margin-top: 15px;">
562
+ <div class="input-group">
563
+ <label>Your Name</label>
564
+ <input type="text" id="playerName" placeholder="Enter your name" maxlength="15" autocomplete="off">
565
+ </div>
566
+ <div class="input-group">
567
+ <label>Room ID (optional)</label>
568
+ <input type="text" id="roomId" placeholder="Leave blank to create new room" autocomplete="off">
569
+ </div>
570
+ <button class="btn btn-primary" onclick="joinRoom()" id="joinBtn">Join / Create Room</button>
571
+ </div>
572
+ </div>
573
+
574
+ <div class="main-container" id="gameContainer" style="display: none;">
575
+ <div class="game-section">
576
+ <div class="board-wrapper">
577
+ <div class="carrom-board" id="board">
578
+ <div class="board-inner"></div>
579
+ <div class="center-circle"></div>
580
+ <div class="center-red-circle"></div>
581
+ <div class="pocket tl"></div>
582
+ <div class="pocket tr"></div>
583
+ <div class="pocket bl"></div>
584
+ <div class="pocket br"></div>
585
+
586
+ <div class="striker-bar bottom" id="strikerBarBottom">
587
+ <div class="striker-handle" id="strikerHandleBottom"></div>
588
+ </div>
589
+ <div class="striker-bar top" id="strikerBarTop">
590
+ <div class="striker-handle" id="strikerHandleTop"></div>
591
+ </div>
592
+
593
+ <canvas id="gameCanvas" width="520" height="520"></canvas>
594
+ </div>
595
+ </div>
596
+
597
+ <div class="player-info" id="playerInfo">
598
+ <div class="player-card" id="player1Card">
599
+ <div class="player-name">Player 1</div>
600
+ <div class="player-color" style="background: #fff; border-color: #333;"></div>
601
+ <div class="score" id="score1">0</div>
602
+ </div>
603
+ <div class="player-card" id="player2Card">
604
+ <div class="player-name">Waiting...</div>
605
+ <div class="player-color" style="background: #333;"></div>
606
+ <div class="score" id="score2">0</div>
607
+ </div>
608
+ </div>
609
+ </div>
610
+
611
+ <div class="controls-section">
612
+ <div class="turn-indicator" id="turnIndicator" style="display: none;">
613
+ Waiting for opponent...
614
+ </div>
615
+
616
+ <button class="btn btn-secondary" id="resetBtn" style="display: none;" onclick="requestReset()">
617
+ Reset Game
618
+ </button>
619
+
620
+ <div class="room-code-display" id="roomCodeDisplay" style="display: none;">
621
+ Room Code: <span id="roomCodeText"></span>
622
+ </div>
623
+
624
+ <div class="game-log" id="gameLog">
625
+ <h3>🎮 Game Log</h3>
626
+ <div id="logContent"></div>
627
+ </div>
628
+
629
+ <div class="rules-section">
630
+ <h4>📜 Rules</h4>
631
+ <ul>
632
+ <li>White coins = Player 1</li>
633
+ <li>Black coins = Player 2</li>
634
+ <li>Queen (Red) = 3 Bonus Points</li>
635
+ <li>Drag striker on bar to position</li>
636
+ <li>Pull back to aim, release to strike</li>
637
+ <li>First to 8 points wins!</li>
638
+ </ul>
639
+ </div>
640
+ </div>
641
+ </div>
642
+
643
+ <script>
644
+ const BOARD_SIZE = 520;
645
+ const COIN_RADIUS = 12;
646
+ const STRIKER_RADIUS = 18;
647
+ const STRIKER_LINE_Y1 = BOARD_SIZE - 50;
648
+ const STRIKER_LINE_Y2 = 50;
649
+ const STRIKER_MIN_X = 60;
650
+ const STRIKER_MAX_X = BOARD_SIZE - 60;
651
+
652
+ let canvas, ctx;
653
+ let socket;
654
+ let roomId;
655
+ let playerNumber;
656
+ let isMyTurn = false;
657
+ let gameState = null;
658
+ let isDraggingStriker = false;
659
+ let isAiming = false;
660
+ let aimStart = null;
661
+ let aimCurrent = null;
662
+ let animationId;
663
+ let localCoins = [];
664
+ let reconnectAttempts = 0;
665
+ const MAX_RECONNECT_ATTEMPTS = 5;
666
+ let boardRect = null;
667
+ let scaleX = 1, scaleY = 1;
668
+
669
+ window.onload = function() {
670
+ canvas = document.getElementById('gameCanvas');
671
+ ctx = canvas.getContext('2d');
672
+ connectToServer();
673
+ setupStrikerBars();
674
+ canvas.addEventListener('mousedown', handleAimStart);
675
+ canvas.addEventListener('mousemove', handleAimMove);
676
+ canvas.addEventListener('mouseup', handleAimEnd);
677
+ canvas.addEventListener('touchstart', handleTouchAimStart, {passive: false});
678
+ canvas.addEventListener('touchmove', handleTouchAimMove, {passive: false});
679
+ canvas.addEventListener('touchend', handleAimEnd);
680
+ window.addEventListener('resize', updateBoardRect);
681
+ };
682
+
683
+ function updateBoardRect() {
684
+ const board = document.getElementById('board');
685
+ if (board) {
686
+ boardRect = board.getBoundingClientRect();
687
+ scaleX = BOARD_SIZE / (boardRect.width - 70);
688
+ scaleY = BOARD_SIZE / (boardRect.height - 70);
689
+ }
690
+ }
691
+
692
+ function setupStrikerBars() {
693
+ const bottomHandle = document.getElementById('strikerHandleBottom');
694
+ const topHandle = document.getElementById('strikerHandleTop');
695
+
696
+ bottomHandle.addEventListener('mousedown', (e) => startDragStriker(e, 1));
697
+ bottomHandle.addEventListener('touchstart', (e) => startDragStriker(e, 1), {passive: false});
698
+ topHandle.addEventListener('mousedown', (e) => startDragStriker(e, 2));
699
+ topHandle.addEventListener('touchstart', (e) => startDragStriker(e, 2), {passive: false});
700
+
701
+ document.addEventListener('mousemove', dragStriker);
702
+ document.addEventListener('touchmove', dragStrikerTouch, {passive: false});
703
+ document.addEventListener('mouseup', endDragStriker);
704
+ document.addEventListener('touchend', endDragStriker);
705
+ }
706
+
707
+ let dragPlayerNum = 0;
708
+ let dragStartX = 0;
709
+ let handleStartX = 0;
710
+
711
+ function startDragStriker(e, playerNum) {
712
+ e.preventDefault();
713
+ e.stopPropagation();
714
+
715
+ if (!isMyTurn || gameState && gameState.isMoving) return;
716
+ if (playerNum !== playerNumber) return;
717
+
718
+ isDraggingStriker = true;
719
+ dragPlayerNum = playerNum;
720
+
721
+ const clientX = e.touches ? e.touches[0].clientX : e.clientX;
722
+ const bar = playerNum === 1 ?
723
+ document.getElementById('strikerBarBottom') :
724
+ document.getElementById('strikerBarTop');
725
+ const barRect = bar.getBoundingClientRect();
726
+
727
+ dragStartX = clientX;
728
+ const handle = bar.querySelector('.striker-handle');
729
+ handleStartX = parseFloat(handle.style.left) || (barRect.width / 2 - 16);
730
+ }
731
+
732
+ function dragStriker(e) {
733
+ if (!isDraggingStriker) return;
734
+
735
+ const clientX = e.clientX;
736
+ const bar = dragPlayerNum === 1 ?
737
+ document.getElementById('strikerBarBottom') :
738
+ document.getElementById('strikerBarTop');
739
+ const barRect = bar.getBoundingClientRect();
740
+ const handle = bar.querySelector('.striker-handle');
741
+
742
+ const deltaX = clientX - dragStartX;
743
+ const barWidth = barRect.width - 32;
744
+ let newLeft = handleStartX + deltaX;
745
+
746
+ newLeft = Math.max(0, Math.min(barWidth, newLeft));
747
+ handle.style.left = newLeft + 'px';
748
+
749
+ const percent = newLeft / barWidth;
750
+ const boardX = STRIKER_MIN_X + percent * (STRIKER_MAX_X - STRIKER_MIN_X);
751
+
752
+ if (socket && roomId) {
753
+ socket.emit('moveStriker', { roomId, x: boardX });
754
+ }
755
+ }
756
+
757
+ function dragStrikerTouch(e) {
758
+ if (!isDraggingStriker) return;
759
+ e.preventDefault();
760
+
761
+ const clientX = e.touches[0].clientX;
762
+ const bar = dragPlayerNum === 1 ?
763
+ document.getElementById('strikerBarBottom') :
764
+ document.getElementById('strikerBarTop');
765
+ const barRect = bar.getBoundingClientRect();
766
+ const handle = bar.querySelector('.striker-handle');
767
+
768
+ const deltaX = clientX - dragStartX;
769
+ const barWidth = barRect.width - 32;
770
+ let newLeft = handleStartX + deltaX;
771
+
772
+ newLeft = Math.max(0, Math.min(barWidth, newLeft));
773
+ handle.style.left = newLeft + 'px';
774
+
775
+ const percent = newLeft / barWidth;
776
+ const boardX = STRIKER_MIN_X + percent * (STRIKER_MAX_X - STRIKER_MIN_X);
777
+
778
+ if (socket && roomId) {
779
+ socket.emit('moveStriker', { roomId, x: boardX });
780
+ }
781
+ }
782
+
783
+ function endDragStriker() {
784
+ isDraggingStriker = false;
785
+ dragPlayerNum = 0;
786
+ }
787
+
788
+ function handleAimStart(e) {
789
+ if (!isMyTurn || !gameState || gameState.isMoving) return;
790
+
791
+ updateBoardRect();
792
+ const pos = getCanvasPos(e);
793
+
794
+ const striker = localCoins.find(c => c.type === 'striker');
795
+ if (striker) {
796
+ const dist = Math.hypot(pos.x - striker.x, pos.y - striker.y);
797
+ if (dist < STRIKER_RADIUS * 2.5) {
798
+ isAiming = true;
799
+ aimStart = { x: striker.x, y: striker.y };
800
+ aimCurrent = pos;
801
+ }
802
+ }
803
+ }
804
+
805
+ function handleAimMove(e) {
806
+ if (!isAiming) return;
807
+ const pos = getCanvasPos(e);
808
+ aimCurrent = pos;
809
+ }
810
+
811
+ function handleAimEnd(e) {
812
+ if (!isAiming) return;
813
+
814
+ isAiming = false;
815
+
816
+ const dx = aimStart.x - aimCurrent.x;
817
+ const dy = aimStart.y - aimCurrent.y;
818
+ const distance = Math.hypot(dx, dy);
819
+
820
+ if (distance > 15) {
821
+ const force = Math.min(distance * 0.12, 22);
822
+ const angle = Math.atan2(dy, dx);
823
+
824
+ socket.emit('strike', {
825
+ roomId,
826
+ force,
827
+ angle
828
+ });
829
+
830
+ log('Striker hit!');
831
+ }
832
+
833
+ aimStart = null;
834
+ aimCurrent = null;
835
+ }
836
+
837
+ function handleTouchAimStart(e) {
838
+ e.preventDefault();
839
+ handleAimStart(e.touches[0]);
840
+ }
841
+
842
+ function handleTouchAimMove(e) {
843
+ e.preventDefault();
844
+ handleAimMove(e.touches[0]);
845
+ }
846
+
847
+ function getCanvasPos(e) {
848
+ if (!boardRect) updateBoardRect();
849
+ return {
850
+ x: (e.clientX - boardRect.left - 35) * scaleX,
851
+ y: (e.clientY - boardRect.top - 35) * scaleY
852
+ };
853
+ }
854
+
855
+ function connectToServer() {
856
+ const serverUrl = window.location.origin;
857
+
858
+ socket = io(serverUrl, {
859
+ transports: ['websocket', 'polling'],
860
+ reconnection: true,
861
+ reconnectionAttempts: MAX_RECONNECT_ATTEMPTS,
862
+ reconnectionDelay: 1000,
863
+ timeout: 20000
864
+ });
865
+
866
+ socket.on('connect', () => {
867
+ reconnectAttempts = 0;
868
+ updateConnectionStatus(true);
869
+ document.getElementById('loader').style.display = 'none';
870
+ document.getElementById('waitingText').textContent = 'Enter Room Details';
871
+ document.getElementById('waitingSubtext').textContent = 'Create or join a room to start playing';
872
+ document.getElementById('roomControls').style.display = 'block';
873
+ document.getElementById('errorMessage').classList.remove('show');
874
+ log('Connected to server');
875
+ });
876
+
877
+ socket.on('connect_error', (error) => {
878
+ reconnectAttempts++;
879
+ if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
880
+ showError('Failed to connect. Please check your internet and refresh.');
881
+ document.getElementById('loader').style.display = 'none';
882
+ } else {
883
+ document.getElementById('waitingSubtext').textContent = `Retrying... (${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`;
884
+ }
885
+ });
886
+
887
+ socket.on('disconnect', (reason) => {
888
+ updateConnectionStatus(false);
889
+ log('Disconnected: ' + reason);
890
+ });
891
+
892
+ socket.on('roomJoined', (data) => {
893
+ roomId = data.roomId;
894
+ playerNumber = data.playerNumber;
895
+
896
+ document.getElementById('waitingOverlay').classList.add('hidden');
897
+ document.getElementById('gameContainer').style.display = 'flex';
898
+ document.getElementById('turnIndicator').style.display = 'block';
899
+ document.getElementById('resetBtn').style.display = 'block';
900
+ document.getElementById('roomCodeDisplay').style.display = 'block';
901
+ document.getElementById('roomCodeText').textContent = roomId;
902
+
903
+ log(`Joined room ${roomId} as Player ${playerNumber}`);
904
+ updatePlayerInfo(data.players);
905
+ updateBoardRect();
906
+ });
907
+
908
+ socket.on('playerJoined', (data) => {
909
+ log(`${data.name} joined as Player ${data.playerNumber}`);
910
+ updatePlayerInfo(data.players);
911
+ });
912
+
913
+ socket.on('gameState', (state) => {
914
+ gameState = state;
915
+ localCoins = JSON.parse(JSON.stringify(state.coins));
916
+ updateUI(state);
917
+ updateStrikerBars(state);
918
+ if (!animationId) {
919
+ animate();
920
+ }
921
+ });
922
+
923
+ socket.on('turnChange', (data) => {
924
+ isMyTurn = data.currentPlayer === playerNumber;
925
+ updateTurnIndicator(data);
926
+ updateStrikerBarVisibility();
927
+ });
928
+
929
+ socket.on('coinPocketed', (data) => {
930
+ log(`${data.playerName} pocketed ${data.coinType}!`);
931
+ });
932
+
933
+ socket.on('gameOver', (data) => {
934
+ const winnerCard = document.getElementById(`player${data.winner}Card`);
935
+ winnerCard.classList.add('winner');
936
+ log(`🎉 ${data.winnerName} WINS! 🎉`);
937
+ setTimeout(() => alert(`🎉 ${data.winnerName} WINS! 🎉`), 100);
938
+ });
939
+
940
+ socket.on('gameReset', () => {
941
+ document.querySelectorAll('.player-card').forEach(card => card.classList.remove('winner'));
942
+ log('Game reset');
943
+ });
944
+
945
+ socket.on('playerLeft', (data) => {
946
+ log(data.message);
947
+ showError(data.message + '. Waiting for new player...');
948
+ });
949
+
950
+ socket.on('error', (msg) => {
951
+ showError(msg);
952
+ document.getElementById('joinBtn').disabled = false;
953
+ document.getElementById('loader').style.display = 'none';
954
+ document.getElementById('roomControls').style.display = 'block';
955
+ });
956
+ }
957
+
958
+ function updateStrikerBars(state) {
959
+ if (!state) return;
960
+
961
+ const bottomHandle = document.getElementById('strikerHandleBottom');
962
+ const topHandle = document.getElementById('strikerHandleTop');
963
+ const bottomBar = document.getElementById('strikerBarBottom');
964
+
965
+ const striker = state.coins.find(c => c.type === 'striker');
966
+ if (!striker || !bottomBar) return;
967
+
968
+ const barWidth = bottomBar.offsetWidth - 32;
969
+ const percent = (striker.x - STRIKER_MIN_X) / (STRIKER_MAX_X - STRIKER_MIN_X);
970
+ const handlePos = Math.max(0, Math.min(barWidth, percent * barWidth));
971
+
972
+ bottomHandle.style.left = handlePos + 'px';
973
+ topHandle.style.left = handlePos + 'px';
974
+ }
975
+
976
+ function updateStrikerBarVisibility() {
977
+ const bottomBar = document.getElementById('strikerBarBottom');
978
+ const topBar = document.getElementById('strikerBarTop');
979
+
980
+ bottomBar.classList.remove('active');
981
+ topBar.classList.remove('active');
982
+
983
+ if (isMyTurn && gameState && !gameState.isMoving) {
984
+ if (playerNumber === 1) {
985
+ bottomBar.classList.add('active');
986
+ } else {
987
+ topBar.classList.add('active');
988
+ }
989
+ }
990
+ }
991
+
992
+ function showError(msg) {
993
+ const errorDiv = document.getElementById('errorMessage');
994
+ errorDiv.textContent = msg;
995
+ errorDiv.classList.add('show');
996
+ setTimeout(() => errorDiv.classList.remove('show'), 5000);
997
+ }
998
+
999
+ function updateConnectionStatus(connected) {
1000
+ const dot = document.getElementById('statusDot');
1001
+ const text = document.getElementById('statusText');
1002
+ if (connected) {
1003
+ dot.classList.add('connected');
1004
+ text.textContent = 'Connected';
1005
+ } else {
1006
+ dot.classList.remove('connected');
1007
+ text.textContent = 'Disconnected';
1008
+ }
1009
+ }
1010
+
1011
+ function joinRoom() {
1012
+ const name = document.getElementById('playerName').value.trim();
1013
+ const room = document.getElementById('roomId').value.trim();
1014
+
1015
+ if (!name) {
1016
+ showError('Please enter your name!');
1017
+ return;
1018
+ }
1019
+
1020
+ document.getElementById('joinBtn').disabled = true;
1021
+ document.getElementById('waitingText').textContent = 'Joining...';
1022
+ document.getElementById('waitingSubtext').textContent = 'Please wait';
1023
+ document.getElementById('roomControls').style.display = 'none';
1024
+ document.getElementById('loader').style.display = 'block';
1025
+
1026
+ socket.emit('joinRoom', { name, roomId: room });
1027
+ }
1028
+
1029
+ function updatePlayerInfo(players) {
1030
+ if (players[0]) {
1031
+ document.querySelector('#player1Card .player-name').textContent = players[0].name;
1032
+ }
1033
+ if (players[1]) {
1034
+ document.querySelector('#player2Card .player-name').textContent = players[1].name;
1035
+ }
1036
+ }
1037
+
1038
+ function updateUI(state) {
1039
+ document.getElementById('score1').textContent = state.scores[0];
1040
+ document.getElementById('score2').textContent = state.scores[1];
1041
+
1042
+ document.getElementById('player1Card').classList.toggle('active', state.currentPlayer === 1);
1043
+ document.getElementById('player2Card').classList.toggle('active', state.currentPlayer === 2);
1044
+ }
1045
+
1046
+ function updateTurnIndicator(data) {
1047
+ const indicator = document.getElementById('turnIndicator');
1048
+ if (data.currentPlayer === playerNumber) {
1049
+ indicator.textContent = '🎯 Your Turn! Position striker & pull back to aim';
1050
+ indicator.style.background = 'rgba(46, 204, 113, 0.3)';
1051
+ } else {
1052
+ indicator.textContent = `⏳ ${data.playerName}'s turn...`;
1053
+ indicator.style.background = 'rgba(231, 76, 60, 0.2)';
1054
+ }
1055
+ }
1056
+
1057
+ function animate() {
1058
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
1059
+
1060
+ if (isAiming && aimStart && aimCurrent) {
1061
+ const dx = aimStart.x - aimCurrent.x;
1062
+ const dy = aimStart.y - aimCurrent.y;
1063
+ const distance = Math.hypot(dx, dy);
1064
+ const angle = Math.atan2(dy, dx);
1065
+
1066
+ ctx.beginPath();
1067
+ ctx.moveTo(aimStart.x, aimStart.y);
1068
+ ctx.lineTo(aimCurrent.x, aimCurrent.y);
1069
+ ctx.strokeStyle = 'rgba(255, 255, 255, 0.6)';
1070
+ ctx.lineWidth = 2;
1071
+ ctx.setLineDash([5, 5]);
1072
+ ctx.stroke();
1073
+ ctx.setLineDash([]);
1074
+
1075
+ const force = Math.min(distance * 0.12, 22);
1076
+ ctx.beginPath();
1077
+ ctx.moveTo(aimStart.x, aimStart.y);
1078
+ ctx.lineTo(
1079
+ aimStart.x + Math.cos(angle) * force * 12,
1080
+ aimStart.y + Math.sin(angle) * force * 12
1081
+ );
1082
+ ctx.strokeStyle = `rgba(255, ${255 - (force/22)*100}, 0, 0.7)`;
1083
+ ctx.lineWidth = 3;
1084
+ ctx.stroke();
1085
+
1086
+ ctx.fillStyle = '#f39c12';
1087
+ ctx.font = 'bold 14px Poppins';
1088
+ ctx.fillText(`Power: ${Math.round((force/22)*100)}%`, aimStart.x - 30, aimStart.y - 20);
1089
+ }
1090
+
1091
+ if (localCoins) {
1092
+ localCoins.forEach(coin => {
1093
+ if (!coin.pocketed) {
1094
+ drawCoin(coin);
1095
+ }
1096
+ });
1097
+ }
1098
+
1099
+ requestAnimationFrame(animate);
1100
+ }
1101
+
1102
+ function drawCoin(coin) {
1103
+ ctx.beginPath();
1104
+ ctx.arc(coin.x, coin.y, coin.radius || COIN_RADIUS, 0, Math.PI * 2);
1105
+
1106
+ if (coin.type === 'striker') {
1107
+ const grad = ctx.createRadialGradient(coin.x - 3, coin.y - 3, 2, coin.x, coin.y, coin.radius);
1108
+ grad.addColorStop(0, '#ff6b6b');
1109
+ grad.addColorStop(1, '#c0392b');
1110
+ ctx.fillStyle = grad;
1111
+ ctx.strokeStyle = '#a93226';
1112
+ ctx.lineWidth = 2;
1113
+ } else if (coin.type === 'white') {
1114
+ const grad = ctx.createRadialGradient(coin.x - 3, coin.y - 3, 2, coin.x, coin.y, coin.radius);
1115
+ grad.addColorStop(0, '#ffffff');
1116
+ grad.addColorStop(1, '#e0e0e0');
1117
+ ctx.fillStyle = grad;
1118
+ ctx.strokeStyle = '#bbb';
1119
+ ctx.lineWidth = 1.5;
1120
+ } else if (coin.type === 'black') {
1121
+ const grad = ctx.createRadialGradient(coin.x - 3, coin.y - 3, 2, coin.x, coin.y, coin.radius);
1122
+ grad.addColorStop(0, '#34495e');
1123
+ grad.addColorStop(1, '#2c3e50');
1124
+ ctx.fillStyle = grad;
1125
+ ctx.strokeStyle = '#1a252f';
1126
+ ctx.lineWidth = 1.5;
1127
+ } else if (coin.type === 'queen') {
1128
+ const grad = ctx.createRadialGradient(coin.x - 3, coin.y - 3, 2, coin.x, coin.y, coin.radius);
1129
+ grad.addColorStop(0, '#ff6b6b');
1130
+ grad.addColorStop(1, '#c0392b');
1131
+ ctx.fillStyle = grad;
1132
+ ctx.strokeStyle = '#a93226';
1133
+ ctx.lineWidth = 2;
1134
+ }
1135
+
1136
+ ctx.fill();
1137
+ ctx.stroke();
1138
+
1139
+ ctx.beginPath();
1140
+ ctx.arc(coin.x - 3, coin.y - 3, (coin.radius || COIN_RADIUS) / 3, 0, Math.PI * 2);
1141
+ ctx.fillStyle = 'rgba(255,255,255,0.4)';
1142
+ ctx.fill();
1143
+
1144
+ if (coin.type === 'queen') {
1145
+ ctx.beginPath();
1146
+ ctx.arc(coin.x, coin.y, 5, 0, Math.PI * 2);
1147
+ ctx.fillStyle = '#f1c40f';
1148
+ ctx.fill();
1149
+ ctx.strokeStyle = '#d4ac0d';
1150
+ ctx.lineWidth = 1;
1151
+ ctx.stroke();
1152
+ }
1153
+ }
1154
+
1155
+ function requestReset() {
1156
+ if (confirm('Reset the game?')) {
1157
+ socket.emit('resetGame', roomId);
1158
+ }
1159
+ }
1160
+
1161
+ function log(message) {
1162
+ const logContent = document.getElementById('logContent');
1163
+ const entry = document.createElement('div');
1164
+ entry.className = 'log-entry';
1165
+ entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
1166
+ logContent.insertBefore(entry, logContent.firstChild);
1167
+
1168
+ while (logContent.children.length > 20) {
1169
+ logContent.removeChild(logContent.lastChild);
1170
+ }
1171
+ }
1172
+ </script>
1173
+ </body>
1174
+ </html>