crates/cli/ents-web/src/assets/ents.css
ents.csshistorycomment on this file
| 1 | /* The workbench stylesheet -- the git-ents "Design Component" handoff |
| 2 | * (design_handoff_git_ents_workbench) recreated as one hand-rolled sheet, |
| 3 | * keyed to the class taxonomy `crate::pages::layout*` and `crate::render` |
| 4 | * already emit (`.wb`/`.rail`/`.wb-bar`/`.split`/`.tree`/`.card`/`.scope`/ |
| 5 | * `.status`/`.editor-open`/`.blob`/`.comment-meta` ...). The design's exact |
| 6 | * tokens (color, geometry, type) live in `:root` below; a page changes its |
| 7 | * markup only where a component needs structure the shared classes here |
| 8 | * cannot express (an avatar, a state pill's dot, a two-line sidebar row) -- |
| 9 | * those additive classes are defined here too, so every page draws from one |
| 10 | * vocabulary. Type is self-hosted IBM Plex (see the `@font-face` block and |
| 11 | * `crate::assets::FONTS`); no rule here depends on a network fetch. |
| 12 | * |
| 13 | * The `--s-*` syntax-token variables feed `.code .keyword`-family consumers |
| 14 | * (`crate::pages::files`'s `arborium` highlighter); the `--diff-*` and |
| 15 | * `--status-*` variables feed `.diff` and `.status` |
| 16 | * (`crate::pages::commits`). */ |
| 17 | |
| 18 | @font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 400; font-display: swap; src: url("/fonts/plex-sans-400.woff2") format("woff2"); } |
| 19 | @font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 500; font-display: swap; src: url("/fonts/plex-sans-500.woff2") format("woff2"); } |
| 20 | @font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 600; font-display: swap; src: url("/fonts/plex-sans-600.woff2") format("woff2"); } |
| 21 | @font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 700; font-display: swap; src: url("/fonts/plex-sans-700.woff2") format("woff2"); } |
| 22 | @font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; font-display: swap; src: url("/fonts/plex-mono-400.woff2") format("woff2"); } |
| 23 | @font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500; font-display: swap; src: url("/fonts/plex-mono-500.woff2") format("woff2"); } |
| 24 | @font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 600; font-display: swap; src: url("/fonts/plex-mono-600.woff2") format("woff2"); } |
| 25 | |
| 26 | /* The handoff's light scheme -- its exact token values. */ |
| 27 | :root { |
| 28 | --bg: #f4f2ee; |
| 29 | --surface: #ffffff; |
| 30 | --surface-2: #faf9f6; |
| 31 | --raised: #fbfaf7; |
| 32 | --ink: #1b1c21; |
| 33 | --ink-2: #54555d; |
| 34 | --ink-3: #8b8c94; |
| 35 | --ink-4: #b6b6bd; |
| 36 | --line: #e6e3dd; |
| 37 | --line-2: #efece7; |
| 38 | --accent: #4a3fc4; |
| 39 | --accent-2: #6a5fe0; |
| 40 | --accent-soft: #ecebfb; |
| 41 | --accent-line: #d8d5f6; |
| 42 | --editor: #0d8f9c; |
| 43 | --editor-soft: #dff3f3; |
| 44 | --editor-line: #b9e3e2; |
| 45 | --green: #1c7a49; |
| 46 | --green-soft: #e3f2e8; |
| 47 | --red: #c23b34; |
| 48 | --red-soft: #fae9e7; |
| 49 | --amber: #a9791d; |
| 50 | --amber-soft: #f6efdd; |
| 51 | --violet: #7a3fb0; |
| 52 | --violet-soft: #f1e8f8; |
| 53 | |
| 54 | --sans: "IBM Plex Sans", system-ui, -apple-system, sans-serif; |
| 55 | --mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace; |
| 56 | |
| 57 | --radius-card: 12px; |
| 58 | --radius-inner: 8px; |
| 59 | --radius-chip: 6px; |
| 60 | --radius-pill: 20px; |
| 61 | |
| 62 | --shadow-pop: 0 24px 60px rgba(20, 20, 30, .28); |
| 63 | --shadow-toast: 0 12px 30px rgba(20, 20, 30, .32); |
| 64 | |
| 65 | /* Deterministic entity/scope chip palette (README: 6 colors, hashed by |
| 66 | * name), consumed by `.scope-c0`..`.scope-c5`. */ |
| 67 | --c0-fg: #4a3fc4; --c0-bg: #ecebfb; |
| 68 | --c1-fg: #0d8f9c; --c1-bg: #dff3f3; |
| 69 | --c2-fg: #1c7a49; --c2-bg: #e3f2e8; |
| 70 | --c3-fg: #a9791d; --c3-bg: #f6efdd; |
| 71 | --c4-fg: #b23c8c; --c4-bg: #f8e8f3; |
| 72 | --c5-fg: #c23b34; --c5-bg: #fae9e7; |
| 73 | |
| 74 | /* Syntax tokens, tuned to the paper palette. */ |
| 75 | --s-comment: #8b8c94; |
| 76 | --s-keyword: #a3324f; |
| 77 | --s-func: #0d8f9c; |
| 78 | --s-type: #a9791d; |
| 79 | --s-string: #1c7a49; |
| 80 | --s-const: #7a3fb0; |
| 81 | --s-op: #54555d; |
| 82 | --s-prop: #4a3fc4; |
| 83 | --diff-add: rgba(28, 122, 73, .10); |
| 84 | --diff-del: rgba(194, 59, 52, .09); |
| 85 | --status-pass: #1c7a49; |
| 86 | --status-fail: #c23b34; |
| 87 | --status-error: #a9791d; |
| 88 | } |
| 89 | |
| 90 | /* Dark scheme (README: "dark is a roadmap item -- design both"), derived |
| 91 | * from the light palette: the same hues over an ink ground. */ |
| 92 | @media (prefers-color-scheme: dark) { |
| 93 | :root { |
| 94 | --bg: #16161a; |
| 95 | --surface: #1d1e23; |
| 96 | --surface-2: #232429; |
| 97 | --raised: #26272d; |
| 98 | --ink: #ecedf2; |
| 99 | --ink-2: #b7b8c0; |
| 100 | --ink-3: #85868f; |
| 101 | --ink-4: #5b5c65; |
| 102 | --line: #303138; |
| 103 | --line-2: #292a30; |
| 104 | --accent: #9d95f5; |
| 105 | --accent-2: #b3abff; |
| 106 | --accent-soft: #262353; |
| 107 | --accent-line: #3a3672; |
| 108 | --editor: #3fc6d2; |
| 109 | --editor-soft: #16333650; |
| 110 | --editor-line: #2c5a5e; |
| 111 | --green: #5cc98b; |
| 112 | --green-soft: #143a2650; |
| 113 | --red: #f0796f; |
| 114 | --red-soft: #3a1e1c50; |
| 115 | --amber: #d9a94a; |
| 116 | --amber-soft: #37301c50; |
| 117 | --violet: #c79be6; |
| 118 | --violet-soft: #2c1f3a50; |
| 119 | --c0-fg: #9d95f5; --c0-bg: #262353; |
| 120 | --c1-fg: #3fc6d2; --c1-bg: #16333650; |
| 121 | --c2-fg: #5cc98b; --c2-bg: #143a2650; |
| 122 | --c3-fg: #d9a94a; --c3-bg: #37301c50; |
| 123 | --c4-fg: #e58fc4; --c4-bg: #3a1f3350; |
| 124 | --c5-fg: #f0796f; --c5-bg: #3a1e1c50; |
| 125 | --s-comment: #85868f; |
| 126 | --s-keyword: #f0796f; |
| 127 | --s-func: #3fc6d2; |
| 128 | --s-type: #d9a94a; |
| 129 | --s-string: #5cc98b; |
| 130 | --s-const: #c79be6; |
| 131 | --s-op: #b7b8c0; |
| 132 | --s-prop: #9d95f5; |
| 133 | --diff-add: rgba(92, 201, 139, .13); |
| 134 | --diff-del: rgba(240, 121, 111, .13); |
| 135 | --status-pass: #5cc98b; |
| 136 | --status-fail: #f0796f; |
| 137 | --status-error: #d9a94a; |
| 138 | --shadow-pop: 0 24px 60px rgba(0, 0, 0, .55); |
| 139 | --shadow-toast: 0 12px 30px rgba(0, 0, 0, .6); |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } |
| 144 | html { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } |
| 145 | body { |
| 146 | font-family: var(--sans); |
| 147 | font-size: 14px; |
| 148 | line-height: 1.5; |
| 149 | background: var(--bg); |
| 150 | color: var(--ink); |
| 151 | min-height: 100vh; |
| 152 | text-rendering: optimizeLegibility; |
| 153 | } |
| 154 | h1, h2, h3, h4, h5, h6 { font-family: var(--sans); font-weight: 600; } |
| 155 | a { color: var(--accent); text-decoration: none; } |
| 156 | a:hover { color: var(--accent-2); text-decoration: underline; } |
| 157 | /* Every bare `<code>` (a short oid, a CLI command name, ...) reads as a |
| 158 | * small code chip by default; contexts that already carry their own |
| 159 | * monospace flow (`.blob-code`, `.doc-body`) reset it back below. */ |
| 160 | code { font-family: var(--mono); font-size: .9em; background: var(--surface-2); border: 1px solid var(--line); border-radius: 5px; padding: 1px 6px; } |
| 161 | ::selection { background: var(--accent-soft); } |
| 162 | a:focus-visible, button:focus-visible, input:focus-visible, textarea:focus-visible, select:focus-visible, summary:focus-visible { |
| 163 | outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; |
| 164 | } |
| 165 | .icon { flex-shrink: 0; fill: none; stroke: currentColor; vertical-align: -0.125em; } |
| 166 | .muted { color: var(--ink-3); } |
| 167 | ::-webkit-scrollbar { width: 10px; height: 10px; } |
| 168 | ::-webkit-scrollbar-thumb { background: var(--line); border-radius: 8px; border: 3px solid var(--bg); } |
| 169 | |
| 170 | /* ============================ SHELL ============================ * |
| 171 | * `crate::pages::layout_shell`: a `.wb` row of the fixed 60px icon |
| 172 | * `.rail` and a `.wb-main` column whose sticky 56px `.wb-bar` carries the |
| 173 | * repo name + branch pill, the `.palette` search, and the `.id-chip`. */ |
| 174 | .wb { display: flex; min-height: 100vh; width: 100%; } |
| 175 | .rail { |
| 176 | position: sticky; top: 0; align-self: flex-start; height: 100vh; width: 60px; flex: none; |
| 177 | background: var(--surface); border-right: 1px solid var(--line); |
| 178 | display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 12px 0; z-index: 40; |
| 179 | } |
| 180 | .rail .nav-mark { |
| 181 | width: 34px; height: 34px; border-radius: 9px; background: var(--ink); color: var(--surface); |
| 182 | display: flex; align-items: center; justify-content: center; |
| 183 | font-family: var(--mono); font-weight: 600; font-size: 15px; letter-spacing: -1px; margin-bottom: 10px; |
| 184 | } |
| 185 | .rail a { |
| 186 | display: flex; align-items: center; justify-content: center; width: 42px; height: 42px; |
| 187 | border-radius: 11px; color: var(--ink-3); |
| 188 | } |
| 189 | .rail a:hover { background: var(--surface-2); color: var(--ink); text-decoration: none; } |
| 190 | .rail a.active { background: var(--accent-soft); color: var(--accent); } |
| 191 | .rail a .icon { width: 19px; height: 19px; stroke-width: 1.7; } |
| 192 | .rail .spacer { flex: 1; } |
| 193 | |
| 194 | .wb-main { flex: 1; min-width: 0; display: flex; flex-direction: column; } |
| 195 | .wb-bar { |
| 196 | position: sticky; top: 0; z-index: 30; height: 56px; flex: none; |
| 197 | background: var(--surface); border-bottom: 1px solid var(--line); |
| 198 | display: flex; align-items: center; gap: 16px; padding: 0 18px; |
| 199 | } |
| 200 | .repo-path { display: flex; align-items: center; gap: 9px; flex: none; min-width: 0; } |
| 201 | .repo-path .here { font-weight: 600; font-size: 14px; color: var(--ink); } |
| 202 | .branch { |
| 203 | display: inline-flex; align-items: center; gap: 5px; height: 24px; padding: 0 9px; |
| 204 | background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius-pill); |
| 205 | font-family: var(--mono); font-size: 11.5px; color: var(--ink-2); |
| 206 | } |
| 207 | .branch .icon { width: 12px; height: 12px; stroke-width: 2; } |
| 208 | /* `.palette` is a form: undo the stacked-field form rules (column, gap, |
| 209 | * bottom margin). Styled as the design's centered palette trigger. */ |
| 210 | .palette { |
| 211 | flex: 1; max-width: 520px; margin: 0 auto; height: 34px; |
| 212 | display: flex; flex-direction: row; align-items: center; gap: 10px; margin-bottom: 0; |
| 213 | padding: 0 12px; background: var(--surface-2); border: 1px solid var(--line); |
| 214 | border-radius: 9px; position: relative; |
| 215 | } |
| 216 | .palette:focus-within { border-color: var(--accent); } |
| 217 | .palette .icon { width: 15px; height: 15px; stroke-width: 1.8; color: var(--ink-3); flex: none; } |
| 218 | .palette input { |
| 219 | flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; |
| 220 | font-family: var(--sans); font-size: 13px; color: var(--ink); |
| 221 | } |
| 222 | .palette input::placeholder { color: var(--ink-3); } |
| 223 | .palette kbd { |
| 224 | font-family: var(--mono); font-size: 11px; color: var(--ink-3); |
| 225 | background: var(--surface); border: 1px solid var(--line); border-bottom-width: 2px; |
| 226 | border-radius: 5px; padding: 2px 6px; flex: none; |
| 227 | } |
| 228 | .id-chip { |
| 229 | flex: none; display: inline-flex; align-items: center; gap: 8px; height: 34px; |
| 230 | padding: 0 12px 0 6px; border: 1px solid var(--line); border-radius: var(--radius-pill); |
| 231 | color: var(--ink); font-size: 12.5px; font-weight: 500; |
| 232 | } |
| 233 | .id-chip:hover { border-color: var(--accent-line); color: var(--ink); text-decoration: none; } |
| 234 | .id-chip .avatar { width: 24px; height: 24px; font-size: 11px; } |
| 235 | |
| 236 | /* Initials avatar on the indigo->teal gradient (README: no image assets). */ |
| 237 | .avatar { |
| 238 | display: inline-flex; align-items: center; justify-content: center; flex: none; |
| 239 | width: 20px; height: 20px; border-radius: 50%; |
| 240 | background: linear-gradient(135deg, var(--accent), var(--editor)); |
| 241 | color: #fff; font-size: 9px; font-weight: 600; text-transform: lowercase; letter-spacing: -.02em; |
| 242 | } |
| 243 | |
| 244 | /* ========================= META RAIL ========================= * |
| 245 | * `crate::pages::layout_meta` -- the governance sub-rail beside every |
| 246 | * meta-namespace page (`crate::pages::META_SECTIONS`). */ |
| 247 | .meta-layout { display: grid; grid-template-columns: 13rem minmax(0, 1fr); gap: 2rem; align-items: start; } |
| 248 | .meta-rail { display: flex; flex-direction: column; gap: 2px; position: sticky; top: 78px; } |
| 249 | .meta-rail a { font-size: 13px; color: var(--ink-2); padding: 7px 11px; border-radius: var(--radius-inner); } |
| 250 | .meta-rail a:hover { color: var(--ink); background: var(--surface-2); text-decoration: none; } |
| 251 | .meta-rail a.active { color: var(--accent); font-weight: 600; background: var(--accent-soft); } |
| 252 | @media (max-width: 900px) { |
| 253 | .meta-layout { grid-template-columns: minmax(0, 1fr); } |
| 254 | .meta-rail { position: static; flex-direction: row; flex-wrap: wrap; } |
| 255 | } |
| 256 | |
| 257 | /* ===================== CONTENT / SPLIT ===================== */ |
| 258 | .content { max-width: 1120px; width: 100%; margin: 0 auto; padding: 30px 34px 60px; flex: 1; } |
| 259 | .readable { max-width: 760px; margin-inline: auto; } |
| 260 | /* Master-detail (`crate::pages::layout_split`): a sticky scrollable |
| 261 | * sidebar beside a `min-height:0` detail pane. */ |
| 262 | .split { display: grid; grid-template-columns: 300px minmax(0, 1fr); height: calc(100vh - 56px); } |
| 263 | .tree { |
| 264 | border-right: 1px solid var(--line); background: var(--surface); |
| 265 | overflow: auto; position: sticky; top: 0; height: 100%; padding: 10px 8px; |
| 266 | font-family: var(--mono); font-size: 12px; |
| 267 | } |
| 268 | .tree a { |
| 269 | display: flex; align-items: center; gap: 7px; height: 28px; padding: 0 10px; |
| 270 | border-radius: 7px; color: var(--ink-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; |
| 271 | } |
| 272 | .tree a:hover { background: var(--surface-2); text-decoration: none; } |
| 273 | .tree a.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; } |
| 274 | .tree a span { display: block; overflow: hidden; text-overflow: ellipsis; } |
| 275 | .tree a .icon { width: 13px; height: 13px; stroke-width: 1.7; color: var(--ink-3); } |
| 276 | .tree .dir { color: var(--ink-2); } |
| 277 | .tree .i1 { padding-left: 24px; } |
| 278 | .tree .i2 { padding-left: 38px; } |
| 279 | .tree .i3 { padding-left: 52px; } |
| 280 | .tree-note { display: block; padding: 4px 10px; color: var(--ink-3); } |
| 281 | .tree .where { font-size: 11px; font-weight: 400; color: var(--ink-3); } |
| 282 | .pane { padding: 20px 28px 70px; min-width: 0; overflow: auto; } |
| 283 | .pane .readable, .pane > div { max-width: 820px; margin: 0 auto; } |
| 284 | |
| 285 | /* A generic two-line master-detail sidebar row (issues, reviews, commit |
| 286 | * lane): a title line and a mono `.side-meta` locator, its active state the |
| 287 | * design's accent-soft fill + inset 3px bar. Sits inside `nav.tree`, so it |
| 288 | * opts out of the tree's own flex-row link layout. */ |
| 289 | .tree a.side-row { |
| 290 | display: flex; flex-direction: column; align-items: stretch; gap: 4px; height: auto; |
| 291 | padding: 11px 15px; border-radius: 0; border-bottom: 1px solid var(--line-2); |
| 292 | font-family: var(--sans); white-space: normal; |
| 293 | } |
| 294 | .tree a.side-row.active { background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--accent); color: inherit; } |
| 295 | .side-title { font-size: 13px; font-weight: 500; color: var(--ink); line-height: 1.35; text-wrap: pretty; } |
| 296 | .side-row.active .side-title { font-weight: 600; } |
| 297 | .side-meta { display: flex; align-items: center; gap: 6px; } |
| 298 | .side-meta .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--ink-4); } |
| 299 | .side-meta .locator { font-family: var(--mono); font-size: 11px; color: var(--ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } |
| 300 | .tree-head { |
| 301 | display: flex; align-items: center; justify-content: space-between; gap: 8px; |
| 302 | padding: 13px 15px; border-bottom: 1px solid var(--line-2); |
| 303 | position: sticky; top: 0; background: var(--surface); z-index: 2; |
| 304 | font-family: var(--sans); font-size: 13px; font-weight: 600; |
| 305 | } |
| 306 | @media (max-width: 900px) { |
| 307 | .split { grid-template-columns: minmax(0, 1fr); height: auto; } |
| 308 | .tree { position: static; height: auto; max-height: 16rem; border-right: none; border-bottom: 1px solid var(--line); } |
| 309 | .pane { height: auto; } |
| 310 | } |
| 311 | |
| 312 | /* ===================== PAGE HEADER ===================== */ |
| 313 | /* Bare `h2` and `p.muted` in page flow are the section-heading and |
| 314 | * lead-in idiom (mock: 14px/600 with 26px above; muted lead-ins breathe |
| 315 | * instead of sitting flush on the card below). */ |
| 316 | .content h2, .pane h2 { margin: 26px 0 12px; font-size: 14px; font-weight: 600; } |
| 317 | .content h2:first-child, .pane h2:first-child { margin-top: 0; } |
| 318 | p.muted { margin-bottom: 16px; font-size: 13px; text-wrap: pretty; } |
| 319 | .page-header { |
| 320 | margin-bottom: 22px; padding-bottom: 16px; border-bottom: 1px solid var(--line); |
| 321 | display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; |
| 322 | } |
| 323 | .page-title { font-size: 22px; font-weight: 600; letter-spacing: -.3px; line-height: 1.25; } |
| 324 | /* A page-title that is itself a repository path (a tree/blob view -- the |
| 325 | * `.crumbs` trail right under it names the exact same path) -- monospace so |
| 326 | * it reads as the path it is, not prose competing with the crumb trail for |
| 327 | * the same information. */ |
| 328 | .page-title.path { font-family: var(--mono); font-size: 18px; word-break: break-all; } |
| 329 | .page-sub { margin-top: 5px; color: var(--ink-3); font-size: 13px; } |
| 330 | .page-header .mono { font-family: var(--mono); color: var(--ink-2); } |
| 331 | /* A `.crumbs` trail directly under a `.page-header` reads as its subtitle -- |
| 332 | * hug it to the title instead of the header's own bottom border/margin, and |
| 333 | * move that border/gap to below the crumbs, in front of the page's content, |
| 334 | * so the title and its path trail read as one grouped header. */ |
| 335 | .page-header:has(+ .crumbs) { margin-bottom: 6px; padding-bottom: 0; border-bottom: none; } |
| 336 | .page-header + .crumbs { margin-bottom: 22px; padding-bottom: 16px; border-bottom: 1px solid var(--line); } |
| 337 | |
| 338 | /* ========================= CARDS ========================= */ |
| 339 | .card { |
| 340 | background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-card); |
| 341 | overflow: hidden; margin-bottom: 18px; |
| 342 | } |
| 343 | .card-header { |
| 344 | display: flex; align-items: center; justify-content: space-between; gap: 10px; |
| 345 | padding: 13px 16px; border-bottom: 1px solid var(--line-2); |
| 346 | font-size: 13px; font-weight: 600; color: var(--ink); |
| 347 | } |
| 348 | .card-header .btn-ghost { margin-left: auto; } |
| 349 | /* A form as a card's body gets the mock composer's inner padding, its |
| 350 | * action row right-aligned as the mock's composer footer. */ |
| 351 | .card > form { padding: 18px 20px; margin-bottom: 0; } |
| 352 | .card > form .composer-buttons { justify-content: flex-end; } |
| 353 | .tree-head .btn { height: 24px; } |
| 354 | /* Stacked card rows (meta index): name over blurb, sans, left-aligned. */ |
| 355 | .card-row.stack { flex-direction: column; align-items: flex-start; gap: 2px; padding: 13px 16px; font-family: var(--sans); } |
| 356 | .card-row.stack a { flex: none; font-size: 13.5px; font-weight: 600; color: var(--accent); text-transform: capitalize; } |
| 357 | .card-row.stack span { font-size: 12.5px; color: var(--ink-3); } |
| 358 | .card-row { |
| 359 | display: flex; align-items: center; gap: 10px; padding: 9px 16px; |
| 360 | font-family: var(--mono); font-size: 12.5px; |
| 361 | } |
| 362 | .card-row + .card-row { border-top: 1px solid var(--line-2); } |
| 363 | .card-row .icon { color: var(--ink-3); stroke-width: 1.7; } |
| 364 | .card-row.is-dir .icon { color: var(--accent); } |
| 365 | .card-row a { color: var(--ink); flex: 1; min-width: 0; word-break: break-all; } |
| 366 | .card-row a:hover { color: var(--accent); } |
| 367 | .card-row a.row-link { display: flex; align-items: center; gap: 10px; text-decoration: none; } |
| 368 | .card-row:has(.row-link):hover { background: var(--surface-2); } |
| 369 | .entry-size { margin-left: auto; font-family: var(--mono); font-size: 11.5px; color: var(--ink-3); white-space: nowrap; } |
| 370 | |
| 371 | /* ========================= DASHBOARD ========================= * |
| 372 | * `crate::pages::dashboard`: three cards on a `.desk` grid, then a |
| 373 | * full-width `.desk-wide` history card. */ |
| 374 | .desk { display: grid; grid-template-columns: 1.15fr 1fr 1fr; gap: 18px; align-items: start; margin-bottom: 18px; } |
| 375 | .desk .card, .desk-wide .card { margin-bottom: 0; } |
| 376 | .desk-wide { grid-column: 1 / -1; } |
| 377 | .attention-row { display: flex; flex-direction: column; gap: 3px; padding: 10px 16px; color: inherit; } |
| 378 | .attention-row:hover { background: var(--surface-2); text-decoration: none; } |
| 379 | .attention-row + .attention-row { border-top: 1px solid var(--line-2); } |
| 380 | .attention-row .what { font-size: 12.5px; color: var(--ink); text-wrap: pretty; } |
| 381 | .attention-row .what .lead { color: var(--amber); font-weight: 500; } |
| 382 | .attention-row .where { font-family: var(--mono); font-size: 11px; color: var(--ink-3); } |
| 383 | .history .card-row { flex-wrap: nowrap; gap: 12px; } |
| 384 | .history .card-row a { flex: none; color: var(--accent); text-decoration: none; } |
| 385 | .history .card-row a:hover { text-decoration: underline; } |
| 386 | .desk-subject { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--sans); font-size: 13px; color: var(--ink); } |
| 387 | .desk-when { flex: none; font-size: 12px; color: var(--ink-3); } |
| 388 | /* Commit-row trailing author/when: sans, quiet, never wrapping (mock). */ |
| 389 | .row-author { flex: none; font-family: var(--sans); font-size: 12px; color: var(--ink-3); } |
| 390 | .row-when { flex: none; font-family: var(--sans); font-size: 12px; color: var(--ink-4); white-space: nowrap; } |
| 391 | @media (max-width: 1100px) { .desk { grid-template-columns: minmax(0, 1fr); } } |
| 392 | |
| 393 | /* ===================== BUTTONS ===================== */ |
| 394 | .btn { |
| 395 | display: inline-flex; align-items: center; gap: 6px; height: 34px; padding: 0 16px; |
| 396 | font-size: 13px; font-weight: 500; color: #fff; background: var(--accent); |
| 397 | border: none; border-radius: var(--radius-inner); cursor: pointer; |
| 398 | white-space: nowrap; flex: none; |
| 399 | } |
| 400 | .btn:hover { background: var(--accent-2); text-decoration: none; color: #fff; } |
| 401 | .btn-sm { height: 24px; padding: 0 10px; font-size: 11.5px; border-radius: var(--radius-chip); } |
| 402 | .btn-ghost { |
| 403 | color: var(--ink-2); background: var(--surface); border: 1px solid var(--line); |
| 404 | } |
| 405 | .btn-ghost:hover { border-color: var(--accent-line); color: var(--accent); background: var(--surface); } |
| 406 | |
| 407 | /* ===================== CHIPS & BADGES ===================== * |
| 408 | * One `.chip` base; state/verdict/check/scope variants layer color and, for |
| 409 | * state/verdict, a leading dot via `.chip .dot`. */ |
| 410 | .chip { |
| 411 | display: inline-flex; align-items: center; gap: 5px; height: 19px; padding: 0 8px; |
| 412 | border-radius: 5px; font-size: 10.5px; font-weight: 600; white-space: nowrap; |
| 413 | color: var(--ink-2); background: var(--surface-2); |
| 414 | } |
| 415 | .chip .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; } |
| 416 | /* Pill-shaped status chips carry more presence (issue/review state). */ |
| 417 | .chip-pill { height: 22px; padding: 0 10px; border-radius: var(--radius-pill); font-size: 11.5px; gap: 5px; } |
| 418 | |
| 419 | .badge { font-family: var(--mono); font-size: 10.5px; color: var(--ink-3); background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius-pill); padding: 1px 8px; } |
| 420 | |
| 421 | /* Issue state (`crate::pages::issues`). */ |
| 422 | .state-open, .comment-state-open { color: var(--green); background: var(--green-soft); } |
| 423 | .state-closed, .comment-state-resolved { color: var(--ink-2); background: var(--line-2); } |
| 424 | .state-in-progress { color: var(--amber); background: var(--amber-soft); } |
| 425 | /* Review state (`crate::pages::commits`/reviews). */ |
| 426 | .review-draft { color: var(--violet); background: var(--violet-soft); } |
| 427 | .review-in-review { color: var(--amber); background: var(--amber-soft); } |
| 428 | .review-approved { color: var(--green); background: var(--green-soft); } |
| 429 | .review-changes-requested { color: var(--red); background: var(--red-soft); } |
| 430 | .review-closed { color: var(--ink-2); background: var(--line-2); } |
| 431 | /* Review verdict badge. */ |
| 432 | .verdict { display: inline-flex; align-items: center; height: 19px; padding: 0 8px; border-radius: 5px; font-size: 10.5px; font-weight: 600; } |
| 433 | .verdict-approve { color: var(--green); background: var(--green-soft); } |
| 434 | .verdict-request-changes { color: var(--red); background: var(--red-soft); } |
| 435 | .verdict-comment { color: var(--ink-2); background: var(--line-2); } |
| 436 | |
| 437 | /* Check-status chips (`crate::pages::commits::checks_section`): a glyph |
| 438 | * plus the value, one color per pass/fail/error. */ |
| 439 | .status { display: inline-flex; align-items: center; gap: 4px; font-family: var(--mono); font-size: 10.5px; font-weight: 600; border-radius: 5px; padding: 2px 8px; } |
| 440 | .status::before { font-weight: 700; } |
| 441 | .status-pass { color: var(--status-pass); background: var(--green-soft); } |
| 442 | .status-pass::before { content: "\2713"; } |
| 443 | .status-fail { color: var(--status-fail); background: var(--red-soft); } |
| 444 | .status-fail::before { content: "\2715"; } |
| 445 | .status-error { color: var(--status-error); background: var(--amber-soft); } |
| 446 | .status-error::before { content: "!"; } |
| 447 | |
| 448 | /* Scope chips (`crate::pages::dashboard::scope_class`): deterministic |
| 449 | * color, fixed 52px width so commit subjects align (README). */ |
| 450 | .scope { |
| 451 | display: inline-flex; align-items: center; justify-content: center; flex: none; |
| 452 | width: 52px; height: 19px; border-radius: 5px; |
| 453 | font-family: var(--mono); font-size: 10px; font-weight: 600; white-space: nowrap; |
| 454 | overflow: hidden; text-overflow: ellipsis; |
| 455 | } |
| 456 | .scope-c0 { color: var(--c0-fg); background: var(--c0-bg); } |
| 457 | .scope-c1 { color: var(--c1-fg); background: var(--c1-bg); } |
| 458 | .scope-c2 { color: var(--c2-fg); background: var(--c2-bg); } |
| 459 | .scope-c3 { color: var(--c3-fg); background: var(--c3-bg); } |
| 460 | .scope-c4 { color: var(--c4-fg); background: var(--c4-bg); } |
| 461 | .scope-c5 { color: var(--c5-fg); background: var(--c5-bg); } |
| 462 | |
| 463 | /* ===================== EDITOR PILL ===================== * |
| 464 | * `crate::pages::editor_open` -- the signature web<->editor motif: a small |
| 465 | * teal pill deep-linking `file:line` into the serving user's editor. |
| 466 | * Rendered nothing at all when no editor is configured. */ |
| 467 | .editor-open { |
| 468 | display: inline-flex; align-items: center; gap: 3px; height: 20px; padding: 0 7px; |
| 469 | background: var(--editor-soft); border: 1px solid var(--editor-line); border-radius: var(--radius-chip); |
| 470 | color: var(--editor); font-family: var(--mono); font-size: 10.5px; font-weight: 600; white-space: nowrap; |
| 471 | } |
| 472 | .editor-open:hover { color: var(--editor); text-decoration: none; filter: brightness(.96); } |
| 473 | .editor-open .icon { width: 11px; height: 11px; stroke-width: 2.4; } |
| 474 | |
| 475 | /* ===================== ENTITY VIEWS ===================== * |
| 476 | * The reflection-driven views (`crate::render`): a definition list for one |
| 477 | * entity, a table for a list, a plain list for bare name lists. */ |
| 478 | .entity-view { font-family: var(--mono); font-size: 12.5px; } |
| 479 | .entity-view dt { padding: 12px 16px 2px; font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-3); } |
| 480 | .entity-view dd { padding: 0 16px 12px; word-break: break-all; color: var(--ink); } |
| 481 | .entity-view dt + dt { padding-top: 2px; } |
| 482 | |
| 483 | .entity-list { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 12px; } |
| 484 | .entity-list th, .entity-list td { padding: 10px 16px; text-align: left; } |
| 485 | .entity-list th { white-space: nowrap; } |
| 486 | .entity-list td.long-token { word-break: break-all; } |
| 487 | .entity-list thead { background: var(--surface-2); font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-3); } |
| 488 | .entity-list tbody tr + tr { border-top: 1px solid var(--line-2); } |
| 489 | .entity-list td a { color: var(--accent); } |
| 490 | .commits-table th:nth-child(1), .commits-table td:nth-child(1), |
| 491 | .commits-table th:nth-child(3), .commits-table td:nth-child(3), |
| 492 | .commits-table th:nth-child(4), .commits-table td:nth-child(4) { white-space: nowrap; } |
| 493 | |
| 494 | .string-list { list-style: none; font-family: var(--mono); font-size: 12.5px; } |
| 495 | .string-list li { display: flex; align-items: center; gap: 10px; padding: 10px 16px; } |
| 496 | .string-list li + li { border-top: 1px solid var(--line-2); } |
| 497 | .string-list a { color: var(--ink); } |
| 498 | .string-list a:hover { color: var(--accent); } |
| 499 | |
| 500 | /* Per-entity read failure (`crate::render::unreadable`). */ |
| 501 | .card-row.unreadable span { color: var(--ink-3); font-style: italic; } |
| 502 | .unreadable-detail { display: block; margin-top: 2px; color: var(--ink-3); font-style: normal; font-family: var(--mono); font-size: 11.5px; word-break: break-all; } |
| 503 | .unreadable-note { margin-bottom: 18px; } |
| 504 | .unreadable-note summary { |
| 505 | display: inline-flex; align-items: center; gap: 5px; font-family: var(--mono); font-size: 10.5px; font-weight: 600; |
| 506 | color: var(--amber); background: var(--amber-soft); border: 1px solid var(--editor-line); border-color: color-mix(in srgb, var(--amber) 30%, transparent); |
| 507 | border-radius: var(--radius-pill); padding: 2px 10px; cursor: pointer; list-style: none; user-select: none; -webkit-user-select: none; |
| 508 | } |
| 509 | .unreadable-note summary::-webkit-details-marker { display: none; } |
| 510 | .unreadable-note[open] summary { margin-bottom: 10px; } |
| 511 | .unreadable-note .card { margin-bottom: 0; } |
| 512 | .unreadable-note dd { color: var(--ink-3); font-size: 11.5px; } |
| 513 | |
| 514 | /* Bare dl/ul markup a custom page emits directly. */ |
| 515 | dl { font-family: var(--mono); font-size: 12.5px; margin-bottom: 18px; } |
| 516 | dl dt { font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-3); margin-top: 10px; } |
| 517 | dl dd { word-break: break-all; } |
| 518 | ul:not(.string-list):not(.doc-body ul) { list-style: none; font-family: var(--mono); font-size: 12.5px; margin-bottom: 18px; } |
| 519 | ul:not(.string-list):not(.doc-body ul) li { padding: 5px 0; } |
| 520 | |
| 521 | /* ===================== BLANKSLATE ===================== */ |
| 522 | .blankslate { |
| 523 | text-align: center; padding: 40px 24px; border: 1px dashed var(--line); |
| 524 | border-radius: var(--radius-card); background: var(--surface); |
| 525 | } |
| 526 | .blankslate h2 { font-size: 13.5px; font-weight: 600; color: var(--ink-2); margin: 0 0 4px; } |
| 527 | .blankslate p { color: var(--ink-3); font-size: 12.5px; } |
| 528 | .blankslate code { font-family: var(--mono); background: var(--surface-2); border: 1px solid var(--line); padding: 1px 6px; border-radius: 5px; font-size: 12px; } |
| 529 | .card .blankslate { border: none; } |
| 530 | |
| 531 | /* ===================== MEMBER CARDS ===================== * |
| 532 | * `crate::pages::members::member_card`: avatar + username + key-type/state/ |
| 533 | * provenance badges, key material truncated with a full-key disclosure. */ |
| 534 | .member-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 14px 16px; } |
| 535 | .member-name { font-family: var(--mono); font-weight: 600; font-size: 15px; color: var(--ink); } |
| 536 | a.member-name:hover { color: var(--accent); } |
| 537 | .key-badge { font-family: var(--mono); font-size: 10.5px; font-weight: 600; color: var(--editor); background: var(--editor-soft); border: 1px solid var(--editor-line); border-radius: 5px; padding: 1px 8px; white-space: nowrap; } |
| 538 | .member-key { padding: 0 16px 14px; font-family: var(--mono); font-size: 12px; } |
| 539 | .member-key code { color: var(--ink-3); background: var(--surface-2); border-radius: 5px; padding: 2px 6px; } |
| 540 | .member-key details { margin-top: 6px; } |
| 541 | .member-key summary { font-size: 11px; color: var(--ink-3); cursor: pointer; user-select: none; -webkit-user-select: none; } |
| 542 | .member-key pre { margin-top: 5px; padding: 9px 11px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius-inner); white-space: pre-wrap; word-break: break-all; font-size: 11px; } |
| 543 | |
| 544 | /* ===================== FORMS ===================== * |
| 545 | * A labeled field is a stacked label + control. Short token fields (`rev`, |
| 546 | * `lines`, `state`, `verdict`) stop short instead of stretching. */ |
| 547 | form { display: flex; flex-direction: column; gap: 15px; margin-bottom: 18px; } |
| 548 | form label { display: flex; flex-direction: column; gap: 6px; font-size: 12px; font-weight: 500; color: var(--ink-2); } |
| 549 | form input, form textarea, form select { |
| 550 | font-family: var(--mono); font-size: 13px; color: var(--ink); |
| 551 | background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius-inner); |
| 552 | padding: 9px 12px; width: 100%; |
| 553 | } |
| 554 | form input { height: 34px; } |
| 555 | form input:focus, form textarea:focus, form select:focus { border-color: var(--accent); outline: none; } |
| 556 | form input[name="rev"], form input[name="lines"], form input[name="state"], form input[name="verdict"] { max-width: 12rem; } |
| 557 | form textarea { resize: vertical; min-height: 120px; line-height: 1.55; font-family: var(--sans); } |
| 558 | form button { |
| 559 | align-self: flex-start; display: inline-flex; align-items: center; gap: 6px; height: 34px; padding: 0 18px; |
| 560 | font-size: 13px; font-weight: 500; color: #fff; background: var(--accent); |
| 561 | border: none; border-radius: var(--radius-inner); cursor: pointer; |
| 562 | } |
| 563 | form button:hover { background: var(--accent-2); } |
| 564 | |
| 565 | /* Segmented closed-enum picker (state/verdict/review-state): a row of |
| 566 | * buttons where the current option fills with its own chip color. */ |
| 567 | .picker { display: flex; flex-wrap: wrap; gap: 6px; } |
| 568 | .picker button, .picker .opt { |
| 569 | display: inline-flex; flex-direction: row; align-items: center; gap: 6px; height: 32px; padding: 0 13px; |
| 570 | border-radius: var(--radius-inner); cursor: pointer; font-size: 12.5px; font-weight: 500; |
| 571 | color: var(--ink-2); background: var(--surface-2); border: 1px solid var(--line); |
| 572 | } |
| 573 | .picker .opt .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-4); } |
| 574 | .picker .opt.active { font-weight: 600; } |
| 575 | |
| 576 | /* Label picker chips (`labelPicker`): pick existing or type-and-create. */ |
| 577 | .label-chip { |
| 578 | display: inline-flex; align-items: center; gap: 5px; height: 26px; padding: 0 10px; |
| 579 | border-radius: var(--radius-chip); cursor: pointer; font-family: var(--mono); font-size: 11px; |
| 580 | color: var(--ink-2); background: var(--surface-2); border: 1px solid var(--line); |
| 581 | } |
| 582 | .label-chip.on { color: var(--accent); background: var(--accent-soft); border-color: var(--accent-line); font-weight: 600; } |
| 583 | |
| 584 | /* ===================== DISCLOSURES ===================== * |
| 585 | * An edit affordance folded shut (issue edit, login-mapping edit): a |
| 586 | * bordered card-let whose summary is the whole closed state. */ |
| 587 | .disclosure { |
| 588 | background: var(--surface); border: 1px solid var(--line); border-radius: 10px; |
| 589 | overflow: hidden; margin-bottom: 12px; |
| 590 | } |
| 591 | .disclosure > summary { |
| 592 | padding: 11px 16px; cursor: pointer; list-style: none; user-select: none; -webkit-user-select: none; |
| 593 | font-size: 12.5px; font-weight: 500; color: var(--ink-2); |
| 594 | } |
| 595 | .disclosure > summary::-webkit-details-marker { display: none; } |
| 596 | .disclosure > summary:hover { color: var(--accent); } |
| 597 | .disclosure form { padding: 4px 16px 16px; margin-bottom: 0; } |
| 598 | |
| 599 | /* ===================== CRUMBS ===================== */ |
| 600 | .crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin-bottom: 16px; font-family: var(--mono); font-size: 12px; color: var(--ink-3); word-break: break-all; } |
| 601 | .crumbs a { color: var(--ink-3); } |
| 602 | .crumbs a:hover { color: var(--accent); } |
| 603 | .crumbs .sep { color: var(--ink-4); opacity: .8; display: inline-flex; } |
| 604 | .crumbs .sep .icon { width: 12px; height: 12px; } |
| 605 | .crumbs .here { color: var(--ink); } |
| 606 | |
| 607 | /* ===================== BLOB VIEW ===================== * |
| 608 | * `crate::pages::files`: a blob header bar over the line-per-row source |
| 609 | * table. */ |
| 610 | .blob-header { |
| 611 | position: relative; |
| 612 | display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; padding: 11px 15px; |
| 613 | background: var(--surface); border: 1px solid var(--line); border-bottom: none; |
| 614 | border-radius: var(--radius-inner) var(--radius-inner) 0 0; |
| 615 | } |
| 616 | .blob-header .icon { width: 15px; height: 15px; stroke-width: 1.7; color: var(--ink-3); } |
| 617 | .blob-title { font-family: var(--mono); font-size: 13px; font-weight: 600; color: var(--ink); } |
| 618 | .blob-meta { font-size: 12px; color: var(--ink-3); } |
| 619 | .blob-actions { margin-left: auto; display: flex; align-items: center; gap: 12px; font-size: 12.5px; color: var(--ink-3); } |
| 620 | .blob-actions a { color: var(--ink-2); } |
| 621 | .blob-actions a:hover { color: var(--accent); } |
| 622 | /* A "comment on this file/commit" trigger's popup (`assets/ents.js`): |
| 623 | * floats below its trigger's own header row instead of pushing the |
| 624 | * header/content box's own seamless border pairing apart. */ |
| 625 | .standalone-composer { position: absolute; top: calc(100% + 8px); right: 14px; z-index: 20; width: min(480px, calc(100vw - 60px)); } |
| 626 | .standalone-composer .composer-form { margin: 0; max-width: none; } |
| 627 | .blob { |
| 628 | overflow-x: auto; background: var(--surface); border: 1px solid var(--line); |
| 629 | border-radius: 0 0 var(--radius-inner) var(--radius-inner); margin-bottom: 18px; padding: 8px 0 10px; |
| 630 | } |
| 631 | /* A blob rendered without a header bar keeps all four corners rounded. */ |
| 632 | .blob:first-child { border-radius: var(--radius-inner); } |
| 633 | .blob table { border-collapse: collapse; width: 100%; font-family: var(--mono); font-size: 12.5px; line-height: 21px; } |
| 634 | .blob td.blob-nums { |
| 635 | position: sticky; left: 0; width: 52px; text-align: right; padding: 0 12px 0 0; vertical-align: top; |
| 636 | color: var(--ink-4); background: var(--surface); user-select: none; -webkit-user-select: none; white-space: nowrap; |
| 637 | } |
| 638 | .blob-nums a { display: block; color: inherit; padding: 0 .4ch; border-radius: 4px; cursor: pointer; } |
| 639 | .blob-nums a:hover { color: var(--accent); } |
| 640 | .blob-nums a:target { color: var(--accent); font-weight: 600; } |
| 641 | .blob td.blob-code { padding: 0 16px 0 4px; white-space: pre; color: var(--ink); vertical-align: top; } |
| 642 | .blob-code code { font-family: inherit; font-size: inherit; background: none; border: none; padding: 0; border-radius: 0; } |
| 643 | .blob tr.blob-comment-row td { padding: 0; background: var(--surface); } |
| 644 | .blob tr.blob-comment-row .card { margin: 5px 26px 7px 52px; } |
| 645 | .binary { padding: 40px; text-align: center; font-family: var(--mono); font-size: 12.5px; color: var(--ink-3); } |
| 646 | |
| 647 | /* Client-side line selection (`assets/ents.js`): `tr.sel` spans a |
| 648 | * clicked/shift-clicked range with the design's accent-soft + inset bar. */ |
| 649 | .blob tr.sel td { background: var(--accent-soft); } |
| 650 | .blob tr.sel td.blob-nums { color: var(--accent); font-weight: 600; box-shadow: inset 3px 0 0 var(--accent); } |
| 651 | .blob-add { |
| 652 | position: absolute; top: 50%; left: 6px; transform: translateY(-50%); |
| 653 | width: 17px; height: 17px; line-height: 16px; padding: 0; text-align: center; |
| 654 | font-size: 13px; font-weight: 600; color: #fff; background: var(--accent); |
| 655 | border: none; border-radius: 5px; opacity: 0; cursor: pointer; transition: opacity .1s; |
| 656 | } |
| 657 | .blob td.blob-nums { position: sticky; } /* keep the affordance's containing block */ |
| 658 | .blob tr:hover .blob-add, .blob-add:focus-visible { opacity: 1; } |
| 659 | |
| 660 | /* Inline comment composer (`assets/ents.js` clones `composer-template`). */ |
| 661 | .blob tr.blob-composer td { padding: 0; background: var(--surface); } |
| 662 | .composer-form { |
| 663 | margin: 5px 26px 8px 52px; max-width: 640px; background: var(--surface); border: 1px solid var(--accent); |
| 664 | border-radius: 9px; padding: 12px 13px; box-shadow: 0 4px 14px rgba(74, 63, 196, .1); |
| 665 | } |
| 666 | .composer-form textarea { min-height: 74px; } |
| 667 | .composer-buttons { display: flex; flex-direction: row; gap: 8px; } |
| 668 | .composer-buttons button { margin: 0; height: 28px; padding: 0 14px; } |
| 669 | .composer-cancel { |
| 670 | align-self: flex-start; height: 28px; padding: 0 12px; font-size: 12.5px; font-weight: 500; |
| 671 | color: var(--ink-2); background: var(--surface); border: 1px solid var(--line); |
| 672 | border-radius: 7px; cursor: pointer; |
| 673 | } |
| 674 | .composer-cancel:hover { color: var(--accent); border-color: var(--accent-line); } |
| 675 | |
| 676 | /* ===================== COMMITS ===================== */ |
| 677 | .commit { padding: 16px 18px 18px; } |
| 678 | .commit-subject { font-size: 19px; font-weight: 600; letter-spacing: -.2px; margin-bottom: 8px; } |
| 679 | /* The commit message body renders through `crate::asciidoc::to_html`, the |
| 680 | * same pipeline an issue body or a `.adoc` blob does -- `.doc-body`'s own |
| 681 | * element rules (headings, lists, code spans) apply, scaled down to this |
| 682 | * compact metadata context instead of a full document card's. */ |
| 683 | .commit-msg.doc-body { padding: 0; max-width: none; font-size: 13px; margin-bottom: 8px; } |
| 684 | .commit-meta { position: relative; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; color: var(--ink-3); font-size: 12px; font-family: var(--mono); margin-top: 6px; } |
| 685 | .commit-meta a { color: var(--ink-3); } |
| 686 | .commit-meta a:hover { color: var(--accent); } |
| 687 | |
| 688 | /* ===================== COMMENT CARDS ===================== * |
| 689 | * `crate::pages::comments::comment_card` -- the universal conversational |
| 690 | * primitive: avatar + author + relative time + optional reply/state, an |
| 691 | * AsciiDoc body reusing `.doc-body`. */ |
| 692 | .comment-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 12px 14px 6px; font-size: 11.5px; color: var(--ink-3); } |
| 693 | .comment-meta .author { color: var(--ink); font-weight: 600; font-size: 12.5px; } |
| 694 | .comment-meta .reply { font-family: var(--mono); font-size: 10px; } |
| 695 | .comment-meta .spacer { flex: 1; } |
| 696 | .comment-body { padding: 0 14px 12px; font-size: 13px; line-height: 1.55; color: var(--ink-2); text-wrap: pretty; } |
| 697 | .comment-card + .comment-card { border-top: 1px solid var(--line-2); } |
| 698 | .outdated { color: var(--amber); background: var(--amber-soft); font-family: var(--mono); font-size: 10px; padding: 2px 7px; border-radius: 5px; } |
| 699 | .comment-state { display: inline-flex; align-items: center; height: 18px; padding: 0 7px; border-radius: 5px; font-size: 10px; font-weight: 600; color: var(--amber); background: var(--amber-soft); } |
| 700 | .comment-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 10px 14px; border-top: 1px solid var(--line-2); background: var(--surface-2); } |
| 701 | .comment-actions form { margin: 0; } |
| 702 | /* An anchored blob thread card's header strip (thread on L<n>). */ |
| 703 | .thread-head { display: flex; align-items: center; gap: 8px; padding: 6px 13px; background: var(--surface-2); border-bottom: 1px solid var(--line-2); font-family: var(--mono); font-size: 11px; color: var(--ink-3); } |
| 704 | .thread-head .dot { width: 5px; height: 5px; border-radius: 50%; background: var(--accent); flex: none; } |
| 705 | |
| 706 | /* ===================== SYNTAX ===================== * |
| 707 | * `crate::pages::files::highlight` (`arborium` `ClassNames`). */ |
| 708 | .code .keyword, .code .macro, .code .tag { color: var(--s-keyword); } |
| 709 | .code .function, .code .constructor { color: var(--s-func); } |
| 710 | .code .type { color: var(--s-type); } |
| 711 | .code .string { color: var(--s-string); } |
| 712 | .code .number, .code .constant, .code .label { color: var(--s-const); } |
| 713 | .code .comment { color: var(--s-comment); font-style: italic; } |
| 714 | .code .operator, .code .punctuation { color: var(--s-op); } |
| 715 | .code .property, .code .attribute { color: var(--s-prop); } |
| 716 | .code .title { color: var(--s-keyword); font-weight: 600; } |
| 717 | .code .strong { font-weight: 600; } |
| 718 | .code .emphasis { font-style: italic; } |
| 719 | .code .link, .code .url, .code .reference { color: var(--s-prop); text-decoration: underline; } |
| 720 | .code .markup { color: var(--s-func); } |
| 721 | |
| 722 | /* ===================== DIFFS ===================== * |
| 723 | * `crate::pages::commits::diff_view`. */ |
| 724 | .diff { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-inner); overflow-x: auto; margin-bottom: 12px; font-family: var(--mono); font-size: 12px; line-height: 19px; } |
| 725 | .diff .ln { display: block; padding: 2px 14px; white-space: pre; } |
| 726 | .diff .add { background: var(--diff-add); color: var(--green); } |
| 727 | .diff .del { background: var(--diff-del); color: var(--red); } |
| 728 | .diff .hunk { color: var(--accent); background: var(--surface-2); } |
| 729 | .diff .meta { color: var(--ink-3); } |
| 730 | .diff .file { color: var(--ink); font-weight: 600; background: var(--surface-2); border-bottom: 1px solid var(--line); padding: 9px 14px; } |
| 731 | |
| 732 | /* ===================== RENDERED DOCS ===================== */ |
| 733 | .doc-props { border: 1px solid var(--line); border-radius: var(--radius-inner); background: var(--surface-2); padding: 6px 0; margin: 0 0 18px; } |
| 734 | .doc-props dd { white-space: pre-wrap; } |
| 735 | .doc-body { padding: 28px 32px 36px; max-width: 44rem; overflow-wrap: break-word; font-size: 14px; line-height: 1.62; color: var(--ink-2); } |
| 736 | .doc-body > :first-child { margin-top: 0; } |
| 737 | .doc-body h1, .doc-body h2, .doc-body h3, .doc-body h4 { font-family: var(--sans); font-weight: 600; letter-spacing: -.01em; line-height: 1.3; margin: 1.6rem 0 .8rem; color: var(--ink); } |
| 738 | .doc-body h1 { font-size: 1.9rem; letter-spacing: -.02em; margin-top: 0; } |
| 739 | .doc-body .doc-subtitle { font-style: italic; font-size: 1.1rem; color: var(--ink-3); margin: -.3rem 0 1.1rem; } |
| 740 | .doc-body h2 { font-size: 1.35rem; padding-bottom: .5rem; border-bottom: 1px solid var(--line); } |
| 741 | .doc-body h3 { font-size: 1.12rem; } |
| 742 | .doc-body p, .doc-body ul, .doc-body ol { margin: 0 0 1rem; } |
| 743 | .doc-body ul, .doc-body ol { padding-left: 1.4rem; list-style: revert; } |
| 744 | .doc-body li { margin: .25rem 0; } |
| 745 | .doc-body a { font-weight: 500; text-decoration: underline; } |
| 746 | .doc-body code, .doc-body .literal { font-family: var(--mono); font-size: .86em; background: var(--surface-2); border: 1px solid var(--line); padding: 1px 5px; border-radius: 5px; } |
| 747 | .doc-body pre { font-family: var(--mono); font-size: 12.5px; line-height: 1.55; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius-inner); padding: 14px 16px; overflow-x: auto; margin: 0 0 1rem; } |
| 748 | .doc-body pre code { background: none; border: none; padding: 0; font-size: inherit; } |
| 749 | .doc-body blockquote { border-left: 3px solid var(--accent); padding: .2rem 0 .2rem 1.1rem; margin: 0 0 1rem; color: var(--ink-3); } |
| 750 | .doc-body table { border-collapse: collapse; margin: 0 0 1rem; font-size: .95rem; } |
| 751 | .doc-body th, .doc-body td { border: 1px solid var(--line); padding: .4rem .7rem; text-align: left; } |
| 752 | .doc-body th { background: var(--surface-2); font-weight: 600; } |
| 753 | .doc-body img { max-width: 100%; height: auto; } |
| 754 | .doc-body hr { border: none; border-top: 1px solid var(--line); margin: 1.6rem 0; } |
| 755 | |
| 756 | /* ===================== LOGIN ===================== * |
| 757 | * `crate::pages::login` -- the inverted device-code flow: a dark code |
| 758 | * block, only ever mounted on a hosted deployment. */ |
| 759 | .login-code { |
| 760 | font-family: var(--mono); font-size: 15px; color: #fff; background: var(--ink); |
| 761 | border-radius: var(--radius-inner); padding: 16px 18px; overflow-x: auto; white-space: pre; |
| 762 | } |
| 763 | .login-code .code { color: var(--editor); font-weight: 600; } |
| 764 | |
| 765 | /* ============ PAGE-SCOPED ADDITIONS ============ * |
| 766 | * Classes the per-page reskin introduced, collected here so the whole |
| 767 | * design system stays in one stylesheet (the page modules never edit CSS |
| 768 | * themselves; they report the rule and it lands here). */ |
| 769 | |
| 770 | /* The editor pill sits inside flex rows (`.card-row`, `.blob-actions`, |
| 771 | * `.commit-meta`) and must never stretch like a `.card-row a` link does. */ |
| 772 | .editor-open { flex: none; } |
| 773 | .ed-loc { white-space: nowrap; } |
| 774 | |
| 775 | /* Dashboard working-tree change-kind chips (README ChangeKindLabel), |
| 776 | * layered on the `.chip` base. */ |
| 777 | .chip-added { color: var(--green); background: var(--green-soft); } |
| 778 | .chip-modified { color: var(--amber); background: var(--amber-soft); } |
| 779 | .chip-deleted { color: var(--red); background: var(--red-soft); } |
| 780 | .chip-renamed { color: var(--accent); background: var(--accent-soft); } |
| 781 | .chip-untracked { color: var(--ink-2); background: var(--line-2); } |
| 782 | |
| 783 | /* Dashboard working-tree path: ellipsize from the front (`direction:rtl`) |
| 784 | * so the filename stays visible when the path overflows. */ |
| 785 | .desk-path { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; direction: rtl; text-align: left; } |
| 786 | |
| 787 | /* Dashboard header's right-aligned deployment-mode line. */ |
| 788 | .desk-status { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--ink-3); } |
| 789 | .desk-status .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--editor); flex: none; } |
| 790 | |
| 791 | /* Files' outdated-comments section heading (warn glyph + title). */ |
| 792 | .outdated-head { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; font-size: 12.5px; font-weight: 600; color: var(--ink-2); } |
| 793 | .outdated-head .icon { width: 14px; height: 14px; stroke-width: 1.9; color: var(--amber); } |
| 794 | |
| 795 | /* Issues sidebar status dot, colored by issue state (the chip's own leading |
| 796 | * dot already inherits via `.chip .dot`). */ |
| 797 | .side-meta .dot.state-open { background: var(--green); } |
| 798 | .side-meta .dot.state-in-progress { background: var(--amber); } |
| 799 | .side-meta .dot.state-closed { background: var(--ink-4); } |
| 800 | |
| 801 | /* Segmented pickers: hide the native radio and fill the chosen option. |
| 802 | * Issues' state picker is server-rendered (`.opt.active.state-*`, colored by |
| 803 | * state); the commit verdict picker is plain radios, so its checked option |
| 804 | * fills via `:has()`. */ |
| 805 | .picker .opt { position: relative; } |
| 806 | .picker .opt input { position: absolute; width: 1px; height: 1px; opacity: 0; margin: 0; pointer-events: none; } |
| 807 | .picker .opt:has(input:checked) { color: var(--accent); background: var(--accent-soft); border-color: var(--accent-line); font-weight: 600; } |
| 808 | .picker .opt:has(input:checked) .dot, .picker .opt.active .dot { background: currentColor; } |
| 809 | .picker .opt.active.state-open { color: var(--green); background: var(--green-soft); border-color: color-mix(in srgb, var(--green) 40%, transparent); } |
| 810 | .picker .opt.active.state-in-progress { color: var(--amber); background: var(--amber-soft); border-color: color-mix(in srgb, var(--amber) 40%, transparent); } |
| 811 | .picker .opt.active.state-closed { color: var(--ink-2); background: var(--line-2); border-color: var(--line); } |
| 812 | |
| 813 | /* Disclosure summaries (issue Edit, account login-mapping) read as |
| 814 | * clickable. */ |
| 815 | details > summary { cursor: pointer; } |
| 816 | |
| 817 | /* ===================== RESPONSIVE ===================== */ |
| 818 | @media (max-width: 640px) { |
| 819 | .content { padding: 22px 18px 40px; } |
| 820 | .wb-bar { gap: 10px; padding: 0 12px; } |
| 821 | .palette { max-width: none; } |
| 822 | .palette kbd { display: none; } |
| 823 | .page-title { font-size: 19px; } |
| 824 | .doc-body { padding: 22px 20px 28px; } |
| 825 | .doc-body h1 { font-size: 1.6rem; } |
| 826 | } |