@obvi/blueprint 1.1.2 → 1.1.3

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;
@@ -266,6 +298,32 @@
266
298
  --bp-radius-pill: 9999px;
267
299
  --bp-radius-round: 50%;
268
300
 
301
+ /* Obvious button palette — the interactive-control vocabulary. Buttons are
302
+ the one chrome surface that carries fill: primary (committed / CTA),
303
+ secondary (resting control), tertiary (quiet track / ghost fill), ghost
304
+ (text-only). Fills stay on the neutral scale (no hue) so figures keep the
305
+ blue monopoly. Each value consumes the live Obvious app variable
306
+ (--button-*) when a document is embedded in the dashboard, and otherwise
307
+ falls back to the OKLCH value from the app's colors.css. */
308
+ --bp-button-primary-bg: var(--button-primary-bg, oklch(0.1344 0 0));
309
+ --bp-button-primary-bg-hover: var(--button-primary-bg-hover, oklch(0.3012 0 0));
310
+ --bp-button-primary-bg-press: var(--button-primary-bg-press, oklch(0.36 0 0));
311
+ --bp-button-primary-fg: var(--button-primary-fg, oklch(1 0 0));
312
+ --bp-button-secondary-bg: var(--button-secondary-bg, oklch(1 0 0));
313
+ --bp-button-secondary-bg-hover: var(--button-secondary-bg-hover, oklch(0.9702 0 0));
314
+ --bp-button-secondary-bg-press: var(--button-secondary-bg-press, oklch(0.9461 0 0));
315
+ --bp-button-secondary-fg: var(--button-secondary-fg, oklch(0 0 0 / 0.8));
316
+ --bp-button-secondary-bd: var(--button-secondary-bd, oklch(0 0 0 / 0.08));
317
+ --bp-button-tertiary-bg: var(--button-tertiary-bg, oklch(0 0 0 / 0.04));
318
+ --bp-button-tertiary-bg-hover: var(--button-tertiary-bg-hover, oklch(0 0 0 / 0.08));
319
+ --bp-button-tertiary-fg: var(--button-tertiary-fg, oklch(0 0 0 / 0.64));
320
+ --bp-button-ghost-fg: var(--button-ghost-fg, oklch(0 0 0 / 0.4));
321
+ --bp-button-ghost-fg-hover: var(--button-ghost-fg-hover, oklch(0 0 0 / 0.64));
322
+ --bp-button-ghost-bg-hover: var(--button-ghost-bg-hover, oklch(0 0 0 / 0.04));
323
+ /* Lifted-border ring carried by a raised (selected) secondary control. */
324
+ --bp-button-lift: 0 0 2px 0 oklch(0 0 0 / 0.05),
325
+ 0 0 1px 0 oklch(0 0 0 / 0.05), 0 0 0 1px oklch(0 0 0 / 0.08);
326
+
269
327
  /* Section-eyebrow marker — the square that leads the "01 / 02" counter.
270
328
  Size is the block edge; gap is the space between it and the number. */
271
329
  --bp-marker-size: 10px;
@@ -354,6 +412,7 @@
354
412
  --bp-ink: var(--obvious-text-primary, oklch(1 0 0 / 0.92));
355
413
  --bp-ink-soft: oklch(1 0 0 / 0.4);
356
414
  --bp-ink-faint: oklch(1 0 0 / 0.16);
415
+ --bp-board-dot: color-mix(in oklch, var(--bp-ink-faint) 35%, oklch(0 0 0 / 0));
357
416
  --bp-highlight: oklch(1 0 0 / 0.14);
358
417
 
359
418
  /* Illustration accent (dark) — brighter blue holds on dark paper. */
@@ -384,6 +443,25 @@
384
443
  --bp-text-secondary: oklch(1 0 0 / 0.64);
385
444
  --bp-positive: oklch(0.74 0.15 150);
386
445
  --bp-positive-faint: oklch(0.34 0.07 150);
446
+
447
+ /* Obvious button palette (dark) — see the light block for the contract. */
448
+ --bp-button-primary-bg: var(--button-primary-bg, oklch(0.4091 0 0));
449
+ --bp-button-primary-bg-hover: var(--button-primary-bg-hover, oklch(0.4819 0 0));
450
+ --bp-button-primary-bg-press: var(--button-primary-bg-press, oklch(0.5795 0 0));
451
+ --bp-button-primary-fg: var(--button-primary-fg, oklch(1 0 0 / 0.92));
452
+ --bp-button-secondary-bg: var(--button-secondary-bg, oklch(0.3012 0 0));
453
+ --bp-button-secondary-bg-hover: var(--button-secondary-bg-hover, oklch(0.3407 0 0));
454
+ --bp-button-secondary-bg-press: var(--button-secondary-bg-press, oklch(0.4091 0 0));
455
+ --bp-button-secondary-fg: var(--button-secondary-fg, oklch(1 0 0 / 0.8));
456
+ --bp-button-secondary-bd: var(--button-secondary-bd, oklch(1 0 0 / 0.04));
457
+ --bp-button-tertiary-bg: var(--button-tertiary-bg, oklch(1 0 0 / 0.04));
458
+ --bp-button-tertiary-bg-hover: var(--button-tertiary-bg-hover, oklch(1 0 0 / 0.08));
459
+ --bp-button-tertiary-fg: var(--button-tertiary-fg, oklch(1 0 0 / 0.64));
460
+ --bp-button-ghost-fg: var(--button-ghost-fg, oklch(1 0 0 / 0.4));
461
+ --bp-button-ghost-fg-hover: var(--button-ghost-fg-hover, oklch(1 0 0 / 0.64));
462
+ --bp-button-ghost-bg-hover: var(--button-ghost-bg-hover, oklch(1 0 0 / 0.04));
463
+ --bp-button-lift: 0 0 2px 0 oklch(1 0 0 / 0.05),
464
+ 0 0 1px 0 oklch(1 0 0 / 0.05), 0 0 0 1px oklch(1 0 0 / 0.08);
387
465
  }
388
466
  }
389
467
 
@@ -1478,7 +1556,7 @@
1478
1556
  padding: var(--bp-space-2) var(--bp-space-3);
1479
1557
  background: var(--bp-ink);
1480
1558
  color: var(--bp-paper);
1481
- border-radius: var(--bp-radius-0);
1559
+ border-radius: var(--bp-radius-6);
1482
1560
  margin-bottom: var(--bp-space-3);
1483
1561
  }
1484
1562
  :where(.bp-choice:has(.bp-choice__commit:checked)) .bp-choice__verdict,
@@ -1538,10 +1616,9 @@
1538
1616
  border: 0;
1539
1617
  padding: 0;
1540
1618
  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;
1619
+ font-family: var(--bp-sans);
1620
+ font-size: var(--bp-text-small);
1621
+ line-height: var(--bp-lh-control);
1545
1622
  color: var(--bp-text-secondary);
1546
1623
  text-decoration: underline dotted;
1547
1624
  text-underline-offset: 3px;
@@ -1564,37 +1641,50 @@
1564
1641
  color: var(--bp-paper);
1565
1642
  background: var(--bp-ink);
1566
1643
  padding: 2px 8px;
1644
+ border-radius: var(--bp-radius-pill);
1567
1645
  }
1568
1646
  :where(.bp-choice__tag--out) {
1569
1647
  color: var(--bp-ink);
1570
1648
  border: 1px solid var(--bp-ink-faint);
1571
1649
  padding: 1px 7px;
1650
+ border-radius: var(--bp-radius-pill);
1572
1651
  }
1573
1652
 
1574
1653
  /* ---- layout: tabs (preview drafts, then adopt) ------------------- */
1654
+ :where(.bp-choice--tabs) {
1655
+ padding: var(--bp-board-card-pad);
1656
+ border: 1px solid var(--bp-ink-line);
1657
+ border-radius: var(--bp-board-card-radius);
1658
+ overflow: hidden;
1659
+ }
1575
1660
  :where(.bp-choice--tabs) :where(.bp-choice__seg) {
1576
1661
  display: flex;
1577
1662
  flex-wrap: wrap;
1578
- gap: 0;
1579
- border: 1px solid var(--bp-ink-line);
1580
- border-radius: var(--bp-radius-0);
1581
- overflow: hidden;
1663
+ gap: 2px;
1664
+ padding: 2px;
1665
+ border: 0;
1666
+ border-radius: var(--bp-radius-pill);
1667
+ background: var(--bp-button-tertiary-bg);
1668
+ overflow: visible;
1582
1669
  margin: 0 0 var(--bp-space-3);
1583
1670
  width: fit-content;
1584
1671
  }
1585
1672
  :where(.bp-choice__seg-opt) {
1586
1673
  position: relative;
1587
1674
  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);
1675
+ font-family: var(--bp-sans);
1676
+ font-size: var(--bp-text-small);
1677
+ line-height: var(--bp-lh-control);
1678
+ color: var(--bp-button-ghost-fg);
1593
1679
  padding: 6px 14px;
1594
- border-right: 1px solid var(--bp-ink-line);
1680
+ border-radius: var(--bp-radius-pill);
1681
+ transition: background var(--bp-duration-fast) var(--bp-ease-out),
1682
+ color var(--bp-duration-fast) var(--bp-ease-out),
1683
+ box-shadow var(--bp-duration-fast) var(--bp-ease-out);
1595
1684
  }
1596
- :where(.bp-choice__seg-opt:last-child) {
1597
- border-right: 0;
1685
+ :where(.bp-choice__seg-opt:hover) {
1686
+ color: var(--bp-button-ghost-fg-hover);
1687
+ background: var(--bp-button-ghost-bg-hover);
1598
1688
  }
1599
1689
  :where(.bp-choice__seg-opt) :where(input) {
1600
1690
  position: absolute;
@@ -1602,8 +1692,9 @@
1602
1692
  pointer-events: none;
1603
1693
  }
1604
1694
  :where(.bp-choice__seg-opt:has(input:checked)) {
1605
- background: var(--bp-ink);
1606
- color: var(--bp-paper);
1695
+ background: var(--bp-button-secondary-bg);
1696
+ color: var(--bp-button-secondary-fg);
1697
+ box-shadow: var(--bp-button-lift);
1607
1698
  }
1608
1699
  :where(.bp-choice--tabs) :where(.bp-choice__panel) {
1609
1700
  display: none;
@@ -1616,18 +1707,19 @@
1616
1707
  align-items: center;
1617
1708
  gap: 8px;
1618
1709
  cursor: pointer;
1619
- border: 1px solid var(--bp-ink);
1620
- border-radius: var(--bp-radius-0);
1710
+ border: 1px solid var(--bp-button-secondary-bd);
1711
+ border-radius: var(--bp-radius-pill);
1621
1712
  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);
1713
+ font-family: var(--bp-sans);
1714
+ font-size: var(--bp-text-small);
1715
+ line-height: var(--bp-lh-control);
1716
+ color: var(--bp-button-secondary-fg);
1717
+ background: var(--bp-button-secondary-bg);
1718
+ transition: background var(--bp-duration-fast) var(--bp-ease-out),
1719
+ color var(--bp-duration-fast) var(--bp-ease-out);
1628
1720
  }
1629
1721
  :where(.bp-choice__adopt:hover) {
1630
- background: var(--bp-fill-hi);
1722
+ background: var(--bp-button-secondary-bg-hover);
1631
1723
  }
1632
1724
  :where(.bp-choice__adopt) :where(input) {
1633
1725
  position: absolute;
@@ -1638,13 +1730,14 @@
1638
1730
  border: 1px solid var(--bp-ink);
1639
1731
  background: var(--bp-fill-amb);
1640
1732
  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);
1733
+ margin-left: calc(-1 * var(--bp-board-card-pad));
1734
+ margin-right: calc(-1 * var(--bp-board-card-pad));
1735
+ border-radius: var(--bp-radius-6);
1644
1736
  }
1645
1737
  :where(.bp-choice__panel:has(.bp-choice__commit:checked)) :where(.bp-choice__adopt) {
1646
- background: var(--bp-ink);
1647
- color: var(--bp-paper);
1738
+ border-color: var(--bp-button-primary-bg);
1739
+ background: var(--bp-button-primary-bg);
1740
+ color: var(--bp-button-primary-fg);
1648
1741
  }
1649
1742
 
1650
1743
  /* ---- layout: stack (all cards visible) --------------------------- */
@@ -1656,7 +1749,7 @@
1656
1749
  display: block;
1657
1750
  position: relative;
1658
1751
  border: 1px solid var(--bp-edge);
1659
- border-radius: var(--bp-radius-0);
1752
+ border-radius: var(--bp-radius-6);
1660
1753
  padding: var(--bp-space-3);
1661
1754
  cursor: pointer;
1662
1755
  background: var(--bp-paper);
@@ -1744,7 +1837,7 @@
1744
1837
  position: relative;
1745
1838
  cursor: pointer;
1746
1839
  border: 1px solid var(--bp-edge);
1747
- border-radius: var(--bp-radius-0);
1840
+ border-radius: var(--bp-radius-6);
1748
1841
  overflow: hidden;
1749
1842
  background: var(--bp-paper);
1750
1843
  }
@@ -1799,18 +1892,32 @@
1799
1892
 
1800
1893
  :where(.bp-choice__compare) {
1801
1894
  margin-top: var(--bp-space-3);
1895
+ /* Override the base <details> inset (0 var(--bp-space-3) padding + a 2px
1896
+ left border) so the summary bar and disclosed body span the full card
1897
+ width — the summary and body carry their own padding. */
1898
+ padding: 0;
1802
1899
  border: 1px solid var(--bp-edge);
1803
- border-radius: var(--bp-radius-0);
1900
+ border-radius: var(--bp-radius-6);
1901
+ overflow: hidden;
1902
+ }
1903
+ :where(.bp-choice__compare[open]) {
1904
+ padding-bottom: 0;
1804
1905
  }
1805
1906
  :where(.bp-choice__compare > summary) {
1806
1907
  cursor: pointer;
1807
1908
  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);
1909
+ padding: var(--bp-space-3);
1910
+ font-family: var(--bp-sans);
1911
+ font-size: var(--bp-text-small);
1912
+ line-height: var(--bp-lh-control);
1913
+ color: var(--bp-button-ghost-fg);
1914
+ background: var(--bp-button-tertiary-bg);
1915
+ transition: background var(--bp-duration-fast) var(--bp-ease-out),
1916
+ color var(--bp-duration-fast) var(--bp-ease-out);
1917
+ }
1918
+ :where(.bp-choice__compare > summary:hover) {
1919
+ background: var(--bp-button-tertiary-bg-hover);
1920
+ color: var(--bp-button-ghost-fg-hover);
1814
1921
  }
1815
1922
  :where(.bp-choice__compare > summary)::-webkit-details-marker {
1816
1923
  display: none;
@@ -1819,11 +1926,17 @@
1819
1926
  padding: var(--bp-space-3);
1820
1927
  border-top: 1px solid var(--bp-edge);
1821
1928
  }
1929
+ /* The slot content is usually a <dl>; drop its document margins so the
1930
+ disclosed body keeps one uniform inset (vertical matches horizontal). */
1931
+ :where(.bp-choice__compare-body) :where(dl) {
1932
+ margin: 0;
1933
+ gap: var(--bp-space-2) var(--bp-space-3);
1934
+ }
1822
1935
 
1823
1936
  /* ---- Pre-flight (decisions before drafting) ---------------------- */
1824
1937
  :where(.bp-preflight) {
1825
1938
  border: 1px solid var(--bp-ink-line);
1826
- border-radius: var(--bp-radius-0);
1939
+ border-radius: var(--bp-board-card-radius);
1827
1940
  overflow: hidden;
1828
1941
  margin: var(--bp-space-4) 0;
1829
1942
  }
@@ -1852,23 +1965,9 @@
1852
1965
  }
1853
1966
  :where(.bp-preflight__q) {
1854
1967
  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;
1968
+ padding: var(--bp-space-4);
1857
1969
  border-bottom: 1px solid var(--bp-edge);
1858
1970
  }
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
1971
  :where(.bp-preflight__prompt) {
1873
1972
  font-family: var(--bp-sans);
1874
1973
  font-weight: var(--bp-weight-strong);
@@ -1878,6 +1977,16 @@
1878
1977
  align-items: baseline;
1879
1978
  gap: var(--bp-space-2);
1880
1979
  }
1980
+ :where(.bp-preflight__prompt::before) {
1981
+ content: counter(bp-preflight-q, decimal-leading-zero);
1982
+ font-family: var(--bp-mono);
1983
+ font-size: var(--bp-label-lg);
1984
+ letter-spacing: 0.08em;
1985
+ color: var(--bp-ink-soft);
1986
+ }
1987
+ :where(.bp-preflight__q:has(input:checked)) :where(.bp-preflight__prompt::before) {
1988
+ color: var(--bp-ink);
1989
+ }
1881
1990
  :where(.bp-preflight__kind) {
1882
1991
  font-family: var(--bp-mono);
1883
1992
  font-size: var(--bp-label-sm);
@@ -1887,11 +1996,11 @@
1887
1996
  font-weight: var(--bp-weight-body);
1888
1997
  }
1889
1998
  :where(.bp-preflight__resolved) {
1890
- display: none;
1999
+ visibility: hidden;
1891
2000
  margin-left: auto;
1892
2001
  }
1893
2002
  :where(.bp-preflight__q:has(input:checked)) :where(.bp-preflight__resolved) {
1894
- display: inline-flex;
2003
+ visibility: visible;
1895
2004
  }
1896
2005
  :where(.bp-preflight__chips) {
1897
2006
  display: flex;
@@ -1904,23 +2013,31 @@
1904
2013
  align-items: center;
1905
2014
  gap: 7px;
1906
2015
  cursor: pointer;
1907
- border: 1px solid var(--bp-ink-faint);
1908
- border-radius: var(--bp-radius-0);
2016
+ border: 1px solid var(--bp-button-secondary-bd);
2017
+ border-radius: var(--bp-radius-pill);
1909
2018
  padding: 5px 12px;
1910
2019
  font-size: var(--bp-text-small);
1911
- background: var(--bp-paper);
2020
+ color: var(--bp-button-secondary-fg);
2021
+ background: oklch(0 0 0 / 0);
2022
+ transition: background var(--bp-duration-fast) var(--bp-ease-out),
2023
+ color var(--bp-duration-fast) var(--bp-ease-out),
2024
+ border-color var(--bp-duration-fast) var(--bp-ease-out);
1912
2025
  }
1913
2026
  :where(.bp-preflight__chip:hover) {
1914
- border-color: var(--bp-ink-line);
2027
+ border-color: var(--bp-button-secondary-bd);
2028
+ background: var(--bp-button-tertiary-bg-hover);
1915
2029
  }
1916
2030
  :where(.bp-preflight__chip) :where(input) {
1917
2031
  accent-color: var(--bp-ink);
1918
2032
  margin: 0;
1919
2033
  }
1920
2034
  :where(.bp-preflight__chip:has(input:checked)) {
1921
- background: var(--bp-ink);
1922
- color: var(--bp-paper);
1923
- border-color: var(--bp-ink);
2035
+ background: var(--bp-button-primary-bg);
2036
+ color: var(--bp-button-primary-fg);
2037
+ border-color: var(--bp-button-primary-bg);
2038
+ }
2039
+ :where(.bp-preflight__chip:has(input:checked)) :where(input) {
2040
+ accent-color: var(--bp-button-primary-fg);
1924
2041
  }
1925
2042
  :where(.bp-preflight__gate) {
1926
2043
  margin: var(--bp-space-3);
@@ -1949,17 +2066,22 @@
1949
2066
  display: inline-flex;
1950
2067
  align-items: center;
1951
2068
  gap: 8px;
1952
- border: 1px solid var(--bp-ink);
1953
- border-radius: var(--bp-radius-0);
2069
+ border: 1px solid var(--bp-button-primary-bg);
2070
+ border-radius: var(--bp-radius-pill);
1954
2071
  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);
2072
+ font-family: var(--bp-sans);
2073
+ font-size: var(--bp-text-small);
2074
+ line-height: var(--bp-lh-control);
2075
+ background: var(--bp-button-primary-bg);
2076
+ color: var(--bp-button-primary-fg);
1961
2077
  cursor: pointer;
1962
2078
  margin-top: var(--bp-space-2);
2079
+ transition: background var(--bp-duration-fast) var(--bp-ease-out),
2080
+ border-color var(--bp-duration-fast) var(--bp-ease-out);
2081
+ }
2082
+ :where(.bp-preflight__draft:hover) {
2083
+ background: var(--bp-button-primary-bg-hover);
2084
+ border-color: var(--bp-button-primary-bg-hover);
1963
2085
  }
1964
2086
  :where(.bp-preflight__footer) {
1965
2087
  display: flex;
@@ -1972,17 +2094,45 @@
1972
2094
  :where(.bp-choice-record) {
1973
2095
  margin: var(--bp-space-4) 0;
1974
2096
  }
1975
- :where(.bp-choice-record__row) {
2097
+ /* One Obvious container card per row: the host carries border and paper
2098
+ fill; radius + overflow come from the [data-bp-rendered] mat rule above. */
2099
+ :where(bp-choice-record-row) {
2100
+ display: block;
1976
2101
  border: 1px solid var(--bp-edge);
1977
- border-radius: var(--bp-radius-0);
2102
+ background-color: var(--bp-paper);
2103
+ }
2104
+ :where(bp-choice-record-row + bp-choice-record-row) {
2105
+ margin-top: var(--bp-space-2);
2106
+ }
2107
+ :where(.bp-choice-record__row) {
2108
+ margin: 0;
2109
+ border: 0;
2110
+ border-radius: 0;
2111
+ background-color: oklch(0 0 0 / 0);
1978
2112
  padding: var(--bp-space-2) var(--bp-space-3);
1979
2113
  display: grid;
1980
2114
  grid-template-columns: minmax(8rem, 14rem) 1fr auto;
1981
2115
  gap: var(--bp-space-2) var(--bp-space-3);
1982
2116
  align-items: baseline;
1983
2117
  }
1984
- :where(.bp-choice-record__row + .bp-choice-record__row) {
1985
- margin-top: var(--bp-space-2);
2118
+ :where(bp-choice-record-row:has(.bp-choice__compare)) :where(.bp-choice-record__row) {
2119
+ border-bottom: 1px solid var(--bp-edge);
2120
+ }
2121
+ /* Compare sits flush in the card: drop the base compare top-margin and the
2122
+ standalone compare card chrome (border / radius) used in gallery layout.
2123
+ Wins over the base :where(.bp-choice__compare) rule by source order — no
2124
+ !important, since the record row no longer carries a JS inline margin. */
2125
+ :where(bp-choice-record-row) :where(.bp-choice__compare) {
2126
+ margin: 0;
2127
+ border: 0;
2128
+ border-radius: 0;
2129
+ padding: 0;
2130
+ }
2131
+ :where(bp-choice-record-row) :where(.bp-choice__compare > summary) {
2132
+ background-color: var(--bp-fill-amb);
2133
+ }
2134
+ :where(bp-choice-record-row) :where(.bp-choice__compare > summary:hover) {
2135
+ background-color: var(--bp-fill-hi);
1986
2136
  }
1987
2137
  :where(.bp-choice-record__row) :where(dt) {
1988
2138
  font-family: var(--bp-mono);
@@ -4058,6 +4208,119 @@
4058
4208
  }
4059
4209
  }
4060
4210
 
4211
+ /* =====================================================================
4212
+ @layer components — drafting board: interactive surfaces sit on a dot
4213
+ mat. Last in the layer so its surface overrides win by source order.
4214
+ ===================================================================== */
4215
+ @layer components {
4216
+ /* The interactive component family (choice, preflight, choice-record,
4217
+ source, mock, gallery) gets a dot-grid backdrop + a padding "mat", so a
4218
+ live/interactive block reads as a working surface distinct from settled
4219
+ prose — a second axis beside the Obvious pill vocabulary, one that marks
4220
+ even the non-decision interactive surfaces. The host is the mat (board
4221
+ shows in its padding and any internal gaps); the rendered root inside is
4222
+ the solid card. */
4223
+ :where(bp-choice, bp-preflight, bp-choice-record, bp-source, bp-mock, bp-gallery) {
4224
+ display: block;
4225
+ margin: var(--bp-space-4) 0;
4226
+ padding: var(--bp-space-5);
4227
+ background-color: var(--bp-board-bg);
4228
+ background-image: var(--bp-board);
4229
+ background-size: var(--bp-board-gap) var(--bp-board-gap);
4230
+ }
4231
+ /* Once upgraded, every dotted-mat host rounds to the Obvious container
4232
+ radius. data-bp-rendered is the uniform runtime hook — all interactive
4233
+ hosts set it on connect. bp-rationale also carries the flag but is not
4234
+ a mat host and is excluded from this selector list. */
4235
+ :where(
4236
+ bp-choice,
4237
+ bp-preflight,
4238
+ bp-choice-record,
4239
+ bp-choice-record-row,
4240
+ bp-source,
4241
+ bp-mock,
4242
+ bp-gallery
4243
+ )[data-bp-rendered="1"] {
4244
+ border-radius: var(--bp-radius-8);
4245
+ overflow: hidden;
4246
+ }
4247
+ /* The rendered root owns no outer margin now — the host carries document
4248
+ rhythm — so the mat reads even on all four sides. */
4249
+ :where(.bp-choice, .bp-choice-record, .bp-source, .bp-mock, .bp-gallery),
4250
+ :where(bp-preflight) :where(.bp-preflight) {
4251
+ margin: 0;
4252
+ }
4253
+ /* Expanded mock floats fullscreen (position: fixed): collapse the host so
4254
+ no empty dotted mat is left behind in the document flow. */
4255
+ :where(bp-mock:has(.bp-mock.is-expanded)) {
4256
+ margin: 0;
4257
+ padding: 0;
4258
+ }
4259
+
4260
+ /* Single-surface specimens read as ONE continuous paper card on the mat;
4261
+ give each an opaque paper fill (source inherits a translucent
4262
+ --bp-fill-amb from <details>, which would let the dots bleed through). */
4263
+ :where(.bp-choice--tabs, .bp-preflight, .bp-source, .bp-gallery) {
4264
+ background-color: var(--bp-paper);
4265
+ }
4266
+
4267
+ /* Multi-card layouts (stack / gallery / resolved / record) stay transparent
4268
+ so the host board shows in their gaps; each card carries its own paper.
4269
+ Chosen / resolved cards swap paper for a translucent fill — restore an
4270
+ opaque paper base so the board can't bleed through the card. */
4271
+ :where(.bp-choice__card:has(.bp-choice__pick:checked)),
4272
+ :where(.bp-choice__card[data-resolved]) {
4273
+ background-color: var(--bp-paper);
4274
+ background-image: linear-gradient(var(--bp-fill-amb), var(--bp-fill-amb));
4275
+ }
4276
+ /* Rejected / set-aside cards use opacity, so the whole card (incl. its
4277
+ hatch) goes translucent and the board bleeds through. Keep the set-aside
4278
+ read via hatch + tag, not element opacity. */
4279
+ :where(
4280
+ .bp-choice--stack:has(.bp-choice__pick:checked)
4281
+ .bp-choice__card:not(:has(.bp-choice__pick:checked)),
4282
+ .bp-choice--resolved .bp-choice__card:not([data-resolved])
4283
+ ) {
4284
+ opacity: 1;
4285
+ background-color: var(--bp-paper);
4286
+ background-image: var(--bp-hatch);
4287
+ }
4288
+ :where(
4289
+ .bp-choice--gallery:has(.bp-choice__pick:checked)
4290
+ .bp-choice__mock:not(:has(.bp-choice__pick:checked))
4291
+ ) {
4292
+ opacity: 1;
4293
+ }
4294
+ :where(
4295
+ .bp-choice--gallery:has(.bp-choice__pick:checked)
4296
+ .bp-choice__mock:not(:has(.bp-choice__pick:checked))
4297
+ .bp-choice__mock-frame
4298
+ ) {
4299
+ background-color: var(--bp-paper);
4300
+ background-image: var(--bp-hatch);
4301
+ }
4302
+
4303
+ /* The compare disclosure sits between transparent siblings (gallery /
4304
+ record); make it an opaque paper card so the dots stop at its edge. */
4305
+ :where(.bp-choice__compare) {
4306
+ background-color: var(--bp-paper);
4307
+ }
4308
+
4309
+ /* Action footers (hint + reset): machine-voice mono directly on the dots is
4310
+ hard to read. A paper strip with a top hairline keeps the gap above as
4311
+ board; horizontal padding matches the content inset above. Tabs already
4312
+ sit on their own paper panel, so skip them. */
4313
+ :where(.bp-choice--stack, .bp-choice--gallery, .bp-choice--resolved) :where(.bp-choice__actions),
4314
+ :where(.bp-preflight__footer) {
4315
+ background-color: var(--bp-paper);
4316
+ padding: var(--bp-board-footer-pad-y) var(--bp-board-footer-pad-x);
4317
+ border-top: var(--bp-stroke) solid var(--bp-edge);
4318
+ }
4319
+ :where(.bp-choice--stack, .bp-choice--gallery, .bp-choice--resolved) :where(.bp-choice__actions) {
4320
+ --bp-board-footer-pad-x: var(--bp-space-3);
4321
+ }
4322
+ }
4323
+
4061
4324
  /* =====================================================================
4062
4325
  @layer utilities — last-resort single-purpose helpers + responsive
4063
4326
  ===================================================================== */
@@ -4429,7 +4692,7 @@ body > .site-nav .site-nav__theme:hover {
4429
4692
  read as one control family (border, radius, surface + hover/focus states). */
4430
4693
  .sidebar-field {
4431
4694
  border: 1px solid var(--bp-edge);
4432
- border-radius: var(--bp-radius-6, 6px);
4695
+ border-radius: var(--bp-radius-pill);
4433
4696
  background: var(--bp-paper);
4434
4697
  /* Hover-only color motion — idle fields snap on theme flip so the root
4435
4698
  view-transition crossfade isn't fighting per-field token tweens. */
@@ -5018,7 +5281,7 @@ body > .site-nav .site-nav__theme:hover {
5018
5281
  height: var(--bp-theme-toggle-height);
5019
5282
  padding: 2px;
5020
5283
  border: 1px solid var(--bp-edge);
5021
- border-radius: var(--bp-radius-6, 6px);
5284
+ border-radius: var(--bp-radius-pill);
5022
5285
  background: var(--bp-fill-amb);
5023
5286
  color: var(--bp-text-secondary);
5024
5287
  cursor: pointer;
@@ -5044,7 +5307,7 @@ body > .site-nav .site-nav__theme:hover {
5044
5307
  width: calc(var(--bp-theme-toggle-height) - 6px);
5045
5308
  height: calc(var(--bp-theme-toggle-height) - 6px);
5046
5309
  border: 1px solid var(--bp-ink-line);
5047
- border-radius: var(--bp-radius-4, 4px);
5310
+ border-radius: var(--bp-radius-round);
5048
5311
  background: var(--bp-paper);
5049
5312
  box-shadow: 0 1px 2px oklch(0 0 0 / 0.06);
5050
5313
  pointer-events: none;
@@ -5092,7 +5355,7 @@ body > .site-nav .site-nav__theme:hover {
5092
5355
  height: var(--bp-sidebar-toggle-size);
5093
5356
  padding: 0;
5094
5357
  border: 1px solid var(--bp-edge);
5095
- border-radius: var(--bp-radius-6, 6px);
5358
+ border-radius: var(--bp-radius-pill);
5096
5359
  background: var(--bp-fill-amb);
5097
5360
  color: var(--bp-text-secondary);
5098
5361
  cursor: pointer;
@@ -5227,7 +5490,7 @@ body > .site-nav .site-nav__theme:hover {
5227
5490
 
5228
5491
  /* Fade scrim above the footer: the TOC scrolls out beneath it (z below the
5229
5492
  footer controls), with a solid bottom band backing the footer. */
5230
- .bp-sidebar::after {
5493
+ #bp-contents-rail::after {
5231
5494
  content: "";
5232
5495
  position: fixed;
5233
5496
  left: 0;
@@ -5242,7 +5505,7 @@ body > .site-nav .site-nav__theme:hover {
5242
5505
  /* Radial scrim around the corner toggle: the TOC dissolves in a circle that
5243
5506
  radiates from the icon as entries scroll up beneath it. The box is large
5244
5507
  enough to hold the gradient's outer radius; transparent beyond it. */
5245
- .bp-sidebar::before {
5508
+ #bp-contents-rail::before {
5246
5509
  content: "";
5247
5510
  position: fixed;
5248
5511
  top: 0;
@@ -5407,7 +5670,7 @@ html[data-bp-document]:has(.bp-cite-pane.is-open) main {
5407
5670
  height: var(--bp-sidebar-toggle-size);
5408
5671
  padding: 0;
5409
5672
  border: var(--bp-stroke) solid var(--bp-edge);
5410
- border-radius: var(--bp-radius-6);
5673
+ border-radius: var(--bp-radius-pill);
5411
5674
  background: var(--bp-fill-amb);
5412
5675
  color: var(--bp-text-secondary);
5413
5676
  cursor: pointer;