pms_md 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +93 -0
  2. package/node-monitor/ARCHITECTURE.md +341 -0
  3. package/node-monitor/CHANGELOG.md +105 -0
  4. package/node-monitor/CONTRIBUTING.md +96 -0
  5. package/node-monitor/DESIGN_IMPROVEMENTS.md +286 -0
  6. package/node-monitor/FILTER_BUTTONS_FIX.md +303 -0
  7. package/node-monitor/GETTING_STARTED.md +416 -0
  8. package/node-monitor/INSTALLATION.md +470 -0
  9. package/node-monitor/LICENSE +22 -0
  10. package/node-monitor/PUBLISHING_GUIDE.md +331 -0
  11. package/node-monitor/QUICK_REFERENCE.md +252 -0
  12. package/node-monitor/README.md +458 -0
  13. package/node-monitor/READY_TO_PUBLISH.md +272 -0
  14. package/node-monitor/SETUP_GUIDE.md +479 -0
  15. package/node-monitor/examples/EMAIL_SETUP_GUIDE.md +282 -0
  16. package/node-monitor/examples/ERROR_LOGGING_GUIDE.md +405 -0
  17. package/node-monitor/examples/GET_APP_PASSWORD.md +145 -0
  18. package/node-monitor/examples/LOG_FILES_REFERENCE.md +336 -0
  19. package/node-monitor/examples/QUICK_START_EMAIL.md +126 -0
  20. package/node-monitor/examples/express-app.js +499 -0
  21. package/node-monitor/examples/package-lock.json +1295 -0
  22. package/node-monitor/examples/package.json +18 -0
  23. package/node-monitor/examples/public/css/style.css +718 -0
  24. package/node-monitor/examples/public/js/dashboard.js +207 -0
  25. package/node-monitor/examples/public/js/health.js +114 -0
  26. package/node-monitor/examples/public/js/main.js +89 -0
  27. package/node-monitor/examples/public/js/metrics.js +225 -0
  28. package/node-monitor/examples/public/js/theme.js +138 -0
  29. package/node-monitor/examples/views/dashboard.ejs +20 -0
  30. package/node-monitor/examples/views/error-logs.ejs +1129 -0
  31. package/node-monitor/examples/views/health.ejs +21 -0
  32. package/node-monitor/examples/views/home.ejs +341 -0
  33. package/node-monitor/examples/views/layout.ejs +50 -0
  34. package/node-monitor/examples/views/metrics.ejs +16 -0
  35. package/node-monitor/examples/views/partials/footer.ejs +16 -0
  36. package/node-monitor/examples/views/partials/header.ejs +35 -0
  37. package/node-monitor/examples/views/partials/nav.ejs +23 -0
  38. package/node-monitor/examples/views/status.ejs +390 -0
  39. package/node-monitor/package-lock.json +4300 -0
  40. package/node-monitor/package.json +76 -0
  41. package/node-monitor/pre-publish-check.js +200 -0
  42. package/node-monitor/src/config/monitoringConfig.js +255 -0
  43. package/node-monitor/src/index.js +300 -0
  44. package/node-monitor/src/logger/errorLogger.js +297 -0
  45. package/node-monitor/src/monitors/apiErrorMonitor.js +156 -0
  46. package/node-monitor/src/monitors/dbConnectionMonitor.js +389 -0
  47. package/node-monitor/src/monitors/serverHealthMonitor.js +320 -0
  48. package/node-monitor/src/monitors/systemResourceMonitor.js +357 -0
  49. package/node-monitor/src/notifiers/emailNotifier.js +248 -0
  50. package/node-monitor/src/notifiers/notificationManager.js +96 -0
  51. package/node-monitor/src/notifiers/slackNotifier.js +209 -0
  52. package/node-monitor/src/views/dashboard.html +530 -0
  53. package/node-monitor/src/views/health.html +399 -0
  54. package/node-monitor/src/views/metrics.html +406 -0
  55. package/package.json +22 -0
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "node-monitor-example",
3
+ "version": "1.0.0",
4
+ "description": "Example application demonstrating Node Monitor usage",
5
+ "main": "express-app.js",
6
+ "scripts": {
7
+ "start": "node express-app.js",
8
+ "dev": "nodemon express-app.js"
9
+ },
10
+ "dependencies": {
11
+ "dotenv": "^17.2.3",
12
+ "ejs": "^3.1.10",
13
+ "express": "^4.18.2"
14
+ },
15
+ "devDependencies": {
16
+ "nodemon": "^3.0.2"
17
+ }
18
+ }
@@ -0,0 +1,718 @@
1
+ /* ===================================
2
+ THEME VARIABLES
3
+ =================================== */
4
+ :root {
5
+ /* Light Mode (Default) */
6
+ --bg-gradient-start: #667eea;
7
+ --bg-gradient-end: #764ba2;
8
+ --card-bg: #ffffff;
9
+ --card-shadow: rgba(0, 0, 0, 0.2);
10
+ --text-primary: #333333;
11
+ --text-secondary: #666666;
12
+ --nav-bg: #f0f0f0;
13
+ --nav-text: #667eea;
14
+ --nav-hover-bg: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
15
+ --nav-hover-text: #ffffff;
16
+ --border-color: #e0e0e0;
17
+ --input-bg: #ffffff;
18
+ --input-border: #ddd;
19
+ --button-bg: #667eea;
20
+ --button-text: #ffffff;
21
+ --success-color: #10b981;
22
+ --error-color: #ef4444;
23
+ --warning-color: #f59e0b;
24
+ }
25
+
26
+ [data-theme="dark"] {
27
+ /* Dark Mode - Maximum Visibility */
28
+ --bg-gradient-start: #0a0f1e;
29
+ --bg-gradient-end: #1a2332;
30
+ --card-bg: #1e2936;
31
+ --card-shadow: rgba(0, 0, 0, 0.8);
32
+ --text-primary: #ffffff;
33
+ --text-secondary: #e2e8f0;
34
+ --nav-bg: #0f1623;
35
+ --nav-text: #60a5fa;
36
+ --nav-hover-bg: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
37
+ --nav-hover-text: #ffffff;
38
+ --border-color: #3d4f66;
39
+ --input-bg: #0f1623;
40
+ --input-border: #3d4f66;
41
+ --button-bg: #3b82f6;
42
+ --button-text: #ffffff;
43
+ --success-color: #34d399;
44
+ --error-color: #f87171;
45
+ --warning-color: #fbbf24;
46
+ }
47
+
48
+ /* Disable all transitions during theme change to prevent flickering */
49
+ html.theme-transitioning,
50
+ html.theme-transitioning *,
51
+ html.theme-transitioning *:before,
52
+ html.theme-transitioning *:after {
53
+ transition: none !important;
54
+ animation: none !important;
55
+ }
56
+
57
+ /* Dark mode specific improvements for maximum visibility */
58
+ [data-theme="dark"] .card {
59
+ border: 1px solid var(--border-color);
60
+ background: var(--card-bg) !important;
61
+ }
62
+
63
+ [data-theme="dark"] .card-title,
64
+ [data-theme="dark"] .header h1,
65
+ [data-theme="dark"] h1,
66
+ [data-theme="dark"] h2,
67
+ [data-theme="dark"] h3 {
68
+ color: var(--text-primary) !important;
69
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
70
+ font-weight: 700;
71
+ }
72
+
73
+ [data-theme="dark"] .metric-label,
74
+ [data-theme="dark"] .stat-label {
75
+ color: var(--text-secondary) !important;
76
+ font-weight: 600;
77
+ }
78
+
79
+ [data-theme="dark"] .metric-value,
80
+ [data-theme="dark"] .stat-value {
81
+ color: var(--text-primary) !important;
82
+ font-weight: 700;
83
+ }
84
+
85
+ [data-theme="dark"] p,
86
+ [data-theme="dark"] span,
87
+ [data-theme="dark"] div {
88
+ color: var(--text-secondary);
89
+ }
90
+
91
+ [data-theme="dark"] .subtitle {
92
+ color: var(--text-secondary) !important;
93
+ font-weight: 500;
94
+ }
95
+
96
+ [data-theme="dark"] .nav-link {
97
+ color: var(--nav-text) !important;
98
+ font-weight: 600;
99
+ }
100
+
101
+ /* ===================================
102
+ GLOBAL STYLES
103
+ =================================== */
104
+ * {
105
+ margin: 0;
106
+ padding: 0;
107
+ box-sizing: border-box;
108
+ }
109
+
110
+ html {
111
+ scroll-behavior: smooth;
112
+ transition: none;
113
+ }
114
+
115
+ body {
116
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
117
+ background: linear-gradient(135deg, var(--bg-gradient-start) 0%, var(--bg-gradient-end) 100%);
118
+ min-height: 100vh;
119
+ padding: 20px;
120
+ transition: none;
121
+ }
122
+
123
+ .container {
124
+ max-width: 1400px;
125
+ margin: 0 auto;
126
+ position: relative;
127
+ z-index: 1;
128
+ }
129
+
130
+ /* ===================================
131
+ HEADER
132
+ =================================== */
133
+ .header {
134
+ background: var(--card-bg);
135
+ border-radius: 15px;
136
+ padding: 30px;
137
+ margin-bottom: 30px;
138
+ box-shadow: 0 10px 30px var(--card-shadow);
139
+ transition: none;
140
+ }
141
+
142
+ .header h1 {
143
+ color: var(--text-primary);
144
+ font-size: 2.5em;
145
+ margin-bottom: 10px;
146
+ transition: none;
147
+ }
148
+
149
+ .subtitle {
150
+ color: var(--text-secondary);
151
+ font-size: 1.1em;
152
+ margin-bottom: 20px;
153
+ transition: none;
154
+ }
155
+
156
+ /* ===================================
157
+ NAVIGATION
158
+ =================================== */
159
+ .nav-links {
160
+ display: flex;
161
+ gap: 15px;
162
+ margin-top: 20px;
163
+ flex-wrap: wrap;
164
+ }
165
+
166
+ .nav-link {
167
+ color: var(--nav-text);
168
+ text-decoration: none;
169
+ padding: 10px 20px;
170
+ background: var(--nav-bg);
171
+ border-radius: 8px;
172
+ transition: all 0.3s ease;
173
+ font-weight: 500;
174
+ }
175
+
176
+ .nav-link:hover,
177
+ .nav-link.active {
178
+ background: var(--nav-hover-bg);
179
+ color: var(--nav-hover-text);
180
+ transform: translateY(-2px);
181
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
182
+ }
183
+
184
+ .nav-link:focus {
185
+ outline: 2px solid var(--nav-text);
186
+ outline-offset: 2px;
187
+ }
188
+
189
+ /* ===================================
190
+ THEME TOGGLE BUTTON
191
+ =================================== */
192
+ .theme-toggle {
193
+ position: fixed !important;
194
+ top: 20px !important;
195
+ right: 20px !important;
196
+ background: var(--card-bg) !important;
197
+ color: var(--text-primary) !important;
198
+ border: 2px solid var(--border-color) !important;
199
+ border-radius: 25px !important;
200
+ padding: 10px 20px !important;
201
+ cursor: pointer !important;
202
+ font-size: 0.9em !important;
203
+ font-weight: 600 !important;
204
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3) !important;
205
+ transition: transform 0.2s ease, box-shadow 0.2s ease !important;
206
+ z-index: 999999 !important;
207
+ display: flex !important;
208
+ align-items: center !important;
209
+ gap: 8px !important;
210
+ opacity: 1 !important;
211
+ visibility: visible !important;
212
+ white-space: nowrap !important;
213
+ line-height: 1 !important;
214
+ pointer-events: auto !important;
215
+ }
216
+
217
+ .theme-toggle:hover {
218
+ transform: translateY(-2px);
219
+ box-shadow: 0 4px 15px var(--card-shadow);
220
+ }
221
+
222
+ .theme-toggle:active {
223
+ transform: translateY(0);
224
+ }
225
+
226
+ /* Responsive positioning for theme toggle */
227
+ @media (max-width: 768px) {
228
+ .theme-toggle {
229
+ top: 10px;
230
+ right: 10px;
231
+ padding: 6px 12px;
232
+ font-size: 0.75em;
233
+ gap: 4px;
234
+ }
235
+ }
236
+
237
+ /* ===================================
238
+ CARDS
239
+ =================================== */
240
+ .card {
241
+ background: var(--card-bg);
242
+ border-radius: 15px;
243
+ padding: 25px;
244
+ box-shadow: 0 10px 30px var(--card-shadow);
245
+ transition: transform 0.3s ease, box-shadow 0.3s ease, background 0.3s ease;
246
+ margin-bottom: 20px;
247
+ }
248
+
249
+ .card:hover {
250
+ transform: translateY(-5px);
251
+ box-shadow: 0 15px 40px var(--card-shadow);
252
+ }
253
+
254
+ .card-header {
255
+ display: flex;
256
+ align-items: center;
257
+ gap: 15px;
258
+ margin-bottom: 20px;
259
+ padding-bottom: 15px;
260
+ border-bottom: 2px solid var(--border-color);
261
+ }
262
+
263
+ .card-icon {
264
+ font-size: 2.5em;
265
+ }
266
+
267
+ .card-title {
268
+ font-size: 1.3em;
269
+ color: var(--text-primary);
270
+ font-weight: 600;
271
+ }
272
+
273
+ /* ===================================
274
+ GRID LAYOUTS
275
+ =================================== */
276
+ .grid {
277
+ display: grid;
278
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
279
+ gap: 20px;
280
+ margin-bottom: 30px;
281
+ }
282
+
283
+ .stats-grid {
284
+ display: grid;
285
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
286
+ gap: 20px;
287
+ margin-bottom: 30px;
288
+ }
289
+
290
+ /* ===================================
291
+ METRICS
292
+ =================================== */
293
+ .metric {
294
+ display: flex;
295
+ justify-content: space-between;
296
+ align-items: center;
297
+ padding: 12px 0;
298
+ border-bottom: 1px solid var(--border-color);
299
+ }
300
+
301
+ .metric:last-child {
302
+ border-bottom: none;
303
+ }
304
+
305
+ .metric-label {
306
+ color: var(--text-secondary);
307
+ font-size: 0.95em;
308
+ }
309
+
310
+ .metric-value {
311
+ font-weight: bold;
312
+ font-size: 1.1em;
313
+ color: var(--text-primary);
314
+ }
315
+
316
+ .metric-value.good {
317
+ color: var(--success-color);
318
+ }
319
+
320
+ .metric-value.warning {
321
+ color: var(--warning-color);
322
+ }
323
+
324
+ .metric-value.danger {
325
+ color: var(--error-color);
326
+ }
327
+
328
+ /* ===================================
329
+ STATUS HEADER
330
+ =================================== */
331
+ .status-header {
332
+ display: flex;
333
+ align-items: center;
334
+ gap: 20px;
335
+ margin-bottom: 25px;
336
+ flex-wrap: wrap;
337
+ }
338
+
339
+ /* ===================================
340
+ STATUS BADGES
341
+ =================================== */
342
+ .status-badge {
343
+ display: inline-flex;
344
+ align-items: center;
345
+ gap: 10px;
346
+ padding: 12px 25px;
347
+ border-radius: 25px;
348
+ font-weight: bold;
349
+ font-size: 1em;
350
+ box-shadow: 0 5px 15px rgba(0,0,0,0.2);
351
+ }
352
+
353
+ .status-badge.healthy {
354
+ background: linear-gradient(135deg, #10b981, #059669);
355
+ color: white;
356
+ }
357
+
358
+ .status-badge.unhealthy {
359
+ background: linear-gradient(135deg, #ef4444, #dc2626);
360
+ color: white;
361
+ }
362
+
363
+ .status-dot {
364
+ width: 12px;
365
+ height: 12px;
366
+ border-radius: 50%;
367
+ background: white;
368
+ animation: pulse 2s infinite;
369
+ }
370
+
371
+ @keyframes pulse {
372
+ 0%, 100% { opacity: 1; transform: scale(1); }
373
+ 50% { opacity: 0.5; transform: scale(1.2); }
374
+ }
375
+
376
+ /* ===================================
377
+ HEALTH PAGE STATUS INDICATOR
378
+ =================================== */
379
+ .health-header {
380
+ background: var(--card-bg);
381
+ border-radius: 15px;
382
+ padding: 30px;
383
+ margin-bottom: 30px;
384
+ box-shadow: 0 10px 30px var(--card-shadow);
385
+ text-align: center;
386
+ transition: background 0.3s ease;
387
+ }
388
+
389
+ .status-indicator {
390
+ display: inline-flex;
391
+ align-items: center;
392
+ gap: 15px;
393
+ padding: 18px 35px;
394
+ border-radius: 50px;
395
+ font-size: 1.4em;
396
+ font-weight: 700;
397
+ margin: 0;
398
+ box-shadow: 0 8px 25px rgba(0,0,0,0.15);
399
+ transition: all 0.3s ease;
400
+ }
401
+
402
+ .status-indicator.healthy {
403
+ background: linear-gradient(135deg, #10b981, #059669);
404
+ color: white;
405
+ }
406
+
407
+ .status-indicator.unhealthy {
408
+ background: linear-gradient(135deg, #ef4444, #dc2626);
409
+ color: white;
410
+ }
411
+
412
+ .status-indicator .pulse {
413
+ width: 16px;
414
+ height: 16px;
415
+ border-radius: 50%;
416
+ background: white;
417
+ animation: pulse 2s infinite;
418
+ flex-shrink: 0;
419
+ }
420
+
421
+ .status-indicator #statusText {
422
+ font-weight: 700;
423
+ letter-spacing: 0.5px;
424
+ }
425
+
426
+ /* ===================================
427
+ PROGRESS BARS
428
+ =================================== */
429
+ .progress-bar {
430
+ width: 100%;
431
+ height: 10px;
432
+ background: var(--border-color);
433
+ border-radius: 10px;
434
+ overflow: hidden;
435
+ margin-top: 8px;
436
+ }
437
+
438
+ .progress-fill {
439
+ height: 100%;
440
+ background: linear-gradient(90deg, #10b981, #059669);
441
+ border-radius: 10px;
442
+ transition: width 0.3s ease;
443
+ }
444
+
445
+ .progress-fill.warning {
446
+ background: linear-gradient(90deg, #f59e0b, #d97706);
447
+ }
448
+
449
+ .progress-fill.danger {
450
+ background: linear-gradient(90deg, #ef4444, #dc2626);
451
+ }
452
+
453
+ /* ===================================
454
+ BUTTONS
455
+ =================================== */
456
+ .refresh-btn {
457
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
458
+ color: white;
459
+ border: none;
460
+ padding: 12px 30px;
461
+ border-radius: 25px;
462
+ font-size: 1em;
463
+ font-weight: bold;
464
+ cursor: pointer;
465
+ transition: transform 0.2s ease;
466
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
467
+ }
468
+
469
+ .refresh-btn:hover {
470
+ transform: scale(1.05);
471
+ }
472
+
473
+ .refresh-btn:active {
474
+ transform: scale(0.95);
475
+ }
476
+
477
+ .refresh-btn:focus {
478
+ outline: 2px solid #667eea;
479
+ outline-offset: 3px;
480
+ }
481
+
482
+ .action-buttons {
483
+ text-align: center;
484
+ margin: 30px 0;
485
+ }
486
+
487
+ /* ===================================
488
+ LOADING
489
+ =================================== */
490
+ .loading {
491
+ text-align: center;
492
+ padding: 40px;
493
+ color: var(--text-secondary);
494
+ animation: fadeIn 0.3s ease-in;
495
+ }
496
+
497
+ @keyframes fadeIn {
498
+ from { opacity: 0; }
499
+ to { opacity: 1; }
500
+ }
501
+
502
+ .spinner {
503
+ border: 4px solid #f3f4f6;
504
+ border-top: 4px solid #667eea;
505
+ border-radius: 50%;
506
+ width: 50px;
507
+ height: 50px;
508
+ animation: spin 1s linear infinite;
509
+ margin: 0 auto 20px;
510
+ }
511
+
512
+ @keyframes spin {
513
+ 0% { transform: rotate(0deg); }
514
+ 100% { transform: rotate(360deg); }
515
+ }
516
+
517
+ /* ===================================
518
+ FOOTER
519
+ =================================== */
520
+ .footer {
521
+ text-align: center;
522
+ color: white;
523
+ margin-top: 30px;
524
+ padding: 20px;
525
+ }
526
+
527
+ .timestamp {
528
+ color: rgba(255,255,255,0.8);
529
+ font-size: 0.9em;
530
+ margin-top: 10px;
531
+ }
532
+
533
+ /* ===================================
534
+ HOME PAGE
535
+ =================================== */
536
+ .home-container {
537
+ background: var(--card-bg);
538
+ border-radius: 20px;
539
+ padding: 50px;
540
+ box-shadow: 0 20px 60px var(--card-shadow);
541
+ transition: background 0.3s ease;
542
+ }
543
+
544
+ .hero {
545
+ text-align: center;
546
+ margin-bottom: 50px;
547
+ }
548
+
549
+ .hero-title {
550
+ font-size: 3.5em;
551
+ color: var(--text-primary);
552
+ margin-bottom: 15px;
553
+ }
554
+
555
+ .hero-subtitle {
556
+ font-size: 1.3em;
557
+ color: var(--text-secondary);
558
+ }
559
+
560
+ .links-grid {
561
+ display: grid;
562
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
563
+ gap: 25px;
564
+ margin-bottom: 50px;
565
+ }
566
+
567
+ .link-card {
568
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
569
+ padding: 40px;
570
+ border-radius: 15px;
571
+ text-align: center;
572
+ text-decoration: none;
573
+ color: white;
574
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
575
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
576
+ }
577
+
578
+ .link-card:hover {
579
+ transform: translateY(-10px);
580
+ box-shadow: 0 15px 40px rgba(102, 126, 234, 0.5);
581
+ }
582
+
583
+ .link-card:focus {
584
+ outline: 3px solid white;
585
+ outline-offset: 3px;
586
+ transform: translateY(-10px);
587
+ box-shadow: 0 15px 40px rgba(102, 126, 234, 0.5);
588
+ }
589
+
590
+ .link-icon {
591
+ font-size: 4em;
592
+ margin-bottom: 15px;
593
+ }
594
+
595
+ .link-title {
596
+ font-size: 1.5em;
597
+ font-weight: bold;
598
+ margin-bottom: 10px;
599
+ }
600
+
601
+ .link-desc {
602
+ font-size: 1em;
603
+ opacity: 0.9;
604
+ }
605
+
606
+ .api-section {
607
+ margin-top: 50px;
608
+ padding-top: 40px;
609
+ border-top: 2px solid var(--border-color);
610
+ }
611
+
612
+ .api-section h2 {
613
+ color: var(--text-primary);
614
+ margin-bottom: 25px;
615
+ font-size: 1.8em;
616
+ }
617
+
618
+ .api-links {
619
+ display: flex;
620
+ gap: 15px;
621
+ flex-wrap: wrap;
622
+ }
623
+
624
+ .api-link {
625
+ padding: 12px 25px;
626
+ background: var(--nav-bg);
627
+ border-radius: 8px;
628
+ text-decoration: none;
629
+ color: var(--nav-text);
630
+ font-weight: 500;
631
+ transition: all 0.3s ease;
632
+ border: 2px solid transparent;
633
+ cursor: pointer;
634
+ }
635
+
636
+ .api-link:hover {
637
+ background: var(--nav-hover-bg);
638
+ color: var(--nav-hover-text);
639
+ transform: translateY(-2px);
640
+ }
641
+
642
+ .api-link:focus {
643
+ outline: none;
644
+ border-color: var(--nav-text);
645
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
646
+ }
647
+
648
+ .features-section {
649
+ margin-top: 50px;
650
+ padding-top: 40px;
651
+ border-top: 2px solid var(--border-color);
652
+ }
653
+
654
+ .features-section h2 {
655
+ color: var(--text-primary);
656
+ margin-bottom: 30px;
657
+ font-size: 1.8em;
658
+ }
659
+
660
+ .features-grid {
661
+ display: grid;
662
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
663
+ gap: 25px;
664
+ }
665
+
666
+ .feature-item {
667
+ text-align: center;
668
+ padding: 25px;
669
+ background: var(--nav-bg);
670
+ border-radius: 12px;
671
+ transition: all 0.3s ease;
672
+ }
673
+
674
+ .feature-item:hover {
675
+ background: var(--border-color);
676
+ transform: translateY(-5px);
677
+ }
678
+
679
+ .feature-icon {
680
+ font-size: 3em;
681
+ margin-bottom: 15px;
682
+ }
683
+
684
+ .feature-item h3 {
685
+ color: var(--text-primary);
686
+ margin-bottom: 10px;
687
+ font-size: 1.2em;
688
+ }
689
+
690
+ .feature-item p {
691
+ color: var(--text-secondary);
692
+ font-size: 0.95em;
693
+ }
694
+
695
+ /* ===================================
696
+ RESPONSIVE
697
+ =================================== */
698
+ @media (max-width: 768px) {
699
+ .header h1 {
700
+ font-size: 1.8em;
701
+ }
702
+
703
+ .hero-title {
704
+ font-size: 2.5em;
705
+ }
706
+
707
+ .grid,
708
+ .stats-grid,
709
+ .links-grid,
710
+ .features-grid {
711
+ grid-template-columns: 1fr;
712
+ }
713
+
714
+ .home-container {
715
+ padding: 30px 20px;
716
+ }
717
+ }
718
+