@obvi/blueprint 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -176,8 +176,8 @@
176
176
  /* Diagonal hatch — drafting "section lining" for fenced / held /
177
177
  out-of-bounds regions. Faint ink lines over whatever fill the host
178
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. */
179
+ Dark mode overrides --bp-hatch in [data-obvious-theme="dark"].
180
+ --bp-hatch-gap controls the line pitch. */
181
181
  --bp-hatch-gap: 7px;
182
182
  --bp-hatch: repeating-linear-gradient(
183
183
  -45deg,
@@ -365,7 +365,17 @@
365
365
  /* Neutral white-alpha panel fills over the dark paper. */
366
366
  --bp-fill-amb: oklch(1 0 0 / 0.04);
367
367
  --bp-fill-hi: oklch(1 0 0 / 0.08);
368
- --bp-scrim: oklch(1 0 0 / 0.58);
368
+ /* Scrim stays a black wash in dark mode too: a modal scrim must dim the
369
+ page behind a floated panel, not brighten it. White-alpha here washed
370
+ the whole overlay out. */
371
+ --bp-scrim: oklch(0 0 0 / 0.58);
372
+ /* Diagonal hatch (dark) — softer than --bp-ink-faint so scrims and
373
+ rejected-choice fills keep texture without brightening the wash. */
374
+ --bp-hatch: repeating-linear-gradient(
375
+ -45deg,
376
+ var(--bp-edge) 0 var(--bp-stroke),
377
+ oklch(0 0 0 / 0) var(--bp-stroke) var(--bp-hatch-gap)
378
+ );
369
379
  --bp-paper: var(--bp-gray-950);
370
380
  --bp-bg: oklch(0.15 0 0); /* the desk sits darker than the sheet, so the page lifts */
371
381
  --bp-edge: oklch(1 0 0 / 0.08);
@@ -2364,6 +2374,46 @@
2364
2374
  text-transform: uppercase;
2365
2375
  color: var(--bp-text-secondary);
2366
2376
  }
2377
+ :where(.bp-cite-actions) {
2378
+ display: flex;
2379
+ align-items: center;
2380
+ gap: var(--bp-space-2);
2381
+ flex: 0 0 auto;
2382
+ }
2383
+ /* Open-in-pane action. Hidden unless a host advertises a side pane, so the
2384
+ standalone popover (no pane host) never shows a dead control. */
2385
+ :where(.bp-cite-expand) {
2386
+ display: none;
2387
+ align-items: center;
2388
+ justify-content: center;
2389
+ width: 22px;
2390
+ height: 22px;
2391
+ margin: 0;
2392
+ padding: 0;
2393
+ border: var(--bp-stroke) solid var(--bp-edge);
2394
+ border-radius: var(--bp-radius-2);
2395
+ background: var(--bp-fill-amb);
2396
+ color: var(--bp-text-secondary);
2397
+ cursor: pointer;
2398
+ transition:
2399
+ color var(--bp-duration-fast) var(--bp-ease-out),
2400
+ border-color var(--bp-duration-fast) var(--bp-ease-out),
2401
+ background-color var(--bp-duration-fast) var(--bp-ease-out);
2402
+ }
2403
+ :where(html[data-bp-cite-pane] .bp-cite-expand) {
2404
+ display: inline-flex;
2405
+ }
2406
+ :where(.bp-cite-expand:hover),
2407
+ :where(.bp-cite-expand:focus-visible) {
2408
+ color: var(--bp-ink);
2409
+ border-color: var(--bp-ink-line);
2410
+ background: var(--bp-paper);
2411
+ }
2412
+ :where(.bp-cite-expand svg) {
2413
+ display: block;
2414
+ width: 13px;
2415
+ height: 13px;
2416
+ }
2367
2417
  /* Code block inside the card — base treatment; blueprint-code.css makes
2368
2418
  the highlighted variant sit flush and rounds the lower corners. */
2369
2419
  :where(.bp-cite-pop pre) {
@@ -2814,9 +2864,12 @@
2814
2864
  padding-bottom: var(--bp-shell-inset-block);
2815
2865
  }
2816
2866
  /* <main> is the paper sheet spanning the full column between the vertical
2817
- frame rules; direct children keep the prose measure from Tier 1b. */
2867
+ frame rules; direct children keep the prose measure from Tier 1b. Keep the
2868
+ shell out of the overflow/scroll chain so viewport-fixed navigation nested
2869
+ by stored Blueprints is not clipped by WebKit. The page owns scrolling. */
2818
2870
  :where(html[data-bp-document] main) {
2819
2871
  max-width: none;
2872
+ overflow: visible;
2820
2873
  margin: 0 var(--bp-shell-inset-right) 0 var(--bp-shell-inset-left);
2821
2874
  min-height: calc(
2822
2875
  100vh - var(--bp-shell-inset-top) - var(--bp-shell-inset-block)
@@ -4192,3 +4245,1233 @@
4192
4245
  }
4193
4246
  }
4194
4247
  }
4248
+
4249
+ /* Shared document chrome. This stays separate in source so the docs site can
4250
+ iterate on it independently, but the npm build appends it to the default
4251
+ blueprint.css export because blueprint.js injects these runtime elements. */
4252
+
4253
+ :root {
4254
+ --bp-site-nav-height: 44px;
4255
+ }
4256
+
4257
+ /* Only the pages that still use the top bar (e.g. the code demo) reserve
4258
+ space for it. The main docs page drops it in favor of the sidebar
4259
+ blueprint switcher + bottom-left theme toggle below. */
4260
+ body:has(.site-nav) {
4261
+ padding-top: var(--bp-site-nav-height);
4262
+ }
4263
+
4264
+ /* Frame and shell top inset align below the site nav. */
4265
+ html[data-bp-document]:has(.site-nav) {
4266
+ --bp-shell-inset-top: calc(var(--bp-site-nav-height) + var(--bp-shell-inset-block));
4267
+ }
4268
+
4269
+ .site-nav {
4270
+ position: fixed;
4271
+ inset: 0 0 auto;
4272
+ z-index: 400;
4273
+ display: flex;
4274
+ align-items: stretch;
4275
+ height: var(--bp-site-nav-height);
4276
+ padding: 0 16px;
4277
+ border-bottom: 1px solid var(--bp-edge);
4278
+ background: var(--bp-paper);
4279
+ color: var(--bp-text);
4280
+ font-family: var(--bp-mono, var(--mono, ui-monospace, monospace));
4281
+ font-size: 11px;
4282
+ letter-spacing: 0.08em;
4283
+ text-transform: uppercase;
4284
+ }
4285
+
4286
+ .site-nav__brand,
4287
+ .site-nav a {
4288
+ display: flex;
4289
+ align-items: center;
4290
+ min-height: 0;
4291
+ padding: 0 12px;
4292
+ color: var(--bp-text-secondary);
4293
+ text-decoration: none;
4294
+ }
4295
+
4296
+ .site-nav__brand {
4297
+ gap: 8px;
4298
+ padding-left: 0;
4299
+ color: var(--bp-ink);
4300
+ font-weight: 650;
4301
+ letter-spacing: 0.04em;
4302
+ text-transform: none;
4303
+ }
4304
+
4305
+ .site-nav__logo {
4306
+ width: 20px;
4307
+ height: 20px;
4308
+ flex: 0 0 20px;
4309
+ color: var(--bp-ink);
4310
+ }
4311
+
4312
+ .site-nav__links {
4313
+ display: flex;
4314
+ align-items: stretch;
4315
+ gap: 2px;
4316
+ margin: 0 0 0 12px;
4317
+ padding: 0;
4318
+ list-style: none;
4319
+ }
4320
+
4321
+ .site-nav__links > li {
4322
+ display: flex;
4323
+ margin: 0;
4324
+ line-height: 1;
4325
+ }
4326
+
4327
+ .site-nav a:hover,
4328
+ .site-nav a[aria-current="page"] {
4329
+ color: var(--bp-ink);
4330
+ background: var(--bp-fill-amb);
4331
+ }
4332
+
4333
+ .site-nav a:focus-visible,
4334
+ .site-nav button:focus-visible {
4335
+ outline: 2px solid var(--bp-ink);
4336
+ outline-offset: -2px;
4337
+ }
4338
+
4339
+ body > .site-nav .site-nav__theme {
4340
+ position: static;
4341
+ inset: auto;
4342
+ z-index: auto;
4343
+ align-self: center;
4344
+ margin: 0 0 0 auto;
4345
+ padding: 6px 10px;
4346
+ border: 1px solid var(--bp-ink);
4347
+ border-radius: var(--bp-radius-0, 0);
4348
+ background: oklch(0 0 0 / 0);
4349
+ color: var(--bp-ink);
4350
+ font-family: inherit;
4351
+ font-size: inherit;
4352
+ letter-spacing: inherit;
4353
+ line-height: 1;
4354
+ text-transform: uppercase;
4355
+ cursor: pointer;
4356
+ }
4357
+
4358
+ body > .site-nav .site-nav__theme:hover {
4359
+ background: var(--bp-fill-amb);
4360
+ }
4361
+
4362
+ /* The docs sidebar remains fixed, but begins below the global site nav. */
4363
+ .site-nav ~ .bp-sidebar {
4364
+ top: var(--bp-site-nav-height);
4365
+ height: calc(100vh - var(--bp-site-nav-height));
4366
+ }
4367
+
4368
+ .site-nav + .scroll-progress {
4369
+ position: fixed;
4370
+ top: calc(var(--bp-site-nav-height)+ 4px);
4371
+ right: 0;
4372
+ left: 0;
4373
+ height: 2px;
4374
+ background: var(--bp-ink);
4375
+ transform: scaleX(0);
4376
+ transform-origin: left;
4377
+ z-index: 500;
4378
+ pointer-events: none;
4379
+ transition: transform var(--bp-duration-instant) var(--bp-ease-linear);
4380
+ }
4381
+
4382
+ @media (max-width: 560px) {
4383
+ :root {
4384
+ --bp-site-nav-height: 40px;
4385
+ }
4386
+
4387
+ .site-nav {
4388
+ padding: 0 8px;
4389
+ }
4390
+
4391
+ .site-nav__brand {
4392
+ padding: 0 8px 0 0;
4393
+ }
4394
+
4395
+ .site-nav__wordmark {
4396
+ display: none;
4397
+ }
4398
+
4399
+ .site-nav__links {
4400
+ margin-left: 0;
4401
+ }
4402
+
4403
+ .site-nav a {
4404
+ padding: 0 8px;
4405
+ }
4406
+
4407
+ body > .site-nav .site-nav__theme {
4408
+ padding: 6px 8px;
4409
+ }
4410
+ }
4411
+
4412
+ /* =====================================================================
4413
+ Blueprint collection switcher (sidebar header) + bottom-left theme toggle
4414
+ ---------------------------------------------------------------------
4415
+ Docs chrome that replaces the old top bar: the sidebar wordmark becomes
4416
+ a menu for jumping between blueprints, and the theme control becomes a
4417
+ line icon pinned to the bottom-left corner.
4418
+ ===================================================================== */
4419
+
4420
+ /* Reset the library <details>/<summary> chrome for the switcher. */
4421
+ .doc-switcher {
4422
+ position: relative;
4423
+ margin: 0 0 var(--bp-space-3);
4424
+ padding: 0;
4425
+ border: 0;
4426
+ background: none;
4427
+ }
4428
+ /* Shared field chrome for the switcher trigger and the search input so they
4429
+ read as one control family (border, radius, surface + hover/focus states). */
4430
+ .sidebar-field {
4431
+ border: 1px solid var(--bp-edge);
4432
+ border-radius: var(--bp-radius-6, 6px);
4433
+ background: var(--bp-paper);
4434
+ /* Hover-only color motion — idle fields snap on theme flip so the root
4435
+ view-transition crossfade isn't fighting per-field token tweens. */
4436
+ transition: none;
4437
+ }
4438
+ .sidebar-field:hover,
4439
+ .sidebar-field:focus-within {
4440
+ border-color: var(--bp-ink-line);
4441
+ background: var(--bp-fill-amb);
4442
+ transition:
4443
+ border-color var(--bp-duration-fast) var(--bp-ease-out),
4444
+ background-color var(--bp-duration-fast) var(--bp-ease-out);
4445
+ }
4446
+ .doc-switcher__current {
4447
+ display: flex;
4448
+ align-items: center;
4449
+ justify-content: space-between;
4450
+ gap: var(--bp-space-3);
4451
+ margin: 0;
4452
+ /* Match the sidebar toggle box so the two controls align on one row. */
4453
+ height: var(--bp-sidebar-toggle-size);
4454
+ padding: 0 var(--bp-space-3);
4455
+ list-style: none;
4456
+ font-weight: 400;
4457
+ cursor: pointer;
4458
+ }
4459
+ .doc-switcher__current::-webkit-details-marker {
4460
+ display: none;
4461
+ }
4462
+ .doc-switcher__current::before {
4463
+ content: none !important;
4464
+ }
4465
+ .doc-switcher__label {
4466
+ display: flex;
4467
+ flex-direction: column;
4468
+ gap: var(--bp-space-1);
4469
+ min-width: 0;
4470
+ }
4471
+ .doc-switcher__title {
4472
+ font-size: var(--bp-text-small);
4473
+ line-height: 1.3;
4474
+ font-weight: var(--bp-weight-medium);
4475
+ color: var(--bp-text);
4476
+ }
4477
+ .doc-switcher__meta {
4478
+ display: none;
4479
+ }
4480
+ .doc-switcher__chevron {
4481
+ display: inline-flex;
4482
+ flex: 0 0 auto;
4483
+ color: var(--bp-text-secondary);
4484
+ }
4485
+ .doc-switcher[open] .doc-switcher__chevron {
4486
+ transform: rotate(180deg);
4487
+ }
4488
+ .doc-switcher__menu {
4489
+ position: absolute;
4490
+ left: 0;
4491
+ right: 0;
4492
+ top: calc(100% + var(--bp-space-1));
4493
+ z-index: 300;
4494
+ display: flex;
4495
+ flex-direction: column;
4496
+ gap: var(--bp-space-1);
4497
+ padding: var(--bp-space-1);
4498
+ border: 1px solid var(--bp-edge);
4499
+ /* Container radius − padding = item radius, so the rows nest perfectly. */
4500
+ border-radius: var(--bp-radius-8, 8px);
4501
+ background: var(--bp-paper);
4502
+ box-shadow: var(--bp-shadow-pop);
4503
+ transform-origin: top center;
4504
+ }
4505
+ /* Open animation: keyframes re-run every time [open] is set on the <details>,
4506
+ unlike @starting-style which only fires on first render. Close stays instant
4507
+ (native <details> hides content immediately). Timing uses motion tokens. */
4508
+ .doc-switcher[open] .doc-switcher__menu {
4509
+ animation: bp-doc-switcher-in var(--bp-duration-normal) var(--bp-ease-out);
4510
+ }
4511
+ @keyframes bp-doc-switcher-in {
4512
+ from {
4513
+ opacity: 0;
4514
+ transform: translateY(-4px) scale(0.98);
4515
+ }
4516
+ to {
4517
+ opacity: 1;
4518
+ transform: none;
4519
+ }
4520
+ }
4521
+ .doc-switcher__menu a {
4522
+ display: flex;
4523
+ align-items: center;
4524
+ justify-content: space-between;
4525
+ gap: var(--bp-space-2);
4526
+ padding: var(--bp-space-2) var(--bp-space-3);
4527
+ border-radius: var(--bp-radius-4, 4px);
4528
+ text-decoration: none;
4529
+ }
4530
+ .doc-switcher__menu a:hover {
4531
+ background: var(--bp-fill-amb);
4532
+ }
4533
+ .doc-switcher__menu a.is-current {
4534
+ background: var(--bp-fill-hi);
4535
+ }
4536
+ .doc-switcher__option {
4537
+ display: flex;
4538
+ flex-direction: column;
4539
+ min-width: 0;
4540
+ }
4541
+ .doc-switcher__name {
4542
+ font-size: var(--bp-text-small);
4543
+ line-height: 1.3;
4544
+ font-weight: var(--bp-weight-medium);
4545
+ color: var(--bp-text);
4546
+ }
4547
+ .doc-switcher__desc {
4548
+ overflow: hidden;
4549
+ font-size: var(--bp-label-md);
4550
+ line-height: 1.4;
4551
+ color: var(--bp-text-secondary);
4552
+ white-space: nowrap;
4553
+ text-overflow: ellipsis;
4554
+ }
4555
+ /* Reused chevron as a quiet "go to" affordance — only on the current and
4556
+ hovered rows so the resting menu stays clean and condensed. */
4557
+ .doc-switcher__go {
4558
+ flex: 0 0 auto;
4559
+ display: inline-flex;
4560
+ color: var(--bp-text-secondary);
4561
+ opacity: 0;
4562
+ transform: rotate(-90deg);
4563
+ }
4564
+ .doc-switcher__menu a:hover .doc-switcher__go,
4565
+ .doc-switcher__menu a.is-current .doc-switcher__go {
4566
+ opacity: 1;
4567
+ }
4568
+
4569
+ /* Sidebar search — header row beside the corner toggle, above the header scrim. */
4570
+ .sidebar-search {
4571
+ position: relative;
4572
+ /* Sit above the header scrim (z 200) so the field stays crisp while the TOC
4573
+ fades behind it. */
4574
+ z-index: 300;
4575
+ /* Left margin clears the fixed corner toggle so search sits on the same row,
4576
+ beside it. Right edge stops at the content edge so it lines up with the
4577
+ TOC entry pills below. */
4578
+ margin: calc(-1 * var(--bp-space-2)) 0 var(--bp-space-3)
4579
+ calc(var(--bp-sidebar-toggle-size) + var(--bp-space-3));
4580
+ }
4581
+ /* Trigger button: magnifier + label + ⌘K hint on one row. It is a <button>
4582
+ (it opens the search dialog, it is not a text field); the .sidebar-field
4583
+ chrome carries the border/radius/surface and children align inside it. */
4584
+ .sidebar-search__field {
4585
+ display: flex;
4586
+ align-items: center;
4587
+ gap: var(--bp-space-2);
4588
+ width: 100%;
4589
+ height: var(--bp-sidebar-toggle-size);
4590
+ padding: 0 var(--bp-space-3);
4591
+ font: inherit;
4592
+ text-align: left;
4593
+ color: inherit;
4594
+ cursor: pointer;
4595
+ -webkit-appearance: none;
4596
+ appearance: none;
4597
+ }
4598
+ .sidebar-search__field:focus-visible {
4599
+ outline: 2px solid var(--bp-ink);
4600
+ outline-offset: 2px;
4601
+ }
4602
+ .sidebar-search__icon {
4603
+ flex: 0 0 auto;
4604
+ display: inline-flex;
4605
+ color: var(--bp-text-secondary);
4606
+ }
4607
+ .sidebar-search__label {
4608
+ flex: 1 1 auto;
4609
+ min-width: 0;
4610
+ font-family: var(--bp-sans);
4611
+ font-size: var(--bp-text-small);
4612
+ line-height: 1.3;
4613
+ font-weight: 400;
4614
+ color: var(--bp-text-secondary);
4615
+ }
4616
+ /* ⌘K affordance — quiet keycaps, sized down from the base kbd for the rail. */
4617
+ .sidebar-search__hint {
4618
+ flex: 0 0 auto;
4619
+ display: inline-flex;
4620
+ gap: 2px;
4621
+ }
4622
+ .sidebar-search__hint kbd {
4623
+ padding: 0 4px;
4624
+ border-width: 1px;
4625
+ border-radius: var(--bp-radius-2, 2px);
4626
+ font-size: var(--bp-label-md);
4627
+ line-height: 1.5;
4628
+ color: var(--bp-text-secondary);
4629
+ }
4630
+
4631
+ /* =====================================================================
4632
+ Search command menu (docs chrome) — Mintlify-style palette
4633
+ ---------------------------------------------------------------------
4634
+ Opened from the sidebar trigger or ⌘K / Ctrl+K (see wireSearchPalette).
4635
+ Reuses the rounded sidebar control family: paper surface, --bp-radius-8
4636
+ panel with --bp-shadow-pop, --bp-radius-4 rows, the --bp-fill-amb /
4637
+ --bp-fill-hi hover + selected surfaces, and the shared open animation.
4638
+ Chrome stays on the neutral ink scale. Query filtering is a plain
4639
+ substring match over the page's headings; richer search is deferred.
4640
+ ===================================================================== */
4641
+
4642
+ /* Modal scrim. Hidden until .is-open; centers the panel near the top of the
4643
+ viewport, the way command palettes sit. Sits above all docs chrome. Uses
4644
+ the same drafting scrim as .bp-lightbox (--bp-scrim + --bp-hatch). */
4645
+ .docs-search-overlay {
4646
+ position: fixed;
4647
+ inset: 0;
4648
+ z-index: 600;
4649
+ display: none;
4650
+ justify-content: center;
4651
+ align-items: flex-start;
4652
+ padding: clamp(var(--bp-space-5), 12vh, var(--bp-space-7)) var(--bp-space-4)
4653
+ var(--bp-space-4);
4654
+ background-color: var(--bp-scrim);
4655
+ background-image: var(--bp-hatch);
4656
+ }
4657
+ .docs-search-overlay.is-open {
4658
+ display: flex;
4659
+ animation: bp-search-overlay-in var(--bp-duration-fast, 120ms)
4660
+ var(--bp-ease-out);
4661
+ }
4662
+ .docs-search-overlay.is-open .docs-search {
4663
+ animation: bp-doc-switcher-in var(--bp-duration-normal) var(--bp-ease-out);
4664
+ }
4665
+ @keyframes bp-search-overlay-in {
4666
+ from {
4667
+ opacity: 0;
4668
+ }
4669
+ to {
4670
+ opacity: 1;
4671
+ }
4672
+ }
4673
+
4674
+ /* The panel. Position-agnostic: the overlay wrapper centers it over a scrim. */
4675
+ .docs-search {
4676
+ display: flex;
4677
+ flex-direction: column;
4678
+ width: min(640px, 100%);
4679
+ max-height: min(560px, 80vh);
4680
+ overflow: hidden;
4681
+ border: 1px solid var(--bp-edge);
4682
+ border-radius: var(--bp-radius-8, 8px);
4683
+ background: var(--bp-paper);
4684
+ box-shadow: var(--bp-shadow-pop);
4685
+ }
4686
+
4687
+ /* Search row */
4688
+ .docs-search__field {
4689
+ display: flex;
4690
+ align-items: center;
4691
+ gap: var(--bp-space-3);
4692
+ padding: var(--bp-space-3) var(--bp-space-4);
4693
+ border-bottom: 1px solid var(--bp-edge);
4694
+ }
4695
+ .docs-search__icon {
4696
+ flex: 0 0 auto;
4697
+ display: inline-flex;
4698
+ color: var(--bp-text-secondary);
4699
+ }
4700
+ .docs-search__input {
4701
+ flex: 1 1 auto;
4702
+ min-width: 0;
4703
+ margin: 0;
4704
+ padding: 0;
4705
+ border: 0;
4706
+ background: none;
4707
+ font-family: var(--bp-sans);
4708
+ font-size: var(--bp-text-body);
4709
+ line-height: var(--bp-lh-body);
4710
+ color: var(--bp-text);
4711
+ -webkit-appearance: none;
4712
+ appearance: none;
4713
+ }
4714
+ .docs-search__input::placeholder {
4715
+ color: var(--bp-text-secondary);
4716
+ }
4717
+ .docs-search__input:focus {
4718
+ outline: none;
4719
+ }
4720
+ .docs-search__input::-webkit-search-decoration,
4721
+ .docs-search__input::-webkit-search-cancel-button {
4722
+ -webkit-appearance: none;
4723
+ }
4724
+ .docs-search__esc {
4725
+ flex: 0 0 auto;
4726
+ padding: 1px 6px;
4727
+ font-size: var(--bp-label-md);
4728
+ letter-spacing: var(--bp-label-md-ls);
4729
+ text-transform: uppercase;
4730
+ color: var(--bp-text-secondary);
4731
+ }
4732
+
4733
+ /* Results region */
4734
+ .docs-search__results {
4735
+ flex: 1 1 auto;
4736
+ overflow-y: auto;
4737
+ padding: var(--bp-space-2);
4738
+ }
4739
+ /* Group label — machine voice (mono + uppercase) with a trailing count. */
4740
+ .docs-search__group {
4741
+ display: flex;
4742
+ align-items: center;
4743
+ gap: var(--bp-space-2);
4744
+ padding: var(--bp-space-2) var(--bp-space-3) var(--bp-space-1);
4745
+ font-family: var(--bp-mono);
4746
+ font-size: var(--bp-label-md);
4747
+ letter-spacing: var(--bp-label-md-ls);
4748
+ text-transform: uppercase;
4749
+ color: var(--bp-text-secondary);
4750
+ }
4751
+ .docs-search__group-count {
4752
+ color: var(--bp-text-secondary);
4753
+ opacity: 0.7;
4754
+ }
4755
+
4756
+ /* A result row: breadcrumb · title · snippet, with a trailing return key.
4757
+ Rows are <a> for navigation; suppress the base prose link underline except
4758
+ on the selected/hovered surface (background carries the affordance). */
4759
+ .docs-search__row {
4760
+ display: grid;
4761
+ grid-template-columns: 1fr auto;
4762
+ align-items: center;
4763
+ gap: var(--bp-space-1) var(--bp-space-3);
4764
+ padding: var(--bp-space-2) var(--bp-space-3);
4765
+ border-radius: var(--bp-radius-4, 4px);
4766
+ cursor: pointer;
4767
+ text-decoration: none;
4768
+ color: inherit;
4769
+ }
4770
+ .docs-search__row:hover {
4771
+ background: var(--bp-fill-amb);
4772
+ text-decoration: none;
4773
+ }
4774
+ /* Keyboard-selected row — reuses the rail's "current" surface. */
4775
+ .docs-search__row[aria-selected="true"] {
4776
+ background: var(--bp-fill-hi);
4777
+ text-decoration: none;
4778
+ }
4779
+ .docs-search__row-main {
4780
+ display: flex;
4781
+ flex-direction: column;
4782
+ gap: var(--bp-space-1);
4783
+ min-width: 0;
4784
+ }
4785
+ .docs-search__crumb {
4786
+ display: block;
4787
+ font-family: var(--bp-mono);
4788
+ font-size: var(--bp-label-md);
4789
+ letter-spacing: var(--bp-label-md-ls);
4790
+ color: var(--bp-text-secondary);
4791
+ white-space: nowrap;
4792
+ overflow: hidden;
4793
+ text-overflow: ellipsis;
4794
+ }
4795
+ .docs-search__title {
4796
+ display: flex;
4797
+ align-items: baseline;
4798
+ gap: var(--bp-space-1);
4799
+ min-width: 0;
4800
+ font-family: var(--bp-sans);
4801
+ font-size: var(--bp-text-small);
4802
+ line-height: 1.3;
4803
+ font-weight: var(--bp-weight-medium);
4804
+ color: var(--bp-text);
4805
+ }
4806
+ /* The "#" anchor glyph borrows the machine voice so it reads as a heading
4807
+ link, not prose. */
4808
+ .docs-search__hash {
4809
+ flex: 0 0 auto;
4810
+ font-family: var(--bp-mono);
4811
+ color: var(--bp-text-secondary);
4812
+ }
4813
+ .docs-search__snippet {
4814
+ overflow: hidden;
4815
+ font-size: var(--bp-text-small);
4816
+ line-height: 1.4;
4817
+ color: var(--bp-text-secondary);
4818
+ white-space: nowrap;
4819
+ text-overflow: ellipsis;
4820
+ }
4821
+ .docs-search__snippet mark {
4822
+ padding: 0;
4823
+ background: var(--bp-highlight);
4824
+ color: var(--bp-text);
4825
+ }
4826
+ /* Return-key affordance — only on the selected/hovered row, like the
4827
+ switcher's quiet "go to" chevron. */
4828
+ .docs-search__enter {
4829
+ flex: 0 0 auto;
4830
+ align-self: center;
4831
+ padding: 1px 5px;
4832
+ font-size: var(--bp-label-md);
4833
+ color: var(--bp-text-secondary);
4834
+ opacity: 0;
4835
+ }
4836
+ .docs-search__row:hover .docs-search__enter,
4837
+ .docs-search__row[aria-selected="true"] .docs-search__enter {
4838
+ opacity: 1;
4839
+ }
4840
+
4841
+ /* Empty state */
4842
+ .docs-search__empty {
4843
+ display: flex;
4844
+ flex-direction: column;
4845
+ align-items: center;
4846
+ gap: var(--bp-space-2);
4847
+ padding: var(--bp-space-6) var(--bp-space-4);
4848
+ text-align: center;
4849
+ }
4850
+ .docs-search__empty-icon {
4851
+ display: inline-flex;
4852
+ color: var(--bp-text-secondary);
4853
+ opacity: 0.6;
4854
+ }
4855
+ .docs-search__empty-title {
4856
+ font-family: var(--bp-sans);
4857
+ font-size: var(--bp-text-small);
4858
+ font-weight: var(--bp-weight-medium);
4859
+ color: var(--bp-text);
4860
+ }
4861
+ .docs-search__empty-note {
4862
+ font-size: var(--bp-text-small);
4863
+ color: var(--bp-text-secondary);
4864
+ }
4865
+
4866
+ /* Footer keyboard legend */
4867
+ .docs-search__foot {
4868
+ display: flex;
4869
+ flex-wrap: wrap;
4870
+ align-items: center;
4871
+ justify-content: space-between;
4872
+ gap: var(--bp-space-1) var(--bp-space-3);
4873
+ padding: var(--bp-space-2) var(--bp-space-4);
4874
+ border-top: 1px solid var(--bp-edge);
4875
+ font-family: var(--bp-mono);
4876
+ font-size: var(--bp-label-md);
4877
+ letter-spacing: var(--bp-label-md-ls);
4878
+ color: var(--bp-text-secondary);
4879
+ }
4880
+ .docs-search__keys {
4881
+ display: flex;
4882
+ flex-wrap: wrap;
4883
+ align-items: center;
4884
+ gap: var(--bp-space-1) var(--bp-space-3);
4885
+ }
4886
+ .docs-search__key {
4887
+ display: inline-flex;
4888
+ align-items: center;
4889
+ gap: var(--bp-space-1);
4890
+ }
4891
+ .docs-search__foot kbd {
4892
+ padding: 0 4px;
4893
+ border-width: 1px;
4894
+ border-radius: var(--bp-radius-2, 2px);
4895
+ font-size: var(--bp-label-md);
4896
+ line-height: 1.5;
4897
+ color: var(--bp-text-secondary);
4898
+ }
4899
+
4900
+ /* Sidebar footer scrim token. The footer (theme switch + author/date) gets a
4901
+ soft fade above it so the scrollable TOC dissolves into the rail rather than
4902
+ colliding with the footer. The bottom band stays solid to back the footer.
4903
+ Interpolating `in oklch` is premultiplied, so a plain `oklch(0 0 0 / 0)` stop
4904
+ fades cleanly (the zero-alpha endpoint contributes no colour, no muddy
4905
+ mid-tone) — and our rail bg is achromatic, so no same-colour stop is needed.
4906
+ The plain gradient syntax stays as the fallback for engines without oklch
4907
+ gradient interpolation. */
4908
+ :root {
4909
+ /* Shared square control size for the corner toggle, doc-switcher trigger,
4910
+ theme switch, and scrim geometry — one token keeps every calc in sync. */
4911
+ --bp-sidebar-toggle-size: 30px;
4912
+ /* Content column inset — toggle, TOC pills, switcher, and footer share one
4913
+ left/right edge so the rail reads as an even column. */
4914
+ --bp-sidebar-content-inset: calc(var(--bp-space-4) + var(--bp-space-3));
4915
+ /* Frame line → chrome row: same gap below the top line and above the bottom. */
4916
+ --bp-sidebar-chrome-gap: var(--bp-space-3);
4917
+ --bp-sidebar-chrome-inset-top: calc(
4918
+ var(--bp-shell-inset-top, var(--bp-space-4)) + var(--bp-sidebar-chrome-gap)
4919
+ );
4920
+ --bp-sidebar-chrome-inset-bottom: calc(
4921
+ var(--bp-shell-inset-block, var(--bp-space-4)) + var(--bp-sidebar-chrome-gap)
4922
+ );
4923
+ --bp-sidebar-footer: calc(
4924
+ var(--bp-sidebar-chrome-inset-bottom) + var(--bp-sidebar-toggle-size)
4925
+ );
4926
+ /* Scroll clearance: footer band + breathing room for the last TOC pill. */
4927
+ --bp-sidebar-scroll-padding-bottom: calc(
4928
+ var(--bp-sidebar-footer) + var(--bp-space-4)
4929
+ );
4930
+ --bp-sidebar-fade: linear-gradient(
4931
+ to top,
4932
+ var(--bp-bg) var(--bp-sidebar-footer),
4933
+ oklch(0 0 0 / 0)
4934
+ );
4935
+
4936
+ /* Header counterpart: a circular scrim centred on the corner toggle so the
4937
+ TOC dissolves *around* the icon as it scrolls up under it — radiating from
4938
+ the icon rather than a flat top edge. Centre = toggle inset + half its box.
4939
+ Solid out to --bp-space-4, then fades over --bp-space-6. */
4940
+ --bp-sidebar-header-scrim: calc(
4941
+ var(--bp-sidebar-icon-center) + var(--bp-space-4) + var(--bp-space-6)
4942
+ );
4943
+ --bp-sidebar-icon-center: calc(
4944
+ var(--bp-sidebar-chrome-inset-top) + var(--bp-sidebar-toggle-size) / 2
4945
+ );
4946
+ --bp-sidebar-icon-fade: radial-gradient(
4947
+ circle at var(--bp-sidebar-icon-center) var(--bp-sidebar-icon-center),
4948
+ var(--bp-bg) var(--bp-space-4),
4949
+ oklch(0 0 0 / 0) calc(var(--bp-space-4) + var(--bp-space-6))
4950
+ );
4951
+ }
4952
+ @supports (
4953
+ background:
4954
+ linear-gradient(in oklch, oklch(0.5 0.1 30), oklch(0.5 0.1 240))
4955
+ ) {
4956
+ :root {
4957
+ --bp-sidebar-fade: linear-gradient(
4958
+ in oklch to top,
4959
+ var(--bp-bg) var(--bp-sidebar-footer),
4960
+ oklch(0 0 0 / 0)
4961
+ );
4962
+ --bp-sidebar-icon-fade: radial-gradient(
4963
+ in oklch circle at var(--bp-sidebar-icon-center)
4964
+ var(--bp-sidebar-icon-center),
4965
+ var(--bp-bg) var(--bp-space-4),
4966
+ oklch(0 0 0 / 0) calc(var(--bp-space-4) + var(--bp-space-6))
4967
+ );
4968
+ }
4969
+ }
4970
+
4971
+ /* Sidebar footer: the theme switch and the author/date block read as two flex
4972
+ items with space between them, spanning the same band as the TOC entries
4973
+ (left edge on the pill, right edge at the rail's content edge). */
4974
+ .sidebar-footer {
4975
+ display: flex;
4976
+ align-items: center;
4977
+ justify-content: space-between;
4978
+ gap: var(--bp-space-3);
4979
+ }
4980
+
4981
+ /* Author / date metadata — grouped as the footer's right-hand flex item. */
4982
+ .sidebar-meta {
4983
+ display: flex;
4984
+ flex-direction: row;
4985
+ align-items: center;
4986
+ gap: var(--bp-space-4);
4987
+ }
4988
+ .sidebar-meta__item {
4989
+ display: flex;
4990
+ flex-direction: column;
4991
+ gap: 0;
4992
+ }
4993
+ .sidebar-meta__item .bp-meta {
4994
+ margin: 0;
4995
+ line-height: 1.25;
4996
+ }
4997
+ .sidebar-meta__item .bp-note {
4998
+ margin: 0;
4999
+ line-height: 1.35;
5000
+ }
5001
+
5002
+ /* Leave room at the bottom of the rail so the last entry clears the footer. */
5003
+ .bp-sidebar {
5004
+ --bp-theme-toggle-height: var(--bp-sidebar-toggle-size);
5005
+ padding-bottom: var(--bp-sidebar-scroll-padding-bottom);
5006
+ }
5007
+
5008
+ /* Bottom-left theme switch (Iconoir half-moon / light-bulb). Scoped to the
5009
+ sidebar so the code demo's in-nav theme button is unaffected. */
5010
+ .bp-sidebar .theme-toggle {
5011
+ /* A flex child of .sidebar-footer; relative so the thumb anchors to it. */
5012
+ position: relative;
5013
+ flex: 0 0 auto;
5014
+ display: grid;
5015
+ grid-template-columns: 1fr 1fr;
5016
+ align-items: center;
5017
+ width: 54px;
5018
+ height: var(--bp-theme-toggle-height);
5019
+ padding: 2px;
5020
+ border: 1px solid var(--bp-edge);
5021
+ border-radius: var(--bp-radius-6, 6px);
5022
+ background: var(--bp-fill-amb);
5023
+ color: var(--bp-text-secondary);
5024
+ cursor: pointer;
5025
+ transition: none;
5026
+ }
5027
+ .bp-sidebar .theme-toggle:hover {
5028
+ border-color: var(--bp-ink-line);
5029
+ background: var(--bp-paper);
5030
+ transition:
5031
+ border-color var(--bp-duration-fast) var(--bp-ease-out),
5032
+ background-color var(--bp-duration-fast) var(--bp-ease-out),
5033
+ color var(--bp-duration-fast) var(--bp-ease-out);
5034
+ }
5035
+ .bp-sidebar .theme-toggle:focus-visible {
5036
+ outline: 2px solid var(--bp-ink);
5037
+ outline-offset: 2px;
5038
+ }
5039
+ .bp-sidebar .theme-toggle__thumb {
5040
+ position: absolute;
5041
+ top: 2px;
5042
+ left: 2px;
5043
+ /* height − (2×border + 2×pad) keeps an even 2px gap on every side. */
5044
+ width: calc(var(--bp-theme-toggle-height) - 6px);
5045
+ height: calc(var(--bp-theme-toggle-height) - 6px);
5046
+ border: 1px solid var(--bp-ink-line);
5047
+ border-radius: var(--bp-radius-4, 4px);
5048
+ background: var(--bp-paper);
5049
+ box-shadow: 0 1px 2px oklch(0 0 0 / 0.06);
5050
+ pointer-events: none;
5051
+ }
5052
+ [data-obvious-theme="dark"] .bp-sidebar .theme-toggle__thumb {
5053
+ transform: translateX(24px);
5054
+ }
5055
+ .bp-sidebar .theme-toggle svg {
5056
+ display: block;
5057
+ }
5058
+ .bp-sidebar .theme-toggle__icon {
5059
+ position: relative;
5060
+ z-index: 1;
5061
+ display: flex;
5062
+ align-items: center;
5063
+ justify-content: center;
5064
+ pointer-events: none;
5065
+ }
5066
+ :not([data-obvious-theme="dark"]) .bp-sidebar .theme-toggle__icon--moon,
5067
+ [data-obvious-theme="dark"] .bp-sidebar .theme-toggle__icon--bulb {
5068
+ color: var(--bp-ink);
5069
+ }
5070
+
5071
+ /* =====================================================================
5072
+ Sidebar collapse toggle (Iconoir sidebar glyph, pinned top-left)
5073
+ ---------------------------------------------------------------------
5074
+ Docs chrome that hides/shows the fixed contents rail, like the doc
5075
+ switcher and theme toggle above. It lives on <body> (not inside the
5076
+ rail) so it stays reachable once the rail is gone. The collapsed-state
5077
+ rules are unlayered author CSS, so they win over blueprint.css per the
5078
+ specificity contract. The whole rail slides as one rigid unit — its scrims
5079
+ + footer ride along (no opacity tricks), and the sheet trails one space-4
5080
+ behind the rail's edge the entire way (shared duration + easing).
5081
+ ===================================================================== */
5082
+ .sidebar-toggle {
5083
+ position: fixed;
5084
+ /* Same offset from the top frame line as the footer keeps from the bottom. */
5085
+ top: var(--bp-sidebar-chrome-inset-top);
5086
+ left: var(--bp-sidebar-content-inset);
5087
+ z-index: 320;
5088
+ display: inline-flex;
5089
+ align-items: center;
5090
+ justify-content: center;
5091
+ width: var(--bp-sidebar-toggle-size);
5092
+ height: var(--bp-sidebar-toggle-size);
5093
+ padding: 0;
5094
+ border: 1px solid var(--bp-edge);
5095
+ border-radius: var(--bp-radius-6, 6px);
5096
+ background: var(--bp-fill-amb);
5097
+ color: var(--bp-text-secondary);
5098
+ cursor: pointer;
5099
+ transition: none;
5100
+ }
5101
+ .sidebar-toggle:hover {
5102
+ border-color: var(--bp-ink-line);
5103
+ background: var(--bp-paper);
5104
+ color: var(--bp-ink);
5105
+ transition:
5106
+ border-color var(--bp-duration-fast) var(--bp-ease-out),
5107
+ background-color var(--bp-duration-fast) var(--bp-ease-out),
5108
+ color var(--bp-duration-fast) var(--bp-ease-out);
5109
+ }
5110
+ .sidebar-toggle:focus-visible {
5111
+ outline: 2px solid var(--bp-ink);
5112
+ outline-offset: 2px;
5113
+ }
5114
+ .sidebar-toggle__icons {
5115
+ position: relative;
5116
+ display: block;
5117
+ width: 16px;
5118
+ height: 16px;
5119
+ }
5120
+ .sidebar-toggle__icon {
5121
+ position: absolute;
5122
+ inset: 0;
5123
+ display: flex;
5124
+ align-items: center;
5125
+ justify-content: center;
5126
+ pointer-events: none;
5127
+ }
5128
+ .sidebar-toggle__icon--collapse {
5129
+ opacity: 1;
5130
+ }
5131
+ .sidebar-toggle__icon--expand {
5132
+ opacity: 0;
5133
+ }
5134
+ [data-sidebar="collapsed"] .sidebar-toggle__icon--collapse {
5135
+ opacity: 0;
5136
+ }
5137
+ [data-sidebar="collapsed"] .sidebar-toggle__icon--expand {
5138
+ opacity: 1;
5139
+ }
5140
+ .sidebar-toggle svg {
5141
+ display: block;
5142
+ }
5143
+
5144
+ @media (min-width: 861px) {
5145
+ /* Top padding lives on the panel (space-6 aligns the search row with the
5146
+ fixed corner toggle); zero the shell's own top padding so it isn't added
5147
+ on top, which would push the search below the toggle. */
5148
+ .bp-sidebar {
5149
+ display: flex;
5150
+ flex-direction: column;
5151
+ overflow: hidden;
5152
+ padding-top: 0;
5153
+ padding-bottom: 0;
5154
+ padding-inline: 0;
5155
+ }
5156
+
5157
+ /* The panel scrolls; padding-bottom reserves the fixed footer band so every
5158
+ TOC entry can scroll fully into view above it. scroll-padding-bottom keeps
5159
+ browser-managed scroll (keyboard focus, scrollIntoView, find-in-page) from
5160
+ landing a near-bottom entry under the fixed footer overlay. Horizontal
5161
+ padding aligns the TOC pills with the fixed corner toggle. */
5162
+ .bp-sidebar__panel {
5163
+ flex: 1 1 auto;
5164
+ min-height: 0;
5165
+ height: auto;
5166
+ overflow-x: hidden;
5167
+ overflow-y: auto;
5168
+ overscroll-behavior: contain;
5169
+ -webkit-overflow-scrolling: touch;
5170
+ padding-top: var(--bp-space-6);
5171
+ padding-inline: var(--bp-sidebar-content-inset);
5172
+ padding-bottom: var(--bp-sidebar-scroll-padding-bottom);
5173
+ scroll-padding-bottom: var(--bp-sidebar-scroll-padding-bottom);
5174
+ }
5175
+
5176
+ /* Drop the switcher below the header scrim (search stays above it on z 300). */
5177
+ .doc-switcher {
5178
+ margin-top: max(
5179
+ 0px,
5180
+ calc(
5181
+ var(--bp-sidebar-header-scrim) - var(--bp-space-6) + var(--bp-space-2) -
5182
+ var(--bp-sidebar-toggle-size) - var(--bp-space-3) + var(--bp-space-2)
5183
+ )
5184
+ );
5185
+ }
5186
+
5187
+ /* The rail slides as one unit. At rest transform is none, so the fixed scrims
5188
+ (::before/::after) + footer stay viewport-anchored for the scroll dissolve;
5189
+ during the slide a non-none transform makes the rail their containing block,
5190
+ so they ride along with it (their %-based geometry resolves against the
5191
+ rail's own width, which is identical math — see the footer rule below).
5192
+ Transition only after bp-sidebar-animate is set (index.html) to skip first
5193
+ paint; visibility flips at the end so the rail leaves the tab order. */
5194
+ html.bp-sidebar-animate .bp-sidebar {
5195
+ transition:
5196
+ transform var(--bp-duration-slow) var(--bp-ease-in-out),
5197
+ visibility var(--bp-duration-slow);
5198
+ }
5199
+ [data-sidebar="collapsed"] .bp-sidebar {
5200
+ transform: translateX(-100%);
5201
+ visibility: hidden;
5202
+ pointer-events: none;
5203
+ }
5204
+
5205
+ /* The sheet reclaims the rail's width in step with the slide. <main> is laid
5206
+ out with margin-inline (blueprint.css shell rules), not `left`, so the
5207
+ reclaim must transition margin — transitioning `left` on a static box was a
5208
+ no-op, which made the sheet snap while the rail slid. Same duration/easing
5209
+ as the rail + frame line so all three move as one. Gated on
5210
+ bp-sidebar-animate so it never fires on first paint. */
5211
+ html.bp-sidebar-animate[data-bp-document] main {
5212
+ transition: margin var(--bp-duration-slow) var(--bp-ease-in-out);
5213
+ }
5214
+ html.bp-sidebar-animate[data-bp-document]::after {
5215
+ transition: background-position var(--bp-duration-slow) var(--bp-ease-in-out);
5216
+ }
5217
+
5218
+ /* Footer pins to the bottom of the rail, spanning the TOC band: left edge on
5219
+ the entry pill (content edge) and right edge at the rail's content edge. */
5220
+ .sidebar-footer {
5221
+ position: fixed;
5222
+ left: var(--bp-sidebar-content-inset);
5223
+ right: calc(100% - var(--bp-sidebar) + var(--bp-sidebar-content-inset));
5224
+ bottom: var(--bp-sidebar-chrome-inset-bottom);
5225
+ z-index: 300;
5226
+ }
5227
+
5228
+ /* Fade scrim above the footer: the TOC scrolls out beneath it (z below the
5229
+ footer controls), with a solid bottom band backing the footer. */
5230
+ .bp-sidebar::after {
5231
+ content: "";
5232
+ position: fixed;
5233
+ left: 0;
5234
+ bottom: 0;
5235
+ width: var(--bp-sidebar);
5236
+ height: calc(var(--bp-sidebar-footer) + var(--bp-space-4));
5237
+ background: var(--bp-sidebar-fade);
5238
+ pointer-events: none;
5239
+ z-index: 200;
5240
+ }
5241
+
5242
+ /* Radial scrim around the corner toggle: the TOC dissolves in a circle that
5243
+ radiates from the icon as entries scroll up beneath it. The box is large
5244
+ enough to hold the gradient's outer radius; transparent beyond it. */
5245
+ .bp-sidebar::before {
5246
+ content: "";
5247
+ position: fixed;
5248
+ top: 0;
5249
+ left: 0;
5250
+ width: var(--bp-sidebar);
5251
+ height: var(--bp-sidebar-header-scrim);
5252
+ background: var(--bp-sidebar-icon-fade);
5253
+ pointer-events: none;
5254
+ z-index: 200;
5255
+ }
5256
+
5257
+ /* Collapsed: the rail (slid out via the rule above) vacates its width and the
5258
+ sheet reclaims it, trailing one space-4 behind the rail's edge throughout. */
5259
+ [data-sidebar="collapsed"][data-bp-document] {
5260
+ --bp-shell-inset-left: var(--bp-space-4);
5261
+ }
5262
+ }
5263
+
5264
+ /* Below the rail's fixed breakpoint it becomes a static horizontal nav,
5265
+ where collapsing has no meaning — hide the control entirely. */
5266
+ @media (max-width: 860px) {
5267
+ .sidebar-toggle {
5268
+ display: none;
5269
+ }
5270
+ }
5271
+
5272
+ @media print {
5273
+ body {
5274
+ padding-top: 0;
5275
+ }
5276
+
5277
+ .site-nav,
5278
+ .bp-sidebar .theme-toggle,
5279
+ .sidebar-toggle {
5280
+ display: none;
5281
+ }
5282
+ }
5283
+
5284
+ /* ---------------------------------------------------------------------------
5285
+ Cited-source side pane
5286
+ ---------------------------------------------------------------------------
5287
+ The host surface a <bp-cite> popover promotes its source into (the "open in
5288
+ side pane" action). It is a right COLUMN, not an overlay: opening reflows the
5289
+ sheet left to reclaim a column inside the desk frame, and the pane's
5290
+ full-height left border is the crosshair divider — the same --bp-edge
5291
+ hairline as the frame rules. No scrim; the document stays interactive beside
5292
+ it. Built and toggled by blueprint.js (wireCitePane); reuses the merged
5293
+ detail-pane shell. */
5294
+ :root {
5295
+ --bp-cite-pane-w: min(480px, 78vw);
5296
+ --bp-cite-pane-swap-shift: var(--bp-space-3);
5297
+ }
5298
+
5299
+ /* Reflow the sheet to open the column. The margin transition is supplied by
5300
+ the bp-sidebar-animate rule above (added one frame after paint), so opening
5301
+ animates while first paint never does — the pane is always closed on load. */
5302
+ html[data-bp-document]:has(.bp-cite-pane.is-open) main {
5303
+ margin-right: calc(var(--bp-shell-inset-right) + var(--bp-cite-pane-w));
5304
+ }
5305
+
5306
+ /* Clip port stops at the right frame rule so the parked pane adds no page
5307
+ scroll. Sits above the desk masks but below the frame rules. */
5308
+ .bp-cite-pane-port {
5309
+ position: fixed;
5310
+ top: 0;
5311
+ left: 0;
5312
+ bottom: 0;
5313
+ right: var(--bp-shell-inset-right, var(--bp-space-4));
5314
+ z-index: calc(var(--bp-shell-mask-z, 150) + 10);
5315
+ overflow: hidden;
5316
+ pointer-events: none;
5317
+ }
5318
+
5319
+ .bp-cite-pane {
5320
+ position: absolute;
5321
+ top: var(--bp-shell-inset-top, var(--bp-space-4));
5322
+ bottom: var(--bp-shell-inset-block, var(--bp-space-4));
5323
+ right: 0;
5324
+ width: var(--bp-cite-pane-w);
5325
+ display: flex;
5326
+ flex-direction: column;
5327
+ background: var(--bp-paper);
5328
+ border-left: var(--bp-stroke) solid var(--bp-edge);
5329
+ /* parked just past the clip so it travels exactly its own width to open */
5330
+ transform: translateX(100%);
5331
+ /* ease-in-out matches the shell push so the divider moves as one boundary */
5332
+ transition: transform var(--bp-duration-slow) var(--bp-ease-in-out);
5333
+ pointer-events: auto;
5334
+ }
5335
+ .bp-cite-pane.is-open {
5336
+ transform: translateX(0);
5337
+ }
5338
+ .bp-cite-pane:focus-visible {
5339
+ outline: none;
5340
+ }
5341
+
5342
+ .bp-cite-pane__head {
5343
+ position: relative;
5344
+ padding:
5345
+ var(--bp-sidebar-chrome-gap)
5346
+ var(--bp-sidebar-chrome-gap)
5347
+ var(--bp-sidebar-chrome-gap)
5348
+ var(--bp-space-4);
5349
+ border-bottom: var(--bp-stroke) solid var(--bp-edge);
5350
+ }
5351
+ .bp-cite-pane__heading {
5352
+ display: flex;
5353
+ flex-direction: column;
5354
+ gap: var(--bp-space-1);
5355
+ min-width: 0;
5356
+ padding-right: calc(
5357
+ var(--bp-sidebar-toggle-size) + var(--bp-sidebar-chrome-gap)
5358
+ );
5359
+ }
5360
+ .bp-cite-pane__eyebrow {
5361
+ font-family: var(--bp-mono);
5362
+ font-size: var(--bp-label-md);
5363
+ letter-spacing: var(--bp-label-md-ls);
5364
+ text-transform: uppercase;
5365
+ color: var(--bp-text-secondary);
5366
+ }
5367
+ /* The locator the source was opened from — scrolls back to the citation. */
5368
+ .bp-cite-pane__loc {
5369
+ display: block;
5370
+ width: 100%;
5371
+ margin: 0;
5372
+ padding: 0;
5373
+ border: 0;
5374
+ background: none;
5375
+ text-align: left;
5376
+ font-family: var(--bp-mono);
5377
+ font-size: var(--bp-text-small);
5378
+ line-height: var(--bp-lh-body);
5379
+ color: var(--bp-text);
5380
+ white-space: nowrap;
5381
+ overflow: hidden;
5382
+ text-overflow: ellipsis;
5383
+ cursor: pointer;
5384
+ text-decoration: underline;
5385
+ text-decoration-color: var(--bp-ink-line);
5386
+ text-underline-offset: 0.18em;
5387
+ transition:
5388
+ color var(--bp-duration-fast) var(--bp-ease-out),
5389
+ text-decoration-color var(--bp-duration-fast) var(--bp-ease-out);
5390
+ }
5391
+ .bp-cite-pane__loc:hover,
5392
+ .bp-cite-pane__loc:focus-visible {
5393
+ color: var(--bp-text);
5394
+ text-decoration-color: var(--bp-text);
5395
+ }
5396
+ .bp-cite-pane__loc[hidden] {
5397
+ display: none;
5398
+ }
5399
+ .bp-cite-pane__close {
5400
+ position: absolute;
5401
+ top: var(--bp-sidebar-chrome-gap);
5402
+ right: var(--bp-sidebar-chrome-gap);
5403
+ display: inline-flex;
5404
+ align-items: center;
5405
+ justify-content: center;
5406
+ width: var(--bp-sidebar-toggle-size);
5407
+ height: var(--bp-sidebar-toggle-size);
5408
+ padding: 0;
5409
+ border: var(--bp-stroke) solid var(--bp-edge);
5410
+ border-radius: var(--bp-radius-6);
5411
+ background: var(--bp-fill-amb);
5412
+ color: var(--bp-text-secondary);
5413
+ cursor: pointer;
5414
+ transition:
5415
+ border-color var(--bp-duration-fast) var(--bp-ease-out),
5416
+ background-color var(--bp-duration-fast) var(--bp-ease-out),
5417
+ color var(--bp-duration-fast) var(--bp-ease-out);
5418
+ }
5419
+ .bp-cite-pane__close:hover {
5420
+ border-color: var(--bp-ink-line);
5421
+ background: var(--bp-paper);
5422
+ color: var(--bp-ink);
5423
+ }
5424
+ .bp-cite-pane__close svg {
5425
+ display: block;
5426
+ width: 16px;
5427
+ height: 16px;
5428
+ }
5429
+
5430
+ /* The body owns vertical scroll; its content sizes to itself so a short
5431
+ snippet keeps a short card instead of a column-tall empty box. */
5432
+ .bp-cite-pane__body {
5433
+ flex: 1;
5434
+ min-height: 0;
5435
+ overflow-y: auto;
5436
+ overscroll-behavior: contain;
5437
+ padding: var(--bp-space-4);
5438
+ }
5439
+ /* Content swap when promoting another citation while the pane is open: the
5440
+ pane holds, only the source crossfades to the right. */
5441
+ .bp-cite-pane__swap {
5442
+ transition:
5443
+ opacity var(--bp-duration-normal) var(--bp-ease-out),
5444
+ transform var(--bp-duration-normal) var(--bp-ease-out);
5445
+ }
5446
+ .bp-cite-pane__swap.is-leaving {
5447
+ opacity: 0;
5448
+ transform: translateX(var(--bp-cite-pane-swap-shift));
5449
+ }
5450
+ /* JS mount point for the cloned <pre>. Visual treatment lives in
5451
+ blueprint-code.css (full highlighted-block + accent bar). Margin is cleared
5452
+ here (not :where) so it beats the base-layer :where(pre) rhythm margin and
5453
+ the theme's margin-block — document specimens outside the pane are untouched. */
5454
+ .bp-cite-pane__source {
5455
+ min-width: 0;
5456
+ }
5457
+ .bp-cite-pane__source pre {
5458
+ margin: 0;
5459
+ margin-block: 0;
5460
+ }
5461
+
5462
+ /* Below the rail's fixed breakpoint the desk frame flattens; let the pane fill
5463
+ the viewport edge-to-edge and drop the reflow (the sheet goes static). */
5464
+ @media (max-width: 700px) {
5465
+ html[data-bp-document]:has(.bp-cite-pane.is-open) main {
5466
+ margin-right: 0;
5467
+ }
5468
+ :root {
5469
+ --bp-cite-pane-w: min(420px, 92vw);
5470
+ }
5471
+ }
5472
+
5473
+ @media print {
5474
+ .bp-cite-pane-port {
5475
+ display: none;
5476
+ }
5477
+ }