@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.
- package/LICENSE +21 -0
- package/README.md +443 -0
- package/THIRD_PARTY_NOTICES.md +27 -0
- package/dist/blueprint.css +2689 -0
- package/dist/blueprint.js +416 -0
- package/dist/code-highlighting/blueprint-code.css +406 -0
- package/dist/code-highlighting/blueprint-code.js +19 -0
- package/package.json +52 -0
|
@@ -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
|
+
}
|