@opendata-ai/openchart-vanilla 8.4.1 → 8.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -0
- package/dist/chunk-2OJZBMXM.js +377 -0
- package/dist/chunk-2OJZBMXM.js.map +1 -0
- package/dist/{chunk-5A2NLYUW.js → chunk-5KV3ZBBL.js} +26 -376
- package/dist/chunk-5KV3ZBBL.js.map +1 -0
- package/dist/chunk-NG7CVCI2.js +58 -0
- package/dist/chunk-NG7CVCI2.js.map +1 -0
- package/dist/{chunk-3UIUANPI.js → chunk-UAK6HWOC.js} +2 -2
- package/dist/chunk-WOLX5EZT.js +3577 -0
- package/dist/chunk-WOLX5EZT.js.map +1 -0
- package/dist/{chunk-KANNWAEA.js → chunk-XGII4ZRT.js} +4 -58
- package/dist/chunk-XGII4ZRT.js.map +1 -0
- package/dist/graph-3d/index.d.ts +272 -0
- package/dist/graph-3d/index.js +1556 -0
- package/dist/graph-3d/index.js.map +1 -0
- package/dist/index.d.ts +4 -252
- package/dist/index.js +24 -3468
- package/dist/index.js.map +1 -1
- package/dist/renderer-registry-B5tW6yAZ.d.ts +376 -0
- package/dist/static.js +3 -2
- package/dist/static.js.map +1 -1
- package/dist/story/index.js +8 -5
- package/dist/story/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +26 -5
- package/dist/chunk-5A2NLYUW.js.map +0 -1
- package/dist/chunk-KANNWAEA.js.map +0 -1
- /package/dist/{chunk-3UIUANPI.js.map → chunk-UAK6HWOC.js.map} +0 -0
package/dist/story/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/story/create-chart-story.ts","../../src/story/crossfade.ts","../../src/story/resolve-camera-target.ts","../../src/story/progress-math.ts","../../src/story/scroll-driver.ts","../../src/story/story-camera.ts"],"sourcesContent":["/**\n * `createChartStory`: a base spec plus an ordered list of deep-partial\n * patch steps, driven by scroll (built-in `ScrollDriver`) or directly via\n * `goTo(n)`.\n *\n * Animation-clock ownership (decision baked 2026-07-11, see\n * plans/design-evolution/11-scrollytelling.md): the data-update-transitions\n * driver in `../transition.ts` owns mark morphing; the ported tween in\n * `./tween.ts` owns camera and any scalar story-level animation. They never\n * animate the same property. `goTo` calls `ChartInstance.update()`, which\n * internally decides morph-vs-instant-swap via `canTransition`; this module\n * predicts that decision ahead of time (`canTransitionSpecShape`) only to\n * decide whether to arm the crossfade fallback, and separately drives the\n * camera tween. It does not bridge the two clocks.\n */\n\nimport type { DataRow, GeoMapSpec } from '@opendata-ai/openchart-core';\nimport { deepMergeSpec, isGeoMapSpec } from '@opendata-ai/openchart-core';\nimport { AUTO_CANVAS_THRESHOLD } from '@opendata-ai/openchart-engine';\nimport { createGeoMap, type GeoMapInstance, type GeoMapMountOptions } from '../map-mount';\nimport { type ChartInstance, createChart, type MountOptions } from '../mount';\nimport {\n CANVAS_DEFAULT_UPDATE_MAX_MARKS,\n canTransitionSpecShape,\n DEFAULT_UPDATE_MAX_MARKS,\n} from '../transition';\nimport {\n type Camera,\n camerasClose,\n dampCamera,\n FULL_VIEW,\n fitTarget,\n interpolateCamera,\n scrubCamera,\n type ViewBoxSize,\n} from './camera-math';\nimport { crossfadeUpdate } from './crossfade';\nimport { isDataCameraTarget, resolveCameraTarget } from './resolve-camera-target';\nimport { createScrollDriver } from './scroll-driver';\nimport { applyStoryCamera, readViewBox } from './story-camera';\nimport { createTween, easingFns, storyMotion } from './tween';\nimport type {\n ChartStoryInstance,\n ChartStoryOptions,\n StorySpec,\n StorySpecPatch,\n StoryStep,\n} from './types';\n\nfunction prefersReducedMotion(): boolean {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false;\n try {\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n } catch {\n return false;\n }\n}\n\n/** Merge a step's `highlight` sugar into its `spec` patch as `encoding.color.highlight`. */\nfunction stepToPatch(step: StoryStep): StorySpecPatch {\n let patch = step.spec ?? {};\n if ('highlight' in step) {\n // `highlight: null` clears a highlight set by an earlier step. It must map\n // to `[]` (not `undefined`): deep-merge skips `undefined`, so `?? undefined`\n // would leave the prior step's highlight in place. An empty array is a real\n // value that replaces it, and the engine reads `highlight.length > 0` as\n // active, so `[]` reads as \"no highlight.\"\n const highlight = step.highlight === null ? [] : (step.highlight ?? undefined);\n patch = deepMergeSpec(patch, {\n encoding: { color: { highlight } },\n });\n }\n return patch;\n}\n\n/** Apply patches 0..index (inclusive) onto the base spec, cumulatively. */\nfunction resolveSpecAtStep(base: StorySpec, steps: StoryStep[], index: number): StorySpec {\n let spec: StorySpec = base;\n for (let i = 0; i <= index && i < steps.length; i++) {\n spec = deepMergeSpec(spec, stepToPatch(steps[i]!));\n }\n return spec;\n}\n\n/**\n * Predict whether a morphing update would clear the runtime mark cap\n * (`canTransition` check 8). The runtime veto fires after `render()` has\n * already swapped, so a step that predicts \"morph\" but exceeds the cap lands\n * as a hard snap; predicting it here keeps the crossfade fallback instead.\n *\n * Data rows ≈ marks only for point and beeswarm specs, so the estimate is\n * scoped to those marks. Point marks auto-promote to canvas above\n * AUTO_CANVAS_THRESHOLD (where the cap is CANVAS_DEFAULT_UPDATE_MAX_MARKS)\n * unless the host forces `renderer: 'svg'`; beeswarm never renders on canvas.\n */\nfunction morphWithinMarkCap(\n prevSpec: StorySpec,\n nextSpec: StorySpec,\n renderer: 'auto' | 'svg' | 'canvas',\n): boolean {\n const mark = (nextSpec as { mark?: string | { type?: string } }).mark;\n const markType = typeof mark === 'string' ? mark : mark?.type;\n if (markType !== 'point' && markType !== 'beeswarm') return true;\n\n const rowCount = (s: StorySpec): number => {\n const data = (s as { data?: unknown }).data;\n return Array.isArray(data) ? data.length : 0;\n };\n const count = Math.max(rowCount(prevSpec), rowCount(nextSpec));\n\n const animation = (nextSpec as { animation?: unknown }).animation;\n const maxMarks =\n typeof animation === 'object' && animation !== null\n ? (animation as { update?: { maxMarks?: number } }).update?.maxMarks\n : undefined;\n\n const canvas =\n markType === 'point' &&\n renderer !== 'svg' &&\n (renderer === 'canvas' || rowCount(nextSpec) > AUTO_CANVAS_THRESHOLD);\n const cap = maxMarks ?? (canvas ? CANVAS_DEFAULT_UPDATE_MAX_MARKS : DEFAULT_UPDATE_MAX_MARKS);\n return count <= cap;\n}\n\n/**\n * Create a scrollytelling story bound to a container. Mounts a chart with\n * the base spec, then drives it through cumulative patch steps as the\n * reader scrolls (or via `goTo`).\n *\n * `editMode` (any of `onEdit`/`onSelect`/`onDeselect`/`onTextEdit` on\n * `mountOptions`) and an active story are mutually exclusive in v1: passing\n * both warns once and disables the story's own drive loop, matching the\n * seriesSearch/edit-mode precedent in `mount.ts`.\n */\nexport function createChartStory<TData extends DataRow = DataRow>(\n container: HTMLElement,\n options: ChartStoryOptions<TData>,\n mountOptions?: MountOptions,\n): ChartStoryInstance {\n const { spec, steps, triggerPosition = 0.4, cameraMode = 'step' } = options;\n // The story machinery is spec-shape agnostic (it only deep-merges patches\n // and hands specs to `update()`, which accepts the non-generic union).\n // Widen once here so the internal helpers don't have to thread `TData`\n // through a discriminated union that TS can't narrow across variants.\n const baseSpec = spec as StorySpec;\n const isMap = isGeoMapSpec(baseSpec as unknown as Record<string, unknown>);\n\n if (isMap && cameraMode === 'scrub') {\n console.warn(\n '[openchart] cameraMode: \"scrub\" is not supported for map stories; falling back to \"step\" mode. Map camera is driven via geo.focus patches in the spec.',\n );\n }\n\n if (isMap && steps.some((s) => s.camera)) {\n console.warn(\n '[openchart] step.camera is ignored for map stories. Drive the map camera via geo.focus patches in the spec instead.',\n );\n }\n\n const editModeRequested = !!(\n mountOptions?.onEdit ||\n mountOptions?.onSelect ||\n mountOptions?.onDeselect ||\n mountOptions?.onTextEdit\n );\n if (editModeRequested) {\n console.warn(\n '[openchart] a chart story and edit mode are mutually exclusive; the story will not drive updates while editing callbacks are active.',\n );\n }\n\n let currentStep = -1;\n let destroyed = false;\n\n const initialSpec = resolveSpecAtStep(baseSpec, steps, 0);\n\n // Branch: map vs chart mount\n let instance: ChartInstance | GeoMapInstance;\n if (isMap) {\n // Not a spread: GeoMapMountOptions callback signatures differ from MountOptions.\n // When adding fields to GeoMapMountOptions, check if they should transfer here.\n const mapOpts: GeoMapMountOptions = {\n theme: mountOptions?.theme,\n darkMode: mountOptions?.darkMode,\n watermark: mountOptions?.watermark,\n responsive: mountOptions?.responsive,\n };\n instance = createGeoMap(container, initialSpec as GeoMapSpec, mapOpts);\n } else {\n instance = createChart(container, initialSpec as Exclude<StorySpec, GeoMapSpec>, mountOptions);\n }\n\n // Chart camera infra (not used for maps)\n const cameraTween = isMap\n ? null\n : createTween<Camera>({\n initial: { cx: 0, cy: 0, k: 1 },\n lerp: interpolateCamera,\n duration: storyMotion.camera,\n ease: easingFns.easeInOutCubic,\n onFrame: (camera) => {\n const vb = readViewBox(container);\n if (vb) applyStoryCamera(container, camera, vb);\n },\n });\n\n /** Fit a step's camera (data-coordinate or raw target, or full view) into the viewBox. */\n function fittedCameraForStep(step: StoryStep | undefined, vb: ViewBoxSize): Camera {\n if (!step?.camera) return fitTarget(FULL_VIEW(vb), vb);\n const target = isDataCameraTarget(step.camera)\n ? resolveCameraTarget((instance as ChartInstance).layout, step.camera)\n : step.camera;\n return fitTarget(target, vb);\n }\n\n // ---- Scrub-mode camera: self-stopping damping loop toward a live target ---\n // Step mode eases discretely on step change (the tween above). Scrub mode\n // reads continuous `stepProgress` and dwells on the active target through a\n // hold zone, transitioning in the tail of the span. The two modes never run\n // at once; the settle loop and the step tween are mutually exclusive drivers.\n let scrubCurrent: Camera | null = null;\n let scrubTarget: Camera | null = null;\n let settleRaf = 0;\n let settling = false;\n\n // Memoized per-step fitted cameras for scrub mode. The fit depends only on\n // the current layout (rebuilt on instance.update) and the viewBox size, so\n // recomputing it on every scroll frame is wasted work. Cache keyed on the\n // layout reference plus the viewBox dimensions; invalidate when either moves.\n let fittedCache: Camera[] | null = null;\n let fittedCacheLayout: unknown = null;\n let fittedCacheVb: ViewBoxSize | null = null;\n\n function fittedCamerasForScrub(vb: ViewBoxSize): Camera[] {\n if (\n fittedCache &&\n fittedCacheLayout === (instance as ChartInstance).layout &&\n fittedCacheVb &&\n fittedCacheVb.width === vb.width &&\n fittedCacheVb.height === vb.height\n ) {\n return fittedCache;\n }\n fittedCache = steps.map((step) => fittedCameraForStep(step, vb));\n fittedCacheLayout = (instance as ChartInstance).layout;\n fittedCacheVb = vb;\n return fittedCache;\n }\n\n function stopSettle(): void {\n if (settling) {\n cancelAnimationFrame(settleRaf);\n settling = false;\n }\n }\n\n function startSettle(): void {\n if (settling) return;\n settling = true;\n let last = performance.now();\n const tick = (now: number): void => {\n const target = scrubTarget;\n const current = scrubCurrent;\n const vb = readViewBox(container);\n if (!target || !current || !vb) {\n settling = false;\n return;\n }\n const dt = Math.min(now - last, 64);\n last = now;\n const next = dampCamera(current, target, storyMotion.cameraTau, dt);\n scrubCurrent = next;\n applyStoryCamera(container, next, vb);\n if (camerasClose(next, target)) {\n scrubCurrent = target;\n applyStoryCamera(container, target, vb);\n settling = false;\n return;\n }\n settleRaf = requestAnimationFrame(tick);\n };\n settleRaf = requestAnimationFrame(tick);\n }\n\n /** Step-mode camera: retargetable eased tween on step change. */\n function applyCameraForStep(step: StoryStep | undefined, snap: boolean): void {\n if (!cameraTween) return;\n const vb = readViewBox(container);\n if (!vb) return;\n cameraTween.to(fittedCameraForStep(step, vb), { snap: snap || prefersReducedMotion() });\n }\n\n /** Scrub-mode camera: continuous, driven by the scroll frame's stepProgress. */\n function applyScrubCamera(frame: { step: number; stepProgress: number }): void {\n const vb = readViewBox(container);\n if (!vb) return;\n const fitted = fittedCamerasForScrub(vb);\n if (fitted.length === 0) return;\n\n if (prefersReducedMotion()) {\n const clamped = Math.min(Math.max(frame.step, 0), fitted.length - 1);\n stopSettle();\n scrubCurrent = fitted[clamped]!;\n applyStoryCamera(container, scrubCurrent, vb);\n return;\n }\n\n scrubTarget = scrubCamera(\n frame.step,\n frame.stepProgress,\n fitted,\n 0.65,\n easingFns.easeInOutCubic,\n );\n if (scrubCurrent === null) scrubCurrent = scrubTarget;\n startSettle();\n }\n\n function goTo(index: number): void {\n if (destroyed) return;\n const clamped = Math.max(0, Math.min(index, steps.length - 1));\n if (clamped === currentStep) return;\n\n const isFirst = currentStep === -1;\n currentStep = clamped;\n\n const nextSpec = resolveSpecAtStep(baseSpec, steps, clamped);\n\n if (isMap) {\n // Maps always apply directly: update handles fill + camera via geo.focus\n (instance as GeoMapInstance).update(nextSpec as GeoMapSpec);\n return;\n }\n\n const prevSpec = isFirst ? null : resolveSpecAtStep(baseSpec, steps, clamped - 1);\n\n const willLikelyMorph =\n !isFirst &&\n !editModeRequested &&\n prevSpec !== null &&\n canTransitionSpecShape(prevSpec, nextSpec) &&\n morphWithinMarkCap(prevSpec, nextSpec, mountOptions?.renderer ?? 'auto');\n\n const applyUpdate = () =>\n (instance as ChartInstance).update(nextSpec as Exclude<StorySpec, GeoMapSpec>);\n\n if (isFirst || willLikelyMorph || editModeRequested) {\n applyUpdate();\n } else {\n // Outside the morph gate (re-encode, type change, etc.): no step may\n // visibly snap, so crossfade the whole chart instead.\n crossfadeUpdate(container, applyUpdate, { reducedMotion: prefersReducedMotion() });\n }\n\n // Step mode drives the camera on each discrete step change. Scrub mode\n // drives it continuously from the scroll frame instead (see the scroll\n // subscription below), so it skips the discrete step tween here.\n if (cameraMode === 'step') applyCameraForStep(steps[clamped], isFirst);\n }\n\n const scrollDriver = editModeRequested ? null : createScrollDriver({ triggerPosition });\n let unsubscribeScroll: (() => void) | null = null;\n if (scrollDriver) {\n unsubscribeScroll = scrollDriver.progress.subscribe((frame) => {\n if (frame.step < 0) return;\n // Discrete spec steps always advance at step boundaries; the v1 non-goal\n // is continuous DATA morph scrubbing, not camera scrub. In scrub mode the\n // camera additionally follows the continuous frame.\n goTo(frame.step);\n if (!isMap && cameraMode === 'scrub') applyScrubCamera(frame);\n });\n }\n\n return {\n goTo,\n registerStep(index, el) {\n scrollDriver?.registerStep(index, el);\n return (nextEl: HTMLElement | null) => scrollDriver?.registerStep(index, nextEl);\n },\n setContainer(el) {\n scrollDriver?.setContainer(el);\n },\n get currentStep() {\n return currentStep;\n },\n get totalSteps() {\n return steps.length;\n },\n destroy() {\n if (destroyed) return;\n destroyed = true;\n unsubscribeScroll?.();\n scrollDriver?.destroy();\n cameraTween?.cancel();\n stopSettle();\n instance.destroy();\n },\n };\n}\n","/**\n * Whole-chart crossfade fallback for story steps whose spec diff falls\n * outside the data-update-transitions morph gate (`canTransition` in\n * `../transition.ts`): re-encodes, type changes, and anything else that\n * would otherwise instant-snap. No step in a story may visibly snap, so\n * this ghosts the current rendered SVG over the container, lets\n * `ChartInstance.update()` perform its normal instant swap underneath,\n * then fades the ghost out to reveal the new state.\n *\n * This is deliberately NOT the mark-morphing transition driver: it never\n * interpolates geometry, only opacity. See the animation-clock ownership\n * note in the scrollytelling plan -- the transitions driver owns mark\n * morphing, this owns the pixel-level fallback when that driver declines.\n */\n\nimport { storyMotion } from './tween';\n\nexport interface CrossfadeOptions {\n /** Fade duration in ms. Default `storyMotion.crossfade`. */\n duration?: number;\n /** Skip the fade and swap instantly (reduced motion). */\n reducedMotion?: boolean;\n}\n\n/**\n * Ghost the container's current SVG, run `applyUpdate` (which mutates the\n * container to its next state), then fade the ghost out.\n */\nexport function crossfadeUpdate(\n container: HTMLElement,\n applyUpdate: () => void,\n options: CrossfadeOptions = {},\n): void {\n const duration = options.duration ?? storyMotion.crossfade;\n const svg = container.querySelector('svg');\n\n if (options.reducedMotion || !svg || duration <= 0) {\n applyUpdate();\n return;\n }\n\n const ghost = svg.cloneNode(true) as SVGElement;\n ghost.setAttribute('aria-hidden', 'true');\n ghost.style.position = 'absolute';\n ghost.style.inset = '0';\n ghost.style.width = '100%';\n ghost.style.height = '100%';\n ghost.style.pointerEvents = 'none';\n ghost.style.transition = `opacity ${duration}ms ease-out`;\n ghost.style.opacity = '1';\n\n const priorPosition = container.style.position;\n container.style.position = priorPosition || 'relative';\n container.appendChild(ghost);\n\n applyUpdate();\n\n // Force layout so the transition starts from opacity: 1 before dropping.\n void ghost.getBoundingClientRect();\n ghost.style.opacity = '0';\n\n const cleanup = () => {\n ghost.remove();\n if (!priorPosition) container.style.position = '';\n };\n ghost.addEventListener('transitionend', cleanup, { once: true });\n // Safety net in case transitionend never fires (e.g. element removed by\n // an intervening render before the transition completes).\n setTimeout(cleanup, duration + 100);\n}\n","/**\n * Resolves data-coordinate camera targets (`{ x: [a, b], y: [c, d] }`) to\n * viewBox-space `CameraTarget` rects using the compiled chart's axis ticks.\n *\n * There is no raw d3 scale on `ChartLayout` (by design -- the compiled\n * layout is the public surface, not engine internals), so resolution works\n * off `AxisTick[]`, which pairs each tick's raw data `value` with its pixel\n * `position`:\n * - Exact tick match (ordinal/categorical values, or a quantitative value\n * that happens to land on a tick): use that tick's position directly.\n * - Otherwise linear interpolation between the two nearest ticks by data\n * value. Works for quantitative and temporal axes (temporal values are\n * compared as timestamps). Ordinal axes with no exact match have no\n * well-ordered \"between\" and fall back to the nearest tick.\n */\n\nimport type { AxisLayout, AxisTick, ChartLayout } from '@opendata-ai/openchart-core';\nimport type { CameraTarget } from './camera-math';\nimport type { StoryDataCameraTarget } from './types';\n\nconst DEFAULT_PADDING = 24;\n\n/** Coerce a tick value (string, number, Date, ISO string) to a comparable number. */\nfunction toComparable(value: unknown): number | null {\n if (typeof value === 'number') return value;\n if (value instanceof Date) return value.getTime();\n if (typeof value === 'string') {\n const asDate = Date.parse(value);\n if (!Number.isNaN(asDate)) return asDate;\n }\n return null;\n}\n\n/** Resolve one data value to a pixel position along an axis, or null if unresolvable. */\nfunction resolveOnAxis(axis: AxisLayout, target: unknown): number | null {\n const ticks = axis.ticks;\n if (ticks.length === 0) return null;\n\n const exact = ticks.find((t) => t.value === target);\n if (exact) return exact.position;\n\n const targetNum = toComparable(target);\n if (targetNum === null) return null;\n\n // Ticks with comparable numeric values, sorted by data value.\n const numeric: Array<{ tick: AxisTick; value: number }> = [];\n for (const tick of ticks) {\n const v = toComparable(tick.value);\n if (v !== null) numeric.push({ tick, value: v });\n }\n if (numeric.length === 0) return null;\n numeric.sort((a, b) => a.value - b.value);\n\n if (targetNum <= numeric[0]!.value) return numeric[0]!.tick.position;\n const last = numeric.at(-1)!;\n if (targetNum >= last.value) return last.tick.position;\n\n for (let i = 0; i < numeric.length - 1; i++) {\n const lo = numeric[i]!;\n const hi = numeric[i + 1]!;\n if (targetNum >= lo.value && targetNum <= hi.value) {\n const span = hi.value - lo.value;\n const t = span === 0 ? 0 : (targetNum - lo.value) / span;\n return lo.tick.position + t * (hi.tick.position - lo.tick.position);\n }\n }\n return null;\n}\n\n/**\n * Resolve a data-coordinate camera target to viewBox space. Falls back to\n * the full chart area (padded) for axes that are missing or unresolvable,\n * so a malformed target degrades to \"camera does nothing\" rather than\n * throwing mid-scroll.\n */\nexport function resolveCameraTarget(\n layout: ChartLayout,\n target: StoryDataCameraTarget,\n): CameraTarget {\n const area = layout.area;\n const padding = target.padding ?? DEFAULT_PADDING;\n\n let x1 = area.x;\n let x2 = area.x + area.width;\n if (target.x && layout.axes.x) {\n const a = resolveOnAxis(layout.axes.x, target.x[0]);\n const b = resolveOnAxis(layout.axes.x, target.x[1]);\n if (a !== null && b !== null) {\n x1 = Math.min(a, b);\n x2 = Math.max(a, b);\n }\n }\n\n let y1 = area.y;\n let y2 = area.y + area.height;\n if (target.y && layout.axes.y) {\n const a = resolveOnAxis(layout.axes.y, target.y[0]);\n const b = resolveOnAxis(layout.axes.y, target.y[1]);\n if (a !== null && b !== null) {\n y1 = Math.min(a, b);\n y2 = Math.max(a, b);\n }\n }\n\n return {\n x: x1,\n y: y1,\n width: Math.max(x2 - x1, 1),\n height: Math.max(y2 - y1, 1),\n padding,\n };\n}\n\n/** Type guard: a camera step given in data coordinates vs. raw viewBox `CameraTarget`. */\nexport function isDataCameraTarget(\n value: StoryDataCameraTarget | CameraTarget,\n): value is StoryDataCameraTarget {\n return !('width' in value) && !('height' in value);\n}\n","import { clamp01 } from './tween';\n\n/**\n * Pure math for continuous scrollytelling progress. The `ScrollDriver`\n * measures DOM rects and feeds them here. Ported from\n * opendata/shared/lib/scrolly/progress-math.ts.\n */\n\nexport interface ScrollyFrame {\n /** Geometric active step; -1 while the trigger line is above step 0 */\n step: number;\n /** 0..1 progress of the trigger line through the active step's span */\n stepProgress: number;\n /** 0..1 traversal from first step top to last step bottom */\n progress: number;\n direction: 'down' | 'up';\n}\n\nexport type ScrollyFrameGeometry = Omit<ScrollyFrame, 'direction'>;\n\n/**\n * Compute the geometric frame from viewport-relative step tops.\n *\n * @param tops viewport-relative step top offsets, ordered by step index\n * @param lastBottom viewport-relative bottom of the final step\n * @param triggerY the trigger line's viewport y (innerHeight * triggerPosition)\n */\nexport function computeProgress(\n tops: number[],\n lastBottom: number,\n triggerY: number,\n): ScrollyFrameGeometry {\n const n = tops.length;\n if (n === 0 || triggerY < tops[0]!) {\n return { step: -1, stepProgress: 0, progress: 0 };\n }\n\n let step = n - 1;\n for (let i = 0; i < n - 1; i++) {\n if (triggerY < tops[i + 1]!) {\n step = i;\n break;\n }\n }\n\n const stepTop = tops[step]!;\n const spanEnd = step < n - 1 ? tops[step + 1]! : lastBottom;\n const span = Math.max(spanEnd - stepTop, 1);\n const firstTop = tops[0]!;\n\n return {\n step,\n stepProgress: clamp01((triggerY - stepTop) / span),\n progress: clamp01((triggerY - firstTop) / Math.max(lastBottom - firstTop, 1)),\n };\n}\n\n/**\n * Reduced-motion variant: no scrubbing. `stepProgress` pins to 0 and overall\n * progress quantizes to the step's start fraction, so consumers see discrete\n * snaps only.\n */\nexport function quantizeFrame(\n frame: ScrollyFrameGeometry,\n totalSteps: number,\n): ScrollyFrameGeometry {\n if (frame.step < 0 || totalSteps <= 0) {\n return { step: frame.step, stepProgress: 0, progress: 0 };\n }\n return {\n step: frame.step,\n stepProgress: 0,\n progress: clamp01(frame.step / totalSteps),\n };\n}\n\nexport function framesEqual(a: ScrollyFrame | null, b: ScrollyFrame): boolean {\n return (\n a !== null &&\n a.step === b.step &&\n a.stepProgress === b.stepProgress &&\n a.progress === b.progress &&\n a.direction === b.direction\n );\n}\n","/**\n * Framework-agnostic scroll-step driver: element registry + rAF-throttled\n * scroll handler + subscription store. Ported from the React\n * `use-scroll-steps` hook in opendata/shared; the store design (60fps\n * frames as subscriptions, never framework state) carries over unchanged\n * because vanilla, React, Vue, and Svelte all want the same contract.\n *\n * Measurement is lazy: with zero subscribers the scroll handler does no\n * rect reads.\n */\n\nimport { computeProgress, framesEqual, quantizeFrame, type ScrollyFrame } from './progress-math';\n\nexport interface ScrollyProgressStore {\n /** Latest frame; `{step: -1, ...}` sentinel before any measurement */\n get(): ScrollyFrame;\n /** Emits the current frame immediately on subscribe, then on every change */\n subscribe(cb: (frame: ScrollyFrame) => void): () => void;\n}\n\nexport interface ScrollDriverOptions {\n /** Fraction of viewport height where the trigger line sits. Default 0.4 */\n triggerPosition?: number;\n}\n\nexport interface ScrollDriver {\n /** Register the scrolling container that wraps all steps. */\n setContainer(el: HTMLElement | null): void;\n /** Register a step element by index. Pass `null` to unregister. */\n registerStep(index: number, el: HTMLElement | null): void;\n /** Continuous progress as a subscription store. */\n progress: ScrollyProgressStore;\n /** Scroll a step into view (center). Honors reduced motion. */\n scrollToStep(index: number): void;\n /** Force a re-measurement (e.g. after layout changes). */\n measure(): void;\n /** Tear down scroll/resize listeners and the reduced-motion media query. */\n destroy(): void;\n}\n\nconst SENTINEL_FRAME: ScrollyFrame = {\n step: -1,\n stepProgress: 0,\n progress: 0,\n direction: 'down',\n};\n\n/**\n * Create a scroll-step driver. Framework wrappers (React/Vue/Svelte hooks)\n * are thin adapters over this: they own component lifecycle, this owns the\n * measurement/subscription machinery.\n */\nexport function createScrollDriver(options: ScrollDriverOptions = {}): ScrollDriver {\n const triggerPosition = options.triggerPosition ?? 0.4;\n\n let container: HTMLElement | null = null;\n const steps = new Map<number, HTMLElement>();\n const subscribers = new Set<(frame: ScrollyFrame) => void>();\n\n let frame: ScrollyFrame = SENTINEL_FRAME;\n let direction: 'down' | 'up' = 'down';\n // Direction is tracked from the container's viewport-relative position, NOT\n // window.scrollY: in a host that scrolls an inner container rather than the\n // page (Ladle, modals, dashboard panes, most app shells) window.scrollY is\n // pinned at 0 and would report no movement. The container's rect top moves\n // whenever ANY ancestor scrolls, so it works in both cases.\n let lastTop: number | null = null;\n let reducedMotion = false;\n let ticking = false;\n\n const measureAndEmit = () => {\n if (subscribers.size === 0) return;\n if (!container || steps.size === 0) return;\n if (typeof window === 'undefined') return;\n\n const viewportH = window.innerHeight;\n const containerRect = container.getBoundingClientRect();\n // Skip when the story is more than a viewport away in either direction.\n if (containerRect.bottom < -viewportH || containerRect.top > viewportH * 2) return;\n\n const count = steps.size;\n const tops: number[] = new Array(count);\n let lastBottom = 0;\n for (let i = 0; i < count; i++) {\n const el = steps.get(i);\n if (!el) return; // sparse registration mid-mount; wait for the next tick\n const rect = el.getBoundingClientRect();\n tops[i] = rect.top;\n if (i === count - 1) lastBottom = rect.bottom;\n }\n\n const triggerY = viewportH * triggerPosition;\n let geometry = computeProgress(tops, lastBottom, triggerY);\n if (reducedMotion) {\n geometry = quantizeFrame(geometry, count);\n }\n\n const next: ScrollyFrame = { ...geometry, direction };\n if (framesEqual(frame, next)) return;\n frame = next;\n for (const cb of subscribers) cb(frame);\n };\n\n const onScroll = () => {\n if (ticking) return;\n ticking = true;\n requestAnimationFrame(() => {\n // The container rising up the viewport (top decreasing) means the reader\n // is moving down through the story.\n const currentTop = container?.getBoundingClientRect().top ?? null;\n if (currentTop !== null && lastTop !== null) {\n const delta = lastTop - currentTop;\n if (delta > 5 || delta < -5) {\n direction = delta > 5 ? 'down' : 'up';\n }\n }\n lastTop = currentTop;\n ticking = false;\n measureAndEmit();\n });\n };\n\n let mediaQuery: MediaQueryList | null = null;\n const onReducedMotionChange = (e: MediaQueryListEvent) => {\n reducedMotion = e.matches;\n measureAndEmit();\n };\n\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\n mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');\n reducedMotion = mediaQuery.matches;\n mediaQuery.addEventListener('change', onReducedMotionChange);\n }\n\n if (typeof window !== 'undefined') {\n // Capture phase on the document, not just `window` scroll. Scroll events do\n // not bubble, so a window listener ONLY fires when the page itself scrolls.\n // Any host that scrolls an inner container instead — Ladle (which sets\n // `body { overflow: hidden }`), modals, dashboard panes, most app shells —\n // would never notify the driver and the story sat frozen on step 0.\n // Capturing on the document sees scroll from any ancestor, and the\n // measurement below is already viewport-relative (getBoundingClientRect vs\n // innerHeight), so it needs no other change to work in both layouts.\n document.addEventListener('scroll', onScroll, { passive: true, capture: true });\n window.addEventListener('resize', onScroll, { passive: true });\n }\n\n const progress: ScrollyProgressStore = {\n get: () => frame,\n subscribe: (cb) => {\n // Refresh `frame` from current geometry BEFORE registering `cb`, so the\n // measure pass notifies only existing subscribers. Then hand the new\n // subscriber the current frame exactly once. Registering after the\n // measure avoids the double-invoke that happened when measureAndEmit\n // changed the frame (looping over cb) and the explicit cb(frame) fired again.\n measureAndEmit();\n subscribers.add(cb);\n cb(frame);\n return () => {\n subscribers.delete(cb);\n };\n },\n };\n\n return {\n setContainer(el) {\n container = el;\n // Seed the direction baseline so the first scroll compares against a real\n // position rather than reporting a spurious jump from null.\n lastTop = el?.getBoundingClientRect().top ?? null;\n measureAndEmit();\n },\n registerStep(index, el) {\n if (el) {\n steps.set(index, el);\n } else {\n steps.delete(index);\n }\n measureAndEmit();\n },\n progress,\n scrollToStep(index) {\n const el = steps.get(index);\n if (!el) return;\n el.scrollIntoView({\n block: 'center',\n behavior: reducedMotion ? 'auto' : 'smooth',\n });\n },\n measure() {\n measureAndEmit();\n },\n destroy() {\n if (typeof window !== 'undefined') {\n // `capture: true` must match the addEventListener call or the listener\n // is not removed.\n document.removeEventListener('scroll', onScroll, { capture: true });\n window.removeEventListener('resize', onScroll);\n }\n mediaQuery?.removeEventListener('change', onReducedMotionChange);\n subscribers.clear();\n steps.clear();\n container = null;\n },\n };\n}\n","/**\n * Applies the scrollytelling camera transform to a mounted chart's marks\n * group (`[data-oc-marks-group]`, stamped by svg-renderer.ts). Re-finds the\n * group on every apply since `ChartInstance.update()` tears down and\n * rebuilds the SVG on every render -- the previous group reference goes\n * stale the instant a step patch triggers a re-render.\n *\n * DO NOT USE THIS TO \"ZOOM IN\" ON A CARTESIAN CHART.\n *\n * This is a geometric magnification of the marks group and nothing else. The\n * axes, gridlines, and annotations are siblings of that group, so they do not\n * move: zoom a line chart to 2010-2020 and the lines blow up while the x-axis\n * still reads 2000-2020 and every annotation drifts off its data point. Stroke\n * widths scale with the transform too, so the lines visibly fatten. The chart\n * ends up misrepresenting its own axis.\n *\n * To zoom a cartesian chart, narrow the scale domain instead and let the engine\n * recompile:\n *\n * { spec: { encoding: { x: { scale: { domain: ['2019', ...], clip: true } } } } }\n *\n * That relabels the axis, re-anchors annotations, keeps stroke widths constant,\n * and still morphs (the field identity is unchanged, so `canTransitionSpecShape`\n * passes and the marks FLIP-tween into the new scale).\n *\n * The camera remains useful where there is no axis to contradict -- graph and\n * other non-cartesian views, where magnifying the geometry IS the intent.\n */\n\nimport { type Camera, cameraTransform, FULL_VIEW, type ViewBoxSize } from './camera-math';\n\n/** Read the current SVG's viewBox as a `ViewBoxSize`, or null if not yet rendered. */\nexport function readViewBox(container: HTMLElement): ViewBoxSize | null {\n const svg = container.querySelector('svg');\n const viewBoxAttr = svg?.getAttribute('viewBox');\n if (!viewBoxAttr) return null;\n const parts = viewBoxAttr.split(/\\s+/).map(Number);\n if (parts.length !== 4 || parts.some((n) => Number.isNaN(n))) return null;\n return { width: parts[2]!, height: parts[3]! };\n}\n\n/** Apply a camera transform to the current marks group, if the chart is rendered. */\nexport function applyStoryCamera(container: HTMLElement, camera: Camera, vb: ViewBoxSize): void {\n const marksGroup = container.querySelector('[data-oc-marks-group]');\n if (!marksGroup) return;\n marksGroup.setAttribute('transform', cameraTransform(camera, vb));\n}\n\nexport { FULL_VIEW };\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/story/create-chart-story.ts","../../src/story/crossfade.ts","../../src/story/resolve-camera-target.ts","../../src/story/progress-math.ts","../../src/story/scroll-driver.ts","../../src/story/story-camera.ts"],"sourcesContent":["/**\n * `createChartStory`: a base spec plus an ordered list of deep-partial\n * patch steps, driven by scroll (built-in `ScrollDriver`) or directly via\n * `goTo(n)`.\n *\n * Animation-clock ownership (decision baked 2026-07-11, see\n * plans/design-evolution/11-scrollytelling.md): the data-update-transitions\n * driver in `../transition.ts` owns mark morphing; the ported tween in\n * `./tween.ts` owns camera and any scalar story-level animation. They never\n * animate the same property. `goTo` calls `ChartInstance.update()`, which\n * internally decides morph-vs-instant-swap via `canTransition`; this module\n * predicts that decision ahead of time (`canTransitionSpecShape`) only to\n * decide whether to arm the crossfade fallback, and separately drives the\n * camera tween. It does not bridge the two clocks.\n */\n\nimport type { DataRow, GeoMapSpec } from '@opendata-ai/openchart-core';\nimport { deepMergeSpec, isGeoMapSpec } from '@opendata-ai/openchart-core';\nimport { AUTO_CANVAS_THRESHOLD } from '@opendata-ai/openchart-engine';\nimport { createGeoMap, type GeoMapInstance, type GeoMapMountOptions } from '../map-mount';\nimport { type ChartInstance, createChart, type MountOptions } from '../mount';\nimport {\n CANVAS_DEFAULT_UPDATE_MAX_MARKS,\n canTransitionSpecShape,\n DEFAULT_UPDATE_MAX_MARKS,\n} from '../transition';\nimport {\n type Camera,\n camerasClose,\n dampCamera,\n FULL_VIEW,\n fitTarget,\n interpolateCamera,\n scrubCamera,\n type ViewBoxSize,\n} from './camera-math';\nimport { crossfadeUpdate } from './crossfade';\nimport { isDataCameraTarget, resolveCameraTarget } from './resolve-camera-target';\nimport { createScrollDriver } from './scroll-driver';\nimport { applyStoryCamera, readViewBox } from './story-camera';\nimport { createTween, easingFns, storyMotion } from './tween';\nimport type {\n ChartStoryInstance,\n ChartStoryOptions,\n StorySpec,\n StorySpecPatch,\n StoryStep,\n} from './types';\n\nfunction prefersReducedMotion(): boolean {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false;\n try {\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n } catch {\n return false;\n }\n}\n\n/** Merge a step's `highlight` sugar into its `spec` patch as `encoding.color.highlight`. */\nfunction stepToPatch(step: StoryStep): StorySpecPatch {\n let patch = step.spec ?? {};\n if ('highlight' in step) {\n // `highlight: null` clears a highlight set by an earlier step. It must map\n // to `[]` (not `undefined`): deep-merge skips `undefined`, so `?? undefined`\n // would leave the prior step's highlight in place. An empty array is a real\n // value that replaces it, and the engine reads `highlight.length > 0` as\n // active, so `[]` reads as \"no highlight.\"\n const highlight = step.highlight === null ? [] : (step.highlight ?? undefined);\n patch = deepMergeSpec(patch, {\n encoding: { color: { highlight } },\n });\n }\n return patch;\n}\n\n/** Apply patches 0..index (inclusive) onto the base spec, cumulatively. */\nfunction resolveSpecAtStep(base: StorySpec, steps: StoryStep[], index: number): StorySpec {\n let spec: StorySpec = base;\n for (let i = 0; i <= index && i < steps.length; i++) {\n spec = deepMergeSpec(spec, stepToPatch(steps[i]!));\n }\n return spec;\n}\n\n/**\n * Predict whether a morphing update would clear the runtime mark cap\n * (`canTransition` check 8). The runtime veto fires after `render()` has\n * already swapped, so a step that predicts \"morph\" but exceeds the cap lands\n * as a hard snap; predicting it here keeps the crossfade fallback instead.\n *\n * Data rows ≈ marks only for point and beeswarm specs, so the estimate is\n * scoped to those marks. Point marks auto-promote to canvas above\n * AUTO_CANVAS_THRESHOLD (where the cap is CANVAS_DEFAULT_UPDATE_MAX_MARKS)\n * unless the host forces `renderer: 'svg'`; beeswarm never renders on canvas.\n */\nfunction morphWithinMarkCap(\n prevSpec: StorySpec,\n nextSpec: StorySpec,\n renderer: 'auto' | 'svg' | 'canvas',\n): boolean {\n const mark = (nextSpec as { mark?: string | { type?: string } }).mark;\n const markType = typeof mark === 'string' ? mark : mark?.type;\n if (markType !== 'point' && markType !== 'beeswarm') return true;\n\n const rowCount = (s: StorySpec): number => {\n const data = (s as { data?: unknown }).data;\n return Array.isArray(data) ? data.length : 0;\n };\n const count = Math.max(rowCount(prevSpec), rowCount(nextSpec));\n\n const animation = (nextSpec as { animation?: unknown }).animation;\n const maxMarks =\n typeof animation === 'object' && animation !== null\n ? (animation as { update?: { maxMarks?: number } }).update?.maxMarks\n : undefined;\n\n const canvas =\n markType === 'point' &&\n renderer !== 'svg' &&\n (renderer === 'canvas' || rowCount(nextSpec) > AUTO_CANVAS_THRESHOLD);\n const cap = maxMarks ?? (canvas ? CANVAS_DEFAULT_UPDATE_MAX_MARKS : DEFAULT_UPDATE_MAX_MARKS);\n return count <= cap;\n}\n\n/**\n * Create a scrollytelling story bound to a container. Mounts a chart with\n * the base spec, then drives it through cumulative patch steps as the\n * reader scrolls (or via `goTo`).\n *\n * `editMode` (any of `onEdit`/`onSelect`/`onDeselect`/`onTextEdit` on\n * `mountOptions`) and an active story are mutually exclusive in v1: passing\n * both warns once and disables the story's own drive loop, matching the\n * seriesSearch/edit-mode precedent in `mount.ts`.\n */\nexport function createChartStory<TData extends DataRow = DataRow>(\n container: HTMLElement,\n options: ChartStoryOptions<TData>,\n mountOptions?: MountOptions,\n): ChartStoryInstance {\n const { spec, steps, triggerPosition = 0.4, cameraMode = 'step' } = options;\n // The story machinery is spec-shape agnostic (it only deep-merges patches\n // and hands specs to `update()`, which accepts the non-generic union).\n // Widen once here so the internal helpers don't have to thread `TData`\n // through a discriminated union that TS can't narrow across variants.\n const baseSpec = spec as StorySpec;\n const isMap = isGeoMapSpec(baseSpec as unknown as Record<string, unknown>);\n\n if (isMap && cameraMode === 'scrub') {\n console.warn(\n '[openchart] cameraMode: \"scrub\" is not supported for map stories; falling back to \"step\" mode. Map camera is driven via geo.focus patches in the spec.',\n );\n }\n\n if (isMap && steps.some((s) => s.camera)) {\n console.warn(\n '[openchart] step.camera is ignored for map stories. Drive the map camera via geo.focus patches in the spec instead.',\n );\n }\n\n const editModeRequested = !!(\n mountOptions?.onEdit ||\n mountOptions?.onSelect ||\n mountOptions?.onDeselect ||\n mountOptions?.onTextEdit\n );\n if (editModeRequested) {\n console.warn(\n '[openchart] a chart story and edit mode are mutually exclusive; the story will not drive updates while editing callbacks are active.',\n );\n }\n\n let currentStep = -1;\n let destroyed = false;\n\n const initialSpec = resolveSpecAtStep(baseSpec, steps, 0);\n\n // Branch: map vs chart mount\n let instance: ChartInstance | GeoMapInstance;\n if (isMap) {\n // Not a spread: GeoMapMountOptions callback signatures differ from MountOptions.\n // When adding fields to GeoMapMountOptions, check if they should transfer here.\n const mapOpts: GeoMapMountOptions = {\n theme: mountOptions?.theme,\n darkMode: mountOptions?.darkMode,\n watermark: mountOptions?.watermark,\n responsive: mountOptions?.responsive,\n };\n instance = createGeoMap(container, initialSpec as GeoMapSpec, mapOpts);\n } else {\n instance = createChart(container, initialSpec as Exclude<StorySpec, GeoMapSpec>, mountOptions);\n }\n\n // Chart camera infra (not used for maps)\n const cameraTween = isMap\n ? null\n : createTween<Camera>({\n initial: { cx: 0, cy: 0, k: 1 },\n lerp: interpolateCamera,\n duration: storyMotion.camera,\n ease: easingFns.easeInOutCubic,\n onFrame: (camera) => {\n const vb = readViewBox(container);\n if (vb) applyStoryCamera(container, camera, vb);\n },\n });\n\n /** Fit a step's camera (data-coordinate or raw target, or full view) into the viewBox. */\n function fittedCameraForStep(step: StoryStep | undefined, vb: ViewBoxSize): Camera {\n if (!step?.camera) return fitTarget(FULL_VIEW(vb), vb);\n const target = isDataCameraTarget(step.camera)\n ? resolveCameraTarget((instance as ChartInstance).layout, step.camera)\n : step.camera;\n return fitTarget(target, vb);\n }\n\n // ---- Scrub-mode camera: self-stopping damping loop toward a live target ---\n // Step mode eases discretely on step change (the tween above). Scrub mode\n // reads continuous `stepProgress` and dwells on the active target through a\n // hold zone, transitioning in the tail of the span. The two modes never run\n // at once; the settle loop and the step tween are mutually exclusive drivers.\n let scrubCurrent: Camera | null = null;\n let scrubTarget: Camera | null = null;\n let settleRaf = 0;\n let settling = false;\n\n // Memoized per-step fitted cameras for scrub mode. The fit depends only on\n // the current layout (rebuilt on instance.update) and the viewBox size, so\n // recomputing it on every scroll frame is wasted work. Cache keyed on the\n // layout reference plus the viewBox dimensions; invalidate when either moves.\n let fittedCache: Camera[] | null = null;\n let fittedCacheLayout: unknown = null;\n let fittedCacheVb: ViewBoxSize | null = null;\n\n function fittedCamerasForScrub(vb: ViewBoxSize): Camera[] {\n if (\n fittedCache &&\n fittedCacheLayout === (instance as ChartInstance).layout &&\n fittedCacheVb &&\n fittedCacheVb.width === vb.width &&\n fittedCacheVb.height === vb.height\n ) {\n return fittedCache;\n }\n fittedCache = steps.map((step) => fittedCameraForStep(step, vb));\n fittedCacheLayout = (instance as ChartInstance).layout;\n fittedCacheVb = vb;\n return fittedCache;\n }\n\n function stopSettle(): void {\n if (settling) {\n cancelAnimationFrame(settleRaf);\n settling = false;\n }\n }\n\n function startSettle(): void {\n if (settling) return;\n settling = true;\n let last = performance.now();\n const tick = (now: number): void => {\n const target = scrubTarget;\n const current = scrubCurrent;\n const vb = readViewBox(container);\n if (!target || !current || !vb) {\n settling = false;\n return;\n }\n const dt = Math.min(now - last, 64);\n last = now;\n const next = dampCamera(current, target, storyMotion.cameraTau, dt);\n scrubCurrent = next;\n applyStoryCamera(container, next, vb);\n if (camerasClose(next, target)) {\n scrubCurrent = target;\n applyStoryCamera(container, target, vb);\n settling = false;\n return;\n }\n settleRaf = requestAnimationFrame(tick);\n };\n settleRaf = requestAnimationFrame(tick);\n }\n\n /** Step-mode camera: retargetable eased tween on step change. */\n function applyCameraForStep(step: StoryStep | undefined, snap: boolean): void {\n if (!cameraTween) return;\n const vb = readViewBox(container);\n if (!vb) return;\n cameraTween.to(fittedCameraForStep(step, vb), { snap: snap || prefersReducedMotion() });\n }\n\n /** Scrub-mode camera: continuous, driven by the scroll frame's stepProgress. */\n function applyScrubCamera(frame: { step: number; stepProgress: number }): void {\n const vb = readViewBox(container);\n if (!vb) return;\n const fitted = fittedCamerasForScrub(vb);\n if (fitted.length === 0) return;\n\n if (prefersReducedMotion()) {\n const clamped = Math.min(Math.max(frame.step, 0), fitted.length - 1);\n stopSettle();\n scrubCurrent = fitted[clamped]!;\n applyStoryCamera(container, scrubCurrent, vb);\n return;\n }\n\n scrubTarget = scrubCamera(\n frame.step,\n frame.stepProgress,\n fitted,\n 0.65,\n easingFns.easeInOutCubic,\n );\n if (scrubCurrent === null) scrubCurrent = scrubTarget;\n startSettle();\n }\n\n function goTo(index: number): void {\n if (destroyed) return;\n const clamped = Math.max(0, Math.min(index, steps.length - 1));\n if (clamped === currentStep) return;\n\n const isFirst = currentStep === -1;\n currentStep = clamped;\n\n const nextSpec = resolveSpecAtStep(baseSpec, steps, clamped);\n\n if (isMap) {\n // Maps always apply directly: update handles fill + camera via geo.focus\n (instance as GeoMapInstance).update(nextSpec as GeoMapSpec);\n return;\n }\n\n const prevSpec = isFirst ? null : resolveSpecAtStep(baseSpec, steps, clamped - 1);\n\n const willLikelyMorph =\n !isFirst &&\n !editModeRequested &&\n prevSpec !== null &&\n canTransitionSpecShape(prevSpec, nextSpec) &&\n morphWithinMarkCap(prevSpec, nextSpec, mountOptions?.renderer ?? 'auto');\n\n const applyUpdate = () =>\n (instance as ChartInstance).update(nextSpec as Exclude<StorySpec, GeoMapSpec>);\n\n if (isFirst || willLikelyMorph || editModeRequested) {\n applyUpdate();\n } else {\n // Outside the morph gate (re-encode, type change, etc.): no step may\n // visibly snap, so crossfade the whole chart instead.\n crossfadeUpdate(container, applyUpdate, { reducedMotion: prefersReducedMotion() });\n }\n\n // Step mode drives the camera on each discrete step change. Scrub mode\n // drives it continuously from the scroll frame instead (see the scroll\n // subscription below), so it skips the discrete step tween here.\n if (cameraMode === 'step') applyCameraForStep(steps[clamped], isFirst);\n }\n\n const scrollDriver = editModeRequested ? null : createScrollDriver({ triggerPosition });\n let unsubscribeScroll: (() => void) | null = null;\n if (scrollDriver) {\n unsubscribeScroll = scrollDriver.progress.subscribe((frame) => {\n if (frame.step < 0) return;\n // Discrete spec steps always advance at step boundaries; the v1 non-goal\n // is continuous DATA morph scrubbing, not camera scrub. In scrub mode the\n // camera additionally follows the continuous frame.\n goTo(frame.step);\n if (!isMap && cameraMode === 'scrub') applyScrubCamera(frame);\n });\n }\n\n return {\n goTo,\n registerStep(index, el) {\n scrollDriver?.registerStep(index, el);\n return (nextEl: HTMLElement | null) => scrollDriver?.registerStep(index, nextEl);\n },\n setContainer(el) {\n scrollDriver?.setContainer(el);\n },\n get currentStep() {\n return currentStep;\n },\n get totalSteps() {\n return steps.length;\n },\n destroy() {\n if (destroyed) return;\n destroyed = true;\n unsubscribeScroll?.();\n scrollDriver?.destroy();\n cameraTween?.cancel();\n stopSettle();\n instance.destroy();\n },\n };\n}\n","/**\n * Whole-chart crossfade fallback for story steps whose spec diff falls\n * outside the data-update-transitions morph gate (`canTransition` in\n * `../transition.ts`): re-encodes, type changes, and anything else that\n * would otherwise instant-snap. No step in a story may visibly snap, so\n * this ghosts the current rendered SVG over the container, lets\n * `ChartInstance.update()` perform its normal instant swap underneath,\n * then fades the ghost out to reveal the new state.\n *\n * This is deliberately NOT the mark-morphing transition driver: it never\n * interpolates geometry, only opacity. See the animation-clock ownership\n * note in the scrollytelling plan -- the transitions driver owns mark\n * morphing, this owns the pixel-level fallback when that driver declines.\n */\n\nimport { storyMotion } from './tween';\n\nexport interface CrossfadeOptions {\n /** Fade duration in ms. Default `storyMotion.crossfade`. */\n duration?: number;\n /** Skip the fade and swap instantly (reduced motion). */\n reducedMotion?: boolean;\n}\n\n/**\n * Ghost the container's current SVG, run `applyUpdate` (which mutates the\n * container to its next state), then fade the ghost out.\n */\nexport function crossfadeUpdate(\n container: HTMLElement,\n applyUpdate: () => void,\n options: CrossfadeOptions = {},\n): void {\n const duration = options.duration ?? storyMotion.crossfade;\n const svg = container.querySelector('svg');\n\n if (options.reducedMotion || !svg || duration <= 0) {\n applyUpdate();\n return;\n }\n\n const ghost = svg.cloneNode(true) as SVGElement;\n ghost.setAttribute('aria-hidden', 'true');\n ghost.style.position = 'absolute';\n ghost.style.inset = '0';\n ghost.style.width = '100%';\n ghost.style.height = '100%';\n ghost.style.pointerEvents = 'none';\n ghost.style.transition = `opacity ${duration}ms ease-out`;\n ghost.style.opacity = '1';\n\n const priorPosition = container.style.position;\n container.style.position = priorPosition || 'relative';\n container.appendChild(ghost);\n\n applyUpdate();\n\n // Force layout so the transition starts from opacity: 1 before dropping.\n void ghost.getBoundingClientRect();\n ghost.style.opacity = '0';\n\n const cleanup = () => {\n ghost.remove();\n if (!priorPosition) container.style.position = '';\n };\n ghost.addEventListener('transitionend', cleanup, { once: true });\n // Safety net in case transitionend never fires (e.g. element removed by\n // an intervening render before the transition completes).\n setTimeout(cleanup, duration + 100);\n}\n","/**\n * Resolves data-coordinate camera targets (`{ x: [a, b], y: [c, d] }`) to\n * viewBox-space `CameraTarget` rects using the compiled chart's axis ticks.\n *\n * There is no raw d3 scale on `ChartLayout` (by design -- the compiled\n * layout is the public surface, not engine internals), so resolution works\n * off `AxisTick[]`, which pairs each tick's raw data `value` with its pixel\n * `position`:\n * - Exact tick match (ordinal/categorical values, or a quantitative value\n * that happens to land on a tick): use that tick's position directly.\n * - Otherwise linear interpolation between the two nearest ticks by data\n * value. Works for quantitative and temporal axes (temporal values are\n * compared as timestamps). Ordinal axes with no exact match have no\n * well-ordered \"between\" and fall back to the nearest tick.\n */\n\nimport type { AxisLayout, AxisTick, ChartLayout } from '@opendata-ai/openchart-core';\nimport type { CameraTarget } from './camera-math';\nimport type { StoryDataCameraTarget } from './types';\n\nconst DEFAULT_PADDING = 24;\n\n/** Coerce a tick value (string, number, Date, ISO string) to a comparable number. */\nfunction toComparable(value: unknown): number | null {\n if (typeof value === 'number') return value;\n if (value instanceof Date) return value.getTime();\n if (typeof value === 'string') {\n const asDate = Date.parse(value);\n if (!Number.isNaN(asDate)) return asDate;\n }\n return null;\n}\n\n/** Resolve one data value to a pixel position along an axis, or null if unresolvable. */\nfunction resolveOnAxis(axis: AxisLayout, target: unknown): number | null {\n const ticks = axis.ticks;\n if (ticks.length === 0) return null;\n\n const exact = ticks.find((t) => t.value === target);\n if (exact) return exact.position;\n\n const targetNum = toComparable(target);\n if (targetNum === null) return null;\n\n // Ticks with comparable numeric values, sorted by data value.\n const numeric: Array<{ tick: AxisTick; value: number }> = [];\n for (const tick of ticks) {\n const v = toComparable(tick.value);\n if (v !== null) numeric.push({ tick, value: v });\n }\n if (numeric.length === 0) return null;\n numeric.sort((a, b) => a.value - b.value);\n\n if (targetNum <= numeric[0]!.value) return numeric[0]!.tick.position;\n const last = numeric.at(-1)!;\n if (targetNum >= last.value) return last.tick.position;\n\n for (let i = 0; i < numeric.length - 1; i++) {\n const lo = numeric[i]!;\n const hi = numeric[i + 1]!;\n if (targetNum >= lo.value && targetNum <= hi.value) {\n const span = hi.value - lo.value;\n const t = span === 0 ? 0 : (targetNum - lo.value) / span;\n return lo.tick.position + t * (hi.tick.position - lo.tick.position);\n }\n }\n return null;\n}\n\n/**\n * Resolve a data-coordinate camera target to viewBox space. Falls back to\n * the full chart area (padded) for axes that are missing or unresolvable,\n * so a malformed target degrades to \"camera does nothing\" rather than\n * throwing mid-scroll.\n */\nexport function resolveCameraTarget(\n layout: ChartLayout,\n target: StoryDataCameraTarget,\n): CameraTarget {\n const area = layout.area;\n const padding = target.padding ?? DEFAULT_PADDING;\n\n let x1 = area.x;\n let x2 = area.x + area.width;\n if (target.x && layout.axes.x) {\n const a = resolveOnAxis(layout.axes.x, target.x[0]);\n const b = resolveOnAxis(layout.axes.x, target.x[1]);\n if (a !== null && b !== null) {\n x1 = Math.min(a, b);\n x2 = Math.max(a, b);\n }\n }\n\n let y1 = area.y;\n let y2 = area.y + area.height;\n if (target.y && layout.axes.y) {\n const a = resolveOnAxis(layout.axes.y, target.y[0]);\n const b = resolveOnAxis(layout.axes.y, target.y[1]);\n if (a !== null && b !== null) {\n y1 = Math.min(a, b);\n y2 = Math.max(a, b);\n }\n }\n\n return {\n x: x1,\n y: y1,\n width: Math.max(x2 - x1, 1),\n height: Math.max(y2 - y1, 1),\n padding,\n };\n}\n\n/** Type guard: a camera step given in data coordinates vs. raw viewBox `CameraTarget`. */\nexport function isDataCameraTarget(\n value: StoryDataCameraTarget | CameraTarget,\n): value is StoryDataCameraTarget {\n return !('width' in value) && !('height' in value);\n}\n","import { clamp01 } from './tween';\n\n/**\n * Pure math for continuous scrollytelling progress. The `ScrollDriver`\n * measures DOM rects and feeds them here. Ported from\n * opendata/shared/lib/scrolly/progress-math.ts.\n */\n\nexport interface ScrollyFrame {\n /** Geometric active step; -1 while the trigger line is above step 0 */\n step: number;\n /** 0..1 progress of the trigger line through the active step's span */\n stepProgress: number;\n /** 0..1 traversal from first step top to last step bottom */\n progress: number;\n direction: 'down' | 'up';\n}\n\nexport type ScrollyFrameGeometry = Omit<ScrollyFrame, 'direction'>;\n\n/**\n * Compute the geometric frame from viewport-relative step tops.\n *\n * @param tops viewport-relative step top offsets, ordered by step index\n * @param lastBottom viewport-relative bottom of the final step\n * @param triggerY the trigger line's viewport y (innerHeight * triggerPosition)\n */\nexport function computeProgress(\n tops: number[],\n lastBottom: number,\n triggerY: number,\n): ScrollyFrameGeometry {\n const n = tops.length;\n if (n === 0 || triggerY < tops[0]!) {\n return { step: -1, stepProgress: 0, progress: 0 };\n }\n\n let step = n - 1;\n for (let i = 0; i < n - 1; i++) {\n if (triggerY < tops[i + 1]!) {\n step = i;\n break;\n }\n }\n\n const stepTop = tops[step]!;\n const spanEnd = step < n - 1 ? tops[step + 1]! : lastBottom;\n const span = Math.max(spanEnd - stepTop, 1);\n const firstTop = tops[0]!;\n\n return {\n step,\n stepProgress: clamp01((triggerY - stepTop) / span),\n progress: clamp01((triggerY - firstTop) / Math.max(lastBottom - firstTop, 1)),\n };\n}\n\n/**\n * Reduced-motion variant: no scrubbing. `stepProgress` pins to 0 and overall\n * progress quantizes to the step's start fraction, so consumers see discrete\n * snaps only.\n */\nexport function quantizeFrame(\n frame: ScrollyFrameGeometry,\n totalSteps: number,\n): ScrollyFrameGeometry {\n if (frame.step < 0 || totalSteps <= 0) {\n return { step: frame.step, stepProgress: 0, progress: 0 };\n }\n return {\n step: frame.step,\n stepProgress: 0,\n progress: clamp01(frame.step / totalSteps),\n };\n}\n\nexport function framesEqual(a: ScrollyFrame | null, b: ScrollyFrame): boolean {\n return (\n a !== null &&\n a.step === b.step &&\n a.stepProgress === b.stepProgress &&\n a.progress === b.progress &&\n a.direction === b.direction\n );\n}\n","/**\n * Framework-agnostic scroll-step driver: element registry + rAF-throttled\n * scroll handler + subscription store. Ported from the React\n * `use-scroll-steps` hook in opendata/shared; the store design (60fps\n * frames as subscriptions, never framework state) carries over unchanged\n * because vanilla, React, Vue, and Svelte all want the same contract.\n *\n * Measurement is lazy: with zero subscribers the scroll handler does no\n * rect reads.\n */\n\nimport { computeProgress, framesEqual, quantizeFrame, type ScrollyFrame } from './progress-math';\n\nexport interface ScrollyProgressStore {\n /** Latest frame; `{step: -1, ...}` sentinel before any measurement */\n get(): ScrollyFrame;\n /** Emits the current frame immediately on subscribe, then on every change */\n subscribe(cb: (frame: ScrollyFrame) => void): () => void;\n}\n\nexport interface ScrollDriverOptions {\n /** Fraction of viewport height where the trigger line sits. Default 0.4 */\n triggerPosition?: number;\n}\n\nexport interface ScrollDriver {\n /** Register the scrolling container that wraps all steps. */\n setContainer(el: HTMLElement | null): void;\n /** Register a step element by index. Pass `null` to unregister. */\n registerStep(index: number, el: HTMLElement | null): void;\n /** Continuous progress as a subscription store. */\n progress: ScrollyProgressStore;\n /** Scroll a step into view (center). Honors reduced motion. */\n scrollToStep(index: number): void;\n /** Force a re-measurement (e.g. after layout changes). */\n measure(): void;\n /** Tear down scroll/resize listeners and the reduced-motion media query. */\n destroy(): void;\n}\n\nconst SENTINEL_FRAME: ScrollyFrame = {\n step: -1,\n stepProgress: 0,\n progress: 0,\n direction: 'down',\n};\n\n/**\n * Create a scroll-step driver. Framework wrappers (React/Vue/Svelte hooks)\n * are thin adapters over this: they own component lifecycle, this owns the\n * measurement/subscription machinery.\n */\nexport function createScrollDriver(options: ScrollDriverOptions = {}): ScrollDriver {\n const triggerPosition = options.triggerPosition ?? 0.4;\n\n let container: HTMLElement | null = null;\n const steps = new Map<number, HTMLElement>();\n const subscribers = new Set<(frame: ScrollyFrame) => void>();\n\n let frame: ScrollyFrame = SENTINEL_FRAME;\n let direction: 'down' | 'up' = 'down';\n // Direction is tracked from the container's viewport-relative position, NOT\n // window.scrollY: in a host that scrolls an inner container rather than the\n // page (Ladle, modals, dashboard panes, most app shells) window.scrollY is\n // pinned at 0 and would report no movement. The container's rect top moves\n // whenever ANY ancestor scrolls, so it works in both cases.\n let lastTop: number | null = null;\n let reducedMotion = false;\n let ticking = false;\n\n const measureAndEmit = () => {\n if (subscribers.size === 0) return;\n if (!container || steps.size === 0) return;\n if (typeof window === 'undefined') return;\n\n const viewportH = window.innerHeight;\n const containerRect = container.getBoundingClientRect();\n // Skip when the story is more than a viewport away in either direction.\n if (containerRect.bottom < -viewportH || containerRect.top > viewportH * 2) return;\n\n const count = steps.size;\n const tops: number[] = new Array(count);\n let lastBottom = 0;\n for (let i = 0; i < count; i++) {\n const el = steps.get(i);\n if (!el) return; // sparse registration mid-mount; wait for the next tick\n const rect = el.getBoundingClientRect();\n tops[i] = rect.top;\n if (i === count - 1) lastBottom = rect.bottom;\n }\n\n const triggerY = viewportH * triggerPosition;\n let geometry = computeProgress(tops, lastBottom, triggerY);\n if (reducedMotion) {\n geometry = quantizeFrame(geometry, count);\n }\n\n const next: ScrollyFrame = { ...geometry, direction };\n if (framesEqual(frame, next)) return;\n frame = next;\n for (const cb of subscribers) cb(frame);\n };\n\n const onScroll = () => {\n if (ticking) return;\n ticking = true;\n requestAnimationFrame(() => {\n // The container rising up the viewport (top decreasing) means the reader\n // is moving down through the story.\n const currentTop = container?.getBoundingClientRect().top ?? null;\n if (currentTop !== null && lastTop !== null) {\n const delta = lastTop - currentTop;\n if (delta > 5 || delta < -5) {\n direction = delta > 5 ? 'down' : 'up';\n }\n }\n lastTop = currentTop;\n ticking = false;\n measureAndEmit();\n });\n };\n\n let mediaQuery: MediaQueryList | null = null;\n const onReducedMotionChange = (e: MediaQueryListEvent) => {\n reducedMotion = e.matches;\n measureAndEmit();\n };\n\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\n mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');\n reducedMotion = mediaQuery.matches;\n mediaQuery.addEventListener('change', onReducedMotionChange);\n }\n\n if (typeof window !== 'undefined') {\n // Capture phase on the document, not just `window` scroll. Scroll events do\n // not bubble, so a window listener ONLY fires when the page itself scrolls.\n // Any host that scrolls an inner container instead — Ladle (which sets\n // `body { overflow: hidden }`), modals, dashboard panes, most app shells —\n // would never notify the driver and the story sat frozen on step 0.\n // Capturing on the document sees scroll from any ancestor, and the\n // measurement below is already viewport-relative (getBoundingClientRect vs\n // innerHeight), so it needs no other change to work in both layouts.\n document.addEventListener('scroll', onScroll, { passive: true, capture: true });\n window.addEventListener('resize', onScroll, { passive: true });\n }\n\n const progress: ScrollyProgressStore = {\n get: () => frame,\n subscribe: (cb) => {\n // Refresh `frame` from current geometry BEFORE registering `cb`, so the\n // measure pass notifies only existing subscribers. Then hand the new\n // subscriber the current frame exactly once. Registering after the\n // measure avoids the double-invoke that happened when measureAndEmit\n // changed the frame (looping over cb) and the explicit cb(frame) fired again.\n measureAndEmit();\n subscribers.add(cb);\n cb(frame);\n return () => {\n subscribers.delete(cb);\n };\n },\n };\n\n return {\n setContainer(el) {\n container = el;\n // Seed the direction baseline so the first scroll compares against a real\n // position rather than reporting a spurious jump from null.\n lastTop = el?.getBoundingClientRect().top ?? null;\n measureAndEmit();\n },\n registerStep(index, el) {\n if (el) {\n steps.set(index, el);\n } else {\n steps.delete(index);\n }\n measureAndEmit();\n },\n progress,\n scrollToStep(index) {\n const el = steps.get(index);\n if (!el) return;\n el.scrollIntoView({\n block: 'center',\n behavior: reducedMotion ? 'auto' : 'smooth',\n });\n },\n measure() {\n measureAndEmit();\n },\n destroy() {\n if (typeof window !== 'undefined') {\n // `capture: true` must match the addEventListener call or the listener\n // is not removed.\n document.removeEventListener('scroll', onScroll, { capture: true });\n window.removeEventListener('resize', onScroll);\n }\n mediaQuery?.removeEventListener('change', onReducedMotionChange);\n subscribers.clear();\n steps.clear();\n container = null;\n },\n };\n}\n","/**\n * Applies the scrollytelling camera transform to a mounted chart's marks\n * group (`[data-oc-marks-group]`, stamped by svg-renderer.ts). Re-finds the\n * group on every apply since `ChartInstance.update()` tears down and\n * rebuilds the SVG on every render -- the previous group reference goes\n * stale the instant a step patch triggers a re-render.\n *\n * DO NOT USE THIS TO \"ZOOM IN\" ON A CARTESIAN CHART.\n *\n * This is a geometric magnification of the marks group and nothing else. The\n * axes, gridlines, and annotations are siblings of that group, so they do not\n * move: zoom a line chart to 2010-2020 and the lines blow up while the x-axis\n * still reads 2000-2020 and every annotation drifts off its data point. Stroke\n * widths scale with the transform too, so the lines visibly fatten. The chart\n * ends up misrepresenting its own axis.\n *\n * To zoom a cartesian chart, narrow the scale domain instead and let the engine\n * recompile:\n *\n * { spec: { encoding: { x: { scale: { domain: ['2019', ...], clip: true } } } } }\n *\n * That relabels the axis, re-anchors annotations, keeps stroke widths constant,\n * and still morphs (the field identity is unchanged, so `canTransitionSpecShape`\n * passes and the marks FLIP-tween into the new scale).\n *\n * The camera remains useful where there is no axis to contradict -- graph and\n * other non-cartesian views, where magnifying the geometry IS the intent.\n */\n\nimport { type Camera, cameraTransform, FULL_VIEW, type ViewBoxSize } from './camera-math';\n\n/** Read the current SVG's viewBox as a `ViewBoxSize`, or null if not yet rendered. */\nexport function readViewBox(container: HTMLElement): ViewBoxSize | null {\n const svg = container.querySelector('svg');\n const viewBoxAttr = svg?.getAttribute('viewBox');\n if (!viewBoxAttr) return null;\n const parts = viewBoxAttr.split(/\\s+/).map(Number);\n if (parts.length !== 4 || parts.some((n) => Number.isNaN(n))) return null;\n return { width: parts[2]!, height: parts[3]! };\n}\n\n/** Apply a camera transform to the current marks group, if the chart is rendered. */\nexport function applyStoryCamera(container: HTMLElement, camera: Camera, vb: ViewBoxSize): void {\n const marksGroup = container.querySelector('[data-oc-marks-group]');\n if (!marksGroup) return;\n marksGroup.setAttribute('transform', cameraTransform(camera, vb));\n}\n\nexport { FULL_VIEW };\n"],"mappings":"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","names":[]}
|
package/dist/styles.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
@layer oc.tokens{.oc-root,.oc-chart-root,.oc-table-wrapper,.oc-table-root,.oc-graph-wrapper,.oc-graph-root,.oc-sankey-root,.oc-tilemap-root,.oc-barlist-root,.oc-map-root{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%;--oc-font-family:"Inter Variable", Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--oc-font-mono:"JetBrains Mono", "Fira Code", "Cascadia Code", monospace;--oc-ease-smooth:linear(0, .157, .438, .64, .766, .85, .906, .941, .964, .978, .988, .994, .998, 1);--oc-ease-snappy:linear(0, .012, .048, .108, .194, .302, .426, .559, .69, .808, .905, .973, 1.013, 1.028, 1.023, 1.006, .984, .966, .957, .957, .964, .975, .986, .995, 1, 1.003, 1.002, 1, .998, .998, .999, 1);--oc-animation-duration:.45s;--oc-animation-stagger:30ms;--oc-annotation-delay:.15s;--oc-title-size:26px;--oc-title-weight:600;--oc-title-tracking:-.022em;--oc-subtitle-size:14px;--oc-subtitle-weight:400;--oc-source-size:11px;--oc-source-weight:400;--oc-body-size:13px;--oc-eyebrow-size:11px;--oc-eyebrow-weight:500;--oc-eyebrow-tracking:.08em;--oc-bg:#fff;--oc-card:#fff;--oc-secondary:#f4f4f5;--oc-text:#09090b;--oc-text-secondary:#3f3f46;--oc-text-muted:#71717a;--oc-text-faint:#d4d4d8;--oc-gray-100:#f0f0f0;--oc-gray-200:#e1e1e2;--oc-gray-300:#c9c9c9;--oc-gray-400:#9d9d9d;--oc-gray-600:#787879;--oc-gray-800:#474748;--oc-gridline:rgba(0,0,0,.08);--oc-axis:rgba(0,0,0,.14);--oc-border:rgba(0,0,0,.08);--oc-border-radius:8px;--oc-radius-sm:2px;--oc-radius-md:6px;--oc-radius-lg:var(--oc-border-radius);--oc-radius-full:999px;--oc-accent:#06b6d4;--oc-accent-strong:#0891b2;--oc-positive:#15803d;--oc-negative:#dc2626;--oc-positive-tint:color-mix(in srgb, var(--oc-positive) 10%, var(--oc-bg));--oc-negative-tint:color-mix(in srgb, var(--oc-negative) 10%, var(--oc-bg));--oc-focus:#3b82f6;--oc-focus-ring:rgba(59,130,246,.1);--oc-focus-ring-strong:rgba(59,130,246,.25);--oc-editable-hover:rgba(79,70,229,.35);--oc-space-2:8px;--oc-space-4:16px;--oc-hover-dim:.3;--oc-hover-duration:.14s;--oc-map-hover-dim:.75;--oc-hover-bg:rgba(0,0,0,.024);--oc-tooltip-bg:rgba(255,255,255,.88);--oc-tooltip-border:rgba(0,0,0,.08);--oc-tooltip-shadow:0 2px 8px rgba(0,0,0,.08), 0 0 1px rgba(0,0,0,.12);--oc-tooltip-text:#09090b;--oc-legend-text:#3f3f46}.oc-dark{--oc-bg:#09090b;--oc-card:#111113;--oc-secondary:#27272a;--oc-text:#f7f8f8;--oc-text-secondary:#d0d6e0;--oc-text-muted:#a1a1aa;--oc-text-faint:#52525b;--oc-gridline:rgba(255,255,255,.06);--oc-axis:rgba(255,255,255,.14);--oc-border:rgba(255,255,255,.1);--oc-accent:#06b6d4;--oc-accent-strong:#06b6d4;--oc-positive:#4ade80;--oc-negative:#f87171;--oc-focus:#60a5fa;--oc-hover-bg:rgba(255,255,255,.05);--oc-tooltip-bg:rgba(17,17,19,.92);--oc-tooltip-border:rgba(255,255,255,.08);--oc-tooltip-shadow:0 2px 8px rgba(0,0,0,.3), 0 0 1px rgba(0,0,0,.4);--oc-tooltip-text:#f7f8f8;--oc-legend-text:#d0d6e0}}@layer oc.base{.oc-chart-root,.oc-table-root,.oc-graph-root,.oc-sankey-root,.oc-barlist-root,.oc-tilemap-root,.oc-map-root{width:100%;height:100%}.oc-table-root{overflow:auto}.oc-chart{font-family:var(--oc-font-family);font-variant-numeric:tabular-nums;width:100%;display:block}.oc-tilemap,.oc-map{width:100%;height:auto;display:block}.oc-map-feature{transition:opacity var(--oc-hover-duration) ease-out, stroke-width .14s ease-out}.oc-barlist{width:100%;display:block}.oc-sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:clip}.oc-editable-hover{outline:1.5px solid var(--oc-editable-hover);outline-offset:2px;border-radius:var(--oc-radius-sm)}.oc-root:focus-visible{outline:2px solid var(--oc-focus);outline-offset:2px}.oc-mark-focused{outline:2px solid var(--oc-focus);outline-offset:1px;border-radius:var(--oc-radius-sm)}}@layer oc.components{.oc-chrome{font-family:var(--oc-font-family)}.oc-eyebrow{font-size:var(--oc-eyebrow-size);font-weight:var(--oc-eyebrow-weight);letter-spacing:var(--oc-eyebrow-tracking);text-transform:uppercase;fill:var(--oc-accent)}.oc-title{font-size:var(--oc-title-size);font-weight:var(--oc-title-weight);letter-spacing:var(--oc-title-tracking);fill:var(--oc-text)}.oc-subtitle{font-size:var(--oc-subtitle-size);font-weight:var(--oc-subtitle-weight);fill:var(--oc-text-muted)}.oc-source,.oc-byline,.oc-footer{font-size:var(--oc-source-size);font-weight:var(--oc-source-weight);fill:var(--oc-text-muted)}.oc-brand{letter-spacing:.02em;fill:var(--oc-text-faint);font-size:11px;font-weight:500}.oc-brand-dot,.oc-eyebrow-dot{fill:var(--oc-accent)}.oc-metrics{font-family:var(--oc-font-family)}.oc-metric-label{letter-spacing:.06em;text-transform:uppercase;fill:var(--oc-text-muted);font-size:11px;font-weight:500}.oc-metric-value{letter-spacing:-.02em;fill:var(--oc-text);font-variant-numeric:tabular-nums;font-size:22px;font-weight:600}.oc-metric-delta-up{fill:var(--oc-positive);font-size:12px;font-weight:500}.oc-metric-delta-down{fill:var(--oc-negative);font-size:12px;font-weight:500}.oc-metric-secondary{fill:var(--oc-text-muted);font-size:12px;font-weight:400}.oc-axis-tick-inline{fill:var(--oc-text-muted);font-size:11px;font-weight:400}.oc-endpoint-labels{font-family:var(--oc-font-family)}.oc-endpoint-label{fill:var(--oc-endpoint-label-color,var(--oc-text))}.oc-endpoint-value{fill:var(--oc-endpoint-value-color,var(--oc-text-muted))}.oc-endpoint-leader{stroke:var(--oc-endpoint-leader-color,currentColor)}.oc-annotation-subtitle{fill:var(--oc-annotation-subtitle-color,var(--oc-text-muted))}.oc-table-footer-row{align-items:baseline;gap:16px;padding:16px 16px 4px;display:flex}.oc-chrome-footer{padding-top:0}.oc-table-footer-row .oc-table-ref{text-align:right;flex-shrink:0;margin-left:auto}.oc-tooltip{pointer-events:none;z-index:1000;background:var(--oc-tooltip-bg);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);border:1px solid var(--oc-tooltip-border);border-radius:var(--oc-radius-lg);box-shadow:var(--oc-tooltip-shadow);color:var(--oc-tooltip-text);font-family:var(--oc-font-family);min-width:140px;max-width:280px;padding:0;font-size:12px;line-height:1.4;transition:left .1s ease-in-out,top .1s ease-in-out;animation:.12s ease-out oc-tooltip-in;display:none;position:absolute}.oc-tooltip .oc-tooltip-header{align-items:center;gap:8px;padding:8px 10px 6px;display:flex}.oc-tooltip .oc-tooltip-dot{border-radius:50%;flex-shrink:0;width:8px;height:8px}.oc-tooltip .oc-tooltip-title{letter-spacing:.04em;text-transform:uppercase;color:var(--oc-text-muted);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.oc-tooltip .oc-tooltip-body{border-top:1px solid var(--oc-tooltip-border);padding:6px 10px 8px}.oc-tooltip .oc-tooltip-body:first-child{border-top:none;padding-top:10px}.oc-tooltip .oc-tooltip-row{justify-content:space-between;align-items:baseline;gap:16px;padding:2px 0;display:flex}.oc-tooltip .oc-tooltip-row-swatch{border-radius:50%;flex-shrink:0;width:8px;height:8px;margin-right:6px;display:inline-block;transform:translateY(-1px)}.oc-tooltip .oc-tooltip-label{color:var(--oc-text-secondary);white-space:nowrap;flex-shrink:0;align-items:center;font-size:12px;font-weight:400;display:inline-flex}.oc-tooltip .oc-tooltip-value{font-variant-numeric:tabular-nums;color:var(--oc-tooltip-text);text-align:right;text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:500;overflow:hidden}.oc-tooltip .oc-tooltip-row--emphasis .oc-tooltip-label{color:var(--oc-tooltip-text);font-weight:600}.oc-tooltip .oc-tooltip-row--emphasis .oc-tooltip-value{font-weight:600}.oc-tooltip .oc-tooltip-row--total{border-top:1px solid var(--oc-tooltip-border);margin-top:4px;padding-top:5px}.oc-tooltip .oc-tooltip-row--total .oc-tooltip-label{color:var(--oc-tooltip-text);font-weight:500}.oc-tooltip .oc-tooltip-row--total .oc-tooltip-value{font-weight:600}.oc-crosshair{pointer-events:none;transition:x1 50ms ease-in-out,x2 50ms ease-in-out}.oc-snap-dots circle{pointer-events:none;transition:cx 50ms ease-in-out,cy 50ms ease-in-out}.oc-legend{font-family:var(--oc-font-family)}.oc-legend-entry{cursor:default}.oc-marks.oc-hover-active .oc-mark--dim:not([opacity="0"]){opacity:var(--oc-hover-dim)}.oc-marks.oc-hover-active .oc-mark-rect.oc-mark--hover>*,.oc-marks.oc-hover-active .oc-mark-arc.oc-mark--hover>*,.oc-marks.oc-hover-active circle.oc-mark-point.oc-mark--hover{stroke:var(--oc-text);stroke-width:1px}.oc-chart:not(.oc-animate):not(.oc-transitioning) .oc-marks .oc-mark{transition:opacity var(--oc-hover-duration) ease-out}.oc-legend-entry--dim .oc-legend-swatch-line,.oc-legend-entry--dim .oc-legend-swatch-rect,.oc-legend-entry--dim>circle{opacity:var(--oc-hover-dim);transition:opacity var(--oc-hover-duration) ease-out}.oc-legend-hit{fill:transparent}.oc-legend-entry:focus{outline:none}.oc-legend-entry:focus-visible .oc-legend-hit{stroke:var(--oc-focus);stroke-width:2px}.oc-endpoint-label-entry{cursor:default}.oc-map-feature--hover{stroke:var(--oc-text);stroke-width:1.5px;vector-effect:non-scaling-stroke}.oc-map-features[data-hover] .oc-map-feature{transition:opacity var(--oc-hover-duration) ease-out}.oc-map-features[data-hover="0"] .oc-map-feature:not([data-bin-index="0"]),.oc-map-features[data-hover="1"] .oc-map-feature:not([data-bin-index="1"]),.oc-map-features[data-hover="2"] .oc-map-feature:not([data-bin-index="2"]),.oc-map-features[data-hover="3"] .oc-map-feature:not([data-bin-index="3"]),.oc-map-features[data-hover="4"] .oc-map-feature:not([data-bin-index="4"]),.oc-map-features[data-hover="5"] .oc-map-feature:not([data-bin-index="5"]),.oc-map-features[data-hover="6"] .oc-map-feature:not([data-bin-index="6"]),.oc-map-features[data-hover="7"] .oc-map-feature:not([data-bin-index="7"]),.oc-map-features[data-hover="8"] .oc-map-feature:not([data-bin-index="8"]),.oc-map-features[data-hover="9"] .oc-map-feature:not([data-bin-index="9"]),.oc-map-features[data-hover="10"] .oc-map-feature:not([data-bin-index="10"]),.oc-map-features[data-hover="11"] .oc-map-feature:not([data-bin-index="11"]){opacity:var(--oc-map-hover-dim)}.oc-legend-bin{cursor:default}.oc-tilemap-tiles.oc-hover-active .oc-tilemap-tile{transition:opacity var(--oc-hover-duration) ease-out}.oc-tilemap-tiles.oc-hover-active .oc-tilemap-tile:not(.oc-tilemap-tile--hover){opacity:var(--oc-hover-dim)}.oc-tilemap-tile--hover>rect{stroke:var(--oc-text);stroke-width:1.5px}.oc-series-search{font-family:var(--oc-font-family);z-index:10;justify-content:flex-end;align-items:center;gap:8px;display:flex;position:absolute}.oc-series-search-chips{scrollbar-width:none;align-items:center;gap:6px;min-width:0;display:flex;overflow-x:auto}.oc-series-search-chips::-webkit-scrollbar{display:none}.oc-series-search-chip{border:1px solid var(--oc-border);border-radius:var(--oc-radius-full);background:var(--oc-secondary);color:var(--oc-text-secondary);white-space:nowrap;align-items:center;gap:4px;padding:3px 4px 3px 10px;font-size:12px;font-weight:500;line-height:16px;display:inline-flex}.oc-series-search-chip-remove{width:16px;height:16px;color:var(--oc-text-muted);cursor:pointer;background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;margin:-4px -2px -4px -4px;padding:4px;transition:color .15s,background-color .15s;display:inline-flex}.oc-series-search-chip-remove:hover{color:var(--oc-text);background:var(--oc-hover-bg)}.oc-series-search-chip-remove:focus-visible{outline:2px solid var(--oc-focus);outline-offset:1px}.oc-series-search-chip-remove svg{display:block}.oc-series-search-box{flex:0 220px;min-width:120px;position:relative}.oc-series-search-icon{color:var(--oc-text-muted);pointer-events:none;position:absolute;top:50%;left:9px;transform:translateY(-50%)}.oc-series-search-input{height:32px;padding:0 10px 0 28px}.oc-series-search-listbox{border:1px solid var(--oc-border);border-radius:var(--oc-radius-lg);background:var(--oc-card);min-width:100%;max-height:240px;box-shadow:var(--oc-tooltip-shadow);z-index:20;margin:0;padding:4px;list-style:none;position:absolute;top:calc(100% + 4px);right:0;overflow-y:auto}.oc-series-search-option{border-radius:var(--oc-radius-sm);color:var(--oc-text);cursor:pointer;white-space:nowrap;text-overflow:ellipsis;padding:6px 10px;font-size:13px;overflow:hidden}.oc-series-search-option[aria-selected=true],.oc-series-search-option:hover{background:var(--oc-hover-bg)}.oc-series-search-match{font-weight:600}.oc-series-search-empty{color:var(--oc-text-muted);padding:6px 10px;font-size:12px}.oc-ydi-region{opacity:.35;transition:opacity .4s}.oc-ydi-region[data-ydi-hidden=true]{opacity:0}.oc-ydi-boundary{stroke:var(--oc-text-muted);stroke-width:1px;stroke-dasharray:3 3;stroke-opacity:.6}.oc-ydi-guess{stroke:var(--oc-focus);stroke-width:2.5px;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:1 6}.oc-ydi-comparison{stroke:var(--oc-text-muted);stroke-width:1.5px;stroke-opacity:.5;stroke-dasharray:4 4}.oc-ydi-clip-rect.oc-ydi-clip-animate{transition:width .7s cubic-bezier(.22,1,.36,1)}.oc-ydi-controls{pointer-events:none;font-family:var(--oc-font-family);box-sizing:border-box;z-index:10;flex-direction:column;justify-content:space-between;align-items:flex-end;padding:8px;display:flex}.oc-ydi-prompt{border-radius:var(--oc-radius-md);background:var(--oc-card);border:1px solid var(--oc-border);max-width:100%;color:var(--oc-text-secondary);text-align:center;box-shadow:var(--oc-tooltip-shadow);align-self:center;padding:6px 12px;font-size:13px;font-weight:500;line-height:16px}.oc-ydi-controls.oc-ydi-revealed .oc-ydi-prompt{display:none}.oc-ydi-reveal-button{pointer-events:auto;border:1px solid var(--oc-border);border-radius:var(--oc-radius-md);background:var(--oc-bg);color:var(--oc-text);cursor:pointer;box-shadow:var(--oc-tooltip-shadow);justify-content:center;align-items:center;padding:6px 14px;font-family:inherit;font-size:13px;font-weight:500;transition:background-color .15s,border-color .15s;display:inline-flex}.oc-ydi-reveal-button:hover{background:var(--oc-hover-bg)}.oc-ydi-reveal-button:focus-visible{outline:2px solid var(--oc-focus);outline-offset:2px}.oc-ydi-reveal-button:disabled{opacity:.5;cursor:default}.oc-table-wrapper{font-family:var(--oc-font-family);color:var(--oc-text);background:var(--oc-bg)}.oc-table-wrapper>.oc-chrome{margin-bottom:16px;padding-left:16px;padding-right:16px}.oc-table-wrapper>.oc-chrome:first-child{padding-top:4px}.oc-table-wrapper table{border-collapse:collapse;width:100%}.oc-table-wrapper th{text-align:left;border-bottom:1px solid var(--oc-border);box-sizing:border-box;padding:12px 16px;line-height:24px}.oc-table-wrapper td{text-align:left;border-bottom:1px solid var(--oc-border);box-sizing:border-box;padding:12px 16px;line-height:24px}.oc-table-wrapper th{text-transform:uppercase;letter-spacing:.04em;color:var(--oc-text-muted);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:12px;font-weight:500}.oc-table-wrapper thead{z-index:2;background:var(--oc-bg);position:sticky;top:0}.oc-table-wrapper thead th{border-bottom-width:1px}.oc-table-wrapper td{font-variant-numeric:tabular-nums;font-size:14px}.oc-table-wrapper th:focus{outline:2px solid var(--oc-focus);outline-offset:-2px}.oc-table-wrapper tbody:focus{outline:none}.oc-table-title{font-size:var(--oc-title-computed-size,var(--oc-title-size));font-weight:var(--oc-title-computed-weight,var(--oc-title-weight));color:var(--oc-title-computed-color,var(--oc-text));margin-bottom:4px}.oc-table-subtitle{font-size:var(--oc-subtitle-computed-size,var(--oc-subtitle-size));font-weight:var(--oc-subtitle-computed-weight,var(--oc-subtitle-weight));color:var(--oc-subtitle-computed-color,var(--oc-text-secondary));margin-bottom:8px}.oc-table-source{font-size:var(--oc-source-computed-size,var(--oc-source-size));color:var(--oc-source-computed-color,var(--oc-text-muted))}.oc-table-footer-text{font-size:var(--oc-footer-computed-size,var(--oc-source-size));color:var(--oc-footer-computed-color,var(--oc-text-muted))}.oc-table-scroll{overflow-x:auto}.oc-table--sticky th:first-child{z-index:1;background:var(--oc-bg);position:sticky;left:0}.oc-table--sticky td:first-child{z-index:1;background:var(--oc-bg);position:sticky;left:0}.oc-table-sort-btn{cursor:pointer;vertical-align:middle;background:0 0;border:none;flex-direction:column;align-items:center;gap:2px;margin-left:6px;padding:2px;display:inline-flex}.oc-table-sort-btn:before{content:"";border-left:5px solid transparent;border-right:5px solid transparent;width:0;height:0;transition:opacity .15s,border-color .15s;display:block}.oc-table-sort-btn:after{content:"";border-left:5px solid transparent;border-right:5px solid transparent;width:0;height:0;transition:opacity .15s,border-color .15s;display:block}.oc-table-sort-btn:before{border-bottom:4.5px solid var(--oc-text-secondary);opacity:.35}.oc-table-sort-btn:after{border-top:4.5px solid var(--oc-text-secondary);opacity:.35}.oc-table-sort-btn:hover:before{opacity:.75}.oc-table-sort-btn:hover:after{opacity:.75}th[aria-sort=ascending] .oc-table-sort-btn:before{opacity:1;border-bottom-color:var(--oc-text)}th[aria-sort=ascending] .oc-table-sort-btn:after{opacity:.15}th[aria-sort=descending] .oc-table-sort-btn:after{opacity:1;border-top-color:var(--oc-text)}th[aria-sort=descending] .oc-table-sort-btn:before{opacity:.15}.oc-table-search input,.oc-series-search-input{border:1px solid var(--oc-border);border-radius:var(--oc-radius-md);background:var(--oc-bg);width:100%;color:var(--oc-text);box-sizing:border-box;font-family:inherit;font-size:13px;transition:border-color .15s}.oc-table-search input::-ms-input-placeholder{color:var(--oc-text-muted);font-size:13px}.oc-table-search input::placeholder{color:var(--oc-text-muted);font-size:13px}.oc-series-search-input::-ms-input-placeholder{color:var(--oc-text-muted);font-size:13px}.oc-series-search-input::placeholder{color:var(--oc-text-muted);font-size:13px}.oc-table-search input:focus,.oc-series-search-input:focus{border-color:var(--oc-focus);box-shadow:0 0 0 3px var(--oc-focus-ring);outline:none}.oc-table-search{padding:8px 0}.oc-table-search input{padding:8px 12px}.oc-table-pagination{color:var(--oc-text-secondary);justify-content:space-between;align-items:center;padding:12px 0 4px;font-size:13px;display:flex}.oc-table-pagination button{border:1px solid var(--oc-border);border-radius:var(--oc-radius-md);background:var(--oc-bg);color:var(--oc-text);cursor:pointer;padding:6px 14px;font-family:inherit;font-size:13px;transition:background .15s,border-color .15s}.oc-table-pagination button:disabled{opacity:.35;cursor:not-allowed}.oc-table-pagination button:hover:not(:disabled){background:var(--oc-hover-bg);border-color:var(--oc-axis)}.oc-table-pagination button:focus-visible{outline:2px solid var(--oc-focus);outline-offset:1px}.oc-table-pagination-info{font-variant-numeric:tabular-nums}.oc-table-pagination-btns{gap:8px;display:flex}.oc-table-bar{position:relative}.oc-table-bar-fill{border-radius:var(--oc-radius-sm);opacity:.18;pointer-events:none;position:absolute;top:8px;bottom:8px;left:0}.oc-table-bar-value{z-index:1;position:relative}.oc-table-sparkline{width:100%;display:block;position:relative}.oc-table-sparkline svg{width:100%;display:block;overflow:visible}.oc-table-sparkline-dot{border-radius:50%;width:5px;height:5px;position:absolute}.oc-table-sparkline-labels{justify-content:space-between;font-size:11px;line-height:1;display:flex}.oc-table-image{vertical-align:middle;display:inline-block}.oc-table-image img{object-fit:cover}.oc-table-image-rounded img{border-radius:50%}.oc-table-flag{font-size:1.2em}:is(.oc-table--condensed,.oc-table--compact) th{padding:8px 12px;font-size:13px;line-height:24px}:is(.oc-table--condensed,.oc-table--compact) td{padding:8px 12px;font-size:13px;line-height:24px}:is(.oc-table--condensed,.oc-table--compact) th{font-size:11px}:is(.oc-table--condensed,.oc-table--compact) .oc-table-bar-fill{top:5px;bottom:5px}.oc-table--relaxed th{padding:16px 20px;line-height:24px}.oc-table--relaxed td{padding:16px 20px;line-height:24px}.oc-table--relaxed .oc-table-bar-fill{top:12px;bottom:12px}.oc-table--striped tbody tr:nth-child(2n){background:color-mix(in srgb, var(--oc-text) 3%, transparent)}.oc-table-delta{border-radius:var(--oc-radius-md);font-variant-numeric:tabular-nums;align-items:baseline;gap:4px;padding:2px 8px;font-weight:500;line-height:16px;display:inline-flex}.oc-table-delta-arrow{font-size:.8em;line-height:1}.oc-table-delta--positive{color:var(--oc-positive);background:var(--oc-positive-tint)}.oc-table-delta--negative{color:var(--oc-negative);background:var(--oc-negative-tint)}.oc-table-delta--neutral{color:var(--oc-text-muted);background:color-mix(in srgb, var(--oc-text) 6%, transparent)}.oc-table-chip{border-radius:var(--oc-radius-full);white-space:nowrap;align-items:center;gap:6px;padding:2px 8px;font-size:12px;font-weight:500;line-height:16px;display:inline-flex}.oc-table-chip-dot{border-radius:50%;flex:none;width:6px;height:6px}tfoot.oc-table-total{z-index:2;background:var(--oc-bg);position:sticky;bottom:0}tfoot.oc-table-total td{border-bottom:none;border-top:1px solid var(--oc-border);font-weight:600}.oc-table--cards .oc-table-scroll{overflow-x:hidden}.oc-table--cards table{table-layout:fixed}.oc-table--cards thead{clip-path:inset(50%);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}.oc-table--cards tbody tr{border-bottom:1px solid var(--oc-border);grid-template-columns:repeat(2,minmax(0,1fr));align-items:baseline;gap:2px 12px;padding:10px 12px;display:grid}.oc-table--cards tfoot tr{border-bottom:1px solid var(--oc-border);grid-template-columns:repeat(2,minmax(0,1fr));align-items:baseline;gap:2px 12px;padding:10px 12px;display:grid}.oc-table--cards td{overflow-wrap:anywhere;text-align:right;border-bottom:none;width:auto;max-width:100%;padding:0;display:block}.oc-table--cards td:before{content:attr(data-label);letter-spacing:.04em;text-transform:uppercase;color:var(--oc-text-muted);text-align:left;font-size:11px;font-weight:500;display:block}.oc-table--cards td[data-label=""]:before{content:none}.oc-table--cards td[data-priority="1"]{text-align:left;grid-column:1/-1;font-size:15px;font-weight:600}.oc-table--cards td[data-priority="1"]:before{content:none}.oc-table--cards td[data-priority="3"]{display:none}.oc-table--cards .oc-table-sparkline{width:100%}.oc-table--clickable tbody tr{cursor:pointer}.oc-table--clickable tbody tr:hover{background:var(--oc-hover-bg)}.oc-table-cell-focus{outline:2px solid var(--oc-focus);outline-offset:-2px}.oc-table-empty{text-align:center;color:var(--oc-text-secondary);padding:32px 16px;font-size:14px;font-style:italic}.oc-graph-wrapper{background:var(--oc-bg);font-family:var(--oc-font-family);width:100%;height:100%;position:relative;overflow:hidden}.oc-graph-canvas{cursor:grab;width:100%;height:100%;display:block}.oc-graph-canvas--dragging{cursor:grabbing}.oc-graph-chrome{z-index:2;pointer-events:none;padding:16px 16px 8px;position:absolute;top:0;left:0;right:0}.oc-graph-chrome .oc-title{font-size:var(--oc-title-size);font-weight:var(--oc-title-weight);letter-spacing:var(--oc-title-tracking);color:var(--oc-text);--_stroke:color-mix(in srgb, var(--oc-bg) 80%, transparent);text-shadow:-2px -2px 0 var(--_stroke), 2px -2px 0 var(--_stroke), -2px 2px 0 var(--_stroke), 2px 2px 0 var(--_stroke), 0 -2px 0 var(--_stroke), 0 2px 0 var(--_stroke), -2px 0 0 var(--_stroke), 2px 0 0 var(--_stroke);margin:0 0 4px}.oc-graph-chrome .oc-subtitle{font-size:var(--oc-subtitle-size);color:var(--oc-text-secondary);--_stroke:color-mix(in srgb, var(--oc-bg) 80%, transparent);text-shadow:-1px -1px 0 var(--_stroke), 1px -1px 0 var(--_stroke), -1px 1px 0 var(--_stroke), 1px 1px 0 var(--_stroke);margin:0}.oc-graph-legend{background:color-mix(in srgb, var(--oc-bg) 88%, transparent);border:1px solid var(--oc-border);border-radius:var(--oc-radius-lg);color:var(--oc-text-secondary);max-height:200px;padding:8px 12px;font-size:12px;position:absolute;top:12px;right:12px;overflow-y:auto}.oc-graph-legend-item{width:100%;font:inherit;color:inherit;text-align:left;cursor:default;background:0 0;border:none;align-items:center;gap:6px;margin:0;padding:2px 0;display:flex}button.oc-graph-legend-item{cursor:pointer;border-radius:var(--oc-radius-sm)}button.oc-graph-legend-item:hover{background:var(--oc-focus-ring)}button.oc-graph-legend-item:focus-visible{outline:2px solid var(--oc-focus);outline-offset:1px}.oc-graph-legend-item--inactive{opacity:.45}.oc-graph-legend-label{flex:auto}.oc-graph-legend-count{color:var(--oc-text-secondary);font-variant-numeric:tabular-nums;margin-left:auto;padding-left:8px}.oc-graph-legend-swatch{border-radius:50%;flex-shrink:0;width:10px;height:10px}.oc-graph-legend-swatch--line{border-radius:1px;width:12px;height:3px}.oc-graph-search{position:absolute;top:12px;left:12px}.oc-graph-search input{font-family:var(--oc-font-family);font-size:var(--oc-body-size);border:1px solid var(--oc-border);border-radius:var(--oc-radius-md);background:color-mix(in srgb, var(--oc-bg) 88%, transparent);color:var(--oc-text);outline:none;padding:6px 10px}.oc-graph-search input:focus{border-color:var(--oc-focus);box-shadow:0 0 0 2px var(--oc-focus-ring-strong)}.oc-sankey:not(.oc-animate) .oc-sankey-link path{transition:fill-opacity var(--oc-hover-duration) ease-out}.oc-sankey:not(.oc-animate) .oc-sankey-node,.oc-sankey:not(.oc-animate) .oc-sankey-label{transition:opacity var(--oc-hover-duration) ease-out}.oc-sankey-label{pointer-events:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.oc-chart[data-display=sparkline]{margin:0;padding:0;display:block}.oc-chart[data-display=sparkline] .oc-pulse{animation:.6s ease-out oc-pulse}@media (prefers-reduced-motion:reduce){.oc-chart[data-display=sparkline] .oc-pulse{animation:none}}}@layer oc.animation{.oc-table-wrapper.oc-animate>.oc-chrome{animation:oc-enter-fade calc(var(--oc-animation-duration) * .6) var(--oc-animation-ease,var(--oc-ease-smooth)) both}.oc-table-wrapper.oc-animate thead{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .4) var(--oc-animation-ease,var(--oc-ease-smooth)) both}.oc-table-wrapper.oc-animate tbody tr{animation:oc-table-enter-row var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0))}.oc-table-wrapper.oc-animate tbody td{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0))}.oc-table-wrapper.oc-animate td.oc-table-heatmap{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .7) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .3)}.oc-table-wrapper.oc-animate td.oc-table-category{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .7) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .3)}.oc-table-wrapper.oc-animate .oc-table-bar-fill{animation:oc-table-enter-bar-fill calc(var(--oc-animation-duration) * .8) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .3)}.oc-table-wrapper.oc-animate .oc-table-sparkline>svg{animation:oc-enter-line calc(var(--oc-animation-duration) * .8) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .4)}.oc-table-wrapper.oc-animate .oc-table-sparkline-dot{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .3) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .8)}.oc-table-wrapper.oc-animate .oc-table-sparkline-labels{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .3) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .8)}.oc-table-wrapper.oc-animate .oc-table-search{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both}.oc-table-wrapper.oc-animate .oc-table-pagination{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both}@keyframes oc-enter-bar{0%{clip-path:inset(100% 0 0);opacity:0}75%{opacity:1}to{clip-path:inset(0);opacity:1}}@keyframes oc-enter-bar-h{0%{clip-path:inset(0 100% 0 0);opacity:0}75%{opacity:1}to{clip-path:inset(0);opacity:1}}@keyframes oc-enter-line{0%{clip-path:inset(0 100% 0 0);opacity:0}15%{opacity:1}to{clip-path:inset(0);opacity:1}}@keyframes oc-enter-point{0%{opacity:0;transform:scale(.3)}to{opacity:1;transform:scale(1)}}@keyframes oc-enter-map-fill{0%{fill:var(--oc-secondary);opacity:0}to{fill:var(--oc-feature-fill);opacity:var(--oc-feature-target-opacity,1)}}@keyframes oc-enter-map-point{0%{opacity:0;transform:scale(.75)}to{opacity:1;transform:scale(1)}}@keyframes oc-enter-fade-only{0%{opacity:0}to{opacity:1}}@keyframes oc-enter-fade{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}@keyframes oc-table-enter-row{0%{transform:translateY(6px)}to{transform:translateY(0)}}@keyframes oc-table-enter-bar-fill{0%{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0)}}@keyframes oc-pulse{0%{r:var(--oc-pulse-r,4px);fill-opacity:1}45%{r:calc(var(--oc-pulse-r,4px) * 1.8);fill-opacity:.5}to{r:var(--oc-pulse-r,4px);fill-opacity:1}}@keyframes oc-tooltip-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}.oc-animate .oc-mark-rect :is(rect,path){animation:oc-enter-bar var(--oc-animation-duration) var(--oc-ease-smooth) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-bar :is(rect,path){animation:oc-enter-bar var(--oc-animation-duration) var(--oc-ease-smooth) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-rect[data-orient=horizontal] :is(rect,path){animation-name:oc-enter-bar-h}.oc-animate .oc-mark-bar[data-orient=horizontal] :is(rect,path){animation-name:oc-enter-bar-h}.oc-animate .oc-mark-rect[data-stack-pos] :is(rect,path){animation-duration:var(--oc-stack-segment-duration,.15s);animation-timing-function:linear;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0) + var(--oc-stack-pos,0) * var(--oc-stack-segment-duration,.15s))}.oc-animate .oc-mark-line{animation:oc-enter-line var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-area{animation:oc-enter-line var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-arc{animation:oc-enter-fade-only var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate circle.oc-mark-point{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .4) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate circle.oc-mark-circle{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .4) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-line~circle.oc-mark-point{animation-delay:calc(var(--oc-animation-duration) * .35 + var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-area~circle.oc-mark-point{animation-delay:calc(var(--oc-animation-duration) * .35 + var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-text text{animation:oc-enter-fade calc(var(--oc-animation-duration) * .6) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-rule line{animation:oc-enter-fade calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-tick line{animation:oc-enter-fade calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-label{animation:oc-enter-fade .25s var(--oc-ease-smooth) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0) + var(--oc-animation-duration) * .7)}.oc-animate .oc-annotation{animation:oc-enter-fade .3s var(--oc-ease-smooth) both;animation-delay:calc(var(--oc-animation-duration) + var(--oc-annotation-delay,.15s))}.oc-animate .oc-tilemap-tile{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .35) ease-in both;animation-delay:var(--oc-tile-delay,0s)}.oc-animate .oc-map-feature{animation:oc-enter-map-fill var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:var(--oc-map-delay,0s)}.oc-animate .oc-map-features[data-bulk-animate]{animation:oc-enter-fade-only var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both}.oc-animate .oc-map-features[data-bulk-animate] .oc-map-feature{animation:none}.oc-animate .oc-map-borders path{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .4) ease-in both;animation-delay:calc(var(--oc-animation-duration) * .7)}.oc-animate .oc-map-point{animation:oc-enter-map-point calc(var(--oc-animation-duration) * .4) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-duration) * .8 + var(--oc-mark-index,0) * var(--oc-point-stagger,60ms))}.oc-animate .oc-sankey-node rect{animation:oc-enter-fade-only var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-sankey-link path{animation:oc-enter-fade-only var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-duration) * .3 + var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-barlist-row{animation:oc-enter-fade calc(var(--oc-animation-duration) * .6) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:var(--oc-row-delay,0s)}.oc-animate .oc-barlist-bar{animation:oc-enter-bar-h var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:var(--oc-row-delay,0s)}.oc-animate[data-display=sparkline] .oc-mark-line,.oc-animate[data-display=sparkline] .oc-mark-area{animation-timing-function:cubic-bezier(.16,1,.3,1)}}@layer oc.reduced-motion{@media (prefers-reduced-motion:reduce){:is(.oc-root,.oc-chart-root,.oc-table-wrapper,.oc-table-root,.oc-graph-wrapper,.oc-graph-root,.oc-sankey-root,.oc-tilemap-root,.oc-barlist-root,.oc-map-root){transition:none;animation:none}:is(.oc-root,.oc-chart-root,.oc-table-wrapper,.oc-table-root,.oc-graph-wrapper,.oc-graph-root,.oc-sankey-root,.oc-tilemap-root,.oc-barlist-root,.oc-map-root) *{transition:none;animation:none}:is(.oc-root,.oc-chart-root,.oc-table-wrapper,.oc-table-root,.oc-graph-wrapper,.oc-graph-root,.oc-sankey-root,.oc-tilemap-root,.oc-barlist-root,.oc-map-root) :before{transition:none;animation:none}:is(.oc-root,.oc-chart-root,.oc-table-wrapper,.oc-table-root,.oc-graph-wrapper,.oc-graph-root,.oc-sankey-root,.oc-tilemap-root,.oc-barlist-root,.oc-map-root) :after{transition:none;animation:none}}}
|
|
1
|
+
@layer oc.tokens{.oc-root,.oc-chart-root,.oc-table-wrapper,.oc-table-root,.oc-graph-wrapper,.oc-graph-root,.oc-sankey-root,.oc-tilemap-root,.oc-barlist-root,.oc-map-root{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%;--oc-font-family:"Inter Variable", Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--oc-font-mono:"JetBrains Mono", "Fira Code", "Cascadia Code", monospace;--oc-ease-smooth:linear(0, .157, .438, .64, .766, .85, .906, .941, .964, .978, .988, .994, .998, 1);--oc-ease-snappy:linear(0, .012, .048, .108, .194, .302, .426, .559, .69, .808, .905, .973, 1.013, 1.028, 1.023, 1.006, .984, .966, .957, .957, .964, .975, .986, .995, 1, 1.003, 1.002, 1, .998, .998, .999, 1);--oc-animation-duration:.45s;--oc-animation-stagger:30ms;--oc-annotation-delay:.15s;--oc-title-size:26px;--oc-title-weight:600;--oc-title-tracking:-.022em;--oc-subtitle-size:14px;--oc-subtitle-weight:400;--oc-source-size:11px;--oc-source-weight:400;--oc-body-size:13px;--oc-eyebrow-size:11px;--oc-eyebrow-weight:500;--oc-eyebrow-tracking:.08em;--oc-bg:#fff;--oc-card:#fff;--oc-secondary:#f4f4f5;--oc-text:#09090b;--oc-text-secondary:#3f3f46;--oc-text-muted:#71717a;--oc-text-faint:#d4d4d8;--oc-gray-100:#f0f0f0;--oc-gray-200:#e1e1e2;--oc-gray-300:#c9c9c9;--oc-gray-400:#9d9d9d;--oc-gray-600:#787879;--oc-gray-800:#474748;--oc-gridline:rgba(0,0,0,.08);--oc-axis:rgba(0,0,0,.14);--oc-border:rgba(0,0,0,.08);--oc-border-radius:8px;--oc-radius-sm:2px;--oc-radius-md:6px;--oc-radius-lg:var(--oc-border-radius);--oc-radius-full:999px;--oc-accent:#06b6d4;--oc-accent-strong:#0891b2;--oc-positive:#15803d;--oc-negative:#dc2626;--oc-positive-tint:color-mix(in srgb, var(--oc-positive) 10%, var(--oc-bg));--oc-negative-tint:color-mix(in srgb, var(--oc-negative) 10%, var(--oc-bg));--oc-focus:#3b82f6;--oc-focus-ring:rgba(59,130,246,.1);--oc-focus-ring-strong:rgba(59,130,246,.25);--oc-editable-hover:rgba(79,70,229,.35);--oc-space-2:8px;--oc-space-4:16px;--oc-hover-dim:.3;--oc-hover-duration:.14s;--oc-map-hover-dim:.75;--oc-hover-bg:rgba(0,0,0,.024);--oc-tooltip-bg:rgba(255,255,255,.88);--oc-tooltip-border:rgba(0,0,0,.08);--oc-tooltip-shadow:0 2px 8px rgba(0,0,0,.08), 0 0 1px rgba(0,0,0,.12);--oc-tooltip-text:#09090b;--oc-legend-text:#3f3f46}.oc-dark{--oc-bg:#09090b;--oc-card:#111113;--oc-secondary:#27272a;--oc-text:#f7f8f8;--oc-text-secondary:#d0d6e0;--oc-text-muted:#a1a1aa;--oc-text-faint:#52525b;--oc-gridline:rgba(255,255,255,.06);--oc-axis:rgba(255,255,255,.14);--oc-border:rgba(255,255,255,.1);--oc-accent:#06b6d4;--oc-accent-strong:#06b6d4;--oc-positive:#4ade80;--oc-negative:#f87171;--oc-focus:#60a5fa;--oc-hover-bg:rgba(255,255,255,.05);--oc-tooltip-bg:rgba(17,17,19,.92);--oc-tooltip-border:rgba(255,255,255,.08);--oc-tooltip-shadow:0 2px 8px rgba(0,0,0,.3), 0 0 1px rgba(0,0,0,.4);--oc-tooltip-text:#f7f8f8;--oc-legend-text:#d0d6e0}}@layer oc.base{.oc-chart-root,.oc-table-root,.oc-graph-root,.oc-sankey-root,.oc-barlist-root,.oc-tilemap-root,.oc-map-root{width:100%;height:100%}.oc-table-root{overflow:auto}.oc-chart{font-family:var(--oc-font-family);font-variant-numeric:tabular-nums;width:100%;display:block}.oc-tilemap,.oc-map{width:100%;height:auto;display:block}.oc-map-feature{transition:opacity var(--oc-hover-duration) ease-out, stroke-width .14s ease-out}.oc-barlist{width:100%;display:block}.oc-sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:clip}.oc-editable-hover{outline:1.5px solid var(--oc-editable-hover);outline-offset:2px;border-radius:var(--oc-radius-sm)}.oc-root:focus-visible{outline:2px solid var(--oc-focus);outline-offset:2px}.oc-mark-focused{outline:2px solid var(--oc-focus);outline-offset:1px;border-radius:var(--oc-radius-sm)}}@layer oc.components{.oc-chrome{font-family:var(--oc-font-family)}.oc-eyebrow{font-size:var(--oc-eyebrow-size);font-weight:var(--oc-eyebrow-weight);letter-spacing:var(--oc-eyebrow-tracking);text-transform:uppercase;fill:var(--oc-accent)}.oc-title{font-size:var(--oc-title-size);font-weight:var(--oc-title-weight);letter-spacing:var(--oc-title-tracking);fill:var(--oc-text)}.oc-subtitle{font-size:var(--oc-subtitle-size);font-weight:var(--oc-subtitle-weight);fill:var(--oc-text-muted)}.oc-source,.oc-byline,.oc-footer{font-size:var(--oc-source-size);font-weight:var(--oc-source-weight);fill:var(--oc-text-muted)}.oc-brand{letter-spacing:.02em;fill:var(--oc-text-faint);font-size:11px;font-weight:500}.oc-brand-dot,.oc-eyebrow-dot{fill:var(--oc-accent)}.oc-metrics{font-family:var(--oc-font-family)}.oc-metric-label{letter-spacing:.06em;text-transform:uppercase;fill:var(--oc-text-muted);font-size:11px;font-weight:500}.oc-metric-value{letter-spacing:-.02em;fill:var(--oc-text);font-variant-numeric:tabular-nums;font-size:22px;font-weight:600}.oc-metric-delta-up{fill:var(--oc-positive);font-size:12px;font-weight:500}.oc-metric-delta-down{fill:var(--oc-negative);font-size:12px;font-weight:500}.oc-metric-secondary{fill:var(--oc-text-muted);font-size:12px;font-weight:400}.oc-axis-tick-inline{fill:var(--oc-text-muted);font-size:11px;font-weight:400}.oc-endpoint-labels{font-family:var(--oc-font-family)}.oc-endpoint-label{fill:var(--oc-endpoint-label-color,var(--oc-text))}.oc-endpoint-value{fill:var(--oc-endpoint-value-color,var(--oc-text-muted))}.oc-endpoint-leader{stroke:var(--oc-endpoint-leader-color,currentColor)}.oc-annotation-subtitle{fill:var(--oc-annotation-subtitle-color,var(--oc-text-muted))}.oc-table-footer-row{align-items:baseline;gap:16px;padding:16px 16px 4px;display:flex}.oc-chrome-footer{padding-top:0}.oc-table-footer-row .oc-table-ref{text-align:right;flex-shrink:0;margin-left:auto}.oc-tooltip{pointer-events:none;z-index:1000;background:var(--oc-tooltip-bg);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);border:1px solid var(--oc-tooltip-border);border-radius:var(--oc-radius-lg);box-shadow:var(--oc-tooltip-shadow);color:var(--oc-tooltip-text);font-family:var(--oc-font-family);min-width:140px;max-width:280px;padding:0;font-size:12px;line-height:1.4;transition:left .1s ease-in-out,top .1s ease-in-out;animation:.12s ease-out oc-tooltip-in;display:none;position:absolute}.oc-tooltip .oc-tooltip-header{align-items:center;gap:8px;padding:8px 10px 6px;display:flex}.oc-tooltip .oc-tooltip-dot{border-radius:50%;flex-shrink:0;width:8px;height:8px}.oc-tooltip .oc-tooltip-title{letter-spacing:.04em;text-transform:uppercase;color:var(--oc-text-muted);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.oc-tooltip .oc-tooltip-body{border-top:1px solid var(--oc-tooltip-border);padding:6px 10px 8px}.oc-tooltip .oc-tooltip-body:first-child{border-top:none;padding-top:10px}.oc-tooltip .oc-tooltip-row{justify-content:space-between;align-items:baseline;gap:16px;padding:2px 0;display:flex}.oc-tooltip .oc-tooltip-row-swatch{border-radius:50%;flex-shrink:0;width:8px;height:8px;margin-right:6px;display:inline-block;transform:translateY(-1px)}.oc-tooltip .oc-tooltip-label{color:var(--oc-text-secondary);white-space:nowrap;flex-shrink:0;align-items:center;font-size:12px;font-weight:400;display:inline-flex}.oc-tooltip .oc-tooltip-value{font-variant-numeric:tabular-nums;color:var(--oc-tooltip-text);text-align:right;text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:500;overflow:hidden}.oc-tooltip .oc-tooltip-row--emphasis .oc-tooltip-label{color:var(--oc-tooltip-text);font-weight:600}.oc-tooltip .oc-tooltip-row--emphasis .oc-tooltip-value{font-weight:600}.oc-tooltip .oc-tooltip-row--total{border-top:1px solid var(--oc-tooltip-border);margin-top:4px;padding-top:5px}.oc-tooltip .oc-tooltip-row--total .oc-tooltip-label{color:var(--oc-tooltip-text);font-weight:500}.oc-tooltip .oc-tooltip-row--total .oc-tooltip-value{font-weight:600}.oc-crosshair{pointer-events:none;transition:x1 50ms ease-in-out,x2 50ms ease-in-out}.oc-snap-dots circle{pointer-events:none;transition:cx 50ms ease-in-out,cy 50ms ease-in-out}.oc-legend{font-family:var(--oc-font-family)}.oc-legend-entry{cursor:default}.oc-marks.oc-hover-active .oc-mark--dim:not([opacity="0"]){opacity:var(--oc-hover-dim)}.oc-marks.oc-hover-active .oc-mark-rect.oc-mark--hover>*,.oc-marks.oc-hover-active .oc-mark-arc.oc-mark--hover>*,.oc-marks.oc-hover-active circle.oc-mark-point.oc-mark--hover{stroke:var(--oc-text);stroke-width:1px}.oc-chart:not(.oc-animate):not(.oc-transitioning) .oc-marks .oc-mark{transition:opacity var(--oc-hover-duration) ease-out}.oc-legend-entry--dim .oc-legend-swatch-line,.oc-legend-entry--dim .oc-legend-swatch-rect,.oc-legend-entry--dim>circle{opacity:var(--oc-hover-dim);transition:opacity var(--oc-hover-duration) ease-out}.oc-legend-hit{fill:transparent}.oc-legend-entry:focus{outline:none}.oc-legend-entry:focus-visible .oc-legend-hit{stroke:var(--oc-focus);stroke-width:2px}.oc-endpoint-label-entry{cursor:default}.oc-map-feature--hover{stroke:var(--oc-text);stroke-width:1.5px;vector-effect:non-scaling-stroke}.oc-map-features[data-hover] .oc-map-feature{transition:opacity var(--oc-hover-duration) ease-out}.oc-map-features[data-hover="0"] .oc-map-feature:not([data-bin-index="0"]),.oc-map-features[data-hover="1"] .oc-map-feature:not([data-bin-index="1"]),.oc-map-features[data-hover="2"] .oc-map-feature:not([data-bin-index="2"]),.oc-map-features[data-hover="3"] .oc-map-feature:not([data-bin-index="3"]),.oc-map-features[data-hover="4"] .oc-map-feature:not([data-bin-index="4"]),.oc-map-features[data-hover="5"] .oc-map-feature:not([data-bin-index="5"]),.oc-map-features[data-hover="6"] .oc-map-feature:not([data-bin-index="6"]),.oc-map-features[data-hover="7"] .oc-map-feature:not([data-bin-index="7"]),.oc-map-features[data-hover="8"] .oc-map-feature:not([data-bin-index="8"]),.oc-map-features[data-hover="9"] .oc-map-feature:not([data-bin-index="9"]),.oc-map-features[data-hover="10"] .oc-map-feature:not([data-bin-index="10"]),.oc-map-features[data-hover="11"] .oc-map-feature:not([data-bin-index="11"]){opacity:var(--oc-map-hover-dim)}.oc-legend-bin{cursor:default}.oc-tilemap-tiles.oc-hover-active .oc-tilemap-tile{transition:opacity var(--oc-hover-duration) ease-out}.oc-tilemap-tiles.oc-hover-active .oc-tilemap-tile:not(.oc-tilemap-tile--hover){opacity:var(--oc-hover-dim)}.oc-tilemap-tile--hover>rect{stroke:var(--oc-text);stroke-width:1.5px}.oc-series-search{font-family:var(--oc-font-family);z-index:10;justify-content:flex-end;align-items:center;gap:8px;display:flex;position:absolute}.oc-series-search-chips{scrollbar-width:none;align-items:center;gap:6px;min-width:0;display:flex;overflow-x:auto}.oc-series-search-chips::-webkit-scrollbar{display:none}.oc-series-search-chip{border:1px solid var(--oc-border);border-radius:var(--oc-radius-full);background:var(--oc-secondary);color:var(--oc-text-secondary);white-space:nowrap;align-items:center;gap:4px;padding:3px 4px 3px 10px;font-size:12px;font-weight:500;line-height:16px;display:inline-flex}.oc-series-search-chip-remove{width:16px;height:16px;color:var(--oc-text-muted);cursor:pointer;background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;margin:-4px -2px -4px -4px;padding:4px;transition:color .15s,background-color .15s;display:inline-flex}.oc-series-search-chip-remove:hover{color:var(--oc-text);background:var(--oc-hover-bg)}.oc-series-search-chip-remove:focus-visible{outline:2px solid var(--oc-focus);outline-offset:1px}.oc-series-search-chip-remove svg{display:block}.oc-series-search-box{flex:0 220px;min-width:120px;position:relative}.oc-series-search-icon{color:var(--oc-text-muted);pointer-events:none;position:absolute;top:50%;left:9px;transform:translateY(-50%)}.oc-series-search-input{height:32px;padding:0 10px 0 28px}.oc-series-search-listbox{border:1px solid var(--oc-border);border-radius:var(--oc-radius-lg);background:var(--oc-card);min-width:100%;max-height:240px;box-shadow:var(--oc-tooltip-shadow);z-index:20;margin:0;padding:4px;list-style:none;position:absolute;top:calc(100% + 4px);right:0;overflow-y:auto}.oc-series-search-option{border-radius:var(--oc-radius-sm);color:var(--oc-text);cursor:pointer;white-space:nowrap;text-overflow:ellipsis;padding:6px 10px;font-size:13px;overflow:hidden}.oc-series-search-option[aria-selected=true],.oc-series-search-option:hover{background:var(--oc-hover-bg)}.oc-series-search-match{font-weight:600}.oc-series-search-empty{color:var(--oc-text-muted);padding:6px 10px;font-size:12px}.oc-ydi-region{opacity:.35;transition:opacity .4s}.oc-ydi-region[data-ydi-hidden=true]{opacity:0}.oc-ydi-boundary{stroke:var(--oc-text-muted);stroke-width:1px;stroke-dasharray:3 3;stroke-opacity:.6}.oc-ydi-guess{stroke:var(--oc-focus);stroke-width:2.5px;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:1 6}.oc-ydi-comparison{stroke:var(--oc-text-muted);stroke-width:1.5px;stroke-opacity:.5;stroke-dasharray:4 4}.oc-ydi-clip-rect.oc-ydi-clip-animate{transition:width .7s cubic-bezier(.22,1,.36,1)}.oc-ydi-controls{pointer-events:none;font-family:var(--oc-font-family);box-sizing:border-box;z-index:10;flex-direction:column;justify-content:space-between;align-items:flex-end;padding:8px;display:flex}.oc-ydi-prompt{border-radius:var(--oc-radius-md);background:var(--oc-card);border:1px solid var(--oc-border);max-width:100%;color:var(--oc-text-secondary);text-align:center;box-shadow:var(--oc-tooltip-shadow);align-self:center;padding:6px 12px;font-size:13px;font-weight:500;line-height:16px}.oc-ydi-controls.oc-ydi-revealed .oc-ydi-prompt{display:none}.oc-ydi-reveal-button{pointer-events:auto;border:1px solid var(--oc-border);border-radius:var(--oc-radius-md);background:var(--oc-bg);color:var(--oc-text);cursor:pointer;box-shadow:var(--oc-tooltip-shadow);justify-content:center;align-items:center;padding:6px 14px;font-family:inherit;font-size:13px;font-weight:500;transition:background-color .15s,border-color .15s;display:inline-flex}.oc-ydi-reveal-button:hover{background:var(--oc-hover-bg)}.oc-ydi-reveal-button:focus-visible{outline:2px solid var(--oc-focus);outline-offset:2px}.oc-ydi-reveal-button:disabled{opacity:.5;cursor:default}.oc-table-wrapper{font-family:var(--oc-font-family);color:var(--oc-text);background:var(--oc-bg)}.oc-table-wrapper>.oc-chrome{margin-bottom:16px;padding-left:16px;padding-right:16px}.oc-table-wrapper>.oc-chrome:first-child{padding-top:4px}.oc-table-wrapper table{border-collapse:collapse;width:100%}.oc-table-wrapper th{text-align:left;border-bottom:1px solid var(--oc-border);box-sizing:border-box;padding:12px 16px;line-height:24px}.oc-table-wrapper td{text-align:left;border-bottom:1px solid var(--oc-border);box-sizing:border-box;padding:12px 16px;line-height:24px}.oc-table-wrapper th{text-transform:uppercase;letter-spacing:.04em;color:var(--oc-text-muted);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:12px;font-weight:500}.oc-table-wrapper thead{z-index:2;background:var(--oc-bg);position:sticky;top:0}.oc-table-wrapper thead th{border-bottom-width:1px}.oc-table-wrapper td{font-variant-numeric:tabular-nums;font-size:14px}.oc-table-wrapper th:focus{outline:2px solid var(--oc-focus);outline-offset:-2px}.oc-table-wrapper tbody:focus{outline:none}.oc-table-title{font-size:var(--oc-title-computed-size,var(--oc-title-size));font-weight:var(--oc-title-computed-weight,var(--oc-title-weight));color:var(--oc-title-computed-color,var(--oc-text));margin-bottom:4px}.oc-table-subtitle{font-size:var(--oc-subtitle-computed-size,var(--oc-subtitle-size));font-weight:var(--oc-subtitle-computed-weight,var(--oc-subtitle-weight));color:var(--oc-subtitle-computed-color,var(--oc-text-secondary));margin-bottom:8px}.oc-table-source{font-size:var(--oc-source-computed-size,var(--oc-source-size));color:var(--oc-source-computed-color,var(--oc-text-muted))}.oc-table-footer-text{font-size:var(--oc-footer-computed-size,var(--oc-source-size));color:var(--oc-footer-computed-color,var(--oc-text-muted))}.oc-table-scroll{overflow-x:auto}.oc-table--sticky th:first-child{z-index:1;background:var(--oc-bg);position:sticky;left:0}.oc-table--sticky td:first-child{z-index:1;background:var(--oc-bg);position:sticky;left:0}.oc-table-sort-btn{cursor:pointer;vertical-align:middle;background:0 0;border:none;flex-direction:column;align-items:center;gap:2px;margin-left:6px;padding:2px;display:inline-flex}.oc-table-sort-btn:before{content:"";border-left:5px solid transparent;border-right:5px solid transparent;width:0;height:0;transition:opacity .15s,border-color .15s;display:block}.oc-table-sort-btn:after{content:"";border-left:5px solid transparent;border-right:5px solid transparent;width:0;height:0;transition:opacity .15s,border-color .15s;display:block}.oc-table-sort-btn:before{border-bottom:4.5px solid var(--oc-text-secondary);opacity:.35}.oc-table-sort-btn:after{border-top:4.5px solid var(--oc-text-secondary);opacity:.35}.oc-table-sort-btn:hover:before{opacity:.75}.oc-table-sort-btn:hover:after{opacity:.75}th[aria-sort=ascending] .oc-table-sort-btn:before{opacity:1;border-bottom-color:var(--oc-text)}th[aria-sort=ascending] .oc-table-sort-btn:after{opacity:.15}th[aria-sort=descending] .oc-table-sort-btn:after{opacity:1;border-top-color:var(--oc-text)}th[aria-sort=descending] .oc-table-sort-btn:before{opacity:.15}.oc-table-search input,.oc-series-search-input{border:1px solid var(--oc-border);border-radius:var(--oc-radius-md);background:var(--oc-bg);width:100%;color:var(--oc-text);box-sizing:border-box;font-family:inherit;font-size:13px;transition:border-color .15s}.oc-table-search input::-ms-input-placeholder{color:var(--oc-text-muted);font-size:13px}.oc-table-search input::placeholder{color:var(--oc-text-muted);font-size:13px}.oc-series-search-input::-ms-input-placeholder{color:var(--oc-text-muted);font-size:13px}.oc-series-search-input::placeholder{color:var(--oc-text-muted);font-size:13px}.oc-table-search input:focus,.oc-series-search-input:focus{border-color:var(--oc-focus);box-shadow:0 0 0 3px var(--oc-focus-ring);outline:none}.oc-table-search{padding:8px 0}.oc-table-search input{padding:8px 12px}.oc-table-pagination{color:var(--oc-text-secondary);justify-content:space-between;align-items:center;padding:12px 0 4px;font-size:13px;display:flex}.oc-table-pagination button{border:1px solid var(--oc-border);border-radius:var(--oc-radius-md);background:var(--oc-bg);color:var(--oc-text);cursor:pointer;padding:6px 14px;font-family:inherit;font-size:13px;transition:background .15s,border-color .15s}.oc-table-pagination button:disabled{opacity:.35;cursor:not-allowed}.oc-table-pagination button:hover:not(:disabled){background:var(--oc-hover-bg);border-color:var(--oc-axis)}.oc-table-pagination button:focus-visible{outline:2px solid var(--oc-focus);outline-offset:1px}.oc-table-pagination-info{font-variant-numeric:tabular-nums}.oc-table-pagination-btns{gap:8px;display:flex}.oc-table-bar{position:relative}.oc-table-bar-fill{border-radius:var(--oc-radius-sm);opacity:.18;pointer-events:none;position:absolute;top:8px;bottom:8px;left:0}.oc-table-bar-value{z-index:1;position:relative}.oc-table-sparkline{width:100%;display:block;position:relative}.oc-table-sparkline svg{width:100%;display:block;overflow:visible}.oc-table-sparkline-dot{border-radius:50%;width:5px;height:5px;position:absolute}.oc-table-sparkline-labels{justify-content:space-between;font-size:11px;line-height:1;display:flex}.oc-table-image{vertical-align:middle;display:inline-block}.oc-table-image img{object-fit:cover}.oc-table-image-rounded img{border-radius:50%}.oc-table-flag{font-size:1.2em}:is(.oc-table--condensed,.oc-table--compact) th{padding:8px 12px;font-size:13px;line-height:24px}:is(.oc-table--condensed,.oc-table--compact) td{padding:8px 12px;font-size:13px;line-height:24px}:is(.oc-table--condensed,.oc-table--compact) th{font-size:11px}:is(.oc-table--condensed,.oc-table--compact) .oc-table-bar-fill{top:5px;bottom:5px}.oc-table--relaxed th{padding:16px 20px;line-height:24px}.oc-table--relaxed td{padding:16px 20px;line-height:24px}.oc-table--relaxed .oc-table-bar-fill{top:12px;bottom:12px}.oc-table--striped tbody tr:nth-child(2n){background:color-mix(in srgb, var(--oc-text) 3%, transparent)}.oc-table-delta{border-radius:var(--oc-radius-md);font-variant-numeric:tabular-nums;align-items:baseline;gap:4px;padding:2px 8px;font-weight:500;line-height:16px;display:inline-flex}.oc-table-delta-arrow{font-size:.8em;line-height:1}.oc-table-delta--positive{color:var(--oc-positive);background:var(--oc-positive-tint)}.oc-table-delta--negative{color:var(--oc-negative);background:var(--oc-negative-tint)}.oc-table-delta--neutral{color:var(--oc-text-muted);background:color-mix(in srgb, var(--oc-text) 6%, transparent)}.oc-table-chip{border-radius:var(--oc-radius-full);white-space:nowrap;align-items:center;gap:6px;padding:2px 8px;font-size:12px;font-weight:500;line-height:16px;display:inline-flex}.oc-table-chip-dot{border-radius:50%;flex:none;width:6px;height:6px}tfoot.oc-table-total{z-index:2;background:var(--oc-bg);position:sticky;bottom:0}tfoot.oc-table-total td{border-bottom:none;border-top:1px solid var(--oc-border);font-weight:600}.oc-table--cards .oc-table-scroll{overflow-x:hidden}.oc-table--cards table{table-layout:fixed}.oc-table--cards thead{clip-path:inset(50%);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}.oc-table--cards tbody tr{border-bottom:1px solid var(--oc-border);grid-template-columns:repeat(2,minmax(0,1fr));align-items:baseline;gap:2px 12px;padding:10px 12px;display:grid}.oc-table--cards tfoot tr{border-bottom:1px solid var(--oc-border);grid-template-columns:repeat(2,minmax(0,1fr));align-items:baseline;gap:2px 12px;padding:10px 12px;display:grid}.oc-table--cards td{overflow-wrap:anywhere;text-align:right;border-bottom:none;width:auto;max-width:100%;padding:0;display:block}.oc-table--cards td:before{content:attr(data-label);letter-spacing:.04em;text-transform:uppercase;color:var(--oc-text-muted);text-align:left;font-size:11px;font-weight:500;display:block}.oc-table--cards td[data-label=""]:before{content:none}.oc-table--cards td[data-priority="1"]{text-align:left;grid-column:1/-1;font-size:15px;font-weight:600}.oc-table--cards td[data-priority="1"]:before{content:none}.oc-table--cards td[data-priority="3"]{display:none}.oc-table--cards .oc-table-sparkline{width:100%}.oc-table--clickable tbody tr{cursor:pointer}.oc-table--clickable tbody tr:hover{background:var(--oc-hover-bg)}.oc-table-cell-focus{outline:2px solid var(--oc-focus);outline-offset:-2px}.oc-table-empty{text-align:center;color:var(--oc-text-secondary);padding:32px 16px;font-size:14px;font-style:italic}.oc-graph-wrapper{background:var(--oc-bg);font-family:var(--oc-font-family);width:100%;height:100%;position:relative;overflow:hidden}.oc-graph-canvas{cursor:grab;width:100%;height:100%;display:block}.oc-graph-canvas--dragging{cursor:grabbing}.oc-graph-3d,.oc-graph-3d .scene-container{width:100%;height:100%;display:block}.oc-graph-3d{cursor:grab}.oc-graph-3d .scene-container canvas{width:100%;height:100%;display:block}.oc-graph-chrome{z-index:2;pointer-events:none;padding:16px 16px 8px;position:absolute;top:0;left:0;right:0}.oc-graph-chrome .oc-title{font-size:var(--oc-title-size);font-weight:var(--oc-title-weight);letter-spacing:var(--oc-title-tracking);color:var(--oc-text);--_stroke:color-mix(in srgb, var(--oc-bg) 80%, transparent);text-shadow:-2px -2px 0 var(--_stroke), 2px -2px 0 var(--_stroke), -2px 2px 0 var(--_stroke), 2px 2px 0 var(--_stroke), 0 -2px 0 var(--_stroke), 0 2px 0 var(--_stroke), -2px 0 0 var(--_stroke), 2px 0 0 var(--_stroke);margin:0 0 4px}.oc-graph-chrome .oc-subtitle{font-size:var(--oc-subtitle-size);color:var(--oc-text-secondary);--_stroke:color-mix(in srgb, var(--oc-bg) 80%, transparent);text-shadow:-1px -1px 0 var(--_stroke), 1px -1px 0 var(--_stroke), -1px 1px 0 var(--_stroke), 1px 1px 0 var(--_stroke);margin:0}.oc-graph-legend{background:color-mix(in srgb, var(--oc-bg) 88%, transparent);border:1px solid var(--oc-border);border-radius:var(--oc-radius-lg);color:var(--oc-text-secondary);max-height:200px;padding:8px 12px;font-size:12px;position:absolute;top:12px;right:12px;overflow-y:auto}.oc-graph-legend-item{width:100%;font:inherit;color:inherit;text-align:left;cursor:default;background:0 0;border:none;align-items:center;gap:6px;margin:0;padding:2px 0;display:flex}button.oc-graph-legend-item{cursor:pointer;border-radius:var(--oc-radius-sm)}button.oc-graph-legend-item:hover{background:var(--oc-focus-ring)}button.oc-graph-legend-item:focus-visible{outline:2px solid var(--oc-focus);outline-offset:1px}.oc-graph-legend-item--inactive{opacity:.45}.oc-graph-legend-label{flex:auto}.oc-graph-legend-count{color:var(--oc-text-secondary);font-variant-numeric:tabular-nums;margin-left:auto;padding-left:8px}.oc-graph-legend-swatch{border-radius:50%;flex-shrink:0;width:10px;height:10px}.oc-graph-legend-swatch--line{border-radius:1px;width:12px;height:3px}.oc-graph-search{position:absolute;top:12px;left:12px}.oc-graph-search input{font-family:var(--oc-font-family);font-size:var(--oc-body-size);border:1px solid var(--oc-border);border-radius:var(--oc-radius-md);background:color-mix(in srgb, var(--oc-bg) 88%, transparent);color:var(--oc-text);outline:none;padding:6px 10px}.oc-graph-search input:focus{border-color:var(--oc-focus);box-shadow:0 0 0 2px var(--oc-focus-ring-strong)}.oc-sankey:not(.oc-animate) .oc-sankey-link path{transition:fill-opacity var(--oc-hover-duration) ease-out}.oc-sankey:not(.oc-animate) .oc-sankey-node,.oc-sankey:not(.oc-animate) .oc-sankey-label{transition:opacity var(--oc-hover-duration) ease-out}.oc-sankey-label{pointer-events:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.oc-chart[data-display=sparkline]{margin:0;padding:0;display:block}.oc-chart[data-display=sparkline] .oc-pulse{animation:.6s ease-out oc-pulse}@media (prefers-reduced-motion:reduce){.oc-chart[data-display=sparkline] .oc-pulse{animation:none}}}@layer oc.animation{.oc-table-wrapper.oc-animate>.oc-chrome{animation:oc-enter-fade calc(var(--oc-animation-duration) * .6) var(--oc-animation-ease,var(--oc-ease-smooth)) both}.oc-table-wrapper.oc-animate thead{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .4) var(--oc-animation-ease,var(--oc-ease-smooth)) both}.oc-table-wrapper.oc-animate tbody tr{animation:oc-table-enter-row var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0))}.oc-table-wrapper.oc-animate tbody td{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0))}.oc-table-wrapper.oc-animate td.oc-table-heatmap{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .7) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .3)}.oc-table-wrapper.oc-animate td.oc-table-category{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .7) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .3)}.oc-table-wrapper.oc-animate .oc-table-bar-fill{animation:oc-table-enter-bar-fill calc(var(--oc-animation-duration) * .8) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .3)}.oc-table-wrapper.oc-animate .oc-table-sparkline>svg{animation:oc-enter-line calc(var(--oc-animation-duration) * .8) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .4)}.oc-table-wrapper.oc-animate .oc-table-sparkline-dot{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .3) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .8)}.oc-table-wrapper.oc-animate .oc-table-sparkline-labels{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .3) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-row-index,0) + var(--oc-animation-duration) * .8)}.oc-table-wrapper.oc-animate .oc-table-search{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both}.oc-table-wrapper.oc-animate .oc-table-pagination{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both}@keyframes oc-enter-bar{0%{clip-path:inset(100% 0 0);opacity:0}75%{opacity:1}to{clip-path:inset(0);opacity:1}}@keyframes oc-enter-bar-h{0%{clip-path:inset(0 100% 0 0);opacity:0}75%{opacity:1}to{clip-path:inset(0);opacity:1}}@keyframes oc-enter-line{0%{clip-path:inset(0 100% 0 0);opacity:0}15%{opacity:1}to{clip-path:inset(0);opacity:1}}@keyframes oc-enter-point{0%{opacity:0;transform:scale(.3)}to{opacity:1;transform:scale(1)}}@keyframes oc-enter-map-fill{0%{fill:var(--oc-secondary);opacity:0}to{fill:var(--oc-feature-fill);opacity:var(--oc-feature-target-opacity,1)}}@keyframes oc-enter-map-point{0%{opacity:0;transform:scale(.75)}to{opacity:1;transform:scale(1)}}@keyframes oc-enter-fade-only{0%{opacity:0}to{opacity:1}}@keyframes oc-enter-fade{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}@keyframes oc-table-enter-row{0%{transform:translateY(6px)}to{transform:translateY(0)}}@keyframes oc-table-enter-bar-fill{0%{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0)}}@keyframes oc-pulse{0%{r:var(--oc-pulse-r,4px);fill-opacity:1}45%{r:calc(var(--oc-pulse-r,4px) * 1.8);fill-opacity:.5}to{r:var(--oc-pulse-r,4px);fill-opacity:1}}@keyframes oc-tooltip-in{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}.oc-animate .oc-mark-rect :is(rect,path){animation:oc-enter-bar var(--oc-animation-duration) var(--oc-ease-smooth) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-bar :is(rect,path){animation:oc-enter-bar var(--oc-animation-duration) var(--oc-ease-smooth) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-rect[data-orient=horizontal] :is(rect,path){animation-name:oc-enter-bar-h}.oc-animate .oc-mark-bar[data-orient=horizontal] :is(rect,path){animation-name:oc-enter-bar-h}.oc-animate .oc-mark-rect[data-stack-pos] :is(rect,path){animation-duration:var(--oc-stack-segment-duration,.15s);animation-timing-function:linear;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0) + var(--oc-stack-pos,0) * var(--oc-stack-segment-duration,.15s))}.oc-animate .oc-mark-line{animation:oc-enter-line var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-area{animation:oc-enter-line var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-arc{animation:oc-enter-fade-only var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate circle.oc-mark-point{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .4) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate circle.oc-mark-circle{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .4) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-line~circle.oc-mark-point{animation-delay:calc(var(--oc-animation-duration) * .35 + var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-area~circle.oc-mark-point{animation-delay:calc(var(--oc-animation-duration) * .35 + var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-text text{animation:oc-enter-fade calc(var(--oc-animation-duration) * .6) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-rule line{animation:oc-enter-fade calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-tick line{animation:oc-enter-fade calc(var(--oc-animation-duration) * .5) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-mark-label{animation:oc-enter-fade .25s var(--oc-ease-smooth) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0) + var(--oc-animation-duration) * .7)}.oc-animate .oc-annotation{animation:oc-enter-fade .3s var(--oc-ease-smooth) both;animation-delay:calc(var(--oc-animation-duration) + var(--oc-annotation-delay,.15s))}.oc-animate .oc-tilemap-tile{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .35) ease-in both;animation-delay:var(--oc-tile-delay,0s)}.oc-animate .oc-map-feature{animation:oc-enter-map-fill var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:var(--oc-map-delay,0s)}.oc-animate .oc-map-features[data-bulk-animate]{animation:oc-enter-fade-only var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both}.oc-animate .oc-map-features[data-bulk-animate] .oc-map-feature{animation:none}.oc-animate .oc-map-borders path{animation:oc-enter-fade-only calc(var(--oc-animation-duration) * .4) ease-in both;animation-delay:calc(var(--oc-animation-duration) * .7)}.oc-animate .oc-map-point{animation:oc-enter-map-point calc(var(--oc-animation-duration) * .4) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-duration) * .8 + var(--oc-mark-index,0) * var(--oc-point-stagger,60ms))}.oc-animate .oc-sankey-node rect{animation:oc-enter-fade-only var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-sankey-link path{animation:oc-enter-fade-only var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:calc(var(--oc-animation-duration) * .3 + var(--oc-animation-stagger) * var(--oc-mark-index,0))}.oc-animate .oc-barlist-row{animation:oc-enter-fade calc(var(--oc-animation-duration) * .6) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:var(--oc-row-delay,0s)}.oc-animate .oc-barlist-bar{animation:oc-enter-bar-h var(--oc-animation-duration) var(--oc-animation-ease,var(--oc-ease-smooth)) both;animation-delay:var(--oc-row-delay,0s)}.oc-animate[data-display=sparkline] .oc-mark-line,.oc-animate[data-display=sparkline] .oc-mark-area{animation-timing-function:cubic-bezier(.16,1,.3,1)}}@layer oc.reduced-motion{@media (prefers-reduced-motion:reduce){:is(.oc-root,.oc-chart-root,.oc-table-wrapper,.oc-table-root,.oc-graph-wrapper,.oc-graph-root,.oc-sankey-root,.oc-tilemap-root,.oc-barlist-root,.oc-map-root){transition:none;animation:none}:is(.oc-root,.oc-chart-root,.oc-table-wrapper,.oc-table-root,.oc-graph-wrapper,.oc-graph-root,.oc-sankey-root,.oc-tilemap-root,.oc-barlist-root,.oc-map-root) *{transition:none;animation:none}:is(.oc-root,.oc-chart-root,.oc-table-wrapper,.oc-table-root,.oc-graph-wrapper,.oc-graph-root,.oc-sankey-root,.oc-tilemap-root,.oc-barlist-root,.oc-map-root) :before{transition:none;animation:none}:is(.oc-root,.oc-chart-root,.oc-table-wrapper,.oc-table-root,.oc-graph-wrapper,.oc-graph-root,.oc-sankey-root,.oc-tilemap-root,.oc-barlist-root,.oc-map-root) :after{transition:none;animation:none}}}
|
|
2
2
|
/*# sourceMappingURL=styles.css.map */
|