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/* Phones: the name gets the full width, the buttons move below it. */
1659@media (max-width: 640px) {
1660 .share-item {
1661 flex-wrap: wrap;
1662 }
1663
1664 .share-item-main,
1665 .share-item-actions {
1666 flex: 1 1 100%;
1667 min-width: 0;
1668 }
1669
1670 .shares-view .share-item-actions select {
1671 flex: 1 1 auto;
1672 min-width: 0;
1673 }
1674
1675 .share-item-actions .btn {
1676 white-space: nowrap;
1677 }
1678}
1679
1680/* A loan's mode select sits beside small buttons: same height. No
1681 percentage cap: the actions box sizes to its content, so a cap would
1682 leave a gap after the select. */
1683.shares-view .share-item-actions select {
1684 max-width: none;
1685 padding-top: 5px;
1686 padding-bottom: 5px;
1687 font-size: 12.5px;
1688}
1689
1690/* Sections of the share management view: the viewer's own links and, for an
1691 admin, one section per account. Native <details>/<summary>. */
1692
1693.share-groups {
1694 display: flex;
1695 flex-direction: column;
1696 gap: 12px;
1697}
1698
1699.share-group {
1700 border: 1px solid var(--border);
1701 background: var(--panel);
1702}
1703
1704.share-group-head {
1705 display: flex;
1706 align-items: center;
1707 gap: 8px;
1708 padding: 10px 12px;
1709 font-size: 14px;
1710 font-weight: 600;
1711 cursor: pointer;
1712 /* The caret below stands in for the disclosure triangle. */
1713 list-style: none;
1714}
1715
1716.share-group-head::-webkit-details-marker {
1717 display: none;
1718}
1719
1720.share-group-head:hover {
1721 background: color-mix(in srgb, var(--accent) 6%, var(--panel));
1722}
1723
1724.share-group-head:focus-visible {
1725 outline: 2px solid var(--accent);
1726 outline-offset: -2px;
1727}
1728
1729.share-caret {
1730 width: 16px;
1731 height: 16px;
1732 flex: none;
1733 transition: rotate 0.15s ease;
1734}
1735
1736.share-group[open] .share-caret {
1737 rotate: 90deg;
1738}
1739
1740.share-group-count {
1741 font-size: 12px;
1742 font-weight: 400;
1743}
1744
1745/* A live link whose account cannot sign in any more. */
1746.share-group-warn {
1747 font-size: 12px;
1748 font-weight: 400;
1749 color: var(--danger);
1750}
1751
1752.share-group-del {
1753 margin-left: auto;
1754}
1755
1756/* Rows inside a section read as one block: the border moves from each row to
1757 the section. */
1758.share-group .share-list {
1759 gap: 0;
1760 border-top: 1px solid var(--border);
1761}
1762
1763.share-group .share-item {
1764 border: 0;
1765 border-top: 1px solid var(--border);
1766}
1767
1768.share-group .share-item:first-child {
1769 border-top: 0;
1770}
1771
1772/* ------------------------------------------------------------------ */
1773/* Admin (milestone 7) */
1774/* ------------------------------------------------------------------ */
1775
1776.admin-view {
1777 display: flex;
1778 flex-direction: column;
1779 gap: 16px;
1780 width: 100%;
1781 max-width: 960px;
1782 margin: 0 auto;
1783}
1784
1785.admin-section-title {
1786 margin: 0 0 14px;
1787 font-size: 15px;
1788}
1789
1790.users-header {
1791 display: flex;
1792 align-items: center;
1793 justify-content: space-between;
1794 margin-bottom: 4px;
1795}
1796
1797.users-header .admin-section-title {
1798 margin-bottom: 0;
1799}
1800
1801.users-header .btn-primary {
1802 width: auto;
1803}
1804
1805.user-list {
1806 display: flex;
1807 flex-direction: column;
1808 gap: 10px;
1809}
1810
1811.user-row {
1812 display: flex;
1813 align-items: center;
1814 justify-content: space-between;
1815 gap: 12px;
1816 padding: 12px 14px;
1817 border: 1px solid var(--border);
1818}
1819
1820.user-row-main {
1821 min-width: 0;
1822}
1823
1824.user-row-name {
1825 display: flex;
1826 align-items: center;
1827 gap: 6px;
1828 font-weight: 600;
1829 font-size: 14px;
1830 flex-wrap: wrap;
1831}
1832
1833.ic-admin {
1834 width: 16px;
1835 height: 16px;
1836 flex: none;
1837}
1838
1839.user-row-actions {
1840 display: flex;
1841 gap: 6px;
1842 flex-shrink: 0;
1843}
1844
1845.badge-off {
1846 background: color-mix(in srgb, var(--danger) 15%, var(--panel));
1847 border-color: var(--danger);
1848 color: var(--danger);
1849}
1850
1851.setting-row {
1852 display: flex;
1853 align-items: center;
1854 gap: 10px;
1855 padding: 14px;
1856 border: 1px solid var(--border);
1857 cursor: pointer;
1858}
1859
1860.setting-row input {
1861 margin-left: auto;
1862}
1863
1864.setting-label {
1865 display: block;
1866 font-weight: 600;
1867 font-size: 14px;
1868}
1869
1870.setting-desc {
1871 display: block;
1872 font-size: 12px;
1873 color: var(--muted);
1874 margin-top: 2px;
1875}
1876
1877/* Setting row whose control is a select, pushed to the right. */
1878.setting-row-select {
1879 cursor: default;
1880}
1881
1882.setting-row-select > span {
1883 flex: 1;
1884}
1885
1886.setting-row-select select {
1887 appearance: none;
1888 margin-left: auto;
1889 padding: 10px 42px 10px 12px;
1890 border: 1px solid var(--border);
1891 background-color: var(--panel);
1892 background-image: var(--select-arrow);
1893 background-repeat: no-repeat;
1894 background-position: right 12px center;
1895 background-size: 14px 14px;
1896 color: var(--text);
1897 font: inherit;
1898 max-width: 190px;
1899}
1900
1901/* User create/edit form */
1902
1903.user-form {
1904 width: 520px;
1905}
1906
1907.field-input {
1908 width: 100%;
1909 box-sizing: border-box;
1910}
1911
1912/* Search exclusions: a bordered block matching the setting row above it. */
1913.excludes-field {
1914 display: flex;
1915 flex-direction: column;
1916 gap: 10px;
1917 padding: 14px;
1918 border: 1px solid var(--border);
1919}
1920
1921.excludes-list {
1922 display: flex;
1923 flex-direction: column;
1924 gap: 2px;
1925}
1926
1927.exclude-row {
1928 display: flex;
1929 align-items: center;
1930 gap: 8px;
1931 padding: 4px 0;
1932}
1933
1934.exclude-path {
1935 flex: 1;
1936 font-size: 13px;
1937 overflow: hidden;
1938 text-overflow: ellipsis;
1939 white-space: nowrap;
1940}
1941
1942.exclude-remove {
1943 flex: none;
1944 padding: 4px 6px;
1945}
1946
1947.exclude-add {
1948 align-self: flex-start;
1949}
1950
1951.excludes-empty {
1952 font-size: 13px;
1953 margin: 0;
1954}
1955
1956.field-hint {
1957 font-size: 12px;
1958 margin: -2px 0 8px;
1959}
1960
1961.check-row {
1962 display: flex;
1963 align-items: center;
1964 gap: 8px;
1965 font-size: 14px;
1966 margin-bottom: 12px;
1967 cursor: pointer;
1968}
1969
1970.form-error {
1971 color: var(--danger);
1972 font-size: 13px;
1973 margin: 4px 0 8px;
1974}
1975
1976.roots-editor {
1977 display: flex;
1978 flex-direction: column;
1979 gap: 6px;
1980}
1981
1982.root-draft {
1983 display: flex;
1984 align-items: center;
1985 gap: 8px;
1986}
1987
1988.root-draft-path {
1989 flex: 1;
1990 font-size: 13px;
1991 overflow: hidden;
1992 text-overflow: ellipsis;
1993 white-space: nowrap;
1994}
1995
1996.root-draft-mode {
1997 width: auto;
1998 padding: 6px 8px;
1999}
2000
2001.root-add {
2002 align-self: flex-start;
2003 margin-top: 4px;
2004}
2005
2006/* ------------------------------------------------------------------ */
2007/* Empty state + keyboard focus (milestone 8) */
2008/* ------------------------------------------------------------------ */
2009
2010.empty-state {
2011 display: flex;
2012 flex-direction: column;
2013 align-items: center;
2014 justify-content: center;
2015 gap: 8px;
2016 padding: 64px 20px;
2017 text-align: center;
2018}
2019
2020.empty-glyph {
2021 width: 52px;
2022 height: 52px;
2023}
2024
2025.empty-state h3 {
2026 margin: 0;
2027 font-size: 16px;
2028}
2029
2030/* Visible keyboard focus on selectable file/folder entries. The ring is
2031 inset (negative offset) so the scroll container never clips it. */
2032.tile:focus-visible,
2033.row:focus-visible {
2034 outline: 2px solid var(--accent);
2035 outline-offset: -2px;
2036}
2037
2038/* ------------------------------------------------------------------ */
2039/* Search view (#/search) */
2040/* ------------------------------------------------------------------ */
2041
2042.search-view {
2043 display: flex;
2044 flex-direction: column;
2045 gap: 14px;
2046}
2047
2048/* Query bar ------------------------------------------------------ */
2049
2050.query-bar {
2051 display: flex;
2052 align-items: stretch;
2053 gap: 8px;
2054}
2055
2056.query-input {
2057 flex: 1;
2058 display: flex;
2059 align-items: center;
2060 gap: 8px;
2061 background: var(--panel);
2062 border: 1px solid var(--border);
2063 padding: 0 10px;
2064 min-width: 0;
2065}
2066
2067.query-input .qi-icon {
2068 width: 18px;
2069 height: 18px;
2070 color: var(--muted);
2071 flex: none;
2072}
2073
2074.query-input input {
2075 flex: 1;
2076 min-width: 0;
2077 border: none;
2078 outline: none;
2079 background: transparent;
2080 color: var(--text);
2081 font: inherit;
2082 font-size: 14px;
2083 height: 38px;
2084 padding: 0;
2085}
2086
2087.query-input input::placeholder {
2088 color: var(--muted);
2089}
2090
2091/* Segmented Name / Content / Both control */
2092
2093.seg {
2094 display: flex;
2095 border: 1px solid var(--border);
2096 background: var(--panel);
2097}
2098
2099.seg button {
2100 appearance: none;
2101 border: none;
2102 border-right: 1px solid var(--border);
2103 background: transparent;
2104 color: var(--muted);
2105 font: inherit;
2106 font-size: 13px;
2107 padding: 0 14px;
2108 cursor: pointer;
2109}
2110
2111.seg button:last-child {
2112 border-right: none;
2113}
2114
2115.seg button:hover {
2116 background: var(--bg);
2117}
2118
2119.seg button.on {
2120 background: var(--accent);
2121 color: #fff;
2122}
2123
2124/* Start folder --------------------------------------------------- */
2125
2126/* The folder a search starts in; opens the folder picker. */
2127.folder-pick {
2128 flex: none;
2129 display: flex;
2130 align-items: center;
2131 gap: 8px;
2132 max-width: 260px;
2133 border: 1px solid var(--border);
2134 background: var(--panel);
2135 color: var(--text);
2136 font: inherit;
2137 font-size: 13px;
2138 padding: 0 10px;
2139 cursor: pointer;
2140}
2141
2142.folder-pick:hover {
2143 border-color: var(--muted);
2144}
2145
2146.folder-pick .fp-icon {
2147 width: 16px;
2148 height: 16px;
2149 color: var(--muted);
2150 flex: none;
2151}
2152
2153.folder-pick .fp-path {
2154 overflow: hidden;
2155 text-overflow: ellipsis;
2156 white-space: nowrap;
2157}
2158
2159/* Run / stop ------------------------------------------------------- */
2160
2161.btn-run {
2162 border: 1px solid var(--accent-border);
2163 background: var(--accent);
2164 color: #fff;
2165 font: inherit;
2166 font-size: 13px;
2167 font-weight: 600;
2168 padding: 0 18px;
2169 cursor: pointer;
2170}
2171
2172.btn-run:hover {
2173 background: var(--accent-hover);
2174}
2175
2176.btn-stop {
2177 display: flex;
2178 align-items: center;
2179 gap: 6px;
2180 border: 1px solid var(--danger-border);
2181 background: var(--danger);
2182 color: #fff;
2183 font: inherit;
2184 font-size: 13px;
2185 font-weight: 600;
2186 padding: 0 14px;
2187 cursor: pointer;
2188}
2189
2190.btn-stop:hover {
2191 opacity: 0.9;
2192}
2193
2194.btn-stop .ic-stop {
2195 width: 14px;
2196 height: 14px;
2197}
2198
2199/* Status line ------------------------------------------------------ */
2200
2201.status-line {
2202 display: flex;
2203 align-items: center;
2204 gap: 8px;
2205 font-size: 13px;
2206 color: var(--muted);
2207}
2208
2209.status-line .dot {
2210 width: 8px;
2211 height: 8px;
2212 background: var(--accent);
2213 animation: search-pulse 1s ease-in-out infinite;
2214}
2215
2216@keyframes search-pulse {
2217 0%,
2218 100% {
2219 opacity: 1;
2220 }
2221 50% {
2222 opacity: 0.3;
2223 }
2224}
2225
2226.status-line .skip {
2227 opacity: 0.75;
2228}
2229
2230/* Group labels ----------------------------------------------------- */
2231
2232.group-label {
2233 display: flex;
2234 align-items: center;
2235 gap: 8px;
2236 font-size: 12px;
2237 font-weight: 600;
2238 letter-spacing: 0.04em;
2239 text-transform: uppercase;
2240 color: var(--muted);
2241}
2242
2243/* Result group: a `display:flex` (or `none`, from the inline style) flex
2244 item of `.search-view` that re-establishes the column layout for its
2245 label/list/more-row children. */
2246.result-group {
2247 flex-direction: column;
2248 gap: 14px;
2249}
2250
2251.group-label .gl-icon {
2252 width: 15px;
2253 height: 15px;
2254}
2255
2256.group-label .n {
2257 font-weight: 400;
2258 text-transform: none;
2259 letter-spacing: 0;
2260}
2261
2262.group-label .grow {
2263 flex: 1;
2264}
2265
2266.collapse-all {
2267 appearance: none;
2268 border: 1px solid var(--border);
2269 background: transparent;
2270 color: var(--muted);
2271 font: inherit;
2272 font-size: 12px;
2273 text-transform: none;
2274 letter-spacing: 0;
2275 padding: 3px 10px;
2276 cursor: pointer;
2277}
2278
2279.collapse-all:hover {
2280 background: var(--bg);
2281 color: var(--text);
2282}
2283
2284/* Name-result rows --------------------------------------------------- */
2285
2286.srow {
2287 display: grid;
2288 /* icon | name+dir | size | goto */
2289 grid-template-columns: 28px minmax(0, 1fr) 90px 30px;
2290 gap: 12px;
2291 align-items: center;
2292 padding: 9px 14px;
2293 border-bottom: 1px solid var(--border);
2294 cursor: pointer;
2295 font-size: 14px;
2296 user-select: none;
2297}
2298
2299.srow:last-child {
2300 border-bottom: none;
2301}
2302
2303.srow:hover {
2304 background: var(--bg);
2305}
2306
2307.srow-icon {
2308 width: 20px;
2309 height: 20px;
2310}
2311
2312.srow-main {
2313 min-width: 0;
2314 display: flex;
2315 flex-direction: column;
2316 line-height: 1.3;
2317}
2318
2319.srow-name {
2320 overflow: hidden;
2321 text-overflow: ellipsis;
2322 white-space: nowrap;
2323}
2324
2325/* Highlighted query hits (plain fragments render as bare spans). */
2326mark.hl-hit {
2327 background: color-mix(in srgb, var(--accent) 25%, transparent);
2328 color: inherit;
2329}
2330
2331/* The directory-path line (the name line lives inside .srow-name). */
2332.srow-dir {
2333 font-size: 12px;
2334 color: var(--muted);
2335 overflow: hidden;
2336 text-overflow: ellipsis;
2337 white-space: nowrap;
2338}
2339
2340.srow-dir mark.hl-hit {
2341 color: inherit;
2342}
2343
2344.ssize {
2345 font-size: 13px;
2346 color: var(--muted);
2347 text-align: right;
2348}
2349
2350/* Paging footer: position in the results on the left, the action on the
2351 right. Separated from the list by its own border so the button never reads
2352 as another result row. */
2353.page-foot {
2354 display: flex;
2355 flex-wrap: wrap;
2356 align-items: center;
2357 justify-content: space-between;
2358 gap: 12px;
2359 padding: 10px 14px;
2360 border-top: 1px solid var(--border);
2361 font-size: 12px;
2362 color: var(--muted);
2363}
2364
2365.page-foot .pf-count {
2366 font-variant-numeric: tabular-nums;
2367}
2368
2369.page-foot .pf-more,
2370.pf-btn {
2371 appearance: none;
2372 padding: 5px 12px;
2373 border: 1px solid var(--border);
2374 background: var(--panel);
2375 font: inherit;
2376 font-weight: 500;
2377 color: var(--text);
2378 cursor: pointer;
2379}
2380
2381.page-foot .pf-more:hover,
2382.pf-btn:hover:not(:disabled) {
2383 border-color: var(--accent);
2384 color: var(--accent);
2385}
2386
2387.pf-pages {
2388 display: flex;
2389 align-items: center;
2390 gap: 4px;
2391 margin-right: auto;
2392}
2393
2394.pf-btn {
2395 min-width: 30px;
2396 padding: 5px 8px;
2397 font-variant-numeric: tabular-nums;
2398}
2399
2400.pf-btn.current {
2401 border-color: var(--accent);
2402 background: var(--accent);
2403 color: #fff;
2404}
2405
2406.pf-btn:disabled {
2407 opacity: 0.4;
2408 cursor: default;
2409}
2410
2411.pf-goto {
2412 display: flex;
2413 align-items: center;
2414 gap: 6px;
2415 margin: 0;
2416}
2417
2418.pf-goto input {
2419 width: 72px;
2420 padding: 5px 8px;
2421}
2422
2423/* An arrow-key page turn is loading. Fixed at the top, so it also shows over
2424 the preview. The delay keeps fast turns from flashing. */
2425.page-busy {
2426 position: fixed;
2427 top: 0;
2428 left: 0;
2429 right: 0;
2430 height: 3px;
2431 overflow: hidden;
2432 z-index: 250;
2433 pointer-events: none;
2434 animation: page-busy-in 0s 200ms both;
2435}
2436
2437.page-busy::after {
2438 content: "";
2439 display: block;
2440 width: 30%;
2441 height: 100%;
2442 background: var(--accent);
2443 animation: upload-indeterminate 1.2s ease-in-out infinite;
2444}
2445
2446body:has(.page-busy) {
2447 cursor: progress;
2448}
2449
2450@keyframes page-busy-in {
2451 from {
2452 opacity: 0;
2453 }
2454 to {
2455 opacity: 1;
2456 }
2457}
2458
2459/* Content matches ----------------------------------------------------- */
2460
2461.match-card {
2462 background: var(--panel);
2463 border: 1px solid var(--border);
2464 border-top: none;
2465}
2466
2467/* The first card is the one right after the group label; `:first-of-type`
2468 cannot be used because the label is an earlier `div` sibling. */
2469.group-label + .match-card {
2470 border-top: 1px solid var(--border);
2471}
2472
2473/* The header opens the file; the matched lines below are text to read. */
2474.match-head {
2475 display: flex;
2476 align-items: center;
2477 gap: 10px;
2478 padding: 9px 14px;
2479 font-size: 14px;
2480 cursor: pointer;
2481}
2482
2483.match-head:hover {
2484 background: var(--bg);
2485}
2486
2487.match-head .mi {
2488 width: 18px;
2489 height: 18px;
2490 color: var(--muted);
2491 flex: none;
2492}
2493
2494/* Takes the slack, so the count and the two buttons sit at the right edge. */
2495.mpath {
2496 flex: 1;
2497 min-width: 0;
2498 overflow: hidden;
2499 text-overflow: ellipsis;
2500 white-space: nowrap;
2501}
2502
2503.mpath b {
2504 font-weight: 600;
2505}
2506
2507.mcount {
2508 flex: none;
2509 font-size: 12px;
2510 color: var(--muted);
2511}
2512
2513/* "Show in folder", in both the name rows and the match-card headers. */
2514.goto-btn {
2515 appearance: none;
2516 flex: none;
2517 justify-self: end;
2518 display: flex;
2519 align-items: center;
2520 justify-content: center;
2521 width: 26px;
2522 height: 26px;
2523 padding: 0;
2524 border: 1px solid transparent;
2525 background: transparent;
2526 cursor: pointer;
2527}
2528
2529.goto-btn:hover {
2530 border-color: var(--border);
2531}
2532
2533.goto-btn .goto-ic {
2534 width: 16px;
2535 height: 16px;
2536 color: var(--muted);
2537}
2538
2539.goto-btn:hover .goto-ic {
2540 color: var(--accent);
2541}
2542
2543.chev {
2544 appearance: none;
2545 border: 1px solid var(--border);
2546 background: transparent;
2547 width: 26px;
2548 height: 26px;
2549 display: flex;
2550 align-items: center;
2551 justify-content: center;
2552 cursor: pointer;
2553 flex: none;
2554}
2555
2556.chev:hover {
2557 background: var(--bg);
2558}
2559
2560.chev .chev-down,
2561.chev .chev-up {
2562 display: block;
2563 width: 10px;
2564 height: 10px;
2565 border-right: 2px solid var(--muted);
2566 border-bottom: 2px solid var(--muted);
2567}
2568
2569.chev .chev-down {
2570 transform: rotate(45deg) translate(-1px, -1px);
2571}
2572
2573.chev .chev-up {
2574 transform: rotate(-135deg) translate(-1px, -1px);
2575}
2576
2577.match-lines {
2578 border-top: 1px solid var(--border);
2579 background: var(--bg);
2580 font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
2581 font-size: 12.5px;
2582}
2583
2584/* Matched lines are text to read, not a control: the card header is what
2585 opens the file, so no pointer cursor and no hover here. */
2586.mline {
2587 display: grid;
2588 grid-template-columns: 52px minmax(0, 1fr);
2589 gap: 12px;
2590 padding: 5px 14px;
2591 border-bottom: 1px solid var(--border);
2592 white-space: pre;
2593 user-select: text;
2594}
2595
2596.mline:last-child {
2597 border-bottom: none;
2598}
2599
2600.mline .ln {
2601 color: var(--muted);
2602 text-align: right;
2603 user-select: none;
2604}
2605
2606.mline .tx {
2607 overflow: hidden;
2608 text-overflow: ellipsis;
2609}
2610
2611.search-empty {
2612 padding: 48px 20px;
2613 text-align: center;
2614 color: var(--muted);
2615 font-size: 14px;
2616}
2617
2618/* ------------------------------------------------------------------ */
2619/* Uploads: overlay panel + log view */
2620/* ------------------------------------------------------------------ */
2621
2622.upload-panel {
2623 position: fixed;
2624 right: 20px;
2625 bottom: 20px;
2626 z-index: 190;
2627 width: min(380px, calc(100vw - 40px));
2628 background: var(--panel);
2629 color: var(--text);
2630 border: 1px solid var(--border);
2631 box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
2632 font-size: 13px;
2633}
2634
2635/* The toast sits above the panel when both show. */
2636.upload-panel ~ .toast,
2637.toast:has(~ .upload-panel) {
2638 bottom: 80px;
2639}
2640
2641.upload-panel-head {
2642 display: flex;
2643 align-items: center;
2644 gap: 8px;
2645 padding: 8px 10px;
2646 border-bottom: 1px solid var(--border);
2647}
2648
2649.upload-panel.collapsed .upload-panel-head {
2650 border-bottom: none;
2651}
2652
2653.upload-panel-title {
2654 font-weight: 600;
2655}
2656
2657.upload-panel-summary {
2658 margin-left: auto;
2659 font-size: 12px;
2660}
2661
2662.upload-panel-list {
2663 max-height: 50vh;
2664 overflow-y: auto;
2665}
2666
2667.upload-job {
2668 padding: 8px 10px;
2669 border-bottom: 1px solid var(--border);
2670}
2671
2672.upload-job:last-child {
2673 border-bottom: none;
2674}
2675
2676.upload-job-head {
2677 display: flex;
2678 align-items: center;
2679 gap: 4px;
2680}
2681
2682.upload-job-label {
2683 flex: 1;
2684 min-width: 0;
2685 overflow: hidden;
2686 text-overflow: ellipsis;
2687 white-space: nowrap;
2688 font-weight: 500;
2689}
2690
2691.upload-job-pct {
2692 font-variant-numeric: tabular-nums;
2693 margin-right: 4px;
2694}
2695
2696.upload-bar {
2697 height: 4px;
2698 margin: 6px 0 4px;
2699 /* Clip the indeterminate block: it slides past both ends. */
2700 overflow: hidden;
2701 background: color-mix(in srgb, var(--accent) 15%, var(--panel));
2702}
2703
2704.upload-bar-fill {
2705 height: 100%;
2706 background: var(--accent);
2707 transition: width 0.2s linear;
2708}
2709
2710/* Preparing: nothing to measure yet, so a sliding block instead of a fill. */
2711.upload-bar.indeterminate .upload-bar-fill {
2712 width: 30% !important;
2713 animation: upload-indeterminate 1.2s ease-in-out infinite;
2714}
2715
2716@keyframes upload-indeterminate {
2717 0% {
2718 transform: translateX(-100%);
2719 }
2720 100% {
2721 transform: translateX(333%);
2722 }
2723}
2724
2725.upload-job.failed .upload-bar-fill {
2726 background: var(--danger);
2727}
2728
2729.upload-job.done .upload-bar-fill {
2730 background: var(--muted);
2731}
2732
2733.upload-job-state {
2734 font-size: 12px;
2735 overflow: hidden;
2736 text-overflow: ellipsis;
2737 white-space: nowrap;
2738}
2739
2740.upload-job.failed .upload-job-state {
2741 color: var(--danger);
2742 white-space: normal;
2743}
2744
2745.upload-conflict {
2746 display: flex;
2747 flex-direction: column;
2748 gap: 6px;
2749 margin-top: 8px;
2750 padding: 8px;
2751 border: 1px solid var(--danger-border);
2752 background: color-mix(in srgb, var(--danger) 8%, var(--panel));
2753}
2754
2755.upload-conflict-all {
2756 display: flex;
2757 /* The label wraps on a narrow dialog; keep the box on the first line. */
2758 align-items: flex-start;
2759 gap: 0.5rem;
2760 font-size: 12px;
2761 cursor: pointer;
2762}
2763
2764.upload-conflict-all input {
2765 flex: none;
2766 margin-top: 2px;
2767}
2768
2769.modal .upload-conflict-all {
2770 margin: 8px 0 0;
2771 font-size: 13px;
2772}
2773
2774.upload-conflict-actions {
2775 display: flex;
2776 justify-content: flex-end;
2777 gap: 6px;
2778}
2779
2780.upload-graph {
2781 margin-top: 8px;
2782 color: var(--accent);
2783 border: 1px solid var(--border);
2784 background: color-mix(in srgb, var(--accent) 6%, var(--panel));
2785}
2786
2787/* Peak label above the plot, so the line can never run through it. */
2788.upload-graph-max {
2789 display: block;
2790 text-align: right;
2791 padding: 2px 6px 0;
2792 font-size: 11px;
2793 line-height: 14px;
2794}
2795
2796.upload-graph svg {
2797 display: block;
2798 width: 100%;
2799 height: 46px;
2800}
2801
2802.upload-errors {
2803 margin: 6px 0 0;
2804 padding-left: 18px;
2805 font-size: 12px;
2806 color: var(--danger);
2807}
2808
2809.upload-err-file {
2810 font-weight: 500;
2811}
2812
2813/* Log view: the same rows, full width, roomier. */
2814.uploads-view .upload-log {
2815 border: 1px solid var(--border);
2816 background: var(--panel);
2817 font-size: 14px;
2818}
2819
2820.uploads-view .upload-job {
2821 padding: 12px 14px;
2822}
2823
2824.uploads-view .upload-graph svg {
2825 height: 76px;
2826}
2827
2828/* Security settings tab: password, passkeys, sign-in requirement. */
2829
2830.security-tab {
2831 display: flex;
2832 flex-direction: column;
2833 gap: 10px;
2834}
2835
2836.security-tab h3 {
2837 margin: 10px 0 0;
2838}
2839
2840/* Matches .excludes-field, so the three blocks of this tab read as one list. */
2841.security-form {
2842 display: flex;
2843 flex-direction: column;
2844 gap: 10px;
2845 padding: 14px;
2846 border: 1px solid var(--border);
2847}
2848
2849.security-form .field input {
2850 width: 100%;
2851 box-sizing: border-box;
2852}
2853
2854.passkey-list {
2855 display: flex;
2856 flex-direction: column;
2857 gap: 2px;
2858 padding: 0 14px;
2859}
2860
2861.passkey-row {
2862 display: flex;
2863 align-items: center;
2864 gap: 8px;
2865 padding: 6px 0;
2866}
2867
2868.passkey-main {
2869 flex: 1;
2870 overflow: hidden;
2871}
2872
2873.passkey-name {
2874 display: block;
2875 font-size: 13px;
2876 overflow: hidden;
2877 text-overflow: ellipsis;
2878 white-space: nowrap;
2879}
2880
2881/* "Needs your user name": a note, not an error. This passkey works, it just
2882 cannot be found without the name typed in first. */
2883.passkey-warning {
2884 display: inline-block;
2885 margin-top: 2px;
2886 padding: 1px 6px;
2887 border: 1px solid var(--border);
2888 font-size: 11px;
2889 color: var(--muted);
2890}
2891
2892/* The sign-in requirement's labels are whole phrases, so the shared 190px
2893 cap for setting selects would clip them. */
2894.security-tab .setting-row-select select {
2895 max-width: none;
2896 width: auto;
2897}
2898
2899/* Separator between the password form and the passkey button. `.auth-card p`
2900 sets a bottom-only margin on every paragraph in the card, and wins on
2901 specificity, so both of these have to name the card too. */
2902.auth-card .login-or {
2903 margin: 16px 0 8px;
2904 font-size: 12px;
2905 color: var(--muted);
2906 text-transform: lowercase;
2907}
2908
2909.auth-card .setting-desc {
2910 margin-top: 8px;
2911}
2912
2913.auth-card .btn {
2914 width: 100%;
2915}
2916
2917/* ------------------------------------------------------------------ */
2918/* Calendars and contacts */
2919/* ------------------------------------------------------------------ */
2920
2921.pim-view {
2922 display: flex;
2923 gap: 16px;
2924 flex: 1;
2925 min-height: 0;
2926 width: 100%;
2927}
2928
2929.pim-side {
2930 flex: none;
2931 width: 260px;
2932 display: flex;
2933 flex-direction: column;
2934 gap: 8px;
2935 min-width: 0;
2936}
2937
2938.pim-side-head {
2939 display: flex;
2940 align-items: center;
2941 gap: 4px;
2942}
2943
2944.pim-side-head .admin-section-title {
2945 flex: 1;
2946 margin: 0;
2947}
2948
2949.pim-list {
2950 display: flex;
2951 flex-direction: column;
2952 border: 1px solid var(--border);
2953 background: var(--panel);
2954}
2955
2956.pim-group-label {
2957 padding: 10px 10px 4px;
2958 font-size: 12px;
2959 color: var(--muted);
2960 border-top: 1px solid var(--border);
2961}
2962
2963.pim-row {
2964 display: flex;
2965 align-items: center;
2966 min-width: 0;
2967}
2968
2969.pim-row:hover {
2970 background: var(--bg);
2971}
2972
2973.pim-toggle {
2974 flex: none;
2975 display: inline-flex;
2976 align-items: center;
2977 justify-content: center;
2978 width: 36px;
2979 height: 36px;
2980 padding: 0;
2981 border: 0;
2982 background: none;
2983 cursor: pointer;
2984}
2985
2986.pim-swatch {
2987 display: inline-block;
2988 flex: none;
2989 width: 14px;
2990 height: 14px;
2991 background: var(--swatch);
2992 border: 2px solid var(--swatch);
2993}
2994
2995/* A hidden collection keeps its color as an outline only. */
2996.pim-row-hidden .pim-swatch {
2997 background: transparent;
2998}
2999
3000.pim-row-hidden .pim-row-name {
3001 color: var(--muted);
3002}
3003
3004.pim-row-main {
3005 flex: 1;
3006 min-width: 0;
3007 display: flex;
3008 align-items: baseline;
3009 justify-content: flex-start;
3010 gap: 6px;
3011 padding: 8px 10px 8px 0;
3012 border: 0;
3013 background: none;
3014 color: var(--text);
3015 font: inherit;
3016 font-size: 14px;
3017 text-align: left;
3018 cursor: pointer;
3019}
3020
3021.pim-row-name {
3022 min-width: 0;
3023 overflow: hidden;
3024 text-overflow: ellipsis;
3025 white-space: nowrap;
3026}
3027
3028.pim-row-gear {
3029 flex: none;
3030 margin-right: 4px;
3031}
3032
3033.pim-show-all {
3034 align-self: flex-start;
3035}
3036
3037.pim-row-note {
3038 flex: none;
3039 max-width: 45%;
3040 font-size: 12px;
3041 color: var(--muted);
3042 overflow: hidden;
3043 text-overflow: ellipsis;
3044 white-space: nowrap;
3045}
3046
3047.pim-main {
3048 flex: 1;
3049 min-width: 0;
3050 border: 1px solid var(--border);
3051 background: var(--panel);
3052}
3053
3054.pim-modal {
3055 width: 600px;
3056 max-height: calc(100vh - 48px);
3057 overflow-y: auto;
3058}
3059
3060.pim-modal-title {
3061 display: flex;
3062 align-items: center;
3063 gap: 10px;
3064 overflow-wrap: anywhere;
3065}
3066
3067.pim-note {
3068 margin: -6px 0 8px;
3069 font-size: 13px;
3070}
3071
3072.pim-section {
3073 display: block;
3074 padding: 14px 0 0;
3075 margin-top: 14px;
3076 border-top: 1px solid var(--border);
3077}
3078
3079.pim-section-title {
3080 margin: 0 0 8px;
3081 font-size: 14px;
3082}
3083
3084.pim-hint {
3085 margin: 0 0 10px;
3086 font-size: 13px;
3087}
3088
3089.pim-section-actions {
3090 display: flex;
3091 justify-content: flex-end;
3092 flex-wrap: wrap;
3093 gap: 8px;
3094}
3095
3096.pim-section-actions.pim-left {
3097 justify-content: flex-start;
3098}
3099
3100.pim-colors {
3101 display: flex;
3102 flex-wrap: wrap;
3103 align-items: center;
3104 gap: 6px;
3105}
3106
3107.pim-color {
3108 width: 26px;
3109 height: 26px;
3110 padding: 0;
3111 border: 2px solid var(--panel);
3112 outline: 1px solid var(--border);
3113 background: var(--swatch);
3114 cursor: pointer;
3115}
3116
3117.pim-color.selected {
3118 outline: 2px solid var(--text);
3119}
3120
3121.pim-color-input {
3122 width: 34px;
3123 height: 30px;
3124 padding: 0 2px;
3125 cursor: pointer;
3126}
3127
3128.pim-items {
3129 display: flex;
3130 flex-direction: column;
3131 gap: 6px;
3132 margin-bottom: 10px;
3133}
3134
3135.pim-item {
3136 display: flex;
3137 align-items: center;
3138 gap: 8px;
3139 min-width: 0;
3140}
3141
3142.pim-item-name {
3143 flex: 1;
3144 min-width: 0;
3145 font-size: 14px;
3146 overflow: hidden;
3147 text-overflow: ellipsis;
3148 white-space: nowrap;
3149}
3150
3151.pim-mode {
3152 flex: none;
3153 width: auto;
3154 max-width: 55%;
3155 padding: 6px 28px 6px 8px;
3156}
3157
3158.pim-add {
3159 display: flex;
3160 gap: 8px;
3161 align-items: center;
3162}
3163
3164.pim-add .field-input {
3165 flex: 1;
3166 min-width: 0;
3167}
3168
3169.pim-link {
3170 display: flex;
3171 align-items: center;
3172 gap: 10px;
3173 padding: 8px 10px;
3174 border: 1px solid var(--border);
3175 min-width: 0;
3176}
3177
3178.pim-link-main {
3179 flex: 1;
3180 min-width: 0;
3181 display: flex;
3182 flex-direction: column;
3183 gap: 2px;
3184}
3185
3186.pim-link-url {
3187 font-size: 13px;
3188 overflow: hidden;
3189 text-overflow: ellipsis;
3190 white-space: nowrap;
3191}
3192
3193.pim-link-meta {
3194 font-size: 12px;
3195}
3196
3197.pim-link-actions {
3198 display: flex;
3199 flex: none;
3200 gap: 6px;
3201}
3202
3203.pim-link-form {
3204 display: flex;
3205 flex-wrap: wrap;
3206 align-items: flex-end;
3207 gap: 10px;
3208}
3209
3210.pim-inline {
3211 display: flex;
3212 flex-direction: column;
3213 gap: 4px;
3214 font-size: 13px;
3215 color: var(--muted);
3216}
3217
3218.pim-inline input,
3219.pim-inline select {
3220 width: auto;
3221 min-width: 140px;
3222}
3223
3224.pim-hints,
3225.pim-sample {
3226 margin: 0 0 8px;
3227 padding-left: 18px;
3228 font-size: 13px;
3229 line-height: 1.5;
3230}
3231
3232.pim-sample li {
3233 overflow-wrap: anywhere;
3234}
3235
3236.admin-stack {
3237 display: flex;
3238 flex-direction: column;
3239 gap: 28px;
3240}
3241
3242.rooms-hint {
3243 margin-bottom: 12px;
3244}
3245
3246.room-address {
3247 font-size: 12px;
3248 margin-top: 2px;
3249 overflow-wrap: anywhere;
3250}
3251
3252.feeds-header {
3253 margin-top: 28px;
3254}
3255
3256/* Below this the month grid needs the width of the collection list. */
3257@media (max-width: 1180px) {
3258 .pim-view {
3259 flex-direction: column;
3260 }
3261
3262 .pim-side {
3263 width: auto;
3264 }
3265
3266 /* The events and contacts come first; the list of collections would
3267 fill a whole phone screen above them. */
3268 .pim-main {
3269 min-height: 240px;
3270 order: -1;
3271 }
3272}
3273
3274@media (max-width: 640px) {
3275 .pim-link {
3276 flex-direction: column;
3277 align-items: stretch;
3278 }
3279
3280 .pim-link-actions {
3281 flex-wrap: wrap;
3282 }
3283
3284 .pim-add {
3285 flex-wrap: wrap;
3286 }
3287
3288 .pim-add .pim-mode {
3289 max-width: 100%;
3290 }
3291
3292 /* A lent row keeps name, mode and remove button on one line. */
3293 .pim-item .pim-mode {
3294 flex: 0 1 auto;
3295 min-width: 0;
3296 }
3297}
3298
3299/* Mirrored chevron: the icon set has only the right-pointing one. */
3300.flip {
3301 transform: scaleX(-1);
3302}
3303
3304/* Selects in the calendar and contacts views, their dialogs, the shares
3305 page, and any form field: the same height and colors as the inputs beside
3306 them, in both themes. */
3307.pim-modal select,
3308.pim-view select,
3309.shares-view select,
3310select.field-input {
3311 appearance: none;
3312 padding: 10px 34px 10px 12px;
3313 border: 1px solid var(--border);
3314 background-color: var(--bg);
3315 background-image: var(--select-arrow);
3316 background-repeat: no-repeat;
3317 background-position: right 11px center;
3318 background-size: 12px 12px;
3319 color: var(--text);
3320 font: inherit;
3321 font-size: 14px;
3322}
3323
3324.pim-modal select:focus,
3325.pim-view select:focus,
3326.shares-view select:focus,
3327select.field-input:focus {
3328 outline: 2px solid var(--accent);
3329 outline-offset: -1px;
3330}
3331
3332.pim-link-form .pim-inline input {
3333 padding: 10px 12px;
3334}
3335
3336.pim-file-input {
3337 display: none;
3338}
3339
3340.pim-main {
3341 display: flex;
3342 flex-direction: column;
3343 min-height: 0;
3344}
3345
3346/* ---- Calendar ---------------------------------------------------- */
3347
3348.cal {
3349 display: flex;
3350 flex-direction: column;
3351 min-width: 0;
3352}
3353
3354.cal-pad {
3355 padding: 12px 14px;
3356}
3357
3358.cal-toolbar {
3359 display: flex;
3360 align-items: center;
3361 flex-wrap: wrap;
3362 gap: 8px 12px;
3363 padding: 10px 12px;
3364 border-bottom: 1px solid var(--border);
3365}
3366
3367.cal-nav {
3368 display: flex;
3369 align-items: center;
3370 gap: 4px;
3371}
3372
3373.cal-title {
3374 flex: 1;
3375 min-width: 0;
3376 margin: 0;
3377 font-size: 17px;
3378 white-space: nowrap;
3379 overflow: hidden;
3380 text-overflow: ellipsis;
3381}
3382
3383.cal-views {
3384 display: flex;
3385}
3386
3387.cal-view-btn {
3388 padding: 6px 12px;
3389 font-size: 13px;
3390 margin-left: -1px;
3391}
3392
3393.cal-view-btn.active {
3394 background: var(--accent);
3395 border-color: var(--accent-border);
3396 color: #fff;
3397 position: relative;
3398 z-index: 1;
3399}
3400
3401/* The buttons overlap by one pixel; the hovered one must draw on top, or its
3402 neighbor covers its right edge. */
3403.cal-view-btn:hover,
3404.cal-view-btn:focus-visible {
3405 position: relative;
3406 z-index: 2;
3407}
3408
3409.cal-badge {
3410 min-width: 18px;
3411 padding: 0 5px;
3412 font-size: 11px;
3413 line-height: 18px;
3414 background: var(--danger);
3415 color: #fff;
3416 text-align: center;
3417}
3418
3419.cal-truncated {
3420 margin: 0;
3421 padding: 8px 12px;
3422 font-size: 13px;
3423 color: var(--danger);
3424 border-bottom: 1px solid var(--border);
3425}
3426
3427.cal-head {
3428 display: grid;
3429 grid-template-columns: repeat(7, minmax(0, 1fr));
3430 border-bottom: 1px solid var(--border);
3431}
3432
3433.cal-head-day {
3434 padding: 6px 8px;
3435 font-size: 12px;
3436 color: var(--muted);
3437 overflow: hidden;
3438 text-overflow: ellipsis;
3439 white-space: nowrap;
3440}
3441
3442.cal-week {
3443 display: grid;
3444 grid-template-columns: repeat(7, minmax(0, 1fr));
3445 grid-template-rows: 26px repeat(var(--lines), 22px) 6px;
3446 border-bottom: 1px solid var(--border);
3447}
3448
3449.cal-cell {
3450 grid-row: 1 / -1;
3451 display: flex;
3452 flex-direction: column;
3453 align-items: flex-start;
3454 justify-content: flex-start;
3455 gap: 4px;
3456 min-width: 0;
3457 padding: 4px 6px;
3458 border: 0;
3459 border-right: 1px solid var(--border);
3460 background: none;
3461 color: var(--text);
3462 font: inherit;
3463 cursor: pointer;
3464}
3465
3466.cal-cell:nth-child(7) {
3467 border-right: 0;
3468}
3469
3470.cal-cell:hover {
3471 background: var(--bg);
3472}
3473
3474.cal-cell.cal-out .cal-num {
3475 color: var(--muted);
3476}
3477
3478.cal-cell.cal-sel {
3479 background: var(--bg);
3480 box-shadow: inset 0 0 0 2px var(--accent);
3481}
3482
3483.cal-num {
3484 font-size: 13px;
3485 line-height: 18px;
3486 min-width: 20px;
3487 text-align: center;
3488}
3489
3490.cal-today .cal-num {
3491 background: var(--accent);
3492 color: #fff;
3493 font-weight: 600;
3494}
3495
3496/* Phone only: the colors of a day's events instead of their titles. */
3497.cal-dots {
3498 display: none;
3499 gap: 3px;
3500}
3501
3502.cal-dot {
3503 display: inline-block;
3504 flex: none;
3505 width: 8px;
3506 height: 8px;
3507 background: var(--c, var(--accent));
3508}
3509
3510.cal-item {
3511 position: relative;
3512 z-index: 1;
3513 display: flex;
3514 align-items: center;
3515 justify-content: flex-start;
3516 gap: 4px;
3517 min-width: 0;
3518 margin: 1px 2px;
3519 padding: 0 6px;
3520 border: 0;
3521 font-size: 12px;
3522 line-height: 20px;
3523 text-align: left;
3524 cursor: pointer;
3525 overflow: hidden;
3526}
3527
3528.cal-bar {
3529 background: var(--c);
3530 color: #fff;
3531 text-shadow: 0 0 2px rgb(0 0 0 / 40%);
3532}
3533
3534.cal-bar.cal-cont-l {
3535 margin-left: 0;
3536}
3537
3538.cal-bar.cal-cont-r {
3539 margin-right: 0;
3540}
3541
3542.cal-chip {
3543 background: none;
3544 color: var(--text);
3545}
3546
3547.cal-chip:hover {
3548 background: var(--bg);
3549}
3550
3551.cal-time {
3552 flex: none;
3553 color: var(--muted);
3554 font-variant-numeric: tabular-nums;
3555}
3556
3557.cal-item-title {
3558 min-width: 0;
3559 overflow: hidden;
3560 text-overflow: ellipsis;
3561 white-space: nowrap;
3562}
3563
3564.cal-cancelled .cal-item-title,
3565.cal-cancelled .cal-row-title,
3566.cal-detail-title.cal-cancelled {
3567 text-decoration: line-through;
3568}
3569
3570.cal-more {
3571 position: relative;
3572 z-index: 1;
3573 justify-content: flex-start;
3574 margin: 1px 2px;
3575 padding: 0 6px;
3576 border: 0;
3577 background: none;
3578 color: var(--muted);
3579 font-size: 12px;
3580 line-height: 20px;
3581 white-space: nowrap;
3582 overflow: hidden;
3583 min-width: 0;
3584}
3585
3586.cal-more:hover {
3587 color: var(--text);
3588 text-decoration: underline;
3589}
3590
3591.cal-day {
3592 border-top: 1px solid var(--border);
3593}
3594
3595.cal-day-title {
3596 margin: 0;
3597 padding: 12px 14px 6px;
3598 font-size: 14px;
3599}
3600
3601.cal-day-today {
3602 color: var(--accent);
3603}
3604
3605.cal-rows {
3606 display: flex;
3607 flex-direction: column;
3608 padding-bottom: 8px;
3609}
3610
3611.cal-row {
3612 display: flex;
3613 align-items: baseline;
3614 justify-content: flex-start;
3615 gap: 10px;
3616 padding: 7px 14px;
3617 border: 0;
3618 background: none;
3619 color: var(--text);
3620 font: inherit;
3621 text-align: left;
3622 cursor: pointer;
3623 min-width: 0;
3624}
3625
3626.cal-row:hover {
3627 background: var(--bg);
3628}
3629
3630.cal-row .cal-dot {
3631 align-self: center;
3632}
3633
3634.cal-row-time {
3635 flex: none;
3636 width: 120px;
3637 font-size: 13px;
3638 color: var(--muted);
3639 font-variant-numeric: tabular-nums;
3640}
3641
3642.cal-row-main {
3643 display: flex;
3644 flex-direction: column;
3645 gap: 2px;
3646 min-width: 0;
3647}
3648
3649.cal-row-title {
3650 font-size: 14px;
3651 overflow-wrap: anywhere;
3652}
3653
3654.cal-row-meta {
3655 font-size: 12.5px;
3656 color: var(--muted);
3657 overflow-wrap: anywhere;
3658}
3659
3660.cal-agenda-day + .cal-agenda-day {
3661 border-top: 1px solid var(--border);
3662}
3663
3664.cal-inv {
3665 display: flex;
3666 align-items: center;
3667 gap: 12px;
3668 padding: 12px 14px;
3669 border-bottom: 1px solid var(--border);
3670}
3671
3672.cal-inv-main {
3673 flex: 1;
3674 min-width: 0;
3675 display: flex;
3676 flex-direction: column;
3677 align-items: flex-start;
3678 gap: 3px;
3679 padding: 0;
3680 border: 0;
3681 background: none;
3682 color: var(--text);
3683 font: inherit;
3684 text-align: left;
3685 cursor: pointer;
3686}
3687
3688.cal-inv-title {
3689 font-weight: 600;
3690 overflow-wrap: anywhere;
3691}
3692
3693.cal-inv-actions {
3694 display: flex;
3695 flex-wrap: wrap;
3696 gap: 6px;
3697}
3698
3699.cal-detail-title {
3700 overflow-wrap: anywhere;
3701}
3702
3703.cal-flag {
3704 display: inline-block;
3705 margin: 0 0 12px;
3706 padding: 2px 8px;
3707 font-size: 12px;
3708 border: 1px solid var(--danger);
3709 color: var(--danger);
3710}
3711
3712.cal-facts {
3713 display: grid;
3714 grid-template-columns: max-content minmax(0, 1fr);
3715 gap: 8px 16px;
3716 margin: 0 0 16px;
3717 font-size: 14px;
3718}
3719
3720.cal-facts dt {
3721 color: var(--muted);
3722}
3723
3724.cal-facts dd {
3725 margin: 0;
3726 overflow-wrap: anywhere;
3727}
3728
3729.cal-pre {
3730 white-space: pre-wrap;
3731}
3732
3733.cal-desc {
3734 margin-bottom: 16px;
3735 padding: 10px 12px;
3736 border: 1px solid var(--border);
3737 background: var(--bg);
3738 font-size: 14px;
3739 overflow-wrap: anywhere;
3740 max-height: 40vh;
3741 overflow-y: auto;
3742}
3743
3744.cal-attendees {
3745 list-style: none;
3746 margin: 0 0 16px;
3747 padding: 0;
3748 display: flex;
3749 flex-direction: column;
3750 gap: 6px;
3751 font-size: 14px;
3752}
3753
3754.cal-attendees li {
3755 display: flex;
3756 align-items: center;
3757 gap: 8px;
3758 min-width: 0;
3759}
3760
3761.cal-att-name {
3762 min-width: 0;
3763 overflow: hidden;
3764 text-overflow: ellipsis;
3765 white-space: nowrap;
3766}
3767
3768.ps-badge {
3769 margin-left: auto;
3770 flex: none;
3771 padding: 1px 7px;
3772 font-size: 11.5px;
3773 border: 1px solid var(--border);
3774 color: var(--muted);
3775}
3776
3777.ps-accepted {
3778 border-color: #16a34a;
3779 color: #16a34a;
3780}
3781
3782.ps-declined {
3783 border-color: var(--danger);
3784 color: var(--danger);
3785}
3786
3787.ps-tentative {
3788 border-color: #d97706;
3789 color: #d97706;
3790}
3791
3792.cal-reply {
3793 border-top: 1px solid var(--border);
3794 padding-top: 12px;
3795}
3796
3797.cal-scope {
3798 display: flex;
3799 flex-wrap: wrap;
3800 gap: 4px 16px;
3801}
3802
3803.cal-scope input[type="radio"] {
3804 width: auto;
3805 margin: 0;
3806 padding: 0;
3807 accent-color: var(--accent);
3808}
3809
3810/* ---- Contacts ---------------------------------------------------- */
3811
3812.ct {
3813 display: flex;
3814 min-height: 0;
3815 flex: 1;
3816}
3817
3818.ct-list {
3819 flex: none;
3820 width: 320px;
3821 display: flex;
3822 flex-direction: column;
3823 border-right: 1px solid var(--border);
3824 min-height: 0;
3825 /* Rows do not wrap; on a phone the list must not grow to their width. */
3826 min-width: 0;
3827}
3828
3829.ct-search {
3830 position: relative;
3831 padding: 10px;
3832 border-bottom: 1px solid var(--border);
3833}
3834
3835.ct-search input {
3836 padding-left: 34px;
3837}
3838
3839.ct-search-icon {
3840 position: absolute;
3841 left: 20px;
3842 top: 50%;
3843 width: 16px;
3844 height: 16px;
3845 transform: translateY(-50%);
3846 color: var(--muted);
3847 pointer-events: none;
3848}
3849
3850.ct-rows {
3851 display: flex;
3852 flex-direction: column;
3853 overflow-y: auto;
3854 max-height: calc(100vh - var(--topbar-h) - 120px);
3855}
3856
3857.ct-row {
3858 display: flex;
3859 align-items: center;
3860 justify-content: flex-start;
3861 gap: 10px;
3862 padding: 8px 12px;
3863 border: 0;
3864 border-bottom: 1px solid var(--border);
3865 background: none;
3866 color: var(--text);
3867 font: inherit;
3868 text-align: left;
3869 cursor: pointer;
3870 min-width: 0;
3871}
3872
3873.ct-row:hover {
3874 background: var(--bg);
3875}
3876
3877.ct-row.ct-sel {
3878 background: var(--bg);
3879 box-shadow: inset 3px 0 0 var(--accent);
3880}
3881
3882/* The contact's address book, colored like its entry in the list beside. */
3883.ct-book {
3884 flex: none;
3885 width: 10px;
3886 height: 10px;
3887 margin-left: auto;
3888 background: var(--swatch);
3889}
3890
3891.ct-avatar {
3892 flex: none;
3893 width: 36px;
3894 height: 36px;
3895 object-fit: cover;
3896 background: var(--bg);
3897}
3898
3899.ct-initials {
3900 position: relative;
3901 overflow: hidden;
3902 display: inline-flex;
3903 align-items: center;
3904 justify-content: center;
3905 border: 1px solid var(--border);
3906 font-size: 13px;
3907 font-weight: 600;
3908 color: var(--muted);
3909}
3910
3911.ct-photo {
3912 position: absolute;
3913 inset: 0;
3914 width: 100%;
3915 height: 100%;
3916 object-fit: cover;
3917 background: var(--bg);
3918}
3919
3920.ct-avatar-lg {
3921 width: 72px;
3922 height: 72px;
3923 font-size: 22px;
3924}
3925
3926.ct-row-main {
3927 display: flex;
3928 flex-direction: column;
3929 gap: 2px;
3930 min-width: 0;
3931}
3932
3933.ct-name {
3934 display: flex;
3935 align-items: center;
3936 gap: 6px;
3937 font-size: 14px;
3938 min-width: 0;
3939}
3940
3941.ct-name-text {
3942 min-width: 0;
3943 overflow: hidden;
3944 text-overflow: ellipsis;
3945 white-space: nowrap;
3946}
3947
3948.ct-meta {
3949 font-size: 12.5px;
3950 color: var(--muted);
3951 overflow: hidden;
3952 text-overflow: ellipsis;
3953 white-space: nowrap;
3954}
3955
3956.ct-detail {
3957 flex: 1;
3958 min-width: 0;
3959 padding: 16px 20px;
3960 overflow-y: auto;
3961}
3962
3963.ct-back {
3964 display: none;
3965 margin-bottom: 12px;
3966}
3967
3968.ct-head {
3969 display: flex;
3970 align-items: center;
3971 gap: 16px;
3972 margin-bottom: 18px;
3973 min-width: 0;
3974}
3975
3976.ct-head-text {
3977 min-width: 0;
3978}
3979
3980.ct-head-text p {
3981 margin: 4px 0 0;
3982 overflow-wrap: anywhere;
3983}
3984
3985.ct-title {
3986 margin: 0;
3987 font-size: 20px;
3988 overflow-wrap: anywhere;
3989}
3990
3991.ct-value {
3992 display: flex;
3993 align-items: baseline;
3994 flex-wrap: wrap;
3995 gap: 0 8px;
3996}
3997
3998.ct-value + .ct-value {
3999 margin-top: 4px;
4000}
4001
4002.ct-label {
4003 font-size: 12px;
4004}
4005
4006@media (max-width: 860px) {
4007 .ct-list {
4008 width: 260px;
4009 }
4010
4011 .cal-row-time {
4012 width: 96px;
4013 }
4014}
4015
4016@media (max-width: 640px) {
4017 .content:has(.pim-view) {
4018 padding: 12px;
4019 }
4020
4021 .cal-toolbar {
4022 padding: 8px;
4023 }
4024
4025 .cal-title {
4026 order: -1;
4027 flex-basis: 100%;
4028 }
4029
4030 .cal-views {
4031 margin-left: auto;
4032 }
4033
4034 .cal-view-btn {
4035 padding: 6px 9px;
4036 }
4037
4038 /* Phones get compact day cells with dots; the day list below the grid
4039 shows the chosen day's events. */
4040 .cal-week {
4041 grid-template-rows: 46px;
4042 }
4043
4044 .cal-item,
4045 .cal-more {
4046 display: none;
4047 }
4048
4049 .cal-dots {
4050 display: flex;
4051 }
4052
4053 .cal-cell {
4054 align-items: center;
4055 padding: 4px 2px;
4056 }
4057
4058 .cal-head-day {
4059 padding: 6px 2px;
4060 text-align: center;
4061 }
4062
4063 .cal-inv {
4064 flex-direction: column;
4065 align-items: stretch;
4066 }
4067
4068 .cal-row-time {
4069 width: 80px;
4070 }
4071
4072 .cal-facts {
4073 grid-template-columns: minmax(0, 1fr);
4074 gap: 2px;
4075 }
4076
4077 .cal-facts dd {
4078 margin-bottom: 8px;
4079 }
4080
4081 /* List and details take turns. */
4082 .ct-list {
4083 width: auto;
4084 flex: 1;
4085 border-right: 0;
4086 }
4087
4088 .ct-rows {
4089 max-height: none;
4090 }
4091
4092 .ct-detail,
4093 .ct-has-open .ct-list {
4094 display: none;
4095 }
4096
4097 .ct-has-open .ct-detail {
4098 display: block;
4099 padding: 12px;
4100 }
4101
4102 .ct-back {
4103 display: inline-flex;
4104 }
4105}
4106