prreviewbuddy 0.45.0 → 0.48.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.
package/static/shell.css CHANGED
@@ -4,9 +4,11 @@
4
4
  * content itself. This file owns only the full-page furniture that a 460px column beside a GitHub
5
5
  * diff never needed: the rail, the document pane, the assistant.
6
6
  *
7
- * The light/dark split is structural, not decorative. Dark is the operator's furniture — where you
8
- * navigate and where you ask. Light is the review document. Moving between them tells you whether
9
- * you are reading the review or working the tool. */
7
+ * The split between furniture and document is structural, not decorative. The furniture is where
8
+ * you navigate and where you ask; the document is the review. Moving between them tells you whether
9
+ * you are reading the review or working the tool. In the dark theme the split is dark against light.
10
+ * In the light theme it is grey chrome around a white document, and the document stays the
11
+ * brightest thing on the screen. The document pane is light in both: its palette is `.prb-doc`'s. */
10
12
 
11
13
  @font-face {
12
14
  font-family: 'Comfortaa';
@@ -16,33 +18,92 @@
16
18
  }
17
19
 
18
20
  :root {
19
- --bg: #0b0f17;
20
- --surface: #121826;
21
- --surface-2: #1b2232;
21
+ /* ── The theme ──
22
+ `data-theme` on <html> is rendered by the server from the reviewer's choice (see theme.ts).
23
+ System leaves both schemes open, and the browser picks from the device, live. */
24
+ color-scheme: light dark;
25
+
26
+ /* Every colour the furniture uses is one of these, and each is a `light-dark(light, dark)` pair
27
+ that the browser resolves from `color-scheme`. That is what lets System follow the device with
28
+ no listener and no reload, and what keeps both themes in one table rather than two that drift.
29
+ A furniture rule names a token and never a colour: shell_theme.test.ts holds that line, so a
30
+ new shade has to be added here, beside its other half, or not at all.
31
+
32
+ Light values are held to 4.5:1 for every piece of informational text on the palest surface it
33
+ sits on (shell_theme.test.ts computes it). That is why muted is darker than a light theme's
34
+ usual grey, and secondary darker again, so the three steps still read as three. */
35
+ --bg: light-dark(#f3f4f7, #0b0f17);
36
+ --surface: light-dark(#f8f9fb, #121826);
37
+ /* Cards, menus, dialogs and the bulk bar: the things that sit on top. One value with --surface in
38
+ the dark theme; white in the light one, which is what makes a card read as lifted off the grey. */
39
+ --surface-raised: light-dark(#ffffff, #121826);
40
+ --surface-2: light-dark(#eceef2, #1b2232);
41
+ /* A row under the pointer in the rail. The dark theme lifts it to --surface; the light theme
42
+ darkens it, because a paler row on a pale rail is invisible. */
43
+ --surface-hover: light-dark(#e8ebef, #121826);
22
44
  --brand: #9333ea;
23
- --brand-hover: #7c3aed;
24
- --brand-muted: rgba(147, 51, 234, 0.15);
25
- --text-primary: #f8fafc;
26
- --text-secondary: #94a3b8;
27
- --text-muted: #64748b;
28
- --border: rgba(255, 255, 255, 0.08);
45
+ --brand-hover: light-dark(#7427e8, #7c3aed);
46
+ --brand-muted: light-dark(#f3e8ff, rgba(147, 51, 234, 0.15));
47
+ /* Brand-coloured words on a light or tinted ground, where #9333ea itself is too pale to read. */
48
+ --brand-ink: light-dark(#6b21a8, #c4a5f5);
49
+ /* Text and marks on a filled --brand control. White in both, because the fill is the same. */
50
+ --on-brand: #ffffff;
51
+ --on-brand-quiet: rgba(255, 255, 255, 0.8);
52
+ --text-primary: light-dark(#20242a, #f8fafc);
53
+ --text-secondary: light-dark(#4b5462, #94a3b8);
54
+ --text-muted: light-dark(#626b79, #64748b);
55
+ --link: light-dark(#7e22ce, #c4b5fd);
56
+ --border: light-dark(#e2e5ea, rgba(255, 255, 255, 0.08));
29
57
  /* Hover used to reach for the brand. Purple then marked the row you happened to be over as
30
58
  loudly as the row you had chosen, so it stopped meaning "chosen". Hover gets a brighter
31
59
  neutral; purple keeps focus, selection and the primary action. */
32
- --border-strong: rgba(255, 255, 255, 0.22);
33
- --danger: #f87171;
34
- --warning: #fbbf24;
35
- --success: #4ade80;
36
- --wordmark-accent:#a855f7;
60
+ --border-strong: light-dark(#c9ced6, rgba(255, 255, 255, 0.22));
61
+ /* A faint wash for a row under the pointer, or the row you are on, over whatever is behind it. */
62
+ --wash: light-dark(rgba(15, 23, 42, 0.04), rgba(255, 255, 255, 0.04));
63
+ --wash-strong: light-dark(rgba(15, 23, 42, 0.06), rgba(255, 255, 255, 0.06));
64
+ --backdrop: light-dark(rgba(15, 23, 42, 0.32), rgba(2, 6, 23, 0.55));
65
+ --shadow-soft: light-dark(rgba(15, 23, 42, 0.08), rgba(0, 0, 0, 0.12));
66
+ --shadow-float: light-dark(rgba(15, 23, 42, 0.14), rgba(0, 0, 0, 0.36));
67
+ --bg-elevated: light-dark(#ffffff, #1f1f24);
68
+
69
+ /* Status. The bare name is a mark or a word on a plain surface; -subtle is a tinted ground;
70
+ -ink is the word written on that ground; -border outlines it. */
71
+ --danger: light-dark(#c12c35, #f87171);
72
+ --danger-subtle: light-dark(#fdedee, rgba(207, 34, 46, 0.16));
73
+ --danger-ink: light-dark(#a40e26, #ff9b9b);
74
+ --danger-ink-strong: light-dark(#86061d, #ffb3b3);
75
+ --danger-border: light-dark(rgba(193, 44, 53, 0.45), rgba(207, 34, 46, 0.55));
76
+ /* The notice that says the server has stopped: a solid band, not a tint. */
77
+ --danger-band: light-dark(#fdedee, #4a1d1d);
78
+ --danger-band-ink:light-dark(#7a1f26, #ffd9d9);
79
+ --warning: light-dark(#8a5c00, #fbbf24);
80
+ --warning-subtle: light-dark(#fff5d6, rgba(210, 153, 34, 0.16));
81
+ --warning-ink: light-dark(#7a5200, #ffd18a);
82
+ --warning-wash: light-dark(rgba(212, 167, 44, 0.16), rgba(251, 191, 36, 0.1));
83
+ --warning-wash-faint: light-dark(rgba(212, 167, 44, 0.03), rgba(251, 191, 36, 0.02));
84
+ --success: light-dark(#13733a, #4ade80);
85
+ --success-subtle: light-dark(#eaf8ef, rgba(46, 160, 67, 0.14));
86
+ --success-ink: light-dark(#116329, #7ee2a8);
87
+ --success-border: light-dark(rgba(19, 115, 58, 0.4), rgba(46, 160, 67, 0.55));
88
+ --neutral-subtle: light-dark(rgba(15, 23, 42, 0.06), rgba(139, 148, 158, 0.16));
89
+ --merged-subtle: light-dark(#f3e8ff, rgba(163, 113, 247, 0.18));
90
+ --merged-ink: light-dark(#6b21a8, #d2a8ff);
91
+ --unread-wash: light-dark(rgba(147, 51, 234, 0.06), rgba(139, 92, 246, 0.09));
92
+ /* The three attention dots in the rail. Marks, not words, so 3:1 is their bar. */
93
+ --attention-high: light-dark(#c12c35, #f87171);
94
+ --attention-medium: light-dark(#c28a00, #f0b232);
95
+ --attention-low: light-dark(#aab1bc, #475569);
96
+
97
+ --wordmark-accent:light-dark(#7e22ce, #a855f7);
37
98
  --wordmark-ink: var(--text-primary);
38
99
 
39
100
  /* Scrollbars, in the brand's purple rather than the OS grey, and one pair per surface.
40
- The shell is near-black and the document pane is near-white, so a single colour cannot serve
41
- both: the lighter `#a855f7` is what stays legible on the dark furniture, and the deeper
42
- `#9333ea` is what has contrast against the pane. Named by surface so putting one on the wrong
43
- side reads as a mistake at the call site. */
44
- --scroll-shell: rgba(168, 85, 247, 0.45);
45
- --scroll-shell-hover: rgba(168, 85, 247, 0.75);
101
+ The dark furniture is near-black and the document pane is near-white, so one colour cannot
102
+ serve both: the lighter `#a855f7` is what stays legible on dark, and the deeper `#9333ea` is
103
+ what has contrast against light, which in the light theme is the furniture too. Named by
104
+ surface so putting one on the wrong side reads as a mistake at the call site. */
105
+ --scroll-shell: light-dark(rgba(147, 51, 234, 0.32), rgba(168, 85, 247, 0.45));
106
+ --scroll-shell-hover: light-dark(rgba(147, 51, 234, 0.58), rgba(168, 85, 247, 0.75));
46
107
  --scroll-pane: rgba(147, 51, 234, 0.32);
47
108
  --scroll-pane-hover: rgba(147, 51, 234, 0.58);
48
109
  --scroll-width: 8px;
@@ -102,6 +163,10 @@
102
163
  --strip-height: 36px;
103
164
  }
104
165
 
166
+ /* A stated theme pins the scheme, so the device no longer decides. */
167
+ :root[data-theme='light'] { color-scheme: light; }
168
+ :root[data-theme='dark'] { color-scheme: dark; }
169
+
105
170
  * { box-sizing: border-box; }
106
171
 
107
172
  html, body {
@@ -500,11 +565,11 @@ body[data-restarting] .prb-body { opacity: 0.55; pointer-events: none; }
500
565
  padding: 22px 24px;
501
566
  border: 1px solid var(--border);
502
567
  border-radius: 12px;
503
- background: var(--surface);
568
+ background: var(--surface-raised);
504
569
  color: var(--text-primary);
505
570
  font: inherit;
506
571
  }
507
- .prb-dialog::backdrop { background: rgba(2, 6, 23, 0.55); }
572
+ .prb-dialog::backdrop { background: var(--backdrop); }
508
573
  .prb-dialog h2 { margin: 0 0 10px; font-size: var(--t-section); }
509
574
  .prb-dialog p { margin: 0 0 10px; font-size: var(--t-body); line-height: var(--lh-body); color: var(--text-secondary); }
510
575
  .prb-dialog-reassure { color: var(--text-tertiary, var(--text-secondary)); }
@@ -523,9 +588,9 @@ body[data-restarting] .prb-body { opacity: 0.55; pointer-events: none; }
523
588
  .prb-dialog-actions button:hover { border-color: var(--border-strong); }
524
589
  /* The destructive action is the one that gets the colour; the way out stays quiet. */
525
590
  .prb-dialog-danger {
526
- border-color: rgba(207, 34, 46, 0.55) !important;
527
- background: rgba(207, 34, 46, 0.14) !important;
528
- color: #ff9b9b !important;
591
+ border-color: var(--danger-border) !important;
592
+ background: var(--danger-subtle) !important;
593
+ color: var(--danger-ink) !important;
529
594
  }
530
595
  .prb-dialog-danger:disabled { opacity: 0.6; cursor: default; }
531
596
 
@@ -536,8 +601,8 @@ body[data-restarting] .prb-body { opacity: 0.55; pointer-events: none; }
536
601
  top: 0;
537
602
  z-index: 20;
538
603
  padding: 10px 18px;
539
- background: #4a1d1d;
540
- color: #ffd9d9;
604
+ background: var(--danger-band);
605
+ color: var(--danger-band-ink);
541
606
  font-size: var(--t-meta);
542
607
  }
543
608
  body[data-server-stopped] .prb-body,
@@ -834,7 +899,7 @@ a.prb-rail-item { text-decoration: none; }
834
899
  cursor: pointer;
835
900
  }
836
901
  .prb-rail-subhead .prb-rail-heading { flex: 1; }
837
- .prb-rail-subhead:hover { background: var(--surface); }
902
+ .prb-rail-subhead:hover { background: var(--surface-hover); }
838
903
  .prb-rail-subhead:hover .prb-rail-heading,
839
904
  .prb-rail-subhead:hover .prb-rail-twisty { color: var(--text-primary); }
840
905
  .prb-rail-subhead:focus-visible { outline: 2px solid var(--brand-hover); outline-offset: -2px; }
@@ -875,7 +940,7 @@ a.prb-rail-item { text-decoration: none; }
875
940
  color: var(--text-secondary);
876
941
  cursor: pointer;
877
942
  }
878
- .prb-rail-item:hover { background: var(--surface); color: var(--text-primary); }
943
+ .prb-rail-item:hover { background: var(--surface-hover); color: var(--text-primary); }
879
944
  .prb-rail-item[aria-current='true'] {
880
945
  background: var(--brand-muted);
881
946
  border-left-color: var(--brand);
@@ -953,7 +1018,7 @@ a.prb-rail-item { text-decoration: none; }
953
1018
  * over the ones still open beside it.
954
1019
  */
955
1020
  .prb-rail-count-done {
956
- color: #4ade80;
1021
+ color: var(--success);
957
1022
  font-weight: 700;
958
1023
  }
959
1024
 
@@ -966,9 +1031,9 @@ a.prb-rail-item { text-decoration: none; }
966
1031
  align-self: center;
967
1032
  background: var(--text-muted);
968
1033
  }
969
- .prb-attention[data-attention='high'] { background: #f87171; }
970
- .prb-attention[data-attention='medium'] { background: #f0b232; }
971
- .prb-attention[data-attention='low'] { background: #475569; }
1034
+ .prb-attention[data-attention='high'] { background: var(--attention-high); }
1035
+ .prb-attention[data-attention='medium'] { background: var(--attention-medium); }
1036
+ .prb-attention[data-attention='low'] { background: var(--attention-low); }
972
1037
 
973
1038
  /* ── Document pane ──
974
1039
  Wrapped in .pr-helper-sidebar so every reused component finds the pill tokens and the type
@@ -1004,6 +1069,10 @@ a.prb-rail-item { text-decoration: none; }
1004
1069
  --pane-warning: #9a6700;
1005
1070
  --pane-success: #1a7f37;
1006
1071
 
1072
+ /* Light in both themes, and native controls in here with it. Without this the dark theme's
1073
+ `color-scheme` would draw this pane's inputs, checkboxes and selects dark on a white page. */
1074
+ color-scheme: light;
1075
+
1007
1076
  overflow-y: auto;
1008
1077
  background: var(--pane-bg);
1009
1078
  }
@@ -1529,6 +1598,9 @@ a.prb-rail-item { text-decoration: none; }
1529
1598
  }
1530
1599
  .prb-bank-add:hover { border-color: var(--border-strong); color: var(--text-primary); }
1531
1600
  .prb-bank-list { display: flex; flex-wrap: wrap; gap: 6px; }
1601
+ /* Folded. The `display` above outranks the browser's own rule for `hidden`, so without this the
1602
+ fold flips the attribute and the chips stay on screen. */
1603
+ .prb-bank-list[hidden] { display: none; }
1532
1604
  /* The pair reads as one chip: the label inserts, the pencil edits. They share a border so the
1533
1605
  edit affordance is visibly part of the prompt it edits rather than a control floating beside it. */
1534
1606
  .prb-bank-item {
@@ -1594,7 +1666,7 @@ a.prb-rail-item { text-decoration: none; }
1594
1666
  .prb-dialog-primary {
1595
1667
  border-color: var(--brand) !important;
1596
1668
  background: var(--brand) !important;
1597
- color: #ffffff !important;
1669
+ color: var(--on-brand) !important;
1598
1670
  }
1599
1671
 
1600
1672
  .prb-msg { font-size: var(--t-body); line-height: var(--lh-body); }
@@ -1611,7 +1683,7 @@ a.prb-rail-item { text-decoration: none; }
1611
1683
  padding: 8px 12px;
1612
1684
  border-radius: 12px 12px 2px 12px;
1613
1685
  background: var(--brand);
1614
- color: #fff;
1686
+ color: var(--on-brand);
1615
1687
  }
1616
1688
  .prb-msg-assistant {
1617
1689
  color: var(--text-secondary);
@@ -1645,7 +1717,7 @@ a.prb-rail-item { text-decoration: none; }
1645
1717
  .prb-md li + li { margin-top: 4px; }
1646
1718
  .prb-md strong { color: var(--text-primary); font-weight: 600; }
1647
1719
  /* A lighter purple than --brand, which is a fill colour and reads as near-black text on this pane. */
1648
- .prb-md a { color: #c4b5fd; }
1720
+ .prb-md a { color: var(--link); }
1649
1721
  .prb-md blockquote {
1650
1722
  margin: 0 0 10px;
1651
1723
  padding-left: 10px;
@@ -1679,8 +1751,8 @@ a.prb-rail-item { text-decoration: none; }
1679
1751
  color: var(--text-secondary);
1680
1752
  }
1681
1753
  .prb-md .pr-helper-code-copy:hover {
1682
- border-color: #c4b5fd;
1683
- color: #c4b5fd;
1754
+ border-color: var(--link);
1755
+ color: var(--link);
1684
1756
  }
1685
1757
  .prb-md .pr-helper-code-copy.copied {
1686
1758
  border-color: var(--success);
@@ -1720,7 +1792,7 @@ a.prb-rail-item { text-decoration: none; }
1720
1792
 
1721
1793
  .prb-md .pr-helper-msg-copy:hover {
1722
1794
  opacity: 1;
1723
- color: #c4b5fd;
1795
+ color: var(--link);
1724
1796
  }
1725
1797
 
1726
1798
  .prb-md .pr-helper-msg-copy.copied {
@@ -1790,7 +1862,7 @@ a.prb-rail-item { text-decoration: none; }
1790
1862
  border-radius: 8px;
1791
1863
  border: 0;
1792
1864
  background: var(--brand);
1793
- color: #fff;
1865
+ color: var(--on-brand);
1794
1866
  font: inherit;
1795
1867
  font-weight: 600;
1796
1868
  cursor: pointer;
@@ -2004,7 +2076,7 @@ a.prb-rail-item { text-decoration: none; }
2004
2076
  that is already carrying the state. */
2005
2077
  .prb-fresh[data-state='changed'] {
2006
2078
  border-left-color: var(--warning);
2007
- background: linear-gradient(90deg, rgba(251, 191, 36, 0.1), transparent 360px);
2079
+ background: linear-gradient(90deg, var(--warning-wash), transparent 360px);
2008
2080
  }
2009
2081
  /* No reading measure on the panel's children.
2010
2082
  A 78ch cap was here to stop prose running to 110ch on a wide screen, and it cost more than it
@@ -2142,7 +2214,7 @@ a.prb-rail-item { text-decoration: none; }
2142
2214
  padding: 12px 18px;
2143
2215
  border-bottom: 1px solid var(--border);
2144
2216
  border-left: 3px solid var(--warning);
2145
- background: linear-gradient(90deg, rgba(251, 191, 36, 0.09), rgba(251, 191, 36, 0.02));
2217
+ background: linear-gradient(90deg, var(--warning-wash), var(--warning-wash-faint));
2146
2218
  font-size: var(--t-meta);
2147
2219
  line-height: var(--lh-body);
2148
2220
  }
@@ -2191,7 +2263,7 @@ a.prb-rail-item { text-decoration: none; }
2191
2263
  }
2192
2264
  /* The row the reader is standing on, marked rather than moved, so the list stays a timeline. Its
2193
2265
  own faint ground says "here" without competing with the row they are being sent to. */
2194
- .prb-lineage-row[data-current] { background: rgba(255, 255, 255, 0.04); }
2266
+ .prb-lineage-row[data-current] { background: var(--wash); }
2195
2267
  .prb-lineage-name { flex: none; font-weight: 400; color: var(--text-primary); }
2196
2268
  .prb-lineage-here,
2197
2269
  .prb-lineage-latest {
@@ -2203,7 +2275,7 @@ a.prb-rail-item { text-decoration: none; }
2203
2275
  letter-spacing: 0.03em;
2204
2276
  text-transform: uppercase;
2205
2277
  }
2206
- .prb-lineage-latest { background: var(--brand); color: #fff; }
2278
+ .prb-lineage-latest { background: var(--brand); color: var(--on-brand); }
2207
2279
  /* Outlined, not filled: it names where the reader already is, which is the one row on this list
2208
2280
  they have no reason to click. */
2209
2281
  .prb-lineage-here { border: 1px solid var(--border); color: var(--text-muted); font-weight: 400; }
@@ -2229,9 +2301,9 @@ a.prb-rail-item { text-decoration: none; }
2229
2301
  text-transform: uppercase;
2230
2302
  white-space: nowrap;
2231
2303
  }
2232
- .prb-lineage-risk[data-risk="high"] { background: rgba(207, 34, 46, 0.16); color: #ff9b9b; }
2233
- .prb-lineage-risk[data-risk="medium"] { background: rgba(210, 153, 34, 0.16); color: #ffd18a; }
2234
- .prb-lineage-risk[data-risk="low"] { background: rgba(46, 160, 67, 0.14); color: #7ee2a8; }
2304
+ .prb-lineage-risk[data-risk="high"] { background: var(--danger-subtle); color: var(--danger-ink); }
2305
+ .prb-lineage-risk[data-risk="medium"] { background: var(--warning-subtle); color: var(--warning-ink); }
2306
+ .prb-lineage-risk[data-risk="low"] { background: var(--success-subtle); color: var(--success-ink); }
2235
2307
  .prb-lineage-open {
2236
2308
  flex: none;
2237
2309
  margin-left: auto;
@@ -2259,13 +2331,13 @@ a.prb-rail-item { text-decoration: none; }
2259
2331
  border: 1px solid var(--brand);
2260
2332
  border-radius: 7px;
2261
2333
  background: var(--brand);
2262
- color: #fff;
2334
+ color: var(--on-brand);
2263
2335
  font: inherit;
2264
2336
  font-size: var(--t-meta);
2265
2337
  font-weight: 600;
2266
2338
  cursor: pointer;
2267
2339
  }
2268
- .prb-update-btn:hover:not(:disabled) { filter: brightness(1.08); color: #fff; }
2340
+ .prb-update-btn:hover:not(:disabled) { filter: brightness(1.08); color: var(--on-brand); }
2269
2341
  .prb-update-btn:disabled { opacity: 0.55; cursor: default; }
2270
2342
 
2271
2343
  /* The two disclosures on the controls line, styled as one thing because they are peers: a quiet
@@ -2479,7 +2551,7 @@ a.prb-rail-item { text-decoration: none; }
2479
2551
  color: #1f2328;
2480
2552
  cursor: pointer;
2481
2553
  }
2482
- .prb-progress-open:hover { border-color: var(--border-strong); color: var(--text-primary); }
2554
+ .prb-progress-open:hover { border-color: rgba(15, 23, 42, 0.3); color: var(--pane-ink); }
2483
2555
 
2484
2556
  .prb-progress-elapsed {
2485
2557
  margin: 20px 0 0;
@@ -2501,7 +2573,7 @@ a.prb-rail-item { text-decoration: none; }
2501
2573
  background: #ffffff;
2502
2574
  color: #1f2328;
2503
2575
  }
2504
- .prb-progress-failure .prb-job-retry:hover { border-color: var(--border-strong); color: var(--text-primary); }
2576
+ .prb-progress-failure .prb-job-retry:hover { border-color: rgba(15, 23, 42, 0.3); color: var(--pane-ink); }
2505
2577
 
2506
2578
  /* ── Responsive ──
2507
2579
  The assistant is the first thing to fold, then the rail. The review document never folds; it is
@@ -2639,7 +2711,7 @@ a.prb-rail-item { text-decoration: none; }
2639
2711
  font-size: var(--t-micro);
2640
2712
  cursor: pointer;
2641
2713
  }
2642
- .prb-code-open:hover { border-color: var(--border-strong); color: var(--text-primary); }
2714
+ .prb-code-open:hover { border-color: rgba(15, 23, 42, 0.3); color: var(--pane-ink); }
2643
2715
  /* Only the first of the two pushes right; the second sits beside it. */
2644
2716
  .prb-code-open ~ .prb-code-open { margin-left: 0; }
2645
2717
  /* Blue, the colour every route out to the pull request uses, so the two buttons read as two
@@ -2784,7 +2856,7 @@ a.prb-rail-item { text-decoration: none; }
2784
2856
  text-align: left;
2785
2857
  }
2786
2858
  .prb-code-more { cursor: pointer; }
2787
- .prb-code-more:hover { color: var(--text-primary); }
2859
+ .prb-code-more:hover { color: var(--pane-ink); }
2788
2860
 
2789
2861
  /* A file row and a citation both open code now, so both have to look like they will. */
2790
2862
  .prb-pane .pr-helper-guide-file { cursor: pointer; }
@@ -2901,7 +2973,7 @@ a.prb-rail-item { text-decoration: none; }
2901
2973
  .prb-index-select[aria-pressed="true"] {
2902
2974
  border-color: var(--brand);
2903
2975
  background: var(--brand);
2904
- color: #ffffff;
2976
+ color: var(--on-brand);
2905
2977
  }
2906
2978
 
2907
2979
  /* Wraps, so a narrow window gives the search its own line and puts the filters beneath it rather
@@ -2941,7 +3013,7 @@ a.prb-rail-item { text-decoration: none; }
2941
3013
  .prb-index-author:hover { border-color: var(--border-strong); }
2942
3014
  .prb-index-author:focus-visible { outline: none; border-color: var(--brand); }
2943
3015
  /* Filled like a pressed pill once it narrows the list, so a filtered view is never mistaken for all of it. */
2944
- .prb-index-author[data-active] { border-color: var(--brand); background-color: var(--brand); color: #ffffff; }
3016
+ .prb-index-author[data-active] { border-color: var(--brand); background-color: var(--brand); color: var(--on-brand); }
2945
3017
  /* Status: one control where five chips were. The box is the label, so "Status:" reads as part of the
2946
3018
  control rather than a caption beside it; the select inside is bare and carries the chevron. Same
2947
3019
  border, radius, height and fill-when-narrowing as the author filter beside it. */
@@ -2968,8 +3040,8 @@ a.prb-rail-item { text-decoration: none; }
2968
3040
  cursor: pointer;
2969
3041
  }
2970
3042
  .prb-index-status select:focus-visible { outline: none; }
2971
- .prb-index-status:has(select[data-active]) { border-color: var(--brand); background-color: var(--brand); color: rgba(255, 255, 255, 0.8); }
2972
- .prb-index-status select[data-active] { color: #ffffff; }
3043
+ .prb-index-status:has(select[data-active]) { border-color: var(--brand); background-color: var(--brand); color: var(--on-brand-quiet); }
3044
+ .prb-index-status select[data-active] { color: var(--on-brand); }
2973
3045
  /* Fetch updates: the secondary treatment Check for updates and Mark reviewed use, at the selects'
2974
3046
  height so the row lines up. What a run did shows in its own label for a moment, not in a row. */
2975
3047
  .prb-index-fetch-button {
@@ -2994,46 +3066,39 @@ a.prb-rail-item { text-decoration: none; }
2994
3066
  gap: 8px;
2995
3067
  }
2996
3068
 
2997
- /* ── Pages ──
2998
- Ordinary numbered pagination, because these are saved work items: a page boundary is somewhere a
2999
- person can come back to and a scroll position is not. Hidden by the client whenever the list it
3000
- is paging fits on one page, since one page is not a choice. */
3001
- .prb-index-pager {
3002
- display: flex;
3003
- align-items: center;
3004
- justify-content: center;
3005
- gap: 6px;
3006
- margin-top: 20px;
3069
+ /* ── Sections and Load more ──
3070
+ Waiting and Done sit under the working list as disclosures: headed at the weight of the count
3071
+ line, because they are the same kind of thing, a count of cards, and must not outrank the list
3072
+ above them. Load more is one per section, quiet, and centred under the cards it extends. */
3073
+ .prb-index-section { margin-top: 24px; }
3074
+ .prb-index-section[hidden] { display: none; }
3075
+ .prb-index-section-head {
3076
+ margin: 0 2px 10px;
3077
+ font-size: var(--t-meta);
3078
+ font-weight: 600;
3079
+ color: var(--text-secondary);
3080
+ cursor: pointer;
3007
3081
  }
3008
- .prb-index-pager[hidden] { display: none; }
3009
- .prb-index-pages { display: flex; gap: 4px; }
3010
- .prb-index-page {
3011
- min-width: 30px;
3012
- padding: 6px 10px;
3082
+ .prb-index-section-head:hover { color: var(--text-primary); }
3083
+ .prb-index-more {
3084
+ display: block;
3085
+ margin: 12px auto 0;
3086
+ padding: 6px 14px;
3013
3087
  border: 1px solid var(--border);
3014
3088
  border-radius: 8px;
3015
3089
  background: transparent;
3016
3090
  color: var(--text-secondary);
3017
3091
  font: inherit;
3018
3092
  font-size: var(--t-meta);
3019
- font-variant-numeric: tabular-nums;
3020
3093
  cursor: pointer;
3021
3094
  }
3022
- .prb-index-page:hover:not(:disabled) { color: var(--text-primary); border-color: var(--border-strong); }
3023
- /* Plainly unavailable at the ends, rather than a control that looks live and does nothing. */
3024
- .prb-index-page:disabled { opacity: 0.4; cursor: default; }
3025
- /* The page you are on, filled the way the selected filter is: this bar has one job and it is to
3026
- say which of these numbers is the answer. */
3027
- .prb-index-page[aria-current="page"] {
3028
- border-color: var(--brand);
3029
- background: var(--brand);
3030
- color: #ffffff;
3031
- }
3095
+ .prb-index-more[hidden] { display: none; }
3096
+ .prb-index-more:hover { color: var(--text-primary); border-color: var(--border-strong); }
3032
3097
  .prb-card {
3033
3098
  position: relative;
3034
3099
  border: 1px solid var(--border);
3035
3100
  border-radius: 12px;
3036
- background: var(--surface);
3101
+ background: var(--surface-raised);
3037
3102
  }
3038
3103
  .prb-card:hover { border-color: var(--border-strong); }
3039
3104
  .prb-card[hidden] { display: none; }
@@ -3079,8 +3144,8 @@ a.prb-rail-item { text-decoration: none; }
3079
3144
  padding: 12px 16px;
3080
3145
  border: 1px solid var(--brand);
3081
3146
  border-radius: 12px;
3082
- background: var(--surface);
3083
- box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
3147
+ background: var(--surface-raised);
3148
+ box-shadow: 0 6px 20px var(--shadow-soft);
3084
3149
  }
3085
3150
  .prb-bulk[hidden] { display: none; }
3086
3151
  .prb-bulk-count {
@@ -3178,7 +3243,7 @@ a.prb-rail-item { text-decoration: none; }
3178
3243
  padding: 1px 6px;
3179
3244
  border-radius: 999px;
3180
3245
  background: var(--brand);
3181
- color: #fff;
3246
+ color: var(--on-brand);
3182
3247
  font-size: var(--t-pill);
3183
3248
  font-weight: 700;
3184
3249
  letter-spacing: 0.03em;
@@ -3352,7 +3417,7 @@ a.prb-rail-item { text-decoration: none; }
3352
3417
  color: var(--text-muted);
3353
3418
  white-space: nowrap;
3354
3419
  }
3355
- .prb-card-lens[data-lens="kiss"] { border-color: var(--brand); color: #c4a5f5; }
3420
+ .prb-card-lens[data-lens="kiss"] { border-color: var(--brand); color: var(--brand-ink); }
3356
3421
 
3357
3422
  .prb-card-status,
3358
3423
  .prb-card-pr-state,
@@ -3367,16 +3432,30 @@ a.prb-rail-item { text-decoration: none; }
3367
3432
  white-space: nowrap;
3368
3433
  }
3369
3434
  .prb-card-status { border: 1px solid var(--border); color: var(--text-tertiary, var(--text-secondary)); }
3370
- .prb-card-status[data-status="new"] { border-color: var(--brand); color: #c4a5f5; }
3371
- .prb-card-status[data-status="complete"] { border-color: rgba(46, 160, 67, 0.5); color: #7ee2a8; }
3435
+ .prb-card-status[data-status="new"] { border-color: var(--brand); color: var(--brand-ink); }
3436
+ .prb-card-status[data-status="complete"] { border-color: var(--success-border); color: var(--success-ink); }
3372
3437
  /* The upstream's state, beside the reviewer's. Filled where the status is outlined, so the two read
3373
3438
  as different kinds of fact. Merged takes the forge's own purple; closed is muted rather than red,
3374
3439
  because red on this card already means high risk. */
3375
- .prb-card-pr-state[data-pr-state="merged"] { background: rgba(163, 113, 247, 0.18); color: #d2a8ff; }
3376
- .prb-card-pr-state[data-pr-state="closed"] { background: rgba(139, 148, 158, 0.16); color: var(--text-secondary); }
3377
- .prb-card-risk[data-risk="high"] { background: rgba(207, 34, 46, 0.16); color: #ff9b9b; }
3378
- .prb-card-risk[data-risk="medium"] { background: rgba(210, 153, 34, 0.16); color: #ffd18a; }
3379
- .prb-card-risk[data-risk="low"] { background: rgba(46, 160, 67, 0.14); color: #7ee2a8; }
3440
+ .prb-card-pr-state[data-pr-state="merged"] { background: var(--merged-subtle); color: var(--merged-ink); }
3441
+ .prb-card-pr-state[data-pr-state="closed"] { background: var(--neutral-subtle); color: var(--text-secondary); }
3442
+ .prb-card-risk[data-risk="high"] { background: var(--danger-subtle); color: var(--danger-ink); }
3443
+ .prb-card-risk[data-risk="medium"] { background: var(--warning-subtle); color: var(--warning-ink); }
3444
+ .prb-card-risk[data-risk="low"] { background: var(--success-subtle); color: var(--success-ink); }
3445
+ /* Why a card is where it is: a Waiting review back in Active, or a Done one that moved. A sentence,
3446
+ so it is not set in capitals like the pills beside it, and tinted like the brand because it is
3447
+ news about the change, as "PR updated" is. */
3448
+ .prb-card-lifecycle {
3449
+ flex: none;
3450
+ padding: 2px 7px;
3451
+ border-radius: 999px;
3452
+ font-size: var(--t-meta);
3453
+ font-weight: 600;
3454
+ white-space: nowrap;
3455
+ background: var(--brand-muted);
3456
+ color: var(--brand-ink);
3457
+ }
3458
+ .prb-card-lifecycle[hidden] { display: none; }
3380
3459
 
3381
3460
  /* Over the card rather than inside its link, so pinning cannot be a mis-click into the review. */
3382
3461
  .prb-card-pin {
@@ -3466,8 +3545,8 @@ a.prb-rail-item { text-decoration: none; }
3466
3545
  padding: 5px;
3467
3546
  border: 1px solid var(--border);
3468
3547
  border-radius: 9px;
3469
- background: var(--surface);
3470
- box-shadow: 0 10px 28px rgba(0, 0, 0, 0.36);
3548
+ background: var(--surface-raised);
3549
+ box-shadow: 0 10px 28px var(--shadow-float);
3471
3550
  text-align: left;
3472
3551
  }
3473
3552
  .prb-menu-item {
@@ -3485,9 +3564,12 @@ a.prb-rail-item { text-decoration: none; }
3485
3564
  }
3486
3565
  .prb-menu-item:hover { background: var(--surface-2); color: var(--text-primary); }
3487
3566
  .prb-menu-item:focus-visible { outline: 2px solid var(--brand-hover); outline-offset: -2px; }
3488
- .prb-menu-item-danger { color: #ff9b9b; }
3489
- .prb-menu-item-danger:hover { background: rgba(207, 34, 46, 0.16); color: #ffb3b3; }
3567
+ .prb-menu-item-danger { color: var(--danger-ink); }
3568
+ .prb-menu-item-danger:hover { background: var(--danger-subtle); color: var(--danger-ink-strong); }
3490
3569
  .prb-menu-item[disabled] { opacity: 0.6; cursor: default; }
3570
+ /* `display: block` above beats the `hidden` attribute, and the lifecycle items are hidden that way:
3571
+ without this, the move that would do nothing is still drawn and still reachable by keyboard. */
3572
+ .prb-menu-item[hidden] { display: none; }
3491
3573
 
3492
3574
  /* The confirmation is the same panel in its second state, so the question appears where the person
3493
3575
  was already looking rather than in a modal over the middle of the screen. */
@@ -3522,7 +3604,7 @@ a.prb-rail-item { text-decoration: none; }
3522
3604
  }
3523
3605
  .prb-menu-error {
3524
3606
  margin: 6px 4px 2px;
3525
- color: #ff9b9b;
3607
+ color: var(--danger-ink);
3526
3608
  font-size: var(--t-micro);
3527
3609
  line-height: var(--lh-body);
3528
3610
  }
@@ -3540,9 +3622,9 @@ a.prb-rail-item { text-decoration: none; }
3540
3622
  }
3541
3623
  .prb-complete:hover { border-color: var(--border-strong); color: var(--text-primary); }
3542
3624
  .prb-complete[aria-pressed="true"] {
3543
- border-color: rgba(46, 160, 67, 0.55);
3544
- background: rgba(46, 160, 67, 0.14);
3545
- color: #7ee2a8;
3625
+ border-color: var(--success-border);
3626
+ background: var(--success-subtle);
3627
+ color: var(--success-ink);
3546
3628
  }
3547
3629
 
3548
3630
  /* ── Diagrams ──
@@ -3588,7 +3670,7 @@ a.prb-rail-item { text-decoration: none; }
3588
3670
  text-align: left;
3589
3671
  cursor: pointer;
3590
3672
  }
3591
- .prb-diagram-toggle:hover { color: var(--text-primary); }
3673
+ .prb-diagram-toggle:hover { color: var(--pane-ink); }
3592
3674
  .prb-diagram-disclosure {
3593
3675
  font-size: 9px; /* the disclosure caret, sized to the mark, not the type scale */
3594
3676
  color: #8c959f;
@@ -3634,7 +3716,7 @@ a.prb-rail-item { text-decoration: none; }
3634
3716
  }
3635
3717
  .prb-diagram-zoom { min-width: 26px; font-variant-numeric: tabular-nums; }
3636
3718
  .prb-diagram-zoom:hover,
3637
- .prb-diagram-redraw:hover { border-color: var(--border-strong); color: var(--text-primary); }
3719
+ .prb-diagram-redraw:hover { border-color: rgba(15, 23, 42, 0.3); color: var(--pane-ink); }
3638
3720
  .prb-diagram-redraw[disabled] { opacity: 0.5; cursor: default; }
3639
3721
 
3640
3722
  .prb-diagram-caption {
@@ -3704,6 +3786,320 @@ a.prb-rail-item { text-decoration: none; }
3704
3786
  .prb-diagram-source > summary::before { content: '\25B8 '; }
3705
3787
  .prb-diagram-source[open] > summary::before { content: '\25BE '; }
3706
3788
 
3789
+ /* ── The two diagram tabs ──
3790
+ Each says what question it answers, so the reviewer never has to open both to learn they differ.
3791
+ A segmented strip rather than the rail's style: these are two views of one place, not two places. */
3792
+ .prb-diagram-modes {
3793
+ display: inline-flex;
3794
+ gap: 2px;
3795
+ margin: 0 0 18px;
3796
+ padding: 4px;
3797
+ border: 1px solid rgba(15, 23, 42, 0.12);
3798
+ border-radius: 10px;
3799
+ background: #f6f8fa;
3800
+ }
3801
+ .prb-diagram-mode {
3802
+ display: flex;
3803
+ flex-direction: column;
3804
+ align-items: flex-start;
3805
+ gap: 2px;
3806
+ min-width: 190px;
3807
+ padding: 8px 18px 9px;
3808
+ border: 0;
3809
+ border-radius: 8px;
3810
+ background: transparent;
3811
+ color: #57606a;
3812
+ font: inherit;
3813
+ text-align: left;
3814
+ cursor: pointer;
3815
+ }
3816
+ .prb-diagram-mode:hover { color: #1f2328; }
3817
+ .prb-diagram-mode[aria-selected='true'] {
3818
+ background: #fff;
3819
+ color: #1f2328;
3820
+ box-shadow: 0 1px 2px rgba(15, 23, 42, 0.12);
3821
+ }
3822
+ .prb-diagram-mode:focus-visible { outline: 2px solid #9333ea; outline-offset: 1px; }
3823
+ .prb-diagram-mode-title { display: inline-flex; align-items: center; gap: 6px; font-size: var(--t-body); font-weight: 600; }
3824
+ /* The picture on this tab already exists: switching to it costs nothing. Passive, and a word for
3825
+ assistive technology rather than colour alone. */
3826
+ .prb-diagram-mode-ready {
3827
+ display: inline-flex;
3828
+ align-items: center;
3829
+ justify-content: center;
3830
+ width: 15px;
3831
+ height: 15px;
3832
+ border-radius: 50%;
3833
+ background: rgba(26, 127, 55, 0.12);
3834
+ color: #1a7f37;
3835
+ font-size: 10px;
3836
+ font-weight: 700;
3837
+ }
3838
+ .prb-diagram-mode-ready[hidden] { display: none; }
3839
+ .prb-diagram-mode-subtitle { font-size: var(--t-micro); color: #8c959f; }
3840
+
3841
+ /* ── Before anything is drawn ──
3842
+ Says what the view is for and offers the one action that spends a run. Not a dead end, and not a
3843
+ button that pretends to be free. */
3844
+ .prb-map-empty {
3845
+ max-width: 560px;
3846
+ margin: 8px 0 20px;
3847
+ padding: 18px 20px;
3848
+ border: 1px dashed rgba(15, 23, 42, 0.2);
3849
+ border-radius: 10px;
3850
+ background: #fff;
3851
+ font-size: var(--t-body);
3852
+ line-height: var(--lh-body);
3853
+ color: #57606a;
3854
+ }
3855
+ .prb-map-empty p { margin: 0 0 10px; }
3856
+ .prb-map-empty-title { margin: 0 0 6px; font-size: var(--t-section); font-weight: 600; color: #1f2328; }
3857
+ .prb-map-empty-note { font-size: var(--t-meta); color: #8c959f; }
3858
+ .prb-map-stale {
3859
+ padding: 7px 10px;
3860
+ border-radius: 7px;
3861
+ background: rgba(251, 191, 36, 0.12);
3862
+ font-size: var(--t-meta);
3863
+ color: #6b5514;
3864
+ }
3865
+ .prb-map-generate {
3866
+ margin-top: 4px;
3867
+ padding: 7px 14px;
3868
+ border: 1px solid #9333ea;
3869
+ border-radius: 6px;
3870
+ background: #9333ea;
3871
+ color: #fff;
3872
+ font: inherit;
3873
+ font-size: var(--t-meta);
3874
+ font-weight: 600;
3875
+ cursor: pointer;
3876
+ }
3877
+ .prb-map-generate:hover { background: #7e22ce; }
3878
+ .prb-map-generate[disabled] { opacity: 0.55; cursor: default; }
3879
+ /* While a map is being made there is one state on screen, the work and its Stop: the button that
3880
+ would start it again goes, whether this page asked or came back to a run already going. */
3881
+ .prb-view[data-map-running] .prb-map-generate { display: none; }
3882
+ /* The implementation tab's own empty state goes the moment its first card is in the pane. */
3883
+ .prb-view[data-diagram-pane]:has([data-diagram-card]) [data-diagram-empty] { display: none; }
3884
+
3885
+ /* ── The change map ── */
3886
+ .prb-map-body { container-type: inline-size; }
3887
+ .prb-map {
3888
+ margin: 0 0 20px;
3889
+ border: 1px solid rgba(15, 23, 42, 0.12);
3890
+ border-radius: 10px;
3891
+ background: #fff;
3892
+ }
3893
+ .prb-map-head {
3894
+ display: flex;
3895
+ flex-wrap: wrap;
3896
+ align-items: flex-start;
3897
+ gap: 12px;
3898
+ padding: 14px 16px 12px;
3899
+ border-bottom: 1px solid rgba(15, 23, 42, 0.08);
3900
+ }
3901
+ .prb-map-heading { flex: 1 1 280px; min-width: 0; }
3902
+ .prb-map-title { margin: 0; font-size: var(--t-section); font-weight: 600; color: #1f2328; }
3903
+ .prb-map-intent { margin: 4px 0 0; font-size: var(--t-body); line-height: var(--lh-body); color: #57606a; }
3904
+ .prb-map-tools { display: flex; align-items: center; gap: 8px; margin-left: auto; }
3905
+ .prb-map-registers {
3906
+ display: inline-flex;
3907
+ padding: 2px;
3908
+ border: 1px solid rgba(15, 23, 42, 0.15);
3909
+ border-radius: 7px;
3910
+ background: #f6f8fa;
3911
+ }
3912
+ .prb-map-register {
3913
+ padding: 3px 10px;
3914
+ border: 0;
3915
+ border-radius: 5px;
3916
+ background: transparent;
3917
+ color: #57606a;
3918
+ font: inherit;
3919
+ font-size: var(--t-micro);
3920
+ cursor: pointer;
3921
+ }
3922
+ .prb-map-register[aria-pressed='true'] { background: #fff; color: #1f2328; font-weight: 600; box-shadow: 0 1px 2px rgba(15, 23, 42, 0.12); }
3923
+ .prb-map-register:focus-visible { outline: 2px solid #9333ea; outline-offset: 1px; }
3924
+ .prb-map-hint { margin: 10px 16px 0; font-size: var(--t-micro); color: #8c959f; }
3925
+
3926
+ /* One graph in the page, two sets of words; the board says which to show. */
3927
+ .prb-map[data-register='simple'] [data-reg='technical'],
3928
+ .prb-map[data-register='technical'] [data-reg='simple'] { display: none; }
3929
+
3930
+ /* Stages as columns, read left to right. The gaps are wide on purpose: the relationship labels sit
3931
+ in them, and a label is the point of a line. */
3932
+ .prb-map-board {
3933
+ position: relative;
3934
+ display: grid;
3935
+ grid-template-columns: repeat(var(--prb-map-stages, 5), minmax(180px, 1fr));
3936
+ column-gap: 112px;
3937
+ padding: 14px 16px 22px;
3938
+ overflow-x: auto;
3939
+ }
3940
+ .prb-map-stage {
3941
+ position: relative;
3942
+ z-index: 1;
3943
+ display: flex;
3944
+ flex-direction: column;
3945
+ gap: 22px;
3946
+ }
3947
+ .prb-map-stage-title {
3948
+ align-self: flex-start;
3949
+ margin: 0 0 -8px;
3950
+ font-size: var(--t-micro);
3951
+ font-weight: 600;
3952
+ text-transform: uppercase;
3953
+ letter-spacing: 0.05em;
3954
+ color: #8c959f;
3955
+ }
3956
+
3957
+ .prb-map-node {
3958
+ --role: #8250df;
3959
+ position: relative;
3960
+ padding: 9px 11px 10px;
3961
+ border: 1px solid rgba(15, 23, 42, 0.14);
3962
+ border-left: 4px solid var(--role);
3963
+ border-radius: 8px;
3964
+ background: #fff;
3965
+ font-size: var(--t-meta);
3966
+ line-height: 1.45;
3967
+ color: #424a53;
3968
+ transition: opacity 0.15s ease, box-shadow 0.15s ease;
3969
+ cursor: pointer;
3970
+ }
3971
+ .prb-map-node:focus-visible { outline: 2px solid #9333ea; outline-offset: 2px; }
3972
+ .prb-map-node[data-role='intent'] { --role: #0969da; }
3973
+ .prb-map-node[data-role='actor'] { --role: #6e7781; }
3974
+ .prb-map-node[data-role='concept'] { --role: #8250df; }
3975
+ .prb-map-node[data-role='state'] { --role: #0f766e; }
3976
+ .prb-map-node[data-role='constraint'] { --role: #9a6700; }
3977
+ .prb-map-node[data-role='risk'] { --role: #cf222e; }
3978
+ .prb-map-node[data-role='outcome'] { --role: #1a7f37; }
3979
+ /* Importance is prominence: a heavier frame and a larger name, never a score. */
3980
+ .prb-map-node[data-weight='primary'] {
3981
+ border-width: 2px 2px 2px 5px;
3982
+ border-color: var(--role);
3983
+ box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
3984
+ }
3985
+ .prb-map-node[data-weight='primary'] .prb-map-node-title { font-size: calc(var(--t-body) + 1px); }
3986
+ .prb-map-role {
3987
+ margin: 0 0 3px;
3988
+ font-size: var(--t-micro);
3989
+ font-weight: 600;
3990
+ text-transform: uppercase;
3991
+ letter-spacing: 0.04em;
3992
+ color: var(--role);
3993
+ }
3994
+ .prb-map-core { color: #57606a; font-weight: 500; }
3995
+ .prb-map-node-title { margin: 0 0 4px; font-size: var(--t-body); font-weight: 600; line-height: 1.3; color: #1f2328; }
3996
+ .prb-map-node-body { margin: 0; }
3997
+ .prb-map code:not(.prb-map-anchor) {
3998
+ padding: 0 3px;
3999
+ border-radius: 3px;
4000
+ background: #f6f8fa;
4001
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
4002
+ font-size: var(--t-code);
4003
+ }
4004
+ .prb-map-why { margin: 6px 0 0; color: #57606a; }
4005
+ .prb-map-why-lead { font-weight: 600; color: #1f2328; }
4006
+ .prb-map-anchor {
4007
+ display: inline-block;
4008
+ max-width: 100%;
4009
+ margin-top: 6px;
4010
+ padding: 1px 5px;
4011
+ border-radius: 4px;
4012
+ background: #f6f8fa;
4013
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
4014
+ font-size: var(--t-code);
4015
+ color: #57606a;
4016
+ overflow-wrap: anywhere;
4017
+ }
4018
+ /* Relationships the board could not draw cleanly, said in the idea they leave from. */
4019
+ .prb-map-inline {
4020
+ margin: 7px 0 0;
4021
+ padding: 6px 0 0;
4022
+ border-top: 1px dashed rgba(15, 23, 42, 0.14);
4023
+ list-style: none;
4024
+ font-size: var(--t-micro);
4025
+ color: #57606a;
4026
+ }
4027
+ .prb-map-inline:not(:has(li:not([hidden]))) { display: none; }
4028
+ .prb-map-inline li + li { margin-top: 2px; }
4029
+ .prb-map-inline em { color: #1f2328; }
4030
+ .prb-map-goto {
4031
+ display: block;
4032
+ margin-top: 8px;
4033
+ padding: 0;
4034
+ border: 0;
4035
+ background: none;
4036
+ color: #8250df;
4037
+ font: inherit;
4038
+ font-size: var(--t-micro);
4039
+ font-weight: 600;
4040
+ text-align: left;
4041
+ cursor: pointer;
4042
+ }
4043
+ .prb-map-goto:hover { text-decoration: underline; }
4044
+
4045
+ .prb-map-edges {
4046
+ position: absolute;
4047
+ top: 0;
4048
+ left: 0;
4049
+ z-index: 0;
4050
+ overflow: visible;
4051
+ pointer-events: none;
4052
+ }
4053
+ .prb-map-edge { fill: none; stroke: #8c959f; stroke-width: 1.5; transition: opacity 0.15s ease; }
4054
+ .prb-map-arrow { fill: #8c959f; }
4055
+ .prb-map-labels { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
4056
+ .prb-map-board:not([data-laid-out]) .prb-map-labels { visibility: hidden; }
4057
+ .prb-map-label {
4058
+ position: absolute;
4059
+ max-width: 100px;
4060
+ padding: 1px 6px;
4061
+ border: 1px solid rgba(15, 23, 42, 0.12);
4062
+ border-radius: 10px;
4063
+ background: #fff;
4064
+ font-size: var(--t-micro);
4065
+ font-style: italic;
4066
+ line-height: 1.35;
4067
+ text-align: center;
4068
+ color: #424a53;
4069
+ transition: opacity 0.15s ease;
4070
+ }
4071
+ .prb-map-label[hidden] { display: none; }
4072
+
4073
+ /* Selecting an idea keeps it, its neighbours and the lines between them, and quiets the rest. */
4074
+ .prb-map-board[data-selecting] .prb-map-node:not([data-active]):not([data-near]) { opacity: 0.35; }
4075
+ .prb-map-board[data-selecting] .prb-map-edge:not([data-active]) { opacity: 0.15; }
4076
+ .prb-map-board[data-selecting] .prb-map-label:not([data-active]) { opacity: 0.2; }
4077
+ .prb-map-board .prb-map-edge[data-active] { stroke: #1f2328; stroke-width: 2; }
4078
+ .prb-map-board .prb-map-label[data-active] { border-color: #1f2328; color: #1f2328; }
4079
+ .prb-map-node[data-active] { box-shadow: 0 0 0 3px rgba(147, 51, 234, 0.25); }
4080
+
4081
+ .prb-map-list { margin: 0 16px 14px; font-size: var(--t-meta); color: #57606a; }
4082
+ .prb-map-list > summary { cursor: pointer; }
4083
+ .prb-map-list ol { margin: 8px 0 0; padding-left: 20px; line-height: var(--lh-body); }
4084
+ .prb-map-list li { margin-bottom: 4px; }
4085
+ .prb-map-list strong { color: #1f2328; font-weight: 600; }
4086
+ .prb-map-note { color: #57606a; }
4087
+
4088
+ /* Five stages side by side, with room between them for the words on the lines, need about 1360px:
4089
+ the expanded pane, not the one between the rail and Buddy AI. Narrower than that the stages stack, top to bottom, in the same reading order,
4090
+ and the lines follow them down. A board that scrolled sideways would hide the half of every line
4091
+ that explains it. */
4092
+ @container (max-width: 1360px) {
4093
+ .prb-map-board { grid-template-columns: minmax(0, 1fr); row-gap: 58px; overflow-x: visible; }
4094
+ .prb-map-stage { flex-direction: row; flex-wrap: wrap; justify-content: center; align-items: stretch; gap: 28px 36px; }
4095
+ .prb-map-stage-title { flex-basis: 100%; margin: 0; }
4096
+ .prb-map-node { flex: 1 1 210px; max-width: 300px; }
4097
+ }
4098
+
4099
+ @media (prefers-reduced-motion: reduce) {
4100
+ .prb-map-node, .prb-map-edge, .prb-map-label { transition: none; }
4101
+ }
4102
+
3707
4103
  /* ── The Overview's stat cards ──
3708
4104
  The drawer stacks each card as a centred column: number, then label, then an optional note. At
3709
4105
  460px that is the only thing that fits. Here it produced two faults at once. Every card is as
@@ -3797,6 +4193,21 @@ a.prb-rail-item { text-decoration: none; }
3797
4193
  }
3798
4194
  .pr-helper-diagram-button:hover { background: rgba(147, 51, 234, 0.08); }
3799
4195
  .pr-helper-diagram-button[disabled] { opacity: 0.55; cursor: default; }
4196
+ /* The Overview's way to the diagrams: the main visual path through the change, so a step above its
4197
+ neighbour with a tinted ground, yet no run behind it, so not the filled treatment Generate has. */
4198
+ .pr-helper-map-button {
4199
+ margin: 0 0 14px;
4200
+ padding: 7px 16px;
4201
+ border: 1px solid #9333ea;
4202
+ border-radius: 6px;
4203
+ background: rgba(147, 51, 234, 0.1);
4204
+ color: #7e22ce;
4205
+ font-family: inherit;
4206
+ font-size: var(--t-meta);
4207
+ font-weight: 600;
4208
+ cursor: pointer;
4209
+ }
4210
+ .pr-helper-map-button:hover { background: rgba(147, 51, 234, 0.16); }
3800
4211
  /* In a theme card the button stands alone in the actions row, whose other occupant — the guided
3801
4212
  review CTA — is hidden in this client. That CTA carried the gap below the review checklist, so
3802
4213
  with it gone the row has to carry it, or the button sits flush against the checklist box: the
@@ -4330,8 +4741,9 @@ a.prb-rail-item { text-decoration: none; }
4330
4741
  * So the native one is turned off and the same chevron the rest of the product uses is drawn in its
4331
4742
  * place: inset from the edge by the padding the text has on the other side, and in the muted grey
4332
4743
  * the borders and the quiet icons already read as. The colour is written into the drawing as a
4333
- * literal because a data URI cannot take a custom property; `%2364748b` is `--text-muted`, and the
4334
- * two change together or not at all. Same for the filled state below, which is `#ffffff`.
4744
+ * literal because a data URI cannot take a custom property; `%2364748b` is the dark `--text-muted`,
4745
+ * and it serves the light theme unchanged because the light `--text-muted` is within a hair of it.
4746
+ * Change either and check the other. Same for the filled state below, which is `--on-brand`.
4335
4747
  *
4336
4748
  * Both selects in the workspace are listed here rather than each carrying its own copy: it is one
4337
4749
  * control with two sizes, and a caret that differed between them would be a bug nobody could name. */
@@ -4385,6 +4797,12 @@ a.prb-rail-item { text-decoration: none; }
4385
4797
  margin: 0;
4386
4798
  accent-color: var(--brand);
4387
4799
  }
4800
+ /* What a choice does, beside its name rather than under it, so three short choices stay three
4801
+ short lines. A <small> rather than a span, because `.prb-field span` is the field's caption. */
4802
+ .prb-settings-choice-help {
4803
+ font-size: var(--t-meta);
4804
+ color: var(--text-secondary);
4805
+ }
4388
4806
  .prb-settings-help {
4389
4807
  margin: 0;
4390
4808
  max-width: 46rem;
@@ -4441,7 +4859,7 @@ a.prb-rail-item { text-decoration: none; }
4441
4859
  }
4442
4860
  .prb-notification:first-child { border-top: 0; }
4443
4861
  /* Unread carries a tint as well as the dot and the weight, so it reads at a glance down a long list. */
4444
- .prb-notification[data-read='false'] { background: rgba(139, 92, 246, 0.09); }
4862
+ .prb-notification[data-read='false'] { background: var(--unread-wash); }
4445
4863
  .prb-notification-open {
4446
4864
  flex: 1;
4447
4865
  min-width: 0;
@@ -4453,7 +4871,7 @@ a.prb-rail-item { text-decoration: none; }
4453
4871
  text-decoration: none;
4454
4872
  }
4455
4873
  /* The whole row is the link, so the row lights up rather than the title underlining. */
4456
- a.prb-notification-open:hover { background: rgba(255, 255, 255, 0.04); }
4874
+ a.prb-notification-open:hover { background: var(--wash); }
4457
4875
  a.prb-notification-open:focus-visible { outline: 2px solid var(--brand-hover); outline-offset: -2px; }
4458
4876
  .prb-notification-dot {
4459
4877
  flex: none;
@@ -4504,9 +4922,9 @@ a.prb-notification-open:focus-visible { outline: 2px solid var(--brand-hover); o
4504
4922
  border: 1px solid var(--border);
4505
4923
  border-left: 3px solid var(--success);
4506
4924
  border-radius: 8px;
4507
- background: var(--bg-elevated, #1f1f24);
4925
+ background: var(--bg-elevated);
4508
4926
  color: var(--text-primary);
4509
- box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
4927
+ box-shadow: 0 6px 24px var(--shadow-float);
4510
4928
  font-size: var(--t-body);
4511
4929
  }
4512
4930
  .prb-toast[data-kind='failed'] { border-left-color: var(--danger); }
@@ -4527,7 +4945,7 @@ a.prb-notification-open:focus-visible { outline: 2px solid var(--brand-hover); o
4527
4945
  line-height: 1;
4528
4946
  cursor: pointer;
4529
4947
  }
4530
- .prb-toast-close:hover { color: var(--text-primary); background: rgba(255, 255, 255, 0.06); }
4948
+ .prb-toast-close:hover { color: var(--text-primary); background: var(--wash-strong); }
4531
4949
  .prb-toast-open:focus-visible,
4532
4950
  .prb-toast-close:focus-visible { outline: 2px solid var(--brand-hover); outline-offset: 2px; }
4533
4951
 
@@ -4544,14 +4962,17 @@ a.prb-notification-open:focus-visible { outline: 2px solid var(--brand-hover); o
4544
4962
  body .prb-body[data-focus] { grid-template-columns: minmax(0, 1fr); }
4545
4963
  body .prb-body[data-focus] .prb-rail { display: none; }
4546
4964
 
4547
- /* A document surface: the review pane takes the width, and gets more room to use it. Issues and
4548
- Questions stay a readable measure; a diff is the one thing here that genuinely wants the width. */
4549
- body .prb-body:is([data-focus='issues'], [data-focus='questions'], [data-focus='files'], [data-focus='guide']) .prb-ask { display: none; }
4550
- body .prb-body:is([data-focus='issues'], [data-focus='questions'], [data-focus='guide']) .pr-helper-sidebar.prb-pane { max-width: 1080px; }
4965
+ /* A document surface, which is every view but the assistant: the review pane takes the width, and
4966
+ gets more room to use it. Prose stays a readable measure; a diff and a drawing are the things here
4967
+ that genuinely want the width. */
4968
+ body .prb-body[data-focus]:not([data-focus='buddy']) .prb-ask { display: none; }
4969
+ body .prb-body[data-focus]:not([data-focus='buddy']) .pr-helper-sidebar.prb-pane { max-width: 1080px; }
4970
+ body .prb-body:is([data-focus='files'], [data-focus='diagram']) .pr-helper-sidebar.prb-pane { max-width: 1600px; }
4551
4971
  /* The rail is where the themes are listed, and it is hidden here, so the picker it stands in for
4552
4972
  below 860px stands in for it now. */
4553
4973
  body .prb-body[data-focus='guide'] .prb-guide-picker { display: flex; }
4554
- body .prb-body[data-focus='files'] .pr-helper-sidebar.prb-pane { max-width: 1600px; }
4974
+ /* An expanded map has room for every stage side by side, which is the point of expanding it. */
4975
+ body .prb-body[data-focus='map']:not([data-focus='buddy']) .pr-helper-sidebar.prb-pane { max-width: 1560px; }
4555
4976
 
4556
4977
  /* The assistant: the review pane goes, and the assistant is a column in the grid again at every
4557
4978
  width. Below 860px it is otherwise a fixed overlay, and an overlay as the whole workspace would
@@ -4606,6 +5027,10 @@ body .prb-body[data-focus='buddy'] .prb-msg-user { max-width: min(90%, 78ch); }
4606
5027
  /* The title row is baseline-aligned for the disclosure's text; an icon has no baseline worth
4607
5028
  aligning, so it centres on the title instead. */
4608
5029
  .prb-doc .prb-doc-head .prb-focus-toggle { align-self: center; margin-left: auto; }
5030
+ /* Beside Analysis details the pair sits together at the right: the disclosure takes the free space,
5031
+ and the toggle follows it rather than splitting the row in two. */
5032
+ .prb-doc .prb-doc-head .prb-doc-provenance { margin-left: auto; }
5033
+ .prb-doc .prb-doc-head .prb-doc-provenance + .prb-focus-toggle { margin-left: -8px; }
4609
5034
 
4610
5035
  /* ── Your prompts, folded ──
4611
5036
  The heading is the toggle. Folded, it keeps a count, so a reviewer knows what is behind it