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,
144textarea:focus {
145 outline: 2px solid var(--accent);
146 outline-offset: -1px;
147}
148
149textarea {
150 width: 100%;
151 padding: 10px 12px;
152 border: 1px solid var(--border);
153 background: var(--bg);
154 color: var(--text);
155 font: inherit;
156 font-size: 14px;
157 resize: vertical;
158}
159
160/* Square checkboxes — native controls are rounded, the theme is sharp. */
161input[type="checkbox"] {
162 appearance: none;
163 -webkit-appearance: none;
164 width: 16px;
165 height: 16px;
166 margin: 0;
167 padding: 0;
168 flex: none;
169 border: 1px solid var(--border);
170 background: var(--panel);
171 cursor: pointer;
172 position: relative;
173 vertical-align: -3px;
174}
175
176input[type="checkbox"]:checked {
177 background: var(--accent);
178 border-color: var(--accent-border);
179}
180
181input[type="checkbox"]:checked::after {
182 content: "";
183 position: absolute;
184 left: 4px;
185 top: 1px;
186 width: 4px;
187 height: 8px;
188 border: solid #fff;
189 border-width: 0 2px 2px 0;
190 transform: rotate(45deg);
191}
192
193button,
194:where(a.btn) {
195 font: inherit;
196 cursor: pointer;
197 display: inline-flex;
198 align-items: center;
199 justify-content: center;
200 gap: 6px;
201 border: 1px solid var(--border);
202 background: var(--panel);
203 color: var(--text);
204 padding: 9px 14px;
205}
206
207/* Browser link colors are unreadable on the dark panels. */
208a {
209 color: var(--accent);
210}
211
212a.btn {
213 color: var(--text);
214 text-decoration: none;
215}
216
217button:hover,
218:where(a.btn):hover {
219 border-color: var(--muted);
220}
221
222button:disabled {
223 opacity: 0.6;
224 cursor: default;
225}
226
227/* No width here on purpose. Full width belongs to the few places that ask
228 for it (.auth-card, .unlock-btn), not to every dialog's action row. */
229.btn-primary {
230 background: var(--accent);
231 /* One step away from the fill, per theme (see palette above), so the
232 button's edge reads the same as on neutral buttons. */
233 border-color: var(--accent-border);
234 color: #fff;
235 font-weight: 600;
236}
237
238.btn-primary:hover {
239 background: var(--accent-hover);
240 border-color: var(--accent-border);
241}
242
243.error {
244 color: var(--danger);
245 font-size: 13px;
246 min-height: 18px;
247 margin: 0 0 10px;
248}
249
250/* layout (signed-in): full-width topbar, then sidebar + content below */
251
252.layout {
253 min-height: 100vh;
254 display: flex;
255 flex-direction: column;
256}
257
258.topbar {
259 flex: none;
260 position: sticky;
261 top: 0;
262 z-index: 130;
263 height: var(--topbar-h);
264 display: flex;
265 align-items: center;
266 justify-content: space-between;
267 gap: 12px;
268 padding: 0 16px;
269 background: var(--panel);
270 border-bottom: 1px solid var(--border);
271}
272
273.topbar-left {
274 display: flex;
275 align-items: center;
276 gap: 10px;
277 min-width: 0;
278}
279
280.icon-btn.nav-toggle {
281 display: none;
282}
283
284.body-row {
285 flex: 1;
286 display: flex;
287 align-items: stretch;
288}
289
290/* ---- sidebar ---- */
291
292.sidebar {
293 flex: none;
294 width: 224px;
295 display: flex;
296 flex-direction: column;
297 background: var(--panel);
298 border-right: 1px solid var(--border);
299 position: sticky;
300 top: var(--topbar-h);
301 height: calc(100vh - var(--topbar-h));
302}
303
304.brand {
305 display: flex;
306 align-items: center;
307 gap: 10px;
308 font-weight: 700;
309 white-space: nowrap;
310 color: inherit;
311 text-decoration: none;
312 cursor: pointer;
313}
314
315.brand:hover {
316 opacity: 0.8;
317}
318
319.brand-name {
320 overflow: hidden;
321 text-overflow: ellipsis;
322}
323
324.logo {
325 width: 26px;
326 height: 26px;
327 flex: none;
328 display: block;
329}
330
331.nav {
332 flex: 1;
333 overflow-y: auto;
334 padding: 14px 8px 16px;
335 display: flex;
336 flex-direction: column;
337 gap: 20px;
338}
339
340.sidebar-user {
341 padding: 0 10px 2px;
342 font-size: 14px;
343 font-weight: 600;
344 overflow: hidden;
345 text-overflow: ellipsis;
346 white-space: nowrap;
347}
348
349.nav-section-label {
350 font-size: 11px;
351 font-weight: 700;
352 text-transform: uppercase;
353 letter-spacing: 0.08em;
354 color: var(--muted);
355 padding: 0 10px 6px;
356}
357
358/* Section header with a right-aligned action (the Files refresh). */
359.nav-section-head {
360 display: flex;
361 align-items: center;
362 justify-content: space-between;
363 gap: 6px;
364 margin-bottom: 4px;
365}
366
367.nav-section-head .nav-section-label {
368 flex: 1;
369 padding-bottom: 2px;
370}
371
372.nav-section-refresh {
373 color: var(--muted);
374 background: none;
375 border-color: transparent;
376 padding: 3px 5px;
377}
378
379.nav-section-refresh:hover {
380 color: var(--text);
381 border-color: var(--border);
382}
383
384.nav-tab {
385 display: flex;
386 align-items: center;
387 justify-content: flex-start;
388 gap: 10px;
389 width: 100%;
390 margin-bottom: 2px;
391 padding: 8px 10px;
392 text-align: left;
393 background: none;
394 border: none;
395 color: var(--muted);
396 font-size: 14px;
397}
398
399.nav-tab:hover {
400 background: var(--bg);
401 color: var(--text);
402}
403
404.nav-tab.active {
405 background: var(--accent);
406 border-color: var(--accent);
407 color: #fff;
408}
409
410.ic-tab {
411 width: 18px;
412 height: 18px;
413 flex: none;
414}
415
416.nav-tab.active .icon {
417 color: #fff;
418}
419
420.nav-tab-name {
421 flex: 1;
422 min-width: 0;
423 overflow: hidden;
424 text-overflow: ellipsis;
425 white-space: nowrap;
426}
427
428.nav-tab-note {
429 flex: none;
430 font-size: 10px;
431 font-weight: 700;
432 padding: 1px 5px;
433 background: var(--bg);
434 border: 1px solid var(--border);
435 color: var(--muted);
436}
437
438.nav-tab.active .nav-tab-note {
439 background: rgb(255 255 255 / 18%);
440 border-color: rgb(255 255 255 / 30%);
441 color: #fff;
442}
443
444.nav-backdrop {
445 display: none;
446}
447
448/* Sidebar footer: theme toggle, log out, and the project version.
449 Sits at the bottom of the sidebar, below the manage section. */
450.sidebar-footer {
451 flex: none;
452 border-top: 1px solid var(--border);
453 padding: 8px;
454}
455
456.sidebar-footer .nav-tab {
457 margin-bottom: 0;
458}
459
460.sidebar-action.logout {
461 color: var(--danger);
462}
463
464.sidebar-action.logout:hover {
465 color: var(--danger);
466}
467
468.sidebar-version {
469 margin-top: 8px;
470 padding: 0 10px;
471 font-size: 11.5px;
472 color: var(--muted);
473 white-space: nowrap;
474 overflow: hidden;
475 text-overflow: ellipsis;
476}
477
478.badge {
479 font-size: 11px;
480 padding: 2px 8px;
481 background: var(--bg);
482 border: 1px solid var(--border);
483 color: var(--muted);
484}
485
486.content {
487 flex: 1;
488 min-width: 0;
489 min-height: 0;
490 display: flex;
491 flex-direction: column;
492 padding: 20px;
493}
494
495/* While a file is open the app is a fixed-height frame: the file view
496 * (editor, image, …) scrolls inside, the page itself never grows. */
497.layout.file-mode {
498 height: 100vh;
499 overflow: hidden;
500}
501
502.layout.file-mode .body-row {
503 min-height: 0;
504}
505
506/* Narrow screens: the sidebar becomes an off-canvas drawer that only
507 covers the content, below the (always visible) topbar. */
508@media (max-width: 860px) {
509 .sidebar {
510 position: fixed;
511 top: var(--topbar-h);
512 bottom: 0;
513 left: 0;
514 right: auto;
515 height: auto;
516 z-index: 120;
517 transform: translateX(-100%);
518 transition: transform 0.15s ease;
519 }
520
521 .sidebar.open {
522 transform: translateX(0);
523 box-shadow: 0 0 48px rgb(0 0 0 / 35%);
524 }
525
526 .nav-backdrop {
527 display: block;
528 position: fixed;
529 top: var(--topbar-h);
530 bottom: 0;
531 left: 0;
532 right: 0;
533 z-index: 110;
534 background: rgb(0 0 0 / 40%);
535 }
536
537 .icon-btn.nav-toggle {
538 display: inline-flex;
539 }
540
541 .content {
542 padding: 14px;
543 }
544}
545
546/* ---- file browser ---- */
547
548.browser {
549 width: 100%;
550 max-width: 1200px;
551 margin: 0 auto;
552 flex: 1;
553 display: flex;
554 flex-direction: column;
555 min-height: 0;
556}
557
558.icon {
559 width: 20px;
560 height: 20px;
561 display: inline-block;
562 vertical-align: middle;
563}
564
565.ic-folder {
566 color: var(--accent);
567}
568
569.ic-muted {
570 color: var(--muted);
571}
572
573.crumbs {
574 display: flex;
575 align-items: center;
576 gap: 2px;
577 padding: 6px 0 12px;
578 font-size: 15px;
579 flex-wrap: wrap;
580}
581
582.crumb {
583 cursor: pointer;
584 color: var(--muted);
585 padding: 3px 6px;
586}
587
588.crumb:hover {
589 color: var(--text);
590 background: var(--panel);
591}
592
593.crumb.current {
594 color: var(--text);
595 font-weight: 600;
596 cursor: default;
597 background: none;
598}
599
600.crumb-sep {
601 color: var(--muted);
602 padding: 0 1px;
603}
604
605.toolbar {
606 display: flex;
607 align-items: center;
608 justify-content: space-between;
609 gap: 6px;
610 padding-bottom: 12px;
611}
612
613.toolbar-right {
614 display: flex;
615 gap: 6px;
616}
617
618/* Sort control in the toolbar. The select mirrors the app's other selects:
619 no native chrome, own arrow. */
620.sort-label {
621 font-size: 13px;
622 color: var(--muted);
623 align-self: center;
624 margin-right: 2px;
625}
626
627.sort-select {
628 appearance: none;
629 width: auto;
630 padding: 7px 32px 7px 10px;
631 border: 1px solid var(--border);
632 background-color: var(--panel);
633 background-image: var(--select-arrow);
634 background-repeat: no-repeat;
635 background-position: right 9px center;
636 background-size: 12px 12px;
637 color: var(--text);
638 font: inherit;
639 font-size: 13px;
640}
641
642.sort-select:focus {
643 outline: 2px solid var(--accent);
644 outline-offset: -1px;
645}
646
647.icon-btn {
648 padding: 7px 9px;
649 border: 1px solid var(--border);
650 background: var(--panel);
651 color: var(--muted);
652 display: inline-flex;
653 align-items: center;
654}
655
656.icon-btn:hover {
657 border-color: var(--muted);
658}
659
660.icon-btn.active {
661 background: var(--accent);
662 border-color: var(--accent);
663 color: #fff;
664}
665
666.ic-btn {
667 width: 18px;
668 height: 18px;
669 color: inherit;
670}
671
672.entries-area {
673 position: relative;
674 flex: 1;
675 min-height: 240px;
676}
677
678.entries-area.drop-target {
679 outline: 2px dashed var(--accent);
680 outline-offset: -2px;
681}
682
683.drop-hint {
684 position: absolute;
685 inset: 0;
686 z-index: 2;
687 display: flex;
688 align-items: center;
689 justify-content: center;
690 gap: 8px;
691 font-weight: 500;
692 color: var(--accent);
693 background: color-mix(in srgb, var(--accent) 10%, var(--panel) 60%);
694 /* The hint must not steal the drag from the area beneath it. */
695 pointer-events: none;
696}
697
698.center-note {
699 text-align: center;
700 padding: 48px 0;
701}
702
703.error-card {
704 display: flex;
705 align-items: center;
706 justify-content: space-between;
707 gap: 16px;
708 color: var(--danger);
709 font-size: 14px;
710}
711
712/* root tiles were replaced by the sidebar's folder tabs */
713
714/* grid view */
715
716.entries-grid {
717 display: grid;
718 grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
719 gap: 10px;
720}
721
722.tile {
723 background: var(--panel);
724 border: 1px solid var(--border);
725 padding: 16px 10px 12px;
726 text-align: center;
727 cursor: pointer;
728 user-select: none;
729}
730
731/* Touch: a long press opens the context menu (see util::LongPress). The
732 iOS callout and text selection would otherwise appear first. Vertical
733 panning stays with the browser. */
734.entries-area,
735.tile,
736.row {
737 -webkit-touch-callout: none;
738 -webkit-user-select: none;
739 touch-action: pan-y;
740}
741
742.tile:hover {
743 border-color: var(--muted);
744}
745
746.tile-icon {
747 width: 42px;
748 height: 42px;
749 margin: 0 auto 10px;
750 display: block;
751}
752
753/* Holds the icon with the thumbnail layered over it. The icon is the
754 fallback when no thumbnail loads. */
755.tile-media {
756 position: relative;
757 display: flex;
758 align-items: center;
759 justify-content: center;
760 margin-bottom: 10px;
761}
762
763.tile-media .tile-icon {
764 margin: 0;
765}
766
767/* One tile height per folder: the taller media area applies to every tile,
768 not only the ones that end up with a thumbnail. */
769.has-thumbs .tile-media {
770 height: 92px;
771}
772
773/* No background: the element must stay transparent until the bytes arrive,
774 or it hides the icon underneath as a dark rectangle. */
775.tile-thumb {
776 position: absolute;
777 inset: 0;
778 width: 100%;
779 height: 100%;
780 object-fit: cover;
781}
782
783.tile-name {
784 font-size: 13px;
785 line-height: 1.3;
786 word-break: break-word;
787 /* Uniform tile height: the name area always takes two lines and
788 never overflows (the full name is in the title tooltip). */
789 min-height: calc(2 * 1.3em);
790 display: -webkit-box;
791 -webkit-line-clamp: 2;
792 -webkit-box-orient: vertical;
793 overflow: hidden;
794}
795
796/* list view */
797
798.entries-list {
799 background: var(--panel);
800 border: 1px solid var(--border);
801 overflow: hidden;
802}
803
804.row {
805 display: grid;
806 grid-template-columns: 28px minmax(0, 1fr) 90px 150px;
807 gap: 12px;
808 align-items: center;
809 padding: 9px 14px;
810 border-bottom: 1px solid var(--border);
811 cursor: pointer;
812 font-size: 14px;
813 user-select: none;
814}
815
816.row:last-child {
817 border-bottom: none;
818}
819
820.row:hover {
821 background: var(--bg);
822}
823
824.row-icon {
825 width: 20px;
826 height: 20px;
827}
828
829.row-name {
830 overflow: hidden;
831 text-overflow: ellipsis;
832 white-space: nowrap;
833}
834
835.row-size,
836.row-date {
837 font-size: 13px;
838 text-align: right;
839}
840
841@media (max-width: 640px) {
842 .row {
843 grid-template-columns: 28px minmax(0, 1fr) 90px;
844 }
845
846 .row-date {
847 display: none;
848 }
849}
850
851/* context menu */
852
853.ctx-menu {
854 position: fixed;
855 z-index: 100;
856 min-width: 190px;
857 background: var(--panel);
858 border: 1px solid var(--border);
859 padding: 5px;
860 box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
861}
862
863.ctx-item {
864 display: flex;
865 align-items: center;
866 gap: 10px;
867 padding: 8px 12px;
868 font-size: 14px;
869 cursor: pointer;
870}
871
872.ic-ctx {
873 width: 18px;
874 height: 18px;
875 flex: none;
876}
877
878.ctx-item:hover {
879 background: var(--bg);
880}
881
882.ctx-item.disabled {
883 opacity: 0.45;
884 cursor: default;
885}
886
887.ctx-item.disabled:hover {
888 background: none;
889}
890
891/* toolbar selection actions (top right) */
892
893.sel-actions {
894 display: flex;
895 align-items: center;
896 gap: 6px;
897 position: relative;
898}
899
900.sel-actions .icon-btn:disabled {
901 opacity: 0.45;
902 cursor: default;
903}
904
905.sel-actions .icon-btn:disabled:hover {
906 border-color: var(--border);
907 color: var(--muted);
908}
909
910.sel-more-menu {
911 position: fixed;
912 inset: auto;
913 margin: 0;
914 min-width: 190px;
915 color: inherit;
916 background: var(--panel);
917 border: 1px solid var(--border);
918 padding: 5px;
919 box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
920}
921
922/* selection actions in the top bar */
923
924.topbar .sel-actions {
925 min-height: 0;
926}
927
928.topbar .sel-actions .icon-btn {
929 padding: 5px 7px;
930}
931
932/* selection highlight in the grid and list */
933
934.tile.selected {
935 border-color: var(--accent);
936 background: color-mix(in srgb, var(--accent) 12%, var(--panel));
937}
938
939.row.selected {
940 background: color-mix(in srgb, var(--accent) 12%, var(--panel));
941 box-shadow: inset 2px 0 0 var(--accent);
942}
943
944/* settings tabs (profile / server) */
945
946.settings-tabs {
947 display: flex;
948 gap: 8px;
949}
950
951.settings-tab-btn {
952 padding: 7px 14px;
953 border: 1px solid var(--border);
954 background: var(--panel);
955 color: var(--muted);
956 font-size: 13px;
957 font-weight: 600;
958}
959
960.settings-tab-btn:hover {
961 border-color: var(--muted);
962 color: var(--text);
963}
964
965.settings-tab-btn.active {
966 border-color: var(--accent);
967 color: var(--accent);
968 background: color-mix(in srgb, var(--accent) 10%, var(--panel));
969}
970
971/* toast */
972
973.toast {
974 position: fixed;
975 bottom: 20px;
976 right: 20px;
977 z-index: 200;
978 background: var(--panel);
979 color: var(--text);
980 border: 1px solid var(--border);
981 padding: 11px 16px;
982 font-size: 14px;
983 box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
984 max-width: 320px;
985}
986
987.toast-error {
988 background: color-mix(in srgb, var(--danger) 12%, var(--panel));
989 border-color: var(--danger-border);
990 color: var(--danger);
991}
992
993
994/* ---- modals ---- */
995
996/* The shell is a native <dialog> opened with showModal(): the browser puts
997 it in the top layer and paints ::backdrop, so there is no overlay element
998 to position and no z-index to keep in order. The reset below strips the
999 UA's own box so the .modal card inside is the only thing that shows. */
1000.modal-host {
1001 padding: 0;
1002 border: none;
1003 background: none;
1004 /* The UA caps an open dialog's size; the card sets its own. */
1005 max-width: none;
1006 max-height: none;
1007 /* Centre it in the inset-0 box, as the old flex overlay did. */
1008 margin: auto;
1009 overflow: visible;
1010}
1011
1012.modal-host::backdrop {
1013 background: rgb(0 0 0 / 45%);
1014}
1015
1016.modal {
1017 width: 440px;
1018 max-width: calc(100vw - 32px);
1019 padding: 20px;
1020 background: var(--panel);
1021 border: 1px solid var(--border);
1022 box-shadow: 0 12px 44px rgb(0 0 0 / 35%);
1023}
1024
1025.modal.picker {
1026 width: 640px;
1027}
1028
1029.modal-title {
1030 margin: 0 0 12px;
1031 font-size: 16px;
1032}
1033
1034.modal-label {
1035 display: block;
1036 font-size: 13px;
1037 color: var(--muted);
1038 margin-bottom: 6px;
1039}
1040
1041.modal .input {
1042 width: 100%;
1043 box-sizing: border-box;
1044 margin-bottom: 16px;
1045}
1046
1047.modal-label.error,
1048.dialog-error {
1049 color: var(--danger);
1050 font-size: 13px;
1051}
1052
1053.dialog-error {
1054 margin-top: 8px;
1055}
1056
1057.modal-message {
1058 white-space: pre-line;
1059 font-size: 14px;
1060 margin: 0 0 8px;
1061}
1062
1063.modal-actions {
1064 display: flex;
1065 justify-content: flex-end;
1066 gap: 8px;
1067 margin-top: 16px;
1068}
1069
1070.btn-danger {
1071 background: var(--danger);
1072 /* A visible border (one step away from the fill, per theme) so the
1073 button's edge reads the same as on neutral buttons. */
1074 border-color: var(--danger-border);
1075 color: #fff;
1076}
1077
1078.btn-danger:hover {
1079 background: color-mix(in srgb, var(--danger) 85%, black);
1080}
1081
1082.btn:disabled {
1083 opacity: 0.5;
1084 cursor: not-allowed;
1085}
1086
1087.conflict-list {
1088 max-height: 200px;
1089 overflow-y: auto;
1090 margin: 8px 0;
1091 padding-left: 20px;
1092 font-size: 13px;
1093}
1094
1095/* Folder picker: roots on the left, the selected root's tree on the right. */
1096
1097.picker-body {
1098 display: grid;
1099 grid-template-columns: 180px minmax(0, 1fr);
1100 border: 1px solid var(--border);
1101 height: 320px;
1102}
1103
1104.picker-roots {
1105 display: flex;
1106 flex-direction: column;
1107 gap: 2px;
1108 padding: 6px;
1109 border-right: 1px solid var(--border);
1110 background: var(--bg);
1111 min-height: 0;
1112 overflow-y: auto;
1113}
1114
1115.picker-root {
1116 display: flex;
1117 align-items: center;
1118 justify-content: flex-start;
1119 gap: 8px;
1120 padding: 7px 8px;
1121 border: none;
1122 background: transparent;
1123 color: var(--text);
1124 font: inherit;
1125 font-size: 13px;
1126 text-align: left;
1127 cursor: pointer;
1128 min-width: 0;
1129}
1130
1131.picker-root:hover {
1132 background: var(--panel);
1133}
1134
1135.picker-root.active {
1136 background: var(--accent);
1137 color: #fff;
1138}
1139
1140.picker-root.ro .picker-root-name {
1141 opacity: 0.6;
1142 text-decoration: line-through dotted;
1143}
1144
1145.picker-root-ic {
1146 width: 16px;
1147 height: 16px;
1148 flex: none;
1149}
1150
1151.picker-root-name {
1152 overflow: hidden;
1153 text-overflow: ellipsis;
1154 white-space: nowrap;
1155}
1156
1157.picker-main {
1158 display: flex;
1159 flex-direction: column;
1160 min-width: 0;
1161 /* Grid items default to min-height:auto, which would let a long
1162 folder list stretch the dialog instead of scrolling. */
1163 min-height: 0;
1164}
1165
1166.picker-crumbs {
1167 padding: 8px 10px;
1168 font-size: 13px;
1169 border-bottom: 1px solid var(--border);
1170 flex: none;
1171}
1172
1173.picker-entries {
1174 flex: 1;
1175 min-height: 0;
1176 overflow-y: auto;
1177}
1178
1179.picker-row {
1180 display: flex;
1181 align-items: center;
1182 gap: 10px;
1183 padding: 8px 12px;
1184 cursor: pointer;
1185 font-size: 14px;
1186 user-select: none;
1187}
1188
1189.picker-row:hover {
1190 background: var(--bg);
1191}
1192
1193.picker-row .row-name {
1194 flex: 1;
1195}
1196
1197/* A right-pointing chevron drawn with borders (no icon needed). */
1198.picker-chev {
1199 width: 7px;
1200 height: 7px;
1201 border-right: 1.5px solid var(--muted);
1202 border-bottom: 1.5px solid var(--muted);
1203 transform: rotate(-45deg);
1204 flex: none;
1205}
1206
1207.picker-empty {
1208 height: 100%;
1209 display: flex;
1210 flex-direction: column;
1211 align-items: center;
1212 justify-content: center;
1213 gap: 8px;
1214 font-size: 13px;
1215}
1216
1217.picker-empty-ic {
1218 width: 32px;
1219 height: 32px;
1220 opacity: 0.5;
1221}
1222
1223.picker-root.active .picker-root-ic {
1224 color: #fff;
1225}
1226
1227@media (max-width: 640px) {
1228 .picker-body {
1229 grid-template-columns: 1fr;
1230 grid-template-rows: auto minmax(0, 1fr);
1231 height: 380px;
1232 }
1233
1234 .picker-roots {
1235 flex-direction: row;
1236 flex-wrap: wrap;
1237 border-right: none;
1238 border-bottom: 1px solid var(--border);
1239 }
1240}
1241
1242/* ---------------------------------------------------------------------------
1243 * Full-page file view (preview / editor / "no preview"). The top bar carries
1244 * the title + actions; the content area below is edge to edge.
1245 * ------------------------------------------------------------------------- */
1246.content.file-open {
1247 padding: 0;
1248 overflow: hidden;
1249}
1250
1251/* The browser stays mounted (state, fetch callback) but hidden. */
1252.content.file-open .browser {
1253 display: none;
1254}
1255
1256.file-view {
1257 flex: 1;
1258 min-height: 0;
1259 display: flex;
1260 flex-direction: column;
1261}
1262
1263.file-title {
1264 display: flex;
1265 align-items: center;
1266 gap: 8px;
1267 min-width: 0;
1268}
1269
1270.file-title-icon {
1271 width: 18px;
1272 height: 18px;
1273 flex: none;
1274 color: var(--muted);
1275}
1276
1277.file-title-name {
1278 font-weight: 600;
1279 font-size: 14px;
1280 white-space: nowrap;
1281 overflow: hidden;
1282 text-overflow: ellipsis;
1283}
1284
1285.file-actions {
1286 display: flex;
1287 align-items: center;
1288 gap: 10px;
1289 flex: none;
1290}
1291
1292.file-close,
1293.file-download {
1294 border: none;
1295 background: transparent;
1296 color: var(--muted);
1297 padding: 6px 8px;
1298}
1299
1300.file-close:hover,
1301.file-download:hover {
1302 color: var(--text);
1303 background: var(--bg);
1304}
1305.preview-body {
1306 flex: 1;
1307 min-height: 0;
1308 overflow: auto;
1309 display: flex;
1310 background: var(--bg);
1311}
1312.preview-img {
1313 max-width: 100%;
1314 max-height: 100%;
1315 display: block;
1316 margin: auto;
1317}
1318.preview-pdf {
1319 width: 100%;
1320 height: 100%;
1321 border: none;
1322 background: #fff;
1323}
1324.preview-video {
1325 width: 100%;
1326 max-height: 100%;
1327 background: #000;
1328}
1329.preview-audio {
1330 display: flex;
1331 align-items: center;
1332 justify-content: center;
1333 width: 100%;
1334 padding: 24px;
1335}
1336.preview-audio audio {
1337 width: 100%;
1338 max-width: 480px;
1339}
1340
1341/* CodeMirror (text editor) */
1342.cm-container {
1343 height: 100%;
1344}
1345.cm-container .cm-editor {
1346 height: 100%;
1347}
1348/* Load/save failure: dims the editor and shows the message in a card
1349 * (the top-bar Save button stays reachable to retry). */
1350.cm-error {
1351 position: absolute;
1352 inset: 0;
1353 z-index: 5;
1354 display: flex;
1355 align-items: center;
1356 justify-content: center;
1357 padding: 24px;
1358 background: rgb(0 0 0 / 45%);
1359}
1360
1361.cm-error-card {
1362 position: relative;
1363 max-width: 440px;
1364 min-width: 200px;
1365 background: var(--panel);
1366 border: 1px solid var(--border);
1367 box-shadow: 0 12px 44px rgb(0 0 0 / 35%);
1368}
1369
1370.cm-error-close {
1371 position: absolute;
1372 top: 7px;
1373 right: 7px;
1374 display: inline-flex;
1375 align-items: center;
1376 justify-content: center;
1377 border: none;
1378 background: transparent;
1379 color: var(--muted);
1380 padding: 3px;
1381}
1382
1383.cm-error-close:hover {
1384 color: var(--text);
1385 background: var(--bg);
1386}
1387
1388.cm-error-close .ic-btn {
1389 width: 16px;
1390 height: 16px;
1391}
1392
1393.cm-error-text {
1394 padding: 16px 40px 16px 20px;
1395 font-size: 14px;
1396 text-align: center;
1397 overflow-wrap: anywhere;
1398}
1399
1400/* ---------------------------------------------------------------------------
1401 * Text editor (full page; title/save/close live in the top bar)
1402 * ------------------------------------------------------------------------- */
1403.file-editor {
1404 flex: 1;
1405 min-height: 0;
1406 display: flex;
1407 position: relative;
1408 background: var(--bg);
1409}
1410.file-editor .cm-container {
1411 width: 100%;
1412 height: 100%;
1413}
1414/* "No preview" fallback (binaries, archives). */
1415.file-nopreview {
1416 flex: 1;
1417 display: flex;
1418 align-items: center;
1419 justify-content: center;
1420}
1421.file-nopreview .btn-primary {
1422 margin-top: 14px;
1423}
1424/* ---------------------------------------------------------------------------
1425 * Download format dialog
1426 * ------------------------------------------------------------------------- */
1427.dl-formats {
1428 display: flex;
1429 flex-direction: column;
1430 gap: 8px;
1431 margin-bottom: 8px;
1432}
1433.dl-fmt {
1434 justify-content: flex-start;
1435}
1436
1437/* ---------------------------------------------------------------------------
1438 * Item info dialog
1439 * ------------------------------------------------------------------------- */
1440.info-list {
1441 margin: 8px 0 16px;
1442 display: grid;
1443 grid-template-columns: 90px 1fr;
1444 row-gap: 8px;
1445 column-gap: 12px;
1446}
1447.info-list dt {
1448 color: var(--muted);
1449}
1450.info-list dd {
1451 margin: 0;
1452 word-break: break-word;
1453}
1454
1455/* ---------------------------------------------------------------------------
1456 Shares (milestone 6)
1457 --------------------------------------------------------------------------- */
1458
1459.share-modal {
1460 width: 480px;
1461}
1462
1463.share-link-row {
1464 display: flex;
1465 gap: 8px;
1466 margin: 12px 0;
1467}
1468
1469.share-link-input {
1470 flex: 1;
1471 font-family: ui-monospace, monospace;
1472 font-size: 12.5px;
1473 padding: 8px 10px;
1474 border: 1px solid var(--border);
1475 background: var(--bg);
1476 color: var(--text);
1477 min-width: 0;
1478}
1479
1480.share-check {
1481 display: flex;
1482 align-items: center;
1483 gap: 8px;
1484 margin: 4px 0 14px;
1485 font-size: 14px;
1486}
1487
1488.share-check input {
1489 width: 16px;
1490 height: 16px;
1491}
1492
1493.share-expiry-row {
1494 display: flex;
1495 align-items: center;
1496 gap: 10px;
1497 margin: 0 0 10px;
1498}
1499
1500.share-expiry-row label {
1501 font-size: 13px;
1502 color: var(--muted);
1503}
1504
1505.share-expiry-row select,
1506.share-expiry-row input {
1507 flex: 1;
1508 padding: 8px 10px;
1509 border: 1px solid var(--border);
1510 background: var(--panel);
1511 color: var(--text);
1512 font: inherit;
1513}
1514
1515/* Optional password on the create-share form. Laid out like the expiry
1516 row above it, so the form reads as one column of labelled fields. */
1517.share-pw-row {
1518 display: flex;
1519 align-items: center;
1520 gap: 10px;
1521 margin: 0 0 6px;
1522}
1523
1524.share-pw-row label {
1525 font-size: 13px;
1526 color: var(--muted);
1527}
1528
1529/* Expiry and password sit in one column, so their inputs line up. */
1530.share-expiry-row label,
1531.share-pw-row label {
1532 min-width: 64px;
1533}
1534
1535.share-pw-row input {
1536 flex: 1;
1537 padding: 8px 10px;
1538 border: 1px solid var(--border);
1539 background: var(--panel);
1540 color: var(--text);
1541 font: inherit;
1542}
1543
1544.share-pw-hint {
1545 font-size: 12px;
1546 margin: 0 0 10px;
1547}
1548
1549/* The password gate of a protected share: the whole page is this card. */
1550.unlock-card {
1551 width: 320px;
1552 max-width: calc(100vw - 32px);
1553 padding: 20px;
1554 text-align: left;
1555}
1556
1557.unlock-card h2 {
1558 margin: 0 0 6px;
1559 font-size: 18px;
1560}
1561
1562.unlock-card input {
1563 margin-top: 14px;
1564}
1565
1566.unlock-btn {
1567 width: 100%;
1568 margin-top: 12px;
1569}
1570
1571.share-expiry-note {
1572 margin: 4px 0 0;
1573 font-size: 12.5px;
1574}
1575
1576.btn-sm {
1577 padding: 5px 10px;
1578 font-size: 12.5px;
1579}
1580
1581.icon-btn-sm {
1582 padding: 4px 6px;
1583}
1584
1585/* "Your shares" manage view */
1586
1587.shares-view {
1588 width: 100%;
1589 max-width: 960px;
1590 margin: 0 auto;
1591}
1592
1593.view-header {
1594 margin-bottom: 16px;
1595}
1596
1597.view-header .admin-section-title {
1598 margin-bottom: 0;
1599}
1600
1601.view-hint {
1602 margin: 4px 0 0;
1603 font-size: 13px;
1604 max-width: 64ch;
1605}
1606
1607.share-list {
1608 display: flex;
1609 flex-direction: column;
1610 gap: 10px;
1611}
1612
1613.share-item {
1614 display: flex;
1615 align-items: center;
1616 justify-content: space-between;
1617 gap: 12px;
1618 padding: 10px 12px;
1619 border: 1px solid var(--border);
1620 background: var(--panel);
1621}
1622
1623.share-item-main {
1624 min-width: 0;
1625}
1626
1627.share-item-name {
1628 display: flex;
1629 align-items: center;
1630 gap: 8px;
1631 font-weight: 600;
1632 font-size: 14px;
1633 overflow: hidden;
1634 text-overflow: ellipsis;
1635 white-space: nowrap;
1636}
1637
1638.ic-row {
1639 width: 18px;
1640 height: 18px;
1641 flex: none;
1642}
1643
1644.share-item-meta {
1645 font-size: 12px;
1646 margin-top: 2px;
1647 overflow: hidden;
1648 text-overflow: ellipsis;
1649 white-space: nowrap;
1650}
1651
1652.share-item-actions {
1653 display: flex;
1654 gap: 6px;
1655 flex-shrink: 0;
1656}
1657
1658/* Sections of the share management view: the viewer's own links and, for an
1659 admin, one section per account. Native <details>/<summary>. */
1660
1661.share-groups {
1662 display: flex;
1663 flex-direction: column;
1664 gap: 12px;
1665}
1666
1667.share-group {
1668 border: 1px solid var(--border);
1669 background: var(--panel);
1670}
1671
1672.share-group-head {
1673 display: flex;
1674 align-items: center;
1675 gap: 8px;
1676 padding: 10px 12px;
1677 font-size: 14px;
1678 font-weight: 600;
1679 cursor: pointer;
1680 /* The caret below stands in for the disclosure triangle. */
1681 list-style: none;
1682}
1683
1684.share-group-head::-webkit-details-marker {
1685 display: none;
1686}
1687
1688.share-group-head:hover {
1689 background: color-mix(in srgb, var(--accent) 6%, var(--panel));
1690}
1691
1692.share-group-head:focus-visible {
1693 outline: 2px solid var(--accent);
1694 outline-offset: -2px;
1695}
1696
1697.share-caret {
1698 width: 16px;
1699 height: 16px;
1700 flex: none;
1701 transition: rotate 0.15s ease;
1702}
1703
1704.share-group[open] .share-caret {
1705 rotate: 90deg;
1706}
1707
1708.share-group-count {
1709 font-size: 12px;
1710 font-weight: 400;
1711}
1712
1713/* A live link whose account cannot sign in any more. */
1714.share-group-warn {
1715 font-size: 12px;
1716 font-weight: 400;
1717 color: var(--danger);
1718}
1719
1720.share-group-del {
1721 margin-left: auto;
1722}
1723
1724/* Rows inside a section read as one block: the border moves from each row to
1725 the section. */
1726.share-group .share-list {
1727 gap: 0;
1728 border-top: 1px solid var(--border);
1729}
1730
1731.share-group .share-item {
1732 border: 0;
1733 border-top: 1px solid var(--border);
1734}
1735
1736.share-group .share-item:first-child {
1737 border-top: 0;
1738}
1739
1740/* ------------------------------------------------------------------ */
1741/* Admin (milestone 7) */
1742/* ------------------------------------------------------------------ */
1743
1744.admin-view {
1745 display: flex;
1746 flex-direction: column;
1747 gap: 16px;
1748 width: 100%;
1749 max-width: 960px;
1750 margin: 0 auto;
1751}
1752
1753.admin-section-title {
1754 margin: 0 0 14px;
1755 font-size: 15px;
1756}
1757
1758.users-header {
1759 display: flex;
1760 align-items: center;
1761 justify-content: space-between;
1762 margin-bottom: 4px;
1763}
1764
1765.users-header .admin-section-title {
1766 margin-bottom: 0;
1767}
1768
1769.users-header .btn-primary {
1770 width: auto;
1771}
1772
1773.user-list {
1774 display: flex;
1775 flex-direction: column;
1776 gap: 10px;
1777}
1778
1779.user-row {
1780 display: flex;
1781 align-items: center;
1782 justify-content: space-between;
1783 gap: 12px;
1784 padding: 12px 14px;
1785 border: 1px solid var(--border);
1786}
1787
1788.user-row-main {
1789 min-width: 0;
1790}
1791
1792.user-row-name {
1793 display: flex;
1794 align-items: center;
1795 gap: 6px;
1796 font-weight: 600;
1797 font-size: 14px;
1798 flex-wrap: wrap;
1799}
1800
1801.ic-admin {
1802 width: 16px;
1803 height: 16px;
1804 flex: none;
1805}
1806
1807.user-row-actions {
1808 display: flex;
1809 gap: 6px;
1810 flex-shrink: 0;
1811}
1812
1813.badge-off {
1814 background: color-mix(in srgb, var(--danger) 15%, var(--panel));
1815 border-color: var(--danger);
1816 color: var(--danger);
1817}
1818
1819.setting-row {
1820 display: flex;
1821 align-items: center;
1822 gap: 10px;
1823 padding: 14px;
1824 border: 1px solid var(--border);
1825 cursor: pointer;
1826}
1827
1828.setting-row input {
1829 margin-left: auto;
1830}
1831
1832.setting-label {
1833 display: block;
1834 font-weight: 600;
1835 font-size: 14px;
1836}
1837
1838.setting-desc {
1839 display: block;
1840 font-size: 12px;
1841 color: var(--muted);
1842 margin-top: 2px;
1843}
1844
1845/* Setting row whose control is a select, pushed to the right. */
1846.setting-row-select {
1847 cursor: default;
1848}
1849
1850.setting-row-select > span {
1851 flex: 1;
1852}
1853
1854.setting-row-select select {
1855 appearance: none;
1856 margin-left: auto;
1857 padding: 10px 42px 10px 12px;
1858 border: 1px solid var(--border);
1859 background-color: var(--panel);
1860 background-image: var(--select-arrow);
1861 background-repeat: no-repeat;
1862 background-position: right 12px center;
1863 background-size: 14px 14px;
1864 color: var(--text);
1865 font: inherit;
1866 max-width: 190px;
1867}
1868
1869/* User create/edit form */
1870
1871.user-form {
1872 width: 520px;
1873}
1874
1875.field-input {
1876 width: 100%;
1877 box-sizing: border-box;
1878}
1879
1880/* Search exclusions: a bordered block matching the setting row above it. */
1881.excludes-field {
1882 display: flex;
1883 flex-direction: column;
1884 gap: 10px;
1885 padding: 14px;
1886 border: 1px solid var(--border);
1887}
1888
1889.excludes-list {
1890 display: flex;
1891 flex-direction: column;
1892 gap: 2px;
1893}
1894
1895.exclude-row {
1896 display: flex;
1897 align-items: center;
1898 gap: 8px;
1899 padding: 4px 0;
1900}
1901
1902.exclude-path {
1903 flex: 1;
1904 font-size: 13px;
1905 overflow: hidden;
1906 text-overflow: ellipsis;
1907 white-space: nowrap;
1908}
1909
1910.exclude-remove {
1911 flex: none;
1912 padding: 4px 6px;
1913}
1914
1915.exclude-add {
1916 align-self: flex-start;
1917}
1918
1919.excludes-empty {
1920 font-size: 13px;
1921 margin: 0;
1922}
1923
1924.field-hint {
1925 font-size: 12px;
1926 margin: -2px 0 8px;
1927}
1928
1929.check-row {
1930 display: flex;
1931 align-items: center;
1932 gap: 8px;
1933 font-size: 14px;
1934 margin-bottom: 12px;
1935 cursor: pointer;
1936}
1937
1938.form-error {
1939 color: var(--danger);
1940 font-size: 13px;
1941 margin: 4px 0 8px;
1942}
1943
1944.roots-editor {
1945 display: flex;
1946 flex-direction: column;
1947 gap: 6px;
1948}
1949
1950.root-draft {
1951 display: flex;
1952 align-items: center;
1953 gap: 8px;
1954}
1955
1956.root-draft-path {
1957 flex: 1;
1958 font-size: 13px;
1959 overflow: hidden;
1960 text-overflow: ellipsis;
1961 white-space: nowrap;
1962}
1963
1964.root-draft-mode {
1965 width: auto;
1966 padding: 6px 8px;
1967}
1968
1969.root-add {
1970 align-self: flex-start;
1971 margin-top: 4px;
1972}
1973
1974/* ------------------------------------------------------------------ */
1975/* Empty state + keyboard focus (milestone 8) */
1976/* ------------------------------------------------------------------ */
1977
1978.empty-state {
1979 display: flex;
1980 flex-direction: column;
1981 align-items: center;
1982 justify-content: center;
1983 gap: 8px;
1984 padding: 64px 20px;
1985 text-align: center;
1986}
1987
1988.empty-glyph {
1989 width: 52px;
1990 height: 52px;
1991}
1992
1993.empty-state h3 {
1994 margin: 0;
1995 font-size: 16px;
1996}
1997
1998/* Visible keyboard focus on selectable file/folder entries. The ring is
1999 inset (negative offset) so the scroll container never clips it. */
2000.tile:focus-visible,
2001.row:focus-visible {
2002 outline: 2px solid var(--accent);
2003 outline-offset: -2px;
2004}
2005
2006/* ------------------------------------------------------------------ */
2007/* Search view (#/search) */
2008/* ------------------------------------------------------------------ */
2009
2010.search-view {
2011 display: flex;
2012 flex-direction: column;
2013 gap: 14px;
2014}
2015
2016/* Query bar ------------------------------------------------------ */
2017
2018.query-bar {
2019 display: flex;
2020 align-items: stretch;
2021 gap: 8px;
2022}
2023
2024.query-input {
2025 flex: 1;
2026 display: flex;
2027 align-items: center;
2028 gap: 8px;
2029 background: var(--panel);
2030 border: 1px solid var(--border);
2031 padding: 0 10px;
2032 min-width: 0;
2033}
2034
2035.query-input .qi-icon {
2036 width: 18px;
2037 height: 18px;
2038 color: var(--muted);
2039 flex: none;
2040}
2041
2042.query-input input {
2043 flex: 1;
2044 min-width: 0;
2045 border: none;
2046 outline: none;
2047 background: transparent;
2048 color: var(--text);
2049 font: inherit;
2050 font-size: 14px;
2051 height: 38px;
2052 padding: 0;
2053}
2054
2055.query-input input::placeholder {
2056 color: var(--muted);
2057}
2058
2059/* Segmented Name / Content / Both control */
2060
2061.seg {
2062 display: flex;
2063 border: 1px solid var(--border);
2064 background: var(--panel);
2065}
2066
2067.seg button {
2068 appearance: none;
2069 border: none;
2070 border-right: 1px solid var(--border);
2071 background: transparent;
2072 color: var(--muted);
2073 font: inherit;
2074 font-size: 13px;
2075 padding: 0 14px;
2076 cursor: pointer;
2077}
2078
2079.seg button:last-child {
2080 border-right: none;
2081}
2082
2083.seg button:hover {
2084 background: var(--bg);
2085}
2086
2087.seg button.on {
2088 background: var(--accent);
2089 color: #fff;
2090}
2091
2092/* Start folder --------------------------------------------------- */
2093
2094/* The folder a search starts in; opens the folder picker. */
2095.folder-pick {
2096 flex: none;
2097 display: flex;
2098 align-items: center;
2099 gap: 8px;
2100 max-width: 260px;
2101 border: 1px solid var(--border);
2102 background: var(--panel);
2103 color: var(--text);
2104 font: inherit;
2105 font-size: 13px;
2106 padding: 0 10px;
2107 cursor: pointer;
2108}
2109
2110.folder-pick:hover {
2111 border-color: var(--muted);
2112}
2113
2114.folder-pick .fp-icon {
2115 width: 16px;
2116 height: 16px;
2117 color: var(--muted);
2118 flex: none;
2119}
2120
2121.folder-pick .fp-path {
2122 overflow: hidden;
2123 text-overflow: ellipsis;
2124 white-space: nowrap;
2125}
2126
2127/* Run / stop ------------------------------------------------------- */
2128
2129.btn-run {
2130 border: 1px solid var(--accent-border);
2131 background: var(--accent);
2132 color: #fff;
2133 font: inherit;
2134 font-size: 13px;
2135 font-weight: 600;
2136 padding: 0 18px;
2137 cursor: pointer;
2138}
2139
2140.btn-run:hover {
2141 background: var(--accent-hover);
2142}
2143
2144.btn-stop {
2145 display: flex;
2146 align-items: center;
2147 gap: 6px;
2148 border: 1px solid var(--danger-border);
2149 background: var(--danger);
2150 color: #fff;
2151 font: inherit;
2152 font-size: 13px;
2153 font-weight: 600;
2154 padding: 0 14px;
2155 cursor: pointer;
2156}
2157
2158.btn-stop:hover {
2159 opacity: 0.9;
2160}
2161
2162.btn-stop .ic-stop {
2163 width: 14px;
2164 height: 14px;
2165}
2166
2167/* Status line ------------------------------------------------------ */
2168
2169.status-line {
2170 display: flex;
2171 align-items: center;
2172 gap: 8px;
2173 font-size: 13px;
2174 color: var(--muted);
2175}
2176
2177.status-line .dot {
2178 width: 8px;
2179 height: 8px;
2180 background: var(--accent);
2181 animation: search-pulse 1s ease-in-out infinite;
2182}
2183
2184@keyframes search-pulse {
2185 0%,
2186 100% {
2187 opacity: 1;
2188 }
2189 50% {
2190 opacity: 0.3;
2191 }
2192}
2193
2194.status-line .skip {
2195 opacity: 0.75;
2196}
2197
2198/* Group labels ----------------------------------------------------- */
2199
2200.group-label {
2201 display: flex;
2202 align-items: center;
2203 gap: 8px;
2204 font-size: 12px;
2205 font-weight: 600;
2206 letter-spacing: 0.04em;
2207 text-transform: uppercase;
2208 color: var(--muted);
2209}
2210
2211/* Result group: a `display:flex` (or `none`, from the inline style) flex
2212 item of `.search-view` that re-establishes the column layout for its
2213 label/list/more-row children. */
2214.result-group {
2215 flex-direction: column;
2216 gap: 14px;
2217}
2218
2219.group-label .gl-icon {
2220 width: 15px;
2221 height: 15px;
2222}
2223
2224.group-label .n {
2225 font-weight: 400;
2226 text-transform: none;
2227 letter-spacing: 0;
2228}
2229
2230.group-label .grow {
2231 flex: 1;
2232}
2233
2234.collapse-all {
2235 appearance: none;
2236 border: 1px solid var(--border);
2237 background: transparent;
2238 color: var(--muted);
2239 font: inherit;
2240 font-size: 12px;
2241 text-transform: none;
2242 letter-spacing: 0;
2243 padding: 3px 10px;
2244 cursor: pointer;
2245}
2246
2247.collapse-all:hover {
2248 background: var(--bg);
2249 color: var(--text);
2250}
2251
2252/* Name-result rows --------------------------------------------------- */
2253
2254.srow {
2255 display: grid;
2256 /* icon | name+dir | size | goto */
2257 grid-template-columns: 28px minmax(0, 1fr) 90px 30px;
2258 gap: 12px;
2259 align-items: center;
2260 padding: 9px 14px;
2261 border-bottom: 1px solid var(--border);
2262 cursor: pointer;
2263 font-size: 14px;
2264 user-select: none;
2265}
2266
2267.srow:last-child {
2268 border-bottom: none;
2269}
2270
2271.srow:hover {
2272 background: var(--bg);
2273}
2274
2275.srow-icon {
2276 width: 20px;
2277 height: 20px;
2278}
2279
2280.srow-main {
2281 min-width: 0;
2282 display: flex;
2283 flex-direction: column;
2284 line-height: 1.3;
2285}
2286
2287.srow-name {
2288 overflow: hidden;
2289 text-overflow: ellipsis;
2290 white-space: nowrap;
2291}
2292
2293/* Highlighted query hits (plain fragments render as bare spans). */
2294mark.hl-hit {
2295 background: color-mix(in srgb, var(--accent) 25%, transparent);
2296 color: inherit;
2297}
2298
2299/* The directory-path line (the name line lives inside .srow-name). */
2300.srow-dir {
2301 font-size: 12px;
2302 color: var(--muted);
2303 overflow: hidden;
2304 text-overflow: ellipsis;
2305 white-space: nowrap;
2306}
2307
2308.srow-dir mark.hl-hit {
2309 color: inherit;
2310}
2311
2312.ssize {
2313 font-size: 13px;
2314 color: var(--muted);
2315 text-align: right;
2316}
2317
2318/* Paging footer: position in the results on the left, the action on the
2319 right. Separated from the list by its own border so the button never reads
2320 as another result row. */
2321.page-foot {
2322 display: flex;
2323 flex-wrap: wrap;
2324 align-items: center;
2325 justify-content: space-between;
2326 gap: 12px;
2327 padding: 10px 14px;
2328 border-top: 1px solid var(--border);
2329 font-size: 12px;
2330 color: var(--muted);
2331}
2332
2333.page-foot .pf-count {
2334 font-variant-numeric: tabular-nums;
2335}
2336
2337.page-foot .pf-more,
2338.pf-btn {
2339 appearance: none;
2340 padding: 5px 12px;
2341 border: 1px solid var(--border);
2342 background: var(--panel);
2343 font: inherit;
2344 font-weight: 500;
2345 color: var(--text);
2346 cursor: pointer;
2347}
2348
2349.page-foot .pf-more:hover,
2350.pf-btn:hover:not(:disabled) {
2351 border-color: var(--accent);
2352 color: var(--accent);
2353}
2354
2355.pf-pages {
2356 display: flex;
2357 align-items: center;
2358 gap: 4px;
2359 margin-right: auto;
2360}
2361
2362.pf-btn {
2363 min-width: 30px;
2364 padding: 5px 8px;
2365 font-variant-numeric: tabular-nums;
2366}
2367
2368.pf-btn.current {
2369 border-color: var(--accent);
2370 background: var(--accent);
2371 color: #fff;
2372}
2373
2374.pf-btn:disabled {
2375 opacity: 0.4;
2376 cursor: default;
2377}
2378
2379.pf-goto {
2380 display: flex;
2381 align-items: center;
2382 gap: 6px;
2383 margin: 0;
2384}
2385
2386.pf-goto input {
2387 width: 72px;
2388 padding: 5px 8px;
2389}
2390
2391/* An arrow-key page turn is loading. Fixed at the top, so it also shows over
2392 the preview. The delay keeps fast turns from flashing. */
2393.page-busy {
2394 position: fixed;
2395 top: 0;
2396 left: 0;
2397 right: 0;
2398 height: 3px;
2399 overflow: hidden;
2400 z-index: 250;
2401 pointer-events: none;
2402 animation: page-busy-in 0s 200ms both;
2403}
2404
2405.page-busy::after {
2406 content: "";
2407 display: block;
2408 width: 30%;
2409 height: 100%;
2410 background: var(--accent);
2411 animation: upload-indeterminate 1.2s ease-in-out infinite;
2412}
2413
2414body:has(.page-busy) {
2415 cursor: progress;
2416}
2417
2418@keyframes page-busy-in {
2419 from {
2420 opacity: 0;
2421 }
2422 to {
2423 opacity: 1;
2424 }
2425}
2426
2427/* Content matches ----------------------------------------------------- */
2428
2429.match-card {
2430 background: var(--panel);
2431 border: 1px solid var(--border);
2432 border-top: none;
2433}
2434
2435/* The first card is the one right after the group label; `:first-of-type`
2436 cannot be used because the label is an earlier `div` sibling. */
2437.group-label + .match-card {
2438 border-top: 1px solid var(--border);
2439}
2440
2441/* The header opens the file; the matched lines below are text to read. */
2442.match-head {
2443 display: flex;
2444 align-items: center;
2445 gap: 10px;
2446 padding: 9px 14px;
2447 font-size: 14px;
2448 cursor: pointer;
2449}
2450
2451.match-head:hover {
2452 background: var(--bg);
2453}
2454
2455.match-head .mi {
2456 width: 18px;
2457 height: 18px;
2458 color: var(--muted);
2459 flex: none;
2460}
2461
2462/* Takes the slack, so the count and the two buttons sit at the right edge. */
2463.mpath {
2464 flex: 1;
2465 min-width: 0;
2466 overflow: hidden;
2467 text-overflow: ellipsis;
2468 white-space: nowrap;
2469}
2470
2471.mpath b {
2472 font-weight: 600;
2473}
2474
2475.mcount {
2476 flex: none;
2477 font-size: 12px;
2478 color: var(--muted);
2479}
2480
2481/* "Show in folder", in both the name rows and the match-card headers. */
2482.goto-btn {
2483 appearance: none;
2484 flex: none;
2485 justify-self: end;
2486 display: flex;
2487 align-items: center;
2488 justify-content: center;
2489 width: 26px;
2490 height: 26px;
2491 padding: 0;
2492 border: 1px solid transparent;
2493 background: transparent;
2494 cursor: pointer;
2495}
2496
2497.goto-btn:hover {
2498 border-color: var(--border);
2499}
2500
2501.goto-btn .goto-ic {
2502 width: 16px;
2503 height: 16px;
2504 color: var(--muted);
2505}
2506
2507.goto-btn:hover .goto-ic {
2508 color: var(--accent);
2509}
2510
2511.chev {
2512 appearance: none;
2513 border: 1px solid var(--border);
2514 background: transparent;
2515 width: 26px;
2516 height: 26px;
2517 display: flex;
2518 align-items: center;
2519 justify-content: center;
2520 cursor: pointer;
2521 flex: none;
2522}
2523
2524.chev:hover {
2525 background: var(--bg);
2526}
2527
2528.chev .chev-down,
2529.chev .chev-up {
2530 display: block;
2531 width: 10px;
2532 height: 10px;
2533 border-right: 2px solid var(--muted);
2534 border-bottom: 2px solid var(--muted);
2535}
2536
2537.chev .chev-down {
2538 transform: rotate(45deg) translate(-1px, -1px);
2539}
2540
2541.chev .chev-up {
2542 transform: rotate(-135deg) translate(-1px, -1px);
2543}
2544
2545.match-lines {
2546 border-top: 1px solid var(--border);
2547 background: var(--bg);
2548 font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
2549 font-size: 12.5px;
2550}
2551
2552/* Matched lines are text to read, not a control: the card header is what
2553 opens the file, so no pointer cursor and no hover here. */
2554.mline {
2555 display: grid;
2556 grid-template-columns: 52px minmax(0, 1fr);
2557 gap: 12px;
2558 padding: 5px 14px;
2559 border-bottom: 1px solid var(--border);
2560 white-space: pre;
2561 user-select: text;
2562}
2563
2564.mline:last-child {
2565 border-bottom: none;
2566}
2567
2568.mline .ln {
2569 color: var(--muted);
2570 text-align: right;
2571 user-select: none;
2572}
2573
2574.mline .tx {
2575 overflow: hidden;
2576 text-overflow: ellipsis;
2577}
2578
2579.search-empty {
2580 padding: 48px 20px;
2581 text-align: center;
2582 color: var(--muted);
2583 font-size: 14px;
2584}
2585
2586/* ------------------------------------------------------------------ */
2587/* Uploads: overlay panel + log view */
2588/* ------------------------------------------------------------------ */
2589
2590.upload-panel {
2591 position: fixed;
2592 right: 20px;
2593 bottom: 20px;
2594 z-index: 190;
2595 width: min(380px, calc(100vw - 40px));
2596 background: var(--panel);
2597 color: var(--text);
2598 border: 1px solid var(--border);
2599 box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
2600 font-size: 13px;
2601}
2602
2603/* The toast sits above the panel when both show. */
2604.upload-panel ~ .toast,
2605.toast:has(~ .upload-panel) {
2606 bottom: 80px;
2607}
2608
2609.upload-panel-head {
2610 display: flex;
2611 align-items: center;
2612 gap: 8px;
2613 padding: 8px 10px;
2614 border-bottom: 1px solid var(--border);
2615}
2616
2617.upload-panel.collapsed .upload-panel-head {
2618 border-bottom: none;
2619}
2620
2621.upload-panel-title {
2622 font-weight: 600;
2623}
2624
2625.upload-panel-summary {
2626 margin-left: auto;
2627 font-size: 12px;
2628}
2629
2630.upload-panel-list {
2631 max-height: 50vh;
2632 overflow-y: auto;
2633}
2634
2635.upload-job {
2636 padding: 8px 10px;
2637 border-bottom: 1px solid var(--border);
2638}
2639
2640.upload-job:last-child {
2641 border-bottom: none;
2642}
2643
2644.upload-job-head {
2645 display: flex;
2646 align-items: center;
2647 gap: 4px;
2648}
2649
2650.upload-job-label {
2651 flex: 1;
2652 min-width: 0;
2653 overflow: hidden;
2654 text-overflow: ellipsis;
2655 white-space: nowrap;
2656 font-weight: 500;
2657}
2658
2659.upload-job-pct {
2660 font-variant-numeric: tabular-nums;
2661 margin-right: 4px;
2662}
2663
2664.upload-bar {
2665 height: 4px;
2666 margin: 6px 0 4px;
2667 /* Clip the indeterminate block: it slides past both ends. */
2668 overflow: hidden;
2669 background: color-mix(in srgb, var(--accent) 15%, var(--panel));
2670}
2671
2672.upload-bar-fill {
2673 height: 100%;
2674 background: var(--accent);
2675 transition: width 0.2s linear;
2676}
2677
2678/* Preparing: nothing to measure yet, so a sliding block instead of a fill. */
2679.upload-bar.indeterminate .upload-bar-fill {
2680 width: 30% !important;
2681 animation: upload-indeterminate 1.2s ease-in-out infinite;
2682}
2683
2684@keyframes upload-indeterminate {
2685 0% {
2686 transform: translateX(-100%);
2687 }
2688 100% {
2689 transform: translateX(333%);
2690 }
2691}
2692
2693.upload-job.failed .upload-bar-fill {
2694 background: var(--danger);
2695}
2696
2697.upload-job.done .upload-bar-fill {
2698 background: var(--muted);
2699}
2700
2701.upload-job-state {
2702 font-size: 12px;
2703 overflow: hidden;
2704 text-overflow: ellipsis;
2705 white-space: nowrap;
2706}
2707
2708.upload-job.failed .upload-job-state {
2709 color: var(--danger);
2710 white-space: normal;
2711}
2712
2713.upload-conflict {
2714 display: flex;
2715 flex-direction: column;
2716 gap: 6px;
2717 margin-top: 8px;
2718 padding: 8px;
2719 border: 1px solid var(--danger-border);
2720 background: color-mix(in srgb, var(--danger) 8%, var(--panel));
2721}
2722
2723.upload-conflict-all {
2724 display: flex;
2725 /* The label wraps on a narrow dialog; keep the box on the first line. */
2726 align-items: flex-start;
2727 gap: 0.5rem;
2728 font-size: 12px;
2729 cursor: pointer;
2730}
2731
2732.upload-conflict-all input {
2733 flex: none;
2734 margin-top: 2px;
2735}
2736
2737.modal .upload-conflict-all {
2738 margin: 8px 0 0;
2739 font-size: 13px;
2740}
2741
2742.upload-conflict-actions {
2743 display: flex;
2744 justify-content: flex-end;
2745 gap: 6px;
2746}
2747
2748.upload-graph {
2749 margin-top: 8px;
2750 color: var(--accent);
2751 border: 1px solid var(--border);
2752 background: color-mix(in srgb, var(--accent) 6%, var(--panel));
2753}
2754
2755/* Peak label above the plot, so the line can never run through it. */
2756.upload-graph-max {
2757 display: block;
2758 text-align: right;
2759 padding: 2px 6px 0;
2760 font-size: 11px;
2761 line-height: 14px;
2762}
2763
2764.upload-graph svg {
2765 display: block;
2766 width: 100%;
2767 height: 46px;
2768}
2769
2770.upload-errors {
2771 margin: 6px 0 0;
2772 padding-left: 18px;
2773 font-size: 12px;
2774 color: var(--danger);
2775}
2776
2777.upload-err-file {
2778 font-weight: 500;
2779}
2780
2781/* Log view: the same rows, full width, roomier. */
2782.uploads-view .upload-log {
2783 border: 1px solid var(--border);
2784 background: var(--panel);
2785 font-size: 14px;
2786}
2787
2788.uploads-view .upload-job {
2789 padding: 12px 14px;
2790}
2791
2792.uploads-view .upload-graph svg {
2793 height: 76px;
2794}
2795
2796/* Security settings tab: password, passkeys, sign-in requirement. */
2797
2798.security-tab {
2799 display: flex;
2800 flex-direction: column;
2801 gap: 10px;
2802}
2803
2804.security-tab h3 {
2805 margin: 10px 0 0;
2806}
2807
2808/* Matches .excludes-field, so the three blocks of this tab read as one list. */
2809.security-form {
2810 display: flex;
2811 flex-direction: column;
2812 gap: 10px;
2813 padding: 14px;
2814 border: 1px solid var(--border);
2815}
2816
2817.security-form .field input {
2818 width: 100%;
2819 box-sizing: border-box;
2820}
2821
2822.passkey-list {
2823 display: flex;
2824 flex-direction: column;
2825 gap: 2px;
2826 padding: 0 14px;
2827}
2828
2829.passkey-row {
2830 display: flex;
2831 align-items: center;
2832 gap: 8px;
2833 padding: 6px 0;
2834}
2835
2836.passkey-main {
2837 flex: 1;
2838 overflow: hidden;
2839}
2840
2841.passkey-name {
2842 display: block;
2843 font-size: 13px;
2844 overflow: hidden;
2845 text-overflow: ellipsis;
2846 white-space: nowrap;
2847}
2848
2849/* "Needs your user name": a note, not an error. This passkey works, it just
2850 cannot be found without the name typed in first. */
2851.passkey-warning {
2852 display: inline-block;
2853 margin-top: 2px;
2854 padding: 1px 6px;
2855 border: 1px solid var(--border);
2856 font-size: 11px;
2857 color: var(--muted);
2858}
2859
2860/* The sign-in requirement's labels are whole phrases, so the shared 190px
2861 cap for setting selects would clip them. */
2862.security-tab .setting-row-select select {
2863 max-width: none;
2864 width: auto;
2865}
2866
2867/* Separator between the password form and the passkey button. `.auth-card p`
2868 sets a bottom-only margin on every paragraph in the card, and wins on
2869 specificity, so both of these have to name the card too. */
2870.auth-card .login-or {
2871 margin: 16px 0 8px;
2872 font-size: 12px;
2873 color: var(--muted);
2874 text-transform: lowercase;
2875}
2876
2877.auth-card .setting-desc {
2878 margin-top: 8px;
2879}
2880
2881.auth-card .btn {
2882 width: 100%;
2883}
2884
2885/* ------------------------------------------------------------------ */
2886/* Calendars and contacts */
2887/* ------------------------------------------------------------------ */
2888
2889.pim-view {
2890 display: flex;
2891 gap: 16px;
2892 flex: 1;
2893 min-height: 0;
2894 width: 100%;
2895}
2896
2897.pim-side {
2898 flex: none;
2899 width: 260px;
2900 display: flex;
2901 flex-direction: column;
2902 gap: 8px;
2903 min-width: 0;
2904}
2905
2906.pim-side-head {
2907 display: flex;
2908 align-items: center;
2909 gap: 4px;
2910}
2911
2912.pim-side-head .admin-section-title {
2913 flex: 1;
2914 margin: 0;
2915}
2916
2917.pim-list {
2918 display: flex;
2919 flex-direction: column;
2920 border: 1px solid var(--border);
2921 background: var(--panel);
2922}
2923
2924.pim-group-label {
2925 padding: 10px 10px 4px;
2926 font-size: 12px;
2927 color: var(--muted);
2928 border-top: 1px solid var(--border);
2929}
2930
2931.pim-row {
2932 display: flex;
2933 align-items: center;
2934 min-width: 0;
2935}
2936
2937.pim-row:hover {
2938 background: var(--bg);
2939}
2940
2941.pim-toggle {
2942 flex: none;
2943 display: inline-flex;
2944 align-items: center;
2945 justify-content: center;
2946 width: 36px;
2947 height: 36px;
2948 padding: 0;
2949 border: 0;
2950 background: none;
2951 cursor: pointer;
2952}
2953
2954.pim-swatch {
2955 display: inline-block;
2956 flex: none;
2957 width: 14px;
2958 height: 14px;
2959 background: var(--swatch);
2960 border: 2px solid var(--swatch);
2961}
2962
2963/* A hidden collection keeps its color as an outline only. */
2964.pim-row-hidden .pim-swatch {
2965 background: transparent;
2966}
2967
2968.pim-row-hidden .pim-row-name {
2969 color: var(--muted);
2970}
2971
2972.pim-row-main {
2973 flex: 1;
2974 min-width: 0;
2975 display: flex;
2976 align-items: baseline;
2977 justify-content: flex-start;
2978 gap: 6px;
2979 padding: 8px 10px 8px 0;
2980 border: 0;
2981 background: none;
2982 color: var(--text);
2983 font: inherit;
2984 font-size: 14px;
2985 text-align: left;
2986 cursor: pointer;
2987}
2988
2989.pim-row-name {
2990 min-width: 0;
2991 overflow: hidden;
2992 text-overflow: ellipsis;
2993 white-space: nowrap;
2994}
2995
2996.pim-row-note {
2997 flex: none;
2998 max-width: 45%;
2999 font-size: 12px;
3000 color: var(--muted);
3001 overflow: hidden;
3002 text-overflow: ellipsis;
3003 white-space: nowrap;
3004}
3005
3006.pim-main {
3007 flex: 1;
3008 min-width: 0;
3009 border: 1px solid var(--border);
3010 background: var(--panel);
3011}
3012
3013.pim-modal {
3014 width: 600px;
3015 max-height: calc(100vh - 48px);
3016 overflow-y: auto;
3017}
3018
3019.pim-modal-title {
3020 display: flex;
3021 align-items: center;
3022 gap: 10px;
3023 overflow-wrap: anywhere;
3024}
3025
3026.pim-note {
3027 margin: -6px 0 8px;
3028 font-size: 13px;
3029}
3030
3031.pim-section {
3032 display: block;
3033 padding: 14px 0 0;
3034 margin-top: 14px;
3035 border-top: 1px solid var(--border);
3036}
3037
3038.pim-section-title {
3039 margin: 0 0 8px;
3040 font-size: 14px;
3041}
3042
3043.pim-hint {
3044 margin: 0 0 10px;
3045 font-size: 13px;
3046}
3047
3048.pim-section-actions {
3049 display: flex;
3050 justify-content: flex-end;
3051 flex-wrap: wrap;
3052 gap: 8px;
3053}
3054
3055.pim-section-actions.pim-left {
3056 justify-content: flex-start;
3057}
3058
3059.pim-colors {
3060 display: flex;
3061 flex-wrap: wrap;
3062 align-items: center;
3063 gap: 6px;
3064}
3065
3066.pim-color {
3067 width: 26px;
3068 height: 26px;
3069 padding: 0;
3070 border: 2px solid var(--panel);
3071 outline: 1px solid var(--border);
3072 background: var(--swatch);
3073 cursor: pointer;
3074}
3075
3076.pim-color.selected {
3077 outline: 2px solid var(--text);
3078}
3079
3080.pim-color-input {
3081 width: 34px;
3082 height: 30px;
3083 padding: 0 2px;
3084 cursor: pointer;
3085}
3086
3087.pim-items {
3088 display: flex;
3089 flex-direction: column;
3090 gap: 6px;
3091 margin-bottom: 10px;
3092}
3093
3094.pim-item {
3095 display: flex;
3096 align-items: center;
3097 gap: 8px;
3098 min-width: 0;
3099}
3100
3101.pim-item-name {
3102 flex: 1;
3103 min-width: 0;
3104 font-size: 14px;
3105 overflow: hidden;
3106 text-overflow: ellipsis;
3107 white-space: nowrap;
3108}
3109
3110.pim-mode {
3111 flex: none;
3112 width: auto;
3113 max-width: 55%;
3114 padding: 6px 28px 6px 8px;
3115}
3116
3117.pim-add {
3118 display: flex;
3119 gap: 8px;
3120 align-items: center;
3121}
3122
3123.pim-add .field-input {
3124 flex: 1;
3125 min-width: 0;
3126}
3127
3128.pim-link {
3129 display: flex;
3130 align-items: center;
3131 gap: 10px;
3132 padding: 8px 10px;
3133 border: 1px solid var(--border);
3134 min-width: 0;
3135}
3136
3137.pim-link-main {
3138 flex: 1;
3139 min-width: 0;
3140 display: flex;
3141 flex-direction: column;
3142 gap: 2px;
3143}
3144
3145.pim-link-url {
3146 font-size: 13px;
3147 overflow: hidden;
3148 text-overflow: ellipsis;
3149 white-space: nowrap;
3150}
3151
3152.pim-link-meta {
3153 font-size: 12px;
3154}
3155
3156.pim-link-actions {
3157 display: flex;
3158 flex: none;
3159 gap: 6px;
3160}
3161
3162.pim-link-form {
3163 display: flex;
3164 flex-wrap: wrap;
3165 align-items: flex-end;
3166 gap: 10px;
3167}
3168
3169.pim-inline {
3170 display: flex;
3171 flex-direction: column;
3172 gap: 4px;
3173 font-size: 13px;
3174 color: var(--muted);
3175}
3176
3177.pim-inline input,
3178.pim-inline select {
3179 width: auto;
3180 min-width: 140px;
3181}
3182
3183.pim-hints,
3184.pim-sample {
3185 margin: 0 0 8px;
3186 padding-left: 18px;
3187 font-size: 13px;
3188 line-height: 1.5;
3189}
3190
3191.pim-sample li {
3192 overflow-wrap: anywhere;
3193}
3194
3195.admin-stack {
3196 display: flex;
3197 flex-direction: column;
3198 gap: 28px;
3199}
3200
3201.rooms-hint {
3202 margin-bottom: 12px;
3203}
3204
3205.room-address {
3206 font-size: 12px;
3207 margin-top: 2px;
3208 overflow-wrap: anywhere;
3209}
3210
3211.feeds-header {
3212 margin-top: 28px;
3213}
3214
3215/* Below this the month grid needs the width of the collection list. */
3216@media (max-width: 1180px) {
3217 .pim-view {
3218 flex-direction: column;
3219 }
3220
3221 .pim-side {
3222 width: auto;
3223 }
3224
3225 /* The events and contacts come first; the list of collections would
3226 fill a whole phone screen above them. */
3227 .pim-main {
3228 min-height: 240px;
3229 order: -1;
3230 }
3231}
3232
3233@media (max-width: 640px) {
3234 .pim-link {
3235 flex-direction: column;
3236 align-items: stretch;
3237 }
3238
3239 .pim-link-actions {
3240 flex-wrap: wrap;
3241 }
3242
3243 .pim-add {
3244 flex-wrap: wrap;
3245 }
3246
3247 .pim-add .pim-mode {
3248 max-width: 100%;
3249 }
3250
3251 /* A lent row keeps name, mode and remove button on one line. */
3252 .pim-item .pim-mode {
3253 flex: 0 1 auto;
3254 min-width: 0;
3255 }
3256}
3257
3258/* Mirrored chevron: the icon set has only the right-pointing one. */
3259.flip {
3260 transform: scaleX(-1);
3261}
3262
3263/* Selects in the calendar and contacts views and their dialogs: the same
3264 height and colors as the inputs beside them, in both themes. */
3265.pim-modal select,
3266.pim-view select {
3267 appearance: none;
3268 padding: 10px 34px 10px 12px;
3269 border: 1px solid var(--border);
3270 background-color: var(--bg);
3271 background-image: var(--select-arrow);
3272 background-repeat: no-repeat;
3273 background-position: right 11px center;
3274 background-size: 12px 12px;
3275 color: var(--text);
3276 font: inherit;
3277 font-size: 14px;
3278}
3279
3280.pim-modal select:focus,
3281.pim-view select:focus {
3282 outline: 2px solid var(--accent);
3283 outline-offset: -1px;
3284}
3285
3286.pim-link-form .pim-inline input {
3287 padding: 10px 12px;
3288}
3289
3290.pim-file-input {
3291 display: none;
3292}
3293
3294.pim-main {
3295 display: flex;
3296 flex-direction: column;
3297 min-height: 0;
3298}
3299
3300/* ---- Calendar ---------------------------------------------------- */
3301
3302.cal {
3303 display: flex;
3304 flex-direction: column;
3305 min-width: 0;
3306}
3307
3308.cal-pad {
3309 padding: 12px 14px;
3310}
3311
3312.cal-toolbar {
3313 display: flex;
3314 align-items: center;
3315 flex-wrap: wrap;
3316 gap: 8px 12px;
3317 padding: 10px 12px;
3318 border-bottom: 1px solid var(--border);
3319}
3320
3321.cal-nav {
3322 display: flex;
3323 align-items: center;
3324 gap: 4px;
3325}
3326
3327.cal-title {
3328 flex: 1;
3329 min-width: 0;
3330 margin: 0;
3331 font-size: 17px;
3332 white-space: nowrap;
3333 overflow: hidden;
3334 text-overflow: ellipsis;
3335}
3336
3337.cal-views {
3338 display: flex;
3339}
3340
3341.cal-view-btn {
3342 padding: 6px 12px;
3343 font-size: 13px;
3344 margin-left: -1px;
3345}
3346
3347.cal-view-btn.active {
3348 background: var(--accent);
3349 border-color: var(--accent-border);
3350 color: #fff;
3351 position: relative;
3352}
3353
3354.cal-badge {
3355 min-width: 18px;
3356 padding: 0 5px;
3357 font-size: 11px;
3358 line-height: 18px;
3359 background: var(--danger);
3360 color: #fff;
3361 text-align: center;
3362}
3363
3364.cal-truncated {
3365 margin: 0;
3366 padding: 8px 12px;
3367 font-size: 13px;
3368 color: var(--danger);
3369 border-bottom: 1px solid var(--border);
3370}
3371
3372.cal-head {
3373 display: grid;
3374 grid-template-columns: repeat(7, minmax(0, 1fr));
3375 border-bottom: 1px solid var(--border);
3376}
3377
3378.cal-head-day {
3379 padding: 6px 8px;
3380 font-size: 12px;
3381 color: var(--muted);
3382 overflow: hidden;
3383 text-overflow: ellipsis;
3384 white-space: nowrap;
3385}
3386
3387.cal-week {
3388 display: grid;
3389 grid-template-columns: repeat(7, minmax(0, 1fr));
3390 grid-template-rows: 26px repeat(var(--lines), 22px) 6px;
3391 border-bottom: 1px solid var(--border);
3392}
3393
3394.cal-cell {
3395 grid-row: 1 / -1;
3396 display: flex;
3397 flex-direction: column;
3398 align-items: flex-start;
3399 justify-content: flex-start;
3400 gap: 4px;
3401 min-width: 0;
3402 padding: 4px 6px;
3403 border: 0;
3404 border-right: 1px solid var(--border);
3405 background: none;
3406 color: var(--text);
3407 font: inherit;
3408 cursor: pointer;
3409}
3410
3411.cal-cell:nth-child(7) {
3412 border-right: 0;
3413}
3414
3415.cal-cell:hover {
3416 background: var(--bg);
3417}
3418
3419.cal-cell.cal-out .cal-num {
3420 color: var(--muted);
3421}
3422
3423.cal-cell.cal-sel {
3424 background: var(--bg);
3425 box-shadow: inset 0 0 0 2px var(--accent);
3426}
3427
3428.cal-num {
3429 font-size: 13px;
3430 line-height: 18px;
3431 min-width: 20px;
3432 text-align: center;
3433}
3434
3435.cal-today .cal-num {
3436 background: var(--accent);
3437 color: #fff;
3438 font-weight: 600;
3439}
3440
3441/* Phone only: the colors of a day's events instead of their titles. */
3442.cal-dots {
3443 display: none;
3444 gap: 3px;
3445}
3446
3447.cal-dot {
3448 display: inline-block;
3449 flex: none;
3450 width: 8px;
3451 height: 8px;
3452 background: var(--c, var(--accent));
3453}
3454
3455.cal-item {
3456 position: relative;
3457 z-index: 1;
3458 display: flex;
3459 align-items: center;
3460 justify-content: flex-start;
3461 gap: 4px;
3462 min-width: 0;
3463 margin: 1px 2px;
3464 padding: 0 6px;
3465 border: 0;
3466 font-size: 12px;
3467 line-height: 20px;
3468 text-align: left;
3469 cursor: pointer;
3470 overflow: hidden;
3471}
3472
3473.cal-bar {
3474 background: var(--c);
3475 color: #fff;
3476 text-shadow: 0 0 2px rgb(0 0 0 / 40%);
3477}
3478
3479.cal-bar.cal-cont-l {
3480 margin-left: 0;
3481}
3482
3483.cal-bar.cal-cont-r {
3484 margin-right: 0;
3485}
3486
3487.cal-chip {
3488 background: none;
3489 color: var(--text);
3490}
3491
3492.cal-chip:hover {
3493 background: var(--bg);
3494}
3495
3496.cal-time {
3497 flex: none;
3498 color: var(--muted);
3499 font-variant-numeric: tabular-nums;
3500}
3501
3502.cal-item-title {
3503 min-width: 0;
3504 overflow: hidden;
3505 text-overflow: ellipsis;
3506 white-space: nowrap;
3507}
3508
3509.cal-cancelled .cal-item-title,
3510.cal-cancelled .cal-row-title,
3511.cal-detail-title.cal-cancelled {
3512 text-decoration: line-through;
3513}
3514
3515.cal-more {
3516 position: relative;
3517 z-index: 1;
3518 justify-content: flex-start;
3519 margin: 1px 2px;
3520 padding: 0 6px;
3521 border: 0;
3522 background: none;
3523 color: var(--muted);
3524 font-size: 12px;
3525 line-height: 20px;
3526 white-space: nowrap;
3527 overflow: hidden;
3528 min-width: 0;
3529}
3530
3531.cal-more:hover {
3532 color: var(--text);
3533 text-decoration: underline;
3534}
3535
3536.cal-day {
3537 border-top: 1px solid var(--border);
3538}
3539
3540.cal-day-title {
3541 margin: 0;
3542 padding: 12px 14px 6px;
3543 font-size: 14px;
3544}
3545
3546.cal-day-today {
3547 color: var(--accent);
3548}
3549
3550.cal-rows {
3551 display: flex;
3552 flex-direction: column;
3553 padding-bottom: 8px;
3554}
3555
3556.cal-row {
3557 display: flex;
3558 align-items: baseline;
3559 justify-content: flex-start;
3560 gap: 10px;
3561 padding: 7px 14px;
3562 border: 0;
3563 background: none;
3564 color: var(--text);
3565 font: inherit;
3566 text-align: left;
3567 cursor: pointer;
3568 min-width: 0;
3569}
3570
3571.cal-row:hover {
3572 background: var(--bg);
3573}
3574
3575.cal-row .cal-dot {
3576 align-self: center;
3577}
3578
3579.cal-row-time {
3580 flex: none;
3581 width: 120px;
3582 font-size: 13px;
3583 color: var(--muted);
3584 font-variant-numeric: tabular-nums;
3585}
3586
3587.cal-row-main {
3588 display: flex;
3589 flex-direction: column;
3590 gap: 2px;
3591 min-width: 0;
3592}
3593
3594.cal-row-title {
3595 font-size: 14px;
3596 overflow-wrap: anywhere;
3597}
3598
3599.cal-row-meta {
3600 font-size: 12.5px;
3601 color: var(--muted);
3602 overflow-wrap: anywhere;
3603}
3604
3605.cal-agenda-day + .cal-agenda-day {
3606 border-top: 1px solid var(--border);
3607}
3608
3609.cal-inv {
3610 display: flex;
3611 align-items: center;
3612 gap: 12px;
3613 padding: 12px 14px;
3614 border-bottom: 1px solid var(--border);
3615}
3616
3617.cal-inv-main {
3618 flex: 1;
3619 min-width: 0;
3620 display: flex;
3621 flex-direction: column;
3622 align-items: flex-start;
3623 gap: 3px;
3624 padding: 0;
3625 border: 0;
3626 background: none;
3627 color: var(--text);
3628 font: inherit;
3629 text-align: left;
3630 cursor: pointer;
3631}
3632
3633.cal-inv-title {
3634 font-weight: 600;
3635 overflow-wrap: anywhere;
3636}
3637
3638.cal-inv-actions {
3639 display: flex;
3640 flex-wrap: wrap;
3641 gap: 6px;
3642}
3643
3644.cal-detail-title {
3645 overflow-wrap: anywhere;
3646}
3647
3648.cal-flag {
3649 display: inline-block;
3650 margin: 0 0 12px;
3651 padding: 2px 8px;
3652 font-size: 12px;
3653 border: 1px solid var(--danger);
3654 color: var(--danger);
3655}
3656
3657.cal-facts {
3658 display: grid;
3659 grid-template-columns: max-content minmax(0, 1fr);
3660 gap: 8px 16px;
3661 margin: 0 0 16px;
3662 font-size: 14px;
3663}
3664
3665.cal-facts dt {
3666 color: var(--muted);
3667}
3668
3669.cal-facts dd {
3670 margin: 0;
3671 overflow-wrap: anywhere;
3672}
3673
3674.cal-pre {
3675 white-space: pre-wrap;
3676}
3677
3678.cal-desc {
3679 margin-bottom: 16px;
3680 padding: 10px 12px;
3681 border: 1px solid var(--border);
3682 background: var(--bg);
3683 font-size: 14px;
3684 overflow-wrap: anywhere;
3685 max-height: 40vh;
3686 overflow-y: auto;
3687}
3688
3689.cal-attendees {
3690 list-style: none;
3691 margin: 0 0 16px;
3692 padding: 0;
3693 display: flex;
3694 flex-direction: column;
3695 gap: 6px;
3696 font-size: 14px;
3697}
3698
3699.cal-attendees li {
3700 display: flex;
3701 align-items: center;
3702 gap: 8px;
3703 min-width: 0;
3704}
3705
3706.cal-att-name {
3707 min-width: 0;
3708 overflow: hidden;
3709 text-overflow: ellipsis;
3710 white-space: nowrap;
3711}
3712
3713.ps-badge {
3714 margin-left: auto;
3715 flex: none;
3716 padding: 1px 7px;
3717 font-size: 11.5px;
3718 border: 1px solid var(--border);
3719 color: var(--muted);
3720}
3721
3722.ps-accepted {
3723 border-color: #16a34a;
3724 color: #16a34a;
3725}
3726
3727.ps-declined {
3728 border-color: var(--danger);
3729 color: var(--danger);
3730}
3731
3732.ps-tentative {
3733 border-color: #d97706;
3734 color: #d97706;
3735}
3736
3737.cal-reply {
3738 border-top: 1px solid var(--border);
3739 padding-top: 12px;
3740}
3741
3742.cal-scope {
3743 display: flex;
3744 flex-wrap: wrap;
3745 gap: 4px 16px;
3746}
3747
3748.cal-scope input[type="radio"] {
3749 width: auto;
3750 margin: 0;
3751 padding: 0;
3752 accent-color: var(--accent);
3753}
3754
3755/* ---- Contacts ---------------------------------------------------- */
3756
3757.ct {
3758 display: flex;
3759 min-height: 0;
3760 flex: 1;
3761}
3762
3763.ct-list {
3764 flex: none;
3765 width: 320px;
3766 display: flex;
3767 flex-direction: column;
3768 border-right: 1px solid var(--border);
3769 min-height: 0;
3770}
3771
3772.ct-search {
3773 position: relative;
3774 padding: 10px;
3775 border-bottom: 1px solid var(--border);
3776}
3777
3778.ct-search input {
3779 padding-left: 34px;
3780}
3781
3782.ct-search-icon {
3783 position: absolute;
3784 left: 20px;
3785 top: 50%;
3786 width: 16px;
3787 height: 16px;
3788 transform: translateY(-50%);
3789 color: var(--muted);
3790 pointer-events: none;
3791}
3792
3793.ct-rows {
3794 display: flex;
3795 flex-direction: column;
3796 overflow-y: auto;
3797 max-height: calc(100vh - var(--topbar-h) - 120px);
3798}
3799
3800.ct-row {
3801 display: flex;
3802 align-items: center;
3803 justify-content: flex-start;
3804 gap: 10px;
3805 padding: 8px 12px;
3806 border: 0;
3807 border-bottom: 1px solid var(--border);
3808 background: none;
3809 color: var(--text);
3810 font: inherit;
3811 text-align: left;
3812 cursor: pointer;
3813 min-width: 0;
3814}
3815
3816.ct-row:hover {
3817 background: var(--bg);
3818}
3819
3820.ct-row.ct-sel {
3821 background: var(--bg);
3822 box-shadow: inset 3px 0 0 var(--accent);
3823}
3824
3825.ct-avatar {
3826 flex: none;
3827 width: 36px;
3828 height: 36px;
3829 object-fit: cover;
3830 background: var(--bg);
3831}
3832
3833.ct-initials {
3834 position: relative;
3835 overflow: hidden;
3836 display: inline-flex;
3837 align-items: center;
3838 justify-content: center;
3839 border: 1px solid var(--border);
3840 font-size: 13px;
3841 font-weight: 600;
3842 color: var(--muted);
3843}
3844
3845.ct-photo {
3846 position: absolute;
3847 inset: 0;
3848 width: 100%;
3849 height: 100%;
3850 object-fit: cover;
3851 background: var(--bg);
3852}
3853
3854.ct-avatar-lg {
3855 width: 72px;
3856 height: 72px;
3857 font-size: 22px;
3858}
3859
3860.ct-row-main {
3861 display: flex;
3862 flex-direction: column;
3863 gap: 2px;
3864 min-width: 0;
3865}
3866
3867.ct-name {
3868 display: flex;
3869 align-items: center;
3870 gap: 6px;
3871 font-size: 14px;
3872 min-width: 0;
3873}
3874
3875.ct-name-text {
3876 min-width: 0;
3877 overflow: hidden;
3878 text-overflow: ellipsis;
3879 white-space: nowrap;
3880}
3881
3882.ct-meta {
3883 font-size: 12.5px;
3884 color: var(--muted);
3885 overflow: hidden;
3886 text-overflow: ellipsis;
3887 white-space: nowrap;
3888}
3889
3890.ct-detail {
3891 flex: 1;
3892 min-width: 0;
3893 padding: 16px 20px;
3894 overflow-y: auto;
3895}
3896
3897.ct-back {
3898 display: none;
3899 margin-bottom: 12px;
3900}
3901
3902.ct-head {
3903 display: flex;
3904 align-items: center;
3905 gap: 16px;
3906 margin-bottom: 18px;
3907 min-width: 0;
3908}
3909
3910.ct-head-text {
3911 min-width: 0;
3912}
3913
3914.ct-head-text p {
3915 margin: 4px 0 0;
3916 overflow-wrap: anywhere;
3917}
3918
3919.ct-title {
3920 margin: 0;
3921 font-size: 20px;
3922 overflow-wrap: anywhere;
3923}
3924
3925.ct-value {
3926 display: flex;
3927 align-items: baseline;
3928 flex-wrap: wrap;
3929 gap: 0 8px;
3930}
3931
3932.ct-value + .ct-value {
3933 margin-top: 4px;
3934}
3935
3936.ct-label {
3937 font-size: 12px;
3938}
3939
3940@media (max-width: 860px) {
3941 .ct-list {
3942 width: 260px;
3943 }
3944
3945 .cal-row-time {
3946 width: 96px;
3947 }
3948}
3949
3950@media (max-width: 640px) {
3951 .content:has(.pim-view) {
3952 padding: 12px;
3953 }
3954
3955 .cal-toolbar {
3956 padding: 8px;
3957 }
3958
3959 .cal-title {
3960 order: -1;
3961 flex-basis: 100%;
3962 }
3963
3964 .cal-views {
3965 margin-left: auto;
3966 }
3967
3968 .cal-view-btn {
3969 padding: 6px 9px;
3970 }
3971
3972 /* Phones get compact day cells with dots; the day list below the grid
3973 shows the chosen day's events. */
3974 .cal-week {
3975 grid-template-rows: 46px;
3976 }
3977
3978 .cal-item,
3979 .cal-more {
3980 display: none;
3981 }
3982
3983 .cal-dots {
3984 display: flex;
3985 }
3986
3987 .cal-cell {
3988 align-items: center;
3989 padding: 4px 2px;
3990 }
3991
3992 .cal-head-day {
3993 padding: 6px 2px;
3994 text-align: center;
3995 }
3996
3997 .cal-inv {
3998 flex-direction: column;
3999 align-items: stretch;
4000 }
4001
4002 .cal-row-time {
4003 width: 80px;
4004 }
4005
4006 .cal-facts {
4007 grid-template-columns: minmax(0, 1fr);
4008 gap: 2px;
4009 }
4010
4011 .cal-facts dd {
4012 margin-bottom: 8px;
4013 }
4014
4015 /* List and details take turns. */
4016 .ct-list {
4017 width: auto;
4018 flex: 1;
4019 border-right: 0;
4020 }
4021
4022 .ct-rows {
4023 max-height: none;
4024 }
4025
4026 .ct-detail,
4027 .ct-has-open .ct-list {
4028 display: none;
4029 }
4030
4031 .ct-has-open .ct-detail {
4032 display: block;
4033 padding: 12px;
4034 }
4035
4036 .ct-back {
4037 display: inline-flex;
4038 }
4039}
4040