@cs4alhaider/screenbook 0.1.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 (91) hide show
  1. package/CLAUDE.md +185 -0
  2. package/LICENSE +21 -0
  3. package/README.md +208 -0
  4. package/TESTING.md +54 -0
  5. package/app-bridge.js +131 -0
  6. package/bridge.js +598 -0
  7. package/cli/screenbook.js +290 -0
  8. package/cli/templates.js +139 -0
  9. package/core/browser.js +108 -0
  10. package/core/fsx.js +71 -0
  11. package/core/meta.js +94 -0
  12. package/core/project.js +482 -0
  13. package/core/serve.js +224 -0
  14. package/docs/README.md +21 -0
  15. package/docs/app-mode-guide.md +79 -0
  16. package/docs/authoring-guide.md +129 -0
  17. package/docs/cli.md +78 -0
  18. package/docs/getting-started.md +88 -0
  19. package/docs/mcp-agents.md +60 -0
  20. package/docs/shots/android-dark.png +0 -0
  21. package/docs/shots/app-mode.png +0 -0
  22. package/docs/shots/hero-studio.png +0 -0
  23. package/docs/shots/rtl-arabic.png +0 -0
  24. package/docs/shots/theme-lab.png +0 -0
  25. package/docs/shots/theming-noir.png +0 -0
  26. package/docs/shots/web-dashboard.png +0 -0
  27. package/docs/studio-guide.md +64 -0
  28. package/docs/troubleshooting.md +55 -0
  29. package/examples/qahwa/app.config.json +9 -0
  30. package/examples/qahwa/features/order/data/scenarios.json +25 -0
  31. package/examples/qahwa/features/order/data/seed.json +21 -0
  32. package/examples/qahwa/features/order/feature.json +7 -0
  33. package/examples/qahwa/features/order/flows.json +16 -0
  34. package/examples/qahwa/features/order/manifest.json +78 -0
  35. package/examples/qahwa/features/order/screens/010-menu.html +121 -0
  36. package/examples/qahwa/features/order/screens/020-drink.html +120 -0
  37. package/examples/qahwa/features/order/screens/030-cart.html +99 -0
  38. package/examples/qahwa/features/order/screens/040-status.html +125 -0
  39. package/examples/qahwa/features/order/screens/050-status-android.html +95 -0
  40. package/examples/qahwa/features/order/screens/shared.css +146 -0
  41. package/examples/qahwa/features/roastery/data/scenarios.json +35 -0
  42. package/examples/qahwa/features/roastery/data/seed.json +17 -0
  43. package/examples/qahwa/features/roastery/feature.json +7 -0
  44. package/examples/qahwa/features/roastery/flows.json +14 -0
  45. package/examples/qahwa/features/roastery/manifest.json +32 -0
  46. package/examples/qahwa/features/roastery/screens/010-orders.html +103 -0
  47. package/examples/qahwa/features/roastery/screens/020-menu-editor.html +88 -0
  48. package/examples/qahwa/features/roastery/screens/shared.css +115 -0
  49. package/examples/qahwa/review/comments.json +3 -0
  50. package/examples/qahwa/themes/qahwa.json +43 -0
  51. package/examples/spa-demo/app/index.html +100 -0
  52. package/examples/spa-demo/app.config.json +9 -0
  53. package/examples/spa-demo/features/notes-app/feature.json +11 -0
  54. package/examples/spa-demo/review/comments.json +3 -0
  55. package/examples/spa-demo/themes/default.json +15 -0
  56. package/examples/tokens-lab/app.config.json +9 -0
  57. package/examples/tokens-lab/features/lab/data/scenarios.json +5 -0
  58. package/examples/tokens-lab/features/lab/data/seed.json +3 -0
  59. package/examples/tokens-lab/features/lab/feature.json +7 -0
  60. package/examples/tokens-lab/features/lab/flows.json +14 -0
  61. package/examples/tokens-lab/features/lab/manifest.json +33 -0
  62. package/examples/tokens-lab/features/lab/screens/010-swatches.html +112 -0
  63. package/examples/tokens-lab/features/lab/screens/020-typography.html +115 -0
  64. package/examples/tokens-lab/review/comments.json +3 -0
  65. package/examples/tokens-lab/themes/noir.json +32 -0
  66. package/examples/tokens-lab/themes/paper.json +32 -0
  67. package/index.html +50 -0
  68. package/mcp/server.js +366 -0
  69. package/package.json +57 -0
  70. package/shell/flowplayer.js +85 -0
  71. package/shell/frames.js +108 -0
  72. package/shell/main.js +397 -0
  73. package/shell/net.js +126 -0
  74. package/shell/review.js +213 -0
  75. package/shell/screenhost.js +380 -0
  76. package/shell/selftest.js +275 -0
  77. package/shell/shell.css +952 -0
  78. package/shell/sidebar.js +112 -0
  79. package/shell/store.js +119 -0
  80. package/shell/themelab.js +186 -0
  81. package/shell/toast.js +15 -0
  82. package/shell/toolbar.js +128 -0
  83. package/skill/SKILL.md +52 -0
  84. package/skill/references/app-mode.md +77 -0
  85. package/skill/references/bridge-api.md +55 -0
  86. package/skill/references/cli-mcp.md +53 -0
  87. package/skill/references/flows.md +44 -0
  88. package/skill/references/i18n.md +52 -0
  89. package/skill/references/scenarios.md +45 -0
  90. package/skill/references/screen-contract.md +83 -0
  91. package/skill/references/tokens.md +56 -0
@@ -0,0 +1,952 @@
1
+ /* ScreenBook shell — studio chrome. Project themes never touch this;
2
+ the shell has its own neutral skin driven by [data-shell="light|dark"]. */
3
+
4
+ :root {
5
+ --sb-bg: #F1EFEB;
6
+ --sb-panel: #FAF9F7;
7
+ --sb-panel-2: #FFFFFF;
8
+ --sb-ink: #211E1A;
9
+ --sb-ink-2: #6F6A61;
10
+ --sb-ink-3: #9B958A;
11
+ --sb-line: #E3DFD7;
12
+ --sb-line-2: #D6D1C6;
13
+ --sb-accent: #5B5BD6;
14
+ --sb-accent-soft: #ECECFB;
15
+ --sb-accent-ink: #FFFFFF;
16
+ --sb-danger: #D6455B;
17
+ --sb-ok: #22A06B;
18
+ --sb-warn: #B47D12;
19
+ --sb-hover: rgba(30, 25, 18, 0.05);
20
+ --sb-active: rgba(30, 25, 18, 0.08);
21
+ --sb-shadow: 0 18px 60px rgba(30, 24, 14, 0.16);
22
+ --sb-shadow-soft: 0 4px 18px rgba(30, 24, 14, 0.08);
23
+ --sb-dots: rgba(30, 25, 18, 0.055);
24
+ --sb-radius: 12px;
25
+ --sb-font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
26
+ --sb-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
27
+ --sb-topbar-h: 52px;
28
+ }
29
+
30
+ html[data-shell="dark"] {
31
+ --sb-bg: #161519;
32
+ --sb-panel: #1D1C21;
33
+ --sb-panel-2: #26252B;
34
+ --sb-ink: #EDEBF3;
35
+ --sb-ink-2: #A7A3B0;
36
+ --sb-ink-3: #726E7C;
37
+ --sb-line: #2E2D34;
38
+ --sb-line-2: #3A3941;
39
+ --sb-accent: #8484F0;
40
+ --sb-accent-soft: #2A2A45;
41
+ --sb-accent-ink: #FFFFFF;
42
+ --sb-hover: rgba(237, 235, 243, 0.06);
43
+ --sb-active: rgba(237, 235, 243, 0.1);
44
+ --sb-shadow: 0 18px 60px rgba(0, 0, 0, 0.5);
45
+ --sb-shadow-soft: 0 4px 18px rgba(0, 0, 0, 0.35);
46
+ --sb-dots: rgba(237, 235, 243, 0.05);
47
+ }
48
+
49
+ * { box-sizing: border-box; }
50
+ [hidden] { display: none !important; }
51
+ html, body { height: 100%; }
52
+ body {
53
+ margin: 0;
54
+ font-family: var(--sb-font);
55
+ font-size: 13px;
56
+ color: var(--sb-ink);
57
+ background: var(--sb-bg);
58
+ overflow: hidden;
59
+ display: flex;
60
+ flex-direction: column;
61
+ -webkit-font-smoothing: antialiased;
62
+ }
63
+ button { font-family: inherit; color: inherit; }
64
+ input { font-family: inherit; }
65
+
66
+ body.solo .topbar,
67
+ body.solo .sidebar,
68
+ body.solo .review-panel,
69
+ body.solo .flowbar { display: none !important; }
70
+ body.solo .canvas { padding: 0; }
71
+
72
+ /* ---------------------------------------------------------------- topbar */
73
+
74
+ .topbar {
75
+ height: var(--sb-topbar-h);
76
+ flex: 0 0 var(--sb-topbar-h);
77
+ display: flex;
78
+ align-items: center;
79
+ justify-content: space-between;
80
+ gap: 16px;
81
+ padding: 0 14px;
82
+ background: var(--sb-panel);
83
+ border-bottom: 1px solid var(--sb-line);
84
+ z-index: 40;
85
+ }
86
+
87
+ .brand { display: flex; align-items: center; gap: 8px; min-width: 0; }
88
+ .brand-mark {
89
+ width: 26px; height: 26px;
90
+ display: grid; place-items: center;
91
+ background: var(--sb-accent);
92
+ color: var(--sb-accent-ink);
93
+ border-radius: 8px;
94
+ font-size: 14px;
95
+ flex: 0 0 auto;
96
+ }
97
+ .brand-name { font-weight: 700; letter-spacing: -0.1px; }
98
+ .brand-sep { color: var(--sb-ink-3); }
99
+ .brand-project {
100
+ color: var(--sb-ink-2);
101
+ font-weight: 600;
102
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
103
+ }
104
+
105
+ .topbar-controls { display: flex; align-items: center; gap: 6px; flex-wrap: nowrap; }
106
+ .tb-divider { width: 1px; height: 22px; background: var(--sb-line); margin: 0 4px; flex: 0 0 auto; }
107
+
108
+ .chip {
109
+ display: inline-flex;
110
+ align-items: center;
111
+ gap: 6px;
112
+ height: 30px;
113
+ padding: 0 10px;
114
+ border: 1px solid var(--sb-line);
115
+ border-radius: 9px;
116
+ background: var(--sb-panel-2);
117
+ cursor: pointer;
118
+ font-size: 12.5px;
119
+ font-weight: 600;
120
+ color: var(--sb-ink);
121
+ white-space: nowrap;
122
+ user-select: none;
123
+ transition: background .12s ease, border-color .12s ease;
124
+ }
125
+ .chip:hover { background: var(--sb-hover); }
126
+ .chip .chip-icon { font-size: 13px; line-height: 1; }
127
+ .chip .chip-caret { color: var(--sb-ink-3); font-size: 9px; margin-left: 1px; }
128
+ .chip .chip-value { color: var(--sb-ink-2); font-weight: 600; max-width: 130px; overflow: hidden; text-overflow: ellipsis; }
129
+ .chip.icon-only { padding: 0 8px; }
130
+ .chip.is-on {
131
+ background: var(--sb-accent-soft);
132
+ border-color: var(--sb-accent);
133
+ color: var(--sb-accent);
134
+ }
135
+ .chip.is-danger.is-on { background: rgba(214, 69, 91, .1); border-color: var(--sb-danger); color: var(--sb-danger); }
136
+
137
+ /* dropdown menus */
138
+ .dropdown { position: relative; }
139
+ .menu {
140
+ position: absolute;
141
+ top: calc(100% + 6px);
142
+ inset-inline-end: 0;
143
+ min-width: 230px;
144
+ max-width: 330px;
145
+ max-height: min(60vh, 480px);
146
+ overflow: auto;
147
+ background: var(--sb-panel-2);
148
+ border: 1px solid var(--sb-line);
149
+ border-radius: 12px;
150
+ box-shadow: var(--sb-shadow);
151
+ padding: 5px;
152
+ z-index: 80;
153
+ }
154
+ .menu-head {
155
+ padding: 7px 10px 5px;
156
+ font-size: 10.5px;
157
+ font-weight: 700;
158
+ text-transform: uppercase;
159
+ letter-spacing: .8px;
160
+ color: var(--sb-ink-3);
161
+ }
162
+ .menu-item {
163
+ display: flex;
164
+ align-items: flex-start;
165
+ gap: 9px;
166
+ width: 100%;
167
+ padding: 8px 10px;
168
+ border: 0;
169
+ border-radius: 8px;
170
+ background: none;
171
+ cursor: pointer;
172
+ text-align: start;
173
+ }
174
+ .menu-item:hover { background: var(--sb-hover); }
175
+ .menu-item .mi-icon { font-size: 15px; line-height: 1.25; flex: 0 0 18px; text-align: center; }
176
+ .menu-item .mi-body { flex: 1; min-width: 0; }
177
+ .menu-item .mi-title { font-size: 12.5px; font-weight: 600; }
178
+ .menu-item .mi-desc { font-size: 11.5px; color: var(--sb-ink-2); margin-top: 1px; line-height: 1.35; }
179
+ .menu-item .mi-check { color: var(--sb-accent); font-weight: 700; visibility: hidden; padding-top: 1px; }
180
+ .menu-item.is-checked .mi-check { visibility: visible; }
181
+ .menu-item.is-checked .mi-title { color: var(--sb-accent); }
182
+ .menu-empty { padding: 14px 12px; color: var(--sb-ink-3); font-size: 12px; }
183
+
184
+ /* ---------------------------------------------------------------- layout */
185
+
186
+ .workspace { flex: 1; display: flex; min-height: 0; }
187
+
188
+ /* ---------------------------------------------------------------- sidebar */
189
+
190
+ .sidebar {
191
+ width: 292px;
192
+ flex: 0 0 292px;
193
+ display: flex;
194
+ flex-direction: column;
195
+ background: var(--sb-panel);
196
+ border-inline-end: 1px solid var(--sb-line);
197
+ min-height: 0;
198
+ }
199
+
200
+ .sidebar-feature { padding: 14px 14px 6px; }
201
+ .sf-title { display: flex; align-items: center; gap: 8px; font-size: 15px; font-weight: 700; letter-spacing: -0.2px; }
202
+ .sf-title .sf-icon { font-size: 17px; }
203
+ .sf-desc { margin-top: 3px; font-size: 12px; line-height: 1.45; color: var(--sb-ink-2); }
204
+
205
+ .sidebar-search { padding: 8px 12px 10px; }
206
+ .sidebar-search input {
207
+ width: 100%;
208
+ height: 30px;
209
+ padding: 0 10px;
210
+ border: 1px solid var(--sb-line);
211
+ border-radius: 9px;
212
+ background: var(--sb-panel-2);
213
+ color: var(--sb-ink);
214
+ font-size: 12.5px;
215
+ outline: none;
216
+ }
217
+ .sidebar-search input:focus { border-color: var(--sb-accent); }
218
+ .sidebar-search input::placeholder { color: var(--sb-ink-3); }
219
+
220
+ .screen-list { flex: 1; overflow-y: auto; padding: 2px 8px 12px; min-height: 0; }
221
+
222
+ .group-head {
223
+ padding: 12px 8px 4px;
224
+ font-size: 10.5px;
225
+ font-weight: 700;
226
+ text-transform: uppercase;
227
+ letter-spacing: .8px;
228
+ color: var(--sb-ink-3);
229
+ }
230
+
231
+ .screen-row {
232
+ display: block;
233
+ width: 100%;
234
+ text-align: start;
235
+ padding: 9px 10px;
236
+ border: 0;
237
+ border-radius: 10px;
238
+ background: none;
239
+ cursor: pointer;
240
+ margin-bottom: 2px;
241
+ }
242
+ .screen-row:hover { background: var(--sb-hover); }
243
+ .screen-row.is-active { background: var(--sb-accent-soft); }
244
+ .screen-row.is-active .sr-title { color: var(--sb-accent); }
245
+ .sr-title { font-size: 13px; font-weight: 650; letter-spacing: -0.1px; }
246
+ .sr-desc {
247
+ margin-top: 2px;
248
+ font-size: 11.5px;
249
+ line-height: 1.4;
250
+ color: var(--sb-ink-2);
251
+ display: -webkit-box;
252
+ -webkit-line-clamp: 2;
253
+ -webkit-box-orient: vertical;
254
+ overflow: hidden;
255
+ }
256
+ .sr-meta { display: flex; align-items: center; gap: 6px; margin-top: 5px; flex-wrap: wrap; }
257
+ .sr-date { font-size: 10.5px; color: var(--sb-ink-3); font-variant-numeric: tabular-nums; }
258
+
259
+ .pill {
260
+ display: inline-flex;
261
+ align-items: center;
262
+ height: 16px;
263
+ padding: 0 7px;
264
+ border-radius: 999px;
265
+ font-size: 9.5px;
266
+ font-weight: 700;
267
+ text-transform: uppercase;
268
+ letter-spacing: .5px;
269
+ background: var(--sb-hover);
270
+ color: var(--sb-ink-2);
271
+ }
272
+ .pill.st-draft { background: rgba(180, 125, 18, .14); color: var(--sb-warn); }
273
+ .pill.st-wip { background: rgba(91, 91, 214, .12); color: var(--sb-accent); }
274
+ .pill.st-ready { background: rgba(34, 160, 107, .13); color: var(--sb-ok); }
275
+ .pill.st-review { background: rgba(214, 69, 91, .11); color: var(--sb-danger); }
276
+ .pill.pill-device { background: none; border: 1px solid var(--sb-line-2); }
277
+
278
+ .list-empty { padding: 26px 14px; text-align: center; color: var(--sb-ink-3); font-size: 12px; line-height: 1.6; }
279
+
280
+ .sidebar-foot {
281
+ flex: 0 0 auto;
282
+ display: flex;
283
+ align-items: center;
284
+ justify-content: space-between;
285
+ padding: 8px 14px;
286
+ border-top: 1px solid var(--sb-line);
287
+ }
288
+ .serve-badge { font-size: 11px; font-weight: 600; color: var(--sb-ink-3); }
289
+ .serve-badge.is-live { color: var(--sb-ok); }
290
+ .foot-link { font-size: 11px; font-weight: 600; color: var(--sb-ink-3); text-decoration: none; }
291
+ .foot-link:hover { color: var(--sb-accent); }
292
+
293
+ /* ---------------------------------------------------------------- canvas */
294
+
295
+ .canvas {
296
+ flex: 1;
297
+ min-width: 0;
298
+ position: relative;
299
+ display: flex;
300
+ flex-direction: column;
301
+ padding: 26px;
302
+ background:
303
+ radial-gradient(var(--sb-dots) 1px, transparent 1px) 0 0 / 22px 22px,
304
+ var(--sb-bg);
305
+ }
306
+
307
+ .stage {
308
+ flex: 1;
309
+ min-height: 0;
310
+ display: grid;
311
+ place-items: center;
312
+ position: relative;
313
+ }
314
+
315
+ .stage-empty {
316
+ max-width: 420px;
317
+ text-align: center;
318
+ color: var(--sb-ink-2);
319
+ font-size: 13px;
320
+ line-height: 1.7;
321
+ }
322
+ .stage-empty h2 { color: var(--sb-ink); font-size: 17px; letter-spacing: -0.2px; margin: 0 0 8px; }
323
+ .stage-empty code {
324
+ font-family: var(--sb-mono);
325
+ font-size: 11.5px;
326
+ background: var(--sb-panel);
327
+ border: 1px solid var(--sb-line);
328
+ padding: 2px 6px;
329
+ border-radius: 6px;
330
+ }
331
+
332
+ /* frame scaling wrapper: JS sets --frame-scale */
333
+ .frame-fit { display: grid; place-items: center; }
334
+ .frame-outer { transform: scale(var(--frame-scale, 1)); transform-origin: center center; }
335
+
336
+ /* screen-switch transition (disabled by config or prefers-reduced-motion) */
337
+ .frame-outer.is-entering { animation: sb-enter 0.18s ease; }
338
+ @keyframes sb-enter {
339
+ from { opacity: 0.35; transform: scale(var(--frame-scale, 1)) translateY(5px); }
340
+ to { opacity: 1; transform: scale(var(--frame-scale, 1)) translateY(0); }
341
+ }
342
+
343
+ /* device-variant chips above the frame */
344
+ .variant-chips {
345
+ position: absolute;
346
+ top: 2px;
347
+ left: 50%;
348
+ transform: translateX(-50%);
349
+ display: flex;
350
+ gap: 4px;
351
+ padding: 3px;
352
+ background: var(--sb-panel-2);
353
+ border: 1px solid var(--sb-line);
354
+ border-radius: 999px;
355
+ box-shadow: var(--sb-shadow-soft);
356
+ z-index: 20;
357
+ }
358
+ .variant-chip {
359
+ border: 0;
360
+ background: none;
361
+ border-radius: 999px;
362
+ padding: 4px 12px;
363
+ font-size: 11.5px;
364
+ font-weight: 650;
365
+ color: var(--sb-ink-2);
366
+ cursor: pointer;
367
+ }
368
+ .variant-chip:hover { background: var(--sb-hover); }
369
+ .variant-chip.is-on {
370
+ background: var(--sb-accent);
371
+ color: var(--sb-accent-ink);
372
+ }
373
+
374
+ /* ------------------------------------------------ device frames */
375
+
376
+ .device { position: relative; box-shadow: var(--sb-shadow); }
377
+ .device iframe {
378
+ border: 0;
379
+ display: block;
380
+ background: #fff;
381
+ width: 100%;
382
+ height: 100%;
383
+ }
384
+
385
+ /* iOS */
386
+ .device.dev-ios {
387
+ width: 393px; height: 852px;
388
+ border-radius: 55px;
389
+ padding: 10px;
390
+ background: #0B0B0E;
391
+ outline: 2px solid rgba(255, 255, 255, 0.06);
392
+ }
393
+ .device.dev-ios .dev-viewport {
394
+ position: relative;
395
+ width: 373px; height: 832px;
396
+ border-radius: 45px;
397
+ overflow: hidden;
398
+ background: #fff;
399
+ }
400
+ .dev-island {
401
+ position: absolute;
402
+ top: 11px; left: 50%;
403
+ transform: translateX(-50%);
404
+ width: 118px; height: 34px;
405
+ background: #000;
406
+ border-radius: 20px;
407
+ z-index: 12;
408
+ }
409
+ .dev-statusbar {
410
+ position: absolute;
411
+ top: 0; left: 0; right: 0;
412
+ height: 56px;
413
+ display: flex;
414
+ align-items: center;
415
+ justify-content: space-between;
416
+ padding: 14px 30px 0;
417
+ z-index: 11;
418
+ pointer-events: none;
419
+ font: 600 14.5px/1 var(--sb-font);
420
+ color: #000;
421
+ }
422
+ .dev-statusbar .sb-icons { display: flex; align-items: center; gap: 5px; font-size: 12px; letter-spacing: -1px; }
423
+ .device[data-screen-mode="dark"] .dev-statusbar { color: #fff; }
424
+ .device[data-statusbar="light"] .dev-statusbar { color: #fff !important; }
425
+ .device[data-statusbar="dark"] .dev-statusbar { color: #000 !important; }
426
+ .dev-homebar {
427
+ position: absolute;
428
+ bottom: 8px; left: 50%;
429
+ transform: translateX(-50%);
430
+ width: 134px; height: 5px;
431
+ border-radius: 3px;
432
+ background: #000;
433
+ opacity: .9;
434
+ z-index: 12;
435
+ }
436
+ .device[data-screen-mode="dark"] .dev-homebar { background: #fff; }
437
+ .device[data-statusbar="light"] .dev-homebar { background: #fff !important; }
438
+ .device[data-statusbar="dark"] .dev-homebar { background: #000 !important; }
439
+
440
+ /* Android */
441
+ .device.dev-android {
442
+ width: 412px; height: 915px;
443
+ border-radius: 32px;
444
+ padding: 8px;
445
+ background: #101013;
446
+ outline: 2px solid rgba(255, 255, 255, 0.06);
447
+ }
448
+ .device.dev-android .dev-viewport {
449
+ position: relative;
450
+ width: 396px; height: 899px;
451
+ border-radius: 24px;
452
+ overflow: hidden;
453
+ background: #fff;
454
+ }
455
+ .dev-punch {
456
+ position: absolute;
457
+ top: 12px; left: 50%;
458
+ transform: translateX(-50%);
459
+ width: 13px; height: 13px;
460
+ border-radius: 50%;
461
+ background: #000;
462
+ outline: 2px solid rgba(255, 255, 255, .08);
463
+ z-index: 12;
464
+ }
465
+ .dev-statusbar-android {
466
+ position: absolute;
467
+ top: 0; left: 0; right: 0;
468
+ height: 40px;
469
+ display: flex;
470
+ align-items: center;
471
+ justify-content: space-between;
472
+ padding: 8px 22px 0;
473
+ z-index: 11;
474
+ pointer-events: none;
475
+ font: 500 13px/1 Roboto, var(--sb-font);
476
+ color: #000;
477
+ }
478
+ .dev-statusbar-android .sb-icons { display: flex; gap: 4px; font-size: 11.5px; }
479
+ .device[data-screen-mode="dark"] .dev-statusbar-android { color: #fff; }
480
+ .device[data-statusbar="light"] .dev-statusbar-android { color: #fff !important; }
481
+ .device[data-statusbar="dark"] .dev-statusbar-android { color: #000 !important; }
482
+ .dev-gesturebar {
483
+ position: absolute;
484
+ bottom: 7px; left: 50%;
485
+ transform: translateX(-50%);
486
+ width: 108px; height: 4px;
487
+ border-radius: 2px;
488
+ background: #000;
489
+ opacity: .75;
490
+ z-index: 12;
491
+ }
492
+ .device[data-screen-mode="dark"] .dev-gesturebar { background: #fff; }
493
+
494
+ /* Web browser */
495
+ .device.dev-web {
496
+ width: 1280px;
497
+ border-radius: 12px;
498
+ background: var(--sb-panel-2);
499
+ border: 1px solid var(--sb-line-2);
500
+ overflow: hidden;
501
+ }
502
+ .dev-browserbar {
503
+ height: 40px;
504
+ display: flex;
505
+ align-items: center;
506
+ gap: 12px;
507
+ padding: 0 14px;
508
+ background: var(--sb-panel);
509
+ border-bottom: 1px solid var(--sb-line);
510
+ }
511
+ .dev-browserbar .bb-dots { display: flex; gap: 6px; }
512
+ .dev-browserbar .bb-dots i {
513
+ width: 11px; height: 11px;
514
+ border-radius: 50%;
515
+ display: block;
516
+ }
517
+ .bb-dots i:nth-child(1) { background: #F26D60; }
518
+ .bb-dots i:nth-child(2) { background: #F2BE4B; }
519
+ .bb-dots i:nth-child(3) { background: #5FC454; }
520
+ .dev-browserbar .bb-url {
521
+ flex: 0 1 460px;
522
+ margin: 0 auto;
523
+ height: 24px;
524
+ display: flex;
525
+ align-items: center;
526
+ justify-content: center;
527
+ gap: 6px;
528
+ border-radius: 7px;
529
+ background: var(--sb-hover);
530
+ color: var(--sb-ink-2);
531
+ font-size: 11.5px;
532
+ font-family: var(--sb-mono);
533
+ white-space: nowrap; overflow: hidden;
534
+ }
535
+ .device.dev-web .dev-viewport { position: relative; width: 1280px; height: 760px; background: #fff; }
536
+
537
+ /* Bare */
538
+ .device.dev-none {
539
+ border-radius: 14px;
540
+ border: 1px solid var(--sb-line-2);
541
+ overflow: hidden;
542
+ background: #fff;
543
+ }
544
+ .device.dev-none .dev-viewport { position: relative; width: 900px; height: 640px; }
545
+
546
+ /* app mode: the app draws its own status/home chrome — engine shows bezel only */
547
+ .device[data-chrome="self"] .dev-statusbar,
548
+ .device[data-chrome="self"] .dev-statusbar-android,
549
+ .device[data-chrome="self"] .dev-homebar,
550
+ .device[data-chrome="self"] .dev-gesturebar,
551
+ .device[data-chrome="self"] .dev-island,
552
+ .device[data-chrome="self"] .dev-browserbar,
553
+ .device[data-chrome="self"] .dev-punch { display: none; }
554
+ .device.dev-web[data-chrome="self"] .dev-viewport { height: 800px; }
555
+
556
+ /* screen loading veil */
557
+ .dev-viewport .load-veil {
558
+ position: absolute; inset: 0;
559
+ display: grid; place-items: center;
560
+ background: var(--sb-panel);
561
+ color: var(--sb-ink-3);
562
+ font-size: 12px;
563
+ z-index: 10;
564
+ opacity: 1;
565
+ transition: opacity .18s ease;
566
+ pointer-events: none;
567
+ }
568
+ .dev-viewport .load-veil.is-hidden {
569
+ opacity: 0;
570
+ visibility: hidden; /* drop out of the a11y tree once faded */
571
+ transition: opacity .18s ease, visibility 0s linear .2s;
572
+ }
573
+
574
+ /* ---------------------------------------------------------------- flowbar */
575
+
576
+ .flowbar {
577
+ position: absolute;
578
+ left: 50%;
579
+ bottom: 20px;
580
+ transform: translateX(-50%);
581
+ display: flex;
582
+ align-items: center;
583
+ gap: 12px;
584
+ max-width: min(720px, calc(100% - 40px));
585
+ padding: 10px 12px;
586
+ background: var(--sb-panel-2);
587
+ border: 1px solid var(--sb-line);
588
+ border-radius: 14px;
589
+ box-shadow: var(--sb-shadow);
590
+ z-index: 30;
591
+ }
592
+ .flowbar .fb-nav {
593
+ width: 30px; height: 30px;
594
+ display: grid; place-items: center;
595
+ border: 1px solid var(--sb-line);
596
+ border-radius: 9px;
597
+ background: var(--sb-panel);
598
+ cursor: pointer;
599
+ font-size: 13px;
600
+ flex: 0 0 auto;
601
+ }
602
+ .flowbar .fb-nav:hover:not(:disabled) { background: var(--sb-hover); }
603
+ .flowbar .fb-nav:disabled { opacity: .35; cursor: default; }
604
+ .flowbar .fb-body { min-width: 0; }
605
+ .flowbar .fb-title { font-size: 12px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
606
+ .flowbar .fb-title .fb-count { color: var(--sb-ink-3); font-weight: 600; margin-inline-start: 6px; font-variant-numeric: tabular-nums; }
607
+ .flowbar .fb-note { font-size: 12px; color: var(--sb-ink-2); line-height: 1.45; margin-top: 1px; }
608
+ .flowbar .fb-close {
609
+ border: 0; background: none;
610
+ color: var(--sb-ink-3);
611
+ cursor: pointer;
612
+ font-size: 14px;
613
+ flex: 0 0 auto;
614
+ padding: 4px 6px;
615
+ }
616
+ .flowbar .fb-close:hover { color: var(--sb-danger); }
617
+
618
+ /* ---------------------------------------------------------------- review */
619
+
620
+ .review-panel {
621
+ width: 320px;
622
+ flex: 0 0 320px;
623
+ display: flex;
624
+ flex-direction: column;
625
+ background: var(--sb-panel);
626
+ border-inline-start: 1px solid var(--sb-line);
627
+ min-height: 0;
628
+ }
629
+ .rp-head {
630
+ display: flex;
631
+ align-items: center;
632
+ justify-content: space-between;
633
+ padding: 13px 14px 9px;
634
+ }
635
+ .rp-head h3 { margin: 0; font-size: 13.5px; letter-spacing: -0.1px; }
636
+ .rp-head .rp-count { color: var(--sb-ink-3); font-weight: 600; margin-inline-start: 6px; }
637
+ .rp-tools { display: flex; gap: 6px; }
638
+ .rp-tool {
639
+ border: 1px solid var(--sb-line);
640
+ background: var(--sb-panel-2);
641
+ border-radius: 7px;
642
+ height: 24px;
643
+ padding: 0 8px;
644
+ font-size: 11px;
645
+ font-weight: 600;
646
+ cursor: pointer;
647
+ color: var(--sb-ink-2);
648
+ }
649
+ .rp-tool:hover { background: var(--sb-hover); }
650
+
651
+ .rp-reviewer { padding: 0 14px 8px; }
652
+ .rp-reviewer input {
653
+ width: 100%;
654
+ height: 27px;
655
+ border: 1px solid var(--sb-line);
656
+ border-radius: 8px;
657
+ background: var(--sb-panel-2);
658
+ color: var(--sb-ink);
659
+ padding: 0 9px;
660
+ font-size: 12px;
661
+ outline: none;
662
+ }
663
+ .rp-reviewer input:focus { border-color: var(--sb-accent); }
664
+
665
+ .rp-hint {
666
+ margin: 0 14px 8px;
667
+ padding: 8px 10px;
668
+ border-radius: 9px;
669
+ background: var(--sb-accent-soft);
670
+ color: var(--sb-accent);
671
+ font-size: 11.5px;
672
+ line-height: 1.5;
673
+ font-weight: 550;
674
+ }
675
+
676
+ .rp-list { flex: 1; overflow-y: auto; padding: 2px 10px 12px; min-height: 0; }
677
+ .rp-item {
678
+ border: 1px solid var(--sb-line);
679
+ border-radius: 11px;
680
+ background: var(--sb-panel-2);
681
+ padding: 9px 11px;
682
+ margin-bottom: 8px;
683
+ cursor: pointer;
684
+ }
685
+ .rp-item:hover { border-color: var(--sb-line-2); }
686
+ .rp-item.is-focus { border-color: var(--sb-accent); }
687
+ .rp-item.is-resolved { opacity: .62; }
688
+ .rp-item .ri-top { display: flex; align-items: center; gap: 7px; }
689
+ .rp-item .ri-n {
690
+ width: 18px; height: 18px;
691
+ border-radius: 50%;
692
+ background: var(--sb-danger);
693
+ color: #fff;
694
+ display: grid; place-items: center;
695
+ font-size: 10px; font-weight: 700;
696
+ flex: 0 0 auto;
697
+ }
698
+ .rp-item.is-resolved .ri-n { background: var(--sb-ok); }
699
+ .rp-item .ri-note { font-size: 12.5px; line-height: 1.45; font-weight: 550; }
700
+ .rp-item .ri-ctx {
701
+ margin-top: 6px;
702
+ font-size: 10.5px;
703
+ color: var(--sb-ink-3);
704
+ font-family: var(--sb-mono);
705
+ line-height: 1.5;
706
+ word-break: break-all;
707
+ }
708
+ .rp-item .ri-meta { display: flex; align-items: center; gap: 6px; margin-top: 6px; flex-wrap: wrap; }
709
+ .rp-item .ri-actions { display: flex; gap: 6px; margin-top: 7px; }
710
+
711
+ .rp-composer {
712
+ border-top: 1px solid var(--sb-line);
713
+ padding: 10px 12px 12px;
714
+ }
715
+ .rp-composer textarea {
716
+ width: 100%;
717
+ min-height: 56px;
718
+ resize: vertical;
719
+ border: 1px solid var(--sb-line);
720
+ border-radius: 9px;
721
+ background: var(--sb-panel-2);
722
+ color: var(--sb-ink);
723
+ font: inherit;
724
+ font-size: 12.5px;
725
+ padding: 8px 9px;
726
+ outline: none;
727
+ }
728
+ .rp-composer textarea:focus { border-color: var(--sb-accent); }
729
+ .rp-composer .rc-row { display: flex; justify-content: space-between; align-items: center; margin-top: 7px; gap: 8px; }
730
+ .rp-composer .rc-ctx { font-size: 10.5px; color: var(--sb-ink-3); font-family: var(--sb-mono); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
731
+ .btn-primary {
732
+ border: 0;
733
+ background: var(--sb-accent);
734
+ color: var(--sb-accent-ink);
735
+ border-radius: 8px;
736
+ height: 27px;
737
+ padding: 0 12px;
738
+ font-size: 12px;
739
+ font-weight: 700;
740
+ cursor: pointer;
741
+ }
742
+ .btn-primary:hover { filter: brightness(1.06); }
743
+ .btn-ghost {
744
+ border: 1px solid var(--sb-line);
745
+ background: none;
746
+ border-radius: 8px;
747
+ height: 27px;
748
+ padding: 0 10px;
749
+ font-size: 12px;
750
+ font-weight: 600;
751
+ color: var(--sb-ink-2);
752
+ cursor: pointer;
753
+ }
754
+ .btn-ghost:hover { background: var(--sb-hover); }
755
+
756
+ /* ---------------------------------------------------------------- theme lab */
757
+
758
+ .theme-panel {
759
+ width: 320px;
760
+ flex: 0 0 320px;
761
+ display: flex;
762
+ flex-direction: column;
763
+ background: var(--sb-panel);
764
+ border-inline-start: 1px solid var(--sb-line);
765
+ min-height: 0;
766
+ }
767
+ .tl-themes { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 14px 10px; }
768
+ .tl-theme-chip {
769
+ border: 1px solid var(--sb-line);
770
+ background: var(--sb-panel-2);
771
+ border-radius: 999px;
772
+ padding: 5px 12px;
773
+ font-size: 12px;
774
+ font-weight: 650;
775
+ color: var(--sb-ink-2);
776
+ cursor: pointer;
777
+ }
778
+ .tl-theme-chip.is-on {
779
+ background: var(--sb-accent-soft);
780
+ border-color: var(--sb-accent);
781
+ color: var(--sb-accent);
782
+ }
783
+ .tl-strip {
784
+ display: flex;
785
+ gap: 6px;
786
+ padding: 2px 14px 10px;
787
+ }
788
+ .tl-strip span {
789
+ flex: 1;
790
+ height: 26px;
791
+ border-radius: 7px;
792
+ border: 1px solid var(--sb-line);
793
+ }
794
+ .tl-rows { flex: 1; overflow-y: auto; padding: 4px 12px 14px; min-height: 0; }
795
+ .tl-row {
796
+ display: flex;
797
+ align-items: center;
798
+ gap: 7px;
799
+ padding: 4px 2px;
800
+ }
801
+ .tl-row label {
802
+ flex: 0 0 108px;
803
+ font-size: 11px;
804
+ font-weight: 650;
805
+ font-family: var(--sb-mono);
806
+ color: var(--sb-ink-2);
807
+ overflow: hidden;
808
+ text-overflow: ellipsis;
809
+ white-space: nowrap;
810
+ }
811
+ .tl-row input[type="color"] {
812
+ flex: 0 0 30px;
813
+ width: 30px; height: 26px;
814
+ padding: 1px;
815
+ border: 1px solid var(--sb-line);
816
+ border-radius: 7px;
817
+ background: var(--sb-panel-2);
818
+ cursor: pointer;
819
+ }
820
+ .tl-row .tl-mini {
821
+ flex: 0 0 30px;
822
+ height: 26px;
823
+ border-radius: 7px;
824
+ border: 1px solid var(--sb-line);
825
+ }
826
+ .tl-row input[type="text"] {
827
+ flex: 1;
828
+ min-width: 0;
829
+ height: 26px;
830
+ border: 1px solid var(--sb-line);
831
+ border-radius: 7px;
832
+ background: var(--sb-panel-2);
833
+ color: var(--sb-ink);
834
+ font-family: var(--sb-mono);
835
+ font-size: 11px;
836
+ padding: 0 8px;
837
+ outline: none;
838
+ }
839
+ .tl-row input[type="text"]:focus { border-color: var(--sb-accent); }
840
+
841
+ /* ---------------------------------------------------------------- toasts */
842
+
843
+ .shell-toasts {
844
+ position: fixed;
845
+ left: 50%;
846
+ bottom: 22px;
847
+ transform: translateX(-50%);
848
+ display: flex;
849
+ flex-direction: column;
850
+ align-items: center;
851
+ gap: 8px;
852
+ z-index: 300;
853
+ pointer-events: none;
854
+ }
855
+ .shell-toast {
856
+ padding: 9px 16px;
857
+ border-radius: 999px;
858
+ background: var(--sb-ink);
859
+ color: var(--sb-bg);
860
+ font-size: 12.5px;
861
+ font-weight: 600;
862
+ box-shadow: var(--sb-shadow);
863
+ opacity: 0;
864
+ transform: translateY(8px);
865
+ transition: all .2s ease;
866
+ }
867
+ .shell-toast.is-in { opacity: 1; transform: translateY(0); }
868
+
869
+ /* ---------------------------------------------------------------- selftest */
870
+
871
+ .selftest-overlay {
872
+ position: fixed;
873
+ inset: 0;
874
+ z-index: 400;
875
+ background: color-mix(in srgb, var(--sb-bg) 88%, transparent);
876
+ backdrop-filter: blur(6px);
877
+ display: grid;
878
+ place-items: center;
879
+ padding: 30px;
880
+ }
881
+ .st-card {
882
+ width: min(760px, 100%);
883
+ max-height: 100%;
884
+ display: flex;
885
+ flex-direction: column;
886
+ background: var(--sb-panel-2);
887
+ border: 1px solid var(--sb-line);
888
+ border-radius: 16px;
889
+ box-shadow: var(--sb-shadow);
890
+ overflow: hidden;
891
+ }
892
+ .st-head {
893
+ display: flex;
894
+ align-items: center;
895
+ justify-content: space-between;
896
+ gap: 12px;
897
+ padding: 16px 18px 12px;
898
+ }
899
+ .st-head h2 { margin: 0; font-size: 16px; letter-spacing: -0.2px; }
900
+ .st-summary { font-size: 12.5px; font-weight: 700; }
901
+ .st-summary.is-pass { color: var(--sb-ok); }
902
+ .st-summary.is-fail { color: var(--sb-danger); }
903
+ .st-progress {
904
+ height: 4px;
905
+ margin: 0 18px;
906
+ border-radius: 2px;
907
+ background: var(--sb-hover);
908
+ overflow: hidden;
909
+ }
910
+ .st-progress i {
911
+ display: block;
912
+ height: 100%;
913
+ width: 0;
914
+ background: var(--sb-accent);
915
+ border-radius: 2px;
916
+ transition: width .15s ease;
917
+ }
918
+ .st-rows { flex: 1; overflow-y: auto; padding: 10px 12px 14px; min-height: 120px; }
919
+ .st-row {
920
+ display: flex;
921
+ align-items: baseline;
922
+ gap: 8px;
923
+ padding: 5px 8px;
924
+ border-radius: 7px;
925
+ font-size: 12px;
926
+ }
927
+ .st-row .st-ico { flex: 0 0 auto; }
928
+ .st-row.is-pass .st-ico { color: var(--sb-ok); }
929
+ .st-row.is-fail { background: rgba(214, 69, 91, .07); }
930
+ .st-row.is-fail .st-ico { color: var(--sb-danger); }
931
+ .st-row .st-name { font-weight: 600; }
932
+ .st-row .st-combo { color: var(--sb-ink-3); font-family: var(--sb-mono); font-size: 10.5px; }
933
+ .st-row .st-err { color: var(--sb-danger); font-size: 11px; margin-inline-start: auto; max-width: 45%; text-align: end; }
934
+ .st-foot {
935
+ display: flex;
936
+ justify-content: space-between;
937
+ align-items: center;
938
+ padding: 10px 18px 14px;
939
+ border-top: 1px solid var(--sb-line);
940
+ }
941
+ .st-foot .st-note { font-size: 11.5px; color: var(--sb-ink-3); }
942
+
943
+ /* ---------------------------------------------------------------- misc */
944
+
945
+ ::-webkit-scrollbar { width: 10px; height: 10px; }
946
+ ::-webkit-scrollbar-thumb { background: var(--sb-line-2); border-radius: 5px; border: 2px solid transparent; background-clip: content-box; }
947
+ ::-webkit-scrollbar-track { background: transparent; }
948
+
949
+ @media (max-width: 900px) {
950
+ .sidebar { width: 240px; flex-basis: 240px; }
951
+ .review-panel { width: 270px; flex-basis: 270px; }
952
+ }