@pipelex/mthds-ui 0.6.4 → 0.7.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.
@@ -624,17 +624,103 @@ svg.react-flow__connectionline {
624
624
  }
625
625
 
626
626
  /* ReactFlow base styles — resolved by the consumer's bundler */
627
- /* Default theme — consumers can override any var on an ancestor element */
627
+ /**
628
+ * Tokens (--surface-*, --text-*, --border-*, --shadow-*, --color-*) are
629
+ * applied to `.react-flow-container` inline by `GraphViewer` based on the
630
+ * active theme (dark / light). Component CSS only references those tokens —
631
+ * never raw hex/rgba — so a new theme is just a new set of values.
632
+ *
633
+ * The block below provides STATIC dark-palette defaults so that any of these
634
+ * cases still render with valid colors / fonts / shadows:
635
+ * - first paint before the React inline-palette effect runs
636
+ * - SSR / hydration mismatches
637
+ * - a consumer rendering a graph subcomponent outside `GraphViewer`
638
+ * - JS disabled / failed
639
+ * Values mirror `DARK_PALETTE_COLORS` in `graphConfig.ts`. The runtime
640
+ * palette (dark or light) is applied as higher-specificity inline styles
641
+ * and wins automatically.
642
+ */
628
643
  .react-flow-container {
644
+ /* Surfaces */
645
+ --surface-page: #0a0a0a;
646
+ --surface-panel: #111118;
647
+ --surface-overlay: rgba(17, 17, 24, 0.8);
648
+ --surface-overlay-hover: rgba(30, 30, 40, 0.9);
649
+ --surface-overlay-disabled: rgba(17, 17, 24, 0.6);
650
+ --surface-elevated: rgba(255, 255, 255, 0.06);
651
+ --surface-elevated-hover: rgba(255, 255, 255, 0.1);
652
+ --surface-sunken: rgba(255, 255, 255, 0.03);
653
+ --surface-pill: rgba(255, 255, 255, 0.06);
654
+ --surface-pill-border: rgba(255, 255, 255, 0.08);
655
+
656
+ /* Borders */
657
+ --border-subtle: rgba(255, 255, 255, 0.06);
658
+ --border-default: rgba(255, 255, 255, 0.1);
659
+ --border-strong: rgba(255, 255, 255, 0.18);
660
+ --border-dashed: rgba(255, 255, 255, 0.15);
661
+
662
+ /* Text */
663
+ --text-primary: #f8fafc;
664
+ --text-default: #e2e8f0;
665
+ --text-secondary: #cbd5e1;
666
+ --text-muted: #94a3b8;
667
+ --text-dim: #64748b;
668
+ --text-on-accent: #0e0e0e;
669
+
670
+ /* Effects */
671
+ --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.4);
672
+ --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.6);
673
+ --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.5);
674
+ --focus-ring: rgba(59, 130, 246, 0.6);
675
+
676
+ /* Domain-semantic colors */
677
+ --color-pipe: #ff6b6b;
678
+ --color-pipe-bg: rgba(224, 108, 117, 0.18);
679
+ --color-pipe-text: #ffffff;
680
+ --color-stuff: #4ecdc4;
681
+ --color-stuff-bg: rgba(78, 205, 196, 0.12);
682
+ --color-stuff-border: #9ddcfd;
683
+ --color-stuff-text: #98fb98;
684
+ --color-stuff-text-dim: #9ddcfd;
685
+ --color-edge: #fffacd;
686
+ --color-batch-item: #bd93f9;
687
+ --color-batch-aggregate: #50fa7b;
688
+ --color-parallel-combine: #d6a4ff;
689
+ --color-success: #50fa7b;
690
+ --color-success-bg: rgba(80, 250, 123, 0.15);
691
+ --color-error: #ff5555;
692
+ --color-error-bg: rgba(255, 85, 85, 0.15);
693
+ --color-error-border: rgba(255, 85, 85, 0.2);
694
+ --color-accent: #8be9fd;
695
+ --color-accent-strong: #3b82f6;
696
+ --color-warning: #ffb86c;
697
+
698
+ /* Controller-group palette */
699
+ --ctrl-sequence-border: rgba(148, 163, 184, 0.25);
700
+ --ctrl-sequence-bg: rgba(148, 163, 184, 0.03);
701
+ --ctrl-sequence-text: #94a3b8;
702
+ --ctrl-parallel-border: rgba(139, 233, 253, 0.35);
703
+ --ctrl-parallel-bg: rgba(139, 233, 253, 0.03);
704
+ --ctrl-parallel-text: #8be9fd;
705
+ --ctrl-condition-border: rgba(251, 191, 36, 0.35);
706
+ --ctrl-condition-bg: rgba(251, 191, 36, 0.03);
707
+ --ctrl-condition-text: #fbbf24;
708
+ --ctrl-batch-border: rgba(167, 139, 250, 0.35);
709
+ --ctrl-batch-bg: rgba(167, 139, 250, 0.03);
710
+ --ctrl-batch-text: #a78bfa;
711
+ --ctrl-folded-bg: rgba(148, 163, 184, 0.25);
712
+ --ctrl-folded-border: rgba(148, 163, 184, 0.4);
713
+
714
+ /* Legacy aliases — still consumed by some inline styles in graph builders */
629
715
  --color-bg: #0a0a0a;
630
716
  --color-bg-dots: rgba(255, 255, 255, 0.35);
631
717
  --color-text-muted: #94a3b8;
632
718
  --color-controller-text: #94a3b8;
719
+
720
+ /* Fonts */
633
721
  --font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
634
722
  --font-mono: "JetBrains Mono", "Monaco", "Menlo", monospace;
635
- --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.5);
636
- }
637
- .react-flow-container {
723
+
638
724
  position: absolute;
639
725
  inset: 0;
640
726
  background: var(--color-bg);
@@ -644,17 +730,17 @@ svg.react-flow__connectionline {
644
730
  cursor: pointer;
645
731
  }
646
732
  .react-flow__background {
647
- background: var(--color-bg) !important;
733
+ background: var(--color-bg, #0a0a0a) !important;
648
734
  }
649
735
  .react-flow__edge-path {
650
736
  stroke-width: 2;
651
737
  }
652
738
  .react-flow__edge-text {
653
739
  font-size: 11px;
654
- fill: var(--color-text-muted);
740
+ fill: var(--text-muted);
655
741
  }
656
742
  .react-flow__edge-textbg {
657
- fill: var(--color-bg);
743
+ fill: var(--color-bg, #0a0a0a);
658
744
  }
659
745
  /* Selected node highlight */
660
746
  .react-flow__node.selected {
@@ -677,8 +763,8 @@ svg.react-flow__connectionline {
677
763
  }
678
764
  .react-flow__node-pipeCard.selected .pipe-card {
679
765
  box-shadow:
680
- 0 0 0 2px var(--color-accent, #3b82f6),
681
- 0 4px 16px rgba(0, 0, 0, 0.6);
766
+ 0 0 0 2px var(--color-accent),
767
+ var(--shadow-md);
682
768
  }
683
769
  .react-flow__node-controllerGroup.selected {
684
770
  box-shadow:
@@ -690,9 +776,9 @@ svg.react-flow__connectionline {
690
776
  }
691
777
  /* ─── Controller Group Node ──────────────────────────────────────────── */
692
778
  .controller-group-node {
693
- --controller-border: 2px dashed rgba(148, 163, 184, 0.3);
694
- --controller-bg: rgba(148, 163, 184, 0.04);
695
- --controller-header-color: #64748b;
779
+ --controller-border: 2px dashed var(--ctrl-sequence-border);
780
+ --controller-bg: var(--ctrl-sequence-bg);
781
+ --controller-header-color: var(--ctrl-sequence-text);
696
782
 
697
783
  width: 100%;
698
784
  height: 100%;
@@ -724,13 +810,13 @@ svg.react-flow__connectionline {
724
810
  text-transform: uppercase;
725
811
  letter-spacing: 0.06em;
726
812
  color: var(--controller-header-color);
727
- font-family: var(--font-mono, "JetBrains Mono", monospace);
813
+ font-family: var(--font-mono);
728
814
  }
729
815
  .controller-group-label {
730
816
  font-size: 11px;
731
- font-family: var(--font-mono, "JetBrains Mono", monospace);
817
+ font-family: var(--font-mono);
732
818
  font-weight: 500;
733
- color: var(--color-controller-text, #94a3b8);
819
+ color: var(--color-controller-text);
734
820
  white-space: nowrap;
735
821
  margin-left: auto;
736
822
  }
@@ -742,23 +828,23 @@ svg.react-flow__connectionline {
742
828
  bottom: 4px;
743
829
  right: 8px;
744
830
  font-size: 10px;
745
- font-family: var(--font-mono, "JetBrains Mono", monospace);
746
- color: var(--controller-header-color, #94a3b8);
831
+ font-family: var(--font-mono);
832
+ color: var(--controller-header-color);
747
833
  cursor: pointer;
748
834
  padding: 2px 8px;
749
835
  border-radius: 4px;
750
- border: 1px dashed rgba(255, 255, 255, 0.15);
836
+ border: 1px dashed var(--border-dashed);
751
837
  pointer-events: auto;
752
838
  z-index: 10;
753
839
  white-space: nowrap;
754
840
  }
755
841
  .controller-group-collapse:hover {
756
- color: #e2e8f0;
757
- border-color: rgba(255, 255, 255, 0.35);
758
- background: rgba(255, 255, 255, 0.05);
842
+ color: var(--text-primary);
843
+ border-color: var(--border-strong);
844
+ background: var(--surface-elevated);
759
845
  }
760
846
  .controller-group-collapse:focus-visible {
761
- outline: 2px solid var(--controller-header-color, #94a3b8);
847
+ outline: 2px solid var(--controller-header-color);
762
848
  outline-offset: 2px;
763
849
  }
764
850
 
@@ -768,73 +854,64 @@ svg.react-flow__connectionline {
768
854
  cursor: pointer;
769
855
  font-size: 20px;
770
856
  line-height: 1;
771
- color: var(--controller-header-color, #94a3b8);
857
+ color: var(--controller-header-color);
772
858
  padding: 4px 10px;
773
859
  border-radius: 6px;
774
860
  pointer-events: auto;
775
861
  margin-left: auto;
776
862
  }
777
863
  .controller-group-fold:hover {
778
- color: #e2e8f0;
779
- background: rgba(255, 255, 255, 0.06);
864
+ color: var(--text-primary);
865
+ background: var(--surface-elevated);
780
866
  }
781
867
  .controller-group-fold:focus-visible {
782
- outline: 2px solid var(--controller-header-color, #94a3b8);
868
+ outline: 2px solid var(--controller-header-color);
783
869
  outline-offset: 2px;
784
870
  }
785
871
 
786
872
  /* Sequence — solid thin border, calm flow */
787
873
  .controller-group--sequence {
788
- --controller-border: 1.5px solid rgba(148, 163, 184, 0.25);
789
- --controller-bg: rgba(148, 163, 184, 0.03);
790
- --controller-header-color: #94a3b8;
874
+ --controller-border: 1.5px solid var(--ctrl-sequence-border);
875
+ --controller-bg: var(--ctrl-sequence-bg);
876
+ --controller-header-color: var(--ctrl-sequence-text);
791
877
  }
792
878
 
793
879
  /* Parallel — double border, forking lanes */
794
880
  .controller-group--parallel {
795
- --controller-border: 3px double rgba(139, 233, 253, 0.35);
796
- --controller-bg: rgba(139, 233, 253, 0.03);
797
- --controller-header-color: #8be9fd;
881
+ --controller-border: 3px double var(--ctrl-parallel-border);
882
+ --controller-bg: var(--ctrl-parallel-bg);
883
+ --controller-header-color: var(--ctrl-parallel-text);
798
884
  }
799
885
 
800
886
  /* Condition — dashed with diamond accent */
801
887
  .controller-group--condition {
802
- --controller-border: 2px dashed rgba(251, 191, 36, 0.35);
803
- --controller-bg: rgba(251, 191, 36, 0.03);
804
- --controller-header-color: #fbbf24;
888
+ --controller-border: 2px dashed var(--ctrl-condition-border);
889
+ --controller-bg: var(--ctrl-condition-bg);
890
+ --controller-header-color: var(--ctrl-condition-text);
805
891
  }
806
892
 
807
893
  /* Batch — dotted border, loop/repeat */
808
894
  .controller-group--batch {
809
- --controller-border: 2px dotted rgba(167, 139, 250, 0.35);
810
- --controller-bg: rgba(167, 139, 250, 0.03);
811
- --controller-header-color: #a78bfa;
895
+ --controller-border: 2px dotted var(--ctrl-batch-border);
896
+ --controller-bg: var(--ctrl-batch-bg);
897
+ --controller-header-color: var(--ctrl-batch-text);
812
898
  }
813
899
 
814
900
  /* ─── Pipe Card Node ─────────────────────────────────────────────────── */
815
- /* All tokens overridable via CSS custom properties on an ancestor. */
816
901
  .pipe-card {
817
- --pipe-card-accent: #ff6b6b;
818
- --pipe-card-bg: rgba(148, 163, 184, 0.08);
819
- --pipe-card-text: #e2e8f0;
820
- --pipe-card-text-bright: #f8fafc;
821
- --pipe-card-text-muted: #94a3b8;
822
- --pipe-card-text-dim: #64748b;
823
- --pipe-card-pill-bg: rgba(255, 255, 255, 0.06);
824
- --pipe-card-pill-border: rgba(255, 255, 255, 0.08);
825
- --pipe-card-pill-text: #cbd5e1;
902
+ --pipe-card-accent: var(--color-pipe);
826
903
  --pipe-card-radius: 8px;
827
904
 
828
905
  border-left: 4px solid var(--pipe-card-accent);
829
906
  border-radius: var(--pipe-card-radius);
830
907
  padding: 12px 14px;
831
- background: var(--pipe-card-bg);
832
- font-family: var(--font-sans, "Inter", -apple-system, sans-serif);
833
- color: var(--pipe-card-text);
908
+ background: var(--surface-elevated);
909
+ font-family: var(--font-sans);
910
+ color: var(--text-default);
834
911
  display: flex;
835
912
  flex-direction: column;
836
913
  gap: 8px;
837
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
914
+ box-shadow: var(--shadow-sm);
838
915
  transition: box-shadow 0.2s;
839
916
  /* Fill the ReactFlow wrapper whose size is locked to ELK's layout dimensions */
840
917
  width: 100%;
@@ -843,7 +920,7 @@ svg.react-flow__connectionline {
843
920
  overflow: hidden;
844
921
  }
845
922
  .pipe-card:hover {
846
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.6);
923
+ box-shadow: var(--shadow-md);
847
924
  }
848
925
 
849
926
  /* Direction-aware sizing: LR = narrow + tall, TB = wide + short */
@@ -872,10 +949,10 @@ svg.react-flow__connectionline {
872
949
  white-space: nowrap;
873
950
  line-height: 1.4;
874
951
  background: var(--pipe-card-accent);
875
- color: #0e0e0e;
952
+ color: var(--text-on-accent);
876
953
  }
877
954
  .pipe-card-code {
878
- font-family: var(--font-mono, "JetBrains Mono", monospace);
955
+ font-family: var(--font-mono);
879
956
  font-size: 13px;
880
957
  font-weight: 600;
881
958
  color: var(--pipe-card-accent);
@@ -906,29 +983,29 @@ svg.react-flow__connectionline {
906
983
  cursor: pointer;
907
984
  font-size: 20px;
908
985
  line-height: 1;
909
- color: var(--pipe-card-text-muted);
986
+ color: var(--text-muted);
910
987
  padding: 4px 8px;
911
988
  border-radius: 6px;
912
989
  flex-shrink: 0;
913
990
  }
914
991
  .pipe-card-expand:hover {
915
- color: var(--pipe-card-text-bright);
916
- background: rgba(255, 255, 255, 0.08);
992
+ color: var(--text-primary);
993
+ background: var(--surface-elevated-hover);
917
994
  }
918
995
  .pipe-card-expand:focus-visible {
919
- outline: 2px solid var(--pipe-card-text-bright);
996
+ outline: 2px solid var(--text-primary);
920
997
  outline-offset: 2px;
921
998
  }
922
999
 
923
1000
  /* Folded-controller card variant — slightly tinted badge to differentiate
924
1001
  from operator cards. */
925
1002
  .pipe-card-badge--controller {
926
- background: rgba(148, 163, 184, 0.25);
927
- color: var(--pipe-card-text-bright);
928
- border: 1px solid rgba(148, 163, 184, 0.4);
1003
+ background: var(--ctrl-folded-bg);
1004
+ color: var(--text-primary);
1005
+ border: 1px solid var(--ctrl-folded-border);
929
1006
  }
930
1007
  .pipe-card--controller {
931
- --pipe-card-accent: #94a3b8;
1008
+ --pipe-card-accent: var(--text-muted);
932
1009
  }
933
1010
  @keyframes pipe-card-pulse {
934
1011
  0%,
@@ -956,7 +1033,7 @@ svg.react-flow__connectionline {
956
1033
  TB gets 1 line (wide card, horizontal layout). Both truncate with ellipsis. */
957
1034
  .pipe-card-description {
958
1035
  font-size: 11.5px;
959
- color: var(--pipe-card-text-muted);
1036
+ color: var(--text-muted);
960
1037
  line-height: 1.4;
961
1038
  overflow: hidden;
962
1039
  text-overflow: ellipsis;
@@ -977,7 +1054,7 @@ svg.react-flow__connectionline {
977
1054
  font-weight: 700;
978
1055
  text-transform: uppercase;
979
1056
  letter-spacing: 0.06em;
980
- color: var(--pipe-card-text-dim);
1057
+ color: var(--text-dim);
981
1058
  flex-shrink: 0;
982
1059
  }
983
1060
  .pipe-card-io-pills {
@@ -990,22 +1067,22 @@ svg.react-flow__connectionline {
990
1067
  align-items: center;
991
1068
  gap: 3px;
992
1069
  font-size: 10px;
993
- background: var(--pipe-card-pill-bg);
994
- border: 1px solid var(--pipe-card-pill-border);
1070
+ background: var(--surface-pill);
1071
+ border: 1px solid var(--surface-pill-border);
995
1072
  border-radius: 4px;
996
1073
  padding: 1px 5px;
997
1074
  line-height: 1.4;
998
1075
  min-width: 0;
999
1076
  }
1000
1077
  .pipe-card-io-pill-name {
1001
- font-family: var(--font-mono, "JetBrains Mono", monospace);
1002
- color: var(--pipe-card-pill-text);
1078
+ font-family: var(--font-mono);
1079
+ color: var(--text-secondary);
1003
1080
  overflow: hidden;
1004
1081
  text-overflow: ellipsis;
1005
1082
  white-space: nowrap;
1006
1083
  }
1007
1084
  .pipe-card-io-pill-concept {
1008
- color: var(--pipe-card-text-dim);
1085
+ color: var(--text-dim);
1009
1086
  font-size: 9px;
1010
1087
  overflow: hidden;
1011
1088
  text-overflow: ellipsis;
@@ -1014,21 +1091,21 @@ svg.react-flow__connectionline {
1014
1091
  .pipe-card-io-more {
1015
1092
  all: unset;
1016
1093
  font-size: 10px;
1017
- font-family: var(--font-mono, "JetBrains Mono", monospace);
1018
- color: var(--pipe-card-text-muted);
1094
+ font-family: var(--font-mono);
1095
+ color: var(--text-muted);
1019
1096
  cursor: pointer;
1020
1097
  padding: 2px 6px;
1021
1098
  border-radius: 4px;
1022
- border: 1px dashed rgba(255, 255, 255, 0.12);
1099
+ border: 1px dashed var(--border-default);
1023
1100
  white-space: nowrap;
1024
1101
  line-height: 1.4;
1025
1102
  }
1026
1103
  .pipe-card-io-more:hover {
1027
- color: var(--pipe-card-text);
1028
- border-color: rgba(255, 255, 255, 0.25);
1104
+ color: var(--text-default);
1105
+ border-color: var(--border-strong);
1029
1106
  }
1030
1107
  .pipe-card-io-more:focus-visible {
1031
- outline: 2px solid var(--pipe-card-text);
1108
+ outline: 2px solid var(--text-default);
1032
1109
  outline-offset: 2px;
1033
1110
  }
1034
1111
 
@@ -1087,18 +1164,23 @@ svg.react-flow__connectionline {
1087
1164
  }
1088
1165
 
1089
1166
  /* ─── StuffViewer component styles ────────────────────────────────────────── */
1090
- /* Uses CSS custom properties for consumer theming. Defaults match graph-core.css. */
1167
+ /* StuffViewer is also exported standalone (used outside GraphViewer e.g.
1168
+ * docs pages, embedded previews). The theme tokens are normally applied to
1169
+ * the GraphViewer container, but when this component is rendered on its own
1170
+ * those tokens may not be set. The fallbacks below match the dark palette
1171
+ * from `graphConfig.ts` (DARK_PALETTE_COLORS) so the component always
1172
+ * renders with valid colors and fonts. */
1091
1173
 
1092
1174
  .stuff-viewer {
1093
- --sv-bg: var(--color-bg, #0a0a0a);
1094
- --sv-surface: var(--color-surface, #16213e);
1095
- --sv-surface-hover: var(--color-surface-hover, #1f2b47);
1096
- --sv-border: var(--color-border, #2a3a5a);
1097
- --sv-text: var(--color-text, #e2e8f0);
1098
- --sv-text-muted: var(--color-text-muted, #94a3b8);
1099
- --sv-accent: var(--color-accent, #3b82f6);
1100
- --sv-success: #10b981;
1101
- --sv-success-bg: rgba(16, 185, 129, 0.2);
1175
+ --sv-bg: var(--surface-page, #0a0a0a);
1176
+ --sv-surface: var(--surface-panel, #111118);
1177
+ --sv-surface-hover: var(--surface-elevated, rgba(255, 255, 255, 0.06));
1178
+ --sv-border: var(--border-default, rgba(255, 255, 255, 0.1));
1179
+ --sv-text: var(--text-default, #e2e8f0);
1180
+ --sv-text-muted: var(--text-muted, #94a3b8);
1181
+ --sv-accent: var(--color-accent, #8be9fd);
1182
+ --sv-success: var(--color-success, #50fa7b);
1183
+ --sv-success-bg: var(--color-success-bg, rgba(80, 250, 123, 0.15));
1102
1184
  --sv-radius: 4px;
1103
1185
  --sv-font-sans: var(
1104
1186
  --font-sans,
@@ -1178,7 +1260,7 @@ svg.react-flow__connectionline {
1178
1260
 
1179
1261
  .stuff-viewer-tab--active {
1180
1262
  background: var(--sv-accent);
1181
- color: #fff;
1263
+ color: var(--text-on-accent, #0e0e0e);
1182
1264
  }
1183
1265
 
1184
1266
  /* ─── Action buttons ─────────────────────────────────────────────────────── */
@@ -1394,7 +1476,31 @@ button.stuff-viewer-local-file--button:focus-visible {
1394
1476
  font-size: 13px;
1395
1477
  }
1396
1478
 
1479
+ .stuff-viewer-error {
1480
+ border: 1px solid var(--color-status-failed, #ff5555);
1481
+ border-radius: 6px;
1482
+ padding: 12px;
1483
+ background: rgba(255, 85, 85, 0.08);
1484
+ }
1485
+
1486
+ .stuff-viewer-error-title {
1487
+ color: var(--color-status-failed, #ff5555);
1488
+ font-weight: 600;
1489
+ font-size: 13px;
1490
+ margin-bottom: 4px;
1491
+ }
1492
+
1493
+ .stuff-viewer-error-detail {
1494
+ color: var(--sv-text-muted);
1495
+ font-family: var(--font-mono, monospace);
1496
+ font-size: 12px;
1497
+ white-space: pre-wrap;
1498
+ word-break: break-word;
1499
+ }
1500
+
1397
1501
  /* ─── Detail Panel — sliding side panel for node inspection ─────────────── */
1502
+ /* Token-only — all colors come from the active theme palette on the */
1503
+ /* container. See `graphConfig.ts` for token definitions. */
1398
1504
 
1399
1505
  .detail-panel {
1400
1506
  position: absolute;
@@ -1402,13 +1508,13 @@ button.stuff-viewer-local-file--button:focus-visible {
1402
1508
  right: 0;
1403
1509
  width: 380px;
1404
1510
  height: 100%;
1405
- background: #111118;
1406
- border-left: 1px solid rgba(255, 255, 255, 0.1);
1511
+ background: var(--surface-panel);
1512
+ border-left: 1px solid var(--border-default);
1407
1513
  z-index: 10;
1408
1514
  display: flex;
1409
1515
  flex-direction: column;
1410
- font-family: "Inter", -apple-system, sans-serif;
1411
- color: #e2e8f0;
1516
+ font-family: var(--font-sans);
1517
+ color: var(--text-default);
1412
1518
  overflow: hidden;
1413
1519
  transition: transform 0.2s ease;
1414
1520
  }
@@ -1445,7 +1551,8 @@ button.stuff-viewer-local-file--button:focus-visible {
1445
1551
 
1446
1552
  .detail-panel-resize-handle:hover::before,
1447
1553
  .detail-panel--dragging .detail-panel-resize-handle::before {
1448
- background: rgba(59, 130, 246, 0.5);
1554
+ background: var(--color-accent);
1555
+ opacity: 0.6;
1449
1556
  }
1450
1557
 
1451
1558
  /* Grip indicator — visible on hover */
@@ -1464,7 +1571,7 @@ button.stuff-viewer-local-file--button:focus-visible {
1464
1571
 
1465
1572
  .detail-panel-resize-handle:hover::after,
1466
1573
  .detail-panel--dragging .detail-panel-resize-handle::after {
1467
- background: rgba(59, 130, 246, 0.8);
1574
+ background: var(--color-accent);
1468
1575
  }
1469
1576
 
1470
1577
  /* Disable transition during drag for smooth resizing */
@@ -1476,33 +1583,48 @@ button.stuff-viewer-local-file--button:focus-visible {
1476
1583
  pointer-events: none;
1477
1584
  }
1478
1585
 
1586
+ /* ─── Close row — scrolls away with the content (intentionally NOT pinned) ─ */
1587
+ /* The close button is part of the content's top, not panel chrome: it
1588
+ * scrolls out of view with the content, and closing the panel means
1589
+ * scrolling back up. The negative margin tucks the following section up
1590
+ * beside it so the row doesn't cost a full line of vertical space. */
1591
+
1592
+ .detail-panel-close-row {
1593
+ display: flex;
1594
+ justify-content: flex-end;
1595
+ /* Pull the next section up by exactly this row's height + the column
1596
+ * gap, so the close button sits ON the first content line (concept /
1597
+ * pipe header), right-aligned — not on its own line above it. */
1598
+ margin-bottom: -40px;
1599
+ }
1600
+
1479
1601
  .detail-panel-close {
1480
1602
  all: unset;
1481
1603
  cursor: pointer;
1482
- position: absolute;
1483
- top: 12px;
1484
- right: 12px;
1485
1604
  width: 24px;
1486
1605
  height: 24px;
1487
1606
  display: flex;
1488
1607
  align-items: center;
1489
1608
  justify-content: center;
1490
1609
  border-radius: 4px;
1491
- color: #64748b;
1610
+ color: var(--text-dim);
1492
1611
  font-size: 18px;
1493
1612
  line-height: 1;
1494
- z-index: 3;
1613
+ /* Above the sticky pipe header (z 1) so the X is visible at rest while
1614
+ * sharing its line; on scroll it briefly slides over the stuck header's
1615
+ * empty right side before leaving the viewport. */
1616
+ z-index: 2;
1495
1617
  }
1496
1618
 
1497
1619
  .detail-panel-close:hover {
1498
- color: #94a3b8;
1499
- background: rgba(255, 255, 255, 0.06);
1620
+ color: var(--text-muted);
1621
+ background: var(--surface-elevated);
1500
1622
  }
1501
1623
 
1502
1624
  .detail-panel-content {
1503
1625
  flex: 1;
1504
1626
  overflow-y: auto;
1505
- padding: 20px 16px;
1627
+ padding: 12px 16px 20px;
1506
1628
  display: flex;
1507
1629
  flex-direction: column;
1508
1630
  gap: 16px;
@@ -1512,11 +1634,11 @@ button.stuff-viewer-local-file--button:focus-visible {
1512
1634
 
1513
1635
  .detail-sticky-header {
1514
1636
  position: sticky;
1515
- top: -20px;
1637
+ top: -12px; /* must mirror .detail-panel-content's top padding */
1516
1638
  z-index: 1;
1517
- background: #111118;
1518
- padding-top: 20px;
1519
- margin-top: -20px;
1639
+ background: var(--surface-panel);
1640
+ padding-top: 12px;
1641
+ margin-top: -12px;
1520
1642
  display: flex;
1521
1643
  flex-direction: column;
1522
1644
  gap: 16px;
@@ -1529,7 +1651,7 @@ button.stuff-viewer-local-file--button:focus-visible {
1529
1651
  font-weight: 700;
1530
1652
  text-transform: uppercase;
1531
1653
  letter-spacing: 0.06em;
1532
- color: #64748b;
1654
+ color: var(--text-dim);
1533
1655
  margin-bottom: 6px;
1534
1656
  }
1535
1657
 
@@ -1550,24 +1672,24 @@ button.stuff-viewer-local-file--button:focus-visible {
1550
1672
  }
1551
1673
 
1552
1674
  .detail-badge--operator {
1553
- background: #ff6b6b;
1554
- color: #0e0e0e;
1675
+ background: var(--color-pipe);
1676
+ color: var(--text-on-accent);
1555
1677
  }
1556
1678
 
1557
1679
  .detail-badge--controller {
1558
- background: #bd93f9;
1559
- color: #0e0e0e;
1680
+ background: var(--color-batch-item);
1681
+ color: var(--text-on-accent);
1560
1682
  }
1561
1683
 
1562
1684
  .detail-pipe-code {
1563
- font-family: "JetBrains Mono", "Monaco", monospace;
1685
+ font-family: var(--font-mono);
1564
1686
  font-size: 14px;
1565
1687
  font-weight: 600;
1566
- color: #ff6b6b;
1688
+ color: var(--color-pipe);
1567
1689
  }
1568
1690
 
1569
1691
  .detail-pipe-code--controller {
1570
- color: #bd93f9;
1692
+ color: var(--color-batch-item);
1571
1693
  }
1572
1694
 
1573
1695
  .detail-status {
@@ -1591,13 +1713,13 @@ button.stuff-viewer-local-file--button:focus-visible {
1591
1713
 
1592
1714
  .detail-duration {
1593
1715
  font-size: 11px;
1594
- color: #64748b;
1595
- font-family: "JetBrains Mono", "Monaco", monospace;
1716
+ color: var(--text-dim);
1717
+ font-family: var(--font-mono);
1596
1718
  }
1597
1719
 
1598
1720
  .detail-description {
1599
1721
  font-size: 12px;
1600
- color: #94a3b8;
1722
+ color: var(--text-muted);
1601
1723
  line-height: 1.5;
1602
1724
  }
1603
1725
 
@@ -1610,17 +1732,17 @@ button.stuff-viewer-local-file--button:focus-visible {
1610
1732
  font-size: 11px;
1611
1733
  padding: 3px 8px;
1612
1734
  border-radius: 4px;
1613
- background: rgba(255, 255, 255, 0.04);
1614
- border: 1px solid rgba(255, 255, 255, 0.06);
1735
+ background: var(--surface-sunken);
1736
+ border: 1px solid var(--border-subtle);
1615
1737
  }
1616
1738
 
1617
1739
  .detail-io-name {
1618
- font-family: "JetBrains Mono", "Monaco", monospace;
1619
- color: #e2e8f0;
1740
+ font-family: var(--font-mono);
1741
+ color: var(--text-default);
1620
1742
  }
1621
1743
 
1622
1744
  .detail-io-concept {
1623
- color: #64748b;
1745
+ color: var(--text-dim);
1624
1746
  font-size: 10px;
1625
1747
  }
1626
1748
 
@@ -1633,11 +1755,11 @@ button.stuff-viewer-local-file--button:focus-visible {
1633
1755
  /* ─── Blueprint-specific sections ───────────────────────────────────────── */
1634
1756
 
1635
1757
  .detail-prompt-block {
1636
- font-family: "JetBrains Mono", "Monaco", monospace;
1758
+ font-family: var(--font-mono);
1637
1759
  font-size: 11px;
1638
- color: #94a3b8;
1639
- background: rgba(255, 255, 255, 0.03);
1640
- border: 1px solid rgba(255, 255, 255, 0.06);
1760
+ color: var(--text-muted);
1761
+ background: var(--surface-sunken);
1762
+ border: 1px solid var(--border-subtle);
1641
1763
  border-radius: 6px;
1642
1764
  padding: 10px 12px;
1643
1765
  white-space: pre-wrap;
@@ -1662,16 +1784,16 @@ button.stuff-viewer-local-file--button:focus-visible {
1662
1784
  align-items: center;
1663
1785
  gap: 4px;
1664
1786
  font-size: 9px;
1665
- color: #64748b;
1787
+ color: var(--text-dim);
1666
1788
  padding: 2px 6px;
1667
1789
  border-radius: 3px;
1668
- background: rgba(255, 255, 255, 0.04);
1669
- border: 1px solid rgba(255, 255, 255, 0.06);
1790
+ background: var(--surface-sunken);
1791
+ border: 1px solid var(--border-subtle);
1670
1792
  transition: color 0.15s;
1671
1793
  }
1672
1794
 
1673
1795
  .detail-prompt-expand-btn:hover {
1674
- color: #94a3b8;
1796
+ color: var(--text-muted);
1675
1797
  }
1676
1798
 
1677
1799
  .detail-kv-row {
@@ -1684,13 +1806,13 @@ button.stuff-viewer-local-file--button:focus-visible {
1684
1806
  }
1685
1807
 
1686
1808
  .detail-kv-key {
1687
- color: #64748b;
1809
+ color: var(--text-dim);
1688
1810
  flex-shrink: 0; /* label stays at its natural width */
1689
1811
  }
1690
1812
 
1691
1813
  .detail-kv-value {
1692
- font-family: "JetBrains Mono", "Monaco", monospace;
1693
- color: #e2e8f0;
1814
+ font-family: var(--font-mono);
1815
+ color: var(--text-default);
1694
1816
  flex: 1 1 0;
1695
1817
  min-width: 0;
1696
1818
  text-align: right;
@@ -1707,17 +1829,17 @@ button.stuff-viewer-local-file--button:focus-visible {
1707
1829
  }
1708
1830
 
1709
1831
  .detail-field-block-label {
1710
- font-family: "JetBrains Mono", "Monaco", monospace;
1832
+ font-family: var(--font-mono);
1711
1833
  font-size: 10px;
1712
- color: #64748b;
1834
+ color: var(--text-dim);
1713
1835
  }
1714
1836
 
1715
1837
  .detail-field-block-value {
1716
- font-family: "JetBrains Mono", "Monaco", monospace;
1838
+ font-family: var(--font-mono);
1717
1839
  font-size: 11px;
1718
- color: #e2e8f0;
1719
- background: rgba(255, 255, 255, 0.03);
1720
- border: 1px solid rgba(255, 255, 255, 0.06);
1840
+ color: var(--text-default);
1841
+ background: var(--surface-sunken);
1842
+ border: 1px solid var(--border-subtle);
1721
1843
  border-radius: 6px;
1722
1844
  padding: 8px 10px;
1723
1845
  white-space: pre-wrap;
@@ -1741,20 +1863,20 @@ button.stuff-viewer-local-file--button:focus-visible {
1741
1863
  margin-top: 6px;
1742
1864
  margin-bottom: 4px;
1743
1865
  padding: 4px 0 4px 8px;
1744
- border-left: 2px solid rgba(80, 250, 123, 0.35);
1866
+ border-left: 2px solid var(--color-success);
1745
1867
  }
1746
1868
 
1747
1869
  .detail-nested-header-name {
1748
- font-family: "JetBrains Mono", "Monaco", monospace;
1870
+ font-family: var(--font-mono);
1749
1871
  font-size: 11px;
1750
1872
  font-weight: 600;
1751
- color: #e2e8f0;
1873
+ color: var(--text-default);
1752
1874
  }
1753
1875
 
1754
1876
  .detail-nested-header-meta {
1755
- font-family: "JetBrains Mono", "Monaco", monospace;
1877
+ font-family: var(--font-mono);
1756
1878
  font-size: 9px;
1757
- color: #64748b;
1879
+ color: var(--text-dim);
1758
1880
  text-transform: uppercase;
1759
1881
  letter-spacing: 0.04em;
1760
1882
  }
@@ -1770,39 +1892,39 @@ button.stuff-viewer-local-file--button:focus-visible {
1770
1892
  .detail-schema-table th {
1771
1893
  text-align: left;
1772
1894
  font-weight: 700;
1773
- color: #64748b;
1895
+ color: var(--text-dim);
1774
1896
  font-size: 9px;
1775
1897
  text-transform: uppercase;
1776
1898
  letter-spacing: 0.06em;
1777
1899
  padding: 4px 6px;
1778
- border-bottom: 1px solid rgba(255, 255, 255, 0.08);
1900
+ border-bottom: 1px solid var(--border-default);
1779
1901
  }
1780
1902
 
1781
1903
  .detail-schema-table td {
1782
1904
  padding: 4px 6px;
1783
- border-bottom: 1px solid rgba(255, 255, 255, 0.04);
1784
- color: #94a3b8;
1905
+ border-bottom: 1px solid var(--border-subtle);
1906
+ color: var(--text-muted);
1785
1907
  }
1786
1908
 
1787
1909
  .detail-schema-field {
1788
- font-family: "JetBrains Mono", "Monaco", monospace;
1789
- color: #e2e8f0;
1910
+ font-family: var(--font-mono);
1911
+ color: var(--text-default);
1790
1912
  }
1791
1913
 
1792
1914
  .detail-schema-type {
1793
- color: #8be9fd;
1915
+ color: var(--color-accent);
1794
1916
  }
1795
1917
 
1796
1918
  .detail-schema-required {
1797
- color: #ff6b6b;
1919
+ color: var(--color-pipe);
1798
1920
  font-size: 9px;
1799
1921
  }
1800
1922
 
1801
1923
  /* ─── Error section ─────────────────────────────────────────────────────── */
1802
1924
 
1803
1925
  .detail-error {
1804
- background: rgba(255, 85, 85, 0.08);
1805
- border: 1px solid rgba(255, 85, 85, 0.2);
1926
+ background: var(--color-error-bg);
1927
+ border: 1px solid var(--color-error-border);
1806
1928
  border-radius: 6px;
1807
1929
  padding: 10px 12px;
1808
1930
  }
@@ -1810,20 +1932,20 @@ button.stuff-viewer-local-file--button:focus-visible {
1810
1932
  .detail-error-type {
1811
1933
  font-size: 12px;
1812
1934
  font-weight: 600;
1813
- color: #ff5555;
1935
+ color: var(--color-error);
1814
1936
  margin-bottom: 4px;
1815
1937
  }
1816
1938
 
1817
1939
  .detail-error-message {
1818
1940
  font-size: 11px;
1819
- color: #94a3b8;
1941
+ color: var(--text-muted);
1820
1942
  line-height: 1.5;
1821
1943
  }
1822
1944
 
1823
1945
  .detail-error-stack {
1824
- font-family: "JetBrains Mono", "Monaco", monospace;
1946
+ font-family: var(--font-mono);
1825
1947
  font-size: 10px;
1826
- color: #64748b;
1948
+ color: var(--text-dim);
1827
1949
  margin-top: 8px;
1828
1950
  max-height: 150px;
1829
1951
  overflow-y: auto;
@@ -1846,20 +1968,20 @@ button.stuff-viewer-local-file--button:focus-visible {
1846
1968
  font-size: 11px;
1847
1969
  padding: 4px 8px;
1848
1970
  border-radius: 4px;
1849
- background: rgba(255, 255, 255, 0.03);
1850
- border: 1px solid rgba(255, 255, 255, 0.06);
1971
+ background: var(--surface-sunken);
1972
+ border: 1px solid var(--border-subtle);
1851
1973
  }
1852
1974
 
1853
1975
  .detail-step-index {
1854
- color: #64748b;
1976
+ color: var(--text-dim);
1855
1977
  font-size: 10px;
1856
1978
  font-weight: 700;
1857
1979
  min-width: 16px;
1858
1980
  }
1859
1981
 
1860
1982
  .detail-step-code {
1861
- font-family: "JetBrains Mono", "Monaco", monospace;
1862
- color: #e2e8f0;
1983
+ font-family: var(--font-mono);
1984
+ color: var(--text-default);
1863
1985
  }
1864
1986
 
1865
1987
  /* ─── Tags/Metrics ──────────────────────────────────────────────────────── */
@@ -1874,53 +1996,96 @@ button.stuff-viewer-local-file--button:focus-visible {
1874
1996
  font-size: 10px;
1875
1997
  padding: 2px 6px;
1876
1998
  border-radius: 3px;
1877
- background: rgba(255, 255, 255, 0.04);
1878
- border: 1px solid rgba(255, 255, 255, 0.06);
1999
+ background: var(--surface-sunken);
2000
+ border: 1px solid var(--border-subtle);
1879
2001
  }
1880
2002
 
1881
2003
  .detail-tag-key {
1882
- color: #64748b;
2004
+ color: var(--text-dim);
1883
2005
  }
1884
2006
 
1885
2007
  .detail-tag-value {
1886
- color: #e2e8f0;
1887
- font-family: "JetBrains Mono", "Monaco", monospace;
2008
+ color: var(--text-default);
2009
+ font-family: var(--font-mono);
2010
+ }
2011
+
2012
+ /* ─── Detail tabs (Data / Structure) ────────────────────────────────────── */
2013
+
2014
+ .detail-tabs {
2015
+ display: flex;
2016
+ gap: 2px;
2017
+ border-bottom: 1px solid var(--border-default);
2018
+ }
2019
+
2020
+ .detail-tab {
2021
+ all: unset;
2022
+ cursor: pointer;
2023
+ font-size: 11px;
2024
+ font-weight: 600;
2025
+ color: var(--text-dim);
2026
+ padding: 5px 10px;
2027
+ border-radius: 4px 4px 0 0;
2028
+ border-bottom: 2px solid transparent;
2029
+ margin-bottom: -1px;
2030
+ transition:
2031
+ color 0.15s,
2032
+ border-color 0.15s;
2033
+ }
2034
+
2035
+ .detail-tab:hover {
2036
+ color: var(--text-muted);
2037
+ }
2038
+
2039
+ .detail-tab--active {
2040
+ color: var(--text-default);
2041
+ border-bottom-color: var(--color-accent);
2042
+ }
2043
+
2044
+ /* `all: unset` strips the UA focus ring — restore a visible indicator for
2045
+ * keyboard users (WCAG 2.4.7) on every unset button in the panel. */
2046
+ .detail-tab:focus-visible,
2047
+ .detail-panel-close:focus-visible,
2048
+ .detail-prompt-expand-btn:focus-visible {
2049
+ outline: 2px solid var(--color-accent);
2050
+ outline-offset: 2px;
1888
2051
  }
1889
2052
 
1890
2053
  /* ─── Concept panel header ──────────────────────────────────────────────── */
1891
2054
 
1892
2055
  .detail-concept-code {
1893
- font-family: "JetBrains Mono", "Monaco", monospace;
2056
+ font-family: var(--font-mono);
1894
2057
  font-size: 14px;
1895
2058
  font-weight: 600;
1896
- color: #50fa7b;
2059
+ color: var(--color-success);
1897
2060
  }
1898
2061
 
1899
2062
  .detail-concept-domain {
1900
2063
  font-size: 11px;
1901
- color: #64748b;
2064
+ color: var(--text-dim);
1902
2065
  }
1903
2066
 
1904
2067
  .detail-refines {
1905
2068
  font-size: 11px;
1906
- color: #94a3b8;
2069
+ color: var(--text-muted);
1907
2070
  }
1908
2071
 
1909
2072
  .detail-refines-code {
1910
- font-family: "JetBrains Mono", "Monaco", monospace;
1911
- color: #8be9fd;
2073
+ font-family: var(--font-mono);
2074
+ color: var(--color-accent);
1912
2075
  }
1913
2076
 
1914
2077
  /* ─── Not available fallback ────────────────────────────────────────────── */
1915
2078
 
1916
2079
  .detail-not-available {
1917
2080
  font-size: 12px;
1918
- color: #64748b;
2081
+ color: var(--text-dim);
1919
2082
  font-style: italic;
1920
2083
  padding: 8px 0;
1921
2084
  }
1922
2085
 
1923
2086
  /* ─── Graph Toolbar — floating controls over the graph background ─────── */
2087
+ /* Token-only styling: theme switching happens by swapping `--surface-*`, */
2088
+ /* `--text-*`, `--border-*`, and `--color-accent` values on the container. */
1924
2089
 
1925
2090
  .graph-toolbar {
1926
2091
  position: absolute;
@@ -1942,9 +2107,9 @@ button.stuff-viewer-local-file--button:focus-visible {
1942
2107
  align-items: center;
1943
2108
  justify-content: center;
1944
2109
  border-radius: 6px;
1945
- background: rgba(17, 17, 24, 0.8);
1946
- border: 1px solid rgba(255, 255, 255, 0.1);
1947
- color: #cbd5e1;
2110
+ background: var(--surface-overlay);
2111
+ border: 1px solid var(--border-default);
2112
+ color: var(--text-secondary);
1948
2113
  backdrop-filter: blur(6px);
1949
2114
  transition:
1950
2115
  background 0.15s,
@@ -1953,46 +2118,42 @@ button.stuff-viewer-local-file--button:focus-visible {
1953
2118
  }
1954
2119
 
1955
2120
  .graph-toolbar-btn:hover {
1956
- background: rgba(30, 30, 40, 0.9);
1957
- color: #f1f5f9;
1958
- border-color: rgba(255, 255, 255, 0.18);
2121
+ background: var(--surface-overlay-hover);
2122
+ color: var(--text-primary);
2123
+ border-color: var(--border-strong);
1959
2124
  }
1960
2125
 
1961
2126
  .graph-toolbar-btn:focus-visible {
1962
- outline: 2px solid rgba(59, 130, 246, 0.6);
2127
+ outline: 2px solid var(--focus-ring);
1963
2128
  outline-offset: 1px;
1964
2129
  }
1965
2130
 
1966
2131
  .graph-toolbar-btn--active {
1967
- background: #3b82f6;
1968
- border-color: #3b82f6;
1969
- color: #ffffff;
2132
+ background: var(--color-accent);
2133
+ border-color: var(--color-accent);
2134
+ color: var(--text-on-accent);
1970
2135
  }
1971
2136
 
1972
2137
  .graph-toolbar-btn--active:hover {
1973
- background: #2563eb;
1974
- border-color: #2563eb;
1975
- color: #ffffff;
2138
+ background: var(--color-accent);
2139
+ border-color: var(--color-accent);
2140
+ color: var(--text-on-accent);
2141
+ filter: brightness(1.1);
1976
2142
  }
1977
2143
 
1978
- .graph-toolbar-btn:disabled {
2144
+ .graph-toolbar-btn:disabled,
2145
+ .graph-toolbar-btn:disabled:hover {
1979
2146
  opacity: 0.4;
1980
2147
  cursor: not-allowed;
1981
- background: rgba(17, 17, 24, 0.6);
1982
- color: #64748b;
1983
- border-color: rgba(255, 255, 255, 0.06);
1984
- }
1985
-
1986
- .graph-toolbar-btn:disabled:hover {
1987
- background: rgba(17, 17, 24, 0.6);
1988
- color: #64748b;
1989
- border-color: rgba(255, 255, 255, 0.06);
2148
+ background: var(--surface-overlay-disabled);
2149
+ color: var(--text-dim);
2150
+ border-color: var(--border-subtle);
1990
2151
  }
1991
2152
 
1992
2153
  .graph-toolbar-separator {
1993
2154
  width: 1px;
1994
2155
  height: 18px;
1995
- background: rgba(255, 255, 255, 0.12);
2156
+ background: var(--border-default);
1996
2157
  margin: 0 2px;
1997
2158
  }
1998
2159