cemo commited on
Commit
bc24ada
·
verified ·
1 Parent(s): 35ad59f

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +1161 -18
index.html CHANGED
@@ -1,19 +1,1162 @@
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
+
4
+ <head>
5
+ <meta charset="utf-8">
6
+ <title>GymFlow Home</title>
7
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
8
+ <meta name="color-scheme" content="light dark">
9
+ <style>
10
+ /* ---------- Design tokens ---------- */
11
+ :root {
12
+ --brand: #6d5efc;
13
+ --brand-2: #22c55e;
14
+ --bg: #f7f7fb;
15
+ --surface: #ffffff;
16
+ --surface-2: #f2f2f7;
17
+ --text: #0f172a;
18
+ --muted: #64748b;
19
+ --border: #e5e7eb;
20
+ --ring: rgba(109, 94, 252, 0.35);
21
+ --shadow: 0 10px 25px rgba(17, 24, 39, 0.08);
22
+ --radius: 14px;
23
+ --radius-sm: 10px;
24
+
25
+ --success: #22c55e;
26
+ --danger: #ef4444;
27
+ --warning: #f59e0b;
28
+
29
+ --focus: 2px solid var(--ring);
30
+ }
31
+
32
+ @media (prefers-color-scheme: dark) {
33
+ :root {
34
+ --bg: #0b1020;
35
+ --surface: #0f152b;
36
+ --surface-2: #0c1226;
37
+ --text: #e6e8ee;
38
+ --muted: #93a0b8;
39
+ --border: #1f2940;
40
+ --ring: rgba(109, 94, 252, 0.45);
41
+ --shadow: 0 10px 25px rgba(0, 0, 0, 0.35);
42
+ }
43
+ }
44
+
45
+ /* ---------- Reset ---------- */
46
+ * {
47
+ box-sizing: border-box;
48
+ }
49
+
50
+ html,
51
+ body {
52
+ height: 100%;
53
+ }
54
+
55
+ body {
56
+ margin: 0;
57
+ font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Helvetica Neue, Arial, "Apple Color Emoji", "Segoe UI Emoji";
58
+ background: radial-gradient(1200px 600px at 85% -200px, rgba(109, 94, 252, 0.12), transparent 60%), var(--bg);
59
+ color: var(--text);
60
+ line-height: 1.35;
61
+ -webkit-font-smoothing: antialiased;
62
+ -moz-osx-font-smoothing: grayscale;
63
+ }
64
+
65
+ img,
66
+ svg {
67
+ display: block;
68
+ }
69
+
70
+ button {
71
+ font: inherit;
72
+ color: inherit;
73
+ }
74
+
75
+ a {
76
+ color: inherit;
77
+ text-decoration: none;
78
+ }
79
+
80
+ /* ---------- App layout ---------- */
81
+ .app {
82
+ max-width: 1100px;
83
+ margin: 0 auto;
84
+ padding: clamp(14px, 2.4vw, 24px);
85
+ display: grid;
86
+ gap: 20px;
87
+ }
88
+
89
+ header.appbar {
90
+ position: sticky;
91
+ top: 0;
92
+ z-index: 40;
93
+ padding: clamp(8px, 1.8vw, 14px);
94
+ border-radius: 16px;
95
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.35), rgba(255, 255, 255, 0));
96
+ backdrop-filter: saturate(1.4) blur(10px);
97
+ border: 1px solid var(--border);
98
+ box-shadow: var(--shadow);
99
+ display: grid;
100
+ grid-template-columns: 1fr auto auto;
101
+ align-items: center;
102
+ gap: 16px;
103
+ }
104
+
105
+ @media (prefers-color-scheme: dark) {
106
+ header.appbar {
107
+ background: linear-gradient(180deg, rgba(17, 25, 40, 0.65), rgba(17, 25, 40, 0.1));
108
+ }
109
+ }
110
+
111
+ .brand {
112
+ display: flex;
113
+ align-items: center;
114
+ gap: 12px;
115
+ }
116
+
117
+ .brand-badge {
118
+ width: 38px;
119
+ height: 38px;
120
+ border-radius: 12px;
121
+ background: conic-gradient(from 200deg at 50% 50%, #6d5efc, #22c55e 55%, #06b6d4 85%, #6d5efc);
122
+ position: relative;
123
+ box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.35), 0 6px 16px rgba(109, 94, 252, 0.4);
124
+ display: grid;
125
+ place-items: center;
126
+ color: white;
127
+ font-weight: 700;
128
+ letter-spacing: 0.5px;
129
+ font-size: 13px;
130
+ }
131
+
132
+ .brand h1 {
133
+ font-size: 18px;
134
+ margin: 0;
135
+ }
136
+
137
+ .brand small {
138
+ color: var(--muted);
139
+ display: block;
140
+ font-size: 12px;
141
+ margin-top: 2px;
142
+ }
143
+
144
+ .brand a.credit {
145
+ margin-left: 8px;
146
+ padding: 6px 10px;
147
+ border-radius: 999px;
148
+ font-size: 12px;
149
+ color: var(--brand);
150
+ border: 1px dashed color-mix(in oklab, var(--brand), transparent 65%);
151
+ background: color-mix(in oklab, var(--brand), transparent 92%);
152
+ transition: 0.2s ease;
153
+ }
154
+
155
+ .brand a.credit:hover {
156
+ transform: translateY(-1px);
157
+ }
158
+
159
+ .search {
160
+ display: none;
161
+ }
162
+
163
+ @media (min-width: 760px) {
164
+ .search {
165
+ display: block;
166
+ position: relative;
167
+ }
168
+
169
+ .search input {
170
+ width: 300px;
171
+ max-width: 46vw;
172
+ padding: 10px 14px 10px 36px;
173
+ border-radius: 999px;
174
+ border: 1px solid var(--border);
175
+ background: var(--surface);
176
+ outline: none;
177
+ transition: 0.2s border, 0.2s box-shadow;
178
+ }
179
+
180
+ .search input:focus {
181
+ border-color: var(--brand);
182
+ box-shadow: 0 0 0 6px var(--ring);
183
+ }
184
+
185
+ .search svg {
186
+ position: absolute;
187
+ top: 50%;
188
+ left: 12px;
189
+ transform: translateY(-50%);
190
+ width: 16px;
191
+ height: 16px;
192
+ opacity: 0.7;
193
+ }
194
+ }
195
+
196
+ .user {
197
+ display: flex;
198
+ align-items: center;
199
+ gap: 12px;
200
+ }
201
+
202
+ .user .avatar {
203
+ width: 36px;
204
+ height: 36px;
205
+ border-radius: 50%;
206
+ background: linear-gradient(135deg, #22c55e, #06b6d4);
207
+ display: grid;
208
+ place-items: center;
209
+ color: white;
210
+ font-weight: 700;
211
+ border: 2px solid rgba(255, 255, 255, 0.7);
212
+ }
213
+
214
+ .user .meta {
215
+ display: none;
216
+ }
217
+
218
+ @media (min-width: 740px) {
219
+ .user .meta {
220
+ display: block;
221
+ }
222
+
223
+ .user .meta .name {
224
+ font-weight: 700;
225
+ font-size: 14px;
226
+ }
227
+
228
+ .user .meta .plan {
229
+ font-size: 12px;
230
+ color: var(--muted);
231
+ }
232
+ }
233
+
234
+ /* ---------- Navigation ---------- */
235
+ nav.tabbar {
236
+ position: sticky;
237
+ top: calc(56px + clamp(14px, 2.4vw, 24px));
238
+ z-index: 30;
239
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.65), rgba(255, 255, 255, 0));
240
+ backdrop-filter: saturate(1.2) blur(8px);
241
+ border: 1px solid var(--border);
242
+ border-radius: 999px;
243
+ width: fit-content;
244
+ margin: 0 auto;
245
+ padding: 6px;
246
+ display: flex;
247
+ gap: 6px;
248
+ }
249
+
250
+ .tabbar button {
251
+ padding: 8px 14px;
252
+ border-radius: 999px;
253
+ border: 0;
254
+ background: transparent;
255
+ color: var(--muted);
256
+ font-weight: 600;
257
+ cursor: pointer;
258
+ transition: 0.2s ease background, 0.2s ease color, 0.2s ease transform;
259
+ }
260
+
261
+ .tabbar button.active {
262
+ background: var(--brand);
263
+ color: white;
264
+ box-shadow: 0 6px 18px rgba(109, 94, 252, 0.35);
265
+ }
266
+
267
+ .tabbar button:hover {
268
+ transform: translateY(-1px);
269
+ }
270
+
271
+ main.content {
272
+ display: grid;
273
+ gap: 24px;
274
+ }
275
+
276
+ section.block {
277
+ background: var(--surface);
278
+ border: 1px solid var(--border);
279
+ border-radius: var(--radius);
280
+ padding: clamp(14px, 2.2vw, 18px);
281
+ box-shadow: var(--shadow);
282
+ }
283
+
284
+ .block h2 {
285
+ margin: 0 0 14px 0;
286
+ font-size: 18px;
287
+ display: flex;
288
+ align-items: center;
289
+ gap: 8px;
290
+ }
291
+
292
+ .muted {
293
+ color: var(--muted);
294
+ }
295
+
296
+ /* ---------- Activities grid ---------- */
297
+ .activities {
298
+ display: grid;
299
+ grid-template-columns: repeat(2, 1fr);
300
+ gap: 12px;
301
+ }
302
+
303
+ @media (min-width: 620px) {
304
+ .activities {
305
+ grid-template-columns: repeat(3, 1fr);
306
+ }
307
+ }
308
+
309
+ @media (min-width: 940px) {
310
+ .activities {
311
+ grid-template-columns: repeat(4, 1fr);
312
+ }
313
+ }
314
+
315
+ .card {
316
+ background: linear-gradient(180deg, var(--surface), var(--surface-2));
317
+ border: 1px solid var(--border);
318
+ border-radius: var(--radius-sm);
319
+ padding: 14px;
320
+ display: grid;
321
+ gap: 10px;
322
+ transition: 0.2s transform, 0.2s box-shadow, 0.2s border-color;
323
+ position: relative;
324
+ isolation: isolate;
325
+ }
326
+
327
+ .card:hover {
328
+ transform: translateY(-2px);
329
+ box-shadow: 0 14px 30px rgba(0, 0, 0, 0.08);
330
+ }
331
+
332
+ .card .row {
333
+ display: flex;
334
+ align-items: center;
335
+ justify-content: space-between;
336
+ gap: 8px;
337
+ }
338
+
339
+ .pill {
340
+ font-size: 12px;
341
+ padding: 4px 8px;
342
+ border-radius: 999px;
343
+ border: 1px solid var(--border);
344
+ color: var(--muted);
345
+ }
346
+
347
+ .card .icon {
348
+ width: 42px;
349
+ height: 42px;
350
+ border-radius: 12px;
351
+ display: grid;
352
+ place-items: center;
353
+ font-size: 22px;
354
+ background: color-mix(in oklab, var(--brand), transparent 85%);
355
+ border: 1px solid color-mix(in oklab, var(--brand), transparent 75%);
356
+ }
357
+
358
+ .card .title {
359
+ font-weight: 700;
360
+ }
361
+
362
+ .card .subtitle {
363
+ font-size: 12px;
364
+ color: var(--muted);
365
+ }
366
+
367
+ .checkin-btn {
368
+ margin-top: 4px;
369
+ padding: 10px 12px;
370
+ border-radius: 12px;
371
+ border: 1px solid var(--border);
372
+ background: var(--surface);
373
+ cursor: pointer;
374
+ display: inline-flex;
375
+ align-items: center;
376
+ justify-content: center;
377
+ gap: 8px;
378
+ font-weight: 700;
379
+ transition: 0.2s ease box-shadow, 0.2s ease background, 0.2s ease color, 0.2s ease border;
380
+ }
381
+
382
+ .checkin-btn:hover {
383
+ transform: translateY(-1px);
384
+ }
385
+
386
+ .checkin-btn:focus-visible {
387
+ outline: none;
388
+ box-shadow: 0 0 0 6px var(--ring);
389
+ }
390
+
391
+ .checkin-btn .dot {
392
+ width: 8px;
393
+ height: 8px;
394
+ border-radius: 999px;
395
+ background: var(--muted);
396
+ box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.6);
397
+ }
398
+
399
+ .checkin-btn[aria-pressed="true"] {
400
+ background: color-mix(in oklab, var(--brand), transparent 92%);
401
+ border-color: color-mix(in oklab, var(--brand), transparent 55%);
402
+ color: color-mix(in oklab, var(--brand), black 25%);
403
+ }
404
+
405
+ .checkin-btn[aria-pressed="true"] .dot {
406
+ background: var(--brand);
407
+ }
408
+
409
+ .checkin-btn[disabled] {
410
+ opacity: 0.7;
411
+ cursor: not-allowed;
412
+ transform: none;
413
+ }
414
+
415
+ /* ---------- Today's classes ---------- */
416
+ .classes {
417
+ display: grid;
418
+ gap: 12px;
419
+ }
420
+
421
+ .class {
422
+ display: grid;
423
+ gap: 8px;
424
+ grid-template-columns: auto 1fr auto;
425
+ align-items: center;
426
+ padding: 12px;
427
+ border: 1px solid var(--border);
428
+ border-radius: 12px;
429
+ background: linear-gradient(180deg, var(--surface), var(--surface-2));
430
+ transition: 0.2s transform, 0.2s box-shadow;
431
+ }
432
+
433
+ .class:hover {
434
+ transform: translateY(-1px);
435
+ box-shadow: 0 10px 22px rgba(0, 0, 0, 0.07);
436
+ }
437
+
438
+ .class .ico {
439
+ width: 42px;
440
+ height: 42px;
441
+ border-radius: 12px;
442
+ display: grid;
443
+ place-items: center;
444
+ font-size: 20px;
445
+ background: color-mix(in oklab, var(--brand), transparent 85%);
446
+ border: 1px solid color-mix(in oklab, var(--brand), transparent 75%);
447
+ }
448
+
449
+ .class .name {
450
+ font-weight: 800;
451
+ }
452
+
453
+ .class .info {
454
+ font-size: 12px;
455
+ color: var(--muted);
456
+ }
457
+
458
+ .class .time {
459
+ font-size: 12px;
460
+ color: var(--muted);
461
+ }
462
+
463
+ .class .act {
464
+ display: flex;
465
+ align-items: center;
466
+ gap: 8px;
467
+ }
468
+
469
+ .tag {
470
+ padding: 6px 10px;
471
+ border-radius: 999px;
472
+ font-size: 12px;
473
+ font-weight: 700;
474
+ border: 1px solid var(--border);
475
+ background: var(--surface);
476
+ }
477
+
478
+ .btn {
479
+ padding: 8px 12px;
480
+ border-radius: 10px;
481
+ border: 1px solid var(--border);
482
+ background: var(--surface);
483
+ font-weight: 700;
484
+ cursor: pointer;
485
+ transition: 0.2s ease;
486
+ }
487
+
488
+ .btn:hover {
489
+ transform: translateY(-1px);
490
+ box-shadow: 0 10px 18px rgba(0, 0, 0, 0.06);
491
+ }
492
+
493
+ .btn.primary {
494
+ background: var(--brand);
495
+ border-color: color-mix(in oklab, var(--brand), black 10%);
496
+ color: white;
497
+ box-shadow: 0 10px 22px rgba(109, 94, 252, 0.35);
498
+ }
499
+
500
+ .btn.ghost {
501
+ background: transparent;
502
+ }
503
+
504
+ /* ---------- Stats ---------- */
505
+ .stats {
506
+ display: grid;
507
+ gap: 12px;
508
+ grid-template-columns: 1fr;
509
+ }
510
+
511
+ @media (min-width: 800px) {
512
+ .stats {
513
+ grid-template-columns: 1.2fr 1fr;
514
+ }
515
+ }
516
+
517
+ .kpis {
518
+ display: grid;
519
+ gap: 12px;
520
+ grid-template-columns: repeat(3, 1fr);
521
+ }
522
+
523
+ .kpi {
524
+ background: linear-gradient(180deg, var(--surface), var(--surface-2));
525
+ border: 1px solid var(--border);
526
+ border-radius: 12px;
527
+ padding: 14px;
528
+ display: grid;
529
+ gap: 6px;
530
+ }
531
+
532
+ .kpi .value {
533
+ font-size: 22px;
534
+ font-weight: 900;
535
+ letter-spacing: 0.2px;
536
+ }
537
+
538
+ .kpi .label {
539
+ font-size: 12px;
540
+ color: var(--muted);
541
+ }
542
+
543
+ .chart {
544
+ background: linear-gradient(180deg, var(--surface), var(--surface-2));
545
+ border: 1px solid var(--border);
546
+ border-radius: 12px;
547
+ padding: 14px;
548
+ display: grid;
549
+ gap: 12px;
550
+ }
551
+
552
+ .bars {
553
+ display: grid;
554
+ grid-template-columns: repeat(7, 1fr);
555
+ gap: 8px;
556
+ align-items: end;
557
+ height: 120px;
558
+ }
559
+
560
+ .bar {
561
+ --h: 0%;
562
+ background: linear-gradient(180deg, color-mix(in oklab, var(--brand), white 15%), var(--brand));
563
+ border-radius: 8px 8px 4px 4px;
564
+ height: var(--h);
565
+ position: relative;
566
+ min-height: 4px;
567
+ box-shadow: 0 6px 12px rgba(109, 94, 252, 0.25);
568
+ }
569
+
570
+ .bar::after {
571
+ content: attr(data-count);
572
+ position: absolute;
573
+ bottom: calc(var(--h) + 6px);
574
+ left: 50%;
575
+ transform: translateX(-50%);
576
+ font-size: 11px;
577
+ color: var(--muted);
578
+ }
579
+
580
+ .days {
581
+ display: grid;
582
+ grid-template-columns: repeat(7, 1fr);
583
+ gap: 8px;
584
+ text-align: center;
585
+ font-size: 11px;
586
+ color: var(--muted);
587
+ }
588
+
589
+ /* ---------- History ---------- */
590
+ .history-list {
591
+ display: grid;
592
+ gap: 10px;
593
+ }
594
+
595
+ .history-item {
596
+ display: grid;
597
+ gap: 8px;
598
+ grid-template-columns: auto 1fr auto;
599
+ align-items: center;
600
+ padding: 12px;
601
+ border: 1px solid var(--border);
602
+ border-radius: 12px;
603
+ background: linear-gradient(180deg, var(--surface), var(--surface-2));
604
+ }
605
+
606
+ .badge {
607
+ padding: 4px 8px;
608
+ font-size: 11px;
609
+ border-radius: 999px;
610
+ font-weight: 700;
611
+ border: 1px solid var(--border);
612
+ color: var(--muted);
613
+ }
614
+
615
+ .empty {
616
+ padding: 18px;
617
+ border: 1px dashed var(--border);
618
+ border-radius: 12px;
619
+ color: var(--muted);
620
+ text-align: center;
621
+ }
622
+
623
+ /* ---------- Footer ---------- */
624
+ footer {
625
+ text-align: center;
626
+ color: var(--muted);
627
+ font-size: 12px;
628
+ padding: 18px 0 40px;
629
+ }
630
+
631
+ /* ---------- Toast ---------- */
632
+ .toast {
633
+ position: fixed;
634
+ z-index: 60;
635
+ left: 50%;
636
+ bottom: 24px;
637
+ transform: translateX(-50%) translateY(40px);
638
+ background: var(--surface);
639
+ border: 1px solid var(--border);
640
+ box-shadow: var(--shadow);
641
+ color: var(--text);
642
+ padding: 10px 14px;
643
+ border-radius: 12px;
644
+ opacity: 0;
645
+ pointer-events: none;
646
+ transition: 0.25s ease transform, 0.25s ease opacity;
647
+ }
648
+
649
+ .toast.show {
650
+ transform: translateX(-50%) translateY(0);
651
+ opacity: 1;
652
+ }
653
+
654
+ /* ---------- Utilities ---------- */
655
+ .spacer {
656
+ height: 4px;
657
+ }
658
+
659
+ .sr-only {
660
+ position: absolute;
661
+ width: 1px;
662
+ height: 1px;
663
+ padding: 0;
664
+ margin: -1px;
665
+ overflow: hidden;
666
+ clip: rect(0, 0, 0, 0);
667
+ white-space: nowrap;
668
+ border: 0;
669
+ }
670
+ </style>
671
+ </head>
672
+
673
+ <body>
674
+ <div class="app">
675
+ <header class="appbar" role="banner">
676
+ <div class="brand">
677
+ <div class="brand-badge" aria-hidden="true">GF</div>
678
+ <div>
679
+ <h1>GymFlow</h1>
680
+ <small>Your day starts here</small>
681
+ <a class="credit" href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" rel="noopener">Built
682
+ with anycoder</a>
683
+ </div>
684
+ </div>
685
+ <div class="search" role="search">
686
+ <svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
687
+ <path d="M21 21l-4.35-4.35m1.35-5.15a7 7 0 11-14 0 7 7 0 0114 0z" stroke="currentColor" stroke-width="1.7"
688
+ stroke-linecap="round" />
689
+ </svg>
690
+ <input id="search" type="search" placeholder="Search activities, classes..." aria-label="Search">
691
+ </div>
692
+ <div class="user" role="navigation" aria-label="User menu">
693
+ <div class="avatar" aria-hidden="true">JD</div>
694
+ <div class="meta">
695
+ <div class="name">Jordan</div>
696
+ <div class="plan">Premium</div>
697
+ </div>
698
+ </div>
699
+ </header>
700
+
701
+ <nav class="tabbar" role="tablist" aria-label="Primary">
702
+ <button role="tab" aria-selected="true" class="active" data-view="home">Home</button>
703
+ <button role="tab" aria-selected="false" data-view="history">History</button>
704
+ </nav>
705
+
706
+ <main class="content">
707
+ <!-- Home View -->
708
+ <section id="view-home">
709
+ <section class="block" aria-labelledby="quick-checkin-title">
710
+ <h2 id="quick-checkin-title">Quick Check-In</h2>
711
+ <p class="muted" style="margin: 0 0 12px;">Tap an activity to check in for today. You can uncheck to remove
712
+ your check-in.</p>
713
+ <div id="activities" class="activities" role="list"></div>
714
+ </section>
715
+
716
+ <section class="block" aria-labelledby="today-classes-title">
717
+ <h2 id="today-classes-title">Today's Classes</h2>
718
+ <div id="classes" class="classes" role="list"></div>
719
+ </section>
720
+
721
+ <section class="block" aria-labelledby="stats-title">
722
+ <h2 id="stats-title">Your Activity</h2>
723
+ <div class="stats">
724
+ <div class="chart" aria-label="Weekly check-ins">
725
+ <div class="muted" style="display:flex;justify-content:space-between;align-items:center;">
726
+ <span>Last 7 days</span>
727
+ <span id="week-total" style="font-weight:800;"></span>
728
+ </div>
729
+ <div id="bars" class="bars" aria-hidden="true"></div>
730
+ <div class="days">
731
+ <div>Mon</div>
732
+ <div>Tue</div>
733
+ <div>Wed</div>
734
+ <div>Thu</div>
735
+ <div>Fri</div>
736
+ <div>Sat</div>
737
+ <div>Sun</div>
738
+ </div>
739
+ </div>
740
+ <div class="kpis" role="list">
741
+ <div class="kpi" role="listitem">
742
+ <div class="value" id="today-count">0</div>
743
+ <div class="label">Check-ins today</div>
744
+ </div>
745
+ <div class="kpi" role="listitem">
746
+ <div class="value" id="streak">0</div>
747
+ <div class="label">Current streak (days)</div>
748
+ </div>
749
+ <div class="kpi" role="listitem">
750
+ <div class="value" id="month-total">0</div>
751
+ <div class="label">This month</div>
752
+ </div>
753
+ </div>
754
+ </div>
755
+ </section>
756
+ </section>
757
+
758
+ <!-- History View -->
759
+ <section id="view-history" hidden>
760
+ <section class="block" aria-labelledby="history-title">
761
+ <h2 id="history-title">Recent Check-Ins</h2>
762
+ <div id="history-list" class="history-list"></div>
763
+ </section>
764
+ </section>
765
+ </main>
766
+
767
+ <footer>
768
+ © <span id="year"></span> GymFlow. Move better, feel better.
769
+ </footer>
770
+ </div>
771
+
772
+ <div id="toast" class="toast" role="status" aria-live="polite"></div>
773
+
774
+ <script>
775
+ // ---------- Data ----------
776
+ const ACTIVITIES = [
777
+ { id: 'crossfit', name: 'CrossFit', icon: '💪', color: '#f97316' },
778
+ { id: 'pilates', name: 'Pilates', icon: '🧘‍♀️', color: '#06b6d4' },
779
+ { id: 'yoga', name: 'Yoga', icon: '🧘', color: '#a78bfa' },
780
+ { id: 'spin', name: 'Spin', icon: '🚴', color: '#22c55e' },
781
+ { id: 'run', name: 'Run', icon: '🏃', color: '#ef4444' },
782
+ { id: 'hiit', name: 'HIIT', icon: '⏱️', color: '#f59e0b' },
783
+ { id: 'strength',name: 'Strength',icon: '🏋️', color: '#64748b' },
784
+ { id: 'mobility',name: 'Mobility',icon: '🦴', color: '#14b8a6' },
785
+ ];
786
+
787
+ const SCHEDULE = [
788
+ { id:'c1', t:'06:30', name:'CrossFit: Foundation', instructor:'Sam', icon:'💪' },
789
+ { id:'c2', t:'08:00', name:'Spin: Intervals', instructor:'Maya', icon:'🚴' },
790
+ { id:'c3', t:'12:15', name:'Power Yoga', instructor:'Jess', icon:'🧘' },
791
+ { id:'c4', t:'18:00', name:'Pilates Mat', instructor:'Lena', icon:'🧘‍♀️' },
792
+ { id:'c5', t:'19:15', name:'HIIT Express', instructor:'Rae', icon:'⏱️' },
793
+ ];
794
+
795
+ // ---------- Storage ----------
796
+ const STORAGE_KEY = 'gymflow_data_v1';
797
+ function loadData() {
798
+ try {
799
+ const raw = localStorage.getItem(STORAGE_KEY);
800
+ if (!raw) return { checkInsByDate: {}, current: [] };
801
+ const data = JSON.parse(raw);
802
+ if (!data.checkInsByDate) data.checkInsByDate = {};
803
+ if (!Array.isArray(data.current)) data.current = [];
804
+ return data;
805
+ } catch {
806
+ return { checkInsByDate: {}, current: [] };
807
+ }
808
+ }
809
+ function saveData() {
810
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(state.data));
811
+ }
812
+ function todayKey(d = new Date()) {
813
+ const y = d.getFullYear();
814
+ const m = String(d.getMonth()+1).padStart(2,'0');
815
+ const dd = String(d.getDate()).padStart(2,'0');
816
+ return `${y}-${m}-${dd}`;
817
+ }
818
+
819
+ // ---------- State ----------
820
+ const state = {
821
+ data: loadData(),
822
+ view: 'home',
823
+ };
824
+
825
+ // ---------- Utils ----------
826
+ function uniq(arr) {
827
+ return [...new Set(arr)];
828
+ }
829
+ function formatTime(date) {
830
+ return date.toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'});
831
+ }
832
+ function countToday() {
833
+ const key = todayKey();
834
+ return (state.data.checkInsByDate[key] || []).length;
835
+ }
836
+ function listTodaySet() {
837
+ const key = todayKey();
838
+ return new Set(state.data.checkInsByDate[key] || []);
839
+ }
840
+ function setToday(arr) {
841
+ const key = todayKey();
842
+ state.data.checkInsByDate[key] = uniq(arr);
843
+ saveData();
844
+ }
845
+ function addToday(id) {
846
+ const key = todayKey();
847
+ const s = new Set(state.data.checkInsByDate[key] || []);
848
+ s.add(id);
849
+ state.data.checkInsByDate[key] = [...s];
850
+ saveData();
851
+ }
852
+ function removeToday(id) {
853
+ const key = todayKey();
854
+ const arr = state.data.checkInsByDate[key] || [];
855
+ state.data.checkInsByDate[key] = arr.filter(x => x !== id);
856
+ saveData();
857
+ }
858
+
859
+ function computeStreak() {
860
+ // Streak of days with at least 1 check-in, ending today
861
+ const map = state.data.checkInsByDate;
862
+ const today = new Date();
863
+ let streak = 0;
864
+ for (let i=0; ; i++) {
865
+ const d = new Date(today);
866
+ d.setDate(today.getDate() - i);
867
+ const k = todayKey(d);
868
+ if ((map[k] || []).length > 0) streak++;
869
+ else break;
870
+ }
871
+ return streak;
872
+ }
873
+
874
+ function computeThisMonth() {
875
+ const map = state.data.checkInsByDate;
876
+ const now = new Date();
877
+ const y = now.getFullYear();
878
+ const m = now.getMonth();
879
+ let total = 0;
880
+ for (const [k, arr] of Object.entries(map)) {
881
+ const [yy, mm] = k.split('-').map(Number);
882
+ if (yy === y && mm-1 === m) total += (arr || []).length;
883
+ }
884
+ return total;
885
+ }
886
+
887
+ function last7DaysCounts() {
888
+ const out = [];
889
+ const map = state.data.checkInsByDate;
890
+ const today = new Date();
891
+ let max = 0;
892
+ for (let i=6; i>=0; i--) {
893
+ const d = new Date(today);
894
+ d.setDate(today.getDate() - i);
895
+ const k = todayKey(d);
896
+ const count = (map[k] || []).length;
897
+ out.push({ d, k, count });
898
+ if (count > max) max = count;
899
+ }
900
+ return { days: out, max: Math.max(max, 1) };
901
+ }
902
+
903
+ // ---------- UI Builders ----------
904
+ function activityCard(a, active) {
905
+ const el = document.createElement('article');
906
+ el.className = 'card';
907
+ el.role = 'listitem';
908
+ el.dataset.activityId = a.id;
909
+ el.style.setProperty('--accent', a.color);
910
+ el.innerHTML = `
911
+ <div class="row">
912
+ <div class="row" style="gap:10px;">
913
+ <div class="icon" aria-hidden="true">${a.icon}</div>
914
+ <div>
915
+ <div class="title">${a.name}</div>
916
+ <div class="subtitle">Check in to track progress</div>
917
+ </div>
918
+ </div>
919
+ <span class="pill">${active ? 'Checked-in' : 'Available'}</span>
920
+ </div>
921
+ <button class="checkin-btn" aria-pressed="${active ? 'true':'false'}" data-id="${a.id}">
922
+ <span class="dot" aria-hidden="true"></span>
923
+ <span>${active ? 'Checked-in' : 'Check in'}</span>
924
+ </button>
925
+ `;
926
+ const btn = el.querySelector('button');
927
+ btn.addEventListener('click', () => onToggleCheckin(a.id, el));
928
+ return el;
929
+ }
930
+
931
+ function classItem(c, checkedForThisClass) {
932
+ // Determine activity id that matches closest by name
933
+ const actId = (() => {
934
+ const lc = c.name.toLowerCase();
935
+ if (lc.includes('crossfit')) return 'crossfit';
936
+ if (lc.includes('spin')) return 'spin';
937
+ if (lc.includes('yoga') || lc.includes('pilates')) return lc.includes('yoga') ? 'yoga' : 'pilates';
938
+ if (lc.includes('hiit')) return 'hiit';
939
+ return 'strength';
940
+ })();
941
+
942
+ const el = document.createElement('article');
943
+ el.className = 'class';
944
+ el.role = 'listitem';
945
+ el.innerHTML = `
946
+ <div class="ico" aria-hidden="true">${c.icon}</div>
947
+ <div>
948
+ <div class="name">${c.name}</div>
949
+ <div class="info">with ${c.instructor}</div>
950
+ <div class="time">${c.t}</div>
951
+ </div>
952
+ <div class="act">
953
+ ${checkedForThisClass ? `<span class="tag" style="border-color: color-mix(in oklab, var(--brand), transparent 50%); color: var(--brand); background: color-mix(in oklab, var(--brand), transparent 92%);">Booked</span>` : ''}
954
+ <button class="btn ${checkedForThisClass ? '' : 'primary'}" data-act="${actId}" data-class="${c.id}">
955
+ ${checkedForThisClass ? 'Unbook' : 'Book & Check-in'}
956
+ </button>
957
+ </div>
958
+ `;
959
+ const btn = el.querySelector('button');
960
+ btn.addEventListener('click', () => {
961
+ if (btn.dataset.action === 'unbook') return; // not used, placeholder
962
+ const id = btn.dataset.act;
963
+ onToggleCheckin(id);
964
+ renderClasses(); // refresh "Booked" state
965
+ showToast(`${c.name} ${listTodaySet().has(id) ? 'checked-in' : 'removed'}.`);
966
+ });
967
+ return el;
968
+ }
969
+
970
+ // ---------- Event Handlers ----------
971
+ function onToggleCheckin(id, cardEl = null) {
972
+ const set = listTodaySet();
973
+ if (set.has(id)) {
974
+ set.delete(id);
975
+ } else {
976
+ set.add(id);
977
+ }
978
+ setToday([...set]);
979
+
980
+ // Update the card UI quickly if provided
981
+ if (cardEl) {
982
+ const btn = cardEl.querySelector('.checkin-btn');
983
+ const pressed = set.has(id);
984
+ btn.setAttribute('aria-pressed', pressed ? 'true' : 'false');
985
+ btn.querySelector('span:last-child').textContent = pressed ? 'Checked-in' : 'Check in';
986
+ const pill = cardEl.querySelector('.pill');
987
+ pill.textContent = pressed ? 'Checked-in' : 'Available';
988
+ }
989
+
990
+ renderStats();
991
+ renderHistory();
992
+ renderClasses();
993
+ const a = ACTIVITIES.find(x => x.id === id);
994
+ showToast(`${a ? a.name : 'Activity'} ${set.has(id) ? 'checked-in' : 'removed'}.`);
995
+ }
996
+
997
+ // ---------- Toast ----------
998
+ let toastTimer = null;
999
+ function showToast(msg) {
1000
+ const t = document.getElementById('toast');
1001
+ t.textContent = msg;
1002
+ t.classList.add('show');
1003
+ clearTimeout(toastTimer);
1004
+ toastTimer = setTimeout(() => t.classList.remove('show'), 1800);
1005
+ }
1006
+
1007
+ // ---------- Renderers ----------
1008
+ function renderActivities() {
1009
+ const wrap = document.getElementById('activities');
1010
+ wrap.innerHTML = '';
1011
+ const q = (document.getElementById('search').value || '').trim().toLowerCase();
1012
+ const activeSet = listTodaySet();
1013
+ const items = ACTIVITIES.filter(a => !q || a.name.toLowerCase().includes(q) || a.id.includes(q));
1014
+ if (items.length === 0) {
1015
+ const empty = document.createElement('div');
1016
+ empty.className = 'empty';
1017
+ empty.textContent = 'No activities match your search.';
1018
+ wrap.appendChild(empty);
1019
+ return;
1020
+ }
1021
+ for (const a of items) {
1022
+ wrap.appendChild(activityCard(a, activeSet.has(a.id)));
1023
+ }
1024
+ }
1025
+
1026
+ function renderClasses() {
1027
+ const wrap = document.getElementById('classes');
1028
+ wrap.innerHTML = '';
1029
+ const activeSet = listTodaySet();
1030
+ for (const c of SCHEDULE) {
1031
+ const id = (() => {
1032
+ const lc = c.name.toLowerCase();
1033
+ if (lc.includes('crossfit')) return 'crossfit';
1034
+ if (lc.includes('spin')) return 'spin';
1035
+ if (lc.includes('yoga') || lc.includes('pilates')) return lc.includes('yoga') ? 'yoga' : 'pilates';
1036
+ if (lc.includes('hiit')) return 'hiit';
1037
+ return 'strength';
1038
+ })();
1039
+ wrap.appendChild(classItem(c, activeSet.has(id)));
1040
+ }
1041
+ }
1042
+
1043
+ function renderStats() {
1044
+ // Today count
1045
+ document.getElementById('today-count').textContent = countToday();
1046
+
1047
+ // Streak
1048
+ document.getElementById('streak').textContent = computeStreak();
1049
+
1050
+ // Month total
1051
+ document.getElementById('month-total').textContent = computeThisMonth();
1052
+
1053
+ // Weekly chart
1054
+ const { days, max } = last7DaysCounts();
1055
+ const bars = document.getElementById('bars');
1056
+ bars.innerHTML = '';
1057
+ let sum = 0;
1058
+ days.forEach(d => {
1059
+ sum += d.count;
1060
+ const bar = document.createElement('div');
1061
+ const pct = Math.round((d.count / max) * 100);
1062
+ bar.className = 'bar';
1063
+ bar.style.setProperty('--h', pct + '%');
1064
+ bar.setAttribute('data-count', d.count);
1065
+ bar.title = `${d.d.toLocaleDateString([], { weekday:'short'})}: ${d.count}`;
1066
+ bars.appendChild(bar);
1067
+ });
1068
+ document.getElementById('week-total').textContent = `${sum} check-ins`;
1069
+ }
1070
+
1071
+ function renderHistory() {
1072
+ const wrap = document.getElementById('history-list');
1073
+ wrap.innerHTML = '';
1074
+ // Get last 10 check-in events
1075
+ const events = [];
1076
+ for (const [k, arr] of Object.entries(state.data.checkInsByDate)) {
1077
+ const d = new Date(k + 'T12:00:00');
1078
+ for (const id of arr) {
1079
+ events.push({ date: d, key: k, id });
1080
+ }
1081
+ }
1082
+ events.sort((a,b) => b.date - a.date);
1083
+ const last = events.slice(0, 12);
1084
+ if (last.length === 0) {
1085
+ const empty = document.createElement('div');
1086
+ empty.className = 'empty';
1087
+ empty.textContent = 'No check-ins yet. Start by checking in to any activity.';
1088
+ wrap.appendChild(empty);
1089
+ return;
1090
+ }
1091
+ for (const ev of last) {
1092
+ const a = ACTIVITIES.find(x => x.id === ev.id);
1093
+ const item = document.createElement('article');
1094
+ item.className = 'history-item';
1095
+ item.innerHTML = `
1096
+ <div class="ico" style="width:36px;height:36px;border-radius:10px;font-size:18px;background: color-mix(in oklab, var(--brand), transparent 85%); border:1px solid color-mix(in oklab, var(--brand), transparent 75%);">${a?.icon || '🏋️'}</div>
1097
+ <div>
1098
+ <div style="font-weight:800;">${a?.name || ev.id}</div>
1099
+ <div class="muted" style="font-size:12px;">${ev.date.toLocaleDateString([], {weekday:'short', month:'short', day:'numeric'})} • ${formatTime(ev.date)}</div>
1100
+ </div>
1101
+ <span class="badge">Check-in</span>
1102
+ `;
1103
+ wrap.appendChild(item);
1104
+ }
1105
+ }
1106
+
1107
+ function renderView() {
1108
+ const homeBtn = document.querySelector('.tabbar [data-view="home"]');
1109
+ const histBtn = document.querySelector('.tabbar [data-view="history"]');
1110
+ const homeView = document.getElementById('view-home');
1111
+ const histView = document.getElementById('view-history');
1112
+ if (state.view === 'home') {
1113
+ homeBtn.classList.add('active'); homeBtn.setAttribute('aria-selected','true');
1114
+ histBtn.classList.remove('active'); histBtn.setAttribute('aria-selected','false');
1115
+ homeView.hidden = false;
1116
+ histView.hidden = true;
1117
+ } else {
1118
+ histBtn.classList.add('active'); histBtn.setAttribute('aria-selected','true');
1119
+ homeBtn.classList.remove('active'); homeBtn.setAttribute('aria-selected','false');
1120
+ homeView.hidden = true;
1121
+ histView.hidden = false;
1122
+ }
1123
+ }
1124
+
1125
+ function renderAll() {
1126
+ renderActivities();
1127
+ renderClasses();
1128
+ renderStats();
1129
+ renderHistory();
1130
+ renderView();
1131
+ }
1132
+
1133
+ // ---------- Events ----------
1134
+ function bindEvents() {
1135
+ document.querySelector('.tabbar').addEventListener('click', (e) => {
1136
+ const btn = e.target.closest('button[data-view]');
1137
+ if (!btn) return;
1138
+ state.view = btn.dataset.view;
1139
+ renderView();
1140
+ });
1141
+
1142
+ // Search
1143
+ let searchDebounce = null;
1144
+ document.getElementById('search').addEventListener('input', (e) => {
1145
+ clearTimeout(searchDebounce);
1146
+ searchDebounce = setTimeout(() => renderActivities(), 120);
1147
+ });
1148
+
1149
+ // Resize: ensure bars heights transition nicely (optional)
1150
+ window.addEventListener('resize', () => renderStats());
1151
+ }
1152
+
1153
+ // ---------- Init ----------
1154
+ (function init() {
1155
+ document.getElementById('year').textContent = new Date().getFullYear();
1156
+ bindEvents();
1157
+ renderAll();
1158
+ })();
1159
+ </script>
1160
+ </body>
1161
+
1162
+ </html>