prreviewbuddy 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4802 @@
1
+ /* The review's own styles, dressing the markup in domain/ui/components/review_tabs.ts. It lives
2
+ here rather than in the extension because the markup does: both surfaces render the same review,
3
+ and a sheet that only one of them owned would drift the moment the other changed a class name.
4
+ Both ship a copy, and the two builds copy it out of this directory.
5
+
6
+ Everything below is written for the harder of the two surfaces. This sheet is injected into
7
+ github.com through the extension's manifest.json content_scripts.css, so it is
8
+ extension-origin CSS and url() below resolves against the extension root, not GitHub's.
9
+ Remote @import is not an option here: GitHub sends `style-src 'unsafe-inline'
10
+ github.githubassets.com` and `font-src github.githubassets.com`, so a Google Fonts @import is
11
+ dropped and even a data: URI font would be refused. The font has to ship with us.
12
+
13
+ (A Space Mono @import used to sit here and was blocked for exactly that reason. Nothing was
14
+ depending on it: the only rule that named Space Mono was the wordmark, now Comfortaa, and every
15
+ mono rule below asks for the ui-monospace stack outright. There is no font left to bundle.)
16
+
17
+ There is deliberately no @font-face here. A relative url() would resolve against github.com and
18
+ be refused by `font-src`, so content/brand_font.ts registers the family from the isolated world
19
+ instead, where the fetch is ours rather than the page's. */
20
+
21
+ /* ── Severity pill palette ──
22
+ The single source for every graded pill in the sidebar: Issues' severity badges and the
23
+ Questions tab's importance badges. Both scales express "how much should this worry me", so
24
+ a MEDIUM question and a MAJOR issue must be the same amber — when these were two hand-copied
25
+ sets of hex values they had already drifted apart by an alpha step or two, which is exactly
26
+ the kind of difference that reads as "these tabs are from different products". Questions map
27
+ onto three of the four: high→danger, medium→warning, low→neutral (info is Issues-only, for
28
+ the extra "minor" rung a three-level scale doesn't have). */
29
+ .pr-helper-sidebar {
30
+ --pr-helper-pill-danger-bg: rgba(255, 80, 80, 0.15);
31
+ --pr-helper-pill-danger-fg: #cf222e;
32
+ --pr-helper-pill-warning-bg: rgba(240, 178, 50, 0.18);
33
+ --pr-helper-pill-warning-fg: #9a6700;
34
+ --pr-helper-pill-info-bg: rgba(84, 174, 255, 0.15);
35
+ --pr-helper-pill-info-fg: #0969da;
36
+ --pr-helper-pill-neutral-bg: rgba(175, 184, 193, 0.2);
37
+ --pr-helper-pill-neutral-fg: #57606a;
38
+ }
39
+
40
+ /* ── Sidebar shell ── */
41
+ .pr-helper-sidebar {
42
+ position: fixed;
43
+ top: 0;
44
+ right: 0;
45
+ width: 460px;
46
+ min-width: 380px;
47
+ max-width: 650px;
48
+ height: 100vh;
49
+ background: #f8fafc;
50
+ /* The inset purple edge is the rail, carried into the panel: it is the LEADING edge as the
51
+ sidebar slides out, so the panel reads as being drawn from the button rather than arriving
52
+ over the top of it. An inset shadow rather than a wider border-left on purpose — this box is
53
+ content-box with an explicit pixel width, so thickening the border would shift every column
54
+ inside it by 3px. */
55
+ box-shadow: inset 4px 0 0 #9333ea, -3px 0 16px rgba(0, 0, 0, 0.18);
56
+ border-left: 1px solid rgba(0, 0, 0, 0.08);
57
+ z-index: 999999;
58
+ transform: translateX(100%);
59
+ transition: transform 0.3s ease;
60
+ display: flex;
61
+ flex-direction: column;
62
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
63
+ }
64
+
65
+ .pr-helper-sidebar.open { transform: translateX(0); }
66
+
67
+ /* ── Resize handle ── */
68
+ .pr-helper-resize-handle {
69
+ position: absolute;
70
+ left: -3px;
71
+ top: 0;
72
+ bottom: 0;
73
+ width: 6px;
74
+ cursor: ew-resize;
75
+ z-index: 10;
76
+ background: transparent;
77
+ }
78
+
79
+ .pr-helper-resize-handle:hover { background: rgba(147, 51, 234, 0.25); }
80
+
81
+ /* ── Generating checklist (rendered inside the Overview tab) ── */
82
+ /* Deliberately not a card: a bordered panel floating in an otherwise empty tab makes the
83
+ whitespace around it the loudest thing on screen. Instead this claims the whole content
84
+ area — same full-bleed treatment as the "No review yet" intro it replaces — so the tab
85
+ itself reads as working. The body cluster centres in the free space via `margin: auto 0`
86
+ (not justify-content, which would fight the footer) and the footer settles at the
87
+ bottom, keeping Cancel in one predictable place through every generating phase. */
88
+ .pr-helper-generating {
89
+ min-height: 100%;
90
+ box-sizing: border-box;
91
+ display: flex;
92
+ flex-direction: column;
93
+ padding: 24px 8px 8px;
94
+ }
95
+
96
+ /* Block flow inside, not a nested flex column — the children (checklist, phrase stack,
97
+ progress bar) already rely on normal block sizing and auto margins to centre. */
98
+ .pr-helper-generating-body {
99
+ margin: auto 0;
100
+ }
101
+
102
+ .pr-helper-generating-footer {
103
+ flex-shrink: 0;
104
+ padding-top: 16px;
105
+ }
106
+
107
+ .pr-helper-generating-title {
108
+ font-size: 12px;
109
+ font-weight: 700;
110
+ color: #9333ea;
111
+ margin: 0 0 16px;
112
+ text-transform: uppercase;
113
+ letter-spacing: 0.06em;
114
+ }
115
+
116
+ @keyframes pr-helper-pulse {
117
+ 0%, 100% { opacity: 0.35; transform: scale(0.8); }
118
+ 50% { opacity: 1; transform: scale(1.15); }
119
+ }
120
+
121
+ /* Thin and treated as a decorative divider, not a primary UI element — in the waiting
122
+ phase it has no real percentage to show (see the shimmer variant below), so giving it
123
+ heavy visual weight would overstate how much it's actually communicating. */
124
+ .pr-helper-progress-bar {
125
+ position: relative;
126
+ height: 3px;
127
+ margin-bottom: 20px;
128
+ background: rgba(147, 51, 234, 0.12);
129
+ border-radius: 3px;
130
+ overflow: hidden;
131
+ }
132
+
133
+ .pr-helper-progress-bar-fill {
134
+ height: 100%;
135
+ background: linear-gradient(90deg, #9333ea, #c084fc);
136
+ width: 0%;
137
+ transition: width 0.4s ease;
138
+ animation: pr-helper-bar-pulse 1.8s ease-in-out infinite;
139
+ }
140
+
141
+ @keyframes pr-helper-bar-pulse {
142
+ 0%, 100% { opacity: 1; }
143
+ 50% { opacity: 0.55; }
144
+ }
145
+
146
+ /* Waiting-phase progress bar: no meaningful percentage to show once the LLM call is
147
+ in flight, so a highlight sweeps across the full track on a loop instead of implying
148
+ real completion — reads as "data is moving" without a fabricated number behind it. */
149
+ .pr-helper-progress-bar-shimmer::after {
150
+ content: '';
151
+ position: absolute;
152
+ top: 0;
153
+ left: 0;
154
+ height: 100%;
155
+ width: 40%;
156
+ background: linear-gradient(90deg, transparent, #c084fc, transparent);
157
+ animation: pr-helper-shimmer-sweep 1.6s ease-in-out infinite;
158
+ }
159
+
160
+ @keyframes pr-helper-shimmer-sweep {
161
+ 0% { transform: translateX(-100%); }
162
+ 100% { transform: translateX(250%); }
163
+ }
164
+
165
+ /* Spacing between this footer cluster (elapsed → hint → powered-by) is carried entirely
166
+ by each element's own margin-bottom, deliberately never margin-top on the element
167
+ after — adjacent margins collapse to their max rather than summing, so relying on the
168
+ "next" element's top margin instead of the "previous" element's bottom margin was
169
+ quietly shrinking these gaps down to whichever was smaller (that's what made "Powered
170
+ by" read as glued to the bar above it). */
171
+ .pr-helper-generating-elapsed {
172
+ margin: 0 0 4px;
173
+ font-size: 11px;
174
+ color: #9333ea;
175
+ font-weight: 600;
176
+ font-variant-numeric: tabular-nums;
177
+ text-align: center;
178
+ opacity: 0;
179
+ transition: opacity 0.6s ease;
180
+ }
181
+
182
+ .pr-helper-generating-elapsed.visible {
183
+ opacity: 1;
184
+ }
185
+
186
+ .pr-helper-generating-powered-by {
187
+ margin: 0;
188
+ font-size: 10px;
189
+ color: var(--color-fg-muted, #57606a);
190
+ opacity: 0.7;
191
+ text-align: center;
192
+ }
193
+
194
+ /* A hint line reserved for the same "past 2 minutes" moment as the direct headline —
195
+ hidden until content.ts's timer reveals it, never a separate banner of its own. */
196
+ .pr-helper-escalation-hint {
197
+ font-size: 11px;
198
+ color: var(--color-fg-muted, #57606a);
199
+ margin: 0 0 4px;
200
+ text-align: center;
201
+ }
202
+
203
+ /* Cancel is deliberately understated — a plain text button, not a bordered/filled
204
+ action — and identically placed across every generating state (see CANCEL_BUTTON in
205
+ review_tabs.ts) so it never reads as a new control appearing mid-wait. Centered via
206
+ auto margins on a block button so it's consistent whether the parent card is
207
+ left-aligned (the connecting checklist) or centered (the focused waiting view). */
208
+ .pr-helper-cancel-button {
209
+ display: block;
210
+ margin: 10px auto 0;
211
+ font-size: 11px;
212
+ font-weight: 600;
213
+ font-family: inherit;
214
+ padding: 2px 4px;
215
+ border: none;
216
+ background: transparent;
217
+ color: var(--color-fg-muted, #57606a);
218
+ cursor: pointer;
219
+ }
220
+
221
+ .pr-helper-cancel-button:hover { color: #cf222e; text-decoration: underline; }
222
+
223
+ /* ── Brand mark + thinking dots (waiting/finalizing phase) ── */
224
+ .pr-helper-brand-mark {
225
+ display: block;
226
+ width: 24px;
227
+ height: 24px;
228
+ margin: 0 auto 20px;
229
+ border-radius: 7px;
230
+ animation: pr-helper-pulse 2s ease-in-out infinite;
231
+ }
232
+
233
+ .pr-helper-generating-focused {
234
+ text-align: center;
235
+ }
236
+
237
+ /* Scaled up from the old card-sized type: at full-bleed the mark and headline are the
238
+ only things in the viewport, so 12px uppercase micro-copy read as a widget label
239
+ stranded in whitespace. Sentence case at 15px reads as the page speaking. */
240
+ .pr-helper-generating-focused .pr-helper-brand-mark {
241
+ width: 40px;
242
+ height: 40px;
243
+ border-radius: 11px;
244
+ margin-bottom: 22px;
245
+ }
246
+
247
+ .pr-helper-generating-focused .pr-helper-generating-title {
248
+ font-size: 15px;
249
+ font-weight: 700;
250
+ text-transform: none;
251
+ letter-spacing: -0.01em;
252
+ margin-bottom: 14px;
253
+ }
254
+
255
+ /* Held well short of the panel edge so the eye stays on the centred cluster rather than
256
+ tracking a full-width rule across the sidebar. */
257
+ .pr-helper-generating-focused .pr-helper-progress-bar {
258
+ max-width: 220px;
259
+ margin: 0 auto 20px;
260
+ }
261
+
262
+ /* Ambient rotating phrases: all six are stacked in the same box and cross-fade on a
263
+ loop via animation-delay offsets, rather than any JS timer swapping text — keeps the
264
+ whole thing driven by the same one-off render as everything else in this card. */
265
+ .pr-helper-thinking-phrases {
266
+ position: relative;
267
+ height: 22px;
268
+ margin: 0 0 14px;
269
+ }
270
+
271
+ .pr-helper-thinking-phrase {
272
+ position: absolute;
273
+ inset: 0;
274
+ margin: 0;
275
+ font-size: 15px;
276
+ letter-spacing: -0.01em;
277
+ font-weight: 700;
278
+ color: #9333ea;
279
+ opacity: 0;
280
+ animation: pr-helper-thinking-phrase-cycle 15s ease-in-out infinite;
281
+ }
282
+
283
+ /* Windows are cut to the phrase count: 4 phrases share a 15s cycle, so each owns 25% of
284
+ the timeline and stays visible for ~its whole slot (3%–23%) with a short cross-fade at
285
+ each edge. Adding or removing a phrase in THINKING_PHRASES means re-cutting these
286
+ percentages, or the stack develops dead gaps where nothing is on screen. */
287
+ @keyframes pr-helper-thinking-phrase-cycle {
288
+ 0% { opacity: 0; }
289
+ 3% { opacity: 1; }
290
+ 23% { opacity: 1; }
291
+ 27% { opacity: 0; }
292
+ 100% { opacity: 0; }
293
+ }
294
+
295
+ .pr-helper-thinking-dots {
296
+ display: flex;
297
+ justify-content: center;
298
+ gap: 6px;
299
+ margin: 0 0 22px;
300
+ }
301
+
302
+ .pr-helper-thinking-dots span {
303
+ width: 7px;
304
+ height: 7px;
305
+ border-radius: 50%;
306
+ background: #9333ea;
307
+ animation: pr-helper-thinking-bounce 1.1s ease-in-out infinite;
308
+ }
309
+
310
+ .pr-helper-thinking-dots span:nth-child(2) { animation-delay: 0.15s; }
311
+ .pr-helper-thinking-dots span:nth-child(3) { animation-delay: 0.3s; }
312
+
313
+ @keyframes pr-helper-thinking-bounce {
314
+ 0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }
315
+ 30% { opacity: 1; transform: translateY(-4px); }
316
+ }
317
+
318
+ /* ── Generation error (rendered inside the Overview tab) ── */
319
+ .pr-helper-generating-error {
320
+ flex-shrink: 0;
321
+ padding: 18px 20px;
322
+ background: rgba(255, 80, 80, 0.05);
323
+ border: 1px solid rgba(255, 80, 80, 0.2);
324
+ border-radius: 10px;
325
+ }
326
+
327
+ .pr-helper-generating-error-text {
328
+ font-size: 13px;
329
+ color: #cf222e;
330
+ margin: 0 0 14px;
331
+ line-height: 1.5;
332
+ }
333
+
334
+ .pr-helper-retry-button {
335
+ font-size: 11px;
336
+ font-weight: 600;
337
+ font-family: inherit;
338
+ padding: 6px 14px;
339
+ border-radius: 6px;
340
+ border: 1px solid #cf222e;
341
+ background: transparent;
342
+ color: #cf222e;
343
+ cursor: pointer;
344
+ }
345
+
346
+ .pr-helper-retry-button:hover { background: rgba(255, 80, 80, 0.08); }
347
+
348
+ /* ── Review tabs ── */
349
+ .pr-helper-review-tabs {
350
+ display: flex;
351
+ flex-wrap: wrap;
352
+ gap: 2px;
353
+ padding: 10px 10px 0;
354
+ border-bottom: 1px solid var(--color-border-default, #d0d7de);
355
+ background: #f8fafc;
356
+ flex-shrink: 0;
357
+ }
358
+
359
+ /* Sized and weighted to read as navigation, not content — one step down from the section
360
+ titles/body text below it so the tab strip doesn't out-compete the thing it's routing to. */
361
+ .pr-helper-review-tab {
362
+ font-size: 13px;
363
+ font-weight: 500;
364
+ font-family: inherit;
365
+ padding: 9px 12px;
366
+ border: none;
367
+ background: transparent;
368
+ border-radius: 6px 6px 0 0;
369
+ cursor: pointer;
370
+ color: var(--color-fg-muted, #57606a);
371
+ }
372
+
373
+ .pr-helper-review-tab.active {
374
+ background: #ffffff;
375
+ color: #9333ea;
376
+ font-weight: 600;
377
+ box-shadow: inset 0 -2px 0 #9333ea;
378
+ }
379
+
380
+ /* Locked until a review exists. Faded rather than hidden so the reviewer can see what
381
+ they're about to unlock, and not-allowed on hover pairs with the title tooltip that
382
+ says why. Kept clickable at the DOM level (aria-disabled, not disabled) — see
383
+ renderReviewTabsNav — so that tooltip actually fires. */
384
+ .pr-helper-review-tab-locked {
385
+ opacity: 0.4;
386
+ cursor: not-allowed;
387
+ }
388
+
389
+ .pr-helper-review-tab-locked:hover {
390
+ background: transparent;
391
+ color: var(--color-fg-muted, #57606a);
392
+ }
393
+
394
+ .pr-helper-tab-divider {
395
+ width: 1px;
396
+ align-self: stretch;
397
+ margin: 4px 4px;
398
+ background: var(--color-border-default, #d0d7de);
399
+ }
400
+
401
+ /*
402
+ * The content canvas.
403
+ *
404
+ * Everything in the sidebar used to sit on the same white, so a card with a hairline border was
405
+ * the only thing separating content from chrome — and on the Assistant tab, where there is one
406
+ * message and no cards at all, nothing separated them. Recessing the scroll area half a step gives
407
+ * every tab the same figure/ground relationship without enclosing anything in a box.
408
+ *
409
+ * #f8fafc rather than a new value: it is what the footer already uses, so the two recessed
410
+ * surfaces at the top and bottom of the sidebar are the same colour rather than two near-identical
411
+ * greys. Cards, inputs and the toolbar stay white and now read as raised against it.
412
+ */
413
+ .pr-helper-review-panel {
414
+ flex: 1;
415
+ min-height: 0;
416
+ overflow-y: auto;
417
+ padding: 14px 16px;
418
+ background: #f8fafc;
419
+ }
420
+
421
+ /*
422
+ * The strip between the tab bar and the canvas: which analysis is on screen, and whether it is
423
+ * still current. White, so it belongs to the chrome above rather than the content below, and
424
+ * closed with a divider so the canvas visibly starts underneath it.
425
+ *
426
+ * Collapses entirely when empty — on History, and before any analysis exists — so no divider or
427
+ * padding is left floating with nothing above it.
428
+ */
429
+ .pr-helper-analysis-toolbar {
430
+ flex-shrink: 0;
431
+ padding: 10px 16px;
432
+ background: #ffffff;
433
+ border-bottom: 1px solid var(--color-border-default, #d0d7de);
434
+ }
435
+
436
+ .pr-helper-analysis-toolbar:empty {
437
+ display: none;
438
+ }
439
+
440
+ /* The toolbar owns the spacing now, so the block inside it sheds the margin it needed as content. */
441
+ .pr-helper-analysis-toolbar .pr-helper-analysis-provenance {
442
+ margin: 0;
443
+ padding: 0;
444
+ }
445
+
446
+ /* The banner keeps its box but sits inside the toolbar's padding rather than the panel's. */
447
+ .pr-helper-analysis-toolbar .pr-helper-update-notice {
448
+ margin: 10px 0 0;
449
+ }
450
+
451
+ .pr-helper-review-empty {
452
+ font-size: 13px;
453
+ color: var(--color-fg-muted, #57606a);
454
+ padding: 12px 0;
455
+ margin: 0;
456
+ }
457
+
458
+ /* ── No-review empty state ── */
459
+ /* Centred in the panel's full height rather than sitting at the top like a stray
460
+ paragraph — the vertical centring is what makes this read as an intentional screen
461
+ instead of an unfinished one. min-height (not height) so it still scrolls rather than
462
+ clipping in a short window. */
463
+ .pr-helper-review-intro {
464
+ min-height: 100%;
465
+ display: flex;
466
+ flex-direction: column;
467
+ align-items: center;
468
+ justify-content: center;
469
+ text-align: center;
470
+ padding: 24px 8px;
471
+ box-sizing: border-box;
472
+ }
473
+
474
+ .pr-helper-intro-mark {
475
+ width: 40px;
476
+ height: 40px;
477
+ border-radius: 10px;
478
+ margin-bottom: 12px;
479
+ }
480
+
481
+ .pr-helper-intro-brand {
482
+ font-size: 10px;
483
+ font-weight: 700;
484
+ letter-spacing: 0.08em;
485
+ text-transform: uppercase;
486
+ color: #9333ea;
487
+ margin: 0 0 10px;
488
+ }
489
+
490
+ .pr-helper-intro-title {
491
+ font-size: 15px;
492
+ font-weight: 700;
493
+ color: var(--color-fg-default, #1f2328);
494
+ margin: 0 0 8px;
495
+ }
496
+
497
+ .pr-helper-intro-body {
498
+ font-size: 13px;
499
+ line-height: 1.55;
500
+ color: var(--color-fg-muted, #57606a);
501
+ margin: 0 0 18px;
502
+ max-width: 280px;
503
+ }
504
+
505
+ .pr-helper-generate-review-button {
506
+ font-size: 13px;
507
+ font-weight: 600;
508
+ font-family: inherit;
509
+ padding: 9px 20px;
510
+ border: none;
511
+ border-radius: 7px;
512
+ background: #9333ea;
513
+ color: #ffffff;
514
+ cursor: pointer;
515
+ }
516
+
517
+ .pr-helper-generate-review-button:hover { background: #7c3aed; }
518
+
519
+ /* ── Overview tab ── */
520
+ .pr-helper-risk-low { background: rgba(63, 185, 80, 0.12); color: #1a7f37; }
521
+ .pr-helper-risk-medium { background: rgba(240, 178, 50, 0.15); color: #9a6700; }
522
+ .pr-helper-risk-high { background: rgba(255, 80, 80, 0.12); color: #cf222e; }
523
+
524
+ .pr-helper-risk-text {
525
+ display: inline-block;
526
+ font-size: 10px;
527
+ font-weight: 700;
528
+ text-transform: uppercase;
529
+ letter-spacing: 0.04em;
530
+ padding: 2px 8px;
531
+ border-radius: 100px;
532
+ white-space: nowrap;
533
+ }
534
+
535
+ .pr-helper-risk-headline {
536
+ padding: 16px 18px;
537
+ border-radius: 10px;
538
+ /* Was #f8fafc, which is now the canvas colour itself. */
539
+ background: #ffffff;
540
+ border: 1px solid var(--color-border-default, #d0d7de);
541
+ margin-bottom: 14px;
542
+ }
543
+
544
+ .pr-helper-risk-headline-top {
545
+ display: flex;
546
+ align-items: center;
547
+ gap: 8px;
548
+ }
549
+
550
+ .pr-helper-risk-dot {
551
+ width: 10px;
552
+ height: 10px;
553
+ border-radius: 50%;
554
+ flex-shrink: 0;
555
+ }
556
+
557
+ .pr-helper-risk-dot-low { background: #1a7f37; }
558
+ .pr-helper-risk-dot-medium { background: #9a6700; }
559
+ .pr-helper-risk-dot-high { background: #cf222e; }
560
+
561
+ .pr-helper-risk-headline-label {
562
+ font-size: 16px;
563
+ font-weight: 700;
564
+ color: var(--color-fg-default, #24292f);
565
+ }
566
+
567
+ .pr-helper-risk-headline-takeaway {
568
+ font-size: 13px;
569
+ color: var(--color-fg-muted, #57606a);
570
+ margin: 8px 0 0;
571
+ line-height: 1.5;
572
+ }
573
+
574
+ .pr-helper-overview-stats {
575
+ display: grid;
576
+ grid-template-columns: repeat(4, 1fr);
577
+ gap: 8px;
578
+ margin-bottom: 14px;
579
+ }
580
+
581
+ /* A <button>, so the browser's default control styling has to be unwound before the card
582
+ reads as a card again — font is the one that bites: buttons don't inherit it, so without
583
+ this the value and label drop to the UA's 13.333px system font. */
584
+ .pr-helper-overview-stat {
585
+ position: relative; /* anchors the coverage tooltip */
586
+ display: flex;
587
+ flex-direction: column;
588
+ align-items: center;
589
+ text-align: center;
590
+ padding: 8px 4px;
591
+ border: 1px solid var(--color-border-default, #d0d7de);
592
+ border-radius: 8px;
593
+ background: #ffffff;
594
+ font: inherit;
595
+ cursor: pointer;
596
+ transition: background 0.12s, border-color 0.12s;
597
+ }
598
+
599
+ /* Matches the history/file rows rather than the filled buttons: these are shortcuts, and a
600
+ row of four solid purple blocks would read as the primary action of the Overview. */
601
+ .pr-helper-overview-stat:hover {
602
+ background: rgba(147, 51, 234, 0.06);
603
+ border-color: #9333ea;
604
+ }
605
+
606
+ .pr-helper-overview-stat:focus-visible {
607
+ outline: 2px solid #9333ea;
608
+ outline-offset: 1px;
609
+ }
610
+
611
+ .pr-helper-overview-stat-value {
612
+ font-size: 18px;
613
+ font-weight: 700;
614
+ color: var(--color-fg-default, #24292f);
615
+ }
616
+
617
+ /* "50 / 482" is eight glyphs where the other three cards show one or two, and the grid gives
618
+ each card a quarter of a narrow sidebar. Dropping two points and holding the digits to a
619
+ fixed advance keeps it on one line without shrinking the cards that don't need it. */
620
+ .pr-helper-overview-stat-value-wide {
621
+ font-size: 16px;
622
+ white-space: nowrap;
623
+ font-variant-numeric: tabular-nums;
624
+ }
625
+
626
+ /* position: relative anchors the two decorations that flank it. Both are taken out of flow on
627
+ purpose: they used to sit inside this box, so the card centred "Files →" as a unit and the
628
+ word itself landed ~6px left of the number above it. With the info icon present the two
629
+ roughly cancelled, which is why only the cards without one looked wrong. */
630
+ .pr-helper-overview-stat-label {
631
+ position: relative;
632
+ font-size: 11px;
633
+ color: var(--color-fg-muted, #57606a);
634
+ margin-top: 3px;
635
+ }
636
+
637
+ /* Explains a capped review, and only ever appears on one. Sits ahead of the 11px label rather
638
+ than beside the value, so it's sized to that row — against the bold number it read as a
639
+ glyph belonging to the count. */
640
+ .pr-helper-overview-stat-info {
641
+ position: absolute;
642
+ right: calc(100% + 3px);
643
+ top: 50%;
644
+ transform: translateY(-50%);
645
+ display: inline-flex;
646
+ align-items: center;
647
+ /* The glyph draws its own ring, so there is no border or font styling here — only the box it
648
+ sits in. It inherits the label's muted colour through the SVG's fill="currentColor", which
649
+ is also why it firms up with the label on hover instead of needing its own rule. */
650
+ margin-right: 3px;
651
+ line-height: 0;
652
+ vertical-align: -2px;
653
+ cursor: default;
654
+ }
655
+
656
+ .pr-helper-overview-stat-info::after {
657
+ content: attr(data-tip);
658
+ position: absolute;
659
+ top: calc(100% + 4px);
660
+ /* Anchored to the card's right edge and allowed to overflow it leftward. Constrained to the
661
+ card's own width this would render as a one-word-per-line column — the cards are a quarter
662
+ of a narrow sidebar. Coverage is the rightmost card, so growing leftward stays in bounds. */
663
+ right: 0;
664
+ left: auto;
665
+ width: 180px;
666
+ z-index: 2;
667
+ padding: 6px 8px;
668
+ border-radius: 6px;
669
+ background: #24292f;
670
+ color: #ffffff;
671
+ font-size: 11px;
672
+ font-weight: 400;
673
+ line-height: 1.4;
674
+ text-align: left;
675
+ white-space: normal;
676
+ opacity: 0;
677
+ pointer-events: none;
678
+ transition: opacity 0.12s;
679
+ }
680
+
681
+ .pr-helper-overview-stat-info:hover::after,
682
+ .pr-helper-overview-stat:focus-visible .pr-helper-overview-stat-info::after {
683
+ opacity: 1;
684
+ }
685
+
686
+ /* Deliberately faint: the arrow is a hint that the card goes somewhere, not a second label.
687
+ It firms up on hover, where the reviewer is already about to click. */
688
+ /* Hangs off the label's right edge rather than extending it, so the label text stays centred
689
+ under the value. No gap: at the sidebar's 380px minimum each card is ~81px wide and the
690
+ longest label ("Questions") leaves comfortable margin — this used to read "Guide topics",
691
+ which was the tightest case and is why the arrow is positioned absolutely rather than inline. */
692
+ .pr-helper-overview-stat-arrow {
693
+ position: absolute;
694
+ left: 100%;
695
+ top: 0;
696
+ opacity: 0.45;
697
+ transition: opacity 0.12s;
698
+ }
699
+
700
+ .pr-helper-overview-stat:hover .pr-helper-overview-stat-arrow { opacity: 1; }
701
+
702
+ .pr-helper-overview-stat:hover .pr-helper-overview-stat-label { color: #9333ea; }
703
+
704
+ .pr-helper-review-summary {
705
+ font-size: 14px;
706
+ line-height: 1.55;
707
+ color: var(--color-fg-default, #24292f);
708
+ margin: 0 0 14px;
709
+ }
710
+
711
+ .pr-helper-sidebar code {
712
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
713
+ font-size: 0.9em;
714
+ background: var(--color-neutral-muted, rgba(175, 184, 193, 0.2));
715
+ border-radius: 4px;
716
+ padding: 0.1em 0.4em;
717
+ }
718
+
719
+ .pr-helper-explain-simply-button {
720
+ font-size: 12px;
721
+ font-weight: 600;
722
+ font-family: inherit;
723
+ padding: 7px 14px;
724
+ border-radius: 6px;
725
+ border: 1px solid #9333ea;
726
+ background: transparent;
727
+ color: #9333ea;
728
+ cursor: pointer;
729
+ margin: 0 0 14px;
730
+ }
731
+
732
+ .pr-helper-explain-simply-button:hover { background: rgba(147, 51, 234, 0.08); }
733
+
734
+ .pr-helper-explain-simply-done {
735
+ font-size: 13px;
736
+ color: var(--color-fg-muted, #57606a);
737
+ margin: 0 0 14px;
738
+ }
739
+
740
+ .pr-helper-review-panel h4 {
741
+ font-size: 11px;
742
+ text-transform: uppercase;
743
+ letter-spacing: 0.06em;
744
+ color: #94a3b8;
745
+ margin: 16px 0 8px;
746
+ }
747
+
748
+ .pr-helper-review-panel ul {
749
+ margin: 0;
750
+ padding-left: 18px;
751
+ font-size: 14px;
752
+ line-height: 1.5;
753
+ }
754
+
755
+ /* ── Pill tooltips ──
756
+ Every pill in the sidebar is one uppercase word standing in for a whole axis of judgement, and
757
+ MAJOR sitting next to "medium confidence" gives no hint that they answer different questions.
758
+ Hovering explains them; see ui/components/pill_tooltips.ts for the copy and why it is worded
759
+ the way it is.
760
+
761
+ Deliberately not the native `title` attribute. That fires after roughly a second of stillness,
762
+ renders in the OS font at the cursor rather than against the card, and cannot be seen at all
763
+ while the pointer is moving — which is exactly the moment a reviewer scanning a list of
764
+ findings wants the answer. This is the same `content: attr(data-tip)` mechanism the Overview's
765
+ coverage note already uses, generalised so any pill can opt in.
766
+
767
+ The pill itself is NOT positioned, on purpose: the ::after resolves against the badge row
768
+ instead, so a 220px card hanging off a pill near the right edge of a 380px sidebar cannot
769
+ overflow and get clipped by the panel's overflow-y. Anchoring to the row also lands every
770
+ tooltip in the same place, so moving along a row of pills swaps the text rather than making
771
+ the box jump. Each anchor is a flex row of pills, listed here rather than given a shared
772
+ class because they already exist and their layout is unrelated. */
773
+ .pr-helper-issue-badges,
774
+ .pr-helper-history-item-top,
775
+ .pr-helper-history-followup-top {
776
+ position: relative;
777
+ }
778
+
779
+ .pr-helper-tip::after {
780
+ content: attr(data-tip);
781
+ position: absolute;
782
+ top: calc(100% + 6px);
783
+ /* Both edges pinned, so the tooltip is exactly as wide as the row it explains. */
784
+ left: 0;
785
+ right: 0;
786
+ z-index: 3;
787
+ padding: 7px 9px;
788
+ border-radius: 6px;
789
+ background: #24292f;
790
+ color: #ffffff;
791
+ /* The pills set uppercase, letter-spacing and 700 weight on themselves, all of which the
792
+ ::after would otherwise inherit and turn a sentence into a ransom note. */
793
+ font-size: 11px;
794
+ font-weight: 400;
795
+ font-family: inherit;
796
+ text-transform: none;
797
+ letter-spacing: normal;
798
+ line-height: 1.45;
799
+ text-align: left;
800
+ white-space: normal;
801
+ opacity: 0;
802
+ pointer-events: none;
803
+ transition: opacity 0.1s;
804
+ }
805
+
806
+ /* Delay on the way in, none on the way out: a reviewer dragging the pointer across a card
807
+ should not trip four tooltips in sequence, but one they've finished reading should vanish the
808
+ instant they leave. Two rules rather than one because a transition-delay applies to both
809
+ directions. */
810
+ .pr-helper-tip:hover::after,
811
+ .pr-helper-tip:focus-visible::after {
812
+ opacity: 1;
813
+ transition: opacity 0.1s 0.35s;
814
+ }
815
+
816
+ /* ── Issues tab ── */
817
+ .pr-helper-issue-card {
818
+ border: 1px solid var(--color-border-default, #d0d7de);
819
+ border-radius: 8px;
820
+ padding: 16px;
821
+ margin-bottom: 12px;
822
+ /* Stated rather than inherited: the surface behind this is now the grey canvas. */
823
+ background: #ffffff;
824
+ }
825
+
826
+ .pr-helper-issue-header {
827
+ display: flex;
828
+ flex-direction: column;
829
+ align-items: flex-start;
830
+ gap: 8px;
831
+ margin-bottom: 8px;
832
+ text-align: left;
833
+ }
834
+
835
+ .pr-helper-issue-badges {
836
+ display: flex;
837
+ align-items: center;
838
+ justify-content: flex-start;
839
+ flex-wrap: wrap;
840
+ gap: 6px;
841
+ width: 100%;
842
+ }
843
+
844
+ .pr-helper-severity-badge {
845
+ font-size: 11px;
846
+ font-weight: 700;
847
+ text-transform: uppercase;
848
+ padding: 3px 9px;
849
+ border-radius: 100px;
850
+ white-space: nowrap;
851
+ }
852
+
853
+ .pr-helper-severity-critical .pr-helper-severity-badge { background: var(--pr-helper-pill-danger-bg); color: var(--pr-helper-pill-danger-fg); }
854
+ .pr-helper-severity-major .pr-helper-severity-badge { background: var(--pr-helper-pill-warning-bg); color: var(--pr-helper-pill-warning-fg); }
855
+ .pr-helper-severity-minor .pr-helper-severity-badge { background: var(--pr-helper-pill-info-bg); color: var(--pr-helper-pill-info-fg); }
856
+ .pr-helper-severity-nit .pr-helper-severity-badge { background: var(--pr-helper-pill-neutral-bg); color: var(--pr-helper-pill-neutral-fg); }
857
+
858
+ .pr-helper-issue-title { font-size: 14px; font-weight: 600; margin: 0; text-align: left; width: 100%; }
859
+
860
+ .pr-helper-issue-desc {
861
+ font-size: 13px;
862
+ line-height: 1.5;
863
+ color: var(--color-fg-muted, #57606a);
864
+ margin: 0 0 8px;
865
+ text-align: left;
866
+ }
867
+
868
+ /* "Reviewed with N existing discussion threads" — a provenance note under the Overview summary,
869
+ sized to be findable but not to compete with the summary itself. */
870
+ .pr-helper-discussion-awareness {
871
+ font-size: 12px;
872
+ line-height: 1.4;
873
+ margin: -4px 0 12px;
874
+ color: var(--color-fg-muted, #57606a);
875
+ text-align: left;
876
+ }
877
+
878
+ /* "This PR changes 482 files..." — the same provenance register as discussion awareness above,
879
+ deliberately so: both tell the reviewer what the review was actually given. */
880
+ .pr-helper-coverage-caveat {
881
+ font-size: 12px;
882
+ line-height: 1.4;
883
+ margin: -4px 0 12px;
884
+ color: var(--color-fg-muted, #57606a);
885
+ text-align: left;
886
+ }
887
+
888
+ /* "Already raised by @x" — deliberately quiet. It qualifies the finding above it rather than
889
+ competing with the severity badge, so it reads as a footnote, not a second headline. */
890
+ /* The one line on a finding that can tell a reviewer to stop reading, so it is set as a status
891
+ rather than as a note. It was a muted 12px line with a 2px rule beside it, sitting between the
892
+ description and the file path; a reader looking straight at two of them asked where the feature
893
+ had got to. Weight and a tint, not a colour shout — it still must not outrank the severity. */
894
+ .pr-helper-issue-discussion {
895
+ font-size: 12px;
896
+ line-height: 1.45;
897
+ margin: 0 0 8px;
898
+ padding: 6px 10px;
899
+ border-radius: 6px;
900
+ border-left: 3px solid var(--pr-helper-pill-neutral-bg, #d0d7de);
901
+ background: var(--pr-helper-discussion-bg, rgba(15, 23, 42, 0.04));
902
+ color: var(--color-fg-muted, #57606a);
903
+ text-align: left;
904
+ }
905
+
906
+ .pr-helper-issue-discussion-lead {
907
+ font-weight: 600;
908
+ color: var(--color-fg-default, #1f2328);
909
+ }
910
+
911
+ .pr-helper-issue-discussion-unresolved {
912
+ border-left-color: var(--pr-helper-pill-warning-bg, #d4a72c);
913
+ background: var(--pr-helper-discussion-unresolved-bg, rgba(212, 167, 44, 0.1));
914
+ }
915
+
916
+ .pr-helper-issue-discussion-link {
917
+ color: inherit;
918
+ text-decoration: none;
919
+ }
920
+
921
+ .pr-helper-issue-discussion-link:hover { text-decoration: underline; }
922
+
923
+ .pr-helper-issue-loc {
924
+ display: block;
925
+ cursor: pointer;
926
+ margin-top: 6px;
927
+ }
928
+
929
+ .pr-helper-issue-loc:hover .pr-helper-path-name { text-decoration: underline; }
930
+
931
+ /* ── Questions tab ── */
932
+ /* Deliberately reuses .pr-helper-issue-header/-title/-badges/-desc/-loc from the Issues tab
933
+ (see renderQuestionCard) rather than defining a parallel set: a question and an issue are
934
+ the same shape of thing — a bordered card with a title, a severity-style badge and a short
935
+ explanation — so they share the ruleset and only the card shell and its own bits below are
936
+ Questions-specific. */
937
+ .pr-helper-question-card {
938
+ border: 1px solid var(--color-border-default, #d0d7de);
939
+ border-radius: 8px;
940
+ padding: 16px;
941
+ margin-bottom: 12px;
942
+ /* Stated rather than inherited: the surface behind this is now the grey canvas. */
943
+ background: #ffffff;
944
+ }
945
+
946
+ /* Answered is additive, same principle as a completed Guide section: dimming or collapsing the
947
+ card would throw away the reviewer's own record that they resolved it. The check mark is the
948
+ whole "done" signal; nothing else about the card recedes. */
949
+ .pr-helper-question-card-answered { box-shadow: inset 3px 0 0 #1a7f37; }
950
+
951
+ .pr-helper-question-card h5 {
952
+ font-size: 10px;
953
+ text-transform: uppercase;
954
+ letter-spacing: 0.06em;
955
+ color: #94a3b8;
956
+ margin: 10px 0 4px;
957
+ }
958
+
959
+ .pr-helper-question-importance-badge {
960
+ font-size: 11px;
961
+ font-weight: 700;
962
+ text-transform: uppercase;
963
+ padding: 3px 9px;
964
+ border-radius: 100px;
965
+ white-space: nowrap;
966
+ }
967
+
968
+ .pr-helper-question-importance-high { background: var(--pr-helper-pill-danger-bg); color: var(--pr-helper-pill-danger-fg); }
969
+ .pr-helper-question-importance-medium { background: var(--pr-helper-pill-warning-bg); color: var(--pr-helper-pill-warning-fg); }
970
+ .pr-helper-question-importance-low { background: var(--pr-helper-pill-neutral-bg); color: var(--pr-helper-pill-neutral-fg); }
971
+
972
+ /* ── Questions progress header ──
973
+ Deliberately lighter than the Guide's progress box (no bar, no "next up" line): the Guide is
974
+ a workflow you walk in order, so it earns a full dashboard, whereas this is a flat list where
975
+ the only useful fact is how many are still open. */
976
+ .pr-helper-questions-progress {
977
+ display: flex;
978
+ align-items: center;
979
+ gap: 8px;
980
+ font-size: 12px;
981
+ color: var(--color-fg-muted, #57606a);
982
+ padding-bottom: 12px;
983
+ margin-bottom: 4px;
984
+ border-bottom: 1px solid var(--color-border-default, #d0d7de);
985
+ }
986
+
987
+ .pr-helper-questions-progress-count { font-weight: 600; }
988
+
989
+ .pr-helper-questions-progress-check { color: #1a7f37; font-weight: 700; }
990
+
991
+ .pr-helper-question-actions {
992
+ display: flex;
993
+ align-items: center;
994
+ justify-content: space-between;
995
+ flex-wrap: wrap;
996
+ gap: 12px;
997
+ margin-top: 12px;
998
+ }
999
+
1000
+ /* Deliberately not a filled button, matching .pr-helper-guide-start-cta: this is "here's the
1001
+ next step", not a demand for a click. */
1002
+ .pr-helper-question-jump {
1003
+ padding: 0;
1004
+ border: none;
1005
+ background: transparent;
1006
+ color: #9333ea;
1007
+ font-size: 13px;
1008
+ font-weight: 700;
1009
+ font-family: inherit;
1010
+ cursor: pointer;
1011
+ }
1012
+
1013
+ .pr-helper-question-jump { text-decoration: none; }
1014
+ .pr-helper-question-jump:hover { color: #7c3aed; text-decoration: underline; }
1015
+
1016
+ /* Same button geometry as .pr-helper-explain-simply-button (the sidebar's existing outlined
1017
+ secondary), but neutral rather than purple: purple is spoken for by the primary action
1018
+ sitting next to it, and an outlined *neutral* button is the standard way to say "real
1019
+ control, lower emphasis". It was plain text before, which made a state-changing action look
1020
+ like a caption. */
1021
+ .pr-helper-question-answer-toggle {
1022
+ font-size: 12px;
1023
+ font-weight: 600;
1024
+ font-family: inherit;
1025
+ padding: 5px 12px;
1026
+ border-radius: 6px;
1027
+ border: 1px solid var(--color-border-default, #d0d7de);
1028
+ background: transparent;
1029
+ color: var(--color-fg-muted, #57606a);
1030
+ cursor: pointer;
1031
+ margin-left: auto;
1032
+ white-space: nowrap;
1033
+ }
1034
+
1035
+ .pr-helper-question-answer-toggle:hover {
1036
+ background: rgba(175, 184, 193, 0.12);
1037
+ color: var(--color-fg-default, #24292f);
1038
+ }
1039
+
1040
+ /* Answered: the button becomes the card's "undo", so it picks up the same green the card's
1041
+ edge uses — the control and the state it produced read as one thing. */
1042
+ .pr-helper-question-answer-toggle[data-answered="true"] {
1043
+ border-color: rgba(26, 127, 55, 0.4);
1044
+ color: #1a7f37;
1045
+ }
1046
+
1047
+ .pr-helper-question-answer-toggle[data-answered="true"]:hover { background: rgba(26, 127, 55, 0.08); }
1048
+
1049
+ /* ── File path display, shared by every tab ──
1050
+ One construct wherever a file is named: the Guide's section files, the Issues and Questions
1051
+ location refs, and the Files tab's browser rows. They were three near-identical pairs of
1052
+ rules with drifting sizes and colours; a reviewer moving between tabs was seeing the same
1053
+ file rendered three different ways.
1054
+
1055
+ The split exists because a deep monorepo path is mostly shared prefix — at full weight it
1056
+ wraps to three lines and buries the filename, which is the only part that identifies it.
1057
+ It's also what stops the overflow: a path is a single unbroken token with no break
1058
+ opportunity, so rendered whole it pushes past the fixed-width sidebar and drags a horizontal
1059
+ scrollbar onto the panel. The directory truncates; only the short filename has to fit. Every
1060
+ call site puts the full path on a title attribute, so nothing is unrecoverable. */
1061
+ .pr-helper-path-dir {
1062
+ display: block;
1063
+ /* Declared, never inherited. Without this the directory picked up whatever font its ancestor
1064
+ chain happened to supply — and the three call sites have different chains, so the same class
1065
+ rendered monospace under one tab and the sidebar's sans-serif under another, with the
1066
+ filename below it always monospace. Half a path in each font. It's a path either way, so it
1067
+ matches the filename. */
1068
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
1069
+ font-size: 11px;
1070
+ color: var(--color-fg-muted, #57606a);
1071
+ opacity: 0.85;
1072
+ white-space: nowrap;
1073
+ overflow: hidden;
1074
+ text-overflow: ellipsis;
1075
+ }
1076
+
1077
+ /* The trailing slash marks the line as a directory rather than a truncated filename. Generated
1078
+ here rather than baked into the markup so it stays out of the title attribute and the
1079
+ data-file path, which must remain the exact path GitHub's diff anchor is hashed from. */
1080
+ .pr-helper-path-dir::after {
1081
+ content: "/";
1082
+ }
1083
+
1084
+ /* Wrapped rather than truncated, unlike the directory above: a prefix is disposable context,
1085
+ but clipping the filename would cost the reviewer the identifier itself. */
1086
+ .pr-helper-path-name {
1087
+ display: block;
1088
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
1089
+ font-size: 13px;
1090
+ font-weight: 600;
1091
+ color: #9333ea;
1092
+ overflow-wrap: anywhere;
1093
+ }
1094
+
1095
+ /* No Files-tab size override here on purpose. A filename means the same thing in every tab,
1096
+ so it should look the same in every tab — the Files rows earn their prominence from the
1097
+ row's own padding and layout, not from larger type than the identical reference in Issues,
1098
+ Questions or the Guide. */
1099
+
1100
+ .pr-helper-question-group { margin-bottom: 4px; }
1101
+
1102
+ .pr-helper-question-group-title {
1103
+ font-size: 11px;
1104
+ font-weight: 700;
1105
+ text-transform: uppercase;
1106
+ letter-spacing: 0.04em;
1107
+ color: #9333ea;
1108
+ margin: 14px 0 6px;
1109
+ }
1110
+
1111
+ .pr-helper-question-group:first-child .pr-helper-question-group-title { margin-top: 0; }
1112
+
1113
+ /* ── Files tab ── */
1114
+ .pr-helper-file-stat {
1115
+ font-size: 12px;
1116
+ color: var(--color-fg-muted, #57606a);
1117
+ white-space: nowrap;
1118
+ }
1119
+
1120
+ .pr-helper-file-stat-issues { color: #9a6700; }
1121
+
1122
+ /* ── Guide tab ── */
1123
+ .pr-helper-guide {
1124
+ display: flex;
1125
+ flex-direction: column;
1126
+ gap: 4px;
1127
+ }
1128
+
1129
+ .pr-helper-suggested-path {
1130
+ padding: 14px 16px;
1131
+ border-radius: 8px;
1132
+ background: rgba(147, 51, 234, 0.06);
1133
+ border: 1px solid rgba(147, 51, 234, 0.2);
1134
+ margin-bottom: 12px;
1135
+ }
1136
+
1137
+ .pr-helper-suggested-path-title {
1138
+ font-size: 11px;
1139
+ font-weight: 700;
1140
+ text-transform: uppercase;
1141
+ letter-spacing: 0.04em;
1142
+ color: #9333ea;
1143
+ margin: 0 0 6px;
1144
+ }
1145
+
1146
+ .pr-helper-suggested-path-list {
1147
+ margin: 0;
1148
+ padding-left: 18px;
1149
+ font-size: 13px;
1150
+ line-height: 1.5;
1151
+ color: var(--color-fg-default, #24292f);
1152
+ }
1153
+
1154
+ .pr-helper-suggested-path-list li { margin-bottom: 3px; }
1155
+
1156
+ .pr-helper-suggested-path-time {
1157
+ font-size: 12px;
1158
+ color: var(--color-fg-muted, #57606a);
1159
+ margin: 8px 0 0;
1160
+ }
1161
+
1162
+ /* Every section done — the box swaps its purple "here's where to go" identity for a green
1163
+ "you're finished" one. Same geometry, so the Guide doesn't reflow underneath it. */
1164
+ .pr-helper-suggested-path-complete {
1165
+ background: rgba(26, 127, 55, 0.07);
1166
+ border-color: rgba(26, 127, 55, 0.25);
1167
+ }
1168
+
1169
+ .pr-helper-suggested-path-complete .pr-helper-suggested-path-title { color: #1a7f37; }
1170
+
1171
+ .pr-helper-progress-counter {
1172
+ display: flex;
1173
+ align-items: center;
1174
+ gap: 6px;
1175
+ font-size: 13px;
1176
+ font-weight: 600;
1177
+ color: var(--color-fg-default, #24292f);
1178
+ margin: 0 0 8px;
1179
+ }
1180
+
1181
+ .pr-helper-progress-check {
1182
+ color: #1a7f37;
1183
+ font-weight: 700;
1184
+ }
1185
+
1186
+ .pr-helper-progress-bar {
1187
+ height: 4px;
1188
+ border-radius: 100px;
1189
+ background: rgba(140, 149, 159, 0.25);
1190
+ overflow: hidden;
1191
+ }
1192
+
1193
+ .pr-helper-progress-bar-fill {
1194
+ height: 100%;
1195
+ border-radius: 100px;
1196
+ background: #9333ea;
1197
+ transition: width 0.35s ease;
1198
+ }
1199
+
1200
+ .pr-helper-suggested-path-complete .pr-helper-progress-bar-fill { background: #1a7f37; }
1201
+
1202
+ .pr-helper-progress-next {
1203
+ font-size: 13px;
1204
+ color: var(--color-fg-default, #24292f);
1205
+ margin: 10px 0 0;
1206
+ }
1207
+
1208
+ .pr-helper-progress-next-label {
1209
+ font-size: 11px;
1210
+ font-weight: 700;
1211
+ text-transform: uppercase;
1212
+ letter-spacing: 0.04em;
1213
+ color: var(--color-fg-muted, #57606a);
1214
+ margin-right: 4px;
1215
+ }
1216
+
1217
+ .pr-helper-guide-section {
1218
+ /* The disclosure arrow is the only thing that lives left of the card's text column;
1219
+ everything else — badges, title, meta row, body — is indented by this gutter so the
1220
+ card reads as a single left edge instead of three competing ones. Width of the arrow
1221
+ (10px) + the header row's gap (8px). */
1222
+ --pr-helper-guide-gutter: 18px;
1223
+ border: 1px solid var(--color-border-default, #d0d7de);
1224
+ border-radius: 8px;
1225
+ padding: 16px;
1226
+ margin-bottom: 12px;
1227
+ /* Drives the just-completed wash below: the class add fades in, the class removal fades
1228
+ back out, so no keyframe or animationend bookkeeping is involved. */
1229
+ transition: background-color 0.6s ease, border-color 0.6s ease;
1230
+ /* Stated rather than inherited: the surface behind this is now the grey canvas. */
1231
+ background: #ffffff;
1232
+ }
1233
+
1234
+ /* Additive only — a completed section keeps full contrast on its title, meta row and badges.
1235
+ See the comment in renderGuideSectionCard: these cards are the reviewer's record of what
1236
+ they've covered, not spent items to be dimmed out of the way. */
1237
+ /* The green edge is an inset shadow rather than a thicker border-left: widening the border
1238
+ would pull the card's content in by 2px at the exact moment the section flips to completed,
1239
+ so the whole card would visibly jog sideways during the transition. */
1240
+ .pr-helper-guide-section-completed {
1241
+ box-shadow: inset 3px 0 0 #1a7f37;
1242
+ background: rgba(26, 127, 55, 0.04);
1243
+ }
1244
+
1245
+ .pr-helper-guide-section-highlight {
1246
+ background: rgba(26, 127, 55, 0.16);
1247
+ border-color: rgba(26, 127, 55, 0.45);
1248
+ }
1249
+
1250
+ /* The auto margin is the split point of the badge row — everything after it goes right. */
1251
+ .pr-helper-guide-section-badges .pr-helper-start-here-badge,
1252
+ .pr-helper-guide-section-badges .pr-helper-completed-badge {
1253
+ margin-right: auto;
1254
+ }
1255
+
1256
+ .pr-helper-guide-check {
1257
+ color: #1a7f37;
1258
+ font-size: 13px;
1259
+ font-weight: 700;
1260
+ flex-shrink: 0;
1261
+ }
1262
+
1263
+ .pr-helper-guide-section-header {
1264
+ display: flex;
1265
+ flex-direction: column;
1266
+ gap: 4px;
1267
+ width: 100%;
1268
+ border: none;
1269
+ background: transparent;
1270
+ padding: 0;
1271
+ cursor: pointer;
1272
+ font-family: inherit;
1273
+ text-align: left;
1274
+ }
1275
+
1276
+ /* Split row: the status badge (Start here / Continue / Completed, plus its check) sits on the
1277
+ left at the title's gutter — it's about the reviewer's position and belongs with the title
1278
+ it labels — while the attention badge is pushed to the right edge as a standing property of
1279
+ the section. flex-end rather than space-between is what makes the attention badge hold the
1280
+ right edge even when there is no status badge to space against. */
1281
+ .pr-helper-guide-section-badges {
1282
+ display: flex;
1283
+ align-items: center;
1284
+ flex-wrap: wrap;
1285
+ justify-content: flex-end;
1286
+ gap: 8px;
1287
+ padding-left: var(--pr-helper-guide-gutter);
1288
+ }
1289
+
1290
+ .pr-helper-guide-section-header-top {
1291
+ display: flex;
1292
+ align-items: baseline;
1293
+ gap: 8px;
1294
+ width: 100%;
1295
+ }
1296
+
1297
+ /* Fixed width rather than glyph width: the gutter every other row is indented by has to be
1298
+ predictable, and ▼ measures differently across the fonts GitHub hands us. */
1299
+ .pr-helper-guide-disclosure {
1300
+ width: 10px;
1301
+ font-size: 10px;
1302
+ color: var(--color-fg-muted, #57606a);
1303
+ transition: transform 0.15s ease;
1304
+ flex-shrink: 0;
1305
+ }
1306
+
1307
+ .pr-helper-guide-section-collapsed .pr-helper-guide-disclosure { transform: rotate(-90deg); }
1308
+ .pr-helper-guide-section-collapsed .pr-helper-guide-section-body { display: none; }
1309
+
1310
+ .pr-helper-guide-section-title {
1311
+ font-size: 14px;
1312
+ font-weight: 700;
1313
+ flex: 1;
1314
+ }
1315
+
1316
+ .pr-helper-guide-badge {
1317
+ font-size: 11px;
1318
+ font-weight: 700;
1319
+ text-transform: uppercase;
1320
+ letter-spacing: 0.04em;
1321
+ padding: 3px 9px;
1322
+ border-radius: 100px;
1323
+ white-space: nowrap;
1324
+ }
1325
+
1326
+ /* Start Here is metadata (which section the model recommends first), same role as the
1327
+ attention badge next to it — not a control. The action to launch guided navigation lives
1328
+ in .pr-helper-guide-start-cta at the bottom of the card instead; see UX principle in
1329
+ renderGuideSectionCard's comment: badges communicate state, buttons perform actions. */
1330
+ .pr-helper-start-here-badge {
1331
+ background: #9333ea;
1332
+ color: #ffffff;
1333
+ }
1334
+
1335
+ /* Filled, not tinted — it's the direct counterpart to Start here, and a completed section
1336
+ should read as an equally definite state rather than a faded one. */
1337
+ .pr-helper-completed-badge {
1338
+ background: #1a7f37;
1339
+ color: #ffffff;
1340
+ }
1341
+
1342
+ .pr-helper-attention-badge.pr-helper-attention-high { background: rgba(255, 80, 80, 0.12); color: #cf222e; }
1343
+ .pr-helper-attention-badge.pr-helper-attention-medium { background: rgba(240, 178, 50, 0.15); color: #9a6700; }
1344
+ .pr-helper-attention-badge.pr-helper-attention-low { background: rgba(175, 184, 193, 0.2); color: #57606a; }
1345
+
1346
+ .pr-helper-guide-section-body {
1347
+ margin-top: 8px;
1348
+ padding-left: var(--pr-helper-guide-gutter);
1349
+ }
1350
+
1351
+ /* Deliberately not a filled button — the Guide is calm and information-focused, and a
1352
+ full-width primary button here competed with the summary/checklist for attention. This
1353
+ reads as "you're ready, here's the next step" rather than a CTA demanding a click. */
1354
+ .pr-helper-guide-start-cta {
1355
+ display: inline-block;
1356
+ margin-top: 12px;
1357
+ padding: 0;
1358
+ border: none;
1359
+ background: transparent;
1360
+ color: #9333ea;
1361
+ font-size: 13px;
1362
+ font-weight: 700;
1363
+ font-family: inherit;
1364
+ cursor: pointer;
1365
+ text-align: left;
1366
+ }
1367
+
1368
+ .pr-helper-guide-start-cta:hover { color: #7c3aed; text-decoration: underline; }
1369
+
1370
+ .pr-helper-guide-section-actions {
1371
+ display: flex;
1372
+ align-items: center;
1373
+ justify-content: space-between;
1374
+ gap: 12px;
1375
+ }
1376
+
1377
+ /* Quieter than the CTA beside it on purpose: undo is a recovery path for a stray "Finish
1378
+ section", not something to invite. */
1379
+ .pr-helper-guide-uncomplete {
1380
+ margin-top: 12px;
1381
+ padding: 0;
1382
+ border: none;
1383
+ background: transparent;
1384
+ color: var(--color-fg-muted, #57606a);
1385
+ font-size: 12px;
1386
+ font-family: inherit;
1387
+ cursor: pointer;
1388
+ }
1389
+
1390
+ .pr-helper-guide-uncomplete:hover { color: var(--color-fg-default, #24292f); text-decoration: underline; }
1391
+
1392
+ .pr-helper-guide-summary {
1393
+ font-size: 14px;
1394
+ font-weight: 600;
1395
+ margin: 0 0 8px;
1396
+ }
1397
+
1398
+ .pr-helper-guide-purpose,
1399
+ .pr-helper-guide-why {
1400
+ font-size: 13px;
1401
+ color: var(--color-fg-muted, #57606a);
1402
+ line-height: 1.55;
1403
+ margin: 0 0 10px;
1404
+ }
1405
+
1406
+ .pr-helper-guide-meta-row {
1407
+ font-size: 12px;
1408
+ color: var(--color-fg-muted, #57606a);
1409
+ padding-left: var(--pr-helper-guide-gutter);
1410
+ }
1411
+
1412
+ .pr-helper-guide-section-body h5 {
1413
+ font-size: 10px;
1414
+ text-transform: uppercase;
1415
+ letter-spacing: 0.06em;
1416
+ color: #94a3b8;
1417
+ margin: 10px 0 4px;
1418
+ }
1419
+
1420
+ .pr-helper-guide-files {
1421
+ margin: 0;
1422
+ padding-left: 0;
1423
+ list-style: none;
1424
+ }
1425
+
1426
+ /* The row is a block, not a text line, so the directory, filename and focus note can each
1427
+ take their own line and carry their own weight. */
1428
+ /* Padded on every side, then pulled back out by the same amount horizontally: the row's text
1429
+ stays aligned with the rest of the card's text column, while the current-target highlight
1430
+ gets room to sit around the content instead of hugging it. */
1431
+ .pr-helper-guide-file {
1432
+ display: block;
1433
+ cursor: pointer;
1434
+ padding: 8px;
1435
+ margin: 0 -8px;
1436
+ }
1437
+
1438
+ /* Underline only the filename on hover — dragging an underline through the directory and focus
1439
+ prose would make the whole three-line row look like one long link. */
1440
+ .pr-helper-guide-file:hover .pr-helper-path-name { text-decoration: underline; }
1441
+
1442
+ .pr-helper-guide-file-focus {
1443
+ font-size: 12px;
1444
+ line-height: 1.5;
1445
+ color: var(--color-fg-muted, #57606a);
1446
+ margin-top: 2px;
1447
+ padding-left: 2px;
1448
+ }
1449
+
1450
+ .pr-helper-guide-checklist {
1451
+ margin: 0;
1452
+ padding: 10px 12px 10px 26px;
1453
+ font-size: 13px;
1454
+ line-height: 1.5;
1455
+ color: var(--color-fg-default, #24292f);
1456
+ background: #f8fafc;
1457
+ border: 1px solid var(--color-border-default, #d0d7de);
1458
+ border-radius: 6px;
1459
+ list-style: none;
1460
+ }
1461
+
1462
+ .pr-helper-guide-checklist li {
1463
+ position: relative;
1464
+ margin-bottom: 5px;
1465
+ line-height: 1.5;
1466
+ }
1467
+
1468
+ .pr-helper-guide-checklist li:last-child { margin-bottom: 0; }
1469
+
1470
+ .pr-helper-guide-checklist li::before {
1471
+ content: '•';
1472
+ position: absolute;
1473
+ left: -14px;
1474
+ color: #9333ea;
1475
+ font-weight: 700;
1476
+ }
1477
+
1478
+ /* Section tags shown on Issues/Questions items, and section badges on Files rows —
1479
+ both point back to the Guide, reusing the same visual language as file locations. */
1480
+ .pr-helper-section-tag {
1481
+ font-size: 11px;
1482
+ font-weight: 600;
1483
+ color: #9333ea;
1484
+ background: rgba(147, 51, 234, 0.08);
1485
+ border: none;
1486
+ border-radius: 100px;
1487
+ padding: 3px 9px;
1488
+ cursor: pointer;
1489
+ font-family: inherit;
1490
+ white-space: nowrap;
1491
+ }
1492
+
1493
+ .pr-helper-section-tag:hover { background: rgba(147, 51, 234, 0.15); }
1494
+
1495
+ .pr-helper-file-section-badge {
1496
+ display: inline-block;
1497
+ width: fit-content;
1498
+ font-size: 11px;
1499
+ color: #9333ea;
1500
+ background: rgba(147, 51, 234, 0.08);
1501
+ border-radius: 100px;
1502
+ padding: 2px 9px;
1503
+ }
1504
+
1505
+ /* ── History tab ── */
1506
+ .pr-helper-history-group-label {
1507
+ font-size: 10px;
1508
+ font-weight: 700;
1509
+ text-transform: uppercase;
1510
+ letter-spacing: 0.06em;
1511
+ color: #94a3b8;
1512
+ margin: 14px 0 6px;
1513
+ }
1514
+
1515
+ .pr-helper-history-item {
1516
+ display: flex;
1517
+ flex-direction: column;
1518
+ gap: 6px;
1519
+ padding: 12px 14px;
1520
+ border-radius: 6px;
1521
+ cursor: pointer;
1522
+ font-size: 13px;
1523
+ border: 1px solid var(--color-border-default, #d0d7de);
1524
+ margin-bottom: 8px;
1525
+ /* Stated rather than inherited: the surface behind this is now the grey canvas. */
1526
+ background: #ffffff;
1527
+ }
1528
+
1529
+ .pr-helper-history-item:hover { background: rgba(147, 51, 234, 0.06); }
1530
+
1531
+ /* Follow-ups hang off the review they were added to. Indented and left-ruled so the parent
1532
+ relationship reads structurally, and with no hover or cursor affordance — a follow-up carries no
1533
+ summary, risk or verdict, so a row that looked clickable would promise a view that cannot exist. */
1534
+ .pr-helper-history-followup {
1535
+ display: flex;
1536
+ flex-direction: column;
1537
+ gap: 4px;
1538
+ margin: -4px 0 8px 16px;
1539
+ padding: 8px 12px;
1540
+ border-left: 2px solid var(--color-border-default, #d0d7de);
1541
+ font-size: 12px;
1542
+ }
1543
+
1544
+ .pr-helper-history-followup-top,
1545
+ .pr-helper-history-followup-bottom {
1546
+ display: flex;
1547
+ align-items: center;
1548
+ justify-content: space-between;
1549
+ gap: 8px;
1550
+ }
1551
+
1552
+ .pr-helper-history-followup-tag {
1553
+ padding: 1px 6px;
1554
+ border-radius: 10px;
1555
+ font-size: 10px;
1556
+ text-transform: uppercase;
1557
+ letter-spacing: 0.03em;
1558
+ color: var(--color-fg-muted, #57606a);
1559
+ background: var(--color-neutral-subtle, rgba(175, 184, 193, 0.2));
1560
+ }
1561
+
1562
+ .pr-helper-history-followup-scope {
1563
+ color: var(--color-fg-default, #1f2328);
1564
+ overflow: hidden;
1565
+ text-overflow: ellipsis;
1566
+ white-space: nowrap;
1567
+ }
1568
+
1569
+ .pr-helper-history-followup-added {
1570
+ flex-shrink: 0;
1571
+ color: var(--color-fg-muted, #57606a);
1572
+ }
1573
+
1574
+ /* An investigation arriving borrows the follow-up row wholesale — same indent, same rule, same
1575
+ two-line geometry, same lack of any clickable affordance — because it is the same kind of thing:
1576
+ something added to the review above it, not a review of its own. Only the left edge and the agent
1577
+ name are purple, which is what investigations are coloured everywhere else, so the two kinds of
1578
+ event stay distinguishable at a glance without a second layout to keep in step. */
1579
+ .pr-helper-history-investigation-event {
1580
+ border-left-color: #9333ea;
1581
+ }
1582
+
1583
+ .pr-helper-history-investigation-event-agent {
1584
+ flex-shrink: 0;
1585
+ font-weight: 600;
1586
+ color: #7e22ce;
1587
+ }
1588
+
1589
+ .pr-helper-history-item-selected {
1590
+ border-color: #9333ea;
1591
+ background: rgba(147, 51, 234, 0.06);
1592
+ box-shadow: inset 0 0 0 1px #9333ea;
1593
+ }
1594
+
1595
+ .pr-helper-history-item-top,
1596
+ .pr-helper-history-item-bottom {
1597
+ display: flex;
1598
+ justify-content: space-between;
1599
+ align-items: center;
1600
+ gap: 8px;
1601
+ }
1602
+
1603
+ .pr-helper-history-model {
1604
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
1605
+ font-size: 12px;
1606
+ font-weight: 500;
1607
+ color: var(--color-fg-muted, #57606a);
1608
+ }
1609
+
1610
+ .pr-helper-history-risk {
1611
+ font-size: 11px;
1612
+ font-weight: 700;
1613
+ text-transform: uppercase;
1614
+ letter-spacing: 0.04em;
1615
+ padding: 3px 9px;
1616
+ border-radius: 100px;
1617
+ flex-shrink: 0;
1618
+ }
1619
+
1620
+ .pr-helper-history-issues,
1621
+ .pr-helper-history-options {
1622
+ font-size: 12px;
1623
+ color: var(--color-fg-muted, #57606a);
1624
+ }
1625
+
1626
+ .pr-helper-history-time {
1627
+ font-size: 12px;
1628
+ font-weight: 600;
1629
+ color: var(--color-fg-muted, #57606a);
1630
+ flex-shrink: 0;
1631
+ }
1632
+
1633
+ .pr-helper-history-status {
1634
+ font-size: 10px;
1635
+ font-weight: 700;
1636
+ text-transform: uppercase;
1637
+ letter-spacing: 0.04em;
1638
+ flex-shrink: 0;
1639
+ }
1640
+
1641
+ .pr-helper-history-status-generating .pr-helper-history-status { color: #9333ea; }
1642
+ .pr-helper-history-status-failed .pr-helper-history-status { color: #cf222e; }
1643
+
1644
+ /* ── Assistant tab ── */
1645
+ /* The panel is the scroll container for every other tab; the Assistant instead owns the full
1646
+ height and scrolls internally, so the composer can sit outside the scrolled region. Scoped
1647
+ with :has() so this only applies while the Assistant tab is mounted — no class to toggle
1648
+ in content.ts and nothing to unset when another tab renders over it. */
1649
+ .pr-helper-review-panel:has(> .pr-helper-chat) {
1650
+ display: flex;
1651
+ flex-direction: column;
1652
+ overflow: hidden;
1653
+ padding: 0;
1654
+ }
1655
+
1656
+ .pr-helper-chat {
1657
+ flex: 1;
1658
+ min-height: 0;
1659
+ display: flex;
1660
+ flex-direction: column;
1661
+ }
1662
+
1663
+ /* Only the conversation scrolls; the chips live in the pinned composer below it. */
1664
+ .pr-helper-chat-scroll {
1665
+ flex: 1;
1666
+ min-height: 0;
1667
+ overflow-y: auto;
1668
+ padding: 14px 16px;
1669
+ /* The Assistant's canvas. Its panel drops padding to zero and delegates scrolling here, so the
1670
+ recess has to be applied to this element rather than to the panel. */
1671
+ background: #f8fafc;
1672
+ }
1673
+
1674
+ .pr-helper-chat-composer {
1675
+ flex-shrink: 0;
1676
+ padding: 10px 16px 12px;
1677
+ background: #ffffff;
1678
+ border-top: 1px solid var(--color-border-default, #d0d7de);
1679
+ }
1680
+
1681
+ .pr-helper-chat-log {
1682
+ display: flex;
1683
+ flex-direction: column;
1684
+ gap: 8px;
1685
+ }
1686
+
1687
+ .pr-helper-chat-msg {
1688
+ font-size: 13px;
1689
+ padding: 10px 12px;
1690
+ border-radius: 8px;
1691
+ max-width: 90%;
1692
+ line-height: 1.5;
1693
+ }
1694
+
1695
+ /* Full width, unlike the user bubble: a reply now carries headings, lists and code blocks,
1696
+ and holding it to 90% only buys an indent that costs a wrapped line on every list item. */
1697
+ .pr-helper-chat-msg.role-assistant {
1698
+ background: rgba(147, 51, 234, 0.08);
1699
+ align-self: flex-start;
1700
+ max-width: 100%;
1701
+ }
1702
+
1703
+ .pr-helper-chat-msg.role-user {
1704
+ background: #0b0f17;
1705
+ color: #f8fafc;
1706
+ align-self: flex-end;
1707
+ }
1708
+
1709
+ /* Markdown lands on assistant bubbles only (see renderAssistantTab), so everything below is
1710
+ styled for the light bubble above and never has to survive the dark user one. Scoped to
1711
+ the bubble class so none of it leaks into the other tabs' prose. */
1712
+ .pr-helper-markdown > *:first-child { margin-top: 0; }
1713
+ .pr-helper-markdown > *:last-child { margin-bottom: 0; }
1714
+
1715
+ .pr-helper-markdown p { margin: 0 0 10px; }
1716
+
1717
+ /* h4-h6 in the markup — sized as section labels rather than document headings, because the
1718
+ bubble they sit in is 13px and the panel already owns the real heading hierarchy. */
1719
+ .pr-helper-markdown h4,
1720
+ .pr-helper-markdown h5,
1721
+ .pr-helper-markdown h6 {
1722
+ margin: 14px 0 6px;
1723
+ font-weight: 600;
1724
+ line-height: 1.35;
1725
+ color: var(--color-fg-default, #24292f);
1726
+ }
1727
+
1728
+ .pr-helper-markdown h4 { font-size: 13px; }
1729
+ .pr-helper-markdown h5 { font-size: 12.5px; }
1730
+ .pr-helper-markdown h6 { font-size: 12px; }
1731
+
1732
+ .pr-helper-markdown ul,
1733
+ .pr-helper-markdown ol {
1734
+ margin: 0 0 10px;
1735
+ padding-left: 18px;
1736
+ }
1737
+
1738
+ .pr-helper-markdown li + li { margin-top: 4px; }
1739
+
1740
+ /* The sidebar is narrow and a code block must never widen it — the block scrolls, the panel
1741
+ doesn't. min-width:0 lets the pre actually shrink inside the flex column above it. */
1742
+ .pr-helper-markdown pre {
1743
+ margin: 0 0 10px;
1744
+ padding: 10px 12px;
1745
+ min-width: 0;
1746
+ overflow-x: auto;
1747
+ background: var(--color-neutral-muted, rgba(175, 184, 193, 0.2));
1748
+ border-radius: 6px;
1749
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
1750
+ font-size: 12px;
1751
+ line-height: 1.45;
1752
+ }
1753
+
1754
+ /* ── The copy control on a fenced block ──
1755
+ The wrapper carries the block's bottom margin so the <pre> can sit flush against the button's
1756
+ corner, and so the surrounding stylesheets keep addressing one element per block.
1757
+
1758
+ Shown, not revealed on hover: the reason the button exists is that a snippet in a box that
1759
+ scrolls sideways is awkward to select, and a control you have to find by moving the pointer over
1760
+ the thing is barely better than the selection it replaces. */
1761
+ .pr-helper-code-block {
1762
+ position: relative;
1763
+ margin: 0 0 10px;
1764
+ }
1765
+
1766
+ .pr-helper-code-block > pre { margin: 0; }
1767
+
1768
+ .pr-helper-code-copy {
1769
+ position: absolute;
1770
+ top: 6px;
1771
+ right: 6px;
1772
+ padding: 2px 8px;
1773
+ border: 1px solid var(--color-border-default, #d0d7de);
1774
+ border-radius: 100px;
1775
+ background: var(--color-canvas-default, #fff);
1776
+ color: var(--color-fg-muted, #57606a);
1777
+ font-family: inherit;
1778
+ font-size: 11px;
1779
+ font-weight: 600;
1780
+ line-height: 18px;
1781
+ cursor: pointer;
1782
+ /* The block scrolls under it, so it needs to sit on its own ground rather than let code slide
1783
+ through it. */
1784
+ opacity: 0.9;
1785
+ }
1786
+
1787
+ .pr-helper-code-copy:hover {
1788
+ opacity: 1;
1789
+ border-color: #9333ea;
1790
+ color: #9333ea;
1791
+ }
1792
+
1793
+ .pr-helper-code-copy.copied {
1794
+ border-color: #1a7f37;
1795
+ color: #1a7f37;
1796
+ opacity: 1;
1797
+ }
1798
+
1799
+ /* Inline code is a chip; inside a block that background and padding would double up. */
1800
+ .pr-helper-markdown pre code {
1801
+ background: none;
1802
+ padding: 0;
1803
+ font-size: inherit;
1804
+ }
1805
+
1806
+ .pr-helper-markdown blockquote {
1807
+ margin: 0 0 10px;
1808
+ padding-left: 10px;
1809
+ border-left: 3px solid var(--color-border-default, #d0d7de);
1810
+ color: var(--color-fg-muted, #57606a);
1811
+ }
1812
+
1813
+ .pr-helper-markdown hr {
1814
+ margin: 12px 0;
1815
+ border: none;
1816
+ border-top: 1px solid var(--color-border-default, #d0d7de);
1817
+ }
1818
+
1819
+ .pr-helper-markdown a {
1820
+ color: #0969da;
1821
+ text-decoration: none;
1822
+ }
1823
+
1824
+ .pr-helper-markdown a:hover { text-decoration: underline; }
1825
+
1826
+ .pr-helper-chat-input-row {
1827
+ display: flex;
1828
+ gap: 6px;
1829
+ align-items: flex-end;
1830
+ }
1831
+
1832
+ .pr-helper-chat-input {
1833
+ flex: 1;
1834
+ padding: 8px 10px;
1835
+ border: 1px solid var(--color-border-default, #d0d7de);
1836
+ border-radius: 8px;
1837
+ font-size: 13px;
1838
+ font-family: inherit;
1839
+ outline: none;
1840
+ resize: none;
1841
+ max-height: 96px;
1842
+ line-height: 1.5;
1843
+ }
1844
+
1845
+ .pr-helper-chat-input:focus {
1846
+ border-color: #9333ea;
1847
+ box-shadow: 0 0 0 2px rgba(147, 51, 234, 0.15);
1848
+ }
1849
+
1850
+ /* Takes the textarea's place while a reply is in flight. Mirrors its box model exactly —
1851
+ same padding, a transparent border standing in for the real one, and a 1.5-line content
1852
+ box — so the swap never changes the composer's height and the Cancel button beside it
1853
+ doesn't shift. */
1854
+ .pr-helper-chat-thinking {
1855
+ flex: 1;
1856
+ display: flex;
1857
+ align-items: center;
1858
+ justify-content: center;
1859
+ box-sizing: content-box;
1860
+ padding: 8px 10px;
1861
+ border: 1px solid transparent;
1862
+ font-size: 13px;
1863
+ line-height: 1.5;
1864
+ min-height: 1.5em;
1865
+ }
1866
+
1867
+ /* An author display:flex would otherwise beat the UA stylesheet's [hidden] { display: none }. */
1868
+ .pr-helper-chat-thinking[hidden] { display: none; }
1869
+
1870
+ /* The dots are shared with the Overview's generating state, where they sit in a centred
1871
+ column with the status copy below them — that bottom margin has no place in a composer row. */
1872
+ .pr-helper-chat-thinking .pr-helper-thinking-dots { margin: 0; }
1873
+
1874
+ .pr-helper-chat-send {
1875
+ padding: 8px 14px;
1876
+ border: none;
1877
+ border-radius: 8px;
1878
+ background: #9333ea;
1879
+ color: #ffffff;
1880
+ font-size: 13px;
1881
+ font-weight: 600;
1882
+ font-family: inherit;
1883
+ cursor: pointer;
1884
+ }
1885
+
1886
+ .pr-helper-chat-send:hover { background: #7c3aed; }
1887
+
1888
+ /* Same button, destructive colour while a request is in flight — clicking it aborts the
1889
+ reply rather than sending anything. */
1890
+ .pr-helper-chat-send-cancel { background: #cf222e; }
1891
+ .pr-helper-chat-send-cancel:hover { background: #a40e26; }
1892
+
1893
+ /* The chips sit in the composer, above the input row. Capped and independently scrollable so
1894
+ a session with many section prompts can never push the input off the bottom of the panel. */
1895
+ .pr-helper-chat-suggestions { margin-bottom: 8px; }
1896
+
1897
+ .pr-helper-chat-suggestions-list {
1898
+ display: flex;
1899
+ flex-direction: column;
1900
+ gap: 8px;
1901
+ max-height: 30vh;
1902
+ overflow-y: auto;
1903
+ }
1904
+
1905
+ .pr-helper-chat-suggestions-list[hidden] { display: none; }
1906
+
1907
+ /* Deliberately quieter than the chips it reveals: once the reviewer is typing their own
1908
+ questions, this is a door they may never open again, not a call to action. */
1909
+ .pr-helper-suggestions-toggle {
1910
+ display: flex;
1911
+ align-items: center;
1912
+ gap: 5px;
1913
+ padding: 0;
1914
+ border: none;
1915
+ background: none;
1916
+ font-size: 11px;
1917
+ font-weight: 700;
1918
+ font-family: inherit;
1919
+ text-transform: uppercase;
1920
+ letter-spacing: 0.06em;
1921
+ color: #94a3b8;
1922
+ cursor: pointer;
1923
+ }
1924
+
1925
+ .pr-helper-suggestions-toggle:hover { color: #9333ea; }
1926
+
1927
+ .pr-helper-suggestions-toggle::before {
1928
+ content: '';
1929
+ width: 0;
1930
+ height: 0;
1931
+ border-left: 4px solid currentColor;
1932
+ border-top: 3.5px solid transparent;
1933
+ border-bottom: 3.5px solid transparent;
1934
+ transition: transform 0.15s ease;
1935
+ }
1936
+
1937
+ .pr-helper-suggestions-toggle[aria-expanded='true']::before { transform: rotate(90deg); }
1938
+
1939
+ /* Open, the toggle is a label for the list right beneath it rather than a lone control. */
1940
+ .pr-helper-suggestions-toggle[aria-expanded='true'] { margin-bottom: 8px; }
1941
+
1942
+ .pr-helper-quick-prompts {
1943
+ display: flex;
1944
+ flex-wrap: wrap;
1945
+ gap: 6px;
1946
+ }
1947
+
1948
+ .pr-helper-quick-prompt-button {
1949
+ font-size: 12px;
1950
+ font-weight: 600;
1951
+ font-family: inherit;
1952
+ padding: 6px 12px;
1953
+ border-radius: 100px;
1954
+ border: 1px solid var(--color-border-default, #d0d7de);
1955
+ background: transparent;
1956
+ color: var(--color-fg-default, #24292f);
1957
+ cursor: pointer;
1958
+ }
1959
+
1960
+ .pr-helper-quick-prompt-button:hover { border-color: #9333ea; color: #9333ea; }
1961
+
1962
+ .pr-helper-quick-prompts-section-group,
1963
+ .pr-helper-quick-prompts-saved-group { display: contents; }
1964
+
1965
+ /* ── The reviewer's own prompts ──
1966
+ The chip and its edit control read as one object, so the border and the hover colour belong to
1967
+ the chip and not to either half. When each half carried its own, they drew themselves in
1968
+ different greys and only the side under the pointer lit up, which split one prompt into two
1969
+ controls that happened to be touching. */
1970
+ .pr-helper-saved-prompt-chip {
1971
+ display: inline-flex;
1972
+ align-items: stretch;
1973
+ /* The same pill as every other prompt button. It was 6px, so a saved prompt was round on the
1974
+ left where the button's own radius showed through and square on the right where this one did,
1975
+ which reads as an unfinished chip rather than as a different kind of one. */
1976
+ border-radius: 100px;
1977
+ border: 1px solid var(--color-border-default, #d0d7de);
1978
+ color: var(--color-fg-default, #24292f);
1979
+ overflow: hidden;
1980
+ }
1981
+
1982
+ .pr-helper-saved-prompt-chip:hover { border-color: #9333ea; color: #9333ea; }
1983
+
1984
+ /* Both halves are surfaces on the chip: no border of their own, no radius of their own, and the
1985
+ colour they draw in is whatever the chip is currently showing. */
1986
+ .pr-helper-saved-prompt-chip .pr-helper-saved-prompt-use,
1987
+ .pr-helper-saved-prompt-chip .pr-helper-saved-prompt-edit {
1988
+ border: 0;
1989
+ border-radius: 0;
1990
+ background: transparent;
1991
+ color: inherit;
1992
+ }
1993
+
1994
+ .pr-helper-saved-prompt-chip .pr-helper-saved-prompt-use:hover { color: inherit; }
1995
+
1996
+ .pr-helper-saved-prompt-edit {
1997
+ /* Wider on the right than the left: the pencil sits inside a curve now, and even padding would
1998
+ crowd it against the arc. */
1999
+ padding: 0 11px 0 7px;
2000
+ font: inherit;
2001
+ font-size: 11px;
2002
+ opacity: 0.55;
2003
+ cursor: pointer;
2004
+ }
2005
+ .pr-helper-saved-prompt-edit:hover { opacity: 1; }
2006
+
2007
+ /* ── The prompt editor ──
2008
+ A name above a paragraph. The two fields were bare boxes with a hairline around them, which
2009
+ read as a form someone had not finished rather than as the place a prompt is written. */
2010
+ .pr-helper-saved-prompt-label {
2011
+ display: block;
2012
+ margin: 0 0 6px;
2013
+ font-size: 11px;
2014
+ font-weight: 600;
2015
+ letter-spacing: 0.03em;
2016
+ text-transform: uppercase;
2017
+ color: var(--color-fg-muted, #57606a);
2018
+ }
2019
+
2020
+ /* One rule for both, because a name and a prompt are the same kind of thing to type into and two
2021
+ sets of numbers is how they drift apart. Filled rather than outlined: on a white card an inset
2022
+ field says "type here" without needing a heavier border to do it. */
2023
+ .pr-helper-saved-prompt-name,
2024
+ .pr-helper-saved-prompt-text {
2025
+ width: 100%;
2026
+ box-sizing: border-box;
2027
+ padding: 9px 11px;
2028
+ border: 1px solid rgba(0, 0, 0, 0.12);
2029
+ border-radius: 8px;
2030
+ background: #f6f8fa;
2031
+ color: #1f2328;
2032
+ font: inherit;
2033
+ font-size: 13px;
2034
+ line-height: 1.5;
2035
+ transition: border-color 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
2036
+ }
2037
+
2038
+ .pr-helper-saved-prompt-name::placeholder,
2039
+ .pr-helper-saved-prompt-text::placeholder { color: #8c959f; }
2040
+
2041
+ .pr-helper-saved-prompt-name:hover,
2042
+ .pr-helper-saved-prompt-text:hover { border-color: rgba(0, 0, 0, 0.22); }
2043
+
2044
+ /* The field lifts to white on focus and takes the product's purple, so the one being typed into
2045
+ is obvious without moving anything. outline: none is safe here only because the ring below
2046
+ replaces it; it is a visible focus indicator, not the absence of one. */
2047
+ .pr-helper-saved-prompt-name:focus,
2048
+ .pr-helper-saved-prompt-text:focus {
2049
+ outline: none;
2050
+ background: #ffffff;
2051
+ border-color: #9333ea;
2052
+ box-shadow: 0 0 0 3px rgba(147, 51, 234, 0.16);
2053
+ }
2054
+
2055
+ .pr-helper-saved-prompt-name { margin-bottom: 16px; }
2056
+
2057
+ .pr-helper-saved-prompt-text {
2058
+ min-height: 132px;
2059
+ resize: vertical;
2060
+ }
2061
+
2062
+ /* The field that was rejected says so itself. An error message under a form with three controls
2063
+ leaves the reviewer to work out which one it is about. */
2064
+ .pr-helper-saved-prompt-name[aria-invalid='true'],
2065
+ .pr-helper-saved-prompt-text[aria-invalid='true'] {
2066
+ border-color: #cf222e;
2067
+ background: rgba(207, 34, 46, 0.04);
2068
+ }
2069
+
2070
+ .pr-helper-saved-prompt-name[aria-invalid='true']:focus,
2071
+ .pr-helper-saved-prompt-text[aria-invalid='true']:focus {
2072
+ border-color: #cf222e;
2073
+ box-shadow: 0 0 0 3px rgba(207, 34, 46, 0.14);
2074
+ }
2075
+
2076
+ .pr-helper-saved-prompt-error {
2077
+ display: flex;
2078
+ align-items: baseline;
2079
+ gap: 6px;
2080
+ margin: 10px 0 0;
2081
+ font-size: 12px;
2082
+ line-height: 1.5;
2083
+ color: #cf222e;
2084
+ }
2085
+
2086
+ .pr-helper-saved-prompt-error::before {
2087
+ content: '!';
2088
+ flex: none;
2089
+ width: 15px;
2090
+ height: 15px;
2091
+ border-radius: 50%;
2092
+ background: #cf222e;
2093
+ color: #ffffff;
2094
+ font-size: 10px;
2095
+ font-weight: 700;
2096
+ line-height: 15px;
2097
+ text-align: center;
2098
+ }
2099
+
2100
+ .pr-helper-quick-prompts-divider {
2101
+ font-size: 10px;
2102
+ font-weight: 700;
2103
+ text-transform: uppercase;
2104
+ letter-spacing: 0.06em;
2105
+ color: #94a3b8;
2106
+ margin-bottom: 6px;
2107
+ }
2108
+
2109
+ /* display:contents above, so the divider and its chip row become items of the suggestions
2110
+ stack directly — one gap governs the whole footer instead of a margin per group. */
2111
+
2112
+ /* ── Header — dark brand strip ── */
2113
+ .pr-helper-sidebar-header {
2114
+ background: #0b0f17;
2115
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
2116
+ padding: 12px 16px 14px 20px;
2117
+ flex-shrink: 0;
2118
+ position: relative;
2119
+ }
2120
+
2121
+ .pr-helper-sidebar-header::before {
2122
+ content: '';
2123
+ position: absolute;
2124
+ left: 0;
2125
+ top: 0;
2126
+ bottom: 0;
2127
+ width: 3px;
2128
+ background: #9333ea;
2129
+ border-radius: 0 2px 2px 0;
2130
+ }
2131
+
2132
+ .pr-helper-header-top {
2133
+ display: flex;
2134
+ justify-content: space-between;
2135
+ align-items: center;
2136
+ margin-bottom: 8px;
2137
+ }
2138
+
2139
+ /* The wordmark, two-tone: "pr" and "buddy" in the accent purple, "review" in the neutral ink.
2140
+ This header is dark (#0b0f17 above), so the ink is near-white. Values are literal rather than
2141
+ tokens because this sheet is injected into GitHub, where our :root never applies. */
2142
+ .pr-helper-brand-text {
2143
+ font-family: 'Comfortaa', ui-rounded, 'SF Pro Rounded', 'Segoe UI Rounded', system-ui, sans-serif;
2144
+ font-size: 14px;
2145
+ font-weight: 700;
2146
+ letter-spacing: 0;
2147
+ text-transform: none;
2148
+ color: #f8fafc;
2149
+ }
2150
+
2151
+ .pr-helper-brand-text .pr-helper-wordmark-accent { color: #a855f7; }
2152
+
2153
+ /* Anchors the bookmark so it is a sibling of the title rather than something the title has to
2154
+ flow around. Matches .pr-meta in popup.css, which solves the identical problem. */
2155
+ .pr-helper-pr-headline {
2156
+ position: relative;
2157
+ margin-bottom: 6px;
2158
+ }
2159
+
2160
+ /* Two lines, clamped — not one line ellipsised as before. A PR title carrying a ticket prefix
2161
+ ("PR #38921 · [INF-3765] …") spends most of its first line before reaching any words that
2162
+ distinguish it, so a single line truncated at the sidebar's width told the reviewer almost
2163
+ nothing. The right padding reserves the bookmark's column so the second line clamps beside it. */
2164
+ .pr-helper-pr-title {
2165
+ font-size: 14px;
2166
+ font-weight: 600;
2167
+ color: #f8fafc;
2168
+ line-height: 1.35;
2169
+ padding-right: 32px;
2170
+ display: -webkit-box;
2171
+ -webkit-box-orient: vertical;
2172
+ -webkit-line-clamp: 2;
2173
+ overflow: hidden;
2174
+ }
2175
+
2176
+ /* The popup's .pr-meta-stats: byline, then size, as plain coloured text rather than pills. The
2177
+ pills these used to be competed with the severity badges further down the sidebar, which are the
2178
+ ones that actually mean something. */
2179
+ .pr-helper-header-meta {
2180
+ display: flex;
2181
+ align-items: center;
2182
+ gap: 10px;
2183
+ flex-wrap: wrap;
2184
+ font-size: 12px;
2185
+ color: #94a3b8;
2186
+ }
2187
+
2188
+ .pr-helper-additions-stat { color: #4ade80; }
2189
+ .pr-helper-deletions-stat { color: #f87171; }
2190
+
2191
+ /* ── Save for later (header bookmark) ──
2192
+ The popup's .pr-meta-save, positioned against the headline for the same reason: it acts on the
2193
+ PR the title names. Saved state is the filled/outline path swap plus the brand colour, never
2194
+ colour alone — see the aria-pressed written alongside it in paintSavedForLaterButton. */
2195
+ .pr-helper-pr-save {
2196
+ position: absolute;
2197
+ top: -2px;
2198
+ right: 0;
2199
+ width: 26px;
2200
+ height: 26px;
2201
+ padding: 0;
2202
+ border: none;
2203
+ background: transparent;
2204
+ color: #94a3b8;
2205
+ border-radius: 6px;
2206
+ cursor: pointer;
2207
+ display: flex;
2208
+ align-items: center;
2209
+ justify-content: center;
2210
+ transition: background 0.15s, color 0.15s;
2211
+ }
2212
+
2213
+ .pr-helper-pr-save svg { fill: currentColor; }
2214
+ .pr-helper-pr-save:hover { background: rgba(255, 255, 255, 0.08); color: #f8fafc; }
2215
+
2216
+ .pr-helper-bookmark-filled { display: none; }
2217
+ .pr-helper-pr-save.saved { color: #a855f7; }
2218
+ .pr-helper-pr-save.saved .pr-helper-bookmark-outline { display: none; }
2219
+ .pr-helper-pr-save.saved .pr-helper-bookmark-filled { display: inline; }
2220
+ .pr-helper-pr-save.saved:hover { background: rgba(168, 85, 247, 0.16); color: #a855f7; }
2221
+
2222
+ /* The byline leads the meta row. Tighter gap than the row's own 6px so the avatar and login read
2223
+ as one unit rather than as two more badges, and no pill treatment: it names a person, while the
2224
+ badges beside it measure the diff. */
2225
+ .pr-helper-pr-author {
2226
+ display: flex;
2227
+ align-items: center;
2228
+ gap: 5px;
2229
+ min-width: 0;
2230
+ max-width: 45%;
2231
+ flex: 0 1 auto;
2232
+ }
2233
+
2234
+ /* Scoped rather than a bare `.hidden`: this sheet is injected into github.com, where an
2235
+ unprefixed class would collide with GitHub's own. Same precedent as the footer's rule above. */
2236
+ .pr-helper-pr-author.hidden { display: none; }
2237
+
2238
+ /* Sized to the 11px meta line, not to the 28px Saved Reviews row: here the avatar is an accent on
2239
+ a line of text, not the leading element of a list row. Colours are literal because this sheet
2240
+ is injected into GitHub, where our :root tokens never apply. */
2241
+ .pr-helper-pr-author-avatar {
2242
+ position: relative;
2243
+ width: 16px;
2244
+ height: 16px;
2245
+ border-radius: 50%;
2246
+ flex-shrink: 0;
2247
+ background: rgba(255, 255, 255, 0.08);
2248
+ display: flex;
2249
+ align-items: center;
2250
+ justify-content: center;
2251
+ font-size: 9px;
2252
+ font-weight: 600;
2253
+ line-height: 1;
2254
+ color: #94a3b8;
2255
+ text-transform: uppercase;
2256
+ overflow: hidden;
2257
+ }
2258
+
2259
+ /* Covers the initial underneath, so a failed load needs no more than removing this element. */
2260
+ .pr-helper-pr-author-avatar img {
2261
+ position: absolute;
2262
+ inset: 0;
2263
+ width: 100%;
2264
+ height: 100%;
2265
+ border-radius: 50%;
2266
+ object-fit: cover;
2267
+ }
2268
+
2269
+ .pr-helper-pr-author-name {
2270
+ font-size: 11px;
2271
+ color: #cbd5e1;
2272
+ overflow: hidden;
2273
+ text-overflow: ellipsis;
2274
+ white-space: nowrap;
2275
+ }
2276
+
2277
+ .pr-helper-pr-branch {
2278
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
2279
+ font-size: 11px;
2280
+ color: #64748b;
2281
+ background: rgba(255, 255, 255, 0.05);
2282
+ border: 1px solid rgba(255, 255, 255, 0.08);
2283
+ border-radius: 4px;
2284
+ padding: 2px 7px;
2285
+ white-space: nowrap;
2286
+ overflow: hidden;
2287
+ text-overflow: ellipsis;
2288
+ /* Was a flat 160px cap, which clipped branch names well short of the header's actual
2289
+ width. flex-grow:0 keeps the pill hugging its own text like the FILES badge next to
2290
+ it, rather than stretching to fill the row's leftover space — it only shrinks (and
2291
+ then ellipsizes) once the row genuinely runs out of room. min-width:0 is required for
2292
+ a flex item's text-overflow to ever kick in — without it the row would just overflow
2293
+ instead of truncating. */
2294
+ flex: 0 1 auto;
2295
+ min-width: 0;
2296
+ }
2297
+
2298
+ /* Workspace controls, grouped: Back/Forward belong with Hide rather than with the branding,
2299
+ which is also what says they act on the sidebar and not on the page under it. */
2300
+ .pr-helper-header-actions {
2301
+ display: flex;
2302
+ align-items: center;
2303
+ gap: 2px;
2304
+ flex-shrink: 0;
2305
+ }
2306
+
2307
+ /* Separates navigation from Hide so three icons in a row don't read as one control group. */
2308
+ .pr-helper-header-actions-divider {
2309
+ width: 1px;
2310
+ height: 14px;
2311
+ background: rgba(255, 255, 255, 0.12);
2312
+ margin: 0 4px;
2313
+ }
2314
+
2315
+ .pr-helper-collapse-button,
2316
+ .pr-helper-nav-button {
2317
+ padding: 4px;
2318
+ background: none;
2319
+ border: none;
2320
+ cursor: pointer;
2321
+ color: #64748b;
2322
+ border-radius: 4px;
2323
+ display: flex;
2324
+ align-items: center;
2325
+ font-family: inherit;
2326
+ transition: color 0.15s;
2327
+ flex-shrink: 0;
2328
+ }
2329
+
2330
+ .pr-helper-collapse-button:hover,
2331
+ .pr-helper-nav-button:not(:disabled):hover { color: #f8fafc; }
2332
+
2333
+ .pr-helper-collapse-button svg,
2334
+ .pr-helper-nav-button svg { width: 16px; height: 16px; }
2335
+
2336
+ /* The real disabled attribute here, unlike the review tabs, which use aria-disabled
2337
+ specifically to keep their explanatory tooltip alive through pointer events. A Back button
2338
+ with nowhere to go has nothing to explain, so swallowing the events is the better trade and
2339
+ comes with the right semantics for assistive tech for free. */
2340
+ .pr-helper-nav-button:disabled {
2341
+ color: #334155;
2342
+ cursor: default;
2343
+ }
2344
+
2345
+ /* The Files tab (search + list) owns the panel's full height and scrolls internally, the
2346
+ same way the Assistant tab does — see the :has() rule above it — so the search bar can
2347
+ stay pinned above a scrolling list instead of scrolling away with it. Scoped with
2348
+ :has() rather than a class so nothing needs unsetting when another tab renders over it. */
2349
+ .pr-helper-review-panel:has(> .pr-helper-search-container) {
2350
+ display: flex;
2351
+ flex-direction: column;
2352
+ overflow: hidden;
2353
+ padding: 0;
2354
+ }
2355
+
2356
+ /* ── Search ── */
2357
+ .pr-helper-search-container {
2358
+ flex-shrink: 0;
2359
+ padding: 16px 16px 14px;
2360
+ border-bottom: 1px solid var(--color-border-default, #d0d7de);
2361
+ background: #ffffff;
2362
+ }
2363
+
2364
+ /* The count's positioning anchor, and the reason it exists: .pr-helper-search-container has
2365
+ asymmetric padding, so an absolute count anchored to it centres on the container's box rather
2366
+ than the input's and lands on the input's right border. This box *is* the input's box. */
2367
+ .pr-helper-search-field {
2368
+ position: relative;
2369
+ }
2370
+
2371
+ .pr-helper-search-input {
2372
+ width: 100%;
2373
+ padding: 10px 36px 10px 12px;
2374
+ border: 1px solid var(--color-border-default, #d0d7de);
2375
+ border-radius: 8px;
2376
+ font-size: 14px;
2377
+ line-height: 20px;
2378
+ color: var(--color-fg-default, #24292f);
2379
+ background: #ffffff;
2380
+ box-sizing: border-box;
2381
+ font-family: inherit;
2382
+ outline: none;
2383
+ }
2384
+
2385
+ .pr-helper-search-input:focus {
2386
+ border-color: #9333ea;
2387
+ box-shadow: 0 0 0 2px rgba(147, 51, 234, 0.15);
2388
+ }
2389
+
2390
+ .pr-helper-search-count {
2391
+ position: absolute;
2392
+ /* Inside the 36px right gutter the input reserves for it (padding-right), clear of the border
2393
+ rather than sitting on it. */
2394
+ right: 12px;
2395
+ top: 50%;
2396
+ transform: translateY(-50%);
2397
+ font-size: 11px;
2398
+ color: var(--color-fg-muted, #57606a);
2399
+ display: none;
2400
+ }
2401
+
2402
+ .pr-helper-no-results {
2403
+ padding: 24px 16px;
2404
+ text-align: center;
2405
+ color: var(--color-fg-muted, #57606a);
2406
+ font-size: 14px;
2407
+ }
2408
+
2409
+ /* ── File list ── */
2410
+ .pr-helper-file-list {
2411
+ flex: 1;
2412
+ min-height: 0;
2413
+ overflow-y: auto;
2414
+ padding: 6px 0;
2415
+ /* Same canvas as the other two scroll areas. The rows below stay white, so the grey shows
2416
+ through at the group headers and separates the groups without adding a rule between them. */
2417
+ background: #f8fafc;
2418
+ }
2419
+
2420
+ .pr-helper-file-item {
2421
+ position: relative;
2422
+ padding: 16px;
2423
+ display: flex;
2424
+ align-items: flex-start;
2425
+ cursor: pointer;
2426
+ color: var(--color-fg-default, #24292f);
2427
+ text-decoration: none;
2428
+ border-left: 2px solid transparent;
2429
+ background: #ffffff;
2430
+ transition: background 0.12s, border-left-color 0.12s;
2431
+ box-sizing: border-box;
2432
+ }
2433
+
2434
+ .pr-helper-file-item:hover {
2435
+ background: rgba(147, 51, 234, 0.04);
2436
+ border-left-color: #9333ea;
2437
+ }
2438
+
2439
+ .pr-helper-file-content {
2440
+ flex: 1;
2441
+ min-width: 0;
2442
+ display: flex;
2443
+ flex-direction: column;
2444
+ /* The gap between filename, badges and description — a single flex gap since all three
2445
+ are direct children of this box, rather than separate margins on each that could
2446
+ drift out of step with one another. */
2447
+ gap: 6px;
2448
+ }
2449
+
2450
+ /* Review context layered onto the raw file rows. The Files tab used to render either GitHub's
2451
+ file browser or a list of AI file summaries, never both; now it's one list carrying GitHub's
2452
+ facts (path, +/- counts, search) and the review's meaning together. These two rules are
2453
+ additive — a file the review didn't cover simply omits them and the row looks as it always
2454
+ did, which is also what every row looks like before a review has been generated. */
2455
+ .pr-helper-file-review-badges {
2456
+ display: flex;
2457
+ align-items: center;
2458
+ gap: 6px;
2459
+ flex-wrap: wrap;
2460
+ }
2461
+
2462
+ /* Borrows the History tab's group-label treatment (.pr-helper-history-group-label) — both
2463
+ partition a flat list into labelled runs, so they should read the same way. */
2464
+ .pr-helper-file-group-header {
2465
+ font-size: 10px;
2466
+ font-weight: 600;
2467
+ text-transform: uppercase;
2468
+ letter-spacing: 0.4px;
2469
+ color: var(--color-fg-muted, #57606a);
2470
+ /* Left inset matches a row's text, not the panel edge: .pr-helper-file-item is 16px of padding
2471
+ inside a 2px left border, so its filename starts 18px in. A header at 2px reads as belonging
2472
+ to the panel rather than to the rows it labels. */
2473
+ padding: 12px 16px 4px 18px;
2474
+ display: flex;
2475
+ align-items: center;
2476
+ justify-content: space-between;
2477
+ gap: 8px;
2478
+ }
2479
+
2480
+ /* Quieter than .pr-helper-action-btn — this acts on a whole group, so it must not out-shout the
2481
+ per-row actions or read as the primary thing to do on the tab. */
2482
+ .pr-helper-file-group-action {
2483
+ font-family: inherit;
2484
+ font-size: 11px;
2485
+ letter-spacing: 0;
2486
+ text-transform: none;
2487
+ padding: 2px 8px;
2488
+ border-radius: 4px;
2489
+ background: #ffffff;
2490
+ border: 1px solid var(--color-border-default, #d0d7de);
2491
+ color: #9333ea;
2492
+ cursor: pointer;
2493
+ white-space: nowrap;
2494
+ }
2495
+
2496
+ .pr-helper-file-group-action:hover:not(:disabled) {
2497
+ border-color: #9333ea;
2498
+ background: #faf5ff;
2499
+ }
2500
+
2501
+ .pr-helper-file-group-action:disabled {
2502
+ opacity: 0.5;
2503
+ cursor: default;
2504
+ }
2505
+
2506
+ .pr-helper-file-group-header:first-child { padding-top: 2px; }
2507
+
2508
+ /* Amber rather than the section badge's purple: purple means "the review has something to say
2509
+ about this file", and this badge means the exact opposite — nothing could read it. Shares the
2510
+ hue with .pr-helper-file-stat-issues, the row's other "needs your attention" signal. */
2511
+ .pr-helper-file-badge-unavailable {
2512
+ display: inline-block;
2513
+ width: fit-content;
2514
+ font-size: 11px;
2515
+ color: #9a6700;
2516
+ background: rgba(154, 103, 0, 0.1);
2517
+ border-radius: 100px;
2518
+ padding: 2px 9px;
2519
+ }
2520
+
2521
+ .pr-helper-file-review-focus {
2522
+ font-size: 12px;
2523
+ line-height: 1.5;
2524
+ color: var(--color-fg-muted, #57606a);
2525
+ }
2526
+
2527
+ /* ── The findings themselves, under the file row ──
2528
+ Indented behind a hairline rather than boxed: these are pointers into the Issues and Questions
2529
+ tabs, and giving each one a card would make the row look like the place the finding lives. The
2530
+ rule reads as "belonging to the file above" without competing with the real cards. */
2531
+ .pr-helper-file-findings {
2532
+ display: flex;
2533
+ flex-direction: column;
2534
+ gap: 6px;
2535
+ margin-top: 6px;
2536
+ padding-left: 8px;
2537
+ border-left: 2px solid var(--color-border-muted, #d8dee4);
2538
+ }
2539
+
2540
+ .pr-helper-file-finding-heading {
2541
+ margin: 0 0 3px;
2542
+ font-size: 11px;
2543
+ font-weight: 600;
2544
+ text-transform: uppercase;
2545
+ letter-spacing: 0.03em;
2546
+ color: var(--color-fg-muted, #57606a);
2547
+ }
2548
+
2549
+ .pr-helper-file-finding-list {
2550
+ list-style: none;
2551
+ margin: 0;
2552
+ padding: 0;
2553
+ display: flex;
2554
+ flex-direction: column;
2555
+ gap: 2px;
2556
+ }
2557
+
2558
+ .pr-helper-file-finding {
2559
+ display: flex;
2560
+ align-items: baseline;
2561
+ gap: 6px;
2562
+ width: 100%;
2563
+ text-align: left;
2564
+ background: none;
2565
+ border: 0;
2566
+ border-radius: 5px;
2567
+ padding: 3px 4px;
2568
+ margin: 0;
2569
+ font: inherit;
2570
+ font-size: 12px;
2571
+ line-height: 1.45;
2572
+ color: var(--color-fg-default, #1f2328);
2573
+ cursor: pointer;
2574
+ }
2575
+
2576
+ .pr-helper-file-finding:hover { background: rgba(175, 184, 193, 0.18); }
2577
+
2578
+ .pr-helper-file-finding:focus-visible {
2579
+ outline: 2px solid var(--color-accent-fg, #0969da);
2580
+ outline-offset: -1px;
2581
+ }
2582
+
2583
+ /* Clamped in CSS, never in the markup: the full title stays in the DOM for find-in-page and in
2584
+ the title attribute for hover. Two lines is enough for the long, specific finding titles that
2585
+ are worth reading and short enough that five findings still fit on a screen. */
2586
+ .pr-helper-file-finding-label {
2587
+ flex: 1;
2588
+ display: -webkit-box;
2589
+ -webkit-line-clamp: 2;
2590
+ -webkit-box-orient: vertical;
2591
+ overflow: hidden;
2592
+ }
2593
+
2594
+ /* Severity as a dot, not a pill. The Issues tab spells the word out on a card the reviewer has
2595
+ arrived at to read; here the finding is one line in a list under a filename, and four uppercase
2596
+ pills down the left edge would out-shout the titles they are grading. Colour carries the rank,
2597
+ the tooltip carries the meaning, and the word itself is one click away. */
2598
+ .pr-helper-file-finding-dot {
2599
+ flex-shrink: 0;
2600
+ width: 7px;
2601
+ height: 7px;
2602
+ border-radius: 50%;
2603
+ align-self: center;
2604
+ background: var(--pr-helper-pill-neutral-fg);
2605
+ }
2606
+
2607
+ .pr-helper-file-finding-dot.pr-helper-severity-critical { background: var(--pr-helper-pill-danger-fg); }
2608
+ .pr-helper-file-finding-dot.pr-helper-severity-major { background: var(--pr-helper-pill-warning-fg); }
2609
+ .pr-helper-file-finding-dot.pr-helper-severity-minor { background: var(--pr-helper-pill-info-fg); }
2610
+ .pr-helper-file-finding-dot.pr-helper-severity-nit { background: var(--pr-helper-pill-neutral-fg); }
2611
+
2612
+ /* Struck through, not hidden: a refuted finding is retired but stays on the record, and the
2613
+ reviewer may well want to open it to read why. The strike is on the label alone so the pill
2614
+ stays readable. */
2615
+ .pr-helper-file-finding-is-refuted .pr-helper-file-finding-label {
2616
+ text-decoration: line-through;
2617
+ color: var(--color-fg-muted, #57606a);
2618
+ }
2619
+
2620
+ .pr-helper-file-finding-is-refuted .pr-helper-file-finding-dot { opacity: 0.4; }
2621
+
2622
+ .pr-helper-file-finding-refuted {
2623
+ flex-shrink: 0;
2624
+ align-self: center;
2625
+ font-size: 10px;
2626
+ color: var(--color-fg-muted, #57606a);
2627
+ background: rgba(175, 184, 193, 0.22);
2628
+ border-radius: 100px;
2629
+ padding: 1px 7px;
2630
+ }
2631
+
2632
+ .pr-helper-file-finding-followup {
2633
+ flex-shrink: 0;
2634
+ align-self: center;
2635
+ font-size: 10px;
2636
+ color: var(--color-fg-muted, #57606a);
2637
+ background: rgba(175, 184, 193, 0.22);
2638
+ border-radius: 100px;
2639
+ padding: 1px 7px;
2640
+ }
2641
+
2642
+ /* Same treatment and duration as .pr-helper-guide-section-highlight, which is the other "you asked
2643
+ to be taken here, this is the one" flash in the sidebar. Blue rather than that one's green: green
2644
+ there means completed, and a finding you have just jumped to is not. */
2645
+ .pr-helper-finding-highlight {
2646
+ background: rgba(9, 105, 218, 0.14);
2647
+ border-color: rgba(9, 105, 218, 0.45);
2648
+ }
2649
+
2650
+
2651
+ .pr-helper-file-stats {
2652
+ font-size: 12px;
2653
+ color: var(--color-fg-muted, #57606a);
2654
+ white-space: nowrap;
2655
+ padding: 2px 8px;
2656
+ background: rgba(175, 184, 193, 0.15);
2657
+ border-radius: 10px;
2658
+ flex-shrink: 0;
2659
+ align-self: flex-start;
2660
+ transition: opacity 0.12s;
2661
+ }
2662
+
2663
+ .pr-helper-file-item:hover .pr-helper-file-stats { opacity: 0; }
2664
+
2665
+ /* ── Hover-reveal action buttons ── */
2666
+ .pr-helper-file-actions {
2667
+ position: absolute;
2668
+ right: 12px;
2669
+ top: 50%;
2670
+ transform: translateY(-50%);
2671
+ display: flex;
2672
+ gap: 4px;
2673
+ opacity: 0;
2674
+ pointer-events: none;
2675
+ transition: opacity 0.12s;
2676
+ }
2677
+
2678
+ /* Opaque too, and for the same reason as the buttons: making only the buttons solid still lets
2679
+ text show through the gaps between them. The tray occludes as one block.
2680
+
2681
+ The colour tracks the row's own state rather than being a flat white, because the row tints on
2682
+ hover — #fbf7fe is that 4% purple composited over white, precomputed because a translucent
2683
+ overlay on a translucent row would double the tint.
2684
+
2685
+ Bordered and shadowed, because the tray is not part of the row: it is absolutely positioned over
2686
+ the top of it, occluding whatever text is underneath. Without an edge, three buttons simply
2687
+ appeared mid-sentence with no account of where the words went — the outline plus the shadow say
2688
+ "this is one panel, floating above", which is exactly what it is.
2689
+
2690
+ Radius 8 against the buttons' 4: an outer corner has to be looser than the corners it contains,
2691
+ or the nesting reads as a mistake. Padding is symmetric now for the same reason — the old
2692
+ `0 … 14px` let the buttons sit flush against the right edge, which is invisible without a border
2693
+ and lopsided with one. */
2694
+ .pr-helper-file-actions {
2695
+ background: #ffffff;
2696
+ padding: 4px 6px;
2697
+ border: 1px solid rgba(147, 51, 234, 0.25);
2698
+ border-radius: 8px;
2699
+ box-shadow: 0 2px 6px rgba(31, 35, 40, 0.10);
2700
+ /* The tray's default `stretch` left the "Analysing…" label top-aligned in a box the taller
2701
+ Cancel button had grown, so the two read as being on different lines. */
2702
+ align-items: center;
2703
+ }
2704
+
2705
+ .pr-helper-file-item:hover .pr-helper-file-actions {
2706
+ opacity: 1;
2707
+ pointer-events: auto;
2708
+ background: #fbf7fe;
2709
+ }
2710
+
2711
+ /* Opts out of the hover gate above, and must: this is the only indication that a follow-up is
2712
+ running and the only way to stop it, so a version of it you have to hover to find would leave
2713
+ the run as invisible as it was before the Cancel existed. */
2714
+ .pr-helper-file-actions-running {
2715
+ opacity: 1;
2716
+ pointer-events: auto;
2717
+ }
2718
+
2719
+ /* Matches .pr-helper-suggested-area-busy — the two say the same thing about the same operation. */
2720
+ .pr-helper-file-action-busy {
2721
+ font-size: 11px;
2722
+ color: var(--color-fg-muted, #57606a);
2723
+ white-space: nowrap;
2724
+ }
2725
+
2726
+ @keyframes pr-helper-spin { to { transform: rotate(360deg); } }
2727
+
2728
+ /* A spinner rather than a progress bar, on every in-flight LLM call the sidebar shows.
2729
+
2730
+ Deliberately indeterminate: each of these is a single LLM call, so there is no percentage to
2731
+ report and no intermediate step to name. A bar that filled at a made-up rate would be a
2732
+ progress claim we cannot support — the same objection as any other number the product states
2733
+ without being able to back it. What the reviewer actually needs to know is "still running,
2734
+ not stuck", which motion conveys and static text does not.
2735
+
2736
+ One rule for all three selectors on purpose. "Applying…" on an investigation and "Analysing…" on
2737
+ a file are the same promise to the reviewer — one request is out, wait for it — and two
2738
+ different-looking spinners would imply a difference that does not exist. Adding a fourth waiting
2739
+ state means adding it to this list, not inventing another animation. */
2740
+ .pr-helper-file-action-busy::before,
2741
+ .pr-helper-suggested-area-busy::before,
2742
+ .pr-helper-investigate-applying::before {
2743
+ content: '';
2744
+ display: inline-block;
2745
+ width: 10px;
2746
+ height: 10px;
2747
+ margin-right: 6px;
2748
+ vertical-align: -1px;
2749
+ border: 1.5px solid rgba(147, 51, 234, 0.25);
2750
+ border-top-color: #9333ea;
2751
+ border-radius: 50%;
2752
+ animation: pr-helper-spin 0.7s linear infinite;
2753
+ }
2754
+
2755
+ /* Explain and Copy — the row itself scrolls to the diff, so both stay explicit buttons.
2756
+ One shared outlined-secondary style: the actions are peers and shouldn't rank each other. */
2757
+ .pr-helper-action-btn {
2758
+ font-size: 12px;
2759
+ font-family: inherit;
2760
+ padding: 4px 10px;
2761
+ border-radius: 4px;
2762
+ /* Opaque, not transparent. The tray these sit in is absolutely positioned over the row, so a
2763
+ see-through button lets the description and badges read straight through its label. Harmless
2764
+ while the row only had Explain and Copy over mostly-empty space; immediately visible once a
2765
+ third button widened the tray far enough to reach the text. */
2766
+ background: #ffffff;
2767
+ border: 1px solid #9333ea;
2768
+ color: #9333ea;
2769
+ cursor: pointer;
2770
+ line-height: 18px;
2771
+ white-space: nowrap;
2772
+ transition: background 0.1s;
2773
+ }
2774
+
2775
+ .pr-helper-action-btn:hover:not(:disabled) { background: #f4e8fd; }
2776
+
2777
+ /* Reads as "not right now" rather than broken — the row is still hovering, so the button has
2778
+ to stay visible while it waits out an in-flight assistant request. */
2779
+ .pr-helper-action-btn:disabled {
2780
+ opacity: 0.55;
2781
+ cursor: default;
2782
+ }
2783
+
2784
+ /* ── Scrollbar ── */
2785
+ .pr-helper-file-list::-webkit-scrollbar { width: 6px; }
2786
+ .pr-helper-file-list::-webkit-scrollbar-track { background: transparent; }
2787
+ .pr-helper-file-list::-webkit-scrollbar-thumb {
2788
+ background: rgba(175, 184, 193, 0.4);
2789
+ border-radius: 3px;
2790
+ }
2791
+ .pr-helper-file-list::-webkit-scrollbar-thumb:hover {
2792
+ background: rgba(175, 184, 193, 0.6);
2793
+ }
2794
+
2795
+ /* ── Toast ── */
2796
+ .pr-helper-toast {
2797
+ position: fixed;
2798
+ bottom: 24px;
2799
+ right: 24px;
2800
+ padding: 8px 16px;
2801
+ background: #2da44e;
2802
+ color: #ffffff;
2803
+ border-radius: 6px;
2804
+ font-size: 13px;
2805
+ z-index: 1000000;
2806
+ opacity: 0;
2807
+ transform: translateY(10px);
2808
+ transition: all 0.3s ease;
2809
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
2810
+ display: flex;
2811
+ align-items: center;
2812
+ gap: 8px;
2813
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
2814
+ }
2815
+
2816
+ .pr-helper-toast svg { width: 16px; height: 16px; flex-shrink: 0; }
2817
+ .pr-helper-toast.show { opacity: 1; transform: translateY(0); }
2818
+ .pr-helper-toast.error { background: #cf222e; }
2819
+
2820
+ /* ── Rating prompt ── */
2821
+ .pr-helper-rating-prompt {
2822
+ position: fixed;
2823
+ bottom: 24px;
2824
+ right: 24px;
2825
+ width: 300px;
2826
+ padding: 16px;
2827
+ background: #ffffff;
2828
+ color: #1f2328;
2829
+ border: 1px solid rgba(0, 0, 0, 0.1);
2830
+ border-radius: 10px;
2831
+ z-index: 1000000;
2832
+ opacity: 0;
2833
+ transform: translateY(10px);
2834
+ transition: all 0.3s ease;
2835
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
2836
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
2837
+ }
2838
+
2839
+ .pr-helper-rating-prompt.show { opacity: 1; transform: translateY(0); }
2840
+
2841
+ .pr-helper-rating-prompt-title {
2842
+ margin: 0 0 6px;
2843
+ font-size: 14px;
2844
+ font-weight: 600;
2845
+ }
2846
+
2847
+ .pr-helper-rating-prompt-body {
2848
+ margin: 0 0 14px;
2849
+ font-size: 12px;
2850
+ line-height: 1.5;
2851
+ color: #57606a;
2852
+ }
2853
+
2854
+ .pr-helper-rating-prompt-actions {
2855
+ display: flex;
2856
+ flex-direction: column;
2857
+ gap: 6px;
2858
+ }
2859
+
2860
+ .pr-helper-rating-prompt-btn {
2861
+ padding: 8px 12px;
2862
+ border-radius: 6px;
2863
+ border: 1px solid rgba(0, 0, 0, 0.12);
2864
+ background: #f6f8fa;
2865
+ color: #1f2328;
2866
+ font-size: 12px;
2867
+ font-family: inherit;
2868
+ cursor: pointer;
2869
+ text-align: center;
2870
+ }
2871
+
2872
+ .pr-helper-rating-prompt-btn:hover { border-color: #9333ea; color: #9333ea; }
2873
+
2874
+ .pr-helper-rating-prompt-btn-primary {
2875
+ background: #9333ea;
2876
+ border-color: #9333ea;
2877
+ color: #ffffff;
2878
+ }
2879
+
2880
+ .pr-helper-rating-prompt-btn-primary:hover { background: #7c3aed; border-color: #7c3aed; color: #ffffff; }
2881
+
2882
+ .pr-helper-rating-prompt-btn-quiet {
2883
+ background: transparent;
2884
+ border-color: transparent;
2885
+ color: #57606a;
2886
+ }
2887
+
2888
+ .pr-helper-rating-prompt-btn-quiet:hover { border-color: transparent; color: #1f2328; }
2889
+
2890
+ /* ── Guided navigation overlay ── */
2891
+ /* Bottom-LEFT: toast (above, bottom-right) and rating-prompt (above, bottom-right) both live
2892
+ in the opposite corner, so this never collides with either. Kept intentionally small —
2893
+ media-player-controls sized, not a second sidebar (no checklist/issues/summary here). */
2894
+ .pr-helper-nav-overlay {
2895
+ position: fixed;
2896
+ bottom: 24px;
2897
+ left: 24px;
2898
+ z-index: 999997; /* below toggle-tab's 999998 and sidebar's 999999; sidebar is docked right so no structural overlap regardless */
2899
+ width: 280px;
2900
+ max-width: calc(100vw - 48px);
2901
+ padding: 12px 14px;
2902
+ background: #ffffff;
2903
+ color: #1f2328;
2904
+ border: 1px solid rgba(0, 0, 0, 0.1);
2905
+ border-radius: 10px;
2906
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
2907
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
2908
+ transition: width 0.2s ease, height 0.2s ease, padding 0.2s ease, border-radius 0.2s ease, left 0.2s ease, background 0.2s ease;
2909
+ }
2910
+
2911
+ /* The tab is a permanent sibling of the panel, only shown while collapsed — see
2912
+ navigation_overlay.ts's showNavigationOverlay() for why (keeps expand/collapse a single
2913
+ class toggle rather than a DOM rebuild, so restoring never re-fetches or resets anything). */
2914
+ .pr-helper-nav-overlay-tab { display: none; }
2915
+
2916
+ /* "Hide guide" collapses to this — mirrors .pr-helper-toggle-button's own vertical-tab
2917
+ pattern (sidebar.css, further down) but anchored to the left edge, flush like that one is
2918
+ flush right, so it reads as the same interaction pattern reused rather than a new one. */
2919
+ .pr-helper-nav-overlay.pr-helper-nav-overlay-collapsed {
2920
+ left: 0;
2921
+ width: auto;
2922
+ padding: 0;
2923
+ background: #9333ea;
2924
+ border: 1px solid #7c3aed;
2925
+ border-left: none;
2926
+ border-radius: 0 6px 6px 0;
2927
+ box-shadow: 2px 0 8px rgba(147, 51, 234, 0.3);
2928
+ }
2929
+
2930
+ .pr-helper-nav-overlay-collapsed .pr-helper-nav-overlay-panel { display: none; }
2931
+
2932
+ .pr-helper-nav-overlay-collapsed .pr-helper-nav-overlay-tab {
2933
+ display: flex;
2934
+ align-items: center;
2935
+ justify-content: center;
2936
+ gap: 8px;
2937
+ padding: 16px 8px;
2938
+ border: none;
2939
+ background: transparent;
2940
+ color: #ffffff;
2941
+ cursor: pointer;
2942
+ font-family: inherit;
2943
+ font-size: 11px;
2944
+ font-weight: 600;
2945
+ letter-spacing: 0.04em;
2946
+ writing-mode: vertical-rl;
2947
+ transition: padding 0.2s ease;
2948
+ }
2949
+
2950
+ .pr-helper-nav-overlay-collapsed .pr-helper-nav-overlay-tab:hover { padding-left: 10px; }
2951
+
2952
+ .pr-helper-nav-overlay-header {
2953
+ display: flex;
2954
+ align-items: flex-start;
2955
+ justify-content: space-between;
2956
+ gap: 8px;
2957
+ }
2958
+
2959
+ .pr-helper-nav-overlay-eyebrow {
2960
+ font-size: 10px;
2961
+ font-weight: 700;
2962
+ text-transform: uppercase;
2963
+ letter-spacing: 0.04em;
2964
+ color: #9333ea;
2965
+ margin-bottom: 2px;
2966
+ }
2967
+
2968
+ .pr-helper-nav-overlay-title {
2969
+ font-size: 13px;
2970
+ font-weight: 600;
2971
+ line-height: 1.3;
2972
+ }
2973
+
2974
+ /* A text label, not a bare "×" — a lone icon is easy to click without realizing what it does
2975
+ (or that the sidebar's toggle tab reopens it), so it's spelled out. */
2976
+ .pr-helper-nav-overlay-close {
2977
+ flex-shrink: 0;
2978
+ background: transparent;
2979
+ border: none;
2980
+ color: #57606a;
2981
+ font-size: 11px;
2982
+ font-weight: 600;
2983
+ font-family: inherit;
2984
+ line-height: 1.3;
2985
+ cursor: pointer;
2986
+ padding: 2px 4px;
2987
+ white-space: nowrap;
2988
+ }
2989
+
2990
+ .pr-helper-nav-overlay-close:hover { color: #1f2328; text-decoration: underline; }
2991
+
2992
+ .pr-helper-nav-overlay-progress {
2993
+ margin-top: 2px;
2994
+ font-size: 11px;
2995
+ color: #57606a;
2996
+ }
2997
+
2998
+ /* Sits between the progress line and the scrollable item list — outside the list on purpose,
2999
+ so it never competes for that 160px scroll region. */
3000
+ .pr-helper-nav-overlay-focus {
3001
+ margin-top: 10px;
3002
+ padding-bottom: 10px;
3003
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
3004
+ }
3005
+
3006
+ .pr-helper-nav-overlay-focus-eyebrow {
3007
+ font-size: 10px;
3008
+ font-weight: 700;
3009
+ text-transform: uppercase;
3010
+ letter-spacing: 0.04em;
3011
+ color: #9333ea;
3012
+ margin-bottom: 4px;
3013
+ }
3014
+
3015
+ /* Shown in full, never clipped — a half-sentence ("Verify cached targets, polymorphic…") is
3016
+ worse than no note at all, since the reviewer can't act on the part that got cut. Length is
3017
+ controlled upstream instead: the prompt caps focus at one or two sentences (~20-35 words),
3018
+ which wraps to about four lines here. The panel is still never widened to fit this text —
3019
+ keeping the navigator narrow matters more than saving a line of wrapping. */
3020
+ .pr-helper-nav-overlay-focus-text {
3021
+ margin: 0;
3022
+ font-size: 11px;
3023
+ line-height: 1.45;
3024
+ color: #57606a;
3025
+ }
3026
+
3027
+ .pr-helper-nav-overlay-items {
3028
+ list-style: none;
3029
+ margin: 10px 0;
3030
+ padding: 0;
3031
+ max-height: 160px;
3032
+ overflow-y: auto;
3033
+ display: flex;
3034
+ flex-direction: column;
3035
+ gap: 2px;
3036
+ /* macOS defaults to overlay scrollbars that are invisible until actively scrolling, so a
3037
+ long file list (large PRs) reads as cut off rather than scrollable — force a slim,
3038
+ always-visible scrollbar instead of relying on the OS setting. */
3039
+ scrollbar-width: thin;
3040
+ scrollbar-color: rgba(0, 0, 0, 0.25) transparent;
3041
+ }
3042
+
3043
+ .pr-helper-nav-overlay-items::-webkit-scrollbar { width: 6px; }
3044
+ .pr-helper-nav-overlay-items::-webkit-scrollbar-track { background: transparent; }
3045
+ .pr-helper-nav-overlay-items::-webkit-scrollbar-thumb {
3046
+ background: rgba(0, 0, 0, 0.25);
3047
+ border-radius: 3px;
3048
+ }
3049
+ .pr-helper-nav-overlay-items::-webkit-scrollbar-thumb:hover { background: rgba(0, 0, 0, 0.4); }
3050
+
3051
+ .pr-helper-nav-item {
3052
+ display: flex;
3053
+ align-items: center;
3054
+ gap: 6px;
3055
+ padding: 4px 6px;
3056
+ border-radius: 4px;
3057
+ font-size: 12px;
3058
+ cursor: pointer;
3059
+ white-space: nowrap;
3060
+ overflow: hidden;
3061
+ text-overflow: ellipsis;
3062
+ /* Without this, the flex column parent's max-height (below) makes rows shrink to fit
3063
+ instead of overflowing — on a long PR every row gets squeezed short rather than the
3064
+ list scrolling, which is the actual bug this fixes (not a missing scrollbar). */
3065
+ flex-shrink: 0;
3066
+ }
3067
+
3068
+ .pr-helper-nav-item:hover { background: rgba(0, 0, 0, 0.04); }
3069
+
3070
+ .pr-helper-nav-item-marker { flex-shrink: 0; width: 14px; text-align: center; color: #57606a; }
3071
+
3072
+ .pr-helper-nav-item-label { overflow: hidden; text-overflow: ellipsis; }
3073
+
3074
+ /* The current item is visually distinct (fill, not just a different glyph) so the active
3075
+ target is unmissable at a glance — persistent, not a timed highlight. */
3076
+ .pr-helper-nav-item-current {
3077
+ background: rgba(147, 51, 234, 0.12);
3078
+ font-weight: 600;
3079
+ }
3080
+ .pr-helper-nav-item-current .pr-helper-nav-item-marker { color: #9333ea; }
3081
+ .pr-helper-nav-item-current:hover { background: rgba(147, 51, 234, 0.18); }
3082
+
3083
+ /* Neutral, not GitHub's approval-green — "visited" means scrolled past, not reviewed or
3084
+ approved, and the color shouldn't suggest otherwise. */
3085
+ .pr-helper-nav-item-visited .pr-helper-nav-item-marker { color: #57606a; }
3086
+
3087
+ .pr-helper-nav-overlay-controls {
3088
+ display: flex;
3089
+ gap: 8px;
3090
+ }
3091
+
3092
+ .pr-helper-nav-overlay-btn {
3093
+ flex: 1;
3094
+ padding: 8px 10px;
3095
+ border-radius: 6px;
3096
+ border: 1px solid rgba(0, 0, 0, 0.12);
3097
+ background: #f6f8fa;
3098
+ color: #1f2328;
3099
+ font-size: 12px;
3100
+ font-weight: 600;
3101
+ font-family: inherit;
3102
+ cursor: pointer;
3103
+ text-align: center;
3104
+ }
3105
+
3106
+ .pr-helper-nav-overlay-btn:hover { border-color: #9333ea; color: #9333ea; }
3107
+
3108
+ .pr-helper-nav-overlay-btn-primary {
3109
+ background: #9333ea;
3110
+ border-color: #9333ea;
3111
+ color: #ffffff;
3112
+ }
3113
+
3114
+ .pr-helper-nav-overlay-btn-primary:hover { background: #7c3aed; border-color: #7c3aed; color: #ffffff; }
3115
+
3116
+ /* Held for ~250ms between the Finish click and the overlay teardown (markOverlayFinishing)
3117
+ so the button commits to the action before everything it lives in disappears. Wins over
3118
+ :hover, which is necessarily still active at the moment of the click. */
3119
+ .pr-helper-nav-overlay-btn {
3120
+ transition: background-color 0.25s ease, border-color 0.25s ease, color 0.25s ease;
3121
+ }
3122
+
3123
+ .pr-helper-nav-overlay-btn-primary.pr-helper-nav-overlay-btn-finishing,
3124
+ .pr-helper-nav-overlay-btn-primary.pr-helper-nav-overlay-btn-finishing:hover {
3125
+ background: #1a7f37;
3126
+ border-color: #1a7f37;
3127
+ color: #ffffff;
3128
+ }
3129
+
3130
+ /* Marks the currently-navigated-to file on the GitHub page — a separate fixed-position box
3131
+ tracking the target's getBoundingClientRect(), not an outline on the container itself (see
3132
+ github_diff_navigator.ts's applyHighlight() for why: GitHub's containment/stacking context
3133
+ swallows a direct outline). Persistent — removed only when navigateTo() moves to a
3134
+ different target, never timed out. pointer-events: none so it never blocks clicks/selection
3135
+ on the diff underneath. */
3136
+ .pr-helper-nav-spotlight {
3137
+ position: fixed;
3138
+ border: 2px solid #9333ea;
3139
+ border-radius: 6px;
3140
+ pointer-events: none;
3141
+ z-index: 999996; /* below the overlay's 999997, above ordinary page content */
3142
+ /* No dimming wash behind this. It used to carry `box-shadow: 0 0 0 2000px rgba(147,51,234,
3143
+ 0.04)` — a full-page purple tint over everything outside the box, painted above all of
3144
+ GitHub's own content. At 4% it read as subtle until something saturated sat under it:
3145
+ GitHub's blue focus ring looked like it was rendering *behind* the page. The border and
3146
+ the file-tree row tint below already say which file is current without recolouring the
3147
+ rest of GitHub. */
3148
+ }
3149
+
3150
+ /* Applied by GitHubDiffNavigator to the current target's container and its first child.
3151
+ Suppresses GitHub's own styling on that one file, for as long as it's the highlighted one:
3152
+
3153
+ - Its rounded border, which the spotlight now sits directly on top of (the spotlight traces
3154
+ the container's own rect exactly — see applyHighlight's rect handling).
3155
+ - Its selected-file styling, which GitHub applies to the container when the file is reached
3156
+ through GitHub's own file tree. Blanked as outline and box-shadow as well as border-color,
3157
+ because only the border was ever suppressed and the rest showed through as blue edges
3158
+ around a purple box. Note this is only half of "one highlight at a time" — it covers our
3159
+ highlight landing on a file GitHub already marked. The other half is the reverse order, and
3160
+ that one can't be done in CSS: see watchForGitHubFileNavigation().
3161
+ - The focus ring GitHub puts on the file header's collapse caret when a #diff-<hash> fragment
3162
+ targets the file. GitHubDiffNavigator.releaseStolenFocus already blurs the focus we caused;
3163
+ this covers GitHub re-focusing it afterwards on its own. Scoped to the highlighted file
3164
+ rather than applied globally so keyboard focus everywhere else on the page still shows a
3165
+ ring — it's a deliberate, narrow trade of that affordance inside one file for a clean box. */
3166
+ .pr-helper-nav-target {
3167
+ border-color: transparent !important;
3168
+ outline: none !important;
3169
+ box-shadow: none !important;
3170
+ }
3171
+
3172
+ .pr-helper-nav-target :focus-visible {
3173
+ outline: none !important;
3174
+ box-shadow: none !important;
3175
+ }
3176
+
3177
+ /* The "Collapse file" bubble GitHub pops when its caret takes focus. GitHubDiffNavigator's
3178
+ focus guard is what actually prevents this (no focus, no tooltip); this is the backstop for
3179
+ the moments the guard isn't holding — GitHub renders these as <tool-tip> custom elements next
3180
+ to their trigger, so hiding the ones inside the highlighted file leaves every other tooltip
3181
+ on the page working normally. */
3182
+ .pr-helper-nav-target tool-tip {
3183
+ display: none !important;
3184
+ }
3185
+
3186
+ /* Marks the current target's row in GitHub's own file-tree sidebar — a plain class toggle
3187
+ works here (unlike the diff container, this row isn't subject to CSS containment), so no
3188
+ spotlight-tracking is needed. !important guards against GitHub's own selected/hover-state
3189
+ background rules on the same row winning by specificity. */
3190
+ .pr-helper-nav-tree-highlight {
3191
+ background: rgba(147, 51, 234, 0.14) !important;
3192
+ box-shadow: inset 3px 0 0 #9333ea;
3193
+ }
3194
+
3195
+ /* Marks the comment behind an "Already raised by @x" link, applied by
3196
+ github_comment_highlighter.ts after the jump. Same purple, same "border, no wash" treatment as
3197
+ the diff spotlight above, so following a link from the Issues tab to a prior comment looks
3198
+ like following one to a file.
3199
+
3200
+ Every declaration is !important because this is a fight with GitHub, not with ourselves: the
3201
+ jump keeps the #discussion_r<id> fragment (it's what expands a collapsed or resolved thread),
3202
+ and that fragment makes GitHub paint its own accent-blue border on the targeted comment. A
3203
+ plain class loses to GitHub's own rules on specificity, so ours have to outrank them.
3204
+
3205
+ No fixed-position spotlight box here, unlike the diff container: a timeline comment is
3206
+ ordinary flow content with no CSS containment to swallow the border. */
3207
+ .pr-helper-comment-highlight {
3208
+ border-color: #9333ea !important;
3209
+ border-radius: 6px !important;
3210
+ box-shadow: 0 0 0 2px #9333ea !important;
3211
+ }
3212
+
3213
+ /* The fragment's element is often a stub *inside* the comment box we highlighted, so GitHub's
3214
+ blue can still be sitting on a descendant after the rule above has recoloured the box itself.
3215
+ Scoped under our class so :target styling anywhere else on the page is left alone. */
3216
+ .pr-helper-comment-highlight :target {
3217
+ border-color: #9333ea !important;
3218
+ box-shadow: none !important;
3219
+ }
3220
+
3221
+ /* Marks the current target's row inside the Guide tab's section card, kept in sync with the
3222
+ overlay by PRHelperSidebar.syncGuidedNavUi(). */
3223
+ .pr-helper-guide-file-current {
3224
+ background: rgba(147, 51, 234, 0.12);
3225
+ border-radius: 6px;
3226
+ }
3227
+
3228
+ /* ── Toggle button — vertical tab ── */
3229
+ .pr-helper-toggle-button {
3230
+ position: fixed;
3231
+ top: 50%;
3232
+ right: 0;
3233
+ transform: translateY(-50%);
3234
+ background: #9333ea;
3235
+ border: 1px solid #7c3aed;
3236
+ border-right: none;
3237
+ border-radius: 6px 0 0 6px;
3238
+ padding: 16px 8px;
3239
+ cursor: pointer;
3240
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
3241
+ font-size: 11px;
3242
+ font-weight: 600;
3243
+ color: #ffffff;
3244
+ z-index: 999998;
3245
+ display: flex;
3246
+ flex-direction: column;
3247
+ align-items: center;
3248
+ gap: 8px;
3249
+ writing-mode: vertical-rl;
3250
+ transition: all 0.2s ease;
3251
+ box-shadow: -2px 0 8px rgba(147, 51, 234, 0.3);
3252
+ letter-spacing: 0.04em;
3253
+ }
3254
+
3255
+ .pr-helper-toggle-button:hover {
3256
+ background: #7c3aed;
3257
+ border-color: #6d28d9;
3258
+ padding-right: 10px;
3259
+ }
3260
+
3261
+ .pr-helper-toggle-button:disabled { opacity: 0.6; cursor: not-allowed; }
3262
+
3263
+ .pr-helper-toggle-button svg {
3264
+ width: 14px;
3265
+ height: 14px;
3266
+ flex-shrink: 0;
3267
+ transform: rotate(90deg);
3268
+ }
3269
+
3270
+ /* ── Floater → sidebar morph ──
3271
+ The floater is the origin point of the sidebar, not a separate control that opens one. Two
3272
+ phases, deliberately sequential rather than one combined move:
3273
+
3274
+ 1. The button itself stretches along the edge it already sits on — from its own height,
3275
+ centred, out to the full viewport — becoming a purple rail. Its label fades as it goes,
3276
+ since a vertical word stretched over 900px is just noise.
3277
+ 2. The sidebar slides out of that rail, using the SAME translateX transition the base rule
3278
+ has always used. Nothing about phase 2 is new, which is the point: the only new motion is
3279
+ phase 1, along an axis the button already occupies.
3280
+
3281
+ Splitting them this way keeps the sidebar's own transform and transition untouched. An earlier
3282
+ version grew a clip-path window over the panel instead, which had to override transform, fight
3283
+ the base transition, and hand both back on teardown — where clip-path reverts to the
3284
+ non-interpolable `none`, snapping the whole panel into view before sliding it off.
3285
+
3286
+ The rail is the real button, not a stand-in, so the purple, the radius and the shadow are the
3287
+ button's own by construction. z-index 999998 keeps it one below the sidebar, so phase 2 covers
3288
+ it and the restore afterwards is never seen. */
3289
+ /* Height AND width are deliberately absent here and driven inline from JS at both ends. The
3290
+ button's resting size follows its label, i.e. `auto`, which cannot interpolate — so the start
3291
+ values have to be written inline as pixels, and an inline value outranks this rule, meaning any
3292
+ end value declared here would simply never win. Everything else is fine to own from CSS.
3293
+
3294
+ The rail narrows as it grows: the button thins to exactly the width of the purple edge the
3295
+ sidebar carries (see the sidebar's inset box-shadow), so the line the button becomes is the same
3296
+ line the panel draws out. A rail at the button's full width reads as a slab. `overflow: hidden`
3297
+ keeps the label clipped rather than spilling as it goes. */
3298
+ .pr-helper-toggle-button--stretching {
3299
+ top: 0;
3300
+ transform: translateY(0);
3301
+ border-radius: 0;
3302
+ cursor: default;
3303
+ padding: 0;
3304
+ min-width: 0;
3305
+ overflow: hidden;
3306
+ box-sizing: border-box;
3307
+ transition:
3308
+ top 0.26s cubic-bezier(0.22, 0.61, 0.36, 1),
3309
+ height 0.26s cubic-bezier(0.22, 0.61, 0.36, 1),
3310
+ width 0.26s cubic-bezier(0.22, 0.61, 0.36, 1),
3311
+ padding 0.26s cubic-bezier(0.22, 0.61, 0.36, 1),
3312
+ transform 0.26s cubic-bezier(0.22, 0.61, 0.36, 1),
3313
+ border-radius 0.26s ease;
3314
+ }
3315
+
3316
+ /* The stretch reads as the button becoming an edge, so its content has to go. Faster than the
3317
+ stretch itself — the label should be gone well before the rail reaches full height. */
3318
+ .pr-helper-toggle-button--stretching svg,
3319
+ .pr-helper-toggle-button--stretching span {
3320
+ opacity: 0;
3321
+ transition: opacity 0.12s ease-out;
3322
+ }
3323
+
3324
+ /* Suppresses every transition for one frame so the rail can be handed back to its resting
3325
+ geometry without animating there. Applied while the open sidebar covers it. */
3326
+ .pr-helper-toggle-button--instant {
3327
+ transition: none;
3328
+ }
3329
+
3330
+ /* Phase 2's timing. Marginally slower than the base 0.3s and eased to match phase 1, so the two
3331
+ halves read as one movement rather than two clips spliced together. */
3332
+ .pr-helper-sidebar--from-rail {
3333
+ transition: transform 0.32s cubic-bezier(0.22, 0.61, 0.36, 1);
3334
+ }
3335
+
3336
+ /* Shown only while the panel has nothing in it at all — a first open, waiting on PR data. Bars,
3337
+ deliberately: a skeleton shaped like findings and pills would be promising content the review
3338
+ may not have. */
3339
+ .pr-helper-panel-skeleton {
3340
+ padding: 20px;
3341
+ }
3342
+
3343
+ .pr-helper-panel-skeleton-bar {
3344
+ position: relative;
3345
+ height: 12px;
3346
+ margin-bottom: 12px;
3347
+ border-radius: 4px;
3348
+ background: rgba(147, 51, 234, 0.09);
3349
+ overflow: hidden;
3350
+ }
3351
+
3352
+ .pr-helper-panel-skeleton-bar::after {
3353
+ content: '';
3354
+ position: absolute;
3355
+ top: 0;
3356
+ left: 0;
3357
+ height: 100%;
3358
+ width: 40%;
3359
+ background: linear-gradient(90deg, transparent, rgba(147, 51, 234, 0.18), transparent);
3360
+ animation: pr-helper-shimmer-sweep 1.6s ease-in-out infinite;
3361
+ }
3362
+
3363
+ /* What the panel says when GitHub cannot be reached and nothing has ever loaded. Full-bleed and
3364
+ quiet rather than a bordered error card: this is the entire content of the surface, not an alert
3365
+ sitting on top of content, and a panel whose only occupant is a red box overstates a connection
3366
+ problem the reviewer can usually fix in one click. */
3367
+ .pr-helper-panel-unreachable {
3368
+ padding: 28px 20px;
3369
+ }
3370
+
3371
+ .pr-helper-panel-unreachable-title {
3372
+ font-size: 13px;
3373
+ font-weight: 700;
3374
+ color: #9333ea;
3375
+ text-transform: uppercase;
3376
+ letter-spacing: 0.06em;
3377
+ margin-bottom: 10px;
3378
+ }
3379
+
3380
+ .pr-helper-panel-unreachable-body {
3381
+ margin: 0 0 16px;
3382
+ font-size: 13px;
3383
+ line-height: 1.55;
3384
+ color: var(--color-fg-muted, #57606a);
3385
+ }
3386
+
3387
+ .pr-helper-panel-retry {
3388
+ padding: 7px 14px;
3389
+ font-family: inherit;
3390
+ font-size: 12px;
3391
+ font-weight: 600;
3392
+ color: #ffffff;
3393
+ background: #9333ea;
3394
+ border: 1px solid #7c3aed;
3395
+ border-radius: 6px;
3396
+ cursor: pointer;
3397
+ transition: background 0.2s ease, border-color 0.2s ease;
3398
+ }
3399
+
3400
+ .pr-helper-panel-retry:hover {
3401
+ background: #7c3aed;
3402
+ border-color: #6d28d9;
3403
+ }
3404
+
3405
+ .pr-helper-panel-skeleton-bar:nth-child(1) { width: 55%; }
3406
+ .pr-helper-panel-skeleton-bar:nth-child(2) { width: 100%; }
3407
+ .pr-helper-panel-skeleton-bar:nth-child(3) { width: 85%; }
3408
+ .pr-helper-panel-skeleton-bar:nth-child(4) { width: 70%; }
3409
+
3410
+ /* ── Save for later ──
3411
+ Bottom-centre: the guided-nav overlay owns bottom-left, toasts and the rating prompt own
3412
+ bottom-right, and the toggle tab owns the right edge. z-index deliberately sits one below the
3413
+ nav overlay's 999997 — if a reviewer is mid-navigation, navigation is the thing that must stay
3414
+ clickable. Dark and purple rather than following GitHub's theme, because sidebar.css has no
3415
+ theme handling to inherit and a surface that guesses wrong is worse than one that commits. */
3416
+ .pr-helper-save-bar {
3417
+ position: fixed;
3418
+ bottom: 24px;
3419
+ left: 50%;
3420
+ transform: translateX(-50%);
3421
+ z-index: 999996;
3422
+ /* A column so the optional label editor can expand underneath the action row without the two
3423
+ competing for the same horizontal space. With the editor hidden this is still one row. */
3424
+ display: flex;
3425
+ flex-direction: column;
3426
+ max-width: calc(100vw - 48px);
3427
+ background: #161b26;
3428
+ border: 1px solid rgba(147, 51, 234, 0.35);
3429
+ border-radius: 10px;
3430
+ box-shadow: 0 8px 28px rgba(0, 0, 0, 0.4);
3431
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
3432
+ opacity: 0;
3433
+ /* Only opacity and the entry lift are transitioned — transform also carries the sidebar offset,
3434
+ which must track a resize drag frame-for-frame rather than easing behind it. */
3435
+ transition: opacity 0.3s ease;
3436
+ pointer-events: none;
3437
+ }
3438
+
3439
+ .pr-helper-save-bar-visible { opacity: 1; pointer-events: auto; }
3440
+
3441
+ /* GitHub's layout is dense at narrow widths, and this is a floating element on somebody else's
3442
+ page — the right answer to "will it fit?" is not to shrink it until it does. */
3443
+ .pr-helper-save-bar-too-narrow { display: none; }
3444
+
3445
+ .pr-helper-save-bar-action {
3446
+ display: flex;
3447
+ align-items: center;
3448
+ gap: 12px;
3449
+ padding: 12px 16px;
3450
+ border: none;
3451
+ background: transparent;
3452
+ color: #f8fafc;
3453
+ cursor: pointer;
3454
+ font-family: inherit;
3455
+ text-align: left;
3456
+ border-radius: 10px 0 0 10px;
3457
+ transition: background 0.15s;
3458
+ }
3459
+
3460
+ .pr-helper-save-bar-action:hover { background: rgba(147, 51, 234, 0.12); }
3461
+ .pr-helper-save-bar-icon { flex-shrink: 0; color: #94a3b8; transition: color 0.15s; }
3462
+ .pr-helper-save-bar-saved .pr-helper-save-bar-icon { color: #a855f7; }
3463
+
3464
+ .pr-helper-save-bar-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
3465
+
3466
+ .pr-helper-save-bar-title {
3467
+ font-size: 13px;
3468
+ font-weight: 600;
3469
+ line-height: 1.3;
3470
+ }
3471
+
3472
+ .pr-helper-save-bar-detail {
3473
+ font-size: 12px;
3474
+ color: #94a3b8;
3475
+ line-height: 1.3;
3476
+ }
3477
+
3478
+ .pr-helper-save-bar-dismiss {
3479
+ display: flex;
3480
+ align-items: center;
3481
+ padding: 0 12px;
3482
+ border: none;
3483
+ border-left: 1px solid rgba(255, 255, 255, 0.08);
3484
+ background: transparent;
3485
+ color: #64748b;
3486
+ cursor: pointer;
3487
+ border-radius: 0 10px 10px 0;
3488
+ transition: color 0.15s, background 0.15s;
3489
+ }
3490
+
3491
+ .pr-helper-save-bar-dismiss:hover { color: #f8fafc; background: rgba(255, 255, 255, 0.05); }
3492
+
3493
+ .pr-helper-save-bar-row { display: flex; align-items: stretch; }
3494
+
3495
+ /* The optional follow-up to a save. Appears only once the PR is saved, so saving itself stays one
3496
+ click — choosing labels is never a step on the way to it. */
3497
+ .pr-helper-save-bar-labels-action {
3498
+ display: flex;
3499
+ align-items: center;
3500
+ padding: 0 14px;
3501
+ border: none;
3502
+ border-left: 1px solid rgba(255, 255, 255, 0.08);
3503
+ background: transparent;
3504
+ color: #a855f7;
3505
+ cursor: pointer;
3506
+ font-family: inherit;
3507
+ font-size: 12px;
3508
+ font-weight: 600;
3509
+ white-space: nowrap;
3510
+ transition: background 0.15s;
3511
+ }
3512
+
3513
+ .pr-helper-save-bar-labels-action[hidden] { display: none; }
3514
+ .pr-helper-save-bar-labels-action:hover { background: rgba(147, 51, 234, 0.12); }
3515
+
3516
+ .pr-helper-save-bar-labels {
3517
+ display: flex;
3518
+ align-items: center;
3519
+ gap: 6px;
3520
+ flex-wrap: wrap;
3521
+ padding: 10px 16px 12px;
3522
+ border-top: 1px solid rgba(255, 255, 255, 0.08);
3523
+ }
3524
+
3525
+ .pr-helper-save-bar-labels[hidden] { display: none; }
3526
+
3527
+ .pr-helper-save-bar-chip {
3528
+ display: inline-flex;
3529
+ align-items: center;
3530
+ gap: 4px;
3531
+ padding: 2px 8px;
3532
+ border-radius: 100px;
3533
+ background: rgba(147, 51, 234, 0.12);
3534
+ color: #d8b4fe;
3535
+ font-size: 11px;
3536
+ font-weight: 600;
3537
+ white-space: nowrap;
3538
+ }
3539
+
3540
+ .pr-helper-save-bar-chip-remove {
3541
+ border: none;
3542
+ background: transparent;
3543
+ color: #94a3b8;
3544
+ font-size: 13px;
3545
+ line-height: 1;
3546
+ padding: 0;
3547
+ cursor: pointer;
3548
+ transition: color 0.15s;
3549
+ }
3550
+
3551
+ .pr-helper-save-bar-chip-remove:hover { color: #f87171; }
3552
+
3553
+ .pr-helper-save-bar-label-input {
3554
+ flex: 1 1 140px;
3555
+ min-width: 120px;
3556
+ padding: 5px 8px;
3557
+ border: 1px solid rgba(255, 255, 255, 0.12);
3558
+ border-radius: 6px;
3559
+ background: #0b0f17;
3560
+ color: #f8fafc;
3561
+ font-family: inherit;
3562
+ font-size: 12px;
3563
+ }
3564
+
3565
+ .pr-helper-save-bar-label-input::placeholder { color: #64748b; }
3566
+ .pr-helper-save-bar-label-input:focus { outline: none; border-color: #9333ea; }
3567
+ .pr-helper-save-bar-label-input:disabled { cursor: not-allowed; opacity: 0.6; }
3568
+
3569
+ .pr-helper-save-bar-label-done {
3570
+ border: none;
3571
+ background: transparent;
3572
+ color: #a855f7;
3573
+ font-family: inherit;
3574
+ font-size: 12px;
3575
+ font-weight: 600;
3576
+ padding: 4px 6px;
3577
+ cursor: pointer;
3578
+ }
3579
+
3580
+ .pr-helper-save-bar-label-done:hover { text-decoration: underline; }
3581
+
3582
+ /* ── Selected-text → Assistant ──
3583
+ Mounted on document.body, not inside the sidebar: the sidebar is a scrolling, clipped
3584
+ flex column, so a popover anchored to viewport coordinates has to live outside it to sit
3585
+ over the selected text. z-index clears the sidebar's own 999999 for the same reason.
3586
+ See selection_action.ts. */
3587
+ .pr-helper-selection-popover {
3588
+ position: fixed;
3589
+ z-index: 1000000;
3590
+ display: flex;
3591
+ flex-direction: column;
3592
+ background: #ffffff;
3593
+ border: 1px solid rgba(0, 0, 0, 0.12);
3594
+ border-radius: 8px;
3595
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
3596
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
3597
+ }
3598
+
3599
+ /* Not clipped with overflow: hidden — the copy tooltip has to escape the popover — so the two
3600
+ bar buttons round their own outer corners instead. */
3601
+ .pr-helper-selection-bar {
3602
+ position: relative;
3603
+ display: flex;
3604
+ align-items: stretch;
3605
+ }
3606
+
3607
+ .pr-helper-selection-trigger {
3608
+ display: flex;
3609
+ align-items: center;
3610
+ gap: 6px;
3611
+ padding: 6px 10px;
3612
+ border: none;
3613
+ border-radius: 7px 0 0 7px;
3614
+ background: #9333ea;
3615
+ color: #ffffff;
3616
+ font-family: inherit;
3617
+ font-size: 12px;
3618
+ font-weight: 600;
3619
+ cursor: pointer;
3620
+ white-space: nowrap;
3621
+ }
3622
+
3623
+ .pr-helper-selection-trigger:hover { background: #7c3aed; }
3624
+
3625
+ /* Deliberately secondary to the trigger: icon-only and unfilled, so the bar still reads as one
3626
+ primary action plus a utility rather than two competing CTAs. */
3627
+ .pr-helper-selection-copy {
3628
+ display: flex;
3629
+ align-items: center;
3630
+ justify-content: center;
3631
+ padding: 6px 10px;
3632
+ border: none;
3633
+ border-radius: 0 7px 7px 0;
3634
+ background: transparent;
3635
+ color: var(--color-fg-muted, #656d76);
3636
+ cursor: pointer;
3637
+ }
3638
+
3639
+ .pr-helper-selection-copy:hover {
3640
+ background: rgba(147, 51, 234, 0.1);
3641
+ color: #9333ea;
3642
+ }
3643
+
3644
+ .pr-helper-selection-copy:focus-visible {
3645
+ outline: 2px solid #9333ea;
3646
+ outline-offset: -2px;
3647
+ }
3648
+
3649
+ .pr-helper-selection-check-icon { display: none; }
3650
+ .pr-helper-selection-copy-done .pr-helper-selection-copy-icon { display: none; }
3651
+ .pr-helper-selection-copy-done .pr-helper-selection-check-icon { display: block; }
3652
+ .pr-helper-selection-copy-done .pr-helper-selection-copy { color: #1a7f37; }
3653
+
3654
+ /* Hover *and* focus, since the popover is keyboard reachable and a title attribute would only
3655
+ cover the pointer. */
3656
+ .pr-helper-selection-tip {
3657
+ position: absolute;
3658
+ top: 100%;
3659
+ right: 0;
3660
+ margin-top: 6px;
3661
+ padding: 4px 8px;
3662
+ border-radius: 6px;
3663
+ background: rgba(28, 30, 33, 0.92);
3664
+ color: #ffffff;
3665
+ font-size: 11px;
3666
+ line-height: 1.4;
3667
+ white-space: nowrap;
3668
+ pointer-events: none;
3669
+ opacity: 0;
3670
+ transition: opacity 90ms ease-in;
3671
+ }
3672
+
3673
+ .pr-helper-selection-copy:hover ~ .pr-helper-selection-tip,
3674
+ .pr-helper-selection-copy:focus-visible ~ .pr-helper-selection-tip {
3675
+ opacity: 1;
3676
+ }
3677
+
3678
+ /* Stage 1 is the trigger bar; clicking it swaps to the action menu in place, so the
3679
+ popover never moves out from under the cursor between the two stages. */
3680
+ .pr-helper-selection-menu { display: none; }
3681
+ .pr-helper-selection-popover-expanded .pr-helper-selection-bar { display: none; }
3682
+
3683
+ .pr-helper-selection-popover-expanded .pr-helper-selection-menu {
3684
+ display: flex;
3685
+ flex-direction: column;
3686
+ min-width: 220px;
3687
+ padding: 4px;
3688
+ }
3689
+
3690
+ .pr-helper-selection-menu-item {
3691
+ padding: 7px 10px;
3692
+ border: none;
3693
+ border-radius: 6px;
3694
+ background: transparent;
3695
+ color: var(--color-fg-default, #24292f);
3696
+ font-family: inherit;
3697
+ font-size: 12px;
3698
+ text-align: left;
3699
+ cursor: pointer;
3700
+ white-space: nowrap;
3701
+ }
3702
+
3703
+ .pr-helper-selection-menu-item:hover {
3704
+ background: rgba(147, 51, 234, 0.1);
3705
+ color: #9333ea;
3706
+ }
3707
+
3708
+ /* The flat layout has no trigger to expand: its two items are the popover. Used where the list is
3709
+ short enough that a click to reveal it costs more than it saves, which is the assistant's own
3710
+ replies. */
3711
+ .pr-helper-selection-popover-flat .pr-helper-selection-menu {
3712
+ display: flex;
3713
+ flex-direction: column;
3714
+ min-width: 150px;
3715
+ padding: 4px;
3716
+ }
3717
+
3718
+ /* Copy is a utility rather than a question, so it sits under a rule and reads quieter. It keeps
3719
+ the popover open, the same as the icon does in the bar above. */
3720
+ .pr-helper-selection-copy-item {
3721
+ margin-top: 4px;
3722
+ padding-top: 8px;
3723
+ border-top: 1px solid rgba(0, 0, 0, 0.08);
3724
+ color: var(--color-fg-muted, #656d76);
3725
+ }
3726
+
3727
+ .pr-helper-selection-copy-done .pr-helper-selection-copy-item { color: #1a7f37; }
3728
+
3729
+ /* The last action ("Ask a question") is a different kind of thing from the four canned
3730
+ prompts — it opens an empty question rather than pre-filling one. Not in the flat layout,
3731
+ where the last item is Copy and has a rule of its own. */
3732
+ .pr-helper-selection-popover:not(.pr-helper-selection-popover-flat) .pr-helper-selection-menu-item:last-child {
3733
+ border-top: 1px solid rgba(0, 0, 0, 0.08);
3734
+ border-radius: 0 0 6px 6px;
3735
+ margin-top: 4px;
3736
+ padding-top: 8px;
3737
+ color: var(--color-fg-muted, #656d76);
3738
+ }
3739
+
3740
+ /* The guide-progress transitions are feedback, not information — every state they animate is
3741
+ also expressed by a check icon, a Completed pill and a counter, so honouring a reduced-motion
3742
+ preference costs the reviewer nothing. Scoped to these rules rather than a blanket
3743
+ `* { transition: none }` so the sidebar's existing loading/thinking indicators are untouched. */
3744
+ @media (prefers-reduced-motion: reduce) {
3745
+ .pr-helper-guide-section,
3746
+ .pr-helper-progress-bar-fill,
3747
+ .pr-helper-nav-overlay-btn,
3748
+ .pr-helper-selection-tip {
3749
+ transition-duration: 0.01ms;
3750
+ }
3751
+
3752
+ /* The morph is pure feedback — where the sidebar came from, nothing the reviewer needs to read.
3753
+ Collapsing it to an instant open costs them nothing, and the JS still cleans up on the
3754
+ transitionend these durations still fire. */
3755
+ .pr-helper-toggle-button--stretching,
3756
+ .pr-helper-toggle-button--stretching svg,
3757
+ .pr-helper-toggle-button--stretching span,
3758
+ .pr-helper-sidebar--from-rail {
3759
+ transition-duration: 0.01ms;
3760
+ }
3761
+ }
3762
+
3763
+ /* Suggested themes — the Guide's second half.
3764
+ Deliberately quieter than a Guide section card: these are places nobody looked, so they must
3765
+ not compete visually with sections describing code the review actually read. Flat background,
3766
+ no border, no status affordances, nothing clickable. The reviewer should be able to tell the
3767
+ two apart without reading the heading. */
3768
+ .pr-helper-suggested-areas {
3769
+ margin-top: 20px;
3770
+ padding-top: 14px;
3771
+ border-top: 1px solid var(--color-border-muted, #d8dee4);
3772
+ }
3773
+
3774
+ .pr-helper-suggested-areas-heading {
3775
+ margin: 0 0 4px;
3776
+ font-size: 12px;
3777
+ font-weight: 600;
3778
+ color: var(--color-fg-default, #1f2328);
3779
+ }
3780
+
3781
+ .pr-helper-suggested-areas-intro {
3782
+ margin: 0 0 10px;
3783
+ font-size: 11px;
3784
+ line-height: 1.4;
3785
+ color: var(--color-fg-muted, #57606a);
3786
+ }
3787
+
3788
+ .pr-helper-suggested-area {
3789
+ padding: 8px 10px;
3790
+ margin-bottom: 6px;
3791
+ border-radius: 6px;
3792
+ background: var(--color-canvas-subtle, #f6f8fa);
3793
+ }
3794
+
3795
+ .pr-helper-suggested-area-head {
3796
+ display: flex;
3797
+ align-items: baseline;
3798
+ justify-content: space-between;
3799
+ gap: 8px;
3800
+ }
3801
+
3802
+ .pr-helper-suggested-area-title {
3803
+ font-size: 12px;
3804
+ font-weight: 600;
3805
+ color: var(--color-fg-default, #1f2328);
3806
+ }
3807
+
3808
+ .pr-helper-suggested-area-count {
3809
+ flex-shrink: 0;
3810
+ font-size: 11px;
3811
+ font-variant-numeric: tabular-nums;
3812
+ color: var(--color-fg-muted, #57606a);
3813
+ }
3814
+
3815
+ .pr-helper-suggested-area-why,
3816
+ .pr-helper-suggested-area-risk {
3817
+ margin: 4px 0 0;
3818
+ font-size: 11px;
3819
+ line-height: 1.45;
3820
+ color: var(--color-fg-muted, #57606a);
3821
+ }
3822
+
3823
+ /* Amber, matching the Files tab's unavailable badge: something to watch, not something found. */
3824
+ .pr-helper-suggested-area-risk {
3825
+ color: var(--color-attention-fg, #9a6700);
3826
+ }
3827
+
3828
+ /* Muted, unlike the section badges beside it: "Explained" is a note about what the reviewer has
3829
+ already asked, not a claim that the review covered this file. It should read as quieter than a
3830
+ real review badge, because it means less. */
3831
+ .pr-helper-file-badge-explained {
3832
+ display: inline-block;
3833
+ padding: 1px 6px;
3834
+ border-radius: 10px;
3835
+ font-size: 10px;
3836
+ color: var(--color-fg-muted, #57606a);
3837
+ background: var(--color-neutral-subtle, rgba(175, 184, 193, 0.2));
3838
+ }
3839
+
3840
+ .pr-helper-file-explained-focus {
3841
+ font-style: italic;
3842
+ color: var(--color-fg-muted, #57606a);
3843
+ }
3844
+
3845
+ /* Busy state: the label sits beside a real Cancel, since a follow-up never shows the generating
3846
+ view that normally hosts one. */
3847
+ .pr-helper-suggested-area-running {
3848
+ display: flex;
3849
+ align-items: center;
3850
+ gap: 8px;
3851
+ margin-top: 10px;
3852
+ }
3853
+
3854
+ .pr-helper-suggested-area-busy {
3855
+ font-size: 11px;
3856
+ color: var(--color-fg-muted, #57606a);
3857
+ }
3858
+
3859
+ .pr-helper-suggested-area-cancel {
3860
+ padding: 4px 10px;
3861
+ font-size: 11px;
3862
+ font-family: inherit;
3863
+ color: var(--color-danger-fg, #cf222e);
3864
+ background: transparent;
3865
+ border: 1px solid var(--color-border-default, #d0d7de);
3866
+ border-radius: 6px;
3867
+ cursor: pointer;
3868
+ }
3869
+
3870
+ .pr-helper-suggested-area-cancel:hover {
3871
+ border-color: var(--color-danger-fg, #cf222e);
3872
+ }
3873
+
3874
+ /* The action that turns a suggested area into a reviewed one. Deliberately quiet — an area card
3875
+ is an offer, not an outstanding task, and a row of primary buttons would read as a to-do list
3876
+ the reviewer is behind on. It gains weight on hover, where intent is already expressed. */
3877
+ .pr-helper-suggested-area-action {
3878
+ margin: 10px 0 0;
3879
+ padding: 4px 10px;
3880
+ font-size: 11px;
3881
+ font-family: inherit;
3882
+ color: var(--color-accent-fg, #0969da);
3883
+ background: transparent;
3884
+ border: 1px solid var(--color-border-default, #d0d7de);
3885
+ border-radius: 6px;
3886
+ cursor: pointer;
3887
+ }
3888
+
3889
+ .pr-helper-suggested-area-action:hover:not(:disabled) {
3890
+ background: var(--color-accent-subtle, #ddf4ff);
3891
+ border-color: var(--color-accent-fg, #0969da);
3892
+ }
3893
+
3894
+ .pr-helper-suggested-area-action:disabled {
3895
+ color: var(--color-fg-muted, #57606a);
3896
+ cursor: default;
3897
+ }
3898
+
3899
+ .pr-helper-suggested-area-remainder {
3900
+ margin: 8px 0 0;
3901
+ font-size: 11px;
3902
+ line-height: 1.4;
3903
+ color: var(--color-fg-muted, #57606a);
3904
+ }
3905
+
3906
+ /* Issue confidence — only rendered below high, so it always means "this is qualified".
3907
+ Quieter than the severity badge beside it: severity is the finding's weight and stays the
3908
+ card's primary signal, while confidence modifies it. */
3909
+ .pr-helper-issue-confidence {
3910
+ font-size: 10px;
3911
+ font-weight: 600;
3912
+ padding: 1px 6px;
3913
+ border-radius: 10px;
3914
+ white-space: nowrap;
3915
+ background: var(--color-neutral-muted, rgba(175, 184, 193, 0.2));
3916
+ color: var(--color-fg-muted, #57606a);
3917
+ }
3918
+
3919
+ .pr-helper-issue-confidence-low {
3920
+ color: var(--color-attention-fg, #9a6700);
3921
+ }
3922
+
3923
+ /* Provisional risk. Sits inline with the risk label rather than replacing it — the verdict is
3924
+ still the headline, it just stops claiming to be final. */
3925
+ .pr-helper-risk-provisional {
3926
+ font-size: 10px;
3927
+ font-weight: 600;
3928
+ text-transform: uppercase;
3929
+ letter-spacing: 0.04em;
3930
+ padding: 2px 6px;
3931
+ border-radius: 10px;
3932
+ border: 1px solid var(--color-attention-emphasis, #9a6700);
3933
+ color: var(--color-attention-fg, #9a6700);
3934
+ }
3935
+
3936
+ .pr-helper-risk-provisional-reason {
3937
+ margin: 6px 0 0;
3938
+ font-size: 11px;
3939
+ line-height: 1.45;
3940
+ color: var(--color-fg-muted, #57606a);
3941
+ }
3942
+
3943
+ /* Review details — provenance, collapsed.
3944
+ Deliberately the quietest thing on the Overview: it answers "how was this made?", which is a
3945
+ question the reviewer only asks when something looks off. It sits below the suggested path and
3946
+ above Next steps, so the page still ends on what to do rather than on how it was produced. */
3947
+ .pr-helper-review-details {
3948
+ margin: 4px 0 14px;
3949
+ font-size: 12px;
3950
+ color: var(--color-fg-muted, #57606a);
3951
+ }
3952
+
3953
+ .pr-helper-review-details-summary {
3954
+ cursor: pointer;
3955
+ list-style: none;
3956
+ display: inline-flex;
3957
+ align-items: center;
3958
+ gap: 4px;
3959
+ padding: 2px 0;
3960
+ user-select: none;
3961
+ }
3962
+
3963
+ .pr-helper-review-details-summary::-webkit-details-marker { display: none; }
3964
+
3965
+ /* Own marker so it can rotate — the native one cannot be styled consistently across the themes
3966
+ GitHub renders this sidebar inside. */
3967
+ .pr-helper-review-details-summary::before {
3968
+ content: '▶';
3969
+ font-size: 8px;
3970
+ transition: transform 0.12s;
3971
+ }
3972
+
3973
+ .pr-helper-review-details[open] .pr-helper-review-details-summary::before {
3974
+ transform: rotate(90deg);
3975
+ }
3976
+
3977
+ .pr-helper-review-details-summary:hover { color: #9333ea; }
3978
+
3979
+ .pr-helper-review-details-list {
3980
+ margin: 6px 0 0;
3981
+ padding-left: 18px;
3982
+ line-height: 1.5;
3983
+ }
3984
+
3985
+ .pr-helper-review-details-list li { margin-bottom: 3px; }
3986
+
3987
+ /* ── Repository Investigation ───────────────────────────────────────────────
3988
+ The escalation action on issue and question cards, plus the modal that shows the generated
3989
+ prompt. Deliberately a secondary/outlined button, not a new primary style: escalating to a
3990
+ repository agent is one option among several on a finding, not the thing the card is asking
3991
+ the reviewer to do. */
3992
+
3993
+ .pr-helper-issue-actions {
3994
+ display: flex;
3995
+ align-items: center;
3996
+ justify-content: flex-end;
3997
+ gap: 12px;
3998
+ margin-top: 12px;
3999
+ }
4000
+
4001
+ /* margin-left:auto rather than relying on the row's space-between: the jump link beside it is
4002
+ conditional (a question without a file has none), and space-between alone would swing this
4003
+ group to the left edge whenever it is the row's only child. Wraps because three controls do
4004
+ not fit a 320px sidebar on one line. */
4005
+ .pr-helper-question-actions-group {
4006
+ display: flex;
4007
+ align-items: center;
4008
+ flex-wrap: wrap;
4009
+ justify-content: flex-end;
4010
+ gap: 8px;
4011
+ margin-left: auto;
4012
+ }
4013
+
4014
+ /* Same geometry as .pr-helper-question-answer-toggle, so the action reads identically whether it
4015
+ sits alone on an issue card or beside Jump to file / Mark answered on a question card. */
4016
+ .pr-helper-investigate-btn {
4017
+ padding: 5px 10px;
4018
+ border-radius: 6px;
4019
+ border: 1px solid rgba(0, 0, 0, 0.15);
4020
+ background: #ffffff;
4021
+ color: #1f2328;
4022
+ font-size: 12px;
4023
+ font-weight: 600;
4024
+ font-family: inherit;
4025
+ cursor: pointer;
4026
+ white-space: nowrap;
4027
+ }
4028
+
4029
+ .pr-helper-investigate-btn:hover {
4030
+ border-color: #9333ea;
4031
+ color: #9333ea;
4032
+ }
4033
+
4034
+ .pr-helper-prompt-modal {
4035
+ position: fixed;
4036
+ inset: 0;
4037
+ /* Above the sidebar's 999999 — the modal is body-mounted and must sit over it, not behind. */
4038
+ z-index: 1000001;
4039
+ display: flex;
4040
+ align-items: center;
4041
+ justify-content: center;
4042
+ opacity: 0;
4043
+ transition: opacity 0.15s ease;
4044
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
4045
+ }
4046
+
4047
+ .pr-helper-prompt-modal.show { opacity: 1; }
4048
+
4049
+ .pr-helper-prompt-modal-backdrop {
4050
+ position: absolute;
4051
+ inset: 0;
4052
+ background: rgba(0, 0, 0, 0.45);
4053
+ }
4054
+
4055
+ .pr-helper-prompt-modal-card {
4056
+ position: relative;
4057
+ width: min(680px, calc(100vw - 48px));
4058
+ max-height: calc(100vh - 96px);
4059
+ display: flex;
4060
+ flex-direction: column;
4061
+ padding: 20px;
4062
+ background: #ffffff;
4063
+ color: #1f2328;
4064
+ border-radius: 12px;
4065
+ box-shadow: 0 16px 48px rgba(0, 0, 0, 0.28);
4066
+ transform: translateY(8px);
4067
+ transition: transform 0.15s ease;
4068
+ }
4069
+
4070
+ .pr-helper-prompt-modal.show .pr-helper-prompt-modal-card { transform: translateY(0); }
4071
+
4072
+ .pr-helper-prompt-modal-title {
4073
+ margin: 0 0 10px;
4074
+ font-size: 16px;
4075
+ font-weight: 600;
4076
+ }
4077
+
4078
+ .pr-helper-prompt-modal-body {
4079
+ margin: 0 0 8px;
4080
+ font-size: 13px;
4081
+ line-height: 1.5;
4082
+ color: #57606a;
4083
+ }
4084
+
4085
+ /* Monospace and scrollable: this is a prompt about code, and the diff inside it only reads
4086
+ correctly with aligned columns. flex:1 lets it absorb the card's spare height so the actions
4087
+ stay pinned at the bottom on a short viewport. */
4088
+ /* The slot that replaces the textarea when a caller supplies `content`. Duplicated in
4089
+ popup.css with the rest of the modal — see prompt_modal.ts. */
4090
+ .pr-helper-prompt-modal-content {
4091
+ display: flex;
4092
+ flex-direction: column;
4093
+ min-height: 0;
4094
+ flex: 1;
4095
+ margin: 8px 0 12px;
4096
+ }
4097
+
4098
+ .pr-helper-prompt-modal-field {
4099
+ flex: 1;
4100
+ min-height: 220px;
4101
+ margin: 8px 0 16px;
4102
+ padding: 12px;
4103
+ border: 1px solid rgba(0, 0, 0, 0.15);
4104
+ border-radius: 8px;
4105
+ background: #f6f8fa;
4106
+ color: #1f2328;
4107
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
4108
+ font-size: 12px;
4109
+ line-height: 1.5;
4110
+ resize: none;
4111
+ white-space: pre;
4112
+ overflow: auto;
4113
+ }
4114
+
4115
+ .pr-helper-prompt-modal-actions {
4116
+ display: flex;
4117
+ justify-content: flex-end;
4118
+ gap: 8px;
4119
+ }
4120
+
4121
+ .pr-helper-prompt-modal-btn {
4122
+ padding: 8px 16px;
4123
+ border-radius: 6px;
4124
+ border: 1px solid rgba(0, 0, 0, 0.15);
4125
+ background: #ffffff;
4126
+ color: #1f2328;
4127
+ font-size: 13px;
4128
+ font-weight: 600;
4129
+ font-family: inherit;
4130
+ cursor: pointer;
4131
+ }
4132
+
4133
+ .pr-helper-prompt-modal-btn:hover { border-color: rgba(0, 0, 0, 0.3); }
4134
+
4135
+ .pr-helper-prompt-modal-btn-primary {
4136
+ background: #9333ea;
4137
+ border-color: #9333ea;
4138
+ color: #ffffff;
4139
+ }
4140
+
4141
+ .pr-helper-prompt-modal-btn-primary:hover {
4142
+ background: #7c3aed;
4143
+ border-color: #7c3aed;
4144
+ }
4145
+
4146
+ .pr-helper-prompt-modal-btn-done {
4147
+ background: #1a7f37;
4148
+ border-color: #1a7f37;
4149
+ }
4150
+
4151
+ .pr-helper-prompt-modal-btn-done:hover {
4152
+ background: #1a7f37;
4153
+ border-color: #1a7f37;
4154
+ }
4155
+
4156
+ /* ── PR update / analysis staleness ─────────────────────────────────────── */
4157
+
4158
+ /*
4159
+ * (The per-tab inset workaround that used to live here is gone: the staleness block moved out of
4160
+ * the review panel into its own toolbar, so it no longer inherits whatever padding and flex
4161
+ * behaviour a given tab happens to impose on the panel.)
4162
+ */
4163
+
4164
+ /*
4165
+ * The provenance line is not dismissible and is rendered on every tab that shows the current
4166
+ * analysis. It is deliberately quiet when the analysis is current and picks up colour only when
4167
+ * the pull request has moved past it — see ui/components/pr_update_notice.ts.
4168
+ */
4169
+ .pr-helper-analysis-provenance {
4170
+ display: flex;
4171
+ align-items: center;
4172
+ gap: 6px;
4173
+ flex-wrap: wrap;
4174
+ margin: 0 0 10px;
4175
+ padding: 0 2px;
4176
+ /* Body size and the UI font: this is product copy about the analysis, not a debug string. */
4177
+ font-size: 13px;
4178
+ color: var(--color-fg-muted, #57606a);
4179
+ }
4180
+
4181
+ .pr-helper-provenance-label {
4182
+ font-weight: 500;
4183
+ }
4184
+
4185
+ /*
4186
+ * The identifier, and only the identifier, stays monospace — a commit SHA is a technical value and
4187
+ * a proportional font makes it harder to compare against one on GitHub's own page. The pill is what
4188
+ * makes that legible as a *reference* rather than as leaked internals: smaller and lighter than the
4189
+ * phrase beside it, so the row reads "Analysis at «commit»" instead of one undifferentiated string.
4190
+ */
4191
+ .pr-helper-provenance-sha {
4192
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
4193
+ font-size: 11px;
4194
+ padding: 1px 6px;
4195
+ border-radius: 4px;
4196
+ color: var(--color-fg-muted, #57606a);
4197
+ background: var(--color-canvas-subtle, #f6f8fa);
4198
+ border: 1px solid var(--color-border-muted, #d8dee4);
4199
+ }
4200
+
4201
+ /*
4202
+ * Purple, and deliberately not blue — that is the whole point of the colour choice.
4203
+ *
4204
+ * Three actions can appear in this region and they need to rank unambiguously: this one is
4205
+ * proactive and always available, "Analyse new changes" is the real work and only exists when
4206
+ * there is something to analyse, and "Not now" declines. Making this blue would put it in a dead
4207
+ * heat with the primary CTA, and its previous neutral-grey-with-a-blue-hover did both wrong at
4208
+ * once — passive at rest, then competing on hover.
4209
+ *
4210
+ * Purple is the product's own accent, so a tint of it reads as "ours, secondary" without borrowing
4211
+ * the primary action's colour. Every value here already exists in this file: the .06 fill is the
4212
+ * Suggested Review Path card's, .12 is the header chip's hover, .25 is the resize handle's, and
4213
+ * #9333ea is the accent itself.
4214
+ */
4215
+ .pr-helper-provenance-check {
4216
+ margin-left: auto;
4217
+ padding: 1px 8px;
4218
+ font-size: 11px;
4219
+ font-family: inherit;
4220
+ font-weight: 500;
4221
+ color: #9333ea;
4222
+ background: rgba(147, 51, 234, 0.06);
4223
+ border: 1px solid rgba(147, 51, 234, 0.25);
4224
+ border-radius: 999px;
4225
+ cursor: pointer;
4226
+ white-space: nowrap;
4227
+ }
4228
+
4229
+ .pr-helper-provenance-check:hover:not(:disabled) {
4230
+ background: rgba(147, 51, 234, 0.12);
4231
+ border-color: #9333ea;
4232
+ }
4233
+
4234
+ .pr-helper-provenance-check:disabled {
4235
+ cursor: default;
4236
+ opacity: 0.65;
4237
+ }
4238
+
4239
+ .pr-helper-provenance-stale {
4240
+ display: inline-flex;
4241
+ align-items: center;
4242
+ padding: 1px 6px;
4243
+ border-radius: 999px;
4244
+ font-weight: 600;
4245
+ color: var(--color-attention-fg, #9a6700);
4246
+ background: var(--color-attention-subtle, #fff8c5);
4247
+ }
4248
+
4249
+ /*
4250
+ * Amber, but as an accent rather than as the whole component.
4251
+ *
4252
+ * This used GitHub's `--color-attention-subtle`, which is a *solid* pale yellow (#fff8c5) — next
4253
+ * to the Suggested Review Path card's 6% purple tint it read as a warning rather than as one more
4254
+ * surface in the sidebar's palette, and it competed with the amber pill directly above it saying
4255
+ * the same thing. Both were shouting the same message.
4256
+ *
4257
+ * So it now follows the idiom the rest of this file already uses for soft surfaces: the hue at a
4258
+ * low alpha over the canvas, matching `.pr-helper-suggested-path` (rgba(147,51,234,.06) / .2).
4259
+ * The triplet is the same amber as the medium-risk pill, so this state and the risk indicator are
4260
+ * visibly the same colour rather than two hand-picked yellows.
4261
+ *
4262
+ * Alpha sits slightly above the purple card's because yellow carries far less contrast against
4263
+ * white at equal alpha — 6% amber is effectively invisible. This is the point where it still reads
4264
+ * as deliberately tinted without becoming the loudest thing on screen.
4265
+ */
4266
+ .pr-helper-update-notice {
4267
+ display: flex;
4268
+ align-items: center;
4269
+ justify-content: space-between;
4270
+ gap: 12px;
4271
+ flex-wrap: wrap;
4272
+ margin: 0 0 12px;
4273
+ padding: 10px 12px;
4274
+ border: 1px solid rgba(240, 178, 50, 0.32);
4275
+ border-radius: 8px;
4276
+ background: rgba(240, 178, 50, 0.1);
4277
+ }
4278
+
4279
+ .pr-helper-update-notice-failed {
4280
+ border-color: var(--color-border-default, #d0d7de);
4281
+ background: var(--color-canvas-subtle, #f6f8fa);
4282
+ }
4283
+
4284
+ .pr-helper-update-notice-running {
4285
+ border-color: var(--color-border-default, #d0d7de);
4286
+ background: var(--color-canvas-subtle, #f6f8fa);
4287
+ }
4288
+
4289
+ .pr-helper-update-notice-body {
4290
+ display: flex;
4291
+ flex-direction: column;
4292
+ gap: 2px;
4293
+ min-width: 0;
4294
+ }
4295
+
4296
+ .pr-helper-update-notice-title {
4297
+ font-size: 12px;
4298
+ font-weight: 600;
4299
+ color: var(--color-fg-default, #1f2328);
4300
+ }
4301
+
4302
+ .pr-helper-update-notice-detail {
4303
+ font-size: 11px;
4304
+ color: var(--color-fg-muted, #57606a);
4305
+ }
4306
+
4307
+ .pr-helper-update-notice-actions {
4308
+ display: flex;
4309
+ align-items: center;
4310
+ gap: 6px;
4311
+ }
4312
+
4313
+ .pr-helper-update-notice-action {
4314
+ padding: 4px 10px;
4315
+ font-size: 11px;
4316
+ font-family: inherit;
4317
+ font-weight: 600;
4318
+ color: #ffffff;
4319
+ background: var(--color-accent-fg, #0969da);
4320
+ border: 1px solid var(--color-accent-fg, #0969da);
4321
+ border-radius: 6px;
4322
+ cursor: pointer;
4323
+ white-space: nowrap;
4324
+ }
4325
+
4326
+ .pr-helper-update-notice-action:hover {
4327
+ filter: brightness(0.94);
4328
+ }
4329
+
4330
+ .pr-helper-update-notice-dismiss,
4331
+ .pr-helper-update-notice-cancel {
4332
+ padding: 4px 10px;
4333
+ font-size: 11px;
4334
+ font-family: inherit;
4335
+ color: var(--color-fg-muted, #57606a);
4336
+ background: transparent;
4337
+ border: 1px solid var(--color-border-default, #d0d7de);
4338
+ border-radius: 6px;
4339
+ cursor: pointer;
4340
+ white-space: nowrap;
4341
+ }
4342
+
4343
+ .pr-helper-update-notice-dismiss:hover,
4344
+ .pr-helper-update-notice-cancel:hover {
4345
+ background: var(--color-canvas-default, #ffffff);
4346
+ color: var(--color-fg-default, #1f2328);
4347
+ }
4348
+
4349
+ /* ── Reconciled finding statuses ────────────────────────────────────────── */
4350
+
4351
+ .pr-helper-finding-status {
4352
+ font-size: 11px;
4353
+ font-weight: 700;
4354
+ text-transform: uppercase;
4355
+ letter-spacing: 0.02em;
4356
+ padding: 1px 6px;
4357
+ border-radius: 999px;
4358
+ white-space: nowrap;
4359
+ }
4360
+
4361
+ .pr-helper-finding-status-resolved {
4362
+ color: var(--color-success-fg, #1a7f37);
4363
+ background: var(--color-success-subtle, #dafbe1);
4364
+ }
4365
+
4366
+ .pr-helper-finding-status-still-open {
4367
+ color: var(--color-danger-fg, #cf222e);
4368
+ background: var(--color-danger-subtle, #ffebe9);
4369
+ }
4370
+
4371
+ .pr-helper-finding-status-affected {
4372
+ color: var(--color-attention-fg, #9a6700);
4373
+ background: var(--color-attention-subtle, #fff8c5);
4374
+ }
4375
+
4376
+ .pr-helper-finding-status-new {
4377
+ color: var(--color-accent-fg, #0969da);
4378
+ background: var(--color-accent-subtle, #ddf4ff);
4379
+ }
4380
+
4381
+ /*
4382
+ * A resolved finding is kept on screen — the reviewer needs to see that it was dealt with — but
4383
+ * muted, so the findings still needing attention are what the eye lands on first.
4384
+ */
4385
+ .pr-helper-issue-resolved {
4386
+ opacity: 0.72;
4387
+ }
4388
+
4389
+ .pr-helper-reconciliation-note {
4390
+ margin: 6px 0 0;
4391
+ padding-left: 8px;
4392
+ border-left: 2px solid var(--color-border-muted, #d8dee4);
4393
+ font-size: 11px;
4394
+ font-style: italic;
4395
+ color: var(--color-fg-muted, #57606a);
4396
+ }
4397
+
4398
+ /* ── History: analyses that follow an earlier one ───────────────────────── */
4399
+
4400
+ /*
4401
+ * An update carries no indent or edge marker of its own.
4402
+ *
4403
+ * It had both, and they cost more than they bought: the indent narrowed the card so it no longer
4404
+ * lined up with the analyses above and below it, and the coloured left edge replaced the card's own
4405
+ * border on one side so the outline changed weight halfway round. Both were saying what the
4406
+ * "New changes …" row below already says in words, which is the same double-signalling the update
4407
+ * banner and the staleness pill were guilty of. Stated once, in the row, is enough.
4408
+ */
4409
+ .pr-helper-history-delta {
4410
+ display: flex;
4411
+ align-items: center;
4412
+ gap: 6px;
4413
+ flex-wrap: wrap;
4414
+ margin-top: 6px;
4415
+ font-size: 10px;
4416
+ color: var(--color-fg-muted, #57606a);
4417
+ }
4418
+
4419
+ .pr-helper-history-delta-label {
4420
+ font-weight: 600;
4421
+ text-transform: uppercase;
4422
+ letter-spacing: 0.03em;
4423
+ }
4424
+
4425
+ .pr-helper-history-delta-range {
4426
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
4427
+ }
4428
+
4429
+ .pr-helper-history-delta-partial {
4430
+ padding: 0 5px;
4431
+ border-radius: 999px;
4432
+ font-weight: 600;
4433
+ color: var(--color-attention-fg, #9a6700);
4434
+ background: var(--color-attention-subtle, #fff8c5);
4435
+ }
4436
+
4437
+ /* ── Repository investigation: the inbound half ─────────────────────────── */
4438
+
4439
+ /*
4440
+ * The paired actions. "Investigate Repository" and "Add Investigation" are the two halves of one
4441
+ * loop and sit together on the finding they concern, so they share .pr-helper-investigate-btn's
4442
+ * geometry exactly — a second control with its own weight would read as an unrelated action that
4443
+ * happened to land nearby.
4444
+ *
4445
+ * The add half is tinted rather than filled. It is the step the reviewer is being invited towards
4446
+ * once a prompt exists, which deserves to be findable; a solid button would outrank the finding's
4447
+ * own text, and the card is a finding first and a control surface second.
4448
+ */
4449
+ .pr-helper-investigate-btn-add {
4450
+ border-color: rgba(147, 51, 234, 0.4);
4451
+ color: #7e22ce;
4452
+ background: rgba(147, 51, 234, 0.06);
4453
+ }
4454
+
4455
+ .pr-helper-investigate-btn-add:hover {
4456
+ border-color: #9333ea;
4457
+ background: rgba(147, 51, 234, 0.12);
4458
+ color: #7e22ce;
4459
+ }
4460
+
4461
+ /* Reports rather than invites — see renderInvestigationActions. Deliberately not a disabled
4462
+ <button>: it is not an action the reviewer is being denied, it is a statement of fact. */
4463
+ .pr-helper-investigate-added {
4464
+ display: inline-flex;
4465
+ align-items: center;
4466
+ padding: 5px 10px;
4467
+ font-size: 12px;
4468
+ font-weight: 600;
4469
+ color: var(--color-success-fg, #1a7f37);
4470
+ white-space: nowrap;
4471
+ }
4472
+
4473
+ /* Same pill geometry as .pr-helper-finding-status — the two axes sit side by side in one badge row
4474
+ and must read as peers, since a finding can legitimately carry both. */
4475
+ .pr-helper-investigation-impact {
4476
+ font-size: 11px;
4477
+ font-weight: 700;
4478
+ text-transform: uppercase;
4479
+ letter-spacing: 0.02em;
4480
+ padding: 1px 6px;
4481
+ border-radius: 999px;
4482
+ white-space: nowrap;
4483
+ }
4484
+
4485
+ /* Purple throughout, matching the Repository AI accent used on the investigate buttons: these
4486
+ verdicts all came from outside the diff, and the colour is what says so at a glance. Weight
4487
+ within the family tracks how much the evidence moved the finding. */
4488
+ .pr-helper-investigation-impact-confirmed {
4489
+ color: #6b21a8;
4490
+ background: rgba(147, 51, 234, 0.14);
4491
+ }
4492
+
4493
+ /* Not struck through: the pill is the verdict, not the thing that was disproven. Striking a status
4494
+ label reads as the status having been withdrawn. The strike goes on the claim instead — see
4495
+ .pr-helper-issue-investigation-refuted below, and the Files tab, which already does this. */
4496
+ .pr-helper-investigation-impact-refuted {
4497
+ color: var(--color-fg-muted, #57606a);
4498
+ background: var(--color-neutral-muted, rgba(175, 184, 193, 0.2));
4499
+ }
4500
+
4501
+ .pr-helper-investigation-impact-revised {
4502
+ color: #7e22ce;
4503
+ background: rgba(147, 51, 234, 0.1);
4504
+ }
4505
+
4506
+ .pr-helper-investigation-impact-unchanged {
4507
+ color: var(--color-fg-muted, #57606a);
4508
+ background: var(--color-neutral-muted, rgba(175, 184, 193, 0.2));
4509
+ }
4510
+
4511
+ /*
4512
+ * A refuted finding stays on the list and is muted, exactly as a resolved one is: the reviewer
4513
+ * needs to see that the concern was investigated and does not hold, and deleting it would destroy
4514
+ * the audit trail this whole loop exists to build.
4515
+ *
4516
+ * Only the claim is struck — the issue title, or the question that was asked. Everything below it
4517
+ * is why we now know better: the answer from the investigation, the note, the provenance. That is
4518
+ * evidence the reviewer escalated for, and it is not retracted by the thing it disproved. Dimming
4519
+ * the whole card would bury the payoff of the loop along with the claim.
4520
+ */
4521
+ .pr-helper-issue-investigation-refuted .pr-helper-issue-title {
4522
+ text-decoration: line-through;
4523
+ color: var(--color-fg-muted, #57606a);
4524
+ }
4525
+
4526
+ /* `major → minor`. Monospace so the two grades line up as values being compared rather than as
4527
+ prose, and the old one struck through so which is current needs no explanation. */
4528
+ .pr-helper-investigation-revision {
4529
+ margin: 4px 0 0;
4530
+ font-size: 11px;
4531
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
4532
+ color: var(--color-fg-muted, #57606a);
4533
+ }
4534
+
4535
+ .pr-helper-investigation-revision-from {
4536
+ text-decoration: line-through;
4537
+ }
4538
+
4539
+ .pr-helper-investigation-revision-to {
4540
+ font-weight: 700;
4541
+ color: #7e22ce;
4542
+ }
4543
+
4544
+ /* Matches .pr-helper-reconciliation-note, with a purple rule instead of grey — same kind of thing
4545
+ (a one-line reason from the model), different provenance. */
4546
+ .pr-helper-investigation-note {
4547
+ margin: 6px 0 0;
4548
+ padding-left: 8px;
4549
+ border-left: 2px solid rgba(147, 51, 234, 0.35);
4550
+ font-size: 11px;
4551
+ font-style: italic;
4552
+ color: var(--color-fg-muted, #57606a);
4553
+ }
4554
+
4555
+ /* The answer to an open question is not a footnote — it is what the reviewer escalated for. Set at
4556
+ body weight with a solid rule, unlike the italic muted note above. */
4557
+ .pr-helper-investigation-answer {
4558
+ padding-left: 8px;
4559
+ border-left: 2px solid #9333ea;
4560
+ }
4561
+
4562
+ .pr-helper-investigation-provenance {
4563
+ display: flex;
4564
+ align-items: center;
4565
+ gap: 6px;
4566
+ flex-wrap: wrap;
4567
+ margin: 8px 0 0;
4568
+ font-size: 10px;
4569
+ color: var(--color-fg-muted, #57606a);
4570
+ }
4571
+
4572
+ .pr-helper-investigation-provenance-label {
4573
+ font-weight: 600;
4574
+ text-transform: uppercase;
4575
+ letter-spacing: 0.03em;
4576
+ }
4577
+
4578
+ .pr-helper-investigation-provenance-agent {
4579
+ font-weight: 600;
4580
+ color: #7e22ce;
4581
+ }
4582
+
4583
+ .pr-helper-investigation-view {
4584
+ padding: 0;
4585
+ border: 0;
4586
+ background: none;
4587
+ font-family: inherit;
4588
+ font-size: 10px;
4589
+ color: var(--color-accent-fg, #0969da);
4590
+ cursor: pointer;
4591
+ text-decoration: underline;
4592
+ }
4593
+
4594
+ /* History row. Same geometry as .pr-helper-history-delta, and for the same reason it carries no
4595
+ indent or edge marker: the row says in words what kind of analysis this is. */
4596
+ .pr-helper-history-investigation {
4597
+ display: flex;
4598
+ align-items: center;
4599
+ gap: 6px;
4600
+ flex-wrap: wrap;
4601
+ margin-top: 6px;
4602
+ font-size: 10px;
4603
+ color: var(--color-fg-muted, #57606a);
4604
+ }
4605
+
4606
+ .pr-helper-history-investigation-label {
4607
+ font-weight: 600;
4608
+ text-transform: uppercase;
4609
+ letter-spacing: 0.03em;
4610
+ }
4611
+
4612
+ .pr-helper-history-investigation-agent {
4613
+ font-weight: 600;
4614
+ color: #7e22ce;
4615
+ }
4616
+
4617
+ /*
4618
+ * The Add Investigation modal's content slot.
4619
+ *
4620
+ * Sidebar-only, unlike the .pr-helper-prompt-modal shell above which is duplicated into popup.css.
4621
+ * The shell is shared because both surfaces open modals; this content is not, because findings only
4622
+ * exist in the sidebar and the popup has no way to reach it. Same rule the context picker follows
4623
+ * in reverse — its styles live only in popup.css, where that surface is reachable.
4624
+ */
4625
+ .pr-helper-add-investigation {
4626
+ display: flex;
4627
+ flex-direction: column;
4628
+ gap: 6px;
4629
+ min-height: 0;
4630
+ }
4631
+
4632
+ .pr-helper-add-investigation-label {
4633
+ font-size: 11px;
4634
+ font-weight: 600;
4635
+ text-transform: uppercase;
4636
+ letter-spacing: 0.03em;
4637
+ color: var(--color-fg-muted, #57606a);
4638
+ }
4639
+
4640
+ .pr-helper-add-investigation-select {
4641
+ padding: 6px 8px;
4642
+ border: 1px solid var(--color-border-default, rgba(0, 0, 0, 0.15));
4643
+ border-radius: 6px;
4644
+ background: var(--color-canvas-default, #ffffff);
4645
+ color: var(--color-fg-default, #1f2328);
4646
+ font-family: inherit;
4647
+ font-size: 13px;
4648
+ }
4649
+
4650
+ /* Monospace and generous, because an agent's report is structured text with file paths and line
4651
+ references in it — proportional type turns those into something the reviewer has to decode. */
4652
+ .pr-helper-add-investigation-field {
4653
+ flex: 1;
4654
+ min-height: 220px;
4655
+ padding: 10px;
4656
+ border: 1px solid var(--color-border-default, rgba(0, 0, 0, 0.15));
4657
+ border-radius: 6px;
4658
+ background: var(--color-canvas-default, #ffffff);
4659
+ color: var(--color-fg-default, #1f2328);
4660
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
4661
+ font-size: 12px;
4662
+ line-height: 1.5;
4663
+ resize: vertical;
4664
+ }
4665
+
4666
+ .pr-helper-add-investigation-error {
4667
+ margin: 0;
4668
+ font-size: 12px;
4669
+ color: var(--color-danger-fg, #cf222e);
4670
+ }
4671
+
4672
+ /* The reminder of which finding sent them out, so the reviewer can tell at a glance that they are
4673
+ pasting into the right place — the one thing a paste box cannot otherwise confirm. */
4674
+ .pr-helper-add-investigation-origin {
4675
+ margin: 0;
4676
+ font-size: 11px;
4677
+ color: var(--color-fg-muted, #57606a);
4678
+ }
4679
+
4680
+ .pr-helper-add-investigation-viewprompt {
4681
+ padding: 0;
4682
+ border: 0;
4683
+ background: none;
4684
+ font-family: inherit;
4685
+ font-size: 11px;
4686
+ color: var(--color-accent-fg, #0969da);
4687
+ cursor: pointer;
4688
+ text-decoration: underline;
4689
+ }
4690
+
4691
+ /*
4692
+ * The completed-investigation report viewer (investigation_output_modal.ts).
4693
+ *
4694
+ * Sidebar-only for the same reason the Add Investigation slot above is: it is reached from a
4695
+ * finding's provenance line, and findings do not exist in the popup.
4696
+ *
4697
+ * The card is 680px and the content is a document, so this is the one prompt-modal surface that
4698
+ * sets prose rather than a field: proportional type, left-aligned, scrolling inside the card so the
4699
+ * action row stays pinned. The report is a scroll container in its own right rather than letting
4700
+ * the slot scroll, so the header stays with the text it describes.
4701
+ */
4702
+ .pr-helper-investigation-report {
4703
+ /* Overrides the slot's `display:flex`. Every other content slot lays out controls, so a flex
4704
+ column is right for them; this one is a document, and as flex items its paragraphs would
4705
+ shrink to fit the constrained height and clip rather than scroll. */
4706
+ display: block;
4707
+ min-height: 0;
4708
+ max-height: 60vh;
4709
+ overflow-y: auto;
4710
+ padding-right: 4px;
4711
+ font-size: 13px;
4712
+ line-height: 1.55;
4713
+ color: var(--color-fg-default, #1f2328);
4714
+ }
4715
+
4716
+ /* Focus is visible because the report is keyboard-scrollable (tabIndex 0 in the module) — an
4717
+ outline is the only thing telling a keyboard user the arrow keys now move this panel. */
4718
+ .pr-helper-investigation-report:focus-visible {
4719
+ outline: 2px solid var(--color-accent-fg, #0969da);
4720
+ outline-offset: 2px;
4721
+ border-radius: 4px;
4722
+ }
4723
+
4724
+ .pr-helper-investigation-report-meta {
4725
+ display: flex;
4726
+ align-items: baseline;
4727
+ gap: 8px;
4728
+ flex-wrap: wrap;
4729
+ margin: 0;
4730
+ font-size: 11px;
4731
+ color: var(--color-fg-muted, #57606a);
4732
+ }
4733
+
4734
+ /* Same purple and weight as .pr-helper-investigation-provenance-agent, so the line the reviewer
4735
+ clicked and the line at the top of what opened read as one object rather than two labels. */
4736
+ .pr-helper-investigation-report-agent {
4737
+ font-weight: 600;
4738
+ color: #7e22ce;
4739
+ }
4740
+
4741
+ .pr-helper-investigation-report-origin {
4742
+ margin: 4px 0 0;
4743
+ font-size: 11px;
4744
+ color: var(--color-fg-muted, #57606a);
4745
+ }
4746
+
4747
+ .pr-helper-investigation-report-rule {
4748
+ margin: 12px 0;
4749
+ border: none;
4750
+ border-top: 1px solid var(--color-border-default, #d0d7de);
4751
+ }
4752
+
4753
+ .pr-helper-investigation-report-empty {
4754
+ margin: 0;
4755
+ font-size: 13px;
4756
+ font-style: italic;
4757
+ color: var(--color-fg-muted, #57606a);
4758
+ }
4759
+
4760
+ /* Inline code chips, repeated here rather than reused.
4761
+ *
4762
+ * The chip rule the rest of the extension uses is scoped `.pr-helper-sidebar code`, and this modal
4763
+ * is mounted on document.body to escape the sidebar's stacking context — so it never matched, and
4764
+ * an agent's backticked file paths arrived as bare monospace with no chip. Identical declarations
4765
+ * on purpose: the two must look the same, since the finding's title appears in both places.
4766
+ *
4767
+ * `.pr-helper-markdown pre code` (one class, two types) outranks this, so code inside a fenced
4768
+ * block still gets its background and padding stripped rather than double-chipped. */
4769
+ .pr-helper-prompt-modal-report code {
4770
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
4771
+ font-size: 0.9em;
4772
+ background: var(--color-neutral-muted, rgba(175, 184, 193, 0.2));
4773
+ border-radius: 4px;
4774
+ padding: 0.1em 0.4em;
4775
+ /* Long paths must not widen the card. Breaking mid-token is right for a chip holding
4776
+ `ocean/deployment/templates/ocean-environment-variables-secret.yaml` and wrong for prose,
4777
+ which is why it sits on code rather than on the report. */
4778
+ overflow-wrap: anywhere;
4779
+ }
4780
+
4781
+ /* .pr-helper-markdown is sized for the 13px assistant bubble in a narrow sidebar (see its block
4782
+ above). Here it has the full card width, so headings get back the step the bubble gave up —
4783
+ an agent's numbered answers are the report's structure and need to read as headings. Nothing
4784
+ else is overridden: the colours, code blocks and lists were already right. */
4785
+ .pr-helper-prompt-modal-report .pr-helper-markdown h4 { font-size: 14px; }
4786
+ .pr-helper-prompt-modal-report .pr-helper-markdown h5 { font-size: 13px; }
4787
+ .pr-helper-prompt-modal-report .pr-helper-markdown h6 { font-size: 12.5px; }
4788
+
4789
+ /* Mirrors .pr-helper-investigate-added's geometry so the inbound slot keeps its width through the
4790
+ request → applying → added transitions, rather than the button row reflowing twice mid-run.
4791
+ That geometry is why this keeps its own type treatment rather than adopting the smaller, muted
4792
+ one "Analysing…" uses: the spinner is the part that had to match, and matching the font size too
4793
+ would shrink the slot and reintroduce the reflow this was built to avoid. */
4794
+ .pr-helper-investigate-applying {
4795
+ display: inline-flex;
4796
+ align-items: center;
4797
+ padding: 5px 10px;
4798
+ font-size: 12px;
4799
+ font-weight: 600;
4800
+ color: #7e22ce;
4801
+ white-space: nowrap;
4802
+ }