'use strict';

var react = require('react');
require('d3-scale');
var jsxRuntime = require('react/jsx-runtime');
var rough = require('roughjs');

function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }

var rough__default = /*#__PURE__*/_interopDefault(rough);

// src/interactive/readMark.ts
function readMark(el) {
  const host = el.closest("[data-gc-mark]");
  if (!host) return null;
  const get = (n) => host.getAttribute(n);
  const raw = get("data-gc-value");
  const value = raw == null ? 0 : raw.startsWith("{") ? JSON.parse(raw) : Number(raw);
  const series = get("data-gc-series");
  const label = get("data-gc-label");
  return {
    kind: get("data-gc-mark"),
    index: Number(get("data-gc-index")),
    value,
    cx: Number(get("data-gc-cx")),
    cy: Number(get("data-gc-cy")),
    ...series != null ? { series } : {},
    ...label != null ? { label } : {}
  };
}

// src/core/zoom.ts
function zoomDomain([lo, hi], focus, factor) {
  const anchor = lo + (hi - lo) * focus;
  return [anchor - (anchor - lo) * factor, anchor + (hi - anchor) * factor];
}
function panDomain([lo, hi], deltaData) {
  return [lo + deltaData, hi + deltaData];
}
function clampDomain([lo, hi], [bLo, bHi], minSpan) {
  const span = Math.max(minSpan, Math.min(hi - lo, bHi - bLo));
  const nLo = Math.max(bLo, Math.min(lo, bHi - span));
  return [nLo, nLo + span];
}
function wheelFactor(deltaY) {
  return deltaY < 0 ? 0.8 : 1.25;
}
function wheelZoom(domain, focus, deltaY, bounds, minSpan) {
  return clampDomain(zoomDomain(domain, focus, wheelFactor(deltaY)), bounds, minSpan);
}
function focusFraction(clientPos, edgeStart, length) {
  if (length <= 0) return 0;
  return Math.max(0, Math.min(1, (clientPos - edgeStart) / length));
}
function extentOf(values, includeZero = true) {
  const finite = values.filter((v) => Number.isFinite(v));
  if (finite.length === 0) return [0, 1];
  let min = Math.min(...finite);
  let max = Math.max(...finite);
  if (includeZero) {
    min = Math.min(min, 0);
    max = Math.max(max, 0);
  }
  if (min === max) max = min + 1;
  return [min, max];
}

// src/interactive/useZoomPan.ts
function chartXExtent(props) {
  const series = props.series;
  if (Array.isArray(series)) {
    const xs = series.flatMap((s) => (s.points ?? []).map((p) => p.x));
    return xs.length ? extentOf(xs, false) : null;
  }
  const data = props.data;
  if (Array.isArray(data) && data.length > 0 && data.every((d) => typeof d?.x === "number")) {
    return extentOf(data.map((d) => d.x), false);
  }
  return null;
}
function useZoomPan(bounds, opts = {}) {
  const minSpan = opts.minSpan ?? (bounds ? (bounds[1] - bounds[0]) / 1e3 || 1 : 1);
  const [domain, setDomain] = react.useState(null);
  const drag = react.useRef(null);
  const onWheel = react.useCallback(
    (e) => {
      if (!bounds) return;
      e.preventDefault();
      const rect = e.currentTarget.getBoundingClientRect();
      const focus = focusFraction(e.clientX, rect.left, rect.width);
      setDomain((d) => wheelZoom(d ?? bounds, focus, e.deltaY, bounds, minSpan));
    },
    [bounds, minSpan]
  );
  const onPointerDown = react.useCallback(
    (e) => {
      if (!bounds || !opts.pan) return;
      drag.current = { x: e.clientX, domain: domain ?? bounds };
    },
    [bounds, opts.pan, domain]
  );
  const onPointerMove = react.useCallback(
    (e) => {
      const d = drag.current;
      if (!d || !bounds) return;
      const rect = e.currentTarget.getBoundingClientRect();
      const span = d.domain[1] - d.domain[0];
      const deltaData = -((e.clientX - d.x) / rect.width) * span;
      setDomain(clampDomain(panDomain(d.domain, deltaData), bounds, minSpan));
    },
    [bounds, minSpan]
  );
  const onPointerUp = react.useCallback(() => {
    drag.current = null;
  }, []);
  const reset = react.useCallback(() => setDomain(null), []);
  return { domain, onWheel, onPointerDown, onPointerMove, onPointerUp, reset };
}

// src/vibe/presets.ts
var VIBE_PRESETS = {
  messy_sketch: {
    preset: "messy_sketch",
    roughness: 1.6,
    bowing: 1.5,
    strokeWidth: 1.5,
    stroke: "#1f2937",
    fill: "#1f2937",
    fillStyle: "hachure",
    fillWeight: 1,
    hachureAngle: -41,
    hachureGap: 4,
    curveStepCount: 9,
    curveTightness: 0,
    disableMultiStroke: false,
    seed: 1,
    fontFamily: '"Comic Neue", "Comic Sans MS", "Segoe Print", cursive',
    fontSize: 14
  },
  clean_blueprint: {
    preset: "clean_blueprint",
    roughness: 0.6,
    bowing: 0.4,
    strokeWidth: 1.25,
    stroke: "#1d4ed8",
    fill: "#3b82f6",
    fillStyle: "cross-hatch",
    fillWeight: 0.5,
    hachureAngle: 45,
    hachureGap: 6,
    curveStepCount: 14,
    curveTightness: 0,
    disableMultiStroke: true,
    seed: 7,
    fontFamily: '"Cousine", "Courier New", ui-monospace, monospace',
    fontSize: 13
  },
  chaotic_notebook: {
    preset: "chaotic_notebook",
    roughness: 2.8,
    bowing: 2.5,
    strokeWidth: 2,
    stroke: "#111827",
    fill: "#f59e0b",
    fillStyle: "zigzag",
    fillWeight: 1.5,
    hachureAngle: 17,
    hachureGap: 3,
    curveStepCount: 6,
    curveTightness: 0.2,
    disableMultiStroke: false,
    seed: 13,
    fontFamily: '"Gloria Hallelujah", "Comic Sans MS", "Segoe Print", cursive',
    fontSize: 15
  },
  pencil: {
    preset: "pencil",
    roughness: 1.2,
    bowing: 1,
    strokeWidth: 0.9,
    stroke: "#4b5563",
    fill: "#9ca3af",
    fillStyle: "hachure",
    fillWeight: 0.6,
    hachureAngle: -35,
    hachureGap: 3,
    curveStepCount: 11,
    curveTightness: 0,
    disableMultiStroke: false,
    seed: 3,
    fontFamily: '"Caveat", "Segoe Print", "Bradley Hand", cursive',
    fontSize: 13
  },
  marker: {
    preset: "marker",
    roughness: 1,
    bowing: 0.8,
    strokeWidth: 3.5,
    stroke: "#0f172a",
    fill: "#38bdf8",
    fillStyle: "solid",
    fillWeight: 2,
    hachureAngle: 0,
    hachureGap: 5,
    curveStepCount: 10,
    curveTightness: 0,
    disableMultiStroke: true,
    seed: 5,
    fontFamily: '"Permanent Marker", "Marker Felt", cursive',
    fontSize: 15
  },
  ink: {
    preset: "ink",
    roughness: 0.8,
    bowing: 0.6,
    strokeWidth: 1.6,
    stroke: "#0b1020",
    fill: "#1e293b",
    fillStyle: "cross-hatch",
    fillWeight: 0.7,
    hachureAngle: 41,
    hachureGap: 5,
    curveStepCount: 13,
    curveTightness: 0,
    disableMultiStroke: false,
    seed: 9,
    fontFamily: '"Spectral", "Iowan Old Style", Georgia, serif',
    fontSize: 14
  },
  crayon: {
    preset: "crayon",
    roughness: 2.2,
    bowing: 1.8,
    strokeWidth: 2.8,
    stroke: "#7c2d12",
    fill: "#fb923c",
    fillStyle: "zigzag",
    fillWeight: 2.5,
    hachureAngle: 25,
    hachureGap: 4,
    curveStepCount: 7,
    curveTightness: 0.15,
    disableMultiStroke: false,
    seed: 21,
    fontFamily: '"Schoolbell", "Comic Sans MS", "Chalkboard", cursive',
    fontSize: 15
  },
  // Leonardo's notebooks: fine, confident iron-gall (sepia) pen on a warm
  // sienna cross-hatch. The negative hachure angle mimics his left-handed
  // shading, which slants top-left → bottom-right. Seed = his birth year.
  davinci_journal: {
    preset: "davinci_journal",
    roughness: 1,
    bowing: 0.7,
    strokeWidth: 1.1,
    stroke: "#4a3520",
    fill: "#b08a55",
    fillStyle: "cross-hatch",
    fillWeight: 0.45,
    hachureAngle: -52,
    hachureGap: 7,
    curveStepCount: 14,
    curveTightness: 0,
    // Single-stroke so connectors and outlines read as one confident pen line
    // rather than a doubled, scratchy one. (Fill hatching is unaffected.)
    disableMultiStroke: true,
    seed: 1452,
    fontFamily: '"EB Garamond", "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif',
    fontSize: 14,
    // Aged-parchment page so the ink has somewhere to sit; this also drives the
    // node fill and the gap behind zone labels, so no stark-white seams show.
    background: "#f2e8cf"
  },
  // Classic drafting blueprint: crisp white/cyan ink on a deep blue page — the
  // inverse of clean_blueprint.
  blueprint_dark: {
    preset: "blueprint_dark",
    roughness: 0.6,
    bowing: 0.4,
    strokeWidth: 1.25,
    stroke: "#dbeafe",
    fill: "#93c5fd",
    fillStyle: "cross-hatch",
    fillWeight: 0.5,
    hachureAngle: 45,
    hachureGap: 6,
    curveStepCount: 14,
    curveTightness: 0,
    disableMultiStroke: true,
    seed: 23,
    fontFamily: '"Cousine", "Courier New", ui-monospace, monospace',
    fontSize: 13,
    background: "#0e3a66"
  },
  // Chalk on a slate board: dusty white strokes and hachure, casual hand.
  chalkboard: {
    preset: "chalkboard",
    roughness: 1.4,
    bowing: 1.1,
    strokeWidth: 1.4,
    stroke: "#eef0e9",
    fill: "#cdd3c8",
    fillStyle: "hachure",
    fillWeight: 0.7,
    hachureAngle: -30,
    hachureGap: 4,
    curveStepCount: 9,
    curveTightness: 0,
    disableMultiStroke: false,
    seed: 28,
    fontFamily: '"Schoolbell", "Comic Sans MS", cursive',
    fontSize: 15,
    background: "#2e3b35"
  },
  // Neon signage: bright cyan tubes and a fuchsia cross-hatch on near-black.
  neon: {
    preset: "neon",
    roughness: 0.5,
    bowing: 0.3,
    strokeWidth: 1.7,
    stroke: "#22d3ee",
    fill: "#d946ef",
    fillStyle: "cross-hatch",
    fillWeight: 0.6,
    hachureAngle: 45,
    hachureGap: 7,
    curveStepCount: 16,
    curveTightness: 0,
    disableMultiStroke: true,
    seed: 31,
    fontFamily: '"Orbitron", ui-monospace, monospace',
    fontSize: 13,
    background: "#0a0a12"
  },
  // Comic book: thick black ink outlines and bold solid fills, punchy lettering.
  comic_book: {
    preset: "comic_book",
    roughness: 0.9,
    bowing: 0.6,
    strokeWidth: 2.8,
    stroke: "#111111",
    fill: "#ef4444",
    fillStyle: "solid",
    fillWeight: 2,
    hachureAngle: 0,
    hachureGap: 5,
    curveStepCount: 12,
    curveTightness: 0,
    disableMultiStroke: true,
    seed: 37,
    fontFamily: '"Bangers", "Comic Sans MS", cursive',
    fontSize: 16,
    background: "#fffdf6"
  },
  // Phosphor-green CRT terminal: crisp green glyphs and sparse hatch on black.
  terminal: {
    preset: "terminal",
    roughness: 0.6,
    bowing: 0.3,
    strokeWidth: 1.3,
    stroke: "#4ade80",
    fill: "#22c55e",
    fillStyle: "cross-hatch",
    fillWeight: 0.5,
    hachureAngle: 0,
    hachureGap: 7,
    curveStepCount: 14,
    curveTightness: 0,
    disableMultiStroke: true,
    seed: 41,
    fontFamily: '"VT323", ui-monospace, monospace',
    fontSize: 17,
    background: "#08120b"
  },
  // Watercolour: soft muted ink, pale solid washes, gently wavy edges on paper.
  watercolor: {
    preset: "watercolor",
    roughness: 0.9,
    bowing: 1.2,
    strokeWidth: 1.1,
    stroke: "#5b6b7a",
    fill: "#bcd3e6",
    fillStyle: "solid",
    fillWeight: 1,
    hachureAngle: 60,
    hachureGap: 5,
    curveStepCount: 16,
    curveTightness: 0,
    disableMultiStroke: false,
    seed: 47,
    fontFamily: '"Quicksand", system-ui, sans-serif',
    fontSize: 14,
    background: "#fbfaf5"
  },
  // Newsprint: high-contrast black ink and dense cross-hatch on cream stock.
  newsprint: {
    preset: "newsprint",
    roughness: 0.8,
    bowing: 0.5,
    strokeWidth: 1.3,
    stroke: "#1a1a1a",
    fill: "#3f3f46",
    fillStyle: "cross-hatch",
    fillWeight: 0.6,
    hachureAngle: 45,
    hachureGap: 4,
    curveStepCount: 13,
    curveTightness: 0,
    disableMultiStroke: false,
    seed: 53,
    fontFamily: '"Playfair Display", Georgia, serif',
    fontSize: 14,
    background: "#f1ece0"
  },
  // Dry-erase whiteboard: bold blue marker and a light wash on a clean board.
  whiteboard: {
    preset: "whiteboard",
    roughness: 1,
    bowing: 0.7,
    strokeWidth: 2.2,
    stroke: "#2563eb",
    fill: "#bfdbfe",
    fillStyle: "solid",
    fillWeight: 2,
    hachureAngle: 0,
    hachureGap: 5,
    curveStepCount: 11,
    curveTightness: 0,
    disableMultiStroke: true,
    seed: 59,
    fontFamily: '"Comic Neue", "Comic Sans MS", cursive',
    fontSize: 14,
    background: "#fbfdff"
  },
  // Typewriter: faded, slightly uneven ink and a thin hatch on cream paper.
  typewriter: {
    preset: "typewriter",
    roughness: 1,
    bowing: 0.5,
    strokeWidth: 1.1,
    stroke: "#2b2b2b",
    fill: "#bdb9ab",
    fillStyle: "hachure",
    fillWeight: 0.5,
    hachureAngle: -30,
    hachureGap: 5,
    curveStepCount: 11,
    curveTightness: 0,
    disableMultiStroke: false,
    seed: 61,
    fontFamily: '"Special Elite", "Courier New", ui-monospace, monospace',
    fontSize: 13,
    background: "#f5f1e6"
  },
  // Midnight dashboard: cool light ink and a crisp accent fill on deep slate.
  midnight: {
    preset: "midnight",
    roughness: 0.6,
    bowing: 0.3,
    strokeWidth: 1.3,
    stroke: "#cbd5e1",
    fill: "#0ea5e9",
    fillStyle: "solid",
    fillWeight: 2,
    hachureAngle: 0,
    hachureGap: 6,
    curveStepCount: 14,
    curveTightness: 0,
    disableMultiStroke: true,
    seed: 67,
    fontFamily: '"IBM Plex Sans", system-ui, sans-serif',
    fontSize: 13,
    background: "#0f172a"
  },
  // Art deco: elegant gold lines and a sparse gilt hatch on midnight navy.
  art_deco: {
    preset: "art_deco",
    roughness: 0.5,
    bowing: 0.3,
    strokeWidth: 1.3,
    stroke: "#d4af37",
    fill: "#caa83a",
    fillStyle: "cross-hatch",
    fillWeight: 0.4,
    hachureAngle: 45,
    hachureGap: 8,
    curveStepCount: 16,
    curveTightness: 0,
    disableMultiStroke: true,
    seed: 71,
    fontFamily: '"Cinzel", Georgia, serif',
    fontSize: 13,
    background: "#0c1c3a"
  },
  // Manga: bold black ink and a dotted screentone fill on white.
  manga: {
    preset: "manga",
    roughness: 0.8,
    bowing: 0.5,
    strokeWidth: 1.8,
    stroke: "#111111",
    fill: "#1a1a1a",
    fillStyle: "dots",
    fillWeight: 1,
    hachureAngle: 0,
    hachureGap: 4,
    curveStepCount: 12,
    curveTightness: 0,
    disableMultiStroke: false,
    seed: 73,
    fontFamily: '"Bangers", "Comic Sans MS", cursive',
    fontSize: 15,
    background: "#fdfdfd"
  },
  // Highlighter: thin pen lines under a bright marker swipe, on notebook white.
  highlighter: {
    preset: "highlighter",
    roughness: 1.1,
    bowing: 0.9,
    strokeWidth: 1.1,
    stroke: "#374151",
    fill: "#fde047",
    fillStyle: "solid",
    fillWeight: 2,
    hachureAngle: 0,
    hachureGap: 5,
    curveStepCount: 11,
    curveTightness: 0,
    disableMultiStroke: false,
    seed: 79,
    fontFamily: '"Caveat", "Segoe Print", cursive',
    fontSize: 15,
    background: "#fffef9"
  },
  // Kraft: dark stamped ink and a brown hatch on recycled cardboard.
  kraft: {
    preset: "kraft",
    roughness: 1.1,
    bowing: 0.7,
    strokeWidth: 1.4,
    stroke: "#3b2f23",
    fill: "#9c7b4f",
    fillStyle: "hachure",
    fillWeight: 0.7,
    hachureAngle: -45,
    hachureGap: 4,
    curveStepCount: 11,
    curveTightness: 0,
    disableMultiStroke: false,
    seed: 83,
    fontFamily: '"Special Elite", "Courier New", ui-monospace, monospace',
    fontSize: 13,
    background: "#c9aa77"
  },
  // Synthwave: hot-magenta tubes and a purple cross-hatch on deep indigo.
  synthwave: {
    preset: "synthwave",
    roughness: 0.5,
    bowing: 0.3,
    strokeWidth: 1.7,
    stroke: "#ff4fd8",
    fill: "#7c3aed",
    fillStyle: "cross-hatch",
    fillWeight: 0.6,
    hachureAngle: 45,
    hachureGap: 6,
    curveStepCount: 16,
    curveTightness: 0,
    disableMultiStroke: true,
    seed: 89,
    fontFamily: '"Orbitron", ui-monospace, monospace',
    fontSize: 13,
    background: "#190a36"
  },
  // Botanical plate: fine green ink and a soft sage hatch on aged ivory.
  botanical: {
    preset: "botanical",
    roughness: 1,
    bowing: 0.8,
    strokeWidth: 1,
    stroke: "#3e5a36",
    fill: "#8aa06a",
    fillStyle: "hachure",
    fillWeight: 0.5,
    hachureAngle: -40,
    hachureGap: 5,
    curveStepCount: 14,
    curveTightness: 0,
    disableMultiStroke: false,
    seed: 97,
    fontFamily: '"EB Garamond", Georgia, serif',
    fontSize: 14,
    background: "#f4f1e4"
  },
  // Risograph: bold fluoro-pink fills and a blue keyline on uncoated stock.
  risograph: {
    preset: "risograph",
    roughness: 0.8,
    bowing: 0.6,
    strokeWidth: 1.6,
    stroke: "#2742d6",
    fill: "#ff5fa2",
    fillStyle: "solid",
    fillWeight: 2,
    hachureAngle: 0,
    hachureGap: 5,
    curveStepCount: 12,
    curveTightness: 0,
    disableMultiStroke: true,
    seed: 101,
    fontFamily: '"IBM Plex Sans", system-ui, sans-serif',
    fontSize: 13,
    background: "#f4f0e7"
  },
  // Sticky note: casual pen and a faint hatch on a bright square of paper.
  sticky_note: {
    preset: "sticky_note",
    roughness: 1.3,
    bowing: 1,
    strokeWidth: 1.3,
    stroke: "#3f3a33",
    fill: "#eab308",
    fillStyle: "hachure",
    fillWeight: 0.6,
    hachureAngle: -35,
    hachureGap: 4,
    curveStepCount: 9,
    curveTightness: 0,
    disableMultiStroke: false,
    seed: 103,
    fontFamily: '"Caveat", "Segoe Print", cursive',
    fontSize: 16,
    background: "#ffe27a"
  },
  // Amber CRT: warm amber phosphor glyphs and a sparse hatch on near-black.
  amber_crt: {
    preset: "amber_crt",
    roughness: 0.6,
    bowing: 0.3,
    strokeWidth: 1.3,
    stroke: "#ffb000",
    fill: "#cc8800",
    fillStyle: "cross-hatch",
    fillWeight: 0.5,
    hachureAngle: 0,
    hachureGap: 7,
    curveStepCount: 14,
    curveTightness: 0,
    disableMultiStroke: true,
    seed: 107,
    fontFamily: '"VT323", ui-monospace, monospace',
    fontSize: 17,
    background: "#0d0a05"
  }
};
var DEFAULT_VIBE = "messy_sketch";
var PAPER_TEXTURED = [
  "messy_sketch",
  "chaotic_notebook",
  "pencil",
  "crayon",
  "davinci_journal",
  "chalkboard",
  "watercolor",
  "newsprint",
  "kraft",
  "botanical",
  "risograph",
  "sticky_note",
  "typewriter"
];
for (const p of PAPER_TEXTURED) {
  VIBE_PRESETS[p].texture = "paper";
}

// src/vibe/resolveVibe.ts
function presetOrDefault(preset) {
  if (preset === void 0) {
    return VIBE_PRESETS[DEFAULT_VIBE];
  }
  const found = VIBE_PRESETS[preset];
  if (found === void 0) {
    console.warn(
      `[goldenchart] Unknown vibe preset "${preset}"; falling back to "${DEFAULT_VIBE}". Valid presets: ${Object.keys(VIBE_PRESETS).join(", ")}.`
    );
    return VIBE_PRESETS[DEFAULT_VIBE];
  }
  return found;
}
function applyDefined(target, source) {
  const mutable = target;
  for (const key of Object.keys(source)) {
    const value = source[key];
    if (value !== void 0) {
      mutable[key] = value;
    }
  }
}
function resolveVibe(config, brandOverrides) {
  const hasBrand = brandOverrides !== void 0 && Object.keys(brandOverrides).length > 0;
  if (config === void 0 && !hasBrand) {
    return VIBE_PRESETS[DEFAULT_VIBE];
  }
  if (typeof config === "string" && !hasBrand) {
    return presetOrDefault(config);
  }
  const base = typeof config === "string" || config === void 0 ? presetOrDefault(config) : presetOrDefault(config.preset);
  const merged = { ...base };
  if (hasBrand) {
    applyDefined(merged, brandOverrides);
  }
  if (typeof config === "object" && config !== void 0) {
    const { preset: _preset, fill, ...overrides } = config;
    applyDefined(merged, overrides);
    if (fill !== void 0) {
      merged.fill = fill;
    }
  }
  return merged;
}
function vibeToRoughOptions(vibe, seedOverride) {
  const options = {
    roughness: vibe.roughness,
    bowing: vibe.bowing,
    stroke: vibe.stroke,
    strokeWidth: vibe.strokeWidth,
    fillStyle: vibe.fillStyle,
    fillWeight: vibe.fillWeight,
    hachureAngle: vibe.hachureAngle,
    hachureGap: vibe.hachureGap,
    curveStepCount: vibe.curveStepCount,
    curveTightness: vibe.curveTightness,
    disableMultiStroke: vibe.disableMultiStroke,
    seed: seedOverride ?? vibe.seed
  };
  if (vibe.fill !== null) {
    options.fill = vibe.fill;
  }
  return options;
}
var VibeContext = react.createContext(VIBE_PRESETS[DEFAULT_VIBE]);
function VibeProvider({ vibe, brandOverrides, children }) {
  const resolved = react.useMemo(() => resolveVibe(vibe, brandOverrides), [vibe, brandOverrides]);
  return /* @__PURE__ */ jsxRuntime.jsx(VibeContext.Provider, { value: resolved, children });
}
function useVibeContext() {
  return react.useContext(VibeContext);
}
function useResolvedVibe(localVibe, seed) {
  const contextVibe = useVibeContext();
  return react.useMemo(() => {
    const base = localVibe === void 0 ? contextVibe : resolveVibe(localVibe);
    return seed === void 0 ? base : { ...base, seed };
  }, [localVibe, contextVibe, seed]);
}
var generator = null;
function getRoughGenerator() {
  if (generator === null) {
    generator = rough__default.default.generator();
  }
  return generator;
}
function allFinite(...nums) {
  return nums.every((n) => Number.isFinite(n));
}
function drawableToPaths(drawable) {
  const infos = getRoughGenerator().toPaths(drawable);
  const sets = drawable.sets ?? [];
  return infos.map((info, i) => ({
    ...info,
    kind: sets[i]?.type === "path" ? "stroke" : "fill"
  }));
}
function SketchPaths({
  paths,
  className,
  style,
  onClick,
  dataAttrs,
  onPointerEnter,
  onPointerMove,
  onPointerLeave,
  onPointerDown,
  onPointerUp,
  children,
  animate,
  clip
}) {
  const rawId = react.useId();
  const hasFill = paths.some((p) => p.kind === "fill");
  const clipId = clip && hasFill ? `gc-clip-${rawId.replace(/:/g, "")}` : null;
  return /* @__PURE__ */ jsxRuntime.jsxs(
    "g",
    {
      className,
      style,
      onClick,
      onPointerEnter,
      onPointerMove,
      onPointerLeave,
      onPointerDown,
      onPointerUp,
      ...dataAttrs,
      children: [
        clipId ? /* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: clipId, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: clip }) }) : null,
        dataAttrs && clip ? /* @__PURE__ */ jsxRuntime.jsx("path", { d: clip, fill: "transparent", stroke: "none", "data-gc-hit": "" }) : null,
        paths.map((p, i) => {
          const isStroke = p.kind === "stroke";
          return /* @__PURE__ */ jsxRuntime.jsx(
            "path",
            {
              d: p.d,
              stroke: p.stroke,
              strokeWidth: p.strokeWidth,
              fill: p.fill,
              strokeLinecap: "round",
              strokeLinejoin: "round",
              clipPath: !isStroke && clipId ? `url(#${clipId})` : void 0,
              className: animate ? isStroke ? "gc-draw-stroke" : "gc-draw-fill" : void 0,
              pathLength: animate && isStroke ? 1 : void 0
            },
            i
          );
        }),
        children
      ]
    }
  );
}
function RoughRectangle({
  x,
  y,
  width,
  height,
  vibe,
  seed,
  stroke,
  fill,
  className,
  style,
  onClick,
  dataAttrs,
  onPointerEnter,
  onPointerMove,
  onPointerLeave,
  onPointerDown,
  onPointerUp,
  children
}) {
  const resolved = useResolvedVibe(vibe, seed);
  const paths = react.useMemo(() => {
    if (!allFinite(x, y, width, height)) return [];
    const shapeVibe = {
      ...resolved,
      stroke: stroke ?? resolved.stroke,
      fill: fill === void 0 ? resolved.fill : fill
    };
    const options = vibeToRoughOptions(shapeVibe, shapeVibe.seed);
    const drawable = getRoughGenerator().rectangle(x, y, width, height, options);
    return drawableToPaths(drawable);
  }, [x, y, width, height, resolved, stroke, fill]);
  const clip = allFinite(x, y, width, height) ? `M${x},${y}h${width}v${height}h${-width}z` : void 0;
  return /* @__PURE__ */ jsxRuntime.jsx(
    SketchPaths,
    {
      paths,
      className,
      style,
      onClick,
      dataAttrs,
      onPointerEnter,
      onPointerMove,
      onPointerLeave,
      onPointerDown,
      onPointerUp,
      animate: !!resolved.animate?.drawOn,
      clip,
      children
    }
  );
}
function Brush({ start, length, height }) {
  if (length <= 0 || height <= 0) return null;
  return /* @__PURE__ */ jsxRuntime.jsx(RoughRectangle, { x: start, y: 0, width: length, height, style: { opacity: 0.25 } });
}

// src/core/brush.ts
function pixelToValue(px, [p0, p1], [v0, v1]) {
  if (p1 === p0) return v0;
  return v0 + (px - p0) / (p1 - p0) * (v1 - v0);
}
function clientToViewBox(client, rectStart, rectLength, vbStart, vbLength) {
  if (rectLength <= 0) return vbStart;
  return vbStart + (client - rectStart) / rectLength * vbLength;
}
function brushRect(a, b) {
  return { start: Math.min(a, b), length: Math.abs(b - a) };
}
function marksInPixelRange(marks, range, axis) {
  const lo = Math.min(range[0], range[1]);
  const hi = Math.max(range[0], range[1]);
  return marks.filter((m) => {
    const v = axis === "x" ? m.cx : m.cy;
    return v >= lo && v <= hi;
  });
}

// src/core/transition.ts
function lerp(a, b, t) {
  return a + (b - a) * t;
}
function interpolateNumberMap(from, to, t) {
  const out = {};
  for (const k of Object.keys(to)) {
    out[k] = k in from ? lerp(from[k], to[k], t) : to[k];
  }
  return out;
}
function easeInOutCubic(t) {
  return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
}
var isDatumArray = (v) => Array.isArray(v) && v.every((d) => d && typeof d.label === "string" && typeof d.value === "number");
var isSeriesArray = (v) => Array.isArray(v) && v.every((s) => s && typeof s.id === "string" && Array.isArray(s.points));
function interpolateChartData(from, to, t) {
  if (isDatumArray(from) && isDatumArray(to)) {
    const prev = new Map(from.map((d) => [d.label, d.value]));
    return to.map((d) => ({ ...d, value: lerp(prev.get(d.label) ?? d.value, d.value, t) }));
  }
  if (isSeriesArray(from) && isSeriesArray(to)) {
    const prev = new Map(from.map((s) => [s.id, s.points]));
    return to.map((s) => {
      const pp = prev.get(s.id);
      return { ...s, points: s.points.map((p, i) => ({ x: p.x, y: pp && pp[i] ? lerp(pp[i].y, p.y, t) : p.y })) };
    });
  }
  return to;
}

// src/interactive/useDataTransition.ts
function prefersReducedMotion() {
  return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;
}
function useDataTransition(data, durationMs = 500, enabled = true) {
  const [frame, setFrame] = react.useState(data);
  const prev = react.useRef(data);
  react.useEffect(() => {
    if (!enabled || prefersReducedMotion() || prev.current === data) {
      setFrame(data);
      prev.current = data;
      return;
    }
    const from = prev.current;
    const start = performance.now();
    let raf = 0;
    const tick = (now) => {
      const t = Math.min(1, (now - start) / durationMs);
      setFrame(interpolateChartData(from, data, easeInOutCubic(t)));
      if (t < 1) raf = requestAnimationFrame(tick);
      else prev.current = data;
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [data, durationMs, enabled]);
  return frame;
}

// src/core/linkSelection.ts
function emptyLink() {
  return { bySource: {} };
}
function setFilter(state, source, keys) {
  if (keys.length === 0) return clearFilter(state, source);
  return { bySource: { ...state.bySource, [source]: keys } };
}
function clearFilter(state, source) {
  if (!(source in state.bySource)) return state;
  const next = { ...state.bySource };
  delete next[source];
  return { bySource: next };
}
function activeFilter(state) {
  const all = /* @__PURE__ */ new Set();
  for (const keys of Object.values(state.bySource)) for (const k of keys) all.add(k);
  return [...all].sort();
}
var LinkContext = react.createContext(null);
function LinkedCharts({ children }) {
  const [state, setState] = react.useState(emptyLink);
  const publish = react.useCallback((source, keys) => {
    setState((s) => setFilter(s, source, keys));
  }, []);
  const value = react.useMemo(() => ({ filter: activeFilter(state), publish }), [state, publish]);
  return /* @__PURE__ */ jsxRuntime.jsx(LinkContext.Provider, { value, children });
}
function useLinkGroup() {
  return react.useContext(LinkContext);
}
var defaultSeriesVisibility = { hidden: /* @__PURE__ */ new Set(), toggle: () => {
}, interactive: false };
var Ctx = react.createContext(defaultSeriesVisibility);
var SeriesVisibilityProvider = Ctx.Provider;
function useSeriesVisibility() {
  return react.useContext(Ctx);
}

// src/core/seriesVisibility.ts
function markKey(mark) {
  return `${mark.series ?? ""}:${mark.index}`;
}
function toggleSelection(current, key, multi) {
  if (!multi) return current.has(key) && current.size === 1 ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([key]);
  const next = new Set(current);
  if (next.has(key)) next.delete(key);
  else next.add(key);
  return next;
}

// src/core/text.ts
var HELVETICA_1000 = {
  " ": 278,
  "!": 278,
  '"': 355,
  "#": 556,
  $: 556,
  "%": 889,
  "&": 667,
  "'": 191,
  "(": 333,
  ")": 333,
  "*": 389,
  "+": 584,
  ",": 278,
  "-": 333,
  ".": 278,
  "/": 278,
  "0": 556,
  "1": 556,
  "2": 556,
  "3": 556,
  "4": 556,
  "5": 556,
  "6": 556,
  "7": 556,
  "8": 556,
  "9": 556,
  ":": 278,
  ";": 278,
  "<": 584,
  "=": 584,
  ">": 584,
  "?": 556,
  "@": 1015,
  A: 667,
  B: 667,
  C: 722,
  D: 722,
  E: 667,
  F: 611,
  G: 778,
  H: 722,
  I: 278,
  J: 500,
  K: 667,
  L: 556,
  M: 833,
  N: 722,
  O: 778,
  P: 667,
  Q: 778,
  R: 722,
  S: 667,
  T: 611,
  U: 722,
  V: 667,
  W: 944,
  X: 667,
  Y: 667,
  Z: 611,
  "[": 278,
  "\\": 278,
  "]": 278,
  "^": 469,
  _: 556,
  "`": 333,
  a: 556,
  b: 556,
  c: 500,
  d: 556,
  e: 556,
  f: 278,
  g: 556,
  h: 556,
  i: 222,
  j: 222,
  k: 500,
  l: 222,
  m: 833,
  n: 556,
  o: 556,
  p: 556,
  q: 556,
  r: 333,
  s: 500,
  t: 278,
  u: 556,
  v: 500,
  w: 722,
  x: 500,
  y: 500,
  z: 500,
  "{": 334,
  "|": 260,
  "}": 334,
  "~": 584
};
function charEm(ch) {
  return (HELVETICA_1000[ch] ?? 556) / 1e3;
}
function familyFactor(fontFamily) {
  const f = fontFamily.toLowerCase();
  if (f.includes("mono") || f.includes("courier")) return { fixed: 0.6, scale: 1 };
  if (f.includes("comic") || f.includes("print") || f.includes("cursive")) return { fixed: null, scale: 1.08 };
  return { fixed: null, scale: 1 };
}
function measureText(text, fontSize, fontFamily) {
  const { fixed, scale } = familyFactor(fontFamily);
  let em = 0;
  for (const ch of text) em += fixed ?? charEm(ch);
  return { width: em * fontSize * scale, height: fontSize * 1.2 };
}
function wrapText(text, maxWidth, fontSize, fontFamily) {
  const words = text.split(/\s+/).filter(Boolean);
  if (words.length === 0) return [];
  const lines = [];
  let line = "";
  const fits = (s) => measureText(s, fontSize, fontFamily).width <= maxWidth;
  for (const word of words) {
    const candidate = line ? `${line} ${word}` : word;
    if (fits(candidate) || !line) {
      if (fits(candidate)) {
        line = candidate;
        continue;
      }
      let chunk = "";
      for (const ch of word) {
        if (chunk && !fits(chunk + ch)) {
          lines.push(chunk);
          chunk = ch;
        } else {
          chunk += ch;
        }
      }
      line = chunk;
    } else {
      lines.push(line);
      line = word;
    }
  }
  if (line) lines.push(line);
  return lines;
}
var BASELINE_MAP = {
  auto: "auto",
  middle: "central",
  hanging: "hanging"
};
var LINE_EM = 1.2;
function RoughText({
  x,
  y,
  children,
  anchor = "middle",
  baseline = "middle",
  rotate,
  vibe,
  seed,
  fill,
  haloColor,
  maxWidth,
  className,
  style,
  onClick,
  dataAttrs,
  onPointerEnter,
  onPointerMove,
  onPointerLeave,
  onPointerDown,
  onPointerUp
}) {
  const resolved = useResolvedVibe(vibe, seed);
  const lines = maxWidth ? wrapText(children, maxWidth, resolved.fontSize, resolved.fontFamily) : null;
  const halo = haloColor ?? resolved.background;
  const haloWidth = halo ? Math.max(2, resolved.fontSize * 0.18) : void 0;
  const content = lines && lines.length > 1 ? lines.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx("tspan", { x, dy: `${i === 0 ? -((lines.length - 1) * LINE_EM) / 2 : LINE_EM}em`, children: line }, i)) : children;
  return /* @__PURE__ */ jsxRuntime.jsx(
    "text",
    {
      x,
      y,
      textAnchor: anchor,
      dominantBaseline: BASELINE_MAP[baseline],
      transform: rotate ? `rotate(${rotate} ${x} ${y})` : void 0,
      fontFamily: resolved.fontFamily,
      fontSize: resolved.fontSize,
      fill: fill ?? resolved.stroke,
      stroke: halo,
      strokeWidth: haloWidth,
      strokeLinejoin: "round",
      paintOrder: halo ? "stroke" : void 0,
      textRendering: "geometricPrecision",
      className,
      style,
      onClick,
      onPointerEnter,
      onPointerMove,
      onPointerLeave,
      onPointerDown,
      onPointerUp,
      ...dataAttrs,
      children: content
    }
  );
}

// src/interactive/placeTooltip.ts
function placeTooltip(anchor, size, bounds, gap = 8) {
  let x = anchor.x + gap;
  let y = anchor.y + gap;
  if (x + size.width > bounds.width) x = anchor.x - gap - size.width;
  if (y + size.height > bounds.height) y = anchor.y - gap - size.height;
  return { x: Math.max(0, x), y: Math.max(0, y) };
}

// src/interactive/defaultTooltipFormat.ts
function defaultTooltipFormat(mark) {
  const title = mark.label ?? mark.series;
  const rows = typeof mark.value === "number" ? [["value", String(mark.value)]] : Object.entries(mark.value).map(([k, v]) => [k, String(v)]);
  return title !== void 0 ? { title, rows } : { rows };
}
function markAriaLabel(mark) {
  const { title, rows } = defaultTooltipFormat(mark);
  return [...title ? [title] : [], ...rows.map(([k, v]) => `${k} ${v}`)].join(", ");
}
var PAD = 7;
var LINE = 15;
var CHAR_W = 7;
function Tooltip({ mark, anchor, bounds, format = defaultTooltipFormat }) {
  const resolved = useResolvedVibe();
  const { title, rows } = format(mark);
  const lines = [...title ? [title] : [], ...rows.map(([k, v]) => `${k}: ${v}`)];
  const width = PAD * 2 + Math.max(0, ...lines.map((l) => l.length)) * CHAR_W;
  const height = PAD * 2 + lines.length * LINE;
  const { x, y } = placeTooltip(anchor, { width, height }, bounds);
  const bg = resolved.background ?? "#ffffff";
  return /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${x}, ${y})`, pointerEvents: "none", "aria-hidden": "true", children: [
    /* @__PURE__ */ jsxRuntime.jsx("rect", { x: 0, y: 0, width, height, rx: 4, fill: bg, opacity: 0.92 }),
    /* @__PURE__ */ jsxRuntime.jsx(RoughRectangle, { x: 0, y: 0, width, height, fill: null }),
    lines.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx(RoughText, { x: PAD, y: PAD + LINE * (i + 0.7), anchor: "start", baseline: "middle", haloColor: bg, children: line }, i))
  ] });
}
function RoughLine({
  x1,
  y1,
  x2,
  y2,
  vibe,
  seed,
  stroke,
  className,
  style,
  onClick,
  dataAttrs,
  onPointerEnter,
  onPointerMove,
  onPointerLeave,
  onPointerDown,
  onPointerUp
}) {
  const resolved = useResolvedVibe(vibe, seed);
  const paths = react.useMemo(() => {
    if (!allFinite(x1, y1, x2, y2)) return [];
    const options = vibeToRoughOptions({ ...resolved, stroke: stroke ?? resolved.stroke }, resolved.seed);
    const drawable = getRoughGenerator().line(x1, y1, x2, y2, options);
    return drawableToPaths(drawable);
  }, [x1, y1, x2, y2, resolved, stroke]);
  return /* @__PURE__ */ jsxRuntime.jsx(
    SketchPaths,
    {
      paths,
      className,
      style,
      onClick,
      dataAttrs,
      onPointerEnter,
      onPointerMove,
      onPointerLeave,
      onPointerDown,
      onPointerUp,
      animate: !!resolved.animate?.drawOn
    }
  );
}
function nearestMark(marks, x, y) {
  let best = null;
  let bestD = Infinity;
  for (const m of marks) {
    const d = (m.cx - x) ** 2 + (m.cy - y) ** 2;
    if (d < bestD) {
      bestD = d;
      best = m;
    }
  }
  return best;
}
function Crosshair({ x, height }) {
  return /* @__PURE__ */ jsxRuntime.jsx(RoughLine, { x1: x, y1: 0, x2: x, y2: height });
}

// src/interactive/hoverStyle.ts
var HOVER_ATTR = "data-gc-hover";
var CURRENT_ATTR = "data-gc-current";
function hoverCss(dimOpacity = 0.35) {
  return `[${HOVER_ATTR}] [data-gc-mark]:not([${CURRENT_ATTR}]){opacity:${dimOpacity}}@media (prefers-reduced-motion: no-preference){[data-gc-mark]{transition:opacity 120ms ease}}`;
}

// src/interactive/selectStyle.ts
var SELECTED_ATTR = "data-gc-selected";
var CHOSEN_ATTR = "data-gc-chosen";
function selectCss(dimOpacity = 0.5) {
  return `[data-gc-mark][${CHOSEN_ATTR}]{opacity:1}[${SELECTED_ATTR}] [data-gc-mark]:not([${CHOSEN_ATTR}]){opacity:${dimOpacity}}`;
}
function markFromEvent(_svg, target, _clientX, _clientY) {
  const mark = readMark(target);
  if (!mark) return null;
  return { mark, x: mark.cx, y: mark.cy };
}
function enhanceMarks(svg) {
  svg.querySelectorAll("[data-gc-mark]").forEach((el) => {
    const mark = readMark(el);
    if (!mark) return;
    el.setAttribute("tabindex", "0");
    el.setAttribute("role", "img");
    el.setAttribute("aria-label", markAriaLabel(mark));
  });
}
function toSet(v) {
  return new Set(v == null ? [] : Array.isArray(v) ? v : [v]);
}
function InteractiveChart({
  children,
  tooltip = true,
  highlight = true,
  onHover,
  selectable = false,
  selected,
  defaultSelected,
  multiSelect = false,
  onSelect,
  legendToggle = true,
  crosshair = false,
  zoom = false,
  pan = false,
  brush = false,
  onBrush,
  transition = false,
  linkGroup,
  vibe
}) {
  const link = useLinkGroup();
  const svgRef = react.useRef(null);
  const currentRef = react.useRef(null);
  const lastKeyRef = react.useRef(null);
  const [hover, setHover] = react.useState(null);
  const [viewBox, setViewBox] = react.useState(void 0);
  const [internalSel, setInternalSel] = react.useState(() => toSet(defaultSelected));
  const selectedKeys = selected !== void 0 ? toSet(selected) : internalSel;
  const selSig = [...selectedKeys].sort().join("|");
  const [hidden, setHidden] = react.useState(/* @__PURE__ */ new Set());
  const toggle = react.useCallback((series) => {
    setHidden((prev) => {
      const next = new Set(prev);
      if (next.has(series)) next.delete(series);
      else next.add(series);
      return next;
    });
  }, []);
  const visibility = react.useMemo(
    () => ({ hidden, toggle, interactive: legendToggle }),
    [hidden, toggle, legendToggle]
  );
  const zoomBounds = react.useMemo(
    () => zoom ? chartXExtent(children.props) : null,
    [zoom, children]
  );
  const zp = useZoomPan(zoomBounds, { pan: pan && !brush });
  const childProps = children.props;
  const dataKey = Array.isArray(childProps.series) ? "series" : Array.isArray(childProps.data) ? "data" : null;
  const transitionOn = !!transition && dataKey != null;
  const durationMs = typeof transition === "object" ? transition.durationMs ?? 500 : 500;
  const animatedData = useDataTransition(dataKey ? childProps[dataKey] : void 0, durationMs, transitionOn);
  const overrides = {};
  if (zp.domain) overrides.xAxis = { ...childProps.xAxis ?? {}, domain: zp.domain };
  if (transitionOn && dataKey) overrides[dataKey] = animatedData;
  const content = Object.keys(overrides).length ? react.cloneElement(children, overrides) : children;
  const [brushPx, setBrushPx] = react.useState(null);
  const brushing = react.useRef(false);
  const clear = react.useCallback(() => {
    const svg = svgRef.current;
    if (svg) {
      svg.removeAttribute(HOVER_ATTR);
      currentRef.current?.removeAttribute(CURRENT_ATTR);
      currentRef.current = null;
    }
    lastKeyRef.current = null;
    setHover(null);
    onHover?.(null);
  }, [onHover]);
  const onMove = react.useCallback(
    (e) => {
      const svg = svgRef.current;
      const target = e.target;
      if (!svg || !target) return;
      const next = markFromEvent(svg, target);
      if (!next) {
        clear();
        return;
      }
      const key = markKey(next.mark);
      if (key === lastKeyRef.current) return;
      lastKeyRef.current = key;
      if (highlight) {
        svg.setAttribute(HOVER_ATTR, "");
        const group = target.closest("[data-gc-mark]");
        if (group !== currentRef.current) {
          currentRef.current?.removeAttribute(CURRENT_ATTR);
          group?.setAttribute(CURRENT_ATTR, "");
          currentRef.current = group;
        }
      }
      setHover(next);
      onHover?.(next.mark);
    },
    [highlight, onHover, clear]
  );
  const activate = react.useCallback(
    (target) => {
      if (!selectable || !target) return;
      const mark = readMark(target);
      if (!mark) return;
      const next = toggleSelection(selectedKeys, markKey(mark), multiSelect);
      if (selected === void 0) setInternalSel(next);
      onSelect?.(mark, [...next]);
      if (linkGroup) link?.publish(linkGroup, [...next]);
    },
    [selectable, multiSelect, selected, onSelect, selSig, linkGroup, link]
    // selSig: stable signature of selectedKeys
  );
  const onClick = react.useCallback((e) => activate(e.target), [activate]);
  const onKey = react.useCallback(
    (e) => {
      const ke = e;
      if (ke.key !== "Enter" && ke.key !== " ") return;
      ke.preventDefault?.();
      activate(e.target);
    },
    [activate]
  );
  const attach = react.useCallback((node) => {
    const svg = node?.querySelector("svg") ?? null;
    svgRef.current = svg;
    setViewBox(svg?.getAttribute("viewBox") ?? void 0);
    if (svg) enhanceMarks(svg);
  }, []);
  const linkSig = linkGroup && link ? link.filter.join("|") : "";
  react.useEffect(() => {
    const svg = svgRef.current;
    if (!svg) return;
    const emphasized = /* @__PURE__ */ new Set([...selectedKeys, ...linkGroup && link ? link.filter : []]);
    if (emphasized.size > 0) svg.setAttribute(SELECTED_ATTR, "");
    else svg.removeAttribute(SELECTED_ATTR);
    svg.querySelectorAll("[data-gc-mark]").forEach((el) => {
      const m = readMark(el);
      if (m && emphasized.has(markKey(m))) el.setAttribute(CHOSEN_ATTR, "");
      else el.removeAttribute(CHOSEN_ATTR);
    });
  }, [selSig, linkSig]);
  react.useEffect(() => {
    const svg = svgRef.current;
    if (!svg || !(zoom || pan)) return;
    const wheel = (e) => zp.onWheel(e);
    const down = (e) => zp.onPointerDown(e);
    const move = (e) => zp.onPointerMove(e);
    const up = () => zp.onPointerUp();
    svg.addEventListener("wheel", wheel, { passive: false });
    if (pan && !brush) {
      svg.addEventListener("pointerdown", down);
      svg.addEventListener("pointermove", move);
      svg.addEventListener("pointerup", up);
      svg.addEventListener("pointerleave", up);
    }
    return () => {
      svg.removeEventListener("wheel", wheel);
      svg.removeEventListener("pointerdown", down);
      svg.removeEventListener("pointermove", move);
      svg.removeEventListener("pointerup", up);
      svg.removeEventListener("pointerleave", up);
    };
  }, [zoom, pan, brush, zp.onWheel, zp.onPointerDown, zp.onPointerMove, zp.onPointerUp]);
  react.useEffect(() => {
    const svg = svgRef.current;
    if (!svg || !brush) return;
    const vb = (viewBox ?? "0 0 0 0").split(/[\s,]+/).map(Number);
    const toVb = (clientX) => {
      const r = svg.getBoundingClientRect();
      return clientToViewBox(clientX, r.left, r.width, vb[0] ?? 0, vb[2] ?? 0);
    };
    const down = (e) => {
      brushing.current = true;
      const x = toVb(e.clientX);
      setBrushPx({ a: x, b: x });
    };
    const move = (e) => {
      if (!brushing.current) return;
      const x = toVb(e.clientX);
      setBrushPx((p) => p ? { a: p.a, b: x } : null);
    };
    const up = () => {
      if (!brushing.current) return;
      brushing.current = false;
      setBrushPx((p) => {
        if (p && svgRef.current) {
          const tagged = [];
          svgRef.current.querySelectorAll("[data-gc-mark]").forEach((el) => {
            const m = readMark(el);
            if (m) tagged.push({ meta: m, key: markKey(m), cx: m.cx, cy: m.cy });
          });
          const hit = marksInPixelRange(tagged, [p.a, p.b], "x").map((t) => t.meta);
          onBrush?.(hit);
          if (linkGroup) link?.publish(linkGroup, hit.map(markKey));
        }
        return null;
      });
    };
    svg.addEventListener("pointerdown", down);
    svg.addEventListener("pointermove", move);
    svg.addEventListener("pointerup", up);
    svg.addEventListener("pointerleave", up);
    return () => {
      svg.removeEventListener("pointerdown", down);
      svg.removeEventListener("pointermove", move);
      svg.removeEventListener("pointerup", up);
      svg.removeEventListener("pointerleave", up);
    };
  }, [brush, viewBox, onBrush, linkGroup, link]);
  react.useEffect(() => {
    const svg = svgRef.current;
    if (!svg) return;
    svg.addEventListener("pointermove", onMove);
    svg.addEventListener("pointerleave", clear);
    svg.addEventListener("focusin", onMove);
    svg.addEventListener("focusout", clear);
    svg.addEventListener("click", onClick);
    svg.addEventListener("keydown", onKey);
    return () => {
      svg.removeEventListener("pointermove", onMove);
      svg.removeEventListener("pointerleave", clear);
      svg.removeEventListener("focusin", onMove);
      svg.removeEventListener("focusout", clear);
      svg.removeEventListener("click", onClick);
      svg.removeEventListener("keydown", onKey);
    };
  }, [onMove, clear, onClick, onKey]);
  const renderTooltip = typeof tooltip === "function" ? tooltip : void 0;
  const vbParts = viewBox ? viewBox.split(/[\s,]+/).map(Number) : void 0;
  const vbWidth = vbParts?.[2];
  const vbHeight = vbParts?.[3];
  const cursor = brush ? "crosshair" : pan ? "grab" : zoom ? "zoom-in" : void 0;
  const zoomed = (zoom || pan) && zp.domain != null;
  return /* @__PURE__ */ jsxRuntime.jsxs(
    "div",
    {
      ref: attach,
      style: { position: "relative", display: "inline-block", overflow: zoom || pan ? "hidden" : void 0, cursor },
      children: [
        /* @__PURE__ */ jsxRuntime.jsx(SeriesVisibilityProvider, { value: visibility, children: content }),
        highlight ? /* @__PURE__ */ jsxRuntime.jsx("style", { children: hoverCss() }) : null,
        selectable || linkGroup ? /* @__PURE__ */ jsxRuntime.jsx("style", { children: selectCss() }) : null,
        selectable ? /* @__PURE__ */ jsxRuntime.jsx("style", { children: `[${HOVER_ATTR}] [data-gc-mark],[data-gc-mark]{cursor:pointer}` }) : null,
        zoomed ? /* @__PURE__ */ jsxRuntime.jsx(
          "button",
          {
            type: "button",
            onClick: () => zp.reset(),
            style: {
              position: "absolute",
              top: 6,
              right: 6,
              font: "11px system-ui, sans-serif",
              padding: "2px 8px",
              borderRadius: 4,
              border: "1px solid rgba(0,0,0,0.25)",
              background: "rgba(255,255,255,0.85)",
              cursor: "pointer"
            },
            children: "Reset view"
          }
        ) : null,
        brush && brushPx && vbHeight !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
          "svg",
          {
            viewBox,
            style: { position: "absolute", inset: 0, pointerEvents: "none", width: "100%", height: "100%" },
            children: /* @__PURE__ */ jsxRuntime.jsx(VibeProvider, { vibe, children: /* @__PURE__ */ jsxRuntime.jsx(Brush, { start: brushRect(brushPx.a, brushPx.b).start, length: brushRect(brushPx.a, brushPx.b).length, height: vbHeight }) })
          }
        ) : null,
        hover && (tooltip || crosshair) ? /* @__PURE__ */ jsxRuntime.jsx(
          "svg",
          {
            viewBox,
            style: { position: "absolute", inset: 0, pointerEvents: "none", width: "100%", height: "100%" },
            children: /* @__PURE__ */ jsxRuntime.jsxs(VibeProvider, { vibe, children: [
              crosshair && vbHeight !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(Crosshair, { x: hover.x, height: vbHeight }) : null,
              tooltip ? renderTooltip ? /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${hover.x}, ${hover.y})`, children: renderTooltip(hover.mark) }) : vbWidth !== void 0 && vbHeight !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { mark: hover.mark, anchor: { x: hover.x, y: hover.y }, bounds: { width: vbWidth, height: vbHeight } }) : null : null
            ] })
          }
        ) : null
      ]
    }
  );
}

// src/interactive/embed.ts
function escapeHtml(s) {
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}
var TIP_CSS = "#gc-tip{position:fixed;display:none;pointer-events:none;background:#111;color:#fff;font:12px/1.4 system-ui,sans-serif;padding:4px 8px;border-radius:4px;box-shadow:0 1px 4px rgba(0,0,0,.3);z-index:10}";
var HYDRATOR = "(function(){var tip=document.getElementById('gc-tip');var root=document.querySelector('svg[data-gc]')||document.querySelector('svg');if(!root||!tip)return;root.addEventListener('pointermove',function(e){var g=e.target.closest&&e.target.closest('[data-gc-mark]');if(!g){tip.style.display='none';return;}var label=g.getAttribute('data-gc-label')||'';var value=g.getAttribute('data-gc-value')||'';tip.textContent=label?(label+': '+value):value;tip.style.display='block';tip.style.left=(e.clientX+12)+'px';tip.style.top=(e.clientY+12)+'px';});root.addEventListener('pointerleave',function(){tip.style.display='none';});})();";
function interactiveEmbed(svg, opts = {}) {
  const tooltip = opts.tooltip !== false;
  const title = escapeHtml(opts.title ?? "GoldenChart");
  const head = '<!doctype html>\n<html>\n<head>\n<meta charset="utf-8">\n<title>' + title + "</title>\n" + (tooltip ? "<style>" + TIP_CSS + "</style>\n" : "") + "</head>\n<body>\n";
  const body = svg + "\n" + (tooltip ? '<div id="gc-tip"></div>\n<script>' + HYDRATOR + "</script>\n" : "");
  return head + body + "</body>\n</html>\n";
}

exports.Brush = Brush;
exports.CHOSEN_ATTR = CHOSEN_ATTR;
exports.CURRENT_ATTR = CURRENT_ATTR;
exports.Crosshair = Crosshair;
exports.HOVER_ATTR = HOVER_ATTR;
exports.InteractiveChart = InteractiveChart;
exports.LinkedCharts = LinkedCharts;
exports.SELECTED_ATTR = SELECTED_ATTR;
exports.SeriesVisibilityProvider = SeriesVisibilityProvider;
exports.Tooltip = Tooltip;
exports.activeFilter = activeFilter;
exports.brushRect = brushRect;
exports.chartXExtent = chartXExtent;
exports.clampDomain = clampDomain;
exports.clearFilter = clearFilter;
exports.clientToViewBox = clientToViewBox;
exports.defaultSeriesVisibility = defaultSeriesVisibility;
exports.defaultTooltipFormat = defaultTooltipFormat;
exports.easeInOutCubic = easeInOutCubic;
exports.emptyLink = emptyLink;
exports.enhanceMarks = enhanceMarks;
exports.focusFraction = focusFraction;
exports.hoverCss = hoverCss;
exports.interactiveEmbed = interactiveEmbed;
exports.interpolateChartData = interpolateChartData;
exports.interpolateNumberMap = interpolateNumberMap;
exports.lerp = lerp;
exports.markAriaLabel = markAriaLabel;
exports.markFromEvent = markFromEvent;
exports.markKey = markKey;
exports.marksInPixelRange = marksInPixelRange;
exports.nearestMark = nearestMark;
exports.panDomain = panDomain;
exports.pixelToValue = pixelToValue;
exports.placeTooltip = placeTooltip;
exports.prefersReducedMotion = prefersReducedMotion;
exports.readMark = readMark;
exports.selectCss = selectCss;
exports.setFilter = setFilter;
exports.toggleSelection = toggleSelection;
exports.useDataTransition = useDataTransition;
exports.useLinkGroup = useLinkGroup;
exports.useSeriesVisibility = useSeriesVisibility;
exports.useZoomPan = useZoomPan;
exports.wheelFactor = wheelFactor;
exports.wheelZoom = wheelZoom;
exports.zoomDomain = zoomDomain;
//# sourceMappingURL=interactive.cjs.map
//# sourceMappingURL=interactive.cjs.map