(function attachPyroFillPaint(root) {
  'use strict';

  const GRADIENT_TYPES = Object.freeze(['linear', 'radial', 'conic']);
  const IMAGE_TYPES = Object.freeze(['image', 'pattern']);
  const PAINT_TYPES = Object.freeze(['solid', 'none', ...GRADIENT_TYPES, ...IMAGE_TYPES]);
  const typeSet = new Set(PAINT_TYPES);
  const gradientSet = new Set(GRADIENT_TYPES);
  const imageSet = new Set(IMAGE_TYPES);
  const spreadMethods = new Set(['pad', 'reflect', 'repeat']);
  const finite = (value, fallback = 0) => Number.isFinite(Number(value)) ? Number(value) : fallback;
  const clamp = (value, minimum, maximum) => Math.min(maximum, Math.max(minimum, value));
  const round = (value, digits = 3) => Number(finite(value).toFixed(digits));
  const normalizeColor = (value, fallback = '#000000') => {
    const text = String(value || '').trim().toLowerCase();
    if (/^#[0-9a-f]{6}$/.test(text)) return text;
    if (/^#[0-9a-f]{3}$/.test(text)) return `#${[...text.slice(1)].map((part) => part + part).join('')}`;
    return fallback;
  };
  const normalizeAngle = (value) => ((round(value) % 360) + 360) % 360;
  const safeId = (value, fallback) => String(value || fallback).replace(/[^a-z0-9_-]/gi, '-').slice(0, 80) || fallback;

  function normalizeGradientMatrix(value) {
    if (!Array.isArray(value) || value.length < 6) return null;
    const matrix = value.slice(0, 6).map((entry) => finite(entry, NaN));
    return matrix.every(Number.isFinite) ? matrix.map((entry) => round(clamp(entry, -1e9, 1e9), 8)) : null;
  }

  function normalizeGradientCoordinates(value, type) {
    if (!value || typeof value !== 'object') return null;
    const keys = type === 'linear' ? ['x1', 'y1', 'x2', 'y2'] : type === 'radial' ? ['cx', 'cy', 'fx', 'fy', 'r', 'fr'] : [];
    if (!keys.length) return null;
    const result = {};
    for (const key of keys) {
      const number = finite(key === 'fr' && value[key] == null ? 0 : value[key], NaN); if (!Number.isFinite(number)) return null;
      result[key] = round(clamp(number, -1e9, 1e9), 8);
    }
    if (type === 'radial' && (!(result.r > 0) || result.fr < 0)) return null;
    return result;
  }

  function normalizeSvgPattern(value) {
    if (!value || typeof value !== 'object') return null;
    const matrix = normalizeGradientMatrix(value.matrix);
    const width = finite(value.width, NaN); const height = finite(value.height, NaN);
    if (!matrix || !(width > 0) || !(height > 0)) return null;
    const rawViewBox = Array.isArray(value.viewBox) ? value.viewBox.slice(0, 4).map(Number) : [];
    const viewBox = rawViewBox.length === 4 && rawViewBox.every(Number.isFinite) && rawViewBox[2] > 0 && rawViewBox[3] > 0
      ? rawViewBox.map((entry) => round(clamp(entry, -1e9, 1e9), 8)) : [0, 0, width, height];
    return {
      x: round(clamp(value.x, -1e9, 1e9), 8), y: round(clamp(value.y, -1e9, 1e9), 8),
      width: round(clamp(width, .00000001, 1e9), 8), height: round(clamp(height, .00000001, 1e9), 8),
      viewBox, matrix, patternUnits: value.patternUnits === 'objectBoundingBox' ? 'objectBoundingBox' : 'userSpaceOnUse',
      patternContentUnits: value.patternContentUnits === 'objectBoundingBox' ? 'objectBoundingBox' : 'userSpaceOnUse',
      preserveAspectRatio: String(value.preserveAspectRatio || 'xMidYMid meet').slice(0, 128),
      opacity: clamp(round(value.opacity == null ? 100 : value.opacity), 0, 100)
    };
  }

  function defaultStops(color = '#4f6df5') {
    const start = normalizeColor(color, '#4f6df5');
    return [
      { id: 'stop-1', offset: 0, color: start, opacity: 100 },
      { id: 'stop-2', offset: 100, color: '#ffffff', opacity: 100 }
    ];
  }

  function normalizeStops(value, fallbackColor = '#4f6df5') {
    const source = Array.isArray(value) ? value.slice(0, 64) : [];
    const seen = new Set();
    const stops = source.map((stop, index) => {
      if (!stop || typeof stop !== 'object') return null;
      let id = safeId(stop.id, `stop-${index + 1}`);
      while (seen.has(id)) id = `${id}-${index + 1}`;
      seen.add(id);
      return {
        id,
        offset: clamp(round(stop.offset), 0, 100),
        color: normalizeColor(stop.color, index ? '#ffffff' : normalizeColor(fallbackColor, '#4f6df5')),
        opacity: clamp(round(stop.opacity == null ? 100 : stop.opacity), 0, 100)
      };
    }).filter(Boolean).sort((left, right) => left.offset - right.offset);
    if (stops.length >= 2) return stops;
    return defaultStops(fallbackColor);
  }

  function defaultPaint(type = 'linear', color = '#4f6df5') {
    const normalizedType = typeSet.has(type) ? type : 'linear';
    const common = {
      type: normalizedType,
      x1: 0, y1: 50, x2: 100, y2: 50,
      cx: 50, cy: 50, radius: 50, angle: 0,
      spreadMethod: 'pad', gradientCoordinates: null, gradientMatrix: null,
      scale: 100, rotation: 0, offsetX: 0, offsetY: 0,
      href: '', imageWidth: 1, imageHeight: 1
    };
    if (gradientSet.has(normalizedType)) {
      common.stops = defaultStops(color);
    }
    return common;
  }

  function normalizePaint(value, { fallbackColor = '#4f6df5', href = null } = {}) {
    if (!value || typeof value !== 'object' || !typeSet.has(value.type) || ['solid', 'none'].includes(value.type)) return null;
    const paint = defaultPaint(value.type, fallbackColor);
    paint.x1 = clamp(round(value.x1 == null ? paint.x1 : value.x1), -1000, 1000);
    paint.y1 = clamp(round(value.y1 == null ? paint.y1 : value.y1), -1000, 1000);
    paint.x2 = clamp(round(value.x2 == null ? paint.x2 : value.x2), -1000, 1000);
    paint.y2 = clamp(round(value.y2 == null ? paint.y2 : value.y2), -1000, 1000);
    paint.cx = clamp(round(value.cx == null ? paint.cx : value.cx), -1000, 1000);
    paint.cy = clamp(round(value.cy == null ? paint.cy : value.cy), -1000, 1000);
    paint.radius = clamp(round(value.radius == null ? paint.radius : value.radius), .1, 2000);
    paint.angle = normalizeAngle(value.angle == null ? paint.angle : value.angle);
    paint.scale = clamp(round(value.scale == null ? paint.scale : value.scale), 1, 2000);
    paint.rotation = normalizeAngle(value.rotation == null ? paint.rotation : value.rotation);
    paint.offsetX = clamp(round(value.offsetX), -100000, 100000);
    paint.offsetY = clamp(round(value.offsetY), -100000, 100000);
    paint.href = String(href == null ? value.href || '' : href);
    paint.imageWidth = clamp(Math.round(finite(value.imageWidth, 1)), 1, 100000);
    paint.imageHeight = clamp(Math.round(finite(value.imageHeight, 1)), 1, 100000);
    if (gradientSet.has(paint.type)) {
      paint.stops = normalizeStops(value.stops, fallbackColor);
      paint.spreadMethod = spreadMethods.has(value.spreadMethod) ? value.spreadMethod : 'pad';
      paint.colorInterpolation = value.colorInterpolation === 'linearRGB' ? 'linearRGB' : 'sRGB';
      paint.gradientCoordinates = normalizeGradientCoordinates(value.gradientCoordinates, paint.type);
      paint.gradientMatrix = paint.gradientCoordinates ? normalizeGradientMatrix(value.gradientMatrix) : null;
    } else {
      delete paint.stops; delete paint.spreadMethod; delete paint.colorInterpolation;
      delete paint.gradientCoordinates; delete paint.gradientMatrix;
      if (paint.type === 'pattern') paint.svgPattern = normalizeSvgPattern(value.svgPattern);
      else delete paint.svgPattern;
    }
    return paint;
  }

  function scaleGeometry(value, scaleX, scaleY) {
    const paint = normalizePaint(value); if (!paint) return paint;
    const sx = finite(scaleX, 1); const sy = finite(scaleY, 1);
    const scaleMatrix = (matrix) => {
      if (!Array.isArray(matrix) || matrix.length < 6) return matrix;
      const [a, b, c, d, e, f] = matrix;
      return [
        round(a * sx, 8), round(b * sy, 8),
        round(c * sx, 8), round(d * sy, 8),
        round(e * sx, 8), round(f * sy, 8)
      ];
    };
    if (paint.gradientCoordinates && paint.gradientMatrix) paint.gradientMatrix = scaleMatrix(paint.gradientMatrix);
    if (paint.svgPattern?.matrix) paint.svgPattern.matrix = scaleMatrix(paint.svgPattern.matrix);
    return paint;
  }

  function convertPaint(value, type, fallbackColor = '#4f6df5') {
    if (type === 'solid' || type === 'none') return null;
    const previous = normalizePaint(value, { fallbackColor });
    const next = defaultPaint(type, fallbackColor);
    if (previous) {
      ['x1', 'y1', 'x2', 'y2', 'cx', 'cy', 'radius', 'angle', 'scale', 'rotation', 'offsetX', 'offsetY', 'href', 'imageWidth', 'imageHeight', 'spreadMethod']
        .forEach((key) => { next[key] = previous[key]; });
      if (gradientSet.has(type)) next.stops = normalizeStops(previous.stops, fallbackColor);
    }
    return normalizePaint(next, { fallbackColor });
  }

  function colorChannels(color) {
    const normalized = normalizeColor(color);
    return [1, 3, 5].map((index) => Number.parseInt(normalized.slice(index, index + 2), 16));
  }

  function colorAt(stops, position) {
    const normalized = normalizeStops(stops); const offset = clamp(finite(position), 0, 100);
    const rightIndex = normalized.findIndex((stop) => stop.offset >= offset);
    if (rightIndex <= 0) return { color: normalized[0].color, opacity: normalized[0].opacity };
    if (rightIndex < 0) {
      const last = normalized.at(-1); return { color: last.color, opacity: last.opacity };
    }
    const left = normalized[rightIndex - 1]; const right = normalized[rightIndex];
    const ratio = right.offset === left.offset ? 1 : (offset - left.offset) / (right.offset - left.offset);
    const leftChannels = colorChannels(left.color); const rightChannels = colorChannels(right.color);
    const channels = leftChannels.map((value, index) => Math.round(value + (rightChannels[index] - value) * ratio));
    return {
      color: `#${channels.map((value) => value.toString(16).padStart(2, '0')).join('')}`,
      opacity: round(left.opacity + (right.opacity - left.opacity) * ratio)
    };
  }

  function addStop(value, offset = 50, { id = `stop-${Date.now().toString(36)}` } = {}) {
    const paint = normalizePaint(value); if (!paint || !gradientSet.has(paint.type)) return paint;
    const position = clamp(round(offset), 0, 100); const sampled = colorAt(paint.stops, position);
    paint.stops = normalizeStops([...paint.stops, { id, offset: position, ...sampled }]);
    return paint;
  }

  function removeStop(value, id) {
    const paint = normalizePaint(value); if (!paint || !gradientSet.has(paint.type) || paint.stops.length <= 2) return paint;
    paint.stops = normalizeStops(paint.stops.filter((stop) => stop.id !== id));
    return paint;
  }

  function updateStop(value, id, patch = {}) {
    const paint = normalizePaint(value); if (!paint || !gradientSet.has(paint.type)) return paint;
    paint.stops = normalizeStops(paint.stops.map((stop) => stop.id === id ? { ...stop, ...patch, id: stop.id } : stop));
    return paint;
  }

  function largestStopGap(stops) {
    const normalized = normalizeStops(stops); let best = { size: -1, offset: 50 };
    for (let index = 1; index < normalized.length; index += 1) {
      const size = normalized[index].offset - normalized[index - 1].offset;
      if (size > best.size) best = { size, offset: (normalized[index].offset + normalized[index - 1].offset) / 2 };
    }
    return round(best.offset);
  }

  function typeLabel(type) {
    return ({ solid: 'Farbe', none: 'Keine', linear: 'Linear', radial: 'Radial', conic: 'Konisch', image: 'Bild', pattern: 'Muster' })[type] || 'Farbe';
  }

  const api = Object.freeze({
    PAINT_TYPES, GRADIENT_TYPES, IMAGE_TYPES,
    isGradient: (type) => gradientSet.has(type),
    isImage: (type) => imageSet.has(type),
    normalizeColor, normalizeStops, normalizePaint, scaleGeometry, defaultPaint, convertPaint,
    colorAt, addStop, removeStop, updateStop, largestStopGap, typeLabel,
    normalizeGradientCoordinates, normalizeGradientMatrix
  });
  root.PyroFillPaint = api;
  if (typeof module === 'object' && module?.exports) module.exports = api;
})(typeof globalThis !== 'undefined' ? globalThis : window);
