Mycc commited on
Commit
37f2790
·
verified ·
1 Parent(s): da10641

Update NEXUS.html

Browse files
Files changed (1) hide show
  1. NEXUS.html +215 -309
NEXUS.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.5, user-scalable=yes">
6
- <title>Genshin Nexus · PRO Оптимизатор</title>
7
  <!-- Font Awesome 6 (нормальные иконки) -->
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
9
  <style>
@@ -42,20 +42,20 @@
42
  overflow: hidden;
43
  }
44
 
45
- ::-webkit-scrollbar { width: 8px; height: 8px; }
 
46
  ::-webkit-scrollbar-track { background: transparent; }
47
- ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
48
  ::-webkit-scrollbar-thumb:hover { background: var(--accent); }
49
 
 
50
  .blur-popup {
51
  position: fixed;
52
  top: 0;
53
  left: 0;
54
  right: 0;
55
  bottom: 0;
56
- backdrop-filter: blur(16px) brightness(0.3);
57
- -webkit-backdrop-filter: blur(16px) brightness(0.3);
58
- background: rgba(0, 0, 0, 0.5);
59
  z-index: 3000;
60
  display: none;
61
  align-items: center;
@@ -65,13 +65,13 @@
65
  .popup-content {
66
  background: var(--bg-card);
67
  border: 1px solid var(--border);
68
- border-radius: 20px;
69
  padding: 28px;
70
  max-width: 600px;
71
  width: 90%;
72
  max-height: 80vh;
73
  overflow-y: auto;
74
- box-shadow: 0 25px 50px -8px rgba(0,0,0,0.6);
75
  position: relative;
76
  }
77
  .popup-close {
@@ -85,6 +85,7 @@
85
  }
86
  .popup-close:hover { color: #fff; }
87
 
 
88
  @media (max-width: 1024px) {
89
  aside { width: 80px; }
90
  .side-header h1 { font-size: 0; background: none; -webkit-text-fill-color: #fff; }
@@ -100,11 +101,6 @@
100
  .stats-strip { gap: 12px; }
101
  .stat-unit b { font-size: 16px; }
102
  .content-area { padding: 16px; }
103
- .view-toggle-btn { padding: 6px 12px; font-size: 12px; }
104
- .data-table th { font-size: 11px; padding: 8px; }
105
- .data-row td { padding: 12px; }
106
- .weapon-badge { font-size: 11px; padding: 4px 10px; }
107
- .role-badge { font-size: 11px; padding: 4px 10px; }
108
  }
109
 
110
  @media (max-width: 768px) {
@@ -116,13 +112,9 @@
116
  header { flex-wrap: wrap; height: auto; padding: 16px; }
117
  .stats-strip { flex-wrap: wrap; justify-content: center; }
118
  .content-area { padding: 16px; }
119
- .chars-grid { grid-template-columns: 1fr; }
120
- .team-members { justify-content: center; }
121
- .synergy-card { padding: 20px; }
122
- .team-member { min-width: 90px; padding: 12px; }
123
- .member-icon { width: 56px; height: 56px; font-size: 24px; }
124
  }
125
 
 
126
  aside {
127
  width: 280px;
128
  background: var(--bg-side);
@@ -130,7 +122,6 @@
130
  display: flex;
131
  flex-direction: column;
132
  flex-shrink: 0;
133
- box-shadow: 6px 0 24px rgba(0,0,0,0.4);
134
  transition: 0.3s;
135
  overflow-y: auto;
136
  }
@@ -138,12 +129,10 @@
138
  .side-header {
139
  padding: 28px 20px;
140
  border-bottom: 1px solid var(--border);
141
- background: radial-gradient(circle at 0% 50%, rgba(139,92,246,0.15), transparent);
142
  }
143
  .side-header h1 {
144
  font-size: 24px;
145
  font-weight: 800;
146
- letter-spacing: -0.5px;
147
  background: linear-gradient(135deg, #fff, var(--accent));
148
  -webkit-background-clip: text;
149
  -webkit-text-fill-color: transparent;
@@ -157,7 +146,7 @@
157
  .nav-group { padding: 20px 16px; flex-grow: 1; overflow-y: auto; }
158
  .nav-item {
159
  padding: 12px 18px;
160
- border-radius: 20px;
161
  cursor: pointer;
162
  margin-bottom: 6px;
163
  font-size: 15px;
@@ -170,14 +159,13 @@
170
  border: 1px solid transparent;
171
  }
172
  .nav-item:hover {
173
- background: rgba(255,255,255,0.06);
174
  color: #fff;
175
  border-color: var(--border);
176
  }
177
  .nav-item.active {
178
  background: linear-gradient(135deg, var(--accent), #a78bfa);
179
  color: #fff;
180
- box-shadow: 0 6px 16px var(--accent-glow);
181
  border: none;
182
  }
183
 
@@ -197,11 +185,11 @@
197
  }
198
  select, .filter-section input {
199
  width: 100%;
200
- background: rgba(0,0,0,0.4);
201
  border: 1px solid var(--border);
202
  color: #fff;
203
  padding: 10px 14px;
204
- border-radius: 20px;
205
  font-size: 13px;
206
  margin-bottom: 16px;
207
  cursor: pointer;
@@ -219,14 +207,11 @@
219
  display: flex;
220
  flex-direction: column;
221
  overflow: hidden;
222
- background: radial-gradient(circle at 80% 20%, rgba(139,92,246,0.1), transparent 60%);
223
  }
224
 
225
  header {
226
  height: 70px;
227
- background: rgba(15, 17, 23, 0.8);
228
- backdrop-filter: blur(16px);
229
- -webkit-backdrop-filter: blur(16px);
230
  border-bottom: 1px solid var(--border);
231
  display: flex;
232
  align-items: center;
@@ -246,9 +231,7 @@
246
  .stat-unit b {
247
  font-size: 20px;
248
  font-weight: 800;
249
- background: linear-gradient(135deg, #fff, #d6ccff);
250
- -webkit-background-clip: text;
251
- -webkit-text-fill-color: transparent;
252
  }
253
 
254
  .content-area {
@@ -259,10 +242,11 @@
259
  scroll-behavior: smooth;
260
  }
261
 
 
262
  .data-table {
263
  width: 100%;
264
  border-collapse: separate;
265
- border-spacing: 0 10px;
266
  }
267
  .data-table th {
268
  text-align: left;
@@ -272,15 +256,11 @@
272
  text-transform: uppercase;
273
  letter-spacing: 1px;
274
  font-weight: 700;
275
- opacity: 0.9;
276
  }
277
  .data-row {
278
- border-radius: 20px;
279
  transition: 0.2s;
280
- position: relative;
281
- background: rgba(26, 30, 42, 0.7);
282
- backdrop-filter: blur(8px);
283
- box-shadow: 0 10px 24px -6px rgba(0,0,0,0.5);
284
  border: 1px solid var(--border);
285
  }
286
  .data-row td {
@@ -288,26 +268,25 @@
288
  vertical-align: middle;
289
  }
290
  .data-row:hover {
291
- background: rgba(37, 43, 56, 0.8);
292
  border-color: var(--accent);
293
  }
294
 
 
295
  .chars-grid {
296
  display: grid;
297
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
298
  gap: 20px;
299
  }
300
  .char-card {
301
- background: rgba(26, 30, 42, 0.9);
302
- backdrop-filter: blur(10px);
303
- border-radius: 24px;
304
  padding: 24px;
305
  border: 1px solid var(--border);
306
  display: flex;
307
  flex-direction: column;
308
  gap: 16px;
309
- transition: 0.25s;
310
- box-shadow: 0 18px 28px -8px rgba(0,0,0,0.6);
311
  position: relative;
312
  overflow: hidden;
313
  }
@@ -319,12 +298,9 @@
319
  right: 0;
320
  height: 4px;
321
  background: linear-gradient(90deg, var(--element-color), var(--element-glow), transparent);
322
- opacity: 0.9;
323
  }
324
  .char-card:hover {
325
- transform: translateY(-4px);
326
  border-color: var(--element-color);
327
- box-shadow: 0 24px 36px -6px var(--element-glow);
328
  }
329
  .char-card-header {
330
  display: flex;
@@ -340,23 +316,17 @@
340
  justify-content: center;
341
  font-size: 30px;
342
  border: 2px solid;
343
- box-shadow: 0 0 25px var(--element-glow);
344
- background: rgba(0,0,0,0.4);
345
- backdrop-filter: blur(4px);
346
  object-fit: cover;
347
  }
348
  .char-name {
349
  font-size: 20px;
350
  font-weight: 800;
351
  margin-bottom: 4px;
352
- display: flex;
353
- align-items: center;
354
- gap: 6px;
355
  }
356
  .char-name a {
357
  color: inherit;
358
  text-decoration: none;
359
- transition: 0.2s;
360
  }
361
  .char-name a:hover {
362
  color: var(--accent);
@@ -365,9 +335,9 @@
365
  .char-level {
366
  color: var(--text-dim);
367
  font-size: 12px;
368
- background: rgba(0,0,0,0.4);
369
  padding: 4px 10px;
370
- border-radius: 20px;
371
  display: inline-block;
372
  border: 1px solid var(--border);
373
  }
@@ -375,13 +345,13 @@
375
  display: flex;
376
  justify-content: space-between;
377
  align-items: center;
378
- background: rgba(0,0,0,0.3);
379
  padding: 14px 18px;
380
- border-radius: 20px;
381
  border: 1px solid var(--border);
382
  }
383
  .weapon-badge {
384
- background: rgba(255,255,255,0.08);
385
  padding: 6px 16px;
386
  border-radius: 30px;
387
  font-size: 12px;
@@ -404,44 +374,42 @@
404
  color: var(--text-soft);
405
  background: rgba(0,0,0,0.2);
406
  padding: 14px 18px;
407
- border-radius: 20px;
408
  line-height: 1.5;
409
  border: 1px solid var(--border);
410
  }
411
 
 
412
  .artifacts-grid {
413
  display: flex;
414
  flex-direction: column;
415
- gap: 16px;
416
  }
417
  .artifact-card {
418
- background: rgba(26, 30, 42, 0.9);
419
- backdrop-filter: blur(8px);
420
- border-radius: 20px;
421
  padding: 20px;
422
  display: flex;
423
  flex-wrap: wrap;
424
  align-items: center;
425
  gap: 20px;
426
  border: 1px solid var(--border);
427
- box-shadow: 0 10px 24px -6px rgba(0,0,0,0.5);
428
  transition: 0.2s;
429
  }
430
  .artifact-card:hover {
431
  border-color: var(--accent);
432
- transform: translateX(6px);
433
- background: rgba(37,43,56,0.9);
434
  }
435
  .artifact-mainstat {
436
  font-weight: 700;
437
  color: #ffd966;
438
- background: rgba(255, 217, 102, 0.12);
439
  padding: 6px 18px;
440
  border-radius: 30px;
441
  border: 1px solid rgba(255, 217, 102, 0.3);
442
  font-size: 13px;
443
  }
444
 
 
445
  .ai-btn {
446
  background: linear-gradient(135deg, #8b5cf6, #c084fc);
447
  border: none;
@@ -454,21 +422,19 @@
454
  display: flex;
455
  align-items: center;
456
  gap: 8px;
457
- white-space: nowrap;
458
- transition: 0.25s;
459
- box-shadow: 0 6px 16px rgba(139,92,246,0.4);
460
  border: 1px solid rgba(255,255,255,0.2);
461
  }
462
  .ai-btn:hover {
463
- transform: scale(1.02);
464
- box-shadow: 0 10px 22px rgba(139,92,246,0.6);
465
  }
466
 
 
467
  .cv-meter {
468
  width: 100px;
469
- height: 6px;
470
  background: rgba(255,255,255,0.1);
471
- border-radius: 3px;
472
  overflow: hidden;
473
  margin-top: 6px;
474
  border: 1px solid var(--border);
@@ -476,40 +442,22 @@
476
  .cv-fill {
477
  height: 100%;
478
  transition: 0.5s;
479
- border-radius: 3px;
480
  }
481
 
 
482
  .synergy-card {
483
- background: rgba(26, 30, 42, 0.9);
484
- backdrop-filter: blur(10px);
485
- border-radius: 24px;
486
  padding: 28px;
487
  border: 1px solid var(--border);
488
  margin-bottom: 24px;
489
  position: relative;
490
  overflow: hidden;
491
- box-shadow: 0 18px 32px -8px rgba(0,0,0,0.6);
492
  transition: 0.2s;
493
  }
494
  .synergy-card:hover {
495
- border-color: rgba(255,255,255,0.15);
496
- background: rgba(32,38,54,0.9);
497
- }
498
- .synergy-card::after {
499
- content: '';
500
- position: absolute;
501
- top: 0;
502
- left: 0;
503
- right: 0;
504
- bottom: 0;
505
- border-radius: 24px;
506
- padding: 2px;
507
- background: radial-gradient(circle at 0% 0%, var(--res-glow), transparent 70%);
508
- -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
509
- mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
510
- -webkit-mask-composite: xor;
511
- mask-composite: exclude;
512
- pointer-events: none;
513
  }
514
 
515
  .team-members {
@@ -522,18 +470,16 @@
522
  display: flex;
523
  flex-direction: column;
524
  align-items: center;
525
- background: rgba(0,0,0,0.3);
526
  padding: 16px 14px;
527
- border-radius: 20px;
528
  min-width: 100px;
529
- backdrop-filter: blur(8px);
530
  border: 1px solid var(--border);
531
  transition: 0.2s;
532
  }
533
  .team-member:hover {
534
- background: rgba(139,92,246,0.15);
535
  border-color: var(--accent);
536
- transform: translateY(-4px);
537
  }
538
  .member-icon {
539
  width: 64px;
@@ -546,7 +492,6 @@
546
  justify-content: center;
547
  font-size: 28px;
548
  margin-bottom: 10px;
549
- box-shadow: 0 0 25px var(--element-glow);
550
  object-fit: cover;
551
  }
552
 
@@ -560,20 +505,17 @@
560
  cursor: pointer;
561
  font-size: 13px;
562
  transition: 0.2s;
563
- box-shadow: 0 5px 14px var(--accent-glow);
564
  border: 1px solid rgba(255,255,255,0.1);
565
  }
566
  .btn:hover {
567
- transform: translateY(-2px);
568
- box-shadow: 0 10px 20px var(--accent-glow);
569
  }
570
  .btn-outline {
571
  background: transparent;
572
  border: 1px solid var(--border);
573
- box-shadow: none;
574
  }
575
  .btn-outline:hover {
576
- background: rgba(255,255,255,0.06);
577
  border-color: var(--accent);
578
  }
579
 
@@ -592,8 +534,7 @@
592
  text-align: center;
593
  color: var(--text-dim);
594
  background: rgba(0,0,0,0.2);
595
- border-radius: 24px;
596
- backdrop-filter: blur(8px);
597
  border: 1px solid var(--border);
598
  }
599
 
@@ -630,7 +571,6 @@
630
  cursor: pointer;
631
  margin-left: 6px;
632
  border: 1px solid var(--border);
633
- transition: 0.2s;
634
  }
635
  .tooltip-icon:hover {
636
  background: var(--accent);
@@ -656,18 +596,15 @@
656
  background: linear-gradient(135deg, var(--accent), #a78bfa);
657
  color: #fff;
658
  border-color: transparent;
659
- box-shadow: 0 4px 10px var(--accent-glow);
660
  }
661
 
662
  .assessment-card {
663
- background: rgba(26,30,42,0.9);
664
- backdrop-filter: blur(10px);
665
- border-radius: 24px;
666
  padding: 28px;
667
  margin-bottom: 24px;
668
  border-left: 6px solid;
669
  border: 1px solid var(--border);
670
- box-shadow: 0 14px 28px -8px rgba(0,0,0,0.5);
671
  }
672
 
673
  #ai-overlay {
@@ -676,16 +613,14 @@
676
  top: 0;
677
  width: 520px;
678
  height: 100vh;
679
- background: rgba(15, 17, 23, 0.98);
680
- backdrop-filter: blur(24px);
681
- -webkit-backdrop-filter: blur(24px);
682
  border-left: 1px solid var(--border);
683
  z-index: 2000;
684
- transition: 0.3s cubic-bezier(0.2, 0.9, 0.4, 1);
685
  display: flex;
686
  flex-direction: column;
687
  padding: 32px;
688
- box-shadow: -14px 0 40px rgba(0,0,0,0.8);
689
  }
690
  #ai-overlay.open { right: 0; }
691
 
@@ -699,7 +634,7 @@
699
  background: rgba(139,92,246,0.1);
700
  border-left: 4px solid var(--accent);
701
  padding: 14px 18px;
702
- border-radius: 18px;
703
  margin-bottom: 20px;
704
  font-size: 13px;
705
  }
@@ -708,16 +643,14 @@
708
  text-decoration: none;
709
  margin-right: 14px;
710
  display: inline-block;
711
- padding: 4px 0;
712
  }
713
  .toc a:hover {
714
  color: #fff;
715
- text-decoration: underline;
716
  }
717
 
718
  .settings-section {
719
  background: rgba(0,0,0,0.2);
720
- border-radius: 24px;
721
  padding: 24px;
722
  margin-bottom: 24px;
723
  border: 1px solid var(--border);
@@ -783,9 +716,8 @@
783
  margin-top: 20px;
784
  }
785
  .team-card {
786
- background: rgba(26,30,42,0.7);
787
- backdrop-filter: blur(8px);
788
- border-radius: 24px;
789
  padding: 20px;
790
  border: 1px solid var(--border);
791
  }
@@ -806,12 +738,21 @@
806
  border-radius: 50%;
807
  background: rgba(0,0,0,0.3);
808
  border: 2px solid;
809
- display: flex;
810
- align-items: center;
811
- justify-content: center;
812
- font-size: 18px;
813
  object-fit: cover;
814
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
815
  </style>
816
  </head>
817
  <body>
@@ -855,7 +796,7 @@
855
  <p style="font-size: 16px; line-height: 1.6; margin-bottom: 20px; color: var(--text-soft);">
856
  <strong>CV = CRIT Rate × 2 + CRIT DMG</strong>
857
  </p>
858
- <div style="background: rgba(0,0,0,0.3); padding: 20px; border-radius: 20px;">
859
  <div style="display: flex; align-items: center; gap: 16px; margin-bottom: 12px;">
860
  <div style="width: 20px; height: 20px; background: var(--success); border-radius: 4px;"></div>
861
  <span><strong>Высокий (>200)</strong> — отличный билд</span>
@@ -876,21 +817,21 @@
876
  <!-- Сайдбар -->
877
  <aside>
878
  <div class="side-header">
879
- <h1>NEXUS PRO</h1>
880
  <p>анализатор Genshin</p>
881
  </div>
882
  <div class="nav-group">
883
  <div class="nav-item active" data-tab="chars" onclick="switchTab('chars')"><i class="fas fa-users"></i> <span>Персонажи</span></div>
884
  <div class="nav-item" data-tab="artifacts" onclick="switchTab('artifacts')"><i class="fas fa-gem"></i> <span>Артефакты</span></div>
885
- <div class="nav-item" data-tab="team" onclick="switchTab('team')"><i class="fas fa-users-cog"></i> <span>Отряды</span></div>
886
  <div class="nav-item" data-tab="assessment" onclick="switchTab('assessment')"><i class="fas fa-chart-line"></i> <span>Оценка</span></div>
887
- <div class="nav-item" data-tab="myteams" onclick="switchTab('myteams')"><i class="fas fa-folder-open"></i> <span>Мои отряды</span></div>
888
- <div class="nav-item" data-tab="profiles" onclick="switchTab('profiles')"><i class="fas fa-id-card"></i> <span>Профили</span></div>
889
  <div class="nav-item" data-tab="settings" onclick="switchTab('settings')"><i class="fas fa-cog"></i> <span>Настройки</span></div>
890
  </div>
891
 
892
  <div class="filter-section">
893
- <label>Фильтр стихии</label>
894
  <select id="f-el" onchange="applyFilters()">
895
  <option value="all">Все элементы</option>
896
  <option value="pyro">Пиро</option>
@@ -904,12 +845,12 @@
904
 
905
  <label>Сортировка</label>
906
  <select id="f-sort" onchange="applyFilters()">
907
- <option value="cv">Крит‑ценность (CV)</option>
908
  <option value="lvl">Уровень</option>
909
  <option value="name">Имя</option>
910
  </select>
911
 
912
- <button class="btn" style="width:100%; background: linear-gradient(135deg, #ef4444, #b91c1c);" onclick="clearDB()"><i class="fas fa-trash-alt"></i> <span>Удалить данные</span></button>
913
  <button class="btn" style="width:100%; margin-top: 14px; background: linear-gradient(135deg, #4b5563, #1f2937);" onclick="openLegend()"><i class="fas fa-book"></i> <span>Легенда</span></button>
914
  </div>
915
  </aside>
@@ -923,10 +864,10 @@
923
  </div>
924
  <div style="display: flex; gap: 12px;">
925
  <label class="btn">
926
- <i class="fas fa-upload"></i> <span>Загрузить GOOD</span>
927
  <input type="file" id="fileIn" accept=".json" style="display:none;">
928
  </label>
929
- <button class="btn btn-outline" onclick="showProfileSelector()"><i class="fas fa-id-card"></i> <span>Профили</span></button>
930
  </div>
931
  </header>
932
 
@@ -934,7 +875,7 @@
934
  <!-- Персонажи -->
935
  <div id="view-chars" class="view-panel active">
936
  <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:24px; flex-wrap:wrap; gap:16px;">
937
- <h2 style="font-size:26px; font-weight:800; background: linear-gradient(135deg, #fff, var(--accent)); -webkit-background-clip: text; -webkit-text-fill-color: transparent;"><i class="fas fa-users"></i> Персонажи</h2>
938
  <div class="view-toggle">
939
  <span class="view-toggle-btn active" data-view="table" onclick="setCharsView('table')"><i class="fas fa-list"></i> Таблица</span>
940
  <span class="view-toggle-btn" data-view="cards" onclick="setCharsView('cards')"><i class="fas fa-th-large"></i> Ка��точки</span>
@@ -961,7 +902,7 @@
961
  <!-- Артефакты -->
962
  <div id="view-artifacts" class="view-panel">
963
  <div style="display:flex; gap:16px; margin-bottom:24px; align-items:center; flex-wrap:wrap;">
964
- <h2 style="font-size:26px; font-weight:800; background: linear-gradient(135deg, #fff, #ffd966); -webkit-background-clip: text; -webkit-text-fill-color: transparent;"><i class="fas fa-gem"></i> Артефакты</h2>
965
  <select id="art-set-filter" onchange="renderArtifacts()" style="width:200px;">
966
  <option value="all">Все сеты</option>
967
  </select>
@@ -991,7 +932,7 @@
991
  <!-- Отряды -->
992
  <div id="view-team" class="view-panel">
993
  <div style="display:flex; align-items:center; gap:20px; margin-bottom:24px; flex-wrap:wrap;">
994
- <h2 style="font-size:26px; font-weight:800; background: linear-gradient(135deg, #fff, #fbbf24); -webkit-background-clip: text; -webkit-text-fill-color: transparent;"><i class="fas fa-users-cog"></i> Отряды</h2>
995
  <button class="btn" onclick="aiTeamBuilder()" style="background: linear-gradient(135deg, #8b5cf6, #c084fc);"><i class="fas fa-microchip"></i> AI‑подбор</button>
996
  <label style="display:flex; align-items:center; gap:8px; background:rgba(0,0,0,0.3); padding:8px 20px; border-radius:30px; border:1px solid var(--border);">
997
  <input type="checkbox" id="googleSearchCheckbox" style="width:18px; height:18px; accent-color:var(--accent);">
@@ -1004,14 +945,14 @@
1004
 
1005
  <!-- Оценка аккаунта -->
1006
  <div id="view-assessment" class="view-panel">
1007
- <h2 style="font-size:26px; font-weight:800; margin-bottom:24px; background: linear-gradient(135deg, #fff, #10b981); -webkit-background-clip: text; -webkit-text-fill-color: transparent;"><i class="fas fa-chart-line"></i> Оценка аккаунта</h2>
1008
- <div id="assessment-content">Загрузите GOOD‑файл для оценки.</div>
1009
  </div>
1010
 
1011
  <!-- Мои отряды -->
1012
  <div id="view-myteams" class="view-panel">
1013
  <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:24px; flex-wrap:wrap; gap:16px;">
1014
- <h2 style="font-size:26px; font-weight:800; background: linear-gradient(135deg, #fff, #f59e0b); -webkit-background-clip: text; -webkit-text-fill-color: transparent;"><i class="fas fa-folder-open"></i> Мои отряды</h2>
1015
  <button class="btn" onclick="generateRandomTeam()" style="background: linear-gradient(135deg, #10b981, #059669);"><i class="fas fa-dice"></i> Рандомный</button>
1016
  </div>
1017
  <div id="myteams-content">
@@ -1019,9 +960,9 @@
1019
  </div>
1020
  </div>
1021
 
1022
- <!-- Профили GOOD -->
1023
  <div id="view-profiles" class="view-panel">
1024
- <h2 style="font-size:26px; font-weight:800; margin-bottom:24px; background: linear-gradient(135deg, #fff, #94a3b8); -webkit-background-clip: text; -webkit-text-fill-color: transparent;"><i class="fas fa-id-card"></i> Профили GOOD</h2>
1025
  <div id="profiles-content">
1026
  <div style="display:flex; gap:16px; margin-bottom:24px; flex-wrap:wrap;">
1027
  <button class="btn" onclick="createNewProfile()"><i class="fas fa-plus"></i> Новый профиль</button>
@@ -1033,7 +974,7 @@
1033
 
1034
  <!-- Настройки -->
1035
  <div id="view-settings" class="view-panel">
1036
- <h2 style="font-size:26px; font-weight:800; margin-bottom:24px; background: linear-gradient(135deg, #fff, #94a3b8); -webkit-background-clip: text; -webkit-text-fill-color: transparent;"><i class="fas fa-cog"></i> Настройки</h2>
1037
 
1038
  <div class="settings-section">
1039
  <h3 style="font-size:20px; margin-bottom:20px;">🤖 AI‑провайдер</h3>
@@ -1053,7 +994,7 @@
1053
  <div class="settings-label">🔑 Токен Hugging Face</div>
1054
  <div class="settings-description"><a href="https://huggingface.co/settings/tokens/new?ownUserPermissions=inference.serverless.write&tokenType=fineGrained" target="_blank" style="color:var(--accent);">Получить токен</a></div>
1055
  </div>
1056
- <input type="password" id="hf-api-key" placeholder="hf_..." value="" style="width:240px;">
1057
  </div>
1058
  <div class="settings-row">
1059
  <div><div class="settings-label">🧠 Модель</div></div>
@@ -1073,7 +1014,7 @@
1073
  <div class="settings-label">🔑 OpenAI API ключ</div>
1074
  <div class="settings-description"><a href="https://platform.openai.com/api-keys" target="_blank" style="color:var(--accent);">Получить ключ</a></div>
1075
  </div>
1076
- <input type="password" id="openai-api-key" placeholder="sk-..." value="" style="width:240px;">
1077
  </div>
1078
  <div class="settings-row">
1079
  <div><div class="settings-label">🧠 Модель</div></div>
@@ -1091,7 +1032,7 @@
1091
  <div class="settings-label">🔑 Gemini API ключ</div>
1092
  <div class="settings-description"><a href="https://aistudio.google.com/apikeys" target="_blank" style="color:var(--accent);">Получить ключ</a></div>
1093
  </div>
1094
- <input type="password" id="gemini-api-key" placeholder="AIza..." value="" style="width:240px;">
1095
  </div>
1096
  <div class="settings-row">
1097
  <div><div class="settings-label">🧠 Модель</div></div>
@@ -1109,7 +1050,7 @@
1109
  <div class="settings-label">🔑 DeepSeek API ключ</div>
1110
  <div class="settings-description"><a href="https://platform.deepseek.com/api_keys" target="_blank" style="color:var(--accent);">Получить ключ</a></div>
1111
  </div>
1112
- <input type="password" id="deepseek-api-key" placeholder="sk-..." value="" style="width:240px;">
1113
  </div>
1114
  <div class="settings-row">
1115
  <div><div class="settings-label">🧠 Модель</div></div>
@@ -1124,7 +1065,7 @@
1124
  <div id="settings-custom" style="display:none;">
1125
  <div class="settings-row">
1126
  <div><div class="settings-label">🔑 Custom ключ</div></div>
1127
- <input type="password" id="custom-api-key" placeholder="ключ" value="" style="width:240px;">
1128
  </div>
1129
  <div class="settings-row">
1130
  <div><div class="settings-label">🌐 Base URL</div></div>
@@ -1132,13 +1073,13 @@
1132
  </div>
1133
  <div class="settings-row">
1134
  <div><div class="settings-label">🧠 Model</div></div>
1135
- <input type="text" id="custom-model" placeholder="model-name" value="" style="width:240px;">
1136
  </div>
1137
  </div>
1138
  </div>
1139
 
1140
  <div style="display:flex; gap:16px; margin-top:20px;">
1141
- <button class="btn" onclick="saveAllApiKeys()"><i class="fas fa-save"></i> Сохранить ключи</button>
1142
  <button class="btn btn-outline" onclick="testCurrentApi()"><i class="fas fa-check-circle"></i> Проверить</button>
1143
  </div>
1144
  <div id="api-test-result" style="margin-top:16px; font-size:13px;"></div>
@@ -1148,7 +1089,8 @@
1148
  <h3 style="font-size:20px; margin-bottom:20px;">🎨 Интерфейс</h3>
1149
  <div class="settings-row">
1150
  <div>
1151
- <div class="settings-label">🌓 Тема оформления</div>
 
1152
  </div>
1153
  <select id="theme-selector" onchange="changeTheme(this.value)" style="width:160px;">
1154
  <option value="dark">Тёмная</option>
@@ -1156,28 +1098,25 @@
1156
  <option value="light">Светлая</option>
1157
  </select>
1158
  </div>
1159
- <div class="settings-row">
1160
- <div>
1161
- <div class="settings-label">🗣️ Язык интерфейса</div>
1162
- </div>
1163
- <select disabled style="width:160px; opacity:0.7;">
1164
- <option selected>Русский</option>
1165
- </select>
1166
- </div>
1167
  </div>
1168
 
1169
  <div class="settings-section">
1170
  <h3 style="font-size:20px; margin-bottom:20px;">💾 Данные</h3>
1171
  <div class="settings-row">
1172
- <div>
1173
- <div class="settings-label">🗄️ Управление базой данных</div>
1174
- </div>
1175
  <div style="display:flex; gap:12px;">
1176
  <button class="btn" style="background:linear-gradient(135deg, #ef4444, #b91c1c);" onclick="clearDB()"><i class="fas fa-trash-alt"></i> Сбросить</button>
1177
  <button class="btn" onclick="exportGOOD()"><i class="fas fa-download"></i> Экспорт</button>
1178
  </div>
1179
  </div>
1180
  </div>
 
 
 
 
 
 
 
1181
  </div>
1182
  </div>
1183
  </main>
@@ -1185,12 +1124,12 @@
1185
  <!-- AI оверлей -->
1186
  <div id="ai-overlay">
1187
  <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
1188
- <h3 style="font-size:24px; background: linear-gradient(135deg, #fff, var(--accent)); -webkit-background-clip: text; -webkit-text-fill-color: transparent;"><i class="fas fa-microchip"></i> AI‑анализ персонажа</h3>
1189
  <button style="background:none; border:none; color:#fff; font-size:32px; cursor:pointer;" onclick="closeAI()">&times;</button>
1190
  </div>
1191
  <div id="ai-toc" class="toc" style="display: none;"></div>
1192
  <div id="ai-text" style="font-size:15px; line-height:1.6; color:var(--text-soft); overflow-y: auto; padding-right: 6px;">
1193
- 💡 Нажмите кнопку «✨ Анализ» у персонажа.
1194
  </div>
1195
  <div id="ai-loader" class="loader" style="margin: 40px auto; display: none;"></div>
1196
  </div>
@@ -1287,7 +1226,7 @@
1287
  'geo_dmg_': '🪨 Гео урон%'
1288
  };
1289
 
1290
- // ==================== ЛОКАЛЬНЫЕ ДАННЫЕ ПЕРСОНАЖЕЙ ====================
1291
  const LOCAL_CHARACTERS = [
1292
  { name: "Арлекино", icon: "pic/arlecchino.png", element: "Pyro" },
1293
  { name: "Нёвиллет", icon: "pic/neuvillette.png", element: "Hydro" },
@@ -1311,7 +1250,6 @@
1311
  { name: "Шэнь Хэ", icon: "pic/shenhe.png", element: "Cryo" }
1312
  ];
1313
 
1314
- // Маппинг английских имён на русские
1315
  const nameMapping = {
1316
  'Arlecchino': 'Арлекино',
1317
  'Furina': 'Фурина',
@@ -1355,66 +1293,48 @@
1355
 
1356
  // ==================== МЕТА ОТРЯДЫ ====================
1357
  const metaTeams = [
1358
- {
1359
- name: '🌊 Пузырьковый реактор',
1360
- fullName: 'Фурина + Нёвиллет',
1361
- chars: ['Furina','Neuvillette','Kazuha','Zhongli'],
1362
- resonance: 'Гидро резонанс: +25% HP',
1363
- buffs: 'Фурина: +75% урон, Нёвиллет: ДД, Кадзуха: +40% элемента, Чжун Ли: -20% резист',
1364
- reactions: 'Пар + Заряжен',
1365
- meta: '6.3+',
1366
- description: 'Топ-1 мета-отряд. Нёвиллет наносит колоссальный урон.'
1367
- },
1368
- {
1369
- name: '🔥 Перегрузка',
1370
- fullName: 'Арлекино + Шеврёз',
1371
- chars: ['Arlecchino','Bennett','Chevreuse','Fischl'],
1372
- resonance: 'Пиро резонанс: +25% ATK',
1373
- buffs: 'Арлекино: ДД, Беннет: +1000 ATK, Шеврёз: -40% резист',
1374
- reactions: 'Перегрузка',
1375
- meta: '6.3+',
1376
- description: 'Мощный взрывной урон.'
1377
- },
1378
- {
1379
- name: '🌱 Гиперблум',
1380
- fullName: 'Нахида + Фурина + Куки',
1381
- chars: ['Nahida','Furina','KukiShinobu','Zhongli'],
1382
- resonance: 'Дендро +80 EM, Гидро +25% HP',
1383
- buffs: 'Нахида: +250 EM, Фурина: бафф, Куки: триггер',
1384
- reactions: 'Гиперблум',
1385
- meta: '6.3+',
1386
- description: 'Стабильный урон через ядра.'
1387
- },
1388
- {
1389
- name: '❄️ Моно-Крио',
1390
- fullName: 'Ризли + Шэнь Хэ',
1391
- chars: ['Wriothesley','Shenhe','Kazuha','Kokomi'],
1392
- resonance: 'Крио резонанс: +15% CR',
1393
- buffs: 'Ризли: ДД, Шэнь Хэ: бафф крио',
1394
- reactions: 'Заморозка',
1395
- meta: '6.3+',
1396
- description: 'Ризли бьёт быстро и сильно.'
1397
- },
1398
- {
1399
- name: '🪨 Геодар',
1400
- fullName: 'Навия + Чжун Ли',
1401
- chars: ['Navia','Zhongli','Bennett','Xiangling'],
1402
- resonance: 'Гео +15% урон, Пиро +25% ATK',
1403
- buffs: 'Навия: ДД, Чжун Ли: щит',
1404
- reactions: 'Кристалл',
1405
- meta: '6.3+',
1406
- description: 'Навия — дробовик.'
1407
- },
1408
- {
1409
- name: '⚡ Стимуляция',
1410
- fullName: 'Клоринда + Нахида',
1411
- chars: ['Clorinde','Nahida','Fischl','Zhongli'],
1412
- resonance: 'Электро резонанс',
1413
- buffs: 'Клоринда: ДД, Нахида: EM',
1414
- reactions: 'Стимуляция',
1415
- meta: '6.3+',
1416
- description: 'Клоринда — тир-1 ДД.'
1417
- }
1418
  ];
1419
 
1420
  // ==================== ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ====================
@@ -1461,16 +1381,7 @@
1461
  return 'anemo';
1462
  }
1463
 
1464
- function getCV(art) {
1465
- if (!art.substats) return 0;
1466
- return art.substats.reduce((acc, s) => {
1467
- if (s.key === 'critRate_') return acc + s.value * 2;
1468
- if (s.key === 'critDMG_') return acc + s.value;
1469
- return acc;
1470
- }, 0);
1471
- }
1472
-
1473
- // ==================== AI ФУНКЦИИ ====================
1474
  async function askAI(prompt, useGoogleSearch = false) {
1475
  let apiKey = '', baseURL = '', model = '';
1476
  switch(currentProvider) {
@@ -1534,7 +1445,7 @@
1534
  deepseekModel = document.getElementById('deepseek-model').value;
1535
  localStorage.setItem('nexus_deepseek_model', deepseekModel);
1536
 
1537
- document.getElementById('api-test-result').innerHTML = '✅ Настройки сохранены';
1538
  }
1539
 
1540
  async function testCurrentApi() {
@@ -1545,7 +1456,7 @@
1545
  if (!key) return alert('🔑 Введите API ключ');
1546
  document.getElementById('api-test-result').innerHTML = '⏳ Проверка...';
1547
  try {
1548
- await askAI('Ответь "OK" одной строкой.', false);
1549
  document.getElementById('api-test-result').innerHTML = '✅ Подключение работает, ключ действителен';
1550
  } catch (e) {
1551
  document.getElementById('api-test-result').innerHTML = `❌ Ошибка: ${e.message}`;
@@ -1582,7 +1493,7 @@
1582
  resultDiv.innerHTML = '<div style="display:flex; align-items:center; gap:12px;"><div class="loader" style="display:block;"></div> 🧠 AI подбирает отряды...</div>';
1583
 
1584
  const charList = localData.characters.map(c => getRussianName(c.key)).join(', ');
1585
- const prompt = `У меня есть персонажи: ${charList}. Составь 3 лучших отряда. Используй строгий шаблон:\n## Название отряда\n**Состав:** персонаж1, персонаж2, персонаж3, персонаж4\n**Резонанс:** ...\n**Реакции:** ...\n**Баффы:** ...\n**Описание:** ...`;
1586
 
1587
  try {
1588
  const advice = await askAI(prompt, document.getElementById('googleSearchCheckbox')?.checked || false);
@@ -1617,13 +1528,13 @@
1617
  </div>`;
1618
  });
1619
  html += `</div>`;
1620
- html += `<div style="display:grid; grid-template-columns:repeat(3,1fr); gap:16px; background:rgba(0,0,0,0.25); border-radius:20px; padding:20px;">
1621
  <div><span style="color:var(--accent);">🎵 Резонанс:</span> ${team.resonance || '—'}</div>
1622
  <div><span style="color:var(--accent);">⚡ Реакции:</span> ${team.reactions || '—'}</div>
1623
  <div><span style="color:var(--accent);">✨ Баффы:</span> ${team.buffs || '—'}</div>
1624
  </div>`;
1625
- if (team.description) html += `<div style="margin-top:16px; color:var(--text-soft); padding:16px; background:rgba(0,0,0,0.15); border-radius:20px;">💬 ${team.description}</div>`;
1626
- html += `<div style="margin-top:16px;"><button class="btn btn-outline" onclick="saveTeamFromAI('${team.title}', ${JSON.stringify(team.composition)})"><i class="fas fa-save"></i> Сохранить отряд</button></div>`;
1627
  html += `</div>`;
1628
  });
1629
  resultDiv.innerHTML = html;
@@ -1634,23 +1545,19 @@
1634
 
1635
  // ==================== СОХРАНЕНИЕ ОТРЯДА ====================
1636
  window.saveTeamFromAI = function(title, composition) {
1637
- const team = {
1638
- id: Date.now(),
1639
- name: title,
1640
- characters: composition,
1641
- date: new Date().toLocaleDateString('ru-RU')
1642
- };
1643
  myTeams.push(team);
1644
  localStorage.setItem('nexus_myteams', JSON.stringify(myTeams));
1645
  renderMyTeams();
1646
  alert('✅ Отряд сохранён в «Мои отряды»');
1647
  };
1648
 
 
1649
  function renderMyTeams() {
1650
  const container = document.getElementById('myteams-grid');
1651
  if (!container) return;
1652
  if (myTeams.length === 0) {
1653
- container.innerHTML = '<div class="empty"><i class="fas fa-folder-open"></i> Нет сохранённых отрядов. Используйте AI‑подбор.</div>';
1654
  return;
1655
  }
1656
  container.innerHTML = myTeams.map(team => {
@@ -1670,7 +1577,7 @@
1670
  </div>
1671
  <div style="margin-top:12px; color:var(--text-dim); font-size:12px;">${team.date || ''}</div>
1672
  <div style="display:flex; gap:8px; margin-top:12px;">
1673
- <button class="btn btn-outline" style="padding:4px 12px; font-size:12px;" onclick="deleteTeam(${team.id})"><i class="fas fa-trash-alt"></i></button>
1674
  </div>
1675
  </div>`;
1676
  }).join('');
@@ -1732,17 +1639,17 @@
1732
  function renderProfiles() {
1733
  const container = document.getElementById('profiles-list');
1734
  if (!container) return;
1735
- if (profiles.length === 0) { container.innerHTML = '<div class="empty"><i class="fas fa-id-card"></i> Нет сохранённых профилей.</div>'; return; }
1736
  container.innerHTML = profiles.map(p => {
1737
  const isActive = p.id == activeProfile;
1738
  return `<div class="profile-badge" style="justify-content:space-between; ${isActive ? 'background:rgba(139,92,246,0.3); border-color:var(--accent);' : ''}">
1739
  <div style="display:flex; align-items:center; gap:12px;">
1740
- <span style="font-size:20px;"><i class="fas fa-id-card"></i></span>
1741
  <div><span style="font-weight:700;">${p.name}</span> <span style="font-size:12px;">${p.date || ''}</span></div>
1742
  </div>
1743
  <div style="display:flex; gap:8px;">
1744
- <button class="btn btn-outline" style="padding:6px 12px;" onclick="loadProfile(${p.id})"><i class="fas fa-folder-open"></i></button>
1745
- <button class="btn btn-outline" style="padding:6px 12px;" onclick="deleteProfile(${p.id})"><i class="fas fa-trash-alt"></i></button>
1746
  </div>
1747
  </div>`;
1748
  }).join('');
@@ -1776,7 +1683,7 @@
1776
  if (inList) html += '</ul>';
1777
  if (toc.length > 1) {
1778
  let tocHtml = '<div class="toc"><strong>📖 Содержание:</strong><div style="display:flex; flex-wrap:wrap; gap:10px; margin-top:8px;">';
1779
- toc.forEach(item => { tocHtml += `<a href="#${item.id}" style="color:var(--accent); background:rgba(139,92,246,0.1); padding:4px 12px; border-radius:40px;">${item.title}</a>`; });
1780
  tocHtml += '</div></div>';
1781
  return { html: tocHtml + html, toc: tocHtml };
1782
  }
@@ -1845,9 +1752,6 @@
1845
  function clearDB() { if (confirm('Удалить все данные?')) { dbInstance.transaction("app", "readwrite").objectStore("app").clear(); localData = null; renderChars(); location.reload(); } }
1846
 
1847
  // ==================== ОБОГАЩЕНИЕ ДАННЫХ ====================
1848
- const charElements = {};
1849
- const roles = {};
1850
-
1851
  function enrichCharData() {
1852
  if (!localData?.characters) return;
1853
  localData.characters.forEach(c => {
@@ -1862,7 +1766,18 @@
1862
  });
1863
  }
1864
 
 
 
 
 
 
 
 
 
 
 
1865
  // ==================== РЕНДЕР ПЕРСОНАЖЕЙ ====================
 
1866
  function renderChars() {
1867
  if (!localData) { document.getElementById('empty-msg').style.display = 'block'; return; }
1868
  renderTable(); renderCards();
@@ -1914,9 +1829,9 @@
1914
  <td><div style="display:flex; align-items:center; gap:12px;">
1915
  <img src="${icon}" style="width:44px; height:44px; border-radius:50%; border:2px solid ${elementColor}; object-fit:cover;" onerror="this.onerror=null;this.src='https://placehold.co/44x44/4A4A4A/FFFFFF?text=${rusName.charAt(0)}';">
1916
  <div><a href="https://www.google.com/search?q=Genshin+Impact+${encodeURIComponent(rusName)}+гайд" target="_blank" style="font-size:16px; font-weight:700; color:#fff;">${rusName}</a>
1917
- <span style="background:rgba(0,0,0,0.4); padding:4px 10px; border-radius:20px; font-size:12px; margin-left:8px;">Ур.${c.level} · С${c.constellation}</span></div>
1918
  </div></td>
1919
- <td><span class="weapon-badge"><i class="fas fa-shield-alt"></i> ${weaponDisplay}</span><br><span class="role-badge" style="margin-top:6px;">🎯 ${roleDisplay}</span></td>
1920
  <td><span style="color:var(--text-soft);">${setsDisplay}</span></td>
1921
  <td><span style="font-size:18px; font-weight:800;">${c.cv.toFixed(1)}</span>
1922
  <div class="cv-meter"><div class="cv-fill" style="width:${Math.min(c.cv/2.2,100)}%; background:${cvColor};"></div></div>
@@ -1967,8 +1882,8 @@
1967
  <div><div class="char-name"><a href="https://www.google.com/search?q=Genshin+Impact+${encodeURIComponent(rusName)}+гайд" target="_blank">${rusName}</a></div>
1968
  <span class="char-level">⭐ Ур.${c.level} · Созвездие ${c.constellation}</span></div>
1969
  </div>
1970
- <div class="char-card-stats"><span class="weapon-badge"><i class="fas fa-shield-alt"></i> ${weaponDisplay}</span><span class="role-badge">🎯 ${roleDisplay}</span></div>
1971
- <div class="char-card-sets"><span style="color:var(--text-dim);"><i class="fas fa-gem"></i> Сеты:</span> ${setsDisplay}</div>
1972
  <div style="display:flex; justify-content:space-between; align-items:center;">
1973
  <div><span style="font-size:22px; font-weight:800;">${c.cv.toFixed(1)}</span>
1974
  <span style="font-size:12px; color:${cvColor}; margin-left:6px;">${cvStatus}</span>
@@ -2011,7 +1926,7 @@
2011
  if (groupBy === 'slot') groupName = slotNames[key] || key;
2012
  if (groupBy === 'rarity') groupName = `${key}★`;
2013
  if (groupBy === 'level') groupName = `+${key} и выше`;
2014
- html += `<h3 style="margin:16px 0 8px; color:var(--text-soft);"><i class="fas fa-layer-group"></i> ${groupName} (${groups[key].length})</h3>`;
2015
  html += renderArtifactList(groups[key]);
2016
  }
2017
  container.innerHTML = html;
@@ -2031,7 +1946,7 @@
2031
  return `<div class="artifact-card" style="flex-direction:column; align-items:stretch;">
2032
  <div style="display:flex; justify-content:space-between;"><div><b>${setRus}</b> · ${slotRus}</div><div class="artifact-mainstat">${mainStatRus} +${art.level}</div></div>
2033
  <div style="display:flex; gap:10px; flex-wrap:wrap;">${art.substats.map(s => `<span style="background:rgba(255,255,255,0.06); border:1px solid var(--border); padding:6px 14px; border-radius:30px; font-size:12px;">${statNames[s.key] || s.key} +${s.value}</span>`).join('')}</div>
2034
- <div style="display:flex; justify-content:space-between; color:var(--text-dim);"><span>CV: <b>${cv.toFixed(1)}</b></span><span><i class="fas fa-user"></i> ${loc}</span></div>
2035
  </div>`;
2036
  }).join('');
2037
  } else {
@@ -2046,7 +1961,7 @@
2046
  <div class="artifact-mainstat">${mainStatRus} +${art.level}</div>
2047
  <div style="flex:1; display:flex; gap:8px; flex-wrap:wrap;">${art.substats.map(s => `<span style="background:rgba(255,255,255,0.06); border:1px solid var(--border); padding:6px 12px; border-radius:30px; font-size:11px;">${statNames[s.key] || s.key} +${s.value}</span>`).join('')}</div>
2048
  <div>CV: <b>${cv.toFixed(1)}</b></div>
2049
- <div style="color:var(--text-dim);"><i class="fas fa-user"></i> ${loc}</div>
2050
  </div>`;
2051
  }).join('');
2052
  }
@@ -2077,15 +1992,15 @@
2077
  ${t.chars.map((c,i) => { const ownedHere = hasChars[i]; const rusName = getRussianName(c);
2078
  return `<span style="display:inline-flex; align-items:center; gap:8px; background:${ownedHere ? 'rgba(16,185,129,0.15)' : 'rgba(239,68,68,0.15)'}; border:1px solid ${ownedHere ? 'rgba(16,185,129,0.3)' : 'rgba(239,68,68,0.3)'}; padding:6px 16px; border-radius:30px;">${rusName} ${ownedHere ? '✅' : '❌'}</span>`; }).join('')}
2079
  </div>
2080
- <div style="display:grid; grid-template-columns:repeat(3,1fr); gap:16px; background:rgba(0,0,0,0.25); border-radius:20px; padding:16px;">
2081
  <div><span style="color:var(--accent);">🎵 Резонанс:</span> ${t.resonance}</div>
2082
  <div><span style="color:var(--accent);">⚡ Реакции:</span> ${t.reactions}</div>
2083
  <div><span style="color:var(--accent);">✨ Баффы:</span> ${t.buffs}</div>
2084
  </div>
2085
- <div style="margin-top:16px; color:var(--text-soft); background:rgba(0,0,0,0.15); padding:16px; border-radius:20px;">💬 ${t.description}</div>
2086
  <div style="margin-top:16px; display:flex; justify-content:space-between; align-items:center;">
2087
- <span><i class="fas fa-star"></i> В наличии: <b>${ownedCount}/${t.chars.length}</b> ${missing.length ? `<span style="color:#ff9999;">❌ Нет: ${missing.join(', ')}</span>` : '<span style="color:#10b981;">✅ Полный отряд</span>'}</span>
2088
- <button class="btn btn-outline" onclick="saveTeamFromAI('${t.fullName}', ${JSON.stringify(t.chars)})"><i class="fas fa-save"></i> Сохранить</button>
2089
  </div>
2090
  </div>`;
2091
  }).join('');
@@ -2093,7 +2008,7 @@
2093
 
2094
  // ==================== ОЦЕНКА АККАУНТА ====================
2095
  function renderAssessment() {
2096
- if (!localData?.characters) { document.getElementById('assessment-content').innerHTML = '<div class="empty">Загрузите GOOD‑файл для оценки.</div>'; return; }
2097
  const chars = localData.characters;
2098
  const arts = localData.artifacts || [];
2099
  const chars80 = chars.filter(c => c.level >= 80).length;
@@ -2127,27 +2042,27 @@
2127
 
2128
  const html = `
2129
  <div class="assessment-card" style="border-left-color: var(--accent);">
2130
- <h3 style="font-size:24px;"><i class="fas fa-chart-pie"></i> Общая статистика</h3>
2131
  <div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(180px,1fr)); gap:20px; margin-top:20px;">
2132
- <div style="background:rgba(0,0,0,0.2); padding:20px; border-radius:20px;"><div style="font-size:14px; color:var(--text-dim);"><i class="fas fa-users"></i> Персонажи</div><div style="font-size:36px; font-weight:800;">${chars.length}</div></div>
2133
- <div style="background:rgba(0,0,0,0.2); padding:20px; border-radius:20px;"><div style="font-size:14px; color:var(--text-dim);"><i class="fas fa-level-up-alt"></i> Уровень 80+</div><div style="font-size:36px; font-weight:800;">${chars80}</div><div style="font-size:13px;">90 ур: ${chars90}</div></div>
2134
- <div style="background:rgba(0,0,0,0.2); padding:20px; border-radius:20px;"><div style="font-size:14px; color:var(--text-dim);"><i class="fas fa-chart-line"></i> Средний CV</div><div style="font-size:36px; font-weight:800;">${avgCV.toFixed(1)}</div></div>
2135
- <div style="background:rgba(0,0,0,0.2); padding:20px; border-radius:20px;"><div style="font-size:14px; color:var(--text-dim);"><i class="fas fa-gem"></i> Артефакты +20</div><div style="font-size:36px; font-weight:800;">${arts20}</div></div>
2136
  </div>
2137
  </div>
2138
  <div class="assessment-card" style="border-left-color: var(--pyro);">
2139
- <div style="display:flex; justify-content:space-between;"><h3 style="font-size:24px;"><i class="fas fa-skull"></i> Бездна 12-3</h3><span style="font-size:42px; font-weight:800; color:${abyssScore>=80?'var(--success)':abyssScore>=60?'var(--warning)':'var(--error)'};">${abyssScore}</span></div>
2140
  <div style="font-size:22px; font-weight:800; margin:12px 0 4px;">${abyssResult}</div>
2141
- <div style="background:rgba(0,0,0,0.2); border-radius:20px; padding:16px; margin-top:16px;">
2142
- <div><i class="fas fa-shield-alt"></i> Топ-саппорты: ${ownedSupports}/8</div>
2143
- <div><i class="fas fa-tachometer-alt"></i> Топ-ДД: ${ownedDPS}/10</div>
2144
- <div><i class="fas fa-chart-line"></i> CV: ${avgCV > 160 ? 'отличный' : avgCV > 140 ? 'хороший' : avgCV > 120 ? 'средний' : 'низкий'}</div>
2145
  </div>
2146
  </div>
2147
  <div class="assessment-card" style="border-left-color: var(--cryo);">
2148
- <div style="display:flex; justify-content:space-between;"><h3 style="font-size:24px;"><i class="fas fa-cloud-moon"></i> Мрачный натиск</h3><span style="font-size:42px; font-weight:800; color:${mgScore>=80?'var(--success)':mgScore>=65?'var(--warning)':'var(--error)'};">${mgScore}</span></div>
2149
  <div style="font-size:22px; font-weight:800; margin:12px 0 4px;">${mgResult}</div>
2150
- <div style="margin-top:16px; color:var(--text-dim);"><i class="fas fa-info-circle"></i> Рекомендованы: Арлекино, Нёвиллет, Навия, Клоринда, Ризли</div>
2151
  </div>
2152
  `;
2153
  document.getElementById('assessment-content').innerHTML = html;
@@ -2157,7 +2072,7 @@
2157
  function applyFilters() { renderChars(); }
2158
  function setCharsView(view) { charsView = view; localStorage.setItem('nexus_chars_view', view); document.querySelectorAll('#view-chars .view-toggle-btn').forEach(btn => btn.classList.toggle('active', btn.dataset.view === view)); renderChars(); }
2159
  function setArtifactsView(view) { artifactsView = view; localStorage.setItem('nexus_artifacts_view', view); document.querySelectorAll('#view-artifacts .view-toggle-btn').forEach(btn => btn.classList.toggle('active', btn.dataset.view === view)); renderArtifacts(); }
2160
- function changeTheme(theme) {
2161
  const root = document.documentElement;
2162
  if (theme === 'light') {
2163
  root.style.setProperty('--bg-main', '#f3f4f6');
@@ -2177,7 +2092,7 @@
2177
  } else {
2178
  location.reload();
2179
  }
2180
- }
2181
 
2182
  // ==================== ИМПОРТ GOOD ====================
2183
  document.getElementById('fileIn').onchange = function(e) {
@@ -2188,26 +2103,16 @@
2188
  const newData = JSON.parse(ev.target.result);
2189
  if (confirm('Сохранить в новый профиль? (Отмена — заменить текущие)')) {
2190
  const profileName = prompt('Название профиля:', `Профиль ${profiles.length + 1}`);
2191
- if (profileName) {
2192
- profiles.push({ id: Date.now(), name: profileName, data: newData, date: new Date().toLocaleDateString('ru-RU') });
2193
- localStorage.setItem('nexus_profiles', JSON.stringify(profiles));
2194
- }
2195
- }
2196
- localData = newData;
2197
- if (localData.artifacts) {
2198
- const sets = [...new Set(localData.artifacts.map(a => a.setKey))].sort();
2199
- const setSelect = document.getElementById('art-set-filter');
2200
- setSelect.innerHTML = '<option value="all">Все сеты</option>' + sets.map(s => `<option value="${s}">${artifactSetNames[s] || s}</option>`).join('');
2201
  }
2202
- enrichCharData(); saveToDB(); renderChars(); renderAssessment();
2203
- switchTab('chars');
2204
- alert('✅ Данные загружены!');
2205
  } catch(ex) { alert('❌ Неверный GOOD‑файл'); }
2206
  };
2207
  reader.readAsText(file);
2208
  };
2209
 
2210
- // ==================== ЭКСПОРТ ====================
2211
  function exportGOOD() { if (!localData) return alert('Нет данных'); const dataStr = JSON.stringify(localData, null, 2); const blob = new Blob([dataStr], {type:'application/json'}); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'genshin_nexus_export.json'; a.click(); }
2212
 
2213
  // ==================== ПОПОВЕРЫ ====================
@@ -2238,6 +2143,7 @@
2238
  renderProfiles(); renderMyTeams();
2239
  }
2240
 
 
2241
  window.switchTab = switchTab;
2242
  window.analyzeCharacter = analyzeCharacter;
2243
  window.aiTeamBuilder = aiTeamBuilder;
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.5, user-scalable=yes">
6
+ <title>Genshin Nexus · BETA Опти</title>
7
  <!-- Font Awesome 6 (нормальные иконки) -->
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
9
  <style>
 
42
  overflow: hidden;
43
  }
44
 
45
+ /* Скроллбар простой */
46
+ ::-webkit-scrollbar { width: 6px; height: 6px; }
47
  ::-webkit-scrollbar-track { background: transparent; }
48
+ ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
49
  ::-webkit-scrollbar-thumb:hover { background: var(--accent); }
50
 
51
+ /* Попапы без блюра */
52
  .blur-popup {
53
  position: fixed;
54
  top: 0;
55
  left: 0;
56
  right: 0;
57
  bottom: 0;
58
+ background: rgba(0, 0, 0, 0.7);
 
 
59
  z-index: 3000;
60
  display: none;
61
  align-items: center;
 
65
  .popup-content {
66
  background: var(--bg-card);
67
  border: 1px solid var(--border);
68
+ border-radius: 12px;
69
  padding: 28px;
70
  max-width: 600px;
71
  width: 90%;
72
  max-height: 80vh;
73
  overflow-y: auto;
74
+ box-shadow: 0 10px 30px rgba(0,0,0,0.5);
75
  position: relative;
76
  }
77
  .popup-close {
 
85
  }
86
  .popup-close:hover { color: #fff; }
87
 
88
+ /* АДАПТИВНЫЙ ДИЗАЙН */
89
  @media (max-width: 1024px) {
90
  aside { width: 80px; }
91
  .side-header h1 { font-size: 0; background: none; -webkit-text-fill-color: #fff; }
 
101
  .stats-strip { gap: 12px; }
102
  .stat-unit b { font-size: 16px; }
103
  .content-area { padding: 16px; }
 
 
 
 
 
104
  }
105
 
106
  @media (max-width: 768px) {
 
112
  header { flex-wrap: wrap; height: auto; padding: 16px; }
113
  .stats-strip { flex-wrap: wrap; justify-content: center; }
114
  .content-area { padding: 16px; }
 
 
 
 
 
115
  }
116
 
117
+ /* Сайдбар */
118
  aside {
119
  width: 280px;
120
  background: var(--bg-side);
 
122
  display: flex;
123
  flex-direction: column;
124
  flex-shrink: 0;
 
125
  transition: 0.3s;
126
  overflow-y: auto;
127
  }
 
129
  .side-header {
130
  padding: 28px 20px;
131
  border-bottom: 1px solid var(--border);
 
132
  }
133
  .side-header h1 {
134
  font-size: 24px;
135
  font-weight: 800;
 
136
  background: linear-gradient(135deg, #fff, var(--accent));
137
  -webkit-background-clip: text;
138
  -webkit-text-fill-color: transparent;
 
146
  .nav-group { padding: 20px 16px; flex-grow: 1; overflow-y: auto; }
147
  .nav-item {
148
  padding: 12px 18px;
149
+ border-radius: 8px;
150
  cursor: pointer;
151
  margin-bottom: 6px;
152
  font-size: 15px;
 
159
  border: 1px solid transparent;
160
  }
161
  .nav-item:hover {
162
+ background: rgba(255,255,255,0.05);
163
  color: #fff;
164
  border-color: var(--border);
165
  }
166
  .nav-item.active {
167
  background: linear-gradient(135deg, var(--accent), #a78bfa);
168
  color: #fff;
 
169
  border: none;
170
  }
171
 
 
185
  }
186
  select, .filter-section input {
187
  width: 100%;
188
+ background: rgba(0,0,0,0.3);
189
  border: 1px solid var(--border);
190
  color: #fff;
191
  padding: 10px 14px;
192
+ border-radius: 8px;
193
  font-size: 13px;
194
  margin-bottom: 16px;
195
  cursor: pointer;
 
207
  display: flex;
208
  flex-direction: column;
209
  overflow: hidden;
 
210
  }
211
 
212
  header {
213
  height: 70px;
214
+ background: var(--bg-side);
 
 
215
  border-bottom: 1px solid var(--border);
216
  display: flex;
217
  align-items: center;
 
231
  .stat-unit b {
232
  font-size: 20px;
233
  font-weight: 800;
234
+ color: #fff;
 
 
235
  }
236
 
237
  .content-area {
 
242
  scroll-behavior: smooth;
243
  }
244
 
245
+ /* Таблица */
246
  .data-table {
247
  width: 100%;
248
  border-collapse: separate;
249
+ border-spacing: 0 8px;
250
  }
251
  .data-table th {
252
  text-align: left;
 
256
  text-transform: uppercase;
257
  letter-spacing: 1px;
258
  font-weight: 700;
 
259
  }
260
  .data-row {
261
+ border-radius: 8px;
262
  transition: 0.2s;
263
+ background: var(--bg-card);
 
 
 
264
  border: 1px solid var(--border);
265
  }
266
  .data-row td {
 
268
  vertical-align: middle;
269
  }
270
  .data-row:hover {
271
+ background: var(--bg-card-hover);
272
  border-color: var(--accent);
273
  }
274
 
275
+ /* Карточки персонажей */
276
  .chars-grid {
277
  display: grid;
278
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
279
  gap: 20px;
280
  }
281
  .char-card {
282
+ background: var(--bg-card);
283
+ border-radius: 8px;
 
284
  padding: 24px;
285
  border: 1px solid var(--border);
286
  display: flex;
287
  flex-direction: column;
288
  gap: 16px;
289
+ transition: 0.2s;
 
290
  position: relative;
291
  overflow: hidden;
292
  }
 
298
  right: 0;
299
  height: 4px;
300
  background: linear-gradient(90deg, var(--element-color), var(--element-glow), transparent);
 
301
  }
302
  .char-card:hover {
 
303
  border-color: var(--element-color);
 
304
  }
305
  .char-card-header {
306
  display: flex;
 
316
  justify-content: center;
317
  font-size: 30px;
318
  border: 2px solid;
319
+ background: rgba(0,0,0,0.3);
 
 
320
  object-fit: cover;
321
  }
322
  .char-name {
323
  font-size: 20px;
324
  font-weight: 800;
325
  margin-bottom: 4px;
 
 
 
326
  }
327
  .char-name a {
328
  color: inherit;
329
  text-decoration: none;
 
330
  }
331
  .char-name a:hover {
332
  color: var(--accent);
 
335
  .char-level {
336
  color: var(--text-dim);
337
  font-size: 12px;
338
+ background: rgba(0,0,0,0.3);
339
  padding: 4px 10px;
340
+ border-radius: 4px;
341
  display: inline-block;
342
  border: 1px solid var(--border);
343
  }
 
345
  display: flex;
346
  justify-content: space-between;
347
  align-items: center;
348
+ background: rgba(0,0,0,0.2);
349
  padding: 14px 18px;
350
+ border-radius: 8px;
351
  border: 1px solid var(--border);
352
  }
353
  .weapon-badge {
354
+ background: rgba(255,255,255,0.05);
355
  padding: 6px 16px;
356
  border-radius: 30px;
357
  font-size: 12px;
 
374
  color: var(--text-soft);
375
  background: rgba(0,0,0,0.2);
376
  padding: 14px 18px;
377
+ border-radius: 8px;
378
  line-height: 1.5;
379
  border: 1px solid var(--border);
380
  }
381
 
382
+ /* Артефакты */
383
  .artifacts-grid {
384
  display: flex;
385
  flex-direction: column;
386
+ gap: 12px;
387
  }
388
  .artifact-card {
389
+ background: var(--bg-card);
390
+ border-radius: 8px;
 
391
  padding: 20px;
392
  display: flex;
393
  flex-wrap: wrap;
394
  align-items: center;
395
  gap: 20px;
396
  border: 1px solid var(--border);
 
397
  transition: 0.2s;
398
  }
399
  .artifact-card:hover {
400
  border-color: var(--accent);
 
 
401
  }
402
  .artifact-mainstat {
403
  font-weight: 700;
404
  color: #ffd966;
405
+ background: rgba(255, 217, 102, 0.1);
406
  padding: 6px 18px;
407
  border-radius: 30px;
408
  border: 1px solid rgba(255, 217, 102, 0.3);
409
  font-size: 13px;
410
  }
411
 
412
+ /* AI кнопка */
413
  .ai-btn {
414
  background: linear-gradient(135deg, #8b5cf6, #c084fc);
415
  border: none;
 
422
  display: flex;
423
  align-items: center;
424
  gap: 8px;
425
+ transition: 0.2s;
 
 
426
  border: 1px solid rgba(255,255,255,0.2);
427
  }
428
  .ai-btn:hover {
429
+ opacity: 0.9;
 
430
  }
431
 
432
+ /* CV-метр */
433
  .cv-meter {
434
  width: 100px;
435
+ height: 4px;
436
  background: rgba(255,255,255,0.1);
437
+ border-radius: 2px;
438
  overflow: hidden;
439
  margin-top: 6px;
440
  border: 1px solid var(--border);
 
442
  .cv-fill {
443
  height: 100%;
444
  transition: 0.5s;
445
+ border-radius: 2px;
446
  }
447
 
448
+ /* Карточки отрядов */
449
  .synergy-card {
450
+ background: var(--bg-card);
451
+ border-radius: 8px;
 
452
  padding: 28px;
453
  border: 1px solid var(--border);
454
  margin-bottom: 24px;
455
  position: relative;
456
  overflow: hidden;
 
457
  transition: 0.2s;
458
  }
459
  .synergy-card:hover {
460
+ border-color: var(--accent);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
461
  }
462
 
463
  .team-members {
 
470
  display: flex;
471
  flex-direction: column;
472
  align-items: center;
473
+ background: rgba(0,0,0,0.2);
474
  padding: 16px 14px;
475
+ border-radius: 8px;
476
  min-width: 100px;
 
477
  border: 1px solid var(--border);
478
  transition: 0.2s;
479
  }
480
  .team-member:hover {
481
+ background: rgba(139,92,246,0.1);
482
  border-color: var(--accent);
 
483
  }
484
  .member-icon {
485
  width: 64px;
 
492
  justify-content: center;
493
  font-size: 28px;
494
  margin-bottom: 10px;
 
495
  object-fit: cover;
496
  }
497
 
 
505
  cursor: pointer;
506
  font-size: 13px;
507
  transition: 0.2s;
 
508
  border: 1px solid rgba(255,255,255,0.1);
509
  }
510
  .btn:hover {
511
+ opacity: 0.9;
 
512
  }
513
  .btn-outline {
514
  background: transparent;
515
  border: 1px solid var(--border);
 
516
  }
517
  .btn-outline:hover {
518
+ background: rgba(255,255,255,0.05);
519
  border-color: var(--accent);
520
  }
521
 
 
534
  text-align: center;
535
  color: var(--text-dim);
536
  background: rgba(0,0,0,0.2);
537
+ border-radius: 8px;
 
538
  border: 1px solid var(--border);
539
  }
540
 
 
571
  cursor: pointer;
572
  margin-left: 6px;
573
  border: 1px solid var(--border);
 
574
  }
575
  .tooltip-icon:hover {
576
  background: var(--accent);
 
596
  background: linear-gradient(135deg, var(--accent), #a78bfa);
597
  color: #fff;
598
  border-color: transparent;
 
599
  }
600
 
601
  .assessment-card {
602
+ background: var(--bg-card);
603
+ border-radius: 8px;
 
604
  padding: 28px;
605
  margin-bottom: 24px;
606
  border-left: 6px solid;
607
  border: 1px solid var(--border);
 
608
  }
609
 
610
  #ai-overlay {
 
613
  top: 0;
614
  width: 520px;
615
  height: 100vh;
616
+ background: var(--bg-side);
 
 
617
  border-left: 1px solid var(--border);
618
  z-index: 2000;
619
+ transition: 0.3s ease;
620
  display: flex;
621
  flex-direction: column;
622
  padding: 32px;
623
+ box-shadow: -10px 0 20px rgba(0,0,0,0.5);
624
  }
625
  #ai-overlay.open { right: 0; }
626
 
 
634
  background: rgba(139,92,246,0.1);
635
  border-left: 4px solid var(--accent);
636
  padding: 14px 18px;
637
+ border-radius: 8px;
638
  margin-bottom: 20px;
639
  font-size: 13px;
640
  }
 
643
  text-decoration: none;
644
  margin-right: 14px;
645
  display: inline-block;
 
646
  }
647
  .toc a:hover {
648
  color: #fff;
 
649
  }
650
 
651
  .settings-section {
652
  background: rgba(0,0,0,0.2);
653
+ border-radius: 8px;
654
  padding: 24px;
655
  margin-bottom: 24px;
656
  border: 1px solid var(--border);
 
716
  margin-top: 20px;
717
  }
718
  .team-card {
719
+ background: var(--bg-card);
720
+ border-radius: 8px;
 
721
  padding: 20px;
722
  border: 1px solid var(--border);
723
  }
 
738
  border-radius: 50%;
739
  background: rgba(0,0,0,0.3);
740
  border: 2px solid;
 
 
 
 
741
  object-fit: cover;
742
  }
743
+
744
+ .copyright {
745
+ margin-top: 30px;
746
+ padding: 16px;
747
+ border-top: 1px solid var(--border);
748
+ text-align: center;
749
+ color: var(--text-dim);
750
+ font-size: 12px;
751
+ }
752
+ .copyright a {
753
+ color: var(--accent);
754
+ text-decoration: none;
755
+ }
756
  </style>
757
  </head>
758
  <body>
 
796
  <p style="font-size: 16px; line-height: 1.6; margin-bottom: 20px; color: var(--text-soft);">
797
  <strong>CV = CRIT Rate × 2 + CRIT DMG</strong>
798
  </p>
799
+ <div style="background: rgba(0,0,0,0.3); padding: 20px; border-radius: 8px;">
800
  <div style="display: flex; align-items: center; gap: 16px; margin-bottom: 12px;">
801
  <div style="width: 20px; height: 20px; background: var(--success); border-radius: 4px;"></div>
802
  <span><strong>Высокий (>200)</strong> — отличный билд</span>
 
817
  <!-- Сайдбар -->
818
  <aside>
819
  <div class="side-header">
820
+ <h1>NEXUS BETA</h1>
821
  <p>анализатор Genshin</p>
822
  </div>
823
  <div class="nav-group">
824
  <div class="nav-item active" data-tab="chars" onclick="switchTab('chars')"><i class="fas fa-users"></i> <span>Персонажи</span></div>
825
  <div class="nav-item" data-tab="artifacts" onclick="switchTab('artifacts')"><i class="fas fa-gem"></i> <span>Артефакты</span></div>
826
+ <div class="nav-item" data-tab="team" onclick="switchTab('team')"><i class="fas fa-users"></i> <span>Отряды</span></div>
827
  <div class="nav-item" data-tab="assessment" onclick="switchTab('assessment')"><i class="fas fa-chart-line"></i> <span>Оценка</span></div>
828
+ <div class="nav-item" data-tab="myteams" onclick="switchTab('myteams')"><i class="fas fa-folder"></i> <span>Мои отряды</span></div>
829
+ <div class="nav-item" data-tab="profiles" onclick="switchTab('profiles')"><i class="fas fa-id-badge"></i> <span>Профили</span></div>
830
  <div class="nav-item" data-tab="settings" onclick="switchTab('settings')"><i class="fas fa-cog"></i> <span>Настройки</span></div>
831
  </div>
832
 
833
  <div class="filter-section">
834
+ <label>Фильтр</label>
835
  <select id="f-el" onchange="applyFilters()">
836
  <option value="all">Все элементы</option>
837
  <option value="pyro">Пиро</option>
 
845
 
846
  <label>Сортировка</label>
847
  <select id="f-sort" onchange="applyFilters()">
848
+ <option value="cv">Крит‑ценность</option>
849
  <option value="lvl">Уровень</option>
850
  <option value="name">Имя</option>
851
  </select>
852
 
853
+ <button class="btn" style="width:100%; background: linear-gradient(135deg, #ef4444, #b91c1c);" onclick="clearDB()"><i class="fas fa-trash-alt"></i> <span>Удалить</span></button>
854
  <button class="btn" style="width:100%; margin-top: 14px; background: linear-gradient(135deg, #4b5563, #1f2937);" onclick="openLegend()"><i class="fas fa-book"></i> <span>Легенда</span></button>
855
  </div>
856
  </aside>
 
864
  </div>
865
  <div style="display: flex; gap: 12px;">
866
  <label class="btn">
867
+ <i class="fas fa-upload"></i> <span>Загрузить</span>
868
  <input type="file" id="fileIn" accept=".json" style="display:none;">
869
  </label>
870
+ <button class="btn btn-outline" onclick="showProfileSelector()"><i class="fas fa-id-badge"></i> <span>Профили</span></button>
871
  </div>
872
  </header>
873
 
 
875
  <!-- Персонажи -->
876
  <div id="view-chars" class="view-panel active">
877
  <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:24px; flex-wrap:wrap; gap:16px;">
878
+ <h2 style="font-size:26px; font-weight:800;"><i class="fas fa-users" style="color:var(--accent);"></i> Персонажи</h2>
879
  <div class="view-toggle">
880
  <span class="view-toggle-btn active" data-view="table" onclick="setCharsView('table')"><i class="fas fa-list"></i> Таблица</span>
881
  <span class="view-toggle-btn" data-view="cards" onclick="setCharsView('cards')"><i class="fas fa-th-large"></i> Ка��точки</span>
 
902
  <!-- Артефакты -->
903
  <div id="view-artifacts" class="view-panel">
904
  <div style="display:flex; gap:16px; margin-bottom:24px; align-items:center; flex-wrap:wrap;">
905
+ <h2 style="font-size:26px; font-weight:800;"><i class="fas fa-gem" style="color:#ffd966;"></i> Артефакты</h2>
906
  <select id="art-set-filter" onchange="renderArtifacts()" style="width:200px;">
907
  <option value="all">Все сеты</option>
908
  </select>
 
932
  <!-- Отряды -->
933
  <div id="view-team" class="view-panel">
934
  <div style="display:flex; align-items:center; gap:20px; margin-bottom:24px; flex-wrap:wrap;">
935
+ <h2 style="font-size:26px; font-weight:800;"><i class="fas fa-users" style="color:#fbbf24;"></i> Отряды</h2>
936
  <button class="btn" onclick="aiTeamBuilder()" style="background: linear-gradient(135deg, #8b5cf6, #c084fc);"><i class="fas fa-microchip"></i> AI‑подбор</button>
937
  <label style="display:flex; align-items:center; gap:8px; background:rgba(0,0,0,0.3); padding:8px 20px; border-radius:30px; border:1px solid var(--border);">
938
  <input type="checkbox" id="googleSearchCheckbox" style="width:18px; height:18px; accent-color:var(--accent);">
 
945
 
946
  <!-- Оценка аккаунта -->
947
  <div id="view-assessment" class="view-panel">
948
+ <h2 style="font-size:26px; font-weight:800; margin-bottom:24px;"><i class="fas fa-chart-line" style="color:#10b981;"></i> Оценка</h2>
949
+ <div id="assessment-content">Загрузите GOOD‑файл.</div>
950
  </div>
951
 
952
  <!-- Мои отряды -->
953
  <div id="view-myteams" class="view-panel">
954
  <div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:24px; flex-wrap:wrap; gap:16px;">
955
+ <h2 style="font-size:26px; font-weight:800;"><i class="fas fa-folder" style="color:#f59e0b;"></i> Мои отряды</h2>
956
  <button class="btn" onclick="generateRandomTeam()" style="background: linear-gradient(135deg, #10b981, #059669);"><i class="fas fa-dice"></i> Рандомный</button>
957
  </div>
958
  <div id="myteams-content">
 
960
  </div>
961
  </div>
962
 
963
+ <!-- Профили -->
964
  <div id="view-profiles" class="view-panel">
965
+ <h2 style="font-size:26px; font-weight:800; margin-bottom:24px;"><i class="fas fa-id-badge" style="color:#94a3b8;"></i> Профили</h2>
966
  <div id="profiles-content">
967
  <div style="display:flex; gap:16px; margin-bottom:24px; flex-wrap:wrap;">
968
  <button class="btn" onclick="createNewProfile()"><i class="fas fa-plus"></i> Новый профиль</button>
 
974
 
975
  <!-- Настройки -->
976
  <div id="view-settings" class="view-panel">
977
+ <h2 style="font-size:26px; font-weight:800; margin-bottom:24px;"><i class="fas fa-cog" style="color:#94a3b8;"></i> Настройки</h2>
978
 
979
  <div class="settings-section">
980
  <h3 style="font-size:20px; margin-bottom:20px;">🤖 AI‑провайдер</h3>
 
994
  <div class="settings-label">🔑 Токен Hugging Face</div>
995
  <div class="settings-description"><a href="https://huggingface.co/settings/tokens/new?ownUserPermissions=inference.serverless.write&tokenType=fineGrained" target="_blank" style="color:var(--accent);">Получить токен</a></div>
996
  </div>
997
+ <input type="password" id="hf-api-key" placeholder="hf_..." style="width:240px;">
998
  </div>
999
  <div class="settings-row">
1000
  <div><div class="settings-label">🧠 Модель</div></div>
 
1014
  <div class="settings-label">🔑 OpenAI API ключ</div>
1015
  <div class="settings-description"><a href="https://platform.openai.com/api-keys" target="_blank" style="color:var(--accent);">Получить ключ</a></div>
1016
  </div>
1017
+ <input type="password" id="openai-api-key" placeholder="sk-..." style="width:240px;">
1018
  </div>
1019
  <div class="settings-row">
1020
  <div><div class="settings-label">🧠 Модель</div></div>
 
1032
  <div class="settings-label">🔑 Gemini API ключ</div>
1033
  <div class="settings-description"><a href="https://aistudio.google.com/apikeys" target="_blank" style="color:var(--accent);">Получить ключ</a></div>
1034
  </div>
1035
+ <input type="password" id="gemini-api-key" placeholder="AIza..." style="width:240px;">
1036
  </div>
1037
  <div class="settings-row">
1038
  <div><div class="settings-label">🧠 Модель</div></div>
 
1050
  <div class="settings-label">🔑 DeepSeek API ключ</div>
1051
  <div class="settings-description"><a href="https://platform.deepseek.com/api_keys" target="_blank" style="color:var(--accent);">Получить ключ</a></div>
1052
  </div>
1053
+ <input type="password" id="deepseek-api-key" placeholder="sk-..." style="width:240px;">
1054
  </div>
1055
  <div class="settings-row">
1056
  <div><div class="settings-label">🧠 Модель</div></div>
 
1065
  <div id="settings-custom" style="display:none;">
1066
  <div class="settings-row">
1067
  <div><div class="settings-label">🔑 Custom ключ</div></div>
1068
+ <input type="password" id="custom-api-key" placeholder="ключ" style="width:240px;">
1069
  </div>
1070
  <div class="settings-row">
1071
  <div><div class="settings-label">🌐 Base URL</div></div>
 
1073
  </div>
1074
  <div class="settings-row">
1075
  <div><div class="settings-label">🧠 Model</div></div>
1076
+ <input type="text" id="custom-model" placeholder="model-name" style="width:240px;">
1077
  </div>
1078
  </div>
1079
  </div>
1080
 
1081
  <div style="display:flex; gap:16px; margin-top:20px;">
1082
+ <button class="btn" onclick="saveAllApiKeys()"><i class="fas fa-save"></i> Сохранить все ключи</button>
1083
  <button class="btn btn-outline" onclick="testCurrentApi()"><i class="fas fa-check-circle"></i> Проверить</button>
1084
  </div>
1085
  <div id="api-test-result" style="margin-top:16px; font-size:13px;"></div>
 
1089
  <h3 style="font-size:20px; margin-bottom:20px;">🎨 Интерфейс</h3>
1090
  <div class="settings-row">
1091
  <div>
1092
+ <div class="settings-label">🌓 Тема</div>
1093
+ <div class="settings-description">Стиль отображения</div>
1094
  </div>
1095
  <select id="theme-selector" onchange="changeTheme(this.value)" style="width:160px;">
1096
  <option value="dark">Тёмная</option>
 
1098
  <option value="light">Светлая</option>
1099
  </select>
1100
  </div>
 
 
 
 
 
 
 
 
1101
  </div>
1102
 
1103
  <div class="settings-section">
1104
  <h3 style="font-size:20px; margin-bottom:20px;">💾 Данные</h3>
1105
  <div class="settings-row">
1106
+ <div><div class="settings-label">🗄️ Управление БД</div></div>
 
 
1107
  <div style="display:flex; gap:12px;">
1108
  <button class="btn" style="background:linear-gradient(135deg, #ef4444, #b91c1c);" onclick="clearDB()"><i class="fas fa-trash-alt"></i> Сбросить</button>
1109
  <button class="btn" onclick="exportGOOD()"><i class="fas fa-download"></i> Экспорт</button>
1110
  </div>
1111
  </div>
1112
  </div>
1113
+
1114
+ <!-- Копирайт и фанатский проект -->
1115
+ <div class="copyright">
1116
+ <p>🎮 Фанатский проект, не связан с HoYoverse или miHoYo.</p>
1117
+ <p>Сделано с ❤️ <a href="https://github.com/Mycc" target="_blank">Mycc</a></p>
1118
+ <p style="margin-top: 8px; opacity: 0.6;">Версия 0.9.0 (BETA)</p>
1119
+ </div>
1120
  </div>
1121
  </div>
1122
  </main>
 
1124
  <!-- AI оверлей -->
1125
  <div id="ai-overlay">
1126
  <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
1127
+ <h3 style="font-size:24px;"><i class="fas fa-microchip" style="color:var(--accent);"></i> AI‑анализ</h3>
1128
  <button style="background:none; border:none; color:#fff; font-size:32px; cursor:pointer;" onclick="closeAI()">&times;</button>
1129
  </div>
1130
  <div id="ai-toc" class="toc" style="display: none;"></div>
1131
  <div id="ai-text" style="font-size:15px; line-height:1.6; color:var(--text-soft); overflow-y: auto; padding-right: 6px;">
1132
+ 💡 Нажмите «AI‑анализ» у персонажа.
1133
  </div>
1134
  <div id="ai-loader" class="loader" style="margin: 40px auto; display: none;"></div>
1135
  </div>
 
1226
  'geo_dmg_': '🪨 Гео урон%'
1227
  };
1228
 
1229
+ // ==================== ЛОКАЛЬНЫЕ ДАННЫЕ ПЕРСОНАЖЕЙ (СОКРАЩЕНО ДЛЯ ПРИМЕРА) ====================
1230
  const LOCAL_CHARACTERS = [
1231
  { name: "Арлекино", icon: "pic/arlecchino.png", element: "Pyro" },
1232
  { name: "Нёвиллет", icon: "pic/neuvillette.png", element: "Hydro" },
 
1250
  { name: "Шэнь Хэ", icon: "pic/shenhe.png", element: "Cryo" }
1251
  ];
1252
 
 
1253
  const nameMapping = {
1254
  'Arlecchino': 'Арлекино',
1255
  'Furina': 'Фурина',
 
1293
 
1294
  // ==================== МЕТА ОТРЯДЫ ====================
1295
  const metaTeams = [
1296
+ { name: '🌊 Пузырьковый реактор', fullName: 'Фурина + Нёвиллет',
1297
+ chars: ['Furina','Neuvillette','Kazuha','Zhongli'],
1298
+ resonance: 'Гидро резонанс: +25% HP',
1299
+ buffs: 'Фурина: +75% урон, Нёвиллет: ДД, Кадзуха: +40% элемента, Чжун Ли: -20% резист',
1300
+ reactions: 'Пар + Заряжен',
1301
+ meta: '6.3+',
1302
+ description: 'Топ-1 мета-отряд. Нёвиллет наносит колоссальный урон.' },
1303
+ { name: '🔥 Перегрузка', fullName: 'Арлекино + Шеврёз',
1304
+ chars: ['Arlecchino','Bennett','Chevreuse','Fischl'],
1305
+ resonance: 'Пиро резонанс: +25% ATK',
1306
+ buffs: 'Арлекино: ДД, Беннет: +1000 ATK, Шеврёз: -40% резист',
1307
+ reactions: 'Перегрузка',
1308
+ meta: '6.3+',
1309
+ description: 'Мощный взрывной урон.' },
1310
+ { name: '🌱 Гиперблум', fullName: 'Нахида + Фурина + Куки',
1311
+ chars: ['Nahida','Furina','KukiShinobu','Zhongli'],
1312
+ resonance: 'Дендро +80 EM, Гидро +25% HP',
1313
+ buffs: 'Нахида: +250 EM, Фурина: бафф, Куки: триггер',
1314
+ reactions: 'Гиперблум',
1315
+ meta: '6.3+',
1316
+ description: 'Стабильный урон через ядра.' },
1317
+ { name: '❄️ Моно-Крио', fullName: 'Ризли + Шэнь Хэ',
1318
+ chars: ['Wriothesley','Shenhe','Kazuha','Kokomi'],
1319
+ resonance: 'Крио резонанс: +15% CR',
1320
+ buffs: 'Ризли: ДД, Шэнь Хэ: бафф крио',
1321
+ reactions: 'Заморозка',
1322
+ meta: '6.3+',
1323
+ description: 'Ризли бьёт быстро и сильно.' },
1324
+ { name: '🪨 Гео-удар', fullName: авия + Чжун Ли',
1325
+ chars: ['Navia','Zhongli','Bennett','Xiangling'],
1326
+ resonance: 'Гео +15% урон, Пиро +25% ATK',
1327
+ buffs: 'Навия: ДД, Чжун Ли: щит',
1328
+ reactions: 'Кристалл',
1329
+ meta: '6.3+',
1330
+ description: 'Навия — дробовик.' },
1331
+ { name: '⚡ Стимуляция', fullName: 'Клоринда + Нахида',
1332
+ chars: ['Clorinde','Nahida','Fischl','Zhongli'],
1333
+ resonance: 'Электро резонанс',
1334
+ buffs: 'Клоринда: ДД, Нахида: EM',
1335
+ reactions: 'Стимуляция',
1336
+ meta: '6.3+',
1337
+ description: 'Клоринда — тир-1 ДД.' }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1338
  ];
1339
 
1340
  // ==================== ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ====================
 
1381
  return 'anemo';
1382
  }
1383
 
1384
+ // ==================== AI ФУНКЦИИ (НЕ ТРОГАЕМ, КРОМЕ ССЫЛОК) ====================
 
 
 
 
 
 
 
 
 
1385
  async function askAI(prompt, useGoogleSearch = false) {
1386
  let apiKey = '', baseURL = '', model = '';
1387
  switch(currentProvider) {
 
1445
  deepseekModel = document.getElementById('deepseek-model').value;
1446
  localStorage.setItem('nexus_deepseek_model', deepseekModel);
1447
 
1448
+ document.getElementById('api-test-result').innerHTML = '✅ Все ключи и настройки сохранены';
1449
  }
1450
 
1451
  async function testCurrentApi() {
 
1456
  if (!key) return alert('🔑 Введите API ключ');
1457
  document.getElementById('api-test-result').innerHTML = '⏳ Проверка...';
1458
  try {
1459
+ const result = await askAI('Ответь "OK" одной строкой.', false);
1460
  document.getElementById('api-test-result').innerHTML = '✅ Подключение работает, ключ действителен';
1461
  } catch (e) {
1462
  document.getElementById('api-test-result').innerHTML = `❌ Ошибка: ${e.message}`;
 
1493
  resultDiv.innerHTML = '<div style="display:flex; align-items:center; gap:12px;"><div class="loader" style="display:block;"></div> 🧠 AI подбирает отряды...</div>';
1494
 
1495
  const charList = localData.characters.map(c => getRussianName(c.key)).join(', ');
1496
+ const prompt = `У меня есть персонажи: ${charList}. Составь 3 лучших отряда ЖЕЛАТЕЛЬНО с нужными баффами. Используй строгий шаблон:\n## Название отряда\n**Состав:** персонаж1, персонаж2, персонаж3, персонаж4\n**Резонанс:** ...\n**Реакции:** ...\n**Баффы:** ...\n**Описание:** ...`;
1497
 
1498
  try {
1499
  const advice = await askAI(prompt, document.getElementById('googleSearchCheckbox')?.checked || false);
 
1528
  </div>`;
1529
  });
1530
  html += `</div>`;
1531
+ html += `<div style="display:grid; grid-template-columns:repeat(3,1fr); gap:16px; background:rgba(0,0,0,0.25); border-radius:8px; padding:20px;">
1532
  <div><span style="color:var(--accent);">🎵 Резонанс:</span> ${team.resonance || '—'}</div>
1533
  <div><span style="color:var(--accent);">⚡ Реакции:</span> ${team.reactions || '—'}</div>
1534
  <div><span style="color:var(--accent);">✨ Баффы:</span> ${team.buffs || '—'}</div>
1535
  </div>`;
1536
+ if (team.description) html += `<div style="margin-top:16px; color:var(--text-soft); padding:16px; background:rgba(0,0,0,0.15); border-radius:8px;">💬 ${team.description}</div>`;
1537
+ html += `<div style="margin-top:16px;"><button class="btn btn-outline" onclick="saveTeamFromAI('${team.title}', ${JSON.stringify(team.composition)})">💾 Сохранить отряд</button></div>`;
1538
  html += `</div>`;
1539
  });
1540
  resultDiv.innerHTML = html;
 
1545
 
1546
  // ==================== СОХРАНЕНИЕ ОТРЯДА ====================
1547
  window.saveTeamFromAI = function(title, composition) {
1548
+ const team = { id: Date.now(), name: title, characters: composition, date: new Date().toLocaleDateString('ru-RU') };
 
 
 
 
 
1549
  myTeams.push(team);
1550
  localStorage.setItem('nexus_myteams', JSON.stringify(myTeams));
1551
  renderMyTeams();
1552
  alert('✅ Отряд сохранён в «Мои отряды»');
1553
  };
1554
 
1555
+ // ==================== РЕНДЕР МОИХ ОТРЯДОВ ====================
1556
  function renderMyTeams() {
1557
  const container = document.getElementById('myteams-grid');
1558
  if (!container) return;
1559
  if (myTeams.length === 0) {
1560
+ container.innerHTML = '<div class="empty">🎲 Нет сохранённых отрядов. Используйте AI‑подбор.</div>';
1561
  return;
1562
  }
1563
  container.innerHTML = myTeams.map(team => {
 
1577
  </div>
1578
  <div style="margin-top:12px; color:var(--text-dim); font-size:12px;">${team.date || ''}</div>
1579
  <div style="display:flex; gap:8px; margin-top:12px;">
1580
+ <button class="btn btn-outline" style="padding:4px 12px; font-size:12px;" onclick="deleteTeam(${team.id})">🗑️</button>
1581
  </div>
1582
  </div>`;
1583
  }).join('');
 
1639
  function renderProfiles() {
1640
  const container = document.getElementById('profiles-list');
1641
  if (!container) return;
1642
+ if (profiles.length === 0) { container.innerHTML = '<div class="empty">👤 Нет сохранённых профилей.</div>'; return; }
1643
  container.innerHTML = profiles.map(p => {
1644
  const isActive = p.id == activeProfile;
1645
  return `<div class="profile-badge" style="justify-content:space-between; ${isActive ? 'background:rgba(139,92,246,0.3); border-color:var(--accent);' : ''}">
1646
  <div style="display:flex; align-items:center; gap:12px;">
1647
+ <span style="font-size:20px;">👤</span>
1648
  <div><span style="font-weight:700;">${p.name}</span> <span style="font-size:12px;">${p.date || ''}</span></div>
1649
  </div>
1650
  <div style="display:flex; gap:8px;">
1651
+ <button class="btn btn-outline" style="padding:6px 12px;" onclick="loadProfile(${p.id})">📂</button>
1652
+ <button class="btn btn-outline" style="padding:6px 12px;" onclick="deleteProfile(${p.id})">🗑️</button>
1653
  </div>
1654
  </div>`;
1655
  }).join('');
 
1683
  if (inList) html += '</ul>';
1684
  if (toc.length > 1) {
1685
  let tocHtml = '<div class="toc"><strong>📖 Содержание:</strong><div style="display:flex; flex-wrap:wrap; gap:10px; margin-top:8px;">';
1686
+ toc.forEach(item => { tocHtml += `<a href="#${item.id}" style="color:var(--accent); background:rgba(139,92,246,0.1); padding:4px 12px; border-radius:30px;">${item.title}</a>`; });
1687
  tocHtml += '</div></div>';
1688
  return { html: tocHtml + html, toc: tocHtml };
1689
  }
 
1752
  function clearDB() { if (confirm('Удалить все данные?')) { dbInstance.transaction("app", "readwrite").objectStore("app").clear(); localData = null; renderChars(); location.reload(); } }
1753
 
1754
  // ==================== ОБОГАЩЕНИЕ ДАННЫХ ====================
 
 
 
1755
  function enrichCharData() {
1756
  if (!localData?.characters) return;
1757
  localData.characters.forEach(c => {
 
1766
  });
1767
  }
1768
 
1769
+ // ==================== CV ====================
1770
+ function getCV(art) {
1771
+ if (!art.substats) return 0;
1772
+ return art.substats.reduce((acc, s) => {
1773
+ if (s.key === 'critRate_') return acc + s.value * 2;
1774
+ if (s.key === 'critDMG_') return acc + s.value;
1775
+ return acc;
1776
+ }, 0);
1777
+ }
1778
+
1779
  // ==================== РЕНДЕР ПЕРСОНАЖЕЙ ====================
1780
+ let charElements = {}, roles = {};
1781
  function renderChars() {
1782
  if (!localData) { document.getElementById('empty-msg').style.display = 'block'; return; }
1783
  renderTable(); renderCards();
 
1829
  <td><div style="display:flex; align-items:center; gap:12px;">
1830
  <img src="${icon}" style="width:44px; height:44px; border-radius:50%; border:2px solid ${elementColor}; object-fit:cover;" onerror="this.onerror=null;this.src='https://placehold.co/44x44/4A4A4A/FFFFFF?text=${rusName.charAt(0)}';">
1831
  <div><a href="https://www.google.com/search?q=Genshin+Impact+${encodeURIComponent(rusName)}+гайд" target="_blank" style="font-size:16px; font-weight:700; color:#fff;">${rusName}</a>
1832
+ <span style="background:rgba(0,0,0,0.4); padding:4px 10px; border-radius:4px; font-size:12px; margin-left:8px;">Ур.${c.level} · С${c.constellation}</span></div>
1833
  </div></td>
1834
+ <td><span class="weapon-badge">⚔️ ${weaponDisplay}</span><br><span class="role-badge" style="margin-top:6px;">🎯 ${roleDisplay}</span></td>
1835
  <td><span style="color:var(--text-soft);">${setsDisplay}</span></td>
1836
  <td><span style="font-size:18px; font-weight:800;">${c.cv.toFixed(1)}</span>
1837
  <div class="cv-meter"><div class="cv-fill" style="width:${Math.min(c.cv/2.2,100)}%; background:${cvColor};"></div></div>
 
1882
  <div><div class="char-name"><a href="https://www.google.com/search?q=Genshin+Impact+${encodeURIComponent(rusName)}+гайд" target="_blank">${rusName}</a></div>
1883
  <span class="char-level">⭐ Ур.${c.level} · Созвездие ${c.constellation}</span></div>
1884
  </div>
1885
+ <div class="char-card-stats"><span class="weapon-badge">⚔️ ${weaponDisplay}</span><span class="role-badge">🎯 ${roleDisplay}</span></div>
1886
+ <div class="char-card-sets"><span style="color:var(--text-dim);">⚙️ Сеты:</span> ${setsDisplay}</div>
1887
  <div style="display:flex; justify-content:space-between; align-items:center;">
1888
  <div><span style="font-size:22px; font-weight:800;">${c.cv.toFixed(1)}</span>
1889
  <span style="font-size:12px; color:${cvColor}; margin-left:6px;">${cvStatus}</span>
 
1926
  if (groupBy === 'slot') groupName = slotNames[key] || key;
1927
  if (groupBy === 'rarity') groupName = `${key}★`;
1928
  if (groupBy === 'level') groupName = `+${key} и выше`;
1929
+ html += `<h3 style="margin:16px 0 8px; color:var(--text-soft);">${groupName} (${groups[key].length})</h3>`;
1930
  html += renderArtifactList(groups[key]);
1931
  }
1932
  container.innerHTML = html;
 
1946
  return `<div class="artifact-card" style="flex-direction:column; align-items:stretch;">
1947
  <div style="display:flex; justify-content:space-between;"><div><b>${setRus}</b> · ${slotRus}</div><div class="artifact-mainstat">${mainStatRus} +${art.level}</div></div>
1948
  <div style="display:flex; gap:10px; flex-wrap:wrap;">${art.substats.map(s => `<span style="background:rgba(255,255,255,0.06); border:1px solid var(--border); padding:6px 14px; border-radius:30px; font-size:12px;">${statNames[s.key] || s.key} +${s.value}</span>`).join('')}</div>
1949
+ <div style="display:flex; justify-content:space-between; color:var(--text-dim);"><span>CV: <b>${cv.toFixed(1)}</b></span><span>📌 ${loc}</span></div>
1950
  </div>`;
1951
  }).join('');
1952
  } else {
 
1961
  <div class="artifact-mainstat">${mainStatRus} +${art.level}</div>
1962
  <div style="flex:1; display:flex; gap:8px; flex-wrap:wrap;">${art.substats.map(s => `<span style="background:rgba(255,255,255,0.06); border:1px solid var(--border); padding:6px 12px; border-radius:30px; font-size:11px;">${statNames[s.key] || s.key} +${s.value}</span>`).join('')}</div>
1963
  <div>CV: <b>${cv.toFixed(1)}</b></div>
1964
+ <div style="color:var(--text-dim);">📌 ${loc}</div>
1965
  </div>`;
1966
  }).join('');
1967
  }
 
1992
  ${t.chars.map((c,i) => { const ownedHere = hasChars[i]; const rusName = getRussianName(c);
1993
  return `<span style="display:inline-flex; align-items:center; gap:8px; background:${ownedHere ? 'rgba(16,185,129,0.15)' : 'rgba(239,68,68,0.15)'}; border:1px solid ${ownedHere ? 'rgba(16,185,129,0.3)' : 'rgba(239,68,68,0.3)'}; padding:6px 16px; border-radius:30px;">${rusName} ${ownedHere ? '✅' : '❌'}</span>`; }).join('')}
1994
  </div>
1995
+ <div style="display:grid; grid-template-columns:repeat(3,1fr); gap:16px; background:rgba(0,0,0,0.25); border-radius:8px; padding:16px;">
1996
  <div><span style="color:var(--accent);">🎵 Резонанс:</span> ${t.resonance}</div>
1997
  <div><span style="color:var(--accent);">⚡ Реакции:</span> ${t.reactions}</div>
1998
  <div><span style="color:var(--accent);">✨ Баффы:</span> ${t.buffs}</div>
1999
  </div>
2000
+ <div style="margin-top:16px; color:var(--text-soft); background:rgba(0,0,0,0.15); padding:16px; border-radius:8px;">💬 ${t.description}</div>
2001
  <div style="margin-top:16px; display:flex; justify-content:space-between; align-items:center;">
2002
+ <span> В наличии: <b>${ownedCount}/${t.chars.length}</b> ${missing.length ? `❌ Нет: ${missing.join(', ')}` : '✅ Полный отряд'}</span>
2003
+ <button class="btn btn-outline" onclick="saveTeamFromAI('${t.fullName}', ${JSON.stringify(t.chars)})">💾 Сохранить</button>
2004
  </div>
2005
  </div>`;
2006
  }).join('');
 
2008
 
2009
  // ==================== ОЦЕНКА АККАУНТА ====================
2010
  function renderAssessment() {
2011
+ if (!localData?.characters) { document.getElementById('assessment-content').innerHTML = '<div class="empty">Загрузите GOOD‑файл.</div>'; return; }
2012
  const chars = localData.characters;
2013
  const arts = localData.artifacts || [];
2014
  const chars80 = chars.filter(c => c.level >= 80).length;
 
2042
 
2043
  const html = `
2044
  <div class="assessment-card" style="border-left-color: var(--accent);">
2045
+ <h3 style="font-size:24px;">📊 Общая статистика</h3>
2046
  <div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(180px,1fr)); gap:20px; margin-top:20px;">
2047
+ <div style="background:rgba(0,0,0,0.2); padding:20px; border-radius:8px;"><div style="font-size:14px; color:var(--text-dim);">Персонажи</div><div style="font-size:36px; font-weight:800;">${chars.length}</div></div>
2048
+ <div style="background:rgba(0,0,0,0.2); padding:20px; border-radius:8px;"><div style="font-size:14px; color:var(--text-dim);">Уровень 80+</div><div style="font-size:36px; font-weight:800;">${chars80}</div><div style="font-size:13px;">90 ур: ${chars90}</div></div>
2049
+ <div style="background:rgba(0,0,0,0.2); padding:20px; border-radius:8px;"><div style="font-size:14px; color:var(--text-dim);">Средний CV</div><div style="font-size:36px; font-weight:800;">${avgCV.toFixed(1)}</div></div>
2050
+ <div style="background:rgba(0,0,0,0.2); padding:20px; border-radius:8px;"><div style="font-size:14px; color:var(--text-dim);">Артефакты +20</div><div style="font-size:36px; font-weight:800;">${arts20}</div></div>
2051
  </div>
2052
  </div>
2053
  <div class="assessment-card" style="border-left-color: var(--pyro);">
2054
+ <div style="display:flex; justify-content:space-between;"><h3 style="font-size:24px;">⚔️ Бездна 12-3</h3><span style="font-size:42px; font-weight:800; color:${abyssScore>=80?'var(--success)':abyssScore>=60?'var(--warning)':'var(--error)'};">${abyssScore}</span></div>
2055
  <div style="font-size:22px; font-weight:800; margin:12px 0 4px;">${abyssResult}</div>
2056
+ <div style="background:rgba(0,0,0,0.2); border-radius:8px; padding:16px; margin-top:16px;">
2057
+ <div>🎯 Топ-саппорты: ${ownedSupports}/8</div>
2058
+ <div> Топ-ДД: ${ownedDPS}/10</div>
2059
+ <div> CV: ${avgCV > 160 ? 'отличный' : avgCV > 140 ? 'хороший' : avgCV > 120 ? 'средний' : 'низкий'}</div>
2060
  </div>
2061
  </div>
2062
  <div class="assessment-card" style="border-left-color: var(--cryo);">
2063
+ <div style="display:flex; justify-content:space-between;"><h3 style="font-size:24px;">🌑 Мрачный натиск</h3><span style="font-size:42px; font-weight:800; color:${mgScore>=80?'var(--success)':mgScore>=65?'var(--warning)':'var(--error)'};">${mgScore}</span></div>
2064
  <div style="font-size:22px; font-weight:800; margin:12px 0 4px;">${mgResult}</div>
2065
+ <div style="margin-top:16px; color:var(--text-dim);">💡 Рекомендованы: Арлекино, Нёвиллет, Навия, Клоринда, Ризли</div>
2066
  </div>
2067
  `;
2068
  document.getElementById('assessment-content').innerHTML = html;
 
2072
  function applyFilters() { renderChars(); }
2073
  function setCharsView(view) { charsView = view; localStorage.setItem('nexus_chars_view', view); document.querySelectorAll('#view-chars .view-toggle-btn').forEach(btn => btn.classList.toggle('active', btn.dataset.view === view)); renderChars(); }
2074
  function setArtifactsView(view) { artifactsView = view; localStorage.setItem('nexus_artifacts_view', view); document.querySelectorAll('#view-artifacts .view-toggle-btn').forEach(btn => btn.classList.toggle('active', btn.dataset.view === view)); renderArtifacts(); }
2075
+ window.changeTheme = function(theme) {
2076
  const root = document.documentElement;
2077
  if (theme === 'light') {
2078
  root.style.setProperty('--bg-main', '#f3f4f6');
 
2092
  } else {
2093
  location.reload();
2094
  }
2095
+ };
2096
 
2097
  // ==================== ИМПОРТ GOOD ====================
2098
  document.getElementById('fileIn').onchange = function(e) {
 
2103
  const newData = JSON.parse(ev.target.result);
2104
  if (confirm('Сохранить в новый профиль? (Отмена — заменить текущие)')) {
2105
  const profileName = prompt('Название профиля:', `Профиль ${profiles.length + 1}`);
2106
+ if (profileName) { profiles.push({ id: Date.now(), name: profileName, data: newData, date: new Date().toLocaleDateString('ru-RU') }); localStorage.setItem('nexus_profiles', JSON.stringify(profiles)); }
 
 
 
 
 
 
 
 
 
2107
  }
2108
+ localData = newData; enrichCharData(); saveToDB(); renderChars();
2109
+ if (localData.artifacts) { const sets = [...new Set(localData.artifacts.map(a => a.setKey))].sort(); const setSelect = document.getElementById('art-set-filter'); setSelect.innerHTML = '<option value="all">Все сеты</option>' + sets.map(s => `<option value="${s}">${artifactSetNames[s] || s}</option>`).join(''); }
2110
+ switchTab('chars'); renderAssessment(); alert('✅ Данные загружены!');
2111
  } catch(ex) { alert('❌ Неверный GOOD‑файл'); }
2112
  };
2113
  reader.readAsText(file);
2114
  };
2115
 
 
2116
  function exportGOOD() { if (!localData) return alert('Нет данных'); const dataStr = JSON.stringify(localData, null, 2); const blob = new Blob([dataStr], {type:'application/json'}); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'genshin_nexus_export.json'; a.click(); }
2117
 
2118
  // ==================== ПОПОВЕРЫ ====================
 
2143
  renderProfiles(); renderMyTeams();
2144
  }
2145
 
2146
+ // Глобальные функции
2147
  window.switchTab = switchTab;
2148
  window.analyzeCharacter = analyzeCharacter;
2149
  window.aiTeamBuilder = aiTeamBuilder;