@kaikybrofc/omnizap-system 2.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/.env.example +534 -0
  2. package/LICENSE +21 -0
  3. package/README.md +431 -0
  4. package/RELEASE-v2.1.2.md +83 -0
  5. package/app/config/adminIdentity.js +87 -0
  6. package/app/config/baileysConfig.js +693 -0
  7. package/app/config/groupUtils.js +388 -0
  8. package/app/connection/socketController.js +992 -0
  9. package/app/controllers/messageController.js +354 -0
  10. package/app/modules/adminModule/groupCommandHandlers.js +1294 -0
  11. package/app/modules/adminModule/groupEventHandlers.js +355 -0
  12. package/app/modules/aiModule/catCommand.js +1006 -0
  13. package/app/modules/broadcastModule/noticeCommand.js +416 -0
  14. package/app/modules/gameModule/diceCommand.js +67 -0
  15. package/app/modules/menuModule/common.js +311 -0
  16. package/app/modules/menuModule/menus.js +59 -0
  17. package/app/modules/playModule/playCommand.js +1615 -0
  18. package/app/modules/quoteModule/quoteCommand.js +851 -0
  19. package/app/modules/rpgPokemonModule/rpgBattleCanvasRenderer.js +786 -0
  20. package/app/modules/rpgPokemonModule/rpgBattleService.js +2082 -0
  21. package/app/modules/rpgPokemonModule/rpgBattleService.test.js +760 -0
  22. package/app/modules/rpgPokemonModule/rpgEvolutionUtils.js +22 -0
  23. package/app/modules/rpgPokemonModule/rpgPokemonCommand.js +172 -0
  24. package/app/modules/rpgPokemonModule/rpgPokemonDomain.js +192 -0
  25. package/app/modules/rpgPokemonModule/rpgPokemonDomain.test.js +93 -0
  26. package/app/modules/rpgPokemonModule/rpgPokemonEvolution.test.js +46 -0
  27. package/app/modules/rpgPokemonModule/rpgPokemonMessages.js +746 -0
  28. package/app/modules/rpgPokemonModule/rpgPokemonRepository.js +1859 -0
  29. package/app/modules/rpgPokemonModule/rpgPokemonService.js +6738 -0
  30. package/app/modules/rpgPokemonModule/rpgProfileCanvasRenderer.js +354 -0
  31. package/app/modules/statsModule/globalRankingCommand.js +65 -0
  32. package/app/modules/statsModule/noMessageCommand.js +288 -0
  33. package/app/modules/statsModule/rankingCommand.js +60 -0
  34. package/app/modules/statsModule/rankingCommon.js +889 -0
  35. package/app/modules/stickerModule/addStickerMetadata.js +239 -0
  36. package/app/modules/stickerModule/convertToWebp.js +390 -0
  37. package/app/modules/stickerModule/stickerCommand.js +454 -0
  38. package/app/modules/stickerModule/stickerConvertCommand.js +156 -0
  39. package/app/modules/stickerModule/stickerTextCommand.js +657 -0
  40. package/app/modules/stickerPackModule/autoPackCollectorRuntime.js +20 -0
  41. package/app/modules/stickerPackModule/autoPackCollectorService.js +284 -0
  42. package/app/modules/stickerPackModule/semanticReclassificationEngine.js +466 -0
  43. package/app/modules/stickerPackModule/semanticReclassificationEngine.test.js +88 -0
  44. package/app/modules/stickerPackModule/semanticThemeClusterService.js +571 -0
  45. package/app/modules/stickerPackModule/stickerAssetClassificationRepository.js +449 -0
  46. package/app/modules/stickerPackModule/stickerAssetRepository.js +400 -0
  47. package/app/modules/stickerPackModule/stickerAssetReprocessQueueRepository.js +180 -0
  48. package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +4078 -0
  49. package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +598 -0
  50. package/app/modules/stickerPackModule/stickerClassificationService.js +588 -0
  51. package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +102 -0
  52. package/app/modules/stickerPackModule/stickerPackCatalogHttp.js +7506 -0
  53. package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +1095 -0
  54. package/app/modules/stickerPackModule/stickerPackEngagementRepository.js +108 -0
  55. package/app/modules/stickerPackModule/stickerPackErrors.js +30 -0
  56. package/app/modules/stickerPackModule/stickerPackInteractionEventRepository.js +110 -0
  57. package/app/modules/stickerPackModule/stickerPackItemRepository.js +440 -0
  58. package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +337 -0
  59. package/app/modules/stickerPackModule/stickerPackMessageService.js +296 -0
  60. package/app/modules/stickerPackModule/stickerPackRepository.js +442 -0
  61. package/app/modules/stickerPackModule/stickerPackService.js +788 -0
  62. package/app/modules/stickerPackModule/stickerPackServiceRuntime.js +51 -0
  63. package/app/modules/stickerPackModule/stickerPackUtils.js +97 -0
  64. package/app/modules/stickerPackModule/stickerStorageService.js +507 -0
  65. package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +233 -0
  66. package/app/modules/stickerPackModule/stickerWorkerTaskQueueRepository.js +205 -0
  67. package/app/modules/systemMetricsModule/pingCommand.js +421 -0
  68. package/app/modules/tiktokModule/tiktokCommand.js +798 -0
  69. package/app/modules/userModule/userCommand.js +1217 -0
  70. package/app/modules/waifuPicsModule/waifuPicsCommand.js +177 -0
  71. package/app/observability/metrics.js +734 -0
  72. package/app/services/captchaService.js +492 -0
  73. package/app/services/dbWriteQueue.js +572 -0
  74. package/app/services/groupMetadataService.js +279 -0
  75. package/app/services/lidMapService.js +663 -0
  76. package/app/services/messagePersistenceService.js +56 -0
  77. package/app/services/newsBroadcastService.js +351 -0
  78. package/app/services/pokeApiService.js +398 -0
  79. package/app/services/queueUtils.js +57 -0
  80. package/app/services/socketState.js +7 -0
  81. package/app/store/aiPromptStore.js +38 -0
  82. package/app/store/groupConfigStore.js +58 -0
  83. package/app/store/premiumUserStore.js +36 -0
  84. package/app/utils/antiLink/antiLinkModule.js +804 -0
  85. package/app/utils/http/getImageBufferModule.js +18 -0
  86. package/app/utils/json/jsonSanitizer.js +113 -0
  87. package/app/utils/json/jsonSanitizer.test.js +40 -0
  88. package/app/utils/logger/loggerModule.js +262 -0
  89. package/app/utils/systemMetrics/systemMetricsModule.js +91 -0
  90. package/database/index.js +2052 -0
  91. package/database/init.js +516 -0
  92. package/database/migrations/20260203_0001_sticker_packs.sql +54 -0
  93. package/database/migrations/20260210_0003_rpg_pokemon.sql +58 -0
  94. package/database/migrations/20260210_0004_rpg_shiny_biome.sql +9 -0
  95. package/database/migrations/20260210_0005_rpg_missions.sql +14 -0
  96. package/database/migrations/20260210_0006_rpg_world_pokedex_traits.sql +27 -0
  97. package/database/migrations/20260210_0007_rpg_raid_pvp.sql +56 -0
  98. package/database/migrations/20260210_0008_rpg_social_system.sql +195 -0
  99. package/database/migrations/20260211_0009_rpg_social_xp.sql +36 -0
  100. package/database/migrations/20260222_0010_remove_message_xp.sql +2 -0
  101. package/database/migrations/20260226_0011_sticker_asset_classification.sql +17 -0
  102. package/database/migrations/20260226_0012_sticker_pack_engagement.sql +16 -0
  103. package/database/migrations/20260226_0013_sticker_marketplace_intelligence.sql +19 -0
  104. package/database/migrations/20260226_0014_sticker_pack_publish_flow.sql +30 -0
  105. package/database/migrations/20260226_0014_sticker_worker_queues.sql +42 -0
  106. package/database/migrations/20260226_0015_sticker_auto_pack_curation_integrity.sql +18 -0
  107. package/database/migrations/20260226_0016_sticker_web_google_auth_persistence.sql +34 -0
  108. package/database/migrations/20260226_0017_sticker_web_admin_ban.sql +22 -0
  109. package/database/migrations/20260226_0018_sticker_web_admin_moderator.sql +18 -0
  110. package/database/migrations/20260227_0019_sticker_classification_v2_signals.sql +12 -0
  111. package/database/migrations/20260227_0020_semantic_theme_clusters.sql +35 -0
  112. package/docker-compose.yml +103 -0
  113. package/ecosystem.prod.config.cjs +35 -0
  114. package/eslint.config.js +61 -0
  115. package/index.js +437 -0
  116. package/ml/clip_classifier/Dockerfile +16 -0
  117. package/ml/clip_classifier/README.md +120 -0
  118. package/ml/clip_classifier/adaptive_scoring.py +40 -0
  119. package/ml/clip_classifier/classifier.py +654 -0
  120. package/ml/clip_classifier/embedding_store.py +481 -0
  121. package/ml/clip_classifier/env_loader.py +15 -0
  122. package/ml/clip_classifier/llm_label_expander.py +144 -0
  123. package/ml/clip_classifier/main.py +213 -0
  124. package/ml/clip_classifier/requirements.txt +10 -0
  125. package/ml/clip_classifier/similarity_engine.py +74 -0
  126. package/observability/alert-rules.yml +60 -0
  127. package/observability/grafana/dashboards/omnizap-mysql.json +136 -0
  128. package/observability/grafana/dashboards/omnizap-overview.json +170 -0
  129. package/observability/grafana/provisioning/dashboards/dashboards.yml +11 -0
  130. package/observability/grafana/provisioning/datasources/datasources.yml +15 -0
  131. package/observability/loki-config.yml +38 -0
  132. package/observability/mysql-exporter.cnf +5 -0
  133. package/observability/mysql-setup.sql +46 -0
  134. package/observability/prometheus.yml +32 -0
  135. package/observability/promtail-config.yml +84 -0
  136. package/package.json +109 -0
  137. package/public/api-docs/index.html +144 -0
  138. package/public/css/github-project-panel.css +297 -0
  139. package/public/css/stickers-admin.css +1272 -0
  140. package/public/css/styles.css +671 -0
  141. package/public/index.html +1311 -0
  142. package/public/js/apps/apiDocsApp.js +310 -0
  143. package/public/js/apps/createPackApp.js +2069 -0
  144. package/public/js/apps/homeApp.js +396 -0
  145. package/public/js/apps/stickersAdminApp.js +1744 -0
  146. package/public/js/apps/stickersApp.js +4830 -0
  147. package/public/js/catalog.js +1019 -0
  148. package/public/js/github-panel/components/CommitList.js +34 -0
  149. package/public/js/github-panel/components/ErrorState.js +16 -0
  150. package/public/js/github-panel/components/GithubProjectPanel.js +106 -0
  151. package/public/js/github-panel/components/ReleaseList.js +38 -0
  152. package/public/js/github-panel/components/SkeletonPanel.js +22 -0
  153. package/public/js/github-panel/components/StatCard.js +15 -0
  154. package/public/js/github-panel/index.js +15 -0
  155. package/public/js/github-panel/useGithubRepoData.js +154 -0
  156. package/public/js/github-panel/vendor/react.js +11 -0
  157. package/public/js/runtime/react-runtime.js +19 -0
  158. package/public/licenca/index.html +106 -0
  159. package/public/stickers/admin/index.html +23 -0
  160. package/public/stickers/create/index.html +47 -0
  161. package/public/stickers/index.html +48 -0
  162. package/public/termos-de-uso/index.html +125 -0
  163. package/scripts/cache-bust.mjs +107 -0
  164. package/scripts/deploy.sh +458 -0
  165. package/scripts/github-deploy-notify.mjs +174 -0
  166. package/scripts/release.sh +129 -0
@@ -0,0 +1,671 @@
1
+ :root {
2
+ color-scheme: dark;
3
+ --bg: #0b1120;
4
+ --bg-2: #101a30;
5
+ --card: rgba(17, 28, 48, 0.9);
6
+ --line: #273d63;
7
+ --line-soft: #304c7a;
8
+ --text: #e8eef8;
9
+ --muted: #9aacc9;
10
+ --primary: #24bb69;
11
+ --primary-strong: #189858;
12
+ --primary-2: #7aa8ff;
13
+ }
14
+
15
+ body.catalog-body {
16
+ font-family: "Manrope", "Bricolage Grotesque", sans-serif;
17
+ color: var(--text);
18
+ background:
19
+ radial-gradient(58rem 24rem at -8% -12%, rgba(34, 197, 94, 0.14), transparent 60%),
20
+ radial-gradient(58rem 24rem at 112% -10%, rgba(56, 189, 248, 0.14), transparent 58%),
21
+ linear-gradient(165deg, var(--bg), var(--bg-2));
22
+ min-height: 100vh;
23
+ background-attachment: fixed;
24
+ }
25
+
26
+ body.catalog-body::before {
27
+ content: "";
28
+ position: fixed;
29
+ inset: 0;
30
+ pointer-events: none;
31
+ background:
32
+ linear-gradient(rgba(255, 255, 255, 0.012) 1px, transparent 1px),
33
+ linear-gradient(90deg, rgba(255, 255, 255, 0.012) 1px, transparent 1px);
34
+ background-size: 26px 26px;
35
+ opacity: 0.2;
36
+ z-index: -1;
37
+ }
38
+
39
+ .glass-nav {
40
+ backdrop-filter: blur(12px);
41
+ background: rgba(10, 17, 33, 0.84);
42
+ border-color: #223250 !important;
43
+ }
44
+
45
+ .navbar-brand,
46
+ .nav-link {
47
+ color: var(--text) !important;
48
+ }
49
+
50
+ .navbar-brand {
51
+ font-weight: 700;
52
+ letter-spacing: 0.1px;
53
+ }
54
+
55
+ .nav-link {
56
+ opacity: 0.9;
57
+ border: 1px solid transparent;
58
+ border-radius: 12px;
59
+ padding: 0.44rem 0.72rem !important;
60
+ transition: 180ms ease;
61
+ }
62
+
63
+ .nav-link:hover,
64
+ .nav-link:focus {
65
+ opacity: 1;
66
+ border-color: #355688;
67
+ background: #0f1b33;
68
+ color: #dff0ff !important;
69
+ }
70
+
71
+ .hero-card {
72
+ border-color: var(--line-soft) !important;
73
+ background: linear-gradient(155deg, rgba(12, 22, 42, 0.97), rgba(15, 27, 51, 0.97));
74
+ box-shadow: 0 18px 50px rgba(4, 9, 20, 0.42);
75
+ }
76
+
77
+ .hero-card .card-body {
78
+ padding-block: clamp(1.3rem, 2.5vw, 2.2rem) !important;
79
+ }
80
+
81
+ .catalog-title,
82
+ .section-title,
83
+ #panel-title {
84
+ font-family: "Sora", "Instrument Serif", serif;
85
+ color: var(--text);
86
+ }
87
+
88
+ .catalog-title {
89
+ font-size: clamp(1.7rem, 4vw, 2.5rem);
90
+ line-height: 1.07;
91
+ max-width: 760px;
92
+ }
93
+
94
+ .status-text {
95
+ color: var(--muted);
96
+ min-height: 20px;
97
+ }
98
+
99
+ .panel-controls {
100
+ border: 1px solid var(--line);
101
+ background: rgba(13, 21, 40, 0.88);
102
+ }
103
+
104
+ .form-control,
105
+ .form-select {
106
+ background: #0f1729;
107
+ border-color: var(--line);
108
+ color: var(--text);
109
+ border-radius: 14px;
110
+ min-height: 46px;
111
+ }
112
+
113
+ .search-wrap {
114
+ position: relative;
115
+ }
116
+
117
+ .search-input-icon {
118
+ position: absolute;
119
+ left: 12px;
120
+ top: 50%;
121
+ transform: translateY(-50%);
122
+ color: #8aa3c8;
123
+ pointer-events: none;
124
+ }
125
+
126
+ #search-input {
127
+ padding-left: 36px;
128
+ }
129
+
130
+ .form-control:focus,
131
+ .form-select:focus {
132
+ border-color: #3f5f90;
133
+ box-shadow: 0 0 0 0.2rem rgba(56, 189, 248, 0.16);
134
+ background: #111b31;
135
+ color: var(--text);
136
+ }
137
+
138
+ #categories-input {
139
+ display: none !important;
140
+ }
141
+
142
+ .categories-picker {
143
+ border: 1px solid #345381;
144
+ border-radius: 14px;
145
+ background: linear-gradient(160deg, rgba(13, 24, 45, 0.98), rgba(9, 18, 34, 0.98));
146
+ box-shadow: inset 0 1px 0 rgba(158, 191, 245, 0.08);
147
+ padding: 0.45rem;
148
+ min-height: 104px;
149
+ display: flex;
150
+ flex-direction: column;
151
+ gap: 0.38rem;
152
+ }
153
+
154
+ .categories-picker:focus-within {
155
+ border-color: #4c72ac;
156
+ box-shadow: 0 0 0 0.2rem rgba(56, 189, 248, 0.2), inset 0 1px 0 rgba(190, 214, 255, 0.12);
157
+ }
158
+
159
+ .categories-search-wrap {
160
+ position: relative;
161
+ }
162
+
163
+ .categories-search-icon {
164
+ position: absolute;
165
+ left: 11px;
166
+ top: 50%;
167
+ transform: translateY(-50%);
168
+ color: #88a4cd;
169
+ pointer-events: none;
170
+ }
171
+
172
+ .categories-search-input {
173
+ min-height: 34px;
174
+ font-size: 0.86rem;
175
+ border-radius: 10px;
176
+ padding-left: 30px;
177
+ }
178
+
179
+ .categories-chips {
180
+ display: flex;
181
+ flex-wrap: wrap;
182
+ gap: 0.28rem;
183
+ min-height: 24px;
184
+ }
185
+
186
+ .categories-empty {
187
+ color: #94a8c7;
188
+ font-size: 0.75rem;
189
+ }
190
+
191
+ .category-chip {
192
+ border: 1px solid rgba(114, 154, 220, 0.56);
193
+ background: linear-gradient(130deg, rgba(19, 40, 72, 0.94), rgba(15, 30, 56, 0.94));
194
+ color: #dcedff;
195
+ border-radius: 999px;
196
+ font-size: 0.68rem;
197
+ font-weight: 700;
198
+ display: inline-flex;
199
+ align-items: center;
200
+ gap: 0.34rem;
201
+ padding: 0.2rem 0.46rem;
202
+ cursor: pointer;
203
+ }
204
+
205
+ .category-chip:hover,
206
+ .category-chip:focus-visible {
207
+ border-color: rgba(146, 183, 240, 0.8);
208
+ background: linear-gradient(130deg, rgba(24, 47, 84, 0.96), rgba(18, 35, 66, 0.96));
209
+ }
210
+
211
+ .category-chip-remove {
212
+ font-size: 0.66rem;
213
+ opacity: 0.9;
214
+ }
215
+
216
+ .categories-options {
217
+ display: grid;
218
+ grid-template-columns: repeat(2, minmax(0, 1fr));
219
+ gap: 0.3rem;
220
+ max-height: 120px;
221
+ overflow: auto;
222
+ padding-right: 2px;
223
+ }
224
+
225
+ .category-option {
226
+ border: 1px solid #33527e;
227
+ background: rgba(16, 30, 54, 0.88);
228
+ color: #d7e7fb;
229
+ border-radius: 10px;
230
+ font-size: 0.74rem;
231
+ line-height: 1.1;
232
+ min-height: 28px;
233
+ padding: 0.28rem 0.4rem;
234
+ text-align: left;
235
+ display: inline-flex;
236
+ align-items: center;
237
+ justify-content: space-between;
238
+ gap: 0.35rem;
239
+ cursor: pointer;
240
+ }
241
+
242
+ .category-option:hover,
243
+ .category-option:focus-visible {
244
+ border-color: #4a74ab;
245
+ background: rgba(22, 39, 68, 0.94);
246
+ }
247
+
248
+ .category-option-check {
249
+ opacity: 0;
250
+ transform: scale(0.7);
251
+ transition: 140ms ease;
252
+ }
253
+
254
+ .category-option.selected {
255
+ border-color: rgba(49, 180, 112, 0.72);
256
+ background: linear-gradient(90deg, rgba(30, 168, 99, 0.36), rgba(23, 124, 76, 0.36));
257
+ color: #e9fff4;
258
+ }
259
+
260
+ .category-option.selected .category-option-check {
261
+ opacity: 1;
262
+ transform: scale(1);
263
+ }
264
+
265
+ .categories-options-empty {
266
+ color: #93a7c7;
267
+ font-size: 0.76rem;
268
+ padding: 0.18rem 0.3rem;
269
+ }
270
+
271
+ .categories-options::-webkit-scrollbar {
272
+ width: 8px;
273
+ }
274
+
275
+ .categories-options::-webkit-scrollbar-track {
276
+ background: rgba(14, 24, 43, 0.8);
277
+ border-radius: 999px;
278
+ }
279
+
280
+ .categories-options::-webkit-scrollbar-thumb {
281
+ background: linear-gradient(180deg, #3a5e92, #284874);
282
+ border-radius: 999px;
283
+ border: 1px solid rgba(14, 24, 43, 0.85);
284
+ }
285
+
286
+ .btn-success {
287
+ background: linear-gradient(90deg, var(--primary), var(--primary-strong));
288
+ border-color: transparent;
289
+ color: #062411;
290
+ font-weight: 700;
291
+ border-radius: 14px;
292
+ box-shadow: 0 8px 24px rgba(24, 152, 88, 0.24);
293
+ transition: transform 180ms ease, box-shadow 180ms ease, filter 180ms ease;
294
+ }
295
+
296
+ .btn-success:hover,
297
+ .btn-success:focus {
298
+ background: linear-gradient(90deg, #21b965, #178d55);
299
+ color: #041a0c;
300
+ transform: translateY(-1px);
301
+ box-shadow: 0 14px 30px rgba(24, 152, 88, 0.3);
302
+ filter: saturate(0.95);
303
+ }
304
+
305
+ .btn-outline-light {
306
+ border-color: #3d557f;
307
+ color: #d5e4fb;
308
+ }
309
+
310
+ .btn-outline-light:hover,
311
+ .btn-outline-light:focus {
312
+ background: #1a2742;
313
+ border-color: #4a6696;
314
+ color: #eff6ff;
315
+ }
316
+
317
+ .pack-card {
318
+ cursor: pointer;
319
+ background: rgba(16, 26, 44, 0.96) !important;
320
+ border-color: var(--line-soft) !important;
321
+ border-radius: 14px !important;
322
+ overflow: hidden;
323
+ transition: transform 220ms ease, box-shadow 220ms ease, border-color 220ms ease;
324
+ animation: fadeInUp 260ms ease both;
325
+ }
326
+
327
+ .pack-card .card-body {
328
+ padding: 0.75rem;
329
+ }
330
+
331
+ .pack-card:nth-child(2n) {
332
+ animation-delay: 0.03s;
333
+ }
334
+
335
+ .pack-card:nth-child(3n) {
336
+ animation-delay: 0.06s;
337
+ }
338
+
339
+ .pack-card:hover,
340
+ .pack-card:focus-visible {
341
+ transform: translateY(-4px);
342
+ border-color: #5171a6 !important;
343
+ box-shadow: 0 0 0 1px rgba(122, 168, 255, 0.22), 0 18px 36px rgba(0, 0, 0, 0.4);
344
+ }
345
+
346
+ .pack-card .card-title {
347
+ font-size: 0.92rem;
348
+ color: #eaf3ff;
349
+ line-height: 1.28;
350
+ }
351
+
352
+ .pack-thumb {
353
+ position: relative;
354
+ aspect-ratio: 1 / 1;
355
+ border: 1px solid #2f4a74;
356
+ border-radius: 0.82rem;
357
+ overflow: hidden;
358
+ background: radial-gradient(circle at 20% 20%, rgba(122, 168, 255, 0.2), rgba(9, 15, 30, 0.95));
359
+ display: grid;
360
+ place-items: center;
361
+ }
362
+
363
+ .pack-thumb::after {
364
+ content: "";
365
+ position: absolute;
366
+ inset: 0;
367
+ background: linear-gradient(to top, rgba(8, 14, 28, 0.32), rgba(8, 14, 28, 0));
368
+ opacity: 0;
369
+ transition: opacity 180ms ease;
370
+ }
371
+
372
+ .pack-card:hover .pack-thumb::after,
373
+ .pack-card:focus-visible .pack-thumb::after {
374
+ opacity: 1;
375
+ }
376
+
377
+ .pack-thumb img {
378
+ width: 100%;
379
+ height: 100%;
380
+ object-fit: cover;
381
+ }
382
+
383
+ .pack-visibility-badge,
384
+ .pack-count-badge {
385
+ position: absolute;
386
+ z-index: 2;
387
+ font-size: 0.63rem;
388
+ line-height: 1;
389
+ border-radius: 999px;
390
+ padding: 0.34rem 0.48rem;
391
+ backdrop-filter: blur(6px);
392
+ border: 1px solid rgba(116, 151, 214, 0.42);
393
+ }
394
+
395
+ .pack-visibility-badge {
396
+ top: 8px;
397
+ left: 8px;
398
+ color: #d5e7ff;
399
+ background: rgba(15, 26, 49, 0.78);
400
+ }
401
+
402
+ .pack-count-badge {
403
+ right: 8px;
404
+ bottom: 8px;
405
+ color: #dbf8e8;
406
+ background: rgba(19, 52, 35, 0.78);
407
+ border-color: rgba(61, 180, 119, 0.45);
408
+ }
409
+
410
+ .pack-thumb-fallback {
411
+ color: #9eb2d2;
412
+ font-size: 0.75rem;
413
+ text-align: center;
414
+ padding: 0.6rem;
415
+ }
416
+
417
+ .pack-meta {
418
+ color: #a7b9d8;
419
+ font-size: 0.73rem;
420
+ line-height: 1.28;
421
+ }
422
+
423
+ .pack-count {
424
+ color: #d5e6ff;
425
+ font-weight: 700;
426
+ }
427
+
428
+ .pack-author {
429
+ color: #8ea6cb;
430
+ }
431
+
432
+ .orphan-card {
433
+ border: 1px solid #334b72;
434
+ background: linear-gradient(165deg, rgba(17, 26, 46, 0.95), rgba(14, 22, 40, 0.95));
435
+ position: relative;
436
+ }
437
+
438
+ .orphan-card img {
439
+ width: 100%;
440
+ aspect-ratio: 1 / 1;
441
+ object-fit: contain;
442
+ border-radius: 0.5rem;
443
+ background: radial-gradient(circle at 20% 20%, rgba(56, 189, 248, 0.13), rgba(7, 12, 24, 0.95));
444
+ }
445
+
446
+ .orphan-meta {
447
+ font-size: 0.75rem;
448
+ color: #b9c9e4;
449
+ word-break: break-word;
450
+ }
451
+
452
+ .sticker-tile {
453
+ border: 1px solid #334b72;
454
+ background: linear-gradient(165deg, rgba(17, 26, 46, 0.95), rgba(14, 22, 40, 0.95));
455
+ border-radius: 0.75rem;
456
+ padding: 0.5rem;
457
+ position: relative;
458
+ }
459
+
460
+ .sticker-tile img {
461
+ width: 100%;
462
+ aspect-ratio: 1 / 1;
463
+ object-fit: contain;
464
+ border-radius: 0.5rem;
465
+ background: radial-gradient(circle at 20% 20%, rgba(56, 189, 248, 0.13), rgba(7, 12, 24, 0.95));
466
+ }
467
+
468
+ #pack-page-stickers .sticker-tile {
469
+ min-height: 120px;
470
+ }
471
+
472
+ .sticker-tags {
473
+ position: static;
474
+ display: flex;
475
+ flex-wrap: wrap;
476
+ justify-content: flex-start;
477
+ gap: 0.28rem;
478
+ max-width: 100%;
479
+ margin-top: 0.42rem;
480
+ margin-bottom: 0.2rem;
481
+ }
482
+
483
+ .sticker-tag {
484
+ font-size: 0.62rem;
485
+ line-height: 1;
486
+ padding: 0.24rem 0.42rem;
487
+ border-radius: 999px;
488
+ color: #eff7ff;
489
+ background: linear-gradient(120deg, rgba(16, 31, 57, 0.92), rgba(28, 53, 94, 0.9));
490
+ border: 1px solid rgba(111, 151, 220, 0.62);
491
+ box-shadow: 0 6px 14px rgba(2, 8, 22, 0.34), inset 0 1px 0 rgba(190, 216, 255, 0.2);
492
+ backdrop-filter: blur(6px);
493
+ text-transform: lowercase;
494
+ letter-spacing: 0.015em;
495
+ font-weight: 600;
496
+ }
497
+
498
+ .pack-tags {
499
+ display: flex;
500
+ flex-wrap: wrap;
501
+ gap: 0.3rem;
502
+ margin-top: 0.1rem;
503
+ }
504
+
505
+ .pack-tag {
506
+ display: inline-flex;
507
+ align-items: center;
508
+ border-radius: 999px;
509
+ padding: 0.2rem 0.44rem;
510
+ font-size: 0.62rem;
511
+ line-height: 1;
512
+ font-weight: 700;
513
+ text-transform: lowercase;
514
+ color: #def0ff;
515
+ border: 1px solid rgba(108, 151, 219, 0.58);
516
+ background: linear-gradient(120deg, rgba(13, 26, 48, 0.96), rgba(20, 39, 70, 0.94));
517
+ box-shadow: inset 0 1px 0 rgba(176, 206, 255, 0.16);
518
+ }
519
+
520
+ .modal-content {
521
+ background: #121a2f !important;
522
+ border-color: var(--line-soft) !important;
523
+ }
524
+
525
+ #panel-subtitle,
526
+ #panel-page-info,
527
+ #orphan-page-info,
528
+ .text-secondary {
529
+ color: var(--muted) !important;
530
+ }
531
+
532
+ .skeleton {
533
+ border-radius: 0.55rem;
534
+ background: linear-gradient(90deg, rgba(61, 85, 127, 0.26), rgba(104, 137, 189, 0.28), rgba(61, 85, 127, 0.26));
535
+ background-size: 220% 100%;
536
+ animation: shimmer 1.2s linear infinite;
537
+ }
538
+
539
+ .skeleton-thumb,
540
+ .skeleton-orphan {
541
+ width: 100%;
542
+ aspect-ratio: 1 / 1;
543
+ }
544
+
545
+ .skeleton-line {
546
+ width: 100%;
547
+ height: 14px;
548
+ }
549
+
550
+ .skeleton-line.short {
551
+ width: 58%;
552
+ }
553
+
554
+ @keyframes shimmer {
555
+ 0% {
556
+ background-position: 100% 0;
557
+ }
558
+ 100% {
559
+ background-position: -100% 0;
560
+ }
561
+ }
562
+
563
+ @keyframes fadeInUp {
564
+ from {
565
+ opacity: 0;
566
+ transform: translateY(6px);
567
+ }
568
+ to {
569
+ opacity: 1;
570
+ transform: translateY(0);
571
+ }
572
+ }
573
+
574
+ @media (max-width: 576px) {
575
+ .hero-card .card-body {
576
+ padding: 1.2rem !important;
577
+ }
578
+
579
+ #packs-section #grid {
580
+ --bs-gutter-x: 0.55rem;
581
+ --bs-gutter-y: 0.55rem;
582
+ }
583
+
584
+ #packs-section .pack-card .card-body {
585
+ padding: 0.48rem;
586
+ gap: 0.3rem !important;
587
+ }
588
+
589
+ #packs-section .pack-card .card-title {
590
+ font-size: 0.66rem;
591
+ line-height: 1.18;
592
+ margin-bottom: 0.1rem;
593
+ display: -webkit-box;
594
+ -webkit-line-clamp: 2;
595
+ -webkit-box-orient: vertical;
596
+ overflow: hidden;
597
+ }
598
+
599
+ #packs-section .pack-thumb {
600
+ border-radius: 0.4rem;
601
+ }
602
+
603
+ #packs-section .pack-meta {
604
+ font-size: 0.56rem;
605
+ line-height: 1.2;
606
+ }
607
+
608
+ .pack-visibility-badge,
609
+ .pack-count-badge {
610
+ font-size: 0.52rem;
611
+ padding: 0.28rem 0.36rem;
612
+ }
613
+
614
+ .sticker-tags {
615
+ gap: 0.18rem;
616
+ margin-top: 0.36rem;
617
+ margin-bottom: 0.16rem;
618
+ }
619
+
620
+ .sticker-tag {
621
+ font-size: 0.5rem;
622
+ padding: 0.18rem 0.28rem;
623
+ }
624
+
625
+ .categories-picker {
626
+ min-height: 96px;
627
+ padding: 0.4rem;
628
+ }
629
+
630
+ .categories-options {
631
+ grid-template-columns: 1fr;
632
+ max-height: 96px;
633
+ }
634
+
635
+ .category-chip {
636
+ font-size: 0.62rem;
637
+ padding: 0.16rem 0.38rem;
638
+ }
639
+
640
+ .category-option {
641
+ font-size: 0.68rem;
642
+ min-height: 26px;
643
+ }
644
+
645
+ .pack-tags {
646
+ gap: 0.2rem;
647
+ }
648
+
649
+ .pack-tag {
650
+ font-size: 0.5rem;
651
+ padding: 0.16rem 0.3rem;
652
+ }
653
+ }
654
+
655
+ @media (max-width: 430px) {
656
+ #packs-section .pack-card .card-body {
657
+ padding: 0.35rem;
658
+ gap: 0.25rem !important;
659
+ }
660
+
661
+ #packs-section .pack-card .card-title {
662
+ font-size: 0.62rem;
663
+ line-height: 1.15;
664
+ min-height: 1.45rem;
665
+ }
666
+
667
+ #packs-section .pack-meta {
668
+ font-size: 0.56rem;
669
+ line-height: 1.15;
670
+ }
671
+ }