{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://svgrid.com/schemas/chart-spec.json",
  "title": "ChartSpec",
  "type": "object",
  "properties": {
    "type": {
      "$ref": "#/$defs/ChartType",
      "description": "Default type for series that don't set their own `type`."
    },
    "categories": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "X-axis labels (one per data point)."
    },
    "series": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/ChartSeries"
      }
    },
    "width": {
      "type": "number"
    },
    "height": {
      "type": "number"
    },
    "palette": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Palette used when a series has no explicit `color`."
    },
    "categoryColors": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      },
      "description": "Per-category color overrides (by category label) - for pie / donut slice recolouring, where colour follows the category, not a series."
    },
    "valueFormat": {
      "$ref": "#/$defs/ChartValueFormat",
      "description": "Number format for the value axis, tooltips, data labels and reference lines. Unset = the default compact `1.2k` / `1.2M` style."
    },
    "locale": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      ],
      "description": "BCP-47 locale for value formatting. Setting this (or `currency`) switches formatting to `Intl.NumberFormat`, so thousands separators, the decimal mark and the compact suffixes follow the locale rather than the built-in English `1.2k` / `1.2M`. Unset = the locale-free default, which is why the default output has never changed under anyone's feet."
    },
    "currency": {
      "type": "string",
      "description": "ISO 4217 code for `valueFormat: 'currency'` (`'EUR'`, `'JPY'`, ...). Unset means the axis reads `$`, which is wrong everywhere outside the dollar zone and was the only currency this chart could draw for a long time."
    },
    "categoryGroups": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string"
          },
          "span": {
            "type": "number"
          }
        },
        "required": [
          "label",
          "span"
        ]
      },
      "description": "Grouped (nested) category axis: a parent tier spanning consecutive leaf categories (spans must sum to `categories.length`). Vertical category charts only (ignored for time / horizontal / pie)."
    },
    "stacked": {
      "type": "boolean",
      "description": "Stack bar / area series (per axis) instead of grouping them."
    },
    "stacked100": {
      "type": "boolean",
      "description": "Stack to 100% (each category normalized to its total). Implies stacked."
    },
    "orientation": {
      "type": "string",
      "enum": [
        "vertical",
        "horizontal"
      ],
      "description": "Bar orientation. `'horizontal'` swaps the axes - categories run down the left, bars grow rightward - which suits long category labels. Only applies when every series is a bar (combo / line / area fall back to vertical)."
    },
    "innerRadius": {
      "type": "number",
      "description": "Pie only: inner radius as a fraction of the outer radius (0..1) -> donut."
    },
    "referenceLines": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/ChartReferenceLine"
      },
      "description": "Horizontal target / goal / average lines."
    },
    "xType": {
      "type": "string",
      "enum": [
        "category",
        "time",
        "ordinal-time",
        "number"
      ],
      "description": "How to read `categories` along the x axis. - `'category'` (default): evenly spaced labels, taken literally. - `'time'`: parsed as dates and positioned by ACTUAL elapsed time, so an irregular gap renders as a proportional gap. - `'ordinal-time'`: parsed as dates but spaced EVENLY, with date-derived ticks. This is what a series of trading sessions or business days needs: on a true time axis every weekend opens a hole a third as wide as the working week, which is noise rather than information."
    },
    "yAxisTitle": {
      "type": "string",
      "description": "Axis titles (reserve gutter space + render)."
    },
    "y2AxisTitle": {
      "type": "string"
    },
    "xAxisTitle": {
      "type": "string"
    },
    "yScale": {
      "type": "string",
      "enum": [
        "linear",
        "log"
      ],
      "description": "Y-axis scale. `'log'` plots base-10 logarithmic - values <= 0 are treated as missing. Necessary for wide-range data (money, audience size, scientific). Default `'linear'`."
    },
    "y2Scale": {
      "type": "string",
      "enum": [
        "linear",
        "log"
      ],
      "description": "Right (secondary) Y-axis scale. Default `'linear'`."
    },
    "xAxis": {
      "$ref": "#/$defs/ChartAxisConfig",
      "description": "The x axis in full: type, title, tick formatter, label rotation, grid lines, reversal. The flat `xType` / `xAxisTitle` fill in what is unset."
    },
    "yAxis": {
      "$ref": "#/$defs/ChartAxisConfig",
      "description": "The left value axis in full: min / max, tick count or interval, format, title, grid lines, reversal, scale. `yAxisTitle` / `yScale` fill in. A radar or radial column reads `min` / `max` as its rim."
    },
    "y2Axis": {
      "$ref": "#/$defs/ChartAxisConfig",
      "description": "The right value axis, for series with `axis: 'right'`."
    },
    "title": {
      "type": "string",
      "description": "Chart title, drawn centred above the plot. Reserves its own room."
    },
    "subtitle": {
      "type": "string",
      "description": "Smaller line under the title."
    },
    "caption": {
      "type": "string",
      "description": "Small text under the plot, left-aligned: a source, a note, a footnote."
    },
    "referenceBands": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/ChartReferenceBand"
      },
      "description": "Shaded bands behind the marks, on a value axis or across the x axis."
    },
    "nullAs": {
      "type": "string",
      "enum": [
        "gap",
        "zero"
      ],
      "description": "What a `null` / `NaN` value means. `'gap'` (default) breaks the line and draws no mark; `'zero'` plots it as 0. Per-series `nullAs` beats this."
    },
    "decimate": {
      "$ref": "#/$defs/ChartDecimateConfig",
      "description": "Thin long line / area series before layout. Default `'auto'`: only when there are more points than pixels. See ."
    },
    "dataLabels": {
      "$ref": "#/$defs/ChartDataLabelConfig",
      "description": "Data labels as the spec's own default; the component's `dataLabels` prop, when set, replaces it. On a pie, `placement: 'outside'` draws callout labels with leader lines instead of percentages on the slices."
    },
    "annotations": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/ChartAnnotation"
      },
      "description": "Pinned text labels at fixed data-space positions (callouts)."
    },
    "seriesLabels": {
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "object",
          "properties": {
            "formatter": {
              "$comment": "a function; set it in code, it has no JSON form"
            }
          }
        }
      ],
      "description": "Name each line / area series at its last point, so a reader never has to match colours against a legend. `true` writes the series label; a formatter gets the label and the last value. Labels on a crowded right edge are pushed apart so they stay legible. Cartesian charts only."
    },
    "responsive": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/ChartResponsiveRule"
      },
      "description": "Rules that patch the spec by the rendered size, checked top to bottom, every matching rule applied in turn (later wins). A rule's `spec` is a partial spec merged over this one; the axis objects merge one level deep, everything else replaces. A rule cannot change `width`, `height` or `responsive` itself. `{ maxWidth: 480, spec: { xAxis: { labelRotation: 90 }, seriesLabels: false } }` keeps a phone-width chart readable; the `legend` field, when set, moves or hides `SvChart`'s legend the same way."
    },
    "style": {
      "$ref": "#/$defs/ChartStyle",
      "description": "This chart's font, size, background and text / grid colours, over the theme tokens. See ."
    },
    "drawings": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/ChartDrawing"
      },
      "description": "Reader drawings (trend lines, rays, Fibonacci levels, notes) in data space. `SvChart`'s drawing tools produce these; see ."
    },
    "lastPriceLine": {
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "object",
          "properties": {
            "label": {
              "type": "string"
            },
            "color": {
              "type": "string"
            }
          }
        }
      ],
      "description": "A dashed line at the last close (or the last value of the first series) with its value in a pill on the axis, green when the last bar closed up and red when it closed down."
    },
    "patternFallback": {
      "type": "boolean",
      "description": "When true, automatically cycle through pattern fills for every series that doesn't set `pattern` explicitly. Useful as a one-flag colorblind fallback. Default false."
    },
    "calendarValues": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string"
          },
          "value": {
            "type": "number"
          }
        },
        "required": [
          "date",
          "value"
        ]
      },
      "description": "Calendar heatmap: array of date+value samples (one per day). Date strings are 'YYYY-MM-DD'. Missing days render as blank cells."
    },
    "calendarStart": {
      "type": "string",
      "description": "Calendar heatmap: year window. Default: span the data."
    },
    "calendarEnd": {
      "type": "string"
    },
    "gaugeValue": {
      "type": "number",
      "description": "Gauge: the value to display."
    },
    "gaugeMin": {
      "type": "number",
      "description": "Gauge: min/max of the dial scale. Defaults [0, 100]."
    },
    "gaugeMax": {
      "type": "number"
    },
    "gaugeTarget": {
      "type": "number",
      "description": "Gauge: target marker (the line/notch on the arc)."
    },
    "gaugeRanges": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "from": {
            "type": "number"
          },
          "to": {
            "type": "number"
          },
          "color": {
            "type": "string"
          }
        },
        "required": [
          "from",
          "to",
          "color"
        ]
      },
      "description": "Gauge: color bands along the arc (e.g. red/amber/green)."
    },
    "gaugeUnit": {
      "type": "string",
      "description": "Gauge: unit / suffix shown next to the value (e.g. '%', 'ms')."
    },
    "treemap": {
      "$ref": "#/$defs/TreeNode",
      "description": "Tree-map: hierarchical root. Leaves have `value`; parents are the sum of their children's totals."
    },
    "sankeyNodes": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "color": {
            "type": "string"
          }
        },
        "required": [
          "id"
        ]
      },
      "description": "Sankey: nodes + flow links between them. Link `source` / `target` reference node ids."
    },
    "sankeyLinks": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "source": {
            "type": "string"
          },
          "target": {
            "type": "string"
          },
          "value": {
            "type": "number"
          },
          "color": {
            "type": "string"
          }
        },
        "required": [
          "source",
          "target",
          "value"
        ]
      }
    },
    "waterfallTotals": {
      "type": "array",
      "items": {
        "type": "boolean"
      },
      "description": "Waterfall: per-category flag marking bars as totals / subtotals, drawn from 0 to the running sum. A total with a value of 0 shows the sum the steps before it reached; a total with a value sets the running sum to that value, which is how a bridge opens on \"Revenue 4300\". Same length as `categories`."
    },
    "waterfallColors": {
      "type": "object",
      "properties": {
        "positive": {
          "type": "string"
        },
        "negative": {
          "type": "string"
        },
        "total": {
          "type": "string"
        }
      },
      "description": "Waterfall: explicit colors for positive/negative/total bars. The series color is ignored when this is set."
    },
    "candleColors": {
      "type": "object",
      "properties": {
        "up": {
          "type": "string"
        },
        "down": {
          "type": "string"
        }
      },
      "description": "Candlestick / OHLC colors. Direction beats series identity here, the same way `waterfallColors` overrides the series color. Defaults to the green / red pair from the palette's own vocabulary."
    },
    "candleStyle": {
      "type": "string",
      "enum": [
        "classic",
        "hollow",
        "heikin-ashi"
      ],
      "description": "How candles are drawn. `'classic'` (default) fills a down candle and leaves an up candle hollow. `'hollow'` colours by close vs the PREVIOUS close and hollows by close vs open, the trader's four-state candle. `'heikin-ashi'` replaces each candle with the smoothed average form before drawing, so trends read as runs of one colour."
    },
    "funnelShape": {
      "type": "string",
      "enum": [
        "trapezoid",
        "pyramid",
        "cone"
      ],
      "description": "Funnel shape: stacked trapezoids (default), an inverted `'pyramid'`, or a `'cone'` that tapers to a point."
    },
    "stackOffset": {
      "type": "string",
      "enum": [
        "zero",
        "wiggle",
        "silhouette"
      ],
      "description": "Baseline for stacked areas. `'zero'` (default) stacks up from the axis; `'wiggle'` centres the stack on a baseline that minimises the wiggle of the layers (a stream graph); `'silhouette'` centres it on zero."
    },
    "binEdges": {
      "type": "array",
      "items": {
        "type": "number"
      },
      "description": "Histogram: the bin edges, one more than there are categories. Set by `binValues`; the x axis labels the edges rather than the bin centres."
    },
    "tree": {
      "$ref": "#/$defs/TreeNode",
      "description": "Hierarchy for a sunburst. An alias of `treemap`: either field feeds either chart."
    },
    "bulletRanges": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "from": {
            "type": "number"
          },
          "to": {
            "type": "number"
          },
          "color": {
            "type": "string"
          }
        },
        "required": [
          "from",
          "to",
          "color"
        ]
      },
      "description": "Bullet chart: qualitative ranges behind the measure bars (poor / ok / good), shared by every category. Falls back to `gaugeRanges`."
    },
    "colorScale": {
      "anyOf": [
        {
          "const": "sequential"
        },
        {
          "const": "diverging"
        },
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      ],
      "description": "Heatmap color scale. `'sequential'` maps min->max through one hue, `'diverging'` runs cold->neutral->warm around 0. A custom array (>=2 hex colors) defines an arbitrary gradient. Default `'sequential'`."
    }
  },
  "required": [
    "type",
    "categories",
    "series"
  ],
  "description": "What to plot - the input you build and hand to a chart. Categories are the x-axis labels and every series supplies one value per category. turns this into a for rendering.",
  "$comment": "Generated from packages/grid/src/chart-types.ts by tools/lib/chart-spec-schema.mjs; do not edit by hand.",
  "$defs": {
    "ChartType": {
      "title": "ChartType",
      "type": "string",
      "enum": [
        "bar",
        "line",
        "area",
        "pie",
        "scatter",
        "heatmap",
        "waterfall",
        "funnel",
        "radar",
        "calendar",
        "gauge",
        "treemap",
        "sankey",
        "candlestick",
        "ohlc",
        "boxplot",
        "histogram",
        "range-bar",
        "range-area",
        "lollipop",
        "dumbbell",
        "pareto",
        "stream",
        "sunburst",
        "radial-bar",
        "radial-column",
        "nightingale",
        "chord",
        "bullet"
      ],
      "description": "Every mark this engine can draw. `bar`, `line` and `area` compose (a series can override the spec's type for a combo chart); the rest are whole-chart types that ignore per-series overrides."
    },
    "ChartSeries": {
      "title": "ChartSeries",
      "type": "object",
      "properties": {
        "label": {
          "type": "string"
        },
        "values": {
          "type": "array",
          "items": {
            "type": "number"
          }
        },
        "color": {
          "type": "string"
        },
        "type": {
          "type": "string",
          "enum": [
            "bar",
            "line",
            "area",
            "scatter",
            "candlestick",
            "ohlc",
            "boxplot",
            "range-bar",
            "range-area",
            "lollipop",
            "dumbbell"
          ],
          "description": "Per-series chart type, for combo charts. Defaults to the spec `type`."
        },
        "ohlc": {
          "type": "array",
          "items": {
            "anyOf": [
              {
                "$ref": "#/$defs/OhlcBar"
              },
              {
                "type": "null"
              }
            ]
          },
          "description": "Open / high / low / close per category, parallel to `categories`. `null` is a gap (a day with no session) and draws nothing. Set `values` to the CLOSING prices alongside this. Everything that reads a series generically reads `values` - the tooltip rows, the CSV export, the screen-reader table, and `overlay` - so filling it in is what lets a candlestick series carry a moving average (`overlay: 'sma:20'`) or export to CSV without a single line of candle-specific code."
        },
        "volumes": {
          "type": "array",
          "items": {
            "type": "number"
          },
          "description": "Traded volume per category, parallel to `ohlc`. Read by the `'vwap'` overlay and the volume / OBV indicator panes."
        },
        "boxes": {
          "type": "array",
          "items": {
            "anyOf": [
              {
                "$ref": "#/$defs/BoxStats"
              },
              {
                "type": "null"
              }
            ]
          },
          "description": "Five-number summaries per category, parallel to `categories`. `null` is a gap and draws nothing. Set `values` to the MEDIANS alongside this, for exactly the reason `ohlc` sets them to the closes: everything that reads a series generically reads `values`, so the tooltip rows, the CSV export, the screen-reader table and `overlay` all keep working with no box-specific code. `boxStats()` turns a raw sample into one of these."
        },
        "errors": {
          "type": "array",
          "items": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "object",
                "properties": {
                  "lo": {
                    "type": "number"
                  },
                  "hi": {
                    "type": "number"
                  }
                },
                "required": [
                  "lo",
                  "hi"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "description": "Symmetric or asymmetric error bars, parallel to `values`. A number is a symmetric +/- margin; a pair is an explicit low/high; `null` draws nothing. These are an ANNOTATION on an existing mark rather than a mark of their own, so they compose: a bar, line, area or scatter series can carry them without changing its type. That is the whole reason they are not a `ChartType` - \"bar chart with error bars\" should not be a different chart."
        },
        "axis": {
          "type": "string",
          "enum": [
            "left",
            "right"
          ],
          "description": "Plot against the left (default) or right Y axis."
        },
        "points": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ScatterPoint"
          },
          "description": "Scatter / bubble points (used when `type === 'scatter'`)."
        },
        "rowIds": {
          "type": "array",
          "items": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                }
              ]
            }
          },
          "description": "Row IDs contributing to each data point - parallel to `values`. When present, click handlers receive these in `ChartSelection.rowIds` so callers can drill the grid back to the source rows."
        },
        "overlay": {
          "$ref": "#/$defs/SeriesOverlay",
          "description": "Draw a smoothing / trend overlay on top of this series."
        },
        "overlayColor": {
          "type": "string",
          "description": "Color for the overlay line. Defaults to the series color."
        },
        "pattern": {
          "$ref": "#/$defs/SeriesPattern",
          "description": "Texture fill (e.g. diagonal stripes) layered over the series color. Lets colorblind viewers tell two series apart even at the same hue."
        },
        "smooth": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "const": "monotone"
            }
          ],
          "description": "Interpolate the line as a curve instead of polylines. `'monotone'` cubic prevents overshoots between points (best default for data); `true` is an alias for `'monotone'`. Only meaningful for line/area."
        },
        "upperValues": {
          "type": "array",
          "items": {
            "type": "number"
          },
          "description": "Upper envelope (e.g. forecast 95th percentile) parallel to `values`. When set alongside `lowerValues`, the chart shades the band between the two as a translucent fill in the series color."
        },
        "lowerValues": {
          "type": "array",
          "items": {
            "type": "number"
          },
          "description": "Lower envelope; pair with `upperValues` for a confidence band."
        },
        "marker": {
          "anyOf": [
            {
              "$ref": "#/$defs/ChartMarkerShape"
            },
            {
              "$ref": "#/$defs/ChartMarker"
            }
          ],
          "description": "Marker drawn at every point of a line / area / scatter series. A shape name is shorthand for `{ shape }`. Default: a 3px circle."
        },
        "markers": {
          "type": "array",
          "items": {
            "anyOf": [
              {
                "$ref": "#/$defs/ChartMarker"
              },
              {
                "type": "null"
              }
            ]
          },
          "description": "Per-point marker overrides, parallel to `values`. `null` keeps the series marker; `{ shape: 'none' }` hides one point's marker."
        },
        "colors": {
          "type": "array",
          "items": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "description": "Per-point colours, parallel to `values`. `null` keeps the series colour. Colours one bar in a series red, or the last point of a line."
        },
        "stack": {
          "type": "string",
          "description": "Stack this bar series with the others naming the same stack, side by side with the rest: `{ stack: 'plan' }` on two series and `{ stack: 'actual' }` on two more draws two stacks per category. Named stacks stack even when `spec.stacked` is off; with it on, series without a name form the default stack. Bars and areas: two area series naming the same stack pile on each other while a third area with another name (or none) sits on the axis, and `stacked100` / `stackOffset` run per stack."
        },
        "strokeWidth": {
          "type": "number",
          "description": "Line width in px for a line / area series. Default 2."
        },
        "dash": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "array",
              "items": {
                "type": "number"
              }
            }
          ],
          "description": "Dash pattern for a line series: an SVG `stroke-dasharray` string or an array of lengths. Unset = solid."
        },
        "opacity": {
          "type": "number",
          "description": "Opacity of the whole series, 0..1. Default 1."
        },
        "visible": {
          "type": "boolean",
          "description": "Start hidden: the legend lists the series switched off and a click shows it. For a chart with many series where a few matter first."
        },
        "gradient": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "object",
              "properties": {
                "from": {
                  "type": "string"
                },
                "to": {
                  "type": "string"
                }
              }
            }
          ],
          "description": "Fade an area fill from the series colour at the top to transparent at the baseline (`true`), or between two explicit colours."
        },
        "step": {
          "type": "string",
          "enum": [
            "before",
            "after",
            "middle"
          ],
          "description": "Draw a line / area as steps instead of straight segments: the step turns `'before'` the point, `'after'` it, or in the `'middle'`."
        },
        "connectNulls": {
          "type": "boolean",
          "description": "Join the points either side of a gap instead of breaking the line. The gap still draws no marker. Default false."
        },
        "nullAs": {
          "type": "string",
          "enum": [
            "gap",
            "zero"
          ],
          "description": "What a `null` / `NaN` value means for this series. Beats the spec's."
        },
        "lowValues": {
          "type": "array",
          "items": {
            "type": "number"
          },
          "description": "The LOW end of a range series (`'range-bar'`, `'range-area'`, `'dumbbell'`), parallel to `values`, which holds the HIGH end. Keeping the high in `values` is the same contract `ohlc` and `boxes` follow: the tooltip, the CSV export, the screen-reader table and `overlay` all read `values` and keep working with no range-specific code."
        },
        "targets": {
          "type": "array",
          "items": {
            "type": "number"
          },
          "description": "Bullet charts: the target per category, drawn as a tick across the bar."
        }
      },
      "required": [
        "label",
        "values"
      ],
      "description": "One plotted series: its label, its values (one per category), and how to draw it."
    },
    "ChartValueFormat": {
      "title": "ChartValueFormat",
      "type": "string",
      "enum": [
        "number",
        "currency",
        "percent",
        "compact"
      ],
      "description": "Value-axis / tooltip / label number format."
    },
    "ChartReferenceLine": {
      "title": "ChartReferenceLine",
      "type": "object",
      "properties": {
        "value": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "string"
            }
          ],
          "description": "A value on the axis. For `axis: 'x'` this is the category label, an ISO date on a time axis, or a number on a numeric axis."
        },
        "label": {
          "type": "string"
        },
        "axis": {
          "type": "string",
          "enum": [
            "left",
            "right",
            "x"
          ]
        },
        "color": {
          "type": "string"
        },
        "dashed": {
          "type": "boolean"
        },
        "pill": {
          "type": "boolean",
          "description": "Draw the label as a filled pill on the value axis instead of a caption over the line, the way a last-price line reads on a ticker chart."
        }
      },
      "required": [
        "value"
      ],
      "description": "A reference / target line drawn across the plot. Horizontal on the left or right value axis by default. `axis: 'x'` draws it vertically at a category, a date (ISO string on a time axis) or a number (on a numeric axis) - \"Release v2 shipped here\", \"budget cut-off\"."
    },
    "ChartAxisConfig": {
      "title": "ChartAxisConfig",
      "type": "object",
      "properties": {
        "min": {
          "type": "number",
          "description": "Pin the low end of the domain. The data still stretches it if a value falls outside, unless `nice` is false."
        },
        "max": {
          "type": "number",
          "description": "Pin the high end of the domain."
        },
        "nice": {
          "type": "boolean",
          "description": "Round the domain out to tick boundaries. Default true. `false` uses the exact data extent (or `min` / `max`)."
        },
        "tickCount": {
          "type": "number",
          "description": "Approximate number of ticks. Default 4."
        },
        "tickInterval": {
          "type": "number",
          "description": "Exact spacing between ticks, in data units. Beats `tickCount`."
        },
        "format": {
          "$ref": "#/$defs/ChartValueFormat",
          "description": "Number format for this axis' labels. Defaults to the spec `valueFormat`."
        },
        "formatter": {
          "$comment": "a function; set it in code, it has no JSON form",
          "description": "Full control over each tick label. Beats `format`."
        },
        "title": {
          "type": "string",
          "description": "Axis title. Mirrors `xAxisTitle` / `yAxisTitle` / `y2AxisTitle`."
        },
        "gridLines": {
          "type": "boolean",
          "description": "Draw grid lines across the plot at each tick. Default true for the left value axis, false for the x and right axes."
        },
        "labelRotation": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "const": "auto"
            }
          ],
          "description": "Category-label rotation in degrees. `'auto'` (default) rotates -40 when labels are long or many; `0` never rotates."
        },
        "reversed": {
          "type": "boolean",
          "description": "Run the axis the other way: values decrease upward (or rightward)."
        },
        "scale": {
          "type": "string",
          "enum": [
            "linear",
            "log"
          ],
          "description": "Value axes (mirrors `yScale` / `y2Scale`), and a `type: 'number'` x axis: `'log'` spaces decades evenly, labels them, and drops categories at or below zero."
        },
        "type": {
          "type": "string",
          "enum": [
            "category",
            "time",
            "ordinal-time",
            "number"
          ],
          "description": "X axis only. Mirrors `xType`. `'number'` parses each category as a number and positions marks by value, so `['1', '2', '10']` spreads out."
        },
        "width": {
          "type": "number",
          "description": "Fixed gutter width in px for a value axis, so stacked charts (panes) share one left edge. Default: measured."
        },
        "labels": {
          "type": "boolean",
          "description": "Draw the tick labels. Default true. `false` keeps the ticks and grid lines but hides the text - what a pane above another pane wants."
        }
      },
      "description": "Per-axis configuration. Every field is optional and additive: the flat shortcuts on the spec (`yAxisTitle`, `yScale`, `xType`, ...) keep working and fill the matching field here when it is unset."
    },
    "ChartReferenceBand": {
      "title": "ChartReferenceBand",
      "type": "object",
      "properties": {
        "from": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "string"
            }
          ]
        },
        "to": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "string"
            }
          ]
        },
        "axis": {
          "type": "string",
          "enum": [
            "left",
            "right",
            "x"
          ]
        },
        "color": {
          "type": "string"
        },
        "opacity": {
          "type": "number",
          "description": "Fill opacity, 0..1. Default 0.08."
        },
        "label": {
          "type": "string"
        }
      },
      "required": [
        "from",
        "to"
      ],
      "description": "A shaded band between two values, behind the marks. On a value axis it is a horizontal stripe (\"target range 80..100\"); on `axis: 'x'` a vertical one (\"Q3\", \"outage window\", \"weekends\"). Values follow the same rules as ."
    },
    "ChartDecimateConfig": {
      "title": "ChartDecimateConfig",
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "const": "auto"
        },
        {
          "type": "object",
          "properties": {
            "method": {
              "type": "string",
              "enum": [
                "lttb",
                "minmax"
              ],
              "description": "`'lttb'` (default) keeps the visual shape; `'minmax'` keeps every extreme in each pixel bucket, which suits spiky monitoring data."
            },
            "target": {
              "type": "number",
              "description": "Points to keep. Default: the plot width in px."
            },
            "threshold": {
              "type": "number",
              "description": "Only decimate above this many points. Default: `target`."
            }
          }
        }
      ],
      "description": "How a long line / area series is thinned before layout. - `true` / `'auto'`: decimate when there are more points than pixels. - `false`: never (every point is laid out, however dense). - object: pick the method and the target point count."
    },
    "ChartDataLabelConfig": {
      "title": "ChartDataLabelConfig",
      "type": "object",
      "properties": {
        "show": {
          "type": "boolean",
          "description": "Draw the labels at all. Default true when the object is given."
        },
        "placement": {
          "type": "string",
          "enum": [
            "top",
            "inside",
            "outside",
            "center"
          ],
          "description": "Where a label sits on its mark. `'top'` above a bar / point (default), `'inside'` centred in the bar, `'outside'` past the end of a horizontal bar, `'center'` at the mark's centre."
        },
        "formatter": {
          "$comment": "a function; set it in code, it has no JSON form",
          "description": "Custom label text. Beats the value format."
        },
        "hideOverlap": {
          "type": "boolean",
          "description": "Hide labels that would overlap an earlier one. Default true."
        },
        "rotation": {
          "type": "number",
          "description": "Rotate every label by this many degrees about its anchor (`-45` reads up and to the right). The overlap test uses the rotated box."
        },
        "connector": {
          "type": "boolean",
          "description": "Instead of hiding a label that would overlap, push it away from its mark (up on a vertical chart, right on a horizontal one) until it is clear, and draw a leader line from the mark to it. Labels that cannot find room within four steps are dropped. Default false."
        }
      },
      "description": "Data-label placement and formatting. `dataLabels: true` on the component is shorthand for the defaults here."
    },
    "ChartAnnotation": {
      "title": "ChartAnnotation",
      "type": "object",
      "properties": {
        "at": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "category": {
                  "type": "string"
                },
                "series": {
                  "type": "string"
                }
              },
              "required": [
                "category"
              ]
            },
            {
              "type": "object",
              "properties": {
                "x": {
                  "type": "number"
                },
                "y": {
                  "type": "number"
                }
              },
              "required": [
                "x"
              ]
            }
          ],
          "description": "Anchor in data space. Provide either `category` + `axis` for a point on an existing series, OR raw `x` / `y` numeric coordinates in data space."
        },
        "label": {
          "type": "string"
        },
        "color": {
          "type": "string"
        },
        "placement": {
          "type": "string",
          "enum": [
            "top",
            "bottom",
            "left",
            "right"
          ],
          "description": "Where the label sits relative to the marker. Defaults to 'top'."
        },
        "shape": {
          "type": "string",
          "enum": [
            "dot",
            "flag",
            "pin",
            "square"
          ],
          "description": "The marker: a small `'dot'` (default), a `'flag'` on a pole, a map `'pin'`, or a `'square'`. Flags and pins carry the label INSIDE the marker, the way earnings and dividend flags read on a price chart."
        },
        "text": {
          "type": "string",
          "description": "Longer text shown in a tooltip when the marker is hovered or focused."
        }
      },
      "required": [
        "at",
        "label"
      ],
      "description": "A pinned label drawn over the plot, anchored to a data point or to an arbitrary (x, y) in data space. Useful for \"Release v1\", \"Outage\", etc."
    },
    "ChartResponsiveRule": {
      "title": "ChartResponsiveRule",
      "type": "object",
      "properties": {
        "maxWidth": {
          "type": "number",
          "description": "Applies when the rendered width is at most this many px."
        },
        "minWidth": {
          "type": "number",
          "description": "Applies when the rendered width is at least this many px."
        },
        "maxHeight": {
          "type": "number",
          "description": "Applies when the rendered height is at most this many px."
        },
        "spec": {
          "$ref": "#/$defs/PartialChartSpec",
          "description": "The partial spec to merge in."
        },
        "legend": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "const": "top"
            },
            {
              "const": "bottom"
            },
            {
              "const": "left"
            },
            {
              "const": "right"
            }
          ],
          "description": "Where `SvChart` puts its legend under this rule (`false` hides it)."
        }
      },
      "description": "One size-conditional patch; see ."
    },
    "ChartStyle": {
      "title": "ChartStyle",
      "type": "object",
      "properties": {
        "fontFamily": {
          "type": "string",
          "description": "The font family for every label. Default: inherited."
        },
        "fontSize": {
          "type": "number",
          "description": "The base type size in px; the chart's labels scale with it (12 is the default size, 14 makes every label a sixth larger)."
        },
        "background": {
          "type": "string",
          "description": "The plot background. Default: transparent over the page."
        },
        "textColor": {
          "type": "string",
          "description": "Titles, axis labels and data labels."
        },
        "gridColor": {
          "type": "string",
          "description": "Grid lines and axis lines."
        }
      },
      "description": "Per-chart looks that beat the theme tokens: the font, its size, the background and the text and grid colours. Applied as CSS custom properties on the chart's host, so an unset field keeps the token and a set one wins for this chart alone; PNG, SVG and PDF exports read the same values."
    },
    "ChartDrawing": {
      "title": "ChartDrawing",
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "kind": {
          "$ref": "#/$defs/ChartDrawingKind"
        },
        "points": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "x": {
                "anyOf": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "string"
                  }
                ]
              },
              "y": {
                "type": "number"
              }
            },
            "required": [
              "x",
              "y"
            ]
          }
        },
        "text": {
          "type": "string"
        },
        "color": {
          "type": "string"
        },
        "axis": {
          "type": "string",
          "enum": [
            "left",
            "right"
          ]
        }
      },
      "required": [
        "id",
        "kind",
        "points"
      ],
      "description": "A drawing on a cartesian chart, anchored in DATA space so it survives a re-layout, a zoom and a resize. `x` follows the reference-line rules (a category label, an ISO date on a time axis, a number on a numeric axis) and `y` is a value on the left axis (or the right one with `axis: 'right'`). Trend lines, arrows, rectangles and Fibonacci retracements take two points; a horizontal ray and a text label take one."
    },
    "TreeNode": {
      "title": "TreeNode",
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "value": {
          "type": "number"
        },
        "color": {
          "type": "string"
        },
        "children": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/TreeNode"
          }
        }
      },
      "required": [
        "name"
      ],
      "description": "A tree-map / sankey / treemap node spec. Used recursively as a tree."
    },
    "OhlcBar": {
      "title": "OhlcBar",
      "type": "object",
      "properties": {
        "o": {
          "type": "number"
        },
        "h": {
          "type": "number"
        },
        "l": {
          "type": "number"
        },
        "c": {
          "type": "number"
        }
      },
      "required": [
        "o",
        "h",
        "l",
        "c"
      ],
      "description": "One open / high / low / close bar."
    },
    "BoxStats": {
      "title": "BoxStats",
      "type": "object",
      "properties": {
        "min": {
          "type": "number"
        },
        "q1": {
          "type": "number"
        },
        "median": {
          "type": "number"
        },
        "q3": {
          "type": "number"
        },
        "max": {
          "type": "number"
        },
        "outliers": {
          "type": "array",
          "items": {
            "type": "number"
          },
          "description": "Values outside the whiskers, drawn as individual points."
        }
      },
      "required": [
        "min",
        "q1",
        "median",
        "q3",
        "max"
      ],
      "description": "A five-number summary: one box, its whiskers, and anything past them. `min` / `max` are the WHISKER ENDS, not the extremes of the sample - with the usual 1.5 IQR rule those differ, and the points beyond go in `outliers` so they can be drawn individually."
    },
    "ScatterPoint": {
      "title": "ScatterPoint",
      "type": "object",
      "properties": {
        "x": {
          "type": "number"
        },
        "y": {
          "type": "number"
        },
        "r": {
          "type": "number"
        },
        "label": {
          "type": "string"
        }
      },
      "required": [
        "x",
        "y"
      ],
      "description": "A single scatter / bubble point."
    },
    "SeriesOverlay": {
      "title": "SeriesOverlay",
      "anyOf": [
        {
          "const": "linear"
        },
        {
          "type": "string",
          "pattern": "^sma:\\d+(?:\\.\\d+)?$"
        },
        {
          "type": "string",
          "pattern": "^ema:\\d+(?:\\.\\d+)?$"
        },
        {
          "type": "string",
          "pattern": "^wma:\\d+(?:\\.\\d+)?$"
        },
        {
          "type": "string",
          "pattern": "^bb:\\d+(?:\\.\\d+)?:\\d+(?:\\.\\d+)?$"
        },
        {
          "const": "vwap"
        },
        {
          "type": "string",
          "pattern": "^poly:\\d+(?:\\.\\d+)?$"
        },
        {
          "const": "exp"
        },
        {
          "const": "log"
        },
        {
          "const": "power"
        }
      ],
      "description": "A statistical / smoothing line drawn on top of a source series. - `'linear'`: ordinary least-squares regression line - `'sma:N'`: simple moving average over a window of N points - `'ema:N'`: exponential moving average with smoothing factor 2/(N+1) - `'wma:N'`: weighted moving average, the latest point weighing N - `'bb:N:K'`: Bollinger bands, an N-point average with a shaded band K standard deviations either side - `'vwap'`: volume-weighted average price; needs `ohlc` and `volumes` - `'poly:N'`: least-squares polynomial of degree N (2..6) - `'exp'`: exponential fit y = a * e^(b x); positive values only - `'log'`: logarithmic fit y = a + b * ln(x + 1) - `'power'`: power-law fit y = a * (x + 1)^b; positive values only The regressions (`linear`, `poly`, `exp`, `log`, `power`) carry their R-squared and equation on the overlay line, and the tooltip reads them."
    },
    "SeriesPattern": {
      "title": "SeriesPattern",
      "type": "string",
      "enum": [
        "solid",
        "stripe",
        "crosshatch",
        "dots",
        "diagonal"
      ],
      "description": "A texture fill applied in addition to (and on top of) the series color. Helps colorblind readers distinguish series at a glance."
    },
    "ChartMarkerShape": {
      "title": "ChartMarkerShape",
      "type": "string",
      "enum": [
        "circle",
        "square",
        "diamond",
        "triangle",
        "cross",
        "none"
      ],
      "description": "The shape drawn at each point of a line / area / scatter series."
    },
    "ChartMarker": {
      "title": "ChartMarker",
      "type": "object",
      "properties": {
        "shape": {
          "$ref": "#/$defs/ChartMarkerShape"
        },
        "size": {
          "type": "number",
          "description": "Half-size in px. Default 3."
        },
        "color": {
          "type": "string",
          "description": "Overrides the series colour for this marker."
        }
      },
      "description": "A point marker: its shape, its size (radius-ish, in px) and its colour."
    },
    "PartialChartSpec": {
      "title": "PartialChartSpec",
      "description": "ChartSpec with every property optional.",
      "type": "object",
      "properties": {
        "type": {
          "$ref": "#/$defs/ChartType",
          "description": "Default type for series that don't set their own `type`."
        },
        "categories": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "X-axis labels (one per data point)."
        },
        "series": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ChartSeries"
          }
        },
        "width": {
          "type": "number"
        },
        "height": {
          "type": "number"
        },
        "palette": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Palette used when a series has no explicit `color`."
        },
        "categoryColors": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          },
          "description": "Per-category color overrides (by category label) - for pie / donut slice recolouring, where colour follows the category, not a series."
        },
        "valueFormat": {
          "$ref": "#/$defs/ChartValueFormat",
          "description": "Number format for the value axis, tooltips, data labels and reference lines. Unset = the default compact `1.2k` / `1.2M` style."
        },
        "locale": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          ],
          "description": "BCP-47 locale for value formatting. Setting this (or `currency`) switches formatting to `Intl.NumberFormat`, so thousands separators, the decimal mark and the compact suffixes follow the locale rather than the built-in English `1.2k` / `1.2M`. Unset = the locale-free default, which is why the default output has never changed under anyone's feet."
        },
        "currency": {
          "type": "string",
          "description": "ISO 4217 code for `valueFormat: 'currency'` (`'EUR'`, `'JPY'`, ...). Unset means the axis reads `$`, which is wrong everywhere outside the dollar zone and was the only currency this chart could draw for a long time."
        },
        "categoryGroups": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "label": {
                "type": "string"
              },
              "span": {
                "type": "number"
              }
            },
            "required": [
              "label",
              "span"
            ]
          },
          "description": "Grouped (nested) category axis: a parent tier spanning consecutive leaf categories (spans must sum to `categories.length`). Vertical category charts only (ignored for time / horizontal / pie)."
        },
        "stacked": {
          "type": "boolean",
          "description": "Stack bar / area series (per axis) instead of grouping them."
        },
        "stacked100": {
          "type": "boolean",
          "description": "Stack to 100% (each category normalized to its total). Implies stacked."
        },
        "orientation": {
          "type": "string",
          "enum": [
            "vertical",
            "horizontal"
          ],
          "description": "Bar orientation. `'horizontal'` swaps the axes - categories run down the left, bars grow rightward - which suits long category labels. Only applies when every series is a bar (combo / line / area fall back to vertical)."
        },
        "innerRadius": {
          "type": "number",
          "description": "Pie only: inner radius as a fraction of the outer radius (0..1) -> donut."
        },
        "referenceLines": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ChartReferenceLine"
          },
          "description": "Horizontal target / goal / average lines."
        },
        "xType": {
          "type": "string",
          "enum": [
            "category",
            "time",
            "ordinal-time",
            "number"
          ],
          "description": "How to read `categories` along the x axis. - `'category'` (default): evenly spaced labels, taken literally. - `'time'`: parsed as dates and positioned by ACTUAL elapsed time, so an irregular gap renders as a proportional gap. - `'ordinal-time'`: parsed as dates but spaced EVENLY, with date-derived ticks. This is what a series of trading sessions or business days needs: on a true time axis every weekend opens a hole a third as wide as the working week, which is noise rather than information."
        },
        "yAxisTitle": {
          "type": "string",
          "description": "Axis titles (reserve gutter space + render)."
        },
        "y2AxisTitle": {
          "type": "string"
        },
        "xAxisTitle": {
          "type": "string"
        },
        "yScale": {
          "type": "string",
          "enum": [
            "linear",
            "log"
          ],
          "description": "Y-axis scale. `'log'` plots base-10 logarithmic - values <= 0 are treated as missing. Necessary for wide-range data (money, audience size, scientific). Default `'linear'`."
        },
        "y2Scale": {
          "type": "string",
          "enum": [
            "linear",
            "log"
          ],
          "description": "Right (secondary) Y-axis scale. Default `'linear'`."
        },
        "xAxis": {
          "$ref": "#/$defs/ChartAxisConfig",
          "description": "The x axis in full: type, title, tick formatter, label rotation, grid lines, reversal. The flat `xType` / `xAxisTitle` fill in what is unset."
        },
        "yAxis": {
          "$ref": "#/$defs/ChartAxisConfig",
          "description": "The left value axis in full: min / max, tick count or interval, format, title, grid lines, reversal, scale. `yAxisTitle` / `yScale` fill in. A radar or radial column reads `min` / `max` as its rim."
        },
        "y2Axis": {
          "$ref": "#/$defs/ChartAxisConfig",
          "description": "The right value axis, for series with `axis: 'right'`."
        },
        "title": {
          "type": "string",
          "description": "Chart title, drawn centred above the plot. Reserves its own room."
        },
        "subtitle": {
          "type": "string",
          "description": "Smaller line under the title."
        },
        "caption": {
          "type": "string",
          "description": "Small text under the plot, left-aligned: a source, a note, a footnote."
        },
        "referenceBands": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ChartReferenceBand"
          },
          "description": "Shaded bands behind the marks, on a value axis or across the x axis."
        },
        "nullAs": {
          "type": "string",
          "enum": [
            "gap",
            "zero"
          ],
          "description": "What a `null` / `NaN` value means. `'gap'` (default) breaks the line and draws no mark; `'zero'` plots it as 0. Per-series `nullAs` beats this."
        },
        "decimate": {
          "$ref": "#/$defs/ChartDecimateConfig",
          "description": "Thin long line / area series before layout. Default `'auto'`: only when there are more points than pixels. See ."
        },
        "dataLabels": {
          "$ref": "#/$defs/ChartDataLabelConfig",
          "description": "Data labels as the spec's own default; the component's `dataLabels` prop, when set, replaces it. On a pie, `placement: 'outside'` draws callout labels with leader lines instead of percentages on the slices."
        },
        "annotations": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ChartAnnotation"
          },
          "description": "Pinned text labels at fixed data-space positions (callouts)."
        },
        "seriesLabels": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "object",
              "properties": {
                "formatter": {
                  "$comment": "a function; set it in code, it has no JSON form"
                }
              }
            }
          ],
          "description": "Name each line / area series at its last point, so a reader never has to match colours against a legend. `true` writes the series label; a formatter gets the label and the last value. Labels on a crowded right edge are pushed apart so they stay legible. Cartesian charts only."
        },
        "responsive": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ChartResponsiveRule"
          },
          "description": "Rules that patch the spec by the rendered size, checked top to bottom, every matching rule applied in turn (later wins). A rule's `spec` is a partial spec merged over this one; the axis objects merge one level deep, everything else replaces. A rule cannot change `width`, `height` or `responsive` itself. `{ maxWidth: 480, spec: { xAxis: { labelRotation: 90 }, seriesLabels: false } }` keeps a phone-width chart readable; the `legend` field, when set, moves or hides `SvChart`'s legend the same way."
        },
        "style": {
          "$ref": "#/$defs/ChartStyle",
          "description": "This chart's font, size, background and text / grid colours, over the theme tokens. See ."
        },
        "drawings": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ChartDrawing"
          },
          "description": "Reader drawings (trend lines, rays, Fibonacci levels, notes) in data space. `SvChart`'s drawing tools produce these; see ."
        },
        "lastPriceLine": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string"
                },
                "color": {
                  "type": "string"
                }
              }
            }
          ],
          "description": "A dashed line at the last close (or the last value of the first series) with its value in a pill on the axis, green when the last bar closed up and red when it closed down."
        },
        "patternFallback": {
          "type": "boolean",
          "description": "When true, automatically cycle through pattern fills for every series that doesn't set `pattern` explicitly. Useful as a one-flag colorblind fallback. Default false."
        },
        "calendarValues": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "date": {
                "type": "string"
              },
              "value": {
                "type": "number"
              }
            },
            "required": [
              "date",
              "value"
            ]
          },
          "description": "Calendar heatmap: array of date+value samples (one per day). Date strings are 'YYYY-MM-DD'. Missing days render as blank cells."
        },
        "calendarStart": {
          "type": "string",
          "description": "Calendar heatmap: year window. Default: span the data."
        },
        "calendarEnd": {
          "type": "string"
        },
        "gaugeValue": {
          "type": "number",
          "description": "Gauge: the value to display."
        },
        "gaugeMin": {
          "type": "number",
          "description": "Gauge: min/max of the dial scale. Defaults [0, 100]."
        },
        "gaugeMax": {
          "type": "number"
        },
        "gaugeTarget": {
          "type": "number",
          "description": "Gauge: target marker (the line/notch on the arc)."
        },
        "gaugeRanges": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "from": {
                "type": "number"
              },
              "to": {
                "type": "number"
              },
              "color": {
                "type": "string"
              }
            },
            "required": [
              "from",
              "to",
              "color"
            ]
          },
          "description": "Gauge: color bands along the arc (e.g. red/amber/green)."
        },
        "gaugeUnit": {
          "type": "string",
          "description": "Gauge: unit / suffix shown next to the value (e.g. '%', 'ms')."
        },
        "treemap": {
          "$ref": "#/$defs/TreeNode",
          "description": "Tree-map: hierarchical root. Leaves have `value`; parents are the sum of their children's totals."
        },
        "sankeyNodes": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "label": {
                "type": "string"
              },
              "color": {
                "type": "string"
              }
            },
            "required": [
              "id"
            ]
          },
          "description": "Sankey: nodes + flow links between them. Link `source` / `target` reference node ids."
        },
        "sankeyLinks": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "source": {
                "type": "string"
              },
              "target": {
                "type": "string"
              },
              "value": {
                "type": "number"
              },
              "color": {
                "type": "string"
              }
            },
            "required": [
              "source",
              "target",
              "value"
            ]
          }
        },
        "waterfallTotals": {
          "type": "array",
          "items": {
            "type": "boolean"
          },
          "description": "Waterfall: per-category flag marking bars as totals / subtotals, drawn from 0 to the running sum. A total with a value of 0 shows the sum the steps before it reached; a total with a value sets the running sum to that value, which is how a bridge opens on \"Revenue 4300\". Same length as `categories`."
        },
        "waterfallColors": {
          "type": "object",
          "properties": {
            "positive": {
              "type": "string"
            },
            "negative": {
              "type": "string"
            },
            "total": {
              "type": "string"
            }
          },
          "description": "Waterfall: explicit colors for positive/negative/total bars. The series color is ignored when this is set."
        },
        "candleColors": {
          "type": "object",
          "properties": {
            "up": {
              "type": "string"
            },
            "down": {
              "type": "string"
            }
          },
          "description": "Candlestick / OHLC colors. Direction beats series identity here, the same way `waterfallColors` overrides the series color. Defaults to the green / red pair from the palette's own vocabulary."
        },
        "candleStyle": {
          "type": "string",
          "enum": [
            "classic",
            "hollow",
            "heikin-ashi"
          ],
          "description": "How candles are drawn. `'classic'` (default) fills a down candle and leaves an up candle hollow. `'hollow'` colours by close vs the PREVIOUS close and hollows by close vs open, the trader's four-state candle. `'heikin-ashi'` replaces each candle with the smoothed average form before drawing, so trends read as runs of one colour."
        },
        "funnelShape": {
          "type": "string",
          "enum": [
            "trapezoid",
            "pyramid",
            "cone"
          ],
          "description": "Funnel shape: stacked trapezoids (default), an inverted `'pyramid'`, or a `'cone'` that tapers to a point."
        },
        "stackOffset": {
          "type": "string",
          "enum": [
            "zero",
            "wiggle",
            "silhouette"
          ],
          "description": "Baseline for stacked areas. `'zero'` (default) stacks up from the axis; `'wiggle'` centres the stack on a baseline that minimises the wiggle of the layers (a stream graph); `'silhouette'` centres it on zero."
        },
        "binEdges": {
          "type": "array",
          "items": {
            "type": "number"
          },
          "description": "Histogram: the bin edges, one more than there are categories. Set by `binValues`; the x axis labels the edges rather than the bin centres."
        },
        "tree": {
          "$ref": "#/$defs/TreeNode",
          "description": "Hierarchy for a sunburst. An alias of `treemap`: either field feeds either chart."
        },
        "bulletRanges": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "from": {
                "type": "number"
              },
              "to": {
                "type": "number"
              },
              "color": {
                "type": "string"
              }
            },
            "required": [
              "from",
              "to",
              "color"
            ]
          },
          "description": "Bullet chart: qualitative ranges behind the measure bars (poor / ok / good), shared by every category. Falls back to `gaugeRanges`."
        },
        "colorScale": {
          "anyOf": [
            {
              "const": "sequential"
            },
            {
              "const": "diverging"
            },
            {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          ],
          "description": "Heatmap color scale. `'sequential'` maps min->max through one hue, `'diverging'` runs cold->neutral->warm around 0. A custom array (>=2 hex colors) defines an arbitrary gradient. Default `'sequential'`."
        }
      }
    },
    "ChartDrawingKind": {
      "title": "ChartDrawingKind",
      "type": "string",
      "enum": [
        "trend",
        "hray",
        "fib",
        "text",
        "arrow",
        "rect"
      ],
      "description": "What a reader can draw on a chart: the kinds the drawing tools offer."
    }
  }
}
