{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "soonish",
  "title": "soonish",
  "description": "The soonish loader engine: <Loader name=\"…\" /> with all 91 5×5 pixel loaders.",
  "files": [
    {
      "path": "packages/soonish/src/index.ts",
      "content": "export { Loader } from './Loader';\nexport { GRID, CELLS } from './types';\nexport { getMatrix5Layout } from './layout';\nexport type { MatrixLayout } from './layout';\nexport type { LoaderProps, LoaderName } from './types';\n",
      "type": "registry:file",
      "target": "components/soonish/index.ts"
    },
    {
      "path": "packages/soonish/src/Loader.tsx",
      "content": "import React, { type CSSProperties } from 'react';\nimport { LOADER_REGISTRY } from './data/loaders';\n// Static stylesheets. The bundler extracts these into a real <link>, so styles\n// land before first paint and no JS is needed to apply them. This is also what\n// keeps Loader free of hooks — and therefore usable as a Server Component.\nimport './styles/base.css';\nimport './styles/fields.generated.css';\nimport { GRID, CELLS } from './types';\nimport { getMatrix5Layout } from './layout';\nimport type { LoaderProps, ProceduralDef, CompiledDef } from './types';\n\nconst DEFAULT_ANIMATION_COLORS: Record<string, string> = {\n  'pg-rain-fade': 'oklch(0.898 0.047 175.965)',\n  'pg-think': 'oklch(0.864 0.05 276.62)',\n  'pg-search': 'oklch(0.897 0.043 181.815)',\n  'pg-find': 'oklch(0.909 0.061 125.704)',\n  'pg-consolidate': 'oklch(0.871 0.059 72.741)',\n  'pg-stream': 'oklch(0.898 0.047 175.965)',\n  'pg-reason': 'oklch(0.866 0.043 5.208)',\n  'pg-index': 'oklch(0.918 0.061 98.216)',\n  'pg-connect': 'oklch(0.897 0.043 181.815)',\n  'pg-generate': 'oklch(0.862 0.05 334.509)',\n  'pg-reflect': 'oklch(0.864 0.05 276.62)',\n  'pg-mandala-round': 'oklch(0.897 0.043 181.815)',\n  'pg-mandala-square': 'oklch(0.862 0.05 334.509)',\n  'pg-mandala-diamond': 'oklch(0.864 0.05 276.62)',\n  'pg-mandala-cross': 'oklch(0.918 0.061 98.216)',\n  'pg-mandala-x': 'oklch(0.866 0.043 5.208)',\n  'pg-mandala-star': 'oklch(0.871 0.059 72.741)',\n  'pg-mandala-petal': 'oklch(0.866 0.043 5.208)',\n  'pg-mandala-snow': 'oklch(0.897 0.043 181.815)',\n  'pg-mandala-gear': 'oklch(0.898 0.047 175.965)',\n  'pg-mandala-kaleido': 'oklch(0.897 0.043 181.815)',\n  'pg-mandala-spiral': 'oklch(0.864 0.05 276.62)',\n  'pg-mandala-pulse': 'oklch(0.871 0.059 72.741)',\n  'pg-mandala-checker': 'oklch(0.898 0.047 175.965)',\n  'pg-mandala-oct': 'oklch(0.918 0.061 98.216)',\n  'pg-mandala-lotus': 'oklch(0.866 0.043 5.208)',\n  'pg-scale-cyan': 'oklch(0.897 0.043 181.815)',\n  'pg-scale-magenta': 'oklch(0.862 0.05 334.509)',\n  'pg-scale-violet': 'oklch(0.864 0.05 276.62)',\n  'pg-scale-amber': 'oklch(0.871 0.059 72.741)',\n  'pg-scale-mint': 'oklch(0.898 0.047 175.965)',\n  'pg-scale-pink': 'oklch(0.866 0.043 5.208)',\n  'pg-twist': 'oklch(0.897 0.043 181.815)',\n  'pg-squash': 'oklch(0.862 0.05 334.509)',\n  'pg-jelly': 'oklch(0.864 0.05 276.62)',\n  'pg-pop-rotate': 'oklch(0.871 0.059 72.741)',\n  'pg-skew': 'oklch(0.898 0.047 175.965)',\n  'pg-heartbeat': 'oklch(0.866 0.043 5.208)',\n  'pg-drop': 'oklch(0.897 0.043 181.815)',\n  'pg-burst': 'oklch(0.918 0.061 98.216)',\n  'pg-spiral': 'oklch(0.864 0.05 276.62)',\n  'pg-zigzag': 'oklch(0.862 0.05 334.509)',\n  'pg-pulse-cyan': 'oklch(0.897 0.043 181.815)',\n  'pg-pulse-magenta': 'oklch(0.862 0.05 334.509)',\n  'pg-pulse-violet': 'oklch(0.864 0.05 276.62)',\n  'pg-pulse-mint': 'oklch(0.898 0.047 175.965)',\n  'pg-pulse-pink': 'oklch(0.866 0.043 5.208)',\n  'pg-pulse-lime': 'oklch(0.909 0.061 125.704)',\n  'pg-pulse-amber': 'oklch(0.871 0.059 72.741)',\n  'pg-pulse-yellow': 'oklch(0.918 0.061 98.216)',\n  'pg-plasma': 'oklch(0.897 0.043 181.815)',\n};\n\n// ---- Procedural loader ----\nfunction ProceduralLoader({\n  def, size, dotSize, cellPadding, speed, color, className, style,\n}: { def: ProceduralDef; size: number; dotSize: number; cellPadding?: number; speed: number; color?: string; className?: string; style?: CSSProperties }) {\n  const duration = def.duration / speed;\n  const { gap, matrixSpan } = getMatrix5Layout(size, dotSize, cellPadding);\n\n  const colorVars: Record<string, string> = {};\n  colorVars['--loader-color'] = color ?? DEFAULT_ANIMATION_COLORS[def.animName] ?? 'oklch(0.897 0.043 181.815)';\n\n  return (\n    <div\n      className={`hl-matrix${def.placeholder ? ' hl-ph' : ''}${className ? ` ${className}` : ''}`}\n      style={{\n        display: 'grid',\n        gridTemplateColumns: `repeat(${GRID}, ${dotSize}px)`,\n        gridTemplateRows: `repeat(${GRID}, ${dotSize}px)`,\n        gap: `${gap}px`,\n        // Span-driven mode leaves a few px of slack when the gap rounds down;\n        // centring the tracks keeps the matrix optically centred in its box.\n        width: `${matrixSpan}px`,\n        height: `${matrixSpan}px`,\n        justifyContent: 'center',\n        alignContent: 'center',\n        ...colorVars,\n        ...style,\n      } as CSSProperties}\n    >\n      {Array.from({ length: CELLS }, (_, i) => {\n        const x = i % GRID;\n        const y = Math.floor(i / GRID);\n        const delay = def.delay(x, y, i);\n        const skip = def.skipOnNine && delay === 999;\n\n        return (\n          <div key={i} className=\"hl-cell\">\n            <div\n              className=\"hl-px\"\n              style={\n                skip\n                  ? // A masked pixel never lights. Where a placeholder is drawn\n                    // it supplies the dim square, so the dot itself is hidden to\n                    // avoid stacking two dim layers; otherwise the dot keeps its\n                    // own resting look so the grid still reads as 25 lamps.\n                    def.placeholder\n                    ? { animation: 'none', opacity: 0 }\n                    : { animation: 'none' }\n                  : {\n                      animationName: def.animName,\n                      animationDuration: `${duration}s`,\n                      animationDelay: `${delay}s`,\n                      animationTimingFunction: def.easing,\n                      animationIterationCount: 'infinite',\n                      animationFillMode: 'none',\n                    }\n              }\n            />\n          </div>\n        );\n      })}\n    </div>\n  );\n}\n\n// ---- Compiled-field loader ----\n// Each dot points at a pre-sampled `linear()` easing curve. Dots may reference\n// *differently shaped* curves, which is what a shared keyframe plus delay can't\n// express. Still one CSS animation per dot, still entirely on the compositor.\nfunction CompiledLoader({\n  def, size, dotSize, cellPadding, speed, color, className, style,\n}: { def: CompiledDef; size: number; dotSize: number; cellPadding?: number; speed: number; color?: string; className?: string; style?: CSSProperties }) {\n  const { gap, matrixSpan } = getMatrix5Layout(size, dotSize, cellPadding);\n  const duration = def.duration / speed;\n\n  return (\n    <div\n      // Always placeholdered. Compiled curves are driven by `hl-field`, which\n      // ramps from opacity 0, so a dot at the bottom of its curve is fully\n      // invisible — unlike procedural keyframes, which floor at 0.16. Without a\n      // placeholder the grid vanishes wherever the field is dark.\n      className={`hl-matrix hl-ph hl-f-${def.slug}${className ? ` ${className}` : ''}`}\n      style={{\n        display: 'grid',\n        gridTemplateColumns: `repeat(${GRID}, ${dotSize}px)`,\n        gridTemplateRows: `repeat(${GRID}, ${dotSize}px)`,\n        gap: `${gap}px`,\n        width: `${matrixSpan}px`,\n        height: `${matrixSpan}px`,\n        justifyContent: 'center',\n        alignContent: 'center',\n        '--loader-color': color ?? 'oklch(0.897 0.043 181.815)',\n        ...style,\n      } as CSSProperties}\n    >\n      {def.dots.map((d, i) => {\n        // hl-d* carries opacity+scale in a single rule (two animation-name\n        // declarations on one element would override rather than combine).\n        // hl-g* is separate because it targets ::after.\n        const cls = ['hl-px', `hl-d${d.d}`];\n        if (d.g >= 0) cls.push(`hl-g${d.g}`);\n        return (\n          <div key={i} className=\"hl-cell\">\n            <div\n              className={cls.join(' ')}\n              style={{\n                // Custom properties rather than animation-* directly: the glow\n                // channel animates ::after, which inline styles cannot target,\n                // but which inherits these.\n                '--hl-dur': `${duration}s`,\n                // Negative delay seeks into the loop, so a phase-shifted curve\n                // reuses a sibling's easing instead of needing its own.\n                '--hl-delay': `${(-d.offset * duration).toFixed(3)}s`,\n              } as CSSProperties}\n            />\n          </div>\n        );\n      })}\n    </div>\n  );\n}\n\n// ---- Main export ----\nexport function Loader({\n  name,\n  color,\n  size = 24,\n  dotSize = 3,\n  cellPadding,\n  speed = 1,\n  className,\n  style,\n}: LoaderProps) {\n  const slug = name.toLowerCase();\n  const def = LOADER_REGISTRY[slug];\n  if (!def) return null;\n\n  if (def.kind === 'compiled') {\n    return (\n      <CompiledLoader\n        def={def}\n        size={size}\n        dotSize={dotSize}\n        cellPadding={cellPadding}\n        speed={speed}\n        color={color}\n        className={className}\n        style={style}\n      />\n    );\n  }\n\n  return (\n    <ProceduralLoader\n      def={def}\n      size={size}\n      dotSize={dotSize}\n      cellPadding={cellPadding}\n      speed={speed}\n      color={color}\n      className={className}\n      style={style}\n    />\n  );\n}\n",
      "type": "registry:file",
      "target": "components/soonish/Loader.tsx"
    },
    {
      "path": "packages/soonish/src/types.ts",
      "content": "import type { CSSProperties } from 'react';\n\nexport type LoaderName =\n  // Pulse wave loaders\n  | 'sweep' | 'diagonal' | 'ripple' | 'rain' | 'spiral' | 'snake'\n  | 'sparkle' | 'heartbeat' | 'scanner' | 'orbit' | 'breathe' | 'checker'\n  | 'stripes' | 'falling' | 'plasma' | 'loadbar' | 'knight-tour'\n  | 'vortex' | 'sine-wave' | 'life' | 'quadrants' | 'crossfade' | 'glider'\n  | 'matrix' | 'pong' | 'concentric' | 'twin-spirals'\n  // AI/process loaders\n  | 'thinking' | 'searching' | 'finding' | 'consolidating' | 'streaming'\n  | 'reasoning' | 'indexing' | 'connecting' | 'generating' | 'reflecting'\n  // Scale loaders\n  | 'scale-ripple' | 'scale-wave' | 'scale-diag' | 'scale-pop' | 'scale-ring' | 'scale-check'\n  // Funky scale\n  | 'twist' | 'squash' | 'jelly' | 'pop-rotate' | 'skew'\n  | 'heartbeat-scale' | 'drop' | 'burst' | 'spiral-scale' | 'zigzag'\n  // Mandalas\n  | 'round' | 'square-mandala' | 'diamond-mandala' | 'cross' | 'x-diagonal'\n  | 'star-burst' | 'petal' | 'snowflake' | 'gear' | 'kaleido' | 'spiral-mandala'\n  | 'pulse-square' | 'check-mandala' | 'octagon' | 'lotus'\n  // Geometric masks ported from the dot-matrix pattern set\n  | 'outline' | 'rings' | 'rose'\n  // Compiled brightness fields (engine/) — per-dot curve shapes\n  | 'front-sweep' | 'ripple-echo' | 'pulse-beat' | 'lumen-bloom'\n  | 'raindrop' | 'pendulum' | 'firefly' | 'halo'\n  // New patterns\n  | 'sonar' | 'pinwheel' | 'domino' | 'corners' | 'frame'\n  // Ported from the dot-matrix reference\n  | 'neon-drift' | 'core-spiral' | 'twin-orbit' | 'prism-sweep'\n  | 'flux-columns' | 'echo-ring' | 'origin-wave';\n\n/** Grid is a fixed 5x5 matrix — 25 pixels. */\nexport const GRID = 5;\nexport const CELLS = GRID * GRID;\n\nexport interface LoaderProps {\n  /** Which loader to display */\n  name: LoaderName;\n  /** Override the animation color (hex or any CSS color value) */\n  color?: string;\n  /** Total width/height of the whole matrix in px — not the dot. Default: 24 */\n  size?: number;\n  /** Size of one dot in px. Default: 3 */\n  dotSize?: number;\n  /**\n   * Fix the gap between dots explicitly, in px. When set, `size` is ignored and\n   * the matrix grows to fit. Leave unset to let the gap be derived from `size`,\n   * which keeps the footprint fixed and the dot-to-gap ratio consistent.\n   */\n  cellPadding?: number;\n  /** Animation speed multiplier. 1 = normal, 2 = twice as fast. Default: 1 */\n  speed?: number;\n  /** Extra CSS class on the wrapper */\n  className?: string;\n  /** Extra inline styles on the wrapper */\n  style?: CSSProperties;\n}\n\n// ---- Internal types ----\n\nexport type DelayFn = (x: number, y: number, i: number) => number;\n\nexport interface ProceduralDef {\n  kind: 'procedural';\n  name: string;\n  animName: string;\n  easing: string;\n  duration: number;\n  delay: DelayFn;\n  skipOnNine?: boolean; // pixels with delay 999 get animation:none\n  /** True when the keyframe scales, so the grid needs a static placeholder behind each dot. */\n  placeholder?: boolean;\n}\n\n/**\n * A field compiled ahead of time into per-dot easing curves.\n *\n * Unlike ProceduralDef — where every dot shares one keyframe and only the delay\n * differs — each dot here may reference a differently *shaped* curve, which is\n * what makes position-dependent brightness expressible.\n */\nexport interface CompiledDef {\n  kind: 'compiled';\n  name: string;\n  slug: string;\n  duration: number;\n  /** d = element-level (opacity+scale) rule index, g = glow rule index or -1. */\n  dots: { d: number; g: number; offset: number }[];\n}\n\nexport type LoaderDef = ProceduralDef | CompiledDef;\n",
      "type": "registry:file",
      "target": "components/soonish/types.ts"
    },
    {
      "path": "packages/soonish/src/layout.ts",
      "content": "import { GRID } from './types';\n\nexport interface MatrixLayout {\n  /** Space between adjacent dots, in px. */\n  gap: number;\n  /** Total width/height of the matrix, in px. */\n  matrixSpan: number;\n}\n\n/**\n * Resolve dot spacing for a GRID x GRID matrix.\n *\n * Two modes:\n *\n * 1. **Span-driven (default).** You give a target `size` and a `dotSize`; the gap\n *    absorbs whatever is left over, keeping the dot-to-gap ratio stable across\n *    sizes. `size` is a budget — the returned span is the largest whole-pixel\n *    layout that fits inside it.\n *\n * 2. **Gap-driven.** Pass `cellPadding` to fix the gap explicitly; the span is\n *    then derived from dots + padding and the footprint grows with the dots.\n *\n * Mode 1 is what makes the dots read as separate dots: at `size=24, dotSize=3`\n * the gap works out to 2, so each dot covers ~60% of its cell pitch. Specifying\n * a tiny fixed gap instead pushes coverage toward 90%, where the grid reads as a\n * solid block and per-pixel animation stops being legible.\n */\nexport function getMatrix5Layout(\n  size: number,\n  dotSize: number,\n  cellPadding?: number,\n): MatrixLayout {\n  const n = GRID;\n  if (cellPadding != null) {\n    const gap = Math.max(0, cellPadding);\n    return { gap, matrixSpan: dotSize * n + gap * (n - 1) };\n  }\n  const gap = Math.max(1, Math.floor((size - dotSize * n) / (n - 1)));\n\n  // Span is the exact content width, NOT the requested `size`.\n  //\n  // The gap is floored, so dots + gaps rarely add up to `size` exactly. Padding\n  // the difference out to `size` and centring would split an odd remainder into\n  // half-pixel offsets (40px box, 37px content -> 1.5px a side), landing every\n  // dot on a fractional boundary. The browser then antialiases each one and\n  // rounds them inconsistently, so identical 3px gaps *render* unequal.\n  //\n  // Treating `size` as a budget and reporting the true span keeps every edge on\n  // a whole pixel. Actual span is therefore <= size.\n  return { gap, matrixSpan: dotSize * n + gap * (n - 1) };\n}\n",
      "type": "registry:file",
      "target": "components/soonish/layout.ts"
    },
    {
      "path": "packages/soonish/src/data/loaders.ts",
      "content": "import type { LoaderDef, ProceduralDef, CompiledDef, DelayFn } from '../types';\nimport { FIELD_DOTS } from '../engine/generated';\nimport { GRID } from '../types';\n\nconst N = GRID;                        // 5\nconst C = (N - 1) / 2;                 // 2   — centre coordinate\nconst MAXR = Math.hypot(C, C);         // 2.83 — max radius from centre\nconst MAXMAN = C * 2;                  // 4   — max manhattan from centre\nconst RINGS = Math.floor(N / 2);       // 2   — outermost ring index\nconst TOTAL = N * N;                   // 25\nconst BORDER = (N - 1) * 4;            // 16  — pixels on the perimeter\n\n// ---- Path helpers (computed once) ----\n\nfunction spiralOrder(n: number): number[] {\n  const r: number[] = [];\n  let t = 0, b = n - 1, l = 0, ri = n - 1;\n  while (t <= b && l <= ri) {\n    for (let i = l; i <= ri; i++) r.push(t * n + i); t++;\n    for (let i = t; i <= b; i++) r.push(i * n + ri); ri--;\n    if (t <= b) { for (let i = ri; i >= l; i--) r.push(b * n + i); b--; }\n    if (l <= ri) { for (let i = b; i >= t; i--) r.push(i * n + l); l++; }\n  }\n  return r;\n}\n\n// Warnsdorff's rule. A 5x5 board has an open knight's tour from a corner;\n// the fallback below keeps the sequence complete if the heuristic dead-ends.\nfunction knightTour(n: number): number[] {\n  const mv = [[1,2],[2,1],[2,-1],[1,-2],[-1,-2],[-2,-1],[-2,1],[-1,2]];\n  const vis = Array(n * n).fill(false);\n  const ord: number[] = [];\n  let x = 0, y = 0;\n  for (let s = 0; s < n * n; s++) {\n    ord.push(y * n + x); vis[y * n + x] = true;\n    let best: [number, number] | null = null, bc = 99;\n    for (const [dx, dy] of mv) {\n      const nx = x + dx, ny = y + dy;\n      if (nx < 0 || ny < 0 || nx >= n || ny >= n || vis[ny * n + nx]) continue;\n      let c = 0;\n      for (const [ddx, ddy] of mv) { const ax = nx+ddx, ay = ny+ddy; if (ax>=0&&ay>=0&&ax<n&&ay<n&&!vis[ay*n+ax]) c++; }\n      if (c < bc) { bc = c; best = [nx, ny]; }\n    }\n    if (best) { [x, y] = best; } else { const i = vis.indexOf(false); if (i < 0) break; x = i % n; y = Math.floor(i / n); }\n  }\n  return ord;\n}\n\n// Diagonal bounce. On a 5x5 the path closes after 8 steps, so 16 covers two\n// full sweeps and every cell the ball actually touches.\nfunction pongPath(n: number): number[] {\n  const p: number[] = [];\n  let x = 0, y = 0, dx = 1, dy = 1;\n  for (let i = 0; i < 16; i++) {\n    p.push(y*n+x);\n    if (x+dx<0||x+dx>=n) dx=-dx;\n    if (y+dy<0||y+dy>=n) dy=-dy;\n    x+=dx; y+=dy;\n  }\n  return p;\n}\n\nconst spiralOrd = spiralOrder(N);\nconst knightOrd = knightTour(N);\nconst pongOrd = pongPath(N);\n\n// ---- Geometry helpers ----\nconst chebyshev = (x: number, y: number) => Math.max(Math.abs(x-C), Math.abs(y-C));\nconst manhattan  = (x: number, y: number) => Math.abs(x-C) + Math.abs(y-C);\nconst euclidean  = (x: number, y: number) => Math.hypot(x-C, y-C);\n\n// ---- Path orderings ported from the dot-matrix reference (core/grid-paths.ts) ----\n\n/** Spiral inward from the top-left, clockwise. order[cellIndex] = step. */\nconst SPIRAL_INWARD_ORDER: number[] = (() => {\n  const o = new Array(TOTAL).fill(0);\n  spiralOrd.forEach((cell, step) => { o[cell] = step; });\n  return o;\n})();\n\n/** Anti-diagonals traversed alternately, so the sweep serpentines. */\nconst DIAGONAL_SNAKE_ORDER: number[] = (() => {\n  const o = new Array(TOTAL).fill(0);\n  let t = 0;\n  for (let d = 0; d <= (N - 1) * 2; d++) {\n    const rowStart = Math.max(0, d - (N - 1));\n    const rowEnd = Math.min(N - 1, d);\n    if (d % 2 === 0) {\n      for (let r = rowEnd; r >= rowStart; r--) o[r * N + (d - r)] = t++;\n    } else {\n      for (let r = rowStart; r <= rowEnd; r++) o[r * N + (d - r)] = t++;\n    }\n  }\n  return o;\n})();\n\n/** Perimeter, clockwise from the top-left. -1 for cells off the ring. */\nconst OUTER_RING_ORDER: number[] = (() => {\n  const o = new Array(TOTAL).fill(-1);\n  const coords: [number, number][] = [\n    [0,0],[0,1],[0,2],[0,3],[0,4],[1,4],[2,4],[3,4],\n    [4,4],[4,3],[4,2],[4,1],[4,0],[3,0],[2,0],[1,0],\n  ];\n  coords.forEach(([r, c], t) => { o[r * N + c] = t; });\n  return o;\n})();\n\n/** The 3x3 ring, anti-clockwise. -1 for cells off the ring. */\nconst MIDDLE_RING_ORDER: number[] = (() => {\n  const o = new Array(TOTAL).fill(-1);\n  const coords: [number, number][] = [[1,1],[2,1],[3,1],[3,2],[3,3],[2,3],[1,3],[1,2]];\n  coords.forEach(([r, c], t) => { o[r * N + c] = t; });\n  return o;\n})();\n\n/**\n * The reference applies a POSITIVE animation-delay, which stalls a dot before it\n * first animates. A negative delay of -(1 - f) is equivalent for an infinite\n * loop but starts immediately, so nothing sits dark on mount.\n */\nconst lead = (frac: number, duration: number) => -(1 - (frac % 1)) * duration;\n\n/**\n * The reference runs every CSS-driven loader on one shared 1500ms cycle\n * (`--dmx-cycle`), so they read as a single family. Its per-loader cycleMsBase\n * values (1400-2000ms) belong to the rAF-driven loaders, which we did not port.\n */\nconst DMX = 1.5;\n\n// ---- Builder helpers ----\n\n/**\n * Keyframes that animate `transform`, so their dots shrink out of existence and\n * need a placeholder behind them. Derived once here rather than flagged per\n * loader, so there is a single list to keep in step with the stylesheet.\n */\nconst SCALING_ANIMS = new Set([\n  'pg-scale-cyan', 'pg-scale-magenta', 'pg-scale-violet', 'pg-scale-amber',\n  'pg-scale-mint', 'pg-scale-pink', 'pg-twist', 'pg-squash', 'pg-jelly',\n  'pg-pop-rotate', 'pg-skew', 'pg-drop', 'pg-burst', 'pg-spiral', 'pg-zigzag',\n  'pg-mandala-round', 'pg-mandala-square', 'pg-mandala-diamond', 'pg-mandala-cross',\n  'pg-mandala-x', 'pg-mandala-star', 'pg-mandala-petal', 'pg-mandala-snow',\n  'pg-mandala-gear', 'pg-mandala-kaleido', 'pg-mandala-spiral', 'pg-mandala-pulse',\n  'pg-mandala-checker', 'pg-mandala-oct', 'pg-mandala-lotus',\n  'pg-domino', 'pg-frame',\n]);\n\nfunction proc(name: string, animName: string, easing: string, duration: number, delay: DelayFn, skipOnNine = false): ProceduralDef {\n  return {\n    kind: 'procedural', name, animName, easing, duration, delay, skipOnNine,\n    placeholder: SCALING_ANIMS.has(animName),\n  };\n}\n\n// ---- Registry ----\nconst DEFS: LoaderDef[] = [\n  // Pulse wave loaders\n  proc('Sweep',        'pg-pulse-cyan',     'linear',       1.6, (x)     => -(x/N)*1.6),\n  proc('Diagonal',     'pg-pulse-magenta',  'linear',       1.8, (x,y)   => -((x+y)/(N*2-2))*1.8),\n  proc('Ripple',       'pg-pulse-violet',   'ease-out',     1.6, (x,y)   => -(euclidean(x,y)/MAXR)*1.6),\n  proc('Rain',         'pg-pulse-mint',     'linear',       1.8, (x,y)   => { const c=[0,.4,.8,.2,.6]; return -((y/N)*1.8+c[x]); }),\n  proc('Spiral',       'pg-pulse-pink',     'linear',       2.4, (x,y)   => { const i=spiralOrd.indexOf(y*N+x); return -(i/TOTAL)*2.4; }),\n  proc('Snake',        'pg-pulse-lime',     'linear',       2.4, (x,y)   => { const c=(y%2===0)?x:(N-1-x); return -((y*N+c)/TOTAL)*2.4; }),\n  proc('Sparkle',      'pg-pulse-amber',    'ease-in-out',  1.8, (x,y,i) => { const r=Math.sin(i*9301+49297)*233280; return -((r-Math.floor(r))*1.8); }),\n  proc('Heartbeat',    'pg-heartbeat',      'ease-in-out',  1.2, ()      => 0),\n  proc('Scanner',      'pg-pulse-cyan',     'linear',       1.4, (x,y)   => -(y/N)*1.4),\n  proc('Orbit',        'pg-pulse-yellow',   'linear',       3.2, (x,y)   => {\n    const iB=(x===0||x===N-1||y===0||y===N-1);\n    if(!iB) return 999;\n    let i: number;\n    if(y===0) i=x; else if(x===N-1) i=(N-1)+y; else if(y===N-1) i=(N-1)*2+(N-1-x); else i=(N-1)*3+(N-1-y);\n    return -(i/BORDER)*3.2;\n  }, true),\n  proc('Breathe',      'pg-pulse-violet',   'ease-in-out',  2.4, (x,y)   => -(Math.min(x,y,N-1-x,N-1-y)/RINGS)*1.2),\n  proc('Checker',      'pg-pulse-mint',     'ease-in-out',  1.4, (x,y)   => ((x+y)%2===0)?0:-0.7),\n  proc('Stripes',      'pg-pulse-pink',     'linear',       1.6, (x,y)   => -(((x+y)%3)/3)*1.6),\n  proc('Falling',      'pg-pulse-cyan',     'linear',       1.6, (x,y)   => -(y/N)*0.8-(x*0.08)),\n  proc('Plasma',       'pg-plasma',         'ease-in-out',  3.0, (x,y)   => -((x+y)/(N*2-2))*3),\n  proc('LoadBar',      'pg-pulse-lime',     'linear',       2.8, (x,y)   => -((y*N+x)/TOTAL)*2.8),\n  proc('Knight Tour',  'pg-pulse-cyan',     'linear',       4.0, (x,y)   => { const i=knightOrd.indexOf(y*N+x); return i>=0?-(i/TOTAL)*4:0; }),\n  proc('Vortex',       'pg-pulse-magenta',  'linear',       2.4, (x,y)   => { const a=Math.atan2(y-C,x-C); const r=euclidean(x,y)/MAXR; return -((((a+Math.PI)/(2*Math.PI))+r*.5)%1)*2.4; }),\n  proc('Sine Wave',    'pg-pulse-mint',     'linear',       2.0, (x,y)   => { const w=(Math.sin((x/N)*Math.PI*2)*.5+.5)*(N-1); return -((x/N)-(Math.abs(y-w)/N)*.3)*2; }),\n  proc('Life',         'pg-pulse-amber',    'ease-in-out',  2.0, (x,y)   => -(((x*3+y*5)%5)/5)*2),\n  proc('Quadrants',    'pg-pulse-pink',     'ease-in-out',  1.6, (x,y)   => -(((x<C?0:1)+(y<C?0:2))/4)*1.6),\n  proc('Crossfade',    'pg-pulse-cyan',     'linear',       1.8, (x,y)   => -(Math.min(Math.abs(x-y),Math.abs(x-(N-1-y)))/N)*1.8),\n  proc('Glider',       'pg-pulse-lime',     'steps(4,end)', 1.8, (x,y)   => -(((x+y)%4)/4)*1.8),\n  proc('Matrix',       'pg-rain-fade',      'linear',       2.0, (x,y)   => { const s=[0,.3,.7,.1,.5]; return -((y/N)*2+s[x]); }),\n  proc('Pong',         'pg-pulse-yellow',   'linear',       2.6, (x,y)   => { const i=pongOrd.indexOf(y*N+x); return i>=0?-(i/pongOrd.length)*2.6:999; }, true),\n  proc('Concentric',   'pg-pulse-violet',   'ease-in-out',  2.0, (x,y)   => { const r=Math.min(x,y,N-1-x,N-1-y); return -(r/RINGS)*1+(r%2)*.5; }),\n  proc('Twin Spirals', 'pg-pulse-magenta',  'linear',       3.0, (x,y)   => { const a=Math.atan2(y-C,x-C); return -(((a*2+Math.PI*2)/(Math.PI*2))%1)*3; }),\n  // AI/process\n  proc('Thinking',     'pg-think',          'ease-in-out',  2.0, (x,y)   => -(Math.sin((y/N)*Math.PI)*.5+.5)*2-(x*.06)),\n  proc('Searching',    'pg-search',         'ease-in-out',  1.6, (x,y)   => -((y/N)*1.6+(Math.abs(x-C)/C)*.3)),\n  proc('Finding',      'pg-find',           'ease-in-out',  1.8, (x,y)   => -(1-euclidean(x,y)/MAXR)*1.8),\n  proc('Consolidating','pg-consolidate',    'ease-in-out',  2.2, (x,y)   => -(Math.min(x,y,N-1-x,N-1-y)/RINGS)*2.2),\n  proc('Streaming',    'pg-stream',         'linear',       1.4, (x,y)   => -((x/N)*1.4+(y%3)*.15)),\n  proc('Reasoning',    'pg-reason',         'ease-in-out',  2.0, (x)     => -(Math.min(x/N,(N-1-x)/N)*2)),\n  proc('Indexing',     'pg-index',          'linear',       1.6, (x,y)   => -((x/N)*1.6+(y*.03))),\n  proc('Connecting',   'pg-connect',        'ease-in-out',  2.0, (x,y)   => -(euclidean(x,y)/MAXR)*1-(((x+y)%2)*.5)),\n  proc('Generating',   'pg-generate',       'ease-out',     2.4, (x,y)   => -((y*N+x)/TOTAL)*2.4),\n  proc('Reflecting',   'pg-reflect',        'ease-in-out',  2.0, (x,y)   => { const t=(y<C)?y:(N-1-y); return -(t/RINGS)*1-(x*.07); }),\n  // Scale loaders\n  proc('Scale Ripple', 'pg-scale-cyan',     'ease-in-out',  1.8, (x,y)   => -(euclidean(x,y)/MAXR)*1.8),\n  proc('Scale Wave',   'pg-scale-magenta',  'ease-in-out',  1.6, (x,y)   => -((x+y*.3)/N)*1.6),\n  proc('Scale Diag',   'pg-scale-violet',   'ease-in-out',  2.0, (x,y)   => -((x+y)/(N*2-2))*2),\n  proc('Scale Pop',    'pg-scale-amber',    'ease-in-out',  1.6, (x,y,i) => { const r=Math.sin(i*9301+49297)*233280; return -((r-Math.floor(r))*1.6); }),\n  proc('Scale Ring',   'pg-scale-mint',     'ease-in-out',  2.0, (x,y)   => -(Math.min(x,y,N-1-x,N-1-y)/RINGS)*2),\n  proc('Scale Check',  'pg-scale-pink',     'ease-in-out',  1.4, (x,y)   => ((x+y)%2===0)?0:-0.7),\n  // Funky scale\n  proc('Twist',        'pg-twist',          'ease-in-out',  2.0, (x,y)   => -(euclidean(x,y)/MAXR)*2),\n  proc('Squash',       'pg-squash',         'ease-in-out',  1.4, (x)     => -(x/N)*1.4),\n  proc('Jelly',        'pg-jelly',          'ease-in-out',  1.6, (x,y)   => -((x+y)/(N*2))*1.6),\n  proc('Pop Rotate',   'pg-pop-rotate',     'cubic-bezier(.5,1.6,.4,1)', 2.4, (x,y,i) => { const r=Math.sin(i*12.9898+78.233)*43758.5453; return -((r-Math.floor(r))*2.4); }),\n  proc('Skew',         'pg-skew',           'ease-in-out',  1.8, (x,y)   => -(y/N)*1.8),\n  proc('Heartbeat Scale','pg-heartbeat',    'ease-in-out',  1.4, (x,y)   => -(euclidean(x,y)/MAXR)*1.4),\n  proc('Drop',         'pg-drop',           'cubic-bezier(.5,0,.5,1.6)', 1.6, (x,y) => -(x*.1+y*.16)),\n  proc('Burst',        'pg-burst',          'steps(8,end)', 1.2, (x,y)   => -(euclidean(x,y)/MAXR)*1.2),\n  proc('Spiral Scale', 'pg-spiral',         'ease-in-out',  2.4, (x,y)   => { const a=Math.atan2(y-C,x-C); return -((a+Math.PI)/(Math.PI*2))*2.4; }),\n  proc('Zigzag',       'pg-zigzag',         'ease-in-out',  1.6, (x,y)   => { const o=(y%2===0)?x:(N-1-x); return -(o/N)*1.6; }),\n\n  // Mandalas — 5x5 has a true centre pixel, so these land symmetrically.\n  proc('Round',          'pg-mandala-round',   'ease-in-out', 2.0, (x,y) => -(euclidean(x,y)/MAXR)*2),\n  proc('Square Mandala', 'pg-mandala-square',  'ease-in-out', 2.0, (x,y) => -(chebyshev(x,y)/RINGS)*2),\n  proc('Diamond Mandala','pg-mandala-diamond', 'ease-in-out', 2.0, (x,y) => -(manhattan(x,y)/MAXMAN)*2),\n  proc('Cross',          'pg-mandala-cross',   'ease-in-out', 1.8, (x,y) => { if(x!==C&&y!==C) return 999; return -(chebyshev(x,y)/RINGS)*1.8; }, true),\n  proc('X Diagonal',     'pg-mandala-x',       'ease-in-out', 1.8, (x,y) => { if(x!==y&&x+y!==N-1) return 999; return -(euclidean(x,y)/MAXR)*1.8; }, true),\n  proc('Star Burst',     'pg-mandala-star',    'ease-in-out', 2.0, (x,y) => { const on=(x===C||y===C||x===y||x+y===N-1); if(!on) return 999; return -(euclidean(x,y)/MAXR)*2; }, true),\n  proc('Petal',          'pg-mandala-petal',   'ease-in-out', 2.4, (x,y) => { const m=['.P.P.','PPPPP','.PPP.','PPPPP','.P.P.']; if(m[y][x]!=='P') return 999; return -(euclidean(x,y)/MAXR)*2.4; }, true),\n  proc('Snowflake',      'pg-mandala-snow',    'ease-in-out', 2.4, (x,y) => { const m=['S.S.S','.SSS.','SSSSS','.SSS.','S.S.S']; if(m[y][x]!=='S') return 999; return -(euclidean(x,y)/MAXR)*2.4; }, true),\n  proc('Gear',           'pg-mandala-gear',    'ease-in-out', 2.0, (x,y) => { const m=['.G.G.','GGGGG','GG.GG','GGGGG','.G.G.']; if(m[y][x]!=='G') return 999; return -(euclidean(x,y)/MAXR)*2; }, true),\n  proc('Kaleido',        'pg-mandala-kaleido', 'ease-in-out', 2.4, (x,y) => -(chebyshev(x,y)/RINGS)*2.4),\n  proc('Spiral Mandala', 'pg-mandala-spiral',  'ease-in-out', 2.4, (x,y) => { const a=Math.atan2(y-C,x-C); return -((a+Math.PI)/(Math.PI*2))*2.4; }),\n  proc('Pulse Square',   'pg-mandala-pulse',   'ease-in-out', 1.8, (x,y) => -(chebyshev(x,y)/(RINGS*2))*1.8),\n  proc('Check Mandala',  'pg-mandala-checker', 'ease-in-out', 1.8, (x,y) => { if((x+y)%2!==0) return 999; return -(chebyshev(x,y)/RINGS)*1.8; }, true),\n  proc('Octagon',        'pg-mandala-oct',     'ease-in-out', 2.0, (x,y) => { const m=['.OOO.','O...O','O...O','O...O','.OOO.']; if(m[y][x]!=='O') return 999; return -((Math.atan2(y-C,x-C)+Math.PI)/(Math.PI*2))*2; }, true),\n  proc('Lotus',          'pg-mandala-lotus',   'ease-in-out', 3.0, (x,y) => { const m=['..L..','.LLL.','LLLLL','.LLL.','..L..']; if(m[y][x]!=='L') return 999; return -(chebyshev(x,y)/RINGS)*3; }, true),\n\n  // Geometric masks ported from the dot-matrix pattern set. Each is expressed as\n  // the original predicate rather than a hand-drawn string, so the shape stays\n  // derived from its maths.\n  //\n  // The full perimeter including corners — unlike Octagon, which cuts them.\n  // Delay keys off euclidean distance, so corners (2.83) lag the edge midpoints\n  // (2.0) and the ring breathes in and out instead of pulsing flat.\n  proc('Outline',        'pg-mandala-oct',     'ease-in-out', 2.0, (x,y) => {\n    if (x !== 0 && x !== N - 1 && y !== 0 && y !== N - 1) return 999;\n    return -(euclidean(x, y) / MAXR) * 2;\n  }, true),\n  // Cells whose radius rounds to 1 or 2 — drops the centre and the four\n  // corners, leaving two concentric bands that pulse outward.\n  proc('Rings',          'pg-mandala-round',   'ease-in-out', 2.2, (x,y) => {\n    const r = Math.round(euclidean(x, y));\n    if (r !== 1 && r !== 2) return 999;\n    return -((r - 1) / 1) * 1.1;\n  }, true),\n  // A three-petal rose curve, |sin(3θ)| thresholded, with the centre excluded.\n  // Delay follows the angle so the petals sweep round.\n  proc('Rose',           'pg-mandala-petal',   'ease-in-out', 2.6, (x,y) => {\n    const dx = x - C, dy = y - C;\n    const radius = Math.hypot(dx, dy);\n    if (Math.abs(Math.sin(3 * Math.atan2(dy, dx))) <= 0.6 || radius < 1) return 999;\n    return -((Math.atan2(dy, dx) + Math.PI) / (Math.PI * 2)) * 2.6;\n  }, true),\n// ---- Ported from the dot-matrix reference (~/Desktop/matrix) ----\n  // Each was a shared @keyframes plus a per-dot animation-delay derived from a\n  // path ordering — i.e. a pure phase shift, which is exactly what proc() is.\n  // Delay formulas are the reference's, converted to lead-in form.\n\n  // dotm-square-1. Anti-diagonal sweep from top-right, alternating parity so\n  // neighbouring diagonals fire half a cycle apart.\n  proc('Neon Drift',   'dmx-neon-drift',   'linear', DMX, (x,y) => {\n    const slice = y + (N - 1 - x);\n    return lead(slice / ((N - 1) * 2) * 0.2 + (slice % 2) * 0.5, DMX);\n  }),\n  // dotm-square-3. A decaying tail chasing the spiral inward.\n  proc('Core Spiral',  'dmx-snake-trail',  'linear', DMX, (x,y) =>\n    lead(SPIRAL_INWARD_ORDER[y * N + x] * 0.04, DMX)),\n  // dotm-square-4. Outer ring clockwise, inner ring anti-clockwise, centre dark.\n  proc('Twin Orbit',   'dmx-snake-trail',  'linear', DMX, (x,y) => {\n    const i = y * N + x;\n    if (x === C && y === C) return 999;\n    const outer = OUTER_RING_ORDER[i];\n    if (outer >= 0) return lead(outer / 16, DMX);\n    return lead(1 - MIDDLE_RING_ORDER[i] / 8, DMX);\n  }, true),\n  // dotm-square-5. Same tail, serpentining across the anti-diagonals.\n  proc('Prism Sweep',  'dmx-snake-trail',  'linear', DMX, (x,y) =>\n    lead(DIAGONAL_SNAKE_ORDER[y * N + x] * 0.04, DMX)),\n  // dotm-square-6. Equaliser columns: each column runs bottom-up or top-down by\n  // parity, stepped so the levels read as discrete bars.\n  proc('Flux Columns', 'dmx-flux-columns', 'steps(5, end)', DMX, (x,y) =>\n    lead(((x % 2 === 0 ? N - 1 - y : y) * 0.2), DMX)),\n  // dotm-square-11. Manhattan rings, with a parity nudge so alternating rings\n  // separate slightly instead of moving as one front.\n  proc('Echo Ring',    'dmx-ripple-echo',  'ease-in-out', DMX, (x,y) => {\n    const ring = Math.min(4, Math.abs(y - C) + Math.abs(x - C));\n    return lead(ring * 0.14 + (ring % 2) * 0.03, DMX);\n  }),\n  // dotm-square-12. Same idea but the origin is off-centre at (1,1), so the wave\n  // is asymmetric across the grid.\n  proc('Origin Wave',  'dmx-origin-wave',  'ease-in-out', DMX, (x,y) => {\n    const ring = Math.min(6, Math.abs(y - 1) + Math.abs(x - 1));\n    return lead(ring * 0.16, DMX);\n  }),\n// ---- New patterns ----\n\n  // A beam sweeping around the centre, leaving a decaying wake. The centre has\n  // no meaningful angle, so it holds steady as the pivot.\n  proc('Sonar',      'pg-sonar',  'linear', 2.4, (x,y) => {\n    if (x === C && y === C) return 0;\n    return -((Math.atan2(y - C, x - C) + Math.PI) / (Math.PI * 2)) * 2.4;\n  }),\n  // Four two-cell arms with 4-fold rotational symmetry, phased by angle so the\n  // whole pinwheel appears to turn.\n  proc('Pinwheel',   'pg-sonar',  'linear', 2.0, (x,y) => {\n    const m = ['.PP..', '....P', 'P.P.P', 'P....', '..PP.'];\n    if (m[y][x] !== 'P') return 999;\n    if (x === C && y === C) return 0;\n    return -((Math.atan2(y - C, x - C) + Math.PI) / (Math.PI * 2)) * 2.0;\n  }, true),\n  // Topples along the serpentine path, each cell tipping into the next.\n  proc('Domino',     'pg-domino', 'ease-in-out', 2.4, (x,y) => {\n    const col = (y % 2 === 0) ? x : (N - 1 - x);\n    return -((y * N + col) / TOTAL) * 2.4;\n  }),\n  // Four runners starting at the corners and converging on the centre.\n  proc('Corners',    'pg-frame',  'ease-in-out', 1.8, (x,y) => {\n    const d = Math.min(\n      Math.hypot(x, y), Math.hypot(N - 1 - x, y),\n      Math.hypot(x, N - 1 - y), Math.hypot(N - 1 - x, N - 1 - y),\n    );\n    return -(d / Math.hypot(C, C)) * 1.8;\n  }),\n  // Nested square frames snapping on from the outside in.\n  proc('Frame',      'pg-frame',  'ease-in-out', 2.0, (x,y) =>\n    -((RINGS - chebyshev(x, y)) / RINGS) * 2.0),\n];\n\n/**\n * Pulse-wave family. Their tempo is tuned as a group, so it lives here rather\n * than being edited loader by loader.\n */\nconst PULSE_WAVE = new Set([\n  'sweep', 'diagonal', 'ripple', 'rain', 'spiral', 'snake', 'sparkle',\n  'heartbeat', 'scanner', 'orbit', 'breathe', 'checker', 'stripes', 'falling',\n  'plasma', 'loadbar', 'knight-tour', 'vortex', 'sine-wave', 'life',\n  'quadrants', 'crossfade', 'glider', 'matrix', 'pong', 'concentric',\n  'twin-spirals',\n]);\n\n/** <1 speeds up. 0.8 == 20% quicker. */\nconst PULSE_TEMPO = 0.8;\n\n/**\n * Rescale a loader's tempo.\n *\n * Duration and every per-dot delay must scale by the same factor. A delay is an\n * absolute number of seconds representing a *fraction* of the cycle, so touching\n * the duration alone silently shifts every dot's phase — the exact trap that the\n * duration-written-twice pattern sets. Scaling the resolved delay sidesteps it\n * whatever arithmetic produced the value.\n */\nfunction retempo(def: ProceduralDef, factor: number): ProceduralDef {\n  return {\n    ...def,\n    duration: Math.round(def.duration * factor * 1000) / 1000,\n    delay: (x, y, i) => {\n      const d = def.delay(x, y, i);\n      return d === 999 ? 999 : d * factor; // 999 is the mask sentinel, not a time\n    },\n  };\n}\n\n// ---- Name-to-slug mapping ----\nfunction toSlug(name: string): string {\n  return name.toLowerCase().replace(/[\\s.]+/g, '-').replace(/[^a-z0-9-]/g, '');\n}\n\nexport const LOADER_REGISTRY: Record<string, LoaderDef> = {};\nfor (const def of DEFS) {\n  const slug = toSlug(def.name);\n  LOADER_REGISTRY[slug] =\n    def.kind === 'procedural' && PULSE_WAVE.has(slug) ? retempo(def, PULSE_TEMPO) : def;\n}\n\n// Compiled fields. Only the sampled curve data reaches the browser — everything\n// in engine/field.ts and engine/compile.ts runs at build time.\nfor (const [slug, f] of Object.entries(FIELD_DOTS)) {\n  const def: CompiledDef = { kind: 'compiled', name: f.name, slug, duration: f.duration, dots: f.dots };\n  LOADER_REGISTRY[slug] = def;\n}\n\n/** Every registered loader slug, in registry order. */\nexport const LOADER_NAMES: string[] = [...DEFS.map(d => toSlug(d.name)), ...Object.keys(FIELD_DOTS)];\n\n// Also map some common aliases\nconst ALIASES: Record<string, string> = {\n  'knight': 'knight-tour',\n  'sine': 'sine-wave',\n  'twin': 'twin-spirals',\n};\nfor (const [alias, target] of Object.entries(ALIASES)) {\n  if (!LOADER_REGISTRY[alias] && LOADER_REGISTRY[target]) {\n    LOADER_REGISTRY[alias] = LOADER_REGISTRY[target];\n  }\n}\n",
      "type": "registry:file",
      "target": "components/soonish/data/loaders.ts"
    },
    {
      "path": "packages/soonish/src/engine/generated.ts",
      "content": "// GENERATED by scripts/compile-fields.ts — do not edit by hand.\n// Only the sampled curve data lives here; the CSS goes to\n// styles/fields.generated.css and nothing in engine/ reaches the browser.\nimport type { CompiledDot } from './compile';\n\nexport const FIELD_DOTS: Record<string, { name: string; duration: number; dots: CompiledDot[] }> = {\n  'front-sweep': { name: \"Front Sweep\", duration: 2.4, dots: [{\"d\":0,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0.5},{\"d\":0,\"g\":-1,\"offset\":0.5},{\"d\":0,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0.5},{\"d\":0,\"g\":-1,\"offset\":0.5},{\"d\":0,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0.5},{\"d\":0,\"g\":-1,\"offset\":0.5},{\"d\":0,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0.5},{\"d\":0,\"g\":-1,\"offset\":0.5},{\"d\":0,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0.5},{\"d\":0,\"g\":-1,\"offset\":0.5}] },\n  'ripple-echo': { name: \"Ripple Echo\", duration: 2.8, dots: [{\"d\":0,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":0,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":3,\"g\":-1,\"offset\":0},{\"d\":4,\"g\":-1,\"offset\":0},{\"d\":3,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":4,\"g\":-1,\"offset\":0},{\"d\":5,\"g\":-1,\"offset\":0},{\"d\":4,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":3,\"g\":-1,\"offset\":0},{\"d\":4,\"g\":-1,\"offset\":0},{\"d\":3,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":0,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":0,\"g\":-1,\"offset\":0}] },\n  'pulse-beat': { name: \"Pulse Beat\", duration: 3.2, dots: [{\"d\":0,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0.03125},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":0,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":3,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0.03125},{\"d\":3,\"g\":-1,\"offset\":0},{\"d\":4,\"g\":-1,\"offset\":0},{\"d\":3,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0.03125},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":3,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":0,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0.03125},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":0,\"g\":-1,\"offset\":0}] },\n  'lumen-bloom': { name: \"Lumen Bloom\", duration: 3, dots: [{\"d\":0,\"g\":0,\"offset\":0},{\"d\":0,\"g\":0,\"offset\":0.07291666666666667},{\"d\":0,\"g\":0,\"offset\":0.10416666666666667},{\"d\":0,\"g\":0,\"offset\":0.07291666666666667},{\"d\":0,\"g\":0,\"offset\":0},{\"d\":0,\"g\":0,\"offset\":0.07291666666666667},{\"d\":0,\"g\":0,\"offset\":0.1736111111111111},{\"d\":0,\"g\":0,\"offset\":0.22569444444444445},{\"d\":0,\"g\":0,\"offset\":0.1736111111111111},{\"d\":0,\"g\":0,\"offset\":0.07291666666666667},{\"d\":0,\"g\":0,\"offset\":0.10416666666666667},{\"d\":0,\"g\":0,\"offset\":0.22569444444444445},{\"d\":0,\"g\":0,\"offset\":0.3506944444444444},{\"d\":0,\"g\":0,\"offset\":0.22569444444444445},{\"d\":0,\"g\":0,\"offset\":0.10416666666666667},{\"d\":0,\"g\":0,\"offset\":0.07291666666666667},{\"d\":0,\"g\":0,\"offset\":0.1736111111111111},{\"d\":0,\"g\":0,\"offset\":0.22569444444444445},{\"d\":0,\"g\":0,\"offset\":0.1736111111111111},{\"d\":0,\"g\":0,\"offset\":0.07291666666666667},{\"d\":0,\"g\":0,\"offset\":0},{\"d\":0,\"g\":0,\"offset\":0.07291666666666667},{\"d\":0,\"g\":0,\"offset\":0.10416666666666667},{\"d\":0,\"g\":0,\"offset\":0.07291666666666667},{\"d\":0,\"g\":0,\"offset\":0}] },\n  'raindrop': { name: \"Raindrop\", duration: 2.6, dots: [{\"d\":0,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":0,\"g\":-1,\"offset\":0},{\"d\":3,\"g\":-1,\"offset\":0},{\"d\":4,\"g\":-1,\"offset\":0},{\"d\":5,\"g\":-1,\"offset\":0},{\"d\":4,\"g\":-1,\"offset\":0},{\"d\":3,\"g\":-1,\"offset\":0},{\"d\":6,\"g\":-1,\"offset\":0},{\"d\":7,\"g\":-1,\"offset\":0},{\"d\":8,\"g\":-1,\"offset\":0},{\"d\":7,\"g\":-1,\"offset\":0},{\"d\":6,\"g\":-1,\"offset\":0},{\"d\":3,\"g\":-1,\"offset\":0},{\"d\":9,\"g\":-1,\"offset\":0},{\"d\":10,\"g\":-1,\"offset\":0},{\"d\":9,\"g\":-1,\"offset\":0},{\"d\":3,\"g\":-1,\"offset\":0},{\"d\":0,\"g\":-1,\"offset\":0},{\"d\":3,\"g\":-1,\"offset\":0},{\"d\":6,\"g\":-1,\"offset\":0},{\"d\":3,\"g\":-1,\"offset\":0},{\"d\":0,\"g\":-1,\"offset\":0}] },\n  'pendulum': { name: \"Pendulum\", duration: 2.8, dots: [{\"d\":0,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0.5},{\"d\":0,\"g\":-1,\"offset\":0.5},{\"d\":3,\"g\":-1,\"offset\":0},{\"d\":0,\"g\":-1,\"offset\":0.9930555555555556},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":0,\"g\":-1,\"offset\":0.4930555555555556},{\"d\":3,\"g\":-1,\"offset\":0.5},{\"d\":4,\"g\":-1,\"offset\":0},{\"d\":5,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":5,\"g\":-1,\"offset\":0.5},{\"d\":4,\"g\":-1,\"offset\":0.5},{\"d\":6,\"g\":-1,\"offset\":0},{\"d\":7,\"g\":-1,\"offset\":0},{\"d\":8,\"g\":-1,\"offset\":0},{\"d\":7,\"g\":-1,\"offset\":0.5},{\"d\":6,\"g\":-1,\"offset\":0.5},{\"d\":9,\"g\":-1,\"offset\":0},{\"d\":10,\"g\":-1,\"offset\":0},{\"d\":11,\"g\":-1,\"offset\":0},{\"d\":10,\"g\":-1,\"offset\":0.5},{\"d\":9,\"g\":-1,\"offset\":0.5}] },\n  'firefly': { name: \"Firefly\", duration: 3.4, dots: [{\"d\":0,\"g\":-1,\"offset\":0},{\"d\":0,\"g\":-1,\"offset\":0.2222222222222222},{\"d\":1,\"g\":-1,\"offset\":0},{\"d\":2,\"g\":-1,\"offset\":0},{\"d\":3,\"g\":-1,\"offset\":0},{\"d\":4,\"g\":-1,\"offset\":0},{\"d\":5,\"g\":-1,\"offset\":0},{\"d\":6,\"g\":-1,\"offset\":0},{\"d\":6,\"g\":-1,\"offset\":0.05555555555555555},{\"d\":7,\"g\":-1,\"offset\":0},{\"d\":8,\"g\":-1,\"offset\":0},{\"d\":9,\"g\":-1,\"offset\":0},{\"d\":10,\"g\":-1,\"offset\":0},{\"d\":11,\"g\":-1,\"offset\":0},{\"d\":0,\"g\":-1,\"offset\":0.8888888888888888},{\"d\":12,\"g\":-1,\"offset\":0},{\"d\":9,\"g\":-1,\"offset\":0.7777777777777778},{\"d\":12,\"g\":-1,\"offset\":0.03125},{\"d\":1,\"g\":-1,\"offset\":0.8923611111111112},{\"d\":13,\"g\":-1,\"offset\":0},{\"d\":14,\"g\":-1,\"offset\":0},{\"d\":1,\"g\":-1,\"offset\":0.6944444444444444},{\"d\":15,\"g\":-1,\"offset\":0},{\"d\":7,\"g\":-1,\"offset\":0.3333333333333333},{\"d\":4,\"g\":-1,\"offset\":0.19444444444444445}] },\n  'halo': { name: \"Halo\", duration: 2.6, dots: [{\"d\":0,\"g\":0,\"offset\":0},{\"d\":1,\"g\":1,\"offset\":0},{\"d\":2,\"g\":2,\"offset\":0},{\"d\":1,\"g\":1,\"offset\":0},{\"d\":0,\"g\":0,\"offset\":0},{\"d\":1,\"g\":1,\"offset\":0},{\"d\":2,\"g\":2,\"offset\":0.17708333333333334},{\"d\":3,\"g\":3,\"offset\":0},{\"d\":2,\"g\":2,\"offset\":0.17708333333333334},{\"d\":1,\"g\":1,\"offset\":0},{\"d\":2,\"g\":2,\"offset\":0},{\"d\":3,\"g\":3,\"offset\":0},{\"d\":4,\"g\":4,\"offset\":0},{\"d\":3,\"g\":3,\"offset\":0},{\"d\":2,\"g\":2,\"offset\":0},{\"d\":1,\"g\":1,\"offset\":0},{\"d\":2,\"g\":2,\"offset\":0.17708333333333334},{\"d\":3,\"g\":3,\"offset\":0},{\"d\":2,\"g\":2,\"offset\":0.17708333333333334},{\"d\":1,\"g\":1,\"offset\":0},{\"d\":0,\"g\":0,\"offset\":0},{\"d\":1,\"g\":1,\"offset\":0},{\"d\":2,\"g\":2,\"offset\":0},{\"d\":1,\"g\":1,\"offset\":0},{\"d\":0,\"g\":0,\"offset\":0}] },\n};\n",
      "type": "registry:file",
      "target": "components/soonish/engine/generated.ts"
    },
    {
      "path": "packages/soonish/src/engine/compile.ts",
      "content": "import { GRID, CELLS } from '../types';\nimport { clamp01, type Field, type MultiField, type Channel } from './field';\n\n/** Channels a dot can animate independently. Each maps to its own keyframe. */\nexport const CHANNELS: Channel[] = ['o', 'g', 's'];\n\nconst CHANNEL_KEYFRAME: Record<Channel, string> = {\n  o: 'hl-field',\n  g: 'hl-field-glow',\n  s: 'hl-field-scale',\n};\n\nexport interface CompiledDot {\n  /**\n   * Index into `combos` — the element-level (opacity, scale) pairing.\n   *\n   * These two MUST be emitted as one rule with comma-separated animation lists.\n   * Two rules each setting `animation-name` on the same element do not combine;\n   * the later one silently wins and the other channel never animates.\n   */\n  d: number;\n  /** Glow curve index, or -1. Safe as its own rule: it targets ::after. */\n  g: number;\n  /** Fraction of the loop to offset by, in [0, 1). Shared across channels. */\n  offset: number;\n}\n\n/** A unique element-level channel pairing. -1 means that channel is static. */\nexport interface Combo {\n  o: number;\n  s: number;\n}\n\nexport interface CompiledField {\n  name: string;\n  slug: string;\n  duration: number;\n  /** Unique curves per channel. */\n  curves: Record<Channel, number[][]>;\n  /** Unique (opacity, scale) pairings, one emitted rule each. */\n  combos: Combo[];\n  dots: CompiledDot[];\n}\n\n/** Round hard enough that float noise does not defeat deduplication. */\nconst q = (v: number) => Math.round(clamp01(v) * 1000) / 1000;\n\nconst key = (curve: number[]) => curve.join(',');\n\n/** Read a closed curve at a continuous loop position, interpolating between samples. */\nfunction sampleAt(curve: number[], phase: number): number {\n  const n = curve.length - 1;\n  const p = ((phase % 1) + 1) % 1;\n  const f = p * n;\n  const i = Math.floor(f);\n  const frac = f - i;\n  const a = curve[i % n];\n  const b = curve[(i + 1) % n];\n  return a + (b - a) * frac;\n}\n\n/**\n * Find a phase offset at which `candidate` reproduces `target`, or null.\n *\n * Integer-rotation matching only works when the true offset lands on a sample\n * boundary. A field offsetting by `x/5` is 7.2 samples at K=36 — never integral —\n * so a one-curve field used to compile to five. Searching sub-sample offsets fixes it.\n */\nfunction findPhaseMatch(candidate: number[], target: number[], eps = 0.02): number | null {\n  const n = target.length - 1;\n  const steps = n * 8;\n  let best: { offset: number; err: number } | null = null;\n  for (let s = 0; s < steps; s++) {\n    const offset = s / steps;\n    let err = 0;\n    for (let i = 0; i < n && err <= eps; i++) {\n      err = Math.max(err, Math.abs(sampleAt(candidate, i / n + offset) - target[i]));\n    }\n    if (err <= eps && (best === null || err < best.err)) best = { offset, err };\n  }\n  return best ? best.offset : null;\n}\n\n/** True when a channel never leaves its resting value, so it needs no animation. */\nconst isFlat = (curve: number[]) => curve.every((v) => v === curve[0]);\n\n/**\n * Sample a multi-channel field into per-dot curves, deduplicating within each\n * channel on two axes: exact equality, and equality under a phase offset.\n *\n * Channels are compiled independently, so a dot's opacity, glow and scale can\n * each follow a differently shaped curve — the thing a single shared keyframe\n * plus animation-delay cannot express.\n */\nexport function compileField(\n  name: string,\n  slug: string,\n  field: Field | MultiField,\n  opts: { duration: number; samples?: number },\n): CompiledField {\n  const samples = opts.samples ?? 36;\n  const curves: Record<Channel, number[][]> = { o: [], g: [], s: [] };\n  const byKey: Record<Channel, Map<string, number>> = { o: new Map(), g: new Map(), s: new Map() };\n  const combos: Combo[] = [];\n  const comboIndex = new Map<string, number>();\n  const dots: CompiledDot[] = [];\n\n  // Normalise a plain Field (returns a number) to the multi-channel shape.\n  const evaluate = (x: number, y: number, t: number): Partial<Record<Channel, number>> => {\n    const v = (field as MultiField)(x, y, t);\n    return typeof v === 'number' ? { o: v } : v;\n  };\n\n  for (let i = 0; i < CELLS; i++) {\n    const x = i % GRID;\n    const y = Math.floor(i / GRID);\n\n    const raw: Record<Channel, number[] | null> = { o: null, g: null, s: null };\n    for (const ch of CHANNELS) {\n      const series: number[] = [];\n      let present = false;\n      for (let s = 0; s <= samples; s++) {\n        const out = evaluate(x, y, s / samples);\n        if (out[ch] !== undefined) present = true;\n        series.push(q(out[ch] ?? 0));\n      }\n      series[series.length - 1] = series[0];\n      // A channel the field never sets, or one that never moves, is skipped —\n      // no wasted animation on the element.\n      raw[ch] = present && !isFlat(series) ? series : null;\n    }\n\n    const chosen: Record<Channel, number> = { o: -1, g: -1, s: -1 };\n    let offset = 0;\n    let offsetSet = false;\n\n    for (const ch of CHANNELS) {\n      const series = raw[ch];\n      if (!series) continue;\n\n      const exact = byKey[ch].get(key(series));\n      if (exact !== undefined) {\n        chosen[ch] = exact;\n        continue;\n      }\n\n      let matched = false;\n      for (let ci = 0; ci < curves[ch].length; ci++) {\n        const found = findPhaseMatch(curves[ch][ci], series);\n        // One offset is shared across a dot's channels (they animate in lockstep),\n        // so only reuse a rotated curve if it agrees with the offset already set.\n        if (found !== null && (!offsetSet || Math.abs(found - offset) < 1e-6)) {\n          chosen[ch] = ci;\n          offset = found;\n          offsetSet = true;\n          matched = true;\n          break;\n        }\n      }\n      if (matched) continue;\n\n      const idx = curves[ch].length;\n      curves[ch].push(series);\n      byKey[ch].set(key(series), idx);\n      chosen[ch] = idx;\n    }\n\n    const comboKey = `${chosen.o}|${chosen.s}`;\n    let d = comboIndex.get(comboKey);\n    if (d === undefined) {\n      d = combos.length;\n      combos.push({ o: chosen.o, s: chosen.s });\n      comboIndex.set(comboKey, d);\n    }\n\n    dots.push({ d, g: chosen.g, offset });\n  }\n\n  return { name, slug, duration: opts.duration, curves, combos, dots };\n}\n\n/**\n * Emit CSS for a compiled field.\n *\n * Each channel becomes its own animation with its own `linear()` timing function.\n * Glow targets the ::after pseudo-element so the shadow itself never re-renders —\n * only a pre-painted layer's opacity changes.\n */\nexport function emitCSS(compiled: CompiledField): string {\n  const { slug, curves, combos } = compiled;\n  const lines: string[] = [];\n  const total = CHANNELS.reduce((n, ch) => n + curves[ch].length, 0);\n  lines.push(\n    `/* ${compiled.name} — ${total} curve(s), ${combos.length} element rule(s), ${curves.g.length} glow rule(s) */`,\n  );\n\n  // Element-level channels in ONE declaration each. Duration and delay come from\n  // custom properties set inline on the dot; ::after inherits them, which inline\n  // styles cannot reach.\n  combos.forEach((combo, k) => {\n    const parts: { keyframe: string; curve: number[] }[] = [];\n    if (combo.o >= 0) parts.push({ keyframe: CHANNEL_KEYFRAME.o, curve: curves.o[combo.o] });\n    if (combo.s >= 0) parts.push({ keyframe: CHANNEL_KEYFRAME.s, curve: curves.s[combo.s] });\n    if (!parts.length) return;\n    const n = parts.length;\n    lines.push(\n      `.hl-f-${slug} .hl-d${k} { ` +\n        `animation-name: ${parts.map((p) => p.keyframe).join(',')}; ` +\n        `animation-duration: ${Array(n).fill('var(--hl-dur)').join(',')}; ` +\n        `animation-delay: ${Array(n).fill('var(--hl-delay,0s)').join(',')}; ` +\n        `animation-iteration-count: ${Array(n).fill('infinite').join(',')}; ` +\n        `animation-fill-mode: ${Array(n).fill('both').join(',')}; ` +\n        `animation-timing-function: ${parts.map((p) => `linear(${p.curve.join(',')})`).join(',')}; }`,\n    );\n  });\n\n  // Glow is its own rule because it targets ::after — no conflict possible.\n  curves.g.forEach((curve, i) => {\n    lines.push(\n      `.hl-f-${slug} .hl-g${i}::after { animation-name: ${CHANNEL_KEYFRAME.g}; ` +\n        `animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); ` +\n        `animation-iteration-count: infinite; animation-fill-mode: both; ` +\n        `animation-timing-function: linear(${curve.join(',')}); }`,\n    );\n  });\n\n  return lines.join('\\n');\n}\n",
      "type": "registry:file",
      "target": "components/soonish/engine/compile.ts"
    },
    {
      "path": "packages/soonish/src/engine/field.ts",
      "content": "import { GRID } from '../types';\n\n/**\n * A brightness field.\n *\n * `t` is normalised loop position in [0, 1] and MUST be periodic — f(x,y,0) has\n * to equal f(x,y,1) or the compiled animation will jump on loop. The return is\n * clamped to [0, 1] by the compiler.\n *\n * Fields are evaluated at compile time only. Nothing here ships to the browser.\n */\nexport type Field = (x: number, y: number, t: number) => number;\n\n/** Independently animatable channels: opacity, glow, scale. */\nexport type Channel = 'o' | 'g' | 's';\n\n/**\n * A field that drives several channels at once. Each returned channel compiles\n * to its own `linear()` easing, so opacity, glow and scale can follow\n * differently shaped curves on the same dot. Omit a channel to leave it static.\n */\nexport type MultiField = (\n  x: number,\n  y: number,\n  t: number,\n) => number | Partial<Record<Channel, number>>;\n\nexport const C = (GRID - 1) / 2;\n\n// ---- scalar helpers ----\n\nexport const clamp01 = (v: number) => (v < 0 ? 0 : v > 1 ? 1 : v);\n\n/** Hermite ramp. The shoulders ease, unlike a hard threshold. */\nexport function smoothstep01(edge0: number, edge1: number, x: number): number {\n  if (edge0 === edge1) return x < edge0 ? 0 : 1;\n  const t = clamp01((x - edge0) / (edge1 - edge0));\n  return t * t * (3 - 2 * t);\n}\n\n/** Gaussian falloff. Wider sigma reads softer and shimmers less. */\nexport const gauss = (d: number, sigma: number) => Math.exp(-(d * d) / (2 * sigma * sigma));\n\nexport const euclidean = (x: number, y: number) => Math.hypot(x - C, y - C);\nexport const manhattan = (x: number, y: number) => Math.abs(x - C) + Math.abs(y - C);\nexport const chebyshev = (x: number, y: number) => Math.max(Math.abs(x - C), Math.abs(y - C));\n\n/** Loop-safe angle in [0,1) around the centre. */\nexport const angle01 = (x: number, y: number) =>\n  (Math.atan2(y - C, x - C) + Math.PI) / (Math.PI * 2);\n\n// ---- field builders ----\n\n/**\n * A front that oscillates back and forth along one axis, with a soft Gaussian\n * edge.\n *\n * This is the shape that phase-shifting cannot express. Because the front\n * reverses, a dot near the centre is crossed twice per cycle at even spacing\n * while an edge dot is crossed once and dwells at the turnaround — so the two\n * dots need genuinely different brightness curves, not the same curve offset.\n */\nexport function oscillatingFront(opts: {\n  axis: 'x' | 'y';\n  lo?: number;\n  hi?: number;\n  sigma?: number;\n}): Field {\n  const { axis, lo = 0, hi = GRID - 1, sigma = 0.9 } = opts;\n  const mid = (lo + hi) / 2;\n  const amp = (hi - lo) / 2;\n  return (x, y, t) => {\n    const front = mid + amp * Math.cos(t * Math.PI * 2);\n    return gauss((axis === 'x' ? x : y) - front, sigma);\n  };\n}\n\n/**\n * N concentric rings expanding outward, summed. Each ring is offset in time, so\n * a dot is lit several times per cycle with radius-dependent spacing — again a\n * different curve shape per dot rather than a shared one.\n */\nexport function radialEcho(opts: { rings?: number; sigma?: number; maxR?: number }): Field {\n  const { rings = 2, sigma = 0.55, maxR = Math.hypot(C, C) } = opts;\n  return (x, y, t) => {\n    const r = euclidean(x, y);\n    let v = 0;\n    for (let k = 0; k < rings; k++) {\n      // each ring starts 1/rings of a cycle apart and sweeps 0 -> maxR\n      const phase = (t + k / rings) % 1;\n      v += gauss(r - phase * maxR, sigma) * (1 - phase * 0.55);\n    }\n    return v;\n  };\n}\n\n/**\n * Two sine components at different frequencies whose phase offsets depend on\n * position in *different* ways. The interference pattern per dot is unique, so\n * this is not reducible to one curve plus a delay.\n */\nexport function beat(opts: { fastMul?: number; sigma?: number } = {}): Field {\n  const { fastMul = 3 } = opts;\n  return (x, y, t) => {\n    const slow = 0.5 + 0.5 * Math.cos((t - euclidean(x, y) / 8) * Math.PI * 2);\n    const fast = 0.5 + 0.5 * Math.cos((t * fastMul - manhattan(x, y) / 10) * Math.PI * 2);\n    return smoothstep01(0.15, 0.95, slow * 0.65 + fast * 0.35);\n  };\n}\n",
      "type": "registry:file",
      "target": "components/soonish/engine/field.ts"
    },
    {
      "path": "packages/soonish/src/styles/base.css",
      "content": "/*\n * soonish — base grid + animation keyframes.\n *\n * A real stylesheet, not a JS string: the browser parses it with the CSS engine,\n * caches it, and applies it before first paint. It used to be injected from a\n * useEffect, which meant dots rendered unstyled until hydration and forced the\n * whole component to be a client component.\n *\n * Field animations live in fields.generated.css (emitted by scripts/compile-fields.ts).\n */\n.hl-matrix {\n  /* Scopes layout and paint to the grid, so a loader can never invalidate\n     anything outside itself. Cheap and worth keeping. */\n  contain: layout paint style;\n  isolation: isolate;\n\n  /* Deliberately NOT promoted to its own compositor layer.\n     `transform: translate3d(0,0,0)` + `will-change: transform` used to live here\n     and forced a permanent GPU layer per matrix — 74 of them on /loaders — while\n     the matrix element itself never animates. Only the dots animate, and the\n     browser promotes those on its own for the duration of a running\n     opacity/transform animation. will-change is for anticipating a future\n     animation, not for annotating a perpetual one. */\n}\n/*\n * Each grid track holds an .hl-cell, which never animates, and an .hl-px inside\n * it, which does. The split exists because a scaling dot physically disappears\n * at scale 0 — and a pseudo-element of that dot would scale away with it. Only\n * an untransformed wrapper can hold a placeholder that survives.\n */\n.hl-matrix .hl-cell {\n  position: relative;\n  width: 100%;\n  height: 100%;\n}\n\n/* The placeholder: an always-visible dim lamp. Opt-in via .hl-ph on the matrix,\n   because loaders that animate opacity alone never vanish and would just read\n   brighter at rest if a second layer were stacked under them. */\n.hl-matrix.hl-ph .hl-cell::before {\n  content: \"\";\n  position: absolute;\n  inset: 0;\n  border-radius: 1px;\n  background: var(--loader-color, oklch(0.897 0.043 181.815));\n  opacity: 0.16;\n}\n\n.hl-matrix .hl-px {\n  position: absolute;\n  inset: 0;\n  border-radius: 1px;\n  background: var(--loader-color, oklch(0.897 0.043 181.815));\n  opacity: 0.16;\n  box-shadow: none;\n  /* transform-origin matters: the scale-based loaders grow from the cell\n     centre, not the top-left. */\n  transform-origin: center;\n}\n\n@keyframes pg-pulse-cyan {\n  0%, 100% { opacity: 0.16; }\n  35%, 55% { opacity: 1; }\n}\n@keyframes pg-pulse-magenta {\n  0%, 100% { opacity: 0.16; }\n  35%, 55% { opacity: 1; }\n}\n@keyframes pg-pulse-violet {\n  0%, 100% { opacity: 0.16; }\n  35%, 55% { opacity: 1; }\n}\n@keyframes pg-pulse-mint {\n  0%, 100% { opacity: 0.16; }\n  35%, 55% { opacity: 1; }\n}\n@keyframes pg-pulse-pink {\n  0%, 100% { opacity: 0.16; }\n  35%, 55% { opacity: 1; }\n}\n@keyframes pg-pulse-lime {\n  0%, 100% { opacity: 0.16; }\n  35%, 55% { opacity: 1; }\n}\n@keyframes pg-pulse-amber {\n  0%, 100% { opacity: 0.16; }\n  50% { opacity: 1; }\n}\n@keyframes pg-pulse-yellow {\n  0%, 100% { opacity: 0.14; }\n  10%, 18% { opacity: 1; }\n}\n@keyframes pg-rain-fade {\n  0%, 100% { opacity: 0.16; }\n  20% { opacity: 1; }\n  60% { opacity: 0.38; }\n}\n@keyframes pg-think {\n  0%, 100% { opacity: 0.16; }\n  30%, 50% { opacity: 1; }\n}\n@keyframes pg-search {\n  0%, 100% { opacity: 0.16; }\n  20%, 30% { opacity: 1; }\n}\n@keyframes pg-find {\n  0%, 100% { opacity: 0.16; }\n  60%, 80% { opacity: 1; }\n}\n@keyframes pg-consolidate {\n  0%, 100% { opacity: 0.16; }\n  40%, 60% { opacity: 1; }\n}\n@keyframes pg-stream {\n  0%, 100% { opacity: 0.14; }\n  20% { opacity: 1; }\n  40% { opacity: 0.45; }\n}\n@keyframes pg-reason {\n  0%, 100% { opacity: 0.16; }\n  40%, 60% { opacity: 1; }\n}\n@keyframes pg-index {\n  0%, 100% { opacity: 0.16; }\n  10%, 25% { opacity: 1; }\n}\n@keyframes pg-connect {\n  0%, 100% { opacity: 0.16; }\n  40%, 60% { opacity: 1; }\n}\n@keyframes pg-generate {\n  0%, 100% { opacity: 0.16; }\n  40%, 70% { opacity: 1; }\n}\n@keyframes pg-reflect {\n  0%, 100% { opacity: 0.16; }\n  40%, 60% { opacity: 1; }\n}\n@keyframes pg-plasma {\n  0%, 100% { opacity: 1; }\n  33% { opacity: 0.72; }\n  66% { opacity: 0.92; }\n}\n@keyframes pg-scale-cyan {\n  0%, 100% { transform: scale(0.2); opacity: 0.28; }\n  50% { transform: scale(1); opacity: 1; }\n}\n@keyframes pg-scale-magenta {\n  0%, 100% { transform: scale(0.2); opacity: 0.28; }\n  50% { transform: scale(1); opacity: 1; }\n}\n@keyframes pg-scale-violet {\n  0%, 100% { transform: scale(0.2); opacity: 0.28; }\n  50% { transform: scale(1.15); opacity: 1; }\n}\n@keyframes pg-scale-amber {\n  0%, 100% { transform: scale(0.1); opacity: 0; }\n  50% { transform: scale(1.2); opacity: 1; }\n}\n@keyframes pg-scale-mint {\n  0%, 100% { transform: scale(0.3); opacity: 0.35; }\n  50% { transform: scale(1); opacity: 1; }\n}\n@keyframes pg-scale-pink {\n  0%, 100% { transform: scale(0.1); opacity: 0; }\n  50% { transform: scale(1); opacity: 1; }\n}\n@keyframes pg-twist {\n  0%, 100% { transform: scale(0.2) rotate(0deg); opacity: 0.2; }\n  50% { transform: scale(1) rotate(180deg); opacity: 1; border-radius: 0; }\n}\n@keyframes pg-squash {\n  0%, 100% { transform: scaleY(0.2) scaleX(1.4); opacity: 0.2; }\n  50% { transform: scaleY(1.4) scaleX(0.7); opacity: 1; }\n}\n@keyframes pg-jelly {\n  0%, 100% { transform: scale(1, 1); opacity: 1; }\n  25% { transform: scale(1.4, 0.6); }\n  50% { transform: scale(0.6, 1.4); opacity: 0.7; }\n  75% { transform: scale(1.2, 0.8); }\n}\n@keyframes pg-pop-rotate {\n  0%, 100% { transform: scale(0) rotate(-180deg); opacity: 0; }\n  30%, 70% { transform: scale(1.1) rotate(0deg); opacity: 1; }\n}\n@keyframes pg-skew {\n  0%, 100% { transform: skewX(-30deg) scale(0.4); opacity: 0.2; }\n  50% { transform: skewX(30deg) scale(1); opacity: 1; }\n}\n@keyframes pg-heartbeat {\n  0%, 100% { opacity: 0.25; }\n  20% { opacity: 1; }\n  40% { opacity: 0.5; }\n  60% { opacity: 1; }\n}\n@keyframes pg-drop {\n  0%, 100% { transform: translateY(-20px) scale(0); opacity: 0; }\n  40%, 60% { transform: translateY(0) scale(1); opacity: 1; }\n}\n@keyframes pg-burst {\n  0% { transform: scale(0); opacity: 0; }\n  20% { transform: scale(1.4); opacity: 1; }\n  40% { transform: scale(0.6); opacity: 0.7; }\n  60% { transform: scale(1); opacity: 1; }\n  80% { transform: scale(0.3); opacity: 0.3; }\n  100% { transform: scale(0); opacity: 0; }\n}\n@keyframes pg-spiral {\n  0%, 100% { transform: scale(0) rotate(0); opacity: 0; }\n  50% { transform: scale(1) rotate(360deg); opacity: 1; border-radius: 50%; }\n}\n@keyframes pg-zigzag {\n  0%, 100% { transform: rotate(-15deg) scale(0.3); opacity: 0.2; }\n  50% { transform: rotate(15deg) scale(1); opacity: 1; }\n}\n@keyframes pg-mandala-round {\n  0%, 100% { transform: scale(0.2); opacity: 0; }\n  50% { transform: scale(1); opacity: 1; }\n}\n@keyframes pg-mandala-square {\n  0%, 100% { transform: scale(0.2); opacity: 0; }\n  50% { transform: scale(1); opacity: 1; }\n}\n@keyframes pg-mandala-diamond {\n  0%, 100% { transform: scale(0.2); opacity: 0; }\n  50% { transform: scale(1.1); opacity: 1; }\n}\n@keyframes pg-mandala-cross {\n  0%, 100% { transform: scale(0.1); opacity: 0; }\n  50% { transform: scale(1); opacity: 1; }\n}\n@keyframes pg-mandala-x {\n  0%, 100% { transform: scale(0.1); opacity: 0; }\n  50% { transform: scale(1); opacity: 1; }\n}\n@keyframes pg-mandala-star {\n  0%, 100% { transform: scale(0.2) rotate(0deg); opacity: 0; }\n  50% { transform: scale(1) rotate(45deg); opacity: 1; }\n}\n@keyframes pg-mandala-petal {\n  0%, 100% { transform: scale(0.1); opacity: 0; }\n  50% { transform: scale(1); opacity: 1; }\n}\n@keyframes pg-mandala-snow {\n  0%, 100% { transform: scale(0.1) rotate(-90deg); opacity: 0; }\n  50% { transform: scale(1) rotate(0deg); opacity: 1; }\n}\n@keyframes pg-mandala-gear {\n  0% { transform: scale(0.3) rotate(0deg); opacity: 0.3; }\n  50% { transform: scale(1) rotate(45deg); opacity: 1; }\n  100% { transform: scale(0.3) rotate(90deg); opacity: 0.3; }\n}\n@keyframes pg-mandala-kaleido {\n  0%, 100% { transform: scale(0.2); opacity: 0; }\n  50% { transform: scale(1); opacity: 1; }\n  75% { opacity: 0.8; }\n}\n@keyframes pg-mandala-spiral {\n  0%, 100% { transform: scale(0) rotate(0); opacity: 0; }\n  50% { transform: scale(1) rotate(360deg); opacity: 1; }\n}\n@keyframes pg-mandala-pulse {\n  0%, 100% { transform: scale(0.4); opacity: 0.3; }\n  50% { transform: scale(1.1); opacity: 1; }\n}\n@keyframes pg-mandala-checker {\n  0%, 100% { transform: scale(0.2); opacity: 0; }\n  50% { transform: scale(1); opacity: 1; }\n}\n@keyframes pg-mandala-oct {\n  0%, 100% { transform: scale(0.2); opacity: 0; }\n  50% { transform: scale(1); opacity: 1; }\n}\n@keyframes pg-mandala-lotus {\n  0%, 100% { transform: scale(0.1) rotate(-45deg); opacity: 0; }\n  35% { opacity: 0.65; }\n  50% { transform: scale(1.1) rotate(0deg); opacity: 1; }\n  65% { opacity: 0.75; }\n}\n/* ---------------------------------------------------------------------------\n   Ported from the dot-matrix reference (~/Desktop/matrix, loaders/styles.css).\n   Their keyframes express opacity as calc() over --dmx-opacity-base/mid/peak;\n   those resolve to 0.16 / 0.32 / 1 by default, so the arithmetic is done here\n   and the literal result is inlined. base 0.16 matches our own resting opacity.\n   --------------------------------------------------------------------------- */\n\n/* dmx-diagonal-alt-sweep — a hard flash with a fast decay */\n@keyframes dmx-neon-drift {\n  0%, 100% { opacity: 0.08; }\n  14% { opacity: 1; }\n  30% { opacity: 0.12; }\n}\n\n/* dmx-spiral-snake / dmx-diagonal-snake — a ~4-dot tail decaying behind the head */\n@keyframes dmx-snake-trail {\n  0%, 100% { opacity: 0.08; }\n  8% { opacity: 1; }\n  16% { opacity: 0.644; }\n  24% { opacity: 0.442; }\n  32% { opacity: 0.24; }\n  40% { opacity: 0.12; }\n}\n\n/* dmx-ripple-echo — peak, fall, then a secondary swell */\n@keyframes dmx-ripple-echo {\n  0%, 100% { opacity: 0.1; }\n  28% { opacity: 0.98; }\n  56% { opacity: 0.32; }\n  78% { opacity: 0.782; }\n}\n\n/* dmx-center-origin-ripple */\n@keyframes dmx-origin-wave {\n  0%, 100% { opacity: 0.1; }\n  34% { opacity: 1; }\n  60% { opacity: 0.24; }\n}\n\n/* dmx-square6-col-snake — five discrete levels, driven with steps(5, end) */\n@keyframes dmx-flux-columns {\n  0%, 20% { opacity: 0.704; }\n  20%, 40% { opacity: 0.492; }\n  40%, 60% { opacity: 0.256; }\n  60%, 80% { opacity: 0.192; }\n  80%, 100% { opacity: 0.1; }\n}\n\n/* ---- New pattern keyframes ---- */\n\n/* Sonar beam: instant flash, long tail. Combined with an angular delay this\n   reads as a rotating sweep line leaving a decaying wake. */\n@keyframes pg-sonar {\n  0% { opacity: 1; }\n  8% { opacity: 0.72; }\n  22% { opacity: 0.4; }\n  45% { opacity: 0.22; }\n  100% { opacity: 0.16; }\n}\n\n/* Domino topple: compresses, overshoots, settles. */\n@keyframes pg-domino {\n  0%, 100% { opacity: 0.16; transform: scaleY(1); }\n  10% { opacity: 1; transform: scaleY(0.5); }\n  20% { opacity: 0.9; transform: scaleY(1.2); }\n  32% { opacity: 0.45; transform: scaleY(0.95); }\n}\n\n/* Crisp frame: snaps on, holds, drops back with a small dip. */\n@keyframes pg-frame {\n  0%, 100% { opacity: 0.16; transform: scale(0.82); }\n  18%, 34% { opacity: 1; transform: scale(1); }\n  52% { opacity: 0.28; transform: scale(0.9); }\n}\n\n/* ---------------------------------------------------------------------------\n   Multi-channel field support.\n\n   A dot can run several animations at once, each with its own linear() easing,\n   so opacity / glow / scale follow independently shaped curves. All three are\n   compositor properties, so the cost is still zero JS per frame.\n\n   Glow is a pseudo-element whose opacity animates, NOT an animated box-shadow.\n   Animating shadow blur repaints every frame; animating a pre-rendered layer's\n   opacity does not.\n   --------------------------------------------------------------------------- */\n/* .hl-px is already `position: absolute` above, which is itself a containing\n   block for ::after. A second `position` declaration here previously overrode\n   that absolute with relative — and since the absolute rule is what supplies\n   the dot's size via inset:0, every dot collapsed to zero. */\n.hl-matrix .hl-px::after {\n  content: \"\";\n  position: absolute;\n  inset: -60%;\n  border-radius: 50%;\n  background: radial-gradient(circle, var(--loader-color) 0%, transparent 70%);\n  opacity: 0;\n  pointer-events: none;\n}\n\n/* Shared ramps. The linear() easing on each animation reshapes these into the\n   sampled curve, so one keyframe per channel serves every loader. */\n@keyframes hl-field { from { opacity: 0; } to { opacity: 1; } }\n@keyframes hl-field-glow { from { opacity: 0; } to { opacity: 1; } }\n@keyframes hl-field-scale { from { scale: 0; } to { scale: 1; } }\n\n@media (prefers-reduced-motion: reduce) {\n  .hl-matrix,\n  .hl-matrix .hl-px,\n  .hl-matrix .hl-px::after {\n    animation-duration: 0.001ms !important;\n    animation-iteration-count: 1 !important;\n  }\n}\n",
      "type": "registry:file",
      "target": "components/soonish/styles/base.css"
    },
    {
      "path": "packages/soonish/src/styles/fields.generated.css",
      "content": "/* GENERATED by scripts/compile-fields.ts — do not edit by hand. */\n/* Front Sweep — 3 curve(s), 3 element rule(s), 0 glow rule(s) */\n.hl-f-front-sweep .hl-d0 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0,0,0,0,0,0.001,0.002,0.007,0.022,0.063,0.151,0.302,0.501,0.702,0.859,0.952,0.99,0.999,1,0.999,0.99,0.952,0.859,0.702,0.501,0.302,0.151,0.063,0.022,0.007,0.002,0.001,0,0,0,0,0); }\n.hl-f-front-sweep .hl-d1 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.002,0.002,0.003,0.006,0.012,0.027,0.063,0.14,0.285,0.501,0.745,0.933,1,0.945,0.822,0.69,0.586,0.522,0.501,0.522,0.586,0.69,0.822,0.945,1,0.933,0.745,0.501,0.285,0.14,0.063,0.027,0.012,0.006,0.003,0.002,0.002); }\n.hl-f-front-sweep .hl-d2 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.063,0.068,0.087,0.125,0.197,0.319,0.501,0.723,0.92,1,0.92,0.723,0.501,0.319,0.197,0.125,0.087,0.068,0.063,0.068,0.087,0.125,0.197,0.319,0.501,0.723,0.92,1,0.92,0.723,0.501,0.319,0.197,0.125,0.087,0.068,0.063); }\n\n/* Ripple Echo — 6 curve(s), 6 element rule(s), 0 glow rule(s) */\n.hl-f-ripple-echo .hl-d0 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.013,0.02,0.029,0.042,0.059,0.081,0.107,0.139,0.175,0.216,0.26,0.305,0.348,0.388,0.421,0.447,0.463,0.468,0.013,0.02,0.029,0.042,0.059,0.081,0.107,0.139,0.175,0.216,0.26,0.305,0.348,0.388,0.421,0.447,0.463,0.468,0.013); }\n.hl-f-ripple-echo .hl-d1 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.188,0.235,0.287,0.342,0.397,0.45,0.497,0.536,0.564,0.58,0.583,0.574,0.555,0.528,0.497,0.467,0.44,0.421,0.188,0.235,0.287,0.342,0.397,0.45,0.497,0.536,0.564,0.58,0.583,0.574,0.555,0.528,0.497,0.467,0.44,0.421,0.188); }\n.hl-f-ripple-echo .hl-d2 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.365,0.425,0.482,0.533,0.576,0.607,0.625,0.63,0.621,0.601,0.573,0.54,0.507,0.478,0.457,0.445,0.446,0.457,0.365,0.425,0.482,0.533,0.576,0.607,0.625,0.63,0.621,0.601,0.573,0.54,0.507,0.478,0.457,0.445,0.446,0.457,0.365); }\n.hl-f-ripple-echo .hl-d3 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.743,0.729,0.702,0.667,0.628,0.591,0.56,0.538,0.529,0.533,0.551,0.579,0.615,0.654,0.691,0.721,0.74,0.744,0.743,0.729,0.702,0.667,0.628,0.591,0.56,0.538,0.529,0.533,0.551,0.579,0.615,0.654,0.691,0.721,0.74,0.744,0.743); }\n.hl-f-ripple-echo .hl-d4 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.65,0.617,0.596,0.588,0.596,0.618,0.651,0.692,0.735,0.774,0.805,0.823,0.825,0.809,0.776,0.726,0.664,0.592,0.65,0.617,0.596,0.588,0.596,0.618,0.651,0.692,0.735,0.774,0.805,0.823,0.825,0.809,0.776,0.726,0.664,0.592,0.65); }\n.hl-f-ripple-echo .hl-d5 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(1,0.981,0.928,0.857,0.772,0.679,0.583,0.488,0.398,0.317,0.247,0.187,0.138,0.099,0.07,0.048,0.032,0.021,1,0.981,0.928,0.857,0.772,0.679,0.583,0.488,0.398,0.317,0.247,0.187,0.138,0.099,0.07,0.048,0.032,0.021,1); }\n\n/* Pulse Beat — 5 curve(s), 5 element rule(s), 0 glow rule(s) */\n.hl-f-pulse-beat .hl-d0 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.001,0.07,0.268,0.522,0.727,0.838,0.867,0.839,0.772,0.694,0.646,0.659,0.737,0.849,0.944,0.99,0.999,0.994,0.947,0.8,0.553,0.296,0.122,0.047,0.03,0.046,0.09,0.148,0.187,0.176,0.115,0.04,0.001,0,0,0,0.001); }\n.hl-f-pulse-beat .hl-d1 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.21,0.468,0.718,0.876,0.94,0.945,0.909,0.834,0.747,0.689,0.69,0.753,0.847,0.929,0.971,0.977,0.946,0.836,0.619,0.347,0.129,0.025,0.002,0.001,0.011,0.049,0.108,0.152,0.151,0.103,0.041,0.004,0,0,0.001,0.048,0.21); }\n.hl-f-pulse-beat .hl-d2 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.765,0.932,0.991,0.999,0.994,0.959,0.871,0.757,0.667,0.639,0.674,0.748,0.821,0.858,0.841,0.747,0.558,0.307,0.094,0.003,0,0,0,0,0.028,0.1,0.17,0.193,0.164,0.107,0.057,0.035,0.045,0.108,0.263,0.51,0.765); }\n.hl-f-pulse-beat .hl-d3 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.973,1,1,0.995,0.95,0.847,0.722,0.631,0.605,0.642,0.715,0.782,0.808,0.772,0.651,0.442,0.202,0.037,0,0,0,0,0,0.041,0.126,0.2,0.222,0.19,0.132,0.083,0.065,0.09,0.183,0.372,0.628,0.854,0.973); }\n.hl-f-pulse-beat .hl-d4 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(1,1,0.985,0.886,0.704,0.514,0.384,0.333,0.35,0.407,0.465,0.482,0.43,0.304,0.14,0.021,0,0,0,0,0,0.021,0.14,0.304,0.43,0.482,0.465,0.407,0.35,0.333,0.384,0.514,0.704,0.886,0.985,1,1); }\n\n/* Lumen Bloom — 3 curve(s), 1 element rule(s), 1 glow rule(s) */\n.hl-f-lumen-bloom .hl-d0 { animation-name: hl-field,hl-field-scale; animation-duration: var(--hl-dur),var(--hl-dur); animation-delay: var(--hl-delay,0s),var(--hl-delay,0s); animation-iteration-count: infinite,infinite; animation-fill-mode: both,both; animation-timing-function: linear(0.711,0.559,0.408,0.275,0.172,0.1,0.054,0.027,0.013,0.006,0.002,0.001,0,0,0.001,0.002,0.005,0.011,0.024,0.047,0.089,0.156,0.252,0.379,0.528,0.681,0.817,0.917,0.974,0.996,1,1,0.998,0.98,0.931,0.84,0.711),linear(0.542,0.412,0.304,0.223,0.169,0.136,0.118,0.108,0.103,0.101,0.101,0.1,0.1,0.101,0.103,0.106,0.114,0.129,0.157,0.203,0.275,0.374,0.499,0.637,0.77,0.879,0.952,0.988,0.999,1,1,0.994,0.967,0.907,0.808,0.681,0.542); }\n.hl-f-lumen-bloom .hl-g0::after { animation-name: hl-field-glow; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.81,0.74,0.628,0.487,0.341,0.211,0.112,0.048,0.014,0.001,0,0,0,0,0,0,0,0,0,0,0,0,0.009,0.037,0.093,0.184,0.307,0.451,0.596,0.716,0.797,0.837,0.849,0.85,0.85,0.842,0.81); }\n\n/* Raindrop — 11 curve(s), 11 element rule(s), 0 glow rule(s) */\n.hl-f-raindrop .hl-d0 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.001,0.003,0.009,0.023,0.052,0.104,0.184,0.288,0.402,0.497,0.545,0.531,0.458,0.35,0); }\n.hl-f-raindrop .hl-d1 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0,0.001,0.003,0.01,0.025,0.051,0.087,0.12,0.135,0.125,0.094,0.058,0.029,0.012,0.004,0.001,0,0,0.001,0.002,0.006,0.016,0.038,0.082,0.156,0.262,0.392,0.521,0.614,0.642,0.596,0.491,0.359,0.233,0.134,0.068,0); }\n.hl-f-raindrop .hl-d2 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.001,0.006,0.024,0.073,0.184,0.38,0.641,0.885,0.999,0.922,0.695,0.428,0.216,0.089,0.03,0.008,0.002,0.001,0.003,0.009,0.024,0.054,0.111,0.202,0.325,0.466,0.593,0.67,0.672,0.598,0.472,0.331,0.206,0.113,0.055,0.024,0.001); }\n.hl-f-raindrop .hl-d3 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.001,0.002,0.006,0.016,0.038,0.082,0.156,0.262,0.392,0.521,0.614,0.642,0.596,0.491,0.359,0.233,0.134,0.068,0); }\n.hl-f-raindrop .hl-d4 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0,0,0,0,0,0.001,0.003,0.009,0.024,0.05,0.085,0.118,0.135,0.126,0.096,0.06,0.03,0.037,0.082,0.161,0.279,0.431,0.59,0.717,0.774,0.742,0.632,0.478,0.321,0.191,0.101,0.047,0.02,0.007,0.002,0.001,0); }\n.hl-f-raindrop .hl-d5 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0,0,0,0,0.001,0.006,0.022,0.069,0.176,0.368,0.628,0.875,0.998,0.93,0.708,0.441,0.225,0.221,0.365,0.537,0.701,0.814,0.839,0.768,0.624,0.45,0.289,0.164,0.083,0.037,0.015,0.005,0.002,0,0,0,0); }\n.hl-f-raindrop .hl-d6 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.001,0.003,0.009,0.024,0.054,0.111,0.202,0.325,0.466,0.593,0.67,0.672,0.598,0.472,0.331,0.206,0.113,0.055,0.024,0); }\n.hl-f-raindrop .hl-d7 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0,0,0,0,0,0,0,0,0,0.001,0.003,0.009,0.023,0.048,0.083,0.117,0.135,0.221,0.365,0.537,0.701,0.814,0.839,0.768,0.624,0.45,0.289,0.164,0.083,0.037,0.015,0.005,0.002,0,0,0,0); }\n.hl-f-raindrop .hl-d8 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0,0,0,0,0,0,0,0,0.001,0.005,0.021,0.066,0.169,0.356,0.614,0.865,0.996,0.942,0.785,0.582,0.383,0.224,0.116,0.054,0.022,0.008,0.003,0.001,0,0,0,0,0,0,0,0,0); }\n.hl-f-raindrop .hl-d9 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0,0,0,0,0,0,0,0,0,0,0,0,0,0.001,0.003,0.008,0.022,0.037,0.082,0.161,0.279,0.431,0.59,0.717,0.774,0.742,0.632,0.478,0.321,0.191,0.101,0.047,0.02,0.007,0.002,0.001,0); }\n.hl-f-raindrop .hl-d10 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0,0,0,0,0,0,0,0,0,0,0,0,0.001,0.005,0.02,0.062,0.162,0.221,0.365,0.537,0.701,0.814,0.839,0.768,0.624,0.45,0.289,0.164,0.083,0.037,0.015,0.005,0.002,0,0,0,0); }\n\n/* Pendulum — 11 curve(s), 12 element rule(s), 0 glow rule(s) */\n.hl-f-pendulum .hl-d0 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0,0,0,0,0.002,0.006,0.015,0.029,0.042,0.048,0.042,0.029,0.015,0.006,0.002,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); }\n.hl-f-pendulum .hl-d1 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0,0,0,0,0,0,0.001,0.003,0.004,0.005,0.004,0.003,0.001,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); }\n.hl-f-pendulum .hl-d3 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0,0,0.001,0.004,0.016,0.045,0.093,0.148,0.191,0.208,0.191,0.148,0.093,0.045,0.016,0.004,0.001,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); }\n.hl-f-pendulum .hl-d4 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.001,0.005,0.024,0.08,0.191,0.345,0.499,0.615,0.681,0.701,0.681,0.615,0.499,0.345,0.191,0.08,0.024,0.005,0.001,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.001); }\n.hl-f-pendulum .hl-d5 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.001,0.004,0.018,0.061,0.147,0.265,0.383,0.473,0.523,0.539,0.523,0.473,0.383,0.265,0.147,0.061,0.018,0.004,0.001,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.001); }\n.hl-f-pendulum .hl-d6 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.027,0.1,0.261,0.502,0.732,0.862,0.882,0.842,0.797,0.779,0.797,0.842,0.882,0.862,0.732,0.502,0.261,0.1,0.027,0.006,0.001,0,0,0,0,0,0,0,0,0,0,0,0,0,0.001,0.006,0.027); }\n.hl-f-pendulum .hl-d7 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.126,0.302,0.528,0.693,0.717,0.628,0.505,0.404,0.343,0.323,0.343,0.404,0.505,0.628,0.717,0.693,0.528,0.302,0.126,0.039,0.009,0.002,0,0,0,0,0,0,0,0,0,0,0,0.002,0.009,0.039,0.126); }\n.hl-f-pendulum .hl-d8 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.644,0.555,0.362,0.188,0.084,0.036,0.016,0.008,0.005,0.005,0.005,0.008,0.016,0.036,0.084,0.188,0.362,0.555,0.644,0.555,0.362,0.188,0.084,0.036,0.016,0.008,0.005,0.005,0.005,0.008,0.016,0.036,0.084,0.188,0.362,0.555,0.644); }\n.hl-f-pendulum .hl-d9 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.174,0.416,0.729,0.956,0.989,0.866,0.696,0.557,0.473,0.446,0.473,0.557,0.696,0.866,0.989,0.956,0.729,0.416,0.174,0.054,0.013,0.003,0.001,0,0,0,0,0,0,0,0,0,0.001,0.003,0.013,0.054,0.174); }\n.hl-f-pendulum .hl-d10 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.481,0.757,0.884,0.793,0.582,0.379,0.239,0.16,0.122,0.111,0.122,0.16,0.239,0.379,0.582,0.793,0.884,0.757,0.481,0.227,0.082,0.025,0.007,0.002,0.001,0,0,0,0,0,0.001,0.002,0.007,0.025,0.082,0.227,0.481); }\n.hl-f-pendulum .hl-d11 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.839,0.723,0.471,0.244,0.109,0.046,0.021,0.011,0.007,0.006,0.007,0.011,0.021,0.046,0.109,0.244,0.471,0.723,0.839,0.723,0.471,0.244,0.109,0.046,0.021,0.011,0.007,0.006,0.007,0.011,0.021,0.046,0.109,0.244,0.471,0.723,0.839); }\n\n/* Firefly — 16 curve(s), 16 element rule(s), 0 glow rule(s) */\n.hl-f-firefly .hl-d0 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.01,0.184,0.483,0.788,0.981,1,1,1,0.932,0.751,0.511,0.269,0.08,0,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05); }\n.hl-f-firefly .hl-d1 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.048,0.277,0.591,0.872,1,1,1,0.993,0.881,0.674,0.427,0.196,0.038,0.05,0.05,0.05,0.05,0.05,0.05); }\n.hl-f-firefly .hl-d2 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.019,0.212,0.517,0.816,0.99,1,1,1,0.918,0.727,0.485,0.246,0.066,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05); }\n.hl-f-firefly .hl-d3 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.401,0.986,1,0.818,0.27,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05); }\n.hl-f-firefly .hl-d4 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.063,0.377,0.761,0.992,1,1,0.953,0.736,0.436,0.158,0.006,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05); }\n.hl-f-firefly .hl-d5 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.996,0.78,0.393,0.069,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.141,0.62,0.984,1,0.996); }\n.hl-f-firefly .hl-d6 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.87,0.602,0.299,0.066,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0,0.185,0.559,0.9,1,1,0.999,0.87); }\n.hl-f-firefly .hl-d7 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.011,0.248,0.633,0.942,1,1,0.99,0.826,0.544,0.246,0.038,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05); }\n.hl-f-firefly .hl-d8 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.038,0.326,0.713,0.978,1,1,0.97,0.772,0.478,0.19,0.015,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05); }\n.hl-f-firefly .hl-d9 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.196,0.689,0.998,1,0.985,0.731,0.34,0.042,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05); }\n.hl-f-firefly .hl-d10 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.165,0.533,0.883,1,1,1,0.883,0.621,0.317,0.077,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05); }\n.hl-f-firefly .hl-d11 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.329,0.96,1,0.862,0.324,0.05,0.05,0.05,0.05,0.05); }\n.hl-f-firefly .hl-d12 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.079,0.334,0.65,0.911,1,1,1,0.982,0.847,0.628,0.38,0.159,0.021,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05); }\n.hl-f-firefly .hl-d13 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.05,0.05,0.005,0.333,0.823,1,1,0.938,0.618,0.23,0.005,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05); }\n.hl-f-firefly .hl-d14 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0,0.121,0.397,0.712,0.947,1,1,1,0.964,0.808,0.578,0.331,0.122,0.008,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05); }\n.hl-f-firefly .hl-d15 { animation-name: hl-field; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.046,0.685,1,0.996,0.616,0.096,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05); }\n\n/* Halo — 15 curve(s), 5 element rule(s), 5 glow rule(s) */\n.hl-f-halo .hl-d0 { animation-name: hl-field,hl-field-scale; animation-duration: var(--hl-dur),var(--hl-dur); animation-delay: var(--hl-delay,0s),var(--hl-delay,0s); animation-iteration-count: infinite,infinite; animation-fill-mode: both,both; animation-timing-function: linear(0,0,0,0,0,0,0,0,0,0,0,0,0,0.001,0.002,0.004,0.008,0.015,0.027,0.046,0.077,0.122,0.185,0.268,0.37,0.489,0.617,0.744,0.857,0.943,0.992,0.996,0.956,0.877,0.768,0.643,0),linear(0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.301,0.301,0.303,0.306,0.311,0.32,0.334,0.356,0.388,0.433,0.493,0.565,0.649,0.739,0.828,0.906,0.964,0.996,0.996,0.966,0.908,0.831,0.743,0.3,0.3); }\n.hl-f-halo .hl-d1 { animation-name: hl-field,hl-field-scale; animation-duration: var(--hl-dur),var(--hl-dur); animation-delay: var(--hl-delay,0s),var(--hl-delay,0s); animation-iteration-count: infinite,infinite; animation-fill-mode: both,both; animation-timing-function: linear(0,0,0,0,0,0,0.001,0.001,0.003,0.005,0.01,0.019,0.034,0.057,0.093,0.145,0.216,0.307,0.417,0.541,0.67,0.793,0.896,0.968,0.999,0.985,0.928,0.835,0.718,0.59,0.463,0.347,0.249,0.17,0.111,0.069,0),linear(0.3,0.3,0.3,0.3,0.3,0.3,0.301,0.302,0.304,0.307,0.314,0.325,0.342,0.368,0.405,0.456,0.521,0.598,0.686,0.776,0.861,0.932,0.981,1,0.987,0.945,0.879,0.796,0.706,0.617,0.537,0.469,0.415,0.375,0.347,0.3,0.3); }\n.hl-f-halo .hl-d2 { animation-name: hl-field,hl-field-scale; animation-duration: var(--hl-dur),var(--hl-dur); animation-delay: var(--hl-delay,0s),var(--hl-delay,0s); animation-iteration-count: infinite,infinite; animation-fill-mode: both,both; animation-timing-function: linear(0,0,0,0,0.001,0.002,0.004,0.008,0.014,0.026,0.045,0.076,0.12,0.182,0.264,0.366,0.484,0.612,0.739,0.853,0.941,0.991,0.997,0.959,0.88,0.773,0.648,0.519,0.397,0.29,0.203,0.135,0.086,0.052,0.031,0.017,0),linear(0.3,0.3,0.3,0.301,0.301,0.303,0.306,0.311,0.319,0.333,0.355,0.387,0.432,0.49,0.562,0.646,0.736,0.824,0.903,0.962,0.995,0.997,0.968,0.911,0.834,0.746,0.656,0.571,0.498,0.438,0.391,0.358,0.335,0.32,0.311,0.3,0.3); }\n.hl-f-halo .hl-d3 { animation-name: hl-field,hl-field-scale; animation-duration: var(--hl-dur),var(--hl-dur); animation-delay: var(--hl-delay,0s),var(--hl-delay,0s); animation-iteration-count: infinite,infinite; animation-fill-mode: both,both; animation-timing-function: linear(0.069,0.111,0.169,0.248,0.346,0.462,0.589,0.717,0.834,0.927,0.985,0.999,0.969,0.897,0.794,0.671,0.542,0.418,0.308,0.217,0.146,0.094,0.058,0.034,0.019,0.01,0.005,0.003,0.001,0.001,0,0,0,0,0,0,0.069),linear(0.38,0.422,0.478,0.548,0.63,0.719,0.809,0.89,0.953,0.991,0.999,0.975,0.923,0.849,0.762,0.672,0.586,0.51,0.447,0.399,0.363,0.339,0.323,0.313,0.307,0.303,0.302,0.301,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.35,0.38); }\n.hl-f-halo .hl-d4 { animation-name: hl-field,hl-field-scale; animation-duration: var(--hl-dur),var(--hl-dur); animation-delay: var(--hl-delay,0s),var(--hl-delay,0s); animation-iteration-count: infinite,infinite; animation-fill-mode: both,both; animation-timing-function: linear(1,0.977,0.913,0.814,0.694,0.565,0.44,0.327,0.232,0.157,0.102,0.063,0.037,0.021,0.011,0.006,0.003,0.001,0.001,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1),linear(0.982,0.934,0.864,0.779,0.688,0.601,0.523,0.458,0.407,0.369,0.342,0.325,0.314,0.308,0.304,0.302,0.301,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,1,0.982); }\n.hl-f-halo .hl-g0::after { animation-name: hl-field-glow; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.745,0.638,0.523,0,0,0,0,0,0,0,0,0,0,0,0,0.001,0.001,0.002,0.005,0.009,0.018,0.031,0.053,0.087,0.135,0.2,0.283,0.383,0.495,0.611,0.721,0.812,0.875,0.9,0.884,0.83,0.745); }\n.hl-f-halo .hl-g1::after { animation-name: hl-field-glow; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.126,0.08,0.049,0,0,0,0,0,0,0.001,0.002,0.003,0.006,0.012,0.023,0.039,0.066,0.105,0.159,0.232,0.322,0.427,0.542,0.657,0.761,0.842,0.89,0.898,0.867,0.799,0.703,0.591,0.475,0.365,0.268,0.188,0.126); }\n.hl-f-halo .hl-g2::after { animation-name: hl-field-glow; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0.037,0.021,0.011,0,0,0,0,0.001,0.002,0.005,0.009,0.017,0.031,0.052,0.085,0.133,0.197,0.279,0.379,0.49,0.607,0.717,0.809,0.873,0.899,0.886,0.833,0.748,0.642,0.527,0.413,0.309,0.221,0.151,0.099,0.061,0.037); }\n.hl-f-halo .hl-g3::after { animation-name: hl-field-glow; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0,0,0,0.078,0.123,0.184,0.263,0.36,0.47,0.586,0.698,0.794,0.864,0.898,0.891,0.845,0.766,0.663,0.548,0.433,0.327,0.236,0.162,0.107,0.067,0.04,0.023,0.013,0.007,0.003,0.002,0.001,0,0,0,0,0); }\n.hl-f-halo .hl-g4::after { animation-name: hl-field-glow; animation-duration: var(--hl-dur); animation-delay: var(--hl-delay,0s); animation-iteration-count: infinite; animation-fill-mode: both; animation-timing-function: linear(0,0,0,0.895,0.856,0.782,0.683,0.569,0.453,0.345,0.251,0.174,0.116,0.073,0.044,0.026,0.014,0.008,0.004,0.002,0.001,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); }\n",
      "type": "registry:file",
      "target": "components/soonish/styles/fields.generated.css"
    }
  ],
  "type": "registry:lib"
}