@effectnode/media 0.3.0 → 0.5.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 (48) hide show
  1. package/dist/backend/movie-backend/agent/agent-backend.js +4 -4
  2. package/dist/backend/movie-backend/agent/prompt/story-writer.txt +137 -0
  3. package/dist/backend/movie-backend/agent/tools/edit-image.d.ts +1 -1
  4. package/dist/backend/movie-backend/agent/tools/edit-image.js +1 -1
  5. package/dist/backend/movie-backend/agent/tools/get-time.d.ts +1 -1
  6. package/dist/backend/movie-backend/agent/tools/grep-files.d.ts +1 -1
  7. package/dist/backend/movie-backend/agent/tools/grep-files.js +1 -1
  8. package/dist/backend/movie-backend/agent/tools/image-to-video-generation.d.ts +1 -1
  9. package/dist/backend/movie-backend/agent/tools/image-to-video-generation.js +1 -1
  10. package/dist/backend/movie-backend/agent/tools/index.d.ts +2 -2
  11. package/dist/backend/movie-backend/agent/tools/index.js +13 -13
  12. package/dist/backend/movie-backend/agent/tools/list-files.d.ts +1 -1
  13. package/dist/backend/movie-backend/agent/tools/list-files.js +1 -1
  14. package/dist/backend/movie-backend/agent/tools/read-file.d.ts +1 -1
  15. package/dist/backend/movie-backend/agent/tools/read-file.js +1 -1
  16. package/dist/backend/movie-backend/agent/tools/remove-file.d.ts +1 -1
  17. package/dist/backend/movie-backend/agent/tools/remove-file.js +1 -1
  18. package/dist/backend/movie-backend/agent/tools/rename-file.d.ts +1 -1
  19. package/dist/backend/movie-backend/agent/tools/rename-file.js +1 -1
  20. package/dist/backend/movie-backend/agent/tools/show-image.d.ts +1 -1
  21. package/dist/backend/movie-backend/agent/tools/show-image.js +1 -1
  22. package/dist/backend/movie-backend/agent/tools/stitch-videos.d.ts +1 -1
  23. package/dist/backend/movie-backend/agent/tools/stitch-videos.js +1 -1
  24. package/dist/backend/movie-backend/agent/tools/text-to-video-generation.d.ts +1 -1
  25. package/dist/backend/movie-backend/agent/tools/text-to-video-generation.js +1 -1
  26. package/dist/backend/movie-backend/agent/tools/update-file.d.ts +1 -1
  27. package/dist/backend/movie-backend/agent/tools/update-file.js +1 -1
  28. package/dist/backend/movie-backend/agent/tools/write-file.d.ts +1 -1
  29. package/dist/backend/movie-backend/agent/tools/write-file.js +1 -1
  30. package/dist/backend/movie-backend/core.js +4 -4
  31. package/dist/backend/movie-backend/generation-queue.js +102 -46
  32. package/dist/backend/movie-backend/render-media.js +1 -1
  33. package/frontend/index.html +8 -1
  34. package/frontend/public/lambobo.png +0 -0
  35. package/frontend/src/movie-app/MediaStudio.tsx +6 -2
  36. package/frontend/src/movie-app/SetupPage.tsx +98 -63
  37. package/frontend/src/movie-app/components/Aurora.tsx +13 -0
  38. package/frontend/src/movie-app/components/EditorTabs/GenerateVideoTab.tsx +5 -8
  39. package/frontend/src/movie-app/components/EditorTabs/MovieStudioTab.tsx +297 -189
  40. package/frontend/src/movie-app/components/EditorTabs/SetupAiModelTab.tsx +345 -0
  41. package/frontend/src/movie-app/components/ProjectEditorPage.tsx +40 -34
  42. package/frontend/src/movie-app/components/ProjectManager.tsx +80 -52
  43. package/frontend/src/movie-app/index.css +260 -19
  44. package/frontend/src/movie-app/stores/aiModelStore.ts +163 -0
  45. package/frontend/src/movie-app/stores/generationStore.ts +2 -2
  46. package/frontend/src/movie-app/stores/movieStudioStore.ts +45 -0
  47. package/package.json +2 -2
  48. package/frontend/src/movie-app/components/EditorTabs/ReferencesToVideoTab.tsx +0 -881
@@ -163,15 +163,20 @@ export default function ProjectManager() {
163
163
  // ========== Render ==========
164
164
 
165
165
  return (
166
- <div className="flex flex-col h-full">
167
- {/* Header */}
168
- <div className="flex items-center justify-between mb-8">
169
- <div>
170
- <h2 className="text-xl font-semibold text-ink-900 tracking-tight">
171
- Projects
172
- </h2>
173
- <p className="text-sm text-ink-600/70 mt-0.5">
174
- Manage your media projects
166
+ <div className="flex flex-col">
167
+ {/* Hero */}
168
+ <header className="flex flex-col gap-7 md:flex-row md:items-end md:justify-between">
169
+ <div className="max-w-xl">
170
+ <p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-tiffany-600">
171
+ Lambobo Studio
172
+ </p>
173
+ <h1 className="mt-3 font-display text-4xl font-light leading-[1.06] text-ink-900 md:text-5xl">
174
+ Make the movies{" "}
175
+ <span className="wordmark font-medium italic">you dream of</span>
176
+ </h1>
177
+ <p className="mt-4 text-sm leading-relaxed text-ink-600/80">
178
+ A local AI studio for writing stories, casting characters, and
179
+ turning scenes into motion.
175
180
  </p>
176
181
  </div>
177
182
  <button
@@ -179,24 +184,24 @@ export default function ProjectManager() {
179
184
  resetForm();
180
185
  setShowCreate(true);
181
186
  }}
182
- className="flex items-center gap-1.5 px-4 py-2 bg-tiffany-500 hover:bg-tiffany-600 active:bg-tiffany-700 text-ink-950 text-sm font-medium rounded-2xl transition-all duration-150 shadow-glow hover:shadow-glow"
187
+ className="btn-primary flex w-fit items-center gap-1.5 rounded-2xl px-5 py-2.5 text-sm font-medium"
183
188
  >
184
189
  {PlusIcon}
185
190
  New Project
186
191
  </button>
187
- </div>
192
+ </header>
188
193
 
189
194
  {/* Error */}
190
195
  {error && (
191
- <div className="mb-4 px-4 py-3 bg-red-50 border border-red-200 rounded-2xl text-red-600 text-sm">
196
+ <div className="mt-8 rounded-2xl border border-rose-200 bg-rose-50/70 px-4 py-3 text-sm text-rose-600 backdrop-blur-sm">
192
197
  {error}
193
198
  </div>
194
199
  )}
195
200
 
196
201
  {/* Create / Edit Form */}
197
202
  {showCreate && (
198
- <div className="mb-5 p-6 bg-white border border-ink-200 rounded-3xl shadow-card">
199
- <h3 className="text-sm font-semibold text-ink-900 mb-3">
203
+ <div className="glass mt-8 rounded-3xl p-6 shadow-card">
204
+ <h3 className="font-display text-lg font-medium text-ink-900">
200
205
  {editingId ? "Edit Project" : "New Project"}
201
206
  </h3>
202
207
  <input
@@ -210,7 +215,7 @@ export default function ProjectManager() {
210
215
  editingId ? handleUpdate() : handleCreate();
211
216
  if (e.key === "Escape") resetForm();
212
217
  }}
213
- className="w-full mb-3 px-4 py-2.5 bg-ink-50 border border-ink-200 rounded-2xl text-ink-900 text-sm placeholder-ink-500/40 focus:outline-none focus:border-tiffany-500 focus:ring-2 focus:ring-tiffany-500/30 transition-all"
218
+ className="mt-4 w-full rounded-2xl border border-white/70 bg-white/70 px-4 py-2.5 text-sm text-ink-900 placeholder-ink-500/40 transition-all focus:border-tiffany-500 focus:outline-none focus:ring-2 focus:ring-tiffany-500/30"
214
219
  />
215
220
  <input
216
221
  type="text"
@@ -222,18 +227,18 @@ export default function ProjectManager() {
222
227
  editingId ? handleUpdate() : handleCreate();
223
228
  if (e.key === "Escape") resetForm();
224
229
  }}
225
- className="w-full mb-4 px-4 py-2.5 bg-ink-50 border border-ink-200 rounded-2xl text-ink-900 text-sm placeholder-ink-500/40 focus:outline-none focus:border-tiffany-500 focus:ring-2 focus:ring-tiffany-500/30 transition-all"
230
+ className="mt-3 w-full rounded-2xl border border-white/70 bg-white/70 px-4 py-2.5 text-sm text-ink-900 placeholder-ink-500/40 transition-all focus:border-tiffany-500 focus:outline-none focus:ring-2 focus:ring-tiffany-500/30"
226
231
  />
227
- <div className="flex gap-2">
232
+ <div className="mt-4 flex gap-2">
228
233
  <button
229
234
  onClick={editingId ? handleUpdate : handleCreate}
230
- className="px-5 py-2 bg-tiffany-500 hover:bg-tiffany-600 active:bg-tiffany-700 text-ink-950 text-sm font-medium rounded-2xl transition-all duration-150"
235
+ className="btn-primary rounded-2xl px-5 py-2 text-sm font-medium"
231
236
  >
232
237
  {editingId ? "Update" : "Create"}
233
238
  </button>
234
239
  <button
235
240
  onClick={resetForm}
236
- className="px-5 py-2 bg-ink-100 hover:bg-ink-300 text-ink-700 text-sm font-medium rounded-2xl transition-all duration-150"
241
+ className="rounded-2xl border border-ink-200 bg-white/60 px-5 py-2 text-sm font-medium text-ink-700 transition-colors hover:bg-ink-100"
237
242
  >
238
243
  Cancel
239
244
  </button>
@@ -241,64 +246,87 @@ export default function ProjectManager() {
241
246
  </div>
242
247
  )}
243
248
 
249
+ {/* Section label */}
250
+ <div className="mt-12 mb-4 flex items-center gap-3">
251
+ <h2 className="font-display text-xl font-medium text-ink-900">
252
+ Projects
253
+ </h2>
254
+ <span className="text-xs text-ink-500">
255
+ {loading ? "…" : `${projects.length} in the studio`}
256
+ </span>
257
+ <div className="hairline flex-1" />
258
+ </div>
259
+
244
260
  {/* Project List */}
245
261
  {loading ? (
246
- <div className="flex items-center justify-center py-16 text-ink-600/50 text-sm">
247
- Loading...
262
+ <div className="flex items-center justify-center py-16 text-sm text-ink-600/50">
263
+ Loading
248
264
  </div>
249
265
  ) : projects.length === 0 ? (
250
- <div className="flex flex-col items-center justify-center py-16">
251
- <div className="w-16 h-16 bg-ink-100 rounded-3xl flex items-center justify-center mb-4">
266
+ <div className="glass flex flex-col items-center justify-center rounded-3xl px-6 py-20 text-center shadow-card">
267
+ <div className="thumb flex h-20 w-20 items-center justify-center rounded-3xl text-ink-900/70 shadow-glow-sm">
252
268
  <svg
253
- width="28"
254
- height="28"
269
+ width="32"
270
+ height="32"
255
271
  viewBox="0 0 24 24"
256
272
  fill="none"
257
- stroke="#81d8d0"
273
+ stroke="currentColor"
258
274
  strokeWidth="1.5"
259
275
  >
260
276
  <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />
261
277
  </svg>
262
278
  </div>
263
- <p className="text-sm font-medium text-ink-800">No projects yet</p>
264
- <p className="text-xs text-ink-600/60 mt-1">
265
- Create your first project to get started
279
+ <p className="mt-5 font-display text-2xl font-light text-ink-900">
280
+ Your studio is empty
281
+ </p>
282
+ <p className="mt-1.5 max-w-xs text-sm leading-relaxed text-ink-600/70">
283
+ Create your first project and start turning an idea into motion.
266
284
  </p>
285
+ <button
286
+ onClick={() => {
287
+ resetForm();
288
+ setShowCreate(true);
289
+ }}
290
+ className="btn-primary mt-6 flex items-center gap-1.5 rounded-2xl px-5 py-2.5 text-sm font-medium"
291
+ >
292
+ {PlusIcon}
293
+ New Project
294
+ </button>
267
295
  </div>
268
296
  ) : (
269
- <div className="flex-1 overflow-y-auto space-y-2">
297
+ <div className="space-y-2.5">
270
298
  {projects.map((p) => (
271
299
  <div
272
300
  key={p.id}
273
- className="flex items-center gap-3 px-4 py-3.5 bg-white border border-ink-200 hover:border-ink-400 hover:shadow-card-hover rounded-2xl transition-all duration-150 group"
301
+ className="group flex items-center gap-3 rounded-2xl px-4 py-3.5 shadow-card transition-all duration-200 hover:-translate-y-0.5 hover:shadow-card-hover glass"
274
302
  >
275
303
  {/* Project info */}
276
304
  <button
277
305
  onClick={() => openProject(p.id)}
278
- className="flex-1 flex items-center gap-3 text-left min-w-0"
306
+ className="flex min-w-0 flex-1 items-center gap-3 text-left"
279
307
  >
280
- <div className="shrink-0 w-9 h-9 bg-ink-100 border border-ink-200 rounded-2xl flex items-center justify-center">
308
+ <div className="thumb flex h-11 w-11 shrink-0 items-center justify-center rounded-2xl text-ink-900/70">
281
309
  <svg
282
- width="15"
283
- height="15"
310
+ width="16"
311
+ height="16"
284
312
  viewBox="0 0 24 24"
285
313
  fill="none"
286
- stroke="#81d8d0"
314
+ stroke="currentColor"
287
315
  strokeWidth="2"
288
316
  >
289
317
  <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />
290
318
  </svg>
291
319
  </div>
292
320
  <div className="min-w-0">
293
- <p className="text-sm font-medium text-ink-900 truncate">
321
+ <p className="truncate font-display text-[15px] font-medium leading-tight text-ink-900">
294
322
  {p.name}
295
323
  </p>
296
324
  {p.description ? (
297
- <p className="text-xs text-ink-600/60 truncate mt-0.5">
325
+ <p className="mt-0.5 truncate text-xs text-ink-600/70">
298
326
  {p.description}
299
327
  </p>
300
328
  ) : (
301
- <p className="text-xs text-ink-500/60 italic mt-0.5">
329
+ <p className="mt-0.5 text-xs italic text-ink-500/60">
302
330
  No description
303
331
  </p>
304
332
  )}
@@ -306,38 +334,38 @@ export default function ProjectManager() {
306
334
  </button>
307
335
 
308
336
  {/* Actions */}
309
- <div className="flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity duration-150">
337
+ <div className="flex items-center gap-0.5 opacity-0 transition-opacity duration-150 group-hover:opacity-100">
310
338
  <button
311
339
  onClick={() => startEdit(p)}
312
- className="p-2 text-ink-600/40 hover:text-tiffany-600 hover:bg-ink-100 rounded-xl transition-colors"
340
+ className="rounded-xl p-2 text-ink-600/40 transition-colors hover:bg-ink-100 hover:text-tiffany-700"
313
341
  title="Edit"
314
342
  >
315
343
  {EditIcon}
316
344
  </button>
317
345
  <button
318
346
  onClick={() => openFolder(p.id, "upload")}
319
- className="p-2 text-ink-600/40 hover:text-tiffany-600 hover:bg-ink-100 rounded-xl transition-colors"
347
+ className="rounded-xl p-2 text-ink-600/40 transition-colors hover:bg-ink-100 hover:text-tiffany-700"
320
348
  title="Open Uploads Folder"
321
349
  >
322
350
  {UploadFolderIcon}
323
351
  </button>
324
352
  <button
325
353
  onClick={() => openFolder(p.id, "output")}
326
- className="p-2 text-ink-600/40 hover:text-tiffany-600 hover:bg-ink-100 rounded-xl transition-colors"
354
+ className="rounded-xl p-2 text-ink-600/40 transition-colors hover:bg-ink-100 hover:text-tiffany-700"
327
355
  title="Open Outputs Folder"
328
356
  >
329
357
  {OutputFolderIcon}
330
358
  </button>
331
359
  <button
332
360
  onClick={() => setDeleteConfirm(p.id)}
333
- className="p-2 text-ink-600/40 hover:text-red-600 hover:bg-red-50 rounded-xl transition-colors"
361
+ className="rounded-xl p-2 text-ink-600/40 transition-colors hover:bg-rose-50 hover:text-rose-600"
334
362
  title="Delete"
335
363
  >
336
364
  {DeleteIcon}
337
365
  </button>
338
366
  <button
339
367
  onClick={() => openProject(p.id)}
340
- className="p-2 text-ink-600/40 hover:text-tiffany-600 hover:bg-ink-100 rounded-xl transition-colors"
368
+ className="rounded-xl p-2 text-ink-600/40 transition-colors hover:bg-ink-100 hover:text-tiffany-700"
341
369
  title="Open"
342
370
  >
343
371
  {OpenIcon}
@@ -350,25 +378,25 @@ export default function ProjectManager() {
350
378
 
351
379
  {/* Delete Confirmation Modal */}
352
380
  {deleteConfirm && (
353
- <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm">
354
- <div className="bg-white border border-ink-200 rounded-3xl p-8 w-80 shadow-modal">
355
- <h3 className="text-sm font-semibold text-ink-900 mb-2">
381
+ <div className="fixed inset-0 z-50 flex items-center justify-center bg-ink-950/40 backdrop-blur-sm">
382
+ <div className="w-80 rounded-3xl border border-white/70 bg-white/90 p-8 shadow-modal backdrop-blur-xl">
383
+ <h3 className="font-display text-lg font-medium text-ink-900">
356
384
  Delete Project
357
385
  </h3>
358
- <p className="text-sm text-ink-600/80 mb-5 leading-relaxed">
386
+ <p className="mt-2 text-sm leading-relaxed text-ink-600/80">
359
387
  Are you sure you want to delete this project? This action cannot
360
388
  be undone.
361
389
  </p>
362
- <div className="flex gap-2 justify-end">
390
+ <div className="mt-5 flex justify-end gap-2">
363
391
  <button
364
392
  onClick={() => setDeleteConfirm(null)}
365
- className="px-4 py-2 bg-ink-100 hover:bg-ink-300 text-ink-700 text-sm font-medium rounded-2xl transition-colors"
393
+ className="rounded-2xl bg-ink-100 px-4 py-2 text-sm font-medium text-ink-700 transition-colors hover:bg-ink-300"
366
394
  >
367
395
  Cancel
368
396
  </button>
369
397
  <button
370
398
  onClick={() => handleDelete(deleteConfirm)}
371
- className="px-4 py-2 bg-red-500 hover:bg-red-600 text-white text-sm font-medium rounded-2xl transition-colors"
399
+ className="rounded-2xl bg-rose-500 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-rose-600"
372
400
  >
373
401
  Delete
374
402
  </button>
@@ -1,22 +1,80 @@
1
1
  @import "tailwindcss";
2
2
 
3
+ /* ==========================================================================
4
+ Lambobo Studio — design tokens
5
+ A dreamy, tiffany-blue film studio. Gradients read like light through
6
+ shallow water: tiffany teal → aqua → periwinkle → blush.
7
+ ========================================================================== */
8
+
9
+ @theme {
10
+ /* ---- Type ---- */
11
+ --font-sans: "Avenir Next", Avenir, "Helvetica Neue", -apple-system,
12
+ BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
13
+ --font-display: "Cormorant", "Iowan Old Style", "Baskerville", "Georgia",
14
+ serif;
15
+ --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas,
16
+ "Liberation Mono", monospace;
17
+
18
+ /* ---- Tiffany (the brand) ---- */
19
+ --color-tiffany-50: #effbf9;
20
+ --color-tiffany-100: #d7f4f1;
21
+ --color-tiffany-200: #b0e8e3;
22
+ --color-tiffany-300: #81d8d0;
23
+ --color-tiffany-400: #4fc3bc;
24
+ --color-tiffany-500: #0abab5;
25
+ --color-tiffany-600: #089a96;
26
+ --color-tiffany-700: #077a77;
27
+ --color-tiffany-800: #06605e;
28
+ --color-tiffany-900: #04403f;
29
+ --color-tiffany-950: #022726;
30
+
31
+ /* ---- Ink (cool, teal-tinted neutrals) ---- */
32
+ --color-ink-50: #f3f7f7;
33
+ --color-ink-100: #e6edee;
34
+ --color-ink-200: #d2dee1;
35
+ --color-ink-300: #b0c3c8;
36
+ --color-ink-400: #83a0a7;
37
+ --color-ink-500: #5e7c83;
38
+ --color-ink-600: #47636b;
39
+ --color-ink-700: #395057;
40
+ --color-ink-800: #283a40;
41
+ --color-ink-900: #18272c;
42
+ --color-ink-950: #0d191c;
43
+
44
+ /* ---- Dream accents (the aurora) ---- */
45
+ --color-dream-periwinkle: #c3b4f4;
46
+ --color-dream-blush: #ffd6ea;
47
+ --color-dream-sky: #b7e1fb;
48
+ --color-dream-mint: #c8f1e6;
49
+
50
+ /* ---- Shadows ---- */
51
+ --shadow-glow: 0 8px 30px -6px rgba(10, 186, 181, 0.55),
52
+ 0 2px 10px -2px rgba(10, 186, 181, 0.4);
53
+ --shadow-glow-sm: 0 0 14px -2px rgba(10, 186, 181, 0.45);
54
+ --shadow-card: 0 1px 2px rgba(13, 25, 28, 0.04),
55
+ 0 10px 30px -12px rgba(13, 25, 28, 0.16);
56
+ --shadow-card-hover: 0 2px 4px rgba(13, 25, 28, 0.05),
57
+ 0 18px 44px -14px rgba(10, 186, 181, 0.35);
58
+ --shadow-modal: 0 24px 80px -16px rgba(13, 25, 28, 0.35);
59
+ }
60
+
3
61
  :root {
4
62
  color-scheme: light;
5
63
  }
6
64
 
7
65
  body {
8
66
  margin: 0;
9
- background: #f5f8f9;
10
- color: #3e4d54;
11
- font-family:
12
- "Avenir Next",
13
- Avenir,
14
- "Helvetica Neue",
15
- -apple-system,
16
- BlinkMacSystemFont,
17
- "Segoe UI",
18
- Roboto,
19
- sans-serif;
67
+ background-color: #f3f7f7;
68
+ background-image: radial-gradient(
69
+ 120% 90% at 85% -10%,
70
+ rgba(183, 225, 251, 0.5),
71
+ transparent 60%
72
+ ),
73
+ radial-gradient(100% 80% at 0% 0%, rgba(255, 214, 234, 0.4), transparent 55%);
74
+ background-attachment: fixed;
75
+ color: #283a40;
76
+ font-family: "Avenir Next", Avenir, "Helvetica Neue", -apple-system,
77
+ BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
20
78
  -webkit-font-smoothing: antialiased;
21
79
  -moz-osx-font-smoothing: grayscale;
22
80
  }
@@ -24,23 +82,206 @@ body {
24
82
  code,
25
83
  pre,
26
84
  .font-mono {
27
- font-family:
28
- ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas,
85
+ font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas,
29
86
  "Liberation Mono", monospace;
30
87
  }
31
88
 
32
- /* Subtle light scrollbar */
89
+ ::selection {
90
+ background: rgba(10, 186, 181, 0.24);
91
+ color: #0d191c;
92
+ }
93
+
94
+ :focus-visible {
95
+ outline: 2px solid rgba(10, 186, 181, 0.6);
96
+ outline-offset: 2px;
97
+ }
98
+
99
+ /* Subtle tiffany scrollbar */
33
100
  ::-webkit-scrollbar {
34
- width: 8px;
35
- height: 8px;
101
+ width: 10px;
102
+ height: 10px;
36
103
  }
37
104
  ::-webkit-scrollbar-track {
38
105
  background: transparent;
39
106
  }
40
107
  ::-webkit-scrollbar-thumb {
41
- background: #d8e2e6;
42
- border-radius: 4px;
108
+ background: rgba(10, 186, 181, 0.28);
109
+ border-radius: 9999px;
110
+ border: 2px solid transparent;
111
+ background-clip: padding-box;
43
112
  }
44
113
  ::-webkit-scrollbar-thumb:hover {
45
- background: #bcc9cf;
114
+ background: rgba(10, 186, 181, 0.5);
115
+ background-clip: padding-box;
116
+ }
117
+
118
+ /* ==========================================================================
119
+ Atmosphere — the signature. Slow-drifting aurora of tiffany, periwinkle,
120
+ blush and sky, breathing behind a frosted-glass interface.
121
+ ========================================================================== */
122
+
123
+ .aurora {
124
+ position: fixed;
125
+ inset: 0;
126
+ z-index: 0;
127
+ overflow: hidden;
128
+ pointer-events: none;
129
+ background: radial-gradient(
130
+ 120% 90% at 85% -10%,
131
+ rgba(183, 225, 251, 0.55),
132
+ transparent 60%
133
+ ),
134
+ radial-gradient(
135
+ 100% 80% at 8% 0%,
136
+ rgba(255, 214, 234, 0.5),
137
+ transparent 55%
138
+ ),
139
+ linear-gradient(165deg, #eaf8f6 0%, #f3f7f7 48%, #eceff9 100%);
140
+ }
141
+
142
+ .aurora-blob {
143
+ position: absolute;
144
+ border-radius: 50%;
145
+ filter: blur(70px);
146
+ opacity: 0.55;
147
+ will-change: transform;
148
+ }
149
+
150
+ .aurora-blob--tiffany {
151
+ width: 44rem;
152
+ height: 44rem;
153
+ left: -12rem;
154
+ top: -14rem;
155
+ background: radial-gradient(circle at 35% 35%, #81d8d0, transparent 68%);
156
+ animation: drift-a 36s ease-in-out infinite;
157
+ }
158
+
159
+ .aurora-blob--periwinkle {
160
+ width: 40rem;
161
+ height: 40rem;
162
+ right: -10rem;
163
+ top: -8rem;
164
+ background: radial-gradient(circle at 60% 40%, #c3b4f4, transparent 70%);
165
+ animation: drift-b 44s ease-in-out infinite;
166
+ }
167
+
168
+ .aurora-blob--blush {
169
+ width: 36rem;
170
+ height: 36rem;
171
+ left: 18%;
172
+ bottom: -16rem;
173
+ background: radial-gradient(circle at 50% 50%, #ffd6ea, transparent 70%);
174
+ animation: drift-c 40s ease-in-out infinite;
175
+ }
176
+
177
+ .aurora-blob--sky {
178
+ width: 46rem;
179
+ height: 46rem;
180
+ right: 8%;
181
+ bottom: -12rem;
182
+ background: radial-gradient(circle at 50% 50%, #b7e1fb, transparent 70%);
183
+ animation: drift-a 50s ease-in-out infinite;
184
+ }
185
+
186
+ @keyframes drift-a {
187
+ 0%,
188
+ 100% {
189
+ transform: translate3d(0, 0, 0) scale(1);
190
+ }
191
+ 50% {
192
+ transform: translate3d(4rem, 3rem, 0) scale(1.08);
193
+ }
194
+ }
195
+
196
+ @keyframes drift-b {
197
+ 0%,
198
+ 100% {
199
+ transform: translate3d(0, 0, 0) scale(1);
200
+ }
201
+ 50% {
202
+ transform: translate3d(-3rem, 4rem, 0) scale(0.94);
203
+ }
204
+ }
205
+
206
+ @keyframes drift-c {
207
+ 0%,
208
+ 100% {
209
+ transform: translate3d(0, 0, 0) scale(1);
210
+ }
211
+ 50% {
212
+ transform: translate3d(2rem, -4rem, 0) scale(1.12);
213
+ }
214
+ }
215
+
216
+ @media (prefers-reduced-motion: reduce) {
217
+ .aurora-blob {
218
+ animation: none;
219
+ }
220
+ }
221
+
222
+ /* ==========================================================================
223
+ Surfaces & primitives
224
+ ========================================================================== */
225
+
226
+ .glass {
227
+ background: rgba(255, 255, 255, 0.55);
228
+ border: 1px solid rgba(255, 255, 255, 0.65);
229
+ backdrop-filter: blur(20px) saturate(150%);
230
+ -webkit-backdrop-filter: blur(20px) saturate(150%);
231
+ }
232
+
233
+ .glass-dark {
234
+ background: rgba(13, 25, 28, 0.82);
235
+ border: 1px solid rgba(129, 216, 208, 0.16);
236
+ backdrop-filter: blur(20px) saturate(140%);
237
+ -webkit-backdrop-filter: blur(20px) saturate(140%);
238
+ }
239
+
240
+ .wordmark {
241
+ background: linear-gradient(
242
+ 115deg,
243
+ #0abab5 0%,
244
+ #4fc3bc 34%,
245
+ #c3b4f4 72%,
246
+ #ffd6ea 100%
247
+ );
248
+ -webkit-background-clip: text;
249
+ background-clip: text;
250
+ color: transparent;
251
+ }
252
+
253
+ .thumb {
254
+ background-image: linear-gradient(135deg, #b0e8e3 0%, #81d8d0 50%, #c3b4f4 100%);
255
+ }
256
+
257
+ .btn-primary {
258
+ background-image: linear-gradient(
259
+ 120deg,
260
+ #089a96 0%,
261
+ #0abab5 55%,
262
+ #4fc3bc 100%
263
+ );
264
+ color: #ffffff;
265
+ box-shadow: var(--shadow-glow);
266
+ transition: filter 150ms ease, box-shadow 150ms ease, transform 150ms ease;
267
+ }
268
+
269
+ .btn-primary:hover {
270
+ filter: brightness(1.05);
271
+ box-shadow: 0 10px 34px -6px rgba(10, 186, 181, 0.6),
272
+ 0 4px 12px -4px rgba(10, 186, 181, 0.45);
273
+ }
274
+
275
+ .btn-primary:active {
276
+ transform: translateY(1px);
277
+ filter: brightness(0.97);
278
+ }
279
+
280
+ .hairline {
281
+ height: 1px;
282
+ background: linear-gradient(
283
+ to right,
284
+ rgba(176, 195, 200, 0.7),
285
+ rgba(176, 195, 200, 0)
286
+ );
46
287
  }