@dillingerstaffing/strand-ui 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/HTML_REFERENCE.md CHANGED
@@ -8,7 +8,9 @@
8
8
 
9
9
  > **Principle 1 (Cognitive Economy):** Every element must earn its place. Test: remove an element. If the task still works, it was decoration. If you're not adding back 10% of what you delete, you're not deleting enough. See [DL L61-69](./DESIGN_LANGUAGE.md#L61).
10
10
 
11
- > **Principle 2 (Biosynthetic Restraint):** Max 12 distinct visual elements per screen. See [DL L71-79](./DESIGN_LANGUAGE.md#L71).
11
+ > **Principle 2 (Biosynthetic Restraint):** Max 12 distinct visual elements per screen. Every composition must have exactly one visually dominant primary element. If all elements have equal visual weight, the composition has no focal point. An instrument without a focal point is a parts bin, not an instrument. See [DL L71-84](./DESIGN_LANGUAGE.md#L71).
12
+
13
+ > **Principle 9 (Typography Carries the Room):** The largest text and smallest text on the same screen must have at least a 3:1 size ratio. Uniform typography is a spreadsheet. Hierarchical typography is an instrument panel. See [DL L165-178](./DESIGN_LANGUAGE.md#L165).
12
14
 
13
15
  ## Required CSS
14
16
 
@@ -265,6 +267,8 @@ All container components (Grid, Stack, Card, Container) enforce boundary integri
265
267
 
266
268
  > **Elevation:** Cards at rest use Level 1 shadow. On hover, Level 2. See [DESIGN_LANGUAGE.md Principle 5: Earned Elevation (L114-L125)](./DESIGN_LANGUAGE.md#L114) and [7.2: Container Elevation Contexts (L698-L708)](./DESIGN_LANGUAGE.md#L698).
267
269
 
270
+ > **Cards are instrument panels, not generic containers.** Before placing multiple cards in a layout, apply [Principle 2 hierarchy test (L71-84)](./DESIGN_LANGUAGE.md#L71): which card is the primary instrument? It should be visually dominant. Apply [Principle 10 (L171-196)](./DESIGN_LANGUAGE.md#L171): describe the layout in laboratory vocabulary. If it sounds generic, redesign.
271
+
268
272
  ---
269
273
 
270
274
  ### Badge
@@ -403,10 +407,12 @@ All container components (Grid, Stack, Card, Container) enforce boundary integri
403
407
  </div>
404
408
  ```
405
409
 
406
- **Sizes:** `--sm` (text-xl, 25px) | default (text-3xl, 39px) | `--lg` (text-4xl, 49px). Label stays xs across all sizes.
410
+ **Sizes:** `--sm` (text-xl, 25px) | default (text-3xl, 39px) | `--lg` (text-4xl, 49px) | `--xl` (fluid 72-112px, primary instrument readout). Label stays xs across all sizes.
407
411
 
408
412
  > **The DataReadout pattern** is uniquely Strand: monospace overline + large light-weight value + tabular numerals. See [DESIGN_LANGUAGE.md 11.2: Data Display (L918-L955)](./DESIGN_LANGUAGE.md#L918).
409
413
 
414
+ > **DataReadout has three sizes for hierarchy, not preference.** Use ONE default or lg readout as the primary focal point, with sm readouts as supporting secondaries. If all readouts on a screen are the same size, apply [Principle 2 hierarchy test (L71-84)](./DESIGN_LANGUAGE.md#L71) and [Principle 9 contrast ratio test (L165-178)](./DESIGN_LANGUAGE.md#L165).
415
+
410
416
  ---
411
417
 
412
418
  ### CodeBlock
@@ -429,6 +435,10 @@ All container components (Grid, Stack, Card, Container) enforce boundary integri
429
435
 
430
436
  > **Spacing rules:** 4px base unit, padding tiers, and the gap > padding hierarchy: [DESIGN_LANGUAGE.md Part V: Spacing (L410-L496)](./DESIGN_LANGUAGE.md#L410). Responsive breakpoints and container system: [Part X: Layout (L829-L877)](./DESIGN_LANGUAGE.md#L829).
431
437
 
438
+ > **Before composing a layout, apply [Principle 2 (L71-84)](./DESIGN_LANGUAGE.md#L71) and [Principle 10 (L171-196)](./DESIGN_LANGUAGE.md#L171).** Identify the primary element. Describe the layout in laboratory vocabulary. If the description sounds generic ("a grid of data cards"), the composition doesn't embody the DL. An analytical readout panel has one dominant readout and supporting secondaries -- not four equal panels.
439
+
440
+ > **Before sizing text, apply [Principle 9 (L165-178)](./DESIGN_LANGUAGE.md#L165).** Largest to smallest text on the same screen must be at least 3:1. If all text is the same size, the typography is uniform. Uniform typography is a spreadsheet, not an instrument panel.
441
+
432
442
  ### Stack
433
443
 
434
444
  ```html
@@ -876,3 +886,104 @@ Intro paragraph. Max 50 characters per line. Used after headlines.
876
886
  Caption/description text. text-sm, gray-500, relaxed leading. The `--xs` modifier reduces to text-xs for metadata and fine print.
877
887
 
878
888
  > **DL foundation:** [DESIGN_LANGUAGE.md Part III.8 Color Roles](./DESIGN_LANGUAGE.md#L290) defines gray-500 as the secondary text role. [Part IV.7 Named Text Patterns](./DESIGN_LANGUAGE.md#L408) specifies the Secondary pattern.
889
+
890
+ ---
891
+
892
+ ## Composition Molecules
893
+
894
+ Named CSS classes for common compositions. Each implements one or more productions from the [DL Composition Grammar (Part XI-B)](./DESIGN_LANGUAGE.md#L1085).
895
+
896
+ > **Composition rules:** [DL 11.10 Productions](./DESIGN_LANGUAGE.md#L1144) | [DL 11.11 Containment](./DESIGN_LANGUAGE.md#L1199) | [DL 11.12 Derivation](./DESIGN_LANGUAGE.md#L1215) | [DL 11.13 Tests](./DESIGN_LANGUAGE.md#L1246)
897
+
898
+ ### Card Section
899
+
900
+ Production: `section-boundary`.
901
+
902
+ ```html
903
+ <!-- Single label -->
904
+ <div class="strand-card-section">
905
+ <span class="strand-overline">Section Label</span>
906
+ </div>
907
+
908
+ <!-- Distributed header (label + secondary) -->
909
+ <div class="strand-card-section">
910
+ <span class="strand-overline">7-Day Forecast</span>
911
+ <span class="strand-overline" style="color: var(--strand-gray-400)">Ethiopian Yirgacheffe</span>
912
+ </div>
913
+ ```
914
+
915
+ Children distribute on the inline axis (space-between). Single child sits at the start. Multiple sections stack; last omits trailing border via `:last-child`.
916
+
917
+ ### Key-Value Row
918
+
919
+ Productions: `inline-pair` + `ranked-sequence`.
920
+
921
+ ```html
922
+ <div class="strand-kv">
923
+ <span class="strand-kv__label">Blend</span>
924
+ <span class="strand-kv__value">Ethiopian Yirgacheffe</span>
925
+ </div>
926
+ <div class="strand-kv">
927
+ <span class="strand-kv__label">Status</span>
928
+ <span class="strand-kv__value strand-kv__value--status">Active</span>
929
+ </div>
930
+ ```
931
+
932
+ **Modifier:** `strand-kv__value--status` applies semantic color.
933
+
934
+ ### Diagnostic Log Entry
935
+
936
+ Productions: `inline-sequence` + `ranked-sequence`.
937
+
938
+ ```html
939
+ <div class="strand-log">
940
+ <span class="strand-log__time">9:15</span>
941
+ <span class="strand-log__status strand-log__status--complete">Brewed</span>
942
+ <span class="strand-text-secondary">Pour over. 18g.</span>
943
+ </div>
944
+ <div class="strand-log">
945
+ <span class="strand-log__time">6:30</span>
946
+ <span class="strand-log__status strand-log__status--process">Brewing</span>
947
+ <span class="strand-text-secondary">Espresso. 18g.</span>
948
+ </div>
949
+ ```
950
+
951
+ **Status variants:** `--complete` (teal) | `--process` (blue) | `--warning` (amber) | `--error` (red)
952
+
953
+ ### Metric Row
954
+
955
+ Production: `centered-group`.
956
+
957
+ ```html
958
+ <div class="strand-metric-row">
959
+ <div class="strand-data-readout strand-data-readout--sm">
960
+ <span class="strand-data-readout__label">Daily</span>
961
+ <span class="strand-data-readout__value">36g</span>
962
+ </div>
963
+ <div class="strand-data-readout strand-data-readout--sm">
964
+ <span class="strand-data-readout__label">Days Left</span>
965
+ <span class="strand-data-readout__value">7.9</span>
966
+ </div>
967
+ </div>
968
+ ```
969
+
970
+ ### Bar Chart
971
+
972
+ Production: `column-array`.
973
+
974
+ ```html
975
+ <div class="strand-bar-chart">
976
+ <div class="strand-bar-chart__col">
977
+ <span class="strand-bar-chart__amount">284</span>
978
+ <div class="strand-bar-chart__bar" style="height: 100%"></div>
979
+ <span class="strand-bar-chart__label">Thu</span>
980
+ </div>
981
+ <div class="strand-bar-chart__col">
982
+ <span class="strand-bar-chart__amount">248</span>
983
+ <div class="strand-bar-chart__bar" style="height: 87%"></div>
984
+ <span class="strand-bar-chart__label">Fri</span>
985
+ </div>
986
+ </div>
987
+ ```
988
+
989
+ Heights are inline styles (data-driven). Place inside `.strand-instrument-viewport` for dual-surface treatment.
@@ -5,8 +5,8 @@ export interface DataReadoutProps extends Omit<JSX.HTMLAttributes<HTMLDivElement
5
5
  label: string;
6
6
  /** The large displayed value */
7
7
  value: string | number;
8
- /** Size variant: sm (compact), md (default), lg (hero) */
9
- size?: "sm" | "md" | "lg";
8
+ /** Size variant: sm (compact), md (default), lg (hero), xl (primary instrument) */
9
+ size?: "sm" | "md" | "lg" | "xl";
10
10
  }
11
11
  export declare const DataReadout: import("preact").FunctionalComponent<import("preact/compat").PropsWithoutRef<DataReadoutProps> & {
12
12
  ref?: import("preact").Ref<HTMLDivElement> | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"DataReadout.d.ts","sourceRoot":"","sources":["../../../src/components/DataReadout/DataReadout.tsx"],"names":[],"mappings":"AAAA,sDAAsD;AAEtD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAGlC,MAAM,WAAW,gBACf,SAAQ,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzD,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,0DAA0D;IAC1D,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;CAC3B;AAED,eAAO,MAAM,WAAW;;EAiBvB,CAAC"}
1
+ {"version":3,"file":"DataReadout.d.ts","sourceRoot":"","sources":["../../../src/components/DataReadout/DataReadout.tsx"],"names":[],"mappings":"AAAA,sDAAsD;AAEtD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAGlC,MAAM,WAAW,gBACf,SAAQ,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzD,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,mFAAmF;IACnF,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;CAClC;AAED,eAAO,MAAM,WAAW;;EAiBvB,CAAC"}
@@ -763,6 +763,10 @@
763
763
  font-size: var(--strand-text-4xl);
764
764
  }
765
765
 
766
+ .strand-data-readout--xl .strand-data-readout__value {
767
+ font-size: clamp(4.5rem, 10vw, 7rem);
768
+ }
769
+
766
770
 
767
771
  /* Dialog */
768
772
  /*! Strand UI | MIT License | dillingerstaffing.com */
@@ -2646,6 +2650,16 @@
2646
2650
  letter-spacing: var(--strand-tracking-tighter);
2647
2651
  }
2648
2652
 
2653
+ /* ── Title (human voice display, DL Part IV.7) ── */
2654
+ .strand-title {
2655
+ font-family: var(--strand-font-sans);
2656
+ font-weight: var(--strand-weight-light);
2657
+ letter-spacing: var(--strand-tracking-tighter);
2658
+ color: var(--strand-blue-midnight);
2659
+ font-size: clamp(1.5rem, 3vw + 0.5rem, 2.5rem);
2660
+ line-height: var(--strand-leading-snug);
2661
+ }
2662
+
2649
2663
  /* ── Lead (intro paragraph, DL Part IV.6) ── */
2650
2664
  .strand-lead {
2651
2665
  font-size: var(--strand-text-lg);
@@ -2665,4 +2679,154 @@
2665
2679
  font-size: var(--strand-text-xs);
2666
2680
  }
2667
2681
 
2682
+ /* ══════════════════════════════════════════════════
2683
+ COMPOSITION MOLECULES (DL Part XI-B Grammar)
2684
+ Named derivations of DL composition primitives.
2685
+ Each molecule is a convenience class for a common
2686
+ derivation chain. See DL 11.10 Productions for the
2687
+ grammar rules; DL 11.12 Derivation for verification.
2688
+ ══════════════════════════════════════════════════ */
2689
+
2690
+ /* ── Card Section ──
2691
+ Derivation: section-boundary production (DL 11.10).
2692
+ OVERLINE + border-bottom + margin-bottom + padding-bottom */
2693
+ .strand-card-section {
2694
+ display: flex;
2695
+ justify-content: space-between;
2696
+ align-items: center;
2697
+ margin-bottom: var(--strand-space-3);
2698
+ padding-bottom: var(--strand-space-2);
2699
+ border-bottom: 1px solid var(--strand-gray-200);
2700
+ }
2701
+
2702
+ /* ── Key-Value Row ──
2703
+ Derivation: inline-pair + ranked-sequence (DL 11.10).
2704
+ identifier(OVERLINE) + quantifier(MONO_VALUE) + RANK_BORDER */
2705
+ .strand-kv {
2706
+ display: flex;
2707
+ justify-content: space-between;
2708
+ align-items: center;
2709
+ padding-block: var(--strand-space-2);
2710
+ }
2711
+
2712
+ .strand-kv + .strand-kv {
2713
+ border-top: 1px solid var(--strand-border-subtle);
2714
+ }
2715
+
2716
+ .strand-kv__label {
2717
+ font-family: var(--strand-font-mono);
2718
+ font-size: var(--strand-text-xs);
2719
+ font-weight: var(--strand-weight-medium);
2720
+ letter-spacing: var(--strand-tracking-wider);
2721
+ text-transform: uppercase;
2722
+ color: var(--strand-gray-500);
2723
+ }
2724
+
2725
+ .strand-kv__value {
2726
+ font-family: var(--strand-font-mono);
2727
+ font-size: var(--strand-text-xs);
2728
+ color: var(--strand-gray-600);
2729
+ font-variant-numeric: tabular-nums;
2730
+ }
2731
+
2732
+ .strand-kv__value--status {
2733
+ color: var(--strand-teal-vital);
2734
+ }
2735
+
2736
+ /* ── Diagnostic Log Entry ──
2737
+ Derivation: inline-sequence + ranked-sequence (DL 11.10).
2738
+ time(OVERLINE) + status(OVERLINE+COLOR_SEMANTIC) + text(SECONDARY) + RANK_BORDER */
2739
+ .strand-log {
2740
+ display: flex;
2741
+ gap: var(--strand-space-3);
2742
+ padding-block: var(--strand-space-2);
2743
+ }
2744
+
2745
+ .strand-log + .strand-log {
2746
+ border-top: 1px solid var(--strand-border-subtle);
2747
+ }
2748
+
2749
+ .strand-log__time {
2750
+ font-family: var(--strand-font-mono);
2751
+ font-size: var(--strand-text-xs);
2752
+ font-weight: var(--strand-weight-medium);
2753
+ letter-spacing: var(--strand-tracking-wider);
2754
+ text-transform: uppercase;
2755
+ color: var(--strand-gray-400);
2756
+ font-variant-numeric: tabular-nums;
2757
+ white-space: nowrap;
2758
+ }
2759
+
2760
+ .strand-log__status {
2761
+ font-family: var(--strand-font-mono);
2762
+ font-size: var(--strand-text-xs);
2763
+ font-weight: var(--strand-weight-semibold);
2764
+ letter-spacing: var(--strand-tracking-wider);
2765
+ text-transform: uppercase;
2766
+ white-space: nowrap;
2767
+ }
2768
+
2769
+ .strand-log__status--complete { color: var(--strand-teal-vital); }
2770
+ .strand-log__status--process { color: var(--strand-blue-primary); }
2771
+ .strand-log__status--warning { color: var(--strand-amber-caution); }
2772
+ .strand-log__status--error { color: var(--strand-red-alert); }
2773
+
2774
+ /* ── Metric Row ──
2775
+ Derivation: centered-group (DL 11.10).
2776
+ self-contained(atom)* with center distribution + responsive gap */
2777
+ .strand-metric-row {
2778
+ display: flex;
2779
+ justify-content: center;
2780
+ gap: clamp(2rem, 5vw, 4rem);
2781
+ text-align: center;
2782
+ }
2783
+
2784
+ /* ── Bar Chart ──
2785
+ Derivation: column-array (DL 11.10).
2786
+ column(amount? + bar + label)* with equal flex + flex-end alignment.
2787
+ Blue Discipline: one color (--strand-blue-indicator). Part XIII viz rules. */
2788
+ .strand-bar-chart {
2789
+ display: flex;
2790
+ gap: var(--strand-space-2);
2791
+ align-items: flex-end;
2792
+ height: var(--strand-space-24);
2793
+ padding: var(--strand-space-5);
2794
+ }
2795
+
2796
+ .strand-bar-chart__col {
2797
+ flex: 1;
2798
+ display: flex;
2799
+ flex-direction: column;
2800
+ align-items: center;
2801
+ gap: var(--strand-space-1);
2802
+ height: 100%;
2803
+ justify-content: flex-end;
2804
+ }
2805
+
2806
+ .strand-bar-chart__bar {
2807
+ width: 100%;
2808
+ background: var(--strand-blue-indicator);
2809
+ border-radius: var(--strand-radius-sm) var(--strand-radius-sm) 0 0;
2810
+ min-height: var(--strand-space-1);
2811
+ }
2812
+
2813
+ .strand-bar-chart__amount {
2814
+ font-family: var(--strand-font-mono);
2815
+ font-size: var(--strand-text-xs);
2816
+ font-weight: var(--strand-weight-medium);
2817
+ letter-spacing: var(--strand-tracking-wider);
2818
+ text-transform: uppercase;
2819
+ color: var(--strand-gray-300);
2820
+ font-variant-numeric: tabular-nums;
2821
+ }
2822
+
2823
+ .strand-bar-chart__label {
2824
+ font-family: var(--strand-font-mono);
2825
+ font-size: var(--strand-text-xs);
2826
+ font-weight: var(--strand-weight-medium);
2827
+ letter-spacing: var(--strand-tracking-wider);
2828
+ text-transform: uppercase;
2829
+ color: var(--strand-gray-400);
2830
+ }
2831
+
2668
2832
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dillingerstaffing/strand-ui",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Strand UI - Preact/React component library built on the Strand Design Language",
5
5
  "author": "Dillinger Staffing <engineering@dillingerstaffing.com> (https://dillingerstaffing.com)",
6
6
  "license": "MIT",
@@ -60,7 +60,7 @@
60
60
  }
61
61
  },
62
62
  "dependencies": {
63
- "@dillingerstaffing/strand": "^0.9.0"
63
+ "@dillingerstaffing/strand": "^0.11.0"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@testing-library/preact": "^3.2.0",
@@ -37,3 +37,7 @@
37
37
  .strand-data-readout--lg .strand-data-readout__value {
38
38
  font-size: var(--strand-text-4xl);
39
39
  }
40
+
41
+ .strand-data-readout--xl .strand-data-readout__value {
42
+ font-size: clamp(4.5rem, 10vw, 7rem);
43
+ }
@@ -111,6 +111,14 @@ describe("DataReadout", () => {
111
111
  expect(readout?.className).toContain("strand-data-readout--lg");
112
112
  });
113
113
 
114
+ it("applies xl size modifier class", () => {
115
+ const { container } = render(
116
+ <DataReadout label="Remaining" value="284g" size="xl" />,
117
+ );
118
+ const readout = container.querySelector(".strand-data-readout");
119
+ expect(readout?.className).toContain("strand-data-readout--xl");
120
+ });
121
+
114
122
  it("does not apply size modifier for md (default)", () => {
115
123
  const { container } = render(
116
124
  <DataReadout label="Metric" value="100" size="md" />,
@@ -9,8 +9,8 @@ export interface DataReadoutProps
9
9
  label: string;
10
10
  /** The large displayed value */
11
11
  value: string | number;
12
- /** Size variant: sm (compact), md (default), lg (hero) */
13
- size?: "sm" | "md" | "lg";
12
+ /** Size variant: sm (compact), md (default), lg (hero), xl (primary instrument) */
13
+ size?: "sm" | "md" | "lg" | "xl";
14
14
  }
15
15
 
16
16
  export const DataReadout = forwardRef<HTMLDivElement, DataReadoutProps>(
package/src/static.css CHANGED
@@ -77,6 +77,16 @@
77
77
  letter-spacing: var(--strand-tracking-tighter);
78
78
  }
79
79
 
80
+ /* ── Title (human voice display, DL Part IV.7) ── */
81
+ .strand-title {
82
+ font-family: var(--strand-font-sans);
83
+ font-weight: var(--strand-weight-light);
84
+ letter-spacing: var(--strand-tracking-tighter);
85
+ color: var(--strand-blue-midnight);
86
+ font-size: clamp(1.5rem, 3vw + 0.5rem, 2.5rem);
87
+ line-height: var(--strand-leading-snug);
88
+ }
89
+
80
90
  /* ── Lead (intro paragraph, DL Part IV.6) ── */
81
91
  .strand-lead {
82
92
  font-size: var(--strand-text-lg);
@@ -95,3 +105,153 @@
95
105
  .strand-text-secondary--xs {
96
106
  font-size: var(--strand-text-xs);
97
107
  }
108
+
109
+ /* ══════════════════════════════════════════════════
110
+ COMPOSITION MOLECULES (DL Part XI-B Grammar)
111
+ Named derivations of DL composition primitives.
112
+ Each molecule is a convenience class for a common
113
+ derivation chain. See DL 11.10 Productions for the
114
+ grammar rules; DL 11.12 Derivation for verification.
115
+ ══════════════════════════════════════════════════ */
116
+
117
+ /* ── Card Section ──
118
+ Derivation: section-boundary production (DL 11.10).
119
+ OVERLINE + border-bottom + margin-bottom + padding-bottom */
120
+ .strand-card-section {
121
+ display: flex;
122
+ justify-content: space-between;
123
+ align-items: center;
124
+ margin-bottom: var(--strand-space-3);
125
+ padding-bottom: var(--strand-space-2);
126
+ border-bottom: 1px solid var(--strand-gray-200);
127
+ }
128
+
129
+ /* ── Key-Value Row ──
130
+ Derivation: inline-pair + ranked-sequence (DL 11.10).
131
+ identifier(OVERLINE) + quantifier(MONO_VALUE) + RANK_BORDER */
132
+ .strand-kv {
133
+ display: flex;
134
+ justify-content: space-between;
135
+ align-items: center;
136
+ padding-block: var(--strand-space-2);
137
+ }
138
+
139
+ .strand-kv + .strand-kv {
140
+ border-top: 1px solid var(--strand-border-subtle);
141
+ }
142
+
143
+ .strand-kv__label {
144
+ font-family: var(--strand-font-mono);
145
+ font-size: var(--strand-text-xs);
146
+ font-weight: var(--strand-weight-medium);
147
+ letter-spacing: var(--strand-tracking-wider);
148
+ text-transform: uppercase;
149
+ color: var(--strand-gray-500);
150
+ }
151
+
152
+ .strand-kv__value {
153
+ font-family: var(--strand-font-mono);
154
+ font-size: var(--strand-text-xs);
155
+ color: var(--strand-gray-600);
156
+ font-variant-numeric: tabular-nums;
157
+ }
158
+
159
+ .strand-kv__value--status {
160
+ color: var(--strand-teal-vital);
161
+ }
162
+
163
+ /* ── Diagnostic Log Entry ──
164
+ Derivation: inline-sequence + ranked-sequence (DL 11.10).
165
+ time(OVERLINE) + status(OVERLINE+COLOR_SEMANTIC) + text(SECONDARY) + RANK_BORDER */
166
+ .strand-log {
167
+ display: flex;
168
+ gap: var(--strand-space-3);
169
+ padding-block: var(--strand-space-2);
170
+ }
171
+
172
+ .strand-log + .strand-log {
173
+ border-top: 1px solid var(--strand-border-subtle);
174
+ }
175
+
176
+ .strand-log__time {
177
+ font-family: var(--strand-font-mono);
178
+ font-size: var(--strand-text-xs);
179
+ font-weight: var(--strand-weight-medium);
180
+ letter-spacing: var(--strand-tracking-wider);
181
+ text-transform: uppercase;
182
+ color: var(--strand-gray-400);
183
+ font-variant-numeric: tabular-nums;
184
+ white-space: nowrap;
185
+ }
186
+
187
+ .strand-log__status {
188
+ font-family: var(--strand-font-mono);
189
+ font-size: var(--strand-text-xs);
190
+ font-weight: var(--strand-weight-semibold);
191
+ letter-spacing: var(--strand-tracking-wider);
192
+ text-transform: uppercase;
193
+ white-space: nowrap;
194
+ }
195
+
196
+ .strand-log__status--complete { color: var(--strand-teal-vital); }
197
+ .strand-log__status--process { color: var(--strand-blue-primary); }
198
+ .strand-log__status--warning { color: var(--strand-amber-caution); }
199
+ .strand-log__status--error { color: var(--strand-red-alert); }
200
+
201
+ /* ── Metric Row ──
202
+ Derivation: centered-group (DL 11.10).
203
+ self-contained(atom)* with center distribution + responsive gap */
204
+ .strand-metric-row {
205
+ display: flex;
206
+ justify-content: center;
207
+ gap: clamp(2rem, 5vw, 4rem);
208
+ text-align: center;
209
+ }
210
+
211
+ /* ── Bar Chart ──
212
+ Derivation: column-array (DL 11.10).
213
+ column(amount? + bar + label)* with equal flex + flex-end alignment.
214
+ Blue Discipline: one color (--strand-blue-indicator). Part XIII viz rules. */
215
+ .strand-bar-chart {
216
+ display: flex;
217
+ gap: var(--strand-space-2);
218
+ align-items: flex-end;
219
+ height: var(--strand-space-24);
220
+ padding: var(--strand-space-5);
221
+ }
222
+
223
+ .strand-bar-chart__col {
224
+ flex: 1;
225
+ display: flex;
226
+ flex-direction: column;
227
+ align-items: center;
228
+ gap: var(--strand-space-1);
229
+ height: 100%;
230
+ justify-content: flex-end;
231
+ }
232
+
233
+ .strand-bar-chart__bar {
234
+ width: 100%;
235
+ background: var(--strand-blue-indicator);
236
+ border-radius: var(--strand-radius-sm) var(--strand-radius-sm) 0 0;
237
+ min-height: var(--strand-space-1);
238
+ }
239
+
240
+ .strand-bar-chart__amount {
241
+ font-family: var(--strand-font-mono);
242
+ font-size: var(--strand-text-xs);
243
+ font-weight: var(--strand-weight-medium);
244
+ letter-spacing: var(--strand-tracking-wider);
245
+ text-transform: uppercase;
246
+ color: var(--strand-gray-300);
247
+ font-variant-numeric: tabular-nums;
248
+ }
249
+
250
+ .strand-bar-chart__label {
251
+ font-family: var(--strand-font-mono);
252
+ font-size: var(--strand-text-xs);
253
+ font-weight: var(--strand-weight-medium);
254
+ letter-spacing: var(--strand-tracking-wider);
255
+ text-transform: uppercase;
256
+ color: var(--strand-gray-400);
257
+ }