Repository navigation
Expand file tree
/
Copy pathclips.html
More file actions
839 lines (819 loc) · 56.5 KB
/
Copy pathclips.html
File metadata and controls
839 lines (819 loc) · 56.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VANTS CLIPS — Momentos destacados de VALORANT</title>
<meta name="description" content="VANTS CLIPS: tu galería de momentos destacados de VALORANT estilo Cortex. Conecta la carpeta de clips de Razer Cortex, sube tus highlights y míralos en un solo lugar. Todo corre en tu navegador.">
<link rel="canonical" href="https://vants.vants.workers.dev/clips.html">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath d='M12 2H52L62 12V52L52 62H12L2 52V12Z' fill='%23191c22' stroke='%23ff0d2e' stroke-width='3'/%3E%3Cpath d='M9 14H23L34.5 44.5L32 52Z' fill='%23e8ecef'/%3E%3Cpath d='M55 14H41L32.4 37L42.2 37Z' fill='%23ff0d2e'/%3E%3C/svg%3E">
<link rel="preload" as="font" type="font/woff2" href="./assets/fonts/archivo-latin.var.woff2?v=1" crossorigin>
<link rel="preload" as="font" type="font/woff2" href="./assets/fonts/archivo-black-400-latin.woff2?v=1" crossorigin>
<link rel="stylesheet" href="./assets/css/fonts.css?v=1">
<style>
:root{--bg:#0B0B0E;--bg2:#0D0D10;--panel:#141418;--panel2:#1A1A20;--line:#232329;--line2:#303039;
--text:#EDEDE7;--muted:#93939D;--dim:#5C5C66;--accent:#FF0D2E;--gold:#F5C044;--grn:#44D62C;
--mono:'JetBrains Mono',monospace;--sans:'Archivo',sans-serif;--disp:'Archivo Black',sans-serif}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:var(--sans);-webkit-font-smoothing:antialiased;min-height:100vh;display:flex;flex-direction:column}
a{color:inherit;text-decoration:none}button{font-family:var(--sans);cursor:pointer;border:none;background:none;color:inherit}
::selection{background:var(--accent);color:#160608}
::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-track{var(--bg);background:var(--bg)}::-webkit-scrollbar-thumb{background:var(--line2);border:2px solid var(--bg)}::-webkit-scrollbar-thumb:hover{background:var(--accent)}
.mono{font-family:var(--mono)}
/* HEADER */
header{border-bottom:1px solid var(--line);position:sticky;top:0;background:rgba(11,11,14,.94);backdrop-filter:blur(10px);z-index:20}
.hnav{max-width:1280px;margin:0 auto;padding:12px 24px;display:flex;align-items:center;gap:20px;flex-wrap:wrap}
.brand{display:flex;align-items:center;gap:10px}
.brand svg{width:30px;height:30px;filter:drop-shadow(0 0 10px rgba(255,13,46,.35))}
.wordmark{display:flex;flex-direction:column;line-height:1;font-family:var(--disp);font-size:17px}
.wordmark em{font-style:normal;color:var(--accent)}
.wordmark small{font:700 7px/1 var(--mono);letter-spacing:.5em;color:var(--dim);margin-top:3px}
.htabs{display:flex;gap:2px;margin-left:8px;flex-wrap:wrap}
.htab{padding:10px 14px;font:700 10px/1 var(--mono);letter-spacing:.14em;color:var(--muted);border-bottom:2px solid transparent;transition:.15s;display:inline-flex;align-items:center;gap:6px}
.htab:hover{color:var(--text);background:var(--panel)}
.htab.on{color:var(--grn);border-bottom-color:var(--grn)}
.htab svg{width:13px;height:13px}
.hright{margin-left:auto;display:flex;align-items:center;gap:10px}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font:800 10.5px/1 var(--mono);letter-spacing:.14em;padding:11px 16px;transition:.18s;clip-path:polygon(7px 0,100% 0,100% calc(100% - 7px),calc(100% - 7px) 100%,0 100%,0 7px)}
.btn svg{width:14px;height:14px}
.btn-ghost{border:1px solid var(--line2);color:var(--text);background:var(--panel)}
.btn-ghost:hover{border-color:var(--accent);color:var(--accent)}
.btn-grn{background:var(--grn);color:#07130a}
.btn-grn:hover{filter:brightness(1.1)}
.btn-red{background:var(--accent);color:#fff}
.btn-red:hover{filter:brightness(1.12)}
.btn:disabled{opacity:.45;cursor:not-allowed}
/* LAYOUT */
.app{max-width:1280px;margin:0 auto;width:100%;padding:26px 24px 40px;display:grid;grid-template-columns:250px 1fr;gap:26px;flex:1}
.side{display:flex;flex-direction:column;gap:8px;align-self:start;position:sticky;top:84px}
.s-item{display:flex;align-items:center;gap:11px;padding:12px 14px;border:1px solid transparent;border-left:3px solid transparent;color:var(--muted);font:700 10.5px/1 var(--mono);letter-spacing:.12em;transition:.15s;cursor:pointer;background:none;text-align:left}
.s-item svg{width:16px;height:16px;flex:none}
.s-item:hover{color:var(--text);background:var(--panel)}
.s-item.on{color:var(--grn);border-color:var(--line);border-left-color:var(--grn);background:var(--panel)}
.s-item b{margin-left:auto;font:700 10px/1 var(--mono);color:var(--dim)}
.s-card{margin-top:14px;border:1px solid var(--line);background:var(--panel);padding:16px}
.s-card h4{font:800 10px/1 var(--mono);letter-spacing:.2em;color:var(--dim);margin-bottom:10px}
.s-card p{font:500 10.5px/1.8 var(--mono);color:var(--muted)}
.s-card p+p{margin-top:8px}
.s-card b{color:var(--text)}
.bar{height:6px;background:var(--bg2);border:1px solid var(--line);margin-top:10px}
.bar i{display:block;height:100%;background:linear-gradient(90deg,var(--grn),#8ef07a);width:0%}
/* MAIN */
.m-head{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:18px}
.m-head h1{font-family:var(--disp);font-size:clamp(18px,2.6vw,26px);letter-spacing:.01em}
.m-head h1 em{font-style:normal;color:var(--grn)}
.m-actions{margin-left:auto;display:flex;gap:10px;flex-wrap:wrap}
.link-btn{font:700 10px/1 var(--mono);letter-spacing:.16em;color:var(--muted);padding:8px 4px;border-bottom:1px solid transparent}
.link-btn:hover{color:var(--grn);border-color:var(--grn)}
/* GRID */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(235px,1fr));gap:16px}
.clip-card{background:var(--panel);border:1px solid var(--line);clip-path:polygon(10px 0,100% 0,100% calc(100% - 10px),calc(100% - 10px) 100%,0 100%,0 10px);transition:.2s;cursor:pointer;overflow:hidden}
.clip-card:hover{border-color:var(--grn);transform:translateY(-3px);box-shadow:0 16px 36px rgba(68,214,44,.12)}
.thumb{position:relative;aspect-ratio:16/9;background:#000;display:block;width:100%}
.thumb video{width:100%;height:100%;object-fit:cover;display:block;background:#000}
.thumb .ph{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;color:var(--dim);font:700 9px/1.6 var(--mono);letter-spacing:.14em;text-align:center;padding:10px}
.thumb .ph svg{width:26px;height:26px}
.cbadge{position:absolute;top:8px;left:8px;font:800 8px/1 var(--mono);letter-spacing:.12em;color:#07130a;background:var(--grn);padding:4px 7px;z-index:2}
.cbadge.ref{color:var(--gold);background:rgba(245,192,68,.14);border:1px solid rgba(245,192,68,.4)}
.cdur{position:absolute;right:8px;bottom:8px;font:700 9.5px/1 var(--mono);letter-spacing:.06em;color:#fff;background:rgba(5,5,7,.78);padding:4px 6px;z-index:2}
.cinfo{padding:12px 13px 13px}
.cname{font:700 12px/1.4 var(--sans);color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.crow{display:flex;align-items:center;gap:8px;margin-top:7px;flex-wrap:wrap;font:600 8.5px/1 var(--mono);letter-spacing:.06em;color:var(--muted)}
.crow .vg{color:#ff4655;border:1px solid rgba(255,70,85,.45);padding:3px 6px;font-weight:800}
.crow .kl{color:var(--grn);font-weight:800}
/* EMPTY */
.empty{grid-column:1/-1;border:1px dashed var(--line2);padding:34px 22px;text-align:center}
.empty h3{font-family:var(--disp);font-size:17px;margin-bottom:10px}
.empty p{font:500 11px/1.9 var(--mono);color:var(--muted);max-width:560px;margin:0 auto}
.empty .steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:12px;margin:22px 0;text-align:left}
.empty .st{border:1px solid var(--line);background:var(--panel);padding:14px}
.empty .st b{display:block;font:800 10px/1 var(--mono);letter-spacing:.16em;color:var(--grn);margin-bottom:8px}
.empty .st span{font:500 10.5px/1.7 var(--mono);color:var(--muted)}
/* TOASTS */
.toasts{position:fixed;bottom:18px;left:50%;transform:translateX(-50%);z-index:60;display:flex;flex-direction:column;gap:8px;align-items:center}
.toast{display:flex;align-items:center;gap:9px;background:var(--panel2);border:1px solid var(--line2);border-left:3px solid var(--grn);padding:11px 16px;font:600 11px/1.4 var(--mono);color:var(--text);opacity:0;transform:translateY(8px);transition:.25s;max-width:90vw}
.toast.show{opacity:1;transform:none}
/* MODAL */
.modal{position:fixed;inset:0;z-index:50;display:none}
.modal.show{display:block}
.modal-bg{position:absolute;inset:0;background:rgba(4,4,6,.82);backdrop-filter:blur(4px)}
.modal-card{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:min(880px,94vw);max-height:92vh;overflow:auto;background:var(--panel);border:1px solid var(--line2);padding:20px}
.mc-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:14px}
.mc-head h3{font-family:var(--disp);font-size:17px}
.d-x{width:34px;height:34px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--line2);color:var(--muted);transition:.15s}
.d-x:hover{color:var(--accent);border-color:var(--accent)}
.d-x svg{width:15px;height:15px}
.player video{width:100%;max-height:56vh;background:#000;display:block}
.mc-row{display:flex;align-items:center;gap:12px;margin-top:14px;flex-wrap:wrap}
.mc-row label{font:700 9px/1 var(--mono);letter-spacing:.18em;color:var(--dim)}
.mc-row input{background:var(--bg2);border:1px solid var(--line2);color:var(--text);padding:10px 12px;font:600 12px var(--mono);width:110px;outline:none}
.mc-row input:focus{border-color:var(--grn)}
.mc-meta{font:500 10px/1.8 var(--mono);color:var(--muted);margin-top:12px}
/* CUENTA (Auth0): cada jugador ve SOLO sus clips */
[hidden]{display:none!important}
.acc-chip{display:inline-flex;align-items:center;gap:8px}
.acc-ava{width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;background:var(--accent);color:#fff;font:800 10px/1 var(--mono);clip-path:polygon(4px 0,100% 0,100% calc(100% - 4px),calc(100% - 4px) 100%,0 100%,0 4px)}
.acc-nm{font:800 10px/1 var(--mono);letter-spacing:.12em;color:var(--text);max-width:110px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.acc-out{width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--line2);color:var(--muted);transition:.15s}
.acc-out:hover{color:var(--accent);border-color:var(--accent)}
.acc-out svg{width:13px;height:13px}
.cta-login{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin:18px 0 6px}
.ax-block{border-top:1px solid var(--line);padding:14px 0}
.ax-block:first-of-type{border-top:none;padding-top:0}
.ax-block h4{font:800 10px/1 var(--mono);letter-spacing:.2em;color:var(--grn);margin-bottom:8px}
.ax-block p{font:500 10.5px/1.8 var(--mono);color:var(--muted);margin-bottom:10px}
.ax-block b{color:var(--text)}
/* FOOTER */
footer{border-top:1px solid var(--line)}
.f-in{max-width:1280px;margin:0 auto;padding:18px 24px;display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap;font:500 9.5px/1.8 var(--mono);color:var(--dim)}
.f-in a{color:var(--muted);border-bottom:1px solid var(--line)}
.f-in a:hover{color:var(--text);border-color:var(--accent)}
@media(max-width:920px){.app{grid-template-columns:1fr;gap:18px}.side{position:static;flex-direction:row;flex-wrap:wrap}.s-card{width:100%}.m-head h1{width:100%}.m-actions{margin-left:0}}
/* DESTACADOS (vitrina pública) */
.cstar{position:absolute;left:8px;top:8px;z-index:3;width:28px;height:28px;border-radius:8px;background:rgba(8,9,12,.82);border:1px solid var(--line2);display:flex;align-items:center;justify-content:center;color:var(--muted);transition:all .2s}
.cstar svg{width:14px;height:14px}
.cstar:hover{color:var(--gold);border-color:var(--gold)}
.cstar.on{color:var(--gold);border-color:var(--gold);background:rgba(245,192,68,.14)}
.feat-grid2{display:grid;grid-template-columns:repeat(auto-fill,minmax(235px,1fr));gap:14px}
.featc{background:var(--panel);border:1px solid var(--line);cursor:pointer;transition:border-color .2s,transform .2s;overflow:hidden}
.featc:hover{border-color:var(--gold);transform:translateY(-2px)}
.featc .ft{position:relative;aspect-ratio:16/9;background:#0b0b0e}
.featc .ft img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.featc .fdur{position:absolute;right:8px;bottom:8px;background:rgba(8,9,12,.85);border:1px solid var(--line2);font:700 9.5px/1 var(--mono);padding:4px 6px;color:var(--text)}
.featc .fplay{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:42px;height:42px;border-radius:50%;background:rgba(255,13,46,.92);display:flex;align-items:center;justify-content:center;color:#fff}
.featc .fplay svg{width:18px;height:18px;fill:#fff}
.featc .finfo{padding:12px 13px 13px}
.featc .ftit{font:800 12.5px/1.35 var(--sans);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.featc .fmeta{display:flex;gap:9px;flex-wrap:wrap;margin-top:7px;font:600 8.5px/1 var(--mono);letter-spacing:.08em;color:var(--muted)}
.featc .fmeta .fnk{color:var(--gold);text-transform:uppercase}
.pbar{height:8px;border:1px solid var(--line2);background:var(--bg2);margin:14px 0 8px;position:relative;overflow:hidden}
.pbar i{position:absolute;left:0;top:0;bottom:0;width:0%;background:var(--grn);transition:width .25s}
.dest-note{font:500 10.5px/1.8 var(--mono);color:var(--muted);border:1px dashed var(--line2);padding:12px 14px;margin-bottom:16px}
.dest-note b{color:var(--gold)}
</style>
<link rel="stylesheet" href="./assets/css/strafe.css?v=4">
<link rel="stylesheet" href="./assets/css/vants-arena-reskin.css?v=2">
<link rel="stylesheet" href="./assets/css/arena.css?v=19">
</head>
<body class="sf" data-crumb="CLIPS|MOMENTOS DESTACADOS">
<!-- ===== SYMBOL DEL LOGO VANTS (usable en todo el sitio) ===== -->
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
<defs>
<linearGradient id="vm-plate" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#1b1f26"/><stop offset="1" stop-color="#08090c"/></linearGradient>
<linearGradient id="vm-edge" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#ff6b78"/><stop offset=".5" stop-color="#2a2f38"/><stop offset="1" stop-color="#ff0d2e"/></linearGradient>
<linearGradient id="vm-steel" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#ffffff"/><stop offset=".55" stop-color="#d9dde3"/><stop offset="1" stop-color="#8d949e"/></linearGradient>
<linearGradient id="vm-red" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#ff5a68"/><stop offset=".5" stop-color="#ff0d2e"/><stop offset="1" stop-color="#9e0019"/></linearGradient>
</defs>
<symbol id="vmark" viewBox="0 0 64 64">
<path d="M12 2 H52 L62 12 V52 L52 62 H12 L2 52 V12 Z" fill="url(#vm-plate)" stroke="url(#vm-edge)" stroke-width="1.5"/>
<path d="M9 14 H23 L34.5 44.5 L32 52 Z" fill="url(#vm-steel)"/>
<path d="M55 14 H41 L32.4 37 L42.2 37 Z" fill="url(#vm-red)"/>
<path d="M38.6 41 L41 41 L32 52 L33.6 47.6 Z" fill="#ff0d2e" opacity=".9"/><path d="M9 14 H23 L23.8 16.2 H9.9 Z" fill="#fff" opacity=".75"/>
</symbol>
</svg>
<header class="nav">
<div class="wrap nav-in">
<a href="index.html" class="nav-logo">
<svg class="mark"><use href="#vmark"/></svg>
<span class="wordmark"><b>VANT<em>S</em></b><small>GAMES</small></span>
</a>
<nav class="nav-links">
<div class="ni has-sub"><a href="jugadores.html">SALA<span class="chev" aria-hidden="true">▾</span></a><div class="dd" role="menu"><a href="jugadores.html" role="menuitem">Sala de jugadores</a><a href="equipos.html" role="menuitem">Equipos del circuito</a><a href="equipo.html" role="menuitem">Equipo VANTS DE 5</a></div></div>
<div class="ni has-sub"><a href="partidos.html">PARTIDOS<span class="chev" aria-hidden="true">▾</span></a><div class="dd" role="menu"><a href="partidos.html" role="menuitem">Agenda en vivo</a><a href="resultados.html" role="menuitem">Resultados</a><a href="calendario.html" role="menuitem">Calendario</a></div></div>
<div class="ni has-sub"><a href="ranking.html">RANKING<span class="chev" aria-hidden="true">▾</span></a><div class="dd" role="menu"><a href="ranking.html" role="menuitem">Ranking global</a><a href="rangos.html" role="menuitem">Rangos</a></div></div>
<div class="ni has-sub"><a href="torneos.html">TORNEOS<span class="chev" aria-hidden="true">▾</span></a><div class="dd" role="menu"><a href="torneos.html" role="menuitem">Calendario abierto</a><a href="torneo.html" role="menuitem">Torneo actual · Ascensión S4</a><a href="llaves.html" role="menuitem">Llaves · Bracket</a></div></div>
<div class="ni"><a href="tienda.html">TIENDA</a></div>
<div class="ni"><a href="descargar.html">DESCARGAR</a></div>
<div class="ni has-sub"><a href="clips.html">CLIPS<span class="chev" aria-hidden="true">▾</span></a><div class="dd" role="menu"><a href="clips.html" role="menuitem">Momentos destacados</a><a href="historia.html" role="menuitem">Historia</a><a href="guia.html" role="menuitem">Guía de la arena</a></div></div>
</nav>
<div class="nav-right">
<span class="acc-chip" id="clipAcc"></span>
<span class="pro-badge" id="proBadge"><i data-lucide="zap"></i>PRO</span>
<a class="btn btn-ghost btn-sm" href="membresias.html"><i data-lucide="crown"></i>MEMBRESÍAS</a>
<button class="btn btn-ghost btn-sm" id="arenaLoginBtn" hidden><i data-lucide="log-in"></i>ENTRAR</button>
<button class="btn btn-ghost btn-sm" id="miCuentaBtn" hidden><i data-lucide="layout-dashboard"></i>MI CUENTA</button>
</div>
</div>
</header>
<div class="app">
<aside class="side">
<button class="s-item on" id="sMis"><i data-lucide="folder"></i>MIS CLIPS DE JUEGO<b id="sCount">0</b></button>
<button class="s-item" id="sDest"><i data-lucide="star"></i>DESTACADOS<b id="sDestCount">0</b></button>
<button class="s-item" id="sAjustes"><i data-lucide="settings"></i>AJUSTES</button>
<div class="s-card">
<h4>ESTADO</h4>
<p id="stFolder">Carpeta Cortex: <b>no conectada</b></p>
<p id="stStore">Almacenamiento local: <b>—</b></p>
<div class="bar"><i id="stBar"></i></div>
</div>
<div class="s-card">
<h4>PRIVACIDAD</h4>
<p>Tu galería corre <b>100% en tu navegador</b> y vive atada a <b>TU CUENTA VANTS</b>: cada clip queda guardado con la cuenta que lo subió.</p>
<p>Sin sesión no se puede subir nada ni ver clips: cada jugador solo ve <b>sus propios clips</b>, nunca los de otra cuenta. Los videos nunca suben a ningún servidor (IndexedDB).</p>
<p>¿Quieres que todos vean tu mejor jugada? Pulsa la <b>estrella</b> de un clip tuyo: se publica en <b>DESTACADOS</b> y sale público en la <b>Sala de jugadores</b> para toda la arena.</p>
</div>
</aside>
<main>
<div class="m-head">
<h1 id="mTitle">MIS CLIPS DE JUEGO <em>RECIENTES</em></h1>
<div class="m-actions">
<button class="link-btn" id="verTodo" type="button">VER TODO</button>
<button class="btn btn-ghost" id="btnCortex" type="button" hidden><i data-lucide="folder-open"></i>CONECTAR CARPETA DE CORTEX</button>
<button class="btn btn-grn" id="btnSubir" type="button" hidden><i data-lucide="upload"></i>SUBIR CLIP</button>
<input type="file" id="fileIn" accept="video/*,.mkv" multiple hidden>
</div>
</div>
<div class="grid" id="grid"></div>
</main>
</div>
<footer>
<div class="f-in">
<span>© 2026 VANTS GAMES — CLIPS · Momentos destacados de VALORANT</span>
<span>No afiliado a Riot Games ni a Razer Inc. «Razer Cortex» es una marca de Razer Inc.; la app lee tus clips locales solo con tu permiso.</span>
<span><a href="politica-de-privacidad.html">Privacidad</a> · <a href="terminos-de-servicio.html">Términos</a> · <a href="preguntas-frecuentes.html">Preguntas frecuentes</a></span>
</div>
</footer>
<div class="modal" id="modal"><div class="modal-bg" id="modalBg"></div><div class="modal-card" id="modalCard"></div></div>
<div class="toasts" id="toasts"></div>
<script src="https://unpkg.com/lucide@0.454.0/dist/umd/lucide.min.js" defer onload="if(window.lucide)lucide.createIcons()"></script>
<script src="https://cdn.auth0.com/js/auth0-spa-js/2.1/auth0-spa-js.production.js"></script>
<script src="./assets/js/auth0-config.js?v=16"></script>
<script>
(function(){
'use strict';
var $=function(s){return document.querySelector(s)};
function icons(){if(window.lucide)lucide.createIcons()}
function esc(s){return String(s==null?'':s).replace(/[&<>"']/g,function(c){return{'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]})}
var SVG_OK='<svg viewBox="0 0 24 24" fill="none" stroke="#44D62C" stroke-width="2.5" stroke-linecap="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>';
function toast(msg){
var el=document.createElement('div');el.className='toast';
el.innerHTML=SVG_OK+'<span>'+esc(msg)+'</span>';
$('#toasts').appendChild(el);requestAnimationFrame(function(){el.classList.add('show')});
setTimeout(function(){el.classList.remove('show');setTimeout(function(){el.remove()},300)},3200);
}
/* ============ CUENTA VANTS (Auth0): cada jugador ve SOLO sus clips ============ */
var USER=null,MI_SUB=null;
async function authBoot(){
try{
if(!window.VantAuth0)return;
await window.VantAuth0.initAuth0();
var A0=window.VantAuth0.client;if(!A0)return;
var q=new URLSearchParams(location.search);
if(q.get('code')&&q.get('state')){
try{await A0.handleRedirectCallback();history.replaceState({},'',location.pathname+location.hash)}catch(eC){}
}
try{USER=await A0.getUser()||null}catch(eU){USER=null}
if(USER&&USER.sub)MI_SUB=String(USER.sub);
}catch(e){console.warn('[clips] auth no disponible',e)}
}
function nickCuenta(){
return String((USER&&(USER.name||USER.nickname||USER.email))||'JUGADOR').split('@')[0].toUpperCase();
}
function entrar(){
var A0=window.VantAuth0&&window.VantAuth0.client;
if(!A0){toast('No se pudo abrir el login: recargá la página');return}
/* sin redirect_uri propio: usa el permitido del sitio (origin/) y vuelve a la arena ya logueado */
A0.loginWithRedirect({authorizationParams:{scope:'openid profile email'}})
.catch(function(){toast('Acá no se puede iniciar sesión: entrá desde la arena (INICIO) y volvé','info')});
}
function salir(){
try{window.location.href='https://vants.eu.auth0.com/v2/logout?client_id=oaOmNizh7HrASWfmfM29bN284IMJvqPG&returnTo='+encodeURIComponent(location.origin+'/')}
catch(e){window.location.href='index.html'}
}
function pintarCuenta(){
var el=$('#clipAcc');if(!el)return;
if(USER){
var nm=nickCuenta();
el.innerHTML='<span class="acc-ava">'+esc(nm.slice(0,2))+'</span>'
+'<span class="acc-nm" title="'+esc((USER.email||nm)+' · clips de tu cuenta')+'">'+esc(nm.slice(0,12))+'</span>'
+'<button class="acc-out" id="accOut" type="button" title="Cerrar sesión"><i data-lucide="log-out"></i></button>';
icons();
$('#accOut').addEventListener('click',salir);
}else{
el.innerHTML='<button class="btn btn-grn" id="accEnter" type="button"><i data-lucide="log-in"></i>INICIAR SESIÓN</button>';
icons();
$('#accEnter').addEventListener('click',entrar);
}
}
function aplicarSesion(){
var con=!!USER;
var b1=$('#btnSubir'),b2=$('#btnCortex');
if(b1)b1.hidden=!con;
if(b2)b2.hidden=!con;
}
/* ============ IndexedDB ============ */
var DB='vants-clips',STORE='clips',META='meta',db=null;
var VID_EXT=['mp4','webm','mkv','mov','avi','m4v','ogv'];
function idbOpen(){return new Promise(function(res,rej){
var r=indexedDB.open(DB,1);
r.onupgradeneeded=function(e){var d=e.target.result;
if(!d.objectStoreNames.contains(STORE))d.createObjectStore(STORE,{keyPath:'id'});
if(!d.objectStoreNames.contains(META))d.createObjectStore(META)};
r.onsuccess=function(e){db=e.target.result;res(db)};
r.onerror=function(){rej(r.error)};
})}
function idbAll(){return new Promise(function(res,rej){
var q=db.transaction(STORE,'readonly').objectStore(STORE).getAll();
q.onsuccess=function(){res(q.result||[])};q.onerror=function(){rej(q.error)}})}
function idbPut(rec){return new Promise(function(res,rej){
var q=db.transaction(STORE,'readwrite').objectStore(STORE).put(rec);
q.onsuccess=function(){res()};q.onerror=function(){rej(q.error)}})}
function idbDel(id){return new Promise(function(res,rej){
var q=db.transaction(STORE,'readwrite').objectStore(STORE)['delete'](id);
q.onsuccess=function(){res()};q.onerror=function(){rej(q.error)}})}
function idbMetaGet(k){return new Promise(function(res,rej){
var q=db.transaction(META,'readonly').objectStore(META).get(k);
q.onsuccess=function(){res(q.result||null)};q.onerror=function(){rej(q.error)}})}
function idbMetaSet(k,v){return new Promise(function(res,rej){
var q=db.transaction(META,'readwrite').objectStore(META).put(v,k);
q.onsuccess=function(){res()};q.onerror=function(){rej(q.error)}})}
/* ============ helpers de video ============ */
function esVideo(name){var m=/\.([a-z0-9]+)$/i.exec(name||'');return m&&VID_EXT.indexOf(m[1].toLowerCase())>=0}
function fmtDur(s){if(!isFinite(s)||s<0)return'';var m=Math.floor(s/60),x=Math.floor(s%60);return(m<10?'0':'')+m+':'+(x<10?'0':'')+x}
function fmtMB(b){return(b/1048576).toFixed(1)+' MB'}
function fmtFecha(ts){try{return new Date(ts).toLocaleDateString('es',{day:'2-digit',month:'short',year:'numeric'})}catch(e){return''}}
/* ============ galería ============ */
var urls={},showAll=false,cache=[],carpetaVacia=null;
function urlFor(rec,file){
if(urls[rec.id])return urls[rec.id];
if(rec.blob){urls[rec.id]=URL.createObjectURL(rec.blob);return urls[rec.id]}
if(file){urls[rec.id]=URL.createObjectURL(file);return urls[rec.id]}
return null;
}
function cardHTML(rec,url){
var isRef=!rec.blob;
var inner=url
?'<video src="'+url+'#t=0.6" preload="metadata" muted playsinline></video>'
:'<span class="ph"><i data-lucide="film"></i>PULSA PARA CARGAR<br>DESDE LA CARPETA</span>';
var star=USER?'<button class="cstar'+(rec.destacado?' on':'')+'" data-star="'+esc(rec.id)+'" title="'+(rec.destacado?'Destacado · pulsa para quitar':'Destacar en la vitrina pública')+'"><i data-lucide="star"'+(rec.destacado?' style="fill:currentColor"':'')+'></i></button>':'';
return '<div class="clip-card" data-id="'+esc(rec.id)+'">'
+'<div class="thumb"><span class="cbadge'+(isRef?' ref':'')+'">'+(isRef?'REFERENCIA':'CARGADO')+'</span>'
+star+inner+'<span class="cdur" data-dur>··:··</span></div>'
+'<div class="cinfo"><div class="cname" title="'+esc(rec.titulo||rec.name)+'">'+esc(rec.titulo||rec.name)+'</div>'
+'<div class="crow"><span class="vg">VALORANT</span>'
+(rec.kills?'<span class="kl">'+esc(rec.kills)+'Kill</span>':'')
+'<span>'+esc(fmtFecha(rec.ts))+'</span><span>'+esc(fmtMB(rec.size||0))+'</span></div></div></div>';
}
var SIN_SESION='<div class="empty"><h3>TUS CLIPS VIVEN EN TU CUENTA VANTS</h3>'
+'<p>Cada jugador tiene su propia galería: entrá con tu cuenta para ver, subir y titular tus momentos destacados de VALORANT. Sin sesión nadie puede subir clips ni ver los de otra cuenta.</p>'
+'<div class="cta-login">'
+'<button class="btn btn-red" id="emptyEnter" type="button"><i data-lucide="log-in"></i>INICIAR SESIÓN CON MI CUENTA</button>'
+'<a class="btn btn-ghost" href="index.html"><i data-lucide="crosshair"></i>CREAR CUENTA EN LA ARENA</a>'
+'</div>'
+'<div class="steps">'
+'<div class="st"><b>1 · ENTRA CON TU CUENTA</b><span>La galería es personal: cada clip que subas queda atado a tu cuenta VANTS.</span></div>'
+'<div class="st"><b>2 · CONECTA CORTEX O SUBE</b><span>Con sesión iniciada conectás tu carpeta de Razer Cortex o subís videos directo.</span></div>'
+'<div class="st"><b>3 · SOLO TÚ LAS VES</b><span>Si cerrás sesión, tus clips quedan guardados para tu cuenta: nadie más los ve en esta PC.</span></div>'
+'</div></div>';
var EMPTY='<div class="empty"><h3>TODAVÍA NO HAY CLIPS ACÁ</h3>'
+'<p>Tus momentos destacados de VALORANT viven en tu PC — esta app los reúne en una galería estilo Cortex, sin subir nada a ningún servidor.</p>'
+'<div class="steps">'
+'<div class="st"><b>1 · GRABA EN CORTEX</b><span>Con Razer Cortex (CLIPS DE JUEGO) graba tus highlights: asesinatos múltiples, clutch, aces.</span></div>'
+'<div class="st"><b>2 · CONECTA LA CARPETA</b><span>Pulsa «CONECTAR CARPETA DE CORTEX» y elige la carpeta donde Cortex guarda tus clips. Queda conectada para próximas visitas.</span></div>'
+'<div class="st"><b>3 · O SUBE DIRECTO</b><span>¿Los tienes en otro lado? «SUBIR CLIP» los guarda en tu navegador (IndexedDB), sin nube.</span></div>'
+'</div>'
+'<p>Tip: los clips marcados <b style="color:var(--gold)">REFERENCIA</b> se leen directo de tu carpeta; los <b style="color:var(--grn)">CARGADO</b> viven dentro de la app.</p></div>';
function emptyConectada(nombre){
return '<div class="empty"><h3>CARPETA CONECTADA · 0 CLIPS EN ESTA CARPETA</h3>'
+'<p>La carpeta <b style="color:var(--gold)">'+esc(nombre)+'</b> quedó conectada y vigilada, pero todavía no contiene ningún video (mp4, webm, mkv, mov, avi, m4v). Nada salió mal: lo más probable es que Cortex todavía no haya guardado clips ahí.</p>'
+'<div class="steps">'
+'<div class="st"><b>1 · RUTA DE CORTEX</b><span>Cortex guarda por defecto en: <b>C:\\Users\\TU_USUARIO\\AppData\\Local\\Razer\\Razer Cortex\\GameClips\\TU_USUARIO\\Videos\\VALORANT</b>. Si elegiste otra carpeta, conecta esa ruta (la de tu usuario y tu juego).</span></div>'
+'<div class="st"><b>2 · ACTIVA LOS CLIPS</b><span>Abre Razer Cortex → <b>CLIPS DE JUEGO</b>: activa la captura automática de highlights (o usa su tecla para guardar clip) y juega una partida. Al primer clip que Cortex grabe, aparece acá solo.</span></div>'
+'<div class="st"><b>3 · O SUBE DIRECTO</b><span>¿Ya tienes el video en otra carpeta? «SUBIR CLIP» lo guarda al instante en tu galería, sin nube y sin esperas.</span></div>'
+'</div>'
+'<p>La conexión queda guardada: cuando Cortex escriba un clip ahí, entra a esta app y aparecerá al instante.</p></div>';
}
async function render(){
if(!USER){
$('#sCount').textContent='0';
$('#grid').innerHTML=SIN_SESION;
var be=$('#emptyEnter');if(be)be.addEventListener('click',entrar);
icons();refreshStorage();
return;
}
var list=cache.slice().sort(function(a,b){return (b.ts||0)-(a.ts||0)});
$('#sCount').textContent=String(list.length);
var view=showAll?list:list.slice(0,12);
for(var i=0;i<view.length;i++){
var rec=view[i];
if(rec.blob){urlFor(rec);continue}
if(!rec.blob&&rec.handle&&!urls[rec.id]){
try{
var perm=await rec.handle.queryPermission({mode:'read'});
if(perm==='granted'){var f=await rec.handle.getFile();urlFor(rec,f)}
}catch(e){}
}
}
$('#grid').innerHTML=view.map(function(r){return cardHTML(r,urls[r.id])}).join('')||(carpetaVacia?emptyConectada(carpetaVacia):EMPTY);
Array.prototype.forEach.call($('#grid').querySelectorAll('.clip-card'),function(card){
card.addEventListener('click',function(){openPlayer(card.getAttribute('data-id'))});
var v=card.querySelector('video');
if(v){v.addEventListener('loadedmetadata',function(){var d=card.querySelector('[data-dur]');if(d)d.textContent=fmtDur(v.duration)})}
});
Array.prototype.forEach.call($('#grid').querySelectorAll('.cstar'),function(st){
st.addEventListener('click',function(ev){ev.stopPropagation();var id=st.getAttribute('data-star');var rec=cache.find(function(r){return r.id===id});if(!rec)return;if(rec.destacado)abrirQuitar(rec);else abrirDestacar(rec);});
});
icons();
refreshStorage();
}
async function cacheMio(){
/* GALERÍA POR CUENTA: solo los clips subidos con esta sesión (owner = sub Auth0) */
var todas=await idbAll();
return MI_SUB?todas.filter(function(r){return r.owner===MI_SUB}):[];
}
async function reload(){cache=await cacheMio();render()}
/* ============ almacenamiento (barra) ============ */
async function refreshStorage(){
if(!navigator.storage||!navigator.storage.estimate)return;
try{
var e=await navigator.storage.estimate();
var pct=e.quota?Math.min(100,(e.usage/e.quota)*100):0;
$('#stStore').innerHTML='Almacenamiento local: <b>'+fmtMB(e.usage||0)+' usados</b>';
$('#stBar').style.width=Math.max(2,pct).toFixed(1)+'%';
}catch(err){}
}
/* ============ subir clips ============ */
$('#btnSubir').addEventListener('click',function(){$('#fileIn').click()});
$('#fileIn').addEventListener('change',async function(e){
var files=Array.prototype.slice.call(e.target.files||[]);e.target.value='';
var n=0;
for(var i=0;i<files.length;i++){
var f=files[i];if(!esVideo(f.name)&&!(f.type||'').startsWith('video/'))continue;
await idbPut({id:'up:'+f.name+':'+f.size+':'+f.lastModified,name:f.name,mime:f.type||'video/mp4',size:f.size,ts:f.lastModified||Date.now(),blob:f,handle:null,kills:null,src:'up',owner:MI_SUB});
n++;
}
if(n){toast(n==1?'1 clip guardado en tu galería':n+' clips guardados en tu galería');await reload()}
else toast('No se encontró ningún video en la selección');
});
/* ============ carpeta de Cortex ============ */
var FOLDER_OK='Carpeta Cortex: <b>conectada</b>',FOLDER_NO='Carpeta Cortex: <b>no conectada</b>';
async function scanDir(dir,depth,added,todas){
if(depth>4)return;
if(!todas)todas=[];
try{
var it=dir.entries();
while(true){
var n=await it.next();if(n.done)break;
var name=n.value[0],h=n.value[1];
if(h.kind==='directory'){if(name.charAt(0)!=='.'&&name.charAt(0)!=='$')await scanDir(h,depth+1,added,todas);continue}
if(h.kind!=='file'||!esVideo(name))continue;
var id='cx:'+name+':'+(h.size||0);
/* el id ya existe para CUALQUIER cuenta: no se toca (nadie roba clips de otra cuenta) */
var existe=todas.some(function(r){return r.id===id});
if(existe){continue}
var f=null;
try{if(await h.queryPermission({mode:'read'})==='granted')f=await h.getFile()}catch(e){}
await idbPut({id:id,name:name,mime:(f&&f.type)||'video/mp4',size:(f&&f.size)||h.size||0,ts:(f&&f.lastModified)||Date.now(),blob:null,handle:h,kills:null,src:'cortex',owner:MI_SUB});
added.v=(added.v||0)+1;
}
}catch(e){}
}
async function connectCortex(silent){
if(!MI_SUB){if(!silent)toast('Iniciá sesión con tu cuenta para conectar tu carpeta de clips','info');return}
if(!window.showDirectoryPicker){
if(!silent)toast('Tu navegador no permite abrir carpetas: usa Chrome/Edge de escritorio o «SUBIR CLIP»');
return;
}
try{
var dir=await window.showDirectoryPicker({id:'vants-cortex',mode:'read',startIn:'videos'});
var added={v:0};await scanDir(dir,0,added,await idbAll());
await idbMetaSet('cortexDir',dir);
cache=await cacheMio();
var cx=cache.filter(function(r){return r.src==='cortex'}).length;
$('#stFolder').innerHTML='Carpeta Cortex: <b>'+esc(dir.name)+'</b> conectada'+(added.v?' · +'+added.v+' nuevos':'');
carpetaVacia=null;
await reload();
if(cx){
toast('Carpeta conectada · '+cx+' clips en tu galería');
}else{
carpetaVacia=dir.name;
render();
toast('Carpeta conectada, pero todavía no tiene clips: mira la guía en la galería');
}
}catch(e){
if(e&&e.name!=='AbortError'&&!silent)toast('No se pudo leer esa carpeta: prueba de nuevo o elige otra carpeta');
}
}
$('#btnCortex').addEventListener('click',function(){connectCortex(false)});
/* ============ AJUSTES: cuenta + carpeta + migración local ============ */
async function abrirAjustes(){
var orf=0;
try{var todas=await idbAll();orf=todas.filter(function(r){return !r.owner}).length}catch(eO){}
var htmlCuenta=USER
?'<div class="ax-block"><h4>CUENTA</h4><p>Esta galería es de <b>'+esc(nickCuenta())+'</b>'+(USER.email?' ('+esc(USER.email)+')':'')+'. Todos los clips que subas quedan atados a tu cuenta: nadie más los ve ni los puede editar.</p><button class="btn btn-ghost" id="axOut" type="button"><i data-lucide="log-out"></i>CERRAR SESIÓN</button></div>'
:'<div class="ax-block"><h4>CUENTA</h4><p><b>Sin sesión.</b> Hasta que entres con tu cuenta VANTS no podés subir clips ni ver galería: cada jugador solo ve sus propios clips.</p><button class="btn btn-grn" id="axIn" type="button"><i data-lucide="log-in"></i>INICIAR SESIÓN</button></div>';
var htmlMig=(USER&&orf)
?'<div class="ax-block"><h4>MIGRACIÓN LOCAL</h4><p>Hay <b>'+orf+' clip'+(orf==1?'':'s')+' sin cuenta</b> guardado'+(orf==1?'':'s')+' en este navegador (de antes de las cuentas). Podés adoptar'+(orf==1?'lo':'los')+' para TU cuenta.</p><button class="btn btn-grn" id="axAdopt" type="button"><i data-lucide="archive-restore"></i>ADOPTAR PARA MI CUENTA</button></div>'
:'';
$('#modalCard').innerHTML=
'<div class="mc-head"><h3>AJUSTES DE CLIPS</h3><button class="d-x" id="axClose" aria-label="Cerrar"><i data-lucide="x"></i></button></div>'
+htmlCuenta
+'<div class="ax-block"><h4>CARPETA DE CORTEX</h4><p id="axFolder">La app vigila tu carpeta de clips de Razer Cortex y los muestra acá como referencias.</p><button class="btn btn-ghost" id="axCortex" type="button"><i data-lucide="folder-open"></i>CONECTAR / RECONECTAR CARPETA</button></div>'
+htmlMig
+'<p class="mc-meta">Regla de la galería: cada clip pertenece a la cuenta que lo subió. Cerrar sesión oculta tus clips hasta que vuelvas a entrar.</p>';
$('#modal').classList.add('show');icons();
$('#axClose').addEventListener('click',closePlayer);
var o1=$('#axOut');if(o1)o1.addEventListener('click',salir);
var o2=$('#axIn');if(o2)o2.addEventListener('click',entrar);
var o3=$('#axCortex');if(o3)o3.addEventListener('click',function(){closePlayer();connectCortex(false)});
var o4=$('#axAdopt');
if(o4)o4.addEventListener('click',async function(){
try{
var todas=await idbAll();var n=0;
for(var i=0;i<todas.length;i++){if(!todas[i].owner){await idbPut(Object.assign({},todas[i],{owner:MI_SUB}));n++}}
closePlayer();
if(n){toast(n==1?'1 clip adoptado para tu cuenta':n+' clips adoptados para tu cuenta');await reload()}
else toast('No había clips por adoptar');
}catch(eM){toast('No se pudo completar la migración')}
});
}
/* ============ reproductor ============ */
function closePlayer(){$('#modal').classList.remove('show');var v=$('#modalCard video');if(v){v.pause();v.removeAttribute('src');v.load()}}
$('#modalBg').addEventListener('click',closePlayer);
document.addEventListener('keydown',function(e){if(e.key==='Escape')closePlayer()});
async function openPlayer(id){
var rec=cache.find(function(r){return r.id===id});if(!rec)return;
var file=null;
if(!rec.blob&&rec.handle){
try{if(await rec.handle.queryPermission({mode:'read'})==='granted')file=await rec.handle.getFile()}catch(e){}
}
var url=urlFor(rec,file);
if(!url){toast('Concede acceso a la carpeta de Cortex para reproducir este clip');return}
$('#modalCard').innerHTML=
'<div class="mc-head"><h3>'+esc(rec.titulo||rec.name)+'</h3><button class="d-x" id="plClose" aria-label="Cerrar"><i data-lucide="x"></i></button></div>'
+'<div class="player"><video src="'+url+'" controls autoplay playsinline></video></div>'
+'<div class="mc-row"><label>TÍTULO DEL CLIP</label><input id="plTitulo" type="text" maxlength="60" value="'+esc(rec.titulo||'')+'" placeholder="Ej: ACE en Ascent · clutch 1v4" style="width:100%;flex:1;min-width:180px"></div>'
+'<div class="mc-row"><label>KILLS DEL CLIP</label><input id="plKills" type="number" min="1" max="50" value="'+(rec.kills||'')+'" placeholder="ej. 4">'
+'<button class="btn btn-grn" id="plSave" type="button"><i data-lucide="save"></i>GUARDAR</button>'
+'<button class="btn btn-ghost" id="plDown" type="button"><i data-lucide="download"></i>DESCARGAR</button>'
+'<button class="btn btn-red" id="plDel" type="button"><i data-lucide="trash-2"></i>ELIMINAR</button></div>'
+'<div class="mc-row" style="margin-top:10px"><button class="btn'+(rec.destacado?' btn-ghost':' btn-grn')+'" id="plFeat" type="button"><i data-lucide="star"'+(rec.destacado?' style="fill:currentColor"':'')+'></i>'+(rec.destacado?'DESTACADO · QUITAR DE LA VITRINA':'DESTACAR EN LA VITRINA')+'</button></div>'
+'<p class="mc-meta">'+esc(fmtFecha(rec.ts))+' · '+esc(fmtMB(rec.size||0))+' · '+(rec.src==='cortex'?'REFERENCIA de tu carpeta Cortex (no ocupa espacio acá)':'GUARDADO en este navegador')+'</p>'
+'<p class="mc-meta" style="margin-top:4px">El título se muestra en la galería: ponle nombre a tus mejores jugadas.</p>';
$('#modal').classList.add('show');icons();
$('#plClose').addEventListener('click',closePlayer);
$('#plSave').addEventListener('click',async function(){
var v=parseInt($('#plKills').value,10);
var ti=$('#plTitulo').value.trim().slice(0,60);
rec.kills=isNaN(v)?null:v;
rec.titulo=ti||null;
await idbPut(rec);await reload();closePlayer();
toast(ti?('Título guardado: «'+ti+'»'):'Datos del clip guardados');
});
$('#plDown').addEventListener('click',function(){
var a=document.createElement('a');a.href=url;a.download=rec.name;document.body.appendChild(a);a.click();a.remove();
});
$('#plDel').addEventListener('click',async function(){
if(rec.handle&&!rec.blob){await idbDel(rec.id);await reload();closePlayer();toast('Referencia eliminada (el archivo sigue en tu carpeta)');return}
await idbDel(rec.id);if(urls[rec.id]){URL.revokeObjectURL(urls[rec.id]);delete urls[rec.id]}
await reload();closePlayer();toast('Clip eliminado de la galería');
});
var pf=$('#plFeat');
if(pf)pf.addEventListener('click',function(){closePlayer();if(rec.destacado)abrirQuitar(rec);else abrirDestacar(rec);});
}
/* ============ ver todo / ajustes ============ */
$('#verTodo').addEventListener('click',function(){
showAll=!showAll;this.textContent=showAll?'VER MENOS':'VER TODO';render();
});
$('#sMis').addEventListener('click',function(){window.scrollTo({top:0,behavior:'smooth'})});
$('#sAjustes').addEventListener('click',abrirAjustes);
/* ============ DESTACADOS: vitrina pública de la comunidad ============
Lo que el jugador destaca acá sale público en la Sala de jugadores.
Publicar exige sesión (JWT): el Worker ata el clip a la cuenta que manda
el vídeo, nadie destaca ni borra en nombre de otra cuenta. */
var VISTA='mis',featCache=null,featAt=0,MAX_FEAT_MB=48;
/* La vitrina vive en el Worker: desde el espejo de GitHub Pages las rutas
relativas /api/* caen en el 404 de Pages (HTML, no JSON) y el modal
mostraba "respuesta_invalida". Base absoluta salvo en el propio Worker. */
var WB=(location.hostname.indexOf('.workers.dev')>=0)?'':'https://vants.vants.workers.dev';
var STAR='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>';
function fmtF(iso){try{return new Date(iso).toLocaleDateString('es',{day:'2-digit',month:'short',year:'numeric'})}catch(e){return''}}
function fmtDur2(s){s=parseInt(s,10)||0;if(!s)return'';var m=Math.floor(s/60),x=s%60;return m+':'+(x<10?'0':'')+x}
async function destacados(){
if(featCache&&Date.now()-featAt<60000)return featCache;
try{
var r=await fetch(WB+'/api/clips/featured',{headers:{Accept:'application/json'}});
var j=await r.json().catch(function(){return null});
featCache=(j&&j.ok&&Array.isArray(j.clips))?j.clips:[];featAt=Date.now();
}catch(e){if(!featCache)featCache=[]}
return featCache;
}
function featCard(c){
var dur=fmtDur2(c.dur),mb=(c.tam>0)?fmtMB(c.tam):'';
return '<div class="featc" data-fid="'+esc(c.id)+'">'
+'<div class="ft">'+(c.thumb?'<img src="'+esc(c.thumb)+'" alt="" loading="lazy">':'')
+(dur?'<span class="fdur">'+esc(dur)+'</span>':'')
+'<span class="fplay">'+STAR+'</span></div>'
+'<div class="finfo"><div class="ftit" title="'+esc(c.titulo||'Clip destacado')+'">'+esc(c.titulo||'Clip destacado')+'</div>'
+'<div class="fmeta"><span class="fnk">'+esc(c.nick||'JUGADOR')+'</span><span>'+esc(c.juego||'VALORANT')+'</span>'+(mb?'<span>'+esc(mb)+'</span>':'')+'<span>'+esc(fmtF(c.fecha))+'</span></div></div></div>';
}
async function renderDestacados(){
$('#sDestCount').textContent='·';
$('#mTitle').innerHTML='CLIPS <em>DESTACADOS</em>';
var b1=$('#verTodo'),b2=$('#btnCortex'),b3=$('#btnSubir');if(b1)b1.style.visibility='hidden';if(b2)b2.hidden=true;if(b3)b3.hidden=true;
var list=await destacados();
$('#sDestCount').textContent=String(list.length);
if(!list.length){
$('#grid').innerHTML='<div class="empty"><h3>TODAVÍA NO HAY CLIPS DESTACADOS</h3>'
+'<p>Sé el primero: entra a <b>MIS CLIPS DE JUEGO</b>, pulsa la estrella de tu mejor jugada y se publicará acá para toda la arena — con tu nombre y tu cuenta.</p>'
+'<div class="steps"><div class="st"><b>1 · ELIGE TU MEJOR CLIP</b><span>En tu galería, cada tarjeta tiene una estrella arriba a la izquierda.</span></div>'
+'<div class="st"><b>2 · PULSA LA ESTRELLA</b><span>Con sesión iniciada, escribe el título y publica: el video se sube a la vitrina de VANTS.</span></div>'
+'<div class="st"><b>3 · SALE EN LA SALA</b><span>Todos lo verán en la Sala de jugadores, con tu perfil y tus insignias.</span></div></div></div>';
return;
}
$('#grid').innerHTML='<div class="dest-note" style="grid-column:1/-1">Los clips destacados son <b>públicos</b>: los ve toda la arena en la <b>Sala de jugadores</b>. Cada clip queda publicado con la cuenta de quien lo subió. Máximo '+MAX_FEAT_MB+' MB por clip.</div>'
+'<div class="feat-grid2" style="grid-column:1/-1">'+list.map(featCard).join('')+'</div>';
Array.prototype.forEach.call($('#grid').querySelectorAll('.featc'),function(card){
card.addEventListener('click',function(){abrirClipPublico(card.getAttribute('data-fid'))});
});
icons();
}
function abrirClipPublico(fid){
destacados().then(function(list){
var c=(list||[]).find(function(x){return x.id===fid});
if(!c){toast('Ese clip ya no está destacado');return}
$('#modalCard').innerHTML=
'<div class="mc-head"><h3 title="'+esc(c.titulo||'Clip destacado')+'">'+esc(c.titulo||'Clip destacado')+'</h3><button class="d-x" id="plClose" aria-label="Cerrar"><i data-lucide="x"></i></button></div>'
+'<div class="player"><video src="'+esc(c.video||'')+'" controls autoplay playsinline></video></div>'
+'<p class="mc-meta">DESTACADO POR <b style="color:var(--gold)">'+esc(c.nick||'JUGADOR')+'</b> · '+esc(c.juego||'VALORANT')+' · '+esc(fmtF(c.fecha))+'</p>'
+'<p class="mc-meta" style="margin-top:4px">Este clip es público: toda la arena lo ve en la Sala de jugadores.</p>';
$('#modal').classList.add('show');icons();
$('#plClose').addEventListener('click',closePlayer);
});
}
async function activarVista(v){
VISTA=v;
var s1=$('#sMis'),s2=$('#sDest');
if(v==='mis'){
if(s1)s1.classList.add('on');if(s2)s2.classList.remove('on');
$('#mTitle').innerHTML='MIS CLIPS DE JUEGO <em>RECIENTES</em>';
var b1=$('#verTodo'),b2=$('#btnCortex'),b3=$('#btnSubir');if(b1)b1.style.visibility='';if(b2)b2.hidden=!USER;if(b3)b3.hidden=!USER;
$('#sDestCount').textContent=String((featCache||[]).length);
await reload();
}else{
if(s1)s1.classList.remove('on');if(s2)s2.classList.add('on');
await renderDestacados();
}
}
$('#sDest').addEventListener('click',function(){activarVista('dest');window.scrollTo({top:0,behavior:'smooth'})});
$('#sMis').addEventListener('click',function(){if(VISTA!=='mis')activarVista('mis');});
/* ============ destacar un clip propio ============ */
function archivoDe(rec){
return new Promise(function(res){
if(rec.blob){res(rec.blob);return}
if(rec.handle){rec.handle.queryPermission({mode:'read'}).then(function(p){
if(p!=='granted'){res(null);return}
rec.handle.getFile().then(res,function(){res(null)});
},function(){res(null)});return}
res(null);
});
}
function capturarThumb(rec,url){
return new Promise(function(res){
var v=document.createElement('video');
try{
v.muted=true;v.playsInline=true;v.preload='auto';v.src=url;
var hecho=false;
function fin(){
if(hecho)return;hecho=true;
try{
var w=480,r=(v.videoWidth&&v.videoHeight)?(v.videoHeight/v.videoWidth):(9/16);
var h=Math.max(200,Math.round(w*r));
var c=document.createElement('canvas');c.width=w;c.height=h;
c.getContext('2d').drawImage(v,0,0,w,h);
var d=c.toDataURL('image/jpeg',0.72);
res({thumb:(d&&d.length>2000)?d:null,dur:isFinite(v.duration)?Math.round(v.duration):0});
}catch(e){res({thumb:null,dur:0})}
}
v.addEventListener('loadeddata',function(){try{v.currentTime=Math.min(0.8,(v.duration||2)/4)}catch(e){fin()}});
v.addEventListener('seeked',function(){setTimeout(fin,80)});
v.addEventListener('error',function(){res({thumb:null,dur:0})});
setTimeout(function(){if(!hecho){res({thumb:null,dur:0})}},5000);
}catch(e){res({thumb:null,dur:0})}
});
}
function subirDestacado(form,onProg){
return new Promise(function(res){
var t=null,mandar=function(){
var x=new XMLHttpRequest();
x.open('POST',WB+'/api/clips/featured',true);
if(t)x.setRequestHeader('Authorization','Bearer '+t);
x.upload.onprogress=function(e){if(e.lengthComputable&&onProg)onProg(e.loaded/e.total)};
x.onload=function(){try{res(JSON.parse(x.responseText))}catch(e){res({ok:false,error:'respuesta_invalida'})}};
x.onerror=function(){res({ok:false,error:'sin_conexion'})};
x.send(form);
};
if(window.VantAuth0&&window.VantAuth0.getToken){window.VantAuth0.getToken().then(function(tk){t=tk;mandar()}).catch(function(){mandar()})}
else mandar();
});
}
async function abrirDestacar(rec){
if(!USER){toast('Iniciá sesión con tu cuenta para destacar clips');return}
var file=await archivoDe(rec);
if(!file){toast('Concede acceso a la carpeta de Cortex o vuelve a subir el clip para destacarlo');return}
if(file.size>MAX_FEAT_MB*1048576){toast('El clip supera '+MAX_FEAT_MB+' MB: recórtalo antes de destacarlo');return}
var url=urlFor(rec,file);
var th={thumb:null,dur:0};
if(url)th=await capturarThumb(rec,url);
var br=esc('«'+(rec.titulo||rec.name)+'»');
$('#modalCard').innerHTML=
'<div class="mc-head"><h3>DESTACAR EN LA VITRINA</h3><button class="d-x" id="plClose" aria-label="Cerrar"><i data-lucide="x"></i></button></div>'
+'<div class="ax-block" style="border-top:none;padding-top:6px"><p>Publicarás <b>'+br+'</b> para toda la arena. Sale público en la <b>Sala de jugadores</b> con tu cuenta: <b>'+esc(nickCuenta())+'</b>. Máximo '+MAX_FEAT_MB+' MB.</p>'
+(th.thumb?'<div style="position:relative;aspect-ratio:16/9;background:#0b0b0e;margin:10px 0 2px"><img src="'+th.thumb+'" alt="" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover"></div>':'<p style="color:var(--muted)">No se pudo generar la miniatura (se publicará sin ella).</p>')+'</div>'
+'<div class="ax-block"><h4>TÍTULO PÚBLICO</h4><input id="dtTitulo" type="text" maxlength="70" value="'+esc(rec.titulo||'')+'" placeholder="Ej: ACE en Ascent · clutch 1v4" style="width:100%;background:var(--bg2);border:1px solid var(--line2);color:var(--text);padding:12px 13px;font:500 12px var(--mono);outline:none"></div>'
+'<div class="pbar" id="dtBar" style="display:none"><i></i></div>'
+'<p class="mc-meta" id="dtMsg" style="display:none"></p>'
+'<div class="cta-login" style="justify-content:flex-start"><button class="btn btn-grn" id="dtGo" type="button"><i data-lucide="star"></i>PUBLICAR EN DESTACADOS</button><button class="btn btn-ghost" id="dtNo" type="button">CANCELAR</button></div>';
$('#modal').classList.add('show');icons();
$('#plClose').addEventListener('click',closePlayer);
$('#dtNo').addEventListener('click',closePlayer);
$('#dtGo').addEventListener('click',async function(){
var btn=$('#dtGo');btn.disabled=true;btn.style.opacity='.55';
var titulo=($('#dtTitulo').value||'').trim().slice(0,70)||'Clip destacado';
var fd=new FormData();
fd.append('video',file,file.name||('clip.'+(rec.name||'mp4').split('.').pop()));
if(th.thumb){var tb=new Uint8Array(atob(th.thumb.split(',')[1]).length);var bin=atob(th.thumb.split(',')[1]);for(var i=0;i<bin.length;i++)tb[i]=bin.charCodeAt(i);fd.append('thumb',new Blob([tb],{type:'image/jpeg'}),'thumb.jpg')}
var extM=/\.([a-z0-9]+)$/i.exec(rec.name||'');var ext=(extM&&['webm','mp4','mkv','mov','m4v'].indexOf(extM[1].toLowerCase())>=0)?extM[1].toLowerCase():'mp4';
fd.append('titulo',titulo);fd.append('juego','VALORANT');fd.append('nick',nickCuenta());
fd.append('username',String((USER&&(USER.preferred_username||USER.nickname))||'').replace(/[^a-z0-9_\-.]/gi,'').slice(0,32));
fd.append('ext',ext);fd.append('dur',String(th.dur||0));
var bar=$('#dtBar'),msg=$('#dtMsg');bar.style.display='';msg.style.display='';
var r=await subirDestacado(fd,function(p){bar.querySelector('i').style.width=(p*100).toFixed(0)+'%';msg.textContent='Subiendo el video: '+(p*100).toFixed(0)+'%'});
if(r&&r.ok){
rec.destacado=r.id;try{await idbPut(rec)}catch(eS){}
featCache=null;featAt=0;
closePlayer();await reload();
toast('Publicado en DESTACADOS: ya está en la Sala de jugadores');
activarVista('dest');
}else{
btn.disabled=false;btn.style.opacity='';
msg.style.display='';
var er=(r&&r.error)||'error';
msg.innerHTML='No se pudo publicar: <b style="color:var(--accent)">'+esc(er==='clip_demasiado_grande'?'el clip supera '+MAX_FEAT_MB+' MB':(er==='sin_sesion'||er==='token_invalido'?'tu sesión expiró, entra de nuevo':(er==='gh_no_config'?'la vitrina se está configurando, prueba más tarde':er)))+'</b>'+(r&&r.detalle?'<span style="display:block;font:500 9.5px/1.6 var(--mono);color:#9a9aa6;margin-top:6px">Motivo: '+esc(String(r.detalle))+'</span>':'');
}
});
}
async function abrirQuitar(rec){
$('#modalCard').innerHTML=
'<div class="mc-head"><h3>QUITAR DE DESTACADOS</h3><button class="d-x" id="plClose" aria-label="Cerrar"><i data-lucide="x"></i></button></div>'
+'<div class="ax-block" style="border-top:none;padding-top:6px"><p>Este clip está publicado en la vitrina pública. Si lo quitas, deja de verse en la Sala de jugadores (tu copia local no se toca).</p></div>'
+'<div class="cta-login" style="justify-content:flex-start"><button class="btn btn-red" id="dqGo" type="button"><i data-lucide="star-off"></i>QUITAR DE DESTACADOS</button><button class="btn btn-ghost" id="dqNo" type="button">DEJARLO PUBLICADO</button></div>';
$('#modal').classList.add('show');icons();
$('#plClose').addEventListener('click',closePlayer);
$('#dqNo').addEventListener('click',closePlayer);
$('#dqGo').addEventListener('click',async function(){
var btn=$('#dqGo');btn.disabled=true;btn.style.opacity='.55';
try{
var tk=window.VantAuth0&&window.VantAuth0.getToken?await window.VantAuth0.getToken():null;
var r=await fetch(WB+'/api/clips/featured?id='+encodeURIComponent(rec.destacado),{method:'DELETE',headers:tk?{Authorization:'Bearer '+tk}:{}});
var j=await r.json().catch(function(){return null});
if(j&&j.ok){rec.destacado=null;try{await idbPut(rec)}catch(eS){}featCache=null;featAt=0;closePlayer();await reload();toast('Clip quitado de la vitrina');}
else{btn.disabled=false;btn.style.opacity='';toast((j&&j.error==='no_es_tuyo')?'Ese clip fue publicado por otra cuenta':'No se pudo quitar ahora');}
}catch(e){btn.disabled=false;btn.style.opacity='';toast('Sin conexión para quitar el clip')}
});
}
/* ============ arranque ============ */
(async function boot(){
/* 1) sesión: cada jugador ve SOLO sus clips */
await authBoot();
pintarCuenta();aplicarSesion();
/* 2) galería local */
await idbOpen();
cache=await cacheMio();
var dir=await idbMetaGet('cortexDir');
if(dir&&MI_SUB){
var perm='prompt';
try{perm=await dir.queryPermission({mode:'read'})}catch(e){}
if(perm==='granted'){
var added={v:0};await scanDir(dir,0,added,await idbAll());cache=await cacheMio();
var cx0=cache.filter(function(r){return r.src==='cortex'}).length;
$('#stFolder').innerHTML='Carpeta Cortex: <b>'+esc(dir.name)+'</b> conectada';
if(!cx0)carpetaVacia=dir.name;
}else{
$('#stFolder').innerHTML='Carpeta Cortex: <b>reconectar permiso</b>';
$('#btnCortex').innerHTML='<i data-lucide="refresh-cw"></i>RECONECTAR CARPETA DE CORTEX';
}
}
render();icons();
/* contador de la vitrina pública (sin bloquear el arranque) */
destacados().then(function(l){var el=$('#sDestCount');if(el)el.textContent=String(l.length)}).catch(function(){});
})();
})();
</script>
<script src="./assets/js/menu-preview.js?v=10" defer></script>
<script>
/* D24: tabs con submenú = abren el panel, NO navegan (solo puntero fino; en táctil el tab sigue navegando). */
(function () {
'use strict';
var fine = window.matchMedia && window.matchMedia("(hover: hover) and (pointer: fine)").matches;
if (!fine) return;
var tabs = document.querySelectorAll('.nav-links .ni.has-sub > a');
Array.prototype.forEach.call(tabs, function (a) {
a.addEventListener("click", function (e) { e.preventDefault(); a.focus(); });
a.addEventListener("keydown", function (e) { if (e.key === "Escape") a.blur(); });
});
})();
</script>
<script src="./assets/js/nav-movil.js?v=2" defer></script>
</body>
</html>