collavre 0.20.3 → 0.22.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 (163) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/collavre/actiontext.css +92 -2
  3. data/app/assets/stylesheets/collavre/code_highlight.css +144 -26
  4. data/app/assets/stylesheets/collavre/comments_popup.css +133 -2
  5. data/app/assets/stylesheets/collavre/landing.css +507 -0
  6. data/app/assets/stylesheets/collavre/popup.css +148 -0
  7. data/app/channels/collavre/agent_channel.rb +205 -0
  8. data/app/channels/collavre/comments_presence_channel.rb +7 -0
  9. data/app/controllers/collavre/admin/integrations_controller.rb +93 -0
  10. data/app/controllers/collavre/admin/settings_controller.rb +22 -17
  11. data/app/controllers/collavre/api/v1/agents_controller.rb +777 -0
  12. data/app/controllers/collavre/api/v1/base_controller.rb +46 -0
  13. data/app/controllers/collavre/application_controller.rb +27 -0
  14. data/app/controllers/collavre/attachments_controller.rb +30 -2
  15. data/app/controllers/collavre/channels_controller.rb +23 -0
  16. data/app/controllers/collavre/comments_controller.rb +1 -1
  17. data/app/controllers/collavre/creatives/attachments_controller.rb +79 -0
  18. data/app/controllers/collavre/creatives_controller.rb +141 -7
  19. data/app/controllers/collavre/landing_controller.rb +8 -0
  20. data/app/controllers/collavre/public_assets_controller.rb +24 -0
  21. data/app/controllers/collavre/tasks_controller.rb +12 -4
  22. data/app/controllers/collavre/topics_controller.rb +36 -30
  23. data/app/controllers/concerns/collavre/comments/approval_actions.rb +57 -14
  24. data/app/helpers/collavre/comments_helper.rb +7 -0
  25. data/app/helpers/collavre/public_assets_helper.rb +14 -0
  26. data/app/javascript/components/InlineLexicalEditor.jsx +42 -59
  27. data/app/javascript/components/plugins/attachment_cleanup_plugin.jsx +3 -0
  28. data/app/javascript/components/plugins/image_upload_plugin.jsx +12 -0
  29. data/app/javascript/controllers/__tests__/link_creative_controller.test.js +447 -0
  30. data/app/javascript/controllers/comment_controller.js +15 -1
  31. data/app/javascript/controllers/comments/__tests__/presence_controller.test.js +108 -0
  32. data/app/javascript/controllers/comments/form_controller.js +4 -0
  33. data/app/javascript/controllers/comments/list_controller.js +27 -9
  34. data/app/javascript/controllers/comments/popup_controller.js +9 -0
  35. data/app/javascript/controllers/comments/presence_controller.js +137 -4
  36. data/app/javascript/controllers/comments/topics_controller.js +15 -0
  37. data/app/javascript/controllers/creatives/__tests__/sync_controller.test.js +89 -0
  38. data/app/javascript/controllers/creatives/__tests__/tree_controller.test.js +120 -0
  39. data/app/javascript/controllers/creatives/sync_controller.js +30 -9
  40. data/app/javascript/controllers/creatives/tree_controller.js +23 -0
  41. data/app/javascript/controllers/index.js +4 -1
  42. data/app/javascript/controllers/landing_video_controller.js +53 -0
  43. data/app/javascript/controllers/link_creative_controller.js +451 -29
  44. data/app/javascript/creatives/tree_renderer.js +6 -0
  45. data/app/javascript/lib/api/__tests__/queue_manager.test.js +27 -0
  46. data/app/javascript/lib/api/creatives.js +13 -0
  47. data/app/javascript/lib/api/queue_manager.js +17 -5
  48. data/app/javascript/lib/lexical/__tests__/color_import.test.js +318 -0
  49. data/app/javascript/lib/lexical/__tests__/minimize_html.test.js +259 -0
  50. data/app/javascript/lib/lexical/color_import.js +186 -0
  51. data/app/javascript/lib/lexical/minimize_html.js +182 -0
  52. data/app/javascript/lib/lexical/video_node.jsx +96 -0
  53. data/app/javascript/modules/__tests__/command_args_form.test.js +103 -0
  54. data/app/javascript/modules/__tests__/html_content_empty.test.js +41 -0
  55. data/app/javascript/modules/__tests__/markdown_source_reconcile.test.js +70 -0
  56. data/app/javascript/modules/command_args_form.js +22 -4
  57. data/app/javascript/modules/command_menu.js +27 -0
  58. data/app/javascript/modules/creative_row_editor.js +227 -17
  59. data/app/javascript/modules/html_content_empty.js +12 -0
  60. data/app/javascript/modules/markdown_source_reconcile.js +53 -0
  61. data/app/jobs/collavre/ai_agent_job.rb +89 -3
  62. data/app/jobs/collavre/cancel_offline_delegated_tasks_job.rb +134 -0
  63. data/app/jobs/collavre/claude_channel_presence_job.rb +91 -0
  64. data/app/jobs/collavre/drop_trigger_job.rb +37 -8
  65. data/app/mailers/collavre/application_mailer.rb +1 -1
  66. data/app/models/collavre/agent_subscription.rb +52 -0
  67. data/app/models/collavre/channel/injected_message.rb +5 -0
  68. data/app/models/collavre/channel.rb +87 -0
  69. data/app/models/collavre/comment/claude_channel_permission.rb +145 -0
  70. data/app/models/collavre/comment.rb +70 -5
  71. data/app/models/collavre/creative/describable.rb +202 -3
  72. data/app/models/collavre/creative.rb +2 -0
  73. data/app/models/collavre/creative_share.rb +1 -0
  74. data/app/models/collavre/integration_setting.rb +35 -0
  75. data/app/models/collavre/preview_channel.rb +93 -0
  76. data/app/models/collavre/system_setting.rb +13 -2
  77. data/app/models/collavre/task.rb +34 -5
  78. data/app/models/collavre/topic.rb +8 -25
  79. data/app/models/collavre/user.rb +4 -0
  80. data/app/models/concerns/collavre/ai_agent_resolvable.rb +12 -3
  81. data/app/services/collavre/agent_session_abort.rb +28 -0
  82. data/app/services/collavre/ai_agent/claude_channel_adapter.rb +79 -0
  83. data/app/services/collavre/ai_agent/response_finalizer.rb +4 -2
  84. data/app/services/collavre/ai_agent_service.rb +68 -49
  85. data/app/services/collavre/ai_client.rb +3 -3
  86. data/app/services/collavre/attachment_backfill.rb +26 -0
  87. data/app/services/collavre/channel_attacher.rb +58 -0
  88. data/app/services/collavre/comments/mcp_command.rb +31 -1
  89. data/app/services/collavre/creatives/breadcrumb_resolver.rb +91 -0
  90. data/app/services/collavre/creatives/filter_pipeline.rb +26 -42
  91. data/app/services/collavre/creatives/filters/search_filter.rb +12 -2
  92. data/app/services/collavre/creatives/index_query.rb +110 -8
  93. data/app/services/collavre/creatives/permission_filter.rb +50 -0
  94. data/app/services/collavre/creatives/reveal_path_resolver.rb +118 -0
  95. data/app/services/collavre/creatives/tree_builder.rb +7 -3
  96. data/app/services/collavre/crons/recurring_task_arguments.rb +28 -0
  97. data/app/services/collavre/google_calendar_service.rb +4 -2
  98. data/app/services/collavre/markdown_converter.rb +130 -15
  99. data/app/services/collavre/markdown_importer.rb +7 -2
  100. data/app/services/collavre/orchestration/policy_resolver.rb +11 -1
  101. data/app/services/collavre/orchestration/stuck_detector.rb +22 -2
  102. data/app/services/collavre/tools/creative_attach_files_service.rb +62 -0
  103. data/app/services/collavre/tools/creative_list_attachments_service.rb +42 -0
  104. data/app/services/collavre/tools/creative_remove_attachment_service.rb +37 -0
  105. data/app/services/collavre/tools/cron_list_service.rb +1 -14
  106. data/app/services/collavre/tools/permission_denied_error.rb +9 -0
  107. data/app/services/collavre/tools/preview_attach_service.rb +128 -0
  108. data/app/services/collavre/tools/preview_detach_service.rb +61 -0
  109. data/app/services/collavre/tools/topic_authorizer.rb +24 -0
  110. data/app/services/collavre/topic_branch_service.rb +34 -26
  111. data/app/services/collavre/topics/orphaned_cron_notifier.rb +68 -0
  112. data/app/views/admin/shared/_tabs.html.erb +1 -0
  113. data/app/views/collavre/admin/integrations/_category.html.erb +22 -0
  114. data/app/views/collavre/admin/integrations/_setting_row.html.erb +70 -0
  115. data/app/views/collavre/admin/integrations/index.html.erb +42 -0
  116. data/app/views/collavre/admin/settings/_system_tab.html.erb +8 -0
  117. data/app/views/collavre/comments/_channel_chips.html.erb +33 -0
  118. data/app/views/collavre/comments/_comment.html.erb +16 -2
  119. data/app/views/collavre/comments/_comments_popup.html.erb +4 -1
  120. data/app/views/collavre/creatives/_inline_edit_form.html.erb +19 -0
  121. data/app/views/collavre/creatives/index.html.erb +10 -2
  122. data/app/views/collavre/landing/show.html.erb +130 -0
  123. data/app/views/collavre/shared/_link_creative_modal.html.erb +6 -2
  124. data/app/views/layouts/collavre/landing.html.erb +33 -0
  125. data/config/locales/admin.en.yml +4 -2
  126. data/config/locales/admin.ko.yml +4 -2
  127. data/config/locales/channels.en.yml +13 -0
  128. data/config/locales/channels.ko.yml +13 -0
  129. data/config/locales/claude_channel.en.yml +16 -0
  130. data/config/locales/claude_channel.ko.yml +16 -0
  131. data/config/locales/comments.en.yml +5 -0
  132. data/config/locales/comments.ko.yml +5 -0
  133. data/config/locales/creatives.en.yml +11 -0
  134. data/config/locales/creatives.ko.yml +10 -0
  135. data/config/locales/integrations.en.yml +55 -0
  136. data/config/locales/integrations.ko.yml +55 -0
  137. data/config/locales/landing.en.yml +51 -0
  138. data/config/locales/landing.ko.yml +51 -0
  139. data/config/routes.rb +30 -0
  140. data/db/migrate/20260526000000_create_channels.rb +42 -0
  141. data/db/migrate/20260527000000_add_dismissed_at_to_channels.rb +6 -0
  142. data/db/migrate/20260527000100_backfill_dismissed_at_for_legacy_detached_channels.rb +28 -0
  143. data/db/migrate/20260528000000_add_preview_channel_unique_index.rb +31 -0
  144. data/db/migrate/20260529000000_add_primary_agent_id_to_topics.rb +40 -0
  145. data/db/migrate/20260529100000_create_integration_settings.rb +15 -0
  146. data/db/migrate/20260609000000_drop_action_text_rich_texts.rb +20 -0
  147. data/db/migrate/20260609005000_add_session_id_to_topics.rb +16 -0
  148. data/db/migrate/20260609010000_create_agent_subscriptions.rb +19 -0
  149. data/db/migrate/20260609020000_add_last_seen_at_to_agent_subscriptions.rb +23 -0
  150. data/db/migrate/20260609030000_add_session_id_to_agent_subscriptions.rb +17 -0
  151. data/db/migrate/20260609190659_backfill_creative_files_into_description.rb +24 -0
  152. data/db/seeds.rb +19 -0
  153. data/lib/collavre/aws_credentials.rb +75 -0
  154. data/lib/collavre/engine.rb +50 -0
  155. data/lib/collavre/integration_settings/key_definition.rb +35 -0
  156. data/lib/collavre/integration_settings/registry.rb +60 -0
  157. data/lib/collavre/integration_settings/resolver.rb +71 -0
  158. data/lib/collavre/integration_settings.rb +46 -0
  159. data/lib/collavre/ses_settings_interceptor.rb +72 -0
  160. data/lib/collavre/version.rb +1 -1
  161. data/lib/collavre.rb +3 -0
  162. metadata +82 -2
  163. data/app/services/collavre/openclaw_abort_service.rb +0 -45
@@ -0,0 +1,507 @@
1
+ /* ============================================================================
2
+ * Collavre Landing Page
3
+ * Uses design tokens exclusively — no hardcoded colors.
4
+ * Primary accent: --color-active (the blue used throughout the app UI)
5
+ *
6
+ * Typography scale (4 levels only):
7
+ * L1 --text-7 Hero title, CTA title
8
+ * L2 --text-5 Section titles, tagline
9
+ * L3 --text-3 Card titles, subtitles, button-lg
10
+ * L4 --text-2 Body copy, descriptions, buttons, footer
11
+ * ============================================================================ */
12
+
13
+ /* ─── Reset & Base ──────────────────────────────────────────────────────────── */
14
+ .landing-page {
15
+ overflow-x: hidden;
16
+ scroll-behavior: smooth;
17
+ }
18
+
19
+ .landing-page nav {
20
+ display: none; /* hide app navigation */
21
+ }
22
+
23
+ .landing {
24
+ font-family: var(--font-sans);
25
+ color: var(--text-primary);
26
+ line-height: var(--leading-2);
27
+ }
28
+
29
+ /* Container: sets the content boundary */
30
+ .landing-container {
31
+ max-width: 1080px;
32
+ margin: 0 auto;
33
+ padding: 0 var(--space-5);
34
+ }
35
+
36
+ /* ─── Buttons ───────────────────────────────────────────────────────────────── */
37
+ .landing-btn {
38
+ display: inline-flex;
39
+ align-items: center;
40
+ justify-content: center;
41
+ padding: var(--space-2) var(--space-5);
42
+ border-radius: var(--radius-3);
43
+ font-weight: var(--weight-6);
44
+ font-size: var(--text-2); /* L4 */
45
+ text-decoration: none;
46
+ transition: all 0.2s var(--ease-2);
47
+ background: var(--color-active);
48
+ color: white;
49
+ border: none;
50
+ cursor: pointer;
51
+ }
52
+
53
+ .landing-btn:hover {
54
+ filter: brightness(1.1);
55
+ transform: translateY(-1px);
56
+ box-shadow: var(--shadow-2);
57
+ }
58
+
59
+ .landing-btn-lg {
60
+ padding: var(--space-3) var(--space-7);
61
+ font-size: var(--text-3); /* L3 */
62
+ border-radius: var(--radius-4);
63
+ }
64
+
65
+ .landing-btn-ghost {
66
+ background: transparent;
67
+ color: var(--text-muted);
68
+ border: var(--border-1) solid var(--border-color);
69
+ }
70
+
71
+ .landing-btn-ghost:hover {
72
+ color: var(--text-primary);
73
+ border-color: var(--text-muted);
74
+ background: var(--surface-hover);
75
+ filter: none;
76
+ }
77
+
78
+ /* ─── Hero ──────────────────────────────────────────────────────────────────── */
79
+ .landing-hero {
80
+ position: relative;
81
+ padding: var(--space-10) 0 var(--space-8);
82
+ text-align: center;
83
+ }
84
+
85
+ .landing-hero-glow {
86
+ position: absolute;
87
+ top: -20%;
88
+ left: 50%;
89
+ transform: translateX(-50%);
90
+ width: 600px;
91
+ height: 600px;
92
+ background: radial-gradient(circle, color-mix(in srgb, var(--color-active) 12%, transparent) 0%, transparent 70%);
93
+ pointer-events: none;
94
+ z-index: 0;
95
+ }
96
+
97
+ .landing-hero-title { /* L1 — main headline */
98
+ font-size: var(--text-7);
99
+ font-weight: var(--weight-8);
100
+ letter-spacing: 0.02em;
101
+ line-height: var(--leading-1);
102
+ color: var(--text-primary);
103
+ margin: 0 auto var(--space-5);
104
+ max-width: 900px;
105
+ position: relative;
106
+ z-index: 1;
107
+ }
108
+
109
+ .landing-hero-sub { /* L3 — subline */
110
+ font-size: var(--text-3);
111
+ font-weight: var(--weight-6);
112
+ color: var(--text-muted);
113
+ margin: 0 auto var(--space-5);
114
+ max-width: 900px;
115
+ text-align: center;
116
+ position: relative;
117
+ z-index: 1;
118
+ }
119
+
120
+ .landing-hero-brand { /* L1 — "콜라브" in brand color */
121
+ font-size: var(--text-7);
122
+ font-weight: var(--weight-8);
123
+ letter-spacing: 0.02em;
124
+ color: var(--color-active);
125
+ margin: 0 auto var(--space-7);
126
+ text-align: center;
127
+ position: relative;
128
+ z-index: 1;
129
+ }
130
+
131
+ .landing-hero-actions {
132
+ display: flex;
133
+ gap: var(--space-3);
134
+ justify-content: center;
135
+ flex-wrap: wrap;
136
+ position: relative;
137
+ z-index: 1;
138
+ }
139
+
140
+ /* ─── Demo Section ──────────────────────────────────────────────────────────── */
141
+
142
+ .landing-demo-frame {
143
+ position: relative;
144
+ max-width: 900px;
145
+ margin: 0 auto;
146
+ aspect-ratio: 16 / 9;
147
+ border-radius: var(--radius-4);
148
+ border: var(--border-1) solid var(--border-color);
149
+ background: var(--surface-section);
150
+ overflow: hidden;
151
+ box-shadow: var(--shadow-3);
152
+ }
153
+
154
+ .landing-demo-video {
155
+ width: 100%;
156
+ height: 100%;
157
+ object-fit: cover;
158
+ display: block;
159
+ }
160
+
161
+ /* ─── Demo: Progress bar ──────────────────────────────────────────────────── */
162
+ .landing-demo-progress {
163
+ position: absolute;
164
+ bottom: 0;
165
+ left: 0;
166
+ right: 0;
167
+ height: 3px;
168
+ background: color-mix(in srgb, var(--text-primary) 15%, transparent);
169
+ z-index: 2;
170
+ }
171
+
172
+ .landing-demo-progress-fill {
173
+ height: 100%;
174
+ width: 0;
175
+ background: var(--color-active);
176
+ transition: width 0.1s linear;
177
+ }
178
+
179
+ /* ─── Demo: Play/Pause toggle ─────────────────────────────────────────────── */
180
+ .landing-demo-toggle {
181
+ position: absolute;
182
+ inset: 0;
183
+ width: 100%;
184
+ height: 100%;
185
+ display: flex;
186
+ align-items: center;
187
+ justify-content: center;
188
+ background: transparent;
189
+ border: none;
190
+ cursor: pointer;
191
+ opacity: 0;
192
+ transition: opacity 0.25s ease;
193
+ z-index: 1;
194
+ }
195
+
196
+ .landing-demo-frame:hover .landing-demo-toggle {
197
+ opacity: 1;
198
+ background: color-mix(in srgb, var(--bg-primary) 40%, transparent);
199
+ }
200
+
201
+ .landing-demo-toggle-icon {
202
+ font-size: var(--text-5); /* L2 */
203
+ color: var(--text-primary);
204
+ background: color-mix(in srgb, var(--bg-primary) 80%, transparent);
205
+ width: 56px;
206
+ height: 56px;
207
+ border-radius: 50%;
208
+ display: flex;
209
+ align-items: center;
210
+ justify-content: center;
211
+ backdrop-filter: blur(4px);
212
+ box-shadow: var(--shadow-2);
213
+ }
214
+
215
+ /* ─── Sections (shared) ─────────────────────────────────────────────────────── */
216
+ .landing-section {
217
+ padding: var(--space-10) 0;
218
+ }
219
+
220
+ .landing-section-title { /* L2 */
221
+ font-size: var(--text-5);
222
+ font-weight: var(--weight-7);
223
+ text-align: center;
224
+ letter-spacing: -0.02em;
225
+ margin-bottom: var(--space-3);
226
+ }
227
+
228
+ .landing-section-sub { /* L4 */
229
+ font-size: var(--text-2);
230
+ color: var(--text-muted);
231
+ text-align: center;
232
+ max-width: 560px;
233
+ margin: 0 auto var(--space-8);
234
+ }
235
+
236
+ /* ─── Problem Section ───────────────────────────────────────────────────────── */
237
+ .landing-problem {
238
+ background: var(--surface-section);
239
+ }
240
+
241
+ .landing-problem-grid {
242
+ display: grid;
243
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
244
+ gap: var(--space-5);
245
+ max-width: 900px;
246
+ margin: var(--space-7) auto 0;
247
+ }
248
+
249
+ .landing-problem-card {
250
+ padding: var(--space-5);
251
+ border-radius: var(--radius-3);
252
+ border: var(--border-1) solid var(--border-color);
253
+ background: var(--surface-bg);
254
+ transition: border-color 0.2s var(--ease-2);
255
+ }
256
+
257
+ .landing-problem-card:hover {
258
+ border-color: var(--text-muted);
259
+ }
260
+
261
+ .landing-problem-icon {
262
+ font-size: var(--text-5); /* L2 — emoji decorative */
263
+ display: block;
264
+ margin-bottom: var(--space-3);
265
+ }
266
+
267
+ .landing-problem-card h3 { /* L3 */
268
+ font-size: var(--text-3);
269
+ font-weight: var(--weight-6);
270
+ margin-bottom: var(--space-2);
271
+ }
272
+
273
+ .landing-problem-card p { /* L4 */
274
+ font-size: var(--text-2);
275
+ color: var(--text-muted);
276
+ line-height: var(--leading-2);
277
+ }
278
+
279
+ /* ─── Features Grid ─────────────────────────────────────────────────────────── */
280
+ .landing-features {
281
+ background: var(--surface-bg);
282
+ }
283
+
284
+ .landing-features-grid {
285
+ display: grid;
286
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
287
+ gap: var(--space-5);
288
+ }
289
+
290
+ .landing-feature-card {
291
+ padding: var(--space-5);
292
+ border-radius: var(--radius-3);
293
+ border: var(--border-1) solid var(--border-color);
294
+ background: var(--surface-section);
295
+ transition: all 0.25s var(--ease-2);
296
+ }
297
+
298
+ .landing-feature-card:hover {
299
+ transform: translateY(-2px);
300
+ box-shadow: var(--shadow-2);
301
+ border-color: var(--color-active);
302
+ }
303
+
304
+ .landing-feature-icon {
305
+ font-size: var(--text-5); /* L2 — emoji decorative */
306
+ margin-bottom: var(--space-3);
307
+ }
308
+
309
+ .landing-feature-card h3 { /* L3 */
310
+ font-size: var(--text-3);
311
+ font-weight: var(--weight-6);
312
+ margin-bottom: var(--space-2);
313
+ }
314
+
315
+ .landing-feature-card p { /* L4 */
316
+ font-size: var(--text-2);
317
+ color: var(--text-muted);
318
+ line-height: var(--leading-2);
319
+ }
320
+
321
+ /* ─── How It Works ──────────────────────────────────────────────────────────── */
322
+ .landing-how {
323
+ background: var(--surface-section);
324
+ }
325
+
326
+ .landing-steps {
327
+ display: flex;
328
+ align-items: flex-start;
329
+ justify-content: center;
330
+ gap: var(--space-3);
331
+ flex-wrap: wrap;
332
+ max-width: 900px;
333
+ margin: var(--space-7) auto 0;
334
+ }
335
+
336
+ .landing-step {
337
+ flex: 1;
338
+ min-width: 200px;
339
+ max-width: 260px;
340
+ text-align: center;
341
+ }
342
+
343
+ .landing-step-num {
344
+ width: 48px;
345
+ height: 48px;
346
+ border-radius: var(--radius-round);
347
+ background: var(--color-active);
348
+ color: white;
349
+ font-size: var(--text-3); /* L3 */
350
+ font-weight: var(--weight-7);
351
+ display: inline-flex;
352
+ align-items: center;
353
+ justify-content: center;
354
+ margin-bottom: var(--space-3);
355
+ }
356
+
357
+ .landing-step h3 { /* L3 */
358
+ font-size: var(--text-3);
359
+ font-weight: var(--weight-6);
360
+ margin-bottom: var(--space-2);
361
+ }
362
+
363
+ .landing-step p { /* L4 */
364
+ font-size: var(--text-2);
365
+ color: var(--text-muted);
366
+ line-height: var(--leading-2);
367
+ }
368
+
369
+ .landing-step-arrow {
370
+ font-size: var(--text-5); /* L2 */
371
+ color: var(--text-muted);
372
+ padding-top: var(--space-3);
373
+ user-select: none;
374
+ }
375
+
376
+ /* ─── CTA Section ───────────────────────────────────────────────────────────── */
377
+ .landing-cta {
378
+ text-align: center;
379
+ padding: var(--space-11) 0;
380
+ background: var(--surface-bg);
381
+ }
382
+
383
+ .landing-cta-title { /* L1 */
384
+ font-size: var(--text-7);
385
+ font-weight: var(--weight-8);
386
+ letter-spacing: 0.02em;
387
+ margin-bottom: var(--space-3);
388
+ }
389
+
390
+ .landing-cta-sub { /* L4 */
391
+ font-size: var(--text-2);
392
+ color: var(--text-muted);
393
+ margin-bottom: var(--space-7);
394
+ max-width: 480px;
395
+ margin-left: auto;
396
+ margin-right: auto;
397
+ }
398
+
399
+ /* ─── Footer (center-aligned) ───────────────────────────────────────────────── */
400
+ .landing-footer {
401
+ border-top: var(--border-1) solid var(--border-color);
402
+ padding: var(--space-5) 0;
403
+ }
404
+
405
+ .landing-footer-inner {
406
+ display: flex;
407
+ flex-direction: column;
408
+ align-items: center;
409
+ gap: var(--space-2);
410
+ text-align: center;
411
+ }
412
+
413
+ .landing-footer-brand { /* L4 */
414
+ font-weight: var(--weight-6);
415
+ font-size: var(--text-2);
416
+ color: var(--text-primary);
417
+ }
418
+
419
+ .landing-footer-copy { /* L4 */
420
+ font-size: var(--text-2);
421
+ color: var(--text-muted);
422
+ }
423
+
424
+ /* ─── Demo: Theme-aware video switching ────────────────────────────────────── */
425
+ .landing-demo-dark { display: none; }
426
+
427
+ body.dark-mode .landing-demo-light { display: none; }
428
+ body.dark-mode .landing-demo-dark { display: block; }
429
+
430
+ @media (prefers-color-scheme: dark) {
431
+ body:not(.light-mode) .landing-demo-light { display: none; }
432
+ body:not(.light-mode) .landing-demo-dark { display: block; }
433
+ }
434
+
435
+ /* ─── Responsive ────────────────────────────────────────────────────────────── */
436
+ @media (max-width: 640px) {
437
+ .landing-container {
438
+ padding: 0 var(--space-3);
439
+ }
440
+
441
+ .landing-hero {
442
+ padding: var(--space-9) 0 var(--space-7);
443
+ }
444
+
445
+ .landing-hero-title { /* L1 scaled down on mobile */
446
+ font-size: var(--text-5);
447
+ }
448
+
449
+ .landing-hero-brand { /* L1 scaled down on mobile */
450
+ font-size: var(--text-5);
451
+ }
452
+
453
+ .landing-hero-glow {
454
+ width: 350px;
455
+ height: 350px;
456
+ }
457
+
458
+ .landing-section {
459
+ padding: var(--space-8) 0;
460
+ }
461
+
462
+ .landing-steps {
463
+ flex-direction: column;
464
+ align-items: center;
465
+ }
466
+
467
+ .landing-step-arrow {
468
+ transform: rotate(90deg);
469
+ padding: 0;
470
+ }
471
+
472
+ .landing-step {
473
+ max-width: 100%;
474
+ }
475
+
476
+ .landing-features-grid {
477
+ grid-template-columns: 1fr;
478
+ }
479
+
480
+ .landing-cta-title { /* L1 scaled down on mobile */
481
+ font-size: var(--text-5);
482
+ }
483
+ }
484
+
485
+ /* ─── Entrance animations ───────────────────────────────────────────────────── */
486
+ @keyframes landing-fade-up {
487
+ from {
488
+ opacity: 0;
489
+ transform: translateY(20px);
490
+ }
491
+ to {
492
+ opacity: 1;
493
+ transform: translateY(0);
494
+ }
495
+ }
496
+
497
+ .landing-hero-title,
498
+ .landing-hero-sub,
499
+ .landing-hero-brand,
500
+ .landing-hero-actions {
501
+ animation: landing-fade-up 0.6s var(--ease-out-3) both;
502
+ }
503
+
504
+ .landing-hero-title { animation-delay: 0s; }
505
+ .landing-hero-sub { animation-delay: 0.1s; }
506
+ .landing-hero-brand { animation-delay: 0.2s; }
507
+ .landing-hero-actions { animation-delay: 0.3s; }
@@ -396,3 +396,151 @@ a.popup-menu-item:hover {
396
396
  min-width: 100%;
397
397
  }
398
398
  }
399
+
400
+ /* ── Link-creative picker: mini-tree + flat search results ── */
401
+ .link-creative-list {
402
+ list-style: none;
403
+ margin: 0;
404
+ padding: 0;
405
+ max-height: 320px;
406
+ overflow-y: auto;
407
+ }
408
+
409
+ /* Tree (browse) rows */
410
+ .link-tree-item {
411
+ list-style: none;
412
+ }
413
+
414
+ .link-tree-children {
415
+ list-style: none;
416
+ margin: 0;
417
+ padding: 0;
418
+ }
419
+
420
+ .link-tree-row {
421
+ display: flex;
422
+ align-items: center;
423
+ gap: 0.35em;
424
+ padding: 0.3em 0.5em;
425
+ border-radius: var(--radius-1);
426
+ cursor: pointer;
427
+ }
428
+
429
+ .link-tree-row:hover,
430
+ .link-tree-row.active {
431
+ background: var(--surface-hover);
432
+ }
433
+
434
+ .link-tree-toggle {
435
+ flex: 0 0 auto;
436
+ width: 1.75em;
437
+ height: 1.75em;
438
+ display: inline-flex;
439
+ align-items: center;
440
+ justify-content: center;
441
+ background: transparent;
442
+ border: none;
443
+ border-radius: var(--radius-1);
444
+ padding: 0;
445
+ line-height: 1;
446
+ color: var(--text-secondary);
447
+ cursor: pointer;
448
+ appearance: none;
449
+ }
450
+
451
+ .link-tree-toggle svg {
452
+ display: block;
453
+ width: 16px;
454
+ height: 16px;
455
+ }
456
+
457
+ .link-tree-toggle:hover {
458
+ color: var(--text-primary);
459
+ background: var(--surface-hover);
460
+ }
461
+
462
+ .link-tree-toggle-empty {
463
+ visibility: hidden;
464
+ cursor: default;
465
+ }
466
+
467
+ .link-tree-label {
468
+ flex: 1 1 auto;
469
+ min-width: 0;
470
+ overflow: hidden;
471
+ text-overflow: ellipsis;
472
+ white-space: nowrap;
473
+ color: var(--text-primary);
474
+ }
475
+
476
+ .link-tree-loading,
477
+ .link-tree-empty,
478
+ .link-popup-message {
479
+ list-style: none;
480
+ padding: 0.35em 0.6em;
481
+ color: var(--text-muted);
482
+ font-size: var(--text-0);
483
+ }
484
+
485
+ /* Flat search results with breadcrumb */
486
+ .link-result-item {
487
+ list-style: none;
488
+ padding: 0.4em 0.5em;
489
+ border-radius: var(--radius-1);
490
+ cursor: pointer;
491
+ }
492
+
493
+ .link-result-item:hover,
494
+ .link-result-item.active {
495
+ background: var(--surface-hover);
496
+ }
497
+
498
+ .link-result-label {
499
+ color: var(--text-primary);
500
+ overflow: hidden;
501
+ text-overflow: ellipsis;
502
+ white-space: nowrap;
503
+ }
504
+
505
+ .link-result-path {
506
+ display: flex;
507
+ flex-wrap: wrap;
508
+ align-items: center;
509
+ gap: 0.15em;
510
+ margin-top: 0.15em;
511
+ font-size: var(--text-00);
512
+ color: var(--text-muted);
513
+ }
514
+
515
+ .link-crumb {
516
+ background: transparent;
517
+ border: none;
518
+ padding: 0 0.1em;
519
+ font-size: inherit;
520
+ color: var(--text-muted);
521
+ cursor: pointer;
522
+ appearance: none;
523
+ max-width: 14ch;
524
+ overflow: hidden;
525
+ text-overflow: ellipsis;
526
+ white-space: nowrap;
527
+ }
528
+
529
+ .link-crumb:hover {
530
+ color: var(--color-link);
531
+ text-decoration: underline;
532
+ }
533
+
534
+ .link-crumb-sep {
535
+ color: var(--text-muted);
536
+ opacity: 0.6;
537
+ }
538
+
539
+ .link-crumb-restricted {
540
+ cursor: default;
541
+ opacity: 0.6;
542
+ }
543
+ .link-crumb-restricted:hover {
544
+ color: var(--text-muted);
545
+ text-decoration: none;
546
+ }