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