@orion-studios/cms 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.
@@ -0,0 +1,444 @@
1
+ /* Orion Studio v2 — self-contained styles (ost- prefix) */
2
+
3
+ .ost-root {
4
+ --ost-bg: #f6f7f9;
5
+ --ost-panel: #ffffff;
6
+ --ost-ink: #1c2433;
7
+ --ost-muted: #6b7486;
8
+ --ost-line: #e3e6ec;
9
+ --ost-accent: #243654;
10
+ --ost-accent-contrast: #ffffff;
11
+ --ost-danger: #b3372f;
12
+ --ost-live: #1e7f4f;
13
+ display: flex;
14
+ min-height: 100dvh;
15
+ background: var(--ost-bg);
16
+ color: var(--ost-ink);
17
+ font: 14px/1.45 system-ui, -apple-system, 'Segoe UI', sans-serif;
18
+ }
19
+
20
+ .ost-muted { color: var(--ost-muted); font-size: 12.5px; }
21
+ .ost-error { color: var(--ost-danger); font-size: 13px; }
22
+ .ost-loading { display: grid; place-items: center; min-height: 40vh; color: var(--ost-muted); }
23
+ .ost-row { display: flex; gap: 8px; align-items: center; margin-top: 8px; }
24
+
25
+ /* Inputs */
26
+ .ost-label { display: grid; gap: 4px; font-size: 12.5px; font-weight: 600; margin-bottom: 10px; }
27
+ .ost-label.ost-checkbox { grid-auto-flow: column; justify-content: start; align-items: center; gap: 8px; }
28
+ .ost-input {
29
+ font: inherit; font-weight: 400; color: var(--ost-ink);
30
+ border: 1px solid var(--ost-line); border-radius: 8px;
31
+ padding: 8px 10px; background: #fff; width: 100%;
32
+ }
33
+ .ost-input:focus { outline: 2px solid color-mix(in srgb, var(--ost-accent) 30%, transparent); border-color: var(--ost-accent); }
34
+ .ost-textarea { min-height: 84px; resize: vertical; }
35
+ .ost-fieldset { border: 1px solid var(--ost-line); border-radius: 10px; padding: 10px 12px; margin: 0 0 12px; }
36
+ .ost-fieldset legend { font-size: 12px; font-weight: 700; padding: 0 4px; color: var(--ost-muted); text-transform: uppercase; letter-spacing: 0.04em; }
37
+
38
+ /* Buttons */
39
+ .ost-btn {
40
+ font: inherit; font-weight: 600; cursor: pointer;
41
+ border: 1px solid var(--ost-line); border-radius: 8px;
42
+ padding: 7px 12px; background: #fff; color: var(--ost-ink);
43
+ display: inline-flex; align-items: center; gap: 6px;
44
+ }
45
+ .ost-btn:hover { border-color: var(--ost-accent); }
46
+ .ost-btn:disabled { opacity: 0.5; cursor: default; }
47
+ .ost-btn-primary { background: var(--ost-accent); border-color: var(--ost-accent); color: var(--ost-accent-contrast); }
48
+ .ost-btn-danger { color: var(--ost-danger); border-color: color-mix(in srgb, var(--ost-danger) 40%, transparent); }
49
+ .ost-pill {
50
+ font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em;
51
+ padding: 2px 8px; border-radius: 999px; background: var(--ost-line); color: var(--ost-muted);
52
+ }
53
+ .ost-pill.is-live { background: color-mix(in srgb, var(--ost-live) 15%, transparent); color: var(--ost-live); }
54
+
55
+ /* Login */
56
+ .ost-login {
57
+ display: grid; place-items: center; min-height: 100dvh; padding: 24px;
58
+ background:
59
+ radial-gradient(1100px 560px at 12% -10%, color-mix(in srgb, var(--ost-accent) 16%, transparent), transparent 60%),
60
+ radial-gradient(900px 520px at 108% 112%, color-mix(in srgb, var(--ost-accent) 12%, transparent), transparent 55%),
61
+ var(--ost-bg);
62
+ }
63
+ .ost-login-card {
64
+ width: min(400px, 92vw); background: var(--ost-panel); border: 1px solid var(--ost-line);
65
+ border-radius: 16px; padding: 36px 32px 24px; display: grid; gap: 4px;
66
+ box-shadow: 0 1px 2px rgba(28, 36, 51, 0.06), 0 24px 60px -18px rgba(28, 36, 51, 0.25);
67
+ animation: ost-login-in 0.35s ease-out;
68
+ }
69
+ @keyframes ost-login-in {
70
+ from { opacity: 0; transform: translateY(8px); }
71
+ to { opacity: 1; transform: none; }
72
+ }
73
+ .ost-login-logo {
74
+ width: auto; max-width: min(280px, 80%); max-height: 96px; height: auto;
75
+ justify-self: center; margin-bottom: 16px; object-fit: contain;
76
+ }
77
+ .ost-login-heading { text-align: center; margin-bottom: 18px; display: grid; gap: 4px; }
78
+ .ost-login-heading h1 { margin: 0; font-size: 21px; letter-spacing: -0.01em; }
79
+ .ost-login .ost-label { margin-bottom: 14px; gap: 6px; }
80
+ .ost-login .ost-input { padding: 10px 12px; border-radius: 10px; }
81
+ .ost-login-error {
82
+ color: var(--ost-danger); font-size: 13px; font-weight: 500;
83
+ background: color-mix(in srgb, var(--ost-danger) 8%, transparent);
84
+ border: 1px solid color-mix(in srgb, var(--ost-danger) 25%, transparent);
85
+ border-radius: 10px; padding: 9px 12px; margin-bottom: 12px;
86
+ }
87
+ .ost-login-submit {
88
+ width: 100%; justify-content: center; padding: 11px 14px; border-radius: 10px;
89
+ font-size: 14.5px; transition: filter 0.15s ease, transform 0.05s ease;
90
+ }
91
+ .ost-login-submit:hover:not(:disabled) { filter: brightness(1.12); }
92
+ .ost-login-submit:active:not(:disabled) { transform: translateY(1px); }
93
+ .ost-login-submit:disabled { opacity: 0.7; cursor: default; }
94
+ .ost-login-foot {
95
+ margin: 18px 0 0; text-align: center; font-size: 11.5px; color: var(--ost-muted);
96
+ letter-spacing: 0.02em;
97
+ }
98
+
99
+ /* Users admin */
100
+ .ost-notice {
101
+ color: var(--ost-live); font-size: 13px; font-weight: 500;
102
+ background: color-mix(in srgb, var(--ost-live) 9%, transparent);
103
+ border: 1px solid color-mix(in srgb, var(--ost-live) 25%, transparent);
104
+ border-radius: 10px; padding: 9px 12px; margin-bottom: 12px;
105
+ }
106
+ .ost-user-row { cursor: default; display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
107
+ .ost-user-id { display: grid; gap: 2px; min-width: 0; }
108
+ .ost-user-id strong { overflow: hidden; text-overflow: ellipsis; }
109
+ .ost-user-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
110
+ .ost-role-select { width: auto; padding: 6px 8px; }
111
+ .ost-role-legend { margin-top: 16px; display: grid; gap: 4px; }
112
+ .ost-role-legend p { margin: 0; }
113
+
114
+ /* Password field with reveal toggle */
115
+ .ost-password { position: relative; }
116
+ .ost-password .ost-input { padding-right: 40px; }
117
+ .ost-password-toggle {
118
+ position: absolute; top: 50%; right: 6px; transform: translateY(-50%);
119
+ display: grid; place-items: center; width: 30px; height: 30px;
120
+ border: 0; border-radius: 7px; background: transparent; cursor: pointer;
121
+ color: var(--ost-muted); padding: 0;
122
+ }
123
+ .ost-password-toggle:hover { color: var(--ost-ink); background: color-mix(in srgb, var(--ost-ink) 7%, transparent); }
124
+
125
+ /* Shell */
126
+ .ost-nav {
127
+ width: 224px; flex-shrink: 0; background: var(--ost-accent); color: #fff;
128
+ display: flex; flex-direction: column; gap: 2px; padding: 16px 10px;
129
+ position: sticky; top: 0; height: 100dvh;
130
+ }
131
+ .ost-brand { display: flex; gap: 10px; align-items: center; padding: 6px 10px 18px; }
132
+ .ost-brand img { height: 30px; width: auto; }
133
+ .ost-brand strong { display: block; font-size: 14px; }
134
+ .ost-brand .ost-muted { color: rgba(255, 255, 255, 0.55); }
135
+ .ost-nav-item {
136
+ font: inherit; font-weight: 600; text-align: left; cursor: pointer;
137
+ background: none; border: none; color: rgba(255, 255, 255, 0.75);
138
+ padding: 9px 12px; border-radius: 8px;
139
+ }
140
+ .ost-nav-item:hover { background: rgba(255, 255, 255, 0.08); color: #fff; }
141
+ .ost-nav-item.is-active { background: rgba(255, 255, 255, 0.14); color: #fff; }
142
+ .ost-nav-footer { margin-top: auto; display: grid; gap: 6px; padding: 10px; }
143
+ .ost-nav-footer .ost-muted { color: rgba(255, 255, 255, 0.55); overflow: hidden; text-overflow: ellipsis; }
144
+ .ost-nav-footer .ost-btn { background: transparent; color: #fff; border-color: rgba(255, 255, 255, 0.25); justify-content: center; }
145
+ .ost-nav-footer .ost-pill { justify-self: start; background: rgba(255, 255, 255, 0.14); color: #fff; }
146
+
147
+ .ost-main { flex: 1; min-width: 0; }
148
+ .ost-view { padding: 26px 30px; max-width: 1060px; }
149
+ .ost-view-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 18px; gap: 12px; }
150
+ .ost-view-header h2 { margin: 0 0 2px; font-size: 20px; }
151
+ .ost-view-header p { margin: 0; }
152
+
153
+ .ost-card { background: var(--ost-panel); border: 1px solid var(--ost-line); border-radius: 12px; padding: 14px 16px; }
154
+ .ost-list { display: grid; gap: 8px; }
155
+ .ost-list-item {
156
+ font: inherit; text-align: left; cursor: pointer; display: flex; justify-content: space-between; align-items: center;
157
+ background: var(--ost-panel); border: 1px solid var(--ost-line); border-radius: 10px; padding: 12px 14px;
158
+ }
159
+ .ost-list-item:hover { border-color: var(--ost-accent); }
160
+ .ost-list-item.is-active { border-color: var(--ost-accent); box-shadow: inset 3px 0 0 var(--ost-accent); }
161
+ .ost-create-form { display: grid; gap: 4px; margin-bottom: 16px; max-width: 420px; }
162
+ .ost-dashboard-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; }
163
+ .ost-dashboard-card { font: inherit; cursor: pointer; text-align: left; padding: 22px 16px; }
164
+ .ost-dashboard-card:hover { border-color: var(--ost-accent); }
165
+
166
+ /* Editor */
167
+ .ost-editor { display: flex; flex-direction: column; height: 100dvh; }
168
+ .ost-editor-bar {
169
+ display: flex; gap: 10px; align-items: center; padding: 10px 14px;
170
+ background: var(--ost-panel); border-bottom: 1px solid var(--ost-line);
171
+ }
172
+ .ost-title-input { max-width: 380px; font-weight: 600; }
173
+ .ost-editor-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }
174
+ .ost-editor-body { display: flex; flex: 1; min-height: 0; }
175
+ .ost-canvas { flex: 1; overflow-y: auto; padding: 0 0 40px; background: #fff; }
176
+ .ost-canvas-empty {
177
+ display: grid; gap: 12px; justify-items: center; padding: 60px 20px; margin: 22px;
178
+ border: 2px dashed var(--ost-line); border-radius: 14px; color: var(--ost-muted);
179
+ }
180
+
181
+ /* The page renders full-bleed exactly like the live site; chrome floats on top. */
182
+ .ost-page { min-height: 40vh; }
183
+ .ost-blockwrap { position: relative; }
184
+ .ost-blockwrap::after {
185
+ content: ''; position: absolute; inset: 0; pointer-events: none;
186
+ outline: 2px solid transparent; outline-offset: -2px; transition: outline-color 120ms;
187
+ z-index: 5;
188
+ }
189
+ .ost-blockwrap:hover::after { outline-color: color-mix(in srgb, var(--ost-accent) 40%, transparent); }
190
+ .ost-blockwrap.is-selected::after { outline-color: var(--ost-accent); }
191
+
192
+ .ost-blockchrome {
193
+ position: absolute; top: 8px; left: 8px; right: 8px; z-index: 6;
194
+ display: flex; justify-content: space-between; align-items: flex-start;
195
+ opacity: 0; transition: opacity 120ms; pointer-events: none;
196
+ }
197
+ .ost-blockwrap:hover .ost-blockchrome,
198
+ .ost-blockwrap.is-selected .ost-blockchrome { opacity: 1; pointer-events: auto; }
199
+ .ost-blockchip {
200
+ font: inherit; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em;
201
+ cursor: pointer; border: none; border-radius: 6px; padding: 4px 10px;
202
+ background: var(--ost-accent); color: var(--ost-accent-contrast);
203
+ box-shadow: 0 2px 10px rgba(20, 26, 38, 0.25);
204
+ }
205
+ .ost-blockactions {
206
+ display: flex; gap: 4px; background: #fff; border-radius: 8px; padding: 3px;
207
+ box-shadow: 0 2px 10px rgba(20, 26, 38, 0.18); border: 1px solid var(--ost-line);
208
+ }
209
+ .ost-blockactions button {
210
+ font: inherit; cursor: pointer; background: none; border: none;
211
+ border-radius: 6px; padding: 3px 9px; color: var(--ost-muted);
212
+ }
213
+ .ost-blockactions button:hover:not(:disabled) { color: var(--ost-ink); background: var(--ost-bg); }
214
+ .ost-blockactions button:disabled { opacity: 0.35; cursor: default; }
215
+ .ost-block-fallback { padding: 30px; color: var(--ost-muted); text-align: center; }
216
+
217
+ /* Inline editable text */
218
+ [data-orion-inline] { cursor: text; transition: box-shadow 100ms; border-radius: 3px; }
219
+ [data-orion-inline]:hover {
220
+ box-shadow:
221
+ 0 0 0 1px rgba(255, 255, 255, 0.95),
222
+ 0 0 0 3px color-mix(in srgb, var(--ost-accent) 55%, transparent);
223
+ }
224
+ [data-orion-inline]:focus {
225
+ outline: none;
226
+ box-shadow:
227
+ 0 0 0 1px rgba(255, 255, 255, 0.98),
228
+ 0 0 0 3px var(--ost-accent);
229
+ background: color-mix(in srgb, var(--ost-accent) 8%, transparent);
230
+ }
231
+
232
+ .ost-add-section {
233
+ font: inherit; font-weight: 600; cursor: pointer; color: var(--ost-muted);
234
+ border: 2px dashed var(--ost-line); border-radius: 12px; padding: 16px; background: none;
235
+ display: block; width: calc(100% - 44px); margin: 18px 22px 0;
236
+ }
237
+ .ost-add-section:hover { color: var(--ost-accent); border-color: var(--ost-accent); }
238
+
239
+ .ost-sidebar-panel {
240
+ width: 320px; flex-shrink: 0; overflow-y: auto; padding: 18px;
241
+ background: var(--ost-panel); border-left: 1px solid var(--ost-line);
242
+ }
243
+ .ost-sidebar-panel h3 { margin: 0 0 14px; font-size: 15px; }
244
+
245
+ /* Versions */
246
+ .ost-versions { padding: 10px 14px; background: var(--ost-bg); border-bottom: 1px solid var(--ost-line); display: grid; gap: 6px; }
247
+ .ost-version-row { display: flex; gap: 10px; align-items: center; font-size: 13px; }
248
+ .ost-version-row .ost-btn { margin-left: auto; padding: 3px 10px; }
249
+
250
+ /* Palette */
251
+ .ost-palette { position: fixed; inset: 0; background: rgba(20, 26, 38, 0.45); display: grid; place-items: center; z-index: 60; }
252
+ .ost-palette-card {
253
+ width: min(640px, 94vw); max-height: 80vh; overflow-y: auto;
254
+ background: #fff; border-radius: 14px; padding: 22px;
255
+ }
256
+ .ost-palette-card h3 { margin: 0 0 14px; }
257
+ .ost-palette-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 10px; }
258
+ .ost-palette-item {
259
+ font: inherit; text-align: left; cursor: pointer; display: grid; gap: 4px;
260
+ border: 1px solid var(--ost-line); border-radius: 10px; padding: 12px; background: #fff;
261
+ }
262
+ .ost-palette-item:hover { border-color: var(--ost-accent); }
263
+ .ost-palette-item span { color: var(--ost-muted); font-size: 12px; }
264
+
265
+ /* Inspector extras */
266
+ .ost-item { border: 1px solid var(--ost-line); border-radius: 8px; margin-bottom: 8px; }
267
+ .ost-item summary { display: flex; justify-content: space-between; align-items: center; cursor: pointer; padding: 8px 10px; font-weight: 600; }
268
+ .ost-item-body { padding: 10px; border-top: 1px solid var(--ost-line); }
269
+ .ost-item-actions button {
270
+ font: inherit; cursor: pointer; background: none; border: 1px solid var(--ost-line);
271
+ border-radius: 6px; padding: 1px 7px; margin-left: 4px; color: var(--ost-muted);
272
+ }
273
+
274
+ /* Media */
275
+ .ost-media-field { display: grid; gap: 8px; }
276
+ .ost-media-thumb { width: 100%; border-radius: 8px; border: 1px solid var(--ost-line); display: block; }
277
+ .ost-media-empty { padding: 24px; text-align: center; color: var(--ost-muted); border: 1px dashed var(--ost-line); border-radius: 8px; }
278
+ .ost-media-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 6px; }
279
+ .ost-media-grid-lg { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
280
+ .ost-media-cell { position: relative; cursor: pointer; border: 2px solid transparent; border-radius: 8px; overflow: hidden; padding: 0; background: var(--ost-line); }
281
+ .ost-media-cell img { width: 100%; aspect-ratio: 1; object-fit: cover; display: block; }
282
+ .ost-media-cell.is-selected { border-color: var(--ost-accent); }
283
+ .ost-media-name { position: absolute; inset: auto 0 0; font-size: 10px; padding: 3px 6px; background: rgba(20, 26, 38, 0.65); color: #fff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
284
+ .ost-media-upload { display: grid; place-items: center; cursor: pointer; aspect-ratio: 1; border: 2px dashed var(--ost-line); border-radius: 8px; color: var(--ost-muted); font-weight: 600; }
285
+ .ost-media-upload:hover { color: var(--ost-accent); border-color: var(--ost-accent); }
286
+ .ost-media-detail { margin-top: 14px; display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
287
+
288
+ /* Globals */
289
+ .ost-globals { display: grid; grid-template-columns: 220px 1fr; gap: 18px; align-items: start; }
290
+ .ost-globals-nav { display: grid; gap: 6px; }
291
+ .ost-globals-editor { background: var(--ost-panel); border: 1px solid var(--ost-line); border-radius: 12px; padding: 18px; }
292
+
293
+ /* Submissions */
294
+ .ost-submission summary { display: flex; justify-content: space-between; cursor: pointer; }
295
+ .ost-submission-data { display: grid; gap: 6px; margin: 12px 0 0; }
296
+ .ost-submission-data div { display: grid; grid-template-columns: 140px 1fr; gap: 10px; }
297
+ .ost-submission-data dt { font-weight: 600; color: var(--ost-muted); }
298
+ .ost-submission-data dd { margin: 0; }
299
+
300
+ /* ---- v0.3 additions: forms manager, dashboard, settings, redirects ---- */
301
+
302
+ .ost-nav-badge {
303
+ margin-left: auto; background: var(--ost-danger); color: #fff; border-radius: 999px;
304
+ font-size: 11px; font-weight: 700; padding: 1px 7px; min-width: 18px; text-align: center;
305
+ }
306
+ .ost-nav-item { display: flex; align-items: center; gap: 8px; }
307
+
308
+ .ost-warn { color: var(--ost-danger); }
309
+ .ost-grow { flex: 1; }
310
+ .ost-inline-check { margin: 0; white-space: nowrap; }
311
+ .ost-load-more { justify-content: center; margin-top: 14px; }
312
+
313
+ /* Submissions */
314
+ .ost-submission.is-unread { border-left: 3px solid var(--ost-accent); }
315
+ .ost-unread-dot {
316
+ display: inline-block; width: 8px; height: 8px; border-radius: 50%;
317
+ background: var(--ost-accent); margin-right: 7px; vertical-align: middle;
318
+ }
319
+ .ost-submission .ost-row { margin-top: 10px; }
320
+
321
+ /* Forms manager */
322
+ .ost-view-wide { max-width: 1280px; }
323
+ .ost-form-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; cursor: default; }
324
+ .ost-form-open { font: inherit; background: none; border: 0; cursor: pointer; text-align: left; padding: 0; color: inherit; }
325
+ .ost-form-open:hover strong { text-decoration: underline; }
326
+ .ost-form-editor { display: grid; grid-template-columns: minmax(0, 1fr) minmax(320px, 420px); gap: 22px; align-items: start; }
327
+ .ost-form-config { min-width: 0; }
328
+ .ost-form-preview { position: sticky; top: 20px; }
329
+ .ost-form-preview h4 { margin: 0 0 8px; }
330
+ .ost-form-preview-frame { background: var(--ost-panel); border: 1px solid var(--ost-line); border-radius: 12px; padding: 16px; }
331
+ .ost-preview-form { display: grid; gap: 2px; }
332
+ .ost-preview-label { font-size: 12.5px; font-weight: 600; display: block; margin-bottom: 4px; }
333
+ .ost-preview-form .ocf-actions { margin-top: 8px; display: flex; gap: 8px; }
334
+ .ost-preview-form .ocf-help { margin: 2px 0 0; font-size: 12px; color: var(--ost-muted); }
335
+
336
+ /* Dashboard */
337
+ .ost-dash-columns { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 14px; align-items: start; }
338
+ .ost-dash-column { display: grid; gap: 14px; }
339
+ .ost-dash-card { display: grid; gap: 6px; }
340
+ .ost-dash-card-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
341
+ .ost-dash-card-head h3 { margin: 0; font-size: 14.5px; }
342
+ .ost-dash-stat { margin: 0 0 4px; }
343
+ .ost-dash-unread { color: var(--ost-accent); }
344
+ .ost-dash-row {
345
+ font: inherit; display: flex; justify-content: space-between; gap: 10px; align-items: baseline;
346
+ background: none; border: 0; border-top: 1px solid var(--ost-line); padding: 8px 2px; cursor: pointer;
347
+ text-align: left; color: inherit;
348
+ }
349
+ .ost-dash-row:hover { color: var(--ost-accent); }
350
+ .ost-dash-row-static { cursor: default; }
351
+ .ost-dash-row-static:hover { color: inherit; }
352
+
353
+ /* Page editor additions */
354
+ .ost-insert-zone {
355
+ display: block; width: 100%; height: 14px; border: 0; background: transparent;
356
+ cursor: pointer; position: relative; padding: 0;
357
+ }
358
+ .ost-insert-zone span {
359
+ position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(0.8);
360
+ width: 22px; height: 22px; line-height: 20px; border-radius: 50%;
361
+ background: var(--ost-accent); color: var(--ost-accent-contrast); font-weight: 700;
362
+ opacity: 0; transition: opacity 120ms ease, transform 120ms ease;
363
+ }
364
+ .ost-insert-zone:hover span { opacity: 1; transform: translate(-50%, -50%) scale(1); }
365
+ .ost-insert-zone:hover { background: color-mix(in srgb, var(--ost-accent) 8%, transparent); }
366
+ .ost-drag-dots { opacity: 0.6; margin-right: 5px; cursor: grab; letter-spacing: -2px; }
367
+ .ost-blockwrap.is-dragging { opacity: 0.45; }
368
+
369
+ /* Settings drawer + version preview */
370
+ .ost-settings-card {
371
+ background: var(--ost-panel); border-radius: 14px; width: min(560px, 92vw);
372
+ max-height: 86vh; overflow-y: auto; padding: 20px 22px;
373
+ }
374
+ .ost-settings-section { border-top: 1px solid var(--ost-line); padding-top: 14px; margin-top: 14px; }
375
+ .ost-settings-section h4 { margin: 0 0 10px; font-size: 13px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ost-muted); }
376
+ .ost-version-preview {
377
+ background: #fff; border-radius: 14px; width: min(1100px, 94vw); max-height: 88vh;
378
+ display: flex; flex-direction: column; overflow: hidden;
379
+ }
380
+ .ost-version-preview-bar {
381
+ display: flex; gap: 12px; align-items: center; justify-content: space-between;
382
+ padding: 12px 16px; border-bottom: 1px solid var(--ost-line); background: var(--ost-panel);
383
+ }
384
+ .ost-version-preview-body { overflow-y: auto; }
385
+ .ost-global-versions { margin-top: 12px; }
386
+
387
+ /* Media */
388
+ .ost-media-doc { font-size: 42px; display: grid; place-items: center; aspect-ratio: 4 / 3; }
389
+ .ost-media-detail { display: grid; gap: 8px; margin-top: 16px; }
390
+ .ost-media-detail-head { display: grid; gap: 3px; }
391
+ .ost-media-preview { max-width: 320px; border-radius: 10px; border: 1px solid var(--ost-line); }
392
+ .ost-file-current { font-size: 13px; padding: 8px 0; }
393
+
394
+ /* Redirects */
395
+ .ost-redirect-form { display: flex; gap: 10px; align-items: end; margin-bottom: 16px; }
396
+ .ost-redirect-form .ost-label { margin-bottom: 0; }
397
+ .ost-redirect-arrow { padding-bottom: 9px; color: var(--ost-muted); }
398
+ .ost-redirect-row { display: flex; justify-content: space-between; align-items: center; gap: 10px; cursor: default; }
399
+ .ost-redirect-row code { background: color-mix(in srgb, var(--ost-ink) 6%, transparent); padding: 2px 6px; border-radius: 6px; font-size: 12.5px; }
400
+
401
+ @media (max-width: 1100px) {
402
+ .ost-form-editor { grid-template-columns: 1fr; }
403
+ .ost-form-preview { position: static; }
404
+ }
405
+
406
+ /* ---- Analytics tab ---- */
407
+ .ost-kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 14px; }
408
+ .ost-kpi { display: grid; gap: 2px; padding: 14px 16px; }
409
+ .ost-kpi.is-highlight { border-color: var(--ost-accent); box-shadow: inset 0 3px 0 var(--ost-accent); }
410
+ .ost-kpi-label { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ost-muted); }
411
+ .ost-kpi-value { font-size: 26px; letter-spacing: -0.02em; }
412
+ .ost-kpi-delta { font-size: 12px; color: var(--ost-muted); }
413
+ .ost-kpi-delta.is-up { color: var(--ost-live); }
414
+ .ost-kpi-delta.is-down { color: var(--ost-danger); }
415
+
416
+ .ost-analytics-section { margin-bottom: 14px; }
417
+ .ost-analytics-note { margin: -4px 0 14px; font-size: 12px; }
418
+ .ost-analytics-section h3 { margin: 0 0 12px; font-size: 14.5px; display: flex; justify-content: space-between; align-items: baseline; }
419
+ .ost-analytics-subhead { margin: 14px 0 8px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ost-muted); }
420
+ .ost-analytics-columns { display: grid; grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); gap: 14px; align-items: start; }
421
+
422
+ .ost-trend-wrap { display: grid; gap: 4px; }
423
+ .ost-trend { width: 100%; height: 130px; display: block; }
424
+ .ost-trend-bar { fill: color-mix(in srgb, var(--ost-accent) 30%, transparent); }
425
+ .ost-trend-conversion { fill: var(--ost-accent); }
426
+ .ost-trend-dates { display: flex; justify-content: space-between; font-size: 11.5px; color: var(--ost-muted); }
427
+ .ost-trend-key { font-size: 11px; font-weight: 500; color: color-mix(in srgb, var(--ost-accent) 45%, transparent); }
428
+ .ost-trend-key em { font-style: normal; color: var(--ost-accent); }
429
+
430
+ .ost-barlist { display: grid; gap: 7px; }
431
+ .ost-barlist-row { display: grid; grid-template-columns: minmax(90px, 1.3fr) 1fr auto; gap: 10px; align-items: center; font-size: 13px; }
432
+ .ost-barlist-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
433
+ .ost-barlist-track { background: color-mix(in srgb, var(--ost-ink) 6%, transparent); border-radius: 99px; height: 8px; overflow: hidden; }
434
+ .ost-barlist-fill { display: block; height: 100%; background: var(--ost-accent); border-radius: 99px; }
435
+ .ost-barlist-value { font-variant-numeric: tabular-nums; white-space: nowrap; }
436
+
437
+ .ost-funnel { margin-bottom: 12px; display: grid; gap: 6px; }
438
+ .ost-journey { display: flex; justify-content: space-between; gap: 12px; padding: 7px 0; border-top: 1px solid var(--ost-line); font-size: 13px; }
439
+ .ost-journey-path { min-width: 0; }
440
+ .ost-journey-arrow { color: var(--ost-muted); }
441
+ .ost-hours { display: flex; align-items: flex-end; gap: 2px; height: 46px; }
442
+ .ost-hour-bar { flex: 1; background: color-mix(in srgb, var(--ost-accent) 55%, transparent); border-radius: 2px 2px 0 0; min-height: 2px; }
443
+ .ost-housekeeping { flex-wrap: wrap; }
444
+ .ost-dash-week { margin: 4px 0 0; font-size: 13px; }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Framework-agnostic submission processing: origin check, honeypot, minimum
3
+ * fill time, rate limiting, and server-side re-validation with the shared
4
+ * rules. The route layer handles HTTP; this module handles the contract.
5
+ */
6
+ declare const HONEYPOT_FIELD_NAME = "website_url_confirm";
7
+ type FormFieldConfig = {
8
+ name?: string;
9
+ label?: string;
10
+ type?: string;
11
+ required?: boolean;
12
+ placeholder?: string;
13
+ help?: string;
14
+ /** For select/radio/checkbox groups. Strings or {label, value} pairs. */
15
+ options?: Array<string | {
16
+ label?: string;
17
+ value?: string;
18
+ }>;
19
+ };
20
+ type FormNotifyConfig = {
21
+ /** Addresses that receive a copy of each submission. */
22
+ emails?: string[];
23
+ /** Subject template; `{form}` is replaced with the form title. */
24
+ subject?: string;
25
+ /** Send the success message back to the submitter's email field. */
26
+ autoReply?: boolean;
27
+ };
28
+ type FormConfig = {
29
+ steps?: Array<{
30
+ title?: string;
31
+ fields?: FormFieldConfig[];
32
+ }>;
33
+ notify?: FormNotifyConfig;
34
+ };
35
+ type RateLimitStore = {
36
+ isLimited(key: string, now: number): boolean | Promise<boolean>;
37
+ };
38
+ declare function createMemoryRateLimitStore(options?: {
39
+ max?: number;
40
+ windowMs?: number;
41
+ }): RateLimitStore;
42
+ type ProcessSubmissionArgs = {
43
+ config: FormConfig;
44
+ data: Record<string, unknown>;
45
+ now?: number;
46
+ minFillTimeMs?: number;
47
+ honeypotFieldName?: string;
48
+ knownGoodEmailDomains?: string[];
49
+ };
50
+ type ProcessSubmissionResult = {
51
+ outcome: 'spam';
52
+ } | {
53
+ outcome: 'invalid';
54
+ fieldErrors: Record<string, string>;
55
+ } | {
56
+ outcome: 'ok';
57
+ normalizedData: Record<string, unknown>;
58
+ };
59
+ declare function processSubmission(args: ProcessSubmissionArgs): ProcessSubmissionResult;
60
+
61
+ export { type FormConfig as F, HONEYPOT_FIELD_NAME as H, type ProcessSubmissionArgs as P, type RateLimitStore as R, type FormFieldConfig as a, type FormNotifyConfig as b, type ProcessSubmissionResult as c, createMemoryRateLimitStore as d, processSubmission as p };
@@ -0,0 +1,30 @@
1
+ type FormFieldConfig = {
2
+ name?: string;
3
+ label?: string;
4
+ type?: string;
5
+ required?: boolean;
6
+ placeholder?: string;
7
+ help?: string;
8
+ /** For select/radio/checkbox groups. Strings or {label, value} pairs. */
9
+ options?: Array<string | {
10
+ label?: string;
11
+ value?: string;
12
+ }>;
13
+ };
14
+ type FormNotifyConfig = {
15
+ /** Addresses that receive a copy of each submission. */
16
+ emails?: string[];
17
+ /** Subject template; `{form}` is replaced with the form title. */
18
+ subject?: string;
19
+ /** Send the success message back to the submitter's email field. */
20
+ autoReply?: boolean;
21
+ };
22
+ type FormConfig = {
23
+ steps?: Array<{
24
+ title?: string;
25
+ fields?: FormFieldConfig[];
26
+ }>;
27
+ notify?: FormNotifyConfig;
28
+ };
29
+
30
+ export type { FormConfig as F, FormNotifyConfig as a };
package/package.json ADDED
@@ -0,0 +1,97 @@
1
+ {
2
+ "name": "@orion-studios/cms",
3
+ "version": "0.5.0",
4
+ "description": "Orion CMS v2 core engine \u2014 JSONB content model on Supabase primitives",
5
+ "type": "module",
6
+ "exports": {
7
+ "./blocks": {
8
+ "types": "./dist/blocks/index.d.ts",
9
+ "import": "./dist/blocks/index.js",
10
+ "default": "./dist/blocks/index.js"
11
+ },
12
+ "./content": {
13
+ "types": "./dist/content/index.d.ts",
14
+ "import": "./dist/content/index.js",
15
+ "default": "./dist/content/index.js"
16
+ },
17
+ "./server": {
18
+ "types": "./dist/server/index.d.ts",
19
+ "import": "./dist/server/index.js",
20
+ "default": "./dist/server/index.js"
21
+ },
22
+ "./forms": {
23
+ "types": "./dist/forms/index.d.ts",
24
+ "import": "./dist/forms/index.js",
25
+ "default": "./dist/forms/index.js"
26
+ },
27
+ "./forms/react": {
28
+ "types": "./dist/forms/react.d.ts",
29
+ "import": "./dist/forms/react.js",
30
+ "default": "./dist/forms/react.js"
31
+ },
32
+ "./studio": {
33
+ "types": "./dist/studio/index.d.ts",
34
+ "import": "./dist/studio/index.js",
35
+ "default": "./dist/studio/index.js"
36
+ },
37
+ "./studio/styles.css": "./dist/studio/styles.css",
38
+ "./sql/bootstrap.sql": "./sql/bootstrap.sql",
39
+ "./package.json": "./package.json",
40
+ "./analytics/react": {
41
+ "types": "./dist/analytics/react.d.ts",
42
+ "import": "./dist/analytics/react.js",
43
+ "default": "./dist/analytics/react.js"
44
+ }
45
+ },
46
+ "files": [
47
+ "dist",
48
+ "sql",
49
+ "README.md"
50
+ ],
51
+ "scripts": {
52
+ "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsup && node -e \"require('fs').copyFileSync('src/studio/styles.css','dist/studio/styles.css')\"",
53
+ "typecheck": "tsc --noEmit",
54
+ "test": "tsx --test src/blocks/__tests__/*.test.ts src/server/__tests__/*.test.ts src/forms/__tests__/*.test.ts src/studio/__tests__/*.test.ts src/analytics/__tests__/*.test.ts"
55
+ },
56
+ "dependencies": {
57
+ "@supabase/ssr": "^0.7.0",
58
+ "@supabase/supabase-js": "^2.58.0",
59
+ "zod": "^3.25.0"
60
+ },
61
+ "peerDependencies": {
62
+ "next": "^15.0.0 || ^16.0.0",
63
+ "react": "^18.0.0 || ^19.0.0"
64
+ },
65
+ "peerDependenciesMeta": {
66
+ "next": {
67
+ "optional": true
68
+ },
69
+ "react": {
70
+ "optional": true
71
+ }
72
+ },
73
+ "devDependencies": {
74
+ "@types/node": "^22.0.0",
75
+ "@types/react": "^19.0.0",
76
+ "tsup": "^8.0.0",
77
+ "tsx": "^4.21.0",
78
+ "typescript": "^5.6.3"
79
+ },
80
+ "author": "Orion Studios",
81
+ "license": "MIT",
82
+ "repository": {
83
+ "type": "git",
84
+ "url": "git+https://github.com/OrionX6/orion-cms-packages.git",
85
+ "directory": "cms-core"
86
+ },
87
+ "keywords": [
88
+ "cms",
89
+ "nextjs",
90
+ "supabase",
91
+ "blocks",
92
+ "orion-studios"
93
+ ],
94
+ "publishConfig": {
95
+ "access": "public"
96
+ }
97
+ }