@keenmate/pure-admin-core 2.6.0 → 2.7.1

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 (40) hide show
  1. package/README.md +24 -16
  2. package/dist/css/main.css +1865 -257
  3. package/package.json +1 -1
  4. package/src/scss/_base-css-variables.scss +37 -19
  5. package/src/scss/_core.scss +11 -0
  6. package/src/scss/core-components/_alerts.scss +2 -2
  7. package/src/scss/core-components/_base.scss +19 -2
  8. package/src/scss/core-components/_buttons.scss +12 -8
  9. package/src/scss/core-components/_callouts.scss +1 -1
  10. package/src/scss/core-components/_cards.scss +4 -4
  11. package/src/scss/core-components/_checkbox-lists.scss +2 -2
  12. package/src/scss/core-components/_comparison.scss +7 -4
  13. package/src/scss/core-components/_data-display.scss +24 -15
  14. package/src/scss/core-components/_data-viz.scss +139 -131
  15. package/src/scss/core-components/_file-selector.scss +34 -34
  16. package/src/scss/core-components/_kpi-base.scss +169 -0
  17. package/src/scss/core-components/_kpi-bento.scss +182 -0
  18. package/src/scss/core-components/_kpi-comparison-gauges.scss +132 -0
  19. package/src/scss/core-components/_kpi-editorial-minimal.scss +128 -0
  20. package/src/scss/core-components/_kpi-hero-supporting.scss +210 -0
  21. package/src/scss/core-components/_kpi-numeric-strip.scss +154 -0
  22. package/src/scss/core-components/_kpi-sparkline-list.scss +171 -0
  23. package/src/scss/core-components/_kpi-terminal.scss +229 -0
  24. package/src/scss/core-components/_lists.scss +4 -4
  25. package/src/scss/core-components/_logic-tree.scss +2 -2
  26. package/src/scss/core-components/_modals.scss +69 -0
  27. package/src/scss/core-components/_notifications.scss +17 -17
  28. package/src/scss/core-components/_popconfirm.scss +1 -1
  29. package/src/scss/core-components/_statistics.scss +25 -19
  30. package/src/scss/core-components/_tabs.scss +12 -12
  31. package/src/scss/core-components/_timeline.scss +30 -30
  32. package/src/scss/core-components/badges/_composite-badge-variants.scss +7 -7
  33. package/src/scss/core-components/badges/_composite-badge.scss +1 -1
  34. package/src/scss/core-components/badges/_labels.scss +6 -6
  35. package/src/scss/core-components/forms/_input-wrapper.scss +1 -1
  36. package/src/scss/core-components/forms/_query-editor.scss +10 -10
  37. package/src/scss/core-components/layout/_sidebar-states.scss +1 -0
  38. package/src/scss/core-components/layout/_sidebar.scss +1 -0
  39. package/src/scss/variables/_colors.scss +1 -0
  40. package/src/scss/variables/_components.scss +3 -2
@@ -0,0 +1,132 @@
1
+ /* ========================================
2
+ KPI · Comparison gauges
3
+ Goal-oriented progress bars. Each KPI shows label · value, a bar with a
4
+ target tick, and a 0 · tgt scale below. Bar fill = value/target * 100%,
5
+ capped visually so overshoots are signalled by colour, not overflow.
6
+ ======================================== */
7
+ @use '../variables' as *;
8
+
9
+ .pa-kpi-gauge-list {
10
+ container-type: inline-size;
11
+ }
12
+ .pa-kpi-gauge-list__body { padding: 0; }
13
+
14
+ /* 2-column internal grid by default; collapses to 1-col when card narrows. */
15
+ .pa-kpi-gauge-list__grid {
16
+ display: grid;
17
+ grid-template-columns: repeat(2, 1fr);
18
+ }
19
+ @container (max-width: 600px) {
20
+ .pa-kpi-gauge-list__grid {
21
+ grid-template-columns: 1fr;
22
+ }
23
+ }
24
+
25
+ /* ----- Gauge tile (label/value head · bar · 0/tgt scale) ---------------- */
26
+ .pa-kpi-gauge {
27
+ position: relative;
28
+ padding: 1.6rem 2rem;
29
+ border-bottom: 1px solid var(--pa-border-color);
30
+ border-right: 1px solid var(--pa-border-color);
31
+
32
+ /* Per-tile bar colour cascade — modifiers below set the var, the fill
33
+ reads it. Cleaner than per-modifier-per-element rules; host apps can
34
+ override at the tile level via inline style="--pa-kpi-bar-color: …". */
35
+ --pa-kpi-bar-color: var(--pa-positive);
36
+
37
+ &:nth-child(2n) { border-right: 0; }
38
+ &:nth-last-child(-n+2) { border-bottom: 0; }
39
+
40
+ &--positive { --pa-kpi-bar-color: var(--pa-positive); }
41
+ &--warning { --pa-kpi-bar-color: var(--pa-warning); }
42
+ &--negative { --pa-kpi-bar-color: var(--pa-negative); }
43
+ &--neutral { --pa-kpi-bar-color: color-mix(in srgb, var(--pa-text-color-1) 70%, transparent); }
44
+ }
45
+
46
+ @container (max-width: 600px) {
47
+ .pa-kpi-gauge {
48
+ border-right: 0;
49
+
50
+ &:nth-last-child(-n+2) { border-bottom: 1px solid var(--pa-border-color); }
51
+ &:last-child { border-bottom: 0; }
52
+ }
53
+ }
54
+
55
+ /* ----- Head: label left, value right (baseline-aligned) ----------------- */
56
+ .pa-kpi-gauge__head {
57
+ display: flex;
58
+ justify-content: space-between;
59
+ align-items: baseline;
60
+ gap: 1.2rem;
61
+ margin-bottom: 0.8rem;
62
+ }
63
+ .pa-kpi-gauge__label {
64
+ font-family: var(--base-font-family-mono);
65
+ font-size: 1.4rem;
66
+ font-weight: 700;
67
+ letter-spacing: 0.1em;
68
+ text-transform: uppercase;
69
+ color: color-mix(in srgb, var(--pa-text-color-1) 60%, transparent);
70
+ }
71
+ .pa-kpi-gauge__value {
72
+ font-family: var(--base-font-family-mono);
73
+ line-height: 1;
74
+ }
75
+ .pa-kpi-gauge__num {
76
+ font-size: 2.6rem;
77
+ font-weight: 700;
78
+ letter-spacing: -0.02em;
79
+ color: var(--pa-text-color-1);
80
+ }
81
+ .pa-kpi-gauge__unit {
82
+ font-size: 1.3rem;
83
+ font-weight: 500;
84
+ color: var(--pa-text-secondary);
85
+ margin-left: 0.2rem;
86
+ }
87
+
88
+ /* ----- Bar (track + sentiment-coloured fill + target tick) -------------- */
89
+ .pa-kpi-gauge__bar {
90
+ /* Author-controlled tick position (default 100% — the target sits at the
91
+ right edge of the bar's "0 → target" scale). Override per-tile via
92
+ style="--pa-kpi-gauge-tick-pos: 80%" if the bar represents a wider
93
+ scale where the target sits inside the bar. */
94
+ --pa-kpi-gauge-tick-pos: 100%;
95
+ --pa-kpi-gauge-tick-color: var(--pa-text-color-1);
96
+
97
+ position: relative;
98
+ height: 0.7rem;
99
+ background: var(--pa-surface-track);
100
+ margin-bottom: 0.5rem;
101
+ overflow: visible; /* tick can sit just outside the track */
102
+
103
+ /* Target tick — small bar slightly taller than the track. Centred on
104
+ --pa-kpi-gauge-tick-pos via the negative margin-left (matches half
105
+ the tick width). Full opacity so it stays readable on dark themes
106
+ against bright orange/green fills. */
107
+ &::after {
108
+ content: '';
109
+ position: absolute;
110
+ left: var(--pa-kpi-gauge-tick-pos);
111
+ margin-left: -0.15rem;
112
+ top: -0.2rem;
113
+ bottom: -0.2rem;
114
+ width: 0.3rem;
115
+ background: var(--pa-kpi-gauge-tick-color);
116
+ }
117
+ }
118
+ .pa-kpi-gauge__fill {
119
+ height: 100%;
120
+ background: var(--pa-kpi-bar-color);
121
+ /* Width set inline per tile via style="width: X%". */
122
+ transition: width 0.3s ease;
123
+ }
124
+
125
+ /* ----- Scale row (0 left · tgt XYZ right) ------------------------------- */
126
+ .pa-kpi-gauge__scale {
127
+ display: flex;
128
+ justify-content: space-between;
129
+ font-family: var(--base-font-family-mono);
130
+ font-size: 1.2rem;
131
+ color: var(--pa-text-tertiary);
132
+ }
@@ -0,0 +1,128 @@
1
+ /* ========================================
2
+ KPI · Editorial minimal
3
+ Six KPIs in a 2×3 / 3×2 grid with hairline rules between cells,
4
+ generous space, and an extra-light-weight number as the focal point per
5
+ tile. No charts, no pills — the design's whole identity is the thin
6
+ numeral. Renders as a table card (zero card-body padding) so hairlines
7
+ go edge-to-edge.
8
+ ======================================== */
9
+ @use '../variables' as *;
10
+
11
+ .pa-kpi-edit__body { padding: 0; }
12
+
13
+ /* ----- Grid + hairline rules -------------------------------------------
14
+ `gap: 1px` over `background: var(--pa-border-color)` with each tile
15
+ painting `background: var(--pa-card-bg)` on top. Only the gap shows
16
+ through, giving perfect single-pixel hairlines on every interior
17
+ boundary (vertical and horizontal) for free, regardless of column
18
+ count. The card's outer border supplies the perimeter. */
19
+ .pa-kpi-edit__grid {
20
+ display: grid;
21
+ grid-template-columns: repeat(3, 1fr);
22
+ gap: 1px;
23
+ background: var(--pa-border-color);
24
+ container-type: inline-size;
25
+ }
26
+ @container (max-width: 640px) {
27
+ .pa-kpi-edit__grid { grid-template-columns: repeat(2, 1fr); }
28
+ }
29
+ @container (max-width: 360px) {
30
+ .pa-kpi-edit__grid { grid-template-columns: 1fr; }
31
+ }
32
+
33
+ /* Modifier: force 2-col regardless of card width. For placements wanting
34
+ a deterministic 2×N layout (e.g. 4 tiles as clean 2×2) instead of
35
+ relying on the container query to land in the right bucket. */
36
+ .pa-kpi-edit__grid--2col { grid-template-columns: repeat(2, 1fr); }
37
+
38
+ /* ----- Tile (editorial spacing leans on the 2.4rem padding) ------------- */
39
+ .pa-kpi-edit__tile {
40
+ background: var(--pa-card-bg);
41
+ padding: 2.4rem 2rem;
42
+ display: flex;
43
+ flex-direction: column;
44
+ gap: 1.2rem;
45
+ position: relative;
46
+ min-width: 0;
47
+
48
+ /* Hover host: faint wash so the cursor-anchored popover has a clear
49
+ "this tile" anchor. */
50
+ &:hover {
51
+ background: color-mix(in srgb, var(--pa-text-color-1) 4%, var(--pa-card-bg));
52
+ }
53
+ }
54
+
55
+ /* ----- Label (uppercase mono caption) ----------------------------------- */
56
+ .pa-kpi-edit__label {
57
+ font-family: var(--base-font-family-mono);
58
+ font-size: 1.2rem;
59
+ font-weight: 600;
60
+ letter-spacing: 0.12em;
61
+ text-transform: uppercase;
62
+ color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
63
+ line-height: 1.3;
64
+ }
65
+
66
+ /* ----- Value (the editorial signature) ---------------------------------
67
+ - NOT mono — mono fonts rarely have a true 200 weight; using the body
68
+ sans gives proper thin glyphs that read as "editorial" rather than
69
+ "monospace at low contrast".
70
+ - font-weight: 200 (extra-light). 300 was tested first but didn't read
71
+ distinctly enough as "light" against the body's 400 default.
72
+ - clamp() lets the number shrink in narrow 25% page-grid cells without
73
+ manual breakpoints. */
74
+ .pa-kpi-edit__value {
75
+ font-family: var(--base-font-family);
76
+ font-size: clamp(3.2rem, 18cqi, 5.6rem);
77
+ font-weight: 200;
78
+ letter-spacing: -0.02em;
79
+ line-height: 1;
80
+ color: var(--pa-text-color-1);
81
+ display: flex;
82
+ align-items: baseline;
83
+ gap: 0.2rem;
84
+ flex-wrap: wrap;
85
+ }
86
+ .pa-kpi-edit__num {
87
+ font-weight: 200;
88
+ font-variant-numeric: tabular-nums;
89
+ }
90
+ .pa-kpi-edit__unit {
91
+ font-size: 0.4em;
92
+ font-weight: 300;
93
+ color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
94
+ letter-spacing: 0;
95
+ }
96
+
97
+ /* ----- Meta row (delta + tgt) ------------------------------------------ */
98
+ .pa-kpi-edit__meta {
99
+ display: flex;
100
+ align-items: baseline;
101
+ gap: 1.4rem;
102
+ font-family: var(--base-font-family-mono);
103
+ font-size: 1.3rem;
104
+ line-height: 1.3;
105
+ flex-wrap: wrap;
106
+ }
107
+ .pa-kpi-edit__delta {
108
+ font-weight: 700;
109
+ white-space: nowrap;
110
+ color: var(--pa-positive);
111
+
112
+ &--positive { color: var(--pa-positive); }
113
+ &--negative { color: var(--pa-negative); }
114
+ &--neutral { color: var(--pa-neutral); }
115
+ &--up-strong { color: var(--pa-very-positive); }
116
+ &--down-strong { color: var(--pa-very-negative); }
117
+ }
118
+ .pa-kpi-edit__target {
119
+ font-weight: 500;
120
+ color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
121
+ white-space: nowrap;
122
+
123
+ em {
124
+ font-style: normal;
125
+ color: color-mix(in srgb, var(--pa-text-color-1) 38%, transparent);
126
+ margin-right: 0.5rem;
127
+ }
128
+ }
@@ -0,0 +1,210 @@
1
+ /* ========================================
2
+ KPI · Hero + supporting
3
+ Marketing/exec dashboard pattern: one headline metric on the left (huge
4
+ container-query-relative value, inline meta row, big filled-area
5
+ sparkline), and a vertical rail of compact supporting tiles on the
6
+ right. Container query collapses to single column on narrow cards.
7
+ ======================================== */
8
+ @use '../variables' as *;
9
+
10
+ .pa-kpi-hero-list {
11
+ container-type: inline-size;
12
+ }
13
+ .pa-kpi-hero-list__body { padding: 1.6rem; }
14
+
15
+ /* ----- Layout: hero left, rail right ------------------------------------ */
16
+ .pa-kpi-hero-list__layout {
17
+ display: grid;
18
+ grid-template-columns: 1fr 1fr;
19
+ gap: 1.4rem;
20
+ }
21
+ @container (max-width: 700px) {
22
+ .pa-kpi-hero-list__layout {
23
+ grid-template-columns: 1fr;
24
+ }
25
+ }
26
+ .pa-kpi-hero-list__rail {
27
+ display: flex;
28
+ flex-direction: column;
29
+ gap: 1rem;
30
+ }
31
+
32
+ /* ----- HERO panel (label · big value · meta row · sparkline) ------------ */
33
+ .pa-kpi-hero-main {
34
+ position: relative;
35
+ padding: 1.8rem 2rem;
36
+ border: 1px solid var(--pa-border-color);
37
+ display: flex;
38
+ flex-direction: column;
39
+ gap: 0.6rem;
40
+
41
+ /* Sentiment cascade — chart and delta inherit via currentColor. */
42
+ --pa-kpi-accent: var(--pa-positive);
43
+
44
+ &--positive { --pa-kpi-accent: var(--pa-positive); }
45
+ &--negative { --pa-kpi-accent: var(--pa-negative); }
46
+ &--neutral { --pa-kpi-accent: var(--pa-neutral); }
47
+ &--up-strong { --pa-kpi-accent: var(--pa-very-positive); }
48
+ }
49
+
50
+ .pa-kpi-hero-main__label {
51
+ font-family: var(--base-font-family-mono);
52
+ font-size: 1.4rem;
53
+ font-weight: 700;
54
+ letter-spacing: 0.1em;
55
+ text-transform: uppercase;
56
+ color: color-mix(in srgb, var(--pa-text-color-1) 60%, transparent);
57
+ }
58
+
59
+ .pa-kpi-hero-main__value {
60
+ display: inline-flex;
61
+ align-items: baseline;
62
+ font-family: var(--base-font-family-mono);
63
+ line-height: 1;
64
+ margin: 0.4rem 0 0.6rem;
65
+ }
66
+ .pa-kpi-hero-main__num {
67
+ /* Container-query-relative: scales with the hero panel's width.
68
+ Floor keeps it readable in narrow page-grid cards. */
69
+ font-size: clamp(4rem, 17cqi, 7rem);
70
+ font-weight: 700;
71
+ letter-spacing: -0.02em;
72
+ color: var(--pa-text-color-1);
73
+ }
74
+ .pa-kpi-hero-main__unit {
75
+ font-size: clamp(1.8rem, 8cqi, 3rem);
76
+ font-weight: 500;
77
+ color: var(--pa-text-secondary);
78
+ margin: 0 0.1rem;
79
+ }
80
+
81
+ /* Meta row: inline delta + period text + target on one baseline. */
82
+ .pa-kpi-hero-main__meta {
83
+ display: grid;
84
+ grid-template-columns: auto minmax(0, 1fr) auto;
85
+ align-items: baseline;
86
+ gap: 0.4rem 1.4rem;
87
+ font-family: var(--base-font-family-mono);
88
+ font-size: 1.3rem;
89
+ }
90
+ .pa-kpi-hero-main__delta {
91
+ display: inline-flex;
92
+ align-items: baseline;
93
+ gap: 0.4rem;
94
+ color: var(--pa-kpi-accent);
95
+ font-weight: 700;
96
+ font-size: 1.5rem;
97
+ line-height: 1.2;
98
+ }
99
+ .pa-kpi-hero-main__period {
100
+ color: color-mix(in srgb, var(--pa-text-color-1) 60%, transparent);
101
+ line-height: 1.3;
102
+ }
103
+ .pa-kpi-hero-main__target {
104
+ color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
105
+ text-align: end;
106
+ }
107
+
108
+ /* Hero sparkline — chart container fills extra vertical space (when the
109
+ rail forces a tall row); SVG inside stays at fixed pixel height so the
110
+ line shape is never distorted along Y. The SVG uses
111
+ preserveAspectRatio="none", so any element that should keep its shape
112
+ needs to live outside that scaling or have fixed pixel dimensions. */
113
+ .pa-kpi-hero-main__chart {
114
+ flex: 1 1 10rem;
115
+ min-height: 10rem;
116
+ margin-top: 0.6rem;
117
+ color: var(--pa-kpi-accent);
118
+ display: flex;
119
+ align-items: flex-end; /* SVG wrap pinned to bottom */
120
+
121
+ polyline {
122
+ fill: none;
123
+ stroke: currentColor;
124
+ stroke-width: var(--pa-chart-trendline-stroke);
125
+ stroke-linecap: round;
126
+ stroke-linejoin: round;
127
+ }
128
+
129
+ polygon {
130
+ fill: currentColor;
131
+ fill-opacity: 0.18;
132
+ stroke: none;
133
+ }
134
+ }
135
+ .pa-kpi-hero-main__chart-svg {
136
+ position: relative;
137
+ display: block;
138
+ width: 100%;
139
+ height: var(--pa-chart-trendline-height);
140
+
141
+ svg {
142
+ display: block;
143
+ width: 100%;
144
+ height: 100%;
145
+ overflow: visible;
146
+ }
147
+ }
148
+
149
+ /* ----- SIDE rail tile (2×2 grid, value spans both rows on right) -------- */
150
+ .pa-kpi-hero-side {
151
+ position: relative;
152
+ padding: 1.2rem 1.4rem;
153
+ border: 1px solid var(--pa-border-color);
154
+ display: grid;
155
+ grid-template-columns: minmax(0, 1fr) auto;
156
+ grid-template-areas:
157
+ "label value"
158
+ "delta value";
159
+ column-gap: 1.2rem;
160
+ row-gap: 0.4rem;
161
+ align-items: center;
162
+
163
+ --pa-kpi-accent: var(--pa-positive);
164
+
165
+ &--positive { --pa-kpi-accent: var(--pa-positive); }
166
+ &--negative { --pa-kpi-accent: var(--pa-negative); }
167
+ &--neutral { --pa-kpi-accent: var(--pa-neutral); }
168
+ &--up-strong { --pa-kpi-accent: var(--pa-very-positive); }
169
+ }
170
+
171
+ .pa-kpi-hero-side__label {
172
+ grid-area: label;
173
+ align-self: end; /* tucks against the vertically-centred value */
174
+ font-family: var(--base-font-family-mono);
175
+ font-size: 1.3rem;
176
+ font-weight: 700;
177
+ letter-spacing: 0.1em;
178
+ text-transform: uppercase;
179
+ color: color-mix(in srgb, var(--pa-text-color-1) 60%, transparent);
180
+ line-height: 1.25;
181
+ }
182
+ .pa-kpi-hero-side__value {
183
+ grid-area: value;
184
+ align-self: center;
185
+ font-family: var(--base-font-family-mono);
186
+ line-height: 1;
187
+ white-space: nowrap;
188
+ display: inline-flex;
189
+ align-items: baseline;
190
+ }
191
+ .pa-kpi-hero-side__num {
192
+ font-size: 2.4rem;
193
+ font-weight: 700;
194
+ letter-spacing: -0.02em;
195
+ color: var(--pa-text-color-1);
196
+ }
197
+ .pa-kpi-hero-side__unit {
198
+ font-size: 1.5rem;
199
+ font-weight: 500;
200
+ color: var(--pa-text-secondary);
201
+ margin: 0 0.35rem;
202
+ }
203
+ .pa-kpi-hero-side__delta {
204
+ grid-area: delta;
205
+ align-self: start;
206
+ font-family: var(--base-font-family-mono);
207
+ font-size: 1.2rem;
208
+ font-weight: 600;
209
+ color: var(--pa-kpi-accent);
210
+ }
@@ -0,0 +1,154 @@
1
+ /* ========================================
2
+ KPI · Numeric strip (densest)
3
+ Tabular "spreadsheet-style" table card with metric/now/prev/Δ%/vs target
4
+ columns — most data per pixel, no chart chrome. Each row is its own
5
+ grid sharing the same column template so cells align across rows
6
+ without needing subgrid. Wide-only by design — no responsive collapse;
7
+ narrow placements should use Comparison gauges instead.
8
+ ======================================== */
9
+ @use '../variables' as *;
10
+
11
+ .pa-kpi-strip__body { padding: 0; }
12
+
13
+ /* ----- Table layout ----------------------------------------------------- */
14
+ .pa-kpi-strip__row,
15
+ .pa-kpi-strip__head-row {
16
+ display: grid;
17
+ grid-template-columns:
18
+ minmax(0, 2fr) /* metric */
19
+ minmax(0, 1.1fr) /* now */
20
+ minmax(0, 1fr) /* prev */
21
+ minmax(0, 1fr) /* delta */
22
+ minmax(0, 1.6fr); /* target (bar + pct stacked) */
23
+ column-gap: 1.6rem;
24
+ align-items: center;
25
+ position: relative;
26
+ padding: 1.1rem 1.6rem;
27
+ }
28
+
29
+ /* Divider between data rows / between head and first row only. */
30
+ .pa-kpi-strip__row + .pa-kpi-strip__row,
31
+ .pa-kpi-strip__head-row + .pa-kpi-strip__row {
32
+ border-top: 1px solid var(--pa-border-color);
33
+ }
34
+
35
+ /* Hover host: subtle row highlight so the cursor-anchored popover has a
36
+ clear "this row" anchor. */
37
+ .pa-kpi-strip__row:hover {
38
+ background: var(--pa-surface-hover);
39
+ }
40
+
41
+ /* ----- Header cells (uppercase mono captions) --------------------------- */
42
+ .pa-kpi-strip__head {
43
+ font-family: var(--base-font-family-mono);
44
+ font-size: 1.1rem;
45
+ font-weight: 700;
46
+ letter-spacing: 0.1em;
47
+ text-transform: uppercase;
48
+ color: var(--pa-text-tertiary);
49
+
50
+ &--num { text-align: end; }
51
+ }
52
+
53
+ /* ----- Metric label ----------------------------------------------------- */
54
+ .pa-kpi-strip__metric {
55
+ font-family: var(--base-font-family-mono);
56
+ font-size: 1.4rem;
57
+ font-weight: 700;
58
+ letter-spacing: 0.08em;
59
+ text-transform: uppercase;
60
+ color: color-mix(in srgb, var(--pa-text-color-1) 78%, transparent);
61
+ line-height: 1.2;
62
+ }
63
+
64
+ /* ----- NOW (focal value) ------------------------------------------------ */
65
+ .pa-kpi-strip__now {
66
+ font-family: var(--base-font-family-mono);
67
+ font-size: 1.8rem;
68
+ font-weight: 700;
69
+ text-align: end;
70
+ display: flex;
71
+ align-items: baseline;
72
+ justify-content: flex-end;
73
+ color: var(--pa-text-color-1);
74
+ white-space: nowrap;
75
+ line-height: 1;
76
+
77
+ .pa-kpi-strip__num { font-weight: 700; }
78
+ .pa-kpi-strip__unit {
79
+ font-size: 1.1rem;
80
+ font-weight: 500;
81
+ color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
82
+ margin: 0 0.2rem;
83
+ }
84
+ }
85
+
86
+ /* ----- PREV (reference data, low contrast) ------------------------------ */
87
+ .pa-kpi-strip__prev {
88
+ font-family: var(--base-font-family-mono);
89
+ font-size: 1.5rem;
90
+ font-weight: 500;
91
+ color: var(--pa-text-tertiary);
92
+ text-align: end;
93
+ white-space: nowrap;
94
+ }
95
+
96
+ /* ----- Δ% (sentiment-coloured) ------------------------------------------ */
97
+ .pa-kpi-strip__delta {
98
+ font-family: var(--base-font-family-mono);
99
+ font-size: 1.5rem;
100
+ font-weight: 700;
101
+ text-align: end;
102
+ white-space: nowrap;
103
+ color: var(--pa-positive);
104
+
105
+ &--positive { color: var(--pa-positive); }
106
+ &--negative { color: var(--pa-negative); }
107
+ &--neutral { color: var(--pa-neutral); }
108
+ &--up-strong { color: var(--pa-very-positive); }
109
+ &--down-strong { color: var(--pa-very-negative); }
110
+ }
111
+
112
+ /* ----- VS TARGET (bar + pct stacked, capped at 100% visual) -------------
113
+ Theme-neutral grey fill — the pct value itself signals overshoot /
114
+ undershoot ("97" vs "108" vs "54") so colour reinforcement isn't
115
+ needed. The bar visually caps at 100% via overflow: hidden so an
116
+ over-target metric reads as "fully filled" rather than overflowing. */
117
+ .pa-kpi-strip__target {
118
+ display: flex;
119
+ flex-direction: column;
120
+ gap: 0.35rem;
121
+ }
122
+ .pa-kpi-strip__bar {
123
+ position: relative;
124
+ height: 0.5rem;
125
+ background: var(--pa-surface-track);
126
+ overflow: hidden;
127
+ border-radius: 0.1rem;
128
+ }
129
+ .pa-kpi-strip__fill {
130
+ position: absolute;
131
+ top: 0;
132
+ left: 0;
133
+ bottom: 0;
134
+ background: color-mix(in srgb, var(--pa-text-color-1) 40%, transparent);
135
+ }
136
+ .pa-kpi-strip__bar-pct {
137
+ font-family: var(--base-font-family-mono);
138
+ font-size: 1.05rem;
139
+ font-weight: 600;
140
+ color: var(--pa-text-secondary);
141
+ align-self: flex-end;
142
+ line-height: 1;
143
+ }
144
+
145
+ /* ----- --no-prev: 4-col layout (metric / now / Δ% / target) -------------
146
+ Useful for narrow placements; markup omits the prev cells. */
147
+ .pa-kpi-strip--no-prev .pa-kpi-strip__row,
148
+ .pa-kpi-strip--no-prev .pa-kpi-strip__head-row {
149
+ grid-template-columns:
150
+ minmax(0, 2fr)
151
+ minmax(0, 1.1fr)
152
+ minmax(0, 1fr)
153
+ minmax(0, 1.6fr);
154
+ }