@obvi/blueprint 1.1.2 → 1.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.
@@ -184,6 +184,37 @@
184
184
  var(--bp-ink-faint) 0 var(--bp-stroke),
185
185
  oklch(0 0 0 / 0) var(--bp-stroke) var(--bp-hatch-gap)
186
186
  );
187
+ /* Drafting board — a faint dot-grid backdrop that marks INTERACTIVE
188
+ component surfaces (choice, preflight, choice-record, source, mock,
189
+ gallery): a second axis distinct from the diagonal --bp-hatch (which
190
+ means fenced / held / rejected). Dots read as an open working surface,
191
+ so the two textures never compete. --bp-board-gap is the grid pitch;
192
+ --bp-board the reusable image. The dot color rides on --bp-ink-faint;
193
+ light uses a stronger mix (55%) than dark (35%) — see the dark block. */
194
+ --bp-board-gap: 18px;
195
+ --bp-board-dot: color-mix(in oklch, var(--bp-ink-faint) 55%, oklch(0 0 0 / 0));
196
+ --bp-board: radial-gradient(
197
+ circle at center,
198
+ var(--bp-board-dot) 0 0.85px,
199
+ oklch(0 0 0 / 0) 0.85px
200
+ );
201
+ /* The board rides on a faintly tinted surface — one elevation step between
202
+ the desk (--bp-bg) and the sheet (--bp-paper) — so a dotted interactive
203
+ section reads as its own panel and the paper cards inside it lift off the
204
+ mat. Derived from the two surface tokens, so it tracks either theme (and
205
+ any host override of paper / bg) from this single definition. */
206
+ --bp-board-bg: color-mix(in oklch, var(--bp-paper) 60%, var(--bp-bg));
207
+ /* Footer strips on the mat (hint + reset): vertical breathing room and a
208
+ horizontal inset that lines up with the component content above.
209
+ --bp-board-footer-pad-x defaults to preflight question padding; choice
210
+ layouts reassign it to match .bp-choice__card inset. */
211
+ --bp-board-footer-pad-y: var(--bp-space-2);
212
+ --bp-board-footer-pad-x: var(--bp-space-4);
213
+ /* Single-surface interactive roots on the mat (tabs choice, preflight):
214
+ uniform inset + Obvious container radius so the paper card reads as a
215
+ decision surface on the dotted board. */
216
+ --bp-board-card-pad: var(--bp-space-4);
217
+ --bp-board-card-radius: var(--bp-radius-8);
187
218
  /* Modal scrim wash — darker than --bp-ink-soft so floated panels (search
188
219
  palette, lightbox, expanded mock) read clearly over the page. Always
189
220
  pair with --bp-hatch:
@@ -224,6 +255,7 @@
224
255
  --bp-text-body: 14px; --bp-lh-body: 20px; /* body copy */
225
256
  --bp-text-small: 13px; --bp-lh-small: 18px; /* small / captions */
226
257
  --bp-text-code: 12.5px; --bp-lh-code: 1.6; /* mono, harmonized to body */
258
+ --bp-lh-control: 1.3; /* tighter than reading text — sans control labels */
227
259
 
228
260
  /* 3-step monospace-label scale, ONE letter-spacing each */
229
261
  --bp-label-sm: 9px; --bp-label-sm-ls: 0.08em;
@@ -257,14 +289,45 @@
257
289
  /* Corner radius — an even 2px-step scale; each token names its pixel
258
290
  value directly (corners read as absolute, so px is the honest unit).
259
291
  -6 is the document default for sheets, panels, and chrome; -2 dresses
260
- inline marks, -4 small code blocks, -8 is the soft top rung. */
292
+ inline marks, -4 small code blocks, -8 is decision containers, -12 is
293
+ the Obvious shell panel rung (dropdowns), -16 is the search command menu. */
261
294
  --bp-radius-0: 0;
262
295
  --bp-radius-2: 2px;
263
296
  --bp-radius-4: 4px;
264
297
  --bp-radius-6: 6px;
265
298
  --bp-radius-8: 8px;
299
+ --bp-radius-12: 12px;
300
+ --bp-radius-16: 16px;
266
301
  --bp-radius-pill: 9999px;
267
302
  --bp-radius-round: 50%;
303
+ /* Runtime shell overlays (doc switcher, contents dropdown, search palette). */
304
+ --bp-shell-panel-radius: var(--bp-radius-12);
305
+
306
+ /* Obvious button palette — the interactive-control vocabulary. Buttons are
307
+ the one chrome surface that carries fill: primary (committed / CTA),
308
+ secondary (resting control), tertiary (quiet track / ghost fill), ghost
309
+ (text-only). Fills stay on the neutral scale (no hue) so figures keep the
310
+ blue monopoly. Each value consumes the live Obvious app variable
311
+ (--button-*) when a document is embedded in the dashboard, and otherwise
312
+ falls back to the OKLCH value from the app's colors.css. */
313
+ --bp-button-primary-bg: var(--button-primary-bg, oklch(0.1344 0 0));
314
+ --bp-button-primary-bg-hover: var(--button-primary-bg-hover, oklch(0.3012 0 0));
315
+ --bp-button-primary-bg-press: var(--button-primary-bg-press, oklch(0.36 0 0));
316
+ --bp-button-primary-fg: var(--button-primary-fg, oklch(1 0 0));
317
+ --bp-button-secondary-bg: var(--button-secondary-bg, oklch(1 0 0));
318
+ --bp-button-secondary-bg-hover: var(--button-secondary-bg-hover, oklch(0.9702 0 0));
319
+ --bp-button-secondary-bg-press: var(--button-secondary-bg-press, oklch(0.9461 0 0));
320
+ --bp-button-secondary-fg: var(--button-secondary-fg, oklch(0 0 0 / 0.8));
321
+ --bp-button-secondary-bd: var(--button-secondary-bd, oklch(0 0 0 / 0.08));
322
+ --bp-button-tertiary-bg: var(--button-tertiary-bg, oklch(0 0 0 / 0.04));
323
+ --bp-button-tertiary-bg-hover: var(--button-tertiary-bg-hover, oklch(0 0 0 / 0.08));
324
+ --bp-button-tertiary-fg: var(--button-tertiary-fg, oklch(0 0 0 / 0.64));
325
+ --bp-button-ghost-fg: var(--button-ghost-fg, oklch(0 0 0 / 0.4));
326
+ --bp-button-ghost-fg-hover: var(--button-ghost-fg-hover, oklch(0 0 0 / 0.64));
327
+ --bp-button-ghost-bg-hover: var(--button-ghost-bg-hover, oklch(0 0 0 / 0.04));
328
+ /* Lifted-border ring carried by a raised (selected) secondary control. */
329
+ --bp-button-lift: 0 0 2px 0 oklch(0 0 0 / 0.05),
330
+ 0 0 1px 0 oklch(0 0 0 / 0.05), 0 0 0 1px oklch(0 0 0 / 0.08);
268
331
 
269
332
  /* Section-eyebrow marker — the square that leads the "01 / 02" counter.
270
333
  Size is the block edge; gap is the space between it and the number. */
@@ -354,6 +417,7 @@
354
417
  --bp-ink: var(--obvious-text-primary, oklch(1 0 0 / 0.92));
355
418
  --bp-ink-soft: oklch(1 0 0 / 0.4);
356
419
  --bp-ink-faint: oklch(1 0 0 / 0.16);
420
+ --bp-board-dot: color-mix(in oklch, var(--bp-ink-faint) 35%, oklch(0 0 0 / 0));
357
421
  --bp-highlight: oklch(1 0 0 / 0.14);
358
422
 
359
423
  /* Illustration accent (dark) — brighter blue holds on dark paper. */
@@ -384,6 +448,25 @@
384
448
  --bp-text-secondary: oklch(1 0 0 / 0.64);
385
449
  --bp-positive: oklch(0.74 0.15 150);
386
450
  --bp-positive-faint: oklch(0.34 0.07 150);
451
+
452
+ /* Obvious button palette (dark) — see the light block for the contract. */
453
+ --bp-button-primary-bg: var(--button-primary-bg, oklch(0.4091 0 0));
454
+ --bp-button-primary-bg-hover: var(--button-primary-bg-hover, oklch(0.4819 0 0));
455
+ --bp-button-primary-bg-press: var(--button-primary-bg-press, oklch(0.5795 0 0));
456
+ --bp-button-primary-fg: var(--button-primary-fg, oklch(1 0 0 / 0.92));
457
+ --bp-button-secondary-bg: var(--button-secondary-bg, oklch(0.3012 0 0));
458
+ --bp-button-secondary-bg-hover: var(--button-secondary-bg-hover, oklch(0.3407 0 0));
459
+ --bp-button-secondary-bg-press: var(--button-secondary-bg-press, oklch(0.4091 0 0));
460
+ --bp-button-secondary-fg: var(--button-secondary-fg, oklch(1 0 0 / 0.8));
461
+ --bp-button-secondary-bd: var(--button-secondary-bd, oklch(1 0 0 / 0.04));
462
+ --bp-button-tertiary-bg: var(--button-tertiary-bg, oklch(1 0 0 / 0.04));
463
+ --bp-button-tertiary-bg-hover: var(--button-tertiary-bg-hover, oklch(1 0 0 / 0.08));
464
+ --bp-button-tertiary-fg: var(--button-tertiary-fg, oklch(1 0 0 / 0.64));
465
+ --bp-button-ghost-fg: var(--button-ghost-fg, oklch(1 0 0 / 0.4));
466
+ --bp-button-ghost-fg-hover: var(--button-ghost-fg-hover, oklch(1 0 0 / 0.64));
467
+ --bp-button-ghost-bg-hover: var(--button-ghost-bg-hover, oklch(1 0 0 / 0.04));
468
+ --bp-button-lift: 0 0 2px 0 oklch(1 0 0 / 0.05),
469
+ 0 0 1px 0 oklch(1 0 0 / 0.05), 0 0 0 1px oklch(1 0 0 / 0.08);
387
470
  }
388
471
  }
389
472
 
@@ -433,6 +516,8 @@
433
516
  /* ---- Tier 1a: page + body ---------------------------------------- */
434
517
  :where(html) {
435
518
  font-size: 16px;
519
+ background: var(--bp-bg);
520
+ overscroll-behavior: none;
436
521
  }
437
522
  :where(body) {
438
523
  font-family: var(--bp-serif);
@@ -501,19 +586,11 @@
501
586
  margin-bottom: var(--bp-space-2);
502
587
  text-wrap: var(--bp-wrap-heading);
503
588
  }
504
- /* Document masthead — the first <header> in <main>. No implicit, position-
505
- based styling: author the eyebrow with <bp-eyebrow> and the lede with
506
- <bp-subheader> explicitly. We only reset the title's top margin and tune
507
- the gap before a following contents index. */
508
- :where(main > header:first-child) {
509
- margin-bottom: 0;
510
- }
589
+ /* Document masthead — the first <header> in <main>. Its title starts flush,
590
+ and a following contents index keeps the masthead's own bottom gap. */
511
591
  :where(main > header:first-child > h1) {
512
592
  margin: 0 0 var(--bp-space-2);
513
593
  }
514
- :where(main > header:first-child:has(+ :is(.bp-contents, bp-toc))) {
515
- margin-bottom: var(--bp-space-4);
516
- }
517
594
  :where(main > header:first-child + :is(.bp-contents, bp-toc)) {
518
595
  margin-top: 0;
519
596
  }
@@ -945,6 +1022,14 @@
945
1022
  margin-bottom: var(--bp-space-5);
946
1023
  border-bottom: 0;
947
1024
  }
1025
+ /* The first main header is the canonical hero band; generic body/article
1026
+ headers remain compact metadata landmarks. */
1027
+ :where(main > header:first-child) {
1028
+ padding-top: var(--bp-space-6);
1029
+ padding-bottom: var(--bp-space-4);
1030
+ margin-bottom: var(--bp-space-5);
1031
+ border-bottom: 1px solid var(--bp-ink-line);
1032
+ }
948
1033
  /* Subordinate sources / status region */
949
1034
  :where(body > footer, main > footer, article > footer) {
950
1035
  margin-top: var(--bp-space-7);
@@ -1127,6 +1212,10 @@
1127
1212
  line-height: var(--bp-lh-lede);
1128
1213
  color: var(--bp-text);
1129
1214
  }
1215
+ :where(main > header:first-child > bp-subheader) {
1216
+ text-align: justify;
1217
+ hyphens: auto;
1218
+ }
1130
1219
 
1131
1220
  /* ---- Section heading permalink + eyebrow --------------------------------
1132
1221
  Injected by blueprint.js on headings with data-sidebar: a numbered eyebrow
@@ -1275,6 +1364,12 @@
1275
1364
  :where(bp-eyebrow[muted]) {
1276
1365
  color: var(--bp-text-secondary);
1277
1366
  }
1367
+ :where(main > header:first-child > bp-eyebrow) {
1368
+ font-size: var(--bp-label-lg);
1369
+ letter-spacing: var(--bp-label-lg-ls);
1370
+ color: var(--bp-ink-soft);
1371
+ margin-bottom: var(--bp-space-3);
1372
+ }
1278
1373
  :where(.bp-meta) {
1279
1374
  font-family: var(--bp-mono);
1280
1375
  font-size: var(--bp-label-md);
@@ -1478,7 +1573,7 @@
1478
1573
  padding: var(--bp-space-2) var(--bp-space-3);
1479
1574
  background: var(--bp-ink);
1480
1575
  color: var(--bp-paper);
1481
- border-radius: var(--bp-radius-0);
1576
+ border-radius: var(--bp-radius-6);
1482
1577
  margin-bottom: var(--bp-space-3);
1483
1578
  }
1484
1579
  :where(.bp-choice:has(.bp-choice__commit:checked)) .bp-choice__verdict,
@@ -1538,10 +1633,9 @@
1538
1633
  border: 0;
1539
1634
  padding: 0;
1540
1635
  cursor: pointer;
1541
- font-family: var(--bp-mono);
1542
- font-size: var(--bp-label-md);
1543
- letter-spacing: var(--bp-label-md-ls);
1544
- text-transform: uppercase;
1636
+ font-family: var(--bp-sans);
1637
+ font-size: var(--bp-text-small);
1638
+ line-height: var(--bp-lh-control);
1545
1639
  color: var(--bp-text-secondary);
1546
1640
  text-decoration: underline dotted;
1547
1641
  text-underline-offset: 3px;
@@ -1564,37 +1658,51 @@
1564
1658
  color: var(--bp-paper);
1565
1659
  background: var(--bp-ink);
1566
1660
  padding: 2px 8px;
1661
+ border-radius: var(--bp-radius-pill);
1567
1662
  }
1568
1663
  :where(.bp-choice__tag--out) {
1569
1664
  color: var(--bp-ink);
1570
1665
  border: 1px solid var(--bp-ink-faint);
1571
1666
  padding: 1px 7px;
1667
+ border-radius: var(--bp-radius-pill);
1572
1668
  }
1573
1669
 
1574
1670
  /* ---- layout: tabs (preview drafts, then adopt) ------------------- */
1671
+ :where(.bp-choice--tabs) {
1672
+ padding: var(--bp-board-card-pad);
1673
+ border: 1px solid var(--bp-ink-line);
1674
+ border-radius: var(--bp-board-card-radius);
1675
+ overflow: hidden;
1676
+ }
1575
1677
  :where(.bp-choice--tabs) :where(.bp-choice__seg) {
1576
1678
  display: flex;
1577
1679
  flex-wrap: wrap;
1578
- gap: 0;
1579
- border: 1px solid var(--bp-ink-line);
1580
- border-radius: var(--bp-radius-0);
1581
- overflow: hidden;
1680
+ gap: 2px;
1681
+ padding: 2px;
1682
+ border: 0;
1683
+ border-radius: var(--bp-radius-pill);
1684
+ background: var(--bp-button-tertiary-bg);
1685
+ overflow: visible;
1582
1686
  margin: 0 0 var(--bp-space-3);
1583
1687
  width: fit-content;
1584
1688
  }
1585
1689
  :where(.bp-choice__seg-opt) {
1586
1690
  position: relative;
1587
1691
  cursor: pointer;
1588
- font-family: var(--bp-mono);
1589
- font-size: var(--bp-label-md);
1590
- letter-spacing: var(--bp-label-md-ls);
1591
- text-transform: uppercase;
1592
- color: var(--bp-text-secondary);
1692
+ font-family: var(--bp-sans);
1693
+ font-size: var(--bp-text-small);
1694
+ line-height: var(--bp-lh-control);
1695
+ color: var(--bp-button-ghost-fg);
1593
1696
  padding: 6px 14px;
1594
- border-right: 1px solid var(--bp-ink-line);
1697
+ border-radius: var(--bp-radius-pill);
1698
+ transition: background var(--bp-duration-fast) var(--bp-ease-out),
1699
+ color var(--bp-duration-fast) var(--bp-ease-out),
1700
+ box-shadow var(--bp-duration-fast) var(--bp-ease-out),
1701
+ transform var(--bp-duration-fast) var(--bp-ease-out);
1595
1702
  }
1596
- :where(.bp-choice__seg-opt:last-child) {
1597
- border-right: 0;
1703
+ :where(.bp-choice__seg-opt:hover) {
1704
+ color: var(--bp-button-ghost-fg-hover);
1705
+ background: var(--bp-button-ghost-bg-hover);
1598
1706
  }
1599
1707
  :where(.bp-choice__seg-opt) :where(input) {
1600
1708
  position: absolute;
@@ -1602,8 +1710,9 @@
1602
1710
  pointer-events: none;
1603
1711
  }
1604
1712
  :where(.bp-choice__seg-opt:has(input:checked)) {
1605
- background: var(--bp-ink);
1606
- color: var(--bp-paper);
1713
+ background: var(--bp-button-secondary-bg);
1714
+ color: var(--bp-button-secondary-fg);
1715
+ box-shadow: var(--bp-button-lift);
1607
1716
  }
1608
1717
  :where(.bp-choice--tabs) :where(.bp-choice__panel) {
1609
1718
  display: none;
@@ -1616,18 +1725,20 @@
1616
1725
  align-items: center;
1617
1726
  gap: 8px;
1618
1727
  cursor: pointer;
1619
- border: 1px solid var(--bp-ink);
1620
- border-radius: var(--bp-radius-0);
1728
+ border: 1px solid var(--bp-button-secondary-bd);
1729
+ border-radius: var(--bp-radius-pill);
1621
1730
  padding: 6px 14px;
1622
- font-family: var(--bp-mono);
1623
- font-size: var(--bp-label-md);
1624
- letter-spacing: var(--bp-label-md-ls);
1625
- text-transform: uppercase;
1626
- color: var(--bp-ink);
1627
- background: var(--bp-paper);
1731
+ font-family: var(--bp-sans);
1732
+ font-size: var(--bp-text-small);
1733
+ line-height: var(--bp-lh-control);
1734
+ color: var(--bp-button-secondary-fg);
1735
+ background: var(--bp-button-secondary-bg);
1736
+ transition: background var(--bp-duration-fast) var(--bp-ease-out),
1737
+ color var(--bp-duration-fast) var(--bp-ease-out),
1738
+ transform var(--bp-duration-fast) var(--bp-ease-out);
1628
1739
  }
1629
1740
  :where(.bp-choice__adopt:hover) {
1630
- background: var(--bp-fill-hi);
1741
+ background: var(--bp-button-secondary-bg-hover);
1631
1742
  }
1632
1743
  :where(.bp-choice__adopt) :where(input) {
1633
1744
  position: absolute;
@@ -1638,13 +1749,14 @@
1638
1749
  border: 1px solid var(--bp-ink);
1639
1750
  background: var(--bp-fill-amb);
1640
1751
  padding: var(--bp-space-3);
1641
- margin-left: calc(-1 * var(--bp-space-3));
1642
- margin-right: calc(-1 * var(--bp-space-3));
1643
- border-radius: var(--bp-radius-0);
1752
+ margin-left: calc(-1 * var(--bp-board-card-pad));
1753
+ margin-right: calc(-1 * var(--bp-board-card-pad));
1754
+ border-radius: var(--bp-radius-6);
1644
1755
  }
1645
1756
  :where(.bp-choice__panel:has(.bp-choice__commit:checked)) :where(.bp-choice__adopt) {
1646
- background: var(--bp-ink);
1647
- color: var(--bp-paper);
1757
+ border-color: var(--bp-button-primary-bg);
1758
+ background: var(--bp-button-primary-bg);
1759
+ color: var(--bp-button-primary-fg);
1648
1760
  }
1649
1761
 
1650
1762
  /* ---- layout: stack (all cards visible) --------------------------- */
@@ -1656,7 +1768,7 @@
1656
1768
  display: block;
1657
1769
  position: relative;
1658
1770
  border: 1px solid var(--bp-edge);
1659
- border-radius: var(--bp-radius-0);
1771
+ border-radius: var(--bp-radius-6);
1660
1772
  padding: var(--bp-space-3);
1661
1773
  cursor: pointer;
1662
1774
  background: var(--bp-paper);
@@ -1744,7 +1856,7 @@
1744
1856
  position: relative;
1745
1857
  cursor: pointer;
1746
1858
  border: 1px solid var(--bp-edge);
1747
- border-radius: var(--bp-radius-0);
1859
+ border-radius: var(--bp-radius-6);
1748
1860
  overflow: hidden;
1749
1861
  background: var(--bp-paper);
1750
1862
  }
@@ -1799,18 +1911,33 @@
1799
1911
 
1800
1912
  :where(.bp-choice__compare) {
1801
1913
  margin-top: var(--bp-space-3);
1914
+ /* Override the base <details> inset (0 var(--bp-space-3) padding + a 2px
1915
+ left border) so the summary bar and disclosed body span the full card
1916
+ width — the summary and body carry their own padding. */
1917
+ padding: 0;
1802
1918
  border: 1px solid var(--bp-edge);
1803
- border-radius: var(--bp-radius-0);
1919
+ border-radius: var(--bp-radius-6);
1920
+ overflow: hidden;
1921
+ }
1922
+ :where(.bp-choice__compare[open]) {
1923
+ padding-bottom: 0;
1804
1924
  }
1805
1925
  :where(.bp-choice__compare > summary) {
1806
1926
  cursor: pointer;
1807
1927
  list-style: none;
1808
- padding: var(--bp-space-2) var(--bp-space-3);
1809
- font-family: var(--bp-mono);
1810
- font-size: var(--bp-label-md);
1811
- letter-spacing: var(--bp-label-md-ls);
1812
- text-transform: uppercase;
1813
- color: var(--bp-text-secondary);
1928
+ padding: var(--bp-space-3);
1929
+ font-family: var(--bp-sans);
1930
+ font-size: var(--bp-text-small);
1931
+ line-height: var(--bp-lh-control);
1932
+ color: var(--bp-button-ghost-fg);
1933
+ background: var(--bp-button-tertiary-bg);
1934
+ transition: background var(--bp-duration-fast) var(--bp-ease-out),
1935
+ color var(--bp-duration-fast) var(--bp-ease-out),
1936
+ transform var(--bp-duration-fast) var(--bp-ease-out);
1937
+ }
1938
+ :where(.bp-choice__compare > summary:hover) {
1939
+ background: var(--bp-button-tertiary-bg-hover);
1940
+ color: var(--bp-button-ghost-fg-hover);
1814
1941
  }
1815
1942
  :where(.bp-choice__compare > summary)::-webkit-details-marker {
1816
1943
  display: none;
@@ -1819,11 +1946,17 @@
1819
1946
  padding: var(--bp-space-3);
1820
1947
  border-top: 1px solid var(--bp-edge);
1821
1948
  }
1949
+ /* The slot content is usually a <dl>; drop its document margins so the
1950
+ disclosed body keeps one uniform inset (vertical matches horizontal). */
1951
+ :where(.bp-choice__compare-body) :where(dl) {
1952
+ margin: 0;
1953
+ gap: var(--bp-space-2) var(--bp-space-3);
1954
+ }
1822
1955
 
1823
1956
  /* ---- Pre-flight (decisions before drafting) ---------------------- */
1824
1957
  :where(.bp-preflight) {
1825
1958
  border: 1px solid var(--bp-ink-line);
1826
- border-radius: var(--bp-radius-0);
1959
+ border-radius: var(--bp-board-card-radius);
1827
1960
  overflow: hidden;
1828
1961
  margin: var(--bp-space-4) 0;
1829
1962
  }
@@ -1852,23 +1985,9 @@
1852
1985
  }
1853
1986
  :where(.bp-preflight__q) {
1854
1987
  counter-increment: bp-preflight-q;
1855
- padding: var(--bp-space-3) var(--bp-space-3) var(--bp-space-3) calc(var(--bp-space-6) + 4px);
1856
- position: relative;
1988
+ padding: var(--bp-space-4);
1857
1989
  border-bottom: 1px solid var(--bp-edge);
1858
1990
  }
1859
- :where(.bp-preflight__q::before) {
1860
- content: counter(bp-preflight-q, decimal-leading-zero);
1861
- position: absolute;
1862
- left: var(--bp-space-3);
1863
- top: var(--bp-space-3);
1864
- font-family: var(--bp-mono);
1865
- font-size: var(--bp-label-lg);
1866
- letter-spacing: 0.08em;
1867
- color: var(--bp-ink-soft);
1868
- }
1869
- :where(.bp-preflight__q:has(input:checked)::before) {
1870
- color: var(--bp-ink);
1871
- }
1872
1991
  :where(.bp-preflight__prompt) {
1873
1992
  font-family: var(--bp-sans);
1874
1993
  font-weight: var(--bp-weight-strong);
@@ -1878,6 +1997,16 @@
1878
1997
  align-items: baseline;
1879
1998
  gap: var(--bp-space-2);
1880
1999
  }
2000
+ :where(.bp-preflight__prompt::before) {
2001
+ content: counter(bp-preflight-q, decimal-leading-zero);
2002
+ font-family: var(--bp-mono);
2003
+ font-size: var(--bp-label-lg);
2004
+ letter-spacing: 0.08em;
2005
+ color: var(--bp-ink-soft);
2006
+ }
2007
+ :where(.bp-preflight__q:has(input:checked)) :where(.bp-preflight__prompt::before) {
2008
+ color: var(--bp-ink);
2009
+ }
1881
2010
  :where(.bp-preflight__kind) {
1882
2011
  font-family: var(--bp-mono);
1883
2012
  font-size: var(--bp-label-sm);
@@ -1887,11 +2016,11 @@
1887
2016
  font-weight: var(--bp-weight-body);
1888
2017
  }
1889
2018
  :where(.bp-preflight__resolved) {
1890
- display: none;
2019
+ visibility: hidden;
1891
2020
  margin-left: auto;
1892
2021
  }
1893
2022
  :where(.bp-preflight__q:has(input:checked)) :where(.bp-preflight__resolved) {
1894
- display: inline-flex;
2023
+ visibility: visible;
1895
2024
  }
1896
2025
  :where(.bp-preflight__chips) {
1897
2026
  display: flex;
@@ -1904,23 +2033,32 @@
1904
2033
  align-items: center;
1905
2034
  gap: 7px;
1906
2035
  cursor: pointer;
1907
- border: 1px solid var(--bp-ink-faint);
1908
- border-radius: var(--bp-radius-0);
2036
+ border: 1px solid var(--bp-button-secondary-bd);
2037
+ border-radius: var(--bp-radius-pill);
1909
2038
  padding: 5px 12px;
1910
2039
  font-size: var(--bp-text-small);
1911
- background: var(--bp-paper);
2040
+ color: var(--bp-button-secondary-fg);
2041
+ background: oklch(0 0 0 / 0);
2042
+ transition: background var(--bp-duration-fast) var(--bp-ease-out),
2043
+ color var(--bp-duration-fast) var(--bp-ease-out),
2044
+ border-color var(--bp-duration-fast) var(--bp-ease-out),
2045
+ transform var(--bp-duration-fast) var(--bp-ease-out);
1912
2046
  }
1913
2047
  :where(.bp-preflight__chip:hover) {
1914
- border-color: var(--bp-ink-line);
2048
+ border-color: var(--bp-button-secondary-bd);
2049
+ background: var(--bp-button-tertiary-bg-hover);
1915
2050
  }
1916
2051
  :where(.bp-preflight__chip) :where(input) {
1917
2052
  accent-color: var(--bp-ink);
1918
2053
  margin: 0;
1919
2054
  }
1920
2055
  :where(.bp-preflight__chip:has(input:checked)) {
1921
- background: var(--bp-ink);
1922
- color: var(--bp-paper);
1923
- border-color: var(--bp-ink);
2056
+ background: var(--bp-button-primary-bg);
2057
+ color: var(--bp-button-primary-fg);
2058
+ border-color: var(--bp-button-primary-bg);
2059
+ }
2060
+ :where(.bp-preflight__chip:has(input:checked)) :where(input) {
2061
+ accent-color: var(--bp-button-primary-fg);
1924
2062
  }
1925
2063
  :where(.bp-preflight__gate) {
1926
2064
  margin: var(--bp-space-3);
@@ -1949,17 +2087,37 @@
1949
2087
  display: inline-flex;
1950
2088
  align-items: center;
1951
2089
  gap: 8px;
1952
- border: 1px solid var(--bp-ink);
1953
- border-radius: var(--bp-radius-0);
2090
+ border: 1px solid var(--bp-button-primary-bg);
2091
+ border-radius: var(--bp-radius-pill);
1954
2092
  padding: 8px 16px;
1955
- font-family: var(--bp-mono);
1956
- font-size: var(--bp-label-md);
1957
- letter-spacing: var(--bp-label-md-ls);
1958
- text-transform: uppercase;
1959
- background: var(--bp-ink);
1960
- color: var(--bp-paper);
2093
+ font-family: var(--bp-sans);
2094
+ font-size: var(--bp-text-small);
2095
+ line-height: var(--bp-lh-control);
2096
+ background: var(--bp-button-primary-bg);
2097
+ color: var(--bp-button-primary-fg);
1961
2098
  cursor: pointer;
1962
2099
  margin-top: var(--bp-space-2);
2100
+ transition: background var(--bp-duration-fast) var(--bp-ease-out),
2101
+ border-color var(--bp-duration-fast) var(--bp-ease-out),
2102
+ transform var(--bp-duration-fast) var(--bp-ease-out);
2103
+ }
2104
+ :where(.bp-preflight__draft:hover) {
2105
+ background: var(--bp-button-primary-bg-hover);
2106
+ border-color: var(--bp-button-primary-bg-hover);
2107
+ }
2108
+
2109
+ /* Press feedback — the decision controls nudge 1px down on :active to match
2110
+ the Obvious app's button press. Motion only: the squared/pill split, the
2111
+ --bp-button-* fills, and the radius contract are all unchanged. Reduced
2112
+ motion is handled globally, so no per-rule query here. */
2113
+ :where(
2114
+ .bp-choice__seg-opt,
2115
+ .bp-choice__adopt,
2116
+ .bp-choice__compare > summary,
2117
+ .bp-preflight__chip,
2118
+ .bp-preflight__draft
2119
+ ):active {
2120
+ transform: translateY(1px);
1963
2121
  }
1964
2122
  :where(.bp-preflight__footer) {
1965
2123
  display: flex;
@@ -1972,17 +2130,45 @@
1972
2130
  :where(.bp-choice-record) {
1973
2131
  margin: var(--bp-space-4) 0;
1974
2132
  }
1975
- :where(.bp-choice-record__row) {
2133
+ /* One Obvious container card per row: the host carries border and paper
2134
+ fill; radius + overflow come from the [data-bp-rendered] mat rule above. */
2135
+ :where(bp-choice-record-row) {
2136
+ display: block;
1976
2137
  border: 1px solid var(--bp-edge);
1977
- border-radius: var(--bp-radius-0);
2138
+ background-color: var(--bp-paper);
2139
+ }
2140
+ :where(bp-choice-record-row + bp-choice-record-row) {
2141
+ margin-top: var(--bp-space-2);
2142
+ }
2143
+ :where(.bp-choice-record__row) {
2144
+ margin: 0;
2145
+ border: 0;
2146
+ border-radius: 0;
2147
+ background-color: oklch(0 0 0 / 0);
1978
2148
  padding: var(--bp-space-2) var(--bp-space-3);
1979
2149
  display: grid;
1980
2150
  grid-template-columns: minmax(8rem, 14rem) 1fr auto;
1981
2151
  gap: var(--bp-space-2) var(--bp-space-3);
1982
2152
  align-items: baseline;
1983
2153
  }
1984
- :where(.bp-choice-record__row + .bp-choice-record__row) {
1985
- margin-top: var(--bp-space-2);
2154
+ :where(bp-choice-record-row:has(.bp-choice__compare)) :where(.bp-choice-record__row) {
2155
+ border-bottom: 1px solid var(--bp-edge);
2156
+ }
2157
+ /* Compare sits flush in the card: drop the base compare top-margin and the
2158
+ standalone compare card chrome (border / radius) used in gallery layout.
2159
+ Wins over the base :where(.bp-choice__compare) rule by source order — no
2160
+ !important, since the record row no longer carries a JS inline margin. */
2161
+ :where(bp-choice-record-row) :where(.bp-choice__compare) {
2162
+ margin: 0;
2163
+ border: 0;
2164
+ border-radius: 0;
2165
+ padding: 0;
2166
+ }
2167
+ :where(bp-choice-record-row) :where(.bp-choice__compare > summary) {
2168
+ background-color: var(--bp-fill-amb);
2169
+ }
2170
+ :where(bp-choice-record-row) :where(.bp-choice__compare > summary:hover) {
2171
+ background-color: var(--bp-fill-hi);
1986
2172
  }
1987
2173
  :where(.bp-choice-record__row) :where(dt) {
1988
2174
  font-family: var(--bp-mono);
@@ -2485,6 +2671,9 @@
2485
2671
  border: 1px solid var(--bp-ink-line);
2486
2672
  margin-top: var(--bp-space-4);
2487
2673
  }
2674
+ :where(main > header:first-child > .bp-title-block) {
2675
+ margin-top: var(--bp-space-5);
2676
+ }
2488
2677
  :where(.bp-tb-cell) {
2489
2678
  flex: 0 0 auto;
2490
2679
  padding: var(--bp-space-2) var(--bp-space-3);
@@ -2626,7 +2815,7 @@
2626
2815
  }
2627
2816
  /* The contents list hosts a single rounded ACTIVE PILL that glides between
2628
2817
  entries as you scroll — no rail, no left border. Obvious-style. */
2629
- :where(.bp-sidebar > ul, .bp-sidebar__panel > ul, .bp-toc > ul) {
2818
+ :where(.bp-sidebar > ul, .bp-toc-switcher > ul, .bp-toc > ul) {
2630
2819
  position: relative;
2631
2820
  counter-reset: bp-toc;
2632
2821
  margin: 0;
@@ -2639,7 +2828,7 @@
2639
2828
  }
2640
2829
  /* The gliding active pill, drawn behind the entries and travelling via
2641
2830
  --bp-toc-y / --bp-toc-h (set by blueprint.js). */
2642
- :where(.bp-sidebar > ul, .bp-sidebar__panel > ul, .bp-toc > ul)::before {
2831
+ :where(.bp-sidebar > ul, .bp-toc-switcher > ul, .bp-toc > ul)::before {
2643
2832
  content: "";
2644
2833
  position: absolute;
2645
2834
  left: 0;
@@ -2657,7 +2846,7 @@
2657
2846
  transition-duration: var(--bp-duration-slow);
2658
2847
  transition-timing-function: var(--bp-ease-out);
2659
2848
  }
2660
- :where(.bp-sidebar > ul > li, .bp-sidebar__panel > ul > li, .bp-toc > ul > li) {
2849
+ :where(.bp-sidebar > ul > li, .bp-toc-switcher > ul > li, .bp-toc > ul > li) {
2661
2850
  counter-increment: bp-toc;
2662
2851
  margin: 0;
2663
2852
  padding: 0;
@@ -2683,7 +2872,7 @@
2683
2872
  transition: none;
2684
2873
  }
2685
2874
  /* Auto-numbered index (01, 02…), tabular so the digits line up. */
2686
- :where(.bp-sidebar > ul > li > a, .bp-sidebar__panel > ul > li > a, .bp-toc > ul > li > a)::before {
2875
+ :where(.bp-sidebar > ul > li > a, .bp-toc-switcher > ul > li > a, .bp-toc > ul > li > a)::before {
2687
2876
  content: counter(bp-toc, decimal-leading-zero);
2688
2877
  flex: 0 0 var(--bp-toc-index-w, 14px);
2689
2878
  min-width: var(--bp-toc-index-w, 14px);
@@ -2730,9 +2919,6 @@
2730
2919
  :where(.bp-sidebar a:hover, .bp-toc a:hover) {
2731
2920
  color: var(--bp-ink);
2732
2921
  background: var(--bp-fill-amb);
2733
- transition:
2734
- color var(--bp-duration-fast) var(--bp-ease),
2735
- background-color var(--bp-duration-fast) var(--bp-ease);
2736
2922
  }
2737
2923
  :where(.bp-sidebar a[aria-current="location"], .bp-toc a[aria-current="location"], .bp-sidebar a.active, .bp-sidebar a.is-active, .bp-toc a.active, .bp-toc a.is-active) {
2738
2924
  color: var(--bp-ink);
@@ -2741,7 +2927,6 @@
2741
2927
  }
2742
2928
  :where(.bp-sidebar a:hover::before, .bp-toc a:hover::before) {
2743
2929
  color: var(--bp-ink);
2744
- transition: color var(--bp-duration-fast) var(--bp-ease);
2745
2930
  }
2746
2931
  :where(.bp-sidebar a[aria-current="location"]::before, .bp-toc a[aria-current="location"]::before, .bp-sidebar a.active::before, .bp-toc a.active::before) {
2747
2932
  color: var(--bp-ink);
@@ -4058,6 +4243,119 @@
4058
4243
  }
4059
4244
  }
4060
4245
 
4246
+ /* =====================================================================
4247
+ @layer components — drafting board: interactive surfaces sit on a dot
4248
+ mat. Last in the layer so its surface overrides win by source order.
4249
+ ===================================================================== */
4250
+ @layer components {
4251
+ /* The interactive component family (choice, preflight, choice-record,
4252
+ source, mock, gallery) gets a dot-grid backdrop + a padding "mat", so a
4253
+ live/interactive block reads as a working surface distinct from settled
4254
+ prose — a second axis beside the Obvious pill vocabulary, one that marks
4255
+ even the non-decision interactive surfaces. The host is the mat (board
4256
+ shows in its padding and any internal gaps); the rendered root inside is
4257
+ the solid card. */
4258
+ :where(bp-choice, bp-preflight, bp-choice-record, bp-source, bp-mock, bp-gallery) {
4259
+ display: block;
4260
+ margin: var(--bp-space-4) 0;
4261
+ padding: var(--bp-space-5);
4262
+ background-color: var(--bp-board-bg);
4263
+ background-image: var(--bp-board);
4264
+ background-size: var(--bp-board-gap) var(--bp-board-gap);
4265
+ }
4266
+ /* Once upgraded, every dotted-mat host rounds to the Obvious container
4267
+ radius. data-bp-rendered is the uniform runtime hook — all interactive
4268
+ hosts set it on connect. bp-rationale also carries the flag but is not
4269
+ a mat host and is excluded from this selector list. */
4270
+ :where(
4271
+ bp-choice,
4272
+ bp-preflight,
4273
+ bp-choice-record,
4274
+ bp-choice-record-row,
4275
+ bp-source,
4276
+ bp-mock,
4277
+ bp-gallery
4278
+ )[data-bp-rendered="1"] {
4279
+ border-radius: var(--bp-radius-8);
4280
+ overflow: hidden;
4281
+ }
4282
+ /* The rendered root owns no outer margin now — the host carries document
4283
+ rhythm — so the mat reads even on all four sides. */
4284
+ :where(.bp-choice, .bp-choice-record, .bp-source, .bp-mock, .bp-gallery),
4285
+ :where(bp-preflight) :where(.bp-preflight) {
4286
+ margin: 0;
4287
+ }
4288
+ /* Expanded mock floats fullscreen (position: fixed): collapse the host so
4289
+ no empty dotted mat is left behind in the document flow. */
4290
+ :where(bp-mock:has(.bp-mock.is-expanded)) {
4291
+ margin: 0;
4292
+ padding: 0;
4293
+ }
4294
+
4295
+ /* Single-surface specimens read as ONE continuous paper card on the mat;
4296
+ give each an opaque paper fill (source inherits a translucent
4297
+ --bp-fill-amb from <details>, which would let the dots bleed through). */
4298
+ :where(.bp-choice--tabs, .bp-preflight, .bp-source, .bp-gallery) {
4299
+ background-color: var(--bp-paper);
4300
+ }
4301
+
4302
+ /* Multi-card layouts (stack / gallery / resolved / record) stay transparent
4303
+ so the host board shows in their gaps; each card carries its own paper.
4304
+ Chosen / resolved cards swap paper for a translucent fill — restore an
4305
+ opaque paper base so the board can't bleed through the card. */
4306
+ :where(.bp-choice__card:has(.bp-choice__pick:checked)),
4307
+ :where(.bp-choice__card[data-resolved]) {
4308
+ background-color: var(--bp-paper);
4309
+ background-image: linear-gradient(var(--bp-fill-amb), var(--bp-fill-amb));
4310
+ }
4311
+ /* Rejected / set-aside cards use opacity, so the whole card (incl. its
4312
+ hatch) goes translucent and the board bleeds through. Keep the set-aside
4313
+ read via hatch + tag, not element opacity. */
4314
+ :where(
4315
+ .bp-choice--stack:has(.bp-choice__pick:checked)
4316
+ .bp-choice__card:not(:has(.bp-choice__pick:checked)),
4317
+ .bp-choice--resolved .bp-choice__card:not([data-resolved])
4318
+ ) {
4319
+ opacity: 1;
4320
+ background-color: var(--bp-paper);
4321
+ background-image: var(--bp-hatch);
4322
+ }
4323
+ :where(
4324
+ .bp-choice--gallery:has(.bp-choice__pick:checked)
4325
+ .bp-choice__mock:not(:has(.bp-choice__pick:checked))
4326
+ ) {
4327
+ opacity: 1;
4328
+ }
4329
+ :where(
4330
+ .bp-choice--gallery:has(.bp-choice__pick:checked)
4331
+ .bp-choice__mock:not(:has(.bp-choice__pick:checked))
4332
+ .bp-choice__mock-frame
4333
+ ) {
4334
+ background-color: var(--bp-paper);
4335
+ background-image: var(--bp-hatch);
4336
+ }
4337
+
4338
+ /* The compare disclosure sits between transparent siblings (gallery /
4339
+ record); make it an opaque paper card so the dots stop at its edge. */
4340
+ :where(.bp-choice__compare) {
4341
+ background-color: var(--bp-paper);
4342
+ }
4343
+
4344
+ /* Action footers (hint + reset): machine-voice mono directly on the dots is
4345
+ hard to read. A paper strip with a top hairline keeps the gap above as
4346
+ board; horizontal padding matches the content inset above. Tabs already
4347
+ sit on their own paper panel, so skip them. */
4348
+ :where(.bp-choice--stack, .bp-choice--gallery, .bp-choice--resolved) :where(.bp-choice__actions),
4349
+ :where(.bp-preflight__footer) {
4350
+ background-color: var(--bp-paper);
4351
+ padding: var(--bp-board-footer-pad-y) var(--bp-board-footer-pad-x);
4352
+ border-top: var(--bp-stroke) solid var(--bp-edge);
4353
+ }
4354
+ :where(.bp-choice--stack, .bp-choice--gallery, .bp-choice--resolved) :where(.bp-choice__actions) {
4355
+ --bp-board-footer-pad-x: var(--bp-space-3);
4356
+ }
4357
+ }
4358
+
4061
4359
  /* =====================================================================
4062
4360
  @layer utilities — last-resort single-purpose helpers + responsive
4063
4361
  ===================================================================== */
@@ -4162,7 +4460,7 @@
4162
4460
  }
4163
4461
  /* The gliding pill only makes sense in the fixed column; in the collapsed
4164
4462
  horizontal nav the active entry just shows its own static pill. */
4165
- :where(.bp-sidebar > ul, .bp-sidebar__panel > ul, .bp-toc > ul)::before { content: none; }
4463
+ :where(.bp-sidebar > ul, .bp-toc-switcher > ul, .bp-toc > ul)::before { content: none; }
4166
4464
  :where(.bp-sidebar a[aria-current="location"], .bp-toc a[aria-current="location"]) {
4167
4465
  background: var(--bp-fill-hi);
4168
4466
  }
@@ -4429,11 +4727,12 @@ body > .site-nav .site-nav__theme:hover {
4429
4727
  read as one control family (border, radius, surface + hover/focus states). */
4430
4728
  .sidebar-field {
4431
4729
  border: 1px solid var(--bp-edge);
4432
- border-radius: var(--bp-radius-6, 6px);
4730
+ border-radius: var(--bp-radius-pill);
4433
4731
  background: var(--bp-paper);
4434
- /* Hover-only color motion — idle fields snap on theme flip so the root
4435
- view-transition crossfade isn't fighting per-field token tweens. */
4436
- transition: none;
4732
+ /* Color motion is hover-only so idle fields snap on theme flip and don't
4733
+ fight the root view-transition crossfade; transform is exempt (not a
4734
+ color) so the press nudge can ease in every state. */
4735
+ transition: transform var(--bp-duration-fast) var(--bp-ease-out);
4437
4736
  }
4438
4737
  .sidebar-field:hover,
4439
4738
  .sidebar-field:focus-within {
@@ -4441,7 +4740,13 @@ body > .site-nav .site-nav__theme:hover {
4441
4740
  background: var(--bp-fill-amb);
4442
4741
  transition:
4443
4742
  border-color var(--bp-duration-fast) var(--bp-ease-out),
4444
- background-color var(--bp-duration-fast) var(--bp-ease-out);
4743
+ background-color var(--bp-duration-fast) var(--bp-ease-out),
4744
+ transform var(--bp-duration-fast) var(--bp-ease-out);
4745
+ }
4746
+ /* Press feedback — match the Obvious app: the select triggers (doc switcher,
4747
+ contents dropdown) and the search trigger nudge 1px down on press. */
4748
+ .sidebar-field:active {
4749
+ transform: translateY(1px);
4445
4750
  }
4446
4751
  .doc-switcher__current {
4447
4752
  display: flex;
@@ -4460,7 +4765,7 @@ body > .site-nav .site-nav__theme:hover {
4460
4765
  display: none;
4461
4766
  }
4462
4767
  .doc-switcher__current::before {
4463
- content: none !important;
4768
+ content: none;
4464
4769
  }
4465
4770
  .doc-switcher__label {
4466
4771
  display: flex;
@@ -4497,7 +4802,7 @@ body > .site-nav .site-nav__theme:hover {
4497
4802
  padding: var(--bp-space-1);
4498
4803
  border: 1px solid var(--bp-edge);
4499
4804
  /* Container radius − padding = item radius, so the rows nest perfectly. */
4500
- border-radius: var(--bp-radius-8, 8px);
4805
+ border-radius: var(--bp-shell-panel-radius, var(--bp-radius-12, 12px));
4501
4806
  background: var(--bp-paper);
4502
4807
  box-shadow: var(--bp-shadow-pop);
4503
4808
  transform-origin: top center;
@@ -4524,7 +4829,7 @@ body > .site-nav .site-nav__theme:hover {
4524
4829
  justify-content: space-between;
4525
4830
  gap: var(--bp-space-2);
4526
4831
  padding: var(--bp-space-2) var(--bp-space-3);
4527
- border-radius: var(--bp-radius-4, 4px);
4832
+ border-radius: var(--bp-radius-8, 8px);
4528
4833
  text-decoration: none;
4529
4834
  }
4530
4835
  .doc-switcher__menu a:hover {
@@ -4566,6 +4871,130 @@ body > .site-nav .site-nav__theme:hover {
4566
4871
  opacity: 1;
4567
4872
  }
4568
4873
 
4874
+ /* =====================================================================
4875
+ Contents dropdown (.bp-toc-switcher)
4876
+ ---------------------------------------------------------------------
4877
+ The runtime wraps the contents list in a <details> so the rail can
4878
+ collapse into the same select-style control as the doc switcher once it
4879
+ becomes a horizontal band (≤860px). On the wide column the summary is
4880
+ hidden and blueprint.js keeps the <details> open, so the list renders as
4881
+ the plain vertical rail; only the narrow strip shows the trigger field +
4882
+ popover menu. ===================================================== */
4883
+ /* Reset the base <details> disclosure chrome (border/surface/padding) so the
4884
+ wrapper is invisible on the wide column — the list must read as the plain
4885
+ rail, not a boxed disclosure. */
4886
+ .bp-toc-switcher {
4887
+ position: relative;
4888
+ margin: 0;
4889
+ padding: 0;
4890
+ border: 0;
4891
+ background: none;
4892
+ }
4893
+ /* Trigger field — hidden on the wide column, shown as the dropdown summary on
4894
+ the narrow strip. Mirrors the doc-switcher current row. */
4895
+ .bp-toc-switcher__current {
4896
+ display: none;
4897
+ align-items: center;
4898
+ justify-content: space-between;
4899
+ gap: var(--bp-space-3);
4900
+ height: var(--bp-sidebar-toggle-size);
4901
+ margin: 0;
4902
+ padding: 0 var(--bp-space-3);
4903
+ list-style: none;
4904
+ font-weight: 400;
4905
+ cursor: pointer;
4906
+ }
4907
+ .bp-toc-switcher__current::-webkit-details-marker {
4908
+ display: none;
4909
+ }
4910
+ .bp-toc-switcher__current::before {
4911
+ content: none;
4912
+ }
4913
+ .bp-toc-switcher__label {
4914
+ display: flex;
4915
+ min-width: 0;
4916
+ }
4917
+ .bp-toc-switcher__title {
4918
+ min-width: 0;
4919
+ overflow: hidden;
4920
+ font-size: var(--bp-text-small);
4921
+ line-height: 1.3;
4922
+ font-weight: var(--bp-weight-medium);
4923
+ color: var(--bp-text);
4924
+ white-space: nowrap;
4925
+ text-overflow: ellipsis;
4926
+ }
4927
+ .bp-toc-switcher__chevron {
4928
+ display: inline-flex;
4929
+ flex: 0 0 auto;
4930
+ color: var(--bp-text-secondary);
4931
+ }
4932
+ .bp-toc-switcher[open] .bp-toc-switcher__chevron {
4933
+ transform: rotate(180deg);
4934
+ }
4935
+
4936
+ @media (max-width: 860px) {
4937
+ /* The rail is a horizontal band here — let the dropdown's popover escape it
4938
+ instead of being clipped by the strip's own scroll box. Size to content so
4939
+ the rail's bottom border lands right after the footer (theme + author) — the
4940
+ fixed-rail full-viewport height (.site-nav ~ .bp-sidebar) would otherwise
4941
+ push it to the bottom of the screen with dead space between. */
4942
+ .bp-sidebar:has(.bp-toc-switcher) {
4943
+ overflow: visible;
4944
+ max-height: none;
4945
+ height: auto;
4946
+ }
4947
+ .bp-toc-switcher__current {
4948
+ display: flex;
4949
+ }
4950
+ /* Popover menu: float the contents below the trigger like the doc-switcher
4951
+ menu, replacing the inline horizontal strip. Native <details> hides it
4952
+ while collapsed, so only the open state needs the floating chrome. */
4953
+ .bp-toc-switcher[open] > ul {
4954
+ position: absolute;
4955
+ left: 0;
4956
+ right: 0;
4957
+ top: calc(100% + var(--bp-space-1));
4958
+ z-index: 300;
4959
+ display: block;
4960
+ max-height: min(60vh, 360px);
4961
+ overflow: hidden auto;
4962
+ gap: 0;
4963
+ margin: 0;
4964
+ padding: var(--bp-space-1);
4965
+ border: 1px solid var(--bp-edge);
4966
+ border-radius: var(--bp-radius-8, 8px);
4967
+ background: var(--bp-paper);
4968
+ box-shadow: var(--bp-shadow-pop);
4969
+ animation: bp-doc-switcher-in var(--bp-duration-normal) var(--bp-ease-out);
4970
+ }
4971
+ /* Nested sub-entries stack as rows too rather than the strip's inline run. */
4972
+ .bp-toc-switcher ul ul {
4973
+ display: block;
4974
+ overflow: visible;
4975
+ }
4976
+ /* Group eyebrow heads its run as a quiet stacked label, not an inline chip. */
4977
+ .bp-toc-switcher .bp-nav-group {
4978
+ display: block;
4979
+ margin: var(--bp-space-2) 0 var(--bp-space-1);
4980
+ }
4981
+ .bp-toc-switcher .bp-nav-group:first-child {
4982
+ margin-top: 0;
4983
+ }
4984
+ /* The gliding pill is suppressed on the strip; the active row carries its
4985
+ own fill, matching the doc-switcher's current row. */
4986
+ .bp-toc-switcher a[aria-current="location"] {
4987
+ background: var(--bp-fill-hi);
4988
+ }
4989
+ /* Menu rows snap on hover like the doc-switcher — no row color tween. */
4990
+ .bp-toc-switcher[open] ul a,
4991
+ .bp-toc-switcher[open] ul a:hover,
4992
+ .bp-toc-switcher[open] ul a::before,
4993
+ .bp-toc-switcher[open] ul a:hover::before {
4994
+ transition: none;
4995
+ }
4996
+ }
4997
+
4569
4998
  /* Sidebar search — header row beside the corner toggle, above the header scrim. */
4570
4999
  .sidebar-search {
4571
5000
  position: relative;
@@ -4632,8 +5061,8 @@ body > .site-nav .site-nav__theme:hover {
4632
5061
  Search command menu (docs chrome) — Mintlify-style palette
4633
5062
  ---------------------------------------------------------------------
4634
5063
  Opened from the sidebar trigger or ⌘K / Ctrl+K (see wireSearchPalette).
4635
- Reuses the rounded sidebar control family: paper surface, --bp-radius-8
4636
- panel with --bp-shadow-pop, --bp-radius-4 rows, the --bp-fill-amb /
5064
+ Reuses the rounded sidebar control family: paper surface, --bp-radius-16
5065
+ panel with --bp-shadow-pop, --bp-radius-8 rows, the --bp-fill-amb /
4637
5066
  --bp-fill-hi hover + selected surfaces, and the shared open animation.
4638
5067
  Chrome stays on the neutral ink scale. Query filtering is a plain
4639
5068
  substring match over the page's headings; richer search is deferred.
@@ -4679,17 +5108,17 @@ body > .site-nav .site-nav__theme:hover {
4679
5108
  max-height: min(560px, 80vh);
4680
5109
  overflow: hidden;
4681
5110
  border: 1px solid var(--bp-edge);
4682
- border-radius: var(--bp-radius-8, 8px);
5111
+ border-radius: var(--bp-radius-16, 16px);
4683
5112
  background: var(--bp-paper);
4684
5113
  box-shadow: var(--bp-shadow-pop);
4685
5114
  }
4686
5115
 
4687
- /* Search row */
5116
+ /* Search row — icon hugging the input via a tight gap. */
4688
5117
  .docs-search__field {
4689
5118
  display: flex;
4690
5119
  align-items: center;
4691
- gap: var(--bp-space-3);
4692
- padding: var(--bp-space-3) var(--bp-space-4);
5120
+ gap: var(--bp-space-2);
5121
+ padding: var(--bp-space-3);
4693
5122
  border-bottom: 1px solid var(--bp-edge);
4694
5123
  }
4695
5124
  .docs-search__icon {
@@ -4736,12 +5165,15 @@ body > .site-nav .site-nav__theme:hover {
4736
5165
  overflow-y: auto;
4737
5166
  padding: var(--bp-space-2);
4738
5167
  }
5168
+ .docs-search__results:has(> .docs-search__empty:only-child) {
5169
+ padding: 0;
5170
+ }
4739
5171
  /* Group label — machine voice (mono + uppercase) with a trailing count. */
4740
5172
  .docs-search__group {
4741
5173
  display: flex;
4742
5174
  align-items: center;
4743
5175
  gap: var(--bp-space-2);
4744
- padding: var(--bp-space-2) var(--bp-space-3) var(--bp-space-1);
5176
+ padding: var(--bp-space-1) var(--bp-space-3) var(--bp-space-1);
4745
5177
  font-family: var(--bp-mono);
4746
5178
  font-size: var(--bp-label-md);
4747
5179
  letter-spacing: var(--bp-label-md-ls);
@@ -4757,12 +5189,11 @@ body > .site-nav .site-nav__theme:hover {
4757
5189
  Rows are <a> for navigation; suppress the base prose link underline except
4758
5190
  on the selected/hovered surface (background carries the affordance). */
4759
5191
  .docs-search__row {
4760
- display: grid;
4761
- grid-template-columns: 1fr auto;
4762
- align-items: center;
4763
- gap: var(--bp-space-1) var(--bp-space-3);
4764
- padding: var(--bp-space-2) var(--bp-space-3);
4765
- border-radius: var(--bp-radius-4, 4px);
5192
+ display: flex;
5193
+ align-items: flex-start;
5194
+ gap: var(--bp-space-1);
5195
+ padding: var(--bp-space-1) var(--bp-space-2);
5196
+ border-radius: var(--bp-radius-8, 8px);
4766
5197
  cursor: pointer;
4767
5198
  text-decoration: none;
4768
5199
  color: inherit;
@@ -4776,13 +5207,31 @@ body > .site-nav .site-nav__theme:hover {
4776
5207
  background: var(--bp-fill-hi);
4777
5208
  text-decoration: none;
4778
5209
  }
5210
+ /* Leading icon cell — a fixed square holding the heading-anchor "#" in the
5211
+ machine voice, so every row aligns on one icon column. */
5212
+ .docs-search__icon-box {
5213
+ grid-column: 1;
5214
+ grid-row: 1 / -1;
5215
+ align-self: start;
5216
+ display: inline-flex;
5217
+ align-items: center;
5218
+ justify-content: center;
5219
+ width: 20px;
5220
+ height: 20px;
5221
+ font-family: var(--bp-mono);
5222
+ font-size: var(--bp-text-small);
5223
+ color: var(--bp-text-secondary);
5224
+ }
4779
5225
  .docs-search__row-main {
4780
- display: flex;
4781
- flex-direction: column;
4782
- gap: var(--bp-space-1);
5226
+ display: grid;
5227
+ flex: 1 1 auto;
5228
+ grid-template-columns: 20px 1fr;
5229
+ gap: 0;
4783
5230
  min-width: 0;
4784
5231
  }
4785
5232
  .docs-search__crumb {
5233
+ grid-column: 2;
5234
+ grid-row: 1;
4786
5235
  display: block;
4787
5236
  font-family: var(--bp-mono);
4788
5237
  font-size: var(--bp-label-md);
@@ -4793,22 +5242,22 @@ body > .site-nav .site-nav__theme:hover {
4793
5242
  text-overflow: ellipsis;
4794
5243
  }
4795
5244
  .docs-search__title {
4796
- display: flex;
4797
- align-items: baseline;
4798
- gap: var(--bp-space-1);
5245
+ grid-column: 2;
5246
+ grid-row: 2;
5247
+ display: block;
4799
5248
  min-width: 0;
4800
5249
  font-family: var(--bp-sans);
4801
5250
  font-size: var(--bp-text-small);
4802
- line-height: 1.3;
4803
- font-weight: var(--bp-weight-medium);
5251
+ line-height: var(--bp-lh-small);
5252
+ font-weight: var(--bp-weight-body);
4804
5253
  color: var(--bp-text);
5254
+ white-space: nowrap;
5255
+ overflow: hidden;
5256
+ text-overflow: ellipsis;
4805
5257
  }
4806
- /* The "#" anchor glyph borrows the machine voice so it reads as a heading
4807
- link, not prose. */
4808
- .docs-search__hash {
4809
- flex: 0 0 auto;
4810
- font-family: var(--bp-mono);
4811
- color: var(--bp-text-secondary);
5258
+ /* When a row has no breadcrumb, the title sits on the icon's line. */
5259
+ .docs-search__row-main:not(:has(.docs-search__crumb)) .docs-search__title {
5260
+ grid-row: 1;
4812
5261
  }
4813
5262
  .docs-search__snippet {
4814
5263
  overflow: hidden;
@@ -4828,8 +5277,15 @@ body > .site-nav .site-nav__theme:hover {
4828
5277
  .docs-search__enter {
4829
5278
  flex: 0 0 auto;
4830
5279
  align-self: center;
4831
- padding: 1px 5px;
4832
- font-size: var(--bp-label-md);
5280
+ display: inline-flex;
5281
+ align-items: center;
5282
+ justify-content: center;
5283
+ width: 20px;
5284
+ height: 20px;
5285
+ padding: 0;
5286
+ border: 0;
5287
+ background: none;
5288
+ font-size: var(--bp-text-small);
4833
5289
  color: var(--bp-text-secondary);
4834
5290
  opacity: 0;
4835
5291
  }
@@ -4838,64 +5294,110 @@ body > .site-nav .site-nav__theme:hover {
4838
5294
  opacity: 1;
4839
5295
  }
4840
5296
 
4841
- /* Empty state */
5297
+ /* Empty state — a quiet centered text stack, no icon. */
4842
5298
  .docs-search__empty {
5299
+ border-top: 1px solid var(--bp-edge);
5300
+ padding: var(--bp-space-2);
5301
+ font-size: var(--bp-text-small);
5302
+ line-height: var(--bp-lh-small);
5303
+ }
5304
+ .docs-search__empty-center {
5305
+ display: flex;
5306
+ align-items: center;
5307
+ justify-content: center;
5308
+ padding-block: var(--bp-space-5);
5309
+ }
5310
+ .docs-search__empty-copy {
4843
5311
  display: flex;
4844
5312
  flex-direction: column;
4845
5313
  align-items: center;
4846
- gap: var(--bp-space-2);
4847
- padding: var(--bp-space-6) var(--bp-space-4);
4848
5314
  text-align: center;
4849
5315
  }
4850
- .docs-search__empty-icon {
4851
- display: inline-flex;
4852
- color: var(--bp-text-secondary);
4853
- opacity: 0.6;
4854
- }
4855
5316
  .docs-search__empty-title {
5317
+ margin: 0;
4856
5318
  font-family: var(--bp-sans);
4857
5319
  font-size: var(--bp-text-small);
4858
- font-weight: var(--bp-weight-medium);
4859
- color: var(--bp-text);
5320
+ line-height: var(--bp-lh-small);
5321
+ font-weight: var(--bp-weight-body);
5322
+ color: var(--bp-text-secondary);
5323
+ text-align: center;
4860
5324
  }
4861
5325
  .docs-search__empty-note {
4862
- font-size: var(--bp-text-small);
4863
- color: var(--bp-text-secondary);
5326
+ margin: var(--bp-space-1) 0 0;
5327
+ font-family: var(--bp-sans);
5328
+ font-size: var(--bp-text-code);
5329
+ line-height: var(--bp-lh-code);
5330
+ color: var(--bp-ink-soft);
5331
+ text-align: center;
4864
5332
  }
4865
5333
 
4866
- /* Footer keyboard legend */
5334
+ /* Footer command rows. A quiet stack of secondary actions, each a full-width
5335
+ button whose icon, label, and chip step from secondary to primary ink on
5336
+ hover — no background wash — and trails a mono shortcut
5337
+ chip — the layout the source palette parks its workspace commands in. */
4867
5338
  .docs-search__foot {
4868
- display: flex;
4869
- flex-wrap: wrap;
4870
- align-items: center;
4871
- justify-content: space-between;
4872
- gap: var(--bp-space-1) var(--bp-space-3);
4873
- padding: var(--bp-space-2) var(--bp-space-4);
4874
5339
  border-top: 1px solid var(--bp-edge);
4875
- font-family: var(--bp-mono);
4876
- font-size: var(--bp-label-md);
4877
- letter-spacing: var(--bp-label-md-ls);
4878
- color: var(--bp-text-secondary);
5340
+ padding: var(--bp-space-2);
5341
+ }
5342
+ .docs-search__actions {
5343
+ display: flex;
5344
+ flex-direction: column;
5345
+ gap: var(--bp-space-1);
4879
5346
  }
4880
- .docs-search__keys {
5347
+ .docs-search__action {
4881
5348
  display: flex;
4882
- flex-wrap: wrap;
4883
5349
  align-items: center;
4884
- gap: var(--bp-space-1) var(--bp-space-3);
5350
+ gap: var(--bp-space-2);
5351
+ width: 100%;
5352
+ padding: var(--bp-space-1) var(--bp-space-2);
5353
+ border: 0;
5354
+ border-radius: var(--bp-radius-4, 4px);
5355
+ background: none;
5356
+ font-family: var(--bp-sans);
5357
+ font-size: var(--bp-text-small);
5358
+ line-height: var(--bp-lh-control);
5359
+ color: var(--bp-text-secondary);
5360
+ text-align: left;
5361
+ cursor: pointer;
5362
+ }
5363
+ .docs-search__action:hover {
5364
+ color: var(--bp-text);
5365
+ }
5366
+ .docs-search__action:focus-visible {
5367
+ color: var(--bp-text);
5368
+ outline: none;
5369
+ box-shadow: inset 0 0 0 2px var(--bp-ink-line);
4885
5370
  }
4886
- .docs-search__key {
5371
+ .docs-search__action-icon {
5372
+ flex: 0 0 auto;
4887
5373
  display: inline-flex;
4888
- align-items: center;
4889
- gap: var(--bp-space-1);
5374
+ color: inherit;
4890
5375
  }
4891
- .docs-search__foot kbd {
4892
- padding: 0 4px;
4893
- border-width: 1px;
5376
+ .docs-search__action-label {
5377
+ flex: 1 1 auto;
5378
+ min-width: 0;
5379
+ white-space: nowrap;
5380
+ overflow: hidden;
5381
+ text-overflow: ellipsis;
5382
+ }
5383
+ .docs-search__chip {
5384
+ flex: 0 0 auto;
5385
+ display: inline-flex;
5386
+ align-items: center;
5387
+ gap: 3px;
5388
+ padding: 1px var(--bp-space-1);
5389
+ border: 1px solid var(--bp-edge);
4894
5390
  border-radius: var(--bp-radius-2, 2px);
5391
+ background: var(--bp-fill-hi);
5392
+ font-family: var(--bp-mono);
4895
5393
  font-size: var(--bp-label-md);
4896
5394
  line-height: 1.5;
4897
5395
  color: var(--bp-text-secondary);
4898
5396
  }
5397
+ .docs-search__action:hover .docs-search__chip,
5398
+ .docs-search__action:focus-visible .docs-search__chip {
5399
+ color: var(--bp-text);
5400
+ }
4899
5401
 
4900
5402
  /* Sidebar footer scrim token. The footer (theme switch + author/date) gets a
4901
5403
  soft fade above it so the scrollable TOC dissolves into the rail rather than
@@ -5018,7 +5520,7 @@ body > .site-nav .site-nav__theme:hover {
5018
5520
  height: var(--bp-theme-toggle-height);
5019
5521
  padding: 2px;
5020
5522
  border: 1px solid var(--bp-edge);
5021
- border-radius: var(--bp-radius-6, 6px);
5523
+ border-radius: var(--bp-radius-pill);
5022
5524
  background: var(--bp-fill-amb);
5023
5525
  color: var(--bp-text-secondary);
5024
5526
  cursor: pointer;
@@ -5044,7 +5546,7 @@ body > .site-nav .site-nav__theme:hover {
5044
5546
  width: calc(var(--bp-theme-toggle-height) - 6px);
5045
5547
  height: calc(var(--bp-theme-toggle-height) - 6px);
5046
5548
  border: 1px solid var(--bp-ink-line);
5047
- border-radius: var(--bp-radius-4, 4px);
5549
+ border-radius: var(--bp-radius-round);
5048
5550
  background: var(--bp-paper);
5049
5551
  box-shadow: 0 1px 2px oklch(0 0 0 / 0.06);
5050
5552
  pointer-events: none;
@@ -5092,7 +5594,7 @@ body > .site-nav .site-nav__theme:hover {
5092
5594
  height: var(--bp-sidebar-toggle-size);
5093
5595
  padding: 0;
5094
5596
  border: 1px solid var(--bp-edge);
5095
- border-radius: var(--bp-radius-6, 6px);
5597
+ border-radius: var(--bp-radius-pill);
5096
5598
  background: var(--bp-fill-amb);
5097
5599
  color: var(--bp-text-secondary);
5098
5600
  cursor: pointer;
@@ -5170,7 +5672,27 @@ body > .site-nav .site-nav__theme:hover {
5170
5672
  padding-top: var(--bp-space-6);
5171
5673
  padding-inline: var(--bp-sidebar-content-inset);
5172
5674
  padding-bottom: var(--bp-sidebar-scroll-padding-bottom);
5675
+ /* Mirror the footer clearance at the top: when the runtime scrolls the
5676
+ active entry into view (blueprint.js revealInRail), keep it below the
5677
+ header dissolve scrim instead of tucking under the corner toggle. */
5678
+ scroll-padding-top: var(--bp-sidebar-header-scrim);
5173
5679
  scroll-padding-bottom: var(--bp-sidebar-scroll-padding-bottom);
5680
+ display: flex;
5681
+ flex-direction: column;
5682
+ }
5683
+ /* Wrapper is mobile-only chrome; on the wide column its children join the
5684
+ panel flex in search → doc switcher → TOC order. */
5685
+ .sidebar-mobile-head {
5686
+ display: contents;
5687
+ }
5688
+ .sidebar-search {
5689
+ order: 1;
5690
+ }
5691
+ .doc-switcher {
5692
+ order: 2;
5693
+ }
5694
+ .bp-toc-switcher {
5695
+ order: 3;
5174
5696
  }
5175
5697
 
5176
5698
  /* Drop the switcher below the header scrim (search stays above it on z 300). */
@@ -5227,7 +5749,7 @@ body > .site-nav .site-nav__theme:hover {
5227
5749
 
5228
5750
  /* Fade scrim above the footer: the TOC scrolls out beneath it (z below the
5229
5751
  footer controls), with a solid bottom band backing the footer. */
5230
- .bp-sidebar::after {
5752
+ #bp-contents-rail::after {
5231
5753
  content: "";
5232
5754
  position: fixed;
5233
5755
  left: 0;
@@ -5242,7 +5764,7 @@ body > .site-nav .site-nav__theme:hover {
5242
5764
  /* Radial scrim around the corner toggle: the TOC dissolves in a circle that
5243
5765
  radiates from the icon as entries scroll up beneath it. The box is large
5244
5766
  enough to hold the gradient's outer radius; transparent beyond it. */
5245
- .bp-sidebar::before {
5767
+ #bp-contents-rail::before {
5246
5768
  content: "";
5247
5769
  position: fixed;
5248
5770
  top: 0;
@@ -5261,12 +5783,154 @@ body > .site-nav .site-nav__theme:hover {
5261
5783
  }
5262
5784
  }
5263
5785
 
5264
- /* Below the rail's fixed breakpoint it becomes a static horizontal nav,
5265
- where collapsing has no meaning hide the control entirely. */
5786
+ /* Below the rail's fixed breakpoint the chrome flattens into a static
5787
+ horizontal nav. The runtime chrome lives in this (unlayered) sheet, so its
5788
+ resting rules beat blueprint.css's layered responsive block — the flattened
5789
+ layout has to be reconciled here or the fixed-rail geometry leaks through. */
5266
5790
  @media (max-width: 860px) {
5791
+ :root {
5792
+ --bp-sidebar-mobile-head-h: calc(
5793
+ var(--bp-sidebar-toggle-size) + var(--bp-space-3) * 2
5794
+ );
5795
+ }
5796
+
5797
+ /* Hash / TOC jumps land below the pinned mobile head instead of tucking the
5798
+ heading under it. Framed layouts (701–860px) reserve the shell top band +
5799
+ head; phones and site-nav offsets are handled in their own blocks below. */
5800
+ html[data-bp-document] {
5801
+ --bp-scroll-margin: calc(
5802
+ var(--bp-sidebar-mobile-head-h) + var(--bp-space-2)
5803
+ );
5804
+ }
5805
+
5806
+ /* Collapsing has no meaning once the rail is a horizontal strip. */
5267
5807
  .sidebar-toggle {
5268
5808
  display: none;
5269
5809
  }
5810
+
5811
+ /* Fixed band: section select + search icon stay pinned while the doc
5812
+ switcher and footer scroll with the page. Sticky failed here because the
5813
+ whole rail scrolls away — fixed keeps the controls always reachable on long
5814
+ documents. No own border: the rail's bottom edge carries the single
5815
+ divider, landing below the footer where the nav content ends. Between phone
5816
+ and rail breakpoints the head sits inside the shell frame, below the top
5817
+ rule and one stroke inset on the sides so the pseudo borders stay visible
5818
+ (≤700px drops the frame and returns to full bleed). */
5819
+ .sidebar-mobile-head {
5820
+ position: fixed;
5821
+ top: 0;
5822
+ left: 0;
5823
+ right: 0;
5824
+ z-index: 320;
5825
+ display: grid;
5826
+ grid-template-columns: 1fr auto;
5827
+ column-gap: var(--bp-space-2);
5828
+ background: var(--bp-bg);
5829
+ padding: var(--bp-space-3) var(--bp-space-4);
5830
+ }
5831
+
5832
+ /* Framed range (701–860px): the desk frame is drawn, so tuck the head inside
5833
+ it and back the top rule so nothing scrolls through. */
5834
+ @media (min-width: 701px) {
5835
+ /* The shell top rule is a height:0 box with an 8%-alpha border-top, so when
5836
+ content scrolls beneath it the near-transparent border lets it bleed
5837
+ through that 1px row (the desk mask only covers the band above it).
5838
+ Extend the opaque desk mask by one stroke so it backs the border row;
5839
+ the frame border (z 210) then draws over solid bg, staying visible. */
5840
+ html[data-bp-document] body::before {
5841
+ height: calc(var(--bp-shell-inset-top) + var(--bp-stroke));
5842
+ }
5843
+ html[data-bp-document] {
5844
+ --bp-scroll-margin: calc(
5845
+ var(--bp-shell-inset-top) + var(--bp-stroke) +
5846
+ var(--bp-sidebar-mobile-head-h) + var(--bp-space-2)
5847
+ );
5848
+ }
5849
+ html[data-bp-document] .sidebar-mobile-head {
5850
+ /* Flush below the top rule (inset-top + its hairline); one stroke inside
5851
+ the vertical rules so they stay visible. */
5852
+ top: calc(var(--bp-shell-inset-top) + var(--bp-stroke));
5853
+ left: calc(var(--bp-shell-inset-left) + var(--bp-stroke));
5854
+ right: calc(var(--bp-shell-inset-right) + var(--bp-stroke));
5855
+ /* Frame inset + this padding matches .bp-sidebar padding-inline. */
5856
+ padding-inline: max(
5857
+ 0px,
5858
+ calc(
5859
+ var(--bp-space-4) - var(--bp-shell-inset-left) - var(--bp-stroke)
5860
+ )
5861
+ );
5862
+ }
5863
+ }
5864
+
5865
+ /* Phone range (≤700px): the desk frame is removed, so the head is full bleed.
5866
+ Only the site nav, when present, pushes it down. */
5867
+ @media (max-width: 700px) {
5868
+ html[data-bp-document]:has(body > .site-nav) {
5869
+ --bp-scroll-margin: calc(
5870
+ var(--bp-site-nav-height) + var(--bp-sidebar-mobile-head-h) +
5871
+ var(--bp-space-2)
5872
+ );
5873
+ }
5874
+ html:has(body > .site-nav) .sidebar-mobile-head {
5875
+ top: var(--bp-site-nav-height);
5876
+ }
5877
+ }
5878
+
5879
+ /* Once the rail's own bottom border has scrolled up under the pinned head,
5880
+ the head carries the divider at that same line (blueprint.js toggles the
5881
+ attribute). At rest the head is borderless — the rail edge is the divider. */
5882
+ .sidebar-mobile-head[data-bp-head-stuck] {
5883
+ border-bottom: 1px solid var(--bp-edge);
5884
+ }
5885
+ .bp-sidebar__panel {
5886
+ display: block;
5887
+ /* Reserve the fixed head height so the doc switcher starts below it. */
5888
+ padding-top: var(--bp-sidebar-mobile-head-h);
5889
+ }
5890
+ .bp-toc-switcher {
5891
+ min-width: 0;
5892
+ }
5893
+ .sidebar-search {
5894
+ margin: 0;
5895
+ align-self: start;
5896
+ }
5897
+ /* Pill icon button — same trigger as the wide search field, opens ⌘K palette. */
5898
+ .sidebar-search__field {
5899
+ width: var(--bp-sidebar-toggle-size);
5900
+ min-width: var(--bp-sidebar-toggle-size);
5901
+ padding: 0;
5902
+ gap: 0;
5903
+ justify-content: center;
5904
+ }
5905
+ .sidebar-search__label,
5906
+ .sidebar-search__hint {
5907
+ display: none;
5908
+ }
5909
+ .doc-switcher {
5910
+ margin: 0;
5911
+ }
5912
+
5913
+ /* The fixed rail reserved a tall bottom band for its pinned footer; in the
5914
+ static strip that band is just dead space below the meta row. Match the
5915
+ top inset so the nav ends as tightly as it begins. Give the search,
5916
+ switchers, and footer row breathing room from the viewport edges. */
5917
+ .bp-sidebar {
5918
+ display: flex;
5919
+ flex-direction: column;
5920
+ gap: var(--bp-space-3);
5921
+ padding-inline: var(--bp-space-4);
5922
+ padding-bottom: var(--bp-space-4);
5923
+ padding-top: 0;
5924
+ }
5925
+
5926
+ /* The footer is a single meta row now, not a full-height column: pull the
5927
+ theme control and author/date into one left-aligned cluster so they read
5928
+ together rather than stranded at opposite edges of a wide strip. */
5929
+ .sidebar-footer {
5930
+ justify-content: flex-start;
5931
+ gap: var(--bp-space-5);
5932
+ margin-top: 0;
5933
+ }
5270
5934
  }
5271
5935
 
5272
5936
  @media print {
@@ -5407,7 +6071,7 @@ html[data-bp-document]:has(.bp-cite-pane.is-open) main {
5407
6071
  height: var(--bp-sidebar-toggle-size);
5408
6072
  padding: 0;
5409
6073
  border: var(--bp-stroke) solid var(--bp-edge);
5410
- border-radius: var(--bp-radius-6);
6074
+ border-radius: var(--bp-radius-pill);
5411
6075
  background: var(--bp-fill-amb);
5412
6076
  color: var(--bp-text-secondary);
5413
6077
  cursor: pointer;