sqa 0.0.31 → 0.0.37

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 (134) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +104 -0
  3. data/CLAUDE.md +21 -0
  4. data/README.md +60 -32
  5. data/Rakefile +52 -10
  6. data/docs/IMPROVEMENT_PLAN.md +531 -0
  7. data/docs/advanced/index.md +1 -13
  8. data/docs/api/dataframe.md +0 -1
  9. data/docs/api/index.md +547 -61
  10. data/docs/api-reference/alphavantageapi.md +1057 -0
  11. data/docs/api-reference/apierror.md +31 -0
  12. data/docs/api-reference/index.md +221 -0
  13. data/docs/api-reference/notimplemented.md +27 -0
  14. data/docs/api-reference/sqa.md +267 -0
  15. data/docs/api-reference/sqa_backtest.md +137 -0
  16. data/docs/api-reference/sqa_backtest_results.md +530 -0
  17. data/docs/api-reference/sqa_badparametererror.md +13 -0
  18. data/docs/api-reference/sqa_config.md +538 -0
  19. data/docs/api-reference/sqa_configurationerror.md +13 -0
  20. data/docs/api-reference/sqa_datafetcherror.md +56 -0
  21. data/docs/api-reference/sqa_dataframe.md +752 -0
  22. data/docs/api-reference/sqa_dataframe_alphavantage.md +30 -0
  23. data/docs/api-reference/sqa_dataframe_data.md +325 -0
  24. data/docs/api-reference/sqa_dataframe_yahoofinance.md +25 -0
  25. data/docs/api-reference/sqa_ensemble.md +413 -0
  26. data/docs/api-reference/sqa_fpop.md +211 -0
  27. data/docs/api-reference/sqa_geneticprogram.md +325 -0
  28. data/docs/api-reference/sqa_geneticprogram_individual.md +114 -0
  29. data/docs/api-reference/sqa_marketregime.md +212 -0
  30. data/docs/api-reference/sqa_multitimeframe.md +227 -0
  31. data/docs/api-reference/sqa_patternmatcher.md +195 -0
  32. data/docs/api-reference/sqa_pluginmanager.md +55 -0
  33. data/docs/api-reference/sqa_portfolio.md +455 -0
  34. data/docs/api-reference/sqa_portfolio_position.md +220 -0
  35. data/docs/api-reference/sqa_portfolio_trade.md +332 -0
  36. data/docs/api-reference/sqa_portfoliooptimizer.md +248 -0
  37. data/docs/api-reference/sqa_riskmanager.md +388 -0
  38. data/docs/api-reference/sqa_seasonalanalyzer.md +121 -0
  39. data/docs/api-reference/sqa_sectoranalyzer.md +163 -0
  40. data/docs/api-reference/sqa_stock.md +649 -0
  41. data/docs/api-reference/sqa_strategy.md +178 -0
  42. data/docs/api-reference/sqa_strategy_bollingerbands.md +26 -0
  43. data/docs/api-reference/sqa_strategy_common.md +29 -0
  44. data/docs/api-reference/sqa_strategy_consensus.md +129 -0
  45. data/docs/api-reference/sqa_strategy_ema.md +41 -0
  46. data/docs/api-reference/sqa_strategy_kbs.md +154 -0
  47. data/docs/api-reference/sqa_strategy_macd.md +26 -0
  48. data/docs/api-reference/sqa_strategy_mp.md +41 -0
  49. data/docs/api-reference/sqa_strategy_mr.md +41 -0
  50. data/docs/api-reference/sqa_strategy_random.md +41 -0
  51. data/docs/api-reference/sqa_strategy_rsi.md +41 -0
  52. data/docs/api-reference/sqa_strategy_sma.md +41 -0
  53. data/docs/api-reference/sqa_strategy_stochastic.md +26 -0
  54. data/docs/api-reference/sqa_strategy_volumebreakout.md +26 -0
  55. data/docs/api-reference/sqa_strategygenerator.md +298 -0
  56. data/docs/api-reference/sqa_strategygenerator_pattern.md +264 -0
  57. data/docs/api-reference/sqa_strategygenerator_patterncontext.md +326 -0
  58. data/docs/api-reference/sqa_strategygenerator_profitablepoint.md +424 -0
  59. data/docs/api-reference/sqa_stream.md +256 -0
  60. data/docs/api-reference/sqa_ticker.md +175 -0
  61. data/docs/api-reference/string.md +135 -0
  62. data/docs/assets/images/advanced-workflow.svg +89 -0
  63. data/docs/assets/images/architecture.svg +107 -0
  64. data/docs/assets/images/data-flow.svg +138 -0
  65. data/docs/assets/images/getting-started-workflow.svg +88 -0
  66. data/docs/assets/images/sqa.jpg +0 -0
  67. data/docs/assets/images/strategy-flow.svg +78 -0
  68. data/docs/assets/images/system-architecture.svg +150 -0
  69. data/docs/concepts/index.md +292 -27
  70. data/docs/data_frame.md +0 -1
  71. data/docs/getting-started/index.md +1 -30
  72. data/docs/getting-started/installation.md +2 -2
  73. data/docs/getting-started/quick-start.md +4 -4
  74. data/docs/index.md +26 -25
  75. data/docs/llms.txt +109 -0
  76. data/docs/strategies/bollinger-bands.md +1 -1
  77. data/docs/strategies/kbs.md +15 -14
  78. data/docs/strategies/rsi.md +1 -1
  79. data/docs/strategy.md +381 -3
  80. data/docs/terms_of_use.md +1 -1
  81. data/examples/README.md +10 -0
  82. data/lib/api/alpha_vantage_api.rb +3 -7
  83. data/lib/sqa/config.rb +109 -28
  84. data/lib/sqa/data_frame/alpha_vantage.rb +13 -3
  85. data/lib/sqa/data_frame/data.rb +13 -1
  86. data/lib/sqa/data_frame.rb +189 -41
  87. data/lib/sqa/errors.rb +79 -17
  88. data/lib/sqa/indicator.rb +17 -4
  89. data/lib/sqa/init.rb +70 -15
  90. data/lib/sqa/pattern_matcher.rb +4 -4
  91. data/lib/sqa/portfolio.rb +1 -1
  92. data/lib/sqa/sector_analyzer.rb +3 -11
  93. data/lib/sqa/stock.rb +236 -20
  94. data/lib/sqa/strategy.rb +62 -4
  95. data/lib/sqa/ticker.rb +107 -42
  96. data/lib/sqa/version.rb +1 -1
  97. data/lib/sqa.rb +16 -8
  98. data/mkdocs.yml +68 -117
  99. metadata +90 -36
  100. data/docs/README.md +0 -43
  101. data/docs/alpha_vantage_technical_indicators.md +0 -62
  102. data/docs/average_true_range.md +0 -9
  103. data/docs/bollinger_bands.md +0 -15
  104. data/docs/candlestick_pattern_recognizer.md +0 -4
  105. data/docs/donchian_channel.md +0 -5
  106. data/docs/double_top_bottom_pattern.md +0 -3
  107. data/docs/exponential_moving_average.md +0 -19
  108. data/docs/fibonacci_retracement.md +0 -30
  109. data/docs/head_and_shoulders_pattern.md +0 -3
  110. data/docs/market_profile.md +0 -4
  111. data/docs/momentum.md +0 -19
  112. data/docs/moving_average_convergence_divergence.md +0 -23
  113. data/docs/peaks_and_valleys.md +0 -11
  114. data/docs/relative_strength_index.md +0 -6
  115. data/docs/simple_moving_average.md +0 -8
  116. data/docs/stochastic_oscillator.md +0 -4
  117. data/docs/ta_lib.md +0 -160
  118. data/docs/true_range.md +0 -12
  119. data/docs/true_strength_index.md +0 -46
  120. data/docs/weighted_moving_average.md +0 -48
  121. data/examples/sinatra_app/Gemfile +0 -22
  122. data/examples/sinatra_app/QUICKSTART.md +0 -159
  123. data/examples/sinatra_app/README.md +0 -461
  124. data/examples/sinatra_app/app.rb +0 -344
  125. data/examples/sinatra_app/config.ru +0 -5
  126. data/examples/sinatra_app/public/css/style.css +0 -659
  127. data/examples/sinatra_app/public/js/app.js +0 -107
  128. data/examples/sinatra_app/views/analyze.erb +0 -306
  129. data/examples/sinatra_app/views/backtest.erb +0 -325
  130. data/examples/sinatra_app/views/dashboard.erb +0 -419
  131. data/examples/sinatra_app/views/error.erb +0 -58
  132. data/examples/sinatra_app/views/index.erb +0 -118
  133. data/examples/sinatra_app/views/layout.erb +0 -61
  134. data/examples/sinatra_app/views/portfolio.erb +0 -43
@@ -1,659 +0,0 @@
1
- /* Reset and Base Styles */
2
- * {
3
- margin: 0;
4
- padding: 0;
5
- box-sizing: border-box;
6
- }
7
-
8
- :root {
9
- --primary-color: #2196F3;
10
- --secondary-color: #1976D2;
11
- --success-color: #4CAF50;
12
- --danger-color: #F44336;
13
- --warning-color: #FF9800;
14
- --dark-bg: #1a1a2e;
15
- --light-bg: #f5f7fa;
16
- --card-bg: #ffffff;
17
- --text-primary: #2c3e50;
18
- --text-secondary: #7f8c8d;
19
- --border-color: #e0e0e0;
20
- --positive-color: #26a69a;
21
- --negative-color: #ef5350;
22
- }
23
-
24
- body {
25
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
26
- background-color: var(--light-bg);
27
- color: var(--text-primary);
28
- line-height: 1.6;
29
- }
30
-
31
- /* Navigation */
32
- .navbar {
33
- background: linear-gradient(135deg, var(--dark-bg) 0%, #16213e 100%);
34
- color: white;
35
- padding: 1rem 0;
36
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
37
- position: sticky;
38
- top: 0;
39
- z-index: 1000;
40
- }
41
-
42
- .nav-container {
43
- max-width: 1200px;
44
- margin: 0 auto;
45
- padding: 0 2rem;
46
- display: flex;
47
- justify-content: space-between;
48
- align-items: center;
49
- }
50
-
51
- .nav-brand {
52
- font-size: 1.5rem;
53
- font-weight: 700;
54
- display: flex;
55
- align-items: center;
56
- gap: 0.5rem;
57
- }
58
-
59
- .nav-brand i {
60
- color: var(--primary-color);
61
- }
62
-
63
- .nav-menu {
64
- display: flex;
65
- list-style: none;
66
- gap: 2rem;
67
- }
68
-
69
- .nav-link {
70
- color: white;
71
- text-decoration: none;
72
- font-weight: 500;
73
- transition: color 0.3s ease;
74
- display: flex;
75
- align-items: center;
76
- gap: 0.5rem;
77
- }
78
-
79
- .nav-link:hover {
80
- color: var(--primary-color);
81
- }
82
-
83
- /* Main Content */
84
- .main-content {
85
- min-height: calc(100vh - 200px);
86
- padding-bottom: 2rem;
87
- }
88
-
89
- /* Hero Section */
90
- .hero {
91
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
92
- color: white;
93
- padding: 4rem 2rem;
94
- text-align: center;
95
- }
96
-
97
- .hero-content h1 {
98
- font-size: 3rem;
99
- margin-bottom: 1rem;
100
- font-weight: 700;
101
- }
102
-
103
- .hero-subtitle {
104
- font-size: 1.25rem;
105
- margin-bottom: 2rem;
106
- opacity: 0.9;
107
- }
108
-
109
- .ticker-search {
110
- max-width: 600px;
111
- margin: 2rem auto;
112
- }
113
-
114
- .search-form {
115
- display: flex;
116
- gap: 1rem;
117
- }
118
-
119
- .search-form input {
120
- flex: 1;
121
- padding: 1rem;
122
- border: none;
123
- border-radius: 8px;
124
- font-size: 1.1rem;
125
- outline: none;
126
- }
127
-
128
- .quick-links {
129
- margin-top: 3rem;
130
- }
131
-
132
- .quick-links h3 {
133
- margin-bottom: 1rem;
134
- font-weight: 600;
135
- }
136
-
137
- .ticker-buttons {
138
- display: flex;
139
- gap: 1rem;
140
- justify-content: center;
141
- flex-wrap: wrap;
142
- }
143
-
144
- .ticker-btn {
145
- background: rgba(255, 255, 255, 0.2);
146
- color: white;
147
- padding: 0.75rem 1.5rem;
148
- border-radius: 8px;
149
- text-decoration: none;
150
- font-weight: 600;
151
- transition: all 0.3s ease;
152
- backdrop-filter: blur(10px);
153
- }
154
-
155
- .ticker-btn:hover {
156
- background: rgba(255, 255, 255, 0.3);
157
- transform: translateY(-2px);
158
- }
159
-
160
- /* Buttons */
161
- .btn {
162
- padding: 0.75rem 1.5rem;
163
- border: none;
164
- border-radius: 8px;
165
- font-size: 1rem;
166
- font-weight: 600;
167
- cursor: pointer;
168
- transition: all 0.3s ease;
169
- text-decoration: none;
170
- display: inline-flex;
171
- align-items: center;
172
- gap: 0.5rem;
173
- }
174
-
175
- .btn-primary {
176
- background: var(--primary-color);
177
- color: white;
178
- }
179
-
180
- .btn-primary:hover {
181
- background: var(--secondary-color);
182
- transform: translateY(-2px);
183
- box-shadow: 0 4px 12px rgba(33, 150, 243, 0.3);
184
- }
185
-
186
- .btn-secondary {
187
- background: white;
188
- color: var(--text-primary);
189
- border: 2px solid var(--border-color);
190
- }
191
-
192
- .btn-secondary:hover {
193
- border-color: var(--primary-color);
194
- color: var(--primary-color);
195
- }
196
-
197
- .btn-large {
198
- padding: 1rem 2rem;
199
- font-size: 1.1rem;
200
- }
201
-
202
- .btn-small {
203
- padding: 0.5rem 1rem;
204
- font-size: 0.875rem;
205
- background: white;
206
- color: var(--text-primary);
207
- border: 1px solid var(--border-color);
208
- }
209
-
210
- .btn-small.active {
211
- background: var(--primary-color);
212
- color: white;
213
- border-color: var(--primary-color);
214
- }
215
-
216
- /* Container */
217
- .container {
218
- max-width: 1200px;
219
- margin: 0 auto;
220
- padding: 0 2rem;
221
- }
222
-
223
- /* Features Section */
224
- .features {
225
- padding: 4rem 0;
226
- background: white;
227
- }
228
-
229
- .features h2 {
230
- text-align: center;
231
- font-size: 2.5rem;
232
- margin-bottom: 3rem;
233
- color: var(--text-primary);
234
- }
235
-
236
- .feature-grid {
237
- display: grid;
238
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
239
- gap: 2rem;
240
- }
241
-
242
- .feature-card {
243
- padding: 2rem;
244
- background: var(--card-bg);
245
- border-radius: 12px;
246
- border: 1px solid var(--border-color);
247
- transition: all 0.3s ease;
248
- }
249
-
250
- .feature-card:hover {
251
- transform: translateY(-5px);
252
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
253
- }
254
-
255
- .feature-icon {
256
- font-size: 3rem;
257
- color: var(--primary-color);
258
- margin-bottom: 1rem;
259
- }
260
-
261
- .feature-card h3 {
262
- font-size: 1.5rem;
263
- margin-bottom: 1rem;
264
- color: var(--text-primary);
265
- }
266
-
267
- .feature-card p {
268
- color: var(--text-secondary);
269
- line-height: 1.6;
270
- }
271
-
272
- /* Info Section */
273
- .info-section {
274
- padding: 4rem 0;
275
- background: var(--light-bg);
276
- }
277
-
278
- .info-content h2 {
279
- font-size: 2rem;
280
- margin-bottom: 1.5rem;
281
- color: var(--text-primary);
282
- }
283
-
284
- .info-content p {
285
- font-size: 1.1rem;
286
- color: var(--text-secondary);
287
- margin-bottom: 1.5rem;
288
- line-height: 1.8;
289
- }
290
-
291
- .info-list {
292
- list-style: none;
293
- display: grid;
294
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
295
- gap: 1rem;
296
- margin-top: 2rem;
297
- }
298
-
299
- .info-list li {
300
- display: flex;
301
- align-items: center;
302
- gap: 0.75rem;
303
- font-size: 1rem;
304
- color: var(--text-primary);
305
- }
306
-
307
- .info-list i {
308
- color: var(--success-color);
309
- font-size: 1.2rem;
310
- }
311
-
312
- /* Dashboard */
313
- .dashboard {
314
- max-width: 1400px;
315
- margin: 0 auto;
316
- padding: 2rem;
317
- }
318
-
319
- .dashboard-header {
320
- display: flex;
321
- justify-content: space-between;
322
- align-items: center;
323
- margin-bottom: 2rem;
324
- padding: 1.5rem;
325
- background: white;
326
- border-radius: 12px;
327
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
328
- }
329
-
330
- .ticker-info h1 {
331
- font-size: 2.5rem;
332
- font-weight: 700;
333
- color: var(--text-primary);
334
- margin-bottom: 0.5rem;
335
- }
336
-
337
- .price-info {
338
- display: flex;
339
- gap: 1rem;
340
- align-items: center;
341
- }
342
-
343
- .current-price {
344
- font-size: 2rem;
345
- font-weight: 600;
346
- }
347
-
348
- .price-change {
349
- font-size: 1.25rem;
350
- font-weight: 600;
351
- padding: 0.25rem 0.75rem;
352
- border-radius: 6px;
353
- }
354
-
355
- .price-change.positive {
356
- color: var(--positive-color);
357
- background: rgba(38, 166, 154, 0.1);
358
- }
359
-
360
- .price-change.negative {
361
- color: var(--negative-color);
362
- background: rgba(239, 83, 80, 0.1);
363
- }
364
-
365
- .header-actions {
366
- display: flex;
367
- gap: 1rem;
368
- }
369
-
370
- /* Metrics Grid */
371
- .metrics-grid {
372
- display: grid;
373
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
374
- gap: 1.5rem;
375
- margin-bottom: 2rem;
376
- }
377
-
378
- .metric-card {
379
- background: white;
380
- padding: 1.5rem;
381
- border-radius: 12px;
382
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
383
- border-left: 4px solid var(--primary-color);
384
- }
385
-
386
- .metric-label {
387
- font-size: 0.875rem;
388
- color: var(--text-secondary);
389
- margin-bottom: 0.5rem;
390
- text-transform: uppercase;
391
- letter-spacing: 0.5px;
392
- }
393
-
394
- .metric-value {
395
- font-size: 1.75rem;
396
- font-weight: 700;
397
- color: var(--text-primary);
398
- }
399
-
400
- .metric-value.signal-buy {
401
- color: var(--success-color);
402
- }
403
-
404
- .metric-value.signal-sell {
405
- color: var(--danger-color);
406
- }
407
-
408
- .metric-value.signal-neutral {
409
- color: var(--warning-color);
410
- }
411
-
412
- .metric-signal {
413
- font-size: 0.875rem;
414
- margin-top: 0.5rem;
415
- padding: 0.25rem 0.5rem;
416
- border-radius: 4px;
417
- display: inline-block;
418
- }
419
-
420
- .metric-signal.signal-buy {
421
- background: rgba(76, 175, 80, 0.1);
422
- color: var(--success-color);
423
- }
424
-
425
- .metric-signal.signal-sell {
426
- background: rgba(244, 67, 54, 0.1);
427
- color: var(--danger-color);
428
- }
429
-
430
- .metric-signal.signal-neutral {
431
- background: rgba(255, 152, 0, 0.1);
432
- color: var(--warning-color);
433
- }
434
-
435
- /* Charts */
436
- .chart-container {
437
- background: white;
438
- padding: 1.5rem;
439
- border-radius: 12px;
440
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
441
- margin-bottom: 2rem;
442
- }
443
-
444
- .chart-header {
445
- display: flex;
446
- justify-content: space-between;
447
- align-items: center;
448
- margin-bottom: 1rem;
449
- }
450
-
451
- .chart-header h2,
452
- .chart-header h3 {
453
- display: flex;
454
- align-items: center;
455
- gap: 0.5rem;
456
- color: var(--text-primary);
457
- }
458
-
459
- .chart-header i {
460
- color: var(--primary-color);
461
- }
462
-
463
- .chart-controls {
464
- display: flex;
465
- gap: 0.5rem;
466
- }
467
-
468
- .chart {
469
- min-height: 300px;
470
- }
471
-
472
- .chart-small {
473
- min-height: 200px;
474
- }
475
-
476
- .indicators-grid {
477
- display: grid;
478
- grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
479
- gap: 1.5rem;
480
- margin-bottom: 2rem;
481
- }
482
-
483
- /* Strategy Results */
484
- .strategy-results {
485
- padding: 1rem 0;
486
- }
487
-
488
- .results-table {
489
- width: 100%;
490
- border-collapse: collapse;
491
- margin-top: 1rem;
492
- }
493
-
494
- .results-table thead {
495
- background: var(--light-bg);
496
- }
497
-
498
- .results-table th,
499
- .results-table td {
500
- padding: 1rem;
501
- text-align: left;
502
- border-bottom: 1px solid var(--border-color);
503
- }
504
-
505
- .results-table th {
506
- font-weight: 600;
507
- color: var(--text-secondary);
508
- text-transform: uppercase;
509
- font-size: 0.875rem;
510
- letter-spacing: 0.5px;
511
- }
512
-
513
- .results-table td {
514
- font-weight: 500;
515
- }
516
-
517
- .results-table tr:hover {
518
- background: var(--light-bg);
519
- }
520
-
521
- .results-table .positive {
522
- color: var(--positive-color);
523
- font-weight: 600;
524
- }
525
-
526
- .results-table .negative {
527
- color: var(--negative-color);
528
- font-weight: 600;
529
- }
530
-
531
- .hint {
532
- color: var(--text-secondary);
533
- font-style: italic;
534
- padding: 2rem;
535
- text-align: center;
536
- }
537
-
538
- .loading {
539
- text-align: center;
540
- padding: 2rem;
541
- color: var(--primary-color);
542
- font-size: 1.1rem;
543
- }
544
-
545
- .error {
546
- text-align: center;
547
- padding: 2rem;
548
- color: var(--danger-color);
549
- }
550
-
551
- /* Modal */
552
- .modal {
553
- display: none;
554
- position: fixed;
555
- z-index: 2000;
556
- left: 0;
557
- top: 0;
558
- width: 100%;
559
- height: 100%;
560
- background-color: rgba(0, 0, 0, 0.5);
561
- backdrop-filter: blur(5px);
562
- }
563
-
564
- .modal-content {
565
- background-color: white;
566
- margin: 15% auto;
567
- padding: 2rem;
568
- border-radius: 12px;
569
- width: 90%;
570
- max-width: 500px;
571
- box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
572
- }
573
-
574
- .modal-content h2 {
575
- margin-bottom: 1.5rem;
576
- color: var(--text-primary);
577
- }
578
-
579
- .modal-content input {
580
- width: 100%;
581
- padding: 1rem;
582
- border: 2px solid var(--border-color);
583
- border-radius: 8px;
584
- font-size: 1.1rem;
585
- margin-bottom: 1rem;
586
- outline: none;
587
- }
588
-
589
- .modal-content input:focus {
590
- border-color: var(--primary-color);
591
- }
592
-
593
- .close {
594
- color: var(--text-secondary);
595
- float: right;
596
- font-size: 2rem;
597
- font-weight: bold;
598
- cursor: pointer;
599
- transition: color 0.3s ease;
600
- }
601
-
602
- .close:hover {
603
- color: var(--text-primary);
604
- }
605
-
606
- /* Footer */
607
- .footer {
608
- background: var(--dark-bg);
609
- color: white;
610
- padding: 2rem 0;
611
- margin-top: 4rem;
612
- }
613
-
614
- .footer-content {
615
- max-width: 1200px;
616
- margin: 0 auto;
617
- padding: 0 2rem;
618
- text-align: center;
619
- }
620
-
621
- .footer-content p {
622
- margin-bottom: 0.5rem;
623
- opacity: 0.8;
624
- }
625
-
626
- .disclaimer {
627
- color: var(--warning-color);
628
- font-weight: 600;
629
- }
630
-
631
- /* Responsive */
632
- @media (max-width: 768px) {
633
- .hero-content h1 {
634
- font-size: 2rem;
635
- }
636
-
637
- .search-form {
638
- flex-direction: column;
639
- }
640
-
641
- .dashboard-header {
642
- flex-direction: column;
643
- align-items: flex-start;
644
- gap: 1rem;
645
- }
646
-
647
- .metrics-grid {
648
- grid-template-columns: 1fr;
649
- }
650
-
651
- .indicators-grid {
652
- grid-template-columns: 1fr;
653
- }
654
-
655
- .nav-menu {
656
- gap: 1rem;
657
- font-size: 0.875rem;
658
- }
659
- }