@gemboss/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +208 -0
- package/catalog/index.html +423 -0
- package/gemboss-ui.css +2889 -0
- package/package.json +51 -0
- package/src/GemMark.tsx +22 -0
- package/src/account.tsx +88 -0
- package/src/app-shell.css +65 -0
- package/src/assistant/dock.ts +142 -0
- package/src/assistant/host.tsx +371 -0
- package/src/assistant/index.ts +6 -0
- package/src/assistant/provider.tsx +269 -0
- package/src/assistant/sessions.tsx +450 -0
- package/src/assistant.css +407 -0
- package/src/auth.css +606 -0
- package/src/auth.tsx +202 -0
- package/src/brand.tsx +31 -0
- package/src/gemboss-field.css +108 -0
- package/src/gemboss-tokens.css +164 -0
- package/src/icons.tsx +75 -0
- package/src/index.ts +9 -0
- package/src/shell.css +1069 -0
- package/src/shell.tsx +212 -0
- package/src/ui.css +403 -0
package/src/shell.css
ADDED
|
@@ -0,0 +1,1069 @@
|
|
|
1
|
+
/* @gemboss/ui: the SHELL every GemBoss surface shares (29/09/2026).
|
|
2
|
+
*
|
|
3
|
+
* Rail (`.icon-nav` + `.gnav-*`), the fixed 56px topbar, `.gemboss-platform-main`, the mobile drawer,
|
|
4
|
+
* the assistant's thread rail (`.gemboss-assistant-*`), the shared popover surface, the account chip and
|
|
5
|
+
* its menu, and the rail's Assistant item. Until 29/09/2026 these rules lived in the admin's
|
|
6
|
+
* apps/admin/src/app/gemboss-shell.css and apps/admin/scripts/export-ui-css.mjs EXTRACTED them for other
|
|
7
|
+
* surfaces by an allow-list. Moved here verbatim (comments included, original order kept) so the package
|
|
8
|
+
* owns them and the admin imports them like every other surface: gemboss-shell.css pulls this file in
|
|
9
|
+
* with @import on its line 2, which puts these rules exactly where they sat before (a cascade check found
|
|
10
|
+
* no staying admin rule of equal specificity that shared an element with a moved one).
|
|
11
|
+
*
|
|
12
|
+
* The admin's own components stay in gemboss-shell.css. export-ui-css.mjs fails the build if a rule of
|
|
13
|
+
* these families reappears there: one owner per component (CLAUDE.md §9.3).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/* ── WHAT AN ANCHORED POPUP IS MADE OF — ONE RECIPE, FOUR WEARERS ─────────────
|
|
17
|
+
*
|
|
18
|
+
* The topbar's popups were four copies of the same six declarations. Counted 25/09/2026:
|
|
19
|
+
* `.gemboss-shop-switcher-menu` and `.gemboss-editor-menu` and `.gemboss-pagepicker-menu` each wrote
|
|
20
|
+
* `background: var(--gemboss-surface)` + divider border + radius + `--gemboss-shadow-raised` in its
|
|
21
|
+
* own block, and the account menu wrote the same idea a fourth time as INLINE STYLES with a
|
|
22
|
+
* hardcoded `#fff` and a hardcoded shadow — so it was the one surface in the family that no token
|
|
23
|
+
* change could ever reach. Three of the four already agreed on 12px; the account chip's 12 was a
|
|
24
|
+
* literal that happened to match.
|
|
25
|
+
*
|
|
26
|
+
* THIS CLASS IS ONLY THE SURFACE — what the popup is made of. Position, z-index, width, padding and
|
|
27
|
+
* the entry animation stay with each popup, because those genuinely differ (the switcher hangs off
|
|
28
|
+
* its trigger at `absolute`, the editor menus are `fixed` at a measured point). Pulling those
|
|
29
|
+
* together too is how a shared class becomes a thing every wearer has to fight.
|
|
30
|
+
*
|
|
31
|
+
* THE FROSTING IS THE POINT, and the blur is the assistant dock's, not a new taste: see
|
|
32
|
+
* packages/ui/src/assistant.css (the assistant dock, formerly AssistantDockHost.module.css), which has read `blur(14px) saturate(1.4)`
|
|
33
|
+
* since 12/09/2026. Chris asked for the topbar popups to read the same way — the page under them
|
|
34
|
+
* stays legible as texture while you scroll, so the popup sits ON the page rather than punching a
|
|
35
|
+
* hole in it.
|
|
36
|
+
*
|
|
37
|
+
* 93%, NOT THE DOCK'S 88% — and the 5 points are a contrast measurement, not a preference.
|
|
38
|
+
* A translucent surface is `a·white + (1−a)·backdrop`, so the DARKEST it can ever go is `a·white`.
|
|
39
|
+
* At 88% that floor is #E0E0E0, where `--gemboss-text-muted` (#686b71) measures 4.05:1 — UNDER AA.
|
|
40
|
+
* That colour is not decoration in this family: it is the switcher's "Your stores" heading, the
|
|
41
|
+
* editor menu's group labels and the account chip's email line, all at 11–12px, so 4.5:1 is the bar
|
|
42
|
+
* they have to clear. 93% puts the floor at #EDEDED and muted at 4.56:1; `--gemboss-danger-strong`
|
|
43
|
+
* ("Sign out") reaches 5.62:1 and body text 14.57:1. A black backdrop is not hypothetical here —
|
|
44
|
+
* the editor menus open over the preview canvas, which renders whatever hero image the theme has.
|
|
45
|
+
* (The dock's own 88% has the same floor. It is not this diff's to move, and it is over the admin's
|
|
46
|
+
* own pale canvas rather than a storefront preview, but it is worth a look.)
|
|
47
|
+
*
|
|
48
|
+
* `@supports` IS NOT DECORATION. 93% opaque is readable ONLY because a blur is smearing what is
|
|
49
|
+
* behind it; where `backdrop-filter` is missing, that same 93% is a sheet of body text showing
|
|
50
|
+
* straight through the menu labels. The fallback below puts the surface back to fully opaque, which
|
|
51
|
+
* is exactly what these four wore before this block existed.
|
|
52
|
+
*/
|
|
53
|
+
.gemboss-popover-surface {
|
|
54
|
+
/* TWO backgrounds, and the first one is not decoration. `color-mix` and `backdrop-filter` ship on
|
|
55
|
+
DIFFERENT schedules — Firefox 103–112 and Safari 15.4–16.1 have the blur and NOT the mix — and a
|
|
56
|
+
declaration the parser rejects is DROPPED, not defaulted. Without this line those browsers give
|
|
57
|
+
the popup no background at all: a fully transparent menu with the page running through the
|
|
58
|
+
labels, which is worse than either the frosted or the opaque version. The `@supports` block
|
|
59
|
+
below guards the other axis and cannot cover this one: it asks about backdrop-filter, which
|
|
60
|
+
those browsers HAVE. Caught by /code-review, 25/09/2026. */
|
|
61
|
+
background: var(--gemboss-surface);
|
|
62
|
+
background: color-mix(in srgb, var(--gemboss-surface) 93%, transparent);
|
|
63
|
+
-webkit-backdrop-filter: blur(14px) saturate(1.4);
|
|
64
|
+
backdrop-filter: blur(14px) saturate(1.4);
|
|
65
|
+
border: 1px solid var(--gemboss-divider);
|
|
66
|
+
border-radius: var(--gemboss-radius-lg);
|
|
67
|
+
box-shadow: var(--gemboss-shadow-raised);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
|
|
71
|
+
.gemboss-popover-surface { background: var(--gemboss-surface); }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* ─── 72px IconNav rail ───────────────────────────────────────────────────── */
|
|
75
|
+
.icon-nav {
|
|
76
|
+
position: fixed;
|
|
77
|
+
top: 56px;
|
|
78
|
+
left: 0;
|
|
79
|
+
z-index: 200;
|
|
80
|
+
width: 72px;
|
|
81
|
+
height: calc(100dvh - 56px);
|
|
82
|
+
background: var(--gemboss-rail-bg);
|
|
83
|
+
border-right: 1px solid var(--gemboss-divider);
|
|
84
|
+
display: flex;
|
|
85
|
+
flex-direction: column;
|
|
86
|
+
padding-top: 8px;
|
|
87
|
+
overflow: hidden;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* Account dropdown (avatar chip). The panel was inline styles on the <div> until 25/09/2026 —
|
|
91
|
+
including a hardcoded `#fff` and its own `0 12px 32px` shadow, the one surface in this family a
|
|
92
|
+
token change could not reach. Surface comes from `gemboss-popover-surface` now, so it frosts and
|
|
93
|
+
re-themes with the other three; the shadow it lost was heavier than `--gemboss-shadow-raised`,
|
|
94
|
+
which is the point of having one. */
|
|
95
|
+
.gemboss-account-menu {
|
|
96
|
+
position: absolute; top: calc(100% + 6px); right: 0; min-width: 220px; z-index: 320;
|
|
97
|
+
overflow: hidden;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* Account dropdown menu items (avatar chip) — CSS-driven hover/focus, replaces inline JS handlers. */
|
|
101
|
+
/* min-height 44 (28/09/2026): 12px padding around a 13px line measured 42.84px — under the 44px control
|
|
102
|
+
floor (CLAUDE.md §9.1), in the admin's own menu and in every surface wearing it through @gemboss/ui. */
|
|
103
|
+
.gemboss-account-menu-item {
|
|
104
|
+
display: block; width: 100%; min-height: 44px; padding: 12px 16px; border: 0; background: transparent;
|
|
105
|
+
font: inherit; font-size: 13px; color: var(--gemboss-text); text-align: left; text-decoration: none;
|
|
106
|
+
cursor: pointer; transition: background var(--gemboss-transition-fast) ease, color var(--gemboss-transition-fast) ease;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.gemboss-account-menu-item:hover { background: var(--gemboss-hover); }
|
|
110
|
+
|
|
111
|
+
.gemboss-account-menu-item:focus-visible { outline: 2px solid var(--gemboss-primary); outline-offset: -2px; background: var(--gemboss-hover); }
|
|
112
|
+
|
|
113
|
+
/* `--gemboss-danger` var(--gemboss-danger) is 4.25:1 on `--gemboss-hover` #eceef2, and this row takes that hover
|
|
114
|
+
background two lines above — so the one moment the item is under the cursor is the moment its label
|
|
115
|
+
drops below AA. `--gemboss-danger-strong` #b42318 is 5.66:1 there and 7.30:1 on white. */
|
|
116
|
+
.gemboss-account-menu-item.danger { color: var(--gemboss-danger-strong); }
|
|
117
|
+
|
|
118
|
+
.gemboss-account-chip {
|
|
119
|
+
width: 30px;
|
|
120
|
+
height: 30px;
|
|
121
|
+
border: 1px solid #cfd6ce;
|
|
122
|
+
border-radius: var(--gemboss-radius-full);
|
|
123
|
+
background: #f5f7f4;
|
|
124
|
+
color: #303633;
|
|
125
|
+
display: inline-flex;
|
|
126
|
+
align-items: center;
|
|
127
|
+
justify-content: center;
|
|
128
|
+
flex: 0 0 auto;
|
|
129
|
+
font-size: 11px;
|
|
130
|
+
font-weight: 700;
|
|
131
|
+
cursor: pointer;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.gemboss-platform-main {
|
|
135
|
+
box-sizing: border-box;
|
|
136
|
+
min-height: 100dvh;
|
|
137
|
+
margin-left: 72px;
|
|
138
|
+
padding-top: 56px;
|
|
139
|
+
background: var(--gemboss-app-bg);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.icon-nav {
|
|
143
|
+
top: 56px;
|
|
144
|
+
height: calc(100dvh - 56px);
|
|
145
|
+
background: var(--gemboss-nav-bg);
|
|
146
|
+
border-right-color: var(--gemboss-divider);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@media (max-width: 1040px) {
|
|
150
|
+
.gemboss-platform-topbar {
|
|
151
|
+
grid-template-columns: minmax(120px, 1fr) minmax(220px, 520px) minmax(120px, 1fr);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@media (max-width: 760px) {
|
|
157
|
+
/* The single-row mobile top-bar layout (logo · search-icon · shop · account) is
|
|
158
|
+
defined in the appended block at the END of this file so it wins the duplicate
|
|
159
|
+
base rules. Here we just hide the rail + dev-only MCP chip and zero the rail
|
|
160
|
+
margin (the bottom-bar clearance + topbar padding also live in that late block). */
|
|
161
|
+
|
|
162
|
+
/* .icon-nav is the off-canvas drawer on mobile (appended block at file end) — not hidden. */
|
|
163
|
+
|
|
164
|
+
.gemboss-platform-main { margin-left: 0; }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.gemboss-account-chip {
|
|
168
|
+
width: 32px;
|
|
169
|
+
height: 32px;
|
|
170
|
+
border-radius: 50%;
|
|
171
|
+
background: var(--gemboss-primary);
|
|
172
|
+
color: #fff;
|
|
173
|
+
font-weight: 600;
|
|
174
|
+
font-size: 12px;
|
|
175
|
+
display: inline-flex;
|
|
176
|
+
align-items: center;
|
|
177
|
+
justify-content: center;
|
|
178
|
+
border: none;
|
|
179
|
+
cursor: pointer;
|
|
180
|
+
transition: transform var(--gemboss-transition-base) ease;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.gemboss-account-chip:hover { transform: scale(1.05); }
|
|
184
|
+
|
|
185
|
+
.gemboss-account-chip:focus-visible { outline: 2px solid var(--gemboss-primary); outline-offset: 2px; }
|
|
186
|
+
|
|
187
|
+
.gemboss-platform-main {
|
|
188
|
+
margin-top: 56px; /* below fixed topbar */
|
|
189
|
+
margin-left: 72px; /* right of fixed icon-nav */
|
|
190
|
+
min-height: calc(100vh - 56px);
|
|
191
|
+
padding: 24px;
|
|
192
|
+
background: #fafafb;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@media (max-width: 768px) {
|
|
196
|
+
.gemboss-platform-main {
|
|
197
|
+
margin-left: 0;
|
|
198
|
+
padding: 16px;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* ─── Canonical shell pass ─────────────────────────────────────────────────
|
|
203
|
+
* Keep this block last. Earlier iterations of the shell left multiple topbar
|
|
204
|
+
* and rail definitions in this file; these rules define the current platform
|
|
205
|
+
* layout explicitly so the app reads as one organized product surface.
|
|
206
|
+
*/
|
|
207
|
+
.gemboss-platform-shell {
|
|
208
|
+
display: block;
|
|
209
|
+
min-height: 100vh;
|
|
210
|
+
background: var(--gemboss-app-bg); /* unify with main content — was #f6f6f4 beige showing through */
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.gemboss-platform-topbar {
|
|
214
|
+
position: fixed;
|
|
215
|
+
inset: 0 0 auto 0;
|
|
216
|
+
z-index: 300;
|
|
217
|
+
height: 56px;
|
|
218
|
+
border-bottom: 1px solid var(--gemboss-divider);
|
|
219
|
+
background: var(--gemboss-topbar-bg);
|
|
220
|
+
display: grid;
|
|
221
|
+
/* THE single `.gemboss-platform-topbar` base rule (two earlier duplicates removed). Symmetric
|
|
222
|
+
side columns so the centre search bar is TRUE-centred (was `244px … 1fr`, which pushed it
|
|
223
|
+
~164px left of centre). Brand sits left in col1, actions right in col3. */
|
|
224
|
+
grid-template-columns: minmax(200px, 1fr) minmax(280px, 560px) minmax(200px, 1fr);
|
|
225
|
+
align-items: center;
|
|
226
|
+
gap: 16px;
|
|
227
|
+
padding: 0 16px;
|
|
228
|
+
box-shadow: var(--gemboss-shadow-resting);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.gemboss-account-chip {
|
|
232
|
+
width: 32px;
|
|
233
|
+
height: 32px;
|
|
234
|
+
border: 1px solid var(--gemboss-chip-line);
|
|
235
|
+
border-radius: var(--gemboss-radius-full);
|
|
236
|
+
background: var(--gemboss-surface);
|
|
237
|
+
color: var(--gemboss-chip-ink);
|
|
238
|
+
display: inline-flex;
|
|
239
|
+
align-items: center;
|
|
240
|
+
justify-content: center;
|
|
241
|
+
flex: 0 0 auto;
|
|
242
|
+
font-size: 11px;
|
|
243
|
+
font-weight: 700;
|
|
244
|
+
cursor: pointer;
|
|
245
|
+
transition: border-color var(--gemboss-transition-base) ease, background-color var(--gemboss-transition-base) ease;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.gemboss-account-chip:hover {
|
|
249
|
+
border-color: var(--gemboss-chip-line-hover);
|
|
250
|
+
background: var(--gemboss-chip-bg-hover);
|
|
251
|
+
transform: none;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.icon-nav {
|
|
255
|
+
position: fixed;
|
|
256
|
+
top: 56px;
|
|
257
|
+
left: 0;
|
|
258
|
+
z-index: 200;
|
|
259
|
+
width: 72px;
|
|
260
|
+
height: calc(100dvh - 56px);
|
|
261
|
+
border-right: 1px solid var(--gemboss-divider);
|
|
262
|
+
background: var(--gemboss-nav-bg);
|
|
263
|
+
padding-top: 12px;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.gemboss-platform-main {
|
|
267
|
+
min-height: calc(100vh - 56px);
|
|
268
|
+
margin-top: 56px;
|
|
269
|
+
margin-left: 72px;
|
|
270
|
+
background: var(--gemboss-app-bg); /* authoritative main-content bg (token = #ffffff); was hardcoded #f6f6f4 gray */
|
|
271
|
+
padding: 24px;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
@media (max-width: 1120px) {
|
|
275
|
+
.gemboss-platform-topbar {
|
|
276
|
+
grid-template-columns: 220px minmax(220px, 1fr) auto;
|
|
277
|
+
gap: 12px;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
@media (max-width: 760px) {
|
|
282
|
+
.gemboss-platform-topbar {
|
|
283
|
+
height: auto;
|
|
284
|
+
min-height: 56px;
|
|
285
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
286
|
+
padding: 12px 12px;
|
|
287
|
+
}
|
|
288
|
+
.gemboss-account-chip {
|
|
289
|
+
display: none;
|
|
290
|
+
}
|
|
291
|
+
.icon-nav {
|
|
292
|
+
display: none;
|
|
293
|
+
}
|
|
294
|
+
.gemboss-platform-main {
|
|
295
|
+
margin-left: 0;
|
|
296
|
+
padding: 16px;
|
|
297
|
+
padding-top: 104px;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/* Split from the admin rule ".gemboss-pagepicker-list, .gnav-scroll" (29/09/2026): the admin keeps ".gemboss-pagepicker-list". */
|
|
302
|
+
.gnav-scroll { scrollbar-width: thin; scrollbar-color: rgba(31,30,29,0.22) transparent; }
|
|
303
|
+
|
|
304
|
+
/* Rail footer → a real button that opens the memory/knowledge drawer. */
|
|
305
|
+
.gemboss-assistant-railfoot.is-button { width: 100%; border: none; background: none; cursor: pointer; text-align: left; font: inherit; border-radius: 8px; transition: background 0.12s ease, color 0.12s ease; }
|
|
306
|
+
|
|
307
|
+
.gemboss-assistant-railfoot.is-button:hover { background: var(--gemboss-primary-soft); color: var(--gemboss-primary-dark); }
|
|
308
|
+
|
|
309
|
+
.gemboss-assistant-empty { padding: 24px 20px; color: var(--gemboss-text-muted); font-size: 13px; }
|
|
310
|
+
|
|
311
|
+
@media (max-width: 760px) {
|
|
312
|
+
.gemboss-platform-topbar {
|
|
313
|
+
display: flex;
|
|
314
|
+
align-items: center;
|
|
315
|
+
gap: 8px;
|
|
316
|
+
height: 56px;
|
|
317
|
+
min-height: 56px;
|
|
318
|
+
padding: 0 12px;
|
|
319
|
+
}
|
|
320
|
+
.gemboss-account-chip { display: inline-flex; }
|
|
321
|
+
/* restore — was hidden ≤760px */
|
|
322
|
+
.gemboss-platform-main {
|
|
323
|
+
padding-top: 56px; /* single-row topbar (was 104px) */
|
|
324
|
+
padding-bottom: calc(56px + env(safe-area-inset-bottom));
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
@media (pointer: coarse) {
|
|
329
|
+
/* 44, not 38. The ≤460px block raises this chip for a narrow phone; a touch screen at any width
|
|
330
|
+
deserves the same target, and both bars were measured with room for it (the seller cluster keeps
|
|
331
|
+
17px+ of slack above 460, the admin bar far more). */
|
|
332
|
+
.gemboss-account-chip { width: 44px; height: 44px; }
|
|
333
|
+
/* Store-checklist card. Measured on a Pixel 5 profile (pointer: coarse, 393px): "Mở" was 20px and
|
|
334
|
+
"Nhờ Assistant làm" 32px, and probing with elementFromPoint showed no ::after overlay covering
|
|
335
|
+
the gap — the box WAS the target. Two tiers on purpose (CLAUDE.md §9.1): a link inside a list is
|
|
336
|
+
24px (WCAG 2.5.8 AA), a control is 44px (2.5.5 AAA). Sizing links to 44 here would push the three
|
|
337
|
+
rows apart by 72px for no standard that asks for it. */
|
|
338
|
+
/* The nav drawer's rows and the autonomy dial — the two families left on /app/operator once the
|
|
339
|
+
topbar (#2177) is taken. Measured 09/09/2026 on a Pixel 5 profile at 393px: `.gnav-item` 36px,
|
|
340
|
+
`.gemboss-autonomy-dial button` 32px, and elementFromPoint found NO overlay making either up —
|
|
341
|
+
the dial's own ::after stops at 34px, so somebody started this and did not finish.
|
|
342
|
+
|
|
343
|
+
min-height, not padding or a bigger font: it grows the target without moving the label, so the
|
|
344
|
+
drawer keeps its rhythm. Touch only — raising these boxes under a MOUSE is what collapsed the
|
|
345
|
+
topbar cluster at 320px the last time somebody tried it. */
|
|
346
|
+
.gnav-item { min-height: 44px; }
|
|
347
|
+
/* /app/assistant — the last screen on this list, and the only one a seller opens to TALK.
|
|
348
|
+
Measured 23/09/2026 on production with the official gate (`scripts/visual-gate`, hit test, not a
|
|
349
|
+
hand count): `tap_target_size` failed at 320, 390 AND 768 with five to six controls under the
|
|
350
|
+
floor, and `tap_target_gap` failed with two pairs closer than 8px. Every other admin screen
|
|
351
|
+
measured the same day came back clean, so this is not the Polaris rules above missing a case —
|
|
352
|
+
these three are app-authored buttons that no rule ever covered.
|
|
353
|
+
|
|
354
|
+
The two chrome buttons are simple: `--collapse` declares 34x34 outright, and `--newchat`
|
|
355
|
+
measured 35x35 because its padding is all it has. A floor on both.
|
|
356
|
+
|
|
357
|
+
THE FEEDBACK BUTTONS ARE THE REAL ONE. 👍/👎 under an answer at 27x23, three in a row 4px apart
|
|
358
|
+
— the smallest targets in the admin, on the control that tells us whether an answer was any
|
|
359
|
+
good. Both halves have to move: growing the buttons without the gap leaves two 44px targets
|
|
360
|
+
4px apart, which §9.1 counts as a different failure for the same reason (a thumb lands between
|
|
361
|
+
them). `gap: 8px` is the floor, not a taste.
|
|
362
|
+
|
|
363
|
+
Why `min-width` too: a target is an AREA, and these are the width case — 27px wide is the
|
|
364
|
+
narrower dimension. The comment on the Polaris rule above records the same lesson, learned by
|
|
365
|
+
shipping a height-only fix first. */
|
|
366
|
+
.gemboss-assistant-collapse,
|
|
367
|
+
.gemboss-assistant-newchat { min-height: 44px; min-width: 44px; }
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/* ════ Rail → labeled sidebar, Shopify-admin / GemCRM look (240px) ═════════════════
|
|
371
|
+
Icon+label rows, sub-nav indented under the ACTIVE hub (click to reveal — no hover-
|
|
372
|
+
flyout, no caret), section separators. `gnav-*` classes, and appended LAST so it
|
|
373
|
+
out-cascades the three duplicate 72px `.icon-nav` base blocks earlier in this file. (It used to
|
|
374
|
+
cite `.icon-nav-item` as the thing to avoid; that family has since been deleted, but the duplicate
|
|
375
|
+
`.icon-nav` blocks are still there and still the reason the order matters.) */
|
|
376
|
+
.icon-nav {
|
|
377
|
+
width: 240px;
|
|
378
|
+
padding: 12px 12px 0;
|
|
379
|
+
overflow: hidden;
|
|
380
|
+
/* White rail (Shopify/Polaris look) against the warm-ivory content — the 1px right
|
|
381
|
+
border (from the base rule) gives the clean nav↔content separation. */
|
|
382
|
+
background: var(--gemboss-nav-bg);
|
|
383
|
+
display: flex;
|
|
384
|
+
flex-direction: column;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.gnav-scroll { flex: 1; overflow-y: auto; overflow-x: hidden; }
|
|
388
|
+
|
|
389
|
+
.gnav-bottom { padding-bottom: 12px; }
|
|
390
|
+
|
|
391
|
+
/* top-level item — a rounded row (Polaris density): icon + label */
|
|
392
|
+
.gnav-item {
|
|
393
|
+
display: flex; align-items: center; gap: 12px; width: 100%;
|
|
394
|
+
padding: 8px 12px; margin: 1px 0;
|
|
395
|
+
border: 0; background: transparent; cursor: pointer; text-align: left;
|
|
396
|
+
border-radius: var(--gemboss-radius-md); text-decoration: none; font-family: inherit;
|
|
397
|
+
color: var(--gemboss-text); font-size: var(--gemboss-fs-body); font-weight: 500; line-height: 1.2;
|
|
398
|
+
transition: background-color var(--gemboss-transition-fast) ease, color var(--gemboss-transition-fast) ease;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.gnav-item:hover { background: var(--gemboss-nav-hover); }
|
|
402
|
+
|
|
403
|
+
/* neutral gray on white (Polaris-like) */
|
|
404
|
+
/* Active hub: a full coral pill when you're ON the hub's own page. But when a deeper
|
|
405
|
+
LEAF is the destination, the hub quiets to coral text only — so the leaf below carries
|
|
406
|
+
the strong "you are here" and the two don't compete. */
|
|
407
|
+
.gnav-item.active { color: var(--gemboss-active-fg); font-weight: 600; }
|
|
408
|
+
|
|
409
|
+
.gnav-group:not(.has-active-child) > .gnav-item.active { background: var(--gemboss-active-bg); }
|
|
410
|
+
|
|
411
|
+
.gnav-group:not(.has-active-child) > .gnav-item.active:hover { background: var(--gemboss-active-bg); }
|
|
412
|
+
|
|
413
|
+
.gnav-group.has-active-child > .gnav-item.active:hover { background: var(--gemboss-nav-hover); }
|
|
414
|
+
|
|
415
|
+
.gnav-ico { flex: 0 0 20px; width: 20px; height: 20px; display: grid; place-items: center; }
|
|
416
|
+
|
|
417
|
+
.gnav-ico svg { width: 20px; height: 20px; fill: currentColor; }
|
|
418
|
+
|
|
419
|
+
.gnav-label { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
420
|
+
|
|
421
|
+
/* sub-nav — indented so the child label aligns EXACTLY under the parent hub's label
|
|
422
|
+
(no "thò thụt"/jutting): hub label starts at 44px = 12px row padding + 20px icon + 12px
|
|
423
|
+
gap, so the child's left padding must be 44px too. Shown only for the active hub. */
|
|
424
|
+
.gnav-sub { list-style: none; margin: 1px 0 6px; padding: 0; }
|
|
425
|
+
|
|
426
|
+
.gnav-subitem {
|
|
427
|
+
position: relative;
|
|
428
|
+
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
429
|
+
padding: 6px 12px 6px 44px; margin: 1px 0; border-radius: var(--gemboss-radius-sm);
|
|
430
|
+
color: var(--gemboss-text-muted); font-size: 13px; line-height: 1.2; text-decoration: none;
|
|
431
|
+
white-space: nowrap; transition: background-color var(--gemboss-transition-fast) ease, color var(--gemboss-transition-fast) ease;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/* NGÓN TAY, chỉ trên cảm ứng. Đo 11/09/2026 ở 768px có `hasTouch`, bắn tia từ tâm: vùng bấm thật là
|
|
435
|
+
216×28 — `padding: 6px … ; margin: 1px 0` cho ra cao 27,6px và khe 1px giữa hai mục.
|
|
436
|
+
32px vẫn ĐẠT tầng link-trong-danh-sách (24px, WCAG 2.5.8 AA), nên chiều cao không phải cái hỏng.
|
|
437
|
+
Cái hỏng là KHE: §9.1 đòi hai target cách nhau ≥8px, và chỉ miễn khi cả hai đã ≥44px. Nâng lên 44
|
|
438
|
+
trên cảm ứng vừa đưa mục vào tầng điều khiển vừa làm luật khe hết áp dụng — một thay đổi thay vì hai.
|
|
439
|
+
Chuột không đổi: một sub-nav cao 44px trên desktop sẽ đẩy nội dung xuống mà không ai cần.
|
|
440
|
+
ĐÃ BẮN TIA TRƯỚC KHI PHÓNG TO. `::after` trả `none`, nên không có lớp phủ nào đang âm thầm cấp
|
|
441
|
+
target — kiểm này tồn tại vì fleet từng phóng to một control vốn ĐÃ đủ 44px bằng overlay. */
|
|
442
|
+
@media (pointer: coarse) {
|
|
443
|
+
.gnav-subitem { min-height: 44px; }
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.gnav-subitem:hover { background: var(--gemboss-nav-hover); color: var(--gemboss-text); }
|
|
447
|
+
|
|
448
|
+
/* Active leaf — the strong "you are here": coral pill + a coral accent bar on the tree line. */
|
|
449
|
+
.gnav-subitem.active { color: var(--gemboss-active-fg); font-weight: 600; background: var(--gemboss-active-bg); }
|
|
450
|
+
|
|
451
|
+
.gnav-subitem.active:hover { background: var(--gemboss-active-bg); color: var(--gemboss-active-fg); }
|
|
452
|
+
|
|
453
|
+
.gnav-subitem.active::before {
|
|
454
|
+
content: ""; position: absolute; left: 20px; top: 50%; transform: translateY(-50%);
|
|
455
|
+
width: 3px; height: 15px; border-radius: 2px; background: var(--gemboss-primary);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.gnav-subitem.is-soon { opacity: 0.5; cursor: default; }
|
|
459
|
+
|
|
460
|
+
.gnav-subitem em {
|
|
461
|
+
font-style: normal; font-size: var(--gemboss-fs-micro); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
|
|
462
|
+
border: 1px solid var(--gemboss-divider); border-radius: var(--gemboss-radius-sm); padding: 1px 6px;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/* section separator (Assistant · Settings) */
|
|
466
|
+
.gnav-sep { height: 1px; background: var(--gemboss-divider); margin: 8px 6px; }
|
|
467
|
+
|
|
468
|
+
/* Assistant = the AI front door, not just another row. A coral-tinted pill that stands
|
|
469
|
+
apart from the neutral hubs above; deepens on hover; becomes the standard active pill
|
|
470
|
+
when it's the current page. */
|
|
471
|
+
.gnav-item.gnav-assistant {
|
|
472
|
+
/* Label uses the DARKER coral for WCAG AA (4.77:1 on the 8% pill; the lighter
|
|
473
|
+
--gemboss-primary was 2.87, failed). Icon + pill keep --gemboss-primary as the accent. */
|
|
474
|
+
color: var(--gemboss-primary-dark); font-weight: 600;
|
|
475
|
+
background: color-mix(in srgb, var(--gemboss-primary) 8%, #fff);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.gnav-item.gnav-assistant .gnav-ico svg { fill: var(--gemboss-primary); }
|
|
479
|
+
|
|
480
|
+
.gnav-item.gnav-assistant:hover { background: color-mix(in srgb, var(--gemboss-primary) 15%, #fff); }
|
|
481
|
+
|
|
482
|
+
.gnav-item.gnav-assistant.active { background: var(--gemboss-active-bg); color: var(--gemboss-active-fg); }
|
|
483
|
+
|
|
484
|
+
.gnav-item.gnav-assistant.active .gnav-ico svg { fill: var(--gemboss-active-fg); }
|
|
485
|
+
|
|
486
|
+
/* Content clears the 240px sidebar. The assistant OVERLAYS the content (pops up on top) — it
|
|
487
|
+
must NOT push the content sideways (Chris: "popup lên trên chứ ko đẩy content dịch ra"). Since
|
|
488
|
+
the popup moved to @gemboss/ui's AssistantHost (a fixed-position host, packages/ui/src/assistant.css)
|
|
489
|
+
nothing here shifts for it; the old slide-out panel's rules were deleted 28/09/2026. */
|
|
490
|
+
.gemboss-platform-main { margin-left: 240px; }
|
|
491
|
+
|
|
492
|
+
/* ════ Mobile (Shopify-style): hamburger → left slide-in nav drawer ════════════════
|
|
493
|
+
Desktop hides the hamburger + backdrop; ≤760px the rail becomes an off-canvas drawer
|
|
494
|
+
over a dimmed backdrop, and the bottom tab bar is retired. */
|
|
495
|
+
.gemboss-mobile-menu-btn { display: none; }
|
|
496
|
+
|
|
497
|
+
.gemboss-mobile-nav-backdrop { display: none; }
|
|
498
|
+
|
|
499
|
+
@media (max-width: 760px) {
|
|
500
|
+
.gemboss-platform-main { margin-left: 0; padding-bottom: env(safe-area-inset-bottom); }
|
|
501
|
+
/* hamburger — leftmost in the top bar, mirrors the account chip's hit size */
|
|
502
|
+
.gemboss-mobile-menu-btn {
|
|
503
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
504
|
+
flex: 0 0 auto; width: 38px; height: 38px; margin-left: -2px;
|
|
505
|
+
border: 0; border-radius: 8px; background: transparent; cursor: pointer;
|
|
506
|
+
color: var(--gemboss-text);
|
|
507
|
+
}
|
|
508
|
+
.gemboss-mobile-menu-btn:active { background: var(--gemboss-hover); }
|
|
509
|
+
/* rail → off-canvas drawer (slides over the content; later source beats the
|
|
510
|
+
earlier ≤760 `display:none`) */
|
|
511
|
+
.icon-nav {
|
|
512
|
+
display: flex;
|
|
513
|
+
width: min(82vw, 300px);
|
|
514
|
+
transform: translateX(-100%);
|
|
515
|
+
transition: transform 0.26s cubic-bezier(0.4, 0, 0.2, 1);
|
|
516
|
+
z-index: 1000;
|
|
517
|
+
box-shadow: 2px 0 26px rgba(31, 30, 29, 0.20);
|
|
518
|
+
}
|
|
519
|
+
.gemboss-platform-shell.is-mobile-nav-open .icon-nav { transform: translateX(0); }
|
|
520
|
+
.gemboss-mobile-nav-backdrop {
|
|
521
|
+
display: block; position: fixed; inset: 56px 0 0 0;
|
|
522
|
+
width: 100%; border: 0; padding: 0;
|
|
523
|
+
background: rgba(20, 19, 18, 0.38);
|
|
524
|
+
opacity: 0; pointer-events: none; transition: opacity 0.22s ease;
|
|
525
|
+
z-index: 999;
|
|
526
|
+
}
|
|
527
|
+
.gemboss-platform-shell.is-mobile-nav-open .gemboss-mobile-nav-backdrop { opacity: 1; pointer-events: auto; }
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
@media (max-width: 305.98px) {
|
|
531
|
+
.gemboss-platform-topbar { padding: 0 8px; }
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
@media (min-width: 306px) and (max-width: 460px) {
|
|
535
|
+
.gemboss-platform-topbar { padding: 0 8px; }
|
|
536
|
+
.gemboss-mobile-menu-btn { width: 44px; height: 44px; }
|
|
537
|
+
.gemboss-account-chip { width: 44px; height: 44px; }
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/* ════ Assistant THREAD HISTORY rail (GemCRM AIAssistant model) — a left column of saved
|
|
541
|
+
chat threads + "New chat", with the centered chat column to its right. The layout owns the
|
|
542
|
+
fixed viewport height; the chat column inside fills it. ════════════════════════════════ */
|
|
543
|
+
/* ── Tab trong popup: nhiều đoạn chat cùng sống ─────────────────────────────────────────────
|
|
544
|
+
Trước đây chat gắn khoá theo mã phiên, nên bấm + hay đổi thread là React THÁO component, và
|
|
545
|
+
cleanup của GemChat huỷ luôn lượt đang chạy. Nay mọi đoạn đang mở đều được render; cái không xem
|
|
546
|
+
thì ẩn bằng `hidden` chứ không tháo. Tab chỉ có ở popup — full mode đã có rail liệt kê thread,
|
|
547
|
+
thêm một danh sách nữa trong 600px thì không còn chỗ đọc. */
|
|
548
|
+
.gemboss-assistant-chatslot { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; min-width: 0; }
|
|
549
|
+
|
|
550
|
+
.gemboss-assistant-chatslot[hidden] { display: none; }
|
|
551
|
+
|
|
552
|
+
/* `.gemboss-assistant-page > *` (specificity 0,1,1) sets `flex: 1 1 auto` on every child and BEATS a
|
|
553
|
+
bare class. Measured: the tab strip was being stretched and the chat behind it got clipped under
|
|
554
|
+
the tabs. Matching the parent in the selector is what makes this rule win. */
|
|
555
|
+
.gemboss-assistant-page > .gemboss-assistant-tabs {
|
|
556
|
+
flex: 0 0 auto;
|
|
557
|
+
display: flex;
|
|
558
|
+
align-items: center;
|
|
559
|
+
gap: 4px;
|
|
560
|
+
padding: 4px 8px 0;
|
|
561
|
+
overflow-x: auto;
|
|
562
|
+
scrollbar-width: none;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.gemboss-assistant-tabs::-webkit-scrollbar { display: none; }
|
|
566
|
+
|
|
567
|
+
.gemboss-assistant-tab {
|
|
568
|
+
display: flex;
|
|
569
|
+
align-items: center;
|
|
570
|
+
flex: 0 1 auto;
|
|
571
|
+
min-width: 0;
|
|
572
|
+
max-width: 190px;
|
|
573
|
+
border: 1px solid var(--gemboss-divider);
|
|
574
|
+
border-radius: var(--gemboss-radius-md);
|
|
575
|
+
background: transparent;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.gemboss-assistant-tab.active { background: var(--gemboss-active-bg); border-color: var(--gemboss-active-bg); }
|
|
579
|
+
|
|
580
|
+
.gemboss-assistant-tab-main {
|
|
581
|
+
display: flex;
|
|
582
|
+
align-items: center;
|
|
583
|
+
gap: 6px;
|
|
584
|
+
min-width: 0;
|
|
585
|
+
min-height: 44px; /* §9.1 — một tab là điều khiển, không phải nhãn */
|
|
586
|
+
padding: 0 4px 0 8px;
|
|
587
|
+
border: 0;
|
|
588
|
+
background: transparent;
|
|
589
|
+
font: inherit;
|
|
590
|
+
font-size: var(--gemboss-fs-caption);
|
|
591
|
+
color: var(--gemboss-text-muted);
|
|
592
|
+
cursor: pointer;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
.gemboss-assistant-tab.active .gemboss-assistant-tab-main { color: var(--gemboss-text-strong); }
|
|
596
|
+
|
|
597
|
+
.gemboss-assistant-tab-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
598
|
+
|
|
599
|
+
.gemboss-assistant-tab-dot {
|
|
600
|
+
flex: 0 0 auto;
|
|
601
|
+
width: 6px; height: 6px;
|
|
602
|
+
border-radius: var(--gemboss-radius-full);
|
|
603
|
+
background: var(--gemboss-divider);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.gemboss-assistant-tab-dot.is-running { background: var(--gemboss-primary); animation: gemboss-assistant-pulse 1.4s ease-in-out infinite; }
|
|
607
|
+
|
|
608
|
+
.gemboss-assistant-tab-close {
|
|
609
|
+
flex: 0 0 auto; /* `width` là điểm xuất phát chứ không phải sàn trong flexbox */
|
|
610
|
+
width: 44px; height: 44px;
|
|
611
|
+
display: grid; place-items: center;
|
|
612
|
+
border: 0; background: transparent;
|
|
613
|
+
color: var(--gemboss-text-muted);
|
|
614
|
+
cursor: pointer; padding: 0;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
.gemboss-assistant-tab-close:hover { color: var(--gemboss-text-strong); }
|
|
618
|
+
|
|
619
|
+
.gemboss-assistant-tab-close svg { width: 14px; height: 14px; }
|
|
620
|
+
|
|
621
|
+
.gemboss-assistant-sronly {
|
|
622
|
+
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
|
|
623
|
+
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/* Rail ở full mode: nhiều thread có thể chạy cùng lúc, phải nói ra cái nào. */
|
|
627
|
+
.gemboss-assistant-thread-running {
|
|
628
|
+
flex: 0 0 auto;
|
|
629
|
+
width: 6px; height: 6px;
|
|
630
|
+
border-radius: var(--gemboss-radius-full);
|
|
631
|
+
background: var(--gemboss-primary);
|
|
632
|
+
animation: gemboss-assistant-pulse 1.4s ease-in-out infinite;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
@media (prefers-reduced-motion: reduce) {
|
|
636
|
+
.gemboss-assistant-tab-dot.is-running, .gemboss-assistant-thread-running { animation: none; }
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/* ── Trợ lý chạy nền: một mount duy nhất trong shell ─────────────────────────────────────────
|
|
640
|
+
Trợ lý từng sống trong route /app/assistant, nên bấm sang menu khác là unmount — và cleanup của
|
|
641
|
+
GemChat abort luôn lượt đang chạy. Nay route chỉ XIN chế độ full; chat nằm ở AssistantDockHost.
|
|
642
|
+
|
|
643
|
+
`is-dock` = dạng popup nổi 600px: không có rail lịch sử (264px trong 600px thì không còn chỗ
|
|
644
|
+
đọc câu trả lời — muốn lịch sử thì bấm nút mở toàn màn hình). */
|
|
645
|
+
.gemboss-assistant-route-claim { display: block; height: 100%; }
|
|
646
|
+
|
|
647
|
+
.gemboss-assistant-layout.is-dock { height: auto; min-height: 0; }
|
|
648
|
+
|
|
649
|
+
.gemboss-assistant-layout.is-dock .gemboss-assistant-page {
|
|
650
|
+
height: auto;
|
|
651
|
+
max-width: none;
|
|
652
|
+
padding: 4px 0 8px;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/* Chấm "đang chạy" trên rail: trợ lý vẫn làm việc trong lúc người bán xem màn khác. Đặt bằng class
|
|
656
|
+
trên <body> (AssistantDockContext) thay vì prop, vì IconNav là memo — re-render cả rail mỗi lần
|
|
657
|
+
busy đổi thì đắt hơn giá trị của cái chấm. */
|
|
658
|
+
.gnav-item.gnav-assistant { position: relative; }
|
|
659
|
+
|
|
660
|
+
body.gemboss-assistant-busy .gnav-item.gnav-assistant::after,
|
|
661
|
+
body.gemboss-assistant-unread .gnav-item.gnav-assistant::after {
|
|
662
|
+
content: "";
|
|
663
|
+
position: absolute;
|
|
664
|
+
top: 6px;
|
|
665
|
+
left: 26px;
|
|
666
|
+
width: 8px;
|
|
667
|
+
height: 8px;
|
|
668
|
+
border-radius: 50%;
|
|
669
|
+
border: 2px solid var(--gemboss-surface);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
body.gemboss-assistant-busy .gnav-item.gnav-assistant::after {
|
|
673
|
+
background: var(--gemboss-primary);
|
|
674
|
+
animation: gemboss-assistant-pulse 1.4s ease-in-out infinite;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
body.gemboss-assistant-unread .gnav-item.gnav-assistant::after { background: var(--gemboss-success-strong); }
|
|
678
|
+
|
|
679
|
+
@media (prefers-reduced-motion: reduce) {
|
|
680
|
+
body.gemboss-assistant-busy .gnav-item.gnav-assistant::after { animation: none; }
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
.gemboss-assistant-layout {
|
|
684
|
+
display: flex;
|
|
685
|
+
height: calc(100dvh - 56px);
|
|
686
|
+
width: 100%;
|
|
687
|
+
min-height: 0;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
.gemboss-assistant-rail {
|
|
691
|
+
flex: 0 0 264px;
|
|
692
|
+
width: 264px;
|
|
693
|
+
display: flex;
|
|
694
|
+
flex-direction: column;
|
|
695
|
+
min-height: 0;
|
|
696
|
+
/* Blend with the page (just a hairline divider) — no panel fill, so the sidebar reads light. */
|
|
697
|
+
border-right: 1px solid var(--gemboss-divider);
|
|
698
|
+
background: transparent;
|
|
699
|
+
padding: 16px 12px 12px;
|
|
700
|
+
gap: 12px;
|
|
701
|
+
transition: flex-basis 180ms ease, width 180ms ease;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
.gemboss-assistant-rail-top { display: flex; align-items: center; gap: 6px; }
|
|
705
|
+
|
|
706
|
+
.gemboss-assistant-newchat {
|
|
707
|
+
display: flex;
|
|
708
|
+
align-items: center;
|
|
709
|
+
gap: 8px;
|
|
710
|
+
flex: 1 1 auto;
|
|
711
|
+
min-width: 0;
|
|
712
|
+
padding: 12px 12px;
|
|
713
|
+
border: 1px solid var(--gemboss-divider);
|
|
714
|
+
border-radius: 12px;
|
|
715
|
+
background: transparent;
|
|
716
|
+
color: var(--gemboss-text);
|
|
717
|
+
font-size: var(--gemboss-fs-body);
|
|
718
|
+
font-weight: 600;
|
|
719
|
+
cursor: pointer;
|
|
720
|
+
transition: background 120ms ease, border-color 120ms ease;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
.gemboss-assistant-newchat:hover { background: rgba(0,0,0,0.035); border-color: var(--gemboss-text-muted); }
|
|
724
|
+
|
|
725
|
+
.gemboss-assistant-newchat svg { width: 17px; height: 17px; fill: currentColor; flex: 0 0 auto; }
|
|
726
|
+
|
|
727
|
+
.gemboss-assistant-newchat span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1 1 auto; }
|
|
728
|
+
|
|
729
|
+
/* Keyboard-shortcut hint badge (new-chat + search). Subtle, muted, monospace digits. */
|
|
730
|
+
.gemboss-assistant-kbd {
|
|
731
|
+
flex: 0 0 auto;
|
|
732
|
+
font-size: var(--gemboss-fs-micro);
|
|
733
|
+
font-weight: 600;
|
|
734
|
+
line-height: 1;
|
|
735
|
+
padding: 4px 6px;
|
|
736
|
+
border: 1px solid var(--gemboss-divider);
|
|
737
|
+
border-radius: 6px;
|
|
738
|
+
background: transparent;
|
|
739
|
+
color: var(--gemboss-text-muted);
|
|
740
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/* hide the "/" hint once the search is focused/typed-in */
|
|
744
|
+
.gemboss-assistant-search:focus-within .gemboss-assistant-kbd { display: none; }
|
|
745
|
+
|
|
746
|
+
.gemboss-assistant-collapse {
|
|
747
|
+
flex: 0 0 auto;
|
|
748
|
+
display: grid;
|
|
749
|
+
place-items: center;
|
|
750
|
+
width: 34px;
|
|
751
|
+
height: 34px;
|
|
752
|
+
border: 0;
|
|
753
|
+
border-radius: 8px;
|
|
754
|
+
background: transparent;
|
|
755
|
+
color: var(--gemboss-text-muted);
|
|
756
|
+
cursor: pointer;
|
|
757
|
+
transition: background 120ms ease, color 120ms ease;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
.gemboss-assistant-collapse:hover { background: rgba(0,0,0,0.05); color: var(--gemboss-text); }
|
|
761
|
+
|
|
762
|
+
.gemboss-assistant-collapse svg { width: 16px; height: 16px; fill: currentColor; }
|
|
763
|
+
|
|
764
|
+
/* Search box — filters the thread list (diacritic-insensitive). */
|
|
765
|
+
/* LAYOUT ONLY — the box is `gemboss-field gemboss-field--group` on the same element. */
|
|
766
|
+
.gemboss-assistant-search { gap: 6px; }
|
|
767
|
+
|
|
768
|
+
/* …and no focus-within: `gemboss-field--group` carries the focus ring for every group. */
|
|
769
|
+
.gemboss-assistant-search > svg { width: 15px; height: 15px; fill: var(--gemboss-text-muted); flex: 0 0 auto; }
|
|
770
|
+
|
|
771
|
+
/* …and nothing here: the wrapper is `gemboss-field gemboss-field--group` and the input inside wears
|
|
772
|
+
`gemboss-field__bare`, which is this exact "draw nothing" written once for every such field. */
|
|
773
|
+
.gemboss-assistant-search-clear {
|
|
774
|
+
display: grid; place-items: center;
|
|
775
|
+
width: 20px; height: 20px;
|
|
776
|
+
border: 0; border-radius: 6px;
|
|
777
|
+
background: transparent; color: var(--gemboss-text-muted); cursor: pointer;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
.gemboss-assistant-search-clear:hover { background: rgba(0,0,0,0.07); color: var(--gemboss-text); }
|
|
781
|
+
|
|
782
|
+
.gemboss-assistant-search-clear svg { width: 13px; height: 13px; fill: currentColor; }
|
|
783
|
+
|
|
784
|
+
/* Reassurance footer — durable store memory persists across threads. Muted + light. */
|
|
785
|
+
.gemboss-assistant-railfoot {
|
|
786
|
+
flex: 0 0 auto;
|
|
787
|
+
display: flex;
|
|
788
|
+
align-items: center;
|
|
789
|
+
gap: 6px;
|
|
790
|
+
padding: 8px 12px 2px;
|
|
791
|
+
border-top: 1px solid var(--gemboss-divider);
|
|
792
|
+
margin-top: 4px;
|
|
793
|
+
color: var(--gemboss-text-muted);
|
|
794
|
+
font-size: var(--gemboss-fs-caption);
|
|
795
|
+
line-height: 1.35;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
.gemboss-assistant-railfoot svg { width: 13px; height: 13px; flex: 0 0 auto; fill: currentColor; opacity: 0.8; }
|
|
799
|
+
|
|
800
|
+
.gemboss-assistant-threads { flex: 1 1 auto; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }
|
|
801
|
+
|
|
802
|
+
.gemboss-assistant-threads-empty { padding: 16px 8px; color: var(--gemboss-text-muted); font-size: var(--gemboss-fs-caption); }
|
|
803
|
+
|
|
804
|
+
.gemboss-assistant-group { display: flex; flex-direction: column; gap: 2px; }
|
|
805
|
+
|
|
806
|
+
.gemboss-assistant-group + .gemboss-assistant-group { margin-top: 12px; }
|
|
807
|
+
|
|
808
|
+
.gemboss-assistant-group-label {
|
|
809
|
+
padding: 4px 12px 4px;
|
|
810
|
+
font-size: 11px;
|
|
811
|
+
font-weight: 600;
|
|
812
|
+
letter-spacing: 0.02em;
|
|
813
|
+
color: var(--gemboss-text-muted);
|
|
814
|
+
text-transform: uppercase;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/* Row is a CONTAINER now (so it can hold the open-button + per-row actions); the open area
|
|
818
|
+
is the inner .gemboss-assistant-thread-main button. */
|
|
819
|
+
.gemboss-assistant-thread {
|
|
820
|
+
position: relative;
|
|
821
|
+
display: flex;
|
|
822
|
+
align-items: center;
|
|
823
|
+
border-radius: 8px;
|
|
824
|
+
transition: background 120ms ease;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
.gemboss-assistant-thread:hover { background: var(--gemboss-hover, rgba(0,0,0,0.04)); }
|
|
828
|
+
|
|
829
|
+
.gemboss-assistant-thread.active { background: var(--gemboss-selected, rgba(0,0,0,0.07)); }
|
|
830
|
+
|
|
831
|
+
.gemboss-assistant-thread-main {
|
|
832
|
+
flex: 1 1 auto;
|
|
833
|
+
min-width: 0;
|
|
834
|
+
display: flex;
|
|
835
|
+
flex-direction: column;
|
|
836
|
+
align-items: flex-start;
|
|
837
|
+
gap: 2px;
|
|
838
|
+
padding: 8px 12px;
|
|
839
|
+
border: 0;
|
|
840
|
+
background: transparent;
|
|
841
|
+
text-align: left;
|
|
842
|
+
cursor: pointer;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
.gemboss-assistant-thread-titlerow { display: flex; align-items: center; gap: 6px; width: 100%; min-width: 0; }
|
|
846
|
+
|
|
847
|
+
.gemboss-assistant-pin-dot { width: 12px; height: 12px; flex: 0 0 auto; fill: var(--gemboss-text-muted); }
|
|
848
|
+
|
|
849
|
+
.gemboss-assistant-thread-title {
|
|
850
|
+
flex: 1 1 auto;
|
|
851
|
+
min-width: 0;
|
|
852
|
+
font-size: 13px;
|
|
853
|
+
font-weight: 500;
|
|
854
|
+
color: var(--gemboss-text);
|
|
855
|
+
white-space: nowrap;
|
|
856
|
+
overflow: hidden;
|
|
857
|
+
text-overflow: ellipsis;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
.gemboss-assistant-thread-meta { font-size: var(--gemboss-fs-caption); color: var(--gemboss-text-muted); }
|
|
861
|
+
|
|
862
|
+
/* Row actions menu (kebab → Ghim / Đổi tên / Xuất Markdown / Xóa). */
|
|
863
|
+
.gemboss-assistant-thread.menu-open { background: var(--gemboss-selected, rgba(0,0,0,0.07)); }
|
|
864
|
+
|
|
865
|
+
.gemboss-assistant-thread.menu-open .gemboss-assistant-thread-actions { opacity: 1; }
|
|
866
|
+
|
|
867
|
+
.gemboss-assistant-menu {
|
|
868
|
+
/* position:fixed + top/left set inline (anchored to the kebab) so the overflow:auto
|
|
869
|
+
thread list can't clip it; flips above the button near the viewport bottom. */
|
|
870
|
+
z-index: 60;
|
|
871
|
+
width: 184px;
|
|
872
|
+
padding: 6px;
|
|
873
|
+
display: flex;
|
|
874
|
+
flex-direction: column;
|
|
875
|
+
/* Surface comes from `gemboss-popover-surface` since 25/09/2026. The line it replaced was
|
|
876
|
+
`background: var(--gemboss-bg, #fff)`, and `--gemboss-bg` is declared NOWHERE — so what actually
|
|
877
|
+
painted here was the hardcoded `#fff`, and the token was decoration. One fewer entry in the
|
|
878
|
+
debt `check-tokens-exist` keeps. */
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
.gemboss-assistant-menu-confirm {
|
|
882
|
+
padding: 8px 12px 6px;
|
|
883
|
+
font-size: var(--gemboss-fs-caption);
|
|
884
|
+
font-weight: 600;
|
|
885
|
+
color: var(--gemboss-text);
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
.gemboss-assistant-menu button {
|
|
889
|
+
display: flex;
|
|
890
|
+
align-items: center;
|
|
891
|
+
gap: 8px;
|
|
892
|
+
width: 100%;
|
|
893
|
+
padding: 8px 12px;
|
|
894
|
+
border: 0;
|
|
895
|
+
border-radius: 8px;
|
|
896
|
+
background: transparent;
|
|
897
|
+
text-align: left;
|
|
898
|
+
font-size: 13px;
|
|
899
|
+
color: var(--gemboss-text);
|
|
900
|
+
cursor: pointer;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
.gemboss-assistant-menu button:hover { background: var(--gemboss-hover, rgba(0,0,0,0.05)); }
|
|
904
|
+
|
|
905
|
+
.gemboss-assistant-menu button.danger { color: var(--gemboss-danger-strong); }
|
|
906
|
+
|
|
907
|
+
.gemboss-assistant-menu button svg { width: 15px; height: 15px; fill: currentColor; flex: 0 0 auto; }
|
|
908
|
+
|
|
909
|
+
/* Per-row actions (rename/delete): hidden until row hover/focus, fading over the meta line. */
|
|
910
|
+
.gemboss-assistant-thread-actions {
|
|
911
|
+
position: absolute;
|
|
912
|
+
right: 6px;
|
|
913
|
+
display: flex;
|
|
914
|
+
gap: 2px;
|
|
915
|
+
opacity: 0;
|
|
916
|
+
transition: opacity 120ms ease;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
.gemboss-assistant-thread:hover .gemboss-assistant-thread-actions,
|
|
920
|
+
.gemboss-assistant-thread:focus-within .gemboss-assistant-thread-actions { opacity: 1; }
|
|
921
|
+
|
|
922
|
+
.gemboss-assistant-thread-act {
|
|
923
|
+
display: grid;
|
|
924
|
+
place-items: center;
|
|
925
|
+
width: 26px;
|
|
926
|
+
height: 26px;
|
|
927
|
+
border: 0;
|
|
928
|
+
border-radius: 8px;
|
|
929
|
+
background: transparent;
|
|
930
|
+
color: var(--gemboss-text-muted);
|
|
931
|
+
cursor: pointer;
|
|
932
|
+
transition: background 120ms ease, color 120ms ease;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
.gemboss-assistant-thread-act:hover { background: rgba(0,0,0,0.07); color: var(--gemboss-text); }
|
|
936
|
+
|
|
937
|
+
.gemboss-assistant-thread-act.danger:hover { color: var(--gemboss-danger-strong); }
|
|
938
|
+
|
|
939
|
+
.gemboss-assistant-thread-act svg { width: 15px; height: 15px; fill: currentColor; }
|
|
940
|
+
|
|
941
|
+
/* Inline rename input replacing the row content while editing. */
|
|
942
|
+
.gemboss-assistant-thread.editing { background: var(--gemboss-selected, rgba(0,0,0,0.07)); }
|
|
943
|
+
|
|
944
|
+
.gemboss-assistant-thread-edit { display: flex; align-items: center; gap: 2px; width: 100%; padding: 6px 6px; }
|
|
945
|
+
|
|
946
|
+
/* LAYOUT ONLY — `gemboss-field` on the input owns the look. At (0,1,1) this rule outranked the class
|
|
947
|
+
and kept the rename box in its own family (13px, muted border) right through the conversion. */
|
|
948
|
+
.gemboss-assistant-thread-edit input { flex: 1 1 auto; min-width: 0; }
|
|
949
|
+
|
|
950
|
+
/* …and no focus rule either: `outline: none` here removed the field's own focus ring (WCAG 2.4.7). */
|
|
951
|
+
/* Inside the history layout the chat column drops its own fixed-height/top-padding (the layout
|
|
952
|
+
owns the height) and stops centering — it fills the space beside the rail. */
|
|
953
|
+
.gemboss-assistant-layout .gemboss-assistant-page {
|
|
954
|
+
height: 100%;
|
|
955
|
+
max-width: 820px;
|
|
956
|
+
flex: 1 1 auto;
|
|
957
|
+
/* A flex item's default `min-width: auto` refuses to shrink below its own min-content
|
|
958
|
+
width. Beside the 60px rail on a phone that left the chat column ~367px wide inside
|
|
959
|
+
319px of space, and `.is-assistant-route .gemboss-platform-main` is `overflow: hidden`
|
|
960
|
+
— so the send button, the header line and the composer's right edge were CLIPPED AWAY,
|
|
961
|
+
not scrollable to. `min-width: 0` lets the column take the width it actually has. */
|
|
962
|
+
min-width: 0;
|
|
963
|
+
padding-top: 16px;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
/* While a thread's turns load, keep the current chat visible but subtly dimmed (no blank flash). */
|
|
967
|
+
.gemboss-assistant-layout .gemboss-assistant-page.is-switching { opacity: 0.55; transition: opacity 120ms ease; pointer-events: none; }
|
|
968
|
+
|
|
969
|
+
/* Collapsed rail (desktop toggle, persisted) — and the same shape applies on narrow screens. */
|
|
970
|
+
.gemboss-assistant-rail.is-collapsed { flex-basis: 60px; width: 60px; padding: 16px 8px 12px; }
|
|
971
|
+
|
|
972
|
+
.gemboss-assistant-rail.is-collapsed .gemboss-assistant-rail-top { flex-direction: column; gap: 8px; }
|
|
973
|
+
|
|
974
|
+
.gemboss-assistant-rail.is-collapsed .gemboss-assistant-newchat { justify-content: center; padding: 8px; flex: 0 0 auto; }
|
|
975
|
+
|
|
976
|
+
.gemboss-assistant-rail.is-collapsed .gemboss-assistant-newchat span,
|
|
977
|
+
.gemboss-assistant-rail.is-collapsed .gemboss-assistant-search,
|
|
978
|
+
.gemboss-assistant-rail.is-collapsed .gemboss-assistant-railfoot,
|
|
979
|
+
.gemboss-assistant-rail.is-collapsed .gemboss-assistant-threads { display: none; }
|
|
980
|
+
|
|
981
|
+
@media (max-width: 760px) {
|
|
982
|
+
.gemboss-assistant-rail { flex-basis: 60px; width: 60px; padding: 16px 8px 12px; }
|
|
983
|
+
.gemboss-assistant-rail .gemboss-assistant-rail-top { flex-direction: column; gap: 8px; }
|
|
984
|
+
.gemboss-assistant-newchat { justify-content: center; padding: 8px; flex: 0 0 auto; }
|
|
985
|
+
.gemboss-assistant-newchat span,
|
|
986
|
+
.gemboss-assistant-search,
|
|
987
|
+
.gemboss-assistant-railfoot,
|
|
988
|
+
.gemboss-assistant-threads,
|
|
989
|
+
.gemboss-assistant-collapse { display: none; }
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
/* ════ Assistant — DEDICATED PAGE (Sidekick/GemCRM-style), replaces the slide-out popup.
|
|
993
|
+
A centered chat column filling the height under the 56px top bar. ════════════════════ */
|
|
994
|
+
/* Fit the viewport exactly (like GemCRM) so ONLY the message list scrolls, not the
|
|
995
|
+
page. `main`'s page gutter is zeroed on this route (see `.is-assistant-route`
|
|
996
|
+
below) — which differs desktop (24px) vs mobile (56/0) — so here we only subtract
|
|
997
|
+
the 56px fixed topbar. No gutter dependency = no stray scrollbar at any width. */
|
|
998
|
+
.gemboss-assistant-page {
|
|
999
|
+
height: calc(100dvh - 56px);
|
|
1000
|
+
width: 100%;
|
|
1001
|
+
max-width: 760px;
|
|
1002
|
+
margin: 0 auto;
|
|
1003
|
+
/* Breathing room so the "Ask the Operator" header isn't jammed under the topbar
|
|
1004
|
+
(GemCRM gives its header a clear top gutter). This padding lives INSIDE the
|
|
1005
|
+
fixed-height column — the flex scroll area shrinks to absorb it, so the page
|
|
1006
|
+
still fits the viewport exactly (no scrollbar). box-sizing:border-box. */
|
|
1007
|
+
padding: 24px 0 16px;
|
|
1008
|
+
display: flex;
|
|
1009
|
+
flex-direction: column;
|
|
1010
|
+
min-height: 0;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
.gemboss-assistant-page > * { flex: 1 1 auto; min-height: 0; }
|
|
1014
|
+
|
|
1015
|
+
.gemboss-assistant-empty {
|
|
1016
|
+
flex: 1; display: grid; place-items: center;
|
|
1017
|
+
color: var(--gemboss-text-muted); font-size: 14px;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
/* ════ Kill the phantom 56px page scroll (fixes the assistant page's stray scrollbar).
|
|
1021
|
+
`.gemboss-platform-main` has `margin-top: 56px` (to clear the fixed topbar) and is the
|
|
1022
|
+
shell's only in-flow child, so that top margin COLLAPSES out through the block-level
|
|
1023
|
+
shell — pushing the whole `min-height:100vh` shell down 56px → every page is 56px
|
|
1024
|
+
taller than the viewport. Giving the shell a block formatting context contains the
|
|
1025
|
+
margin: `main` still sits at 56px, but the shell no longer gets shoved down. Visually
|
|
1026
|
+
identical everywhere; just removes the always-on 56px overflow. ════════════════════ */
|
|
1027
|
+
.gemboss-platform-shell { display: flow-root; }
|
|
1028
|
+
|
|
1029
|
+
/* CHỪA CHỖ CHO THIỆP, thay vì để nó đè lên đuôi trang.
|
|
1030
|
+
`--gemboss-invite-h` do chính DesktopInvite.tsx công bố bằng ResizeObserver, và được GỠ khi thẻ
|
|
1031
|
+
biến mất — nên khi không có thiệp, `var(…, 0px)` cho ra đúng padding cũ.
|
|
1032
|
+
|
|
1033
|
+
VÌ SAO LUẬT NÀY NẰM Ở ĐÂY, CUỐI FILE, CHỨ KHÔNG NẰM CẠNH `.gemboss-platform-main`:
|
|
1034
|
+
selector ấy được khai BỐN LẦN trong file này (dòng 285, 770, 2060, 5285), và bản ở 2060 dùng
|
|
1035
|
+
SHORTHAND `padding: 24px`. Shorthand đặt lại cả bốn cạnh, nên một `padding-bottom` viết ở dòng
|
|
1036
|
+
285 sẽ bị xoá sạch mà không có cảnh báo nào — luật vẫn nằm trong file, diff đọc như đã vá, và
|
|
1037
|
+
trang vẫn bị đè. Đó đúng là hình dạng đã để link "All tests" tụt xuống 16px suốt 9 ngày
|
|
1038
|
+
(xem `a-tap-target-rule-outlived-its-markup.test.ts`). Đặt sau cả bốn là cách duy nhất chắc chắn.
|
|
1039
|
+
24px là giá trị của chính bản 2060; nhắc lại ở đây vì shorthand không cho cộng thêm. */
|
|
1040
|
+
.gemboss-platform-main { padding-bottom: calc(24px + var(--gemboss-invite-h, 0px)); }
|
|
1041
|
+
|
|
1042
|
+
@keyframes gemboss-assistant-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
|
|
1043
|
+
|
|
1044
|
+
/* ── AccountMenu (./account.tsx) ───────────────────────────────────────────────────────────────
|
|
1045
|
+
* The chip, the popover and the rows are the `.gemboss-account-*` rules above. These are the
|
|
1046
|
+
* <details> wrapper the popover positions against, the marker reset, and the head block. Until
|
|
1047
|
+
* 29/09/2026 the admin drew that head with INLINE styles in AccountChip.tsx; these are those values
|
|
1048
|
+
* (12/16 padding, divider rule, 13/600 name, 12 muted email, the 11/600 role pill), and since then the
|
|
1049
|
+
* admin renders AccountMenu itself. Moved here from app-shell.css so the admin, which loads this file
|
|
1050
|
+
* and not that one, gets them. */
|
|
1051
|
+
.gb-account { position: relative; }
|
|
1052
|
+
.gb-account > summary { list-style: none; }
|
|
1053
|
+
.gb-account > summary::-webkit-details-marker { display: none; }
|
|
1054
|
+
.gb-account-head { padding: 12px 16px; border-bottom: 1px solid var(--gemboss-divider); }
|
|
1055
|
+
.gb-account-name { font-size: 13px; font-weight: 600; color: var(--gemboss-text-strong); }
|
|
1056
|
+
.gb-account-email { font-size: 12px; color: var(--gemboss-text-muted); }
|
|
1057
|
+
.gb-account-role {
|
|
1058
|
+
display: inline-block; margin-top: 4px; padding: 1px 6px; border-radius: 4px;
|
|
1059
|
+
font-size: 11px; font-weight: 600; color: var(--gemboss-active-fg); background: var(--gemboss-active-bg);
|
|
1060
|
+
}
|
|
1061
|
+
.gb-account-form { margin: 0; }
|
|
1062
|
+
|
|
1063
|
+
/* The rail's WebKit scrollbar (Safari; Chrome follows `scrollbar-width`/`-color` on `.gnav-scroll`).
|
|
1064
|
+
* Split out of the admin's `.gemboss-pagepicker-list, .gnav-scroll` rules on 29/09/2026: #3514 moved
|
|
1065
|
+
* `.gnav-scroll` itself but left these three behind, because the exporter's family matched whole
|
|
1066
|
+
* selectors and these pseudo-element selectors were not on it. Same values as the page picker's. */
|
|
1067
|
+
.gnav-scroll::-webkit-scrollbar { width: 9px; }
|
|
1068
|
+
.gnav-scroll::-webkit-scrollbar-track { background: transparent; }
|
|
1069
|
+
.gnav-scroll::-webkit-scrollbar-thumb { background: rgba(31,30,29,0.2); border-radius: 6px; border: 2px solid transparent; background-clip: content-box; }
|