@gemboss/ui 0.12.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +395 -1
- package/README.md +321 -6
- package/catalog/editor.html +611 -0
- package/catalog/index.html +3114 -13
- package/gemboss-ui.css +3 -4682
- package/package.json +16 -4
- package/src/app-shell.css +1 -256
- package/src/assistant.css +1 -558
- package/src/auth.css +1 -606
- package/src/board-move.ts +194 -0
- package/src/chat-composer.css +1 -110
- package/src/chat.css +1 -432
- package/src/collections.css +1 -0
- package/src/collections.tsx +543 -0
- package/src/editor/controls.tsx +206 -0
- package/src/editor/css-url.ts +11 -0
- package/src/editor/frame.tsx +242 -0
- package/src/editor/index.ts +43 -0
- package/src/editor/panel.tsx +188 -0
- package/src/editor/panes.tsx +44 -0
- package/src/editor/swatch.ts +45 -0
- package/src/editor-plain.js +1 -0
- package/src/editor.css +1 -1099
- package/src/gemboss-field.css +1 -112
- package/src/gemboss-tokens.css +1 -164
- package/src/icons.tsx +2 -0
- package/src/index-table.tsx +179 -0
- package/src/index.ts +19 -0
- package/src/kit-plain.css +1 -483
- package/src/kit-plain.js +1 -440
- package/src/menu-layer.ts +87 -0
- package/src/menu.tsx +28 -2
- package/src/portal.css +1 -139
- package/src/reactions-data.ts +59 -0
- package/src/reactions.css +1 -0
- package/src/reactions.tsx +139 -0
- package/src/resource-list-select.tsx +72 -0
- package/src/segmented-click.ts +20 -0
- package/src/segmented.tsx +98 -0
- package/src/selection.tsx +78 -0
- package/src/shell.css +1 -1079
- package/src/theme-dark.css +1 -66
- package/src/ui.css +1 -610
package/src/assistant.css
CHANGED
|
@@ -1,558 +1 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
3
|
-
* Moved from gemboss apps/admin/src/components/assistant/AssistantDockHost.module.css, where it was
|
|
4
|
-
* written for the admin's assistant; gemwatcher's and content-radar's assistants now wear the same
|
|
5
|
-
* host. Class names took a `gb-dock` prefix instead of CSS-module hashes (a consumer without CSS
|
|
6
|
-
* modules must be able to load it); every value is unchanged. The geometry assumes the GemBoss shell
|
|
7
|
-
* (@gemboss/ui AppShell): 56px topbar, 240px rail, drawer below 760px.
|
|
8
|
-
*
|
|
9
|
-
* Rendered by AssistantHost (assistant/host.tsx). State and the rules for when it shows are
|
|
10
|
-
* assistant/dock.ts + assistant/provider.tsx.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
.gb-dock {
|
|
15
|
-
position: fixed;
|
|
16
|
-
z-index: 320; /* above the mobile tab bar (300), below the page editor (400) */
|
|
17
|
-
right: 24px;
|
|
18
|
-
bottom: 0;
|
|
19
|
-
/* MEASURED 2026-09-12, not assumed: the rail is 240px at every width above 760 (the shell's own
|
|
20
|
-
`margin-left: 240px`), and there is NO bottom tab bar — `.gemboss-mobile-nav` is not in the DOM
|
|
21
|
-
at 320 or 390. An earlier pass here wrote 72px and 56px from a stale comment in AppShell and
|
|
22
|
-
the popup covered a third of the rail, swallowing the menu clicks this feature exists to free.
|
|
23
|
-
240 (rail) + 24 (right gutter) + 24 (left gap) = 288. */
|
|
24
|
-
/* 460 is the app's own modal width (`.ui-modal`), so the assistant reads as part of the same
|
|
25
|
-
family instead of a wider one-off. 600 was a guess; this one already exists. */
|
|
26
|
-
width: min(460px, calc(100vw - 288px));
|
|
27
|
-
display: flex;
|
|
28
|
-
flex-direction: column;
|
|
29
|
-
min-height: 0;
|
|
30
|
-
/* Frosted, the way the settings dialog reads — translucent surface over a small blur, so the page
|
|
31
|
-
underneath shows through as texture rather than detail.
|
|
32
|
-
NOT a full-screen scrim, though that is what settings uses. A scrim would block the page, and a
|
|
33
|
-
popup you cannot work behind is just the assistant page in a smaller box: the whole point of
|
|
34
|
-
this surface is that the screen under it stays usable. */
|
|
35
|
-
/* 93%, AND THE 5 POINTS THIS GAINED ON 25/09/2026 ARE A CONTRAST MEASUREMENT.
|
|
36
|
-
A translucent surface is `a·white + (1−a)·backdrop`, so the darkest it can ever go is `a·white`.
|
|
37
|
-
At 88% that floor was #E0E0E0, and `--gemboss-text-muted` (#686b71) measures 4.05:1 there —
|
|
38
|
-
UNDER AA. That is not a hypothetical corner of this component: `.header` and `.miniBar` both
|
|
39
|
-
declare `background: transparent` on purpose (see the note beside them), so `.headerStatus`
|
|
40
|
-
12px, `.miniStatus` 12px and `.booting` 14px all sit DIRECTLY on this surface in that colour.
|
|
41
|
-
93% puts the floor at #EDEDED and muted at 4.56:1. Chosen with, and kept equal to,
|
|
42
|
-
`.gemboss-popover-surface` in app/gemboss-shell.css — `one-popover-surface-four-wearers.test.ts`
|
|
43
|
-
fails if the two numbers ever stop agreeing. Change them THERE too.
|
|
44
|
-
|
|
45
|
-
TWO BACKGROUNDS, and the first is not decoration. `color-mix` and `backdrop-filter` ship on
|
|
46
|
-
DIFFERENT schedules — Firefox 103–112 and Safari 15.4–16.1 have the blur and not the mix — and
|
|
47
|
-
a declaration the parser rejects is DROPPED, not defaulted. Without the plain line, the dock had
|
|
48
|
-
NO background at all in those browsers: the page running straight through the chat. It shipped
|
|
49
|
-
that way on 12/09/2026 and nobody could see it from here. NOT a custom property: `background:
|
|
50
|
-
var(--some-mix-token)` would fail at computed-value time instead, which falls back to `unset`
|
|
51
|
-
rather than to the line above — worse than the bug it would be trying to share. */
|
|
52
|
-
background: var(--gemboss-surface);
|
|
53
|
-
background: color-mix(in srgb, var(--gemboss-surface) 93%, transparent);
|
|
54
|
-
-webkit-backdrop-filter: blur(14px) saturate(1.4);
|
|
55
|
-
backdrop-filter: blur(14px) saturate(1.4);
|
|
56
|
-
border: 1px solid var(--gemboss-divider);
|
|
57
|
-
border-bottom: 0;
|
|
58
|
-
border-radius: 12px 12px 0 0;
|
|
59
|
-
box-shadow: 0 -2px 6px rgba(15, 23, 42, 0.05), 0 24px 64px rgba(15, 23, 42, 0.18);
|
|
60
|
-
overflow: hidden;
|
|
61
|
-
|
|
62
|
-
/* ── Arriving ────────────────────────────────────────────────────────────────────────────────
|
|
63
|
-
Short and plain: 140ms, ease-out, a small rise and a fade. Long enough to read as "it came
|
|
64
|
-
from somewhere", short enough that nobody waits for it — a popup a seller opens twenty times
|
|
65
|
-
a day must not perform.
|
|
66
|
-
|
|
67
|
-
TRANSFORM AND OPACITY ONLY. Height and horizontal offset are set from a pointer, and a
|
|
68
|
-
transition on those means the box trails the finger instead of following it. Those two stay
|
|
69
|
-
instant by construction, not by luck. */
|
|
70
|
-
transition: transform 140ms cubic-bezier(0.2, 0.8, 0.3, 1), opacity 140ms ease-out;
|
|
71
|
-
}
|
|
72
|
-
/* The entry itself. `@starting-style` is what lets an element animate the frame it appears in
|
|
73
|
-
(Chrome 117+/Safari 17.5+; the desktop app is Chromium 132). Anywhere older simply appears —
|
|
74
|
-
degrading to instant is the right failure for chrome nobody asked to watch. */
|
|
75
|
-
@starting-style {
|
|
76
|
-
.gb-dock-dock, .gb-dock-mini { transform: translateY(10px); opacity: 0; }
|
|
77
|
-
.gb-dock-side { transform: translateX(12px); opacity: 0; }
|
|
78
|
-
}
|
|
79
|
-
/* Nothing animates while a pointer is setting the numbers — see `dragging` in the host. */
|
|
80
|
-
.gb-dock-dragging, .gb-dock-dragging * { transition: none !important; }
|
|
81
|
-
/* Where `backdrop-filter` is missing there is no blur to smear the page, and 93% opaque over sharp
|
|
82
|
-
body text is a chat you read through. Back to a solid surface — what this dock wore before it was
|
|
83
|
-
frosted. Same reasoning, same shape, as `.gemboss-popover-surface`. */
|
|
84
|
-
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
|
|
85
|
-
.gb-dock { background: var(--gemboss-surface); }
|
|
86
|
-
}
|
|
87
|
-
@media (prefers-reduced-motion: reduce) {
|
|
88
|
-
.gb-dock { transition: none; }
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/* The floating popup IS the base geometry above; this class exists so the DOM names the state —
|
|
92
|
-
`styles[mode]` was `undefined` for it, which is invisible in a screenshot and unnameable in a test. */
|
|
93
|
-
.gb-dock-dock { }
|
|
94
|
-
|
|
95
|
-
/* Still mounted — that is what keeps a run alive — just not on screen. */
|
|
96
|
-
.gb-dock-hidden { display: none; }
|
|
97
|
-
|
|
98
|
-
/* ── Full: exactly the area `main` occupies, so the dedicated page looks untouched ── */
|
|
99
|
-
.gb-dock-full {
|
|
100
|
-
/* BELOW `.gemboss-route-shell` (z-index 5), which covers this exact rectangle while the next route
|
|
101
|
-
loads. At the popup's 320 the assistant stayed painted over the holding screen, so leaving this
|
|
102
|
-
page looked like nothing happened — and the screen's click-catcher was defeated, so clicks
|
|
103
|
-
during the wait landed on chat controls. Fixed position still puts it over `main`'s content. */
|
|
104
|
-
z-index: 4;
|
|
105
|
-
top: 56px;
|
|
106
|
-
left: 240px; /* the rail, measured — see the note on .host */
|
|
107
|
-
right: 0;
|
|
108
|
-
bottom: 0;
|
|
109
|
-
width: auto;
|
|
110
|
-
border: 0;
|
|
111
|
-
border-radius: 0;
|
|
112
|
-
box-shadow: none;
|
|
113
|
-
background: var(--gemboss-app-bg);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/* ── Side: a full-height panel pinned to the right edge (01/10/2026) ─────────────────────────────
|
|
117
|
-
The third working shape, beside the popup and full screen: a long conversation next to the screen
|
|
118
|
-
it is about. Opt-in per surface (`<AssistantHost allowSide>`), because unlike the popup it TAKES
|
|
119
|
-
ROOM — the page beside it moves over (rule below), so nothing under it is hidden. That is why the
|
|
120
|
-
surface is solid rather than frosted: nothing sits behind it to show through.
|
|
121
|
-
Above `.gemboss-route-shell` (5), so the panel stays put while routes change — a sidebar that
|
|
122
|
-
vanishes on every click is a popup with extra steps. BELOW the topbar (300): the account menu
|
|
123
|
-
opens down out of the topbar's right end, exactly where this panel starts, and at the popup's 320
|
|
124
|
-
the menu fell behind the panel (review). 290 still clears the desktop invite (260/280). The host
|
|
125
|
-
is rendered inside `main` (AppShell's slot), which works because `main` makes no stacking context
|
|
126
|
-
or containing block; a surface that gives its `main` a transform or z-index must mount it outside. */
|
|
127
|
-
.gb-dock-side {
|
|
128
|
-
z-index: 290;
|
|
129
|
-
top: 56px;
|
|
130
|
-
right: 0;
|
|
131
|
-
bottom: 0;
|
|
132
|
-
width: var(--gb-dock-side-w, 400px);
|
|
133
|
-
background: var(--gemboss-surface);
|
|
134
|
-
-webkit-backdrop-filter: none;
|
|
135
|
-
backdrop-filter: none;
|
|
136
|
-
border: 0;
|
|
137
|
-
border-left: 1px solid var(--gemboss-divider);
|
|
138
|
-
border-radius: 0;
|
|
139
|
-
box-shadow: none;
|
|
140
|
-
overflow: visible; /* the seam's touch band reaches 14px past the left edge */
|
|
141
|
-
}
|
|
142
|
-
.gb-dock-side .gb-dock-body, .gb-dock-side .gb-dock-pane { overflow: hidden; }
|
|
143
|
-
/* The header does not drag a panel that is pinned. */
|
|
144
|
-
.gb-dock-side .gb-dock-header { cursor: default; touch-action: auto; }
|
|
145
|
-
/* The word for what the dot already says stands down here, as it does in a popup under 420px: the panel has
|
|
146
|
-
five controls in its header, and the title needs the room (measured: "Gem assi…" / "Re…" at 400). Visually
|
|
147
|
-
only — the dot is aria-hidden, so the word is the one place a screen reader hears "Working" (review #3622). */
|
|
148
|
-
.gb-dock-side .gb-dock-status {
|
|
149
|
-
flex: 0 0 auto; position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
|
|
150
|
-
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
|
|
151
|
-
}
|
|
152
|
-
.gb-dock-side .gb-dock-header:active { cursor: default; }
|
|
153
|
-
/* The seam: the panel's left edge, the fleet's one `.ui-resizer` (ui.css draws the grip). A 5px strip
|
|
154
|
-
centred on the border, ABOVE the page beside it. */
|
|
155
|
-
/* `.gb-dock-side >` is not decoration: `.ui-resizer { position: relative }` (ui.css) has the weight of one
|
|
156
|
-
class too, and which file a bundler loads last would decide whether the seam is a 5px strip or a 0px
|
|
157
|
-
line nobody can grab. */
|
|
158
|
-
.gb-dock-side > .gb-dock-sidegrip {
|
|
159
|
-
position: absolute;
|
|
160
|
-
top: 0;
|
|
161
|
-
bottom: 0;
|
|
162
|
-
left: -3px;
|
|
163
|
-
width: 5px;
|
|
164
|
-
z-index: 3;
|
|
165
|
-
}
|
|
166
|
-
/* The page makes room. `margin-right`, not padding: main's padding is set by four rules in shell.css
|
|
167
|
-
and a fifth here would fight them. The provider puts the class and the width on <body>; the default
|
|
168
|
-
`bodyClassPrefix` is `gemboss-assistant` — a surface with another prefix writes its own copy of this
|
|
169
|
-
one rule. No transition: the seam is set from a pointer, and a page that trails the finger is the
|
|
170
|
-
bug `.gb-dock` already avoids. */
|
|
171
|
-
@media (min-width: 1100px) {
|
|
172
|
-
body.gemboss-assistant-side .gemboss-platform-main { margin-right: var(--gb-assistant-side-w, 400px); }
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
/* ── Full: the one control full screen needs — the way back to the popup ──────────────────────
|
|
177
|
-
Floated over the chat's own header, whose right half is empty, rather than given a bar of its
|
|
178
|
-
own: a whole strip of chrome for one button would push the conversation down on every screen. */
|
|
179
|
-
.gb-dock-fullchrome {
|
|
180
|
-
position: absolute;
|
|
181
|
-
top: 10px;
|
|
182
|
-
right: 16px;
|
|
183
|
-
z-index: 1;
|
|
184
|
-
display: flex;
|
|
185
|
-
gap: 8px;
|
|
186
|
-
}
|
|
187
|
-
.gb-dock-shrink {
|
|
188
|
-
display: inline-flex;
|
|
189
|
-
align-items: center;
|
|
190
|
-
gap: 6px;
|
|
191
|
-
min-height: 44px; /* §9.1 control tier */
|
|
192
|
-
padding: 0 12px;
|
|
193
|
-
border: 1px solid var(--gemboss-divider);
|
|
194
|
-
border-radius: var(--gemboss-radius-full);
|
|
195
|
-
background: var(--gemboss-surface);
|
|
196
|
-
color: var(--gemboss-text-muted);
|
|
197
|
-
font: inherit;
|
|
198
|
-
font-size: var(--gemboss-fs-caption);
|
|
199
|
-
cursor: pointer;
|
|
200
|
-
white-space: nowrap;
|
|
201
|
-
}
|
|
202
|
-
.gb-dock-shrink:hover { background: var(--gemboss-hover); color: var(--gemboss-text); }
|
|
203
|
-
.gb-dock-shrink:focus-visible { outline: 2px solid var(--gemboss-primary); outline-offset: 2px; }
|
|
204
|
-
.gb-dock-shrink svg { width: 16px; height: 16px; flex: 0 0 auto; }
|
|
205
|
-
/* On a phone the label would crowd the chat header; the icon carries it there. */
|
|
206
|
-
@media (max-width: 560px) {
|
|
207
|
-
.gb-dock-shrink span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
|
208
|
-
.gb-dock-shrink { padding: 0; width: 44px; justify-content: center; }
|
|
209
|
-
/* Except the way to a person: a head-and-shoulders icon alone does not say "a human answers". */
|
|
210
|
-
.gb-dock-shrink-keep span { position: static; width: auto; height: auto; overflow: visible; clip: auto; }
|
|
211
|
-
.gb-dock-shrink-keep { padding: 0 12px; width: auto; }
|
|
212
|
-
/* Two controls do not fit in the empty half of the chat header at phone width — floated, they sat
|
|
213
|
-
on the title ("Ask th…", measured at 320 and 390). In the flow they cost one short strip here,
|
|
214
|
-
and only here. */
|
|
215
|
-
.gb-dock-fullchrome { position: static; justify-content: flex-end; padding: 8px 12px 0; }
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/* ── Mini: a status bar, not a window ── */
|
|
219
|
-
.gb-dock-mini {
|
|
220
|
-
height: auto;
|
|
221
|
-
width: min(320px, calc(100vw - 288px));
|
|
222
|
-
-webkit-backdrop-filter: blur(14px) saturate(1.4); /* Safari; the host above carries the rest */
|
|
223
|
-
backdrop-filter: blur(14px) saturate(1.4);
|
|
224
|
-
border-radius: 12px 12px 0 0;
|
|
225
|
-
}
|
|
226
|
-
.gb-dock-mini .gb-dock-body { display: none; }
|
|
227
|
-
.gb-dock-minirow { display: flex; align-items: center; padding-right: 4px; }
|
|
228
|
-
.gb-dock-minirow .gb-dock-minibar { flex: 1 1 auto; min-width: 0; }
|
|
229
|
-
/* `width: 44px` is a BASIS, not a floor: the default `flex-shrink: 1` let this button give ground to
|
|
230
|
-
the row beside it, so the × measured under 44 at all six widths while the rule above read as done.
|
|
231
|
-
MEASURED 2026-09-12 — the same trap as every other "the declaration is there" bug today. */
|
|
232
|
-
.gb-dock-minirow .gb-dock-icon { flex: 0 0 auto; }
|
|
233
|
-
|
|
234
|
-
.gb-dock-minibar {
|
|
235
|
-
display: flex;
|
|
236
|
-
align-items: center;
|
|
237
|
-
gap: 8px;
|
|
238
|
-
width: 100%;
|
|
239
|
-
min-height: 48px;
|
|
240
|
-
padding: 0 12px;
|
|
241
|
-
border: 0;
|
|
242
|
-
background: transparent;
|
|
243
|
-
font: inherit;
|
|
244
|
-
color: var(--gemboss-text-strong);
|
|
245
|
-
cursor: pointer;
|
|
246
|
-
text-align: left;
|
|
247
|
-
}
|
|
248
|
-
.gb-dock-minibar:hover { background: var(--gemboss-hover); }
|
|
249
|
-
.gb-dock-minibar strong { font-size: 13px; font-weight: 600; flex: 0 0 auto; }
|
|
250
|
-
.gb-dock-ministatus {
|
|
251
|
-
flex: 1 1 auto;
|
|
252
|
-
min-width: 0;
|
|
253
|
-
font-size: 12px;
|
|
254
|
-
color: var(--gemboss-text-muted);
|
|
255
|
-
overflow: hidden;
|
|
256
|
-
text-overflow: ellipsis;
|
|
257
|
-
white-space: nowrap;
|
|
258
|
-
}
|
|
259
|
-
.gb-dock-minichevron { font-size: 10px; color: var(--gemboss-text-muted); }
|
|
260
|
-
|
|
261
|
-
/* ── Resize grip (dock only) ── */
|
|
262
|
-
.gb-dock-grip {
|
|
263
|
-
/* OVERLAID on the header's top edge, not stacked above it. As its own 14px row it pushed the
|
|
264
|
-
title down and the header read as top-heavy — Chris: "header hơi lệch do padding top hơi nhiều".
|
|
265
|
-
The drag area is unchanged; it simply no longer costs vertical space. */
|
|
266
|
-
position: absolute;
|
|
267
|
-
top: 0;
|
|
268
|
-
left: 0;
|
|
269
|
-
right: 0;
|
|
270
|
-
height: 14px;
|
|
271
|
-
z-index: 2;
|
|
272
|
-
display: grid;
|
|
273
|
-
place-items: center;
|
|
274
|
-
cursor: ns-resize;
|
|
275
|
-
touch-action: none;
|
|
276
|
-
background: transparent;
|
|
277
|
-
}
|
|
278
|
-
.gb-dock-grip > span {
|
|
279
|
-
display: block;
|
|
280
|
-
width: 44px; /* a mouse target wide enough to find without aiming */
|
|
281
|
-
height: 4px;
|
|
282
|
-
border-radius: 2px;
|
|
283
|
-
background: var(--gemboss-divider);
|
|
284
|
-
}
|
|
285
|
-
.gb-dock-grip:hover > span, .gb-dock-grip:focus-visible > span { background: var(--gemboss-text-muted); }
|
|
286
|
-
.gb-dock-grip:focus-visible { outline: 2px solid var(--gemboss-primary); outline-offset: -2px; }
|
|
287
|
-
|
|
288
|
-
/* ── Dock header ── */
|
|
289
|
-
.gb-dock-header {
|
|
290
|
-
flex: 0 0 auto;
|
|
291
|
-
cursor: grab; /* the strip itself drags the popup sideways */
|
|
292
|
-
touch-action: none;
|
|
293
|
-
user-select: none;
|
|
294
|
-
display: flex;
|
|
295
|
-
align-items: center;
|
|
296
|
-
gap: 8px;
|
|
297
|
-
padding: 0 4px 0 16px;
|
|
298
|
-
min-height: 48px;
|
|
299
|
-
border-bottom: 1px solid var(--gemboss-divider);
|
|
300
|
-
background: transparent; /* the frosted host shows through; a second fill would double it */
|
|
301
|
-
}
|
|
302
|
-
.gb-dock-title {
|
|
303
|
-
font-size: 13px;
|
|
304
|
-
font-weight: 600;
|
|
305
|
-
color: var(--gemboss-text-strong);
|
|
306
|
-
white-space: nowrap;
|
|
307
|
-
/* Let the NAME absorb a squeeze. Adding the + button pushed the header to 290px inside a 277px
|
|
308
|
-
popup at 320 — a nowrap title with no min-width cannot give ground, so the row overflowed
|
|
309
|
-
instead. Measured 2026-09-12. */
|
|
310
|
-
flex: 0 1 auto;
|
|
311
|
-
min-width: 0;
|
|
312
|
-
overflow: hidden;
|
|
313
|
-
text-overflow: ellipsis;
|
|
314
|
-
}
|
|
315
|
-
.gb-dock-status {
|
|
316
|
-
flex: 1 1 auto;
|
|
317
|
-
min-width: 0;
|
|
318
|
-
font-size: 12px;
|
|
319
|
-
color: var(--gemboss-text-muted);
|
|
320
|
-
overflow: hidden;
|
|
321
|
-
text-overflow: ellipsis;
|
|
322
|
-
white-space: nowrap;
|
|
323
|
-
}
|
|
324
|
-
.gb-dock-header:active { cursor: grabbing; }
|
|
325
|
-
/* `margin-left: auto`: the status word was the only thing that stretched, so wherever it stands down (a popup under
|
|
326
|
-
420px, the side panel) the controls followed the title instead of the right edge — 350px adrift in a 720px panel
|
|
327
|
-
(review #3622). With the word showing, it still takes the free space first and nothing moves. */
|
|
328
|
-
.gb-dock-actions { display: flex; align-items: center; gap: 2px; cursor: default; margin-left: auto; }
|
|
329
|
-
/* One ACTION, then the three WINDOW controls — ordered by how much each changes the window: bigger,
|
|
330
|
-
smaller, gone. The gap is what says they are two different kinds of button; without it four icons
|
|
331
|
-
in a row read as one undifferentiated strip, which is what "các icon đang không đồng đều" was
|
|
332
|
-
describing. The 📌 that used to sit in the middle of them is gone entirely — see lib/assistant-dock. */
|
|
333
|
-
.gb-dock-action { margin-right: 8px; }
|
|
334
|
-
|
|
335
|
-
.gb-dock-icon {
|
|
336
|
-
/* 44px control target — Apple HIG / WCAG 2.5.5. The icon inside stays 18px. */
|
|
337
|
-
width: 44px;
|
|
338
|
-
height: 44px;
|
|
339
|
-
display: grid;
|
|
340
|
-
place-items: center;
|
|
341
|
-
border: 0;
|
|
342
|
-
border-radius: 8px;
|
|
343
|
-
background: transparent;
|
|
344
|
-
color: var(--gemboss-text-muted);
|
|
345
|
-
cursor: pointer;
|
|
346
|
-
padding: 0;
|
|
347
|
-
}
|
|
348
|
-
.gb-dock-icon:hover { background: var(--gemboss-hover); color: var(--gemboss-text-strong); }
|
|
349
|
-
.gb-dock-icon:focus-visible { outline: 2px solid var(--gemboss-primary); outline-offset: -2px; }
|
|
350
|
-
.gb-dock-icon svg { width: 18px; height: 18px; }
|
|
351
|
-
.gb-dock-icon-on { color: var(--gemboss-active-fg); background: var(--gemboss-active-bg); }
|
|
352
|
-
|
|
353
|
-
.gb-dock-dot {
|
|
354
|
-
flex: 0 0 auto;
|
|
355
|
-
width: 8px;
|
|
356
|
-
height: 8px;
|
|
357
|
-
border-radius: 50%;
|
|
358
|
-
background: var(--gemboss-divider);
|
|
359
|
-
}
|
|
360
|
-
.gb-dock-dot-busy { background: var(--gemboss-primary); animation: gb-dock-pulse 1.4s ease-in-out infinite; }
|
|
361
|
-
.gb-dock-dot-unread { background: var(--gemboss-success-strong); }
|
|
362
|
-
@keyframes gb-dock-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
|
|
363
|
-
@media (prefers-reduced-motion: reduce) { .gb-dock-dot-busy { animation: none; } }
|
|
364
|
-
|
|
365
|
-
/* ── Tabs: the assistant, or a person on the Gem team (dock only) ──────────────────────────────
|
|
366
|
-
Their own row under the header rather than inside it: at 320px the header already spends 176px
|
|
367
|
-
on four 44px controls, and two text tabs do not fit in what is left. */
|
|
368
|
-
.gb-dock-tabs {
|
|
369
|
-
flex: 0 0 auto;
|
|
370
|
-
display: flex;
|
|
371
|
-
gap: 4px;
|
|
372
|
-
padding: 0 8px;
|
|
373
|
-
border-bottom: 1px solid var(--gemboss-divider);
|
|
374
|
-
}
|
|
375
|
-
.gb-dock-tab {
|
|
376
|
-
position: relative;
|
|
377
|
-
display: inline-flex;
|
|
378
|
-
align-items: center;
|
|
379
|
-
gap: 6px;
|
|
380
|
-
min-height: 44px; /* §9.1 control tier */
|
|
381
|
-
padding: 0 12px;
|
|
382
|
-
border: 0;
|
|
383
|
-
border-bottom: 2px solid transparent;
|
|
384
|
-
background: transparent;
|
|
385
|
-
color: var(--gemboss-text-muted);
|
|
386
|
-
font: inherit;
|
|
387
|
-
font-size: 13px;
|
|
388
|
-
cursor: pointer;
|
|
389
|
-
white-space: nowrap;
|
|
390
|
-
}
|
|
391
|
-
.gb-dock-tab:hover { color: var(--gemboss-text-strong); }
|
|
392
|
-
.gb-dock-tab[aria-selected="true"] { color: var(--gemboss-text-strong); font-weight: 600; border-bottom-color: var(--gemboss-primary); }
|
|
393
|
-
.gb-dock-tab:focus-visible { outline: 2px solid var(--gemboss-primary); outline-offset: -2px; }
|
|
394
|
-
.gb-dock-badge {
|
|
395
|
-
min-width: 18px;
|
|
396
|
-
padding: 0 6px;
|
|
397
|
-
border-radius: var(--gemboss-radius-full);
|
|
398
|
-
background: var(--gemboss-primary);
|
|
399
|
-
color: var(--gemboss-on-primary, #fff);
|
|
400
|
-
font-size: 11px;
|
|
401
|
-
font-weight: 700;
|
|
402
|
-
line-height: 18px;
|
|
403
|
-
text-align: center;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/* ── Body: the assistant itself ── */
|
|
407
|
-
.gb-dock-body {
|
|
408
|
-
flex: 1 1 auto;
|
|
409
|
-
min-height: 0;
|
|
410
|
-
display: flex;
|
|
411
|
-
min-width: 0;
|
|
412
|
-
}
|
|
413
|
-
.gb-dock-body > * { flex: 1 1 auto; min-width: 0; min-height: 0; }
|
|
414
|
-
/* Each tab's pane. A hidden pane stays MOUNTED — `display:none`, never unmount (see the host). */
|
|
415
|
-
.gb-dock-pane { display: flex; min-width: 0; min-height: 0; }
|
|
416
|
-
.gb-dock-pane > * { flex: 1 1 auto; min-width: 0; min-height: 0; }
|
|
417
|
-
/* The package's own workspace (AssistantWorkspaceLayout) sizes itself to the viewport on its own page;
|
|
418
|
-
inside the host it fills the host instead. Moved here from the admin's assistant-dock-admin.css
|
|
419
|
-
(28/09/2026): every surface that puts the layout in the host needs it, or the layout keeps
|
|
420
|
-
`height: calc(100dvh - 56px)` inside a popup a few hundred pixels tall. */
|
|
421
|
-
.gb-dock-body .gemboss-assistant-layout,
|
|
422
|
-
.gb-dock-body .gemboss-assistant-page { height: auto; min-height: 0; }
|
|
423
|
-
.gb-dock-pane[hidden] { display: none; }
|
|
424
|
-
|
|
425
|
-
.gb-dock-booting {
|
|
426
|
-
flex: 1 1 auto;
|
|
427
|
-
display: grid;
|
|
428
|
-
place-items: center;
|
|
429
|
-
padding: 24px;
|
|
430
|
-
color: var(--gemboss-text-muted);
|
|
431
|
-
font-size: 14px;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
.gb-dock-sr {
|
|
435
|
-
position: absolute;
|
|
436
|
-
width: 1px; height: 1px;
|
|
437
|
-
padding: 0; margin: -1px;
|
|
438
|
-
overflow: hidden;
|
|
439
|
-
clip: rect(0 0 0 0);
|
|
440
|
-
white-space: nowrap;
|
|
441
|
-
border: 0;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
/* ── Phone: the popup spans the screen above the bottom tab bar; full mode loses the rail gutter ── */
|
|
445
|
-
/* ── Narrow: five 44px controls + a title do not fit ────────────────────────────────────────
|
|
446
|
-
MEASURED 2026-09-12 at 320px: the header row wanted 342px inside a 291px popup. 5 × 44 = 220
|
|
447
|
-
leaves 71px for a title and a status line. So the two controls that are about BROWSING WHILE IT
|
|
448
|
-
RUNS — the pin, and the word for what the dot already says — stand down; new chat, full screen,
|
|
449
|
-
minimize and close stay. Dropping a control beats letting the row overflow off the edge. */
|
|
450
|
-
@media (max-width: 420px) {
|
|
451
|
-
.gb-dock-status { display: none; }
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/* ── Phone: the rail is an off-canvas drawer, so both geometries reclaim the full width ── */
|
|
455
|
-
@media (max-width: 760px) {
|
|
456
|
-
.gb-dock {
|
|
457
|
-
right: 8px;
|
|
458
|
-
left: 8px;
|
|
459
|
-
width: auto;
|
|
460
|
-
padding-bottom: env(safe-area-inset-bottom);
|
|
461
|
-
border-radius: 12px 12px 0 0;
|
|
462
|
-
}
|
|
463
|
-
.gb-dock-mini { width: auto; }
|
|
464
|
-
.gb-dock-full {
|
|
465
|
-
left: 0;
|
|
466
|
-
right: 0;
|
|
467
|
-
top: 56px;
|
|
468
|
-
bottom: 0;
|
|
469
|
-
border-radius: 0;
|
|
470
|
-
}
|
|
471
|
-
/* No room beside a phone screen: the side panel takes the screen, the way full does, and there is
|
|
472
|
-
nothing to drag. The page is not pushed (the rule above stops at 761). */
|
|
473
|
-
.gb-dock-side {
|
|
474
|
-
left: 0;
|
|
475
|
-
right: 0;
|
|
476
|
-
width: auto;
|
|
477
|
-
border-left: 0;
|
|
478
|
-
border-radius: 0; /* the phone .gb-dock rule above rounds the top; this panel is the screen */
|
|
479
|
-
}
|
|
480
|
-
.gb-dock-sidegrip { display: none; }
|
|
481
|
-
/* …so the button that pins it there would only say "full screen" a second time. */
|
|
482
|
-
.gb-dock-pinside { display: none; }
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/* 1100 = rail 240 + the panel's narrowest 380 + the page's narrowest 480 (SIDE_MIN_WIDTH, SIDE_PAGE_MIN in
|
|
486
|
-
assistant/dock.ts). Below it the two do not fit side by side — MEASURED at 768: pushed, the page kept
|
|
487
|
-
120px and its table ran under the panel. So between the phone and 1100 the panel covers the content
|
|
488
|
-
area the way full screen does, nothing is pushed, there is no seam to drag, and the popup does not
|
|
489
|
-
offer to pin.
|
|
490
|
-
AT THE END OF THE FILE on purpose: `.gb-dock-icon { display: grid }` comes later than the side
|
|
491
|
-
section and has the same weight, so placed there this block lost and the pin still showed at 768
|
|
492
|
-
(measured). */
|
|
493
|
-
@media (min-width: 761px) and (max-width: 1099.98px) {
|
|
494
|
-
.gb-dock-side { left: 240px; width: auto; }
|
|
495
|
-
.gb-dock-side .gb-dock-sidegrip, .gb-dock-pinside { display: none; }
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
/* ── AssistantLauncher: the button that opens the assistant. NOT floating (03/10/2026) ────────────────────────────────
|
|
499
|
-
0.4.2 floated it bottom-right (z 280). visual-gate on partner-radar prod, 03/10: `content_covered` red at 5 of 6 widths
|
|
500
|
-
on every page measured (/, /apps, /sources): a fixed button always sits on whatever text scrolls under it, and at
|
|
501
|
-
scroll 0 that is a table column. GemCommunity had dropped its floating buttons for the same reason (#239). Chris chose
|
|
502
|
-
"bỏ nút nổi". It now lives where nothing scrolls under it:
|
|
503
|
-
· `--rail` wide screens: the last row of the rail (`railBottom`), a full-width primary button. Hidden ≤760px,
|
|
504
|
-
where the rail is an off-canvas drawer nobody sees until they open it.
|
|
505
|
-
· `--topbar` phones (≤760px): a 44px icon button in the topbar's `actions`, left of the account chip. Hidden above.
|
|
506
|
-
The top-right corner stays the ACCOUNT. A surface renders both; exactly one is displayed at any width. */
|
|
507
|
-
.gb-assistant-launcher {
|
|
508
|
-
display: inline-flex;
|
|
509
|
-
align-items: center;
|
|
510
|
-
gap: 8px;
|
|
511
|
-
border: 0;
|
|
512
|
-
cursor: pointer;
|
|
513
|
-
font-family: var(--gemboss-font-sans, system-ui, sans-serif);
|
|
514
|
-
}
|
|
515
|
-
.gb-assistant-launcher svg { flex: 0 0 auto; }
|
|
516
|
-
.gb-assistant-launcher:focus-visible { outline: 2px solid var(--gemboss-accent, #5661f5); outline-offset: 2px; }
|
|
517
|
-
.gb-assistant-launcher--rail {
|
|
518
|
-
width: 100%;
|
|
519
|
-
min-height: 44px; /* a control, not a list link: §9.1 asks 44 (visual-gate red at 768 with 36) */
|
|
520
|
-
margin: 8px 0 4px;
|
|
521
|
-
padding: 0 12px;
|
|
522
|
-
border-radius: var(--gemboss-radius-md, 8px);
|
|
523
|
-
/* The SAME iris pill as the admin's rail Assistant (.gnav-item.gnav-assistant, 0.6.8), not a solid near-black button
|
|
524
|
-
(0.7.1, 04/10/2026): content-radar and partner-radar open their assistant through this launcher, and after 0.6.8 the
|
|
525
|
-
two drew the one AI front door in two different colours beside the admin. */
|
|
526
|
-
background: var(--gemboss-accent-soft);
|
|
527
|
-
color: var(--gemboss-accent-strong);
|
|
528
|
-
font-size: 13px;
|
|
529
|
-
font-weight: 600;
|
|
530
|
-
line-height: 1;
|
|
531
|
-
text-align: left;
|
|
532
|
-
}
|
|
533
|
-
.gb-assistant-launcher--rail:hover { background: color-mix(in srgb, var(--gemboss-accent) 16%, var(--gemboss-surface)); }
|
|
534
|
-
.gb-assistant-launcher--rail .gb-assistant-launcher-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
535
|
-
.gb-assistant-launcher-kbd {
|
|
536
|
-
font: inherit; font-size: 11px; opacity: 0.7;
|
|
537
|
-
border: 1px solid currentColor; border-radius: 5px; padding: 1px 5px;
|
|
538
|
-
}
|
|
539
|
-
.gb-assistant-launcher--topbar {
|
|
540
|
-
display: none;
|
|
541
|
-
position: relative; /* holds the clipped label: an absolute child of a static button escapes to the nearest positioned box */
|
|
542
|
-
justify-content: center;
|
|
543
|
-
flex: 0 0 auto;
|
|
544
|
-
width: 44px;
|
|
545
|
-
height: 44px;
|
|
546
|
-
padding: 0;
|
|
547
|
-
border-radius: var(--gemboss-radius-md, 8px);
|
|
548
|
-
background: transparent;
|
|
549
|
-
color: var(--gemboss-text, #1b1c1e);
|
|
550
|
-
}
|
|
551
|
-
.gb-assistant-launcher--topbar:hover, .gb-assistant-launcher--topbar:active { background: var(--gemboss-hover, #eceef2); }
|
|
552
|
-
.gb-assistant-launcher--topbar .gb-assistant-launcher-label, .gb-assistant-launcher--topbar .gb-assistant-launcher-kbd {
|
|
553
|
-
position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
|
|
554
|
-
}
|
|
555
|
-
@media (max-width: 760px) {
|
|
556
|
-
.gb-assistant-launcher--rail { display: none; }
|
|
557
|
-
.gb-assistant-launcher--topbar { display: inline-flex; }
|
|
558
|
-
}
|
|
1
|
+
.gb-dock{position:fixed;z-index:320;right:24px;bottom:0;width:min(460px,calc(100vw - 288px));display:flex;flex-direction:column;min-height:0;background:var(--gemboss-surface);background:color-mix(in srgb,var(--gemboss-surface) 93%,transparent);-webkit-backdrop-filter:blur(14px) saturate(1.4);backdrop-filter:blur(14px) saturate(1.4);border:1px solid var(--gemboss-divider);border-bottom:0;border-radius:12px 12px 0 0;box-shadow:0 -2px 6px #0f172a0d,0 24px 64px #0f172a2e;overflow:hidden;transition:transform .14s cubic-bezier(.2,.8,.3,1),opacity .14s ease-out}@starting-style{.gb-dock-dock,.gb-dock-mini{transform:translateY(10px);opacity:0}.gb-dock-side{transform:translate(12px);opacity:0}}.gb-dock-dragging,.gb-dock-dragging *{transition:none!important}@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){.gb-dock{background:var(--gemboss-surface)}}@media (prefers-reduced-motion: reduce){.gb-dock{transition:none}}.gb-dock-hidden{display:none}.gb-dock-full{z-index:4;inset:56px 0 0 240px;width:auto;border:0;border-radius:0;box-shadow:none;background:var(--gemboss-app-bg)}.gb-dock-side{z-index:290;top:56px;right:0;bottom:0;width:var(--gb-dock-side-w, 400px);background:var(--gemboss-surface);-webkit-backdrop-filter:none;backdrop-filter:none;border:0;border-left:1px solid var(--gemboss-divider);border-radius:0;box-shadow:none;overflow:visible}.gb-dock-side .gb-dock-body,.gb-dock-side .gb-dock-pane{overflow:hidden}.gb-dock-side .gb-dock-header{cursor:default;touch-action:auto}.gb-dock-side .gb-dock-status{flex:0 0 auto;position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.gb-dock-side .gb-dock-header:active{cursor:default}.gb-dock-side>.gb-dock-sidegrip{position:absolute;top:0;bottom:0;left:-3px;width:5px;z-index:3}@media (min-width: 1100px){body.gemboss-assistant-side .gemboss-platform-main{margin-right:var(--gb-assistant-side-w, 400px)}}.gb-dock-fullchrome{position:absolute;top:10px;right:16px;z-index:1;display:flex;gap:8px}.gb-dock-shrink{display:inline-flex;align-items:center;gap:6px;min-height:44px;padding:0 12px;border:1px solid var(--gemboss-divider);border-radius:var(--gemboss-radius-full);background:var(--gemboss-surface);color:var(--gemboss-text-muted);font:inherit;font-size:var(--gemboss-fs-caption);cursor:pointer;white-space:nowrap}.gb-dock-shrink:hover{background:var(--gemboss-hover);color:var(--gemboss-text)}.gb-dock-shrink:focus-visible{outline:2px solid var(--gemboss-primary);outline-offset:2px}.gb-dock-shrink svg{width:16px;height:16px;flex:0 0 auto}@media (max-width: 560px){.gb-dock-shrink span{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}.gb-dock-shrink{padding:0;width:44px;justify-content:center}.gb-dock-shrink-keep span{position:static;width:auto;height:auto;overflow:visible;clip:auto}.gb-dock-shrink-keep{padding:0 12px;width:auto}.gb-dock-fullchrome{position:static;justify-content:flex-end;padding:8px 12px 0}}.gb-dock-mini{height:auto;width:min(320px,calc(100vw - 288px));-webkit-backdrop-filter:blur(14px) saturate(1.4);backdrop-filter:blur(14px) saturate(1.4);border-radius:12px 12px 0 0}.gb-dock-mini .gb-dock-body{display:none}.gb-dock-minirow{display:flex;align-items:center;padding-right:4px}.gb-dock-minirow .gb-dock-minibar{flex:1 1 auto;min-width:0}.gb-dock-minirow .gb-dock-icon{flex:0 0 auto}.gb-dock-minibar{display:flex;align-items:center;gap:8px;width:100%;min-height:48px;padding:0 12px;border:0;background:transparent;font:inherit;color:var(--gemboss-text-strong);cursor:pointer;text-align:left}.gb-dock-minibar:hover{background:var(--gemboss-hover)}.gb-dock-minibar strong{font-size:13px;font-weight:600;flex:0 0 auto}.gb-dock-ministatus{flex:1 1 auto;min-width:0;font-size:12px;color:var(--gemboss-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.gb-dock-minichevron{font-size:10px;color:var(--gemboss-text-muted)}.gb-dock-grip{position:absolute;top:0;left:0;right:0;height:14px;z-index:2;display:grid;place-items:center;cursor:ns-resize;touch-action:none;background:transparent}.gb-dock-grip>span{display:block;width:44px;height:4px;border-radius:2px;background:var(--gemboss-divider)}.gb-dock-grip:hover>span,.gb-dock-grip:focus-visible>span{background:var(--gemboss-text-muted)}.gb-dock-grip:focus-visible{outline:2px solid var(--gemboss-primary);outline-offset:-2px}.gb-dock-header{flex:0 0 auto;cursor:grab;touch-action:none;user-select:none;display:flex;align-items:center;gap:8px;padding:0 4px 0 16px;min-height:48px;border-bottom:1px solid var(--gemboss-divider);background:transparent}.gb-dock-title{font-size:13px;font-weight:600;color:var(--gemboss-text-strong);white-space:nowrap;flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.gb-dock-status{flex:1 1 auto;min-width:0;font-size:12px;color:var(--gemboss-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.gb-dock-header:active{cursor:grabbing}.gb-dock-actions{display:flex;align-items:center;gap:2px;cursor:default;margin-left:auto}.gb-dock-action{margin-right:8px}.gb-dock-icon{width:44px;height:44px;display:grid;place-items:center;border:0;border-radius:8px;background:transparent;color:var(--gemboss-text-muted);cursor:pointer;padding:0}.gb-dock-icon:hover{background:var(--gemboss-hover);color:var(--gemboss-text-strong)}.gb-dock-icon:focus-visible{outline:2px solid var(--gemboss-primary);outline-offset:-2px}.gb-dock-icon svg{width:18px;height:18px}.gb-dock-icon-on{color:var(--gemboss-active-fg);background:var(--gemboss-active-bg)}.gb-dock-dot{flex:0 0 auto;width:8px;height:8px;border-radius:50%;background:var(--gemboss-divider)}.gb-dock-dot-busy{background:var(--gemboss-primary);animation:gb-dock-pulse 1.4s ease-in-out infinite}.gb-dock-dot-unread{background:var(--gemboss-success-strong)}@keyframes gb-dock-pulse{0%,to{opacity:1}50%{opacity:.35}}@media (prefers-reduced-motion: reduce){.gb-dock-dot-busy{animation:none}}.gb-dock-tabs{flex:0 0 auto;display:flex;gap:4px;padding:0 8px;border-bottom:1px solid var(--gemboss-divider)}.gb-dock-tab{position:relative;display:inline-flex;align-items:center;gap:6px;min-height:44px;padding:0 12px;border:0;border-bottom:2px solid transparent;background:transparent;color:var(--gemboss-text-muted);font:inherit;font-size:13px;cursor:pointer;white-space:nowrap}.gb-dock-tab:hover{color:var(--gemboss-text-strong)}.gb-dock-tab[aria-selected=true]{color:var(--gemboss-text-strong);font-weight:600;border-bottom-color:var(--gemboss-primary)}.gb-dock-tab:focus-visible{outline:2px solid var(--gemboss-primary);outline-offset:-2px}.gb-dock-badge{min-width:18px;padding:0 6px;border-radius:var(--gemboss-radius-full);background:var(--gemboss-primary);color:var(--gemboss-on-primary, #fff);font-size:11px;font-weight:700;line-height:18px;text-align:center}.gb-dock-body{flex:1 1 auto;min-height:0;display:flex;min-width:0}.gb-dock-body>*{flex:1 1 auto;min-width:0;min-height:0}.gb-dock-pane{display:flex;min-width:0;min-height:0}.gb-dock-pane>*{flex:1 1 auto;min-width:0;min-height:0}.gb-dock-body .gemboss-assistant-layout,.gb-dock-body .gemboss-assistant-page{height:auto;min-height:0}.gb-dock-pane[hidden]{display:none}.gb-dock-booting{flex:1 1 auto;display:grid;place-items:center;padding:24px;color:var(--gemboss-text-muted);font-size:14px}.gb-dock-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}@media (max-width: 420px){.gb-dock-status{display:none}}@media (max-width: 760px){.gb-dock{right:8px;left:8px;width:auto;padding-bottom:env(safe-area-inset-bottom);border-radius:12px 12px 0 0}.gb-dock-mini{width:auto}.gb-dock-full{inset:56px 0 0;border-radius:0}.gb-dock-side{left:0;right:0;width:auto;border-left:0;border-radius:0}.gb-dock-sidegrip,.gb-dock-pinside{display:none}}@media (min-width: 761px) and (max-width: 1099.98px){.gb-dock-side{left:240px;width:auto}.gb-dock-side .gb-dock-sidegrip,.gb-dock-pinside{display:none}}.gb-assistant-launcher{display:inline-flex;align-items:center;gap:8px;border:0;cursor:pointer;font-family:var(--gemboss-font-sans, system-ui, sans-serif)}.gb-assistant-launcher svg{flex:0 0 auto}.gb-assistant-launcher:focus-visible{outline:2px solid var(--gemboss-accent, #5661f5);outline-offset:2px}.gb-assistant-launcher--rail{width:100%;min-height:44px;margin:8px 0 4px;padding:0 12px;border-radius:var(--gemboss-radius-md, 8px);background:var(--gemboss-accent-soft);color:var(--gemboss-accent-strong);font-size:13px;font-weight:600;line-height:1;text-align:left}.gb-assistant-launcher--rail:hover{background:color-mix(in srgb,var(--gemboss-accent) 16%,var(--gemboss-surface))}.gb-assistant-launcher--rail .gb-assistant-launcher-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.gb-assistant-launcher-kbd{font:inherit;font-size:11px;opacity:.7;border:1px solid currentColor;border-radius:5px;padding:1px 5px}.gb-assistant-launcher--topbar{display:none;position:relative;justify-content:center;flex:0 0 auto;width:44px;height:44px;padding:0;border-radius:var(--gemboss-radius-md, 8px);background:transparent;color:var(--gemboss-text, #1b1c1e)}.gb-assistant-launcher--topbar:hover,.gb-assistant-launcher--topbar:active{background:var(--gemboss-hover, #eceef2)}.gb-assistant-launcher--topbar .gb-assistant-launcher-label,.gb-assistant-launcher--topbar .gb-assistant-launcher-kbd{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}@media (max-width: 760px){.gb-assistant-launcher--rail{display:none}.gb-assistant-launcher--topbar{display:inline-flex}}
|