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.tsxBlame2452 lines · 2 contributors
fc1817aClaude1import type { FC, PropsWithChildren } from "hono/jsx";
06d5ffeClaude2import type { User } from "../db/schema";
3import { hljsThemeCss } from "../lib/highlight";
45e31d0Claude4import { clientJs } from "./client-js";
05cdb85Claude5import { getBuildInfo } from "../lib/build-info";
fc1817aClaude6
06d5ffeClaude7export const Layout: FC<
3ef4c9dClaude8 PropsWithChildren<{
9 title?: string;
10 user?: User | null;
11 notificationCount?: number;
6fc53bdClaude12 theme?: "dark" | "light";
5f2e749Claude13 // Block L10 — additive SEO + Open Graph fields. All optional;
14 // omission preserves the prior render exactly (regression-safe).
15 fullTitle?: string;
16 description?: string;
17 ogTitle?: string;
18 ogDescription?: string;
19 ogType?: string;
20 twitterCard?: "summary" | "summary_large_image";
3ef4c9dClaude21 }>
5f2e749Claude22> = ({
23 children,
24 title,
25 user,
26 notificationCount,
27 theme,
28 fullTitle,
29 description,
30 ogTitle,
31 ogDescription,
32 ogType,
33 twitterCard,
34}) => {
6fc53bdClaude35 const initialTheme = theme === "light" ? "light" : "dark";
05cdb85Claude36 const build = getBuildInfo();
5f2e749Claude37 // L10 — when `fullTitle` is provided, use it verbatim (no " — gluecron"
38 // suffix); otherwise fall back to the existing `title` + suffix behaviour.
39 const renderedTitle = fullTitle
40 ? fullTitle
41 : title
42 ? `${title} — gluecron`
43 : "gluecron";
fc1817aClaude44 return (
6fc53bdClaude45 <html lang="en" data-theme={initialTheme}>
fc1817aClaude46 <head>
47 <meta charset="UTF-8" />
48 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
eae38d1Claude49 <meta name="theme-color" content="#0d1117" />
50 <link rel="manifest" href="/manifest.webmanifest" />
51 <link rel="icon" type="image/svg+xml" href="/icon.svg" />
5f2e749Claude52 <title>{renderedTitle}</title>
53 {description && <meta name="description" content={description} />}
54 {(ogTitle || fullTitle || title) && (
55 <meta property="og:title" content={ogTitle ?? fullTitle ?? renderedTitle} />
56 )}
57 {(ogDescription || description) && (
58 <meta
59 property="og:description"
60 content={ogDescription ?? description ?? ""}
61 />
62 )}
63 {ogType && <meta property="og:type" content={ogType} />}
64 {twitterCard && <meta name="twitter:card" content={twitterCard} />}
fa880f2Claude65 <script dangerouslySetInnerHTML={{ __html: themeInitScript }} />
66 <style dangerouslySetInnerHTML={{ __html: css }} />
67 <style dangerouslySetInnerHTML={{ __html: hljsThemeCss }} />
fc1817aClaude68 </head>
69 <body>
4a52a98Claude70 <div class="prelaunch-banner" role="status" aria-live="polite">
71 Pre-launch &mdash; Gluecron is in final validation. Public signups
72 and git hosting for non-owner users open after launch review.
73 </div>
fc1817aClaude74 <header>
75 <nav>
76 <a href="/" class="logo">
77 gluecron
78 </a>
3ef4c9dClaude79 <div class="nav-search">
001af43Claude80 <form method="get" action="/search">
3ef4c9dClaude81 <input
82 type="search"
83 name="q"
84 placeholder="Search (press /)"
85 aria-label="Search"
86 />
87 </form>
88 </div>
06d5ffeClaude89 <div class="nav-right">
f764c07Claude90 {/* Block N3 — site-admin platform-deploy status pill. Hidden
91 by default; revealed client-side once /admin/deploys/latest.json
92 responds 200 (non-admins get 401/403 and the pill stays
93 hidden). Subscribes to the `platform:deploys` SSE topic
94 for live updates. See `deployPillScript` below. */}
95 {user && (
96 <a
97 id="deploy-pill"
98 href="/admin/deploys"
99 class="nav-deploy-pill"
100 style="display:none"
101 aria-label="Platform deploy status"
102 title="Platform deploy status"
103 >
104 <span class="deploy-pill-dot" />
105 <span class="deploy-pill-text">Deploys</span>
106 </a>
107 )}
6fc53bdClaude108 <a
109 href="/theme/toggle"
110 class="nav-link nav-theme"
111 title="Toggle theme"
112 aria-label="Toggle theme"
113 >
114 <span class="theme-icon-dark">{"\u263E"}</span>
115 <span class="theme-icon-light">{"\u2600"}</span>
116 </a>
c81ab7aClaude117 <a href="/explore" class="nav-link">
118 Explore
119 </a>
06d5ffeClaude120 {user ? (
121 <>
f1ab587Claude122 <a href="/dashboard" class="nav-link" style="font-weight: 600">
123 Dashboard
124 </a>
bdbd0deClaude125 <a href="/import" class="nav-link">
126 Import
127 </a>
06d5ffeClaude128 <a href="/new" class="btn btn-sm btn-primary">
129 + New
130 </a>
131 <a href={`/${user.username}`} class="nav-user">
132 {user.displayName || user.username}
133 </a>
134 <a href="/settings" class="nav-link">
135 Settings
136 </a>
137 <a href="/logout" class="nav-link">
138 Sign out
139 </a>
140 </>
141 ) : (
142 <>
143 <a href="/login" class="nav-link">
144 Sign in
145 </a>
146 <a href="/register" class="btn btn-sm btn-primary">
147 Register
148 </a>
149 </>
150 )}
151 </div>
fc1817aClaude152 </nav>
153 </header>
45e31d0Claude154 <main id="main-content">{children}</main>
fc1817aClaude155 <footer>
958d26aClaude156 <div class="footer-inner">
157 <div class="footer-brand">
158 <a href="/" class="logo">gluecron</a>
159 <p class="footer-tag">
160 AI-native code intelligence. Self-hosted git, automated CI,
161 push-time gates. Software that ships itself.
162 </p>
163 </div>
164 <div class="footer-links">
165 <div class="footer-col">
166 <div class="footer-col-title">Product</div>
b0148e9Claude167 <a href="/features">Features</a>
168 <a href="/pricing">Pricing</a>
958d26aClaude169 <a href="/explore">Explore</a>
170 <a href="/marketplace">Marketplace</a>
171 </div>
172 <div class="footer-col">
173 <div class="footer-col-title">Platform</div>
b0148e9Claude174 <a href="/help">Quickstart</a>
958d26aClaude175 <a href="/status">Status</a>
176 <a href="/api/graphql">GraphQL</a>
177 <a href="/mcp">MCP server</a>
178 </div>
179 <div class="footer-col">
b0148e9Claude180 <div class="footer-col-title">Company</div>
181 <a href="/about">About</a>
958d26aClaude182 <a href="/terms">Terms</a>
183 <a href="/privacy">Privacy</a>
184 <a href="/acceptable-use">Acceptable use</a>
185 </div>
186 </div>
187 </div>
188 <div class="footer-bottom">
189 <span>&copy; {new Date().getFullYear()} gluecron</span>
05cdb85Claude190 <span class="footer-build" title={`commit ${build.shaFull}\nbuilt ${build.builtAt}`}>
191 <span class="footer-build-dot" aria-hidden="true" />
192 {build.sha} · {build.branch}
193 </span>
36b4cbdClaude194 </div>
fc1817aClaude195 </footer>
05cdb85Claude196 {/* Live update poller — checks /api/version every 15s, prompts
197 reload when the running sha changes. Pure progressive-
198 enhancement; degrades to nothing if JS is off. */}
199 <div
200 id="version-banner"
201 style="display:none;position:fixed;bottom:18px;left:50%;transform:translateX(-50%);z-index:9999;background:var(--bg-elevated);border:1px solid rgba(140,109,255,0.45);border-radius:9999px;padding:8px 14px 8px 14px;font-size:13px;color:var(--text-strong);box-shadow:0 12px 28px -8px rgba(0,0,0,0.55),0 0 24px -6px rgba(140,109,255,0.40);font-family:var(--font-sans);align-items:center;gap:10px"
202 >
203 <span style="display:inline-flex;align-items:center;gap:8px">
204 <span style="width:8px;height:8px;border-radius:50%;background:#34d399;box-shadow:0 0 10px rgba(52,211,153,0.6)" />
205 <span>New version available</span>
206 </span>
207 <button
208 type="button"
209 id="version-banner-reload"
210 style="background:linear-gradient(135deg,#8c6dff 0%,#36c5d6 100%);color:#fff;border:0;border-radius:9999px;padding:5px 12px;font-size:12px;font-weight:600;cursor:pointer;font-family:inherit"
211 >
212 Reload
213 </button>
214 </div>
fa880f2Claude215 <script dangerouslySetInnerHTML={{ __html: versionPollerScript }} />
f764c07Claude216 {/* Block N3 — site-admin deploy status pill (script-only). The pill
217 container is rendered above for authed users; this script bootstraps
218 it by fetching /admin/deploys/latest.json. Non-admins get 401/403
219 and the pill stays display:none — zero leak. */}
220 {user && (
221 <script dangerouslySetInnerHTML={{ __html: deployPillScript }} />
222 )}
699e5c7Claude223 {/* Block I4 — Command palette shell (hidden by default) */}
224 <div
225 id="cmdk-backdrop"
226 style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.5);z-index:9998"
227 />
228 <div
229 id="cmdk-panel"
230 style="display:none;position:fixed;top:10%;left:50%;transform:translateX(-50%);width:min(560px,92vw);background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius);box-shadow:0 12px 32px rgba(0,0,0,0.4);z-index:9999;overflow:hidden"
231 >
232 <input
233 id="cmdk-input"
234 type="text"
235 placeholder="Type a command..."
236 aria-label="Command palette"
237 style="width:100%;padding:12px 16px;background:transparent;color:var(--text);border:0;border-bottom:1px solid var(--border);outline:none;font-size:14px"
238 />
239 <div id="cmdk-list" style="max-height:60vh;overflow-y:auto" />
240 </div>
fa880f2Claude241 <script dangerouslySetInnerHTML={{ __html: clientJs }} />
242 <script dangerouslySetInnerHTML={{ __html: pwaRegisterScript }} />
534f04aClaude243 {/* Block M2 — smart install banner + push-SW bootstrap. Authed
244 users only; the banner respects a 3+ visits + 14-day cooldown
245 heuristic. The push SW is registered on the same gate so we
246 don't ask anonymous visitors to opt into anything. */}
247 {user && (
248 <script
249 dangerouslySetInnerHTML={{ __html: pwaInstallBannerScript }}
250 />
251 )}
fa880f2Claude252 <script dangerouslySetInnerHTML={{ __html: navScript }} />
fc1817aClaude253 </body>
254 </html>
255 );
256};
257
05cdb85Claude258// Live version poller. Checks /api/version every 15s; if the sha differs
259// from the one we booted with, reveal the floating 'New version' pill so
260// the user can reload onto the new code without manually refreshing.
261const versionPollerScript = `
262 (function(){
263 var loadedSha = null;
264 var banner, btn;
265 function poll(){
266 fetch('/api/version', { cache: 'no-store' })
267 .then(function(r){ return r.ok ? r.json() : null; })
268 .then(function(j){
269 if (!j || !j.sha) return;
270 if (loadedSha === null) { loadedSha = j.sha; return; }
271 if (j.sha !== loadedSha && banner) {
272 banner.style.display = 'inline-flex';
273 }
274 })
275 .catch(function(){});
276 }
277 function init(){
278 banner = document.getElementById('version-banner');
279 btn = document.getElementById('version-banner-reload');
280 if (btn) btn.addEventListener('click', function(){ window.location.reload(); });
281 poll();
282 setInterval(poll, 15000);
283 }
284 if (document.readyState === 'loading') {
285 document.addEventListener('DOMContentLoaded', init);
286 } else {
287 init();
288 }
289 })();
290`;
291
f764c07Claude292// Block N3 — site-admin deploy status pill. Fetches /admin/deploys/latest.json;
293// if 200, reveals the pill in the nav and subscribes to the `platform:deploys`
294// SSE topic for live status updates. Non-admins get 401/403 and the pill stays
295// display:none — there's no leakage of admin-only data to other users.
296//
297// Pill states (CSS classes on the container drive colour):
298// .deploy-pill-success 🟢 "Deployed 12s ago"
299// .deploy-pill-progress 🟡 "Deploying… 14s" (pulsing dot)
300// .deploy-pill-failed 🔴 "Deploy failed 1m ago"
301// .deploy-pill-empty ⚪ "No deploys yet"
302//
303// Relative-time auto-refreshes every 15s without re-fetching.
304export const deployPillScript = `
305 (function(){
306 var pill, dot, text;
307 var state = { latest: null, asOf: null };
308
309 function classifyAge(ms){
310 if (ms < 0) return 'just now';
311 var s = Math.floor(ms / 1000);
312 if (s < 5) return 'just now';
313 if (s < 60) return s + 's ago';
314 var m = Math.floor(s / 60);
315 if (m < 60) return m + 'm ago';
316 var h = Math.floor(m / 60);
317 if (h < 24) return h + 'h ago';
318 var d = Math.floor(h / 24);
319 return d + 'd ago';
320 }
321 function elapsed(ms){
322 if (ms < 0) ms = 0;
323 var s = Math.floor(ms / 1000);
324 if (s < 60) return s + 's';
325 var m = Math.floor(s / 60);
326 var rem = s - m * 60;
327 return m + 'm ' + rem + 's';
328 }
329
330 function render(){
331 if (!pill || !text || !dot) return;
332 var d = state.latest;
333 if (!d) {
334 pill.className = 'nav-deploy-pill deploy-pill-empty';
335 text.textContent = 'No deploys yet';
336 pill.style.display = 'inline-flex';
337 return;
338 }
339 pill.style.display = 'inline-flex';
340 var now = Date.now();
341 if (d.status === 'in_progress') {
342 pill.className = 'nav-deploy-pill deploy-pill-progress';
343 var started = Date.parse(d.started_at) || now;
344 text.textContent = 'Deploying… ' + elapsed(now - started);
345 } else if (d.status === 'succeeded') {
346 pill.className = 'nav-deploy-pill deploy-pill-success';
347 var ref = Date.parse(d.finished_at || d.started_at) || now;
348 text.textContent = 'Deployed ' + classifyAge(now - ref);
349 } else if (d.status === 'failed') {
350 pill.className = 'nav-deploy-pill deploy-pill-failed';
351 var refF = Date.parse(d.finished_at || d.started_at) || now;
352 text.textContent = 'Deploy failed ' + classifyAge(now - refF);
353 } else {
354 pill.className = 'nav-deploy-pill';
355 text.textContent = d.status;
356 }
357 }
358
359 function fetchLatest(){
360 fetch('/admin/deploys/latest.json', { cache: 'no-store', credentials: 'same-origin' })
361 .then(function(r){ if (!r.ok) return null; return r.json(); })
362 .then(function(j){
363 if (!j || j.ok !== true) return;
364 state.latest = j.latest;
365 state.asOf = j.asOf;
366 render();
367 subscribe();
368 })
369 .catch(function(){});
370 }
371
372 var subscribed = false;
373 function subscribe(){
374 if (subscribed) return;
375 if (typeof EventSource === 'undefined') return;
376 subscribed = true;
377 var es;
378 var delay = 1500;
379 function connect(){
380 try { es = new EventSource('/live-events/platform:deploys'); }
381 catch(e){ setTimeout(connect, delay); return; }
382 es.onmessage = function(m){
383 try {
384 var d = JSON.parse(m.data);
385 if (d && d.run_id) {
386 if (!state.latest || state.latest.run_id === d.run_id ||
387 Date.parse(d.started_at) >= Date.parse(state.latest.started_at)) {
388 state.latest = d;
389 render();
390 }
391 }
392 } catch(e){}
393 };
394 es.onerror = function(){
395 try { es.close(); } catch(e){}
396 setTimeout(connect, delay);
397 };
398 }
399 connect();
400 }
401
402 function init(){
403 pill = document.getElementById('deploy-pill');
404 if (!pill) return;
405 dot = pill.querySelector('.deploy-pill-dot');
406 text = pill.querySelector('.deploy-pill-text');
407 fetchLatest();
408 // Refresh relative-time labels every 15s so "12s ago" → "27s ago"
409 // without a fresh fetch.
410 setInterval(render, 15000);
411 }
412 if (document.readyState === 'loading') {
413 document.addEventListener('DOMContentLoaded', init);
414 } else {
415 init();
416 }
417 })();
418`;
419
6fc53bdClaude420// Runs before paint — reads the theme cookie and flips data-theme so there's
421// no dark-to-light flash on load. SSR default is dark.
422const themeInitScript = `
423 (function(){
424 try {
425 var m = document.cookie.match(/(?:^|; )theme=([^;]+)/);
426 var t = m ? decodeURIComponent(m[1]) : 'dark';
427 if (t !== 'light' && t !== 'dark') t = 'dark';
428 document.documentElement.setAttribute('data-theme', t);
429 } catch(_){}
430 })();
431`;
432
eae38d1Claude433// Block G1 — register service worker for offline / install support.
434// Kept inline (and tiny) so we don't block first paint.
435const pwaRegisterScript = `
436 if ('serviceWorker' in navigator) {
437 window.addEventListener('load', function(){
438 navigator.serviceWorker.register('/sw.js').catch(function(){});
439 });
440 }
441`;
442
534f04aClaude443// Block M2 — smart install banner (authed users only). Three heuristics:
444// 1. user is authenticated (gated server-side via the `{user &&}` JSX)
445// 2. visit counter (localStorage) ≥ 3
446// 3. dismissed-cooldown < 14 days ago
447// Also bootstraps the push + offline SW at /sw-push.js. Idempotent — safe
448// to load on every page; only inserts DOM nodes if all conditions match.
449const pwaInstallBannerScript = `
450(function(){
451 try {
452 var DISMISS_KEY = 'gc:pwa-banner-dismissed-at';
453 var VISITS_KEY = 'gc:pwa-visit-count';
454 var DISMISS_MS = 14 * 24 * 60 * 60 * 1000;
455 var visits = parseInt(localStorage.getItem(VISITS_KEY) || '0', 10) || 0;
456 visits++;
457 try { localStorage.setItem(VISITS_KEY, String(visits)); } catch(_){}
458 // Bootstrap the push + offline SW (separate from /sw.js's locked behaviour).
459 if ('serviceWorker' in navigator) {
460 window.addEventListener('load', function(){
461 navigator.serviceWorker.register('/sw-push.js', { scope: '/' })
462 .catch(function(){});
463 });
464 }
465 var deferredPrompt = null;
466 window.addEventListener('beforeinstallprompt', function(e){
467 e.preventDefault();
468 deferredPrompt = e;
469 maybeShow();
470 });
471 function maybeShow(){
472 if (!deferredPrompt) return;
473 if (visits < 3) return;
474 var dismissedAt = parseInt(localStorage.getItem(DISMISS_KEY) || '0', 10) || 0;
475 if (dismissedAt && (Date.now() - dismissedAt) < DISMISS_MS) return;
476 if (document.getElementById('gc-install-banner')) return;
477 var bar = document.createElement('div');
478 bar.id = 'gc-install-banner';
479 bar.setAttribute('role', 'dialog');
480 bar.setAttribute('aria-label', 'Install Gluecron');
481 bar.style.cssText = 'position:fixed;left:12px;right:12px;bottom:12px;z-index:9997;'
482 + 'background:var(--bg-elevated,#161b22);color:var(--text,#c9d1d9);'
483 + 'border:1px solid var(--border,#30363d);border-radius:8px;padding:12px 14px;'
484 + 'display:flex;gap:10px;align-items:center;justify-content:space-between;'
485 + 'box-shadow:0 8px 24px rgba(0,0,0,0.45);font-size:13px;line-height:1.3;'
486 + 'max-width:560px;margin:0 auto';
487 bar.innerHTML = '<span style="flex:1">'
488 + '<strong>Install Gluecron</strong> to keep working when offline + get push notifications.'
489 + '</span>'
490 + '<button type="button" id="gc-install-go" style="background:#238636;color:#fff;'
491 + 'border:0;border-radius:6px;padding:6px 12px;font-size:12px;cursor:pointer;'
492 + 'font-family:inherit">Install</button>'
493 + '<button type="button" id="gc-install-no" style="background:transparent;color:inherit;'
494 + 'border:1px solid var(--border,#30363d);border-radius:6px;padding:6px 12px;'
495 + 'font-size:12px;cursor:pointer;font-family:inherit">Not now</button>';
496 document.body.appendChild(bar);
497 var go = bar.querySelector('#gc-install-go');
498 var no = bar.querySelector('#gc-install-no');
499 if (go) go.addEventListener('click', function(){
500 try { deferredPrompt.prompt(); } catch(_){}
501 bar.parentNode && bar.parentNode.removeChild(bar);
502 deferredPrompt = null;
503 });
504 if (no) no.addEventListener('click', function(){
505 try { localStorage.setItem(DISMISS_KEY, String(Date.now())); } catch(_){}
506 bar.parentNode && bar.parentNode.removeChild(bar);
507 });
508 }
509 } catch(_) {}
510})();
511`;
512
3ef4c9dClaude513const navScript = `
514 (function(){
515 var chord = null;
516 var chordTimer = null;
517 function isTyping(t){
518 t = t || {};
519 var tag = (t.tagName || '').toLowerCase();
520 return tag === 'input' || tag === 'textarea' || t.isContentEditable;
521 }
71cd5ecClaude522
523 // ---------- Block I4 — Command palette ----------
524 var COMMANDS = [
525 { label: 'Go to Dashboard', href: '/dashboard', kw: 'home' },
526 { label: 'Go to Explore', href: '/explore', kw: 'browse discover' },
527 { label: 'Go to Notifications', href: '/notifications', kw: 'inbox' },
528 { label: 'Go to Ask AI', href: '/ask', kw: 'chat assistant' },
529 { label: 'Create new repository', href: '/new', kw: 'add create' },
530 { label: 'Marketplace', href: '/marketplace', kw: 'apps store' },
531 { label: 'Installed apps', href: '/settings/apps', kw: 'my apps' },
532 { label: 'Register new app', href: '/developer/apps-new', kw: 'developer create' },
533 { label: 'Keyboard shortcuts', href: '/shortcuts', kw: 'help keys' },
534 { label: 'Settings (profile)', href: '/settings', kw: 'account' },
535 { label: '2FA settings', href: '/settings/2fa', kw: 'two factor security' },
536 { label: 'Passkeys settings', href: '/settings/passkeys', kw: 'webauthn' },
537 { label: 'Personal access tokens', href: '/settings/tokens', kw: 'pat api' },
538 { label: 'Billing + quotas', href: '/settings/billing', kw: 'plans money' },
539 { label: 'Audit log (personal)', href: '/settings/audit', kw: 'history' },
540 { label: 'Gists', href: '/gists', kw: 'snippets' },
541 { label: 'GraphQL explorer', href: '/api/graphql', kw: 'api query' },
542 { label: 'Admin dashboard', href: '/admin', kw: 'superuser' },
543 { label: 'Toggle theme', href: '/theme/toggle', kw: 'dark light mode' }
544 ];
545
546 function fuzzyMatch(item, q){
547 if (!q) return true;
548 var hay = (item.label + ' ' + (item.kw||'') + ' ' + item.href).toLowerCase();
549 q = q.toLowerCase();
550 var qi = 0;
551 for (var i = 0; i < hay.length && qi < q.length; i++) {
552 if (hay[i] === q[qi]) qi++;
553 }
554 return qi === q.length;
555 }
556
557 var backdrop, panel, input, list, selected = 0, filtered = COMMANDS.slice();
558
559 function render(){
560 if (!list) return;
561 var html = '';
562 for (var i = 0; i < filtered.length; i++) {
563 var item = filtered[i];
564 var cls = i === selected ? 'cmdk-item cmdk-active' : 'cmdk-item';
565 var bg = i === selected ? 'background:var(--bg);' : '';
ea52715copilot-swe-agent[bot]566 html += '<div class="' + cls + '" data-idx="' + i + '" data-url="' + item.href + '"' +
71cd5ecClaude567 ' style="padding:10px 16px;cursor:pointer;border-bottom:1px solid var(--border);' + bg + '">' +
568 '<div>' + item.label + '</div>' +
569 '<div style="font-size:11px;color:var(--text-muted)">' + item.href + '</div>' +
570 '</div>';
571 }
572 if (filtered.length === 0) {
573 html = '<div style="padding:16px;color:var(--text-muted);text-align:center">No matches.</div>';
574 }
575 list.innerHTML = html;
576 }
577
578 function openPalette(){
579 backdrop = document.getElementById('cmdk-backdrop');
580 panel = document.getElementById('cmdk-panel');
581 input = document.getElementById('cmdk-input');
582 list = document.getElementById('cmdk-list');
583 if (!backdrop || !panel) return;
584 backdrop.style.display = 'block';
585 panel.style.display = 'block';
586 input.value = '';
587 selected = 0;
588 filtered = COMMANDS.slice();
589 render();
590 input.focus();
591 }
592 function closePalette(){
593 if (backdrop) backdrop.style.display = 'none';
594 if (panel) panel.style.display = 'none';
595 }
596 function go(href){ closePalette(); window.location.href = href; }
597
598 document.addEventListener('click', function(e){
599 var t = e.target;
600 if (t && t.id === 'cmdk-backdrop') { closePalette(); return; }
601 var item = t && t.closest && t.closest('.cmdk-item');
ea52715copilot-swe-agent[bot]602 if (item) { go(item.getAttribute('data-url')); }
71cd5ecClaude603 });
604
605 document.addEventListener('input', function(e){
606 if (e.target && e.target.id === 'cmdk-input') {
607 var q = e.target.value;
608 filtered = COMMANDS.filter(function(c){ return fuzzyMatch(c, q); });
609 selected = 0;
610 render();
611 }
612 });
613
3ef4c9dClaude614 document.addEventListener('keydown', function(e){
71cd5ecClaude615 // Palette-scoped keys take priority when open
616 if (panel && panel.style.display === 'block') {
617 if (e.key === 'Escape') { e.preventDefault(); closePalette(); return; }
618 if (e.key === 'ArrowDown') {
619 e.preventDefault();
620 selected = Math.min(filtered.length - 1, selected + 1);
621 render();
622 return;
623 }
624 if (e.key === 'ArrowUp') {
625 e.preventDefault();
626 selected = Math.max(0, selected - 1);
627 render();
628 return;
629 }
630 if (e.key === 'Enter') {
631 e.preventDefault();
632 var item = filtered[selected];
633 if (item) go(item.href);
634 return;
635 }
636 return;
637 }
638
3ef4c9dClaude639 if (isTyping(e.target)) return;
640 // Single key shortcuts
641 if (e.key === '/') {
642 var el = document.querySelector('.nav-search input');
643 if (el) { e.preventDefault(); el.focus(); return; }
644 }
645 if ((e.metaKey||e.ctrlKey) && e.key.toLowerCase() === 'k') {
71cd5ecClaude646 e.preventDefault();
647 openPalette();
648 return;
3ef4c9dClaude649 }
650 if (e.key === '?' && !e.ctrlKey && !e.metaKey) {
651 e.preventDefault(); window.location.href = '/shortcuts'; return;
652 }
653 if (e.key === 'n' && !e.ctrlKey && !e.metaKey) {
654 e.preventDefault(); window.location.href = '/new'; return;
655 }
656 // "g" chord
657 if (e.key === 'g' && !e.ctrlKey && !e.metaKey) {
658 chord = 'g';
659 clearTimeout(chordTimer);
660 chordTimer = setTimeout(function(){ chord = null; }, 1200);
661 return;
662 }
663 if (chord === 'g') {
664 if (e.key === 'd') { e.preventDefault(); window.location.href = '/dashboard'; }
665 else if (e.key === 'n') { e.preventDefault(); window.location.href = '/notifications'; }
666 else if (e.key === 'e') { e.preventDefault(); window.location.href = '/explore'; }
667 else if (e.key === 'a') { e.preventDefault(); window.location.href = '/ask'; }
668 chord = null;
669 }
670 });
671 })();
672`;
673
fc1817aClaude674const css = `
2ce1d0bClaude675 /* ================================================================
958d26aClaude676 * Gluecron design system — 2026.05 "Editorial-Technical"
677 * Slate-noir base · refined violet signature · hairline geometry ·
678 * mono-as-feature · cinematic motion · Inter Tight + JetBrains Mono.
679 * All class names preserved for back-compat across 50+ route views.
2ce1d0bClaude680 * ============================================================== */
6fc53bdClaude681 :root, :root[data-theme='dark'] {
958d26aClaude682 /* Surfaces — slate, not black. More depth, less crush. */
683 --bg: #08090f;
684 --bg-secondary: #0c0d14;
685 --bg-tertiary: #11131c;
686 --bg-elevated: #0f111a;
687 --bg-surface: #161826;
688 --bg-hover: rgba(255,255,255,0.04);
689 --bg-active: rgba(255,255,255,0.08);
690 --bg-inset: rgba(0,0,0,0.30);
691
692 /* Borders — three weights, used deliberately */
693 --border: rgba(255,255,255,0.06);
694 --border-subtle: rgba(255,255,255,0.035);
695 --border-strong: rgba(255,255,255,0.13);
696 --border-focus: rgba(140,109,255,0.55);
697
698 /* Text */
699 --text: #ededf2;
700 --text-strong: #f7f7fb;
701 --text-muted: #8b8c9c;
702 --text-faint: #555665;
703 --text-link: #b69dff;
704
705 /* Accent — refined violet (less candy), warm amber as secondary signal */
706 --accent: #8c6dff;
707 --accent-2: #36c5d6;
708 --accent-warm: #ffb45e;
709 --accent-hover: #a48bff;
710 --accent-pressed:#7559e8;
711 --accent-gradient: linear-gradient(135deg, #8c6dff 0%, #36c5d6 100%);
712 --accent-gradient-soft: linear-gradient(135deg, rgba(140,109,255,0.18) 0%, rgba(54,197,214,0.18) 100%);
713 --accent-gradient-faint: linear-gradient(135deg, rgba(140,109,255,0.07) 0%, rgba(54,197,214,0.07) 100%);
714 --accent-glow: 0 0 24px rgba(140,109,255,0.28);
715
716 /* Semantic */
717 --green: #34d399;
718 --red: #f87171;
719 --yellow: #fbbf24;
720 --amber: #fbbf24;
721 --blue: #60a5fa;
722
fb71554Claude723 /* Type — system fonts FIRST so we never depend on Google Fonts loading.
724 Segoe UI (Win), -apple-system / SF (Mac), Roboto (Android), Inter as
725 optional upgrade if the user already has it. NEVER falls back to serif. */
726 --font-mono: ui-monospace, 'SF Mono', 'Cascadia Code', 'Cascadia Mono', Menlo, Consolas, 'Courier New', monospace;
727 --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI Variable', 'Segoe UI', system-ui, Roboto, 'Helvetica Neue', Arial, 'Inter', sans-serif;
728 --font-display: -apple-system, BlinkMacSystemFont, 'Segoe UI Variable', 'Segoe UI', system-ui, Roboto, 'Helvetica Neue', Arial, 'Inter Tight', 'Inter', sans-serif;
958d26aClaude729 --mono-feat: 'calt', 'liga', 'ss01';
730
731 /* Radius — sharper than before */
732 --r-sm: 5px;
733 --r: 7px;
734 --r-md: 9px;
735 --r-lg: 12px;
736 --r-xl: 16px;
737 --r-2xl: 22px;
738 --r-full: 9999px;
739 --radius: 7px;
740
741 /* Type scale — bigger display sizes for editorial feel */
742 --t-xs: 11px;
743 --t-sm: 13px;
744 --t-base: 14px;
745 --t-md: 16px;
746 --t-lg: 20px;
747 --t-xl: 28px;
748 --t-2xl: 40px;
749 --t-3xl: 56px;
750 --t-display: 72px;
751 --t-display-lg:96px;
752
753 /* Spacing — 4px base */
2ce1d0bClaude754 --s-0: 0;
958d26aClaude755 --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px;
756 --s-5: 20px; --s-6: 24px; --s-7: 28px; --s-8: 32px;
757 --s-10:40px; --s-12:48px; --s-14:56px; --s-16:64px;
758 --s-20:80px; --s-24:96px; --s-32:128px;
759
760 /* Elevation — softer + more layered */
761 --elev-0: 0 0 0 1px var(--border);
762 --elev-1: 0 1px 2px rgba(0,0,0,0.50), 0 0 0 1px var(--border);
763 --elev-2: 0 8px 24px -8px rgba(0,0,0,0.60), 0 0 0 1px var(--border);
764 --elev-3: 0 20px 48px -12px rgba(0,0,0,0.70), 0 0 0 1px var(--border-strong);
765 --elev-glow: 0 0 0 1px rgba(140,109,255,0.40), 0 0 32px -4px rgba(140,109,255,0.30);
766 --ring: 0 0 0 3px rgba(140,109,255,0.28);
767 --ring-warn: 0 0 0 3px rgba(251,191,36,0.28);
768 --ring-err: 0 0 0 3px rgba(248,113,113,0.28);
769
770 /* Motion */
771 --ease: cubic-bezier(0.16, 1, 0.3, 1);
772 --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
773 --ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1);
774 --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
775 --t-fast: 120ms;
776 --t-base: 200ms;
777 --t-slow: 360ms;
778 --t-slower:560ms;
779
780 --header-h: 60px;
fc1817aClaude781 }
782
6fc53bdClaude783 :root[data-theme='light'] {
958d26aClaude784 --bg: #fbfbfc;
4c47454Claude785 --bg-secondary: #ffffff;
958d26aClaude786 --bg-tertiary: #f3f3f6;
787 --bg-elevated: #ffffff;
788 --bg-surface: #f6f6f9;
789 --bg-hover: rgba(0,0,0,0.035);
790 --bg-active: rgba(0,0,0,0.07);
791 --bg-inset: rgba(0,0,0,0.04);
792
793 --border: rgba(15,16,28,0.08);
794 --border-subtle: rgba(15,16,28,0.04);
795 --border-strong: rgba(15,16,28,0.16);
796
797 --text: #0e1020;
798 --text-strong: #050617;
799 --text-muted: #5a5b70;
800 --text-faint: #8a8b9e;
801 --text-link: #6d4dff;
802
803 --accent: #6d4dff;
804 --accent-2: #0891b2;
805 --accent-hover: #5a3df0;
806 --accent-pressed:#4a30d6;
807 --accent-glow: 0 0 24px rgba(109,77,255,0.18);
808
809 --green: #059669;
810 --red: #dc2626;
4c47454Claude811 --yellow: #d97706;
958d26aClaude812
813 --elev-1: 0 1px 2px rgba(15,16,28,0.06), 0 0 0 1px var(--border);
814 --elev-2: 0 8px 24px -10px rgba(15,16,28,0.10), 0 0 0 1px var(--border);
815 --elev-3: 0 20px 48px -16px rgba(15,16,28,0.14), 0 0 0 1px var(--border-strong);
6fc53bdClaude816 }
817
818 /* Theme toggle — show the icon for the *opposite* theme so users see what they'll switch to. */
958d26aClaude819 .nav-theme { display: inline-flex; align-items: center; font-size: 15px; line-height: 1; opacity: 0.85; }
820 .nav-theme:hover { opacity: 1; }
6fc53bdClaude821 :root[data-theme='dark'] .theme-icon-dark { display: none; }
822 :root[data-theme='light'] .theme-icon-light { display: none; }
823
fc1817aClaude824 * { margin: 0; padding: 0; box-sizing: border-box; }
958d26aClaude825 *::selection { background: rgba(140,109,255,0.32); color: var(--text-strong); }
2ce1d0bClaude826
958d26aClaude827 html { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; }
fc1817aClaude828
829 body {
830 font-family: var(--font-sans);
831 background: var(--bg);
832 color: var(--text);
2ce1d0bClaude833 line-height: 1.55;
958d26aClaude834 letter-spacing: -0.011em;
fc1817aClaude835 min-height: 100vh;
836 display: flex;
837 flex-direction: column;
958d26aClaude838 font-feature-settings: 'cv11', 'ss01', 'ss03', 'calt';
fc1817aClaude839 }
840
958d26aClaude841 /* Whole-page atmosphere: very subtle gradient + dot-grid layered behind everything.
842 Keeps every page feeling like part of the same product without competing with hero art. */
2ce1d0bClaude843 body::before {
844 content: '';
845 position: fixed;
846 inset: 0;
847 pointer-events: none;
958d26aClaude848 z-index: -2;
2ce1d0bClaude849 background:
958d26aClaude850 radial-gradient(70% 55% at 85% -20%, rgba(140,109,255,0.07), transparent 65%),
851 radial-gradient(55% 45% at -10% 115%, rgba(54,197,214,0.05), transparent 65%);
852 }
853 body::after {
854 content: '';
855 position: fixed;
856 inset: 0;
857 pointer-events: none;
858 z-index: -1;
859 background-image: radial-gradient(rgba(255,255,255,0.025) 1px, transparent 1px);
860 background-size: 28px 28px;
861 background-position: 0 0;
862 opacity: 0.55;
863 mask-image: radial-gradient(ellipse at 50% 0%, #000 0%, transparent 70%);
864 -webkit-mask-image: radial-gradient(ellipse at 50% 0%, #000 0%, transparent 70%);
865 }
866 :root[data-theme='light'] body::before { opacity: 0.55; }
867 :root[data-theme='light'] body::after {
868 background-image: radial-gradient(rgba(15,16,28,0.06) 1px, transparent 1px);
869 opacity: 0.4;
fc1817aClaude870 }
2ce1d0bClaude871
872 a { color: var(--text-link); text-decoration: none; transition: color var(--t-fast) var(--ease); }
873 a:hover { color: var(--accent-hover); text-decoration: none; }
fc1817aClaude874
958d26aClaude875 /* Heading scale — confident, editorial, tight tracking */
876 h1, h2, h3, h4, h5, h6 {
877 font-family: var(--font-display);
2ce1d0bClaude878 font-weight: 600;
958d26aClaude879 letter-spacing: -0.022em;
880 line-height: 1.18;
881 color: var(--text-strong);
882 }
883 h1 { font-size: var(--t-xl); letter-spacing: -0.028em; }
884 h2 { font-size: var(--t-lg); letter-spacing: -0.022em; }
885 h3 { font-size: var(--t-md); font-weight: 600; letter-spacing: -0.015em; }
886 h4 { font-size: var(--t-base); font-weight: 600; letter-spacing: -0.01em; }
887 h5, h6 { font-size: var(--t-sm); font-weight: 600; letter-spacing: -0.005em; }
888
889 /* Editorial display heading utility — used by landing + marketing pages */
890 .display {
891 font-family: var(--font-display);
892 font-size: clamp(40px, 7.5vw, 96px);
893 line-height: 0.98;
894 letter-spacing: -0.04em;
895 font-weight: 600;
896 color: var(--text-strong);
2ce1d0bClaude897 }
fc1817aClaude898
958d26aClaude899 /* Eyebrow — uppercase mono label that sits above section headings */
900 .eyebrow {
901 display: inline-flex;
902 align-items: center;
903 gap: 8px;
904 font-family: var(--font-mono);
905 font-size: 11px;
906 font-weight: 500;
907 letter-spacing: 0.14em;
908 text-transform: uppercase;
909 color: var(--accent);
910 margin-bottom: var(--s-3);
911 }
912 .eyebrow::before {
913 content: '';
914 width: 18px;
915 height: 1px;
916 background: currentColor;
917 opacity: 0.6;
918 }
919
920 /* Section header — paired eyebrow + title + lede */
921 .section-header { max-width: 720px; margin: 0 auto var(--s-10); text-align: center; }
922 .section-header.left { text-align: left; margin-left: 0; margin-right: auto; }
923 .section-header h2 {
924 font-size: clamp(28px, 4vw, 44px);
925 line-height: 1.05;
926 letter-spacing: -0.028em;
927 margin-bottom: var(--s-3);
928 }
929 .section-header p {
930 color: var(--text-muted);
931 font-size: var(--t-md);
932 line-height: 1.6;
933 max-width: 580px;
934 margin: 0 auto;
935 }
936 .section-header.left p { margin-left: 0; }
fc1817aClaude937
958d26aClaude938 code, kbd, samp {
2ce1d0bClaude939 font-family: var(--font-mono);
958d26aClaude940 font-feature-settings: var(--mono-feat);
941 }
942 code {
943 font-size: 0.9em;
2ce1d0bClaude944 background: var(--bg-tertiary);
958d26aClaude945 border: 1px solid var(--border-subtle);
2ce1d0bClaude946 padding: 1px 6px;
947 border-radius: var(--r-sm);
948 color: var(--text);
949 }
958d26aClaude950 kbd, .kbd {
951 display: inline-flex;
952 align-items: center;
953 padding: 1px 6px;
954 font-family: var(--font-mono);
955 font-size: 11px;
956 background: var(--bg-elevated);
957 border: 1px solid var(--border);
958 border-bottom-width: 2px;
959 border-radius: 4px;
960 color: var(--text);
961 line-height: 1.5;
962 vertical-align: middle;
963 }
2ce1d0bClaude964
958d26aClaude965 /* Mono utility for technical chrome (paths, IDs, dates) */
966 .mono { font-family: var(--font-mono); font-feature-settings: var(--mono-feat); font-size: 0.96em; }
967 .meta-mono { font-family: var(--font-mono); font-size: var(--t-xs); color: var(--text-muted); letter-spacing: 0; }
968
969 /* Pre-launch banner — slim, refined, mono caption */
4a52a98Claude970 .prelaunch-banner {
958d26aClaude971 position: relative;
2ce1d0bClaude972 background:
958d26aClaude973 linear-gradient(180deg, rgba(251,191,36,0.10), rgba(251,191,36,0.03)),
2ce1d0bClaude974 var(--bg);
958d26aClaude975 border-bottom: 1px solid rgba(251,191,36,0.28);
4a52a98Claude976 color: var(--yellow);
958d26aClaude977 padding: 7px 24px;
978 font-family: var(--font-mono);
979 font-size: 11px;
4a52a98Claude980 font-weight: 500;
981 text-align: center;
2ce1d0bClaude982 line-height: 1.5;
958d26aClaude983 letter-spacing: 0.04em;
984 text-transform: uppercase;
985 }
986 .prelaunch-banner::before {
987 content: '◆';
988 margin-right: 8px;
989 font-size: 9px;
990 opacity: 0.7;
991 vertical-align: 1px;
4a52a98Claude992 }
993
958d26aClaude994 /* Header — sticky, blurred, hairline border, taller for breathing room */
fc1817aClaude995 header {
2ce1d0bClaude996 position: sticky;
997 top: 0;
998 z-index: 100;
fc1817aClaude999 border-bottom: 1px solid var(--border);
2ce1d0bClaude1000 padding: 0 24px;
1001 height: var(--header-h);
958d26aClaude1002 background: rgba(8,9,15,0.72);
1003 backdrop-filter: saturate(180%) blur(18px);
1004 -webkit-backdrop-filter: saturate(180%) blur(18px);
fc1817aClaude1005 }
958d26aClaude1006 :root[data-theme='light'] header { background: rgba(251,251,252,0.78); }
fc1817aClaude1007
06d5ffeClaude1008 header nav {
1009 display: flex;
1010 align-items: center;
958d26aClaude1011 gap: 18px;
1012 max-width: 1240px;
06d5ffeClaude1013 margin: 0 auto;
2ce1d0bClaude1014 height: 100%;
1015 }
1016 .logo {
958d26aClaude1017 font-family: var(--font-display);
1018 font-size: 16px;
2ce1d0bClaude1019 font-weight: 700;
958d26aClaude1020 letter-spacing: -0.025em;
1021 color: var(--text-strong);
2ce1d0bClaude1022 display: inline-flex;
1023 align-items: center;
958d26aClaude1024 gap: 9px;
1025 transition: opacity var(--t-fast) var(--ease);
06d5ffeClaude1026 }
2ce1d0bClaude1027 .logo::before {
1028 content: '';
958d26aClaude1029 width: 20px; height: 20px;
1030 border-radius: 6px;
2ce1d0bClaude1031 background: var(--accent-gradient);
958d26aClaude1032 box-shadow:
1033 inset 0 1px 0 rgba(255,255,255,0.25),
1034 0 0 0 1px rgba(140,109,255,0.45),
1035 0 0 20px rgba(140,109,255,0.30);
2ce1d0bClaude1036 flex-shrink: 0;
958d26aClaude1037 transition: transform var(--t-base) var(--ease-spring), box-shadow var(--t-base) var(--ease);
1038 }
1039 .logo:hover { text-decoration: none; color: var(--text-strong); }
1040 .logo:hover::before {
1041 transform: rotate(8deg) scale(1.05);
1042 box-shadow:
1043 inset 0 1px 0 rgba(255,255,255,0.30),
1044 0 0 0 1px rgba(140,109,255,0.55),
1045 0 0 28px rgba(140,109,255,0.45);
06d5ffeClaude1046 }
fc1817aClaude1047
2ce1d0bClaude1048 .nav-search {
1049 flex: 1;
958d26aClaude1050 max-width: 360px;
1051 margin: 0 4px 0 8px;
1052 position: relative;
2ce1d0bClaude1053 }
1054 .nav-search input {
1055 width: 100%;
958d26aClaude1056 padding: 7px 12px 7px 32px;
2ce1d0bClaude1057 background: var(--bg-tertiary);
1058 border: 1px solid var(--border);
1059 border-radius: var(--r-sm);
1060 color: var(--text);
1061 font-family: var(--font-sans);
1062 font-size: var(--t-sm);
958d26aClaude1063 transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
1064 }
1065 .nav-search::before {
1066 content: '';
1067 position: absolute;
1068 left: 11px; top: 50%;
1069 transform: translateY(-50%);
1070 width: 12px; height: 12px;
1071 border: 1.5px solid var(--text-faint);
1072 border-radius: 50%;
1073 pointer-events: none;
1074 }
1075 .nav-search::after {
1076 content: '';
1077 position: absolute;
1078 left: 19px; top: calc(50% + 4px);
1079 width: 5px; height: 1.5px;
1080 background: var(--text-faint);
1081 transform: rotate(45deg);
1082 pointer-events: none;
2ce1d0bClaude1083 }
1084 .nav-search input::placeholder { color: var(--text-faint); }
1085 .nav-search input:focus {
1086 outline: none;
1087 background: var(--bg-secondary);
958d26aClaude1088 border-color: var(--border-focus);
1089 box-shadow: var(--ring);
2ce1d0bClaude1090 }
06d5ffeClaude1091
958d26aClaude1092 .nav-right { display: flex; align-items: center; gap: 2px; margin-left: auto; }
f764c07Claude1093
1094 /* Block N3 — site-admin deploy status pill */
1095 .nav-deploy-pill {
1096 display: inline-flex;
1097 align-items: center;
1098 gap: 6px;
1099 padding: 4px 10px;
1100 margin: 0 6px 0 0;
1101 border-radius: 9999px;
1102 font-size: 11px;
1103 font-weight: 600;
1104 line-height: 1;
1105 color: var(--text-strong);
1106 background: var(--bg-elevated);
1107 border: 1px solid var(--border);
1108 text-decoration: none;
1109 transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
1110 }
1111 .nav-deploy-pill:hover { background: var(--bg-hover); text-decoration: none; }
1112 .nav-deploy-pill .deploy-pill-dot {
1113 display: inline-block;
1114 width: 8px; height: 8px;
1115 border-radius: 50%;
1116 background: #6b7280;
1117 }
1118 .deploy-pill-success .deploy-pill-dot { background: #34d399; box-shadow: 0 0 6px rgba(52,211,153,0.45); }
1119 .deploy-pill-failed .deploy-pill-dot { background: #f87171; box-shadow: 0 0 6px rgba(248,113,113,0.45); }
1120 .deploy-pill-failed { border-color: rgba(248,113,113,0.4); }
1121 .deploy-pill-progress .deploy-pill-dot {
1122 background: #fbbf24;
1123 box-shadow: 0 0 6px rgba(251,191,36,0.55);
1124 animation: deployPillPulse 1.2s ease-in-out infinite;
1125 }
1126 @keyframes deployPillPulse {
1127 0%, 100% { opacity: 1; transform: scale(1); }
1128 50% { opacity: 0.45; transform: scale(0.8); }
1129 }
1130 .deploy-pill-empty .deploy-pill-dot { background: #9ca3af; }
1131
2ce1d0bClaude1132 .nav-link {
958d26aClaude1133 position: relative;
2ce1d0bClaude1134 color: var(--text-muted);
1135 font-size: var(--t-sm);
1136 font-weight: 500;
958d26aClaude1137 padding: 7px 11px;
2ce1d0bClaude1138 border-radius: var(--r-sm);
958d26aClaude1139 transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
2ce1d0bClaude1140 }
1141 .nav-link:hover {
958d26aClaude1142 color: var(--text-strong);
2ce1d0bClaude1143 background: var(--bg-hover);
1144 text-decoration: none;
1145 }
958d26aClaude1146 .nav-link.active { color: var(--text-strong); }
1147 .nav-link.active::after {
1148 content: '';
1149 position: absolute;
1150 left: 11px; right: 11px;
1151 bottom: -1px;
1152 height: 2px;
1153 background: var(--accent-gradient);
1154 border-radius: 2px;
1155 }
2ce1d0bClaude1156 .nav-user {
958d26aClaude1157 color: var(--text-strong);
2ce1d0bClaude1158 font-weight: 600;
1159 font-size: var(--t-sm);
1160 padding: 6px 10px;
1161 border-radius: var(--r-sm);
958d26aClaude1162 margin-left: 6px;
2ce1d0bClaude1163 transition: background var(--t-fast) var(--ease);
1164 }
958d26aClaude1165 .nav-user::before {
1166 content: '';
1167 display: inline-block;
1168 width: 8px; height: 8px;
1169 background: var(--green);
1170 border-radius: 50%;
1171 margin-right: 7px;
1172 box-shadow: 0 0 8px rgba(52,211,153,0.5);
1173 vertical-align: 1px;
1174 }
2ce1d0bClaude1175 .nav-user:hover { background: var(--bg-hover); text-decoration: none; }
fc1817aClaude1176
2ce1d0bClaude1177 main {
958d26aClaude1178 max-width: 1240px;
2ce1d0bClaude1179 margin: 0 auto;
958d26aClaude1180 padding: 36px 24px 80px;
2ce1d0bClaude1181 flex: 1;
1182 width: 100%;
1183 }
fc1817aClaude1184
958d26aClaude1185 /* Editorial footer: link grid + tagline row */
fc1817aClaude1186 footer {
1187 border-top: 1px solid var(--border);
958d26aClaude1188 padding: 56px 24px 40px;
fc1817aClaude1189 color: var(--text-muted);
958d26aClaude1190 font-size: var(--t-sm);
1191 background:
1192 linear-gradient(180deg, transparent 0%, rgba(140,109,255,0.025) 100%),
1193 var(--bg);
1194 }
1195 footer .footer-inner {
1196 max-width: 1240px;
1197 margin: 0 auto;
1198 display: grid;
1199 grid-template-columns: 1fr auto;
1200 gap: 48px;
1201 align-items: start;
1202 }
1203 footer .footer-brand .logo { margin-bottom: var(--s-3); }
1204 footer .footer-tag {
1205 color: var(--text-muted);
1206 font-size: var(--t-sm);
1207 max-width: 320px;
1208 line-height: 1.55;
1209 }
1210 footer .footer-links {
1211 display: grid;
1212 grid-template-columns: repeat(3, minmax(120px, auto));
1213 gap: 0 56px;
1214 }
1215 footer .footer-col-title {
1216 font-family: var(--font-mono);
1217 font-size: 11px;
1218 text-transform: uppercase;
1219 letter-spacing: 0.14em;
1220 color: var(--text-faint);
1221 margin-bottom: var(--s-3);
1222 }
1223 footer .footer-col a {
1224 display: block;
1225 color: var(--text-muted);
1226 font-size: var(--t-sm);
1227 padding: 5px 0;
1228 transition: color var(--t-fast) var(--ease);
1229 }
1230 footer .footer-col a:hover { color: var(--text-strong); text-decoration: none; }
1231 footer .footer-bottom {
1232 max-width: 1240px;
1233 margin: 40px auto 0;
1234 padding-top: 24px;
1235 border-top: 1px solid var(--border-subtle);
1236 display: flex;
1237 justify-content: space-between;
1238 align-items: center;
2ce1d0bClaude1239 color: var(--text-faint);
1240 font-size: var(--t-xs);
958d26aClaude1241 font-family: var(--font-mono);
1242 letter-spacing: 0.02em;
1243 }
1244 footer .footer-bottom a { color: var(--text-faint); }
1245 footer .footer-bottom a:hover { color: var(--text-muted); text-decoration: none; }
05cdb85Claude1246 footer .footer-build {
1247 display: inline-flex;
1248 align-items: center;
1249 gap: 6px;
1250 color: var(--text-faint);
1251 font-family: var(--font-mono);
1252 font-size: 11px;
1253 cursor: help;
1254 }
1255 footer .footer-build-dot {
1256 width: 6px; height: 6px;
1257 border-radius: 50%;
1258 background: var(--green);
1259 box-shadow: 0 0 6px rgba(52,211,153,0.55);
1260 animation: footer-build-pulse 2.4s ease-in-out infinite;
1261 }
1262 @keyframes footer-build-pulse {
1263 0%, 100% { opacity: 0.6; }
1264 50% { opacity: 1; }
1265 }
958d26aClaude1266 @media (max-width: 768px) {
1267 footer .footer-inner { grid-template-columns: 1fr; gap: 32px; }
1268 footer .footer-links { grid-template-columns: repeat(2, 1fr); gap: 24px 32px; }
1269 footer .footer-bottom { flex-direction: column; gap: 8px; text-align: center; }
fc1817aClaude1270 }
1271
2ce1d0bClaude1272 /* ============================================================ */
1273 /* Buttons */
1274 /* ============================================================ */
06d5ffeClaude1275 .btn {
1276 display: inline-flex;
1277 align-items: center;
2ce1d0bClaude1278 justify-content: center;
958d26aClaude1279 gap: 7px;
2ce1d0bClaude1280 padding: 7px 14px;
1281 border-radius: var(--r-sm);
958d26aClaude1282 font-family: var(--font-sans);
2ce1d0bClaude1283 font-size: var(--t-sm);
06d5ffeClaude1284 font-weight: 500;
1285 border: 1px solid var(--border);
2ce1d0bClaude1286 background: var(--bg-elevated);
06d5ffeClaude1287 color: var(--text);
1288 cursor: pointer;
1289 text-decoration: none;
958d26aClaude1290 line-height: 1.25;
1291 letter-spacing: -0.008em;
2ce1d0bClaude1292 transition:
1293 background var(--t-fast) var(--ease),
1294 border-color var(--t-fast) var(--ease),
1295 transform var(--t-fast) var(--ease),
958d26aClaude1296 box-shadow var(--t-fast) var(--ease),
1297 color var(--t-fast) var(--ease);
2ce1d0bClaude1298 user-select: none;
1299 white-space: nowrap;
958d26aClaude1300 position: relative;
06d5ffeClaude1301 }
2ce1d0bClaude1302 .btn:hover {
1303 background: var(--bg-surface);
1304 border-color: var(--border-strong);
958d26aClaude1305 color: var(--text-strong);
2ce1d0bClaude1306 text-decoration: none;
1307 }
958d26aClaude1308 .btn:active { transform: translateY(1px); }
2ce1d0bClaude1309 .btn:focus-visible { outline: none; box-shadow: var(--ring); }
1310
1311 .btn-primary {
1312 background: var(--accent-gradient);
1313 border-color: transparent;
1314 color: #fff;
1315 font-weight: 600;
958d26aClaude1316 text-shadow: 0 1px 0 rgba(0,0,0,0.15);
2ce1d0bClaude1317 box-shadow:
958d26aClaude1318 inset 0 1px 0 rgba(255,255,255,0.22),
1319 inset 0 -1px 0 rgba(0,0,0,0.10),
1320 0 1px 2px rgba(0,0,0,0.40),
1321 0 0 0 1px rgba(140,109,255,0.30);
06d5ffeClaude1322 }
958d26aClaude1323 .btn-primary::before {
1324 content: '';
1325 position: absolute;
1326 inset: 0;
1327 border-radius: inherit;
1328 background: linear-gradient(180deg, rgba(255,255,255,0.18), transparent 60%);
1329 opacity: 0;
1330 transition: opacity var(--t-fast) var(--ease);
1331 pointer-events: none;
2ce1d0bClaude1332 }
1333 .btn-primary:hover {
958d26aClaude1334 color: #fff;
2ce1d0bClaude1335 background: var(--accent-gradient);
958d26aClaude1336 border-color: transparent;
2ce1d0bClaude1337 box-shadow:
958d26aClaude1338 inset 0 1px 0 rgba(255,255,255,0.30),
1339 inset 0 -1px 0 rgba(0,0,0,0.10),
1340 0 6px 18px -4px rgba(140,109,255,0.45),
1341 0 0 0 1px rgba(140,109,255,0.45);
2ce1d0bClaude1342 }
958d26aClaude1343 .btn-primary:hover::before { opacity: 1; }
2ce1d0bClaude1344
1345 .btn-danger {
1346 background: transparent;
958d26aClaude1347 border-color: rgba(248,113,113,0.40);
2ce1d0bClaude1348 color: var(--red);
1349 }
1350 .btn-danger:hover {
958d26aClaude1351 background: rgba(248,113,113,0.08);
2ce1d0bClaude1352 border-color: var(--red);
958d26aClaude1353 color: var(--red);
2ce1d0bClaude1354 }
958d26aClaude1355 .btn-danger:focus-visible { box-shadow: var(--ring-err); }
2ce1d0bClaude1356
1357 .btn-ghost {
1358 background: transparent;
1359 border-color: transparent;
1360 color: var(--text-muted);
1361 }
1362 .btn-ghost:hover {
1363 background: var(--bg-hover);
958d26aClaude1364 color: var(--text-strong);
2ce1d0bClaude1365 border-color: var(--border);
1366 }
1367
958d26aClaude1368 .btn-secondary {
1369 background: var(--bg-elevated);
1370 border-color: var(--border-strong);
1371 color: var(--text-strong);
1372 }
1373 .btn-secondary:hover {
1374 background: var(--bg-surface);
1375 border-color: var(--border-strong);
1376 }
1377
1378 .btn-sm { padding: 4px 10px; font-size: var(--t-xs); border-radius: var(--r-sm); gap: 5px; }
1379 .btn-lg { padding: 11px 22px; font-size: var(--t-base); border-radius: var(--r); }
1380 .btn-xl { padding: 14px 28px; font-size: var(--t-md); border-radius: var(--r); font-weight: 600; }
1381 .btn-block { width: 100%; }
2ce1d0bClaude1382
1383 .btn:disabled, .btn[aria-disabled='true'] {
958d26aClaude1384 opacity: 0.45;
2ce1d0bClaude1385 cursor: not-allowed;
1386 pointer-events: none;
1387 }
1388
1389 /* ============================================================ */
1390 /* Forms */
1391 /* ============================================================ */
1392 .form-group { margin-bottom: 20px; }
1393 .form-group label {
1394 display: block;
1395 font-size: var(--t-sm);
1396 font-weight: 500;
1397 margin-bottom: 6px;
1398 color: var(--text);
1399 letter-spacing: -0.005em;
1400 }
1401 .form-group input,
1402 .form-group textarea,
1403 .form-group select,
1404 input[type='text'], input[type='email'], input[type='password'],
1405 input[type='url'], input[type='search'], input[type='number'],
1406 textarea, select {
06d5ffeClaude1407 width: 100%;
2ce1d0bClaude1408 padding: 9px 12px;
1409 background: var(--bg-secondary);
06d5ffeClaude1410 border: 1px solid var(--border);
2ce1d0bClaude1411 border-radius: var(--r-sm);
06d5ffeClaude1412 color: var(--text);
2ce1d0bClaude1413 font-size: var(--t-sm);
06d5ffeClaude1414 font-family: var(--font-sans);
2ce1d0bClaude1415 transition:
1416 border-color var(--t-fast) var(--ease),
1417 background var(--t-fast) var(--ease),
1418 box-shadow var(--t-fast) var(--ease);
06d5ffeClaude1419 }
2ce1d0bClaude1420 .form-group input::placeholder, textarea::placeholder, input::placeholder {
1421 color: var(--text-faint);
06d5ffeClaude1422 }
2ce1d0bClaude1423 .form-group input:hover, textarea:hover, select:hover,
1424 input[type='text']:hover, input[type='email']:hover, input[type='password']:hover {
1425 border-color: var(--border-strong);
1426 }
1427 .form-group input:focus, .form-group textarea:focus, .form-group select:focus,
1428 input:focus, textarea:focus, select:focus {
06d5ffeClaude1429 outline: none;
2ce1d0bClaude1430 background: var(--bg);
1431 border-color: var(--border-focus);
1432 box-shadow: var(--ring);
06d5ffeClaude1433 }
2ce1d0bClaude1434 textarea { font-family: var(--font-mono); font-size: var(--t-sm); line-height: 1.55; }
06d5ffeClaude1435 .input-disabled { opacity: 0.5; cursor: not-allowed; }
1436
2ce1d0bClaude1437 /* ============================================================ */
1438 /* Auth (register / login / verify) */
1439 /* ============================================================ */
1440 .auth-container {
1441 max-width: 420px;
1442 margin: 64px auto;
1443 padding: 32px;
1444 background: var(--bg-elevated);
1445 border: 1px solid var(--border);
1446 border-radius: var(--r-lg);
1447 box-shadow: var(--elev-2);
1448 }
1449 .auth-container h2 {
1450 margin-bottom: 6px;
1451 font-size: var(--t-lg);
1452 letter-spacing: -0.02em;
1453 }
1454 .auth-container > p {
1455 color: var(--text-muted);
1456 font-size: var(--t-sm);
1457 margin-bottom: 24px;
1458 }
1459 .auth-container .btn-primary { width: 100%; padding: 10px 16px; }
06d5ffeClaude1460 .auth-error {
958d26aClaude1461 background: rgba(248,113,113,0.08);
1462 border: 1px solid rgba(248,113,113,0.35);
06d5ffeClaude1463 color: var(--red);
2ce1d0bClaude1464 padding: 10px 14px;
1465 border-radius: var(--r-sm);
06d5ffeClaude1466 margin-bottom: 16px;
2ce1d0bClaude1467 font-size: var(--t-sm);
06d5ffeClaude1468 }
1469 .auth-success {
958d26aClaude1470 background: rgba(52,211,153,0.08);
1471 border: 1px solid rgba(52,211,153,0.35);
06d5ffeClaude1472 color: var(--green);
2ce1d0bClaude1473 padding: 10px 14px;
1474 border-radius: var(--r-sm);
06d5ffeClaude1475 margin-bottom: 16px;
2ce1d0bClaude1476 font-size: var(--t-sm);
1477 }
1478 .auth-switch {
1479 margin-top: 20px;
1480 font-size: var(--t-sm);
1481 color: var(--text-muted);
1482 text-align: center;
1483 }
1484 .banner {
1485 background: var(--accent-gradient-faint);
958d26aClaude1486 border: 1px solid rgba(140,109,255,0.35);
2ce1d0bClaude1487 color: var(--text);
1488 padding: 10px 14px;
1489 border-radius: var(--r-sm);
1490 font-size: var(--t-sm);
06d5ffeClaude1491 }
1492
2ce1d0bClaude1493 /* ============================================================ */
1494 /* Settings */
1495 /* ============================================================ */
1496 .settings-container { max-width: 720px; }
1497 .settings-container h2 { margin-bottom: 8px; font-size: var(--t-xl); letter-spacing: -0.02em; }
1498 .settings-container > h2 + p { color: var(--text-muted); font-size: var(--t-sm); margin-bottom: 24px; }
1499 .settings-container h3 { font-size: var(--t-md); margin-bottom: 12px; margin-top: 28px; }
1500 .settings-container h3:first-of-type { margin-top: 0; }
06d5ffeClaude1501 .ssh-keys-list { margin-bottom: 24px; }
1502 .ssh-key-item {
1503 display: flex;
1504 justify-content: space-between;
1505 align-items: center;
2ce1d0bClaude1506 padding: 14px 16px;
06d5ffeClaude1507 border: 1px solid var(--border);
2ce1d0bClaude1508 border-radius: var(--r-md);
06d5ffeClaude1509 margin-bottom: 8px;
2ce1d0bClaude1510 background: var(--bg-elevated);
1511 transition: border-color var(--t-fast) var(--ease);
06d5ffeClaude1512 }
2ce1d0bClaude1513 .ssh-key-item:hover { border-color: var(--border-strong); }
1514 .ssh-key-meta { font-size: var(--t-xs); color: var(--text-muted); margin-top: 4px; }
1515 .ssh-key-meta code { font-size: var(--t-xs); background: var(--bg-tertiary); padding: 1px 6px; border-radius: 3px; margin-right: 8px; }
06d5ffeClaude1516
2ce1d0bClaude1517 /* ============================================================ */
1518 /* Repo header + nav */
1519 /* ============================================================ */
fc1817aClaude1520 .repo-header {
1521 display: flex;
1522 align-items: center;
debcf27Claude1523 gap: 12px;
1524 margin-bottom: 22px;
1525 }
1526 .repo-header-title {
1527 display: flex;
1528 align-items: center;
1529 gap: 10px;
1530 font-family: var(--font-display);
1531 font-size: 24px;
1532 letter-spacing: -0.025em;
1533 flex-wrap: wrap;
1534 }
1535 .repo-header .owner {
1536 color: var(--text-muted);
1537 font-weight: 500;
1538 transition: color var(--t-fast) var(--ease);
1539 }
1540 .repo-header .owner:hover { color: var(--text-link); text-decoration: none; }
1541 .repo-header .separator { color: var(--text-faint); font-weight: 300; }
1542 .repo-header .name {
1543 color: var(--text-strong);
1544 font-weight: 700;
1545 letter-spacing: -0.028em;
fc1817aClaude1546 }
2ce1d0bClaude1547 .repo-header .name:hover { color: var(--text-link); text-decoration: none; }
debcf27Claude1548 .repo-header-fork {
1549 font-family: var(--font-mono);
1550 font-size: 11px;
1551 color: var(--text-muted);
1552 margin-top: 4px;
1553 letter-spacing: 0.01em;
1554 }
1555 .repo-header-fork a { color: var(--text-muted); }
1556 .repo-header-fork a:hover { color: var(--accent); }
1557 .repo-header-pill {
1558 display: inline-flex;
1559 align-items: center;
1560 padding: 2px 10px;
1561 border-radius: var(--r-full);
1562 font-family: var(--font-mono);
1563 font-size: 10px;
1564 font-weight: 600;
1565 letter-spacing: 0.12em;
1566 text-transform: uppercase;
1567 line-height: 1.6;
1568 vertical-align: 4px;
1569 }
1570 .repo-header-pill-archived {
1571 background: rgba(251,191,36,0.10);
1572 color: var(--yellow);
1573 border: 1px solid rgba(251,191,36,0.30);
1574 }
1575 .repo-header-pill-template {
1576 background: var(--accent-gradient-faint);
1577 color: var(--accent);
1578 border: 1px solid rgba(140,109,255,0.30);
fc1817aClaude1579 }
06d5ffeClaude1580 .repo-header-actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }
fc1817aClaude1581
1582 .repo-nav {
1583 display: flex;
debcf27Claude1584 gap: 1px;
fc1817aClaude1585 border-bottom: 1px solid var(--border);
debcf27Claude1586 margin-bottom: 28px;
2ce1d0bClaude1587 overflow-x: auto;
1588 scrollbar-width: thin;
fc1817aClaude1589 }
2ce1d0bClaude1590 .repo-nav::-webkit-scrollbar { height: 0; }
fc1817aClaude1591 .repo-nav a {
debcf27Claude1592 position: relative;
1593 padding: 11px 14px;
fc1817aClaude1594 color: var(--text-muted);
1595 border-bottom: 2px solid transparent;
2ce1d0bClaude1596 font-size: var(--t-sm);
1597 font-weight: 500;
1598 margin-bottom: -1px;
debcf27Claude1599 transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
2ce1d0bClaude1600 white-space: nowrap;
1601 }
debcf27Claude1602 .repo-nav a:hover { text-decoration: none; color: var(--text-strong); background: var(--bg-hover); }
2ce1d0bClaude1603 .repo-nav a.active {
debcf27Claude1604 color: var(--text-strong);
1605 font-weight: 600;
1606 }
1607 .repo-nav a.active::after {
1608 content: '';
1609 position: absolute;
1610 left: 14px;
1611 right: 14px;
1612 bottom: -1px;
1613 height: 2px;
1614 background: var(--accent-gradient);
1615 border-radius: 2px;
1616 }
1617 /* AI links in the right-side cluster — sparkle accent */
1618 .repo-nav-ai {
1619 color: var(--accent) !important;
1620 font-weight: 500;
1621 }
1622 .repo-nav-ai:hover {
1623 color: var(--accent-hover) !important;
1624 background: var(--accent-gradient-faint) !important;
1625 }
1626 .repo-nav-ai.active {
1627 color: var(--accent-hover) !important;
2ce1d0bClaude1628 font-weight: 600;
fc1817aClaude1629 }
1630
2ce1d0bClaude1631 .breadcrumb {
1632 display: flex;
debcf27Claude1633 gap: 6px;
2ce1d0bClaude1634 align-items: center;
debcf27Claude1635 margin-bottom: 18px;
2ce1d0bClaude1636 color: var(--text-muted);
1637 font-size: var(--t-sm);
1638 font-family: var(--font-mono);
debcf27Claude1639 font-feature-settings: var(--mono-feat);
2ce1d0bClaude1640 }
1641 .breadcrumb a { color: var(--text-link); font-weight: 500; }
1642 .breadcrumb a:hover { color: var(--accent-hover); }
debcf27Claude1643 .breadcrumb strong {
1644 color: var(--text-strong);
1645 font-weight: 600;
fc1817aClaude1646 }
1647
debcf27Claude1648 /* Page header — eyebrow + title + optional actions row.
1649 Use on dashboard, settings, admin, any "section landing" page. */
1650 .page-header {
1651 display: flex;
1652 align-items: flex-end;
1653 justify-content: space-between;
1654 gap: 16px;
1655 margin-bottom: 28px;
1656 padding-bottom: 20px;
1657 border-bottom: 1px solid var(--border-subtle);
1658 flex-wrap: wrap;
1659 }
1660 .page-header-text { flex: 1; min-width: 280px; }
1661 .page-header .eyebrow { margin-bottom: var(--s-2); }
1662 .page-header h1 {
1663 font-family: var(--font-display);
1664 font-size: clamp(24px, 3vw, 36px);
1665 line-height: 1.1;
1666 letter-spacing: -0.028em;
1667 margin-bottom: 6px;
1668 }
1669 .page-header p {
1670 color: var(--text-muted);
1671 font-size: var(--t-sm);
1672 line-height: 1.55;
1673 max-width: 640px;
1674 }
1675 .page-header-actions { display: flex; gap: 8px; align-items: center; }
fc1817aClaude1676
2ce1d0bClaude1677 /* ============================================================ */
1678 /* File browser table */
1679 /* ============================================================ */
1680 .file-table {
1681 width: 100%;
1682 border: 1px solid var(--border);
1683 border-radius: var(--r-md);
1684 overflow: hidden;
1685 background: var(--bg-elevated);
debcf27Claude1686 border-collapse: collapse;
2ce1d0bClaude1687 }
debcf27Claude1688 .file-table tr { border-bottom: 1px solid var(--border-subtle); transition: background var(--t-fast) var(--ease); }
fc1817aClaude1689 .file-table tr:last-child { border-bottom: none; }
debcf27Claude1690 .file-table td {
1691 padding: 9px 16px;
1692 font-size: var(--t-sm);
1693 font-family: var(--font-mono);
1694 font-feature-settings: var(--mono-feat);
1695 }
2ce1d0bClaude1696 .file-table tr:hover { background: var(--bg-hover); }
debcf27Claude1697 .file-icon {
1698 width: 22px;
1699 color: var(--text-faint);
1700 font-size: 13px;
1701 text-align: center;
1702 }
1703 .file-name a {
1704 color: var(--text);
1705 font-weight: 500;
1706 transition: color var(--t-fast) var(--ease);
1707 }
1708 .file-name a:hover { color: var(--accent); text-decoration: none; }
fc1817aClaude1709
2ce1d0bClaude1710 /* ============================================================ */
1711 /* Blob view */
1712 /* ============================================================ */
fc1817aClaude1713 .blob-view {
1714 border: 1px solid var(--border);
2ce1d0bClaude1715 border-radius: var(--r-md);
fc1817aClaude1716 overflow: hidden;
2ce1d0bClaude1717 background: var(--bg-elevated);
fc1817aClaude1718 }
1719 .blob-header {
1720 background: var(--bg-secondary);
2ce1d0bClaude1721 padding: 10px 16px;
fc1817aClaude1722 border-bottom: 1px solid var(--border);
2ce1d0bClaude1723 font-size: var(--t-sm);
fc1817aClaude1724 color: var(--text-muted);
06d5ffeClaude1725 display: flex;
1726 justify-content: space-between;
1727 align-items: center;
fc1817aClaude1728 }
1729 .blob-code {
1730 overflow-x: auto;
1731 font-family: var(--font-mono);
2ce1d0bClaude1732 font-size: var(--t-sm);
1733 line-height: 1.65;
fc1817aClaude1734 }
1735 .blob-code table { width: 100%; border-collapse: collapse; }
1736 .blob-code .line-num {
1737 width: 1%;
2ce1d0bClaude1738 min-width: 56px;
1739 padding: 0 14px;
fc1817aClaude1740 text-align: right;
2ce1d0bClaude1741 color: var(--text-faint);
fc1817aClaude1742 user-select: none;
1743 white-space: nowrap;
1744 border-right: 1px solid var(--border);
1745 }
2ce1d0bClaude1746 .blob-code .line-content { padding: 0 16px; white-space: pre; }
1747 .blob-code tr:hover { background: var(--bg-hover); }
fc1817aClaude1748
2ce1d0bClaude1749 /* ============================================================ */
1750 /* Commits + diffs */
1751 /* ============================================================ */
1752 .commit-list {
1753 border: 1px solid var(--border);
1754 border-radius: var(--r-md);
1755 overflow: hidden;
1756 background: var(--bg-elevated);
1757 }
fc1817aClaude1758 .commit-item {
1759 display: flex;
1760 justify-content: space-between;
1761 align-items: center;
debcf27Claude1762 padding: 14px 18px;
1763 border-bottom: 1px solid var(--border-subtle);
2ce1d0bClaude1764 transition: background var(--t-fast) var(--ease);
fc1817aClaude1765 }
1766 .commit-item:last-child { border-bottom: none; }
2ce1d0bClaude1767 .commit-item:hover { background: var(--bg-hover); }
debcf27Claude1768 .commit-message {
1769 font-size: var(--t-sm);
1770 font-weight: 500;
1771 line-height: 1.45;
1772 color: var(--text-strong);
1773 letter-spacing: -0.005em;
1774 }
1775 .commit-meta {
1776 font-family: var(--font-mono);
1777 font-size: 11px;
1778 color: var(--text-muted);
1779 margin-top: 5px;
1780 letter-spacing: 0.01em;
1781 }
fc1817aClaude1782 .commit-sha {
1783 font-family: var(--font-mono);
debcf27Claude1784 font-feature-settings: var(--mono-feat);
1785 font-size: 11px;
1786 padding: 4px 9px;
fc1817aClaude1787 background: var(--bg-tertiary);
1788 border: 1px solid var(--border);
2ce1d0bClaude1789 border-radius: var(--r-sm);
debcf27Claude1790 color: var(--accent);
1791 font-weight: 600;
1792 letter-spacing: 0.02em;
2ce1d0bClaude1793 transition: all var(--t-fast) var(--ease);
fc1817aClaude1794 }
debcf27Claude1795 .commit-sha:hover {
1796 border-color: rgba(140,109,255,0.40);
1797 background: var(--accent-gradient-faint);
1798 color: var(--accent-hover);
1799 text-decoration: none;
fc1817aClaude1800 }
1801
1802 .diff-view { margin-top: 16px; }
1803 .diff-file {
1804 border: 1px solid var(--border);
2ce1d0bClaude1805 border-radius: var(--r-md);
fc1817aClaude1806 margin-bottom: 16px;
1807 overflow: hidden;
2ce1d0bClaude1808 background: var(--bg-elevated);
fc1817aClaude1809 }
1810 .diff-file-header {
1811 background: var(--bg-secondary);
2ce1d0bClaude1812 padding: 10px 16px;
fc1817aClaude1813 border-bottom: 1px solid var(--border);
1814 font-family: var(--font-mono);
2ce1d0bClaude1815 font-size: var(--t-sm);
1816 font-weight: 500;
fc1817aClaude1817 }
1818 .diff-content {
1819 overflow-x: auto;
1820 font-family: var(--font-mono);
2ce1d0bClaude1821 font-size: var(--t-sm);
1822 line-height: 1.65;
fc1817aClaude1823 }
958d26aClaude1824 .diff-content .line-add { background: rgba(52,211,153,0.10); color: var(--green); }
1825 .diff-content .line-del { background: rgba(248,113,113,0.08); color: var(--red); }
1826 .diff-content .line-hunk { background: rgba(140,109,255,0.06); color: var(--text-link); }
2ce1d0bClaude1827 .diff-content .line { padding: 0 16px; white-space: pre; display: block; }
fc1817aClaude1828
1829 .stat-add { color: var(--green); font-weight: 600; }
1830 .stat-del { color: var(--red); font-weight: 600; }
1831
2ce1d0bClaude1832 /* ============================================================ */
1833 /* Empty state */
1834 /* ============================================================ */
fc1817aClaude1835 .empty-state {
1836 text-align: center;
958d26aClaude1837 padding: 96px 24px;
fc1817aClaude1838 color: var(--text-muted);
2ce1d0bClaude1839 border: 1px dashed var(--border);
1840 border-radius: var(--r-lg);
958d26aClaude1841 background:
1842 radial-gradient(60% 60% at 50% 0%, rgba(140,109,255,0.05), transparent 70%),
1843 var(--bg-elevated);
1844 position: relative;
1845 overflow: hidden;
1846 }
1847 .empty-state::before {
1848 content: '';
1849 position: absolute;
1850 inset: 0;
1851 background-image: radial-gradient(rgba(255,255,255,0.025) 1px, transparent 1px);
1852 background-size: 24px 24px;
1853 opacity: 0.5;
1854 pointer-events: none;
1855 mask-image: radial-gradient(ellipse at center, #000 30%, transparent 70%);
1856 -webkit-mask-image: radial-gradient(ellipse at center, #000 30%, transparent 70%);
1857 }
1858 :root[data-theme='light'] .empty-state::before {
1859 background-image: radial-gradient(rgba(15,16,28,0.08) 1px, transparent 1px);
fc1817aClaude1860 }
958d26aClaude1861 .empty-state > * { position: relative; z-index: 1; }
2ce1d0bClaude1862 .empty-state h2 {
958d26aClaude1863 font-size: var(--t-xl);
2ce1d0bClaude1864 margin-bottom: 8px;
958d26aClaude1865 color: var(--text-strong);
1866 letter-spacing: -0.022em;
2ce1d0bClaude1867 }
958d26aClaude1868 .empty-state p { font-size: var(--t-md); max-width: 480px; margin: 0 auto; line-height: 1.55; }
fc1817aClaude1869 .empty-state pre {
1870 text-align: left;
1871 display: inline-block;
1872 background: var(--bg-secondary);
958d26aClaude1873 padding: 18px 24px;
1874 border-radius: var(--r-md);
fc1817aClaude1875 border: 1px solid var(--border);
1876 font-family: var(--font-mono);
958d26aClaude1877 font-feature-settings: var(--mono-feat);
2ce1d0bClaude1878 font-size: var(--t-sm);
958d26aClaude1879 margin-top: 24px;
fc1817aClaude1880 line-height: 1.8;
2ce1d0bClaude1881 color: var(--text);
958d26aClaude1882 box-shadow: var(--elev-1);
fc1817aClaude1883 }
1884
2ce1d0bClaude1885 /* ============================================================ */
1886 /* Badges */
1887 /* ============================================================ */
fc1817aClaude1888 .badge {
2ce1d0bClaude1889 display: inline-flex;
1890 align-items: center;
1891 gap: 4px;
1892 padding: 2px 9px;
1893 border-radius: var(--r-full);
1894 font-size: var(--t-xs);
fc1817aClaude1895 font-weight: 500;
1896 background: var(--bg-tertiary);
1897 border: 1px solid var(--border);
1898 color: var(--text-muted);
2ce1d0bClaude1899 line-height: 1.5;
fc1817aClaude1900 }
1901
2ce1d0bClaude1902 /* ============================================================ */
1903 /* Branch dropdown */
1904 /* ============================================================ */
fc1817aClaude1905 .branch-selector {
1906 display: inline-flex;
1907 align-items: center;
2ce1d0bClaude1908 gap: 6px;
1909 padding: 6px 12px;
1910 background: var(--bg-elevated);
fc1817aClaude1911 border: 1px solid var(--border);
2ce1d0bClaude1912 border-radius: var(--r-sm);
1913 font-size: var(--t-sm);
fc1817aClaude1914 color: var(--text);
1915 margin-bottom: 12px;
2ce1d0bClaude1916 transition: border-color var(--t-fast) var(--ease);
fc1817aClaude1917 }
2ce1d0bClaude1918 .branch-selector:hover { border-color: var(--border-strong); }
fc1817aClaude1919
06d5ffeClaude1920 .branch-dropdown {
1921 position: relative;
1922 display: inline-block;
1923 margin-bottom: 12px;
1924 }
1925 .branch-dropdown-content {
1926 display: none;
1927 position: absolute;
2ce1d0bClaude1928 top: calc(100% + 4px);
06d5ffeClaude1929 left: 0;
1930 z-index: 10;
2ce1d0bClaude1931 min-width: 220px;
1932 background: var(--bg-elevated);
1933 border: 1px solid var(--border-strong);
1934 border-radius: var(--r-md);
1935 overflow: hidden;
1936 box-shadow: var(--elev-3);
06d5ffeClaude1937 }
1938 .branch-dropdown:hover .branch-dropdown-content,
1939 .branch-dropdown:focus-within .branch-dropdown-content { display: block; }
1940 .branch-dropdown-content a {
1941 display: block;
2ce1d0bClaude1942 padding: 9px 14px;
1943 font-size: var(--t-sm);
06d5ffeClaude1944 color: var(--text);
1945 border-bottom: 1px solid var(--border);
2ce1d0bClaude1946 transition: background var(--t-fast) var(--ease);
06d5ffeClaude1947 }
1948 .branch-dropdown-content a:last-child { border-bottom: none; }
2ce1d0bClaude1949 .branch-dropdown-content a:hover { background: var(--bg-hover); text-decoration: none; }
1950 .branch-dropdown-content a.active-branch { color: var(--text-link); font-weight: 600; background: var(--accent-gradient-faint); }
06d5ffeClaude1951
2ce1d0bClaude1952 /* ============================================================ */
1953 /* Card grid */
1954 /* ============================================================ */
1955 .card-grid {
1956 display: grid;
1957 grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
1958 gap: 16px;
1959 }
fc1817aClaude1960 .card {
1961 border: 1px solid var(--border);
2ce1d0bClaude1962 border-radius: var(--r-md);
958d26aClaude1963 padding: 20px;
2ce1d0bClaude1964 background: var(--bg-elevated);
1965 transition:
958d26aClaude1966 border-color var(--t-base) var(--ease),
1967 transform var(--t-base) var(--ease-out-quart),
2ce1d0bClaude1968 box-shadow var(--t-base) var(--ease);
1969 position: relative;
1970 overflow: hidden;
958d26aClaude1971 isolation: isolate;
1972 }
1973 .card::before {
1974 content: '';
1975 position: absolute;
1976 inset: 0;
1977 background: linear-gradient(135deg, rgba(140,109,255,0.06), transparent 50%);
1978 opacity: 0;
1979 transition: opacity var(--t-base) var(--ease);
1980 pointer-events: none;
1981 z-index: -1;
2ce1d0bClaude1982 }
1983 .card:hover {
1984 border-color: var(--border-strong);
1985 box-shadow: var(--elev-2);
958d26aClaude1986 transform: translateY(-2px);
2ce1d0bClaude1987 }
958d26aClaude1988 .card:hover::before { opacity: 1; }
1989 .card h3 { font-size: var(--t-md); margin-bottom: 6px; letter-spacing: -0.012em; }
2ce1d0bClaude1990 .card h3 a { color: var(--text); font-weight: 600; }
1991 .card h3 a:hover { color: var(--text-link); }
1992 .card p { font-size: var(--t-sm); color: var(--text-muted); line-height: 1.5; }
1993 .card-meta {
1994 display: flex;
1995 gap: 14px;
1996 margin-top: 14px;
1997 padding-top: 14px;
1998 border-top: 1px solid var(--border);
1999 font-size: var(--t-xs);
2000 color: var(--text-muted);
2001 }
2002 .card-meta span { display: flex; align-items: center; gap: 5px; }
2003
2004 /* ============================================================ */
2005 /* Stat card / box */
2006 /* ============================================================ */
2007 .stat-card {
2008 background: var(--bg-elevated);
2009 border: 1px solid var(--border);
2010 border-radius: var(--r-md);
fc1817aClaude2011 padding: 16px;
2ce1d0bClaude2012 transition: border-color var(--t-fast) var(--ease);
2013 }
2014 .stat-card:hover { border-color: var(--border-strong); }
2015 .stat-label {
2016 font-size: var(--t-xs);
2017 color: var(--text-muted);
2018 text-transform: uppercase;
2019 letter-spacing: 0.06em;
2020 font-weight: 500;
2021 }
2022 .stat-value {
2023 font-size: var(--t-xl);
2024 font-weight: 700;
2025 margin-top: 4px;
2026 letter-spacing: -0.025em;
2027 color: var(--text);
2028 font-feature-settings: 'tnum';
fc1817aClaude2029 }
06d5ffeClaude2030
2ce1d0bClaude2031 /* ============================================================ */
2032 /* Star button */
2033 /* ============================================================ */
06d5ffeClaude2034 .star-btn {
2035 display: inline-flex;
2036 align-items: center;
2037 gap: 6px;
2ce1d0bClaude2038 padding: 5px 12px;
2039 background: var(--bg-elevated);
06d5ffeClaude2040 border: 1px solid var(--border);
2ce1d0bClaude2041 border-radius: var(--r-sm);
06d5ffeClaude2042 color: var(--text);
2ce1d0bClaude2043 font-size: var(--t-sm);
2044 font-weight: 500;
06d5ffeClaude2045 cursor: pointer;
2ce1d0bClaude2046 transition: all var(--t-fast) var(--ease);
2047 }
2048 .star-btn:hover { background: var(--bg-surface); border-color: var(--border-strong); text-decoration: none; }
2049 .star-btn.starred {
2050 color: var(--yellow);
958d26aClaude2051 border-color: rgba(251,191,36,0.4);
2052 background: rgba(251,191,36,0.08);
06d5ffeClaude2053 }
2054
2ce1d0bClaude2055 /* ============================================================ */
2056 /* User profile */
2057 /* ============================================================ */
06d5ffeClaude2058 .user-profile {
2059 display: flex;
2060 gap: 32px;
2061 margin-bottom: 32px;
2ce1d0bClaude2062 padding: 24px;
2063 background: var(--bg-elevated);
2064 border: 1px solid var(--border);
2065 border-radius: var(--r-lg);
06d5ffeClaude2066 }
2067 .user-avatar {
2068 width: 96px;
2069 height: 96px;
2ce1d0bClaude2070 border-radius: var(--r-full);
2071 background: var(--accent-gradient-soft);
2072 border: 1px solid var(--border-strong);
06d5ffeClaude2073 display: flex;
2074 align-items: center;
2075 justify-content: center;
2ce1d0bClaude2076 font-size: 36px;
2077 font-weight: 600;
2078 color: var(--text);
06d5ffeClaude2079 flex-shrink: 0;
2ce1d0bClaude2080 letter-spacing: -0.02em;
06d5ffeClaude2081 }
2ce1d0bClaude2082 .user-info h2 { font-size: var(--t-xl); margin-bottom: 2px; letter-spacing: -0.025em; }
2083 .user-info .username { font-size: var(--t-md); color: var(--text-muted); }
2084 .user-info .bio { font-size: var(--t-sm); color: var(--text-muted); margin-top: 10px; line-height: 1.55; }
06d5ffeClaude2085
2ce1d0bClaude2086 /* ============================================================ */
2087 /* New repo form */
2088 /* ============================================================ */
2089 .new-repo-form { max-width: 640px; }
2090 .new-repo-form h2 { margin-bottom: 24px; font-size: var(--t-xl); letter-spacing: -0.025em; }
2091 .visibility-options { display: flex; gap: 12px; margin-bottom: 20px; }
06d5ffeClaude2092 .visibility-option {
2093 flex: 1;
2ce1d0bClaude2094 padding: 16px;
06d5ffeClaude2095 border: 1px solid var(--border);
2ce1d0bClaude2096 border-radius: var(--r-md);
2097 background: var(--bg-elevated);
06d5ffeClaude2098 cursor: pointer;
2ce1d0bClaude2099 text-align: left;
2100 transition: all var(--t-fast) var(--ease);
2101 }
2102 .visibility-option:hover { border-color: var(--border-strong); background: var(--bg-surface); }
2103 .visibility-option:has(input:checked) {
2104 border-color: var(--accent);
2105 background: var(--accent-gradient-faint);
2106 box-shadow: 0 0 0 1px var(--accent);
06d5ffeClaude2107 }
2108 .visibility-option input { display: none; }
2ce1d0bClaude2109 .visibility-option .vis-label { font-size: var(--t-sm); font-weight: 600; margin-bottom: 4px; }
2110 .visibility-option .vis-desc { font-size: var(--t-xs); color: var(--text-muted); }
79136bbClaude2111
2ce1d0bClaude2112 /* ============================================================ */
2113 /* Issues */
2114 /* ============================================================ */
2115 .issue-tabs { display: flex; gap: 4px; }
2116 .issue-tabs a {
2117 color: var(--text-muted);
2118 font-size: var(--t-sm);
2119 font-weight: 500;
2120 padding: 6px 12px;
2121 border-radius: var(--r-sm);
2122 transition: all var(--t-fast) var(--ease);
2123 }
2124 .issue-tabs a:hover { color: var(--text); background: var(--bg-hover); text-decoration: none; }
2125 .issue-tabs a.active { color: var(--text); background: var(--bg-elevated); }
79136bbClaude2126
2ce1d0bClaude2127 .issue-list {
2128 border: 1px solid var(--border);
2129 border-radius: var(--r-md);
2130 overflow: hidden;
2131 background: var(--bg-elevated);
2132 }
79136bbClaude2133 .issue-item {
2134 display: flex;
2ce1d0bClaude2135 gap: 14px;
79136bbClaude2136 align-items: flex-start;
2ce1d0bClaude2137 padding: 14px 16px;
79136bbClaude2138 border-bottom: 1px solid var(--border);
2ce1d0bClaude2139 transition: background var(--t-fast) var(--ease);
79136bbClaude2140 }
2141 .issue-item:last-child { border-bottom: none; }
2ce1d0bClaude2142 .issue-item:hover { background: var(--bg-hover); }
2143 .issue-state-icon {
2144 font-size: 14px;
2145 padding-top: 2px;
2146 width: 18px;
2147 height: 18px;
2148 display: inline-flex;
2149 align-items: center;
2150 justify-content: center;
2151 border-radius: var(--r-full);
2152 flex-shrink: 0;
2153 }
79136bbClaude2154 .state-open { color: var(--green); }
958d26aClaude2155 .state-closed { color: #b69dff; }
2ce1d0bClaude2156 .issue-title {
debcf27Claude2157 font-family: var(--font-display);
2158 font-size: var(--t-md);
2ce1d0bClaude2159 font-weight: 600;
debcf27Claude2160 line-height: 1.35;
2161 letter-spacing: -0.012em;
2162 }
2163 .issue-title a { color: var(--text-strong); transition: color var(--t-fast) var(--ease); }
2164 .issue-title a:hover { color: var(--accent); text-decoration: none; }
2165 .issue-meta {
2166 font-family: var(--font-mono);
2167 font-size: 11px;
2168 color: var(--text-muted);
2169 margin-top: 5px;
2170 letter-spacing: 0.01em;
2ce1d0bClaude2171 }
79136bbClaude2172
2173 .issue-badge {
2174 display: inline-flex;
2175 align-items: center;
2ce1d0bClaude2176 gap: 6px;
79136bbClaude2177 padding: 4px 12px;
2ce1d0bClaude2178 border-radius: var(--r-full);
2179 font-size: var(--t-sm);
79136bbClaude2180 font-weight: 500;
2ce1d0bClaude2181 line-height: 1.4;
79136bbClaude2182 }
958d26aClaude2183 .badge-open { background: rgba(52,211,153,0.10); color: var(--green); border: 1px solid rgba(52,211,153,0.35); }
2184 .badge-closed { background: rgba(182,157,255,0.10); color: #b69dff; border: 1px solid rgba(182,157,255,0.35); }
2185 .badge-merged { background: rgba(140,109,255,0.10); color: var(--accent); border: 1px solid rgba(140,109,255,0.35); }
2ce1d0bClaude2186 .state-merged { color: var(--accent); }
958d26aClaude2187 .ai-review { border-color: var(--accent); box-shadow: 0 0 0 1px rgba(140,109,255,0.3); }
79136bbClaude2188
2ce1d0bClaude2189 .issue-detail { max-width: 920px; }
79136bbClaude2190 .issue-comment-box {
2191 border: 1px solid var(--border);
2ce1d0bClaude2192 border-radius: var(--r-md);
79136bbClaude2193 margin-bottom: 16px;
2194 overflow: hidden;
2ce1d0bClaude2195 background: var(--bg-elevated);
79136bbClaude2196 }
2197 .comment-header {
2198 background: var(--bg-secondary);
2ce1d0bClaude2199 padding: 10px 16px;
79136bbClaude2200 border-bottom: 1px solid var(--border);
2ce1d0bClaude2201 font-size: var(--t-sm);
79136bbClaude2202 color: var(--text-muted);
2203 }
2204
2ce1d0bClaude2205 /* ============================================================ */
2206 /* Panel — flexible container used across many pages */
2207 /* ============================================================ */
2208 .panel {
2209 background: var(--bg-elevated);
2210 border: 1px solid var(--border);
2211 border-radius: var(--r-md);
2212 overflow: hidden;
2213 }
2214 .panel-item {
2215 display: flex;
2216 align-items: center;
2217 gap: 12px;
2218 padding: 12px 16px;
79136bbClaude2219 border-bottom: 1px solid var(--border);
2ce1d0bClaude2220 transition: background var(--t-fast) var(--ease);
2221 }
2222 .panel-item:last-child { border-bottom: none; }
2223 .panel-item:hover { background: var(--bg-hover); }
2224 .panel-empty {
2225 padding: 24px;
2226 text-align: center;
79136bbClaude2227 color: var(--text-muted);
2ce1d0bClaude2228 font-size: var(--t-sm);
79136bbClaude2229 }
2230
2ce1d0bClaude2231 /* ============================================================ */
2232 /* Search */
2233 /* ============================================================ */
79136bbClaude2234 .search-results .diff-file { margin-bottom: 12px; }
16b325cClaude2235
2ce1d0bClaude2236 /* ============================================================ */
2237 /* Timeline */
2238 /* ============================================================ */
2239 .timeline { position: relative; padding-left: 28px; }
16b325cClaude2240 .timeline::before {
2241 content: '';
2242 position: absolute;
2243 left: 4px;
2244 top: 8px;
2245 bottom: 8px;
2246 width: 2px;
2ce1d0bClaude2247 background: linear-gradient(180deg, var(--border) 0%, transparent 100%);
16b325cClaude2248 }
2ce1d0bClaude2249 .timeline-item { position: relative; padding-bottom: 16px; }
16b325cClaude2250 .timeline-dot {
2251 position: absolute;
2ce1d0bClaude2252 left: -28px;
2253 top: 8px;
2254 width: 12px;
2255 height: 12px;
2256 border-radius: var(--r-full);
2257 background: var(--accent-gradient);
16b325cClaude2258 border: 2px solid var(--bg);
2ce1d0bClaude2259 box-shadow: 0 0 0 1px var(--border-strong);
16b325cClaude2260 }
2261 .timeline-content {
2ce1d0bClaude2262 background: var(--bg-elevated);
16b325cClaude2263 border: 1px solid var(--border);
2ce1d0bClaude2264 border-radius: var(--r-md);
2265 padding: 14px 16px;
16b325cClaude2266 }
f1ab587Claude2267
2ce1d0bClaude2268 /* ============================================================ */
2269 /* Toggle switch */
2270 /* ============================================================ */
f1ab587Claude2271 .toggle-switch {
2272 position: relative;
2273 display: inline-block;
2ce1d0bClaude2274 width: 40px;
2275 height: 22px;
f1ab587Claude2276 flex-shrink: 0;
2277 margin-left: 16px;
2278 }
2279 .toggle-switch input { opacity: 0; width: 0; height: 0; }
2280 .toggle-slider {
2281 position: absolute;
2282 cursor: pointer;
2283 top: 0; left: 0; right: 0; bottom: 0;
2284 background: var(--bg-tertiary);
2285 border: 1px solid var(--border);
2ce1d0bClaude2286 border-radius: var(--r-full);
2287 transition: all var(--t-base) var(--ease);
f1ab587Claude2288 }
2289 .toggle-slider::before {
2290 content: '';
2291 position: absolute;
2ce1d0bClaude2292 height: 16px;
2293 width: 16px;
f1ab587Claude2294 left: 2px;
2295 bottom: 2px;
2296 background: var(--text-muted);
2ce1d0bClaude2297 border-radius: var(--r-full);
2298 transition: all var(--t-base) var(--ease);
f1ab587Claude2299 }
2300 .toggle-switch input:checked + .toggle-slider {
2ce1d0bClaude2301 background: var(--accent-gradient);
2302 border-color: transparent;
958d26aClaude2303 box-shadow: 0 0 0 1px rgba(140,109,255,0.4);
f1ab587Claude2304 }
2305 .toggle-switch input:checked + .toggle-slider::before {
2ce1d0bClaude2306 transform: translateX(18px);
f1ab587Claude2307 background: #fff;
2308 }
ef8d378Claude2309
2310 /* ============================================================
2311 * 2026 polish layer (purely additive — no layout changes).
2312 * Improves typography rendering, focus states, hover affordances,
2313 * and adds the gradient brand cue to primary buttons. Anything
2314 * that could alter dimensions stays in the rules above.
2315 * ============================================================ */
2316 html { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
2317 body { letter-spacing: -0.005em; font-feature-settings: 'cv11', 'ss01', 'ss03'; }
2318 *::selection { background: rgba(168,85,247,0.35); color: var(--text); }
2319
2320 h1, h2, h3, h4 { letter-spacing: -0.018em; }
2321 h1 { letter-spacing: -0.025em; }
2322
2323 /* Smoother colour transitions everywhere links live */
2324 a { transition: color 120ms cubic-bezier(0.16,1,0.3,1); }
2325
2326 /* Buttons: focus rings + smoother transitions; primary gets the gradient */
2327 .btn {
2328 transition:
2329 background 120ms cubic-bezier(0.16,1,0.3,1),
2330 border-color 120ms cubic-bezier(0.16,1,0.3,1),
2331 transform 120ms cubic-bezier(0.16,1,0.3,1),
2332 box-shadow 120ms cubic-bezier(0.16,1,0.3,1);
2333 }
2334 .btn:active { transform: translateY(0.5px); }
2335 .btn:focus-visible {
2336 outline: none;
2337 box-shadow: 0 0 0 3px rgba(168,85,247,0.30);
2338 }
2339 .btn-primary {
2340 background: linear-gradient(135deg, #a855f7 0%, #06b6d4 100%);
2341 border-color: transparent;
2342 box-shadow:
2343 inset 0 1px 0 rgba(255,255,255,0.15),
2344 0 1px 2px rgba(168,85,247,0.25);
2345 }
2346 .btn-primary:hover {
2347 background: linear-gradient(135deg, #b766f8 0%, #22cce0 100%);
2348 filter: none;
2349 box-shadow:
2350 inset 0 1px 0 rgba(255,255,255,0.20),
2351 0 4px 12px rgba(168,85,247,0.30);
2352 }
2353
2354 /* Inputs: cleaner focus ring + hover */
2355 .form-group input:hover,
2356 .form-group textarea:hover,
2357 .form-group select:hover {
2358 border-color: rgba(255,255,255,0.14);
2359 }
2360 .form-group input:focus,
2361 .form-group textarea:focus,
2362 .form-group select:focus {
2363 border-color: rgba(168,85,247,0.55);
2364 box-shadow: 0 0 0 3px rgba(168,85,247,0.22);
2365 }
2366 :root[data-theme='light'] .form-group input:hover,
2367 :root[data-theme='light'] .form-group textarea:hover,
2368 :root[data-theme='light'] .form-group select:hover {
2369 border-color: rgba(0,0,0,0.18);
2370 }
2371
2372 /* Cards: subtle hover lift */
2373 .card {
2374 transition:
2375 border-color 160ms cubic-bezier(0.16,1,0.3,1),
2376 transform 160ms cubic-bezier(0.16,1,0.3,1),
2377 box-shadow 200ms cubic-bezier(0.16,1,0.3,1);
2378 }
2379 .card:hover {
2380 border-color: rgba(255,255,255,0.18);
2381 transform: translateY(-1px);
2382 box-shadow: 0 8px 24px rgba(0,0,0,0.30);
2383 }
2384 :root[data-theme='light'] .card:hover {
2385 border-color: rgba(0,0,0,0.18);
2386 box-shadow: 0 8px 24px rgba(0,0,0,0.08);
2387 }
2388
2389 /* Issue / commit / panel rows: smoother hover */
2390 .issue-item, .commit-item {
2391 transition: background 120ms cubic-bezier(0.16,1,0.3,1);
2392 }
2393 .repo-nav a {
2394 transition: color 120ms cubic-bezier(0.16,1,0.3,1),
2395 border-bottom-color 120ms cubic-bezier(0.16,1,0.3,1);
2396 }
2397 .nav-link {
2398 transition: color 120ms cubic-bezier(0.16,1,0.3,1);
2399 }
2400
2401 /* Auth card: subtle elevation so register/login feel premium */
2402 .auth-container {
2403 background: var(--bg-secondary);
2404 border: 1px solid var(--border);
2405 border-radius: var(--r-lg, 12px);
2406 padding: 32px;
2407 box-shadow: 0 4px 16px rgba(0,0,0,0.30), 0 0 0 1px var(--border);
2408 }
2409 :root[data-theme='light'] .auth-container {
2410 box-shadow: 0 4px 16px rgba(0,0,0,0.06), 0 0 0 1px var(--border);
2411 }
2412 .auth-container h2 { letter-spacing: -0.025em; }
2413
2414 /* Empty state: dashed border, generous padding */
2415 .empty-state {
2416 border: 1px dashed var(--border);
2417 border-radius: var(--r-lg, 12px);
2418 background: var(--bg);
2419 }
2420
2421 /* Badges + commit-sha: smoother transition */
2422 .commit-sha, .badge { transition: all 120ms cubic-bezier(0.16,1,0.3,1); }
2423
2424 /* Gradient text utility — matches landing's accent treatment */
2425 .gradient-text {
2426 background: linear-gradient(135deg, #a855f7 0%, #06b6d4 100%);
2427 -webkit-background-clip: text;
2428 background-clip: text;
2429 -webkit-text-fill-color: transparent;
2430 }
2431
2432 /* Custom scrollbars (subtle, themed) */
2433 ::-webkit-scrollbar { width: 10px; height: 10px; }
2434 ::-webkit-scrollbar-track { background: transparent; }
2435 ::-webkit-scrollbar-thumb {
2436 background: rgba(255,255,255,0.06);
2437 border: 2px solid var(--bg);
2438 border-radius: 9999px;
2439 }
2440 ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.14); }
2441 :root[data-theme='light'] ::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.10); }
2442 :root[data-theme='light'] ::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.18); }
2443
2444 /* Honour reduced-motion preference */
2445 @media (prefers-reduced-motion: reduce) {
2446 *, *::before, *::after {
2447 animation-duration: 0.01ms !important;
2448 animation-iteration-count: 1 !important;
2449 transition-duration: 0.01ms !important;
2450 }
2451 }
fc1817aClaude2452`;