@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/dist/css/main.css CHANGED
@@ -16651,6 +16651,1526 @@ code {
16651
16651
  height: 0.3rem;
16652
16652
  }
16653
16653
 
16654
+ /* ========================================
16655
+ KPI · shared base
16656
+ Cross-cutting building blocks used by every pa-kpi-* showcase.
16657
+ Tokens (--pa-positive, --pa-detail-bg, --pa-chart-trendline-*) live in
16658
+ _base-css-variables.scss; this file only defines the shared class
16659
+ surface that consumes them.
16660
+ ======================================== */
16661
+ /* ----- LIVE indicator (title-bar pulse) ---------------------------------
16662
+ Used in every KPI card header — a small mono "LIVE" caption with a
16663
+ pulsing green dot. Sits next to the card title. */
16664
+ .pa-kpi-live {
16665
+ display: inline-flex;
16666
+ align-items: center;
16667
+ gap: 0.6rem;
16668
+ font-family: var(--base-font-family-mono);
16669
+ font-size: 1.2rem;
16670
+ font-weight: 600;
16671
+ letter-spacing: 0.06em;
16672
+ color: var(--pa-text-secondary);
16673
+ }
16674
+ .pa-kpi-live__dot {
16675
+ width: 0.8rem;
16676
+ height: 0.8rem;
16677
+ border-radius: 50%;
16678
+ background: var(--pa-positive);
16679
+ box-shadow: 0 0 6px var(--pa-positive);
16680
+ animation: pa-kpi-pulse 1.6s ease-in-out infinite;
16681
+ }
16682
+
16683
+ @keyframes pa-kpi-pulse {
16684
+ 50% {
16685
+ opacity: 0.35;
16686
+ }
16687
+ }
16688
+ /* ----- Card header row --------------------------------------------------
16689
+ Title (h2/h3 by author choice) on one side, controls + LIVE on the other.
16690
+ Wraps onto multiple rows on narrow cards. Every showcase uses this shape. */
16691
+ .pa-kpi-header {
16692
+ display: flex;
16693
+ justify-content: space-between;
16694
+ align-items: center;
16695
+ gap: 1.6rem;
16696
+ flex-wrap: wrap;
16697
+ }
16698
+
16699
+ /* ----- Card footer caption ---------------------------------------------
16700
+ Two-line mono caption row at the bottom of the card (left: data source,
16701
+ right: timestamp). Strong text inside picks up the focal color so things
16702
+ like a metric name or count stand out. */
16703
+ .pa-kpi-footer {
16704
+ display: flex;
16705
+ justify-content: space-between;
16706
+ align-items: center;
16707
+ flex-wrap: wrap;
16708
+ gap: 1rem;
16709
+ font-family: var(--base-font-family-mono);
16710
+ font-size: 1.2rem;
16711
+ color: var(--pa-text-secondary);
16712
+ }
16713
+ .pa-kpi-footer strong {
16714
+ color: var(--pa-text-color-1);
16715
+ font-weight: 700;
16716
+ }
16717
+
16718
+ /* ----- Sparkline endpoint dot ------------------------------------------
16719
+ The SVG circle that marks the end of a sparkline is converted to a CSS
16720
+ span at init time — the SVG uses preserveAspectRatio="none" so an
16721
+ embedded <circle> renders as an oval at non-square aspect ratios.
16722
+ See kpi-showcases.js for the conversion; here we only style the result. */
16723
+ .pa-kpi-spark-dot {
16724
+ position: absolute;
16725
+ width: 6px;
16726
+ height: 6px;
16727
+ margin: -3px 0 0 -3px; /* centre on the (left, top) anchor */
16728
+ border-radius: 50%;
16729
+ background: currentColor; /* inherits sentiment color from chart wrapper */
16730
+ pointer-events: none;
16731
+ }
16732
+
16733
+ /* Wrapper inserted around an SVG sparkline at init time when its parent
16734
+ isn't already a positioned anchor — so the .pa-kpi-spark-dot can be
16735
+ absolutely positioned relative to the chart. */
16736
+ .pa-kpi-spark-wrap {
16737
+ display: block;
16738
+ position: relative;
16739
+ width: 100%;
16740
+ }
16741
+
16742
+ /* ----- Hover detail popover --------------------------------------------
16743
+ Bloomberg-dark by default regardless of host theme (terminal/data-dashboard
16744
+ aesthetic). Override the --pa-detail-* tokens at :root or .pa-kpi-detail
16745
+ level for a light/theme-aware variant.
16746
+
16747
+ Positioned by Floating UI anchored to a virtual element at the cursor —
16748
+ the JS appends each popover to <body> at init so ancestor overflow:hidden
16749
+ doesn't clip it. position: fixed because cursor coords are viewport-
16750
+ relative; pointer-events:none so the cursor passes through cleanly and
16751
+ tile mouseleave fires reliably. */
16752
+ .pa-kpi-detail {
16753
+ position: fixed;
16754
+ top: 0;
16755
+ left: 0;
16756
+ visibility: hidden;
16757
+ pointer-events: none;
16758
+ z-index: 9000;
16759
+ min-width: 26rem;
16760
+ max-width: 32rem;
16761
+ background: var(--pa-detail-bg);
16762
+ color: var(--pa-detail-text);
16763
+ padding: 1.2rem 1.5rem;
16764
+ font-family: var(--base-font-family-mono);
16765
+ font-size: 1.25rem;
16766
+ line-height: 1.5;
16767
+ border-radius: 0.4rem;
16768
+ box-shadow: var(--pa-detail-shadow);
16769
+ }
16770
+ .pa-kpi-detail[data-show] {
16771
+ visibility: visible;
16772
+ }
16773
+ .pa-kpi-detail__title {
16774
+ text-transform: uppercase;
16775
+ letter-spacing: 0.08em;
16776
+ font-size: 1.05rem;
16777
+ font-weight: 700;
16778
+ color: var(--pa-detail-title);
16779
+ margin-bottom: 0.6rem;
16780
+ }
16781
+ .pa-kpi-detail dl {
16782
+ display: grid;
16783
+ grid-template-columns: 1fr auto;
16784
+ gap: 0.35rem 1.6rem;
16785
+ margin: 0;
16786
+ }
16787
+ .pa-kpi-detail dt {
16788
+ margin: 0;
16789
+ color: var(--pa-detail-row-label);
16790
+ font-weight: 400;
16791
+ }
16792
+ .pa-kpi-detail dd {
16793
+ margin: 0;
16794
+ text-align: end;
16795
+ font-weight: 600;
16796
+ font-variant-numeric: tabular-nums;
16797
+ }
16798
+ .pa-kpi-detail {
16799
+ /* Inline sentiment hints for dd content (e.g. <dd>+12% <span class="pos">…</span></dd>) */
16800
+ }
16801
+ .pa-kpi-detail .pos {
16802
+ color: var(--pa-positive);
16803
+ }
16804
+ .pa-kpi-detail .neg {
16805
+ color: var(--pa-negative);
16806
+ }
16807
+ .pa-kpi-detail .warn {
16808
+ color: var(--pa-warning);
16809
+ }
16810
+
16811
+ /* ----- Section heading strip used outside card chrome -------------------
16812
+ The 7 showcases each render a "stress-test" section that places tiles
16813
+ directly inside .pa-col-* page-grid cells (no card wrapper). The strip
16814
+ above those layouts hosts a label or LIVE indicator at the row's end. */
16815
+ .pa-kpi-sectionhead {
16816
+ display: flex;
16817
+ justify-content: flex-end;
16818
+ align-items: center;
16819
+ gap: 1.6rem;
16820
+ margin-bottom: 1.2rem;
16821
+ }
16822
+
16823
+ /* ========================================
16824
+ KPI · Terminal grid
16825
+ Bloomberg-style dense panel: mono numbers, status pills, inline SVG
16826
+ sparklines, ▲▼ deltas, segmented view-mode toggle (VALUE/Δ%/TREND).
16827
+ Shared chrome (header, live, footer, detail popover, spark-dot) is in
16828
+ _kpi-base.scss.
16829
+ ======================================== */
16830
+ /* ----- View-mode toggle (segmented button group) ------------------------ */
16831
+ .pa-kpi-terminal__controls {
16832
+ display: inline-flex;
16833
+ align-items: center;
16834
+ gap: 1.6rem;
16835
+ }
16836
+
16837
+ .pa-kpi-terminal__viewtoggle {
16838
+ display: inline-flex;
16839
+ border: 1px solid var(--pa-border-color);
16840
+ border-radius: 0.4rem;
16841
+ overflow: hidden;
16842
+ }
16843
+
16844
+ .pa-kpi-terminal__viewbtn {
16845
+ background: transparent;
16846
+ border: 0;
16847
+ color: var(--pa-text-color-2);
16848
+ padding: 0.4rem 1.1rem;
16849
+ font-family: var(--base-font-family-mono);
16850
+ font-size: 1.1rem;
16851
+ font-weight: 600;
16852
+ letter-spacing: 0.06em;
16853
+ cursor: pointer;
16854
+ transition: background-color 0.1s ease-out, color 0.1s ease-out;
16855
+ }
16856
+ .pa-kpi-terminal__viewbtn:hover {
16857
+ color: var(--pa-text-color-1);
16858
+ }
16859
+ .pa-kpi-terminal__viewbtn.is-active {
16860
+ background: var(--pa-text-color-1);
16861
+ color: var(--pa-card-bg);
16862
+ }
16863
+
16864
+ /* ----- Grid + tile borders ----------------------------------------------
16865
+ Hairline 1px borders between tiles, no gap. Last-row/last-column
16866
+ borders suppressed via :nth-last-child / :nth-child selectors. */
16867
+ .pa-kpi-terminal__body {
16868
+ padding: 0;
16869
+ }
16870
+
16871
+ .pa-kpi-terminal__grid {
16872
+ display: grid;
16873
+ grid-template-columns: repeat(2, 1fr);
16874
+ }
16875
+ .pa-kpi-terminal__grid .pa-kpi-tile {
16876
+ border-bottom: 1px solid var(--pa-border-color);
16877
+ border-right: 1px solid var(--pa-border-color);
16878
+ }
16879
+
16880
+ .pa-kpi-terminal__grid--2col .pa-kpi-tile:nth-child(2n) {
16881
+ border-right: 0;
16882
+ }
16883
+ .pa-kpi-terminal__grid--2col .pa-kpi-tile:nth-last-child(-n+2) {
16884
+ border-bottom: 0;
16885
+ }
16886
+
16887
+ /* ----- Tile (per-KPI panel) --------------------------------------------- */
16888
+ .pa-kpi-tile {
16889
+ position: relative;
16890
+ padding: 1.4rem 1.8rem 1.6rem;
16891
+ min-height: 16rem;
16892
+ display: flex;
16893
+ flex-direction: column;
16894
+ /* Standalone modifier: tile lives directly inside a .pa-col-* (no
16895
+ neighbour cells in a parent grid) — gets a full border + card bg so
16896
+ it doesn't look orphaned. */
16897
+ }
16898
+ .pa-kpi-tile--standalone {
16899
+ background: var(--pa-card-bg);
16900
+ border: 1px solid var(--pa-border-color);
16901
+ margin-bottom: 1.2rem;
16902
+ }
16903
+ .pa-kpi-tile--standalone:last-child {
16904
+ margin-bottom: 0;
16905
+ }
16906
+
16907
+ /* ----- Tile head: ID · period + status pill ----------------------------- */
16908
+ .pa-kpi-tile__head {
16909
+ display: flex;
16910
+ justify-content: space-between;
16911
+ align-items: center;
16912
+ font-family: var(--base-font-family-mono);
16913
+ font-size: 1.3rem;
16914
+ letter-spacing: 0.04em;
16915
+ margin-bottom: 0.3rem;
16916
+ }
16917
+
16918
+ .pa-kpi-tile__id {
16919
+ color: var(--pa-text-tertiary);
16920
+ font-weight: 600;
16921
+ }
16922
+
16923
+ .pa-kpi-tile__status {
16924
+ font-family: var(--base-font-family-mono);
16925
+ font-size: 1.2rem;
16926
+ font-weight: 700;
16927
+ letter-spacing: 0.08em;
16928
+ padding: 0.3rem 0.9rem;
16929
+ line-height: 1.3;
16930
+ }
16931
+ .pa-kpi-tile__status--warn {
16932
+ background: var(--pa-warning-bg);
16933
+ color: var(--pa-btn-warning-text);
16934
+ }
16935
+ .pa-kpi-tile__status {
16936
+ /* GOOD is the "default" state — text-only, no chrome */
16937
+ }
16938
+ .pa-kpi-tile__status--good {
16939
+ background: transparent;
16940
+ color: var(--pa-text-color-1);
16941
+ padding: 0.2rem 0;
16942
+ }
16943
+ .pa-kpi-tile__status--neutral {
16944
+ background: color-mix(in srgb, var(--pa-text-color-2) 25%, transparent);
16945
+ color: var(--pa-text-color-1);
16946
+ }
16947
+
16948
+ /* ----- Label ------------------------------------------------------------ */
16949
+ .pa-kpi-tile__label {
16950
+ font-family: var(--base-font-family-mono);
16951
+ font-size: 1.4rem;
16952
+ font-weight: 700;
16953
+ letter-spacing: 0.1em;
16954
+ text-transform: uppercase;
16955
+ color: var(--pa-text-strong);
16956
+ margin-bottom: 0.8rem;
16957
+ }
16958
+
16959
+ /* ----- Big value with 3-mode swap --------------------------------------
16960
+ Author renders three .pa-kpi-tile__value siblings (data-mode="value" /
16961
+ "delta" / "trend"); the active mode is selected via the
16962
+ .pa-kpi-terminal[data-view="X"] attribute. JS toggles that attribute
16963
+ when the segmented control is clicked. */
16964
+ .pa-kpi-tile__values {
16965
+ margin-bottom: 0.4rem;
16966
+ }
16967
+
16968
+ .pa-kpi-tile__value {
16969
+ display: none;
16970
+ align-items: baseline;
16971
+ gap: 0.3rem;
16972
+ font-family: var(--base-font-family-mono);
16973
+ line-height: 1;
16974
+ }
16975
+ .pa-kpi-tile__value--very-positive .pa-kpi-tile__num {
16976
+ color: var(--pa-very-positive);
16977
+ }
16978
+ .pa-kpi-tile__value--positive .pa-kpi-tile__num {
16979
+ color: var(--pa-positive);
16980
+ }
16981
+ .pa-kpi-tile__value--neutral .pa-kpi-tile__num {
16982
+ color: var(--pa-neutral);
16983
+ }
16984
+ .pa-kpi-tile__value--negative .pa-kpi-tile__num {
16985
+ color: var(--pa-negative);
16986
+ }
16987
+ .pa-kpi-tile__value--very-negative .pa-kpi-tile__num {
16988
+ color: var(--pa-very-negative);
16989
+ }
16990
+
16991
+ .pa-kpi-terminal[data-view=value] .pa-kpi-tile__value[data-mode=value],
16992
+ .pa-kpi-terminal[data-view=delta] .pa-kpi-tile__value[data-mode=delta],
16993
+ .pa-kpi-terminal[data-view=trend] .pa-kpi-tile__value[data-mode=trend] {
16994
+ display: inline-flex;
16995
+ }
16996
+
16997
+ .pa-kpi-tile__num {
16998
+ font-size: 3.8rem;
16999
+ font-weight: 700;
17000
+ letter-spacing: -0.02em;
17001
+ color: var(--pa-text-color-1);
17002
+ }
17003
+
17004
+ .pa-kpi-tile__unit {
17005
+ font-size: 1.6rem;
17006
+ font-weight: 500;
17007
+ color: var(--pa-text-secondary);
17008
+ }
17009
+
17010
+ /* ----- Previous-value + delta row -------------------------------------- */
17011
+ .pa-kpi-tile__prev {
17012
+ display: flex;
17013
+ justify-content: space-between;
17014
+ align-items: center;
17015
+ font-family: var(--base-font-family-mono);
17016
+ font-size: 1.3rem;
17017
+ margin-top: auto; /* push to bottom of flex column */
17018
+ margin-bottom: 0.4rem;
17019
+ color: var(--pa-text-tertiary);
17020
+ }
17021
+
17022
+ .pa-kpi-tile__delta--very-positive {
17023
+ color: var(--pa-very-positive);
17024
+ }
17025
+ .pa-kpi-tile__delta--positive {
17026
+ color: var(--pa-positive);
17027
+ }
17028
+ .pa-kpi-tile__delta--neutral {
17029
+ color: var(--pa-neutral);
17030
+ }
17031
+ .pa-kpi-tile__delta--negative {
17032
+ color: var(--pa-negative);
17033
+ }
17034
+ .pa-kpi-tile__delta--very-negative {
17035
+ color: var(--pa-very-negative);
17036
+ }
17037
+
17038
+ /* ----- Sparkline -------------------------------------------------------- */
17039
+ .pa-kpi-tile__spark {
17040
+ display: block;
17041
+ width: 100%;
17042
+ height: var(--pa-chart-trendline-height);
17043
+ overflow: visible;
17044
+ }
17045
+ .pa-kpi-tile__spark polyline {
17046
+ fill: none;
17047
+ stroke: currentColor;
17048
+ stroke-width: var(--pa-chart-trendline-stroke);
17049
+ stroke-linecap: round;
17050
+ stroke-linejoin: round;
17051
+ }
17052
+
17053
+ /* Sentiment-coloured sparkline. Class names use up/down wording but pick
17054
+ colours from the sentiment-of-the-change axis, not line shape — so an
17055
+ error-rate metric whose line is falling but the change is good uses
17056
+ --up. See showcase docs for the rationale. Colour is set on both the
17057
+ SVG and the JS-inserted .pa-kpi-spark-wrap so currentColor resolves
17058
+ for the SVG content (line) and the HTML dot (inside the wrap). */
17059
+ .pa-kpi-tile--up-strong .pa-kpi-tile__spark, .pa-kpi-tile--up-strong .pa-kpi-spark-wrap {
17060
+ color: var(--pa-very-positive);
17061
+ }
17062
+ .pa-kpi-tile--up .pa-kpi-tile__spark, .pa-kpi-tile--up .pa-kpi-spark-wrap {
17063
+ color: var(--pa-positive);
17064
+ }
17065
+ .pa-kpi-tile--flat .pa-kpi-tile__spark, .pa-kpi-tile--flat .pa-kpi-spark-wrap {
17066
+ color: var(--pa-neutral);
17067
+ }
17068
+ .pa-kpi-tile--down .pa-kpi-tile__spark, .pa-kpi-tile--down .pa-kpi-spark-wrap {
17069
+ color: var(--pa-negative);
17070
+ }
17071
+ .pa-kpi-tile--down-strong .pa-kpi-tile__spark, .pa-kpi-tile--down-strong .pa-kpi-spark-wrap {
17072
+ color: var(--pa-very-negative);
17073
+ }
17074
+
17075
+ /* ========================================
17076
+ KPI · Sparkline list
17077
+ Each KPI is one row: label · sparkline · value · Δ%. Built for vertical
17078
+ scanning rather than per-tile depth — no view-mode toggle, no status
17079
+ pills. Container queries collapse 4-col → 2-row → 3-row as the card
17080
+ narrows.
17081
+ ======================================== */
17082
+ /* Card is a query container so rows react to *card* width, not viewport. */
17083
+ .pa-kpi-spark-list {
17084
+ container-type: inline-size;
17085
+ }
17086
+
17087
+ .pa-kpi-spark-list__body {
17088
+ padding: 0;
17089
+ }
17090
+
17091
+ /* ----- Row: wide 4-column grid (label · chart · value · delta) ---------- */
17092
+ .pa-kpi-spark-row {
17093
+ display: grid;
17094
+ grid-template-columns: minmax(14rem, 28%) minmax(10rem, 1fr) minmax(8rem, 18%) minmax(7rem, 12%);
17095
+ align-items: center;
17096
+ gap: 1.6rem;
17097
+ padding: 1.4rem 2rem;
17098
+ border-bottom: 1px solid var(--pa-border-color);
17099
+ }
17100
+ .pa-kpi-spark-row:last-child {
17101
+ border-bottom: 0;
17102
+ }
17103
+
17104
+ /* Mid-narrow card (1×3 page-grid + 45% column): stack to 2 rows.
17105
+ Label/value/delta on top, full-width chart below. Default order is
17106
+ value-above-chart; use .pa-kpi-spark-list--chart-first to flip. */
17107
+ @container (max-width: 640px) {
17108
+ .pa-kpi-spark-row {
17109
+ grid-template-columns: minmax(0, 1fr) auto auto;
17110
+ grid-template-rows: auto auto;
17111
+ grid-template-areas: "label value delta" "chart chart chart";
17112
+ column-gap: 1.2rem;
17113
+ row-gap: 0.4rem;
17114
+ align-items: baseline;
17115
+ padding: 1.2rem 1.6rem;
17116
+ }
17117
+ .pa-kpi-spark-row__label {
17118
+ grid-area: label;
17119
+ align-self: center;
17120
+ font-size: 1.25rem;
17121
+ }
17122
+ .pa-kpi-spark-row__value {
17123
+ grid-area: value;
17124
+ }
17125
+ .pa-kpi-spark-row__delta {
17126
+ grid-area: delta;
17127
+ font-size: 1.25rem;
17128
+ }
17129
+ .pa-kpi-spark-row__chart {
17130
+ grid-area: chart;
17131
+ }
17132
+ .pa-kpi-spark-row__num {
17133
+ font-size: 2rem;
17134
+ }
17135
+ /* --chart-first: rotates the canonical L→R order 90°. Label on top,
17136
+ chart in the middle, value+delta side-by-side at the bottom. */
17137
+ .pa-kpi-spark-list--chart-first .pa-kpi-spark-row {
17138
+ grid-template-columns: 1fr auto;
17139
+ grid-template-rows: auto auto auto;
17140
+ grid-template-areas: "label label" "chart chart" "value delta";
17141
+ row-gap: 0.5rem;
17142
+ column-gap: 1rem;
17143
+ padding: 1.2rem 1.6rem;
17144
+ }
17145
+ .pa-kpi-spark-list--chart-first .pa-kpi-spark-row__label {
17146
+ align-self: start;
17147
+ }
17148
+ .pa-kpi-spark-list--chart-first .pa-kpi-spark-row__value {
17149
+ text-align: start;
17150
+ align-self: baseline;
17151
+ }
17152
+ .pa-kpi-spark-list--chart-first .pa-kpi-spark-row__delta {
17153
+ align-self: baseline;
17154
+ }
17155
+ }
17156
+ /* Very narrow card (~280–360px, 25% page-grid stress test): force the
17157
+ chart-first 3-row layout regardless of modifier. */
17158
+ @container (max-width: 360px) {
17159
+ .pa-kpi-spark-row {
17160
+ grid-template-columns: 1fr auto;
17161
+ grid-template-rows: auto auto auto;
17162
+ grid-template-areas: "label label" "chart chart" "value delta";
17163
+ row-gap: 0.5rem;
17164
+ column-gap: 1rem;
17165
+ padding: 1.2rem 1.4rem;
17166
+ }
17167
+ .pa-kpi-spark-row__label {
17168
+ grid-area: label;
17169
+ align-self: start;
17170
+ }
17171
+ .pa-kpi-spark-row__chart {
17172
+ grid-area: chart;
17173
+ }
17174
+ .pa-kpi-spark-row__value {
17175
+ grid-area: value;
17176
+ text-align: start;
17177
+ align-self: baseline;
17178
+ }
17179
+ .pa-kpi-spark-row__delta {
17180
+ grid-area: delta;
17181
+ align-self: baseline;
17182
+ }
17183
+ }
17184
+ /* ----- Cell typography -------------------------------------------------- */
17185
+ .pa-kpi-spark-row__label {
17186
+ font-family: var(--base-font-family-mono);
17187
+ font-size: 1.4rem;
17188
+ font-weight: 700;
17189
+ letter-spacing: 0.1em;
17190
+ text-transform: uppercase;
17191
+ color: color-mix(in srgb, var(--pa-text-color-1) 60%, transparent);
17192
+ }
17193
+
17194
+ /* Sparkline cell — line + filled area + trailing dot (the dot is rendered
17195
+ as a CSS span by the JS init pass; see kpi-showcases.js). */
17196
+ .pa-kpi-spark-row__chart {
17197
+ position: relative; /* anchor for .pa-kpi-spark-dot */
17198
+ }
17199
+ .pa-kpi-spark-row__chart svg {
17200
+ display: block;
17201
+ width: 100%;
17202
+ height: var(--pa-chart-trendline-height);
17203
+ overflow: visible;
17204
+ }
17205
+ .pa-kpi-spark-row__chart polyline {
17206
+ fill: none;
17207
+ stroke: currentColor;
17208
+ stroke-width: var(--pa-chart-trendline-stroke);
17209
+ stroke-linecap: round;
17210
+ stroke-linejoin: round;
17211
+ }
17212
+ .pa-kpi-spark-row__chart polygon {
17213
+ fill: currentColor;
17214
+ fill-opacity: 0.18; /* same hue as line, soft area shading */
17215
+ stroke: none;
17216
+ }
17217
+
17218
+ /* Sentiment-coloured sparkline. Color is set on the chart wrapper so
17219
+ currentColor resolves for both the SVG content (line/area) and the
17220
+ dot inside the wrapper. */
17221
+ .pa-kpi-spark-row--up-strong .pa-kpi-spark-row__chart {
17222
+ color: var(--pa-very-positive);
17223
+ }
17224
+ .pa-kpi-spark-row--up .pa-kpi-spark-row__chart {
17225
+ color: var(--pa-positive);
17226
+ }
17227
+ .pa-kpi-spark-row--flat .pa-kpi-spark-row__chart {
17228
+ color: var(--pa-neutral);
17229
+ }
17230
+ .pa-kpi-spark-row--down .pa-kpi-spark-row__chart {
17231
+ color: var(--pa-negative);
17232
+ }
17233
+ .pa-kpi-spark-row--down-strong .pa-kpi-spark-row__chart {
17234
+ color: var(--pa-very-negative);
17235
+ }
17236
+
17237
+ /* ----- Value (focal white number + muted unit) ------------------------- */
17238
+ .pa-kpi-spark-row__value {
17239
+ font-family: var(--base-font-family-mono);
17240
+ text-align: end;
17241
+ line-height: 1;
17242
+ }
17243
+
17244
+ .pa-kpi-spark-row__num {
17245
+ font-size: 2.6rem;
17246
+ font-weight: 700;
17247
+ letter-spacing: -0.02em;
17248
+ color: var(--pa-text-color-1);
17249
+ }
17250
+
17251
+ .pa-kpi-spark-row__unit {
17252
+ font-size: 1.3rem;
17253
+ font-weight: 500;
17254
+ color: var(--pa-text-secondary);
17255
+ margin-left: 0.2rem;
17256
+ }
17257
+
17258
+ /* ----- Delta (sentiment-coloured) -------------------------------------- */
17259
+ .pa-kpi-spark-row__delta {
17260
+ font-family: var(--base-font-family-mono);
17261
+ font-size: 1.4rem;
17262
+ font-weight: 600;
17263
+ text-align: end;
17264
+ font-variant-numeric: tabular-nums;
17265
+ }
17266
+ .pa-kpi-spark-row__delta--very-positive {
17267
+ color: var(--pa-very-positive);
17268
+ }
17269
+ .pa-kpi-spark-row__delta--positive {
17270
+ color: var(--pa-positive);
17271
+ }
17272
+ .pa-kpi-spark-row__delta--neutral {
17273
+ color: var(--pa-neutral);
17274
+ }
17275
+ .pa-kpi-spark-row__delta--negative {
17276
+ color: var(--pa-negative);
17277
+ }
17278
+ .pa-kpi-spark-row__delta--very-negative {
17279
+ color: var(--pa-very-negative);
17280
+ }
17281
+
17282
+ /* ========================================
17283
+ KPI · Comparison gauges
17284
+ Goal-oriented progress bars. Each KPI shows label · value, a bar with a
17285
+ target tick, and a 0 · tgt scale below. Bar fill = value/target * 100%,
17286
+ capped visually so overshoots are signalled by colour, not overflow.
17287
+ ======================================== */
17288
+ .pa-kpi-gauge-list {
17289
+ container-type: inline-size;
17290
+ }
17291
+
17292
+ .pa-kpi-gauge-list__body {
17293
+ padding: 0;
17294
+ }
17295
+
17296
+ /* 2-column internal grid by default; collapses to 1-col when card narrows. */
17297
+ .pa-kpi-gauge-list__grid {
17298
+ display: grid;
17299
+ grid-template-columns: repeat(2, 1fr);
17300
+ }
17301
+
17302
+ @container (max-width: 600px) {
17303
+ .pa-kpi-gauge-list__grid {
17304
+ grid-template-columns: 1fr;
17305
+ }
17306
+ }
17307
+ /* ----- Gauge tile (label/value head · bar · 0/tgt scale) ---------------- */
17308
+ .pa-kpi-gauge {
17309
+ position: relative;
17310
+ padding: 1.6rem 2rem;
17311
+ border-bottom: 1px solid var(--pa-border-color);
17312
+ border-right: 1px solid var(--pa-border-color);
17313
+ /* Per-tile bar colour cascade — modifiers below set the var, the fill
17314
+ reads it. Cleaner than per-modifier-per-element rules; host apps can
17315
+ override at the tile level via inline style="--pa-kpi-bar-color: …". */
17316
+ --pa-kpi-bar-color: var(--pa-positive);
17317
+ }
17318
+ .pa-kpi-gauge:nth-child(2n) {
17319
+ border-right: 0;
17320
+ }
17321
+ .pa-kpi-gauge:nth-last-child(-n+2) {
17322
+ border-bottom: 0;
17323
+ }
17324
+ .pa-kpi-gauge--positive {
17325
+ --pa-kpi-bar-color: var(--pa-positive);
17326
+ }
17327
+ .pa-kpi-gauge--warning {
17328
+ --pa-kpi-bar-color: var(--pa-warning);
17329
+ }
17330
+ .pa-kpi-gauge--negative {
17331
+ --pa-kpi-bar-color: var(--pa-negative);
17332
+ }
17333
+ .pa-kpi-gauge--neutral {
17334
+ --pa-kpi-bar-color: color-mix(in srgb, var(--pa-text-color-1) 70%, transparent);
17335
+ }
17336
+
17337
+ @container (max-width: 600px) {
17338
+ .pa-kpi-gauge {
17339
+ border-right: 0;
17340
+ }
17341
+ .pa-kpi-gauge:nth-last-child(-n+2) {
17342
+ border-bottom: 1px solid var(--pa-border-color);
17343
+ }
17344
+ .pa-kpi-gauge:last-child {
17345
+ border-bottom: 0;
17346
+ }
17347
+ }
17348
+ /* ----- Head: label left, value right (baseline-aligned) ----------------- */
17349
+ .pa-kpi-gauge__head {
17350
+ display: flex;
17351
+ justify-content: space-between;
17352
+ align-items: baseline;
17353
+ gap: 1.2rem;
17354
+ margin-bottom: 0.8rem;
17355
+ }
17356
+
17357
+ .pa-kpi-gauge__label {
17358
+ font-family: var(--base-font-family-mono);
17359
+ font-size: 1.4rem;
17360
+ font-weight: 700;
17361
+ letter-spacing: 0.1em;
17362
+ text-transform: uppercase;
17363
+ color: color-mix(in srgb, var(--pa-text-color-1) 60%, transparent);
17364
+ }
17365
+
17366
+ .pa-kpi-gauge__value {
17367
+ font-family: var(--base-font-family-mono);
17368
+ line-height: 1;
17369
+ }
17370
+
17371
+ .pa-kpi-gauge__num {
17372
+ font-size: 2.6rem;
17373
+ font-weight: 700;
17374
+ letter-spacing: -0.02em;
17375
+ color: var(--pa-text-color-1);
17376
+ }
17377
+
17378
+ .pa-kpi-gauge__unit {
17379
+ font-size: 1.3rem;
17380
+ font-weight: 500;
17381
+ color: var(--pa-text-secondary);
17382
+ margin-left: 0.2rem;
17383
+ }
17384
+
17385
+ /* ----- Bar (track + sentiment-coloured fill + target tick) -------------- */
17386
+ .pa-kpi-gauge__bar {
17387
+ /* Author-controlled tick position (default 100% — the target sits at the
17388
+ right edge of the bar's "0 → target" scale). Override per-tile via
17389
+ style="--pa-kpi-gauge-tick-pos: 80%" if the bar represents a wider
17390
+ scale where the target sits inside the bar. */
17391
+ --pa-kpi-gauge-tick-pos: 100%;
17392
+ --pa-kpi-gauge-tick-color: var(--pa-text-color-1);
17393
+ position: relative;
17394
+ height: 0.7rem;
17395
+ background: var(--pa-surface-track);
17396
+ margin-bottom: 0.5rem;
17397
+ overflow: visible; /* tick can sit just outside the track */
17398
+ /* Target tick — small bar slightly taller than the track. Centred on
17399
+ --pa-kpi-gauge-tick-pos via the negative margin-left (matches half
17400
+ the tick width). Full opacity so it stays readable on dark themes
17401
+ against bright orange/green fills. */
17402
+ }
17403
+ .pa-kpi-gauge__bar::after {
17404
+ content: "";
17405
+ position: absolute;
17406
+ left: var(--pa-kpi-gauge-tick-pos);
17407
+ margin-left: -0.15rem;
17408
+ top: -0.2rem;
17409
+ bottom: -0.2rem;
17410
+ width: 0.3rem;
17411
+ background: var(--pa-kpi-gauge-tick-color);
17412
+ }
17413
+
17414
+ .pa-kpi-gauge__fill {
17415
+ height: 100%;
17416
+ background: var(--pa-kpi-bar-color);
17417
+ /* Width set inline per tile via style="width: X%". */
17418
+ transition: width 0.3s ease;
17419
+ }
17420
+
17421
+ /* ----- Scale row (0 left · tgt XYZ right) ------------------------------- */
17422
+ .pa-kpi-gauge__scale {
17423
+ display: flex;
17424
+ justify-content: space-between;
17425
+ font-family: var(--base-font-family-mono);
17426
+ font-size: 1.2rem;
17427
+ color: var(--pa-text-tertiary);
17428
+ }
17429
+
17430
+ /* ========================================
17431
+ KPI · Hero + supporting
17432
+ Marketing/exec dashboard pattern: one headline metric on the left (huge
17433
+ container-query-relative value, inline meta row, big filled-area
17434
+ sparkline), and a vertical rail of compact supporting tiles on the
17435
+ right. Container query collapses to single column on narrow cards.
17436
+ ======================================== */
17437
+ .pa-kpi-hero-list {
17438
+ container-type: inline-size;
17439
+ }
17440
+
17441
+ .pa-kpi-hero-list__body {
17442
+ padding: 1.6rem;
17443
+ }
17444
+
17445
+ /* ----- Layout: hero left, rail right ------------------------------------ */
17446
+ .pa-kpi-hero-list__layout {
17447
+ display: grid;
17448
+ grid-template-columns: 1fr 1fr;
17449
+ gap: 1.4rem;
17450
+ }
17451
+
17452
+ @container (max-width: 700px) {
17453
+ .pa-kpi-hero-list__layout {
17454
+ grid-template-columns: 1fr;
17455
+ }
17456
+ }
17457
+ .pa-kpi-hero-list__rail {
17458
+ display: flex;
17459
+ flex-direction: column;
17460
+ gap: 1rem;
17461
+ }
17462
+
17463
+ /* ----- HERO panel (label · big value · meta row · sparkline) ------------ */
17464
+ .pa-kpi-hero-main {
17465
+ position: relative;
17466
+ padding: 1.8rem 2rem;
17467
+ border: 1px solid var(--pa-border-color);
17468
+ display: flex;
17469
+ flex-direction: column;
17470
+ gap: 0.6rem;
17471
+ /* Sentiment cascade — chart and delta inherit via currentColor. */
17472
+ --pa-kpi-accent: var(--pa-positive);
17473
+ }
17474
+ .pa-kpi-hero-main--positive {
17475
+ --pa-kpi-accent: var(--pa-positive);
17476
+ }
17477
+ .pa-kpi-hero-main--negative {
17478
+ --pa-kpi-accent: var(--pa-negative);
17479
+ }
17480
+ .pa-kpi-hero-main--neutral {
17481
+ --pa-kpi-accent: var(--pa-neutral);
17482
+ }
17483
+ .pa-kpi-hero-main--up-strong {
17484
+ --pa-kpi-accent: var(--pa-very-positive);
17485
+ }
17486
+
17487
+ .pa-kpi-hero-main__label {
17488
+ font-family: var(--base-font-family-mono);
17489
+ font-size: 1.4rem;
17490
+ font-weight: 700;
17491
+ letter-spacing: 0.1em;
17492
+ text-transform: uppercase;
17493
+ color: color-mix(in srgb, var(--pa-text-color-1) 60%, transparent);
17494
+ }
17495
+
17496
+ .pa-kpi-hero-main__value {
17497
+ display: inline-flex;
17498
+ align-items: baseline;
17499
+ font-family: var(--base-font-family-mono);
17500
+ line-height: 1;
17501
+ margin: 0.4rem 0 0.6rem;
17502
+ }
17503
+
17504
+ .pa-kpi-hero-main__num {
17505
+ /* Container-query-relative: scales with the hero panel's width.
17506
+ Floor keeps it readable in narrow page-grid cards. */
17507
+ font-size: clamp(4rem, 17cqi, 7rem);
17508
+ font-weight: 700;
17509
+ letter-spacing: -0.02em;
17510
+ color: var(--pa-text-color-1);
17511
+ }
17512
+
17513
+ .pa-kpi-hero-main__unit {
17514
+ font-size: clamp(1.8rem, 8cqi, 3rem);
17515
+ font-weight: 500;
17516
+ color: var(--pa-text-secondary);
17517
+ margin: 0 0.1rem;
17518
+ }
17519
+
17520
+ /* Meta row: inline delta + period text + target on one baseline. */
17521
+ .pa-kpi-hero-main__meta {
17522
+ display: grid;
17523
+ grid-template-columns: auto minmax(0, 1fr) auto;
17524
+ align-items: baseline;
17525
+ gap: 0.4rem 1.4rem;
17526
+ font-family: var(--base-font-family-mono);
17527
+ font-size: 1.3rem;
17528
+ }
17529
+
17530
+ .pa-kpi-hero-main__delta {
17531
+ display: inline-flex;
17532
+ align-items: baseline;
17533
+ gap: 0.4rem;
17534
+ color: var(--pa-kpi-accent);
17535
+ font-weight: 700;
17536
+ font-size: 1.5rem;
17537
+ line-height: 1.2;
17538
+ }
17539
+
17540
+ .pa-kpi-hero-main__period {
17541
+ color: color-mix(in srgb, var(--pa-text-color-1) 60%, transparent);
17542
+ line-height: 1.3;
17543
+ }
17544
+
17545
+ .pa-kpi-hero-main__target {
17546
+ color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
17547
+ text-align: end;
17548
+ }
17549
+
17550
+ /* Hero sparkline — chart container fills extra vertical space (when the
17551
+ rail forces a tall row); SVG inside stays at fixed pixel height so the
17552
+ line shape is never distorted along Y. The SVG uses
17553
+ preserveAspectRatio="none", so any element that should keep its shape
17554
+ needs to live outside that scaling or have fixed pixel dimensions. */
17555
+ .pa-kpi-hero-main__chart {
17556
+ flex: 1 1 10rem;
17557
+ min-height: 10rem;
17558
+ margin-top: 0.6rem;
17559
+ color: var(--pa-kpi-accent);
17560
+ display: flex;
17561
+ align-items: flex-end; /* SVG wrap pinned to bottom */
17562
+ }
17563
+ .pa-kpi-hero-main__chart polyline {
17564
+ fill: none;
17565
+ stroke: currentColor;
17566
+ stroke-width: var(--pa-chart-trendline-stroke);
17567
+ stroke-linecap: round;
17568
+ stroke-linejoin: round;
17569
+ }
17570
+ .pa-kpi-hero-main__chart polygon {
17571
+ fill: currentColor;
17572
+ fill-opacity: 0.18;
17573
+ stroke: none;
17574
+ }
17575
+
17576
+ .pa-kpi-hero-main__chart-svg {
17577
+ position: relative;
17578
+ display: block;
17579
+ width: 100%;
17580
+ height: var(--pa-chart-trendline-height);
17581
+ }
17582
+ .pa-kpi-hero-main__chart-svg svg {
17583
+ display: block;
17584
+ width: 100%;
17585
+ height: 100%;
17586
+ overflow: visible;
17587
+ }
17588
+
17589
+ /* ----- SIDE rail tile (2×2 grid, value spans both rows on right) -------- */
17590
+ .pa-kpi-hero-side {
17591
+ position: relative;
17592
+ padding: 1.2rem 1.4rem;
17593
+ border: 1px solid var(--pa-border-color);
17594
+ display: grid;
17595
+ grid-template-columns: minmax(0, 1fr) auto;
17596
+ grid-template-areas: "label value" "delta value";
17597
+ column-gap: 1.2rem;
17598
+ row-gap: 0.4rem;
17599
+ align-items: center;
17600
+ --pa-kpi-accent: var(--pa-positive);
17601
+ }
17602
+ .pa-kpi-hero-side--positive {
17603
+ --pa-kpi-accent: var(--pa-positive);
17604
+ }
17605
+ .pa-kpi-hero-side--negative {
17606
+ --pa-kpi-accent: var(--pa-negative);
17607
+ }
17608
+ .pa-kpi-hero-side--neutral {
17609
+ --pa-kpi-accent: var(--pa-neutral);
17610
+ }
17611
+ .pa-kpi-hero-side--up-strong {
17612
+ --pa-kpi-accent: var(--pa-very-positive);
17613
+ }
17614
+
17615
+ .pa-kpi-hero-side__label {
17616
+ grid-area: label;
17617
+ align-self: end; /* tucks against the vertically-centred value */
17618
+ font-family: var(--base-font-family-mono);
17619
+ font-size: 1.3rem;
17620
+ font-weight: 700;
17621
+ letter-spacing: 0.1em;
17622
+ text-transform: uppercase;
17623
+ color: color-mix(in srgb, var(--pa-text-color-1) 60%, transparent);
17624
+ line-height: 1.25;
17625
+ }
17626
+
17627
+ .pa-kpi-hero-side__value {
17628
+ grid-area: value;
17629
+ align-self: center;
17630
+ font-family: var(--base-font-family-mono);
17631
+ line-height: 1;
17632
+ white-space: nowrap;
17633
+ display: inline-flex;
17634
+ align-items: baseline;
17635
+ }
17636
+
17637
+ .pa-kpi-hero-side__num {
17638
+ font-size: 2.4rem;
17639
+ font-weight: 700;
17640
+ letter-spacing: -0.02em;
17641
+ color: var(--pa-text-color-1);
17642
+ }
17643
+
17644
+ .pa-kpi-hero-side__unit {
17645
+ font-size: 1.5rem;
17646
+ font-weight: 500;
17647
+ color: var(--pa-text-secondary);
17648
+ margin: 0 0.35rem;
17649
+ }
17650
+
17651
+ .pa-kpi-hero-side__delta {
17652
+ grid-area: delta;
17653
+ align-self: start;
17654
+ font-family: var(--base-font-family-mono);
17655
+ font-size: 1.2rem;
17656
+ font-weight: 600;
17657
+ color: var(--pa-kpi-accent);
17658
+ }
17659
+
17660
+ /* ========================================
17661
+ KPI · Bento layout
17662
+ Magazine-style asymmetric tile sizing with sparklines as soft background
17663
+ fills behind the values. 6 tiles arranged on a 6-col × 3-row grid
17664
+ (hero left-half × 2 rows, two stacked right-half × 2 rows, three equal
17665
+ tiles bottom row). Tile placement by source order via :nth-child.
17666
+ ======================================== */
17667
+ .pa-kpi-bento {
17668
+ container-type: inline-size;
17669
+ }
17670
+
17671
+ .pa-kpi-bento__body {
17672
+ padding: 1.6rem;
17673
+ }
17674
+
17675
+ /* ----- Bento grid ------------------------------------------------------- */
17676
+ .pa-kpi-bento__grid {
17677
+ display: grid;
17678
+ grid-template-columns: repeat(6, 1fr);
17679
+ grid-template-rows: 12rem 12rem 12rem;
17680
+ grid-template-areas: "hero hero hero a a a" "hero hero hero b b b" "c c d d e e";
17681
+ gap: 1rem;
17682
+ }
17683
+ .pa-kpi-bento__grid > :nth-child(1) {
17684
+ grid-area: hero;
17685
+ }
17686
+ .pa-kpi-bento__grid > :nth-child(2) {
17687
+ grid-area: a;
17688
+ }
17689
+ .pa-kpi-bento__grid > :nth-child(3) {
17690
+ grid-area: b;
17691
+ }
17692
+ .pa-kpi-bento__grid > :nth-child(4) {
17693
+ grid-area: c;
17694
+ }
17695
+ .pa-kpi-bento__grid > :nth-child(5) {
17696
+ grid-area: d;
17697
+ }
17698
+ .pa-kpi-bento__grid > :nth-child(6) {
17699
+ grid-area: e;
17700
+ }
17701
+
17702
+ /* Narrow card → stack everything single column. Hero modifier still
17703
+ bumps the value font-size so it reads as the headline. */
17704
+ @container (max-width: 700px) {
17705
+ .pa-kpi-bento__grid {
17706
+ grid-template-columns: 1fr;
17707
+ grid-template-rows: auto;
17708
+ grid-template-areas: none;
17709
+ gap: 0.8rem;
17710
+ }
17711
+ .pa-kpi-bento__grid > :nth-child(n) {
17712
+ grid-area: auto;
17713
+ }
17714
+ .pa-kpi-bento-tile {
17715
+ min-height: 10rem;
17716
+ }
17717
+ .pa-kpi-bento-tile--hero {
17718
+ min-height: 14rem;
17719
+ }
17720
+ }
17721
+ /* ----- Tile (per-KPI bento cell) ---------------------------------------- */
17722
+ .pa-kpi-bento-tile {
17723
+ /* Per-tile inline-size container so the value font-size scales with
17724
+ *this* tile's width via cqi (not the outer card's width). The hero
17725
+ and the smaller bottom tiles each scale to their own column. */
17726
+ container-type: inline-size;
17727
+ position: relative;
17728
+ overflow: hidden;
17729
+ padding: 1.3rem 1.5rem;
17730
+ border: 1px solid var(--pa-border-color);
17731
+ background: var(--pa-card-bg);
17732
+ display: grid;
17733
+ grid-template-columns: minmax(0, 1fr) auto;
17734
+ grid-template-rows: auto 1fr auto;
17735
+ grid-template-areas: "label delta" ". . " "value value";
17736
+ gap: 0.4rem 1rem;
17737
+ /* Sparkline + delta sentiment cascade via currentColor. */
17738
+ --pa-kpi-accent: var(--pa-positive);
17739
+ }
17740
+ .pa-kpi-bento-tile--positive {
17741
+ --pa-kpi-accent: var(--pa-positive);
17742
+ }
17743
+ .pa-kpi-bento-tile--negative {
17744
+ --pa-kpi-accent: var(--pa-negative);
17745
+ }
17746
+ .pa-kpi-bento-tile--neutral {
17747
+ --pa-kpi-accent: var(--pa-neutral);
17748
+ }
17749
+ .pa-kpi-bento-tile--up-strong {
17750
+ --pa-kpi-accent: var(--pa-very-positive);
17751
+ }
17752
+ .pa-kpi-bento-tile--down-strong {
17753
+ --pa-kpi-accent: var(--pa-very-negative);
17754
+ }
17755
+ .pa-kpi-bento-tile--hero {
17756
+ padding: 1.6rem 1.8rem;
17757
+ }
17758
+ .pa-kpi-bento-tile--hero .pa-kpi-bento-tile__num {
17759
+ font-size: clamp(3.6rem, 22cqi, 7rem);
17760
+ }
17761
+ .pa-kpi-bento-tile--hero .pa-kpi-bento-tile__unit {
17762
+ font-size: clamp(1.6rem, 9cqi, 2.6rem);
17763
+ }
17764
+ .pa-kpi-bento-tile--hero .pa-kpi-bento-tile__label,
17765
+ .pa-kpi-bento-tile--hero .pa-kpi-bento-tile__delta {
17766
+ font-size: 1.4rem;
17767
+ }
17768
+ .pa-kpi-bento-tile--hero .pa-kpi-bento-tile__chart {
17769
+ height: 70%;
17770
+ }
17771
+
17772
+ /* ----- Top row: label + delta, both layered over the chart -------------- */
17773
+ .pa-kpi-bento-tile__label {
17774
+ grid-area: label;
17775
+ font-family: var(--base-font-family-mono);
17776
+ font-size: 1.3rem;
17777
+ font-weight: 700;
17778
+ letter-spacing: 0.1em;
17779
+ text-transform: uppercase;
17780
+ color: color-mix(in srgb, var(--pa-text-color-1) 60%, transparent);
17781
+ z-index: 1;
17782
+ line-height: 1.2;
17783
+ }
17784
+
17785
+ .pa-kpi-bento-tile__delta {
17786
+ grid-area: delta;
17787
+ font-family: var(--base-font-family-mono);
17788
+ font-size: 1.3rem;
17789
+ font-weight: 700;
17790
+ color: var(--pa-kpi-accent);
17791
+ z-index: 1;
17792
+ align-self: start;
17793
+ line-height: 1.2;
17794
+ }
17795
+
17796
+ /* ----- Value (bottom of grid, layered over chart) ----------------------- */
17797
+ .pa-kpi-bento-tile__value {
17798
+ grid-area: value;
17799
+ align-self: end;
17800
+ position: relative;
17801
+ z-index: 1;
17802
+ font-family: var(--base-font-family-mono);
17803
+ display: inline-flex;
17804
+ align-items: baseline;
17805
+ line-height: 1;
17806
+ white-space: nowrap;
17807
+ }
17808
+
17809
+ .pa-kpi-bento-tile__num {
17810
+ font-size: clamp(2rem, 14cqi, 3.2rem);
17811
+ font-weight: 700;
17812
+ letter-spacing: -0.02em;
17813
+ color: var(--pa-text-color-1);
17814
+ }
17815
+
17816
+ .pa-kpi-bento-tile__unit {
17817
+ font-size: clamp(1.2rem, 7cqi, 1.6rem);
17818
+ font-weight: 500;
17819
+ color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
17820
+ margin: 0 0.3rem;
17821
+ }
17822
+
17823
+ /* ----- Sparkline background ---------------------------------------------
17824
+ Absolutely positioned, fills the bottom 65% of the tile. The value sits
17825
+ over it (z-index: 1 above). The line/area read through behind the
17826
+ digits at lower opacity so the value remains the focal point. SVG is
17827
+ nested in a fixed-height wrapper so taller tiles don't distort the line
17828
+ shape (preserveAspectRatio="none" would otherwise stretch the Y axis). */
17829
+ .pa-kpi-bento-tile__chart {
17830
+ position: absolute;
17831
+ left: 0;
17832
+ right: 0;
17833
+ bottom: 0;
17834
+ height: 65%;
17835
+ pointer-events: none;
17836
+ color: var(--pa-kpi-accent);
17837
+ z-index: 0;
17838
+ display: flex;
17839
+ align-items: flex-end;
17840
+ }
17841
+ .pa-kpi-bento-tile__chart svg {
17842
+ display: block;
17843
+ width: 100%;
17844
+ height: 100%;
17845
+ overflow: visible;
17846
+ }
17847
+ .pa-kpi-bento-tile__chart polyline {
17848
+ fill: none;
17849
+ stroke: currentColor;
17850
+ stroke-opacity: 0.55;
17851
+ stroke-width: var(--pa-chart-trendline-stroke);
17852
+ stroke-linecap: round;
17853
+ stroke-linejoin: round;
17854
+ }
17855
+ .pa-kpi-bento-tile__chart polygon {
17856
+ fill: currentColor;
17857
+ fill-opacity: 0.1;
17858
+ stroke: none;
17859
+ }
17860
+
17861
+ .pa-kpi-bento-tile__chart-svg {
17862
+ position: relative;
17863
+ display: block;
17864
+ width: 100%;
17865
+ height: var(--pa-chart-trendline-height);
17866
+ }
17867
+
17868
+ /* ========================================
17869
+ KPI · Numeric strip (densest)
17870
+ Tabular "spreadsheet-style" table card with metric/now/prev/Δ%/vs target
17871
+ columns — most data per pixel, no chart chrome. Each row is its own
17872
+ grid sharing the same column template so cells align across rows
17873
+ without needing subgrid. Wide-only by design — no responsive collapse;
17874
+ narrow placements should use Comparison gauges instead.
17875
+ ======================================== */
17876
+ .pa-kpi-strip__body {
17877
+ padding: 0;
17878
+ }
17879
+
17880
+ /* ----- Table layout ----------------------------------------------------- */
17881
+ .pa-kpi-strip__row,
17882
+ .pa-kpi-strip__head-row {
17883
+ display: grid;
17884
+ grid-template-columns: minmax(0, 2fr) minmax(0, 1.1fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.6fr); /* target (bar + pct stacked) */
17885
+ column-gap: 1.6rem;
17886
+ align-items: center;
17887
+ position: relative;
17888
+ padding: 1.1rem 1.6rem;
17889
+ }
17890
+
17891
+ /* Divider between data rows / between head and first row only. */
17892
+ .pa-kpi-strip__row + .pa-kpi-strip__row,
17893
+ .pa-kpi-strip__head-row + .pa-kpi-strip__row {
17894
+ border-top: 1px solid var(--pa-border-color);
17895
+ }
17896
+
17897
+ /* Hover host: subtle row highlight so the cursor-anchored popover has a
17898
+ clear "this row" anchor. */
17899
+ .pa-kpi-strip__row:hover {
17900
+ background: var(--pa-surface-hover);
17901
+ }
17902
+
17903
+ /* ----- Header cells (uppercase mono captions) --------------------------- */
17904
+ .pa-kpi-strip__head {
17905
+ font-family: var(--base-font-family-mono);
17906
+ font-size: 1.1rem;
17907
+ font-weight: 700;
17908
+ letter-spacing: 0.1em;
17909
+ text-transform: uppercase;
17910
+ color: var(--pa-text-tertiary);
17911
+ }
17912
+ .pa-kpi-strip__head--num {
17913
+ text-align: end;
17914
+ }
17915
+
17916
+ /* ----- Metric label ----------------------------------------------------- */
17917
+ .pa-kpi-strip__metric {
17918
+ font-family: var(--base-font-family-mono);
17919
+ font-size: 1.4rem;
17920
+ font-weight: 700;
17921
+ letter-spacing: 0.08em;
17922
+ text-transform: uppercase;
17923
+ color: color-mix(in srgb, var(--pa-text-color-1) 78%, transparent);
17924
+ line-height: 1.2;
17925
+ }
17926
+
17927
+ /* ----- NOW (focal value) ------------------------------------------------ */
17928
+ .pa-kpi-strip__now {
17929
+ font-family: var(--base-font-family-mono);
17930
+ font-size: 1.8rem;
17931
+ font-weight: 700;
17932
+ text-align: end;
17933
+ display: flex;
17934
+ align-items: baseline;
17935
+ justify-content: flex-end;
17936
+ color: var(--pa-text-color-1);
17937
+ white-space: nowrap;
17938
+ line-height: 1;
17939
+ }
17940
+ .pa-kpi-strip__now .pa-kpi-strip__num {
17941
+ font-weight: 700;
17942
+ }
17943
+ .pa-kpi-strip__now .pa-kpi-strip__unit {
17944
+ font-size: 1.1rem;
17945
+ font-weight: 500;
17946
+ color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
17947
+ margin: 0 0.2rem;
17948
+ }
17949
+
17950
+ /* ----- PREV (reference data, low contrast) ------------------------------ */
17951
+ .pa-kpi-strip__prev {
17952
+ font-family: var(--base-font-family-mono);
17953
+ font-size: 1.5rem;
17954
+ font-weight: 500;
17955
+ color: var(--pa-text-tertiary);
17956
+ text-align: end;
17957
+ white-space: nowrap;
17958
+ }
17959
+
17960
+ /* ----- Δ% (sentiment-coloured) ------------------------------------------ */
17961
+ .pa-kpi-strip__delta {
17962
+ font-family: var(--base-font-family-mono);
17963
+ font-size: 1.5rem;
17964
+ font-weight: 700;
17965
+ text-align: end;
17966
+ white-space: nowrap;
17967
+ color: var(--pa-positive);
17968
+ }
17969
+ .pa-kpi-strip__delta--positive {
17970
+ color: var(--pa-positive);
17971
+ }
17972
+ .pa-kpi-strip__delta--negative {
17973
+ color: var(--pa-negative);
17974
+ }
17975
+ .pa-kpi-strip__delta--neutral {
17976
+ color: var(--pa-neutral);
17977
+ }
17978
+ .pa-kpi-strip__delta--up-strong {
17979
+ color: var(--pa-very-positive);
17980
+ }
17981
+ .pa-kpi-strip__delta--down-strong {
17982
+ color: var(--pa-very-negative);
17983
+ }
17984
+
17985
+ /* ----- VS TARGET (bar + pct stacked, capped at 100% visual) -------------
17986
+ Theme-neutral grey fill — the pct value itself signals overshoot /
17987
+ undershoot ("97" vs "108" vs "54") so colour reinforcement isn't
17988
+ needed. The bar visually caps at 100% via overflow: hidden so an
17989
+ over-target metric reads as "fully filled" rather than overflowing. */
17990
+ .pa-kpi-strip__target {
17991
+ display: flex;
17992
+ flex-direction: column;
17993
+ gap: 0.35rem;
17994
+ }
17995
+
17996
+ .pa-kpi-strip__bar {
17997
+ position: relative;
17998
+ height: 0.5rem;
17999
+ background: var(--pa-surface-track);
18000
+ overflow: hidden;
18001
+ border-radius: 0.1rem;
18002
+ }
18003
+
18004
+ .pa-kpi-strip__fill {
18005
+ position: absolute;
18006
+ top: 0;
18007
+ left: 0;
18008
+ bottom: 0;
18009
+ background: color-mix(in srgb, var(--pa-text-color-1) 40%, transparent);
18010
+ }
18011
+
18012
+ .pa-kpi-strip__bar-pct {
18013
+ font-family: var(--base-font-family-mono);
18014
+ font-size: 1.05rem;
18015
+ font-weight: 600;
18016
+ color: var(--pa-text-secondary);
18017
+ align-self: flex-end;
18018
+ line-height: 1;
18019
+ }
18020
+
18021
+ /* ----- --no-prev: 4-col layout (metric / now / Δ% / target) -------------
18022
+ Useful for narrow placements; markup omits the prev cells. */
18023
+ .pa-kpi-strip--no-prev .pa-kpi-strip__row,
18024
+ .pa-kpi-strip--no-prev .pa-kpi-strip__head-row {
18025
+ grid-template-columns: minmax(0, 2fr) minmax(0, 1.1fr) minmax(0, 1fr) minmax(0, 1.6fr);
18026
+ }
18027
+
18028
+ /* ========================================
18029
+ KPI · Editorial minimal
18030
+ Six KPIs in a 2×3 / 3×2 grid with hairline rules between cells,
18031
+ generous space, and an extra-light-weight number as the focal point per
18032
+ tile. No charts, no pills — the design's whole identity is the thin
18033
+ numeral. Renders as a table card (zero card-body padding) so hairlines
18034
+ go edge-to-edge.
18035
+ ======================================== */
18036
+ .pa-kpi-edit__body {
18037
+ padding: 0;
18038
+ }
18039
+
18040
+ /* ----- Grid + hairline rules -------------------------------------------
18041
+ `gap: 1px` over `background: var(--pa-border-color)` with each tile
18042
+ painting `background: var(--pa-card-bg)` on top. Only the gap shows
18043
+ through, giving perfect single-pixel hairlines on every interior
18044
+ boundary (vertical and horizontal) for free, regardless of column
18045
+ count. The card's outer border supplies the perimeter. */
18046
+ .pa-kpi-edit__grid {
18047
+ display: grid;
18048
+ grid-template-columns: repeat(3, 1fr);
18049
+ gap: 1px;
18050
+ background: var(--pa-border-color);
18051
+ container-type: inline-size;
18052
+ }
18053
+
18054
+ @container (max-width: 640px) {
18055
+ .pa-kpi-edit__grid {
18056
+ grid-template-columns: repeat(2, 1fr);
18057
+ }
18058
+ }
18059
+ @container (max-width: 360px) {
18060
+ .pa-kpi-edit__grid {
18061
+ grid-template-columns: 1fr;
18062
+ }
18063
+ }
18064
+ /* Modifier: force 2-col regardless of card width. For placements wanting
18065
+ a deterministic 2×N layout (e.g. 4 tiles as clean 2×2) instead of
18066
+ relying on the container query to land in the right bucket. */
18067
+ .pa-kpi-edit__grid--2col {
18068
+ grid-template-columns: repeat(2, 1fr);
18069
+ }
18070
+
18071
+ /* ----- Tile (editorial spacing leans on the 2.4rem padding) ------------- */
18072
+ .pa-kpi-edit__tile {
18073
+ background: var(--pa-card-bg);
18074
+ padding: 2.4rem 2rem;
18075
+ display: flex;
18076
+ flex-direction: column;
18077
+ gap: 1.2rem;
18078
+ position: relative;
18079
+ min-width: 0;
18080
+ /* Hover host: faint wash so the cursor-anchored popover has a clear
18081
+ "this tile" anchor. */
18082
+ }
18083
+ .pa-kpi-edit__tile:hover {
18084
+ background: color-mix(in srgb, var(--pa-text-color-1) 4%, var(--pa-card-bg));
18085
+ }
18086
+
18087
+ /* ----- Label (uppercase mono caption) ----------------------------------- */
18088
+ .pa-kpi-edit__label {
18089
+ font-family: var(--base-font-family-mono);
18090
+ font-size: 1.2rem;
18091
+ font-weight: 600;
18092
+ letter-spacing: 0.12em;
18093
+ text-transform: uppercase;
18094
+ color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
18095
+ line-height: 1.3;
18096
+ }
18097
+
18098
+ /* ----- Value (the editorial signature) ---------------------------------
18099
+ - NOT mono — mono fonts rarely have a true 200 weight; using the body
18100
+ sans gives proper thin glyphs that read as "editorial" rather than
18101
+ "monospace at low contrast".
18102
+ - font-weight: 200 (extra-light). 300 was tested first but didn't read
18103
+ distinctly enough as "light" against the body's 400 default.
18104
+ - clamp() lets the number shrink in narrow 25% page-grid cells without
18105
+ manual breakpoints. */
18106
+ .pa-kpi-edit__value {
18107
+ font-family: var(--base-font-family);
18108
+ font-size: clamp(3.2rem, 18cqi, 5.6rem);
18109
+ font-weight: 200;
18110
+ letter-spacing: -0.02em;
18111
+ line-height: 1;
18112
+ color: var(--pa-text-color-1);
18113
+ display: flex;
18114
+ align-items: baseline;
18115
+ gap: 0.2rem;
18116
+ flex-wrap: wrap;
18117
+ }
18118
+
18119
+ .pa-kpi-edit__num {
18120
+ font-weight: 200;
18121
+ font-variant-numeric: tabular-nums;
18122
+ }
18123
+
18124
+ .pa-kpi-edit__unit {
18125
+ font-size: 0.4em;
18126
+ font-weight: 300;
18127
+ color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
18128
+ letter-spacing: 0;
18129
+ }
18130
+
18131
+ /* ----- Meta row (delta + tgt) ------------------------------------------ */
18132
+ .pa-kpi-edit__meta {
18133
+ display: flex;
18134
+ align-items: baseline;
18135
+ gap: 1.4rem;
18136
+ font-family: var(--base-font-family-mono);
18137
+ font-size: 1.3rem;
18138
+ line-height: 1.3;
18139
+ flex-wrap: wrap;
18140
+ }
18141
+
18142
+ .pa-kpi-edit__delta {
18143
+ font-weight: 700;
18144
+ white-space: nowrap;
18145
+ color: var(--pa-positive);
18146
+ }
18147
+ .pa-kpi-edit__delta--positive {
18148
+ color: var(--pa-positive);
18149
+ }
18150
+ .pa-kpi-edit__delta--negative {
18151
+ color: var(--pa-negative);
18152
+ }
18153
+ .pa-kpi-edit__delta--neutral {
18154
+ color: var(--pa-neutral);
18155
+ }
18156
+ .pa-kpi-edit__delta--up-strong {
18157
+ color: var(--pa-very-positive);
18158
+ }
18159
+ .pa-kpi-edit__delta--down-strong {
18160
+ color: var(--pa-very-negative);
18161
+ }
18162
+
18163
+ .pa-kpi-edit__target {
18164
+ font-weight: 500;
18165
+ color: color-mix(in srgb, var(--pa-text-color-1) 50%, transparent);
18166
+ white-space: nowrap;
18167
+ }
18168
+ .pa-kpi-edit__target em {
18169
+ font-style: normal;
18170
+ color: color-mix(in srgb, var(--pa-text-color-1) 38%, transparent);
18171
+ margin-right: 0.5rem;
18172
+ }
18173
+
16654
18174
  /* ========================================
16655
18175
  Utility Components
16656
18176
  Font utilities, compact mode, component showcase