fabrica

hanna/fabrica

64210 bytes
Raw
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 *
5 * base.css — structure only. No literal colours: every colour is a `--fb-*`
6 * custom property a theme supplies. A theme that sets only the documented tokens
7 * yields a complete UI (docs/theming.md). System font stacks; no web fonts. */
8
9:root {
10 --fb-font-sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial,
11 sans-serif;
12 --fb-font-mono: ui-monospace, "SF Mono", "JetBrains Mono", "Fira Code",
13 Menlo, Consolas, monospace;
14 --fb-radius: 6px;
15 --fb-gap: 1rem;
16 --fb-topbar-h: 3rem;
17}
18
19* {
20 box-sizing: border-box;
21}
22
23html {
24 color-scheme: var(--fb-color-scheme, dark);
25}
26
27body {
28 margin: 0;
29 font-family: var(--fb-font-sans);
30 font-size: 14px;
31 line-height: 1.5;
32 color: var(--fb-fg);
33 background: var(--fb-bg);
34 -webkit-font-smoothing: antialiased;
35}
36
37a {
38 color: var(--fb-accent);
39 text-decoration: none;
40}
41a:hover {
42 text-decoration: underline;
43}
44
45h1, h2, h3, h4 {
46 line-height: 1.25;
47 margin: 0 0 0.5em;
48}
49
50code, pre, .mono {
51 font-family: var(--fb-font-mono);
52}
53
54/* Inline icons inherit colour and scale to the current font size. */
55.icon {
56 width: 1em;
57 height: 1em;
58 flex: none;
59 vertical-align: -0.125em;
60}
61
62/* Accessibility: visible focus, skip link. */
63:focus-visible {
64 outline: 2px solid var(--fb-accent);
65 outline-offset: 2px;
66}
67.skip-link {
68 position: absolute;
69 left: -999px;
70 top: 0;
71 z-index: 100;
72 padding: 0.5rem 1rem;
73 background: var(--fb-bg-raised);
74 border: 1px solid var(--fb-border);
75}
76.skip-link:focus {
77 left: 0.5rem;
78 top: 0.5rem;
79}
80
81/* ---- Top bar ---- */
82.topbar {
83 display: flex;
84 align-items: center;
85 gap: 0.75rem;
86 height: var(--fb-topbar-h);
87 padding: 0 0.75rem;
88 border-bottom: 1px solid var(--fb-border);
89 background: var(--fb-bg-raised);
90 position: sticky;
91 top: 0;
92 z-index: 20;
93}
94.topbar .slug {
95 font-weight: 600;
96}
97.topbar .slug a {
98 color: var(--fb-fg);
99}
100.topbar .tabs {
101 display: flex;
102 gap: 0.25rem;
103 margin: 0 auto;
104 overflow-x: auto;
105}
106.topbar .tabs a {
107 padding: 0.35rem 0.7rem;
108 border-radius: var(--fb-radius);
109 color: var(--fb-fg-muted);
110 white-space: nowrap;
111}
112.topbar .tabs a:hover {
113 background: var(--fb-bg-inset);
114 text-decoration: none;
115}
116.topbar .tabs a[aria-current="page"] {
117 color: var(--fb-fg);
118 background: var(--fb-bg-inset);
119}
120
121/* The create menu and search group at the right of the top bar. Both carry
122 `margin-left: auto` so the group hugs the right edge whether or not the create
123 menu is present (when signed out it is absent, so the search keeps its place). */
124.topbar-create {
125 margin-left: auto;
126 position: relative;
127}
128.topbar-search {
129 display: flex;
130 align-items: center;
131 gap: 0.25rem;
132 flex: 0 1 28rem;
133 margin-left: auto;
134}
135/* When the create menu is present it owns the right-alignment, so the search
136 must not add a second auto margin (two would split the free space and float
137 the create menu into the middle). */
138.topbar-create ~ .topbar-search {
139 margin-left: 0;
140}
141.topbar-search input[type="search"] {
142 width: 100%;
143 padding: 0.3rem 0.6rem;
144 background: var(--fb-bg-inset);
145 border: 1px solid var(--fb-border);
146 border-radius: var(--fb-radius);
147 color: var(--fb-fg);
148}
149
150/* Create (+) popover in the top bar. */
151.topbar-create > summary {
152 list-style: none;
153 cursor: pointer;
154}
155.topbar-create > summary::-webkit-details-marker {
156 display: none;
157}
158.menu-popover {
159 position: absolute;
160 right: 0;
161 top: calc(100% + 0.35rem);
162 z-index: 30;
163 min-width: 13rem;
164 padding: 0.35rem;
165 background: var(--fb-bg-raised);
166 border: 1px solid var(--fb-border);
167 border-radius: var(--fb-radius);
168 box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);
169}
170.menu-item {
171 display: flex;
172 align-items: center;
173 gap: 0.55rem;
174 padding: 0.45rem 0.6rem;
175 border-radius: var(--fb-radius);
176 color: var(--fb-fg);
177 white-space: nowrap;
178}
179/* Multi-label dropdown on the new-issue form. */
180.new-issue-actions {
181 display: flex;
182 align-items: center;
183 gap: 0.6rem;
184 flex-wrap: wrap;
185 margin-top: 1rem;
186}
187.label-dropdown {
188 display: inline-block;
189 position: relative;
190 margin-bottom: 0.5rem;
191}
192.new-issue-actions .label-dropdown {
193 margin-bottom: 0;
194}
195.label-dropdown > summary {
196 list-style: none;
197 width: auto;
198 cursor: pointer;
199 display: inline-flex;
200 align-items: center;
201 gap: 0.4rem;
202}
203.label-dropdown-label {
204 display: inline-flex;
205 align-items: center;
206 flex-wrap: wrap;
207 gap: 0.3rem;
208}
209.label-dropdown > summary::-webkit-details-marker {
210 display: none;
211}
212.label-menu {
213 left: 0;
214 right: auto;
215 display: flex;
216 flex-direction: column;
217 gap: 0.35rem;
218 max-height: 16rem;
219 overflow-y: auto;
220}
221.label-menu .checkbox {
222 margin: 0;
223}
224.menu-item:hover {
225 background: var(--fb-bg-inset);
226 text-decoration: none;
227}
228
229.icon-btn {
230 display: inline-flex;
231 align-items: center;
232 justify-content: center;
233 width: 2rem;
234 height: 2rem;
235 padding: 0;
236 border: none;
237 border-radius: var(--fb-radius);
238 background: transparent;
239 color: var(--fb-fg);
240 cursor: pointer;
241}
242.icon-btn:hover {
243 background: var(--fb-bg-inset);
244}
245.icon-btn .icon {
246 width: 1.15rem;
247 height: 1.15rem;
248}
249
250/* ---- Drawer (off-canvas nav) ---- */
251.drawer-backdrop {
252 display: none;
253 position: fixed;
254 inset: 0;
255 background: rgba(0, 0, 0, 0.5);
256 z-index: 30;
257}
258.drawer {
259 position: fixed;
260 top: 0;
261 left: 0;
262 bottom: 0;
263 width: 16rem;
264 max-width: 85vw;
265 transform: translateX(-100%);
266 transition: transform 0.15s ease;
267 background: var(--fb-bg-raised);
268 border-right: 1px solid var(--fb-border);
269 z-index: 40;
270 padding: 1rem;
271 overflow-y: auto;
272}
273body.drawer-open .drawer {
274 transform: translateX(0);
275}
276body.drawer-open .drawer-backdrop {
277 display: block;
278}
279.drawer h2 {
280 font-size: 1rem;
281}
282.drawer nav a,
283.drawer .drawer-item {
284 display: flex;
285 align-items: center;
286 gap: 0.6rem;
287 width: 100%;
288 padding: 0.4rem 0.5rem;
289 border-radius: var(--fb-radius);
290 color: var(--fb-fg);
291 font: inherit;
292 text-align: left;
293}
294.drawer .drawer-item {
295 border: none;
296 background: none;
297 cursor: pointer;
298}
299.drawer nav a .icon,
300.drawer .drawer-item .icon {
301 color: var(--fb-fg-muted);
302}
303.drawer nav a:hover,
304.drawer .drawer-item:hover {
305 background: var(--fb-bg-inset);
306 text-decoration: none;
307}
308.drawer hr {
309 border: none;
310 border-top: 1px solid var(--fb-border);
311 margin: 0.75rem 0;
312}
313
314/* ---- Layout ---- */
315.page {
316 max-width: 76rem;
317 margin: 0 auto;
318 /* Extra breathing room below the fixed-height top bar. */
319 padding: calc(var(--fb-gap) * 1.75) var(--fb-gap) calc(var(--fb-gap) * 2);
320}
321
322.card {
323 background: var(--fb-bg-raised);
324 border: 1px solid var(--fb-border);
325 border-radius: var(--fb-radius);
326 padding: 1rem;
327}
328/* The first/last child sits against the card padding, so its own top/bottom
329 margin would double the gap — most visibly on a card holding only an
330 empty-state note ("No open issues."), which would otherwise sit off-centre. */
331.card > :first-child {
332 margin-top: 0;
333}
334.card > :last-child {
335 margin-bottom: 0;
336}
337
338.muted {
339 color: var(--fb-fg-muted);
340}
341
342/* ---- Buttons & forms ---- */
343.btn {
344 display: inline-flex;
345 align-items: center;
346 gap: 0.4rem;
347 padding: 0.4rem 0.8rem;
348 border: 1px solid var(--fb-border);
349 border-radius: var(--fb-radius);
350 background: var(--fb-bg-inset);
351 color: var(--fb-fg);
352 font: inherit;
353 cursor: pointer;
354}
355.btn:hover {
356 border-color: var(--fb-accent);
357 text-decoration: none;
358}
359.btn-primary {
360 background: var(--fb-accent);
361 border-color: var(--fb-accent);
362 color: var(--fb-accent-fg, #fff);
363}
364.btn-danger {
365 border-color: var(--fb-danger);
366 color: var(--fb-danger);
367}
368.btn-danger:hover {
369 background: var(--fb-danger);
370 color: var(--fb-accent-fg, #fff);
371 border-color: var(--fb-danger);
372}
373
374/* Repo settings: Save changes + Archive on one row. */
375.settings-actions {
376 display: flex;
377 align-items: center;
378 gap: 0.6rem;
379 margin-top: 1.25rem;
380}
381.settings-actions form {
382 display: inline;
383}
384
385/* Labels. */
386.label-chip {
387 display: inline-flex;
388 align-items: center;
389 padding: 0.1rem 0.55rem;
390 border-radius: 999px;
391 font-size: 0.85em;
392 font-weight: 600;
393 color: #fff;
394 background: var(--label-color, #888);
395 border: 1px solid rgba(0, 0, 0, 0.25);
396}
397/* Scoped label (`scope: value`): the scope sits on a neutral theme background,
398 the value in the label colour. The pill clips the two segments. */
399.label-chip-scoped {
400 padding: 0;
401 overflow: hidden;
402}
403.label-chip-scoped .label-scope,
404.label-chip-scoped .label-value {
405 padding: 0.1rem 0.55rem;
406}
407.label-chip-scoped .label-scope {
408 background: var(--fb-bg-inset);
409 color: var(--fb-fg);
410}
411.label-chip-scoped .label-value {
412 background: var(--label-color, #888);
413 color: #fff;
414}
415.label-list {
416 margin-bottom: 1.5rem;
417}
418.label-list .entry-row {
419 padding: 0.85rem 0;
420}
421.label-list .entry-row:first-child {
422 padding-top: 0;
423}
424.label-list .entry-row-body {
425 display: flex;
426 flex-direction: row;
427 align-items: center;
428 gap: 0.75rem;
429 flex-wrap: wrap;
430}
431.label-list .entry-row-meta {
432 margin: 0;
433 font-size: 0.95em;
434}
435.label-add {
436 display: flex;
437 gap: 0.5rem;
438 flex-wrap: wrap;
439 align-items: stretch;
440 padding-top: 1.25rem;
441 border-top: 1px solid var(--fb-border);
442}
443.label-add input[type="text"] {
444 flex: 1;
445 min-width: 10rem;
446}
447.label-add input[type="color"] {
448 width: 2.8rem;
449 height: 2.4rem;
450 padding: 0;
451 border: 1px solid var(--fb-border);
452 border-radius: var(--fb-radius);
453 background: var(--fb-bg-inset);
454 cursor: pointer;
455}
456.label-add input[type="color"]::-webkit-color-swatch-wrapper {
457 padding: 3px;
458}
459.label-add input[type="color"]::-webkit-color-swatch {
460 border: none;
461 border-radius: 3px;
462}
463.label-add input[type="color"]::-moz-color-swatch {
464 border: none;
465 border-radius: 3px;
466}
467.label-add button {
468 flex: none;
469}
470
471/* ---- Issues & pull requests ---- */
472.issues-head {
473 display: flex;
474 justify-content: space-between;
475 align-items: center;
476 gap: 1rem;
477 flex-wrap: wrap;
478 margin-bottom: 0.5rem;
479}
480.issues-head .subnav {
481 margin: 0;
482 justify-content: flex-start;
483}
484.state-open {
485 color: var(--fb-success);
486 display: inline-flex;
487}
488.state-closed {
489 color: var(--fb-danger);
490 display: inline-flex;
491}
492.issue-title-row {
493 display: flex;
494 align-items: center;
495 gap: 0.5rem;
496 flex-wrap: wrap;
497}
498.badge-state-open {
499 color: var(--fb-success);
500 border-color: var(--fb-success);
501}
502.badge-state-closed {
503 color: var(--fb-danger);
504 border-color: var(--fb-danger);
505}
506.badge-state-merged {
507 color: var(--fb-accent);
508 border-color: var(--fb-accent);
509}
510
511/* Pull requests: compare form, merge panel, and change list. */
512.compare-refs {
513 display: flex;
514 align-items: center;
515 gap: 0.75rem;
516 flex-wrap: wrap;
517 margin-bottom: 1rem;
518}
519.compare-refs label {
520 display: inline-flex;
521 align-items: center;
522 gap: 0.4rem;
523}
524.compare-refs select {
525 width: auto;
526}
527.compare-arrow {
528 font-size: 1.25rem;
529 color: var(--fb-muted);
530}
531.merge-panel {
532 border-left: 3px solid var(--fb-border);
533 padding: 0.85rem 1rem;
534}
535.merge-panel > p {
536 margin: 0;
537}
538.merge-panel.clean {
539 border-left-color: var(--fb-success);
540}
541.merge-panel.conflicts {
542 border-left-color: var(--fb-danger);
543}
544.merge-panel.merged {
545 border-left-color: var(--fb-accent);
546}
547.merge-form {
548 display: flex;
549 align-items: center;
550 gap: 0.5rem;
551 flex-wrap: wrap;
552 margin-top: 0.65rem;
553}
554.merge-form label {
555 margin: 0;
556}
557.merge-form select {
558 width: auto;
559}
560.pr-changes {
561 margin-top: 1.5rem;
562}
563.pr-changes h2 {
564 margin-bottom: 0.85rem;
565}
566.entry-list.pr-commits {
567 margin-bottom: 1.25rem;
568}
569.issue-header {
570 margin-bottom: 1rem;
571 padding-bottom: 0.75rem;
572 border-bottom: 1px solid var(--fb-border);
573}
574.issue-header h1 {
575 margin: 0;
576}
577.issue-title-line {
578 display: flex;
579 align-items: center;
580 gap: 0.5rem;
581 margin-bottom: 0.4rem;
582}
583.issue-edit > summary,
584.comment-edit > summary {
585 cursor: pointer;
586 list-style: none;
587}
588.issue-edit > summary::-webkit-details-marker,
589.comment-edit > summary::-webkit-details-marker {
590 display: none;
591}
592/* Edit / Cancel label swap on the toggle. */
593.issue-edit:not([open]) .edit-close,
594.comment-edit:not([open]) .edit-close {
595 display: none;
596}
597.issue-edit[open] .edit-open,
598.comment-edit[open] .edit-open {
599 display: none;
600}
601/* When closed, the Edit toggle sits at the right; the editor bits are hidden. */
602.issue-edit {
603 margin-left: auto;
604}
605.title-edit-form,
606.title-save {
607 display: none;
608}
609/* Title editor (GitHub-style): a full-width input, then Cancel (the toggle) and
610 Save. The three are siblings of the title, revealed via :has. */
611.issue-title-line:has(.issue-edit[open]) h1 {
612 display: none;
613}
614.issue-title-line:has(.issue-edit[open]) .issue-edit {
615 order: 2;
616 margin-left: 0;
617}
618.issue-title-line:has(.issue-edit[open]) .title-edit-form {
619 display: block;
620 order: 1;
621 flex: 1;
622}
623.issue-title-line:has(.issue-edit[open]) .title-save {
624 display: inline-flex;
625 order: 3;
626}
627.title-edit-form input[type="text"] {
628 width: 100%;
629}
630/* Comment editor: the toggle sits in the header; when open the rendered body is
631 replaced by the textarea. */
632.comment-edit {
633 margin-left: auto;
634}
635.comment-edit > summary {
636 font-size: 0.9em;
637 color: var(--fb-accent);
638}
639.comment-editor {
640 display: none;
641 padding: 0.85rem;
642}
643.comment:has(.comment-edit[open]) .comment-editor {
644 display: block;
645}
646.comment:has(.comment-edit[open]) .comment-body {
647 display: none;
648}
649.comment-editor textarea {
650 width: 100%;
651 font-family: inherit;
652}
653.comment-editor-actions {
654 display: flex;
655 justify-content: flex-end;
656 margin-top: 0.5rem;
657}
658.issue-sub {
659 display: flex;
660 align-items: center;
661 gap: 0.5rem;
662}
663.issue-main {
664 display: flex;
665 gap: 1.5rem;
666 align-items: flex-start;
667}
668.issue-thread {
669 flex: 1;
670 min-width: 0;
671 display: flex;
672 flex-direction: column;
673 gap: 1rem;
674}
675.issue-side {
676 flex: none;
677 width: 16rem;
678}
679.issue-side section {
680 margin-bottom: 1.25rem;
681 padding-bottom: 1rem;
682 border-bottom: 1px solid var(--fb-border);
683}
684.issue-side section:last-child {
685 margin-bottom: 0;
686 padding-bottom: 0;
687 border-bottom: none;
688}
689.issue-side h3 {
690 font-size: 0.9rem;
691 margin-bottom: 0.5rem;
692}
693.comment {
694 padding: 0;
695 overflow: hidden;
696}
697.comment-head {
698 display: flex;
699 align-items: center;
700 flex-wrap: wrap;
701 gap: 0.35rem;
702 padding: 0.5rem 0.85rem;
703 border-bottom: 1px solid var(--fb-border);
704 font-size: 0.9em;
705}
706.comment-body {
707 padding: 0.85rem;
708}
709.comment-form textarea {
710 margin-bottom: 0.5rem;
711}
712.comment-actions {
713 display: flex;
714 justify-content: flex-end;
715 gap: 0.5rem;
716 align-items: center;
717}
718.inline-form {
719 display: inline;
720}
721.side-form {
722 margin-top: 0.5rem;
723 display: flex;
724 gap: 0.4rem;
725 align-items: stretch;
726 flex-wrap: wrap;
727}
728.side-form select {
729 flex: 1;
730}
731.label-set {
732 display: flex;
733 flex-wrap: wrap;
734 gap: 0.35rem;
735}
736/* Existing-issue label editor: the dropdown fills the sidebar width, its toggle
737 shows the applied labels as chips, and Apply sits at the foot of the popover
738 (the column popover stretches it full width). */
739.side-form .label-dropdown {
740 flex: 1;
741 margin-bottom: 0;
742}
743.side-form .label-dropdown > summary {
744 width: 100%;
745 justify-content: space-between;
746}
747.label-apply {
748 margin-top: 0.5rem;
749}
750.label-swatch {
751 width: 0.8rem;
752 height: 0.8rem;
753 border-radius: 3px;
754 background: var(--label-color, #888);
755 display: inline-block;
756 flex: none;
757}
758
759/* Repo settings danger zone. */
760.danger-zone {
761 border-color: var(--fb-danger);
762}
763.danger-zone p {
764 margin-top: 0;
765}
766/* When the card holds only an explanatory paragraph (no delete form), drop the
767 trailing margin so the card doesn't carry extra bottom padding. */
768.danger-zone p:last-child {
769 margin-bottom: 0;
770}
771
772/* Collaborators: a bordered list inside the card, then an add row. Each row is
773 [avatar] [name] [role] on the left, remove button on the right. */
774.collab-list {
775 margin-bottom: 1rem;
776}
777.collab-list .entry-row {
778 padding-left: 0;
779 padding-right: 0;
780}
781/* The first row abuts the card padding; drop its own top padding so the list
782 doesn't sit with a doubled gap at the top of the card. */
783.collab-list .entry-row:first-child {
784 padding-top: 0;
785}
786.collab-ident {
787 display: flex;
788 align-items: center;
789 gap: 0.6rem;
790 min-width: 0;
791 /* Tight line boxes so the name and the role pill centre on each other. */
792 line-height: 1;
793}
794.collab-ident .entry-row-title {
795 display: inline;
796}
797.collab-perm {
798 font-size: 0.8em;
799 color: var(--fb-fg-muted);
800 border: 1px solid var(--fb-border);
801 border-radius: 999px;
802 padding: 0.05rem 0.55rem;
803 text-transform: capitalize;
804}
805.collab-list .entry-row-actions .btn {
806 padding: 0.25rem 0.6rem;
807 font-size: 0.85em;
808}
809.collab-add {
810 display: flex;
811 gap: 0.5rem;
812 flex-wrap: wrap;
813 align-items: stretch;
814}
815.collab-add input[type="text"] {
816 flex: 1;
817 min-width: 12rem;
818}
819.collab-add select {
820 width: auto;
821}
822.collab-add button {
823 flex: none;
824}
825
826/* Repository language breakdown bar (a collapsible <details>). */
827.lang-bar-wrap {
828 margin: 0 0 1rem;
829}
830.lang-bar-summary {
831 /* `display: block` drops the default disclosure triangle (and the indent it
832 reserves) in Chromium; the list-style/marker resets cover other engines. */
833 display: block;
834 list-style: none;
835 cursor: pointer;
836}
837.lang-bar-summary::-webkit-details-marker {
838 display: none;
839}
840.lang-bar-summary::marker {
841 content: "";
842}
843.lang-bar {
844 display: flex;
845 height: 0.6rem;
846 border-radius: 999px;
847 overflow: hidden;
848 background: var(--fb-bg-inset);
849}
850.lang-seg {
851 height: 100%;
852}
853.lang-seg:not(:last-child) {
854 margin-right: 2px;
855}
856.lang-legend {
857 list-style: none;
858 padding: 0;
859 display: flex;
860 flex-wrap: wrap;
861 gap: 0.35rem 1.1rem;
862 margin: 0.75rem 0 0;
863}
864.lang-legend li {
865 display: flex;
866 align-items: center;
867 gap: 0.4rem;
868 font-size: 0.9em;
869}
870.lang-dot {
871 width: 0.7rem;
872 height: 0.7rem;
873 border-radius: 50%;
874 flex: none;
875}
876.lang-legend .lang-name {
877 font-weight: 600;
878}
879
880/* License summary banner shown above a LICENSE file. */
881.license-banner {
882 margin-bottom: 1rem;
883}
884.license-head {
885 display: flex;
886 align-items: center;
887 gap: 0.75rem;
888}
889.license-head > .icon {
890 width: 2rem;
891 height: 2rem;
892 flex: none;
893 color: var(--fb-fg-muted);
894}
895.license-head h3 {
896 margin: 0;
897}
898.license-eyebrow {
899 margin: 0;
900 font-size: 0.85em;
901}
902.license-desc {
903 margin: 0.75rem 0;
904}
905.license-cols {
906 display: grid;
907 grid-template-columns: repeat(3, 1fr);
908 gap: 1rem;
909 padding-top: 0.75rem;
910 border-top: 1px solid var(--fb-border);
911}
912.license-col h4 {
913 margin: 0 0 0.5rem;
914 font-size: 0.9rem;
915}
916.license-col ul {
917 list-style: none;
918 display: flex;
919 flex-direction: column;
920 gap: 0.3rem;
921}
922.license-col li {
923 display: flex;
924 align-items: center;
925 gap: 0.4rem;
926}
927.license-col li .icon {
928 width: 0.95rem;
929 height: 0.95rem;
930 flex: none;
931}
932.license-col li.ok .icon {
933 color: var(--fb-success);
934}
935.license-col li.no .icon {
936 color: var(--fb-danger);
937}
938.license-col li.cond .icon {
939 color: var(--fb-accent);
940}
941.license-note {
942 margin: 0.75rem 0 0;
943 font-size: 0.82em;
944}
945@media (max-width: 40rem) {
946 .license-cols {
947 grid-template-columns: 1fr;
948 }
949}
950
951/* Issue/PR dependency lists in the sidebar. */
952.dep-list {
953 list-style: none;
954 padding: 0;
955 display: flex;
956 flex-direction: column;
957 gap: 0.25rem;
958 margin: 0 0 0.75rem;
959}
960.dep-row {
961 display: flex;
962 align-items: center;
963 gap: 0.45rem;
964 font-size: 0.9em;
965 padding: 0.3rem 0.5rem;
966 border-radius: var(--fb-radius);
967}
968.dep-row:hover {
969 background: var(--fb-bg-inset);
970}
971.dep-row > .icon {
972 flex: none;
973}
974.dep-row > a {
975 flex: 1;
976 min-width: 0;
977 color: var(--fb-fg);
978 overflow: hidden;
979 text-overflow: ellipsis;
980 white-space: nowrap;
981}
982.dep-row > a:hover {
983 text-decoration: none;
984 color: var(--fb-accent);
985}
986.dep-row .icon-btn {
987 width: 1.4rem;
988 height: 1.4rem;
989 flex: none;
990 color: var(--fb-fg-muted);
991}
992.dep-row .icon-btn:hover {
993 color: var(--fb-danger);
994}
995.dep-add {
996 display: flex;
997 gap: 0.4rem;
998 margin-top: 0.25rem;
999}
1000.dep-add input {
1001 flex: 1;
1002 min-width: 0;
1003}
1004
1005/* Pagination controls for long lists. */
1006.pagination {
1007 display: flex;
1008 align-items: center;
1009 gap: 0.75rem;
1010 margin-top: 1rem;
1011 flex-wrap: wrap;
1012}
1013.pagination .btn.disabled {
1014 opacity: 0.5;
1015 pointer-events: none;
1016}
1017.pagination .per-page {
1018 display: flex;
1019 align-items: center;
1020 gap: 0.4rem;
1021}
1022.pagination .per-page label {
1023 display: flex;
1024 align-items: center;
1025 gap: 0.4rem;
1026 margin: 0;
1027 font-weight: 400;
1028 color: var(--fb-fg-muted);
1029}
1030.pagination .per-page select {
1031 width: auto;
1032}
1033.pagination-page {
1034 margin-left: auto;
1035}
1036
1037/* Admin dashboard: stat grid and management lists. */
1038.stat-grid {
1039 display: grid;
1040 grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
1041 gap: 0.75rem;
1042}
1043.stat-card {
1044 padding: 1rem;
1045 background: var(--fb-bg-raised);
1046 border: 1px solid var(--fb-border);
1047 border-radius: var(--fb-radius);
1048}
1049.stat-value {
1050 font-size: 1.6rem;
1051 font-weight: 600;
1052}
1053.stat-label {
1054 font-size: 0.85em;
1055}
1056.admin-actions {
1057 flex-wrap: wrap;
1058 gap: 0.35rem;
1059}
1060/* The card already pads its edges, so admin list rows drop their own side
1061 padding and align with the create form below them. */
1062.admin-list .entry-row {
1063 padding-left: 0;
1064 padding-right: 0;
1065}
1066/* Separate a populated list from the create form directly beneath it. */
1067.admin-list:not(:empty) + form {
1068 margin-top: 1rem;
1069}
1070.admin-form-row {
1071 display: flex;
1072 align-items: center;
1073 gap: 0.5rem;
1074 flex-wrap: wrap;
1075 margin-bottom: 0.5rem;
1076}
1077.admin-form-row input,
1078.admin-form-row select {
1079 flex: 1;
1080 min-width: 9rem;
1081}
1082/* Give every control the same explicit height so inputs, selects, and the
1083 inline submit button line up exactly (native selects render taller than a
1084 button would otherwise, so flex `stretch` alone is not enough). */
1085.admin-form-row > input,
1086.admin-form-row > select,
1087.admin-form-row > button {
1088 height: 2.4rem;
1089}
1090/* A control row that is the last thing in its form carries no trailing gap. */
1091.admin-form-row-last {
1092 margin-bottom: 0;
1093}
1094/* A bottom row pairing option toggles/checkboxes with the submit button. */
1095.admin-form-actions {
1096 display: flex;
1097 align-items: center;
1098 flex-wrap: wrap;
1099 gap: 0.75rem;
1100 margin-top: 0.5rem;
1101}
1102.admin-form-actions label.checkbox {
1103 margin: 0;
1104}
1105/* Stacked label + control groups (admin settings). */
1106.form-field {
1107 margin-bottom: 0.9rem;
1108}
1109.form-field label {
1110 margin-top: 0;
1111}
1112/* Empty-state line above a create form: keep it snug against the form. */
1113.empty-note {
1114 margin: 0 0 0.75rem;
1115}
1116/* Mirror settings: the last-error text on a configured mirror. */
1117.mirror-error {
1118 color: var(--fb-danger);
1119}
1120/* Separate the "Add a push mirror" sub-form from the list of configured mirrors. */
1121.mirror-add {
1122 margin-top: 1.25rem;
1123 padding-top: 1.25rem;
1124 border-top: 1px solid var(--fb-border);
1125}
1126.mirror-add h3 {
1127 margin-top: 0;
1128}
1129.mirror-add .admin-form-actions {
1130 margin-top: 1rem;
1131}
1132
1133/* Settings: a left tab rail and the active tab's content. */
1134.settings-layout {
1135 display: flex;
1136 gap: 2rem;
1137 align-items: flex-start;
1138}
1139.settings-nav {
1140 flex: none;
1141 width: 13rem;
1142 position: sticky;
1143 top: calc(var(--fb-topbar-h) + 1rem);
1144}
1145.settings-nav h1 {
1146 font-size: 1.4rem;
1147 margin-bottom: 0.75rem;
1148}
1149.settings-nav nav {
1150 display: flex;
1151 flex-direction: column;
1152 gap: 0.15rem;
1153}
1154.settings-nav nav a {
1155 padding: 0.4rem 0.7rem;
1156 border-radius: var(--fb-radius);
1157 color: var(--fb-fg-muted);
1158}
1159.settings-nav nav a:hover {
1160 background: var(--fb-bg-inset);
1161 text-decoration: none;
1162}
1163.settings-nav nav a[aria-current="page"] {
1164 background: var(--fb-bg-inset);
1165 color: var(--fb-fg);
1166 font-weight: 600;
1167}
1168.settings-content {
1169 flex: 1;
1170 min-width: 0;
1171}
1172.settings-content .listing:first-child {
1173 margin-top: 0;
1174}
1175.email-list {
1176 margin-bottom: 0;
1177}
1178.badge-verified {
1179 color: var(--fb-success);
1180 border-color: var(--fb-success);
1181}
1182.badge-unverified {
1183 color: var(--fb-fg-muted);
1184 border-color: var(--fb-border);
1185}
1186.email-visibility {
1187 margin: 0;
1188 padding: 0.85rem 0;
1189 border-top: 1px solid var(--fb-border);
1190 border-bottom: 1px solid var(--fb-border);
1191}
1192.email-visibility label.checkbox {
1193 margin: 0;
1194}
1195.email-add {
1196 margin-top: 1rem;
1197}
1198.email-add label {
1199 margin-top: 0;
1200}
1201.email-add-row {
1202 display: flex;
1203 gap: 0.5rem;
1204 align-items: stretch;
1205}
1206.email-add-row input {
1207 flex: 1;
1208 min-width: 12rem;
1209}
1210@media (max-width: 48rem) {
1211 /* Stack the rail above the content. align-items must reset to stretch or the
1212 content column shrinks to its widest child and overflows the viewport. */
1213 .settings-layout {
1214 flex-direction: column;
1215 align-items: stretch;
1216 gap: 1rem;
1217 }
1218 .settings-content {
1219 width: 100%;
1220 }
1221 .settings-nav {
1222 width: 100%;
1223 position: static;
1224 }
1225 /* The tab rail scrolls horizontally (like the repo tabs) instead of wrapping
1226 onto several rows. */
1227 .settings-nav nav {
1228 flex-direction: row;
1229 flex-wrap: nowrap;
1230 overflow-x: auto;
1231 -webkit-overflow-scrolling: touch;
1232 }
1233 .settings-nav nav a {
1234 flex: 0 0 auto;
1235 }
1236 /* Stat cards fill the width in as many columns as fit. */
1237 .stat-grid {
1238 grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
1239 }
1240}
1241
1242label {
1243 display: block;
1244 margin: 0.75rem 0 0.25rem;
1245 font-weight: 600;
1246}
1247/* The first label in a card form abuts the card padding — drop its top margin so
1248 the form doesn't sit with a doubled gap at the top. The CSRF hidden input is
1249 usually the real first child, so also match the label right after it. */
1250.card form > label:first-child,
1251.card form > input[type="hidden"]:first-child + label {
1252 margin-top: 0;
1253}
1254input[type="text"],
1255input[type="password"],
1256input[type="email"],
1257input[type="search"],
1258input[type="url"],
1259input[type="number"],
1260textarea,
1261select {
1262 width: 100%;
1263 padding: 0.5rem 0.75rem;
1264 font: inherit;
1265 color: var(--fb-fg);
1266 background: var(--fb-bg-inset);
1267 border: 1px solid var(--fb-border);
1268 border-radius: var(--fb-radius);
1269}
1270/* Replace the native select arrow (which ignores padding and hugs the border)
1271 with a token-coloured caret drawn from two gradients — no literal colours,
1272 and given breathing room from the edge. */
1273select {
1274 appearance: none;
1275 -webkit-appearance: none;
1276 padding-right: 2rem;
1277 background-image:
1278 linear-gradient(45deg, transparent 50%, var(--fb-fg-muted) 50%),
1279 linear-gradient(135deg, var(--fb-fg-muted) 50%, transparent 50%);
1280 background-position: right 1.05rem center, right 0.7rem center;
1281 background-size: 0.35rem 0.35rem;
1282 background-repeat: no-repeat;
1283}
1284select:disabled {
1285 background-image: none;
1286}
1287input:disabled,
1288textarea:disabled,
1289select:disabled {
1290 opacity: 0.6;
1291 cursor: not-allowed;
1292}
1293input:focus,
1294textarea:focus,
1295select:focus {
1296 border-color: var(--fb-accent);
1297 outline: none;
1298}
1299
1300/* Submit buttons in a card form get breathing room from the last field —
1301 except buttons that sit inline in a control row (they must not be nudged
1302 down out of alignment with their input). */
1303.card form button[type="submit"]:not(.inline-btn) {
1304 margin-top: 1.1rem;
1305}
1306.field-hint {
1307 margin: 0.25rem 0 0.5rem;
1308 font-size: 0.85em;
1309}
1310
1311/* API token settings. */
1312.token-secret {
1313 margin: 0.5rem 0 0;
1314 padding: 0.6rem 0.85rem;
1315 overflow-x: auto;
1316 background: var(--fb-bg-inset);
1317 border-radius: var(--fb-radius);
1318}
1319.token-list {
1320 margin-bottom: 1rem;
1321}
1322.key-list {
1323 margin-bottom: 1.25rem;
1324}
1325.key-list .entry-row {
1326 align-items: center;
1327}
1328.key-list .entry-row-title {
1329 display: flex;
1330 align-items: center;
1331 gap: 0.5rem;
1332}
1333.key-list .entry-row-meta {
1334 margin-top: 0.2rem;
1335 font-size: 0.85em;
1336}
1337.key-verify {
1338 margin-top: 0.5rem;
1339}
1340.key-verify > summary {
1341 cursor: pointer;
1342 color: var(--fb-accent);
1343 font-size: 0.9em;
1344}
1345.key-verify textarea {
1346 font-family: var(--fb-font-mono);
1347 font-size: 0.85em;
1348}
1349.key-challenge {
1350 padding: 0.6rem 0.75rem;
1351 background: var(--fb-bg-inset);
1352 border-radius: var(--fb-radius);
1353 overflow-x: auto;
1354 white-space: pre-wrap;
1355}
1356.key-add {
1357 padding-top: 1rem;
1358 border-top: 1px solid var(--fb-border);
1359}
1360.key-add textarea {
1361 font-family: var(--fb-font-mono);
1362 font-size: 0.88em;
1363}
1364.key-add-row {
1365 display: flex;
1366 align-items: center;
1367 gap: 0.6rem;
1368 flex-wrap: wrap;
1369 margin-bottom: 0.6rem;
1370}
1371.key-add-row label {
1372 margin: 0;
1373}
1374.key-add-row select {
1375 width: auto;
1376}
1377.key-add-row input {
1378 flex: 1;
1379 min-width: 12rem;
1380}
1381.token-scopes {
1382 margin: 0.75rem 0 0;
1383 padding: 0.5rem 0.85rem;
1384 border: 1px solid var(--fb-border);
1385 border-radius: var(--fb-radius);
1386}
1387.token-scopes legend {
1388 padding: 0 0.35rem;
1389 font-size: 0.9em;
1390 font-weight: 600;
1391}
1392input[type="checkbox"],
1393input[type="radio"] {
1394 accent-color: var(--fb-accent);
1395 width: 1.05rem;
1396 height: 1.05rem;
1397 cursor: pointer;
1398}
1399label.checkbox {
1400 display: inline-flex;
1401 align-items: center;
1402 gap: 0.45rem;
1403 margin: 0 1rem 0 0;
1404 font-weight: 400;
1405 cursor: pointer;
1406}
1407label.checkbox input {
1408 width: 1.05rem;
1409 flex: none;
1410}
1411/* Link slots stack full-width; the Add button sits beside the last one. */
1412.link-fields {
1413 display: flex;
1414 flex-wrap: wrap;
1415 align-items: stretch;
1416 gap: 0.5rem;
1417}
1418.link-slot {
1419 flex: 1 1 100%;
1420}
1421/* Concrete basis (not auto) so the global input width:100% doesn't claim the
1422 whole row and wrap the Add button below. */
1423.link-slot.last-visible {
1424 flex: 1 1 12rem;
1425}
1426.link-add {
1427 flex: 0 0 auto;
1428}
1429
1430/* Inline form rows (links, avatar upload, collaborators): every control gets
1431 the same explicit height so inputs, selects, and buttons line up exactly. */
1432.collab-add > input,
1433.collab-add > select,
1434.collab-add > button,
1435.avatar-upload-row > input[type="file"],
1436.avatar-upload-row > button,
1437.link-slot,
1438.link-add {
1439 height: 2.4rem;
1440}
1441.avatar-upload-row input[type="file"] {
1442 padding-top: 0;
1443 padding-bottom: 0;
1444}
1445/* Without JS every slot shows; the enhancement hides empties behind Add. */
1446[hidden] {
1447 display: none !important;
1448}
1449
1450/* New-repository form: a comfortably narrow, centered column. */
1451.new-repo {
1452 max-width: 40rem;
1453 margin: 0 auto;
1454}
1455
1456/* Centered auth card (sign in, invite activation). */
1457.auth-wrap {
1458 display: flex;
1459 flex-direction: column;
1460 align-items: center;
1461 justify-content: center;
1462 min-height: calc(100vh - var(--fb-topbar-h) - 4rem);
1463 padding: 1rem 0;
1464}
1465.auth-card {
1466 width: 100%;
1467 max-width: 22rem;
1468 background: var(--fb-bg-raised);
1469 border: 1px solid var(--fb-border);
1470 border-radius: var(--fb-radius);
1471 padding: 1.75rem;
1472}
1473.auth-card h1 {
1474 text-align: center;
1475 margin-bottom: 1rem;
1476}
1477.auth-card form .btn {
1478 width: 100%;
1479 justify-content: center;
1480 margin-top: 1.25rem;
1481}
1482.auth-alt {
1483 margin: 1rem 0 0;
1484 text-align: center;
1485 font-size: 0.9em;
1486}
1487
1488/* ---- Flash / notices ---- */
1489.notice {
1490 padding: 0.6rem 0.8rem;
1491 border-radius: var(--fb-radius);
1492 border: 1px solid var(--fb-border);
1493 background: var(--fb-bg-inset);
1494 margin-bottom: 0.75rem;
1495}
1496.notice-error {
1497 border-color: var(--fb-danger);
1498}
1499.notice-success {
1500 border-color: var(--fb-success);
1501}
1502
1503/* ---- Badges ---- */
1504.badge {
1505 display: inline-flex;
1506 align-items: center;
1507 gap: 0.25rem;
1508 padding: 0.05rem 0.45rem;
1509 font-size: 0.8em;
1510 border-radius: var(--fb-radius);
1511 border: 1px solid var(--fb-border);
1512}
1513.badge-verified {
1514 color: var(--fb-success);
1515 border-color: var(--fb-success);
1516}
1517.badge-unverified {
1518 color: var(--fb-warning);
1519 border-color: var(--fb-warning);
1520}
1521.badge-private {
1522 color: var(--fb-fg-muted);
1523 /* An explicit muted border: Carbon's --fb-border equals --fb-bg-inset, so the
1524 default badge border vanishes against a hovered inset row. */
1525 border-color: var(--fb-fg-muted);
1526}
1527.badge-default {
1528 color: var(--fb-accent);
1529 border-color: var(--fb-accent);
1530}
1531.badge-archived {
1532 color: var(--fb-warning);
1533 border-color: var(--fb-warning);
1534}
1535/* Sign-up invite status: unused is neutral; used (redeemed) reads as done. */
1536.badge-unused {
1537 color: var(--fb-fg-muted);
1538 border-color: var(--fb-fg-muted);
1539}
1540.badge-used {
1541 color: var(--fb-success);
1542 border-color: var(--fb-success);
1543}
1544
1545/* Inline search row: input grows, button sits to its right with margin. */
1546.search-row {
1547 display: flex;
1548 align-items: stretch;
1549 gap: 0.6rem;
1550 margin: 0.75rem 0 1.25rem;
1551}
1552.search-row input[type="search"] {
1553 flex: 1;
1554}
1555.search-row .btn {
1556 flex: none;
1557}
1558
1559/* ---- Repo/list rows ---- */
1560.listing {
1561 margin-bottom: 1.5rem;
1562}
1563.repo-list {
1564 list-style: none;
1565 margin: 0;
1566 padding: 0;
1567}
1568.repo-list li {
1569 padding: 0.75rem 0;
1570 border-bottom: 1px solid var(--fb-border);
1571}
1572.repo-list .name {
1573 font-weight: 600;
1574 font-size: 1.05em;
1575}
1576
1577/* Listing card: bordered rows inside a single card. */
1578.repo-list.card {
1579 padding: 0;
1580 overflow: hidden;
1581}
1582.repo-list.card li {
1583 padding: 0;
1584 border-bottom: 1px solid var(--fb-border);
1585}
1586.repo-list.card li:last-child {
1587 border-bottom: none;
1588}
1589.repo-row {
1590 display: flex;
1591 flex-direction: column;
1592 gap: 0.15rem;
1593 padding: 0.75rem 1rem;
1594 color: var(--fb-fg);
1595}
1596.repo-row:hover {
1597 background: var(--fb-bg-inset);
1598 text-decoration: none;
1599}
1600.repo-row-name {
1601 display: inline-flex;
1602 align-items: center;
1603 gap: 0.45rem;
1604 font-weight: 600;
1605 max-width: 100%;
1606}
1607/* Truncate a long repo/group path so it never pushes past the card width; the
1608 trailing visibility/archived badges stay put (they are not the name span). */
1609.repo-row-name > span:not(.badge) {
1610 overflow: hidden;
1611 text-overflow: ellipsis;
1612 white-space: nowrap;
1613 min-width: 0;
1614}
1615.repo-row-name > .icon,
1616.repo-row-name > .badge {
1617 flex: none;
1618}
1619.repo-row-name .icon {
1620 color: var(--fb-fg-muted);
1621}
1622.repo-row-desc {
1623 font-size: 0.9em;
1624}
1625.repo-row-meta {
1626 font-size: 0.82em;
1627}
1628
1629/* User directory rows (Explore → Users). */
1630.user-row {
1631 display: flex;
1632 align-items: center;
1633 gap: 0.75rem;
1634 padding: 0.75rem 1rem;
1635 color: var(--fb-fg);
1636}
1637.user-row:hover {
1638 background: var(--fb-bg-inset);
1639 text-decoration: none;
1640}
1641.user-row .avatar {
1642 width: 2.5rem;
1643 height: 2.5rem;
1644 flex: none;
1645}
1646.user-row-main {
1647 display: flex;
1648 flex-direction: column;
1649 min-width: 0;
1650}
1651.user-row-name {
1652 font-weight: 600;
1653}
1654.user-row-meta {
1655 font-size: 0.85em;
1656}
1657
1658/* ---- Dashboard (activity + repo sidebar) ---- */
1659.dashboard {
1660 display: flex;
1661 gap: 1.5rem;
1662 align-items: flex-start;
1663}
1664.dashboard-main {
1665 flex: 1;
1666 min-width: 0;
1667}
1668.dashboard-side {
1669 flex: none;
1670 width: 20rem;
1671}
1672.dashboard-side .search-row {
1673 margin-top: 0;
1674}
1675
1676/* Contribution heatmap. */
1677.heatmap-wrap {
1678 overflow-x: auto;
1679 padding-bottom: 0.25rem;
1680}
1681.heatmap {
1682 display: block;
1683 width: 100%;
1684 height: auto;
1685}
1686.hm-label {
1687 fill: var(--fb-fg-muted);
1688 font-size: 9px;
1689 font-family: var(--fb-font-sans);
1690}
1691.hm-cell {
1692 fill: var(--fb-bg-inset);
1693}
1694.hm-cell[data-level="1"] {
1695 fill: color-mix(in srgb, var(--fb-accent) 30%, var(--fb-bg-inset));
1696}
1697.hm-cell[data-level="2"] {
1698 fill: color-mix(in srgb, var(--fb-accent) 55%, var(--fb-bg-inset));
1699}
1700.hm-cell[data-level="3"] {
1701 fill: color-mix(in srgb, var(--fb-accent) 78%, var(--fb-bg-inset));
1702}
1703.hm-cell[data-level="4"] {
1704 fill: var(--fb-accent);
1705}
1706.heatmap-caption {
1707 margin: 0.25rem 0 1.5rem;
1708 font-size: 0.85em;
1709}
1710
1711/* Recent-activity feed. */
1712.activity-feed {
1713 list-style: none;
1714 margin: 0;
1715 padding: 0;
1716}
1717.activity-item {
1718 padding: 0.75rem 1rem;
1719 border-bottom: 1px solid var(--fb-border);
1720}
1721.activity-item:last-child {
1722 border-bottom: none;
1723}
1724.activity-head {
1725 display: flex;
1726 align-items: center;
1727 gap: 0.4rem;
1728 flex-wrap: wrap;
1729 font-size: 0.9em;
1730}
1731.activity-time {
1732 margin-left: auto;
1733}
1734.activity-commit {
1735 display: flex;
1736 gap: 0.6rem;
1737 margin-top: 0.35rem;
1738 align-items: baseline;
1739 color: var(--fb-fg);
1740}
1741.activity-commit:hover {
1742 color: var(--fb-accent);
1743 text-decoration: none;
1744}
1745.commit-sha {
1746 flex: none;
1747 color: var(--fb-fg-muted);
1748 font-size: 0.85em;
1749}
1750.commit-summary {
1751 min-width: 0;
1752 overflow: hidden;
1753 text-overflow: ellipsis;
1754 white-space: nowrap;
1755}
1756
1757/* ---- Avatars & profile header ---- */
1758.avatar {
1759 border-radius: 6px;
1760 object-fit: cover;
1761 background: var(--fb-bg-inset);
1762 border: 1px solid var(--fb-border);
1763}
1764.avatar-sm {
1765 width: 1.75rem;
1766 height: 1.75rem;
1767 border-radius: 5px;
1768}
1769.avatar-lg {
1770 width: 5rem;
1771 height: 5rem;
1772 border-radius: 8px;
1773}
1774.avatar-xl {
1775 width: 100%;
1776 max-width: 16rem;
1777 aspect-ratio: 1;
1778 height: auto;
1779 border-radius: 10px;
1780}
1781/* Two-column profile: info sidebar on the left, repositories on the right. */
1782.profile-layout {
1783 display: flex;
1784 gap: 1.5rem;
1785 align-items: flex-start;
1786}
1787.profile-sidebar {
1788 flex: none;
1789 width: 19rem;
1790}
1791.profile-main {
1792 flex: 1;
1793 min-width: 0;
1794}
1795.profile-card {
1796 display: flex;
1797 flex-direction: column;
1798 gap: 0.5rem;
1799}
1800.profile-card .avatar-xl {
1801 align-self: center;
1802}
1803.profile-name {
1804 margin: 0.5rem 0 0;
1805 font-size: 1.5rem;
1806 text-align: center;
1807}
1808.profile-handle {
1809 margin: 0;
1810 text-align: center;
1811}
1812/* Follower/following counts and the follow button in the profile sidebar. */
1813.profile-follow {
1814 display: flex;
1815 align-items: center;
1816 justify-content: center;
1817 gap: 0.35rem;
1818 margin: 0.5rem 0 0;
1819 font-size: 0.9em;
1820}
1821.profile-follow a {
1822 color: inherit;
1823}
1824.profile-follow a:hover strong {
1825 color: var(--fb-accent);
1826}
1827.profile-follow-btn {
1828 display: block;
1829 text-align: center;
1830 margin: 0.75rem 0 0.25rem;
1831}
1832/* A list of users (followers/following) as follow-cards. */
1833.user-list {
1834 list-style: none;
1835 margin: 0;
1836 padding: 0;
1837}
1838.user-row {
1839 display: flex;
1840 align-items: flex-start;
1841 gap: 0.75rem;
1842 padding: 0.85rem 1rem;
1843 border-bottom: 1px solid var(--fb-border);
1844}
1845.user-row:last-child {
1846 border-bottom: none;
1847}
1848.user-row .avatar {
1849 width: 3rem;
1850 height: 3rem;
1851 flex: none;
1852}
1853.user-row-body {
1854 flex: 1;
1855 min-width: 0;
1856}
1857.user-row-name {
1858 font-weight: 600;
1859 color: var(--fb-fg);
1860}
1861.user-row-bio,
1862.user-row-loc {
1863 margin: 0.2rem 0 0;
1864 font-size: 0.9em;
1865}
1866.user-row-bio > :first-child {
1867 margin-top: 0;
1868}
1869.user-row-bio > :last-child {
1870 margin-bottom: 0;
1871}
1872.user-row > .btn {
1873 flex: none;
1874 align-self: center;
1875}
1876.profile-pronouns {
1877 padding: 0.05rem 0.5rem;
1878 border: 1px solid var(--fb-border);
1879 border-radius: var(--fb-radius);
1880 font-size: 0.82em;
1881}
1882.profile-bio {
1883 margin: 0.5rem 0;
1884 padding-top: 0.75rem;
1885 border-top: 1px solid var(--fb-border);
1886}
1887.profile-bio :first-child {
1888 margin-top: 0;
1889}
1890.profile-bio :last-child {
1891 margin-bottom: 0;
1892}
1893.profile-links {
1894 list-style: none;
1895 margin: 0;
1896 padding: 0.75rem 0 0;
1897 border-top: 1px solid var(--fb-border);
1898 display: flex;
1899 flex-direction: column;
1900 gap: 0.4rem;
1901}
1902.profile-links li {
1903 display: inline-flex;
1904 align-items: center;
1905 gap: 0.5rem;
1906 min-width: 0;
1907}
1908.profile-links li .icon {
1909 flex: none;
1910 color: var(--fb-fg-muted);
1911}
1912.profile-links a {
1913 display: inline-flex;
1914 align-items: center;
1915 gap: 0.5rem;
1916 min-width: 0;
1917 color: var(--fb-accent);
1918}
1919.profile-links a span {
1920 overflow: hidden;
1921 text-overflow: ellipsis;
1922 white-space: nowrap;
1923}
1924.profile-joined {
1925 margin: 0;
1926 padding-top: 0.75rem;
1927 border-top: 1px solid var(--fb-border);
1928 font-size: 0.9em;
1929}
1930.avatar-settings {
1931 display: flex;
1932 align-items: center;
1933 gap: 1.25rem;
1934 flex-wrap: wrap;
1935}
1936.avatar-form {
1937 flex: 1;
1938 min-width: 15rem;
1939}
1940.avatar-upload-row {
1941 display: flex;
1942 align-items: stretch;
1943 gap: 0.75rem;
1944 flex-wrap: wrap;
1945 margin-top: 0.6rem;
1946}
1947.avatar-form input[type="file"] {
1948 flex: 0 1 auto;
1949 min-width: 0;
1950 max-width: 100%;
1951 padding: 0.4rem;
1952 background: var(--fb-bg-inset);
1953 border: 1px solid var(--fb-border);
1954 border-radius: var(--fb-radius);
1955 color: var(--fb-fg);
1956}
1957.avatar-upload-row button[type="submit"] {
1958 flex: none;
1959}
1960
1961/* ---- Search hits ---- */
1962.search-repo {
1963 margin: 1.5rem 0 0.6rem;
1964 font-family: var(--fb-font-mono);
1965 font-size: 0.95rem;
1966}
1967.search-repo a {
1968 color: var(--fb-fg);
1969}
1970.search-repo a:hover {
1971 color: var(--fb-accent);
1972}
1973.search-hit {
1974 padding: 0;
1975 overflow: hidden;
1976 margin-bottom: 0.75rem;
1977}
1978.search-hit-path {
1979 padding: 0.55rem 0.85rem;
1980 font-weight: 600;
1981 border-bottom: 1px solid var(--fb-border);
1982}
1983.search-hit-lines {
1984 margin: 0;
1985 padding: 0.4rem 0;
1986 background: var(--fb-bg-inset);
1987 font-family: var(--fb-font-mono);
1988 font-size: 0.85rem;
1989 overflow-x: auto;
1990}
1991.search-hit-line {
1992 display: flex;
1993 gap: 1rem;
1994 padding: 0 0.85rem;
1995 white-space: pre;
1996}
1997.search-hit-line .lineno {
1998 flex: none;
1999 min-width: 2.5rem;
2000 text-align: right;
2001 color: var(--fb-fg-muted);
2002 user-select: none;
2003}
2004.search-hit-line code {
2005 white-space: pre;
2006}
2007
2008/* ---- Tables ---- */
2009table.list {
2010 width: 100%;
2011 border-collapse: collapse;
2012}
2013table.list th,
2014table.list td {
2015 text-align: left;
2016 padding: 0.5rem 0.6rem;
2017 border-bottom: 1px solid var(--fb-border);
2018}
2019table.list th {
2020 color: var(--fb-fg-muted);
2021 font-weight: 600;
2022}
2023
2024/* ---- Code ---- */
2025pre.code {
2026 margin: 0;
2027 padding: 0.75rem 1rem;
2028 overflow-x: auto;
2029 background: var(--fb-bg-inset);
2030 border-radius: var(--fb-radius);
2031}
2032
2033/* ---- Rendered markdown (README, bios) ---- */
2034.markdown {
2035 overflow-wrap: break-word;
2036}
2037.markdown > :first-child {
2038 margin-top: 0;
2039}
2040.markdown > :last-child {
2041 margin-bottom: 0;
2042}
2043.markdown h1,
2044.markdown h2 {
2045 padding-bottom: 0.3em;
2046 border-bottom: 1px solid var(--fb-border);
2047 margin: 1.5em 0 1rem;
2048}
2049.markdown h3,
2050.markdown h4 {
2051 margin: 1.4em 0 0.8rem;
2052}
2053.markdown p,
2054.markdown ul,
2055.markdown ol,
2056.markdown blockquote,
2057.markdown table,
2058.markdown pre {
2059 margin: 0 0 1rem;
2060}
2061.markdown ul,
2062.markdown ol {
2063 padding-left: 1.5rem;
2064}
2065.markdown a {
2066 text-decoration: underline;
2067}
2068.markdown blockquote {
2069 padding: 0 1rem;
2070 color: var(--fb-fg-muted);
2071 border-left: 3px solid var(--fb-border);
2072}
2073/* Inline code: a subtle chip. */
2074.markdown code {
2075 padding: 0.15em 0.4em;
2076 font-size: 0.9em;
2077 background: var(--fb-bg-inset);
2078 border: 1px solid var(--fb-border);
2079 border-radius: 5px;
2080}
2081/* Fenced blocks: a clearly delimited panel; the inner code resets the chip. */
2082.markdown pre {
2083 padding: 0.85rem 1rem;
2084 overflow-x: auto;
2085 background: var(--fb-bg-inset);
2086 border: 1px solid var(--fb-border);
2087 border-radius: var(--fb-radius);
2088 line-height: 1.45;
2089}
2090.markdown pre code {
2091 padding: 0;
2092 font-size: 0.9em;
2093 background: none;
2094 border: none;
2095 border-radius: 0;
2096}
2097.markdown table {
2098 border-collapse: collapse;
2099}
2100.markdown th,
2101.markdown td {
2102 padding: 0.4rem 0.75rem;
2103 border: 1px solid var(--fb-border);
2104}
2105.markdown th {
2106 background: var(--fb-bg-inset);
2107}
2108.markdown img {
2109 max-width: 100%;
2110}
2111.markdown hr {
2112 border: none;
2113 border-top: 1px solid var(--fb-border);
2114 margin: 1.5rem 0;
2115}
2116.readme {
2117 margin-top: 1rem;
2118}
2119
2120/* Highlight classes — coloured entirely by the theme's --fb-hl-* tokens. */
2121.hl-keyword { color: var(--fb-hl-keyword); }
2122.hl-function { color: var(--fb-hl-function); }
2123.hl-type { color: var(--fb-hl-type); }
2124.hl-string { color: var(--fb-hl-string); }
2125.hl-number { color: var(--fb-hl-number); }
2126.hl-comment { color: var(--fb-hl-comment); font-style: italic; }
2127.hl-constant { color: var(--fb-hl-constant); }
2128.hl-variable { color: var(--fb-hl-variable); }
2129.hl-operator { color: var(--fb-hl-operator); }
2130.hl-punctuation { color: var(--fb-hl-punctuation); }
2131.hl-attribute { color: var(--fb-hl-attribute); }
2132.hl-tag { color: var(--fb-hl-tag); }
2133
2134/* ---- Error page ---- */
2135.error-page {
2136 text-align: center;
2137 padding: 5rem 1rem;
2138}
2139.error-page .code {
2140 font-size: 4rem;
2141 font-weight: 700;
2142 color: var(--fb-fg-muted);
2143}
2144.error-page .req-id {
2145 font-size: 0.85em;
2146 color: var(--fb-fg-muted);
2147}
2148
2149/* ---- Repo views ---- */
2150.repo-head {
2151 margin-bottom: 1.25rem;
2152}
2153/* Group listing header: slug on the left, admin actions on the right. */
2154.group-head {
2155 display: flex;
2156 align-items: center;
2157 justify-content: space-between;
2158 gap: 1rem;
2159 flex-wrap: wrap;
2160 margin-bottom: 1.25rem;
2161}
2162.group-head-actions {
2163 display: flex;
2164 align-items: center;
2165 gap: 0.5rem;
2166}
2167/* Icon-only buttons (e.g. the settings gear) must match the height of the
2168 text buttons beside them: a bare 1em icon is shorter than a text line box, so
2169 pin both the icon and the button's line-height to the same value. */
2170.group-head-actions .btn {
2171 line-height: 1.25rem;
2172}
2173.group-head-actions .btn .icon {
2174 width: 1.25rem;
2175 height: 1.25rem;
2176}
2177/* "forked from owner/repo" line under the repo slug. */
2178.fork-note {
2179 display: flex;
2180 align-items: center;
2181 gap: 0.3rem;
2182 font-size: 0.9em;
2183 margin: 0.2rem 0 0;
2184}
2185.repo-slug {
2186 display: flex;
2187 align-items: center;
2188 gap: 0.4rem;
2189 flex-wrap: wrap;
2190 font-size: 1.4rem;
2191 font-weight: 400;
2192 /* Tight line-height so the small private pill centres on the text's optical
2193 centre rather than the tall default line box. */
2194 line-height: 1;
2195}
2196.repo-slug a {
2197 color: var(--fb-accent);
2198}
2199.slug-sep {
2200 color: var(--fb-fg-muted);
2201}
2202.repo-slug .badge {
2203 font-size: 0.7rem;
2204 padding: 0.12rem 0.55rem;
2205 align-self: center;
2206 /* Nudge down: flex-centring sits on the em-box centre, which is above the
2207 optical centre of the lowercase slug text. */
2208 position: relative;
2209 top: 0.12rem;
2210}
2211/* Tab strips (repo sub-nav, explore sub-nav): active-pill style. */
2212.repo-tabs,
2213.profile-tabs,
2214.subnav {
2215 display: flex;
2216 gap: 0.5rem;
2217 flex-wrap: wrap;
2218 margin: 0.75rem 0 1.25rem;
2219}
2220.profile-tabs {
2221 margin-top: 0;
2222}
2223/* Active toggle button (e.g. a bookmarked repo). */
2224.btn-active {
2225 color: var(--fb-accent);
2226 border-color: var(--fb-accent);
2227}
2228/* Inline #n / @user references in rendered markdown. */
2229.issue-ref,
2230.user-ref {
2231 font-weight: 600;
2232}
2233/* "View all N repositories" link under the dashboard sidebar list. */
2234.dashboard-viewall {
2235 margin: 0.6rem 0 0;
2236 font-size: 0.9em;
2237}
2238/* Releases. */
2239.releases-head {
2240 display: flex;
2241 align-items: center;
2242 justify-content: space-between;
2243 gap: 1rem;
2244 margin-bottom: 1rem;
2245}
2246.back-link {
2247 font-size: 0.9em;
2248}
2249/* Breadcrumb (Releases / name / Edit). */
2250.crumbs {
2251 display: flex;
2252 align-items: center;
2253 gap: 0.4rem;
2254 flex-wrap: wrap;
2255 margin-bottom: 1rem;
2256 font-size: 0.95em;
2257}
2258.crumbs a {
2259 color: var(--fb-accent);
2260}
2261.crumb-current {
2262 color: var(--fb-fg-muted);
2263}
2264.release {
2265 margin-bottom: 1.25rem;
2266}
2267.release-head {
2268 display: flex;
2269 align-items: flex-start;
2270 justify-content: space-between;
2271 gap: 1rem;
2272}
2273.release-title {
2274 margin: 0;
2275 font-size: 1.4rem;
2276}
2277.release-title a {
2278 color: var(--fb-fg);
2279}
2280.release-meta {
2281 display: flex;
2282 align-items: center;
2283 gap: 0.35rem;
2284 flex-wrap: wrap;
2285 margin-top: 0.35rem;
2286 font-size: 0.9em;
2287}
2288.release-actions {
2289 display: flex;
2290 align-items: center;
2291 gap: 0.5rem;
2292 flex: none;
2293}
2294/* The Delete button sits in a card form, so it would otherwise inherit the
2295 stacked-form top margin and float below the Edit link. Specificity here must
2296 beat `.card form button[type="submit"]:not(.inline-btn)`. */
2297.card .release-actions form button[type="submit"]:not(.inline-btn) {
2298 margin-top: 0;
2299}
2300/* Asset management sits below the edit form; give it room to breathe. */
2301.release-assets-editor {
2302 margin-top: 1.5rem;
2303}
2304.release-notes {
2305 margin: 1rem 0;
2306 padding-top: 1rem;
2307 border-top: 1px solid var(--fb-border);
2308}
2309.release-assets h3 {
2310 margin: 1rem 0 0.5rem;
2311}
2312.asset-list {
2313 list-style: none;
2314 margin: 0;
2315 padding: 0;
2316 border: 1px solid var(--fb-border);
2317 border-radius: var(--fb-radius);
2318}
2319.asset-row {
2320 display: flex;
2321 align-items: center;
2322 gap: 0.6rem;
2323 padding: 0.55rem 0.85rem;
2324 border-bottom: 1px solid var(--fb-border);
2325}
2326.asset-row:last-child {
2327 border-bottom: none;
2328}
2329.asset-name {
2330 flex: 1;
2331 min-width: 0;
2332 overflow: hidden;
2333 text-overflow: ellipsis;
2334 white-space: nowrap;
2335 font-weight: 600;
2336}
2337.asset-row.source .asset-name {
2338 font-weight: 400;
2339}
2340.asset-size {
2341 flex: none;
2342 font-size: 0.85em;
2343}
2344.asset-upload {
2345 margin-top: 0.75rem;
2346}
2347/* A dashed drop target that also opens the file picker on click (the file input
2348 is visually hidden inside the label but stays keyboard- and click-reachable). */
2349.dropzone {
2350 display: flex;
2351 align-items: center;
2352 justify-content: center;
2353 gap: 0.5rem;
2354 padding: 1.5rem;
2355 border: 1px dashed var(--fb-border);
2356 border-radius: var(--fb-radius);
2357 color: var(--fb-fg-muted);
2358 text-align: center;
2359 cursor: pointer;
2360}
2361.dropzone:hover,
2362.dropzone.drag {
2363 border-color: var(--fb-accent);
2364 color: var(--fb-fg);
2365}
2366.dropzone-input {
2367 position: absolute;
2368 width: 1px;
2369 height: 1px;
2370 padding: 0;
2371 opacity: 0;
2372 pointer-events: none;
2373}
2374.asset-upload .btn {
2375 margin-top: 0.75rem;
2376}
2377/* The per-asset delete button sits in a card form; kill the stacked-form top
2378 margin so it centres in the row, and keep it compact. Specificity beats
2379 `.card form button[type="submit"]:not(.inline-btn)`. */
2380.card .asset-list .asset-row form button[type="submit"] {
2381 margin-top: 0;
2382}
2383.asset-row .icon-btn {
2384 width: 1.75rem;
2385 height: 1.75rem;
2386}
2387.release-form-actions {
2388 display: flex;
2389 align-items: center;
2390 gap: 0.75rem;
2391 margin-top: 1rem;
2392}
2393/* Notifications inbox. */
2394.notif-head {
2395 display: flex;
2396 align-items: center;
2397 justify-content: space-between;
2398 gap: 1rem;
2399 margin-bottom: 1rem;
2400}
2401.notif-list {
2402 list-style: none;
2403 margin: 0;
2404 padding: 0;
2405}
2406.notif-row {
2407 display: flex;
2408 align-items: center;
2409 gap: 0.75rem;
2410 padding: 0.7rem 1rem;
2411 border-bottom: 1px solid var(--fb-border);
2412}
2413.notif-row:last-child {
2414 border-bottom: none;
2415}
2416.notif-row.unread {
2417 background: var(--fb-bg-inset);
2418}
2419.notif-icon {
2420 flex: none;
2421 color: var(--fb-fg-muted);
2422}
2423.notif-row.unread .notif-icon {
2424 color: var(--fb-accent);
2425}
2426.notif-body {
2427 display: flex;
2428 align-items: center;
2429 justify-content: space-between;
2430 gap: 1rem;
2431 flex: 1;
2432 min-width: 0;
2433 color: var(--fb-fg);
2434}
2435.notif-text {
2436 min-width: 0;
2437 overflow: hidden;
2438 text-overflow: ellipsis;
2439 white-space: nowrap;
2440}
2441.notif-time {
2442 flex: none;
2443 font-size: 0.85em;
2444}
2445.repo-tabs a,
2446.profile-tabs a,
2447.subnav a {
2448 display: inline-flex;
2449 align-items: center;
2450 gap: 0.4rem;
2451 padding: 0.4rem 0.9rem;
2452 border-radius: var(--fb-radius);
2453 color: var(--fb-fg-muted);
2454}
2455.repo-tabs a:hover,
2456.profile-tabs a:hover,
2457.subnav a:hover {
2458 color: var(--fb-fg);
2459 background: var(--fb-bg-inset);
2460 text-decoration: none;
2461}
2462.repo-tabs a[aria-current="page"],
2463.profile-tabs a[aria-current="page"],
2464.subnav a[aria-current="page"] {
2465 color: var(--fb-fg);
2466 background: var(--fb-bg-inset);
2467 font-weight: 600;
2468}
2469.subnav {
2470 justify-content: center;
2471}
2472/* Tabs share their line with the branch/commits/clone actions. */
2473.repo-nav {
2474 display: flex;
2475 align-items: center;
2476 justify-content: space-between;
2477 gap: 1rem;
2478 flex-wrap: wrap;
2479}
2480.repo-nav .repo-tabs {
2481 margin: 0.75rem 0 0;
2482 flex: 1;
2483}
2484.repo-actions {
2485 display: flex;
2486 align-items: center;
2487 gap: 0.5rem;
2488 flex-wrap: wrap;
2489}
2490.repo-codesearch {
2491 margin: 1rem 0 1.25rem;
2492}
2493
2494/* Clone dropdown: a no-JS details/summary popover. */
2495.clone-menu {
2496 position: relative;
2497}
2498.clone-menu > summary {
2499 list-style: none;
2500 cursor: pointer;
2501}
2502.clone-menu > summary::-webkit-details-marker {
2503 display: none;
2504}
2505.clone-pop {
2506 position: absolute;
2507 right: 0;
2508 top: calc(100% + 0.35rem);
2509 z-index: 30;
2510 width: 24rem;
2511 max-width: 88vw;
2512 display: flex;
2513 flex-direction: column;
2514 gap: 0.75rem;
2515 box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);
2516}
2517.clone-field-label {
2518 display: block;
2519 margin-bottom: 0.3rem;
2520 font-size: 0.78em;
2521 font-weight: 600;
2522}
2523.clone-input {
2524 display: flex;
2525 gap: 0.4rem;
2526}
2527.clone-input input {
2528 font-family: var(--fb-font-mono);
2529 font-size: 0.8em;
2530}
2531
2532/* Branch switcher dropdown. */
2533.branch-menu {
2534 position: relative;
2535}
2536.branch-menu > summary {
2537 list-style: none;
2538 cursor: pointer;
2539}
2540.branch-menu > summary::-webkit-details-marker {
2541 display: none;
2542}
2543.branch-menu > summary .icon:last-child {
2544 margin-left: 0.1rem;
2545}
2546.branch-pop {
2547 position: absolute;
2548 left: 0;
2549 top: calc(100% + 0.35rem);
2550 z-index: 30;
2551 width: 17rem;
2552 max-width: 88vw;
2553 max-height: 20rem;
2554 overflow-y: auto;
2555 padding: 0.35rem;
2556 box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);
2557}
2558.branch-pop-head {
2559 padding: 0.3rem 0.5rem;
2560 font-size: 0.78em;
2561 font-weight: 600;
2562}
2563.branch-pop a {
2564 display: block;
2565 padding: 0.35rem 0.5rem;
2566 border-radius: var(--fb-radius);
2567 color: var(--fb-fg);
2568 font-family: var(--fb-font-mono);
2569 font-size: 0.88em;
2570 overflow: hidden;
2571 text-overflow: ellipsis;
2572 white-space: nowrap;
2573}
2574.branch-pop a:hover {
2575 background: var(--fb-bg-inset);
2576 text-decoration: none;
2577}
2578.branch-pop a[aria-current="page"] {
2579 color: var(--fb-accent);
2580 font-weight: 600;
2581}
2582.branch-pop-all {
2583 margin-top: 0.25rem;
2584 border-top: 1px solid var(--fb-border);
2585 color: var(--fb-accent) !important;
2586 font-family: var(--fb-font-sans) !important;
2587}
2588.breadcrumb {
2589 margin-bottom: 0.75rem;
2590 font-family: var(--fb-font-mono);
2591}
2592
2593/* File tree: a bordered card of compact rows. */
2594.file-tree {
2595 padding: 0;
2596 overflow: hidden;
2597}
2598.file-row {
2599 display: flex;
2600 align-items: center;
2601 gap: 0.6rem;
2602 padding: 0.4rem 0.85rem;
2603 color: var(--fb-fg);
2604 border-bottom: 1px solid var(--fb-border);
2605}
2606.file-row:last-child {
2607 border-bottom: none;
2608}
2609.file-row:hover {
2610 background: var(--fb-bg-inset);
2611 text-decoration: none;
2612}
2613.file-icon {
2614 display: inline-flex;
2615 color: var(--fb-fg-muted);
2616}
2617.file-row.is-dir .file-icon {
2618 color: var(--fb-accent);
2619}
2620.file-name {
2621 min-width: 0;
2622 overflow: hidden;
2623 text-overflow: ellipsis;
2624 white-space: nowrap;
2625}
2626/* A small pill beside a tree entry's name (LFS-tracked file, submodule). */
2627.tree-pill {
2628 flex: none;
2629 padding: 0.02rem 0.4rem;
2630 font-size: 0.72em;
2631 font-weight: 600;
2632 letter-spacing: 0.03em;
2633 color: var(--fb-fg-muted);
2634 border: 1px solid var(--fb-border);
2635 border-radius: 999px;
2636}
2637.lfs-pill {
2638 color: var(--fb-accent);
2639 border-color: var(--fb-accent);
2640}
2641/* Blob header left group: file size with an optional LFS pill beside it. */
2642.blob-meta {
2643 display: flex;
2644 align-items: center;
2645 gap: 0.5rem;
2646}
2647.file-row.is-submodule .file-icon {
2648 color: var(--fb-accent);
2649}
2650
2651.blob-header {
2652 display: flex;
2653 justify-content: space-between;
2654 align-items: center;
2655 gap: 0.75rem;
2656 flex-wrap: wrap;
2657 padding: 0.5rem 0.75rem;
2658 background: var(--fb-bg-raised);
2659 border: 1px solid var(--fb-border);
2660 border-bottom: none;
2661 border-radius: var(--fb-radius) var(--fb-radius) 0 0;
2662}
2663.blob-actions {
2664 display: flex;
2665 align-items: center;
2666 gap: 0.4rem;
2667 flex-wrap: wrap;
2668}
2669.blob-header .btn {
2670 padding: 0.3rem 0.6rem;
2671 font-size: 0.85em;
2672}
2673.blob-header .btn.active {
2674 border-color: var(--fb-accent);
2675 color: var(--fb-accent);
2676}
2677
2678/* Rendered markdown preview continues the blob-header box (no seam). */
2679.blob-preview {
2680 padding: 1.25rem 1.5rem;
2681 background: var(--fb-bg-raised);
2682 border: 1px solid var(--fb-border);
2683 border-top: none;
2684 border-radius: 0 0 var(--fb-radius) var(--fb-radius);
2685}
2686/* Inline image preview: flush under the header (no baseline gap), clipped to the
2687 rounded bottom corners. */
2688.blob-image {
2689 background: var(--fb-bg-inset);
2690 border: 1px solid var(--fb-border);
2691 border-top: none;
2692 border-radius: 0 0 var(--fb-radius) var(--fb-radius);
2693 overflow: hidden;
2694 line-height: 0;
2695}
2696.blob-image img {
2697 display: block;
2698 max-width: 100%;
2699 margin: 0 auto;
2700}
2701
2702/* The blob box: border, rounded bottom, scroll, and vertical breathing room. */
2703.blob-code {
2704 border: 1px solid var(--fb-border);
2705 border-top: none;
2706 border-radius: 0 0 var(--fb-radius) var(--fb-radius);
2707 background: var(--fb-bg-inset);
2708 overflow-x: auto;
2709 padding: 0.5rem 0;
2710}
2711table.blob {
2712 width: 100%;
2713 border-collapse: collapse;
2714 font-family: var(--fb-font-mono);
2715 font-size: 0.85rem;
2716}
2717table.blob td.lineno {
2718 width: 1%;
2719 min-width: 3rem;
2720 text-align: right;
2721 padding: 0 0.75rem;
2722 color: var(--fb-fg-muted);
2723 user-select: none;
2724 vertical-align: top;
2725}
2726table.blob td.lineno a {
2727 color: inherit;
2728}
2729table.blob td.code-line {
2730 padding: 0 0.75rem;
2731 white-space: pre;
2732 tab-size: 4;
2733}
2734table.blob tr:target {
2735 background: var(--fb-diff-add-bg);
2736}
2737
2738/* ---- Entry rows (commits, branches, tags) ---- */
2739.entry-list {
2740 list-style: none;
2741 margin: 0;
2742 padding: 0;
2743}
2744.entry-row {
2745 display: flex;
2746 align-items: center;
2747 justify-content: space-between;
2748 gap: 1rem;
2749 padding: 0.7rem 1rem;
2750 border-bottom: 1px solid var(--fb-border);
2751}
2752.entry-row:last-child {
2753 border-bottom: none;
2754}
2755.entry-row-body {
2756 min-width: 0;
2757}
2758.entry-row-title {
2759 display: block;
2760 font-weight: 600;
2761 color: var(--fb-fg);
2762 overflow: hidden;
2763 text-overflow: ellipsis;
2764 white-space: nowrap;
2765}
2766.entry-row-title:hover {
2767 color: var(--fb-accent);
2768}
2769.entry-row-meta {
2770 display: flex;
2771 gap: 0.4rem;
2772 flex-wrap: wrap;
2773 margin-top: 0.15rem;
2774 font-size: 0.85em;
2775}
2776.entry-row-meta .ahead {
2777 color: var(--fb-success);
2778}
2779/* Branch list meta: bullet-separate the segments (counts · updated · author).
2780 Ahead/behind stay grouped in `.branch-counts`, so they read as one segment. */
2781.branch-meta > * + *::before {
2782 content: "·";
2783 margin-right: 0.4rem;
2784 color: var(--fb-fg-muted);
2785}
2786.branch-counts {
2787 display: inline-flex;
2788 gap: 0.35rem;
2789}
2790.entry-row-actions {
2791 display: flex;
2792 align-items: center;
2793 gap: 0.5rem;
2794 flex: none;
2795}
2796/* Action-row submit buttons must not inherit the stacked-form top margin, or
2797 they float out of vertical alignment with the row. This selector is kept more
2798 specific than `.card form button[type="submit"]:not(.inline-btn)`. */
2799.card .entry-row .entry-row-actions form button[type="submit"] {
2800 margin-top: 0;
2801}
2802.commit-sha-pill {
2803 padding: 0.1rem 0.45rem;
2804 background: var(--fb-bg-inset);
2805 border: 1px solid var(--fb-border);
2806 border-radius: 5px;
2807 font-size: 0.82em;
2808 color: var(--fb-fg);
2809}
2810.commit-sha-pill:hover {
2811 border-color: var(--fb-accent);
2812 text-decoration: none;
2813}
2814.commit-copy {
2815 width: 1.75rem;
2816 height: 1.75rem;
2817}
2818
2819/* Verified-signature hover popover in the commits list. */
2820.sig-pop-wrap {
2821 position: relative;
2822 display: inline-flex;
2823}
2824.sig-pop {
2825 position: absolute;
2826 right: 0;
2827 top: calc(100% + 0.35rem);
2828 z-index: 40;
2829 width: 21rem;
2830 max-width: 88vw;
2831 display: none;
2832 text-align: left;
2833 white-space: normal;
2834 cursor: default;
2835 box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);
2836}
2837.sig-pop-wrap:hover .sig-pop,
2838.sig-pop-wrap:focus-within .sig-pop {
2839 display: block;
2840}
2841.sig-pop-title {
2842 font-weight: 600;
2843 margin-bottom: 0.6rem;
2844}
2845.sig-pop-signer {
2846 margin-bottom: 0.6rem;
2847}
2848.sig-pop-fp {
2849 font-size: 0.85em;
2850 color: var(--fb-fg-muted);
2851 margin-bottom: 0.4rem;
2852}
2853.sig-pop-fp .mono {
2854 display: block;
2855 margin-top: 0.15rem;
2856 color: var(--fb-fg);
2857 word-break: break-all;
2858}
2859.sig-pop-when {
2860 font-size: 0.85em;
2861}
2862
2863/* Commit signature detail bar. */
2864.sig-detail {
2865 display: flex;
2866 justify-content: space-between;
2867 align-items: center;
2868 gap: 1rem;
2869 flex-wrap: wrap;
2870 padding: 0.5rem 0.85rem;
2871 margin-bottom: 1rem;
2872 border: 1px solid var(--fb-border);
2873 border-radius: var(--fb-radius);
2874 font-size: 0.9em;
2875}
2876.sig-verified {
2877 border-color: var(--fb-success);
2878}
2879.sig-verified .sig-signer {
2880 color: var(--fb-success);
2881}
2882.sig-unverified {
2883 border-color: var(--fb-warning);
2884}
2885.sig-fp {
2886 color: var(--fb-fg-muted);
2887 word-break: break-all;
2888}
2889
2890/* ---- Commit view & diffs ---- */
2891.commit-meta {
2892 margin-bottom: 1rem;
2893}
2894.commit-title-row {
2895 display: flex;
2896 align-items: baseline;
2897 justify-content: space-between;
2898 gap: 1rem;
2899 flex-wrap: wrap;
2900}
2901.commit-title {
2902 margin: 0;
2903 min-width: 0;
2904}
2905.commit-title-meta {
2906 margin: 0;
2907 font-size: 0.85em;
2908 text-align: right;
2909}
2910.commit-body {
2911 margin: 0.5rem 0;
2912 padding: 0.5rem 0.75rem;
2913 background: var(--fb-bg-inset);
2914 border-radius: var(--fb-radius);
2915 white-space: pre-wrap;
2916 font-family: var(--fb-font-mono);
2917 font-size: 0.85rem;
2918}
2919.diff-toolbar {
2920 display: flex;
2921 justify-content: space-between;
2922 align-items: center;
2923 flex-wrap: wrap;
2924 gap: 0.5rem;
2925 margin-bottom: 0.75rem;
2926}
2927.diff-toggle {
2928 display: inline-flex;
2929 gap: 0.5rem;
2930}
2931.diff-toggle .btn {
2932 padding: 0.2rem 0.6rem;
2933}
2934.diff-toggle .btn.active {
2935 border-color: var(--fb-accent);
2936 color: var(--fb-accent);
2937}
2938.changed-files {
2939 display: flex;
2940 flex-direction: column;
2941 gap: 0.15rem;
2942 padding: 0.5rem 0.75rem;
2943 margin-bottom: 1rem;
2944 background: var(--fb-bg-raised);
2945 border: 1px solid var(--fb-border);
2946 border-radius: var(--fb-radius);
2947 font-family: var(--fb-font-mono);
2948 font-size: 0.85rem;
2949}
2950.diff-file {
2951 margin-bottom: 1rem;
2952 border: 1px solid var(--fb-border);
2953 border-radius: var(--fb-radius);
2954 overflow: hidden;
2955}
2956.diff-file > summary {
2957 padding: 0.5rem 0.75rem;
2958 background: var(--fb-bg-raised);
2959 cursor: pointer;
2960}
2961
2962table.diff {
2963 width: 100%;
2964 border-collapse: collapse;
2965 font-family: var(--fb-font-mono);
2966 font-size: 0.82rem;
2967 background: var(--fb-bg-inset);
2968}
2969table.diff td {
2970 padding: 0 0.5rem;
2971 white-space: pre-wrap;
2972 word-break: break-word;
2973 vertical-align: top;
2974}
2975table.diff td.lineno {
2976 width: 1%;
2977 min-width: 2.5rem;
2978 text-align: right;
2979 color: var(--fb-fg-muted);
2980 user-select: none;
2981 white-space: nowrap;
2982}
2983table.diff td.diff-marker {
2984 width: 1ch;
2985 text-align: center;
2986 user-select: none;
2987}
2988tr.diff-add,
2989td.diff-add {
2990 background: var(--fb-diff-add-bg);
2991}
2992tr.diff-add td.diff-marker,
2993tr.diff-add td.code-line {
2994 color: var(--fb-diff-add-fg);
2995}
2996tr.diff-del,
2997td.diff-del {
2998 background: var(--fb-diff-del-bg);
2999}
3000tr.diff-del td.diff-marker,
3001tr.diff-del td.code-line {
3002 color: var(--fb-diff-del-fg);
3003}
3004tr.hunk-header td {
3005 background: var(--fb-bg-raised);
3006 color: var(--fb-fg-muted);
3007}
3008.diff-expander td {
3009 background: var(--fb-bg-raised);
3010 text-align: center;
3011 padding: 0.15rem;
3012}
3013.expand-btn {
3014 background: none;
3015 border: none;
3016 color: var(--fb-accent);
3017 cursor: pointer;
3018 font: inherit;
3019}
3020td.diff-empty {
3021 background: var(--fb-bg-raised);
3022}
3023
3024@media (max-width: 48rem) {
3025 /* Force unified on narrow screens (§9.6). */
3026 table.diff-split {
3027 display: block;
3028 overflow-x: auto;
3029 }
3030}
3031
3032/* ---- Responsive: single breakpoint at 48rem ---- */
3033@media (max-width: 48rem) {
3034 .topbar .tabs {
3035 margin: 0;
3036 }
3037 .page {
3038 padding: 0.75rem;
3039 }
3040 /* The body never scrolls horizontally; wide content scrolls within itself. */
3041 body {
3042 overflow-x: hidden;
3043 }
3044 /* Comfortable touch targets (>= 44px) for the primary interactive elements. */
3045 .topbar .tabs a,
3046 .repo-tabs a,
3047 .drawer nav a,
3048 .drawer .drawer-item,
3049 .btn,
3050 .icon-btn {
3051 min-height: 44px;
3052 display: inline-flex;
3053 align-items: center;
3054 }
3055 .icon-btn {
3056 justify-content: center;
3057 min-width: 44px;
3058 }
3059 /* Stack the repo sub-nav: the tab strip scrolls horizontally on its own row
3060 (rather than wrapping into a tall column), with the branch/clone actions
3061 on the row below instead of crowding the tabs. */
3062 .repo-nav {
3063 flex-direction: column;
3064 align-items: stretch;
3065 }
3066 /* min-width:0 lets the strip shrink below its content so overflow-x:auto
3067 actually scrolls, instead of the tabs pushing the whole page wider than
3068 the viewport. The specificity matches the base `.repo-nav .repo-tabs`. */
3069 .repo-nav .repo-tabs {
3070 flex: 1 1 auto;
3071 min-width: 0;
3072 max-width: 100%;
3073 flex-wrap: nowrap;
3074 overflow-x: auto;
3075 -webkit-overflow-scrolling: touch;
3076 }
3077 .repo-tabs a {
3078 flex: 0 0 auto;
3079 }
3080 /* Branch switcher and Clone split the row 50/50, full width. */
3081 .repo-actions {
3082 width: 100%;
3083 }
3084 .repo-actions > * {
3085 flex: 1;
3086 min-width: 0;
3087 }
3088 .repo-actions summary.btn,
3089 .repo-actions > a.btn {
3090 width: 100%;
3091 justify-content: center;
3092 }
3093 /* List rows stack so a wide action cluster drops below the body instead of
3094 squeezing it to an unreadable sliver (admin users, keys, emails, commits). */
3095 .entry-row,
3096 .key-list .entry-row {
3097 flex-direction: column;
3098 align-items: stretch;
3099 gap: 0.5rem;
3100 }
3101 .entry-row-actions {
3102 flex-wrap: wrap;
3103 }
3104 /* Long unbroken meta (key fingerprints, emails) wraps rather than overflowing. */
3105 .entry-row-meta {
3106 overflow-wrap: anywhere;
3107 }
3108 /* Diff tables scroll within their own box (the blob wrapper already does). */
3109 table.diff {
3110 display: block;
3111 overflow-x: auto;
3112 }
3113 /* Stack the profile, dashboard, and issue columns on narrow screens. The
3114 containers use align-items other than stretch when side-by-side, so reset
3115 it here or the main column shrinks to its content and overflows. */
3116 .profile-layout,
3117 .dashboard,
3118 .issue-main {
3119 flex-direction: column;
3120 align-items: stretch;
3121 }
3122 .profile-sidebar,
3123 .dashboard-side,
3124 .issue-side,
3125 .profile-main,
3126 .dashboard-main {
3127 width: 100%;
3128 min-width: 0;
3129 }
3130}