calcsuite-react 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/CHANGELOG.md +25 -0
  2. package/LICENSE +26 -0
  3. package/README.md +87 -0
  4. package/dist/calcsuite.js +6699 -0
  5. package/dist/core/calculators/deposits.d.ts +2 -0
  6. package/dist/core/calculators/investments.d.ts +2 -0
  7. package/dist/core/calculators/loans.d.ts +2 -0
  8. package/dist/core/calculators/retirement_us.d.ts +2 -0
  9. package/dist/core/calculators/returns.d.ts +2 -0
  10. package/dist/core/calculators/scientific.d.ts +2 -0
  11. package/dist/core/calculators/tax.d.ts +2 -0
  12. package/dist/core/calculators/utility.d.ts +2 -0
  13. package/dist/core/currency.d.ts +20 -0
  14. package/dist/core/decimal.d.ts +9 -0
  15. package/dist/core/finance.d.ts +30 -0
  16. package/dist/core/format.d.ts +16 -0
  17. package/dist/core/kit.d.ts +109 -0
  18. package/dist/core/liveRates.d.ts +11 -0
  19. package/dist/core/loan.d.ts +70 -0
  20. package/dist/core/registry.d.ts +10 -0
  21. package/dist/core/sci/evaluator.d.ts +14 -0
  22. package/dist/core/sci/index.d.ts +22 -0
  23. package/dist/core/sci/parser.d.ts +32 -0
  24. package/dist/core/sci/tokenizer.d.ts +10 -0
  25. package/dist/export/index.d.ts +56 -0
  26. package/dist/index.d.ts +49 -0
  27. package/dist/settings/SettingsContext.d.ts +20 -0
  28. package/dist/settings/settings.d.ts +72 -0
  29. package/dist/transport/client.d.ts +54 -0
  30. package/dist/transport/fx.d.ts +23 -0
  31. package/dist/transport/payload.d.ts +21 -0
  32. package/dist/transport/types.d.ts +198 -0
  33. package/dist/ui/CalculatorPanel.d.ts +14 -0
  34. package/dist/ui/Chart.d.ts +4 -0
  35. package/dist/ui/CommandPalette.d.ts +13 -0
  36. package/dist/ui/CurrencyConverter.d.ts +1 -0
  37. package/dist/ui/Dialog.d.ts +8 -0
  38. package/dist/ui/ExportMenu.d.ts +4 -0
  39. package/dist/ui/HistoryPanel.d.ts +6 -0
  40. package/dist/ui/IntegrationPanel.d.ts +8 -0
  41. package/dist/ui/Launcher.d.ts +34 -0
  42. package/dist/ui/LoanEmiPanel.d.ts +1 -0
  43. package/dist/ui/ResultCard.d.ts +4 -0
  44. package/dist/ui/SaveButton.d.ts +4 -0
  45. package/dist/ui/ScheduleTable.d.ts +4 -0
  46. package/dist/ui/SciCalculator.d.ts +4 -0
  47. package/dist/ui/SettingsPanel.d.ts +1 -0
  48. package/dist/ui/Shell.d.ts +5 -0
  49. package/dist/ui/fields.d.ts +9 -0
  50. package/dist/ui/history.d.ts +15 -0
  51. package/dist/ui/icons.d.ts +8 -0
  52. package/dist/ui/overlayStack.d.ts +3 -0
  53. package/dist/ui/themePresets.d.ts +19 -0
  54. package/package.json +94 -0
  55. package/src/theme.css +751 -0
package/src/theme.css ADDED
@@ -0,0 +1,751 @@
1
+ /* ─────────────── Design tokens (§11.3 / §11.4) ─────────────── */
2
+ [data-fincalc-root] {
3
+ --fc-ink: #141a1f;
4
+ --fc-paper: #fbfcfd;
5
+ --fc-surface: #ffffff;
6
+ --fc-slate: #5a6672;
7
+ --fc-line: #e3e8ec;
8
+ --fc-accent: #078dee; /* Cyan default (overridden live by settings.ui.accent) */
9
+ /* derived from the accent so colour presets propagate everywhere */
10
+ --fc-accent-weak: color-mix(in srgb, var(--fc-accent) 12%, #ffffff);
11
+ --fc-principal: #2c7a6b;
12
+ --fc-interest: #b5762a;
13
+
14
+ --fc-radius-field: 10px;
15
+ --fc-radius-surface: 16px;
16
+ --fc-font-ui: 'Inter Tight', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
17
+ --fc-space: 8px;
18
+ --fc-field-height: 44px;
19
+ --fc-shadow: 0 1px 2px rgba(20, 26, 31, 0.04), 0 8px 24px rgba(20, 26, 31, 0.06);
20
+ }
21
+
22
+ /* Dark mode is a separate ramp, not an inversion (§11.3). */
23
+ [data-fincalc-root][data-theme='dark'] {
24
+ --fc-ink: #eef2f5;
25
+ --fc-paper: #0e1418;
26
+ --fc-surface: #161e24;
27
+ --fc-slate: #94a2af;
28
+ --fc-line: #26313a;
29
+ --fc-accent: #5aa2e0;
30
+ --fc-accent-weak: color-mix(in srgb, var(--fc-accent) 22%, #0e1418);
31
+ --fc-principal: #4bab98;
32
+ --fc-interest: #d99a4e;
33
+ --fc-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 32px rgba(0, 0, 0, 0.35);
34
+ }
35
+
36
+ * { box-sizing: border-box; }
37
+
38
+ body {
39
+ margin: 0;
40
+ font-family: var(--fc-font-ui);
41
+ background: var(--fc-paper);
42
+ color: var(--fc-ink);
43
+ -webkit-font-smoothing: antialiased;
44
+ }
45
+
46
+ /* tabular figures, mandatory on every amount (§11.3) */
47
+ .num { font-variant-numeric: tabular-nums; }
48
+
49
+ /* Keyboard focus ring — always the primary accent (§11.7) */
50
+ :where(button, a, select, input, [role='option'], [tabindex]):focus-visible {
51
+ outline: 2px solid var(--fc-accent);
52
+ outline-offset: 2px;
53
+ border-radius: 6px;
54
+ }
55
+ .input-wrap:focus-within { outline: 2px solid var(--fc-accent); outline-offset: 1px; }
56
+
57
+ /* Accent scrollbars */
58
+ * { scrollbar-color: color-mix(in srgb, var(--fc-accent) 60%, var(--fc-slate)) transparent; scrollbar-width: thin; }
59
+ ::-webkit-scrollbar { width: 11px; height: 11px; }
60
+ ::-webkit-scrollbar-track { background: transparent; }
61
+ ::-webkit-scrollbar-thumb {
62
+ background: color-mix(in srgb, var(--fc-accent) 55%, var(--fc-slate));
63
+ border-radius: 8px;
64
+ border: 3px solid transparent;
65
+ background-clip: padding-box;
66
+ }
67
+ ::-webkit-scrollbar-thumb:hover { background: var(--fc-accent); background-clip: padding-box; }
68
+
69
+ [data-fincalc-root][data-density='compact'] { --fc-field-height: 36px; }
70
+
71
+ .page-inline {
72
+ min-height: 100vh;
73
+ }
74
+
75
+ .shell {
76
+ max-width: 860px;
77
+ margin: 0 auto;
78
+ }
79
+
80
+ /* ─────────────── Header ─────────────── */
81
+ .app-head {
82
+ display: flex;
83
+ align-items: center;
84
+ justify-content: space-between;
85
+ margin-bottom: calc(var(--fc-space) * 3);
86
+ }
87
+ .brand { display: flex; align-items: baseline; gap: 10px; }
88
+ .brand h1 { font-size: 20px; font-weight: 700; margin: 0; letter-spacing: -0.01em; }
89
+ .brand small { color: var(--fc-slate); font-size: 13px; }
90
+ .head-actions { display: flex; gap: var(--fc-space); align-items: center; }
91
+
92
+ /* ─────────────── Cards & fields ─────────────── */
93
+ .card {
94
+ background: var(--fc-surface);
95
+ border: 1px solid var(--fc-line);
96
+ border-radius: var(--fc-radius-surface);
97
+ padding: calc(var(--fc-space) * 3);
98
+ box-shadow: var(--fc-shadow);
99
+ }
100
+ .card + .card { margin-top: calc(var(--fc-space) * 2); }
101
+
102
+ .grid { display: grid; gap: calc(var(--fc-space) * 2); }
103
+ @media (min-width: 640px) { .grid.two { grid-template-columns: 1fr 1fr; } }
104
+
105
+ label.field { display: block; }
106
+ .field > span.lbl {
107
+ display: block;
108
+ font-size: 13px;
109
+ color: var(--fc-slate);
110
+ margin-bottom: 6px;
111
+ }
112
+ .input-wrap {
113
+ display: flex;
114
+ align-items: center;
115
+ height: var(--fc-field-height);
116
+ border: 1px solid var(--fc-line);
117
+ border-radius: var(--fc-radius-field);
118
+ background: var(--fc-paper);
119
+ padding: 0 12px;
120
+ gap: 6px;
121
+ transition: border-color 0.12s, box-shadow 0.12s;
122
+ }
123
+ .input-wrap:focus-within {
124
+ border-color: var(--fc-accent);
125
+ box-shadow: 0 0 0 3px var(--fc-accent-weak);
126
+ }
127
+ .input-wrap .affix { color: var(--fc-slate); font-size: 14px; white-space: nowrap; }
128
+ .input-wrap input {
129
+ border: 0;
130
+ outline: 0;
131
+ background: transparent;
132
+ color: var(--fc-ink);
133
+ font: inherit;
134
+ font-variant-numeric: tabular-nums;
135
+ width: 100%;
136
+ min-width: 0;
137
+ text-align: right;
138
+ }
139
+ /* amount / numeric values in the accent colour */
140
+ .input-wrap input.num { color: var(--fc-accent); font-weight: 500; }
141
+ .input-wrap.solved {
142
+ border-style: dashed;
143
+ border-color: var(--fc-accent);
144
+ background: var(--fc-accent-weak);
145
+ }
146
+ .solved-note { font-size: 12px; color: var(--fc-accent); margin-top: 4px; }
147
+ .tenure-pair { display: flex; gap: var(--fc-space); }
148
+ .tenure-pair > div { flex: 1; }
149
+
150
+ /* ─────────────── Segmented / chips ─────────────── */
151
+ .seg {
152
+ display: inline-flex;
153
+ border: 1px solid var(--fc-line);
154
+ border-radius: 999px;
155
+ padding: 3px;
156
+ background: var(--fc-paper);
157
+ gap: 2px;
158
+ }
159
+ .seg button {
160
+ border: 0;
161
+ background: transparent;
162
+ color: var(--fc-slate);
163
+ font: inherit;
164
+ font-size: 13px;
165
+ padding: 6px 14px;
166
+ border-radius: 999px;
167
+ cursor: pointer;
168
+ min-height: 32px;
169
+ }
170
+ .seg button[aria-pressed='true'] {
171
+ background: var(--fc-accent);
172
+ color: #fff;
173
+ font-weight: 600;
174
+ }
175
+ [data-theme='dark'] .seg button[aria-pressed='true'] { color: #06121c; }
176
+
177
+ .solve-row { margin-bottom: calc(var(--fc-space) * 2.5); }
178
+ .solve-row .lbl { font-size: 13px; color: var(--fc-slate); margin-bottom: 8px; display: block; }
179
+
180
+ .icon-btn {
181
+ height: 36px;
182
+ min-width: 36px;
183
+ padding: 0 12px;
184
+ border: 1px solid var(--fc-line);
185
+ background: var(--fc-surface);
186
+ color: var(--fc-ink);
187
+ border-radius: 999px;
188
+ cursor: pointer;
189
+ font: inherit;
190
+ font-size: 13px;
191
+ display: inline-flex;
192
+ align-items: center;
193
+ justify-content: center;
194
+ gap: 6px;
195
+ line-height: 1;
196
+ }
197
+ .icon-btn svg { display: block; flex: 0 0 auto; }
198
+ .icon-btn.icon-only { width: 36px; min-width: 36px; padding: 0; }
199
+ .icon-btn:hover { border-color: var(--fc-accent); }
200
+ .icon-btn.saved { border-color: var(--fc-principal); color: var(--fc-principal); font-weight: 600; }
201
+
202
+ /* ─────────────── Result card (the hero, §11.2) ─────────────── */
203
+ .result { text-align: left; }
204
+ .result .label { color: var(--fc-slate); font-size: 14px; }
205
+ .result .value {
206
+ font-size: clamp(40px, 9vw, 64px);
207
+ font-weight: 700;
208
+ letter-spacing: -0.02em;
209
+ line-height: 1.02;
210
+ font-variant-numeric: tabular-nums;
211
+ margin: 4px 0 2px;
212
+ }
213
+ .result .value.value-text {
214
+ font-size: clamp(22px, 3.4vw, 34px);
215
+ line-height: 1.2;
216
+ letter-spacing: -0.01em;
217
+ }
218
+ .result .value .cur { font-size: 0.55em; font-weight: 600; color: var(--fc-slate); margin-right: 0.15em; }
219
+ .result .per { color: var(--fc-slate); font-size: 15px; }
220
+
221
+ .secondary {
222
+ display: flex;
223
+ flex-wrap: wrap;
224
+ gap: calc(var(--fc-space) * 3);
225
+ margin-top: calc(var(--fc-space) * 2);
226
+ padding-top: calc(var(--fc-space) * 2);
227
+ border-top: 1px solid var(--fc-line);
228
+ }
229
+ .secondary .kv .k { font-size: 12px; color: var(--fc-slate); }
230
+ .secondary .kv .v { font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums; }
231
+ .dot { display: inline-block; width: 9px; height: 9px; border-radius: 3px; margin-right: 6px; }
232
+ .dot.pr { background: var(--fc-principal); }
233
+ .dot.in { background: var(--fc-interest); }
234
+
235
+ /* split bar (§11.3 semantic colours, pattern as non-colour signal §11.7) */
236
+ .splitbar {
237
+ display: flex;
238
+ height: 14px;
239
+ border-radius: 7px;
240
+ overflow: hidden;
241
+ margin-top: calc(var(--fc-space) * 2);
242
+ border: 1px solid var(--fc-line);
243
+ }
244
+ .splitbar .pr { background: var(--fc-principal); }
245
+ .splitbar .in {
246
+ background: var(--fc-interest);
247
+ background-image: repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.22) 0 4px, transparent 4px 8px);
248
+ }
249
+ .split-legend { display: flex; gap: 20px; margin-top: 8px; font-size: 13px; color: var(--fc-slate); }
250
+
251
+ /* ─────────────── Tabs ─────────────── */
252
+ .tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--fc-line); margin-bottom: calc(var(--fc-space) * 2); }
253
+ .tabs button {
254
+ border: 0;
255
+ background: transparent;
256
+ font: inherit;
257
+ font-size: 14px;
258
+ color: var(--fc-slate);
259
+ padding: 10px 14px;
260
+ cursor: pointer;
261
+ border-bottom: 2px solid transparent;
262
+ margin-bottom: -1px;
263
+ }
264
+ .tabs button[aria-selected='true'] { color: var(--fc-ink); border-bottom-color: var(--fc-accent); font-weight: 600; }
265
+
266
+ /* ─────────────── Amortisation table (§11.7 real <table>) ─────────────── */
267
+ .sched-wrap { max-height: 520px; overflow: auto; border: 1px solid var(--fc-line); border-radius: var(--fc-radius-field); }
268
+ table.sched { width: 100%; border-collapse: collapse; font-size: 13px; }
269
+ table.sched caption { text-align: left; padding: 10px 12px; color: var(--fc-slate); font-size: 12px; }
270
+ table.sched th, table.sched td { padding: 9px 12px; text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
271
+ table.sched th:first-child, table.sched td:first-child { text-align: left; }
272
+ table.sched thead th {
273
+ position: sticky;
274
+ top: 0;
275
+ background: var(--fc-surface);
276
+ color: var(--fc-slate);
277
+ font-weight: 600;
278
+ font-size: 12px;
279
+ border-bottom: 1px solid var(--fc-line);
280
+ z-index: 1;
281
+ }
282
+ tr.year-row { cursor: pointer; }
283
+ tr.year-row:hover { background: var(--fc-accent-weak); }
284
+ tr.year-row td { border-top: 1px solid var(--fc-line); font-weight: 600; }
285
+ tr.year-row .caret { color: var(--fc-slate); display: inline-block; width: 14px; }
286
+ tr.month-row td { color: var(--fc-slate); border-top: 1px dashed var(--fc-line); }
287
+ td .pr-txt { color: var(--fc-principal); }
288
+ td .in-txt { color: var(--fc-interest); }
289
+
290
+ /* ─────────────── Chart ─────────────── */
291
+ .chart svg { width: 100%; height: auto; display: block; }
292
+ .chart-legend { display: flex; gap: 20px; font-size: 13px; color: var(--fc-slate); margin-top: 8px; }
293
+
294
+ /* ─────────────── Formula / misc ─────────────── */
295
+ .formula code {
296
+ display: block;
297
+ background: var(--fc-paper);
298
+ border: 1px solid var(--fc-line);
299
+ border-radius: var(--fc-radius-field);
300
+ padding: 14px 16px;
301
+ font-family: var(--fc-font-ui);
302
+ font-size: 15px;
303
+ color: var(--fc-ink);
304
+ }
305
+ .assumptions { margin-top: 12px; color: var(--fc-slate); font-size: 13px; line-height: 1.6; }
306
+
307
+ .disclaimer {
308
+ margin-top: calc(var(--fc-space) * 2);
309
+ font-size: 12px;
310
+ color: var(--fc-slate);
311
+ line-height: 1.5;
312
+ }
313
+ .error {
314
+ color: #b3261e;
315
+ background: #fdecea;
316
+ border: 1px solid #f5c4bf;
317
+ padding: 12px 14px;
318
+ border-radius: var(--fc-radius-field);
319
+ font-size: 14px;
320
+ }
321
+ [data-theme='dark'] .error { background: #2a1512; border-color: #5a241d; color: #f2a99f; }
322
+
323
+ .notice {
324
+ margin-top: 8px;
325
+ font-size: 13px;
326
+ color: var(--fc-interest);
327
+ background: color-mix(in srgb, var(--fc-interest) 10%, transparent);
328
+ border-radius: var(--fc-radius-field);
329
+ padding: 10px 12px;
330
+ }
331
+
332
+ /* ─────────────── Generic panel additions ─────────────── */
333
+ .panel-head { margin-bottom: calc(var(--fc-space) * 2); }
334
+ .panel-head h2 { font-size: 20px; font-weight: 700; margin: 0 0 2px; letter-spacing: -0.01em; }
335
+ .panel-head p { margin: 0; color: var(--fc-slate); font-size: 14px; }
336
+
337
+ .span2 { grid-column: 1 / -1; }
338
+
339
+ .input-wrap select {
340
+ border: 0;
341
+ outline: 0;
342
+ background: transparent;
343
+ color: var(--fc-ink);
344
+ font: inherit;
345
+ width: 100%;
346
+ cursor: pointer;
347
+ }
348
+ .input-wrap input[type='date'] { color: var(--fc-ink); }
349
+
350
+ .help { color: var(--fc-slate); cursor: help; font-size: 12px; }
351
+
352
+ .accent-txt { color: var(--fc-accent); }
353
+ .pos-txt { color: var(--fc-principal); }
354
+ .neg-txt { color: #b3261e; }
355
+ [data-theme='dark'] .neg-txt { color: #f2a99f; }
356
+ .dot.ac { background: var(--fc-accent); }
357
+ .splitbar .accent { background: var(--fc-accent); }
358
+
359
+ /* toggle switch */
360
+ .toggle-field { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
361
+ .toggle {
362
+ width: 44px;
363
+ height: 26px;
364
+ border-radius: 999px;
365
+ border: 1px solid var(--fc-line);
366
+ background: var(--fc-line);
367
+ position: relative;
368
+ cursor: pointer;
369
+ flex: 0 0 auto;
370
+ transition: background 0.15s;
371
+ }
372
+ .toggle.on { background: var(--fc-accent); border-color: var(--fc-accent); }
373
+ .toggle .knob {
374
+ position: absolute;
375
+ top: 2px;
376
+ left: 2px;
377
+ width: 20px;
378
+ height: 20px;
379
+ border-radius: 50%;
380
+ background: #fff;
381
+ transition: left 0.15s;
382
+ }
383
+ .toggle.on .knob { left: 20px; }
384
+
385
+ /* accordion */
386
+ .accordion { border-top: 1px solid var(--fc-line); margin-top: calc(var(--fc-space) * 2); padding-top: calc(var(--fc-space) * 1.5); }
387
+ .accordion-head {
388
+ border: 0;
389
+ background: transparent;
390
+ font: inherit;
391
+ font-size: 14px;
392
+ font-weight: 600;
393
+ color: var(--fc-ink);
394
+ cursor: pointer;
395
+ padding: 0;
396
+ display: flex;
397
+ align-items: center;
398
+ gap: 6px;
399
+ }
400
+ .accordion-head .caret { color: var(--fc-slate); }
401
+
402
+ .actions-row { display: flex; flex-wrap: wrap; gap: var(--fc-space); margin-top: calc(var(--fc-space) * 2); }
403
+ .cashflows .lbl { margin-bottom: 8px; }
404
+
405
+ /* ─────────────── Shell chrome ─────────────── */
406
+ .fc-shell { display: flex; flex-direction: column; min-height: 100vh; background: var(--fc-paper); }
407
+ .fc-topbar {
408
+ display: flex; align-items: center; justify-content: space-between;
409
+ padding: 12px 20px; border-bottom: 1px solid var(--fc-line);
410
+ position: sticky; top: 0; background: var(--fc-paper); z-index: 5;
411
+ }
412
+ .fc-topbar .brand strong { font-size: 18px; letter-spacing: -0.01em; }
413
+ .fc-brand { flex-direction: column; align-items: flex-start; gap: 0; }
414
+ .fc-brand strong { line-height: 1.15; }
415
+ .fc-brand-sub { font-size: 11px; color: var(--fc-accent); text-decoration: none; font-weight: 500; line-height: 1.15; }
416
+ .fc-brand-sub:hover { text-decoration: underline; }
417
+ .fc-top-actions { display: flex; align-items: center; gap: 8px; }
418
+
419
+ .fc-body { display: flex; flex: 1; min-height: 0; }
420
+ .fc-rail {
421
+ width: 210px; flex: 0 0 210px; border-right: 1px solid var(--fc-line);
422
+ padding: 16px 10px; overflow-y: auto;
423
+ }
424
+ .rail-group { margin-bottom: 16px; }
425
+ .rail-group-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--fc-slate); padding: 0 10px 6px; }
426
+ .rail-item {
427
+ display: block; width: 100%; text-align: left; border: 0; background: transparent;
428
+ font: inherit; font-size: 13px; line-height: 1.3; color: var(--fc-ink); padding: 7px 10px;
429
+ border-radius: 8px; cursor: pointer;
430
+ }
431
+ .rail-item:hover { background: var(--fc-accent-weak); }
432
+ .rail-item.active { background: var(--fc-accent); color: #fff; font-weight: 600; }
433
+ [data-theme='dark'] .rail-item.active { color: #06121c; }
434
+
435
+ .fc-main { flex: 1; min-width: 0; padding: 24px; overflow-y: auto; max-width: 900px; margin: 0 auto; width: 100%; }
436
+
437
+ .fc-footer {
438
+ flex: 0 0 auto;
439
+ padding: 10px 20px;
440
+ border-top: 1px solid var(--fc-line);
441
+ background: var(--fc-paper);
442
+ color: var(--fc-slate);
443
+ font-size: 12px;
444
+ text-align: center;
445
+ line-height: 1.5;
446
+ }
447
+ .fc-footer strong { color: var(--fc-ink); font-weight: 600; }
448
+ .fc-footer-sep { margin: 0 6px; opacity: 0.5; }
449
+ .fc-footer-link { color: var(--fc-accent); text-decoration: none; font-weight: 500; transition: opacity 0.12s ease; }
450
+ .fc-footer-link:hover { text-decoration: underline; opacity: 0.85; }
451
+ @media (max-width: 720px) { .fc-footer { font-size: 11px; } }
452
+
453
+ @media (max-width: 720px) {
454
+ .fc-body { flex-direction: column; }
455
+ .fc-rail { width: 100%; flex: none; display: flex; gap: 6px; overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--fc-line); }
456
+ .rail-group { display: flex; gap: 6px; margin: 0; }
457
+ .rail-group-label { display: none; }
458
+ .rail-item { white-space: nowrap; }
459
+ .fc-main { padding: 16px; }
460
+ }
461
+
462
+ /* FAB */
463
+ .fc-fab {
464
+ position: fixed; z-index: 40; width: 56px; height: 56px; border-radius: 50%;
465
+ border: 0; background: var(--fc-accent); color: #fff; cursor: pointer;
466
+ box-shadow: var(--fc-shadow);
467
+ display: flex; align-items: center; justify-content: center;
468
+ transition: transform 0.12s ease, box-shadow 0.12s ease;
469
+ }
470
+ .fc-fab:hover { transform: translateY(-1px); }
471
+ .fc-fab:active { transform: translateY(0); }
472
+ .fc-fab.pos-bottom-right { right: 24px; bottom: 24px; }
473
+ .fc-fab.pos-bottom-left { left: 24px; bottom: 24px; }
474
+ .fc-fab.pos-top-right { right: 24px; top: 24px; }
475
+ .fc-fab.pos-top-left { left: 24px; top: 24px; }
476
+
477
+ /* Dialog */
478
+ .dialog-backdrop {
479
+ position: fixed; inset: 0; background: rgba(10, 16, 20, 0.5);
480
+ display: flex; align-items: center; justify-content: center; z-index: 50; padding: 20px;
481
+ animation: fc-fade 0.12s ease;
482
+ }
483
+ .dialog {
484
+ background: var(--fc-paper); border-radius: var(--fc-radius-surface); overflow: hidden;
485
+ box-shadow: var(--fc-shadow); width: 100%; max-height: 92vh; display: flex; flex-direction: column;
486
+ animation: fc-scale 0.34s cubic-bezier(0.34, 1.56, 0.64, 1);
487
+ }
488
+ .dialog.surface-full { max-width: 1100px; height: 92vh; }
489
+ .dialog.surface-xl { max-width: 960px; }
490
+ .dialog.surface-lg { max-width: 720px; }
491
+ .dialog .fc-shell { min-height: 0; height: 100%; }
492
+ @keyframes fc-fade { from { opacity: 0; } }
493
+ @keyframes fc-scale { from { transform: scale(0.98); opacity: 0; } }
494
+ @keyframes fc-fade-out { to { opacity: 0; } }
495
+ @keyframes fc-scale-out { to { transform: scale(0.96); opacity: 0; } }
496
+ .dialog-backdrop.closing { animation: fc-fade-out 0.2s ease forwards; }
497
+ .dialog.closing { animation: fc-scale-out 0.2s ease forwards; }
498
+ @media (max-width: 720px) {
499
+ .dialog-backdrop { padding: 0; align-items: flex-end; }
500
+ .dialog.surface-full { height: 96vh; border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
501
+ }
502
+
503
+ /* Command palette */
504
+ .palette-backdrop { position: fixed; inset: 0; background: rgba(10, 16, 20, 0.4); z-index: 60; display: flex; justify-content: center; align-items: flex-start; padding-top: 12vh; }
505
+ .palette { background: var(--fc-surface); border: 1px solid var(--fc-line); border-radius: 14px; width: min(560px, 92vw); box-shadow: var(--fc-shadow); overflow: hidden; }
506
+ .palette-input { width: 100%; border: 0; border-bottom: 1px solid var(--fc-line); outline: 0; background: transparent; color: var(--fc-ink); font: inherit; font-size: 16px; padding: 16px; }
507
+ .palette-list { list-style: none; margin: 0; padding: 6px; max-height: 50vh; overflow-y: auto; }
508
+ .palette-item { display: flex; justify-content: space-between; align-items: center; padding: 10px 12px; border-radius: 8px; cursor: pointer; }
509
+ .palette-item.active { background: var(--fc-accent-weak); }
510
+ .palette-label { font-size: 14px; }
511
+ .palette-group { font-size: 11px; color: var(--fc-slate); text-transform: uppercase; letter-spacing: 0.05em; }
512
+ .palette-empty { padding: 16px; color: var(--fc-slate); text-align: center; }
513
+
514
+ /* Settings */
515
+ .settings-section h3 { font-size: 15px; margin: 0 0 12px; }
516
+ .settings-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 0; border-top: 1px solid var(--fc-line); }
517
+ .settings-row:first-of-type { border-top: 0; }
518
+ .settings-row .lbl { margin: 0; font-size: 14px; color: var(--fc-ink); }
519
+ .mini-wrap { height: 36px; min-width: 150px; }
520
+ .mini { height: 36px; border: 1px solid var(--fc-line); border-radius: 8px; background: var(--fc-paper); padding: 0 10px; color: var(--fc-ink); font: inherit; width: 90px; text-align: center; }
521
+
522
+ /* History */
523
+ .history-list { list-style: none; margin: 0; padding: 0; }
524
+ .history-list li { border-top: 1px solid var(--fc-line); }
525
+ .history-list li:first-child { border-top: 0; }
526
+ .history-item { display: flex; width: 100%; justify-content: space-between; align-items: center; gap: 12px; border: 0; background: transparent; font: inherit; text-align: left; padding: 14px 16px; cursor: pointer; color: var(--fc-ink); }
527
+ .history-item:hover { background: var(--fc-accent-weak); }
528
+ .history-title { font-size: 14px; font-weight: 500; }
529
+ .history-when { font-size: 12px; color: var(--fc-slate); }
530
+ .history-value { font-weight: 600; }
531
+ .empty { text-align: center; color: var(--fc-slate); }
532
+
533
+ /* Export menu */
534
+ .menu { position: relative; display: inline-block; }
535
+ .menu-list { position: absolute; z-index: 30; background: var(--fc-surface); border: 1px solid var(--fc-line); border-radius: 10px; box-shadow: var(--fc-shadow); padding: 6px; min-width: 180px; margin-top: 4px; list-style: none; }
536
+ .menu-list button { display: block; width: 100%; text-align: left; border: 0; background: transparent; font: inherit; font-size: 11px; padding: 5px 10px; border-radius: 6px; cursor: pointer; color: var(--fc-ink); }
537
+ .menu-list button:hover, .menu-list button:focus-visible { background: var(--fc-accent-weak); outline: none; }
538
+
539
+ /* ─────────────── Currency converter ─────────────── */
540
+ .cc-row { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; }
541
+ .cc-amount { flex: 1 1 160px; }
542
+ .cc-cur { flex: 1 1 140px; }
543
+ .cc-swap {
544
+ flex: 0 0 auto; height: var(--fc-field-height); width: 42px; margin-bottom: 0;
545
+ border: 1px solid var(--fc-line); background: var(--fc-surface); color: var(--fc-accent);
546
+ border-radius: 999px; cursor: pointer; font-size: 18px;
547
+ }
548
+ .cc-swap:hover { border-color: var(--fc-accent); background: var(--fc-accent-weak); }
549
+ .cc-rate { display: flex; gap: 8px; align-items: center; }
550
+ .cc-rate .input-wrap { flex: 1; }
551
+ .cc-stale { color: var(--fc-interest) !important; }
552
+ .cc-status { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--fc-slate); margin-top: 8px; }
553
+ .cc-live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fc-principal); display: inline-block; box-shadow: 0 0 0 3px color-mix(in srgb, var(--fc-principal) 25%, transparent); }
554
+ .cc-modebar { display: flex; justify-content: flex-end; margin-bottom: 14px; }
555
+
556
+ /* Accent swatches (settings) */
557
+ .swatches { display: flex; gap: 8px; }
558
+ .swatch { width: 26px; height: 26px; border-radius: 50%; border: 2px solid var(--fc-line); cursor: pointer; padding: 0; }
559
+ .swatch.active { box-shadow: 0 0 0 2px var(--fc-surface), 0 0 0 4px var(--fc-accent); border-color: transparent; }
560
+ .cc-book { list-style: none; margin: 0; padding: 0; }
561
+ .cc-book li { display: flex; align-items: center; gap: 8px; border-top: 1px solid var(--fc-line); }
562
+ .cc-book li:first-child { border-top: 0; }
563
+ .cc-book-item {
564
+ flex: 1; display: flex; align-items: center; justify-content: space-between; gap: 12px;
565
+ border: 0; background: transparent; font: inherit; text-align: left; padding: 12px 4px;
566
+ cursor: pointer; color: var(--fc-ink);
567
+ }
568
+ .cc-book-item:hover { background: var(--fc-accent-weak); }
569
+ .cc-book-pair { font-weight: 600; }
570
+ .cc-book-when { font-size: 12px; color: var(--fc-slate); }
571
+ .cc-forget { flex: 0 0 auto; height: 32px; min-width: 32px; padding: 0 8px; }
572
+ @media (max-width: 720px) { .cc-swap { order: 3; } }
573
+
574
+ /* Export toast (success / friendly-error notice) */
575
+ .export-toast {
576
+ position: fixed;
577
+ bottom: 20px;
578
+ left: 50%;
579
+ transform: translateX(-50%);
580
+ z-index: 50;
581
+ padding: 10px 16px;
582
+ border-radius: 999px;
583
+ font-size: 13px;
584
+ border: 1px solid var(--fc-line);
585
+ background: var(--fc-surface);
586
+ color: var(--fc-ink);
587
+ box-shadow: var(--fc-shadow);
588
+ }
589
+ .export-toast.warn { color: var(--fc-interest); }
590
+ .export-toast.error { color: #b3261e; border-color: #f5c4bf; background: #fdecea; }
591
+ [data-theme='dark'] .export-toast.error { background: #2a1512; border-color: #5a241d; color: #f2a99f; }
592
+
593
+ /* ─────────────── Motion ─────────────── */
594
+ @keyframes fc-enter { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
595
+ @keyframes fc-fade-up { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }
596
+ @keyframes fc-fade { from { opacity: 0; } to { opacity: 1; } }
597
+ @keyframes fc-draw { from { stroke-dashoffset: 1400; } to { stroke-dashoffset: 0; } }
598
+ @keyframes fc-pop { 0% { transform: scale(0.985); opacity: 0.5; } 100% { transform: scale(1); opacity: 1; } }
599
+ @keyframes fc-bar { from { transform: scaleX(0); } to { transform: scaleX(1); } }
600
+
601
+ /* springy easing (overshoots slightly for a bouncy feel) */
602
+ :root { --fc-spring: cubic-bezier(0.34, 1.56, 0.64, 1); }
603
+ /* screen / calculator / menu switch */
604
+ .fc-anim-in { animation: fc-enter 0.42s var(--fc-spring) both; }
605
+ /* tab-panel change */
606
+ .tab-anim { animation: fc-fade-up 0.34s var(--fc-spring) both; }
607
+
608
+ /* segmented control — the selected pill springs in */
609
+ @keyframes fc-seg-pop { from { transform: scale(0.85); } to { transform: scale(1); } }
610
+ .seg button[aria-pressed='true'] { animation: fc-seg-pop 0.34s var(--fc-spring); }
611
+ /* active tab underline springs too */
612
+ .tabs button[aria-selected='true'] { animation: fc-seg-pop 0.3s var(--fc-spring); }
613
+ /* result value settle */
614
+ .result .value { animation: fc-pop 0.28s ease both; }
615
+
616
+ /* chart draw-in (plays on mount) */
617
+ .chart-line { stroke-dasharray: 1400; animation: fc-draw 0.95s ease-out both; }
618
+ .chart-area { animation: fc-fade 0.7s ease both; }
619
+
620
+ /* split bar grows from the left */
621
+ .splitbar > * { transform-origin: left center; animation: fc-bar 0.5s ease-out both; }
622
+
623
+ /* subtle list-item entrance + smooth hovers */
624
+ .palette-item, .rail-item, .menu-list button, .history-item, .cc-book-item { transition: background 0.12s ease, color 0.12s ease; }
625
+ .tabs button, .seg button, .icon-btn, .rail-item { transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease; }
626
+ tr.year-row, tr.month-row { transition: background 0.12s ease; }
627
+
628
+ @media (prefers-reduced-motion: reduce) {
629
+ * { transition: none !important; animation: none !important; }
630
+ }
631
+
632
+ /* ─────────────── Print view (§10) ─────────────── */
633
+ /* Off-screen normally; the print adapter appends .fc-print-root and toggles body.fc-printing. */
634
+ .fc-print-root { display: none; }
635
+
636
+ @media print {
637
+ body.fc-printing > *:not(.fc-print-root) { display: none !important; }
638
+ body.fc-printing .fc-print-root { display: block; }
639
+ .fc-print-root {
640
+ color: #000;
641
+ font-family: var(--fc-font-ui);
642
+ padding: 0;
643
+ }
644
+ .fc-print-root h1 { font-size: 20px; margin: 0 0 12px; }
645
+ .fc-print-root h2 { font-size: 15px; margin: 18px 0 8px; }
646
+ .fc-print-root table { width: 100%; border-collapse: collapse; font-size: 12px; }
647
+ .fc-print-root th,
648
+ .fc-print-root td { border: 1px solid #ccc; padding: 5px 8px; text-align: left; }
649
+ .fc-print-root thead th { background: #f2f2f2; }
650
+ .fc-print-root .fc-print-foot { margin-top: 16px; font-size: 11px; color: #666; }
651
+ @page { margin: 16mm; }
652
+ }
653
+
654
+ /* ─────────────── Scientific calculator (§7) ─────────────── */
655
+ .card.sci { max-width: 520px; margin-left: auto; margin-right: auto; }
656
+ .sci-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--fc-space); margin-bottom: calc(var(--fc-space) * 1.5); }
657
+ .sci-toolbar .seg button { text-transform: uppercase; padding: 6px 10px; font-size: 12px; }
658
+ .sci-mem {
659
+ margin-left: auto; font-weight: 700; font-size: 13px; color: #fff;
660
+ background: var(--fc-accent); border-radius: 999px; padding: 2px 9px;
661
+ }
662
+
663
+ .sci-display {
664
+ border: 1px solid var(--fc-line); border-radius: var(--fc-radius-field);
665
+ background: var(--fc-paper); padding: 10px 12px; margin-bottom: calc(var(--fc-space) * 1.5);
666
+ }
667
+ .sci-expr {
668
+ width: 100%; border: 0; outline: 0; background: transparent; color: var(--fc-ink);
669
+ font: inherit; font-size: 22px; text-align: right; font-variant-numeric: tabular-nums; min-width: 0;
670
+ }
671
+ .sci-out { text-align: right; color: var(--fc-slate); font-size: 16px; min-height: 20px; margin-top: 4px; }
672
+ .sci-err { color: #b3261e; font-size: 13px; }
673
+ [data-theme='dark'] .sci-err { color: #f2a99f; }
674
+
675
+ .sci-pad { display: flex; flex-direction: column; gap: 6px; }
676
+ .sci-row { display: flex; gap: 6px; }
677
+ .sci-key {
678
+ flex: 1 1 0; min-width: 0; min-height: 44px;
679
+ border: 1px solid var(--fc-line); border-radius: var(--fc-radius-field);
680
+ background: var(--fc-surface); color: var(--fc-ink);
681
+ font: inherit; font-size: 15px; cursor: pointer;
682
+ display: flex; align-items: center; justify-content: center;
683
+ transition: background 0.1s, border-color 0.1s;
684
+ }
685
+ .sci-key:hover { border-color: var(--fc-accent); }
686
+ .sci-key:active { background: var(--fc-accent-weak); }
687
+ .sci-key.num { font-weight: 600; }
688
+ .sci-key.fn { color: var(--fc-slate); font-size: 14px; }
689
+ .sci-key.op { background: var(--fc-paper); font-weight: 600; }
690
+ .sci-key.accent { background: var(--fc-accent); color: #fff; border-color: var(--fc-accent); font-weight: 700; }
691
+ .sci-key.mode { background: var(--fc-paper); color: var(--fc-slate); font-size: 13px; }
692
+ .sci-key.mode.on { background: var(--fc-accent); color: #fff; border-color: var(--fc-accent); }
693
+
694
+ .sci-memrow { margin-top: 6px; }
695
+
696
+ .sci-tape { margin-top: calc(var(--fc-space) * 2); border-top: 1px solid var(--fc-line); padding-top: var(--fc-space); }
697
+ .sci-tape-head { display: flex; align-items: center; justify-content: space-between; color: var(--fc-slate); font-size: 12px; margin-bottom: 6px; }
698
+ .sci-tape ul { list-style: none; margin: 0; padding: 0; max-height: 200px; overflow: auto; }
699
+ .sci-tape li button {
700
+ width: 100%; display: flex; justify-content: space-between; gap: 12px; align-items: baseline;
701
+ border: 0; background: transparent; color: var(--fc-ink); font: inherit; cursor: pointer;
702
+ padding: 7px 8px; border-radius: var(--fc-radius-field); text-align: left;
703
+ }
704
+ .sci-tape li button:hover { background: var(--fc-accent-weak); }
705
+ .sci-tape-expr { color: var(--fc-slate); font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
706
+ .sci-tape-res { font-weight: 600; font-size: 14px; white-space: nowrap; }
707
+
708
+ /* ─────────────── Integration panel (§8.2) ─────────────── */
709
+ .int-note { font-size: 12px; color: var(--fc-slate); margin-top: 4px; line-height: 1.4; }
710
+ .mono { font-family: ui-monospace, 'SF Mono', Menlo, monospace; font-size: 12px; word-break: break-all; }
711
+
712
+ .int-table-wrap { overflow-x: auto; border: 1px solid var(--fc-line); border-radius: var(--fc-radius-field); }
713
+ table.int-table { width: 100%; border-collapse: collapse; font-size: 13px; }
714
+ table.int-table th, table.int-table td { padding: 8px 10px; text-align: left; border-bottom: 1px solid var(--fc-line); vertical-align: top; }
715
+ table.int-table thead th { color: var(--fc-slate); font-size: 12px; font-weight: 600; background: var(--fc-paper); }
716
+ table.int-table select, .int-cell-input {
717
+ border: 1px solid var(--fc-line);
718
+ background: var(--fc-paper);
719
+ color: var(--fc-ink);
720
+ border-radius: 8px;
721
+ padding: 6px 8px;
722
+ font: inherit;
723
+ font-size: 13px;
724
+ width: 100%;
725
+ min-width: 90px;
726
+ }
727
+ .int-rowbtns { white-space: nowrap; display: flex; gap: 4px; }
728
+ .int-rowbtns .icon-btn { height: 30px; min-width: 30px; padding: 0 8px; }
729
+
730
+ .int-preview {
731
+ background: var(--fc-paper);
732
+ border: 1px solid var(--fc-line);
733
+ border-radius: var(--fc-radius-field);
734
+ padding: 12px 14px;
735
+ font-family: ui-monospace, 'SF Mono', Menlo, monospace;
736
+ font-size: 12px;
737
+ line-height: 1.5;
738
+ color: var(--fc-ink);
739
+ overflow: auto;
740
+ max-height: 420px;
741
+ margin: 6px 0 0;
742
+ white-space: pre-wrap;
743
+ word-break: break-word;
744
+ }
745
+
746
+ .int-cols { display: grid; gap: calc(var(--fc-space) * 2); }
747
+ @media (min-width: 820px) { .int-cols { grid-template-columns: 1fr 360px; } }
748
+
749
+ .int-includes { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 12px; }
750
+ .int-chk { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--fc-ink); }
751
+ .int-subform { border: 1px dashed var(--fc-line); border-radius: var(--fc-radius-field); padding: 12px; }