@jjlmoya/utils-sports 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/package.json +62 -0
  2. package/src/category/i18n/en.ts +108 -0
  3. package/src/category/i18n/es.ts +108 -0
  4. package/src/category/i18n/fr.ts +95 -0
  5. package/src/category/index.ts +21 -0
  6. package/src/category/seo.astro +15 -0
  7. package/src/components/PreviewNavSidebar.astro +116 -0
  8. package/src/components/PreviewToolbar.astro +143 -0
  9. package/src/data.ts +11 -0
  10. package/src/env.d.ts +5 -0
  11. package/src/index.ts +55 -0
  12. package/src/layouts/PreviewLayout.astro +117 -0
  13. package/src/pages/[locale]/[slug].astro +146 -0
  14. package/src/pages/[locale].astro +251 -0
  15. package/src/pages/index.astro +4 -0
  16. package/src/tests/faq_count.test.ts +19 -0
  17. package/src/tests/locale_completeness.test.ts +42 -0
  18. package/src/tests/mocks/astro_mock.js +2 -0
  19. package/src/tests/no_h1_in_components.test.ts +48 -0
  20. package/src/tests/schemas_fulfillment.test.ts +23 -0
  21. package/src/tests/seo_length.test.ts +22 -0
  22. package/src/tests/title_quality.test.ts +55 -0
  23. package/src/tests/tool_validation.test.ts +17 -0
  24. package/src/tool/gymTracker/bibliography.astro +15 -0
  25. package/src/tool/gymTracker/component.astro +835 -0
  26. package/src/tool/gymTracker/exercises.ts +28 -0
  27. package/src/tool/gymTracker/i18n/en.ts +225 -0
  28. package/src/tool/gymTracker/i18n/es.ts +225 -0
  29. package/src/tool/gymTracker/i18n/fr.ts +225 -0
  30. package/src/tool/gymTracker/index.ts +34 -0
  31. package/src/tool/gymTracker/logic.ts +169 -0
  32. package/src/tool/gymTracker/seo.astro +15 -0
  33. package/src/tool/gymTracker/storage.ts +43 -0
  34. package/src/tool/gymTracker/timer.ts +126 -0
  35. package/src/tool/gymTracker/types.ts +11 -0
  36. package/src/tool/gymTracker/ui-utils.ts +59 -0
  37. package/src/tool/gymTracker/ui.ts +27 -0
  38. package/src/tool/reactionTester/bibliography.astro +2 -0
  39. package/src/tool/reactionTester/component.astro +1074 -0
  40. package/src/tool/reactionTester/i18n/en.ts +144 -0
  41. package/src/tool/reactionTester/i18n/es.ts +144 -0
  42. package/src/tool/reactionTester/i18n/fr.ts +144 -0
  43. package/src/tool/reactionTester/index.ts +34 -0
  44. package/src/tool/reactionTester/seo.astro +12 -0
  45. package/src/tool/reactionTester/ui.ts +43 -0
  46. package/src/tool/scoreKeeper/bibliography.astro +14 -0
  47. package/src/tool/scoreKeeper/component.astro +858 -0
  48. package/src/tool/scoreKeeper/i18n/en.ts +207 -0
  49. package/src/tool/scoreKeeper/i18n/es.ts +207 -0
  50. package/src/tool/scoreKeeper/i18n/fr.ts +207 -0
  51. package/src/tool/scoreKeeper/index.ts +35 -0
  52. package/src/tool/scoreKeeper/logic.ts +275 -0
  53. package/src/tool/scoreKeeper/seo.astro +15 -0
  54. package/src/tool/scoreKeeper/sports.ts +70 -0
  55. package/src/tool/scoreKeeper/ui.ts +19 -0
  56. package/src/tool/tournamentBracket/bibliography.astro +10 -0
  57. package/src/tool/tournamentBracket/component.astro +1092 -0
  58. package/src/tool/tournamentBracket/i18n/en.ts +160 -0
  59. package/src/tool/tournamentBracket/i18n/es.ts +178 -0
  60. package/src/tool/tournamentBracket/i18n/fr.ts +160 -0
  61. package/src/tool/tournamentBracket/index.ts +34 -0
  62. package/src/tool/tournamentBracket/logic/active.controller.ts +106 -0
  63. package/src/tool/tournamentBracket/logic/generator.ts +71 -0
  64. package/src/tool/tournamentBracket/logic/manager.ts +165 -0
  65. package/src/tool/tournamentBracket/logic/setup.controller.ts +84 -0
  66. package/src/tool/tournamentBracket/logic/sharing.ts +81 -0
  67. package/src/tool/tournamentBracket/logic/storage.ts +56 -0
  68. package/src/tool/tournamentBracket/models.ts +34 -0
  69. package/src/tool/tournamentBracket/seo.astro +12 -0
  70. package/src/tool/tournamentBracket/tournament.controller.ts +65 -0
  71. package/src/tool/tournamentBracket/tournament.renderer.ts +45 -0
  72. package/src/tool/tournamentBracket/ui/bracket-desktop.ts +143 -0
  73. package/src/tool/tournamentBracket/ui/bracket-mobile.ts +82 -0
  74. package/src/tool/tournamentBracket/ui/mediator.ts +96 -0
  75. package/src/tool/tournamentBracket/ui/navigator.ts +84 -0
  76. package/src/tool/tournamentBracket/ui/setup.ts +120 -0
  77. package/src/tool/tournamentBracket/ui.ts +42 -0
  78. package/src/tools.ts +13 -0
  79. package/src/types.ts +72 -0
@@ -0,0 +1,1092 @@
1
+ ---
2
+ import type { TournamentBracketUI } from './ui';
3
+ interface Props { ui: TournamentBracketUI }
4
+ const { ui: t } = Astro.props;
5
+ ---
6
+
7
+ <div id="tb-app" class="tb-app" data-tb-ui={JSON.stringify(t)}>
8
+ <div id="active-controls" class="tb-active-controls tb-hidden">
9
+ <div class="tb-tournament-header">
10
+ <h2 id="tournament-title-display" class="tb-tournament-title">{t.tournamentInProgress}</h2>
11
+ <span id="tournament-date-display" class="tb-tournament-date"></span>
12
+ </div>
13
+ <div class="tb-controls-actions">
14
+ <button id="next-match-btn" class="tb-btn-next-match">
15
+ <span>{t.nextMatch}</span>
16
+ <svg xmlns="http://www.w3.org/2000/svg" class="tb-icon-md" viewBox="0 0 24 24" fill="currentColor"><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"/></svg>
17
+ </button>
18
+ <button id="share-tournament-btn" class="tb-btn-share">
19
+ <svg xmlns="http://www.w3.org/2000/svg" class="tb-icon-md" viewBox="0 0 24 24" fill="currentColor"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92-1.31-2.92-2.92-2.92z"/></svg>
20
+ <span class="tb-share-label">{t.share}</span>
21
+ </button>
22
+ <button id="reset-btn" class="tb-btn-back">
23
+ <svg xmlns="http://www.w3.org/2000/svg" class="tb-icon-md" viewBox="0 0 24 24" fill="currentColor"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></svg>
24
+ <span class="tb-back-full">{t.backNew}</span>
25
+ <span class="tb-back-short">{t.back}</span>
26
+ </button>
27
+ </div>
28
+ </div>
29
+
30
+ <div id="setup-view" class="tb-setup-view">
31
+ <div class="tb-setup-grid">
32
+ <div class="tb-setup-card">
33
+ <div class="tb-setup-card-header">
34
+ <h2 class="tb-setup-title">{t.newTournament}</h2>
35
+ <p class="tb-setup-subtitle">{t.setupSubtitle}</p>
36
+ </div>
37
+ <div class="tb-field">
38
+ <label class="tb-label">{t.tournamentNameLabel}</label>
39
+ <input type="text" id="tournament-name-input" placeholder={t.tournamentNamePlaceholder} class="tb-input" />
40
+ </div>
41
+ <div class="tb-field">
42
+ <label class="tb-label">{t.addPlayersLabel}</label>
43
+ <div class="tb-input-row">
44
+ <input type="text" id="new-player-input" placeholder={t.addPlayerPlaceholder} class="tb-input" />
45
+ <button id="add-player-btn" class="tb-btn-add" aria-label={t.addPlayersLabel}>
46
+ <svg xmlns="http://www.w3.org/2000/svg" class="tb-icon-lg" viewBox="0 0 24 24" fill="currentColor"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
47
+ </button>
48
+ </div>
49
+ </div>
50
+ <div class="tb-field">
51
+ <div class="tb-players-header">
52
+ <span class="tb-label">{t.playersLabel} (<span id="player-count">0</span>)</span>
53
+ <button id="clear-players-btn" class="tb-btn-clear tb-hidden">{t.clearAll}</button>
54
+ </div>
55
+ <ul id="player-list" class="tb-player-list">
56
+ <li class="tb-player-empty">{t.emptyList}</li>
57
+ </ul>
58
+ </div>
59
+ <div id="shuffle-wrapper"></div>
60
+ <button id="generate-btn" class="tb-btn-generate tb-btn-disabled" disabled>
61
+ <svg xmlns="http://www.w3.org/2000/svg" class="tb-icon-md" viewBox="0 0 24 24" fill="currentColor"><path d="M2 2v2h5v4H2v2h5c1.11 0 2-.89 2-2V7h5v10H9v-1c0-1.11-.89-2-2-2H2v2h5v4H2v2h5c1.11 0 2-.89 2-2v-1h5c1.11 0 2-.89 2-2v-4h6v-2h-6V7c0-1.11-.89-2-2-2H9V4c0-1.11-.89-2-2-2z"/></svg>
62
+ {t.generateBtn}
63
+ </button>
64
+ </div>
65
+
66
+ <div class="tb-setup-side">
67
+ <div class="tb-how-card">
68
+ <h3 class="tb-how-title">{t.howItWorks}</h3>
69
+ <p class="tb-how-text">{t.howItWorksText}</p>
70
+ </div>
71
+ <div class="tb-history-card">
72
+ <h3 class="tb-history-title">
73
+ <svg xmlns="http://www.w3.org/2000/svg" class="tb-icon-sm" viewBox="0 0 24 24" fill="currentColor"><path d="M13,3A9,9 0 0,0 4,12H1L4.89,15.89L4.96,16.03L9,12H6A7,7 0 0,1 13,5A7,7 0 0,1 20,12A7,7 0 0,1 13,19C11.07,19 9.32,18.21 8.06,16.94L6.64,18.36C8.27,19.99 10.51,21 13,21A9,9 0 0,0 22,12A9,9 0 0,0 13,3Z"/></svg>
74
+ {t.historyLabel}
75
+ </h3>
76
+ <div id="history-container" class="tb-history-container">
77
+ <div class="tb-history-empty">{t.noHistory}</div>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </div>
83
+
84
+ <div id="bracket-view" class="tb-bracket-view tb-hidden">
85
+ <div class="tb-mobile-bracket">
86
+ <div class="bracket-mobile"></div>
87
+ </div>
88
+ <div class="desktop-bracket-container tb-desktop-bracket tb-cursor-grab">
89
+ <div class="tb-drag-hint">
90
+ <svg xmlns="http://www.w3.org/2000/svg" class="tb-icon-sm" viewBox="0 0 24 24" fill="currentColor"><path d="M9 3c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2m6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2M9 9c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2m6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2m-3 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2m6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2"/></svg>
91
+ {t.dragHint}
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+
97
+ <script>
98
+ import { TournamentController } from './tournament.controller';
99
+ document.addEventListener('DOMContentLoaded', () => new TournamentController());
100
+ </script>
101
+
102
+ <style>
103
+ .tb-app {
104
+ display: flex;
105
+ flex-direction: column;
106
+ gap: 1.5rem;
107
+ }
108
+
109
+ :global(#tb-app .tb-hidden) {
110
+ display: none;
111
+ }
112
+
113
+ .tb-active-controls {
114
+ display: flex;
115
+ justify-content: space-between;
116
+ align-items: center;
117
+ gap: 1rem;
118
+ flex-wrap: wrap;
119
+ }
120
+
121
+ .tb-tournament-header {
122
+ flex: 1;
123
+ min-width: 0;
124
+ }
125
+
126
+ .tb-tournament-title {
127
+ font-size: 1.25rem;
128
+ font-weight: 700;
129
+ color: var(--text-base);
130
+ margin: 0;
131
+ white-space: nowrap;
132
+ overflow: hidden;
133
+ text-overflow: ellipsis;
134
+ }
135
+
136
+ .tb-tournament-date {
137
+ font-size: 0.75rem;
138
+ color: var(--text-muted);
139
+ }
140
+
141
+ :global(#tb-app .tb-title-input) {
142
+ background: transparent;
143
+ border: none;
144
+ border-bottom: 2px solid var(--accent);
145
+ color: var(--text-base);
146
+ font-weight: 700;
147
+ font-size: 1.25rem;
148
+ outline: none;
149
+ min-width: 200px;
150
+ }
151
+
152
+ :global(#tb-app .tb-edit-icon) {
153
+ display: inline-block;
154
+ width: 0.9em;
155
+ height: 0.9em;
156
+ background: currentcolor;
157
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1 1 0 0 0 0-1.41l-2.34-2.34a1 1 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z'/%3E%3C/svg%3E");
158
+ mask-size: contain;
159
+ opacity: 0.5;
160
+ cursor: pointer;
161
+ vertical-align: middle;
162
+ margin-left: 0.5rem;
163
+ }
164
+
165
+ .tb-controls-actions {
166
+ display: flex;
167
+ align-items: center;
168
+ gap: 0.75rem;
169
+ flex-wrap: wrap;
170
+ }
171
+
172
+ .tb-btn-next-match {
173
+ display: none;
174
+ align-items: center;
175
+ gap: 0.5rem;
176
+ background: var(--accent);
177
+ color: white;
178
+ font-size: 0.875rem;
179
+ font-weight: 700;
180
+ padding: 0.5rem 1rem;
181
+ border: none;
182
+ border-radius: 0.5rem;
183
+ cursor: pointer;
184
+ }
185
+
186
+ @media (min-width: 768px) {
187
+ .tb-btn-next-match {
188
+ display: flex;
189
+ }
190
+ }
191
+
192
+ .tb-btn-share {
193
+ display: flex;
194
+ align-items: center;
195
+ gap: 0.5rem;
196
+ background: #059669;
197
+ color: white;
198
+ font-size: 0.875rem;
199
+ font-weight: 700;
200
+ padding: 0.5rem 0.75rem;
201
+ border: none;
202
+ border-radius: 0.5rem;
203
+ cursor: pointer;
204
+ }
205
+
206
+ .tb-share-label {
207
+ display: none;
208
+ }
209
+
210
+ @media (min-width: 768px) {
211
+ .tb-share-label {
212
+ display: inline;
213
+ }
214
+ }
215
+
216
+ .tb-btn-back {
217
+ display: flex;
218
+ align-items: center;
219
+ gap: 0.25rem;
220
+ background: var(--bg-surface);
221
+ border: 1px solid var(--border-color);
222
+ color: var(--text-muted);
223
+ font-size: 0.875rem;
224
+ font-weight: 700;
225
+ padding: 0.375rem 0.75rem;
226
+ border-radius: 0.5rem;
227
+ cursor: pointer;
228
+ white-space: nowrap;
229
+ }
230
+
231
+ .tb-btn-back:hover {
232
+ color: var(--accent);
233
+ border-color: var(--accent);
234
+ }
235
+
236
+ .tb-back-short {
237
+ display: inline;
238
+ }
239
+
240
+ .tb-back-full {
241
+ display: none;
242
+ }
243
+
244
+ @media (min-width: 640px) {
245
+ .tb-back-short {
246
+ display: none;
247
+ }
248
+
249
+ .tb-back-full {
250
+ display: inline;
251
+ }
252
+ }
253
+
254
+ .tb-setup-view {
255
+ display: flex;
256
+ }
257
+
258
+ .tb-setup-grid {
259
+ display: grid;
260
+ grid-template-columns: 1fr;
261
+ gap: 2rem;
262
+ width: 100%;
263
+ }
264
+
265
+ @media (min-width: 768px) {
266
+ .tb-setup-grid {
267
+ grid-template-columns: 1fr 1fr;
268
+ align-items: start;
269
+ }
270
+ }
271
+
272
+ .tb-setup-card {
273
+ background: var(--bg-surface);
274
+ border: 1px solid var(--border-color);
275
+ border-radius: 1.5rem;
276
+ padding: 2rem;
277
+ display: flex;
278
+ flex-direction: column;
279
+ gap: 1.25rem;
280
+ }
281
+
282
+ .tb-setup-card-header {
283
+ text-align: center;
284
+ }
285
+
286
+ .tb-setup-title {
287
+ font-size: 1.5rem;
288
+ font-weight: 900;
289
+ color: var(--text-base);
290
+ margin: 0 0 0.5rem;
291
+ }
292
+
293
+ .tb-setup-subtitle {
294
+ color: var(--text-muted);
295
+ font-size: 0.875rem;
296
+ margin: 0;
297
+ }
298
+
299
+ .tb-field {
300
+ display: flex;
301
+ flex-direction: column;
302
+ gap: 0.5rem;
303
+ }
304
+
305
+ .tb-label {
306
+ font-size: 0.75rem;
307
+ font-weight: 700;
308
+ color: var(--text-muted);
309
+ text-transform: uppercase;
310
+ letter-spacing: 0.05em;
311
+ }
312
+
313
+ .tb-input {
314
+ background: var(--bg-page);
315
+ border: 1px solid var(--border-color);
316
+ border-radius: 0.75rem;
317
+ padding: 0.75rem 1rem;
318
+ color: var(--text-base);
319
+ font-size: 1rem;
320
+ outline: none;
321
+ width: 100%;
322
+ box-sizing: border-box;
323
+ }
324
+
325
+ .tb-input:focus {
326
+ border-color: var(--accent);
327
+ box-shadow: 0 0 0 2px rgba(244, 63, 94, 0.15);
328
+ }
329
+
330
+ .tb-input-row {
331
+ display: flex;
332
+ gap: 0.5rem;
333
+ }
334
+
335
+ .tb-input-row .tb-input {
336
+ flex: 1;
337
+ }
338
+
339
+ .tb-btn-add {
340
+ background: var(--accent);
341
+ color: white;
342
+ border: none;
343
+ border-radius: 0.75rem;
344
+ padding: 0.75rem;
345
+ cursor: pointer;
346
+ display: flex;
347
+ align-items: center;
348
+ justify-content: center;
349
+ flex-shrink: 0;
350
+ }
351
+
352
+ .tb-btn-add:active {
353
+ transform: scale(0.95);
354
+ }
355
+
356
+ .tb-players-header {
357
+ display: flex;
358
+ justify-content: space-between;
359
+ align-items: center;
360
+ }
361
+
362
+ .tb-btn-clear {
363
+ background: none;
364
+ border: none;
365
+ color: #ef4444;
366
+ font-size: 0.7rem;
367
+ cursor: pointer;
368
+ padding: 0;
369
+ }
370
+
371
+ .tb-btn-clear:hover {
372
+ color: #dc2626;
373
+ }
374
+
375
+ .tb-player-list {
376
+ list-style: none;
377
+ margin: 0;
378
+ padding: 0;
379
+ display: flex;
380
+ flex-direction: column;
381
+ gap: 0.5rem;
382
+ max-height: 200px;
383
+ overflow-y: auto;
384
+ }
385
+
386
+ :global(#tb-app .tb-player-item) {
387
+ display: flex;
388
+ justify-content: space-between;
389
+ align-items: center;
390
+ background: var(--bg-page);
391
+ border: 1px solid var(--border-color);
392
+ border-radius: 0.75rem;
393
+ padding: 0.75rem 1rem;
394
+ }
395
+
396
+ :global(#tb-app .tb-player-item:hover) {
397
+ border-color: var(--accent);
398
+ }
399
+
400
+ :global(#tb-app .tb-player-name) {
401
+ font-weight: 500;
402
+ font-size: 0.875rem;
403
+ color: var(--text-base);
404
+ overflow: hidden;
405
+ text-overflow: ellipsis;
406
+ white-space: nowrap;
407
+ flex: 1;
408
+ padding-right: 1rem;
409
+ }
410
+
411
+ :global(#tb-app .tb-player-empty) {
412
+ text-align: center;
413
+ color: var(--text-muted);
414
+ font-style: italic;
415
+ font-size: 0.875rem;
416
+ padding: 1rem;
417
+ border: 2px dashed var(--border-color);
418
+ border-radius: 0.75rem;
419
+ }
420
+
421
+ :global(#tb-app .tb-player-remove) {
422
+ background: none;
423
+ border: none;
424
+ color: var(--text-muted);
425
+ cursor: pointer;
426
+ padding: 0.25rem;
427
+ display: flex;
428
+ }
429
+
430
+ :global(#tb-app .tb-player-remove:hover) {
431
+ color: #ef4444;
432
+ }
433
+
434
+ :global(#tb-app .tb-toggle-group) {
435
+ display: flex;
436
+ flex-direction: column;
437
+ gap: 0.5rem;
438
+ }
439
+
440
+ :global(#tb-app .tb-toggle-label) {
441
+ display: flex;
442
+ align-items: center;
443
+ gap: 0.75rem;
444
+ cursor: pointer;
445
+ padding: 0.5rem;
446
+ border-radius: 0.5rem;
447
+ }
448
+
449
+ :global(#tb-app .tb-toggle-label:hover) {
450
+ background: var(--bg-page);
451
+ }
452
+
453
+ :global(#tb-app .tb-toggle) {
454
+ position: relative;
455
+ width: 2.5rem;
456
+ height: 1.5rem;
457
+ flex-shrink: 0;
458
+ }
459
+
460
+ :global(#tb-app .tb-toggle-input) {
461
+ position: absolute;
462
+ width: 1px;
463
+ height: 1px;
464
+ opacity: 0;
465
+ }
466
+
467
+ :global(#tb-app .tb-toggle-track) {
468
+ position: absolute;
469
+ inset: 0;
470
+ background: var(--border-color);
471
+ border-radius: 9999px;
472
+ transition: background 0.2s;
473
+ }
474
+
475
+ :global(#tb-app .tb-toggle-input:checked + .tb-toggle-track) {
476
+ background: var(--accent);
477
+ }
478
+
479
+ :global(#tb-app .tb-toggle-thumb) {
480
+ position: absolute;
481
+ top: 0.25rem;
482
+ left: 0.25rem;
483
+ width: 1rem;
484
+ height: 1rem;
485
+ background: white;
486
+ border-radius: 50%;
487
+ transition: transform 0.2s;
488
+ }
489
+
490
+ :global(#tb-app .tb-toggle-input:checked ~ .tb-toggle-thumb) {
491
+ transform: translateX(1rem);
492
+ }
493
+
494
+ :global(#tb-app .tb-toggle-text) {
495
+ font-size: 0.875rem;
496
+ font-weight: 500;
497
+ color: var(--text-base);
498
+ }
499
+
500
+ .tb-btn-generate {
501
+ width: 100%;
502
+ background: var(--accent);
503
+ color: white;
504
+ border: none;
505
+ border-radius: 0.75rem;
506
+ padding: 1rem;
507
+ font-size: 1.125rem;
508
+ font-weight: 700;
509
+ cursor: pointer;
510
+ display: flex;
511
+ align-items: center;
512
+ justify-content: center;
513
+ gap: 0.5rem;
514
+ transition: opacity 0.2s;
515
+ }
516
+
517
+ .tb-btn-generate:hover:not(.tb-btn-disabled) {
518
+ opacity: 0.9;
519
+ transform: scale(1.02);
520
+ }
521
+
522
+ .tb-btn-generate:active:not(.tb-btn-disabled) {
523
+ transform: scale(0.98);
524
+ }
525
+
526
+ :global(#tb-app .tb-btn-disabled) {
527
+ opacity: 0.5;
528
+ cursor: not-allowed;
529
+ }
530
+
531
+ .tb-setup-side {
532
+ display: flex;
533
+ flex-direction: column;
534
+ gap: 1.5rem;
535
+ }
536
+
537
+ .tb-how-card {
538
+ background: #1e1b4b;
539
+ color: white;
540
+ border-radius: 1.5rem;
541
+ padding: 1.5rem;
542
+ }
543
+
544
+ .tb-how-title {
545
+ font-weight: 700;
546
+ font-size: 1.125rem;
547
+ margin: 0 0 0.5rem;
548
+ }
549
+
550
+ .tb-how-text {
551
+ color: rgba(199, 210, 254, 0.9);
552
+ font-size: 0.875rem;
553
+ line-height: 1.6;
554
+ margin: 0;
555
+ }
556
+
557
+ .tb-history-card {
558
+ background: var(--bg-surface);
559
+ border: 1px solid var(--border-color);
560
+ border-radius: 1.5rem;
561
+ padding: 1.5rem;
562
+ flex: 1;
563
+ }
564
+
565
+ .tb-history-title {
566
+ font-size: 0.875rem;
567
+ font-weight: 700;
568
+ color: var(--text-muted);
569
+ text-transform: uppercase;
570
+ letter-spacing: 0.05em;
571
+ margin: 0 0 1rem;
572
+ display: flex;
573
+ align-items: center;
574
+ gap: 0.5rem;
575
+ }
576
+
577
+ .tb-history-container {
578
+ display: flex;
579
+ flex-direction: column;
580
+ gap: 0.75rem;
581
+ max-height: 300px;
582
+ overflow-y: auto;
583
+ }
584
+
585
+ :global(#tb-app .tb-history-empty) {
586
+ text-align: center;
587
+ color: var(--text-muted);
588
+ font-size: 0.875rem;
589
+ padding: 2rem;
590
+ font-style: italic;
591
+ }
592
+
593
+ :global(#tb-app .tb-history-item) {
594
+ display: flex;
595
+ align-items: center;
596
+ justify-content: space-between;
597
+ padding: 0.75rem;
598
+ background: var(--bg-page);
599
+ border: 1px solid var(--border-color);
600
+ border-radius: 0.75rem;
601
+ }
602
+
603
+ :global(#tb-app .tb-history-item:hover) {
604
+ border-color: var(--accent);
605
+ background: rgba(244, 63, 94, 0.03);
606
+ }
607
+
608
+ :global(#tb-app .tb-history-load) {
609
+ background: none;
610
+ border: none;
611
+ text-align: left;
612
+ flex: 1;
613
+ cursor: pointer;
614
+ display: flex;
615
+ flex-direction: column;
616
+ gap: 0.25rem;
617
+ overflow: hidden;
618
+ padding: 0;
619
+ }
620
+
621
+ :global(#tb-app .tb-history-name) {
622
+ font-weight: 700;
623
+ color: var(--text-base);
624
+ font-size: 0.875rem;
625
+ overflow: hidden;
626
+ text-overflow: ellipsis;
627
+ white-space: nowrap;
628
+ display: block;
629
+ }
630
+
631
+ :global(#tb-app .tb-history-date) {
632
+ font-size: 0.625rem;
633
+ color: var(--text-muted);
634
+ display: flex;
635
+ align-items: center;
636
+ gap: 0.25rem;
637
+ flex-wrap: wrap;
638
+ }
639
+
640
+ :global(#tb-app .tb-history-badge-winner) {
641
+ color: #16a34a;
642
+ font-weight: 700;
643
+ font-size: 0.625rem;
644
+ background: rgba(22, 163, 74, 0.1);
645
+ padding: 0.125rem 0.375rem;
646
+ border-radius: 9999px;
647
+ display: inline-flex;
648
+ align-items: center;
649
+ gap: 0.25rem;
650
+ }
651
+
652
+ :global(#tb-app .tb-history-badge-active) {
653
+ color: #f97316;
654
+ }
655
+
656
+ :global(#tb-app .tb-history-delete) {
657
+ background: rgba(239, 68, 68, 0.1);
658
+ border: none;
659
+ color: #ef4444;
660
+ cursor: pointer;
661
+ padding: 0.5rem;
662
+ border-radius: 0.5rem;
663
+ display: flex;
664
+ margin-left: 0.5rem;
665
+ }
666
+
667
+ :global(#tb-app .tb-history-delete:hover) {
668
+ background: rgba(239, 68, 68, 0.2);
669
+ }
670
+
671
+ .tb-bracket-view {
672
+ display: flex;
673
+ flex-direction: column;
674
+ flex: 1;
675
+ position: relative;
676
+ }
677
+
678
+ .tb-mobile-bracket {
679
+ display: block;
680
+ }
681
+
682
+ @media (min-width: 768px) {
683
+ .tb-mobile-bracket {
684
+ display: none;
685
+ }
686
+ }
687
+
688
+ .tb-desktop-bracket {
689
+ display: none;
690
+ flex: 1;
691
+ min-height: 600px;
692
+ max-height: 80vh;
693
+ background: var(--bg-surface);
694
+ border: 1px solid var(--border-color);
695
+ border-radius: 1.5rem;
696
+ position: relative;
697
+ overflow: auto;
698
+ }
699
+
700
+ @media (min-width: 768px) {
701
+ .tb-desktop-bracket {
702
+ display: block;
703
+ }
704
+ }
705
+
706
+ .tb-cursor-grab {
707
+ cursor: grab;
708
+ }
709
+
710
+ :global(#tb-app .tb-cursor-grabbing) {
711
+ cursor: grabbing;
712
+ }
713
+
714
+ .tb-drag-hint {
715
+ position: absolute;
716
+ bottom: 1rem;
717
+ right: 2rem;
718
+ background: rgba(255, 255, 255, 0.8);
719
+ backdrop-filter: blur(4px);
720
+ padding: 0.25rem 0.75rem;
721
+ border-radius: 9999px;
722
+ font-size: 0.75rem;
723
+ color: var(--text-muted);
724
+ border: 1px solid var(--border-color);
725
+ pointer-events: none;
726
+ display: flex;
727
+ align-items: center;
728
+ gap: 0.25rem;
729
+ }
730
+
731
+ :global(#tb-app .tb-tabs) {
732
+ display: flex;
733
+ overflow-x: auto;
734
+ gap: 0.5rem;
735
+ padding: 0.25rem;
736
+ position: sticky;
737
+ top: 0;
738
+ background: rgba(248, 250, 252, 0.95);
739
+ backdrop-filter: blur(4px);
740
+ z-index: 20;
741
+ margin-bottom: 1rem;
742
+ }
743
+
744
+ :global(#tb-app .tb-round-tab) {
745
+ white-space: nowrap;
746
+ padding: 0.5rem 1rem;
747
+ border-radius: 9999px;
748
+ font-size: 0.75rem;
749
+ font-weight: 700;
750
+ text-transform: uppercase;
751
+ letter-spacing: 0.05em;
752
+ background: var(--bg-surface);
753
+ color: var(--text-muted);
754
+ border: 1px solid var(--border-color);
755
+ cursor: pointer;
756
+ }
757
+
758
+ :global(#tb-app .tb-round-tab-active) {
759
+ background: var(--accent);
760
+ color: white;
761
+ border-color: var(--accent);
762
+ }
763
+
764
+ :global(#tb-app .tb-rounds) {
765
+ position: relative;
766
+ min-height: 500px;
767
+ margin-top: 1rem;
768
+ }
769
+
770
+ :global(#tb-app .tb-round-content) {
771
+ width: 100%;
772
+ transition: all 0.3s;
773
+ position: absolute;
774
+ inset: 0;
775
+ opacity: 0;
776
+ pointer-events: none;
777
+ transform: translateX(2rem);
778
+ display: flex;
779
+ flex-direction: column;
780
+ gap: 1rem;
781
+ padding-bottom: 2rem;
782
+ }
783
+
784
+ :global(#tb-app .tb-round-active) {
785
+ opacity: 1;
786
+ pointer-events: auto;
787
+ transform: translateX(0);
788
+ position: relative;
789
+ }
790
+
791
+ :global(#tb-app .tb-round-empty) {
792
+ text-align: center;
793
+ padding: 2rem;
794
+ color: var(--text-muted);
795
+ font-style: italic;
796
+ }
797
+
798
+ :global(#tb-app .tb-match-card) {
799
+ background: var(--bg-surface);
800
+ border: 1px solid var(--border-color);
801
+ border-radius: 0.75rem;
802
+ overflow: hidden;
803
+ position: relative;
804
+ width: 100%;
805
+ display: flex;
806
+ flex-direction: column;
807
+ justify-content: center;
808
+ }
809
+
810
+ :global(#tb-app .tb-match-card-bye) {
811
+ background: var(--bg-surface);
812
+ border: 1px solid var(--border-color);
813
+ border-radius: 0.75rem;
814
+ padding: 0.75rem 1rem;
815
+ position: relative;
816
+ }
817
+
818
+ :global(#tb-app .tb-match-card::before),
819
+ :global(#tb-app .tb-match-card-bye::before) {
820
+ content: '';
821
+ position: absolute;
822
+ left: 0;
823
+ top: 0;
824
+ bottom: 0;
825
+ width: 3px;
826
+ }
827
+
828
+ :global(#tb-app .tb-match-card::before) {
829
+ background: var(--border-color);
830
+ }
831
+
832
+ :global(#tb-app .tb-match-card-bye::before) {
833
+ background: var(--accent);
834
+ }
835
+
836
+ :global(#tb-app .tb-match-row) {
837
+ display: flex;
838
+ align-items: center;
839
+ gap: 0.5rem;
840
+ padding: 0.5rem 0.75rem;
841
+ }
842
+
843
+ :global(#tb-app .tb-match-divider) {
844
+ height: 1px;
845
+ background: var(--border-color);
846
+ margin: 0 0.75rem;
847
+ }
848
+
849
+ :global(#tb-app .tb-match-btn) {
850
+ flex: 1;
851
+ display: flex;
852
+ align-items: center;
853
+ justify-content: space-between;
854
+ padding: 0.5rem 0.75rem;
855
+ border-radius: 0.5rem;
856
+ border: none;
857
+ background: none;
858
+ cursor: pointer;
859
+ text-align: left;
860
+ color: var(--text-base);
861
+ }
862
+
863
+ :global(#tb-app .tb-match-btn:hover:not([disabled])) {
864
+ background: var(--bg-page);
865
+ }
866
+
867
+ :global(#tb-app .tb-match-btn[disabled]) {
868
+ cursor: default;
869
+ }
870
+
871
+ :global(#tb-app .tb-match-btn-winner) {
872
+ background: rgba(99, 102, 241, 0.1);
873
+ font-weight: 700;
874
+ color: #4338ca;
875
+ box-shadow: inset 0 0 0 1px rgba(99, 102, 241, 0.2);
876
+ }
877
+
878
+ :global(#tb-app .tb-player-label) {
879
+ font-size: 0.875rem;
880
+ overflow: hidden;
881
+ text-overflow: ellipsis;
882
+ white-space: nowrap;
883
+ }
884
+
885
+ :global(#tb-app .tb-player-empty-label) {
886
+ font-size: 0.875rem;
887
+ color: var(--text-muted);
888
+ font-style: italic;
889
+ }
890
+
891
+ :global(#tb-app .tb-winner-icon) {
892
+ display: inline-block;
893
+ width: 1rem;
894
+ height: 1rem;
895
+ flex-shrink: 0;
896
+ background: #4338ca;
897
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20.2,2H19.5H18C17.1,2 16,3 16,4H8C8,3 6.9,2 6,2H4.5H3.8C2.8,2 2,2.8 2,3.8V4.5C2,8.9 5.6,12.5 10,12.5V15H7V17H17V15H14V12.5C18.4,12.5 22,8.9 22,4.5V3.8C22,2.8 21.2,2 20.2,2Z'/%3E%3C/svg%3E");
898
+ mask-size: contain;
899
+ }
900
+
901
+ :global(#tb-app .tb-score-input) {
902
+ width: 3rem;
903
+ height: 2.25rem;
904
+ text-align: center;
905
+ border: 1px solid var(--border-color);
906
+ border-radius: 0.5rem;
907
+ font-size: 0.875rem;
908
+ font-weight: 700;
909
+ background: var(--bg-page);
910
+ color: var(--text-base);
911
+ flex-shrink: 0;
912
+ }
913
+
914
+ :global(#tb-app .tb-score-input:focus) {
915
+ border-color: var(--accent);
916
+ outline: none;
917
+ }
918
+
919
+ :global(#tb-app .tb-bye-label) {
920
+ display: block;
921
+ font-size: 0.625rem;
922
+ font-weight: 700;
923
+ color: var(--accent);
924
+ text-transform: uppercase;
925
+ letter-spacing: 0.1em;
926
+ margin-bottom: 0.25rem;
927
+ }
928
+
929
+ :global(#tb-app .tb-bye-name) {
930
+ font-weight: 700;
931
+ color: var(--text-base);
932
+ font-size: 1rem;
933
+ overflow: hidden;
934
+ text-overflow: ellipsis;
935
+ white-space: nowrap;
936
+ }
937
+
938
+ :global(#tb-app .tb-bracket-wrapper) {
939
+ position: relative;
940
+ }
941
+
942
+ :global(#tb-app .tb-round-header) {
943
+ text-align: center;
944
+ font-weight: 700;
945
+ color: var(--text-muted);
946
+ text-transform: uppercase;
947
+ letter-spacing: 0.05em;
948
+ font-size: 0.75rem;
949
+ border-bottom: 1px solid var(--border-color);
950
+ padding-bottom: 0.5rem;
951
+ margin-bottom: 1.5rem;
952
+ position: sticky;
953
+ top: 0;
954
+ background: var(--bg-surface);
955
+ z-index: 30;
956
+ }
957
+
958
+ :global(#tb-app .tb-desktop-card) {
959
+ background: var(--bg-surface);
960
+ border: 1px solid var(--border-color);
961
+ border-radius: 0.75rem;
962
+ overflow: hidden;
963
+ position: relative;
964
+ width: 100%;
965
+ height: 100px;
966
+ display: flex;
967
+ flex-direction: column;
968
+ justify-content: center;
969
+ }
970
+
971
+ :global(#tb-app .tb-desktop-card:hover) {
972
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
973
+ }
974
+
975
+ :global(#tb-app .tb-desktop-card-bye) {
976
+ background: var(--bg-surface);
977
+ border: 1px solid var(--border-color);
978
+ border-radius: 0.75rem;
979
+ position: relative;
980
+ width: 100%;
981
+ height: 100px;
982
+ display: flex;
983
+ flex-direction: column;
984
+ justify-content: center;
985
+ padding: 0.75rem 1rem;
986
+ box-sizing: border-box;
987
+ }
988
+
989
+ :global(#tb-app .tb-connector-dot-left) {
990
+ position: absolute;
991
+ top: 50%;
992
+ left: -6px;
993
+ width: 12px;
994
+ height: 12px;
995
+ background: var(--border-color);
996
+ border-radius: 50%;
997
+ transform: translateY(-50%);
998
+ border: 2px solid var(--bg-surface);
999
+ box-sizing: content-box;
1000
+ }
1001
+
1002
+ :global(#tb-app .tb-connector-dot-right) {
1003
+ position: absolute;
1004
+ top: 50%;
1005
+ right: -6px;
1006
+ width: 12px;
1007
+ height: 12px;
1008
+ background: rgba(99, 102, 241, 0.3);
1009
+ border-radius: 50%;
1010
+ transform: translateY(-50%);
1011
+ border: 2px solid var(--bg-surface);
1012
+ box-sizing: content-box;
1013
+ }
1014
+
1015
+ :global(#tb-app .tb-connectors) {
1016
+ position: absolute;
1017
+ inset: 0;
1018
+ pointer-events: none;
1019
+ z-index: 0;
1020
+ }
1021
+
1022
+ :global(#tb-app .tb-trophy-icon) {
1023
+ width: 1rem;
1024
+ height: 1rem;
1025
+ display: inline-block;
1026
+ color: #4338ca;
1027
+ margin-left: 0.25rem;
1028
+ }
1029
+
1030
+ :global(#tb-app .tb-icon-xs) {
1031
+ width: 0.75rem;
1032
+ height: 0.75rem;
1033
+ display: inline-block;
1034
+ }
1035
+
1036
+ :global(#tb-app .tb-icon-sm) {
1037
+ width: 1.25rem;
1038
+ height: 1.25rem;
1039
+ display: inline-block;
1040
+ }
1041
+
1042
+ :global(#tb-app .tb-icon-md) {
1043
+ width: 1.25rem;
1044
+ height: 1.25rem;
1045
+ display: inline-block;
1046
+ }
1047
+
1048
+ :global(#tb-app .tb-icon-lg) {
1049
+ width: 1.5rem;
1050
+ height: 1.5rem;
1051
+ display: inline-block;
1052
+ }
1053
+
1054
+ :global(#tb-app .tb-highlight) {
1055
+ box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.4);
1056
+ }
1057
+
1058
+ :global(#tb-app .tb-toast) {
1059
+ position: fixed;
1060
+ bottom: 2rem;
1061
+ left: 50%;
1062
+ transform: translateX(-50%);
1063
+ padding: 0.75rem 1.5rem;
1064
+ border-radius: 0.75rem;
1065
+ font-size: 0.875rem;
1066
+ font-weight: 500;
1067
+ color: white;
1068
+ z-index: 9999;
1069
+ }
1070
+
1071
+ :global(#tb-app .tb-toast-victory) {
1072
+ background: #4338ca;
1073
+ top: 50%;
1074
+ bottom: auto;
1075
+ transform: translate(-50%, -50%);
1076
+ font-size: 1.25rem;
1077
+ font-weight: 700;
1078
+ padding: 1rem 2rem;
1079
+ }
1080
+
1081
+ :global(#tb-app .tb-toast-success) {
1082
+ background: #059669;
1083
+ }
1084
+
1085
+ :global(#tb-app .tb-toast-error) {
1086
+ background: #dc2626;
1087
+ }
1088
+
1089
+ :global(#tb-app .tb-toast-info) {
1090
+ background: var(--accent);
1091
+ }
1092
+ </style>