lumina-slides 8.9.4 → 9.0.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 (119) hide show
  1. package/LUMINA_LLM_EXAMPLES.json +234 -0
  2. package/README.md +18 -18
  3. package/dist/lumina-slides.js +13207 -12659
  4. package/dist/lumina-slides.umd.cjs +215 -215
  5. package/dist/style.css +1 -1
  6. package/package.json +5 -4
  7. package/src/App.vue +16 -0
  8. package/src/animation/index.ts +11 -0
  9. package/src/animation/registry.ts +126 -0
  10. package/src/animation/stagger.ts +95 -0
  11. package/src/animation/types.ts +53 -0
  12. package/src/components/LandingPage.vue +229 -0
  13. package/src/components/LuminaDeck.vue +224 -0
  14. package/src/components/LuminaSpeakerNotes.vue +701 -0
  15. package/src/components/base/BaseSlide.vue +122 -0
  16. package/src/components/base/LuminaElement.vue +67 -0
  17. package/src/components/base/VideoPlayer.vue +204 -0
  18. package/src/components/layouts/LayoutAuto.vue +71 -0
  19. package/src/components/layouts/LayoutChart.vue +287 -0
  20. package/src/components/layouts/LayoutCustom.vue +92 -0
  21. package/src/components/layouts/LayoutDiagram.vue +253 -0
  22. package/src/components/layouts/LayoutFeatures.vue +121 -0
  23. package/src/components/layouts/LayoutFlex.vue +172 -0
  24. package/src/components/layouts/LayoutFree.vue +62 -0
  25. package/src/components/layouts/LayoutHalf.vue +127 -0
  26. package/src/components/layouts/LayoutStatement.vue +74 -0
  27. package/src/components/layouts/LayoutSteps.vue +106 -0
  28. package/src/components/layouts/LayoutTimeline.vue +104 -0
  29. package/src/components/layouts/LayoutVideo.vue +41 -0
  30. package/src/components/parts/FlexBullets.vue +45 -0
  31. package/src/components/parts/FlexButton.vue +132 -0
  32. package/src/components/parts/FlexImage.vue +54 -0
  33. package/src/components/parts/FlexOrdered.vue +44 -0
  34. package/src/components/parts/FlexSpacer.vue +13 -0
  35. package/src/components/parts/FlexStepper.vue +59 -0
  36. package/src/components/parts/FlexText.vue +29 -0
  37. package/src/components/parts/FlexTimeline.vue +67 -0
  38. package/src/components/parts/FlexTitle.vue +39 -0
  39. package/src/components/parts/LuminaBackground.vue +100 -0
  40. package/src/components/site/LivePreview.vue +101 -0
  41. package/src/components/site/SiteApi.vue +301 -0
  42. package/src/components/site/SiteDashboard.vue +604 -0
  43. package/src/components/site/SiteDocs.vue +3267 -0
  44. package/src/components/site/SiteExamples.vue +65 -0
  45. package/src/components/site/SiteFooter.vue +6 -0
  46. package/src/components/site/SiteHome.vue +362 -0
  47. package/src/components/site/SiteNavBar.vue +122 -0
  48. package/src/components/site/SitePlayground.vue +389 -0
  49. package/src/components/site/SitePromptBuilder.vue +266 -0
  50. package/src/components/site/SiteUserMenu.vue +90 -0
  51. package/src/components/studio/ActionEditor.vue +108 -0
  52. package/src/components/studio/ArrayEditor.vue +124 -0
  53. package/src/components/studio/CollapsibleSection.vue +33 -0
  54. package/src/components/studio/ColorField.vue +22 -0
  55. package/src/components/studio/EditorCanvas.vue +326 -0
  56. package/src/components/studio/EditorLayoutFeatures.vue +18 -0
  57. package/src/components/studio/EditorLayoutFixed.vue +46 -0
  58. package/src/components/studio/EditorLayoutFlex.vue +133 -0
  59. package/src/components/studio/EditorLayoutHalf.vue +18 -0
  60. package/src/components/studio/EditorLayoutStatement.vue +18 -0
  61. package/src/components/studio/EditorLayoutSteps.vue +18 -0
  62. package/src/components/studio/EditorLayoutTimeline.vue +18 -0
  63. package/src/components/studio/EditorNode.vue +89 -0
  64. package/src/components/studio/FieldEditor.vue +133 -0
  65. package/src/components/studio/IconPicker.vue +109 -0
  66. package/src/components/studio/LayerItem.vue +117 -0
  67. package/src/components/studio/LuminaStudio.vue +30 -0
  68. package/src/components/studio/SaveSuccessModal.vue +138 -0
  69. package/src/components/studio/SlideNavigator.vue +373 -0
  70. package/src/components/studio/SliderField.vue +44 -0
  71. package/src/components/studio/StudioInspector.vue +595 -0
  72. package/src/components/studio/StudioJsonEditor.vue +191 -0
  73. package/src/components/studio/StudioLayers.vue +145 -0
  74. package/src/components/studio/StudioSettings.vue +514 -0
  75. package/src/components/studio/StudioSidebar.vue +29 -0
  76. package/src/components/studio/StudioToolbar.vue +222 -0
  77. package/src/components/studio/fieldLabels.ts +224 -0
  78. package/src/components/studio/inspectors/DiagramEdgeEditor.vue +77 -0
  79. package/src/components/studio/inspectors/DiagramNodeEditor.vue +117 -0
  80. package/src/components/studio/nodes/StudioDiagramNode.vue +138 -0
  81. package/src/composables/useAuth.ts +87 -0
  82. package/src/composables/useEditor.ts +224 -0
  83. package/src/composables/useElementState.ts +81 -0
  84. package/src/composables/useFlexLayout.ts +122 -0
  85. package/src/composables/useKeyboard.ts +45 -0
  86. package/src/composables/useLumina.ts +32 -0
  87. package/src/composables/useStudio.ts +87 -0
  88. package/src/composables/useSwipeNav.ts +53 -0
  89. package/src/composables/useTransition.ts +373 -0
  90. package/src/core/Lumina.ts +819 -0
  91. package/src/core/animationConfig.ts +251 -0
  92. package/src/core/compression.ts +34 -0
  93. package/src/core/elementController.ts +170 -0
  94. package/src/core/elementId.ts +27 -0
  95. package/src/core/elementResolver.ts +207 -0
  96. package/src/core/events.ts +53 -0
  97. package/src/core/fonts.ts +100 -0
  98. package/src/core/presets.ts +231 -0
  99. package/src/core/prompts.ts +272 -0
  100. package/src/core/schema.ts +478 -0
  101. package/src/core/speaker-channel.ts +250 -0
  102. package/src/core/store.ts +461 -0
  103. package/src/core/theme.ts +666 -0
  104. package/src/core/types.ts +1611 -0
  105. package/src/directives/vStudio.ts +45 -0
  106. package/src/index.ts +175 -0
  107. package/src/main.ts +17 -0
  108. package/src/router/index.ts +92 -0
  109. package/src/style/main.css +462 -0
  110. package/src/utils/deep.ts +127 -0
  111. package/src/utils/firebase.ts +184 -0
  112. package/src/utils/streaming.ts +134 -0
  113. package/src/views/DashboardView.vue +32 -0
  114. package/src/views/DeckView.vue +289 -0
  115. package/src/views/HomeView.vue +17 -0
  116. package/src/views/SiteLayout.vue +21 -0
  117. package/src/views/StudioView.vue +61 -0
  118. package/src/vite-env.d.ts +6 -0
  119. package/IMPLEMENTATION.md +0 -418
@@ -0,0 +1,462 @@
1
+ @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&family=Inter:wght@100..900&display=swap');
2
+
3
+ @tailwind base;
4
+ @tailwind components;
5
+ @tailwind utilities;
6
+
7
+ /* ============================================================================
8
+ LUMINA ENGINE - BASE STYLES & CSS CUSTOM PROPERTIES
9
+ Modern, Refined, Dynamic Design System v9
10
+ ============================================================================ */
11
+
12
+ :root {
13
+ /* === COLORS === */
14
+ --lumina-color-primary: #3b82f6;
15
+ --lumina-color-secondary: #8b5cf6;
16
+ --lumina-color-accent: #06b6d4;
17
+ --lumina-color-background: #030303;
18
+ --lumina-color-surface: #0a0a0a;
19
+ --lumina-color-text: #ffffff;
20
+ --lumina-color-text-secondary: #e5e7eb;
21
+ --lumina-color-muted: #9ca3af;
22
+ --lumina-color-success: #10b981;
23
+ --lumina-color-warning: #f59e0b;
24
+ --lumina-color-danger: #ef4444;
25
+ --lumina-color-info: #3b82f6;
26
+ --lumina-color-border: rgba(255, 255, 255, 0.1);
27
+ --lumina-color-border-subtle: rgba(255, 255, 255, 0.05);
28
+ --lumina-color-shadow: #000000;
29
+ --lumina-color-overlay: rgba(0, 0, 0, 0.5);
30
+ --lumina-color-highlight: rgba(59, 130, 246, 0.2);
31
+ --lumina-color-gradient-from: #3b82f6;
32
+ --lumina-color-gradient-to: #8b5cf6;
33
+
34
+ /* RGB Values for alpha usage */
35
+ --lumina-color-primary-rgb: 59, 130, 246;
36
+ --lumina-color-secondary-rgb: 139, 92, 246;
37
+ --lumina-color-background-rgb: 3, 3, 3;
38
+ --lumina-color-text-rgb: 255, 255, 255;
39
+
40
+ /* === TYPOGRAPHY === */
41
+ --lumina-font-heading: 'Inter', system-ui, sans-serif;
42
+ --lumina-font-body: 'Inter', system-ui, sans-serif;
43
+ --lumina-font-mono: ui-monospace, SFMono-Regular, monospace;
44
+
45
+ --lumina-text-xs: 0.75rem;
46
+ --lumina-text-sm: 0.875rem;
47
+ --lumina-text-base: 1rem;
48
+ --lumina-text-lg: 1.125rem;
49
+ --lumina-text-xl: 1.25rem;
50
+ --lumina-text-2xl: 1.5rem;
51
+ --lumina-text-3xl: 1.875rem;
52
+ --lumina-text-4xl: 2.25rem;
53
+ --lumina-text-5xl: 3rem;
54
+ --lumina-text-6xl: 3.75rem;
55
+ --lumina-text-7xl: 4.5rem;
56
+
57
+ --lumina-font-weight-light: 300;
58
+ --lumina-font-weight-normal: 400;
59
+ --lumina-font-weight-medium: 500;
60
+ --lumina-font-weight-semibold: 600;
61
+ --lumina-font-weight-bold: 800;
62
+ --lumina-font-weight-extrabold: 900;
63
+
64
+ --lumina-leading-tight: 1.1;
65
+ --lumina-leading-snug: 1.25;
66
+ --lumina-leading-normal: 1.5;
67
+ --lumina-leading-relaxed: 1.625;
68
+ --lumina-leading-loose: 2;
69
+
70
+ --lumina-tracking-tighter: -0.05em;
71
+ --lumina-tracking-tight: -0.025em;
72
+ --lumina-tracking-normal: 0;
73
+ --lumina-tracking-wide: 0.025em;
74
+ --lumina-tracking-wider: 0.05em;
75
+ --lumina-tracking-widest: 0.1em;
76
+
77
+ /* === SPACING === */
78
+ --lumina-space-none: 0;
79
+ --lumina-space-xs: 0.25rem;
80
+ --lumina-space-sm: 0.5rem;
81
+ --lumina-space-md: 1rem;
82
+ --lumina-space-lg: 1.5rem;
83
+ --lumina-space-xl: 2rem;
84
+ --lumina-space-2xl: 3rem;
85
+ --lumina-space-3xl: 4rem;
86
+ --lumina-space-4xl: 6rem;
87
+
88
+ /* === BORDER RADIUS === */
89
+ --lumina-radius-none: 0;
90
+ --lumina-radius-sm: 0.25rem;
91
+ --lumina-radius-md: 0.5rem;
92
+ --lumina-radius-lg: 0.75rem;
93
+ --lumina-radius-xl: 1rem;
94
+ --lumina-radius-2xl: 1.5rem;
95
+ --lumina-radius-3xl: 2rem;
96
+ --lumina-radius-full: 9999px;
97
+
98
+ /* === MODERN SHADOWS (Layered for depth) === */
99
+ --lumina-shadow-sm:
100
+ 0 1px 2px rgba(0, 0, 0, 0.1);
101
+ --lumina-shadow-md:
102
+ 0 4px 6px -1px rgba(0, 0, 0, 0.15),
103
+ 0 2px 4px -2px rgba(0, 0, 0, 0.1);
104
+ --lumina-shadow-lg:
105
+ 0 10px 15px -3px rgba(0, 0, 0, 0.2),
106
+ 0 4px 6px -4px rgba(0, 0, 0, 0.1);
107
+ --lumina-shadow-xl:
108
+ 0 20px 25px -5px rgba(0, 0, 0, 0.25),
109
+ 0 8px 10px -6px rgba(0, 0, 0, 0.1);
110
+ --lumina-shadow-2xl:
111
+ 0 25px 50px -12px rgba(0, 0, 0, 0.4);
112
+ --lumina-shadow-glow:
113
+ 0 0 40px rgba(var(--lumina-color-primary-rgb), 0.3);
114
+ --lumina-shadow-inner-glow:
115
+ inset 0 1px 0 rgba(255, 255, 255, 0.1),
116
+ inset 0 -1px 0 rgba(0, 0, 0, 0.1);
117
+
118
+ /* === EFFECTS === */
119
+ --lumina-use-gradients: 1;
120
+ --lumina-use-shadows: 1;
121
+ --lumina-use-glass: 1;
122
+ --lumina-use-orb: 1;
123
+ --lumina-use-animations: 1;
124
+
125
+ --lumina-gradient-direction: to bottom;
126
+ --lumina-shadow-intensity: md;
127
+ --lumina-shadow-color: rgba(0, 0, 0, 0.4);
128
+
129
+ --lumina-glass-opacity: 0.08;
130
+ --lumina-glass-blur: 24px;
131
+ --lumina-glass-border-opacity: 0.1;
132
+
133
+ --lumina-orb-opacity: 0.20;
134
+ --lumina-orb-blur: 120px;
135
+ --lumina-orb-size: 80vw;
136
+
137
+ --lumina-transition-duration: 0.3s;
138
+ --lumina-transition-easing: cubic-bezier(0.16, 1, 0.3, 1);
139
+ /* Apply flags to values */
140
+ --lumina-orb-opacity-effective: calc(var(--lumina-orb-opacity) * var(--lumina-use-orb));
141
+ --lumina-glass-opacity-effective: calc(var(--lumina-glass-opacity) * var(--lumina-use-glass));
142
+ --lumina-glass-line-opacity-effective: calc(var(--lumina-glass-border-opacity) * var(--lumina-use-glass));
143
+ --lumina-glass-blur-effective: calc(var(--lumina-glass-blur) * var(--lumina-use-glass));
144
+ --lumina-duration-effective: calc(var(--lumina-transition-duration) * var(--lumina-use-animations));
145
+
146
+ /* Gradient Text Control */
147
+ --lumina-gradient-text-fill: transparent;
148
+ --lumina-gradient-bg-clip: text;
149
+
150
+
151
+ /* === COMPONENTS === */
152
+ --lumina-button-radius: 9999px;
153
+ --lumina-button-padding: 0.875rem 1.75rem;
154
+ --lumina-button-font-weight: 600;
155
+
156
+ --lumina-card-radius: 1.25rem;
157
+ --lumina-card-padding: 1.75rem;
158
+ --lumina-card-border-width: 1px;
159
+
160
+ --lumina-timeline-node-size: 0.875rem;
161
+ --lumina-timeline-line-width: 2px;
162
+
163
+ --lumina-tag-padding: 0.375rem 1rem;
164
+ --lumina-tag-radius: 9999px;
165
+ --lumina-tag-font-size: 0.75rem;
166
+
167
+ /* Legacy compat */
168
+ --lumina-bg: var(--lumina-color-background);
169
+ --lumina-colors-primary: var(--lumina-color-primary);
170
+ --lumina-colors-background: var(--lumina-color-background);
171
+ --lumina-colors-text: var(--lumina-color-text);
172
+ --lumina-colors-muted: var(--lumina-color-muted);
173
+
174
+ --ease-expo: var(--lumina-transition-easing);
175
+
176
+ --lumina-safe-area-bottom: 0px;
177
+ }
178
+
179
+ /* === RESPONSIVE TYPOGRAPHY === */
180
+ @media (max-width: 1023px) {
181
+ :root {
182
+ /* Aggressive size reduction for mobile */
183
+ --lumina-text-3xl: 1.25rem; /* 20px */
184
+ --lumina-text-4xl: 1.5rem; /* 24px */
185
+ --lumina-text-5xl: 1.75rem; /* 28px */
186
+ --lumina-text-6xl: 2rem; /* 32px - effectively smaller than original clamp */
187
+ --lumina-text-7xl: 2.5rem; /* 40px */
188
+
189
+ /* Tighter spacing for "compressed" look */
190
+ --lumina-space-md: 0.75rem;
191
+ --lumina-space-lg: 1rem;
192
+ --lumina-space-xl: 1.5rem;
193
+ --lumina-space-2xl: 2rem;
194
+ --lumina-space-3xl: 2.5rem;
195
+
196
+ --lumina-leading-tight: 1.1;
197
+ --lumina-tracking-tighter: -0.05em;
198
+
199
+ /* Mobile Navigation Safe Area */
200
+ --lumina-safe-area-bottom: 6rem;
201
+ }
202
+ }
203
+
204
+ @media (max-width: 639px) {
205
+ :root {
206
+ /* Even smaller sizes for phones */
207
+ --lumina-text-3xl: 1rem !important; /* 16px */
208
+ --lumina-text-4xl: 1.5rem !important; /* 18px */
209
+ --lumina-text-5xl: 2rem !important; /* 20px */
210
+ --lumina-text-6xl: 2.5rem !important; /* 24px */
211
+ --lumina-text-7xl: 3rem !important; /* 28px */
212
+
213
+ --lumina-space-md: 0.5rem;
214
+ --lumina-space-lg: 0.75rem;
215
+ --lumina-space-xl: 1rem;
216
+ }
217
+ }
218
+
219
+ /* === BASE STYLES === */
220
+ body {
221
+ background-color: var(--lumina-color-background);
222
+ color: var(--lumina-color-text);
223
+ font-family: var(--lumina-font-body);
224
+ overflow-x: hidden;
225
+ overflow-y: auto;
226
+ margin: 0;
227
+ -webkit-font-smoothing: antialiased;
228
+ -moz-osx-font-smoothing: grayscale;
229
+ }
230
+
231
+ /* === ENHANCED GLASS PANEL === */
232
+ .glass-panel {
233
+ background: linear-gradient(
234
+ 135deg,
235
+ rgba(var(--lumina-color-text-rgb), calc(var(--lumina-glass-opacity-effective) * 1.5)) 0%,
236
+ rgba(var(--lumina-color-text-rgb), var(--lumina-glass-opacity-effective)) 100%
237
+ );
238
+ backdrop-filter: blur(var(--lumina-glass-blur-effective));
239
+ -webkit-backdrop-filter: blur(var(--lumina-glass-blur-effective));
240
+ border: var(--lumina-card-border-width) solid rgba(var(--lumina-color-text-rgb), var(--lumina-glass-line-opacity-effective));
241
+ box-shadow:
242
+ var(--lumina-shadow-lg),
243
+ inset 0 1px 0 rgba(255, 255, 255, 0.08);
244
+ transition: all var(--lumina-duration-effective) var(--lumina-transition-easing);
245
+ }
246
+
247
+ .glass-panel:hover {
248
+ transform: translateY(-2px);
249
+ box-shadow:
250
+ var(--lumina-shadow-xl),
251
+ inset 0 1px 0 rgba(255, 255, 255, 0.12);
252
+ border-color: rgba(var(--lumina-color-text-rgb), calc(var(--lumina-glass-line-opacity-effective) + 0.05));
253
+ }
254
+
255
+ /* === SCROLLBAR === */
256
+ ::-webkit-scrollbar { width: 6px; }
257
+ ::-webkit-scrollbar-track { background: transparent; }
258
+ ::-webkit-scrollbar-thumb {
259
+ background: rgba(var(--lumina-color-text-rgb), 0.15);
260
+ border-radius: 10px;
261
+ }
262
+ ::-webkit-scrollbar-thumb:hover {
263
+ background: rgba(var(--lumina-color-text-rgb), 0.25);
264
+ }
265
+
266
+ /* === AMBIENT ORB === */
267
+ .ambient-orb {
268
+ position: absolute;
269
+ border-radius: 50%;
270
+ filter: blur(var(--lumina-orb-blur));
271
+ opacity: var(--lumina-orb-opacity-effective);
272
+ transition: all 2s var(--lumina-transition-easing);
273
+ z-index: 0;
274
+ pointer-events: none;
275
+ }
276
+
277
+ /* === TIMELINE SCROLL MASK === */
278
+ .timeline-scroll {
279
+ mask-image: linear-gradient(to right, transparent, black 5%, black 95%, transparent);
280
+ }
281
+
282
+ /* === TRANSITIONS === */
283
+ .slide-fade-enter-active {
284
+ transition: all 0.5s var(--lumina-transition-easing);
285
+ }
286
+
287
+ .slide-fade-leave-active {
288
+ transition: all 0.3s ease-out;
289
+ }
290
+
291
+ .slide-fade-enter-from,
292
+ .slide-fade-leave-to {
293
+ transform: translateX(20px);
294
+ opacity: 0;
295
+ }
296
+
297
+ .fade-enter-active,
298
+ .fade-leave-active {
299
+ transition: opacity var(--lumina-transition-duration) ease;
300
+ }
301
+
302
+ .fade-enter-from,
303
+ .fade-leave-to {
304
+ opacity: 0;
305
+ }
306
+
307
+ /* === MICRO-ANIMATIONS === */
308
+ @keyframes pulse-glow {
309
+ 0%, 100% {
310
+ box-shadow: 0 0 20px rgba(var(--lumina-color-primary-rgb), 0.4);
311
+ }
312
+ 50% {
313
+ box-shadow: 0 0 35px rgba(var(--lumina-color-primary-rgb), 0.6);
314
+ }
315
+ }
316
+
317
+ @keyframes float {
318
+ 0%, 100% { transform: translateY(0); }
319
+ 50% { transform: translateY(-6px); }
320
+ }
321
+
322
+ @keyframes shimmer {
323
+ 0% { background-position: -200% center; }
324
+ 100% { background-position: 200% center; }
325
+ }
326
+
327
+ .animate-pulse-glow {
328
+ animation: pulse-glow 2s ease-in-out infinite;
329
+ }
330
+
331
+ .animate-float {
332
+ animation: float 3s ease-in-out infinite;
333
+ }
334
+
335
+ /* === UTILITY CLASSES === */
336
+
337
+ /* Typography */
338
+ .font-heading {
339
+ font-family: var(--lumina-font-heading);
340
+ font-weight: var(--lumina-font-weight-extrabold, 900);
341
+ }
342
+ .font-body { font-family: var(--lumina-font-body); }
343
+ .font-mono { font-family: var(--lumina-font-mono); }
344
+
345
+ /* Colors */
346
+ .text-primary { color: var(--lumina-color-primary); }
347
+ .text-secondary { color: var(--lumina-color-secondary); }
348
+ .text-muted { color: var(--lumina-color-muted); }
349
+ .bg-primary { background-color: var(--lumina-color-primary); }
350
+ .bg-surface { background-color: var(--lumina-color-surface); }
351
+ .border-theme { border-color: var(--lumina-color-border); }
352
+
353
+ /* Gradient text */
354
+ .text-gradient {
355
+ background: linear-gradient(
356
+ var(--lumina-gradient-direction),
357
+ var(--lumina-color-gradient-from),
358
+ var(--lumina-color-gradient-to)
359
+ );
360
+ -webkit-background-clip: var(--lumina-gradient-bg-clip);
361
+ -webkit-text-fill-color: var(--lumina-gradient-text-fill);
362
+ background-clip: var(--lumina-gradient-bg-clip);
363
+ }
364
+
365
+ /* Gradient background */
366
+ .bg-gradient-theme {
367
+ background: linear-gradient(
368
+ var(--lumina-gradient-direction),
369
+ var(--lumina-color-gradient-from),
370
+ var(--lumina-color-gradient-to)
371
+ );
372
+ }
373
+
374
+ /* Modern button styles */
375
+ .btn-primary {
376
+ background: linear-gradient(
377
+ 135deg,
378
+ var(--lumina-color-primary) 0%,
379
+ var(--lumina-color-secondary) 100%
380
+ );
381
+ color: white;
382
+ padding: var(--lumina-button-padding);
383
+ border-radius: var(--lumina-button-radius);
384
+ font-weight: var(--lumina-button-font-weight);
385
+ border: none;
386
+ cursor: pointer;
387
+ transition: all var(--lumina-duration-effective) var(--lumina-transition-easing);
388
+ box-shadow: var(--lumina-shadow-md);
389
+ }
390
+
391
+ .btn-primary:hover {
392
+ transform: translateY(-2px) scale(1.02);
393
+ box-shadow:
394
+ var(--lumina-shadow-lg),
395
+ 0 0 30px rgba(var(--lumina-color-primary-rgb), 0.4);
396
+ }
397
+
398
+ /* Shadow utilities */
399
+ .shadow-soft { box-shadow: var(--lumina-shadow-md); }
400
+ .shadow-elevated { box-shadow: var(--lumina-shadow-xl); }
401
+ .shadow-glow { box-shadow: var(--lumina-shadow-glow); }
402
+
403
+ /* Interactive card hover */
404
+ .card-interactive {
405
+ transition: all var(--lumina-duration-effective) var(--lumina-transition-easing);
406
+ }
407
+
408
+ .card-interactive:hover {
409
+ transform: translateY(-4px);
410
+ box-shadow: var(--lumina-shadow-xl);
411
+ }
412
+
413
+ /* Icon container with glow */
414
+ .icon-glow {
415
+ position: relative;
416
+ }
417
+
418
+ .icon-glow::after {
419
+ content: '';
420
+ position: absolute;
421
+ inset: -4px;
422
+ border-radius: inherit;
423
+ background: radial-gradient(
424
+ circle,
425
+ rgba(var(--lumina-color-primary-rgb), 0.3) 0%,
426
+ transparent 70%
427
+ );
428
+ opacity: 0;
429
+ transition: opacity var(--lumina-transition-duration) ease;
430
+ }
431
+
432
+ .icon-glow:hover::after {
433
+ opacity: 1;
434
+ }
435
+
436
+
437
+ /* === DECK VIEWPORT (mobile: avoid container > 100vh, 100dvh when supported) === */
438
+ .lumina-deck-root {
439
+ height: 100vh;
440
+ overflow: hidden;
441
+ }
442
+ @supports (height: 100dvh) {
443
+ .lumina-deck-root {
444
+ height: 100dvh;
445
+ max-height: 100dvh;
446
+ }
447
+ }
448
+
449
+ /* === ANIMATION FOUC PREVENTION === */
450
+ /* Hide elements that are explicitly expected to be revealed by GSAP.
451
+ Scoped to current slide content only. */
452
+ .reveal-up,
453
+ .reveal-zoom,
454
+ .reveal-card,
455
+ .reveal-left,
456
+ .reveal-right,
457
+ .reveal-img,
458
+ .reveal-char,
459
+ .reveal-child,
460
+ [data-split-text] {
461
+ opacity: 0;
462
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * LUMINA DEEP UTILS
3
+ * Provides immutable operations for complex nested data structures.
4
+ *
5
+ * Design Principles:
6
+ * 1. Immutability: All operations return a NEW object reference for the modified branch.
7
+ * 2. Safety: Path traversal checks for existence.
8
+ * 3. Performance: Structural sharing is preserved for unchanged branches.
9
+ */
10
+
11
+ /**
12
+ * Retrieves a value from a nested object using dot notation.
13
+ * @param obj - The source object.
14
+ * @param path - Dot-separated path (e.g., "slides.0.elements.1").
15
+ * @returns The value at path or undefined.
16
+ */
17
+ export function getByPath(obj: any, path: string): any {
18
+ if (!obj || !path) return undefined;
19
+ const parts = path.split('.');
20
+ let current = obj;
21
+ for (const part of parts) {
22
+ if (current === null || current === undefined) return undefined;
23
+ current = current[part];
24
+ }
25
+ return current;
26
+ }
27
+
28
+ /**
29
+ * Sets a value deep in a nested object, returning a new object (immutable).
30
+ * Handles array updates efficiently.
31
+ *
32
+ * @param obj - The source object.
33
+ * @param path - Dot-separated path.
34
+ * @param value - The new value to set.
35
+ * @returns A new object with the updated value.
36
+ */
37
+ export function setByPath(obj: any, path: string, value: any): any {
38
+ if (!obj) return obj;
39
+ if (!path) return value; // Root replacement logic could go here, but usually assume obj is root.
40
+
41
+ const parts = path.split('.');
42
+ const head = parts[0];
43
+ const tail = parts.slice(1).join('.');
44
+
45
+ // Handle Array
46
+ if (Array.isArray(obj)) {
47
+ const index = parseInt(head, 10);
48
+ if (isNaN(index)) return obj; // Invalid array usage
49
+
50
+ const newArray = [...obj];
51
+ if (parts.length === 1) {
52
+ newArray[index] = value;
53
+ } else {
54
+ newArray[index] = setByPath(obj[index], tail, value);
55
+ }
56
+ return newArray;
57
+ }
58
+
59
+ // Handle Object
60
+ const newObj = { ...obj };
61
+ if (parts.length === 1) {
62
+ newObj[head] = value;
63
+ } else {
64
+ newObj[head] = setByPath(obj[head] || {}, tail, value);
65
+ }
66
+ return newObj;
67
+ }
68
+
69
+ /**
70
+ * Inserts an item into an array at a specific deep path.
71
+ *
72
+ * @param obj - The root object.
73
+ * @param arrayPath - Path to the array itself (e.g., "slides.0.elements").
74
+ * @param index - Index to insert at.
75
+ * @param item - The item to insert.
76
+ * @returns New root object.
77
+ */
78
+ export function insertByPath(obj: any, arrayPath: string, index: number, item: any): any {
79
+ const targetArray = getByPath(obj, arrayPath);
80
+ if (!Array.isArray(targetArray)) {
81
+ console.warn(`[Lumina Deep] Insert failed: Target at '${arrayPath}' is not an array.`);
82
+ return obj;
83
+ }
84
+
85
+ const newArray = [...targetArray];
86
+ newArray.splice(index, 0, item);
87
+ return setByPath(obj, arrayPath, newArray);
88
+ }
89
+
90
+ /**
91
+ * Removes an item from an array at a specific deep path.
92
+ *
93
+ * @param obj - The root object.
94
+ * @param arrayPath - Path to the array.
95
+ * @param index - Index to remove.
96
+ * @returns New root object.
97
+ */
98
+ export function removeByPath(obj: any, arrayPath: string, index: number): any {
99
+ const targetArray = getByPath(obj, arrayPath);
100
+ if (!Array.isArray(targetArray)) {
101
+ console.warn(`[Lumina Deep] Remove failed: Target at '${arrayPath}' is not an array.`);
102
+ return obj;
103
+ }
104
+
105
+ const newArray = [...targetArray];
106
+ newArray.splice(index, 1);
107
+ return setByPath(obj, arrayPath, newArray);
108
+ }
109
+
110
+ /**
111
+ * Moves an item within a specific array (reordering).
112
+ *
113
+ * @param obj - Root object
114
+ * @param arrayPath - Path to the array
115
+ * @param fromIndex - Source index
116
+ * @param toIndex - Destination index
117
+ */
118
+ export function moveByPath(obj: any, arrayPath: string, fromIndex: number, toIndex: number): any {
119
+ const targetArray = getByPath(obj, arrayPath);
120
+ if (!Array.isArray(targetArray)) return obj;
121
+
122
+ const newArray = [...targetArray];
123
+ const [movedItem] = newArray.splice(fromIndex, 1);
124
+ newArray.splice(toIndex, 0, movedItem);
125
+
126
+ return setByPath(obj, arrayPath, newArray);
127
+ }