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.auth-card {
88 width: 100%;
89 max-width: 380px;
90}
91
92.auth-card h1 {
93 margin: 0 0 8px;
94 font-size: 22px;
95}
96
97.auth-logo {
98 width: 44px;
99 height: 44px;
100 margin-bottom: 12px;
101}
102
103.auth-card p {
104 margin: 0 0 20px;
105 font-size: 13px;
106}
107
108.field {
109 display: block;
110 margin-bottom: 14px;
111}
112
113.field span {
114 display: block;
115 font-size: 13px;
116 color: var(--muted);
117 margin-bottom: 4px;
118}
119
120input {
121 width: 100%;
122 padding: 10px 12px;
123 border: 1px solid var(--border);
124 background: var(--bg);
125 color: var(--text);
126 font-size: 14px;
127}
128
129input:focus {
130 outline: 2px solid var(--accent);
131 outline-offset: -1px;
132}
133
134/* Square checkboxes — native controls are rounded, the theme is sharp. */
135input[type="checkbox"] {
136 appearance: none;
137 -webkit-appearance: none;
138 width: 16px;
139 height: 16px;
140 margin: 0;
141 padding: 0;
142 flex: none;
143 border: 1px solid var(--border);
144 background: var(--panel);
145 cursor: pointer;
146 position: relative;
147 vertical-align: -3px;
148}
149
150input[type="checkbox"]:checked {
151 background: var(--accent);
152 border-color: var(--accent-border);
153}
154
155input[type="checkbox"]:checked::after {
156 content: "";
157 position: absolute;
158 left: 4px;
159 top: 1px;
160 width: 4px;
161 height: 8px;
162 border: solid #fff;
163 border-width: 0 2px 2px 0;
164 transform: rotate(45deg);
165}
166
167button {
168 font: inherit;
169 cursor: pointer;
170 display: inline-flex;
171 align-items: center;
172 justify-content: center;
173 gap: 6px;
174 border: 1px solid var(--border);
175 background: var(--panel);
176 color: var(--text);
177 padding: 9px 14px;
178}
179
180button:hover {
181 border-color: var(--muted);
182}
183
184button:disabled {
185 opacity: 0.6;
186 cursor: default;
187}
188
189.btn-primary {
190 width: 100%;
191 background: var(--accent);
192 /* One step away from the fill, per theme (see palette above), so the
193 button's edge reads the same as on neutral buttons. */
194 border-color: var(--accent-border);
195 color: #fff;
196 font-weight: 600;
197}
198
199.btn-primary:hover {
200 background: var(--accent-hover);
201 border-color: var(--accent-border);
202}
203
204.error {
205 color: var(--danger);
206 font-size: 13px;
207 min-height: 18px;
208 margin: 0 0 10px;
209}
210
211/* layout (signed-in): full-width topbar, then sidebar + content below */
212
213.layout {
214 min-height: 100vh;
215 display: flex;
216 flex-direction: column;
217}
218
219.topbar {
220 flex: none;
221 position: sticky;
222 top: 0;
223 z-index: 130;
224 height: var(--topbar-h);
225 display: flex;
226 align-items: center;
227 justify-content: space-between;
228 gap: 12px;
229 padding: 0 16px;
230 background: var(--panel);
231 border-bottom: 1px solid var(--border);
232}
233
234.topbar-left {
235 display: flex;
236 align-items: center;
237 gap: 10px;
238 min-width: 0;
239}
240
241.icon-btn.nav-toggle {
242 display: none;
243}
244
245.body-row {
246 flex: 1;
247 display: flex;
248 align-items: stretch;
249}
250
251/* ---- sidebar ---- */
252
253.sidebar {
254 flex: none;
255 width: 224px;
256 display: flex;
257 flex-direction: column;
258 background: var(--panel);
259 border-right: 1px solid var(--border);
260 position: sticky;
261 top: var(--topbar-h);
262 height: calc(100vh - var(--topbar-h));
263}
264
265.brand {
266 display: flex;
267 align-items: center;
268 gap: 10px;
269 font-weight: 700;
270 white-space: nowrap;
271 color: inherit;
272 text-decoration: none;
273 cursor: pointer;
274}
275
276.brand:hover {
277 opacity: 0.8;
278}
279
280.brand-name {
281 overflow: hidden;
282 text-overflow: ellipsis;
283}
284
285.logo {
286 width: 26px;
287 height: 26px;
288 flex: none;
289 display: block;
290}
291
292.nav {
293 flex: 1;
294 overflow-y: auto;
295 padding: 14px 8px 16px;
296 display: flex;
297 flex-direction: column;
298 gap: 20px;
299}
300
301.sidebar-user {
302 padding: 0 10px 2px;
303 font-size: 14px;
304 font-weight: 600;
305 overflow: hidden;
306 text-overflow: ellipsis;
307 white-space: nowrap;
308}
309
310.nav-section-label {
311 font-size: 11px;
312 font-weight: 700;
313 text-transform: uppercase;
314 letter-spacing: 0.08em;
315 color: var(--muted);
316 padding: 0 10px 6px;
317}
318
319/* Section header with a right-aligned action (the Files refresh). */
320.nav-section-head {
321 display: flex;
322 align-items: center;
323 justify-content: space-between;
324 gap: 6px;
325 margin-bottom: 4px;
326}
327
328.nav-section-head .nav-section-label {
329 flex: 1;
330 padding-bottom: 2px;
331}
332
333.nav-section-refresh {
334 color: var(--muted);
335 background: none;
336 border-color: transparent;
337 padding: 3px 5px;
338}
339
340.nav-section-refresh:hover {
341 color: var(--text);
342 border-color: var(--border);
343}
344
345.nav-tab {
346 display: flex;
347 align-items: center;
348 justify-content: flex-start;
349 gap: 10px;
350 width: 100%;
351 margin-bottom: 2px;
352 padding: 8px 10px;
353 text-align: left;
354 background: none;
355 border: none;
356 color: var(--muted);
357 font-size: 14px;
358}
359
360.nav-tab:hover {
361 background: var(--bg);
362 color: var(--text);
363}
364
365.nav-tab.active {
366 background: var(--accent);
367 border-color: var(--accent);
368 color: #fff;
369}
370
371.ic-tab {
372 width: 18px;
373 height: 18px;
374 flex: none;
375}
376
377.nav-tab.active .icon {
378 color: #fff;
379}
380
381.nav-tab-name {
382 flex: 1;
383 min-width: 0;
384 overflow: hidden;
385 text-overflow: ellipsis;
386 white-space: nowrap;
387}
388
389.nav-tab-note {
390 flex: none;
391 font-size: 10px;
392 font-weight: 700;
393 padding: 1px 5px;
394 background: var(--bg);
395 border: 1px solid var(--border);
396 color: var(--muted);
397}
398
399.nav-tab.active .nav-tab-note {
400 background: rgb(255 255 255 / 18%);
401 border-color: rgb(255 255 255 / 30%);
402 color: #fff;
403}
404
405.nav-backdrop {
406 display: none;
407}
408
409/* Sidebar footer: theme toggle, log out, and the project version.
410 Sits at the bottom of the sidebar, below the manage section. */
411.sidebar-footer {
412 flex: none;
413 border-top: 1px solid var(--border);
414 padding: 8px;
415}
416
417.sidebar-footer .nav-tab {
418 margin-bottom: 0;
419}
420
421.sidebar-action.logout {
422 color: var(--danger);
423}
424
425.sidebar-action.logout:hover {
426 color: var(--danger);
427}
428
429.sidebar-version {
430 margin-top: 8px;
431 padding: 0 10px;
432 font-size: 11.5px;
433 color: var(--muted);
434 white-space: nowrap;
435 overflow: hidden;
436 text-overflow: ellipsis;
437}
438
439.badge {
440 font-size: 11px;
441 padding: 2px 8px;
442 background: var(--bg);
443 border: 1px solid var(--border);
444 color: var(--muted);
445}
446
447.content {
448 flex: 1;
449 min-width: 0;
450 min-height: 0;
451 display: flex;
452 flex-direction: column;
453 padding: 20px;
454}
455
456/* While a file is open the app is a fixed-height frame: the file view
457 * (editor, image, …) scrolls inside, the page itself never grows. */
458.layout.file-mode {
459 height: 100vh;
460 overflow: hidden;
461}
462
463.layout.file-mode .body-row {
464 min-height: 0;
465}
466
467/* Narrow screens: the sidebar becomes an off-canvas drawer that only
468 covers the content, below the (always visible) topbar. */
469@media (max-width: 860px) {
470 .sidebar {
471 position: fixed;
472 top: var(--topbar-h);
473 bottom: 0;
474 left: 0;
475 right: auto;
476 height: auto;
477 z-index: 120;
478 transform: translateX(-100%);
479 transition: transform 0.15s ease;
480 }
481
482 .sidebar.open {
483 transform: translateX(0);
484 box-shadow: 0 0 48px rgb(0 0 0 / 35%);
485 }
486
487 .nav-backdrop {
488 display: block;
489 position: fixed;
490 top: var(--topbar-h);
491 bottom: 0;
492 left: 0;
493 right: 0;
494 z-index: 110;
495 background: rgb(0 0 0 / 40%);
496 }
497
498 .icon-btn.nav-toggle {
499 display: inline-flex;
500 }
501
502 .content {
503 padding: 14px;
504 }
505}
506
507/* ---- file browser ---- */
508
509.browser {
510 width: 100%;
511 max-width: 1200px;
512 margin: 0 auto;
513 flex: 1;
514 display: flex;
515 flex-direction: column;
516 min-height: 0;
517}
518
519.icon {
520 width: 20px;
521 height: 20px;
522 display: inline-block;
523 vertical-align: middle;
524}
525
526.ic-folder {
527 color: var(--accent);
528}
529
530.ic-muted {
531 color: var(--muted);
532}
533
534.crumbs {
535 display: flex;
536 align-items: center;
537 gap: 2px;
538 padding: 6px 0 12px;
539 font-size: 15px;
540 flex-wrap: wrap;
541}
542
543.crumb {
544 cursor: pointer;
545 color: var(--muted);
546 padding: 3px 6px;
547}
548
549.crumb:hover {
550 color: var(--text);
551 background: var(--panel);
552}
553
554.crumb.current {
555 color: var(--text);
556 font-weight: 600;
557 cursor: default;
558 background: none;
559}
560
561.crumb-sep {
562 color: var(--muted);
563 padding: 0 1px;
564}
565
566.toolbar {
567 display: flex;
568 align-items: center;
569 justify-content: space-between;
570 gap: 6px;
571 padding-bottom: 12px;
572}
573
574.toolbar-right {
575 display: flex;
576 gap: 6px;
577}
578
579.icon-btn {
580 padding: 7px 9px;
581 border: 1px solid var(--border);
582 background: var(--panel);
583 color: var(--muted);
584 display: inline-flex;
585 align-items: center;
586}
587
588.icon-btn:hover {
589 border-color: var(--muted);
590}
591
592.icon-btn.active {
593 background: var(--accent);
594 border-color: var(--accent);
595 color: #fff;
596}
597
598.ic-btn {
599 width: 18px;
600 height: 18px;
601 color: inherit;
602}
603
604.entries-area {
605 flex: 1;
606 min-height: 240px;
607}
608
609.center-note {
610 text-align: center;
611 padding: 48px 0;
612}
613
614.error-card {
615 display: flex;
616 align-items: center;
617 justify-content: space-between;
618 gap: 16px;
619 color: var(--danger);
620 font-size: 14px;
621}
622
623/* root tiles were replaced by the sidebar's folder tabs */
624
625/* grid view */
626
627.entries-grid {
628 display: grid;
629 grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
630 gap: 10px;
631}
632
633.tile {
634 background: var(--panel);
635 border: 1px solid var(--border);
636 padding: 16px 10px 12px;
637 text-align: center;
638 cursor: pointer;
639 user-select: none;
640}
641
642.tile:hover {
643 border-color: var(--muted);
644}
645
646.tile-icon {
647 width: 42px;
648 height: 42px;
649 margin: 0 auto 10px;
650 display: block;
651}
652
653.tile-name {
654 font-size: 13px;
655 line-height: 1.3;
656 word-break: break-word;
657}
658
659/* list view */
660
661.entries-list {
662 background: var(--panel);
663 border: 1px solid var(--border);
664 overflow: hidden;
665}
666
667.row {
668 display: grid;
669 grid-template-columns: 28px minmax(0, 1fr) 90px 150px;
670 gap: 12px;
671 align-items: center;
672 padding: 9px 14px;
673 border-bottom: 1px solid var(--border);
674 cursor: pointer;
675 font-size: 14px;
676 user-select: none;
677}
678
679.row:last-child {
680 border-bottom: none;
681}
682
683.row:hover {
684 background: var(--bg);
685}
686
687.row-icon {
688 width: 20px;
689 height: 20px;
690}
691
692.row-name {
693 overflow: hidden;
694 text-overflow: ellipsis;
695 white-space: nowrap;
696}
697
698.row-size,
699.row-date {
700 font-size: 13px;
701 text-align: right;
702}
703
704@media (max-width: 640px) {
705 .row {
706 grid-template-columns: 28px minmax(0, 1fr) 90px;
707 }
708
709 .row-date {
710 display: none;
711 }
712}
713
714/* context menu */
715
716.ctx-menu {
717 position: fixed;
718 z-index: 100;
719 min-width: 190px;
720 background: var(--panel);
721 border: 1px solid var(--border);
722 padding: 5px;
723 box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
724}
725
726.ctx-item {
727 display: flex;
728 align-items: center;
729 gap: 10px;
730 padding: 8px 12px;
731 font-size: 14px;
732 cursor: pointer;
733}
734
735.ic-ctx {
736 width: 18px;
737 height: 18px;
738 flex: none;
739}
740
741.ctx-item:hover {
742 background: var(--bg);
743}
744
745.ctx-item.disabled {
746 opacity: 0.45;
747 cursor: default;
748}
749
750.ctx-item.disabled:hover {
751 background: none;
752}
753
754/* toolbar selection actions (top right) */
755
756.sel-actions {
757 display: flex;
758 align-items: center;
759 gap: 6px;
760 position: relative;
761}
762
763.sel-actions .icon-btn:disabled {
764 opacity: 0.45;
765 cursor: default;
766}
767
768.sel-actions .icon-btn:disabled:hover {
769 border-color: var(--border);
770 color: var(--muted);
771}
772
773.sel-more-wrap {
774 position: relative;
775}
776
777.sel-more-menu {
778 position: absolute;
779 right: 0;
780 top: calc(100% + 6px);
781 z-index: 100;
782 min-width: 190px;
783 background: var(--panel);
784 border: 1px solid var(--border);
785 padding: 5px;
786 box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
787}
788
789.sel-more-menu.hidden {
790 display: none;
791}
792
793/* selection actions in the top bar */
794
795.topbar .sel-actions {
796 min-height: 0;
797}
798
799.topbar .sel-actions .icon-btn {
800 padding: 5px 7px;
801}
802
803/* selection highlight in the grid and list */
804
805.tile.selected {
806 border-color: var(--accent);
807 background: color-mix(in srgb, var(--accent) 12%, var(--panel));
808}
809
810.row.selected {
811 background: color-mix(in srgb, var(--accent) 12%, var(--panel));
812 box-shadow: inset 2px 0 0 var(--accent);
813}
814
815/* settings tabs (profile / server) */
816
817.settings-tabs {
818 display: flex;
819 gap: 8px;
820}
821
822.settings-tab-btn {
823 padding: 7px 14px;
824 border: 1px solid var(--border);
825 background: var(--panel);
826 color: var(--muted);
827 font-size: 13px;
828 font-weight: 600;
829}
830
831.settings-tab-btn:hover {
832 border-color: var(--muted);
833 color: var(--text);
834}
835
836.settings-tab-btn.active {
837 border-color: var(--accent);
838 color: var(--accent);
839 background: color-mix(in srgb, var(--accent) 10%, var(--panel));
840}
841
842/* toast */
843
844.toast {
845 position: fixed;
846 bottom: 20px;
847 right: 20px;
848 z-index: 200;
849 background: var(--panel);
850 color: var(--text);
851 border: 1px solid var(--border);
852 padding: 11px 16px;
853 font-size: 14px;
854 box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
855 max-width: 320px;
856}
857
858.toast-error {
859 background: color-mix(in srgb, var(--danger) 12%, var(--panel));
860 border-color: var(--danger-border);
861 color: var(--danger);
862}
863
864
865/* ---- modals ---- */
866
867.modal-overlay {
868 position: fixed;
869 inset: 0;
870 background: rgb(0 0 0 / 45%);
871 display: flex;
872 align-items: center;
873 justify-content: center;
874 z-index: 100;
875}
876
877.modal {
878 width: 440px;
879 max-width: calc(100vw - 32px);
880 padding: 20px;
881 background: var(--panel);
882 border: 1px solid var(--border);
883 box-shadow: 0 12px 44px rgb(0 0 0 / 35%);
884}
885
886.modal.picker {
887 width: 520px;
888}
889
890.modal-title {
891 margin: 0 0 12px;
892 font-size: 16px;
893}
894
895.modal-label {
896 display: block;
897 font-size: 13px;
898 color: var(--muted);
899 margin-bottom: 6px;
900}
901
902.modal .input {
903 width: 100%;
904 box-sizing: border-box;
905 margin-bottom: 16px;
906}
907
908.modal-label.error,
909.dialog-error {
910 color: var(--danger);
911 font-size: 13px;
912}
913
914.dialog-error {
915 margin-top: 8px;
916}
917
918.modal-message {
919 white-space: pre-line;
920 font-size: 14px;
921 margin: 0 0 8px;
922}
923
924.modal-actions {
925 display: flex;
926 justify-content: flex-end;
927 gap: 8px;
928 margin-top: 16px;
929}
930
931.btn-danger {
932 background: var(--danger);
933 /* A visible border (one step away from the fill, per theme) so the
934 button's edge reads the same as on neutral buttons. */
935 border-color: var(--danger-border);
936 color: #fff;
937}
938
939.btn-danger:hover {
940 background: color-mix(in srgb, var(--danger) 85%, black);
941}
942
943.btn:disabled {
944 opacity: 0.5;
945 cursor: not-allowed;
946}
947
948.conflict-list {
949 max-height: 200px;
950 overflow-y: auto;
951 margin: 8px 0;
952 padding-left: 20px;
953 font-size: 13px;
954}
955
956.picker-roots {
957 display: flex;
958 gap: 6px;
959 flex-wrap: wrap;
960 margin-bottom: 12px;
961}
962
963.root-opt {
964 font-size: 13px;
965}
966
967.root-opt.active {
968 background: var(--accent);
969 border-color: var(--accent);
970 color: #fff;
971}
972
973.picker-current {
974 font-size: 13px;
975 margin: 0 0 8px;
976}
977
978.picker-crumbs {
979 margin-bottom: 8px;
980}
981
982.picker-entries {
983 max-height: 240px;
984 overflow-y: auto;
985 border: 1px solid var(--border);
986}
987
988.picker-row {
989 display: flex;
990 align-items: center;
991 gap: 10px;
992 padding: 8px 12px;
993 cursor: pointer;
994}
995
996.picker-row:hover {
997 background: var(--bg);
998}
999
1000/* ---------------------------------------------------------------------------
1001 * Full-page file view (preview / editor / "no preview"). The top bar carries
1002 * the title + actions; the content area below is edge to edge.
1003 * ------------------------------------------------------------------------- */
1004.content.file-open {
1005 padding: 0;
1006 overflow: hidden;
1007}
1008
1009/* The browser stays mounted (state, fetch callback) but hidden. */
1010.content.file-open .browser {
1011 display: none;
1012}
1013
1014.file-view {
1015 flex: 1;
1016 min-height: 0;
1017 display: flex;
1018 flex-direction: column;
1019}
1020
1021.file-title {
1022 display: flex;
1023 align-items: center;
1024 gap: 8px;
1025 min-width: 0;
1026}
1027
1028.file-title-icon {
1029 width: 18px;
1030 height: 18px;
1031 flex: none;
1032 color: var(--muted);
1033}
1034
1035.file-title-name {
1036 font-weight: 600;
1037 font-size: 14px;
1038 white-space: nowrap;
1039 overflow: hidden;
1040 text-overflow: ellipsis;
1041}
1042
1043.file-actions {
1044 display: flex;
1045 align-items: center;
1046 gap: 10px;
1047 flex: none;
1048}
1049
1050.file-close {
1051 border: none;
1052 background: transparent;
1053 color: var(--muted);
1054 padding: 6px 8px;
1055}
1056
1057.file-close:hover {
1058 color: var(--text);
1059 background: var(--bg);
1060}
1061.preview-body {
1062 flex: 1;
1063 min-height: 0;
1064 overflow: auto;
1065 display: flex;
1066 background: var(--bg);
1067}
1068.preview-img {
1069 max-width: 100%;
1070 max-height: 100%;
1071 display: block;
1072 margin: auto;
1073}
1074.preview-pdf {
1075 width: 100%;
1076 height: 100%;
1077 border: none;
1078 background: #fff;
1079}
1080.preview-video {
1081 width: 100%;
1082 max-height: 100%;
1083 background: #000;
1084}
1085.preview-audio {
1086 display: flex;
1087 align-items: center;
1088 justify-content: center;
1089 width: 100%;
1090 padding: 24px;
1091}
1092.preview-audio audio {
1093 width: 100%;
1094 max-width: 480px;
1095}
1096
1097/* CodeMirror (text editor) */
1098.cm-container {
1099 height: 100%;
1100}
1101.cm-container .cm-editor {
1102 height: 100%;
1103}
1104/* Load/save failure: dims the editor and shows the message in a card
1105 * (the top-bar Save button stays reachable to retry). */
1106.cm-error {
1107 position: absolute;
1108 inset: 0;
1109 z-index: 5;
1110 display: flex;
1111 align-items: center;
1112 justify-content: center;
1113 padding: 24px;
1114 background: rgb(0 0 0 / 45%);
1115}
1116
1117.cm-error-card {
1118 position: relative;
1119 max-width: 440px;
1120 min-width: 200px;
1121 background: var(--panel);
1122 border: 1px solid var(--border);
1123 box-shadow: 0 12px 44px rgb(0 0 0 / 35%);
1124}
1125
1126.cm-error-close {
1127 position: absolute;
1128 top: 7px;
1129 right: 7px;
1130 display: inline-flex;
1131 align-items: center;
1132 justify-content: center;
1133 border: none;
1134 background: transparent;
1135 color: var(--muted);
1136 padding: 3px;
1137}
1138
1139.cm-error-close:hover {
1140 color: var(--text);
1141 background: var(--bg);
1142}
1143
1144.cm-error-close .ic-btn {
1145 width: 16px;
1146 height: 16px;
1147}
1148
1149.cm-error-text {
1150 padding: 16px 40px 16px 20px;
1151 font-size: 14px;
1152 text-align: center;
1153 overflow-wrap: anywhere;
1154}
1155
1156/* ---------------------------------------------------------------------------
1157 * Text editor (full page; title/save/close live in the top bar)
1158 * ------------------------------------------------------------------------- */
1159.file-editor {
1160 flex: 1;
1161 min-height: 0;
1162 display: flex;
1163 position: relative;
1164 background: var(--bg);
1165}
1166.file-editor .cm-container {
1167 width: 100%;
1168 height: 100%;
1169}
1170/* "No preview" fallback (binaries, archives). */
1171.file-nopreview {
1172 flex: 1;
1173 display: flex;
1174 align-items: center;
1175 justify-content: center;
1176}
1177.file-nopreview .btn-primary {
1178 width: auto;
1179 margin-top: 14px;
1180}
1181/* Conflict / discard dialogs render above the file view. */
1182.editor-sub-overlay {
1183 z-index: 101;
1184 background: rgb(0 0 0 / 25%);
1185}
1186
1187/* ---------------------------------------------------------------------------
1188 * Download format dialog
1189 * ------------------------------------------------------------------------- */
1190.dl-formats {
1191 display: flex;
1192 flex-direction: column;
1193 gap: 8px;
1194 margin-bottom: 8px;
1195}
1196.dl-fmt {
1197 justify-content: flex-start;
1198}
1199
1200/* ---------------------------------------------------------------------------
1201 * Item info dialog
1202 * ------------------------------------------------------------------------- */
1203.info-list {
1204 margin: 8px 0 16px;
1205 display: grid;
1206 grid-template-columns: 90px 1fr;
1207 row-gap: 8px;
1208 column-gap: 12px;
1209}
1210.info-list dt {
1211 color: var(--muted);
1212}
1213.info-list dd {
1214 margin: 0;
1215 word-break: break-word;
1216}
1217
1218/* ---------------------------------------------------------------------------
1219 Shares (milestone 6)
1220 --------------------------------------------------------------------------- */
1221
1222.share-modal {
1223 width: 480px;
1224}
1225
1226.share-link-row {
1227 display: flex;
1228 gap: 8px;
1229 margin: 12px 0;
1230}
1231
1232.share-link-input {
1233 flex: 1;
1234 font-family: ui-monospace, monospace;
1235 font-size: 12.5px;
1236 padding: 8px 10px;
1237 border: 1px solid var(--border);
1238 background: var(--bg);
1239 color: var(--text);
1240 min-width: 0;
1241}
1242
1243.share-check {
1244 display: flex;
1245 align-items: center;
1246 gap: 8px;
1247 margin: 4px 0 14px;
1248 font-size: 14px;
1249}
1250
1251.share-check input {
1252 width: 16px;
1253 height: 16px;
1254}
1255
1256.share-expiry-row {
1257 display: flex;
1258 align-items: center;
1259 gap: 10px;
1260 margin: 0 0 10px;
1261}
1262
1263.share-expiry-row label {
1264 font-size: 13px;
1265 color: var(--muted);
1266 min-width: 52px;
1267}
1268
1269.share-expiry-row select,
1270.share-expiry-row input {
1271 flex: 1;
1272 padding: 8px 10px;
1273 border: 1px solid var(--border);
1274 background: var(--panel);
1275 color: var(--text);
1276 font: inherit;
1277}
1278
1279.share-expiry-note {
1280 margin: 4px 0 0;
1281 font-size: 12.5px;
1282}
1283
1284.btn-sm {
1285 padding: 5px 10px;
1286 font-size: 12.5px;
1287}
1288
1289.icon-btn-sm {
1290 padding: 4px 6px;
1291}
1292
1293/* "Your shares" manage view */
1294
1295.shares-view {
1296 width: 100%;
1297 max-width: 960px;
1298 margin: 0 auto;
1299}
1300
1301.view-header {
1302 margin-bottom: 16px;
1303}
1304
1305.view-header .admin-section-title {
1306 margin-bottom: 0;
1307}
1308
1309.view-hint {
1310 margin: 4px 0 0;
1311 font-size: 13px;
1312 max-width: 64ch;
1313}
1314
1315.share-list {
1316 display: flex;
1317 flex-direction: column;
1318 gap: 10px;
1319}
1320
1321.share-item {
1322 display: flex;
1323 align-items: center;
1324 justify-content: space-between;
1325 gap: 12px;
1326 padding: 10px 12px;
1327 border: 1px solid var(--border);
1328 background: var(--panel);
1329}
1330
1331.share-item-main {
1332 min-width: 0;
1333}
1334
1335.share-item-name {
1336 display: flex;
1337 align-items: center;
1338 gap: 8px;
1339 font-weight: 600;
1340 font-size: 14px;
1341 overflow: hidden;
1342 text-overflow: ellipsis;
1343 white-space: nowrap;
1344}
1345
1346.ic-row {
1347 width: 18px;
1348 height: 18px;
1349 flex: none;
1350}
1351
1352.share-item-meta {
1353 font-size: 12px;
1354 margin-top: 2px;
1355 overflow: hidden;
1356 text-overflow: ellipsis;
1357 white-space: nowrap;
1358}
1359
1360.share-item-actions {
1361 display: flex;
1362 gap: 6px;
1363 flex-shrink: 0;
1364}
1365
1366/* ------------------------------------------------------------------ */
1367/* Admin (milestone 7) */
1368/* ------------------------------------------------------------------ */
1369
1370.admin-view {
1371 display: flex;
1372 flex-direction: column;
1373 gap: 16px;
1374 width: 100%;
1375 max-width: 960px;
1376 margin: 0 auto;
1377}
1378
1379.admin-section-title {
1380 margin: 0 0 14px;
1381 font-size: 15px;
1382}
1383
1384.users-header {
1385 display: flex;
1386 align-items: center;
1387 justify-content: space-between;
1388 margin-bottom: 4px;
1389}
1390
1391.users-header .admin-section-title {
1392 margin-bottom: 0;
1393}
1394
1395.users-header .btn-primary {
1396 width: auto;
1397}
1398
1399.user-list {
1400 display: flex;
1401 flex-direction: column;
1402 gap: 10px;
1403}
1404
1405.user-row {
1406 display: flex;
1407 align-items: center;
1408 justify-content: space-between;
1409 gap: 12px;
1410 padding: 12px 14px;
1411 border: 1px solid var(--border);
1412}
1413
1414.user-row-main {
1415 min-width: 0;
1416}
1417
1418.user-row-name {
1419 display: flex;
1420 align-items: center;
1421 gap: 6px;
1422 font-weight: 600;
1423 font-size: 14px;
1424 flex-wrap: wrap;
1425}
1426
1427.ic-admin {
1428 width: 16px;
1429 height: 16px;
1430 flex: none;
1431}
1432
1433.user-row-actions {
1434 display: flex;
1435 gap: 6px;
1436 flex-shrink: 0;
1437}
1438
1439.badge-off {
1440 background: color-mix(in srgb, var(--danger) 15%, var(--panel));
1441 border-color: var(--danger);
1442 color: var(--danger);
1443}
1444
1445.setting-row {
1446 display: flex;
1447 align-items: center;
1448 gap: 10px;
1449 padding: 14px;
1450 border: 1px solid var(--border);
1451 cursor: pointer;
1452}
1453
1454.setting-row input {
1455 margin-left: auto;
1456}
1457
1458.setting-label {
1459 display: block;
1460 font-weight: 600;
1461 font-size: 14px;
1462}
1463
1464.setting-desc {
1465 display: block;
1466 font-size: 12px;
1467 color: var(--muted);
1468 margin-top: 2px;
1469}
1470
1471/* Setting row whose control is a select, pushed to the right. */
1472.setting-row-select {
1473 cursor: default;
1474}
1475
1476.setting-row-select > span {
1477 flex: 1;
1478}
1479
1480.setting-row-select select {
1481 appearance: none;
1482 margin-left: auto;
1483 padding: 10px 42px 10px 12px;
1484 border: 1px solid var(--border);
1485 background-color: var(--panel);
1486 background-image: var(--select-arrow);
1487 background-repeat: no-repeat;
1488 background-position: right 12px center;
1489 background-size: 14px 14px;
1490 color: var(--text);
1491 font: inherit;
1492 max-width: 190px;
1493}
1494
1495/* User create/edit form */
1496
1497.user-form {
1498 width: 520px;
1499}
1500
1501.field-input {
1502 width: 100%;
1503 box-sizing: border-box;
1504}
1505
1506.field-hint {
1507 font-size: 12px;
1508 margin: -2px 0 8px;
1509}
1510
1511.check-row {
1512 display: flex;
1513 align-items: center;
1514 gap: 8px;
1515 font-size: 14px;
1516 margin-bottom: 12px;
1517 cursor: pointer;
1518}
1519
1520.check-row input {
1521 width: auto;
1522}
1523
1524.form-error {
1525 color: var(--danger);
1526 font-size: 13px;
1527 margin: 4px 0 8px;
1528}
1529
1530.roots-editor {
1531 display: flex;
1532 flex-direction: column;
1533 gap: 6px;
1534}
1535
1536.root-draft {
1537 display: flex;
1538 align-items: center;
1539 gap: 8px;
1540}
1541
1542.root-draft-path {
1543 flex: 1;
1544 font-size: 13px;
1545 overflow: hidden;
1546 text-overflow: ellipsis;
1547 white-space: nowrap;
1548}
1549
1550.root-draft-mode {
1551 width: auto;
1552 padding: 6px 8px;
1553}
1554
1555.root-add {
1556 display: flex;
1557 gap: 8px;
1558 margin-top: 4px;
1559}
1560
1561/* ------------------------------------------------------------------ */
1562/* Empty state + keyboard focus (milestone 8) */
1563/* ------------------------------------------------------------------ */
1564
1565.empty-state {
1566 display: flex;
1567 flex-direction: column;
1568 align-items: center;
1569 justify-content: center;
1570 gap: 8px;
1571 padding: 64px 20px;
1572 text-align: center;
1573}
1574
1575.empty-glyph {
1576 width: 52px;
1577 height: 52px;
1578}
1579
1580.empty-state h3 {
1581 margin: 0;
1582 font-size: 16px;
1583}
1584
1585/* Visible keyboard focus on selectable file/folder entries. The ring is
1586 inset (negative offset) so the scroll container never clips it. */
1587.tile:focus-visible,
1588.row:focus-visible {
1589 outline: 2px solid var(--accent);
1590 outline-offset: -2px;
1591}
1592