{"version":3,"sources":["../src/path.ts","../src/internal/math.ts","../src/config/palettes.ts","../src/config/defaults.ts","../src/config/merge.ts","../src/geometry/clip-path.ts","../src/geometry/rng.ts","../src/geometry/edges.ts","../src/geometry/noise-tile.ts","../src/geometry/pool.ts","../src/geometry/mark-space.ts"],"sourcesContent":["/**\n * `@highlighters/core/path` - the DOM-free, SSR-safe subpath.\n *\n * MUST NOT touch `window`/`document`/`Element`/`Range` at module load: only pure\n * geometry, config/palette helpers, and pure types, never `render/*` or `targeting/*`.\n * Seed-deterministic, so identical inputs produce byte-identical server and client output.\n */\n\nexport { resolveOptions, mergeOptions } from \"./config/merge.js\";\nexport { DEFAULT_OPTIONS } from \"./config/defaults.js\";\nexport { PALETTES, getPalette, resolveSwatch, defaultSwatch } from \"./config/palettes.js\";\n\nexport { buildMarkGeometry } from \"./geometry/mark-space.js\";\nexport { buildClipPath } from \"./geometry/clip-path.js\";\nexport { buildEdge } from \"./geometry/edges.js\";\nexport { buildNoiseTile, buildNoiseTileDataUrl } from \"./geometry/noise-tile.js\";\nexport { buildPoolGradient } from \"./geometry/pool.js\";\nexport { hashJitter, hashU32, mulberry } from \"./geometry/rng.js\";\n\n// Pure types only - no DOM-touching types.\nexport type {\n  ColorValue,\n  MarkType,\n  ShapeType,\n  TipType,\n  EdgeCap,\n  BlendMode,\n  SnapMode,\n  RendererTier,\n  RendererTierPreference,\n  AnimationDirection,\n  AnimationTrigger,\n  EasingValue,\n  GradientStop,\n  GradientConfig,\n  PaletteName,\n  PaletteSwatch,\n  Palette,\n  TipOptions,\n  InkOptions,\n  EdgeOptions,\n  PaperOptions,\n  GlowOptions,\n  AnimationOptions,\n  HighlightOptions,\n  ResolvedTip,\n  ResolvedInk,\n  ResolvedEdge,\n  ResolvedPaper,\n  ResolvedGlow,\n  ResolvedAnimation,\n  ResolvedOptions,\n  Box,\n  LineRect,\n  Anchor,\n  EdgeVertex,\n  NoiseTile,\n  MaskOffset,\n  PoolGradient,\n  MarkGeometry,\n} from \"./types.js\";\n","export function clamp(value: number, lo: number, hi: number): number {\n  return value < lo ? lo : value > hi ? hi : value;\n}\n","/** Curated palette families, each tuned so any pair of its swatches reads coherently for color-coding. Pure data. */\n\nimport type {\n  ColorValue,\n  Palette,\n  PaletteName,\n  PaletteSwatch,\n} from \"../types.js\";\n\n/** Each `swatches` map is ordered; that order is the color-coding cycle. */\nexport const PALETTES: Record<PaletteName, Palette> = {\n  fluorescent: {\n    name: \"fluorescent\",\n    swatches: {\n      yellow: \"#fff14d\",\n      green: \"#9bff5a\",\n      orange: \"#ffb24d\",\n      pink: \"#ff6fae\",\n      blue: \"#5ad7ff\",\n      purple: \"#c08bff\",\n    },\n  },\n  mild: {\n    name: \"mild\",\n    swatches: {\n      yellow: \"#f5e6a8\",\n      green: \"#bfe0b2\",\n      blue: \"#aacfe0\",\n      pink: \"#eec2cf\",\n      orange: \"#f0cdb0\",\n      purple: \"#cdc1e0\",\n    },\n  },\n  vintage: {\n    name: \"vintage\",\n    swatches: {\n      mustard: \"#e3c567\",\n      olive: \"#bcc06a\",\n      rust: \"#d99873\",\n      rose: \"#d39ba0\",\n      teal: \"#7fb3a8\",\n      plum: \"#a98bb0\",\n    },\n  },\n  neutral: {\n    name: \"neutral\",\n    swatches: {\n      sand: \"#e7dcc4\",\n      stone: \"#d8d2c4\",\n      clay: \"#dcc3b0\",\n      sage: \"#cdd6c2\",\n      slate: \"#c4ccd2\",\n      taupe: \"#d3c8c2\",\n    },\n  },\n  calm: {\n    name: \"calm\",\n    swatches: {\n      sky: \"#bcd6ec\",\n      mint: \"#bfe4d6\",\n      lavender: \"#cfcae8\",\n      blush: \"#ecd2da\",\n      seafoam: \"#c6e3df\",\n      periwinkle: \"#c2cce8\",\n    },\n  },\n};\n\n/** Default swatch per family: the least-text-obscuring hue. */\nconst DEFAULT_SWATCH_NAMES: Record<PaletteName, string> = {\n  fluorescent: \"yellow\",\n  mild: \"yellow\",\n  vintage: \"mustard\",\n  neutral: \"sand\",\n  calm: \"sky\",\n};\n\n/** Return a palette family by name. Throws on an unknown family. */\nexport function getPalette(name: PaletteName): Palette {\n  const palette = PALETTES[name];\n  if (!palette) {\n    throw new Error(`@highlighters: unknown palette \"${name}\"`);\n  }\n  return palette;\n}\n\n/** Resolve a `{ palette, swatch }` reference to a {@link ColorValue}. Throws on an unknown swatch. */\nexport function resolveSwatch(ref: PaletteSwatch): ColorValue {\n  const palette = getPalette(ref.palette);\n  const color = palette.swatches[ref.swatch];\n  if (color === undefined) {\n    throw new Error(\n      `@highlighters: unknown swatch \"${ref.swatch}\" in palette \"${ref.palette}\"`,\n    );\n  }\n  return color;\n}\n\n/** The default color for a family. */\nexport function defaultSwatch(name: PaletteName): ColorValue {\n  return resolveSwatch({ palette: name, swatch: DEFAULT_SWATCH_NAMES[name] });\n}\n","/** The fully-resolved baseline at the bottom of the merge chain. Deep-frozen so it can be shared as the merge floor without mutation. */\n\nimport type { ResolvedOptions } from \"../types.js\";\nimport { defaultSwatch } from \"./palettes.js\";\n\nexport const DEFAULT_OPTIONS: ResolvedOptions = Object.freeze({\n  markType: \"highlight\",\n  color: defaultSwatch(\"mild\"),\n  gradient: null,\n  opacity: 0.55,\n  blendMode: \"multiply\",\n  vivid: false,\n  tip: Object.freeze({\n    type: \"chisel\",\n    width: 16,\n    thickness: 4,\n    angle: 35,\n    overshoot: 2,\n    overshootJitter: 1,\n    angleJitter: 0,\n  }),\n  ink: Object.freeze({\n    flow: 0.45,\n    viscosity: 0.5,\n    feathering: 0.2,\n    streakiness: 0.25,\n    dryout: 0.1,\n    startEndBuildup: 0.1,\n    flowFade: 0.5,\n  }),\n  // Speed-aware deposit is Beta and off by default; thresholds are px/ms.\n  speed: Object.freeze({\n    enabled: false,\n    sensitivity: 1,\n    slowSpeed: 2.5,\n    fastSpeed: 10.5,\n    minDeposit: 0.4,\n    smoothing: 1,\n    resolution: 24,\n    dryoutBoost: 1,\n    streakBoost: 0.08,\n    featherReduce: 1,\n    poolBoost: 1,\n  }),\n  edge: Object.freeze({\n    waviness: 1,\n    frequency: 22, // px segment length of the wave grid\n    roughness: 0.2,\n    cap: \"round\",\n    radius: 5,\n  }),\n  paper: Object.freeze({\n    absorbency: 0.3,\n  }),\n  glow: Object.freeze({\n    enabled: false,\n    intensity: 0.5,\n    spread: 4,\n    // Empty resolves to a brightened form of the ink color at render time.\n    color: \"\",\n  }),\n  snap: \"word\",\n  fadeOnClear: true,\n  renderer: \"auto\",\n  animation: Object.freeze({\n    draw: true,\n    duration: 420,\n    easing: \"ease-out\",\n    direction: \"left-to-right\",\n    stagger: 90,\n    trigger: \"immediate\",\n    threshold: 0.2,\n    rootMargin: \"0px\",\n    repeat: false,\n  }),\n  semantic: false,\n  contrastBackground: null,\n  // Null signals each mark must derive its seed from a stable target identity.\n  seed: null,\n}) as ResolvedOptions;\n","/** Option merging and resolution. SSR-safe (no DOM access). */\n\nimport type {\n  BlendMode,\n  ColorValue,\n  HighlightOptions,\n  PaletteSwatch,\n  ResolvedAnimation,\n  ResolvedEdge,\n  ResolvedGlow,\n  ResolvedInk,\n  ResolvedOptions,\n  ResolvedPaper,\n  ResolvedSpeedDynamics,\n  ResolvedTip,\n} from \"../types.js\";\nimport { clamp } from \"../internal/math.js\";\nimport { DEFAULT_OPTIONS } from \"./defaults.js\";\nimport { defaultSwatch, resolveSwatch } from \"./palettes.js\";\n\n/** A finite number, else `fallback`. Guards against NaN/Infinity leaking into geometry/CSS. */\nfunction finiteOr(value: number | undefined, fallback: number): number {\n  return typeof value === \"number\" && Number.isFinite(value) ? value : fallback;\n}\n\n/** A finite, strictly-positive number, else `fallback`. */\nfunction positiveOr(value: number | undefined, fallback: number): number {\n  return typeof value === \"number\" && Number.isFinite(value) && value > 0 ? value : fallback;\n}\n\n/** The namespaced option groups that are merged field-wise rather than replaced. */\nconst GROUP_KEYS = [\"tip\", \"ink\", \"speed\", \"edge\", \"paper\", \"glow\", \"animation\"] as const;\n\ntype GroupKey = (typeof GROUP_KEYS)[number];\n\n/** Shallow-merge a single namespaced group, with `override` winning per field. */\nfunction mergeGroup(\n  base: Record<string, unknown> | undefined,\n  override: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n  if (base === undefined) return override;\n  if (override === undefined) return base;\n  return { ...base, ...override };\n}\n\n/** Deep-merge two partials: scalars take `override`, namespaced groups merge field-wise, `shape`/`markType` collapse onto `markType`. Pure. */\nexport function mergeOptions(\n  base: HighlightOptions,\n  override: HighlightOptions,\n): HighlightOptions {\n  const result: HighlightOptions = { ...base, ...override };\n\n  for (const key of GROUP_KEYS) {\n    const merged = mergeGroup(\n      base[key] as Record<string, unknown> | undefined,\n      override[key] as Record<string, unknown> | undefined,\n    );\n    if (merged === undefined) {\n      delete result[key];\n    } else {\n      (result as Record<GroupKey, unknown>)[key] = merged;\n    }\n  }\n\n  const markType =\n    override.markType ?? override.shape ?? base.markType ?? base.shape;\n  if (markType !== undefined) {\n    result.markType = markType;\n    delete result.shape;\n  }\n\n  return result;\n}\n\n/** Resolve the `color`/`palette` inputs to a concrete {@link ColorValue}. */\nfunction resolveColor(\n  color: ColorValue | PaletteSwatch | undefined,\n  palette: HighlightOptions[\"palette\"],\n  fallback: ColorValue,\n): ColorValue {\n  // Empty/whitespace string is treated as unset so it can't emit an empty colour token into the gradient.\n  if (typeof color === \"string\" && color.trim() !== \"\") {\n    return color;\n  }\n  if (color && typeof color === \"object\") {\n    return resolveSwatch(color);\n  }\n  if (palette) {\n    return defaultSwatch(palette);\n  }\n  return fallback;\n}\n\n/** Resolve a partial {@link HighlightOptions} into a fully-defaulted {@link ResolvedOptions}. Pure. */\nexport function resolveOptions(input: HighlightOptions = {}): ResolvedOptions {\n  const d = DEFAULT_OPTIONS;\n\n  const merged = mergeOptions({}, input);\n\n  const tip: ResolvedTip = {\n    type: merged.tip?.type ?? d.tip.type,\n    width: finiteOr(merged.tip?.width, d.tip.width),\n    thickness: finiteOr(merged.tip?.thickness, d.tip.thickness),\n    angle: finiteOr(merged.tip?.angle, d.tip.angle),\n    overshoot: finiteOr(merged.tip?.overshoot, d.tip.overshoot),\n    overshootJitter: finiteOr(merged.tip?.overshootJitter, d.tip.overshootJitter),\n    angleJitter: finiteOr(merged.tip?.angleJitter, d.tip.angleJitter),\n  };\n\n  const ink: ResolvedInk = {\n    flow: finiteOr(merged.ink?.flow, d.ink.flow),\n    viscosity: finiteOr(merged.ink?.viscosity, d.ink.viscosity),\n    feathering: finiteOr(merged.ink?.feathering, d.ink.feathering),\n    streakiness: finiteOr(merged.ink?.streakiness, d.ink.streakiness),\n    dryout: finiteOr(merged.ink?.dryout, d.ink.dryout),\n    startEndBuildup: finiteOr(merged.ink?.startEndBuildup, d.ink.startEndBuildup),\n    flowFade: finiteOr(merged.ink?.flowFade, d.ink.flowFade),\n  };\n\n  const sd = d.speed;\n  // Order thresholds so fastSpeed >= slowSpeed; an inverted pair collapses the velocity normalizer's denominator.\n  const rawSlow = Math.max(0, finiteOr(merged.speed?.slowSpeed, sd.slowSpeed));\n  const rawFast = Math.max(0, finiteOr(merged.speed?.fastSpeed, sd.fastSpeed));\n  const speed: ResolvedSpeedDynamics = {\n    enabled: merged.speed?.enabled ?? sd.enabled,\n    sensitivity: clamp(finiteOr(merged.speed?.sensitivity, sd.sensitivity), 0, 1),\n    slowSpeed: Math.min(rawSlow, rawFast),\n    fastSpeed: Math.max(rawSlow, rawFast),\n    minDeposit: clamp(finiteOr(merged.speed?.minDeposit, sd.minDeposit), 0, 1),\n    smoothing: clamp(finiteOr(merged.speed?.smoothing, sd.smoothing), 0, 1),\n    resolution: Math.max(4, Math.min(24, Math.round(finiteOr(merged.speed?.resolution, sd.resolution)))),\n    dryoutBoost: clamp(finiteOr(merged.speed?.dryoutBoost, sd.dryoutBoost), 0, 1),\n    streakBoost: clamp(finiteOr(merged.speed?.streakBoost, sd.streakBoost), 0, 1),\n    featherReduce: clamp(finiteOr(merged.speed?.featherReduce, sd.featherReduce), 0, 1),\n    poolBoost: clamp(finiteOr(merged.speed?.poolBoost, sd.poolBoost), 0, 1),\n  };\n\n  const edge: ResolvedEdge = {\n    waviness: finiteOr(merged.edge?.waviness, d.edge.waviness),\n    frequency: finiteOr(merged.edge?.frequency, d.edge.frequency),\n    roughness: finiteOr(merged.edge?.roughness, d.edge.roughness),\n    cap: merged.edge?.cap ?? d.edge.cap,\n    radius: finiteOr(merged.edge?.radius, d.edge.radius),\n  };\n\n  const paper: ResolvedPaper = {\n    absorbency: finiteOr(merged.paper?.absorbency, d.paper.absorbency),\n  };\n\n  // A `palette` with no `color` draws that palette's default swatch; detect from raw input, not merged.\n  const color =\n    input.color === undefined && input.palette !== undefined\n      ? defaultSwatch(input.palette)\n      : resolveColor(merged.color, merged.palette, d.color);\n\n  const glow: ResolvedGlow = {\n    enabled: merged.glow?.enabled ?? d.glow.enabled,\n    intensity: finiteOr(merged.glow?.intensity, d.glow.intensity),\n    spread: finiteOr(merged.glow?.spread, d.glow.spread),\n    color: merged.glow?.color ?? (d.glow.color || color),\n  };\n\n  const animation: ResolvedAnimation = {\n    draw: merged.animation?.draw ?? d.animation.draw,\n    duration: positiveOr(merged.animation?.duration, d.animation.duration),\n    easing: merged.animation?.easing ?? d.animation.easing,\n    direction: merged.animation?.direction ?? d.animation.direction,\n    stagger: finiteOr(merged.animation?.stagger, d.animation.stagger),\n    trigger: merged.animation?.trigger ?? d.animation.trigger,\n    threshold: finiteOr(merged.animation?.threshold, d.animation.threshold),\n    rootMargin: merged.animation?.rootMargin ?? d.animation.rootMargin,\n    repeat: merged.animation?.repeat ?? d.animation.repeat,\n  };\n\n  const blendMode: BlendMode = merged.blendMode ?? d.blendMode;\n\n  return {\n    markType: merged.markType ?? merged.shape ?? d.markType,\n    color,\n    gradient: merged.gradient ?? d.gradient,\n    opacity: Math.max(0, Math.min(1, finiteOr(merged.opacity, d.opacity))),\n    blendMode,\n    vivid: merged.vivid ?? d.vivid,\n    tip,\n    ink,\n    speed,\n    edge,\n    paper,\n    glow,\n    snap: merged.snap ?? d.snap,\n    fadeOnClear: merged.fadeOnClear ?? d.fadeOnClear,\n    renderer: merged.renderer ?? d.renderer,\n    animation,\n    semantic: merged.semantic ?? d.semantic,\n    contrastBackground: merged.contrastBackground ?? d.contrastBackground,\n    seed: merged.seed ?? d.seed,\n  };\n}\n","import type { Box, EdgeCap, EdgeVertex, ResolvedTip } from \"../types.js\";\n\n/**\n * Build the `clip-path: path(...)` value for one band, in absolute-px local coordinates.\n *\n * The shape is the chisel parallelogram, edges threaded through the wave vertices and\n * joined by rounded `Q`-arc corners. Slant is an absolute-px x-shift (not a percentage)\n * so radius and slant keep their true size at any width. Every coordinate is a pure\n * function of the inputs, so growing the mark only appends commands and the covered prefix\n * stays byte-identical.\n */\n\nexport interface ClipPathOptions {\n  /** The band's local box. Only `width`/`height` are used (origin is `0,0`). */\n  box: Box;\n  tip: ResolvedTip;\n  /** Top-edge wave vertices (ascending x), threaded left → right. */\n  topEdge: EdgeVertex[];\n  /** Bottom-edge wave vertices (ascending x), threaded right → left. */\n  bottomEdge: EdgeVertex[];\n  cap: EdgeCap;\n  /** Corner radius in absolute px (clamped against short marks). */\n  radius: number;\n  /** Advancing-front truncation width in local px (the draw-on); the emitted prefix stays byte-identical as it grows. Defaults to `box.width`. */\n  front?: number;\n}\n\nfunction fx(value: number): string {\n  return value.toFixed(1);\n}\n\nfunction fy(value: number): string {\n  return value.toFixed(2);\n}\n\n/** The chisel slant in absolute px: how far the top edge leads the bottom. Capped at half the width so it can't invert the parallelogram. */\nexport function chiselSlant(tip: ResolvedTip, width: number, height: number): number {\n  if (tip.type !== \"chisel\") return 0;\n  const norm = Math.min(Math.abs(tip.angle), 90) / 90;\n  return Math.min(norm * height * 0.9, width * 0.5);\n}\n\n/** Resolve slant and corner radius for a tip type and box. Radius is clamped so the four arcs always fit. */\nfunction resolveSlantAndRadius(\n  tip: ResolvedTip,\n  cap: EdgeCap,\n  width: number,\n  height: number,\n  radius: number,\n): { slant: number; r: number } {\n  const slant = chiselSlant(tip, width, height);\n\n  const usableWidth = Math.max(0, width - slant);\n  const maxByWidth = usableWidth / 2;\n  const maxByHeight = height / 2;\n  const radiusCeil = Math.max(0, Math.min(maxByWidth, maxByHeight));\n\n  let r: number;\n  if (cap === \"flat\" || cap === \"square\") {\n    r = 0;\n  } else if (tip.type === \"bullet\") {\n    r = radiusCeil;\n  } else if (tip.type === \"fine\") {\n    r = Math.min(radius, radiusCeil, height * 0.25);\n  } else {\n    r = Math.min(Math.max(0, radius), radiusCeil);\n  }\n  return { slant, r };\n}\n\n/** The smallest visible front (local px): the touchdown width where the leading cap's arcs just meet (`slant + 2·radius`). Below it the parallelogram inverts. */\nexport function minVisibleFront(\n  tip: ResolvedTip,\n  cap: EdgeCap,\n  width: number,\n  height: number,\n  radius: number,\n): number {\n  const { slant, r } = resolveSlantAndRadius(tip, cap, width, height, radius);\n  return slant + 2 * r;\n}\n\ninterface Pt {\n  x: number;\n  y: number;\n}\n\n/** Collect wave vertices strictly inside the corner-arc inset so a wave point can't kink a corner arc. `reverse` consumes right to left (the bottom edge). */\nfunction collectVertices(\n  vertices: EdgeVertex[],\n  reverse: boolean,\n  minX: number,\n  maxX: number,\n): Pt[] {\n  const out: Pt[] = [];\n  const n = vertices.length;\n  for (let k = 0; k < n; k++) {\n    const v = reverse ? vertices[n - 1 - k] : vertices[k];\n    if (v.x <= minX || v.x >= maxX) continue;\n    out.push({ x: v.x, y: v.y });\n  }\n  return out;\n}\n\n/** Emit a smooth edge through the wave vertices as quadratic Béziers (midpoint smoothing) so corners round instead of kinking. No vertices yields a straight `L`. */\nfunction smoothEdge(sx: number, sy: number, verts: Pt[], ex: number, ey: number): string {\n  if (verts.length === 0) return `L ${fx(ex)} ${fy(ey)} `;\n  const pts: Pt[] = [{ x: sx, y: sy }, ...verts, { x: ex, y: ey }];\n  let out = \"\";\n  for (let i = 1; i < pts.length - 2; i++) {\n    const mx = (pts[i].x + pts[i + 1].x) / 2;\n    const my = (pts[i].y + pts[i + 1].y) / 2;\n    out += `Q ${fx(pts[i].x)} ${fy(pts[i].y)} ${fx(mx)} ${fy(my)} `;\n  }\n  const c = pts[pts.length - 2];\n  out += `Q ${fx(c.x)} ${fy(c.y)} ${fx(ex)} ${fy(ey)} `;\n  return out;\n}\n\n/** Build the bare `path(...)` clip-path value, walking the parallelogram clockwise with a `Q` arc at each corner. */\nexport function buildClipPath(opts: ClipPathOptions): string {\n  const { box, tip, topEdge, bottomEdge, cap, radius } = opts;\n  const ow = box.width;\n  const oh = box.height;\n\n  // Resolve slant/radius from the FULL box, never the current front, so the leading cap shape is identical from first frame to last.\n  const { slant: sl, r: R } = resolveSlantAndRadius(tip, cap, ow, oh, radius);\n\n  // `front <= 0` is the empty state. Clamp into [minFront, ow] so the band starts at a tip-shaped touchdown and never inverts.\n  const reqFront = opts.front ?? ow;\n  if (reqFront <= 0) return 'path(\"M 0 0 Z\")';\n  const minFront = sl + 2 * R;\n  const F = Math.max(minFront, Math.min(reqFront, ow));\n\n  // Only the leading (right) cap rides the front `F`; the left cap is fixed.\n  const topStartX = sl + R;\n  const topEndX = F - R;\n  const bottomStartX = F - sl - R;\n  const bottomEndX = R;\n\n  const topVerts = collectVertices(topEdge, false, topStartX, topEndX);\n  const bottomVerts = collectVertices(bottomEdge, true, bottomEndX, bottomStartX);\n\n  return (\n    `path(\"M ${fx(topStartX)} 0 ` +\n    smoothEdge(topStartX, 0, topVerts, topEndX, 0) +\n    `Q ${fx(F)} 0 ${fx(F)} ${fy(R)} ` +\n    `L ${fx(F - sl)} ${fy(oh - R)} ` +\n    `Q ${fx(F - sl)} ${fy(oh)} ${fx(bottomStartX)} ${fy(oh)} ` +\n    smoothEdge(bottomStartX, oh, bottomVerts, bottomEndX, oh) +\n    `Q 0 ${fy(oh)} 0 ${fy(oh - R)} ` +\n    `L ${fx(sl)} ${fy(R)} ` +\n    `Q ${fx(sl)} 0 ${fx(topStartX)} 0 Z\")`\n  );\n}\n","/** Deterministic randomness primitives: no platform PRNG or wall-clock, so identical seeds yield byte-identical geometry on server and client. */\n\n/** Canonical jitter primitive: a value in `[-1, 1]` from a `sin()`-based hash. Draw decorrelated values by offsetting the seed. */\nexport function hashJitter(seed: number): number {\n  const x = Math.sin(seed * 9301 + 49297) * 233280;\n  return (x - Math.floor(x)) * 2 - 1;\n}\n\n/** Mulberry-style integer hash to u32. Avalanches the bits hard, for deriving one stable seed from another without low-bit correlation. */\nexport function hashU32(seed: number): number {\n  let z = (seed | 0) + 0x6d2b79f5;\n  z = Math.imul(z ^ (z >>> 15), z | 1);\n  z ^= z + Math.imul(z ^ (z >>> 7), z | 61);\n  return (z ^ (z >>> 14)) >>> 0;\n}\n\n/** Deterministic mulberry32 PRNG in `[0, 1)`, for cases needing many sequential draws rather than offset {@link hashJitter} lookups. */\nexport function mulberry(seed: number): () => number {\n  let a = seed | 0;\n  return function next(): number {\n    a |= 0;\n    a = (a + 0x6d2b79f5) | 0;\n    let t = Math.imul(a ^ (a >>> 15), a | 1);\n    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n  };\n}\n","import type { EdgeVertex } from \"../types.js\";\nimport { hashJitter } from \"./rng.js\";\n\n/**\n * Wavy-edge vertices on a fixed spatial grid.\n *\n * Vertices live at `x = gridIndex * segmentLength` on a global px grid, jitter\n * seeded by that grid index, so wavelength and per-x phase are width-independent\n * and growing the extent only appends vertices, never shifting the existing ones.\n * (Interpolating by current width and seeding by loop index would make the wobble swim during a drag.)\n */\n\n/** Per-role seed offsets so independent jitter draws never move in lockstep. */\nconst BASE_WAVE_OFFSET = 17;\nconst ROUGHNESS_OFFSET = 911;\n\nexport interface EdgeBuildOptions {\n  /** One endpoint in absolute px (lower or higher x; either order). */\n  startX: number;\n  /** The other endpoint in absolute px. May be less than `startX` (reversed edge). */\n  endX: number;\n  /** The edge's baseline y in absolute px; vertices jitter around this. */\n  baseY: number;\n  /** Wave wavelength: the px spacing between adjacent grid vertices. */\n  segmentLength: number;\n  /** Peak wave displacement from `baseY`, in absolute px. */\n  amplitude: number;\n  /** High-frequency micro-jitter on the base wave, `0`-`1` (up to ~30% of `amplitude`) so the edge reads frayed rather than sinusoidal. */\n  roughness: number;\n  /** Base seed for this edge (e.g. line seed + a top/bottom offset). */\n  seed: number;\n}\n\n/**\n * Generate wave vertices on the fixed global grid for one edge, ascending in `x`.\n * @returns Vertices ascending in `x`; empty for a sub-`segmentLength` span (caller draws straight).\n */\nexport function buildEdge(opts: EdgeBuildOptions): EdgeVertex[] {\n  const { startX, endX, baseY, segmentLength, amplitude, roughness, seed } = opts;\n\n  if (!(segmentLength > 0)) return [];\n\n  const lo = Math.min(startX, endX);\n  const hi = Math.max(startX, endX);\n\n  // Half-px epsilon keeps the extreme grid points clear of the corner arcs.\n  const firstIdx = Math.ceil((lo + 0.5) / segmentLength);\n  const lastIdx = Math.floor((hi - 0.5) / segmentLength);\n\n  const vertices: EdgeVertex[] = [];\n  const roughAmp = amplitude * 0.3 * roughness;\n  for (let i = firstIdx; i <= lastIdx; i++) {\n    const x = i * segmentLength;\n    let y = baseY + hashJitter(seed + i * BASE_WAVE_OFFSET) * amplitude;\n    if (roughAmp !== 0) {\n      y += hashJitter(seed + i * BASE_WAVE_OFFSET + ROUGHNESS_OFFSET) * roughAmp;\n    }\n    vertices.push({ x, y, gridIndex: i });\n  }\n  return vertices;\n}\n","import { clamp } from \"../internal/math.js\";\nimport type { NoiseTile } from \"../types.js\";\nimport { hashU32 } from \"./rng.js\";\n\n/**\n * The fixed-pixel noise tile that gives the mark its hand-inked grain.\n *\n * Two `feTurbulence` layers (striation lanes + pressure patches) baked once into a\n * constant-pixel, seamlessly-stitched tile and repeated, never scaled, so grain density\n * is invariant to mark size. Pure string-built SVG `data:` URL: no DOM access (SSR-safe).\n */\n\nconst DEFAULT_TILE_WIDTH = 256;\nconst DEFAULT_TILE_HEIGHT = 64;\n\n// Low x + moderate y yields chunky horizontal lanes that read as bands rather than hair-thin noise.\nconst STRIATION_FREQUENCY = \"0.04 0.34\";\nconst PATCH_FREQUENCY = \"0.012\";\n\n// Base mask-alpha floors/slopes; the builder lowers the floor as the knobs rise. Baseline stays high enough that the tip caps don't sit on a low-alpha hole.\nconst STRIATION_ALPHA_MIN = 0.82;\nconst STRIATION_ALPHA_SLOPE = 0.16;\nconst PATCH_ALPHA_MIN = 0.86;\nconst PATCH_ALPHA_SLOPE = 0.14;\n\nconst STRIATION_OCTAVES = 1;\nconst PATCH_OCTAVES = 2;\n\nexport interface NoiseTileOptions {\n  /** Fixed tile width in px (default {@link DEFAULT_TILE_WIDTH}). */\n  width?: number;\n  /** Fixed tile height in px (default {@link DEFAULT_TILE_HEIGHT}). */\n  height?: number;\n  /** Master seed; mixed into both layers' `feTurbulence` seeds deterministically. */\n  seed: number;\n  /** Striation density, `0`-`1`. Raises the streak layer's alpha contrast. Clamped internally. */\n  streakiness: number;\n  /** Pressure-patch density, `0`-`1`. Raises the soft pressure-blob contribution. Clamped internally. */\n  feathering: number;\n  /** Probabilistic alpha gaps, `0`-`1`: a discrete threshold cuts low-coverage regions to transparent. Defaults to `0`. */\n  dryout?: number;\n}\n\n/** Round to 3 decimals so the emitted SVG string is byte-stable. */\nfunction fmt(value: number): string {\n  return String(Math.round(value * 1000) / 1000);\n}\n\n/** Clamp then snap a density knob to its 0.02 bucket; sub-bucket alpha deltas are below perceptual threshold. */\nfunction quantizeKnob(value: number): number {\n  return Math.round(clamp(value, 0, 1) * 50) / 50;\n}\n\n// ASCII-only base64; avoids `btoa` (a DOM global absent in some SSR runtimes) while encoding identically everywhere.\nconst B64_ALPHABET =\n  \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\nfunction toBase64Ascii(input: string): string {\n  let out = \"\";\n  const len = input.length;\n  for (let i = 0; i < len; i += 3) {\n    const b0 = input.charCodeAt(i) & 0xff;\n    const b1 = i + 1 < len ? input.charCodeAt(i + 1) & 0xff : 0;\n    const b2 = i + 2 < len ? input.charCodeAt(i + 2) & 0xff : 0;\n    const triple = (b0 << 16) | (b1 << 8) | b2;\n    out += B64_ALPHABET[(triple >> 18) & 0x3f];\n    out += B64_ALPHABET[(triple >> 12) & 0x3f];\n    out += i + 1 < len ? B64_ALPHABET[(triple >> 6) & 0x3f] : \"=\";\n    out += i + 2 < len ? B64_ALPHABET[triple & 0x3f] : \"=\";\n  }\n  return out;\n}\n\n/** Build the SVG source for the dual-`feTurbulence` tile; arithmetic `feComposite` multiplies the two layers so dark streak and dark patch reinforce. */\nfunction buildNoiseTileSvg(opts: Required<NoiseTileOptions>): string {\n  const { width, height, seed, streakiness, feathering, dryout } = opts;\n\n  // Decorrelate the two layers' seeds; `% 256` keeps them in feTurbulence's documented range.\n  const striationSeed = hashU32(seed * 2 + 3) % 256;\n  const patchSeed = hashU32(seed * 2 + 7) % 256;\n\n  // Floor bottoms out at ~0.22 at streakiness 1 so dark lanes go deep without punching a hole under the tip caps.\n  const striationSlope = STRIATION_ALPHA_SLOPE * (0.5 + 6 * streakiness);\n  const striationMin = STRIATION_ALPHA_MIN - 0.6 * streakiness;\n\n  // Dryout steepens the patch layer and lowers its floor so more area falls below the cut threshold.\n  const patchSlope = PATCH_ALPHA_SLOPE * (0.5 + feathering) + 0.6 * dryout;\n  const patchMin = PATCH_ALPHA_MIN - 0.62 * dryout;\n\n  const dryoutCut = dryoutTransfer(dryout);\n\n  return (\n    `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\">` +\n    `<defs>` +\n    `<filter id=\"g\" x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" color-interpolation-filters=\"sRGB\">` +\n    `<feTurbulence type=\"fractalNoise\" baseFrequency=\"${STRIATION_FREQUENCY}\" numOctaves=\"${STRIATION_OCTAVES}\" stitchTiles=\"stitch\" seed=\"${striationSeed}\" result=\"s\"/>` +\n    `<feColorMatrix in=\"s\" values=\"0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 ${fmt(striationSlope)} ${fmt(striationMin)}\" result=\"sa\"/>` +\n    `<feTurbulence type=\"fractalNoise\" baseFrequency=\"${PATCH_FREQUENCY}\" numOctaves=\"${PATCH_OCTAVES}\" stitchTiles=\"stitch\" seed=\"${patchSeed}\" result=\"p\"/>` +\n    `<feColorMatrix in=\"p\" values=\"0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 ${fmt(patchSlope)} ${fmt(patchMin)}\" result=\"pa\"/>` +\n    `<feComposite in=\"sa\" in2=\"pa\" operator=\"arithmetic\" k1=\"1\" k2=\"0\" k3=\"0\" k4=\"0\" result=\"ink\"/>` +\n    `<feComponentTransfer in=\"ink\">` +\n    `<feFuncA type=\"discrete\" tableValues=\"${dryoutCut}\"/>` +\n    `</feComponentTransfer>` +\n    `</filter>` +\n    `</defs>` +\n    `<rect width=\"${width}\" height=\"${height}\" fill=\"black\" filter=\"url(#g)\"/>` +\n    `</svg>`\n  );\n}\n\n/** Build the `tableValues` for the dryout alpha threshold: `\"1\"` at `dryout = 0`, leading entries flipping to `0` (transparent) up to ~45% of the range. */\nfunction dryoutTransfer(dryout: number): string {\n  if (dryout <= 0) return \"1\";\n  const segments = 16;\n  const cut = Math.round(dryout * 0.45 * segments);\n  const values: string[] = [];\n  for (let i = 0; i < segments; i++) values.push(i < cut ? \"0\" : \"1\");\n  return values.join(\" \");\n}\n\n/** Build a bare `data:image/svg+xml;base64,…` URL for the noise tile. Deterministic from `seed` and the density knobs (quantized to 0.02 buckets). Memoised, so a knob drag rebuilds at most one tile per bucket. */\nconst tileCache = new Map<string, string>();\n\nexport function buildNoiseTileDataUrl(opts: NoiseTileOptions): string {\n  // Quantize before both the key and the SVG, so cache identity and emitted tile always agree.\n  const resolved: Required<NoiseTileOptions> = {\n    width: opts.width ?? DEFAULT_TILE_WIDTH,\n    height: opts.height ?? DEFAULT_TILE_HEIGHT,\n    seed: opts.seed,\n    streakiness: quantizeKnob(opts.streakiness),\n    feathering: quantizeKnob(opts.feathering),\n    dryout: quantizeKnob(opts.dryout ?? 0),\n  };\n  const key = `${resolved.width}x${resolved.height}|${resolved.seed}|${resolved.streakiness}|${resolved.feathering}|${resolved.dryout}`;\n  const hit = tileCache.get(key);\n  if (hit !== undefined) {\n    // Re-promote so the oldest-half eviction approximates LRU; a drag can't evict its own hot tiles.\n    tileCache.delete(key);\n    tileCache.set(key, hit);\n    return hit;\n  }\n  const url = `data:image/svg+xml;base64,${toBase64Ascii(buildNoiseTileSvg(resolved))}`;\n  // Evict the oldest half rather than clearing all, so a continuous drag keeps its hot working set.\n  if (tileCache.size > 512) {\n    const half = Math.floor(tileCache.size / 2);\n    const iter = tileCache.keys();\n    for (let i = 0; i < half; i++) tileCache.delete(iter.next().value!);\n  }\n  tileCache.set(key, url);\n  return url;\n}\n\n/** Wrap {@link buildNoiseTileDataUrl} plus the fixed px dimensions (the renderer's `mask-size`, repeated never scaled) into a {@link NoiseTile}. */\nexport function buildNoiseTile(opts: NoiseTileOptions): NoiseTile {\n  const width = opts.width ?? DEFAULT_TILE_WIDTH;\n  const height = opts.height ?? DEFAULT_TILE_HEIGHT;\n  return {\n    dataUrl: buildNoiseTileDataUrl({ ...opts, width, height }),\n    width,\n    height,\n  };\n}\n","import { clamp } from \"../internal/math.js\";\nimport type { ColorValue, GradientStop, PoolGradient } from \"../types.js\";\n\n/**\n * The absolute-px end-pool gradient: end darkening is a fixed px width, not a stroke percentage.\n * The `min`/`max` clamps stop the two pools overrunning each other on a short mark, so long and\n * short marks share the same pool width. `startEndBuildup` scales pool darkness bidirectionally\n * (positive darker ends, negative lighter ends). The renderer turns the {@link PoolGradient} into CSS.\n */\n\nconst DEFAULT_ANGLE = 85;\n\nconst START_INSET_PX = 2;\nconst CORE_PX = 10;\nconst START_CORE_PCT = 40;\nconst END_CORE_PCT = 60;\nconst END_INSET_PX = 2;\n\nexport interface PoolOptions {\n  /** The band length in px (stroke direction). Drives the px↔% clamps. */\n  lengthPx: number;\n  /** Bidirectional end deposit, `-1`-`1`: positive darker ends, negative lighter ends. */\n  startEndBuildup: number;\n  color: ColorValue;\n  /** Base ink alpha at the flat core, `0`-`1`. */\n  opacity: number;\n  /** Gradient angle in degrees; defaults to `85`. */\n  angle?: number;\n  /** Directional dry-out, `0`-`1`: scales every stop's alpha down toward the end. Defaults to `0`. */\n  flowFade?: number;\n  /** Reverse the dry-out direction for a right-to-left live selection. No effect when `flowFade` is `0`. */\n  flowReversed?: boolean;\n  /** Live-speed only. Number of interior core stops (>=2). Absent yields the 4-stop gradient. */\n  coreStopCount?: number;\n  /** Live-speed only. Deposit multiplier in `[minDeposit, 1]` at core fraction `f`: `1` where slow, lower where fast. */\n  depositAt?: (fraction: number) => number;\n  /** Live-speed only. Extra end-pool build-up from deceleration, added to {@link startEndBuildup} for the end stops. */\n  decelBuildup?: number;\n}\n\nfunction roundAlpha(value: number): number {\n  return Math.round(clamp(value, 0, 1) * 1000) / 1000;\n}\n\n/** Build the absolute-px end-pool gradient. `startEndBuildup` lifts/drops the end stops, then `flowFade` tilts the whole ramp toward the drying end. */\nexport function buildPoolGradient(opts: PoolOptions): PoolGradient {\n  const buildup = clamp(opts.startEndBuildup, -1, 1);\n  const base = clamp(opts.opacity, 0, 1);\n  const fade = clamp(opts.flowFade ?? 0, 0, 1);\n\n  // 0.7 pool-contrast gain: +1 makes the ends 1.7x the core alpha, -1 drops them to 0.3x.\n  const endBase = base * (1 + 0.7 * buildup);\n  const coreBase = base;\n  const dryAt = (offset: number): number =>\n    1 - fade * (opts.flowReversed ? 1 - offset : offset);\n\n  const color: ColorValue = opts.color;\n\n  const meta = {\n    angle: opts.angle ?? DEFAULT_ANGLE,\n    startInsetPx: START_INSET_PX,\n    startCorePx: CORE_PX,\n    startCorePct: START_CORE_PCT,\n    endCorePx: CORE_PX,\n    endCorePct: END_CORE_PCT,\n    endInsetPx: END_INSET_PX,\n  };\n\n  // 4-stop gradient whenever no live speed profile is present, so static geometry stays byte-identical.\n  if (opts.coreStopCount == null && opts.depositAt == null && opts.decelBuildup == null) {\n    return {\n      ...meta,\n      stops: [\n        { offset: 0, color, opacity: roundAlpha(endBase * dryAt(0)) },\n        { offset: 0.4, color, opacity: roundAlpha(coreBase * dryAt(0.4)) },\n        { offset: 0.6, color, opacity: roundAlpha(coreBase * dryAt(0.6)) },\n        { offset: 1, color, opacity: roundAlpha(endBase * dryAt(1)) },\n      ],\n    };\n  }\n\n  // Live speed path: N core stops carrying the per-x deposit profile.\n  const n = Math.max(2, Math.round(opts.coreStopCount ?? 12));\n  const deposit = opts.depositAt ?? ((): number => 1);\n  const endBuildup = clamp(buildup + (opts.decelBuildup ?? 0), -1, 1);\n  const endAlphaBase = base * (1 + 0.7 * endBuildup);\n\n  // Core stops sit between the two px-clamped pool insets, positions resolved to px so the renderer needs no nested calc/min/max.\n  const len = Math.max(1, opts.lengthPx);\n  const startCorePos = Math.min(CORE_PX, (START_CORE_PCT / 100) * len);\n  const endCorePos = Math.max(len - CORE_PX, (END_CORE_PCT / 100) * len);\n  const span = Math.max(0, endCorePos - startCorePos);\n\n  const stops: GradientStop[] = [];\n  const coreStopsPositionsPx: number[] = [];\n\n  // The renderer normalizes per-stop alpha to the brightest stop, so a uniformly-fast line would\n  // normalize back to full. Tracking peak alpha with and without the speed factor lets `layerScale`\n  // carry the absolute dimming into layer opacity, leaving the stops to express only relative shape.\n  const leadBaked = endAlphaBase * dryAt(0) * deposit(0);\n  const trailBaked = endAlphaBase * dryAt(1) * deposit(1);\n  let maxBaked = Math.max(leadBaked, trailBaked);\n  let maxBare = Math.max(endBase * dryAt(0), endBase * dryAt(1));\n\n  stops.push({ offset: 0, color, opacity: roundAlpha(leadBaked) });\n  for (let i = 0; i < n; i++) {\n    const f = n === 1 ? 0.5 : i / (n - 1);\n    const baked = coreBase * dryAt(f) * deposit(f);\n    const bare = coreBase * dryAt(f);\n    if (baked > maxBaked) maxBaked = baked;\n    if (bare > maxBare) maxBare = bare;\n    stops.push({ offset: f, color, opacity: roundAlpha(baked) });\n    coreStopsPositionsPx.push(startCorePos + f * span);\n  }\n  stops.push({ offset: 1, color, opacity: roundAlpha(trailBaked) });\n\n  const layerScale = maxBare > 0 ? clamp(maxBaked / maxBare, 0, 1) : 1;\n\n  return {\n    ...meta,\n    stops,\n    coreStopCount: n,\n    coreStopsPositionsPx,\n    layerScale,\n  };\n}\n","import type {\n  Box,\n  EdgeVertex,\n  LineRect,\n  LineSpeedProfile,\n  MarkGeometry,\n  MaskOffset,\n  NoiseTile,\n  ResolvedOptions,\n} from \"../types.js\";\nimport { clamp } from \"../internal/math.js\";\nimport { buildClipPath, chiselSlant, minVisibleFront } from \"./clip-path.js\";\nimport { buildEdge } from \"./edges.js\";\nimport { buildNoiseTile } from \"./noise-tile.js\";\nimport { buildPoolGradient } from \"./pool.js\";\nimport { hashJitter } from \"./rng.js\";\n\n/**\n * From one line rect + resolved options + seed, produce the complete absolute-px\n * {@link MarkGeometry} for a single visual line. Everything is anchored to absolute px\n * and seeded by the stable line seed, so identical inputs yield byte-identical output\n * and growing the mark only appends grid vertices. No DOM access (SSR-safe).\n */\n\n/** Vertical padding above/below the text so the wavy edge stays clear of glyphs. */\nconst VERT_PAD = 2;\n\n/** Decorrelating seed offsets, one per derived role. */\nconst SEED_LEFT_OVER = 0;\nconst SEED_RIGHT_OVER = 11;\nconst SEED_TOP_EDGE = 200;\nconst SEED_BOTTOM_EDGE = 300;\nconst SEED_ANGLE = 400;\n\nconst MASK_X_MULT = 37;\nconst MASK_Y_MULT = 13;\n// Fixed width-invariant noise tile, wide enough that the mask-repeat seam clears any normal band.\n// WebKit draws a hairline at mask tile edges; a smaller tile lands that seam inside the band.\nconst NOISE_TILE_W = 1024;\nconst NOISE_TILE_H = 64;\nconst MASK_OFFSET_X = 96;\nconst MASK_OFFSET_Y = 16;\n\n/** A non-negative modulo (JS `%` keeps the sign of the dividend). */\nfunction mod(n: number, m: number): number {\n  return ((n % m) + m) % m;\n}\n\n/** Resolve the band's vertical offset and height (px) for the mark type. Every mark is one band primitive at a different position/height. */\nfunction resolveBand(\n  markType: ResolvedOptions[\"markType\"],\n  lineHeight: number,\n): { offsetY: number; height: number } {\n  const thin = Math.max(2, lineHeight * 0.12);\n  switch (markType) {\n    case \"underline\":\n      return { offsetY: lineHeight - thin, height: thin };\n    case \"overline\":\n      return { offsetY: 0, height: thin };\n    case \"strike-through\":\n      return { offsetY: (lineHeight - thin) / 2, height: thin };\n    default:\n      return { offsetY: 0, height: lineHeight };\n  }\n}\n\n/**\n * Build the full geometry for one visual line.\n *\n * @param seed - The stable seed every value derives from.\n * @param flowReversed - Pour the dry-out gradient from the right (backward live selection).\n * @param speedProfile - Live-only per-line swipe-speed read; absent leaves byte-identical geometry.\n */\nexport function buildMarkGeometry(\n  lineRect: LineRect,\n  options: ResolvedOptions,\n  seed: number,\n  flowReversed = false,\n  speedProfile?: LineSpeedProfile,\n): MarkGeometry {\n  const { edge, ink, tip, paper, speed } = options;\n\n  // How far each end runs past (or short of) the text edge, jittered per end and seeded by the width-independent line seed so growing a mark never shifts its start.\n  const endOver = (s: number): number =>\n    tip.overshoot + hashJitter(s) * tip.overshootJitter;\n  const leftExt = endOver(seed + SEED_LEFT_OVER);\n  const rightExt = endOver(seed + SEED_RIGHT_OVER);\n\n  // Per-line slant jitter drives both the clip-path and the slant/minFront fields, so painted shape and draw-on front agree.\n  const slantTip =\n    tip.angleJitter > 0\n      ? { ...tip, angle: tip.angle + hashJitter(seed + SEED_ANGLE) * tip.angleJitter }\n      : tip;\n\n  const band = resolveBand(options.markType, lineRect.height);\n  const boxX = lineRect.left - leftExt;\n  const boxY = lineRect.top - VERT_PAD + band.offsetY;\n  // Positive-width clamp so an aggressive negative overshoot can't invert the box.\n  const boxWidth = Math.max(1, lineRect.width + leftExt + rightExt);\n  const boxHeight = band.height + VERT_PAD * 2;\n  const box: Box = { x: boxX, y: boxY, width: boxWidth, height: boxHeight };\n\n  // Amplitude grows with paper absorbency (wetter, fuzzier edge); wavelength is the width-independent px frequency.\n  const amplitude = edge.waviness * (1 + paper.absorbency * 0.5);\n  const segmentLength = edge.frequency;\n\n  // Absolute-x grid so growing the extent appends fresh vertices; shifted to local box coords below.\n  const absStartX = box.x;\n  const absEndX = box.x + box.width;\n\n  const topAbs = buildEdge({\n    startX: absStartX,\n    endX: absEndX,\n    baseY: 0,\n    segmentLength,\n    amplitude,\n    roughness: edge.roughness,\n    seed: seed + SEED_TOP_EDGE,\n  });\n  const bottomAbs = buildEdge({\n    startX: absStartX,\n    endX: absEndX,\n    baseY: box.height,\n    segmentLength,\n    amplitude,\n    roughness: edge.roughness,\n    seed: seed + SEED_BOTTOM_EDGE,\n  });\n\n  // Grid index preserved so prefix-stability tests can compare per-index vertices.\n  const toLocal = (v: EdgeVertex): EdgeVertex => ({\n    x: v.x - box.x,\n    y: v.y,\n    gridIndex: v.gridIndex,\n  });\n  const topEdge: EdgeVertex[] = topAbs.map(toLocal);\n  const bottomEdge: EdgeVertex[] = bottomAbs.map(toLocal);\n\n  // Clip-path parameterized by an advancing front; the entrance grows the front so the band gains grid nodes rather than stretching.\n  const clipAtFront = (front: number): string =>\n    buildClipPath({\n      box,\n      tip: slantTip,\n      topEdge,\n      bottomEdge,\n      cap: edge.cap,\n      radius: edge.radius,\n      front,\n    });\n  const clipPath = clipAtFront(box.width);\n\n  // Per-line noise tile (never scaled). With a live profile a faster mean swipe lays a drier, streakier line; without one the three values reduce to the base expressions.\n  const sp = speedProfile;\n  const m = sp ? sp.meanNorm : 0;\n  const sens = speed.sensitivity;\n  const baseDryout = ink.dryout + ink.viscosity * 0.2;\n  const baseStreak = ink.streakiness;\n  const baseFeather = ink.feathering + paper.absorbency * 0.25;\n  const noiseTile: NoiseTile = buildNoiseTile({\n    width: NOISE_TILE_W,\n    height: NOISE_TILE_H,\n    seed,\n    streakiness: sp\n      ? clamp(baseStreak + speed.streakBoost * sens * m * Math.max(0, 1 - baseStreak), 0, 1)\n      : baseStreak,\n    feathering: sp ? Math.max(0, baseFeather * (1 - speed.featherReduce * sens * m)) : baseFeather,\n    dryout: sp\n      ? clamp(baseDryout + speed.dryoutBoost * sens * m * Math.max(0, 1 - baseDryout), 0, 1)\n      : baseDryout,\n  });\n  // Offsets capped to the headroom so the wrap stays past the band's right/bottom edge.\n  const maskOffset: MaskOffset = {\n    x: -mod(seed * MASK_X_MULT, MASK_OFFSET_X),\n    y: -mod(seed * MASK_Y_MULT, MASK_OFFSET_Y),\n  };\n\n  const pool = buildPoolGradient({\n    lengthPx: box.width,\n    startEndBuildup: ink.startEndBuildup,\n    color: options.color,\n    opacity: options.opacity,\n    angle: options.gradient?.angle ?? undefined,\n    flowFade: ink.flowFade,\n    flowReversed,\n    // Live speed path: per-x deposit across N stops + extra end pooling from deceleration. Omitted without a profile.\n    ...(sp\n      ? {\n          coreStopCount: speed.resolution,\n          depositAt: sp.depositAt,\n          decelBuildup: speed.poolBoost * sens * sp.decel,\n        }\n      : {}),\n  });\n\n  return {\n    box,\n    seed,\n    clipPath,\n    clipAtFront,\n    slant: chiselSlant(slantTip, box.width, box.height),\n    minFront: Math.min(minVisibleFront(slantTip, edge.cap, box.width, box.height, edge.radius), box.width),\n    topEdge,\n    bottomEdge,\n    noiseTile,\n    maskOffset,\n    pool,\n  };\n}\n"],"mappings":"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","names":[]}