# @zakkster/lite-gradient
> Zero-GC OKLCH gradient generator. N-stop perceptually uniform gradients.

## Install
npm i @zakkster/lite-gradient

## Import
import { Gradient, monochromeGradient, gradientMonoWarm, gradientSunset } from '@zakkster/lite-gradient';

## Key Facts
- N-stop OKLCH gradients (perceptually uniform, no gray dead zones)
- at(t, out): caller-owned output object (zero-GC)
- css(t): CSS oklch() string (allocates - use for setup)
- palette(count): array of CSS color strings
- sampleArray(out, count): fill Float32Array LUT (zero-GC)
- toLinear(ctx, x0,y0,x1,y1): Canvas2D linear gradient
- toRadial(ctx, cx,cy,r): Canvas2D radial gradient
- toCssLinear(angle?), toCssRadial(): CSS gradient strings
- 7 presets: Sunset, Ocean, Fire, Neon, Grey, MonoWarm, MonoCool
- Dependencies: @zakkster/lite-color, @zakkster/lite-lerp

## Monochrome (v1.1.0)
monochromeGradient(base, { mode?, range?, stops? }) -> Gradient
  Tone-on-tone gradient from a single base OKLCH color. Chroma and hue
  held constant; L varies across `range`. mode='tinted' (default) retains
  base c/h; mode='grayscale' forces c=0. range=[lo,hi] with 0<=lo<hi<=1
  (default [0,1]). stops=integer>=2 (default 2 endpoints; smooth OKLCH
  interpolation makes higher counts visually identical for continuous
  rendering; use 11 or 12 for anchored export stop placement).

  Pairs with @zakkster/lite-hueforge's monochromeScale(base, opts) which
  returns discrete Radix-style step arrays; monochromeGradient returns a
  continuous Gradient ready for canvas/CSS emission.

Presets:
  gradientMonoWarm - warm sepia (photography/editorial classic)
  gradientMonoCool - cool blue-grey (client-safe neutral)

## Usage
const g = new Gradient([
    { l: 0.3, c: 0.15, h: 270 },
    { l: 0.9, c: 0.12, h: 60 },
]);
const out = { l: 0, c: 0, h: 0 };
g.at(0.5, out); // zero-GC sampling
const css = g.css(0.5); // setup only

// Monochrome from a brand color
const brand = monochromeGradient({ l: 0.5, c: 0.08, h: 245 });
ctx.fillStyle = brand.toLinear(ctx, 0, 0, 800, 600);
ctx.fillRect(0, 0, 800, 600);

// v1.2.0 — closed / cyclic gradients
const wheel = new Gradient([
    { l: 0.65, c: 0.18, h: 0 },
    { l: 0.65, c: 0.18, h: 120 },
    { l: 0.65, c: 0.18, h: 240 },
], { closed: true });
// Default spacing: i/n (period), not i/(n-1) — wrap segment covers [(n-1)/n, 1]
// at(t): t = t - Math.floor(t) replaces the clamp; raw accumulating phase works directly
wheel.at(1.5, out); // same as at(0.5)
wheel.at(-0.25, out); // same as at(0.75)
// palette(n) / sampleArray(out, n): period spacing i/n (no duplicated endpoint)
// toCssLinear / toCssRadial: append first color again at 100% so CSS output closes visually
// toLinear / toRadial: same treatment for CanvasGradient
// Instance carries `readonly closed: boolean` for downstream consumers to branch on.
// Open-mode paths byte-identical to v1.1.0; no cost when opts absent.
