beadcyte 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/LICENSE +661 -0
  3. package/README.md +386 -0
  4. package/THIRD_PARTY_NOTICES.md +56 -0
  5. package/bin/beadcyte.mjs +60 -0
  6. package/package.json +77 -0
  7. package/src/changelog-cli.mjs +105 -0
  8. package/src/changelog.mjs +196 -0
  9. package/src/cli.mjs +577 -0
  10. package/src/estimator.mjs +314 -0
  11. package/src/format.mjs +22 -0
  12. package/src/history-walk.mjs +170 -0
  13. package/src/index.mjs +5 -0
  14. package/src/mutate.mjs +193 -0
  15. package/src/projects.mjs +120 -0
  16. package/src/provenance.mjs +75 -0
  17. package/src/review-hours.mjs +117 -0
  18. package/src/roster-path.mjs +24 -0
  19. package/src/scheduler.mjs +424 -0
  20. package/src/serve.mjs +411 -0
  21. package/src/server-state.mjs +105 -0
  22. package/src/ship.mjs +178 -0
  23. package/src/stage-waits.mjs +69 -0
  24. package/src/start.mjs +111 -0
  25. package/src/stop.mjs +66 -0
  26. package/src/velocity.mjs +209 -0
  27. package/src/web/App.vue +691 -0
  28. package/src/web/app.css +54 -0
  29. package/src/web/assets/favicon.svg +12 -0
  30. package/src/web/avatar.ts +53 -0
  31. package/src/web/bead-detail.ts +240 -0
  32. package/src/web/changelog-view.ts +41 -0
  33. package/src/web/components/BeadDrawer.vue +1883 -0
  34. package/src/web/components/BeadSubGraph.vue +326 -0
  35. package/src/web/components/BeadSubGraphOverlay.vue +192 -0
  36. package/src/web/components/BeadTooltip.vue +516 -0
  37. package/src/web/components/BeadcyteMark.vue +64 -0
  38. package/src/web/components/BeadsGantt.vue +2125 -0
  39. package/src/web/components/BeadsGrid.vue +468 -0
  40. package/src/web/components/BeadsIncytes.vue +567 -0
  41. package/src/web/components/BeadsMine.vue +325 -0
  42. package/src/web/components/BeadsTable.vue +335 -0
  43. package/src/web/components/ChangelogOverlay.vue +198 -0
  44. package/src/web/components/ContextMenu.vue +386 -0
  45. package/src/web/components/ControlsPanel.vue +476 -0
  46. package/src/web/components/CostTrend.vue +206 -0
  47. package/src/web/components/FilterPopover.vue +245 -0
  48. package/src/web/components/GroupProgress.vue +274 -0
  49. package/src/web/components/LoadMeter.vue +144 -0
  50. package/src/web/components/MineRow.vue +28 -0
  51. package/src/web/components/OptionsMenu.vue +825 -0
  52. package/src/web/components/PriorityChip.vue +105 -0
  53. package/src/web/components/ScoreStrip.vue +131 -0
  54. package/src/web/components/SearchPalette.vue +210 -0
  55. package/src/web/components/ShipTrend.vue +510 -0
  56. package/src/web/components/ShortcutsOverlay.vue +164 -0
  57. package/src/web/components/Term.vue +177 -0
  58. package/src/web/components/Toast.vue +50 -0
  59. package/src/web/components/TriageMeters.vue +426 -0
  60. package/src/web/components/TypeChip.vue +96 -0
  61. package/src/web/components/Walkthrough.vue +209 -0
  62. package/src/web/components/WhatIfPanel.vue +206 -0
  63. package/src/web/components/WipBullets.vue +191 -0
  64. package/src/web/components/filter-option.ts +9 -0
  65. package/src/web/composables/url-codec.ts +136 -0
  66. package/src/web/composables/useBeadTooltip.ts +148 -0
  67. package/src/web/composables/useKeyboard.ts +97 -0
  68. package/src/web/composables/useLiveRefresh.ts +69 -0
  69. package/src/web/composables/useTheme.ts +125 -0
  70. package/src/web/composables/useUrlState.ts +208 -0
  71. package/src/web/controls-scope.ts +83 -0
  72. package/src/web/cost.ts +251 -0
  73. package/src/web/dep-headings.ts +62 -0
  74. package/src/web/economics.ts +440 -0
  75. package/src/web/env.d.ts +85 -0
  76. package/src/web/frontier.ts +208 -0
  77. package/src/web/gantt-viewport.ts +99 -0
  78. package/src/web/highlights.ts +124 -0
  79. package/src/web/index.html +46 -0
  80. package/src/web/insights.ts +107 -0
  81. package/src/web/keybindings.ts +200 -0
  82. package/src/web/load-meter.ts +72 -0
  83. package/src/web/main.ts +20 -0
  84. package/src/web/markdown.ts +14 -0
  85. package/src/web/mine.ts +137 -0
  86. package/src/web/mutations.ts +21 -0
  87. package/src/web/person.ts +102 -0
  88. package/src/web/projects-text.ts +15 -0
  89. package/src/web/projects.ts +188 -0
  90. package/src/web/refresh.ts +47 -0
  91. package/src/web/search.ts +50 -0
  92. package/src/web/shortcuts.ts +113 -0
  93. package/src/web/status-filter.ts +48 -0
  94. package/src/web/store.ts +1378 -0
  95. package/src/web/style-audit.mjs +346 -0
  96. package/src/web/styles-alt.css +111 -0
  97. package/src/web/styles-ported.css +270 -0
  98. package/src/web/subgraph.ts +362 -0
  99. package/src/web/table.ts +201 -0
  100. package/src/web/theme.ts +88 -0
  101. package/src/web/tokens.css +168 -0
  102. package/src/web/triage.ts +914 -0
  103. package/src/web/view-model.ts +717 -0
  104. package/src/web/walkthrough.ts +133 -0
  105. package/src/web/watchlist.ts +47 -0
  106. package/src/web/whatif.ts +291 -0
  107. package/src/web/window.ts +73 -0
  108. package/src/web/wip.ts +83 -0
@@ -0,0 +1,516 @@
1
+ <script setup lang="ts">
2
+ // BeadTooltip.vue — floating panel with the hovered bead's details. Teleported
3
+ // to <body> so it escapes SVG clipping; positioned in fixed viewport coords
4
+ // from the parent. Purely presentational — the parent decides what to show
5
+ // and when (hover state, grace period, etc).
6
+
7
+ import { computed, nextTick, onUpdated, ref, watch } from "vue";
8
+ import { Pin, PinOff } from "@lucide/vue";
9
+ import { formatEstimate, type BeadDetail } from "../bead-detail";
10
+ import { TRIAGE_WEIGHTS, type TriageResult, type TriageSignal } from "../triage";
11
+ import { formatHours, type BeadEconomics } from "../economics";
12
+ import PriorityChip from "./PriorityChip.vue";
13
+ import TypeChip from "./TypeChip.vue";
14
+
15
+ const props = defineProps<{
16
+ detail: BeadDetail | null;
17
+ x: number;
18
+ y: number;
19
+ /** Is the shown bead on the watch list? Drives the pin button's state. */
20
+ watched?: boolean;
21
+ /**
22
+ * Viewport y-extent of the hovered row's band (bp-67g.41). When given, the
23
+ * panel sits below the band, or above it when there is no room, so it never
24
+ * covers the bar being hovered or its neighbours in the same row.
25
+ */
26
+ bandTop?: number;
27
+ bandBottom?: number;
28
+ /**
29
+ * Horizontal extent in viewport px to keep the panel inside (bp-67g.42). A
30
+ * host that lives in a column — the drawer's subgraph — passes its own
31
+ * edges so the panel never straddles them; absent, or too narrow for the
32
+ * panel, the viewport is the bound.
33
+ */
34
+ boundsLeft?: number;
35
+ boundsRight?: number;
36
+ /** The bead's triage result, when the parent has it; null hides the line. */
37
+ triage?: TriageResult | null;
38
+ /** "top 7% of 216 open beads", to go with the score. */
39
+ rank?: string | null;
40
+ /** The bead's economics; the Time line is absent unless it carries any. */
41
+ economics?: BeadEconomics | null;
42
+ }>();
43
+
44
+ const emit = defineEmits<{
45
+ details: [id: string];
46
+ /** Toggle the bead on the watch list. The parent owns the store call. */
47
+ watch: [id: string];
48
+ "tooltip-enter": [];
49
+ "tooltip-leave": [];
50
+ }>();
51
+
52
+ // Placement (bp-67g.41). The panel used to hang off the cursor with a 12px
53
+ // offset, which kept it on screen and on top of the data: the bar you came
54
+ // from, its neighbours, the next bar along the row. Given the hovered row's
55
+ // band it now sits BELOW the band, or above it when the viewport runs out,
56
+ // so nothing in that row is ever covered; horizontally it follows the cursor,
57
+ // clamped to the viewport. The panel's real height is measured after render
58
+ // so the flip is exact rather than guessed from a constant.
59
+ const panelEl = ref<HTMLElement | null>(null);
60
+ const panelH = ref(240);
61
+ const measure = () => {
62
+ if (panelEl.value) panelH.value = panelEl.value.offsetHeight || panelH.value;
63
+ };
64
+ onUpdated(measure);
65
+ watch(
66
+ () => props.detail?.id,
67
+ async () => {
68
+ await nextTick();
69
+ measure();
70
+ },
71
+ );
72
+
73
+ const style = computed(() => {
74
+ if (!props.detail) return { display: "none" };
75
+ const w = 320;
76
+ const pad = 12;
77
+ const gap = 8;
78
+ const vw = typeof window !== "undefined" ? window.innerWidth : 1200;
79
+ const vh = typeof window !== "undefined" ? window.innerHeight : 900;
80
+ const h = panelH.value;
81
+ let lo = pad;
82
+ let hi = vw - w - pad;
83
+ if (
84
+ props.boundsLeft !== undefined &&
85
+ props.boundsRight !== undefined &&
86
+ props.boundsRight - props.boundsLeft >= w + 2 * pad
87
+ ) {
88
+ lo = props.boundsLeft + pad;
89
+ hi = props.boundsRight - w - pad;
90
+ }
91
+ const left = Math.min(Math.max(lo, props.x - 24), hi);
92
+ let top: number;
93
+ if (props.bandTop !== undefined && props.bandBottom !== undefined) {
94
+ top = props.bandBottom + gap;
95
+ if (top + h + pad > vh) top = props.bandTop - gap - h;
96
+ top = Math.max(pad, top);
97
+ } else {
98
+ top = props.y + pad;
99
+ if (top + h > vh) top = Math.max(pad, props.y - h);
100
+ }
101
+ return {
102
+ position: "fixed" as const,
103
+ left: `${left}px`,
104
+ top: `${top}px`,
105
+ width: `${w}px`,
106
+ };
107
+ });
108
+
109
+ const fmtPct = (v: number) => `${(v * 100).toFixed(1)}%`;
110
+
111
+ /** The signal contributing most of the score, and its share of it. */
112
+ const topSignal = computed(() => {
113
+ const t = props.triage;
114
+ if (!t || t.score <= 0) return null;
115
+ let best: TriageSignal = "pagerank";
116
+ for (const k of Object.keys(TRIAGE_WEIGHTS) as TriageSignal[]) {
117
+ if (t.breakdown[k] > t.breakdown[best]) best = k;
118
+ }
119
+ return { label: best.replace(/_/g, " "), share: Math.round((t.breakdown[best] / t.score) * 100) };
120
+ });
121
+
122
+ /** The phases that carry a figure, as "unclaimed 40h (derived) · worked 3.2h". */
123
+ const timeParts = computed(() => {
124
+ const e = props.economics;
125
+ if (!e || !(e.hasAny || e.hasDerived)) return [];
126
+ const parts: string[] = [];
127
+ const add = (label: string, hours: number | null, prov: { kind: string } | null) => {
128
+ if (hours === null) return;
129
+ parts.push(`${label} ${formatHours(hours)}${prov && prov.kind !== "recorded" ? ` (${prov.kind})` : ""}`);
130
+ };
131
+ add("unclaimed", e.unclaimedHours, e.provenance.unclaimedHours);
132
+ add("in review", e.reviewHours, e.provenance.reviewHours);
133
+ add("worked", e.workedHours, e.provenance.workedHours);
134
+ return parts;
135
+ });
136
+
137
+ const statusLabel = (s: string): string => s.replace(/_/g, " ");
138
+
139
+ /** "Mon 8 Sep", and with the year once it isn't this one. */
140
+ function fmtDate(d: Date): string {
141
+ const sameYear = d.getFullYear() === new Date().getFullYear();
142
+ return d.toLocaleDateString(undefined, {
143
+ weekday: "short",
144
+ day: "numeric",
145
+ month: "short",
146
+ ...(sameYear ? {} : { year: "numeric" }),
147
+ });
148
+ }
149
+
150
+ /** "today", "tomorrow", "in 7 days", "4 days ago" — the human's units. */
151
+ function relative(days: number): string {
152
+ const n = Math.round(days);
153
+ if (n === 0) return "today";
154
+ if (n === 1) return "tomorrow";
155
+ if (n === -1) return "yesterday";
156
+ return n > 0 ? `in ${n} days` : `${-n} days ago`;
157
+ }
158
+
159
+ /**
160
+ * What to call the finish. A clamped value is a BOUND, not a projection:
161
+ * scheduleGreedy caps end_day at the scheduling horizon, so the number
162
+ * means "still running then" and moves when the window preset moves.
163
+ */
164
+ const finishLabel = computed(() => {
165
+ const s = props.detail?.scheduled;
166
+ if (!s) return "";
167
+ if (s.beyondHorizon) return "Finishes after";
168
+ return s.inFlight ? "Projected finish" : "Projected";
169
+ });
170
+ </script>
171
+
172
+ <template>
173
+ <Teleport to="body">
174
+ <div
175
+ v-if="detail"
176
+ ref="panelEl"
177
+ class="bead-tooltip"
178
+ :style="style"
179
+ @mouseenter="emit('tooltip-enter')"
180
+ @mouseleave="emit('tooltip-leave')"
181
+ >
182
+ <header class="row">
183
+ <code class="id">{{ detail.id }}</code>
184
+ <PriorityChip :priority="detail.priority" />
185
+ <span class="pill" :class="`status-${detail.status}`">
186
+ {{ statusLabel(detail.status) }}
187
+ </span>
188
+ <TypeChip :type="detail.type" />
189
+ </header>
190
+
191
+ <div class="title">{{ detail.title }}</div>
192
+
193
+ <dl class="fields">
194
+ <template v-if="detail.assignee">
195
+ <dt>Assignee</dt>
196
+ <dd>
197
+ <code>{{ detail.assignee }}</code>
198
+ <span v-if="detail.syntheticAssignee" class="synth-note">
199
+ synthetic (affinity match)
200
+ </span>
201
+ </dd>
202
+ </template>
203
+
204
+ <template v-if="detail.estimateSource">
205
+ <dt>Estimate</dt>
206
+ <dd>
207
+ <template v-if="detail.estimateDays !== null">
208
+ {{ formatEstimate(detail.estimateDays) }} ·
209
+ </template>
210
+ <span class="source">{{ detail.estimateSource }}</span>
211
+ </dd>
212
+ </template>
213
+
214
+ <!-- The finish DATE is the headline: it is what anyone hovering a
215
+ future bar wants. This used to read "+7d → +8d", offsets from
216
+ an origin the UI never stated. -->
217
+ <template v-if="detail.scheduled">
218
+ <dt>{{ finishLabel }}</dt>
219
+ <dd>
220
+ <template v-if="detail.scheduled.beyondHorizon">
221
+ <span class="beyond">
222
+ {{ fmtDate(detail.scheduled.endDate) }} — the end of the
223
+ scheduling window, not a projection
224
+ </span>
225
+ </template>
226
+ <template v-else>
227
+ <strong>{{ fmtDate(detail.scheduled.endDate) }}</strong>
228
+ <span class="source">
229
+ · {{ relative(detail.scheduled.endDay) }}
230
+ </span>
231
+ </template>
232
+ </dd>
233
+
234
+ <dt>{{ detail.scheduled.inFlight ? "Started" : "Starts" }}</dt>
235
+ <dd>
236
+ {{ fmtDate(detail.scheduled.startDate) }}
237
+ <span class="source">
238
+ {{ relative(detail.scheduled.startDay) }} ·
239
+ {{ detail.scheduled.spanDays }}d<template v-if="detail.scheduled.workDays !== null">
240
+ · work {{ formatEstimate(detail.scheduled.workDays) }}<template v-if="detail.scheduled.waitBeforeDays + detail.scheduled.waitAfterDays > 0.01"> · waiting {{ formatEstimate(detail.scheduled.waitBeforeDays + detail.scheduled.waitAfterDays) }}</template></template>
241
+ </span>
242
+ </dd>
243
+ </template>
244
+
245
+ <template v-if="detail.dependencyCount > 0">
246
+ <dt>Blocks</dt>
247
+ <dd>{{ detail.dependencyCount }} dep{{ detail.dependencyCount === 1 ? "" : "s" }}</dd>
248
+ </template>
249
+
250
+ <template v-if="triage">
251
+ <dt>Triage</dt>
252
+ <dd>
253
+ <strong>{{ fmtPct(triage.score) }}</strong><template v-if="rank"> · {{ rank }}</template>
254
+ <span v-if="topSignal" class="source">
255
+ largest signal: {{ topSignal.label }} ({{ topSignal.share }}% of the score)
256
+ </span>
257
+ </dd>
258
+ </template>
259
+
260
+ <template v-if="timeParts.length">
261
+ <dt>Time</dt>
262
+ <dd>{{ timeParts.join(" · ") }}</dd>
263
+ </template>
264
+
265
+ <template v-if="detail.ship">
266
+ <dt>Ship</dt>
267
+ <dd>
268
+ <a
269
+ v-if="detail.ship.mrUrl"
270
+ :href="detail.ship.mrUrl"
271
+ target="_blank"
272
+ rel="noopener"
273
+ >
274
+ {{ detail.ship.mrUrl.replace(/^https?:\/\//, "").slice(0, 40) }}…
275
+ </a>
276
+ <code v-if="detail.ship.shortCommit">{{ detail.ship.shortCommit }}</code>
277
+ </dd>
278
+ </template>
279
+
280
+ <!-- Only when nothing counted. This is the confusing moment the
281
+ tooltip is worth spending a line on: the bar in front of you is
282
+ drawn dim because a ship record exists and was rejected. The
283
+ drawer reports the rejection even when other evidence stands;
284
+ here there is no room for a caveat on a bar that already looks
285
+ right. -->
286
+ <template v-else-if="detail.shipProblem">
287
+ <dt>Ship</dt>
288
+ <dd class="tip-dim">{{ detail.shipProblem }}</dd>
289
+ </template>
290
+
291
+ <template v-if="detail.labels.length">
292
+ <dt>Labels</dt>
293
+ <dd class="labels">
294
+ <span v-for="l in detail.labels" :key="l" class="label">{{ l }}</span>
295
+ </dd>
296
+ </template>
297
+ </dl>
298
+
299
+ <footer class="actions">
300
+ <button class="btn" @click="emit('details', detail.id)">Details</button>
301
+ <!-- Same affordance as the drawer header: pin to watch, pin-off to
302
+ unwatch. Emits rather than touching the store, like Details. -->
303
+ <button
304
+ class="btn watch-toggle"
305
+ :class="{ 'is-watched': watched }"
306
+ :aria-pressed="!!watched"
307
+ :aria-label="watched ? 'Unwatch this bead' : 'Watch this bead'"
308
+ :title="watched ? 'Remove from watch list' : 'Add to watch list'"
309
+ @click="emit('watch', detail.id)"
310
+ >
311
+ <component :is="watched ? PinOff : Pin" class="icon" />
312
+ {{ watched ? "Unwatch" : "Watch" }}
313
+ </button>
314
+ </footer>
315
+ </div>
316
+ </Teleport>
317
+ </template>
318
+
319
+ <style>
320
+ .bead-tooltip {
321
+ z-index: 9999;
322
+ background: var(--c-panel);
323
+ border: 1px solid var(--c-border);
324
+ border-radius: 6px;
325
+ padding: 12px;
326
+ color: var(--c-text);
327
+ font-size: 12px;
328
+ line-height: 1.4;
329
+ box-shadow: var(--shadow-pop);
330
+ pointer-events: auto;
331
+ font-family:
332
+ ui-sans-serif,
333
+ -apple-system,
334
+ Segoe UI,
335
+ Roboto,
336
+ sans-serif;
337
+ }
338
+
339
+ .bead-tooltip .row {
340
+ display: flex;
341
+ align-items: center;
342
+ gap: 6px;
343
+ margin-bottom: 4px;
344
+ }
345
+
346
+ .bead-tooltip .id {
347
+ color: var(--c-accent);
348
+ font-family: ui-monospace, monospace;
349
+ font-size: 11px;
350
+ }
351
+
352
+ .bead-tooltip .pill {
353
+ font-size: 10px;
354
+ padding: 1px 6px;
355
+ border-radius: 2px;
356
+ border: 1px solid var(--c-border);
357
+ text-transform: lowercase;
358
+ color: var(--c-dim);
359
+ }
360
+ .bead-tooltip .pill.pri-p0,
361
+ .bead-tooltip .pill.pri-p1 {
362
+ color: var(--c-danger-bright);
363
+ border-color: var(--c-danger-bright);
364
+ }
365
+ .bead-tooltip .pill.pri-p2 {
366
+ color: var(--c-warning);
367
+ border-color: var(--c-warning);
368
+ }
369
+ .bead-tooltip .pill.status-closed {
370
+ color: var(--c-shipped);
371
+ border-color: var(--c-shipped-stroke);
372
+ }
373
+ .bead-tooltip .pill.status-in_progress {
374
+ color: var(--c-in-progress);
375
+ border-color: var(--c-in-progress);
376
+ }
377
+
378
+ .bead-tooltip .title {
379
+ font-size: 13px;
380
+ color: var(--c-text);
381
+ margin: 4px 0 10px;
382
+ overflow-wrap: anywhere;
383
+ }
384
+
385
+ .bead-tooltip .fields {
386
+ display: grid;
387
+ grid-template-columns: 72px 1fr;
388
+ gap: 4px 8px;
389
+ margin: 0;
390
+ }
391
+
392
+ .bead-tooltip dt {
393
+ color: var(--c-dim);
394
+ font-size: 10px;
395
+ text-transform: uppercase;
396
+ letter-spacing: 0.05em;
397
+ padding-top: 2px;
398
+ }
399
+
400
+ .bead-tooltip dd {
401
+ margin: 0;
402
+ color: var(--c-text);
403
+ overflow: hidden;
404
+ text-overflow: ellipsis;
405
+ }
406
+
407
+ .bead-tooltip .synth-note {
408
+ color: var(--c-accent);
409
+ margin-left: 6px;
410
+ font-size: 10px;
411
+ }
412
+
413
+ /* A rejected ship record. Every other dossier value here is short enough
414
+ for the dd's clip rule; this one is a sentence, so it wraps. */
415
+ .bead-tooltip .tip-dim {
416
+ color: var(--c-dim);
417
+ font-size: 11px;
418
+ line-height: 1.4;
419
+ overflow-wrap: anywhere;
420
+ }
421
+
422
+ .bead-tooltip .source {
423
+ color: var(--c-dim);
424
+ font-family: ui-monospace, monospace;
425
+ font-size: 11px;
426
+ }
427
+
428
+ /* A finish clamped to the scheduling horizon. Warning-coloured because it
429
+ is the one figure here that must not be read as a projection. */
430
+ .bead-tooltip .beyond {
431
+ color: var(--c-warning-strong);
432
+ font-size: 11px;
433
+ line-height: 1.4;
434
+ display: inline-block;
435
+ }
436
+
437
+ /* The projected date is the answer, so it carries the weight. */
438
+ .bead-tooltip dd strong {
439
+ font-weight: 600;
440
+ color: var(--c-text);
441
+ }
442
+
443
+ .bead-tooltip .labels {
444
+ display: flex;
445
+ flex-wrap: wrap;
446
+ gap: 4px;
447
+ }
448
+
449
+ .bead-tooltip .label {
450
+ font-size: 10px;
451
+ padding: 1px 6px;
452
+ background: var(--c-border);
453
+ border-radius: 2px;
454
+ color: var(--c-dim);
455
+ }
456
+
457
+ .bead-tooltip a {
458
+ color: var(--c-accent);
459
+ text-decoration: none;
460
+ }
461
+
462
+ .bead-tooltip a:hover {
463
+ text-decoration: underline;
464
+ }
465
+
466
+ .bead-tooltip .actions {
467
+ display: flex;
468
+ align-items: center;
469
+ justify-content: space-between;
470
+ gap: 8px;
471
+ margin-top: 10px;
472
+ padding-top: 10px;
473
+ border-top: 1px solid var(--c-border);
474
+ }
475
+
476
+ .bead-tooltip .btn {
477
+ background: var(--c-lift);
478
+ border: 1px solid var(--c-border);
479
+ color: var(--c-text);
480
+ font-size: 11px;
481
+ font-weight: 500;
482
+ padding: 5px 10px;
483
+ border-radius: 4px;
484
+ cursor: pointer;
485
+ text-decoration: none;
486
+ display: inline-flex;
487
+ align-items: center;
488
+ gap: 5px;
489
+ line-height: 1.2;
490
+ font-family: inherit;
491
+ transition: background 0.08s ease-out, border-color 0.08s ease-out, color 0.08s ease-out;
492
+ }
493
+
494
+ .bead-tooltip .btn:hover {
495
+ background: var(--c-accent-wash-strong);
496
+ border-color: var(--c-accent);
497
+ color: var(--c-accent);
498
+ }
499
+
500
+ .bead-tooltip .btn-primary {
501
+ background: var(--c-accent);
502
+ color: var(--c-bg);
503
+ border-color: var(--c-accent);
504
+ }
505
+
506
+ .bead-tooltip .btn-primary:hover {
507
+ background: var(--c-accent);
508
+ color: var(--c-bg);
509
+ opacity: 0.85;
510
+ }
511
+
512
+ .bead-tooltip .watch-toggle.is-watched {
513
+ color: var(--c-accent);
514
+ border-color: var(--c-accent);
515
+ }
516
+ </style>
@@ -0,0 +1,64 @@
1
+ <script setup lang="ts">
2
+ // BeadcyteMark.vue — the wordmark, inline so it inherits `currentColor`.
3
+ //
4
+ // Traced from mark.png, which could not be used directly: it is a 1024px
5
+ // screenshot of a design-tool canvas with NO alpha channel and the
6
+ // transparency checkerboard baked in as flat 207/255 greys, saved lossily
7
+ // (251 distinct colours in a corner region that should hold two). Dropped
8
+ // into the header it renders as a grey checkered box.
9
+ //
10
+ // So: threshold the ink well clear of both checker values, trace to Bézier
11
+ // paths, and paint with `fill="currentColor"`. One file, correct in light,
12
+ // dark and every named style, sharp at any size — the same reason the icons
13
+ // went this way in bp-67g.5.
14
+ //
15
+ // `fill-rule="evenodd"` is load-bearing. The counters of B, E, A and D come
16
+ // out as subpaths wound the same way as their outlines, so under the
17
+ // default nonzero rule every hole fills in and BEAD becomes four blobs.
18
+ //
19
+ // A real vector export from the original design source would still beat a
20
+ // trace of a lossy screenshot; this is good to well past the sizes the app
21
+ // uses, and swapping the path data is a one-line change if that export
22
+ // turns up.
23
+
24
+ defineProps<{ variant?: "lockup" | "glyph" }>();
25
+ </script>
26
+
27
+ <template>
28
+ <svg
29
+ v-if="variant !== 'glyph'"
30
+ class="beadcyte-mark"
31
+ viewBox="0 0 876 210"
32
+ fill="currentColor"
33
+ fill-rule="evenodd"
34
+ aria-hidden="true"
35
+ focusable="false"
36
+ >
37
+ <path d="M 179.4 3.4 C 175.4 5.2, 171.2 10.3, 170.4 14.5 C 169.6 18.7, 171.5 24.7, 174.6 27.5 L 176.9 29.7 175 36.6 C 174 40.4, 172.6 45.3, 172.1 47.6 C 171.1 51.2, 170.5 51.8, 167.1 52.9 C 159.4 55.1, 154.5 64.1, 156.9 71.2 C 157.9 74.2, 157.8 74.7, 155.1 77.4 L 152.2 80.3 154.3 83.2 C 158.4 88.8, 156.2 97.4, 150 100 C 143.1 102.9, 136.1 98.4, 135.3 90.6 C 134.8 86.4, 135 85.8, 138.3 82.5 C 141.2 79.6, 142.5 79, 145.9 79 C 148.2 79, 150 78.6, 150 78.1 C 150 77.6, 147.9 76.2, 145.2 74.9 C 141.5 73, 138.9 72.5, 133 72.5 C 119.9 72.5, 108.8 79.4, 103.8 90.8 C 100.9 97.4, 100.3 107.5, 102.4 113.7 L 104.1 118.5 101 121.3 C 98.1 123.9, 97.8 124, 95 122.5 C 89.1 119.5, 82.1 121, 77 126.5 C 74.8 128.8, 74.5 130.1, 74.5 136.1 L 74.4 143.1 66.5 150.1 C 59.9 155.8, 58.1 156.9, 56 156.4 C 46.6 154.1, 42 155.1, 37.1 160.4 C 33.9 163.8, 33.2 171.2, 35.7 176 C 38.1 180.7, 45.1 184.3, 50.3 183.7 C 58.4 182.6, 63.2 176.7, 62.8 168.1 L 62.6 162.6 71.2 155.2 C 79 148.6, 80.1 148, 82.1 149 C 83.4 149.7, 86.7 150, 90.1 149.8 C 94.9 149.5, 96.6 148.8, 99 146.5 C 102.5 143.2, 104.4 137.2, 103.5 132.8 C 102.9 130.4, 103.3 129.3, 105.5 127 L 108.3 124.1 112.7 128 C 118.7 133.2, 125.4 135.4, 133.9 134.8 C 142.6 134.1, 150 130.8, 155.1 125.3 C 161.5 118.4, 163.5 113, 163.5 103 C 163.5 95.5, 163.2 93.9, 160.7 89.6 L 157.9 84.6 160.3 82.3 C 162.4 80.1, 163 80, 167.2 80.9 C 176.8 83.1, 184.8 77.7, 186 68.2 L 186.5 63.9 199.2 57.3 L 211.9 50.8 216.5 53 C 226.8 58, 238 51.5, 238 40.5 C 238 29, 227.2 23, 215.4 28 C 212.4 29.3, 211.9 29.1, 206 25.4 C 199.9 21.5, 199.8 21.4, 199.6 16.8 C 199.2 6.6, 188.3 -0.6, 179.4 3.4 M 186.4 10.5 C 184.9 14.3, 189.3 17.7, 191.6 14.5 C 192.5 13.2, 192.6 12.2, 191.9 10.9 C 190.7 8.6, 187.2 8.4, 186.4 10.5 M 120.5 24.7 C 106.3 26.7, 86 35.2, 70.1 45.8 C 61.9 51.3, 61.1 51.6, 58.9 50.4 C 53.8 47.6, 45.7 49.4, 41.1 54.4 C 38.3 57.3, 37 66.3, 38.8 69.7 C 39.7 71.4, 39.5 72.1, 37.3 74.1 C 35.9 75.4, 32.3 79.4, 29.3 83 L 23.9 89.5 18 89.5 C 11.7 89.5, 8.5 91.1, 4.6 96.3 C 1.9 100, 1.8 107.9, 4.6 111.7 C 7.8 116.1, 14.7 119.2, 19.6 118.4 C 23.7 117.8, 23.7 117.8, 29 124.4 C 34.3 131.2, 51.9 147.8, 56 150 C 58 151.1, 58.6 150.9, 60.9 148.6 L 63.5 146 61.2 144.4 C 50.1 136.6, 28.4 113.8, 29.6 111.1 C 32.1 105.3, 32.4 102, 30.7 98.5 L 29 95 36.7 86.2 C 40.9 81.4, 44.4 77.4, 44.5 77.2 C 44.6 77.1, 45.6 77.3, 46.6 77.7 C 48.2 78.4, 48.5 79.7, 48.6 86.5 C 48.7 93.3, 48.5 94.7, 46.9 95.8 C 43.3 98.3, 41.8 102.7, 43 106.5 C 45.5 114.1, 53.7 116.4, 59.2 111 C 61.7 108.5, 62.9 108, 67.1 108 C 71.3 108, 72 108.3, 72 110 C 72 111, 72.5 114.1, 73.1 116.8 L 74.1 121.7 78.8 119.4 C 81.4 118.1, 84.6 116.8, 86 116.6 L 88.6 116.1 87.6 109 C 84.6 87.6, 98.4 66.6, 119.7 60 C 127.5 57.6, 134.4 57.5, 142.5 59.6 C 145.8 60.4, 149.5 61.4, 150.6 61.6 C 152.3 62.1, 152.9 61.6, 153.4 59.6 C 153.7 58.2, 155.4 55.4, 157.1 53.5 C 158.9 51.5, 160.1 49.8, 159.9 49.6 C 158.1 48.3, 148.4 45, 143.6 44.1 C 129.9 41.6, 118.9 43.1, 106 49.2 C 89.2 57.2, 75.9 74.7, 72.9 92.5 L 71.6 100 66.9 100 C 62.9 100, 61.6 99.5, 59.1 96.9 C 56.2 94, 56 93.4, 56 86.2 C 56.1 78.7, 56.1 78.5, 59.3 76.5 C 64.7 73.1, 67.2 68.3, 66.8 62.5 L 66.4 57.4 76.1 51.2 C 106.6 31.6, 132.2 27, 159.8 36.2 C 164.9 37.9, 169.3 39.1, 169.5 38.8 C 169.7 38.6, 170.1 36.9, 170.2 35.1 C 170.5 31.9, 170.3 31.7, 165 29.8 C 151 24.9, 133.6 22.9, 120.5 24.7 M 189 30.5 L 183.6 32.8 180.9 43.2 L 178.2 53.5 180.3 55.2 C 181.5 56.2, 182.7 56.9, 183 56.9 C 183.3 56.9, 189.1 54.1, 196 50.7 L 208.5 44.5 208.8 40 L 209.1 35.5 202.7 31.7 C 199.2 29.7, 195.9 28.1, 195.4 28.1 C 194.9 28.2, 192 29.3, 189 30.5 M 226.2 32.7 C 223.6 33.7, 225.6 39, 228.7 39 C 229.4 39, 230.4 38.1, 231 37.1 C 231.7 35.6, 231.6 34.8, 230.4 33.6 C 228.8 31.9, 228.3 31.8, 226.2 32.7 M 53.7 57.4 C 53.4 58.2, 53.9 59.9, 54.9 61.1 C 57.3 64.1, 60.3 63, 59.8 59.3 C 59.6 57.1, 58.9 56.4, 56.9 56.2 C 55.2 56, 54 56.5, 53.7 57.4 M 204.8 58.8 C 203.2 59.8, 202 60.8, 202 61.1 C 202 61.4, 204.1 63.2, 206.7 65.1 C 211.9 68.8, 224 80.7, 231.4 89.3 C 235.7 94.3, 236 95, 234.9 97.4 C 233.8 99.7, 233.1 100, 228.2 100 C 223.6 100, 222.3 99.5, 219.3 97 C 214.9 93.1, 211.1 93.1, 206.7 97 C 203.4 99.9, 203.1 99.9, 190.9 100.2 L 178.5 100.5 177.9 108.2 C 176.6 123.6, 165.9 138.5, 151.3 145.2 C 139.3 150.7, 123.1 150.2, 110.8 143.9 L 107 141.9 106 144.6 C 105.4 146.1, 103.3 148.7, 101.4 150.5 L 97.9 153.8 103.2 156.8 C 122.6 167.8, 144.9 167.1, 165.2 154.9 L 169 152.6 171.9 155.4 C 173.8 157.3, 174.8 159.2, 174.8 160.9 C 174.7 162.4, 175.1 164.4, 175.6 165.3 C 176.1 166.2, 185.3 175.8, 195.9 186.5 C 216.5 207.2, 218.7 208.7, 225.3 206.9 C 229.4 205.8, 233.7 201.4, 234.9 197.2 C 236.9 190.3, 235.2 187.9, 214.1 166.8 C 196 148.6, 194.1 147, 190.9 147 C 188.3 147, 186.6 146.2, 184.3 143.8 L 181.2 140.5 185.1 134 C 189.4 126.9, 193 116.7, 193 111.4 L 193 108 198.4 108 C 202.6 108, 204.2 108.4, 205.2 109.8 C 208.8 115, 214.6 115.2, 219.9 110.5 C 223.2 107.5, 224.4 107, 228.7 107 C 233.1 107, 233.7 107.2, 234.6 109.9 C 235.6 112.4, 235.4 113.2, 233.2 116.1 C 229.2 121.4, 219.2 131.5, 210.3 139.2 L 202.1 146.2 204.5 148.8 C 206.7 151.2, 207.1 151.3, 209.5 150 C 213.3 148, 231.7 130.3, 237 123.6 C 241.4 118.1, 241.6 118, 245.5 118.6 C 254.2 119.8, 263 112.5, 263 104 C 263 95.6, 254.7 88.3, 246.3 89.4 C 242.2 89.9, 242.1 89.9, 235.6 82.4 C 226.5 72, 209.6 56.6, 207.8 57.1 C 207.7 57.1, 206.3 57.9, 204.8 58.8 M 172.6 60.5 C 171.9 62.5, 173.6 66, 175.3 66 C 177.7 66, 179.2 64.2, 178.8 61.9 C 178.4 59, 173.6 57.9, 172.6 60.5 M 629.9 68.3 C 616.6 72, 608 81.1, 604.4 95 C 598.6 117.8, 611.5 138.7, 634.1 143 C 644.7 145, 658.6 141.6, 667.1 134.9 L 671.1 131.7 668.4 128.9 C 666.9 127.3, 665.3 126, 664.8 126 C 664.4 126, 662 127.5, 659.6 129.4 C 644.8 140.7, 620 133.9, 614.6 117 C 609.6 101.5, 613.4 87.6, 624.7 80.1 C 636 72.6, 649.7 73.7, 663.2 83.3 C 664.7 84.3, 665.4 84, 667.7 81.8 L 670.4 79.1 668 76.5 C 666.7 75.1, 662.6 72.5, 659.1 70.7 C 653.4 67.9, 651.4 67.5, 643.5 67.3 C 638.5 67.1, 632.5 67.6, 629.9 68.3 M 293 68.2 C 292.7 70.1, 292.6 130.2, 292.8 135.3 L 293.2 142.2 317.8 141.8 C 339.6 141.5, 342.9 141.3, 346 139.6 C 354 135.4, 358.1 129.6, 358.2 122.2 C 358.4 115.4, 356.4 110.5, 351.9 106.6 L 347.9 103.3 350.6 101.6 C 357.5 97, 357.9 82.7, 351.3 75.7 C 349.7 74.1, 346.7 71.8, 344.5 70.7 C 340.8 68.7, 338.8 68.5, 316.8 68.2 C 303.7 68, 293 68, 293 68.2 M 370 105 L 370 142 399 142 L 428 142 428 134.5 L 428 127 407 127 L 386 127 386 119.5 L 386 112 406 112 L 426 112 426 105 L 426 98 406 98 L 386 98 386 90.5 L 386 83 407 83 L 428 83 428 75.5 L 428 68 399 68 L 370 68 370 105 M 464.1 72.6 C 462.9 75.2, 459.1 83.5, 455.8 90.9 C 452.5 98.4, 449.4 105, 449 105.5 C 448.6 106, 446.7 110.1, 444.7 114.5 C 442.8 118.9, 439.1 126.9, 436.6 132.2 L 432.1 142 440.8 142 L 449.6 142 452.8 135.2 L 455.9 128.4 474.7 128.1 L 493.6 127.9 497 134.9 L 500.4 142 509.1 142 L 517.8 142 510.5 126.2 C 506.5 117.6, 498.7 101, 493.3 89.5 L 483.4 68.5 474.9 68.2 L 466.4 67.9 464.1 72.6 M 524.8 74.2 C 524.7 77.7, 524.6 94.4, 524.6 111.3 L 524.5 142.2 546.5 141.8 C 568.2 141.5, 568.6 141.5, 574.2 138.7 C 581.1 135.3, 586.3 129.7, 590.1 121.6 C 594 113.3, 594.7 105.4, 592.6 95.6 C 590.4 86.1, 586.3 79.7, 579 74.8 C 570.7 69.2, 564.6 68, 543.3 68 L 525 68 524.8 74.2 M 675 68.8 C 675 69.2, 681.6 79.5, 689.6 91.5 L 704.1 113.5 703.9 121.5 C 703.7 125.9, 703.7 132.3, 703.8 135.8 L 704 142 708.5 142 L 713 142 713 127.4 C 713 112.9, 713 112.8, 715.9 108.7 C 717.5 106.4, 724.3 96.3, 730.9 86.2 L 743 68 737.6 68 L 732.2 68 720.2 86 L 708.3 104 696.5 86 L 684.8 68 679.9 68 C 677.2 68, 675 68.4, 675 68.8 M 747.2 72.2 L 747.5 76.5 760.2 76.1 L 773 75.8 773 108.9 L 773 142 777.8 142 L 782.5 142 782.5 109 L 782.5 76 795.2 76 L 808 76 808 72 L 808 68 777.4 68 L 746.9 68 747.2 72.2 M 819.3 93.8 C 819.1 107.9, 819.3 124.6, 819.6 130.8 L 820.2 142 847.1 142 L 874 142 874 138 L 874 134 851.2 133.8 L 828.5 133.5 828.5 121 L 828.5 108.5 849.8 108.8 L 871 109 871 104.5 L 871 100 849.8 100 L 828.5 100 828.5 87.8 L 828.5 75.7 850.8 76.4 L 873 77.1 873 72.5 L 873 68 846.2 68 L 819.5 68 819.3 93.8 M 185.4 80.2 C 184.3 81.3, 181.6 83.1, 179.2 84.2 C 176.9 85.2, 175 86.2, 175 86.3 C 175 86.4, 175.6 88.5, 176.4 91 L 177.7 95.5 185.1 95.1 C 189.2 94.9, 192.7 94.6, 192.8 94.5 C 193.4 94, 188.8 79, 188 78.5 C 187.6 78.2, 186.4 79, 185.4 80.2 M 309 89.5 L 309 97 320.9 97 C 327.4 97, 334.1 96.5, 335.9 95.9 C 339.3 94.7, 340.6 91.7, 339.6 87.4 C 338.6 83.4, 333.8 82, 320.5 82 L 309 82 309 89.5 M 541 105 L 541 127 550.9 127 C 565.5 127, 571.7 124.2, 575.3 116.1 C 577.6 110.8, 577.9 100.7, 575.9 95.6 C 571.9 85.6, 567 83.1, 551.8 83 L 541 83 541 105 M 468.4 99.8 L 462.2 113.5 474.2 113.8 C 480.9 113.9, 486.5 113.8, 486.8 113.5 C 487.1 113.3, 485.3 108.6, 482.7 103.3 C 480.2 97.9, 477.5 91.8, 476.6 89.8 C 475.8 87.7, 475 86, 474.8 86 C 474.6 86, 471.7 92.2, 468.4 99.8 M 19.1 97.3 C 16.6 100.4, 21.1 104.4, 23.9 101.5 C 25.1 100.3, 25.2 99.6, 24.4 98.2 C 22.9 96, 20.6 95.6, 19.1 97.3 M 250.7 96.6 C 249.4 97.9, 250 101.8, 251.5 102.4 C 254.2 103.4, 256.1 101.9, 255.8 99.1 C 255.5 96.5, 252.3 95, 250.7 96.6 M 309 119 L 309 128 321.4 128 C 338.4 128, 342 126.5, 342 119.2 C 342 115.6, 338.4 112.1, 333.3 110.9 C 331.2 110.4, 324.9 110, 319.2 110 L 309 110 309 119 M 90.4 130.5 C 90 131.4, 90.4 132.8, 91.3 133.7 C 93.4 135.8, 96.2 134.6, 95.8 131.8 C 95.4 129, 91.3 128, 90.4 130.5 M 73.2 159.3 C 71.1 161.5, 71.1 161.7, 72.7 163.1 C 80.5 169.6, 96.6 177.2, 109.8 180.7 C 121.7 183.8, 138.9 184.2, 151.5 181.5 C 159.6 179.8, 174 174.4, 174 173.1 C 174 172.7, 173 171.4, 171.7 170.2 L 169.4 168 164.3 170 C 153.9 173.9, 144.9 175.5, 132 175.5 C 115.5 175.5, 108.4 173.6, 90 164.3 C 82 160.3, 75.5 157, 75.4 157 C 75.4 157, 74.4 158, 73.2 159.3 M 50 163.9 C 50 166.3, 52.5 168.4, 54.5 167.6 C 55.3 167.3, 56 165.9, 56 164.5 C 56 162.4, 55.5 162, 53 162 C 50.7 162, 50 162.5, 50 163.9" />
38
+ </svg>
39
+ <svg
40
+ v-else
41
+ class="beadcyte-mark is-glyph"
42
+ viewBox="0 0 265 210"
43
+ fill="currentColor"
44
+ fill-rule="evenodd"
45
+ aria-hidden="true"
46
+ focusable="false"
47
+ >
48
+ <path d="M 179.4 3.4 C 175.4 5.2, 171.2 10.3, 170.4 14.5 C 169.6 18.7, 171.5 24.7, 174.6 27.5 L 176.9 29.7 175 36.6 C 174 40.4, 172.6 45.3, 172.1 47.6 C 171.1 51.2, 170.5 51.8, 167.1 52.9 C 159.4 55.1, 154.5 64.1, 156.9 71.2 C 157.9 74.2, 157.8 74.7, 155.1 77.4 L 152.2 80.3 154.3 83.2 C 158.4 88.8, 156.2 97.4, 150 100 C 143.1 102.9, 136.1 98.4, 135.3 90.6 C 134.8 86.4, 135 85.8, 138.3 82.5 C 141.2 79.6, 142.5 79, 145.9 79 C 148.2 79, 150 78.6, 150 78.1 C 150 77.6, 147.9 76.2, 145.2 74.9 C 141.5 73, 138.9 72.5, 133 72.5 C 119.9 72.5, 108.8 79.4, 103.8 90.8 C 100.9 97.4, 100.3 107.5, 102.4 113.7 L 104.1 118.5 101 121.3 C 98.1 123.9, 97.8 124, 95 122.5 C 89.1 119.5, 82.1 121, 77 126.5 C 74.8 128.8, 74.5 130.1, 74.5 136.1 L 74.4 143.1 66.5 150.1 C 59.9 155.8, 58.1 156.9, 56 156.4 C 46.6 154.1, 42 155.1, 37.1 160.4 C 33.9 163.8, 33.2 171.2, 35.7 176 C 38.1 180.7, 45.1 184.3, 50.3 183.7 C 58.4 182.6, 63.2 176.7, 62.8 168.1 L 62.6 162.6 71.2 155.2 C 79 148.6, 80.1 148, 82.1 149 C 83.4 149.7, 86.7 150, 90.1 149.8 C 94.9 149.5, 96.6 148.8, 99 146.5 C 102.5 143.2, 104.4 137.2, 103.5 132.8 C 102.9 130.4, 103.3 129.3, 105.5 127 L 108.3 124.1 112.7 128 C 118.7 133.2, 125.4 135.4, 133.9 134.8 C 142.6 134.1, 150 130.8, 155.1 125.3 C 161.5 118.4, 163.5 113, 163.5 103 C 163.5 95.5, 163.2 93.9, 160.7 89.6 L 157.9 84.6 160.3 82.3 C 162.4 80.1, 163 80, 167.2 80.9 C 176.8 83.1, 184.8 77.7, 186 68.2 L 186.5 63.9 199.2 57.3 L 211.9 50.8 216.5 53 C 226.8 58, 238 51.5, 238 40.5 C 238 29, 227.2 23, 215.4 28 C 212.4 29.3, 211.9 29.1, 206 25.4 C 199.9 21.5, 199.8 21.4, 199.6 16.8 C 199.2 6.6, 188.3 -0.6, 179.4 3.4 M 186.4 10.5 C 184.9 14.3, 189.3 17.7, 191.6 14.5 C 192.5 13.2, 192.6 12.2, 191.9 10.9 C 190.7 8.6, 187.2 8.4, 186.4 10.5 M 120.5 24.7 C 106.3 26.7, 86 35.2, 70.1 45.8 C 61.9 51.3, 61.1 51.6, 58.9 50.4 C 53.8 47.6, 45.7 49.4, 41.1 54.4 C 38.3 57.3, 37 66.3, 38.8 69.7 C 39.7 71.4, 39.5 72.1, 37.3 74.1 C 35.9 75.4, 32.3 79.4, 29.3 83 L 23.9 89.5 18 89.5 C 11.7 89.5, 8.5 91.1, 4.6 96.3 C 1.9 100, 1.8 107.9, 4.6 111.7 C 7.8 116.1, 14.7 119.2, 19.6 118.4 C 23.7 117.8, 23.7 117.8, 29 124.4 C 34.3 131.2, 51.9 147.8, 56 150 C 58 151.1, 58.6 150.9, 60.9 148.6 L 63.5 146 61.2 144.4 C 50.1 136.6, 28.4 113.8, 29.6 111.1 C 32.1 105.3, 32.4 102, 30.7 98.5 L 29 95 36.7 86.2 C 40.9 81.4, 44.4 77.4, 44.5 77.2 C 44.6 77.1, 45.6 77.3, 46.6 77.7 C 48.2 78.4, 48.5 79.7, 48.6 86.5 C 48.7 93.3, 48.5 94.7, 46.9 95.8 C 43.3 98.3, 41.8 102.7, 43 106.5 C 45.5 114.1, 53.7 116.4, 59.2 111 C 61.7 108.5, 62.9 108, 67.1 108 C 71.3 108, 72 108.3, 72 110 C 72 111, 72.5 114.1, 73.1 116.8 L 74.1 121.7 78.8 119.4 C 81.4 118.1, 84.6 116.8, 86 116.6 L 88.6 116.1 87.6 109 C 84.6 87.6, 98.4 66.6, 119.7 60 C 127.5 57.6, 134.4 57.5, 142.5 59.6 C 145.8 60.4, 149.5 61.4, 150.6 61.6 C 152.3 62.1, 152.9 61.6, 153.4 59.6 C 153.7 58.2, 155.4 55.4, 157.1 53.5 C 158.9 51.5, 160.1 49.8, 159.9 49.6 C 158.1 48.3, 148.4 45, 143.6 44.1 C 129.9 41.6, 118.9 43.1, 106 49.2 C 89.2 57.2, 75.9 74.7, 72.9 92.5 L 71.6 100 66.9 100 C 62.9 100, 61.6 99.5, 59.1 96.9 C 56.2 94, 56 93.4, 56 86.2 C 56.1 78.7, 56.1 78.5, 59.3 76.5 C 64.7 73.1, 67.2 68.3, 66.8 62.5 L 66.4 57.4 76.1 51.2 C 106.6 31.6, 132.2 27, 159.8 36.2 C 164.9 37.9, 169.3 39.1, 169.5 38.8 C 169.7 38.6, 170.1 36.9, 170.2 35.1 C 170.5 31.9, 170.3 31.7, 165 29.8 C 151 24.9, 133.6 22.9, 120.5 24.7 M 189 30.5 L 183.6 32.8 180.9 43.2 L 178.2 53.5 180.3 55.2 C 181.5 56.2, 182.7 56.9, 183 56.9 C 183.3 56.9, 189.1 54.1, 196 50.7 L 208.5 44.5 208.8 40 L 209.1 35.5 202.7 31.7 C 199.2 29.7, 195.9 28.1, 195.4 28.1 C 194.9 28.2, 192 29.3, 189 30.5 M 226.2 32.7 C 223.6 33.7, 225.6 39, 228.7 39 C 229.4 39, 230.4 38.1, 231 37.1 C 231.7 35.6, 231.6 34.8, 230.4 33.6 C 228.8 31.9, 228.3 31.8, 226.2 32.7 M 53.7 57.4 C 53.4 58.2, 53.9 59.9, 54.9 61.1 C 57.3 64.1, 60.3 63, 59.8 59.3 C 59.6 57.1, 58.9 56.4, 56.9 56.2 C 55.2 56, 54 56.5, 53.7 57.4 M 204.8 58.8 C 203.2 59.8, 202 60.8, 202 61.1 C 202 61.4, 204.1 63.2, 206.7 65.1 C 211.9 68.8, 224 80.7, 231.4 89.3 C 235.7 94.3, 236 95, 234.9 97.4 C 233.8 99.7, 233.1 100, 228.2 100 C 223.6 100, 222.3 99.5, 219.3 97 C 214.9 93.1, 211.1 93.1, 206.7 97 C 203.4 99.9, 203.1 99.9, 190.9 100.2 L 178.5 100.5 177.9 108.2 C 176.6 123.6, 165.9 138.5, 151.3 145.2 C 139.3 150.7, 123.1 150.2, 110.8 143.9 L 107 141.9 106 144.6 C 105.4 146.1, 103.3 148.7, 101.4 150.5 L 97.9 153.8 103.2 156.8 C 122.6 167.8, 144.9 167.1, 165.2 154.9 L 169 152.6 171.9 155.4 C 173.8 157.3, 174.8 159.2, 174.8 160.9 C 174.7 162.4, 175.1 164.4, 175.6 165.3 C 176.1 166.2, 185.3 175.8, 195.9 186.5 C 216.5 207.2, 218.7 208.7, 225.3 206.9 C 229.4 205.8, 233.7 201.4, 234.9 197.2 C 236.9 190.3, 235.2 187.9, 214.1 166.8 C 196 148.6, 194.1 147, 190.9 147 C 188.3 147, 186.6 146.2, 184.3 143.8 L 181.2 140.5 185.1 134 C 189.4 126.9, 193 116.7, 193 111.4 L 193 108 198.4 108 C 202.6 108, 204.2 108.4, 205.2 109.8 C 208.8 115, 214.6 115.2, 219.9 110.5 C 223.2 107.5, 224.4 107, 228.7 107 C 233.1 107, 233.7 107.2, 234.6 109.9 C 235.6 112.4, 235.4 113.2, 233.2 116.1 C 229.2 121.4, 219.2 131.5, 210.3 139.2 L 202.1 146.2 204.5 148.8 C 206.7 151.2, 207.1 151.3, 209.5 150 C 213.3 148, 231.7 130.3, 237 123.6 C 241.4 118.1, 241.6 118, 245.5 118.6 C 254.2 119.8, 263 112.5, 263 104 C 263 95.6, 254.7 88.3, 246.3 89.4 C 242.2 89.9, 242.1 89.9, 235.6 82.4 C 226.5 72, 209.6 56.6, 207.8 57.1 C 207.7 57.1, 206.3 57.9, 204.8 58.8 M 172.6 60.5 C 171.9 62.5, 173.6 66, 175.3 66 C 177.7 66, 179.2 64.2, 178.8 61.9 C 178.4 59, 173.6 57.9, 172.6 60.5 M 185.4 80.2 C 184.3 81.3, 181.6 83.1, 179.2 84.2 C 176.9 85.2, 175 86.2, 175 86.3 C 175 86.4, 175.6 88.5, 176.4 91 L 177.7 95.5 185.1 95.1 C 189.2 94.9, 192.7 94.6, 192.8 94.5 C 193.4 94, 188.8 79, 188 78.5 C 187.6 78.2, 186.4 79, 185.4 80.2 M 19.1 97.3 C 16.6 100.4, 21.1 104.4, 23.9 101.5 C 25.1 100.3, 25.2 99.6, 24.4 98.2 C 22.9 96, 20.6 95.6, 19.1 97.3 M 250.7 96.6 C 249.4 97.9, 250 101.8, 251.5 102.4 C 254.2 103.4, 256.1 101.9, 255.8 99.1 C 255.5 96.5, 252.3 95, 250.7 96.6 M 90.4 130.5 C 90 131.4, 90.4 132.8, 91.3 133.7 C 93.4 135.8, 96.2 134.6, 95.8 131.8 C 95.4 129, 91.3 128, 90.4 130.5 M 73.2 159.3 C 71.1 161.5, 71.1 161.7, 72.7 163.1 C 80.5 169.6, 96.6 177.2, 109.8 180.7 C 121.7 183.8, 138.9 184.2, 151.5 181.5 C 159.6 179.8, 174 174.4, 174 173.1 C 174 172.7, 173 171.4, 171.7 170.2 L 169.4 168 164.3 170 C 153.9 173.9, 144.9 175.5, 132 175.5 C 115.5 175.5, 108.4 173.6, 90 164.3 C 82 160.3, 75.5 157, 75.4 157 C 75.4 157, 74.4 158, 73.2 159.3 M 50 163.9 C 50 166.3, 52.5 168.4, 54.5 167.6 C 55.3 167.3, 56 165.9, 56 164.5 C 56 162.4, 55.5 162, 53 162 C 50.7 162, 50 162.5, 50 163.9" />
49
+ </svg>
50
+ </template>
51
+
52
+ <style scoped>
53
+ /* Height-driven: callers set a height and the aspect ratio does the rest,
54
+ so the mark tracks whatever type size it sits beside. */
55
+ .beadcyte-mark {
56
+ display: block;
57
+ height: 1.6em;
58
+ width: auto;
59
+ }
60
+
61
+ .beadcyte-mark.is-glyph {
62
+ height: 1.25em;
63
+ }
64
+ </style>