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}
640
641.tile:hover {
642 border-color: var(--muted);
643}
644
645.tile-icon {
646 width: 42px;
647 height: 42px;
648 margin: 0 auto 10px;
649 display: block;
650}
651
652.tile-name {
653 font-size: 13px;
654 line-height: 1.3;
655 word-break: break-word;
656}
657
658/* list view */
659
660.entries-list {
661 background: var(--panel);
662 border: 1px solid var(--border);
663 overflow: hidden;
664}
665
666.row {
667 display: grid;
668 grid-template-columns: 28px minmax(0, 1fr) 90px 150px;
669 gap: 12px;
670 align-items: center;
671 padding: 9px 14px;
672 border-bottom: 1px solid var(--border);
673 cursor: pointer;
674 font-size: 14px;
675}
676
677.row:last-child {
678 border-bottom: none;
679}
680
681.row:hover {
682 background: var(--bg);
683}
684
685.row-icon {
686 width: 20px;
687 height: 20px;
688}
689
690.row-name {
691 overflow: hidden;
692 text-overflow: ellipsis;
693 white-space: nowrap;
694}
695
696.row-size,
697.row-date {
698 font-size: 13px;
699 text-align: right;
700}
701
702@media (max-width: 640px) {
703 .row {
704 grid-template-columns: 28px minmax(0, 1fr) 90px;
705 }
706
707 .row-date {
708 display: none;
709 }
710}
711
712/* context menu */
713
714.ctx-menu {
715 position: fixed;
716 z-index: 100;
717 min-width: 190px;
718 background: var(--panel);
719 border: 1px solid var(--border);
720 padding: 5px;
721 box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
722}
723
724.ctx-item {
725 display: flex;
726 align-items: center;
727 gap: 10px;
728 padding: 8px 12px;
729 font-size: 14px;
730 cursor: pointer;
731}
732
733.ic-ctx {
734 width: 18px;
735 height: 18px;
736 flex: none;
737}
738
739.ctx-item:hover {
740 background: var(--bg);
741}
742
743.ctx-item.disabled {
744 opacity: 0.45;
745 cursor: default;
746}
747
748.ctx-item.disabled:hover {
749 background: none;
750}
751
752/* toolbar selection actions (top right) */
753
754.sel-actions {
755 display: flex;
756 align-items: center;
757 gap: 6px;
758 position: relative;
759}
760
761.sel-actions .icon-btn:disabled {
762 opacity: 0.45;
763 cursor: default;
764}
765
766.sel-actions .icon-btn:disabled:hover {
767 border-color: var(--border);
768 color: var(--muted);
769}
770
771.sel-more-wrap {
772 position: relative;
773}
774
775.sel-more-menu {
776 position: absolute;
777 right: 0;
778 top: calc(100% + 6px);
779 z-index: 100;
780 min-width: 190px;
781 background: var(--panel);
782 border: 1px solid var(--border);
783 padding: 5px;
784 box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
785}
786
787.sel-more-menu.hidden {
788 display: none;
789}
790
791/* selection actions in the top bar */
792
793.topbar .sel-actions {
794 min-height: 0;
795}
796
797.topbar .sel-actions .icon-btn {
798 padding: 5px 7px;
799}
800
801/* selection highlight in the grid and list */
802
803.tile.selected {
804 border-color: var(--accent);
805 background: color-mix(in srgb, var(--accent) 12%, var(--panel));
806}
807
808.row.selected {
809 background: color-mix(in srgb, var(--accent) 12%, var(--panel));
810 box-shadow: inset 2px 0 0 var(--accent);
811}
812
813/* settings tabs (profile / server) */
814
815.settings-tabs {
816 display: flex;
817 gap: 8px;
818}
819
820.settings-tab-btn {
821 padding: 7px 14px;
822 border: 1px solid var(--border);
823 background: var(--panel);
824 color: var(--muted);
825 font-size: 13px;
826 font-weight: 600;
827}
828
829.settings-tab-btn:hover {
830 border-color: var(--muted);
831 color: var(--text);
832}
833
834.settings-tab-btn.active {
835 border-color: var(--accent);
836 color: var(--accent);
837 background: color-mix(in srgb, var(--accent) 10%, var(--panel));
838}
839
840/* toast */
841
842.toast {
843 position: fixed;
844 bottom: 20px;
845 right: 20px;
846 z-index: 200;
847 background: var(--panel);
848 color: var(--text);
849 border: 1px solid var(--border);
850 padding: 11px 16px;
851 font-size: 14px;
852 box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
853 max-width: 320px;
854}
855
856
857/* ---- modals ---- */
858
859.modal-overlay {
860 position: fixed;
861 inset: 0;
862 background: rgb(0 0 0 / 45%);
863 display: flex;
864 align-items: center;
865 justify-content: center;
866 z-index: 100;
867}
868
869.modal {
870 width: 440px;
871 max-width: calc(100vw - 32px);
872 padding: 20px;
873 background: var(--panel);
874 border: 1px solid var(--border);
875 box-shadow: 0 12px 44px rgb(0 0 0 / 35%);
876}
877
878.modal.picker {
879 width: 520px;
880}
881
882.modal-title {
883 margin: 0 0 12px;
884 font-size: 16px;
885}
886
887.modal-label {
888 display: block;
889 font-size: 13px;
890 color: var(--muted);
891 margin-bottom: 6px;
892}
893
894.modal .input {
895 width: 100%;
896 box-sizing: border-box;
897 margin-bottom: 16px;
898}
899
900.modal-message {
901 white-space: pre-line;
902 font-size: 14px;
903 margin: 0 0 8px;
904}
905
906.modal-actions {
907 display: flex;
908 justify-content: flex-end;
909 gap: 8px;
910 margin-top: 16px;
911}
912
913.btn-danger {
914 background: var(--danger);
915 /* A visible border (one step away from the fill, per theme) so the
916 button's edge reads the same as on neutral buttons. */
917 border-color: var(--danger-border);
918 color: #fff;
919}
920
921.btn-danger:hover {
922 background: color-mix(in srgb, var(--danger) 85%, black);
923}
924
925.btn:disabled {
926 opacity: 0.5;
927 cursor: not-allowed;
928}
929
930.conflict-list {
931 max-height: 200px;
932 overflow-y: auto;
933 margin: 8px 0;
934 padding-left: 20px;
935 font-size: 13px;
936}
937
938.picker-roots {
939 display: flex;
940 gap: 6px;
941 flex-wrap: wrap;
942 margin-bottom: 12px;
943}
944
945.root-opt {
946 font-size: 13px;
947}
948
949.root-opt.active {
950 background: var(--accent);
951 border-color: var(--accent);
952 color: #fff;
953}
954
955.picker-current {
956 font-size: 13px;
957 margin: 0 0 8px;
958}
959
960.picker-crumbs {
961 margin-bottom: 8px;
962}
963
964.picker-entries {
965 max-height: 240px;
966 overflow-y: auto;
967 border: 1px solid var(--border);
968}
969
970.picker-row {
971 display: flex;
972 align-items: center;
973 gap: 10px;
974 padding: 8px 12px;
975 cursor: pointer;
976}
977
978.picker-row:hover {
979 background: var(--bg);
980}
981
982/* ---------------------------------------------------------------------------
983 * Full-page file view (preview / editor / "no preview"). The top bar carries
984 * the title + actions; the content area below is edge to edge.
985 * ------------------------------------------------------------------------- */
986.content.file-open {
987 padding: 0;
988 overflow: hidden;
989}
990
991/* The browser stays mounted (state, fetch callback) but hidden. */
992.content.file-open .browser {
993 display: none;
994}
995
996.file-view {
997 flex: 1;
998 min-height: 0;
999 display: flex;
1000 flex-direction: column;
1001}
1002
1003.file-title {
1004 display: flex;
1005 align-items: center;
1006 gap: 8px;
1007 min-width: 0;
1008}
1009
1010.file-title-icon {
1011 width: 18px;
1012 height: 18px;
1013 flex: none;
1014 color: var(--muted);
1015}
1016
1017.file-title-name {
1018 font-weight: 600;
1019 font-size: 14px;
1020 white-space: nowrap;
1021 overflow: hidden;
1022 text-overflow: ellipsis;
1023}
1024
1025.file-actions {
1026 display: flex;
1027 align-items: center;
1028 gap: 10px;
1029 flex: none;
1030}
1031
1032.file-close {
1033 border: none;
1034 background: transparent;
1035 color: var(--muted);
1036 padding: 6px 8px;
1037}
1038
1039.file-close:hover {
1040 color: var(--text);
1041 background: var(--bg);
1042}
1043.preview-body {
1044 flex: 1;
1045 min-height: 0;
1046 overflow: auto;
1047 display: flex;
1048 background: var(--bg);
1049}
1050.preview-img {
1051 max-width: 100%;
1052 max-height: 100%;
1053 display: block;
1054 margin: auto;
1055}
1056.preview-pdf {
1057 width: 100%;
1058 height: 100%;
1059 border: none;
1060 background: #fff;
1061}
1062.preview-video {
1063 width: 100%;
1064 max-height: 100%;
1065 background: #000;
1066}
1067.preview-audio {
1068 display: flex;
1069 align-items: center;
1070 justify-content: center;
1071 width: 100%;
1072 padding: 24px;
1073}
1074.preview-audio audio {
1075 width: 100%;
1076 max-width: 480px;
1077}
1078
1079/* CodeMirror (text editor) */
1080.cm-container {
1081 height: 100%;
1082}
1083.cm-container .cm-editor {
1084 height: 100%;
1085}
1086/* Load/save failure: dims the editor and shows the message in a card
1087 * (the top-bar Save button stays reachable to retry). */
1088.cm-error {
1089 position: absolute;
1090 inset: 0;
1091 z-index: 5;
1092 display: flex;
1093 align-items: center;
1094 justify-content: center;
1095 padding: 24px;
1096 background: rgb(0 0 0 / 45%);
1097}
1098
1099.cm-error-card {
1100 position: relative;
1101 max-width: 440px;
1102 min-width: 200px;
1103 background: var(--panel);
1104 border: 1px solid var(--border);
1105 box-shadow: 0 12px 44px rgb(0 0 0 / 35%);
1106}
1107
1108.cm-error-close {
1109 position: absolute;
1110 top: 7px;
1111 right: 7px;
1112 display: inline-flex;
1113 align-items: center;
1114 justify-content: center;
1115 border: none;
1116 background: transparent;
1117 color: var(--muted);
1118 padding: 3px;
1119}
1120
1121.cm-error-close:hover {
1122 color: var(--text);
1123 background: var(--bg);
1124}
1125
1126.cm-error-close .ic-btn {
1127 width: 16px;
1128 height: 16px;
1129}
1130
1131.cm-error-text {
1132 padding: 16px 40px 16px 20px;
1133 font-size: 14px;
1134 text-align: center;
1135 overflow-wrap: anywhere;
1136}
1137
1138/* ---------------------------------------------------------------------------
1139 * Text editor (full page; title/save/close live in the top bar)
1140 * ------------------------------------------------------------------------- */
1141.file-editor {
1142 flex: 1;
1143 min-height: 0;
1144 display: flex;
1145 position: relative;
1146 background: var(--bg);
1147}
1148.file-editor .cm-container {
1149 width: 100%;
1150 height: 100%;
1151}
1152/* "No preview" fallback (binaries, archives). */
1153.file-nopreview {
1154 flex: 1;
1155 display: flex;
1156 align-items: center;
1157 justify-content: center;
1158}
1159.file-nopreview .btn-primary {
1160 width: auto;
1161 margin-top: 14px;
1162}
1163/* Conflict / discard dialogs render above the file view. */
1164.editor-sub-overlay {
1165 z-index: 101;
1166 background: rgb(0 0 0 / 25%);
1167}
1168
1169/* ---------------------------------------------------------------------------
1170 * Download format dialog
1171 * ------------------------------------------------------------------------- */
1172.dl-formats {
1173 display: flex;
1174 flex-direction: column;
1175 gap: 8px;
1176 margin-bottom: 8px;
1177}
1178.dl-fmt {
1179 justify-content: flex-start;
1180}
1181
1182/* ---------------------------------------------------------------------------
1183 * Item info dialog
1184 * ------------------------------------------------------------------------- */
1185.info-list {
1186 margin: 8px 0 16px;
1187 display: grid;
1188 grid-template-columns: 90px 1fr;
1189 row-gap: 8px;
1190 column-gap: 12px;
1191}
1192.info-list dt {
1193 color: var(--muted);
1194}
1195.info-list dd {
1196 margin: 0;
1197 word-break: break-word;
1198}
1199
1200/* ---------------------------------------------------------------------------
1201 Shares (milestone 6)
1202 --------------------------------------------------------------------------- */
1203
1204.share-modal {
1205 width: 480px;
1206}
1207
1208.share-link-row {
1209 display: flex;
1210 gap: 8px;
1211 margin: 12px 0;
1212}
1213
1214.share-link-input {
1215 flex: 1;
1216 font-family: ui-monospace, monospace;
1217 font-size: 12.5px;
1218 padding: 8px 10px;
1219 border: 1px solid var(--border);
1220 background: var(--bg);
1221 color: var(--text);
1222 min-width: 0;
1223}
1224
1225.share-check {
1226 display: flex;
1227 align-items: center;
1228 gap: 8px;
1229 margin: 4px 0 14px;
1230 font-size: 14px;
1231}
1232
1233.share-check input {
1234 width: 16px;
1235 height: 16px;
1236}
1237
1238.share-expiry-row {
1239 display: flex;
1240 align-items: center;
1241 gap: 10px;
1242 margin: 0 0 10px;
1243}
1244
1245.share-expiry-row label {
1246 font-size: 13px;
1247 color: var(--muted);
1248 min-width: 52px;
1249}
1250
1251.share-expiry-row select,
1252.share-expiry-row input {
1253 flex: 1;
1254 padding: 8px 10px;
1255 border: 1px solid var(--border);
1256 background: var(--panel);
1257 color: var(--text);
1258 font: inherit;
1259}
1260
1261.share-expiry-note {
1262 margin: 4px 0 0;
1263 font-size: 12.5px;
1264}
1265
1266.btn-sm {
1267 padding: 5px 10px;
1268 font-size: 12.5px;
1269}
1270
1271.icon-btn-sm {
1272 padding: 4px 6px;
1273}
1274
1275/* "Your shares" manage view */
1276
1277.shares-view {
1278 width: 100%;
1279 max-width: 960px;
1280 margin: 0 auto;
1281}
1282
1283.view-header {
1284 margin-bottom: 16px;
1285}
1286
1287.view-header .admin-section-title {
1288 margin-bottom: 0;
1289}
1290
1291.view-hint {
1292 margin: 4px 0 0;
1293 font-size: 13px;
1294 max-width: 64ch;
1295}
1296
1297.share-list {
1298 display: flex;
1299 flex-direction: column;
1300 gap: 10px;
1301}
1302
1303.share-item {
1304 display: flex;
1305 align-items: center;
1306 justify-content: space-between;
1307 gap: 12px;
1308 padding: 10px 12px;
1309 border: 1px solid var(--border);
1310 background: var(--panel);
1311}
1312
1313.share-item-main {
1314 min-width: 0;
1315}
1316
1317.share-item-name {
1318 display: flex;
1319 align-items: center;
1320 gap: 8px;
1321 font-weight: 600;
1322 font-size: 14px;
1323 overflow: hidden;
1324 text-overflow: ellipsis;
1325 white-space: nowrap;
1326}
1327
1328.ic-row {
1329 width: 18px;
1330 height: 18px;
1331 flex: none;
1332}
1333
1334.share-item-meta {
1335 font-size: 12px;
1336 margin-top: 2px;
1337 overflow: hidden;
1338 text-overflow: ellipsis;
1339 white-space: nowrap;
1340}
1341
1342.share-item-actions {
1343 display: flex;
1344 gap: 6px;
1345 flex-shrink: 0;
1346}
1347
1348/* ------------------------------------------------------------------ */
1349/* Admin (milestone 7) */
1350/* ------------------------------------------------------------------ */
1351
1352.admin-view {
1353 display: flex;
1354 flex-direction: column;
1355 gap: 16px;
1356 width: 100%;
1357 max-width: 960px;
1358 margin: 0 auto;
1359}
1360
1361.admin-section-title {
1362 margin: 0 0 14px;
1363 font-size: 15px;
1364}
1365
1366.users-header {
1367 display: flex;
1368 align-items: center;
1369 justify-content: space-between;
1370 margin-bottom: 4px;
1371}
1372
1373.users-header .admin-section-title {
1374 margin-bottom: 0;
1375}
1376
1377.users-header .btn-primary {
1378 width: auto;
1379}
1380
1381.user-list {
1382 display: flex;
1383 flex-direction: column;
1384 gap: 10px;
1385}
1386
1387.user-row {
1388 display: flex;
1389 align-items: center;
1390 justify-content: space-between;
1391 gap: 12px;
1392 padding: 12px 14px;
1393 border: 1px solid var(--border);
1394}
1395
1396.user-row-main {
1397 min-width: 0;
1398}
1399
1400.user-row-name {
1401 display: flex;
1402 align-items: center;
1403 gap: 6px;
1404 font-weight: 600;
1405 font-size: 14px;
1406 flex-wrap: wrap;
1407}
1408
1409.ic-admin {
1410 width: 16px;
1411 height: 16px;
1412 flex: none;
1413}
1414
1415.user-row-actions {
1416 display: flex;
1417 gap: 6px;
1418 flex-shrink: 0;
1419}
1420
1421.badge-off {
1422 background: color-mix(in srgb, var(--danger) 15%, var(--panel));
1423 border-color: var(--danger);
1424 color: var(--danger);
1425}
1426
1427.setting-row {
1428 display: flex;
1429 align-items: center;
1430 gap: 10px;
1431 padding: 14px;
1432 border: 1px solid var(--border);
1433 cursor: pointer;
1434}
1435
1436.setting-row input {
1437 margin-left: auto;
1438}
1439
1440.setting-label {
1441 display: block;
1442 font-weight: 600;
1443 font-size: 14px;
1444}
1445
1446.setting-desc {
1447 display: block;
1448 font-size: 12px;
1449 color: var(--muted);
1450 margin-top: 2px;
1451}
1452
1453/* Setting row whose control is a select, pushed to the right. */
1454.setting-row-select {
1455 cursor: default;
1456}
1457
1458.setting-row-select > span {
1459 flex: 1;
1460}
1461
1462.setting-row-select select {
1463 appearance: none;
1464 margin-left: auto;
1465 padding: 10px 42px 10px 12px;
1466 border: 1px solid var(--border);
1467 background-color: var(--panel);
1468 background-image: var(--select-arrow);
1469 background-repeat: no-repeat;
1470 background-position: right 12px center;
1471 background-size: 14px 14px;
1472 color: var(--text);
1473 font: inherit;
1474 max-width: 190px;
1475}
1476
1477/* User create/edit form */
1478
1479.user-form {
1480 width: 520px;
1481}
1482
1483.field-input {
1484 width: 100%;
1485 box-sizing: border-box;
1486}
1487
1488.field-hint {
1489 font-size: 12px;
1490 margin: -2px 0 8px;
1491}
1492
1493.check-row {
1494 display: flex;
1495 align-items: center;
1496 gap: 8px;
1497 font-size: 14px;
1498 margin-bottom: 12px;
1499 cursor: pointer;
1500}
1501
1502.check-row input {
1503 width: auto;
1504}
1505
1506.form-error {
1507 color: var(--danger);
1508 font-size: 13px;
1509 margin: 4px 0 8px;
1510}
1511
1512.roots-editor {
1513 display: flex;
1514 flex-direction: column;
1515 gap: 6px;
1516}
1517
1518.root-draft {
1519 display: flex;
1520 align-items: center;
1521 gap: 8px;
1522}
1523
1524.root-draft-path {
1525 flex: 1;
1526 font-size: 13px;
1527 overflow: hidden;
1528 text-overflow: ellipsis;
1529 white-space: nowrap;
1530}
1531
1532.root-draft-mode {
1533 width: auto;
1534 padding: 6px 8px;
1535}
1536
1537.root-add {
1538 display: flex;
1539 gap: 8px;
1540 margin-top: 4px;
1541}
1542
1543/* ------------------------------------------------------------------ */
1544/* Empty state + keyboard focus (milestone 8) */
1545/* ------------------------------------------------------------------ */
1546
1547.empty-state {
1548 display: flex;
1549 flex-direction: column;
1550 align-items: center;
1551 justify-content: center;
1552 gap: 8px;
1553 padding: 64px 20px;
1554 text-align: center;
1555}
1556
1557.empty-glyph {
1558 width: 52px;
1559 height: 52px;
1560}
1561
1562.empty-state h3 {
1563 margin: 0;
1564 font-size: 16px;
1565}
1566
1567/* Visible keyboard focus on selectable file/folder entries. */
1568.tile:focus-visible,
1569.row:focus-visible {
1570 outline: 2px solid var(--accent);
1571 outline-offset: 2px;
1572}
1573