/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. * * base.css — structure only. No literal colours: every colour is a `--fb-*` * custom property a theme supplies. A theme that sets only the documented tokens * yields a complete UI (docs/theming.md). System font stacks; no web fonts. */ :root { --fb-font-sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; --fb-font-mono: ui-monospace, "SF Mono", "JetBrains Mono", "Fira Code", Menlo, Consolas, monospace; --fb-radius: 6px; --fb-gap: 1rem; --fb-topbar-h: 3rem; } * { box-sizing: border-box; } html { color-scheme: var(--fb-color-scheme, dark); } body { margin: 0; font-family: var(--fb-font-sans); font-size: 14px; line-height: 1.5; color: var(--fb-fg); background: var(--fb-bg); -webkit-font-smoothing: antialiased; } a { color: var(--fb-accent); text-decoration: none; } a:hover { text-decoration: underline; } h1, h2, h3, h4 { line-height: 1.25; margin: 0 0 0.5em; } code, pre, .mono { font-family: var(--fb-font-mono); } /* Inline icons inherit colour and scale to the current font size. */ .icon { width: 1em; height: 1em; flex: none; vertical-align: -0.125em; } /* Accessibility: visible focus, skip link. */ :focus-visible { outline: 2px solid var(--fb-accent); outline-offset: 2px; } .skip-link { position: absolute; left: -999px; top: 0; z-index: 100; padding: 0.5rem 1rem; background: var(--fb-bg-raised); border: 1px solid var(--fb-border); } .skip-link:focus { left: 0.5rem; top: 0.5rem; } /* ---- Top bar ---- */ .topbar { display: flex; align-items: center; gap: 0.75rem; height: var(--fb-topbar-h); padding: 0 0.75rem; border-bottom: 1px solid var(--fb-border); background: var(--fb-bg-raised); position: sticky; top: 0; z-index: 20; } .topbar .slug { font-weight: 600; } .topbar .slug a { color: var(--fb-fg); } .topbar .tabs { display: flex; gap: 0.25rem; margin: 0 auto; overflow-x: auto; } .topbar .tabs a { padding: 0.35rem 0.7rem; border-radius: var(--fb-radius); color: var(--fb-fg-muted); white-space: nowrap; } .topbar .tabs a:hover { background: var(--fb-bg-inset); text-decoration: none; } .topbar .tabs a[aria-current="page"] { color: var(--fb-fg); background: var(--fb-bg-inset); } /* The create menu and search group at the right of the top bar. Both carry `margin-left: auto` so the group hugs the right edge whether or not the create menu is present (when signed out it is absent, so the search keeps its place). */ .topbar-create { margin-left: auto; position: relative; } .topbar-search { display: flex; align-items: center; gap: 0.25rem; flex: 0 1 28rem; margin-left: auto; } /* When the create menu is present it owns the right-alignment, so the search must not add a second auto margin (two would split the free space and float the create menu into the middle). */ .topbar-create ~ .topbar-search { margin-left: 0; } .topbar-search input[type="search"] { width: 100%; padding: 0.3rem 0.6rem; background: var(--fb-bg-inset); border: 1px solid var(--fb-border); border-radius: var(--fb-radius); color: var(--fb-fg); } /* Create (+) popover in the top bar. */ .topbar-create > summary { list-style: none; cursor: pointer; } .topbar-create > summary::-webkit-details-marker { display: none; } .menu-popover { position: absolute; right: 0; top: calc(100% + 0.35rem); z-index: 30; min-width: 13rem; padding: 0.35rem; background: var(--fb-bg-raised); border: 1px solid var(--fb-border); border-radius: var(--fb-radius); box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35); } .menu-item { display: flex; align-items: center; gap: 0.55rem; padding: 0.45rem 0.6rem; border-radius: var(--fb-radius); color: var(--fb-fg); white-space: nowrap; } /* Multi-label dropdown on the new-issue form. */ .new-issue-actions { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; margin-top: 1rem; } .label-dropdown { display: inline-block; position: relative; margin-bottom: 0.5rem; } .new-issue-actions .label-dropdown { margin-bottom: 0; } .label-dropdown > summary { list-style: none; width: auto; cursor: pointer; display: inline-flex; align-items: center; gap: 0.4rem; } .label-dropdown-label { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 0.3rem; } .label-dropdown > summary::-webkit-details-marker { display: none; } .label-menu { left: 0; right: auto; display: flex; flex-direction: column; gap: 0.35rem; max-height: 16rem; overflow-y: auto; } .label-menu .checkbox { margin: 0; } .menu-item:hover { background: var(--fb-bg-inset); text-decoration: none; } .icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 2rem; height: 2rem; padding: 0; border: none; border-radius: var(--fb-radius); background: transparent; color: var(--fb-fg); cursor: pointer; } .icon-btn:hover { background: var(--fb-bg-inset); } .icon-btn .icon { width: 1.15rem; height: 1.15rem; } /* ---- Drawer (off-canvas nav) ---- */ .drawer-backdrop { display: none; position: fixed; inset: 0; background: rgba(0, 0, 0, 0.5); z-index: 30; } .drawer { position: fixed; top: 0; left: 0; bottom: 0; width: 16rem; max-width: 85vw; transform: translateX(-100%); transition: transform 0.15s ease; background: var(--fb-bg-raised); border-right: 1px solid var(--fb-border); z-index: 40; padding: 1rem; overflow-y: auto; } body.drawer-open .drawer { transform: translateX(0); } body.drawer-open .drawer-backdrop { display: block; } .drawer h2 { font-size: 1rem; } .drawer nav a, .drawer .drawer-item { display: flex; align-items: center; gap: 0.6rem; width: 100%; padding: 0.4rem 0.5rem; border-radius: var(--fb-radius); color: var(--fb-fg); font: inherit; text-align: left; } .drawer .drawer-item { border: none; background: none; cursor: pointer; } .drawer nav a .icon, .drawer .drawer-item .icon { color: var(--fb-fg-muted); } .drawer nav a:hover, .drawer .drawer-item:hover { background: var(--fb-bg-inset); text-decoration: none; } .drawer hr { border: none; border-top: 1px solid var(--fb-border); margin: 0.75rem 0; } /* ---- Layout ---- */ .page { max-width: 76rem; margin: 0 auto; /* Extra breathing room below the fixed-height top bar. */ padding: calc(var(--fb-gap) * 1.75) var(--fb-gap) calc(var(--fb-gap) * 2); } .card { background: var(--fb-bg-raised); border: 1px solid var(--fb-border); border-radius: var(--fb-radius); padding: 1rem; } /* The first/last child sits against the card padding, so its own top/bottom margin would double the gap — most visibly on a card holding only an empty-state note ("No open issues."), which would otherwise sit off-centre. */ .card > :first-child { margin-top: 0; } .card > :last-child { margin-bottom: 0; } .muted { color: var(--fb-fg-muted); } /* ---- Buttons & forms ---- */ .btn { display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.4rem 0.8rem; border: 1px solid var(--fb-border); border-radius: var(--fb-radius); background: var(--fb-bg-inset); color: var(--fb-fg); font: inherit; cursor: pointer; } .btn:hover { border-color: var(--fb-accent); text-decoration: none; } .btn-primary { background: var(--fb-accent); border-color: var(--fb-accent); color: var(--fb-accent-fg, #fff); } .btn-danger { border-color: var(--fb-danger); color: var(--fb-danger); } .btn-danger:hover { background: var(--fb-danger); color: var(--fb-accent-fg, #fff); border-color: var(--fb-danger); } /* Repo settings: Save changes + Archive on one row. */ .settings-actions { display: flex; align-items: center; gap: 0.6rem; margin-top: 1.25rem; } .settings-actions form { display: inline; } /* Labels. */ .label-chip { display: inline-flex; align-items: center; padding: 0.1rem 0.55rem; border-radius: 999px; font-size: 0.85em; font-weight: 600; color: #fff; background: var(--label-color, #888); border: 1px solid rgba(0, 0, 0, 0.25); } /* Scoped label (`scope: value`): the scope sits on a neutral theme background, the value in the label colour. The pill clips the two segments. */ .label-chip-scoped { padding: 0; overflow: hidden; } .label-chip-scoped .label-scope, .label-chip-scoped .label-value { padding: 0.1rem 0.55rem; } .label-chip-scoped .label-scope { background: var(--fb-bg-inset); color: var(--fb-fg); } .label-chip-scoped .label-value { background: var(--label-color, #888); color: #fff; } .label-list { margin-bottom: 1.5rem; } .label-list .entry-row { padding: 0.85rem 0; } .label-list .entry-row:first-child { padding-top: 0; } .label-list .entry-row-body { display: flex; flex-direction: row; align-items: center; gap: 0.75rem; flex-wrap: wrap; } .label-list .entry-row-meta { margin: 0; font-size: 0.95em; } .label-add { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: stretch; padding-top: 1.25rem; border-top: 1px solid var(--fb-border); } .label-add input[type="text"] { flex: 1; min-width: 10rem; } .label-add input[type="color"] { width: 2.8rem; height: 2.4rem; padding: 0; border: 1px solid var(--fb-border); border-radius: var(--fb-radius); background: var(--fb-bg-inset); cursor: pointer; } .label-add input[type="color"]::-webkit-color-swatch-wrapper { padding: 3px; } .label-add input[type="color"]::-webkit-color-swatch { border: none; border-radius: 3px; } .label-add input[type="color"]::-moz-color-swatch { border: none; border-radius: 3px; } .label-add button { flex: none; } /* ---- Issues & pull requests ---- */ .issues-head { display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; margin-bottom: 0.5rem; } .issues-head .subnav { margin: 0; justify-content: flex-start; } .state-open { color: var(--fb-success); display: inline-flex; } .state-closed { color: var(--fb-danger); display: inline-flex; } .issue-title-row { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; } .badge-state-open { color: var(--fb-success); border-color: var(--fb-success); } .badge-state-closed { color: var(--fb-danger); border-color: var(--fb-danger); } .badge-state-merged { color: var(--fb-accent); border-color: var(--fb-accent); } /* Pull requests: compare form, merge panel, and change list. */ .compare-refs { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; margin-bottom: 1rem; } .compare-refs label { display: inline-flex; align-items: center; gap: 0.4rem; } .compare-refs select { width: auto; } .compare-arrow { font-size: 1.25rem; color: var(--fb-muted); } .merge-panel { border-left: 3px solid var(--fb-border); padding: 0.85rem 1rem; } .merge-panel > p { margin: 0; } .merge-panel.clean { border-left-color: var(--fb-success); } .merge-panel.conflicts { border-left-color: var(--fb-danger); } .merge-panel.merged { border-left-color: var(--fb-accent); } .merge-form { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.65rem; } .merge-form label { margin: 0; } .merge-form select { width: auto; } .pr-changes { margin-top: 1.5rem; } .pr-changes h2 { margin-bottom: 0.85rem; } .entry-list.pr-commits { margin-bottom: 1.25rem; } .issue-header { margin-bottom: 1rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--fb-border); } .issue-header h1 { margin: 0; } .issue-title-line { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.4rem; } .issue-edit > summary, .comment-edit > summary { cursor: pointer; list-style: none; } .issue-edit > summary::-webkit-details-marker, .comment-edit > summary::-webkit-details-marker { display: none; } /* Edit / Cancel label swap on the toggle. */ .issue-edit:not([open]) .edit-close, .comment-edit:not([open]) .edit-close { display: none; } .issue-edit[open] .edit-open, .comment-edit[open] .edit-open { display: none; } /* When closed, the Edit toggle sits at the right; the editor bits are hidden. */ .issue-edit { margin-left: auto; } .title-edit-form, .title-save { display: none; } /* Title editor (GitHub-style): a full-width input, then Cancel (the toggle) and Save. The three are siblings of the title, revealed via :has. */ .issue-title-line:has(.issue-edit[open]) h1 { display: none; } .issue-title-line:has(.issue-edit[open]) .issue-edit { order: 2; margin-left: 0; } .issue-title-line:has(.issue-edit[open]) .title-edit-form { display: block; order: 1; flex: 1; } .issue-title-line:has(.issue-edit[open]) .title-save { display: inline-flex; order: 3; } .title-edit-form input[type="text"] { width: 100%; } /* Comment editor: the toggle sits in the header; when open the rendered body is replaced by the textarea. */ .comment-edit { margin-left: auto; } .comment-edit > summary { font-size: 0.9em; color: var(--fb-accent); } .comment-editor { display: none; padding: 0.85rem; } .comment:has(.comment-edit[open]) .comment-editor { display: block; } .comment:has(.comment-edit[open]) .comment-body { display: none; } .comment-editor textarea { width: 100%; font-family: inherit; } .comment-editor-actions { display: flex; justify-content: flex-end; margin-top: 0.5rem; } .issue-sub { display: flex; align-items: center; gap: 0.5rem; } .issue-main { display: flex; gap: 1.5rem; align-items: flex-start; } .issue-thread { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1rem; } .issue-side { flex: none; width: 16rem; } .issue-side section { margin-bottom: 1.25rem; padding-bottom: 1rem; border-bottom: 1px solid var(--fb-border); } .issue-side section:last-child { margin-bottom: 0; padding-bottom: 0; border-bottom: none; } .issue-side h3 { font-size: 0.9rem; margin-bottom: 0.5rem; } .comment { padding: 0; overflow: hidden; } .comment-head { display: flex; align-items: center; flex-wrap: wrap; gap: 0.35rem; padding: 0.5rem 0.85rem; border-bottom: 1px solid var(--fb-border); font-size: 0.9em; } .comment-body { padding: 0.85rem; } .comment-form textarea { margin-bottom: 0.5rem; } .comment-actions { display: flex; justify-content: flex-end; gap: 0.5rem; align-items: center; } .inline-form { display: inline; } .side-form { margin-top: 0.5rem; display: flex; gap: 0.4rem; align-items: stretch; flex-wrap: wrap; } .side-form select { flex: 1; } .label-set { display: flex; flex-wrap: wrap; gap: 0.35rem; } /* Existing-issue label editor: the dropdown fills the sidebar width, its toggle shows the applied labels as chips, and Apply sits at the foot of the popover (the column popover stretches it full width). */ .side-form .label-dropdown { flex: 1; margin-bottom: 0; } .side-form .label-dropdown > summary { width: 100%; justify-content: space-between; } .label-apply { margin-top: 0.5rem; } .label-swatch { width: 0.8rem; height: 0.8rem; border-radius: 3px; background: var(--label-color, #888); display: inline-block; flex: none; } /* Repo settings danger zone. */ .danger-zone { border-color: var(--fb-danger); } .danger-zone p { margin-top: 0; } /* When the card holds only an explanatory paragraph (no delete form), drop the trailing margin so the card doesn't carry extra bottom padding. */ .danger-zone p:last-child { margin-bottom: 0; } /* Collaborators: a bordered list inside the card, then an add row. Each row is [avatar] [name] [role] on the left, remove button on the right. */ .collab-list { margin-bottom: 1rem; } .collab-list .entry-row { padding-left: 0; padding-right: 0; } /* The first row abuts the card padding; drop its own top padding so the list doesn't sit with a doubled gap at the top of the card. */ .collab-list .entry-row:first-child { padding-top: 0; } .collab-ident { display: flex; align-items: center; gap: 0.6rem; min-width: 0; /* Tight line boxes so the name and the role pill centre on each other. */ line-height: 1; } .collab-ident .entry-row-title { display: inline; } .collab-perm { font-size: 0.8em; color: var(--fb-fg-muted); border: 1px solid var(--fb-border); border-radius: 999px; padding: 0.05rem 0.55rem; text-transform: capitalize; } .collab-list .entry-row-actions .btn { padding: 0.25rem 0.6rem; font-size: 0.85em; } .collab-add { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: stretch; } .collab-add input[type="text"] { flex: 1; min-width: 12rem; } .collab-add select { width: auto; } .collab-add button { flex: none; } /* Repository language breakdown bar (a collapsible
). */ .lang-bar-wrap { margin: 0 0 1rem; } .lang-bar-summary { /* `display: block` drops the default disclosure triangle (and the indent it reserves) in Chromium; the list-style/marker resets cover other engines. */ display: block; list-style: none; cursor: pointer; } .lang-bar-summary::-webkit-details-marker { display: none; } .lang-bar-summary::marker { content: ""; } .lang-bar { display: flex; height: 0.6rem; border-radius: 999px; overflow: hidden; background: var(--fb-bg-inset); } .lang-seg { height: 100%; } .lang-seg:not(:last-child) { margin-right: 2px; } .lang-legend { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: 0.35rem 1.1rem; margin: 0.75rem 0 0; } .lang-legend li { display: flex; align-items: center; gap: 0.4rem; font-size: 0.9em; } .lang-dot { width: 0.7rem; height: 0.7rem; border-radius: 50%; flex: none; } .lang-legend .lang-name { font-weight: 600; } /* License summary banner shown above a LICENSE file. */ .license-banner { margin-bottom: 1rem; } .license-head { display: flex; align-items: center; gap: 0.75rem; } .license-head > .icon { width: 2rem; height: 2rem; flex: none; color: var(--fb-fg-muted); } .license-head h3 { margin: 0; } .license-eyebrow { margin: 0; font-size: 0.85em; } .license-desc { margin: 0.75rem 0; } .license-cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; padding-top: 0.75rem; border-top: 1px solid var(--fb-border); } .license-col h4 { margin: 0 0 0.5rem; font-size: 0.9rem; } .license-col ul { list-style: none; display: flex; flex-direction: column; gap: 0.3rem; } .license-col li { display: flex; align-items: center; gap: 0.4rem; } .license-col li .icon { width: 0.95rem; height: 0.95rem; flex: none; } .license-col li.ok .icon { color: var(--fb-success); } .license-col li.no .icon { color: var(--fb-danger); } .license-col li.cond .icon { color: var(--fb-accent); } .license-note { margin: 0.75rem 0 0; font-size: 0.82em; } @media (max-width: 40rem) { .license-cols { grid-template-columns: 1fr; } } /* Issue/PR dependency lists in the sidebar. */ .dep-list { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 0.25rem; margin: 0 0 0.75rem; } .dep-row { display: flex; align-items: center; gap: 0.45rem; font-size: 0.9em; padding: 0.3rem 0.5rem; border-radius: var(--fb-radius); } .dep-row:hover { background: var(--fb-bg-inset); } .dep-row > .icon { flex: none; } .dep-row > a { flex: 1; min-width: 0; color: var(--fb-fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .dep-row > a:hover { text-decoration: none; color: var(--fb-accent); } .dep-row .icon-btn { width: 1.4rem; height: 1.4rem; flex: none; color: var(--fb-fg-muted); } .dep-row .icon-btn:hover { color: var(--fb-danger); } .dep-add { display: flex; gap: 0.4rem; margin-top: 0.25rem; } .dep-add input { flex: 1; min-width: 0; } /* Pagination controls for long lists. */ .pagination { display: flex; align-items: center; gap: 0.75rem; margin-top: 1rem; flex-wrap: wrap; } .pagination .btn.disabled { opacity: 0.5; pointer-events: none; } .pagination .per-page { display: flex; align-items: center; gap: 0.4rem; } .pagination .per-page label { display: flex; align-items: center; gap: 0.4rem; margin: 0; font-weight: 400; color: var(--fb-fg-muted); } .pagination .per-page select { width: auto; } .pagination-page { margin-left: auto; } /* Admin dashboard: stat grid and management lists. */ .stat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); gap: 0.75rem; } .stat-card { padding: 1rem; background: var(--fb-bg-raised); border: 1px solid var(--fb-border); border-radius: var(--fb-radius); } .stat-value { font-size: 1.6rem; font-weight: 600; } .stat-label { font-size: 0.85em; } .admin-actions { flex-wrap: wrap; gap: 0.35rem; } /* The card already pads its edges, so admin list rows drop their own side padding and align with the create form below them. */ .admin-list .entry-row { padding-left: 0; padding-right: 0; } /* Separate a populated list from the create form directly beneath it. */ .admin-list:not(:empty) + form { margin-top: 1rem; } .admin-form-row { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 0.5rem; } .admin-form-row input, .admin-form-row select { flex: 1; min-width: 9rem; } /* Give every control the same explicit height so inputs, selects, and the inline submit button line up exactly (native selects render taller than a button would otherwise, so flex `stretch` alone is not enough). */ .admin-form-row > input, .admin-form-row > select, .admin-form-row > button { height: 2.4rem; } /* A control row that is the last thing in its form carries no trailing gap. */ .admin-form-row-last { margin-bottom: 0; } /* A bottom row pairing option toggles/checkboxes with the submit button. */ .admin-form-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 0.75rem; margin-top: 0.5rem; } .admin-form-actions label.checkbox { margin: 0; } /* Stacked label + control groups (admin settings). */ .form-field { margin-bottom: 0.9rem; } .form-field label { margin-top: 0; } /* Empty-state line above a create form: keep it snug against the form. */ .empty-note { margin: 0 0 0.75rem; } /* Mirror settings: the last-error text on a configured mirror. */ .mirror-error { color: var(--fb-danger); } /* Separate the "Add a push mirror" sub-form from the list of configured mirrors. */ .mirror-add { margin-top: 1.25rem; padding-top: 1.25rem; border-top: 1px solid var(--fb-border); } .mirror-add h3 { margin-top: 0; } .mirror-add .admin-form-actions { margin-top: 1rem; } /* Settings: a left tab rail and the active tab's content. */ .settings-layout { display: flex; gap: 2rem; align-items: flex-start; } .settings-nav { flex: none; width: 13rem; position: sticky; top: calc(var(--fb-topbar-h) + 1rem); } .settings-nav h1 { font-size: 1.4rem; margin-bottom: 0.75rem; } .settings-nav nav { display: flex; flex-direction: column; gap: 0.15rem; } .settings-nav nav a { padding: 0.4rem 0.7rem; border-radius: var(--fb-radius); color: var(--fb-fg-muted); } .settings-nav nav a:hover { background: var(--fb-bg-inset); text-decoration: none; } .settings-nav nav a[aria-current="page"] { background: var(--fb-bg-inset); color: var(--fb-fg); font-weight: 600; } .settings-content { flex: 1; min-width: 0; } .settings-content .listing:first-child { margin-top: 0; } .email-list { margin-bottom: 0; } .badge-verified { color: var(--fb-success); border-color: var(--fb-success); } .badge-unverified { color: var(--fb-fg-muted); border-color: var(--fb-border); } .email-visibility { margin: 0; padding: 0.85rem 0; border-top: 1px solid var(--fb-border); border-bottom: 1px solid var(--fb-border); } .email-visibility label.checkbox { margin: 0; } .email-add { margin-top: 1rem; } .email-add label { margin-top: 0; } .email-add-row { display: flex; gap: 0.5rem; align-items: stretch; } .email-add-row input { flex: 1; min-width: 12rem; } @media (max-width: 48rem) { /* Stack the rail above the content. align-items must reset to stretch or the content column shrinks to its widest child and overflows the viewport. */ .settings-layout { flex-direction: column; align-items: stretch; gap: 1rem; } .settings-content { width: 100%; } .settings-nav { width: 100%; position: static; } /* The tab rail scrolls horizontally (like the repo tabs) instead of wrapping onto several rows. */ .settings-nav nav { flex-direction: row; flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; } .settings-nav nav a { flex: 0 0 auto; } /* Stat cards fill the width in as many columns as fit. */ .stat-grid { grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr)); } } label { display: block; margin: 0.75rem 0 0.25rem; font-weight: 600; } /* The first label in a card form abuts the card padding — drop its top margin so the form doesn't sit with a doubled gap at the top. The CSRF hidden input is usually the real first child, so also match the label right after it. */ .card form > label:first-child, .card form > input[type="hidden"]:first-child + label { margin-top: 0; } input[type="text"], input[type="password"], input[type="email"], input[type="search"], input[type="url"], input[type="number"], textarea, select { width: 100%; padding: 0.5rem 0.75rem; font: inherit; color: var(--fb-fg); background: var(--fb-bg-inset); border: 1px solid var(--fb-border); border-radius: var(--fb-radius); } /* Replace the native select arrow (which ignores padding and hugs the border) with a token-coloured caret drawn from two gradients — no literal colours, and given breathing room from the edge. */ select { appearance: none; -webkit-appearance: none; padding-right: 2rem; background-image: linear-gradient(45deg, transparent 50%, var(--fb-fg-muted) 50%), linear-gradient(135deg, var(--fb-fg-muted) 50%, transparent 50%); background-position: right 1.05rem center, right 0.7rem center; background-size: 0.35rem 0.35rem; background-repeat: no-repeat; } select:disabled { background-image: none; } input:disabled, textarea:disabled, select:disabled { opacity: 0.6; cursor: not-allowed; } input:focus, textarea:focus, select:focus { border-color: var(--fb-accent); outline: none; } /* Submit buttons in a card form get breathing room from the last field — except buttons that sit inline in a control row (they must not be nudged down out of alignment with their input). */ .card form button[type="submit"]:not(.inline-btn) { margin-top: 1.1rem; } .field-hint { margin: 0.25rem 0 0.5rem; font-size: 0.85em; } /* API token settings. */ .token-secret { margin: 0.5rem 0 0; padding: 0.6rem 0.85rem; overflow-x: auto; background: var(--fb-bg-inset); border-radius: var(--fb-radius); } .token-list { margin-bottom: 1rem; } .key-list { margin-bottom: 1.25rem; } .key-list .entry-row { align-items: center; } .key-list .entry-row-title { display: flex; align-items: center; gap: 0.5rem; } .key-list .entry-row-meta { margin-top: 0.2rem; font-size: 0.85em; } .key-verify { margin-top: 0.5rem; } .key-verify > summary { cursor: pointer; color: var(--fb-accent); font-size: 0.9em; } .key-verify textarea { font-family: var(--fb-font-mono); font-size: 0.85em; } .key-challenge { padding: 0.6rem 0.75rem; background: var(--fb-bg-inset); border-radius: var(--fb-radius); overflow-x: auto; white-space: pre-wrap; } .key-add { padding-top: 1rem; border-top: 1px solid var(--fb-border); } .key-add textarea { font-family: var(--fb-font-mono); font-size: 0.88em; } .key-add-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 0.6rem; } .key-add-row label { margin: 0; } .key-add-row select { width: auto; } .key-add-row input { flex: 1; min-width: 12rem; } .token-scopes { margin: 0.75rem 0 0; padding: 0.5rem 0.85rem; border: 1px solid var(--fb-border); border-radius: var(--fb-radius); } .token-scopes legend { padding: 0 0.35rem; font-size: 0.9em; font-weight: 600; } input[type="checkbox"], input[type="radio"] { accent-color: var(--fb-accent); width: 1.05rem; height: 1.05rem; cursor: pointer; } label.checkbox { display: inline-flex; align-items: center; gap: 0.45rem; margin: 0 1rem 0 0; font-weight: 400; cursor: pointer; } label.checkbox input { width: 1.05rem; flex: none; } /* Link slots stack full-width; the Add button sits beside the last one. */ .link-fields { display: flex; flex-wrap: wrap; align-items: stretch; gap: 0.5rem; } .link-slot { flex: 1 1 100%; } /* Concrete basis (not auto) so the global input width:100% doesn't claim the whole row and wrap the Add button below. */ .link-slot.last-visible { flex: 1 1 12rem; } .link-add { flex: 0 0 auto; } /* Inline form rows (links, avatar upload, collaborators): every control gets the same explicit height so inputs, selects, and buttons line up exactly. */ .collab-add > input, .collab-add > select, .collab-add > button, .avatar-upload-row > input[type="file"], .avatar-upload-row > button, .link-slot, .link-add { height: 2.4rem; } .avatar-upload-row input[type="file"] { padding-top: 0; padding-bottom: 0; } /* Without JS every slot shows; the enhancement hides empties behind Add. */ [hidden] { display: none !important; } /* New-repository form: a comfortably narrow, centered column. */ .new-repo { max-width: 40rem; margin: 0 auto; } /* Centered auth card (sign in, invite activation). */ .auth-wrap { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: calc(100vh - var(--fb-topbar-h) - 4rem); padding: 1rem 0; } .auth-card { width: 100%; max-width: 22rem; background: var(--fb-bg-raised); border: 1px solid var(--fb-border); border-radius: var(--fb-radius); padding: 1.75rem; } .auth-card h1 { text-align: center; margin-bottom: 1rem; } .auth-card form .btn { width: 100%; justify-content: center; margin-top: 1.25rem; } .auth-alt { margin: 1rem 0 0; text-align: center; font-size: 0.9em; } /* ---- Flash / notices ---- */ .notice { padding: 0.6rem 0.8rem; border-radius: var(--fb-radius); border: 1px solid var(--fb-border); background: var(--fb-bg-inset); margin-bottom: 0.75rem; } .notice-error { border-color: var(--fb-danger); } .notice-success { border-color: var(--fb-success); } /* ---- Badges ---- */ .badge { display: inline-flex; align-items: center; gap: 0.25rem; padding: 0.05rem 0.45rem; font-size: 0.8em; border-radius: var(--fb-radius); border: 1px solid var(--fb-border); } .badge-verified { color: var(--fb-success); border-color: var(--fb-success); } .badge-unverified { color: var(--fb-warning); border-color: var(--fb-warning); } .badge-private { color: var(--fb-fg-muted); /* An explicit muted border: Carbon's --fb-border equals --fb-bg-inset, so the default badge border vanishes against a hovered inset row. */ border-color: var(--fb-fg-muted); } .badge-default { color: var(--fb-accent); border-color: var(--fb-accent); } .badge-archived { color: var(--fb-warning); border-color: var(--fb-warning); } /* Sign-up invite status: unused is neutral; used (redeemed) reads as done. */ .badge-unused { color: var(--fb-fg-muted); border-color: var(--fb-fg-muted); } .badge-used { color: var(--fb-success); border-color: var(--fb-success); } /* Inline search row: input grows, button sits to its right with margin. */ .search-row { display: flex; align-items: stretch; gap: 0.6rem; margin: 0.75rem 0 1.25rem; } .search-row input[type="search"] { flex: 1; } .search-row .btn { flex: none; } /* ---- Repo/list rows ---- */ .listing { margin-bottom: 1.5rem; } .repo-list { list-style: none; margin: 0; padding: 0; } .repo-list li { padding: 0.75rem 0; border-bottom: 1px solid var(--fb-border); } .repo-list .name { font-weight: 600; font-size: 1.05em; } /* Listing card: bordered rows inside a single card. */ .repo-list.card { padding: 0; overflow: hidden; } .repo-list.card li { padding: 0; border-bottom: 1px solid var(--fb-border); } .repo-list.card li:last-child { border-bottom: none; } .repo-row { display: flex; flex-direction: column; gap: 0.15rem; padding: 0.75rem 1rem; color: var(--fb-fg); } .repo-row:hover { background: var(--fb-bg-inset); text-decoration: none; } .repo-row-name { display: inline-flex; align-items: center; gap: 0.45rem; font-weight: 600; max-width: 100%; } /* Truncate a long repo/group path so it never pushes past the card width; the trailing visibility/archived badges stay put (they are not the name span). */ .repo-row-name > span:not(.badge) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; } .repo-row-name > .icon, .repo-row-name > .badge { flex: none; } .repo-row-name .icon { color: var(--fb-fg-muted); } .repo-row-desc { font-size: 0.9em; } .repo-row-meta { font-size: 0.82em; } /* User directory rows (Explore → Users). */ .user-row { display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 1rem; color: var(--fb-fg); } .user-row:hover { background: var(--fb-bg-inset); text-decoration: none; } .user-row .avatar { width: 2.5rem; height: 2.5rem; flex: none; } .user-row-main { display: flex; flex-direction: column; min-width: 0; } .user-row-name { font-weight: 600; } .user-row-meta { font-size: 0.85em; } /* ---- Dashboard (activity + repo sidebar) ---- */ .dashboard { display: flex; gap: 1.5rem; align-items: flex-start; } .dashboard-main { flex: 1; min-width: 0; } .dashboard-side { flex: none; width: 20rem; } .dashboard-side .search-row { margin-top: 0; } /* Contribution heatmap. */ .heatmap-wrap { overflow-x: auto; padding-bottom: 0.25rem; } .heatmap { display: block; width: 100%; height: auto; } .hm-label { fill: var(--fb-fg-muted); font-size: 9px; font-family: var(--fb-font-sans); } .hm-cell { fill: var(--fb-bg-inset); } .hm-cell[data-level="1"] { fill: color-mix(in srgb, var(--fb-accent) 30%, var(--fb-bg-inset)); } .hm-cell[data-level="2"] { fill: color-mix(in srgb, var(--fb-accent) 55%, var(--fb-bg-inset)); } .hm-cell[data-level="3"] { fill: color-mix(in srgb, var(--fb-accent) 78%, var(--fb-bg-inset)); } .hm-cell[data-level="4"] { fill: var(--fb-accent); } .heatmap-caption { margin: 0.25rem 0 1.5rem; font-size: 0.85em; } /* Recent-activity feed. */ .activity-feed { list-style: none; margin: 0; padding: 0; } .activity-item { padding: 0.75rem 1rem; border-bottom: 1px solid var(--fb-border); } .activity-item:last-child { border-bottom: none; } .activity-head { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; font-size: 0.9em; } .activity-time { margin-left: auto; } .activity-commit { display: flex; gap: 0.6rem; margin-top: 0.35rem; align-items: baseline; color: var(--fb-fg); } .activity-commit:hover { color: var(--fb-accent); text-decoration: none; } .commit-sha { flex: none; color: var(--fb-fg-muted); font-size: 0.85em; } .commit-summary { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* ---- Avatars & profile header ---- */ .avatar { border-radius: 6px; object-fit: cover; background: var(--fb-bg-inset); border: 1px solid var(--fb-border); } .avatar-sm { width: 1.75rem; height: 1.75rem; border-radius: 5px; } .avatar-lg { width: 5rem; height: 5rem; border-radius: 8px; } .avatar-xl { width: 100%; max-width: 16rem; aspect-ratio: 1; height: auto; border-radius: 10px; } /* Two-column profile: info sidebar on the left, repositories on the right. */ .profile-layout { display: flex; gap: 1.5rem; align-items: flex-start; } .profile-sidebar { flex: none; width: 19rem; } .profile-main { flex: 1; min-width: 0; } .profile-card { display: flex; flex-direction: column; gap: 0.5rem; } .profile-card .avatar-xl { align-self: center; } .profile-name { margin: 0.5rem 0 0; font-size: 1.5rem; text-align: center; } .profile-handle { margin: 0; text-align: center; } /* Follower/following counts and the follow button in the profile sidebar. */ .profile-follow { display: flex; align-items: center; justify-content: center; gap: 0.35rem; margin: 0.5rem 0 0; font-size: 0.9em; } .profile-follow a { color: inherit; } .profile-follow a:hover strong { color: var(--fb-accent); } .profile-follow-btn { display: block; text-align: center; margin: 0.75rem 0 0.25rem; } /* A list of users (followers/following) as follow-cards. */ .user-list { list-style: none; margin: 0; padding: 0; } .user-row { display: flex; align-items: flex-start; gap: 0.75rem; padding: 0.85rem 1rem; border-bottom: 1px solid var(--fb-border); } .user-row:last-child { border-bottom: none; } .user-row .avatar { width: 3rem; height: 3rem; flex: none; } .user-row-body { flex: 1; min-width: 0; } .user-row-name { font-weight: 600; color: var(--fb-fg); } .user-row-bio, .user-row-loc { margin: 0.2rem 0 0; font-size: 0.9em; } .user-row-bio > :first-child { margin-top: 0; } .user-row-bio > :last-child { margin-bottom: 0; } .user-row > .btn { flex: none; align-self: center; } .profile-pronouns { padding: 0.05rem 0.5rem; border: 1px solid var(--fb-border); border-radius: var(--fb-radius); font-size: 0.82em; } .profile-bio { margin: 0.5rem 0; padding-top: 0.75rem; border-top: 1px solid var(--fb-border); } .profile-bio :first-child { margin-top: 0; } .profile-bio :last-child { margin-bottom: 0; } .profile-links { list-style: none; margin: 0; padding: 0.75rem 0 0; border-top: 1px solid var(--fb-border); display: flex; flex-direction: column; gap: 0.4rem; } .profile-links li { display: inline-flex; align-items: center; gap: 0.5rem; min-width: 0; } .profile-links li .icon { flex: none; color: var(--fb-fg-muted); } .profile-links a { display: inline-flex; align-items: center; gap: 0.5rem; min-width: 0; color: var(--fb-accent); } .profile-links a span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .profile-joined { margin: 0; padding-top: 0.75rem; border-top: 1px solid var(--fb-border); font-size: 0.9em; } .avatar-settings { display: flex; align-items: center; gap: 1.25rem; flex-wrap: wrap; } .avatar-form { flex: 1; min-width: 15rem; } .avatar-upload-row { display: flex; align-items: stretch; gap: 0.75rem; flex-wrap: wrap; margin-top: 0.6rem; } .avatar-form input[type="file"] { flex: 0 1 auto; min-width: 0; max-width: 100%; padding: 0.4rem; background: var(--fb-bg-inset); border: 1px solid var(--fb-border); border-radius: var(--fb-radius); color: var(--fb-fg); } .avatar-upload-row button[type="submit"] { flex: none; } /* ---- Search hits ---- */ .search-repo { margin: 1.5rem 0 0.6rem; font-family: var(--fb-font-mono); font-size: 0.95rem; } .search-repo a { color: var(--fb-fg); } .search-repo a:hover { color: var(--fb-accent); } .search-hit { padding: 0; overflow: hidden; margin-bottom: 0.75rem; } .search-hit-path { padding: 0.55rem 0.85rem; font-weight: 600; border-bottom: 1px solid var(--fb-border); } .search-hit-lines { margin: 0; padding: 0.4rem 0; background: var(--fb-bg-inset); font-family: var(--fb-font-mono); font-size: 0.85rem; overflow-x: auto; } .search-hit-line { display: flex; gap: 1rem; padding: 0 0.85rem; white-space: pre; } .search-hit-line .lineno { flex: none; min-width: 2.5rem; text-align: right; color: var(--fb-fg-muted); user-select: none; } .search-hit-line code { white-space: pre; } /* ---- Tables ---- */ table.list { width: 100%; border-collapse: collapse; } table.list th, table.list td { text-align: left; padding: 0.5rem 0.6rem; border-bottom: 1px solid var(--fb-border); } table.list th { color: var(--fb-fg-muted); font-weight: 600; } /* ---- Code ---- */ pre.code { margin: 0; padding: 0.75rem 1rem; overflow-x: auto; background: var(--fb-bg-inset); border-radius: var(--fb-radius); } /* ---- Rendered markdown (README, bios) ---- */ .markdown { overflow-wrap: break-word; } .markdown > :first-child { margin-top: 0; } .markdown > :last-child { margin-bottom: 0; } .markdown h1, .markdown h2 { padding-bottom: 0.3em; border-bottom: 1px solid var(--fb-border); margin: 1.5em 0 1rem; } .markdown h3, .markdown h4 { margin: 1.4em 0 0.8rem; } .markdown p, .markdown ul, .markdown ol, .markdown blockquote, .markdown table, .markdown pre { margin: 0 0 1rem; } .markdown ul, .markdown ol { padding-left: 1.5rem; } .markdown a { text-decoration: underline; } .markdown blockquote { padding: 0 1rem; color: var(--fb-fg-muted); border-left: 3px solid var(--fb-border); } /* Inline code: a subtle chip. */ .markdown code { padding: 0.15em 0.4em; font-size: 0.9em; background: var(--fb-bg-inset); border: 1px solid var(--fb-border); border-radius: 5px; } /* Fenced blocks: a clearly delimited panel; the inner code resets the chip. */ .markdown pre { padding: 0.85rem 1rem; overflow-x: auto; background: var(--fb-bg-inset); border: 1px solid var(--fb-border); border-radius: var(--fb-radius); line-height: 1.45; } .markdown pre code { padding: 0; font-size: 0.9em; background: none; border: none; border-radius: 0; } .markdown table { border-collapse: collapse; } .markdown th, .markdown td { padding: 0.4rem 0.75rem; border: 1px solid var(--fb-border); } .markdown th { background: var(--fb-bg-inset); } .markdown img { max-width: 100%; } .markdown hr { border: none; border-top: 1px solid var(--fb-border); margin: 1.5rem 0; } .readme { margin-top: 1rem; } /* Highlight classes — coloured entirely by the theme's --fb-hl-* tokens. */ .hl-keyword { color: var(--fb-hl-keyword); } .hl-function { color: var(--fb-hl-function); } .hl-type { color: var(--fb-hl-type); } .hl-string { color: var(--fb-hl-string); } .hl-number { color: var(--fb-hl-number); } .hl-comment { color: var(--fb-hl-comment); font-style: italic; } .hl-constant { color: var(--fb-hl-constant); } .hl-variable { color: var(--fb-hl-variable); } .hl-operator { color: var(--fb-hl-operator); } .hl-punctuation { color: var(--fb-hl-punctuation); } .hl-attribute { color: var(--fb-hl-attribute); } .hl-tag { color: var(--fb-hl-tag); } /* ---- Error page ---- */ .error-page { text-align: center; padding: 5rem 1rem; } .error-page .code { font-size: 4rem; font-weight: 700; color: var(--fb-fg-muted); } .error-page .req-id { font-size: 0.85em; color: var(--fb-fg-muted); } /* ---- Repo views ---- */ .repo-head { margin-bottom: 1.25rem; } /* Group listing header: slug on the left, admin actions on the right. */ .group-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.25rem; } .group-head-actions { display: flex; align-items: center; gap: 0.5rem; } /* Icon-only buttons (e.g. the settings gear) must match the height of the text buttons beside them: a bare 1em icon is shorter than a text line box, so pin both the icon and the button's line-height to the same value. */ .group-head-actions .btn { line-height: 1.25rem; } .group-head-actions .btn .icon { width: 1.25rem; height: 1.25rem; } /* "forked from owner/repo" line under the repo slug. */ .fork-note { display: flex; align-items: center; gap: 0.3rem; font-size: 0.9em; margin: 0.2rem 0 0; } .repo-slug { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; font-size: 1.4rem; font-weight: 400; /* Tight line-height so the small private pill centres on the text's optical centre rather than the tall default line box. */ line-height: 1; } .repo-slug a { color: var(--fb-accent); } .slug-sep { color: var(--fb-fg-muted); } .repo-slug .badge { font-size: 0.7rem; padding: 0.12rem 0.55rem; align-self: center; /* Nudge down: flex-centring sits on the em-box centre, which is above the optical centre of the lowercase slug text. */ position: relative; top: 0.12rem; } /* Tab strips (repo sub-nav, explore sub-nav): active-pill style. */ .repo-tabs, .profile-tabs, .subnav { display: flex; gap: 0.5rem; flex-wrap: wrap; margin: 0.75rem 0 1.25rem; } .profile-tabs { margin-top: 0; } /* Active toggle button (e.g. a bookmarked repo). */ .btn-active { color: var(--fb-accent); border-color: var(--fb-accent); } /* Inline #n / @user references in rendered markdown. */ .issue-ref, .user-ref { font-weight: 600; } /* "View all N repositories" link under the dashboard sidebar list. */ .dashboard-viewall { margin: 0.6rem 0 0; font-size: 0.9em; } /* Releases. */ .releases-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1rem; } .back-link { font-size: 0.9em; } /* Breadcrumb (Releases / name / Edit). */ .crumbs { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; margin-bottom: 1rem; font-size: 0.95em; } .crumbs a { color: var(--fb-accent); } .crumb-current { color: var(--fb-fg-muted); } .release { margin-bottom: 1.25rem; } .release-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; } .release-title { margin: 0; font-size: 1.4rem; } .release-title a { color: var(--fb-fg); } .release-meta { display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; margin-top: 0.35rem; font-size: 0.9em; } .release-actions { display: flex; align-items: center; gap: 0.5rem; flex: none; } /* The Delete button sits in a card form, so it would otherwise inherit the stacked-form top margin and float below the Edit link. Specificity here must beat `.card form button[type="submit"]:not(.inline-btn)`. */ .card .release-actions form button[type="submit"]:not(.inline-btn) { margin-top: 0; } /* Asset management sits below the edit form; give it room to breathe. */ .release-assets-editor { margin-top: 1.5rem; } .release-notes { margin: 1rem 0; padding-top: 1rem; border-top: 1px solid var(--fb-border); } .release-assets h3 { margin: 1rem 0 0.5rem; } .asset-list { list-style: none; margin: 0; padding: 0; border: 1px solid var(--fb-border); border-radius: var(--fb-radius); } .asset-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.55rem 0.85rem; border-bottom: 1px solid var(--fb-border); } .asset-row:last-child { border-bottom: none; } .asset-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; } .asset-row.source .asset-name { font-weight: 400; } .asset-size { flex: none; font-size: 0.85em; } .asset-upload { margin-top: 0.75rem; } /* A dashed drop target that also opens the file picker on click (the file input is visually hidden inside the label but stays keyboard- and click-reachable). */ .dropzone { display: flex; align-items: center; justify-content: center; gap: 0.5rem; padding: 1.5rem; border: 1px dashed var(--fb-border); border-radius: var(--fb-radius); color: var(--fb-fg-muted); text-align: center; cursor: pointer; } .dropzone:hover, .dropzone.drag { border-color: var(--fb-accent); color: var(--fb-fg); } .dropzone-input { position: absolute; width: 1px; height: 1px; padding: 0; opacity: 0; pointer-events: none; } .asset-upload .btn { margin-top: 0.75rem; } /* The per-asset delete button sits in a card form; kill the stacked-form top margin so it centres in the row, and keep it compact. Specificity beats `.card form button[type="submit"]:not(.inline-btn)`. */ .card .asset-list .asset-row form button[type="submit"] { margin-top: 0; } .asset-row .icon-btn { width: 1.75rem; height: 1.75rem; } .release-form-actions { display: flex; align-items: center; gap: 0.75rem; margin-top: 1rem; } /* Notifications inbox. */ .notif-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1rem; } .notif-list { list-style: none; margin: 0; padding: 0; } .notif-row { display: flex; align-items: center; gap: 0.75rem; padding: 0.7rem 1rem; border-bottom: 1px solid var(--fb-border); } .notif-row:last-child { border-bottom: none; } .notif-row.unread { background: var(--fb-bg-inset); } .notif-icon { flex: none; color: var(--fb-fg-muted); } .notif-row.unread .notif-icon { color: var(--fb-accent); } .notif-body { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex: 1; min-width: 0; color: var(--fb-fg); } .notif-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .notif-time { flex: none; font-size: 0.85em; } .repo-tabs a, .profile-tabs a, .subnav a { display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.4rem 0.9rem; border-radius: var(--fb-radius); color: var(--fb-fg-muted); } .repo-tabs a:hover, .profile-tabs a:hover, .subnav a:hover { color: var(--fb-fg); background: var(--fb-bg-inset); text-decoration: none; } .repo-tabs a[aria-current="page"], .profile-tabs a[aria-current="page"], .subnav a[aria-current="page"] { color: var(--fb-fg); background: var(--fb-bg-inset); font-weight: 600; } .subnav { justify-content: center; } /* Tabs share their line with the branch/commits/clone actions. */ .repo-nav { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; } .repo-nav .repo-tabs { margin: 0.75rem 0 0; flex: 1; } .repo-actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; } .repo-codesearch { margin: 1rem 0 1.25rem; } /* Clone dropdown: a no-JS details/summary popover. */ .clone-menu { position: relative; } .clone-menu > summary { list-style: none; cursor: pointer; } .clone-menu > summary::-webkit-details-marker { display: none; } .clone-pop { position: absolute; right: 0; top: calc(100% + 0.35rem); z-index: 30; width: 24rem; max-width: 88vw; display: flex; flex-direction: column; gap: 0.75rem; box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35); } .clone-field-label { display: block; margin-bottom: 0.3rem; font-size: 0.78em; font-weight: 600; } .clone-input { display: flex; gap: 0.4rem; } .clone-input input { font-family: var(--fb-font-mono); font-size: 0.8em; } /* Branch switcher dropdown. */ .branch-menu { position: relative; } .branch-menu > summary { list-style: none; cursor: pointer; } .branch-menu > summary::-webkit-details-marker { display: none; } .branch-menu > summary .icon:last-child { margin-left: 0.1rem; } .branch-pop { position: absolute; left: 0; top: calc(100% + 0.35rem); z-index: 30; width: 17rem; max-width: 88vw; max-height: 20rem; overflow-y: auto; padding: 0.35rem; box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35); } .branch-pop-head { padding: 0.3rem 0.5rem; font-size: 0.78em; font-weight: 600; } .branch-pop a { display: block; padding: 0.35rem 0.5rem; border-radius: var(--fb-radius); color: var(--fb-fg); font-family: var(--fb-font-mono); font-size: 0.88em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .branch-pop a:hover { background: var(--fb-bg-inset); text-decoration: none; } .branch-pop a[aria-current="page"] { color: var(--fb-accent); font-weight: 600; } .branch-pop-all { margin-top: 0.25rem; border-top: 1px solid var(--fb-border); color: var(--fb-accent) !important; font-family: var(--fb-font-sans) !important; } .breadcrumb { margin-bottom: 0.75rem; font-family: var(--fb-font-mono); } /* File tree: a bordered card of compact rows. */ .file-tree { padding: 0; overflow: hidden; } .file-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.4rem 0.85rem; color: var(--fb-fg); border-bottom: 1px solid var(--fb-border); } .file-row:last-child { border-bottom: none; } .file-row:hover { background: var(--fb-bg-inset); text-decoration: none; } .file-icon { display: inline-flex; color: var(--fb-fg-muted); } .file-row.is-dir .file-icon { color: var(--fb-accent); } .file-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* A small pill beside a tree entry's name (LFS-tracked file, submodule). */ .tree-pill { flex: none; padding: 0.02rem 0.4rem; font-size: 0.72em; font-weight: 600; letter-spacing: 0.03em; color: var(--fb-fg-muted); border: 1px solid var(--fb-border); border-radius: 999px; } .lfs-pill { color: var(--fb-accent); border-color: var(--fb-accent); } /* Blob header left group: file size with an optional LFS pill beside it. */ .blob-meta { display: flex; align-items: center; gap: 0.5rem; } .file-row.is-submodule .file-icon { color: var(--fb-accent); } .blob-header { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; flex-wrap: wrap; padding: 0.5rem 0.75rem; background: var(--fb-bg-raised); border: 1px solid var(--fb-border); border-bottom: none; border-radius: var(--fb-radius) var(--fb-radius) 0 0; } .blob-actions { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; } .blob-header .btn { padding: 0.3rem 0.6rem; font-size: 0.85em; } .blob-header .btn.active { border-color: var(--fb-accent); color: var(--fb-accent); } /* Rendered markdown preview continues the blob-header box (no seam). */ .blob-preview { padding: 1.25rem 1.5rem; background: var(--fb-bg-raised); border: 1px solid var(--fb-border); border-top: none; border-radius: 0 0 var(--fb-radius) var(--fb-radius); } /* Inline image preview: flush under the header (no baseline gap), clipped to the rounded bottom corners. */ .blob-image { background: var(--fb-bg-inset); border: 1px solid var(--fb-border); border-top: none; border-radius: 0 0 var(--fb-radius) var(--fb-radius); overflow: hidden; line-height: 0; } .blob-image img { display: block; max-width: 100%; margin: 0 auto; } /* The blob box: border, rounded bottom, scroll, and vertical breathing room. */ .blob-code { border: 1px solid var(--fb-border); border-top: none; border-radius: 0 0 var(--fb-radius) var(--fb-radius); background: var(--fb-bg-inset); overflow-x: auto; padding: 0.5rem 0; } table.blob { width: 100%; border-collapse: collapse; font-family: var(--fb-font-mono); font-size: 0.85rem; } table.blob td.lineno { width: 1%; min-width: 3rem; text-align: right; padding: 0 0.75rem; color: var(--fb-fg-muted); user-select: none; vertical-align: top; } table.blob td.lineno a { color: inherit; } table.blob td.code-line { padding: 0 0.75rem; white-space: pre; tab-size: 4; } table.blob tr:target { background: var(--fb-diff-add-bg); } /* ---- Entry rows (commits, branches, tags) ---- */ .entry-list { list-style: none; margin: 0; padding: 0; } .entry-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.7rem 1rem; border-bottom: 1px solid var(--fb-border); } .entry-row:last-child { border-bottom: none; } .entry-row-body { min-width: 0; } .entry-row-title { display: block; font-weight: 600; color: var(--fb-fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .entry-row-title:hover { color: var(--fb-accent); } .entry-row-meta { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-top: 0.15rem; font-size: 0.85em; } .entry-row-meta .ahead { color: var(--fb-success); } /* Branch list meta: bullet-separate the segments (counts · updated · author). Ahead/behind stay grouped in `.branch-counts`, so they read as one segment. */ .branch-meta > * + *::before { content: "·"; margin-right: 0.4rem; color: var(--fb-fg-muted); } .branch-counts { display: inline-flex; gap: 0.35rem; } .entry-row-actions { display: flex; align-items: center; gap: 0.5rem; flex: none; } /* Action-row submit buttons must not inherit the stacked-form top margin, or they float out of vertical alignment with the row. This selector is kept more specific than `.card form button[type="submit"]:not(.inline-btn)`. */ .card .entry-row .entry-row-actions form button[type="submit"] { margin-top: 0; } .commit-sha-pill { padding: 0.1rem 0.45rem; background: var(--fb-bg-inset); border: 1px solid var(--fb-border); border-radius: 5px; font-size: 0.82em; color: var(--fb-fg); } .commit-sha-pill:hover { border-color: var(--fb-accent); text-decoration: none; } .commit-copy { width: 1.75rem; height: 1.75rem; } /* Verified-signature hover popover in the commits list. */ .sig-pop-wrap { position: relative; display: inline-flex; } .sig-pop { position: absolute; right: 0; top: calc(100% + 0.35rem); z-index: 40; width: 21rem; max-width: 88vw; display: none; text-align: left; white-space: normal; cursor: default; box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35); } .sig-pop-wrap:hover .sig-pop, .sig-pop-wrap:focus-within .sig-pop { display: block; } .sig-pop-title { font-weight: 600; margin-bottom: 0.6rem; } .sig-pop-signer { margin-bottom: 0.6rem; } .sig-pop-fp { font-size: 0.85em; color: var(--fb-fg-muted); margin-bottom: 0.4rem; } .sig-pop-fp .mono { display: block; margin-top: 0.15rem; color: var(--fb-fg); word-break: break-all; } .sig-pop-when { font-size: 0.85em; } /* Commit signature detail bar. */ .sig-detail { display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; padding: 0.5rem 0.85rem; margin-bottom: 1rem; border: 1px solid var(--fb-border); border-radius: var(--fb-radius); font-size: 0.9em; } .sig-verified { border-color: var(--fb-success); } .sig-verified .sig-signer { color: var(--fb-success); } .sig-unverified { border-color: var(--fb-warning); } .sig-fp { color: var(--fb-fg-muted); word-break: break-all; } /* ---- Commit view & diffs ---- */ .commit-meta { margin-bottom: 1rem; } .commit-title-row { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; } .commit-title { margin: 0; min-width: 0; } .commit-title-meta { margin: 0; font-size: 0.85em; text-align: right; } .commit-body { margin: 0.5rem 0; padding: 0.5rem 0.75rem; background: var(--fb-bg-inset); border-radius: var(--fb-radius); white-space: pre-wrap; font-family: var(--fb-font-mono); font-size: 0.85rem; } .diff-toolbar { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.75rem; } .diff-toggle { display: inline-flex; gap: 0.5rem; } .diff-toggle .btn { padding: 0.2rem 0.6rem; } .diff-toggle .btn.active { border-color: var(--fb-accent); color: var(--fb-accent); } .changed-files { display: flex; flex-direction: column; gap: 0.15rem; padding: 0.5rem 0.75rem; margin-bottom: 1rem; background: var(--fb-bg-raised); border: 1px solid var(--fb-border); border-radius: var(--fb-radius); font-family: var(--fb-font-mono); font-size: 0.85rem; } .diff-file { margin-bottom: 1rem; border: 1px solid var(--fb-border); border-radius: var(--fb-radius); overflow: hidden; } .diff-file > summary { padding: 0.5rem 0.75rem; background: var(--fb-bg-raised); cursor: pointer; } table.diff { width: 100%; border-collapse: collapse; font-family: var(--fb-font-mono); font-size: 0.82rem; background: var(--fb-bg-inset); } table.diff td { padding: 0 0.5rem; white-space: pre-wrap; word-break: break-word; vertical-align: top; } table.diff td.lineno { width: 1%; min-width: 2.5rem; text-align: right; color: var(--fb-fg-muted); user-select: none; white-space: nowrap; } table.diff td.diff-marker { width: 1ch; text-align: center; user-select: none; } tr.diff-add, td.diff-add { background: var(--fb-diff-add-bg); } tr.diff-add td.diff-marker, tr.diff-add td.code-line { color: var(--fb-diff-add-fg); } tr.diff-del, td.diff-del { background: var(--fb-diff-del-bg); } tr.diff-del td.diff-marker, tr.diff-del td.code-line { color: var(--fb-diff-del-fg); } tr.hunk-header td { background: var(--fb-bg-raised); color: var(--fb-fg-muted); } .diff-expander td { background: var(--fb-bg-raised); text-align: center; padding: 0.15rem; } .expand-btn { background: none; border: none; color: var(--fb-accent); cursor: pointer; font: inherit; } td.diff-empty { background: var(--fb-bg-raised); } @media (max-width: 48rem) { /* Force unified on narrow screens (§9.6). */ table.diff-split { display: block; overflow-x: auto; } } /* ---- Responsive: single breakpoint at 48rem ---- */ @media (max-width: 48rem) { .topbar .tabs { margin: 0; } .page { padding: 0.75rem; } /* The body never scrolls horizontally; wide content scrolls within itself. */ body { overflow-x: hidden; } /* Comfortable touch targets (>= 44px) for the primary interactive elements. */ .topbar .tabs a, .repo-tabs a, .drawer nav a, .drawer .drawer-item, .btn, .icon-btn { min-height: 44px; display: inline-flex; align-items: center; } .icon-btn { justify-content: center; min-width: 44px; } /* Stack the repo sub-nav: the tab strip scrolls horizontally on its own row (rather than wrapping into a tall column), with the branch/clone actions on the row below instead of crowding the tabs. */ .repo-nav { flex-direction: column; align-items: stretch; } /* min-width:0 lets the strip shrink below its content so overflow-x:auto actually scrolls, instead of the tabs pushing the whole page wider than the viewport. The specificity matches the base `.repo-nav .repo-tabs`. */ .repo-nav .repo-tabs { flex: 1 1 auto; min-width: 0; max-width: 100%; flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; } .repo-tabs a { flex: 0 0 auto; } /* Branch switcher and Clone split the row 50/50, full width. */ .repo-actions { width: 100%; } .repo-actions > * { flex: 1; min-width: 0; } .repo-actions summary.btn, .repo-actions > a.btn { width: 100%; justify-content: center; } /* List rows stack so a wide action cluster drops below the body instead of squeezing it to an unreadable sliver (admin users, keys, emails, commits). */ .entry-row, .key-list .entry-row { flex-direction: column; align-items: stretch; gap: 0.5rem; } .entry-row-actions { flex-wrap: wrap; } /* Long unbroken meta (key fingerprints, emails) wraps rather than overflowing. */ .entry-row-meta { overflow-wrap: anywhere; } /* Diff tables scroll within their own box (the blob wrapper already does). */ table.diff { display: block; overflow-x: auto; } /* Stack the profile, dashboard, and issue columns on narrow screens. The containers use align-items other than stretch when side-by-side, so reset it here or the main column shrinks to its content and overflows. */ .profile-layout, .dashboard, .issue-main { flex-direction: column; align-items: stretch; } .profile-sidebar, .dashboard-side, .issue-side, .profile-main, .dashboard-main { width: 100%; min-width: 0; } }