@morphika/andami 0.1.8 → 0.1.10

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 (49) hide show
  1. package/README.md +3 -0
  2. package/components/admin/nav-builder/NavBuilder.tsx +90 -14
  3. package/components/admin/nav-builder/NavGeneralSettings.tsx +521 -271
  4. package/components/admin/nav-builder/NavItemSettings.tsx +331 -312
  5. package/components/admin/nav-builder/NavMobileSettings.tsx +159 -140
  6. package/components/admin/nav-builder/NavSettingsFields.tsx +287 -21
  7. package/components/admin/nav-builder/NavSettingsPanel.tsx +137 -127
  8. package/components/blocks/TextBlockRenderer.tsx +1 -1
  9. package/components/builder/SettingsPanel.tsx +29 -543
  10. package/components/builder/editors/ButtonBlockEditor.tsx +8 -3
  11. package/components/builder/editors/CoverBlockEditor.tsx +14 -6
  12. package/components/builder/editors/ImageBlockEditor.tsx +8 -3
  13. package/components/builder/editors/ImageGridBlockEditor.tsx +8 -3
  14. package/components/builder/editors/ProjectGridEditor.tsx +7 -46
  15. package/components/builder/editors/SpacerBlockEditor.tsx +4 -1
  16. package/components/builder/editors/StaggerSettings.tsx +2 -1
  17. package/components/builder/editors/TextBlockEditor.tsx +8 -3
  18. package/components/builder/editors/VideoBlockEditor.tsx +10 -4
  19. package/components/builder/editors/section-icons.tsx +492 -0
  20. package/components/builder/editors/shared.tsx +23 -4
  21. package/components/builder/live-preview/GhostCard.tsx +84 -0
  22. package/components/builder/live-preview/LiveProjectGridPreview.tsx +294 -1010
  23. package/components/builder/live-preview/LiveTextEditor.tsx +1 -1
  24. package/components/builder/live-preview/ProjectCardWrapper.tsx +291 -0
  25. package/components/builder/live-preview/drag-utils.tsx +89 -0
  26. package/components/builder/live-preview/useDragReorder.ts +370 -0
  27. package/components/builder/settings-panel/AnimationTab.tsx +152 -0
  28. package/components/builder/settings-panel/BlockLayoutTab.tsx +13 -58
  29. package/components/builder/settings-panel/CardEntranceSection.tsx +114 -0
  30. package/components/builder/settings-panel/ColumnV2AnimationTab.tsx +32 -0
  31. package/components/builder/settings-panel/ColumnV2Settings.tsx +4 -1
  32. package/components/builder/settings-panel/CustomSectionSettings.tsx +150 -0
  33. package/components/builder/settings-panel/LayoutTab.tsx +11 -47
  34. package/components/builder/settings-panel/PageSettings.tsx +10 -4
  35. package/components/builder/settings-panel/ParallaxGroupSettings.tsx +6 -2
  36. package/components/builder/settings-panel/ParallaxSlideSettings.tsx +8 -3
  37. package/components/builder/settings-panel/SectionV2LayoutTab.tsx +11 -47
  38. package/components/builder/settings-panel/SectionV2Settings.tsx +6 -27
  39. package/components/builder/settings-panel/index.ts +6 -0
  40. package/components/builder/settings-panel/useSettingsPanelSelection.ts +184 -0
  41. package/components/ui/Navbar.tsx +151 -30
  42. package/lib/builder/serializer/migrations.ts +107 -0
  43. package/lib/builder/serializer/normalizers.ts +278 -0
  44. package/lib/builder/serializer/serializers.ts +393 -0
  45. package/lib/builder/serializer/shared.ts +102 -0
  46. package/lib/builder/serializer.ts +11 -846
  47. package/lib/sanity/types.ts +22 -0
  48. package/package.json +13 -10
  49. package/styles/base.css +7 -3
@@ -0,0 +1,492 @@
1
+ /**
2
+ * section-icons.tsx — Centralized colored section header icons.
3
+ *
4
+ * Single source of truth for ALL SettingsSection icons across the builder.
5
+ * Each concept (Spacing, Background, Border, etc.) has one icon + one color,
6
+ * used consistently everywhere it appears (blocks, sections, pages, etc.).
7
+ *
8
+ * Color palette (semantic grouping):
9
+ * Amber #d97706 / #fef3c7 — Spacing, Style
10
+ * Rose #e11d48 / #ffe4e6 — Offset
11
+ * Indigo #6366f1 / #e0e7ff — Background, Cover Background
12
+ * Slate #475569 / #f1f5f9 — Border
13
+ * Blue #2563eb / #dbeafe — Typography, Grid, Grid Gaps, Column Size
14
+ * Cyan #0891b2 / #cffafe — Content, Source, Images, Projects
15
+ * Green #059669 / #d1fae5 — Animation, Stagger, Transition
16
+ * Purple #7c3aed / #ede9fe — Position, Link, Alignment
17
+ * Orange #ea580c / #ffedd5 — Appearance, Cover Effects
18
+ * Teal #0d9488 / #ccfbf1 — Layout, Layout Preset
19
+ * Sky #0284c7 / #e0f2fe — Playback, Video, Height
20
+ * Pink #db2777 / #fce7f3 — Options, CTA Button
21
+ * Neutral #525252 / #f5f5f5 — General, Info, SEO
22
+ * Violet #8b5cf6 / #ede9fe — Navigation, Navbar Color
23
+ * Red #dc2626 / #fee2e2 — Overlay
24
+ *
25
+ * Session 163: Created — unified icon system for the entire builder.
26
+ */
27
+
28
+ import type { ReactNode } from "react";
29
+
30
+ // ============================================
31
+ // Icon + color definitions
32
+ // ============================================
33
+
34
+ export interface SectionIconDef {
35
+ icon: ReactNode;
36
+ color: string; // stroke/fill color for the SVG
37
+ bg: string; // background pill color
38
+ }
39
+
40
+ // ── Spacing (Amber) ──
41
+ export function SpacingIcon() {
42
+ return (
43
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#d97706" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
44
+ <rect x="3" y="3" width="18" height="18" rx="2" strokeDasharray="4 2" />
45
+ <rect x="7" y="7" width="10" height="10" rx="1" />
46
+ </svg>
47
+ );
48
+ }
49
+ SpacingIcon.bg = "#fef3c7";
50
+
51
+ // ── Offset (Rose) ──
52
+ export function OffsetIcon() {
53
+ return (
54
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#e11d48" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
55
+ <rect x="3" y="3" width="14" height="14" rx="2" strokeDasharray="4 2" opacity="0.5" />
56
+ <rect x="7" y="7" width="14" height="14" rx="2" />
57
+ <path d="M5 5 L7 7" strokeWidth="1.5" />
58
+ </svg>
59
+ );
60
+ }
61
+ OffsetIcon.bg = "#ffe4e6";
62
+
63
+ // ── Background (Indigo) ──
64
+ export function BackgroundIcon() {
65
+ return (
66
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#6366f1" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
67
+ <rect x="3" y="3" width="18" height="18" rx="2" />
68
+ <circle cx="8.5" cy="8.5" r="1.5" />
69
+ <polyline points="21 15 16 10 5 21" />
70
+ </svg>
71
+ );
72
+ }
73
+ BackgroundIcon.bg = "#e0e7ff";
74
+
75
+ // ── Border (Slate) ──
76
+ export function BorderIcon() {
77
+ return (
78
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#475569" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
79
+ <rect x="3" y="3" width="18" height="18" rx="3" />
80
+ <path d="M3 9 L21 9" strokeWidth="1.5" opacity="0.5" />
81
+ </svg>
82
+ );
83
+ }
84
+ BorderIcon.bg = "#f1f5f9";
85
+
86
+ // ── Alignment (Purple) ──
87
+ export function AlignmentIcon() {
88
+ return (
89
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
90
+ <line x1="18" y1="10" x2="6" y2="10" />
91
+ <line x1="21" y1="6" x2="3" y2="6" />
92
+ <line x1="15" y1="14" x2="9" y2="14" />
93
+ <line x1="18" y1="18" x2="6" y2="18" />
94
+ </svg>
95
+ );
96
+ }
97
+ AlignmentIcon.bg = "#ede9fe";
98
+
99
+ // ── Typography (Blue) ──
100
+ export function TypographyIcon() {
101
+ return (
102
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#2563eb" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
103
+ <polyline points="4 7 4 4 20 4 20 7" />
104
+ <line x1="9" y1="20" x2="15" y2="20" />
105
+ <line x1="12" y1="4" x2="12" y2="20" />
106
+ </svg>
107
+ );
108
+ }
109
+ TypographyIcon.bg = "#dbeafe";
110
+
111
+ // ── Grid / Grid Gaps (Blue) ──
112
+ export function GridIcon() {
113
+ return (
114
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#2563eb" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
115
+ <rect x="3" y="3" width="7" height="7" />
116
+ <rect x="14" y="3" width="7" height="7" />
117
+ <rect x="14" y="14" width="7" height="7" />
118
+ <rect x="3" y="14" width="7" height="7" />
119
+ </svg>
120
+ );
121
+ }
122
+ GridIcon.bg = "#dbeafe";
123
+
124
+ // ── Grid Gaps (Blue — slightly different icon) ──
125
+ export function GridGapsIcon() {
126
+ return (
127
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#2563eb" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
128
+ <rect x="3" y="3" width="7" height="7" rx="1" />
129
+ <rect x="14" y="3" width="7" height="7" rx="1" />
130
+ <rect x="3" y="14" width="7" height="7" rx="1" />
131
+ <rect x="14" y="14" width="7" height="7" rx="1" />
132
+ <line x1="12" y1="3" x2="12" y2="21" strokeDasharray="2 2" opacity="0.5" />
133
+ <line x1="3" y1="12" x2="21" y2="12" strokeDasharray="2 2" opacity="0.5" />
134
+ </svg>
135
+ );
136
+ }
137
+ GridGapsIcon.bg = "#dbeafe";
138
+
139
+ // ── Column Size (Blue) ──
140
+ export function ColumnSizeIcon() {
141
+ return (
142
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#2563eb" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
143
+ <rect x="3" y="4" width="18" height="16" rx="2" />
144
+ <line x1="9" y1="4" x2="9" y2="20" />
145
+ <path d="M4 12 L7 12" />
146
+ <path d="M11 12 L20 12" />
147
+ </svg>
148
+ );
149
+ }
150
+ ColumnSizeIcon.bg = "#dbeafe";
151
+
152
+ // ── Layout Preset (Teal) ──
153
+ export function LayoutPresetIcon() {
154
+ return (
155
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#0d9488" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
156
+ <rect x="3" y="3" width="18" height="18" rx="2" />
157
+ <line x1="9" y1="3" x2="9" y2="21" />
158
+ <line x1="3" y1="12" x2="9" y2="12" />
159
+ </svg>
160
+ );
161
+ }
162
+ LayoutPresetIcon.bg = "#ccfbf1";
163
+
164
+ // ── Layout (Teal) ──
165
+ export function LayoutIcon() {
166
+ return (
167
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#0d9488" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
168
+ <rect x="3" y="3" width="18" height="18" rx="2" />
169
+ <line x1="3" y1="9" x2="21" y2="9" />
170
+ <line x1="9" y1="9" x2="9" y2="21" />
171
+ </svg>
172
+ );
173
+ }
174
+ LayoutIcon.bg = "#ccfbf1";
175
+
176
+ // ── Content / Source (Cyan) ──
177
+ export function ContentIcon() {
178
+ return (
179
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#0891b2" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
180
+ <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
181
+ <polyline points="14 2 14 8 20 8" />
182
+ <line x1="16" y1="13" x2="8" y2="13" />
183
+ <line x1="16" y1="17" x2="8" y2="17" />
184
+ </svg>
185
+ );
186
+ }
187
+ ContentIcon.bg = "#cffafe";
188
+
189
+ // ── Source (Cyan) ──
190
+ export function SourceIcon() {
191
+ return (
192
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#0891b2" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
193
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
194
+ <polyline points="7 10 12 15 17 10" />
195
+ <line x1="12" y1="15" x2="12" y2="3" />
196
+ </svg>
197
+ );
198
+ }
199
+ SourceIcon.bg = "#cffafe";
200
+
201
+ // ── Images (Cyan) ──
202
+ export function ImagesIcon() {
203
+ return (
204
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#0891b2" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
205
+ <rect x="3" y="3" width="18" height="18" rx="2" />
206
+ <circle cx="8.5" cy="8.5" r="1.5" />
207
+ <polyline points="21 15 16 10 5 21" />
208
+ </svg>
209
+ );
210
+ }
211
+ ImagesIcon.bg = "#cffafe";
212
+
213
+ // ── Projects (Cyan) ──
214
+ export function ProjectsIcon() {
215
+ return (
216
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#0891b2" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
217
+ <rect x="3" y="3" width="18" height="7" rx="2" />
218
+ <rect x="3" y="14" width="18" height="7" rx="2" />
219
+ <circle cx="7" cy="6.5" r="1" />
220
+ <circle cx="7" cy="17.5" r="1" />
221
+ <line x1="10" y1="6.5" x2="17" y2="6.5" />
222
+ <line x1="10" y1="17.5" x2="17" y2="17.5" />
223
+ </svg>
224
+ );
225
+ }
226
+ ProjectsIcon.bg = "#cffafe";
227
+
228
+ // ── Text (Cyan) ──
229
+ export function TextIcon() {
230
+ return (
231
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#0891b2" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
232
+ <line x1="17" y1="10" x2="3" y2="10" />
233
+ <line x1="21" y1="6" x2="3" y2="6" />
234
+ <line x1="21" y1="14" x2="3" y2="14" />
235
+ <line x1="17" y1="18" x2="3" y2="18" />
236
+ </svg>
237
+ );
238
+ }
239
+ TextIcon.bg = "#cffafe";
240
+
241
+ // ── Columns (Blue) ──
242
+ export function ColumnsIcon() {
243
+ return (
244
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#2563eb" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
245
+ <rect x="3" y="3" width="18" height="18" rx="2" />
246
+ <line x1="9" y1="3" x2="9" y2="21" />
247
+ <line x1="15" y1="3" x2="15" y2="21" />
248
+ </svg>
249
+ );
250
+ }
251
+ ColumnsIcon.bg = "#dbeafe";
252
+
253
+ // ── Appearance (Orange) ──
254
+ export function AppearanceIcon() {
255
+ return (
256
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#ea580c" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
257
+ <circle cx="12" cy="12" r="9" />
258
+ <path d="M12 3 A9 9 0 0 1 12 21 Z" fill="#ea580c" opacity="0.2" />
259
+ <circle cx="12" cy="12" r="3" />
260
+ </svg>
261
+ );
262
+ }
263
+ AppearanceIcon.bg = "#ffedd5";
264
+
265
+ // ── Style (Amber) ──
266
+ export function StyleIcon() {
267
+ return (
268
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#d97706" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
269
+ <circle cx="13.5" cy="6.5" r="2.5" />
270
+ <path d="M17 2H7a5 5 0 0 0-5 5v10a5 5 0 0 0 5 5h10a5 5 0 0 0 5-5V7a5 5 0 0 0-5-5z" />
271
+ </svg>
272
+ );
273
+ }
274
+ StyleIcon.bg = "#fef3c7";
275
+
276
+ // ── Animation / Entrance (Green) ──
277
+ export function AnimationIcon() {
278
+ return (
279
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#059669" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
280
+ <path d="M5 12h14" />
281
+ <path d="M12 5l7 7-7 7" />
282
+ </svg>
283
+ );
284
+ }
285
+ AnimationIcon.bg = "#d1fae5";
286
+
287
+ // ── Stagger Children (Green) ──
288
+ export function StaggerIcon() {
289
+ return (
290
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#059669" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
291
+ <line x1="4" y1="6" x2="14" y2="6" />
292
+ <line x1="7" y1="12" x2="17" y2="12" />
293
+ <line x1="10" y1="18" x2="20" y2="18" />
294
+ </svg>
295
+ );
296
+ }
297
+ StaggerIcon.bg = "#d1fae5";
298
+
299
+ // ── Transition Effect (Green) ──
300
+ export function TransitionIcon() {
301
+ return (
302
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#059669" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
303
+ <rect x="2" y="7" width="8" height="10" rx="2" opacity="0.5" />
304
+ <rect x="14" y="7" width="8" height="10" rx="2" />
305
+ <path d="M10 12 L14 12" />
306
+ <path d="M12 10 L14 12 L12 14" />
307
+ </svg>
308
+ );
309
+ }
310
+ TransitionIcon.bg = "#d1fae5";
311
+
312
+ // ── Position (Purple) ──
313
+ export function PositionIcon() {
314
+ return (
315
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
316
+ <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" />
317
+ <circle cx="12" cy="10" r="3" />
318
+ </svg>
319
+ );
320
+ }
321
+ PositionIcon.bg = "#ede9fe";
322
+
323
+ // ── Link (Purple) ──
324
+ export function LinkIcon() {
325
+ return (
326
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
327
+ <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
328
+ <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
329
+ </svg>
330
+ );
331
+ }
332
+ LinkIcon.bg = "#ede9fe";
333
+
334
+ // ── Video (Sky) ──
335
+ export function VideoIcon() {
336
+ return (
337
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#0284c7" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
338
+ <rect x="2" y="4" width="14" height="16" rx="2" />
339
+ <path d="M16 10 L22 7 L22 17 L16 14 Z" />
340
+ </svg>
341
+ );
342
+ }
343
+ VideoIcon.bg = "#e0f2fe";
344
+
345
+ // ── Playback (Sky) ──
346
+ export function PlaybackIcon() {
347
+ return (
348
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#0284c7" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
349
+ <circle cx="12" cy="12" r="9" />
350
+ <polygon points="10 8 16 12 10 16" fill="#0284c7" opacity="0.3" />
351
+ </svg>
352
+ );
353
+ }
354
+ PlaybackIcon.bg = "#e0f2fe";
355
+
356
+ // ── Height (Sky) ──
357
+ export function HeightIcon() {
358
+ return (
359
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#0284c7" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
360
+ <path d="M12 4 L12 20" />
361
+ <path d="M8 8 L12 4 L16 8" />
362
+ <path d="M8 16 L12 20 L16 16" />
363
+ </svg>
364
+ );
365
+ }
366
+ HeightIcon.bg = "#e0f2fe";
367
+
368
+ // ── Options / CTA Button (Pink) ──
369
+ export function OptionsIcon() {
370
+ return (
371
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#db2777" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
372
+ <circle cx="12" cy="12" r="1" />
373
+ <circle cx="19" cy="12" r="1" />
374
+ <circle cx="5" cy="12" r="1" />
375
+ </svg>
376
+ );
377
+ }
378
+ OptionsIcon.bg = "#fce7f3";
379
+
380
+ // ── CTA Button (Pink) ──
381
+ export function CTAButtonIcon() {
382
+ return (
383
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#db2777" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
384
+ <rect x="3" y="8" width="18" height="8" rx="3" />
385
+ <line x1="8" y1="12" x2="16" y2="12" />
386
+ </svg>
387
+ );
388
+ }
389
+ CTAButtonIcon.bg = "#fce7f3";
390
+
391
+ // ── Settings (Amber) — generic ──
392
+ export function SettingsIcon() {
393
+ return (
394
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#d97706" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
395
+ <circle cx="12" cy="12" r="3" />
396
+ <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
397
+ </svg>
398
+ );
399
+ }
400
+ SettingsIcon.bg = "#fef3c7";
401
+
402
+ // ── General / Info (Neutral) ──
403
+ export function GeneralIcon() {
404
+ return (
405
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#525252" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
406
+ <circle cx="12" cy="12" r="9" />
407
+ <line x1="12" y1="16" x2="12" y2="12" />
408
+ <line x1="12" y1="8" x2="12.01" y2="8" />
409
+ </svg>
410
+ );
411
+ }
412
+ GeneralIcon.bg = "#f5f5f5";
413
+
414
+ // ── Info (Neutral) ──
415
+ export function InfoIcon() {
416
+ return (
417
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#525252" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
418
+ <circle cx="12" cy="12" r="9" />
419
+ <line x1="12" y1="16" x2="12" y2="12" />
420
+ <line x1="12" y1="8" x2="12.01" y2="8" />
421
+ </svg>
422
+ );
423
+ }
424
+ InfoIcon.bg = "#f5f5f5";
425
+
426
+ // ── SEO (Neutral) ──
427
+ export function SEOIcon() {
428
+ return (
429
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#525252" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
430
+ <circle cx="11" cy="11" r="8" />
431
+ <line x1="21" y1="21" x2="16.65" y2="16.65" />
432
+ </svg>
433
+ );
434
+ }
435
+ SEOIcon.bg = "#f5f5f5";
436
+
437
+ // ── Navigation (Violet) ──
438
+ export function NavigationIcon() {
439
+ return (
440
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#8b5cf6" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
441
+ <line x1="3" y1="12" x2="21" y2="12" />
442
+ <line x1="3" y1="6" x2="21" y2="6" />
443
+ <line x1="3" y1="18" x2="21" y2="18" />
444
+ </svg>
445
+ );
446
+ }
447
+ NavigationIcon.bg = "#ede9fe";
448
+
449
+ // ── Navbar Color (Violet) ──
450
+ export function NavbarColorIcon() {
451
+ return (
452
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#8b5cf6" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
453
+ <rect x="3" y="3" width="18" height="6" rx="2" />
454
+ <circle cx="7" cy="6" r="1.5" fill="#8b5cf6" opacity="0.3" />
455
+ <line x1="11" y1="6" x2="17" y2="6" />
456
+ </svg>
457
+ );
458
+ }
459
+ NavbarColorIcon.bg = "#ede9fe";
460
+
461
+ // ── Overlay (Red) ──
462
+ export function OverlayIcon() {
463
+ return (
464
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#dc2626" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
465
+ <rect x="3" y="3" width="18" height="18" rx="2" />
466
+ <rect x="3" y="3" width="18" height="18" rx="2" fill="#dc2626" opacity="0.15" />
467
+ </svg>
468
+ );
469
+ }
470
+ OverlayIcon.bg = "#fee2e2";
471
+
472
+ // ── Cover Background (Indigo) ──
473
+ export function CoverBackgroundIcon() {
474
+ return (
475
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#6366f1" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
476
+ <rect x="3" y="3" width="18" height="18" rx="2" />
477
+ <path d="M3 3 L21 21" opacity="0.4" />
478
+ <path d="M21 3 L3 21" opacity="0.4" />
479
+ </svg>
480
+ );
481
+ }
482
+ CoverBackgroundIcon.bg = "#e0e7ff";
483
+
484
+ // ── Cover Effects (Orange) ──
485
+ export function CoverEffectsIcon() {
486
+ return (
487
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#ea580c" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
488
+ <path d="M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z" />
489
+ </svg>
490
+ );
491
+ }
492
+ CoverEffectsIcon.bg = "#ffedd5";
@@ -153,8 +153,13 @@ export function SettingsField({
153
153
  // ============================================
154
154
 
155
155
  /**
156
- * Collapsible section: clean text header + thin border separator.
157
- * No gray background, no uppercase.
156
+ * Collapsible section with optional colored icon pill.
157
+ *
158
+ * When an `icon` is provided with a `bg` static property (from section-icons.tsx),
159
+ * it renders as a colored pill (26×26px rounded square with tinted background).
160
+ * Without `bg`, the icon renders inline in neutral gray (legacy behavior).
161
+ *
162
+ * Session 163: Upgraded to colored icon pills for visual consistency with nav builder.
158
163
  */
159
164
  export function SettingsSection({
160
165
  title,
@@ -169,14 +174,28 @@ export function SettingsSection({
169
174
  }) {
170
175
  const [isOpen, setIsOpen] = useState(defaultOpen);
171
176
 
177
+ // Check if icon component has a `.bg` property (colored icon from section-icons.tsx)
178
+ const iconBg = icon && typeof icon === "object"
179
+ ? ((icon as unknown as { type?: { bg?: string } }).type?.bg ?? null)
180
+ : null;
181
+
172
182
  return (
173
183
  <div className="border-b border-[#f0f0f0] last:border-b-0">
174
184
  <button
175
185
  onClick={() => setIsOpen(!isOpen)}
176
186
  className="w-full flex items-center justify-between px-4 py-3 transition-colors hover:bg-[#fafafa] group"
177
187
  >
178
- <span className="flex items-center gap-1.5 text-xs font-medium text-neutral-900">
179
- {icon && <span className="text-neutral-400 flex-shrink-0">{icon}</span>}
188
+ <span className="flex items-center gap-2 text-xs font-medium text-neutral-900">
189
+ {icon && iconBg ? (
190
+ <span
191
+ className="w-[26px] h-[26px] rounded-[7px] flex items-center justify-center shrink-0"
192
+ style={{ background: iconBg }}
193
+ >
194
+ {icon}
195
+ </span>
196
+ ) : icon ? (
197
+ <span className="text-neutral-400 flex-shrink-0">{icon}</span>
198
+ ) : null}
180
199
  {title}
181
200
  </span>
182
201
  <span className="text-base text-neutral-300 font-light leading-none transition-colors group-hover:text-neutral-500">
@@ -0,0 +1,84 @@
1
+ "use client";
2
+
3
+ /**
4
+ * GhostCard — Floating card that follows the cursor during drag.
5
+ * Portaled to document.body by the parent to escape canvas CSS transforms.
6
+ *
7
+ * Session 162: Extracted from LiveProjectGridPreview.tsx (Phase B3).
8
+ */
9
+
10
+ import { ProjectGridCard } from "./shared";
11
+ import { ADMIN_BLUE, CANCEL_DURATION, CrossArrowIcon, type DragState } from "./drag-utils";
12
+ import type { ProjectGridItem } from "../../../lib/sanity/types";
13
+
14
+ export default function GhostCard({
15
+ item,
16
+ thumbMap,
17
+ borderRadius,
18
+ dragState,
19
+ }: {
20
+ item: ProjectGridItem;
21
+ thumbMap: Map<string, string | undefined>;
22
+ borderRadius: number;
23
+ dragState: DragState;
24
+ }) {
25
+ const isCancelling = dragState.phase === "cancelling";
26
+ return (
27
+ <div
28
+ style={{
29
+ position: "fixed",
30
+ left: dragState.mouseX - dragState.offsetX,
31
+ top: dragState.mouseY - dragState.offsetY,
32
+ width: dragState.cardWidth,
33
+ height: dragState.cardHeight,
34
+ zIndex: 9999,
35
+ pointerEvents: "none",
36
+ borderRadius: borderRadius > 0 ? borderRadius : 8,
37
+ overflow: "hidden",
38
+ transform: isCancelling ? "scale(1)" : "scale(1.03)",
39
+ outline: `2px solid ${ADMIN_BLUE}`,
40
+ outlineOffset: -2,
41
+ boxShadow: isCancelling
42
+ ? "0 4px 16px rgba(0,0,0,0.15)"
43
+ : "0 16px 48px rgba(0,0,0,0.3)",
44
+ transition: isCancelling
45
+ ? `left ${CANCEL_DURATION}ms ease, top ${CANCEL_DURATION}ms ease, transform ${CANCEL_DURATION}ms ease, box-shadow ${CANCEL_DURATION}ms ease`
46
+ : undefined,
47
+ }}
48
+ >
49
+ <ProjectGridCard
50
+ slug={item.project_slug}
51
+ thumbPath={thumbMap.get(item.project_slug)}
52
+ customThumb={item.custom_thumbnail}
53
+ borderRadius={borderRadius > 0 ? String(borderRadius) : undefined}
54
+ style={{ width: "100%", height: "100%" }}
55
+ />
56
+ {/* Centered cross-arrow icon */}
57
+ <div
58
+ style={{
59
+ position: "absolute",
60
+ inset: 0,
61
+ display: "flex",
62
+ alignItems: "center",
63
+ justifyContent: "center",
64
+ pointerEvents: "none",
65
+ }}
66
+ >
67
+ <div
68
+ style={{
69
+ width: 40,
70
+ height: 40,
71
+ borderRadius: "50%",
72
+ backgroundColor: "rgba(255,255,255,0.92)",
73
+ display: "flex",
74
+ alignItems: "center",
75
+ justifyContent: "center",
76
+ boxShadow: "0 2px 12px rgba(0,0,0,0.2)",
77
+ }}
78
+ >
79
+ <CrossArrowIcon size={20} color="#333" />
80
+ </div>
81
+ </div>
82
+ </div>
83
+ );
84
+ }