@opendata-ai/openchart-core 8.0.0 → 8.1.1

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.
@@ -1 +1 @@
1
- {"version":3,"mappings":"AAWA,iBEEE,62DEFA,4lBJAF,eQNE,mIAeA,6BAQA,qEAUA,yDAOA,uCAIA,qCASA,gJAgBA,qGAcA,4EAQA,yFRrFF,qBGNE,6CAIA,2KAQA,wIAOA,+GAMA,+HAQA,wFAOA,oDAYA,8CAIA,wHAQA,4HAQA,2EAMA,6EAMA,4EAWA,8EAgBA,sDAIA,sEAMA,4EAIA,wEAaA,sFAUA,sFASA,gCAMA,mFIhKA,ogBAsBE,4FAOA,iFAOA,gMAWA,iGAMA,0EAKA,mHAQA,yJAUA,oKAUA,uMAkBF,qFAOA,4FDlHA,2EAKA,gCAIA,2CIJA,kJAaA,iHASA,wDAIA,sRAeA,6RAoBA,uFAKA,iGAKA,gDAMA,qEAMA,oIAWA,0DAKA,2RAiBA,yKAWA,0GAMA,wCAIA,mFTtIA,kDAKA,+CAKA,qGASA,wHAUA,yGAWA,qFAKA,0MAYA,oRAeA,6DAOA,mXAoBA,0DAIA,yFAKA,yDIpHA,iGAKE,qFAMA,yDAIA,4DAKA,gGAAA,gGAOA,oJASA,gFAOA,mDAIA,sEAKA,iFAKA,2CAKF,sNAOA,kPAOA,2IAKA,gJAKA,iCASE,0FAAA,0FAaF,oLAYE,mLAAA,kLAaA,yFAKA,qFAKA,4CAAA,2CAOA,+FAKA,6DAMA,4FAKA,+DAaF,iPAaA,wFAAA,8EAAA,yFAAA,+EAMA,+IAOA,+BAGE,wCASF,oJAQE,6OAaE,oEAKA,2GAKA,+FAOJ,4DAIA,+CASA,gCAIA,iHAUA,gDASA,+DAKE,kEAOF,iFAOA,mGAWA,2DAIE,qCAKF,8CAQA,+BASE,qDAAA,qDAMA,qCAUA,6CAIA,kEASF,2EASA,oHQjYA,qIASA,kEAOA,2CAIA,4GASE,8bAkBA,2RAaF,qPAcA,qLAiBA,6DAIE,kEAIA,+FAOF,4CAIA,iCAIA,0HAOA,+EAQA,sEAMA,oDAKE,0OAUE,2GAQJ,qEAYA,iKFrKA,oEXCF,oBSJI,4JAMA,4JAOA,8NAOA,yOAOA,wRAAA,yRAcA,4RAaA,uRAYA,4RAAA,+RAaA,uKAAA,2KK/EF,8GAgBA,kHAgBA,iHAeA,4FAgBA,8IAaA,iGAaA,yDAUA,sGAaA,uFAWA,yFAUA,sGFpIE,yLAAA,wLAOA,qFAAA,oFASA,oRAYA,8MAOA,8MASA,kNAUA,qOAAA,sOAQA,+JAAA,+JAWA,8NAOA,8NAOA,8NAOA,iMAWA,sKAOA,wJAQA,gLASA,8JAKA,+EAKA,2KAQA,mQAYA,0NAQA,8PAYA,uLAMA,6KAeF,wJZrMF,yBeDE,uCACE,6LAAA,+LAAA,qMAAA","sources":["../src/styles/index.css","../src/styles/you-draw-it.css","../src/styles/tokens.css","../src/styles/chrome.css","../src/styles/dark.css","../src/styles/table.css","../src/styles/legend.css","../src/styles/tooltip.css","../src/styles/base.css","../src/styles/table-animation.css","../src/styles/series-search.css","../src/styles/sparkline.css","../src/styles/keyframes.css","../src/styles/graph.css","../src/styles/animation.css","../src/styles/reduced-motion.css"],"sourcesContent":["/**\n * @opendata-ai/openchart-core styles\n *\n * CSS with oc- prefix for all class names.\n * CSS custom properties for theme overrides.\n * Dark mode via .oc-dark class on the container.\n * Cascade layers: oc.tokens, oc.base, oc.components, oc.animation, oc.reduced-motion.\n */\n\n/* For optimal typography, load Inter: https://fonts.google.com/specimen/Inter */\n\n@layer oc.tokens, oc.base, oc.components, oc.animation, oc.reduced-motion;\n\n@import \"./tokens.css\";\n@import \"./dark.css\";\n@import \"./base.css\";\n@import \"./chrome.css\";\n@import \"./tooltip.css\";\n@import \"./legend.css\";\n@import \"./series-search.css\";\n@import \"./you-draw-it.css\";\n@import \"./table.css\";\n@import \"./table-animation.css\";\n@import \"./graph.css\";\n@import \"./sparkline.css\";\n@import \"./keyframes.css\";\n@import \"./animation.css\";\n@import \"./reduced-motion.css\";\n","@layer oc.components {\n /* ---------------------------------------------------------------------------\n * You draw it (youDrawIt)\n *\n * NYT-style draw-then-reveal. SVG-space elements (hatched region, guess path,\n * reveal clip) render inside the chart SVG; the prompt + skip-to-reveal button\n * are an absolutely-positioned HTML overlay the vanilla adapter sizes over the\n * drawing region (inline left/top/width/height). All colors come from --oc-*\n * tokens so they flip in .oc-dark.\n * --------------------------------------------------------------------------- */\n\n /* Hatched \"draw here\" region. Fill comes from an SVG <pattern> (line color =\n * the target series color); this only governs overall opacity + fade-out. */\n .oc-ydi-region {\n opacity: 0.35;\n transition: opacity 0.4s ease;\n }\n\n .oc-ydi-region[data-ydi-hidden=\"true\"] {\n opacity: 0;\n }\n\n /* The vertical rule at `from` where drawing begins. */\n .oc-ydi-boundary {\n stroke: var(--oc-text-muted);\n stroke-width: 1;\n stroke-dasharray: 3 3;\n stroke-opacity: 0.6;\n }\n\n /* Reader's guess: a distinct pen-like stroke that reads as \"yours\" against the\n * solid real line. Dashed + focus-blue so it stays legible in both modes. */\n .oc-ydi-guess {\n stroke: var(--oc-focus);\n stroke-width: 2.5;\n stroke-linecap: round;\n stroke-linejoin: round;\n stroke-dasharray: 1 6;\n }\n\n /* Host-supplied comparison line (\"what everyone else guessed\"). Muted so it\n * sits behind the reader's guess and the real line. */\n .oc-ydi-comparison {\n stroke: var(--oc-text-muted);\n stroke-width: 1.5;\n stroke-opacity: 0.5;\n stroke-dasharray: 4 4;\n }\n\n /* Reveal clip rect: the width animates from the `from` boundary to the full\n * area on reveal, wiping the real line in left-to-right. The vanilla adapter\n * adds .oc-ydi-clip-animate right before setting the final width; reduced\n * motion skips the class so the width snaps. */\n .oc-ydi-clip-rect.oc-ydi-clip-animate {\n transition: width 0.7s cubic-bezier(0.22, 1, 0.36, 1);\n }\n\n /* HTML controls overlay: prompt (top) + reveal button (bottom-right). */\n .oc-ydi-controls {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: flex-end;\n pointer-events: none;\n font-family: var(--oc-font-family);\n padding: 8px;\n box-sizing: border-box;\n z-index: 10;\n }\n\n .oc-ydi-prompt {\n align-self: center;\n max-width: 100%;\n padding: 6px 12px;\n border-radius: 6px;\n background: var(--oc-card);\n border: 1px solid var(--oc-border);\n color: var(--oc-text-secondary);\n font-size: 13px;\n font-weight: 500;\n line-height: 16px;\n text-align: center;\n box-shadow: var(--oc-tooltip-shadow);\n }\n\n .oc-ydi-controls.oc-ydi-revealed .oc-ydi-prompt {\n display: none;\n }\n\n /* Skip-to-reveal button. pointer-events re-enabled (the overlay disables it)\n * so it stays clickable and keyboard-reachable. Min height enforced inline by\n * the adapter (>= 24px effective touch target). */\n .oc-ydi-reveal-button {\n pointer-events: auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 6px 14px;\n border: 1px solid var(--oc-border);\n border-radius: 6px;\n background: var(--oc-bg);\n color: var(--oc-text);\n font-family: inherit;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n box-shadow: var(--oc-tooltip-shadow);\n transition:\n background-color 0.15s,\n border-color 0.15s;\n }\n\n .oc-ydi-reveal-button:hover {\n background: var(--oc-hover-bg);\n }\n\n .oc-ydi-reveal-button:focus-visible {\n outline: 2px solid var(--oc-focus);\n outline-offset: 2px;\n }\n\n .oc-ydi-reveal-button:disabled {\n opacity: 0.5;\n cursor: default;\n }\n}\n","/* GENERATED FILE — do not edit. Source: packages/core/src/styles/token-definitions.ts. Regenerate: bun run generate:tokens */\n\n@layer oc.tokens {\n /* ---------------------------------------------------------------------------\n * Custom properties (light mode defaults)\n *\n * These are FALLBACK defaults. At runtime, the JS theme engine stamps the\n * resolved theme as inline --oc-* custom properties on each .oc-root\n * container (see packages/vanilla/src/theme-tokens.ts). These CSS values\n * serve as fallbacks for contexts where JS hasn't mounted yet (SSR, static\n * HTML examples).\n * --------------------------------------------------------------------------- */\n\n .oc-root,\n .oc-chart-root,\n .oc-table-wrapper,\n .oc-table-root,\n .oc-graph-wrapper,\n .oc-graph-root,\n .oc-sankey-root,\n .oc-tilemap-root,\n .oc-barlist-root,\n .oc-map-root {\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n\n /* Opt out of iOS text auto-inflation so measured widths match rendered text. */\n -webkit-text-size-adjust: 100%;\n text-size-adjust: 100%;\n\n --oc-font-family:\n \"Inter Variable\", Inter, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n --oc-font-mono: \"JetBrains Mono\", \"Fira Code\", \"Cascadia Code\", monospace;\n\n /* Animation easing presets via CSS linear() */\n --oc-ease-smooth: linear(\n 0,\n 0.157,\n 0.438,\n 0.64,\n 0.766,\n 0.85,\n 0.906,\n 0.941,\n 0.964,\n 0.978,\n 0.988,\n 0.994,\n 0.998,\n 1\n );\n --oc-ease-snappy: linear(\n 0,\n 0.012,\n 0.048,\n 0.108,\n 0.194,\n 0.302,\n 0.426,\n 0.559,\n 0.69,\n 0.808,\n 0.905,\n 0.973,\n 1.013,\n 1.028,\n 1.023,\n 1.006,\n 0.984,\n 0.966,\n 0.957,\n 0.957,\n 0.964,\n 0.975,\n 0.986,\n 0.995,\n 1,\n 1.003,\n 1.002,\n 1,\n 0.998,\n 0.998,\n 0.999,\n 1\n );\n\n /* Animation timing defaults */\n --oc-animation-duration: 500ms;\n --oc-animation-stagger: 80ms;\n --oc-annotation-delay: 200ms;\n\n /* Typography scale (editorial design system) */\n --oc-title-size: 26px;\n --oc-title-weight: 590;\n --oc-title-tracking: -0.022em;\n --oc-subtitle-size: 14px;\n --oc-subtitle-weight: 450;\n --oc-source-size: 11px;\n --oc-source-weight: 450;\n --oc-body-size: 13px;\n --oc-eyebrow-size: 11px;\n --oc-eyebrow-weight: 550;\n --oc-eyebrow-tracking: 0.08em;\n\n /* Surfaces (light mode defaults) */\n --oc-bg: #ffffff;\n --oc-card: #ffffff;\n --oc-secondary: #f4f4f5; /* zinc-100, raised surface */\n\n /*\n * Text levels. Names invert across modes: in light mode \"muted\" sits at\n * a lighter zinc step than \"subtle\" because both are picked relative to\n * the active background, not from a fixed lightness ladder. The intent\n * is \"muted = first step away from primary text\"; \"subtle = next step\n * down\"; etc.\n *\n * Light mode: text=zinc-950, secondary=zinc-700, muted=zinc-500,\n * subtle=zinc-400, faint=zinc-300\n * Dark mode (dark.css): inverts the surface tokens but keeps the same\n * muted -> subtle -> faint progression away from primary.\n */\n --oc-text: #09090b;\n --oc-text-secondary: #3f3f46;\n --oc-text-muted: #71717a;\n --oc-text-faint: #d4d4d8;\n\n /* Lines */\n --oc-gridline: rgba(0, 0, 0, 0.1);\n --oc-axis: rgba(0, 0, 0, 0.1);\n --oc-border: rgba(0, 0, 0, 0.08);\n --oc-border-radius: 2px;\n\n /* Brand and semantic */\n --oc-accent: #06b6d4;\n --oc-accent-strong: #0891b2; /* darker cyan for line strokes on light bg */\n --oc-positive: #10b981;\n --oc-negative: #e11d48;\n --oc-focus: #3b82f6;\n /* static, intentionally NOT derived from --oc-focus and NOT flipped in dark mode */\n --oc-focus-ring: rgba(59, 130, 246, 0.1);\n --oc-focus-ring-strong: rgba(59, 130, 246, 0.25);\n --oc-editable-hover: rgba(79, 70, 229, 0.35); /* edit-mode hover outline, indigo one-off */\n\n /* Spacing scale (4px base) — only --oc-space-2/4 are consumed (JS-stamped) */\n --oc-space-2: 8px;\n --oc-space-4: 16px;\n\n /* Interactive states */\n --oc-hover-bg: rgba(0, 0, 0, 0.025);\n --oc-tooltip-bg: rgba(255, 255, 255, 0.88);\n --oc-tooltip-border: rgba(0, 0, 0, 0.08);\n --oc-tooltip-shadow: 0 2px 8px rgba(0, 0, 0, 0.08), 0 0 1px rgba(0, 0, 0, 0.12);\n --oc-tooltip-text: #09090b;\n --oc-legend-text: #3f3f46;\n }\n}\n","@layer oc.components {\n /* ---------------------------------------------------------------------------\n * Chrome (eyebrow, title, subtitle, source, byline, footer, brand)\n * --------------------------------------------------------------------------- */\n\n .oc-chrome {\n font-family: var(--oc-font-family);\n }\n\n .oc-eyebrow {\n font-size: var(--oc-eyebrow-size);\n font-weight: var(--oc-eyebrow-weight);\n letter-spacing: var(--oc-eyebrow-tracking);\n text-transform: uppercase;\n fill: var(--oc-accent);\n }\n\n .oc-title {\n font-size: var(--oc-title-size);\n font-weight: var(--oc-title-weight);\n letter-spacing: var(--oc-title-tracking);\n fill: var(--oc-text);\n }\n\n .oc-subtitle {\n font-size: var(--oc-subtitle-size);\n font-weight: var(--oc-subtitle-weight);\n fill: var(--oc-text-muted);\n }\n\n .oc-source,\n .oc-byline,\n .oc-footer {\n font-size: var(--oc-source-size);\n font-weight: var(--oc-source-weight);\n fill: var(--oc-text-muted);\n }\n\n .oc-brand {\n font-size: 11px;\n font-weight: 510;\n letter-spacing: 0.02em;\n fill: var(--oc-text-faint);\n }\n\n .oc-brand-dot {\n fill: var(--oc-accent);\n }\n\n .oc-eyebrow-dot {\n fill: var(--oc-accent);\n }\n\n /* ---------------------------------------------------------------------------\n * Metric bar (.oc-metrics) — optional row of label/value cells above chart\n * --------------------------------------------------------------------------- */\n\n .oc-metrics {\n font-family: var(--oc-font-family);\n }\n\n .oc-metric-label {\n font-size: 10px;\n font-weight: 510;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n fill: var(--oc-text-muted);\n }\n\n .oc-metric-value {\n font-size: 22px;\n font-weight: 510;\n letter-spacing: -0.01em;\n fill: var(--oc-text);\n font-variant-numeric: tabular-nums;\n }\n\n .oc-metric-delta-up {\n fill: var(--oc-positive);\n font-size: 12px;\n font-weight: 510;\n }\n\n .oc-metric-delta-down {\n fill: var(--oc-negative);\n font-size: 12px;\n font-weight: 510;\n }\n\n .oc-metric-secondary {\n fill: var(--oc-positive);\n font-size: 12px;\n font-weight: 400;\n }\n\n /* ---------------------------------------------------------------------------\n * Axis tick labels — inline variant sits above each gridline at the\n * chart-area left edge (no left gutter). Used by line/area y-axes.\n * --------------------------------------------------------------------------- */\n\n .oc-axis-tick-inline {\n font-size: 11px;\n font-weight: 400;\n fill: var(--oc-text-muted);\n }\n\n /* ---------------------------------------------------------------------------\n * Endpoint labels (right-side per-series column for line/area charts)\n *\n * Swatch idiom: a thicker colored stroke segment with an optional thinner\n * lighter strip below. Mirrors the refreshed traditional legend swatch so a\n * single chart never shows two swatch idioms (mock-2 alignment). Per-series\n * color is stamped inline by the renderer; CSS tokens here cover the muted\n * value text and the leader line so theme overrides flow through naturally.\n * --------------------------------------------------------------------------- */\n\n .oc-endpoint-labels {\n font-family: var(--oc-font-family);\n }\n\n .oc-endpoint-label {\n /* Color set inline per series; this fallback only kicks in if the renderer\n ever omits the inline fill. */\n fill: var(--oc-endpoint-label-color, var(--oc-text));\n }\n\n .oc-endpoint-value {\n fill: var(--oc-endpoint-value-color, var(--oc-text-muted));\n }\n\n .oc-endpoint-leader {\n stroke: var(--oc-endpoint-leader-color, currentColor);\n }\n\n /* .oc-endpoint-marker — fill/stroke set inline (open-ring style: bg fill, series stroke). */\n /* .oc-endpoint-swatch — color set inline per series. */\n\n /* ---------------------------------------------------------------------------\n * Annotation dot + subtitle (text-annotation extensions)\n * --------------------------------------------------------------------------- */\n\n /* .oc-annotation-dot — fill/stroke resolved by the engine (open-ring default = bg fill, series stroke). */\n\n .oc-annotation-subtitle {\n fill: var(--oc-annotation-subtitle-color, var(--oc-text-muted));\n }\n\n /* ---------------------------------------------------------------------------\n * Table footer chrome\n * --------------------------------------------------------------------------- */\n\n /* Footer row: source/footer text on the left, brand watermark on the right,\n * sharing a single baseline. gap keeps them apart if the row gets narrow. */\n .oc-table-footer-row {\n display: flex;\n align-items: baseline;\n gap: 16px;\n padding: 16px 16px 4px;\n }\n\n /* Chrome block sits inside the flex row now, so it no longer owns the\n * footer's top padding or horizontal inset (the row provides both). */\n .oc-chrome-footer {\n padding-top: 0;\n }\n\n /* Brand watermark: pushed to the right edge (margin-left:auto holds it there\n * even when the source text is absent), never shrinks below its own text. */\n .oc-table-footer-row .oc-table-ref {\n margin-left: auto;\n flex-shrink: 0;\n text-align: right;\n }\n}\n","/* GENERATED FILE — do not edit. Source: packages/core/src/styles/token-definitions.ts. Regenerate: bun run generate:tokens */\n\n@layer oc.tokens {\n /* ---------------------------------------------------------------------------\n * Dark mode overrides (fallback defaults)\n *\n * At runtime, the JS theme engine stamps dark-adapted --oc-* custom\n * properties on each .oc-root container. These CSS values serve as\n * fallbacks for contexts where JS hasn't mounted yet.\n * --------------------------------------------------------------------------- */\n\n .oc-dark {\n /* Surfaces (zinc-based achromatic ramp) */\n --oc-bg: #09090b;\n --oc-card: #111113;\n --oc-secondary: #27272a;\n\n /* Text levels — see tokens.css for the cross-mode naming rationale */\n --oc-text: #f7f8f8;\n --oc-text-secondary: #d0d6e0;\n --oc-text-muted: #a1a1aa;\n --oc-text-faint: #52525b;\n\n /* Lines */\n --oc-gridline: rgba(255, 255, 255, 0.05);\n --oc-axis: rgba(255, 255, 255, 0.1);\n --oc-border: rgba(255, 255, 255, 0.1);\n\n /* Brand and semantic — accent stays cyan (no darkening on dark bg) */\n --oc-accent: #06b6d4;\n --oc-accent-strong: #06b6d4;\n --oc-positive: #34d399;\n --oc-negative: #fb7185;\n --oc-focus: #60a5fa;\n\n /* Interactive states */\n --oc-hover-bg: rgba(255, 255, 255, 0.05);\n --oc-tooltip-bg: rgba(17, 17, 19, 0.92);\n --oc-tooltip-border: rgba(255, 255, 255, 0.08);\n --oc-tooltip-shadow: 0 2px 8px rgba(0, 0, 0, 0.3), 0 0 1px rgba(0, 0, 0, 0.4);\n --oc-tooltip-text: #f7f8f8;\n --oc-legend-text: #d0d6e0;\n }\n}\n","@layer oc.components {\n /* ---------------------------------------------------------------------------\n * Table base\n * --------------------------------------------------------------------------- */\n\n .oc-table-wrapper {\n font-family: var(--oc-font-family);\n color: var(--oc-text);\n background: var(--oc-bg);\n\n & > .oc-chrome {\n margin-bottom: 16px;\n padding-left: 16px;\n padding-right: 16px;\n }\n\n & > .oc-chrome:first-child {\n padding-top: 4px;\n }\n\n & table {\n width: 100%;\n border-collapse: collapse;\n }\n\n & th,\n & td {\n padding: 10px 16px;\n text-align: left;\n border-bottom: 1px solid var(--oc-border);\n }\n\n & th {\n font-weight: 600;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--oc-text-secondary);\n white-space: nowrap;\n }\n\n & thead {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--oc-bg);\n }\n\n & thead th {\n border-bottom-width: 2px;\n }\n\n & td {\n font-size: 14px;\n font-variant-numeric: tabular-nums;\n }\n\n & th:focus {\n outline: 2px solid var(--oc-focus);\n outline-offset: -2px;\n }\n\n & tbody:focus {\n outline: none;\n }\n }\n\n .oc-table-title {\n margin-bottom: 4px;\n font-size: var(--oc-title-computed-size, var(--oc-title-size));\n font-weight: var(--oc-title-computed-weight, var(--oc-title-weight));\n color: var(--oc-title-computed-color, var(--oc-text));\n }\n\n .oc-table-subtitle {\n margin-bottom: 8px;\n font-size: var(--oc-subtitle-computed-size, var(--oc-subtitle-size));\n font-weight: var(--oc-subtitle-computed-weight, var(--oc-subtitle-weight));\n color: var(--oc-subtitle-computed-color, var(--oc-text-secondary));\n }\n\n .oc-table-source {\n font-size: var(--oc-source-computed-size, var(--oc-source-size));\n color: var(--oc-source-computed-color, var(--oc-text-muted));\n }\n\n .oc-table-footer-text {\n font-size: var(--oc-footer-computed-size, var(--oc-source-size));\n color: var(--oc-footer-computed-color, var(--oc-text-muted));\n }\n\n .oc-table-scroll {\n overflow-x: auto;\n }\n\n /* ---------------------------------------------------------------------------\n * Sticky first column\n * --------------------------------------------------------------------------- */\n\n .oc-table--sticky {\n & th:first-child,\n & td:first-child {\n position: sticky;\n left: 0;\n z-index: 1;\n background: var(--oc-bg);\n }\n }\n\n /* ---------------------------------------------------------------------------\n * Sort controls\n * --------------------------------------------------------------------------- */\n\n .oc-table-sort-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 2px;\n margin-left: 6px;\n display: inline-flex;\n flex-direction: column;\n align-items: center;\n vertical-align: middle;\n gap: 2px;\n\n &::before,\n &::after {\n content: \"\";\n display: block;\n width: 0;\n height: 0;\n border-left: 5px solid transparent;\n border-right: 5px solid transparent;\n transition:\n opacity 0.15s,\n border-color 0.15s;\n }\n\n &::before {\n border-bottom: 4.5px solid var(--oc-text-secondary);\n opacity: 0.45;\n }\n\n &::after {\n border-top: 4.5px solid var(--oc-text-secondary);\n opacity: 0.45;\n }\n\n &:hover::before,\n &:hover::after {\n opacity: 0.75;\n }\n }\n\n th[aria-sort=\"ascending\"] .oc-table-sort-btn {\n &::before {\n opacity: 1;\n border-bottom-color: var(--oc-text);\n }\n\n &::after {\n opacity: 0.15;\n }\n }\n\n th[aria-sort=\"descending\"] .oc-table-sort-btn {\n &::after {\n opacity: 1;\n border-top-color: var(--oc-text);\n }\n\n &::before {\n opacity: 0.15;\n }\n }\n\n /* ---------------------------------------------------------------------------\n * Search\n * --------------------------------------------------------------------------- */\n\n /* Shared search-input skin (table + series-search).\n Padding excluded: it diverges (table: 8px 12px; series: 0 10px 0 28px)\n and series-search.css is imported before table.css — a padding declaration\n here would beat the series-search override by source order. */\n .oc-table-search input,\n .oc-series-search-input {\n width: 100%;\n border: 1px solid var(--oc-border);\n border-radius: 6px;\n font-size: 13px;\n font-family: inherit;\n background: var(--oc-bg);\n color: var(--oc-text);\n box-sizing: border-box;\n transition: border-color 0.15s;\n }\n\n .oc-table-search input::placeholder,\n .oc-series-search-input::placeholder {\n color: var(--oc-text-muted);\n font-size: 13px;\n }\n\n .oc-table-search input:focus,\n .oc-series-search-input:focus {\n outline: none;\n border-color: var(--oc-focus);\n box-shadow: 0 0 0 3px var(--oc-focus-ring);\n }\n\n .oc-table-search {\n padding: 8px 0;\n\n & input {\n padding: 8px 12px;\n }\n }\n\n /* ---------------------------------------------------------------------------\n * Pagination\n * --------------------------------------------------------------------------- */\n\n .oc-table-pagination {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 0 4px;\n font-size: 13px;\n color: var(--oc-text-secondary);\n\n & button {\n padding: 6px 14px;\n border: 1px solid var(--oc-border);\n border-radius: 6px;\n background: var(--oc-bg);\n color: var(--oc-text);\n cursor: pointer;\n font-size: 13px;\n font-family: inherit;\n transition:\n background 0.15s,\n border-color 0.15s;\n\n &:disabled {\n opacity: 0.35;\n cursor: not-allowed;\n }\n\n &:hover:not(:disabled) {\n background: var(--oc-hover-bg);\n border-color: var(--oc-axis);\n }\n\n &:focus-visible {\n outline: 2px solid var(--oc-focus);\n outline-offset: 1px;\n }\n }\n }\n\n .oc-table-pagination-info {\n font-variant-numeric: tabular-nums;\n }\n\n .oc-table-pagination-btns {\n display: flex;\n gap: 8px;\n }\n\n /* ---------------------------------------------------------------------------\n * Bar cells\n * --------------------------------------------------------------------------- */\n\n .oc-table-bar {\n position: relative;\n }\n\n .oc-table-bar-fill {\n position: absolute;\n top: 6px;\n left: 0;\n bottom: 6px;\n border-radius: 2px;\n opacity: 0.15;\n pointer-events: none;\n }\n\n .oc-table-bar-value {\n position: relative;\n z-index: 1;\n }\n\n /* ---------------------------------------------------------------------------\n * Sparkline cells\n * --------------------------------------------------------------------------- */\n\n .oc-table-sparkline {\n display: block;\n width: 100%;\n position: relative;\n\n & svg {\n display: block;\n width: 100%;\n overflow: visible;\n }\n }\n\n .oc-table-sparkline-dot {\n position: absolute;\n border-radius: 50%;\n width: 5px;\n height: 5px;\n }\n\n .oc-table-sparkline-labels {\n display: flex;\n justify-content: space-between;\n font-size: 11px;\n line-height: 1;\n }\n\n /* ---------------------------------------------------------------------------\n * Image cells\n * --------------------------------------------------------------------------- */\n\n .oc-table-image {\n display: inline-block;\n vertical-align: middle;\n\n & img {\n object-fit: cover;\n }\n }\n\n .oc-table-image-rounded img {\n border-radius: 50%;\n }\n\n /* ---------------------------------------------------------------------------\n * Flag cells\n * --------------------------------------------------------------------------- */\n\n .oc-table-flag {\n font-size: 1.2em;\n }\n\n /* ---------------------------------------------------------------------------\n * Compact mode\n * --------------------------------------------------------------------------- */\n\n .oc-table--compact {\n & th,\n & td {\n padding: 4px 8px;\n font-size: 13px;\n }\n\n & th {\n font-size: 11px;\n }\n }\n\n /* ---------------------------------------------------------------------------\n * Row hover (when onRowClick is set)\n * --------------------------------------------------------------------------- */\n\n .oc-table--clickable tbody {\n & tr {\n cursor: pointer;\n }\n\n & tr:hover {\n background: var(--oc-hover-bg);\n }\n }\n\n /* ---------------------------------------------------------------------------\n * Keyboard cell focus indicator\n * --------------------------------------------------------------------------- */\n\n .oc-table-cell-focus {\n outline: 2px solid var(--oc-focus);\n outline-offset: -2px;\n }\n\n /* ---------------------------------------------------------------------------\n * Empty state\n * --------------------------------------------------------------------------- */\n\n .oc-table-empty {\n padding: 32px 16px;\n text-align: center;\n color: var(--oc-text-secondary);\n font-size: 14px;\n font-style: italic;\n }\n}\n","@layer oc.components {\n /* ---------------------------------------------------------------------------\n * Legend\n * --------------------------------------------------------------------------- */\n\n .oc-legend {\n font-family: var(--oc-font-family);\n font-size: var(--oc-body-size);\n }\n\n .oc-legend-entry {\n cursor: default;\n }\n\n .oc-legend text {\n fill: var(--oc-legend-text);\n }\n}\n","@layer oc.components {\n /* ---------------------------------------------------------------------------\n * Tooltip\n * Editorial card matching the new design system: zinc surface, cyan accent,\n * Inter Variable, mono numerics. Sized for a slice tooltip that lists every\n * series at the snapped x.\n * --------------------------------------------------------------------------- */\n\n .oc-tooltip {\n position: absolute;\n display: none;\n pointer-events: none;\n z-index: 1000;\n background: var(--oc-tooltip-bg);\n backdrop-filter: blur(14px);\n border: 1px solid var(--oc-tooltip-border);\n border-radius: var(--oc-border-radius, 2px);\n box-shadow: var(--oc-tooltip-shadow);\n color: var(--oc-tooltip-text);\n font-family: var(--oc-font-family);\n font-size: 12px;\n padding: 0;\n max-width: 280px;\n min-width: 160px;\n line-height: 1.4;\n animation: oc-tooltip-in 120ms ease-out;\n transition:\n left 100ms ease-in-out,\n top 100ms ease-in-out;\n\n & .oc-tooltip-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px 6px;\n }\n\n & .oc-tooltip-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n }\n\n & .oc-tooltip-title {\n font-weight: 590;\n font-size: 11px;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--oc-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n & .oc-tooltip-body {\n padding: 6px 12px 10px;\n border-top: 1px solid var(--oc-tooltip-border);\n }\n\n /* No separator when body is the only child */\n & .oc-tooltip-body:first-child {\n border-top: none;\n padding-top: 10px;\n }\n\n & .oc-tooltip-row {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 16px;\n padding: 2px 0;\n }\n\n & .oc-tooltip-row-swatch {\n display: inline-block;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n margin-right: 6px;\n flex-shrink: 0;\n transform: translateY(-1px);\n }\n\n & .oc-tooltip-label {\n color: var(--oc-text-secondary);\n font-size: 12px;\n font-weight: 400;\n white-space: nowrap;\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n }\n\n & .oc-tooltip-value {\n font-weight: 510;\n font-size: 12px;\n font-variant-numeric: tabular-nums;\n color: var(--oc-tooltip-text);\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n }\n\n /* ---------------------------------------------------------------------------\n * Crosshair guideline + per-series snap dots (line/area charts)\n * Animate the snap between adjacent points so the indicator slides instead\n * of jumping. ~50ms ease-in-out keeps the motion crisp without lagging.\n * --------------------------------------------------------------------------- */\n\n .oc-crosshair {\n pointer-events: none;\n transition:\n x1 50ms ease-in-out,\n x2 50ms ease-in-out;\n }\n\n .oc-snap-dots circle {\n pointer-events: none;\n transition:\n cx 50ms ease-in-out,\n cy 50ms ease-in-out;\n }\n}\n","@layer oc.base {\n /* ---------------------------------------------------------------------------\n * Wrapper roots\n * --------------------------------------------------------------------------- */\n\n .oc-chart-root {\n width: 100%;\n height: 100%;\n }\n\n .oc-table-root,\n .oc-graph-root,\n .oc-sankey-root,\n .oc-barlist-root,\n .oc-tilemap-root,\n .oc-map-root {\n width: 100%;\n height: 100%;\n }\n\n .oc-table-root {\n overflow: auto;\n }\n\n /* ---------------------------------------------------------------------------\n * Chart container\n * --------------------------------------------------------------------------- */\n\n .oc-chart {\n font-family: var(--oc-font-family);\n display: block;\n width: 100%;\n }\n\n /* ---------------------------------------------------------------------------\n * BarList SVG container\n * --------------------------------------------------------------------------- */\n\n .oc-tilemap,\n .oc-map {\n display: block;\n width: 100%;\n height: auto;\n }\n\n .oc-map-feature {\n transition: opacity 400ms ease;\n }\n\n .oc-barlist {\n display: block;\n width: 100%;\n }\n\n /* ---------------------------------------------------------------------------\n * Screen reader only utility\n * --------------------------------------------------------------------------- */\n\n .oc-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border-width: 0;\n }\n\n /* ---------------------------------------------------------------------------\n * Editable hover feedback\n * --------------------------------------------------------------------------- */\n\n .oc-editable-hover {\n outline: 1.5px solid var(--oc-editable-hover);\n outline-offset: 2px;\n border-radius: 2px;\n }\n\n /* ---------------------------------------------------------------------------\n * Keyboard focus indicators\n *\n * --oc-focus carries a light value in tokens.css and a dark value in\n * dark.css, so both rings stay visible in either mode.\n * --------------------------------------------------------------------------- */\n\n /* Container ring when the chart itself receives keyboard focus. */\n .oc-root:focus-visible {\n outline: 2px solid var(--oc-focus);\n outline-offset: 2px;\n }\n\n /* Mark ring during arrow-key navigation (see wireKeyboardNav). Outline on\n * SVG elements draws around the mark's bounding box, same technique as\n * .oc-editable-hover above. */\n .oc-mark-focused {\n outline: 2px solid var(--oc-focus);\n outline-offset: 1px;\n border-radius: 2px;\n }\n}\n","@layer oc.animation {\n /* ---------------------------------------------------------------------------\n * Table entrance animations (.oc-table-wrapper.oc-animate)\n * --------------------------------------------------------------------------- */\n\n .oc-table-wrapper.oc-animate {\n /* Chrome (title/subtitle): fade + slide up */\n & > .oc-chrome {\n animation: oc-enter-fade calc(var(--oc-animation-duration) * 0.6)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n }\n\n /* Table header: quick fade before rows start */\n & thead {\n animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.4)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n }\n\n /* Row entrance: slide up only, no opacity (cells handle their own fading\n to avoid opacity compounding between row and cell animations) */\n & tbody tr {\n animation: oc-table-enter-row var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-row-index, 0));\n }\n\n /* Cell text: fade in synced with row slide */\n & tbody td {\n animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.5)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-row-index, 0));\n }\n\n /* Heatmap/category cell backgrounds: longer fade, delayed after row appears */\n & td.oc-table-heatmap,\n & td.oc-table-category {\n animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.7)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(\n var(--oc-animation-stagger) *\n var(--oc-row-index, 0) +\n var(--oc-animation-duration) *\n 0.3\n );\n }\n\n /* Bar fill: clip-path grow only (no opacity change to preserve resting\n opacity: 0.15 from table.css) */\n & .oc-table-bar-fill {\n animation: oc-table-enter-bar-fill calc(var(--oc-animation-duration) * 0.8)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(\n var(--oc-animation-stagger) *\n var(--oc-row-index, 0) +\n var(--oc-animation-duration) *\n 0.3\n );\n }\n\n /* Sparkline SVG: clip-path reveal left-to-right. Targets the SVG directly\n (not the wrapper) to avoid clipping absolutely-positioned dots and labels. */\n & .oc-table-sparkline > svg {\n animation: oc-enter-line calc(var(--oc-animation-duration) * 0.8)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(\n var(--oc-animation-stagger) *\n var(--oc-row-index, 0) +\n var(--oc-animation-duration) *\n 0.4\n );\n }\n\n /* Sparkline dots and labels: fade in after the line draws through */\n & .oc-table-sparkline-dot,\n & .oc-table-sparkline-labels {\n animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.3)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(\n var(--oc-animation-stagger) *\n var(--oc-row-index, 0) +\n var(--oc-animation-duration) *\n 0.8\n );\n }\n\n /* Search and pagination: quick fade on mount */\n & .oc-table-search,\n & .oc-table-pagination {\n animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.5)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n }\n }\n}\n","@layer oc.components {\n /* ---------------------------------------------------------------------------\n * Series search (seriesSearch)\n *\n * DOM combobox overlaid on the SVG band the engine reserves below chrome.\n * The wrapper is absolutely positioned by the vanilla adapter (inline\n * left/top/width/height), so everything here is visual only. All colors come\n * from --oc-* tokens, which flip in .oc-dark.\n * --------------------------------------------------------------------------- */\n\n .oc-series-search {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n font-family: var(--oc-font-family);\n z-index: 10;\n }\n\n /* Chips: removable filter tokens for the current selection. They flow to the\n * left of the input and scroll horizontally rather than wrapping over the\n * chart when a reader picks many series. */\n .oc-series-search-chips {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n overflow-x: auto;\n scrollbar-width: none;\n }\n\n .oc-series-search-chips::-webkit-scrollbar {\n display: none;\n }\n\n .oc-series-search-chip {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 4px 3px 10px;\n border: 1px solid var(--oc-border);\n border-radius: 999px;\n background: var(--oc-secondary);\n color: var(--oc-text-secondary);\n font-size: 12px;\n font-weight: 500;\n line-height: 16px;\n white-space: nowrap;\n }\n\n .oc-series-search-chip-remove {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n /* 16px glyph, but a >=24px hit area (WCAG 2.5.5 target size) via padding\n pulled back with negative margin so the chip layout stays tight. */\n width: 16px;\n height: 16px;\n padding: 4px;\n margin: -4px -2px -4px -4px;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--oc-text-muted);\n cursor: pointer;\n transition:\n color 0.15s,\n background-color 0.15s;\n }\n\n .oc-series-search-chip-remove:hover {\n color: var(--oc-text);\n background: var(--oc-hover-bg);\n }\n\n .oc-series-search-chip-remove:focus-visible {\n outline: 2px solid var(--oc-focus);\n outline-offset: 1px;\n }\n\n .oc-series-search-chip-remove svg {\n display: block;\n }\n\n /* Combobox: input + popup listbox. Styling matches the table search input\n * (border, radius, focus ring) so the two search surfaces read as one system. */\n .oc-series-search-box {\n position: relative;\n flex: 0 1 220px;\n min-width: 120px;\n }\n\n .oc-series-search-icon {\n position: absolute;\n left: 9px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--oc-text-muted);\n pointer-events: none;\n }\n\n /* Shared skin provided by the grouped rule in table.css.\n Only divergent props live here. */\n .oc-series-search-input {\n height: 32px;\n padding: 0 10px 0 28px;\n }\n\n .oc-series-search-listbox {\n position: absolute;\n top: calc(100% + 4px);\n right: 0;\n min-width: 100%;\n max-height: 240px;\n overflow-y: auto;\n margin: 0;\n padding: 4px;\n list-style: none;\n border: 1px solid var(--oc-border);\n border-radius: 8px;\n background: var(--oc-card);\n box-shadow: var(--oc-tooltip-shadow);\n z-index: 20;\n }\n\n .oc-series-search-option {\n padding: 6px 10px;\n border-radius: 4px;\n font-size: 13px;\n color: var(--oc-text);\n cursor: pointer;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .oc-series-search-option[aria-selected=\"true\"],\n .oc-series-search-option:hover {\n background: var(--oc-hover-bg);\n }\n\n /* The typed substring within each suggestion. */\n .oc-series-search-match {\n font-weight: 600;\n }\n\n .oc-series-search-empty {\n padding: 6px 10px;\n font-size: 12px;\n color: var(--oc-text-muted);\n }\n}\n","@layer oc.components {\n /**\n * Sparkline display mode.\n *\n * Stamped on the SVG root as data-display=\"sparkline\" by the renderer when\n * the spec sets display: 'sparkline'. Strips any inherited padding/margin\n * so the mark renders truly edge-to-edge in tight container layouts (KPI\n * cards, table cells, dashboard tiles).\n */\n\n .oc-chart[data-display=\"sparkline\"] {\n display: block;\n margin: 0;\n padding: 0;\n }\n}\n","@layer oc.animation {\n /* ---------------------------------------------------------------------------\n * Animation keyframes\n * --------------------------------------------------------------------------- */\n\n /* Bar entrance: clip-path reveal bottom-to-top + fade in (vertical columns).\n Bars grow upward from baseline with a soft opacity fade. */\n @keyframes oc-enter-bar {\n from {\n clip-path: inset(100% 0 0 0);\n opacity: 0;\n }\n 75% {\n opacity: 1;\n }\n to {\n clip-path: inset(0 0 0 0);\n opacity: 1;\n }\n }\n\n /* Bar entrance: clip-path reveal left-to-right + fade in (horizontal bars).\n Bars grow rightward from axis with a soft opacity fade. */\n @keyframes oc-enter-bar-h {\n from {\n clip-path: inset(0 100% 0 0);\n opacity: 0;\n }\n 75% {\n opacity: 1;\n }\n to {\n clip-path: inset(0 0 0 0);\n opacity: 1;\n }\n }\n\n /* Line/area entrance: clip-path reveal left-to-right + fade in.\n Lines draw in following reading direction with a soft lead-in. */\n @keyframes oc-enter-line {\n from {\n clip-path: inset(0 100% 0 0);\n opacity: 0;\n }\n 15% {\n opacity: 1;\n }\n to {\n clip-path: inset(0 0 0 0);\n opacity: 1;\n }\n }\n\n /* Point/arc entrance: scale up + fade in from center */\n @keyframes oc-enter-point {\n from {\n opacity: 0;\n transform: scale(0.3);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n }\n\n /* Map feature entrance: fade in while transitioning fill from a neutral base\n to the data-driven color stored in --oc-feature-fill. The target opacity is\n --oc-feature-target-opacity (default 1) so features under a focus dim\n animate straight to their dimmed rest opacity instead of hitting 1 and then\n snapping down when the dim is applied post-animation. */\n @keyframes oc-enter-map-fill {\n from {\n fill: var(--oc-secondary);\n opacity: 0;\n }\n to {\n fill: var(--oc-feature-fill);\n opacity: var(--oc-feature-target-opacity, 1);\n }\n }\n\n /* Map point pop-in: gentle scale from 0.75 + fade. Uses transform-origin to\n scale from the circle's own center (cx/cy) rather than the SVG origin. */\n @keyframes oc-enter-map-point {\n from {\n opacity: 0;\n transform: scale(0.75);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n }\n\n /* Pure opacity fade (no transform). Safe for elements with existing transforms\n like arc groups that use translate() for positioning. */\n @keyframes oc-enter-fade-only {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n }\n\n /* Fade + subtle slide up for text, rule, tick, annotations */\n @keyframes oc-enter-fade {\n from {\n opacity: 0;\n transform: translateY(4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n /* Table row entrance: slide up only, no opacity (cells fade independently\n to avoid opacity compounding between row and cell animations) */\n @keyframes oc-table-enter-row {\n from {\n transform: translateY(6px);\n }\n to {\n transform: translateY(0);\n }\n }\n\n /* Table bar fill entrance: clip-path grow only, no opacity\n (preserves resting opacity: 0.15 from table.css) */\n @keyframes oc-table-enter-bar-fill {\n from {\n clip-path: inset(0 100% 0 0);\n }\n to {\n clip-path: inset(0 0 0 0);\n }\n }\n\n /* Tooltip entrance */\n @keyframes oc-tooltip-in {\n from {\n opacity: 0;\n transform: translateY(2px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n}\n","@layer oc.components {\n /* ---------------------------------------------------------------------------\n * Graph\n * --------------------------------------------------------------------------- */\n\n .oc-graph-wrapper {\n position: relative;\n overflow: hidden;\n background: var(--oc-bg);\n font-family: var(--oc-font-family);\n width: 100%;\n height: 100%;\n }\n\n .oc-graph-canvas {\n display: block;\n width: 100%;\n height: 100%;\n cursor: grab;\n }\n\n .oc-graph-canvas--dragging {\n cursor: grabbing;\n }\n\n .oc-graph-chrome {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n z-index: 2;\n padding: 16px 16px 8px;\n pointer-events: none;\n\n & .oc-title {\n font-size: var(--oc-title-size);\n font-weight: var(--oc-title-weight);\n letter-spacing: var(--oc-title-tracking);\n color: var(--oc-text);\n margin: 0 0 4px;\n --_stroke: color-mix(in srgb, var(--oc-bg) 80%, transparent);\n text-shadow:\n -2px -2px 0 var(--_stroke),\n 2px -2px 0 var(--_stroke),\n -2px 2px 0 var(--_stroke),\n 2px 2px 0 var(--_stroke),\n 0 -2px 0 var(--_stroke),\n 0 2px 0 var(--_stroke),\n -2px 0 0 var(--_stroke),\n 2px 0 0 var(--_stroke);\n }\n\n & .oc-subtitle {\n font-size: var(--oc-subtitle-size);\n color: var(--oc-text-secondary);\n margin: 0;\n --_stroke: color-mix(in srgb, var(--oc-bg) 80%, transparent);\n text-shadow:\n -1px -1px 0 var(--_stroke),\n 1px -1px 0 var(--_stroke),\n -1px 1px 0 var(--_stroke),\n 1px 1px 0 var(--_stroke);\n }\n }\n\n .oc-graph-legend {\n position: absolute;\n top: 8px;\n right: 8px;\n background: var(--oc-bg);\n border: 1px solid var(--oc-border);\n border-radius: var(--oc-border-radius);\n padding: 8px 12px;\n font-size: 12px;\n color: var(--oc-text-secondary);\n max-height: 200px;\n overflow-y: auto;\n }\n\n .oc-graph-legend-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 2px 0;\n /* Button reset for interactive rows (rendered as <button>). */\n width: 100%;\n margin: 0;\n background: none;\n border: none;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: default;\n }\n\n /* Interactive legend rows are buttons; give them affordance + a focus ring. */\n button.oc-graph-legend-item {\n cursor: pointer;\n border-radius: 4px;\n\n &:hover {\n background: var(--oc-focus-ring);\n }\n\n &:focus-visible {\n outline: 2px solid var(--oc-focus);\n outline-offset: 1px;\n }\n }\n\n /* De-emphasized (toggled-off) category. */\n .oc-graph-legend-item--inactive {\n opacity: 0.45;\n }\n\n .oc-graph-legend-label {\n flex: 1 1 auto;\n }\n\n .oc-graph-legend-count {\n color: var(--oc-text-secondary);\n font-variant-numeric: tabular-nums;\n margin-left: auto;\n padding-left: 8px;\n }\n\n .oc-graph-legend-swatch {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n flex-shrink: 0;\n }\n\n /* Edge-legend swatches render as a short line, not a dot. */\n .oc-graph-legend-swatch--line {\n width: 12px;\n height: 3px;\n border-radius: 1px;\n }\n\n .oc-graph-search {\n position: absolute;\n top: 8px;\n left: 8px;\n\n & input {\n font-family: var(--oc-font-family);\n font-size: var(--oc-body-size);\n padding: 6px 10px;\n border: 1px solid var(--oc-border);\n border-radius: var(--oc-border-radius);\n background: var(--oc-bg);\n color: var(--oc-text);\n outline: none;\n\n &:focus {\n border-color: var(--oc-focus);\n box-shadow: 0 0 0 2px var(--oc-focus-ring-strong);\n }\n }\n }\n\n /* Dark mode graph overrides (darker bg for canvas-based rendering) */\n .oc-dark .oc-graph-wrapper,\n .oc-graph-wrapper.oc-dark {\n /* GitHub-dark canvas background, intentionally not a token */\n --oc-bg: #0d1117;\n }\n\n /*\n * graph-mount.ts stamps .oc-dark on both .oc-graph-wrapper and\n * .oc-graph-container simultaneously, so the self-variant\n * (.oc-dark.oc-graph-wrapper) and descendant variant\n * (.oc-dark .oc-graph-legend) resolve identically.\n */\n .oc-dark .oc-graph-legend,\n .oc-dark.oc-graph-wrapper .oc-graph-legend,\n .oc-dark .oc-graph-search input {\n background: rgba(13, 17, 23, 0.85);\n border-color: var(--oc-border);\n }\n}\n","@layer oc.animation {\n /* ---------------------------------------------------------------------------\n * Animation scoped rules (.oc-animate enables animation on the chart root)\n * --------------------------------------------------------------------------- */\n\n .oc-animate {\n /* Vertical bars (default): smooth ease-out, no overshoot.\n oc-mark-bar is reserved for future mark type aliases. */\n & .oc-mark-rect rect,\n & .oc-mark-bar rect {\n animation: oc-enter-bar var(--oc-animation-duration) var(--oc-ease-smooth) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Horizontal bars (data-orient is on the mark group, not the SVG root) */\n & .oc-mark-rect[data-orient=\"horizontal\"] rect,\n & .oc-mark-bar[data-orient=\"horizontal\"] rect {\n animation-name: oc-enter-bar-h;\n }\n\n /* Stacked bar/column segments: chain sequentially so each segment starts\n right when the previous one finishes, creating one fluid reveal.\n Uses linear easing so handoffs between segments are seamless (no\n deceleration/acceleration stutter at segment boundaries). */\n & .oc-mark-rect[data-stack-pos] rect {\n animation-duration: var(--oc-stack-segment-duration, 150ms);\n animation-timing-function: linear;\n animation-delay: calc(\n var(--oc-animation-stagger) *\n var(--oc-mark-index, 0) +\n var(--oc-stack-pos, 0) *\n var(--oc-stack-segment-duration, 150ms)\n );\n }\n\n /* Line marks: entire group clips left-to-right (no WAAPI needed) */\n & .oc-mark-line {\n animation: oc-enter-line var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Area marks: entire group clips left-to-right + fades in */\n & .oc-mark-area {\n animation: oc-enter-line var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Arc marks (pie/donut slices): simple fade in.\n Scale transforms break arc positioning because arcs use translate()\n on parent groups. A clean fade is more elegant for pie/donut anyway. */\n & .oc-mark-arc {\n animation: oc-enter-fade-only var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Point marks (scatter, dot).\n Points render as bare <circle> elements with class oc-mark-point directly,\n not wrapped in a group, so we target circle.oc-mark-point (not descendant).\n Duration is 40% of the configured duration (quick pop-in relative to other marks). */\n & circle.oc-mark-point,\n & circle.oc-mark-circle {\n animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.4)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Points on line/area charts: delay so they pop in as the line draws through. */\n & .oc-mark-line ~ circle.oc-mark-point,\n & .oc-mark-area ~ circle.oc-mark-point {\n animation-delay: calc(\n var(--oc-animation-duration) *\n 0.35 +\n var(--oc-animation-stagger) *\n var(--oc-mark-index, 0)\n );\n }\n\n /* Text marks: fade + slight slide up */\n & .oc-mark-text text {\n animation: oc-enter-fade calc(var(--oc-animation-duration) * 0.6)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Rule marks: fade in */\n & .oc-mark-rule line {\n animation: oc-enter-fade calc(var(--oc-animation-duration) * 0.5)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Tick marks: fade in */\n & .oc-mark-tick line {\n animation: oc-enter-fade calc(var(--oc-animation-duration) * 0.5)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Data labels: fade after their parent mark */\n & .oc-mark-label {\n animation: oc-enter-fade 300ms var(--oc-ease-smooth) both;\n animation-delay: calc(\n var(--oc-animation-stagger) *\n var(--oc-mark-index, 0) +\n var(--oc-animation-duration) *\n 0.7\n );\n }\n\n /* Annotations: fade in after marks finish */\n & .oc-annotation {\n animation: oc-enter-fade 400ms var(--oc-ease-smooth) both;\n animation-delay: calc(var(--oc-animation-duration) + var(--oc-annotation-delay, 200ms));\n }\n\n /* Tilemap tiles: fade in with jittered stagger for organic feel.\n Per-tile delay is computed in the renderer with pseudo-random variation. */\n & .oc-tilemap-tile {\n animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.35) ease-in both;\n animation-delay: var(--oc-tile-delay, 0ms);\n }\n\n /* Map features: fill color reveal with shuffled stagger.\n Per-feature --oc-map-delay is computed in the renderer via a seeded\n Fisher-Yates shuffle so features pop in organically. */\n & .oc-map-feature {\n animation: oc-enter-map-fill var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: var(--oc-map-delay, 0ms);\n }\n\n /* Bulk mode: high feature counts (counties, ~3k+) get a single group\n fade instead of per-feature fill animations. opacity is GPU-compositable\n so this stays smooth regardless of child count. */\n & .oc-map-features[data-bulk-animate] {\n animation: oc-enter-fade-only var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n }\n\n & .oc-map-features[data-bulk-animate] .oc-map-feature {\n animation: none;\n }\n\n /* Map borders: fade in near the end of the feature sweep */\n & .oc-map-borders path {\n animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.4) ease-in both;\n animation-delay: calc(var(--oc-animation-duration) * 0.7);\n }\n\n /* Map points: gentle scale (0.75->1) + fade pop-in after borders.\n transform-origin set per-circle in the renderer so scale happens\n around each circle's own center, not the SVG origin. */\n & .oc-map-point {\n animation: oc-enter-map-point calc(var(--oc-animation-duration) * 0.4)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(\n var(--oc-animation-duration) *\n 0.8 +\n var(--oc-mark-index, 0) *\n var(--oc-point-stagger, 60ms)\n );\n }\n\n /* Sankey nodes: fade in with stagger by column depth (left-to-right) */\n & .oc-sankey-node rect {\n animation: oc-enter-fade-only var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Sankey links: fade in after nodes, with stagger.\n Links delay by 30% of duration so nodes appear first, then links flow in. */\n & .oc-sankey-link path {\n animation: oc-enter-fade-only var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(\n var(--oc-animation-duration) *\n 0.3 +\n var(--oc-animation-stagger) *\n var(--oc-mark-index, 0)\n );\n }\n\n /* Barlist rows: fade + slide up per row, bars grow left-to-right */\n & .oc-barlist-row {\n animation: oc-enter-fade calc(var(--oc-animation-duration) * 0.6)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: var(--oc-row-delay, 0ms);\n }\n\n & .oc-barlist-bar {\n animation: oc-enter-bar-h var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: var(--oc-row-delay, 0ms);\n }\n }\n\n /* ---------------------------------------------------------------------------\n * Sparkline mode: pair the line/area reveal with a stronger ease-out so it\n * doesn't read as a uniform left-to-right swipe. cubic-bezier(0.16, 1, 0.3, 1)\n * is \"expo-out\" — fast initial draw that decelerates noticeably at the end,\n * giving the trend a hand-drawn feel. Duration is bumped via the engine\n * (compile.ts) so the cleanup timer stays in sync.\n * --------------------------------------------------------------------------- */\n\n .oc-animate[data-display=\"sparkline\"] .oc-mark-line,\n .oc-animate[data-display=\"sparkline\"] .oc-mark-area {\n animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);\n }\n}\n","/* ---------------------------------------------------------------------------\n * Reduced motion\n *\n * Catch-all: last-declared layer beats every earlier layer regardless of\n * specificity. No !important, no hand-maintained selector mirror.\n *\n * Selector list matches CSS_TOKEN_ROOT_SELECTORS in token-definitions.ts.\n * --------------------------------------------------------------------------- */\n\n@layer oc.reduced-motion {\n @media (prefers-reduced-motion: reduce) {\n :is(\n .oc-root,\n .oc-chart-root,\n .oc-table-wrapper,\n .oc-table-root,\n .oc-graph-wrapper,\n .oc-graph-root,\n .oc-sankey-root,\n .oc-tilemap-root,\n .oc-barlist-root,\n .oc-map-root\n ),\n :is(\n .oc-root,\n .oc-chart-root,\n .oc-table-wrapper,\n .oc-table-root,\n .oc-graph-wrapper,\n .oc-graph-root,\n .oc-sankey-root,\n .oc-tilemap-root,\n .oc-barlist-root,\n .oc-map-root\n )\n *,\n :is(\n .oc-root,\n .oc-chart-root,\n .oc-table-wrapper,\n .oc-table-root,\n .oc-graph-wrapper,\n .oc-graph-root,\n .oc-sankey-root,\n .oc-tilemap-root,\n .oc-barlist-root,\n .oc-map-root\n )\n *::before,\n :is(\n .oc-root,\n .oc-chart-root,\n .oc-table-wrapper,\n .oc-table-root,\n .oc-graph-wrapper,\n .oc-graph-root,\n .oc-sankey-root,\n .oc-tilemap-root,\n .oc-barlist-root,\n .oc-map-root\n )\n *::after {\n animation: none;\n transition: none;\n }\n }\n}\n"],"names":[]}
1
+ {"version":3,"mappings":"AAWA,iBIEE,62DDFA,4lBHAF,eONE,mIAeA,6BAQA,qEAUA,yDAOA,uCAIA,qCASA,gJAgBA,qGAcA,4EAQA,yFPrFF,qBENE,6CAIA,2KAQA,wIAOA,+GAMA,+HAQA,wFAOA,oDAYA,8CAIA,wHAQA,4HAQA,2EAMA,6EAMA,4EAWA,8EAgBA,sDAIA,sEAMA,4EAIA,wEAaA,sFAUA,sFASA,gCAMA,mFIhKA,ogBAsBE,4FAOA,iFAOA,gMAWA,iGAMA,0EAKA,mHAQA,yJAUA,oKAUA,uMAkBF,qFAOA,4FIlHA,2EAKA,gCAIA,2CGJA,kJAaA,iHASA,wDAIA,sRAeA,6RAoBA,uFAKA,iGAKA,gDAMA,qEAMA,oIAWA,0DAKA,2RAiBA,yKAWA,0GAMA,wCAIA,mFZtIA,kDAKA,+CAKA,qGASA,wHAUA,yGAWA,qFAKA,0MAYA,oRAeA,6DAOA,mXAoBA,0DAIA,yFAKA,yDIpHA,iGAKE,qFAMA,yDAIA,4DAKA,gGAAA,gGAOA,oJASA,gFAOA,mDAIA,sEAKA,iFAKA,2CAKF,sNAOA,kPAOA,2IAKA,gJAKA,iCASE,0FAAA,0FAaF,oLAYE,mLAAA,kLAaA,yFAKA,qFAKA,4CAAA,2CAOA,+FAKA,6DAMA,4FAKA,+DAaF,iPAaA,wFAAA,8EAAA,yFAAA,+EAMA,+IAOA,+BAGE,wCASF,oJAQE,6OAaE,oEAKA,2GAKA,+FAOJ,4DAIA,+CASA,gCAIA,iHAUA,gDASA,+DAKE,kEAOF,iFAOA,mGAWA,2DAIE,qCAKF,8CAQA,+BASE,qDAAA,qDAMA,qCAUA,6CAIA,kEASF,2EASA,oHMjYA,qIASA,kEAOA,2CAIA,4GASE,8bAkBA,2RAaF,qPAcA,qLAiBA,6DAIE,kEAIA,+FAOF,4CAIA,iCAIA,0HAOA,+EAQA,sEAMA,oDAKE,0OAUE,2GAQJ,qEAYA,iKHrKA,oERCF,oBSJI,4JAMA,4JAOA,8NAOA,yOAOA,wRAAA,yRAcA,4RAaA,uRAYA,4RAAA,+RAaA,uKAAA,2KG/EF,8GAgBA,kHAgBA,iHAeA,4FAgBA,8IAaA,iGAaA,yDAUA,sGAaA,uFAWA,yFAUA,sGEpIE,yLAAA,wLAOA,qFAAA,oFASA,oRAYA,8MAOA,8MASA,kNAUA,qOAAA,sOAQA,+JAAA,+JAWA,8NAOA,8NAOA,8NAOA,iMAWA,sKAOA,wJAQA,gLASA,8JAKA,+EAKA,2KAQA,mQAYA,0NAQA,8PAYA,uLAMA,6KAeF,wJdrMF,yBeDE,uCACE,6LAAA,+LAAA,qMAAA","sources":["../src/styles/index.css","../src/styles/you-draw-it.css","../src/styles/chrome.css","../src/styles/dark.css","../src/styles/tokens.css","../src/styles/table.css","../src/styles/tooltip.css","../src/styles/base.css","../src/styles/sparkline.css","../src/styles/graph.css","../src/styles/legend.css","../src/styles/series-search.css","../src/styles/keyframes.css","../src/styles/table-animation.css","../src/styles/animation.css","../src/styles/reduced-motion.css"],"sourcesContent":["/**\n * @opendata-ai/openchart-core styles\n *\n * CSS with oc- prefix for all class names.\n * CSS custom properties for theme overrides.\n * Dark mode via .oc-dark class on the container.\n * Cascade layers: oc.tokens, oc.base, oc.components, oc.animation, oc.reduced-motion.\n */\n\n/* For optimal typography, load Inter: https://fonts.google.com/specimen/Inter */\n\n@layer oc.tokens, oc.base, oc.components, oc.animation, oc.reduced-motion;\n\n@import \"./tokens.css\";\n@import \"./dark.css\";\n@import \"./base.css\";\n@import \"./chrome.css\";\n@import \"./tooltip.css\";\n@import \"./legend.css\";\n@import \"./series-search.css\";\n@import \"./you-draw-it.css\";\n@import \"./table.css\";\n@import \"./table-animation.css\";\n@import \"./graph.css\";\n@import \"./sparkline.css\";\n@import \"./keyframes.css\";\n@import \"./animation.css\";\n@import \"./reduced-motion.css\";\n","@layer oc.components {\n /* ---------------------------------------------------------------------------\n * You draw it (youDrawIt)\n *\n * NYT-style draw-then-reveal. SVG-space elements (hatched region, guess path,\n * reveal clip) render inside the chart SVG; the prompt + skip-to-reveal button\n * are an absolutely-positioned HTML overlay the vanilla adapter sizes over the\n * drawing region (inline left/top/width/height). All colors come from --oc-*\n * tokens so they flip in .oc-dark.\n * --------------------------------------------------------------------------- */\n\n /* Hatched \"draw here\" region. Fill comes from an SVG <pattern> (line color =\n * the target series color); this only governs overall opacity + fade-out. */\n .oc-ydi-region {\n opacity: 0.35;\n transition: opacity 0.4s ease;\n }\n\n .oc-ydi-region[data-ydi-hidden=\"true\"] {\n opacity: 0;\n }\n\n /* The vertical rule at `from` where drawing begins. */\n .oc-ydi-boundary {\n stroke: var(--oc-text-muted);\n stroke-width: 1;\n stroke-dasharray: 3 3;\n stroke-opacity: 0.6;\n }\n\n /* Reader's guess: a distinct pen-like stroke that reads as \"yours\" against the\n * solid real line. Dashed + focus-blue so it stays legible in both modes. */\n .oc-ydi-guess {\n stroke: var(--oc-focus);\n stroke-width: 2.5;\n stroke-linecap: round;\n stroke-linejoin: round;\n stroke-dasharray: 1 6;\n }\n\n /* Host-supplied comparison line (\"what everyone else guessed\"). Muted so it\n * sits behind the reader's guess and the real line. */\n .oc-ydi-comparison {\n stroke: var(--oc-text-muted);\n stroke-width: 1.5;\n stroke-opacity: 0.5;\n stroke-dasharray: 4 4;\n }\n\n /* Reveal clip rect: the width animates from the `from` boundary to the full\n * area on reveal, wiping the real line in left-to-right. The vanilla adapter\n * adds .oc-ydi-clip-animate right before setting the final width; reduced\n * motion skips the class so the width snaps. */\n .oc-ydi-clip-rect.oc-ydi-clip-animate {\n transition: width 0.7s cubic-bezier(0.22, 1, 0.36, 1);\n }\n\n /* HTML controls overlay: prompt (top) + reveal button (bottom-right). */\n .oc-ydi-controls {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: flex-end;\n pointer-events: none;\n font-family: var(--oc-font-family);\n padding: 8px;\n box-sizing: border-box;\n z-index: 10;\n }\n\n .oc-ydi-prompt {\n align-self: center;\n max-width: 100%;\n padding: 6px 12px;\n border-radius: 6px;\n background: var(--oc-card);\n border: 1px solid var(--oc-border);\n color: var(--oc-text-secondary);\n font-size: 13px;\n font-weight: 500;\n line-height: 16px;\n text-align: center;\n box-shadow: var(--oc-tooltip-shadow);\n }\n\n .oc-ydi-controls.oc-ydi-revealed .oc-ydi-prompt {\n display: none;\n }\n\n /* Skip-to-reveal button. pointer-events re-enabled (the overlay disables it)\n * so it stays clickable and keyboard-reachable. Min height enforced inline by\n * the adapter (>= 24px effective touch target). */\n .oc-ydi-reveal-button {\n pointer-events: auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 6px 14px;\n border: 1px solid var(--oc-border);\n border-radius: 6px;\n background: var(--oc-bg);\n color: var(--oc-text);\n font-family: inherit;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n box-shadow: var(--oc-tooltip-shadow);\n transition:\n background-color 0.15s,\n border-color 0.15s;\n }\n\n .oc-ydi-reveal-button:hover {\n background: var(--oc-hover-bg);\n }\n\n .oc-ydi-reveal-button:focus-visible {\n outline: 2px solid var(--oc-focus);\n outline-offset: 2px;\n }\n\n .oc-ydi-reveal-button:disabled {\n opacity: 0.5;\n cursor: default;\n }\n}\n","@layer oc.components {\n /* ---------------------------------------------------------------------------\n * Chrome (eyebrow, title, subtitle, source, byline, footer, brand)\n * --------------------------------------------------------------------------- */\n\n .oc-chrome {\n font-family: var(--oc-font-family);\n }\n\n .oc-eyebrow {\n font-size: var(--oc-eyebrow-size);\n font-weight: var(--oc-eyebrow-weight);\n letter-spacing: var(--oc-eyebrow-tracking);\n text-transform: uppercase;\n fill: var(--oc-accent);\n }\n\n .oc-title {\n font-size: var(--oc-title-size);\n font-weight: var(--oc-title-weight);\n letter-spacing: var(--oc-title-tracking);\n fill: var(--oc-text);\n }\n\n .oc-subtitle {\n font-size: var(--oc-subtitle-size);\n font-weight: var(--oc-subtitle-weight);\n fill: var(--oc-text-muted);\n }\n\n .oc-source,\n .oc-byline,\n .oc-footer {\n font-size: var(--oc-source-size);\n font-weight: var(--oc-source-weight);\n fill: var(--oc-text-muted);\n }\n\n .oc-brand {\n font-size: 11px;\n font-weight: 510;\n letter-spacing: 0.02em;\n fill: var(--oc-text-faint);\n }\n\n .oc-brand-dot {\n fill: var(--oc-accent);\n }\n\n .oc-eyebrow-dot {\n fill: var(--oc-accent);\n }\n\n /* ---------------------------------------------------------------------------\n * Metric bar (.oc-metrics) — optional row of label/value cells above chart\n * --------------------------------------------------------------------------- */\n\n .oc-metrics {\n font-family: var(--oc-font-family);\n }\n\n .oc-metric-label {\n font-size: 10px;\n font-weight: 510;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n fill: var(--oc-text-muted);\n }\n\n .oc-metric-value {\n font-size: 22px;\n font-weight: 510;\n letter-spacing: -0.01em;\n fill: var(--oc-text);\n font-variant-numeric: tabular-nums;\n }\n\n .oc-metric-delta-up {\n fill: var(--oc-positive);\n font-size: 12px;\n font-weight: 510;\n }\n\n .oc-metric-delta-down {\n fill: var(--oc-negative);\n font-size: 12px;\n font-weight: 510;\n }\n\n .oc-metric-secondary {\n fill: var(--oc-positive);\n font-size: 12px;\n font-weight: 400;\n }\n\n /* ---------------------------------------------------------------------------\n * Axis tick labels — inline variant sits above each gridline at the\n * chart-area left edge (no left gutter). Used by line/area y-axes.\n * --------------------------------------------------------------------------- */\n\n .oc-axis-tick-inline {\n font-size: 11px;\n font-weight: 400;\n fill: var(--oc-text-muted);\n }\n\n /* ---------------------------------------------------------------------------\n * Endpoint labels (right-side per-series column for line/area charts)\n *\n * Swatch idiom: a thicker colored stroke segment with an optional thinner\n * lighter strip below. Mirrors the refreshed traditional legend swatch so a\n * single chart never shows two swatch idioms (mock-2 alignment). Per-series\n * color is stamped inline by the renderer; CSS tokens here cover the muted\n * value text and the leader line so theme overrides flow through naturally.\n * --------------------------------------------------------------------------- */\n\n .oc-endpoint-labels {\n font-family: var(--oc-font-family);\n }\n\n .oc-endpoint-label {\n /* Color set inline per series; this fallback only kicks in if the renderer\n ever omits the inline fill. */\n fill: var(--oc-endpoint-label-color, var(--oc-text));\n }\n\n .oc-endpoint-value {\n fill: var(--oc-endpoint-value-color, var(--oc-text-muted));\n }\n\n .oc-endpoint-leader {\n stroke: var(--oc-endpoint-leader-color, currentColor);\n }\n\n /* .oc-endpoint-marker — fill/stroke set inline (open-ring style: bg fill, series stroke). */\n /* .oc-endpoint-swatch — color set inline per series. */\n\n /* ---------------------------------------------------------------------------\n * Annotation dot + subtitle (text-annotation extensions)\n * --------------------------------------------------------------------------- */\n\n /* .oc-annotation-dot — fill/stroke resolved by the engine (open-ring default = bg fill, series stroke). */\n\n .oc-annotation-subtitle {\n fill: var(--oc-annotation-subtitle-color, var(--oc-text-muted));\n }\n\n /* ---------------------------------------------------------------------------\n * Table footer chrome\n * --------------------------------------------------------------------------- */\n\n /* Footer row: source/footer text on the left, brand watermark on the right,\n * sharing a single baseline. gap keeps them apart if the row gets narrow. */\n .oc-table-footer-row {\n display: flex;\n align-items: baseline;\n gap: 16px;\n padding: 16px 16px 4px;\n }\n\n /* Chrome block sits inside the flex row now, so it no longer owns the\n * footer's top padding or horizontal inset (the row provides both). */\n .oc-chrome-footer {\n padding-top: 0;\n }\n\n /* Brand watermark: pushed to the right edge (margin-left:auto holds it there\n * even when the source text is absent), never shrinks below its own text. */\n .oc-table-footer-row .oc-table-ref {\n margin-left: auto;\n flex-shrink: 0;\n text-align: right;\n }\n}\n","/* GENERATED FILE — do not edit. Source: packages/core/src/styles/token-definitions.ts. Regenerate: bun run generate:tokens */\n\n@layer oc.tokens {\n /* ---------------------------------------------------------------------------\n * Dark mode overrides (fallback defaults)\n *\n * At runtime, the JS theme engine stamps dark-adapted --oc-* custom\n * properties on each .oc-root container. These CSS values serve as\n * fallbacks for contexts where JS hasn't mounted yet.\n * --------------------------------------------------------------------------- */\n\n .oc-dark {\n /* Surfaces (zinc-based achromatic ramp) */\n --oc-bg: #09090b;\n --oc-card: #111113;\n --oc-secondary: #27272a;\n\n /* Text levels — see tokens.css for the cross-mode naming rationale */\n --oc-text: #f7f8f8;\n --oc-text-secondary: #d0d6e0;\n --oc-text-muted: #a1a1aa;\n --oc-text-faint: #52525b;\n\n /* Lines */\n --oc-gridline: rgba(255, 255, 255, 0.05);\n --oc-axis: rgba(255, 255, 255, 0.1);\n --oc-border: rgba(255, 255, 255, 0.1);\n\n /* Brand and semantic — accent stays cyan (no darkening on dark bg) */\n --oc-accent: #06b6d4;\n --oc-accent-strong: #06b6d4;\n --oc-positive: #34d399;\n --oc-negative: #fb7185;\n --oc-focus: #60a5fa;\n\n /* Interactive states */\n --oc-hover-bg: rgba(255, 255, 255, 0.05);\n --oc-tooltip-bg: rgba(17, 17, 19, 0.92);\n --oc-tooltip-border: rgba(255, 255, 255, 0.08);\n --oc-tooltip-shadow: 0 2px 8px rgba(0, 0, 0, 0.3), 0 0 1px rgba(0, 0, 0, 0.4);\n --oc-tooltip-text: #f7f8f8;\n --oc-legend-text: #d0d6e0;\n }\n}\n","/* GENERATED FILE — do not edit. Source: packages/core/src/styles/token-definitions.ts. Regenerate: bun run generate:tokens */\n\n@layer oc.tokens {\n /* ---------------------------------------------------------------------------\n * Custom properties (light mode defaults)\n *\n * These are FALLBACK defaults. At runtime, the JS theme engine stamps the\n * resolved theme as inline --oc-* custom properties on each .oc-root\n * container (see packages/vanilla/src/theme-tokens.ts). These CSS values\n * serve as fallbacks for contexts where JS hasn't mounted yet (SSR, static\n * HTML examples).\n * --------------------------------------------------------------------------- */\n\n .oc-root,\n .oc-chart-root,\n .oc-table-wrapper,\n .oc-table-root,\n .oc-graph-wrapper,\n .oc-graph-root,\n .oc-sankey-root,\n .oc-tilemap-root,\n .oc-barlist-root,\n .oc-map-root {\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n\n /* Opt out of iOS text auto-inflation so measured widths match rendered text. */\n -webkit-text-size-adjust: 100%;\n text-size-adjust: 100%;\n\n --oc-font-family:\n \"Inter Variable\", Inter, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n --oc-font-mono: \"JetBrains Mono\", \"Fira Code\", \"Cascadia Code\", monospace;\n\n /* Animation easing presets via CSS linear() */\n --oc-ease-smooth: linear(\n 0,\n 0.157,\n 0.438,\n 0.64,\n 0.766,\n 0.85,\n 0.906,\n 0.941,\n 0.964,\n 0.978,\n 0.988,\n 0.994,\n 0.998,\n 1\n );\n --oc-ease-snappy: linear(\n 0,\n 0.012,\n 0.048,\n 0.108,\n 0.194,\n 0.302,\n 0.426,\n 0.559,\n 0.69,\n 0.808,\n 0.905,\n 0.973,\n 1.013,\n 1.028,\n 1.023,\n 1.006,\n 0.984,\n 0.966,\n 0.957,\n 0.957,\n 0.964,\n 0.975,\n 0.986,\n 0.995,\n 1,\n 1.003,\n 1.002,\n 1,\n 0.998,\n 0.998,\n 0.999,\n 1\n );\n\n /* Animation timing defaults */\n --oc-animation-duration: 500ms;\n --oc-animation-stagger: 80ms;\n --oc-annotation-delay: 200ms;\n\n /* Typography scale (editorial design system) */\n --oc-title-size: 26px;\n --oc-title-weight: 590;\n --oc-title-tracking: -0.022em;\n --oc-subtitle-size: 14px;\n --oc-subtitle-weight: 450;\n --oc-source-size: 11px;\n --oc-source-weight: 450;\n --oc-body-size: 13px;\n --oc-eyebrow-size: 11px;\n --oc-eyebrow-weight: 550;\n --oc-eyebrow-tracking: 0.08em;\n\n /* Surfaces (light mode defaults) */\n --oc-bg: #ffffff;\n --oc-card: #ffffff;\n --oc-secondary: #f4f4f5; /* zinc-100, raised surface */\n\n /*\n * Text levels. Names invert across modes: in light mode \"muted\" sits at\n * a lighter zinc step than \"subtle\" because both are picked relative to\n * the active background, not from a fixed lightness ladder. The intent\n * is \"muted = first step away from primary text\"; \"subtle = next step\n * down\"; etc.\n *\n * Light mode: text=zinc-950, secondary=zinc-700, muted=zinc-500,\n * subtle=zinc-400, faint=zinc-300\n * Dark mode (dark.css): inverts the surface tokens but keeps the same\n * muted -> subtle -> faint progression away from primary.\n */\n --oc-text: #09090b;\n --oc-text-secondary: #3f3f46;\n --oc-text-muted: #71717a;\n --oc-text-faint: #d4d4d8;\n\n /* Lines */\n --oc-gridline: rgba(0, 0, 0, 0.1);\n --oc-axis: rgba(0, 0, 0, 0.1);\n --oc-border: rgba(0, 0, 0, 0.08);\n --oc-border-radius: 2px;\n\n /* Brand and semantic */\n --oc-accent: #06b6d4;\n --oc-accent-strong: #0891b2; /* darker cyan for line strokes on light bg */\n --oc-positive: #10b981;\n --oc-negative: #e11d48;\n --oc-focus: #3b82f6;\n /* static, intentionally NOT derived from --oc-focus and NOT flipped in dark mode */\n --oc-focus-ring: rgba(59, 130, 246, 0.1);\n --oc-focus-ring-strong: rgba(59, 130, 246, 0.25);\n --oc-editable-hover: rgba(79, 70, 229, 0.35); /* edit-mode hover outline, indigo one-off */\n\n /* Spacing scale (4px base) — only --oc-space-2/4 are consumed (JS-stamped) */\n --oc-space-2: 8px;\n --oc-space-4: 16px;\n\n /* Interactive states */\n --oc-hover-bg: rgba(0, 0, 0, 0.025);\n --oc-tooltip-bg: rgba(255, 255, 255, 0.88);\n --oc-tooltip-border: rgba(0, 0, 0, 0.08);\n --oc-tooltip-shadow: 0 2px 8px rgba(0, 0, 0, 0.08), 0 0 1px rgba(0, 0, 0, 0.12);\n --oc-tooltip-text: #09090b;\n --oc-legend-text: #3f3f46;\n }\n}\n","@layer oc.components {\n /* ---------------------------------------------------------------------------\n * Table base\n * --------------------------------------------------------------------------- */\n\n .oc-table-wrapper {\n font-family: var(--oc-font-family);\n color: var(--oc-text);\n background: var(--oc-bg);\n\n & > .oc-chrome {\n margin-bottom: 16px;\n padding-left: 16px;\n padding-right: 16px;\n }\n\n & > .oc-chrome:first-child {\n padding-top: 4px;\n }\n\n & table {\n width: 100%;\n border-collapse: collapse;\n }\n\n & th,\n & td {\n padding: 10px 16px;\n text-align: left;\n border-bottom: 1px solid var(--oc-border);\n }\n\n & th {\n font-weight: 600;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--oc-text-secondary);\n white-space: nowrap;\n }\n\n & thead {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--oc-bg);\n }\n\n & thead th {\n border-bottom-width: 2px;\n }\n\n & td {\n font-size: 14px;\n font-variant-numeric: tabular-nums;\n }\n\n & th:focus {\n outline: 2px solid var(--oc-focus);\n outline-offset: -2px;\n }\n\n & tbody:focus {\n outline: none;\n }\n }\n\n .oc-table-title {\n margin-bottom: 4px;\n font-size: var(--oc-title-computed-size, var(--oc-title-size));\n font-weight: var(--oc-title-computed-weight, var(--oc-title-weight));\n color: var(--oc-title-computed-color, var(--oc-text));\n }\n\n .oc-table-subtitle {\n margin-bottom: 8px;\n font-size: var(--oc-subtitle-computed-size, var(--oc-subtitle-size));\n font-weight: var(--oc-subtitle-computed-weight, var(--oc-subtitle-weight));\n color: var(--oc-subtitle-computed-color, var(--oc-text-secondary));\n }\n\n .oc-table-source {\n font-size: var(--oc-source-computed-size, var(--oc-source-size));\n color: var(--oc-source-computed-color, var(--oc-text-muted));\n }\n\n .oc-table-footer-text {\n font-size: var(--oc-footer-computed-size, var(--oc-source-size));\n color: var(--oc-footer-computed-color, var(--oc-text-muted));\n }\n\n .oc-table-scroll {\n overflow-x: auto;\n }\n\n /* ---------------------------------------------------------------------------\n * Sticky first column\n * --------------------------------------------------------------------------- */\n\n .oc-table--sticky {\n & th:first-child,\n & td:first-child {\n position: sticky;\n left: 0;\n z-index: 1;\n background: var(--oc-bg);\n }\n }\n\n /* ---------------------------------------------------------------------------\n * Sort controls\n * --------------------------------------------------------------------------- */\n\n .oc-table-sort-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 2px;\n margin-left: 6px;\n display: inline-flex;\n flex-direction: column;\n align-items: center;\n vertical-align: middle;\n gap: 2px;\n\n &::before,\n &::after {\n content: \"\";\n display: block;\n width: 0;\n height: 0;\n border-left: 5px solid transparent;\n border-right: 5px solid transparent;\n transition:\n opacity 0.15s,\n border-color 0.15s;\n }\n\n &::before {\n border-bottom: 4.5px solid var(--oc-text-secondary);\n opacity: 0.45;\n }\n\n &::after {\n border-top: 4.5px solid var(--oc-text-secondary);\n opacity: 0.45;\n }\n\n &:hover::before,\n &:hover::after {\n opacity: 0.75;\n }\n }\n\n th[aria-sort=\"ascending\"] .oc-table-sort-btn {\n &::before {\n opacity: 1;\n border-bottom-color: var(--oc-text);\n }\n\n &::after {\n opacity: 0.15;\n }\n }\n\n th[aria-sort=\"descending\"] .oc-table-sort-btn {\n &::after {\n opacity: 1;\n border-top-color: var(--oc-text);\n }\n\n &::before {\n opacity: 0.15;\n }\n }\n\n /* ---------------------------------------------------------------------------\n * Search\n * --------------------------------------------------------------------------- */\n\n /* Shared search-input skin (table + series-search).\n Padding excluded: it diverges (table: 8px 12px; series: 0 10px 0 28px)\n and series-search.css is imported before table.css — a padding declaration\n here would beat the series-search override by source order. */\n .oc-table-search input,\n .oc-series-search-input {\n width: 100%;\n border: 1px solid var(--oc-border);\n border-radius: 6px;\n font-size: 13px;\n font-family: inherit;\n background: var(--oc-bg);\n color: var(--oc-text);\n box-sizing: border-box;\n transition: border-color 0.15s;\n }\n\n .oc-table-search input::placeholder,\n .oc-series-search-input::placeholder {\n color: var(--oc-text-muted);\n font-size: 13px;\n }\n\n .oc-table-search input:focus,\n .oc-series-search-input:focus {\n outline: none;\n border-color: var(--oc-focus);\n box-shadow: 0 0 0 3px var(--oc-focus-ring);\n }\n\n .oc-table-search {\n padding: 8px 0;\n\n & input {\n padding: 8px 12px;\n }\n }\n\n /* ---------------------------------------------------------------------------\n * Pagination\n * --------------------------------------------------------------------------- */\n\n .oc-table-pagination {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 0 4px;\n font-size: 13px;\n color: var(--oc-text-secondary);\n\n & button {\n padding: 6px 14px;\n border: 1px solid var(--oc-border);\n border-radius: 6px;\n background: var(--oc-bg);\n color: var(--oc-text);\n cursor: pointer;\n font-size: 13px;\n font-family: inherit;\n transition:\n background 0.15s,\n border-color 0.15s;\n\n &:disabled {\n opacity: 0.35;\n cursor: not-allowed;\n }\n\n &:hover:not(:disabled) {\n background: var(--oc-hover-bg);\n border-color: var(--oc-axis);\n }\n\n &:focus-visible {\n outline: 2px solid var(--oc-focus);\n outline-offset: 1px;\n }\n }\n }\n\n .oc-table-pagination-info {\n font-variant-numeric: tabular-nums;\n }\n\n .oc-table-pagination-btns {\n display: flex;\n gap: 8px;\n }\n\n /* ---------------------------------------------------------------------------\n * Bar cells\n * --------------------------------------------------------------------------- */\n\n .oc-table-bar {\n position: relative;\n }\n\n .oc-table-bar-fill {\n position: absolute;\n top: 6px;\n left: 0;\n bottom: 6px;\n border-radius: 2px;\n opacity: 0.15;\n pointer-events: none;\n }\n\n .oc-table-bar-value {\n position: relative;\n z-index: 1;\n }\n\n /* ---------------------------------------------------------------------------\n * Sparkline cells\n * --------------------------------------------------------------------------- */\n\n .oc-table-sparkline {\n display: block;\n width: 100%;\n position: relative;\n\n & svg {\n display: block;\n width: 100%;\n overflow: visible;\n }\n }\n\n .oc-table-sparkline-dot {\n position: absolute;\n border-radius: 50%;\n width: 5px;\n height: 5px;\n }\n\n .oc-table-sparkline-labels {\n display: flex;\n justify-content: space-between;\n font-size: 11px;\n line-height: 1;\n }\n\n /* ---------------------------------------------------------------------------\n * Image cells\n * --------------------------------------------------------------------------- */\n\n .oc-table-image {\n display: inline-block;\n vertical-align: middle;\n\n & img {\n object-fit: cover;\n }\n }\n\n .oc-table-image-rounded img {\n border-radius: 50%;\n }\n\n /* ---------------------------------------------------------------------------\n * Flag cells\n * --------------------------------------------------------------------------- */\n\n .oc-table-flag {\n font-size: 1.2em;\n }\n\n /* ---------------------------------------------------------------------------\n * Compact mode\n * --------------------------------------------------------------------------- */\n\n .oc-table--compact {\n & th,\n & td {\n padding: 4px 8px;\n font-size: 13px;\n }\n\n & th {\n font-size: 11px;\n }\n }\n\n /* ---------------------------------------------------------------------------\n * Row hover (when onRowClick is set)\n * --------------------------------------------------------------------------- */\n\n .oc-table--clickable tbody {\n & tr {\n cursor: pointer;\n }\n\n & tr:hover {\n background: var(--oc-hover-bg);\n }\n }\n\n /* ---------------------------------------------------------------------------\n * Keyboard cell focus indicator\n * --------------------------------------------------------------------------- */\n\n .oc-table-cell-focus {\n outline: 2px solid var(--oc-focus);\n outline-offset: -2px;\n }\n\n /* ---------------------------------------------------------------------------\n * Empty state\n * --------------------------------------------------------------------------- */\n\n .oc-table-empty {\n padding: 32px 16px;\n text-align: center;\n color: var(--oc-text-secondary);\n font-size: 14px;\n font-style: italic;\n }\n}\n","@layer oc.components {\n /* ---------------------------------------------------------------------------\n * Tooltip\n * Editorial card matching the new design system: zinc surface, cyan accent,\n * Inter Variable, mono numerics. Sized for a slice tooltip that lists every\n * series at the snapped x.\n * --------------------------------------------------------------------------- */\n\n .oc-tooltip {\n position: absolute;\n display: none;\n pointer-events: none;\n z-index: 1000;\n background: var(--oc-tooltip-bg);\n backdrop-filter: blur(14px);\n border: 1px solid var(--oc-tooltip-border);\n border-radius: var(--oc-border-radius, 2px);\n box-shadow: var(--oc-tooltip-shadow);\n color: var(--oc-tooltip-text);\n font-family: var(--oc-font-family);\n font-size: 12px;\n padding: 0;\n max-width: 280px;\n min-width: 160px;\n line-height: 1.4;\n animation: oc-tooltip-in 120ms ease-out;\n transition:\n left 100ms ease-in-out,\n top 100ms ease-in-out;\n\n & .oc-tooltip-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px 6px;\n }\n\n & .oc-tooltip-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n }\n\n & .oc-tooltip-title {\n font-weight: 590;\n font-size: 11px;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--oc-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n & .oc-tooltip-body {\n padding: 6px 12px 10px;\n border-top: 1px solid var(--oc-tooltip-border);\n }\n\n /* No separator when body is the only child */\n & .oc-tooltip-body:first-child {\n border-top: none;\n padding-top: 10px;\n }\n\n & .oc-tooltip-row {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 16px;\n padding: 2px 0;\n }\n\n & .oc-tooltip-row-swatch {\n display: inline-block;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n margin-right: 6px;\n flex-shrink: 0;\n transform: translateY(-1px);\n }\n\n & .oc-tooltip-label {\n color: var(--oc-text-secondary);\n font-size: 12px;\n font-weight: 400;\n white-space: nowrap;\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n }\n\n & .oc-tooltip-value {\n font-weight: 510;\n font-size: 12px;\n font-variant-numeric: tabular-nums;\n color: var(--oc-tooltip-text);\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n }\n\n /* ---------------------------------------------------------------------------\n * Crosshair guideline + per-series snap dots (line/area charts)\n * Animate the snap between adjacent points so the indicator slides instead\n * of jumping. ~50ms ease-in-out keeps the motion crisp without lagging.\n * --------------------------------------------------------------------------- */\n\n .oc-crosshair {\n pointer-events: none;\n transition:\n x1 50ms ease-in-out,\n x2 50ms ease-in-out;\n }\n\n .oc-snap-dots circle {\n pointer-events: none;\n transition:\n cx 50ms ease-in-out,\n cy 50ms ease-in-out;\n }\n}\n","@layer oc.base {\n /* ---------------------------------------------------------------------------\n * Wrapper roots\n * --------------------------------------------------------------------------- */\n\n .oc-chart-root {\n width: 100%;\n height: 100%;\n }\n\n .oc-table-root,\n .oc-graph-root,\n .oc-sankey-root,\n .oc-barlist-root,\n .oc-tilemap-root,\n .oc-map-root {\n width: 100%;\n height: 100%;\n }\n\n .oc-table-root {\n overflow: auto;\n }\n\n /* ---------------------------------------------------------------------------\n * Chart container\n * --------------------------------------------------------------------------- */\n\n .oc-chart {\n font-family: var(--oc-font-family);\n display: block;\n width: 100%;\n }\n\n /* ---------------------------------------------------------------------------\n * BarList SVG container\n * --------------------------------------------------------------------------- */\n\n .oc-tilemap,\n .oc-map {\n display: block;\n width: 100%;\n height: auto;\n }\n\n .oc-map-feature {\n transition: opacity 400ms ease;\n }\n\n .oc-barlist {\n display: block;\n width: 100%;\n }\n\n /* ---------------------------------------------------------------------------\n * Screen reader only utility\n * --------------------------------------------------------------------------- */\n\n .oc-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border-width: 0;\n }\n\n /* ---------------------------------------------------------------------------\n * Editable hover feedback\n * --------------------------------------------------------------------------- */\n\n .oc-editable-hover {\n outline: 1.5px solid var(--oc-editable-hover);\n outline-offset: 2px;\n border-radius: 2px;\n }\n\n /* ---------------------------------------------------------------------------\n * Keyboard focus indicators\n *\n * --oc-focus carries a light value in tokens.css and a dark value in\n * dark.css, so both rings stay visible in either mode.\n * --------------------------------------------------------------------------- */\n\n /* Container ring when the chart itself receives keyboard focus. */\n .oc-root:focus-visible {\n outline: 2px solid var(--oc-focus);\n outline-offset: 2px;\n }\n\n /* Mark ring during arrow-key navigation (see wireKeyboardNav). Outline on\n * SVG elements draws around the mark's bounding box, same technique as\n * .oc-editable-hover above. */\n .oc-mark-focused {\n outline: 2px solid var(--oc-focus);\n outline-offset: 1px;\n border-radius: 2px;\n }\n}\n","@layer oc.components {\n /**\n * Sparkline display mode.\n *\n * Stamped on the SVG root as data-display=\"sparkline\" by the renderer when\n * the spec sets display: 'sparkline'. Strips any inherited padding/margin\n * so the mark renders truly edge-to-edge in tight container layouts (KPI\n * cards, table cells, dashboard tiles).\n */\n\n .oc-chart[data-display=\"sparkline\"] {\n display: block;\n margin: 0;\n padding: 0;\n }\n}\n","@layer oc.components {\n /* ---------------------------------------------------------------------------\n * Graph\n * --------------------------------------------------------------------------- */\n\n .oc-graph-wrapper {\n position: relative;\n overflow: hidden;\n background: var(--oc-bg);\n font-family: var(--oc-font-family);\n width: 100%;\n height: 100%;\n }\n\n .oc-graph-canvas {\n display: block;\n width: 100%;\n height: 100%;\n cursor: grab;\n }\n\n .oc-graph-canvas--dragging {\n cursor: grabbing;\n }\n\n .oc-graph-chrome {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n z-index: 2;\n padding: 16px 16px 8px;\n pointer-events: none;\n\n & .oc-title {\n font-size: var(--oc-title-size);\n font-weight: var(--oc-title-weight);\n letter-spacing: var(--oc-title-tracking);\n color: var(--oc-text);\n margin: 0 0 4px;\n --_stroke: color-mix(in srgb, var(--oc-bg) 80%, transparent);\n text-shadow:\n -2px -2px 0 var(--_stroke),\n 2px -2px 0 var(--_stroke),\n -2px 2px 0 var(--_stroke),\n 2px 2px 0 var(--_stroke),\n 0 -2px 0 var(--_stroke),\n 0 2px 0 var(--_stroke),\n -2px 0 0 var(--_stroke),\n 2px 0 0 var(--_stroke);\n }\n\n & .oc-subtitle {\n font-size: var(--oc-subtitle-size);\n color: var(--oc-text-secondary);\n margin: 0;\n --_stroke: color-mix(in srgb, var(--oc-bg) 80%, transparent);\n text-shadow:\n -1px -1px 0 var(--_stroke),\n 1px -1px 0 var(--_stroke),\n -1px 1px 0 var(--_stroke),\n 1px 1px 0 var(--_stroke);\n }\n }\n\n .oc-graph-legend {\n position: absolute;\n top: 8px;\n right: 8px;\n background: var(--oc-bg);\n border: 1px solid var(--oc-border);\n border-radius: var(--oc-border-radius);\n padding: 8px 12px;\n font-size: 12px;\n color: var(--oc-text-secondary);\n max-height: 200px;\n overflow-y: auto;\n }\n\n .oc-graph-legend-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 2px 0;\n /* Button reset for interactive rows (rendered as <button>). */\n width: 100%;\n margin: 0;\n background: none;\n border: none;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: default;\n }\n\n /* Interactive legend rows are buttons; give them affordance + a focus ring. */\n button.oc-graph-legend-item {\n cursor: pointer;\n border-radius: 4px;\n\n &:hover {\n background: var(--oc-focus-ring);\n }\n\n &:focus-visible {\n outline: 2px solid var(--oc-focus);\n outline-offset: 1px;\n }\n }\n\n /* De-emphasized (toggled-off) category. */\n .oc-graph-legend-item--inactive {\n opacity: 0.45;\n }\n\n .oc-graph-legend-label {\n flex: 1 1 auto;\n }\n\n .oc-graph-legend-count {\n color: var(--oc-text-secondary);\n font-variant-numeric: tabular-nums;\n margin-left: auto;\n padding-left: 8px;\n }\n\n .oc-graph-legend-swatch {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n flex-shrink: 0;\n }\n\n /* Edge-legend swatches render as a short line, not a dot. */\n .oc-graph-legend-swatch--line {\n width: 12px;\n height: 3px;\n border-radius: 1px;\n }\n\n .oc-graph-search {\n position: absolute;\n top: 8px;\n left: 8px;\n\n & input {\n font-family: var(--oc-font-family);\n font-size: var(--oc-body-size);\n padding: 6px 10px;\n border: 1px solid var(--oc-border);\n border-radius: var(--oc-border-radius);\n background: var(--oc-bg);\n color: var(--oc-text);\n outline: none;\n\n &:focus {\n border-color: var(--oc-focus);\n box-shadow: 0 0 0 2px var(--oc-focus-ring-strong);\n }\n }\n }\n\n /* Dark mode graph overrides (darker bg for canvas-based rendering) */\n .oc-dark .oc-graph-wrapper,\n .oc-graph-wrapper.oc-dark {\n /* GitHub-dark canvas background, intentionally not a token */\n --oc-bg: #0d1117;\n }\n\n /*\n * graph-mount.ts stamps .oc-dark on both .oc-graph-wrapper and\n * .oc-graph-container simultaneously, so the self-variant\n * (.oc-dark.oc-graph-wrapper) and descendant variant\n * (.oc-dark .oc-graph-legend) resolve identically.\n */\n .oc-dark .oc-graph-legend,\n .oc-dark.oc-graph-wrapper .oc-graph-legend,\n .oc-dark .oc-graph-search input {\n background: rgba(13, 17, 23, 0.85);\n border-color: var(--oc-border);\n }\n}\n","@layer oc.components {\n /* ---------------------------------------------------------------------------\n * Legend\n * --------------------------------------------------------------------------- */\n\n .oc-legend {\n font-family: var(--oc-font-family);\n font-size: var(--oc-body-size);\n }\n\n .oc-legend-entry {\n cursor: default;\n }\n\n .oc-legend text {\n fill: var(--oc-legend-text);\n }\n}\n","@layer oc.components {\n /* ---------------------------------------------------------------------------\n * Series search (seriesSearch)\n *\n * DOM combobox overlaid on the SVG band the engine reserves below chrome.\n * The wrapper is absolutely positioned by the vanilla adapter (inline\n * left/top/width/height), so everything here is visual only. All colors come\n * from --oc-* tokens, which flip in .oc-dark.\n * --------------------------------------------------------------------------- */\n\n .oc-series-search {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n font-family: var(--oc-font-family);\n z-index: 10;\n }\n\n /* Chips: removable filter tokens for the current selection. They flow to the\n * left of the input and scroll horizontally rather than wrapping over the\n * chart when a reader picks many series. */\n .oc-series-search-chips {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n overflow-x: auto;\n scrollbar-width: none;\n }\n\n .oc-series-search-chips::-webkit-scrollbar {\n display: none;\n }\n\n .oc-series-search-chip {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 4px 3px 10px;\n border: 1px solid var(--oc-border);\n border-radius: 999px;\n background: var(--oc-secondary);\n color: var(--oc-text-secondary);\n font-size: 12px;\n font-weight: 500;\n line-height: 16px;\n white-space: nowrap;\n }\n\n .oc-series-search-chip-remove {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n /* 16px glyph, but a >=24px hit area (WCAG 2.5.5 target size) via padding\n pulled back with negative margin so the chip layout stays tight. */\n width: 16px;\n height: 16px;\n padding: 4px;\n margin: -4px -2px -4px -4px;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--oc-text-muted);\n cursor: pointer;\n transition:\n color 0.15s,\n background-color 0.15s;\n }\n\n .oc-series-search-chip-remove:hover {\n color: var(--oc-text);\n background: var(--oc-hover-bg);\n }\n\n .oc-series-search-chip-remove:focus-visible {\n outline: 2px solid var(--oc-focus);\n outline-offset: 1px;\n }\n\n .oc-series-search-chip-remove svg {\n display: block;\n }\n\n /* Combobox: input + popup listbox. Styling matches the table search input\n * (border, radius, focus ring) so the two search surfaces read as one system. */\n .oc-series-search-box {\n position: relative;\n flex: 0 1 220px;\n min-width: 120px;\n }\n\n .oc-series-search-icon {\n position: absolute;\n left: 9px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--oc-text-muted);\n pointer-events: none;\n }\n\n /* Shared skin provided by the grouped rule in table.css.\n Only divergent props live here. */\n .oc-series-search-input {\n height: 32px;\n padding: 0 10px 0 28px;\n }\n\n .oc-series-search-listbox {\n position: absolute;\n top: calc(100% + 4px);\n right: 0;\n min-width: 100%;\n max-height: 240px;\n overflow-y: auto;\n margin: 0;\n padding: 4px;\n list-style: none;\n border: 1px solid var(--oc-border);\n border-radius: 8px;\n background: var(--oc-card);\n box-shadow: var(--oc-tooltip-shadow);\n z-index: 20;\n }\n\n .oc-series-search-option {\n padding: 6px 10px;\n border-radius: 4px;\n font-size: 13px;\n color: var(--oc-text);\n cursor: pointer;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .oc-series-search-option[aria-selected=\"true\"],\n .oc-series-search-option:hover {\n background: var(--oc-hover-bg);\n }\n\n /* The typed substring within each suggestion. */\n .oc-series-search-match {\n font-weight: 600;\n }\n\n .oc-series-search-empty {\n padding: 6px 10px;\n font-size: 12px;\n color: var(--oc-text-muted);\n }\n}\n","@layer oc.animation {\n /* ---------------------------------------------------------------------------\n * Animation keyframes\n * --------------------------------------------------------------------------- */\n\n /* Bar entrance: clip-path reveal bottom-to-top + fade in (vertical columns).\n Bars grow upward from baseline with a soft opacity fade. */\n @keyframes oc-enter-bar {\n from {\n clip-path: inset(100% 0 0 0);\n opacity: 0;\n }\n 75% {\n opacity: 1;\n }\n to {\n clip-path: inset(0 0 0 0);\n opacity: 1;\n }\n }\n\n /* Bar entrance: clip-path reveal left-to-right + fade in (horizontal bars).\n Bars grow rightward from axis with a soft opacity fade. */\n @keyframes oc-enter-bar-h {\n from {\n clip-path: inset(0 100% 0 0);\n opacity: 0;\n }\n 75% {\n opacity: 1;\n }\n to {\n clip-path: inset(0 0 0 0);\n opacity: 1;\n }\n }\n\n /* Line/area entrance: clip-path reveal left-to-right + fade in.\n Lines draw in following reading direction with a soft lead-in. */\n @keyframes oc-enter-line {\n from {\n clip-path: inset(0 100% 0 0);\n opacity: 0;\n }\n 15% {\n opacity: 1;\n }\n to {\n clip-path: inset(0 0 0 0);\n opacity: 1;\n }\n }\n\n /* Point/arc entrance: scale up + fade in from center */\n @keyframes oc-enter-point {\n from {\n opacity: 0;\n transform: scale(0.3);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n }\n\n /* Map feature entrance: fade in while transitioning fill from a neutral base\n to the data-driven color stored in --oc-feature-fill. The target opacity is\n --oc-feature-target-opacity (default 1) so features under a focus dim\n animate straight to their dimmed rest opacity instead of hitting 1 and then\n snapping down when the dim is applied post-animation. */\n @keyframes oc-enter-map-fill {\n from {\n fill: var(--oc-secondary);\n opacity: 0;\n }\n to {\n fill: var(--oc-feature-fill);\n opacity: var(--oc-feature-target-opacity, 1);\n }\n }\n\n /* Map point pop-in: gentle scale from 0.75 + fade. Uses transform-origin to\n scale from the circle's own center (cx/cy) rather than the SVG origin. */\n @keyframes oc-enter-map-point {\n from {\n opacity: 0;\n transform: scale(0.75);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n }\n\n /* Pure opacity fade (no transform). Safe for elements with existing transforms\n like arc groups that use translate() for positioning. */\n @keyframes oc-enter-fade-only {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n }\n\n /* Fade + subtle slide up for text, rule, tick, annotations */\n @keyframes oc-enter-fade {\n from {\n opacity: 0;\n transform: translateY(4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n /* Table row entrance: slide up only, no opacity (cells fade independently\n to avoid opacity compounding between row and cell animations) */\n @keyframes oc-table-enter-row {\n from {\n transform: translateY(6px);\n }\n to {\n transform: translateY(0);\n }\n }\n\n /* Table bar fill entrance: clip-path grow only, no opacity\n (preserves resting opacity: 0.15 from table.css) */\n @keyframes oc-table-enter-bar-fill {\n from {\n clip-path: inset(0 100% 0 0);\n }\n to {\n clip-path: inset(0 0 0 0);\n }\n }\n\n /* Tooltip entrance */\n @keyframes oc-tooltip-in {\n from {\n opacity: 0;\n transform: translateY(2px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n}\n","@layer oc.animation {\n /* ---------------------------------------------------------------------------\n * Table entrance animations (.oc-table-wrapper.oc-animate)\n * --------------------------------------------------------------------------- */\n\n .oc-table-wrapper.oc-animate {\n /* Chrome (title/subtitle): fade + slide up */\n & > .oc-chrome {\n animation: oc-enter-fade calc(var(--oc-animation-duration) * 0.6)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n }\n\n /* Table header: quick fade before rows start */\n & thead {\n animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.4)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n }\n\n /* Row entrance: slide up only, no opacity (cells handle their own fading\n to avoid opacity compounding between row and cell animations) */\n & tbody tr {\n animation: oc-table-enter-row var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-row-index, 0));\n }\n\n /* Cell text: fade in synced with row slide */\n & tbody td {\n animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.5)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-row-index, 0));\n }\n\n /* Heatmap/category cell backgrounds: longer fade, delayed after row appears */\n & td.oc-table-heatmap,\n & td.oc-table-category {\n animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.7)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(\n var(--oc-animation-stagger) *\n var(--oc-row-index, 0) +\n var(--oc-animation-duration) *\n 0.3\n );\n }\n\n /* Bar fill: clip-path grow only (no opacity change to preserve resting\n opacity: 0.15 from table.css) */\n & .oc-table-bar-fill {\n animation: oc-table-enter-bar-fill calc(var(--oc-animation-duration) * 0.8)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(\n var(--oc-animation-stagger) *\n var(--oc-row-index, 0) +\n var(--oc-animation-duration) *\n 0.3\n );\n }\n\n /* Sparkline SVG: clip-path reveal left-to-right. Targets the SVG directly\n (not the wrapper) to avoid clipping absolutely-positioned dots and labels. */\n & .oc-table-sparkline > svg {\n animation: oc-enter-line calc(var(--oc-animation-duration) * 0.8)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(\n var(--oc-animation-stagger) *\n var(--oc-row-index, 0) +\n var(--oc-animation-duration) *\n 0.4\n );\n }\n\n /* Sparkline dots and labels: fade in after the line draws through */\n & .oc-table-sparkline-dot,\n & .oc-table-sparkline-labels {\n animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.3)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(\n var(--oc-animation-stagger) *\n var(--oc-row-index, 0) +\n var(--oc-animation-duration) *\n 0.8\n );\n }\n\n /* Search and pagination: quick fade on mount */\n & .oc-table-search,\n & .oc-table-pagination {\n animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.5)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n }\n }\n}\n","@layer oc.animation {\n /* ---------------------------------------------------------------------------\n * Animation scoped rules (.oc-animate enables animation on the chart root)\n * --------------------------------------------------------------------------- */\n\n .oc-animate {\n /* Vertical bars (default): smooth ease-out, no overshoot.\n oc-mark-bar is reserved for future mark type aliases. */\n & .oc-mark-rect rect,\n & .oc-mark-bar rect {\n animation: oc-enter-bar var(--oc-animation-duration) var(--oc-ease-smooth) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Horizontal bars (data-orient is on the mark group, not the SVG root) */\n & .oc-mark-rect[data-orient=\"horizontal\"] rect,\n & .oc-mark-bar[data-orient=\"horizontal\"] rect {\n animation-name: oc-enter-bar-h;\n }\n\n /* Stacked bar/column segments: chain sequentially so each segment starts\n right when the previous one finishes, creating one fluid reveal.\n Uses linear easing so handoffs between segments are seamless (no\n deceleration/acceleration stutter at segment boundaries). */\n & .oc-mark-rect[data-stack-pos] rect {\n animation-duration: var(--oc-stack-segment-duration, 150ms);\n animation-timing-function: linear;\n animation-delay: calc(\n var(--oc-animation-stagger) *\n var(--oc-mark-index, 0) +\n var(--oc-stack-pos, 0) *\n var(--oc-stack-segment-duration, 150ms)\n );\n }\n\n /* Line marks: entire group clips left-to-right (no WAAPI needed) */\n & .oc-mark-line {\n animation: oc-enter-line var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Area marks: entire group clips left-to-right + fades in */\n & .oc-mark-area {\n animation: oc-enter-line var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Arc marks (pie/donut slices): simple fade in.\n Scale transforms break arc positioning because arcs use translate()\n on parent groups. A clean fade is more elegant for pie/donut anyway. */\n & .oc-mark-arc {\n animation: oc-enter-fade-only var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Point marks (scatter, dot).\n Points render as bare <circle> elements with class oc-mark-point directly,\n not wrapped in a group, so we target circle.oc-mark-point (not descendant).\n Duration is 40% of the configured duration (quick pop-in relative to other marks). */\n & circle.oc-mark-point,\n & circle.oc-mark-circle {\n animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.4)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Points on line/area charts: delay so they pop in as the line draws through. */\n & .oc-mark-line ~ circle.oc-mark-point,\n & .oc-mark-area ~ circle.oc-mark-point {\n animation-delay: calc(\n var(--oc-animation-duration) *\n 0.35 +\n var(--oc-animation-stagger) *\n var(--oc-mark-index, 0)\n );\n }\n\n /* Text marks: fade + slight slide up */\n & .oc-mark-text text {\n animation: oc-enter-fade calc(var(--oc-animation-duration) * 0.6)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Rule marks: fade in */\n & .oc-mark-rule line {\n animation: oc-enter-fade calc(var(--oc-animation-duration) * 0.5)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Tick marks: fade in */\n & .oc-mark-tick line {\n animation: oc-enter-fade calc(var(--oc-animation-duration) * 0.5)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Data labels: fade after their parent mark */\n & .oc-mark-label {\n animation: oc-enter-fade 300ms var(--oc-ease-smooth) both;\n animation-delay: calc(\n var(--oc-animation-stagger) *\n var(--oc-mark-index, 0) +\n var(--oc-animation-duration) *\n 0.7\n );\n }\n\n /* Annotations: fade in after marks finish */\n & .oc-annotation {\n animation: oc-enter-fade 400ms var(--oc-ease-smooth) both;\n animation-delay: calc(var(--oc-animation-duration) + var(--oc-annotation-delay, 200ms));\n }\n\n /* Tilemap tiles: fade in with jittered stagger for organic feel.\n Per-tile delay is computed in the renderer with pseudo-random variation. */\n & .oc-tilemap-tile {\n animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.35) ease-in both;\n animation-delay: var(--oc-tile-delay, 0ms);\n }\n\n /* Map features: fill color reveal with shuffled stagger.\n Per-feature --oc-map-delay is computed in the renderer via a seeded\n Fisher-Yates shuffle so features pop in organically. */\n & .oc-map-feature {\n animation: oc-enter-map-fill var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: var(--oc-map-delay, 0ms);\n }\n\n /* Bulk mode: high feature counts (counties, ~3k+) get a single group\n fade instead of per-feature fill animations. opacity is GPU-compositable\n so this stays smooth regardless of child count. */\n & .oc-map-features[data-bulk-animate] {\n animation: oc-enter-fade-only var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n }\n\n & .oc-map-features[data-bulk-animate] .oc-map-feature {\n animation: none;\n }\n\n /* Map borders: fade in near the end of the feature sweep */\n & .oc-map-borders path {\n animation: oc-enter-fade-only calc(var(--oc-animation-duration) * 0.4) ease-in both;\n animation-delay: calc(var(--oc-animation-duration) * 0.7);\n }\n\n /* Map points: gentle scale (0.75->1) + fade pop-in after borders.\n transform-origin set per-circle in the renderer so scale happens\n around each circle's own center, not the SVG origin. */\n & .oc-map-point {\n animation: oc-enter-map-point calc(var(--oc-animation-duration) * 0.4)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(\n var(--oc-animation-duration) *\n 0.8 +\n var(--oc-mark-index, 0) *\n var(--oc-point-stagger, 60ms)\n );\n }\n\n /* Sankey nodes: fade in with stagger by column depth (left-to-right) */\n & .oc-sankey-node rect {\n animation: oc-enter-fade-only var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(var(--oc-animation-stagger) * var(--oc-mark-index, 0));\n }\n\n /* Sankey links: fade in after nodes, with stagger.\n Links delay by 30% of duration so nodes appear first, then links flow in. */\n & .oc-sankey-link path {\n animation: oc-enter-fade-only var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: calc(\n var(--oc-animation-duration) *\n 0.3 +\n var(--oc-animation-stagger) *\n var(--oc-mark-index, 0)\n );\n }\n\n /* Barlist rows: fade + slide up per row, bars grow left-to-right */\n & .oc-barlist-row {\n animation: oc-enter-fade calc(var(--oc-animation-duration) * 0.6)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: var(--oc-row-delay, 0ms);\n }\n\n & .oc-barlist-bar {\n animation: oc-enter-bar-h var(--oc-animation-duration)\n var(--oc-animation-ease, var(--oc-ease-smooth)) both;\n animation-delay: var(--oc-row-delay, 0ms);\n }\n }\n\n /* ---------------------------------------------------------------------------\n * Sparkline mode: pair the line/area reveal with a stronger ease-out so it\n * doesn't read as a uniform left-to-right swipe. cubic-bezier(0.16, 1, 0.3, 1)\n * is \"expo-out\" — fast initial draw that decelerates noticeably at the end,\n * giving the trend a hand-drawn feel. Duration is bumped via the engine\n * (compile.ts) so the cleanup timer stays in sync.\n * --------------------------------------------------------------------------- */\n\n .oc-animate[data-display=\"sparkline\"] .oc-mark-line,\n .oc-animate[data-display=\"sparkline\"] .oc-mark-area {\n animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);\n }\n}\n","/* ---------------------------------------------------------------------------\n * Reduced motion\n *\n * Catch-all: last-declared layer beats every earlier layer regardless of\n * specificity. No !important, no hand-maintained selector mirror.\n *\n * Selector list matches CSS_TOKEN_ROOT_SELECTORS in token-definitions.ts.\n * --------------------------------------------------------------------------- */\n\n@layer oc.reduced-motion {\n @media (prefers-reduced-motion: reduce) {\n :is(\n .oc-root,\n .oc-chart-root,\n .oc-table-wrapper,\n .oc-table-root,\n .oc-graph-wrapper,\n .oc-graph-root,\n .oc-sankey-root,\n .oc-tilemap-root,\n .oc-barlist-root,\n .oc-map-root\n ),\n :is(\n .oc-root,\n .oc-chart-root,\n .oc-table-wrapper,\n .oc-table-root,\n .oc-graph-wrapper,\n .oc-graph-root,\n .oc-sankey-root,\n .oc-tilemap-root,\n .oc-barlist-root,\n .oc-map-root\n )\n *,\n :is(\n .oc-root,\n .oc-chart-root,\n .oc-table-wrapper,\n .oc-table-root,\n .oc-graph-wrapper,\n .oc-graph-root,\n .oc-sankey-root,\n .oc-tilemap-root,\n .oc-barlist-root,\n .oc-map-root\n )\n *::before,\n :is(\n .oc-root,\n .oc-chart-root,\n .oc-table-wrapper,\n .oc-table-root,\n .oc-graph-wrapper,\n .oc-graph-root,\n .oc-sankey-root,\n .oc-tilemap-root,\n .oc-barlist-root,\n .oc-map-root\n )\n *::after {\n animation: none;\n transition: none;\n }\n }\n}\n"],"names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendata-ai/openchart-core",
3
- "version": "8.0.0",
3
+ "version": "8.1.1",
4
4
  "description": "Types, theme, colors, accessibility, and utilities for openchart",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Riley Hilliard",
@@ -5044,7 +5044,7 @@
5044
5044
  },
5045
5045
  "padding": {
5046
5046
  "type": "number",
5047
- "description": "Padding for band/point scales."
5047
+ "description": "Padding for band/point scales. Point-scale default (a categorical x/y on a line/area/point mark) is 0 for line and area marks (the trend runs flush to the plot edges) and 0.5 for other marks (e.g. scatter, so points don't collide with the axis). `paddingOuter` is accepted as an alias here since point scales have no inner bands."
5048
5048
  },
5049
5049
  "paddingInner": {
5050
5050
  "type": "number",
@@ -9190,7 +9190,7 @@
9190
9190
  },
9191
9191
  "watermark": {
9192
9192
  "type": "boolean",
9193
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
9193
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
9194
9194
  },
9195
9195
  "hiddenSeries": {
9196
9196
  "type": "array",
@@ -5073,7 +5073,7 @@
5073
5073
  },
5074
5074
  "padding": {
5075
5075
  "type": "number",
5076
- "description": "Padding for band/point scales."
5076
+ "description": "Padding for band/point scales. Point-scale default (a categorical x/y on a line/area/point mark) is 0 for line and area marks (the trend runs flush to the plot edges) and 0.5 for other marks (e.g. scatter, so points don't collide with the axis). `paddingOuter` is accepted as an alias here since point scales have no inner bands."
5077
5077
  },
5078
5078
  "paddingInner": {
5079
5079
  "type": "number",
@@ -9616,7 +9616,7 @@
9616
9616
  },
9617
9617
  "watermark": {
9618
9618
  "type": "boolean",
9619
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
9619
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
9620
9620
  },
9621
9621
  "hiddenSeries": {
9622
9622
  "type": "array",
@@ -10080,7 +10080,7 @@
10080
10080
  },
10081
10081
  "watermark": {
10082
10082
  "type": "boolean",
10083
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
10083
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
10084
10084
  },
10085
10085
  "hiddenSeries": {
10086
10086
  "type": "array",
@@ -10544,7 +10544,7 @@
10544
10544
  },
10545
10545
  "watermark": {
10546
10546
  "type": "boolean",
10547
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
10547
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
10548
10548
  },
10549
10549
  "hiddenSeries": {
10550
10550
  "type": "array",
@@ -11008,7 +11008,7 @@
11008
11008
  },
11009
11009
  "watermark": {
11010
11010
  "type": "boolean",
11011
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
11011
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
11012
11012
  },
11013
11013
  "hiddenSeries": {
11014
11014
  "type": "array",
@@ -11472,7 +11472,7 @@
11472
11472
  },
11473
11473
  "watermark": {
11474
11474
  "type": "boolean",
11475
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
11475
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
11476
11476
  },
11477
11477
  "hiddenSeries": {
11478
11478
  "type": "array",
@@ -11936,7 +11936,7 @@
11936
11936
  },
11937
11937
  "watermark": {
11938
11938
  "type": "boolean",
11939
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
11939
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
11940
11940
  },
11941
11941
  "hiddenSeries": {
11942
11942
  "type": "array",
@@ -12400,7 +12400,7 @@
12400
12400
  },
12401
12401
  "watermark": {
12402
12402
  "type": "boolean",
12403
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
12403
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
12404
12404
  },
12405
12405
  "hiddenSeries": {
12406
12406
  "type": "array",
@@ -12864,7 +12864,7 @@
12864
12864
  },
12865
12865
  "watermark": {
12866
12866
  "type": "boolean",
12867
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
12867
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
12868
12868
  },
12869
12869
  "hiddenSeries": {
12870
12870
  "type": "array",
@@ -13328,7 +13328,7 @@
13328
13328
  },
13329
13329
  "watermark": {
13330
13330
  "type": "boolean",
13331
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
13331
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
13332
13332
  },
13333
13333
  "hiddenSeries": {
13334
13334
  "type": "array",
@@ -13792,7 +13792,7 @@
13792
13792
  },
13793
13793
  "watermark": {
13794
13794
  "type": "boolean",
13795
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
13795
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
13796
13796
  },
13797
13797
  "hiddenSeries": {
13798
13798
  "type": "array",
@@ -14256,7 +14256,7 @@
14256
14256
  },
14257
14257
  "watermark": {
14258
14258
  "type": "boolean",
14259
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
14259
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
14260
14260
  },
14261
14261
  "hiddenSeries": {
14262
14262
  "type": "array",
@@ -14720,7 +14720,7 @@
14720
14720
  },
14721
14721
  "watermark": {
14722
14722
  "type": "boolean",
14723
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
14723
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
14724
14724
  },
14725
14725
  "hiddenSeries": {
14726
14726
  "type": "array",
@@ -15184,7 +15184,7 @@
15184
15184
  },
15185
15185
  "watermark": {
15186
15186
  "type": "boolean",
15187
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
15187
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
15188
15188
  },
15189
15189
  "hiddenSeries": {
15190
15190
  "type": "array",
@@ -15648,7 +15648,7 @@
15648
15648
  },
15649
15649
  "watermark": {
15650
15650
  "type": "boolean",
15651
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
15651
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
15652
15652
  },
15653
15653
  "hiddenSeries": {
15654
15654
  "type": "array",
@@ -16112,7 +16112,7 @@
16112
16112
  },
16113
16113
  "watermark": {
16114
16114
  "type": "boolean",
16115
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
16115
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
16116
16116
  },
16117
16117
  "hiddenSeries": {
16118
16118
  "type": "array",
@@ -16576,7 +16576,7 @@
16576
16576
  },
16577
16577
  "watermark": {
16578
16578
  "type": "boolean",
16579
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
16579
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
16580
16580
  },
16581
16581
  "hiddenSeries": {
16582
16582
  "type": "array",
@@ -17089,6 +17089,29 @@
17089
17089
  },
17090
17090
  "description": "Per-node visual overrides, keyed by node id."
17091
17091
  },
17092
+ "seedNode": {
17093
+ "anyOf": [
17094
+ {
17095
+ "type": "string"
17096
+ },
17097
+ {
17098
+ "type": "object",
17099
+ "properties": {
17100
+ "id": {
17101
+ "type": "string"
17102
+ },
17103
+ "style": {
17104
+ "$ref": "#/definitions/NodeOverride"
17105
+ }
17106
+ },
17107
+ "required": [
17108
+ "id"
17109
+ ],
17110
+ "additionalProperties": false
17111
+ }
17112
+ ],
17113
+ "description": "Node treated as the graph's seed/root: gets a distinct ring and an always-on label without needing a `nodeOverrides` entry, and stays lit when a category highlight or legend filter dims everything else.\n\nString shorthand is the node id; the object form adds styling that layers under any `nodeOverrides` entry for the same id (an explicit override always wins). The seed's radius is deliberately left to the `nodeSize` encoding — set `style.radius` to make it bigger.\n\nNOT related to `layout.seed`, which is the deterministic RNG seed for the force layout. This field names a node; `layout.seed` is a number.\n\nAn id that isn't in `nodes` warns and is ignored (it does not throw), so a filtered or paginated node update can't crash the host."
17114
+ },
17092
17115
  "chrome": {
17093
17116
  "$ref": "#/definitions/Chrome",
17094
17117
  "description": "Editorial chrome."
@@ -18253,7 +18276,7 @@
18253
18276
  },
18254
18277
  "key": {
18255
18278
  "$ref": "#/definitions/EncodingChannel",
18256
- "description": "Stable id for event callbacks. Data-update transitions for points are not yet supported."
18279
+ "description": "Stable id for event callbacks, and the key used to match points across renders so fill, radius, and position tween on data update."
18257
18280
  },
18258
18281
  "opacity": {
18259
18282
  "type": "number",
@@ -18518,7 +18541,7 @@
18518
18541
  },
18519
18542
  "watermark": {
18520
18543
  "type": "boolean",
18521
- "description": "Whether to show the tryOpenData.ai watermark. Defaults to true."
18544
+ "description": "Whether to show the tryOpenData.ai watermark. Defaults to true; auto-hides when the container is under 200px tall (the cramped height class) unless explicitly set."
18522
18545
  },
18523
18546
  "hiddenSeries": {
18524
18547
  "type": "array",