@startsimpli/funnels 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/package.json +9 -31
  2. package/src/api/README.md +507 -0
  3. package/src/api/adapter.ts +106 -0
  4. package/src/api/client.test.ts +640 -0
  5. package/src/api/client.ts +385 -0
  6. package/src/api/default-adapter.ts +243 -0
  7. package/src/api/index.ts +24 -0
  8. package/src/components/FilterRuleEditor/ARCHITECTURE.md +354 -0
  9. package/src/components/FilterRuleEditor/FieldSelector.tsx +91 -0
  10. package/src/components/FilterRuleEditor/FilterRuleEditor.stories.tsx +462 -0
  11. package/src/components/FilterRuleEditor/FilterRuleEditor.test.tsx +520 -0
  12. package/src/components/FilterRuleEditor/FilterRuleEditor.tsx +225 -0
  13. package/src/components/FilterRuleEditor/LogicToggle.tsx +64 -0
  14. package/src/components/FilterRuleEditor/OperatorSelector.tsx +75 -0
  15. package/src/components/FilterRuleEditor/README.md +291 -0
  16. package/src/components/FilterRuleEditor/RuleRow.tsx +246 -0
  17. package/src/components/FilterRuleEditor/ValueInputs/BooleanValueInput.tsx +54 -0
  18. package/src/components/FilterRuleEditor/ValueInputs/ChoiceValueInput.tsx +83 -0
  19. package/src/components/FilterRuleEditor/ValueInputs/DateValueInput.tsx +70 -0
  20. package/src/components/FilterRuleEditor/ValueInputs/MultiChoiceValueInput.tsx +132 -0
  21. package/src/components/FilterRuleEditor/ValueInputs/NumberValueInput.tsx +73 -0
  22. package/src/components/FilterRuleEditor/ValueInputs/TextValueInput.tsx +50 -0
  23. package/src/components/FilterRuleEditor/ValueInputs/index.ts +12 -0
  24. package/src/components/FilterRuleEditor/constants.ts +64 -0
  25. package/src/components/FilterRuleEditor/index.ts +14 -0
  26. package/src/components/FunnelCard/DESIGN.md +447 -0
  27. package/src/components/FunnelCard/FunnelCard.stories.tsx +484 -0
  28. package/src/components/FunnelCard/FunnelCard.test.ts +257 -0
  29. package/src/components/FunnelCard/FunnelCard.test.tsx +336 -0
  30. package/src/components/FunnelCard/FunnelCard.tsx +204 -0
  31. package/src/components/FunnelCard/FunnelStats.tsx +68 -0
  32. package/src/components/FunnelCard/IMPLEMENTATION_SUMMARY.md +505 -0
  33. package/src/components/FunnelCard/INSTALLATION.md +304 -0
  34. package/src/components/FunnelCard/MatchBar.tsx +49 -0
  35. package/src/components/FunnelCard/README.md +294 -0
  36. package/src/components/FunnelCard/StageIndicator.tsx +62 -0
  37. package/src/components/FunnelCard/StatusBadge.tsx +52 -0
  38. package/src/components/FunnelCard/index.ts +14 -0
  39. package/src/components/FunnelPreview/EntityCard.tsx +72 -0
  40. package/src/components/FunnelPreview/FunnelPreview.stories.tsx +227 -0
  41. package/src/components/FunnelPreview/FunnelPreview.test.tsx +316 -0
  42. package/src/components/FunnelPreview/FunnelPreview.tsx +249 -0
  43. package/src/components/FunnelPreview/LoadingPreview.tsx +60 -0
  44. package/src/components/FunnelPreview/PreviewStats.tsx +78 -0
  45. package/src/components/FunnelPreview/README.md +337 -0
  46. package/src/components/FunnelPreview/StageBreakdown.tsx +94 -0
  47. package/src/components/FunnelPreview/example.tsx +286 -0
  48. package/src/components/FunnelPreview/index.ts +14 -0
  49. package/src/components/FunnelRunHistory/COMPONENT_SUMMARY.md +246 -0
  50. package/src/components/FunnelRunHistory/FunnelRunHistory.stories.tsx +272 -0
  51. package/src/components/FunnelRunHistory/FunnelRunHistory.test.tsx +323 -0
  52. package/src/components/FunnelRunHistory/FunnelRunHistory.tsx +329 -0
  53. package/src/components/FunnelRunHistory/README.md +325 -0
  54. package/src/components/FunnelRunHistory/RunActions.tsx +168 -0
  55. package/src/components/FunnelRunHistory/RunDetailsModal.tsx +221 -0
  56. package/src/components/FunnelRunHistory/RunFilters.tsx +128 -0
  57. package/src/components/FunnelRunHistory/RunRow.tsx +122 -0
  58. package/src/components/FunnelRunHistory/RunStatusBadge.tsx +75 -0
  59. package/src/components/FunnelRunHistory/StageBreakdownList.tsx +110 -0
  60. package/src/components/FunnelRunHistory/index.ts +51 -0
  61. package/src/components/FunnelRunHistory/types.ts +40 -0
  62. package/src/components/FunnelRunHistory/utils.test.ts +126 -0
  63. package/src/components/FunnelRunHistory/utils.ts +100 -0
  64. package/src/components/FunnelStageBuilder/AddStageButton.tsx +52 -0
  65. package/src/components/FunnelStageBuilder/FunnelStageBuilder.css +413 -0
  66. package/src/components/FunnelStageBuilder/FunnelStageBuilder.stories.tsx +312 -0
  67. package/src/components/FunnelStageBuilder/FunnelStageBuilder.test.tsx +304 -0
  68. package/src/components/FunnelStageBuilder/FunnelStageBuilder.tsx +321 -0
  69. package/src/components/FunnelStageBuilder/README.md +341 -0
  70. package/src/components/FunnelStageBuilder/StageActions.test.tsx +205 -0
  71. package/src/components/FunnelStageBuilder/StageActions.tsx +126 -0
  72. package/src/components/FunnelStageBuilder/StageCard.tsx +202 -0
  73. package/src/components/FunnelStageBuilder/StageForm.tsx +262 -0
  74. package/src/components/FunnelStageBuilder/TagInput.test.tsx +178 -0
  75. package/src/components/FunnelStageBuilder/TagInput.tsx +129 -0
  76. package/src/components/FunnelStageBuilder/index.ts +21 -0
  77. package/src/components/FunnelVisualFlow/FlowLegend.tsx +77 -0
  78. package/{dist/components/index.css → src/components/FunnelVisualFlow/FunnelVisualFlow.css} +89 -13
  79. package/src/components/FunnelVisualFlow/FunnelVisualFlow.stories.tsx +254 -0
  80. package/src/components/FunnelVisualFlow/FunnelVisualFlow.test.tsx +208 -0
  81. package/src/components/FunnelVisualFlow/FunnelVisualFlow.tsx +229 -0
  82. package/src/components/FunnelVisualFlow/README.md +323 -0
  83. package/src/components/FunnelVisualFlow/StageNode.tsx +188 -0
  84. package/src/components/FunnelVisualFlow/example.tsx +227 -0
  85. package/src/components/FunnelVisualFlow/index.ts +10 -0
  86. package/src/components/index.ts +102 -0
  87. package/src/core/README.md +307 -0
  88. package/src/core/engine.test.ts +1087 -0
  89. package/src/core/engine.ts +329 -0
  90. package/src/core/evaluator.example.ts +353 -0
  91. package/src/core/evaluator.test.ts +639 -0
  92. package/src/core/evaluator.ts +261 -0
  93. package/src/core/field-resolver.example.ts +175 -0
  94. package/src/core/field-resolver.test.ts +541 -0
  95. package/src/core/field-resolver.ts +247 -0
  96. package/src/core/index.ts +34 -0
  97. package/src/core/operators.test.ts +539 -0
  98. package/src/core/operators.ts +241 -0
  99. package/src/hooks/index.ts +5 -0
  100. package/src/hooks/useDebouncedValue.ts +28 -0
  101. package/src/index.ts +155 -0
  102. package/src/store/README.md +342 -0
  103. package/src/store/create-funnel-store.test.ts +686 -0
  104. package/src/store/create-funnel-store.ts +538 -0
  105. package/src/store/index.ts +9 -0
  106. package/src/store/types.ts +294 -0
  107. package/src/stories/CrossDomain.stories.tsx +149 -0
  108. package/src/stories/Welcome.stories.tsx +81 -0
  109. package/src/stories/demo-data/index.ts +3 -0
  110. package/src/stories/demo-data/investors.ts +216 -0
  111. package/src/stories/demo-data/leads.ts +223 -0
  112. package/src/stories/demo-data/recipes.ts +217 -0
  113. package/src/test/setup.ts +5 -0
  114. package/src/types/index.ts +843 -0
  115. package/dist/client-3ESO2NHy.d.ts +0 -310
  116. package/dist/client-CZu03ACp.d.cts +0 -310
  117. package/dist/components/index.cjs +0 -3241
  118. package/dist/components/index.cjs.map +0 -1
  119. package/dist/components/index.css.map +0 -1
  120. package/dist/components/index.d.cts +0 -726
  121. package/dist/components/index.d.ts +0 -726
  122. package/dist/components/index.js +0 -3194
  123. package/dist/components/index.js.map +0 -1
  124. package/dist/core/index.cjs +0 -500
  125. package/dist/core/index.cjs.map +0 -1
  126. package/dist/core/index.d.cts +0 -359
  127. package/dist/core/index.d.ts +0 -359
  128. package/dist/core/index.js +0 -486
  129. package/dist/core/index.js.map +0 -1
  130. package/dist/hooks/index.cjs +0 -20
  131. package/dist/hooks/index.cjs.map +0 -1
  132. package/dist/hooks/index.d.cts +0 -11
  133. package/dist/hooks/index.d.ts +0 -11
  134. package/dist/hooks/index.js +0 -18
  135. package/dist/hooks/index.js.map +0 -1
  136. package/dist/index-BGDEXbuz.d.cts +0 -434
  137. package/dist/index-BGDEXbuz.d.ts +0 -434
  138. package/dist/index.cjs +0 -4499
  139. package/dist/index.cjs.map +0 -1
  140. package/dist/index.css +0 -198
  141. package/dist/index.css.map +0 -1
  142. package/dist/index.d.cts +0 -99
  143. package/dist/index.d.ts +0 -99
  144. package/dist/index.js +0 -4421
  145. package/dist/index.js.map +0 -1
  146. package/dist/store/index.cjs +0 -389
  147. package/dist/store/index.cjs.map +0 -1
  148. package/dist/store/index.d.cts +0 -225
  149. package/dist/store/index.d.ts +0 -225
  150. package/dist/store/index.js +0 -386
  151. package/dist/store/index.js.map +0 -1
@@ -0,0 +1,100 @@
1
+ /**
2
+ * FunnelRunHistory Utilities
3
+ *
4
+ * Helper functions for formatting and calculations
5
+ */
6
+
7
+ /**
8
+ * Format duration in milliseconds to human-readable string
9
+ *
10
+ * Examples:
11
+ * - 1234 → "1.2s"
12
+ * - 65000 → "1m 5s"
13
+ * - 3661000 → "1h 1m"
14
+ */
15
+ export function formatDuration(ms?: number): string {
16
+ if (ms === undefined || ms === null) return '-';
17
+ if (ms === 0) return '0ms';
18
+
19
+ const seconds = Math.floor(ms / 1000);
20
+ const minutes = Math.floor(seconds / 60);
21
+ const hours = Math.floor(minutes / 60);
22
+
23
+ if (hours > 0) {
24
+ const remainingMinutes = minutes % 60;
25
+ return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`;
26
+ }
27
+
28
+ if (minutes > 0) {
29
+ const remainingSeconds = seconds % 60;
30
+ return remainingSeconds > 0 ? `${minutes}m ${remainingSeconds}s` : `${minutes}m`;
31
+ }
32
+
33
+ if (seconds > 0) {
34
+ return `${seconds}s`;
35
+ }
36
+
37
+ return `${ms}ms`;
38
+ }
39
+
40
+ /**
41
+ * Format relative time
42
+ *
43
+ * Examples:
44
+ * - 5 minutes ago → "5m ago"
45
+ * - 2 hours ago → "2h ago"
46
+ * - Yesterday → "1d ago"
47
+ */
48
+ export function formatRelativeTime(date: Date | string): string {
49
+ const now = new Date();
50
+ const then = new Date(date);
51
+ const diffMs = now.getTime() - then.getTime();
52
+ const diffSeconds = Math.floor(diffMs / 1000);
53
+ const diffMinutes = Math.floor(diffSeconds / 60);
54
+ const diffHours = Math.floor(diffMinutes / 60);
55
+ const diffDays = Math.floor(diffHours / 24);
56
+
57
+ if (diffDays > 0) {
58
+ return `${diffDays}d ago`;
59
+ }
60
+
61
+ if (diffHours > 0) {
62
+ return `${diffHours}h ago`;
63
+ }
64
+
65
+ if (diffMinutes > 0) {
66
+ return `${diffMinutes}m ago`;
67
+ }
68
+
69
+ return 'Just now';
70
+ }
71
+
72
+ /**
73
+ * Calculate match rate percentage
74
+ */
75
+ export function calculateMatchRate(matched: number, total: number): number {
76
+ if (total === 0) return 0;
77
+ return Math.round((matched / total) * 100);
78
+ }
79
+
80
+ /**
81
+ * Format number with commas
82
+ */
83
+ export function formatNumber(num: number): string {
84
+ return num.toLocaleString();
85
+ }
86
+
87
+ /**
88
+ * Format full timestamp for tooltips
89
+ */
90
+ export function formatFullTimestamp(date: Date | string): string {
91
+ const d = new Date(date);
92
+ return d.toLocaleString('en-US', {
93
+ year: 'numeric',
94
+ month: 'short',
95
+ day: 'numeric',
96
+ hour: '2-digit',
97
+ minute: '2-digit',
98
+ second: '2-digit',
99
+ });
100
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * AddStageButton - Button to add new stages
3
+ *
4
+ * Features:
5
+ * - Different styles for top/bottom/inline positions
6
+ * - Clear visual affordance
7
+ */
8
+
9
+ import React from 'react';
10
+
11
+ export interface AddStageButtonProps {
12
+ /** Click handler */
13
+ onClick: () => void;
14
+
15
+ /** Position context */
16
+ position: 'top' | 'bottom' | 'inline';
17
+
18
+ /** Optional CSS class */
19
+ className?: string;
20
+ }
21
+
22
+ /**
23
+ * AddStageButton component
24
+ */
25
+ export function AddStageButton({
26
+ onClick,
27
+ position,
28
+ className = '',
29
+ }: AddStageButtonProps) {
30
+ return (
31
+ <button
32
+ type="button"
33
+ onClick={onClick}
34
+ className={`add-stage-button ${position} ${className}`}
35
+ aria-label={`Add stage ${position === 'top' ? 'at top' : position === 'bottom' ? 'at bottom' : 'below'}`}
36
+ >
37
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
38
+ <path
39
+ d="M10 5v10M5 10h10"
40
+ stroke="currentColor"
41
+ strokeWidth="2"
42
+ strokeLinecap="round"
43
+ />
44
+ </svg>
45
+ <span>
46
+ {position === 'top' && 'Add Stage'}
47
+ {position === 'bottom' && 'Add Stage Below'}
48
+ {position === 'inline' && 'Add Stage'}
49
+ </span>
50
+ </button>
51
+ );
52
+ }
@@ -0,0 +1,413 @@
1
+ /**
2
+ * FunnelStageBuilder styles
3
+ *
4
+ * Design principles:
5
+ * - Clean, minimal design
6
+ * - Clear visual hierarchy
7
+ * - Accessible colors and contrast
8
+ * - Responsive spacing
9
+ */
10
+
11
+ /* ============================================================================
12
+ * Main Container
13
+ * ============================================================================ */
14
+
15
+ .funnel-stage-builder {
16
+ width: 100%;
17
+ max-width: 900px;
18
+ margin: 0 auto;
19
+ }
20
+
21
+ /* ============================================================================
22
+ * Stage Card
23
+ * ============================================================================ */
24
+
25
+ .stage-card {
26
+ background: white;
27
+ border: 1px solid #e5e7eb;
28
+ border-radius: 8px;
29
+ margin-bottom: 16px;
30
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
31
+ transition: all 0.2s ease;
32
+ }
33
+
34
+ .stage-card.dragging {
35
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
36
+ cursor: grabbing;
37
+ }
38
+
39
+ .stage-card.error {
40
+ border-color: #ef4444;
41
+ }
42
+
43
+ /* ============================================================================
44
+ * Stage Header
45
+ * ============================================================================ */
46
+
47
+ .stage-header {
48
+ display: flex;
49
+ align-items: center;
50
+ gap: 12px;
51
+ padding: 16px;
52
+ border-bottom: 1px solid #f3f4f6;
53
+ }
54
+
55
+ .drag-handle {
56
+ flex-shrink: 0;
57
+ padding: 4px;
58
+ color: #9ca3af;
59
+ background: none;
60
+ border: none;
61
+ cursor: grab;
62
+ transition: color 0.2s ease;
63
+ }
64
+
65
+ .drag-handle:hover {
66
+ color: #6b7280;
67
+ }
68
+
69
+ .drag-handle:active {
70
+ cursor: grabbing;
71
+ }
72
+
73
+ .stage-title-button {
74
+ flex: 1;
75
+ display: flex;
76
+ align-items: center;
77
+ gap: 8px;
78
+ text-align: left;
79
+ background: none;
80
+ border: none;
81
+ cursor: pointer;
82
+ font-size: 16px;
83
+ padding: 4px;
84
+ transition: color 0.2s ease;
85
+ }
86
+
87
+ .stage-title-button:hover {
88
+ color: #3b82f6;
89
+ }
90
+
91
+ .stage-number {
92
+ font-weight: 600;
93
+ color: #6b7280;
94
+ }
95
+
96
+ .stage-name {
97
+ font-weight: 500;
98
+ color: #111827;
99
+ }
100
+
101
+ .expand-icon {
102
+ margin-left: auto;
103
+ transition: transform 0.2s ease;
104
+ color: #9ca3af;
105
+ }
106
+
107
+ .expand-icon.expanded {
108
+ transform: rotate(180deg);
109
+ }
110
+
111
+ .delete-button {
112
+ flex-shrink: 0;
113
+ padding: 4px;
114
+ color: #9ca3af;
115
+ background: none;
116
+ border: none;
117
+ cursor: pointer;
118
+ transition: color 0.2s ease;
119
+ }
120
+
121
+ .delete-button:hover {
122
+ color: #ef4444;
123
+ }
124
+
125
+ /* ============================================================================
126
+ * Messages
127
+ * ============================================================================ */
128
+
129
+ .error-message,
130
+ .warning-message {
131
+ display: flex;
132
+ align-items: center;
133
+ gap: 8px;
134
+ padding: 12px 16px;
135
+ font-size: 14px;
136
+ }
137
+
138
+ .error-message {
139
+ background: #fef2f2;
140
+ color: #991b1b;
141
+ border-top: 1px solid #fecaca;
142
+ }
143
+
144
+ .warning-message {
145
+ background: #fffbeb;
146
+ color: #92400e;
147
+ border-top: 1px solid #fde68a;
148
+ }
149
+
150
+ /* ============================================================================
151
+ * Stage Summary (collapsed)
152
+ * ============================================================================ */
153
+
154
+ .stage-summary {
155
+ display: flex;
156
+ flex-wrap: wrap;
157
+ gap: 16px;
158
+ padding: 16px;
159
+ font-size: 14px;
160
+ }
161
+
162
+ .summary-item {
163
+ display: flex;
164
+ gap: 4px;
165
+ }
166
+
167
+ .summary-label {
168
+ color: #6b7280;
169
+ font-weight: 500;
170
+ }
171
+
172
+ .summary-value {
173
+ color: #111827;
174
+ }
175
+
176
+ /* ============================================================================
177
+ * Stage Form (expanded)
178
+ * ============================================================================ */
179
+
180
+ .stage-form-wrapper {
181
+ padding: 16px;
182
+ border-top: 1px solid #f3f4f6;
183
+ }
184
+
185
+ .stage-form {
186
+ display: flex;
187
+ flex-direction: column;
188
+ gap: 20px;
189
+ }
190
+
191
+ /* ============================================================================
192
+ * Form Elements
193
+ * ============================================================================ */
194
+
195
+ .form-group {
196
+ display: flex;
197
+ flex-direction: column;
198
+ gap: 8px;
199
+ }
200
+
201
+ .form-label {
202
+ font-size: 14px;
203
+ font-weight: 600;
204
+ color: #374151;
205
+ }
206
+
207
+ .form-input,
208
+ .form-textarea,
209
+ .form-select {
210
+ width: 100%;
211
+ padding: 8px 12px;
212
+ border: 1px solid #d1d5db;
213
+ border-radius: 6px;
214
+ font-size: 14px;
215
+ color: #111827;
216
+ background: white;
217
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
218
+ }
219
+
220
+ .form-input:focus,
221
+ .form-textarea:focus,
222
+ .form-select:focus {
223
+ outline: none;
224
+ border-color: #3b82f6;
225
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
226
+ }
227
+
228
+ .form-input::placeholder,
229
+ .form-textarea::placeholder {
230
+ color: #9ca3af;
231
+ }
232
+
233
+ .form-textarea {
234
+ resize: vertical;
235
+ min-height: 80px;
236
+ }
237
+
238
+ .form-hint {
239
+ font-size: 13px;
240
+ color: #6b7280;
241
+ margin: 0;
242
+ }
243
+
244
+ /* ============================================================================
245
+ * Filter Logic Toggle
246
+ * ============================================================================ */
247
+
248
+ .filter-logic-toggle {
249
+ display: flex;
250
+ gap: 8px;
251
+ }
252
+
253
+ .toggle-button {
254
+ flex: 1;
255
+ padding: 10px 16px;
256
+ border: 1px solid #d1d5db;
257
+ border-radius: 6px;
258
+ background: white;
259
+ font-size: 14px;
260
+ font-weight: 600;
261
+ color: #6b7280;
262
+ cursor: pointer;
263
+ transition: all 0.2s ease;
264
+ }
265
+
266
+ .toggle-button:hover {
267
+ border-color: #9ca3af;
268
+ }
269
+
270
+ .toggle-button.active {
271
+ border-color: #3b82f6;
272
+ background: #eff6ff;
273
+ color: #1d4ed8;
274
+ }
275
+
276
+ .sr-only {
277
+ position: absolute;
278
+ width: 1px;
279
+ height: 1px;
280
+ padding: 0;
281
+ margin: -1px;
282
+ overflow: hidden;
283
+ clip: rect(0, 0, 0, 0);
284
+ white-space: nowrap;
285
+ border-width: 0;
286
+ }
287
+
288
+ /* ============================================================================
289
+ * Tag Input
290
+ * ============================================================================ */
291
+
292
+ .tag-input-container {
293
+ display: flex;
294
+ flex-wrap: wrap;
295
+ gap: 6px;
296
+ padding: 8px;
297
+ border: 1px solid #d1d5db;
298
+ border-radius: 6px;
299
+ background: white;
300
+ min-height: 42px;
301
+ }
302
+
303
+ .tag-chip {
304
+ display: flex;
305
+ align-items: center;
306
+ gap: 4px;
307
+ padding: 4px 8px;
308
+ background: #eff6ff;
309
+ color: #1e40af;
310
+ border-radius: 4px;
311
+ font-size: 13px;
312
+ font-weight: 500;
313
+ }
314
+
315
+ .tag-text {
316
+ line-height: 1;
317
+ }
318
+
319
+ .tag-remove {
320
+ display: flex;
321
+ align-items: center;
322
+ justify-content: center;
323
+ padding: 2px;
324
+ background: none;
325
+ border: none;
326
+ color: #3b82f6;
327
+ cursor: pointer;
328
+ transition: color 0.2s ease;
329
+ }
330
+
331
+ .tag-remove:hover {
332
+ color: #1d4ed8;
333
+ }
334
+
335
+ .tag-input-field {
336
+ flex: 1;
337
+ min-width: 120px;
338
+ border: none;
339
+ outline: none;
340
+ font-size: 14px;
341
+ padding: 4px;
342
+ }
343
+
344
+ .tag-input-hint {
345
+ font-size: 13px;
346
+ color: #6b7280;
347
+ margin: 4px 0 0 0;
348
+ }
349
+
350
+ /* ============================================================================
351
+ * Add Stage Button
352
+ * ============================================================================ */
353
+
354
+ .add-stage-button {
355
+ width: 100%;
356
+ display: flex;
357
+ align-items: center;
358
+ justify-content: center;
359
+ gap: 8px;
360
+ padding: 12px 16px;
361
+ border: 2px dashed #d1d5db;
362
+ border-radius: 8px;
363
+ background: white;
364
+ color: #6b7280;
365
+ font-size: 14px;
366
+ font-weight: 600;
367
+ cursor: pointer;
368
+ transition: all 0.2s ease;
369
+ }
370
+
371
+ .add-stage-button:hover {
372
+ border-color: #3b82f6;
373
+ background: #f9fafb;
374
+ color: #3b82f6;
375
+ }
376
+
377
+ /* ============================================================================
378
+ * Stage Arrow
379
+ * ============================================================================ */
380
+
381
+ .stage-arrow {
382
+ display: flex;
383
+ justify-content: center;
384
+ color: #d1d5db;
385
+ margin: -8px 0;
386
+ }
387
+
388
+ /* ============================================================================
389
+ * Empty State
390
+ * ============================================================================ */
391
+
392
+ .empty-state {
393
+ text-align: center;
394
+ padding: 32px 16px;
395
+ }
396
+
397
+ /* ============================================================================
398
+ * Rules Placeholder
399
+ * ============================================================================ */
400
+
401
+ .rules-header {
402
+ display: flex;
403
+ align-items: center;
404
+ justify-content: space-between;
405
+ }
406
+
407
+ .rules-placeholder {
408
+ padding: 24px;
409
+ border: 2px dashed #e5e7eb;
410
+ border-radius: 6px;
411
+ text-align: center;
412
+ background: #f9fafb;
413
+ }