~bigbes/sr-ht-spec

ref: 394285f4cb998359c0a127454de4d32284a3ef22 sr-ht-spec/scss/main.scss -rw-r--r-- 22.3 KiB
394285f4 — Eugene Blikh chore(beads): file spec-rsb and spec-ovo, the two admin commands 13 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
// spec.sr.ht stylesheet.
//
// Mirrors the SourceHut service pattern (see paste.sr.ht/scss/main.scss and
// compare.sr.ht/scss/main.scss): pull in the shared `base` partial (Bootstrap 4
// plus the SourceHut chrome — contrast, variables, nav, events, highlight,
// dark) and then add only what is unique to the document views.
//
// Build: `make css`, which is `sassc -I /usr/share/sourcehut/scss scss/main.scss`
// plus minify plus a content-hashed filename. The shared partials come from
// core.sr.ht's `make install`; dart-sass works too for a local build.
//
// The output is EMBEDDED INTO THE BINARY (web/templates.go's //go:embed static),
// so `make css` alone does not restyle a running daemon: build the CSS, then
// `go build`, then restart.

@import "base";

// Let scheme-aware widgets follow prefers-color-scheme, as compare.sr.ht does.
:root {
  color-scheme: light dark;
}

// ---- Rendered document ----------------------------------------------------

.spec-doc {
  // Prose, not code: a comfortable measure matters more than filling the
  // column. The sidebar already takes a quarter of the width, so this only
  // bites on very wide screens.
  max-width: 46rem;

  h1, h2, h3, h4, h5, h6 {
    margin-top: 1.5rem;
  }

  // The first heading sits directly under the page title; the extra top margin
  // reads as a gap rather than as structure.
  > :first-child {
    margin-top: 0;
  }

  blockquote {
    padding-left: 0.75rem;
    border-left: 3px solid $gray-400;
    color: $gray-700;

    @media (prefers-color-scheme: dark) {
      border-left-color: $gray-700;
      color: $gray-300;
    }
  }

  table {
    @extend .table;
    width: auto;
  }

  img {
    max-width: 100%;
  }
}

// A wikilink that resolved to nothing. The renderer emits it as a span rather
// than dropping it, so the read plane doubles as a link checker — which only
// works if it is visibly different from a link that resolved.
.wikilink-missing {
  color: $danger;
  text-decoration: line-through dotted;
}

// ---- Search ---------------------------------------------------------------

.search-hit {
  margin-bottom: 0.75rem;
}

.search-snippet {
  color: $gray-700;

  // bleve wraps matched terms in <mark>; the browser default is a yellow that
  // is unreadable on the dark chrome.
  mark {
    padding: 0;
    background: transparent;
    color: inherit;
    font-weight: 700;
  }

  @media (prefers-color-scheme: dark) {
    color: $gray-300;
  }
}

// ---- Proposal review ------------------------------------------------------

.proposal-actions {
  margin: 1rem 0;

  form {
    margin-right: 0.5rem;
  }
}

.proposal-doc {
  margin-top: 1.5rem;
  padding-top: 1rem;
  border-top: 1px solid $gray-300;

  @media (prefers-color-scheme: dark) {
    border-top-color: $gray-700;
  }
}

// ---- Prose diff -----------------------------------------------------------
//
// A unified, line-numbered diff table (web/diffrows.go emits it): four columns
// — old number, new number, sign, text — one row per source line, plus a
// full-width `ph-notes` row per block carrying that block's review threads.
//
// This replaced a block-card renderer, and the reasons it was rejected are the
// reasons these rules look the way they do. The cards labelled every block
// ("ADDED PARAGRAPH", "MODIFIED PARAGRAPH") and boxed it; on a new document the
// whole page is one change, so every card carried identical chrome and the
// labels outweighed the prose they described. Two columns of digits say the
// same thing and then get out of the way, which is the whole point of a gutter.
//
// The markup is a TABLE on purpose. Prose lines wrap — often to three or four
// visual lines — and a line's number has to stay on the FIRST of them. Only a
// table cell does that for free (`vertical-align: top`); a flex row centres the
// number and a grid row stretches it.

.prosediff {
  // ONE row metric for the whole diff, in absolute units. The gutter is small
  // tabular monospace and the prose is larger body text; if each side kept its
  // own unitless line-height, the two would build different row boxes and the
  // numerals would drift off the text baseline the moment the sizes diverged.
  // Everything that participates in a row reads this variable and nothing sets
  // its own line-height.
  --row-lh: 1.4rem;

  // Settled in the bead's review: 34px number tracks. Four digits of tabular
  // figures fit; anything wider and the gutter stops being a margin.
  --num-w: 34px;
  --sign-w: 1.25rem;

  // The rail ground and its single hairline are named once here, so light and
  // dark differ in one place rather than in a dozen nested media queries.
  --rail-bg: #{$gray-100};
  --rail-line: #{$gray-300};
  --num-fg: #{$gray-600};

  // Change tints have to be re-mixed for dark: an alpha that reads as a wash
  // over white is invisible over near-black.
  --ins-tint: #{rgba($success, 0.1)};
  --del-tint: #{rgba($danger, 0.1)};
  --move-tint: #{rgba($info, 0.08)};
  --sel-tint: #{rgba($info, 0.26)};

  font-size: 0.95rem;

  @media (prefers-color-scheme: dark) {
    --rail-bg: #{$gray-800};
    --rail-line: #{$gray-700};
    --num-fg: #{$gray-500};
    --ins-tint: #{rgba($success, 0.22)};
    --del-tint: #{rgba($danger, 0.22)};
    --move-tint: #{rgba($info, 0.18)};
    --sel-tint: #{rgba($info, 0.3)};
  }

  .ph-diff {
    width: 100%;
    // Fixed layout pins the three narrow columns to the widths declared below
    // instead of letting the longest line in the document negotiate them, and
    // it is what makes the rail the same width in every table on the page.
    table-layout: fixed;
    border-collapse: collapse;
    margin: 0;
  }

  // Cells never get their own vertical rhythm: they inherit the row metric and
  // hang from the top, which is what keeps a number level with the first visual
  // line of the wrapped line beside it. `height` on a table cell behaves as a
  // minimum, so a blank source line still occupies a full row instead of
  // collapsing to nothing.
  .ph-row > td {
    padding: 0;
    line-height: var(--row-lh);
    height: var(--row-lh);
    vertical-align: top;
  }

  // ---- the rail ----
  //
  // The review's exact complaint about the first attempt was that a border on
  // each cell drew a CAGE. So: one ground colour behind BOTH number tracks
  // (they are adjacent collapsed cells of the same colour, so there is no seam
  // to see) and exactly one hairline, on the right edge of the new-number
  // track, dividing the rail from the content. No horizontal rules at all — the
  // line height is what separates the rows.
  .ph-n {
    width: var(--num-w);
    padding: 0 0.35rem 0 0.15rem;
    background-color: var(--rail-bg);
    color: var(--num-fg);
    font-family: $font-family-monospace;
    font-size: 0.78rem;
    // Digits must not change width between rows, or the right-aligned column
    // jitters as the numbers grow.
    font-variant-numeric: tabular-nums;
    text-align: right;
    // The numbers are chrome, not content: leaving them selectable means every
    // copied diff arrives with the gutter interleaved into the prose. The sign
    // and the text stay selectable, so a copied selection is still a usable
    // diff.
    -webkit-user-select: none;
    user-select: none;
    cursor: default;
  }

  .ph-n-new {
    border-right: 1px solid var(--rail-line);
  }

  .ph-sign {
    width: var(--sign-w);
    padding: 0 0.25rem;
    font-family: $font-family-monospace;
    font-size: 0.78rem;
    text-align: center;
  }

  .ph-text {
    padding: 0 0.6rem;
    // Fixed table layout has no escape hatch for an unbreakable token, so a
    // pasted URL would otherwise push the text column past the page.
    overflow-wrap: break-word;
  }

  // Code fences, frontmatter and raw HTML blocks: significant whitespace, and
  // read line by line rather than as a measure of prose.
  .ph-mono {
    font-family: $font-family-monospace;
    font-size: 0.85em;
    white-space: pre-wrap;
  }

  // ---- change tints ----
  //
  // The tint starts at the SIGN column and never touches the gutter, so the
  // rail reads as the frame of the diff rather than as part of the change. This
  // is also why the tint is set on the two content cells and not on the row.
  .ph-r-ins {
    > .ph-sign,
    > .ph-text {
      background-color: var(--ins-tint);
    }

    > .ph-sign {
      color: $success;
    }
  }

  .ph-r-del {
    > .ph-sign,
    > .ph-text {
      background-color: var(--del-tint);
    }

    > .ph-sign {
      color: $danger;
    }
  }

  // A move is not an edit: the text is unchanged, only its position moved, so
  // it gets the informational colour and no red or green.
  .ph-r-move {
    > .ph-sign,
    > .ph-text {
      background-color: var(--move-tint);
    }

    > .ph-sign {
      color: $info;
    }

    > .ph-text {
      color: $gray-700;

      @media (prefers-color-scheme: dark) {
        color: $gray-400;
      }
    }
  }

  // A marker row is the renderer speaking: "paragraph moved here (was line 3)".
  // It sits in the text column, in the same face as the prose it introduces, and
  // a reviewer skimming a moved block would otherwise read it as the block's
  // first sentence. Italic and quiet is enough to place it outside the document
  // without giving it the weight of a heading.
  .ph-marker > .ph-text {
    font-style: italic;
    font-size: 0.85em;
    color: $gray-600;

    @media (prefers-color-scheme: dark) {
      color: $gray-400;
    }
  }

  // The region fallback (a modified block the word mapper could not attribute
  // per line) states a line RANGE in the gutter — "12–15" — because a single
  // number there would be a guess. Five monospace glyphs do not fit 34px, and
  // the track cannot grow, so the range drops to the proportional face, which
  // is narrow enough for a two-plus-two-digit range on one line. A four-digit
  // document will still overflow that, so wrapping stays allowed: the range
  // then breaks after its dash and its two ends land on the row's first two
  // lines. Either way the whole range is in the cell's title.
  .ph-region .ph-n {
    font-family: inherit;
    font-size: 0.62rem;
    padding-left: 0;
    padding-right: 0.1rem;
    white-space: normal;
    overflow-wrap: anywhere;
  }

  // ---- sticky section readout ----
  //
  // There is no per-hunk breadcrumb any more: it restated a heading that was
  // visible a few rows above, and on a new file it restated it on every hunk.
  // Instead the heading row itself pins, so the section is only ever named when
  // its heading has scrolled off. The background must be opaque or the rows
  // scroll through it, and it is the rail colour so the band reads as one strip
  // rather than as a heading with a differently-coloured gutter.
  //
  // The ROW is sticky as well as its cells, and it carries a background of its
  // own, because sticky cells alone do not survive a second heading. Every
  // heading pins at top: 0 and none of them ever unpins — the containing block
  // of a table part is the whole table, not the section — so from the second
  // heading onwards two of them share the same 22px band, and the newest one
  // has to cover the rest. Measured in Chrome 141: with sticky cells only, the
  // earlier heading's TEXT paints above the later heading's cell background and
  // the two section names render on top of each other ("## Storalge"); a sticky
  // row without a background does the same; a sticky row WITH a background is
  // the one combination that hides the older headings completely. The cell rule
  // stays underneath it because Safari sticks table cells but not table rows.
  tr.ph-head {
    position: sticky;
    top: 0;
    z-index: 2;
    background-color: var(--rail-bg);
  }

  .ph-head > td {
    position: sticky;
    top: 0;
    z-index: 2;
    background-color: var(--rail-bg);
    font-weight: 700;
  }

  // ...but a sticky heading that is also an insert has to keep its tint, or an
  // added section stops looking added the moment it pins.
  .ph-head.ph-r-ins > .ph-sign,
  .ph-head.ph-r-ins > .ph-text {
    background-image: linear-gradient(var(--ins-tint), var(--ins-tint));
  }

  .ph-head.ph-r-del > .ph-sign,
  .ph-head.ph-r-del > .ph-text {
    background-image: linear-gradient(var(--del-tint), var(--del-tint));
  }

  // ---- selection ----
  //
  // Set by diff.js when the reviewer drags across the gutter. Unlike a change
  // tint this one DOES cover the number cells: the selection is about the lines
  // themselves, so the rail is part of what is selected. `background-image`
  // rather than `background-color` so it paints over both the rail ground and
  // the change tint without having to out-specify either.
  .ph-sel > td {
    background-image: linear-gradient(var(--sel-tint), var(--sel-tint));
  }

  // diff.js gives the table a single tab stop and walks the rows with the
  // arrow keys, so the focused row has to be visible as a row — inset so the
  // ring does not overlap the neighbouring line's tint.
  .ph-row:focus-visible {
    outline: 2px solid $info;
    outline-offset: -2px;
  }

  // ---- folding ----
  //
  // A long run of untouched context collapses behind a checkbox, not behind a
  // script: folding is the one interaction a reviewer needs before deciding
  // whether the page is worth trusting, so it works with JavaScript off.
  .ph-folded {
    display: none;
  }

  .ph-fold:has(.ph-fold-cb:checked) .ph-folded {
    display: table-row;
  }

  .ph-fold-head > td {
    padding: 0.1rem 0.6rem;
    background-color: var(--rail-bg);
    border-top: 1px solid var(--rail-line);
    border-bottom: 1px solid var(--rail-line);
    font-size: 0.8rem;
    color: var(--num-fg);
  }

  // Hidden from sight, NOT from the tab order: `display: none` on the checkbox
  // would take the only control of the fold away from the keyboard. This is the
  // clip-rect pattern, which leaves the input focusable, and the focus ring is
  // drawn on the label so there is something visible to focus.
  .ph-fold-cb {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    border: 0;
  }

  .ph-fold-cb + label {
    display: inline-block;
    margin: 0;
    padding: 0 0.2rem;
    cursor: pointer;
    text-decoration: underline dotted;
  }

  .ph-fold-cb:focus-visible + label {
    outline: 2px solid $info;
    outline-offset: 1px;
  }

  .ph-fold-cb:checked + label {
    text-decoration: none;
    opacity: 0.7;
  }

  // ---- word-level marks ----
  //
  // A deletion is struck through in danger; an insertion keeps the browser's
  // underline off and is coloured in success instead, because a line of
  // underlined prose is harder to read than the same line in green. Both carry
  // a faint background so a one-word change inside a long line is visible
  // without reading the colour.
  del {
    text-decoration: line-through;
    color: $danger;
    background: rgba($danger, 0.12);
    text-decoration-thickness: 1px;
  }

  ins {
    text-decoration: none;
    color: darken($success, 8%);
    background: rgba($success, 0.14);

    @media (prefers-color-scheme: dark) {
      color: lighten($success, 8%);
    }
  }

  // ---- the block's review threads ----
  //
  // A full-width cell under the block it belongs to. It is indented to the text
  // column so a thread lines up with the prose it criticises, and it carries no
  // rail: the rail numbers lines, and this row is not a line.
  .ph-notes > td {
    padding: 0.25rem 0.6rem 0.4rem
      calc(var(--num-w) * 2 + var(--sign-w) + 0.6rem);
  }

  // On a phone that indent is a quarter of the screen, and a review comment is
  // the one thing on this page that has to stay readable at any width. Below
  // Bootstrap's sm breakpoint the alignment gives way to the text.
  @media (max-width: 575.98px) {
    .ph-notes > td {
      padding-left: 0.6rem;
    }
  }

  // ---- the composer, once JavaScript is in ----
  //
  // The server renders a shut `<details class="ph-compose">Comment on this
  // block</details>` under EVERY block, and it has to: with diff.js absent that
  // control is the only way to comment, so removing it from the template would
  // take commenting away from anyone without JavaScript. Drawn as-is, though, a
  // seventeen-block document shows seventeen identical "Comment on this block"
  // summaries, each on its own full-width row with an empty gutter beside it.
  // That is the same identical-chrome-under-every-block that got the block-card
  // renderer rejected, and it chops the continuous run of numbered lines this
  // table exists to be into seventeen pieces.
  //
  // So the composer is hidden only when there is another way to reach it.
  // diff.js marks each table it has successfully wired `.ph-js`, and under that
  // class a notes row is drawn only if it has something to show: a thread
  // (someone's critique is content, not chrome), an OPEN composer — which is
  // how selecting lines brings it back, since diff.js sets `open` on the
  // composer of the block whose lines were picked, by mouse or by keyboard —
  // or a composer marked `.ph-draft`. Selecting the lines is the point of the
  // port; diff.js also writes the one `.ph-hint` line below that says the
  // gutter is draggable.
  //
  // `.ph-draft` is diff.js saying "there are words in this textarea that have
  // not been posted", and it is what stops this rule from eating a review
  // someone is in the middle of writing. Without it, collapsing the composer
  // from its own summary hides the row, and the disclosure control the reviewer
  // would click to get their text back goes with it. An empty composer is
  // chrome and this rule may hide it; one holding a draft is content, exactly
  // as a posted thread is, and stays drawn until it is posted or emptied.
  //
  // DO NOT "tidy this up" by deleting the composer from threads.html, and do
  // not drop the `.ph-js` qualifier: with JavaScript off no table is marked,
  // this rule matches nothing, and every block keeps its visible composer,
  // which is the only reason hiding it here is honest.
  .ph-diff.ph-js tr.ph-notes:not(:has(.ph-threads)):not(:has(.ph-compose[open])):not(:has(.ph-compose.ph-draft)) {
    display: none;
  }

  // ...and the same draft outranks the fold. A notes row inside a collapsed run
  // carries `.ph-folded` like the lines around it, so closing the fold again
  // hides it — including when the reviewer opened its composer from inside the
  // fold and typed into it. Measured in Chrome 150 before this rule: the
  // composer stayed `open` with the text intact, its row computed `display:
  // none` and the textarea measured 0px tall, so a half-written critique was
  // still in the document with nothing on the page to say so.
  //
  // The rejected fix was to refuse the fold's second click while a composer was
  // open inside it. That makes a control that does nothing when clicked, and it
  // needs script to enforce, which would take folding away from the no-JS path
  // it was built for. Letting the fold close and keeping the one row that holds
  // typed text is the honest half: the lines go, the words stay, and the range
  // diff.js prefixed onto the note ("Lines 16–17 · Anchors to …") is what says
  // which folded lines they were about.
  .ph-diff.ph-js tr.ph-notes.ph-folded:has(.ph-compose.ph-draft) {
    display: table-row;
  }

  // The hint diff.js writes above the table, standing in for those seventeen
  // summaries. It has to weigh less than any one of them, so it takes the
  // gutter's size and colour: one quiet line, above the diff and outside it,
  // read once and then ignored.
  .ph-hint {
    margin: 0 0 0.25rem;
    font-size: 0.8rem;
    color: var(--num-fg);
  }
}

// The composer and each thread state which block they attach to, because
// selection is by line while anchoring is by block — the indirection is real,
// so it is written down rather than left for the reviewer to infer. Server
// rendered, so it is honest with JavaScript off; diff.js only prefixes the
// selected line range onto it. Outside .prosediff because a thread whose anchor
// was lost is rendered on the page itself, away from any diff table.
.ph-anchor-note {
  margin: 0 0 0.3rem;
  font-size: 0.8rem;
  color: $gray-600;

  code {
    font-size: inherit;
  }

  @media (prefers-color-scheme: dark) {
    color: $gray-400;
  }
}

// ---- Review threads -------------------------------------------------------
//
// Rendered both inside a diff block (web/diff.go executes "blockthreads" into
// the block's markup) and, for comments whose anchor is lost, in the page's own
// area — so these styles are outside .prosediff and apply in both places.

.ph-threads {
  margin: 0.35rem 0 0.15rem;
}

.ph-thread {
  margin: 0.35rem 0;
  padding: 0.4rem 0.6rem;
  border-left: 3px solid $info;
  background: rgba($info, 0.06);
  border-radius: 2px;

  // A resolved thread is history: it stays readable and stays where it was, but
  // stops asking for attention the way an open one does.
  &.ph-thread-resolved {
    border-left-color: $gray-400;
    background: transparent;
    opacity: 0.7;
  }
}

.ph-thread-head {
  font-size: 0.8rem;
  margin-bottom: 0.2rem;

  .ph-author {
    font-weight: 700;
  }

  .ph-when {
    color: $gray-600;
    margin-left: 0.35rem;

    @media (prefers-color-scheme: dark) {
      color: $gray-400;
    }
  }

  .badge {
    margin-left: 0.35rem;
  }
}

// A comment is prose someone typed, newlines included; the server escapes it and
// the browser must not then reflow it into one paragraph.
.ph-comment-body {
  white-space: pre-wrap;
  word-break: break-word;
}

.ph-reply {
  margin-top: 0.4rem;
  padding-left: 0.6rem;
  border-left: 2px solid $gray-400;

  @media (prefers-color-scheme: dark) {
    border-left-color: $gray-700;
  }
}

.ph-thread-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.4rem;
}

// The compose and reply forms fold away behind a summary: the review page shows
// one control per block, and a textarea per block would bury the diff.
.ph-form {
  font-size: 0.85rem;

  > summary {
    cursor: pointer;
    color: $gray-600;

    @media (prefers-color-scheme: dark) {
      color: $gray-400;
    }
  }

  textarea {
    margin: 0.3rem 0;
  }
}

.ph-compose {
  margin: 0.2rem 0 0.4rem;
}

// Comments whose block is gone. The warning border is the point: they are shown
// because dropping them would hide a critique, and marked because they no longer
// describe anything on this page.
.ph-lost {
  border-top-color: $warning;

  .ph-thread {
    border-left-color: $warning;
    background: rgba($warning, 0.06);
  }
}