@harmonia-core/ui 1.2.2 β†’ 1.2.4

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.
@@ -0,0 +1,327 @@
1
+ // lib/capacity/mode.ts
2
+ function deriveMode(field) {
3
+ var _a;
4
+ const lowCognitive = field.cognitive < 0.4;
5
+ const highCognitive = field.cognitive > 0.7;
6
+ const lowEmotional = field.emotional < 0.4;
7
+ const highEmotional = field.emotional > 0.6;
8
+ const lowTemporal = field.temporal < 0.4;
9
+ const highValence = field.valence > 0.15;
10
+ const negValence = field.valence < -0.15;
11
+ const density = lowCognitive ? "low" : highCognitive ? "high" : "medium";
12
+ const choiceLoad = lowTemporal ? "minimal" : "normal";
13
+ const guidance = lowCognitive ? "high" : lowTemporal ? "medium" : "low";
14
+ const veryLowEmotional = field.emotional < 0.15;
15
+ const motion = veryLowEmotional ? "off" : lowEmotional ? "soothing" : highEmotional && highValence ? "expressive" : "subtle";
16
+ const contrast = negValence ? "boosted" : "standard";
17
+ const focus = motion === "off" ? "default" : lowCognitive ? "guided" : !highCognitive ? "gentle" : "default";
18
+ const arousal = (_a = field.arousal) != null ? _a : 0.5;
19
+ const pace = arousal < 0.35 ? "calm" : arousal > 0.65 ? "activated" : "neutral";
20
+ return { density, guidance, motion, contrast, choiceLoad, focus, pace };
21
+ }
22
+ function deriveModeLabel(inputs) {
23
+ const { cognitive, temporal, emotional } = inputs;
24
+ if (cognitive > 0.6 && emotional > 0.6) {
25
+ return "Exploratory";
26
+ }
27
+ if (cognitive < 0.4 && temporal < 0.4) {
28
+ return "Minimal";
29
+ }
30
+ if (cognitive >= 0.55 && temporal >= 0.55) {
31
+ return "Focused";
32
+ }
33
+ return "Calm";
34
+ }
35
+ function getModeBadgeColor(label) {
36
+ switch (label) {
37
+ case "Calm":
38
+ return "oklch(0.65 0.15 220)";
39
+ // Soft blue
40
+ case "Focused":
41
+ return "oklch(0.68 0.16 45)";
42
+ // Primary rust
43
+ case "Exploratory":
44
+ return "oklch(0.65 0.2 135)";
45
+ // Toxic green
46
+ case "Minimal":
47
+ return "oklch(0.55 0.1 280)";
48
+ // Muted purple
49
+ default:
50
+ return "oklch(0.5 0 0)";
51
+ }
52
+ }
53
+
54
+ // lib/capacity/validation.ts
55
+ function detectConflicts(field) {
56
+ var _a;
57
+ const conflicts = [];
58
+ const arousal = (_a = field.arousal) != null ? _a : 0.5;
59
+ if (field.emotional < 0.15 && arousal > 0.65) {
60
+ conflicts.push({
61
+ id: "dead-pace",
62
+ severity: "info",
63
+ label: "Pace has no effect",
64
+ message: "Arousal is high (pace: activated) but emotional capacity has disabled all animations. The pace multiplier runs on nothing.",
65
+ affectedTokens: ["motion", "pace"],
66
+ suggestion: "Lower arousal below 0.65, or raise emotional capacity above 0.15 to let pace take effect."
67
+ });
68
+ }
69
+ if (arousal > 0.7 && field.emotional < 0.3 && !conflicts.find((c) => c.id === "dead-pace")) {
70
+ conflicts.push({
71
+ id: "anxiety-pattern",
72
+ severity: "warning",
73
+ label: "Anxiety pattern detected",
74
+ message: "High arousal with low emotional capacity signals an overwhelm/anxiety state. The UI is protective (slow or static motion) but internal pace is fast \u2014 these work against each other.",
75
+ affectedTokens: ["motion", "pace"],
76
+ suggestion: "Lower arousal to match emotional capacity, or raise emotional capacity if the high energy is intentional."
77
+ });
78
+ }
79
+ if (field.cognitive > 0.75 && field.temporal < 0.2) {
80
+ conflicts.push({
81
+ id: "density-choice-inversion",
82
+ severity: "info",
83
+ label: "Dense content, minimal choices",
84
+ message: "High cognitive capacity requests full information density, but low temporal capacity minimises available choices. Content will be rich but most actions will be hidden.",
85
+ affectedTokens: ["density", "choiceLoad", "guidance"]
86
+ });
87
+ }
88
+ if (field.valence > 0.5 && field.emotional < 0.15) {
89
+ conflicts.push({
90
+ id: "mute-expressiveness",
91
+ severity: "info",
92
+ label: "Positive tone, no motion",
93
+ message: "Emotional valence is strongly positive, but emotional capacity has disabled all animations. The expressive tone cannot be conveyed through motion.",
94
+ affectedTokens: ["motion", "contrast"],
95
+ suggestion: "Raise emotional capacity above 0.15 to allow at least soothing motion."
96
+ });
97
+ }
98
+ return conflicts;
99
+ }
100
+
101
+ // lib/capacity/animation.ts
102
+ var ENTRANCE_PRESETS = {
103
+ /** Liquid organic morph -> gentle scale fade -> soft bloom -> none */
104
+ morph: { expressive: "morph-fade-in", subtle: "sacred-fade", soothing: "bloom", off: "" },
105
+ /** Spinning vortex -> gentle scale fade -> soft bloom -> none */
106
+ vortex: { expressive: "vortex-reveal", subtle: "sacred-fade", soothing: "bloom", off: "" },
107
+ /** Spiral in from corner -> soft bloom -> soft bloom -> none */
108
+ spiral: { expressive: "spiral-in", subtle: "bloom", soothing: "bloom", off: "" }
109
+ };
110
+ function entranceClass(motion, preset, hasPlayed) {
111
+ if (hasPlayed) return "";
112
+ return ENTRANCE_PRESETS[preset][motion];
113
+ }
114
+ function hoverClass(motion) {
115
+ if (motion === "expressive") return "hover-expand";
116
+ if (motion === "subtle" || motion === "soothing") return "hover-lift";
117
+ return "";
118
+ }
119
+ function ambientClass(motion, type) {
120
+ if (motion === "expressive") return type;
121
+ if (motion === "soothing" && (type === "breathe" || type === "float")) return type;
122
+ return "";
123
+ }
124
+ function listItemClass(motion) {
125
+ if (motion === "expressive") return "helix-rise";
126
+ if (motion === "subtle" || motion === "soothing") return "sacred-fade";
127
+ return "";
128
+ }
129
+ function focusBeaconClass(focus) {
130
+ if (focus === "guided") return "attention-beacon focus-highlight";
131
+ if (focus === "gentle") return "gentle-beacon gentle-highlight";
132
+ return "";
133
+ }
134
+ function focusTextClass(focus) {
135
+ if (focus === "guided") return "attention-text";
136
+ if (focus === "gentle") return "gentle-text";
137
+ return "";
138
+ }
139
+
140
+ // lib/capacity/constants.ts
141
+ var PHI = 1.618033988749895;
142
+ var PHI_INVERSE = 0.618033988749895;
143
+ var FIBONACCI = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144];
144
+ var FEEDBACK_FREQUENCIES = {
145
+ low: 396,
146
+ // Foundation/root elements
147
+ mid: 528,
148
+ // Primary interactive content
149
+ high: 741
150
+ // Dynamic/feedback elements
151
+ };
152
+ var DEFAULT_CAPACITY_FIELD = {
153
+ cognitive: 0.5,
154
+ temporal: 0.5,
155
+ emotional: 0.5,
156
+ valence: 0
157
+ };
158
+ var DEFAULT_COMPONENT_RESPONSE = {
159
+ visual: {
160
+ opacityRange: [0.4, 1],
161
+ scaleRange: [0.95, 1]
162
+ },
163
+ spatial: {
164
+ densityRange: [0.6, 1],
165
+ spacingMultiplier: [1, PHI]
166
+ },
167
+ sonic: {
168
+ enabled: false
169
+ // Opt-in
170
+ },
171
+ semantic: {
172
+ verbosityLevel: "concise",
173
+ urgencyFraming: "neutral"
174
+ }
175
+ };
176
+ var MOTION_TOKENS = {
177
+ off: {
178
+ durationFast: 0,
179
+ durationBase: 0,
180
+ durationSlow: 0,
181
+ easing: "linear",
182
+ // Essential transitions still allowed (opacity, focus rings)
183
+ essentialDuration: 100,
184
+ essentialEasing: "ease-out"
185
+ },
186
+ soothing: {
187
+ durationFast: 0,
188
+ // No fast motion -- everything is slow and rhythmic
189
+ durationBase: 800,
190
+ durationSlow: 1200,
191
+ easing: "ease-in-out",
192
+ // Smooth, no sharp edges
193
+ essentialDuration: 200,
194
+ essentialEasing: "ease-in-out"
195
+ },
196
+ subtle: {
197
+ durationFast: 100,
198
+ durationBase: 200,
199
+ durationSlow: 350,
200
+ easing: "ease-out",
201
+ essentialDuration: 150,
202
+ essentialEasing: "ease-out"
203
+ },
204
+ expressive: {
205
+ durationFast: 200,
206
+ durationBase: 400,
207
+ durationSlow: 700,
208
+ easing: "cubic-bezier(0.34, 1.56, 0.64, 1)",
209
+ // Spring-like overshoot
210
+ essentialDuration: 150,
211
+ essentialEasing: "ease-out"
212
+ }
213
+ };
214
+
215
+ // lib/capacity/utils/typography.ts
216
+ var BASE_FONT_SIZE = 16;
217
+ var MIN_FONT_SIZE = 14;
218
+ var JITTER_FACTOR = 0.05;
219
+ var SCALE_STEPS = {
220
+ h1: 4,
221
+ // Ο†^4 β‰ˆ 6.85x base
222
+ h2: 3,
223
+ // Ο†^3 β‰ˆ 4.24x base
224
+ h3: 2,
225
+ // Ο†^2 β‰ˆ 2.62x base
226
+ h4: 1,
227
+ // Ο†^1 β‰ˆ 1.62x base
228
+ body: 0,
229
+ // Ο†^0 = 1x base
230
+ label: -0.5,
231
+ // Ο†^-0.5 β‰ˆ 0.79x base
232
+ caption: -1
233
+ // Ο†^-1 β‰ˆ 0.62x base
234
+ };
235
+ var ENERGY_BIAS = {
236
+ low: 1.05,
237
+ // +5% for better readability when tired
238
+ medium: 1,
239
+ // Neutral
240
+ high: 0.95
241
+ // -5% for higher density when alert
242
+ };
243
+ var ATTENTION_WEIGHT = {
244
+ low: 400,
245
+ // Regular
246
+ medium: 450,
247
+ // Medium
248
+ high: 500
249
+ // Medium-bold for focus
250
+ };
251
+ var ATTENTION_TRACKING = {
252
+ low: 0.02,
253
+ // Loose tracking for comfortable reading
254
+ medium: 0,
255
+ // Normal
256
+ high: -0.01
257
+ // Tight tracking for focus
258
+ };
259
+ function modularScale(step, base = BASE_FONT_SIZE) {
260
+ return base * Math.pow(PHI, step);
261
+ }
262
+ function getFontSize(role, energy = "medium", options) {
263
+ const { base = BASE_FONT_SIZE, jitter = true, minSize = MIN_FONT_SIZE } = options || {};
264
+ const step = SCALE_STEPS[role];
265
+ const baseSize = modularScale(step, base);
266
+ const jitterAmount = jitter ? (Math.random() - 0.5) * 2 * JITTER_FACTOR : 0;
267
+ const jitteredSize = baseSize * (1 + jitterAmount);
268
+ const energyAdjustedSize = jitteredSize * ENERGY_BIAS[energy];
269
+ return Math.max(energyAdjustedSize, minSize);
270
+ }
271
+ function getFontWeight(attention = "medium") {
272
+ return ATTENTION_WEIGHT[attention];
273
+ }
274
+ function getLetterSpacing(attention = "medium") {
275
+ return ATTENTION_TRACKING[attention];
276
+ }
277
+ function getLineHeight(role) {
278
+ const lineHeights = {
279
+ h1: 1.2,
280
+ h2: 1.25,
281
+ h3: 1.3,
282
+ h4: 1.35,
283
+ body: 1.5,
284
+ label: 1.4,
285
+ caption: 1.45
286
+ };
287
+ return lineHeights[role];
288
+ }
289
+ function getTypographyStyles(role, energy = "medium", attention = "medium") {
290
+ return {
291
+ fontSize: `${getFontSize(role, energy)}px`,
292
+ fontWeight: getFontWeight(attention),
293
+ lineHeight: getLineHeight(role),
294
+ letterSpacing: `${getLetterSpacing(attention)}em`
295
+ };
296
+ }
297
+ function getFluidFontSize(role, energy = "medium") {
298
+ const minSize = getFontSize(role, energy, { jitter: false });
299
+ const maxSize = minSize * 1.2;
300
+ return `clamp(${minSize}px, ${minSize}px + (${maxSize - minSize}) * ((100vw - 320px) / 1600), ${maxSize}px)`;
301
+ }
302
+ var SPACING_BASE = 4;
303
+ var SPACING_SCALE = FIBONACCI.map((f) => f * SPACING_BASE);
304
+ function getSpacing(step, unit = "px") {
305
+ const clampedStep = Math.max(0, Math.min(step, SPACING_SCALE.length - 1));
306
+ const raw = SPACING_SCALE[clampedStep];
307
+ if (unit === "raw") return raw;
308
+ if (unit === "rem") return `${(raw / 16).toFixed(4).replace(/\.?0+$/, "")}rem`;
309
+ return `${raw}px`;
310
+ }
311
+ function getProportionalSpacing(density) {
312
+ const shift = density === "low" ? -1 : density === "high" ? 1 : 0;
313
+ return {
314
+ xs: getSpacing(2 + shift),
315
+ sm: getSpacing(3 + shift),
316
+ md: getSpacing(5 + shift),
317
+ lg: getSpacing(7 + shift),
318
+ gap: getSpacing(4 + shift)
319
+ };
320
+ }
321
+ function phiRatio(steps) {
322
+ return Math.pow(PHI, steps);
323
+ }
324
+
325
+ export { DEFAULT_CAPACITY_FIELD, DEFAULT_COMPONENT_RESPONSE, FEEDBACK_FREQUENCIES, FIBONACCI, MOTION_TOKENS, PHI, PHI_INVERSE, SPACING_SCALE, ambientClass, deriveMode, deriveModeLabel, detectConflicts, entranceClass, focusBeaconClass, focusTextClass, getFluidFontSize, getFontSize, getFontWeight, getLetterSpacing, getLineHeight, getModeBadgeColor, getProportionalSpacing, getSpacing, getTypographyStyles, hoverClass, listItemClass, modularScale, phiRatio };
326
+ //# sourceMappingURL=server.mjs.map
327
+ //# sourceMappingURL=server.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../lib/capacity/mode.ts","../../lib/capacity/validation.ts","../../lib/capacity/animation.ts","../../lib/capacity/constants.ts","../../lib/capacity/utils/typography.ts"],"names":[],"mappings":"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","file":"server.mjs","sourcesContent":["/**\n * Mode Derivation - Field β†’ Mode transformation\n *\n * This is the key insight: don't map sliders directly to 50 UI changes.\n * Instead, derive 2-4 coherent modes and let modes drive everything.\n *\n * STRICT SEPARATION OF CONCERNS:\n * β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”\n * β”‚ Slider β”‚ Controls β”‚ Must NOT Control β”‚\n * β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€\n * β”‚ Cognitive β”‚ density, hierarchy, concurrency β”‚ tone, animation speed β”‚\n * β”‚ Temporal β”‚ content length, shortcuts, defaultsβ”‚ color, layout structure β”‚\n * β”‚ Emotional β”‚ motion restraint, friction β”‚ content importance β”‚\n * β”‚ Valence β”‚ tone, expressiveness β”‚ information volume β”‚\n * β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜\n */\n\nimport type { CapacityField, InterfaceMode, InterfaceModeLabel, ArousalMode } from \"./types\"\n\n// ============================================================================\n// Mode Derivation Rules\n// ============================================================================\n\n/**\n * Derives InterfaceMode from CapacityField\n *\n * Rules:\n * - Cognitive β†’ density (how many things compete for attention at once)\n * - Temporal β†’ content length, shortcuts (how much time the UI asks from user)\n * - Emotional β†’ motion restraint (nervous-system-safe UI, no surprises)\n * - Valence β†’ tone/expressiveness (emotional color, not information volume)\n */\nexport function deriveMode(field: CapacityField): InterfaceMode {\n const lowCognitive = field.cognitive < 0.4\n const highCognitive = field.cognitive > 0.7\n const lowEmotional = field.emotional < 0.4\n const highEmotional = field.emotional > 0.6\n const lowTemporal = field.temporal < 0.4\n const highValence = field.valence > 0.15\n const negValence = field.valence < -0.15\n\n // ═══════════════════════════════════════════════════════════════════════════\n // COGNITIVE β†’ Density, Hierarchy, Concurrency\n // Controls how many things compete for attention at once\n // Wider thresholds β†’ clearer visual jumps between modes\n // ═══════════════════════════════════════════════════════════════════════════\n const density: InterfaceMode[\"density\"] = lowCognitive\n ? \"low\"\n : highCognitive\n ? \"high\"\n : \"medium\"\n\n // ═══════════════════════════════════════════════════════════════════════════\n // TEMPORAL β†’ Content Length, Shortcuts, Defaults\n // Controls how much time the UI asks from the user\n // ═══════════════════════════════════════════════════════════════════════════\n const choiceLoad: InterfaceMode[\"choiceLoad\"] = lowTemporal ? \"minimal\" : \"normal\"\n\n // Guidance increases when temporal is low (provide shortcuts/defaults)\n // Also increases when cognitive is low (need more explanation)\n const guidance: InterfaceMode[\"guidance\"] = lowCognitive\n ? \"high\"\n : lowTemporal\n ? \"medium\"\n : \"low\"\n\n // ═══════════════════════════════════════════════════════════════════════════\n // EMOTIONAL β†’ Motion Restraint, Friction\n // Controls nervous-system-safe UI (no surprises when capacity is low)\n // Four distinct tiers:\n // off: emotional < 0.15 β†’ fully protective, static UI\n // soothing: emotional 0.15-0.4 β†’ slow rhythmic motion only (breathe, float)\n // subtle: emotional 0.4-0.6 or low valence β†’ grounded, minimal motion\n // expressive: emotional > 0.6 AND positive valence β†’ full animation suite\n // ═══════════════════════════════════════════════════════════════════════════\n const veryLowEmotional = field.emotional < 0.15\n const motion: InterfaceMode[\"motion\"] = veryLowEmotional\n ? \"off\"\n : lowEmotional\n ? \"soothing\"\n : highEmotional && highValence\n ? \"expressive\"\n : \"subtle\"\n\n // ═══════════════════════════════════════════════════════════════════════════\n // VALENCE β†’ Tone, Expressiveness (NOT information volume)\n // Controls emotional color: warmth, playfulness, accent frequency\n // ═══════════════════════════════════════════════════════════════════════════\n // Boosted contrast when mood is low helps with visual accessibility\n // This is a subtle visual adjustment, not information density\n const contrast: InterfaceMode[\"contrast\"] = negValence ? \"boosted\" : \"standard\"\n\n // ═══════════════════════════════════════════════════════════════════════════\n // COGNITIVE β†’ Focus Guidance\n // Draws attention to important elements proportional to cognitive load.\n // Three tiers:\n // guided: cognitive < 0.4 β†’ strong beacon glow + border (distracted)\n // gentle: cognitive < 0.7 β†’ soft highlight + muted glow (calm/moderate)\n // default: cognitive >= 0.7 β†’ no special treatment (focused/sharp)\n // Only activates when there IS some motion available (not \"off\"),\n // so the beacons don't appear on an already-static UI.\n // ═══════════════════════════════════════════════════════════════════════════\n const focus: InterfaceMode[\"focus\"] = motion === \"off\"\n ? \"default\"\n : lowCognitive\n ? \"guided\"\n : !highCognitive\n ? \"gentle\"\n : \"default\"\n\n // ═══════════════════════════════════════════════════════════════════════════\n // AROUSAL β†’ Animation Pacing (Phase 3)\n // Controls HOW FAST animations play, independent of motion intensity.\n // calm: arousal < 0.35 β†’ slow, deliberate pacing (+50% duration)\n // neutral: arousal 0.35–0.65 β†’ standard pacing\n // activated: arousal > 0.65 β†’ fast, energetic pacing (-35% duration)\n // ═══════════════════════════════════════════════════════════════════════════\n const arousal = field.arousal ?? 0.5\n const pace: ArousalMode = arousal < 0.35 ? \"calm\"\n : arousal > 0.65 ? \"activated\"\n : \"neutral\"\n\n return { density, guidance, motion, contrast, choiceLoad, focus, pace }\n}\n\n// ============================================================================\n// Mode Label Derivation\n// ============================================================================\n\n/**\n * Derives a human-readable mode label from raw capacity inputs\n *\n * We use RAW VALUES, not derived mode, because:\n * - Neutral (0.5, 0.5, 0.5) and Focused (0.7, 0.7, 0.6) produce the same InterfaceMode\n * - But they should have different labels (Calm vs Focused)\n * - The distinction is the RAW capacity level, not the derived mode\n *\n * Preset β†’ Label / Motion / Focus mapping:\n * - Exhausted (0.1, 0.1, 0.1) β†’ Minimal motion: off focus: default (static)\n * - Overwhelmed (0.2, 0.15, 0.2) β†’ Minimal motion: soothing focus: guided (warm beacon)\n * - Distracted (0.35, 0.25, 0.5) β†’ Minimal motion: subtle focus: guided (warm beacon)\n * - Neutral (0.5, 0.5, 0.5) β†’ Calm motion: subtle focus: gentle (cool glow)\n * - Focused (0.75, 0.75, 0.55) β†’ Focused motion: subtle focus: default\n * - Energized (0.9, 0.85, 0.85) β†’ Exploratory motion: expressive focus: default\n * - Exploring (1.0, 1.0, 1.0) β†’ Exploratory motion: expressive focus: default\n */\nexport function deriveModeLabel(inputs: CapacityField): InterfaceModeLabel {\n const { cognitive, temporal, emotional } = inputs\n\n // Exploratory: High cognitive AND high emotional capacity (energetic, engaged)\n // Threshold: both > 0.6 (lowered to capture Energized preset)\n if (cognitive > 0.6 && emotional > 0.6) {\n return \"Exploratory\"\n }\n\n // Minimal: Low capacity (cognitive AND temporal both below midpoint)\n // Threshold: both < 0.4 (raised to capture Overwhelmed + Exhausted distinctly)\n if (cognitive < 0.4 && temporal < 0.4) {\n return \"Minimal\"\n }\n\n // Focused: Good cognitive AND good temporal capacity (ready to work)\n // Threshold: both >= 0.55 (lowered slightly to capture Focused preset cleanly)\n if (cognitive >= 0.55 && temporal >= 0.55) {\n return \"Focused\"\n }\n\n // Calm: Everything else\n // Includes: Neutral (0.5s), Distracted (ok cognitive but low temporal), moderate states\n return \"Calm\"\n}\n\n// ============================================================================\n// Mode Utilities\n// ============================================================================\n\n/**\n * Get mode badge color based on label\n */\nexport function getModeBadgeColor(label: InterfaceModeLabel): string {\n switch (label) {\n case \"Calm\":\n return \"oklch(0.65 0.15 220)\" // Soft blue\n case \"Focused\":\n return \"oklch(0.68 0.16 45)\" // Primary rust\n case \"Exploratory\":\n return \"oklch(0.65 0.2 135)\" // Toxic green\n case \"Minimal\":\n return \"oklch(0.55 0.1 280)\" // Muted purple\n default:\n return \"oklch(0.5 0 0)\" // Gray\n }\n}\n","/**\n * Emotional State Conflict Detection\n *\n * Detects combinations of capacity/emotional inputs that produce contradictory\n * or meaningless derived tokens. Does not throw β€” returns warnings so the UI\n * can surface them non-intrusively.\n *\n * Conflicts are structural (inputs fight each other at the token level), not\n * value judgements. A panic state is valid input; it just produces tokens that\n * partially cancel each other out.\n */\n\nimport type { CapacityField } from \"./types\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ConflictSeverity = \"info\" | \"warning\"\n\nexport interface ConflictWarning {\n /** Stable ID β€” use for React keys and deduplication */\n id: string\n severity: ConflictSeverity\n /** Short label shown in the UI */\n label: string\n /** Full description of what's conflicting and why it matters */\n message: string\n /** Which derived tokens are affected β€” at least one required */\n affectedTokens: [string, ...string[]]\n /** Optional resolution hint shown to the user */\n suggestion?: string\n}\n\n// ============================================================================\n// Conflict Rules\n// ============================================================================\n\n/**\n * Detect conflicting emotional/capacity state combinations.\n * Returns an empty array when all inputs are coherent.\n */\nexport function detectConflicts(field: CapacityField): ConflictWarning[] {\n const conflicts: ConflictWarning[] = []\n const arousal = field.arousal ?? 0.5\n\n // ── Conflict 1: Dead pace tokens ────────────────────────────────────────\n // emotional < 0.15 disables all animations (motion = \"off\").\n // arousal > 0.65 drives pace = \"activated\" and multiplier = 0.65.\n // The multiplier applies to zero-duration animations β€” no effect.\n if (field.emotional < 0.15 && arousal > 0.65) {\n conflicts.push({\n id: \"dead-pace\",\n severity: \"info\",\n label: \"Pace has no effect\",\n message:\n \"Arousal is high (pace: activated) but emotional capacity has disabled all animations. The pace multiplier runs on nothing.\",\n affectedTokens: [\"motion\", \"pace\"],\n suggestion: \"Lower arousal below 0.65, or raise emotional capacity above 0.15 to let pace take effect.\",\n })\n }\n\n // ── Conflict 2: Anxiety/panic pattern ───────────────────────────────────\n // High physiological activation (arousal) paired with low emotional capacity\n // produces a protective, near-static UI (motion: soothing or off) running at\n // an internally fast pace. The experience feels frozen but wired.\n if (arousal > 0.7 && field.emotional < 0.3 && !conflicts.find(c => c.id === \"dead-pace\")) {\n conflicts.push({\n id: \"anxiety-pattern\",\n severity: \"warning\",\n label: \"Anxiety pattern detected\",\n message:\n \"High arousal with low emotional capacity signals an overwhelm/anxiety state. The UI is protective (slow or static motion) but internal pace is fast β€” these work against each other.\",\n affectedTokens: [\"motion\", \"pace\"],\n suggestion: \"Lower arousal to match emotional capacity, or raise emotional capacity if the high energy is intentional.\",\n })\n }\n\n // ── Conflict 3: Density–choice inversion ────────────────────────────────\n // High cognitive drives density = \"high\" (many items, full hierarchy).\n // Low temporal drives choiceLoad = \"minimal\" (few decisions, shortcuts).\n // Dense content with minimal choices is structurally contradictory β€” the UI\n // shows everything but hides most of the actions.\n if (field.cognitive > 0.75 && field.temporal < 0.2) {\n conflicts.push({\n id: \"density-choice-inversion\",\n severity: \"info\",\n label: \"Dense content, minimal choices\",\n message:\n \"High cognitive capacity requests full information density, but low temporal capacity minimises available choices. Content will be rich but most actions will be hidden.\",\n affectedTokens: [\"density\", \"choiceLoad\", \"guidance\"],\n })\n }\n\n // ── Conflict 4: Mute expressiveness ─────────────────────────────────────\n // Strong positive valence signals a warm, expressive tone. But very low\n // emotional capacity disables all decorative motion. The tone has no outlet.\n if (field.valence > 0.5 && field.emotional < 0.15) {\n conflicts.push({\n id: \"mute-expressiveness\",\n severity: \"info\",\n label: \"Positive tone, no motion\",\n message:\n \"Emotional valence is strongly positive, but emotional capacity has disabled all animations. The expressive tone cannot be conveyed through motion.\",\n affectedTokens: [\"motion\", \"contrast\"],\n suggestion: \"Raise emotional capacity above 0.15 to allow at least soothing motion.\",\n })\n }\n\n return conflicts\n}\n","/**\n * Capacity-aware animation class utilities.\n *\n * Centralizes the repeated pattern of selecting CSS animation classes\n * based on the current motion mode (off / subtle / expressive).\n * Each section was independently deriving the same entranceClass / hoverClass /\n * ambientClass -- this module makes it a single source of truth.\n */\n\nimport type { MotionMode, FocusMode } from \"./types\"\n\n// ============================================================================\n// Entrance animations (one-shot, runs once when section scrolls into view)\n// ============================================================================\n\n/**\n * Map of named entrance animation presets.\n * Each preset maps a MotionMode to a CSS class name from globals.css.\n * \"off\" always maps to \"\" (no animation).\n */\nconst ENTRANCE_PRESETS = {\n /** Liquid organic morph -> gentle scale fade -> soft bloom -> none */\n morph: { expressive: \"morph-fade-in\", subtle: \"sacred-fade\", soothing: \"bloom\", off: \"\" },\n /** Spinning vortex -> gentle scale fade -> soft bloom -> none */\n vortex: { expressive: \"vortex-reveal\", subtle: \"sacred-fade\", soothing: \"bloom\", off: \"\" },\n /** Spiral in from corner -> soft bloom -> soft bloom -> none */\n spiral: { expressive: \"spiral-in\", subtle: \"bloom\", soothing: \"bloom\", off: \"\" },\n} as const\n\ntype EntrancePreset = keyof typeof ENTRANCE_PRESETS\n\n/**\n * Returns the appropriate entrance animation class for the given motion mode.\n * Returns \"\" when hasPlayed is true, preventing re-render flicker.\n */\nexport function entranceClass(\n motion: MotionMode,\n preset: EntrancePreset,\n hasPlayed: boolean,\n): string {\n if (hasPlayed) return \"\"\n return ENTRANCE_PRESETS[preset][motion]\n}\n\n// ============================================================================\n// Hover animations (applied as a persistent class, triggered by :hover in CSS)\n// ============================================================================\n\n/**\n * Returns the appropriate hover animation class.\n * \"off\" mode disables hover animations entirely.\n */\nexport function hoverClass(motion: MotionMode): string {\n if (motion === \"expressive\") return \"hover-expand\"\n if (motion === \"subtle\" || motion === \"soothing\") return \"hover-lift\"\n return \"\"\n}\n\n// ============================================================================\n// Ambient animations (looping, always active while mode is expressive)\n// ============================================================================\n\n/**\n * Returns a class for continuous ambient animation (breathing, floating, etc.)\n *\n * - expressive: all ambient types active\n * - soothing: only slow, rhythmic types (breathe, float) -- calms the nervous system\n * - subtle / off: no ambient animation\n */\nexport function ambientClass(motion: MotionMode, type: \"breathe\" | \"float\" | \"pulse\" | \"vibrate\"): string {\n if (motion === \"expressive\") return type\n if (motion === \"soothing\" && (type === \"breathe\" || type === \"float\")) return type\n return \"\"\n}\n\n/**\n * Returns the appropriate animation class for list items (staggered entrance).\n * Expressive: helix-rise, Subtle: sacred-fade, Off: none.\n */\nexport function listItemClass(motion: MotionMode): string {\n if (motion === \"expressive\") return \"helix-rise\"\n if (motion === \"subtle\" || motion === \"soothing\") return \"sacred-fade\"\n return \"\"\n}\n\n// ============================================================================\n// Focus / attention-drawing classes (activated by FocusMode \"guided\" or \"gentle\")\n// ============================================================================\n\n/**\n * Returns attention-beacon class for important container elements (cards, CTAs).\n * - guided: strong warm glow (3s cycle) + border accent\n * - gentle: muted cool glow (5s cycle) + softer border\n * - default: no treatment\n */\nexport function focusBeaconClass(focus: FocusMode): string {\n if (focus === \"guided\") return \"attention-beacon focus-highlight\"\n if (focus === \"gentle\") return \"gentle-beacon gentle-highlight\"\n return \"\"\n}\n\n/**\n * Returns attention-text class for important headings / labels.\n * - guided: warm text-shadow pulse (3s)\n * - gentle: cool text-shadow pulse (5s)\n * - default: no treatment\n */\nexport function focusTextClass(focus: FocusMode): string {\n if (focus === \"guided\") return \"attention-text\"\n if (focus === \"gentle\") return \"gentle-text\"\n return \"\"\n}\n","/**\n * Capacity-Adaptive UI Constants\n *\n * Structural Principles Layer - Mathematical foundations for proportional design\n */\n\n// ============================================================================\n// Proportional Systems (Golden Ratio, Fibonacci)\n// ============================================================================\n\n/** Golden ratio Ο† */\nexport const PHI = 1.618033988749895\n\n/** Inverse golden ratio (1/Ο†) */\nexport const PHI_INVERSE = 0.618033988749895\n\n/** Fibonacci sequence for natural scaling steps */\nexport const FIBONACCI = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144] as const\n\n// ============================================================================\n// Auditory Feedback Frequencies (Hz) - Phase 3\n// ============================================================================\n\n/**\n * Frequency ranges for optional auditory feedback\n * Used for interaction confirmation and depth signaling\n * Note: These are constrained ranges, not healing claims\n */\nexport const FEEDBACK_FREQUENCIES = {\n low: 396, // Foundation/root elements\n mid: 528, // Primary interactive content\n high: 741, // Dynamic/feedback elements\n} as const\n\n// ============================================================================\n// Field Defaults\n// ============================================================================\n\n/** Default field configuration */\nexport const DEFAULT_FIELD_CONFIG = {\n smoothing: 0.15, // Exponential smoothing factor\n velocityThreshold: 0.05, // Min velocity to register as trend\n debounceMs: 100, // Debounce rapid changes\n} as const\n\n/** Default user capacity (neutral state) */\nexport const DEFAULT_USER_CAPACITY = {\n cognitive: 0.7,\n temporal: 0.7,\n emotional: 0.7,\n} as const\n\n/** Default emotional state (positive to show expressive animations) */\nexport const DEFAULT_EMOTIONAL_STATE = {\n valence: 0.3, // > 0.15 (with emotional > 0.6) triggers expressive motion mode\n arousal: 0.5,\n} as const\n\n/** Default capacity field (neutral state) */\nexport const DEFAULT_CAPACITY_FIELD = {\n cognitive: 0.5,\n temporal: 0.5,\n emotional: 0.5,\n valence: 0.0,\n} as const\n\n// ============================================================================\n// Component Response Presets\n// ============================================================================\n\n/**\n * Intelligent defaults for component responses\n * 90% of components can use these without override\n */\ninterface ComponentResponse {\n visual: {\n opacityRange: [number, number]\n scaleRange: [number, number]\n }\n spatial: {\n densityRange: [number, number]\n spacingMultiplier: [number, number]\n }\n sonic: {\n enabled: boolean\n }\n semantic: {\n verbosityLevel: string\n urgencyFraming: string\n }\n}\n\nexport const DEFAULT_COMPONENT_RESPONSE: ComponentResponse = {\n visual: {\n opacityRange: [0.4, 1.0],\n scaleRange: [0.95, 1.0],\n },\n spatial: {\n densityRange: [0.6, 1.0],\n spacingMultiplier: [1.0, PHI],\n },\n sonic: {\n enabled: false, // Opt-in\n },\n semantic: {\n verbosityLevel: \"concise\",\n urgencyFraming: \"neutral\",\n },\n} as const\n\n// ============================================================================\n// Accessibility Constants\n// ============================================================================\n\n/** Minimum contrast ratio (WCAG AA) - invariant across all states */\nexport const MIN_CONTRAST_RATIO = 4.5\n\n/** Reduced motion media query key */\nexport const PREFERS_REDUCED_MOTION = \"(prefers-reduced-motion: reduce)\"\n\n/** Maximum animation duration (ms) for time-sensitive users */\nexport const MAX_ANIMATION_DURATION_MS = 300\n\n// ============================================================================\n// Motion Tokens\n// ============================================================================\n\n/**\n * Motion tokens by mode\n * \n * \"off\" means no decorative motion, NOT no transitions at all.\n * You still keep: opacity fades, height/visibility transitions, focus transitions.\n * This preserves usability and avoids \"broken UI\" feelings.\n * \n * \"subtle\" = grounded, low-amplitude, slow easing\n * \"expressive\" = playful, elastic, higher amplitude\n */\nexport const MOTION_TOKENS = {\n off: {\n durationFast: 0,\n durationBase: 0,\n durationSlow: 0,\n easing: \"linear\",\n // Essential transitions still allowed (opacity, focus rings)\n essentialDuration: 100,\n essentialEasing: \"ease-out\",\n },\n soothing: {\n durationFast: 0, // No fast motion -- everything is slow and rhythmic\n durationBase: 800,\n durationSlow: 1200,\n easing: \"ease-in-out\", // Smooth, no sharp edges\n essentialDuration: 200,\n essentialEasing: \"ease-in-out\",\n },\n subtle: {\n durationFast: 100,\n durationBase: 200,\n durationSlow: 350,\n easing: \"ease-out\",\n essentialDuration: 150,\n essentialEasing: \"ease-out\",\n },\n expressive: {\n durationFast: 200,\n durationBase: 400,\n durationSlow: 700,\n easing: \"cubic-bezier(0.34, 1.56, 0.64, 1)\", // Spring-like overshoot\n essentialDuration: 150,\n essentialEasing: \"ease-out\",\n },\n} as const\n","/**\n * Typography Utilities - Renge (Proportional Form)\n *\n * Font scaling based on golden ratio with subtle randomness\n * and energy-based adjustments for cognitive load adaptation.\n *\n * Philosophy:\n * - Legibility first: minimum sizes and contrast are non-negotiable\n * - Ο†-based modular scale prevents arbitrary sizing\n * - Small random jitter (Β±5%) prevents mechanical rigidity\n * - Energy-based bias: low energy = larger (readability), high = smaller (density)\n */\n\nimport { PHI, FIBONACCI } from \"../constants\"\nimport type { DensityMode } from \"../types\"\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n\n/**\n * Typography roles in the UI hierarchy\n * Maps to semantic HTML elements\n */\nexport type TypographyRole = \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"body\" | \"caption\" | \"label\"\n\n/**\n * Energy levels derived from EnergyField\n * Influences sizing bias for cognitive adaptation\n */\nexport type EnergyLevel = \"low\" | \"medium\" | \"high\"\n\n/**\n * Attention levels derived from AttentionField\n * Influences weight and spacing for focus\n */\nexport type AttentionLevel = \"low\" | \"medium\" | \"high\"\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Base font size in pixels (browser default) */\nconst BASE_FONT_SIZE = 16\n\n/** Minimum font size for accessibility (WCAG) */\nconst MIN_FONT_SIZE = 14\n\n/** Maximum random jitter factor (Β±5%) */\nconst JITTER_FACTOR = 0.05\n\n/**\n * Scale steps for each typography role\n * Uses powers of Ο† for natural growth\n */\nconst SCALE_STEPS: Record<TypographyRole, number> = {\n h1: 4, // Ο†^4 β‰ˆ 6.85x base\n h2: 3, // Ο†^3 β‰ˆ 4.24x base\n h3: 2, // Ο†^2 β‰ˆ 2.62x base\n h4: 1, // Ο†^1 β‰ˆ 1.62x base\n body: 0, // Ο†^0 = 1x base\n label: -0.5, // Ο†^-0.5 β‰ˆ 0.79x base\n caption: -1, // Ο†^-1 β‰ˆ 0.62x base\n}\n\n/**\n * Energy-based size multipliers\n * Low energy: slightly larger for readability under cognitive load\n * High energy: slightly smaller for information density\n */\nconst ENERGY_BIAS: Record<EnergyLevel, number> = {\n low: 1.05, // +5% for better readability when tired\n medium: 1.0, // Neutral\n high: 0.95, // -5% for higher density when alert\n}\n\n/**\n * Attention-based font weight adjustments\n * Higher attention = bolder to aid focus\n */\nconst ATTENTION_WEIGHT: Record<AttentionLevel, number> = {\n low: 400, // Regular\n medium: 450, // Medium\n high: 500, // Medium-bold for focus\n}\n\n/**\n * Attention-based letter spacing (em units)\n * Tighter when focused, looser when relaxed\n */\nconst ATTENTION_TRACKING: Record<AttentionLevel, number> = {\n low: 0.02, // Loose tracking for comfortable reading\n medium: 0, // Normal\n high: -0.01, // Tight tracking for focus\n}\n\n// ============================================================================\n// Core Functions\n// ============================================================================\n\n/**\n * Modular scale function using golden ratio\n *\n * @param step - Power of Ο† to scale by (can be negative for smaller sizes)\n * @param base - Base size in pixels (default: 16)\n * @returns Scaled size in pixels\n *\n * Example:\n * modularScale(2) β†’ 16 * Ο†^2 β‰ˆ 41.89px\n * modularScale(-1) β†’ 16 * Ο†^-1 β‰ˆ 9.89px\n */\nexport function modularScale(step: number, base: number = BASE_FONT_SIZE): number {\n return base * Math.pow(PHI, step)\n}\n\n/**\n * Get font size with Ο†-based scaling, random jitter, and energy bias\n *\n * @param role - Typography role (h1, h2, body, etc.)\n * @param energy - Energy level from EnergyField (default: medium)\n * @param options - Optional overrides\n * @returns Font size in pixels (clamped to minimum)\n *\n * Design decisions:\n * 1. Uses Ο†-based modular scale for natural proportions\n * 2. Adds Β±5% random jitter to prevent mechanical feel\n * 3. Biases size based on energy: low = larger, high = smaller\n * 4. Always respects minimum font size for accessibility\n */\nexport function getFontSize(\n role: TypographyRole,\n energy: EnergyLevel = \"medium\",\n options?: {\n base?: number\n jitter?: boolean\n minSize?: number\n },\n): number {\n const { base = BASE_FONT_SIZE, jitter = true, minSize = MIN_FONT_SIZE } = options || {}\n\n // Calculate base scale from Ο† ratio\n const step = SCALE_STEPS[role]\n const baseSize = modularScale(step, base)\n\n // Apply random jitter (Β±5%) if enabled\n // This prevents the UI from feeling too rigid/mechanical\n const jitterAmount = jitter ? (Math.random() - 0.5) * 2 * JITTER_FACTOR : 0\n const jitteredSize = baseSize * (1 + jitterAmount)\n\n // Apply energy-based bias for cognitive adaptation\n const energyAdjustedSize = jitteredSize * ENERGY_BIAS[energy]\n\n // Clamp to minimum for accessibility\n return Math.max(energyAdjustedSize, minSize)\n}\n\n/**\n * Get font weight based on attention level\n *\n * @param attention - Attention level from AttentionField\n * @returns Font weight value (400-500)\n *\n * Higher attention = bolder text to help maintain focus\n */\nexport function getFontWeight(attention: AttentionLevel = \"medium\"): number {\n return ATTENTION_WEIGHT[attention]\n}\n\n/**\n * Get letter spacing based on attention level\n *\n * @param attention - Attention level from AttentionField\n * @returns Letter spacing in em units\n *\n * Tighter tracking when focused, looser when relaxed\n */\nexport function getLetterSpacing(attention: AttentionLevel = \"medium\"): number {\n return ATTENTION_TRACKING[attention]\n}\n\n/**\n * Get line height based on typography role\n *\n * @param role - Typography role\n * @returns Line height as unitless multiplier\n *\n * Design decisions:\n * - Headings: tighter (1.2-1.3) for visual impact\n * - Body: comfortable reading (1.5-1.6)\n * - Always maintains readability standards\n */\nexport function getLineHeight(role: TypographyRole): number {\n const lineHeights: Record<TypographyRole, number> = {\n h1: 1.2,\n h2: 1.25,\n h3: 1.3,\n h4: 1.35,\n body: 1.5,\n label: 1.4,\n caption: 1.45,\n }\n\n return lineHeights[role]\n}\n\n// ============================================================================\n// Composite Helpers\n// ============================================================================\n\n/**\n * Get complete typography styles for a role\n *\n * @param role - Typography role\n * @param energy - Energy level from EnergyField\n * @param attention - Attention level from AttentionField\n * @returns Complete CSS-in-JS typography object\n *\n * Returns all typography properties in one call for convenience\n */\nexport function getTypographyStyles(\n role: TypographyRole,\n energy: EnergyLevel = \"medium\",\n attention: AttentionLevel = \"medium\",\n) {\n return {\n fontSize: `${getFontSize(role, energy)}px`,\n fontWeight: getFontWeight(attention),\n lineHeight: getLineHeight(role),\n letterSpacing: `${getLetterSpacing(attention)}em`,\n }\n}\n\n/**\n * Get responsive font size with clamp()\n *\n * @param role - Typography role\n * @param energy - Energy level\n * @returns CSS clamp() expression for fluid typography\n *\n * Creates fluid typography that scales between viewport sizes\n * while respecting Ο†-based proportions\n */\nexport function getFluidFontSize(role: TypographyRole, energy: EnergyLevel = \"medium\"): string {\n const minSize = getFontSize(role, energy, { jitter: false })\n const maxSize = minSize * 1.2 // 20% range for fluidity\n\n // Fluid scaling between 320px and 1920px viewports\n return `clamp(${minSize}px, ${minSize}px + (${maxSize - minSize}) * ((100vw - 320px) / 1600), ${maxSize}px)`\n}\n\n// ============================================================================\n// Proportional Spacing System (Fibonacci / Golden Ratio) β€” Phase 3\n// ============================================================================\n\n/** Base spacing unit in pixels (4px = 0.25rem grid) */\nconst SPACING_BASE = 4\n\n/**\n * Fibonacci-based spacing scale.\n * Each step is a Fibonacci number Γ— SPACING_BASE (4px):\n *\n * Step | Fibonacci | px | rem\n * -----|-----------|-----|-----\n * 0 | 1 | 4 | 0.25\n * 1 | 1 | 4 | 0.25\n * 2 | 2 | 8 | 0.5\n * 3 | 3 | 12 | 0.75\n * 4 | 5 | 20 | 1.25\n * 5 | 8 | 32 | 2\n * 6 | 13 | 52 | 3.25\n * 7 | 21 | 84 | 5.25\n * 8 | 34 | 136 | 8.5\n * 9 | 55 | 220 | 13.75\n */\nexport const SPACING_SCALE = FIBONACCI.map((f) => f * SPACING_BASE)\n\n/**\n * Get spacing value from Fibonacci scale.\n *\n * @param step - Scale step (0–11, maps to FIBONACCI sequence)\n * @param unit - Return \"px\" string, \"rem\" string, or raw number (default: \"px\")\n * @returns Spacing value\n *\n * @example\n * getSpacing(4) // \"20px\" (Fibonacci[4]=5 Γ— 4)\n * getSpacing(5, \"rem\") // \"2rem\" (Fibonacci[5]=8 Γ— 4 / 16)\n * getSpacing(3, \"raw\") // 12\n */\nexport function getSpacing(\n step: number,\n unit: \"px\" | \"rem\" | \"raw\" = \"px\",\n): string | number {\n const clampedStep = Math.max(0, Math.min(step, SPACING_SCALE.length - 1))\n const raw = SPACING_SCALE[clampedStep]\n\n if (unit === \"raw\") return raw\n if (unit === \"rem\") return `${(raw / 16).toFixed(4).replace(/\\.?0+$/, \"\")}rem`\n return `${raw}px`\n}\n\n/**\n * Get proportional padding/gap values based on current density mode.\n *\n * Returns CSS-ready spacing strings using the Fibonacci scale,\n * scaled back at low density (less space) and up at high density (more breathing room).\n *\n * @param density - Current density mode from InterfaceMode\n * @returns Object of CSS spacing values for padding, gap, etc.\n */\nexport function getProportionalSpacing(density: DensityMode): {\n xs: string // Inline/tight spacing\n sm: string // Component internal padding\n md: string // Section/card padding\n lg: string // Between-section gap\n gap: string // Grid/flex gap\n} {\n // Fibonacci steps shift by density\n const shift = density === \"low\" ? -1 : density === \"high\" ? 1 : 0\n\n return {\n xs: getSpacing(2 + shift) as string,\n sm: getSpacing(3 + shift) as string,\n md: getSpacing(5 + shift) as string,\n lg: getSpacing(7 + shift) as string,\n gap: getSpacing(4 + shift) as string,\n }\n}\n\n/**\n * Get a Ο†-based size ratio for proportional component scaling.\n *\n * @param steps - Number of Ο† steps (positive = larger, negative = smaller)\n * @returns Unitless multiplier based on powers of Ο†\n *\n * @example\n * phiRatio(1) // 1.618 β€” golden ratio\n * phiRatio(-1) // 0.618 β€” inverse golden ratio\n * phiRatio(2) // 2.618 β€” φ²\n */\nexport function phiRatio(steps: number): number {\n return Math.pow(PHI, steps)\n}\n"]}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Emotional State Conflict Detection
3
+ *
4
+ * Detects combinations of capacity/emotional inputs that produce contradictory
5
+ * or meaningless derived tokens. Does not throw β€” returns warnings so the UI
6
+ * can surface them non-intrusively.
7
+ *
8
+ * Conflicts are structural (inputs fight each other at the token level), not
9
+ * value judgements. A panic state is valid input; it just produces tokens that
10
+ * partially cancel each other out.
11
+ */
12
+ import type { CapacityField } from "./types";
13
+ export type ConflictSeverity = "info" | "warning";
14
+ export interface ConflictWarning {
15
+ /** Stable ID β€” use for React keys and deduplication */
16
+ id: string;
17
+ severity: ConflictSeverity;
18
+ /** Short label shown in the UI */
19
+ label: string;
20
+ /** Full description of what's conflicting and why it matters */
21
+ message: string;
22
+ /** Which derived tokens are affected β€” at least one required */
23
+ affectedTokens: [string, ...string[]];
24
+ /** Optional resolution hint shown to the user */
25
+ suggestion?: string;
26
+ }
27
+ /**
28
+ * Detect conflicting emotional/capacity state combinations.
29
+ * Returns an empty array when all inputs are coherent.
30
+ */
31
+ export declare function detectConflicts(field: CapacityField): ConflictWarning[];
32
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../lib/capacity/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAM5C,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,SAAS,CAAA;AAEjD,MAAM,WAAW,eAAe;IAC9B,uDAAuD;IACvD,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAA;IACf,gEAAgE;IAChE,cAAc,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAA;IACrC,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,eAAe,EAAE,CAoEvE"}
@@ -1 +1 @@
1
- {"version":3,"file":"capacity-controls.d.ts","sourceRoot":"","sources":["../../lib/components/capacity-controls.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAiKH,wBAAgB,gBAAgB,4CAgR/B"}
1
+ {"version":3,"file":"capacity-controls.d.ts","sourceRoot":"","sources":["../../lib/components/capacity-controls.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAgKH,wBAAgB,gBAAgB,4CAyR/B"}
@@ -2,7 +2,7 @@
2
2
  * @harmonia-core/ui β€” Pre-built Components
3
3
  *
4
4
  * Drop-in components for the capacity system.
5
- * Requires DaisyUI for styling and `motion` for CapacityControls animations.
5
+ * Requires DaisyUI for styling.
6
6
  *
7
7
  * @example
8
8
  * import { CapacityControls, CapacityDemoCard, AmbientFieldMonitor } from "@harmonia-core/ui/components"