From 0d2c3fcb43eabb1488ddb3d4f9997b9048f497dd Mon Sep 17 00:00:00 2001 From: Alex Swearingen Date: Tue, 29 Sep 2026 17:13:49 -0500 Subject: [PATCH 1/2] Adopt the shared SlopCop documentation design Move the docs onto the foundation stylesheet shared with UsageBench: Apfel Grotezk and JetBrains Mono, restrained chamfers, the octagon-node mark, and a centred content column with the contents list floating beside it. Tables sit in a scroll box and their long identifiers wrap at separators instead of widening the page; the component tables keep their figures and analyzer names on one line and give identifier columns a floor, so nothing pans at desktop widths. The landing page leads with a full-width verdict above equal columns, then the release figures under their own heading. The light theme deepens Starlight's orange, green, red and purple so chart labels in those analyzer colours stay legible; the dark theme and every published hue are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/astro.config.mjs | 15 +- docs/public/favicon.svg | 13 +- docs/src/assets/mark.svg | 15 + docs/src/components/AnalyzerLandscape.astro | 114 +- docs/src/components/AnalyzerProfiles.astro | 38 +- docs/src/components/BackToTop.astro | 15 +- docs/src/components/CaseEvidence.astro | 20 +- docs/src/components/DecisionLandscape.astro | 154 ++- docs/src/components/DimensionRates.astro | 14 +- docs/src/components/LandingResults.astro | 987 ++++++-------- docs/src/components/LanguageMatrix.astro | 24 +- docs/src/components/LatencyRanking.astro | 143 +-- docs/src/components/LatencyTier.astro | 50 +- docs/src/components/ScatterTooltip.astro | 13 +- docs/src/components/ScorecardSummary.astro | 17 +- docs/src/components/SnapshotArchive.astro | 29 +- docs/src/components/SnapshotBanner.astro | 22 +- docs/src/components/SnapshotEvolution.astro | 158 +-- docs/src/components/TemplateMatrix.astro | 20 +- docs/src/data/latency.ts | 8 +- docs/src/data/scatter-layout.test.ts | 61 + docs/src/data/scatter-layout.ts | 155 +++ docs/src/fonts/ApfelGrotezk-Fett.woff2 | Bin 0 -> 20128 bytes docs/src/fonts/ApfelGrotezk-Mittel.woff2 | Bin 0 -> 20564 bytes docs/src/fonts/ApfelGrotezk-Regular.woff2 | Bin 0 -> 19852 bytes docs/src/fonts/JetBrainsMono-wght.woff2 | Bin 0 -> 40404 bytes docs/src/fonts/OFL.txt | 100 ++ docs/src/plugins/rehype-table-scroll.mjs | 99 ++ docs/src/styles/custom.css | 252 ++-- docs/src/styles/foundation.css | 1278 +++++++++++++++++++ 30 files changed, 2703 insertions(+), 1111 deletions(-) create mode 100644 docs/src/assets/mark.svg create mode 100644 docs/src/data/scatter-layout.test.ts create mode 100644 docs/src/data/scatter-layout.ts create mode 100644 docs/src/fonts/ApfelGrotezk-Fett.woff2 create mode 100644 docs/src/fonts/ApfelGrotezk-Mittel.woff2 create mode 100644 docs/src/fonts/ApfelGrotezk-Regular.woff2 create mode 100644 docs/src/fonts/JetBrainsMono-wght.woff2 create mode 100644 docs/src/fonts/OFL.txt create mode 100644 docs/src/plugins/rehype-table-scroll.mjs create mode 100644 docs/src/styles/foundation.css diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 4edf905c2..27d2ad17d 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -4,6 +4,7 @@ import { defineConfig } from 'astro/config'; import starlight from '@astrojs/starlight'; import { snapshots } from './src/data/snapshots'; import { remarkHeadingIds } from './src/plugins/remark-heading-ids.mjs'; +import { rehypeTableScroll } from './src/plugins/rehype-table-scroll.mjs'; // --------------------------------------------------------------------------- // Snapshot navigation. @@ -64,6 +65,8 @@ export default defineConfig({ // `## Heading {#legacy-id}` — explicit anchors for headings whose ids are // citable and must not follow the auto-slugger. See the plugin's comment. remarkPlugins: [remarkHeadingIds], + // Each table in a scroll box, so a wide one pans instead of overflowing. + rehypePlugins: [rehypeTableScroll], }, redirects: { // Explicit current-snapshot pointer alongside versioned snapshot URLs. @@ -75,7 +78,17 @@ export default defineConfig({ favicon: '/favicon.svg', description: 'Analyzer-neutral benchmark for data-flow analysis, published exclusively from immutable freeze evidence.', - customCss: ['./src/styles/custom.css'], + // The shared foundation first (identical in the UsageBench repository), + // then this site's accent, palette and layout on top of it. + customCss: ['./src/styles/foundation.css', './src/styles/custom.css'], + logo: { src: './src/assets/mark.svg', alt: '' }, + expressiveCode: { + styleOverrides: { + borderRadius: '0px', + codeFontFamily: "'JetBrains Mono', ui-monospace, monospace", + uiFontFamily: "'Apfel Grotezk', ui-sans-serif, system-ui, sans-serif", + }, + }, components: { // Starlight's own footer, with the floating "back to top" control // appended. The footer is the one slot that renders exactly once on diff --git a/docs/public/favicon.svg b/docs/public/favicon.svg index 182f96962..f45ab4a4c 100644 --- a/docs/public/favicon.svg +++ b/docs/public/favicon.svg @@ -1,14 +1,15 @@ - - + + - - - + + + - + diff --git a/docs/src/assets/mark.svg b/docs/src/assets/mark.svg new file mode 100644 index 000000000..f45ab4a4c --- /dev/null +++ b/docs/src/assets/mark.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + diff --git a/docs/src/components/AnalyzerLandscape.astro b/docs/src/components/AnalyzerLandscape.astro index 4c0f44bd4..93954b3df 100644 --- a/docs/src/components/AnalyzerLandscape.astro +++ b/docs/src/components/AnalyzerLandscape.astro @@ -12,6 +12,7 @@ import { vendorOrder, } from '../data/snapshots'; import { analyzerLanguageSupport, type AnalyzerLanguageSupport } from '../data/analyzer-language-support'; +import { percentAxis, placeLabels } from '../data/scatter-layout'; import ScatterTooltip from './ScatterTooltip.astro'; interface AnalyzerPoint { @@ -67,28 +68,28 @@ const formatPercent = (value: number) => `${value.toFixed(1)}%`; const width = 820; const height = 470; -const padLeft = 66; -const padRight = 30; -const padTop = 34; -const padBottom = 62; +const padLeft = 60; +const padRight = 24; +const padTop = 28; +const padBottom = 56; const plotWidth = width - padLeft - padRight; const plotHeight = height - padTop - padBottom; const languageAxisMax = 14; +// The vertical axis starts at the tens below the lowest analyzer rather than +// at 0, so the plot spends its height on the range the analyzers occupy. +const axis = percentAxis(points.map((point) => percent(point.correct, point.covered))); const x = (languages: number) => padLeft + languages * plotWidth / languageAxisMax; -const y = (correct: number, covered: number) => padTop + (100 - percent(correct, covered)) * plotHeight / 100; +const y = (value: number) => padTop + (axis.max - value) * plotHeight / (axis.max - axis.min); const xTicks = [0, 2, 4, 6, 8, 10, 12, 14]; -const yTicks = [50, 60, 70, 80, 90, 100]; -// Small, stable offsets keep labels readable without changing point geometry. -const labelOffsets: Record = { - bifrost: { x: -9, y: -11, anchor: 'end' }, - codeql: { x: -9, y: 17, anchor: 'end' }, - joern: { x: -9, y: -11, anchor: 'end' }, - semgrep: { x: -9, y: 17, anchor: 'end' }, - opentaint: { x: -10, y: -11, anchor: 'end' }, - infer: { x: 9, y: -11 }, - flowdroid: { x: 9, y: 17 }, - pysa: { x: 9, y: 17 }, -}; +const plotted = points.map((point) => ({ + ...point, + px: x(point.languageSupport.languages.length), + py: y(percent(point.correct, point.covered)), +})); +const labels = placeLabels( + plotted.map((point) => ({ x: point.px, y: point.py, text: point.name })), + { bounds: { left: padLeft + 4, top: 4, right: width - 4, bottom: height - padBottom - 4 }, radius: 8, fontSize: 13, charWidth: 0.58 }, +); ---

Accuracy and language coverage — current snapshot

@@ -108,33 +109,26 @@ const labelOffsets: Record Current analyzer accuracy by supported data-flow languages Scatter plot of all analyzers in DataFlowBench {currentSnapshot.version}. The horizontal axis is the documented number of languages in which each analyzer performs data-flow analysis. The vertical axis is decisive-correct assertions divided by every assertion in the benchmark kernels that analyzer covers, including non-answers in the denominator. - {yTicks.map((tick) => { - const tickY = padTop + (100 - tick) * plotHeight / 100; - return <> - - {tick}% - ; - })} - {xTicks.map((tick) => { - const tickX = x(tick); - return <> - - {tick} - ; - })} + {axis.ticks.map((tick) => <> + + {tick}% + )} + {xTicks.map((tick) => <> + + {tick} + )} - languages with documented data-flow support - accuracy within covered kernels - {points.map((point) => { - const pointX = x(point.languageSupport.languages.length); - const pointY = y(point.correct, point.covered); - const offset = labelOffsets[point.tool] ?? { x: 9, y: -9 }; + Languages with documented data-flow support + Accuracy within covered kernels + {plotted.map((point, index) => { + const label = labels[index]; const accuracy = percent(point.correct, point.covered); const tooltip = `${point.name} — x: ${point.languageSupport.languages.length} supported languages · y: ${formatPercent(accuracy)} accuracy`; return - - {point.name} + {label.leader && } + + {point.name} ; })} @@ -148,15 +142,15 @@ const labelOffsets: Record Exact figures -
+
{points.map((point) => - - + + )}
AnalyzerScopeSupported languagesBenchmark kernelsAccuracyCorrectWrongIncomplete
{point.name} {point.cohort} {point.languageSupport.languages.length}{point.languageSupport.languages.join(', ')}{point.kernels}/{kernelCount} ({formatPercent(percent(point.kernels, kernelCount))}){point.correct}/{point.covered} ({formatPercent(percent(point.correct, point.covered))}){point.kernels}/{kernelCount} ({formatPercent(percent(point.kernels, kernelCount))}){point.correct}/{point.covered} ({formatPercent(percent(point.correct, point.covered))}) {point.correct}{point.wrong}{point.incomplete}
@@ -164,26 +158,32 @@ const labelOffsets: Record diff --git a/docs/src/components/AnalyzerProfiles.astro b/docs/src/components/AnalyzerProfiles.astro index a51a64129..e44a47224 100644 --- a/docs/src/components/AnalyzerProfiles.astro +++ b/docs/src/components/AnalyzerProfiles.astro @@ -300,17 +300,19 @@ const cards = model.profiles.map((profile) => { } .profile-card { margin: 0; - border: 1px solid var(--sl-color-hairline); - border-radius: 0.75rem; - padding: 0.75rem 0.75rem 0.6rem; - background: var(--sl-color-bg-sidebar); + border: 1px solid var(--ds-line); + border-radius: var(--ds-cut-control); + corner-shape: bevel; + padding: 0.95rem 1rem 0.85rem; + background: var(--ds-surface); } .profile-name { display: flex; align-items: center; - font-weight: 600; + font-weight: 500; font-size: var(--sl-text-base); - color: var(--sl-color-white); + letter-spacing: -0.01em; + color: var(--ds-text); margin: 0 0 0.25rem; } .vendor-dot { @@ -318,7 +320,7 @@ const cards = model.profiles.map((profile) => { width: 0.55rem; height: 0.55rem; border-radius: 50%; - margin-right: 0.4rem; + margin-right: 0.45rem; background: var(--vendor-rule, var(--sl-color-accent)); } .profile-radar { @@ -328,11 +330,11 @@ const cards = model.profiles.map((profile) => { } .profile-radar .ring { fill: none; - stroke: var(--sl-color-hairline); + stroke: var(--ds-line); stroke-width: 1; } .profile-radar .spoke { - stroke: var(--sl-color-hairline); + stroke: var(--ds-line-soft); stroke-width: 1; } .profile-radar .shape { @@ -346,9 +348,10 @@ const cards = model.profiles.map((profile) => { fill: var(--vendor-rule, var(--sl-color-accent)); } .profile-radar .axis-label { - font-family: var(--__sl-font-mono); - font-size: 11px; - fill: var(--sl-color-gray-3); + font-family: var(--ds-font); + font-size: 11.5px; + font-weight: 500; + fill: var(--ds-text-3); } .readings { display: grid; @@ -358,26 +361,25 @@ const cards = model.profiles.map((profile) => { font-size: var(--sl-text-xs); } .readings dt { - font-family: var(--__sl-font-mono); - color: var(--sl-color-gray-3); + color: var(--ds-text-3); } .readings dd { margin: 0; display: flex; flex-direction: column; line-height: 1.3; - color: var(--sl-color-gray-2); + color: var(--ds-text-2); } .readings .basis { - color: var(--sl-color-gray-3); + color: var(--ds-text-3); } .axis-notes { font-size: var(--sl-text-sm); - color: var(--sl-color-gray-3); + color: var(--ds-text-3); } .caption { font-size: var(--sl-text-sm); - color: var(--sl-color-gray-3); + color: var(--ds-text-3); } /* The same fixed vendor→colour identity map every other section uses. */ .v0 { diff --git a/docs/src/components/BackToTop.astro b/docs/src/components/BackToTop.astro index 431758f54..84b88bfa6 100644 --- a/docs/src/components/BackToTop.astro +++ b/docs/src/components/BackToTop.astro @@ -68,13 +68,14 @@ justify-content: center; width: 2.5rem; height: 2.5rem; - border: 1px solid var(--sl-color-gray-5); - border-radius: 999px; + border: 1px solid var(--ds-line-strong); + border-radius: var(--ds-cut-chip); + corner-shape: bevel; /* The nav's own background: this floats over the page, so it should read as chrome rather than as another card. */ background: var(--sl-color-bg-nav); - color: var(--sl-color-gray-2); - box-shadow: var(--sl-shadow-md); + color: var(--ds-text-2); + box-shadow: var(--ds-shadow-float); /* Hidden until the reader has scrolled. `visibility` rather than opacity alone so the anchor leaves the tab order while it is not on offer. */ @@ -94,12 +95,12 @@ } .to-top:hover { - color: var(--sl-color-white); - border-color: var(--sl-color-accent); + color: var(--ds-text); + border-color: var(--ds-accent); } .to-top:focus-visible { - outline: 2px solid var(--sl-color-accent-high); + outline: 2px solid var(--ds-accent); outline-offset: 2px; } diff --git a/docs/src/components/CaseEvidence.astro b/docs/src/components/CaseEvidence.astro index 1103b9fdb..e8d157784 100644 --- a/docs/src/components/CaseEvidence.astro +++ b/docs/src/components/CaseEvidence.astro @@ -14,7 +14,7 @@ interface Props { const { snapshot, tier } = Astro.props; --- -
+
@@ -30,17 +30,17 @@ const { snapshot, tier } = Astro.props; { tier.cases.map((result) => ( - - - +
+ {result.template_id.replace('dfb-template-', '')} + {result.case_id} {result.polarity} {result.outcome} {result.classification}{result.classification} {shortDigest(result.raw_evidence.sha256)}… @@ -57,4 +57,16 @@ const { snapshot, tier } = Astro.props; .table-scroll { overflow-x: auto; } + /* Template and case ids wrap at their hyphens, two or three pieces to a + line; below that the table pans instead of stacking one piece per line. + A classification such as true-negative is one term. */ + .template { + min-width: 9rem; + } + .case { + min-width: 11rem; + } + .classification { + white-space: nowrap; + } diff --git a/docs/src/components/DecisionLandscape.astro b/docs/src/components/DecisionLandscape.astro index cf5467bed..aed02e409 100644 --- a/docs/src/components/DecisionLandscape.astro +++ b/docs/src/components/DecisionLandscape.astro @@ -11,6 +11,7 @@ import { vendorName, vendorOrder, } from '../data/snapshots'; +import { percentAxis, placeLabels, type LabelPlacement, type PercentAxis } from '../data/scatter-layout'; import ScatterTooltip from './ScatterTooltip.astro'; interface Point { @@ -30,6 +31,13 @@ interface Panel { points: Point[]; } +interface PlottedPoint extends Point { + decided: number; + px: number; + py: number; + label: LabelPlacement; +} + const populations = coreKernelPopulations(currentSnapshot.results); const totalAssertions = populations.reduce((sum, population) => sum + population.cases, 0); @@ -73,36 +81,49 @@ const panels: Panel[] = [ const width = 820; const height = 470; -const padLeft = 66; -const padRight = 30; -const padTop = 34; -const padBottom = 62; +const padLeft = 60; +const padRight = 24; +const padTop = 28; +const padBottom = 56; const plotWidth = width - padLeft - padRight; const plotHeight = height - padTop - padBottom; const percent = (part: number, whole: number) => whole === 0 ? 0 : (100 * part) / whole; const formatPercent = (value: number) => `${value.toFixed(1)}%`; -const x = (decided: number, denominator: number) => padLeft + percent(decided, denominator) * plotWidth / 100; -const y = (correct: number, decided: number) => padTop + (100 - percent(correct, decided)) * plotHeight / 100; const xTicks = [0, 25, 50, 75, 100]; -const yTicks = [50, 60, 70, 80, 90, 100]; -const labelOffsets: Record = { - bifrost: { x: -9, y: -11, anchor: 'end' }, - codeql: { x: -9, y: 17, anchor: 'end' }, - joern: { x: -9, y: -11, anchor: 'end' }, - semgrep: { x: 9, y: 17 }, - opentaint: { x: -10, y: -11, anchor: 'end' }, - infer: { x: 9, y: -11 }, - flowdroid: { x: 9, y: 17 }, - pysa: { x: -9, y: 17, anchor: 'end' }, -}; +// Blind answering — the same verdict on both halves of every pair — is right +// exactly half the time, so 50% correctness is the floor worth marking. +const baseline = 50; + +// Each view gets a vertical axis fitted to its own points, trimmed from 0 to +// the tens below the lowest one but always keeping the baseline clear of the +// floor: most views share the same 40–100% axis, and one that dips below it +// says so on its ticks. +// Labels are placed per view, since which points crowd depends on the view. +const layouts = panels.map((panel) => { + const axis: PercentAxis = percentAxis( + panel.points.map((point) => percent(point.correct, point.correct + point.wrong)), + baseline - 10, + ); + const x = (value: number) => padLeft + value * plotWidth / 100; + const y = (value: number) => padTop + (axis.max - value) * plotHeight / (axis.max - axis.min); + const located = panel.points.map((point) => { + const decided = point.correct + point.wrong; + return { ...point, decided, px: x(percent(decided, panel.denominator)), py: y(percent(point.correct, decided)) }; + }); + const labels = placeLabels( + located.map((point) => ({ x: point.px, y: point.py, text: point.name })), + { bounds: { left: padLeft + 4, top: 4, right: width - 4, bottom: height - padBottom - 4 }, radius: 8, fontSize: 13, charWidth: 0.58 }, + ); + const points: PlottedPoint[] = located.map((point, index) => ({ ...point, label: labels[index] })); + return { panel, axis, x, y, points }; +}); const toggleCss = panels.map((panel) => ` #decision-view-${panel.id}:checked ~ .decision-ribbon label[for='decision-view-${panel.id}'] { - background: var(--sl-color-accent-low); color: var(--sl-color-white); - box-shadow: inset 0 0 0 1px var(--sl-color-accent); + border-color: var(--ds-accent); background: var(--ds-accent-low); color: var(--ds-text); } #decision-view-${panel.id}:focus-visible ~ .decision-ribbon label[for='decision-view-${panel.id}'] { - outline: 2px solid var(--sl-color-accent-high); outline-offset: 2px; + outline: 2px solid var(--ds-accent); outline-offset: 2px; } #decision-view-${panel.id}:checked ~ .decision-panels > #decision-panel-${panel.id} { display: block; } `).join('\n'); @@ -111,35 +132,34 @@ const toggleCss = panels.map((panel) => `
{panels.map((panel, index) => )}
- {panels.map((panel) => )} + {panels.map((panel, index) => )}
- {panels.map((panel) =>
+ {layouts.map(({ panel, axis, x, y, points }) =>
{panel.label} analyzer decisiveness and correctness - Scatter plot for {panel.label}. The horizontal axis is decisive answers divided by {panel.denominator} assertions. The vertical axis is correct answers divided by decisive answers. - {yTicks.map((tick) => { - const tickY = padTop + (100 - tick) * plotHeight / 100; - return <>{tick}%; - })} - {xTicks.map((tick) => { - const tickX = padLeft + tick * plotWidth / 100; - return <>{tick}%; - })} + Scatter plot for {panel.label}. The horizontal axis is decisive answers divided by {panel.denominator} assertions. The vertical axis is correct answers divided by decisive answers, from {axis.min}% to 100%; a line marks the {baseline}% a blind answerer would score. + {axis.ticks.map((tick) => <> + + {tick}% + )} + {xTicks.map((tick) => <> + + {tick}% + )} + + blind baseline - decisiveness — share of {panel.denominator} assertions answered - correctness among decisive answers - {panel.points.map((point) => { - const decided = point.correct + point.wrong; - const pointX = x(decided, panel.denominator); - const pointY = y(point.correct, decided); - const offset = labelOffsets[point.tool] ?? { x: 9, y: -9 }; - const tooltip = `${point.name} — x: ${formatPercent(percent(decided, panel.denominator))} decisiveness (${decided}/${panel.denominator}) · y: ${formatPercent(percent(point.correct, decided))} correctness (${point.correct}/${decided})`; + Decisiveness: share of {panel.denominator} assertions answered + Correctness among decisive answers + {points.map((point) => { + const tooltip = `${point.name} — x: ${formatPercent(percent(point.decided, panel.denominator))} decisiveness (${point.decided}/${panel.denominator}) · y: ${formatPercent(percent(point.correct, point.decided))} correctness (${point.correct}/${point.decided})`; return - - {point.name} + {point.label.leader && } + + {point.name} ; })} @@ -152,16 +172,16 @@ const toggleCss = panels.map((panel) => `

Exact figures -
+
{panel.kernelId && }{panel.points.map((point) => { const decided = point.correct + point.wrong; return - - + + - {panel.kernelId && } + {panel.kernelId && } ; })}
AnalyzerDecisivenessCorrectness when decisiveUnansweredEvidence
{point.name}{decided}/{panel.denominator} ({formatPercent(percent(decided, panel.denominator))}){point.correct}/{decided} ({formatPercent(percent(point.correct, decided))}){decided}/{panel.denominator} ({formatPercent(percent(decided, panel.denominator))}){point.correct}/{decided} ({formatPercent(percent(point.correct, decided))}) {panel.denominator - decided}
@@ -172,30 +192,38 @@ const toggleCss = panels.map((panel) => ` diff --git a/docs/src/components/DimensionRates.astro b/docs/src/components/DimensionRates.astro index ad96ffb89..cb254563c 100644 --- a/docs/src/components/DimensionRates.astro +++ b/docs/src/components/DimensionRates.astro @@ -18,7 +18,7 @@ function fraction(rate: RateFraction | null): string { } --- -
+
@@ -50,10 +50,10 @@ function fraction(rate: RateFraction | null): string { - - - - + + + + )) } @@ -65,4 +65,8 @@ function fraction(rate: RateFraction | null): string { .table-scroll { overflow-x: auto; } + /* A rate reads as one figure; its header wraps over it instead. */ + .rate { + white-space: nowrap; + } diff --git a/docs/src/components/LandingResults.astro b/docs/src/components/LandingResults.astro index df33f336f..3a429699f 100644 --- a/docs/src/components/LandingResults.astro +++ b/docs/src/components/LandingResults.astro @@ -469,54 +469,25 @@ const nativeDecidingAdapters = [ const snapshotPage = `/snapshots/${snapshot.slug}`; --- -

- Immutable evaluation · DataFlowBench {snapshot.version} -

- -{/* The subtitle the page title used to be. The h1 names the benchmark, which - is what a reader arriving from a citation is looking for; the motivation - belongs directly under it, stated rather than implied. */} -

- BrokkAi's benchmark for semantic data flow: Bifrost measured against {analyzerCount - 1} reference analyzers, from immutable - freeze evidence. -

- -{/* Starlight's "On this page" nav is built from markdown headings and cannot - see the h2s this component emits, so the landing page carries its own - section strip. The ids it targets are permanent deep-link anchors: chosen - once, never renamed. */} - - -{/* What the benchmark is, ahead of what this particular freeze found. These - two describe the dataset and the conflict of interest behind it, so they - belong above the verdict rather than inside it — a reader meeting the page - for the first time needs them before the numbers mean anything. Wrapped - because they are adjacent: two paragraphs each holding their own pair of - columns read in the wrong order (see `.prose-columns` in custom.css). */} -
-

- DataFlowBench is BrokkAi's own - benchmark: we build it to measure where Bifrost stands against other analyzers, and to keep ourselves honest while doing - it. The methodology is deliberately analyzer-neutral — every number below is - generated from digest-bound freeze evidence that we cannot edit after the - fact, misses and crashes included — but the motivation is not neutral, and - we would rather say so. +{/* The verdict answers the page title, so it follows it directly, across the + full column above the two paragraphs that frame it: how the kernels are + read, and who builds the benchmark and why. A reader meeting the page for + the first time needs both before the numbers mean anything. */} +

+ Nobody is perfect. No analyzer answers a whole core correctly in any of the + {kernels.length} languages{spotless.length === 0 ? ( + <>, and every one of the {analyzerCount} in the field makes at least one + decisive mistake. + ) : ( + <>, and of the {analyzerCount} in the field only{' '} + {spotless.map((tool) => tool.name).join(' and ')} makes no decisive + mistake at all — bought by declining{' '} + {spotless.reduce((sum, tool) => sum + tool.incomplete, 0)} of the{' '} + {coreAssertions} core assertions rather than guessing at them. + )}

+

Each kernel poses one positive and one negative assertion per semantic template, and each language's kernel is its own population — cores of @@ -529,24 +500,39 @@ const snapshotPage = `/snapshots/${snapshot.slug}`; dialog. A blank analyzer on a kernel means no report in this freeze: no extractor, no frontend, or no adapter, which is coverage rather than a score.

+

+ DataFlowBench is BrokkAi's own + benchmark for semantic data flow: we build it to measure where Bifrost stands against {analyzerCount - 1} reference analyzers, and to keep + ourselves honest while doing it. The methodology is deliberately + analyzer-neutral — every number here is generated from digest-bound freeze + evidence that we cannot edit after the fact, misses and crashes included — + but the motivation is not neutral, and we would rather say so. +

- +
+
+
Frozen case results, across three populations
+
{caseCount.toLocaleString('en-US')}
+
+
+
Kernel languages, {templateSizes} templates each
+
{kernels.length}
+
+
+
Languages in the direct-flow breadth baseline
+
{breadth.length}
+
+
+
Immutable evidence release
+
{snapshot.version}
+
+
+
+

How decisive, and how correct

@@ -873,25 +878,6 @@ const snapshotPage = `/snapshots/${snapshot.slug}`;

-
-
-

{caseCount}

-

frozen case results, across three populations

-
-
-

{breadth.length}

-

languages in the breadth baseline

-
-
-

{kernels.length}

-

kernel languages ({templateSizes} templates each)

-
-
-

{snapshot.version}

-

immutable evidence release

-
-
-

Modeling matrix — is the model surface load-bearing?

@@ -915,16 +901,12 @@ const snapshotPage = `/snapshots/${snapshot.slug}`;

- {card.name} {card.toolVersion} + {card.name} + {card.toolVersion}

{card.rows.map((row) => (
-

- {row.language} - - {row.scored} scored · {row.unsupported} declined of {row.total} - -

+ {row.language} -

- {tierVerdict(row)} - {row.inconclusive > 0 ? ` · ${row.inconclusive} inconclusive` : ''} +

+ + {tierVerdict(row)} + {row.inconclusive > 0 ? ` · ${row.inconclusive} inconclusive` : ''} + + + {row.scored} scored · {row.unsupported} declined of {row.total} +

))} @@ -996,16 +983,12 @@ const snapshotPage = `/snapshots/${snapshot.slug}`;

- {card.name} {card.toolVersion} + {card.name} + {card.toolVersion}

{card.rows.map((row) => (
-

- {row.language} - - {row.scored} scored · {row.unsupported} declined of {row.total} - -

+ {row.language} -

- {tierVerdict(row)} - {row.falsePositives > 0 ? ` · ${row.falsePositives} FP` : ''} - {row.falseNegatives > 0 ? ` · ${row.falseNegatives} FN` : ''} +

+ + {tierVerdict(row)} + {row.falsePositives > 0 ? ` · ${row.falsePositives} FP` : ''} + {row.falseNegatives > 0 ? ` · ${row.falseNegatives} FN` : ''} + + + {row.scored} scored · {row.unsupported} declined of {row.total} +

))} @@ -1100,10 +1088,20 @@ const snapshotPage = `/snapshots/${snapshot.slug}`;
{kernels.map((kernel) => (
-

- {kernel.language} - {kernel.cases} assertions -

+
+

+ {kernel.language} + {kernel.cases} assertions +

+ +
{kernel.vendors.map((vendor, index) => (

@@ -1128,21 +1126,11 @@ const snapshotPage = `/snapshots/${snapshot.slug}`; class="seg declined" style={`width:${barWidth(vendor.unsupported, vendor.total)}`}>

-

+

{vendor.correct}/{vendor.total}

))} -

- -

))}
@@ -1242,11 +1230,13 @@ const snapshotPage = `/snapshots/${snapshot.slug}`; width: min(64rem, calc(100vw - 2rem)); max-height: calc(100vh - 4rem); overflow-y: auto; - border: 1px solid var(--sl-color-accent); - border-radius: 0.75rem; - padding: 1.25rem 1.5rem 1.5rem; - background: var(--sl-color-bg-sidebar); + border: 1px solid var(--ds-line-strong); + border-radius: var(--ds-cut-panel); + corner-shape: bevel; + padding: 1.5rem 1.75rem 1.75rem; + background: var(--ds-surface); color: var(--sl-color-text); + box-shadow: var(--ds-shadow-panel); } /* Starlight's content flow adds margin-top to every sibling block; the dialog lays out its own spacing. */ @@ -1254,7 +1244,7 @@ const snapshotPage = `/snapshots/${snapshot.slug}`; margin-top: 0; } #kernel-dialog::backdrop { - background: rgb(0 0 0 / 0.6); + background: var(--sl-color-backdrop-overlay); } #kernel-dialog .dialog-head { display: flex; @@ -1263,39 +1253,48 @@ const snapshotPage = `/snapshots/${snapshot.slug}`; gap: 1rem; } #kernel-dialog .label { - font-family: var(--__sl-font-mono); font-size: var(--sl-text-xs); - letter-spacing: 0.15em; - text-transform: uppercase; - color: var(--sl-color-accent-high); + font-weight: 500; + color: var(--ds-text-3); margin: 0; } #kernel-dialog h2 { font-size: var(--sl-text-2xl); - line-height: 1.2; - margin: 0.25rem 0 0; - color: var(--sl-color-white); + font-weight: 500; + letter-spacing: -0.03em; + line-height: 1.15; + margin: 0.3rem 0 0; + color: var(--ds-text); border: 0; padding: 0; } #kernel-dialog .caption { font-size: var(--sl-text-sm); - color: var(--sl-color-gray-3); - margin: 0.25rem 0 0; + color: var(--ds-text-3); + margin: 0.3rem 0 0; } #kernel-dialog .dialog-close { flex: 0 0 auto; - font-size: 1.5rem; + display: inline-flex; + align-items: center; + justify-content: center; + width: 2.25rem; + height: 2.25rem; + font-size: 1.25rem; line-height: 1; - padding: 0.25rem 0.6rem; - border: 1px solid var(--sl-color-hairline); - border-radius: 0.5rem; - background: transparent; - color: var(--sl-color-text); + border: 1px solid var(--ds-line-strong); + border-radius: var(--ds-cut-chip); + corner-shape: bevel; + background: var(--ds-control); + color: var(--ds-text-2); cursor: pointer; + transition: + border-color var(--ds-duration) ease, + color var(--ds-duration) ease; } #kernel-dialog .dialog-close:hover { - background: var(--sl-color-gray-6); + border-color: var(--ds-accent-line); + color: var(--ds-text); } /* A grid rather than a wrapping flex row: with four analyzers the leftover panel on the second row keeps its column width instead of stretching to @@ -1304,57 +1303,78 @@ const snapshotPage = `/snapshots/${snapshot.slug}`; display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1rem; - margin-top: 1.25rem; + margin-top: 1.5rem; } #kernel-dialog .panel { - border: 1px solid var(--sl-color-hairline); - border-radius: 0.75rem; - padding: 0.75rem 1rem 1rem; + border: 1px solid var(--ds-line); + border-radius: var(--ds-cut-control); + corner-shape: bevel; + background: var(--ds-canvas); + padding: 1rem 1.1rem 1.1rem; + } + #kernel-dialog .panel-head { + padding-bottom: 0.6rem; + border-bottom: 1px solid var(--ds-line-soft); } #kernel-dialog .panel .who { - font-family: var(--__sl-font-mono); - font-size: var(--sl-text-xs); - letter-spacing: 0.1em; - text-transform: uppercase; + display: flex; + align-items: center; + gap: 0.45rem; + font-size: var(--sl-text-base); + font-weight: 500; + letter-spacing: -0.01em; margin: 0; - color: var(--vendor-color, var(--sl-color-accent-high)); - border-top: 3px solid var(--vendor-color, var(--sl-color-accent)); - padding-top: 0.25rem; + color: var(--ds-text); + } + #kernel-dialog .panel .who::before { + content: ''; + flex: none; + width: 0.55rem; + height: 0.55rem; + border-radius: 50%; + background: var(--vendor-rule, var(--ds-accent)); + } + #kernel-dialog .panel-head .caption { + font-family: var(--ds-font-mono); + font-size: var(--sl-text-2xs); } #kernel-dialog .matrix { width: 100%; - margin: 0.75rem 0 0; + margin: 0.85rem 0 0; border-collapse: collapse; font-size: var(--sl-text-sm); } #kernel-dialog .matrix th { - font-family: var(--__sl-font-mono); - font-size: var(--sl-text-xs); + font-family: var(--ds-font-mono); + font-size: var(--sl-text-2xs); font-weight: 400; - color: var(--sl-color-gray-3); + color: var(--ds-text-3); text-align: left; padding: 0.25rem 0.4rem; border: 0; background: transparent; } #kernel-dialog .matrix td { - border: 1px solid var(--sl-color-hairline); - padding: 0.4rem 0.5rem; + border: 1px solid var(--ds-line); + padding: 0.45rem 0.55rem; text-align: left; + background: transparent; } #kernel-dialog .matrix .matrix-corner { border: 0; } #kernel-dialog .matrix-count { - font-size: 1.5rem; - font-weight: 700; + font-family: var(--ds-font-mono); + font-variant-numeric: tabular-nums; + font-size: 1.375rem; + font-weight: 500; line-height: 1.1; margin-right: 0.35rem; } #kernel-dialog .matrix-tag { - font-family: var(--__sl-font-mono); - font-size: var(--sl-text-xs); - color: var(--sl-color-gray-3); + font-family: var(--ds-font-mono); + font-size: var(--sl-text-2xs); + color: var(--ds-text-3); } #kernel-dialog .matrix-cell.good .matrix-count { color: var(--sl-color-green-high); @@ -1364,34 +1384,32 @@ const snapshotPage = `/snapshots/${snapshot.slug}`; } #kernel-dialog .rates { display: flex; - gap: 1.5rem; - margin-top: 0.75rem; + gap: 1.75rem; + margin-top: 0.85rem; } #kernel-dialog .rate-label { - font-family: var(--__sl-font-mono); font-size: var(--sl-text-xs); - letter-spacing: 0.1em; - color: var(--sl-color-gray-3); + font-weight: 500; + color: var(--ds-text-3); margin: 0; } #kernel-dialog .rate-value { - font-size: 1.25rem; - font-weight: 700; + font-family: var(--ds-font-mono); + font-variant-numeric: tabular-nums; + font-size: 1.2rem; + font-weight: 500; margin: 0; - color: var(--vendor-color, var(--sl-color-accent-high)); + color: var(--vendor-color, var(--ds-accent-text)); } #kernel-dialog .coverage { - margin-top: 0.75rem; - border: 1px dashed var(--sl-color-orange); - border-radius: 0.5rem; - padding: 0.5rem 0.75rem; - background: var(--sl-color-bg); + margin-top: 0.85rem; + border: 1px dashed var(--ds-line-strong); + padding: 0.55rem 0.75rem; + background: transparent; } #kernel-dialog .coverage-label { - font-family: var(--__sl-font-mono); font-size: var(--sl-text-xs); - letter-spacing: 0.08em; - text-transform: uppercase; + font-weight: 500; color: var(--sl-color-orange-high); margin: 0; } @@ -1407,21 +1425,24 @@ const snapshotPage = `/snapshots/${snapshot.slug}`; margin: 0; } #kernel-dialog .coverage-count { - font-weight: 700; - margin-right: 0.25rem; + font-family: var(--ds-font-mono); + font-weight: 500; + color: var(--ds-text); + margin-right: 0.3rem; } #kernel-dialog .coverage-name { - font-family: var(--__sl-font-mono); - font-size: var(--sl-text-xs); - color: var(--sl-color-gray-3); + font-family: var(--ds-font-mono); + font-size: var(--sl-text-2xs); + color: var(--ds-text-3); } #kernel-dialog .scorecard { - font-family: var(--__sl-font-mono); - font-size: var(--sl-text-xs); + font-family: var(--ds-font-mono); + font-size: var(--sl-text-2xs); overflow-wrap: anywhere; } #kernel-dialog .dialog-foot { - margin-top: 1rem; + margin-top: 1.25rem; + max-width: 48rem; } #kernel-dialog .v0 { --vendor-color: var(--sl-color-accent-high); @@ -1456,330 +1477,59 @@ const snapshotPage = `/snapshots/${snapshot.slug}`; --vendor-color: var(--dfb-vendor-7); --vendor-rule: var(--dfb-vendor-7); } - #kernel-dialog .panel .who { - border-top-color: var(--vendor-rule, var(--sl-color-accent)); - } diff --git a/docs/src/components/LatencyRanking.astro b/docs/src/components/LatencyRanking.astro index e93fe6ca8..35f36d0fe 100644 --- a/docs/src/components/LatencyRanking.astro +++ b/docs/src/components/LatencyRanking.astro @@ -228,12 +228,14 @@ function layout(view: (typeof ranking.views)[number]) { }); y += mainRowHeight / 2; for (const phase of entry.phases) { + // A materialization phase says so on its own line rather than in a + // second, wider line under it; the figure notes say what it means. y += phaseRowHeight / 2; rows.push({ kind: 'phase', tool: entry.tool, - label: phase.phase, - sublabel: phase.includedInAnalyzerTotal ? '' : 'materialization; excluded', + label: phase.includedInAnalyzerTotal ? phase.phase : `${phase.phase} · excluded`, + sublabel: '', d: phase, warm: null, estimate: null, @@ -270,21 +272,19 @@ const panels = panelsToRender.map((view) => ({ // that silently rots the next time a kernel is added — so they are emitted // from the same array the panels are, and cannot drift from it. // -// The selected pill takes the accent rather than the evolution toggle's -// `--sl-color-gray-6`: with two options a subtle fill is enough to find, and -// in the dark theme gray-6 happens to equal the ribbon's own background, so -// the only cue left is the label's colour. With fourteen options that is not -// enough, and the accent reads against the ribbon in both themes. +// Each option is a chip, and the selected one takes the accent border and +// fill: with fourteen options, colour of the label alone is not enough to find +// the current one. const toggleCss = panels .map( (panel) => ` #${panel.radioId}:checked ~ .lat-ribbon label[for='${panel.radioId}'] { - background: var(--sl-color-accent-low); - color: var(--sl-color-white); - box-shadow: inset 0 0 0 1px var(--sl-color-accent); + border-color: var(--ds-accent); + background: var(--ds-accent-low); + color: var(--ds-text); } #${panel.radioId}:focus-visible ~ .lat-ribbon label[for='${panel.radioId}'] { - outline: 2px solid var(--sl-color-accent-high); + outline: 2px solid var(--ds-accent); outline-offset: 2px; } #${panel.radioId}:checked ~ .lat-panels > #${panel.panelId} { @@ -338,7 +338,7 @@ const belowThreshold = overheadPublished.filter( {kernelViews && (
{panels.map((panel) => ( - + ))}
)} @@ -413,7 +413,7 @@ const belowThreshold = overheadPublished.filter( y={panel.height - padBottom + 39} text-anchor="middle" > - wall-clock per invocation — logarithmic scale + Wall-clock per invocation, logarithmic scale {panel.rows.map((row) => { @@ -431,7 +431,7 @@ const belowThreshold = overheadPublished.filter( {row.label} @@ -473,7 +473,7 @@ const belowThreshold = overheadPublished.filter( y={row.y - boxHeight / 2} width={Math.max(1.5, x(row.d.q3) - x(row.d.q1))} height={boxHeight} - rx="2" + /> - measured warm marginal per case — the range its - retained repeats span + measured warm marginal per case — the range + its retained repeats span )} {drawsEstimateMarks && (
  • - estimated per-invocation overhead (trivial + estimated per-invocation overhead (trivial fixture, upper bound), drawn across the range its repeats spanned, where that range starts at or above{' '} - {Math.round(SIGNIFICANCE_SHARE * 100)}% of the row's median + {Math.round(SIGNIFICANCE_SHARE * 100)}% of the row's median
  • )}
    {/* The sentence directly below each chart is its caption. The longer reading - guidance is ordinary companion prose: title it explicitly so it does not - present as one enormous caption, while preserving its evidence and the - wrapper that gives adjacent paragraphs one coherent column flow. */} -
    -

    How to read this figure

    + guidance is companion prose under its own title, so it does not present as + one enormous caption. On the latency page it is open, since reading the + figure closely is what that page is for; on the landing page, where the + figure is one of several, it starts closed and is one click away. */} +
    +How to read this figure
    {warmMeasurements.length > 0 && (

    @@ -814,7 +815,7 @@ const belowThreshold = overheadPublished.filter(

    )}
    -
    + @@ -837,26 +838,20 @@ const belowThreshold = overheadPublished.filter( display: flex; align-items: center; flex-wrap: wrap; - gap: 0.25rem; - margin: 0 0 0.75rem; - padding: 0.2rem; - border: 1px solid var(--sl-color-hairline); - border-radius: 0.6rem; - background: var(--sl-color-bg-sidebar); + gap: 0.4rem; + margin: 0 0 1rem; } .lat-ribbon label { /* The content area's global sibling rule adds margin-top to successive - elements; inside the pill row that staggers the labels vertically. */ + elements; inside the chip row that staggers the labels vertically. */ margin: 0 !important; + min-height: 2rem; + padding-inline: 0.65rem; + font-size: var(--sl-text-xs); cursor: pointer; - border-radius: 0.45rem; - padding: 0.28rem 0.6rem; - font-size: var(--sl-text-sm); - color: var(--sl-color-gray-2); - line-height: 1.3; } - .lat-ribbon label:hover { - color: var(--sl-color-white); + .lat-ribbon label:first-child { + margin-right: 0.6rem !important; } .lat-panel { display: none; @@ -870,18 +865,14 @@ const belowThreshold = overheadPublished.filter( } .figure-notes { margin-top: 1.5rem; - padding-top: 1rem; - border-top: 1px solid var(--sl-color-hairline); } - .figure-notes > h3 { - margin: 0 0 0.75rem; - font-size: var(--sl-text-lg); + .figure-notes > .prose-columns { + padding: 0.5rem 0 0.75rem; } .chart-frame { - border: 1px solid var(--sl-color-hairline); - border-radius: 0.75rem; - padding: 0.75rem 0.5rem 0.25rem; - background: var(--sl-color-bg-sidebar); + border: 1px solid var(--ds-line); + padding: 1rem 0.75rem 0.5rem; + background: var(--ds-well); overflow-x: auto; } .ranking-chart { @@ -891,35 +882,41 @@ const belowThreshold = overheadPublished.filter( height: auto; } .ranking-chart .grid { - stroke: var(--sl-color-hairline); + stroke: var(--ds-line-soft); stroke-width: 1; } .ranking-chart .axis { - stroke: var(--sl-color-gray-4); - stroke-width: 1.5; + stroke: var(--ds-line-strong); + stroke-width: 1; } .ranking-chart .row-sublabel, .ranking-chart .phase-label, .ranking-chart .phase-value { - font-family: var(--__sl-font-mono); + font-family: var(--ds-font-mono); font-size: 10px; - fill: var(--sl-color-gray-3); + fill: var(--ds-text-3); } - .ranking-chart .tick, - .ranking-chart .axis-title { - font-family: var(--__sl-font-mono); + .ranking-chart .tick { + font-family: var(--ds-font-mono); font-size: 11px; - fill: var(--sl-color-gray-3); + fill: var(--ds-text-3); } - .ranking-chart .row-label { + .ranking-chart .axis-title { + font-family: var(--ds-font); font-size: 13px; - font-weight: 600; - fill: var(--sl-color-white); + font-weight: 500; + fill: var(--ds-text-2); + } + .ranking-chart .row-label { + font-family: var(--ds-font); + font-size: 13.5px; + font-weight: 500; + fill: var(--ds-text); } .ranking-chart .row-value { - font-family: var(--__sl-font-mono); + font-family: var(--ds-font-mono); font-size: 12px; - fill: var(--sl-color-gray-2); + fill: var(--ds-text-2); } .ranking-chart .row .whisker { stroke: var(--vendor-rule, var(--sl-color-accent)); @@ -988,28 +985,29 @@ const belowThreshold = overheadPublished.filter( gap: 0.4rem 1.25rem; margin: 0.75rem 0 0; padding: 0; - font-size: var(--sl-text-sm); - color: var(--sl-color-gray-2); + font-size: var(--sl-text-xs); + color: var(--ds-text-3); } + /* The label is one flex item, so a bold lead word wraps with its sentence + instead of being squeezed into a column of its own. */ .chart-legend li { margin: 0; display: flex; - align-items: center; + align-items: baseline; } .chart-legend .swatch { + flex: none; display: inline-block; width: 1.1rem; height: 0.7rem; margin-right: 0.45rem; - background: var(--sl-color-gray-3); + background: var(--ds-text-3); } .chart-legend .key-median .swatch { width: 0.2rem; - border-radius: 0.1rem; } .chart-legend .key-box .swatch { opacity: 0.42; - border-radius: 0.15rem; } .chart-legend .key-whisker .swatch { height: 0.15rem; @@ -1018,7 +1016,6 @@ const belowThreshold = overheadPublished.filter( .chart-legend .key-phase .swatch { height: 0.35rem; opacity: 0.28; - border-radius: 0.1rem; } .chart-legend .key-warm .swatch { width: 0.7rem; @@ -1029,13 +1026,13 @@ const belowThreshold = overheadPublished.filter( width: 1.1rem; height: 0.45rem; background: none; - border-top: 1.5px dashed var(--sl-color-gray-3); - border-left: 1.5px solid var(--sl-color-gray-3); - border-right: 1.5px solid var(--sl-color-gray-3); + border-top: 1.5px dashed var(--ds-text-3); + border-left: 1.5px solid var(--ds-text-3); + border-right: 1.5px solid var(--ds-text-3); } .caption { font-size: var(--sl-text-sm); - color: var(--sl-color-gray-3); + color: var(--ds-text-3); } /* The same fixed vendor→colour identity map every other section uses, keyed by tool rather than by row position. Slots v0–v3 keep the definitions they diff --git a/docs/src/components/LatencyTier.astro b/docs/src/components/LatencyTier.astro index c822a908c..cefe81351 100644 --- a/docs/src/components/LatencyTier.astro +++ b/docs/src/components/LatencyTier.astro @@ -173,7 +173,7 @@ interface Props { every number on this page.

    -
    +
    {dimension.counts.inconclusive} {dimension.counts.unsupported} {dimension.counts.runner_errors}{fraction(dimension.true_positive_rate)}{fraction(dimension.false_positive_rate)}{percentCell(dimension.template_macro.true_positive_rate_percent)}{percentCell(dimension.template_macro.false_positive_rate_percent)}{fraction(dimension.true_positive_rate)}{fraction(dimension.false_positive_rate)}{percentCell(dimension.template_macro.true_positive_rate_percent)}{percentCell(dimension.template_macro.false_positive_rate_percent)}
    @@ -235,7 +235,7 @@ interface Props { side by side as though they were.

    -
    +
    @@ -329,7 +329,7 @@ interface Props { Show the data table — analyzer-invocation wall-clock per adapter, with minima and maxima -
    +
    @@ -404,7 +404,7 @@ interface Props { adapter.phases.length } observable phases against its analyzer total`} -
    +
    @@ -507,7 +507,7 @@ interface Props {

    ) : ( <> -
    +
    @@ -611,7 +611,7 @@ interface Props { auditable if it is still legible beside the one that replaced it.

    -
    +
    @@ -707,7 +707,7 @@ interface Props { measurement.language } kernel, all ${measurement.runs.length} retained repeats`} -
    +
    @@ -798,7 +798,7 @@ interface Props { runtime or from another adapter's slope.

    -
    +
    @@ -816,7 +816,7 @@ interface Props { aria-hidden="true"> {vendorName(measurement.tool)} - + -
    Yes — measuredYes — measured A released multi-case batch that does the same per-case work, timed above. @@ -832,7 +832,7 @@ interface Props { aria-hidden="true"> {vendorName(decline.tool)} + {decline.verdict === 'deferred' ? ( Yes in the CLI — not measured here ) : ( @@ -913,7 +913,7 @@ interface Props { one, and never used to order a row.

    -
    +
    @@ -1076,7 +1076,7 @@ interface Props { selects.

    -
    +
    @@ -1185,7 +1185,7 @@ interface Props { adapter.slices.length === 1 ? 'slice' : 'slices' } for ${vendorName(adapter.tool)}`} -
    +
    @@ -1207,7 +1207,7 @@ interface Props { {adapter.slices.map((slice) => ( - +
    {slice.report.replace(/^reports\//, '').replace(/\.json$/, '')}{slice.language}{slice.language} {slice.modelProfile} {slice.timed} / {slice.cases} @@ -1243,7 +1243,7 @@ interface Props { accounted for:

    -
    +
    @@ -1306,21 +1306,35 @@ interface Props { )} diff --git a/docs/src/components/SnapshotArchive.astro b/docs/src/components/SnapshotArchive.astro index 55a4fcae3..873a735db 100644 --- a/docs/src/components/SnapshotArchive.astro +++ b/docs/src/components/SnapshotArchive.astro @@ -113,11 +113,12 @@ const entries = archived.map((snapshot) => ({ gap: 1rem; } .archive-entry { - border: 1px solid var(--sl-color-hairline); - border-inline-start: 4px solid var(--sl-color-gray-4); - border-radius: 0.5rem; - padding: 0.75rem 1rem 1rem; - background: var(--sl-color-bg-sidebar); + border: 1px solid var(--ds-line); + border-inline-start: 2px solid var(--ds-line-strong); + border-radius: 0 var(--ds-cut-control) var(--ds-cut-control) 0; + corner-shape: bevel; + padding: 0.9rem 1.1rem 1.1rem; + background: var(--ds-surface); } .archive-entry h3 { margin: 0; @@ -128,12 +129,14 @@ const entries = archived.map((snapshot) => ({ gap: 0.5rem; } .archived-pill { - padding: 0 0.5rem; - border-radius: 999px; + padding: 0.05rem 0.45rem; + border: 1px solid var(--ds-line-strong); + border-radius: var(--ds-cut-badge); + corner-shape: bevel; font-size: var(--sl-text-xs); - font-weight: 400; - background: var(--sl-color-gray-6); - color: var(--sl-color-gray-2); + font-weight: 500; + background: var(--ds-control); + color: var(--ds-text-2); } .summary { margin: 0.5rem 0 0; @@ -141,7 +144,7 @@ const entries = archived.map((snapshot) => ({ .scale { margin: 0.5rem 0 0; font-size: var(--sl-text-sm); - color: var(--sl-color-gray-2); + color: var(--ds-text-2); } .identity { margin: 0.75rem 0 0; @@ -151,8 +154,8 @@ const entries = archived.map((snapshot) => ({ font-size: var(--sl-text-sm); } .identity dt { - font-weight: 600; - color: var(--sl-color-gray-2); + font-weight: 500; + color: var(--ds-text-3); } .identity dd { margin: 0 0 0.4rem; diff --git a/docs/src/components/SnapshotBanner.astro b/docs/src/components/SnapshotBanner.astro index 4bf27ad47..7f5413eaf 100644 --- a/docs/src/components/SnapshotBanner.astro +++ b/docs/src/components/SnapshotBanner.astro @@ -34,11 +34,12 @@ const { benchmark, manifest, claim } = snapshot.results; diff --git a/docs/src/components/SnapshotEvolution.astro b/docs/src/components/SnapshotEvolution.astro index 087c6199c..ee69ff41c 100644 --- a/docs/src/components/SnapshotEvolution.astro +++ b/docs/src/components/SnapshotEvolution.astro @@ -377,10 +377,10 @@ const last = columns.at(-1)!; />
    {heading && ( - + )} - - + +
    {heading && ( @@ -500,7 +500,7 @@ const last = columns.at(-1)!; ))} - assertions + Assertions @@ -635,7 +635,7 @@ const last = columns.at(-1)!; ))} - % of its covered population + Share of its covered population @@ -669,8 +669,8 @@ const last = columns.at(-1)!; ))}
  • - marker size and the k/n label under it: kernels covered of the - snapshot's kernels + marker size and the k/n label under it: kernels covered + of the snapshot's kernels
  • @@ -690,7 +690,7 @@ const last = columns.at(-1)!; )}

    -
    +
    @@ -763,35 +763,11 @@ const last = columns.at(-1)!; diff --git a/docs/src/data/latency.ts b/docs/src/data/latency.ts index c2163d024..c9b753e45 100644 --- a/docs/src/data/latency.ts +++ b/docs/src/data/latency.ts @@ -735,8 +735,10 @@ export function kernelCorpusDistributions( } export function formatMs(value: number): string { + // A no-break space: a figure never parts from its unit, in a table cell or + // a sentence. const ms = Math.round(value); - if (ms < 1000) return `${ms} ms`; - if (ms < 10_000) return `${(Math.round(ms / 10) / 100).toFixed(2)} s`; - return `${(Math.round(ms / 100) / 10).toFixed(1)} s`; + if (ms < 1000) return `${ms}\u00a0ms`; + if (ms < 10_000) return `${(Math.round(ms / 10) / 100).toFixed(2)}\u00a0s`; + return `${(Math.round(ms / 100) / 10).toFixed(1)}\u00a0s`; } diff --git a/docs/src/data/scatter-layout.test.ts b/docs/src/data/scatter-layout.test.ts new file mode 100644 index 000000000..b559d66c9 --- /dev/null +++ b/docs/src/data/scatter-layout.test.ts @@ -0,0 +1,61 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { percentAxis, placeLabels } from './scatter-layout.ts'; + +test('the percent axis keeps 50% in view and trims empty space below', () => { + assert.deepEqual(percentAxis([78, 86, 100]), { min: 50, max: 100, ticks: [50, 60, 70, 80, 90, 100] }); + assert.equal(percentAxis([45, 90]).min, 40); + // A point just above a tick would sit on the axis; it gets the tick below. + assert.equal(percentAxis([41.2, 90]).min, 30); + // A value on a tick still gets room beneath it. + assert.equal(percentAxis([30]).min, 20); + assert.equal(percentAxis([]).min, 50); + assert.equal(percentAxis([-5]).min, 0); +}); + +const bounds = { left: 0, top: 0, right: 800, bottom: 400 }; + +function boxes(points: { x: number; y: number; text: string }[]) { + return placeLabels(points, { bounds, fontSize: 12, charWidth: 0.6 }).map((label, index) => { + const width = points[index].text.length * 7.2; + const left = label.anchor === 'start' ? label.x : label.anchor === 'end' ? label.x - width : label.x - width / 2; + return { left, right: left + width, top: label.y - 8.6, bottom: label.y + 2.6 }; + }); +} + +test('labels of a tight cluster do not overlap one another', () => { + const cluster = [ + { x: 200, y: 120, text: 'OpenTaint' }, + { x: 206, y: 124, text: 'FlowDroid' }, + { x: 212, y: 130, text: 'Semgrep CE' }, + { x: 222, y: 128, text: 'Infer' }, + ]; + const placed = boxes(cluster); + for (let a = 0; a < placed.length; a += 1) { + for (let b = a + 1; b < placed.length; b += 1) { + const overlaps = + placed[a].left < placed[b].right && + placed[b].left < placed[a].right && + placed[a].top < placed[b].bottom && + placed[b].top < placed[a].bottom; + assert.equal(overlaps, false, `${cluster[a].text} overlaps ${cluster[b].text}`); + } + } +}); + +test('labels stay inside the plot', () => { + const corners = [ + { x: 795, y: 5, text: 'Bifrost' }, + { x: 5, y: 395, text: 'Pysa' }, + ]; + for (const box of boxes(corners)) { + assert.ok(box.left >= bounds.left && box.right <= bounds.right, 'horizontal'); + assert.ok(box.top >= bounds.top && box.bottom <= bounds.bottom, 'vertical'); + } +}); + +test('an isolated point keeps its label beside it', () => { + const [label] = placeLabels([{ x: 400, y: 200, text: 'Joern' }], { bounds }); + assert.equal(label.leader, false); + assert.equal(label.anchor, 'start'); +}); diff --git a/docs/src/data/scatter-layout.ts b/docs/src/data/scatter-layout.ts new file mode 100644 index 000000000..c27575f24 --- /dev/null +++ b/docs/src/data/scatter-layout.ts @@ -0,0 +1,155 @@ +// Layout helpers shared by the scatter charts: a value axis fitted to the data +// it has to show, and point labels placed so that they do not collide. +// +// Everything here works in SVG user units and is deterministic, so a chart +// renders identically on every build and its geometry can be tested without +// a browser. + +export interface PercentAxis { + min: number; + max: number; + ticks: number[]; +} + +/** A 0–100 axis trimmed at the bottom to the lowest value it must show. + * + * The floor never rises above `ceiling` (so a reference line such as the + * 50% blind baseline stays inside the plot) and drops to the next multiple of + * `step` below the lowest value, so nothing sits on the bottom edge. */ +export function percentAxis(values: number[], ceiling = 50, step = 10): PercentAxis { + const min = + values.length === 0 + ? ceiling + : Math.max(0, Math.min(ceiling, Math.floor((Math.min(...values) - step / 4) / step) * step)); + const ticks: number[] = []; + for (let tick = min; tick <= 100; tick += step) ticks.push(tick); + return { min, max: 100, ticks }; +} + +export interface LabelInput { + x: number; + y: number; + text: string; +} + +export interface LabelPlacement { + x: number; + y: number; + anchor: 'start' | 'middle' | 'end'; + /** True when the label had to move far enough from its point to need a leader line. */ + leader: boolean; +} + +export interface LabelOptions { + /** The rectangle labels must stay inside. */ + bounds: { left: number; top: number; right: number; bottom: number }; + /** Point radius, including its stroke. */ + radius?: number; + /** Label font size in user units. */ + fontSize?: number; + /** Average advance per character as a fraction of the font size. */ + charWidth?: number; +} + +interface Box { + left: number; + top: number; + right: number; + bottom: number; +} + +const overlapArea = (a: Box, b: Box) => + Math.max(0, Math.min(a.right, b.right) - Math.max(a.left, b.left)) * + Math.max(0, Math.min(a.bottom, b.bottom) - Math.max(a.top, b.top)); + +/** Place one label per point, greedily, avoiding other labels, other points + * and the plot edge. + * + * Points are placed in order of how crowded their neighbourhood is, most + * crowded first, so the points with the fewest free positions choose before + * the open ones take them. Each label tries the eight compass positions at + * increasing distance; the first ring is preferred, and a label that has to go + * further out is flagged for a leader line back to its point. */ +export function placeLabels(points: LabelInput[], options: LabelOptions): LabelPlacement[] { + const radius = options.radius ?? 8; + const fontSize = options.fontSize ?? 12; + const charWidth = options.charWidth ?? 0.6; + const { bounds } = options; + const ascent = fontSize * 0.72; + const descent = fontSize * 0.22; + const pad = 2; + + const pointBoxes: Box[] = points.map((point) => ({ + left: point.x - radius, + top: point.y - radius, + right: point.x + radius, + bottom: point.y + radius, + })); + + const crowding = points.map((point) => + points.reduce((count, other) => count + (Math.hypot(other.x - point.x, other.y - point.y) < 90 ? 1 : 0), 0), + ); + const order = points.map((_, index) => index).sort((a, b) => crowding[b] - crowding[a] || a - b); + + const directions: { dx: number; dy: number; anchor: LabelPlacement['anchor'] }[] = [ + { dx: 1, dy: 0, anchor: 'start' }, + { dx: -1, dy: 0, anchor: 'end' }, + { dx: 0, dy: -1, anchor: 'middle' }, + { dx: 0, dy: 1, anchor: 'middle' }, + { dx: 1, dy: -1, anchor: 'start' }, + { dx: -1, dy: -1, anchor: 'end' }, + { dx: 1, dy: 1, anchor: 'start' }, + { dx: -1, dy: 1, anchor: 'end' }, + ]; + const rings = [radius + 4, radius + 18, radius + 34, radius + 54]; + + const placed: Box[] = []; + const result: LabelPlacement[] = new Array(points.length); + + for (const index of order) { + const point = points[index]; + const width = point.text.length * fontSize * charWidth; + let best: { score: number; placement: LabelPlacement; box: Box } | undefined; + + rings.forEach((distance, ring) => { + directions.forEach((direction, preference) => { + // Diagonals sit on the circle, not on the square around it. + const scale = direction.dx !== 0 && direction.dy !== 0 ? Math.SQRT1_2 : 1; + const anchorX = point.x + direction.dx * distance * scale; + // Vertical offsets are measured to the text's near edge, not its baseline. + const edgeY = point.y + direction.dy * distance * scale; + const baseline = + direction.dy < 0 ? edgeY - descent : direction.dy > 0 ? edgeY + ascent : point.y + (ascent - descent) / 2; + const left = + direction.anchor === 'start' ? anchorX : direction.anchor === 'end' ? anchorX - width : anchorX - width / 2; + const box: Box = { + left: left - pad, + top: baseline - ascent - pad, + right: left + width + pad, + bottom: baseline + descent + pad, + }; + + let score = ring * 1000 + preference; + for (const other of placed) score += overlapArea(box, other) * 100; + pointBoxes.forEach((other, otherIndex) => { + if (otherIndex !== index) score += overlapArea(box, other) * 60; + }); + const outside = + Math.max(0, bounds.left - box.left) + + Math.max(0, box.right - bounds.right) + + Math.max(0, bounds.top - box.top) + + Math.max(0, box.bottom - bounds.bottom); + score += outside * 5000; + + if (!best || score < best.score) { + best = { score, box, placement: { x: anchorX, y: baseline, anchor: direction.anchor, leader: ring > 1 } }; + } + }); + }); + + placed.push(best!.box); + result[index] = best!.placement; + } + + return result; +} diff --git a/docs/src/fonts/ApfelGrotezk-Fett.woff2 b/docs/src/fonts/ApfelGrotezk-Fett.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..73d6417fb2a87e9dc3e991ecd6bd1e20a1047cc2 GIT binary patch literal 20128 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

    WZ7)}jU^o)@fzAq9$lo-AY&&-#`vdm0vWFFH ziOb5jJ&2z_jiZq?VH-JFMaeBH)*NSVNqG><5=)DZV%TLk{Rwk=zE)742Ag;FV7F5z z^ZhLJ_v~rmnlWLmw;_l3?*|a52l6$9Y4R?T?ww?>gCMsHxli8r25*au;7Eeyn>DkL z(ZkXYuGnIN2pi0!23)oL4E12eSJRIBFUFs%%| z`4fq*KpR=cPd!7Uz?603QDoY)a7GA3^C@%R46H0<>V5x+u6HV+Jy@X-Qc;3hEYb=V zU+jxJ@$A%pAD^i2g2D>(Zbd}33>fV9y&d9tEc0N}jr+EH@nK0*qVjTTxX=|@3+KCj zMS^u&=?Ny1I?Dnw`HrFbOJEPnX((#o_09yb0qQ0R#7i{;e4r+uCh)1O=?SmOcRk_m zL++%8yMx?&ba_QyJ^WvBjg`%#2TS=cGQ&cJTKy?D1S1wh3m+@IW1-E$aC@6eYyh!~ z$eqSpX+j)%JR^I%Av+cRZkY-bc|O-Nyhw}X@_Kw$AA5-Z4IY1XX(mT*mTi(zS7u*bWuLM$J7by+jaO!^D$HSL zcHk;aqV(G4DK66^h-bh2pgU^Gxp@kpbaatzY78EbVHzC#r71OcWR72^kaK%w$j5?3 zE1i^jJK=x2a4C$~4UV3r-lQ8Ycn%5{j8DO+BjH{<-WF~ngW&kK!ajFORmu77gfdwnzAzec-A5#RT6o%?C?5JpP74Q_{u{DrxQ8735DJ5*xqWJ0=8gd{RD1=}f zOu&H`ybWSbmTy*!OuD@sRmf0>w6v26|&yDIxohNi=}!DLpx6@C5T&eiC+v$~j9 zG5^ogKeTJ!6~2WMspJ4Rnon8eOzxDen^;VFa3>ecwv*ThJFrumwBX{c$MFftDe-e= z#N}iqbL!)WCdhvdsq;d`w~Uf4pb3^h+!3h1_}9+G)>RIPj*;)4cfkmywBWPv7B)#) z_(NF{+nbM_eYPtlF)r=&#cdIXxQi}!X!X1ub2VhqJ+#fpOKEqO=_EXd70-+fu_0+g zC^YsxP0Ie0FW*VKd-EAVn-3p3DI=6MMPV|?$3dqFV=B@N$=&(Vk&X8fIlQyy)9_Cx8T~$ z6P(CQK7a1oL!{78i$Gfw63-^vMRrEQyu>Y>ae~!s?UGGqHj)Mp>p1dKPDd#xo=sU* zkQGM~&>v4K>*UEr(^>H*WYWEFcI#xinu$u19;gq1txY^5Gg+b~U5S10i#>mJ_V?p# zW-Ta4SlWliVlWZ#)h-4LUhN{{QNT_W z5tqF5U(&Sq#ToC8>TS-msk^dsXKBXI?s)X%#?92E_$BGvIM)SMvA%a&CmVjbEckma zCsBnDlj(8KjzuLU6>nc!w9!GohV0$vo!;CIQ?%Ql&XwLKYWAKFh0!ejP`;sVN^5GP zlbTQBFuCVy4r*gV_Tyb4CN@5}d+H(3rP+Q^qW7&`v3%WHtyP7jyjo!TSOTjtqUaJc zj$=@>!91QvbJ-nf4z?#MD?wjt^|Dp20y^CH5wG6f9XHB&6q}-9 ztT|c;yF&ui-=urZh=gy(BdK_5o2Am53sgX9a1!Kv1hfF)i?n}ClvW-ai)VKN7WHRi z8o)aipP^s=?F;V&%)J9Y8jyCqt9xryxR?DmA#9+gky?LM8M9`4cO2cDtm-WvGxzXL zX+Exb*-s>B9FE*Omy`*ewiKet{4PVa1el<0+FPh?)tSKw8DcM@KYY7_} zO+i+saxQh}JYji8HJ9(*H9~gQM=xhX#T>;NKMlNtIdcTIkw`i4&i}udKl=+CY``HV zZBD0Ih*Qp09nIg81@dO=6)*bSvpev`_bV~6s%JNa*jv6H(!uIeH@9|?o#rcI>gaGA8gloC+GRSk~Ud;LA$JU?Wqr_D(VURNZV|+==Rcl5-urG zbuOn7_9J=EZ^&fWx$L8PT(1BwYC-dT5!C28Dd9_5H7XKLA*Hcp(#GkD4KNW&mXcR2Jj%#IL0f4v(}GgB4I>3tF%KdW#x_YXwn}S) z5ClR*QB3rqi4;g8MAZvQWsJELhqRM|$X%1UA53XA)k;@0A|chFgf%S5x~Pz>@Z}O( zNJ_FIC9L{}g6f305~k+CHT8W8%L{w*QXHj4-h&c!qOM-v_@_GI;f$pUpOkW(VI0S4 zM++J7`G^#1B0p8jhB?E?&tw`;EHxsd;mZ?W42g>-G@~&N%;2*_vN_EyU-KD(Oa(*Q zOjS~61_6d9Ju#YrP}~-HuO$hNx2HRu5nyRp4!Ez!Af=O4xLvyz61RoC84-$FRWGiS z?&i8GZCmw5cYfT)4XJAw*os>X<$97}`v7uj0)mzwnN9J!aEVS?)vZA2+u_BJ(Oir+2>tkf#a-|ow_BdeVGP5 z==t(vhBYSD`Rf%;M@4Md_m`q#KC>_Nis%gto8jyk-Ehs$S{+IiwNW(j5XOO%AYLs1 zF>w0eyqE-eDT*2vgD{I!3@6c=6MB;f>qc@4V^ddie^?v|w+vd$iqX;}nf`k<2{|0W z6qH{`t>~bO)R7+wKLj)NmMm@%%Pd3ExL?C-yk#`d5PXIWUXE&o-W1aG2hEM*!}1$5 z_FdD^fi~(uYrhbfO#aL*^OFpxNb<$eiwq(y!BnJ4H|g!58cr|quFto4L%%cX#noG* zLu2-luBEwi6|z3vFBC44v(VA+8**NT$x4OL1IjTZvY|*bNTq^T_`OdFy{2~z{Tyy@ z#X-VJaPjUwE22s8Wm4K`wB(3}f4>6ipjMAz8eLPQic07$--|vCl5U@fXjMh<`sRtI z*k(_&B-U$4f)@5j!%Wh`fs(%NiU zj04H|TK=J99M>&CdhU2wWlv|4Z=#&(TJl6oFu{yoGX~4vwzB(s143s%kxGjku7`qHJcY0__z==TDj($T9wBZ*A;3=Y zt23Acd0;p(x2_;u;gitSgYdvUrEm=eaUcZ5gdYe${^J?AG~ilJ@}o)<>}W8m5c55>oTNPsgEUVlcRBt*c|vL0!7RG8 zYR@r_nG;7xN*O~4X65?bLhYZx{&{e>l;r&>V-3Ea_gHU^-q9dIuQ!{U4{iPTpn@N7 zR)jOFmj%$gK$OKLBISqE^AaFfs6<)XuVS~2Yjo0GZ~YB2)&x_`ve|C?TyVz|Z+!(L z63E8ZjcZEF+S{qF_q@+&Q%e6YLs3Q(Qm8;M!iFwO*g-jFE|AA0)gXgyYN(@~e#V(s zgr+G~naWYI7O6^lth6xxg!O^)!hq5k_8~PSp3K2*os5X>BK_Z>{TG; ztGYk2)JqAOMYh`0x|gD&;Zpe=`m^881({c{KGHfD%ZiqkFIu$J(9ELMQ*7vd#Almh z;IODc=DCTglp*E!H&Y69wA2bk3$Oe$ftF3CZp@2q3DFQ$Cr8qXTxuLa`SkXMYv;LI zS&(c2C=l20QetB8)a2GXTUf#j%R%b#Pzvi@W>s?c=QS^2fu*jERf(B3mrFQg$-hcFp=BXm3CiF_$7m6Nr(r zE}cR<=sb+l^%;fnn$V)QEahpaQtY}E{!>Q*fVNoSDa8xZTtEybcqEcF3p>U*;eu}7u=J3fYPPH;R z91Sf#sdzbr^snL7JCBf?$=|aGq@#h6eX0np2CDg*njl3BzTmZe@XQGpw3lYymsyxM z7cuj8=7B_sVF8YszuXa(5Y`~>0|^YmOtKZuX}8F^eKctqErp3RPlF`Pt|YS&wsy>r z%C$7p3*lb7vD|m8BmH>|p|@AElt#myEhOxOV@X9l-dfI^g+*|#Ij1qp7?vr+ty(%f ztDdP1KwebHkbBVM4}%xbu*oFzSLZb@$KXMkpL`LRxv~bM?sy%Mfp9_;j51nvUbJ-P z=!hP25|9I63-&Fd(f)3r4X>=-85d%wq|XZ7#6FR>)b6i8rZMcRUcyZ!PJ75mt9%YK zSv&OwR4KFZ=P(`U1ijLeidGhay|B>~JJLb@G4-f6&{8x}L`8k-Y>5b-s#O#VsJ3b4 zMSJRp-8a+Atinl$ah)=MLE+prENZ^9wS-^~hpAv-Kn4=Y7Ai{B$rz7Qn`z{4+VLhr zqp=yyoc_v16o%4%Q$b0dq#}2Q9*)z{k@su;^q`7G3f>lK-$^f>HxMloS8?vs0JOf> ztle}KMvL?e4}Pm@Wc*fR-a6q4_;c~3<4cLLzhJzj+B2xEiL($R7|4*tkflsDcXBI!WI^@S@}C&ZCQkJm(a&4n6?gad#T>#(yrmMG|m z&YHST_#lWUHOJ!V&B>8xrq|)l$OitenY3aS1|Y7!R3QYjtsq1PSY0#pwsQi8}`a{lUZk?8Um{?A-E~#e?^78_MExkTplR|FlCY4?Xt8 zGta&7(kox#U-$^0kbz8O{Y7UllBCI!r%0O)LFl0t86Jz2XCitol3s{}SCkQg$Y$#! z$owUV#XuG?gBe`l1Y}sVslWzd!*oXenOE*aYH$r&0xrx~;-}9m7b%HAs>Hdyj@WXQ z5-NkpQ7$^EaO0{}ji*LEzVnkPapAmDms}xnpNiAxq(`94EI7*(h+-9TQ^}Y&lTm4XEx+|Sd_A>QZsge4HAaI3JaLNK!3YPfiS1z-!3V-6L-KO%?wx+ZU z6!R~K)U_oIvZ9#De6=(tjh~b)YvemxN^>^ARNmP#W(XrRu`Aqacah^(xmR9%#!9W^?6=U0cXf7N4{q{9 z0GH3C4lA6Odtb~GS|1-0@+X4&KY{>h0D!^9>1VPQqf9l&So18@VzF(e+u^9y&bwi! zTkbe~{23q1waQLp={eacnN!nff`Bz43A?>)f{YT2iUHaA1r`&D>i+_R8?2OjQpSTX zX-D{ofQ>(q03n#{B1lC_#F7L{O15k%;&+YrSkh$T$kG;9I|%zocnG3GM2NE`8uXyj zQ!na4a*!K9R%&okW{9!W$HJt-WX)8E!+TOwO{X-&Oj5JxZ%VVxAvMo@&K6imYVl;H zmOLV})G8{gZKJlsZW?X7wWSW951tfHof&mj2 zY&dX@H1wp9o}uFb_y%g}2K?d|VUcINN%+D)mewp@K@9Cx(x#bCW=17#rdecWr)hJ{ zWiSuWi#)4s!Ixg3Z5QGLd5Pv_3mtY8&oSpX6$cSw)QF@R4~QouKTcrPu}bwoe@H@$ z3MB#w3WziO202cs-FRJssEr7+D5V=Cn8B(n1^^Mbc<=N4I61p1H^a?wv)xQsDgK2Q z3QbZj`&TUDB+2@JC`r=(U%uRQl79`BHgUZwkW}Ans+u$cKrjU;B}M`xO$ES=nX8Dv~!h*6QR z+>d(ftJWbI%W58|C9jz8CW6S{{T9N4m4QP%5)BN1J90(x+lXr#aYdmXWyl|5oEEEX z%3V1@h;rAx^K?WNZ2i=5@C#6YxP5W}q?-4N=dtNnCScW!J#lS`4lwNdJG|t7XCPq@ zn|copv;YA7l^6qdxjp~_h=i<)CCyPQ1As1~FxdF8Yp3Ivm+1l4G%~5C%_X0KZ26aK(o1VuEa&F>gCCm!b`Ft}rAqs9d-$51kU~*WaWPYaUYi7Oa5}zId-TpR&jL#= zv)WeM?Xbro$6QT-?g=r%7a(ZKH}CxN$XI$NcT*ObOC548%tv^SSw&~?4|9BgUsHKI z)aHM|Oy)E3vGDo`H=3H3j+G}b7P<2x#EKFvQA`066v~sYK$R2+b#ecYX6p*H$tlO3aMESlT*1*5LldrUMEa2GPpmJ6L6in_Hk#f@ z24iRp)5-*9len75T{9oE_?pez6#nK5v{10cf-DkZiBQXhTPe&6@ivOHL6R-f?UZS^ z47+68tBw6y+b72n9UNBdj8f+mIjzK5)oNW+?}j?pwRJ#8M{E0E^7=Z~zU%=j6^~jU zm?Y1<{4c7w5=tty_!3JlGzV-L_lWxVWj}M%Djo%fdI6(PK?0e=XB3FSSd9Md!E*X#BJq>F@OCO%gO7}<)CSz#O# z=Ss@3jCj@w^sj!f(D6Y$cfI-FSvGP3Fi$b&M8oNs;<&!leF8SLc-bbIV`QcxV5-Fj z&8!1yhbLDLbg!}Ygp)+?+XrLz;T4TdQs-b z<(q_HNoF4lo--TT2;hver4l_!>^XysODbe1=`PF?n^bU{C>ecf_R2f z^bN#4pPuO!`ZQx99BZ@zcNyiOXqqZ1N6WRF6w`zJ4Z!O=aARN?9m#kykDW*7@yDO{ zn3?7ml+9Su84V9RDNCoa13MPTlW8B0oLm8Li@`laA945Wd^3Q-b*2+KOX5*nFJ1cp zHPul~6P#@3MtzcV0ntQ?5vP|}!dDrmeF1Q9-tFdsvD=BlI8IBr&AAC;Z}N7nW904H zkF5x6F!5FH9+ik|lF~Kua z{f5@(&C_hTQ>I($i3=bLo&`W?9TKiM0N@E`j@)MjClQ2NpG0yf9!Ra9#1LWDNla!p zPGU>)?<5XQ377I%3vm)(NE1#H3hEdUxwidjMT|cRZ6r%$kg1ajjSLbr8Pe^!FHMay zOL12fqIFV9PW22Dbf!R9EMy5U;v0T0X7081By@t1_gym2Ot~zbtS43<&2l>yxAIFIZ0Y0 z5yHj+L1Fm5jY85*y36eUPe@K=Y;XnktESa}L>-e(Qfi0;Q{5d1B+fDzrP9dZQIxcc^hoIZvEZsTg9g>n4sTJ3*)FR* z^0I$V^oTY)Hu&;WlNU8Rk0>{1FPDc9;*o@KKoTwxAV3HqURg{HW( z)1kbjVAr`Jk=;o6BL7c+&u{H>p9B;K5rvgB0jEKnND^uu!snll{aE)r#J&KD#LyBY zh^@rFrEO2*O;9jku)e^3`x6Cp=fy%{5cu$6FensyyTBk&2z&?x0uLU99`;_{nzkU%nO+6phyP+Z?qCo=KHjnm! zt!V@dj1ucPr}G1X?Bf5Tut_?VAPYhjqD=uiVB_@;(gFj=>*u3Q`?Dc=%iWi}Ze;m0 zLq&w9DMTD%M#;vBDYQ;iQ_o49_!3dCrN+4r>E8eU$+r68FjeKb>(<%B-C_dgb~T2C zAz^9}Q$$CM2{A?5+&Y*MQ(jtp|+y7IwZ2t_ocZ78BS#YGYROp!wxoG4NyO@j4t}p;8aWLYo29&%6D0vsu zC6E^ca1+SAkYkl9r%aF=2(8X0NbNoGGUZ6gN$U_|mqXFHD2kM>)>OaHk?}G{9nS1+ z02KgA?goH_DhN~Ai7F5IC-UV<^#}`A#na_7SVlj~;>G4ub*!UdaZt6E)4VJ{VNZj? zFy`tpu)1z+YiojniX#`6D286I#n|vj=enp&etzfs|KBwsz4zUSs-upoiii<0Mnr5; z{X35t2p$2==1BK9@sTzJmv|H}j4@CYgzaDS6VcBB82&}WU$EgDeB_G9nEj{l_aDDL zCrS1%cgb~*`&o&#PXJW|goQ`Q8zqp2j=@A$Q)QcFt|ApGt+qjfo%T5Bs8i1S$}JBJ zcHe&*FK;8*?L`9D@8p8^Xjs@M|h zHfueMmUh%rEzcT7X=z8`9+u`n1ZO24aE5UOh>}<#a9pe6aS4PVlhp;Sl4L>FcB^a7 zqcy+bTA5;zrswJny2{>K!)TBl(%b>jT8*}R4Q*vjryF>v8$a$X_lNZ7wCq8sSO7xJ zDJVHuvGHuk_)fMnu}uhi5hWrox}oQOucp`XwJFxPW7wamG@}`wjn1N0G!qdER?XF` z+%t&dC}4$w5qCxyuO>vyJ7iwwg>X*{;}Cpy!_EhHFas2BCCXAx=ycqP;E-t2Fx?dE zb?FeAH7o3R2)mHw@Yseq6UGTe7WTU7qo!xf%hU^-Pf}K46;@b?soauT z8UD1m9N)i7|5z4ir%k%4H~*^n8>(h4NDreK*zl z-OR4kHOmrW+AtDGIU_9SZG1sh>Ab?cliFdGe(cA(~l=0xPUn z-yn74gfDfw?v}@1`Nbdk&`R)u9P<<_x6BIFYHZNrf|~{!noj;R$UKXURG={NlJrci z0w}g?&#_P6x!dE-#(!3EyxW>P+x2{P7MS-A2N&}ZD8hJ2g0R%mXq~pvR!#P4b<#PP zUG=f>imL2%_YbUy+n#XEKY8o#{&VVZnK^k_#0K=V(}$N{&F0|b=I(8#3#@Xj4=lIY zLF>G(-#$lUH@38Pbas{8w6dCAVdYh|rW=2SF;w!B7G-I|Rq0u^R}pvHMxBpuOUgc( ze9K$`i`S;cgY(XleLNO ziIwWvPU2${d?e!+^fWur6+?Gq;%G5oIXKq2X-{oo|F@XCEd+bv>*Wm~_!djzSstm8 zZF?gbiWoX2^SGIUB}rL{r!h5$aXu-}?X!Vv1T+s;{k0*T==Br^UvWrWr8*P)lBvot}QM zumW+KR}oGUC@ggnzyn5&8m!UkuY$~X1dU3K6AlsM^#gRqP4$|Y`^~QtH@`t#H@`*v zleSXLA`i~aMzUwaDS#CB214TGT6L0ZE)MJm4^|9oB*I4gh?&csJV!=L)hvx^W)O=- z$KM5<@(tnyA8wtaU~L`TXqNG2v{~wYB#UAkKgN1N{LY^r>~->LcX#cWwLGU>jkxy5 zvdwS(!97iG6u~HQ-OQUpPB?87OR@72UqAM2@I9m)`=Ny6GE3s)Gc(lc;*1)ch;D0- z3^9x!J)rCsDFvDOdOhMa<3e?*@Ew18^L51G<4|T?3Y&KD(86!xzu`?O^PxPa%Mml~ z4!*M6o}zU^t6rc*6Q{P=#l{`>$J%V?coWf?$?$?7rW+-LQ!*IyB9hag0QHgV)I*4j z_>B&Nvil)T;?o^QN(zd2ibyBN5zpO)JGK6N{QLp%PjYf|3yIWkcQ<7ARaLxr5+%kR zX#^ulP(*=-NE*hF8LU|Acu19y!=AU{xCc9?0tPlBody{d6wAbz&~D(s2`|BL7~-fP zDN`gcVngM~*DX}n-SmBcxT-PHcCkScJ(s5gFB<_uieL(*saX%MT*<}ThNSIl&Ro7ATC07a7oyl} z%I7~AF;=X0CmzxtEam^EOa`{#5h;K~nFK{5$i`2QG$Qkh>4azFY}%m-^mgO}yk$Pi z_-ay679;K?Z1|rM7G;Z{-fn4>ndl9e`mvL{Xzv6vx(#huG3lr;l zPr?_~F`<~N4W~P%oY{_MTz9vRHd)WFu8WM%Opg;rR`KMUyx7WTYL%~fwNS5b&PDwB z{Qb)_9nAR-=0B5aYKEZByX}`XVNC&EzS^rz!vTUugH}k0g%C}F>EDR^bmyh3Bk#HahEsH{ zf}&>{d|-xff39+=y48d{&D5Y9q~Dy$9cnFg?16N`C4hpCswhBp6ICM*8W}!41IeGL z5ratNR6;aOnp<09?a2_Wnz&y0JC7FDS^tynIhSbbpo<#1WbnrNiRlon6WwFI)3G>l z>N81{xeBgeM$CK)`0a-0PD{;84!KxA0PZgz-b`#8QG~!7I~zjQkr5RPIrVq)G7@ibN_qaOG*uf zj8e5aBRwvzB%Q+?{cakPd%&QEY6yUQc=0-rzaj7B5#~lh=$G&_MCyBlUEwOiIY2jV zp-Ky0j45Bn;f-f%LuK>uxn+&YT(^aZc4#ens{z$wonF{fEuABKBH_uC`vHE0WK}OC zgq*_5PWbaOA2RLfZ*TA1=;^sOK8%xHLw{-H3*7UDa8AUd0Y+FjF?v_1rM=7)P4kdD9dyqRd=d2)QiU8lv*X^h_6SVNqDB z1)!Q!QYx`&2Il3P$ieH4+#+fQ@yYS+!&`7qmt!}A2(YVyHne{r z3Yrph!gO>jG*>|fzB=gG0Yv+{t3fQW^T9P}A$B6@ffi?eGjs;@LZSwfq8;EC2?r9Q z2cacMjNXP;qIW`vfyuU=zye}a`TkIM@GZ0lk_88Ktm#1!2J$pgPnri74|`|M=%DsDb^=g3NXk83YNnnC`-BmZ&4f&b;kofCl9Wgb|t6` zRznrkz$Z}M&Whkuum-9aKrl=kK0;($nvU(Z=?PGd1gkvs!q5JyhgPDY2~W^jYWKi} z&01a3P0P~XLoMA&w(HE4o1KT@LA#6v-wHmgXrbO1{#-EbK96V>8rb_3QaZ z`7i7q`|ax?j*oJxsHcot+L+O0iQgC2+k<*~M8=gYb738P0Gr`9AIT5?t(pgBwul$) zGJZ)|vX|0j`Lb$hTz+1WTkz^wo7S^y&pNc8e}Vp9N4KEOxLLQ=+wtwm_R}udr|p~e zE0?*%KH@NOj5tfYcK2|Pb8i=L1kZv(L51K`!7jlm!F9m{!Qa`TLZ{G<$Ck){owTHI z$$by5j#DSlGwB36nf`*_O@Bj|(j6*^;Hbw^~2)ntzQxu{p{7#X5m`i&@UBW6oMVR-g5bZMtZys9*BL z|EB!!y!~s<_W1Dmd zj}M;{zBqhi_}=h54W9?wBN{f#)0)jEYu9SGYOiX`v>aK#MZd_|kq`Nms&rRtjjhOf zsy2M=XD9aSPRu7;cRF1scK$5zq?df)3*VjS%PqR0d*J^645Nci#oY#h{$L%&z^Kf? zXMuEfJ?)qUQtVteuwp-`imqLAF&V}w#ZQ}r6-sALW&nC*+*VMFCA>F-(8?qU!9)XJ zk{W>^Vh5#jMxBWOydDkMWrzMww{fmTnh0ZUO~9L>#Nsm(V4-6%@O7wbF5p*~6BGun z-MbyZoMYfxS*a9X#1@9Y0cQuTtBs^xTdnTa67k$+kqdlvcW>DhE^2fxF7$ z%kblkaPCood7JMK487q=_eF6#z2HibwZd>~gyEoe)eiQy!M*Y|?7w|!Y+T&%u@X&( z^7)Lx*_^JS(R+UUz=5O2_cryn{uFZjaXjdKi(q_Zwwo;Y4(iQs3b3}#A5)UV%V6m7 z?|EYYi|8XQn~r!ayK`B#{m-8cvG+I4vY6X|Lu5n%E{~Q+G(NsyH>48KC=DCEB8I98YZP2EW|ceSc@| z;Y?i$JQW{fz%JiU^Ic)W4Qpxw{yPTv`kBsv4|m&A5d+-|VmZxaXClGf%@-PiPhmqC z9N=0MF@V9}%Y>$HEDlthtud9wI|^~hS6fgj{(m0fl62~Wt|(0ha@$;Ju_G8_oOv9I zA)mpM*qtfP+}6)ns0LA}^wTpQiuT8}zxvE`a7dhWXl$bs7mTo9q@`?za8H?$0t?lR>q3?lxF@+xZh^{T#Y9qD2<3gzTUzu01=e{>0 z9{Bd4I)U(6I^oyU^v@bY$jLH3Z-=U47R9IHcTlDn*V-AUHJxjTj*jbn;$Y76#FI%@ z5j<3yFQ>B7vQZWOiADPahBe%-NSslR!;uV5hE|Kzm@E%ds9MGRrzGk(HzyMX7nH1Pb|i3(OG z6JFc~SZRep?dkYq(Tm6Kg-U9g%^Z|(hI_N8#y3vZRduhYzJ&syJd=+Yg*y~^!L@CA zg~qu*D;}A02PwR@wQSSkmnk|v%19C|%-_MRK~ZVrStH^7;TDbaoV`kr!7iFRE!iJL z@@2|#T4kPSRir@Gd(wy)0F9b$#$>{S7SMY>0Iuu?1~2TsPa=ARK^9LpFmI5Z#kMed zJvnc+9#z|Ss5fw4RkwlB)d7Enr{bwN8to$id0)$>z|+%B;hc>_j9!JwMqz)a+IOAc zNk$~?xp-o^Fy2YIuy-b%uu)vq?*-eSUv4@pv$*kV91X9mL|IEl*8A}shY zmJMCQJ{FC`&)kO54|MGZrz9%*LoB)jKe&X}JGoEuHs0RG=^<&^rLnXb+kj!yx7R&; zb;v0yrdnN{K-`!u14YB+`A(t%#GAd68hK#fDPR1O9H66!el_Aju^Qus!IBP)&kOWs z;Cc?Kz-Cpk=sMMMG@V1#Tb_#6oIc!JXAOE04}`(OFYZ2Px$75ouS3yYW%_@tMff5u zqB{{9tK7!Pui)?^TbnA3=3(UMqKy3Rrh9J{&!5C1b@k0M?7-QJU>YQgHS@3TC|zwM zGkLD0oT7}M4o~uf+3~qiB`;-X)+yF3lU`7kuF4=af|STsm;t zII}T}V?Ire=R{elc&ATVH7-KWALdFd=CK)Uy3rHz*AZuq2N~xq*_$?Z2Ok`738YQ9 z;pHG=%r9b_uH<5_jV;Arh#es0Y6O=?aMcO1O()WAcSDF#R9|fB2|Lw|!;7XLZ2)Xu zKzKiq@V1%o(OlAZuA-5W2(i>Y0PU=%8L(Ik};U@7J`_l?*RFb&Bj5$2t2($e7*!2P> zevxBRxoNO`&G&sp0m-DE9fV``0M9Q@MB>%fL8)r2q+qv=0>Xxqhcjr^jdc0;mCFxh zQx7Htc{3uLH>2|aud=#SYHuWRq%Zb+w&0N)0I@QkMZ?Xed?E^8y<3_IPTi~yO4!RP zP5JHGf1q?C(m~}u84Rxw(9nhH9v2n+_{y*FCw0j$_@M%hm<-4ZYtqnw$8 z^f`qX^Th;V8my{XycBK^hmdL=!zR*izXVe+Bsn$$gK1vlDOTi?lvQR-`WFmDSmD#7 zHQ|Vj?{f+NdRdtf1wLTB+R%anYFjLRuYq1u)u_GPl_dXBUIWmZ=Yef}>kZ{cjIo$; z-E2f9NYA$(xqitET*$LtSTzscnR_>H&2UX9o|cgCZTw&`cj-aMQa*X1cULiwyHe`E zDFyxOly~E9)b=gIy*P(n-EQ|pRi#X6#;|B4&8dA{bleXq){&LMFq%lr`_~Xa9L^jD ziD+WdX;PTQ0zOc*J;6C4y+u3BNXj`DNt{qxF#Sj*h3pXhp_$<1`P^Qzx)Gh#fc>;ktd4>oI?xWO{=*9367i0rHRN(7ImyyA=PG#26t*7RAl}NM zVdcSaap=pBlUAepCI^^z}yZ>U@8kn@tb zGneqB(UjQND#ajE{5LM2@bLo9V2J^ZW{;xf=1wWD1G~Ku3c{7xjA~Do|}0c zpCFz!0IXc=)M1U=K--#Y1guE_EhVBSG_S!osHK-CAwwl@!h}0(x_k4Ub|9( zEt z=bAWwAH>YR`ed^y5QBbC6njc#L|5C45fjZzg#(Xtiim98QL)Om{9^?R?xyM*R#wM^ zzUET)^0mo}Hn^mIDHBWMlGEe6eB0u7^1CNRr8je3{f^%mR~q3Iu-Z6!DC^V~7aI;R zCu%(g3~v`d+keKz_c?4A_i!Arp*5=YO^V=RKe0vLK8o!#FMhnqpLsM)`Ol!?hqqNx zxizSWVq1P-%KeC?0|pkf66W1)p6SwO)PmJi0}Ei{G-@(l^w*;qQ-IYS?oUk;sU>0l z=bYGqt!hsvW_j z`z!wC@6fZTKkYBMx1Com4=wE6bJ~F^dnRrFW@&8tnzXeCSAR2d=7sp;Un^&xn3O&0 zo4pGs@0c7Ry&Kh&zEbuym?h?x0V8_Tl%i+W=ifARbkt36)Tm!lv`c@R0v~Slk;sQR zOXa<#vw%Z>*od^HM#Pr^;#q*I{!p_C_@vllU=|^>^8yYk@lz|Mn8%rV?r?n}4sJ9| z{}kVv$AI(2J%g*IM%AiVKT0vzNw+82Ilcz8F7Bd$Cd#0wkA|(C^z3F!kkuLtT#;S&6qLsBXF_ET*~>Lg#& z9DF? z0IEty5t@rb4bBoeefUV;ar4}P+`PRPqO-`&(v01yThh#lB{QaunF%i1D3<#1@mVY{ zp@T}$5$q4m*qg_HUZm~;pn`fJFp`Hyx^}1Fz*2$5l zD3nF(RDtN43Pq3i%CW&>y!{l2kw?#xZy7hcW9DNNnF0px{dp4M+x|hi)FIR6dV8<^ ztOIgRSv`1~(>fS+c2bwl>X zP4Ds{RTSQTOY7g?sU~$>M*;1LbOg$_OS#db>7UvvRX{CT$?6DnkKS{0 zsoAMv&u#iq+X}gV&z%06PV_R6bU1XP7}|5Jf!GF$jyAIgQ(!cXg;6f40b3I7*)uvikR%FGFY^?R^BBkb6bKRLin}}3k)<4B8>gwwTj3!F6HPS zxhmo&^dhhri?I%e*<>s1L>Hwap05{KPG%9UkE+LfDEc-X5Zmck&Ww&WdatP01YYSL zKM4M4RqSoh=ppDna*q;w8BoXi%h&&^+btROCHOZ98vmf7GQHe zV9E3cek94(nM`aga+B3(C{lVG=$B@?7eM6$qWyY89$O7*G1XADy@z_JtgX@**nVIF z@58h4Y}_90kC$@Y=e4AXz8SO)w(y%qI>HrW&k>_=+}TXaHRJN(^FNt;rmxeIRxDfO z!W(Uj*^fKpX_ErhkiKa!B#IjMfh?ru(H7;RLQj$s)st*UP6hBkO|J>*Hon&r{`Fi` zM}ZF*t2VS?pV}6yG|*rwu<4YN+S1V)`w?N(6=&jn{iZmJZti}cF7F2GPIVmZwT(tw zrpEV2mbT!E-o*CsAdsJ+x*sB6@8%V#^HR9Y!*YdV1%_Rlbqe^JJR{ zDXk|lQd#nKyC5}F2aFbdbnpE)?)&}q?#MpzElmEHm&iv``yn71!IRcI^m2SR!Nj|t zJW!@r*(`2T@t3sxz|^}k5qc4Rl0AHe+Eod2#%H?EXRx)89?}u4s>wj!v2l*?CYik5 z^B1n6UF(19M&jKF9|Qe=q20LXzk9#CyV1+U&+=~ZQ7%1qe>#-#i(pJ&DZY>-x?Xy- z=tjONPg-UAZlEkVTHnegZ8?KPA5A}_q1`%Q~>BR;kx(%N@KCF9&ZSh&}@ zypRj+RtkFw=Pcr!6x;oye1o$+x9>Z_{Tp#XIX=q+ypajm=^)*`jyn?TH29;&ON#EW z7va&zFKJBaSSv>ZOn#)q_u zmP4`2$5*9*=ix1Q3%-G$10OYcj6{{x_W2qz=s}@Z;Ml9RgwMpy`ubu1o~b=jC4In8 zwa;BUqf#hBRqkks^hbg4>E{+o37#t6oQ=hq^Abt;K&>K54S`nQtCO+&k+@lRkn6i@ zy+8H_ag0V2`aAeUx87&*+hW2$;{dNkT1baNF zE%3rG0WxlZZi98?&G-ezg3=toMwlvoG6XX3b0YISY$ijUYD=wjeFl?%U__EI%$8ob zfjP9u0_WnxI2Rvj?SXS&PgSn7g$SMog+U4K#t5h5#pTgkP;X}Hr=KZS}* ze%VU97|s_ShKJ)}c(^fi^Ue(c>dvWd;A5Jp;RbK6RV$!ju~Z+6oxW=ljyMIo+cqdA3~)|lnIH( zjlL+LWGv1FcDcOa{jhWr% zQ6{{d-Aq_0Q%Mr zbPZ{a)ZrqWLuZ6PXTc_F21GIejw;p(mC^|Jl^q}SNy8EbZL2MFxF3i8-45^;%W2Gz zd>WM~FPYy1c>KOjb*P$xQd%&H1*yRskJd~dHv&R-iL!g8ZGT`v!fEU$)09_?(z$_6 zT%*)wl8`1zvT5aJi>y)f=Qc?34aZoTO_tNd(#GOStD8=cEj|y24eKh+QPA|G334vI z=`JZ7#WD=C1`V%A&T60o0g1_pl;(@Ykuc6j8SSJ(n?x>Kmd=~bU4&PiZExv?`3L1V zxqW0S&A=v*t%wJc#lM?OGuOhqxa$8wuj{46?M_ECz+XlIZ&lOx`mA9|BCg~h)T1Dn zzYg%dRKin{g!wwa<2uVbp77*Yk`#+nJOU2Np8vRxaE$3Dsw^TqR_h2TgU)bvG z5)|%v7S*SNqD`eGoaks#cH*hiXHHziqGaWV;LYT;gh*yVUHph+*$-;Yy$1!?QTIOZ zckR{53E`;+9~L5oW~Ne$eMXddknamE{$k>1s8enDiu-hnn}#UR1pH1ye^j6=wC8$W zSNDRoPv1-~;J zrc50B{^3-c6kx5nF`9$o)pyd(dyN82LiBT+6mun!iX@*7gWWdOC~#6s$HPFz&K~K zH8#QDcI52Ydtac)^?=BpVM#DKUqe)yp-Z!XUia%!NEgOVRI8EbBb81^WzkQA#)L5f z=wVSC0CiBM?DtfIgvweI6)lr|0nZ;k3n?E&%DU)*KuhIGbokXghEGqIR;UxhyU63^ z;3FAH_~X@PXUFQ?M2-py>^cFwM?;VWc+-0Cgu$F(Vg_@`Al|eq2s$(nLm~FU*6TBY7s!Y%n!y_o$Pl1HM<$ez zI68u+XuM&Qh9r?>r%J1`(f0}~J+OO2PkuJ%6{>x6J`?xp1JUyE?D7I{eRX1DNs;YV zT|U<}VeK2+OdMj5p0sKf7c!cH#Qh2Dl^*-7gm=Zl)8Pw=PUx6vtU3M4SF_OB#Wzgi zD*I_eS0EzV9)G{ljU)5SzGfd86ggHXM-xp5ASoLXb`LYJ>se$GfU{A&@%hh_0X z*ww;aCchG<9uZ9u%%_}!%PB%Z&BHV2s`Wd8okiG@C zr_fEjx`K=I-=u78+S+ zC;EiL=75T8KcIWZ+Nm#OtVN-}#~fSa4#-gZ>7;(KZkZPcz4_6R_Y<$cnEM1h*7QlJ z7VovgK47fnJ04ms zl8|W&udx>M`6>rCS90h^Z6x)a^4bV?4<+PKA?W}>wE>Sv2a(yVZIo@gvU3(Bv{|{t z=$tNaONu6@^F}MV=wrZjd}VqTB|Q}dEHn_XiVX#$EHgL+bw^NJuNEO{Q&n$MQ&R6_ z$Kb%QcL&Zp6`HL)Y4q)U?lFThaMi;;WPbPfA18@j!QNQHWW@L!!k6M$j^sz&SmI9D zZ%A!)(*1`Ul9GHNIaZ^3i!oQNU0pSOB!70l&AD7wXeCRmAVxF}3Xh(~LyD_o74T$s zcTL*r%2l4bW0t6o;fbkiR^;kG6LK3@_N_I_qCW)4_|LrtU zt_s7yA$A)d_x~WBK^YuzNcUy&a~`wec{+9}T>OAhuPBi^;|-ReVVBK7c4fOcTN=~e z-`g3c{AC+MxNh3f%FPfSQo0^2v-iG0Q&ZoF(%wG#)C}cCU7#l$EMWc`4nm9-(FaIi z6>)P+%meXKvVvFJmtiHPEo<@<=1B=#Gn-0e8KVT1Qn)<^62xkpRwa$Avy{=cjNr!TTV=r zE3>QXQ2vV)!Qxrftr&XQzhHawtlZSMq5QZsZHSqXJ#l#WNM z-}t|A;bM|Ah8<>Azvua*(WG+ok)2)!|1dF5?0?DGK~Web*2Pn#A8-H2tW|9m*XW zK=P7OK&Mq_BV3_u;95SEIop64I)Inu7IWov!E<}=E#bUBk}|>$&`C0h-;C6h;6s`a z9R@y=_#1hJG(1*kiOyb0W9N_A)7w{chtcEv!(0m=oOe&xwO*enj4*j6(%0Ma${O&i^HRzTuR}f} z3Lrihh=(35a1jAr|A>G>ox<~$?aF`{?diM42!~`*p6@CUh4~j^K*5~2j}C~QjUGD? z(J^go@0t1nLn#&wd9k-DvKmFVZ`_6$sU(Az=29L7lZxgz1719SKEf#o*^cdGl{zcD zW45uPYvM{psIzl;s0UU~`Cj1@Z!Bgt$w1(GpeP{RP&|8WZ(%yy2yR3-VVoCN|XgH}gU$nA@zqvcUyQ&!KGun?!gslB)m>k}PlY2^PNi2OhI-F>@0pbU%4 zwFj!|N03E7E+vHZWOw~E;*wql%NFkZd|K^pxq3Sr zN9_l$b!>#MXm)V-Yv0+kb>~Z?eQ#m!e$eigULIy6X7niH6VWjWOlL?BfaT48PxqgG z5VgWX18)-$1(VtepB8q3e54LGg@dKzX+^bdOyf%(GDJ}fRM4SDsq$RXrTTBmKcpSX z$T$=vu+5pqi(1oz8<2TJdG%&=7?pK1)_p_^hV0#sc0OEq>+qrsP}wX zU2iQlxqe~2(_IJ(4NF_t5t7MdXr{27B)RQSZFt;n($Wn6GG$7Qbezfm(*Xa&1rS+B zc+|nBFN1h}M3dV68S`F<@iaKFvA&d^wl^`}48)#qZC&Rb9Ma!=`J z;D3&}v{({G@lpkokFuA7`_}H=yK!&CgOp3P=F^l-Ys@F~FGm+DZFLXIr0I# z&#n+CW^+Rk#a<|mT6R5FuL@iMsYf6;U@szu9m7@t0G_hUA!w zc-c3xqh!L;+r+Q4KHanEhZTrc%3_X8_F>4-)@LJ9tiRydx5vPJqq$flTV-rePqb9_ ze~!p3Cmf0>X=RWUj8)-~FTLBI8o<1yik6>53L@M_s63}1L*p|A?wB+AFOPP%J^Avf z)6C9H@IqVF4@IyIN?_ff^6|(6fWX~h@(SCt<7)$}Fv?lMJ&E?S?K>{t&Sk!d z%5Dj;ofY1Z=oq_u_-S3Yt*dSF>TK*U03cI%_8pSjD2SL5kg2 zICH8AwGlUJ95H5Q$Cj#bbPN+c5U`!eSI(SbT&%A&4Jluy5Z^dY@l(S$%>tWsQSlf_ zss~!cPiEo+DJILJLT;4B2V;ocegK|QY%z)PkdKTk!*>P5yr8c{K}gBNA*Cas)z7ffp~50s&CKf8G$uNfA*f z=J{~)-<1YPiU#tkezkoI&ML~? zK?Nj$?D&Dh#&Ii#?!*HU6fljN2OiKbMm8vL)GQ<2smCpimcx%Y5&_CW=}lJ}c!Q_~ zM6q40RG}x(0nmF%4?z?ONYAiTZQEXEf4M?64^DYRGQ}ULLG?2zUQ;jkMYz+tPhd%IFan4zg0Occ6@rTc{HU6KLW8A&1BfdAABw}u;? zkiks0s(xz0(dZx@t@0;k*d*nksP>wSbj_?^RShs$67A3oMbJAIjdG9`WV=TAoOhP! zG2?uVilaC{ph{98Nu$Y9P#^1*=qZ#ylKxYhhy${A)$m?@uO=1*gPYjjgauie4NN&W zQ8BSN(z4WGqfFuJN&bWv3ZC?&e&R1D8IAIUZq7t#WVfptHOK~LuoABYA;^``6cbd) z%Y(DwlE>*mNmm<_||9op|}f(e=gjBe`7;dZ#yd|MJdb%~?+2xibxqNf?)ThV}dlZNio za|_g#)FaU~9oXWu(K(cDU2_Rf#Fl8ej6(@@7`o`>Mmf)i@~mm+lilbVK|HQ$p*QI_ z!(CeAd9Itc^uo}Hp$kV1F@2Y}Xc>F=r@8P%{=*^qicB!QOx^&K=xYG_9AH)`1_R<2 zT?sn3fkpd@l_Yhl#Nz?4*+`~c>(6OnTv5!o`&j~+I4+&yt4!i|&!W2&-yTu3hh=+q z?NOy;kLJpY{{N*N)&Hhhbne|%y}FiB{qM%>e~N3y8Q~0|)=rgs`WPNBe|>$0iD6=q zW|~Yn@;NN@kuo(l*yW%$cm3#HY(xv~#|4%}w__6*7op zCGmlC|MqCn?y?@&X>?tFGaBC9$SbOVLW?N6@l6am=(^e(YU*N-3;2N>?kmO|SN}KA z$NdT4`BO#JnZ9YV6i{%=VmsW4F1NXc4)#Tt>+WukdfEHKW4W-z_q+O+`eWG?HC}b8 z3)MyHD)l{ellrNeq3%}qso#dx>-;-X>K)c`@Txq`-NO1jwv7i|_3atD&}#G4F4&!g z74B2%)#Au>)6KO1e*V@jjSYIVnHO9x_C~ z;^XWWQiUAG-rU4DWqz@NkH$}PhOREPAv9DS7ZSq7b>cJcr7p|MFL)U1@*f__`LrXN z@|mO0tQ<|A=SWosBV{e|{EUO+=oXrT9u06}ZfQP-Tx{ey{2{GGU}rTU>>HTAemCwe z03hDud3VU)%SeiwQ^pe#HDq$~N$?GEH39P1KpAJx7ru?-bK_cFhm)5dFBtV$vpuuSc!0&9r)g`w+6=^7uM~}xV(wEaOa?Um zjkJWXNz3G}B3>@UxcE!T$F$L#CJe0TJTGZ@kO?CkGJ-){QckI-kx2~Hk9wd#Cwlm> z4IHP((Fv3aCx-V>g~y2$SN~2yKOmX1{oxaWLsyQM&T_nX+feg6M^~_7D&N6{AQk)s z9madn5qt$M@5|F@ln#d8Kx^QeDQ+C7&8Q~c(wmm4pHmKzmt-k=K;>mqrGI|&ow0V& z_kTC10bdQE@`~|)Vyv)kOxKD&Zu2q*Q*RBFKGtVRJS?^Zfs+$FxHptU9xExXn+-CN zMlO_4VUAItbX{BiYN7@X`O@pJZG`rC}LyFY>+oh(g&VG;8xDylX zl$Xp(i8+t>2#X&C4C%vnf>NGB@$r^ILfV~a8&_dOUZK|t63@FYJAxz>F7bWVXB1Bo zQGVTn29l@a>JUCj=;Vi0kphZuCQ-M7;$BG7-bSd;9OeKYU(RQq+*%8MNP$BRIcZM*N5st1T6pIt zdz;Ggt6mfD96D;E5 zMchIWw^GEd7csRWu3p47i?}uscV!{ChX!GudIs~t_i(TM1`7Af`_ z_>J#nMIbN^fEv5#A%OzjQ(F3(eBKDe*Ho+cmOAB*?-SC6v}7B2{~mJjZA$>G;q#&* z;FD664uBxC5-gPC?Oc8k9u)u(K_vZ?)ITl)geTzu%bNMQ`o~qko!hcJGeFvRO~yEO zLFW~jMBw8v*Rz(&Igau*%N9k=or2^g7X^p~Wd(0VpYF24o8@FQa1BYPnmP6KZ=;2 z88ZcC&-CAw{%0sRnaBo~!3CFuJocOw#{uQQONPOX&=|r^ZZ^w}zrdA7I7=j+d8f(n zbCV$pN47kCvnSC-zBtAlKdu4}uI4Gkb9=G`?&&l1DVTfaI>QSvR{lEM5JY$1GiE7| zO3y-8#;G~mk4P!jvY9T<&cmv)wUIp;u=ijn` zONHUg1X-m;=k+MlLc1;atN$aecrEjDmF;oug@gtps&@-DLI_Dz=M&ak34*nnRK?x2 zEtAQjn{`--$qeyo2|;5MwKtbIQxpLnop_Wk^^xFo%kqztxDo!-XRw?s5}sEz`ZVD2 z7@o&p-N&%I9tW@TU*B;u-33^*o>b|+8I;iV>;!HJDW4b`^J-&6Jh71|jFC@g^CN~2emYYSL?LCd zNH7iIDA&)V-ni(;W|hi?O**b=U}%8cSG?NkV2o#A}g)ub@!XaHrs-{0sa!&Y8y|DwiDVx*F5diL}<6q`D(U@ z(7q{!TK*B)uNA4y8D2VE;O(MLKE9e}VbZgN0@~M43Z4G z19^t_Qh}UDuseCwZUM-Gv5Uxfm~n1eF_(qL$4)!&i*TUzb{339Cm6w!45dZ|H5FY19 zA*Lw+RB&qy90K_&Y|-jfYQM!et;K;vaY2&!)+h=}DjLXR$P>u#PbKjulh*!%{0(uz z5O1M2&%L-|r@rT8oE#)%oVfld+c%AyD$`|Mr`zJgAkTddije;>l~Rlnu@>KJjKF}G ze%)lIwYcB*a?A@uSRg#Bz=)kf1oEWVIoyGr@WB727J@IQ1T(Pw=eD0RRIgC2C@cR`EnCfJKa$w8F)hCQEsL(~k65 zQp<&>BEzMu1~!8jLETd1N+ zx@E4n?%4Jq+aTyKn|q7f!SFu5uGdYq?t{-z6H=udxT zGXd5AW%h?Bk^TB`t^bE(i&=QsT*nAM881F0)Ix+(+2xBfiO~c}CKgqq95dK`XfBJd zEK;PzVjn5DM5P+5d}_7rw!WPI-&vv6F-Lsiq@#`PgzK*9@s0b==;NXkQ!%bGgk^+2 z=59GLCr_X7wU*Qxly$sSQ`jg_odBCCZ4qtyW5iPf9VMs(7gmMg8LLeHu8iSvIOWpgL`~R4t;Zwc1@6bF$T7a$OUXD< z6tY=3NY)6Hc^XJCogpIbNbm~DICUI%giK*$`ZlPOa^$yug4Yj#qaV)0TiW|=7y%!B45Tt8H7MI-uPvL%;@A1i*I` z14?uOfDo|ji1!@FAcoI0$Y78*hzqfCkSR{bAdAx5EQfhikTK}OyMjSi>YV%W=;I&c z)Aed75poYU)nrzbR3+vpLP=D-m6xGd%`^Limaf(WB?`!^5GBbX#mZ=4nFZ49bCoMJ zYmS$-)HJR+)+AQxW+;xZ%fc{b`eoGInPHAaO3ll3kzy<+DYO*-Ih3MAQq!U+-%$H? zecK7n1rH}h&65+V#DMY{=6aw^x!59wIlM1M2}GHsSfR7cQTEe$7AuZHM?iClQsacv zIy?HQS=UP(tBSY6-)I)Nya;3YRL86~p4_eG@GJ DkRz}F literal 0 HcmV?d00001 diff --git a/docs/src/fonts/JetBrainsMono-wght.woff2 b/docs/src/fonts/JetBrainsMono-wght.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..cd5102a44c763f1cfe9f2b199fd5d7257491c0dd GIT binary patch literal 40404 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 wrapper does the +// scrolling; see "Tables" in foundation.css. +// +// Cells hold identifiers, paths and digests that have no spaces. Left alone +// they either force their column wide or, allowed to break anywhere, crumble +// a character at a time. Instead each long token gets a `` after its own +// separators — `_ - / . :` — and a long hex digest splits into even pieces of +// at most sixteen characters, so it wraps where a reader would split it. +// +// Kept identical in the UsageBench repository. + +/** Tokens shorter than this never need a break. */ +const LONG = 14; +const SEPARATOR = /[_\-/.:]/; +const HEX_RUN = /^[0-9a-f]{16,}$/i; + +/** Splits one long token into pieces, each ending where a break belongs. */ +function pieces(token) { + if (HEX_RUN.test(token)) { + // Even pieces of at most sixteen: a 40-character revision in three, a + // 64-character digest in four. + const size = Math.ceil(token.length / Math.ceil(token.length / 16)); + return token.match(new RegExp(`.{1,${size}}`, 'g')); + } + const out = []; + let start = 0; + for (let i = 0; i < token.length - 1; i++) { + if (SEPARATOR.test(token[i])) { + out.push(token.slice(start, i + 1)); + start = i + 1; + } + } + out.push(token.slice(start)); + return out; +} + +/** Text node → text and `` nodes, or null when nothing is long. */ +function breakText(value) { + const parts = value.split(/(\s+)/); + if (!parts.some((part) => part.length >= LONG && !/\s/.test(part))) return null; + const nodes = []; + let pending = ''; + for (const part of parts) { + if (part.length < LONG || /\s/.test(part)) { + pending += part; + continue; + } + const split = pieces(part); + split.forEach((piece, index) => { + pending += piece; + if (index < split.length - 1) { + nodes.push({ type: 'text', value: pending }); + nodes.push({ type: 'element', tagName: 'wbr', properties: {}, children: [] }); + pending = ''; + } + }); + } + if (pending) nodes.push({ type: 'text', value: pending }); + return nodes; +} + +/** @param {any} node */ +function addBreaks(node) { + if (!Array.isArray(node.children)) return; + node.children = node.children.flatMap((child) => { + if (child.type === 'text') return breakText(child.value) ?? [child]; + addBreaks(child); + return [child]; + }); +} + +/** @param {any} node */ +function wrapTables(node) { + if (!Array.isArray(node.children)) return; + node.children = node.children.map((child) => { + if (child.type === 'element' && child.tagName === 'table') { + addBreaks(child); + return { + type: 'element', + tagName: 'div', + properties: { className: ['ds-table'] }, + children: [child], + }; + } + wrapTables(child); + return child; + }); +} + +export function rehypeTableScroll() { + return (/** @type {any} */ tree) => wrapTables(tree); +} diff --git a/docs/src/styles/custom.css b/docs/src/styles/custom.css index 52171a0ad..693642aff 100644 --- a/docs/src/styles/custom.css +++ b/docs/src/styles/custom.css @@ -1,14 +1,35 @@ -/* DataFlowBench accent styling over Starlight defaults. */ +/* DataFlowBench's own layer over the shared foundation (foundation.css, which + is kept identical to UsageBench's copy). This file holds what is particular + to this site: its cyan accent, the analyzer palette, and the page widths its + charts need. */ + +/* ---- Accent --------------------------------------------------------------- + Two sets of names, on purpose. The `--sl-color-accent*` trio is Bifrost's + identity colour on every chart (vendor slot 0) as well as Starlight's + accent, so its values are the ones published since v0.1.0 and must not + move. The `--ds-accent*` tokens are the interface accent — focus rings, + selected controls, list ticks, links — tuned for contrast on the new + grounds from the same cyan. */ :root { --sl-color-accent-low: #10222b; --sl-color-accent: #0e7490; --sl-color-accent-high: #a5f3fc; + + --ds-accent: #2aa7c2; + --ds-accent-text: #8fe3f2; + --ds-accent-low: #0c1d22; + --ds-accent-line: #235d6b; } :root[data-theme='light'] { --sl-color-accent-low: #cffafe; --sl-color-accent: #0e7490; --sl-color-accent-high: #155e75; + + --ds-accent: #0e7490; + --ds-accent-text: #0b5f76; + --ds-accent-low: #dcf0f3; + --ds-accent-line: #7eb8c5; } /* Vendor colour slots 4-7, added for the four analyzers v0.6.0 introduced. @@ -33,139 +54,64 @@ :root[data-theme='light'] { --dfb-vendor-6: hsl(150, 90%, 28%); --dfb-vendor-7: hsl(310, 80%, 42%); -} -table code { - white-space: nowrap; + /* Starlight's light orange and green (lightness 60% and 46%) sit near 1.6:1 + on the light grounds, too faint for the chart labels, counts and legend + text set in them. The hues — and so the identities a reader learned — + stay; only the lightness drops, to about 4:1 or better on every ground. + Red and purple come down with them so no slot reads paler than the rest. + The dark theme, the primary one, is untouched. */ + --sl-color-orange: hsl(38, 95%, 33%); + --sl-color-green: hsl(101, 75%, 30%); + --sl-color-red: hsl(339, 75%, 46%); + --sl-color-purple: hsl(281, 70%, 48%); } :root { - /* Roughly 75 characters at the body size: the measure the prose on these - pages was written against. */ - --dfb-measure: 46rem; + /* The prose measure: roughly 75 characters at the body size. */ + --dfb-measure: var(--ds-measure); } -/* Desktop content width. - ---------------------------------------------------------------------- - Starlight publishes a 45rem content column, which is the right measure for - a page of prose and the wrong one for these pages: almost everything here - is a grid of cards, a bar panel or a viewBox chart, and at 45rem those stack - into a column several screens tall while two thirds of a desktop display - sits empty. - - The fix is the one variable the whole layout is built from rather than a - per-component breakout, so every grid, chart and table on every page gets - the room at once. The grids are already `auto-fit minmax(...)`, so they - spend the extra width on columns without any further help. - - The width is a clamp, not a step, so it grows with the viewport and never - shrinks below what Starlight published: - - the lower bound is 45rem, so nothing below the 72rem breakpoint — every - phone and tablet — is touched, and neither is a narrow desktop window; - - the middle term leaves room for the chrome that is actually on the page: - the nav sidebar always, plus the table-of-contents sidebar on the pages - that have one (Starlight gives the two sidebars equal width), and 6rem - of gutter; - - the upper bound is 80rem, past which a wider column stops buying - columns and starts buying whitespace. - - `screen and` keeps this out of print, where Starlight sets its own - full-bleed width on `.main-pane`. */ -@media screen and (min-width: 72rem) { - :root[data-has-sidebar] { - --sl-content-width: clamp( - 45rem, - calc(100vw - var(--sl-sidebar-width) - 6rem), - 80rem - ); - } - - /* A page with a table of contents pays for a second sidebar, and Starlight - splits whatever is left over between the content pane and the contents - pane — so every rem of gutter asked for here costs the column two. The - reservation is halved to match: the same gutter, half the price. */ - :root[data-has-sidebar][data-has-toc] { - --sl-content-width: clamp( - 45rem, - calc(100vw - (2 * var(--sl-sidebar-width)) - 3rem), - 80rem - ); - } - - /* A wider column must widen the grids and the charts, not the sentences. - Running text keeps a fixed measure and everything else — grids, tables, - charts, card strips, the components' own containers — takes the full - width. - - The rule reaches paragraphs at any depth, because the prose that would - otherwise run to 130 characters is as often a chart's explanatory - paragraph inside a component as it is a top-level one. It is a - `max-width`, so on everything already narrower than the measure — every - paragraph inside a card, a bar row or a dialog panel — it does nothing. - Lists that carry a class are layout (legends, chip strips, coverage - lists), not prose, and are left alone. */ - .sl-markdown-content :where(p, ul:not([class]), ol:not([class]), blockquote, dl) { - max-width: var(--dfb-measure); - } +/* A wider column widens the grids and the charts, never the sentences. + Running text keeps one fixed measure and everything else — grids, tables, + charts, card strips, the components' own containers — takes the full width. - /* On the landing (recognized by its decision landscape), top-level explanatory - paragraphs flow as two newspaper columns instead of a capped single - measure that leaves the right third of the wide column dead. Each column - is ~half the content width, i.e. the same readable measure the cap - enforces — the width is spent on flow, not on line length. Short - paragraphs simply occupy the first column; the eyebrow label stays as - is. Browsers without :has() keep the capped single column. + The rule reaches paragraphs at any depth, because the prose that would + otherwise run to 130 characters is as often a chart's explanatory paragraph + inside a component as it is a top-level one. It is a `max-width`, so on + everything already narrower than the measure — every paragraph inside a + card, a bar row or a dialog panel — it does nothing. Lists that carry a + class are layout (legends, chip strips, coverage lists), not prose, and are + left alone. - This applies to a paragraph *on its own*, where the two columns are one - continuous read: down the left, then down the right, and nothing else is - interleaved. It must never be given to two adjacent paragraphs, which - would put the tail of the first at the top of the right column and the - tail of the second directly beneath it — two half-paragraphs stacked in - reading order, which is not the order they are meant to be read in. Runs - of adjacent paragraphs use `.prose-columns` below, which flows the whole - run through one multicol box instead. */ - .sl-markdown-content:has(.decision-landscape) > p:not(.eyebrow):not(.subtitle) { - columns: 2; - column-gap: 2.5rem; - max-width: none; - } + Earlier versions flowed long runs of prose as two newspaper columns to use + the width. On a screen that means reading down one column, scrolling back + up and reading down the next, so the prose now keeps one column and the + width goes to the figures. `.prose-columns` remains as a plain wrapper. */ +.sl-markdown-content :where(p, ul:not([class]), ol:not([class]), blockquote, dl) { + max-width: var(--dfb-measure); +} +.sl-markdown-content .prose-columns > p { + max-width: var(--dfb-measure); +} - /* A run of consecutive paragraphs as one newspaper block: every paragraph - of the run flows down the left column before any of it appears on the - right. The wrapper is what carries the columns, so the paragraphs inside - are ordinary block children and the rule above no longer reaches them. */ - .sl-markdown-content:has(.decision-landscape) .prose-columns { - columns: 2; - column-gap: 2.5rem; - max-width: none; - } - .sl-markdown-content:has(.decision-landscape) .prose-columns > p { - max-width: none; - } - /* Paragraphs may straddle the column break, and should: the balancer needs - to split something, and a run of two or three long paragraphs that each - refuse to break leaves one column half-empty. A continuation is legible - here in a way it was not under the old per-paragraph rule — the right - column carries one text in reading order, so the reader arrives at the - top of it from the bottom of the left. Orphans and widows are held at - three lines so a break can never strand one line at either end. */ - .sl-markdown-content:has(.decision-landscape) .prose-columns > * { - orphans: 3; - widows: 3; - } +/* Tables a step below the prose, as on UsageBench: the evidence tables here + hold ids and figures, and the smaller size keeps their identifier columns + to two or three lines inside the desktop column instead of one piece to a + line. */ +.sl-markdown-content table:not(:where(.not-content *)) { + font-size: 0.875rem; } /* The two viewBox charts scale with their container in both axes, so an unbounded column would spend the vertical space the grids just gave back on taller pictures. Past roughly 64rem the labels have stopped gaining legibility and the plot has stopped gaining resolution, so that is where - they stop growing, centred in whatever column they are given. Semantics, - geometry and every number inside the SVG are untouched — this is the - rendered box only. */ + they stop growing. Semantics, geometry and every number inside the SVG are + untouched — this is the rendered box only. */ .evolution-chart, .ranking-chart { max-width: 64rem; - margin-inline: auto; } /* Collapsed data tables. @@ -182,70 +128,67 @@ table code { latency ribbon use. Those switch between views of one region; there is no competing view here, only a table that may be shown or not, and inventing a second radio state for that would be a toggle with one meaningful - setting. */ -details.data-table { + setting. + + The same look serves every "Exact figures" / "Show the data table" + disclosure on the site, whatever its class: a hairline row with a plus that + turns into a cross. */ +:is(details.data-table, details.decision-data, details.landscape-data, details.historical-progress, details.figure-notes) { margin-top: 1rem; - border: 1px solid var(--sl-color-hairline); - border-radius: 0.75rem; - background: var(--sl-color-bg-sidebar); + border-top: 1px solid var(--ds-line); + border-bottom: 1px solid var(--ds-line); + background: transparent; } -details.data-table > summary { +:is(details.data-table, details.decision-data, details.landscape-data, details.historical-progress, details.figure-notes) > summary { cursor: pointer; list-style: none; - padding: 0.45rem 0.75rem; + padding: 0.6rem 0; font-size: var(--sl-text-sm); - color: var(--sl-color-gray-2); - border-radius: 0.75rem; + font-weight: 500; + color: var(--ds-text-2); display: flex; align-items: center; - gap: 0.5rem; + justify-content: space-between; + gap: 0.75rem; + transition: color var(--ds-duration) ease; } /* Safari still needs the WebKit pseudo-element to drop the native triangle. */ -details.data-table > summary::-webkit-details-marker { +:is(details.data-table, details.decision-data, details.landscape-data, details.historical-progress, details.figure-notes) > summary::-webkit-details-marker { display: none; } -details.data-table > summary::before { - content: ''; +:is(details.data-table, details.decision-data, details.landscape-data, details.historical-progress, details.figure-notes) > summary::after { + content: '+'; flex: none; - width: 0; - height: 0; - border-top: 0.3rem solid transparent; - border-bottom: 0.3rem solid transparent; - border-left: 0.42rem solid currentColor; - transition: transform 0.15s ease; + font-family: var(--ds-font-mono); + font-size: 1.05rem; + line-height: 1; + color: var(--ds-text-3); + transition: transform var(--ds-duration) ease; } -details.data-table[open] > summary::before { - transform: rotate(90deg); +:is(details.data-table, details.decision-data, details.landscape-data, details.historical-progress, details.figure-notes)[open] > summary::after { + transform: rotate(45deg); } -@media (prefers-reduced-motion: reduce) { - details.data-table > summary::before { - transition: none; - } -} - -details.data-table > summary:hover { - color: var(--sl-color-white); +:is(details.data-table, details.decision-data, details.landscape-data, details.historical-progress, details.figure-notes) > summary:hover { + color: var(--ds-text); } -details.data-table > summary:focus-visible { - outline: 2px solid var(--sl-color-accent-high); +:is(details.data-table, details.decision-data, details.landscape-data, details.historical-progress, details.figure-notes) > summary:focus-visible { + outline: 2px solid var(--ds-accent); outline-offset: 2px; } details.data-table[open] > summary { - border-bottom: 1px solid var(--sl-color-hairline); - border-end-start-radius: 0; - border-end-end-radius: 0; + border-bottom: 1px solid var(--ds-line-soft); } details.data-table > .table-scroll { overflow-x: auto; - padding: 0 0.75rem 0.25rem; + padding: 0 0 0.75rem; } /* The content area's global sibling rule adds a top margin to every element @@ -255,6 +198,13 @@ details.data-table > .table-scroll > table { margin-top: 0.75rem; } +/* Numbers in the site's tables line up: the mono's figures are tabular. */ +.sl-markdown-content :is(td, th) :is(.num, .ds-num) { + font-family: var(--ds-font-mono); + font-variant-numeric: tabular-nums; + white-space: nowrap; +} + /* In-page anchor ergonomics. ---------------------------------------------------------------------- The landing page's section strip and the snapshot pages' "On this page" diff --git a/docs/src/styles/foundation.css b/docs/src/styles/foundation.css new file mode 100644 index 000000000..bbddb4c6b --- /dev/null +++ b/docs/src/styles/foundation.css @@ -0,0 +1,1278 @@ +/* ========================================================================== + Benchmark site foundation + -------------------------------------------------------------------------- + Shared, byte for byte, by the DataFlowBench and UsageBench documentation + sites: edit it in one repository and copy it to the other. Everything + site-specific — the accent colour, the analyzer palette, page layout — lives + in each site's own stylesheet, which loads after this one and sets the + `--ds-accent*` tokens this file reads. + + The language is borrowed from the Brokk SlopCop design system and turned + down for reading: a near-black ground, bone-white type, hairline rules and + chamfered panels, with one accent per site and nothing rounded, glowing or + decorative. Separation comes from hairlines, not from fills. SlopCop's + display face and its red are deliberately not carried over: these are + reference pages, read for minutes at a time, not a landing page. + + Starlight's own styles live in cascade layers, so every rule here — which is + unlayered — wins over them without `!important`. + ========================================================================== */ + +/* ---- Type --------------------------------------------------------------- + Apfel Grotezk for everything a reader reads; JetBrains Mono for evidence + only — identifiers, digests, paths, code and the numbers in tables and + charts (Apfel's figures are proportional and it has no `tnum` feature, so + any column of numbers is set in the mono). Both are OFL; see + `src/fonts/OFL.txt`. */ +@font-face { + font-family: 'Apfel Grotezk'; + src: url('../fonts/ApfelGrotezk-Regular.woff2') format('woff2'); + font-weight: 400; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: 'Apfel Grotezk'; + src: url('../fonts/ApfelGrotezk-Mittel.woff2') format('woff2'); + font-weight: 500 600; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: 'Apfel Grotezk'; + src: url('../fonts/ApfelGrotezk-Fett.woff2') format('woff2'); + font-weight: 700 900; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: 'JetBrains Mono'; + src: url('../fonts/JetBrainsMono-wght.woff2') format('woff2-variations'); + font-weight: 100 800; + font-style: normal; + font-display: swap; +} + +/* ---- Tokens: dark (the primary theme) ------------------------------------ */ +:root { + color-scheme: dark; + + /* Grounds, darkest out. They differ by a few percent on purpose. */ + --ds-canvas: #07080a; + --ds-well: #0b0c0e; + --ds-surface: #0e1012; + --ds-control: #131517; + --ds-surface-hover: #17191c; + + /* Hairlines. */ + --ds-line-soft: #1f2124; + --ds-line: #2e3134; + --ds-line-strong: #45484b; + + /* Text. All three clear 4.5:1 on every ground above. */ + --ds-text: #f2f0eb; + --ds-text-2: #c2c0bb; + --ds-text-3: #9a9da0; + + /* The accent is the site's. These are neutral fallbacks only. */ + --ds-accent: #8a8d90; + --ds-accent-text: #d6d4cf; + --ds-accent-low: #16181a; + --ds-accent-line: #5d6063; + + /* Figures and code. */ + --ds-code-text: #d4d1cb; + + --ds-shadow-panel: 0 18px 40px rgb(0 0 0 / 0.32); + --ds-shadow-float: 0 12px 28px rgb(0 0 0 / 0.45); + + --ds-font: 'Apfel Grotezk', ui-sans-serif, system-ui, -apple-system, + 'Segoe UI', sans-serif; + --ds-font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, + Consolas, monospace; + + --ds-duration: 160ms; + --ds-measure: 45rem; + + /* One spacing scale for everything the components lay out. Paragraphs keep + Starlight's 1rem flow; a figure, grid or statement sits `--ds-space-lg` + from its neighbours; a caption sits `--ds-space-sm` under its figure; the + inside of a card is padded `--ds-space-md`–`lg` and separated in `sm`s. */ + --ds-space-2xs: 0.25rem; + --ds-space-xs: 0.5rem; + --ds-space-sm: 0.75rem; + --ds-space-md: 1rem; + --ds-space-lg: 1.5rem; + --ds-space-xl: 2.5rem; + --ds-card-pad: 1.25rem; +} + +/* ---- Tokens: light ------------------------------------------------------ + Warm paper rather than white, ink rather than black, the same hairline + structure. The site stylesheet sets light accents alongside. */ +:root[data-theme='light'] { + color-scheme: light; + + --ds-canvas: #f7f6f2; + --ds-well: #efeee9; + --ds-surface: #fbfaf7; + --ds-control: #f1f0eb; + --ds-surface-hover: #ebeae4; + + --ds-line-soft: #e4e2dc; + --ds-line: #d2d0c9; + --ds-line-strong: #a9a7a0; + + --ds-text: #15171a; + --ds-text-2: #3a3d41; + --ds-text-3: #5c5f63; + + --ds-accent: #5c5f63; + --ds-accent-text: #2b2d30; + --ds-accent-low: #ebeae4; + --ds-accent-line: #a9a7a0; + + --ds-code-text: #2a2c2f; + + --ds-shadow-panel: 0 14px 32px rgb(40 36 28 / 0.08); + --ds-shadow-float: 0 10px 24px rgb(40 36 28 / 0.14); +} + +/* ---- The chamfer --------------------------------------------------------- + Nothing is rounded. Controls and panels have their corners cut, drawn with + `corner-shape: bevel` so the border, the fill, the shadow and the focus + ring all follow the cut and nothing inside is clipped (the chart tooltips + and the dialog's scroll area both depend on that). A browser without + `corner-shape` gets square corners: the cut tokens are zero until the + feature query sets them, so no one ever sees a rounded fallback. Use them + as `border-radius: var(--ds-cut-…); corner-shape: bevel;`. */ +:root { + --ds-cut-tick: 0px; + --ds-cut-badge: 0px; + --ds-cut-chip: 0px; + --ds-cut-control: 0px; + --ds-cut-panel: 0px; + --ds-cut-lead: 0px; +} +@supports (corner-shape: bevel) { + :root { + --ds-cut-tick: 2px; + --ds-cut-badge: 4px; + --ds-cut-chip: 6px; + --ds-cut-control: 8px; + --ds-cut-panel: 14px; + /* The longer cut a chip or paper panel takes at its top-left corner. */ + --ds-cut-lead: 10px; + } +} + +/* ---- Starlight's variables, mapped onto the tokens ---------------------- + Most components on both sites read Starlight's grey ramp and hairlines, so + this mapping is what carries the palette into them. The hue ramps (orange, + green, purple, red, blue) are deliberately left alone: the analyzer + palettes are built from them and must not shift. */ +:root, +::backdrop { + --sl-font: var(--ds-font); + --sl-font-mono: var(--ds-font-mono); + + --sl-color-white: var(--ds-text); + --sl-color-gray-1: #e2e0db; + --sl-color-gray-2: var(--ds-text-2); + --sl-color-gray-3: var(--ds-text-3); + --sl-color-gray-4: #64676b; + --sl-color-gray-5: var(--ds-line-strong); + --sl-color-gray-6: var(--ds-control); + --sl-color-black: var(--ds-canvas); + + --sl-color-text: var(--ds-text-2); + --sl-color-bg: var(--ds-canvas); + --sl-color-bg-nav: var(--ds-canvas); + --sl-color-bg-sidebar: var(--ds-canvas); + --sl-color-bg-inline-code: var(--ds-control); + --sl-color-hairline-light: var(--ds-line); + --sl-color-hairline: var(--ds-line-soft); + --sl-color-hairline-shade: var(--ds-canvas); + --sl-color-backdrop-overlay: rgb(3 4 5 / 0.72); + + --sl-shadow-md: var(--ds-shadow-float); + --sl-shadow-lg: var(--ds-shadow-panel); + + --sl-text-h1: clamp(2rem, 1.5rem + 1.6vw, 2.625rem); + --sl-text-h2: 1.75rem; + --sl-text-h3: 1.3125rem; + --sl-text-h4: 1.125rem; + --sl-text-h5: 1rem; + --sl-line-height: 1.65; + --sl-line-height-headings: 1.15; + + --sl-nav-height: 4rem; +} + +:root[data-theme='light'], +[data-theme='light'] ::backdrop { + --sl-color-white: var(--ds-text); + --sl-color-gray-1: #24272a; + --sl-color-gray-2: var(--ds-text-2); + --sl-color-gray-3: var(--ds-text-3); + --sl-color-gray-4: #8d9094; + --sl-color-gray-5: var(--ds-line-strong); + --sl-color-gray-6: var(--ds-control); + --sl-color-gray-7: var(--ds-surface); + --sl-color-black: var(--ds-canvas); + + --sl-color-text: var(--ds-text-2); + --sl-color-bg: var(--ds-canvas); + --sl-color-bg-nav: var(--ds-canvas); + --sl-color-bg-sidebar: var(--ds-canvas); + --sl-color-bg-inline-code: var(--ds-control); + --sl-color-hairline-light: var(--ds-line); + --sl-color-hairline: var(--ds-line-soft); + --sl-color-hairline-shade: var(--ds-line); + --sl-color-backdrop-overlay: rgb(40 36 28 / 0.35); +} + +/* ---- Base -------------------------------------------------------------- */ +body { + background: var(--ds-canvas); + color: var(--ds-text-2); + font-family: var(--ds-font); + font-kerning: normal; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; +} + +::selection { + background: var(--ds-accent-low); + color: var(--ds-text); +} + +/* One focus ring everywhere: 2px of the accent, clear of the shape. */ +:focus-visible { + outline: 2px solid var(--ds-accent); + outline-offset: 3px; +} + +/* ---- Header -------------------------------------------------------------- */ +/* Starlight names both the bar and its inner row `.header`; only the bar + draws the rule. */ +header.header { + border-bottom: 1px solid var(--ds-line-soft); + background: color-mix(in srgb, var(--ds-canvas) 92%, transparent); + backdrop-filter: blur(8px); +} + +.site-title { + gap: 0.6rem; + font-size: 1.3125rem; + font-weight: 500; + letter-spacing: -0.03em; + color: var(--ds-text); +} +.site-title img { + height: 1.6rem; + width: auto; +} + +button[data-open-modal] { + border-radius: var(--ds-cut-control); + corner-shape: bevel; +} +@media (min-width: 50rem) { + button[data-open-modal] { + border-color: var(--ds-line-strong); + background-color: var(--ds-control); + color: var(--ds-text-3); + transition: + border-color var(--ds-duration) ease, + color var(--ds-duration) ease; + } + button[data-open-modal]:hover { + border-color: var(--ds-accent-line); + color: var(--ds-text); + } +} +button[data-open-modal] > kbd { + border-radius: var(--ds-cut-badge); + corner-shape: bevel; + background-color: var(--ds-surface); + font-family: var(--ds-font-mono); +} + +site-search dialog { + border-radius: var(--ds-cut-panel); + corner-shape: bevel; + border-color: var(--ds-line-strong); + background-color: var(--ds-surface); +} + +starlight-theme-select select, +starlight-lang-select select { + font-family: var(--ds-font); +} + +/* ---- Page geometry ------------------------------------------------------- + The content and its contents list travel together as one block right of + the sidebar: the column of content first, then a narrow contents column + beside it. The block is the same width on every page, so the text and the + contents list sit in the same place on every page, and anything wide — + tables, charts, card grids — fits the column rather than moving it. + + On a large display the room the block leaves is split one part left to + two right, so the text lands a little left of centre, where the eye + starts, without a wide empty margin beside the sidebar. On a narrower + display the block simply fills the room, one gutter each side, and the + contents list and its gap narrow before the column does. Pages without a + contents list give its room to the content. + + On a large display the whole scale grows a little — 16px at 1440px wide, + 18px at 2560px — so the sidebars stay readable. Breakpoints are unaffected: + media queries resolve rem against the browser default, not this size. */ +html { + font-size: clamp(100%, 0.8393rem + 0.1786vw, 112.5%); +} +:root { + --sl-sidebar-width: 17rem; + --ds-column: 56rem; + --sl-content-width: var(--ds-column); + --ds-toc-width: clamp(11rem, 16vw - 1rem, 14rem); + --ds-toc-gap: clamp(2rem, 6vw - 3.5rem, 3rem); + --ds-page-pad: clamp(2.5rem, 4vw, 4rem); + --ds-page-width: calc(var(--ds-column) + var(--ds-toc-gap) + var(--ds-toc-width)); + --ds-page-lead: 0.3333; +} +@media screen and (min-width: 72rem) { + :root[data-has-sidebar] { + --sl-content-pad-x: var(--ds-page-pad); + } + :root[data-has-sidebar] .main-frame > .lg\:sl-flex { + --ds-page-start: max( + var(--ds-page-pad), + (100% - var(--ds-page-width)) * var(--ds-page-lead) + ); + margin-inline-start: var(--ds-page-start); + width: min(var(--ds-page-width), 100% - var(--ds-page-start) - var(--ds-page-pad)); + } + /* The content pane takes the block; the panels inside it add no gutter of + their own, and their rules span only the content. */ + :root[data-has-sidebar] .main-pane { + order: 1; + flex: 1 1 0; + min-width: 0; + width: auto; + } + :root[data-has-sidebar] .main-pane .content-panel { + padding-inline: 0; + } + :root[data-has-sidebar] .main-pane .sl-container { + max-width: none; + margin-inline: 0; + } + /* Starlight pins the contents pane to the window at full width; here it + is a column of the block that follows the reader down the page. */ + :root[data-has-sidebar] .right-sidebar-container { + order: 2; + flex: 0 0 var(--ds-toc-width); + width: auto; + margin-inline-start: var(--ds-toc-gap); + } + :root[data-has-sidebar] .right-sidebar { + position: sticky; + top: var(--sl-nav-height); + width: auto; + height: auto; + max-height: calc(100vh - var(--sl-nav-height)); + padding-top: 0; + border-inline-start: 0; + } + :root[data-has-sidebar] .right-sidebar-panel { + padding: 2rem 0; + } + :root[data-has-sidebar] .right-sidebar-panel .sl-container { + width: auto; + max-width: none; + } + /* The search box starts where the content does: the header's first column + runs to the sidebar's edge plus the block's offset. The percentage is the + header row's width, which is the window's less the bar's own padding. */ + :root[data-has-sidebar] .header { + --__main-column-fr: max( + var(--ds-page-pad), + ( + 100% + 2 * var(--sl-nav-pad-x) - var(--sl-sidebar-width) - + var(--ds-page-width) + ) * var(--ds-page-lead) + ); + } +} + +/* ---- Sidebar ------------------------------------------------------------- + Section labels quiet, links a step quieter, and the current page marked by + a short accent rule rather than a filled pill. */ +.sidebar-pane { + border-inline-end: 1px solid var(--ds-line-soft); +} +.sidebar-content .large, +.sidebar-content a { + font-size: 0.9375rem; +} +.sidebar-content .large { + font-weight: 500; + letter-spacing: -0.01em; + color: var(--ds-text); +} +.sidebar-content a { + position: relative; + border-radius: 0; + color: var(--ds-text-3); + transition: color var(--ds-duration) ease; +} +.sidebar-content a:hover, +.sidebar-content a:focus-visible { + color: var(--ds-text); +} +/* The top-level entries sit on the same step as a group label. */ +.sidebar-content .top-level > li > a { + color: var(--ds-text-2); + font-weight: 500; +} +.sidebar-content [aria-current='page'], +.sidebar-content [aria-current='page']:hover, +.sidebar-content [aria-current='page']:focus { + color: var(--ds-text); + background: transparent; + font-weight: 500; +} +.sidebar-content [aria-current='page']::before { + content: ''; + position: absolute; + inset-block: 0.35em; + inset-inline-start: -1px; + width: 2px; + background: var(--ds-accent); +} +/* Top-level current pages have no rail to sit on; give them a tick instead. */ +.sidebar-content .top-level > li > a[aria-current='page']::before { + inset-inline-start: -0.35rem; +} +.sidebar-content ul ul li { + border-inline-start-color: var(--ds-line-soft); +} +.sidebar-content summary { + border-radius: 0; +} +.sidebar-content .caret { + color: var(--ds-text-3); +} + +/* ---- Right-hand contents --------------------------------------------------- */ +.right-sidebar-panel h2 { + font-size: var(--sl-text-sm); + font-weight: 500; + color: var(--ds-text-3); + letter-spacing: 0; +} +.right-sidebar-panel a, +starlight-toc a { + font-size: var(--sl-text-sm); + line-height: 1.4; + border-radius: 0; + color: var(--ds-text-3); +} +.right-sidebar-panel a:hover { + color: var(--ds-text); +} +starlight-toc a[aria-current='true'] { + color: var(--ds-accent-text); + box-shadow: inset 2px 0 0 var(--ds-accent); +} +.right-sidebar { + border-inline-start-color: var(--ds-line-soft); +} + +/* ---- Page title and content --------------------------------------------- */ +.content-panel { + border-top-color: var(--ds-line-soft); +} +.content-panel h1, +#_top { + font-weight: 500; + letter-spacing: -0.035em; + line-height: 1.08; + color: var(--ds-text); + text-wrap: balance; +} + +.sl-markdown-content { + font-size: 1.0625rem; +} +.sl-markdown-content :is(h1, h2, h3, h4, h5, h6) { + font-weight: 500; + color: var(--ds-text); + text-wrap: balance; +} +.sl-markdown-content h2 { + letter-spacing: -0.03em; + line-height: 1.12; +} +.sl-markdown-content h3 { + letter-spacing: -0.02em; + line-height: 1.2; +} +.sl-markdown-content h4, +.sl-markdown-content h5 { + letter-spacing: -0.01em; +} +/* A section starts with a hairline across the column, not with space alone. */ +.sl-markdown-content :is(h2, .sl-heading-wrapper.level-h2):not(:first-child) { + margin-top: 2.75rem; + padding-top: 1.5rem; + border-top: 1px solid var(--ds-line); +} +.sl-markdown-content .sl-heading-wrapper.level-h2 > h2 { + margin-top: 0; + padding-top: 0; + border-top: 0; +} +.sl-markdown-content :is(h3, .sl-heading-wrapper.level-h3):not(:first-child) { + margin-top: 2.25rem; +} +.sl-markdown-content .sl-anchor-link { + color: var(--ds-text-3); +} + +.sl-markdown-content :is(p, li, dd) { + text-wrap: pretty; +} +.sl-markdown-content strong { + color: var(--ds-text); + font-weight: 500; +} + +.sl-markdown-content a:not(:where(.not-content *)) { + color: var(--ds-accent-text); + text-decoration-thickness: 1px; + text-underline-offset: 0.18em; + text-decoration-color: color-mix(in srgb, currentColor 45%, transparent); + transition: text-decoration-color var(--ds-duration) ease; +} +.sl-markdown-content a:not(:where(.not-content *)):hover { + text-decoration-color: currentColor; +} + +/* Lists: 6px octagon ticks in the accent. Ordered lists keep their numbers. */ +.sl-markdown-content ul:not([class]):not(:where(.not-content *)) { + list-style: none; + padding-inline-start: 1.25rem; +} +.sl-markdown-content ul:not([class]):not(:where(.not-content *)) > li { + position: relative; +} +.sl-markdown-content ul:not([class]):not(:where(.not-content *)) > li::before { + content: ''; + position: absolute; + inset-inline-start: -1.1rem; + top: 0.66em; + width: 6px; + height: 6px; + background: var(--ds-accent); + clip-path: polygon(30% 0, 70% 0, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0 70%, 0 30%); +} +.sl-markdown-content ol:not([class]) > li::marker { + color: var(--ds-text-3); + font-family: var(--ds-font-mono); + font-size: 0.85em; +} + +.sl-markdown-content hr { + border-color: var(--ds-line); +} + +.sl-markdown-content blockquote { + border-inline-start: 1px solid var(--ds-line-strong); + color: var(--ds-text-2); +} + +/* Inline code is evidence: mono, a hairline chip, never coloured by kind. */ +.sl-markdown-content :not(pre) > code:not(:where(.not-content *)), +.sl-markdown-content code:not(:where(pre *, .not-content *)) { + font-family: var(--ds-font-mono); + font-size: 0.8em; + color: var(--ds-code-text); + background: var(--ds-control); + border: 1px solid var(--ds-line-soft); + border-radius: var(--ds-cut-badge); + corner-shape: bevel; + padding: 0.05em 0.3em; + overflow-wrap: anywhere; +} +.sl-markdown-content a code:not(:where(pre *, .not-content *)) { + color: inherit; +} + +kbd { + font-family: var(--ds-font-mono); +} + +/* ---- Tables -------------------------------------------------------------- + Plain tables on the canvas: a quiet head under a rule, body rows divided by + the softest hairline, no zebra, no side borders. */ +.sl-markdown-content table:not(:where(.not-content *)) { + border-collapse: collapse; + font-size: 0.9375rem; + line-height: 1.5; +} +/* Every table spans the column, so tables line up with each other and with + the rules between sections. A table with more columns than the column has + room for pans inside its box (the `.ds-table` wrapper every markdown table + gets, from rehype-table-scroll) rather than running under the contents + list, and it pans before its cells get too narrow to read: each column is + owed about 5rem — 4rem from eight columns up, where they are mostly + figures — so a table of many columns keeps a floor of its own, short of + the column on a 1440px display. + Text wraps between words, and identifiers and digests only at the break + points the plugin gives them — after `_ - / . :`, and in even pieces of + at most sixteen characters of a hash — never mid-token (inline code + elsewhere may break anywhere; in a cell that let the column shrink below + its longest token and split `v0.2.0`). Numbers, the right-aligned + cells, never break; their headers wrap over them. */ +.sl-markdown-content .ds-table { + overflow-x: auto; +} +.sl-markdown-content .ds-table > table { + display: table; + width: 100%; + overflow: visible; +} +.sl-markdown-content .ds-table > table:has(tr > :nth-child(4)) { min-width: 20rem; } +.sl-markdown-content .ds-table > table:has(tr > :nth-child(5)) { min-width: 25rem; } +.sl-markdown-content .ds-table > table:has(tr > :nth-child(6)) { min-width: 30rem; } +.sl-markdown-content .ds-table > table:has(tr > :nth-child(7)) { min-width: 35rem; } +.sl-markdown-content .ds-table > table:has(tr > :nth-child(8)) { min-width: 36rem; } +.sl-markdown-content .ds-table > table:has(tr > :nth-child(10)) { min-width: 42rem; } +.sl-markdown-content .ds-table > table:has(tr > :nth-child(12)) { min-width: 48rem; } +.sl-markdown-content .ds-table :is(td, th), +.sl-markdown-content .ds-table :is(td, th) code { + overflow-wrap: normal; +} +.sl-markdown-content .ds-table td code { + white-space: normal; +} +.sl-markdown-content .ds-table td[align='right'] { + white-space: nowrap; +} +.sl-markdown-content :is(th, td):not(:where(.not-content *)) { + border: 0; + border-bottom: 1px solid var(--ds-line-soft); + padding: 0.55rem 1.1rem 0.55rem 0; + background: transparent; + vertical-align: top; +} +.sl-markdown-content :is(th, td):last-child:not(:where(.not-content *)) { + padding-inline-end: 0; +} +/* Tables of many columns are mostly short figures: a tighter gap between + them keeps a twelve-column table inside a 1440px display. */ +.sl-markdown-content .ds-table > table:has(tr > :nth-child(8)) :is(th, td):not(:last-child) { + padding-inline-end: 0.7rem; +} +.sl-markdown-content thead th:not(:where(.not-content *)) { + border-bottom: 1px solid var(--ds-line); + color: var(--ds-text-3); + font-size: var(--sl-text-xs); + font-weight: 500; + text-wrap: balance; +} +.sl-markdown-content tbody th:not(:where(.not-content *)) { + color: var(--ds-text); + font-weight: 500; +} +.sl-markdown-content tbody tr:not(:where(.not-content *)) { + background: transparent; +} +.sl-markdown-content td:not(:where(.not-content *)) { + color: var(--ds-text-2); +} + +/* ---- Code blocks (Expressive Code) ---------------------------------------- */ +.expressive-code { + --ec-brdRad: 0px; + font-family: var(--ds-font-mono); +} +.expressive-code .frame, +.expressive-code figure { + border-radius: var(--ds-cut-control); + corner-shape: bevel; +} +.expressive-code pre { + border-color: var(--ds-line) !important; + background: var(--ds-well) !important; +} +.expressive-code .frame.has-title .header, +.expressive-code .frame.is-terminal .header { + background: var(--ds-surface) !important; + border-color: var(--ds-line) !important; +} +.expressive-code .frame .header::before { + border-color: var(--ds-line) !important; +} +.expressive-code .copy button { + border-radius: var(--ds-cut-badge); + corner-shape: bevel; +} + +/* ---- Asides -------------------------------------------------------------- + A notice, not a banner: a quiet panel with a hairline, the aside's kind in + its title, and the colour kept to a 2px rule on the leading edge. */ +.starlight-aside { + border: 1px solid var(--ds-line); + border-inline-start: 2px solid var(--sl-color-asides-border, var(--ds-accent)); + border-radius: 0; + background: var(--ds-surface); + padding: 1rem 1.25rem; +} +.starlight-aside--note { + --sl-color-asides-border: var(--ds-accent); + --sl-color-asides-text-accent: var(--ds-accent-text); +} +.starlight-aside__title { + font-weight: 500; + font-size: var(--sl-text-sm); + letter-spacing: 0; +} +.starlight-aside__content { + color: var(--ds-text-2); +} + +/* ---- Details --------------------------------------------------------------- + Native disclosures in running content read as an accordion row: the summary + in text, a plus that turns into a cross when open. Components that style + their own `details` carry a class and are left alone. */ +.sl-markdown-content details:not([class]) { + border-top: 1px solid var(--ds-line); + border-bottom: 1px solid var(--ds-line); + padding: 0; +} +.sl-markdown-content details:not([class]) + details:not([class]) { + margin-top: 0; + border-top: 0; +} +/* Starlight's own disclosure look (a side rule and a chevron) gives way to + the hairline row for every disclosure in content, classed or not. */ +.sl-markdown-content details:not(:where(.not-content *)) { + border-inline-start: 0; + padding-inline-start: 0; +} +.sl-markdown-content summary:not(:where(.not-content *)) { + margin-inline-start: 0; + padding-inline-start: 0; +} +.sl-markdown-content summary:not(:where(.not-content *))::before { + content: none; +} +.sl-markdown-content details:not([class]) > summary { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + padding: 0.75rem 0; + cursor: pointer; + list-style: none; + color: var(--ds-text); + font-weight: 500; +} +.sl-markdown-content details:not([class]) > summary::-webkit-details-marker { + display: none; +} +.sl-markdown-content details:not([class]) > summary::after { + content: '+'; + flex: none; + font-family: var(--ds-font-mono); + font-size: 1.1rem; + line-height: 1; + color: var(--ds-text-3); + transition: transform var(--ds-duration) ease; +} +.sl-markdown-content details:not([class])[open] > summary::after { + transform: rotate(45deg); +} +.sl-markdown-content details:not([class]) > summary:hover { + color: var(--ds-accent-text); +} +.sl-markdown-content details:not([class]) > :not(summary) { + margin-bottom: 1rem; +} + +/* ---- Cards, badges, tabs (Starlight's components) ------------------------- */ +.card { + border: 1px solid var(--ds-line); + border-radius: var(--ds-cut-panel); + corner-shape: bevel; + background: var(--ds-surface); + box-shadow: none; +} +.card .icon { + border-radius: 0; +} +.sl-badge { + border-radius: var(--ds-cut-badge); + corner-shape: bevel; + font-family: var(--ds-font); + font-weight: 500; +} +starlight-tabs [role='tablist'] { + border-bottom-color: var(--ds-line); +} +starlight-tabs [role='tab'][aria-selected='true'] { + color: var(--ds-text); + border-color: var(--ds-accent); +} + +/* ---- Pagination ------------------------------------------------------------ + Previous and next take slants: each keeps the corners on its outer side + square and cuts the two that point the way it goes, so the pair reads as a + pair of arrows. */ +.pagination-links a { + border: 1px solid var(--ds-line-strong); + background: var(--ds-control); + box-shadow: none; + color: var(--ds-text-3); + font-size: var(--sl-text-sm); + transition: + border-color var(--ds-duration) ease, + color var(--ds-duration) ease; +} +.pagination-links a[rel='prev'] { + border-radius: var(--ds-cut-panel) 0 0 var(--ds-cut-panel); + corner-shape: bevel; +} +.pagination-links a[rel='next'] { + border-radius: 0 var(--ds-cut-panel) var(--ds-cut-panel) 0; + corner-shape: bevel; +} +.pagination-links a:hover { + border-color: var(--ds-accent-line); + color: var(--ds-text-2); +} +.pagination-links .link-title { + font-size: var(--sl-text-xl); + font-weight: 500; + letter-spacing: -0.02em; + color: var(--ds-text); +} + +/* ---- Footer ---------------------------------------------------------------- */ +footer .meta { + color: var(--ds-text-3); + font-size: var(--sl-text-sm); +} +footer .meta a { + color: var(--ds-text-3); +} +footer .meta a:hover { + color: var(--ds-text); +} + +/* ========================================================================== + Shared component vocabulary + -------------------------------------------------------------------------- + Classes the sites' own components compose. Component-scoped styles may add + to them, but the look of a label, a panel, a chip or a figure is decided + here once. + ========================================================================== */ + +/* A small label above a block: sentence case, never uppercase or tracked. */ +.ds-label { + margin: 0; + font-size: var(--sl-text-xs); + font-weight: 500; + line-height: 1.4; + letter-spacing: 0; + color: var(--ds-text-3); +} + +/* A panel: the one surface that sits on the canvas with its own ground. */ +.ds-panel { + border: 1px solid var(--ds-line-strong); + border-radius: var(--ds-cut-panel); + corner-shape: bevel; + background: var(--ds-surface); + box-shadow: var(--ds-shadow-panel); +} + +/* A quieter frame for a figure or a card within a grid. */ +.ds-frame { + border: 1px solid var(--ds-line); + border-radius: var(--ds-cut-control); + corner-shape: bevel; + background: var(--ds-surface); +} + +/* A chip: a toggle, a filter, an in-page link. */ +.ds-chip { + display: inline-flex; + align-items: center; + gap: 0.4rem; + min-height: 2.25rem; + padding: 0 0.8rem; + border: 1px solid var(--ds-line-strong); + border-radius: var(--ds-cut-lead) var(--ds-cut-chip) var(--ds-cut-chip) + var(--ds-cut-chip); + corner-shape: bevel; + background: var(--ds-control); + color: var(--ds-text-2); + font-size: var(--sl-text-sm); + font-weight: 500; + line-height: 1; + text-decoration: none; + white-space: nowrap; + cursor: pointer; + transition: + border-color var(--ds-duration) ease, + color var(--ds-duration) ease, + background-color var(--ds-duration) ease; +} +.ds-chip:hover { + border-color: var(--ds-accent-line); + color: var(--ds-text); +} +.ds-chip[aria-pressed='true'], +.ds-chip[aria-current='true'], +.ds-chip.is-selected { + border-color: var(--ds-accent); + background: var(--ds-accent-low); + color: var(--ds-text); +} + +/* A small quiet button, e.g. "Confusion matrix". */ +.ds-button { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.35rem 0.7rem; + border: 1px solid var(--ds-line-strong); + border-radius: var(--ds-cut-lead) var(--ds-cut-badge) var(--ds-cut-badge) + var(--ds-cut-badge); + corner-shape: bevel; + background: var(--ds-control); + color: var(--ds-text-2); + font-family: var(--ds-font); + font-size: var(--sl-text-xs); + font-weight: 500; + line-height: 1.2; + letter-spacing: 0; + text-transform: none; + white-space: nowrap; + cursor: pointer; + transition: + border-color var(--ds-duration) ease, + color var(--ds-duration) ease; +} +.ds-button:hover { + border-color: var(--ds-accent); + color: var(--ds-text); +} + +/* Numbers that sit in rows or columns. */ +.ds-num { + font-family: var(--ds-font-mono); + font-variant-numeric: tabular-nums; + letter-spacing: -0.01em; +} + +/* A statement: the one sentence a page exists to say, set larger than the + prose around it and marked by a rule rather than boxed. */ +.ds-statement { + margin: var(--ds-space-lg) 0 0; + max-width: var(--ds-measure); + padding-inline-start: var(--ds-space-lg); + border-inline-start: 2px solid var(--ds-accent); + font-size: clamp(1.1875rem, 1.05rem + 0.45vw, 1.375rem); + font-weight: 500; + line-height: 1.4; + letter-spacing: -0.015em; + color: var(--ds-text); + text-wrap: pretty; +} +.ds-statement strong { + font-weight: 500; + white-space: nowrap; + color: var(--ds-accent-text); +} + +/* Figures: a row of numbers, each above what it counts, divided by hairlines. + Written as a

    of
    ; the number is shown first. The + column count comes from `--ds-figures-cols` (3 by default), and a row of + four folds to two by two on a narrow screen. */ +.ds-figures { + --ds-figures-cols: 3; + display: grid; + grid-template-columns: repeat(var(--ds-figures-cols), minmax(0, 1fr)); + margin: var(--ds-space-lg) 0 0; + padding: 0; + border-block: 1px solid var(--ds-line); +} +.ds-figures > div { + display: flex; + flex-direction: column; + gap: var(--ds-space-2xs); + min-width: 0; + margin: 0 !important; + padding: var(--ds-space-md) var(--ds-space-md) var(--ds-space-md) 0; +} +.ds-figures > div + div { + padding-inline-start: var(--ds-space-md); + border-inline-start: 1px solid var(--ds-line-soft); +} +.ds-figures :is(dt, dd) { + margin: 0 !important; + padding: 0; +} +.ds-figures dt { + font-size: var(--sl-text-sm); + font-weight: 400; + line-height: 1.4; + color: var(--ds-text-2); +} +.ds-figures dd { + order: -1; + font-family: var(--ds-font-mono); + font-size: 1.75rem; + font-weight: 500; + font-variant-numeric: tabular-nums; + line-height: 1.1; + letter-spacing: -0.02em; + color: var(--ds-text); +} +.ds-figures .ds-figure-unit { + margin-inline-start: 0.35em; + font-family: var(--ds-font); + font-size: var(--sl-text-sm); + font-weight: 400; + letter-spacing: 0; + color: var(--ds-text-3); +} +/* A second
    under the label: provenance, in small type. */ +.ds-figures dd.ds-figure-meta { + order: 0; + font-family: var(--ds-font); + font-size: var(--sl-text-xs); + font-weight: 400; + line-height: 1.4; + letter-spacing: 0; + color: var(--ds-text-3); +} +.ds-figures[data-cols='4'] { + --ds-figures-cols: 4; +} +@media (max-width: 40rem) { + .ds-figures[data-cols='4'] { + --ds-figures-cols: 2; + } + .ds-figures[data-cols='4'] > div:nth-child(odd) { + padding-inline-start: 0; + border-inline-start: 0; + } + .ds-figures[data-cols='4'] > div:nth-child(n + 3) { + border-top: 1px solid var(--ds-line-soft); + } +} +/* On a phone a row of three reads as a list: each number beside its label. */ +@media (max-width: 36rem) { + .ds-figures:not(.is-compact, [data-cols='4']) { + grid-template-columns: minmax(0, 1fr); + } + .ds-figures:not(.is-compact, [data-cols='4']) > div { + display: grid; + grid-template-columns: 6rem minmax(0, 1fr); + column-gap: var(--ds-space-sm); + align-items: baseline; + padding: var(--ds-space-sm) 0; + border-inline-start: 0; + } + .ds-figures:not(.is-compact, [data-cols='4']) > div + div { + padding-inline-start: 0; + border-inline-start: 0; + border-top: 1px solid var(--ds-line-soft); + } + .ds-figures:not(.is-compact, [data-cols='4']) dd:not(.ds-figure-meta) { + grid-column: 1; + grid-row: 1 / span 2; + font-size: 1.5rem; + } + .ds-figures:not(.is-compact, [data-cols='4']) dt, + .ds-figures:not(.is-compact, [data-cols='4']) .ds-figure-meta { + grid-column: 2; + } + .ds-figures:not(.is-compact, [data-cols='4']) .ds-figure-unit { + display: block; + margin: 0; + font-size: var(--sl-text-xs); + } +} +/* Inside a card: smaller numbers, no closing rule, and the card's own inset. */ +.ds-figures.is-compact { + margin-top: var(--ds-space-lg); + border-top-color: var(--ds-line-soft); + border-bottom: 0; +} +.ds-figures.is-compact > div { + padding-block: var(--ds-space-sm) 0; + padding-inline-end: var(--ds-space-sm); +} +.ds-figures.is-compact > div + div { + padding-inline-start: var(--ds-space-sm); +} +.ds-figures.is-compact dt { + font-size: var(--sl-text-xs); + color: var(--ds-text-3); +} +.ds-figures.is-compact dd { + font-size: 1.125rem; +} + +/* A card in a grid: a name, one line of identity under it, then its body. + The head has no rule under it; spacing separates it. */ +.ds-card { + display: flex; + flex-direction: column; + min-width: 0; + margin: 0 !important; + padding: var(--ds-space-md) var(--ds-card-pad) var(--ds-card-pad); + border: 1px solid var(--ds-line); + border-radius: var(--ds-cut-control); + corner-shape: bevel; + background: var(--ds-surface); +} +.ds-card > * { + margin-top: 0; +} +.ds-card-head { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: var(--ds-space-2xs); + margin: 0 0 var(--ds-space-md) !important; +} +.ds-card-name { + display: flex; + align-items: center; + gap: var(--ds-space-xs); + margin: 0 !important; + font-size: var(--sl-text-base); + font-weight: 500; + line-height: 1.3; + letter-spacing: -0.01em; + color: var(--ds-text); +} +.ds-card-meta { + margin: 0 !important; + overflow: hidden; + font-family: var(--ds-font-mono); + font-size: var(--sl-text-2xs); + line-height: 1.4; + color: var(--ds-text-3); + white-space: nowrap; + text-overflow: ellipsis; +} + +/* Bars: label, track, value, on one grid so every row in a card — and every + card in a grid — starts its track at the same place. */ +.ds-bars { + display: grid; + grid-template-columns: var(--ds-bars-label, 7rem) minmax(0, 1fr) auto; + align-items: center; + gap: var(--ds-space-xs) var(--ds-space-sm); + margin: 0; +} +.ds-bars > * { + margin: 0 !important; +} +.ds-bars-label { + overflow: hidden; + font-size: var(--sl-text-sm); + color: var(--ds-text-2); + white-space: nowrap; + text-overflow: ellipsis; +} +.ds-bars-value { + min-width: 3.25rem; + font-family: var(--ds-font-mono); + font-size: var(--sl-text-xs); + font-variant-numeric: tabular-nums; + text-align: end; + white-space: nowrap; + color: var(--ds-text); +} +.ds-bars-value small { + font-size: 1em; + color: var(--ds-text-3); +} +.ds-track { + display: flex; + height: 0.5rem; + overflow: hidden; + background: var(--ds-control); + box-shadow: inset 0 0 0 1px var(--ds-line-soft); +} +.ds-track > * { + height: 100%; +} + +/* A split: two things read side by side once the column has room for both + at a readable width — a passage and the figures it introduces, or two + independent paragraphs. Each keeps its own reading order; nothing flows from + one column into the other. Below the breakpoint they simply stack. */ +@media (min-width: 100rem) { + .ds-split { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + align-items: start; + gap: var(--ds-space-lg) var(--ds-space-xl); + } + .ds-split > * { + margin-top: 0 !important; + } + .ds-split > :is(p, ul, ol) + :is(p, ul, ol) { + max-width: var(--ds-measure); + } + /* In the second column a row of four figures folds to two by two. */ + .ds-split .ds-figures[data-cols='4'] { + --ds-figures-cols: 2; + } + .ds-split .ds-figures[data-cols='4'] > div:nth-child(odd) { + padding-inline-start: 0; + border-inline-start: 0; + } + .ds-split .ds-figures[data-cols='4'] > div:nth-child(n + 3) { + border-top: 1px solid var(--ds-line-soft); + } +} + +/* A figure caption. */ +.ds-caption { + margin: var(--ds-space-sm) 0 0; + max-width: var(--ds-measure); + color: var(--ds-text-3); + font-size: var(--sl-text-sm); + line-height: 1.55; +} + +/* ---- Motion ------------------------------------------------------------------ */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + transition-duration: 0s !important; + animation-duration: 0s !important; + animation-iteration-count: 1 !important; + scroll-behavior: auto !important; + } +} + +/* ---- Print ---------------------------------------------------------------- */ +@media print { + .ds-panel, + .ds-frame { + box-shadow: none; + } +} From 1c18bd111670d543f4b86a789b14adb9a4dea6a3 Mon Sep 17 00:00:00 2001 From: Alex Swearingen Date: Tue, 29 Sep 2026 17:15:35 -0500 Subject: [PATCH 2/2] Move the docs to dataflowbench.slopcop.com and prepare them for search Point the production site, CNAME and README at dataflowbench.slopcop.com, and name SlopCop as the benchmark's publisher on the landing page and in its description. GitHub links stay on the BrokkAi organisation and Bifrost links on bifrost.brokk.ai. For search and link previews: a robots.txt that names the generated sitemap, a social card image with its Open Graph and Twitter tags, WebSite structured data naming the site and its publisher, and a route middleware that names the release in the search description of each archived snapshot page, so no two pages share one. The frozen pages themselves are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) --- README.md | 2 +- docs/astro.config.mjs | 36 ++++++++++++++++++++++- docs/public/CNAME | 2 +- docs/public/og-image.png | Bin 0 -> 67109 bytes docs/src/components/LandingResults.astro | 2 +- docs/src/content/docs/index.mdx | 2 +- docs/src/pages/robots.txt.ts | 8 +++++ docs/src/routeData.ts | 26 ++++++++++++++++ 8 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 docs/public/og-image.png create mode 100644 docs/src/pages/robots.txt.ts create mode 100644 docs/src/routeData.ts diff --git a/README.md b/README.md index e724c34cb..6f272be6b 100644 --- a/README.md +++ b/README.md @@ -479,7 +479,7 @@ the `--check` mode. ## Website -The public site at is an Astro +The public site at is an Astro Starlight package in `docs/`; every number it shows is rendered from the generated `results/results.json`, never hand-authored. Develop locally with: diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 27d2ad17d..a184e4dac 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -59,8 +59,15 @@ const previousLabel = previousSnapshot : ''; // Production deployment target for GitHub Pages. +const site = 'https://dataflowbench.slopcop.com'; +const base = '/'; +/** The published root, for the absolute URLs social cards need. */ +const siteRoot = new URL(base.replace(/\/?$/, '/'), site); +const socialCard = new URL('og-image.png', siteRoot).href; + export default defineConfig({ - site: 'https://dataflowbench.brokk.ai', + site, + base, markdown: { // `## Heading {#legacy-id}` — explicit anchors for headings whose ids are // citable and must not follow the auto-slugger. See the plugin's comment. @@ -82,6 +89,33 @@ export default defineConfig({ // then this site's accent, palette and layout on top of it. customCss: ['./src/styles/foundation.css', './src/styles/custom.css'], logo: { src: './src/assets/mark.svg', alt: '' }, + head: [ + { tag: 'meta', attrs: { property: 'og:image', content: socialCard } }, + { tag: 'meta', attrs: { property: 'og:image:width', content: '1200' } }, + { tag: 'meta', attrs: { property: 'og:image:height', content: '630' } }, + { + tag: 'meta', + attrs: { + property: 'og:image:alt', + content: 'DataFlowBench: how well do static analyzers follow data flow?', + }, + }, + { tag: 'meta', attrs: { name: 'twitter:image', content: socialCard } }, + { + tag: 'script', + attrs: { type: 'application/ld+json' }, + content: JSON.stringify({ + '@context': 'https://schema.org', + '@type': 'WebSite', + name: 'DataFlowBench', + url: siteRoot.href, + publisher: { '@type': 'Organization', name: 'SlopCop', url: 'https://slopcop.com' }, + }), + }, + ], + // Archived snapshot pages get their release named in their search + // description, so no two pages share one. See src/routeData.ts. + routeMiddleware: './src/routeData.ts', expressiveCode: { styleOverrides: { borderRadius: '0px', diff --git a/docs/public/CNAME b/docs/public/CNAME index af10f63da..2b2629ea3 100644 --- a/docs/public/CNAME +++ b/docs/public/CNAME @@ -1 +1 @@ -dataflowbench.brokk.ai +dataflowbench.slopcop.com diff --git a/docs/public/og-image.png b/docs/public/og-image.png new file mode 100644 index 0000000000000000000000000000000000000000..143d8b8a0c330d01cc4080c4cd5916492d59c33a GIT binary patch literal 67109 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

    - DataFlowBench is BrokkAi's own + DataFlowBench is SlopCop's own benchmark for semantic data flow: we build it to measure where Bifrost stands against {analyzerCount - 1} reference analyzers, and to keep diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx index d0eb7f1f6..4dd4ce9a4 100644 --- a/docs/src/content/docs/index.mdx +++ b/docs/src/content/docs/index.mdx @@ -1,6 +1,6 @@ --- title: Current overview of DataFlowBench -description: BrokkAi's benchmark for data-flow analysis — Bifrost measured against reference analyzers, published exclusively from immutable freeze evidence. +description: SlopCop's benchmark for data-flow analysis — Bifrost measured against reference analyzers, published exclusively from immutable freeze evidence. tableOfContents: false --- diff --git a/docs/src/pages/robots.txt.ts b/docs/src/pages/robots.txt.ts new file mode 100644 index 000000000..a971be8d9 --- /dev/null +++ b/docs/src/pages/robots.txt.ts @@ -0,0 +1,8 @@ +import type { APIRoute } from 'astro'; + +// Every page is public; the sitemap Starlight generates lists them. Built +// from the configured site so a preview build points at its own sitemap. +export const GET: APIRoute = ({ site }) => { + const sitemap = new URL(`${import.meta.env.BASE_URL.replace(/\/?$/, '/')}sitemap-index.xml`, site); + return new Response(`User-agent: *\nAllow: /\n\nSitemap: ${sitemap.href}\n`); +}; diff --git a/docs/src/routeData.ts b/docs/src/routeData.ts new file mode 100644 index 000000000..66618138d --- /dev/null +++ b/docs/src/routeData.ts @@ -0,0 +1,26 @@ +/// + +import { defineRouteMiddleware } from '@astrojs/starlight/route-data'; + +// Archived snapshot pages were written before each page named its release in +// its description, so an early freeze's analyzers page shares one sentence +// with three others and search results cannot tell them apart. The frozen +// pages stay as they were published; only the description their +// carries gains the release it belongs to. +const SNAPSHOT_PAGE = /^snapshots\/v(\d+)-(\d+)-(\d+)(?:\/|$)/; +const DESCRIPTION_TAGS = new Set(['description', 'og:description']); + +export const onRequest = defineRouteMiddleware((context) => { + const route = context.locals.starlightRoute; + const match = SNAPSHOT_PAGE.exec(route.entry.id); + const description = route.entry.data.description; + if (!match || !description) return; + const version = `v${match[1]}.${match[2]}.${match[3]}`; + if (description.includes(version)) return; + for (const tag of route.head) { + const name = tag.attrs?.name ?? tag.attrs?.property; + if (tag.tag === 'meta' && typeof name === 'string' && DESCRIPTION_TAGS.has(name)) { + tag.attrs!.content = `Snapshot ${version}: ${description}`; + } + } +});