/ src / globals.css
globals.css
  1  @import 'tailwindcss';
  2  @import 'tw-animate-css';
  3  
  4  /* Tell Tailwind to scan components and lib at the project root */
  5  @source "../components";
  6  @source "../lib";
  7  
  8  @custom-variant dark (&:is(.dark *));
  9  
 10  @theme inline {
 11    --color-background: var(--background);
 12    --color-foreground: var(--foreground);
 13    --font-sans: var(--font-sans);
 14    --font-mono: var(--font-mono);
 15    --font-display: var(--font-display);
 16    --color-sidebar-ring: var(--sidebar-ring);
 17    --color-sidebar-border: var(--sidebar-border);
 18    --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
 19    --color-sidebar-accent: var(--sidebar-accent);
 20    --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
 21    --color-sidebar-primary: var(--sidebar-primary);
 22    --color-sidebar-foreground: var(--sidebar-foreground);
 23    --color-sidebar: var(--sidebar);
 24    --color-chart-5: var(--chart-5);
 25    --color-chart-4: var(--chart-4);
 26    --color-chart-3: var(--chart-3);
 27    --color-chart-2: var(--chart-2);
 28    --color-chart-1: var(--chart-1);
 29    --color-ring: var(--ring);
 30    --color-input: var(--input);
 31    --color-border: var(--border);
 32    --color-destructive: var(--destructive);
 33    --color-accent-foreground: var(--accent-foreground);
 34    --color-accent: var(--accent);
 35    --color-muted-foreground: var(--muted-foreground);
 36    --color-muted: var(--muted);
 37    --color-secondary-foreground: var(--secondary-foreground);
 38    --color-secondary: var(--secondary);
 39    --color-primary-foreground: var(--primary-foreground);
 40    --color-primary: var(--primary);
 41    --color-popover-foreground: var(--popover-foreground);
 42    --color-popover: var(--popover);
 43    --color-card-foreground: var(--card-foreground);
 44    --color-card: var(--card);
 45    --radius-sm: calc(var(--radius) - 4px);
 46    --radius-md: calc(var(--radius) - 2px);
 47    --radius-lg: var(--radius);
 48    --radius-xl: calc(var(--radius) + 4px);
 49    --radius-2xl: calc(var(--radius) + 8px);
 50    --radius-3xl: calc(var(--radius) + 12px);
 51    --radius-4xl: calc(var(--radius) + 16px);
 52  }
 53  
 54  :root {
 55    --font-sans: 'Inter', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
 56    --font-display: 'Sora', ui-sans-serif, system-ui, sans-serif;
 57    --font-mono: 'JetBrains Mono', ui-monospace, 'Cascadia Code', 'Fira Code', monospace;
 58  
 59    --radius: 0.625rem;
 60    --background: oklch(1 0 0);
 61    --foreground: oklch(0.145 0 0);
 62    --card: oklch(1 0 0);
 63    --card-foreground: oklch(0.145 0 0);
 64    --popover: oklch(1 0 0);
 65    --popover-foreground: oklch(0.145 0 0);
 66    --primary: oklch(0.205 0 0);
 67    --primary-foreground: oklch(0.985 0 0);
 68    --secondary: oklch(0.97 0 0);
 69    --secondary-foreground: oklch(0.205 0 0);
 70    --muted: oklch(0.97 0 0);
 71    --muted-foreground: oklch(0.556 0 0);
 72    --accent: oklch(0.97 0 0);
 73    --accent-foreground: oklch(0.205 0 0);
 74    --destructive: oklch(0.577 0.245 27.325);
 75    --border: oklch(0.922 0 0);
 76    --input: oklch(0.922 0 0);
 77    --ring: oklch(0.708 0 0);
 78    --chart-1: oklch(0.646 0.222 41.116);
 79    --chart-2: oklch(0.6 0.118 184.704);
 80    --chart-3: oklch(0.398 0.07 227.392);
 81    --chart-4: oklch(0.828 0.189 84.429);
 82    --chart-5: oklch(0.769 0.188 70.08);
 83    --sidebar: oklch(0.985 0 0);
 84    --sidebar-foreground: oklch(0.145 0 0);
 85    --sidebar-primary: oklch(0.205 0 0);
 86    --sidebar-primary-foreground: oklch(0.985 0 0);
 87    --sidebar-accent: oklch(0.97 0 0);
 88    --sidebar-accent-foreground: oklch(0.205 0 0);
 89    --sidebar-border: oklch(0.922 0 0);
 90    --sidebar-ring: oklch(0.708 0 0);
 91  }
 92  
 93  .dark {
 94    --background: oklch(0.145 0 0);
 95    --foreground: oklch(0.985 0 0);
 96    --card: oklch(0.205 0 0);
 97    --card-foreground: oklch(0.985 0 0);
 98    --popover: oklch(0.205 0 0);
 99    --popover-foreground: oklch(0.985 0 0);
100    --primary: oklch(0.65 0.15 195);
101    --primary-foreground: oklch(0.13 0.02 195);
102    --secondary: oklch(0.269 0 0);
103    --secondary-foreground: oklch(0.985 0 0);
104    --muted: oklch(0.269 0 0);
105    --muted-foreground: oklch(0.708 0 0);
106    --accent: oklch(0.269 0 0);
107    --accent-foreground: oklch(0.985 0 0);
108    --destructive: oklch(0.704 0.191 22.216);
109    --border: oklch(1 0 0 / 10%);
110    --input: oklch(1 0 0 / 15%);
111    --ring: oklch(0.55 0.12 195);
112    --chart-1: oklch(0.488 0.243 264.376);
113    --chart-2: oklch(0.696 0.17 162.48);
114    --chart-3: oklch(0.769 0.188 70.08);
115    --chart-4: oklch(0.627 0.265 303.9);
116    --chart-5: oklch(0.645 0.246 16.439);
117    --sidebar: oklch(0.205 0 0);
118    --sidebar-foreground: oklch(0.985 0 0);
119    --sidebar-primary: oklch(0.65 0.15 195);
120    --sidebar-primary-foreground: oklch(0.985 0 0);
121    --sidebar-accent: oklch(0.269 0 0);
122    --sidebar-accent-foreground: oklch(0.985 0 0);
123    --sidebar-border: oklch(1 0 0 / 10%);
124    --sidebar-ring: oklch(0.556 0 0);
125    --accent-glow: oklch(0.65 0.15 195 / 30%);
126  }
127  
128  @layer base {
129    * {
130      @apply border-border outline-ring/50;
131      scrollbar-color: oklch(0.35 0 0) transparent;
132      -webkit-tap-highlight-color: transparent;
133      -webkit-touch-callout: none;
134    }
135  
136    html,
137    body {
138      overscroll-behavior: none;
139      overflow: hidden;
140    }
141  
142    body {
143      @apply bg-background text-foreground;
144      font-family: var(--font-sans);
145      -webkit-user-select: none;
146      user-select: none;
147      -webkit-font-smoothing: antialiased;
148      -moz-osx-font-smoothing: grayscale;
149      text-rendering: optimizeLegibility;
150      -webkit-text-size-adjust: 100%;
151    }
152  
153    input,
154    textarea,
155    [contenteditable],
156    .selectable {
157      -webkit-user-select: text;
158      user-select: text;
159    }
160  
161    label,
162    button,
163    [role='button'],
164    span,
165    p,
166    h1,
167    h2,
168    h3,
169    h4,
170    h5,
171    h6,
172    .ui-label {
173      cursor: default;
174    }
175  
176    input,
177    textarea,
178    [contenteditable] {
179      cursor: text;
180    }
181  
182    a,
183    button,
184    summary,
185    [role='button'],
186    .clickable {
187      cursor: pointer;
188    }
189  
190    img,
191    a,
192    svg {
193      -webkit-user-drag: none;
194    }
195  
196    button:focus:not(:focus-visible),
197    [role='button']:focus:not(:focus-visible) {
198      outline: none;
199    }
200  
201    button:focus-visible,
202    [role='button']:focus-visible {
203      outline: 2px solid var(--ring);
204      outline-offset: 2px;
205    }
206  }
207  
208  /* ─── Dark scrollbars (WebKit) ───────────────────────────────── */
209  ::-webkit-scrollbar {
210    width: 8px;
211    height: 8px;
212  }
213  
214  ::-webkit-scrollbar-track {
215    background: transparent;
216  }
217  
218  ::-webkit-scrollbar-thumb {
219    background: oklch(0.35 0 0);
220    border-radius: 4px;
221  }
222  
223  ::-webkit-scrollbar-thumb:hover {
224    background: oklch(0.45 0 0);
225  }
226  
227  ::-webkit-scrollbar-corner {
228    background: transparent;
229  }
230  
231  /* ─── Shiki base ─────────────────────────────────────────────── */
232  .shiki {
233    font-family: var(--font-mono) !important;
234    overflow-x: auto;
235    padding: 0.5rem 0 !important;
236  }
237  
238  /* Shiki renders \n text nodes between <span class="line"> elements.
239     Setting font-size:0 here collapses those text nodes to zero height
240     so they don't create extra blank lines between code lines.
241     font-size is restored on .line. */
242  .shiki code {
243    display: block;
244    font-size: 0;
245    /* wide enough that backgrounds fill the full scrollable area */
246    min-width: 100%;
247    width: max-content;
248  }
249  
250  .shiki .line {
251    display: block;
252    font-size: 0.8125rem;
253    line-height: 1.5;
254    padding: 0 1.25rem;
255  }
256  
257  /* ─── Diff gutter (only inside has-diff blocks) ──────────────── */
258  
259  /* All lines in a diff block get a fixed-width gutter column so
260     indentation stays perfectly aligned between +, - and context. */
261  .has-diff .line {
262    padding-left: 0;
263  }
264  
265  .has-diff .line::before {
266    /* 1ch = one character width in the current monospace font */
267    display: inline-block;
268    width: 1ch;
269    margin-right: 1ch; /* one char gap between gutter and code */
270    padding-left: 0.75rem;
271    content: ' ';
272    user-select: none;
273    pointer-events: none;
274  }
275  
276  /* Added lines */
277  .has-diff .line.diff.add {
278    background-color: rgba(46, 160, 67, 0.15);
279  }
280  
281  .has-diff .line.diff.add::before {
282    content: '+';
283    color: #3fb950;
284  }
285  
286  /* Removed lines */
287  .has-diff .line.diff.remove {
288    background-color: rgba(248, 81, 73, 0.15);
289  }
290  
291  .has-diff .line.diff.remove::before {
292    content: '-';
293    color: #f85149;
294  }
295  
296  /* ─── Interactive diff lines ─────────────────────────────────── */
297  
298  /* Override the default gutter for interactive lines since we render our own */
299  .has-diff .line.interactive-line::before {
300    display: none;
301  }
302  
303  .line.interactive-line:hover .add-comment-icon {
304    opacity: 1 !important;
305  }
306  
307  .line.interactive-line:hover .line-number-gutter {
308    color: rgba(255, 255, 255, 0.7) !important;
309  }
310  
311  .line.interactive-line {
312    padding-left: 0 !important;
313  }
314  
315  /* ─── Split diff view ─────────────────────────────────────────── */
316  
317  .split-diff-cell-add {
318    background-color: rgba(46, 160, 67, 0.15);
319  }
320  
321  .split-diff-cell-remove {
322    background-color: rgba(248, 81, 73, 0.15);
323  }
324  
325  .split-diff-cell-empty {
326    background: oklch(0.2 0 0 / 30%);
327  }
328  
329  .split-diff-separator {
330    background-color: rgba(255, 255, 255, 0.06);
331  }
332  
333  .split-diff-grid > span.split-diff-line-num:hover {
334    color: rgba(255, 255, 255, 0.7) !important;
335  }
336  
337  .split-diff-grid > span.split-diff-comment-icon:hover .split-icon-hover {
338    opacity: 1 !important;
339  }
340  
341  /* ─── Check suggestion jump highlight ────────────────────────── */
342  
343  .check-highlight {
344    position: relative;
345  }
346  .check-highlight::after {
347    content: '';
348    position: absolute;
349    inset: 0;
350    pointer-events: none;
351    background-color: oklch(0.65 0.15 195 / 30%);
352    animation: check-highlight-fade 1.5s ease-out forwards;
353  }
354  @keyframes check-highlight-fade {
355    from {
356      opacity: 1;
357    }
358    to {
359      opacity: 0;
360    }
361  }
362  
363  /* ─── Freshness banner ────────────────────────────────────────── */
364  
365  .staleBanner-current {
366    border: 1px solid oklch(0.55 0.12 155 / 30%);
367    background: oklch(0.25 0.04 155 / 40%);
368    color: oklch(0.78 0.1 155);
369  }
370  
371  .staleBanner-unknown {
372    border: 1px solid oklch(0.45 0 0 / 40%);
373    background: oklch(0.22 0 0 / 50%);
374    color: oklch(0.65 0 0);
375  }
376  
377  .staleBanner-warn {
378    border: 1px solid oklch(0.55 0.12 75 / 40%);
379    background: oklch(0.25 0.05 75 / 40%);
380    color: oklch(0.82 0.1 80);
381  }
382  
383  .staleBanner-warn-toggle:hover {
384    color: oklch(0.9 0.1 80);
385  }
386  
387  .staleBanner-warn-btn {
388    border: 1px solid oklch(0.5 0.1 75 / 40%);
389    background: oklch(0.3 0.06 75 / 50%);
390    color: oklch(0.82 0.1 80);
391  }
392  
393  .staleBanner-warn-btn:hover {
394    background: oklch(0.35 0.08 75 / 55%);
395  }
396  
397  .staleBanner-warn-list {
398    border-top: 1px solid oklch(0.5 0.1 75 / 20%);
399  }
400  
401  .staleBanner-warn-sha {
402    color: oklch(0.72 0.08 80 / 70%);
403  }
404  
405  .staleBanner-warn-meta {
406    color: oklch(0.72 0.08 80 / 50%);
407  }
408  
409  /* ─── Update banner ──────────────────────────────────────────── */
410  
411  .updateBanner {
412    border-bottom: 1px solid oklch(0.55 0.12 230 / 30%);
413    background: oklch(0.25 0.04 230 / 40%);
414    color: oklch(0.78 0.1 230);
415  }
416  
417  .updateBanner-btn {
418    border: 1px solid oklch(0.5 0.1 230 / 40%);
419    background: oklch(0.3 0.06 230 / 50%);
420    color: oklch(0.85 0.08 230);
421  }
422  
423  .updateBanner-btn:hover {
424    background: oklch(0.35 0.08 230 / 55%);
425  }
426  
427  /* ─── PR status pills ─────────────────────────────────────────── */
428  
429  .statusPill-green,
430  .statusPill-red,
431  .statusPill-amber,
432  .statusPill-neutral,
433  .statusPill-label {
434    display: inline-flex;
435    align-items: center;
436    gap: 0.375rem;
437    padding: 0.25rem 0.625rem;
438    border-radius: 9999px;
439    font-size: 0.75rem;
440    font-weight: 500;
441    line-height: 1;
442    white-space: nowrap;
443  }
444  
445  .statusPill-green {
446    border: 1px solid oklch(0.55 0.12 155 / 40%);
447    background: oklch(0.25 0.04 155 / 40%);
448    color: oklch(0.78 0.1 155);
449  }
450  
451  .statusPill-red {
452    border: 1px solid oklch(0.55 0.15 25 / 40%);
453    background: oklch(0.25 0.05 25 / 40%);
454    color: oklch(0.78 0.12 25);
455  }
456  
457  .statusPill-amber {
458    border: 1px solid oklch(0.55 0.12 75 / 40%);
459    background: oklch(0.25 0.05 75 / 40%);
460    color: oklch(0.82 0.1 80);
461  }
462  
463  .statusPill-neutral {
464    border: 1px solid oklch(0.45 0 0 / 30%);
465    background: oklch(0.22 0 0 / 40%);
466    color: oklch(0.65 0 0);
467  }
468  
469  .statusPill-label {
470    border: 1px solid oklch(0.5 0.08 260 / 35%);
471    background: oklch(0.25 0.03 260 / 40%);
472    color: oklch(0.75 0.06 260);
473  }
474  
475  .statusPill-skeleton {
476    border: 1px solid oklch(0.35 0 0 / 20%);
477    background: oklch(0.22 0 0 / 50%);
478  }
479  
480  /* ─── Review focus callout ────────────────────────────────────── */
481  
482  .review-focus-content ul,
483  ul.review-focus-content {
484    list-style: none;
485    padding-left: 0;
486  }
487  
488  .review-focus-content ul > li,
489  ul.review-focus-content > li {
490    position: relative;
491    padding-left: 1.5rem;
492  }
493  
494  .review-focus-content ul > li::before,
495  ul.review-focus-content > li::before {
496    content: '▸';
497    position: absolute;
498    left: 0;
499    color: var(--primary);
500    opacity: 0.7;
501    font-size: 0.875rem;
502    line-height: inherit;
503  }
504  
505  /* ─── Mermaid fullscreen dialog ───────────────────────────────── */
506  
507  .mermaid-fullscreen svg {
508    width: auto !important;
509    max-width: 100% !important;
510    height: auto !important;
511    max-height: calc(90vh - 6rem);
512  }
513  
514  /* ─── Slide transitions (Priority 3) ─────────────────────────── */
515  
516  @keyframes slide-enter {
517    from {
518      opacity: 0;
519      transform: translateY(6px);
520    }
521    to {
522      opacity: 1;
523      transform: translateY(0);
524    }
525  }
526  
527  .slide-enter {
528    animation: slide-enter 0.25s ease-out;
529  }
530  
531  /* ─── Entrance animations (Priority 5) ────────────────────────── */
532  
533  @keyframes fade-in-up {
534    from {
535      opacity: 0;
536      transform: translateY(8px);
537    }
538    to {
539      opacity: 1;
540      transform: translateY(0);
541    }
542  }
543  
544  .animate-fade-in-up {
545    animation: fade-in-up 0.35s ease-out both;
546  }
547  
548  /* ─── Hero glow (Priority 7) ──────────────────────────────────── */
549  
550  .hero-glow {
551    position: relative;
552  }
553  
554  .hero-glow::before {
555    content: '';
556    position: absolute;
557    top: -200px;
558    left: 50%;
559    transform: translateX(-50%);
560    width: 800px;
561    height: 600px;
562    background: radial-gradient(ellipse, oklch(0.65 0.15 195 / 15%) 0%, transparent 70%);
563    pointer-events: none;
564    z-index: 0;
565  }
566  
567  /* ─── Loading screen rings (Priority 4) ──────────────────────── */
568  
569  @keyframes pulse-ring {
570    0% {
571      opacity: 0.4;
572      transform: scale(1);
573    }
574    100% {
575      opacity: 0;
576      transform: scale(1.6);
577    }
578  }
579  
580  .loading-ring {
581    animation: pulse-ring 2s ease-out infinite;
582  }
583  
584  .loading-ring-delayed {
585    animation: pulse-ring 2s ease-out 0.5s infinite;
586  }
587  
588  .loading-ring-delayed-2 {
589    animation: pulse-ring 2s ease-out 1s infinite;
590  }