{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"line-chart","type":"registry:ui","title":"Line chart","description":"One or more series over time, with hover readout and an optional band.","dependencies":["clsx","d3-shape","tailwind-merge"],"devDependencies":["@types/d3-shape@^3.2.0"],"registryDependencies":["https://caliperui.site/r/styles.json"],"files":[{"path":"ui/chart-parts.tsx","type":"registry:ui","target":"components/ui/chart-parts.tsx","content":"\"use client\";\nimport { useEffect, useRef, type ReactNode, type KeyboardEvent } from \"react\";\nimport { line, curveMonotoneX } from \"d3-shape\";\nimport \"./shared/chip-option.css\";\nimport \"./chart-parts.css\";\nexport type ChartSeries = { id: string; label: string; color?: string };\nexport type ChartDatum = {\n  id: string;\n  label: string;\n  values: Record<string, number | undefined>;\n  x?: number;\n  event?: string;\n};\nexport type ChartPart = {\n  id: string;\n  label: string;\n  value: number;\n  color?: string;\n};\nexport const chartColor = (index: number) => `var(--chart-${(index % 6) + 1})`;\nexport const numberText = (value: number) =>\n  new Intl.NumberFormat(\"en-US\", { maximumFractionDigits: 2 }).format(value);\nexport function useChartReveal() {\n  const root = useRef<HTMLElement>(null);\n  useEffect(() => {\n    const element = root.current;\n    if (!element) return;\n    const observer = new IntersectionObserver(\n      (entries) => {\n        if (entries[0].isIntersecting) {\n          element.dataset.visible = \"true\";\n          observer.disconnect();\n        }\n      },\n      { threshold: 0.3 },\n    );\n    observer.observe(element);\n    return () => observer.disconnect();\n  }, []);\n  return root;\n}\nexport function ChartTable({\n  label,\n  headers,\n  rows,\n}: {\n  label: string;\n  headers: string[];\n  rows: (string | number)[][];\n}) {\n  return (\n    <div className=\"sr-only\"><table>\n      <caption>{label}</caption>\n      <thead>\n        <tr>\n          {headers.map((header) => (\n            <th key={header} scope=\"col\">\n              {header}\n            </th>\n          ))}\n        </tr>\n      </thead>\n      <tbody>\n        {rows.map((row, index) => (\n          <tr key={index}>\n            {row.map((cell, column) =>\n              column === 0 ? (\n                <th key={column} scope=\"row\">\n                  {cell}\n                </th>\n              ) : (\n                <td key={column}>{cell}</td>\n              ),\n            )}\n          </tr>\n        ))}\n      </tbody>\n    </table></div>\n  );\n}\nexport function ChartMessage({\n  loading,\n  error,\n  empty,\n}: {\n  loading?: boolean;\n  error?: string;\n  empty?: boolean;\n}) {\n  return error || empty ? (\n    <p\n      role=\"status\"\n      className=\"grid min-h-48 place-items-center text-sm text-muted-foreground\"\n    >\n      {error ?? (loading ? \"Loading chart…\" : \"No data for this range.\")}\n    </p>\n  ) : loading ? (\n    <p role=\"status\" className=\"text-xs text-muted-foreground\">\n      Updating chart…\n    </p>\n  ) : null;\n}\nexport function ChartLegend({\n  series,\n  hidden,\n  onToggle,\n  active,\n  onActive,\n}: {\n  series: readonly ChartSeries[];\n  hidden: readonly string[];\n  onToggle: (id: string) => void;\n  active?: string | null;\n  onActive?: (id: string | null) => void;\n}) {\n  return (\n    <div\n      role=\"group\"\n      aria-label=\"Chart series\"\n      className=\"flex flex-wrap gap-2\"\n      onKeyDown={(event) => legendKeys(event)}\n    >\n      {series.map((item, index) => (\n        <button\n          key={item.id}\n          type=\"button\"\n          data-slot=\"chart-legend-item\"\n          aria-pressed={!hidden.includes(item.id)}\n          onClick={() => onToggle(item.id)}\n          onPointerEnter={() => onActive?.(item.id)}\n          onPointerLeave={() => onActive?.(null)}\n          onFocus={() => onActive?.(item.id)}\n          onBlur={() => onActive?.(null)}\n          data-active={active === item.id}\n        >\n          <span\n            aria-hidden=\"true\"\n            style={{ background: item.color ?? chartColor(index) }}\n            className=\"size-2 rounded-full\"\n          />\n          {item.label}\n        </button>\n      ))}\n    </div>\n  );\n}\nexport function legendKeys(event: KeyboardEvent<HTMLElement>) {\n  const nodes = [\n    ...event.currentTarget.querySelectorAll<HTMLButtonElement>(\"button\"),\n  ];\n  const index = nodes.indexOf(document.activeElement as HTMLButtonElement);\n  const target =\n    event.key === \"Home\"\n      ? 0\n      : event.key === \"End\"\n        ? nodes.length - 1\n        : [\"ArrowRight\", \"ArrowDown\"].includes(event.key)\n          ? (index + 1) % nodes.length\n          : [\"ArrowLeft\", \"ArrowUp\"].includes(event.key)\n            ? (index - 1 + nodes.length) % nodes.length\n            : -1;\n  if (target >= 0) {\n    event.preventDefault();\n    nodes[target]?.focus();\n  }\n}\nexport function pointKey(\n  event: KeyboardEvent<HTMLElement>,\n  active: number | null,\n  length: number,\n) {\n  const index = active ?? length - 1;\n  const step = Math.max(1, Math.floor(length / 6));\n  const next =\n    event.key === \"Home\"\n      ? 0\n      : event.key === \"End\"\n        ? length - 1\n        : event.key === \"PageUp\"\n          ? index + step\n          : event.key === \"PageDown\"\n            ? index - step\n            : [\"ArrowRight\", \"ArrowUp\"].includes(event.key)\n              ? index + 1\n              : [\"ArrowLeft\", \"ArrowDown\"].includes(event.key)\n                ? index - 1\n                : event.key === \"Escape\"\n                  ? null\n                  : undefined;\n  if (next !== undefined) event.preventDefault();\n  return next == null ? next : Math.max(0, Math.min(length - 1, next));\n}\nexport function pathFor(\n  data: readonly ChartDatum[],\n  id: string,\n  minimum: number,\n  maximum: number,\n  hidden = false,\n) {\n  if (!data.length) return \"\";\n  const first = data[0].x ?? 0,\n    last = data[data.length - 1].x ?? data.length - 1;\n  const points = data.map((item, index) => ({\n    x: (600 * ((item.x ?? index) - first)) / (last - first || 1),\n    y: item.values[id],\n  }));\n  // Monotone cubic tangents (Fritsch-Carlson): the line is smooth through every point and never overshoots it.\n  const ok = (value: number | undefined): value is number => value !== undefined && Number.isFinite(value);\n  const slope = (k: number) => {\n    const p = points[k], q = points[k + 1];\n    return p && q && ok(p.y) && ok(q.y) ? (q.y - p.y) / (q.x - p.x || 1) : Number.NaN;\n  };\n  const tangents = points.map((_, k) => {\n    const before = slope(k - 1), after = slope(k);\n    if (!Number.isFinite(before)) return Number.isFinite(after) ? after : 0;\n    if (!Number.isFinite(after)) return before;\n    return before * after <= 0 ? 0 : (2 * before * after) / (before + after);\n  });\n  const samples = Array.from({ length: 97 }, (_, index) => {\n    const x = (index / 96) * 600;\n    let right = points.findIndex((point) => point.x >= x);\n    if (right < 0) right = points.length - 1;\n    const left = Math.max(0, right - 1);\n    const a = points[left],\n      b = points[right];\n    const valid = ok(a.y) && ok(b.y);\n    const h = b.x - a.x || 1;\n    const t = Math.min(1, Math.max(0, (x - a.x) / h));\n    const value = valid\n      ? (2 * t ** 3 - 3 * t ** 2 + 1) * a.y! + (t ** 3 - 2 * t ** 2 + t) * h * tangents[left]! + (-2 * t ** 3 + 3 * t ** 2) * b.y! + (t ** 3 - t ** 2) * h * tangents[right]!\n      : Number.NaN;\n    return [\n      x,\n      hidden ? 210 : 210 - ((value - minimum) / (maximum - minimum || 1)) * 200,\n    ] as [number, number];\n  });\n  return (\n    line<[number, number]>()\n      .defined((point) => Number.isFinite(point[1]))\n      .x((point) => point[0])\n      .y((point) => point[1])\n      .curve(curveMonotoneX)(samples) ?? \"\"\n  );\n}\nexport function ChartReadout({ children }: { children: ReactNode }) {\n  return (\n    <div\n      data-slot=\"chart-readout\"\n      aria-hidden=\"true\"\n      // A ruled line under the plot, not a box: the chart already sits on a surface.\n      className=\"mt-4 min-h-11 border-t border-border pt-3 text-xs leading-5 tabular-nums\"\n    >\n      {children}\n    </div>\n  );\n}\n"},{"path":"ui/lib/cn.ts","type":"registry:ui","target":"components/ui/lib/cn.ts","content":"import { clsx, type ClassValue } from \"clsx\";\nimport { extendTailwindMerge } from \"tailwind-merge\";\n\nconst twMerge = extendTailwindMerge({\n  extend: { theme: { radius: [\"control\", \"surface\", \"overlay\"], shadow: [\"overlay\"] } },\n});\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n"},{"path":"ui/line-chart.tsx","type":"registry:ui","target":"components/ui/line-chart.tsx","content":"\"use client\";\nimport { area, curveMonotoneX } from \"d3-shape\";\nimport { useId, useState, type CSSProperties } from \"react\";\nimport { cn } from \"./lib/cn\";\nimport {\n  ChartTable,\n  ChartMessage,\n  ChartLegend,\n  ChartReadout,\n  chartColor,\n  numberText,\n  pathFor,\n  pointKey,\n  useChartReveal,\n  type ChartDatum,\n  type ChartSeries,\n} from \"./chart-parts\";\nimport \"./shared/chart-crosshair.css\";\nimport \"./line-chart.css\";\nexport type LineChartProps = {\n  label: string;\n  data: readonly ChartDatum[];\n  series: readonly ChartSeries[];\n  unit?: string;\n  loading?: boolean;\n  error?: string;\n  className?: string;\n  band?: { lower: string; upper: string };\n};\nexport function LineChart({\n  label,\n  data,\n  series,\n  unit = \"\",\n  loading,\n  error,\n  className,\n  band,\n}: LineChartProps) {\n  const root = useChartReveal();\n  const washId = useId();\n  const [hidden, setHidden] = useState<string[]>([]),\n    [reading, setReading] = useState<number | null>(null);\n  const visible = series.filter((item) => !hidden.includes(item.id));\n  const values = data.flatMap((item) =>\n    visible\n      .map((s) => item.values[s.id])\n      .filter(\n        (value): value is number =>\n          value !== undefined && Number.isFinite(value),\n      ),\n  );\n  const extremes = band\n    ? data.flatMap((item) => [\n        item.values[band.lower] ?? 0,\n        item.values[band.upper] ?? 0,\n      ])\n    : values;\n  const peak = Math.max(1, ...extremes);\n  // Round the top up to a whole step of its magnitude, so the axis reads 0 · 50 · 100, not 0 · 41 · 82.\n  const step = 10 ** Math.floor(Math.log10(peak));\n  const min = Math.min(0, ...extremes),\n    max = Math.ceil(peak / step) * step;\n  // Up to five labels along the bottom, spread evenly from first to last.\n  const tickCount = Math.min(data.length, 5);\n  const xTicks = [...new Set(Array.from({ length: tickCount }, (_, k) => Math.round((k * (data.length - 1)) / Math.max(1, tickCount - 1))))];\n  const index = reading === null ? null : Math.min(reading, data.length - 1);\n  const datum = index === null ? null : data[index];\n  function scrub(clientX: number, rect: DOMRect) {\n    if (!data.length) return;\n    const ratio = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));\n    const first = data[0].x ?? 0,\n      last = data.at(-1)?.x ?? data.length - 1;\n    let nearest = 0;\n    data.forEach((item, i) => {\n      if (\n        Math.abs((item.x ?? i) - (first + ratio * (last - first))) <\n        Math.abs(\n          (data[nearest].x ?? nearest) - (first + ratio * (last - first)),\n        )\n      )\n        nearest = i;\n    });\n    setReading(nearest);\n  }\n  const text = datum\n    ? `${datum.label}. ${visible.map((item) => `${item.label}: ${datum.values[item.id] === undefined ? \"No reading\" : numberText(datum.values[item.id]!)} ${unit}`).join(\". \")}${datum.event ? `. ${datum.event}` : \"\"}`\n    : label;\n  return (\n    <figure\n      ref={root}\n      aria-label={label}\n      data-slot=\"line-chart\"\n      aria-busy={loading}\n      className={cn(\"min-w-0\", className)}\n    >\n      <figcaption className=\"mb-3 text-xs font-medium text-muted-foreground\">{label}</figcaption>\n      <ChartLegend\n        series={series}\n        hidden={hidden}\n        onToggle={(id) =>\n          setHidden(\n            hidden.includes(id)\n              ? hidden.filter((key) => key !== id)\n              : [...hidden, id],\n          )\n        }\n      />\n      <ChartMessage loading={loading} error={error} empty={!data.length} />\n      {!error && !!data.length && (\n        <>\n          <div className=\"relative mt-4 pl-9\" data-slot=\"chart-frame\">\n            <div\n              className=\"absolute inset-y-0 left-0 flex flex-col justify-between pb-7 text-[11px] tabular-nums text-muted-foreground\"\n              aria-hidden=\"true\"\n            >\n              <span>{numberText(max)}</span>\n              <span>{numberText((max + min) / 2)}</span>\n              <span>{numberText(min)}</span>\n            </div>\n            <div\n              role=\"slider\"\n              tabIndex={0}\n              aria-label={`${label}, explore points`}\n              aria-valuemin={0}\n              aria-valuemax={data.length - 1}\n              aria-valuenow={index ?? data.length - 1}\n              aria-valuetext={text}\n              data-slot=\"chart-plot\"\n              className=\"relative touch-pan-y outline-none focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\"\n              onFocus={() => setReading(data.length - 1)}\n              onBlur={() => setReading(null)}\n              onKeyDown={(event) => {\n                const next = pointKey(event, index, data.length);\n                if (next !== undefined) setReading(next);\n              }}\n              onPointerMove={(event) => {\n                if (\n                  event.pointerType === \"mouse\" ||\n                  event.currentTarget.hasPointerCapture(event.pointerId)\n                )\n                  scrub(\n                    event.clientX,\n                    event.currentTarget.getBoundingClientRect(),\n                  );\n              }}\n              onPointerDown={(event) => {\n                event.currentTarget.setPointerCapture(event.pointerId);\n                scrub(\n                  event.clientX,\n                  event.currentTarget.getBoundingClientRect(),\n                );\n              }}\n              onPointerLeave={(event) => {\n                if (\n                  !event.currentTarget.hasPointerCapture(event.pointerId) &&\n                  document.activeElement !== event.currentTarget\n                )\n                  setReading(null);\n              }}\n              onPointerUp={(event) => {\n                event.currentTarget.releasePointerCapture(event.pointerId);\n                if (event.pointerType !== \"mouse\") setReading(null);\n              }}\n            >\n              <svg\n                aria-hidden=\"true\"\n                viewBox=\"0 0 600 220\"\n                className=\"block h-52 w-full overflow-visible\"\n                preserveAspectRatio=\"none\"\n              >\n                {[10, 110, 210].map((y) => (\n                  <line\n                    key={y}\n                    x1=\"0\"\n                    x2=\"600\"\n                    y1={y}\n                    y2={y}\n                    stroke=\"var(--border)\"\n                    strokeDasharray=\"2 4\"\n                  />\n                ))}\n                {band && (\n                  <path\n                    fill=\"var(--chart-1)\"\n                    opacity=\".12\"\n                    d={\n                      area<ChartDatum>()\n                        .x(\n                          (item, i) =>\n                            (600 * ((item.x ?? i) - (data[0].x ?? 0))) /\n                            ((data.at(-1)?.x ?? data.length - 1) -\n                              (data[0].x ?? 0) || 1),\n                        )\n                        .y0(\n                          (item) =>\n                            210 -\n                            (((item.values[band.lower] ?? 0) - min) /\n                              (max - min)) *\n                              200,\n                        )\n                        .y1(\n                          (item) =>\n                            210 -\n                            (((item.values[band.upper] ?? 0) - min) /\n                              (max - min)) *\n                              200,\n                        )\n                        .curve(curveMonotoneX)([...data]) ?? \"\"\n                    }\n                  />\n                )}\n                {series[0] && !hidden.includes(series[0].id) && (() => {\n                  // A wash under the lead series, strongest at the line and gone at the baseline, gives the chart weight without a second colour.\n                  const line = pathFor(data, series[0].id, min, max);\n                  const d = line ? `${line}L600,210L0,210Z` : \"\";\n                  const color = series[0].color ?? chartColor(0);\n                  return (\n                    <>\n                      <defs>\n                        <linearGradient id={washId} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n                          <stop offset=\"0\" stopColor={color} stopOpacity=\"0.3\" />\n                          <stop offset=\"1\" stopColor={color} stopOpacity=\"0\" />\n                        </linearGradient>\n                      </defs>\n                      <path data-slot=\"chart-wash\" d={d} style={{ d: `path(\"${d}\")` } as CSSProperties} fill={`url(#${washId})`} stroke=\"none\" aria-hidden=\"true\" />\n                    </>\n                  );\n                })()}\n                {series.map((item, i) => {\n                  const d = pathFor(\n                    data,\n                    item.id,\n                    min,\n                    max,\n                    hidden.includes(item.id),\n                  );\n                  return (\n                    <path\n                      key={item.id}\n                      data-slot=\"chart-line\"\n                      d={d}\n                      style={\n                        {\n                          d: `path(\"${d}\")`,\n                          opacity: hidden.includes(item.id) ? 0.12 : 1,\n                        } as CSSProperties\n                      }\n                      fill=\"none\"\n                      stroke={item.color ?? chartColor(i)}\n                      strokeWidth=\"2\"\n                      vectorEffect=\"non-scaling-stroke\"\n                      pathLength=\"1\"\n                    />\n                  );\n                })}\n                {datum && (\n                  <g\n                    data-slot=\"chart-crosshair\"\n                    style={{\n                      transform: `translateX(${(600 * ((datum.x ?? index!) - (data[0].x ?? 0))) / ((data.at(-1)?.x ?? data.length - 1) - (data[0].x ?? 0) || 1)}px)`,\n                    }}\n                  >\n                    <line\n                      y1=\"0\"\n                      y2=\"220\"\n                      stroke=\"var(--muted-foreground)\"\n                      strokeDasharray=\"3 3\"\n                    />\n                    {visible.map(\n                      (item, i) =>\n                        datum.values[item.id] !== undefined && (\n                          <circle\n                            key={item.id}\n                            cy={\n                              210 -\n                              ((datum.values[item.id]! - min) / (max - min)) *\n                                200\n                            }\n                            r=\"4\"\n                            fill={item.color ?? chartColor(i)}\n                            stroke=\"var(--elevated)\"\n                            strokeWidth=\"2\"\n                          />\n                        ),\n                    )}\n                  </g>\n                )}\n                {data.map(\n                  (item, i) =>\n                    item.event && (\n                      <circle\n                        key={item.id}\n                        cx={(600 * i) / (data.length - 1 || 1)}\n                        cy=\"220\"\n                        r=\"3\"\n                        fill=\"var(--chart-2)\"\n                      />\n                    ),\n                )}\n              </svg>\n            </div>\n            <div\n              aria-hidden=\"true\"\n              className=\"relative mt-2 h-4 text-[11px] text-muted-foreground\"\n            >\n              {xTicks.map((i, k) => (\n                <span\n                  key={i}\n                  className=\"absolute top-0 whitespace-nowrap\"\n                  style={{\n                    left: `${(100 * ((data[i].x ?? i) - (data[0].x ?? 0))) / ((data.at(-1)?.x ?? data.length - 1) - (data[0].x ?? 0) || 1)}%`,\n                    translate: k === 0 ? \"0\" : k === xTicks.length - 1 ? \"-100%\" : \"-50%\",\n                  }}\n                >\n                  {data[i].label}\n                </span>\n              ))}\n            </div>\n          </div>\n          <ChartReadout>\n            {datum ? (\n              <>\n                <span className=\"font-medium\">{datum.label}</span>\n                <div className=\"flex flex-wrap gap-x-4\">\n                  {visible.map((item) => (\n                    <span key={item.id}>\n                      {item.label}{\" \"}\n                      <strong className=\"font-medium tabular-nums\">\n                        {datum.values[item.id] === undefined\n                          ? \"—\"\n                          : numberText(datum.values[item.id]!)}{\" \"}\n                        {unit}\n                      </strong>\n                    </span>\n                  ))}\n                  {datum.event && <span>{datum.event}</span>}\n                </div>\n              </>\n            ) : (\n              <span className=\"text-muted-foreground\">\n                Hover, drag, or use arrow keys to read a point.\n                {!visible.length ? \" All series hidden.\" : \"\"}\n              </span>\n            )}\n          </ChartReadout>\n        </>\n      )}\n      <ChartTable\n        label={label}\n        headers={[\"Point\", ...series.map((item) => item.label), \"Event\"]}\n        rows={data.map((item) => [\n          item.label,\n          ...series.map((s) => item.values[s.id] ?? \"—\"),\n          item.event ?? \"\",\n        ])}\n      />\n    </figure>\n  );\n}\n"},{"path":"ui/shared/chip-option.css","type":"registry:file","target":"components/ui/shared/chip-option.css","content":"/* Shared by calendar.tsx, chart-parts.tsx, chip-group.tsx, data-table.tsx, number-field.tsx, password-field.tsx, stepper.tsx; each imports it. Edit freely. */\n@layer theme, base, components, utilities;\n\n@layer components {\n  /* Depth sets 2 and 3 end */\n\n  /* Press feedback (2026-10-20, motion audit): every pressable control answers a press. Compact controls take the\n     system press scale, as Button does; wide rows deepen their fill instead, since scaling a full-width row reads as a\n     jump. Disabled controls stay still. */\n  :is([data-slot=\"chip-option\"], [data-slot=\"number-field\"] button, [data-slot=\"password-toggle\"], [data-slot=\"calendar\"] button, [data-slot=\"chart-legend-item\"], [data-slot=\"sort-button\"], [data-slot=\"stepper\"] button):active:not(:disabled):not([aria-disabled=\"true\"]) {\n    scale: var(--motion-press-scale);\n  }\n}\n"},{"path":"ui/chart-parts.css","type":"registry:file","target":"components/ui/chart-parts.css","content":"/* chart-parts.tsx's styles; it imports this file. Edit freely. */\n@layer theme, base, components, utilities;\n\n@layer components {\n  [data-slot=\"chart-legend-item\"] {\n    display: inline-flex;\n    align-items: center;\n    gap: 0.5rem;\n    min-height: var(--control-height);\n    padding: 0.25rem 0.5rem;\n    border-radius: var(--radius-control);\n    font-size: 0.75rem;\n    color: var(--muted-foreground);\n    outline: none;\n    transition:\n      background-color var(--duration-fast) var(--ease-out),\n      color var(--duration-fast) var(--ease-out);\n  }\n  [data-slot=\"chart-legend-item\"]:hover,\n  [data-slot=\"chart-legend-item\"][data-active=\"true\"] {\n    background: var(--muted);\n    color: var(--foreground);\n  }\n  [data-slot=\"chart-legend-item\"]:focus-visible {\n    outline: 2px solid var(--ring);\n    outline-offset: 2px;\n  }\n  [data-slot=\"chart-legend-item\"][aria-pressed=\"false\"] {\n    opacity: 0.45;\n    text-decoration: line-through;\n  }\n}\n"},{"path":"ui/shared/chart-crosshair.css","type":"registry:file","target":"components/ui/shared/chart-crosshair.css","content":"/* Shared by line-chart.tsx, market-terminal.tsx, price-target-fan.tsx, revenue-explorer.tsx, ridgeline.tsx, streamgraph.tsx; each imports it. Edit freely. */\n@layer theme, base, components, utilities;\n\n@layer components {\n  [data-slot=\"chart-crosshair\"] {\n    transition: transform var(--duration-fast) var(--ease-out);\n  }\n  @media (prefers-reduced-motion: reduce) {\n    [data-slot=\"chart-crosshair\"] {\n      transition: none;\n      animation: none !important;\n    }\n  }\n}\n"},{"path":"ui/line-chart.css","type":"registry:file","target":"components/ui/line-chart.css","content":"/* line-chart.tsx's styles; it imports this file. Edit freely. */\n@layer theme, base, components, utilities;\n\n@layer components {\n  [data-slot=\"chart-line\"],\n  [data-slot=\"chart-wash\"] {\n    transition:\n      d var(--duration-travel) var(--ease-showcase),\n      opacity var(--duration-normal) var(--ease-out);\n  }\n  /* The wash carries its own fade (a gradient in line-chart.tsx), so the path itself is fully opaque. */\n  [data-slot=\"line-chart\"][data-visible=\"true\"] [data-slot=\"chart-wash\"] {\n    animation: caliper-chart-fade var(--duration-showcase) var(--ease-out);\n  }\n  [data-slot=\"line-chart\"][data-visible=\"true\"] [data-slot=\"chart-line\"] {\n    animation: caliper-chart-draw var(--duration-showcase) var(--ease-showcase);\n  }\n  @media (prefers-reduced-motion: reduce) {\n    [data-slot=\"chart-line\"] {\n      transition: none;\n      animation: none !important;\n    }\n  }\n  [data-slot=\"line-chart\"][aria-busy=\"true\"] [data-slot=\"chart-plot\"] {\n    opacity: 0.5;\n  }\n}\n"}],"categories":["charts"],"meta":{"tier":"free","license":"MIT","family":"core-charts","docs":"/docs/components/line-chart"}}