app.css
⎇
Raw
1/* Theme palettes. The base is light; dark is applied either by the OS
2 preference (theme = auto, the default) or forced by the theme toggle,
3 which puts `light`/`dark` on <html> (see web/src/theme.rs). Keep the two
4 dark blocks in sync. */
5:root {
6 color-scheme: light;
7 --bg: #f6f7f9;
8 --panel: #ffffff;
9 --text: #1a1d21;
10 --muted: #6b7280;
11 --accent: #3b82f6;
12 --accent-hover: #2563eb;
13 --accent-border: #1d4ed8;
14 --danger: #ef4444;
15 --danger-border: #dc2626;
16 --border: #e5e7eb;
17 --topbar-h: 48px;
18 --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2.5' stroke-linecap='square'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
19}
20
21:root.dark {
22 color-scheme: dark;
23 --bg: #0f1115;
24 --panel: #171a20;
25 --text: #e5e7eb;
26 --muted: #9ca3af;
27 --accent: #60a5fa;
28 --accent-hover: #3b82f6;
29 --accent-border: #79b5fc;
30 --danger: #f87171;
31 --danger-border: #fa8b8b;
32 --border: #2a2f3a;
33 --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='2.5' stroke-linecap='square'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
34}
35
36@media (prefers-color-scheme: dark) {
37 :root:not(.light) {
38 color-scheme: dark;
39 --bg: #0f1115;
40 --panel: #171a20;
41 --text: #e5e7eb;
42 --muted: #9ca3af;
43 --accent: #60a5fa;
44 --accent-hover: #3b82f6;
45 --accent-border: #79b5fc;
46 --danger: #f87171;
47 --danger-border: #fa8b8b;
48 --border: #2a2f3a;
49 --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='2.5' stroke-linecap='square'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
50 }
51}
52
53* {
54 box-sizing: border-box;
55 /* The design language is sharp: everything stays square. */
56 border-radius: 0;
57}
58
59body {
60 margin: 0;
61 font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
62 background: var(--bg);
63 color: var(--text);
64 font-size: 15px;
65}
66
67.muted {
68 color: var(--muted);
69}
70
71.card {
72 background: var(--panel);
73 border: 1px solid var(--border);
74 padding: 24px;
75}
76
77.center-screen {
78 min-height: 100vh;
79 display: flex;
80 flex-direction: column;
81 align-items: center;
82 justify-content: center;
83 padding: 16px;
84 text-align: center;
85}
86
87/* Inside the app frame the topbar already took part of the viewport, so a
88 full 100vh here overflows and shows a scrollbar with nothing to scroll to.
89 Login and setup render .center-screen as the whole page, where the 100vh
90 above is right. */
91.content > .center-screen {
92 min-height: 0;
93 flex: 1;
94}
95
96.auth-card {
97 width: 100%;
98 max-width: 380px;
99}
100
101/* The auth forms have a single action, so its button spans the form. */
102.auth-card .btn-primary {
103 width: 100%;
104}
105
106.auth-card h1 {
107 margin: 0 0 8px;
108 font-size: 22px;
109}
110
111.auth-logo {
112 width: 44px;
113 height: 44px;
114 margin-bottom: 12px;
115}
116
117.auth-card p {
118 margin: 0 0 20px;
119 font-size: 13px;
120}
121
122.field {
123 display: block;
124 margin-bottom: 14px;
125}
126
127.field span {
128 display: block;
129 font-size: 13px;
130 color: var(--muted);
131 margin-bottom: 4px;
132}
133
134input {
135 width: 100%;
136 padding: 10px 12px;
137 border: 1px solid var(--border);
138 background: var(--bg);
139 color: var(--text);
140 font-size: 14px;
141}
142
143input:focus {
144 outline: 2px solid var(--accent);
145 outline-offset: -1px;
146}
147
148/* Square checkboxes — native controls are rounded, the theme is sharp. */
149input[type="checkbox"] {
150 appearance: none;
151 -webkit-appearance: none;
152 width: 16px;
153 height: 16px;
154 margin: 0;
155 padding: 0;
156 flex: none;
157 border: 1px solid var(--border);
158 background: var(--panel);
159 cursor: pointer;
160 position: relative;
161 vertical-align: -3px;
162}
163
164input[type="checkbox"]:checked {
165 background: var(--accent);
166 border-color: var(--accent-border);
167}
168
169input[type="checkbox"]:checked::after {
170 content: "";
171 position: absolute;
172 left: 4px;
173 top: 1px;
174 width: 4px;
175 height: 8px;
176 border: solid #fff;
177 border-width: 0 2px 2px 0;
178 transform: rotate(45deg);
179}
180
181button {
182 font: inherit;
183 cursor: pointer;
184 display: inline-flex;
185 align-items: center;
186 justify-content: center;
187 gap: 6px;
188 border: 1px solid var(--border);
189 background: var(--panel);
190 color: var(--text);
191 padding: 9px 14px;
192}
193
194button:hover {
195 border-color: var(--muted);
196}
197
198button:disabled {
199 opacity: 0.6;
200 cursor: default;
201}
202
203/* No width here on purpose. Full width belongs to the few places that ask
204 for it (.auth-card, .unlock-btn), not to every dialog's action row. */
205.btn-primary {
206 background: var(--accent);
207 /* One step away from the fill, per theme (see palette above), so the
208 button's edge reads the same as on neutral buttons. */
209 border-color: var(--accent-border);
210 color: #fff;
211 font-weight: 600;
212}
213
214.btn-primary:hover {
215 background: var(--accent-hover);
216 border-color: var(--accent-border);
217}
218
219.error {
220 color: var(--danger);
221 font-size: 13px;
222 min-height: 18px;
223 margin: 0 0 10px;
224}
225
226/* layout (signed-in): full-width topbar, then sidebar + content below */
227
228.layout {
229 min-height: 100vh;
230 display: flex;
231 flex-direction: column;
232}
233
234.topbar {
235 flex: none;
236 position: sticky;
237 top: 0;
238 z-index: 130;
239 height: var(--topbar-h);
240 display: flex;
241 align-items: center;
242 justify-content: space-between;
243 gap: 12px;
244 padding: 0 16px;
245 background: var(--panel);
246 border-bottom: 1px solid var(--border);
247}
248
249.topbar-left {
250 display: flex;
251 align-items: center;
252 gap: 10px;
253 min-width: 0;
254}
255
256.icon-btn.nav-toggle {
257 display: none;
258}
259
260.body-row {
261 flex: 1;
262 display: flex;
263 align-items: stretch;
264}
265
266/* ---- sidebar ---- */
267
268.sidebar {
269 flex: none;
270 width: 224px;
271 display: flex;
272 flex-direction: column;
273 background: var(--panel);
274 border-right: 1px solid var(--border);
275 position: sticky;
276 top: var(--topbar-h);
277 height: calc(100vh - var(--topbar-h));
278}
279
280.brand {
281 display: flex;
282 align-items: center;
283 gap: 10px;
284 font-weight: 700;
285 white-space: nowrap;
286 color: inherit;
287 text-decoration: none;
288 cursor: pointer;
289}
290
291.brand:hover {
292 opacity: 0.8;
293}
294
295.brand-name {
296 overflow: hidden;
297 text-overflow: ellipsis;
298}
299
300.logo {
301 width: 26px;
302 height: 26px;
303 flex: none;
304 display: block;
305}
306
307.nav {
308 flex: 1;
309 overflow-y: auto;
310 padding: 14px 8px 16px;
311 display: flex;
312 flex-direction: column;
313 gap: 20px;
314}
315
316.sidebar-user {
317 padding: 0 10px 2px;
318 font-size: 14px;
319 font-weight: 600;
320 overflow: hidden;
321 text-overflow: ellipsis;
322 white-space: nowrap;
323}
324
325.nav-section-label {
326 font-size: 11px;
327 font-weight: 700;
328 text-transform: uppercase;
329 letter-spacing: 0.08em;
330 color: var(--muted);
331 padding: 0 10px 6px;
332}
333
334/* Section header with a right-aligned action (the Files refresh). */
335.nav-section-head {
336 display: flex;
337 align-items: center;
338 justify-content: space-between;
339 gap: 6px;
340 margin-bottom: 4px;
341}
342
343.nav-section-head .nav-section-label {
344 flex: 1;
345 padding-bottom: 2px;
346}
347
348.nav-section-refresh {
349 color: var(--muted);
350 background: none;
351 border-color: transparent;
352 padding: 3px 5px;
353}
354
355.nav-section-refresh:hover {
356 color: var(--text);
357 border-color: var(--border);
358}
359
360.nav-tab {
361 display: flex;
362 align-items: center;
363 justify-content: flex-start;
364 gap: 10px;
365 width: 100%;
366 margin-bottom: 2px;
367 padding: 8px 10px;
368 text-align: left;
369 background: none;
370 border: none;
371 color: var(--muted);
372 font-size: 14px;
373}
374
375.nav-tab:hover {
376 background: var(--bg);
377 color: var(--text);
378}
379
380.nav-tab.active {
381 background: var(--accent);
382 border-color: var(--accent);
383 color: #fff;
384}
385
386.ic-tab {
387 width: 18px;
388 height: 18px;
389 flex: none;
390}
391
392.nav-tab.active .icon {
393 color: #fff;
394}
395
396.nav-tab-name {
397 flex: 1;
398 min-width: 0;
399 overflow: hidden;
400 text-overflow: ellipsis;
401 white-space: nowrap;
402}
403
404.nav-tab-note {
405 flex: none;
406 font-size: 10px;
407 font-weight: 700;
408 padding: 1px 5px;
409 background: var(--bg);
410 border: 1px solid var(--border);
411 color: var(--muted);
412}
413
414.nav-tab.active .nav-tab-note {
415 background: rgb(255 255 255 / 18%);
416 border-color: rgb(255 255 255 / 30%);
417 color: #fff;
418}
419
420.nav-backdrop {
421 display: none;
422}
423
424/* Sidebar footer: theme toggle, log out, and the project version.
425 Sits at the bottom of the sidebar, below the manage section. */
426.sidebar-footer {
427 flex: none;
428 border-top: 1px solid var(--border);
429 padding: 8px;
430}
431
432.sidebar-footer .nav-tab {
433 margin-bottom: 0;
434}
435
436.sidebar-action.logout {
437 color: var(--danger);
438}
439
440.sidebar-action.logout:hover {
441 color: var(--danger);
442}
443
444.sidebar-version {
445 margin-top: 8px;
446 padding: 0 10px;
447 font-size: 11.5px;
448 color: var(--muted);
449 white-space: nowrap;
450 overflow: hidden;
451 text-overflow: ellipsis;
452}
453
454.badge {
455 font-size: 11px;
456 padding: 2px 8px;
457 background: var(--bg);
458 border: 1px solid var(--border);
459 color: var(--muted);
460}
461
462.content {
463 flex: 1;
464 min-width: 0;
465 min-height: 0;
466 display: flex;
467 flex-direction: column;
468 padding: 20px;
469}
470
471/* While a file is open the app is a fixed-height frame: the file view
472 * (editor, image, …) scrolls inside, the page itself never grows. */
473.layout.file-mode {
474 height: 100vh;
475 overflow: hidden;
476}
477
478.layout.file-mode .body-row {
479 min-height: 0;
480}
481
482/* Narrow screens: the sidebar becomes an off-canvas drawer that only
483 covers the content, below the (always visible) topbar. */
484@media (max-width: 860px) {
485 .sidebar {
486 position: fixed;
487 top: var(--topbar-h);
488 bottom: 0;
489 left: 0;
490 right: auto;
491 height: auto;
492 z-index: 120;
493 transform: translateX(-100%);
494 transition: transform 0.15s ease;
495 }
496
497 .sidebar.open {
498 transform: translateX(0);
499 box-shadow: 0 0 48px rgb(0 0 0 / 35%);
500 }
501
502 .nav-backdrop {
503 display: block;
504 position: fixed;
505 top: var(--topbar-h);
506 bottom: 0;
507 left: 0;
508 right: 0;
509 z-index: 110;
510 background: rgb(0 0 0 / 40%);
511 }
512
513 .icon-btn.nav-toggle {
514 display: inline-flex;
515 }
516
517 .content {
518 padding: 14px;
519 }
520}
521
522/* ---- file browser ---- */
523
524.browser {
525 width: 100%;
526 max-width: 1200px;
527 margin: 0 auto;
528 flex: 1;
529 display: flex;
530 flex-direction: column;
531 min-height: 0;
532}
533
534.icon {
535 width: 20px;
536 height: 20px;
537 display: inline-block;
538 vertical-align: middle;
539}
540
541.ic-folder {
542 color: var(--accent);
543}
544
545.ic-muted {
546 color: var(--muted);
547}
548
549.crumbs {
550 display: flex;
551 align-items: center;
552 gap: 2px;
553 padding: 6px 0 12px;
554 font-size: 15px;
555 flex-wrap: wrap;
556}
557
558.crumb {
559 cursor: pointer;
560 color: var(--muted);
561 padding: 3px 6px;
562}
563
564.crumb:hover {
565 color: var(--text);
566 background: var(--panel);
567}
568
569.crumb.current {
570 color: var(--text);
571 font-weight: 600;
572 cursor: default;
573 background: none;
574}
575
576.crumb-sep {
577 color: var(--muted);
578 padding: 0 1px;
579}
580
581.toolbar {
582 display: flex;
583 align-items: center;
584 justify-content: space-between;
585 gap: 6px;
586 padding-bottom: 12px;
587}
588
589.toolbar-right {
590 display: flex;
591 gap: 6px;
592}
593
594/* Sort control in the toolbar. The select mirrors the app's other selects:
595 no native chrome, own arrow. */
596.sort-label {
597 font-size: 13px;
598 color: var(--muted);
599 align-self: center;
600 margin-right: 2px;
601}
602
603.sort-select {
604 appearance: none;
605 width: auto;
606 padding: 7px 32px 7px 10px;
607 border: 1px solid var(--border);
608 background-color: var(--panel);
609 background-image: var(--select-arrow);
610 background-repeat: no-repeat;
611 background-position: right 9px center;
612 background-size: 12px 12px;
613 color: var(--text);
614 font: inherit;
615 font-size: 13px;
616}
617
618.sort-select:focus {
619 outline: 2px solid var(--accent);
620 outline-offset: -1px;
621}
622
623.icon-btn {
624 padding: 7px 9px;
625 border: 1px solid var(--border);
626 background: var(--panel);
627 color: var(--muted);
628 display: inline-flex;
629 align-items: center;
630}
631
632.icon-btn:hover {
633 border-color: var(--muted);
634}
635
636.icon-btn.active {
637 background: var(--accent);
638 border-color: var(--accent);
639 color: #fff;
640}
641
642.ic-btn {
643 width: 18px;
644 height: 18px;
645 color: inherit;
646}
647
648.entries-area {
649 flex: 1;
650 min-height: 240px;
651}
652
653.center-note {
654 text-align: center;
655 padding: 48px 0;
656}
657
658/* Server-side listing cap notice, under the paging footer. */
659.list-note {
660 padding: 8px 14px 0;
661 font-size: 12px;
662}
663
664.error-card {
665 display: flex;
666 align-items: center;
667 justify-content: space-between;
668 gap: 16px;
669 color: var(--danger);
670 font-size: 14px;
671}
672
673/* root tiles were replaced by the sidebar's folder tabs */
674
675/* grid view */
676
677.entries-grid {
678 display: grid;
679 grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
680 gap: 10px;
681}
682
683.tile {
684 background: var(--panel);
685 border: 1px solid var(--border);
686 padding: 16px 10px 12px;
687 text-align: center;
688 cursor: pointer;
689 user-select: none;
690}
691
692.tile:hover {
693 border-color: var(--muted);
694}
695
696.tile-icon {
697 width: 42px;
698 height: 42px;
699 margin: 0 auto 10px;
700 display: block;
701}
702
703.tile-name {
704 font-size: 13px;
705 line-height: 1.3;
706 word-break: break-word;
707 /* Uniform tile height: the name area always takes two lines and
708 never overflows (the full name is in the title tooltip). */
709 min-height: calc(2 * 1.3em);
710 display: -webkit-box;
711 -webkit-line-clamp: 2;
712 -webkit-box-orient: vertical;
713 overflow: hidden;
714}
715
716/* list view */
717
718.entries-list {
719 background: var(--panel);
720 border: 1px solid var(--border);
721 overflow: hidden;
722}
723
724.row {
725 display: grid;
726 grid-template-columns: 28px minmax(0, 1fr) 90px 150px;
727 gap: 12px;
728 align-items: center;
729 padding: 9px 14px;
730 border-bottom: 1px solid var(--border);
731 cursor: pointer;
732 font-size: 14px;
733 user-select: none;
734}
735
736.row:last-child {
737 border-bottom: none;
738}
739
740.row:hover {
741 background: var(--bg);
742}
743
744.row-icon {
745 width: 20px;
746 height: 20px;
747}
748
749.row-name {
750 overflow: hidden;
751 text-overflow: ellipsis;
752 white-space: nowrap;
753}
754
755.row-size,
756.row-date {
757 font-size: 13px;
758 text-align: right;
759}
760
761@media (max-width: 640px) {
762 .row {
763 grid-template-columns: 28px minmax(0, 1fr) 90px;
764 }
765
766 .row-date {
767 display: none;
768 }
769}
770
771/* context menu */
772
773.ctx-menu {
774 position: fixed;
775 z-index: 100;
776 min-width: 190px;
777 background: var(--panel);
778 border: 1px solid var(--border);
779 padding: 5px;
780 box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
781}
782
783.ctx-item {
784 display: flex;
785 align-items: center;
786 gap: 10px;
787 padding: 8px 12px;
788 font-size: 14px;
789 cursor: pointer;
790}
791
792.ic-ctx {
793 width: 18px;
794 height: 18px;
795 flex: none;
796}
797
798.ctx-item:hover {
799 background: var(--bg);
800}
801
802.ctx-item.disabled {
803 opacity: 0.45;
804 cursor: default;
805}
806
807.ctx-item.disabled:hover {
808 background: none;
809}
810
811/* toolbar selection actions (top right) */
812
813.sel-actions {
814 display: flex;
815 align-items: center;
816 gap: 6px;
817 position: relative;
818}
819
820.sel-actions .icon-btn:disabled {
821 opacity: 0.45;
822 cursor: default;
823}
824
825.sel-actions .icon-btn:disabled:hover {
826 border-color: var(--border);
827 color: var(--muted);
828}
829
830.sel-more-wrap {
831 position: relative;
832}
833
834.sel-more-menu {
835 position: absolute;
836 right: 0;
837 top: calc(100% + 6px);
838 z-index: 100;
839 min-width: 190px;
840 background: var(--panel);
841 border: 1px solid var(--border);
842 padding: 5px;
843 box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
844}
845
846.sel-more-menu.hidden {
847 display: none;
848}
849
850/* selection actions in the top bar */
851
852.topbar .sel-actions {
853 min-height: 0;
854}
855
856.topbar .sel-actions .icon-btn {
857 padding: 5px 7px;
858}
859
860/* selection highlight in the grid and list */
861
862.tile.selected {
863 border-color: var(--accent);
864 background: color-mix(in srgb, var(--accent) 12%, var(--panel));
865}
866
867.row.selected {
868 background: color-mix(in srgb, var(--accent) 12%, var(--panel));
869 box-shadow: inset 2px 0 0 var(--accent);
870}
871
872/* settings tabs (profile / server) */
873
874.settings-tabs {
875 display: flex;
876 gap: 8px;
877}
878
879.settings-tab-btn {
880 padding: 7px 14px;
881 border: 1px solid var(--border);
882 background: var(--panel);
883 color: var(--muted);
884 font-size: 13px;
885 font-weight: 600;
886}
887
888.settings-tab-btn:hover {
889 border-color: var(--muted);
890 color: var(--text);
891}
892
893.settings-tab-btn.active {
894 border-color: var(--accent);
895 color: var(--accent);
896 background: color-mix(in srgb, var(--accent) 10%, var(--panel));
897}
898
899/* toast */
900
901.toast {
902 position: fixed;
903 bottom: 20px;
904 right: 20px;
905 z-index: 200;
906 background: var(--panel);
907 color: var(--text);
908 border: 1px solid var(--border);
909 padding: 11px 16px;
910 font-size: 14px;
911 box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
912 max-width: 320px;
913}
914
915.toast-error {
916 background: color-mix(in srgb, var(--danger) 12%, var(--panel));
917 border-color: var(--danger-border);
918 color: var(--danger);
919}
920
921
922/* ---- modals ---- */
923
924/* The shell is a native <dialog> opened with showModal(): the browser puts
925 it in the top layer and paints ::backdrop, so there is no overlay element
926 to position and no z-index to keep in order. The reset below strips the
927 UA's own box so the .modal card inside is the only thing that shows. */
928.modal-host {
929 padding: 0;
930 border: none;
931 background: none;
932 /* The UA caps an open dialog's size; the card sets its own. */
933 max-width: none;
934 max-height: none;
935 /* Centre it in the inset-0 box, as the old flex overlay did. */
936 margin: auto;
937 overflow: visible;
938}
939
940.modal-host::backdrop {
941 background: rgb(0 0 0 / 45%);
942}
943
944.modal {
945 width: 440px;
946 max-width: calc(100vw - 32px);
947 padding: 20px;
948 background: var(--panel);
949 border: 1px solid var(--border);
950 box-shadow: 0 12px 44px rgb(0 0 0 / 35%);
951}
952
953.modal.picker {
954 width: 640px;
955}
956
957.modal-title {
958 margin: 0 0 12px;
959 font-size: 16px;
960}
961
962.modal-label {
963 display: block;
964 font-size: 13px;
965 color: var(--muted);
966 margin-bottom: 6px;
967}
968
969.modal .input {
970 width: 100%;
971 box-sizing: border-box;
972 margin-bottom: 16px;
973}
974
975.modal-label.error,
976.dialog-error {
977 color: var(--danger);
978 font-size: 13px;
979}
980
981.dialog-error {
982 margin-top: 8px;
983}
984
985.modal-message {
986 white-space: pre-line;
987 font-size: 14px;
988 margin: 0 0 8px;
989}
990
991.modal-actions {
992 display: flex;
993 justify-content: flex-end;
994 gap: 8px;
995 margin-top: 16px;
996}
997
998.btn-danger {
999 background: var(--danger);
1000 /* A visible border (one step away from the fill, per theme) so the
1001 button's edge reads the same as on neutral buttons. */
1002 border-color: var(--danger-border);
1003 color: #fff;
1004}
1005
1006.btn-danger:hover {
1007 background: color-mix(in srgb, var(--danger) 85%, black);
1008}
1009
1010.btn:disabled {
1011 opacity: 0.5;
1012 cursor: not-allowed;
1013}
1014
1015.conflict-list {
1016 max-height: 200px;
1017 overflow-y: auto;
1018 margin: 8px 0;
1019 padding-left: 20px;
1020 font-size: 13px;
1021}
1022
1023/* Folder picker: roots on the left, the selected root's tree on the right. */
1024
1025.picker-body {
1026 display: grid;
1027 grid-template-columns: 180px minmax(0, 1fr);
1028 border: 1px solid var(--border);
1029 height: 320px;
1030}
1031
1032.picker-roots {
1033 display: flex;
1034 flex-direction: column;
1035 gap: 2px;
1036 padding: 6px;
1037 border-right: 1px solid var(--border);
1038 background: var(--bg);
1039 min-height: 0;
1040 overflow-y: auto;
1041}
1042
1043.picker-root {
1044 display: flex;
1045 align-items: center;
1046 justify-content: flex-start;
1047 gap: 8px;
1048 padding: 7px 8px;
1049 border: none;
1050 background: transparent;
1051 color: var(--text);
1052 font: inherit;
1053 font-size: 13px;
1054 text-align: left;
1055 cursor: pointer;
1056 min-width: 0;
1057}
1058
1059.picker-root:hover {
1060 background: var(--panel);
1061}
1062
1063.picker-root.active {
1064 background: var(--accent);
1065 color: #fff;
1066}
1067
1068.picker-root.ro .picker-root-name {
1069 opacity: 0.6;
1070 text-decoration: line-through dotted;
1071}
1072
1073.picker-root-ic {
1074 width: 16px;
1075 height: 16px;
1076 flex: none;
1077}
1078
1079.picker-root-name {
1080 overflow: hidden;
1081 text-overflow: ellipsis;
1082 white-space: nowrap;
1083}
1084
1085.picker-main {
1086 display: flex;
1087 flex-direction: column;
1088 min-width: 0;
1089 /* Grid items default to min-height:auto, which would let a long
1090 folder list stretch the dialog instead of scrolling. */
1091 min-height: 0;
1092}
1093
1094.picker-crumbs {
1095 padding: 8px 10px;
1096 font-size: 13px;
1097 border-bottom: 1px solid var(--border);
1098 flex: none;
1099}
1100
1101.picker-entries {
1102 flex: 1;
1103 min-height: 0;
1104 overflow-y: auto;
1105}
1106
1107.picker-row {
1108 display: flex;
1109 align-items: center;
1110 gap: 10px;
1111 padding: 8px 12px;
1112 cursor: pointer;
1113 font-size: 14px;
1114 user-select: none;
1115}
1116
1117.picker-row:hover {
1118 background: var(--bg);
1119}
1120
1121.picker-row .row-name {
1122 flex: 1;
1123}
1124
1125/* A right-pointing chevron drawn with borders (no icon needed). */
1126.picker-chev {
1127 width: 7px;
1128 height: 7px;
1129 border-right: 1.5px solid var(--muted);
1130 border-bottom: 1.5px solid var(--muted);
1131 transform: rotate(-45deg);
1132 flex: none;
1133}
1134
1135.picker-empty {
1136 height: 100%;
1137 display: flex;
1138 flex-direction: column;
1139 align-items: center;
1140 justify-content: center;
1141 gap: 8px;
1142 font-size: 13px;
1143}
1144
1145.picker-empty-ic {
1146 width: 32px;
1147 height: 32px;
1148 opacity: 0.5;
1149}
1150
1151.picker-root.active .picker-root-ic {
1152 color: #fff;
1153}
1154
1155@media (max-width: 640px) {
1156 .picker-body {
1157 grid-template-columns: 1fr;
1158 grid-template-rows: auto minmax(0, 1fr);
1159 height: 380px;
1160 }
1161
1162 .picker-roots {
1163 flex-direction: row;
1164 flex-wrap: wrap;
1165 border-right: none;
1166 border-bottom: 1px solid var(--border);
1167 }
1168}
1169
1170/* ---------------------------------------------------------------------------
1171 * Full-page file view (preview / editor / "no preview"). The top bar carries
1172 * the title + actions; the content area below is edge to edge.
1173 * ------------------------------------------------------------------------- */
1174.content.file-open {
1175 padding: 0;
1176 overflow: hidden;
1177}
1178
1179/* The browser stays mounted (state, fetch callback) but hidden. */
1180.content.file-open .browser {
1181 display: none;
1182}
1183
1184.file-view {
1185 flex: 1;
1186 min-height: 0;
1187 display: flex;
1188 flex-direction: column;
1189}
1190
1191.file-title {
1192 display: flex;
1193 align-items: center;
1194 gap: 8px;
1195 min-width: 0;
1196}
1197
1198.file-title-icon {
1199 width: 18px;
1200 height: 18px;
1201 flex: none;
1202 color: var(--muted);
1203}
1204
1205.file-title-name {
1206 font-weight: 600;
1207 font-size: 14px;
1208 white-space: nowrap;
1209 overflow: hidden;
1210 text-overflow: ellipsis;
1211}
1212
1213.file-actions {
1214 display: flex;
1215 align-items: center;
1216 gap: 10px;
1217 flex: none;
1218}
1219
1220.file-close,
1221.file-download {
1222 border: none;
1223 background: transparent;
1224 color: var(--muted);
1225 padding: 6px 8px;
1226}
1227
1228.file-close:hover,
1229.file-download:hover {
1230 color: var(--text);
1231 background: var(--bg);
1232}
1233.preview-body {
1234 flex: 1;
1235 min-height: 0;
1236 overflow: auto;
1237 display: flex;
1238 background: var(--bg);
1239}
1240.preview-img {
1241 max-width: 100%;
1242 max-height: 100%;
1243 display: block;
1244 margin: auto;
1245}
1246.preview-pdf {
1247 width: 100%;
1248 height: 100%;
1249 border: none;
1250 background: #fff;
1251}
1252.preview-video {
1253 width: 100%;
1254 max-height: 100%;
1255 background: #000;
1256}
1257.preview-audio {
1258 display: flex;
1259 align-items: center;
1260 justify-content: center;
1261 width: 100%;
1262 padding: 24px;
1263}
1264.preview-audio audio {
1265 width: 100%;
1266 max-width: 480px;
1267}
1268
1269/* CodeMirror (text editor) */
1270.cm-container {
1271 height: 100%;
1272}
1273.cm-container .cm-editor {
1274 height: 100%;
1275}
1276/* Load/save failure: dims the editor and shows the message in a card
1277 * (the top-bar Save button stays reachable to retry). */
1278.cm-error {
1279 position: absolute;
1280 inset: 0;
1281 z-index: 5;
1282 display: flex;
1283 align-items: center;
1284 justify-content: center;
1285 padding: 24px;
1286 background: rgb(0 0 0 / 45%);
1287}
1288
1289.cm-error-card {
1290 position: relative;
1291 max-width: 440px;
1292 min-width: 200px;
1293 background: var(--panel);
1294 border: 1px solid var(--border);
1295 box-shadow: 0 12px 44px rgb(0 0 0 / 35%);
1296}
1297
1298.cm-error-close {
1299 position: absolute;
1300 top: 7px;
1301 right: 7px;
1302 display: inline-flex;
1303 align-items: center;
1304 justify-content: center;
1305 border: none;
1306 background: transparent;
1307 color: var(--muted);
1308 padding: 3px;
1309}
1310
1311.cm-error-close:hover {
1312 color: var(--text);
1313 background: var(--bg);
1314}
1315
1316.cm-error-close .ic-btn {
1317 width: 16px;
1318 height: 16px;
1319}
1320
1321.cm-error-text {
1322 padding: 16px 40px 16px 20px;
1323 font-size: 14px;
1324 text-align: center;
1325 overflow-wrap: anywhere;
1326}
1327
1328/* ---------------------------------------------------------------------------
1329 * Text editor (full page; title/save/close live in the top bar)
1330 * ------------------------------------------------------------------------- */
1331.file-editor {
1332 flex: 1;
1333 min-height: 0;
1334 display: flex;
1335 position: relative;
1336 background: var(--bg);
1337}
1338.file-editor .cm-container {
1339 width: 100%;
1340 height: 100%;
1341}
1342/* "No preview" fallback (binaries, archives). */
1343.file-nopreview {
1344 flex: 1;
1345 display: flex;
1346 align-items: center;
1347 justify-content: center;
1348}
1349.file-nopreview .btn-primary {
1350 margin-top: 14px;
1351}
1352/* ---------------------------------------------------------------------------
1353 * Download format dialog
1354 * ------------------------------------------------------------------------- */
1355.dl-formats {
1356 display: flex;
1357 flex-direction: column;
1358 gap: 8px;
1359 margin-bottom: 8px;
1360}
1361.dl-fmt {
1362 justify-content: flex-start;
1363}
1364
1365/* ---------------------------------------------------------------------------
1366 * Item info dialog
1367 * ------------------------------------------------------------------------- */
1368.info-list {
1369 margin: 8px 0 16px;
1370 display: grid;
1371 grid-template-columns: 90px 1fr;
1372 row-gap: 8px;
1373 column-gap: 12px;
1374}
1375.info-list dt {
1376 color: var(--muted);
1377}
1378.info-list dd {
1379 margin: 0;
1380 word-break: break-word;
1381}
1382
1383/* ---------------------------------------------------------------------------
1384 Shares (milestone 6)
1385 --------------------------------------------------------------------------- */
1386
1387.share-modal {
1388 width: 480px;
1389}
1390
1391.share-link-row {
1392 display: flex;
1393 gap: 8px;
1394 margin: 12px 0;
1395}
1396
1397.share-link-input {
1398 flex: 1;
1399 font-family: ui-monospace, monospace;
1400 font-size: 12.5px;
1401 padding: 8px 10px;
1402 border: 1px solid var(--border);
1403 background: var(--bg);
1404 color: var(--text);
1405 min-width: 0;
1406}
1407
1408.share-check {
1409 display: flex;
1410 align-items: center;
1411 gap: 8px;
1412 margin: 4px 0 14px;
1413 font-size: 14px;
1414}
1415
1416.share-check input {
1417 width: 16px;
1418 height: 16px;
1419}
1420
1421.share-expiry-row {
1422 display: flex;
1423 align-items: center;
1424 gap: 10px;
1425 margin: 0 0 10px;
1426}
1427
1428.share-expiry-row label {
1429 font-size: 13px;
1430 color: var(--muted);
1431}
1432
1433.share-expiry-row select,
1434.share-expiry-row input {
1435 flex: 1;
1436 padding: 8px 10px;
1437 border: 1px solid var(--border);
1438 background: var(--panel);
1439 color: var(--text);
1440 font: inherit;
1441}
1442
1443/* Optional password on the create-share form. Laid out like the expiry
1444 row above it, so the form reads as one column of labelled fields. */
1445.share-pw-row {
1446 display: flex;
1447 align-items: center;
1448 gap: 10px;
1449 margin: 0 0 6px;
1450}
1451
1452.share-pw-row label {
1453 font-size: 13px;
1454 color: var(--muted);
1455}
1456
1457/* Expiry and password sit in one column, so their inputs line up. */
1458.share-expiry-row label,
1459.share-pw-row label {
1460 min-width: 64px;
1461}
1462
1463.share-pw-row input {
1464 flex: 1;
1465 padding: 8px 10px;
1466 border: 1px solid var(--border);
1467 background: var(--panel);
1468 color: var(--text);
1469 font: inherit;
1470}
1471
1472.share-pw-hint {
1473 font-size: 12px;
1474 margin: 0 0 10px;
1475}
1476
1477/* The password gate of a protected share: the whole page is this card. */
1478.unlock-card {
1479 width: 320px;
1480 max-width: calc(100vw - 32px);
1481 padding: 20px;
1482 text-align: left;
1483}
1484
1485.unlock-card h2 {
1486 margin: 0 0 6px;
1487 font-size: 18px;
1488}
1489
1490.unlock-card input {
1491 margin-top: 14px;
1492}
1493
1494.unlock-btn {
1495 width: 100%;
1496 margin-top: 12px;
1497}
1498
1499.share-expiry-note {
1500 margin: 4px 0 0;
1501 font-size: 12.5px;
1502}
1503
1504.btn-sm {
1505 padding: 5px 10px;
1506 font-size: 12.5px;
1507}
1508
1509.icon-btn-sm {
1510 padding: 4px 6px;
1511}
1512
1513/* "Your shares" manage view */
1514
1515.shares-view {
1516 width: 100%;
1517 max-width: 960px;
1518 margin: 0 auto;
1519}
1520
1521.view-header {
1522 margin-bottom: 16px;
1523}
1524
1525.view-header .admin-section-title {
1526 margin-bottom: 0;
1527}
1528
1529.view-hint {
1530 margin: 4px 0 0;
1531 font-size: 13px;
1532 max-width: 64ch;
1533}
1534
1535.share-list {
1536 display: flex;
1537 flex-direction: column;
1538 gap: 10px;
1539}
1540
1541.share-item {
1542 display: flex;
1543 align-items: center;
1544 justify-content: space-between;
1545 gap: 12px;
1546 padding: 10px 12px;
1547 border: 1px solid var(--border);
1548 background: var(--panel);
1549}
1550
1551.share-item-main {
1552 min-width: 0;
1553}
1554
1555.share-item-name {
1556 display: flex;
1557 align-items: center;
1558 gap: 8px;
1559 font-weight: 600;
1560 font-size: 14px;
1561 overflow: hidden;
1562 text-overflow: ellipsis;
1563 white-space: nowrap;
1564}
1565
1566.ic-row {
1567 width: 18px;
1568 height: 18px;
1569 flex: none;
1570}
1571
1572.share-item-meta {
1573 font-size: 12px;
1574 margin-top: 2px;
1575 overflow: hidden;
1576 text-overflow: ellipsis;
1577 white-space: nowrap;
1578}
1579
1580.share-item-actions {
1581 display: flex;
1582 gap: 6px;
1583 flex-shrink: 0;
1584}
1585
1586/* ------------------------------------------------------------------ */
1587/* Admin (milestone 7) */
1588/* ------------------------------------------------------------------ */
1589
1590.admin-view {
1591 display: flex;
1592 flex-direction: column;
1593 gap: 16px;
1594 width: 100%;
1595 max-width: 960px;
1596 margin: 0 auto;
1597}
1598
1599.admin-section-title {
1600 margin: 0 0 14px;
1601 font-size: 15px;
1602}
1603
1604.users-header {
1605 display: flex;
1606 align-items: center;
1607 justify-content: space-between;
1608 margin-bottom: 4px;
1609}
1610
1611.users-header .admin-section-title {
1612 margin-bottom: 0;
1613}
1614
1615.users-header .btn-primary {
1616 width: auto;
1617}
1618
1619.user-list {
1620 display: flex;
1621 flex-direction: column;
1622 gap: 10px;
1623}
1624
1625.user-row {
1626 display: flex;
1627 align-items: center;
1628 justify-content: space-between;
1629 gap: 12px;
1630 padding: 12px 14px;
1631 border: 1px solid var(--border);
1632}
1633
1634.user-row-main {
1635 min-width: 0;
1636}
1637
1638.user-row-name {
1639 display: flex;
1640 align-items: center;
1641 gap: 6px;
1642 font-weight: 600;
1643 font-size: 14px;
1644 flex-wrap: wrap;
1645}
1646
1647.ic-admin {
1648 width: 16px;
1649 height: 16px;
1650 flex: none;
1651}
1652
1653.user-row-actions {
1654 display: flex;
1655 gap: 6px;
1656 flex-shrink: 0;
1657}
1658
1659.badge-off {
1660 background: color-mix(in srgb, var(--danger) 15%, var(--panel));
1661 border-color: var(--danger);
1662 color: var(--danger);
1663}
1664
1665.setting-row {
1666 display: flex;
1667 align-items: center;
1668 gap: 10px;
1669 padding: 14px;
1670 border: 1px solid var(--border);
1671 cursor: pointer;
1672}
1673
1674.setting-row input {
1675 margin-left: auto;
1676}
1677
1678.setting-label {
1679 display: block;
1680 font-weight: 600;
1681 font-size: 14px;
1682}
1683
1684.setting-desc {
1685 display: block;
1686 font-size: 12px;
1687 color: var(--muted);
1688 margin-top: 2px;
1689}
1690
1691/* Setting row whose control is a select, pushed to the right. */
1692.setting-row-select {
1693 cursor: default;
1694}
1695
1696.setting-row-select > span {
1697 flex: 1;
1698}
1699
1700.setting-row-select select {
1701 appearance: none;
1702 margin-left: auto;
1703 padding: 10px 42px 10px 12px;
1704 border: 1px solid var(--border);
1705 background-color: var(--panel);
1706 background-image: var(--select-arrow);
1707 background-repeat: no-repeat;
1708 background-position: right 12px center;
1709 background-size: 14px 14px;
1710 color: var(--text);
1711 font: inherit;
1712 max-width: 190px;
1713}
1714
1715/* User create/edit form */
1716
1717.user-form {
1718 width: 520px;
1719}
1720
1721.field-input {
1722 width: 100%;
1723 box-sizing: border-box;
1724}
1725
1726/* Search exclusions: a bordered block matching the setting row above it. */
1727.excludes-field {
1728 display: flex;
1729 flex-direction: column;
1730 gap: 10px;
1731 padding: 14px;
1732 border: 1px solid var(--border);
1733}
1734
1735.excludes-list {
1736 display: flex;
1737 flex-direction: column;
1738 gap: 2px;
1739}
1740
1741.exclude-row {
1742 display: flex;
1743 align-items: center;
1744 gap: 8px;
1745 padding: 4px 0;
1746}
1747
1748.exclude-path {
1749 flex: 1;
1750 font-size: 13px;
1751 overflow: hidden;
1752 text-overflow: ellipsis;
1753 white-space: nowrap;
1754}
1755
1756.exclude-remove {
1757 flex: none;
1758 padding: 4px 6px;
1759}
1760
1761.exclude-add {
1762 align-self: flex-start;
1763}
1764
1765.excludes-empty {
1766 font-size: 13px;
1767 margin: 0;
1768}
1769
1770.field-hint {
1771 font-size: 12px;
1772 margin: -2px 0 8px;
1773}
1774
1775.check-row {
1776 display: flex;
1777 align-items: center;
1778 gap: 8px;
1779 font-size: 14px;
1780 margin-bottom: 12px;
1781 cursor: pointer;
1782}
1783
1784.form-error {
1785 color: var(--danger);
1786 font-size: 13px;
1787 margin: 4px 0 8px;
1788}
1789
1790.roots-editor {
1791 display: flex;
1792 flex-direction: column;
1793 gap: 6px;
1794}
1795
1796.root-draft {
1797 display: flex;
1798 align-items: center;
1799 gap: 8px;
1800}
1801
1802.root-draft-path {
1803 flex: 1;
1804 font-size: 13px;
1805 overflow: hidden;
1806 text-overflow: ellipsis;
1807 white-space: nowrap;
1808}
1809
1810.root-draft-mode {
1811 width: auto;
1812 padding: 6px 8px;
1813}
1814
1815.root-add {
1816 align-self: flex-start;
1817 margin-top: 4px;
1818}
1819
1820/* ------------------------------------------------------------------ */
1821/* Empty state + keyboard focus (milestone 8) */
1822/* ------------------------------------------------------------------ */
1823
1824.empty-state {
1825 display: flex;
1826 flex-direction: column;
1827 align-items: center;
1828 justify-content: center;
1829 gap: 8px;
1830 padding: 64px 20px;
1831 text-align: center;
1832}
1833
1834.empty-glyph {
1835 width: 52px;
1836 height: 52px;
1837}
1838
1839.empty-state h3 {
1840 margin: 0;
1841 font-size: 16px;
1842}
1843
1844/* Visible keyboard focus on selectable file/folder entries. The ring is
1845 inset (negative offset) so the scroll container never clips it. */
1846.tile:focus-visible,
1847.row:focus-visible {
1848 outline: 2px solid var(--accent);
1849 outline-offset: -2px;
1850}
1851
1852/* ------------------------------------------------------------------ */
1853/* Search view (#/search) */
1854/* ------------------------------------------------------------------ */
1855
1856.search-view {
1857 display: flex;
1858 flex-direction: column;
1859 gap: 14px;
1860}
1861
1862/* Query bar ------------------------------------------------------ */
1863
1864.query-bar {
1865 display: flex;
1866 align-items: stretch;
1867 gap: 8px;
1868}
1869
1870.query-input {
1871 flex: 1;
1872 display: flex;
1873 align-items: center;
1874 gap: 8px;
1875 background: var(--panel);
1876 border: 1px solid var(--border);
1877 padding: 0 10px;
1878 min-width: 0;
1879}
1880
1881.query-input .qi-icon {
1882 width: 18px;
1883 height: 18px;
1884 color: var(--muted);
1885 flex: none;
1886}
1887
1888.query-input input {
1889 flex: 1;
1890 min-width: 0;
1891 border: none;
1892 outline: none;
1893 background: transparent;
1894 color: var(--text);
1895 font: inherit;
1896 font-size: 14px;
1897 height: 38px;
1898 padding: 0;
1899}
1900
1901.query-input input::placeholder {
1902 color: var(--muted);
1903}
1904
1905/* Segmented Name / Content / Both control */
1906
1907.seg {
1908 display: flex;
1909 border: 1px solid var(--border);
1910 background: var(--panel);
1911}
1912
1913.seg button {
1914 appearance: none;
1915 border: none;
1916 border-right: 1px solid var(--border);
1917 background: transparent;
1918 color: var(--muted);
1919 font: inherit;
1920 font-size: 13px;
1921 padding: 0 14px;
1922 cursor: pointer;
1923}
1924
1925.seg button:last-child {
1926 border-right: none;
1927}
1928
1929.seg button:hover {
1930 background: var(--bg);
1931}
1932
1933.seg button.on {
1934 background: var(--accent);
1935 color: #fff;
1936}
1937
1938/* Start folder --------------------------------------------------- */
1939
1940/* The folder a search starts in; opens the folder picker. */
1941.folder-pick {
1942 flex: none;
1943 display: flex;
1944 align-items: center;
1945 gap: 8px;
1946 max-width: 260px;
1947 border: 1px solid var(--border);
1948 background: var(--panel);
1949 color: var(--text);
1950 font: inherit;
1951 font-size: 13px;
1952 padding: 0 10px;
1953 cursor: pointer;
1954}
1955
1956.folder-pick:hover {
1957 border-color: var(--muted);
1958}
1959
1960.folder-pick .fp-icon {
1961 width: 16px;
1962 height: 16px;
1963 color: var(--muted);
1964 flex: none;
1965}
1966
1967.folder-pick .fp-path {
1968 overflow: hidden;
1969 text-overflow: ellipsis;
1970 white-space: nowrap;
1971}
1972
1973.ms {
1974 position: relative;
1975 display: flex;
1976}
1977
1978.ms-btn {
1979 display: flex;
1980 align-items: center;
1981 gap: 6px;
1982 border: 1px solid var(--border);
1983 background: var(--panel);
1984 color: var(--text);
1985 font: inherit;
1986 font-size: 13px;
1987 padding: 0 10px;
1988 cursor: pointer;
1989 max-width: 180px;
1990}
1991
1992.ms-btn:hover {
1993 background: var(--bg);
1994}
1995
1996.ms-btn > span {
1997 overflow: hidden;
1998 text-overflow: ellipsis;
1999 white-space: nowrap;
2000}
2001
2002/* Square caret drawn with borders (the icon set has no chevron). */
2003.ms-caret {
2004 flex: none;
2005 width: 8px;
2006 height: 8px;
2007 border-right: 2px solid var(--muted);
2008 border-bottom: 2px solid var(--muted);
2009 transform: rotate(45deg) translate(-1px, -1px);
2010}
2011
2012.ms-backdrop {
2013 position: fixed;
2014 inset: 0;
2015 z-index: 40;
2016}
2017
2018.ms-drop {
2019 position: absolute;
2020 right: 0;
2021 top: calc(100% + 4px);
2022 z-index: 41;
2023 min-width: 200px;
2024 max-height: 280px;
2025 overflow: auto;
2026 background: var(--panel);
2027 border: 1px solid var(--border);
2028 padding: 4px 0;
2029}
2030
2031.ms-opt {
2032 display: flex;
2033 align-items: center;
2034 gap: 8px;
2035 padding: 7px 12px;
2036 font-size: 14px;
2037 cursor: pointer;
2038 user-select: none;
2039}
2040
2041.ms-opt:hover {
2042 background: var(--bg);
2043}
2044
2045.ms-opt span {
2046 overflow: hidden;
2047 text-overflow: ellipsis;
2048 white-space: nowrap;
2049}
2050
2051/* Run / stop ------------------------------------------------------- */
2052
2053.btn-run {
2054 border: 1px solid var(--accent-border);
2055 background: var(--accent);
2056 color: #fff;
2057 font: inherit;
2058 font-size: 13px;
2059 font-weight: 600;
2060 padding: 0 18px;
2061 cursor: pointer;
2062}
2063
2064.btn-run:hover {
2065 background: var(--accent-hover);
2066}
2067
2068.btn-stop {
2069 display: flex;
2070 align-items: center;
2071 gap: 6px;
2072 border: 1px solid var(--danger-border);
2073 background: var(--danger);
2074 color: #fff;
2075 font: inherit;
2076 font-size: 13px;
2077 font-weight: 600;
2078 padding: 0 14px;
2079 cursor: pointer;
2080}
2081
2082.btn-stop:hover {
2083 opacity: 0.9;
2084}
2085
2086.btn-stop .ic-stop {
2087 width: 14px;
2088 height: 14px;
2089}
2090
2091/* Status line ------------------------------------------------------ */
2092
2093.status-line {
2094 display: flex;
2095 align-items: center;
2096 gap: 8px;
2097 font-size: 13px;
2098 color: var(--muted);
2099}
2100
2101.status-line .dot {
2102 width: 8px;
2103 height: 8px;
2104 background: var(--accent);
2105 animation: search-pulse 1s ease-in-out infinite;
2106}
2107
2108@keyframes search-pulse {
2109 0%,
2110 100% {
2111 opacity: 1;
2112 }
2113 50% {
2114 opacity: 0.3;
2115 }
2116}
2117
2118.status-line .skip {
2119 opacity: 0.75;
2120}
2121
2122/* Group labels ----------------------------------------------------- */
2123
2124.group-label {
2125 display: flex;
2126 align-items: center;
2127 gap: 8px;
2128 font-size: 12px;
2129 font-weight: 600;
2130 letter-spacing: 0.04em;
2131 text-transform: uppercase;
2132 color: var(--muted);
2133}
2134
2135/* Result group: a `display:flex` (or `none`, from the inline style) flex
2136 item of `.search-view` that re-establishes the column layout for its
2137 label/list/more-row children. */
2138.result-group {
2139 flex-direction: column;
2140 gap: 14px;
2141}
2142
2143.group-label .gl-icon {
2144 width: 15px;
2145 height: 15px;
2146}
2147
2148.group-label .n {
2149 font-weight: 400;
2150 text-transform: none;
2151 letter-spacing: 0;
2152}
2153
2154.group-label .grow {
2155 flex: 1;
2156}
2157
2158.collapse-all {
2159 appearance: none;
2160 border: 1px solid var(--border);
2161 background: transparent;
2162 color: var(--muted);
2163 font: inherit;
2164 font-size: 12px;
2165 text-transform: none;
2166 letter-spacing: 0;
2167 padding: 3px 10px;
2168 cursor: pointer;
2169}
2170
2171.collapse-all:hover {
2172 background: var(--bg);
2173 color: var(--text);
2174}
2175
2176/* Name-result rows --------------------------------------------------- */
2177
2178.srow {
2179 display: grid;
2180 /* icon | name+dir | size | goto */
2181 grid-template-columns: 28px minmax(0, 1fr) 90px 30px;
2182 gap: 12px;
2183 align-items: center;
2184 padding: 9px 14px;
2185 border-bottom: 1px solid var(--border);
2186 cursor: pointer;
2187 font-size: 14px;
2188 user-select: none;
2189}
2190
2191.srow:last-child {
2192 border-bottom: none;
2193}
2194
2195.srow:hover {
2196 background: var(--bg);
2197}
2198
2199.srow-icon {
2200 width: 20px;
2201 height: 20px;
2202}
2203
2204.srow-main {
2205 min-width: 0;
2206 display: flex;
2207 flex-direction: column;
2208 line-height: 1.3;
2209}
2210
2211.srow-name {
2212 overflow: hidden;
2213 text-overflow: ellipsis;
2214 white-space: nowrap;
2215}
2216
2217/* Highlighted query hits (plain fragments render as bare spans). */
2218mark.hl-hit {
2219 background: color-mix(in srgb, var(--accent) 25%, transparent);
2220 color: inherit;
2221}
2222
2223/* The directory-path line (the name line lives inside .srow-name). */
2224.srow-dir {
2225 font-size: 12px;
2226 color: var(--muted);
2227 overflow: hidden;
2228 text-overflow: ellipsis;
2229 white-space: nowrap;
2230}
2231
2232.srow-dir mark.hl-hit {
2233 color: inherit;
2234}
2235
2236.ssize {
2237 font-size: 13px;
2238 color: var(--muted);
2239 text-align: right;
2240}
2241
2242/* Paging footer: position in the results on the left, the action on the
2243 right. Separated from the list by its own border so the button never reads
2244 as another result row. */
2245.page-foot {
2246 display: flex;
2247 align-items: center;
2248 justify-content: space-between;
2249 gap: 12px;
2250 padding: 10px 14px;
2251 border-top: 1px solid var(--border);
2252 font-size: 12px;
2253 color: var(--muted);
2254}
2255
2256.page-foot .pf-count {
2257 font-variant-numeric: tabular-nums;
2258}
2259
2260.page-foot .pf-more {
2261 appearance: none;
2262 padding: 5px 12px;
2263 border: 1px solid var(--border);
2264 background: var(--panel);
2265 font: inherit;
2266 font-weight: 500;
2267 color: var(--text);
2268 cursor: pointer;
2269}
2270
2271.page-foot .pf-more:hover {
2272 border-color: var(--accent);
2273 color: var(--accent);
2274}
2275
2276/* Content matches ----------------------------------------------------- */
2277
2278.match-card {
2279 background: var(--panel);
2280 border: 1px solid var(--border);
2281 border-top: none;
2282}
2283
2284/* The first card is the one right after the group label; `:first-of-type`
2285 cannot be used because the label is an earlier `div` sibling. */
2286.group-label + .match-card {
2287 border-top: 1px solid var(--border);
2288}
2289
2290/* The header opens the file; the matched lines below are text to read. */
2291.match-head {
2292 display: flex;
2293 align-items: center;
2294 gap: 10px;
2295 padding: 9px 14px;
2296 font-size: 14px;
2297 cursor: pointer;
2298}
2299
2300.match-head:hover {
2301 background: var(--bg);
2302}
2303
2304.match-head .mi {
2305 width: 18px;
2306 height: 18px;
2307 color: var(--muted);
2308 flex: none;
2309}
2310
2311/* Takes the slack, so the count and the two buttons sit at the right edge. */
2312.mpath {
2313 flex: 1;
2314 min-width: 0;
2315 overflow: hidden;
2316 text-overflow: ellipsis;
2317 white-space: nowrap;
2318}
2319
2320.mpath b {
2321 font-weight: 600;
2322}
2323
2324.mcount {
2325 flex: none;
2326 font-size: 12px;
2327 color: var(--muted);
2328}
2329
2330/* "Show in folder", in both the name rows and the match-card headers. */
2331.goto-btn {
2332 appearance: none;
2333 flex: none;
2334 justify-self: end;
2335 display: flex;
2336 align-items: center;
2337 justify-content: center;
2338 width: 26px;
2339 height: 26px;
2340 padding: 0;
2341 border: 1px solid transparent;
2342 background: transparent;
2343 cursor: pointer;
2344}
2345
2346.goto-btn:hover {
2347 border-color: var(--border);
2348}
2349
2350.goto-btn .goto-ic {
2351 width: 16px;
2352 height: 16px;
2353 color: var(--muted);
2354}
2355
2356.goto-btn:hover .goto-ic {
2357 color: var(--accent);
2358}
2359
2360.chev {
2361 appearance: none;
2362 border: 1px solid var(--border);
2363 background: transparent;
2364 width: 26px;
2365 height: 26px;
2366 display: flex;
2367 align-items: center;
2368 justify-content: center;
2369 cursor: pointer;
2370 flex: none;
2371}
2372
2373.chev:hover {
2374 background: var(--bg);
2375}
2376
2377.chev .chev-down,
2378.chev .chev-up {
2379 display: block;
2380 width: 10px;
2381 height: 10px;
2382 border-right: 2px solid var(--muted);
2383 border-bottom: 2px solid var(--muted);
2384}
2385
2386.chev .chev-down {
2387 transform: rotate(45deg) translate(-1px, -1px);
2388}
2389
2390.chev .chev-up {
2391 transform: rotate(-135deg) translate(-1px, -1px);
2392}
2393
2394.match-lines {
2395 border-top: 1px solid var(--border);
2396 background: var(--bg);
2397 font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
2398 font-size: 12.5px;
2399}
2400
2401/* Matched lines are text to read, not a control: the card header is what
2402 opens the file, so no pointer cursor and no hover here. */
2403.mline {
2404 display: grid;
2405 grid-template-columns: 52px minmax(0, 1fr);
2406 gap: 12px;
2407 padding: 5px 14px;
2408 border-bottom: 1px solid var(--border);
2409 white-space: pre;
2410 user-select: text;
2411}
2412
2413.mline:last-child {
2414 border-bottom: none;
2415}
2416
2417.mline .ln {
2418 color: var(--muted);
2419 text-align: right;
2420 user-select: none;
2421}
2422
2423.mline .tx {
2424 overflow: hidden;
2425 text-overflow: ellipsis;
2426}
2427
2428.search-empty {
2429 padding: 48px 20px;
2430 text-align: center;
2431 color: var(--muted);
2432 font-size: 14px;
2433}
2434