typebulb 0.60.0 → 0.60.2

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.
@@ -181,13 +181,13 @@ body {
181
181
  session list). Wrap = positioning context; the popover anchors bottom/right and
182
182
  grows up-and-left — the chips sit in the right-aligned cluster, so left:0 would
183
183
  run them off the viewport's right edge. */
184
- .token-wrap, .sid-wrap, .servers-wrap, .model-wrap, .gitdiff-wrap { position: relative; display: inline-block; }
184
+ .token-wrap, .sid-wrap, .servers-wrap, .model-wrap, .gitdiff-wrap, .children-wrap { position: relative; display: inline-block; }
185
185
  /* Each pill renders its wrap unconditionally, emitting an empty div when it has
186
186
  nothing to show (no tokens / no session / nothing running). An empty wrap is
187
187
  still a flex child, so .statusbar-actions' gap lands on both its sides —
188
188
  doubling the visible gap between its neighbours. Collapse the empties so the
189
189
  cluster spaces evenly regardless of which pills are present. */
190
- .token-wrap:empty, .sid-wrap:empty, .servers-wrap:empty, .model-wrap:empty, .gitdiff-wrap:empty { display: none; }
190
+ .token-wrap:empty, .sid-wrap:empty, .servers-wrap:empty, .model-wrap:empty, .gitdiff-wrap:empty, .children-wrap:empty { display: none; }
191
191
  .picker, .servers-pop {
192
192
  --menu-h: 428px; /* one height for both statusbar menus — they sit side by side, so a
193
193
  mismatched cap reads as a glitch; .picker grows to it, .servers-pop
@@ -644,9 +644,11 @@ body.tips-off [data-tip]::after { display: none; }
644
644
  .picker-row:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
645
645
  /* Keyboard cursor (arrow keys / hover) — same borderless tint as the launcher's .server-row.active. */
646
646
  .picker-row.active { background: color-mix(in srgb, var(--accent) 16%, transparent); }
647
- /* The attached session — a leading accent dot in its own gutter slot, reserved (transparent) on
648
- every row so titles align whether or not it's shown, and filled only on the attached row so it
649
- reads as "you are here" even when the cursor has moved off. */
647
+ /* One dot in a reserved gutter slot, transparent on every row so titles align whether or not it
648
+ shows. Green = the harness reports this session mid-turn (the live-app axis, the launcher's
649
+ running-:port green); accent = "you are here", and it wins, because the row you are reading is
650
+ self-evidently working and a second mark at 7px would say nothing new. Deliberately NOT process
651
+ liveness, which is true of every window left open — see TB-Agent-Mirror-Ready.md. */
650
652
  .picker-dot {
651
653
  flex: none;
652
654
  align-self: center;
@@ -655,6 +657,8 @@ body.tips-off [data-tip]::after { display: none; }
655
657
  border-radius: 50%;
656
658
  background: transparent;
657
659
  }
660
+ .picker-dot.working { background: var(--fold-marker); }
661
+ /* Higher specificity than .working on purpose: attachment outranks the turn state. */
658
662
  .picker-row.current .picker-dot { background: var(--accent); }
659
663
  .picker-preview {
660
664
  flex: 1; min-width: 0;
@@ -670,6 +674,12 @@ body.tips-off [data-tip]::after { display: none; }
670
674
  (--fold-marker) when the session changed since the mirror opened and hasn't been viewed. */
671
675
  .picker-time.unseen { color: var(--fold-marker); }
672
676
  .picker-hits { color: var(--muted); font-size: .72rem; white-space: nowrap; }
677
+ /* A full-text hit inside a session's child transcript (TB-Agent-Children.md): indented under its
678
+ parent's row and muted, at the SAME size — the row's distinguishing content is the agent's
679
+ description, so shrinking it is the last thing that should happen to it. */
680
+ .picker-row.child { padding-left: 1.9rem; }
681
+ .picker-row.child .picker-preview { color: var(--muted); }
682
+ .picker-child-kind { flex: none; max-width: 7rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); opacity: .8; }
673
683
  /* Full-text result context: first matching turn, indented past the dot gutter into the title column. */
674
684
  .picker-snippet {
675
685
  color: var(--muted);
@@ -947,21 +957,20 @@ a.server-port:hover { text-decoration: underline; }
947
957
  }
948
958
  .bulb-filter::placeholder { color: var(--muted); }
949
959
  .bulb-filter:focus { outline: none; border-color: var(--accent); }
960
+ /* The inset controls (clear ×, then the mode toggle) flow in one cluster flush to the input's
961
+ top/right/bottom, inside its 1px border — so neither carries an offset for the other's width. */
962
+ .bulb-filter-inset { position: absolute; right: 1px; top: 1px; bottom: 1px; display: flex; align-items: stretch; }
950
963
  /* Clear (×) — only rendered when the filter is non-empty; wipes it and refocuses the box. */
951
964
  .bulb-filter-clear {
952
- position: absolute; right: .3rem; top: 50%; transform: translateY(-50%);
953
965
  appearance: none; border: none; background: transparent; cursor: pointer;
954
966
  color: var(--muted); font-size: 1.05rem; line-height: 1; padding: 0 .3rem; border-radius: 4px;
955
967
  }
956
968
  .bulb-filter-clear:hover { color: var(--accent); }
957
- /* Filter-mode toggle (full-text search) — inset at the input's right edge; the clear × shifts
958
- inward beside it. Both comboboxes render it (SearchComboboxPill's modeToggle). */
969
+ /* Filter-mode toggle (full-text search) — the cluster's right end. Both comboboxes render it
970
+ (ComboboxPill's modeToggle). */
959
971
  .bulb-filter-control.has-trailing .bulb-filter { padding-right: 3.8rem; }
960
- .bulb-filter-control.has-trailing .bulb-filter-clear { right: 2.5rem; }
961
972
  .bulb-filter-mode {
962
- /* Stretches flush to the input's top/right/bottom, inside its 1px border; the right corners
963
- match the input's 6px radius so the latched fill reads as part of the box. */
964
- position: absolute; right: 1px; top: 1px; bottom: 1px;
973
+ /* The right corners match the input's 6px radius so the latched fill reads as part of the box. */
965
974
  appearance: none; border: none; background: transparent; cursor: pointer;
966
975
  font-size: .8rem; line-height: 1; padding: 0 .45rem;
967
976
  border-radius: 0 5px 5px 0;
@@ -1164,10 +1173,13 @@ a.server-port:hover { text-decoration: underline; }
1164
1173
  .gitdiff-pill .icon { width: 8px; height: 12px; }
1165
1174
  .gitdiff-count { font-size: var(--pill-font); font-variant-numeric: tabular-nums; }
1166
1175
  .gitdiff-pop { width: min(480px, calc(100vw - 2rem)); }
1167
- .gitdiff-list { display: flex; flex-direction: column; gap: .15rem; flex: 1; min-height: 0; overflow-y: scroll; padding-right: 4px; }
1168
- .gitdiff-row { display: flex; align-items: center; gap: .55rem; flex: none; min-height: var(--menu-row-h); padding: .05rem .45rem; border-radius: 6px; font-size: .85rem; cursor: pointer; }
1169
- .gitdiff-row:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
1170
- .gitdiff-row.active { background: color-mix(in srgb, var(--accent) 16%, transparent); }
1176
+ /* The single-line menu-row geometry both swap-pills use (the session picker's rows are a column —
1177
+ they grow for a search snippet — so they keep their own). Merged selectors, the convention the
1178
+ .*-wrap rules above already follow: a third pill adds its name here rather than a fourth copy. */
1179
+ .gitdiff-list, .children-list { display: flex; flex-direction: column; gap: .15rem; flex: 1; min-height: 0; overflow-y: scroll; padding-right: 4px; }
1180
+ .gitdiff-row, .children-row { display: flex; align-items: center; gap: .55rem; flex: none; min-height: var(--menu-row-h); padding: .05rem .45rem; border-radius: 6px; font-size: .85rem; cursor: pointer; }
1181
+ .gitdiff-row:hover, .children-row:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
1182
+ .gitdiff-row.active, .children-row.active { background: color-mix(in srgb, var(--accent) 16%, transparent); }
1171
1183
  .gitdiff-row.viewing .gitdiff-path { color: var(--accent); }
1172
1184
  /* One status letter, VS Code's palette: modified/renamed amber, added/untracked green, deleted red. */
1173
1185
  .gitdiff-status { flex: none; width: 1rem; text-align: center; font: 600 .74rem ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: var(--muted); }
@@ -1189,13 +1201,53 @@ a.server-port:hover { text-decoration: underline; }
1189
1201
  .gitdiff-pill.viewing :is(.gitdiff-status, .gitdiff-doc-path, .gitdiff-counts span) { text-box: trim-both cap alphabetic; }
1190
1202
  .gitdiff-doc-path { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; direction: rtl; text-align: left; font-size: var(--pill-font); color: var(--accent); }
1191
1203
  .gitdiff-doc-path:hover { text-decoration: underline; }
1192
- .gitdiff-close { flex: none; display: inline-flex; padding: 0 .15rem; color: var(--muted); }
1193
- .gitdiff-close .icon { width: 10px; height: 10px; }
1194
- .gitdiff-close:hover { color: var(--accent); }
1204
+ /* The × on a pill holding the transcript's slot (ui.ts closeChip) — one rule, both swap-pills. */
1205
+ .pill-close { flex: none; display: inline-flex; padding: 0 .15rem; color: var(--muted); }
1206
+ .pill-close .icon { width: 10px; height: 10px; }
1207
+ .pill-close:hover { color: var(--accent); }
1195
1208
  /* Doc open ⇒ transcript-scoped pills hide: model switcher and session picker act on a conversation
1196
1209
  that is not on screen (the token pill stays — its shimmer says the live diff may still grow).
1197
1210
  Display only, never unmount — the model pill's superSelect must keep its mount. */
1198
- .statusbar-actions.doc-open :is(.model-wrap, .sid-wrap) { display: none; }
1211
+ .statusbar-actions.doc-open :is(.model-wrap, .sid-wrap, .children-wrap) { display: none; }
1212
+
1213
+ /* ── the agents pill (TB-Agent-Children.md): this session's child transcripts. The git-diff pill's
1214
+ shape throughout — presence is the signal (a session that spawned nothing has no pill), picking a
1215
+ row swaps the transcript for that child's conversation, and the pill then wears its identity so
1216
+ the child view carries no chrome of its own. ── */
1217
+ /* Only the VIEWING form is a glyph pill (it needs the fixed square's width freed); the chip is an
1218
+ ordinary word pill, `🤖${n}🤖`, so it takes .pill's centering recipe untouched. */
1219
+ .pill.glyph.children-pill { width: auto; padding: 0 .45rem; gap: .3rem; }
1220
+ /* The chip is a word pill, so it sits outside `.pill.glyph` twice over: its emoji keep their colour
1221
+ at rest (they read as the agents themselves, not as chrome), and they take none of the glyph
1222
+ padding. `.pill`'s .7rem is measured for letters; an emoji's advance already carries its own side
1223
+ bearings, so that padding lands outside whitespace the glyph brought with it and reads as a gap.
1224
+ The margin then goes on each glyph's INNER side only: the padding already owns the outer gap, so a
1225
+ symmetric one doubles up exactly where it shows. Chip only, since the viewing form is a flex row
1226
+ with a gap of its own. */
1227
+ .children-pill { padding: 0 .3rem; }
1228
+ .children-pill:not(.glyph) .glyph-img:first-child { margin-right: .12rem; }
1229
+ .children-pill:not(.glyph) .glyph-img:last-child { margin-left: .12rem; }
1230
+ .children-pop { width: min(520px, calc(100vw - 2rem)); }
1231
+ .children-row.viewing .children-label { color: var(--accent); }
1232
+ /* One dot carries the state: still working (the fold-marker green), finished (dim), killed by the
1233
+ user (warn). Green because running is the LIVE-APP axis, the same one the launcher's running
1234
+ :port and play glyph sit on; the blue accent is the source/name axis and would read as "selected"
1235
+ here, which the row already says by other means. */
1236
+ .children-dot { flex: none; align-self: center; width: 7px; height: 7px; border-radius: 50%; background: var(--muted); opacity: .4; }
1237
+ .children-dot.running { background: var(--fold-marker); opacity: 1; }
1238
+ .children-dot.stopped { background: var(--warn); opacity: .85; }
1239
+ /* The agent's type, capped so the common long one (general-purpose) can't crowd out the
1240
+ description — which is the only thing telling eleven siblings apart. Muted by colour, not size. */
1241
+ .children-kind { flex: none; max-width: 7rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); }
1242
+ .children-label { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1243
+ .children-model { flex: none; color: var(--muted); opacity: .8; }
1244
+ .children-time { flex: none; color: var(--muted); font-size: .72rem; min-width: 2.6rem; text-align: right; }
1245
+ /* The viewing form: the open child's identity in place of the icon+count, with the × that returns.
1246
+ Same seating rule as the diff pill's — .pill.glyph opts out of the text-box trim, so each TEXT
1247
+ flex item takes it back or the words sit low against the icon. */
1248
+ .pill.glyph.children-pill.viewing { max-width: min(18rem, 35vw); gap: .45rem; padding: 0 .35rem 0 .5rem; }
1249
+ .children-pill.viewing .children-doc-label { text-box: trim-both cap alphabetic; }
1250
+ .children-doc-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--pill-font); color: var(--accent); }
1199
1251
 
1200
1252
  /* The diff doc — takes the transcript's slot in .chat (same flex geometry as .messages). Full-bleed
1201
1253
  like a code surface (no --content-max column: diff lines want the width); the body is the scroll
@@ -1303,6 +1355,27 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
1303
1355
  border-radius: 12px;
1304
1356
  padding: .7rem .9rem;
1305
1357
  }
1358
+ /* A user bubble is a panel, so its hover copy pill clears the border instead of sitting on it.
1359
+ .5rem matches the controls' own `bottom`, so the pill stands equally clear on both axes.
1360
+ Inherited by the agent variant below, and by any later bubble that grows a surface. */
1361
+ .bubble.user { --pill-inset: .5rem; }
1362
+ /* A sub-agent's hand-back (TB-Agent-Children.md): a user turn the harness delivered on an agent's
1363
+ behalf, never one the user typed, so it wears its own outline instead of the plain send's. The
1364
+ accent is the source/name axis (the same one the agents pill's open-child label sits on); green
1365
+ belongs to the live axis and would read as "still running", which a report never is. */
1366
+ .bubble.user.agent {
1367
+ border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
1368
+ background: color-mix(in srgb, var(--accent) 7%, transparent);
1369
+ }
1370
+ /* The frame's header: the agents pill's glyph, the agent's type, then its description, which is the
1371
+ link into its transcript. Muted by colour and NOT by size — the description is the one thing that
1372
+ tells eleven siblings apart, so it stays at the body's size (the picker's child-row rule). */
1373
+ .agent-head { display: flex; align-items: baseline; gap: .4rem; margin-bottom: .55rem; }
1374
+ .agent-head .glyph-img { flex: none; align-self: center; }
1375
+ .agent-kind { flex: none; color: var(--muted); }
1376
+ .agent-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1377
+ a.agent-name { color: var(--accent); cursor: pointer; }
1378
+ a.agent-name:hover { text-decoration: underline; }
1306
1379
  /* Consecutive tool-only bubbles sit tight via negative margin (flex `gap`
1307
1380
  can't be overridden per sibling pair). The adjacent-sibling selector leaves
1308
1381
  the first tools-only bubble's gap intact, preserving the turn boundary. */
@@ -1435,8 +1508,26 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
1435
1508
  inline bulb and a spread bulb). `100%` in --lane-ml resolves against each user's own containing block
1436
1509
  (prose width) at point of use, so both centre on the column axis — which is the viewport centre,
1437
1510
  since the gutters are symmetric. One home so the geometry can't drift between the two (the bug
1438
- that let an off-centre nudge survive in one rule after being removed from the other). */
1439
- .md { --lane-w: calc(100vw - 2rem); --lane-ml: calc((100% - var(--lane-w)) / 2); }
1511
+ that let an off-centre nudge survive in one rule after being removed from the other).
1512
+ --chop-inset (the stripe-chop's reach into the gutter) and --chop-bg (the fill that covers the
1513
+ stripe) live here too: all four describe the transcript column, so all four are the container's
1514
+ to declare rather than the artifact's. */
1515
+ .md {
1516
+ --lane-w: calc(100vw - 2rem); --lane-ml: calc((100% - var(--lane-w)) / 2);
1517
+ --chop-inset: 1rem; --chop-bg: var(--bg);
1518
+ }
1519
+
1520
+ /* A padded panel that holds rendered markdown redeclares all four as its own box: a sub-agent
1521
+ hand-back frame (TB-Agent-Children.md), a disclosure, a blockquote. Inside one there is no turn
1522
+ stripe to reach for or to cover, so the reach and the fill go to zero and an embed keeps only its
1523
+ vertical band, while the lane collapses to the panel: a wide table then shares the panel's width
1524
+ through its two ceilings and scrolls in .table-scroll, instead of a band that bleeds over the
1525
+ card's border and paints page colour across its tint. The reset must land on the `.md` element
1526
+ itself, since `.md` declares the defaults on itself and would beat a rule on an ancestor. The
1527
+ next panel is one more selector here. */
1528
+ .bubble.user > .md, .md details, .md blockquote {
1529
+ --lane-w: 100%; --lane-ml: 0px; --chop-inset: 0px; --chop-bg: transparent;
1530
+ }
1440
1531
 
1441
1532
  /* Full-lane breakout — the mermaid embed (whole element) and a SPREAD bulb's iframe step out of
1442
1533
  the prose column to the transcript width so wide content gets room. Both use --lane-w/--lane-ml
@@ -1468,7 +1559,7 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
1468
1559
  overflow-x: auto;
1469
1560
  display: flex;
1470
1561
  justify-content: safe center;
1471
- box-shadow: 0 0 0 1rem var(--bg);
1562
+ box-shadow: 0 0 0 1rem var(--chop-bg);
1472
1563
  }
1473
1564
  /* flex-shrink:0: otherwise the SVG shrinks to the flex container and its viewBox
1474
1565
  scales it down — no overflow, no scrollbar. Natural width is what scrolls. */
@@ -1480,17 +1571,20 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
1480
1571
  .md .svg-embed { justify-content: center; }
1481
1572
  .md .svg-embed svg { max-width: 100%; height: auto; }
1482
1573
 
1483
- /* Markdown tables: chop in-column (the `.embed` rule below) and `safe center` — a table that fits its
1484
- box centres, and one that overflows falls back to left so it scrolls from its first column rather than
1485
- clipping. Centring holds in both modes: a small in-column table sits centred in the prose column, a
1486
- spread one centred in the lane. The scroll lives on the inner `.table-scroll`, not the `.embed`
1574
+ /* Markdown tables: chop in-column (the `.embed` rule below). An IN-COLUMN table starts where the prose
1575
+ starts — it is part of the paragraph flow, and a narrow one centred in the column reads as shifted
1576
+ right against the left-aligned text above and below it, which is the whole of that bug. Only a
1577
+ SPREAD table centres, and it has earned it: spreading means it is already wider than the column, so
1578
+ centring keeps it symmetric about the column axis exactly as the mermaid lane does. `safe` on both
1579
+ means one that overflows its box falls back to the start and scrolls from its first column rather
1580
+ than clipping. The scroll lives on the inner `.table-scroll`, not the `.embed`
1487
1581
  wrapper, so a horizontal scrollbar rides above the chop's bottom padding instead of flush against the
1488
1582
  next line (a scrollbar sits at its own container's bottom edge). It's the centred flex child, capped at
1489
1583
  the box so it shrinks-to-fit and scrolls only what overflows; `max-width:100%` + the overflow-induced
1490
1584
  0 min-width let it shrink below the table's natural width. `spread` widens the wrapper to the lane (the
1491
1585
  mermaid geometry) for a table fitTableEmbeds found too wide for the column. The opaque `.embed`
1492
1586
  padding/z-index still occludes the turn stripe the spread crosses — no box-shadow needed. */
1493
- .md .table-embed { justify-content: safe center; }
1587
+ .md .table-embed { justify-content: safe flex-start; }
1494
1588
  .md .table-embed .table-scroll { overflow-x: auto; max-width: 100%; }
1495
1589
  /* max-content keeps the table at its natural (unwrapped) width so it overflows `.table-scroll` and earns
1496
1590
  a scrollbar, rather than wrapping its cells to fit — the bar is the point, and it now rides inside the
@@ -1511,15 +1605,24 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
1511
1605
  shattering text; a pathological no-space token (a long URL) then scrolls rather than breaking. */
1512
1606
  .md .table-embed table { margin: 0; width: max-content; max-width: var(--lane-w); }
1513
1607
  .md .table-embed th, .md .table-embed td { max-width: 40ch; word-break: normal; }
1608
+ /* The spread band's comfort goes on the RIGHT only, which looks asymmetric written down and is what
1609
+ reads as even on screen. `.messages` reserves a scrollbar gutter at BOTH edges (to hold the column
1610
+ on the window axis), but only the right one ever contains a scrollbar: the left gutter is dead
1611
+ space that already reads as margin, while on the right the lane's own 1rem inset is all there is
1612
+ between the table and the bar. Inset both sides equally and the left gap measures a gutter wider
1613
+ than the right; inset neither and the table sits flush against the bar, which is the bug. So the
1614
+ band adds back one inset on the right, and the table's cap comes down by that same inset or it
1615
+ would overflow the band's content box by exactly that at every width — the permanent scrollbar the
1616
+ zeroed inset was avoiding. */
1514
1617
  .md .table-embed.spread {
1618
+ justify-content: safe center;
1515
1619
  width: var(--lane-w);
1516
1620
  margin-left: var(--lane-ml);
1517
1621
  margin-right: auto;
1518
- /* Zero the `.embed` inset: spread makes the border box the lane, so a 1rem left padding would
1519
- leave the scroller 1rem narrower than the table's own lane cap — a capped table then overflows
1520
- by exactly that inset at every window width, which is the permanent 16px-of-travel scrollbar. */
1521
1622
  padding-left: 0;
1623
+ padding-right: var(--chop-inset);
1522
1624
  }
1625
+ .md .table-embed.spread table { max-width: calc(var(--lane-w) - var(--chop-inset)); }
1523
1626
 
1524
1627
  /* ````bulb```` inline bulbs: a sandboxed nested app. createBulbFrame owns the iframe (auto-height,
1525
1628
  borderless). No frame of our own — like the mermaid/svg embeds, it sits in the flow rather
@@ -1531,9 +1634,10 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
1531
1634
  under a live inline bulb (both monospace, muted red). */
1532
1635
  /* The stripe-chop, shared by every opaque in-column visual artifact (the fit bulb and a raw
1533
1636
  `svg` — each opts in by carrying `.embed`). An opaque bg over position:relative + z-index:0
1534
- paints above .bubble::before; a -1rem left extension reaches the stripe in the gutter without a
1535
- full breakout (padding-left:1rem keeps the content in the prose column, and the box's right edge
1536
- stays at prose-right, so a bulb's controls anchored there don't move). The vertical gap is opaque
1637
+ paints above .bubble::before; a --chop-inset left extension reaches the stripe in the gutter
1638
+ without a full breakout (the matching padding-left keeps the content in the prose column, and the
1639
+ box's right edge stays at prose-right, so a bulb's controls anchored there don't move); inside a
1640
+ panel both zero out (the nesting reset above). The vertical gap is opaque
1537
1641
  padding, not margin: it occludes the stripe top and bottom so the cut is flush with the prose (no
1538
1642
  stub in a transparent gap), symmetric above/below to separate the artifact from surrounding text.
1539
1643
  margin top/bottom are zeroed — and the abutting prose margins too (below) — so the gap butts the
@@ -1543,9 +1647,9 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
1543
1647
  display: flex;
1544
1648
  position: relative;
1545
1649
  z-index: 0;
1546
- background: var(--bg);
1547
- margin: 0 0 0 -1rem;
1548
- padding: 1.1rem 0 1.1rem 1rem;
1650
+ background: var(--chop-bg);
1651
+ margin: 0 0 0 calc(-1 * var(--chop-inset));
1652
+ padding: 1.1rem 0 1.1rem var(--chop-inset);
1549
1653
  }
1550
1654
  /* A bulb stacks its controls/frame/code vertically; in spread mode the inner iframe's own
1551
1655
  breakout covers the stripe too (the wrapper still chops in column). */
@@ -1595,7 +1699,12 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
1595
1699
  .overlay-pill {
1596
1700
  position: absolute;
1597
1701
  bottom: .5rem;
1598
- right: 0; /* flush with the content column's right edge (prose / statusbar pills) */
1702
+ /* Flush with the content column's right edge (prose / statusbar pills) when the pill sits on the
1703
+ page's own background. A PANEL sets --pill-inset instead, so the pill clears its edge rather
1704
+ than landing on the border: `right: 0` resolves against the containing block's padding box, so
1705
+ inside a bordered box it means "touching the border". Only this axis needed it — `bottom` has
1706
+ always stood the pill off, which is why the defect read as a right-edge one. */
1707
+ right: var(--pill-inset, 0px);
1599
1708
  font: inherit;
1600
1709
  font-size: var(--pill-font);
1601
1710
  padding: 0 .45rem;
@@ -1614,6 +1723,10 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
1614
1723
  the chop/breakout; the code-block wrapper needs it for the absolute pill. The pill is positioned
1615
1724
  (absolute), so it paints over the static fence content regardless of DOM order. */
1616
1725
  .md .copyable { position: relative; }
1726
+ /* A code fence IS a panel (the <pre> carries the border and background, the wrapper is the
1727
+ positioning context), so its copy pill takes the inset. The svg and mermaid embeds are
1728
+ transparent bands on the page background and keep the flush edge. */
1729
+ .md .code-block { --pill-inset: .5rem; }
1617
1730
  .md .copyable:hover .copy-src,
1618
1731
  .md .copy-src:focus-visible { opacity: 1; }
1619
1732
  .md .copy-src.done { color: var(--accent); border-color: var(--accent); }
@@ -1974,7 +2087,7 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
1974
2087
  .bubble-controls {
1975
2088
  position: absolute;
1976
2089
  bottom: .5rem;
1977
- right: 0; /* flush with the content column's right edge (prose / statusbar pills) */
2090
+ right: var(--pill-inset, 0px);
1978
2091
  display: flex;
1979
2092
  gap: .4rem;
1980
2093
  }