chati-dev 4.3.0 → 4.4.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.
Files changed (156) hide show
  1. package/README.md +18 -13
  2. package/bin/chati.js +32 -4
  3. package/framework/agents/build/dev.md +9 -5
  4. package/framework/agents/discover/brief.md +2 -0
  5. package/framework/agents/discover/brownfield-wu.md +2 -0
  6. package/framework/agents/discover/greenfield-wu.md +18 -1
  7. package/framework/agents/plan/detail.md +2 -0
  8. package/framework/agents/plan/tasks.md +31 -0
  9. package/framework/agents/plan/ux-brand-architect.md +21 -2
  10. package/framework/agents/plan/ux-component-engineer.md +10 -0
  11. package/framework/agents/quality/qa-implementation.md +1 -1
  12. package/framework/agents/quality/qa-planning.md +1 -1
  13. package/framework/agents/quality/qa-visual.md +33 -21
  14. package/framework/config.yaml +5 -4
  15. package/framework/constitution.md +58 -11
  16. package/framework/context/governance.md +12 -10
  17. package/framework/context/root.md +2 -2
  18. package/framework/data/entity-registry.yaml +12 -4
  19. package/framework/data/qa-rubrics.yaml +335 -0
  20. package/framework/domains/constitution.yaml +3 -3
  21. package/framework/executors/json-validate.js +80 -0
  22. package/framework/executors/npm-script.js +72 -0
  23. package/framework/executors/path-check.js +67 -0
  24. package/framework/executors/registry.yaml +36 -0
  25. package/framework/hooks/advance-trigger.js +47 -3
  26. package/framework/hooks/constitution-guard.js +9 -4
  27. package/framework/hooks/git-push-authority.js +113 -0
  28. package/framework/hooks/license-guard.js +59 -9
  29. package/framework/hooks/model-governance.js +40 -17
  30. package/framework/hooks/prism-engine.js +78 -11
  31. package/framework/hooks/session-digest.js +12 -3
  32. package/framework/hooks/settings.json +7 -3
  33. package/framework/hooks/style-guard.js +52 -3
  34. package/framework/hooks/team-quality-gate.js +43 -5
  35. package/framework/i18n/en.yaml +3 -3
  36. package/framework/i18n/es.yaml +3 -3
  37. package/framework/i18n/fr.yaml +3 -3
  38. package/framework/i18n/pt.yaml +3 -3
  39. package/framework/intelligence/confidence.yaml +85 -6
  40. package/framework/intelligence/context-engine.md +9 -5
  41. package/framework/intelligence/decision-engine.md +1 -1
  42. package/framework/orchestrator/chati-router.js +59 -5
  43. package/framework/orchestrator/chati-update.md +19 -3
  44. package/framework/orchestrator/chati.md +20 -1
  45. package/framework/quality-gates/planning-gate.md +2 -0
  46. package/framework/rules/human-writing-style.md +47 -0
  47. package/framework/scaffold/motion-premium/README.md +12 -2
  48. package/framework/scaffold/motion-premium/lib/animations/tokens.ts.template +1 -1
  49. package/framework/scaffold/motion-premium/scaffold.yaml +6 -1
  50. package/framework/scaffold/motion-premium-3d/README.md +10 -0
  51. package/framework/scaffold/motion-premium-3d/components/webgl/WebGLContext.tsx.template +1 -1
  52. package/framework/scaffold/motion-premium-3d/scaffold.yaml +3 -0
  53. package/framework/scaffold/saas-dashboard/README.md +58 -0
  54. package/framework/scaffold/saas-dashboard/app/dashboard/page.tsx.template +546 -0
  55. package/framework/scaffold/saas-dashboard/app/globals.css.template +746 -0
  56. package/framework/scaffold/saas-dashboard/app/layout.tsx.template +192 -0
  57. package/framework/scaffold/saas-dashboard/app/shell.css.template +1070 -0
  58. package/framework/scaffold/saas-dashboard/components/data/Chart.tsx.template +725 -0
  59. package/framework/scaffold/saas-dashboard/components/data/DataTable.tsx.template +623 -0
  60. package/framework/scaffold/saas-dashboard/components/data/DataTable.types.ts.template +104 -0
  61. package/framework/scaffold/saas-dashboard/components/data/DataTablePagination.tsx.template +234 -0
  62. package/framework/scaffold/saas-dashboard/components/data/SkeletonBlock.tsx.template +111 -0
  63. package/framework/scaffold/saas-dashboard/components/data/StatCard.tsx.template +393 -0
  64. package/framework/scaffold/saas-dashboard/components/data/StatCardGrid.tsx.template +121 -0
  65. package/framework/scaffold/saas-dashboard/components/feedback/EmptyState.tsx.template +281 -0
  66. package/framework/scaffold/saas-dashboard/components/interaction/CommandPalette.tsx.template +767 -0
  67. package/framework/scaffold/saas-dashboard/components/interaction/Field.tsx.template +351 -0
  68. package/framework/scaffold/saas-dashboard/components/interaction/Form.tsx.template +424 -0
  69. package/framework/scaffold/saas-dashboard/components/primitives/Badge.tsx.template +193 -0
  70. package/framework/scaffold/saas-dashboard/components/primitives/Button.tsx.template +261 -0
  71. package/framework/scaffold/saas-dashboard/components/primitives/Card.tsx.template +287 -0
  72. package/framework/scaffold/saas-dashboard/components/primitives/Input.tsx.template +310 -0
  73. package/framework/scaffold/saas-dashboard/components/primitives/Spinner.tsx.template +135 -0
  74. package/framework/scaffold/saas-dashboard/components/primitives/index.ts.template +60 -0
  75. package/framework/scaffold/saas-dashboard/components/shell/AppShell.tsx.template +174 -0
  76. package/framework/scaffold/saas-dashboard/components/shell/Breadcrumb.tsx.template +186 -0
  77. package/framework/scaffold/saas-dashboard/components/shell/Container.tsx.template +86 -0
  78. package/framework/scaffold/saas-dashboard/components/shell/NavItem.tsx.template +147 -0
  79. package/framework/scaffold/saas-dashboard/components/shell/Sidebar.tsx.template +378 -0
  80. package/framework/scaffold/saas-dashboard/components/shell/SidebarContext.tsx.template +169 -0
  81. package/framework/scaffold/saas-dashboard/components/shell/ThemeToggle.tsx.template +128 -0
  82. package/framework/scaffold/saas-dashboard/components/shell/Topbar.tsx.template +301 -0
  83. package/framework/scaffold/saas-dashboard/lib/brand.ts.template +324 -0
  84. package/framework/scaffold/saas-dashboard/scaffold.yaml +225 -0
  85. package/framework/schemas/session.schema.json +5 -0
  86. package/framework/schemas/task.schema.json +9 -0
  87. package/framework/scripts/visual-qa.js +101 -3
  88. package/framework/tasks/orchestrator-health.md +4 -4
  89. package/framework/tasks/qa-impl-verdict.md +11 -5
  90. package/framework/templates/brandbook-html-tmpl.md +1 -1
  91. package/framework/templates/qa-gate-tmpl.yaml +24 -11
  92. package/package.json +4 -3
  93. package/src/config/context-file-generator.js +0 -6
  94. package/src/dashboard/renderer.js +0 -36
  95. package/src/executors/runner.js +204 -0
  96. package/src/installer/core.js +13 -3
  97. package/src/installer/templates.js +5 -6
  98. package/src/intelligence/registry-manager.js +1 -1
  99. package/src/license/client.js +9 -3
  100. package/src/license/commands.js +12 -2
  101. package/src/license/machine-id.js +42 -1
  102. package/src/memory/gotchas.js +58 -40
  103. package/src/memory/magic-docs.js +1 -1
  104. package/src/memory/session-digest.js +9 -4
  105. package/src/orchestrator/cli.js +186 -15
  106. package/src/orchestrator/doctor.js +98 -16
  107. package/src/orchestrator/pipeline-manager.js +44 -22
  108. package/src/orchestrator/session-manager.js +64 -8
  109. package/src/telemetry/sender.js +7 -19
  110. package/src/terminal/run-team.js +3 -3
  111. package/src/upgrade/tracked-files-detector.js +34 -16
  112. package/src/utils/feature-flags.js +1 -1
  113. package/src/utils/flatten-entities.js +4 -40
  114. package/src/utils/schema-validator.js +0 -14
  115. package/src/wizard/i18n.js +3 -3
  116. package/src/api/index.js +0 -120
  117. package/src/autonomy/autonomous-gate.js +0 -294
  118. package/src/autonomy/build-loop.js +0 -281
  119. package/src/autonomy/build-state.js +0 -286
  120. package/src/autonomy/cause-analyzer.js +0 -177
  121. package/src/autonomy/escalation.js +0 -214
  122. package/src/autonomy/index.js +0 -51
  123. package/src/autonomy/mode-manager.js +0 -225
  124. package/src/autonomy/mode-suggester.js +0 -283
  125. package/src/autonomy/progress-reporter.js +0 -275
  126. package/src/autonomy/safety-net.js +0 -370
  127. package/src/config/agent-customizer.js +0 -231
  128. package/src/decision/analyzer.js +0 -291
  129. package/src/decision/engine.js +0 -250
  130. package/src/decision/index.js +0 -38
  131. package/src/decision/registry-healer.js +0 -468
  132. package/src/decision/registry-updater.js +0 -339
  133. package/src/extensions/loader.js +0 -145
  134. package/src/extensions/registry.js +0 -134
  135. package/src/gates/circuit-breaker.js +0 -151
  136. package/src/gates/g1-planning-complete.js +0 -154
  137. package/src/gates/g2-qa-planning.js +0 -156
  138. package/src/gates/g3-implementation.js +0 -215
  139. package/src/gates/g4-qa-implementation.js +0 -240
  140. package/src/gates/g5-deploy-ready.js +0 -181
  141. package/src/gates/gate-base.js +0 -185
  142. package/src/gates/index.js +0 -46
  143. package/src/health/auto-fix.js +0 -216
  144. package/src/health/engine.js +0 -246
  145. package/src/merger/semantic-merger.js +0 -292
  146. package/src/preview/detector.js +0 -238
  147. package/src/preview/index.js +0 -20
  148. package/src/preview/launcher.js +0 -235
  149. package/src/preview/log-buffer.js +0 -103
  150. package/src/quality/metrics-collector.js +0 -281
  151. package/src/quality/test-runner.js +0 -366
  152. package/src/tasks/executor.js +0 -195
  153. package/src/tasks/index.js +0 -4
  154. package/src/tasks/loader.js +0 -210
  155. package/src/tasks/router.js +0 -182
  156. package/src/utils/event-bus.js +0 -126
@@ -0,0 +1,746 @@
1
+ /*
2
+ * app/globals.css -- saas-dashboard preset v1.0.0
3
+ *
4
+ * WHY THIS FILE EXISTS
5
+ * --------------------
6
+ * Every CSS custom property that any component in this preset may reference
7
+ * is declared exactly once here. Components never use raw hex values, raw
8
+ * pixel numbers, or bare ease keywords. The rule is: if you cannot find a
9
+ * CSS variable for the value you want, add it here first, THEN use it.
10
+ *
11
+ * ARCHITECTURE: THREE LAYERS
12
+ *
13
+ * Layer 1 -- Raw scale (neutral-0 through neutral-1000, accent raw values):
14
+ * Calibrated hex values. Nothing references these directly except
15
+ * Layer 2 semantic tokens. Changing a raw value here propagates
16
+ * everywhere that semantic token is consumed.
17
+ *
18
+ * Layer 2 -- Semantic tokens (bg-base, text-primary, border-default, etc.):
19
+ * Named by intent, not by value. Components consume only these.
20
+ * A theme switch (dark <-> light) overrides only Layer 2, in the
21
+ * [data-theme='light'] block below, without touching any component file.
22
+ *
23
+ * Layer 3 -- Component-level tokens (btn-height, input-pad, etc.):
24
+ * Derived from Layer 2. Centralise component defaults so a single
25
+ * change in :root propagates to all instances without touching TSX.
26
+ *
27
+ * MOTION SYSTEM
28
+ * -------------
29
+ * Every transition in the codebase must:
30
+ * 1. Name EXACTLY the property being transitioned (never transition:all).
31
+ * 2. Use a duration token from --duration-*.
32
+ * 3. Use an easing token from --ease-*.
33
+ * 4. Match the spatial metaphor: ease-out entering, ease-in leaving,
34
+ * ease-in-out toggling, ease-spring for delightful micro-interactions.
35
+ *
36
+ * The @media (prefers-reduced-motion: reduce) block at the bottom of this
37
+ * file collapses all durations to 0ms or 80ms and removes transform
38
+ * animations. This is the ONLY place reduced-motion is handled; components
39
+ * read it via the useReducedMotion() hook from framer-motion.
40
+ *
41
+ * TAILWIND v4 INTEGRATION
42
+ * -----------------------
43
+ * The @theme block registers tokens as Tailwind utility classes. This means
44
+ * bg-bg-base, text-text-primary, border-border-default etc. work without
45
+ * arbitrary value syntax. The xl breakpoint is overridden to 75rem (1200px)
46
+ * to eliminate the 1200-1279px dead zone on MacBook Air viewports where
47
+ * lg: applied but xl: did not, producing layout gaps on laptops.
48
+ */
49
+
50
+ @import "tailwindcss";
51
+
52
+ /* Exclude non-source directories from Tailwind v4 scanning. Non-existent paths
53
+ are a no-op in Tailwind v4, so this is safe in both a full chati.dev project
54
+ (where these dirs exist) and a plain Next.js app (where they do not). */
55
+ @source not "../artifacts";
56
+ @source not "../.chati";
57
+ @source not "../.chati.dev";
58
+ @source not "../docs";
59
+
60
+ /* ================================================================
61
+ FONT IMPORTS
62
+ Inter Variable and JetBrains Mono Variable from Google Fonts.
63
+ next/font handles this in layout.tsx; these @import declarations
64
+ are the CSS fallback for storybook / non-Next contexts.
65
+ ================================================================ */
66
+
67
+ /* In Next.js the fonts are loaded via next/font in layout.tsx.
68
+ This comment is intentionally left as a reminder; do not add
69
+ @import url() here as it conflicts with next/font's optimization. */
70
+
71
+ /* ================================================================
72
+ ROOT: DESIGN TOKEN DECLARATIONS
73
+ All custom properties for both dark (default) and light themes.
74
+ ================================================================ */
75
+
76
+ :root {
77
+ color-scheme: dark;
78
+
79
+ /* ---------------------------------------------------------------
80
+ LAYER 1: NEUTRAL SCALE
81
+ Philosophy: A single hue (220deg, blue-shifted) at 12% saturation
82
+ produces a neutral that reads as "cool grey" without drifting
83
+ toward purple or green. The 14 steps provide even perceptual
84
+ luminance increments -- verified against APCA contrast model.
85
+ --------------------------------------------------------------- */
86
+ --color-neutral-0: #ffffff; /* pure white */
87
+ --color-neutral-50: #f7f8fa; /* off-white surface */
88
+ --color-neutral-100: #eef0f4; /* subtle divider bg */
89
+ --color-neutral-200: #dde1e9; /* border default (light mode) */
90
+ --color-neutral-300: #c4cad6; /* border emphasis (light mode) */
91
+ --color-neutral-400: #9aa3b4; /* placeholder text */
92
+ --color-neutral-500: #6b7689; /* muted text */
93
+ --color-neutral-600: #4e5769; /* secondary text */
94
+ --color-neutral-700: #343d4f; /* primary text (light mode), strong border (dark) */
95
+ --color-neutral-800: #1f2635; /* card surface (dark mode) */
96
+ --color-neutral-850: #171d2b; /* sidebar/modal background */
97
+ --color-neutral-900: #0f1420; /* page background (dark mode) */
98
+ --color-neutral-950: #080c14; /* deepest dark surface */
99
+ --color-neutral-1000: #000000; /* pure black */
100
+
101
+ /* ---------------------------------------------------------------
102
+ LAYER 1: ACCENT SCALE
103
+ Violet (258deg) is used in exactly two semantic roles:
104
+ Role 1 -- interactive affordance: buttons, links, active states.
105
+ Role 2 -- brand emphasis: tinted backgrounds, selected rows.
106
+ Using it in more than two roles kills hierarchy and makes the
107
+ UI feel "purple-washed". If you need a third role, create a
108
+ new semantic token with a different hue.
109
+ --------------------------------------------------------------- */
110
+ --color-accent: {{ACCENT_HEX}}; /* role 1: interactive affordance */
111
+ --color-accent-hover: {{ACCENT_HOVER_HEX}}; /* role 1 hover state (+15% lightness) */
112
+ --color-accent-active: #6644e8; /* role 1 pressed state (-10% lightness) */
113
+ --color-accent-subtle: #1e1640; /* role 2: tinted background (dark) */
114
+ --color-accent-muted: #4a3490; /* role 2: tinted border (dark) */
115
+
116
+ /* ---------------------------------------------------------------
117
+ LAYER 2: SEMANTIC SURFACE TOKENS
118
+ Components reference these, never raw neutrals.
119
+ The hierarchy communicates depth: base -> raised -> overlay.
120
+ Sunken is lower than base (input wells, code blocks).
121
+ --------------------------------------------------------------- */
122
+ --color-bg-base: var(--color-neutral-900); /* page background */
123
+ --color-bg-raised: var(--color-neutral-800); /* card surfaces */
124
+ --color-bg-raised-hover: var(--color-neutral-700); /* raised-surface hover (e.g. secondary button) */
125
+ --color-bg-raised-active: var(--color-neutral-800); /* raised-surface pressed */
126
+ --color-bg-overlay: var(--color-neutral-850); /* sidebar, modal backdrop */
127
+ --color-bg-sunken: var(--color-neutral-950); /* input wells, code bg */
128
+ --color-bg-highlight: var(--color-accent-subtle); /* selected rows, active nav */
129
+
130
+ /* ---------------------------------------------------------------
131
+ LAYER 2: SEMANTIC TEXT TOKENS
132
+ Contrast ratios verified at their intended sizes (WCAG 2.1 AA):
133
+ text-primary on bg-base: 19.4:1 (passes AAA)
134
+ text-secondary on bg-base: 5.8:1 (passes AA at body size)
135
+ text-muted on bg-base: 3.9:1 (passes AA at 18px+)
136
+ --------------------------------------------------------------- */
137
+ --color-text-primary: var(--color-neutral-50); /* headings, labels, strong copy */
138
+ --color-text-secondary: var(--color-neutral-400); /* supporting copy, metadata */
139
+ --color-text-muted: var(--color-neutral-500); /* captions, placeholders, disabled */
140
+ --color-text-inverse: var(--color-neutral-950); /* text on light/accent backgrounds */
141
+ --color-text-accent: var(--color-accent-hover); /* links, active sidebar items */
142
+
143
+ /* ---------------------------------------------------------------
144
+ LAYER 2: SEMANTIC BORDER TOKENS
145
+ --------------------------------------------------------------- */
146
+ --color-border-default: var(--color-neutral-700); /* cards, dividers, table rows */
147
+ --color-border-emphasis: var(--color-neutral-600); /* focused inputs, hover borders */
148
+ --color-border-accent: var(--color-accent-muted); /* accent focus ring, active cards */
149
+
150
+ /* ---------------------------------------------------------------
151
+ LAYER 2: STATUS TOKENS
152
+ These are the only raw hex values in Layer 2 because status
153
+ colors are hue-distinct by nature and do not belong to the
154
+ neutral or accent ramps.
155
+ --------------------------------------------------------------- */
156
+ --color-success: #22c55e; /* text, icon on success-subtle */
157
+ --color-success-subtle: #052e16; /* background tint */
158
+ --color-warning: #f59e0b; /* text, icon on warning-subtle */
159
+ --color-warning-subtle: #27200a; /* background tint */
160
+ --color-danger: #ef4444; /* text, icon on danger-subtle */
161
+ --color-danger-subtle: #2a0a0a; /* background tint */
162
+ --color-danger-hover-bg: rgba(239, 68, 68, 0.20); /* destructive hover surface */
163
+ --color-danger-active-bg: rgba(239, 68, 68, 0.30); /* destructive active surface */
164
+ --color-overlay-scrim: rgba(8, 12, 20, 0.72); /* modal and palette backdrop */
165
+ --color-info: #38bdf8; /* text, icon on info-subtle */
166
+ --color-info-subtle: #082030; /* background tint */
167
+
168
+ /* ---------------------------------------------------------------
169
+ DATA VISUALISATION SERIES
170
+ Six perceptually-spaced hues that remain distinguishable:
171
+ - At 4px line weight on dark background
172
+ - To the most common color-vision deficiencies (deuteranopia,
173
+ protanopia, tritanopia)
174
+ The series starts with accent (violet) to tie viz to brand,
175
+ then fans out through sky, emerald, amber, pink, orange --
176
+ no two adjacent hues share the same luminance band.
177
+ --------------------------------------------------------------- */
178
+ --color-viz-1: #7c5af8; /* violet -- accent, brand-tied */
179
+ --color-viz-2: #38bdf8; /* sky -- info-adjacent */
180
+ --color-viz-3: #22c55e; /* emerald -- success-adjacent */
181
+ --color-viz-4: #f59e0b; /* amber -- warning-adjacent */
182
+ --color-viz-5: #f472b6; /* pink -- no semantic overlap */
183
+ --color-viz-6: #fb923c; /* orange -- no semantic overlap */
184
+
185
+ /* ---------------------------------------------------------------
186
+ FOCUS RING
187
+ WCAG 2.1 SC 2.4.11 (Level AA, WCAG 2.2): focus indicator must
188
+ be at least 2px wide and have a contrast ratio >= 3:1 against
189
+ adjacent colors. Our 2px outline at 2px offset on bg-base gives
190
+ approximately 8:1 contrast for the accent color.
191
+ Applied globally via :focus-visible in the @layer base block.
192
+ --------------------------------------------------------------- */
193
+ --color-focus-ring: var(--color-accent);
194
+
195
+ /* ================================================================
196
+ TYPE SCALE
197
+ Ratio: 1.250 (Major Third) applied to a 16px base.
198
+ Fluid clamp() interpolates linearly from 320px to 1440px viewport.
199
+ Formula: clamp(min, min + (max - min) * (100vw - 320px) / 1120, max)
200
+ Simplified: clamp(min_rem, slope_vw + intercept_rem, max_rem)
201
+
202
+ Tracking decisions:
203
+ Display/h1: -0.03em (optical tightening at large sizes prevents
204
+ letterforms from appearing to float apart)
205
+ h2/h3: -0.02em to -0.01em (gradual relaxation toward body size)
206
+ body/small: 0em (neutral -- reading comfort over optical correction)
207
+ caption: +0.01em (slight opening improves legibility at 12px)
208
+ mono: 0em (monospace already has designed optical spacing)
209
+
210
+ Line-height decisions:
211
+ display/h1 (1.1): content is usually 1-2 lines, no long reads;
212
+ tight LH maximises visual impact of large type.
213
+ h2/h3 (1.25-1.30): medium titles may wrap; needs breathing room.
214
+ body (1.5): WCAG 1.4.8 recommends >= 1.5 for body text.
215
+ caption (1.4): compact but not cramped; used only for short labels.
216
+ mono (1.6): extra breathing for code lines improves scan speed.
217
+ ================================================================ */
218
+
219
+ --type-display: clamp(2rem, 4vw + 1rem, 3rem);
220
+ --type-display-lh: 1.1;
221
+ --type-display-ls: -0.03em;
222
+
223
+ --type-h1: clamp(1.5rem, 2.5vw + 0.75rem, 2.25rem);
224
+ --type-h1-lh: 1.15;
225
+ --type-h1-ls: -0.025em;
226
+
227
+ --type-h2: clamp(1.25rem, 1.8vw + 0.6rem, 1.75rem);
228
+ --type-h2-lh: 1.25;
229
+ --type-h2-ls: -0.02em;
230
+
231
+ --type-h3: clamp(1.125rem, 1.2vw + 0.5rem, 1.375rem);
232
+ --type-h3-lh: 1.3;
233
+ --type-h3-ls: -0.01em;
234
+
235
+ --type-h4: clamp(1rem, 0.5vw + 0.875rem, 1.125rem);
236
+ --type-h4-lh: 1.35;
237
+ --type-h4-ls: -0.005em;
238
+
239
+ /* Body is not fluid -- a stable 16px baseline prevents micro-jitter
240
+ in dense layouts when the viewport resizes by a few pixels. */
241
+ --type-body: 1rem;
242
+ --type-body-lh: 1.5;
243
+ --type-body-ls: 0em;
244
+
245
+ --type-small: 0.875rem;
246
+ --type-small-lh: 1.45;
247
+ --type-small-ls: 0em;
248
+
249
+ --type-caption: 0.75rem;
250
+ --type-caption-lh: 1.4;
251
+ --type-caption-ls: 0.01em;
252
+
253
+ --type-mono: 0.8125rem; /* 13px -- slightly smaller than body for visual weight parity */
254
+ --type-mono-lh: 1.6;
255
+ --type-mono-ls: 0em;
256
+
257
+ --font-sans: 'Inter Variable', system-ui, -apple-system, sans-serif;
258
+ --font-mono: 'JetBrains Mono Variable', ui-monospace, 'Cascadia Code', monospace;
259
+
260
+ --font-weight-regular: 400;
261
+ --font-weight-medium: 500;
262
+ --font-weight-semibold: 600;
263
+ --font-weight-bold: 700;
264
+
265
+ /* ================================================================
266
+ SPACING SCALE
267
+ Base unit: 4px (--space-1).
268
+ Why 4px and not 8px: dashboard UIs are data-dense. The 4px base
269
+ gives fine-grained control for compact table cells, badge padding,
270
+ and inline micro-gaps without resorting to arbitrary values.
271
+ Named steps ascend as multiples of 4px up to 256px.
272
+ Semantic aliases are what components USE; numeric steps are what
273
+ the design spec references.
274
+ ================================================================ */
275
+
276
+ --space-px: 1px;
277
+ --space-0: 0px;
278
+ --space-1: 4px; /* micro: icon-to-text gap, badge inset */
279
+ --space-2: 8px; /* tight: chip padding, table cell vertical */
280
+ --space-3: 12px; /* compact: input vertical padding, small gap */
281
+ --space-4: 16px; /* base: button horizontal padding, list spacing */
282
+ --space-5: 20px; /* medium: label-to-input gap */
283
+ --space-6: 24px; /* card internal padding, section inner spacing */
284
+ --space-7: 28px; /* modal header height, sidebar section gap */
285
+ --space-8: 32px; /* section vertical gap */
286
+ --space-9: 36px;
287
+ --space-10: 40px; /* sidebar item height, large button height */
288
+ --space-11: 44px; /* table row height -- WCAG 2.5.5 touch target */
289
+ --space-12: 48px; /* topbar height */
290
+ --space-14: 56px;
291
+ --space-16: 64px; /* sidebar width collapsed */
292
+ --space-20: 80px;
293
+ --space-24: 96px;
294
+ --space-32: 128px; /* empty state illustration reserved area */
295
+ --space-40: 160px;
296
+ --space-48: 192px;
297
+ --space-56: 224px;
298
+ --space-64: 256px;
299
+
300
+ /* Semantic spacing aliases -- what components reference */
301
+ --spacing-page-edge: clamp(1.5rem, 4vw, 2.5rem); /* 24px to 40px */
302
+ --spacing-section: var(--space-8); /* 32px between page sections */
303
+ --spacing-card-pad: var(--space-6); /* 24px card internal padding */
304
+ --spacing-card-gap: var(--space-5); /* 20px gap between cards in grid */
305
+ --spacing-input-pad-v: var(--space-3); /* 12px input vertical padding */
306
+ --spacing-input-pad-h: var(--space-4); /* 16px input horizontal padding */
307
+ --spacing-topbar-h: var(--space-12); /* 48px topbar height */
308
+ --spacing-sidebar-w: 240px; /* sidebar expanded width */
309
+ --spacing-sidebar-w-col: var(--space-16); /* sidebar collapsed width: 64px */
310
+
311
+ /* ================================================================
312
+ RADIUS SYSTEM
313
+ Three distinct radii communicate affordance tier:
314
+ sharp (2px): data-dense elements: table chips, code blocks.
315
+ Signals precision and density.
316
+ default (8px): cards, inputs, modals. Neutral -- does not
317
+ draw attention to its own rounding.
318
+ large (12px): prominent panels, toasts, sidepanels.
319
+ Communicates soft/approachable without being playful.
320
+ pill: badges, tags, toggles. Unmistakably interactive.
321
+ circle: avatars, icon buttons, spinners.
322
+ ================================================================ */
323
+
324
+ --radius-sharp: 2px;
325
+ --radius-sm: 4px;
326
+ --radius-default: 8px;
327
+ --radius-lg: 12px;
328
+ --radius-xl: 16px;
329
+ --radius-pill: 9999px;
330
+ --radius-circle: 50%;
331
+
332
+ /* ================================================================
333
+ ELEVATION SYSTEM (3 levels)
334
+ Philosophy: On dark backgrounds, standard box-shadow produces
335
+ muddy grey blobs that reduce depth instead of conveying it.
336
+ Instead we use:
337
+ - Inset top border: simulates a "light source from above" highlight
338
+ that reads as a raised surface.
339
+ - Subtle ambient shadow: very low opacity dark shadow for the
340
+ physical impression of lift.
341
+ - No colored glows on structural shadows (only focus states use
342
+ a colored outline).
343
+ Level 1: card/panel lift (structural)
344
+ Level 2: floating layer (dropdown, popover, tooltip)
345
+ Level 3: modal layer (dialog, command palette)
346
+ ================================================================ */
347
+
348
+ --shadow-1:
349
+ inset 0 1px 0 rgba(255,255,255,0.06),
350
+ 0 1px 3px rgba(0,0,0,0.30),
351
+ 0 2px 8px rgba(0,0,0,0.20);
352
+
353
+ --shadow-2:
354
+ inset 0 1px 0 rgba(255,255,255,0.08),
355
+ 0 4px 12px rgba(0,0,0,0.40),
356
+ 0 8px 24px rgba(0,0,0,0.25),
357
+ 0 0 0 1px rgba(255,255,255,0.04);
358
+
359
+ --shadow-3:
360
+ 0 8px 32px rgba(0,0,0,0.55),
361
+ 0 24px 64px rgba(0,0,0,0.40),
362
+ 0 0 0 1px rgba(255,255,255,0.06),
363
+ 0 0 60px rgba(124,90,248,0.08);
364
+
365
+ /* Focus ring shadow -- supplements outline on complex elements */
366
+ --shadow-focus:
367
+ 0 0 0 2px var(--color-bg-base),
368
+ 0 0 0 4px var(--color-focus-ring);
369
+
370
+ /* ================================================================
371
+ MOTION SYSTEM
372
+ Design principle: motion communicates spatial metaphor.
373
+
374
+ Entering (ease-out): element travels from its source toward its
375
+ resting position, shedding velocity on arrival. The observer
376
+ sees it "land" naturally.
377
+
378
+ Leaving (ease-in): element builds momentum toward its destination.
379
+ The exit feels intentional, not accidental.
380
+
381
+ Toggling (ease-in-out): symmetric: equal energy on departure
382
+ and arrival. Appropriate for accordions, sliders, state flips
383
+ where no direction is conceptually "upward".
384
+
385
+ Spring (ease-spring): slight overshoot produces the delightful
386
+ "bounce" sensation reserved for micro-interactions where extra
387
+ delight is warranted (badge appear, success icon pop).
388
+
389
+ Subtle: minimal energy, for low-profile state fades (muted hover
390
+ on secondary elements).
391
+
392
+ Duration philosophy:
393
+ micro (80-150ms): state changes the eye tracks peripherally.
394
+ Used for: focus ring, status dot, badge colour.
395
+ component (200-350ms): elements in the focal region.
396
+ Used for: button hover, dropdown open, input focus.
397
+ layout (400-500ms): page-level spatial changes.
398
+ Used for: sidebar collapse, panel slide, route transition.
399
+
400
+ Stagger: 40ms between list items. Below 30ms the items appear
401
+ simultaneous; above 60ms the animation feels mechanical.
402
+ ================================================================ */
403
+
404
+ --ease-out: cubic-bezier(0.16, 1, 0.3, 1); /* spring-like enter */
405
+ --ease-in: cubic-bezier(0.4, 0, 1, 1); /* momentum exit */
406
+ --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); /* symmetric toggle */
407
+ --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); /* overshoot delight */
408
+ --ease-subtle: cubic-bezier(0.25, 0, 0, 1); /* low-profile fade */
409
+
410
+ --duration-instant: 80ms; /* state dot, focus ring, icon swap */
411
+ --duration-fast: 120ms; /* badge appear, status change */
412
+ --duration-normal: 200ms; /* button hover, input focus border */
413
+ --duration-medium: 300ms; /* dropdown open, tooltip appear */
414
+ --duration-slow: 400ms; /* sidebar collapse, panel open */
415
+ --duration-layout: 500ms; /* page-level spatial transitions */
416
+
417
+ --stagger-base: 40ms; /* per-item stagger delay in list animations */
418
+
419
+ /* ================================================================
420
+ Z-INDEX SCALE
421
+ A named scale prevents the "z-index arms race" where components
422
+ competing for stacking order escalate to z-index:9999.
423
+ Each level owns a clearly-described stacking context.
424
+ ================================================================ */
425
+
426
+ --z-sidebar: 30;
427
+ --z-topbar: 40;
428
+ --z-dropdown: 50;
429
+ --z-tooltip: 60;
430
+ --z-modal: 70;
431
+ --z-toast: 80;
432
+ --z-command: 90;
433
+
434
+ /* ================================================================
435
+ COMPONENT-LEVEL TOKENS (LAYER 3)
436
+ Derived from semantic tokens. Allow centralised component
437
+ defaults: change one token here, all instances update.
438
+ ================================================================ */
439
+
440
+ /* Button */
441
+ --btn-h-sm: 32px;
442
+ --btn-h-md: 40px;
443
+ --btn-h-lg: 48px;
444
+ --btn-px-sm: var(--space-3);
445
+ --btn-px-md: var(--space-4);
446
+ --btn-px-lg: var(--space-6);
447
+ --btn-radius: var(--radius-default);
448
+
449
+ /* Input */
450
+ --input-h: 40px;
451
+ --input-radius: var(--radius-default);
452
+ --input-pl: var(--spacing-input-pad-h); /* overridden when iconLeft present */
453
+
454
+ /* Table */
455
+ --table-row-h: var(--space-11); /* 44px -- WCAG touch target minimum */
456
+ --table-header-h: 40px;
457
+ }
458
+
459
+ /* ================================================================
460
+ LIGHT THEME OVERRIDES
461
+ Applied when <html data-theme="light"> is set.
462
+ ONLY Layer 2 semantic tokens are overridden here.
463
+ Raw scales (Layer 1) and component tokens (Layer 3) are
464
+ intentionally NOT overridden -- they derive their values from
465
+ the semantic tokens, so they update automatically.
466
+ ================================================================ */
467
+
468
+ [data-theme='light'] {
469
+ color-scheme: light;
470
+
471
+ --color-bg-base: var(--color-neutral-50);
472
+ --color-bg-raised: var(--color-neutral-0);
473
+ --color-bg-raised-hover: var(--color-neutral-100);
474
+ --color-bg-raised-active: var(--color-neutral-200);
475
+ --color-bg-overlay: var(--color-neutral-100);
476
+ --color-bg-sunken: var(--color-neutral-100);
477
+ --color-bg-highlight: #ede9ff;
478
+
479
+ --color-text-primary: var(--color-neutral-900);
480
+ --color-text-secondary: var(--color-neutral-600);
481
+ --color-text-muted: var(--color-neutral-500);
482
+ --color-text-accent: var(--color-accent);
483
+
484
+ --color-border-default: var(--color-neutral-200);
485
+ --color-border-emphasis: var(--color-neutral-300);
486
+
487
+ --color-accent-subtle: #ede9ff;
488
+ --color-accent-muted: #a78bfa;
489
+
490
+ --color-success-subtle: #f0fdf4;
491
+ --color-warning-subtle: #fffbeb;
492
+ --color-danger-subtle: #fef2f2;
493
+ --color-overlay-scrim: rgba(15, 20, 30, 0.45);
494
+ --color-info-subtle: #f0f9ff;
495
+
496
+ /* Light mode shadows: softer, less opaque -- dark shadows on
497
+ light backgrounds need lower opacity to avoid over-saturation. */
498
+ --shadow-1:
499
+ inset 0 1px 0 rgba(255,255,255,0.80),
500
+ 0 1px 3px rgba(0,0,0,0.08),
501
+ 0 2px 8px rgba(0,0,0,0.06);
502
+
503
+ --shadow-2:
504
+ 0 4px 12px rgba(0,0,0,0.10),
505
+ 0 8px 24px rgba(0,0,0,0.07),
506
+ 0 0 0 1px rgba(0,0,0,0.05);
507
+
508
+ --shadow-3:
509
+ 0 8px 32px rgba(0,0,0,0.15),
510
+ 0 24px 64px rgba(0,0,0,0.10),
511
+ 0 0 0 1px rgba(0,0,0,0.06);
512
+ }
513
+
514
+ /* ================================================================
515
+ TAILWIND v4 @theme BLOCK
516
+ Registers all tokens as Tailwind utility classes so components
517
+ can use class-based notation (bg-bg-base, text-text-primary)
518
+ without arbitrary value syntax.
519
+
520
+ The xl breakpoint override (75rem instead of default 80rem /
521
+ 1280px) eliminates the "MacBook Air dead zone": laptop viewports
522
+ between 1200-1279px were stuck at lg: utilities despite having
523
+ enough horizontal space for the xl: layout tier.
524
+ ================================================================ */
525
+
526
+ @theme {
527
+ /* Breakpoint override */
528
+ --breakpoint-xl: 75rem; /* 1200px instead of Tailwind default 1280px */
529
+
530
+ /* Font families */
531
+ --font-sans: var(--font-sans);
532
+ --font-mono: var(--font-mono);
533
+
534
+ /* Accent colors for Tailwind utilities */
535
+ --color-accent: {{ACCENT_HEX}};
536
+ --color-accent-hover: {{ACCENT_HOVER_HEX}};
537
+ --color-accent-subtle: #1e1640;
538
+ --color-accent-muted: #4a3490;
539
+
540
+ /* Neutral scale */
541
+ --color-neutral-0: #ffffff;
542
+ --color-neutral-50: #f7f8fa;
543
+ --color-neutral-100: #eef0f4;
544
+ --color-neutral-200: #dde1e9;
545
+ --color-neutral-300: #c4cad6;
546
+ --color-neutral-400: #9aa3b4;
547
+ --color-neutral-500: #6b7689;
548
+ --color-neutral-600: #4e5769;
549
+ --color-neutral-700: #343d4f;
550
+ --color-neutral-800: #1f2635;
551
+ --color-neutral-850: #171d2b;
552
+ --color-neutral-900: #0f1420;
553
+ --color-neutral-950: #080c14;
554
+
555
+ /* Status colors */
556
+ --color-success: #22c55e;
557
+ --color-warning: #f59e0b;
558
+ --color-danger: #ef4444;
559
+ --color-info: #38bdf8;
560
+
561
+ /* Viz series */
562
+ --color-viz-1: #7c5af8;
563
+ --color-viz-2: #38bdf8;
564
+ --color-viz-3: #22c55e;
565
+ --color-viz-4: #f59e0b;
566
+ --color-viz-5: #f472b6;
567
+ --color-viz-6: #fb923c;
568
+ }
569
+
570
+ /* ================================================================
571
+ BASE STYLES
572
+ Semantic HTML defaults. Applied inside @layer base so Tailwind
573
+ utilities in components can override via normal cascade order.
574
+ ================================================================ */
575
+
576
+ @layer base {
577
+ *,
578
+ *::before,
579
+ *::after {
580
+ box-sizing: border-box;
581
+ }
582
+
583
+ html {
584
+ /* scroll-behavior:smooth is NOT set here; it breaks browser
585
+ native scroll-jump (anchor links) in Firefox and conflicts
586
+ with framer-motion AnimatePresence page transitions.
587
+ Individual smooth-scroll sections set it on their container. */
588
+ scroll-behavior: auto;
589
+ /* Prevent horizontal overflow from miscalculated widths */
590
+ overflow-x: hidden;
591
+ }
592
+
593
+ body {
594
+ background-color: var(--color-bg-base);
595
+ color: var(--color-text-primary);
596
+ font-family: var(--font-sans);
597
+ font-size: var(--type-body);
598
+ line-height: var(--type-body-lh);
599
+ letter-spacing: var(--type-body-ls);
600
+ -webkit-font-smoothing: antialiased;
601
+ -moz-osx-font-smoothing: grayscale;
602
+ font-feature-settings: 'calt' 1, 'cv02' 1, 'cv03' 1, 'cv04' 1;
603
+ /* Transitioning background-color here enables smooth theme toggle.
604
+ Only background-color -- not color or other properties. */
605
+ transition:
606
+ background-color var(--duration-medium) var(--ease-in-out),
607
+ color var(--duration-medium) var(--ease-in-out);
608
+ }
609
+
610
+ /* Heading hierarchy with role-differentiated type scale */
611
+ h1 {
612
+ font-family: var(--font-sans);
613
+ font-size: var(--type-h1);
614
+ font-weight: var(--font-weight-bold);
615
+ line-height: var(--type-h1-lh);
616
+ letter-spacing: var(--type-h1-ls);
617
+ color: var(--color-text-primary);
618
+ }
619
+
620
+ h2 {
621
+ font-family: var(--font-sans);
622
+ font-size: var(--type-h2);
623
+ font-weight: var(--font-weight-bold);
624
+ line-height: var(--type-h2-lh);
625
+ letter-spacing: var(--type-h2-ls);
626
+ color: var(--color-text-primary);
627
+ }
628
+
629
+ h3 {
630
+ font-family: var(--font-sans);
631
+ font-size: var(--type-h3);
632
+ font-weight: var(--font-weight-semibold);
633
+ line-height: var(--type-h3-lh);
634
+ letter-spacing: var(--type-h3-ls);
635
+ color: var(--color-text-primary);
636
+ }
637
+
638
+ h4 {
639
+ font-family: var(--font-sans);
640
+ font-size: var(--type-h4);
641
+ font-weight: var(--font-weight-semibold);
642
+ line-height: var(--type-h4-lh);
643
+ letter-spacing: var(--type-h4-ls);
644
+ color: var(--color-text-primary);
645
+ }
646
+
647
+ /* Focus ring: global :focus-visible rule.
648
+ Components with complex inner structure (button with icon children)
649
+ add --shadow-focus via box-shadow in addition to this outline.
650
+ NEVER suppress focus-visible for accessibility. */
651
+ :focus-visible {
652
+ outline: 2px solid var(--color-focus-ring);
653
+ outline-offset: 2px;
654
+ border-radius: var(--radius-sm);
655
+ }
656
+
657
+ /* Scroll margin for anchor-linked headings: accounts for sticky topbar */
658
+ [id] {
659
+ scroll-margin-top: calc(var(--spacing-topbar-h) + var(--space-4));
660
+ }
661
+
662
+ /* Code elements use mono font and sunken background */
663
+ code, pre, kbd, samp {
664
+ font-family: var(--font-mono);
665
+ font-size: var(--type-mono);
666
+ line-height: var(--type-mono-lh);
667
+ letter-spacing: var(--type-mono-ls);
668
+ }
669
+
670
+ /* Reduce motion for SVG animations: browsers do not always honour
671
+ the @media block for inline SVG animations */
672
+ @media (prefers-reduced-motion: reduce) {
673
+ svg * {
674
+ animation-duration: 0.01ms !important;
675
+ transition-duration: 0.01ms !important;
676
+ }
677
+ }
678
+ }
679
+
680
+ /* ================================================================
681
+ SKELETON SHIMMER ANIMATION
682
+ Used by SkeletonBlock component. Defined globally because @keyframes
683
+ inside a component CSS module would duplicate across instances.
684
+ The animation uses transform:translateX only (no layout-affecting
685
+ properties) to stay on the GPU compositor thread.
686
+ ================================================================ */
687
+
688
+ @keyframes skeleton-shimmer {
689
+ 0% { transform: translateX(-100%); }
690
+ 100% { transform: translateX(100%); }
691
+ }
692
+
693
+ /* Stat card loading pulse: opacity-only fade (no geometry change) */
694
+ @keyframes pulse-opacity {
695
+ 0%, 100% { opacity: 1; }
696
+ 50% { opacity: 0.4; }
697
+ }
698
+
699
+ /* Toast/badge entrance from below */
700
+ @keyframes slide-up {
701
+ from { transform: translateY(8px); opacity: 0; }
702
+ to { transform: translateY(0); opacity: 1; }
703
+ }
704
+
705
+ /* ================================================================
706
+ REDUCED MOTION OVERRIDE
707
+ This block is the single source of truth for reduced-motion
708
+ behaviour. It intentionally uses !important to override any
709
+ inline or component-level transition declaration.
710
+
711
+ Opacity fades are preserved at 80ms (--duration-instant) because
712
+ users with vestibular disorders can still perceive colour/opacity
713
+ changes without experiencing motion sickness from transforms.
714
+
715
+ Transform-based animations are removed entirely (set to none).
716
+ Framer-motion variants should check useReducedMotion() and skip
717
+ x/y/scale variants, keeping only opacity.
718
+ ================================================================ */
719
+
720
+ @media (prefers-reduced-motion: reduce) {
721
+ *,
722
+ *::before,
723
+ *::after {
724
+ animation-duration: 0.01ms !important;
725
+ animation-iteration-count: 1 !important;
726
+ transition-duration: 0.01ms !important;
727
+ scroll-behavior: auto !important;
728
+ }
729
+
730
+ /* Restore opacity transitions for non-motion feedback */
731
+ [data-motion-safe] {
732
+ transition-duration: 80ms !important;
733
+ animation-duration: 80ms !important;
734
+ }
735
+
736
+ /* Skeleton shimmer: show static muted colour instead of animating */
737
+ .skeleton-shimmer::after {
738
+ animation: none !important;
739
+ }
740
+ }
741
+
742
+ /* DataTable row hover (the table sets per-cell styles inline; the row hover is
743
+ a global rule so non-selected rows get a token-driven hover surface). */
744
+ .data-table-row { transition: background-color var(--duration-fast, 150ms) var(--ease-out, ease-out); }
745
+ .data-table-row:hover { background-color: var(--color-bg-overlay); }
746
+ @media (prefers-reduced-motion: reduce) { .data-table-row { transition: none; } }