@divlet/web 0.0.0-stage → 0.0.1

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 (44) hide show
  1. package/README.md +12 -2
  2. package/dist/LICENSE +21 -0
  3. package/dist/board.css +948 -0
  4. package/dist/board.js +427 -0
  5. package/dist/index.d.ts +4 -0
  6. package/dist/index.html +73 -0
  7. package/dist/index.js +3 -0
  8. package/dist/lib/actions.js +472 -0
  9. package/dist/lib/activity.js +311 -0
  10. package/dist/lib/artboards.js +56 -0
  11. package/dist/lib/ask.js +350 -0
  12. package/dist/lib/attrs.js +144 -0
  13. package/dist/lib/board-host.d.ts +14 -0
  14. package/dist/lib/board-host.js +2 -0
  15. package/dist/lib/board-session.d.ts +53 -0
  16. package/dist/lib/board-session.js +24 -0
  17. package/dist/lib/canvas.js +1594 -0
  18. package/dist/lib/chat.js +156 -0
  19. package/dist/lib/clip.js +231 -0
  20. package/dist/lib/crumbbar.js +56 -0
  21. package/dist/lib/crumbs.js +63 -0
  22. package/dist/lib/css.js +238 -0
  23. package/dist/lib/expandbar.js +64 -0
  24. package/dist/lib/file-picker.js +103 -0
  25. package/dist/lib/help.js +273 -0
  26. package/dist/lib/host.js +16 -0
  27. package/dist/lib/http-services.js +24 -0
  28. package/dist/lib/http-session.js +45 -0
  29. package/dist/lib/http.js +17 -0
  30. package/dist/lib/inspector.js +857 -0
  31. package/dist/lib/layers.js +298 -0
  32. package/dist/lib/menu.js +119 -0
  33. package/dist/lib/palette.js +292 -0
  34. package/dist/lib/panels.js +27 -0
  35. package/dist/lib/project.js +178 -0
  36. package/dist/lib/resize.js +114 -0
  37. package/dist/lib/shortcuts.js +91 -0
  38. package/dist/lib/sidebar.js +85 -0
  39. package/dist/lib/store.js +249 -0
  40. package/dist/lib/suggest.js +130 -0
  41. package/dist/lib/toolbar.js +54 -0
  42. package/dist/lib/typography.js +120 -0
  43. package/dist/lib/util.js +177 -0
  44. package/package.json +37 -4
package/dist/board.css ADDED
@@ -0,0 +1,948 @@
1
+ /* divlet — a quiet, fluid workspace. One accent, hairlines, no chrome you do not need. */
2
+ :root {
3
+ color-scheme: light;
4
+ /* Interface size (Settings): the chrome is drawn at --ui times its size, the canvas never is. */
5
+ --ui: 1;
6
+ --vw: calc(100vw / var(--ui));
7
+ --vh: calc(100dvh / var(--ui));
8
+ --bg: #f3f2ee;
9
+ --dot: #d6d3c9;
10
+ --ink: #1b1b19;
11
+ --muted: #6a675d;
12
+ --faint: #b2afa3;
13
+ --panel: rgba(255, 255, 253, .94);
14
+ --solid: #fffffd;
15
+ --field: rgba(27, 27, 25, .05);
16
+ --field-hover: rgba(27, 27, 25, .085);
17
+ --hair: rgba(27, 27, 25, .1);
18
+ --accent: #5b5bf0; --on-accent: #fff; --bad-ink: #c4262c;
19
+ --accent-ink: #4545d6;
20
+ --accent-soft: rgba(91, 91, 240, .13);
21
+ --ok: #2fa56f;
22
+ --warn: #c98a1c;
23
+ --bad: #e5484d;
24
+ --bad-soft: rgba(229, 72, 77, .12);
25
+ --guide: #ff4d8d;
26
+ --component: #b2459b;
27
+ --component-soft: rgba(178, 69, 155, .13);
28
+ --syn-comment: #9a978a; --syn-atrule: #9333ea; --syn-selector: #b45309; --syn-class: #c2410c; --syn-pseudo: #0e7490; --syn-prop: #2563eb; --syn-number: #0f766e; --syn-color: #be185d; --syn-string: #15803d; --syn-func: #0369a1; --syn-var: #7c3aed; --syn-important: #dc2626; --syn-punct: #8d8a7f;
29
+ --shadow: 0 1px 2px rgba(20, 20, 15, .06), 0 10px 32px rgba(20, 20, 15, .1);
30
+ --page-shadow: 0 1px 3px rgba(20, 20, 15, .1), 0 14px 44px rgba(20, 20, 15, .14);
31
+ --radius: 14px;
32
+ /* Inside a chrome surface: one step smaller than the surface, so a row reads as part of the pill. */
33
+ --radius-inner: 9px;
34
+ /* Small controls (chips, cells, swatches, mini links) and the one focus ring every control shares. */
35
+ --radius-small: 6px;
36
+ --focus-ring: 2px;
37
+ /* A disabled control is one step back; a disabled field keeps its text readable. */
38
+ --disabled: .4;
39
+ --disabled-field: .55;
40
+ --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
41
+ --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
42
+ --ease: cubic-bezier(.2, .7, .2, 1);
43
+ }
44
+ @media (prefers-color-scheme: dark) {
45
+ :root:not([data-theme="light"]) {
46
+ color-scheme: dark;
47
+ --bg: #131312; --dot: #2b2b28; --ink: #ecebe6; --muted: #a3a199; --faint: #57564f;
48
+ --panel: rgba(28, 28, 26, .93); --solid: #1c1c1a; --field: rgba(236, 235, 230, .06); --field-hover: rgba(236, 235, 230, .1); --hair: rgba(236, 235, 230, .1);
49
+ --accent: #8d8dff; --on-accent: #15141f; --bad-ink: #ff8f93; --accent-ink: #a5a5ff; --accent-soft: rgba(141, 141, 255, .17); --ok: #46c88e; --warn: #e3a83b; --bad-soft: rgba(229, 72, 77, .18); --component: #e58ad0; --component-soft: rgba(229, 138, 208, .16);
50
+ --syn-comment: #6f6e65; --syn-atrule: #c792ea; --syn-selector: #f0b36b; --syn-class: #ffa07a; --syn-pseudo: #7fd6e6; --syn-prop: #82aaff; --syn-number: #7fdbca; --syn-color: #f78fb3; --syn-string: #a5d6a7; --syn-func: #7dcfff; --syn-var: #bb9af7; --syn-important: #ff757f; --syn-punct: #7d7b72;
51
+ --shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 12px 36px rgba(0, 0, 0, .45);
52
+ --page-shadow: 0 1px 3px rgba(0, 0, 0, .4), 0 16px 50px rgba(0, 0, 0, .5);
53
+ }
54
+ }
55
+ :root[data-theme="dark"] {
56
+ color-scheme: dark;
57
+ --bg: #131312; --dot: #2b2b28; --ink: #ecebe6; --muted: #a3a199; --faint: #57564f;
58
+ --panel: rgba(28, 28, 26, .93); --solid: #1c1c1a; --field: rgba(236, 235, 230, .06); --field-hover: rgba(236, 235, 230, .1); --hair: rgba(236, 235, 230, .1);
59
+ --accent: #8d8dff; --on-accent: #15141f; --bad-ink: #ff8f93; --accent-ink: #a5a5ff; --accent-soft: rgba(141, 141, 255, .17); --ok: #46c88e; --warn: #e3a83b; --bad-soft: rgba(229, 72, 77, .18); --component: #e58ad0; --component-soft: rgba(229, 138, 208, .16);
60
+ --syn-comment: #6f6e65; --syn-atrule: #c792ea; --syn-selector: #f0b36b; --syn-class: #ffa07a; --syn-pseudo: #7fd6e6; --syn-prop: #82aaff; --syn-number: #7fdbca; --syn-color: #f78fb3; --syn-string: #a5d6a7; --syn-func: #7dcfff; --syn-var: #bb9af7; --syn-important: #ff757f; --syn-punct: #7d7b72;
61
+ --shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 12px 36px rgba(0, 0, 0, .45);
62
+ --page-shadow: 0 1px 3px rgba(0, 0, 0, .4), 0 16px 50px rgba(0, 0, 0, .5);
63
+ }
64
+ * { box-sizing: border-box; }
65
+ /* Everything that is not the canvas lives here and is scaled as one. */
66
+ #ui { position: fixed; left: 0; top: 0; z-index: 50; width: var(--vw); height: var(--vh); transform: scale(var(--ui)); transform-origin: 0 0; pointer-events: none; }
67
+ #ui > * { pointer-events: auto; }
68
+ /* Popups sit outside #ui (they are placed from on-screen rectangles) and scale from their own corner. */
69
+ @keyframes rise-ui { from { opacity: 0; transform: translateY(6px) scale(calc(var(--ui) * .985)); } }
70
+ html, body { margin: 0; height: 100%; overflow: hidden; background: var(--bg); color: var(--ink); font: 12.5px/1.4 var(--font); -webkit-font-smoothing: antialiased; overscroll-behavior: none; }
71
+ button, input, textarea { font: inherit; color: inherit; }
72
+ button { cursor: pointer; }
73
+ [hidden] { display: none !important; }
74
+ .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
75
+ kbd { flex: none; white-space: nowrap; font: 600 11px var(--mono); color: var(--muted); border: 1px solid var(--hair); border-radius: 6px; padding: 1px 6px; }
76
+ :focus-visible { outline: var(--focus-ring) solid var(--accent); outline-offset: 1px; }
77
+ /* A control that lives inside a field wrapper is ringed by the wrapper, never twice. */
78
+ :is(.vf, .num, .code, .classes, .ident-field, .search, .color-field, .bm-field, .settings-output) :is(input, textarea):focus-visible { outline: none; }
79
+ * { scrollbar-width: thin; scrollbar-color: var(--hair) transparent; }
80
+ .grow { flex: 1; }
81
+
82
+ /* ---- canvas --------------------------------------------------------------- */
83
+ .viewport { position: fixed; inset: 0; overflow: hidden; touch-action: none; outline: none; cursor: default; user-select: none; -webkit-user-select: none;
84
+ --grid: 24px; --gx: 0px; --gy: 0px;
85
+ background-image: radial-gradient(circle at 1px 1px, var(--dot) 1.1px, transparent 1.5px); background-size: var(--grid) var(--grid); background-position: var(--gx) var(--gy); }
86
+ .viewport.space-ready, .viewport.mode-interact.space-ready { cursor: grab; }
87
+ .viewport.panning, .viewport.moving { cursor: grabbing; }
88
+ .stage { position: absolute; inset: 0; }
89
+ .guides { position: absolute; inset: 0; pointer-events: none; z-index: 5; }
90
+ .guide { position: absolute; background: var(--guide); }
91
+ .guide-x { width: 1px; }
92
+ .guide-y { height: 1px; }
93
+ .marquee { position: absolute; z-index: 6; border: 1px solid var(--accent); background: var(--accent-soft); border-radius: 3px; pointer-events: none; }
94
+ .hover-badge { position: absolute; z-index: 7; display: flex; align-items: center; gap: 6px; min-height: 21px; padding: 0 7px; border-radius: 6px; background: var(--accent); color: var(--on-accent); font: 11px var(--mono); pointer-events: none; box-shadow: 0 2px 8px rgba(0, 0, 0, .18); }
95
+ .hover-badge b { font-weight: 700; }
96
+ .hover-badge span { opacity: .82; max-width: 16em; white-space: normal; overflow-wrap: anywhere; }
97
+ .hover-badge i { font-style: normal; opacity: .7; }
98
+
99
+ .artboard { position: absolute; left: 0; top: 0; transform-origin: 0 0; will-change: transform; background: #fff; box-shadow: var(--page-shadow); border-radius: 2px; }
100
+ .artboard::after { content: ""; position: absolute; inset: -1px; border: 1px solid var(--hair); border-radius: 3px; pointer-events: none; transition: border-color .15s, box-shadow .15s; }
101
+ .mode-select .artboard:hover:not(.selected)::after { border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
102
+ .artboard.selected::after { inset: -2px; border: 2px solid var(--accent); }
103
+ .artboard.working::after { inset: -2px; border: 2px solid var(--accent); animation: work-glow 1.5s ease-in-out infinite; }
104
+ @keyframes work-glow { 0%, 100% { box-shadow: 0 0 0 0 transparent; border-color: color-mix(in srgb, var(--accent) 45%, transparent); } 50% { box-shadow: 0 0 0 8px var(--accent-soft), 0 0 28px var(--accent-soft); border-color: var(--accent); } }
105
+ .artboard.working .ab-body::after { content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 2; background: linear-gradient(100deg, transparent 30%, color-mix(in srgb, var(--accent) 14%, transparent) 50%, transparent 70%) 150% 0 / 250% 100% no-repeat; animation: work-sweep 1.8s linear infinite; }
106
+ @keyframes work-sweep { to { background-position: -150% 0; } }
107
+ .artboard.touched::after { box-shadow: 0 0 0 6px var(--accent-soft); }
108
+ .ab-body { position: absolute; left: 0; top: 0; transform-origin: 0 0; overflow: hidden; border-radius: 2px; background: #fff; }
109
+ .ab-body iframe { display: block; width: 100%; height: 100%; border: 0; background: #fff; color-scheme: light; }
110
+ .frame-cover { position: absolute; inset: 0; pointer-events: none; }
111
+ /* Far zoom: the cached picture replaces the live page, which stays loaded but is not painted. */
112
+ .ab-body img.frame-snapshot { position: absolute; left: 0; top: 0; z-index: 1; width: 100%; height: 100%; display: block; pointer-events: none; user-select: none; }
113
+ .artboard.lod iframe { visibility: hidden; }
114
+ /* The previous page of a frame, held above its replacement until that one has painted (no blank flash). */
115
+ .ab-body iframe.frame-ghost { position: absolute; left: 0; top: 0; z-index: 1; pointer-events: none; }
116
+ .mode-select .frame-cover { pointer-events: auto; cursor: default; }
117
+ .mode-select .artboard.editing .frame-cover { pointer-events: none; }
118
+ .frame-placeholder { position: absolute; inset: 0; background: linear-gradient(100deg, transparent 30%, rgba(127, 127, 127, .09) 50%, transparent 70%) 0 0 / 220% 100%, #fff; animation: shimmer 1.6s linear infinite; }
119
+ .artboard[data-status="live"] .frame-placeholder, .artboard[data-status="updating"] .frame-placeholder { display: none; }
120
+ @keyframes shimmer { to { background-position: -220% 0; } }
121
+
122
+ .ab-label { --label-scale: min(1, var(--canvas-scale, 1)); position: absolute; left: 0; bottom: calc(100% + 6px * min(1, var(--canvas-scale, 1))); display: flex; align-items: center; gap: 6px; width: calc(100% / var(--label-scale)); min-width: 0; height: calc(22px * var(--ui)); color: var(--muted); font-size: calc(12px * var(--ui)); white-space: nowrap; cursor: grab; transform: scale(var(--label-scale)); transform-origin: bottom left; transition: color .15s; }
123
+ .artboard:is(:hover, .selected, :focus-within) .ab-label { --label-scale: 1; min-width: min(220px, calc(100vw - 32px)); }
124
+ .artboard:is(:hover, .selected, :focus-within) .ab-name { flex: none; overflow: visible; max-width: calc(100vw - 32px); }
125
+ .artboard:hover .ab-label, .artboard.selected .ab-label { color: var(--ink); }
126
+ .ab-name { overflow: hidden; text-overflow: ellipsis; min-width: 0; }
127
+ .ab-size { font: calc(11px * var(--ui)) var(--mono); color: var(--muted); opacity: 0; transition: opacity .15s; flex: none; }
128
+ .artboard:hover .ab-size, .artboard.selected .ab-size, .artboard.resizing .ab-size { opacity: 1; }
129
+ .artboard.resizing .ab-size { color: var(--accent); }
130
+ .ab-size-input { width: 9.5em; font: 11px var(--mono); }
131
+ .ab-size { cursor: text; }
132
+ .chip.lock { display: inline-grid; place-items: center; width: 26px; padding: 0; }
133
+ .chip.lock .icon { width: 13px; height: 13px; }
134
+ .ab-rename { font: inherit; width: 14em; padding: 1px 5px; border: 0; border-radius: var(--radius-small); background: var(--solid); color: var(--ink); outline: var(--focus-ring) solid var(--accent); }
135
+ .ab-expand { margin-left: auto; }
136
+ .ab-expand, .ab-more { display: grid; place-items: center; width: 20px; height: 20px; flex: none; padding: 0; border: 0; border-radius: 6px; background: transparent; color: var(--muted); opacity: 0; transition: opacity .15s, background .15s; }
137
+ .artboard:hover :is(.ab-expand, .ab-more), .artboard.selected :is(.ab-expand, .ab-more), :is(.ab-expand, .ab-more):focus-visible { opacity: 1; }
138
+ :is(.ab-expand, .ab-more):hover { background: var(--field-hover); color: var(--ink); }
139
+ :is(.ab-expand, .ab-more) .icon { width: 13px; height: 13px; }
140
+ .ab-error { display: none; flex: none; color: var(--bad); font-weight: 700; }
141
+ .artboard[data-status="error"] .ab-error { display: block; }
142
+ @keyframes pulse { 50% { opacity: .35; } }
143
+
144
+ .ab-handles { position: absolute; inset: 0; pointer-events: none; z-index: 3; display: none; }
145
+ .artboard.sole .ab-handles { display: block; }
146
+ .ab-handle { position: absolute; pointer-events: auto; touch-action: none; }
147
+ .h-n, .h-s { left: 8px; right: 8px; height: 12px; cursor: ns-resize; }
148
+ .h-e, .h-w { top: 8px; bottom: 8px; width: 12px; cursor: ew-resize; }
149
+ .h-n { top: -7px; } .h-s { bottom: -7px; } .h-e { right: -7px; } .h-w { left: -7px; }
150
+ .h-ne, .h-nw, .h-se, .h-sw { width: 10px; height: 10px; background: #fff; border: 1.5px solid var(--accent); border-radius: 3px; }
151
+ .h-ne { top: -6px; right: -6px; cursor: nesw-resize; } .h-nw { top: -6px; left: -6px; cursor: nwse-resize; }
152
+ .h-se { bottom: -6px; right: -6px; cursor: nwse-resize; } .h-sw { bottom: -6px; left: -6px; cursor: nesw-resize; }
153
+ @media (pointer: coarse) { .h-ne, .h-nw, .h-se, .h-sw { width: 16px; height: 16px; } .h-ne { top: -9px; right: -9px; } .h-nw { top: -9px; left: -9px; } .h-se { bottom: -9px; right: -9px; } .h-sw { bottom: -9px; left: -9px; } .ab-expand, .ab-more { opacity: 1; } }
154
+
155
+ .artboard.expanded { position: fixed; inset: 0; z-index: 40; width: 100% !important; height: 100% !important; transform: none !important; border-radius: 0; box-shadow: none; will-change: auto; }
156
+ .artboard.expanded::after, .artboard.expanded .ab-label, .artboard.expanded .ab-handles, .artboard.expanded .frame-cover { display: none; }
157
+ .artboard.expanded .ab-body { width: 100% !important; height: 100% !important; transform: none !important; border-radius: 0; }
158
+
159
+ .empty { position: absolute; inset: 0; display: grid; place-content: center; justify-items: center; gap: 10px; color: var(--muted); }
160
+ .empty svg { width: 44px; height: 44px; fill: none; color: var(--faint); stroke: currentColor; stroke-width: .9; stroke-linejoin: round; stroke-linecap: round; margin-bottom: 4px; }
161
+ .empty p { margin: 0; font-size: 14px; color: var(--ink); }
162
+ .empty .hint { color: var(--muted); font-size: 12px; }
163
+ .cta { display: inline-flex; align-items: center; gap: 10px; height: 34px; padding: 0 8px 0 14px; border: 0; border-radius: 10px; background: var(--accent); color: var(--on-accent); font-weight: 600; box-shadow: var(--shadow); transition: transform .12s, background .12s; }
164
+ .cta:hover { background: var(--accent-ink); }
165
+ .cta:active { transform: scale(.97); }
166
+ .cta kbd { color: inherit; border-color: rgba(255, 255, 255, .35); }
167
+ .loading { position: absolute; inset: 0; display: grid; place-items: center; }
168
+ .loading i { width: 56px; height: 2px; border-radius: 2px; background: linear-gradient(90deg, transparent, var(--accent), transparent) 0 0 / 200% 100%; animation: shimmer 1s linear infinite; }
169
+
170
+ /* ---- chrome --------------------------------------------------------------- */
171
+ /* Left column: logo + search on top, the sidebar, its tabs below. Folded, the top and bottom pieces stay as two separate pills. */
172
+ :root { --chrome-h: 44px; --edge: 10px; }
173
+ :root { --brand-h: 36px; }
174
+ .dock { position: fixed; z-index: 65; height: var(--chrome-h); display: flex; align-items: center; gap: 2px; padding: 4px; border: 1px solid var(--hair); border-radius: var(--radius); background: var(--panel); backdrop-filter: blur(18px) saturate(1.4); box-shadow: var(--shadow); }
175
+ /* Islands: the logo, search and tabs are separate pieces that stay in place; the tabs join the sidebar when it opens. */
176
+ .brand { position: fixed; z-index: 65; left: var(--edge); top: var(--edge); display: flex; gap: 6px; max-width: calc(var(--vw) - 2 * var(--edge)); }
177
+ .status, .search-pill { border: 1px solid var(--hair); border-radius: var(--radius); background: var(--panel); backdrop-filter: blur(18px) saturate(1.4); box-shadow: var(--shadow); }
178
+ /* One chrome language, chosen by the panels: folded panels leave the pinned chrome floating and frosted over the canvas; an
179
+ open panel makes the chrome part of one solid body (sidebar.js publishes which side is open). */
180
+ :is([data-panels="left"], [data-panels="right"], [data-panels="both"]) :is(.status, .search-pill, .dock, .tab) { background: var(--solid); backdrop-filter: none; }
181
+ :is([data-panels="left"], [data-panels="right"], [data-panels="both"]) .pill { background: var(--solid); backdrop-filter: none; }
182
+ /* The tab row is as wide as the sidebar whether the sidebar is open or folded, and its tabs share that width equally: opening
183
+ the sidebar then changes only the surface behind the labels, never where a label sits. */
184
+ .sidebar-foot { position: fixed; z-index: 65; left: var(--edge); bottom: max(var(--edge), env(safe-area-inset-bottom)); display: flex; flex-direction: column; gap: 8px; width: min(var(--sidebar-w, 300px), calc(var(--vw) - 2 * var(--edge))); max-width: calc(var(--vw) - 2 * var(--edge)); }
185
+ .side-tabs { display: flex; align-items: center; gap: 6px; min-width: 0; border: 1px solid transparent; border-top: 0; border-bottom: 0; container: sidebar-tabs / inline-size; }
186
+ .side-tabs .tab { flex: 1 1 0; }
187
+ /* Opening the sidebar changes the surroundings of the tabs, never their boxes: the row keeps its height, padding and gap, so
188
+ the labels stay exactly where they were and only the surface behind them (and the pill borders) changes. */
189
+ body.has-sidebar .side-tabs { height: var(--brand-h); padding: 0; gap: 6px; border: 1px solid var(--hair); border-top: 0; border-bottom: 0; border-radius: 0 0 var(--radius) var(--radius); background: var(--solid); box-shadow: var(--shadow); }
190
+ body.has-sidebar .tab { border-color: transparent; border-radius: var(--radius-inner); background: transparent; box-shadow: none; backdrop-filter: none; }
191
+ .tool { display: inline-grid; place-items: center; min-width: 32px; height: 32px; padding: 0 7px; border: 0; border-radius: var(--radius-inner); background: transparent; color: var(--muted); text-decoration: none; transition: background .12s, color .12s, transform .12s; }
192
+ .tool:hover:not(:disabled) { background: var(--field-hover); color: var(--ink); }
193
+ .tool:active:not(:disabled) { transform: scale(.94); }
194
+ .tool.on { background: var(--accent-soft); color: var(--accent); }
195
+ .tool:disabled { opacity: var(--disabled); cursor: default; }
196
+ .tool.danger:hover:not(:disabled) { color: var(--bad); background: var(--bad-soft); }
197
+ .tool.mini { min-width: 26px; height: 26px; padding: 0 3px; border-radius: var(--radius-small); }
198
+ .tool.mini .icon { width: 14px; height: 14px; }
199
+ .tool.up .icon { transform: rotate(-90deg); } .tool.down .icon { transform: rotate(90deg); }
200
+ .icon { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; flex: none; }
201
+ .pill { height: 32px; background: var(--panel); backdrop-filter: blur(16px); border: 1px solid var(--hair); box-shadow: var(--shadow); border-radius: var(--radius-inner); }
202
+ .status { display: inline-flex; align-items: center; gap: 8px; min-height: var(--brand-h); min-width: 32px; max-width: min(260px, calc(var(--vw) - 80px)); padding: 0 12px 0 10px; color: var(--ink); transition: background .12s; }
203
+ .logo { display: inline-grid; place-items: center; flex: none; color: var(--faint); transition: color .25s; }
204
+ .logo .icon { width: 18px; height: 18px; stroke-width: 1.7; }
205
+ /* The logo itself is the connection light: brand colour when live, amber while saving, red when attention is needed or offline. */
206
+ .logo[data-kind="live"] { color: var(--accent); }
207
+ .logo[data-kind="saving"] { color: var(--warn); animation: pulse 1s ease-in-out infinite; }
208
+ .logo[data-kind="attention"], .logo[data-kind="offline"] { color: var(--bad); }
209
+ .logo[data-kind="offline"], .logo[data-kind="connecting"] { animation: pulse 1.2s ease-in-out infinite; }
210
+ .status:hover { background: var(--solid); }
211
+ .status .project { font-weight: 700; letter-spacing: .12em; }
212
+ .status:hover { cursor: pointer; }
213
+ .tab { position: relative; display: inline-flex; flex: 1 1 auto; align-items: center; justify-content: center; gap: 6px; min-width: 0; height: var(--brand-h); padding: 0 4px; border: 1px solid var(--hair); border-radius: var(--radius); background: var(--panel); backdrop-filter: blur(18px) saturate(1.4); box-shadow: var(--shadow); color: var(--muted); font-size: 12.5px; white-space: nowrap; transition: background .12s, color .12s; }
214
+ .tab:hover { background: var(--field-hover); color: var(--ink); }
215
+ .tab.on { background: var(--accent-soft); color: var(--accent); }
216
+ .tab .icon { width: 15px; height: 15px; }
217
+ .tab-mark { display: inline-flex; flex: none; align-items: center; }
218
+ .tab-copy { display: flex; flex-wrap: wrap; align-items: center; min-width: 0; column-gap: 3px; }
219
+ .tab .badge { position: static; flex: none; min-width: 14px; padding: 0 4px; font-size: 9.5px; line-height: 14px; pointer-events: none; }
220
+ .tab .tool-label { flex: none; line-height: 16px; }
221
+ /* Icons always precede the full label; only a count may wrap within the text column. Folding does not change density. */
222
+ @container sidebar-tabs (max-width: 340px) {
223
+ .tab { gap: 3px; padding: 0 2px; }
224
+ .tab .icon { width: 13px; height: 13px; }
225
+ }
226
+ @container sidebar-tabs (max-width: 220px) {
227
+ .tab { gap: 2px; padding: 0 1px; font-size: 11px; }
228
+ .tab .icon { width: 12px; height: 12px; }
229
+ }
230
+ .tab .spinner { width: 10px; height: 10px; }
231
+ /* Working: a band sweeps across the tab, over the surface it already has. No spinner, so the label never shifts or runs out of
232
+ room, and the same shape works on the folded pills and inside the open panel's bar. */
233
+ body.agent-busy #open-agent { background-image: linear-gradient(100deg, transparent 32%, color-mix(in srgb, var(--accent) 34%, transparent) 50%, transparent 68%); background-size: 55% 100%; background-repeat: no-repeat; animation: tab-sweep 1.9s linear infinite; }
234
+ @keyframes tab-sweep { from { background-position: -110% 0, 0 0; } to { background-position: 210% 0, 0 0; } }
235
+ .project { white-space: nowrap; font-weight: 500; } /* the brand word, never broken */
236
+ .dot { width: 8px; height: 8px; flex: none; border-radius: 50%; background: var(--faint); transition: background .25s; }
237
+ .dot[data-kind="live"] { background: var(--ok); }
238
+ .dot[data-kind="saving"] { background: var(--warn); animation: pulse 1s ease-in-out infinite; }
239
+ .dot[data-kind="attention"], .dot[data-kind="offline"] { background: var(--bad); }
240
+ .dot[data-kind="offline"], .dot[data-kind="connecting"] { animation: pulse 1.2s ease-in-out infinite; }
241
+
242
+ .dock { right: var(--edge); bottom: max(var(--edge), env(safe-area-inset-bottom)); z-index: 20; gap: 1px; max-width: calc(var(--vw) - 2 * var(--edge)); flex-wrap: wrap; justify-content: flex-end; height: auto; min-height: var(--chrome-h); }
243
+ .dock::-webkit-scrollbar { display: none; }
244
+ .sep { width: 1px; height: 18px; margin: 0 4px; background: var(--hair); flex: none; }
245
+ .zoom { min-width: 50px; height: 32px; padding: 0 4px; border: 0; border-radius: 9px; background: transparent; color: var(--ink); font: 11.5px var(--mono); font-variant-numeric: tabular-nums; }
246
+ .zoom:hover { background: var(--field-hover); }
247
+ @media (pointer: coarse) { .tool { min-width: 40px; height: 40px; } .zoom { height: 40px; } }
248
+
249
+ /* An open panel is one opaque body; the chrome around it follows (see the [data-panels] rules below). Only a floating,
250
+ folded-away surface would be frosted, and no surface is shown in that state. */
251
+ .panel { position: fixed; z-index: 15; display: flex; flex-direction: column; border: 1px solid var(--hair); border-radius: var(--radius); background: var(--solid); box-shadow: var(--shadow); animation: rise .22s var(--ease); }
252
+ @keyframes rise { from { opacity: 0; transform: translateY(6px) scale(.985); } }
253
+ .panel.left { --sidebar-max-height: calc(var(--vh) - 2 * var(--edge) - var(--brand-h) - var(--brand-h) - 8px); left: var(--edge); top: calc(var(--edge) + var(--brand-h) + 8px); bottom: calc(max(var(--edge), env(safe-area-inset-bottom)) + var(--brand-h) - 1px); z-index: 64; width: min(var(--sidebar-w, 300px), calc(var(--vw) - 2 * var(--edge))); border-bottom: 0; border-radius: var(--radius) var(--radius) 0 0; background: var(--solid); backdrop-filter: none; box-shadow: var(--shadow); animation: none; }
254
+ .panel.right { right: var(--edge); top: auto; bottom: calc(max(var(--edge), env(safe-area-inset-bottom)) + var(--dock-h, var(--chrome-h)) + 8px); width: min(var(--inspector-w, 336px), calc(var(--vw) - 2 * var(--edge))); max-height: calc(var(--vh) - var(--dock-h, var(--chrome-h)) - 2 * var(--edge) - 8px); overflow-x: hidden; overflow-y: auto; padding: 10px; gap: 12px; }
255
+ .panel.right > * { min-width: 0; max-width: 100%; }
256
+ /* the grab edge of a resizable panel */
257
+ .resize-handle { position: fixed; z-index: 60; width: 8px; cursor: ew-resize; touch-action: none; border-radius: 4px; }
258
+ .resize-handle::after { content: ""; position: absolute; inset: 24px 3px; border-radius: 2px; background: transparent; transition: background .12s; }
259
+ .resize-handle:hover::after, .resize-handle:focus-visible::after, .resize-handle.dragging::after { background: var(--accent); }
260
+ body.resizing-panel { cursor: ew-resize; user-select: none; }
261
+ body.resizing-panel iframe { pointer-events: none; }
262
+ body.is-expanded .resize-handle, body:has(.scrim:not([hidden])) .resize-handle { display: none; }
263
+ .pane { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
264
+ .pane[hidden] { display: none; }
265
+ .panel-head { position: relative; display: flex; align-items: center; gap: 2px; padding: 6px 8px 4px; }
266
+ .panel-body { flex: 1 1 auto; min-height: 0; overflow-x: hidden; overflow-y: auto; padding: 4px 6px 8px; }
267
+ .panel-foot { display: flex; gap: 12px; margin: 0 8px 8px; padding: 7px 12px; border-radius: var(--radius-inner); background: var(--field); }
268
+ .quiet { padding: 14px 10px; color: var(--muted); font-size: 12px; line-height: 1.5; }
269
+ .search { flex: 1; display: flex; align-items: center; gap: 6px; height: 28px; min-width: 0; padding: 0 8px; border-radius: var(--radius-inner); background: var(--field); color: var(--muted); }
270
+ .search:focus-within { outline-offset: 1px; outline: var(--focus-ring) solid var(--accent); }
271
+ .search input { flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; color: var(--ink); }
272
+ .search input::placeholder { color: var(--faint); }
273
+ .search .icon { width: 13px; height: 13px; }
274
+
275
+ /* sidebar tabs */
276
+ .badge { min-width: 17px; padding: 0 5px; border-radius: 9px; background: var(--accent); color: var(--on-accent); font: 600 10.5px/17px var(--mono); text-align: center; }
277
+ .badge.bad { background: color-mix(in srgb, var(--bad) 80%, #000); color: #fff; }
278
+
279
+ /* layers */
280
+ .board, .node { position: relative; display: flex; align-items: center; gap: 6px; min-height: 27px; padding: 0 8px; border-radius: var(--radius-small); cursor: default; color: var(--ink); outline-offset: -2px; }
281
+ .board { font-size: 12px; }
282
+ .board .icon { width: 14px; height: 14px; color: var(--muted); flex: none; }
283
+ .board .name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
284
+ .board.on .icon { color: var(--accent); }
285
+ /* Reserve at least one row; cap against the sidebar budget so a long tree cannot crush it. */
286
+ .board-list { position: relative; flex: 0 0 auto; min-height: 33px; max-height: min(calc(7 * 27px + 8px), max(33px, calc((var(--sidebar-max-height) - 88px) / 2))); overflow-x: hidden; overflow-y: auto; margin: 0 6px 4px; padding: 3px; border-radius: var(--radius-inner); background: var(--field); }
287
+ .panel .panel-head .caption { font-size: 11px; letter-spacing: .02em; color: var(--muted); padding-left: 4px; white-space: normal; overflow-wrap: anywhere; min-width: 0; }
288
+ .board:hover, .node:hover, .node.hover { background: var(--field); }
289
+ .board-list .board:hover { background: var(--field-hover); }
290
+ .board.on, .board-list .board.on { background: var(--accent-soft); }
291
+ .tree { margin: 1px 0 6px; }
292
+ .node { padding-left: calc(var(--depth) * 12px + 4px); font-size: 12px; }
293
+ .node.on { background: var(--accent-soft); }
294
+ .node.dragging { opacity: .45; }
295
+ .twist { display: grid; place-items: center; width: 16px; height: 16px; color: var(--muted); flex: none; border-radius: 4px; }
296
+ .twist:hover { background: var(--field-hover); color: var(--ink); }
297
+ .twist .icon { width: 10px; height: 10px; transition: transform .12s; }
298
+ .twist.open .icon { transform: rotate(90deg); }
299
+ .twist.none { visibility: hidden; }
300
+ .tagname { font: 11px var(--mono); color: var(--accent); flex: none; }
301
+ .tagname.is-text { color: var(--muted); }
302
+ .snip { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 1; }
303
+ .snip.named { color: var(--ink); }
304
+ .row-rename { flex: 1; min-width: 0; height: 21px; padding: 0 5px; border: 0; border-radius: var(--radius-small); background: var(--solid); color: var(--ink); outline: var(--focus-ring) solid var(--accent); font: inherit; }
305
+ .drop-before::before, .drop-after::after { content: ""; position: absolute; left: 6px; right: 6px; height: 2px; border-radius: 2px; background: var(--accent); }
306
+ .drop-before::before { top: -1px; } .drop-after::after { bottom: -1px; }
307
+ .drop-inside { box-shadow: inset 0 0 0 1.5px var(--accent); }
308
+
309
+ /* menus */
310
+ .menu { position: fixed; z-index: 95; display: flex; flex-direction: column; min-width: min(200px, calc(var(--vw) - 16px)); max-width: min(320px, calc(var(--vw) - 16px)); max-height: calc(var(--vh) - 16px); overflow-y: auto; padding: 5px; border: 1px solid var(--hair); border-radius: var(--radius); background: var(--solid); box-shadow: var(--shadow), 0 0 0 .5px rgba(0, 0, 0, .04); animation: rise-ui .14s var(--ease); transform: scale(var(--ui)); transform-origin: 0 0; outline: none; }
311
+ .menu-item { display: flex; align-items: center; gap: 10px; min-height: 30px; padding: 4px 10px 4px 8px; border: 0; border-radius: var(--radius-small); background: transparent; color: var(--ink); text-align: left; }
312
+ .menu-item:hover:not(:disabled), .menu-item:focus-visible { background: var(--accent-soft); outline: none; }
313
+ .menu-item .icon { width: 15px; height: 15px; color: var(--muted); }
314
+ .menu-item:hover:not(:disabled) .icon, .menu-item:focus-visible .icon { color: var(--accent); }
315
+ .menu-item.danger { color: var(--bad-ink); }
316
+ .menu-item.danger .icon, .menu-item.danger:hover .icon { color: var(--bad); }
317
+ .menu-item.danger:hover:not(:disabled), .menu-item.danger:focus-visible { background: var(--bad-soft); }
318
+ .menu-item:disabled { opacity: var(--disabled); cursor: default; }
319
+ .menu-item.checked { color: var(--accent); font-weight: 600; }
320
+ .menu-item.checked .icon-space::before { content: ""; display: block; width: 6px; height: 6px; margin: 4px; border-radius: 50%; background: var(--accent); }
321
+ .icon-space { width: 15px; flex: none; }
322
+ .menu-label { flex: 1; min-width: 0; white-space: normal; overflow-wrap: anywhere; }
323
+ .menu-keys { flex: none; white-space: nowrap; margin-left: 18px; font: 11px var(--mono); color: var(--muted); }
324
+ .menu-sep { height: 1px; margin: 4px 6px; background: var(--hair); flex: none; }
325
+ .menu-heading { padding: 6px 10px 3px 8px; font-size: 11px; color: var(--muted); }
326
+ .menu-tags { columns: 1; }
327
+ .menu-tags .menu-item { height: 26px; font: 12px var(--mono); }
328
+
329
+ /* activity */
330
+ .filters { gap: 4px; padding: 6px 10px 6px; }
331
+ .change, .diag { display: flex; align-items: flex-start; gap: 9px; width: 100%; padding: 7px 8px; border: 0; border-radius: var(--radius-inner); background: transparent; text-align: left; color: var(--ink); }
332
+ button.change:hover:not(:disabled) { background: var(--field); }
333
+ .change:disabled { opacity: var(--disabled-field); cursor: default; }
334
+ .change .icon, .diag .icon { margin-top: 1px; color: var(--muted); }
335
+ .change.actor-agent .icon { color: var(--accent); }
336
+ .change.actor-file .icon { color: var(--warn); }
337
+ .diag { background: var(--bad-soft); margin-bottom: 4px; }
338
+ .diag .icon { color: var(--bad); }
339
+ .text { flex: 1; min-width: 0; }
340
+ .msg { white-space: normal; overflow-wrap: anywhere; }
341
+ .diag .msg { white-space: normal; }
342
+ .kind { display: inline-block; margin-right: 6px; padding: 0 5px; border-radius: 5px; background: var(--field-hover); color: var(--muted); font-size: 10.5px; font-weight: 600; line-height: 16px; vertical-align: 1px; }
343
+ .kind-undo .kind, .kind-redo .kind { background: var(--accent-soft); color: var(--accent); }
344
+ .kind-delete .kind { background: var(--bad-soft); color: var(--bad); }
345
+ .sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); font-size: 11px; }
346
+ .when { font: 11px var(--mono); color: var(--muted); flex: none; }
347
+ .value { display: inline-flex; gap: 5px; align-items: baseline; font: 11px var(--mono); color: var(--ink); }
348
+ .value i { font-style: normal; color: var(--muted); }
349
+ .value.warn { color: var(--warn); }
350
+ .link-button { flex: none; height: 24px; padding: 0 8px; border: 0; border-radius: var(--radius-small); background: var(--field-hover); color: var(--ink); font-weight: 500; }
351
+ .link-button:hover { background: var(--accent-soft); color: var(--accent); }
352
+
353
+ /* Addressable states and audit activity have different semantics, not two competing replay controls. */
354
+ .history-state { position: relative; cursor: pointer; }
355
+ .history-state.is-current { background: var(--accent-soft); }
356
+ .history-state[aria-disabled="true"] { cursor: default; }
357
+ .history-state[aria-busy="true"] { box-shadow: inset 0 -2px var(--accent); }
358
+ .history-state .icon { flex: none; }
359
+ .history-tag { flex: none; min-width: calc(7ch + 8px); padding: 2px 4px; border-radius: var(--radius-small); background: var(--field); color: var(--muted); font: 10px/16px var(--mono); text-align: center; white-space: nowrap; }
360
+ .history-state.is-current .history-tag { color: var(--accent-ink); }
361
+ .history-caption { padding: 7px 8px; }
362
+ .history-boundary { margin: 6px 8px 10px; color: var(--muted); font-size: 11px; line-height: 1.45; overflow-wrap: anywhere; }
363
+ .history-log { margin-top: 8px; border-radius: var(--radius-inner); background: var(--field); }
364
+ .history-log > summary { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px; cursor: pointer; color: var(--muted); }
365
+ .history-log[open] > summary .icon { transform: rotate(90deg); }
366
+ #activity .sub { overflow: visible; white-space: normal; overflow-wrap: anywhere; text-overflow: clip; }
367
+ .history-foot { display: grid; gap: 4px; }
368
+ .history-counters { display: flex; gap: 12px; flex-wrap: wrap; }
369
+ .history-feedback { min-height: 2.8em; font-size: 11px; line-height: 1.4; overflow-wrap: anywhere; }
370
+ .history-feedback.is-error { color: var(--bad-ink); }
371
+
372
+ /* inspector */
373
+ .group { display: grid; grid-template-columns: minmax(0, 1fr); gap: 7px; }
374
+ .html-presentation { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; min-width: 0; margin: 0; padding: 0; border: 0; }
375
+ .html-presentation:disabled { opacity: var(--disabled); }
376
+ .html-scope-toggle { justify-self: start; height: auto; min-height: 30px; padding: 5px 8px; white-space: normal; text-align: left; }
377
+ .grid2, .box3, .box4 { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--control-min, 140px)), 1fr)); gap: 6px; }
378
+ .geometry-grid, .box3, .box4, .layout-box > .grid2, .appearance-box > .grid2, .typography-controls > .grid2 { --control-min: 120px; }
379
+ /* Selectors wrap as whole fields according to their label/value widths, not a shared numeric-field minimum. */
380
+ .grid2:has(> .pick) { display: flex; flex-wrap: wrap; }
381
+ .grid2:has(> .pick) > * { flex: 1 1 var(--control-min, 140px); }
382
+ .grid2:has(> .pick) > .pick { flex-basis: auto; max-width: 100%; }
383
+ /* layout, spacing, effects: small controls that write Tailwind classes */
384
+ .layout-box, .spacing-box { display: grid; gap: 8px; }
385
+ .pick { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) max-content; align-items: center; gap: 2px 6px; min-width: 0; min-height: 30px; padding: 6px 22px 6px 9px; line-height: 18px; border: 0; border-radius: var(--radius-inner); background: var(--field); color: var(--ink); text-align: left; }
386
+ .pick:hover { background: var(--field-hover); }
387
+ .pick-k { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; font-weight: 600; color: var(--ink); }
388
+ .pick-v { text-align: right; white-space: nowrap; overflow-wrap: normal; color: var(--muted); font: 11.5px/18px var(--mono); }
389
+ .pick-caret { position: absolute; right: 7px; top: calc(50% - 5px); width: 10px; height: 10px; color: var(--muted); transform: rotate(90deg); }
390
+ .variant-dot { position: absolute; top: 4px; right: 4px; width: 5px; height: 5px; border-radius: 50%; background: var(--accent); pointer-events: none; }
391
+ .flow-row { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px; }
392
+ .flow-side { flex: 1 1 105px; min-width: 0; display: grid; gap: 6px; align-content: start; }
393
+ .align-grid { display: grid; grid-template-columns: repeat(3, 20px); grid-auto-rows: 20px; gap: 1px; padding: 3px; border-radius: var(--radius-small); background: var(--field); flex: none; }
394
+ .align-cell { display: grid; place-items: center; padding: 0; border: 0; border-radius: var(--radius-small); background: transparent; color: var(--faint); }
395
+ .align-cell i { width: 4px; height: 4px; border-radius: 50%; background: currentColor; transition: width .12s, height .12s; }
396
+ .align-cell.half { color: var(--muted); }
397
+ .align-cell:hover { background: var(--field-hover); color: var(--ink); }
398
+ .align-cell.on { background: var(--accent-soft); color: var(--accent); }
399
+ .align-cell.on i { width: 10px; height: 4px; border-radius: 2px; }
400
+ .segs-row { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; min-width: 0; }
401
+ .align-controls { justify-content: space-between; }
402
+ .labeled-segs { display: inline-flex; flex: none; align-items: center; gap: 6px; white-space: nowrap; }
403
+ .segs { display: inline-flex; gap: 2px; padding: 2px; border-radius: var(--radius-inner); background: var(--field); }
404
+ .seg-icon { display: grid; place-items: center; width: 26px; height: 24px; padding: 0; border: 0; border-radius: var(--radius-small); background: transparent; color: var(--muted); }
405
+ .seg-icon .icon { width: 14px; height: 14px; }
406
+ .seg-icon:hover { color: var(--ink); }
407
+ .seg-icon.on { background: var(--solid); color: var(--ink); box-shadow: 0 1px 2px rgba(0, 0, 0, .14); }
408
+ .seg-icon.solo { width: 28px; height: 28px; border-radius: var(--radius-small); background: var(--field); }
409
+ .seg-icon.solo.on { background: var(--accent-soft); color: var(--accent); box-shadow: none; }
410
+ .vf { position: relative; display: flex; align-items: center; gap: 3px; min-width: 0; height: 28px; padding: 0 6px 0 0; border-radius: var(--radius-small); background: var(--field); }
411
+ .vf:focus-within, .vf.scrubbing { outline-offset: 1px; outline: var(--focus-ring) solid var(--accent); }
412
+ .vf-k { flex: none; align-self: stretch; display: grid; place-items: center; min-width: 18px; padding-left: 6px; font: 600 10.5px var(--mono); color: var(--muted); cursor: ew-resize; user-select: none; touch-action: none; }
413
+ .vf-k:hover, .scrubbing .vf-k { color: var(--accent); }
414
+ .vf-input { flex: 1; width: 100%; min-width: 0; border: 0; outline: 0; background: transparent; color: var(--ink); font: 11.5px var(--mono); font-variant-numeric: tabular-nums; }
415
+ .vf-input::placeholder { color: var(--faint); }
416
+ .vf-unit, .num-unit { flex: none; font: 10px var(--mono); color: var(--muted); pointer-events: none; user-select: none; }
417
+ .vf-unit[hidden] { display: none; }
418
+ .num-unit { padding-right: 8px; }
419
+ /* Developer-tools box model: nested, non-proportional CSS bands with the actual values on each edge. */
420
+ .boxmodel { --bm-edge: 24px; --bm-field: 24px; position: relative; min-width: 0; container: boxmodel / inline-size; }
421
+ .bm-sq { position: relative; padding: 20px var(--bm-edge); min-width: 0; border: 1px solid #4c4b40; color: #202621; }
422
+ .bm-position { border: 0; color: var(--ink); }
423
+ .bm-margin { background: #bc8c61; border-style: dashed; }
424
+ .bm-border { background: #e3c78f; }
425
+ .bm-padding { background: #c6d7a7; border-style: dashed; }
426
+ .bm-caption { position: absolute; top: 2px; left: 4px; font: 10px/14px var(--font); pointer-events: none; }
427
+ .bm-cell { position: absolute; display: flex; justify-content: center; align-items: center; width: var(--bm-field); height: 18px; min-width: 0; font: 10px/16px var(--mono); }
428
+ .bm-cell.bm-t { top: 1px; left: 50%; transform: translateX(-50%); }
429
+ .bm-cell.bm-b { bottom: 1px; left: 50%; transform: translateX(-50%); }
430
+ .bm-cell.bm-l { left: 0; top: 50%; transform: translateY(-50%); width: var(--bm-field); }
431
+ .bm-cell.bm-r { right: 0; top: 50%; transform: translateY(-50%); width: var(--bm-field); }
432
+ .bm-centre { min-width: 0; }
433
+ .vf.bm-field, .vf.bm-offset, .vf.bm-value { flex: none; width: var(--bm-field); min-height: 18px; height: 18px; padding: 0; border-radius: var(--radius-small); background: transparent; box-shadow: none; color: inherit; }
434
+ .bm-field:hover, .bm-offset:hover, .bm-value:hover { background: #ffffff30; }
435
+ .bm-field:focus-within, .bm-offset:focus-within, .bm-value:focus-within { background: var(--solid); color: var(--ink); }
436
+ .bm-field .vf-input, .bm-offset .vf-input, .bm-value .vf-input { width: 100%; min-width: 0; padding: 0; color: inherit; font: 10px/16px var(--mono); text-align: center; }
437
+ .bm-field .vf-input::placeholder, .bm-offset .vf-input::placeholder, .bm-value .vf-input::placeholder { color: inherit; opacity: 1; }
438
+ .bm-core { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 0 3px; min-width: 0; min-height: 23px; padding: 2px; border: 1px solid #314554; background: #8eaec2; color: #17232b; font: 10px/15px var(--mono); }
439
+ .vf.bm-dimension { width: min(100%, var(--bm-value-width)); min-width: 3ch; }
440
+ .bm-value .vf-input { font-size: inherit; }
441
+ .spacing-box { display: grid; gap: 6px; }
442
+ .bm-unit { position: absolute; top: 4px; right: 5px; color: var(--muted); font: 9px/12px var(--mono); pointer-events: none; }
443
+ @container boxmodel (max-width: 270px) {
444
+ .bm-sq { --bm-edge: 20px; --bm-field: 20px; }
445
+ .bm-cell, .bm-field .vf-input, .bm-offset .vf-input { font-size: 8.5px; }
446
+ }
447
+ /* At phone/200% widths, stagger the side values down the bands instead of hiding or overlapping them. */
448
+ @container boxmodel (max-width: 230px) {
449
+ .bm-sq { --bm-edge: 8px; --bm-field: 24px; }
450
+ .bm-padding { --bm-edge: 24px; }
451
+ .bm-caption { font-size: 8px; }
452
+ .bm-cell.bm-l, .bm-cell.bm-r { top: auto; bottom: 4px; transform: none; }
453
+ .bm-cell, .bm-field .vf-input, .bm-offset .vf-input, .bm-core { font-size: 9px; }
454
+ }
455
+ /* Long signed/fractional values earn rows, not clipping or tiny type; short values keep the reference's compact shape. */
456
+ .bm-expanded-values { padding-top: 76px; }
457
+ .bm-expanded-values > .bm-cell { width: max(var(--bm-field), var(--bm-value-width)); max-width: calc(100% - 4px); }
458
+ .bm-expanded-values > .bm-cell > .vf { width: 100%; }
459
+ .bm-expanded-values > .bm-t { top: 18px; }
460
+ .bm-expanded-values > .bm-l { top: 36px; left: 2px; bottom: auto; transform: none; }
461
+ .bm-expanded-values > .bm-r { top: 54px; right: 2px; bottom: auto; transform: none; }
462
+ .box4 { min-width: 0; flex: 1 1 var(--control-min); }
463
+ .caption.has-variants::after { content: ""; display: inline-block; width: 5px; height: 5px; margin-left: 4px; border-radius: 50%; background: var(--accent); vertical-align: 1px; }
464
+ .quick-attrs { margin-top: 1px; }
465
+ /* code with colors: a highlighted copy under a transparent textarea, identical metrics */
466
+ .code { position: relative; border-radius: var(--radius-inner); background: var(--field); }
467
+ .code-hl, .code-ta { margin: 0; padding: 7px 9px; border: 0; font: 11.5px/1.5 var(--mono); letter-spacing: 0; tab-size: 2; white-space: pre-wrap; overflow-wrap: anywhere; word-break: normal; }
468
+ .code-hl { position: absolute; inset: 0; overflow: hidden; color: var(--ink); pointer-events: none; }
469
+ .code-ta { position: relative; display: block; width: 100%; min-height: 54px; resize: none; outline: 0; background: transparent; color: transparent; caret-color: var(--ink); scrollbar-width: none; border-radius: var(--radius-inner); }
470
+ .code-ta::selection { background: var(--accent-soft); color: transparent; }
471
+ .code-ta::placeholder { color: var(--faint); }
472
+ .code:focus-within { outline-offset: 1px; outline: var(--focus-ring) solid var(--accent); }
473
+ .syn-comment { color: var(--syn-comment); font-style: italic; }
474
+ .syn-atrule { color: var(--syn-atrule); }
475
+ .syn-tag, .syn-id { color: var(--syn-selector); }
476
+ .syn-class { color: var(--syn-class); }
477
+ .syn-pseudo { color: var(--syn-pseudo); }
478
+ .syn-attr { color: var(--syn-string); }
479
+ .syn-prop { color: var(--syn-prop); }
480
+ .syn-number { color: var(--syn-number); }
481
+ .syn-color { color: var(--syn-color); }
482
+ .syn-string { color: var(--syn-string); }
483
+ .syn-func { color: var(--syn-func); }
484
+ .syn-var { color: var(--syn-var); }
485
+ .syn-important { color: var(--syn-important); font-weight: 600; }
486
+ .syn-punct { color: var(--syn-punct); }
487
+ .num { display: flex; align-items: center; height: 30px; border-radius: var(--radius-inner); background: var(--field); overflow: hidden; }
488
+ .num:focus-within, .num.scrubbing { outline-offset: 1px; outline: var(--focus-ring) solid var(--accent); }
489
+ .num-label { width: 26px; text-align: center; font: 600 10.5px var(--mono); color: var(--muted); cursor: ew-resize; user-select: none; touch-action: none; align-self: stretch; display: grid; place-items: center; }
490
+ .num-label:hover, .scrubbing .num-label { color: var(--accent); }
491
+ .num-input, .field { min-width: 0; border: 0; outline: 0; background: transparent; color: var(--ink); }
492
+ .num-input { flex: 1; width: 100%; padding: 0 3px 0 0; font: 12px var(--mono); font-variant-numeric: tabular-nums; }
493
+ .field { width: 100%; padding: 6px 9px; border-radius: var(--radius-inner); background: var(--field); resize: none; display: block; }
494
+ .field:focus { outline-offset: 1px; outline: var(--focus-ring) solid var(--accent); }
495
+ .field.mono, textarea.field { font: 11.5px/1.5 var(--mono); }
496
+ textarea.field { overflow: auto; }
497
+ .field::placeholder { color: var(--faint); }
498
+ .chips { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
499
+ .chip { height: 22px; padding: 0 8px; border: 0; border-radius: 11px; background: var(--field); color: var(--muted); font: 11px var(--mono); }
500
+ .chip:hover { color: var(--ink); background: var(--field-hover); }
501
+ .chip.on { background: var(--accent-soft); color: var(--accent); }
502
+ .note { display: flex; align-items: center; gap: 7px; padding: 5px 5px 5px 9px; border-radius: var(--radius-inner); background: var(--accent-soft); color: var(--ink); font-size: 11.5px; }
503
+ .note .icon { width: 14px; height: 14px; color: var(--accent); }
504
+ .note span { flex: 1; }
505
+ .fold > summary { list-style: none; cursor: pointer; color: var(--muted); font-size: 11.5px; display: inline-flex; align-items: center; gap: 6px; padding: 2px 0; user-select: none; border-radius: 5px; }
506
+ .fold > summary:hover { color: var(--ink); }
507
+ .fold > summary::-webkit-details-marker { display: none; }
508
+ .fold > summary::before { content: ""; width: 5px; height: 5px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(-45deg); transition: transform .12s; }
509
+ .fold[open] > summary::before { transform: rotate(45deg); }
510
+ .fold[open] > summary { margin-bottom: 6px; }
511
+ .fold-count { font: 10.5px var(--mono); color: var(--muted); }
512
+ .caption { font-size: 11px; color: var(--muted); }
513
+ /* Key–value rows share one label column, as wide as the longest label (up to 45%). */
514
+ .rows { display: grid; grid-template-columns: 16px fit-content(45%) minmax(0, 1fr) auto; gap: 5px; }
515
+ .rows > * { grid-column: 1 / -1; min-width: 0; }
516
+ .kv { display: grid; grid-template-columns: subgrid; align-items: center; }
517
+ .kv .k { font: 11px var(--mono); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
518
+ .kv.add > :first-child { grid-column: 1 / 3; }
519
+ .kv.add > :last-child { grid-column: 3 / 5; }
520
+ .kv .field { padding: 4px 7px; }
521
+ .swatch { width: 16px; height: 16px; padding: 0; border: 1px solid var(--hair); border-radius: var(--radius-small); background: none; cursor: pointer; overflow: hidden; }
522
+ .swatch::-webkit-color-swatch-wrapper { padding: 0; } .swatch::-webkit-color-swatch { border: 0; }
523
+ .swatch.plain { background: var(--field); cursor: default; display: inline-block; }
524
+ .swatch.none { visibility: hidden; }
525
+ .classes { display: flex; flex-wrap: wrap; gap: 4px; padding: 5px; border-radius: var(--radius-inner); background: var(--field); min-height: 32px; cursor: text; }
526
+ .classes:focus-within { outline-offset: 1px; outline: var(--focus-ring) solid var(--accent); }
527
+ .cls { position: relative; display: inline-flex; align-items: center; min-height: 20px; max-width: 100%; border-radius: 6px; background: var(--accent-soft); color: var(--accent); font: 11px var(--mono); }
528
+ .cls-name { align-self: stretch; min-width: 0; min-height: 20px; padding: 0 7px; border: 0; border-radius: inherit; background: transparent; color: inherit; font: inherit; text-align: left; white-space: normal; overflow-wrap: anywhere; } /* a long arbitrary class wraps inside its chip instead of being cut */
529
+ .cls-x { position: absolute; right: 2px; top: 2px; display: none; place-items: center; width: 16px; height: 16px; margin: 0; padding: 0; border: 0; border-radius: 4px; background: color-mix(in srgb, var(--solid) 88%, var(--accent)); color: inherit; opacity: .9; } /* an overlay: showing it must never reflow the chips (a reflow can move the chip out from under the pointer) */
530
+ .cls-x .icon { width: 10px; height: 10px; }
531
+ .cls:hover .cls-x, .cls:focus-within .cls-x { display: grid; }
532
+ .cls-x:hover { opacity: 1; background: var(--bad-soft); color: var(--bad); }
533
+ .cls:has(.cls-name:hover) { background: color-mix(in srgb, var(--accent) 22%, transparent); }
534
+ .cls-color { width: 8px; height: 8px; margin-left: 6px; margin-right: -3px; flex: none; border-radius: 2px; box-shadow: inset 0 0 0 1px var(--hair); }
535
+ .cls[data-kind="custom"] { background: var(--field-hover); color: var(--ink); }
536
+ .cls[data-kind="custom"] .cls-name { text-decoration: underline dotted var(--muted); text-underline-offset: 3px; }
537
+ .cls[data-kind="marker"] { background: var(--field-hover); color: var(--muted); }
538
+ .cls[data-kind="unknown"] { background: var(--bad-soft); color: var(--bad); }
539
+ .cls[data-kind="unknown"] .cls-name { text-decoration: underline wavy color-mix(in srgb, var(--bad) 70%, transparent); text-underline-offset: 3px; }
540
+ .cls-edit { height: 20px; min-width: 4ch; max-width: 100%; padding: 0 6px; border: 0; border-radius: var(--radius-small); outline: var(--focus-ring) solid var(--accent); background: var(--solid); color: var(--ink); font: 11px var(--mono); }
541
+ .cls-input { flex: 1; min-width: 6em; height: 20px; border: 0; outline: 0; background: transparent; color: var(--ink); font: 11.5px var(--mono); }
542
+ .cls-input::placeholder { color: var(--faint); }
543
+ .suggest { transform: scale(var(--ui)); transform-origin: 0 0; position: fixed; z-index: 96; width: min(300px, calc(var(--vw) - 12px)); padding: 4px; border: 1px solid var(--hair); border-radius: 10px; background: var(--solid); box-shadow: var(--shadow); font: 11.5px var(--mono); }
544
+ .suggest-item { display: flex; align-items: center; gap: 8px; height: 26px; padding: 0 8px; border-radius: 6px; color: var(--ink); cursor: pointer; }
545
+ .suggest-item.on { background: var(--accent-soft); }
546
+ .suggest-mark { width: 8px; height: 8px; flex: none; border-radius: 2px; background: var(--field-hover); box-shadow: inset 0 0 0 1px var(--hair); }
547
+ .suggest-mark.variant { border-radius: 50%; background: transparent; box-shadow: inset 0 0 0 1.5px var(--accent); }
548
+ .suggest-mark.used { background: var(--accent); box-shadow: none; }
549
+ .suggest-name { flex: none; max-width: 60%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
550
+ .suggest-hint { flex: 1; min-width: 0; color: var(--muted); font-size: 10.5px; text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
551
+ .crumbs { display: flex; align-items: center; flex-wrap: wrap; gap: 2px; margin: -2px 0 -4px; }
552
+ .crumb { display: inline-flex; align-items: center; gap: 5px; max-width: 100%; min-height: 22px; padding: 0 6px; border: 0; border-radius: 6px; background: transparent; color: var(--muted); font: 11px var(--mono); }
553
+ .crumb.home { font: 11.5px var(--font); padding-left: 4px; }
554
+ .crumb.home { min-width: 0; }
555
+ .crumb.home span { white-space: normal; overflow-wrap: anywhere; max-width: 100%; }
556
+ .crumb .icon { width: 13px; height: 13px; }
557
+ .crumb:hover { background: var(--field); color: var(--ink); }
558
+ .crumb-sep { width: 4px; height: 4px; border-right: 1.2px solid var(--faint); border-top: 1.2px solid var(--faint); transform: rotate(45deg); margin: 0 2px; }
559
+ .node-head { display: flex; align-items: center; gap: 6px; min-width: 0; }
560
+ .tag { display: inline-flex; align-items: center; gap: 4px; height: 24px; padding: 0 6px 0 8px; border: 0; border-radius: var(--radius-small); background: var(--accent-soft); color: var(--accent); font: 600 12px var(--mono); }
561
+ .tag:hover { background: color-mix(in srgb, var(--accent) 22%, transparent); }
562
+ .tag.static { padding-right: 8px; }
563
+ .tag .caret { width: 10px; height: 10px; transform: rotate(90deg); }
564
+ .ident { min-width: 0; height: 24px; padding: 0 6px; border: 0; border-radius: var(--radius-small); background: transparent; font: 11px var(--mono); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
565
+ .ident:hover { background: var(--field); color: var(--ink); }
566
+ .ident-field { display: inline-flex; align-items: center; min-width: 0; height: 24px; padding: 0 6px 0 7px; border-radius: var(--radius-small); color: var(--muted); font: 11px var(--mono); cursor: text; }
567
+ .ident-field:hover { background: var(--field); }
568
+ .ident-field:focus-within { background: var(--field); outline-offset: 1px; outline: var(--focus-ring) solid var(--accent); color: var(--ink); }
569
+ .ident-hash { flex: none; }
570
+ .ident-input { min-width: 0; max-width: 100%; padding: 0; border: 0; outline: 0; background: transparent; color: var(--ink); font: inherit; }
571
+ .values { display: flex; flex-wrap: wrap; gap: 4px 12px; padding: 7px 9px; border-radius: var(--radius-inner); background: var(--field); }
572
+ .count { display: flex; align-items: baseline; gap: 8px; color: var(--muted); }
573
+ .count b { font: 600 20px var(--mono); color: var(--ink); }
574
+ .actions { display: flex; flex-wrap: wrap; gap: 2px; margin: 0 -4px; }
575
+ .actions.smart-bar { flex-shrink: 0; flex-wrap: nowrap; overflow: hidden; margin: 0; }
576
+ .tool[hidden] { display: none; }
577
+
578
+ /* ---- floating hints ------------------------------------------------------- */
579
+ /* Floating surfaces follow the theme like panels and menus: never an inverted white box in dark mode. */
580
+ .tip { transform-origin: 0 0; position: fixed; z-index: 100; width: max-content; max-width: min(280px, calc(var(--vw) - 12px)); padding: 4px 9px; border: 1px solid var(--hair); border-radius: var(--radius-small); background: var(--solid); color: var(--ink); box-shadow: var(--shadow); font-size: 11.5px; line-height: 1.35; pointer-events: none; white-space: pre-wrap; overflow-wrap: anywhere; opacity: 0; transform: scale(var(--ui)) translateY(2px); transition: opacity .12s, transform .12s; }
581
+ .tip.show { opacity: 1; transform: scale(var(--ui)); }
582
+ .tip.code { max-width: min(380px, calc(var(--vw) - 12px)); padding: 7px 10px 8px; white-space: normal; }
583
+ .tip-title { display: block; margin-bottom: 4px; font: 600 11px var(--mono); color: var(--ink); white-space: normal; overflow-wrap: anywhere; }
584
+ .tip-code { margin: 0; font: 11px/1.5 var(--mono); color: var(--muted); white-space: pre-wrap; overflow-wrap: anywhere; max-height: calc(var(--vh) * 0.5); overflow: hidden; }
585
+ .toast { position: fixed; z-index: 90; left: 50%; top: calc(var(--edge) + 2px); display: flex; align-items: center; gap: 10px; max-width: min(540px, calc(var(--vw) - 24px)); padding: 6px 6px 6px 14px; transform: translateX(-50%); border: 1px solid var(--hair); border-radius: 12px; background: var(--solid); color: var(--ink); box-shadow: var(--shadow); }
586
+ .toast.pop { animation: rise .2s var(--ease); }
587
+ .toast-msg { flex: 1; min-width: 0; }
588
+ .toast-action { flex: none; height: 26px; padding: 0 10px; border: 0; border-radius: var(--radius-small); background: var(--accent-soft); color: var(--accent-ink); font-weight: 600; }
589
+ .toast-action:hover { background: color-mix(in srgb, var(--accent) 24%, transparent); }
590
+ .toast-close { flex: none; display: grid; place-items: center; width: 24px; height: 24px; padding: 0; border: 0; border-radius: var(--radius-small); background: transparent; color: inherit; opacity: .6; }
591
+ .toast-close:hover { opacity: 1; background: var(--field-hover); }
592
+ .toast-close .icon { width: 13px; height: 13px; }
593
+
594
+ .expanded-bar { position: fixed; z-index: 60; top: 10px; left: 50%; display: flex; align-items: center; gap: 4px; padding: 3px; transform: translateX(-50%); border: 1px solid var(--hair); border-radius: var(--radius); background: var(--panel); backdrop-filter: blur(18px); box-shadow: var(--shadow); animation: rise .2s var(--ease); }
595
+ .expanded-bar.moved { transform: none; }
596
+ .expanded-bar.dragging { cursor: grabbing; box-shadow: 0 6px 24px rgba(0, 0, 0, .3); }
597
+ .expanded-back { display: inline-flex; align-items: center; gap: 8px; min-height: 30px; max-width: calc(var(--vw) * 0.6); padding: 0 12px 0 9px; border: 0; border-radius: 10px; background: transparent; color: var(--ink); font-weight: 500; cursor: grab; touch-action: none; user-select: none; }
598
+ .expanded-back:hover { background: var(--field-hover); }
599
+ .expanded-back:active, .expanded-bar.dragging .expanded-back { cursor: grabbing; }
600
+ .expanded-back .icon { width: 15px; height: 15px; flex: none; color: var(--muted); }
601
+ .expanded-back #expanded-title { white-space: normal; overflow-wrap: anywhere; }
602
+ body.bar-dragging iframe { pointer-events: none; }
603
+ body.bar-dragging { user-select: none; }
604
+ /* In full screen only the name/back control stays. */
605
+
606
+ /* open / save project */
607
+ .project-dialog { width: min(560px, 100%); display: grid; gap: 10px; }
608
+ .image-history { display: grid; gap: 4px; min-width: 0; }
609
+ .image-history .field { width: 100%; min-width: 0; text-overflow: ellipsis; }
610
+ .image-review { margin: 0; }
611
+ .image-review legend { padding: 0 4px; font-weight: 600; }
612
+ .image-review label { display: flex; align-items: flex-start; gap: 8px; margin: 10px 0; }
613
+ .image-review input[type="checkbox"] { flex: none; margin-top: 3px; accent-color: var(--accent); }
614
+ .project-hint { margin: 0; color: var(--muted); font-size: 12px; line-height: 1.5; }
615
+ .file-picker { display: grid; gap: 8px; min-width: 0; }
616
+ .file-picker-path { display: flex; align-items: center; gap: 6px; min-width: 0; }
617
+ .file-picker-path .field { flex: 1; min-width: 0; }
618
+ .file-picker-path .tool { flex: none; }
619
+ .file-picker[aria-busy="true"] .dir-list { opacity: .6; }
620
+ .file-picker-status { min-height: 3em; margin: 0; overflow-wrap: anywhere; }
621
+ .dir-list { display: grid; align-content: start; gap: 1px; height: 260px; overflow-y: auto; padding: 4px; border-radius: 10px; background: var(--field); }
622
+ .dir { display: flex; align-items: center; gap: 9px; height: 30px; padding: 0 9px; border: 0; border-radius: var(--radius-small); background: transparent; color: var(--ink); text-align: left; }
623
+ .dir:hover, .dir:focus-visible { background: var(--field-hover); }
624
+ .dir .icon { width: 15px; height: 15px; color: var(--muted); flex: none; }
625
+ .dir.is-project .icon { color: var(--accent); }
626
+ .dir-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
627
+ .dir-tag { padding: 0 7px; border-radius: 6px; background: var(--accent-soft); color: var(--accent-ink); font-size: 10.5px; line-height: 18px; }
628
+ .dir-empty { padding: 18px 10px; color: var(--muted); }
629
+ .project-status { min-width: 0; min-height: 3em; overflow-wrap: anywhere; }
630
+ /* Anchored to the top so a longer message or recovery box grows downward and never moves the controls above it. */
631
+ #project { place-items: start center; padding-top: min(calc(var(--vh) * 0.14), 120px); }
632
+ #project .dialog-foot { align-items: flex-start; padding-bottom: 0; }
633
+ .project-go { height: 32px; min-width: 156px; padding: 0 14px; }
634
+ .project-go:disabled { opacity: var(--disabled); cursor: default; }
635
+ /* Read-only external-file recovery (picker, launcher and diagnostics). */
636
+ .project-recovery:empty, .project-revision:empty { display: none; }
637
+ .project-revision { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; }
638
+ .resource-access { min-width: 0; max-width: 100%; padding: 12px; border: 1px solid var(--hair); border-radius: 10px; background: var(--field); color: var(--ink); font-size: 12px; line-height: 1.5; overflow-wrap: anywhere; }
639
+ .resource-access h3 { margin: 0 0 8px; font-size: 13px; }
640
+ .resource-access p { margin: 6px 0; }
641
+ .resource-access code { font: 11px var(--mono); }
642
+ .resource-access :focus-visible { outline: var(--focus-ring) solid var(--accent); outline-offset: 2px; }
643
+ /* help */
644
+ .scrim { position: fixed; inset: 0; z-index: 80; display: grid; place-items: center; padding: 16px; background: rgba(10, 10, 8, .28); backdrop-filter: blur(2px); animation: fade .15s ease-out; }
645
+ @keyframes fade { from { opacity: 0; } }
646
+ .dialog { width: min(720px, 100%); max-height: calc(var(--vh) - 32px); overflow-y: auto; padding: 16px 18px 14px; border: 1px solid var(--hair); border-radius: 16px; background: var(--solid); box-shadow: var(--shadow); animation: rise .2s var(--ease); }
647
+ .dialog-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
648
+ .dialog h2 { margin: 0; font-size: 15px; font-weight: 600; }
649
+ /* Settings: one block per section, the divider between blocks, so nothing is glued to a line and the space above and below it
650
+ is the same everywhere. */
651
+ .settings-block { padding: 12px 0; }
652
+ .settings-block + .settings-block { border-top: 1px solid var(--hair); }
653
+ .settings-row { display: flex; align-items: center; gap: 12px; }
654
+ .settings-label { min-width: 96px; color: var(--muted); }
655
+ .settings-row { flex-wrap: wrap; }
656
+ #help .dialog { width: min(560px, 100%); min-width: 0; }
657
+ #help .segmented { flex-wrap: wrap; max-width: 100%; }
658
+ .settings-output { display: grid; gap: 8px; min-width: 0; }
659
+ .settings-field { display: flex; align-items: stretch; gap: 6px; min-width: 0; }
660
+ .settings-field input { flex: 1 1 auto; min-width: 0; }
661
+ .settings-field .tool { flex: none; min-width: 34px; }
662
+ .settings-picker { display: grid; gap: 6px; min-width: 0; padding: 8px; border: 1px solid var(--hair); border-radius: var(--radius-inner); background: var(--field); }
663
+ .settings-picker[hidden] { display: none; }
664
+ .settings-picker .dir-list { height: 180px; }
665
+ .settings-picker > .tool { justify-self: end; }
666
+ .settings-output h3 { margin: 0 0 4px; font-size: 13px; font-weight: 600; }
667
+ .settings-path { display: grid; gap: 3px; min-width: 0; }
668
+ .settings-path code { font: 11px var(--mono); overflow-wrap: anywhere; }
669
+ .settings-field input { width: 100%; min-width: 0; padding: 7px 9px; border: 1px solid var(--hair); border-radius: var(--radius-inner); color: var(--ink); background: var(--field); font: inherit; }
670
+ .settings-output .caption { margin: 0; overflow-wrap: anywhere; }
671
+ .settings-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px; margin-top: 4px; }
672
+ .settings-actions .tool { width: auto; height: auto; min-height: 30px; padding: 5px 9px; white-space: normal; }
673
+ /* Buttons that look like buttons: the one that saves is filled, the others are quiet fields. */
674
+ .settings-actions .tool { background: var(--field-hover); color: var(--ink); font-weight: 500; }
675
+ .settings-actions .tool:hover:not(:disabled) { background: var(--accent-soft); color: var(--accent); }
676
+ .settings-actions .tool.primary { background: var(--accent); color: var(--on-accent); }
677
+ .settings-actions .tool.primary:hover:not(:disabled) { background: var(--accent-ink); color: var(--on-accent); }
678
+ .settings-output :disabled { opacity: var(--disabled); cursor: default; }
679
+ .settings-status { min-height: 1.5em; }
680
+ .settings-row:last-child { border-bottom: 0; }
681
+ .shortcut-grid { columns: 2 260px; column-gap: 28px; }
682
+ .shortcut-group { break-inside: avoid; margin-bottom: 14px; }
683
+ .shortcut-group h3 { margin: 0 0 6px; font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
684
+ .shortcut-group dl { display: grid; grid-template-columns: minmax(0, 1fr) max-content; gap: 4px 12px; margin: 0; }
685
+ .shortcut-group dt { min-width: 0; color: var(--ink); overflow-wrap: anywhere; }
686
+ .shortcut-group dd { margin: 0; font: 11px var(--mono); color: var(--muted); text-align: right; white-space: nowrap; }
687
+ /* Narrow screens: a name and its keys are one unit stacked on two lines, so a long key list never squeezes every name. */
688
+ html.w440 .shortcut-group dl { grid-template-columns: minmax(0, 1fr); gap: 0; }
689
+ html.w440 .shortcut-group dt { padding-top: 5px; }
690
+ html.w440 .shortcut-group dd { text-align: left; padding-bottom: 5px; white-space: normal; }
691
+ .dialog-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding-top: 12px; border-top: 1px solid var(--hair); }
692
+ .segmented { display: inline-flex; gap: 2px; padding: 2px; border-radius: 9px; background: var(--field); }
693
+ .seg { display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 10px; border: 0; border-radius: var(--radius-small); background: transparent; color: var(--muted); }
694
+ .seg .icon { width: 14px; height: 14px; }
695
+ .seg:hover { color: var(--ink); }
696
+ .seg.on { background: var(--solid); color: var(--ink); box-shadow: 0 1px 2px rgba(0, 0, 0, .12); }
697
+
698
+ /* ---- search pill, agent chip ---------------------------------------------- */
699
+ /* The search pill is a chrome pill like the DIVLET button and the tabs, so it carries the same frame and radius even though it
700
+ is also a `.tool` (whose reset would otherwise leave it borderless and squarer). */
701
+ /* Also a `.tool`, whose reset is transparent: the search pill is a chrome pill like the DIVLET button, the tabs and the dock. */
702
+ .search-pill { display: inline-flex; align-items: center; gap: 7px; height: var(--brand-h); padding: 0 8px 0 11px; border: 1px solid var(--hair); border-radius: var(--radius); background: var(--panel); backdrop-filter: blur(18px) saturate(1.4); box-shadow: var(--shadow); color: var(--muted); }
703
+ .search-pill .icon { width: 14px; height: 14px; }
704
+ .search-pill kbd { font-size: 10.5px; padding: 0 5px; line-height: 17px; }
705
+ .search-pill:hover { color: var(--ink); background: var(--solid); }
706
+ .spinner { width: 12px; height: 12px; flex: none; border-radius: 50%; border: 1.6px solid var(--accent-soft); border-top-color: var(--accent); animation: spin .8s linear infinite; }
707
+ @keyframes spin { to { transform: rotate(360deg); } }
708
+
709
+ /* ---- Ask Pi --------------------------------------------------------------- */
710
+ /* the context of a request is a path, like the layers tree: artboard › container › element */
711
+ .ask-about { display: flex; align-items: center; flex-wrap: wrap; gap: 2px; min-width: 0; margin: 0; }
712
+ .ask-about .crumb { height: 24px; }
713
+ .crumbs-bar { position: fixed; z-index: 14; bottom: max(var(--edge), env(safe-area-inset-bottom)); min-height: var(--chrome-h); display: flex; align-items: flex-end; justify-content: center; margin: 0; pointer-events: none; }
714
+ .crumbs-bar.cramped { visibility: hidden; }
715
+ .crumbs-track { display: flex; align-items: center; flex-wrap: wrap; justify-content: center; gap: 2px; max-width: 100%; padding: 3px 5px; border: 1px solid var(--hair); border-radius: 10px; background: var(--panel); backdrop-filter: blur(16px); box-shadow: var(--shadow); pointer-events: auto; }
716
+ .crumbs-bar .crumb { height: 24px; flex: none; max-width: 100%; }
717
+ .crumbs-bar .crumb.home { flex: 0 1 auto; min-width: 0; }
718
+ .crumb.current { background: var(--field); color: var(--ink); }
719
+ .ask-more { padding: 0 6px; font: 11px var(--mono); color: var(--muted); }
720
+ .ask-input { width: 100%; min-height: 40px; max-height: 220px; padding: 9px 11px; border: 0; border-radius: var(--radius-inner); background: var(--field); color: var(--ink); font: 13.5px/1.45 var(--font); resize: none; outline: none; }
721
+ .ask-input:focus { outline-offset: 1px; outline: var(--focus-ring) solid var(--accent); }
722
+ .ask-input::placeholder { color: var(--faint); }
723
+ .ask-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px; }
724
+
725
+ /* The session that answers this board, when the host can change it: two chips, no labels — a model and a thinking level read as
726
+ their own values. An MCP client cannot offer this, so there the row is absent rather than disabled. */
727
+ .agent-session { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; min-width: 0; flex: 0 1 auto; }
728
+ .agent-session[hidden] { display: none; }
729
+ .agent-chip { max-width: 100%; height: 24px; min-width: 0; padding: 0 9px; border: 0; border-radius: 999px; background: var(--field); color: var(--muted); font: 10.5px var(--mono); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
730
+ .agent-chip:hover, .agent-chip:focus-visible { background: var(--field-hover); color: var(--ink); }
731
+ .agent-settings-error { margin-top: 4px; padding: 5px 8px; border-radius: var(--radius-small); background: var(--field); color: var(--bad); font-size: 11.5px; line-height: 1.4; }
732
+ .ask-hint { flex: 1 1 140px; min-width: 0; white-space: normal; color: var(--muted); font-size: 11.5px; line-height: 1.4; }
733
+ .ask-hint.busy { color: var(--accent); }
734
+ .ask-scope { display: flex; flex-wrap: wrap; gap: 2px 6px; align-items: baseline; padding: 0 2px; font-size: 11.5px; line-height: 1.4; color: var(--muted); }
735
+ .ask-scope-k { flex: none; font-weight: 600; }
736
+ .ask-scope-v { display: inline-flex; align-items: baseline; gap: 4px; min-width: 0; max-width: 100%; color: var(--ink); }
737
+ /* The frame name may be long; it is the one part of the scope line that is cut. What follows it never is. */
738
+ .ask-scope-frame { min-width: 3ch; max-width: 18ch; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
739
+ .ask-scope-sep, .ask-scope-tail { flex: none; white-space: nowrap; }
740
+ .ask-suggest { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 12px 8px; }
741
+ .ask-suggest[hidden] { display: none; }
742
+ .ask-idea { flex: 0 1 auto; max-width: 100%; min-height: 28px; padding: 4px 10px; border: 0; border-radius: 14px; background: var(--field); color: var(--muted); font: 11.5px/1.35 var(--font); white-space: normal; overflow-wrap: anywhere; text-align: left; }
743
+ .ask-idea:hover { background: var(--field-hover); color: var(--ink); }
744
+ .node-name { font-size: 12px; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; padding: 0 2px; }
745
+ .ask-hint .ask-copy { vertical-align: middle; margin-left: 4px; }
746
+ .ask-stop { color: var(--bad); }
747
+ .ask-stop .icon { width: 12px; height: 12px; }
748
+ .ask-send { flex: none; align-self: flex-end; margin-left: auto; height: 30px; padding: 0 10px 0 12px; gap: 6px; }
749
+ .ask-send .icon { width: 14px; height: 14px; }
750
+ .ask-send:disabled { opacity: var(--disabled); cursor: default; transform: none; }
751
+
752
+ /* ---- the Agent tab: conversation above, composer below ---------------------- */
753
+ /* The same plaque lives above the composer or, when folded, in the tab footer. */
754
+ .agent-status-dock { display: flex; flex-direction: column; min-height: 0; max-height: calc(var(--vh) * 0.4); }
755
+ .agent-status-dock:empty { display: none; }
756
+ .agent-status-dock > .agent-status { margin: 0; min-height: 0; grid-template-rows: minmax(0, 1fr) minmax(0, 1fr) max-content; border: 1px solid var(--hair); border-radius: var(--radius); background: linear-gradient(var(--accent-soft), var(--accent-soft)), var(--solid); box-shadow: var(--shadow); }
757
+ .agent-status-dock .agent-status-text { grid-column: 1 / -1; align-self: stretch; min-height: 0; overflow-y: auto; }
758
+ .agent-status-dock .agent-status > .spinner { grid-row: 3; align-self: center; margin-top: 0; }
759
+ .agent-status { display: grid; grid-template-columns: 12px repeat(3, minmax(0, 1fr)) max-content; grid-template-rows: auto auto max-content; gap: 4px 6px; align-items: start; margin: 0 8px 6px; padding: 7px 12px; border-radius: var(--radius-inner); background: var(--accent-soft); color: var(--accent); font-size: 12px; font-weight: 500; }
760
+ .agent-status[hidden] { display: none; }
761
+ .agent-status > .spinner { grid-column: 1; grid-row: 1; margin-top: 3px; }
762
+ .agent-status-text { grid-column: 2 / 6; grid-row: 1 / 3; min-width: 0; white-space: normal; overflow-wrap: anywhere; }
763
+ .agent-status-time { grid-column: 2 / 5; grid-row: 3; align-self: center; justify-self: end; font: 11px var(--mono); color: var(--muted); white-space: nowrap; }
764
+ .agent-status > .ask-stop { grid-column: 5; grid-row: 3; align-self: end; justify-self: end; }
765
+ .chat-list { flex: 1 1 auto; min-height: 0; overflow-x: hidden; overflow-y: auto; overflow-anchor: none; display: flex; flex-direction: column; gap: 2px; padding: 8px; }
766
+ .chat-list > * { flex-shrink: 0; }
767
+ /* Auto margin absorbs spare space, but disappears on overflow so the oldest message stays reachable. */
768
+ .chat-list > :first-child { margin-top: auto; }
769
+ .agent-composer { display: grid; gap: 8px; margin: 0 8px 8px; padding: 8px; border-radius: var(--radius-inner); background: var(--field); }
770
+ /* A dragged-narrow sidebar (sidebar.js sets data-sidebar): the same content, less air, so nothing is pushed off the edge. */
771
+ html[data-sidebar="narrow"] .agent-composer, html[data-sidebar="tiny"] .agent-composer { margin: 0 6px 6px; padding: 6px; gap: 6px; }
772
+ html[data-sidebar="tiny"] .agent-composer { margin: 0 4px 4px; padding: 5px; }
773
+ html[data-sidebar="narrow"] .bubble.from-user, html[data-sidebar="tiny"] .bubble.from-user { margin-left: 8px; }
774
+ html[data-sidebar="tiny"] .bubble { padding: 6px 8px 7px; }
775
+ html[data-sidebar="tiny"] .ask-hint { font-size: 11px; }
776
+ html[data-sidebar="tiny"] .chat-list { padding: 0 1px; }
777
+ .agent-composer .ask-input { background: var(--solid); }
778
+ .agent-composer .ask-input:disabled { opacity: var(--disabled-field); cursor: not-allowed; }
779
+
780
+ /* ---- palette -------------------------------------------------------------- */
781
+ .palette-scrim { place-items: start center; padding-top: min(calc(var(--vh) * 0.14), 120px); }
782
+ .palette { display: flex; flex-direction: column; width: min(620px, 100%); max-height: min(560px, calc(var(--vh) - 32px)); padding: 0; overflow: hidden; }
783
+ .palette-field { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--hair); color: var(--muted); }
784
+ .palette-field .icon { width: 17px; height: 17px; }
785
+ .palette-input { flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; color: var(--ink); font-size: 15px; }
786
+ .palette-input::placeholder { color: var(--faint); }
787
+ .palette-list { flex: 1; min-height: 0; overflow-y: auto; padding: 6px; }
788
+ .palette-group { padding: 8px 10px 4px; font-size: 11px; font-weight: 600; letter-spacing: .03em; color: var(--muted); }
789
+ .palette-item { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 34px; padding: 4px 10px; border: 0; border-radius: var(--radius-small); background: transparent; color: var(--ink); text-align: left; }
790
+ .palette-item .icon { width: 15px; height: 15px; color: var(--muted); }
791
+ .palette-item.on { background: var(--accent-soft); }
792
+ .palette-item.on .icon, .palette-item.pal-ask .icon { color: var(--accent); }
793
+ .palette-item.pal-ask .palette-label { color: var(--accent-ink); font-weight: 500; }
794
+ .palette-label { flex: 1; min-width: 0; white-space: normal; overflow-wrap: anywhere; }
795
+ .palette-hint { flex: none; font: 11px var(--mono); color: var(--muted); max-width: 40%; white-space: normal; overflow-wrap: anywhere; text-align: right; }
796
+ .palette-tag { font-size: 10.5px; color: var(--muted); flex: none; }
797
+ .palette-foot { display: flex; gap: 14px; padding: 7px 14px; border-top: 1px solid var(--hair); color: var(--muted); font-size: 11px; }
798
+
799
+ /* ---- history: conversation and collapsed steps ---------------------------- */
800
+ .times { margin-left: 6px; padding: 0 5px; border-radius: 5px; background: var(--field-hover); color: var(--muted); font: 600 10.5px/16px var(--mono); }
801
+ .bubble { display: grid; gap: 4px; margin: 4px 0 6px; padding: 8px 10px 9px; border-radius: var(--radius-inner); background: var(--field); }
802
+ .bubble.from-user { margin-left: 18px; background: light-dark(#e4edf8, #182b45); }
803
+ .bubble.from-agent { margin-right: 10px; border: 1px solid var(--hair); background: var(--solid); }
804
+ .bubble.flash { animation: glow 1.2s var(--ease); }
805
+ @keyframes glow { 0% { box-shadow: 0 0 0 3px var(--accent); } 100% { box-shadow: 0 0 0 3px transparent; } }
806
+ .bubble-head { display: flex; flex-wrap: nowrap; align-items: center; gap: 6px; min-width: 0; font-size: 11.5px; }
807
+ .bubble-head .icon { width: 14px; height: 14px; color: var(--muted); }
808
+ .from-agent .bubble-head .icon { color: var(--accent); }
809
+ /* One line: a narrow panel must not turn the context label into a block of wrapped text. The whole label is on hover. */
810
+ .bubble-about { flex: 0 1 auto; min-width: 0; max-width: none; min-height: 20px; padding: 0 6px; border: 0; border-radius: 6px; background: var(--solid); color: var(--muted); font: 10.5px var(--mono); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: left; }
811
+ .bubble-about:hover:not(:disabled) { color: var(--accent); }
812
+ .bubble-about:disabled { cursor: default; opacity: var(--disabled-field); }
813
+ .rich { font-size: 12.5px; line-height: 1.5; overflow-wrap: anywhere; }
814
+ .rich.clamped { display: -webkit-box; -webkit-line-clamp: 6; -webkit-box-orient: vertical; overflow: hidden; }
815
+ .rich code { padding: 0 4px; border-radius: 4px; background: var(--field-hover); font: 11.5px var(--mono); }
816
+ .bubble .more { justify-self: start; height: 22px; font-size: 11.5px; }
817
+ .change.flash { animation: glow 1.2s var(--ease); }
818
+
819
+ /* ---- components ----------------------------------------------------------- */
820
+ .tagname.is-instance { display: inline-flex; align-items: center; gap: 3px; color: var(--component); }
821
+ .tagname.is-instance .icon { width: 10px; height: 10px; }
822
+ .node .component-dot { width: 11px; height: 11px; color: var(--component); }
823
+ .tag.instance { display: inline-flex; align-items: center; gap: 5px; background: var(--component-soft); color: var(--component); }
824
+ .tag.instance .icon { width: 11px; height: 11px; }
825
+ .component-mark { display: inline-flex; align-items: center; gap: 4px; height: 24px; padding: 0 7px; border: 0; border-radius: var(--radius-small); background: var(--component-soft); color: var(--component); font-size: 11px; font-weight: 600; }
826
+ .component-mark .icon { width: 12px; height: 12px; }
827
+ .component-mark:hover { background: color-mix(in srgb, var(--component) 26%, transparent); }
828
+ .instance-head { display: flex; align-items: center; gap: 7px; min-width: 0; padding: 6px 6px 6px 9px; border-radius: var(--radius-inner); background: var(--component-soft); }
829
+ .instance-head .instance-icon { width: 13px; height: 13px; color: var(--component); }
830
+ .instance-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
831
+ .instance-from { flex: 1; min-width: 0; color: var(--muted); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
832
+ .kv.override .swatch.set { visibility: visible; width: 6px; height: 6px; margin: 5px; border: 0; border-radius: 50%; background: var(--component); }
833
+ .instance-foot { display: flex; align-items: center; gap: 6px; }
834
+ .instance-foot .caption { flex: 1; }
835
+
836
+ /* ---- images --------------------------------------------------------------- */
837
+ .image-box { display: flex; align-items: center; gap: 10px; }
838
+ .thumb { position: relative; display: grid; place-items: center; width: 64px; height: 52px; flex: none; overflow: hidden; border-radius: var(--radius-inner); background: repeating-conic-gradient(var(--field-hover) 0 25%, transparent 0 50%) 0 0 / 12px 12px; border: 1px solid var(--hair); }
839
+ .thumb img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
840
+ .thumb .thumb-icon { color: var(--faint); width: 18px; height: 18px; }
841
+ .thumb img + .thumb-icon { display: none; }
842
+ .thumb.broken img { display: none; }
843
+ .thumb.broken .thumb-icon { display: block; color: var(--bad); }
844
+ .image-side { display: grid; gap: 6px; min-width: 0; }
845
+ .image-src { font: 11px var(--mono); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
846
+ .image-buttons { display: flex; gap: 4px; }
847
+ .menu-assets .menu-item { font: 12px var(--mono); }
848
+ .artboard.drop-target::after { inset: -3px; border: 2px dashed var(--accent); box-shadow: 0 0 0 6px var(--accent-soft); }
849
+
850
+ /* ---- fluid layouts -------------------------------------------------------- */
851
+ /* Phones and narrow windows: the sidebar opens almost full screen between the logo row and the tabs. The dock is the base row
852
+ (it is used most, so it keeps the thumb-reachable bottom edge) and the tabs sit above it; the inspector is a sheet above both.
853
+ Only one big surface is shown at a time. */
854
+ html.w720 { --edge: 8px; }
855
+ html.w720 .brand { right: var(--edge); max-width: none; justify-content: space-between; }
856
+ html.w720 .sidebar-foot { right: var(--edge); width: auto; max-width: none; }
857
+ html.w720 .side-tabs .tab { flex: 1 1 0; }
858
+ html.w720 body.has-sidebar .sidebar-foot { bottom: max(var(--edge), env(safe-area-inset-bottom)); }
859
+ html.w720 .panel.left { right: var(--edge); width: auto; }
860
+ html.w720 .dock { bottom: max(var(--edge), env(safe-area-inset-bottom)); right: var(--edge); }
861
+ html.w720 .sidebar-foot { bottom: calc(max(var(--edge), env(safe-area-inset-bottom)) + var(--dock-h, var(--chrome-h)) + 8px); }
862
+ html.w720 .crumbs-bar { bottom: calc(max(var(--edge), env(safe-area-inset-bottom)) + var(--dock-h, var(--chrome-h)) + var(--chrome-h) + 12px + var(--agent-float-h, 0px)); }
863
+ html.w720 .panel.right { left: var(--edge); right: var(--edge); width: auto; bottom: calc(max(var(--edge), env(safe-area-inset-bottom)) + var(--dock-h, var(--chrome-h)) + var(--chrome-h) + 16px + var(--agent-float-h, 0px)); max-height: calc(var(--vh) * 0.5); }
864
+ html.w720 body.agent-busy:not(.has-sidebar) .panel.right { max-height: min(calc(var(--vh) * 0.5), calc(var(--vh) - var(--dock-h, var(--chrome-h)) - var(--chrome-h) - 2 * var(--edge) - var(--brand-h) - 24px - var(--agent-float-h, 0px))); }
865
+ html.w720 body.has-sidebar .panel.right, html.w720 body.has-sidebar .dock { display: none; }
866
+ html.w440 .search-pill-text, html.w440 .search-pill kbd { display: none; } html.w440 .search-pill { padding: 0 9px; }
867
+ /* A full-screen page owns the screen: only the Back pill and the page title stay. */
868
+ body.is-expanded .brand, body.is-expanded .sidebar-foot, body.is-expanded .panel, body.is-expanded .dock, body.is-expanded .crumbs-bar { display: none; }
869
+ /* Phones: the dock keeps tools, zoom, undo and settings; the rest is in Ctrl K and the menus. */ html.w420 .dock .zoom-step { display: none; }
870
+ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; } }
871
+
872
+ /* Fluid rules: a value (key, count, time, tag) is as wide as its text and never wraps or squashes; a label and its control wrap together. */
873
+ .badge, .chip, .times, .dir-tag, .when, .palette-tag, time, .caption.kbd, .dim, .value { flex: none; white-space: nowrap; }
874
+ .hint-unit { display: inline-block; margin-right: 10px; white-space: nowrap; }
875
+
876
+ /* Forced-colors (Windows high contrast): backgrounds are dropped, so fields, pickers and the lit controls get real outlines. */
877
+ @media (forced-colors: active) {
878
+ .vf, .num, .field, .pick, .code-ta, .cls-input, .ask-input, .palette-input, .bm-field, .seg, .segs, .link-button, .settings-actions .tool { border: 1px solid ButtonText; }
879
+ .tool.on, .tab.on, .seg.on, .board.on, .palette-item.on, .menu-item:focus-visible, .menu-item:hover:not(:disabled) { outline: 2px solid Highlight; outline-offset: -2px; }
880
+ .tool:focus-visible, .tab:focus-visible, .pick:focus-visible, button:focus-visible { outline: 2px solid Highlight; outline-offset: 1px; }
881
+ }
882
+
883
+ /* Touch: every control is at least 24px in both directions (WCAG 2.5.8) and the common ones are roomier. */
884
+ @media (pointer: coarse) {
885
+ .tool.mini { min-width: 32px; height: 32px; }
886
+ .chip, .bm-link, .link-button, .seg-icon, .agent-chip { min-height: 32px; }
887
+ .seg-icon { min-width: 32px; }
888
+ .seg-icon.solo { width: 32px; height: 32px; }
889
+ .align-cell { min-width: 28px; min-height: 28px; }
890
+ .classes { gap: 6px; padding: 6px; }
891
+ .cls { min-height: 28px; }
892
+ .cls-name { min-height: 28px; }
893
+ .cls-input { height: 28px; }
894
+ .ident-field, .ident { height: 32px; }
895
+ .vf, .num, .pick { min-height: 36px; }
896
+ .boxmodel .bm-sq { --bm-edge: 24px; --bm-field: 24px; padding-top: 28px; padding-bottom: 28px; }
897
+ .boxmodel .bm-cell, .boxmodel .vf.bm-field, .boxmodel .vf.bm-offset, .boxmodel .vf.bm-value { min-height: 24px; height: 24px; }
898
+ .boxmodel .bm-core { font-size: 9px; }
899
+ .boxmodel .bm-expanded-values { padding-top: 104px; }
900
+ .boxmodel .bm-expanded-values > .bm-t { top: 20px; }
901
+ .boxmodel .bm-expanded-values > .bm-l { top: 48px; }
902
+ .boxmodel .bm-expanded-values > .bm-r { top: 76px; }
903
+ @container boxmodel (max-width: 230px) {
904
+ .boxmodel .bm-sq { --bm-edge: 8px; }
905
+ .boxmodel .bm-padding { --bm-edge: 24px; }
906
+ }
907
+ .swatch:not(.plain) { width: 28px; height: 28px; }
908
+ }
909
+
910
+ /* Border owns its line, edge widths, colour and radius; Appearance keeps fill and typography. */
911
+ .appearance-box, .border-box { display: grid; gap: 8px; }
912
+ .border-widths { --control-min: max(106px, calc(50% - 3px)); }
913
+ .border-box .color-field { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 4px 6px; padding: 4px 6px; }
914
+ .border-box .color-field > .vf-k { grid-column: 1 / -1; justify-self: start; min-width: 0; padding-left: 0; cursor: default; }
915
+ .appearance-box > .grid2, .typography-controls > .grid2 { align-items: center; }
916
+ .typography-controls { display: grid; gap: 8px; min-width: 0; }
917
+ .font-family-control { display: grid; gap: 4px; min-width: 0; }
918
+ .font-family-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: 4px; align-items: center; }
919
+ .font-family-row.with-upload { grid-template-columns: minmax(0, 1fr) auto; }
920
+ .font-upload { position: relative; }
921
+ .font-upload[aria-busy="true"] .icon { visibility: hidden; }
922
+ .font-upload[aria-busy="true"]::after { content: ""; position: absolute; inset: 0; margin: auto; width: 12px; height: 12px; border: 2px solid var(--accent); border-right-color: transparent; border-radius: 50%; animation: spin 1s linear infinite; }
923
+ .font-family-scroll { min-width: 0; max-width: 100%; overflow-x: auto; border-radius: var(--radius-inner); }
924
+ .font-family { width: 100%; min-width: max-content; }
925
+ .border-geometry { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
926
+ .border-geometry > .vf { flex: 0 1 84px; }
927
+ .border-geometry > .radius-presets { flex: 1 1 130px; }
928
+ .radius-presets { display: flex; flex-wrap: wrap; gap: 4px; }
929
+ .radius-presets .chip.on { background: var(--accent-soft); color: var(--accent-ink); }
930
+ .color-field { position: relative; display: flex; align-items: center; gap: 6px; min-width: 0; min-height: 28px; padding: 0 4px 0 0; border-radius: var(--radius-inner); background: var(--field); }
931
+ .color-field:focus-within { outline-offset: 1px; outline: var(--focus-ring) solid var(--accent); }
932
+ .color-field .vf-k { min-width: 30px; }
933
+ .color-field .swatch { flex: none; width: 20px; height: 20px; border-radius: var(--radius-small); }
934
+ .color-field .color-hex { min-width: 0; flex: 1; }
935
+ .color-block { display: grid; gap: 4px; }
936
+ .token-swatches { display: flex; flex-wrap: wrap; gap: 4px; padding-left: 36px; }
937
+ .token-swatch { width: 18px; height: 18px; padding: 0; border: 1px solid var(--hair); border-radius: 50%; cursor: pointer; }
938
+ .token-swatch:hover, .token-swatch:focus-visible { outline: var(--focus-ring) solid var(--accent); outline-offset: 1px; }
939
+ /* A control that lives inside a field wrapper is ringed by the wrapper, never twice. */
940
+ :is(.vf, .num, .code, .classes, .ident-field, .search, .color-field, .bm-field, .settings-output) :is(input, textarea):focus-visible { outline: none; }
941
+ .centre-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
942
+
943
+ @media (pointer: coarse) {
944
+ .radius-presets .chip { min-width: 32px; }
945
+ .color-field { min-height: 36px; }
946
+ .color-field .swatch { width: 28px; height: 28px; }
947
+ .token-swatch { width: 26px; height: 26px; }
948
+ }