@keenmate/pure-admin-core 2.7.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keenmate/pure-admin-core",
3
- "version": "2.7.0",
3
+ "version": "2.7.1",
4
4
  "description": "Lightweight, data-focused HTML/CSS admin framework built with PureCSS foundation and comprehensive component system",
5
5
  "style": "dist/css/main.css",
6
6
  "exports": {
@@ -117,5 +117,16 @@
117
117
  // Data Visualization (progress bars, rings, gauges, heatmaps, sparklines)
118
118
  @use 'core-components/data-viz' as *;
119
119
 
120
+ // KPI showcase components — shared chrome + 7 distinct designs.
121
+ // Base must come first; design files use the shared classes from base.
122
+ @use 'core-components/kpi-base' as *;
123
+ @use 'core-components/kpi-terminal' as *;
124
+ @use 'core-components/kpi-sparkline-list' as *;
125
+ @use 'core-components/kpi-comparison-gauges' as *;
126
+ @use 'core-components/kpi-hero-supporting' as *;
127
+ @use 'core-components/kpi-bento' as *;
128
+ @use 'core-components/kpi-numeric-strip' as *;
129
+ @use 'core-components/kpi-editorial-minimal' as *;
130
+
120
131
  // Utility classes and helpers
121
132
  @use 'core-components/utilities' as *;
@@ -0,0 +1,169 @@
1
+ /* ========================================
2
+ KPI · shared base
3
+ Cross-cutting building blocks used by every pa-kpi-* showcase.
4
+ Tokens (--pa-positive, --pa-detail-bg, --pa-chart-trendline-*) live in
5
+ _base-css-variables.scss; this file only defines the shared class
6
+ surface that consumes them.
7
+ ======================================== */
8
+ @use '../variables' as *;
9
+
10
+ /* ----- LIVE indicator (title-bar pulse) ---------------------------------
11
+ Used in every KPI card header — a small mono "LIVE" caption with a
12
+ pulsing green dot. Sits next to the card title. */
13
+ .pa-kpi-live {
14
+ display: inline-flex;
15
+ align-items: center;
16
+ gap: 0.6rem;
17
+ font-family: var(--base-font-family-mono);
18
+ font-size: 1.2rem;
19
+ font-weight: 600;
20
+ letter-spacing: 0.06em;
21
+ color: var(--pa-text-secondary);
22
+
23
+ &__dot {
24
+ width: 0.8rem;
25
+ height: 0.8rem;
26
+ border-radius: 50%;
27
+ background: var(--pa-positive);
28
+ box-shadow: 0 0 6px var(--pa-positive);
29
+ animation: pa-kpi-pulse 1.6s ease-in-out infinite;
30
+ }
31
+ }
32
+
33
+ @keyframes pa-kpi-pulse {
34
+ 50% { opacity: 0.35; }
35
+ }
36
+
37
+ /* ----- Card header row --------------------------------------------------
38
+ Title (h2/h3 by author choice) on one side, controls + LIVE on the other.
39
+ Wraps onto multiple rows on narrow cards. Every showcase uses this shape. */
40
+ .pa-kpi-header {
41
+ display: flex;
42
+ justify-content: space-between;
43
+ align-items: center;
44
+ gap: 1.6rem;
45
+ flex-wrap: wrap;
46
+ }
47
+
48
+ /* ----- Card footer caption ---------------------------------------------
49
+ Two-line mono caption row at the bottom of the card (left: data source,
50
+ right: timestamp). Strong text inside picks up the focal color so things
51
+ like a metric name or count stand out. */
52
+ .pa-kpi-footer {
53
+ display: flex;
54
+ justify-content: space-between;
55
+ align-items: center;
56
+ flex-wrap: wrap;
57
+ gap: 1rem;
58
+ font-family: var(--base-font-family-mono);
59
+ font-size: 1.2rem;
60
+ color: var(--pa-text-secondary);
61
+
62
+ strong {
63
+ color: var(--pa-text-color-1);
64
+ font-weight: 700;
65
+ }
66
+ }
67
+
68
+ /* ----- Sparkline endpoint dot ------------------------------------------
69
+ The SVG circle that marks the end of a sparkline is converted to a CSS
70
+ span at init time — the SVG uses preserveAspectRatio="none" so an
71
+ embedded <circle> renders as an oval at non-square aspect ratios.
72
+ See kpi-showcases.js for the conversion; here we only style the result. */
73
+ .pa-kpi-spark-dot {
74
+ position: absolute;
75
+ width: 6px;
76
+ height: 6px;
77
+ margin: -3px 0 0 -3px; /* centre on the (left, top) anchor */
78
+ border-radius: 50%;
79
+ background: currentColor; /* inherits sentiment color from chart wrapper */
80
+ pointer-events: none;
81
+ }
82
+
83
+ /* Wrapper inserted around an SVG sparkline at init time when its parent
84
+ isn't already a positioned anchor — so the .pa-kpi-spark-dot can be
85
+ absolutely positioned relative to the chart. */
86
+ .pa-kpi-spark-wrap {
87
+ display: block;
88
+ position: relative;
89
+ width: 100%;
90
+ }
91
+
92
+ /* ----- Hover detail popover --------------------------------------------
93
+ Bloomberg-dark by default regardless of host theme (terminal/data-dashboard
94
+ aesthetic). Override the --pa-detail-* tokens at :root or .pa-kpi-detail
95
+ level for a light/theme-aware variant.
96
+
97
+ Positioned by Floating UI anchored to a virtual element at the cursor —
98
+ the JS appends each popover to <body> at init so ancestor overflow:hidden
99
+ doesn't clip it. position: fixed because cursor coords are viewport-
100
+ relative; pointer-events:none so the cursor passes through cleanly and
101
+ tile mouseleave fires reliably. */
102
+ .pa-kpi-detail {
103
+ position: fixed;
104
+ top: 0;
105
+ left: 0;
106
+ visibility: hidden;
107
+ pointer-events: none;
108
+ z-index: 9000;
109
+ min-width: 26rem;
110
+ max-width: 32rem;
111
+ background: var(--pa-detail-bg);
112
+ color: var(--pa-detail-text);
113
+ padding: 1.2rem 1.5rem;
114
+ font-family: var(--base-font-family-mono);
115
+ font-size: 1.25rem;
116
+ line-height: 1.5;
117
+ border-radius: 0.4rem;
118
+ box-shadow: var(--pa-detail-shadow);
119
+
120
+ &[data-show] {
121
+ visibility: visible;
122
+ }
123
+
124
+ &__title {
125
+ text-transform: uppercase;
126
+ letter-spacing: 0.08em;
127
+ font-size: 1.05rem;
128
+ font-weight: 700;
129
+ color: var(--pa-detail-title);
130
+ margin-bottom: 0.6rem;
131
+ }
132
+
133
+ dl {
134
+ display: grid;
135
+ grid-template-columns: 1fr auto;
136
+ gap: 0.35rem 1.6rem;
137
+ margin: 0;
138
+ }
139
+
140
+ dt {
141
+ margin: 0;
142
+ color: var(--pa-detail-row-label);
143
+ font-weight: 400;
144
+ }
145
+
146
+ dd {
147
+ margin: 0;
148
+ text-align: end;
149
+ font-weight: 600;
150
+ font-variant-numeric: tabular-nums;
151
+ }
152
+
153
+ /* Inline sentiment hints for dd content (e.g. <dd>+12% <span class="pos">…</span></dd>) */
154
+ .pos { color: var(--pa-positive); }
155
+ .neg { color: var(--pa-negative); }
156
+ .warn { color: var(--pa-warning); }
157
+ }
158
+
159
+ /* ----- Section heading strip used outside card chrome -------------------
160
+ The 7 showcases each render a "stress-test" section that places tiles
161
+ directly inside .pa-col-* page-grid cells (no card wrapper). The strip
162
+ above those layouts hosts a label or LIVE indicator at the row's end. */
163
+ .pa-kpi-sectionhead {
164
+ display: flex;
165
+ justify-content: flex-end;
166
+ align-items: center;
167
+ gap: 1.6rem;
168
+ margin-bottom: 1.2rem;
169
+ }
@@ -0,0 +1,182 @@
1
+ /* ========================================
2
+ KPI · Bento layout
3
+ Magazine-style asymmetric tile sizing with sparklines as soft background
4
+ fills behind the values. 6 tiles arranged on a 6-col × 3-row grid
5
+ (hero left-half × 2 rows, two stacked right-half × 2 rows, three equal
6
+ tiles bottom row). Tile placement by source order via :nth-child.
7
+ ======================================== */
8
+ @use '../variables' as *;
9
+
10
+ .pa-kpi-bento {
11
+ container-type: inline-size;
12
+ }
13
+ .pa-kpi-bento__body { padding: 1.6rem; }
14
+
15
+ /* ----- Bento grid ------------------------------------------------------- */
16
+ .pa-kpi-bento__grid {
17
+ display: grid;
18
+ grid-template-columns: repeat(6, 1fr);
19
+ grid-template-rows: 12rem 12rem 12rem;
20
+ grid-template-areas:
21
+ "hero hero hero a a a"
22
+ "hero hero hero b b b"
23
+ "c c d d e e";
24
+ gap: 1rem;
25
+
26
+ > :nth-child(1) { grid-area: hero; }
27
+ > :nth-child(2) { grid-area: a; }
28
+ > :nth-child(3) { grid-area: b; }
29
+ > :nth-child(4) { grid-area: c; }
30
+ > :nth-child(5) { grid-area: d; }
31
+ > :nth-child(6) { grid-area: e; }
32
+ }
33
+
34
+ /* Narrow card → stack everything single column. Hero modifier still
35
+ bumps the value font-size so it reads as the headline. */
36
+ @container (max-width: 700px) {
37
+ .pa-kpi-bento__grid {
38
+ grid-template-columns: 1fr;
39
+ grid-template-rows: auto;
40
+ grid-template-areas: none;
41
+ gap: 0.8rem;
42
+
43
+ > :nth-child(n) { grid-area: auto; }
44
+ }
45
+ .pa-kpi-bento-tile { min-height: 10rem; }
46
+ .pa-kpi-bento-tile--hero { min-height: 14rem; }
47
+ }
48
+
49
+ /* ----- Tile (per-KPI bento cell) ---------------------------------------- */
50
+ .pa-kpi-bento-tile {
51
+ /* Per-tile inline-size container so the value font-size scales with
52
+ *this* tile's width via cqi (not the outer card's width). The hero
53
+ and the smaller bottom tiles each scale to their own column. */
54
+ container-type: inline-size;
55
+ position: relative;
56
+ overflow: hidden;
57
+ padding: 1.3rem 1.5rem;
58
+ border: 1px solid var(--pa-border-color);
59
+ background: var(--pa-card-bg);
60
+ display: grid;
61
+ grid-template-columns: minmax(0, 1fr) auto;
62
+ grid-template-rows: auto 1fr auto;
63
+ grid-template-areas:
64
+ "label delta"
65
+ ". . "
66
+ "value value";
67
+ gap: 0.4rem 1rem;
68
+
69
+ /* Sparkline + delta sentiment cascade via currentColor. */
70
+ --pa-kpi-accent: var(--pa-positive);
71
+
72
+ &--positive { --pa-kpi-accent: var(--pa-positive); }
73
+ &--negative { --pa-kpi-accent: var(--pa-negative); }
74
+ &--neutral { --pa-kpi-accent: var(--pa-neutral); }
75
+ &--up-strong { --pa-kpi-accent: var(--pa-very-positive); }
76
+ &--down-strong { --pa-kpi-accent: var(--pa-very-negative); }
77
+
78
+ &--hero {
79
+ padding: 1.6rem 1.8rem;
80
+
81
+ .pa-kpi-bento-tile__num { font-size: clamp(3.6rem, 22cqi, 7rem); }
82
+ .pa-kpi-bento-tile__unit { font-size: clamp(1.6rem, 9cqi, 2.6rem); }
83
+ .pa-kpi-bento-tile__label,
84
+ .pa-kpi-bento-tile__delta { font-size: 1.4rem; }
85
+ .pa-kpi-bento-tile__chart { height: 70%; }
86
+ }
87
+ }
88
+
89
+ /* ----- Top row: label + delta, both layered over the chart -------------- */
90
+ .pa-kpi-bento-tile__label {
91
+ grid-area: label;
92
+ font-family: var(--base-font-family-mono);
93
+ font-size: 1.3rem;
94
+ font-weight: 700;
95
+ letter-spacing: 0.1em;
96
+ text-transform: uppercase;
97
+ color: color-mix(in srgb, var(--pa-text-color-1) 60%, transparent);
98
+ z-index: 1;
99
+ line-height: 1.2;
100
+ }
101
+ .pa-kpi-bento-tile__delta {
102
+ grid-area: delta;
103
+ font-family: var(--base-font-family-mono);
104
+ font-size: 1.3rem;
105
+ font-weight: 700;
106
+ color: var(--pa-kpi-accent);
107
+ z-index: 1;
108
+ align-self: start;
109
+ line-height: 1.2;
110
+ }
111
+
112
+ /* ----- Value (bottom of grid, layered over chart) ----------------------- */
113
+ .pa-kpi-bento-tile__value {
114
+ grid-area: value;
115
+ align-self: end;
116
+ position: relative;
117
+ z-index: 1;
118
+ font-family: var(--base-font-family-mono);
119
+ display: inline-flex;
120
+ align-items: baseline;
121
+ line-height: 1;
122
+ white-space: nowrap;
123
+ }
124
+ .pa-kpi-bento-tile__num {
125
+ font-size: clamp(2rem, 14cqi, 3.2rem);
126
+ font-weight: 700;
127
+ letter-spacing: -0.02em;
128
+ color: var(--pa-text-color-1);
129
+ }
130
+ .pa-kpi-bento-tile__unit {
131
+ font-size: clamp(1.2rem, 7cqi, 1.6rem);
132
+ font-weight: 500;
133
+ color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
134
+ margin: 0 0.3rem;
135
+ }
136
+
137
+ /* ----- Sparkline background ---------------------------------------------
138
+ Absolutely positioned, fills the bottom 65% of the tile. The value sits
139
+ over it (z-index: 1 above). The line/area read through behind the
140
+ digits at lower opacity so the value remains the focal point. SVG is
141
+ nested in a fixed-height wrapper so taller tiles don't distort the line
142
+ shape (preserveAspectRatio="none" would otherwise stretch the Y axis). */
143
+ .pa-kpi-bento-tile__chart {
144
+ position: absolute;
145
+ left: 0;
146
+ right: 0;
147
+ bottom: 0;
148
+ height: 65%;
149
+ pointer-events: none;
150
+ color: var(--pa-kpi-accent);
151
+ z-index: 0;
152
+ display: flex;
153
+ align-items: flex-end;
154
+
155
+ svg {
156
+ display: block;
157
+ width: 100%;
158
+ height: 100%;
159
+ overflow: visible;
160
+ }
161
+
162
+ polyline {
163
+ fill: none;
164
+ stroke: currentColor;
165
+ stroke-opacity: 0.55;
166
+ stroke-width: var(--pa-chart-trendline-stroke);
167
+ stroke-linecap: round;
168
+ stroke-linejoin: round;
169
+ }
170
+
171
+ polygon {
172
+ fill: currentColor;
173
+ fill-opacity: 0.10;
174
+ stroke: none;
175
+ }
176
+ }
177
+ .pa-kpi-bento-tile__chart-svg {
178
+ position: relative;
179
+ display: block;
180
+ width: 100%;
181
+ height: var(--pa-chart-trendline-height);
182
+ }
@@ -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
+ }