@vimoxshah/tokenflow 1.1.1 → 1.2.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.
- package/CHANGELOG.md +228 -0
- package/Dockerfile.team +20 -0
- package/README.md +30 -11
- package/bin/tokenflow.js +147 -12
- package/design/tokens.yaml +330 -0
- package/docs/architecture.md +5 -4
- package/docs/cli.md +204 -0
- package/docs/configuration.md +117 -2
- package/docs/design-system.md +187 -0
- package/docs/exports-and-budgets.md +85 -0
- package/docs/guard-codex.md +132 -0
- package/docs/ledger.md +144 -0
- package/docs/live-mode.md +40 -0
- package/docs/media/overview-aurora-dark.png +0 -0
- package/docs/media/receipts-aurora-dark.png +0 -0
- package/docs/providers-otel.md +179 -0
- package/docs/providers.md +54 -1
- package/docs/receipt-schema.md +74 -0
- package/docs/roadmap.md +182 -0
- package/docs/team-server.md +170 -0
- package/docs/ui-views.md +322 -0
- package/package.json +7 -2
- package/schemas/receipt.v0.json +160 -0
- package/scripts/build-dmg.sh +11 -2
- package/scripts/build-menubar-app.sh +58 -7
- package/scripts/design-build.js +475 -0
- package/src/analytics/anatomy.js +467 -0
- package/src/analytics/branch-compare.js +159 -0
- package/src/analytics/cache-health.js +141 -0
- package/src/analytics/live-view.js +266 -0
- package/src/analytics/receipt-schema.js +214 -0
- package/src/analytics/receipt.js +709 -0
- package/src/analytics/rhythm.js +184 -0
- package/src/analytics/whatif.js +263 -0
- package/src/commands/budget-scopes.js +133 -0
- package/src/commands/doctor-checks.js +400 -0
- package/src/commands/guard.js +531 -0
- package/src/commands/hooks.js +238 -0
- package/src/commands/pricing-diff.js +316 -0
- package/src/commands/receipt.js +226 -0
- package/src/commands/team-serve.js +407 -0
- package/src/commands/week.js +86 -0
- package/src/core/annotations.js +97 -0
- package/src/core/budget.js +33 -0
- package/src/core/bundle.js +45 -2
- package/src/core/ingest.js +33 -0
- package/src/core/live-status.js +227 -2
- package/src/core/policy.js +103 -0
- package/src/core/receipt-note.js +123 -0
- package/src/core/repo.js +64 -0
- package/src/core/sync.js +163 -26
- package/src/core/team.js +0 -0
- package/src/export/html-snapshot.js +28 -1
- package/src/export/menubar.js +21 -0
- package/src/export/receipt-card.js +210 -0
- package/src/export/week-card.js +185 -0
- package/src/providers/mock/index.js +383 -52
- package/src/providers/openai/index.js +31 -1
- package/src/providers/otel/index.js +656 -0
- package/src/server/routes/annotations.js +42 -0
- package/src/server/routes/cache-health.js +95 -0
- package/src/server/routes/index.js +54 -0
- package/src/server/routes/session.js +157 -0
- package/src/server/server.js +47 -1
- package/src/ui/app.js +541 -308
- package/src/ui/charts.js +95 -0
- package/src/ui/first-run.js +144 -0
- package/src/ui/index.html +4 -1
- package/src/ui/palette.js +335 -0
- package/src/ui/styles/anatomy.css +117 -0
- package/src/ui/styles/annotations.css +40 -0
- package/src/ui/styles/branches.css +99 -0
- package/src/ui/styles/cache.css +6 -0
- package/src/ui/styles/first-run.css +31 -0
- package/src/ui/styles/live.css +100 -0
- package/src/ui/styles/palette.css +85 -0
- package/src/ui/styles/rhythm.css +8 -0
- package/src/ui/styles/whatif.css +55 -0
- package/src/ui/styles.css +303 -196
- package/src/ui/views/anatomy.js +567 -0
- package/src/ui/views/annotations.js +121 -0
- package/src/ui/views/branches.js +304 -0
- package/src/ui/views/cache.js +232 -0
- package/src/ui/views/index.js +85 -0
- package/src/ui/views/live.js +683 -0
- package/src/ui/views/rhythm.js +206 -0
- package/src/ui/views/whatif.js +196 -0
package/src/ui/styles.css
CHANGED
|
@@ -1,58 +1,57 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Tokenflow — design system
|
|
3
|
-
|
|
4
|
-
Two independent axes on <html>:
|
|
5
|
-
data-mode = dark | light ink, surfaces polarity, series steps
|
|
6
|
-
data-skin = aurora | terminal | editorial
|
|
7
|
-
|
|
8
|
-
The categorical series slots belong to the MODE, not the skin: they were run
|
|
9
|
-
through the dataviz validator (lightness band, chroma floor, adjacent CVD
|
|
10
|
-
separation, normal-vision floor, contrast) against each skin's actual chart
|
|
11
|
-
surface, and all three dark surfaces and all three light surfaces pass with
|
|
12
|
-
the same eight steps. A skin therefore restyles the room, never the data —
|
|
13
|
-
which is the point: switching theme must not change what a colour means.
|
|
14
|
-
|
|
15
|
-
Three light-mode slots (aqua, yellow, magenta) sit under 3:1 on a light
|
|
16
|
-
surface. That is the documented relief case, and the relief ships: every
|
|
17
|
-
chart in this app has a table twin and direct labels.
|
|
18
|
-
========================================================================= */
|
|
19
|
-
|
|
1
|
+
/* @generated design-tokens:start — TokenFlow design tokens for the dashboard and the offline snapshot. Source: design/tokens.yaml · rebuild: npm run design. Hand edits here are overwritten and fail test/design.test.js. */
|
|
20
2
|
:root {
|
|
21
|
-
/* --- mode-independent ------------------------------------------------- */
|
|
22
3
|
--sans: system-ui, -apple-system, 'Segoe UI', sans-serif;
|
|
23
4
|
--mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;
|
|
24
5
|
--serif: 'Iowan Old Style', 'Palatino Linotype', Palatino, 'Book Antiqua', Georgia, serif;
|
|
25
|
-
--display: var(--sans);
|
|
26
|
-
--figure: var(--sans);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
--
|
|
31
|
-
--
|
|
32
|
-
--
|
|
33
|
-
--
|
|
34
|
-
--
|
|
35
|
-
--hero-
|
|
36
|
-
--
|
|
37
|
-
|
|
38
|
-
/*
|
|
39
|
-
--
|
|
40
|
-
|
|
41
|
-
|
|
6
|
+
--display: var(--sans);
|
|
7
|
+
--figure: var(--sans);
|
|
8
|
+
|
|
9
|
+
/* type scale — size-specific tracking and leading */
|
|
10
|
+
--fs-micro: 10.5px; --tr-micro: .02em; --lh-micro: 1.3;
|
|
11
|
+
--fs-label: 11.5px; --tr-label: .012em; --lh-label: 1.35;
|
|
12
|
+
--fs-caption: 12.5px; --tr-caption: .004em; --lh-caption: 1.4;
|
|
13
|
+
--fs-body: 13.5px; --tr-body: 0; --lh-body: 1.45;
|
|
14
|
+
--fs-title: 15px; --tr-title: -.006em; --lh-title: 1.3;
|
|
15
|
+
--fs-figure: 25px; --tr-figure: -.015em; --lh-figure: 1.12;
|
|
16
|
+
--fs-hero: 42px; --tr-hero: -.025em; --lh-hero: 1.05;
|
|
17
|
+
--fs-display: 60px; --tr-display: -.03em; --lh-display: 1;
|
|
18
|
+
|
|
19
|
+
/* space */
|
|
20
|
+
--sp-2: 2px; --sp-4: 4px; --sp-6: 6px; --sp-8: 8px; --sp-10: 10px; --sp-12: 12px; --sp-14: 14px; --sp-16: 16px; --sp-20: 20px; --sp-24: 24px; --sp-32: 32px; --sp-40: 40px; --sp-56: 56px; --sp-72: 72px;
|
|
21
|
+
|
|
22
|
+
/* radius */
|
|
23
|
+
--radius-xs: 5px; --radius-sm: 8px; --radius: 12px; --radius-lg: 16px; --radius-pill: 999px;
|
|
24
|
+
|
|
25
|
+
/* motion · ui tier — nothing over 300ms, no animation on keyboard actions */
|
|
26
|
+
--dur-press: 120ms; --dur-fast: 160ms; --dur-base: 220ms; --dur-slow: 300ms;
|
|
27
|
+
--ease-out: cubic-bezier(.23, 1, .32, 1);
|
|
28
|
+
--ease-in-out: cubic-bezier(.77, 0, .175, 1);
|
|
29
|
+
--ease-drawer: cubic-bezier(.32, .72, 0, 1);
|
|
30
|
+
--stagger: 40ms; --stagger-max: 6;
|
|
31
|
+
/* motion · narrative tier — reveals on reading surfaces */
|
|
32
|
+
--nar-fast: 180ms; --nar-base: 320ms; --nar-slow: 700ms;
|
|
33
|
+
--nar-ease: cubic-bezier(.2, .7, .3, 1);
|
|
34
|
+
--nar-ease-out: cubic-bezier(.16, 1, .3, 1);
|
|
35
|
+
--nar-stagger: 85ms; --nar-stagger-max: 7;
|
|
36
|
+
|
|
37
|
+
/* status — reserved for state, never a series, never a mark, never themed */
|
|
38
|
+
--good: #0ca30c;
|
|
39
|
+
--warning: #fab219;
|
|
40
|
+
--serious: #ec835a;
|
|
42
41
|
--critical: #d03b3b;
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
/*
|
|
44
|
+
/* series / sequential / diverging steps belong to the MODE, never the skin */
|
|
46
45
|
:root, :root[data-mode='dark'] {
|
|
47
46
|
color-scheme: dark;
|
|
48
|
-
--series-1: #3987e5;
|
|
49
|
-
--series-2: #d95926;
|
|
50
|
-
--series-3: #199e70;
|
|
51
|
-
--series-4: #c98500;
|
|
52
|
-
--series-5: #d55181;
|
|
53
|
-
--series-6: #008300;
|
|
54
|
-
--series-7: #9085e9;
|
|
55
|
-
--series-8: #e66767;
|
|
47
|
+
--series-1: #3987e5;
|
|
48
|
+
--series-2: #d95926;
|
|
49
|
+
--series-3: #199e70;
|
|
50
|
+
--series-4: #c98500;
|
|
51
|
+
--series-5: #d55181;
|
|
52
|
+
--series-6: #008300;
|
|
53
|
+
--series-7: #9085e9;
|
|
54
|
+
--series-8: #e66767;
|
|
56
55
|
--seq-1: #184f95;
|
|
57
56
|
--seq-2: #1c5cab;
|
|
58
57
|
--seq-3: #256abf;
|
|
@@ -60,8 +59,12 @@
|
|
|
60
59
|
--seq-5: #3987e5;
|
|
61
60
|
--seq-6: #5598e7;
|
|
62
61
|
--seq-7: #86b6ef;
|
|
62
|
+
--div-1: #d95926;
|
|
63
|
+
--div-2: #e39a78;
|
|
64
|
+
--div-3: #6b7385;
|
|
65
|
+
--div-4: #7fb0f0;
|
|
66
|
+
--div-5: #3987e5;
|
|
63
67
|
}
|
|
64
|
-
|
|
65
68
|
:root[data-mode='light'] {
|
|
66
69
|
color-scheme: light;
|
|
67
70
|
--series-1: #2a78d6;
|
|
@@ -79,155 +82,150 @@
|
|
|
79
82
|
--seq-5: #2a78d6;
|
|
80
83
|
--seq-6: #1c5cab;
|
|
81
84
|
--seq-7: #104281;
|
|
85
|
+
--div-1: #eb6834;
|
|
86
|
+
--div-2: #f2b28f;
|
|
87
|
+
--div-3: #9aa2b3;
|
|
88
|
+
--div-4: #8dbaf0;
|
|
89
|
+
--div-5: #2a78d6;
|
|
82
90
|
}
|
|
83
91
|
|
|
84
|
-
/*
|
|
85
|
-
/* Deep indigo-slate, layered cards, one luminous accent, a restrained glow
|
|
86
|
-
behind the header. The default because it reads as a product rather than a
|
|
87
|
-
tool, without tinting the data. */
|
|
92
|
+
/* skin: aurora — Indigo-slate, layered, luminous */
|
|
88
93
|
:root, :root[data-skin='aurora'] {
|
|
89
|
-
--plane:
|
|
90
|
-
--surface-1:
|
|
91
|
-
--surface-2:
|
|
92
|
-
--surface-3:
|
|
93
|
-
--text-primary:
|
|
94
|
+
--plane: #0a0d14;
|
|
95
|
+
--surface-1: #111624;
|
|
96
|
+
--surface-2: #171d2e;
|
|
97
|
+
--surface-3: #1f2637;
|
|
98
|
+
--text-primary: #f3f6fc;
|
|
94
99
|
--text-secondary: #a7b2c9;
|
|
95
|
-
--text-muted:
|
|
96
|
-
--grid:
|
|
97
|
-
--axis:
|
|
98
|
-
--border:
|
|
99
|
-
--border-strong:
|
|
100
|
-
--
|
|
101
|
-
--accent
|
|
102
|
-
--accent-
|
|
103
|
-
--
|
|
104
|
-
--radius-sm: 9px;
|
|
100
|
+
--text-muted: #7d879e;
|
|
101
|
+
--grid: #1e2436;
|
|
102
|
+
--axis: #2b3348;
|
|
103
|
+
--border: rgba(154, 172, 255, 0.11);
|
|
104
|
+
--border-strong: rgba(154, 172, 255, 0.20);
|
|
105
|
+
--hairline: rgba(154, 172, 255, 0.066);
|
|
106
|
+
--accent: #8f9dff;
|
|
107
|
+
--accent-solid: #4a5ae0;
|
|
108
|
+
--accent-ink: #ffffff;
|
|
105
109
|
--shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 14px 40px rgba(4, 8, 20, .45);
|
|
106
|
-
--glow:
|
|
107
|
-
radial-gradient(1100px 460px at 8% -10%, rgba(94, 114, 255, .17), transparent 62%),
|
|
108
|
-
radial-gradient(820px 420px at 92% -14%, rgba(25, 158, 112, .11), transparent 60%);
|
|
110
|
+
--glow: radial-gradient(1100px 460px at 8% -10%, rgba(94, 114, 255, .17), transparent 62%), radial-gradient(820px 420px at 92% -14%, rgba(25, 158, 112, .11), transparent 60%);
|
|
109
111
|
--hero-glow: radial-gradient(460px 220px at 8% 0%, rgba(94, 114, 255, .22), transparent 72%);
|
|
112
|
+
--radius: 14px; --radius-sm: 9px;
|
|
113
|
+
--pad-card: 14px 16px; --gap-grid: 14px; --fig-scale: 1; --sec-space: 26px; --tracking-h: -0.015em;
|
|
114
|
+
--display: var(--sans); --figure: var(--sans);
|
|
110
115
|
}
|
|
111
|
-
|
|
112
116
|
:root[data-skin='aurora'][data-mode='light'] {
|
|
113
|
-
--plane:
|
|
114
|
-
--surface-1:
|
|
115
|
-
--surface-2:
|
|
116
|
-
--surface-3:
|
|
117
|
-
--text-primary:
|
|
117
|
+
--plane: #f5f7fc;
|
|
118
|
+
--surface-1: #ffffff;
|
|
119
|
+
--surface-2: #f1f4fa;
|
|
120
|
+
--surface-3: #e6eaf4;
|
|
121
|
+
--text-primary: #0d1220;
|
|
118
122
|
--text-secondary: #4b5468;
|
|
119
|
-
--text-muted:
|
|
120
|
-
--grid:
|
|
121
|
-
--axis:
|
|
122
|
-
--border:
|
|
123
|
-
--border-strong:
|
|
124
|
-
--
|
|
125
|
-
--accent
|
|
126
|
-
--accent-
|
|
123
|
+
--text-muted: #6c7488;
|
|
124
|
+
--grid: #e4e8f1;
|
|
125
|
+
--axis: #c7cddb;
|
|
126
|
+
--border: rgba(13, 18, 32, 0.09);
|
|
127
|
+
--border-strong: rgba(13, 18, 32, 0.17);
|
|
128
|
+
--hairline: rgba(13, 18, 32, 0.054);
|
|
129
|
+
--accent: #3d4dd6;
|
|
130
|
+
--accent-solid: #3d4dd6;
|
|
131
|
+
--accent-ink: #ffffff;
|
|
127
132
|
--shadow: 0 1px 2px rgba(16, 24, 48, .06), 0 12px 32px rgba(16, 24, 48, .08);
|
|
128
133
|
--glow: radial-gradient(1100px 460px at 8% -10%, rgba(94, 114, 255, .10), transparent 62%);
|
|
129
134
|
--hero-glow: radial-gradient(420px 180px at 12% 0%, rgba(94, 114, 255, .09), transparent 70%);
|
|
130
135
|
}
|
|
131
136
|
|
|
132
|
-
/*
|
|
133
|
-
/* Near-black, hairlines, mono numerals, one electric accent. An instrument. */
|
|
137
|
+
/* skin: terminal — Near-black, hairlines, mono */
|
|
134
138
|
:root[data-skin='terminal'] {
|
|
135
|
-
--plane:
|
|
136
|
-
--surface-1:
|
|
137
|
-
--surface-2:
|
|
138
|
-
--surface-3:
|
|
139
|
-
--text-primary:
|
|
139
|
+
--plane: #06070a;
|
|
140
|
+
--surface-1: #0c0e13;
|
|
141
|
+
--surface-2: #11141a;
|
|
142
|
+
--surface-3: #171b22;
|
|
143
|
+
--text-primary: #eef2f6;
|
|
140
144
|
--text-secondary: #9aa4ad;
|
|
141
|
-
--text-muted:
|
|
142
|
-
--grid:
|
|
143
|
-
--axis:
|
|
144
|
-
--border:
|
|
145
|
-
--border-strong:
|
|
146
|
-
--
|
|
147
|
-
--accent
|
|
148
|
-
--accent-
|
|
149
|
-
--
|
|
150
|
-
--radius-sm: 4px;
|
|
145
|
+
--text-muted: #737b85;
|
|
146
|
+
--grid: #171b22;
|
|
147
|
+
--axis: #262c34;
|
|
148
|
+
--border: rgba(255, 255, 255, 0.09);
|
|
149
|
+
--border-strong: rgba(255, 255, 255, 0.16);
|
|
150
|
+
--hairline: rgba(255, 255, 255, 0.054);
|
|
151
|
+
--accent: #24d67e;
|
|
152
|
+
--accent-solid: #24d67e;
|
|
153
|
+
--accent-ink: #04140b;
|
|
151
154
|
--shadow: 0 0 0 1px rgba(255, 255, 255, .05), 0 10px 26px rgba(0, 0, 0, .55);
|
|
152
155
|
--glow: none;
|
|
153
156
|
--hero-glow: none;
|
|
154
|
-
--
|
|
155
|
-
--
|
|
156
|
-
--
|
|
157
|
-
--pad-card: 11px 13px;
|
|
158
|
-
--gap-grid: 9px;
|
|
159
|
-
--fig-scale: 0.94;
|
|
160
|
-
--sec-space: 20px;
|
|
157
|
+
--radius: 5px; --radius-sm: 4px;
|
|
158
|
+
--pad-card: 11px 13px; --gap-grid: 9px; --fig-scale: 0.94; --sec-space: 20px; --tracking-h: 0.01em;
|
|
159
|
+
--display: var(--mono); --figure: var(--mono);
|
|
161
160
|
}
|
|
162
|
-
|
|
163
161
|
:root[data-skin='terminal'][data-mode='light'] {
|
|
164
|
-
--plane:
|
|
165
|
-
--surface-1:
|
|
166
|
-
--surface-2:
|
|
167
|
-
--surface-3:
|
|
168
|
-
--text-primary:
|
|
162
|
+
--plane: #f7f8f9;
|
|
163
|
+
--surface-1: #ffffff;
|
|
164
|
+
--surface-2: #f2f4f5;
|
|
165
|
+
--surface-3: #e8eaec;
|
|
166
|
+
--text-primary: #0a0c0f;
|
|
169
167
|
--text-secondary: #494f57;
|
|
170
|
-
--text-muted:
|
|
171
|
-
--grid:
|
|
172
|
-
--axis:
|
|
173
|
-
--border:
|
|
174
|
-
--border-strong:
|
|
175
|
-
--
|
|
176
|
-
--accent
|
|
177
|
-
--accent-
|
|
168
|
+
--text-muted: #666d75;
|
|
169
|
+
--grid: #e8eaec;
|
|
170
|
+
--axis: #c9cdd2;
|
|
171
|
+
--border: rgba(10, 12, 15, 0.10);
|
|
172
|
+
--border-strong: rgba(10, 12, 15, 0.18);
|
|
173
|
+
--hairline: rgba(10, 12, 15, 0.06);
|
|
174
|
+
--accent: #0b6b3d;
|
|
175
|
+
--accent-solid: #0b6b3d;
|
|
176
|
+
--accent-ink: #ffffff;
|
|
178
177
|
--shadow: 0 0 0 1px rgba(10, 12, 15, .05), 0 8px 20px rgba(10, 12, 15, .07);
|
|
178
|
+
--glow: none;
|
|
179
|
+
--hero-glow: none;
|
|
179
180
|
}
|
|
180
181
|
|
|
181
|
-
/*
|
|
182
|
-
/* Warm charcoal, wide margins, serif display, numerals treated as type. */
|
|
182
|
+
/* skin: editorial — Warm charcoal, serif figures */
|
|
183
183
|
:root[data-skin='editorial'] {
|
|
184
|
-
--plane:
|
|
185
|
-
--surface-1:
|
|
186
|
-
--surface-2:
|
|
187
|
-
--surface-3:
|
|
188
|
-
--text-primary:
|
|
184
|
+
--plane: #16130c;
|
|
185
|
+
--surface-1: #201c13;
|
|
186
|
+
--surface-2: #282318;
|
|
187
|
+
--surface-3: #322b1f;
|
|
188
|
+
--text-primary: #f8f4ec;
|
|
189
189
|
--text-secondary: #c4bcab;
|
|
190
|
-
--text-muted:
|
|
191
|
-
--grid:
|
|
192
|
-
--axis:
|
|
193
|
-
--border:
|
|
194
|
-
--border-strong:
|
|
195
|
-
--
|
|
196
|
-
--accent
|
|
197
|
-
--accent-
|
|
198
|
-
--
|
|
199
|
-
--radius-sm: 6px;
|
|
190
|
+
--text-muted: #998f7d;
|
|
191
|
+
--grid: #262218;
|
|
192
|
+
--axis: #3a3529;
|
|
193
|
+
--border: rgba(255, 240, 214, 0.10);
|
|
194
|
+
--border-strong: rgba(255, 240, 214, 0.18);
|
|
195
|
+
--hairline: rgba(255, 240, 214, 0.06);
|
|
196
|
+
--accent: #e0a852;
|
|
197
|
+
--accent-solid: #e0a852;
|
|
198
|
+
--accent-ink: #1a1409;
|
|
200
199
|
--shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 12px 34px rgba(20, 14, 4, .38);
|
|
201
200
|
--glow: radial-gradient(900px 420px at 88% -12%, rgba(224, 168, 82, .11), transparent 60%);
|
|
202
201
|
--hero-glow: none;
|
|
203
|
-
--
|
|
204
|
-
--
|
|
205
|
-
--
|
|
206
|
-
--pad-card: 20px 22px;
|
|
207
|
-
--gap-grid: 18px;
|
|
208
|
-
--fig-scale: 1.12;
|
|
209
|
-
--sec-space: 38px;
|
|
202
|
+
--radius: 8px; --radius-sm: 6px;
|
|
203
|
+
--pad-card: 20px 22px; --gap-grid: 18px; --fig-scale: 1.12; --sec-space: 38px; --tracking-h: -0.005em;
|
|
204
|
+
--display: var(--serif); --figure: var(--serif);
|
|
210
205
|
}
|
|
211
|
-
|
|
212
206
|
:root[data-skin='editorial'][data-mode='light'] {
|
|
213
|
-
--plane:
|
|
214
|
-
--surface-1:
|
|
215
|
-
--surface-2:
|
|
216
|
-
--surface-3:
|
|
217
|
-
--text-primary:
|
|
207
|
+
--plane: #f9f6ef;
|
|
208
|
+
--surface-1: #fffdf8;
|
|
209
|
+
--surface-2: #f4f0e6;
|
|
210
|
+
--surface-3: #eae5d8;
|
|
211
|
+
--text-primary: #1a1712;
|
|
218
212
|
--text-secondary: #544e42;
|
|
219
|
-
--text-muted:
|
|
220
|
-
--grid:
|
|
221
|
-
--axis:
|
|
222
|
-
--border:
|
|
223
|
-
--border-strong:
|
|
224
|
-
--
|
|
225
|
-
--accent
|
|
226
|
-
--accent-
|
|
213
|
+
--text-muted: #736c60;
|
|
214
|
+
--grid: #e8e2d4;
|
|
215
|
+
--axis: #cbc4b2;
|
|
216
|
+
--border: rgba(26, 23, 18, 0.10);
|
|
217
|
+
--border-strong: rgba(26, 23, 18, 0.18);
|
|
218
|
+
--hairline: rgba(26, 23, 18, 0.06);
|
|
219
|
+
--accent: #8a5514;
|
|
220
|
+
--accent-solid: #8a5514;
|
|
221
|
+
--accent-ink: #ffffff;
|
|
227
222
|
--shadow: 0 1px 2px rgba(60, 45, 20, .06), 0 12px 30px rgba(60, 45, 20, .08);
|
|
228
223
|
--glow: radial-gradient(900px 420px at 88% -12%, rgba(224, 168, 82, .13), transparent 60%);
|
|
224
|
+
--hero-glow: none;
|
|
229
225
|
}
|
|
230
226
|
|
|
227
|
+
/* @generated design-tokens:end */
|
|
228
|
+
|
|
231
229
|
* { box-sizing: border-box; }
|
|
232
230
|
|
|
233
231
|
html, body {
|
|
@@ -250,12 +248,18 @@ a:hover { text-decoration: underline; }
|
|
|
250
248
|
/* ---------------------------------------------------------------- layout -- */
|
|
251
249
|
.wrap { max-width: 1680px; margin: 0 auto; padding: 0 20px 72px; }
|
|
252
250
|
|
|
251
|
+
/* A translucent material: content scrolls under it, and the blur + saturation
|
|
252
|
+
keep the header legible over any chart. Frostier when the user asks for it. */
|
|
253
253
|
header.top {
|
|
254
254
|
position: sticky; top: 0; z-index: 40;
|
|
255
|
-
background: color-mix(in srgb, var(--plane)
|
|
256
|
-
backdrop-filter: blur(
|
|
255
|
+
background: color-mix(in srgb, var(--plane) 78%, transparent);
|
|
256
|
+
backdrop-filter: blur(18px) saturate(1.6);
|
|
257
|
+
-webkit-backdrop-filter: blur(18px) saturate(1.6);
|
|
257
258
|
border-bottom: 1px solid var(--border);
|
|
258
259
|
}
|
|
260
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
261
|
+
header.top { background: var(--plane); backdrop-filter: none; -webkit-backdrop-filter: none; }
|
|
262
|
+
}
|
|
259
263
|
.top-inner {
|
|
260
264
|
max-width: 1680px; margin: 0 auto; padding: 14px 20px 12px;
|
|
261
265
|
display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
|
|
@@ -280,13 +284,20 @@ header.top {
|
|
|
280
284
|
/* ---------------------------------------------------------------- inputs -- */
|
|
281
285
|
.btn {
|
|
282
286
|
appearance: none; border: 1px solid var(--border-strong); background: var(--surface-2);
|
|
283
|
-
color: var(--text-primary); font: inherit; font-size: 13px; padding: 7px 12px;
|
|
287
|
+
color: var(--text-primary); font: inherit; font-size: 13px; letter-spacing: var(--tr-body); padding: 7px 12px;
|
|
284
288
|
border-radius: var(--radius-sm); cursor: pointer; display: inline-flex;
|
|
285
|
-
align-items: center; gap: 7px;
|
|
286
|
-
|
|
289
|
+
align-items: center; gap: 7px; white-space: nowrap;
|
|
290
|
+
transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), transform var(--dur-press) var(--ease-out);
|
|
291
|
+
}
|
|
292
|
+
@media (hover: hover) and (pointer: fine) {
|
|
293
|
+
.btn:hover { background: var(--surface-3); border-color: var(--text-muted); }
|
|
294
|
+
}
|
|
295
|
+
/* Feedback lives on the press, not the release: the interface heard you. */
|
|
296
|
+
.btn:active { transform: scale(.97); }
|
|
297
|
+
.btn:focus-visible, .chip:focus-visible, nav.tabs button:focus-visible, select:focus-visible,
|
|
298
|
+
input:focus-visible, .theme-row:focus-visible, .ms-row:focus-visible, table.tbl tbody tr:focus-visible {
|
|
299
|
+
outline: 2px solid var(--accent); outline-offset: 2px;
|
|
287
300
|
}
|
|
288
|
-
.btn:hover { background: var(--surface-3); border-color: var(--text-muted); }
|
|
289
|
-
.btn:active { transform: translateY(1px); }
|
|
290
301
|
.btn[disabled] { opacity: .5; cursor: default; }
|
|
291
302
|
.btn.primary { background: var(--accent-solid); border-color: transparent; color: var(--accent-ink); }
|
|
292
303
|
.btn.primary:hover { filter: brightness(1.08); }
|
|
@@ -310,9 +321,14 @@ label.fld { display: flex; flex-direction: column; gap: 4px; font-size: 11px; co
|
|
|
310
321
|
.chips { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
|
|
311
322
|
.chip {
|
|
312
323
|
border: 1px solid var(--border-strong); background: var(--surface-2); color: var(--text-secondary);
|
|
313
|
-
border-radius:
|
|
324
|
+
border-radius: var(--radius-pill); padding: 4px 10px; font-size: 12px; letter-spacing: var(--tr-caption);
|
|
325
|
+
cursor: pointer; white-space: nowrap;
|
|
326
|
+
transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), transform var(--dur-press) var(--ease-out);
|
|
327
|
+
}
|
|
328
|
+
@media (hover: hover) and (pointer: fine) {
|
|
329
|
+
.chip:hover { background: var(--surface-3); color: var(--text-primary); }
|
|
314
330
|
}
|
|
315
|
-
.chip:
|
|
331
|
+
.chip:active { transform: scale(.97); }
|
|
316
332
|
.chip[aria-pressed=true] { background: var(--accent-solid); border-color: transparent; color: var(--accent-ink); }
|
|
317
333
|
.chip .x { opacity: .7; margin-left: 4px; }
|
|
318
334
|
|
|
@@ -323,8 +339,17 @@ label.fld { display: flex; flex-direction: column; gap: 4px; font-size: 11px; co
|
|
|
323
339
|
position: absolute; z-index: 60; top: calc(100% + 5px); left: 0; width: 268px;
|
|
324
340
|
background: var(--surface-1); border: 1px solid var(--border-strong);
|
|
325
341
|
border-radius: var(--radius); box-shadow: var(--shadow); padding: 8px; display: none;
|
|
342
|
+
/* A popover grows from its trigger, never from its own centre. */
|
|
343
|
+
transform-origin: top left;
|
|
344
|
+
transition: opacity var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
|
|
345
|
+
}
|
|
346
|
+
.ms.open .ms-pop { display: block; opacity: 1; transform: scale(1); }
|
|
347
|
+
@starting-style {
|
|
348
|
+
.ms.open .ms-pop { opacity: 0; transform: scale(.96); }
|
|
349
|
+
.theme.open .theme-pop { opacity: 0; transform: scale(.96); }
|
|
350
|
+
dialog[open] { opacity: 0; transform: scale(.97); }
|
|
351
|
+
dialog[open]::backdrop { opacity: 0; }
|
|
326
352
|
}
|
|
327
|
-
.ms.open .ms-pop { display: block; }
|
|
328
353
|
.ms-pop input { width: 100%; margin-bottom: 7px; }
|
|
329
354
|
.ms-list { max-height: 258px; overflow: auto; }
|
|
330
355
|
.ms-row {
|
|
@@ -346,31 +371,44 @@ label.fld { display: flex; flex-direction: column; gap: 4px; font-size: 11px; co
|
|
|
346
371
|
.card {
|
|
347
372
|
background: var(--surface-1); border: 1px solid var(--border);
|
|
348
373
|
border-radius: var(--radius); padding: var(--pad-card); min-width: 0;
|
|
349
|
-
transition: border-color
|
|
350
|
-
}
|
|
351
|
-
|
|
374
|
+
transition: border-color var(--dur-fast) var(--ease-out);
|
|
375
|
+
}
|
|
376
|
+
@media (hover: hover) and (pointer: fine) {
|
|
377
|
+
.card:hover { border-color: var(--border-strong); }
|
|
378
|
+
}
|
|
379
|
+
/* Cards stagger in once, when the VIEW changes (app.js sets .view-enter for
|
|
380
|
+
that render only). A filter change re-renders the same view instantly. */
|
|
381
|
+
@keyframes card-in { from { opacity: 0; transform: translateY(6px); } }
|
|
382
|
+
#view.view-enter .card { animation: card-in var(--dur-base) var(--ease-out) both; }
|
|
383
|
+
#view.view-enter .card:nth-child(2) { animation-delay: var(--stagger); }
|
|
384
|
+
#view.view-enter .card:nth-child(3) { animation-delay: calc(var(--stagger) * 2); }
|
|
385
|
+
#view.view-enter .card:nth-child(4) { animation-delay: calc(var(--stagger) * 3); }
|
|
386
|
+
#view.view-enter .card:nth-child(5) { animation-delay: calc(var(--stagger) * 4); }
|
|
387
|
+
#view.view-enter .card:nth-child(6) { animation-delay: calc(var(--stagger) * 5); }
|
|
388
|
+
#view.view-enter .card:nth-child(n+7) { animation-delay: calc(var(--stagger) * 6); }
|
|
352
389
|
/* The hero KPI is the one place a gradient is allowed: a wash behind a single
|
|
353
390
|
number, never behind a mark whose colour has to stay comparable. */
|
|
354
391
|
.card.hero-card { background-image: var(--hero-glow); }
|
|
355
392
|
.card.pad0 { padding: 0; }
|
|
356
393
|
.card h3 {
|
|
357
|
-
margin: 0 0 2px; font-size:
|
|
358
|
-
font-family: var(--display);
|
|
359
|
-
letter-spacing:
|
|
394
|
+
margin: 0 0 2px; font-size: var(--fs-caption); font-weight: 600; color: var(--text-secondary);
|
|
395
|
+
font-family: var(--display); line-height: var(--lh-caption);
|
|
396
|
+
letter-spacing: var(--tr-caption); display: flex; align-items: center; gap: 8px;
|
|
360
397
|
}
|
|
361
|
-
.card .hint { color: var(--text-muted); font-size:
|
|
398
|
+
.card .hint { color: var(--text-muted); font-size: var(--fs-label); letter-spacing: var(--tr-label); line-height: var(--lh-label); margin: 0 0 10px; }
|
|
362
399
|
.card-head { display: flex; align-items: flex-start; gap: 10px; padding: var(--pad-card); padding-bottom: 0; }
|
|
363
400
|
.card-head .spacer { flex: 1; }
|
|
364
401
|
.card-body { padding: 10px 16px 14px; }
|
|
365
402
|
|
|
366
403
|
.kpi { display: flex; flex-direction: column; gap: 3px; }
|
|
367
|
-
.kpi .k-label { font-size:
|
|
404
|
+
.kpi .k-label { font-size: var(--fs-label); letter-spacing: var(--tr-label); color: var(--text-muted); }
|
|
368
405
|
.kpi .k-value {
|
|
369
|
-
font-size: calc(
|
|
370
|
-
letter-spacing: var(--tracking-h); line-height:
|
|
406
|
+
font-size: calc(var(--fs-figure) * var(--fig-scale)); font-weight: 640;
|
|
407
|
+
letter-spacing: var(--tracking-h); line-height: var(--lh-figure);
|
|
371
408
|
font-family: var(--figure); font-variant-numeric: tabular-nums;
|
|
372
409
|
}
|
|
373
|
-
|
|
410
|
+
/* The one hero number on a view: bigger, and tighter still as type grows. */
|
|
411
|
+
.kpi .k-value.hero { font-size: calc(var(--fs-hero) * var(--fig-scale)); letter-spacing: calc(var(--tracking-h) - .01em); line-height: var(--lh-hero); }
|
|
374
412
|
/* A long identifier is not a hero figure: keep it readable and let it wrap. */
|
|
375
413
|
.kpi .k-value.str { font-size: 17px; font-weight: 600; line-height: 1.25; overflow-wrap: anywhere; }
|
|
376
414
|
.card.wide { grid-column: span 2; }
|
|
@@ -392,19 +430,28 @@ label.fld { display: flex; flex-direction: column; gap: 4px; font-size: 11px; co
|
|
|
392
430
|
}
|
|
393
431
|
.sec-title::after { content: ''; flex: 1; height: 1px; background: var(--border); }
|
|
394
432
|
|
|
395
|
-
|
|
433
|
+
/* The tab bar wraps to a second (or third) row rather than scrolling: with
|
|
434
|
+
twenty-plus tabs registered across several views, every tab and the ⌘K
|
|
435
|
+
chip stay visible with no clipped label at any width, and the active tab
|
|
436
|
+
never needs a scrollIntoView() call to stay on screen. */
|
|
437
|
+
.tabbar { display: flex; align-items: flex-end; gap: 8px; border-bottom: 1px solid var(--border); margin-top: 4px; }
|
|
438
|
+
nav.tabs { display: flex; flex-wrap: wrap; gap: 4px; flex: 1 1 auto; min-width: 0; }
|
|
439
|
+
.tabbar .palette-chip { flex: none; margin-bottom: 6px; }
|
|
396
440
|
nav.tabs button {
|
|
397
441
|
appearance: none; background: none; border: 0; border-bottom: 2px solid transparent;
|
|
398
|
-
color: var(--text-muted); font: inherit; font-size:
|
|
442
|
+
color: var(--text-muted); font: inherit; font-size: var(--fs-body); letter-spacing: var(--tr-body); padding: 9px 12px;
|
|
399
443
|
cursor: pointer; white-space: nowrap;
|
|
444
|
+
transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
|
|
445
|
+
}
|
|
446
|
+
@media (hover: hover) and (pointer: fine) {
|
|
447
|
+
nav.tabs button:hover { color: var(--text-primary); }
|
|
400
448
|
}
|
|
401
|
-
nav.tabs button:hover { color: var(--text-primary); }
|
|
402
449
|
nav.tabs button[aria-selected=true] { color: var(--text-primary); border-bottom-color: var(--accent); }
|
|
403
450
|
|
|
404
451
|
/* ---------------------------------------------------------------- charts -- */
|
|
405
452
|
.chart { width: 100%; display: block; overflow: visible; }
|
|
406
453
|
.chart text { font-family: var(--sans); }
|
|
407
|
-
.tick text { fill: var(--text-muted); font-size:
|
|
454
|
+
.tick text { fill: var(--text-muted); font-size: var(--fs-micro); letter-spacing: var(--tr-micro); font-variant-numeric: tabular-nums; }
|
|
408
455
|
.grid-line { stroke: var(--grid); stroke-width: 1; }
|
|
409
456
|
.axis-line { stroke: var(--axis); stroke-width: 1; }
|
|
410
457
|
.series-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
|
|
@@ -430,9 +477,11 @@ nav.tabs button[aria-selected=true] { color: var(--text-primary); border-bottom-
|
|
|
430
477
|
position: fixed; z-index: 200; pointer-events: none; min-width: 150px; max-width: 320px;
|
|
431
478
|
background: var(--surface-1); border: 1px solid var(--border-strong);
|
|
432
479
|
border-radius: var(--radius-sm); box-shadow: var(--shadow); padding: 9px 11px;
|
|
433
|
-
font-size: 12px; opacity: 0;
|
|
480
|
+
font-size: 12px; opacity: 0; transform: scale(.98); transform-origin: top left;
|
|
481
|
+
transition: opacity var(--dur-press) var(--ease-out), transform var(--dur-press) var(--ease-out);
|
|
482
|
+
color: var(--text-primary);
|
|
434
483
|
}
|
|
435
|
-
.tip.on { opacity: 1; }
|
|
484
|
+
.tip.on { opacity: 1; transform: scale(1); }
|
|
436
485
|
.tip .t-head { color: var(--text-secondary); font-size: 11px; margin-bottom: 6px; }
|
|
437
486
|
.tip .t-row { display: flex; align-items: baseline; gap: 8px; padding: 1.5px 0; }
|
|
438
487
|
.tip .t-key { width: 12px; height: 2.5px; border-radius: 2px; flex: none; align-self: center; }
|
|
@@ -493,10 +542,10 @@ table.tbl .mini {
|
|
|
493
542
|
|
|
494
543
|
/* --------------------------------------------------------------- badges -- */
|
|
495
544
|
.badge {
|
|
496
|
-
display: inline-flex; align-items: center; gap: 4px; font-size:
|
|
497
|
-
padding: 2px 7px; border-radius:
|
|
545
|
+
display: inline-flex; align-items: center; gap: 4px; font-size: var(--fs-micro);
|
|
546
|
+
padding: 2px 7px; border-radius: var(--radius-pill); border: 1px solid var(--border-strong);
|
|
498
547
|
color: var(--text-secondary); background: var(--surface-2); white-space: nowrap;
|
|
499
|
-
letter-spacing:
|
|
548
|
+
letter-spacing: var(--tr-micro);
|
|
500
549
|
}
|
|
501
550
|
.badge.est { color: var(--warning); border-color: color-mix(in srgb, var(--warning) 45%, transparent); }
|
|
502
551
|
.badge.meas { color: var(--good); border-color: color-mix(in srgb, var(--good) 45%, transparent); }
|
|
@@ -537,12 +586,14 @@ table.tbl .mini {
|
|
|
537
586
|
.kv dt { color: var(--text-muted); }
|
|
538
587
|
.kv dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }
|
|
539
588
|
|
|
589
|
+
/* A modal is the one popover that stays centred: it is not anchored to a trigger. */
|
|
540
590
|
dialog {
|
|
541
591
|
border: 1px solid var(--border-strong); border-radius: var(--radius); background: var(--surface-1);
|
|
542
592
|
color: var(--text-primary); padding: 0; max-width: 720px; width: calc(100vw - 40px);
|
|
543
|
-
box-shadow: var(--shadow);
|
|
593
|
+
box-shadow: var(--shadow); opacity: 1; transform: scale(1);
|
|
594
|
+
transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
|
|
544
595
|
}
|
|
545
|
-
dialog::backdrop { background: rgba(0,0,0,.55); backdrop-filter: blur(2px); }
|
|
596
|
+
dialog::backdrop { background: rgba(0,0,0,.55); backdrop-filter: blur(2px); opacity: 1; transition: opacity var(--dur-base) var(--ease-out); }
|
|
546
597
|
dialog .d-head { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--border); }
|
|
547
598
|
dialog .d-body { padding: 14px 16px; max-height: 66vh; overflow: auto; }
|
|
548
599
|
dialog .d-foot { display: flex; gap: 8px; justify-content: flex-end; padding: 12px 16px; border-top: 1px solid var(--border); }
|
|
@@ -570,7 +621,9 @@ dialog h2 { margin: 0; font-size: 15px; }
|
|
|
570
621
|
|
|
571
622
|
@media print {
|
|
572
623
|
header.top { position: static; }
|
|
573
|
-
.
|
|
624
|
+
/* .tabbar, not nav.tabs: the wrapper also carries the ⌘K chip, and hiding
|
|
625
|
+
only the nested nav would leave that chip printed on its own row. */
|
|
626
|
+
.btn, .tabbar, .filters { display: none !important; }
|
|
574
627
|
.card { break-inside: avoid; }
|
|
575
628
|
}
|
|
576
629
|
|
|
@@ -579,8 +632,10 @@ dialog h2 { margin: 0; font-size: 15px; }
|
|
|
579
632
|
position: absolute; z-index: 70; top: calc(100% + 6px); right: 0; width: 268px;
|
|
580
633
|
background: var(--surface-1); border: 1px solid var(--border-strong);
|
|
581
634
|
border-radius: var(--radius); box-shadow: var(--shadow); padding: 8px; display: none;
|
|
635
|
+
transform-origin: top right;
|
|
636
|
+
transition: opacity var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
|
|
582
637
|
}
|
|
583
|
-
.theme.open .theme-pop { display: block; }
|
|
638
|
+
.theme.open .theme-pop { display: block; opacity: 1; transform: scale(1); }
|
|
584
639
|
.theme { position: relative; }
|
|
585
640
|
.theme-row {
|
|
586
641
|
display: flex; align-items: center; gap: 10px; padding: 7px 8px; width: 100%;
|
|
@@ -621,6 +676,8 @@ dialog h2 { margin: 0; font-size: 15px; }
|
|
|
621
676
|
}
|
|
622
677
|
|
|
623
678
|
/* ---------------------------------------------------------------- live --- */
|
|
679
|
+
/* The pill lives in the header, so it stays here. The Live TAB's own rules
|
|
680
|
+
moved to src/ui/styles/live.css when the tab became a registered view. */
|
|
624
681
|
|
|
625
682
|
.live-pill {
|
|
626
683
|
display: inline-flex;
|
|
@@ -639,6 +696,56 @@ dialog h2 { margin: 0; font-size: 15px; }
|
|
|
639
696
|
.badge.warn { color: var(--warning); border-color: color-mix(in srgb, var(--warning) 45%, transparent); }
|
|
640
697
|
.badge.stale { color: var(--text-muted); }
|
|
641
698
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
699
|
+
/* ---------------------------------------------------------------- story --- */
|
|
700
|
+
/* The three sentences that matter, before any chart. Numbers wear the figure
|
|
701
|
+
face so the eye lands on them; the words stay words. */
|
|
702
|
+
.story {
|
|
703
|
+
display: grid; gap: 10px;
|
|
704
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
705
|
+
}
|
|
706
|
+
.story-line {
|
|
707
|
+
display: flex; gap: 12px; align-items: flex-start;
|
|
708
|
+
padding: 14px 16px; background: var(--surface-1); border: 1px solid var(--border);
|
|
709
|
+
border-radius: var(--radius); background-image: var(--hero-glow);
|
|
710
|
+
}
|
|
711
|
+
.story-line.correlation { border-left: 2px solid var(--series-7); }
|
|
712
|
+
.story-ico { font-size: 18px; line-height: 1.25; flex: none; }
|
|
713
|
+
.story-text {
|
|
714
|
+
margin: 0; font-size: var(--fs-title); letter-spacing: var(--tr-title); line-height: 1.4;
|
|
715
|
+
color: var(--text-secondary); text-wrap: pretty;
|
|
716
|
+
}
|
|
717
|
+
.story-text strong {
|
|
718
|
+
color: var(--text-primary); font-family: var(--figure); font-weight: 680;
|
|
719
|
+
font-variant-numeric: tabular-nums; letter-spacing: var(--tracking-h);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/* -------------------------------------------------------------- receipts --- */
|
|
723
|
+
.branch-cell { display: inline-flex; align-items: center; gap: 8px; }
|
|
724
|
+
.branch-name { font-family: var(--mono); font-size: 12px; color: var(--text-primary); }
|
|
725
|
+
.receipt { display: grid; gap: 14px; padding: 4px 2px; }
|
|
726
|
+
.receipt-head { display: flex; flex-direction: column; gap: 2px; }
|
|
727
|
+
.receipt-kicker { font-size: var(--fs-label); letter-spacing: .06em; text-transform: uppercase; color: var(--text-muted); }
|
|
728
|
+
.receipt-branch { font-family: var(--mono); font-size: var(--fs-title); color: var(--text-primary); overflow-wrap: anywhere; }
|
|
729
|
+
.receipt-total { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
|
|
730
|
+
.receipt-amount {
|
|
731
|
+
font-family: var(--figure); font-size: calc(var(--fs-hero) * var(--fig-scale)); font-weight: 680;
|
|
732
|
+
letter-spacing: calc(var(--tracking-h) - .01em); line-height: var(--lh-hero); font-variant-numeric: tabular-nums;
|
|
733
|
+
}
|
|
734
|
+
.receipt-amount-sub { color: var(--text-muted); font-size: var(--fs-caption); }
|
|
735
|
+
.split-bar { display: flex; height: 10px; border-radius: var(--radius-pill); overflow: hidden; gap: 2px; background: var(--surface-3); }
|
|
736
|
+
.split-bar .seg { display: block; height: 100%; }
|
|
737
|
+
.split-bar .seg.ctx, .sw.ctx { background: var(--series-1); }
|
|
738
|
+
.split-bar .seg.work, .sw.work { background: var(--series-2); flex: 1; }
|
|
739
|
+
.split-legend { display: flex; gap: 16px; font-size: var(--fs-caption); color: var(--text-secondary); }
|
|
740
|
+
.split-legend .sw { display: inline-block; width: 10px; height: 10px; border-radius: 3px; vertical-align: -1px; }
|
|
741
|
+
.receipt-kv { font-size: var(--fs-caption); }
|
|
742
|
+
.receipt-kv dd { color: var(--text-primary); }
|
|
743
|
+
.receipt-foot { margin: 0; color: var(--text-muted); font-size: var(--fs-label); line-height: var(--lh-label); }
|
|
744
|
+
|
|
745
|
+
/* --------------------------------------------------------- reduced motion --- */
|
|
746
|
+
/* Every primitive degrades to an instant, complete state — never a half-state. */
|
|
747
|
+
@media (prefers-reduced-motion: reduce) {
|
|
748
|
+
*, *::before, *::after { animation-duration: 1ms !important; animation-iteration-count: 1 !important; transition-duration: 1ms !important; }
|
|
749
|
+
.dot.busy { animation: none; opacity: 1; }
|
|
750
|
+
.btn:active, .chip:active { transform: none; }
|
|
751
|
+
}
|