dsh-pocket-relay 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1005 @@
1
+ /**
2
+ * Mobile stylesheet for the DSH web shell.
3
+ *
4
+ * Hooks are the stable framework attributes only — no hashed classes:
5
+ * - `[data-mobile-nav="frame"]` our marker on the AppFrame element (value-scoped so
6
+ * it never matches the plugin's own controls)
7
+ * - `[data-sidebar-collapsed]` AppFrame: sidebar is in the compact rail state
8
+ * - `[data-side="sidebar"|"details"]` AppFrame drag handles
9
+ * - `[data-shell-overlay]` AppFrame overlay layer (used to locate the frame)
10
+ * - `[data-phase]` conversation root phase (hero|active|settling)
11
+ *
12
+ * Below the official auto-collapse breakpoint (1024px) the rail is removed
13
+ * from the grid entirely; the sidebar column becomes an overlay drawer that
14
+ * slides in when the frame leaves the collapsed state (narrowExpanded).
15
+ */
16
+ export const MOBILE_CSS = `
17
+ /* ---------- base control styles (rendered at any width, hidden where unused) ---------- */
18
+
19
+ [data-mobile-nav="toggle"],
20
+ [data-mobile-nav="files"] {
21
+ display: inline-flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ width: 28px;
25
+ height: 28px;
26
+ flex: none;
27
+ padding: 0;
28
+ border: none;
29
+ border-radius: 50%;
30
+ background: transparent;
31
+ color: var(--dsw-alias-label-secondary, inherit);
32
+ cursor: pointer;
33
+ -webkit-tap-highlight-color: transparent;
34
+ }
35
+ [data-mobile-nav="toggle"]:hover,
36
+ [data-mobile-nav="files"]:hover {
37
+ background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, .06));
38
+ }
39
+ [data-mobile-nav="toggle"]:focus-visible,
40
+ [data-mobile-nav="files"]:focus-visible {
41
+ outline: 2px solid var(--dsw-alias-state-business-primary, #4f6ef7);
42
+ outline-offset: 1px;
43
+ }
44
+
45
+ /* Drawer footer actions: the relocated Session log download plus the Files
46
+ action that opens the dsh-web-ui explorer sheet. */
47
+ [data-mobile-nav="drawer-actions"] {
48
+ display: inline-flex;
49
+ align-items: center;
50
+ gap: 8px;
51
+ }
52
+ /* 宿主没有 aionui explorer 列(官方 DSH 无 dsh-web-ui,issue #48)时隐藏
53
+ 移动端「文件浏览」入口(header 图标 + drawer footer 项)——不然点了没反应。 */
54
+ [data-mobile-nav-explorer="0"] [data-mobile-nav="files"],
55
+ [data-mobile-nav-explorer="0"] [data-mobile-nav="explorer"] {
56
+ display: none !important;
57
+ }
58
+ [data-mobile-nav="session-log"],
59
+ [data-mobile-nav="explorer"] {
60
+ display: inline-flex;
61
+ align-items: center;
62
+ justify-content: center;
63
+ gap: 6px;
64
+ height: 34px;
65
+ padding: 0 12px;
66
+ border: 1px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, .12));
67
+ border-radius: 12px;
68
+ background: transparent;
69
+ color: var(--dsw-alias-label-primary, inherit);
70
+ font-family: inherit;
71
+ font-size: 13px;
72
+ line-height: 20px;
73
+ cursor: pointer;
74
+ -webkit-tap-highlight-color: transparent;
75
+ }
76
+ [data-mobile-nav="session-log"]:hover:not(:disabled),
77
+ [data-mobile-nav="explorer"]:hover {
78
+ background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, .06));
79
+ }
80
+ [data-mobile-nav="session-log"]:disabled {
81
+ color: var(--dsw-alias-label-dimmed, rgba(0, 0, 0, .35));
82
+ cursor: default;
83
+ }
84
+
85
+ /* Floating fallback button (hero / blank phases without a session header).
86
+ The top clears the camera band below the status bar; when the client has
87
+ set viewport-fit=cover the safe-area inset moves it below the notch too. */
88
+ [data-mobile-nav="fab"] {
89
+ position: absolute;
90
+ top: calc(env(safe-area-inset-top, 0px) + 72px);
91
+ left: 10px;
92
+ z-index: 21;
93
+ display: inline-flex;
94
+ align-items: center;
95
+ justify-content: center;
96
+ width: 38px;
97
+ height: 38px;
98
+ padding: 0;
99
+ border: 1px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, .12));
100
+ border-radius: 50%;
101
+ background: var(--dsw-alias-button-floating-fill, #ffffff);
102
+ color: var(--dsw-alias-label-primary, inherit);
103
+ cursor: pointer;
104
+ box-shadow: 0 2px 12px rgba(0, 0, 0, .18);
105
+ -webkit-tap-highlight-color: transparent;
106
+ }
107
+ [data-mobile-nav="fab"]:hover {
108
+ background: var(--dsw-alias-button-floating-hover, rgba(0, 0, 0, .08));
109
+ }
110
+ [data-mobile-nav="fab"]:focus-visible {
111
+ outline: 2px solid var(--dsw-alias-state-business-primary, #4f6ef7);
112
+ outline-offset: 2px;
113
+ }
114
+
115
+ /* Dimmed backdrop under the open drawer; above every column, below the drawer.
116
+ pointer-events: none —— 点击穿透(issue #38):backdrop 只负责视觉压暗,
117
+ 不抢点击。关闭抽屉改由 MobileNavOverlay 的 document 级「抽屉外点击」监听处理
118
+ (等价于原来的点击遮罩关闭,且抽屉内点击不再被 backdrop 吃掉)。 */
119
+ [data-mobile-nav="backdrop"] {
120
+ position: absolute;
121
+ inset: 0;
122
+ z-index: 30;
123
+ background: rgba(0, 0, 0, .45);
124
+ pointer-events: none;
125
+ animation: dsh-mobile-nav-fade .2s var(--ds-ease-in-out, ease-in-out);
126
+ -webkit-tap-highlight-color: transparent;
127
+ }
128
+ @keyframes dsh-mobile-nav-fade {
129
+ from { opacity: 0; }
130
+ to { opacity: 1; }
131
+ }
132
+ /* Settings sheet entrance: the official dialog mounts with no animation at
133
+ all, so it snaps in. Fade + slight rise/scale reads as a proper sheet. */
134
+ @keyframes dsh-mobile-nav-sheet-in {
135
+ from {
136
+ opacity: 0;
137
+ transform: translateY(14px) scale(.98);
138
+ }
139
+ to {
140
+ opacity: 1;
141
+ transform: none;
142
+ }
143
+ }
144
+ /* Preview sheet rise: the aionui preview column opens as a bottom sheet. */
145
+ @keyframes dsh-mobile-nav-sheet-up {
146
+ from {
147
+ opacity: 0;
148
+ transform: translateY(28px);
149
+ }
150
+ to {
151
+ opacity: 1;
152
+ transform: none;
153
+ }
154
+ }
155
+
156
+ /* ---------- mobile-only layout ---------- */
157
+
158
+ @media (max-width: 1023px) {
159
+ /* --- Phone chrome ---
160
+ The system status bar stays visible (no fullscreen). Two adjustments
161
+ make it behave:
162
+ - touch-action: manipulation kills double-tap-to-zoom (and the 300ms
163
+ tap delay) while keeping pan and pinch zoom; the client also
164
+ suppresses legacy-iOS gesturestart as a fallback.
165
+ - With the client's viewport-fit=cover, env(safe-area-inset-top) is the
166
+ status bar / notch height; the rules below push the app content below
167
+ it so the status bar never covers anything. Off notched phones (or in
168
+ a normal browser tab where the layout viewport already sits below the
169
+ status bar) the inset is 0 and nothing shifts. */
170
+ html,
171
+ body {
172
+ touch-action: manipulation !important;
173
+ }
174
+
175
+ /* AppFrame: the drawer takes the sidebar column out of grid flow, so the
176
+ remaining in-flow items (center, details) land in tracks 1..2: give the
177
+ center every pixel and keep the details track at zero. The top padding
178
+ clears the status bar / notch for every in-flow surface (session header,
179
+ messages, composer); the absolutely-positioned drawer is unaffected (its
180
+ containing block is the frame's padding box, i.e. still the frame top). */
181
+ [data-mobile-nav="frame"] {
182
+ position: relative !important;
183
+ grid-template-columns: minmax(0, 1fr) 0 0 !important;
184
+ padding-top: env(safe-area-inset-top, 0px) !important;
185
+ }
186
+
187
+ /* 主内容列(第 2 个网格子元素)在官方样式里有显式 grid-column: 2——
188
+ 网格被压缩成 [1fr, 0, 0] 后它会落在 0px 的第 2 轨,整个主界面被挤出
189
+ 视口(只剩背景图)。必须显式把它拉回第 1 轨(issue #5)。
190
+ 第 3 列(details)保持 0 轨即可,无需处理。 */
191
+ [data-mobile-nav="frame"] > :nth-child(2) {
192
+ grid-column: 1 !important;
193
+ grid-row: 1 !important;
194
+ min-width: 0 !important;
195
+ }
196
+
197
+ /* The sidebar column (first grid child) becomes a left drawer. The drawer
198
+ hugs the sidebar content exactly (the wide sidebar carries an inline
199
+ width, ~280px): a fixed 92vw box would leave a white strip where the
200
+ container background shows beside the content.
201
+ Closed state: translateX(-110%) — more than -100% of the max-content
202
+ width — guarantees the whole drawer (and its shadow, had it one) leaves
203
+ the viewport. A mere -100% leaves a sliver on screen; -105% (as used
204
+ before) left 14px of the drawer plus a long 32px-blur shadow gradient
205
+ visible along the left edge of the main UI. No box-shadow at all: the
206
+ dimmed backdrop already separates drawer from content.
207
+ Z-index note: the backdrop renders inside the shell's overlay layer
208
+ ([data-shell-overlay]), which forms its own stacking context. Third-party
209
+ plugins can force that layer up with !important (dsh-update-checker sets
210
+ it to 500), and when the layer outranks the drawer, the backdrop paints
211
+ ABOVE the drawer and swallows every tap — the drawer opens but no row
212
+ can be pressed (every tap just closes it). The drawer must therefore
213
+ outrank any such raise.
214
+ 1200 (was 600) clears the mobile layers shipped by
215
+ @linxin666/dsh-web-ui-all — its sidebar pane is z-index 1100, its
216
+ details pane 1000 and its full-screen frame ::after mask 1050 (issue
217
+ #67: that mask sat on top of the 600 drawer and ate every tap). Still
218
+ far under the fixed-position banners/toasts (z 9999) that float at the
219
+ viewport level. */
220
+ [data-mobile-nav="frame"] > :first-child {
221
+ position: absolute !important;
222
+ inset: 0 auto 0 0 !important;
223
+ width: max-content !important;
224
+ max-width: 92vw !important;
225
+ z-index: 1200 !important;
226
+ transform: translateX(-110%);
227
+ transition: transform .28s var(--ds-ease-in-out, ease-in-out);
228
+ background: var(--dsw-alias-bg-base, #ffffff);
229
+ /* Keep the drawer's own content below the status bar / notch: the drawer
230
+ spans the full frame height (its absolute containing block is the
231
+ frame's padding box, so the frame's own safe-area padding does NOT
232
+ reach it). The drawer background paints the status-bar strip, which
233
+ the client's theme-color meta matches, so the strip reads seamless. */
234
+ padding-top: env(safe-area-inset-top, 0px) !important;
235
+ /* Kill the official sidebarCol right border: with the backdrop the edge
236
+ reads cleanly, and the settings dialog (width:100% of this box) stays
237
+ pixel-flush with the drawer. */
238
+ border-right: none !important;
239
+ }
240
+
241
+ /* Expanded state (frame without data-sidebar-collapsed) slides the drawer in.
242
+ The open state must be transform:none — NOT translateX(0): an identity
243
+ transform still makes the drawer the containing block for fixed-position
244
+ descendants (the settings dialog's .VOzbGW_overlay is portaled into the
245
+ sidebar DOM). With the identity transform the wide settings sheet
246
+ (100vw-16) overflows the 280px drawer, the dialog's focus scrolls the
247
+ overflow:hidden drawer to scrollLeft=102, and every static child (plus the
248
+ fixed overlay) shifts 102px off-screen. With transform:none the overlay is
249
+ viewport-anchored: it dims the full screen and the sheet sits at left:8. */
250
+ [data-mobile-nav="frame"]:not([data-sidebar-collapsed]) > :first-child {
251
+ transform: none !important;
252
+ }
253
+
254
+ /* Kill a competing full-screen mask (issue #67).
255
+ @linxin666/dsh-web-ui-all ships its own mobile drawer, and part of it is
256
+
257
+ [data-dsh-frame]:not([data-sidebar-collapsed])::after {
258
+ content: ""; position: fixed; inset: 0; z-index: 1050;
259
+ background: rgb(0 0 0 / 24%);
260
+ }
261
+
262
+ The pseudo-element belongs to the frame we already mark, and the frame
263
+ carries only "position: relative" with z-index auto — no stacking context
264
+ — so this mask competes with the drawer in the parent stacking context
265
+ and, at 1050, paints over it. It covers the whole viewport, so every tap
266
+ on a session row lands on the mask instead: the drawer opens but nothing
267
+ inside it can be pressed, and the page behind cannot be scrolled.
268
+ Removing it is safe: the mobile stylesheet already renders its own
269
+ backdrop, and tapping outside the drawer is handled in JS.
270
+
271
+ The attribute selector is repeated on purpose. Their rule has the same
272
+ specificity (0,2,1) once ours is written the obvious way, and plugin
273
+ stylesheets are injected in load order, so a tie would be decided by
274
+ whichever plugin happened to load last. Doubling the attribute makes it
275
+ (0,3,1) and deterministic. */
276
+ [data-mobile-nav="frame"][data-mobile-nav="frame"]:not([data-sidebar-collapsed])::after {
277
+ content: none !important;
278
+ }
279
+
280
+ /* Drag handles are useless on touch and would float over the drawer. */
281
+ [data-side="sidebar"],
282
+ [data-side="details"] {
283
+ display: none !important;
284
+ }
285
+
286
+ /* --- Conversation text on mobile ---
287
+ The official message flow keeps desktop's 32px side gutters and 16px
288
+ type. On a phone: shrink the type a notch and widen the lines by
289
+ trimming the gutters (the sidebar drawer list keeps its size). The
290
+ flow's scroll container is the only _scroll element holding markdown
291
+ <p> paragraphs — the composer's own scroll (textarea) is excluded
292
+ via :has(p). */
293
+ /* The official main scroll body reserves scrollbar-gutter for desktop
294
+ scrollbars (8px), which shoves every column off-center on a phone.
295
+ Classic desktop scrollbars (Edge/Chrome) also occupy ~8-17px in a
296
+ phone-sized viewport, shifting the column further. Mobile scrolling
297
+ is touch/wheel, so remove the scrollbar entirely on phones: the
298
+ column is then exactly centered in every browser. */
299
+ [data-phase] [class$="_scrollBody"] {
300
+ scrollbar-gutter: auto !important;
301
+ scrollbar-width: none !important;
302
+ }
303
+ [data-phase] [class$="_scrollBody"]::-webkit-scrollbar {
304
+ display: none !important;
305
+ width: 0 !important;
306
+ height: 0 !important;
307
+ }
308
+ /* Message action rows (copy / run-time badges) can overflow the right
309
+ edge on narrow screens — keep them inside the message width. */
310
+ [data-phase] [class$="_actions"] {
311
+ overflow: hidden !important;
312
+ }
313
+ [data-phase] [class$="_actions"] [class$="_timeEnd"] {
314
+ flex: 0 1 auto !important;
315
+ min-width: 0 !important;
316
+ overflow: hidden !important;
317
+ text-overflow: ellipsis !important;
318
+ white-space: nowrap !important;
319
+ }
320
+
321
+ [data-phase] [class$="_scroll"]:has(p) {
322
+ padding-left: 20px !important;
323
+ padding-right: 20px !important;
324
+ font-size: 15px !important;
325
+ }
326
+ /* The official markdown styles set an explicit 16px on paragraphs and
327
+ list items, so the container's inherited 15px is not enough. User
328
+ messages render their text in a div whose class carries _text_
329
+ (16px too) — cover it as well. */
330
+ [data-phase] [class$="_scroll"]:has(p) p,
331
+ [data-phase] [class$="_scroll"]:has(p) li,
332
+ [data-phase] [class$="_scroll"]:has(p) [class*="_text_"] {
333
+ font-size: 15px !important;
334
+ }
335
+
336
+ /* Keep DSH's own process disclosures and their expand/collapse behaviour,
337
+ but remove desktop-sized vertical breathing room between consecutive
338
+ context, Skill, and system-prompt entries. */
339
+ [data-phase] [data-turn-process] {
340
+ height: 28px !important;
341
+ padding-bottom: 4px !important;
342
+ margin-bottom: 4px !important;
343
+ }
344
+ [data-phase] [data-turn-process][data-open] {
345
+ margin-bottom: 4px !important;
346
+ }
347
+ [data-phase] [data-disclosure-row] {
348
+ min-height: 24px !important;
349
+ }
350
+ [data-phase] :is([data-context-injection-body], [data-system-prompt-body]) {
351
+ margin-top: 2px !important;
352
+ }
353
+
354
+ /* --- Composer bottom row on mobile ---
355
+ Keep add, permission, model, reasoning and send controls on one line at
356
+ the Honor 50's 360px CSS viewport. DSH's stable data-composer-card hook
357
+ survives the editor's textarea -> contenteditable migration. */
358
+ [data-phase] [data-composer-card="true"] > [class$="_row"] {
359
+ flex-wrap: nowrap !important;
360
+ gap: 6px !important;
361
+ }
362
+ [data-phase] [data-composer-card="true"] > [class$="_row"] > :first-child {
363
+ gap: 8px !important;
364
+ min-width: 0 !important;
365
+ }
366
+ [data-phase] [data-composer-card="true"] > [class$="_row"] > :first-child > :nth-child(2) {
367
+ flex: 0 1 auto !important;
368
+ min-width: 0 !important;
369
+ }
370
+ [data-phase] [data-composer-card="true"] > [class$="_row"] > :last-child {
371
+ flex: 1 1 0 !important;
372
+ min-width: 0 !important;
373
+ gap: 6px !important;
374
+ }
375
+
376
+ /* --- Composer popups as bottom sheets on mobile ---
377
+ Two composer-anchored popups break on phones (field: bottom + side
378
+ cut, only part of the popup visible):
379
+ 1. the model pill menu ([role=menu], max 360px, opens upward from the
380
+ pill inside the composer card);
381
+ 2. the "/" command palette (max 320px card with the search box — it
382
+ hosts the /model popupSelect list: search + provider-grouped rows).
383
+ Both are position:absolute INSIDE the conversation scrollBody
384
+ (overflow:hidden) and the shell center column (overflow:hidden), so
385
+ the scroll containers clip them mid-list. Forensics showed neither
386
+ layer creates a containing block (no transform/contain/will-change),
387
+ so on mobile we snap whichever popup is open to a viewport-anchored
388
+ sheet: fixed positioning escapes the scroll clip entirely, width is
389
+ deterministic, safe-area keeps it off the gesture bar. A transient
390
+ picker covering the composer is standard mobile UX; selection or an
391
+ outside tap still dismisses it. */
392
+ [data-phase] [class$="_root"]:has(> [aria-haspopup="menu"]) > [role="menu"],
393
+ [data-phase] [class$="_card"]:has(> [class$="_search"]) {
394
+ position: fixed !important;
395
+ left: 12px !important;
396
+ right: 12px !important;
397
+ top: auto !important;
398
+ bottom: calc(env(safe-area-inset-bottom, 0px) + 12px) !important;
399
+ width: auto !important;
400
+ min-width: 0 !important;
401
+ max-width: none !important;
402
+ max-height: min(65vh, 480px) !important;
403
+ max-height: min(65dvh, 480px) !important;
404
+ z-index: 130 !important;
405
+ border-radius: 14px !important;
406
+ box-shadow: 0 -4px 28px rgba(0, 0, 0, .18) !important;
407
+ }
408
+
409
+ /* --- Session header on mobile ---
410
+ Layout goal: [toggle] [session title] [mode badge] in a row, with the
411
+ Session log capsule removed from the header (relocated to the drawer
412
+ footer). Stable structural hooks only:
413
+ [data-phase] header the session header element
414
+ header > :first-child titleRow (titleCluster + utilities)
415
+ header > :first-child > :last-child headerUtilities (Session log seat) */
416
+ [data-phase] header {
417
+ padding: 8px 12px 0 !important;
418
+ }
419
+ /* The directory and Files controls are absolutely positioned, so reserve
420
+ their lanes and let the title use the remaining width without squeezing. */
421
+ [data-phase] header > :first-child {
422
+ min-height: 36px !important;
423
+ padding: 0 32px !important;
424
+ }
425
+ [data-phase] header [class$="_titleCluster"],
426
+ [data-phase] header [class$="_crumbs"] {
427
+ min-width: 0 !important;
428
+ }
429
+ [data-phase] header button[class*="_crumb"] {
430
+ max-width: calc(100vw - 104px) !important;
431
+ padding-left: 0 !important;
432
+ padding-right: 0 !important;
433
+ }
434
+ /* The directory toggle sits at the far left of the header (the header
435
+ is position:relative; the data-slot wrappers are display:contents). */
436
+ [data-mobile-nav="toggle"] {
437
+ position: absolute !important;
438
+ left: 8px !important;
439
+ top: 12px !important;
440
+ z-index: 2 !important;
441
+ }
442
+ /* The Files action sits at the FAR RIGHT of the header so it reads as a
443
+ distinct control from the directory toggle on the left (which opens
444
+ the history sidebar). */
445
+ [data-mobile-nav="files"] {
446
+ position: absolute !important;
447
+ left: auto !important;
448
+ right: 8px !important;
449
+ top: 12px !important;
450
+ z-index: 2 !important;
451
+ }
452
+ /* Session log download: gone from the header row on mobile (the utilities
453
+ seat holds only the session-log-export capsule). */
454
+ [data-phase] header > :first-child > :last-child {
455
+ display: none !important;
456
+ }
457
+
458
+ /* --- Settings dialog on mobile ---
459
+ Desktop: 800px two-column flex (188px nav + content). Mobile: a
460
+ near-full-width sheet — nav tabs wrap into rows on top, option rows
461
+ stay horizontal (title+description left, control right). Structural
462
+ selectors are scoped to the unique aria-modal dialog; every
463
+ settings-specific rule is gated with
464
+ :has(> :first-child > :last-child > button) — the settings nav tab
465
+ list holds <button> tabs, so the transient export dialog (the same
466
+ primitives Modal, header(title+close)+description+body) keeps its
467
+ official centered card layout. Requires :has() support
468
+ (Chromium 105+, 2022). */
469
+ [aria-modal="true"]:has(> :first-child > :last-child > button):not(:has([role="navigation"])) {
470
+ position: absolute !important;
471
+ left: 8px !important;
472
+ /* Fixed top (no translateY): a transform on the panel combined with the
473
+ panel overflowing the max-content drawer shifts the fixed overlay's
474
+ coordinate frame, dragging the whole sidebar content off-screen. The
475
+ safe-area inset keeps the sheet below the status bar / notch. */
476
+ top: calc(env(safe-area-inset-top, 0px) + 12px) !important;
477
+ width: calc(100vw - 16px) !important;
478
+ max-width: calc(100vw - 16px) !important;
479
+ /* Height follows the content (no dead space under a short page); it
480
+ caps at 100dvh-24 (less the safe-area top) and the options area
481
+ scrolls only then. */
482
+ height: auto !important;
483
+ max-height: min(800px, calc(100vh - 24px - env(safe-area-inset-top, 0px))) !important;
484
+ max-height: min(800px, calc(100dvh - 24px - env(safe-area-inset-top, 0px))) !important;
485
+ flex-direction: column !important;
486
+ border-radius: 14px !important;
487
+ animation: dsh-mobile-nav-sheet-in .22s var(--ds-ease-out, ease-in-out);
488
+ }
489
+ /* The settings sheet's dimmed mask fades in with the panel (the mask is
490
+ the first child of the overlay that directly contains the sheet). */
491
+ :has(> [aria-modal="true"]:has(> :first-child > :last-child > button):not(:has([role="navigation"]))) > :first-child {
492
+ animation: dsh-mobile-nav-fade .18s var(--ds-ease-out, ease-in-out);
493
+ }
494
+ @media (prefers-reduced-motion: reduce) {
495
+ [aria-modal="true"]:has(> :first-child > :last-child > button):not(:has([role="navigation"])),
496
+ :has(> [aria-modal="true"]:has(> :first-child > :last-child > button):not(:has([role="navigation"]))) > :first-child {
497
+ animation: none !important;
498
+ }
499
+ }
500
+ /* The export dialog (not the settings sheet) must never overflow the
501
+ viewport: the official centered card can be wider than 390px. */
502
+ [aria-modal="true"]:not(:has(> :first-child > :last-child > button)) {
503
+ max-width: calc(100vw - 32px) !important;
504
+ }
505
+ /* Nav bar: hide the "Settings" caption (redundant on a full-width sheet)
506
+ and wrap the tab list so every tab is visible — a horizontal scroll cut
507
+ the last tab ("Plugins") off with no affordance to scroll. */
508
+ [aria-modal="true"]:has(> :first-child > :last-child > button):not(:has([role="navigation"])) > :first-child {
509
+ width: 100% !important;
510
+ flex-direction: row !important;
511
+ align-items: center !important;
512
+ gap: 6px !important;
513
+ padding: 10px 12px 8px !important;
514
+ }
515
+ [aria-modal="true"]:has(> :first-child > :last-child > button):not(:has([role="navigation"])) > :first-child > :first-child {
516
+ display: none !important;
517
+ }
518
+ [aria-modal="true"]:has(> :first-child > :last-child > button):not(:has([role="navigation"])) > :first-child > :last-child {
519
+ flex-direction: row !important;
520
+ flex-wrap: wrap !important;
521
+ width: 100% !important;
522
+ gap: 6px !important;
523
+ overflow: visible !important;
524
+ }
525
+ /* Content toolbar (Open configuration file + close): spread to the edges
526
+ instead of clustering right with a dead zone on the left. The toolbar
527
+ children carry official auto-margins that would defeat space-between,
528
+ so neutralize them. The close button gets a round tappable base so it
529
+ reads as its own control, not part of the outline button. */
530
+ [aria-modal="true"]:has(> :first-child > :last-child > button):not(:has([role="navigation"])) > :last-child > :first-child {
531
+ justify-content: space-between !important;
532
+ align-items: center !important;
533
+ padding: 0 12px !important;
534
+ min-height: 40px !important;
535
+ }
536
+ [aria-modal="true"]:has(> :first-child > :last-child > button):not(:has([role="navigation"])) > :last-child > :first-child > * {
537
+ margin-left: 0 !important;
538
+ margin-right: 0 !important;
539
+ }
540
+ [aria-modal="true"]:has(> :first-child > :last-child > button):not(:has([role="navigation"])) > :last-child > :first-child > :last-child {
541
+ width: 32px !important;
542
+ height: 32px !important;
543
+ border-radius: 50% !important;
544
+ display: inline-flex !important;
545
+ align-items: center !important;
546
+ justify-content: center !important;
547
+ background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, .06)) !important;
548
+ }
549
+ /* Appearance mode cards: the official cube row renders three tall
550
+ vertical cards (~268px) that eat half the sheet. Turn them into a
551
+ compact horizontal trio (icon + label inline, equal widths).
552
+ Relies on the official cube-row class name of this version. */
553
+ [aria-modal="true"] [class$="_cubeRow"] {
554
+ gap: 6px !important;
555
+ }
556
+ [aria-modal="true"] [class$="_cubeRow"] > * {
557
+ flex: 1 1 0 !important;
558
+ flex-direction: row !important;
559
+ align-items: center !important;
560
+ justify-content: center !important;
561
+ gap: 6px !important;
562
+ padding: 10px 8px !important;
563
+ min-height: 0 !important;
564
+ }
565
+ /* Content: the options scroll area gets bottom breathing room so the last
566
+ row never sits flush against the sheet's rounded corner. */
567
+ [aria-modal="true"]:has(> :first-child > :last-child > button):not(:has([role="navigation"])) > :last-child {
568
+ flex: 1 1 auto !important;
569
+ min-height: 0 !important;
570
+ }
571
+ [aria-modal="true"]:has(> :first-child > :last-child > button):not(:has([role="navigation"])) > :last-child > :last-child {
572
+ padding: 0 12px 24px !important;
573
+ }
574
+
575
+ /* ---------- dsh-web-ui family compatibility ----------
576
+ The linxin666 plugin suite extends the shell frame directly:
577
+ - aionui-panel appends two trailing grid columns (explorer / preview)
578
+ plus absolute drag handles to [data-dsh-frame]; its 5-track inline
579
+ grid is already overridden above, but the handles and columns would
580
+ still float over the main UI. On mobile the columns leave the grid
581
+ as floating bottom sheets and keep their own visibility state —
582
+ the suite's collapse chevron / preview tabs still work, so no
583
+ feature is lost. The task-board / ssh plugins inject sidebar
584
+ entries and center-column takeover panels; the entries need
585
+ spacing and the kanban needs scrollable columns. */
586
+
587
+ /* Touch devices: the drag handles are useless — the floating expand
588
+ button is the opener. */
589
+ .aionui-explorer-handle,
590
+ .aionui-preview-handle {
591
+ display: none !important;
592
+ }
593
+
594
+ /* Shared base: both columns leave the grid as floating panels. The
595
+ explorer is gated shut by default (its own persisted expanded state
596
+ must never cover the mobile UI on load); the header Files action opens
597
+ it via the frame marker below, and the sheet's own collapse chevron
598
+ clears it. Preview stays owned by the suite (hidden while no tab is
599
+ open). The per-column rules below override the geometry. */
600
+ [data-aionui-explorer-col],
601
+ [data-aionui-preview-col] {
602
+ position: fixed !important;
603
+ z-index: 55 !important;
604
+ background: var(--aion-bg-base, #ffffff) !important;
605
+ border-left: none !important;
606
+ }
607
+ /* Explorer (file tree) bottom sheet: bottom edge aligned exactly with
608
+ the composer card's bottom line — the card sits 36px above the
609
+ viewport bottom (8px composer padding + the 28px stats strip below
610
+ the card), so the sheet uses the same 36px bottom offset. */
611
+ [data-aionui-explorer-col] {
612
+ visibility: hidden !important;
613
+ left: 8px !important;
614
+ right: 8px !important;
615
+ top: auto !important;
616
+ bottom: 36px !important;
617
+ width: auto !important;
618
+ height: min(55dvh, 460px) !important;
619
+ max-height: calc(100dvh - 44px) !important;
620
+ border-radius: 14px !important;
621
+ overflow: hidden !important;
622
+ box-shadow: 0 -4px 28px rgba(0, 0, 0, .18) !important;
623
+ animation: dsh-mobile-nav-sheet-up .24s var(--ds-ease-out, ease-in-out) !important;
624
+ }
625
+ /* Preview (file content) bottom sheet. Gated shut by default: the suite
626
+ persists open preview tabs in localStorage and restores them on load,
627
+ which would pop the sheet over the fresh UI. The client only sets the
628
+ frame marker after the user taps a file row in the explorer; the
629
+ suite's own collapse chevron clears it via the visibility watcher. */
630
+ [data-aionui-preview-col] {
631
+ visibility: hidden !important;
632
+ position: fixed !important;
633
+ left: 8px !important;
634
+ right: 8px !important;
635
+ top: auto !important;
636
+ bottom: 40px !important;
637
+ width: auto !important;
638
+ height: min(50dvh, 420px) !important;
639
+ max-height: calc(100dvh - 48px) !important;
640
+ border-radius: 14px !important;
641
+ overflow: hidden !important;
642
+ box-shadow: 0 -4px 28px rgba(0, 0, 0, .18) !important;
643
+ z-index: 56 !important;
644
+ animation: dsh-mobile-nav-sheet-up .24s var(--ds-ease-out, ease-in-out) !important;
645
+ }
646
+ /* User-opened preview sheet (frame marker, set on file-row tap). */
647
+ [data-mobile-nav="frame"][data-aionui-preview-open] [data-aionui-preview-col] {
648
+ visibility: visible !important;
649
+ }
650
+ /* The Files action opens the explorer sheet (frame marker). */
651
+ [data-mobile-nav="frame"][data-aionui-explorer-open] [data-aionui-explorer-col] {
652
+ visibility: visible !important;
653
+ }
654
+ /* The open drawer must never sit under a sheet: while the frame is in the
655
+ narrow-expanded state both sheets yield (later in the file than the
656
+ open marker rule, so it wins at equal specificity). */
657
+ [data-mobile-nav="frame"]:not([data-sidebar-collapsed]) [data-aionui-explorer-col],
658
+ [data-mobile-nav="frame"]:not([data-sidebar-collapsed]) [data-aionui-preview-col] {
659
+ visibility: hidden !important;
660
+ }
661
+ /* The suite's own expand button reads the store state we bypass on
662
+ mobile — hide it; the header Files action is the opener. */
663
+ .aionui-floating-expand {
664
+ display: none !important;
665
+ }
666
+
667
+ /* dsh-web-ui sidebar entries (task board / ssh) sit flush against each
668
+ other — give the injected rows breathing room. */
669
+ button[data-dsh-taskboard-entry],
670
+ button[data-dsh-ssh-entry] {
671
+ margin-bottom: 8px !important;
672
+ }
673
+
674
+ /* Task board: five kanban columns at minmax(0,1fr) crush into ~78px phone
675
+ strips. Give every column a usable minimum and let the row scroll. */
676
+ [data-dsh-taskboard-board] > [class$="_columns"] {
677
+ grid-template-columns: repeat(5, minmax(240px, 1fr)) !important;
678
+ overflow-x: auto !important;
679
+ }
680
+ /* The floating button must not float over a takeover panel (task board /
681
+ ssh own the center column while active). */
682
+ html[data-dsh-taskboard-active] [data-mobile-nav="fab"],
683
+ html[data-dsh-ssh-active] [data-mobile-nav="fab"],
684
+ html[data-dsh-taskboard-active] [data-mobile-nav="backdrop"],
685
+ html[data-dsh-ssh-active] [data-mobile-nav="backdrop"] {
686
+ display: none !important;
687
+ }
688
+ /* Board header: let the search field take the slack instead of squeezing
689
+ the action buttons. */
690
+ [data-dsh-taskboard-board] > [class$="_boardHeader"] [class$="_search"] {
691
+ flex: 1 1 auto !important;
692
+ min-width: 80px !important;
693
+ }
694
+
695
+ /* ---------- dsh-web-ui polish: plugin market search ----------
696
+ The market tab row (Discover / Themes / Installed + the plugin search
697
+ box) is a no-wrap flex: at 390px the tabs plus the ~218px search box
698
+ (~475px total) overflow the ~334px sheet and the search box runs off
699
+ the right edge of the screen (it also forces a horizontal scrollbar on
700
+ the sheet's options area). Let the row wrap: the tabs keep the first
701
+ line and the search box gets its own full-width second line. */
702
+
703
+ [aria-modal="true"] [class$="_tabs"] {
704
+ flex-wrap: wrap !important;
705
+ row-gap: 8px !important;
706
+ }
707
+ [aria-modal="true"] [class$="_searchInline"] {
708
+ flex: 1 1 100% !important;
709
+ width: 100% !important;
710
+ max-width: 100% !important;
711
+ }
712
+
713
+ /* ---------- dsh-usage-stats polish: usage & balance panel ----------
714
+ The panel's stats row shows three token counters side by side
715
+ (today / month / total). The counters use tabular nowrap figures whose
716
+ min-content width overflows the ~336px panel body on a phone: figures
717
+ clip at the row's edges and the panel grows a horizontal scrollbar.
718
+ Stack the three counters vertically — full-width rows, so the figures
719
+ always fit. */
720
+
721
+ [class*="usg_"][class$="_statsRow"] {
722
+ flex-direction: column !important;
723
+ }
724
+ [class*="usg_"][class$="_stat"] {
725
+ flex: 0 0 auto !important;
726
+ width: 100% !important;
727
+ min-width: 0 !important;
728
+ }
729
+
730
+ /* ---------- dsh-web-ui polish: settings sheet ----------
731
+ The official dialog is a desktop two-column form; on a phone the
732
+ label/control split leaves a huge dead gap and long descriptions wrap
733
+ into tall stacks. Stack each row (text above, control full-width) and
734
+ compact the nav tabs into an even wrap. */
735
+
736
+ /* Nav tabs: a stable 3-per-row grid (two clean rows instead of a ragged
737
+ wrap) with tighter cells. */
738
+ [aria-modal="true"]:has(> :first-child > :last-child > button):not(:has([role="navigation"])) > :first-child > :last-child {
739
+ display: grid !important;
740
+ grid-template-columns: repeat(3, 1fr) !important;
741
+ gap: 6px !important;
742
+ }
743
+ [aria-modal="true"] [class$="_navCell"] {
744
+ padding: 6px 8px !important;
745
+ gap: 6px !important;
746
+ font-size: 13px !important;
747
+ justify-content: flex-start !important;
748
+ }
749
+ [aria-modal="true"] [class$="_navCell"] svg {
750
+ width: 14px !important;
751
+ height: 14px !important;
752
+ flex: none !important;
753
+ }
754
+ /* Setting rows: text on top, control below at full width. */
755
+ [aria-modal="true"] [class$="_section"] [class$="_row"] {
756
+ flex-direction: column !important;
757
+ align-items: stretch !important;
758
+ gap: 8px !important;
759
+ }
760
+ [aria-modal="true"] [class$="_section"] [class$="_row"] > :first-child {
761
+ width: 100% !important;
762
+ max-width: none !important;
763
+ }
764
+ [aria-modal="true"] [class$="_section"] [class$="_row"] > :last-child {
765
+ width: 100% !important;
766
+ max-width: none !important;
767
+ }
768
+ /* Appearance mode group: give the cube row a consistent bordered
769
+ segmented look (the official borders differ per state). */
770
+ [aria-modal="true"] [class$="_cubeRow"] > * {
771
+ border: 1px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, .12)) !important;
772
+ }
773
+
774
+ /* ---------- dsh-web-ui polish: explorer sheet ----------
775
+ The aionui explorer was designed for a desktop side column: compact the
776
+ header, search box and tree rows so a phone shows more entries, and pad
777
+ the scroll bottom so the last row never sits flush on the edge. */
778
+
779
+ [data-aionui-explorer-col] [class$="_tabBar"] {
780
+ height: 36px !important;
781
+ }
782
+ [data-aionui-explorer-col] [class$="_tabBtn"],
783
+ [data-aionui-explorer-col] [class$="_tabBtnActive"] {
784
+ padding: 0 12px !important;
785
+ font-size: 13px !important;
786
+ }
787
+ [data-aionui-explorer-col] [class$="_searchBox"] {
788
+ height: 32px !important;
789
+ font-size: 13px !important;
790
+ }
791
+ [data-aionui-explorer-col] [class$="_treeRow"] {
792
+ height: 30px !important;
793
+ font-size: 13px !important;
794
+ }
795
+ [data-aionui-explorer-col] [class$="_treeRow"] svg {
796
+ width: 14px !important;
797
+ height: 14px !important;
798
+ }
799
+ [data-aionui-explorer-col] [class$="_scrollArea"] {
800
+ padding-bottom: 28px !important;
801
+ }
802
+
803
+ /* ---------- dsh-web-ui polish: drawer footer ----------
804
+ The injected footer actions (Files + Session log) become two equal pill
805
+ buttons instead of text-width capsules. */
806
+
807
+ /* The official footerActions row also hosts the remote-web-ui entry
808
+ row (two icon buttons); without wrapping the two groups squeeze each
809
+ other on one line. Wrap so each group gets its own full-width row. */
810
+ [data-mobile-nav="frame"] [class$="_footerActions"] {
811
+ flex-wrap: wrap !important;
812
+ gap: 6px !important;
813
+ }
814
+ [data-mobile-nav="drawer-actions"] {
815
+ width: 100% !important;
816
+ }
817
+ [data-mobile-nav="drawer-actions"] > button {
818
+ flex: 1 1 0 !important;
819
+ padding: 0 8px !important;
820
+ white-space: nowrap !important;
821
+ }
822
+
823
+ /* ---------- dsh-web-ui polish: floating pet ----------
824
+ The whale-girl pet (dsh-pet) floats at the viewport corner with a
825
+ persisted, draggable position. On phones the pet is scaled down so
826
+ it does not dominate the screen; the plugin's own drag + persist
827
+ still work (the position itself is left alone — the mobile default
828
+ position is seeded via the pet API to just above the composer). */
829
+
830
+ body > [class$="_float"]:has([class$="_sprite"][role="button"]) {
831
+ transform: scale(.66);
832
+ transform-origin: bottom right;
833
+ }
834
+ /* While a modal dialog (settings sheet / export) owns the screen the pet
835
+ floats ABOVE it and covers the dialog content; modal semantics say the
836
+ background is inert, so hide the pet for the modal's lifetime. */
837
+ body:has([aria-modal="true"]) > [class$="_float"]:has([class$="_sprite"][role="button"]) {
838
+ display: none !important;
839
+ }
840
+
841
+ /* ---------- dsh-web-ui polish: conversation stats line ----------
842
+ The official session-status row (turns / steps / LLM time / TTFT /
843
+ cache) is long. The client marks the exact row with
844
+ [data-mobile-nav="stats"] (text-anchored, hashed classes can't be
845
+ targeted). Layout: ONE fixed-height (28px) flex strip that scrolls
846
+ horizontally — the full metrics stream stays reachable by swiping,
847
+ the row never grows vertically, no ellipsis or fade, 12px gaps
848
+ between metric groups, a 2px scrollbar as the swipe affordance. */
849
+
850
+ [data-mobile-nav="stats"] {
851
+ display: flex !important;
852
+ flex-flow: row nowrap !important;
853
+ align-items: center !important;
854
+ width: 100% !important;
855
+ max-width: 100% !important;
856
+ min-width: 0 !important;
857
+ height: 28px !important;
858
+ min-height: 28px !important;
859
+ max-height: 28px !important;
860
+ box-sizing: border-box !important;
861
+ white-space: nowrap !important;
862
+ overflow-x: auto !important;
863
+ overflow-y: hidden !important;
864
+ -webkit-overflow-scrolling: touch;
865
+ overscroll-behavior-x: contain;
866
+ scrollbar-width: thin !important;
867
+ scrollbar-color: var(--dsw-alias-border-l1, rgba(0, 0, 0, .28)) transparent !important;
868
+ padding: 0 0 4px !important;
869
+ line-height: 20px !important;
870
+ font-size: 12px !important;
871
+ }
872
+ [data-mobile-nav="stats"]::-webkit-scrollbar {
873
+ height: 2px !important;
874
+ }
875
+ [data-mobile-nav="stats"]::-webkit-scrollbar-thumb {
876
+ background: var(--dsw-alias-label-tertiary, rgba(0, 0, 0, .3)) !important;
877
+ border-radius: 2px !important;
878
+ }
879
+ [data-mobile-nav="stats"]::-webkit-scrollbar-track {
880
+ background: transparent !important;
881
+ }
882
+ [data-mobile-nav="stats"] > * {
883
+ display: flex !important;
884
+ flex: 0 0 auto !important;
885
+ flex-flow: row nowrap !important;
886
+ align-items: center !important;
887
+ width: max-content !important;
888
+ min-width: max-content !important;
889
+ max-width: none !important;
890
+ white-space: nowrap !important;
891
+ margin-right: 12px !important;
892
+ padding: 0 !important;
893
+ }
894
+ [data-mobile-nav="stats"] > *:last-child {
895
+ margin-right: 0 !important;
896
+ }
897
+ [data-mobile-nav="stats"] * {
898
+ white-space: nowrap !important;
899
+ }
900
+
901
+ /* ---------- hero composer on mobile ----------
902
+ The official hero card carries a 2-line textarea plus a tall tool row,
903
+ which reads oversized on a phone. Tighten the empty-state rhythm: keep
904
+ the official centered hero, shrink the textarea line box, slim the card
905
+ padding and the tool row, and close the gap under the headline. */
906
+
907
+ [data-phase="hero"] [class$="_card"]:has(textarea) {
908
+ padding-top: 6px !important;
909
+ gap: 8px !important;
910
+ }
911
+ /* The official composer autosizes the textarea and writes an inline
912
+ height (2 lines on the hero empty state) on the textarea's scroll/grow
913
+ wrappers. :placeholder-shown lets us collapse the EMPTY state to one
914
+ line with !important; as soon as the user types, the pseudo-class no
915
+ longer matches and the autosizer's inline height takes over again — so
916
+ multi-line growth keeps working. */
917
+ [data-phase="hero"] textarea:placeholder-shown {
918
+ height: 28px !important;
919
+ }
920
+ [data-phase="hero"] [class$="_card"]:has(textarea:placeholder-shown) > [class$="_scroll"],
921
+ [data-phase="hero"] [class$="_card"]:has(textarea:placeholder-shown) [class$="_grow"] {
922
+ height: 28px !important;
923
+ }
924
+ [data-phase="hero"] [class$="_card"]:has(textarea) > [class$="_row"] {
925
+ padding-top: 2px !important;
926
+ }
927
+ [data-phase="hero"] [class$="_headline"] {
928
+ line-height: 1.15 !important;
929
+ margin-bottom: 0 !important;
930
+ }
931
+ [data-phase="hero"] [class$="_stack"] {
932
+ gap: 0 !important;
933
+ }
934
+
935
+ /* ---------- 隐藏「添加工作区」入口(手机上配工作区无意义,issue #17 修正) ----------
936
+ 图标按钮的 aria-label 随语言变化(zh「添加工作区」/ en「Add workspace」),
937
+ 两种都覆盖;下拉菜单里的「添加工作区…」项由 fileGuard.ts 的 MutationObserver
938
+ 按文案兜底隐藏(CSS 选不到纯文本节点)。只在窄屏生效——桌面端照常保留。 */
939
+ button[aria-label="添加工作区"],
940
+ button[aria-label="添加工作区…"],
941
+ button[aria-label="Add workspace"],
942
+ button[aria-label="Add workspace…"] {
943
+ display: none !important;
944
+ }
945
+
946
+ /* ---------- 文件链接旁的「复制」按钮(issue #17:复制文件内容) ----------
947
+ 挂在对话里的文件链接(<button>/<a>,文案即路径)紧邻位置,由 fileGuard.ts
948
+ 注入。只屏内可见:桌面端不注入、不显示;这里再兜底一层,避免任何遗漏。
949
+ 文件链接多为 inline,按钮用 inline-flex 紧跟其后即可。 */
950
+ [data-mobile-nav="copy-file"] {
951
+ display: inline-flex !important;
952
+ align-items: center;
953
+ justify-content: center;
954
+ margin-left: 6px !important;
955
+ vertical-align: baseline !important;
956
+ height: 22px !important;
957
+ padding: 0 8px !important;
958
+ border: 1px solid var(--dsw-alias-border-l1, rgba(0, 0, 0, .14)) !important;
959
+ border-radius: 6px !important;
960
+ background: var(--dsw-alias-bg-layer-1, #fff) !important;
961
+ color: var(--dsw-alias-label-primary, inherit) !important;
962
+ font-family: inherit !important;
963
+ font-size: 11px !important;
964
+ line-height: 1 !important;
965
+ cursor: pointer !important;
966
+ -webkit-tap-highlight-color: transparent !important;
967
+ box-shadow: 0 1px 3px rgba(0, 0, 0, .12) !important;
968
+ }
969
+ [data-mobile-nav="copy-file"]:active {
970
+ background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, .06)) !important;
971
+ }
972
+ [data-mobile-nav="copy-file"][disabled] {
973
+ opacity: .55 !important;
974
+ cursor: default !important;
975
+ }
976
+ }
977
+
978
+ /* ---------- mobile: stop iOS Safari forced zoom on input focus ----------
979
+ * Inputs are rendered with inline fontSize 13-14px, below the 16px threshold
980
+ * that makes iOS Safari zoom the whole page on focus (and never recover).
981
+ * Force the safe 16px minimum on narrow viewports only, so desktop keeps its
982
+ * tighter metrics. !important is required to beat the inline styles. */
983
+ @media (max-width: 1024px) {
984
+ input,
985
+ textarea,
986
+ [contenteditable="true"] {
987
+ font-size: 16px !important;
988
+ }
989
+ }
990
+
991
+ /* ---------- desktop: the mobile controls must never appear ---------- */
992
+
993
+ @media (min-width: 1024px) {
994
+ [data-mobile-nav="toggle"],
995
+ [data-mobile-nav="files"],
996
+ [data-mobile-nav="fab"],
997
+ [data-mobile-nav="backdrop"],
998
+ [data-mobile-nav="session-log"],
999
+ [data-mobile-nav="explorer"],
1000
+ [data-mobile-nav="drawer-actions"] {
1001
+ display: none !important;
1002
+ }
1003
+ }
1004
+
1005
+ `