proctolog 0.1.0 → 0.2.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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -3
  3. data/README.md +15 -4
  4. data/app/controllers/proctolog/application_controller.rb +1 -1
  5. data/app/controllers/proctolog/bursts_controller.rb +1 -1
  6. data/app/controllers/proctolog/choices_controller.rb +3 -1
  7. data/app/controllers/proctolog/decisions_controller.rb +1 -1
  8. data/app/controllers/proctolog/notes_controller.rb +2 -2
  9. data/app/controllers/proctolog/prompts_controller.rb +1 -1
  10. data/app/controllers/proctolog/pull_requests_controller.rb +2 -4
  11. data/app/controllers/proctolog/review_data_controller.rb +3 -1
  12. data/app/controllers/proctolog/shares_controller.rb +27 -0
  13. data/app/helpers/proctolog/decisions_helper.rb +6 -2
  14. data/app/helpers/proctolog/desk_helper.rb +48 -8
  15. data/app/helpers/proctolog/rounds_helper.rb +4 -2
  16. data/app/helpers/proctolog/shares_helper.rb +11 -0
  17. data/app/helpers/proctolog/standings_helper.rb +5 -4
  18. data/app/models/proctolog/burst.rb +17 -8
  19. data/app/models/proctolog/choices.rb +8 -0
  20. data/app/models/proctolog/doctor.rb +9 -1
  21. data/app/models/proctolog/github.rb +1 -1
  22. data/app/models/proctolog/marks.rb +1 -1
  23. data/app/models/proctolog/note.rb +4 -0
  24. data/app/models/proctolog/owner.rb +3 -0
  25. data/app/models/proctolog/pick.rb +12 -0
  26. data/app/models/proctolog/rounds.rb +37 -0
  27. data/app/models/proctolog/share/ngrok.rb +120 -0
  28. data/app/models/proctolog/share/settings.rb +23 -0
  29. data/app/models/proctolog/share/tailscale.rb +103 -0
  30. data/app/models/proctolog/share.rb +101 -0
  31. data/app/models/proctolog/standing.rb +24 -7
  32. data/app/views/proctolog/bursts/_line.html.erb +2 -2
  33. data/app/views/proctolog/bursts/_side.html.erb +6 -5
  34. data/app/views/proctolog/bursts/_side_stop.html.erb +4 -4
  35. data/app/views/proctolog/notes/_card.html.erb +7 -3
  36. data/app/views/proctolog/open_pull_requests/_side.html.erb +4 -3
  37. data/app/views/proctolog/open_pull_requests/_start.html.erb +8 -0
  38. data/app/views/proctolog/open_pull_requests/index.html.erb +4 -5
  39. data/app/views/proctolog/open_pull_requests/start/_first_time.html.erb +3 -0
  40. data/app/views/proctolog/open_pull_requests/start/_nothing_open.html.erb +3 -0
  41. data/app/views/proctolog/open_pull_requests/start/_nothing_written.html.erb +3 -0
  42. data/app/views/proctolog/shares/_line.html.erb +5 -0
  43. data/app/views/proctolog/shares/show.html.erb +48 -0
  44. data/app/views/proctolog/shares/ways/_ngrok.html.erb +6 -0
  45. data/app/views/proctolog/shares/ways/_tailscale.html.erb +2 -0
  46. data/bin/proctolog +92 -6
  47. data/completions/_proctolog +28 -4
  48. data/config/application.rb +23 -4
  49. data/config/initializers/grouse.rb +1 -1
  50. data/config/routes.rb +3 -0
  51. data/docs/manual/bursts.md +7 -2
  52. data/docs/manual/deciding.md +4 -1
  53. data/docs/manual/handover.md +6 -0
  54. data/docs/manual/reading.md +54 -27
  55. data/docs/manual/sharing.md +77 -0
  56. data/lib/proctolog/changelog.rb +50 -0
  57. data/lib/proctolog/version.rb +1 -1
  58. data/public/asking.js +31 -4
  59. data/public/decisions.js +4 -4
  60. data/public/desk.js +10 -6
  61. data/public/keys.js +6 -0
  62. data/public/notes.js +164 -58
  63. data/public/pick.css +21 -0
  64. data/public/respond.css +6 -0
  65. data/public/respond.js +51 -7
  66. data/public/review-drafts.js +47 -9
  67. data/public/review-rounds.js +27 -3
  68. data/public/share.css +58 -0
  69. data/public/side.js +11 -20
  70. data/public/terminal.css +451 -77
  71. data/public/viewer.js +700 -173
  72. metadata +18 -1
data/public/terminal.css CHANGED
@@ -41,9 +41,12 @@
41
41
  --change: var(--ink);
42
42
  --question: var(--ink);
43
43
  --side: 320px;
44
+ --section-gap: 16px;
44
45
  --group-gap: 24px;
45
46
  --row-pad: 4px;
46
47
  --control-gap: 8px;
48
+ --lane-gap: 20px;
49
+ --key-space: 12rem;
47
50
 
48
51
  /* One bead, 2.3px across, every 6px: the line round the plaque's figure,
49
52
  and the only line the desk draws that is not a hairline. It frames the
@@ -128,6 +131,13 @@ h1 {
128
131
  font-size: 0.933rem;
129
132
  }
130
133
 
134
+ .burst .lede,
135
+ .burst .tally,
136
+ .films-head .tally {
137
+ box-sizing: border-box;
138
+ padding-inline: 16px;
139
+ }
140
+
131
141
  /* The title plate has a cut corner and a stippled lower edge. Its full width
132
142
  belongs to the title, with no ornament that could be mistaken for a control. */
133
143
  .burst h1,
@@ -454,7 +464,7 @@ body.shell::before {
454
464
 
455
465
  .side h2 {
456
466
  margin-top: var(--group-gap);
457
- padding: 6px 12px;
467
+ padding: 6px 16px;
458
468
  border-block: 1px solid var(--rule-soft);
459
469
  background: var(--surface-head);
460
470
  color: var(--quiet);
@@ -491,7 +501,10 @@ body.shell::before {
491
501
  white-space: nowrap;
492
502
  }
493
503
 
504
+ /* The ↩ of a pull request the agent's answers came back on asks for the owner. */
494
505
  .side .stops .mark {
506
+ color: var(--ink);
507
+ font-weight: 700;
495
508
  white-space: pre;
496
509
  }
497
510
 
@@ -518,8 +531,7 @@ body.shell::before {
518
531
  background: var(--rest);
519
532
  }
520
533
 
521
- .side .stops [aria-current] .number,
522
- .side .stops [aria-current] .mark {
534
+ .side .stops [aria-current] .number {
523
535
  color: var(--ink);
524
536
  }
525
537
 
@@ -529,11 +541,6 @@ body.shell::before {
529
541
  white-space: normal;
530
542
  }
531
543
 
532
- .side .stops [aria-current] .fraction {
533
- border-color: var(--rule-strong);
534
- color: var(--ink);
535
- }
536
-
537
544
  /* The bars: how far, drawn square */
538
545
 
539
546
  meter,
@@ -768,6 +775,10 @@ body.shell .foot {
768
775
  align-items: center;
769
776
  }
770
777
 
778
+ .modebar .mode > span {
779
+ white-space: nowrap;
780
+ }
781
+
771
782
  .modebar .mode strong {
772
783
  padding: 0 1ch;
773
784
  background: var(--ink);
@@ -946,18 +957,33 @@ body.shell .foot {
946
957
  color: var(--ink);
947
958
  }
948
959
 
960
+ /* The stage, and beside it, for a pair, the panel of its views and regions;
961
+ under them the divider's slider, the caption and the keys, the width of
962
+ the window. */
949
963
  .viewer[open] {
950
964
  display: grid;
965
+ grid-template-columns: minmax(0, 1fr) auto;
951
966
  grid-template-rows: minmax(0, 1fr) auto auto auto;
952
967
  }
953
968
 
954
969
  .viewer .stage {
955
970
  position: relative;
971
+ grid-column: 1;
972
+ grid-row: 1;
956
973
  overflow: hidden;
957
974
  cursor: crosshair;
958
975
  touch-action: none;
959
976
  }
960
977
 
978
+ .viewer .swipe {
979
+ grid-column: 1;
980
+ }
981
+
982
+ .viewer .caption,
983
+ .viewer .keys {
984
+ grid-column: 1 / -1;
985
+ }
986
+
961
987
  .viewer .stage[data-zoomed="true"] {
962
988
  cursor: grab;
963
989
  }
@@ -997,7 +1023,9 @@ body.shell .foot {
997
1023
  }
998
1024
 
999
1025
  /* The swipe's divider and the changed regions: as thin on the screen at any
1000
- zoom, white inside black, so that they show on a light picture and a dark. */
1026
+ zoom, white with black, so that they show on a light picture and a dark.
1027
+ A changed region is a black line dashed white, lighter than a note's solid
1028
+ frame, so the two are told apart at a glance. */
1001
1029
  .viewer .divider,
1002
1030
  .viewer .change {
1003
1031
  position: absolute;
@@ -1024,24 +1052,211 @@ body.shell .foot {
1024
1052
  }
1025
1053
 
1026
1054
  .viewer .change {
1027
- margin: calc(var(--line) * -2);
1028
- border: calc(var(--line) * 2) solid #fff;
1029
- outline: var(--line) solid #000;
1055
+ --dash: calc(var(--line) * 2);
1056
+ margin: calc(var(--dash) * -1);
1057
+ border: var(--dash) solid #000;
1058
+ outline: var(--dash) dashed #fff;
1059
+ outline-offset: calc(var(--dash) * -1);
1030
1060
  }
1031
1061
 
1032
1062
  .viewer .change[data-current] {
1033
- outline-width: calc(var(--line) * 3);
1063
+ --dash: calc(var(--line) * 3);
1064
+ }
1065
+
1066
+ /* The region focused on says its number and its half, above it, as large on
1067
+ the screen at any zoom. */
1068
+ .viewer .change .tag {
1069
+ position: absolute;
1070
+ bottom: 100%;
1071
+ left: calc(var(--line) * -3);
1072
+ margin-bottom: calc(var(--line) * 3);
1073
+ padding: 0 calc(var(--line) * 5);
1074
+ background: #fff;
1075
+ color: #000;
1076
+ font-size: calc(var(--line) * 12);
1077
+ font-weight: 700;
1078
+ line-height: 1.5;
1079
+ white-space: nowrap;
1034
1080
  }
1035
1081
 
1036
1082
  .viewer .swipe {
1037
1083
  margin: 0.3rem 1.1rem;
1038
1084
  }
1039
1085
 
1040
- .viewer .keys .current {
1086
+ /* The map in the stage's corner, once the stage shows only part of the
1087
+ picture: the whole, small, its regions, and the part shown, framed. */
1088
+ .viewer .minimap {
1089
+ position: absolute;
1090
+ right: 12px;
1091
+ bottom: 12px;
1092
+ z-index: 1;
1093
+ padding: 4px;
1094
+ border: 1px solid var(--rule-strong);
1095
+ background: var(--paper);
1096
+ cursor: pointer;
1097
+ --line: 1px;
1098
+ }
1099
+
1100
+ .viewer .minimap[hidden] {
1101
+ display: none;
1102
+ }
1103
+
1104
+ .viewer .map-canvas {
1105
+ position: relative;
1106
+ overflow: hidden;
1107
+ }
1108
+
1109
+ .viewer .map-regions,
1110
+ .viewer .map-shown {
1111
+ pointer-events: none;
1112
+ }
1113
+
1114
+ .viewer .map-region {
1115
+ position: absolute;
1116
+ outline: 1px solid #000;
1117
+ }
1118
+
1119
+ .viewer .map-region[data-current] {
1120
+ outline: 2px solid #fff;
1121
+ }
1122
+
1123
+ .viewer .map-shown {
1124
+ position: absolute;
1125
+ border: 1px solid #fff;
1126
+ outline: 1px solid #000;
1127
+ box-shadow: 0 0 0 100vmax rgb(0 0 0 / 0.45);
1128
+ }
1129
+
1130
+ /* The panel beside a pair, quiet: rows without boxes, the view looked at in
1131
+ reverse with a line on what it shows, the regions by number, and the one
1132
+ focused on as each half has it. It scrolls under the ✕'s corner. */
1133
+ .viewer .compare {
1134
+ grid-column: 2;
1135
+ grid-row: 1;
1136
+ box-sizing: border-box;
1137
+ width: 16rem;
1138
+ min-height: 0;
1139
+ margin-top: 3rem;
1140
+ padding: 0.6rem 0.8rem 1rem;
1141
+ overflow-y: auto;
1142
+ border-left: 1px solid var(--rule);
1143
+ background: var(--panel);
1144
+ font-size: 0.86rem;
1145
+ --line: 1px;
1146
+ }
1147
+
1148
+ .viewer .compare[hidden] {
1149
+ display: none;
1150
+ }
1151
+
1152
+ .viewer .compare h2 {
1153
+ margin: 0 0 0.4rem;
1154
+ color: var(--quiet);
1155
+ font-size: 0.8rem;
1156
+ font-weight: 600;
1157
+ letter-spacing: 0.025em;
1158
+ text-transform: uppercase;
1159
+ }
1160
+
1161
+ .viewer .compare h2 .count {
1041
1162
  color: var(--ink);
1163
+ }
1164
+
1165
+ .viewer .compare section + section {
1166
+ margin-top: 1.4rem;
1167
+ }
1168
+
1169
+ .viewer .compare p {
1170
+ margin: 0.4rem 0;
1171
+ color: var(--quiet);
1172
+ }
1173
+
1174
+ .viewer .compare button {
1175
+ border-color: transparent;
1176
+ cursor: pointer;
1177
+ }
1178
+
1179
+ .viewer .compare .way {
1180
+ display: flex;
1181
+ flex-wrap: wrap;
1182
+ align-items: baseline;
1183
+ gap: 0 1ch;
1184
+ width: 100%;
1185
+ padding: 0.15rem 0.4rem;
1186
+ text-align: left;
1187
+ }
1188
+
1189
+ .viewer .compare .way .shows {
1190
+ display: flex;
1191
+ flex-wrap: wrap;
1192
+ gap: 0 1.2ch;
1193
+ width: 100%;
1194
+ padding-left: calc(1.7em + 1ch);
1195
+ font-size: 0.8rem;
1196
+ }
1197
+
1198
+ .viewer .compare [aria-pressed="true"] kbd {
1199
+ border-color: currentcolor;
1200
+ color: inherit;
1201
+ }
1202
+
1203
+ .viewer .compare .places {
1204
+ display: flex;
1205
+ flex-wrap: wrap;
1206
+ gap: 0.15rem;
1207
+ margin: 0;
1208
+ padding: 0;
1209
+ list-style: none;
1210
+ }
1211
+
1212
+ .viewer .compare .place {
1213
+ min-width: 2em;
1214
+ padding: 0 0.3rem;
1215
+ color: var(--quiet);
1216
+ font-size: 0.8rem;
1217
+ }
1218
+
1219
+ .viewer .compare .place[aria-current] {
1220
+ background: var(--ink);
1221
+ color: var(--paper);
1042
1222
  font-weight: 700;
1043
1223
  }
1044
1224
 
1225
+ .viewer .compare .going {
1226
+ display: flex;
1227
+ flex-wrap: wrap;
1228
+ gap: 0 1.2ch;
1229
+ font-size: 0.8rem;
1230
+ }
1231
+
1232
+ /* The region focused on, as the before has it and as the after does; a
1233
+ press on either writes a note on it there. */
1234
+ .viewer .compare .crop {
1235
+ display: block;
1236
+ width: 100%;
1237
+ margin-top: 0.4rem;
1238
+ padding: 0.3rem;
1239
+ text-align: left;
1240
+ }
1241
+
1242
+ .viewer .compare .crop .frame {
1243
+ position: relative;
1244
+ display: block;
1245
+ overflow: hidden;
1246
+ background: #000;
1247
+ }
1248
+
1249
+ .viewer .compare .crop .label {
1250
+ display: block;
1251
+ margin-top: 0.15rem;
1252
+ color: var(--quiet);
1253
+ font-size: 0.8rem;
1254
+ }
1255
+
1256
+ .viewer .compare .crop:hover:not(:disabled) .label {
1257
+ color: var(--ink);
1258
+ }
1259
+
1045
1260
  /* A region being drawn, and the place a note is being written on: white
1046
1261
  inside black, so that it shows on a light picture and a dark, with all
1047
1262
  outside it dimmed. */
@@ -1057,8 +1272,10 @@ body.shell .foot {
1057
1272
  display: none;
1058
1273
  }
1059
1274
 
1060
- /* The notes on a picture, as thin on the screen at any zoom as the changed
1061
- regions; a pin is a ring. */
1275
+ /* The notes on a picture: a solid frame, black outside and white inside, so
1276
+ that one band shows on a light picture and the other on a dark; heavier
1277
+ than a changed region's dashes, and as heavy on the screen at any zoom. A
1278
+ pin is a small square of the same frame. */
1062
1279
  .viewer .notes {
1063
1280
  position: absolute;
1064
1281
  inset: 0;
@@ -1066,17 +1283,18 @@ body.shell .foot {
1066
1283
  }
1067
1284
 
1068
1285
  .viewer .note {
1286
+ --frame: calc(var(--line) * 2);
1069
1287
  position: absolute;
1070
- margin: calc(var(--line) * -2);
1071
- border: calc(var(--line) * 2) solid #fff;
1072
- outline: var(--line) solid #000;
1288
+ margin: calc(var(--frame) * -2);
1289
+ padding: var(--frame);
1290
+ border: var(--frame) solid #000;
1291
+ box-shadow: inset 0 0 0 var(--frame) #fff;
1073
1292
  }
1074
1293
 
1075
1294
  .viewer .note.pin {
1076
- width: calc(var(--line) * 14);
1077
- height: calc(var(--line) * 14);
1078
- margin: calc(var(--line) * -7);
1079
- border-radius: 50%;
1295
+ width: calc(var(--line) * 12);
1296
+ height: calc(var(--line) * 12);
1297
+ margin: calc(var(--line) * -6 - var(--frame) * 2);
1080
1298
  }
1081
1299
 
1082
1300
  /* A note saved opens at a click, and says so under the pointer. */
@@ -1085,19 +1303,21 @@ body.shell .foot {
1085
1303
  cursor: pointer;
1086
1304
  }
1087
1305
 
1088
- .viewer[data-takes-notes] .note[data-note]:hover {
1089
- border-width: calc(var(--line) * 3);
1306
+ .viewer[data-takes-notes] .note[data-note]:hover,
1307
+ .viewer .note.drafted {
1308
+ --frame: calc(var(--line) * 3);
1090
1309
  }
1091
1310
 
1092
1311
  .viewer .note.drafted {
1093
- border-width: calc(var(--line) * 3);
1094
- box-shadow: 0 0 0 calc(var(--line) * 100000) rgb(0 0 0 / 0.5);
1312
+ box-shadow:
1313
+ inset 0 0 0 var(--frame) #fff,
1314
+ 0 0 0 calc(var(--line) * 100000) rgb(0 0 0 / 0.5);
1095
1315
  }
1096
1316
 
1097
- /* The note's form, beside the place it is about. */
1317
+ /* The note's form, beside the place it is about, over the map. */
1098
1318
  .viewer .beside-note {
1099
1319
  position: absolute;
1100
- z-index: 1;
1320
+ z-index: 2;
1101
1321
  width: min(24rem, calc(100% - 24px));
1102
1322
  cursor: auto;
1103
1323
  }
@@ -1136,7 +1356,7 @@ body.shell .foot {
1136
1356
  position: absolute;
1137
1357
  top: 0.6rem;
1138
1358
  right: 0.8rem;
1139
- z-index: 2;
1359
+ z-index: 3;
1140
1360
  min-width: 2.2rem;
1141
1361
  padding: 0.1rem 0.6ch;
1142
1362
  border: 1px solid var(--rule-strong);
@@ -1180,10 +1400,10 @@ body.shell .foot {
1180
1400
  beside it, set smaller, measures the same; it gives way before the keys
1181
1401
  beside it and the outline do. */
1182
1402
  body[data-page="pull_requests"] > .main {
1183
- --rail: 15rem;
1403
+ --rail: 16rem;
1184
1404
  --measure: min(
1185
1405
  41rem,
1186
- calc(100vw - var(--side) - var(--gutter) - var(--rail) - 18px - 14rem - 28px)
1406
+ calc(100vw - var(--side) - var(--gutter) - var(--rail) - 24px - var(--key-space) - var(--lane-gap) - var(--lane-gap))
1187
1407
  );
1188
1408
  }
1189
1409
 
@@ -1286,15 +1506,60 @@ body[data-page="pull_requests"] .flash {
1286
1506
  font-weight: 400;
1287
1507
  }
1288
1508
 
1289
- .side .stops .fraction {
1290
- min-width: 4ch;
1509
+ .side .stops .decisions {
1291
1510
  padding-left: 0.6ch;
1292
- border-left: 1px solid var(--rule-soft);
1293
1511
  color: var(--quiet);
1294
1512
  font-size: 0.8rem;
1295
1513
  text-align: right;
1296
1514
  }
1297
1515
 
1516
+ /* Decisions counted as beads, the stepper's own (below): a bead to each
1517
+ blocking decision, a smaller one to each non-blocking, hollow while it
1518
+ waits for the owner and solid once reviewed. In the pane they are smaller
1519
+ again. */
1520
+
1521
+ .beads {
1522
+ display: inline-flex;
1523
+ gap: 1ch;
1524
+ align-items: center;
1525
+ vertical-align: middle;
1526
+ }
1527
+
1528
+ .beads > span {
1529
+ display: inline-flex;
1530
+ gap: 3px;
1531
+ align-items: center;
1532
+ }
1533
+
1534
+ .beads .non-blocking .bead {
1535
+ width: 6px;
1536
+ height: 6px;
1537
+ border-color: var(--quiet);
1538
+ }
1539
+
1540
+ .side .beads {
1541
+ gap: 0.8ch;
1542
+ }
1543
+
1544
+ .side .beads > span {
1545
+ gap: 2px;
1546
+ }
1547
+
1548
+ .side .beads .bead {
1549
+ width: 7px;
1550
+ height: 7px;
1551
+ }
1552
+
1553
+ .side .beads .non-blocking .bead {
1554
+ width: 5px;
1555
+ height: 5px;
1556
+ }
1557
+
1558
+ /* A burst's count: a hollow bead of the grade, then how many of it wait. */
1559
+ .side .beads .count {
1560
+ gap: 0.4ch;
1561
+ }
1562
+
1298
1563
  .pull-request .status {
1299
1564
  display: block;
1300
1565
  margin: 8px 0 var(--group-gap);
@@ -1321,7 +1586,7 @@ body[data-page="pull_requests"] .flash {
1321
1586
  .pull-request .whole {
1322
1587
  position: absolute;
1323
1588
  top: 5px;
1324
- left: calc(100% + 28px);
1589
+ left: calc(100% + var(--lane-gap));
1325
1590
  display: none;
1326
1591
  }
1327
1592
 
@@ -1333,7 +1598,7 @@ body[data-page="pull_requests"] .flash {
1333
1598
  .cursor .beside {
1334
1599
  gap: var(--control-gap);
1335
1600
  justify-items: start;
1336
- width: 13.5rem;
1601
+ width: var(--key-space);
1337
1602
  overflow: hidden;
1338
1603
  color: var(--quiet);
1339
1604
  font-size: 0.867rem;
@@ -1408,7 +1673,7 @@ body[data-page="pull_requests"] .flash {
1408
1673
  .cursor .beside {
1409
1674
  position: absolute;
1410
1675
  top: 2px;
1411
- left: calc(var(--gutter) + var(--measure) + 28px);
1676
+ left: calc(var(--gutter) + var(--measure) + var(--lane-gap));
1412
1677
  display: grid;
1413
1678
  pointer-events: auto;
1414
1679
  }
@@ -1430,13 +1695,13 @@ li[data-decision]:focus-visible,
1430
1695
 
1431
1696
  .rail {
1432
1697
  position: fixed;
1433
- top: 36px;
1434
- right: 18px;
1435
- bottom: 56px;
1698
+ top: 32px;
1699
+ left: calc(var(--side) + var(--gutter) + var(--measure) + var(--key-space) + var(--lane-gap) + var(--lane-gap));
1700
+ bottom: calc(var(--foot-height, 44px) + 12px);
1436
1701
  z-index: 2;
1437
1702
  display: flex;
1438
1703
  flex-direction: column;
1439
- gap: var(--group-gap);
1704
+ gap: var(--section-gap);
1440
1705
  width: var(--rail);
1441
1706
  }
1442
1707
 
@@ -1446,17 +1711,20 @@ li[data-decision]:focus-visible,
1446
1711
  .outline {
1447
1712
  display: grid;
1448
1713
  flex: none;
1714
+ max-height: 48%;
1449
1715
  padding: 8px;
1716
+ overflow-y: auto;
1450
1717
  border: 1px solid var(--rule);
1451
1718
  background: var(--panel);
1452
1719
  font-size: 0.867rem;
1720
+ scrollbar-width: thin;
1453
1721
  }
1454
1722
 
1455
1723
  .outline :is(button, a) {
1456
1724
  display: flex;
1457
1725
  gap: 1ch;
1458
1726
  justify-content: space-between;
1459
- padding: 4px 6px 4px 0;
1727
+ padding: 3px 6px 3px 0;
1460
1728
  border: 0;
1461
1729
  background: none;
1462
1730
  color: var(--quiet);
@@ -1502,8 +1770,8 @@ li[data-decision]:focus-visible,
1502
1770
  }
1503
1771
 
1504
1772
  .outline .apart {
1505
- margin-top: 12px;
1506
- padding-top: 12px;
1773
+ margin-top: 8px;
1774
+ padding-top: 8px;
1507
1775
  border-top: 1px solid var(--rule);
1508
1776
  }
1509
1777
 
@@ -1524,6 +1792,7 @@ details.section > summary .count[data-waits="true"] {
1524
1792
  same beads as Standing distinguish what is ready from what still waits. */
1525
1793
 
1526
1794
  .try {
1795
+ flex: 1;
1527
1796
  min-height: 0;
1528
1797
  padding: 0;
1529
1798
  overflow-y: auto;
@@ -1531,10 +1800,16 @@ details.section > summary .count[data-waits="true"] {
1531
1800
  background: var(--surface);
1532
1801
  font-size: 0.867rem;
1533
1802
  line-height: 1.5;
1803
+ overscroll-behavior-y: contain;
1804
+ scroll-padding-block: 12px;
1805
+ scrollbar-gutter: stable;
1534
1806
  scrollbar-width: thin;
1535
1807
  }
1536
1808
 
1537
1809
  .try h2 {
1810
+ position: sticky;
1811
+ top: 0;
1812
+ z-index: 1;
1538
1813
  margin: 0;
1539
1814
  padding: 8px 12px;
1540
1815
  border-bottom: 1px solid var(--rule);
@@ -1622,7 +1897,7 @@ details.section > summary .count[data-waits="true"] {
1622
1897
  .try-pop {
1623
1898
  position: fixed;
1624
1899
  inset: 42px auto auto;
1625
- right: calc(18px + var(--rail) + 14px);
1900
+ right: max(16px, calc(100vw - var(--side) - var(--gutter) - var(--measure) - var(--key-space) - var(--lane-gap) - var(--lane-gap) + 14px));
1626
1901
  width: min(72ch, 56vw);
1627
1902
  max-height: calc(100vh - 110px);
1628
1903
  margin: 0;
@@ -1723,7 +1998,7 @@ details.section > summary .count[data-waits="true"] {
1723
1998
  .try .tail {
1724
1999
  position: fixed;
1725
2000
  top: 42px;
1726
- right: calc(18px + var(--rail) + 14px);
2001
+ right: max(16px, calc(100vw - var(--side) - var(--gutter) - var(--measure) - var(--key-space) - var(--lane-gap) - var(--lane-gap) + 14px));
1727
2002
  z-index: 9;
1728
2003
  display: none;
1729
2004
  width: min(84ch, 52vw);
@@ -1850,7 +2125,7 @@ main.description + details.section {
1850
2125
 
1851
2126
  details.section > summary {
1852
2127
  position: relative;
1853
- gap: 1.4ch;
2128
+ gap: 1ch;
1854
2129
  align-items: baseline;
1855
2130
  margin-inline: -16px;
1856
2131
  padding: 9px 16px;
@@ -1917,6 +2192,7 @@ details.section > summary .count {
1917
2192
  margin-left: 0;
1918
2193
  color: var(--quiet);
1919
2194
  font-size: 0.867rem;
2195
+ white-space: nowrap;
1920
2196
  }
1921
2197
 
1922
2198
  [data-quotable] {
@@ -2471,17 +2747,23 @@ button.choice[hidden] {
2471
2747
  }
2472
2748
 
2473
2749
  .stepper .cell,
2474
- .finish .bead {
2750
+ .finish .bead,
2751
+ .beads .bead {
2475
2752
  box-sizing: border-box;
2476
2753
  width: 9px;
2477
2754
  height: 9px;
2478
2755
  border: 1px solid var(--ink);
2479
2756
  border-radius: 50% !important;
2757
+ }
2758
+
2759
+ .stepper .cell,
2760
+ .finish .bead {
2480
2761
  cursor: pointer;
2481
2762
  }
2482
2763
 
2483
2764
  .stepper .cell:not([data-state=""]),
2484
- .finish [data-line-of]:not([data-state=""]) .bead {
2765
+ .finish [data-line-of]:not([data-state=""]) .bead,
2766
+ .beads .bead.reviewed {
2485
2767
  border-color: var(--quiet);
2486
2768
  background: var(--quiet);
2487
2769
  }
@@ -2865,14 +3147,14 @@ body[data-page="decisions"] .empty {
2865
3147
  }
2866
3148
 
2867
3149
  .part {
2868
- margin-top: var(--group-gap);
3150
+ margin-top: var(--section-gap);
2869
3151
  border: 1px solid var(--rule);
2870
3152
  background: var(--surface);
2871
3153
  }
2872
3154
 
2873
3155
  .part header {
2874
3156
  position: relative;
2875
- padding: 10px 16px;
3157
+ padding: 8px 16px;
2876
3158
  border-top: 0;
2877
3159
  border-bottom: 0;
2878
3160
  background: var(--surface-head);
@@ -3003,14 +3285,14 @@ details.more-pictures > summary {
3003
3285
  cursor rests on, each decision with its pull request above it. */
3004
3286
 
3005
3287
  .theme-group {
3006
- margin-top: var(--group-gap);
3288
+ margin-top: var(--section-gap);
3007
3289
  border: 1px solid var(--rule);
3008
3290
  background: var(--surface);
3009
3291
  }
3010
3292
 
3011
3293
  .theme-group header {
3012
3294
  position: relative;
3013
- padding: 10px 16px;
3295
+ padding: 8px 16px;
3014
3296
  border-top: 0;
3015
3297
  background: var(--surface-head);
3016
3298
  }
@@ -3103,14 +3385,14 @@ details.more-pictures > summary {
3103
3385
 
3104
3386
  .box[aria-current] {
3105
3387
  margin: 0;
3106
- padding: 16px 16px 12px 2.6rem;
3388
+ padding: 12px 16px 12px 2.6rem;
3107
3389
  border-top: 1px solid var(--rule);
3108
3390
  background: var(--rest);
3109
3391
  outline: 0;
3110
3392
  }
3111
3393
 
3112
3394
  .box[aria-current]::before {
3113
- top: 1.45rem;
3395
+ top: 1.2rem;
3114
3396
  }
3115
3397
 
3116
3398
  .box[aria-current] .body {
@@ -3123,12 +3405,12 @@ details.more-pictures > summary {
3123
3405
  .box[aria-current] .body > strong:first-of-type,
3124
3406
  .box[aria-current] .body > p:first-child > strong:first-child {
3125
3407
  display: block;
3126
- margin-bottom: 8px;
3408
+ margin-bottom: 4px;
3127
3409
  }
3128
3410
 
3129
3411
  .box[aria-current] .beneath {
3130
3412
  max-width: none;
3131
- margin-top: 14px;
3413
+ margin-top: 12px;
3132
3414
  font: inherit;
3133
3415
  font-size: 1rem;
3134
3416
  }
@@ -3142,11 +3424,16 @@ details.more-pictures > summary {
3142
3424
  margin-top: 0;
3143
3425
  }
3144
3426
 
3427
+ .box button.choice,
3428
+ .box .beneath > .actions > a {
3429
+ padding-block: 6px;
3430
+ }
3431
+
3145
3432
  /* The cursor at the decision at hand, in the gutter, as on a pull request's page. */
3146
3433
  .decisions .box[aria-current] .body::before {
3147
3434
  content: ">";
3148
3435
  position: absolute;
3149
- top: 14px;
3436
+ top: 10px;
3150
3437
  left: -2.4ch;
3151
3438
  color: var(--ink);
3152
3439
  font-size: 1rem;
@@ -3294,13 +3581,20 @@ details.more-pictures > summary {
3294
3581
  }
3295
3582
 
3296
3583
  .filters {
3297
- gap: var(--control-gap) 2.4ch;
3584
+ gap: 4px 12px;
3298
3585
  align-items: baseline;
3299
3586
  margin-top: 12px;
3300
3587
  padding-top: 10px;
3301
3588
  border-top: 1px solid var(--rule);
3302
3589
  }
3303
3590
 
3591
+ .filters button.quiet,
3592
+ .filters a {
3593
+ min-height: 24px;
3594
+ padding: 2px 6px;
3595
+ white-space: nowrap;
3596
+ }
3597
+
3304
3598
  .filters [data-filter="open"],
3305
3599
  .filters [data-filter="all"] {
3306
3600
  margin-left: 0;
@@ -3309,8 +3603,7 @@ details.more-pictures > summary {
3309
3603
  .filters button.quiet[aria-pressed="true"],
3310
3604
  .filters a[aria-current] {
3311
3605
  border: 0;
3312
- text-decoration: underline;
3313
- text-underline-offset: 0.4em;
3606
+ text-decoration: none;
3314
3607
  background: var(--plate);
3315
3608
  color: var(--panel);
3316
3609
  font-weight: 700;
@@ -3333,6 +3626,7 @@ details.more-pictures > summary {
3333
3626
  }
3334
3627
 
3335
3628
  .filters .by-state > summary {
3629
+ padding-block: 2px;
3336
3630
  color: var(--quiet);
3337
3631
  }
3338
3632
 
@@ -3367,6 +3661,8 @@ body[data-page="scenarios"] main.scenarios {
3367
3661
  }
3368
3662
 
3369
3663
  .server {
3664
+ display: grid;
3665
+ grid-template-columns: minmax(0, 1fr) max-content;
3370
3666
  gap: 4px 2.4ch;
3371
3667
  margin: 16px 0 0;
3372
3668
  padding: 12px 16px;
@@ -3376,6 +3672,9 @@ body[data-page="scenarios"] main.scenarios {
3376
3672
  }
3377
3673
 
3378
3674
  .server .status {
3675
+ grid-column: 1;
3676
+ grid-row: 1;
3677
+ justify-content: flex-start;
3379
3678
  gap: 1.2ch;
3380
3679
  padding: 0;
3381
3680
  border: 0;
@@ -3403,14 +3702,22 @@ body[data-page="scenarios"] main.scenarios {
3403
3702
  }
3404
3703
 
3405
3704
  .server .where {
3406
- flex-basis: 100%;
3705
+ grid-column: 1 / -1;
3407
3706
  min-width: 0;
3408
3707
  margin-left: calc(9px + 1.2ch);
3409
3708
  overflow-wrap: anywhere;
3410
3709
  }
3411
3710
 
3412
- .server .action {
3413
- margin: 0 0 0 calc(9px + 1.2ch);
3711
+ .server > form {
3712
+ grid-column: 2;
3713
+ grid-row: 1;
3714
+ margin: 0;
3715
+ }
3716
+
3717
+ .server > details {
3718
+ grid-column: 1 / -1;
3719
+ min-width: 0;
3720
+ margin-left: calc(9px + 1.2ch);
3414
3721
  }
3415
3722
 
3416
3723
  .scenarios h2 {
@@ -3444,8 +3751,8 @@ body[data-page="scenarios"] main.scenarios {
3444
3751
 
3445
3752
  .scenario {
3446
3753
  grid-template-columns: minmax(0, 1fr) minmax(8rem, 11rem) minmax(10rem, 15rem);
3447
- gap: 12px 2.4ch;
3448
- padding: 16px 0;
3754
+ gap: 0;
3755
+ padding: 0;
3449
3756
  border-top: 1px solid var(--rule);
3450
3757
  background: var(--surface);
3451
3758
  }
@@ -3455,12 +3762,12 @@ body[data-page="scenarios"] main.scenarios {
3455
3762
  }
3456
3763
 
3457
3764
  .scenario > .text {
3458
- padding-left: 12px;
3765
+ padding: 12px;
3459
3766
  }
3460
3767
 
3461
3768
  .scenario > .act,
3462
3769
  .scenario > .seen {
3463
- padding: 8px 12px;
3770
+ padding: 12px;
3464
3771
  border-left: 1px solid var(--rule);
3465
3772
  background: var(--surface-head);
3466
3773
  }
@@ -3501,6 +3808,7 @@ body[data-page="scenarios"] main.scenarios {
3501
3808
  font-weight: 700;
3502
3809
  text-decoration: underline;
3503
3810
  text-underline-offset: 0.22em;
3811
+ text-align: left;
3504
3812
  }
3505
3813
 
3506
3814
  .open:disabled {
@@ -3511,7 +3819,14 @@ body[data-page="scenarios"] main.scenarios {
3511
3819
  }
3512
3820
 
3513
3821
  .results {
3514
- gap: var(--control-gap) 1.6ch;
3822
+ display: grid;
3823
+ grid-template-columns: repeat(2, max-content);
3824
+ gap: var(--control-gap) 12px;
3825
+ }
3826
+
3827
+ .results .result {
3828
+ min-height: 24px;
3829
+ text-align: left;
3515
3830
  }
3516
3831
 
3517
3832
  .result,
@@ -3777,7 +4092,7 @@ body[data-page="prompts"] .main > details {
3777
4092
 
3778
4093
  .round-results h2 {
3779
4094
  margin: 0 0 6px;
3780
- padding: 8px 80px 8px 12px;
4095
+ padding: 8px 80px 8px 16px;
3781
4096
  border: 1px solid var(--rule);
3782
4097
  background: var(--surface-head);
3783
4098
  font-size: 1rem;
@@ -3785,6 +4100,8 @@ body[data-page="prompts"] .main > details {
3785
4100
  }
3786
4101
 
3787
4102
  .round-results > p {
4103
+ box-sizing: border-box;
4104
+ padding-inline: 16px;
3788
4105
  color: var(--quiet);
3789
4106
  font-size: 0.867rem;
3790
4107
  }
@@ -3801,6 +4118,10 @@ body[data-page="prompts"] .main > details {
3801
4118
  cursor: pointer;
3802
4119
  }
3803
4120
 
4121
+ .round-results > button {
4122
+ margin-inline-start: 16px;
4123
+ }
4124
+
3804
4125
  .round-result {
3805
4126
  position: relative;
3806
4127
  border-top: 1px solid var(--rule);
@@ -3938,6 +4259,10 @@ body[data-page="bursts"] > .main {
3938
4259
  text-align: right;
3939
4260
  }
3940
4261
 
4262
+ .lines .stands strong {
4263
+ color: var(--ink);
4264
+ }
4265
+
3941
4266
  .lines .number {
3942
4267
  margin: 0;
3943
4268
  color: var(--quiet);
@@ -4181,15 +4506,21 @@ details.section[open] > summary {
4181
4506
  background-image: var(--grain-dots);
4182
4507
  }
4183
4508
 
4509
+ /* Spare width belongs to setup details, not to longer lines of prose. */
4510
+ @media (min-width: 110rem) {
4511
+ body[data-page="pull_requests"] > .main {
4512
+ --rail: 20rem;
4513
+ }
4514
+ }
4515
+
4184
4516
  /* A window too narrow for the column, its keys and the outline at once gives
4185
4517
  up the outline first: the reading keeps its width, and [ and ] still go
4186
4518
  from section to section. */
4187
-
4188
4519
  @media (max-width: 88rem) {
4189
4520
  body[data-page="pull_requests"] > .main {
4190
4521
  --measure: min(
4191
4522
  41rem,
4192
- calc(100vw - var(--side) - var(--gutter) - 14rem - 28px)
4523
+ calc(100vw - var(--side) - var(--gutter) - var(--key-space) - var(--lane-gap) - 24px)
4193
4524
  );
4194
4525
  }
4195
4526
 
@@ -4206,8 +4537,24 @@ details.section[open] > summary {
4206
4537
  }
4207
4538
 
4208
4539
  .try {
4540
+ flex: none;
4209
4541
  overflow: visible;
4210
4542
  }
4543
+
4544
+ .try h2 {
4545
+ position: static;
4546
+ }
4547
+
4548
+ .try-pop {
4549
+ inset: 24px 16px auto;
4550
+ width: auto;
4551
+ max-width: 72ch;
4552
+ margin-inline: auto;
4553
+ }
4554
+
4555
+ .try .tail {
4556
+ right: 16px;
4557
+ }
4211
4558
  }
4212
4559
 
4213
4560
  /* Narrower still, the route folds over the page, and a block's keys are named
@@ -4320,14 +4667,41 @@ details.section[open] > summary {
4320
4667
  }
4321
4668
 
4322
4669
  .modebar {
4670
+ display: flex;
4323
4671
  flex-wrap: wrap;
4324
- gap: 8px;
4325
- padding-block: 8px;
4672
+ gap: 4px 8px;
4673
+ padding-block: 4px;
4326
4674
  }
4327
4675
 
4328
4676
  .modebar .mode {
4329
- flex-basis: 100%;
4330
- gap: 4px 12px;
4677
+ display: contents;
4678
+ font-size: 0.8rem;
4679
+ }
4680
+
4681
+ .modebar .all-keys {
4682
+ padding-block: 1px;
4683
+ }
4684
+
4685
+ .draft-tools {
4686
+ padding-block: 8px;
4687
+ }
4688
+
4689
+ .draft-tools .bar {
4690
+ gap: 8px;
4691
+ }
4692
+
4693
+ .progress-line {
4694
+ gap: 4px 8px;
4695
+ }
4696
+
4697
+ .progress-line meter,
4698
+ .progress-line meter::-webkit-meter-bar {
4699
+ width: 4rem;
4700
+ }
4701
+
4702
+ .filters {
4703
+ margin-top: 8px;
4704
+ padding-top: 8px;
4331
4705
  }
4332
4706
 
4333
4707
  .keys-help {