@obvi/blueprint 1.0.9

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.
@@ -0,0 +1,2689 @@
1
+ /* =====================================================================
2
+ blueprint.css — a classless-first CSS design system for blueprints
3
+ ---------------------------------------------------------------------
4
+ Drop this one file onto plain, semantic HTML and it renders as a
5
+ polished technical "blueprint" document. On top of the classless base
6
+ sits a small layer of composition components for the patterns authors
7
+ keep hand-rolling (decision panels, typed callouts, option grids…).
8
+
9
+ THE SPECIFICITY CONTRACT (why this architecture exists)
10
+ ---------------------------------------------------------------------
11
+ The prior base sheet styled base elements with REAL specificity
12
+ (e.g. `.prose p` = (0,1,1)) while component primitives used
13
+ `:where()` (specificity 0). A bare single-class component like
14
+ `.lede` (0,1,0) therefore LOST the cascade to `.prose p` and silently
15
+ collapsed to flat 14px body type — a failure only computed-style
16
+ measurement could catch.
17
+
18
+ This file makes that class of bug impossible, two independent ways:
19
+
20
+ 1. CASCADE LAYERS. Everything ships inside `@layer`s declared in a
21
+ fixed order: reset < tokens < base < components < utilities.
22
+ Later layers win over earlier layers REGARDLESS of selector
23
+ specificity. A component rule in `components` always beats a base
24
+ element rule in `base`, even `base td` vs `.bp-table`.
25
+
26
+ 2. ZERO SPECIFICITY. Every selector in every layer is wrapped in
27
+ `:where()`, so each rule has specificity (0,0,0). Unlayered
28
+ author CSS — anything you write in your own page — beats ALL
29
+ layered rules automatically. No parent-scoping, no `!important`,
30
+ no specificity gymnastics. You write `.lede {…}` and you win.
31
+
32
+ Layer order is declared ONCE, up front, so it holds no matter where
33
+ the rules physically appear below.
34
+ ===================================================================== */
35
+
36
+ @layer reset, tokens, base, components, utilities;
37
+
38
+ /* Geist Mono (the monospace label / code face) from Google Fonts. The sans
39
+ identity is Booton, loaded via the @font-face blocks below from the
40
+ Obvious CDN. @import must sit here — after the @layer declaration, before
41
+ any other rule — to be valid. */
42
+ @import url('https://fonts.googleapis.com/css2?family=Geist+Mono:ital,wght@0,100..900;1,100..900&display=swap');
43
+
44
+ /* ---------------------------------------------------------------------
45
+ @layer reset — box model + margin normalization
46
+ ------------------------------------------------------------------- */
47
+ @layer reset {
48
+ :where(*, *::before, *::after) {
49
+ box-sizing: border-box;
50
+ }
51
+ :where(body, h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, ol, ul) {
52
+ margin: 0;
53
+ }
54
+ :where(ul, ol) {
55
+ padding: 0;
56
+ }
57
+ :where(html) {
58
+ -webkit-text-size-adjust: 100%;
59
+ text-size-adjust: 100%;
60
+ }
61
+ :where(img, svg) {
62
+ max-width: 100%;
63
+ }
64
+ @media (prefers-reduced-motion: reduce) {
65
+ :where(*, *::before, *::after) {
66
+ scroll-behavior: auto !important;
67
+ animation-duration: 0.01ms !important;
68
+ animation-iteration-count: 1 !important;
69
+ transition-duration: 0.01ms !important;
70
+ }
71
+ }
72
+ }
73
+
74
+
75
+ /* ---------------------------------------------------------------------
76
+ @layer tokens — color ramp, type scale, spacing rhythm, fonts
77
+ ---------------------------------------------------------------------
78
+ COLOR DOCTRINE (Obvious): the document is monochrome ink-on-paper.
79
+ Text, rules, labels, borders, and chrome all draw from the neutral
80
+ Obvious scale (opacity-based black/white). Blue is reserved for ONE
81
+ thing — illustrations: the SVG node/edge marks and diagram line work.
82
+ So a reader's eye reads structure in ink and only the drawings carry
83
+ the drafting-blue identity.
84
+
85
+ Type identity is Booton (sans, via @font-face below) with Geist Mono
86
+ for labels and code. Light/dark resolve through [data-obvious-theme].
87
+ ------------------------------------------------------------------- */
88
+ @layer tokens {
89
+ :where(:root) {
90
+ /* ---- Absolute primitives ---------------------------------------- */
91
+ --bp-white: oklch(1 0 0);
92
+ --bp-black: oklch(0 0 0);
93
+
94
+ /* ---- Blueprint-blue primitive ramp (brand hue ≈ 264–275°, OKLCH)
95
+ The raw drafting-blue palette. Now reserved for ILLUSTRATIONS only
96
+ (SVG marks / diagram line work) via the --bp-illustration-* tokens
97
+ below. Text and chrome never reach for these — they use the neutral
98
+ ink scale. */
99
+ --bp-blue-50: oklch(0.9705 0.0054 274.97);
100
+ --bp-blue-100: oklch(0.9422 0.0275 274.66);
101
+ --bp-blue-200: oklch(0.8376 0.0774 273.32);
102
+ --bp-blue-300: oklch(0.7612 0.1218 272.4);
103
+ --bp-blue-400: oklch(0.6379 0.1899 269.89);
104
+ --bp-blue-500: oklch(0.5766 0.2443 267.62);
105
+ --bp-blue-600: oklch(0.5058 0.2886 264.84);
106
+ --bp-blue-700: oklch(0.4765 0.21 264.11); /* sRGB-safe; C capped — P3-only at 0.31 */
107
+ --bp-blue-800: oklch(0.41 0.24 264.5);
108
+ --bp-blue-900: oklch(0.36 0.19 265);
109
+ --bp-blue-950: oklch(0.29 0.13 267);
110
+
111
+ /* ---- Slate primitive ramp (low-chroma blueprint-blue neutrals) ---
112
+ Same hue family as --bp-blue, desaturated. For surfaces, fills and
113
+ faint edges that should read as near-neutral charcoal/paper rather
114
+ than vivid blue — the dark-theme surfaces map onto these. */
115
+ --bp-slate-50: oklch(0.985 0.01 264);
116
+ --bp-slate-100: oklch(0.967 0.02 264);
117
+ --bp-slate-200: oklch(0.928 0.038 264);
118
+ --bp-slate-300: oklch(0.868 0.055 264);
119
+ --bp-slate-400: oklch(0.7 0.11 264);
120
+ --bp-slate-500: oklch(0.555 0.14 264);
121
+ --bp-slate-600: oklch(0.43 0.145 264);
122
+ --bp-slate-700: oklch(0.372 0.125 264);
123
+ --bp-slate-800: oklch(0.305 0.095 264);
124
+ --bp-slate-900: oklch(0.25 0.055 264);
125
+ --bp-slate-950: oklch(0.18 0.038 264);
126
+
127
+ /* ---- Gray primitive ramp (pure neutral, chroma 0) ---------------
128
+ The document neutral. Backs page paper/bg and body text so the
129
+ large surfaces read as true charcoal/white — independent of the
130
+ blue accent and slate panel tints. */
131
+ --bp-gray-50: oklch(0.985 0 0);
132
+ --bp-gray-100: oklch(0.97 0 0);
133
+ --bp-gray-200: oklch(0.922 0 0);
134
+ --bp-gray-300: oklch(0.87 0 0);
135
+ --bp-gray-400: oklch(0.708 0 0);
136
+ --bp-gray-500: oklch(0.556 0 0);
137
+ --bp-gray-600: oklch(0.439 0 0);
138
+ --bp-gray-700: oklch(0.371 0 0);
139
+ --bp-gray-800: oklch(0.269 0 0);
140
+ --bp-gray-900: oklch(0.227 0 0);
141
+ --bp-gray-950: oklch(0.205 0 0);
142
+
143
+ /* Neutral ink scale (light) — the monochrome workhorse for text,
144
+ rules, labels, key borders, and the fills behind inverted labels.
145
+ Opacity-based black, matching the Obvious darken scale, so it
146
+ composites cleanly over any paper. --bp-ink also doubles as a fill
147
+ behind --bp-paper-colored labels, so it stays near-opaque-dark.
148
+ --bp-ink text-primary (Obvious darken-1400, 92% black)
149
+ --bp-ink-soft text-tertiary (Obvious darken-900, 40% black)
150
+ --bp-ink-faint border-strong (Obvious darken-600, 16% black) */
151
+ --bp-ink: var(--obvious-text-primary, oklch(0 0 0 / 0.92));
152
+ --bp-ink-soft: oklch(0 0 0 / 0.4);
153
+ --bp-ink-faint: oklch(0 0 0 / 0.16);
154
+ /* Neutral selection wash — keeps --bp-text at AAA contrast on top. */
155
+ --bp-highlight: oklch(0 0 0 / 0.1);
156
+
157
+ /* Illustration accent — blue for diagram line work and citation
158
+ markers (footnotes, figure numbers, <bp-cite> </> icon). Honors a
159
+ host accent override (e.g. Obvious #29a2ff) when embedded. */
160
+ --bp-illustration: var(--obvious-accent-default, var(--bp-blue-700));
161
+ --bp-illustration-soft: var(--bp-blue-400);
162
+ --bp-illustration-faint: var(--bp-blue-200);
163
+ --bp-illustration-fill: oklch(0.53 0.28 264 / 0.08);
164
+
165
+ /* Neutrals — true-neutral --bp-gray ramp + pure white for the page
166
+ surfaces and body text (the doc reads neutral, not tinted).
167
+ Hairlines stay translucent black/white alpha literals so fallbacks
168
+ work without relative-color support. */
169
+ --bp-paper: var(--obvious-bg-base, var(--bp-white));
170
+ --bp-bg: var(--obvious-bg-subtle, var(--bp-gray-100)); /* soft #f5f5f5 page, white paper sits on top */
171
+ /* Panel fills as neutral black washes over --bp-paper (Obvious
172
+ surface-subtle / surface-active opacities). -amb is the ambient
173
+ surface, -hi the stronger highlight. Self-adjusts to any paper. */
174
+ --bp-fill-amb: oklch(0 0 0 / 0.025);
175
+ --bp-fill-hi: oklch(0 0 0 / 0.055);
176
+ /* Diagonal hatch — drafting "section lining" for fenced / held /
177
+ out-of-bounds regions. Faint ink lines over whatever fill the host
178
+ element already has, so it reads as texture, not a second color.
179
+ Theme-aware for free: it composes --bp-ink-faint, which flips in
180
+ dark. --bp-hatch-gap controls the line pitch. */
181
+ --bp-hatch-gap: 7px;
182
+ --bp-hatch: repeating-linear-gradient(
183
+ -45deg,
184
+ var(--bp-ink-faint) 0 var(--bp-stroke),
185
+ transparent var(--bp-stroke) var(--bp-hatch-gap)
186
+ );
187
+ --bp-edge: var(--obvious-border-subtle, oklch(0 0 0 / 0.08));
188
+ --bp-ink-line: var(--obvious-border-default, oklch(0 0 0 / 0.16));
189
+ --bp-text: var(--obvious-text-primary, oklch(0 0 0 / 0.92));
190
+ --bp-text-secondary: var(--obvious-text-secondary, oklch(0 0 0 / 0.64));
191
+ --bp-positive: oklch(0.52 0.15 150); /* darkened for AA on paper (5.1:1) */
192
+ --bp-positive-faint: oklch(0.9 0.06 150);
193
+
194
+ /* Font stacks — Booton is the Obvious identity (loaded via @font-face
195
+ below). Falls back through the system sans so text stays legible if
196
+ the CDN font is unavailable. */
197
+ --bp-sans: var(--obvious-font-sans, "Booton", system-ui, -apple-system, "Segoe UI", sans-serif);
198
+ --bp-serif: var(--bp-sans); /* intentional alias: one type identity throughout */
199
+ --bp-mono: var(
200
+ --obvious-font-mono,
201
+ "Geist Mono",
202
+ ui-monospace,
203
+ "SF Mono",
204
+ SFMono-Regular,
205
+ Menlo,
206
+ Consolas,
207
+ monospace
208
+ );
209
+
210
+ /* Named type scale (compact technical-document hierarchy), tuned to
211
+ the Obvious document scale and Booton's metrics. Each step pairs a
212
+ size with its intended line-height; tracking lives with each rule. */
213
+ --bp-text-title: 24px; --bp-lh-title: 32px; /* h1 / masthead */
214
+ --bp-text-h2: 20px; --bp-lh-h2: 28px;
215
+ --bp-text-h3: 18px; --bp-lh-h3: 26px;
216
+ --bp-text-h4: 16px; --bp-lh-h4: 24px;
217
+ --bp-text-lede: 16px; --bp-lh-lede: 26px; /* narrative lede */
218
+ --bp-text-body: 14px; --bp-lh-body: 20px; /* body copy */
219
+ --bp-text-small: 13px; --bp-lh-small: 18px; /* small / captions */
220
+ --bp-text-code: 12.5px; --bp-lh-code: 1.6; /* mono, harmonized to body */
221
+
222
+ /* 3-step monospace-label scale, ONE letter-spacing each */
223
+ --bp-label-sm: 9px; --bp-label-sm-ls: 0.08em;
224
+ --bp-label-md: 10px; --bp-label-md-ls: 0.1em;
225
+ --bp-label-lg: 11px; --bp-label-lg-ls: 0.14em;
226
+
227
+ /* 8px spacing rhythm */
228
+ --bp-space-1: 4px;
229
+ --bp-space-2: 8px;
230
+ --bp-space-3: 16px;
231
+ --bp-space-4: 24px;
232
+ --bp-space-5: 32px;
233
+ --bp-space-6: 48px;
234
+ --bp-space-7: 64px;
235
+
236
+ /* Single stroke-width token for SVG marks (1px hairline default) */
237
+ --bp-stroke: 1px;
238
+
239
+ /* Elevation — the two drop shadows the chrome lifts surfaces with: a
240
+ two-layer (ambient + key) recipe per rung. -sheet floats the document
241
+ off the desk; -pop raises transient overlays (popovers, menus) higher.
242
+ Black-alpha only, so the same values read on light paper and the dark
243
+ desk alike — theme-independent, hence no dark-mode override. */
244
+ --bp-shadow-sheet:
245
+ 0 1px 2px oklch(0 0 0 / 0.05),
246
+ 0 20px 48px -18px oklch(0 0 0 / 0.22);
247
+ --bp-shadow-pop:
248
+ 0 1px 2px oklch(0 0 0 / 0.06),
249
+ 0 14px 32px -10px oklch(0 0 0 / 0.32);
250
+
251
+ /* Corner radius — an even 2px-step scale; each token names its pixel
252
+ value directly (corners read as absolute, so px is the honest unit).
253
+ -6 is the document default for sheets, panels, and chrome; -2 dresses
254
+ inline marks, -4 small code blocks, -8 is the soft top rung. */
255
+ --bp-radius-0: 0;
256
+ --bp-radius-2: 2px;
257
+ --bp-radius-4: 4px;
258
+ --bp-radius-6: 6px;
259
+ --bp-radius-8: 8px;
260
+ --bp-radius-pill: 9999px;
261
+ --bp-radius-round: 50%;
262
+
263
+ /* Section-eyebrow marker — the square that leads the "01 / 02" counter.
264
+ Size is the block edge; gap is the space between it and the number. */
265
+ --bp-marker-size: 10px;
266
+ --bp-marker-gap: var(--bp-space-2);
267
+
268
+ /* Layout measures */
269
+ --bp-content: 980px;
270
+ --bp-wide: 1120px;
271
+ --bp-sidebar-track: 300px;
272
+ --bp-sidebar: calc(var(--bp-sidebar-track) + var(--bp-space-4));
273
+
274
+ /* Legacy token aliases for stored Blueprints. New documents use --bp-*. */
275
+ --ink: var(--bp-ink);
276
+ --ink-soft: var(--bp-ink-soft);
277
+ --ink-faint: var(--bp-ink-faint);
278
+ --fill-hi: var(--bp-fill-hi);
279
+ --fill-amb: var(--bp-fill-amb);
280
+ --paper: var(--bp-paper);
281
+ --bg: var(--bp-bg);
282
+ --edge: var(--bp-edge);
283
+ --ink-line: var(--bp-ink-line);
284
+ --text: var(--bp-text);
285
+ --text-secondary: var(--bp-text-secondary);
286
+ --serif: var(--bp-serif);
287
+ --mono: var(--bp-mono);
288
+ --sans: var(--bp-sans);
289
+ --content: var(--bp-content);
290
+ --wide: var(--bp-wide);
291
+ --sidebar: var(--bp-sidebar);
292
+
293
+ /* Tunables */
294
+ /* Three-rung weight scale tuned for Booton (variable 100–900), aligned
295
+ with Obvious: body = regular copy, medium = labels/eyebrows/b,
296
+ strong = headings + emphasis (Obvious sets bold ≈ 580, headings 600). */
297
+ --bp-weight-body: 460;
298
+ --bp-weight-medium: 520;
299
+ --bp-weight-strong: 600;
300
+ --bp-tracking-body: -0.21px;
301
+
302
+ /* Text-rendering primitives. Keyword aliases so wrap/figure intent is
303
+ declared once and reused — change the strategy here, not per rule.
304
+ Theme-independent (rendering, not color), so no dark-mode override. */
305
+ --bp-wrap-heading: balance; /* even line lengths on short headings */
306
+ --bp-wrap-body: pretty; /* no orphan on the last line of body/short text */
307
+ --bp-numeric-tabular: tabular-nums; /* equal-width digits for aligned/updating numbers */
308
+
309
+ /* Motion — the single source of truth for transition timing. Pick an
310
+ intent, never a raw number. Durations name how often a user sees the
311
+ motion (HPM ~100ms perceptual cycle; UI stays under 300ms); easings name
312
+ the curve's job. Theme-independent (timing, not color), so no dark-mode
313
+ override. The prefers-reduced-motion reset in @layer reset neutralizes
314
+ every transition built from these for free.
315
+ instant press feedback, high-frequency taps
316
+ fast tooltips, hover reveals, color shifts (the default)
317
+ normal dropdowns, default UI
318
+ slow modals, drawers, large overlays
319
+ deliberate rare/first-time only (onboarding) — breaks the <300ms rule */
320
+ --bp-duration-instant: 100ms;
321
+ --bp-duration-fast: 150ms;
322
+ --bp-duration-normal: 200ms;
323
+ --bp-duration-slow: 300ms;
324
+ --bp-duration-deliberate: 450ms;
325
+
326
+ /* Easing — strong curves only; never ease-in for UI (its slow start delays
327
+ feedback and feels sluggish).
328
+ ease hover / color (gentle browser default)
329
+ out enter / exit — the workhorse (the default)
330
+ in-out on-screen movement / morph
331
+ standard neutral Material curve
332
+ overshoot playful bounce — use sparingly
333
+ linear constant motion only (progress, marquees) */
334
+ --bp-ease: ease;
335
+ --bp-ease-out: cubic-bezier(0.23, 1, 0.32, 1);
336
+ --bp-ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
337
+ --bp-ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
338
+ --bp-ease-overshoot: cubic-bezier(0.34, 1.56, 0.64, 1);
339
+ --bp-ease-linear: linear;
340
+ }
341
+
342
+ /* Dark mode — neutral --bp-gray page surfaces; the ink scale flips to
343
+ white-alpha (Obvious's lighten scale) so text, rules, and label fills
344
+ stay correct on dark paper. Illustration blue brightens to a light
345
+ drafting tint so diagram line work reads on the near-black paper. */
346
+ :where([data-obvious-theme="dark"]) {
347
+ --bp-ink: var(--obvious-text-primary, oklch(1 0 0 / 0.92));
348
+ --bp-ink-soft: oklch(1 0 0 / 0.4);
349
+ --bp-ink-faint: oklch(1 0 0 / 0.16);
350
+ --bp-highlight: oklch(1 0 0 / 0.14);
351
+
352
+ /* Illustration accent (dark) — brighter blue holds on dark paper. */
353
+ --bp-illustration: var(--obvious-accent-default, var(--bp-blue-300));
354
+ --bp-illustration-soft: var(--bp-blue-500);
355
+ --bp-illustration-faint: var(--bp-slate-600);
356
+ --bp-illustration-fill: var(--bp-blue-900);
357
+
358
+ /* Neutral white-alpha panel fills over the dark paper. */
359
+ --bp-fill-amb: oklch(1 0 0 / 0.04);
360
+ --bp-fill-hi: oklch(1 0 0 / 0.08);
361
+ --bp-paper: var(--bp-gray-950);
362
+ --bp-bg: oklch(0.15 0 0); /* the desk sits darker than the sheet, so the page lifts */
363
+ --bp-edge: oklch(1 0 0 / 0.08);
364
+ --bp-ink-line: oklch(1 0 0 / 0.16);
365
+ --bp-text: var(--bp-gray-100);
366
+ --bp-text-secondary: oklch(1 0 0 / 0.64);
367
+ --bp-positive: oklch(0.74 0.15 150);
368
+ --bp-positive-faint: oklch(0.34 0.07 150);
369
+ }
370
+ }
371
+
372
+
373
+ /* Booton variable font (Obvious identity). Outside layers — @font-face
374
+ is unaffected by the cascade.
375
+
376
+ FONT CDN DEPENDENCY — these @font-face rules load Booton from the
377
+ Obvious CDN (https://obvious.ai/fonts/). Online consumers (the
378
+ default) receive the font automatically.
379
+
380
+ If you need offline / self-hosted use, replace the src URLs with
381
+ paths to your local copies of BootonVF.woff2 and BootonItalicsVF.woff2,
382
+ e.g.:
383
+ src: url("./fonts/BootonVF.woff2") format("woff2");
384
+
385
+ If Booton is unavailable, the --bp-sans stack falls back to
386
+ system-ui / -apple-system / sans-serif, which keeps text fully
387
+ legible at all weights and sizes. */
388
+ @font-face {
389
+ font-family: "Booton";
390
+ src: url("https://obvious.ai/fonts/BootonVF.woff2") format("woff2");
391
+ font-weight: 100 900;
392
+ font-style: normal;
393
+ font-display: swap;
394
+ }
395
+ @font-face {
396
+ font-family: "Booton";
397
+ src: url("https://obvious.ai/fonts/BootonItalicsVF.woff2") format("woff2");
398
+ font-weight: 100 900;
399
+ font-style: italic;
400
+ font-display: swap;
401
+ }
402
+
403
+ /* ---------------------------------------------------------------------
404
+ @layer base — classless typographic foundation
405
+ Styles SEMANTIC HTML so raw, class-free markup renders as a finished
406
+ blueprint. Authors write meaning; design arrives for free. Ordered by
407
+ the companion spec's 5 tiers:
408
+ 1. structural + heading spine 4. definitions + inline technical
409
+ 2. citation + provenance 5. landmarks + disclosure chrome
410
+ 3. tables
411
+ Every selector is :where() (specificity 0) and lives in `base`, below
412
+ `components` — so any component class or author rule always wins.
413
+ ------------------------------------------------------------------- */
414
+ @layer base {
415
+ /* ---- Tier 1a: page + body ---------------------------------------- */
416
+ :where(html) {
417
+ font-size: 16px;
418
+ }
419
+ :where(body) {
420
+ font-family: var(--bp-serif);
421
+ color: var(--bp-text);
422
+ background: var(--bp-bg);
423
+ font-size: var(--bp-text-body);
424
+ line-height: var(--bp-lh-body);
425
+ font-weight: var(--bp-weight-body);
426
+ letter-spacing: var(--bp-tracking-body);
427
+ /* macOS renders text heavier than intended; thin it once at the root.
428
+ Inert on other platforms, so safe to apply universally. */
429
+ -webkit-font-smoothing: antialiased;
430
+ -moz-osx-font-smoothing: grayscale;
431
+ counter-reset: bp-sec bp-fig bp-fn;
432
+ }
433
+ /* Pseudo-elements can't live inside :where()/:is() — Chromium (incl. Dia)
434
+ and Firefox drop the whole rule. Keep these as plain selectors, and
435
+ split ::selection / ::-moz-selection so one invalid token doesn't
436
+ invalidate the other. */
437
+ ::selection {
438
+ background: var(--bp-highlight);
439
+ color: var(--bp-text);
440
+ }
441
+ ::-moz-selection {
442
+ background: var(--bp-highlight);
443
+ color: var(--bp-text);
444
+ }
445
+
446
+ /* ---- Tier 1b: landmarks set the prose measure -------------------- */
447
+ :where(main, article) {
448
+ display: block;
449
+ max-width: var(--bp-content);
450
+ margin-inline: auto;
451
+ padding: var(--bp-space-6);
452
+ }
453
+
454
+ /* Section rhythm + automatic "01 / 02" eyebrow counter before each h2.
455
+ The counter is keyed to <section>, so reordering renumbers for free. */
456
+ :where(section) {
457
+ counter-increment: bp-sec;
458
+ }
459
+ :where(section + section) {
460
+ margin-top: calc(var(--bp-space-7) + var(--bp-space-5));
461
+ }
462
+
463
+ /* ---- Tier 1c: headings (compact technical hierarchy) ------------- */
464
+ :where(h1) {
465
+ font-family: var(--bp-sans);
466
+ font-size: var(--bp-text-title);
467
+ font-weight: var(--bp-weight-strong);
468
+ letter-spacing: -0.48px;
469
+ line-height: var(--bp-lh-title);
470
+ margin-bottom: var(--bp-space-2);
471
+ text-wrap: var(--bp-wrap-heading);
472
+ }
473
+ :where(h2) {
474
+ font-family: var(--bp-sans);
475
+ font-size: var(--bp-text-h2);
476
+ font-weight: var(--bp-weight-strong);
477
+ letter-spacing: -0.3px;
478
+ line-height: var(--bp-lh-h2);
479
+ margin: var(--bp-space-4) 0 var(--bp-space-3);
480
+ scroll-margin-top: var(--bp-space-4);
481
+ text-wrap: var(--bp-wrap-heading);
482
+ }
483
+ /* Section-scoped eyebrow: "01", "02"… rendered above each section's h2.
484
+ A solid ink square leads the counter, set off by --bp-marker-gap. The
485
+ square is a left-anchored background so it stays a single pseudo-element
486
+ (no extra node) and centers vertically against the label line. */
487
+ :where(section > h2:first-child)::before,
488
+ :where(section > hgroup:first-child > h2)::before {
489
+ content: counter(bp-sec, decimal-leading-zero);
490
+ display: block;
491
+ width: max-content;
492
+ font-family: var(--bp-mono);
493
+ font-size: var(--bp-label-lg);
494
+ font-weight: var(--bp-weight-medium);
495
+ letter-spacing: 0.18em;
496
+ color: var(--bp-ink);
497
+ padding-left: calc(var(--bp-marker-size) + var(--bp-marker-gap));
498
+ background: linear-gradient(var(--bp-ink), var(--bp-ink)) left center / var(--bp-marker-size) var(--bp-marker-size) no-repeat;
499
+ margin-bottom: var(--bp-space-2);
500
+ }
501
+ :where(h3) {
502
+ font-family: var(--bp-sans);
503
+ font-size: var(--bp-text-h3);
504
+ font-weight: var(--bp-weight-strong);
505
+ letter-spacing: -0.27px;
506
+ line-height: var(--bp-lh-h3);
507
+ margin: var(--bp-space-4) 0 var(--bp-space-2);
508
+ scroll-margin-top: var(--bp-space-4);
509
+ text-wrap: var(--bp-wrap-heading);
510
+ }
511
+ :where(h4) {
512
+ font-family: var(--bp-sans);
513
+ font-size: var(--bp-text-h4);
514
+ font-weight: var(--bp-weight-strong);
515
+ letter-spacing: -0.24px;
516
+ line-height: var(--bp-lh-h4);
517
+ margin: var(--bp-space-3) 0 var(--bp-space-1);
518
+ text-wrap: var(--bp-wrap-heading);
519
+ }
520
+ :where(h5, h6) {
521
+ font-family: var(--bp-mono);
522
+ font-size: var(--bp-label-lg);
523
+ letter-spacing: var(--bp-label-lg-ls);
524
+ text-transform: uppercase;
525
+ color: var(--bp-text-secondary);
526
+ margin: var(--bp-space-3) 0 var(--bp-space-1);
527
+ }
528
+ /* hgroup binds a heading to its tagline */
529
+ :where(hgroup > * + *) {
530
+ margin-top: var(--bp-space-1);
531
+ }
532
+
533
+ /* ---- Tier 1d: body copy, lede-by-position, lists, links ---------- */
534
+ :where(p) {
535
+ margin-bottom: 1.2em;
536
+ font-size: var(--bp-text-body);
537
+ line-height: var(--bp-lh-body);
538
+ text-align: left; /* left by default; justify rarely reads well on-screen */
539
+ text-wrap: var(--bp-wrap-body);
540
+ }
541
+ /* Lede-by-position was removed — a paragraph following a heading should
542
+ render as ordinary body type. Use .bp-lede explicitly when you want
543
+ the narrative lead-in treatment. */
544
+ :where(ul, ol) {
545
+ margin: 0 0 1.2em;
546
+ padding-left: 1.3em;
547
+ }
548
+ :where(li) {
549
+ margin-bottom: var(--bp-space-1);
550
+ line-height: var(--bp-lh-body);
551
+ text-wrap: var(--bp-wrap-body);
552
+ }
553
+ :where(li > ul, li > ol) {
554
+ margin: var(--bp-space-1) 0 0;
555
+ }
556
+ :where(a) {
557
+ color: var(--bp-ink);
558
+ text-decoration: underline;
559
+ text-underline-offset: 2px;
560
+ text-decoration-thickness: 1px;
561
+ text-decoration-color: var(--bp-ink-faint);
562
+ }
563
+ :where(a:hover) {
564
+ text-decoration-color: var(--bp-ink);
565
+ }
566
+ :where(hr) {
567
+ border: 0;
568
+ margin: var(--bp-space-6) 0;
569
+ height: 0;
570
+ }
571
+ }
572
+
573
+
574
+
575
+ /* ---------------------------------------------------------------------
576
+ @layer base (cont.) — Tier 2: citation + provenance (first-class)
577
+ Provenance is native HTML, not a bolted-on class. Style all of it.
578
+ ------------------------------------------------------------------- */
579
+ @layer base {
580
+ /* Block quotation + its source URL (the cite ATTRIBUTE is machine-
581
+ readable provenance; we also surface it visibly via the footer). */
582
+ :where(blockquote) {
583
+ margin: var(--bp-space-4) 0;
584
+ padding: var(--bp-space-1) 0 var(--bp-space-1) var(--bp-space-3);
585
+ border-left: 2px solid var(--bp-ink);
586
+ color: var(--bp-text);
587
+ }
588
+ :where(blockquote p) {
589
+ font-size: var(--bp-text-lede);
590
+ line-height: var(--bp-lh-lede);
591
+ text-wrap: var(--bp-wrap-body);
592
+ }
593
+ :where(blockquote footer) {
594
+ margin-top: var(--bp-space-2);
595
+ font-family: var(--bp-sans);
596
+ font-size: var(--bp-text-small);
597
+ color: var(--bp-text-secondary);
598
+ /* reset the subordinate-footer landmark treatment inside a quote */
599
+ border-top: 0;
600
+ padding-top: 0;
601
+ }
602
+ /* <cite> = the TITLE of a cited work (not a person) */
603
+ :where(cite) {
604
+ font-style: italic;
605
+ color: var(--bp-ink);
606
+ }
607
+ /* Inline quotation with locale-aware quotation marks */
608
+ :where(q) {
609
+ quotes: "\201C" "\201D" "\2018" "\2019";
610
+ font-style: italic;
611
+ }
612
+ :where(q)::before { content: open-quote; }
613
+ :where(q)::after { content: close-quote; }
614
+
615
+ /* Captioned, self-contained evidence with an automatic figure number.
616
+ Diagram SVGs size to their viewBox width (--bp-diagram-w, set by
617
+ blueprint.js); the figure shrink-wraps and scales down on narrow screens. */
618
+ :where(figure) {
619
+ margin: var(--bp-space-5) auto;
620
+ width: fit-content;
621
+ max-width: 100%;
622
+ }
623
+ :where(figure svg[viewBox]) {
624
+ display: block;
625
+ width: auto;
626
+ max-width: min(100%, var(--bp-diagram-w));
627
+ height: auto;
628
+ }
629
+ :where(figure img) {
630
+ display: block;
631
+ width: auto;
632
+ max-width: 100%;
633
+ height: auto;
634
+ }
635
+ :where(figcaption) {
636
+ counter-increment: bp-fig;
637
+ margin-top: var(--bp-space-2);
638
+ font-family: var(--bp-mono);
639
+ font-size: var(--bp-label-md);
640
+ letter-spacing: var(--bp-label-md-ls);
641
+ text-transform: uppercase;
642
+ color: var(--bp-text-secondary);
643
+ text-align: center;
644
+ text-wrap: var(--bp-wrap-body);
645
+ }
646
+ :where(figcaption)::before {
647
+ content: "Fig. " counter(bp-fig, decimal-leading-zero) " \2014 ";
648
+ color: var(--bp-illustration);
649
+ }
650
+
651
+ /* Footnote reference marker: <sup><a href="#fn-n">n</a></sup> resolving
652
+ to an <ol> of sources in the document <footer>. */
653
+ :where(sup a) {
654
+ text-decoration: none;
655
+ color: var(--bp-illustration);
656
+ font-family: var(--bp-mono);
657
+ font-size: var(--bp-label-md);
658
+ padding: 0 1px;
659
+ }
660
+ :where(sup a:hover) {
661
+ text-decoration: underline;
662
+ }
663
+
664
+ /* Machine-readable date/time */
665
+ :where(time) {
666
+ color: inherit;
667
+ }
668
+ }
669
+
670
+ /* ---------------------------------------------------------------------
671
+ @layer base (cont.) — Tier 3: tables (semantic, row-header aware)
672
+ ------------------------------------------------------------------- */
673
+ @layer base {
674
+ :where(table) {
675
+ width: 100%;
676
+ border-collapse: collapse;
677
+ font-family: var(--bp-sans);
678
+ margin: var(--bp-space-3) 0 var(--bp-space-4);
679
+ }
680
+ :where(caption) {
681
+ caption-side: top;
682
+ text-align: left;
683
+ font-family: var(--bp-mono);
684
+ font-size: var(--bp-label-md);
685
+ letter-spacing: var(--bp-label-md-ls);
686
+ text-transform: uppercase;
687
+ color: var(--bp-text-secondary);
688
+ padding-bottom: var(--bp-space-2);
689
+ }
690
+ /* Column headers: monospace key-line in ink */
691
+ :where(th[scope="col"], thead th) {
692
+ font-family: var(--bp-mono);
693
+ font-size: var(--bp-label-md);
694
+ letter-spacing: var(--bp-label-md-ls);
695
+ text-transform: uppercase;
696
+ color: var(--bp-ink);
697
+ text-align: left;
698
+ padding: var(--bp-space-2) var(--bp-space-2);
699
+ border-bottom: 1px solid var(--bp-ink);
700
+ vertical-align: bottom;
701
+ }
702
+ :where(td) {
703
+ font-size: var(--bp-text-body);
704
+ padding: var(--bp-space-2) var(--bp-space-2);
705
+ border-bottom: 1px solid var(--bp-edge);
706
+ vertical-align: top;
707
+ line-height: 1.5;
708
+ /* equal-width digits keep numeric columns aligned (sans is proportional) */
709
+ font-variant-numeric: var(--bp-numeric-tabular);
710
+ }
711
+ /* LOAD-BEARING: row headers are the KEY COLUMN of the table — read as
712
+ labels (sans, natural case, semibold) with a vertical divider, NOT as
713
+ repeated column headers. `scope` styles the column AND tells assistive
714
+ tech which header governs which cell. */
715
+ :where(th[scope="row"]) {
716
+ font-family: var(--bp-sans);
717
+ font-size: var(--bp-text-body);
718
+ font-weight: var(--bp-weight-strong);
719
+ text-align: left;
720
+ text-transform: none;
721
+ letter-spacing: 0;
722
+ color: var(--bp-text);
723
+ padding: var(--bp-space-2) var(--bp-space-3) var(--bp-space-2) var(--bp-space-2);
724
+ border-bottom: 1px solid var(--bp-edge);
725
+ border-right: 1px solid var(--bp-ink-line);
726
+ vertical-align: top;
727
+ white-space: nowrap;
728
+ }
729
+ /* Summary row reads with a heavier top divider */
730
+ :where(tfoot th, tfoot td) {
731
+ border-top: 1px solid var(--bp-ink-line);
732
+ border-bottom: 0;
733
+ font-weight: var(--bp-weight-strong);
734
+ font-variant-numeric: var(--bp-numeric-tabular);
735
+ }
736
+ }
737
+
738
+
739
+ /* ---------------------------------------------------------------------
740
+ @layer base (cont.) — Tier 4: definitions + inline technical family
741
+ ------------------------------------------------------------------- */
742
+ @layer base {
743
+ /* Definition list: label-left / description-right rows. The
744
+ `.bp-deflist` component is a styled extension of this; a bare <dl>
745
+ already reads as a blueprint definition list. */
746
+ :where(dl) {
747
+ display: grid;
748
+ grid-template-columns: minmax(8rem, 14rem) 1fr;
749
+ gap: var(--bp-space-3) var(--bp-space-3);
750
+ margin: var(--bp-space-4) 0 var(--bp-space-5);
751
+ }
752
+ :where(dt) {
753
+ font-family: var(--bp-sans);
754
+ font-weight: var(--bp-weight-strong);
755
+ color: var(--bp-text);
756
+ padding: 0 var(--bp-space-3) 0 0;
757
+ }
758
+ :where(dd) {
759
+ margin: 0;
760
+ color: var(--bp-text-secondary);
761
+ padding: 0;
762
+ line-height: var(--bp-lh-body);
763
+ text-wrap: var(--bp-wrap-body);
764
+ }
765
+ /* Defining instance of a term */
766
+ :where(dfn) {
767
+ font-style: normal;
768
+ font-weight: var(--bp-weight-strong);
769
+ color: var(--bp-ink);
770
+ }
771
+ /* Abbreviation: discoverable dotted underline + hover expansion */
772
+ :where(abbr[title]) {
773
+ text-decoration: underline dotted;
774
+ text-underline-offset: 2px;
775
+ cursor: help;
776
+ }
777
+
778
+ /* Monospace technical family — differentiated so input/output/code/var
779
+ read distinctly with no class. */
780
+ :where(code, kbd, samp, var) {
781
+ font-family: var(--bp-mono);
782
+ font-size: var(--bp-text-code);
783
+ }
784
+ :where(code) {
785
+ background: var(--bp-fill-amb);
786
+ padding: 2px 5px;
787
+ color: var(--bp-ink);
788
+ border-radius: var(--bp-radius-2);
789
+ }
790
+ :where(pre) {
791
+ font-family: var(--bp-mono);
792
+ font-size: var(--bp-text-code);
793
+ background: var(--bp-fill-amb);
794
+ border: 1px solid var(--bp-edge);
795
+ border-left: 2px solid var(--bp-ink);
796
+ padding: var(--bp-space-3);
797
+ margin: var(--bp-space-3) 0;
798
+ overflow-x: auto;
799
+ line-height: var(--bp-lh-code);
800
+ color: var(--bp-text);
801
+ }
802
+ /* a <code> inside <pre> is a plain block; drop the inline chrome */
803
+ :where(pre code) {
804
+ background: none;
805
+ padding: 0;
806
+ color: inherit;
807
+ border-radius: var(--bp-radius-0);
808
+ }
809
+ :where(kbd) {
810
+ background: var(--bp-paper);
811
+ border: 1px solid var(--bp-ink-line);
812
+ border-bottom-width: 2px;
813
+ border-radius: var(--bp-radius-2);
814
+ padding: 1px 5px;
815
+ color: var(--bp-text);
816
+ }
817
+ :where(samp) {
818
+ color: var(--bp-text-secondary);
819
+ }
820
+ :where(var) {
821
+ font-style: italic;
822
+ color: var(--bp-ink);
823
+ }
824
+
825
+ /* Semantic emphasis weighted ABOVE stylistic offset */
826
+ :where(strong) {
827
+ font-weight: var(--bp-weight-strong);
828
+ color: var(--bp-text);
829
+ }
830
+ :where(em) {
831
+ font-style: italic;
832
+ }
833
+ :where(b) {
834
+ font-weight: var(--bp-weight-medium);
835
+ }
836
+ :where(i) {
837
+ font-style: italic;
838
+ }
839
+
840
+ /* Relevance highlight, edits, fine print, sub/sup, contact — all
841
+ theme-aware. */
842
+ :where(mark) {
843
+ background: var(--bp-fill-hi);
844
+ color: var(--bp-text);
845
+ padding: 0 2px;
846
+ }
847
+ :where(del) {
848
+ color: var(--bp-text-secondary);
849
+ text-decoration: line-through;
850
+ }
851
+ :where(ins) {
852
+ color: var(--bp-positive);
853
+ text-decoration: underline;
854
+ text-decoration-color: var(--bp-positive-faint);
855
+ }
856
+ :where(small) {
857
+ font-size: var(--bp-text-small);
858
+ color: var(--bp-text-secondary);
859
+ }
860
+ :where(sub, sup) {
861
+ font-size: 0.75em;
862
+ line-height: 0;
863
+ position: relative;
864
+ vertical-align: baseline;
865
+ }
866
+ :where(sup) { top: -0.5em; }
867
+ :where(sub) { bottom: -0.25em; }
868
+ :where(address) {
869
+ font-style: normal;
870
+ font-family: var(--bp-sans);
871
+ color: var(--bp-text-secondary);
872
+ line-height: var(--bp-lh-body);
873
+ }
874
+ }
875
+
876
+ /* ---------------------------------------------------------------------
877
+ @layer base (cont.) — Tier 5: landmark + disclosure chrome
878
+ ------------------------------------------------------------------- */
879
+ @layer base {
880
+ /* Masthead: METADATA ONLY (author, date, status) — never the argument */
881
+ :where(body > header, main > header, article > header) {
882
+ font-family: var(--bp-sans);
883
+ padding-bottom: 0;
884
+ margin-bottom: var(--bp-space-5);
885
+ border-bottom: 0;
886
+ }
887
+ /* Subordinate sources / status region */
888
+ :where(body > footer, main > footer, article > footer) {
889
+ margin-top: var(--bp-space-7);
890
+ padding-top: 0;
891
+ border-top: 0;
892
+ font-size: var(--bp-text-small);
893
+ color: var(--bp-text-secondary);
894
+ }
895
+ /* Tangential, visually subordinate content */
896
+ :where(aside) {
897
+ color: var(--bp-text-secondary);
898
+ border-left: 2px solid var(--bp-edge);
899
+ padding-left: var(--bp-space-3);
900
+ margin: var(--bp-space-3) 0;
901
+ }
902
+
903
+ /* Contents rail: monospace, muted, compact */
904
+ :where(nav) {
905
+ font-family: var(--bp-sans);
906
+ font-size: var(--bp-text-small);
907
+ }
908
+ :where(nav ol, nav ul) {
909
+ list-style: none;
910
+ margin: 0;
911
+ padding: 0;
912
+ }
913
+ :where(nav a) {
914
+ display: block;
915
+ color: var(--bp-text-secondary);
916
+ text-decoration: none;
917
+ padding: var(--bp-space-1) var(--bp-space-2);
918
+ }
919
+ /* --bp-fill-hi (slate-200), not --bp-fill-amb: the ambient fill (slate-100)
920
+ is nearly identical to the sidebar's --bp-bg, so the hover vanished. */
921
+ :where(nav a:hover, nav a[aria-current], nav a.active, nav a.is-active) {
922
+ color: var(--bp-ink);
923
+ background: var(--bp-fill-hi);
924
+ }
925
+
926
+ /* Native, JS-free progressive disclosure */
927
+ :where(details) {
928
+ border: 1px solid var(--bp-edge);
929
+ border-left: 2px solid var(--bp-ink-faint);
930
+ background: var(--bp-fill-amb);
931
+ padding: 0 var(--bp-space-3);
932
+ margin: var(--bp-space-3) 0;
933
+ }
934
+ :where(summary) {
935
+ font-family: var(--bp-sans);
936
+ font-weight: var(--bp-weight-strong);
937
+ cursor: pointer;
938
+ padding: var(--bp-space-2) 0;
939
+ list-style: none;
940
+ display: flex;
941
+ align-items: center;
942
+ gap: var(--bp-space-2);
943
+ }
944
+ :where(summary)::-webkit-details-marker {
945
+ display: none;
946
+ }
947
+ :where(summary)::before {
948
+ content: "";
949
+ width: 0;
950
+ height: 0;
951
+ border-left: 5px solid var(--bp-ink);
952
+ border-top: 4px solid transparent;
953
+ border-bottom: 4px solid transparent;
954
+ /* Motion: .bp-transition-transform .bp-ease (disclosure caret rotate) */
955
+ transition-property: transform;
956
+ transition-duration: var(--bp-duration-fast);
957
+ transition-timing-function: var(--bp-ease);
958
+ }
959
+ :where(details[open]) > :where(summary)::before {
960
+ transform: rotate(90deg);
961
+ }
962
+ :where(details[open]) {
963
+ padding-bottom: var(--bp-space-3);
964
+ }
965
+
966
+ /* Visible focus for keyboard users */
967
+ :where(a, button, summary, [tabindex]):focus-visible {
968
+ outline: 2px solid var(--bp-ink);
969
+ outline-offset: 3px;
970
+ border-radius: var(--bp-radius-2);
971
+ }
972
+ }
973
+
974
+ /* =====================================================================
975
+ @layer components — promoted composition primitives
976
+ ---------------------------------------------------------------------
977
+ These are COMPOSITIONS of semantic elements, not replacements for
978
+ them: a decision panel is a styled <section> + heading + <dl>; a
979
+ typed callout is an <aside> with a type chip; the option grid is
980
+ parallel <article>s. Each earns a class only because it combines
981
+ multiple elements into a structure no single element implies, AND it
982
+ recurs. You can always name the semantic element underneath.
983
+
984
+ Because this layer is declared AFTER `base`, every rule here beats the
985
+ base element rules regardless of specificity — and because each
986
+ selector is :where() (specificity 0), author CSS still beats these.
987
+ ===================================================================== */
988
+ @layer components {
989
+
990
+ /* ---- Narrative lede (explicit opt-in) ---------------------------
991
+ A lede must be asked for — apply .bp-lede to any paragraph
992
+ that should read as the narrative lead-in. Lede-by-position (the
993
+ old automatic `section > h2 + p` rule) was removed; a paragraph
994
+ following a heading is now ordinary body type unless classed. */
995
+ :where(.bp-lede) {
996
+ font-size: var(--bp-text-lede);
997
+ line-height: var(--bp-lh-lede);
998
+ color: var(--bp-text);
999
+ }
1000
+
1001
+ /* ---- Section heading permalink ------------------------------------
1002
+ Injected by blueprint.js: pixelarticons sit in the main-column gutter
1003
+ (left of the title, no text shift). Link on heading hover, copy on icon
1004
+ hover, check after a successful copy. */
1005
+ :where(h2:has(.bp-heading-row)) {
1006
+ position: relative;
1007
+ }
1008
+ /* Invisible bridge from the title into the gutter so the icon stays
1009
+ reachable while the pointer crosses the gap outside h2's border box. */
1010
+ :where(h2:has(.bp-heading-row))::after {
1011
+ content: "";
1012
+ position: absolute;
1013
+ left: calc(-1 * (16px + var(--bp-space-2)));
1014
+ bottom: 0;
1015
+ z-index: 0;
1016
+ width: calc(16px + var(--bp-space-2) + var(--bp-space-3));
1017
+ height: var(--bp-lh-h2);
1018
+ pointer-events: none;
1019
+ }
1020
+ :where(h2:is(:hover, :focus-within, [data-heading-link="visible"]):has(.bp-heading-row))::after {
1021
+ pointer-events: auto;
1022
+ }
1023
+ :where(.bp-heading-row) {
1024
+ display: inline;
1025
+ position: relative;
1026
+ max-width: 100%;
1027
+ }
1028
+ :where(.bp-heading-title) {
1029
+ min-width: 0;
1030
+ }
1031
+ :where(button.bp-heading-link) {
1032
+ position: absolute;
1033
+ left: calc(-1 * (16px + var(--bp-space-2)));
1034
+ top: 50%;
1035
+ bottom: auto;
1036
+ transform: translateY(-50%);
1037
+ z-index: 1;
1038
+ display: inline-flex;
1039
+ align-items: center;
1040
+ justify-content: center;
1041
+ width: 16px;
1042
+ height: 16px;
1043
+ margin: 0;
1044
+ padding: 0;
1045
+ border: 0;
1046
+ background: transparent;
1047
+ color: var(--bp-ink-soft);
1048
+ cursor: pointer;
1049
+ opacity: 0;
1050
+ pointer-events: none;
1051
+ transition:
1052
+ opacity var(--bp-duration-fast) var(--bp-ease-out),
1053
+ color var(--bp-duration-fast) var(--bp-ease-out);
1054
+ }
1055
+ /* Widen the hit target toward the title so the gutter icon is easy to click. */
1056
+ :where(button.bp-heading-link)::before {
1057
+ content: "";
1058
+ position: absolute;
1059
+ inset: -6px -10px -6px -6px;
1060
+ }
1061
+ :where(.bp-heading-link__icons) {
1062
+ position: relative;
1063
+ display: block;
1064
+ width: 16px;
1065
+ height: 16px;
1066
+ }
1067
+ :where(.bp-heading-link__icon) {
1068
+ position: absolute;
1069
+ inset: 0;
1070
+ display: block;
1071
+ opacity: 0;
1072
+ image-rendering: pixelated;
1073
+ transition: opacity var(--bp-duration-fast) var(--bp-ease-out);
1074
+ }
1075
+ :where(h2:hover .bp-heading-link),
1076
+ :where(h2:focus-within .bp-heading-link),
1077
+ :where(h2[data-heading-link="visible"] .bp-heading-link),
1078
+ :where(button.bp-heading-link:focus-visible),
1079
+ :where(button.bp-heading-link[data-bp-copy-state="copied"]),
1080
+ :where(button.bp-heading-link[data-bp-copy-state="error"]) {
1081
+ opacity: 1;
1082
+ pointer-events: auto;
1083
+ }
1084
+ :where(h2:hover .bp-heading-link__icon--link),
1085
+ :where(h2:focus-within .bp-heading-link__icon--link),
1086
+ :where(h2[data-heading-link="visible"] .bp-heading-link__icon--link),
1087
+ :where(button.bp-heading-link:focus-visible .bp-heading-link__icon--link) {
1088
+ opacity: 1;
1089
+ }
1090
+ :where(button.bp-heading-link:hover:not([data-bp-copy-state="copied"]) .bp-heading-link__icon--link),
1091
+ :where(button.bp-heading-link:focus-visible:not([data-bp-copy-state="copied"]) .bp-heading-link__icon--link) {
1092
+ opacity: 0;
1093
+ }
1094
+ :where(button.bp-heading-link:hover:not([data-bp-copy-state="copied"]) .bp-heading-link__icon--copy),
1095
+ :where(button.bp-heading-link:focus-visible:not([data-bp-copy-state="copied"]) .bp-heading-link__icon--copy) {
1096
+ opacity: 1;
1097
+ }
1098
+ :where(button.bp-heading-link[data-bp-copy-state="copied"] .bp-heading-link__icon--link),
1099
+ :where(button.bp-heading-link[data-bp-copy-state="copied"] .bp-heading-link__icon--copy) {
1100
+ opacity: 0;
1101
+ }
1102
+ :where(button.bp-heading-link[data-bp-copy-state="copied"] .bp-heading-link__icon--check) {
1103
+ opacity: 1;
1104
+ }
1105
+ :where(button.bp-heading-link:hover),
1106
+ :where(button.bp-heading-link:focus-visible),
1107
+ :where(button.bp-heading-link[data-bp-copy-state="copied"]) {
1108
+ color: var(--bp-ink);
1109
+ }
1110
+ :where(button.bp-heading-link:focus-visible) {
1111
+ outline: 2px solid var(--bp-ink);
1112
+ outline-offset: 2px;
1113
+ border-radius: var(--bp-radius-2);
1114
+ }
1115
+ :where(button.bp-heading-link[data-bp-copy-state="error"]) {
1116
+ color: var(--bp-ink-soft);
1117
+ }
1118
+
1119
+ /* ---- Eyebrow / label / meta — the mono-label family -------------- */
1120
+ :where(.bp-eyebrow, .bp-label) {
1121
+ display: block;
1122
+ font-family: var(--bp-mono);
1123
+ font-size: var(--bp-label-md);
1124
+ letter-spacing: var(--bp-label-md-ls);
1125
+ text-transform: uppercase;
1126
+ color: var(--bp-ink);
1127
+ margin-bottom: var(--bp-space-2);
1128
+ }
1129
+ :where(.bp-meta) {
1130
+ font-family: var(--bp-mono);
1131
+ font-size: var(--bp-label-md);
1132
+ letter-spacing: var(--bp-label-md-ls);
1133
+ text-transform: uppercase;
1134
+ color: var(--bp-text-secondary);
1135
+ }
1136
+ :where(.bp-note) {
1137
+ color: var(--bp-text-secondary);
1138
+ }
1139
+
1140
+ /* ---- 1. Decision panel ------------------------------------------
1141
+ The single load-bearing decision, given more weight than a routine
1142
+ callout. An inverted masthead band carries the kicker, an optional
1143
+ status pill, and optional provenance (decider + <time>); the headline
1144
+ statement is the typographic hero; stacked tenets explain it; and a
1145
+ distinct hatched footer states the condition for revisiting it. Every
1146
+ part but the statement is optional. Markup:
1147
+ <section class="bp-decision">
1148
+ <header class="bp-decision__bar">
1149
+ <span class="bp-label">Decision</span>
1150
+ <span class="bp-decision__status">Locked</span>
1151
+ <p class="bp-decision__meta">Obvious · <time datetime="2026-06-16">16 Jun 2026</time></p>
1152
+ </header>
1153
+ <div class="bp-decision__body">
1154
+ <p class="bp-decision-stmt">…the headline…</p>
1155
+ <dl class="bp-decision__tenets"> <dt>Holds because</dt><dd>…</dd> … </dl>
1156
+ </div>
1157
+ <p class="bp-decision__revisit"><b>Revisit when</b> …</p>
1158
+ </section> */
1159
+ :where(.bp-decision) {
1160
+ border: 1px solid var(--bp-ink);
1161
+ border-radius: var(--bp-radius-6);
1162
+ background: var(--bp-paper);
1163
+ overflow: hidden;
1164
+ margin: var(--bp-space-4) 0;
1165
+ }
1166
+ /* Inverted masthead band: kicker left, status pill, provenance pushed
1167
+ right. Paper-on-ink, so it flips with the theme like the doc band. */
1168
+ :where(.bp-decision__bar) {
1169
+ display: flex;
1170
+ align-items: center;
1171
+ gap: var(--bp-space-2);
1172
+ padding: var(--bp-space-2) var(--bp-space-3);
1173
+ background: var(--bp-ink);
1174
+ color: var(--bp-paper);
1175
+ }
1176
+ :where(.bp-decision__bar) > :where(.bp-label) {
1177
+ margin: 0;
1178
+ color: var(--bp-paper);
1179
+ font-size: var(--bp-label-lg);
1180
+ letter-spacing: var(--bp-label-lg-ls);
1181
+ font-weight: var(--bp-weight-medium);
1182
+ }
1183
+ :where(.bp-decision__status) {
1184
+ font-family: var(--bp-mono);
1185
+ font-size: var(--bp-label-sm);
1186
+ letter-spacing: var(--bp-label-sm-ls);
1187
+ text-transform: uppercase;
1188
+ border: 1px solid color-mix(in oklch, var(--bp-paper) 45%, transparent);
1189
+ border-radius: var(--bp-radius-pill);
1190
+ padding: 1px 8px;
1191
+ }
1192
+ :where(.bp-decision__meta) {
1193
+ margin: 0 0 0 auto;
1194
+ font-family: var(--bp-mono);
1195
+ font-size: var(--bp-label-md);
1196
+ letter-spacing: var(--bp-label-md-ls);
1197
+ text-transform: uppercase;
1198
+ color: color-mix(in oklch, var(--bp-paper) 72%, transparent);
1199
+ text-align: right;
1200
+ }
1201
+ :where(.bp-decision__meta) :where(time) {
1202
+ color: color-mix(in oklch, var(--bp-paper) 90%, transparent);
1203
+ }
1204
+ :where(.bp-decision__body) {
1205
+ padding: var(--bp-space-4) var(--bp-space-4) var(--bp-space-4);
1206
+ }
1207
+ :where(.bp-decision-stmt) {
1208
+ font-family: var(--bp-sans);
1209
+ font-size: var(--bp-text-title);
1210
+ font-weight: var(--bp-weight-strong);
1211
+ letter-spacing: -0.48px;
1212
+ line-height: var(--bp-lh-title);
1213
+ color: var(--bp-text);
1214
+ margin: 0;
1215
+ text-wrap: balance;
1216
+ }
1217
+ /* Stacked tenets — mono label ABOVE its value (no stranded label column),
1218
+ so several points stay aligned to the left margin and read cleanly. */
1219
+ :where(.bp-decision__tenets) {
1220
+ display: block;
1221
+ margin: var(--bp-space-3) 0 0;
1222
+ padding: 0;
1223
+ }
1224
+ :where(.bp-decision__tenets) > :where(dt) {
1225
+ font-family: var(--bp-mono);
1226
+ font-size: var(--bp-label-md);
1227
+ font-weight: var(--bp-weight-body);
1228
+ letter-spacing: var(--bp-label-md-ls);
1229
+ text-transform: uppercase;
1230
+ color: var(--bp-text-secondary);
1231
+ padding: 0;
1232
+ margin: 0 0 var(--bp-space-1);
1233
+ }
1234
+ :where(.bp-decision__tenets) > :where(dt ~ dt) {
1235
+ margin-top: var(--bp-space-3);
1236
+ }
1237
+ :where(.bp-decision__tenets) > :where(dd) {
1238
+ color: var(--bp-text);
1239
+ font-size: var(--bp-text-body);
1240
+ line-height: var(--bp-lh-body);
1241
+ margin: 0;
1242
+ }
1243
+ /* Change condition — a distinct hatched footer (drafting "subject to
1244
+ revision" texture). Tight, flush to the panel's bottom edge. */
1245
+ :where(.bp-decision__revisit) {
1246
+ display: flex;
1247
+ align-items: baseline;
1248
+ gap: var(--bp-space-2);
1249
+ margin: 0;
1250
+ padding: var(--bp-space-2) var(--bp-space-4);
1251
+ border-top: 1px solid var(--bp-edge);
1252
+ background-image: var(--bp-hatch);
1253
+ font-size: var(--bp-text-small);
1254
+ line-height: var(--bp-lh-small);
1255
+ color: var(--bp-text-secondary);
1256
+ }
1257
+ :where(.bp-decision__revisit) > :where(b) {
1258
+ font-family: var(--bp-mono);
1259
+ font-size: var(--bp-label-md);
1260
+ font-weight: var(--bp-weight-body);
1261
+ letter-spacing: var(--bp-label-md-ls);
1262
+ text-transform: uppercase;
1263
+ color: var(--bp-ink);
1264
+ white-space: nowrap;
1265
+ }
1266
+
1267
+ /* ---- 1b. Collapsible decision -----------------------------------
1268
+ A <details> variant for pages that carry several decisions. Collapsed,
1269
+ the whole panel is just the inverted bar — the decision reads on the
1270
+ left (compact, not hero scale), provenance on the right. Expanding
1271
+ drops the tenets and the revisit footer below the same bar. No JS —
1272
+ native disclosure. Markup:
1273
+ <details class="bp-decision bp-decision--collapsible">
1274
+ <summary class="bp-decision__bar">
1275
+ <span class="bp-label">Decision</span>
1276
+ <span class="bp-decision__title">…the decision…</span>
1277
+ <span class="bp-decision__meta">Obvious · <time …>…</time></span>
1278
+ <span class="bp-decision__caret" aria-hidden="true"></span>
1279
+ </summary>
1280
+ <dl class="bp-decision__tenets"> … </dl>
1281
+ <p class="bp-decision__revisit"> … </p>
1282
+ </details> */
1283
+ :where(.bp-decision--collapsible) {
1284
+ padding: 0;
1285
+ }
1286
+ /* The summary IS the bar — clicking it toggles the disclosure. */
1287
+ :where(.bp-decision--collapsible) > :where(summary) {
1288
+ cursor: pointer;
1289
+ list-style: none;
1290
+ gap: var(--bp-space-3);
1291
+ }
1292
+ :where(.bp-decision--collapsible) > :where(summary)::-webkit-details-marker {
1293
+ display: none;
1294
+ }
1295
+ /* Suppress the global <summary> caret; this variant carries its own. */
1296
+ :where(.bp-decision--collapsible) > :where(summary)::before {
1297
+ content: none;
1298
+ }
1299
+ /* The decision itself, compact (not hero scale), sits left of the meta. */
1300
+ :where(.bp-decision__title) {
1301
+ min-width: 0;
1302
+ font-family: var(--bp-sans);
1303
+ font-size: var(--bp-text-h4);
1304
+ font-weight: var(--bp-weight-strong);
1305
+ letter-spacing: -0.2px;
1306
+ line-height: var(--bp-lh-h4);
1307
+ color: var(--bp-paper);
1308
+ }
1309
+ :where(.bp-decision--collapsible) :where(.bp-decision__meta) {
1310
+ margin-right: 0;
1311
+ }
1312
+ /* Caret rides at the far right and rotates open. */
1313
+ :where(.bp-decision__caret) {
1314
+ flex: 0 0 auto;
1315
+ width: 0;
1316
+ height: 0;
1317
+ border-left: 5px solid currentColor;
1318
+ border-top: 4px solid transparent;
1319
+ border-bottom: 4px solid transparent;
1320
+ transition: transform var(--bp-duration-fast) var(--bp-ease);
1321
+ }
1322
+ /* No meta? Caret takes the auto margin so it still parks on the right. */
1323
+ :where(.bp-decision--collapsible) :where(.bp-decision__bar):not(:has(.bp-decision__meta)) :where(.bp-decision__caret) {
1324
+ margin-left: auto;
1325
+ }
1326
+ :where(.bp-decision--collapsible[open]) :where(.bp-decision__caret) {
1327
+ transform: rotate(90deg);
1328
+ }
1329
+ :where(.bp-decision--collapsible) > :where(.bp-decision__tenets) {
1330
+ padding: var(--bp-space-4) var(--bp-space-4) var(--bp-space-3);
1331
+ }
1332
+
1333
+ /* ---- 2. Typed callout family ------------------------------------
1334
+ A coherent callout family with legible roles, beneath the decision.
1335
+ Drafting-style: instead of a box, four L-shaped registration ticks
1336
+ bracket the corners (drawn as layered background gradients — no extra
1337
+ DOM), with an icon + mono type label heading the body. The base is
1338
+ .bp-callout (a styled <aside>); modifiers set the role. Markup:
1339
+ <aside class="bp-callout bp-callout--locked">
1340
+ <span class="bp-ctag"><svg …/>Locked</span>
1341
+ <p>…</p>
1342
+ </aside> */
1343
+ :where(.bp-callout) {
1344
+ --bp-tick: var(--bp-ink); /* tick ink (variants retint) */
1345
+ --bp-tick-len: 14px; /* arm length of each corner mark */
1346
+ --bp-tick-wt: 2px; /* arm thickness */
1347
+ --bp-callout-fill: none; /* optional texture layer (invariant) */
1348
+ position: relative;
1349
+ /* Reset the base <aside> chrome (border-left + padding-left). */
1350
+ border: 0;
1351
+ padding: var(--bp-space-3) var(--bp-space-4);
1352
+ margin: var(--bp-space-4) 0;
1353
+ color: var(--bp-text);
1354
+ background-color: transparent;
1355
+ background-repeat: no-repeat;
1356
+ /* 8 solid slivers = 4 corner Ls, then an optional texture fill below. */
1357
+ background-image:
1358
+ linear-gradient(var(--bp-tick), var(--bp-tick)),
1359
+ linear-gradient(var(--bp-tick), var(--bp-tick)),
1360
+ linear-gradient(var(--bp-tick), var(--bp-tick)),
1361
+ linear-gradient(var(--bp-tick), var(--bp-tick)),
1362
+ linear-gradient(var(--bp-tick), var(--bp-tick)),
1363
+ linear-gradient(var(--bp-tick), var(--bp-tick)),
1364
+ linear-gradient(var(--bp-tick), var(--bp-tick)),
1365
+ linear-gradient(var(--bp-tick), var(--bp-tick)),
1366
+ var(--bp-callout-fill);
1367
+ background-size:
1368
+ var(--bp-tick-len) var(--bp-tick-wt), var(--bp-tick-wt) var(--bp-tick-len),
1369
+ var(--bp-tick-len) var(--bp-tick-wt), var(--bp-tick-wt) var(--bp-tick-len),
1370
+ var(--bp-tick-len) var(--bp-tick-wt), var(--bp-tick-wt) var(--bp-tick-len),
1371
+ var(--bp-tick-len) var(--bp-tick-wt), var(--bp-tick-wt) var(--bp-tick-len),
1372
+ auto;
1373
+ background-position:
1374
+ left top, left top,
1375
+ right top, right top,
1376
+ left bottom, left bottom,
1377
+ right bottom, right bottom,
1378
+ 0 0;
1379
+ }
1380
+ /* Remove trailing margin from the last child of ANY boxed component so
1381
+ padding, not stray paragraph space, controls the inner bottom gap. */
1382
+ :where(
1383
+ .bp-decision,
1384
+ .bp-callout,
1385
+ .bp-option-grid > *,
1386
+ .bp-state-grid > *,
1387
+ .bp-sequence > li,
1388
+ details
1389
+ ) > :last-child {
1390
+ margin-bottom: 0;
1391
+ }
1392
+ /* Type label — icon + compact mono caption (no chip border). */
1393
+ :where(.bp-ctag) {
1394
+ display: inline-flex;
1395
+ align-items: center;
1396
+ gap: 5px;
1397
+ font-family: var(--bp-mono);
1398
+ font-size: var(--bp-label-sm);
1399
+ letter-spacing: var(--bp-label-sm-ls);
1400
+ text-transform: uppercase;
1401
+ color: var(--bp-ink);
1402
+ margin-bottom: var(--bp-space-2);
1403
+ }
1404
+ :where(.bp-ctag) > svg {
1405
+ flex: 0 0 auto;
1406
+ width: 13px;
1407
+ height: 13px;
1408
+ }
1409
+ /* Legacy chip (stored docs) — keep the bordered pill. */
1410
+ :where(.bp-chip) {
1411
+ display: inline-block;
1412
+ font-family: var(--bp-mono);
1413
+ font-size: var(--bp-label-md);
1414
+ letter-spacing: var(--bp-label-md-ls);
1415
+ text-transform: uppercase;
1416
+ color: var(--bp-ink);
1417
+ border: 1px solid var(--bp-ink-faint);
1418
+ padding: 2px 7px;
1419
+ margin-bottom: var(--bp-space-2);
1420
+ }
1421
+ /* Locked = a product commitment (solid ink ticks). */
1422
+ :where(.bp-callout--locked) {
1423
+ --bp-tick: var(--bp-ink);
1424
+ }
1425
+ /* Invariant = a must-hold rule: heaviest ticks + the drafting hatch. */
1426
+ :where(.bp-callout--invariant) {
1427
+ --bp-tick: var(--bp-ink);
1428
+ --bp-tick-len: 16px;
1429
+ --bp-tick-wt: 2.5px;
1430
+ --bp-callout-fill: var(--bp-hatch);
1431
+ background-color: var(--bp-fill-hi);
1432
+ }
1433
+ /* Reference = enumerated values (subdued, soft ticks). */
1434
+ :where(.bp-callout--ref) {
1435
+ --bp-tick: var(--bp-ink-soft);
1436
+ }
1437
+ :where(.bp-callout--ref) > :where(.bp-ctag) {
1438
+ color: var(--bp-text-secondary);
1439
+ }
1440
+ /* <bp-callout> expands to the markup above (blueprint.js). Reserve block
1441
+ flow before upgrade so pre-script content does not flash inline. */
1442
+ :where(bp-callout) {
1443
+ display: block;
1444
+ }
1445
+
1446
+ /* ---- 3. Definition list component -------------------------------
1447
+ A bare <dl> already renders as label-left/description-right rows in
1448
+ the base. .bp-deflist is the variant hook — same semantics, tuned
1449
+ column for long labels. */
1450
+ :where(.bp-deflist) {
1451
+ grid-template-columns: minmax(10rem, 16rem) 1fr;
1452
+ }
1453
+
1454
+ /* ---- 4. Option / comparison grid --------------------------------
1455
+ True parallel comparisons (A / B / C). Equal-column cards built
1456
+ from <article>s, with a role label and an optional verdict pill.
1457
+ Mark the chosen option with .bp-opt--rec. */
1458
+ :where(.bp-option-grid) {
1459
+ display: grid;
1460
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
1461
+ gap: 0;
1462
+ border: 1px solid var(--bp-edge);
1463
+ margin: var(--bp-space-4) 0;
1464
+ }
1465
+ :where(.bp-option-grid > *) {
1466
+ padding: var(--bp-space-3);
1467
+ border-right: 1px solid var(--bp-edge);
1468
+ background: var(--bp-paper);
1469
+ }
1470
+ :where(.bp-option-grid > :last-child) {
1471
+ border-right: 0;
1472
+ }
1473
+ :where(.bp-opt--rec) {
1474
+ background: var(--bp-fill-amb);
1475
+ box-shadow: inset 3px 0 0 var(--bp-ink);
1476
+ }
1477
+ :where(.bp-verdict) {
1478
+ display: inline-block;
1479
+ font-family: var(--bp-mono);
1480
+ font-size: var(--bp-label-sm);
1481
+ letter-spacing: var(--bp-label-sm-ls);
1482
+ text-transform: uppercase;
1483
+ color: var(--bp-paper);
1484
+ background: var(--bp-ink);
1485
+ padding: 2px 8px;
1486
+ margin-top: var(--bp-space-2);
1487
+ }
1488
+
1489
+ /* ---- 5. Numbered linear sequence --------------------------------
1490
+ A linear mechanism (pipeline, escalation). NOT a comparison grid.
1491
+ Built from an <ol class="bp-sequence">; each <li> is a step with an
1492
+ auto number. */
1493
+ :where(.bp-sequence) {
1494
+ list-style: none;
1495
+ counter-reset: bp-step;
1496
+ padding: 0;
1497
+ margin: var(--bp-space-4) 0;
1498
+ }
1499
+ :where(.bp-sequence > li) {
1500
+ counter-increment: bp-step;
1501
+ position: relative;
1502
+ padding: var(--bp-space-2) 0 var(--bp-space-2) calc(var(--bp-space-6) + var(--bp-space-1));
1503
+ margin: 0;
1504
+ }
1505
+ :where(.bp-sequence > li + li) {
1506
+ margin-top: var(--bp-space-3);
1507
+ }
1508
+ :where(.bp-sequence > li)::before {
1509
+ content: counter(bp-step, decimal-leading-zero);
1510
+ position: absolute;
1511
+ left: 0;
1512
+ top: var(--bp-space-2);
1513
+ font-family: var(--bp-mono);
1514
+ font-size: var(--bp-label-lg);
1515
+ font-weight: var(--bp-weight-medium);
1516
+ letter-spacing: 0.08em;
1517
+ color: var(--bp-ink);
1518
+ }
1519
+
1520
+ /* ---- State grid -------------------------------------------------
1521
+ Off-happy-path state model (empty / loading / ready / error …).
1522
+ Built from <article>s; each gets a mono state name via .bp-label. */
1523
+ :where(.bp-state-grid) {
1524
+ display: grid;
1525
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
1526
+ gap: 0;
1527
+ border: 1px solid var(--bp-edge);
1528
+ margin: var(--bp-space-4) 0;
1529
+ }
1530
+ :where(.bp-state-grid > *) {
1531
+ padding: var(--bp-space-3);
1532
+ border-right: 1px solid var(--bp-edge);
1533
+ border-bottom: 1px solid var(--bp-edge);
1534
+ background: var(--bp-paper);
1535
+ }
1536
+ /* Void / fenced-off state — a dead cell (orphaned, reclaimed away).
1537
+ The hatch layers over the paper set above; declared after so the
1538
+ zero-specificity rules resolve by source order. */
1539
+ :where(.bp-state--void) {
1540
+ background-image: var(--bp-hatch);
1541
+ }
1542
+
1543
+ /* ---- Hatch utility ----------------------------------------------
1544
+ Drop .bp-hatch on any block element to mark a fenced / held /
1545
+ out-of-bounds region with drafting section-lining. Layers the hatch
1546
+ over the element's existing background, so pair it with a surface. */
1547
+ :where(.bp-hatch) {
1548
+ background-image: var(--bp-hatch);
1549
+ }
1550
+
1551
+ /* ---- Data-table wrap: let wide tables break past the prose measure */
1552
+ :where(.bp-table-wrap) {
1553
+ max-width: 100%;
1554
+ overflow-x: auto;
1555
+ margin: var(--bp-space-3) 0 var(--bp-space-4);
1556
+ }
1557
+ /* Opt-in breakout: a wide table expands toward --bp-wide */
1558
+ :where(.bp-table-wrap--wide) {
1559
+ width: 100vw;
1560
+ max-width: var(--bp-wide);
1561
+ margin-inline: calc(50% - 50vw);
1562
+ margin-inline: max(calc(50% - 50vw), calc((100% - var(--bp-wide)) / 2));
1563
+ }
1564
+
1565
+ /* ---- Source disclosure -------------------------------------------
1566
+ A reusable source-code panel. The semantic shell is a native <details>;
1567
+ authors provide the explicit language/copy toolbar, blueprint-code.js
1568
+ enables the copy button, and blueprint-code.css adds Prism color. */
1569
+ :where(bp-source) {
1570
+ display: block;
1571
+ }
1572
+ :where(.bp-source) {
1573
+ padding: 0;
1574
+ }
1575
+ :where(.bp-source[open]) {
1576
+ padding-bottom: 0;
1577
+ }
1578
+ /* The summary IS the header bar: marker + intent label on the left, the
1579
+ language tag + copy action pushed to the right. One strip, then the code
1580
+ beneath it — no nested boxes. */
1581
+ :where(.bp-source > summary) {
1582
+ display: flex;
1583
+ align-items: center;
1584
+ gap: var(--bp-space-3);
1585
+ padding: var(--bp-space-2) var(--bp-space-3);
1586
+ }
1587
+ :where(.bp-source-name) {
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);
1593
+ }
1594
+ :where(.bp-source-toolbar) {
1595
+ display: flex;
1596
+ align-items: center;
1597
+ gap: var(--bp-space-3);
1598
+ margin-left: auto;
1599
+ }
1600
+ :where(.bp-source-language) {
1601
+ font-family: var(--bp-mono);
1602
+ font-size: var(--bp-label-sm);
1603
+ letter-spacing: var(--bp-label-sm-ls);
1604
+ text-transform: uppercase;
1605
+ color: var(--bp-text-secondary);
1606
+ }
1607
+ :where(.bp-source-copy) {
1608
+ appearance: none;
1609
+ border: 1px solid var(--bp-ink-line);
1610
+ border-radius: var(--bp-radius-2);
1611
+ padding: 4px var(--bp-space-2);
1612
+ background: transparent;
1613
+ color: var(--bp-ink);
1614
+ font-family: var(--bp-mono);
1615
+ font-size: var(--bp-label-sm);
1616
+ line-height: 1;
1617
+ letter-spacing: var(--bp-label-sm-ls);
1618
+ text-transform: uppercase;
1619
+ cursor: pointer;
1620
+ }
1621
+ :where(.bp-source-copy:hover) {
1622
+ border-color: var(--bp-ink);
1623
+ background: var(--bp-fill-amb);
1624
+ }
1625
+ :where(.bp-source-copy[data-bp-copy-state="copied"]) {
1626
+ border-color: var(--bp-ink);
1627
+ background: var(--bp-fill-hi);
1628
+ }
1629
+ :where(.bp-source-copy[data-bp-copy-state="error"]) {
1630
+ border-style: dashed;
1631
+ }
1632
+ :where(.bp-source > pre) {
1633
+ max-height: 360px;
1634
+ margin: 0;
1635
+ border: 0;
1636
+ border-top: 1px solid var(--bp-edge);
1637
+ }
1638
+
1639
+ /* ---- Code citation tooltip ---------------------------------------
1640
+ <bp-cite> renders an inline TRIGGER (the cited text) plus a top-layer
1641
+ POPOVER card that reveals the cited code on hover / focus / tap. The
1642
+ code block inside is dressed flush by blueprint-code.css (where the
1643
+ Prism <pre> rules live). Citation markers use --bp-illustration blue.
1644
+ The runtime (blueprint-code.js) owns show/hide + viewport placement. */
1645
+ :where(bp-cite) {
1646
+ display: inline;
1647
+ }
1648
+ :where(.bp-cite-trigger) {
1649
+ font: inherit;
1650
+ margin: 0;
1651
+ padding: 0;
1652
+ border: 0;
1653
+ background: none;
1654
+ color: inherit;
1655
+ cursor: help;
1656
+ text-decoration: underline dotted var(--bp-illustration-faint);
1657
+ text-underline-offset: 3px;
1658
+ text-decoration-thickness: 1px;
1659
+ /* Motion: .bp-transition-colors .bp-ease (hover color shift) */
1660
+ transition-property: text-decoration-color;
1661
+ transition-duration: var(--bp-duration-fast);
1662
+ transition-timing-function: var(--bp-ease);
1663
+ }
1664
+ :where(.bp-cite-trigger:hover) {
1665
+ text-decoration-color: var(--bp-illustration-soft);
1666
+ }
1667
+ :where(.bp-cite-trigger[aria-expanded="true"]) {
1668
+ text-decoration-color: var(--bp-blue-800);
1669
+ }
1670
+ :where([data-obvious-theme="dark"] .bp-cite-trigger[aria-expanded="true"]) {
1671
+ text-decoration-color: var(--bp-blue-200);
1672
+ }
1673
+ :where(.bp-cite-icon) {
1674
+ font-family: var(--bp-mono);
1675
+ font-size: 0.72em;
1676
+ letter-spacing: -0.05em;
1677
+ color: var(--bp-illustration);
1678
+ margin-left: 0.32em;
1679
+ vertical-align: 0.08em;
1680
+ /* Motion: .bp-transition-colors .bp-ease (hover color shift) */
1681
+ transition-property: color;
1682
+ transition-duration: var(--bp-duration-fast);
1683
+ transition-timing-function: var(--bp-ease);
1684
+ }
1685
+ :where(.bp-cite-icon)::before {
1686
+ content: "</>";
1687
+ }
1688
+ :where(.bp-cite-trigger:hover .bp-cite-icon) {
1689
+ color: var(--bp-illustration-soft);
1690
+ }
1691
+ :where(.bp-cite-trigger[aria-expanded="true"] .bp-cite-icon) {
1692
+ color: var(--bp-blue-800);
1693
+ }
1694
+ :where([data-obvious-theme="dark"] .bp-cite-trigger[aria-expanded="true"] .bp-cite-icon) {
1695
+ color: var(--bp-blue-200);
1696
+ }
1697
+
1698
+ /* The popover card. Resets the UA's centered popover positioning so the
1699
+ runtime can place it; falls back to .is-open when popover is absent. */
1700
+ :where(.bp-cite-pop) {
1701
+ position: fixed;
1702
+ margin: 0;
1703
+ inset: auto;
1704
+ width: min(460px, calc(100vw - 20px));
1705
+ padding: 0;
1706
+ border: 1px solid var(--bp-ink-line);
1707
+ border-radius: var(--bp-radius-6);
1708
+ background: var(--bp-paper);
1709
+ color: var(--bp-text);
1710
+ box-shadow: var(--bp-shadow-pop);
1711
+ opacity: 0;
1712
+ transform: translateY(4px);
1713
+ /* Motion: .bp-transition-opacity + transform, fast + ease-out (tooltip
1714
+ enter/exit). overlay + display need allow-discrete for top-layer. */
1715
+ transition-property: opacity, transform, overlay, display;
1716
+ transition-duration: var(--bp-duration-fast);
1717
+ transition-timing-function: var(--bp-ease-out);
1718
+ transition-behavior: allow-discrete;
1719
+ }
1720
+ :where(.bp-cite-pop:not([popover])) {
1721
+ display: none;
1722
+ }
1723
+ :where(.bp-cite-pop.is-open) {
1724
+ display: block;
1725
+ }
1726
+ :where(.bp-cite-pop:popover-open),
1727
+ :where(.bp-cite-pop.is-open) {
1728
+ opacity: 1;
1729
+ transform: translateY(0);
1730
+ }
1731
+ @starting-style {
1732
+ :where(.bp-cite-pop:popover-open),
1733
+ :where(.bp-cite-pop.is-open) {
1734
+ opacity: 0;
1735
+ transform: translateY(4px);
1736
+ }
1737
+ }
1738
+ /* Hairline caret — a border triangle behind a paper triangle. */
1739
+ :where(.bp-cite-pop)::before,
1740
+ :where(.bp-cite-pop)::after {
1741
+ content: "";
1742
+ position: absolute;
1743
+ left: var(--bp-cite-caret, 50%);
1744
+ width: 0;
1745
+ height: 0;
1746
+ border: 7px solid transparent;
1747
+ transform: translateX(-50%);
1748
+ }
1749
+ :where(.bp-cite-pop[data-placement="top"])::before {
1750
+ bottom: -14px;
1751
+ border-top-color: var(--bp-ink-line);
1752
+ }
1753
+ :where(.bp-cite-pop[data-placement="top"])::after {
1754
+ bottom: -12px;
1755
+ border-top-color: var(--bp-paper);
1756
+ }
1757
+ :where(.bp-cite-pop[data-placement="bottom"])::before {
1758
+ top: -14px;
1759
+ border-bottom-color: var(--bp-ink-line);
1760
+ }
1761
+ :where(.bp-cite-pop[data-placement="bottom"])::after {
1762
+ top: -12px;
1763
+ border-bottom-color: var(--bp-paper);
1764
+ }
1765
+ :where(.bp-cite-head) {
1766
+ display: flex;
1767
+ align-items: center;
1768
+ justify-content: space-between;
1769
+ gap: var(--bp-space-3);
1770
+ padding: var(--bp-space-2) var(--bp-space-3);
1771
+ border-bottom: 1px solid var(--bp-edge);
1772
+ }
1773
+ :where(.bp-cite-loc) {
1774
+ font-family: var(--bp-mono);
1775
+ font-size: var(--bp-label-sm);
1776
+ letter-spacing: var(--bp-label-sm-ls);
1777
+ text-transform: uppercase;
1778
+ color: var(--bp-text);
1779
+ white-space: nowrap;
1780
+ overflow: hidden;
1781
+ text-overflow: ellipsis;
1782
+ }
1783
+ :where(.bp-cite-lang) {
1784
+ flex: 0 0 auto;
1785
+ font-family: var(--bp-mono);
1786
+ font-size: var(--bp-label-sm);
1787
+ letter-spacing: var(--bp-label-sm-ls);
1788
+ text-transform: uppercase;
1789
+ color: var(--bp-text-secondary);
1790
+ }
1791
+ /* Code block inside the card — base treatment; blueprint-code.css makes
1792
+ the highlighted variant sit flush and rounds the lower corners. */
1793
+ :where(.bp-cite-pop pre) {
1794
+ max-height: 320px;
1795
+ margin: 0;
1796
+ border: 0;
1797
+ border-radius: 0 0 var(--bp-radius-6) var(--bp-radius-6);
1798
+ }
1799
+
1800
+ /* ---- SVG figure marks (1px hairline default via --bp-stroke) -------
1801
+ ILLUSTRATIONS are the one place blue lives. Node/edge line work draws
1802
+ in --bp-illustration; only the in-diagram TEXT labels stay neutral ink
1803
+ (text everywhere reads in the monochrome scale). */
1804
+ :where(.bp-node) {
1805
+ fill: var(--bp-paper);
1806
+ stroke: var(--bp-illustration);
1807
+ stroke-width: var(--bp-stroke);
1808
+ }
1809
+ :where(.bp-node--amb) {
1810
+ fill: var(--bp-illustration-fill);
1811
+ }
1812
+ /* Fenced node — a barrier / held step. Filled with the diagonal hatch
1813
+ paint server (define the <pattern> once in the figure's <defs>; see
1814
+ the .bp-hatch-fill / .bp-hatch-line tiles below). The .bp-node stroke
1815
+ keeps the illustration border. CSS gradients can't paint an SVG shape,
1816
+ so SVG uses a <pattern> while HTML uses the --bp-hatch gradient token. */
1817
+ :where(.bp-node--fenced) {
1818
+ fill: url(#bp-hatch);
1819
+ }
1820
+ /* Tiles for the SVG hatch <pattern> — token-driven so both themes track.
1821
+ The fill rect carries the illustration wash; the line is the drafting
1822
+ section-lining, both in the blue illustration family. */
1823
+ :where(.bp-hatch-fill) {
1824
+ fill: var(--bp-illustration-fill);
1825
+ }
1826
+ :where(.bp-hatch-line) {
1827
+ stroke: var(--bp-illustration-faint);
1828
+ stroke-width: var(--bp-stroke);
1829
+ }
1830
+ :where(.bp-edge) {
1831
+ fill: none;
1832
+ stroke: var(--bp-illustration);
1833
+ stroke-width: var(--bp-stroke);
1834
+ }
1835
+ :where(.bp-edge--skip) {
1836
+ stroke: var(--bp-illustration-soft);
1837
+ stroke-dasharray: 4 3;
1838
+ }
1839
+ :where(.bp-svg-label) {
1840
+ font-family: var(--bp-mono);
1841
+ font-size: 11px;
1842
+ fill: var(--bp-text);
1843
+ }
1844
+ :where(.bp-svg-meta) {
1845
+ font-family: var(--bp-mono);
1846
+ font-size: 9px;
1847
+ fill: var(--bp-text-secondary);
1848
+ }
1849
+ /* Callout terminator dot — illustration line work, so it draws in blue */
1850
+ :where(.bp-svg-dot) {
1851
+ fill: var(--bp-illustration);
1852
+ }
1853
+
1854
+ /* ---- Isometric solids (overview illustration) ---------------------
1855
+ Generic isometric primitives for axis-aligned boxes: side faces read
1856
+ as paper edges, top faces carry the illustration wash, and a faint
1857
+ floor grid grounds the scene. Used for the overview factory line
1858
+ (machines, conveyors, parts); blue is line work only, labels stay
1859
+ ink. .bp-svg-dot terminates callout leaders in the illustration blue. */
1860
+ :where(.bp-stack-face) {
1861
+ fill: var(--bp-paper);
1862
+ stroke: var(--bp-illustration);
1863
+ stroke-width: var(--bp-stroke);
1864
+ }
1865
+ :where(.bp-stack-top) {
1866
+ fill: var(--bp-illustration-fill);
1867
+ stroke: var(--bp-illustration);
1868
+ stroke-width: var(--bp-stroke);
1869
+ }
1870
+ :where(.bp-stack-top--fenced) {
1871
+ fill: url(#overview-stack-hatch);
1872
+ }
1873
+ :where(.bp-stack-grid) {
1874
+ fill: none;
1875
+ stroke: var(--bp-illustration-faint);
1876
+ stroke-width: var(--bp-stroke);
1877
+ opacity: 0.45;
1878
+ }
1879
+
1880
+ /* ---- Masthead title-block: a metadata strip (author/date/status) - */
1881
+ :where(.bp-title-block) {
1882
+ display: flex;
1883
+ flex-wrap: wrap;
1884
+ border: 1px solid var(--bp-ink-line);
1885
+ margin-top: var(--bp-space-4);
1886
+ }
1887
+ :where(.bp-tb-cell) {
1888
+ flex: 0 0 auto;
1889
+ padding: var(--bp-space-2) var(--bp-space-3);
1890
+ border-right: 1px solid var(--bp-ink-line);
1891
+ }
1892
+ :where(.bp-tb-cell:last-child) {
1893
+ border-right: 0;
1894
+ }
1895
+ :where(.bp-tb-cell--grow) {
1896
+ flex: 1 1 auto;
1897
+ }
1898
+
1899
+ /* ---- Transitional authored-markup compatibility ------------------
1900
+ These aliases preserve current-skill and stored Blueprint contracts while
1901
+ semantic markup remains the preferred API. */
1902
+ :where(.bp-content) {
1903
+ max-width: var(--bp-content);
1904
+ margin-inline: auto;
1905
+ padding: var(--bp-space-6);
1906
+ }
1907
+ :where(.bp-document) {
1908
+ min-height: 100vh;
1909
+ color: var(--bp-text);
1910
+ background: var(--bp-bg);
1911
+ font-family: var(--bp-serif);
1912
+ }
1913
+ :where(.bp-title) {
1914
+ font-family: var(--bp-sans);
1915
+ font-size: var(--bp-text-title);
1916
+ font-weight: var(--bp-weight-strong);
1917
+ letter-spacing: -0.48px;
1918
+ line-height: var(--bp-lh-title);
1919
+ }
1920
+ :where(.bp-placeholder) {
1921
+ border: 1px dashed var(--bp-ink-faint);
1922
+ background: var(--bp-fill-amb);
1923
+ }
1924
+ :where(.bp-layout, .bp-card-grid) {
1925
+ display: grid;
1926
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1927
+ gap: 1px;
1928
+ border: 1px solid var(--bp-edge);
1929
+ background: var(--bp-edge);
1930
+ margin: var(--bp-space-3) 0 var(--bp-space-4);
1931
+ }
1932
+ :where(.bp-card) {
1933
+ min-width: 0;
1934
+ padding: var(--bp-space-3);
1935
+ background: var(--bp-paper);
1936
+ }
1937
+ :where(.bp-section) {
1938
+ margin-top: calc(var(--bp-space-7) + var(--bp-space-5));
1939
+ }
1940
+ :where(.bp-section-number) {
1941
+ display: block;
1942
+ width: max-content;
1943
+ font-family: var(--bp-mono);
1944
+ font-size: var(--bp-label-lg);
1945
+ letter-spacing: var(--bp-label-lg-ls);
1946
+ text-transform: uppercase;
1947
+ color: var(--bp-ink);
1948
+ margin-bottom: var(--bp-space-2);
1949
+ }
1950
+ :where(.bp-figure) {
1951
+ width: fit-content;
1952
+ max-width: 100%;
1953
+ margin: var(--bp-space-4) auto;
1954
+ }
1955
+ :where(.bp-figure svg[viewBox]) {
1956
+ display: block;
1957
+ width: auto;
1958
+ max-width: min(100%, var(--bp-diagram-w));
1959
+ height: auto;
1960
+ }
1961
+ :where(.bp-figure svg:not([viewBox])) {
1962
+ display: block;
1963
+ width: auto;
1964
+ max-width: 100%;
1965
+ height: auto;
1966
+ }
1967
+ :where(.bp-figure-caption) {
1968
+ font-family: var(--bp-mono);
1969
+ font-size: var(--bp-label-sm);
1970
+ letter-spacing: var(--bp-label-sm-ls);
1971
+ text-transform: uppercase;
1972
+ color: var(--bp-text-secondary);
1973
+ }
1974
+ :where(.bp-table) {
1975
+ width: 100%;
1976
+ border-collapse: collapse;
1977
+ font-family: var(--bp-sans);
1978
+ }
1979
+ :where(.bp-toc) {
1980
+ font-size: var(--bp-text-small);
1981
+ color: var(--bp-text-secondary);
1982
+ }
1983
+ :where(.bp-toc ul) {
1984
+ list-style: none;
1985
+ margin: 0;
1986
+ padding: 0;
1987
+ }
1988
+ :where(.bp-toc a) {
1989
+ display: block;
1990
+ color: var(--bp-text-secondary);
1991
+ text-decoration: none;
1992
+ padding: var(--bp-space-1) var(--bp-space-2);
1993
+ }
1994
+
1995
+ /* ---- Sidebar / Table of Contents + reading-progress chrome --------
1996
+ A drafting-grade contents rail: a hairline GUIDE TRACK runs the full
1997
+ height of the list; a single INK GLIDE marker (the ul::after) slides
1998
+ smoothly to the active entry; each entry carries an auto-numbered
1999
+ mono index that matches the section eyebrows. blueprint.js sets
2000
+ --bp-toc-y / --bp-toc-h (active entry geometry) and --bp-toc-fill
2001
+ (read-so-far ratio); everything degrades to the aria-current rail
2002
+ with no JS. All neutral ink — blue stays in the illustrations. */
2003
+ :where(.bp-sidebar, .bp-toc) {
2004
+ position: fixed;
2005
+ top: 0;
2006
+ left: 0;
2007
+ width: var(--bp-sidebar);
2008
+ height: 100vh;
2009
+ overflow-y: auto;
2010
+ padding: var(--bp-space-5) var(--bp-space-4);
2011
+ background: var(--bp-bg);
2012
+ z-index: 100;
2013
+ }
2014
+ /* Sidebar masthead — the wordmark is the panel header and must read larger
2015
+ than the contents list; the meta is a subordinate caption beneath it,
2016
+ with clear separation before the TOC. */
2017
+ :where(.bp-sidebar > .bp-eyebrow, .bp-toc > .bp-eyebrow) {
2018
+ font-size: var(--bp-text-body);
2019
+ letter-spacing: var(--bp-label-md-ls);
2020
+ font-weight: var(--bp-weight-medium);
2021
+ margin-bottom: var(--bp-space-1);
2022
+ }
2023
+ :where(.bp-sidebar > .bp-meta, .bp-toc > .bp-meta) {
2024
+ margin-bottom: var(--bp-space-5);
2025
+ }
2026
+ /* The contents list hosts a single rounded ACTIVE PILL that glides between
2027
+ entries as you scroll — no rail, no left border. Obvious-style. */
2028
+ :where(.bp-sidebar > ul, .bp-sidebar__panel > ul, .bp-toc > ul) {
2029
+ position: relative;
2030
+ counter-reset: bp-toc;
2031
+ margin: 0;
2032
+ padding: 0;
2033
+ list-style: none;
2034
+ }
2035
+ /* The gliding active pill, drawn behind the entries and travelling via
2036
+ --bp-toc-y / --bp-toc-h (set by blueprint.js). */
2037
+ :where(.bp-sidebar > ul, .bp-sidebar__panel > ul, .bp-toc > ul)::before {
2038
+ content: "";
2039
+ position: absolute;
2040
+ left: 0;
2041
+ right: 0;
2042
+ top: var(--bp-toc-y, 0);
2043
+ height: var(--bp-toc-h, 0);
2044
+ background: var(--bp-fill-hi);
2045
+ border-radius: var(--bp-radius-6);
2046
+ z-index: 0;
2047
+ pointer-events: none;
2048
+ /* Motion: slow + ease-out (large pill gliding between TOC entries). The
2049
+ old hand-tuned cubic-bezier was already an ease-out, so it maps onto
2050
+ --bp-ease-out; 0.32s rounds to the slow token. */
2051
+ transition-property: top, height;
2052
+ transition-duration: var(--bp-duration-slow);
2053
+ transition-timing-function: var(--bp-ease-out);
2054
+ }
2055
+ :where(.bp-sidebar > ul > li, .bp-sidebar__panel > ul > li, .bp-toc > ul > li) {
2056
+ counter-increment: bp-toc;
2057
+ margin: 0;
2058
+ padding: 0;
2059
+ }
2060
+ /* TOC nav items: proportional sans, auto-numbered, comfortable target.
2061
+ The entry labels read as prose (Booton); only the small index tick keeps
2062
+ equal-width digits so the numbers stay aligned. Each is a rounded pill. */
2063
+ :where(.bp-sidebar ul a, .bp-toc ul a) {
2064
+ position: relative;
2065
+ z-index: 1;
2066
+ display: flex;
2067
+ align-items: baseline;
2068
+ gap: var(--bp-space-2);
2069
+ font-family: var(--bp-sans);
2070
+ font-size: var(--bp-text-body);
2071
+ line-height: 1.35;
2072
+ color: var(--bp-text-secondary);
2073
+ text-decoration: none;
2074
+ padding: 6px var(--bp-space-3);
2075
+ border-radius: var(--bp-radius-6);
2076
+ /* Motion: .bp-transition-colors .bp-ease (hover color shift) */
2077
+ transition-property: color, background;
2078
+ transition-duration: var(--bp-duration-fast);
2079
+ transition-timing-function: var(--bp-ease);
2080
+ }
2081
+ /* Auto-numbered index (01, 02…), tabular so the digits line up. */
2082
+ :where(.bp-sidebar > ul > li > a, .bp-sidebar__panel > ul > li > a, .bp-toc > ul > li > a)::before {
2083
+ content: counter(bp-toc, decimal-leading-zero);
2084
+ flex: 0 0 auto;
2085
+ font-size: var(--bp-label-lg);
2086
+ font-variant-numeric: tabular-nums;
2087
+ letter-spacing: 0.02em;
2088
+ color: var(--bp-ink-soft);
2089
+ /* Motion: .bp-transition-colors .bp-ease (hover color shift) */
2090
+ transition-property: color;
2091
+ transition-duration: var(--bp-duration-fast);
2092
+ transition-timing-function: var(--bp-ease);
2093
+ }
2094
+ /* Nested sub-entries indent and drop the number. */
2095
+ :where(.bp-sidebar ul ul, .bp-toc ul ul) {
2096
+ margin: 0;
2097
+ padding: 0 0 0 var(--bp-space-3);
2098
+ list-style: none;
2099
+ }
2100
+ :where(.bp-sidebar ul ul a, .bp-toc ul ul a) {
2101
+ font-size: var(--bp-text-small);
2102
+ }
2103
+ /* Hover shows its own subtle pill; the active entry rides the gliding pill
2104
+ above (so it keeps no own background) and reads in ink + medium weight. */
2105
+ :where(.bp-sidebar a:hover, .bp-toc a:hover) {
2106
+ color: var(--bp-ink);
2107
+ background: var(--bp-fill-amb);
2108
+ }
2109
+ :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) {
2110
+ color: var(--bp-ink);
2111
+ background: none;
2112
+ font-weight: var(--bp-weight-medium);
2113
+ }
2114
+ :where(.bp-sidebar a:hover::before, .bp-toc a:hover::before, .bp-sidebar a[aria-current="location"]::before, .bp-toc a[aria-current="location"]::before, .bp-sidebar a.active::before, .bp-toc a.active::before) {
2115
+ color: var(--bp-ink);
2116
+ }
2117
+ /* ---- Full-width contents block -----------------------------------
2118
+ A table-of-contents SECTION — the inline, content-width counterpart
2119
+ to the fixed `.bp-sidebar` rail. A numbered, responsive multi-column
2120
+ index with dotted leaders; its links share the same aria-current
2121
+ active state from blueprint.js (no gliding marker — it's a grid). */
2122
+ :where(.bp-contents) {
2123
+ margin: var(--bp-space-5) 0 var(--bp-space-6);
2124
+ }
2125
+ :where(.bp-contents > .bp-eyebrow, .bp-contents > .bp-label, .bp-contents > .bp-meta) {
2126
+ margin: var(--bp-space-3) 0 var(--bp-space-1);
2127
+ }
2128
+ :where(.bp-contents > ol, .bp-contents > ul) {
2129
+ display: grid;
2130
+ grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
2131
+ gap: 0 var(--bp-space-6);
2132
+ counter-reset: bp-contents;
2133
+ list-style: none;
2134
+ margin: 0;
2135
+ padding: 0;
2136
+ }
2137
+ :where(.bp-contents li) {
2138
+ counter-increment: bp-contents;
2139
+ margin: 0;
2140
+ }
2141
+ :where(.bp-contents a) {
2142
+ display: flex;
2143
+ align-items: baseline;
2144
+ gap: var(--bp-space-2);
2145
+ padding: var(--bp-space-2) 0;
2146
+ font-family: var(--bp-sans);
2147
+ font-size: var(--bp-text-body);
2148
+ color: var(--bp-text);
2149
+ text-decoration: none;
2150
+ /* Motion: .bp-transition-colors .bp-duration-normal .bp-ease-out */
2151
+ transition-property: color;
2152
+ transition-duration: var(--bp-duration-normal);
2153
+ transition-timing-function: var(--bp-ease-out);
2154
+ }
2155
+ /* Label | dotted leader | number — flex order puts ::before after link text. */
2156
+ :where(.bp-contents a)::before {
2157
+ content: "";
2158
+ order: 2;
2159
+ flex: 1 1 auto;
2160
+ align-self: center;
2161
+ border-bottom: 1px dotted var(--bp-ink-faint);
2162
+ transition-property: border-color;
2163
+ transition-duration: var(--bp-duration-normal);
2164
+ transition-timing-function: var(--bp-ease-out);
2165
+ }
2166
+ :where(.bp-contents a)::after {
2167
+ content: counter(bp-contents, decimal-leading-zero);
2168
+ order: 3;
2169
+ flex: 0 0 auto;
2170
+ font-size: var(--bp-label-md);
2171
+ font-variant-numeric: tabular-nums;
2172
+ letter-spacing: 0.02em;
2173
+ color: var(--bp-ink-soft);
2174
+ /* Color + a small slide so the dotted leader visibly lengthens on hover. */
2175
+ transition-property: color, transform;
2176
+ transition-duration: var(--bp-duration-normal);
2177
+ transition-timing-function: var(--bp-ease-out);
2178
+ }
2179
+ /* No active/selected state: a contents index at the top of a document has
2180
+ no "current" entry. Hover is the only affordance. */
2181
+ :where(.bp-contents a:hover) {
2182
+ color: var(--bp-ink);
2183
+ background: none;
2184
+ }
2185
+ :where(.bp-contents a:hover)::before {
2186
+ border-bottom-color: var(--bp-ink-soft);
2187
+ }
2188
+ :where(.bp-contents a:hover)::after {
2189
+ color: var(--bp-ink);
2190
+ transform: translateX(var(--bp-space-1));
2191
+ }
2192
+
2193
+ /* Column frame: the shell is pinned between fixed top/bottom insets so the
2194
+ desk margin always shows while content scrolls inside; vertical rules at
2195
+ the column edges run full viewport height; horizontal rules are one
2196
+ continuous full-width line at the shell's top and bottom inset, with a
2197
+ solid top band masking scroll bleed into the margin. Shadows off while
2198
+ testing. */
2199
+ :where(html:has(.bp-shell)) {
2200
+ --bp-shell-inset-block: var(--bp-space-4);
2201
+ --bp-shell-inset-top: var(--bp-shell-inset-block);
2202
+ --bp-shell-inset-left: var(--bp-sidebar);
2203
+ --bp-shell-inset-right: var(--bp-space-4);
2204
+ /* Frame stack order: sidebar content (100) < margin mask < sidebar scrims +
2205
+ scroll-progress (200) < frame rules. The mask hides scrolling rail
2206
+ content in the top band; the rules ride above the scrims so they read as
2207
+ one unbroken hairline across the sidebar and shell. */
2208
+ --bp-shell-mask-z: 150;
2209
+ --bp-frame-line-z: 210;
2210
+ --bp-scroll-margin: var(--bp-space-6);
2211
+ }
2212
+ :where(body:has(.bp-shell)) {
2213
+ overflow: hidden;
2214
+ }
2215
+ :where(.bp-shell) {
2216
+ position: fixed;
2217
+ inset: var(--bp-shell-inset-top) var(--bp-shell-inset-right)
2218
+ var(--bp-shell-inset-block) var(--bp-shell-inset-left);
2219
+ margin: 0;
2220
+ overflow-x: hidden;
2221
+ overflow-y: auto;
2222
+ overscroll-behavior: contain;
2223
+ -webkit-overflow-scrolling: touch;
2224
+ background: var(--bp-paper);
2225
+ border: 0;
2226
+ border-radius: var(--bp-radius-0);
2227
+ box-shadow: none;
2228
+ }
2229
+ /* TOC / hash targets scroll inside the shell — reserve top air so headings
2230
+ don't land flush against the inner edge. */
2231
+ :where(.bp-shell section[id]) {
2232
+ scroll-margin-top: var(--bp-scroll-margin);
2233
+ }
2234
+ :where(html:has(.bp-shell))::before {
2235
+ content: "";
2236
+ position: fixed;
2237
+ inset-inline: 0;
2238
+ top: var(--bp-shell-inset-top);
2239
+ height: 0;
2240
+ border-top: var(--bp-stroke) solid var(--bp-edge);
2241
+ pointer-events: none;
2242
+ z-index: var(--bp-frame-line-z);
2243
+ }
2244
+ :where(html:has(.bp-shell))::after {
2245
+ content: "";
2246
+ position: fixed;
2247
+ inset-block: 0;
2248
+ inset-inline: 0;
2249
+ pointer-events: none;
2250
+ z-index: var(--bp-frame-line-z);
2251
+ background-image:
2252
+ linear-gradient(var(--bp-edge), var(--bp-edge)),
2253
+ linear-gradient(var(--bp-edge), var(--bp-edge));
2254
+ background-size: var(--bp-stroke) 100%;
2255
+ background-repeat: no-repeat;
2256
+ background-position:
2257
+ var(--bp-shell-inset-left) 0,
2258
+ calc(100% - var(--bp-shell-inset-right)) 0;
2259
+ }
2260
+ /* Top inset: solid desk band masks scroll bleed; frame lines sit above rail
2261
+ scrims so verticals run edge-to-edge through the horizontals. Bottom: line
2262
+ only. */
2263
+ :where(body:has(.bp-shell))::before {
2264
+ content: "";
2265
+ position: fixed;
2266
+ inset-inline: 0;
2267
+ top: 0;
2268
+ height: var(--bp-shell-inset-top);
2269
+ background: var(--bp-bg);
2270
+ pointer-events: none;
2271
+ z-index: var(--bp-shell-mask-z);
2272
+ }
2273
+ :where(body:has(.bp-shell))::after {
2274
+ content: "";
2275
+ position: fixed;
2276
+ inset-inline: 0;
2277
+ height: 0;
2278
+ border-top: var(--bp-stroke) solid var(--bp-edge);
2279
+ pointer-events: none;
2280
+ z-index: var(--bp-frame-line-z);
2281
+ bottom: var(--bp-shell-inset-block);
2282
+ }
2283
+ :where(.scroll-progress) {
2284
+ position: fixed;
2285
+ top: 0;
2286
+ left: 0;
2287
+ right: 0;
2288
+ height: 2px;
2289
+ background: var(--bp-ink);
2290
+ transform: scaleX(0);
2291
+ transform-origin: left;
2292
+ z-index: 200;
2293
+ /* Motion: linear (constant motion — scroll-progress follower). Bumped
2294
+ from a sub-token 50ms to the instant token; the bar tracks scroll so
2295
+ it stays effectively immediate. */
2296
+ transition-property: transform;
2297
+ transition-duration: var(--bp-duration-instant);
2298
+ transition-timing-function: var(--bp-ease-linear);
2299
+ }
2300
+ }
2301
+
2302
+ /* Stored Blueprint shell contracts. New documents use bp-* components. */
2303
+ @layer components {
2304
+ :where(.sidebar) {
2305
+ position: fixed;
2306
+ inset: 0 auto 0 0;
2307
+ width: var(--sidebar);
2308
+ height: 100vh;
2309
+ overflow-y: auto;
2310
+ padding: 34px 20px 34px 26px;
2311
+ z-index: 100;
2312
+ }
2313
+ :where(.sidebar-head) {
2314
+ padding-bottom: 16px;
2315
+ border-bottom: 1px solid var(--edge);
2316
+ margin-bottom: 16px;
2317
+ }
2318
+ :where(.sidebar-title) {
2319
+ font: 500 11px/1.4 var(--mono);
2320
+ letter-spacing: 0.12em;
2321
+ text-transform: uppercase;
2322
+ color: var(--ink);
2323
+ }
2324
+ :where(.sidebar-doc) {
2325
+ font: 400 10px/1.4 var(--mono);
2326
+ letter-spacing: 0.08em;
2327
+ text-transform: uppercase;
2328
+ color: var(--text-secondary);
2329
+ margin-top: 6px;
2330
+ }
2331
+ :where(.sidebar ul) {
2332
+ list-style: none;
2333
+ margin: 0;
2334
+ padding: 0;
2335
+ counter-reset: navsec;
2336
+ display: flex;
2337
+ flex-direction: column;
2338
+ }
2339
+ :where(.sidebar li) {
2340
+ margin: 0;
2341
+ padding: 0;
2342
+ }
2343
+ :where(.sidebar a) {
2344
+ position: relative;
2345
+ display: block;
2346
+ font: 400 12px/1.35 var(--sans);
2347
+ color: var(--text-secondary);
2348
+ text-decoration: none;
2349
+ padding: 7px 10px 7px 36px;
2350
+ }
2351
+ :where(.sidebar a)::before {
2352
+ counter-increment: navsec;
2353
+ content: counter(navsec, decimal-leading-zero);
2354
+ position: absolute;
2355
+ left: 10px;
2356
+ top: 8px;
2357
+ font: 400 10px/1 var(--mono);
2358
+ letter-spacing: 0.06em;
2359
+ color: var(--ink-faint);
2360
+ }
2361
+ :where(.sidebar a:hover, .sidebar a[aria-current="location"]) {
2362
+ color: var(--ink);
2363
+ background: var(--fill-amb);
2364
+ }
2365
+ :where(.sidebar a[aria-current="location"])::after {
2366
+ content: "";
2367
+ position: absolute;
2368
+ inset: 0 auto 0 0;
2369
+ width: 2px;
2370
+ background: var(--ink);
2371
+ }
2372
+ :where(.page-body) {
2373
+ margin-left: var(--sidebar);
2374
+ }
2375
+ :where(.sheet) {
2376
+ margin: 0;
2377
+ min-height: 100vh;
2378
+ background: var(--paper);
2379
+ border-left: 1px solid var(--edge);
2380
+ }
2381
+ :where(.hero) {
2382
+ padding: 48px 56px 36px;
2383
+ border-bottom: 1px solid var(--ink-line);
2384
+ font-family: var(--sans);
2385
+ }
2386
+ :where(.hero-meta) {
2387
+ font: 400 11px/1.4 var(--mono);
2388
+ letter-spacing: 0.14em;
2389
+ text-transform: uppercase;
2390
+ color: var(--ink-soft);
2391
+ margin-bottom: 16px;
2392
+ }
2393
+ :where(.hero h1) {
2394
+ font: 600 24px/32px var(--sans);
2395
+ letter-spacing: -0.48px;
2396
+ margin: 10px 0 14px;
2397
+ }
2398
+ :where(.hero-sub) {
2399
+ font: 400 14px/20px var(--serif);
2400
+ text-align: justify;
2401
+ hyphens: auto;
2402
+ }
2403
+ :where(.title-block) {
2404
+ margin-top: 34px;
2405
+ border: 1px solid var(--ink-line);
2406
+ display: flex;
2407
+ flex-wrap: wrap;
2408
+ }
2409
+ :where(.tb-cell) {
2410
+ flex: 0 0 auto;
2411
+ padding: 10px 20px 11px 14px;
2412
+ border-right: 1px solid var(--ink-line);
2413
+ }
2414
+ :where(.tb-cell--grow) {
2415
+ flex: 1 1 auto;
2416
+ }
2417
+ :where(.tb-cell:last-child) {
2418
+ border-right: 0;
2419
+ }
2420
+ :where(.tb-label) {
2421
+ font: 400 10px/1.4 var(--mono);
2422
+ letter-spacing: 0.1em;
2423
+ text-transform: uppercase;
2424
+ color: var(--ink-soft);
2425
+ margin-bottom: 3px;
2426
+ }
2427
+ :where(.tb-value) {
2428
+ font: 400 12px/1.4 var(--sans);
2429
+ color: var(--text);
2430
+ }
2431
+ :where(.body) {
2432
+ padding: 8px 56px 56px;
2433
+ }
2434
+ :where(.prose) {
2435
+ max-width: var(--content);
2436
+ margin-inline: auto;
2437
+ }
2438
+ :where(.oq) {
2439
+ display: flex;
2440
+ gap: 12px;
2441
+ padding: 10px 0;
2442
+ border-bottom: 1px solid var(--edge);
2443
+ }
2444
+ :where(.oq-tag) {
2445
+ font: 400 10px/1.4 var(--mono);
2446
+ letter-spacing: 0.08em;
2447
+ text-transform: uppercase;
2448
+ color: var(--ink);
2449
+ border: 1px solid var(--ink-faint);
2450
+ padding: 2px 7px;
2451
+ height: max-content;
2452
+ white-space: nowrap;
2453
+ }
2454
+ :where(.assumption .oq-tag) {
2455
+ color: var(--text-secondary);
2456
+ border-color: var(--edge);
2457
+ }
2458
+ :where(.states) {
2459
+ display: grid;
2460
+ grid-template-columns: repeat(2, minmax(0, 1fr));
2461
+ border: 1px solid var(--edge);
2462
+ margin: 20px 0;
2463
+ }
2464
+ :where(.state) {
2465
+ padding: 14px 16px;
2466
+ border-right: 1px solid var(--edge);
2467
+ border-bottom: 1px solid var(--edge);
2468
+ }
2469
+ :where(.state:nth-child(2n)) {
2470
+ border-right: 0;
2471
+ }
2472
+ :where(.state-name, .states-label) {
2473
+ font: 400 10px/1.4 var(--mono);
2474
+ letter-spacing: 0.08em;
2475
+ text-transform: uppercase;
2476
+ color: var(--ink);
2477
+ }
2478
+ :where(.state-name) {
2479
+ margin-bottom: 6px;
2480
+ }
2481
+ :where(.state-desc) {
2482
+ font-size: 13px;
2483
+ line-height: 18px;
2484
+ color: var(--text-secondary);
2485
+ }
2486
+ :where(.states-label) {
2487
+ margin-top: 26px;
2488
+ }
2489
+ :where(.states-label + .states) {
2490
+ margin-top: 10px;
2491
+ }
2492
+ :where(.figure) {
2493
+ width: fit-content;
2494
+ max-width: 100%;
2495
+ margin: 32px auto;
2496
+ }
2497
+ :where(.figure svg[viewBox]) {
2498
+ display: block;
2499
+ width: auto;
2500
+ max-width: min(100%, var(--bp-diagram-w));
2501
+ height: auto;
2502
+ }
2503
+ :where(.figure svg:not([viewBox])) {
2504
+ display: block;
2505
+ width: auto;
2506
+ max-width: 100%;
2507
+ height: auto;
2508
+ }
2509
+ :where(.fig-node) {
2510
+ fill: var(--paper);
2511
+ stroke: var(--bp-illustration);
2512
+ stroke-width: 1.5;
2513
+ }
2514
+ :where(.fig-node-amb) {
2515
+ fill: var(--bp-illustration-fill);
2516
+ stroke: var(--bp-illustration);
2517
+ stroke-width: 1.5;
2518
+ }
2519
+ :where(.fig-edge) {
2520
+ fill: none;
2521
+ stroke: var(--bp-illustration);
2522
+ stroke-width: 1.5;
2523
+ }
2524
+ :where(.fig-label) {
2525
+ font: 400 11px/1 var(--mono);
2526
+ fill: var(--text);
2527
+ }
2528
+ :where(.fig-label-soft) {
2529
+ font: 400 9px/1 var(--mono);
2530
+ fill: var(--text-secondary);
2531
+ }
2532
+ :where(.fig-cap) {
2533
+ font: 400 10px/1.4 var(--mono);
2534
+ letter-spacing: 0.06em;
2535
+ text-transform: uppercase;
2536
+ color: var(--text-secondary);
2537
+ text-align: center;
2538
+ margin-top: 14px;
2539
+ }
2540
+ }
2541
+
2542
+ /* =====================================================================
2543
+ @layer utilities — last-resort single-purpose helpers + responsive
2544
+ ===================================================================== */
2545
+ @layer utilities {
2546
+ :where(.bp-wide) { max-width: var(--bp-wide); }
2547
+ :where(.bp-center) { text-align: center; }
2548
+ :where(.bp-muted) { color: var(--bp-text-secondary); }
2549
+ :where(.bp-no-print) { /* hidden in print below */ }
2550
+ :where([hidden]) { display: none !important; }
2551
+
2552
+ /* Motion — composable CSS-only transition utilities. Base classes carry the
2553
+ default timing (fast + ease-out); add at most one duration and one easing
2554
+ modifier to override. Each class maps 1:1 to a --bp-duration-*/--bp-ease-*
2555
+ token, and modifiers win over base classes by source order (all zero
2556
+ specificity via :where). Reduced motion is handled globally in @layer
2557
+ reset, so no per-class media query is needed. */
2558
+ :where(.bp-transition) {
2559
+ transition-property: color, background-color, border-color, opacity, transform, box-shadow;
2560
+ transition-duration: var(--bp-duration-fast);
2561
+ transition-timing-function: var(--bp-ease-out);
2562
+ }
2563
+ :where(.bp-transition-colors) {
2564
+ transition-property: color, background-color, border-color;
2565
+ transition-duration: var(--bp-duration-fast);
2566
+ transition-timing-function: var(--bp-ease-out);
2567
+ }
2568
+ :where(.bp-transition-transform) {
2569
+ transition-property: transform;
2570
+ transition-duration: var(--bp-duration-fast);
2571
+ transition-timing-function: var(--bp-ease-out);
2572
+ }
2573
+ :where(.bp-transition-opacity) {
2574
+ transition-property: opacity;
2575
+ transition-duration: var(--bp-duration-fast);
2576
+ transition-timing-function: var(--bp-ease-out);
2577
+ }
2578
+ /* Duration modifiers */
2579
+ :where(.bp-duration-instant) { transition-duration: var(--bp-duration-instant); }
2580
+ :where(.bp-duration-fast) { transition-duration: var(--bp-duration-fast); }
2581
+ :where(.bp-duration-normal) { transition-duration: var(--bp-duration-normal); }
2582
+ :where(.bp-duration-slow) { transition-duration: var(--bp-duration-slow); }
2583
+ :where(.bp-duration-deliberate) { transition-duration: var(--bp-duration-deliberate); }
2584
+ /* Easing modifiers */
2585
+ :where(.bp-ease) { transition-timing-function: var(--bp-ease); }
2586
+ :where(.bp-ease-out) { transition-timing-function: var(--bp-ease-out); }
2587
+ :where(.bp-ease-in-out) { transition-timing-function: var(--bp-ease-in-out); }
2588
+ :where(.bp-ease-standard) { transition-timing-function: var(--bp-ease-standard); }
2589
+ :where(.bp-ease-overshoot) { transition-timing-function: var(--bp-ease-overshoot); }
2590
+ :where(.bp-ease-linear) { transition-timing-function: var(--bp-ease-linear); }
2591
+
2592
+ /* Responsive: keep section navigation reachable while collapsing the rail. */
2593
+ @media (max-width: 860px) {
2594
+ :where(.bp-sidebar, .bp-toc, .sidebar) {
2595
+ position: static;
2596
+ width: auto;
2597
+ height: auto;
2598
+ max-height: 40vh;
2599
+ overflow: auto;
2600
+ padding: var(--bp-space-3);
2601
+ border-right: 0;
2602
+ border-bottom: 1px solid var(--bp-edge);
2603
+ }
2604
+ :where(.bp-sidebar ul, .bp-toc ul, .sidebar ul) {
2605
+ display: flex;
2606
+ gap: var(--bp-space-1);
2607
+ overflow-x: auto;
2608
+ padding-bottom: var(--bp-space-1);
2609
+ }
2610
+ :where(.bp-sidebar li, .bp-toc li, .sidebar li) { flex: 0 0 auto; }
2611
+ /* The gliding pill only makes sense in the fixed column; in the collapsed
2612
+ horizontal nav the active entry just shows its own static pill. */
2613
+ :where(.bp-sidebar > ul, .bp-sidebar__panel > ul, .bp-toc > ul)::before { content: none; }
2614
+ :where(.bp-sidebar a[aria-current="location"], .bp-toc a[aria-current="location"]) {
2615
+ background: var(--bp-fill-hi);
2616
+ }
2617
+ /* Rail is now on top; the sheet floats with a uniform margin. */
2618
+ :where(html:has(.bp-shell)) {
2619
+ --bp-shell-inset-block: var(--bp-space-3);
2620
+ --bp-shell-inset-top: var(--bp-shell-inset-block);
2621
+ --bp-shell-inset-left: var(--bp-space-3);
2622
+ --bp-shell-inset-right: var(--bp-space-3);
2623
+ }
2624
+ :where(.page-body) { margin-left: 0; }
2625
+ }
2626
+ @media (max-width: 700px) {
2627
+ :where(main, article) {
2628
+ padding: var(--bp-space-4) var(--bp-space-3);
2629
+ }
2630
+ /* Edge-to-edge on phones — the float costs too much width here. */
2631
+ :where(body:has(.bp-shell)) {
2632
+ overflow: auto;
2633
+ }
2634
+ :where(.bp-shell) {
2635
+ position: static;
2636
+ inset: auto;
2637
+ margin: 0;
2638
+ overflow: visible;
2639
+ border: 0;
2640
+ border-radius: var(--bp-radius-0);
2641
+ box-shadow: none;
2642
+ }
2643
+ :where(html:has(.bp-shell))::before,
2644
+ :where(html:has(.bp-shell))::after,
2645
+ :where(body:has(.bp-shell))::before,
2646
+ :where(body:has(.bp-shell))::after {
2647
+ content: none;
2648
+ }
2649
+ :where(.bp-option-grid, .bp-state-grid, .bp-card-grid, .bp-layout) {
2650
+ grid-template-columns: 1fr;
2651
+ }
2652
+ :where(.bp-content) {
2653
+ padding: var(--bp-space-4) var(--bp-space-3);
2654
+ }
2655
+ :where(.hero, .body) {
2656
+ padding-left: var(--bp-space-3);
2657
+ padding-right: var(--bp-space-3);
2658
+ }
2659
+ /* Stacked dl reads as label-over-description on narrow screens */
2660
+ :where(dl) {
2661
+ grid-template-columns: 1fr;
2662
+ row-gap: var(--bp-space-2);
2663
+ }
2664
+ }
2665
+
2666
+ @media print {
2667
+ :where(.bp-sidebar, .bp-toc, .sidebar, .scroll-progress, .bp-no-print) {
2668
+ display: none !important;
2669
+ }
2670
+ :where(.bp-shell, .page-body, .sheet) { margin: 0; border: 0; border-radius: var(--bp-radius-0); box-shadow: none; }
2671
+ :where(body:has(.bp-shell)) {
2672
+ overflow: visible;
2673
+ }
2674
+ :where(.bp-shell) {
2675
+ position: static;
2676
+ inset: auto;
2677
+ overflow: visible;
2678
+ }
2679
+ :where(html:has(.bp-shell))::before,
2680
+ :where(html:has(.bp-shell))::after,
2681
+ :where(body:has(.bp-shell))::before,
2682
+ :where(body:has(.bp-shell))::after {
2683
+ content: none;
2684
+ }
2685
+ :where(.bp-decision, .bp-callout, .bp-card, .bp-section, .bp-figure, figure, .bp-option-grid, details) {
2686
+ break-inside: avoid;
2687
+ }
2688
+ }
2689
+ }