Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesPull RequestsActionsSecurityInsightsSettings
✨ AI
More
Blame · Line-by-line history

layout.tsx

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

layout.tsxBlame860 lines · 1 contributor
fc1817aClaude1import type { FC, PropsWithChildren } from "hono/jsx";
06d5ffeClaude2import type { User } from "../db/schema";
3import { hljsThemeCss } from "../lib/highlight";
fc1817aClaude4
06d5ffeClaude5export const Layout: FC<
3ef4c9dClaude6 PropsWithChildren<{
7 title?: string;
8 user?: User | null;
9 notificationCount?: number;
10 }>
11> = ({ children, title, user, notificationCount }) => {
fc1817aClaude12 return (
13 <html lang="en">
14 <head>
15 <meta charset="UTF-8" />
16 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
17 <title>{title ? `${title} — gluecron` : "gluecron"}</title>
18 <style>{css}</style>
06d5ffeClaude19 <style>{hljsThemeCss}</style>
fc1817aClaude20 </head>
21 <body>
22 <header>
23 <nav>
24 <a href="/" class="logo">
25 gluecron
26 </a>
3ef4c9dClaude27 <div class="nav-search">
28 <form method="GET" action="/search">
29 <input
30 type="search"
31 name="q"
32 placeholder="Search (press /)"
33 aria-label="Search"
34 />
35 </form>
36 </div>
06d5ffeClaude37 <div class="nav-right">
c81ab7aClaude38 <a href="/explore" class="nav-link">
39 Explore
40 </a>
06d5ffeClaude41 {user ? (
42 <>
3ef4c9dClaude43 <a href="/ask" class="nav-link" title="Ask AI (Cmd+K)">
44 {"\u2728"} Ask
45 </a>
46 <a
47 href="/notifications"
48 class="nav-link nav-notifications"
49 title="Notifications"
50 >
51 {"\u2709"}
52 {notificationCount !== undefined && notificationCount > 0 && (
53 <span class="nav-badge">
54 {notificationCount > 99 ? "99+" : notificationCount}
55 </span>
56 )}
57 </a>
06d5ffeClaude58 <a href="/new" class="btn btn-sm btn-primary">
59 + New
60 </a>
61 <a href={`/${user.username}`} class="nav-user">
62 {user.displayName || user.username}
63 </a>
64 <a href="/settings" class="nav-link">
65 Settings
66 </a>
67 <a href="/logout" class="nav-link">
68 Sign out
69 </a>
70 </>
71 ) : (
72 <>
73 <a href="/login" class="nav-link">
74 Sign in
75 </a>
76 <a href="/register" class="btn btn-sm btn-primary">
77 Register
78 </a>
79 </>
80 )}
81 </div>
fc1817aClaude82 </nav>
83 </header>
84 <main>{children}</main>
85 <footer>
86 <span>gluecron — AI-native code intelligence</span>
87 </footer>
3ef4c9dClaude88 <script>{navScript}</script>
fc1817aClaude89 </body>
90 </html>
91 );
92};
93
3ef4c9dClaude94const navScript = `
95 (function(){
96 var chord = null;
97 var chordTimer = null;
98 function isTyping(t){
99 t = t || {};
100 var tag = (t.tagName || '').toLowerCase();
101 return tag === 'input' || tag === 'textarea' || t.isContentEditable;
102 }
103 document.addEventListener('keydown', function(e){
104 if (isTyping(e.target)) return;
105 // Single key shortcuts
106 if (e.key === '/') {
107 var el = document.querySelector('.nav-search input');
108 if (el) { e.preventDefault(); el.focus(); return; }
109 }
110 if ((e.metaKey||e.ctrlKey) && e.key.toLowerCase() === 'k') {
111 e.preventDefault(); window.location.href = '/ask'; return;
112 }
113 if (e.key === '?' && !e.ctrlKey && !e.metaKey) {
114 e.preventDefault(); window.location.href = '/shortcuts'; return;
115 }
116 if (e.key === 'n' && !e.ctrlKey && !e.metaKey) {
117 e.preventDefault(); window.location.href = '/new'; return;
118 }
119 // "g" chord
120 if (e.key === 'g' && !e.ctrlKey && !e.metaKey) {
121 chord = 'g';
122 clearTimeout(chordTimer);
123 chordTimer = setTimeout(function(){ chord = null; }, 1200);
124 return;
125 }
126 if (chord === 'g') {
127 if (e.key === 'd') { e.preventDefault(); window.location.href = '/dashboard'; }
128 else if (e.key === 'n') { e.preventDefault(); window.location.href = '/notifications'; }
129 else if (e.key === 'e') { e.preventDefault(); window.location.href = '/explore'; }
130 else if (e.key === 'a') { e.preventDefault(); window.location.href = '/ask'; }
131 chord = null;
132 }
133 });
134 })();
135`;
136
fc1817aClaude137const css = `
138 :root {
139 --bg: #0d1117;
140 --bg-secondary: #161b22;
141 --bg-tertiary: #21262d;
142 --border: #30363d;
143 --text: #e6edf3;
144 --text-muted: #8b949e;
145 --text-link: #58a6ff;
146 --accent: #1f6feb;
147 --accent-hover: #388bfd;
148 --green: #3fb950;
149 --red: #f85149;
150 --yellow: #d29922;
151 --font-mono: 'SF Mono', 'Cascadia Code', 'Fira Code', monospace;
152 --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
153 --radius: 6px;
154 }
155
156 * { margin: 0; padding: 0; box-sizing: border-box; }
157
158 body {
159 font-family: var(--font-sans);
160 background: var(--bg);
161 color: var(--text);
162 line-height: 1.5;
163 min-height: 100vh;
164 display: flex;
165 flex-direction: column;
166 }
167
168 a { color: var(--text-link); text-decoration: none; }
169 a:hover { text-decoration: underline; }
170
171 header {
172 border-bottom: 1px solid var(--border);
173 padding: 12px 24px;
174 background: var(--bg-secondary);
175 }
176
06d5ffeClaude177 header nav {
178 display: flex;
179 align-items: center;
180 justify-content: space-between;
181 max-width: 1200px;
182 margin: 0 auto;
183 }
fc1817aClaude184 .logo { font-size: 20px; font-weight: 700; color: var(--text); }
185 .logo:hover { text-decoration: none; color: var(--text-link); }
186
06d5ffeClaude187 .nav-right { display: flex; align-items: center; gap: 16px; }
188 .nav-link { color: var(--text-muted); font-size: 14px; }
189 .nav-link:hover { color: var(--text); text-decoration: none; }
190 .nav-user { color: var(--text); font-weight: 600; font-size: 14px; }
191 .nav-user:hover { color: var(--text-link); text-decoration: none; }
192
fc1817aClaude193 main { max-width: 1200px; margin: 0 auto; padding: 24px; flex: 1; width: 100%; }
194
195 footer {
196 border-top: 1px solid var(--border);
197 padding: 16px 24px;
198 text-align: center;
199 color: var(--text-muted);
200 font-size: 13px;
201 }
202
06d5ffeClaude203 /* Buttons */
204 .btn {
205 display: inline-flex;
206 align-items: center;
207 gap: 6px;
208 padding: 8px 16px;
209 border-radius: var(--radius);
210 font-size: 14px;
211 font-weight: 500;
212 border: 1px solid var(--border);
213 background: var(--bg-tertiary);
214 color: var(--text);
215 cursor: pointer;
216 text-decoration: none;
217 line-height: 1.4;
218 }
219 .btn:hover { background: var(--border); text-decoration: none; }
220 .btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
221 .btn-primary:hover { background: var(--accent-hover); }
222 .btn-danger { background: transparent; border-color: var(--red); color: var(--red); }
223 .btn-danger:hover { background: rgba(248, 81, 73, 0.15); }
224 .btn-sm { padding: 4px 12px; font-size: 13px; }
225
226 /* Forms */
227 .form-group { margin-bottom: 16px; }
228 .form-group label { display: block; font-size: 14px; font-weight: 500; margin-bottom: 6px; color: var(--text); }
229 .form-group input, .form-group textarea, .form-group select {
230 width: 100%;
231 padding: 8px 12px;
232 background: var(--bg);
233 border: 1px solid var(--border);
234 border-radius: var(--radius);
235 color: var(--text);
236 font-size: 14px;
237 font-family: var(--font-sans);
238 }
239 .form-group input:focus, .form-group textarea:focus, .form-group select:focus {
240 outline: none;
241 border-color: var(--accent);
242 box-shadow: 0 0 0 2px rgba(31, 111, 235, 0.3);
243 }
244 .input-disabled { opacity: 0.5; cursor: not-allowed; }
245
246 /* Auth */
247 .auth-container { max-width: 400px; margin: 40px auto; }
248 .auth-container h2 { margin-bottom: 20px; font-size: 24px; }
249 .auth-error {
250 background: rgba(248, 81, 73, 0.1);
251 border: 1px solid var(--red);
252 color: var(--red);
253 padding: 8px 12px;
254 border-radius: var(--radius);
255 margin-bottom: 16px;
256 font-size: 14px;
257 }
258 .auth-success {
259 background: rgba(63, 185, 80, 0.1);
260 border: 1px solid var(--green);
261 color: var(--green);
262 padding: 8px 12px;
263 border-radius: var(--radius);
264 margin-bottom: 16px;
265 font-size: 14px;
266 }
267 .auth-switch { margin-top: 16px; font-size: 14px; color: var(--text-muted); }
268
269 /* Settings */
270 .settings-container { max-width: 600px; }
271 .settings-container h2 { margin-bottom: 20px; font-size: 24px; }
272 .settings-container h3 { font-size: 18px; margin-bottom: 12px; }
273 .ssh-keys-list { margin-bottom: 24px; }
274 .ssh-key-item {
275 display: flex;
276 justify-content: space-between;
277 align-items: center;
278 padding: 12px 16px;
279 border: 1px solid var(--border);
280 border-radius: var(--radius);
281 margin-bottom: 8px;
282 background: var(--bg-secondary);
283 }
284 .ssh-key-meta { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
285 .ssh-key-meta code { font-size: 11px; background: var(--bg-tertiary); padding: 1px 6px; border-radius: 3px; margin-right: 8px; }
286
287 /* Repo header */
fc1817aClaude288 .repo-header {
289 display: flex;
290 align-items: center;
291 gap: 8px;
292 margin-bottom: 16px;
293 font-size: 20px;
294 }
295 .repo-header .owner { color: var(--text-link); }
296 .repo-header .separator { color: var(--text-muted); }
297 .repo-header .name { color: var(--text-link); font-weight: 600; }
06d5ffeClaude298 .repo-header-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }
fc1817aClaude299
300 .repo-nav {
301 display: flex;
302 gap: 0;
303 border-bottom: 1px solid var(--border);
304 margin-bottom: 20px;
305 }
306 .repo-nav a {
307 padding: 8px 16px;
308 color: var(--text-muted);
309 border-bottom: 2px solid transparent;
310 font-size: 14px;
311 }
312 .repo-nav a:hover { text-decoration: none; color: var(--text); }
313 .repo-nav a.active { color: var(--text); border-bottom-color: var(--accent); }
314
315 .breadcrumb { display: flex; gap: 4px; align-items: center; margin-bottom: 16px; color: var(--text-muted); font-size: 14px; }
316 .breadcrumb a { color: var(--text-link); }
317
318 .file-table { width: 100%; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
319 .file-table tr { border-bottom: 1px solid var(--border); }
320 .file-table tr:last-child { border-bottom: none; }
321 .file-table td { padding: 8px 16px; font-size: 14px; }
322 .file-table tr:hover { background: var(--bg-secondary); }
323 .file-icon { width: 20px; color: var(--text-muted); }
324 .file-name a { color: var(--text); }
325 .file-name a:hover { color: var(--text-link); text-decoration: underline; }
326
327 .blob-view {
328 border: 1px solid var(--border);
329 border-radius: var(--radius);
330 overflow: hidden;
331 }
332 .blob-header {
333 background: var(--bg-secondary);
334 padding: 8px 16px;
335 border-bottom: 1px solid var(--border);
336 font-size: 13px;
337 color: var(--text-muted);
06d5ffeClaude338 display: flex;
339 justify-content: space-between;
340 align-items: center;
fc1817aClaude341 }
342 .blob-code {
343 overflow-x: auto;
344 font-family: var(--font-mono);
345 font-size: 13px;
346 line-height: 1.6;
347 }
348 .blob-code table { width: 100%; border-collapse: collapse; }
349 .blob-code .line-num {
350 width: 1%;
351 min-width: 50px;
352 padding: 0 12px;
353 text-align: right;
354 color: var(--text-muted);
355 user-select: none;
356 white-space: nowrap;
357 border-right: 1px solid var(--border);
358 }
359 .blob-code .line-content { padding: 0 12px; white-space: pre; }
360
361 .commit-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
362 .commit-item {
363 display: flex;
364 justify-content: space-between;
365 align-items: center;
366 padding: 12px 16px;
367 border-bottom: 1px solid var(--border);
368 }
369 .commit-item:last-child { border-bottom: none; }
370 .commit-item:hover { background: var(--bg-secondary); }
371 .commit-message { font-size: 14px; font-weight: 500; }
372 .commit-meta { font-size: 12px; color: var(--text-muted); }
373 .commit-sha {
374 font-family: var(--font-mono);
375 font-size: 12px;
376 padding: 2px 8px;
377 background: var(--bg-tertiary);
378 border: 1px solid var(--border);
379 border-radius: var(--radius);
380 color: var(--text-link);
381 }
382
383 .diff-view { margin-top: 16px; }
384 .diff-file {
385 border: 1px solid var(--border);
386 border-radius: var(--radius);
387 margin-bottom: 16px;
388 overflow: hidden;
389 }
390 .diff-file-header {
391 background: var(--bg-secondary);
392 padding: 8px 16px;
393 border-bottom: 1px solid var(--border);
394 font-family: var(--font-mono);
395 font-size: 13px;
396 }
397 .diff-content {
398 overflow-x: auto;
399 font-family: var(--font-mono);
400 font-size: 13px;
401 line-height: 1.6;
402 }
403 .diff-content .line-add { background: rgba(63, 185, 80, 0.15); color: var(--green); }
404 .diff-content .line-del { background: rgba(248, 81, 73, 0.1); color: var(--red); }
405 .diff-content .line-hunk { background: rgba(56, 139, 253, 0.1); color: var(--text-link); }
406 .diff-content .line { padding: 0 12px; white-space: pre; display: block; }
407
408 .stat-add { color: var(--green); font-weight: 600; }
409 .stat-del { color: var(--red); font-weight: 600; }
410
411 .empty-state {
412 text-align: center;
413 padding: 60px 20px;
414 color: var(--text-muted);
415 }
416 .empty-state h2 { font-size: 24px; margin-bottom: 8px; color: var(--text); }
417 .empty-state pre {
418 text-align: left;
419 display: inline-block;
420 background: var(--bg-secondary);
421 padding: 16px 24px;
422 border-radius: var(--radius);
423 border: 1px solid var(--border);
424 font-family: var(--font-mono);
425 font-size: 13px;
426 margin-top: 16px;
427 line-height: 1.8;
428 }
429
430 .badge {
431 display: inline-block;
432 padding: 2px 8px;
433 border-radius: 12px;
434 font-size: 12px;
435 font-weight: 500;
436 background: var(--bg-tertiary);
437 border: 1px solid var(--border);
438 color: var(--text-muted);
439 }
440
441 .branch-selector {
442 display: inline-flex;
443 align-items: center;
444 gap: 4px;
445 padding: 4px 12px;
446 background: var(--bg-tertiary);
447 border: 1px solid var(--border);
448 border-radius: var(--radius);
449 font-size: 13px;
450 color: var(--text);
451 margin-bottom: 12px;
06d5ffeClaude452 position: relative;
fc1817aClaude453 }
454
06d5ffeClaude455 .branch-dropdown {
456 position: relative;
457 display: inline-block;
458 margin-bottom: 12px;
459 }
460 .branch-dropdown-content {
461 display: none;
462 position: absolute;
463 top: 100%;
464 left: 0;
465 z-index: 10;
466 min-width: 200px;
467 background: var(--bg-secondary);
468 border: 1px solid var(--border);
469 border-radius: var(--radius);
470 margin-top: 4px;
471 box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
472 }
473 .branch-dropdown:hover .branch-dropdown-content,
474 .branch-dropdown:focus-within .branch-dropdown-content { display: block; }
475 .branch-dropdown-content a {
476 display: block;
477 padding: 8px 12px;
478 font-size: 13px;
479 color: var(--text);
480 border-bottom: 1px solid var(--border);
481 }
482 .branch-dropdown-content a:last-child { border-bottom: none; }
483 .branch-dropdown-content a:hover { background: var(--bg-tertiary); text-decoration: none; }
484 .branch-dropdown-content a.active-branch { color: var(--text-link); font-weight: 600; }
485
fc1817aClaude486 .card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; }
487 .card {
488 border: 1px solid var(--border);
489 border-radius: var(--radius);
490 padding: 16px;
491 background: var(--bg-secondary);
06d5ffeClaude492 transition: border-color 0.15s;
fc1817aClaude493 }
06d5ffeClaude494 .card:hover { border-color: var(--text-muted); }
fc1817aClaude495 .card h3 { font-size: 16px; margin-bottom: 4px; }
06d5ffeClaude496 .card h3 a { color: var(--text-link); }
fc1817aClaude497 .card p { font-size: 13px; color: var(--text-muted); }
06d5ffeClaude498 .card-meta { display: flex; gap: 16px; margin-top: 12px; font-size: 12px; color: var(--text-muted); }
499 .card-meta span { display: flex; align-items: center; gap: 4px; }
500
501 .star-btn {
502 display: inline-flex;
503 align-items: center;
504 gap: 6px;
505 padding: 4px 12px;
506 background: var(--bg-tertiary);
507 border: 1px solid var(--border);
508 border-radius: var(--radius);
509 color: var(--text);
510 font-size: 13px;
511 cursor: pointer;
512 }
513 .star-btn:hover { background: var(--border); text-decoration: none; }
514 .star-btn.starred { color: var(--yellow); border-color: var(--yellow); }
515
516 .user-profile {
517 display: flex;
518 gap: 32px;
519 margin-bottom: 32px;
520 }
521 .user-avatar {
522 width: 96px;
523 height: 96px;
524 border-radius: 50%;
525 background: var(--bg-tertiary);
526 border: 1px solid var(--border);
527 display: flex;
528 align-items: center;
529 justify-content: center;
530 font-size: 40px;
531 color: var(--text-muted);
532 flex-shrink: 0;
533 }
534 .user-info h2 { font-size: 24px; margin-bottom: 2px; }
535 .user-info .username { font-size: 16px; color: var(--text-muted); }
536 .user-info .bio { font-size: 14px; color: var(--text-muted); margin-top: 8px; }
537
538 .new-repo-form { max-width: 600px; }
539 .new-repo-form h2 { margin-bottom: 20px; }
540 .visibility-options { display: flex; gap: 12px; margin-bottom: 16px; }
541 .visibility-option {
542 flex: 1;
543 padding: 12px;
544 border: 1px solid var(--border);
545 border-radius: var(--radius);
546 background: var(--bg-secondary);
547 cursor: pointer;
548 text-align: center;
549 }
550 .visibility-option:has(input:checked) { border-color: var(--accent); background: rgba(31, 111, 235, 0.1); }
551 .visibility-option input { display: none; }
552 .visibility-option .vis-label { font-size: 14px; font-weight: 500; }
553 .visibility-option .vis-desc { font-size: 12px; color: var(--text-muted); }
79136bbClaude554
555 /* Issues */
556 .issue-tabs { display: flex; gap: 16px; }
557 .issue-tabs a { color: var(--text-muted); font-size: 14px; font-weight: 500; }
558 .issue-tabs a:hover { color: var(--text); text-decoration: none; }
559 .issue-tabs a.active { color: var(--text); }
560
561 .issue-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
562 .issue-item {
563 display: flex;
564 gap: 12px;
565 align-items: flex-start;
566 padding: 12px 16px;
567 border-bottom: 1px solid var(--border);
568 }
569 .issue-item:last-child { border-bottom: none; }
570 .issue-item:hover { background: var(--bg-secondary); }
571 .issue-state-icon { font-size: 16px; padding-top: 2px; }
572 .state-open { color: var(--green); }
573 .state-closed { color: #986ee2; }
574 .issue-title { font-size: 15px; font-weight: 600; }
575 .issue-title a { color: var(--text); }
576 .issue-title a:hover { color: var(--text-link); }
577 .issue-meta { font-size: 12px; color: var(--text-muted); margin-top: 2px; }
578
579 .issue-badge {
580 display: inline-flex;
581 align-items: center;
582 gap: 4px;
583 padding: 4px 12px;
584 border-radius: 20px;
585 font-size: 13px;
586 font-weight: 500;
587 }
588 .badge-open { background: rgba(63, 185, 80, 0.15); color: var(--green); border: 1px solid var(--green); }
589 .badge-closed { background: rgba(152, 110, 226, 0.15); color: #986ee2; border: 1px solid #986ee2; }
0074234Claude590 .badge-merged { background: rgba(152, 110, 226, 0.15); color: #986ee2; border: 1px solid #986ee2; }
591 .state-merged { color: #986ee2; }
592 .ai-review { border-color: var(--accent); }
79136bbClaude593
594 .issue-detail { max-width: 900px; }
595 .issue-comment-box {
596 border: 1px solid var(--border);
597 border-radius: var(--radius);
598 margin-bottom: 16px;
599 overflow: hidden;
600 }
601 .comment-header {
602 background: var(--bg-secondary);
603 padding: 8px 16px;
604 border-bottom: 1px solid var(--border);
605 font-size: 13px;
606 color: var(--text-muted);
607 }
608
609 /* Search */
610 .search-results .diff-file { margin-bottom: 12px; }
3ef4c9dClaude611
612 /* Nav — search + ask + notifications badge */
613 .nav-search { flex: 1; max-width: 320px; margin: 0 16px; }
614 .nav-search form { width: 100%; }
615 .nav-search input {
616 width: 100%;
617 padding: 6px 10px;
618 background: var(--bg);
619 border: 1px solid var(--border);
620 border-radius: var(--radius);
621 color: var(--text);
622 font-size: 13px;
623 }
624 .nav-search input::placeholder { color: var(--text-muted); }
625 .nav-search input:focus { outline: none; border-color: var(--accent); }
626 .nav-notifications { position: relative; display: inline-flex; align-items: center; }
627 .nav-badge {
628 position: absolute;
629 top: -6px;
630 right: -10px;
631 min-width: 18px;
632 height: 18px;
633 padding: 0 5px;
634 border-radius: 9px;
635 background: var(--red);
636 color: #fff;
637 font-size: 11px;
638 font-weight: 700;
639 display: flex;
640 align-items: center;
641 justify-content: center;
642 }
643
644 /* Notifications list */
645 .notification-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
646 .notification-item {
647 display: flex;
648 gap: 12px;
649 align-items: flex-start;
650 padding: 12px 16px;
651 border-bottom: 1px solid var(--border);
652 background: var(--bg);
653 }
654 .notification-item.unread { background: rgba(56, 139, 253, 0.06); }
655 .notification-item:last-child { border-bottom: none; }
656 .notification-badge {
657 flex-shrink: 0;
658 padding: 2px 8px;
659 border-radius: 10px;
660 font-size: 11px;
661 font-weight: 600;
662 border: 1px solid;
663 text-transform: lowercase;
664 }
665 .notification-body { flex: 1; min-width: 0; }
666 .notification-title { font-size: 14px; font-weight: 500; margin-bottom: 2px; }
667 .notification-title a { color: var(--text); }
668 .notification-title a:hover { color: var(--text-link); }
669 .notification-desc { font-size: 13px; color: var(--text-muted); margin-bottom: 4px; }
670 .notification-meta { font-size: 12px; color: var(--text-muted); }
671 .notification-meta a { color: var(--text-link); }
672 .notification-actions { display: flex; gap: 4px; align-items: center; }
673 .notification-actions .btn { padding: 2px 8px; font-size: 12px; line-height: 1; }
674
675 /* Dashboard */
676 .dashboard-grid {
677 display: grid;
678 grid-template-columns: 2fr 1fr;
679 gap: 24px;
680 }
681 .dashboard-section { margin-bottom: 24px; }
682 .dashboard-section h3 {
683 font-size: 14px;
684 text-transform: uppercase;
685 letter-spacing: 0.5px;
686 color: var(--text-muted);
687 margin-bottom: 12px;
688 display: flex;
689 justify-content: space-between;
690 align-items: center;
691 }
692 .dashboard-section h3 a { font-size: 12px; font-weight: 400; text-transform: none; letter-spacing: 0; }
693 .panel {
694 border: 1px solid var(--border);
695 border-radius: var(--radius);
696 background: var(--bg-secondary);
697 overflow: hidden;
698 }
699 .panel-item {
700 display: flex;
701 align-items: flex-start;
702 gap: 10px;
703 padding: 10px 14px;
704 border-bottom: 1px solid var(--border);
705 font-size: 13px;
706 }
707 .panel-item:last-child { border-bottom: none; }
708 .panel-item .dot {
709 width: 8px; height: 8px; border-radius: 50%;
710 margin-top: 6px; flex-shrink: 0; background: var(--text-muted);
711 }
712 .panel-item .dot.green { background: var(--green); }
713 .panel-item .dot.red { background: var(--red); }
714 .panel-item .dot.yellow { background: var(--yellow); }
715 .panel-item .dot.blue { background: var(--accent); }
716 .panel-item .meta { color: var(--text-muted); font-size: 12px; margin-top: 2px; }
717 .panel-empty { padding: 24px; text-align: center; color: var(--text-muted); font-size: 13px; }
718
719 /* AI Ask chat */
720 .ask-container { max-width: 900px; margin: 0 auto; }
721 .chat-log {
722 border: 1px solid var(--border);
723 border-radius: var(--radius);
724 background: var(--bg-secondary);
725 padding: 16px;
726 margin-bottom: 16px;
727 min-height: 200px;
728 max-height: 60vh;
729 overflow-y: auto;
730 }
731 .chat-message {
732 padding: 10px 14px;
733 border-radius: var(--radius);
734 margin-bottom: 10px;
735 white-space: pre-wrap;
736 word-wrap: break-word;
737 font-size: 14px;
738 line-height: 1.5;
739 }
740 .chat-message.user { background: var(--bg-tertiary); border: 1px solid var(--border); }
741 .chat-message.assistant { background: rgba(188, 140, 255, 0.08); border: 1px solid rgba(188, 140, 255, 0.3); }
742 .chat-message .role {
743 font-size: 11px;
744 font-weight: 600;
745 text-transform: uppercase;
746 letter-spacing: 0.5px;
747 color: var(--text-muted);
748 margin-bottom: 6px;
749 }
750 .chat-form textarea {
751 width: 100%;
752 min-height: 80px;
753 padding: 10px 12px;
754 background: var(--bg);
755 border: 1px solid var(--border);
756 border-radius: var(--radius);
757 color: var(--text);
758 font-family: var(--font-sans);
759 font-size: 14px;
760 resize: vertical;
761 }
762 .chat-hint { font-size: 12px; color: var(--text-muted); margin-top: 6px; }
763 .chat-cited {
764 display: inline-block;
765 font-family: var(--font-mono);
766 font-size: 11px;
767 padding: 1px 6px;
768 background: var(--bg-tertiary);
769 border: 1px solid var(--border);
770 border-radius: 3px;
771 margin-right: 4px;
772 }
773
774 /* Releases */
775 .release-card {
776 border: 1px solid var(--border);
777 border-radius: var(--radius);
778 background: var(--bg-secondary);
779 padding: 16px;
780 margin-bottom: 16px;
781 }
782 .release-header {
783 display: flex;
784 justify-content: space-between;
785 align-items: baseline;
786 margin-bottom: 8px;
787 gap: 12px;
788 flex-wrap: wrap;
789 }
790 .release-name { font-size: 18px; font-weight: 600; }
791 .release-tag {
792 font-family: var(--font-mono);
793 font-size: 12px;
794 padding: 2px 8px;
795 background: var(--bg-tertiary);
796 border: 1px solid var(--border);
797 border-radius: var(--radius);
798 color: var(--green);
799 }
800 .release-latest { background: var(--green); color: var(--bg); border-color: var(--green); }
801
802 /* Gate runs */
803 .gate-list { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
804 .gate-run-row {
805 display: flex;
806 align-items: center;
807 gap: 12px;
808 padding: 10px 14px;
809 border-bottom: 1px solid var(--border);
810 font-size: 13px;
811 }
812 .gate-run-row:last-child { border-bottom: none; }
813 .gate-status {
814 display: inline-flex;
815 align-items: center;
816 gap: 4px;
817 padding: 2px 8px;
818 border-radius: 10px;
819 font-size: 11px;
820 font-weight: 600;
821 text-transform: uppercase;
822 }
823 .gate-status.passed { background: rgba(63, 185, 80, 0.15); color: var(--green); border: 1px solid var(--green); }
824 .gate-status.failed { background: rgba(248, 81, 73, 0.15); color: var(--red); border: 1px solid var(--red); }
825 .gate-status.repaired { background: rgba(188, 140, 255, 0.15); color: #bc8cff; border: 1px solid #bc8cff; }
826 .gate-status.skipped { background: var(--bg-tertiary); color: var(--text-muted); border: 1px solid var(--border); }
827 .gate-status.running, .gate-status.pending { background: rgba(210, 153, 34, 0.15); color: var(--yellow); border: 1px solid var(--yellow); }
828
829 /* Search */
830 .search-filters { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
831 .search-hit {
832 border: 1px solid var(--border);
833 border-radius: var(--radius);
834 padding: 12px 16px;
835 background: var(--bg-secondary);
836 margin-bottom: 8px;
837 }
838 .search-hit h4 { font-size: 14px; margin-bottom: 4px; }
839 .search-hit .hit-path { font-size: 12px; color: var(--text-muted); font-family: var(--font-mono); }
840 .search-hit pre { margin-top: 8px; font-size: 12px; }
841
842 /* Toasts (prepared for future UI hooks) */
843 .toast-container {
844 position: fixed; bottom: 24px; right: 24px;
845 z-index: 50; display: flex; flex-direction: column; gap: 8px;
846 }
847
848 /* Mobile */
849 @media (max-width: 768px) {
850 header nav { flex-wrap: wrap; gap: 8px; }
851 .nav-search { max-width: 100%; margin: 8px 0; order: 3; width: 100%; }
852 .nav-right { flex-wrap: wrap; gap: 8px; }
853 main { padding: 16px; }
854 .dashboard-grid { grid-template-columns: 1fr; }
855 .card-grid { grid-template-columns: 1fr; }
856 .user-profile { flex-direction: column; gap: 16px; }
857 .repo-header { flex-wrap: wrap; }
858 .repo-nav { overflow-x: auto; }
859 }
fc1817aClaude860`;