@pixodesk/svg-animator-web 1.0.41 → 1.0.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../svg-animator-core/src/schema/PxSchema.ts","../../svg-animator-core/src/version/PxSchemaVersion.ts","../../svg-animator-core/src/version/PxWireVersion.ts","../../svg-animator-core/src/format/PxAnimatorConstants.ts","../../svg-animator-core/src/format/PxAnimatorTypes.ts","../../svg-animator-core/src/util/PxIdUtil.ts","../../svg-animator-core/src/util/PxAnimatorUtil.ts","../../svg-animator-core/src/util/PxNodeProps.ts","../../svg-animator-core/src/materialize/PxMotionPath.ts","../../svg-animator-core/src/animation/PxDefinitions.ts","../../svg-animator-core/src/effects/text/pathSampler.ts","../../svg-animator-core/src/effects/shared/transformParts.ts","../../svg-animator-core/src/util/PxNodeCloneUtil.ts","../../svg-animator-core/src/effects/shared/util.ts","../../svg-animator-core/src/effects/text/textPathEffect.ts","../../svg-animator-core/src/effects/text/elementFactory.ts","../../svg-animator-core/src/effects/text/glyphPathBake.ts","../../svg-animator-core/src/effects/text/textGlyphsEffect.ts","../../svg-animator-core/src/playback/PxDiagnostics.ts","../../svg-animator-core/src/format/PxDocumentDiagnostic.ts","../../svg-animator-core/src/effects/transform/transformationEffect.ts","../../svg-animator-core/src/effects/reference/contentRefSplit.ts","../../svg-animator-core/src/effects/paint/gradientEffect.ts","../../svg-animator-core/src/effects/clipping/clipPathEffect.ts","../../svg-animator-core/src/effects/clipping/maskedByEffect.ts","../../svg-animator-core/src/effects/reference/refEffect.ts","../../svg-animator-core/src/effects/transform/repeaterEffect.ts","../../svg-animator-core/src/effects/reference/retimeEffect.ts","../../svg-animator-core/src/effects/stroke/strokeTrimEffect.ts","../../svg-animator-core/src/effects/PlayerEffectsUtil.ts","../../svg-animator-core/src/materialize/PxOffsetPathMaterializer.ts","../../svg-animator-core/src/materialize/PxAnimatorUseMaterializer.ts","../../svg-animator-core/src/materialize/PxAnimatorMaterializeAll.ts","../../svg-animator-core/src/playback/PxPlaybackTime.ts","../../svg-animator-core/src/playback/PxFrameLoop.ts","../../svg-animator-core/src/playback/PxDiagnosticCode.ts","../../svg-animator-core/src/playback/PxAnimatorConfigPatch.ts","../src/triggers/PxVisibilityGate.ts","../src/shared/PxAnimatorKeys.ts"],"sourcesContent":["/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n/**\n * Lightweight Zod-like schema system — type declaration + runtime sanitization.\n *\n * Two operations per schema:\n * isValid(raw) — true only when raw already conforms; no repair needed.\n * sanitize(raw) — always returns T; fixes or replaces invalid input with defaults.\n *\n * Field rules inside px.object():\n * required field → if absent/invalid, field's declared default is used.\n * optional field → if absent/invalid (or wrong JS type), field becomes undefined.\n *\n * Array: items that cannot even be attempted are filtered out.\n * Record: values that cannot even be attempted are dropped.\n *\n * The distinction between isValid and _canSanitize:\n * isValid — strict; ALL fields must be correct, no repairs accepted.\n * _canSanitize — permissive; \"is the JS type right enough to attempt repair?\"\n * For containers (object/array/record) this is a structural check\n * so that partially-valid nested objects are sanitized rather than dropped.\n * For primitives it equals isValid (a wrong primitive type is unrecoverable).\n *\n * Validation context (optional):\n * Pass a PxValidationContext as the second arg to isValid() to collect per-field\n * error and warning messages with their dot-paths.\n * e.g. schema.isValid(raw, ctx, []) where ctx = { errors: [], warnings: [] }\n * Path segments are pushed/popped during recursion; join with '.' only when reporting.\n */\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Public interfaces\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The reason text a STRICT closed object reports for an undeclared key — `\"<dot.path>: <this>\"`.\n *\n * Exported because it is the one validation finding that carries a MEANING beyond \"invalid\":\n * a key this build does not declare is a key some other build wrote, which is the signal a\n * reader turns into \"written for a newer schema — update your player/editor\". Callers match on\n * this constant rather than on a copy of the sentence.\n * @internal\n */\nexport const PX_UNKNOWN_KEY_ERROR = 'unexpected extra key';\n\n/** Collects structured validation feedback. Pass to isValid() as the second argument. @public @advanced */\nexport interface PxValidationContext {\n /** Per-field errors — each entry is \"<dot.path>: <reason>\". */\n errors: Array<string>;\n /** Per-field warnings — same format as errors but non-fatal. */\n warnings: Array<string>;\n /**\n * When true, closed objects (`px.object` / `px.extendedObject`) report any\n * extra (undeclared) keys as errors. Default `false` (or omitted) — extras\n * are ignored, matching `sanitize`'s strip-extras behavior, which is what\n * production app code wants (forward-compat with unknown future fields).\n * Tests that want to lock the wire shape down to its declared schema\n * should pass `strict: true`.\n *\n * Open objects (`px.openObject`) are unaffected by `strict` — they accept\n * extras by design (used for SVG element nodes that carry arbitrary attrs).\n */\n strict?: boolean;\n}\n\n/** @public @advanced */\nexport interface PxSchema<T, IsOptional extends boolean = false> {\n /** Phantom discriminator — `false` for required schemas, `true` for optional. Used by InferShape. */\n readonly _optional: IsOptional;\n sanitize(raw: unknown): T;\n /**\n * Returns true when raw already conforms to this schema.\n * Pass an optional PxValidationContext to collect per-field error messages.\n * Pass a mutable Array<string> as `path` — segments are pushed/popped during\n * recursion so only a single array allocation is needed per validation call.\n */\n isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean;\n /** True if raw has the right structure to attempt sanitization (may still need repair). */\n _canSanitize(raw: unknown): boolean;\n readonly _default: T;\n /** Returns a new schema that marks this field as optional (?: in object shapes). */\n optional(): PxSchema<T | undefined, true>;\n}\n\n/** Extract the TypeScript type from a schema. @public @advanced */\nexport type PxInfer<S> = S extends PxSchema<infer T, any> ? T : never;\n\n/**\n * Removes string/number index signatures from T, leaving only explicitly named properties.\n * Useful for strict property-access checking on types that have an open `[key: string]: any`.\n *\n * @example\n * type Strict = PxRemoveIndex<PxAnimatedSvgDocument>;\n * Strict['animator'] // PxAnimatorConfig | undefined ✓\n * Strict['anything'] // compile error ✓\n * @public @advanced\n */\nexport type PxRemoveIndex<T> = {\n [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K]\n};\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internal path helper\n// ─────────────────────────────────────────────────────────────────────────────\n\n// Joins path segments into a dot-path string for error messages.\n// Segments starting with '[' are appended without a leading dot.\n// e.g. ['obj', 'key'] -> 'obj.key' ['arr', '[0]'] -> 'arr[0]' [] -> '.'\nfunction pathStr(path: Array<string>): string {\n if (!path.length) return '.';\n let result = '';\n for (const seg of path) {\n if (seg.startsWith('[')) result += seg;\n else result += (result ? '.' : '') + seg;\n }\n return result;\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Base — default _canSanitize = isValid (correct for primitives)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Shared base; overrides _canSanitize only when the structural check must differ from isValid. */\nabstract class Base<T, IsOptional extends boolean = false> implements PxSchema<T, IsOptional> {\n // `declare` emits no runtime code; purely satisfies the interface's phantom _optional property.\n declare readonly _optional: IsOptional;\n abstract sanitize(raw: unknown): T;\n abstract isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean;\n abstract readonly _default: T;\n\n _canSanitize(raw: unknown): boolean { return this.isValid(raw); }\n\n optional(): PxSchema<T | undefined, true> { return new Optional(this); }\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Optional wrapper\n// Uses _canSanitize (not isValid) so that partially-valid nested objects are\n// repaired rather than dropped entirely.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Wraps any schema to make its value optional.\n * sanitize uses _canSanitize (not isValid) so partially-valid objects are repaired, not dropped.\n */\nclass Optional<T> extends Base<T | undefined, true> {\n readonly _default = undefined as T | undefined;\n constructor(private readonly inner: PxSchema<T, any>) { super(); }\n\n sanitize(raw: unknown): T | undefined {\n if (raw === undefined || raw === null) return undefined;\n return this.inner._canSanitize(raw) ? this.inner.sanitize(raw) : undefined;\n }\n\n isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean {\n if (raw === undefined || raw === null) return true;\n return this.inner.isValid(raw, ctx, path);\n }\n\n override _canSanitize(raw: unknown): boolean {\n return raw === undefined || raw === null || this.inner._canSanitize(raw);\n }\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Primitives — _canSanitize = isValid (a wrong primitive type is unrecoverable)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** String schema; wrong type is unrecoverable so _canSanitize = isValid (inherited default). */\nclass Str extends Base<string> {\n constructor(readonly _default: string = '') { super(); }\n sanitize(raw: unknown): string { return typeof raw === 'string' ? raw : this._default; }\n isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean {\n if (typeof raw === 'string') return true;\n ctx?.errors.push(pathStr(path ?? []) + ': expected string, got ' + typeof raw);\n return false;\n }\n}\n\n/** Finite-number schema; rejects NaN and ±Infinity as unrecoverable. */\nclass Num extends Base<number> {\n constructor(readonly _default: number = 0) { super(); }\n sanitize(raw: unknown): number {\n return typeof raw === 'number' && isFinite(raw) ? raw : this._default;\n }\n isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean {\n if (typeof raw === 'number' && isFinite(raw)) return true;\n ctx?.errors.push(pathStr(path ?? []) + ': expected finite number, got ' + JSON.stringify(raw));\n return false;\n }\n}\n\n/** Boolean schema. */\nclass Bool extends Base<boolean> {\n constructor(readonly _default: boolean = false) { super(); }\n sanitize(raw: unknown): boolean { return typeof raw === 'boolean' ? raw : this._default; }\n isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean {\n if (typeof raw === 'boolean') return true;\n ctx?.errors.push(pathStr(path ?? []) + ': expected boolean, got ' + typeof raw);\n return false;\n }\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Literal — exact value match; default is the literal itself\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Matches one exact primitive value; its _default is the value itself. */\nclass Literal<T extends string | number | boolean> extends Base<T> {\n readonly _default: T;\n constructor(private readonly value: T) { super(); this._default = value; }\n sanitize(raw: unknown): T { return raw === this.value ? this.value : this._default; }\n isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean {\n if (raw === this.value) return true;\n ctx?.errors.push(pathStr(path ?? []) + ': expected ' + JSON.stringify(this.value) + ', got ' + JSON.stringify(raw));\n return false;\n }\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Enum — union of string/number literals; default is first value\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Union of string/number literals; _default is the first value unless overridden. */\nclass Enum<T extends string | number> extends Base<T> {\n readonly _default: T;\n constructor(private readonly values: readonly T[], defaultVal?: T) {\n super();\n this._default = defaultVal ?? values[0];\n }\n sanitize(raw: unknown): T { return this.values.includes(raw as T) ? (raw as T) : this._default; }\n isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean {\n if (this.values.includes(raw as T)) return true;\n ctx?.errors.push(pathStr(path ?? []) + ': expected one of ' + this.values.map(v => JSON.stringify(v)).join(' | ') + ', got ' + JSON.stringify(raw));\n return false;\n }\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Union — first matching schema wins\n// _canSanitize: true if any member can attempt sanitization\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** How many of the best-matching member's errors a failed union appends after its headline\n * (review §2.8). Enough to name the offending key and its neighbours, short of a wall. */\nconst UNION_MEMBER_ERROR_LIMIT = 4;\n\n/** Tries member schemas in order; first whose isValid passes wins. sanitize returns _default when none match. */\nclass Union<T> extends Base<T> {\n /** Structural tag read by {@link describeSchema} — `schemas` alone cannot tell Union from Tuple. */\n readonly _kind = 'union' as const;\n readonly _default: T;\n constructor(private readonly schemas: ReadonlyArray<PxSchema<T>>, defaultVal?: T) {\n super();\n this._default = defaultVal ?? schemas[0]._default;\n }\n sanitize(raw: unknown): T {\n for (const s of this.schemas) {\n if (s.isValid(raw)) return s.sanitize(raw);\n }\n return this._default;\n }\n isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean {\n // Members see the MODE but NOT the caller's error sink (V6). Two separate\n // things were bundled in `ctx`: passing it whole made every non-matching\n // branch push errors even when a later branch matched; passing nothing at\n // all left `strict` inert inside every union. A scratch ctx carries the\n // flags and swallows the per-branch noise.\n const probe: PxValidationContext | undefined = ctx && { errors: [], warnings: [], strict: ctx.strict };\n if (this.schemas.some(s => s.isValid(raw, probe, path ? [...path] : undefined))) return true;\n if (!ctx) return false;\n\n const base = pathStr(path ?? []);\n ctx.errors.push(base + ': no union member matched for value ' + (JSON.stringify(raw) ?? '').slice(0, 240));\n\n // …then say WHY (review §2.8). The headline alone reads the same for a\n // `keyframes`/`keyframe` typo, for short `t`/`v` keys and for a plain type\n // mismatch, so an entry diagnostic — or an LLM repair loop — got no pointer to\n // the fix. Re-run each member into its OWN sink and report the one that got\n // FURTHEST into the value: the likeliest intended shape.\n //\n // Not every member's errors: the real unions run to 11 alternatives\n // (`PxKeyframeValueSchema`), so a typo inside one object member would arrive\n // buried under ten \"expected string, got object\" lines.\n let best: Array<string> | undefined;\n let bestDepth = -1;\n const leafExpectations: Array<string> = [];\n\n for (const member of this.schemas) {\n const sink: PxValidationContext = { errors: [], warnings: [], strict: ctx.strict };\n member.isValid(raw, sink, path ? [...path] : undefined);\n if (!sink.errors.length) continue; // cannot happen (it failed), but keeps this total\n\n // How far in did it get? Every message is \"<path>: <reason>\", and a member that\n // descended reports at a LONGER path than the union's own.\n const depth = Math.max(...sink.errors.map(e => e.slice(0, e.indexOf(':')).length));\n if (depth > bestDepth || (depth === bestDepth && best && sink.errors.length < best.length)) {\n bestDepth = depth;\n best = sink.errors;\n }\n // A member that stayed at the union's own path is a shape mismatch, not a\n // near-miss: collect just its expectation for the folded line below.\n if (depth <= base.length) {\n for (const e of sink.errors) {\n const m = /: expected (.+?), got /.exec(e);\n if (m && !leafExpectations.includes(m[1])) leafExpectations.push(m[1]);\n }\n }\n }\n\n if (best && bestDepth > base.length) {\n // One member reached inside the value — its errors ARE the diagnosis.\n for (const e of best.slice(0, UNION_MEMBER_ERROR_LIMIT)) {\n if (!ctx.errors.includes(e)) ctx.errors.push(e);\n }\n } else if (leafExpectations.length) {\n // Nothing descended: one line naming every shape this slot accepts.\n ctx.errors.push(base + ': expected ' + leafExpectations.join(' | '));\n }\n return false;\n }\n override _canSanitize(raw: unknown): boolean { return this.schemas.some(s => s._canSanitize(raw)); }\n}\n\n// Infers the union of all member types from a tuple of schemas.\n// Mapped-then-indexed, NOT `T extends ReadonlyArray<PxSchema<infer U>>`: the latter\n// gives TS one inference site for the whole array, so it collapses a heterogeneous\n// union to a single member (in practice the object branch) and the bare statics\n// vanish from the inferred type — `px.union([px.number(), px.object(…)])` typed as\n// the object alone. Per-position inference unions them properly (V6/Q1).\ntype UnionMembers<T extends ReadonlyArray<PxSchema<any, any>>> = {\n [K in keyof T]: T[K] extends PxSchema<infer U, any> ? U : never\n}[number];\n// NOTE: this only works because `px.union` / `px.discriminatedUnion` declare their\n// schema list as `const T` (Q1). Without the modifier TS infers the argument as a\n// plain ARRAY and unifies the element type to one `PxSchema<…>`, so `keyof T` has a\n// single member to map and the union collapses to whichever branch won unification —\n// in practice the last object branch, silently dropping every bare-static member\n// (`px.union([tuple, …, propAnim])` typed as propAnim alone). Source-level checks and\n// the emitted .d.ts both went wrong, so the dist types rejected legal wire values.\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// DiscriminatedUnion — routes by a literal key field, not first-match order\n// ─────────────────────────────────────────────────────────────────────────────\n\n// `undefined` admitted: a member may declare its discriminant `.optional()` (see `_absentMember`).\ntype AnyDiscriminantShape<K extends string> = Record<K, PxSchema<string | number | boolean | undefined, any>>;\n\n/**\n * Reads `raw[key]`, finds the member schema whose literal matches that value,\n * then delegates sanitize/isValid to that member. Falls back to the first\n * member for sanitize when no match is found.\n */\nclass DiscriminatedUnion<T> extends Base<T> {\n /** Structural tag read by {@link describeSchema}. */\n readonly _kind = 'discriminatedUnion' as const;\n readonly _default: T;\n private readonly _map: Map<string | number | boolean, PxSchema<T>>;\n /** The member an ABSENT discriminant selects — the one whose discriminant slot is\n * `.optional()` (e.g. `timeline.type` omitted = the time-driven timeline). */\n private readonly _absentMember: PxSchema<T> | undefined;\n\n constructor(\n private readonly _key: string,\n private readonly _schemas: ReadonlyArray<PxSchema<T> & { readonly _shape: AnyDiscriminantShape<string> }>,\n defaultVal?: T\n ) {\n super();\n this._default = defaultVal ?? _schemas[0]._default;\n this._map = new Map();\n for (const s of _schemas) {\n const keySchema = s._shape[_key] as any;\n if (!keySchema) continue;\n // An optional discriminant wraps its literal: map the literal's value, and\n // remember this member as the one to use when the key is missing.\n const literal = keySchema.inner ?? keySchema;\n this._map.set(literal._default, s);\n if (keySchema.inner) this._absentMember = s;\n }\n }\n\n private _findSchema(raw: unknown): PxSchema<T> | undefined {\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return undefined;\n const val = (raw as Record<string, unknown>)[this._key];\n if (val === undefined || val === null) return this._absentMember;\n return this._map.get(val as string | number | boolean);\n }\n\n sanitize(raw: unknown): T {\n return (this._findSchema(raw) ?? this._schemas[0]).sanitize(raw);\n }\n\n isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean {\n const schema = this._findSchema(raw);\n if (!schema) {\n const val = (raw !== null && typeof raw === 'object' && !Array.isArray(raw))\n ? (raw as Record<string, unknown>)[this._key] : undefined;\n ctx?.errors.push(pathStr(path ?? []) + ': no discriminated union member matched '\n + this._key + '=' + JSON.stringify(val));\n return false;\n }\n return schema.isValid(raw, ctx, path);\n }\n\n override _canSanitize(raw: unknown): boolean {\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return false;\n const schema = this._findSchema(raw);\n return schema ? schema._canSanitize(raw) : this._schemas[0]._canSanitize(raw);\n }\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Object — strips unknown keys; required fields use default, optional → undefined\n// _canSanitize: true when raw is a plain (non-array) object\n// ─────────────────────────────────────────────────────────────────────────────\n\ntype AnyShape = Record<string, PxSchema<any, any>>;\n\n// Required keys: IsOptional=false → plain property.\n// Optional keys: IsOptional=true → ?:, with Exclude<T,undefined> (?: already adds undefined).\ntype InferShape<S extends AnyShape> =\n { [K in keyof S as S[K] extends PxSchema<any, true> ? never : K]: PxInfer<S[K]> } &\n { [K in keyof S as S[K] extends PxSchema<any, true> ? K : never]?: Exclude<PxInfer<S[K]>, undefined> };\n\n/**\n * Typed object schema; strips unknown keys, repairs required fields via their defaults.\n * _canSanitize checks structure only (non-array object) so partially-valid objects are repaired.\n */\nclass Obj<S extends AnyShape> extends Base<InferShape<S>> {\n readonly _default: InferShape<S>;\n\n constructor(readonly _shape: S) {\n super();\n const d: any = {};\n for (const key of Object.keys(_shape)) d[key] = _shape[key]._default;\n this._default = d;\n }\n\n sanitize(raw: unknown): InferShape<S> {\n const src = (raw && typeof raw === 'object' && !Array.isArray(raw)) ? raw as any : {};\n const out: any = {};\n for (const key of Object.keys(this._shape)) {\n const v = this._shape[key].sanitize(src[key]);\n // An OPTIONAL key that was absent sanitizes to `undefined`. Writing it anyway\n // produced a \"phantom\" own key — invisible to JSON, but NOT to `Object.keys`,\n // and consumers branch on that: `PxOffsetPathMaterializer` bails when a transform\n // carries keys beyond translate/origin, so phantoms silently disabled the CSS\n // Motion Path path, and `contentRefSplit` emitted `transform=\"\"`. Measured across\n // 135 real documents: 16,367 phantoms, changing the render of 10 of them.\n if (v !== undefined) out[key] = v;\n }\n return out;\n }\n\n isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n ctx?.errors.push(pathStr(path ?? []) + ': expected object, got ' + (Array.isArray(raw) ? 'array' : typeof raw));\n return false;\n }\n const obj = raw as any;\n const p = path ?? [];\n let ok = true;\n for (const key of Object.keys(this._shape)) {\n p.push(key);\n if (!this._shape[key].isValid(obj[key], ctx, p)) ok = false;\n p.pop();\n }\n // Strict mode: closed objects reject extra (undeclared) keys.\n // Default mode silently ignores them (matching `sanitize`'s strip-extras).\n if (ctx?.strict) {\n for (const key of Object.keys(obj)) {\n if (key in this._shape) continue;\n // An undefined-valued key is indistinguishable from an absent one for\n // every consumer, and JSON.stringify drops it — strict judges the\n // DOCUMENT, not the in-memory object that produced it (V6). Without\n // this, validating a freshly-built (pre-serialization) object flags\n // phantom keys that cannot exist on the wire.\n if (obj[key] === undefined) continue;\n p.push(key);\n ctx.errors.push(pathStr(p) + ': ' + PX_UNKNOWN_KEY_ERROR);\n p.pop();\n ok = false;\n }\n }\n return ok;\n }\n\n override _canSanitize(raw: unknown): boolean {\n return !!raw && typeof raw === 'object' && !Array.isArray(raw);\n }\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// OpenObj — like Obj but passes unknown keys through unchanged (or validates/sanitizes\n// them against an optional open-value schema).\n// Known keys are validated/sanitized; unknown keys are passed through as-is when no\n// open schema is given, or validated/sanitized against the open schema when one is provided.\n// Inferred type: InferShape<S> & { [key: string]: V } where V defaults to any.\n// Note: TypeScript intersection semantics mean named-property access on the\n// derived type yields `any` rather than the specific declared type. For\n// type-precise access keep a hand-written interface alongside the schema.\n// ─────────────────────────────────────────────────────────────────────────────\n\ntype InferOpenShape<S extends AnyShape, _V = any> = InferShape<S> & { [key: string]: any };\n\n/**\n * Open object schema; validates/repairs known keys, passes unknown keys through unchanged.\n * Use when the object may carry arbitrary extra properties (e.g. SVG element attributes).\n *\n * @param shape Known key schemas (validated and type-inferred).\n * @param openSchema Optional schema applied to every unknown key's value.\n * When omitted, unknown values are passed through as-is (`any`).\n */\nclass OpenObj<S extends AnyShape, V = any> extends Base<InferOpenShape<S, V>> {\n readonly _default: InferOpenShape<S, V>;\n\n constructor(readonly _shape: S, private readonly _openSchema?: PxSchema<V>) {\n super();\n const d: any = {};\n for (const key of Object.keys(_shape)) d[key] = _shape[key]._default;\n this._default = d;\n }\n\n sanitize(raw: unknown): InferOpenShape<S, V> {\n const src: Record<string, unknown> = (raw && typeof raw === 'object' && !Array.isArray(raw))\n ? raw as Record<string, unknown>\n : {};\n const out: Record<string, unknown> = { ...src };\n for (const key of Object.keys(this._shape)) {\n const v = this._shape[key].sanitize(src[key]);\n if (v !== undefined) out[key] = v; // no phantom keys — see Obj.sanitize\n }\n if (this._openSchema) {\n for (const key of Object.keys(src)) {\n if (!(key in this._shape)) out[key] = this._openSchema.sanitize(src[key]);\n }\n }\n return out as InferOpenShape<S, V>;\n }\n\n isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n ctx?.errors.push(pathStr(path ?? []) + ': expected object, got ' + (Array.isArray(raw) ? 'array' : typeof raw));\n return false;\n }\n const obj = raw as any;\n const p = path ?? [];\n let ok = true;\n for (const key of Object.keys(this._shape)) {\n p.push(key);\n if (!this._shape[key].isValid(obj[key], ctx, p)) ok = false;\n p.pop();\n }\n if (this._openSchema) {\n for (const key of Object.keys(obj)) {\n if (key in this._shape) continue;\n p.push(key);\n if (!this._openSchema.isValid(obj[key], ctx, p)) ok = false;\n p.pop();\n }\n }\n return ok;\n }\n\n override _canSanitize(raw: unknown): boolean {\n return !!raw && typeof raw === 'object' && !Array.isArray(raw);\n }\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Array — items that cannot be attempted are filtered out; default is []\n// Uses _canSanitize for filtering so partially-valid objects are repaired, not dropped.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Array schema; items failing _canSanitize are filtered out rather than blocking the whole array. */\nclass Arr<T> extends Base<Array<T>> {\n readonly _default: Array<T> = [];\n constructor(private readonly item: PxSchema<T>) { super(); }\n\n sanitize(raw: unknown): Array<T> {\n if (!Array.isArray(raw)) return [];\n const out: Array<T> = [];\n for (const el of raw) {\n if (this.item._canSanitize(el)) out.push(this.item.sanitize(el));\n }\n return out;\n }\n\n isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean {\n if (!Array.isArray(raw)) {\n ctx?.errors.push(pathStr(path ?? []) + ': expected array, got ' + typeof raw);\n return false;\n }\n const p = path ?? [];\n let ok = true;\n for (let i = 0; i < raw.length; i++) {\n p.push('[' + i + ']');\n if (!this.item.isValid(raw[i], ctx, p)) ok = false;\n p.pop();\n }\n return ok;\n }\n\n override _canSanitize(raw: unknown): boolean { return Array.isArray(raw); }\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Record — invalid values are dropped; default is {}\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** String-keyed record; values failing _canSanitize are dropped rather than blocking the whole record. */\nclass Rec<T> extends Base<Record<string, T>> {\n /** Structural tag read by {@link describeSchema}. */\n readonly _kind = 'record' as const;\n readonly _default: Record<string, T> = {};\n constructor(private readonly value: PxSchema<T>) { super(); }\n\n sanitize(raw: unknown): Record<string, T> {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return {};\n const out: Record<string, T> = {};\n for (const [k, v] of Object.entries(raw)) {\n if (this.value._canSanitize(v)) out[k] = this.value.sanitize(v);\n }\n return out;\n }\n\n isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n ctx?.errors.push(pathStr(path ?? []) + ': expected object/record, got ' + (Array.isArray(raw) ? 'array' : typeof raw));\n return false;\n }\n const p = path ?? [];\n let ok = true;\n for (const [k, v] of Object.entries(raw as object)) {\n p.push(k);\n if (!this.value.isValid(v, ctx, p)) ok = false;\n p.pop();\n }\n return ok;\n }\n\n override _canSanitize(raw: unknown): boolean {\n return !!raw && typeof raw === 'object' && !Array.isArray(raw);\n }\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Any — passes raw through unchanged, always valid\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Passes any value through unchanged; always valid. Useful for opaque blobs with no schema. */\nclass Any extends Base<any> {\n readonly _default: any = undefined;\n sanitize(raw: unknown): any { return raw; }\n isValid(_raw: unknown, _ctx?: PxValidationContext, _path?: Array<string>): boolean { return true; }\n override _canSanitize(_raw: unknown): boolean { return true; }\n}\n\n\n/**\n * Like {@link Any} but REQUIRES the value to be present: anything except `undefined`.\n *\n * Use it for a key whose type is open but whose PRESENCE is what identifies the\n * shape — e.g. the structured-static branch `{value: …}` (V6). With `px.any()`\n * there, the branch matched EVERY object, because `any` accepts `undefined` and\n * an absent key is indistinguishable from one holding `undefined`.\n */\nclass Defined extends Base<any> {\n readonly _default: any = undefined;\n sanitize(raw: unknown): any { return raw; }\n isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean {\n if (raw !== undefined) return true;\n ctx?.errors.push(pathStr(path ?? []) + ': required value is missing');\n return false;\n }\n override _canSanitize(raw: unknown): boolean { return raw !== undefined; }\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Lazy — resolves schema on first use, required for recursive types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Defers schema resolution to first use; required to break circular references in recursive types. */\nclass Lazy<T> extends Base<T> {\n private resolved: PxSchema<T> | null = null;\n constructor(private readonly fn: () => PxSchema<T>, readonly _default: T) { super(); }\n\n private get schema(): PxSchema<T> {\n return this.resolved ?? (this.resolved = this.fn());\n }\n\n sanitize(raw: unknown): T { return this.schema.sanitize(raw); }\n isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean { return this.schema.isValid(raw, ctx, path); }\n override _canSanitize(raw: unknown): boolean { return this.schema._canSanitize(raw); }\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Tuple — fixed-length array with per-position schemas; default is defaults of each position\n// _canSanitize: true only when raw is an array of the exact expected length\n// ─────────────────────────────────────────────────────────────────────────────\n\n// Maps a tuple of schemas to a tuple of their inferred types.\ntype TupleItems<T extends ReadonlyArray<PxSchema<any, any>>> =\n { -readonly [K in keyof T]: T[K] extends PxSchema<infer U, any> ? U : never };\n\n/** Fixed-length array schema; validates element count and each position individually. */\nclass Tuple<T extends ReadonlyArray<PxSchema<any, any>>> extends Base<TupleItems<T>> {\n /** Structural tag read by {@link describeSchema} — `schemas` alone cannot tell Tuple from Union. */\n readonly _kind = 'tuple' as const;\n readonly _default: TupleItems<T>;\n\n constructor(private readonly schemas: T) {\n super();\n this._default = schemas.map(s => s._default) as unknown as TupleItems<T>;\n }\n\n sanitize(raw: unknown): TupleItems<T> {\n if (!Array.isArray(raw) || raw.length !== this.schemas.length) return this._default;\n return this.schemas.map((s, i) => s.sanitize((raw as unknown[])[i])) as unknown as TupleItems<T>;\n }\n\n isValid(raw: unknown, ctx?: PxValidationContext, path?: Array<string>): boolean {\n if (!Array.isArray(raw) || raw.length !== this.schemas.length) {\n ctx?.errors.push(pathStr(path ?? []) + ': expected tuple of length ' + this.schemas.length + ', got ' + (Array.isArray(raw) ? 'array[' + (raw as unknown[]).length + ']' : typeof raw));\n return false;\n }\n const p = path ?? [];\n let ok = true;\n for (let i = 0; i < this.schemas.length; i++) {\n p.push('[' + i + ']');\n if (!(this.schemas as ReadonlyArray<PxSchema<any, any>>)[i].isValid((raw as unknown[])[i], ctx, p)) ok = false;\n p.pop();\n }\n return ok;\n }\n\n // Require exact length so wrong-length arrays are dropped rather than repaired to default.\n override _canSanitize(raw: unknown): boolean {\n return Array.isArray(raw) && raw.length === this.schemas.length;\n }\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// implementsInterface — compile-time lock between a schema and an interface\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Returns a pass-through wrapper that enforces at compile time that the schema's\n * inferred type is structurally assignable to `T`. Catches type mismatches on\n * required fields and value-type mismatches on optional fields.\n *\n * For key-level equality (catching optional field renames), pair with a\n * `KeysMatch` assertion after the inferred type alias:\n * ```ts\n * const _ck: KeysMatch<PxFoo, _PxFoo> = true;\n * ```\n *\n * @example\n * export const PxLoopSchema = implementsInterface<_PxLoop>()(px.object({ ... }));\n */\nexport function implementsInterface<T>() {\n return <S extends PxSchema<T>>(schema: S): S => schema;\n}\n\n/**\n * Evaluates to `true` when A and B have exactly the same set of keys; `false` otherwise.\n * Use with a `const` assertion to get a compile-time error on key renames:\n * ```ts\n * const _ck: KeysMatch<PxFoo, _PxFoo> = true;\n * ```\n * @public @advanced\n */\nexport type KeysMatch<A, B> =\n [Exclude<keyof A, keyof B>] extends [never]\n ? [Exclude<keyof B, keyof A>] extends [never] ? true : false\n : false;\n\n/**\n * Builds a `{ key: 'key', ... }` object from a schema's shape.\n * Each value is typed as the literal key name, so `schemaKeys(PxFooSchema).bar`\n * is typed as `'bar'` — use in @serializable calls to get a compile-time error\n * if the field is renamed in the schema.\n * @public @advanced\n */\nexport function schemaKeys<S extends { readonly _shape: Record<string, any> }>(schema: S) {\n return Object.fromEntries(\n Object.keys(schema['_shape']).map(k => [k, k])\n ) as { [K in keyof S['_shape']]: K };\n}\n\n/**\n * Describes the structural kind of a schema for traversal purposes.\n * Use with `getMissingCoveragePaths` or similar tree-walking utilities.\n *\n * Every composite kind is reported, so a walker can reach EVERY field a document\n * may legally carry — that is what makes schema-coverage checking possible\n * (see the app's `collectSchemaFieldUniverse`).\n *\n * - `shape` — object schema (Obj/OpenObj); `shape` maps key → sub-schema.\n * `openValue` is set for open objects: the schema unknown keys\n * are validated against (undefined ⇒ unknown keys pass through as-is)\n * - `array` — array schema; `item` is the element schema\n * - `optional` — optional wrapper; `inner` is the wrapped schema\n * - `lazy` — lazy/recursive schema; `resolved` is the resolved schema (cached)\n * - `union` — `px.union`; `members` are the alternatives, tried in order\n * - `discriminatedUnion`— `px.discriminatedUnion`; `key` is the discriminant field and\n * `members` the alternatives (each an object schema with a literal at `key`)\n * - `record` — `px.record`; `value` is the schema every entry's value must match\n * - `tuple` — `px.tuple`; `items` are the positional element schemas\n * - `leaf` — primitive, literal, enum, any — no traversable children\n * @public @advanced\n */\nexport type PxSchemaDesc =\n | { kind: 'shape'; shape: Record<string, PxSchema<any, any>>; openValue?: PxSchema<any, any> }\n | { kind: 'array'; item: PxSchema<any, any> }\n | { kind: 'optional'; inner: PxSchema<any, any> }\n | { kind: 'lazy'; resolved: PxSchema<any, any> }\n | { kind: 'union'; members: ReadonlyArray<PxSchema<any, any>> }\n | { kind: 'discriminatedUnion'; key: string; members: ReadonlyArray<PxSchema<any, any>> }\n | { kind: 'record'; value: PxSchema<any, any> }\n | { kind: 'tuple'; items: ReadonlyArray<PxSchema<any, any>> }\n | { kind: 'leaf' };\n\n/** @public @advanced */\nexport function describeSchema(schema: PxSchema<any, any>): PxSchemaDesc {\n const s = schema as any;\n // `_kind` is set only by the classes that are otherwise indistinguishable by\n // duck-typing (Union vs Tuple both carry `schemas`), so it is checked first.\n switch (s._kind) {\n case 'union': return { kind: 'union', members: s.schemas };\n case 'discriminatedUnion': return { kind: 'discriminatedUnion', key: s._key, members: s._schemas };\n case 'record': return { kind: 'record', value: s.value };\n case 'tuple': return { kind: 'tuple', items: s.schemas };\n }\n if ('_shape' in s) return { kind: 'shape', shape: s._shape, openValue: s._openSchema };\n if ('item' in s) return { kind: 'array', item: s.item };\n if ('inner' in s) return { kind: 'optional', inner: s.inner };\n // `??=` (not `??`): an unresolved lazy would otherwise build a FRESH schema on\n // every call, so two traversals could never agree on schema identity.\n if ('fn' in s) return { kind: 'lazy', resolved: (s.resolved ??= s.fn()) };\n return { kind: 'leaf' };\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Public factory\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** @public @advanced */\nexport const px = {\n /** Matches a string. Default: '' or provided value. */\n string: (defaultVal = ''): PxSchema<string> => new Str(defaultVal),\n\n /** Matches a finite number. Default: 0 or provided value. */\n number: (defaultVal = 0): PxSchema<number> => new Num(defaultVal),\n\n /** Matches a boolean. Default: false or provided value. */\n boolean: (defaultVal = false): PxSchema<boolean> => new Bool(defaultVal),\n\n /** Matches one exact primitive value; its default is the value itself. */\n literal: <T extends string | number | boolean>(value: T): PxSchema<T> =>\n new Literal(value),\n\n /** Matches one of a fixed set of string/number values. Default: first value. */\n enum: <T extends string | number>(values: readonly T[], defaultVal?: T): PxSchema<T> =>\n new Enum(values, defaultVal),\n\n /**\n * Returns the first schema whose isValid passes.\n * TypeScript infers the union of all member types automatically.\n */\n union: <const T extends ReadonlyArray<PxSchema<any, any>>>(\n schemas: T,\n defaultVal?: UnionMembers<T>\n ): PxSchema<UnionMembers<T>> =>\n new Union(schemas as any, defaultVal) as any,\n\n /**\n * Discriminated union — reads `raw[key]`, finds the member schema whose\n * literal at `key` matches, then delegates sanitize/isValid to that member.\n * Each member must be an object schema with a `px.literal(...)` at `key`.\n * TypeScript infers the union of all member types automatically.\n */\n discriminatedUnion: <\n K extends string,\n const T extends ReadonlyArray<PxSchema<any, any> & { readonly _shape: AnyDiscriminantShape<K> }>\n >(key: K, schemas: T): PxSchema<UnionMembers<T>> =>\n new DiscriminatedUnion(key, schemas as any) as any,\n\n /** Typed object — unknown keys are stripped. Required fields fall back to their default. */\n object: <S extends AnyShape>(shape: S): PxSchema<InferShape<S>> & { readonly _shape: S } =>\n new Obj(shape),\n\n /**\n * Open object — validates known keys; passes unknown keys through as-is,\n * or validates/sanitizes them against `openSchema` when provided.\n */\n openObject: <S extends AnyShape, V = any>(\n shape: S,\n openSchema?: PxSchema<V>\n ): PxSchema<InferOpenShape<S, V>> & { readonly _shape: S } =>\n new OpenObj(shape, openSchema) as any,\n\n /**\n * Creates a new closed object schema by merging a base schema's shape with additional fields.\n * The base can be the result of px.object() or px.openObject() — anything with a _shape property.\n *\n * @example\n * const PxSvgNodeSchema = px.extendedObject(PxNodeBaseSchema, { width: px.number().optional() });\n */\n extendedObject: <B extends AnyShape, E extends AnyShape>(\n base: { readonly _shape: B },\n extra: E\n ): PxSchema<InferShape<B & E>> & { readonly _shape: B & E } =>\n new Obj({ ...base._shape, ...extra } as B & E),\n\n /** Array whose unrecoverable items are filtered out. Default: []. */\n array: <T>(item: PxSchema<T>): PxSchema<Array<T>> =>\n new Arr(item),\n\n /** String-keyed record whose unrecoverable values are dropped. Default: {}. */\n record: <T>(value: PxSchema<T>): PxSchema<Record<string, T>> =>\n new Rec(value),\n\n /** Passes anything through unchanged — always valid. */\n any: (): PxSchema<any> => new Any(),\n\n /** Anything EXCEPT `undefined` — an open type whose presence is required (V6). */\n defined: (): PxSchema<any> => new Defined(),\n\n /** Fixed-length tuple — validates element count and each position individually. */\n tuple: <T extends ReadonlyArray<PxSchema<any, any>>>(schemas: T): PxSchema<TupleItems<T>> =>\n new Tuple(schemas),\n\n /** Defers schema creation — required for recursive types. Must supply a default value. */\n lazy: <T>(fn: () => PxSchema<T>, defaultVal: T): PxSchema<T> =>\n new Lazy(fn, defaultVal),\n} as const;\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// The player's schema version, alone in its own file: a bump is a one-line diff that nothing\n// else rides along with. It must stay import-free (the pre-rendered builds load it).\n// How and when to bump it: dev-docs/versioning.md.\n\n/**\n * THE PLAYER'S WIRE SCHEMA VERSION — the `a.b` of `svgRoot.animator.version`.\n *\n * `a` a generation; no conversion bridges one to another.\n * `b` a revision within the generation. THIS player reads any file at `a.[b' <= b]`.\n *\n * A document may carry a third part (`a.b.c`) — the EDITOR's extension revision, covering\n * everything under `meta.*`. The player neither reads nor compares it.\n *\n * NOT the library's npm version, and never derived from it: the library ships far more often\n * than the format changes, so tying the two would make every release look like a format change\n * and every format change invisible between releases.\n * @public @advanced\n */\nexport const PX_WIRE_SCHEMA_VERSION = '1.2';\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// ============================================================================\n// WIRE FORMAT VERSION + THE PLAYER'S CONVERSIONS — `svgRoot.animator.version`, `\"a.b[.c]\"`.\n//\n// a.b THE PLAYER SCHEMA. A reader at `a.b` reads any file at `a.[b' <= b]`, converting\n// it forward through the steps below. `a` is a generation nothing bridges.\n// c the EDITOR's extension, covering `meta.*` only. The player neither reads nor\n// compares it, and no step here may touch `meta.*`.\n//\n// WHY THIS LIVES IN THE LIBRARY, not the editor: the player ships standalone. A viewer opening\n// a file has no editor to repair it first, so the code that understands the version and moves a\n// document forward has to travel with the player. The editor then EXTENDS this — its own fixer\n// runs `convertWireDocument` first and only afterwards touches `meta.*`:\n//\n// editorFixer(json) { json = convertWireDocument(json).doc; /* then meta.* steps */ }\n//\n// THE VERSION IS A DIAGNOSTIC, NOT A GATE. A gap on its own means nothing and must never warn:\n// a bump says the SCHEMA gained something, not that THIS document uses it. A 1.5 file using no\n// post-1.1 feature plays correctly and silently in a 1.1 player. The number is consulted in\n// exactly two situations — a conversion needs it, or unknown content was actually met, where it\n// turns \"something is wrong\" into \"written for 1.5, this player reads 1.1; update the player\".\n//\n// ABSENT means UNKNOWN, never \"the oldest\": nothing is assumed and nothing is migrated on a guess.\n// ============================================================================\n\nimport { PX_WIRE_SCHEMA_VERSION } from './PxSchemaVersion';\n\n/** The wire key, under the animator config block. @public @advanced */\nexport const PX_WIRE_VERSION_KEY = 'version';\n\nconst ANIMATOR_KEY = 'animator';\nconst META_KEY = 'meta';\n\n/** Parsed form. Compare these, NEVER the strings: `\"1.10\" < \"1.9\"` lexically. @public @advanced */\nexport interface PxWireVersion {\n /** Generation. A change no conversion can bridge. */\n readonly a: number;\n /** Player schema revision within the generation. */\n readonly b: number;\n /** Editor extension revision, scoped to `(a,b)`. The player ignores it. */\n readonly c: number;\n}\n\n/** How a file's version relates to a reader's. @public @advanced */\nexport enum PxWireVersionRelation {\n /** No stamp — unknown provenance. Assume nothing, migrate nothing. */\n unstamped = 'unstamped',\n same = 'same',\n /** The file predates this reader. Any conversion its content needs may be applied. */\n older = 'older',\n /** The file is from a later build. Only meaningful once unknown content is actually met. */\n newer = 'newer',\n /** A different generation — no conversion path exists in either direction. */\n otherGeneration = 'otherGeneration',\n}\n\nconst VERSION_RE = /^(\\d+)\\.(\\d+)(?:\\.(\\d+))?$/;\n\n/** `\"1.2.3\"` → `{a:1,b:2,c:3}`; a missing `c` is baseline 0. `undefined` when unparseable —\n * treated exactly like an absent stamp, never as an error. * @public @advanced\n */\nexport function parseWireVersion(raw: unknown): PxWireVersion | undefined {\n if (typeof raw !== 'string') return undefined;\n const m = VERSION_RE.exec(raw.trim());\n if (!m) return undefined;\n return { a: Number(m[1]), b: Number(m[2]), c: m[3] === undefined ? 0 : Number(m[3]) };\n}\n\n/** @public @advanced */\nexport function formatWireVersion(v: PxWireVersion): string {\n return v.a + '.' + v.b + '.' + v.c;\n}\n\n/** The version this PLAYER implements, parsed. `c` is 0: the player has no editor extension. @public @advanced */\nexport const PX_WIRE_VERSION: PxWireVersion =\n parseWireVersion(PX_WIRE_SCHEMA_VERSION) ?? { a: 1, b: 1, c: 0 };\n\n/** The animator config block, at either of its two wire addresses: lifted to the top level in\n * the lightweight JSON, carried under `meta` in a pre-rendered SVG. */\nfunction getAnimatorBlock(doc: unknown): { [key: string]: unknown } | undefined {\n if (!doc || typeof doc !== 'object') return undefined;\n const atRoot = readObjectProp(doc, ANIMATOR_KEY);\n if (atRoot) return atRoot;\n const meta = readObjectProp(doc, META_KEY);\n return meta ? readObjectProp(meta, ANIMATOR_KEY) : undefined;\n}\n\nfunction readObjectProp(obj: object, key: string): { [key: string]: unknown } | undefined {\n const value = (obj as { [k: string]: unknown })[key];\n return value && typeof value === 'object' && !Array.isArray(value)\n ? value as { [key: string]: unknown }\n : undefined;\n}\n\n/** The version stamped on a document, or `undefined` when it carries none. @public @advanced */\nexport function readWireVersion(doc: unknown): PxWireVersion | undefined {\n const animator = getAnimatorBlock(doc);\n return animator ? parseWireVersion(animator[PX_WIRE_VERSION_KEY]) : undefined;\n}\n\n/**\n * How `file` relates to `mine`. `readerReadsEditorPart` separates the two readers: the EDITOR\n * compares all three parts, the PLAYER compares only `a.b` and is blind to `c`.\n * @public @advanced\n */\nexport function compareWireVersion(\n file: PxWireVersion | undefined, mine: PxWireVersion, readerReadsEditorPart: boolean,\n): PxWireVersionRelation {\n if (!file) return PxWireVersionRelation.unstamped;\n if (file.a !== mine.a) return PxWireVersionRelation.otherGeneration;\n if (file.b !== mine.b) return file.b > mine.b ? PxWireVersionRelation.newer : PxWireVersionRelation.older;\n if (!readerReadsEditorPart || file.c === mine.c) return PxWireVersionRelation.same;\n return file.c > mine.c ? PxWireVersionRelation.newer : PxWireVersionRelation.older;\n}\n\n/**\n * What to TELL the user about a version gap — and only ever alongside unknown content actually\n * met. `undefined` means the version explains nothing, so nothing is said.\n *\n * Every gap has one of exactly two remedies: upgrade the file, or upgrade the reader. Neither is\n * a refusal — the reader has already dropped what it could not understand and the rest renders,\n * so the worst case is a document missing a feature plus a sentence saying how to close the gap.\n * @public @advanced\n */\nexport function wireVersionAdvice(\n relation: PxWireVersionRelation, file: PxWireVersion | undefined, mine: PxWireVersion, isPlayer: boolean,\n): string | undefined {\n if (!file) return undefined;\n const target = isPlayer ? 'player' : 'editor';\n const gap = 'written for schema ' + formatWireVersion(file)\n + ', this ' + target + ' reads ' + formatWireVersion(mine);\n switch (relation) {\n case PxWireVersionRelation.newer:\n return 'This file is ' + gap + '. Update the ' + target + ' to open it fully.';\n case PxWireVersionRelation.older:\n // The unknown part is a spelling the format has since dropped, so the FILE is what\n // moves. Re-saving from this build rewrites it in the current spelling.\n return 'This file is ' + gap + '. Saving it from this ' + target\n + ' rewrites it in the current format.';\n case PxWireVersionRelation.otherGeneration:\n // No conversion path exists in either direction — so both remedies are named and the\n // user picks. Still not a refusal: what could be read has been.\n return 'This file is ' + gap + ' \\u2014 a different format generation, which no conversion bridges.'\n + ' Open it in a ' + target\n + ' of that generation, or accept this document without the parts named above.';\n default:\n // `same` and `unstamped`: the version accounts for nothing here.\n return undefined;\n }\n}\n\n\n// ═══════════════════════════════════════════════════════════\n// THE STEP TABLE\n// ═══════════════════════════════════════════════════════════\n\n/** What a step DOES to documents — and therefore whether it needs conversion code. @public @advanced */\nexport enum PxWireStepKind {\n /**\n * Only OPTIONAL fields were added. An older reader ignores them; a newer reader finds them\n * absent and defaults. Nothing to convert either way — but it must still be DECLARED, so\n * that \"no converter\" is a decision on the record rather than an omission.\n */\n additive = 'additive',\n /** A shape or spelling changed. `up` is then mandatory; `down` where it is possible at all. */\n converted = 'converted',\n}\n\n/** One `b` step of the player schema. @public @advanced */\nexport interface PxWireVersionStep {\n /** The version this step converts FROM, e.g. `'1.1'`. */\n readonly from: string;\n /** …and TO. Must be the `from` of the next step, so the table is one unbroken chain. */\n readonly to: string;\n /** Why the format moved — the sentence a future reader needs, not a commit hash. */\n readonly reason: string;\n readonly kind: PxWireStepKind;\n /**\n * Older → newer, MUTATING the document in place. Required for {@link PxWireStepKind.converted}.\n * It may not touch `meta.*`: that subtree is the editor's, and its steps are the `c` part.\n */\n readonly up?: (doc: Record<string, unknown>) => void;\n /** Newer → older. Absent means the step is one-way and down-conversion refuses. */\n readonly down?: (doc: Record<string, unknown>) => void;\n}\n\n/** Where the chain starts: the first RELEASED player schema. Everything older is pre-release. @public @advanced */\nexport const PX_WIRE_BASELINE_VERSION = '1.1';\n\n/**\n * Every `b` step from {@link PX_WIRE_BASELINE_VERSION} to {@link PX_WIRE_SCHEMA_VERSION}.\n *\n * One step so far: the 1.2 trigger block. The guard spec keys off this table — bump\n * `PX_WIRE_SCHEMA_VERSION` without adding the matching step and the suite fails naming the gap.\n * That is the whole point: the last three renames shipped because nothing forced anyone to say\n * they had happened.\n * @public @advanced\n */\nexport const PX_WIRE_STEPS: ReadonlyArray<PxWireVersionStep> = [\n {\n from: '1.1',\n to: '1.2',\n reason: 'The trigger block became two axes: `start` says what STARTS an animation, '\n + '`offScreen` + `visibilityThreshold` + `visibilityDebounce` say whether it may RUN. '\n + '`scrollIntoView` is no longer a start value — it is what `start: \\'load\\'` behind '\n + 'the default gate already means — and `outAction` split into `offScreen` (scroll) '\n + 'and `mouseOut` (hover), because one field meant three things.',\n kind: PxWireStepKind.converted,\n up: upTriggerTwoAxes,\n // No `down`: 1.2 can express combinations 1.1 could not (\"start on click AND pause when\n // scrolled away\"), so the mapping is not invertible.\n },\n];\n\n/**\n * 1.1 → 1.2. `outAction` meant three different things depending on `startOn`, so it can only be\n * moved by reading both together.\n *\n * | 1.1 | 1.2 |\n * |---|---|\n * | `startOn: 'scrollIntoView'` + `outAction: X` | `offScreen: X`, and NO `start` — its default already means this |\n * | `startOn: 'mouseOver'` + `outAction: X` | `start: 'mouseOver'`, `mouseOut: X` |\n * | `startOn: 'click'` / `'load'` + `outAction` | the action is dropped; it was never read for those |\n * | `startOn: 'programmatic'` | `start: 'none'` |\n * | `scrollIntoViewThreshold: T` | `visibilityThreshold: T` — including an explicit `0`, which is no longer the default |\n * | `finishAction: X` | `finish: X` |\n */\nfunction upTriggerTwoAxes(doc: Record<string, unknown>): void {\n const animator = doc['animator'];\n if (!animator || typeof animator !== 'object') return;\n const timeline = (animator as Record<string, unknown>)['timeline'];\n const holder = (timeline && typeof timeline === 'object' ? timeline : animator) as Record<string, unknown>;\n const trigger = holder['trigger'];\n if (!trigger || typeof trigger !== 'object') return;\n const t = trigger as Record<string, unknown>;\n\n const startOn = t['startOn'];\n const outAction = t['outAction'];\n delete t['startOn'];\n delete t['outAction'];\n\n if (startOn === 'scrollIntoView') {\n // The gate IS the default now, so the document says nothing about what starts it.\n if (outAction !== undefined) t['offScreen'] = outAction;\n } else {\n if (startOn === 'programmatic') t['start'] = 'none';\n else if (startOn !== undefined) t['start'] = startOn;\n // A hover document keeps its out action; a click or load document never read one.\n if (startOn === 'mouseOver' && outAction !== undefined) t['mouseOut'] = outAction;\n // Nothing said visibility should stop it, so 1.1 behaviour is kept explicitly.\n if (t['offScreen'] === undefined) t['offScreen'] = 'continue';\n }\n\n if (t['scrollIntoViewThreshold'] !== undefined) {\n t['visibilityThreshold'] = t['scrollIntoViewThreshold'];\n delete t['scrollIntoViewThreshold'];\n }\n if (t['finishAction'] !== undefined) {\n t['finish'] = t['finishAction'];\n delete t['finishAction'];\n }\n}\n\n\n/** What a conversion pass did, so a caller can report it. @public @advanced */\nexport interface PxWireConversionResult {\n /**\n * The document to read. A converted COPY when steps applied, otherwise the input itself,\n * unchanged and identical by reference — the caller's object is never mutated, so a failed\n * or partial conversion can never leave a half-migrated document behind.\n */\n readonly doc: unknown;\n /** The version found on the file, if any. */\n readonly from: PxWireVersion | undefined;\n readonly relation: PxWireVersionRelation;\n /** The steps actually applied, oldest first. Empty when nothing was needed. */\n readonly applied: ReadonlyArray<PxWireVersionStep>;\n /** Set only when the version could not be honored — never a refusal to render. */\n readonly advice?: string;\n}\n\n/** Which table to run, and what to bring the document up TO. @public @advanced */\nexport interface PxWireConversionOptions {\n /** The step table — the player's, or the editor's `meta.*` one. */\n readonly steps: ReadonlyArray<PxWireVersionStep>;\n /** The version the document should end up at. */\n readonly target: PxWireVersion;\n /** `true` for the EDITOR (compares and stamps `c`), `false` for the PLAYER (blind to it). */\n readonly readerReadsEditorPart: boolean;\n}\n\n/**\n * THE STEP ENGINE — one implementation, two tables.\n *\n * The player runs it over `PX_WIRE_STEPS`; the editor runs it a second time over its own\n * `meta.*` table, on the document the player pass returned. Keeping it one function is what\n * stops the two halves from drifting into two different ideas of what conversion means.\n *\n * It NEVER refuses and never throws:\n * · unstamped → nothing is assumed, nothing is converted; the file is read as it is.\n * · newer / other generation → no step exists, so nothing is applied; `advice` says which way\n * to close the gap, and the caller renders whatever the schema could keep.\n * · older → the due steps run on a COPY. A step with no `up` is skipped; a step that\n * throws stops the chain, and the ORIGINAL comes back rather than a half-\n * converted one.\n * @public @advanced\n */\nexport function applyWireSteps(doc: unknown, cfg: PxWireConversionOptions): PxWireConversionResult {\n const from = readWireVersion(doc);\n const relation = compareWireVersion(from, cfg.target, cfg.readerReadsEditorPart);\n if (!from || relation !== PxWireVersionRelation.older) {\n return {\n doc, from, relation, applied: [],\n advice: wireVersionAdvice(relation, from, cfg.target, !cfg.readerReadsEditorPart),\n };\n }\n\n // Which steps this document actually needs, decided BEFORE anything is copied so the common\n // \"older but nothing to do\" case costs nothing. A step is due when the FILE is older than\n // what that step produces — the same comparison the reader itself uses.\n const due: Array<PxWireVersionStep> = [];\n for (const step of cfg.steps) {\n const stepTo = parseWireVersion(step.to);\n if (!stepTo) continue;\n if (compareWireVersion(from, stepTo, cfg.readerReadsEditorPart) !== PxWireVersionRelation.older) continue;\n if (!step.up) continue; // additive, or one-way with no code\n due.push(step);\n }\n if (!due.length || !doc || typeof doc !== 'object') return { doc, from, relation, applied: [] };\n\n // Work on a COPY: a step that throws mid-way must not leave the caller's document\n // half-migrated, and callers routinely hold the parsed file for other purposes.\n const target = clonePlain(doc) as Record<string, unknown>;\n const applied: Array<PxWireVersionStep> = [];\n for (const step of due) {\n // One bad step degrades to \"that step did not happen\" — never to a failed open.\n try {\n step.up!(target);\n applied.push(step);\n } catch {\n break; // stop at the first failure: later steps assume this one ran\n }\n }\n if (!applied.length) return { doc, from, relation, applied: [] };\n // The document now speaks the target schema, so it says so.\n stampVersion(target, cfg.target, cfg.readerReadsEditorPart);\n return { doc: target, from, relation, applied };\n}\n\n/**\n * BRING A DOCUMENT UP TO THIS PLAYER'S SCHEMA — the `playerFixer` half of the pair.\n * Runs {@link applyWireSteps} over {@link PX_WIRE_STEPS}, touching nothing under `meta.*`.\n * @public @advanced\n */\nexport function convertWireDocument(doc: unknown): PxWireConversionResult {\n return applyWireSteps(doc, {\n steps: PX_WIRE_STEPS, target: PX_WIRE_VERSION, readerReadsEditorPart: false,\n });\n}\n\n/** A structural copy of a parsed JSON document. `structuredClone` where the runtime has it\n * (every browser the player targets, and Node 17+), else a JSON round trip — the input is\n * parsed wire data either way, so both are lossless for it. */\nfunction clonePlain<T>(value: T): T {\n const structured = (globalThis as { structuredClone?: (v: unknown) => unknown }).structuredClone;\n return structured ? structured(value) as T : JSON.parse(JSON.stringify(value)) as T;\n}\n\n/**\n * Re-stamp a converted document with the version it now conforms to.\n *\n * A PLAYER pass moves `a.b` and PRESERVES the file's `c`: it did not touch `meta.*`, so it has\n * no business claiming the editor's extension moved with it. An EDITOR pass stamps all three.\n */\nfunction stampVersion(doc: Record<string, unknown>, target: PxWireVersion, readerReadsEditorPart: boolean): void {\n const animator = getAnimatorBlock(doc);\n if (!animator) return;\n const previous = parseWireVersion(animator[PX_WIRE_VERSION_KEY]);\n animator[PX_WIRE_VERSION_KEY] = formatWireVersion({\n a: target.a, b: target.b,\n c: readerReadsEditorPart ? target.c : (previous ? previous.c : 0),\n });\n}\n\n\n// ═══════════════════════════════════════════════════════════\n// DOWN-CONVERSION (task 6.7)\n// ═══════════════════════════════════════════════════════════\n\n/**\n * What an EXPLICIT down-conversion did. Unlike reading, this may refuse — it is an output the\n * user asked for (\"save for an older player\"), and handing back a file that silently lacks what\n * the older schema cannot say would be worse than saying no.\n * @public @advanced\n */\nexport type PxWireDowngradeResult =\n | { readonly ok: true; readonly doc: unknown; readonly applied: ReadonlyArray<PxWireVersionStep> }\n | { readonly ok: false; readonly reason: string; readonly blocking: ReadonlyArray<PxWireVersionStep> };\n\n/** Which table to walk BACK through, from the version the document is at to the one wanted. @public @advanced */\nexport interface PxWireDowngradeOptions {\n readonly steps: ReadonlyArray<PxWireVersionStep>;\n /** The version the document must end up at. */\n readonly target: PxWireVersion;\n /** `true` for the editor table (`c` steps), `false` for the player table (`b` steps). */\n readonly readerReadsEditorPart: boolean;\n}\n\n/**\n * BRING A DOCUMENT DOWN TO AN OLDER SCHEMA — only when EVERY step in between can be undone.\n *\n * All or nothing: if even one step on the way back has no `down` (the shape rework never will —\n * its old shell had one clock and cannot express per-parameter animation), the whole request is\n * refused and the reason names those steps. A partial downgrade is never produced, and the\n * caller's document is never mutated — the work happens on a copy.\n * @public @advanced\n */\nexport function applyWireStepsDown(doc: unknown, cfg: PxWireDowngradeOptions): PxWireDowngradeResult {\n const from = readWireVersion(doc);\n if (!from) {\n return { ok: false, blocking: [], reason: 'The document carries no version, so there is nothing to convert down from.' };\n }\n const relation = compareWireVersion(from, cfg.target, cfg.readerReadsEditorPart);\n if (relation === PxWireVersionRelation.otherGeneration) {\n return {\n ok: false, blocking: [],\n reason: 'Schema ' + formatWireVersion(from) + ' and ' + formatWireVersion(cfg.target)\n + ' are different generations; no conversion bridges them.',\n };\n }\n // Already at (or older than) the target: nothing to undo.\n if (relation !== PxWireVersionRelation.newer) return { ok: true, doc, applied: [] };\n\n // Every step whose RESULT is newer than the target must be undone, newest first.\n const toUndo: Array<PxWireVersionStep> = [];\n for (const step of cfg.steps) {\n const stepTo = parseWireVersion(step.to);\n if (!stepTo) continue;\n if (compareWireVersion(stepTo, cfg.target, cfg.readerReadsEditorPart) !== PxWireVersionRelation.newer) continue;\n if (compareWireVersion(stepTo, from, cfg.readerReadsEditorPart) === PxWireVersionRelation.newer) continue;\n toUndo.push(step);\n }\n toUndo.reverse();\n\n // Additive steps need no `down`: an older reader simply ignores what they added.\n const blocking = toUndo.filter(s => s.kind === PxWireStepKind.converted && !s.down);\n if (blocking.length) {\n return {\n ok: false, blocking,\n reason: 'Cannot convert down to ' + formatWireVersion(cfg.target) + ': '\n + blocking.map(s => s.from + ' → ' + s.to + ' (' + s.reason + ')').join('; ')\n + ' cannot be undone.',\n };\n }\n\n const target = clonePlain(doc) as Record<string, unknown>;\n const applied: Array<PxWireVersionStep> = [];\n for (const step of toUndo) {\n if (!step.down) continue; // additive\n try {\n step.down(target);\n applied.push(step);\n } catch (e) {\n // Output path: a half-downgraded file is exactly what this function exists to avoid.\n return {\n ok: false, blocking: [step],\n reason: 'Undoing ' + step.from + ' → ' + step.to + ' failed: ' + String(e),\n };\n }\n }\n stampVersion(target, cfg.target, cfg.readerReadsEditorPart);\n return { ok: true, doc: target, applied };\n}\n\n/** Down-convert through the PLAYER table only — `meta.*` is untouched, as on the way up. @public @advanced */\nexport function downgradeWireDocument(doc: unknown, target: PxWireVersion): PxWireDowngradeResult {\n return applyWireStepsDown(doc, { steps: PX_WIRE_STEPS, target, readerReadsEditorPart: false });\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// ============================================================================\n// Wire CONSTANTS and schema-free helpers.\n//\n// Split out of `PxAnimatorTypes` so that code needing only an enum does not drag the\n// SCHEMA ENGINE in with it. `PxAnimatorTypes` builds ~31 schema declarations at module\n// scope through curried `implementsInterface<T>()(px.object(...))` calls, which no\n// minifier can treat as side-effect-free — so a single value import from it pulls in\n// `PxSchema` plus every declaration.\n//\n// That is exactly what happened: `PxDefinitions` imported `PxLoopExtend` (one small const)\n// and the pre-rendered player builds ended up carrying 14 KB of validation code they never\n// call. See dev-docs/plans/prerendered-player-builds.md.\n//\n// RULE: nothing in this file may import a VALUE from `PxAnimatorTypes`. Type-only imports\n// are fine — they are erased at build time and cannot create a runtime edge.\n// ============================================================================\n\nimport type { PxAnimatedSvgDocument, PxAnimatorConfig, PxBinding, PxDefinitions, PxNode, PxScroll, PxTrigger } from './PxAnimatorTypes';\n\n// ── WIRE ENUMS (review §2.7) ────────────────────────────────────────────────\n// ONE shape for every enumerated wire value: an exported `Px*` const namespace plus the\n// string type derived from it, so a call site can write `PxFillMode.forwards` and the\n// schema can reference the same members instead of repeating bare literals.\n//\n// NOT a TypeScript `enum`: these are WIRE values, and a document is authored as plain\n// JSON — `{ fill: 'forwards' }`. A string `enum` is nominal, so that literal would not\n// typecheck without importing the enum; the derived union accepts both spellings. It is\n// also the only form that composes (`PxTimelineEngineSetting` spreads `PxTimelineEngine`)\n// and that survives erasable-syntax / type-stripping builds.\n\n/** WAAPI `fill` — which values apply outside the active period. @public */\nexport const PxFillMode = {\n forwards: 'forwards',\n backwards: 'backwards',\n both: 'both',\n none: 'none',\n} as const;\n\nexport type PxFillMode = typeof PxFillMode[keyof typeof PxFillMode];\n\n/** WAAPI `direction` — which way each iteration runs. @public */\nexport const PxPlaybackDirection = {\n normal: 'normal',\n reverse: 'reverse',\n alternate: 'alternate',\n alternateReverse: 'alternate-reverse',\n} as const;\n\nexport type PxPlaybackDirection = typeof PxPlaybackDirection[keyof typeof PxPlaybackDirection];\n\n\n/** @internal */\nexport const PX_ANIM_SRC_ATTR_NAME = 'data-px-animation-src';\n\n/** @internal */\nexport const PX_ANIM_ATTR_NAME = '_px_animator';\n\n/** `trigger.start` — what starts the animation. `none` waits for `play()`.\n *\n * There is no `scrollIntoView` member: visibility is not a trigger but a permission, and it is\n * governed by `offScreen` + `visibilityThreshold` whatever starts the animation. `start: 'load'`\n * behind the default gate is what `startOn: 'scrollIntoView'` used to mean. @public */\nexport const PxTriggerStart = {\n load: 'load',\n mouseOver: 'mouseOver',\n click: 'click',\n none: 'none',\n} as const;\n\nexport type PxTriggerStart = typeof PxTriggerStart[keyof typeof PxTriggerStart];\n\n/** `trigger.offScreen` — what happens while none of the graphic is on screen. No `reverse`:\n * nobody can watch an animation play backwards off screen. @public */\nexport const PxOffScreenAction = {\n pause: 'pause',\n continue: 'continue',\n reset: 'reset',\n} as const;\n\nexport type PxOffScreenAction = typeof PxOffScreenAction[keyof typeof PxOffScreenAction];\n\n/** `trigger.mouseOut` — what happens when the pointer leaves. Read only when\n * `start` is `mouseOver`. @public */\nexport const PxMouseOutAction = {\n continue: 'continue',\n pause: 'pause',\n reset: 'reset',\n reverse: 'reverse',\n} as const;\n\nexport type PxMouseOutAction = typeof PxMouseOutAction[keyof typeof PxMouseOutAction];\n\n/** `trigger.finish` — what happens after a NATURAL finish. @public */\nexport const PxFinishAction = {\n hold: 'hold',\n reset: 'reset',\n} as const;\n\nexport type PxFinishAction = typeof PxFinishAction[keyof typeof PxFinishAction];\n\n/** `scroll.kind` — which scroll-driven timeline member this is: the subject's journey\n * through the scrollport (`view`), or the scroll container's own offset (`scroll`). * @public\n */\nexport const PxScrollKind = {\n view: 'view',\n scroll: 'scroll',\n} as const;\n\nexport type PxScrollKind = typeof PxScrollKind[keyof typeof PxScrollKind];\n\n/** `timeline.axis` — which axis of the scroll container drives progress. @public */\nexport const PxScrollAxis = {\n block: 'block',\n inline: 'inline',\n x: 'x',\n y: 'y',\n} as const;\n\nexport type PxScrollAxis = typeof PxScrollAxis[keyof typeof PxScrollAxis];\n\n/** `timeline.source` (`scroll` kind) — which scroll container is measured. @public */\nexport const PxScrollSource = {\n nearest: 'nearest',\n root: 'root',\n} as const;\n\nexport type PxScrollSource = typeof PxScrollSource[keyof typeof PxScrollSource];\n\n/** `timeline.pin.align` — where the pinned canvas is held in the scrollport. @public */\nexport const PxPinAlign = {\n top: 'top',\n center: 'center',\n bottom: 'bottom',\n} as const;\n\nexport type PxPinAlign = typeof PxPinAlign[keyof typeof PxPinAlign];\n\n/** The subject's journey phases across the scrollport (see scroll-timeline.design.md §4\n * for the exact `u`-space intervals each phase maps to). * @public\n */\nexport const PxScrollPhase = {\n cover: 'cover',\n contain: 'contain',\n entry: 'entry',\n exit: 'exit',\n entryCrossing: 'entry-crossing',\n exitCrossing: 'exit-crossing',\n} as const;\n\nexport type PxScrollPhase = typeof PxScrollPhase[keyof typeof PxScrollPhase];\n\n/** `alongPathMode` — how a value is sampled along a motion path. @public */\nexport const PxAlongPathMode = {\n sampled: 'sampled',\n offsetPath: 'offsetPath',\n} as const;\n\nexport type PxAlongPathMode = typeof PxAlongPathMode[keyof typeof PxAlongPathMode];\n\n/** WIRE `timeline.engine` — who runs the animation. `auto` (default) prefers the\n * platform's animation API and falls back to JS when the document needs something it\n * cannot express; `native` DEMANDS that API (WAAPI — and, for scroll/view timelines,\n * the browser's ScrollTimeline) with no fallback; `js` pins the player's own frame loop\n * and its own progress measurement. Const-namespace + matching string type so call\n * sites use named members (`PxTimelineEngineSetting.js`), not bare literals.\n *\n * NAMED `engine`, not `mode`: it selects HOW the animated attributes get updated, not\n * WHAT you see — an implementation preference. (`native` is the one value that can also\n * change the outcome: being a demand, an attribute WAAPI declines simply does not\n * animate. See `isNativeForced`.) */\n/** Timeline keys that BOTH union members carry, so they survive a change of `type`. */\nexport const PX_TIMELINE_SHARED_KEYS = ['duration', 'iterations', 'engine', 'frameRate'] as const;\n\n/** Timeline keys that exist ONLY on the time-driven member — a scroll/view timeline is\n * scrubbed by position, so nothing starts it and nothing delays it. */\nexport const PX_TIME_ONLY_TIMELINE_KEYS = ['trigger', 'delay', 'fillMode', 'direction'] as const;\n\n/**\n * The flat RUNTIME-VIEW keys — `duration`, `trigger`, `scroll`, … at the animator ROOT.\n *\n * They are the internal view the engines consume, never a wire spelling: on the wire playback\n * lives inside `timeline`. `getAnimatorConfig` drops them from a document, so a stray one is an\n * unknown key the diagnostic reports rather than a second spelling that quietly plays.\n */\nexport const PX_FLAT_RUNTIME_VIEW_KEYS: ReadonlyArray<string> = [\n ...PX_TIMELINE_SHARED_KEYS, ...PX_TIME_ONLY_TIMELINE_KEYS,\n 'fill', 'resetOnFinish', 'timelineSource', 'scroll',\n];\n\n/**\n * THE ENGINES — the two things that can actually update an animated attribute: hand it to the\n * platform's animation API (`native`), or write it from the player's own frame loop (`js`).\n *\n * This is the CORE set. Code that always knows which engine is running takes this (e.g.\n * `normalizeBindings`'s `engine` arg gates motion-along-path materialization).\n * @public\n */\nexport const PxTimelineEngine = {\n native: 'native',\n js: 'js',\n} as const;\n\nexport type PxTimelineEngine = typeof PxTimelineEngine[keyof typeof PxTimelineEngine];\n\n/**\n * What `timeline.engine` ACCEPTS on the wire: the engines above plus `auto` — \"you pick\", which\n * prefers `native` and falls back to `js` per document when the platform API declines an\n * attribute.\n *\n * Built by ADDING to the core set rather than subtracting from a wider one, so the two cannot\n * drift: every engine is automatically an accepted value, and `auto` is visibly the one extra.\n * @public\n */\nexport const PxTimelineEngineSetting = {\n ...PxTimelineEngine,\n auto: 'auto',\n} as const;\n\nexport type PxTimelineEngineSetting = typeof PxTimelineEngineSetting[keyof typeof PxTimelineEngineSetting];\n\n/** What a requested engine resolves to BEFORE the runtime probes support: `js` pins the frame\n * loop, anything else starts at `native`. NOTE this is only the STARTING point — `auto` still\n * falls back to `js` per document when the platform API declines an attribute, which happens at\n * bind time (see `PxAnimatorBind`), not here. * @public @advanced\n */\nexport function resolveTimelineEngine(engine: PxTimelineEngineSetting | undefined): PxTimelineEngine {\n return engine === PxTimelineEngineSetting.js ? PxTimelineEngine.js : PxTimelineEngine.native;\n}\n\n/** `native` is a demand, not a preference: no JS fallback when the platform API declines an attribute. @public @advanced */\nexport function isNativeForced(engine: PxTimelineEngineSetting | undefined): boolean {\n return engine === PxTimelineEngineSetting.native;\n}\n\n/** May the browser's ScrollTimeline/ViewTimeline drive a scroll/view timeline?\n * `auto` tries it first (falling back to the player's own measurement), `native`\n * asks for it, `js` never uses it. * @public @advanced\n */\nexport function mayUseNativeScrollTimeline(engine: PxTimelineEngineSetting | undefined): boolean {\n return engine !== PxTimelineEngineSetting.js;\n}\n\n/**\n * THE TRIGGER DEFAULTS — what a missing `trigger` field means. One table, declared by\n * `PxTriggerSchema` and applied by {@link resolveTrigger}, which every player calls (the web's\n * `setupAnimationTriggers`, the React Native component) — so a file behaves the same everywhere:\n * - `start` 'load' — a document is designed to play\n * - `offScreen` 'pause' — an animation nobody can see does not run\n * - `mouseOut` 'continue' — leaving the element does not interrupt playback\n * - `visibilityThreshold` 0.5 — half of it must be on screen before it may run\n * - `visibilityDebounce` 150 — and stay that way this long, so a fast scroll past starts nothing\n * @public @advanced\n */\nexport const PX_TRIGGER_DEFAULTS = {\n start: 'load',\n offScreen: 'pause',\n mouseOut: 'continue',\n visibilityThreshold: 0.5,\n visibilityDebounce: 150,\n} as const;\n\n/** A trigger with every default filled in. @public @advanced */\nexport interface PxResolvedTrigger {\n readonly start: NonNullable<PxTrigger['start']>;\n readonly offScreen: NonNullable<PxTrigger['offScreen']>;\n readonly mouseOut: NonNullable<PxTrigger['mouseOut']>;\n readonly visibilityThreshold: number;\n readonly visibilityDebounce: number;\n}\n\n// ── CONTROL MODE (API review §1 / §7) ────────────────────────────────────────\n// Which set of props drives playback. Every component picked its own order, so\n// `autoplay` + `progress={0.5}` played on React and Vue but seeked on React Native, and\n// React let a REF choose the mode — `<PixodeskSvgAnimator autoplay apiRef={api} />` never\n// started, because the imperative branch forced `start: 'none'`.\n//\n// One order, decided once, used by react / vue / rn. This module owns the LOGIC and the\n// WARNING TEXT only; each component keeps its own `console.warn` wiring.\n\n/** Which props drive playback. `apiRef` is deliberately NOT a mode: the handle is filled in\n * every mode, so passing it alone leaves the document's own trigger in charge. * @public\n */\nexport const PxControlMode = {\n /** No control props — the document's trigger decides, and nothing is taken over. */\n static: 'static',\n /** `progress` / `time` — the host scrubs; the component seeks and stays paused. */\n fixedTime: 'fixedTime',\n /** `play` / `pause` — the host drives playback with booleans. */\n play: 'play',\n /** `autoplay` — the document's own trigger starts it. */\n autoplay: 'autoplay',\n} as const;\n\nexport type PxControlMode = typeof PxControlMode[keyof typeof PxControlMode];\n\n/**\n * The control props every framework component takes. `resolveControlMode` reads only WHICH\n * are set; the components read the values. ONE definition (review §9) — React and React\n * Native extend it, so the hover text below is what their users see.\n * @public\n */\nexport interface PxControlProps {\n /**\n * Show the frame at this position in the whole timeline (duration × iterations): `0` the\n * first frame, `1` the last — of ONE iteration when `iterations` is `'infinite'`, since an\n * endless run has no whole to be a fraction of. Wins over every other control prop.\n */\n progress?: number;\n /** Show the frame at this time, ms from the start of the whole run. Wins like `progress`. */\n time?: number;\n /** `true` plays now, whatever the document's trigger says; `false` holds where it is. */\n play?: boolean;\n /** Hold the current frame; set it back to `false` to resume. */\n pause?: boolean;\n /** Start the way the document says — its own `trigger` block. */\n autoplay?: boolean;\n}\n\n/** The chosen mode plus any conflict warnings — ready-made sentences, so three components\n * cannot word the same conflict three ways. * @public\n */\nexport interface PxResolvedControlMode {\n readonly mode: PxControlMode;\n readonly warnings: ReadonlyArray<string>;\n}\n\n/**\n * Picks the control mode from the props a component was given.\n *\n * PRECEDENCE, most specific first:\n * 1. `progress` / `time` — an explicit position is the most precise instruction there is\n * 2. `play` / `pause` — explicit playback state\n * 3. `autoplay` — defer to the document's trigger\n * 4. otherwise `static` — the document's trigger, with nothing taken over\n *\n * `apiRef` is absent on purpose. A ref is a handle, not an instruction: it is populated in\n * every mode, so `autoplay` + `apiRef` autostarts AND gives you the handle.\n *\n * A warning is produced only when props from two different tiers are set together — the\n * lower tier is then ignored, and silence about that is what made this hard to debug.\n * @public\n */\nexport function resolveControlMode(props: PxControlProps): PxResolvedControlMode {\n const hasFixedTime = props.progress !== undefined || props.time !== undefined;\n const hasPlayPause = props.play !== undefined || props.pause !== undefined;\n const hasAutoplay = !!props.autoplay;\n\n const warnings: Array<string> = [];\n const named = (a: string, b: string, winner: string): string =>\n a + ' and ' + b + ' were both set — ' + winner + ' wins, ' + (winner === a ? b : a) + ' is ignored.';\n\n if (hasFixedTime) {\n if (hasPlayPause) warnings.push(named('progress/time', 'play/pause', 'progress/time'));\n if (hasAutoplay) warnings.push(named('progress/time', 'autoplay', 'progress/time'));\n return { mode: PxControlMode.fixedTime, warnings };\n }\n if (hasPlayPause) {\n if (hasAutoplay) warnings.push(named('play/pause', 'autoplay', 'play/pause'));\n return { mode: PxControlMode.play, warnings };\n }\n if (hasAutoplay) return { mode: PxControlMode.autoplay, warnings };\n return { mode: PxControlMode.static, warnings };\n}\n\n/**\n * True when the component must take the document's trigger over, by forcing\n * `start: 'none'` into its config patch.\n *\n * Every mode except `autoplay` — INCLUDING `static`. A component given no control props at all\n * must not start on its own: `<PixodeskSvgAnimator doc={…} />` renders the first frame and waits.\n * `autoplay` is the one mode that says \"let the document's trigger decide\".\n * @public\n */\nexport function controlModeTakesOverTrigger(mode: PxControlMode): boolean {\n return mode !== PxControlMode.autoplay;\n}\n\n/** A document's trigger with the defaults filled in. (`finish` is not a start/stop decision:\n * it reaches the engines as the runtime view's `resetOnFinish`.) * @public @advanced\n */\nexport function resolveTrigger(trigger: PxTrigger | undefined): PxResolvedTrigger {\n return {\n start: trigger?.start ?? PX_TRIGGER_DEFAULTS.start,\n offScreen: trigger?.offScreen ?? PX_TRIGGER_DEFAULTS.offScreen,\n mouseOut: trigger?.mouseOut ?? PX_TRIGGER_DEFAULTS.mouseOut,\n visibilityThreshold: trigger?.visibilityThreshold ?? PX_TRIGGER_DEFAULTS.visibilityThreshold,\n visibilityDebounce: trigger?.visibilityDebounce ?? PX_TRIGGER_DEFAULTS.visibilityDebounce,\n };\n}\n\n// V3 — every closed value list is a NAMED const + a strict `px.enum` slot, so a\n// typo is a schema ERROR instead of silently shipping. Plain `px.string()` stays\n// ONLY where SVG itself is open-ended (`gradientTransform`, `viewBox`, `path` d,\n// ids/refs, `debugGlobalName`).\n\n/** `loop.repeatAt` — WHICH END of the keyframe sequence the repeated segment is taken\n * from, and therefore which side of the timeline the repetition fills. A named\n * two-way selector (not a boolean) so a third value stays possible. * @public\n */\nexport const PxLoopRepeatAt = {\n /** Segment from the START; the repetition runs BEFORE the first keyframe\n * (intro loops that play until the main timeline begins). */\n start: 'start',\n /** DEFAULT — segment from the END; the repetition runs AFTER the last keyframe\n * (idle/outro loops that continue once the main timeline has finished). */\n end: 'end',\n} as const;\n\nexport type PxLoopRepeatAt = typeof PxLoopRepeatAt[keyof typeof PxLoopRepeatAt];\n\n/** `loop.direction` — how successive repetitions play, spelled like the timeline's\n * own `direction` so the two read as one idea. * @public\n */\nexport const PxLoopDirection = {\n /** DEFAULT — cycle: every repetition replays the segment the same way round. */\n normal: 'normal',\n /** Ping-pong: repetitions alternate forward / backward. */\n alternate: 'alternate',\n} as const;\n\nexport type PxLoopDirection = typeof PxLoopDirection[keyof typeof PxLoopDirection];\n\n/** SVG `mask-type` — how the mask source's pixels become alpha. @public */\nexport const PxMaskType = {\n luminance: 'luminance',\n alpha: 'alpha',\n} as const;\n\nexport type PxMaskType = typeof PxMaskType[keyof typeof PxMaskType];\n\n/** SVG coordinate system for `maskUnits` / `maskContentUnits` (and the gradient twin below). @public */\nexport const PxUnits = {\n userSpaceOnUse: 'userSpaceOnUse',\n objectBoundingBox: 'objectBoundingBox',\n} as const;\n\nexport type PxUnits = typeof PxUnits[keyof typeof PxUnits];\n\n/** `clone.without` — which part of the SOURCE'S OWN transform a `<use>` clone leaves\n * out. Absent = the whole element, as SVG `<use>` (a direct link, moves with the source);\n * `translate` = the source's placement is dropped, so the clone stays where the `<use>` put\n * it but still rotates/scales with the source. A future value `transform` may drop the\n * whole transform (content only) — not implemented yet.\n * (Was `clone.type: 'content'`; the wire is subtractive because the mechanism is a\n * ladder — the `<use>` can only point at one wrapper layer of the source.)\n * Old doc line:\n * `content` excludes the target's own translate (see `contentRefSplit`). * @public\n */\nexport const PxCloneWithout = {\n translate: 'translate',\n // transform: 'transform', // future: drop rotate/scale too (content only)\n} as const;\n\nexport type PxCloneWithout = typeof PxCloneWithout[keyof typeof PxCloneWithout];\n\n/** `textPath.pathOverflow` — glyphs past the path end: hide them, or keep laying\n * them along the tangent extension. * @public\n */\nexport const PxPathOverflow = {\n clip: 'clip',\n extend: 'extend',\n} as const;\n\nexport type PxPathOverflow = typeof PxPathOverflow[keyof typeof PxPathOverflow];\n\n/** SVG `lengthAdjust` — what `textLength` stretches. @public */\nexport const PxLengthAdjust = {\n spacing: 'spacing',\n spacingAndGlyphs: 'spacingAndGlyphs',\n} as const;\n\nexport type PxLengthAdjust = typeof PxLengthAdjust[keyof typeof PxLengthAdjust];\n\n/** SVG `<textPath method>` — how glyphs follow curvature. @public */\nexport const PxTextPathMethod = {\n align: 'align',\n stretch: 'stretch',\n} as const;\n\nexport type PxTextPathMethod = typeof PxTextPathMethod[keyof typeof PxTextPathMethod];\n\n/** SVG `<textPath spacing>` — whether the renderer may adjust spacing. @public */\nexport const PxTextPathSpacing = {\n auto: 'auto',\n exact: 'exact',\n} as const;\n\nexport type PxTextPathSpacing = typeof PxTextPathSpacing[keyof typeof PxTextPathSpacing];\n\n/** `strokeTrim.subPaths` — what the 0..1 `range`/`offset` window is measured over.\n * `separate` (default): each sub-path against its OWN length, all trimmed alike.\n * `combined`: every descendant sub-path chained end-to-end into one virtual path,\n * so the window slides across siblings (AE \"Trim All As One\"). * @public\n */\nexport const PxStrokeTrimSubPaths = {\n separate: 'separate',\n combined: 'combined',\n} as const;\n\nexport type PxStrokeTrimSubPaths = typeof PxStrokeTrimSubPaths[keyof typeof PxStrokeTrimSubPaths];\n\n\n// S8: `textContent` is the ONE text-content key (the DOM property name). `text` is not a wire\n// key: it was triply overloaded (the `text` tag, the `effects.text` group, and a content alias)\n// and no reader accepts it.\n/** @internal */\nexport const PX_TEXT_CONTENT_ATTR = 'textContent';\n\n/** The DOM `class` attribute. A name we EMIT but do not own, so it is written through this\n * constant rather than as an identifier — every other emitted attribute name reaches the\n * DOM as a string, and `class` was the one exception, which is why the minifier renamed it\n * to `ct` in the shipped bundles (dev-docs/plans/minification-boundary.md §1.1). */\nexport const CLASS_ATTR = 'class';\n\n/** The DOM `transform` attribute, and the key the animation record uses for it. Both are\n * DATA names — a dictionary key, not a field of one of our typed structures — so they are\n * written as constants rather than as identifiers. */\nexport const TRANSFORM_ATTR = 'transform';\n\n/** `animate.offsetDistance` — the CSS Motion Path channel the offset-path materializer writes. */\nexport const OFFSET_DISTANCE_ATTR = 'offsetDistance';\n\n// Wire keys that are NEVER DOM attributes (internal use only).\n//\n// `effects` is here for safety rather than necessity: `materializeNodeEffects` deletes it at\n// load, so today nothing reaches the renderer with it still attached. That is a property\n// of the pipeline, though, not of the contract — an effect path that returns early, or a\n// document carrying an effect key the pipeline does not recognize, would otherwise leave\n// the object behind and the renderer would write `effects=\"[object Object]\"` with no error\n// anywhere. Listing it makes the invariant structural (J4).\n/** @internal */\nexport const INTERNAL_ATTRS = new Set([\n 'type', 'children', 'animator', 'meta', 'animate', 'effects', PX_TEXT_CONTENT_ATTR\n]);\n\n// ============================================================================\n// TRANSFORM\n// ============================================================================\n\n/**\n * Names of the transform parts that can appear inside a transform value record.\n * The unified `transform` slot replaces the earlier per-part top-level keys\n * (`translate`, `rotate`, `scale`, `origin`) — those names now live as keys\n * inside a `PxTransformParts` record.\n */\n/** The transform-part names, as a named record — they are keys of a DATA record (the\n * transform value), so code reaches them through this rather than as bare literals. */\nexport const TRANSFORM_PART = {\n translate: 'translate',\n rotate: 'rotate',\n scale: 'scale',\n origin: 'origin',\n} as const;\n\n/** @public */\nexport const PX_TRANSFORM_PART_KEYS = [\n TRANSFORM_PART.translate, TRANSFORM_PART.rotate, TRANSFORM_PART.scale, TRANSFORM_PART.origin,\n] as const;\n\n/** One of the transform-part key strings. @public */\nexport type PxTransformPartKey = typeof PX_TRANSFORM_PART_KEYS[number];\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Gradient paint effect — `fillGradient` / `strokeGradient`.\n//\n// Materializer pattern mirrors `maskedByEffect`: at apply time the gradient\n// effect generates a `<linearGradient>` / `<radialGradient>` def into `ctx.defs`,\n// then sets the host element's `fill` / `stroke` to `url(#auto-id)`. The wire\n// gradient is geometry parts (`p1`/`p2` linear, `c`/`r`/`fp` radial — standard\n// animatable slots) + a stop sequence that is either static (bare array) or\n// animated (a single `{keyframes}` block whose each kf's `value` is the FULL\n// `Array<{offset, color}>` snapshot at that time). Per-stop independent\n// timelines are intentionally NOT modelled — the source is a single\n// stop-color keyframe group. Animated geometry is frames-engine only\n// (CSS/WAAPI cannot animate gradient endpoints; `mode: 'auto'` handles it).\n//\n// Stop count is constant across kfs. `gradientTransform` is captured as static\n// only (animated transform is vanishingly rare).\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Loose enums for `spreadMethod` / gradient `type` — kept on the wire as\n * plain strings (matches the rest of the schema's loose-enum stance) but\n * collected here so call sites use named constants instead of bare literals.\n *\n * `gradientUnits` has NO enum of its own: it takes the same two values as every other\n * units slot, so it reuses {@link PxUnits} (review §2.7 — one name per value set). * @public\n */\nexport const PxGradientSpreadMethod = {\n pad: 'pad',\n reflect: 'reflect',\n repeat: 'repeat',\n} as const;\n\nexport type PxGradientSpreadMethod = typeof PxGradientSpreadMethod[keyof typeof PxGradientSpreadMethod];\n\n/** @public */\nexport const PxGradientType = {\n linear: 'linear',\n radial: 'radial',\n} as const;\n\nexport type PxGradientType = typeof PxGradientType[keyof typeof PxGradientType];\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/** @public @advanced */\nexport function isPxDocument(doc: any): doc is PxAnimatedSvgDocument {\n if (!(\n doc &&\n typeof doc === 'object' &&\n !Array.isArray(doc)\n )) {\n return false;\n }\n\n // `type` is the tag, and the ONLY discriminator — it is what the schema requires\n // (`px.literal('svg')`). A `tagName` alternative was accepted here until 2026-08,\n // which meant a tagName-only document passed this gate and then failed\n // `isValidPxDocument`; nothing ever wrote it.\n return doc.type === 'svg';\n}\n\n/**\n * The animator config, at either of its TWO canonical addresses (S4).\n *\n * `animator` is the only name. It has two addresses because the SVG form has no other\n * slot: a `.svga`/JSON document carries it at the top level, while a pre-rendered\n * `.svg` carries it inside the root element's `data-px-meta` blob — i.e. under `meta`.\n * The editor lifts/un-lifts between the two on write/read.\n *\n * The `animation` / `meta.animation` spellings were removed 2026-08: nothing wrote\n * them and they were never in the schema.\n * @public @advanced\n */\nexport function getAnimatorConfig(doc: PxAnimatedSvgDocument): PxAnimatorConfig | undefined {\n const cfg = doc?.animator || doc?.meta?.animator;\n if (!cfg) return undefined;\n const memoised = wireViewMemo.get(cfg as object);\n if (memoised) return memoised;\n\n // A document states playback ONLY inside `timeline`. A flat key at the animator root is not a\n // second spelling to honor — it is an unknown key (`validateDocument` and the entry diagnostic\n // both report it), so it is dropped here and never reaches an engine.\n const wire = cfg as Record<string, unknown>;\n const stray = PX_FLAT_RUNTIME_VIEW_KEYS.filter(k => wire[k] !== undefined);\n const source = stray.length ? { ...wire } : cfg;\n for (const k of stray) delete (source as Record<string, unknown>)[k];\n\n // Every internal consumer sees the FLAT view — the nested `timeline` spelling is\n // folded down here, once, so the engines/effects/drivers never branch on it.\n const view = flattenAnimatorTimeline(source as PxAnimatorConfig);\n // Memoised on the DOCUMENT's config: repeated calls must return the same object (callers\n // compare identity and cache off it), and `source` is a fresh object when keys were dropped.\n wireViewMemo.set(cfg as object, view);\n return view;\n}\n\n/** Memo for {@link getAnimatorConfig} — see the identity note inside it. */\nconst wireViewMemo = new WeakMap<object, PxAnimatorConfig>();\n\n\n// ============================================================================\n// TIMELINE SPELLING (review §2.1)\n//\n// The wire spelling is `animator.timeline: { type?: 'time'|'scroll'|'view', … }` (absent = 'time');\n// the flat form (`timelineSource` + `scroll` + loose clock knobs) is the INTERNAL\n// runtime view only — not a wire format. These two functions convert between them:\n// • flattenAnimatorTimeline — wire → runtime view; applied by `getAnimatorConfig`,\n// so ALL runtime code keeps consuming the flat form it always has.\n// • nestAnimatorTimeline — runtime view → wire; applied by writers (the editor) so\n// files carry only the nested spelling and its mode-dead keys are structurally absent.\n// ============================================================================\n\n/** Memo: flatten allocates a new config; repeated `getAnimatorConfig` calls must keep\n * returning the SAME object (some callers compare identity / cache off it). */\nconst flattenMemo = new WeakMap<object, PxAnimatorConfig>();\n\n/**\n * Folds `cfg.timeline` (the wire spelling) into the flat runtime-view fields the engines\n * consume. Returns `cfg` unchanged when there is nothing to fold. Never mutates input.\n * @public @advanced\n */\nexport function flattenAnimatorTimeline(cfg: PxAnimatorConfig): PxAnimatorConfig {\n const timeline: any = (cfg as any).timeline;\n if (timeline === undefined || timeline === null || typeof timeline !== 'object') return cfg;\n\n const memoised = flattenMemo.get(cfg as object);\n if (memoised) return memoised;\n\n const { timeline: _dropped, ...flat } = cfg as any;\n\n // `engine` and `frameRate` are shared by every timeline type: how the attributes get\n // updated, and at what rate when that is the player's own frame loop.\n if (timeline.engine !== undefined) flat.engine = timeline.engine;\n if (timeline.frameRate !== undefined) flat.frameRate = timeline.frameRate;\n\n if (timeline.type === 'scroll' || timeline.type === 'view') {\n flat.timelineSource = 'scroll';\n if (timeline.duration !== undefined) flat.duration = timeline.duration; // §2.8\n if (timeline.iterations !== undefined) flat.iterations = timeline.iterations;\n const scroll: PxScroll = { ...(flat.scroll || {}) };\n scroll.kind = timeline.type;\n if (timeline.axis !== undefined) scroll.axis = timeline.axis;\n if (timeline.source !== undefined) scroll.source = timeline.source;\n if (timeline.subject !== undefined) scroll.subject = timeline.subject;\n if (timeline.smoothing !== undefined) scroll.smoothing = timeline.smoothing;\n if (timeline.range !== undefined) scroll.range = timeline.range;\n const pin = timeline.pin;\n if (typeof pin === 'boolean') scroll.pin = pin;\n else if (pin && typeof pin === 'object') {\n scroll.pin = true;\n if (pin.align !== undefined) scroll.pinAlign = pin.align;\n if (pin.offset !== undefined) scroll.pinOffset = pin.offset;\n if (pin.distance !== undefined) scroll.pinDistance = pin.distance;\n }\n flat.scroll = scroll;\n } else { // 'time', absent, or unknown — the time-driven timeline is the default\n if (timeline.duration !== undefined) flat.duration = timeline.duration; // §2.8\n if (timeline.trigger !== undefined) {\n const { finish, ...restTrigger } = timeline.trigger;\n if (Object.keys(restTrigger).length) flat.trigger = restTrigger;\n if (finish !== undefined) flat.resetOnFinish = finish === 'reset';\n }\n if (timeline.delay !== undefined) flat.delay = timeline.delay;\n if (timeline.iterations !== undefined) flat.iterations = timeline.iterations;\n if (timeline.direction !== undefined) flat.direction = timeline.direction;\n if (timeline.fillMode !== undefined) flat.fill = timeline.fillMode; // wire `fillMode` → runtime `fill`\n }\n\n flattenMemo.set(cfg as object, flat);\n return flat;\n}\n\n/**\n * Which scroll-driven member an absent `scroll.kind` selects — `'view'`, per `_PxScroll.kind`.\n *\n * The flat view says WHICH FAMILY drives progress (`timelineSource: 'scroll'`) separately from\n * WHICH MEMBER of it (`scroll.kind`), and only the family is named \"scroll\". Defaulting the\n * member to its family's name reads natural and is wrong: it silently rewrote every document\n * that left the kind at its default — which, being the default, is most of them.\n */\nfunction scrollKindOrDefault(kind: unknown): 'view' | 'scroll' {\n return kind === 'scroll' ? 'scroll' : 'view';\n}\n\n/**\n * Converts a FLAT animator config into the written spelling: mode-specific keys fold into\n * one discriminated `timeline` object; the legacy flat keys are removed from the output.\n * Returns a new object (input untouched); a config already carrying `timeline` passes\n * through unchanged; a pure-shared config (duration/mode/… only) gets no `timeline` at all.\n * @public @advanced\n */\nexport function nestAnimatorTimeline(cfg: PxAnimatorConfig): PxAnimatorConfig {\n if (!cfg || (cfg as any).timeline !== undefined) return cfg;\n\n const { timelineSource, scroll, trigger, delay, iterations, direction, fill, resetOnFinish,\n duration, engine, frameRate, ...shared } = cfg as any;\n\n if (timelineSource === 'scroll') {\n const timeline: any = { type: scrollKindOrDefault(scroll?.kind) };\n if (engine !== undefined) timeline.engine = engine;\n if (frameRate !== undefined) timeline.frameRate = frameRate;\n if (duration !== undefined) timeline.duration = duration; // §2.8\n // Finite iterations survive scrubbing (D4); 'infinite' cannot map to a range.\n if (typeof iterations === 'number') timeline.iterations = iterations;\n if (scroll) {\n if (scroll.axis !== undefined) timeline.axis = scroll.axis;\n if (scroll.source !== undefined) timeline.source = scroll.source;\n if (scroll.subject !== undefined) timeline.subject = scroll.subject;\n if (scroll.smoothing !== undefined) timeline.smoothing = scroll.smoothing;\n if (scroll.range !== undefined) timeline.range = scroll.range;\n const hasPinParams = scroll.pinAlign !== undefined || scroll.pinOffset !== undefined || scroll.pinDistance !== undefined;\n if (hasPinParams) {\n timeline.pin = {\n ...(scroll.pinAlign !== undefined ? { align: scroll.pinAlign } : {}),\n ...(scroll.pinOffset !== undefined ? { offset: scroll.pinOffset } : {}),\n ...(scroll.pinDistance !== undefined ? { distance: scroll.pinDistance } : {}),\n };\n } else if (scroll.pin !== undefined) {\n timeline.pin = scroll.pin;\n }\n }\n return { ...shared, timeline };\n }\n\n // Time-driven: `type` is optional on the wire and 'time' is the default, so the\n // writer omits it — the common case declares nothing.\n const timeline: any = {};\n if (engine !== undefined) timeline.engine = engine;\n if (frameRate !== undefined) timeline.frameRate = frameRate;\n if (duration !== undefined) timeline.duration = duration; // §2.8\n if (trigger !== undefined || resetOnFinish) {\n const t: any = { ...(trigger || {}) };\n if (resetOnFinish) t.finish = 'reset';\n timeline.trigger = t;\n }\n if (delay !== undefined) timeline.delay = delay;\n if (iterations !== undefined) timeline.iterations = iterations;\n if (direction !== undefined) timeline.direction = direction;\n if (fill !== undefined) timeline.fillMode = fill; // runtime `fill` → wire `fillMode`\n\n // An empty time timeline says nothing — omit the block entirely.\n return Object.keys(timeline).length > 0 ? { ...shared, timeline } : shared;\n}\n\n\n/** @public @advanced */\nexport function getDefinitions(doc: PxAnimatedSvgDocument): PxDefinitions | undefined {\n if (!doc) return undefined;\n return getAnimatorConfig(doc)?.definitions;\n}\n\n/** The bind-by-id document's `animator.bindings`, as written — `target` keeps its `#`. @public @advanced */\nexport function getBindings(doc: PxAnimatedSvgDocument): PxBinding[] | undefined {\n if (!doc) return undefined;\n return getAnimatorConfig(doc)?.bindings;\n}\n\n\n/** @public @advanced */\nexport function getChildren(doc: PxAnimatedSvgDocument): PxNode[] | undefined {\n return doc?.children;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport type { KeysMatch, PxInfer, PxSchema, PxValidationContext, PxRemoveIndex } from '../schema/PxSchema';\nimport { implementsInterface, px } from '../schema/PxSchema';\n// Constants live in their own module so importing one does not pull the schema engine\n// in; re-exported here so this module's public surface is unchanged. See there.\nexport * from './PxAnimatorConstants';\nimport { getAnimatorConfig, INTERNAL_ATTRS, isPxDocument, PX_TRANSFORM_PART_KEYS, PX_TRIGGER_DEFAULTS, PxTimelineEngineSetting, PxCloneWithout, PxPathOverflow, PxGradientSpreadMethod, PxGradientType, PxLengthAdjust, PxLoopRepeatAt, PxLoopDirection, PxMaskType, PxTextPathMethod, PxTextPathSpacing, PxStrokeTrimSubPaths, PxUnits,\n // Wire enums named in review §2.7 — used as VALUES by the schemas below.\n PxAlongPathMode, PxFillMode, PxFinishAction, PxMouseOutAction, PxOffScreenAction, PxPinAlign, PxPlaybackDirection,\n PxScrollAxis, PxScrollKind, PxScrollPhase, PxScrollSource, PxTriggerStart } from './PxAnimatorConstants';\nimport type { PxTimelineEngine, PxTransformPartKey } from './PxAnimatorConstants';\n// Version stamps are parsed by the reader's own parser (review §2.10) so the validator and the\n// reader can never disagree on what counts as a stamp. `PxWireVersion` imports only\n// `PxSchemaVersion`, so this edge creates no cycle.\nimport { parseWireVersion } from '../version/PxWireVersion';\n// The diagnostics channel's payload, named by `PxEngineCallbacks` below (review §5).\n// `PxDiagnostics` imports nothing, so this edge creates no cycle either.\nimport type { PxDiagnosticKind, PxDiagnosticsConfig } from '../playback/PxDiagnostics';\n\n// ============================================================================\n// EASING\n// ============================================================================\n\n/**\n * Easing function definition.\n * Can be a named reference to a predefined easing or a cubic-bezier array [x1, y1, x2, y2].\n *\n * @example \"ease-in\" | [0.68, -0.55, 0.265, 1.55]\n *\n * `string | [x1, y1, x2, y2]`\n * @public @advanced\n */\nexport const PxEasingOrRefSchema = px.union([\n px.string(),\n px.tuple([px.number(), px.number(), px.number(), px.number()] as const),\n]);\n\n/**\n * Easing function definition.\n * Can be a named reference to a predefined easing or a cubic-bezier array [x1, y1, x2, y2].\n *\n * @example \"ease-in\" | \"easeOut\" | [0.68, -0.55, 0.265, 1.55]\n */\nexport type PxEasingOrRef = PxInfer<typeof PxEasingOrRefSchema>;\n\n\n// ============================================================================\n// KEYFRAME\n// ============================================================================\n\n/**\n * A single animation keyframe defining the state at a specific point in time.\n *\n * THE WIRE FORM, and only that: `time` / `value` / `easing` / `tangentIn` / `tangentOut`.\n * Locked to `PxKeyframeSchema` by the `KeysMatch` assertion below, so this interface and the\n * validator cannot drift apart.\n *\n * The engines consume {@link _PxNormalizedKeyframe} instead — the short-field form\n * `normalizeKeyframes` produces, with easing refs resolved and values parsed. The two used to\n * be ONE interface carrying both spellings, which meant no key-set lock was possible here and\n * nothing in the types said which form a given function expected.\n */\nexport interface _PxKeyframe {\n\n /** Timestamp in milliseconds from animation start */\n time?: number;\n\n /** The value of the animated property at this keyframe */\n value?: any;\n\n /** Easing function applied to the interval from this keyframe to the next */\n easing?: PxEasingOrRef;\n\n /**\n * Outgoing spatial tangent `[dx, dy]` for motion-along-path interpolation\n * (translate animations only).\n *\n * Stored as a *delta relative to* this keyframe's translate position —\n * the cubic Bezier segment between this kf and the next is built from\n * `(P0=value, P1=value+tangentOut, P2=next.value+next.tangentIn, P3=next.value)`.\n *\n * Defined when the segment leaving this keyframe is curved.\n */\n tangentOut?: [number, number];\n\n /**\n * Incoming spatial tangent `[dx, dy]` for motion-along-path interpolation\n * (translate animations only). Delta relative to this keyframe's translate\n * position. See `tangentOut` for the segment construction.\n *\n * Defined when the segment arriving at this keyframe is curved.\n */\n tangentIn?: [number, number];\n\n}\n\n/**\n * Allowed shapes for a single keyframe `value` across the wire schema:\n * - `number` — scalar properties (rotate-degree, opacity, offset-distance, …)\n * - `Array<number>` — vector properties (translate `[x,y]`, scale `[sx,sy]`, stroke-dasharray, RGBA …)\n * - `string` — color (hex / `url(#…)` / named) and other string-valued props\n * - `PxTransformParts` — unified body `transform` parts record\n * `{translate, rotate, scale, origin}`\n * - `{ paths: Array<PxBezierPath> }` — animated SVG path `d` value\n * - `Array<PxGradientStop>` — gradient `stops` timeline (each kf value is\n * the FULL `[{offset, color}, …]` snapshot)\n *\n * Plugged into `PxKeyframeSchema.value` / `.v` — every keyframe value on the\n * wire is validated against this union. The inferred TS type of `PxKeyframe`\n * stays permissive (`value?: any` via the generic default) so the duck-typed\n * interpolator code in `PxDefinitions.ts` (`prevV?.paths`,\n * `Array.isArray(prevV)`, …) keeps working without per-shape narrowing.\n */\nexport type _PxKeyframeValue =\n | string\n | number\n | Array<number>\n | PxTransformParts\n | { pathData: string }\n | Array<_PxGradientStop>;\n\n// `string | number | Array<number> | PxTransformParts | { pathData: string }`\n//\n// `{ pathData: \"M…\" }` is the ONE form for animated path geometry: a single `d` string\n// (a compound shape is one string with several `M…` sub-paths). The Lottie-style\n// `{ paths: [{v,i,o,c}] }` array was retired 2026-09-11 — it lives on only as the\n// interpolator's INTERNAL shape (`normalizePathValue` in `PxDefinitions.ts`).\n//\n// `PxTransformPartsSchema` is declared later in this file — `px.lazy` defers the lookup\n// until validation time so the declarations stay in narrative order without a TDZ at load.\n/** @public @advanced */\nexport const PxKeyframeValueSchema = implementsInterface<_PxKeyframeValue>()(px.union([\n px.string(), // e.g. for colors\n px.number(),\n px.array(px.number()),\n // ORDER LAW: the key-discriminated object shape (`{pathData}`) comes BEFORE the\n // all-optional transform-parts record. In default (non-strict) mode that record accepts\n // ANY object (every key optional, unknown keys ignored), so listing it earlier made\n // Union.sanitize route `{pathData}` values into it and strip them to `{}` —\n // silent morph-data loss (repro: the editor's keyframeValueSanitize spec). Validity is\n // order-independent (`some()`); only sanitize routing depends on this order.\n px.object({ pathData: px.string() }),\n // Gradient `stops` timeline — each kf value is the full stops-array snapshot.\n px.lazy<Array<_PxGradientStop>>(() => px.array(PxGradientStopSchema), []),\n px.lazy<PxTransformParts>(() => PxTransformPartsSchema, {}),\n]));\n\n/** A single keyframe `value` — union of all wire-allowed shapes. */\nexport type PxKeyframeValue = PxInfer<typeof PxKeyframeValueSchema>;\n\n// `{ time?:number, t?:number, value?:PxKeyframeValue, v?:PxKeyframeValue,\n// easing?:Easing, e?:Easing, tangentOut?:[dx,dy], tangentIn?:[dx,dy] }`\n//\n// `value` / `v` validate against {@link PxKeyframeValueSchema} — malformed\n// keyframe values are now schema errors instead of passing as `px.any()`\n// (SCHEMA-DESIGN I-5). The `_PxKeyframe` interface keeps `value?: any` (and\n// `PxKeyframe<T = any>` its generic default) so the duck-typed interpolator\n// access in `PxDefinitions.ts` stays untyped-permissive at compile time.\n// LONG SPELLINGS ONLY (review §1.2/§6.1): the short aliases (`t`/`v`/`e`/`to`/`ti`)\n// were removed from the wire outright — one clear spelling, no mixing ambiguity.\n// They survive only as the internal normalized runtime view (see `_PxKeyframe`).\n/** @public @advanced */\nexport const PxKeyframeSchema = implementsInterface<_PxKeyframe>()(px.object({\n time: px.number().optional(),\n value: PxKeyframeValueSchema.optional(),\n easing: PxEasingOrRefSchema.optional(),\n tangentOut: px.tuple([px.number(), px.number()] as const).optional(),\n tangentIn: px.tuple([px.number(), px.number()] as const).optional(),\n // (`selected` — editor timeline-selection UI state — was REMOVED from the wire\n // (review §1.3): editor data lives under `meta`. The editor still carries it on\n // its internal COPY-PASTE payload, which never validates against this schema.)\n}));\n\n/**\n * THE RUNTIME FORM — what `normalizeKeyframes` hands the engines, and what the tree-level\n * materializers (`materializeInternalLoopsInTree` and everything after it in\n * `materializeAllInTree`) write back into the document.\n *\n * Short-named on purpose: these are read once per property per frame. `e` is a RESOLVED easing\n * (named refs already looked up in `definitions.easings`) and `v` is a PARSED value (colors as\n * RGBA arrays, path `d` normalized) — which is the substantive difference from the wire form,\n * not just the spelling. Deliberately NOT a wire shape: `validateDocument` rejects it, and a\n * materialized document is a runtime artefact that is never written to disk.\n */\nexport interface _PxNormalizedKeyframe {\n /** Time in ms from the animation start (the wire spells it `time`). */\n t?: number;\n /** The parsed value at this keyframe (the wire spells it `value`). */\n v?: any;\n /** The RESOLVED easing — never a name (the wire spells it `easing`). */\n e?: PxEasingOrRef;\n /** Incoming spatial tangent, same meaning as the wire's. */\n tangentIn?: [number, number];\n /** Outgoing spatial tangent, same meaning as the wire's. */\n tangentOut?: [number, number];\n}\n\n/** @internal */\nexport type PxNormalizedKeyframe = _PxNormalizedKeyframe;\n\n/**\n * Either spelling. For the handful of helpers that genuinely run on BOTH sides of\n * normalization — read them through the `kf*` accessors below rather than branching inline.\n * @internal\n */\nexport type PxAnyKeyframe = _PxKeyframe | _PxNormalizedKeyframe;\n\nconst anyKf = (kf: PxAnyKeyframe) => kf as _PxKeyframe & _PxNormalizedKeyframe;\n\n/** Time in ms, whichever spelling the keyframe is in. @internal */\nexport const keyframeTime = (kf: PxAnyKeyframe): number => anyKf(kf).time ?? anyKf(kf).t ?? 0;\n/** Value, whichever spelling. @internal */\nexport const keyframeValue = (kf: PxAnyKeyframe): any => anyKf(kf).value ?? anyKf(kf).v;\n/** Easing — resolved on a normalized keyframe, possibly a NAME on a wire one. @internal */\nexport const keyframeEasing = (kf: PxAnyKeyframe): PxEasingOrRef | undefined => anyKf(kf).easing ?? anyKf(kf).e;\n/** Incoming spatial tangent, whichever spelling. @internal */\nexport const keyframeTangentIn = (kf: PxAnyKeyframe): [number, number] | undefined => anyKf(kf).tangentIn;\n/** Outgoing spatial tangent, whichever spelling. @internal */\nexport const keyframeTangentOut = (kf: PxAnyKeyframe): [number, number] | undefined => anyKf(kf).tangentOut;\n\n/**\n * A single animation keyframe defining the state at a specific point in time.\n *\n * Generic over the keyframe `value` type for callers that know the per-property\n * value shape (e.g. `PxKeyframe<PxVec2>` in the effect appliers). Defaults to\n * `any`, matching the schema (`value` is stored as `px.any()` on the wire).\n * @public\n */\n// The WIRE type, generic over the value type. The engines use `PxNormalizedKeyframe`.\nexport type PxKeyframe<T = any> = Omit<_PxKeyframe, 'value'> & { value?: T };\n// Locks the interface to the schema at the default instantiation.\nconst _ck_PxKeyframe: KeysMatch<PxInfer<typeof PxKeyframeSchema>, _PxKeyframe> = true;\n\n/** {@link PxNormalizedKeyframe}, generic over the value type — the runtime counterpart. */\nexport type PxNormalizedKeyframeOf<T = any> = Omit<_PxNormalizedKeyframe, 'v'> & { v?: T };\n\n/**\n * A property animation whose keyframes are in the RUNTIME form.\n *\n * What `normalizeKeyframes` produces, what the engines consume — and what the EDITOR's in-memory\n * model is: its keyframe objects carry the short field names and serialize to the long wire ones\n * through `@serializable`, so the model implements this rather than the wire shape.\n * @internal\n */\nexport type PxNormalizedPropertyAnimation =\n Omit<_PxPropertyAnimation, 'keyframes'> & { keyframes?: Array<_PxNormalizedKeyframe> };\n\n\n// ============================================================================\n// LOOP\n// ============================================================================\n\n/**\n * Defines how a property's keyframe animation is extended beyond its defined keyframe range\n * by continuously repeating a chosen segment of the sequence.\n *\n * The repeated segment is a contiguous run of keyframe *intervals* (gaps between consecutive\n * keyframes). Which end of the sequence is repeated is controlled by `before`, and whether\n * each repetition plays in the same direction or alternates is controlled by `alternate`.\n *\n * **Relationship to `animator.iterations`**\n *\n * `PxLoop` and `animator.iterations` are independent mechanisms operating at different levels:\n *\n * - `PxLoop` is a **pre-processing step**: it expands the property's keyframe list to fill the\n * full `animator.duration` before any playback begins. The runtime sees a single, fully\n * expanded keyframe sequence — it has no knowledge of the loop.\n *\n * - `animator.iterations` repeats the **entire document timeline** (all properties, all\n * elements) as a unit, after the expanded keyframes are already in place.\n *\n * The two compose independently: a property with `loop: true` inside a document with\n * `iterations: \"infinite\"` will cycle its own segment within each document iteration, and\n * that iteration will itself repeat forever — loop-within-loop.\n */\nexport interface _PxLoop {\n\n /**\n * Number of keyframe intervals (gaps between consecutive keyframes) that form the repeating\n * segment.\n *\n * - `undefined` → the entire keyframe sequence is used as the loop segment.\n * - `N` → only the first `N` intervals (when `repeatAt: 'start'`) or the last `N`\n * intervals (when `repeatAt: 'end'`) are looped. Clamped to `[1, keyframes.length - 1]`.\n */\n segmentCount?: number;\n\n /**\n * Which end of the keyframe sequence the repetition fills — see {@link PxLoopRepeatAt}.\n * `'start'` repeats ahead of the first keyframe (intro loop); `'end'` (default)\n * repeats past the last (idle/outro loop).\n */\n repeatAt?: PxLoopRepeatAt;\n\n /**\n * How successive repetitions play — see {@link PxLoopDirection}.\n *\n * - `'normal'` (default) → **cycle**: every repetition replays the segment the same way round.\n * - `'alternate'` → **ping-pong**: repetitions alternate forward and backward\n * (even repetitions play forward, odd ones in reverse).\n */\n direction?: PxLoopDirection;\n}\n\n// `{ segmentCount?:number, repeatAt?:'start'|'end', direction?:'normal'|'alternate' }`\n/** @public @advanced */\nexport const PxLoopSchema = implementsInterface<_PxLoop>()(px.object({\n segmentCount: px.number().optional(),\n repeatAt: px.enum([PxLoopRepeatAt.start, PxLoopRepeatAt.end] as const).optional(),\n direction: px.enum([PxLoopDirection.normal, PxLoopDirection.alternate] as const).optional(),\n}));\n\n/**\n * Defines how a property's keyframe animation is extended beyond its defined keyframe range\n * by continuously repeating a chosen segment of the sequence.\n * @public\n */\nexport type PxLoop = PxInfer<typeof PxLoopSchema>;\nconst _ck_PxLoop: KeysMatch<PxLoop, _PxLoop> = true; // the key sets are identical\n\n\n// ============================================================================\n// PROPERTY ANIMATION\n// ============================================================================\n\n/**\n * Animation definition for a single CSS/SVG property.\n * Contains an array of keyframes that define how the property changes over time.\n */\nexport interface _PxPropertyAnimation {\n\n /**\n * Optional static / base value for the animated property.\n *\n * Two uses:\n * - structured static: `{value}` with no keyframes is the static form of\n * the universal animatable pattern (`PxAnimatable<T>`);\n * - base + keyframes: when both are present, `value` is the baseline the\n * animation starts from. For most properties keyframe values are\n * complete and `value` is just the pre-tick DOM baseline; slots with\n * patch semantics (the editor's extended-d `shape` effect) merge each\n * keyframe's partial value over this base.\n */\n value?: any;\n\n /** Array of keyframes defining the animation timeline */\n keyframes?: PxKeyframe[];\n\n /**\n * Optional loop configuration. When set, the keyframe sequence is expanded at pre-processing\n * time to fill the gap between the keyframe range and `animator.duration` by repeating a\n * chosen segment. `true` is shorthand for the default {@link PxLoop} (loop the last segment\n * after the final keyframe, cycling forward). See {@link PxLoop} for details.\n *\n * Note: this operates independently of `animator.iterations` — see {@link _PxLoop} for the\n * interaction between the two.\n */\n loop?: PxLoop | boolean;\n\n /**\n * Motion-along-path \"auto-orient\" flag. Only meaningful for translate\n * animations whose keyframes carry spatial tangents (`tangentIn` /\n * `tangentOut`): when true, the element rotates so its local X axis aligns\n * with the path tangent at the current position. The rotation is computed\n * from the cubic-Bezier derivative at the eased progress along the\n * arc-length-parametrised segment.\n */\n autoOrient?: boolean;\n\n /**\n * How a motion-along-path `transform` animation is RENDERED: `'sampled'` (default,\n * absent) — the path is pre-sampled into plain transform keyframes;\n * `'offsetPath'` — the browser drives it as a CSS Motion Path (`offset-path` /\n * `offset-distance`). Written by the editor, consumed by `materializeAllInTree`.\n */\n alongPathMode?: 'sampled' | 'offsetPath';\n}\n\n// `{ value?:KeyframeValue, keyframes?:Keyframe[], loop?:Loop|boolean, autoOrient?:bool, alongPathMode?:'sampled'|'offsetPath' }`\n// (the `kfs` alias was removed outright — review §1.2/§6.1: one spelling only)\n/** @public @advanced */\nexport const PxPropertyAnimationSchema = implementsInterface<_PxPropertyAnimation>()(px.object({\n value: PxKeyframeValueSchema.optional(),\n keyframes: px.array(PxKeyframeSchema).optional(),\n loop: px.union([PxLoopSchema, px.boolean()]).optional(),\n autoOrient: px.boolean().optional(),\n alongPathMode: px.enum([PxAlongPathMode.sampled, PxAlongPathMode.offsetPath] as const).optional(),\n}));\n\n/** Animation definition for a single CSS/SVG property. @public */\n// The runtime-VIEW type: its `keyframes` items are runtime-view PxKeyframes (they may\n// carry the internal normalized short fields), which the schema-inferred type cannot.\nexport type PxPropertyAnimation = _PxPropertyAnimation;\n// KeysMatch compares only the KEY SETS, so it still locks the schema to the interface even\n// though the two disagree about the VALUE type of `keyframes` (above). Worth having here in\n// particular: this is the object that carried the `kfs` alias until it was deleted.\nconst _ck_PxPropertyAnimation: KeysMatch<PxInfer<typeof PxPropertyAnimationSchema>, _PxPropertyAnimation> = true;\n\n\n/**\n * Record of transform parts forming a single transform `value`. Each present\n * key contributes one segment of the composed CSS transform string at render /\n * interpolation time, in the canonical order\n * `translate, translate(+origin), rotate, scale, translate(-origin)`.\n *\n * `origin` is meaningful only when `rotate` or `scale` is also present in the\n * same record — see \"When does origin belong inside a keyframe value?\" in\n * `file-format-remaining-design-issues2.md`.\n */\nexport interface _PxTransformParts {\n\n /** Translation offset `[x, y]` in user units. */\n translate?: [number, number];\n\n /** Rotation in degrees. */\n rotate?: number;\n\n /** Skew (skewX) in degrees, pivoting at `origin` — composed between `rotate`\n * and `scale` (matches Lottie's transform order). */\n skew?: number;\n\n /** Scale factor `[sx, sy]`. */\n scale?: [number, number];\n\n /**\n * Pivot for rotate / scale `[x, y]`. Only meaningful alongside `rotate` or\n * `scale` in the same record.\n */\n origin?: [number, number];\n}\n\n// `{ translate?:[x,y], rotate?:deg, skew?:deg, scale?:[sx,sy], origin?:[x,y] }`\n/** @public @advanced */\nexport const PxTransformPartsSchema = implementsInterface<_PxTransformParts>()(px.object({\n translate: px.tuple([px.number(), px.number()] as const).optional(),\n rotate: px.number().optional(),\n skew: px.number().optional(),\n scale: px.tuple([px.number(), px.number()] as const).optional(),\n origin: px.tuple([px.number(), px.number()] as const).optional(),\n}));\n\n/** Record of transform parts forming a single transform `value`. @public */\nexport type PxTransformParts = PxInfer<typeof PxTransformPartsSchema>;\nconst _ck_PxTransformParts: KeysMatch<PxTransformParts, _PxTransformParts> = true; // the key sets are identical\n\n/**\n * Unified `transform` slot value. Valid shapes:\n *\n * - **Bare parts record** — `{translate:[100,100], rotate:45, scale:[1.5,1.5],\n * origin:[25,25]}` — THE canonical lightweight static (SCHEMA-DESIGN §2/S1):\n * the authored parts verbatim, the exact record grammar animated keyframe\n * values use. Unambiguous because a body attr never carries animation — that\n * lives in the parallel `animate` channel (R2); the parts keys are disjoint\n * from the animation-wrapper keys.\n * - **SVG transform string** — `\"translate(125,125)rotate(45)…translate(-25,-25)\"`.\n * The pre-rendered/browser form (origin baked into a pivot sandwich) and the\n * foreign-SVG import path. Read forever.\n * - **Structured static** — `{value: PxTransformParts}`. Read-accepted legacy\n * spelling of the record.\n * - **Animated** — `{keyframes: [{time, value: PxTransformParts, …}, …]}`.\n * Legacy inline form (the editor writes the `animate` channel instead).\n *\n * Replaces the earlier convention of putting each animated transform part\n * under its own top-level attribute name (`translate`, `rotate`, `scale`,\n * `origin`).\n * @public @advanced\n */\nexport const PxTransformValueSchema = px.union([\n px.string(),\n PxTransformPartsSchema,\n px.object({ value: PxTransformPartsSchema }),\n PxPropertyAnimationSchema,\n]);\n\n/** Unified `transform` slot value: string | structured static | animated. @public */\nexport type PxTransformValue = PxInfer<typeof PxTransformValueSchema>;\n\n\n// ============================================================================\n// ANIMATION DEFINITION\n// ============================================================================\n\n/**\n * Complete animation definition containing one or more property animations.\n * Each key is a CSS/SVG property name (e.g., \"opacity\", \"translate\", \"fill\").\n *\n * @example\n * { \"opacity\": { keyframes: [...] }, \"translate\": { keyframes: [...] } }\n */\nexport interface _PxAnimationDefinition {\n [property: string]: PxPropertyAnimation;\n}\n\n// `Record<propName, PropertyAnimation>`\n/** @public @advanced */\nexport const PxAnimationDefinitionSchema = implementsInterface<_PxAnimationDefinition>()(\n px.record(PxPropertyAnimationSchema)\n);\n\n/**\n * Complete animation definition containing one or more property animations.\n * Each key is a CSS/SVG property name (e.g., \"opacity\", \"scale\", \"rotate\").\n * @public\n */\nexport type PxAnimationDefinition = PxInfer<typeof PxAnimationDefinitionSchema>;\n\n\n// ============================================================================\n// ELEMENT ANIMATION\n// ============================================================================\n\n/**\n * Element animation specification. Can be:\n * - A string referencing a named animation from `definitions.animations`\n * - An array of named references\n * - An inline `AnimationDefinition` object\n * - A mixed array of references and inline definitions\n *\n * @example\n * \"fadeIn\"\n * [\"fadeIn\", \"spin\"]\n * { opacity: { keyframes: [...] } }\n * [\"fadeIn\", { scale: { keyframes: [...] } }]\n */\nexport type _PxElementAnimation =\n | string\n | string[]\n | PxAnimationDefinition\n | (string | PxAnimationDefinition)[];\n\n// `string | Array<string|AnimationDefinition> | AnimationDefinition`\n/** @public @advanced */\nexport const PxElementAnimationSchema = implementsInterface<_PxElementAnimation>()(px.union([\n px.string(),\n px.array(px.union([px.string(), PxAnimationDefinitionSchema])),\n PxAnimationDefinitionSchema,\n]));\n\n/**\n * Element animation specification.\n * Can be a string reference, array of references, inline definition, or a mixed array.\n * @public\n */\nexport type PxElementAnimation = PxInfer<typeof PxElementAnimationSchema>;\n\n\n// ============================================================================\n// TRIGGER\n// ============================================================================\n\n/**\n * Defines when and how an animation should be triggered.\n */\nexport interface _PxTrigger {\n\n /** What starts the animation. Default `'load'` — a document is designed to play;\n * `'none'` waits for `play()`. Starting is one axis; whether it may RUN is the other\n * (`offScreen` below), so every combination is sayable. */\n start?: PxTriggerStart;\n\n /** What happens while none of the graphic is on screen, whatever started it.\n * Default `'pause'` — an animation nobody can see does not run. */\n offScreen?: PxOffScreenAction;\n\n /** What happens when the pointer leaves. Read only when `start` is `'mouseOver'`.\n * Default `'continue'`. */\n mouseOut?: PxMouseOutAction;\n\n /** How much of the graphic must be on screen before it may run, as a fraction of its own\n * area (0–1, default 0.5). The gate OPENS here and closes only at zero visibility, so one\n * value serves two edges and a graphic resting on the boundary cannot flap. */\n visibilityThreshold?: number;\n\n /** How long, in ms, `visibilityThreshold` must hold before playback actually starts\n * (default 150). Scrolling straight past a graphic therefore starts nothing. `0` starts\n * the moment the threshold is met. */\n visibilityDebounce?: number;\n\n /** After a NATURAL finish: `'hold'` (default — keep the end state per `fill`) or `'reset'`\n * (snap back to the start). Named for its occasion like its siblings, and NOT `onFinish`,\n * which is the CALLBACK on `PxEngineCallbacks` — a value key and a function key with\n * one name read badly side by side in a document literal or in JSX. */\n finish?: PxFinishAction;\n}\n\n// `{ start?:'load'|'mouseOver'|'click'|'none', offScreen?:…, mouseOut?:…, visibilityThreshold?:number,\n// visibilityDebounce?:number, finish?:'hold'|'reset' }`\n// An absent field means its PX_TRIGGER_DEFAULTS entry — the table every player resolves through.\n// Keys are bare because the parent says `trigger`; the two that name a measured QUANTITY rather\n// than an occasion keep their subject word (plan: trigger-model.md D6).\n/** @public @advanced */\nexport const PxTriggerSchema = implementsInterface<_PxTrigger>()(px.object({\n start: px.enum([PxTriggerStart.load, PxTriggerStart.mouseOver, PxTriggerStart.click, PxTriggerStart.none] as const, PX_TRIGGER_DEFAULTS.start).optional(),\n offScreen: px.enum([PxOffScreenAction.pause, PxOffScreenAction.continue, PxOffScreenAction.reset] as const, PX_TRIGGER_DEFAULTS.offScreen).optional(),\n mouseOut: px.enum([PxMouseOutAction.continue, PxMouseOutAction.pause, PxMouseOutAction.reset, PxMouseOutAction.reverse] as const, PX_TRIGGER_DEFAULTS.mouseOut).optional(),\n // What happens after a NATURAL finish — `'hold'` (default: keep the end state per\n // `fill`) or `'reset'` (snap back to the start state). One of four occasion keys\n // (`start`, `offScreen`, `mouseOut`, `finish`), all named the same way.\n finish: px.enum([PxFinishAction.hold, PxFinishAction.reset] as const).optional(),\n visibilityThreshold: px.number().optional(),\n visibilityDebounce: px.number().optional(),\n}));\n\n/** Defines when and how an animation should be triggered. @public */\nexport type PxTrigger = PxInfer<typeof PxTriggerSchema>;\nconst _ck_PxTrigger: KeysMatch<PxTrigger, _PxTrigger> = true; // the key sets are identical\n\n\n// ============================================================================\n// DEFS\n// ============================================================================\n\n/**\n * A single character's embedded outline (glyph-mode text). Coordinates and\n * advance are in the owning {@link _PxGlyphFont}'s `unitsPerEm` units, so the\n * player can render text without the original font. See svga.text.design.md.\n */\nexport interface _PxGlyph {\n /** Advance width, in the font's `unitsPerEm`. */\n width: number;\n /** Outline path data, in the font's `unitsPerEm` (empty for whitespace). Named\n * `pathData`, never `d`: `d` is SVG's name for the node ATTRIBUTE only, and every\n * structure of ours spells the concept out (review §2.4). */\n pathData: string;\n}\n\nexport const PxGlyphSchema = implementsInterface<_PxGlyph>()(px.object({\n width: px.number(),\n pathData: px.string(),\n}));\n\n/** @public */\nexport type PxGlyph = PxInfer<typeof PxGlyphSchema>;\nconst _ck_PxGlyph: KeysMatch<PxGlyph, _PxGlyph> = true; // the key sets are identical\n\n/**\n * The used glyphs of one font, keyed by character. Referenced by a text's\n * `font-family` (the key in {@link _PxDefs.fonts}).\n */\nexport interface _PxGlyphFont {\n /** CSS family name, e.g. \"Roboto\". */\n fontFamily: string;\n /** Font-style notation, e.g. \"\" | \"italic\" (review §5.2 — `style` would collide with the node's CSS `style`). */\n fontStyle: string;\n /** Ascent, in `unitsPerEm` units (baseline placement). */\n ascent: number;\n /** Units per em the glyph `width`/`pathData` are expressed in, e.g. 1000. */\n unitsPerEm: number;\n /** Outlines of the used characters, keyed by the character itself. */\n glyphs: { [char: string]: PxGlyph; };\n}\n\nexport const PxGlyphFontSchema = implementsInterface<_PxGlyphFont>()(px.object({\n fontFamily: px.string(),\n fontStyle: px.string(),\n ascent: px.number(),\n unitsPerEm: px.number(),\n glyphs: px.record(PxGlyphSchema),\n}));\n\n/** @public */\nexport type PxGlyphFont = PxInfer<typeof PxGlyphFontSchema>;\nconst _ck_PxGlyphFont: KeysMatch<PxGlyphFont, _PxGlyphFont> = true; // the key sets are identical\n\n/**\n * Reusable definitions library for easings, animations and fonts.\n * Defined once here, referenced by name on elements (or, for animations, from bindings).\n * (`styles` — named style presets — was removed on 2026-09-13, review 2.13: nothing wrote it.)\n */\nexport interface _PxDefs {\n\n /** Named cubic-bezier easing functions */\n easings?: { [name: string]: [number, number, number, number]; };\n\n /** Named animation definitions that can be referenced by elements */\n animations?: { [name: string]: PxAnimationDefinition; };\n\n /** Embedded fonts — per-font glyph outlines, keyed by the text's `font-family`.\n * Lets glyph-mode `<text>` render without an external font. */\n fonts?: { [fontName: string]: PxGlyphFont; };\n}\n\n// `{ easings?:Record<name,[x1,y1,x2,y2]>, animations?:Record<name,AnimationDefinition>, fonts?:Record<fontName,PxGlyphFont> }`\n/** @public @advanced */\nexport const PxDefinitionsSchema = implementsInterface<_PxDefs>()(px.object({\n easings: px.record(px.tuple([px.number(), px.number(), px.number(), px.number()] as const)).optional(),\n animations: px.record(PxAnimationDefinitionSchema).optional(),\n fonts: px.record(PxGlyphFontSchema).optional(),\n}));\n\n/** Reusable definitions library for easings, animations and fonts. @public */\nexport type PxDefinitions = PxInfer<typeof PxDefinitionsSchema>;\nconst _ck_PxDefs: KeysMatch<PxDefinitions, _PxDefs> = true; // the key sets are identical\n\n\n// ============================================================================\n// SCROLL TIMELINE\n// ============================================================================\n\n/**\n * A point on a scroll timeline's range, anchoring where the animation's 0%/100% sit.\n *\n * For `kind: 'view'`, `phase` names WHICH slice of the subject's journey across the\n * scrollport the point is measured in (CSS \"timeline range name\"), and `fraction` is the\n * 0..1 position within that slice. For `kind: 'scroll'` there are no phases — `phase` is\n * ignored and `fraction` is a fraction of the scroller's total scroll range.\n *\n * Structured on purpose (never a CSS string like `\"cover 0%\"`): atomic values, mechanical\n * translation to CSS `animation-range` / WAAPI `rangeStart/rangeEnd` where needed.\n */\nexport interface _PxScrollRangePoint {\n /** `view` kind only: the journey phase this point is anchored in. Default `'cover'`. */\n phase?: PxScrollPhase;\n /** 0..1 within the phase (or of the total scroll range for `kind: 'scroll'`). */\n fraction?: number;\n}\n\n/** @public @advanced */\nexport const PxScrollRangePointSchema = implementsInterface<_PxScrollRangePoint>()(px.object({\n phase: px.enum([PxScrollPhase.cover, PxScrollPhase.contain, PxScrollPhase.entry,\n PxScrollPhase.exit, PxScrollPhase.entryCrossing, PxScrollPhase.exitCrossing] as const).optional(),\n fraction: px.number().optional(),\n}));\n/** @public */\nexport type PxScrollRangePoint = PxInfer<typeof PxScrollRangePointSchema>;\nconst _ck_PxScrollRangePoint: KeysMatch<PxScrollRangePoint, _PxScrollRangePoint> = true; // the key sets are identical\n\n/**\n * Scroll-timeline configuration — consulted only when `timelineSource: 'scroll'`.\n * All fields optional; the defaults give \"scrub the whole animation as the SVG crosses\n * the viewport\" (`view` / `block` / full `cover` range).\n */\nexport interface _PxScroll {\n /** What progress measures. `'view'` (default): the SVG's own journey across the\n * scrollport (enter → leave). `'scroll'`: the scroll container's offset ratio,\n * regardless of where the SVG sits. */\n kind?: 'view' | 'scroll';\n\n /** Scroll axis. `block`/`inline` are writing-mode relative (block = vertical in\n * horizontal writing); `x`/`y` are physical. Default `'block'`. */\n axis?: 'block' | 'inline' | 'x' | 'y';\n\n /** `kind: 'scroll'` only: which scroller. `'nearest'` (default) = nearest scrollable\n * ancestor of the SVG; `'root'` = the document. (`view` always tracks the nearest\n * scrollport.) */\n source?: 'nearest' | 'root';\n\n /**\n * `kind: 'view'` only — WHICH ELEMENT'S journey is measured. Unset (default) = the\n * animation's own `<svg>`. The same indirection CSS gives via `view-timeline-name` +\n * `timeline-scope`, GSAP via `trigger`, Framer via \"Section in view\".\n *\n * - `'parent'` — the nearest ancestor that actually scrolls past: any `sticky`/`fixed`\n * ancestors are skipped and their container is used instead. This is what makes a\n * PINNED section work (a stuck element's rect stops moving, so measuring the graphic\n * itself would freeze); its `contain` phase is exactly the pinned stretch.\n * - `'scroller'` — the scroll container itself.\n * - anything else — a CSS selector, resolved against the host document.\n *\n * An unresolvable selector warns and falls back to the `<svg>` (never a silent freeze).\n */\n subject?: string;\n\n /**\n * MILLISECONDS of catch-up lag — the same idea as GSAP's `scrub: <seconds>`, in the unit\n * the rest of this schema uses (`duration`, `delay`). Unset/0 = the playhead is locked to\n * the scrollbar; above 0 the progress eases toward the scroll position instead of snapping\n * to it, which reads far smoother under momentum scrolling and trackpads.\n * Custom driver only — a browser-native `ScrollTimeline` has no equivalent, so setting\n * this forces the player's own measurement (the browser timeline is skipped).\n */\n smoothing?: number;\n\n /**\n * Hold the canvas still on screen while scrolling scrubs it — GSAP's `pin: true`, done with\n * `position: sticky` (which keeps the element's space in normal flow, so unlike GSAP's\n * `position: fixed` no spacer padding has to be injected into the host's layout).\n *\n * The player owns the DOM inside its own container, so this needs NO host CSS. Pair it with\n * `subject: 'parent'` for the complete scrollytelling pattern.\n */\n pin?: boolean;\n\n /**\n * `pin` only: WHERE in the scrollport the canvas is held — the alignment the sticky\n * offset is computed from. `top` (default) holds it against the top edge; `center`\n * and `bottom` need the canvas's own height, so the player measures it and keeps the\n * offset in sync on resize. `pinOffset` is added on top of whichever alignment is chosen.\n */\n pinAlign?: 'top' | 'center' | 'bottom';\n\n /** `pin` only: offset from the alignment position (see `pinAlign`), in px. Default 0.\n * The runtime-view twin of the wire `timeline.pin.offset` (review §2.6). */\n pinOffset?: number;\n\n /**\n * `pin` only: how much scroll travel the pin should last, in VIEWPORT HEIGHTS — the player\n * injects a wrapper of that height around the canvas to create it. Omit to pin inside\n * whatever tall section the host page already provides.\n */\n pinDistance?: number;\n\n /** The timeline slice mapped onto animation progress 0..1.\n * Default `{ start: {phase:'cover', fraction:0}, end: {phase:'cover', fraction:1} }`. */\n range?: {\n start?: _PxScrollRangePoint;\n end?: _PxScrollRangePoint;\n };\n}\n\n/** The `range` sub-object — named so consumers can derive its keys. @public @advanced */\nexport const PxScrollRangeSchema = px.object({\n start: PxScrollRangePointSchema.optional(),\n end: PxScrollRangePointSchema.optional(),\n});\n\n/** @public @advanced */\nexport const PxScrollSchema = implementsInterface<_PxScroll>()(px.object({\n kind: px.enum([PxScrollKind.view, PxScrollKind.scroll] as const).optional(),\n axis: px.enum([PxScrollAxis.block, PxScrollAxis.inline, PxScrollAxis.x, PxScrollAxis.y] as const).optional(),\n source: px.enum([PxScrollSource.nearest, PxScrollSource.root] as const).optional(),\n // Free-form: the two keywords `parent`/`scroller` plus any CSS selector.\n subject: px.string().optional(),\n smoothing: px.number().optional(),\n pin: px.boolean().optional(),\n pinAlign: px.enum([PxPinAlign.top, PxPinAlign.center, PxPinAlign.bottom] as const).optional(),\n pinOffset: px.number().optional(),\n pinDistance: px.number().optional(),\n range: PxScrollRangeSchema.optional(),\n}));\n/** @public */\nexport type PxScroll = PxInfer<typeof PxScrollSchema>;\nconst _ck_PxScroll: KeysMatch<PxScroll, _PxScroll> = true; // the key sets are identical\n\n\n// ============================================================================\n// TIMELINE — what advances the animation's progress (review §2.1)\n// ============================================================================\n//\n// `animator.timeline` is a discriminated object: `type?: 'time' | 'scroll' | 'view'`,\n// deliberately mirroring WAAPI's three timeline classes (DocumentTimeline /\n// ScrollTimeline / ViewTimeline) and CSS `animation-timeline: auto | scroll() | view()`.\n// Each mode carries ONLY its own parameters, so a key that is dead in the other mode is\n// structurally unwritable — the old flat spelling (`timelineSource` + sibling `scroll` +\n// clock knobs loose at the animator root) required design rules (D3/D4) to keep dead keys\n// out; readers accept BOTH spellings (see `flattenAnimatorTimeline`), writers emit only\n// this one.\n\n/** Pin parameters as one object — presence enables pinning (review §2.2; the flat runtime-view\n * spelling is `scroll.pin/pinAlign/pinOffset/pinDistance`). */\nexport interface _PxTimelinePin {\n /** Where the pinned canvas sits in the viewport. Default `'top'`. */\n align?: 'top' | 'center' | 'bottom';\n /** Offset from the alignment position, in px. Default 0. Named `offset`, not `top`: it is a\n * delta from whichever edge `align` picked, so `{ align: 'bottom', top: 20 }` read as a\n * contradiction (review §2.6). */\n offset?: number;\n /** How much scroll travel the pin lasts, in VIEWPORT HEIGHTS. Omit to pin inside\n * whatever tall section the host page already provides. */\n distance?: number;\n}\n\n/** @public @advanced */\nexport const PxTimelinePinSchema = implementsInterface<_PxTimelinePin>()(px.object({\n align: px.enum([PxPinAlign.top, PxPinAlign.center, PxPinAlign.bottom] as const).optional(),\n offset: px.number().optional(),\n distance: px.number().optional(),\n}));\n/** @public */\nexport type PxTimelinePin = PxInfer<typeof PxTimelinePinSchema>;\nconst _ck_PxTimelinePin: KeysMatch<PxTimelinePin, _PxTimelinePin> = true; // the key sets are identical\n\n// Time-driven — wall-clock playback: something STARTS it (trigger) and it has the\n// WAAPI playback dynamics. `type` is OPTIONAL: an absent `type` (or an absent\n// `timeline` altogether) means this one — the common case declares nothing.\n// `resetOnFinish` has no slot here: its successor is `trigger.finish: 'reset'`.\n/** `timeline.engine` — HOW the animated attributes get updated (every timeline type;\n * default `auto`). Not `mode`: an implementation preference, not a behavior switch. */\nconst PxTimelineEngineSchema = px.enum([PxTimelineEngineSetting.auto, PxTimelineEngineSetting.native, PxTimelineEngineSetting.js] as const).optional();\n\n/**\n * The time-driven timeline. Declared as an interface so a rename inside the schema below is a\n * COMPILE error rather than a silent wire-format change — `flattenAnimatorTimeline` reads the\n * timeline through `any`, so without this lock nothing else in the repo would notice.\n */\nexport interface _PxTimeTimeline {\n /** Optional: an absent `type` (or an absent `timeline`) already means this member. */\n type?: 'time';\n /** How the animated attributes get updated. Default `auto`. */\n engine?: PxTimelineEngineSetting;\n /** Target fps for the player's frame loop — a parameter of the `engine` chosen above, so it\n * sits beside it. Uncapped when absent; ignored by every engine except the frame loop. */\n frameRate?: number;\n /** §2.8: how long one pass takes, ms. */\n duration?: number;\n /** What starts it, and what happens when that condition ends. */\n trigger?: _PxTrigger;\n /** Wait before the first iteration, ms. Negative skips ahead. */\n delay?: number;\n /** Repeat count, or `'infinite'`. */\n iterations?: number | 'infinite';\n /** CSS `animation-fill-mode` — what shows outside the active time. NEVER spelled `fill`,\n * which is paint everywhere else in the format; the runtime view calls it `fill`. */\n fillMode?: 'forwards' | 'backwards' | 'both' | 'none';\n /** Forward, backward, or turning around each iteration. */\n direction?: 'normal' | 'reverse' | 'alternate' | 'alternate-reverse';\n}\n\nconst PxTimeTimelineSchema = implementsInterface<_PxTimeTimeline>()(px.object({\n type: px.literal('time').optional(),\n engine: PxTimelineEngineSchema,\n frameRate: px.number().optional(),\n // §2.8: duration is a property of the TIMELINE — how long one pass takes.\n duration: px.number().optional(),\n trigger: PxTriggerSchema.optional(),\n delay: px.number().optional(),\n iterations: px.union([px.number(), px.literal('infinite')]).optional(),\n // `fillMode` on the wire (CSS `animation-fill-mode`; the runtime view calls it `fill`)\n // — never `fill`, which is paint everywhere else in the format.\n fillMode: px.enum([PxFillMode.forwards, PxFillMode.backwards, PxFillMode.both, PxFillMode.none] as const).optional(),\n direction: px.enum([PxPlaybackDirection.normal, PxPlaybackDirection.reverse, PxPlaybackDirection.alternate, PxPlaybackDirection.alternateReverse] as const).optional(),\n}));\nconst _ck_PxTimeTimeline: KeysMatch<PxInfer<typeof PxTimeTimelineSchema>, _PxTimeTimeline> = true;\n\n// Scroll-driven modes — progress scrubbed from scroll position; nothing starts or\n// finishes it, so none of the clock knobs exist here. `'scroll'` tracks a scroller's\n// scroll offset, `'view'` tracks the subject's visibility through the viewport —\n// exactly WAAPI ScrollTimeline vs ViewTimeline (the old nested `scroll.kind` dissolved\n// into this discriminant). `mode` (shared by every timeline type) says who runs the\n// animation; `pin` is boolean-or-object (§2.2).\nconst scrollishTimelineShape = {\n // §2.8: duration is a property of the TIMELINE — under scrubbing it is the keyframe\n // span the scroll range maps onto.\n duration: px.number().optional(),\n // Finite repeat count IS meaningful when scrubbing — the scroll range maps onto\n // duration × iterations (rule D4; `'infinite'` cannot map to a range, so no literal here).\n iterations: px.number().optional(),\n engine: PxTimelineEngineSchema,\n frameRate: px.number().optional(),\n axis: px.enum([PxScrollAxis.block, PxScrollAxis.inline, PxScrollAxis.x, PxScrollAxis.y] as const).optional(),\n source: px.enum([PxScrollSource.nearest, PxScrollSource.root] as const).optional(),\n subject: px.string().optional(), // 'parent' | 'scroller' | any CSS selector\n smoothing: px.number().optional(), // ms\n pin: px.union([px.boolean(), PxTimelinePinSchema]).optional(),\n range: PxScrollRangeSchema.optional(),\n};\n/** The keys both scroll-driven members carry. Locked the same way as the time member. */\nexport interface _PxScrollishTimelineShape {\n /** §2.8: under scrubbing, the keyframe span the scroll range maps onto. */\n duration?: number;\n /** Finite only — `'infinite'` cannot map onto a range (rule D4). */\n iterations?: number;\n /** How the animated attributes get updated. Default `auto`. */\n engine?: PxTimelineEngineSetting;\n /** Target fps for the player's frame loop (shared with the time member). */\n frameRate?: number;\n /** `block`/`inline` are writing-mode relative; `x`/`y` are physical. Default `'block'`. */\n axis?: 'block' | 'inline' | 'x' | 'y';\n /** `'nearest'` (default) scrollable ancestor, or `'root'`, the document. */\n source?: 'nearest' | 'root';\n /** `'parent'` · `'scroller'` · any CSS selector. */\n subject?: string;\n /** How long the playhead takes to catch up with the scrollbar, ms. */\n smoothing?: number;\n /** `true` to pin with defaults, or the parameters (review §2.2). */\n pin?: boolean | _PxTimelinePin;\n /** The slice of the timeline mapped onto progress 0..1. */\n range?: { start?: _PxScrollRangePoint; end?: _PxScrollRangePoint };\n}\n\n/** `interface X extends Y { type: 'scroll' }` — spelled as an intersection so the key-set\n * check below compares exactly the discriminant plus the shared shape. */\nexport type _PxScrollTimeline = _PxScrollishTimelineShape & { type: 'scroll' };\nexport type _PxViewTimeline = _PxScrollishTimelineShape & { type: 'view' };\n\nconst PxScrollTimelineSchema = implementsInterface<_PxScrollTimeline>()(\n px.object({ type: px.literal('scroll'), ...scrollishTimelineShape }));\nconst PxViewTimelineSchema = implementsInterface<_PxViewTimeline>()(\n px.object({ type: px.literal('view'), ...scrollishTimelineShape }));\nconst _ck_PxScrollTimeline: KeysMatch<PxInfer<typeof PxScrollTimelineSchema>, _PxScrollTimeline> = true;\nconst _ck_PxViewTimeline: KeysMatch<PxInfer<typeof PxViewTimelineSchema>, _PxViewTimeline> = true;\n\n/** @public @advanced */\nexport const PxTimelineSchema = px.discriminatedUnion('type', [\n PxTimeTimelineSchema, // first = the member an absent `type` selects\n PxScrollTimelineSchema,\n PxViewTimelineSchema,\n]);\n/** @public */\nexport type PxTimeline = PxInfer<typeof PxTimelineSchema>;\n\n\n// ============================================================================\n// ANIMATOR CONFIG\n// ============================================================================\n\n/**\n * Global animation configuration that applies to all animations in the document.\n * Defines timing, playback behavior, and rendering strategy.\n */\nexport interface _PxAnimatorConfig {\n\n /** RUNTIME VIEW ONLY (not wire — the wire spells it `timeline.engine`, on every\n * timeline type; same word both sides). How the animated attributes get updated;\n * see {@link PxTimelineEngineSetting}. */\n engine?: PxTimelineEngineSetting;\n\n /** RUNTIME VIEW ONLY (not wire — §2.8: the wire spells it `timeline.duration`).\n * Total animation duration in milliseconds. */\n duration?: number;\n\n /** Delay before animation starts in milliseconds */\n delay?: number;\n\n /**\n * Number of times to repeat the entire document timeline. Use `\"infinite\"` for endless loop.\n *\n * This repeats **all properties across all elements** as a unit. It is independent of\n * per-property `loop` configuration: if a property uses `loop`, its keyframes are already\n * expanded to fill `duration` before `iterations` takes effect — the two do not interfere,\n * but they do compose (a looping property inside an infinitely iterating document loops\n * within each iteration).\n */\n iterations?: number | \"infinite\";\n\n /** After a natural finish, snap the document back to its start state (same\n * mechanics as the trigger `reset` out-action). Off by default — the animation\n * holds its end state per `fill`. */\n resetOnFinish?: boolean;\n\n /**\n * Defines which values are applied before/after the active animation period\n * (maps directly to the Web Animations API `fill` option).\n * Defaults to `'forwards'` when not set so that elements hold their final\n * state after the animation ends — consistent with Lottie and other animation\n * runtimes. Without this default, seeking to the last frame would cause\n * elements to revert to their pre-animation state.\n */\n fill?: PxFillMode;\n\n /** Direction of animation playback */\n direction?: PxPlaybackDirection;\n\n /** RUNTIME VIEW ONLY — the wire spells it `timeline.frameRate`. Target frame rate for the\n * player's frame loop; ignored by WAAPI, React Native and the pre-rendered CSS export. */\n frameRate?: number;\n\n /** Trigger configuration for when animation should start */\n trigger?: PxTrigger;\n\n /** Named easings, animations and embedded fonts — referenced by elements and bindings */\n definitions?: PxDefinitions;\n\n /**\n * The bind-by-id document (a pre-rendered SVG + JS export, no `children`): the elements\n * already exist as markup, so instead of carrying them again the document lists WHICH\n * element plays WHICH named animations — see {@link _PxBinding}. Written by the editor's\n * exporter only; a self-contained document keeps its keyframes on the nodes (`node.animate`).\n *\n * @example [{ target: \"#_px_3cnuvau3\", animateWith: [\"a0\"] }]\n */\n bindings?: Array<PxBinding>;\n\n /**\n * RUNTIME VIEW ONLY (not wire) — what ADVANCES the animation. `'time'` (default)\n * is the wall clock; `'scroll'` is scroll-linked playback (\"scrubbing\"), matching\n * CSS scroll-driven animations.\n *\n * The wire spells this as `timeline.type` ('time' — or absent — vs 'scroll'/'view');\n * `flattenAnimatorTimeline` folds it into this field for the engines.\n * Distinct from `trigger.start`, which says what STARTS the animation: one\n * names the beginning, this one names what moves the playhead afterwards.\n * Design: app `svgeditor/animation/scroll-timeline.design.md`.\n */\n timelineSource?: string;\n\n /** RUNTIME VIEW ONLY (not wire) — scroll-timeline parameters, only consulted when\n * `timelineSource: 'scroll'`. The wire spells them inside `timeline`. */\n scroll?: PxScroll;\n\n /** What advances the animation — THE wire spelling (review §2.1): a discriminated\n * `{ type?: 'time' | 'scroll' | 'view', … }` object mirroring WAAPI's timeline\n * classes. Carries the playback dynamics (`trigger`/`delay`/`iterations`/`fillMode`/\n * `direction` for time; scroll geometry for scroll/view); the flat fields above\n * are the internal runtime view `flattenAnimatorTimeline` produces from it. */\n timeline?: PxTimeline;\n\n /** Debug helper: exposes the animator instance as `window[debugGlobalName]`. */\n debugGlobalName?: string;\n\n /**\n * WIRE FORMAT VERSION, `\"a.b.c\"` — the layering, not a build number:\n * `a.b` the PLAYER schema. A reader at `a.b` reads any file at `a.[b' <= b]`.\n * `c` the EDITOR's extension on top of that player schema (`meta.*`). The player\n * ignores it entirely; it is scoped to `(a,b)` and restarts when `b` moves.\n *\n * A mismatch on its own means NOTHING and must never warn: a bump says the schema gained\n * something, not that this document uses it. The number is consulted only when a\n * conversion needs it, or when unknown content was actually met — where it turns\n * \"something is wrong\" into \"written for 1.5, this build reads 1.1; update the player\".\n *\n * ABSENT means unknown, never \"oldest\": no version is assumed and no migration is guessed.\n */\n version?: string;\n}\n\n// ============================================================================\n// BINDINGS — the bind-by-id document (a pre-rendered SVG + JS export)\n// ============================================================================\n\n/**\n * One binding: WHICH element (`target`, `#id`-spelled like every element reference) plays\n * WHICH named animations (`animateWith` — names into `definitions.animations`, applied in\n * order, always an array).\n *\n * `…With` is the format's naming convention for \"by name, from `definitions`\" (review 2.12):\n * a bare key holds the thing itself (`node.animate` holds keyframes); `<key>With` holds an\n * ARRAY of names of the same concept (`animateWith` → `definitions.animations`). Two kinds of\n * reference, two spellings — `source` / `target` point at ELEMENTS (`#id`), `…With` points at\n * DEFINITIONS. Nothing else uses the convention yet; it is written down here so the next\n * key that refers to a definition by name spells it the same way.\n */\nexport interface _PxBinding {\n /** The element to animate — `'#id'`. */\n target: string;\n /** Names into `definitions.animations`, applied in order. */\n animateWith: Array<string>;\n}\n\n/** @public @advanced */\nexport const PxBindingSchema = implementsInterface<_PxBinding>()(px.object({\n target: px.string(),\n animateWith: px.array(px.string()),\n}));\n\n/** @public */\nexport type PxBinding = PxInfer<typeof PxBindingSchema>;\nconst _ck_PxBinding: KeysMatch<PxBinding, _PxBinding> = true; // the key sets are identical\n\n/**\n * RUNTIME VIEW ONLY (not wire) — a binding once `normalizeBindings` has resolved it: the\n * bare DOM id and the merged, normalized animation. A self-contained document yields the same\n * shape from every animated node, so the engines never see which kind of document they play.\n * @internal\n */\nexport interface PxNormalizedBinding {\n id: string;\n animate: PxAnimationDefinition;\n}\n\n// The WIRE format (review §2.1): playback dynamics live only inside `timeline` —\n// the flat spelling (`trigger`/`delay`/`iterations`/`fill`/`direction`/`resetOnFinish`/\n// `timelineSource`/`scroll`) is NOT part of the format. It exists only as the internal\n// runtime VIEW (`_PxAnimatorConfig`) that `flattenAnimatorTimeline` produces for the engines.\n/** @public @advanced */\nexport const PxAnimatorConfigSchema = implementsInterface<_PxAnimatorConfig>()(px.object({\n // (`mode`, `duration` and `frameRate` live INSIDE `timeline` on the wire — §2.8; they exist\n // at this level only on the runtime view, like the rest of the playback dynamics.)\n // THE spelling of \"what advances progress\" — clock / scroll / view (review §2.1).\n timeline: PxTimelineSchema.optional(),\n definitions: PxDefinitionsSchema.optional(),\n bindings: px.array(PxBindingSchema).optional(),\n debugGlobalName: px.string().optional(),\n // Declared HERE because this is a closed object: an undeclared key would be stripped by\n // `sanitize` and flagged by strict validation on our own files.\n version: px.string().optional(),\n}));\n\n/**\n * Global animation configuration that applies to all animations in the document.\n * Defines timing, playback behavior, and rendering strategy.\n *\n * This is the runtime VIEW type: the wire carries the playback dynamics nested in\n * `timeline` (see `PxAnimatorConfigSchema`), and `flattenAnimatorTimeline` folds them\n * into the flat fields the engines consume — so the type is a superset of the wire.\n * @public\n */\nexport type PxAnimatorConfig = _PxAnimatorConfig;\n\n\n\n// ============================================================================\n// NODE\n// ============================================================================\n\n/**\n * Per-attribute value shape on the element body. A property key carries either:\n * - a primitive (string/number) — static SVG attribute\n * - a number array — static number-LIST attribute (`strokeDasharray: [16, 16]`);\n * the canonical static form for list attrs (the \"5,5\" string form is also\n * accepted). Raw arrays are unambiguous — only plain OBJECTS need the\n * `{value}` wrapper.\n * - a `{value: …}` object — structured static parametric source (record-shaped\n * static value, used by attributes whose static representation is itself a\n * record — notably `transform: {value: PxTransformParts}`)\n * - a `{keyframes}` object — inline property animation\n *\n * The unified rule (primitive/array | `{value}` | `{keyframes}`) applies across\n * the format. For most attributes the `{value}` form is rarely used on the body\n * (a primitive suffices for static); for `transform` it is the canonical\n * structured-static shape. See `PxTransformValueSchema`.\n */\n/**\n * Value of an open (undeclared) attribute key on a node — i.e. a BODY attribute.\n *\n * STATIC ONLY (R2/J3): a body attr never carries its own animation. Animation goes\n * in the parallel `animate` channel, keyed by attribute name — that is what keeps a\n * document degradable to valid static SVG. `PxPropertyAnimationSchema` used to be a\n * member here, which made an inline `\"opacity\": {keyframes:[…]}` schema-LEGAL even\n * though nothing writes it and nothing reads it; worse, being an all-optional object\n * schema it was ALSO what (accidentally) validated the transform parts record. The\n * parts record is now declared explicitly, so the two are no longer conflated.\n * @public @advanced\n */\nexport const PxAttrValueSchema = px.union([\n px.string(),\n px.number(),\n px.array(px.number()),\n // Structured static — `{value: …}` (read-accepted transitional spelling, S1).\n // `defined`, not `any`: the KEY's presence is what identifies this branch (V6).\n px.object({ value: px.defined() }),\n // Bare transform parts record — the canonical static `transform` on the wire (T2).\n PxTransformPartsSchema,\n]);\n\n/** Per-attribute value: primitive/number-array for static, `{value}` for structured\n * static, or a bare transform parts record. NEVER an animation — see `animate` (R2). * @public\n */\nexport type PxAttrValue = string | number | Array<number> | { value: any } | PxTransformParts;\n\n\n/**\n * Base interface for all SVG elements.\n * Named properties take precedence over the index signature when accessed.\n */\nexport interface _PxNode {\n\n /** SVG element type (e.g., \"circle\", \"rect\", \"path\", \"g\") */\n type: string;\n\n /** A REAL `type` attribute, for the elements that have one (`<feTurbulence\n * type=\"fractalNoise\">`, `<feFuncR type=\"table\">`) — `type` itself is the tag name.\n * The renderer turns this back into the attribute. */\n domType?: string;\n\n /** Text content of a `<text>` / `<tspan>` — the one text-content key (the DOM property name). */\n textContent?: string;\n\n /** Child elements (for container elements like <g>) */\n children?: PxNode[];\n\n /** Meta informaion about this element */\n meta?: any;\n\n /**\n * Player-effects bucket (transformation/repeater/maskedBy/strokeTrim/retime/ref)\n * emitted by the Editor's lightweight design format. `materializeNodeEffects`\n * materializes and removes these before any other normalization, so the\n * Player never observes a non-empty `effects` after entry-point processing.\n *\n * Typed against `PxEffectsSchema` (closed object — strict-mode validation\n * flags unknown effect keys). Adding a new effect requires extending the\n * `_PxEffects` interface AND the schema in lockstep.\n */\n effects?: PxEffects;\n\n /**\n * In-place property animations for this element. Same shape as the\n * `node.animate` values: string ref, array of refs, inline\n * definition (`{propName: PxPropertyAnimation}`), or mixed array.\n * The static initial value of an animated property is still carried as a\n * plain attribute on the body.\n */\n animate?: PxElementAnimation;\n\n /**\n * Inline style declarations for this element — camelCase CSS property names, exactly like\n * React's `style` prop (`whiteSpace`, `pointerEvents`, `mixBlendMode`) → value. The web\n * player writes them to `element.style`, React Native applies them as props; an explicit\n * attribute on the node wins. An OBJECT only: no CSS text, no preset names\n * (`definitions.styles` was removed — review 2.13).\n */\n style?: Record<string, string | number>;\n\n /**\n * Every other key is an SVG attribute under its camelCase DOM name — the spelling React\n * uses (`strokeWidth`, `fontSize`, `viewBox`, `clipPath`), which is what the editor writes.\n * The player renders it to the standard attribute (`stroke-width`) and accepts that kebab\n * spelling on the way in. A value is either a primitive (static) or a PxPropertyAnimation\n * (in-place animation `{ keyframes: [...] }`).\n */\n [camelCaseDomKey: string]: any;\n}\n\n// ============================================================================\n// PLAYER-EFFECTS BUCKET — INTERFACES + SCHEMAS (linked via `implementsInterface`)\n// ============================================================================\n//\n// Schemas for the `node.effects` payload emitted by the Editor's lightweight\n// design format. `materializeNodeEffects` (in `effects/PlayerEffectsUtil.ts`)\n// materializes and removes these before any other normalization, so the Player\n// never observes a non-empty `effects` after entry-point processing.\n//\n// Each effect is declared as a `_Px*` interface, paired with a `Px*Schema`\n// wrapped in `implementsInterface<_Px*>()(…)`. The runtime schema and the\n// compile-time interface drift together: a new field added to either without\n// matching the other is a TS error. `KeysMatch` asserts key-set equality so\n// renames are caught too. This is the same pattern used by `PxKeyframe`,\n// `PxLoop`, etc. earlier in this file.\n//\n// `effects/types.ts` re-exports these types so the applier internals\n// (`effects/*.ts`) can still `import from './types'` unchanged.\n\n/** Fixed-length 2-number tuple. `[x, y]` for positions, `[sx, sy]` for scale, …. @public */\nexport type PxVec2 = [number, number];\n\n/**\n * Animatable wire value — the ONE grammar for every animatable slot:\n *\n * T — raw static (non-object T)\n * { value: T } — structured static\n * PxPropertyAnimation — animated: `{value?, keyframes, loop?, autoOrient?}`\n *\n * The animated form IS `PxPropertyAnimation` — the exact object `node.animate`\n * channels use — so effect slots and node attributes share one schema, one\n * reader (`effects/transformParts.readAnimatable`) and one loop-materialization\n * path. `value` inside the animated form is the optional static baseline (see\n * `_PxPropertyAnimation.value`).\n *\n * Generic over the per-kf value type `T` for compile-time narrowing of the\n * static / `{value}` forms. The animated form uses the lib's non-generic\n * `PxKeyframe` (whose `value` is `any`) — kf values are read with care in the\n * applier (the visualModel walker / `interpParts` know per-property shapes).\n * @internal\n */\nexport type PxAnimatable<T> = T | { value: T } | _PxPropertyAnimation;\n\n// ORDER LAW (same medicine as PxKeyframeValueSchema): in every animatable union the\n// PropertyAnimation member comes BEFORE the bare `{value}` wrapper. Union.sanitize takes\n// the first member that validates, and default-mode object validation tolerates unknown\n// keys — wrapper-first would route `{value, keyframes}` to the wrapper and silently strip\n// the keyframes. A `{value}`-only static hitting PropertyAnimation first loses nothing\n// (it declares `value` too). Validity is order-independent; only repair cares.\n\n// PxAnimatable<number> — static number OR `{value}` static OR PxPropertyAnimation.\nconst PxAnimatableNumberSchema = px.union([\n px.number(),\n PxPropertyAnimationSchema,\n px.object({ value: px.number() }),\n]);\n\n// PxAnimatable<PxVec2> — static `[x,y]` OR `{value:[x,y]}` OR PxPropertyAnimation.\n// `as const` on the tuples is REQUIRED for TS to infer `[number, number]` (a\n// fixed-length tuple = `PxVec2`) instead of the looser `number[]`.\nconst PxAnimatableVec2Schema = px.union([\n px.tuple([px.number(), px.number()] as const),\n PxPropertyAnimationSchema,\n px.object({ value: px.tuple([px.number(), px.number()] as const) }),\n]);\n\n// PxAnimatable<string> — static `\"M…\"` OR `{value:\"M…\"}` OR PxPropertyAnimation.\nconst PxAnimatableStringSchema = px.union([\n px.string(),\n PxPropertyAnimationSchema,\n px.object({ value: px.string() }),\n]);\n\n// ── CHANNEL vs CONFIG (V2) — the split is declared in the SOURCE, twice over ──\n// An effect slot is one of exactly two kinds, and both declarations must agree:\n// channel (samplable per frame) → interface `PxAnimatable<T>` + a named\n// `PxAnimatable*Schema` in the schema\n// static config (read once) → the bare type + a bare `px.*()` slot\n// Never hand-inline the `[T, {value:T}, PxPropertyAnimation]` union at a slot:\n// the NAME is what makes the split machine-readable. Editor side mirrors this\n// with `isAnimatable: true` on the value's config.\n\n\n/** Per-part editor transform (`transformBy` effect). All parts optional and animatable. */\nexport interface _PxTransformByEffect {\n translate?: PxAnimatable<PxVec2>;\n rotate?: PxAnimatable<number>;\n scale?: PxAnimatable<PxVec2>;\n /** Skew (skewX) in degrees — a NUMBER (matches the editor's scalar skew part). */\n skew?: PxAnimatable<number>;\n origin?: PxAnimatable<PxVec2>;\n}\n/** @public @advanced */\nexport const PxTransformByEffectSchema = implementsInterface<_PxTransformByEffect>()(px.object({\n translate: PxAnimatableVec2Schema.optional(),\n rotate: PxAnimatableNumberSchema.optional(),\n scale: PxAnimatableVec2Schema.optional(),\n skew: PxAnimatableNumberSchema.optional(),\n origin: PxAnimatableVec2Schema.optional(),\n}));\n/** @public */\nexport type PxTransformByEffect = PxInfer<typeof PxTransformByEffectSchema>;\nconst _ck_PxTransformByEffect: KeysMatch<PxTransformByEffect, _PxTransformByEffect> = true;\n\n\n/** Per-copy repeater offsets. Each part is animatable; per-copy values scale\n * with the copy index `i` (translate/rotate/skew × i; scale per-axis `v^i`).\n * Static repeater values pass through as a structured `transform: {value:…}` on\n * the per-copy wrapper; animated values are emitted as `animate.transform.keyframes`\n * with each kf value scaled by `i`. See `effects/repeaterEffect.ts`.\n *\n * NAMING — why `repeater`, NOT `repeat` (SCHEMA-DESIGN R5 / issues N6): this\n * effect repeats in SPACE (N copies, each with a compounding per-copy delta), but\n * in an ANIMATION format a bare `repeat` reads as TIME — and this format has real\n * time-repetition concepts for it to be confused with: `animator.iterations`,\n * per-property `loop {segmentCount, alternate}`, and SVG/SMIL's own\n * `repeatCount`/`repeatDur`. The agent noun keeps it unambiguously spatial, and\n * matches the term the audience already knows (After Effects \"Repeater\",\n * Lottie shape item `rp`). Same principle as `maskedBy` over `mask`: prefer the\n * form that preserves the right MEANING over the grammatically uniform one. */\nexport interface _PxRepeaterEffect {\n copies?: number;\n translate?: PxAnimatable<PxVec2>;\n rotate?: PxAnimatable<number>;\n /** Per-copy skew (skewX) increment in degrees — copy `i` is skewed by `skew × i`. */\n skew?: PxAnimatable<number>;\n scale?: PxAnimatable<PxVec2>; // per-copy FACTOR (0.85 = 85% per copy), like every other scale\n origin?: PxAnimatable<PxVec2>;\n}\n/** @public @advanced */\nexport const PxRepeaterEffectSchema = implementsInterface<_PxRepeaterEffect>()(px.object({\n // STATIC config, not a channel (V2/SCHEMA-DESIGN R5): the copy COUNT is read\n // once at expansion time and never sampled — plain number, no `keyframes`.\n copies: px.number().optional(),\n translate: PxAnimatableVec2Schema.optional(),\n rotate: PxAnimatableNumberSchema.optional(),\n skew: PxAnimatableNumberSchema.optional(),\n scale: PxAnimatableVec2Schema.optional(),\n origin: PxAnimatableVec2Schema.optional(),\n}));\n/** @public */\nexport type PxRepeaterEffect = PxInfer<typeof PxRepeaterEffectSchema>;\nconst _ck_PxRepeaterEffect: KeysMatch<PxRepeaterEffect, _PxRepeaterEffect> = true;\n\n\n/** Mask source ref + standard `<mask>` attributes.\n * `source` is `#id` (canonical ref spelling, SCHEMA-DESIGN §4 E-5); bare `id` is legacy, read-only.\n * `start`/`size` are the `<mask>` viewport — its `x`/`y` and `width`/`height` in\n * `maskUnits` space. Absent = SVG's implicit mask region (−10% … 120% of the\n * bounding box), which is also the editor's default — so they only appear when a\n * document (typically an imported SVG) carries explicit mask bounds. */\nexport interface _PxMaskedByEffect {\n source?: string;\n maskType?: string;\n maskUnits?: string;\n maskContentUnits?: string;\n // Mask viewport in `maskUnits` space — the SVG `<mask>` attrs verbatim (B5).\n // NOT `start`/`size` pairs: those were the EDITOR's model FIELD names, never a\n // wire spelling — the editor has always written these four scalars, so the old\n // pair declaration meant the player silently dropped every non-default viewport.\n x?: number;\n y?: number;\n width?: number;\n height?: number;\n}\n/** @public @advanced */\nexport const PxMaskedByEffectSchema = implementsInterface<_PxMaskedByEffect>()(px.object({\n source: px.string().optional(),\n maskType: px.enum([PxMaskType.luminance, PxMaskType.alpha] as const).optional(),\n maskUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox] as const).optional(),\n maskContentUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox] as const).optional(),\n x: px.number().optional(),\n y: px.number().optional(),\n width: px.number().optional(),\n height: px.number().optional(),\n}));\n/** @public */\nexport type PxMaskedByEffect = PxInfer<typeof PxMaskedByEffectSchema>;\nconst _ck_PxMaskedByEffect: KeysMatch<PxMaskedByEffect, _PxMaskedByEffect> = true;\n\n\n/**\n * Clip-path effect — clips the host element to a vector path. `pathData` is a standard\n * animatable slot (same grammar as the body `d` ATTRIBUTE, which keeps SVG's own name):\n * static = plain SVG path-data string (one or more subpaths); animated = `{keyframes}`\n * whose values are `{pathData:\"M…\"}`.\n * At apply time an animated slot lands on the generated `<path>`'s `animate.d`, so the\n * player's frame loop rewrites the clip path's `d` attribute per frame. `clip-path`\n * is a live reference, so the browser re-clips each frame (unlike `<marker>` —\n * verified across SMIL/CSS/JS/WAAPI).\n *\n * At apply time the effect generates a `<clipPath><path d/></clipPath>` def and sets\n * `clip-path=\"url(#auto-id)\"` on the host (materializer pattern, like `maskedBy` /\n * gradient). See `effects/clipPathEffect.ts`.\n */\nexport interface _PxClipPathEffect {\n pathData?: PxAnimatable<string>;\n}\n/** @public @advanced */\nexport const PxClipPathEffectSchema = implementsInterface<_PxClipPathEffect>()(px.object({\n pathData: PxAnimatableStringSchema.optional(),\n}));\nexport type PxClipPathEffect = PxInfer<typeof PxClipPathEffectSchema>;\nconst _ck_PxClipPathEffect: KeysMatch<PxClipPathEffect, _PxClipPathEffect> = true;\n\n\n/**\n * Stroke-trim effect. `range[0..1]` is the visible fraction of the STROKE; `offset`\n * shifts the visible window along the path (also a fraction). Both are animatable.\n * `subPaths` says what that fraction is measured over: `separate` (default) trims\n * each sub-path against its own length; `combined` chains all descendant sub-path\n * lengths into one virtual path (\"Trim All As One\") so the window slides across\n * siblings — see `effects/strokeTrimEffect.ts`.\n *\n * NAME — renamed from `trimPath` (2026-08, hard rename, no legacy alias): this\n * trims the STROKE only. It emits `stroke-dasharray` / `stroke-dashoffset` (plus\n * `stroke-opacity` for the empty-range hide) and NEVER rewrites `d`, so the fill\n * is untouched. Lottie's same-named `ty:'tm'` is a path OPERATOR that rewrites\n * geometry (and therefore does change the fill) — the old name imported that\n * wrong mental model from the format most authors convert from.\n */\nexport interface _PxStrokeTrimEffect {\n offset?: PxAnimatable<number>;\n range?: PxAnimatable<PxVec2>;\n subPaths?: PxStrokeTrimSubPaths;\n}\n/** @public @advanced */\nexport const PxStrokeTrimEffectSchema = implementsInterface<_PxStrokeTrimEffect>()(px.object({\n offset: PxAnimatableNumberSchema.optional(),\n range: PxAnimatableVec2Schema.optional(),\n subPaths: px.enum([PxStrokeTrimSubPaths.separate, PxStrokeTrimSubPaths.combined] as const).optional(),\n}));\n/** @public */\nexport type PxStrokeTrimEffect = PxInfer<typeof PxStrokeTrimEffectSchema>;\nconst _ck_PxStrokeTrimEffect: KeysMatch<PxStrokeTrimEffect, _PxStrokeTrimEffect> = true;\n\n\n/** Ref-attr naming rule (see editor dev-docs/schema-design.md): `source` = ref to an EXTERNAL element\n * (clone/maskedBy/retime); `coreId` = a unit's own survivor; `partOf` = a derived node's host.\n *\n * `<use>` retime: pure timing — the source ref lives ONCE, on the parent `clone.source`\n * (review §4.3; retime's own duplicate `source` was removed outright — no consumer ever\n * read it: the materializer follows `href`). `start`/`timeCrop` in ms.\n * `timeCrop: [inMs, outMs]` is a VISIBILITY WINDOW on the document timeline — implemented\n * (2026-08) as an opacity gate on a player-side wrapper `<g>`, independent of the\n * `start`/`stretch` remap (see `effects/retimeEffect.ts`). */\nexport interface _PxRetimeEffect {\n start?: number;\n stretch?: number;\n timeCrop?: [number, number];\n}\n/** @public @advanced */\nexport const PxRetimeEffectSchema = implementsInterface<_PxRetimeEffect>()(px.object({\n start: px.number().optional(),\n stretch: px.number().optional(),\n timeCrop: px.tuple([px.number(), px.number()] as const).optional(),\n}));\n/** @public */\nexport type PxRetimeEffect = PxInfer<typeof PxRetimeEffectSchema>;\nconst _ck_PxRetimeEffect: KeysMatch<PxRetimeEffect, _PxRetimeEffect> = true;\n\n\n/**\n * `<use>` CLONE — merges the former `ref` + `retime` effects. A `<use>` is a clone\n * of something: `type`/`source` say WHAT it clones, `retime` says WHEN.\n * - `without: 'translate'` → content-ref: the source's own translate is left out (the\n * clone stays where the `<use>` put it, still rotates/scales with the source);\n * absent → direct / whole-element link (keeps translate). A future `'transform'`\n * value may leave out the whole transform.\n * - `source` = the source element ref, `#id` (canonical spelling, SCHEMA-DESIGN §4 E-5;\n * bare `id` is legacy, read-only). Lives once here; the player follows `href`.\n * - `retime` = optional time-shift (nested).\n * Omitted entirely when all-default (a bare `<use href>` carries no `clone` bucket).\n */\nexport interface _PxCloneEffect {\n without?: string;\n source?: string;\n retime?: _PxRetimeEffect;\n}\n/** @public @advanced */\nexport const PxCloneEffectSchema = implementsInterface<_PxCloneEffect>()(px.object({\n // Subtractive on purpose: the `<use>` can only point at one wrapper layer of the\n // source, so the choices form a ladder — 'translate' now, maybe 'transform' later.\n without: px.enum([PxCloneWithout.translate] as const).optional(),\n source: px.string().optional(),\n retime: PxRetimeEffectSchema.optional(),\n}));\n/** @public */\nexport type PxCloneEffect = PxInfer<typeof PxCloneEffectSchema>;\nconst _ck_PxCloneEffect: KeysMatch<PxCloneEffect, _PxCloneEffect> = true;\n\n\n/** A single color stop. `offset` is in `[0, 1]`; `color` is a CSS color\n * string (`#rrggbb`, `rgb(…)`, `rgba(…)`, or named). */\nexport interface _PxGradientStop {\n offset: number;\n color: string;\n}\n/** @public @advanced */\nexport const PxGradientStopSchema = implementsInterface<_PxGradientStop>()(px.object({\n offset: px.number(),\n color: px.string(),\n}));\n/** @public */\nexport type PxGradientStop = PxInfer<typeof PxGradientStopSchema>;\nconst _ck_PxGradientStop: KeysMatch<PxGradientStop, _PxGradientStop> = true;\n\n/** `PxAnimatable<Array<PxGradientStop>>` schema. Static is the bare array;\n * `{value: […]}` wraps the same; the animated form is `PxPropertyAnimation`\n * (one timeline whose each kf's `value` is the FULL stops array at that time).\n * LAW (SCHEMA-DESIGN R5, S9): stops are ONE animatable value — whole-array\n * snapshots on a single timeline, deliberately NO per-stop keyframes/easing\n * (gradient GEOMETRY animates per-slot with independent timelines). */\nconst PxAnimatableGradientStopsSchema = px.union([\n px.array(PxGradientStopSchema),\n px.object({ value: px.array(PxGradientStopSchema) }),\n PxPropertyAnimationSchema,\n]);\n\n/** Gradient paint effect — used by both `fillGradient` and `strokeGradient`\n * (same shape, different host attribute). Linear: `start`/`end`. Radial:\n * `center`/`radius`/`focal`. Stops animate as one timeline; geometry stays static. */\n// (The old `_PxGradientGeometryAnimation` per-scalar channel record —\n// `animate: {gradientX1: …}` — was REMOVED outright, read included: geometry\n// animates on the `start`/`end`/`center`/`radius`/`focal` slots. Backward compat dropped\n// deliberately. Frames-engine-only note still applies to animated geometry:\n// CSS/WAAPI cannot animate gradient endpoints; `mode: 'auto'` handles it.)\n\nexport interface _PxFillGradientEffect {\n type: PxGradientType; // 'linear' | 'radial'\n start?: PxAnimatable<PxVec2>; // linear start ([x1,y1]; review §4.2 — plain words, no abbreviations)\n end?: PxAnimatable<PxVec2>; // linear end ([x2,y2])\n center?: PxAnimatable<PxVec2>; // radial center ([cx,cy])\n radius?: PxAnimatable<number>; // radial radius (r)\n focal?: PxAnimatable<PxVec2>; // radial focal point ([fx,fy])\n stops?: PxAnimatable<Array<_PxGradientStop>>; // single animation timeline\n gradientUnits?: string; // PxUnits values\n spreadMethod?: string; // PxGradientSpreadMethod values\n gradientTransform?: string; // static only in v1\n}\n/** @public @advanced */\nexport const PxFillGradientEffectSchema = implementsInterface<_PxFillGradientEffect>()(px.object({\n // Contextual kind — the `type` convention, see `PxNodeBaseSchema.type`.\n type: px.enum([PxGradientType.linear, PxGradientType.radial] as const),\n start: PxAnimatableVec2Schema.optional(),\n end: PxAnimatableVec2Schema.optional(),\n center: PxAnimatableVec2Schema.optional(),\n radius: PxAnimatableNumberSchema.optional(),\n focal: PxAnimatableVec2Schema.optional(),\n stops: PxAnimatableGradientStopsSchema.optional(),\n gradientUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox] as const).optional(),\n spreadMethod: px.enum([PxGradientSpreadMethod.pad, PxGradientSpreadMethod.reflect, PxGradientSpreadMethod.repeat] as const).optional(),\n gradientTransform: px.string().optional(),\n}));\n/** @public */\nexport type PxFillGradientEffect = PxInfer<typeof PxFillGradientEffectSchema>;\nconst _ck_PxFillGradientEffect: KeysMatch<PxFillGradientEffect, _PxFillGradientEffect> = true;\n\n/** Stroke gradient is the same shape as fill gradient; the difference is\n * only which host attribute (`fill` vs `stroke`) the applier rewrites. */\nexport type _PxStrokeGradientEffect = _PxFillGradientEffect;\n/** @public @advanced */\nexport const PxStrokeGradientEffectSchema = PxFillGradientEffectSchema;\n/** @public */\nexport type PxStrokeGradientEffect = PxFillGradientEffect;\n\n/** Text-path effect on a `<text>` host. The path geometry is carried INLINE as\n * `pathData` (an SVG `d`; static for now, keyframed animation is a later step) — the\n * applier generates a `<path>` def from it and wraps the text's children in a native\n * `<textPath href=\"#…\">` at apply time. All SVG-native textPath attrs\n * (`lengthAdjust`, `method`, `spacing`, `startOffset`, `textLength`) ride on this\n * effect; `startOffset`/`textLength` accept the full `PxAnimatable<number>` shape.\n *\n * `pathOverflow` controls what happens to glyphs past the end of an OPEN path:\n * - `'extend'` (default): glyphs continue straight along the endpoint tangent\n * (Lottie / native-glyph behavior).\n * - `'clip'`: glyphs past the end disappear (native `<textPath>` behavior). */\nexport interface _PxTextPathEffect {\n pathData: string; // inline SVG `d`\n pathOverflow?: string; // 'clip' | 'extend' (default 'extend')\n lengthAdjust?: string; // 'spacing' | 'spacingAndGlyphs'\n method?: string; // 'align' | 'stretch'\n spacing?: string; // 'auto' | 'exact'\n startOffset?: PxAnimatable<number>;\n textLength?: PxAnimatable<number>;\n}\n/** @public @advanced */\nexport const PxTextPathEffectSchema = implementsInterface<_PxTextPathEffect>()(px.object({\n pathData: px.string(),\n pathOverflow: px.enum([PxPathOverflow.clip, PxPathOverflow.extend] as const).optional(),\n lengthAdjust: px.enum([PxLengthAdjust.spacing, PxLengthAdjust.spacingAndGlyphs] as const).optional(),\n method: px.enum([PxTextPathMethod.align, PxTextPathMethod.stretch] as const).optional(),\n spacing: px.enum([PxTextPathSpacing.auto, PxTextPathSpacing.exact] as const).optional(),\n startOffset: PxAnimatableNumberSchema.optional(),\n textLength: PxAnimatableNumberSchema.optional(),\n}));\n/** @public */\nexport type PxTextPathEffect = PxInfer<typeof PxTextPathEffectSchema>;\nconst _ck_PxTextPathEffect: KeysMatch<PxTextPathEffect, _PxTextPathEffect> = true;\n\n\n/**\n * `effects.text` — text-rendering options for a `<text>` node.\n *\n * `useGlyphs: true` tells the player to render this text from the embedded\n * per-glyph outlines in `definitions.fonts` (self-contained, no external\n * font) instead of a native `<text>`. See svga.text.design.md.\n */\nexport interface _PxTextEffect {\n useGlyphs?: boolean;\n}\n/** @public @advanced */\nexport const PxTextEffectSchema = implementsInterface<_PxTextEffect>()(px.object({\n useGlyphs: px.boolean().optional(),\n}));\nexport type PxTextEffect = PxInfer<typeof PxTextEffectSchema>;\nconst _ck_PxTextEffect: KeysMatch<PxTextEffect, _PxTextEffect> = true;\n\n\n/**\n * The full `node.effects` bucket. Closed — each known effect is declared\n * (strict-mode validation flags an unknown effect key as a wire-format drift).\n *\n * DESIGN LAW — attribute vs effect:\n * - An ATTRIBUTE is a value the browser consumes as-is on that element\n * (`fill=\"#f00\"`, `opacity`, `d`); animating it is \"this value over time\" —\n * one channel, zero structure. The test is STRUCTURE, not value-encoding\n * complexity: `transform` has a parts-record wire value but lands in one\n * attribute on the same element, so it stays an attribute.\n * - An EFFECT is anything whose realization requires structure — generating defs\n * (gradient, clipPath, maskedBy, textPath), wrapper nodes (transformation),\n * clones (repeater, clone), or geometry-derived multi-attr rewrites (strokeTrim).\n * - The same attribute name can sit on both sides, split by value: flat `fill`\n * is an attribute; gradient fill is an effect (no value of `fill` IS a\n * gradient — it needs a def + stops + a `url(#id)` indirection).\n * New features follow the same test: pattern fills / filters need defs → effects.\n *\n * COMPOSITION ORDER (SCHEMA-DESIGN §R5): one bag per element — JSON key order\n * carries NO meaning and is never read. The applier composes in one hard-coded\n * order, innermost → outermost:\n * glyphs/textPath → fill/strokeGradient → strokeTrim → repeater → maskedBy\n * → clipPath → clone-href+transformBy (retime = pass 2, time-remap only)\n * \"Other\" orders are expressed by STRUCTURE (nest elements), never by key order.\n * If authorable order is ever demanded: an explicit `effects.order: [names]`\n * extension — never key-order significance (JSON tooling silently reorders).\n */\nexport interface _PxEffects {\n transformBy?: _PxTransformByEffect;\n repeater?: _PxRepeaterEffect;\n maskedBy?: _PxMaskedByEffect;\n clipPath?: _PxClipPathEffect;\n strokeTrim?: _PxStrokeTrimEffect;\n clone?: _PxCloneEffect;\n fillGradient?: _PxFillGradientEffect;\n strokeGradient?: _PxStrokeGradientEffect;\n textPath?: _PxTextPathEffect;\n text?: _PxTextEffect;\n}\n/** @public @advanced */\nexport const PxEffectsSchema = implementsInterface<_PxEffects>()(px.object({\n transformBy: PxTransformByEffectSchema.optional(),\n repeater: PxRepeaterEffectSchema.optional(),\n maskedBy: PxMaskedByEffectSchema.optional(),\n clipPath: PxClipPathEffectSchema.optional(),\n strokeTrim: PxStrokeTrimEffectSchema.optional(),\n clone: PxCloneEffectSchema.optional(),\n fillGradient: PxFillGradientEffectSchema.optional(),\n strokeGradient: PxStrokeGradientEffectSchema.optional(),\n textPath: PxTextPathEffectSchema.optional(),\n text: PxTextEffectSchema.optional(),\n}));\n/** @public */\nexport type PxEffects = PxInfer<typeof PxEffectsSchema>;\nconst _ck_PxEffects: KeysMatch<PxEffects, _PxEffects> = true;\n\n/**\n * Walks `root` and validates every `node.effects` bucket against `PxEffectsSchema`.\n * Returns an array of human-readable warning strings (empty when all good).\n * Doesn't mutate the tree. Called by `createAnimatorImpl` before applying effects.\n *\n * Pass `strict: true` to also flag undeclared keys (useful in dev / tests).\n * @public @advanced\n */\nexport function validateNodeEffects(root: PxNode, options?: { strict?: boolean }): Array<string> {\n const warnings: Array<string> = [];\n // `path` is a human-readable breadcrumb prepended to each warning so the\n // reader can locate the offending node in the tree (e.g.\n // `root.children[0].children[2].effects.transformBy.translate: …`).\n const walk = (node: PxNode, path: string): void => {\n if (node && node.effects) {\n const ctx: PxValidationContext = { errors: [], warnings: [], strict: !!options?.strict };\n const ok = PxEffectsSchema.isValid(node.effects, ctx, [path + '.effects']);\n if (!ok) {\n for (const err of ctx.errors) warnings.push(err);\n }\n }\n if (node && Array.isArray(node.children)) {\n node.children.forEach((c, i) => walk(c, path + '.children[' + i + ']'));\n }\n };\n walk(root, 'root');\n return warnings;\n}\n\n/**\n * Cross-checks glyph-mode text against the embedded faces (review §2.5).\n *\n * A `definitions.fonts` key IS the face name, matched against the node's `font-family`\n * verbatim — so a name with no entry renders a row of □ placeholder boxes behind nothing but\n * a console warning. Nothing else catches that: the schema validates each side's SHAPE, never\n * that the two agree.\n *\n * Deliberately silent in two legal cases:\n * - the document embeds NO faces — browser-font text, a different situation entirely;\n * - a node carries no `font-family` — with exactly one face embedded the player resolves it\n * (`soleFont`), and with none there is nothing to name.\n *\n * The reverse (a face nothing references) is NOT reported: keeping the outlines of a text\n * whose glyph mode is currently off is legal and deliberate, so that toggling it back on\n * needs no font reload.\n */\nexport function validateGlyphFontRefs(root: PxNode, fonts: { [face: string]: unknown; } | undefined): Array<string> {\n if (!fonts || !Object.keys(fonts).length) return [];\n\n const problems: Array<string> = [];\n const walk = (node: PxNode, path: string, inherited: string | undefined, inGlyphText: boolean): void => {\n if (!node) return;\n // `font-family` inherits down the text tree, exactly as the renderer resolves it.\n const own = typeof node.fontFamily === 'string' ? node.fontFamily : undefined;\n const family = own ?? inherited;\n const isGlyphText = inGlyphText || (node.type === 'text' && !!node.effects?.text?.useGlyphs);\n\n // Report at the node that DECLARES the family — one problem per mistake, not one per\n // descendant that merely inherits it.\n if (isGlyphText && own && !Object.prototype.hasOwnProperty.call(fonts, own)) {\n const problem = path + ': glyph-mode text uses font-family \"' + own\n + '\", which has no entry in animator.definitions.fonts';\n if (!problems.includes(problem)) problems.push(problem);\n }\n if (Array.isArray(node.children)) {\n node.children.forEach((c, i) => walk(c, path + '.children[' + i + ']', family, isGlyphText));\n }\n };\n walk(root, 'root', undefined, false);\n return problems;\n}\n\n/**\n * Cross-checks named easing references against `definitions.easings` (review §2.9).\n *\n * A keyframe's `easing` is either a cubic-bezier array or the NAME of an entry in\n * `definitions.easings` — CSS keywords are deliberately not built in (player weight), so\n * `easing: \"ease-in-out\"` validates as a string, resolves to nothing, and plays LINEAR behind\n * one `console.warn`. That makes it the likeliest silent mistake in a generated document, and\n * the schema cannot catch it: it checks the shape of each side, never that the two agree.\n *\n * Only the wire spelling `easing` is read. The runtime view's `e` is an already-RESOLVED curve,\n * never a name, so it has nothing to cross-check.\n */\nexport function validateEasingRefs(root: PxNode, easings: { [name: string]: unknown; } | undefined): Array<string> {\n const problems: Array<string> = [];\n const walk = (node: unknown, path: string): void => {\n if (Array.isArray(node)) {\n node.forEach((item, i) => walk(item, path + '[' + i + ']'));\n return;\n }\n if (!node || typeof node !== 'object') return;\n\n const easing = (node as { easing?: unknown }).easing;\n if (typeof easing === 'string' && !(easings && Object.prototype.hasOwnProperty.call(easings, easing))) {\n const problem = path + '.easing: \"' + easing\n + '\" names no entry in animator.definitions.easings — it will play linear';\n if (!problems.includes(problem)) problems.push(problem);\n }\n for (const [key, value] of Object.entries(node as Record<string, unknown>)) {\n if (key === 'easing') continue; // already handled; a string has nothing to walk\n if (value && typeof value === 'object') walk(value, path + '.' + key);\n }\n };\n walk(root, 'root');\n return problems;\n}\n\n/**\n * Checks the `animator.version` stamp parses (review §2.10).\n *\n * The slot is `px.string()`, so `\"v1\"` validates and is then read as UNSTAMPED — the document\n * silently loses the one diagnostic that says which schema wrote it. Parsing is delegated to\n * {@link parseWireVersion} so this can never disagree with the reader.\n *\n * An ABSENT stamp is legal and silent: only a present-but-unparseable one is reported.\n */\nexport function validateVersionStamp(doc: PxAnimatedSvgDocument): Array<string> {\n const version = getAnimatorConfig(doc)?.version;\n if (version === undefined) return [];\n if (parseWireVersion(version) !== undefined) return [];\n return ['root.animator.version: ' + JSON.stringify(version)\n + ' is not a version stamp (\"a.b\" or \"a.b.c\") — it reads as unstamped'];\n}\n\n/**\n * Validates a WHOLE document against the wire schema — strictly, so undeclared keys are\n * reported too — plus every node's `effects` bucket and the glyph-font references. Returns\n * human-readable problems (`path: what is wrong`), empty when the document is sound; never\n * throws. The player itself only warns and skips what it cannot read; this is the one call\n * for tooling, CI and agents that want a yes/no answer before shipping a document.\n * @public\n */\nexport function validateDocument(doc: unknown, options?: { strict?: boolean }): Array<string> {\n // Strict (the default) rejects keys the schema does not declare — the right answer for a\n // document you are about to ship. `strict: false` tolerates them, which is what a READER\n // wants: an unknown key usually means a newer writer — worth a warning, never a refusal.\n const strict = options?.strict !== false;\n const ctx: PxValidationContext = { errors: [], warnings: [], strict };\n const problems: Array<string> = PxAnimatedSvgDocumentSchema.isValid(doc, ctx, ['root']) ? [] : [...ctx.errors];\n if (doc && typeof doc === 'object') {\n for (const w of validateNodeEffects(doc as PxNode, { strict })) {\n if (!problems.includes(w)) problems.push(w);\n }\n const defs = getAnimatorConfig(doc as PxAnimatedSvgDocument)?.definitions;\n for (const w of validateGlyphFontRefs(doc as PxNode, defs?.fonts)) {\n if (!problems.includes(w)) problems.push(w);\n }\n for (const w of validateEasingRefs(doc as PxNode, defs?.easings)) {\n if (!problems.includes(w)) problems.push(w);\n }\n for (const w of validateVersionStamp(doc as PxAnimatedSvgDocument)) {\n if (!problems.includes(w)) problems.push(w);\n }\n }\n return problems;\n}\n\n\n// ============================================================================\n// NODE\n// ============================================================================\n\n/**\n * Base shape for all SVG element nodes.\n * Open object: validated known keys + arbitrary SVG attributes whose values are\n * either primitives (static) or PxPropertyAnimation objects (in-place animation).\n * Non-recursive — excludes `children` (circular reference). Used for type extraction via PxInfer.\n *\n * `{ type:string, style?:…, [key:string]: string|number|PxPropertyAnimation }`\n * @public @advanced\n */\nexport const PxNodeBaseSchema = px.openObject({\n // CONVENTION (SCHEMA-DESIGN R1 / issues N4): `type` is the ONE word for \"what\n // kind of thing is this\", discriminated by its CARRIER — here the node TAG\n // (`rect`, `text`), and inside a sub-object that object's kind (`fillGradient.type`,\n // `fillGradient.type`, editor `preset.type`). Each sits in its own object, so\n // the carrier disambiguates completely; synonyms (`cloneKind`, `presetShape`)\n // would add words that all mean \"type\" and still need the carrier to read.\n // Guarding a `type` SLOT against a wrong VALUE is the job of strict enums\n // (issues V3), never of distinct key names.\n type: px.string(),\n // The escape hatch for elements that carry a REAL `type` attribute — `<feTurbulence\n // type=\"fractalNoise\">`, `<feFuncR type=\"table\">`, `<feColorMatrix type=\"saturate\">`.\n // `type` is taken by the tag name, so the attribute travels here and the renderer puts\n // it back (`PxAnimatorDOM.renderNode`, `PxRnRender`). Declared here — not merely\n // documented — because a wire key that is not in a schema is invisible to the\n // minifier's reserve list and gets renamed (dev-docs/plans/minification-boundary.md §1.1).\n domType: px.string().optional(),\n // Text content of a `<text>` / `<tspan>`. Declared, so a non-string value is a schema error\n // and the minifier reserves the key; `text` is NOT an alias for it and is not read anywhere.\n textContent: px.string().optional(),\n id: px.string().optional(),\n meta: px.any().optional(),\n // Player-effects bucket emitted by the Editor's lightweight design format.\n // Consumed and removed by `materializeNodeEffects` before any other normalization\n // (see `createAnimatorImpl`), so downstream code never sees it.\n effects: PxEffectsSchema.optional(),\n // `PxElementAnimation` (not just `PxAnimationDefinition`) — accepts\n // string ref / array of refs / inline definition / mixed array; mirrors\n // `node.animate` values and what `processNode` resolves at runtime.\n animate: PxElementAnimationSchema.optional(),\n style: px.record(px.union([px.string(), px.number()])).optional(),\n}, PxAttrValueSchema);\n\n// `let` so the lazy closure can capture the variable reference after assignment.\n// By the time the lazy resolves (first isValid/sanitize call), PxNodeSchema is assigned.\n// `PxNodeBaseSchema & { children?:PxNode[] }`\n/** @public @advanced */\nlet PxNodeSchema: PxSchema<any> = px.openObject({\n ...PxNodeBaseSchema._shape,\n children: px.lazy(() => px.array(PxNodeSchema), []).optional(),\n}, PxAttrValueSchema);\nexport { PxNodeSchema };\n\n/**\n * Base interface for all SVG elements.\n * Extends schema-derived typed fields; adds recursive children and the open\n * index signature for arbitrary SVG attributes under their camelCase DOM names\n * (cx, cy, r, fill, strokeWidth, …) — see `_PxNodeBase`.\n * Named properties take precedence over the index signature when accessed.\n * @public\n */\nexport interface PxNode extends PxInfer<typeof PxNodeBaseSchema> {\n children?: PxNode[];\n [camelCaseDomKey: string]: any;\n}\n\n\n// ============================================================================\n// SVG NODE (ROOT)\n// ============================================================================\n\n/**\n * Root SVG element containing the entire animated graphic.\n * Extends PxNode with SVG-specific properties and global configuration.\n */\nexport interface _PxSvgNode extends PxNode {\n\n /** SVG viewport width. `number` OR an SVG length string (`\"100%\"`, `\"12em\"`) —\n * percentages are legal SVG and appear in real documents. */\n width?: number | string;\n\n /** SVG viewport height — `number` or SVG length string, see `width`. */\n height?: number | string;\n\n /** FIXME - do we need it? SVG viewBox attribute defining coordinate system */\n viewBox?: string;\n\n /** Global animation configuration */\n animator?: PxAnimatorConfig;\n}\n\n/**\n * Extra fields present on the root SVG node, on top of PxNode.\n * Used for type extraction via PxInfer.\n *\n * `{ width?:number, height?:number, viewBox?:string, animator?:AnimatorConfig }`\n * @public @advanced\n */\nexport const PxSvgNodeRootSchema = px.object({\n // `\"100%\"` and other SVG length strings are legal here — a number-only slot rejected\n // real documents (e.g. apple-store-look-14-main.json) at the root <svg>.\n width: px.union([px.number(), px.string()]).optional(),\n height: px.union([px.number(), px.string()]).optional(),\n viewBox: px.string().optional(),\n animator: PxAnimatorConfigSchema.optional(),\n});\n\n/**\n * Root SVG element containing the entire animated graphic.\n * Extends PxNode (inheriting the open index signature) plus schema-derived\n * SVG-root fields.\n * @public\n */\nexport interface PxSvgNode extends PxNode, Omit<PxInfer<typeof PxSvgNodeRootSchema>, 'animator'> {\n /** The RUNTIME-VIEW type, not the wire shape: in-memory documents may carry the\n * flat playback fields (`flattenAnimatorTimeline` output, prop overrides in the\n * RN/React wrappers), while `PxAnimatorConfigSchema` validates only the nested\n * `timeline` spelling on the wire (review §2.1). */\n animator?: PxAnimatorConfig;\n}\n\n\n// ============================================================================\n// DOCUMENT\n// ============================================================================\n\n/**\n * Root SVG document schema. Enforces `type === 'svg'` to distinguish from child nodes.\n * This is the root type for the entire file format.\n *\n * `{ type:'svg', style?:…, width?:number, height?:number,\n * viewBox?:string, animator?:AnimatorConfig, children?:PxNode[],\n * [svgAttr]: string|number|PxPropertyAnimation }`\n * @public @advanced\n */\nexport const PxAnimatedSvgDocumentSchema = px.openObject({\n ...PxNodeBaseSchema._shape,\n ...PxSvgNodeRootSchema._shape,\n type: px.literal('svg'), // override string → literal to require 'svg'\n children: px.array(PxNodeSchema).optional()\n}, PxAttrValueSchema);\n\n/**\n * The complete animated SVG document.\n * This is the root type for the entire file format.\n * @public\n */\nexport interface PxAnimatedSvgDocument extends PxSvgNode {\n}\n\n\n// ============================================================================\n// API INTERFACES\n// ============================================================================\n\n// -- Callbacks: one chain, three levels (API review §9, §5; dev-docs/reviews/api-surface-review.md §26.1) ----------------------------\n//\n// PxDiagnosticsConfig onWarn / onError / muteWarn / muteError — what `createDiagnostics` reads\n// PxEngineCallbacks + onPlay / onPause / onCancel / onFinish / onRemove — what an ENGINE takes\n// PxAnimatorCallbacks + onStop — what every PUBLIC surface takes\n//\n// Each level extends the one above, so a field is spelled once and the surfaces cannot drift.\n// The diagnostics rule (`onError` = this instance will not play; `onWarn` = it plays, but\n// something was ignored, degraded or misspelled) is written on `PxDiagnosticsConfig`.\n\n/**\n * The callbacks an ENGINE takes — the frame loop, WAAPI, React Native's sampler: the playback\n * lifecycle plus the diagnostics channel. Public surfaces take `PxAnimatorCallbacks`.\n * @public\n */\nexport interface PxEngineCallbacks extends PxDiagnosticsConfig {\n\n /** Callback executed when the animation starts or resumes. */\n onPlay?: () => void;\n\n /** Callback executed when the animation is paused. */\n onPause?: () => void;\n\n /** Callback executed when the animation is canceled. */\n onCancel?: () => void;\n\n /**\n * Callback executed when the animation reaches its end — it played every iteration, or\n * `finish()` was called. Not fired when playback is stopped early (pause / cancel / remove).\n */\n onFinish?: () => void;\n\n /** Callback executed when the animation is removed. */\n onRemove?: () => void;\n}\n\n/**\n * What every PUBLIC surface takes, inline and under the same names — `createAnimator({ onFinish })`,\n * `loadTagAnimators`, the pre-rendered entries, and `<PixodeskSvgAnimator onFinish />` on React,\n * Vue and React Native (review §9, §24): the engine's callbacks plus `onStop`, which fires after\n * any of `onPause` / `onCancel` / `onFinish` / `onRemove` — for callers who only care that\n * playback is no longer running, whatever the reason.\n *\n * ONE definition: the components derive their props from it instead of each spelling the same\n * names, which is how their comments had already started to drift.\n * @public\n */\nexport interface PxAnimatorCallbacks extends PxEngineCallbacks {\n onStop?: () => void;\n}\n\n\nexport type PxPoint2D = Array<number>;\n\n\n// ============================================================================\n// BEZIER PATH\n// ============================================================================\n\n/** Represents a vector path for SVG shape animations. */\nexport interface _PxBezierPath {\n\n /** An array of vertex points [[x, y], ...]. */\n v: Array<PxPoint2D>;\n\n /** An array of 'in' tangent handles for each vertex [[x, y], ...]. */\n i?: Array<PxPoint2D>;\n\n /** An array of 'out' tangent handles for each vertex [[x, y], ...]. */\n o?: Array<PxPoint2D>;\n\n /** A boolean indicating if the path is closed. */\n c?: boolean;\n}\n\n// `{ v:number[][], i?:number[][], o?:number[][], c?:boolean }`\n/** @public @advanced */\nexport const PxBezierPathSchema = implementsInterface<_PxBezierPath>()(px.object({\n v: px.array(px.array(px.number())),\n i: px.array(px.array(px.number())).optional(),\n o: px.array(px.array(px.number())).optional(),\n c: px.boolean().optional(),\n}));\n\n/** Represents a vector path for SVG shape animations. @public */\nexport type PxBezierPath = PxInfer<typeof PxBezierPathSchema>;\nconst _ck_PxBezierPath: KeysMatch<PxBezierPath, _PxBezierPath> = true; // the key sets are identical\n\n\n// ============================================================================\n// ANIMATOR API\n// ============================================================================\n\n/**\n * Basic animation controls common to all animator types.\n *\n * Generic over the platform's root-element type (`TRoot`) so this package stays\n * platform-neutral: the web player specializes it to the DOM `Element`, a\n * React Native player to its own view handle. Defaults to `unknown`.\n * @public\n */\nexport interface PxPlaybackApi<TRoot = unknown> {\n\n isReady(): boolean;\n\n /** Returns the root element for the animation (platform-specific type). */\n getRootElement(): TRoot | null;\n\n /** Returns true if the animation is currently running. */\n isPlaying(): boolean;\n\n /** Starts or resumes the animation. */\n play(): void;\n\n /** Pauses the animation at its current state. */\n pause(): void;\n\n /** Stops the animation and resets it to its initial state. */\n cancel(): void;\n\n}\n\n/**\n * The full programmatic control interface for an animation.\n *\n * ### The time contract (API review §3)\n *\n * Every engine — the browser's WAAPI, the frame loop, React Native — answers these the same way:\n *\n * - **Time is ms from the start of the WHOLE run**, iterations included; never ms within the\n * current iteration. A time slider therefore reads the same on every player instead of\n * jumping back each time the animation repeats.\n * - **A seek clamps to `[0, duration × iterations]`**, with no upper bound when `iterations`\n * is `'infinite'`.\n * - **A rate of 0 is rejected** with a warning, everywhere. Use `pause()`.\n *\n * The maths behind it lives in `playback/PxPlaybackTime.ts`, so there is one implementation\n * rather than one per engine.\n * @public\n */\nexport interface PxAnimatorApi<TRoot = unknown> extends PxPlaybackApi<TRoot> {\n\n /** Jumps to the end of the animation and holds the final state. */\n finish(): void;\n\n /**\n * Changes the speed of the animation. 1 is normal, 2 is double, -1 is reverse.\n * A rate of 0 — or a non-finite one — is rejected with a warning; use `pause()`.\n */\n setPlaybackRate(rate: number): void;\n\n /** Current playback time, ms from the start of the whole run. `null` before ready. */\n getCurrentTime(): number | null;\n\n /** Seeks, ms from the start of the whole run; clamped to `[0, duration × iterations]`. */\n setCurrentTime(time: number): void;\n\n /**\n * Current position as 0–1 of the whole run. `null` before ready.\n *\n * The span is `duration × iterations`, or ONE iteration when `iterations` is `'infinite'`\n * (where the value wraps) — the same rule the components' `progress` prop already uses.\n */\n getCurrentProgress(): number | null;\n\n /** Seeks to 0–1 of the whole run, clamped to `[0, 1]`. The twin of `getCurrentProgress`. */\n setCurrentProgress(progress: number): void;\n\n /** Stops the animation and cleans up all associated resources. */\n destroy(): void;\n}\n\n/**\n * The imperative handle a framework component exposes through its ref: the player API minus\n * what the component itself owns — `isReady` (the document is inline, so it is always ready),\n * `getRootElement` (the framework renders it) and `destroy` (unmounting does it).\n *\n * ONE definition (review §9). `ReactAnimatorApi`, `VueAnimatorApi` and `RnAnimatorApi` are\n * aliases of this, so the three can no longer drift — they had: React Native's\n * `setPlaybackRate` comment had already lost \"negative plays backwards\".\n * @public\n */\nexport type PxAnimatorHandle = Omit<PxAnimatorApi, 'isReady' | 'getRootElement' | 'destroy'>;\n\n\n// ============================================================================\n// DEEP VALIDATION\n// ============================================================================\n\n/** @public @advanced */\nexport interface PxValidationResult {\n valid: boolean;\n errors: Array<string>;\n}\n\n/**\n * The pass/fail form of {@link validateDocument}, NON-strict — unknown keys are tolerated —\n * for readers that want a flag plus messages rather than a list.\n *\n * One implementation (review §10). This used to run the schema on its own and answer\n * `'Document failed schema validation'` without ever saying what failed; now every problem\n * `validateDocument` can name comes back with its path. Non-strict on purpose: the editor calls\n * this on OPEN, where a key from a newer version is worth a warning, never a refusal.\n * @public @advanced\n */\nexport function isValidPxDocument(doc: unknown): PxValidationResult {\n const errors = validateDocument(doc, { strict: false });\n return { valid: errors.length === 0, errors };\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport type { PxAnimatedSvgDocument } from '../format/PxAnimatorTypes';\n\n\n/**\n * Generates a unique ID with a random suffix.\n * Format: _px_{random base36 string}\n */\nlet _idCounter = 0;\n/** @internal */\nexport function generateUniqueId(): string {\n const timestamp = Date.now().toString(36);\n const counter = (++_idCounter).toString(36);\n const random = Math.random().toString(36).substring(2, 6);\n return '_px_' + timestamp + counter + random;\n}\n\n/**\n * Deep clones a JSON-like value (objects, arrays, primitives).\n * Does not handle special types like Date, Map, Set, functions, etc.\n * @internal\n */\nexport function deepClone<T>(value: T): T {\n if (value === null || typeof value !== 'object') return value;\n if (Array.isArray(value)) return value.map(item => deepClone(item)) as T;\n\n const obj = value as Record<string, unknown>;\n\n const cloned: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n cloned[key] = deepClone(obj[key]);\n }\n return cloned as T;\n}\n\n/**\n * Regenerates all IDs in the document and updates references.\n *\n * This function:\n * 1. Deep clones the document to avoid mutating the original\n * 2. Traverses all nodes and regenerates IDs, keeping a mapping of old → new\n * 3. Updates all references to old IDs in attributes:\n * - Hash references: \"#old-id\" → \"#new-id\" (href, xlink:href)\n * - URL references: \"url(#old-id)\" → \"url(#new-id)\" (fill, clip-path, mask, marker, etc.)\n * - Style URL references: { offsetPath: \"url(#old-id)\" }\n *\n * @param doc - The animated SVG document to process\n * @returns A new document with regenerated IDs\n * @public @advanced\n */\nexport function generateNewIds(doc: PxAnimatedSvgDocument): PxAnimatedSvgDocument {\n // Deep clone the document\n const cloned: PxAnimatedSvgDocument = deepClone(doc);\n\n // Map of old ID → new ID\n const idMap = new Map<string, string>();\n\n // Attributes that contain hash references (#id)\n const hashRefAttrs = new Set(['href', 'xlink:href']);\n\n // Attributes that contain url(#id) references\n const urlRefAttrs = new Set([\n 'fill', 'stroke', 'clip-path', 'clipPath', 'mask',\n 'marker', 'marker-start', 'marker-mid', 'marker-end',\n 'filter', 'flood-color', 'lighting-color'\n ]);\n\n // Attributes that contain direct ID references (no # or url()). `source` is one\n // only inside an effect bucket (`maskedBy.source`, `clone.source`) — the scroll\n // timeline also has a `source` key ('nearest' | 'root'), which is not an id.\n const directIdRefAttrs = new Set(['targetId', 'boundElementId']);\n const isEffectSourceRef = (key: string, parentKey: string | undefined) =>\n key === 'source' && (parentKey === 'maskedBy' || parentKey === 'clone');\n\n // Phase 1: Collect all IDs and generate new ones\n function collectIds(node: any): void {\n if (!node || typeof node !== 'object') return;\n\n if (node.id && typeof node.id === 'string') {\n const oldId = node.id;\n const newId = generateUniqueId();\n idMap.set(oldId, newId);\n node.id = newId;\n } else if (node.animate) {\n // An animated node needs an id so the animation can bind to its rendered element.\n node.id = generateUniqueId();\n }\n\n // Process children\n if (Array.isArray(node.children)) {\n for (const child of node.children) {\n collectIds(child);\n }\n }\n }\n\n // Phase 2: Update all references to old IDs\n function updateRefs(node: any, parentKey?: string): void {\n if (!node || typeof node !== 'object') return;\n\n for (const [key, value] of Object.entries(node)) {\n if (key === 'children') {\n if (Array.isArray(value)) {\n for (const child of value) {\n updateRefs(child);\n }\n }\n continue;\n }\n\n if (typeof value === 'string') {\n // Check for hash references: href=\"#old-id\"\n if (hashRefAttrs.has(key) && value.startsWith('#')) {\n const oldId = value.slice(1);\n const newId = idMap.get(oldId);\n if (newId) {\n node[key] = '#' + newId;\n }\n }\n // Check for url() references: fill=\"url(#old-id)\"\n else if (urlRefAttrs.has(key)) {\n node[key] = replaceUrlRefs(value, idMap);\n }\n // Check for direct ID references: source=\"#_px_xxx\" (canonical `#id`,\n // SCHEMA-DESIGN §4 E-5) or bare source=\"_px_xxx\" (legacy) —\n // rewrite preserving the incoming spelling.\n else if (directIdRefAttrs.has(key) || isEffectSourceRef(key, parentKey)) {\n const hasHash = value.startsWith('#');\n const newId = idMap.get(hasHash ? value.slice(1) : value);\n if (newId) {\n node[key] = hasHash ? '#' + newId : newId;\n }\n }\n // Check for url() in any string value (e.g., in style strings)\n else if (value.includes('url(#')) {\n node[key] = replaceUrlRefs(value, idMap);\n }\n }\n // Check style object for url() references\n else if (key === 'style' && typeof value === 'object' && value !== null) {\n for (const [styleProp, styleValue] of Object.entries(value)) {\n if (typeof styleValue === 'string') {\n (value as any)[styleProp] = replaceUrlRefs(styleValue, idMap);\n }\n }\n }\n // Recursively process nested objects (effect buckets carry source/targetId refs;\n // in-place animated values like { keyframes: [{ value: \"url(#grad)\" }] }\n // also need ref rewriting on string values they contain).\n else if (typeof value === 'object' && value !== null) {\n updateRefs(value, key);\n }\n }\n }\n\n collectIds(cloned);\n updateRefs(cloned);\n\n // Re-point the bindings of a bind-by-id document. `target` is `#id`-spelled (review\n // §3.2) — strip for the lookup, keep the spelling on the way out.\n const docBindings = cloned.animator?.bindings;\n if (Array.isArray(docBindings)) {\n const updatedBindings = docBindings.map(binding => {\n const hashed = binding.target.startsWith('#');\n const id = hashed ? binding.target.slice(1) : binding.target;\n const newId = idMap.get(id) ?? id;\n return { ...binding, target: hashed ? '#' + newId : newId };\n });\n cloned.animator = { ...cloned.animator, bindings: updatedBindings };\n }\n\n return cloned;\n}\n\n/**\n * Replaces url(#old-id) references in a string with new IDs from the map.\n */\nfunction replaceUrlRefs(value: string, idMap: Map<string, string>): string {\n return value.replace(/url\\(#([^)]+)\\)/g, (match, oldId) => {\n const newId = idMap.get(oldId);\n return newId ? 'url(#' + newId + ')' : match;\n });\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport type { PxBezierPath, PxTransformParts } from '../format/PxAnimatorTypes';\n\n\n/**\n * Converts a PxBezierPath to an SVG path string.\n * Control points (i, o) are treated as ABSOLUTE coordinates.\n * @param {PxBezierPath} path\n * @returns {string}\n */\n/**\n * @param forceCurves Emit EVERY segment (incl. the closing one) as a cubic `C`, even when\n * its control points are degenerate (a straight line). Needed for keyframe values the\n * BROWSER interpolates (WAAPI / CSS `path()`): CSS only interpolates paths with\n * IDENTICAL command sequences, so an opportunistic `L` in one keyframe vs a `C` in the\n * next (e.g. a round-corner radius animating from 0) turns the whole animation\n * DISCRETE — it flips at 50% instead of morphing.\n * @internal\n */\nexport function bezierToSvgPath(path: PxBezierPath, forceCurves = false): string {\n const v = path.v;\n const i = path.i;\n const o = path.o;\n const c = path.c;\n\n if (!v.length) return \"\";\n\n const d: Array<string> = [];\n const len = v.length;\n d.push(\"M\" + v[0][0] + \",\" + v[0][1]);\n\n for (let idx = 1; idx < len; idx++) {\n const prevV = v[idx - 1];\n const prevO = o?.[idx - 1] ?? prevV;\n const currI = i?.[idx] ?? v[idx];\n const currV = v[idx];\n\n // Check if it's a straight line (control points coincide with vertices)\n const isLine = !forceCurves && (prevO[0] === prevV[0] && prevO[1] === prevV[1]) &&\n (currI[0] === currV[0] && currI[1] === currV[1]);\n\n if (isLine) {\n d.push(\"L\" + currV[0] + \",\" + currV[1]);\n } else {\n // Control points are absolute coordinates\n d.push(\"C\" + prevO[0] + \",\" + prevO[1] + \",\" + currI[0] + \",\" + currI[1] + \",\" + currV[0] + \",\" + currV[1]);\n }\n }\n\n if (c && len > 0) {\n const lastV = v[len - 1];\n const lastO = o?.[len - 1] ?? lastV;\n const firstI = i?.[0] ?? v[0];\n const firstV = v[0];\n\n // Check if closing segment is a straight line\n const isLine = !forceCurves && (lastO[0] === lastV[0] && lastO[1] === lastV[1]) &&\n (firstI[0] === firstV[0] && firstI[1] === firstV[1]);\n\n if (!isLine) {\n d.push(\"C\" + lastO[0] + \",\" + lastO[1] + \",\" + firstI[0] + \",\" + firstI[1] + \",\" + firstV[0] + \",\" + firstV[1]);\n }\n\n d.push(\"z\");\n }\n\n return d.join(\"\");\n}\n\n/**\n * @param {number} a \n * @param {number} b \n * @param {number} t \n * @returns {number}\n */\nexport function interpolateNum(a: number, b: number, t: number): number {\n return a + (b - a) * t;\n}\n\n\n/**\n * @param {Array<number>} a \n * @param {Array<number>} b \n * @param {number} t \n * @returns {Array<number>}\n */\nexport function interpolateVec(a: Array<number>, b: Array<number>, t: number): Array<number> {\n const res: Array<number> = [];\n const count = Math.max(a.length, b.length);\n for (let i = 0; i < count; i++) {\n res[i] = interpolateNum(a[i] || 0, b[i] || 0, t);\n }\n return res;\n}\n\n/**\n * Interpolates between two color arrays [r, g, b] or [r, g, b, a].\n * Normalizes both colors to 4 elements, defaulting alpha to 1 if missing.\n */\nexport function interpolateColor(a: Array<number>, b: Array<number>, t: number): Array<number> {\n return [\n interpolateNum(a[0] || 0, b[0] || 0, t),\n interpolateNum(a[1] || 0, b[1] || 0, t),\n interpolateNum(a[2] || 0, b[2] || 0, t),\n interpolateNum(a[3] === undefined ? 1 : a[3], b[3] === undefined ? 1 : b[3], t)\n ];\n}\n\n/**\n * Interpolates between two arrays of bezier paths.\n * @param paths1 The starting array of paths.\n * @param paths2 The ending array of paths.\n * @param progress The interpolation progress from 0.0 to 1.0.\n * @internal\n */\nexport function interpolateBeziers(\n paths1: Array<PxBezierPath>,\n paths2: Array<PxBezierPath>,\n progress: number\n): Array<PxBezierPath> {\n const count = Math.max(paths1.length, paths2.length);\n const res: Array<PxBezierPath> = [];\n for (let i = 0; i < count; i++) {\n res.push(interpolateBezier(paths1[i], paths2[i], progress));\n }\n return res;\n}\n\n/**\n * Interpolates between two bezier paths.\n * Control points (i, o) are treated as ABSOLUTE coordinates.\n * When control points are missing, they default to the vertex position.\n * @param {PxBezierPath} path1\n * @param {PxBezierPath} path2\n * @param {number} progress\n * @returns {PxBezierPath}\n */\nexport function interpolateBezier(\n path1: PxBezierPath | undefined,\n path2: PxBezierPath | undefined,\n progress: number\n): PxBezierPath {\n if (!path1 || !path2) return path1 || path2 || { v: [] };\n\n const t = Math.min(Math.max(progress, 0), 1);\n const len = Math.min(path1.v.length, path2.v.length);\n\n const v: Array<Array<number>> = [];\n const i: Array<Array<number>> = [];\n const o: Array<Array<number>> = [];\n\n for (let idx = 0; idx < len; idx++) {\n const v1 = path1.v[idx];\n const v2 = path2.v[idx];\n v.push(interpolateVec(v1, v2, t));\n\n // For absolute control points, default to vertex position (straight line)\n const i1 = path1.i?.[idx] ?? v1;\n const i2 = path2.i?.[idx] ?? v2;\n i.push(interpolateVec(i1, i2, t));\n\n const o1 = path1.o?.[idx] ?? v1;\n const o2 = path2.o?.[idx] ?? v2;\n o.push(interpolateVec(o1, o2, t));\n }\n\n return { v, i: i.length ? i : undefined, o: o.length ? o : undefined, c: path1.c ?? path2.c };\n}\n\n\n/**\n * Remap a number from one range to another.\n *\n * @param {number} value - The input value.\n * @param {number} inMin - Lower bound of the input range.\n * @param {number} inMax - Upper bound of the input range.\n * @param {number} outMin - Lower bound of the output range.\n * @param {number} outMax - Upper bound of the output range.\n * @returns The remapped value.\n */\nexport function remap(\n value: number,\n inMin: number,\n inMax: number,\n outMin: number,\n outMax: number\n): number {\n if (inMax === inMin) return outMin; // avoid divide-by-zero\n const t = (value - inMin) / (inMax - inMin);\n return outMin + t * (outMax - outMin);\n}\n\n/**\n * Solves for the parameter t such that the cubic bezier X(t) = x,\n * where the bezier has control point x-coordinates p1x and p2x\n * (endpoints are fixed at x=0 and x=1).\n * Uses Newton-Raphson with bisection fallback.\n */\nexport function solveCubicBezierX(p1x: number, p2x: number, x: number): number {\n if (x <= 0) return 0;\n if (x >= 1) return 1;\n\n const cx = 3 * p1x;\n const bx = 3 * (p2x - p1x) - cx;\n const ax = 1 - cx - bx;\n\n function sampleX(t: number) { return ((ax * t + bx) * t + cx) * t; }\n function sampleDX(t: number) { return (3 * ax * t + 2 * bx) * t + cx; }\n\n let t2 = x;\n let t0 = 0;\n let t1 = 1;\n\n for (let i = 0; i < 8; i++) {\n const x2 = sampleX(t2) - x;\n if (Math.abs(x2) < 1e-6) return t2;\n const d2 = sampleDX(t2);\n if (Math.abs(d2) < 1e-6) break;\n t2 -= x2 / d2;\n }\n\n t2 = x;\n while (t0 < t1) {\n const x2 = sampleX(t2);\n if (Math.abs(x2 - x) < 1e-6) return t2;\n if (x > x2) t0 = t2;\n else t1 = t2;\n t2 = (t1 + t0) / 2;\n }\n\n return t2;\n}\n\n/**\n * Creates a cubic-bezier easing function.\n * @param easing An array of four numbers [x1, y1, x2, y2] defining the bezier curve.\n * @returns A function that takes a progress value (0-1) and returns an eased value.\n * @internal\n */\nexport function cubicBezier(easing: [number, number, number, number]) {\n const [p1x, p1y, p2x, p2y] = easing;\n\n const cy = 3 * p1y;\n const by = 3 * (p2y - p1y) - cy;\n const ay = 1 - cy - by;\n\n function sampleCurveY(t: number) { return ((ay * t + by) * t + cy) * t; }\n\n return function (x: number) {\n return sampleCurveY(solveCubicBezierX(p1x, p2x, x));\n };\n}\n\ntype Point2 = [number, number];\n\nfunction lerp2(a: Point2, b: Point2, t: number): Point2 {\n return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];\n}\n\n/**\n * Splits a cubic bezier curve at parameter t using De Casteljau's algorithm.\n * Returns the left and right sub-curves as 4-point tuples.\n * @internal\n */\nexport function subdivideCubicBezier(\n p0: Point2, p1: Point2, p2: Point2, p3: Point2, t: number\n): { left: [Point2, Point2, Point2, Point2], right: [Point2, Point2, Point2, Point2] } {\n const q0 = lerp2(p0, p1, t);\n const q1 = lerp2(p1, p2, t);\n const q2 = lerp2(p2, p3, t);\n const r0 = lerp2(q0, q1, t);\n const r1 = lerp2(q1, q2, t);\n const s = lerp2(r0, r1, t);\n return {\n left: [p0, q0, r0, s],\n right: [s, r1, q2, p3]\n };\n}\n\ntype Easing = [number, number, number, number];\n\n/**\n * Splits a CSS cubic-bezier easing [x1,y1,x2,y2] at a given x-axis fraction.\n * Each half is re-normalized to map [0,0]→[1,1].\n * Returns undefined for either half if the input is undefined (linear) or the split is degenerate.\n * @internal\n */\nexport function splitEasing(\n easing: Easing | undefined,\n xFraction: number\n): { left: Easing | undefined, right: Easing | undefined } {\n if (!easing) return { left: undefined, right: undefined };\n if (xFraction <= 0) return { left: undefined, right: easing };\n if (xFraction >= 1) return { left: easing, right: undefined };\n\n const [x1, y1, x2, y2] = easing;\n const t = solveCubicBezierX(x1, x2, xFraction);\n\n const p0: Point2 = [0, 0];\n const p1: Point2 = [x1, y1];\n const p2: Point2 = [x2, y2];\n const p3: Point2 = [1, 1];\n\n const { left, right } = subdivideCubicBezier(p0, p1, p2, p3, t);\n\n // Split point coordinates\n const sx = left[3][0];\n const sy = left[3][1];\n\n let leftEasing: Easing | undefined;\n if (sx > 1e-9 && Math.abs(sy) > 1e-9) {\n leftEasing = [\n left[1][0] / sx, left[1][1] / sy,\n left[2][0] / sx, left[2][1] / sy\n ];\n }\n\n let rightEasing: Easing | undefined;\n const rx = 1 - sx;\n const ry = 1 - sy;\n if (rx > 1e-9 && Math.abs(ry) > 1e-9) {\n rightEasing = [\n (right[1][0] - sx) / rx, (right[1][1] - sy) / ry,\n (right[2][0] - sx) / rx, (right[2][1] - sy) / ry\n ];\n }\n\n return { left: leftEasing, right: rightEasing };\n}\n\n/**\n * Reverses a cubic-bezier easing for backward playback.\n * [x1,y1,x2,y2] → [1-x2, 1-y2, 1-x1, 1-y1].\n * @internal\n */\nexport function reverseEasing(easing: Easing | undefined): Easing | undefined {\n if (!easing) return undefined;\n return [1 - easing[2], 1 - easing[3], 1 - easing[0], 1 - easing[1]];\n}\n\n/**\n * Converts a color from a [r, g, b, a] array (where values are 0-1) to an rgba() or rgb() CSS string.\n * @param color The color array.\n * @internal\n */\nexport function toRGBA(color: Array<number>): string {\n const r = Math.round(color[0] * 255);\n const g = Math.round(color[1] * 255);\n const b = Math.round(color[2] * 255);\n return color.length === 4 ?\n 'rgba(' + r + ',' + g + ',' + b + ',' + color[3] + ')' :\n 'rgb(' + r + ',' + g + ',' + b + ')';\n}\n\n/** Parse rgb/rgba string to normalized array */\nexport function parseRgba(s: string): number[] {\n const inner = s.match(/rgba?\\((.*)\\)/)?.[1];\n if (!inner) throw new Error('Invalid rgb/rgba format');\n const parts = inner.split(',').map(v => +v.trim());\n return [parts[0] / 255, parts[1] / 255, parts[2] / 255, ...(parts[3] !== undefined ? [parts[3]] : [])];\n}\n\n/** Parse hex string to normalized array (#RGB, #RGBA, #RRGGBB, #RRGGBBAA) */\nfunction parseHex(s: string): number[] {\n const hex = s.slice(1); // Remove '#'\n const isShort = hex.length <= 4; // #RGB or #RGBA vs #RRGGBB or #RRGGBBAA\n\n const r = isShort ? hex[0] + hex[0] : hex.slice(0, 2);\n const g = isShort ? hex[1] + hex[1] : hex.slice(2, 4);\n const b = isShort ? hex[2] + hex[2] : hex.slice(4, 6);\n const a = hex.length === 4 ? hex[3] + hex[3] : hex.length === 8 ? hex.slice(6, 8) : null;\n\n const result = [\n parseInt(r, 16) / 255,\n parseInt(g, 16) / 255,\n parseInt(b, 16) / 255\n ];\n\n if (a !== null) {\n result.push(parseInt(a, 16) / 255);\n }\n\n return result;\n}\n\n//// FIXME - support normalization of config from different formats\n/** Parse color string (hex or rgb/rgba) to normalized [r, g, b] or [r, g, b, a] array (0-1 range) */\nexport function parseColor(s: any): number[] | undefined {\n if (!s) return undefined;\n if (Array.isArray(s)) return s;\n if (typeof s !== 'string') return undefined;\n if (s.startsWith('#')) {\n return parseHex(s);\n } else if (s.startsWith('rgb')) {\n return parseRgba(s);\n } else {\n // FIXME - come up with some solution how to report errors...\n console.warn('Unsupported color format: ' + s);\n }\n return undefined;\n}\n\n/** @internal */\nexport const PX_COLOR_ATTR_NAMES = new Set([\"color\", \"fill\", \"flood-color\", \"lighting-color\", \"stop-color\", \"stroke\"]);\n/** @internal */\nexport const PX_TRANSFORM_FN_NAMES = new Set([\"translate\", \"rotate\", \"scale\", \"skew\"]);\n/** @internal */\nexport const PX_PCT_BASED_ATTR_NAMES = new Set([\"offset-distance\", \"offsetDistance\"]);\n\n/**\n * Compose a `PxTransformParts` record into a single SVG/CSS transform string in\n * the canonical order:\n *\n * translate, translate(+origin), rotate, scale, translate(-origin)\n *\n * Each part is omitted when not present. `origin` becomes a `translate(+o)` /\n * `translate(-o)` pair surrounding the rotate/scale segment — the SVG-native\n * way to render a transform-origin pivot.\n *\n * @param parts the parts record (translate / rotate / scale / origin)\n * @param opts.withUnits when true (default), translates use `px` and rotate\n * uses `deg` — required for CSS / WebAnimations keyframes. When false, no\n * units are emitted — required for the SVG `transform` attribute.\n * @internal\n */\nexport function composeTransformParts(\n parts: PxTransformParts | null | undefined,\n opts?: { withUnits?: boolean }\n): string {\n if (!parts) return '';\n const withUnits = opts?.withUnits ?? true;\n const segs: Array<string> = [];\n const t = parts.translate;\n const o = parts.origin;\n const r = parts.rotate;\n const k = parts.skew;\n const s = parts.scale;\n const tu = withUnits ? 'px' : '';\n const ru = withUnits ? 'deg' : '';\n if (t) segs.push('translate(' + t[0] + tu + ',' + t[1] + tu + ')');\n if (o) segs.push('translate(' + o[0] + tu + ',' + o[1] + tu + ')');\n if (r !== undefined && r !== null) segs.push('rotate(' + r + ru + ')');\n // Canonical slot: between rotate and scale (Lottie-compatible; pivots at origin).\n if (k !== undefined && k !== null) segs.push('skewX(' + k + ru + ')');\n if (s) segs.push('scale(' + s[0] + ',' + s[1] + ')');\n if (o) segs.push('translate(' + (-o[0]) + tu + ',' + (-o[1]) + tu + ')');\n return segs.join('');\n}\n/**\n * Parses an SVG `transform` attribute string back into a `PxTransformParts` record —\n * the inverse of {@link composeTransformParts} for its canonical shapes.\n *\n * Deliberately CONSERVATIVE (review §0.4 — used to merge a static transform under an\n * animation, where a wrong guess is worse than no merge): accepts only a linear\n * sequence with at most one `translate`, `rotate`, `skewX`, `scale` in the canonical\n * order. Anything else — `matrix(…)`, repeated functions, the ±origin translate\n * sandwich, three-arg `rotate(a cx cy)` — returns `undefined` (caller skips the merge).\n * @internal\n */\nexport function parseTransformParts(str: string | null | undefined): PxTransformParts | undefined {\n if (!str || typeof str !== 'string') return undefined;\n const out: PxTransformParts = {};\n const re = /([a-zA-Z]+)\\s*\\(([^)]*)\\)/g;\n const order = ['translate', 'rotate', 'skewX', 'scale'];\n let lastIdx = -1;\n let m: RegExpExecArray | null;\n while ((m = re.exec(str)) !== null) {\n const fn = m[1];\n const idx = order.indexOf(fn);\n if (idx < 0 || idx <= lastIdx) return undefined; // unknown fn, repeat, or out of order\n lastIdx = idx;\n const nums = m[2].split(/[\\s,]+/).filter(Boolean).map(Number);\n if (nums.some(n => Number.isNaN(n))) return undefined;\n if (fn === 'translate') {\n if (nums.length < 1 || nums.length > 2) return undefined;\n out.translate = [nums[0], nums[1] ?? 0];\n } else if (fn === 'rotate') {\n if (nums.length !== 1) return undefined; // rotate(a cx cy) has no parts spelling\n out.rotate = nums[0];\n } else if (fn === 'skewX') {\n if (nums.length !== 1) return undefined;\n out.skew = nums[0];\n } else { // scale\n if (nums.length < 1 || nums.length > 2) return undefined;\n out.scale = [nums[0], nums[1] ?? nums[0]];\n }\n }\n // Reject when anything but whitespace remains outside the parsed functions.\n if (str.replace(/([a-zA-Z]+)\\s*\\(([^)]*)\\)/g, '').replace(/[\\s,]/g, '').length) return undefined;\n return Object.keys(out).length ? out : undefined;\n}\n\n/** @internal */\nexport const PX_STYLE_ATTR_NAMES = new Set([\"offset-distance\", \"offsetDistance\"]); // Props that need to go to style\n/** @internal */\nexport const PX_DEFAULT_DURATION_MS = 1000;\n\n/**\n * Converts a kebab-case string to camelCase.\n * @param kebab The kebab-case string.\n * @internal\n */\nexport function kebabToCamelCaseWord(kebab: string): string {\n return kebab.includes('-') ? kebab.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) : kebab;\n}\n\n/**\n * Checks if a string is in camelCase.\n * @param word The string to check.\n */\nexport function isCamelCaseWord(word: string): boolean {\n return !word.includes('-') && /[a-z][A-Z]/.test(word);\n}\n\n// FIXME - docs, rename?\nconst SVG_CAMEL_CASE_ATTRS = new Set([\n // Transform/positioning\n 'viewBox',\n 'preserveAspectRatio',\n\n // Gradient\n 'gradientUnits',\n 'gradientTransform',\n 'spreadMethod',\n\n // Pattern\n 'patternUnits',\n 'patternContentUnits',\n 'patternTransform',\n\n // Clipping/masking\n 'clipPathUnits',\n 'maskUnits',\n 'maskContentUnits',\n\n // Marker (SVG spec keeps these camelCase, like viewBox)\n 'markerUnits',\n 'markerWidth',\n 'markerHeight',\n 'refX',\n 'refY',\n\n // Text\n 'textLength',\n 'lengthAdjust',\n 'startOffset',\n\n // Filter\n 'filterUnits',\n 'primitiveUnits',\n 'tableValues', // feFuncR/G/B/A transfer table (type=\"table\")\n 'stdDeviation',\n 'baseFrequency',\n 'numOctaves',\n 'surfaceScale',\n 'diffuseConstant',\n 'specularConstant',\n 'specularExponent',\n 'kernelMatrix',\n 'kernelUnitLength',\n 'edgeMode',\n 'preserveAlpha',\n 'targetX',\n 'targetY',\n\n // // Animation\n // 'attributeName',\n // 'attributeType',\n // 'calcMode',\n // 'keyTimes',\n // 'keySplines',\n // 'repeatCount',\n // 'repeatDur' \n]);\n\n/**\n * Converts a camelCase string to kebab-case.\n * @param camel The camelCase string.\n * @internal\n */\nexport function camelCaseToKebabWordIfNeeded(camel: string): string { // FIXME - docs, rename function?\n return SVG_CAMEL_CASE_ATTRS.has(camel) ?\n camel :\n camel.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();\n}\n\n/**\n * Checks if a CSS property is set inline on an element's style.\n *\n * Typed structurally (not as the DOM `CSSStyleDeclaration`) so this package\n * stays platform-neutral; a real `element.style` satisfies the shape.\n *\n * @param style - element.style (CSSStyleDeclaration-shaped)\n * @param propName - property name (camelCase or kebab-case)\n */\nexport function hasStyleProp(\n style: { getPropertyValue(propName: string): string },\n propName: string\n): boolean {\n return style.getPropertyValue(propName) !== '';\n}\n\n/**\n * Clamps a number between a minimum and maximum value.\n * @param value The number to clamp.\n * @param min The minimum value.\n * @param max The maximum value.\n * @internal\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(value, max));\n}\n\n\n// ============================================================================\n// 2D CUBIC BÉZIER PRIMITIVES — motion-along-path support\n// ============================================================================\n//\n// Hand-written, dependency-free 2D Bézier maths used by the motion-along-path\n// playback paths (both WAAPI normalization and frames-mode direct compute).\n// See `fix-motion-along-path--fix-plan.md` for the wider plan.\n//\n// Conventions:\n// - Points are `[x, y]` tuples (`Point2`) — matches the wire format.\n// - A cubic segment is `(P0, P1, P2, P3)` where `P0` and `P3` are the\n// endpoints and `P1`, `P2` are the control points in ABSOLUTE coordinates.\n// - The Lottie-style tangent storage convention is `kf.to` =\n// outgoing-from-kf-as-a-delta, `kf.ti` = incoming-at-the-next-kf-as-a-delta.\n// The wire format re-attaches them to the natural endpoints as\n// `tangentOut` on the FROM keyframe and `tangentIn` on the TO keyframe.\n// Caller is responsible for the position-to-control-point lift, i.e.\n// `P1 = fromKf.value + fromKf.tangentOut`, `P2 = toKf.value + toKf.tangentIn`.\n\n\n/**\n * Evaluate a 2D cubic Bézier at parameter `t ∈ [0, 1]` via Bernstein form:\n *\n * B(t) = (1-t)³ P0 + 3t(1-t)² P1 + 3t²(1-t) P2 + t³ P3\n *\n * `t` is the curve PARAMETER, not arc-length. For arc-length (CSS Motion Path\n * / `offset-distance`) semantics, use `bezier2D_tForDistance` first to convert\n * a distance to its parameter, then call this. Out-of-range `t` is clamped.\n */\nexport function bezier2D_pointAt(\n P0: Point2, P1: Point2, P2: Point2, P3: Point2,\n t: number\n): Point2 {\n if (t <= 0) return [P0[0], P0[1]];\n if (t >= 1) return [P3[0], P3[1]];\n const u = 1 - t;\n const u2 = u * u;\n const u3 = u2 * u;\n const t2 = t * t;\n const t3 = t2 * t;\n const w0 = u3;\n const w1 = 3 * t * u2;\n const w2 = 3 * t2 * u;\n const w3 = t3;\n return [\n w0 * P0[0] + w1 * P1[0] + w2 * P2[0] + w3 * P3[0],\n w0 * P0[1] + w1 * P1[1] + w2 * P2[1] + w3 * P3[1],\n ];\n}\n\n\n/**\n * Evaluate the derivative `B'(t)` of a 2D cubic Bézier at parameter `t`.\n *\n * B'(t) = 3(1-t)² (P1-P0) + 6t(1-t) (P2-P1) + 3t² (P3-P2)\n *\n * Returns the tangent VECTOR (not a unit vector). For auto-orient rotation,\n * caller computes `Math.atan2(d.y, d.x)`.\n *\n * Epsilon-nudge for degenerate endpoints: when a handle coincides with its\n * endpoint (e.g. `P1 === P0` and `t === 0`, common for the start/end of a\n * Lottie spatial-tangent path), the derivative collapses to zero. The\n * exact-endpoint value is then meaningless; we nudge `t` inward by `1e-4`\n * and retry.\n */\nconst BEZIER_T_NUDGE = 1e-4;\nexport function bezier2D_derivativeAt(\n P0: Point2, P1: Point2, P2: Point2, P3: Point2,\n t: number\n): Point2 {\n const result = _bezier2D_derivativeAtRaw(P0, P1, P2, P3, t);\n if (result[0] === 0 && result[1] === 0) {\n // Degenerate (handle = endpoint). Nudge inward and retry.\n const nudgedT = t < 0.5 ? t + BEZIER_T_NUDGE : t - BEZIER_T_NUDGE;\n return _bezier2D_derivativeAtRaw(P0, P1, P2, P3, nudgedT);\n }\n return result;\n}\n\nfunction _bezier2D_derivativeAtRaw(\n P0: Point2, P1: Point2, P2: Point2, P3: Point2,\n t: number\n): Point2 {\n const u = 1 - t;\n const a = 3 * u * u;\n const b = 6 * t * u;\n const c = 3 * t * t;\n return [\n a * (P1[0] - P0[0]) + b * (P2[0] - P1[0]) + c * (P3[0] - P2[0]),\n a * (P1[1] - P0[1]) + b * (P2[1] - P1[1]) + c * (P3[1] - P2[1]),\n ];\n}\n\n\n/**\n * Arc-length lookup table for a 2D cubic Bézier.\n *\n * Samples the curve at `steps + 1` evenly-spaced parameter values\n * (`t = 0, 1/steps, 2/steps, …, 1`), computes the cumulative Euclidean\n * distance between consecutive samples, and returns parallel\n * `Float64Array`s for parameter (`ts`) and arc length (`ds`). `ds[steps]`\n * is the total arc length of the curve.\n *\n * The LUT shape is `{ts, ds}` with parallel Float64Arrays rather than\n * `Array<{t, d}>` because:\n * - One contiguous allocation per array instead of `steps+1` objects.\n * - Binary search in `bezier2D_tForDistance` reads `Float64Array` directly.\n * - The structure is read-only after construction — no need for per-sample\n * field access.\n *\n * Approximation error is roughly `O((1/steps)²)` for smooth curves; the\n * default 100 samples gives <1% error vs analytic arc length on typical\n * motion paths.\n */\nexport interface ArcLengthLUT {\n readonly ts: Float64Array;\n readonly ds: Float64Array;\n}\n\nexport function bezier2D_arcLengthLUT(\n P0: Point2, P1: Point2, P2: Point2, P3: Point2,\n steps: number = 100\n): ArcLengthLUT {\n const n = steps + 1;\n const ts = new Float64Array(n);\n const ds = new Float64Array(n);\n\n let prev = bezier2D_pointAt(P0, P1, P2, P3, 0);\n ts[0] = 0;\n ds[0] = 0;\n\n let cum = 0;\n for (let i = 1; i < n; i++) {\n const t = i / steps;\n const cur = bezier2D_pointAt(P0, P1, P2, P3, t);\n const dx = cur[0] - prev[0];\n const dy = cur[1] - prev[1];\n cum += Math.sqrt(dx * dx + dy * dy);\n ts[i] = t;\n ds[i] = cum;\n prev = cur;\n }\n return { ts, ds };\n}\n\n\n/**\n * Inverse of `bezier2D_arcLengthLUT` lookup: given a distance along the\n * curve, return the curve parameter `t` reached at that distance via\n * binary search on `lut.ds` + linear interpolation between adjacent\n * samples.\n *\n * Distance is clamped to `[0, lut.ds[last]]`. The returned `t` is in\n * `[0, 1]`. Pair with `bezier2D_pointAt` to get the point at that\n * arc-length distance:\n *\n * const t = bezier2D_tForDistance(lut, distance);\n * const point = bezier2D_pointAt(P0, P1, P2, P3, t);\n */\nexport function bezier2D_tForDistance(lut: ArcLengthLUT, distance: number): number {\n const { ts, ds } = lut;\n const last = ds.length - 1;\n if (distance <= 0) return ts[0];\n if (distance >= ds[last]) return ts[last];\n\n // Binary search for the upper-bound index `hi` such that ds[hi-1] <= distance < ds[hi].\n let lo = 1;\n let hi = last;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n if (ds[mid] < distance) lo = mid + 1;\n else hi = mid;\n }\n // Linear interpolate between the bracketing samples.\n const dPrev = ds[hi - 1];\n const dCur = ds[hi];\n const span = dCur - dPrev;\n const frac = span > 0 ? (distance - dPrev) / span : 0;\n return ts[hi - 1] + frac * (ts[hi] - ts[hi - 1]);\n}\n\n\n/**\n * `bezier2D_tForDistance` convenience taking a fraction of the total arc\n * length instead of an absolute distance. `pct` is in `[0, 1]` (CSS Motion\n * Path `offset-distance` semantics — `offset-distance: 50%` ≡\n * `bezier2D_tForDistancePct(lut, 0.5)`). Out-of-range values clamp.\n */\nexport function bezier2D_tForDistancePct(lut: ArcLengthLUT, pct: number): number {\n const total = lut.ds[lut.ds.length - 1];\n return bezier2D_tForDistance(lut, pct * total);\n}\n\n\n/**\n * Inverse of {@link bezier2D_tForDistance}: given a curve parameter `t`,\n * returns the arc length from the start. Binary searches the LUT's `ts`\n * (uniformly spaced) and linearly interpolates `ds` between adjacent samples.\n */\nexport function bezier2D_arcAtT(lut: ArcLengthLUT, t: number): number {\n const { ts, ds } = lut;\n const last = ts.length - 1;\n if (t <= ts[0]) return ds[0];\n if (t >= ts[last]) return ds[last];\n let lo = 1, hi = last;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n if (ts[mid] < t) lo = mid + 1;\n else hi = mid;\n }\n const tPrev = ts[hi - 1];\n const span = ts[hi] - tPrev;\n const frac = span > 0 ? (t - tPrev) / span : 0;\n return ds[hi - 1] + frac * (ds[hi] - ds[hi - 1]);\n}\n\n\n/**\n * Inverse of {@link cubicBezier}: for a CSS easing `[x1,y1,x2,y2]` and a\n * target output `y`, returns the input `x` such that\n * `cubicBezier(easing)(x) ≈ y`. Works for monotonic easings (the CSS\n * default) by swapping the easing's x/y axes — the inverse easing has\n * controls `[y1, x1, y2, x2]`, which `cubicBezier` evaluates directly.\n *\n * `undefined` easing (linear) → identity function.\n */\nexport function invertEasing(easing: Easing | undefined): (y: number) => number {\n if (!easing) return (y) => y;\n const flipped: Easing = [easing[1], easing[0], easing[3], easing[2]];\n return cubicBezier(flipped);\n}","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { type PxDefinitions } from '../format/PxAnimatorTypes';\nimport { INTERNAL_ATTRS, TRANSFORM_ATTR } from '../format/PxAnimatorConstants';\nimport { PX_COLOR_ATTR_NAMES, composeTransformParts, kebabToCamelCaseWord, toRGBA, PX_TRANSFORM_FN_NAMES } from './PxAnimatorUtil';\n\n\n/**\n * Tags that MUST never be created — the only real XSS surfaces in SVG:\n * - `<script>` — direct JS execution.\n * - `<foreignObject>` — embeds arbitrary HTML (incl. `<script>`, `<iframe>`).\n *\n * Everything else (shape elements, gradients, patterns, markers, filters\n * including `feComponentTransfer` / `feFuncA` / `feFlood` / `feComposite` /\n * `feImage`, SMIL `<animate>` family, `<a>` hyperlinks, …) is allowed. URL\n * sanitization in `sanitizeAttributeValue` blocks `javascript:` / external\n * refs on `href` / `src` / `mask` / `marker*`.\n * @internal\n */\nexport const PX_DISALLOWED_SVG_TAGS_LOWER = new Set([\n 'script',\n 'foreignobject',\n]);\n\n\n/**\n * URL-valued attributes that must reference an internal `#id` / `url(#id)`\n * only — never an external URL, `javascript:`, etc. Values for these names\n * are sanitized in `sanitizeAttributeValue`; non-internal refs are dropped.\n *\n * Exception: image-ref attributes (`href` / `xlink:href` / `src`) ALSO\n * accept `data:image/…` URIs — base64 raster (`image/png`, `image/jpeg`,\n * `image/gif`, `image/webp`, `image/bmp`) and `image/svg+xml` (base64 or\n * URL-encoded). Browsers render any image referenced from `<image>` in\n * SVG \"secure static mode\": scripts inside the referenced SVG do NOT\n * execute, external resources don't load, and interaction is disabled —\n * the same sandbox `<img src=…svg>` uses. The `image/svg+xml` form is\n * therefore safe as an image source even though a top-level SVG document\n * with the same bytes could embed scripts.\n *\n * Stored lowercased so `sanitizeAttributeValue`'s `name.toLowerCase()`\n * lookup matches regardless of input casing (`href` / `xlink:href` /\n * `clipPath` / `clip-path` all hit the same entry).\n */\nconst URL_VALUE_ATTRS_LOWER = new Set([\n 'href', // <use>, <image>\n 'xlink:href', // legacy <use>\n 'src', // <image>\n 'filter', // url(#filterId)\n 'clippath', // clip-path=\"url(#…)\"\n 'mask', // url(#maskId)\n 'markerstart', // marker-start=\"url(#…)\"\n 'markermid', // marker-mid=\"url(#…)\"\n 'markerend', // marker-end=\"url(#…)\"\n]);\n\n/** Attrs where a `data:image/…` URI is a legitimate value (i.e. the SVG\n * element actually renders the bytes — `<image>`). Other URL-valued\n * attrs ignore data URIs at the browser layer, so widening the allow-\n * list there would be cargo-culted and noisy. */\nconst IMAGE_REF_ATTRS_LOWER = new Set(['href', 'xlink:href', 'src']);\n\n/** CSS-only properties that are NOT SVG presentation attributes — the browser\n * ignores them via `setAttribute`, so they must be applied through `element.style`.\n * Keyed camelCase to match the normalized prop names (`element.style.mixBlendMode`). * @internal\n */\nexport const PX_CSS_ONLY_STYLE_PROPS = new Set<string>(['mixBlendMode', 'isolation']);\n\n/** Matches `data:image/{png|jpeg|jpg|gif|webp|bmp};base64,<payload>`.\n * Rejects any non-base64-encoded raster form. */\nconst DATA_RASTER_IMAGE_RE = /^data:image\\/(?:png|jpe?g|gif|webp|bmp);base64,/i;\n\n/** Matches `data:image/svg+xml[;params],<payload>` — base64\n * (`;base64,`), `;utf8,` / `;charset=UTF-8,`, and bare `,` (URL-encoded)\n * forms. Safe because the browser sandboxes any SVG referenced from an\n * image source (no scripts, no external loads, no interaction). */\nconst DATA_SVG_IMAGE_RE = /^data:image\\/svg\\+xml(?:;[^,]*)?,/i;\n\n/** Content-sniff fallback for `data:image/*;base64,…` URIs whose declared\n * subtype isn't a known raster type — e.g. `data:image/undefined;base64,<png>`,\n * which some Lottie exporters emit. The browser (and lottie-web) render these by\n * sniffing the leading bytes; we mirror that by matching the base64 payload\n * against common raster magic numbers (PNG `\\x89PNG`, JPEG `\\xFF\\xD8\\xFF`, GIF\n * `GIF8`, WebP/RIFF, BMP `BM`). Accepting on real image bytes keeps the\n * sanitizer's safety guarantee — raster bytes are inert — without trusting the\n * (here bogus) subtype, so the image renders instead of being dropped. */\nconst DATA_IMAGE_BASE64_PAYLOAD_RE = /^data:image\\/[^;,]*;base64,([A-Za-z0-9+/]{8})/i;\nconst BASE64_RASTER_MAGICS = ['iVBORw0K', '/9j/', 'R0lGOD', 'UklGR', 'Qk'];\nfunction isContentSniffedRasterImage(str: string): boolean {\n const m = DATA_IMAGE_BASE64_PAYLOAD_RE.exec(str);\n return !!m && BASE64_RASTER_MAGICS.some(magic => m[1].startsWith(magic));\n}\n\n\n/**\n * Attribute-name predicate: anything `name` matching this is dropped at\n * `setAttribute` time. The list is intentionally small — SVG's real\n * security surface is event handlers; every other concerning attribute\n * (URL refs, fill/stroke `url(…)`) is value-sanitized below, not name-\n * blocked. Adding entries here is a structural decision, not whack-a-mole.\n */\nfunction isDangerousAttrName(nameLower: string): boolean {\n // Event handlers — `onclick`, `onload`, `onerror`, `onmouseover`,\n // `onfocus`, `onfocusin`, SMIL `onbegin`/`onend`/`onrepeat`, … and any\n // future one. No legitimate SVG attribute starts with `on`, so this\n // prefix is a safe blanket block.\n if (nameLower.startsWith('on')) return true;\n return false;\n}\n\n\n/**\n * Returns the value to pass to `setAttribute`, or `undefined` to drop the\n * attribute entirely. Dropping leaves the DOM clean — the caller skips\n * `setAttribute` when this returns `undefined`.\n *\n * Three layers:\n * 1. Drop dangerous names (`on*` event handlers).\n * 2. Restrict `url(…)` in `fill` / `stroke` / `stop-color` to\n * internal `url(#id)` references.\n * 3. Restrict URL-valued attrs (`href`, `src`, `filter`, `clip-path`,\n * `mask`, `marker*`) to internal `#id` / `url(#id)`. Image-ref\n * attrs (`href` / `xlink:href` / `src`) additionally accept\n * `data:image/…` URIs — base64 raster (png/jpeg/gif/webp/bmp) and\n * `image/svg+xml` (sandboxed by the browser when used as an image\n * source). Blocks `javascript:` and external URLs.\n * Everything else passes through.\n * @internal\n */\nexport function sanitizeAttributeValue(name: string, value: any): any | undefined {\n const nameLower = name.toLowerCase();\n\n if (isDangerousAttrName(nameLower)) {\n console.warn('Attribute blocked (event handler / dangerous): ', nameLower);\n return undefined;\n }\n\n if (nameLower === 'fill' || nameLower === 'stroke' || nameLower === 'stopcolor') {\n const str = String(value);\n if (str.includes('url(') && !/^url\\(#[^)]+\\)$/.test(str)) {\n console.warn('Attribute \"' + nameLower + '\" blocked: url() must be internal url(#id), got:', value);\n return undefined;\n }\n return value;\n }\n\n if (URL_VALUE_ATTRS_LOWER.has(nameLower)) {\n const str = String(value);\n if (str.startsWith('#')) return value; // internal fragment ref\n if (/^url\\(#[^)]+\\)$/.test(str)) return value; // internal `url(#id)`\n // Image-ref attrs accept `data:image/…` URIs — raster bytes are\n // inert, and SVG referenced via an image source is sandboxed by\n // the browser (no script execution).\n if (IMAGE_REF_ATTRS_LOWER.has(nameLower) && (DATA_RASTER_IMAGE_RE.test(str) || DATA_SVG_IMAGE_RE.test(str) || isContentSniffedRasterImage(str))) return value;\n console.warn('Attribute \"' + nameLower + '\" blocked: must be #id, url(#id), or data:image/… URI, got:', value);\n return undefined;\n }\n\n return value;\n}\n\n/** @public @advanced */\nexport function toDomProps(props: Record<string, any>) {\n const propsCopy: Record<string, any> = {};\n\n // Process regular attributes\n for (const rawKey of Object.keys(props)) {\n // Wire-format inputs may use kebab-case SVG attribute names (e.g.\n // `stroke-width`); normalize to camelCase up-front so the whitelist\n // (camelCase) and the `camelCaseToKebabWordIfNeeded` re-conversion\n // at write time both work. `kebabToCamelCaseWord` is a no-op for\n // keys with no `-`, leaving camelCase inputs untouched.\n const key = kebabToCamelCaseWord(rawKey);\n if (INTERNAL_ATTRS.has(key)) continue;\n if (key === 'style') continue;\n\n let value = props[rawKey];\n\n if (PX_COLOR_ATTR_NAMES.has(key) && Array.isArray(value)) {\n propsCopy[key] = toRGBA(value);\n } else if (\n key === 'transform' && value !== null && typeof value === 'object' &&\n !Array.isArray(value) && !value.keyframes\n ) {\n // Unified transform static record — bare `{translate, rotate, …}`\n // (canonical) or the legacy `{value: PxTransformParts}` wrapper.\n // Compose into an SVG transform string (no units — SVG transform attribute).\n const parts = value.value && typeof value.value === 'object' ? value.value : value;\n propsCopy[TRANSFORM_ATTR] = composeTransformParts(parts, { withUnits: false });\n } else if (PX_TRANSFORM_FN_NAMES.has(key)) {\n if (Array.isArray(value)) {\n if (key === 'translate') value = value.map((v: number) => v + 'px');\n value = value.join(',');\n }\n if (key === 'rotate') value = value + 'deg';\n propsCopy[TRANSFORM_ATTR] = key + '(' + value + ')';\n } else if (Array.isArray(value)) {\n // Raw-array STATIC form of number-list attributes — `strokeDasharray: [16, 16]`\n // (the wire's canonical static shape; the string form \"16,16\" is also accepted\n // and passes through the String() branch below). SVG list attributes take the\n // comma-separated string. Color arrays were already handled above.\n propsCopy[key] = value.join(',');\n } else if (value !== undefined && value !== null) {\n propsCopy[key] = String(value);\n }\n }\n\n return propsCopy;\n}\n\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n/**\n * Motion-along-path → plain transform keyframes.\n *\n * Editor wire format for a curved-translation (and/or `autoOrient`) animation\n * carries per-kf `tangentIn` / `tangentOut` plus an animation-level `autoOrient`\n * flag — a parametric representation that any consumer must evaluate per frame.\n *\n * This module DESUGARS that shape into a regular unified-transform animation:\n * extra `{ translate, rotate? }` keyframes are inserted at curve extrema and\n * adaptive bisection points so the linear chord between adjacent samples stays\n * within `flatnessTolerance` of the true Bezier, and the original easing is\n * SPLIT (via De Casteljau, `splitEasing` in `PxAnimatorUtil`) across the\n * sub-segments so the combined timing reproduces the input easing exactly.\n *\n * Output kfs have no tangents and no `autoOrient` — both engines (`frames` and\n * `waapi`) and any future renderer (e.g. react-native-svg) consume them via\n * their normal unified-transform code path.\n *\n * The plan + rationale (extremes-aware sampling, easing-split, full pipeline\n * order) is in `motion-along-path-waapi-rework.md`.\n */\n\n\nimport { bezier2D_arcAtT, bezier2D_arcLengthLUT, bezier2D_derivativeAt, bezier2D_pointAt, clamp, invertEasing, splitEasing } from '../util/PxAnimatorUtil';\nimport type { ArcLengthLUT } from '../util/PxAnimatorUtil';\nimport type { PxAnyKeyframe, PxKeyframe, PxNormalizedKeyframe, PxNode, PxPropertyAnimation, PxTransformParts } from '../format/PxAnimatorTypes';\nimport { keyframeTime, keyframeValue, keyframeEasing, keyframeTangentIn, keyframeTangentOut } from '../format/PxAnimatorTypes';\n\n\ntype Point2 = [number, number];\ntype Easing = [number, number, number, number];\n\n\nfunction getKfTranslate(kf: PxAnyKeyframe): Point2 | undefined {\n const v = keyframeValue(kf);\n if (!v) return undefined;\n if (Array.isArray(v) && v.length >= 2 && typeof v[0] === 'number' && typeof v[1] === 'number') {\n // Composite per-part shape: `value: [x, y]` directly.\n return [v[0], v[1]];\n }\n const tr = (v as PxTransformParts).translate;\n if (Array.isArray(tr) && tr.length >= 2) return [tr[0], tr[1]];\n return undefined;\n}\n\nfunction getKfTime(kf: PxAnyKeyframe): number {\n return keyframeTime(kf) as number;\n}\n\nfunction getKfEasing(kf: PxAnyKeyframe): Easing | undefined {\n return keyframeEasing(kf) as Easing | undefined;\n}\n\n\n/**\n * True when `anim` is a motion-along-path animation — at least one keyframe\n * carries spatial tangents (`tangentIn` / `tangentOut`) and/or the animation\n * has `autoOrient` set. Animation-level helper; works for either the body\n * `transform` slot or a composite per-part `translate` slot.\n * @internal\n */\nexport function propAnimIsMotionPath(anim: PxPropertyAnimation): boolean {\n const kfs: Array<PxAnyKeyframe> | undefined = anim.keyframes;\n if (!Array.isArray(kfs)) return false;\n if (anim.autoOrient) return true;\n for (const kf of kfs) {\n if (keyframeTangentIn(kf) || keyframeTangentOut(kf)) return true;\n }\n return false;\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Segment cache — shared by `evaluateMotionPathSegment` (frames-mode kernel)\n// and the materializer. Keyed by FROM-keyframe identity (WeakMap), so cache\n// entries vanish automatically when keyframes are replaced.\n// ─────────────────────────────────────────────────────────────────────────────\n\n\ninterface MotionPathSegmentCache {\n readonly P0: Point2;\n readonly P1: Point2;\n readonly P2: Point2;\n readonly P3: Point2;\n readonly lut: ArcLengthLUT;\n readonly totalArc: number;\n}\n\n// Keyed on the PAIR, not on `prevKf` alone: one keyframe can start different segments\n// across evaluations (a lookup that falls outside the keyframe range answers with a\n// first→last pair), and a `prevKf`-only key let that bogus segment's Bezier be served\n// for every later evaluation of the real `prevKf`→next segment.\nconst _segmentCache = new WeakMap<PxAnyKeyframe, WeakMap<PxAnyKeyframe, MotionPathSegmentCache>>();\n\nfunction getSegmentCache(\n prevKf: PxAnyKeyframe,\n nextKf: PxAnyKeyframe,\n prevPos: Point2,\n nextPos: Point2,\n): MotionPathSegmentCache {\n let byNext = _segmentCache.get(prevKf);\n const existing = byNext?.get(nextKf);\n if (existing) return existing;\n const to = keyframeTangentOut(prevKf);\n const ti = keyframeTangentIn(nextKf);\n const P1: Point2 = [prevPos[0] + (to ? to[0] : 0), prevPos[1] + (to ? to[1] : 0)];\n const P2: Point2 = [nextPos[0] + (ti ? ti[0] : 0), nextPos[1] + (ti ? ti[1] : 0)];\n const lut = bezier2D_arcLengthLUT(prevPos, P1, P2, nextPos);\n const entry: MotionPathSegmentCache = {\n P0: prevPos, P1, P2, P3: nextPos,\n lut,\n totalArc: lut.ds[lut.ds.length - 1],\n };\n if (!byNext) { byNext = new WeakMap<PxAnyKeyframe, MotionPathSegmentCache>(); _segmentCache.set(prevKf, byNext); }\n byNext.set(nextKf, entry);\n return entry;\n}\n\n/** Test helper. No-op in production (WeakMap; entries self-evict). Tests\n * should use fresh keyframe objects to force cache misses. */\nexport function _resetMotionPathSegmentCache(): void {\n // Intentionally empty — kept for backwards-compat with any callers.\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Frames-mode kernel — preserved for any caller that still wants parametric\n// evaluation. The binding pipeline no longer needs it (motion-path is\n// materialized at `normalizeBindings` time), but it's a useful primitive\n// on its own.\n// ─────────────────────────────────────────────────────────────────────────────\n\n\n/** @internal */\nexport interface MotionPathSample {\n /** Translate at the current time (motion-path arc-length-parametrised). */\n readonly translate: Point2;\n /** Auto-orient rotation in degrees (only when `autoOrient` is set). */\n readonly rotateDeg?: number;\n}\n\n/**\n * Evaluates the motion-path position (and optional auto-orient rotation) for a\n * single segment kf[i] → kf[i+1], given local progress already remapped to\n * `[0, 1]` and eased. Builds (or reuses cached) Bezier control points\n * `P1 = P0 + tangentOut`, `P2 = P3 + tangentIn`, maps `localProgress` to arc\n * length, then to curve parameter `t` via the arc-length LUT.\n * @internal\n */\nexport function evaluateMotionPathSegment(\n prevKf: PxKeyframe,\n nextKf: PxKeyframe,\n prevPos: Point2,\n nextPos: Point2,\n localProgress: number,\n autoOrient: boolean,\n): MotionPathSample {\n const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);\n const t = seg.totalArc === 0\n ? localProgress\n : tFromArcFraction(seg.lut, localProgress);\n const point = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, t);\n if (!autoOrient) return { translate: point };\n const tan = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, t);\n const rotateDeg = Math.atan2(tan[1], tan[0]) * 180 / Math.PI;\n return { translate: point, rotateDeg };\n}\n\nfunction tFromArcFraction(lut: ArcLengthLUT, arcFrac: number): number {\n const total = lut.ds[lut.ds.length - 1];\n // Binary search on `ds` for `arcFrac * total` (mirrors bezier2D_tForDistance).\n const target = arcFrac * total;\n const { ts, ds } = lut;\n const last = ds.length - 1;\n if (target <= 0) return ts[0];\n if (target >= ds[last]) return ts[last];\n let lo = 1, hi = last;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n if (ds[mid] < target) lo = mid + 1;\n else hi = mid;\n }\n const dPrev = ds[hi - 1];\n const span = ds[hi] - dPrev;\n const frac = span > 0 ? (target - dPrev) / span : 0;\n return ts[hi - 1] + frac * (ts[hi] - ts[hi - 1]);\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Public API — materialize parametric motion-path into plain transform kfs\n// ─────────────────────────────────────────────────────────────────────────────\n\n\n/** Sampling configuration shared by `materializeMotionPathInPropAnim` + `materializeMotionPathsInTree`. @internal */\nexport interface MotionPathMaterializationOptions {\n /** Max chord-to-curve deviation per sub-interval, in user units (default 0.5). */\n flatnessTolerance?: number;\n /** Max rotation delta (in degrees) between adjacent samples when `autoOrient`\n * is set (default 5). */\n rotationTolerance?: number;\n /** Hard cap on samples per original segment; prevents runaway recursion on\n * pathological inputs (default 32). */\n maxSamplesPerSegment?: number;\n}\n\nconst DEFAULT_FLATNESS_TOL = 0.5;\nconst DEFAULT_ROTATION_TOL = 5;\nconst DEFAULT_MAX_SAMPLES = 32;\n\n\n/**\n * Converts ONE animated property to sampled transform kfs.\n *\n * Returns a NEW `PxPropertyAnimation` whose `kfs` are flat\n * `{ translate, rotate? }` records placed at curve extrema and adaptive\n * bisection points. Positions are arc-length-parametrised; easing is split via\n * De Casteljau so per-sub-segment easings reproduce the input easing exactly.\n * Original `loop` is carried across; `autoOrient` and per-kf `tangentIn` /\n * `tangentOut` are consumed.\n *\n * Returns the input unchanged (by reference) when it's not a motion-path\n * animation — callers can blindly run it through every propAnim.\n * @internal\n */\nexport function materializeMotionPathInPropAnim(\n anim: PxPropertyAnimation,\n opts?: MotionPathMaterializationOptions,\n): PxPropertyAnimation {\n if (!propAnimIsMotionPath(anim)) return anim;\n const kfs = anim.keyframes as Array<PxAnyKeyframe> | undefined;\n if (!Array.isArray(kfs) || kfs.length < 2) return anim;\n\n const autoOrient = !!anim.autoOrient;\n const flatnessTol = opts?.flatnessTolerance ?? DEFAULT_FLATNESS_TOL;\n const rotationTol = opts?.rotationTolerance ?? DEFAULT_ROTATION_TOL;\n const maxSamples = opts?.maxSamplesPerSegment ?? DEFAULT_MAX_SAMPLES;\n\n const out: Array<PxNormalizedKeyframe> = [];\n\n // First output kf — translate from input; rotate from segment-0 derivative\n // at t=0 if autoOrient. All other transform parts (`origin`, `scale`, an\n // explicit `rotate` when `autoOrient` is false, …) come from kfs[0].value.\n const firstPos = getKfTranslate(kfs[0]);\n if (!firstPos) return anim;\n const firstRotate = autoOrient ? derivAngleForFirstKf(kfs[0], kfs[1]) : undefined;\n out.push(makeOutKf(\n getKfTime(kfs[0]),\n buildOutKfValue(getKfValueParts(kfs[0]), getKfValueParts(kfs[0]), 0, firstPos, firstRotate, autoOrient),\n ));\n\n for (let i = 0; i < kfs.length - 1; i++) {\n const prevKf = kfs[i];\n const nextKf = kfs[i + 1];\n const prevPos = getKfTranslate(prevKf);\n const nextPos = getKfTranslate(nextKf);\n if (!prevPos || !nextPos) {\n // Skip undefined translate kfs — just push next as-is.\n out.push(makeOutKf(\n getKfTime(nextKf),\n buildOutKfValue(getKfValueParts(nextKf), getKfValueParts(nextKf), 1, nextPos ?? [0, 0], undefined, autoOrient),\n ));\n continue;\n }\n\n // Sharp-corner step: between adjacent segments, the prev-segment's\n // exit tangent angle (already on out[last]) can differ from this\n // segment's entry tangent angle (deriv at t=0 of the segment about to\n // start) by a lot — e.g. a rectangular path has 90° steps at each\n // corner. Linear interp from prev-exit to the FAR-END kf of this\n // segment would slide rotation through the whole segment instead of\n // stepping at the boundary. Fix: insert a duplicate kf at the\n // boundary time carrying this segment's entry angle. With `e=undef`\n // on the prior kf, the engines render an instant step boundary then\n // constant rotation through the segment.\n if (autoOrient && i > 0) {\n insertSharpCornerStepKfIfNeeded(out, prevKf, nextKf, prevPos, nextPos, rotationTol);\n }\n\n materializeSegment(out, prevKf, nextKf, prevPos, nextPos, autoOrient, flatnessTol, rotationTol, maxSamples);\n }\n\n // The very last out-kf inherits the original last kf's `easing` (which\n // applies to the NEXT segment after this animation, or nothing — but the\n // wire format preserves it regardless).\n const lastInE = getKfEasing(kfs[kfs.length - 1]);\n if (lastInE) out[out.length - 1].e = lastInE;\n\n // Unwrap autoOrient rotations so linear interp between samples doesn't\n // take the long way around the circle. atan2 returns in (-180°, 180°];\n // a tangent rotating slowly across the +X axis (e.g. -179° → +179°)\n // would otherwise be lerp'd as a ~358° spin instead of a ~2° step.\n if (autoOrient) unwrapAutoOrientRotations(out);\n\n // Output converges on `keyframes` — the `kfs` alias is gone (review §1.2/§6.1).\n const result: PxPropertyAnimation = { keyframes: out };\n if (anim.loop !== undefined) (result as { loop?: unknown }).loop = anim.loop;\n return result;\n}\n\n\n/** Walks `kfs` in order; for each kf with a `rotate` value, shifts it by ±360°\n * multiples so the delta from the previous kf's rotate stays within ±180°.\n * Linear interpolation between consecutive samples then always takes the\n * shorter arc. The accumulated shift means a continuously-rotating element\n * may end up with rotate values well outside [-180°, 180°], which is fine —\n * CSS / SVG rotation accepts any range. Exported — the glyph along-path baker\n * (`buildAnimatedAlongPath`) needs the identical seam fix for its sampled\n * per-glyph tangent rotations. */\nexport function unwrapAutoOrientRotations(kfs: Array<PxAnyKeyframe>): void {\n let prev: number | undefined;\n for (const kf of kfs) {\n const v = keyframeValue(kf) as { rotate?: number } | undefined;\n if (!v || typeof v.rotate !== 'number') continue;\n if (prev === undefined) { prev = v.rotate; continue; }\n let r = v.rotate;\n while (r - prev > 180) r -= 360;\n while (r - prev < -180) r += 360;\n v.rotate = r;\n prev = r;\n }\n}\n\n\nfunction makeOutKf(time: number, value: PxTransformParts): PxNormalizedKeyframe {\n return { t: time, v: value };\n}\n\n/** Reads the kf's value-as-parts (object form). Returns `undefined` if the kf\n * has no value or it's an array form (single-part composite). */\nfunction getKfValueParts(kf: PxAnyKeyframe): PxTransformParts | undefined {\n const v = keyframeValue(kf);\n if (!v || typeof v !== 'object' || Array.isArray(v)) return undefined;\n return v as PxTransformParts;\n}\n\n/** Linearly interpolates one transform-part value (number / PxVec2). Returns the\n * non-undefined input when only one side is present, falls back to `prev`\n * for unsupported types. */\nfunction interpolatePart(prev: unknown, next: unknown, p: number): unknown {\n if (prev === undefined) return next;\n if (next === undefined) return prev;\n if (typeof prev === 'number' && typeof next === 'number') {\n return prev + (next - prev) * p;\n }\n if (Array.isArray(prev) && Array.isArray(next) && prev.length === next.length) {\n const out: Array<number> = new Array(prev.length);\n for (let i = 0; i < prev.length; i++) {\n const a = typeof prev[i] === 'number' ? prev[i] : 0;\n const b = typeof next[i] === 'number' ? next[i] : 0;\n out[i] = a + (b - a) * p;\n }\n return out;\n }\n return p < 0.5 ? prev : next;\n}\n\n/** Builds the value-record for one sampled output kf. `translate` overrides\n * whatever the per-part interpolation would have produced (motion path is the\n * source of truth for position); `rotateDegFromAutoOrient`, when defined, is\n * ADDED on top of the (interpolated) explicit animated `rotate` — auto-orient\n * and a user-set rotation compose, matching After Effects / Lottie semantics.\n * All OTHER parts present on `prevV` / `nextV` (origin, scale, etc.) are\n * interpolated at the eased arc-progress `p` — mirroring the frames-mode\n * `calcPropertyValue` loop. */\nfunction buildOutKfValue(\n prevV: PxTransformParts | undefined,\n nextV: PxTransformParts | undefined,\n p: number,\n translate: Point2,\n rotateDegFromAutoOrient: number | undefined,\n autoOrient: boolean,\n): PxTransformParts {\n const value: { [k: string]: unknown } = { translate };\n const keys = new Set<string>();\n if (prevV) for (const k of Object.keys(prevV)) keys.add(k);\n if (nextV) for (const k of Object.keys(nextV)) keys.add(k);\n for (const k of keys) {\n if (k === 'translate') continue; // overridden by motion path\n if (k === 'rotate' && autoOrient) continue; // composed below with autoOrient\n const pv = (prevV as Record<string, unknown> | undefined)?.[k];\n const nv = (nextV as Record<string, unknown> | undefined)?.[k];\n if (pv === undefined && nv === undefined) continue;\n value[k] = interpolatePart(pv, nv, p);\n }\n if (rotateDegFromAutoOrient !== undefined) {\n // Sum the auto-orient tangent angle with the element's own animated\n // rotation (interpolated at `p`, 0 when absent) rather than discarding it.\n value.rotate = rotateDegFromAutoOrient + explicitRotateAt(prevV, nextV, p);\n }\n return value as PxTransformParts;\n}\n\n/** Interpolated explicit `rotate` from a transform-parts pair at arc-progress\n * `p`; 0 when neither side carries a numeric rotate. */\nfunction explicitRotateAt(\n prevV: PxTransformParts | undefined,\n nextV: PxTransformParts | undefined,\n p: number,\n): number {\n const pv = typeof prevV?.rotate === 'number' ? prevV.rotate : undefined;\n const nv = typeof nextV?.rotate === 'number' ? nextV.rotate : undefined;\n if (pv === undefined && nv === undefined) return 0;\n const r = interpolatePart(pv, nv, p);\n return typeof r === 'number' ? r : 0;\n}\n\n\n/** Derivative angle at t=0 of segment kf[0] → kf[1]. Used to seed the first\n * output kf's rotation; without this the very first frame would render with\n * no rotation while every subsequent sample has one. */\nfunction derivAngleForFirstKf(kf0: PxAnyKeyframe, kf1: PxAnyKeyframe): number {\n const p0 = getKfTranslate(kf0);\n const p1 = getKfTranslate(kf1);\n if (!p0 || !p1) return 0;\n const seg = getSegmentCache(kf0, kf1, p0, p1);\n const tan = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, 0);\n return Math.atan2(tan[1], tan[0]) * 180 / Math.PI;\n}\n\n\n/** Shortest signed difference of `a − b` wrapped into (-180°, 180°]. */\nfunction wrappedAngleDelta(a: number, b: number): number {\n let d = a - b;\n while (d > 180) d -= 360;\n while (d < -180) d += 360;\n return d;\n}\n\n\n/**\n * Appends a \"step\" kf to `out` at the boundary time iff the next segment's\n * entry tangent direction differs from the last emitted kf's rotation by more\n * than `rotationTol`. The new kf carries the SAME translate / origin / scale\n * as the boundary kf already in `out` (continuous in space), but a different\n * `rotate` — making engines render an instant rotation snap at the boundary\n * rather than linearly sliding rotation across the whole next segment.\n *\n * Called between `materializeSegment` calls. The boundary kf already in `out`\n * keeps its outgoing easing as undefined (no easing between the two duplicates\n * — the step is instant); the next `materializeSegment` will then attach its\n * sequential-split easing to the newly-inserted duplicate, so the per-sample\n * easing on segment `i+1` works exactly as before.\n */\nfunction insertSharpCornerStepKfIfNeeded(\n out: Array<PxNormalizedKeyframe>,\n prevKf: PxAnyKeyframe, nextKf: PxAnyKeyframe,\n prevPos: Point2, nextPos: Point2,\n rotationTol: number,\n): void {\n const lastKf = out[out.length - 1];\n const lastV = keyframeValue(lastKf) as { rotate?: number } | undefined;\n const prevExit = lastV?.rotate;\n if (typeof prevExit !== 'number') return;\n\n // `prevExit` is the SUMMED rotation (tangent + explicit). Compare pure\n // tangent-to-tangent by subtracting the boundary kf's explicit rotate,\n // which is shared by both adjoining segments at this shared keyframe.\n const boundaryV = getKfValueParts(prevKf);\n const prevExitTangent = prevExit - explicitRotateAt(boundaryV, boundaryV, 0);\n\n const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);\n const tanAtStart = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, 0);\n const nextEntry = Math.atan2(tanAtStart[1], tanAtStart[0]) * 180 / Math.PI;\n const delta = wrappedAngleDelta(nextEntry, prevExitTangent);\n if (Math.abs(delta) <= rotationTol) return; // continuous within tolerance\n\n // Step kf carrying the new entry angle, a hair AFTER the boundary — the boundary\n // time itself must render the PREVIOUS segment's exit pose. A duplicate at the exact\n // boundary time made CSS/WAAPI resolve the LATER keyframe there, so scrubbing to\n // exactly the corner showed the next segment's angle while the editor (and the\n // parametric frames engine, whose segment lookup treats a boundary as the END of the\n // segment before it) showed the previous one. The offset is far below a frame, so\n // playback still reads as an instant step at the corner.\n const prevTime = getKfTime(prevKf);\n const stepTime = Math.min(prevTime + CORNER_STEP_AFTER_BOUNDARY_MS, (prevTime + getKfTime(nextKf)) / 2);\n const dupValue = buildOutKfValue(\n getKfValueParts(prevKf), getKfValueParts(prevKf), 0,\n prevPos, nextEntry, true,\n );\n out.push(makeOutKf(stepTime, dupValue));\n}\n\n/** How far past a sharp corner the entry-angle step kf sits (ms). Small enough to be\n * invisible in playback (a frame is ~16 ms) — and strictly under the 0.1 ms step-width\n * budget the corner-step CSS spec asserts — while still a distinct WAAPI offset. */\nconst CORNER_STEP_AFTER_BOUNDARY_MS = 0.05;\n\n\n/** Materializes a single segment into `out`. Appends one kf per sample (interior\n * critical/adaptive points + the next-kf endpoint), with positions, optional\n * rotations, and split easings. */\nfunction materializeSegment(\n out: Array<PxNormalizedKeyframe>,\n prevKf: PxKeyframe, nextKf: PxKeyframe,\n prevPos: Point2, nextPos: Point2,\n autoOrient: boolean,\n flatnessTol: number, rotationTol: number, maxSamples: number,\n): void {\n const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);\n const prevTime = getKfTime(prevKf);\n const nextTime = getKfTime(nextKf);\n const prevEasing = getKfEasing(prevKf);\n const invertFn = invertEasing(prevEasing);\n const prevV = getKfValueParts(prevKf);\n const nextV = getKfValueParts(nextKf);\n\n // 1. Critical t-values: axis extrema + endpoints.\n const interiorTs = computeSampleTs(seg, autoOrient, flatnessTol, rotationTol, maxSamples);\n // `interiorTs` is the list of t in (0, 1] in ascending order, ending with t=1.\n\n // 2. For each sample t, compute position, rotation, the eased arc-fraction\n // `p` (= the value frames-mode would use for non-translate part interp),\n // and the linear-time fraction `u` (via invertEasing of `p`).\n interface Sample { u: number; p: number; pos: Point2; rotateDeg?: number; }\n const samples: Array<Sample> = [];\n for (const t of interiorTs) {\n const arc = bezier2D_arcAtT(seg.lut, t);\n const p = clamp(seg.totalArc > 0 ? arc / seg.totalArc : t, 0, 1);\n const u = clamp(invertFn(p), 0, 1);\n const pos = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, t);\n const sample: Sample = { u, p, pos };\n if (autoOrient) {\n const tan = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, t);\n sample.rotateDeg = Math.atan2(tan[1], tan[0]) * 180 / Math.PI;\n }\n samples.push(sample);\n }\n\n // 3. Sequential easing split. The easing for sub-segment k (out[L+k-1] → out[L+k])\n // is the `left` half of splitting the still-unconsumed easing at\n // `(u_k - u_{k-1}) / (1 - u_{k-1})`.\n let remaining = prevEasing;\n let prevU = 0;\n const startIdx = out.length - 1; // out[startIdx] is the prev kf — it owns the FIRST sub-easing.\n for (let i = 0; i < samples.length; i++) {\n const s = samples[i];\n const xFrac = prevU < 1 ? clamp((s.u - prevU) / (1 - prevU), 0, 1) : 1;\n const { left, right } = splitEasing(remaining, xFrac);\n\n // Assign `left` as the outgoing easing of the kf at the end of `out`\n // (which is the kf that PRECEDES this sub-kf — the prev kf for i=0,\n // or the previously-emitted sub-kf for i>0).\n const ownerIdx = i === 0 ? startIdx : out.length - 1;\n if (left) out[ownerIdx].e = left;\n else delete out[ownerIdx].e;\n\n const tGlobal = prevTime + s.u * (nextTime - prevTime);\n const value = buildOutKfValue(prevV, nextV, s.p, s.pos, s.rotateDeg, autoOrient);\n out.push(makeOutKf(tGlobal, value));\n\n remaining = right;\n prevU = s.u;\n }\n}\n\n\n/** Returns t-values in (0, 1], sorted ascending, ending with t=1. The list\n * always includes the input segment's axis extrema interior to (0, 1), plus\n * adaptive bisection samples wherever the chord deviates from the curve by\n * more than `flatnessTol` (or the rotation delta exceeds `rotationTol` for\n * autoOrient). Capped at `maxSamples`. */\nfunction computeSampleTs(\n seg: MotionPathSegmentCache, autoOrient: boolean,\n flatnessTol: number, rotationTol: number, maxSamples: number,\n): Array<number> {\n // Axis extrema (interior to (0, 1)).\n const extremes: Array<number> = [];\n addAxisExtremes(seg.P0[0], seg.P1[0], seg.P2[0], seg.P3[0], extremes);\n addAxisExtremes(seg.P0[1], seg.P1[1], seg.P2[1], seg.P3[1], extremes);\n extremes.sort((a, b) => a - b);\n\n const critical: Array<number> = [0];\n for (const t of extremes) {\n if (t > critical[critical.length - 1] + 1e-6 && t < 1 - 1e-6) {\n critical.push(t);\n }\n }\n critical.push(1);\n\n const out: Array<number> = [];\n const budget = { remaining: maxSamples - critical.length }; // already-committed critical points count against the budget\n for (let i = 0; i < critical.length - 1; i++) {\n bisect(critical[i], critical[i + 1], out, seg, autoOrient, flatnessTol, rotationTol, budget);\n }\n return out;\n}\n\n\n/** Solves `P'(t).axis = 0` for one axis (a quadratic in t). Pushes any real\n * roots in `(0, 1)` into `out`. Coefficients via standard cubic Bezier\n * derivative: with `a = p1 − p0, b = p2 − p1, c = p3 − p2`, the equation is\n * `(a − 2b + c)·t² + 2(b − a)·t + a = 0`. */\nfunction addAxisExtremes(p0: number, p1: number, p2: number, p3: number, out: Array<number>): void {\n const a = p1 - p0;\n const b = p2 - p1;\n const c = p3 - p2;\n const A = a - 2 * b + c;\n const B = 2 * (b - a);\n const C = a;\n if (Math.abs(A) < 1e-10) {\n if (Math.abs(B) > 1e-10) {\n const t = -C / B;\n if (t > 1e-6 && t < 1 - 1e-6) out.push(t);\n }\n return;\n }\n const disc = B * B - 4 * A * C;\n if (disc < 0) return;\n const sq = Math.sqrt(disc);\n const t1 = (-B - sq) / (2 * A);\n const t2 = (-B + sq) / (2 * A);\n if (t1 > 1e-6 && t1 < 1 - 1e-6) out.push(t1);\n if (t2 > 1e-6 && t2 < 1 - 1e-6) out.push(t2);\n}\n\n\n/** Adaptive bisection between `tA` and `tB`. Always appends `tB` exactly once\n * (either directly when the chord is flat enough, or via recursion).\n *\n * Flatness is tested at THREE interior points (t = 0.25, 0.5, 0.75 of the\n * sub-interval), not just the midpoint. Symmetric Bezier segments often have\n * the curve crossing the chord exactly at t=0.5 — a single-midpoint check\n * would mistake that for flatness and skip subdivision, leaving visible\n * chord deviation between samples. */\nfunction bisect(\n tA: number, tB: number,\n out: Array<number>,\n seg: MotionPathSegmentCache,\n autoOrient: boolean,\n flatnessTol: number, rotationTol: number,\n budget: { remaining: number },\n): void {\n const tMid = (tA + tB) / 2;\n const pA = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, tA);\n const pB = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, tB);\n const span = tB - tA;\n const p25 = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, tA + span * 0.25);\n const p50 = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, tMid);\n const p75 = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, tA + span * 0.75);\n\n const dev = Math.max(\n perpDist(p25, pA, pB),\n perpDist(p50, pA, pB),\n perpDist(p75, pA, pB),\n );\n let rotOk = true;\n if (autoOrient) {\n const tanA = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, tA);\n const tanB = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, tB);\n const angA = Math.atan2(tanA[1], tanA[0]) * 180 / Math.PI;\n const angB = Math.atan2(tanB[1], tanB[0]) * 180 / Math.PI;\n let delta = Math.abs(angA - angB);\n if (delta > 180) delta = 360 - delta;\n if (delta > rotationTol) rotOk = false;\n }\n\n if ((dev <= flatnessTol && rotOk) || budget.remaining <= 0 || span < 1e-6) {\n out.push(tB);\n return;\n }\n\n budget.remaining -= 1;\n bisect(tA, tMid, out, seg, autoOrient, flatnessTol, rotationTol, budget);\n bisect(tMid, tB, out, seg, autoOrient, flatnessTol, rotationTol, budget);\n}\n\n\nfunction perpDist(q: Point2, pA: Point2, pB: Point2): number {\n const dx = pB[0] - pA[0];\n const dy = pB[1] - pA[1];\n const len2 = dx * dx + dy * dy;\n if (len2 < 1e-20) {\n const qdx = q[0] - pA[0];\n const qdy = q[1] - pA[1];\n return Math.sqrt(qdx * qdx + qdy * qdy);\n }\n const cross = (q[0] - pA[0]) * dy - (q[1] - pA[1]) * dx;\n return Math.abs(cross) / Math.sqrt(len2);\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Tree walker — applies `materializeMotionPathInPropAnim` to every `node.animate.transform`\n// whose propAnim is a motion-path. Immutable: returns the input by reference\n// when no changes were needed; otherwise clones along the path to each\n// converted node and shares all other sub-trees.\n// ─────────────────────────────────────────────────────────────────────────────\n\n\n/** @internal */\nexport function materializeMotionPathsInTree(\n root: PxNode,\n opts?: MotionPathMaterializationOptions,\n): PxNode {\n const out = walkAndMaterialize(root, opts);\n return out ?? root;\n}\n\nfunction walkAndMaterialize(node: PxNode, opts?: MotionPathMaterializationOptions): PxNode | null {\n let newChildren: Array<PxNode> | undefined;\n if (node.children) {\n for (let i = 0; i < node.children.length; i++) {\n const ch = node.children[i];\n const ret = walkAndMaterialize(ch, opts);\n if (ret !== null) {\n if (!newChildren) newChildren = node.children.slice();\n newChildren[i] = ret;\n }\n }\n }\n\n let newAnimate: Record<string, PxPropertyAnimation> | undefined;\n const animBucket = node.animate;\n if (animBucket && typeof animBucket === 'object' && !Array.isArray(animBucket)) {\n const animDef = animBucket as Record<string, PxPropertyAnimation>;\n const transformAnim = animDef.transform;\n if (transformAnim && typeof transformAnim === 'object' && propAnimIsMotionPath(transformAnim)) {\n const materialized = materializeMotionPathInPropAnim(transformAnim, opts);\n if (materialized !== transformAnim) {\n newAnimate = { ...animDef, transform: materialized };\n }\n }\n }\n\n if (!newChildren && !newAnimate) return null;\n const cloned: PxNode = { ...node };\n if (newChildren) cloned.children = newChildren;\n if (newAnimate) cloned.animate = newAnimate as PxNode['animate'];\n return cloned;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { type PxAnimatedSvgDocument, type PxAnimationDefinition, type PxBezierPath, type PxNormalizedBinding, type PxBinding, type PxDefinitions, type PxElementAnimation, type PxKeyframe, type PxNormalizedKeyframe, type PxLoop, type PxNode, type PxPropertyAnimation, type PxTransformParts, keyframeTime, keyframeValue, keyframeEasing, keyframeTangentIn, keyframeTangentOut } from '../format/PxAnimatorTypes';\nimport { getBindings, getDefinitions, TRANSFORM_ATTR } from '../format/PxAnimatorConstants';\nimport { getAnimatorConfig, PxTimelineEngine, PxLoopDirection, PxLoopRepeatAt } from '../format/PxAnimatorConstants';\nimport { bezierToSvgPath, camelCaseToKebabWordIfNeeded, clamp, PX_COLOR_ATTR_NAMES, composeTransformParts, cubicBezier, interpolateBeziers, interpolateColor, interpolateNum, interpolateVec, isCamelCaseWord, parseColor, parseTransformParts, PX_PCT_BASED_ATTR_NAMES, remap, reverseEasing, splitEasing, toRGBA, PX_TRANSFORM_FN_NAMES } from '../util/PxAnimatorUtil';\nimport { evaluateMotionPathSegment, materializeMotionPathInPropAnim, propAnimIsMotionPath } from '../materialize/PxMotionPath';\n\n/**\n * Time separation between a cycle's snap-back keyframe and the previous repetition's\n * end, in ms.\n *\n * SINGLE SOURCE OF TRUTH — the editor imports this and converts to its own frame unit\n * (`TLoop.smallFrameShift = PX_LOOP_JUMP_SHIFT_MS / FRAME_DURATION_MS`), so the two sides\n * cannot drift apart and materialize different keyframes (B7).\n *\n * 1ms, not one 10ms editor frame: a 10ms snap-back is long enough to read as a visible\n * jump in a looping animation (confirmed visually). The gap only has to be non-zero — it\n * exists so the snap-back is a discontinuity rather than an interpolated tween.\n *\n * ⚠️ The editor works in FRAMES (`FRAME_DURATION_MS = 10`), so this is a sub-frame shift\n * there. `TKeyframeGroup` rounds keyframe times to integer frames in some paths; the\n * editor side keeps the fractional value and must not be re-clamped to a whole frame.\n * @internal\n */\nexport const PX_LOOP_JUMP_SHIFT_MS = 1;\n\n/** Structural equality for keyframe values (numbers, arrays, transform-part records). */\nfunction deepEqualValue(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a !== typeof b || a === null || b === null || typeof a !== 'object') return false;\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n const ka = Object.keys(a as object);\n const kb = Object.keys(b as object);\n if (ka.length !== kb.length) return false;\n return ka.every(k => deepEqualValue((a as Record<string, unknown>)[k], (b as Record<string, unknown>)[k]));\n}\n\n\n\n// ============================================================================\n// PATH PARSING: Convert \"path(...)\" strings to PxBezierPath format\n// ============================================================================\n\ninterface PathCommand {\n type: string;\n values: Array<number>;\n}\n\n/**\n * Parses an SVG path string into command tokens.\n * Supports M, L, C, Z commands (case-insensitive).\n */\nfunction parsePathCommands(d: string): Array<PathCommand> {\n const tokens = d.split(/([MLCZmlcz]|[\\s,]+)/).map(t => t.trim()).filter(t => t && t !== ',');\n\n const commands: Array<PathCommand> = [];\n let currentCommand: PathCommand | null = null;\n\n for (const token of tokens) {\n if (/[MLCZmlcz]/.test(token)) {\n currentCommand = { type: token, values: [] };\n commands.push(currentCommand);\n } else if (currentCommand) {\n const value = +token;\n currentCommand.values.push(Number.isNaN(value) ? 0 : value);\n }\n }\n\n return commands;\n}\n\n/**\n * Parses an internal SVG path string into PxBezierPath array.\n * Handles M (moveto), L (lineto), C (curveto), Z (close) commands.\n */\nexport function parseSvgPathToBezier(d: string): Array<PxBezierPath> {\n const res: Array<PxBezierPath> = [];\n let currentPath: PxBezierPath | undefined;\n\n const commands = parsePathCommands(d);\n\n for (const command of commands) {\n const type = command.type;\n const values = command.values;\n\n if (type === 'M' || type === 'm') {\n const x = values[0] || 0;\n const y = values[1] || 0;\n currentPath = {\n v: [[x, y]],\n i: [[x, y]],\n o: [[x, y]],\n c: false\n };\n res.push(currentPath);\n continue;\n }\n\n // Ensure we have a current path\n if (!currentPath) {\n currentPath = {\n v: [[0, 0]],\n i: [[0, 0]],\n o: [[0, 0]],\n c: false\n };\n res.push(currentPath);\n }\n\n if (type === 'L') {\n const x = values[0] || 0;\n const y = values[1] || 0;\n currentPath.v.push([x, y]);\n currentPath.i!.push([x, y]);\n currentPath.o!.push([x, y]);\n\n } else if (type === 'C') {\n const outX = values[0] || 0;\n const outY = values[1] || 0;\n const inX2 = values[2] || 0;\n const inY2 = values[3] || 0;\n const x2 = values[4] || 0;\n const y2 = values[5] || 0;\n\n // Update out-point of previous vertex\n currentPath.o![currentPath.o!.length - 1] = [outX, outY];\n\n // Add new vertex with its in-point\n currentPath.v.push([x2, y2]);\n currentPath.i!.push([inX2, inY2]);\n currentPath.o!.push([x2, y2]);\n\n } else if (type === 'Z' || type === 'z') {\n currentPath.c = true;\n\n } else {\n console.warn('Unsupported path command \"' + type + '\"');\n }\n }\n\n return res;\n}\n\n/**\n * Extracts SVG path data from a string.\n * Handles both \"path(M...)\" wrapper format and raw \"M...\" format.\n * @returns The path data string, or undefined if not a valid path string\n */\nfunction extractPathData(str: string): string | undefined {\n if (str.startsWith('path(') && str.endsWith(')')) {\n return str.slice(5, -1); // Remove \"path(\" and \")\"\n }\n // Raw path string starting with a path command (M, m, or other commands)\n if (/^[MmZzLlHhVvCcSsQqTtAa]/.test(str)) {\n return str;\n }\n return undefined;\n}\n\n/**\n * Checks if the value is a path string (either \"path(...)\" or raw \"M...\").\n */\nfunction isPathString(value: any): value is string {\n return typeof value === 'string' && extractPathData(value) !== undefined;\n}\n\n/**\n * Normalizes a 'd' attribute value to { paths: PxBezierPath[] } format.\n * Handles:\n * - { pathData: \"M...\" } / { pathData: \"path(...)\" } -> { paths: [PxBezierPath] } (THE wire form)\n * - { paths: [\"path(...)\"] } -> { paths: [PxBezierPath] }\n * - { paths: [\"M...\"] } -> { paths: [PxBezierPath] }\n * - [\"path(...)\"] -> { paths: [PxBezierPath] }\n * - [\"M...\"] -> { paths: [PxBezierPath] }\n * - \"path(...)\" -> { paths: [PxBezierPath] }\n * - \"M...\" -> { paths: [PxBezierPath] }\n * - { paths: [PxBezierPath] } -> as-is\n */\nfunction normalizePathValue(value: any): { paths: Array<PxBezierPath> } | any {\n // THE wire form: { pathData: \"M...\" } (compound = multiple `M…` sub-paths in one string).\n // The `{ paths: [...] }` shapes below are this function's OWN output, not wire spellings.\n if (value && typeof value === 'object' && typeof value.pathData === 'string') {\n const d = extractPathData(value.pathData);\n return d ? { paths: parseSvgPathToBezier(d) } : value;\n }\n\n // If already in { paths: [...] } format\n if (value && typeof value === 'object' && 'paths' in value) {\n const pathsArray = value.paths;\n if (Array.isArray(pathsArray) && pathsArray.length > 0) {\n // Check if paths contain path strings that need parsing\n if (isPathString(pathsArray[0])) {\n const paths: Array<PxBezierPath> = [];\n for (const pathStr of pathsArray) {\n const d = extractPathData(pathStr);\n if (d) {\n paths.push(...parseSvgPathToBezier(d));\n }\n }\n return { paths };\n }\n }\n // Already in correct format\n return value;\n }\n\n // If it's an array of path strings\n if (Array.isArray(value)) {\n if (value.length > 0 && isPathString(value[0])) {\n const paths: Array<PxBezierPath> = [];\n for (const pathStr of value) {\n const d = extractPathData(pathStr);\n if (d) {\n paths.push(...parseSvgPathToBezier(d));\n }\n }\n return { paths };\n }\n // Already an array of PxBezierPath - wrap in { paths: }\n return { paths: value };\n }\n\n // If it's a single path string\n if (isPathString(value)) {\n const d = extractPathData(value)!;\n return { paths: parseSvgPathToBezier(d) };\n }\n\n return value;\n}\n\n\n// ============================================================================\n// NORMALIZATION: Convert new API format to internal normalized format\n// ============================================================================\n\n/**\n * Resolves an easing reference to a cubic-bezier array.\n * @param easing The easing reference (string name or bezier array)\n * @param defs The definitions containing named easings\n * @returns The resolved cubic-bezier array or undefined\n */\nfunction resolveEasing(\n easing: string | [number, number, number, number] | undefined,\n defs?: PxDefinitions\n): [number, number, number, number] | undefined {\n if (!easing) return undefined;\n\n if (Array.isArray(easing)) {\n return easing;\n }\n\n // Look up named easing in defs\n if (defs?.easings?.[easing]) {\n return defs.easings[easing];\n }\n\n // Unknown easing name - return undefined\n console.warn('Unknown easing name: ' + easing);\n return undefined;\n}\n\n/**\n * Resolves an animation reference to an AnimationDefinition.\n * @param animRef The animation reference (string name or inline definition)\n * @param defs The definitions containing named animations\n * @returns The resolved animation definition\n */\nfunction resolveAnimation(\n animRef: string | PxAnimationDefinition,\n defs?: PxDefinitions\n): PxAnimationDefinition | undefined {\n if (typeof animRef === 'string') {\n // Look up named animation in defs\n const resolved = defs?.animations?.[animRef];\n if (!resolved) {\n console.warn('Unknown animation name: ' + animRef);\n }\n return resolved;\n }\n\n // It's an inline definition\n return animRef;\n}\n\n/**\n * Resolves an element animation (which can be string, array, or inline) to an array of AnimationDefinitions.\n * @param animate The element animation specification\n * @param defs The definitions containing named animations\n * @returns Array of resolved animation definitions\n */\nfunction resolveElementAnimation(\n animate: PxElementAnimation | undefined,\n defs?: PxDefinitions\n): PxAnimationDefinition[] {\n if (!animate) return [];\n\n const results: PxAnimationDefinition[] = [];\n\n if (typeof animate === 'string') {\n const resolved = resolveAnimation(animate, defs);\n if (resolved) results.push(resolved);\n } else if (Array.isArray(animate)) {\n for (const item of animate) {\n const resolved = resolveAnimation(item, defs);\n if (resolved) results.push(resolved);\n }\n } else {\n // It's an inline AnimationDefinition\n results.push(animate);\n }\n\n return results;\n}\n\n// ============================================================================\n// LOOP EXPANSION: Duplicate keyframe segments to fill gaps in the timeline\n// ============================================================================\n\n/**\n * Interpolates between two keyframe values based on property type.\n * Returns the raw interpolated value (not a CSS string).\n *\n * Dispatch order matters — the unified-transform-record branch must run\n * BEFORE the standalone-`rotate` branch, otherwise a `transform`-named\n * animation whose kfs are number-typed (rare but valid) would be routed\n * through the vec path.\n * @internal\n */\nexport function interpolateValue(propName: string, a: any, b: any, t: number): any {\n if (propName === 'd') {\n const aPaths = a?.paths ?? (Array.isArray(a) ? a : []);\n const bPaths = b?.paths ?? (Array.isArray(b) ? b : []);\n return { paths: interpolateBeziers(aPaths, bPaths, t) };\n }\n if (PX_COLOR_ATTR_NAMES.has(propName)) {\n return interpolateColor(a || [0, 0, 0, 1], b || [0, 0, 0, 1], t);\n }\n // Unified-transform record (`{translate, rotate, scale, origin}`) — the\n // most common animated `propName === 'transform'` shape. Per-part interp\n // so `+({}) = NaN` (the old fall-through) doesn't poison the boundary kf\n // at a loop seam.\n if (propName === 'transform'\n && typeof a === 'object' && a !== null && !Array.isArray(a)\n && typeof b === 'object' && b !== null && !Array.isArray(b)\n ) {\n return interpolateTransformParts(a as PxTransformParts, b as PxTransformParts, t);\n }\n // Standalone `rotate` as a propAnim — value is a NUMBER, not a vector.\n // The general PX_TRANSFORM_FN_NAMES branch below would route it through\n // `interpolateVec`, which on a scalar returns `[]` (length NaN → no loop)\n // — wrong CSS output.\n if (propName === 'rotate' && typeof a === 'number' && typeof b === 'number') {\n return interpolateNum(a, b, t);\n }\n if (PX_TRANSFORM_FN_NAMES.has(propName) || propName === 'stroke-dasharray' || propName === 'strokeDasharray') {\n return interpolateVec(a || [], b || [], t);\n }\n return interpolateNum(+(a || 0), +(b || 0), t);\n}\n\n/** Per-part interpolation for a unified-transform parts record. Mirrors the\n * inline logic in `calcPropertyValue`'s transform branch. */\nfunction interpolateTransformParts(a: PxTransformParts, b: PxTransformParts, t: number): PxTransformParts {\n const keys = new Set<string>([...Object.keys(a ?? {}), ...Object.keys(b ?? {})]);\n const out: { [k: string]: unknown } = {};\n for (const k of keys) {\n const av = (a as { [k: string]: unknown })?.[k];\n const bv = (b as { [k: string]: unknown })?.[k];\n if (k === 'rotate' || k === 'skew') {\n out[k] = interpolateNum(+(av ?? 0), +(bv ?? 0), t);\n } else if (k === 'translate' || k === 'scale' || k === 'origin') {\n const fallback: Array<number> = k === 'scale' ? [1, 1] : [0, 0];\n out[k] = interpolateVec((av as Array<number>) || fallback, (bv as Array<number>) || fallback, t);\n } else {\n // Unknown part — prefer next if present, otherwise prev.\n out[k] = bv ?? av;\n }\n }\n return out as PxTransformParts;\n}\n\ninterface LoopTemplateEntry {\n relT: number; // 0..1 relative position within segment\n v: any;\n e: [number, number, number, number] | undefined;\n // Per-vertex spatial tangents (motion-along-path). Carried so repeated\n // segments keep their curvature; reversed reps swap in<->out (see appendRep).\n tangentIn?: [number, number];\n tangentOut?: [number, number];\n}\n\n/**\n * Expands keyframes by repeating a segment to fill the gap between the keyframe\n * range and the global animation duration, implementing PxLoop \"local loop\" behavior.\n */\nfunction expandLoopKeyframes(\n propName: string,\n keyframes: PxNormalizedKeyframe[],\n loop: PxLoop,\n duration: number\n): PxNormalizedKeyframe[] {\n const totalIntervals = keyframes.length - 1;\n const segCount = clamp(loop.segmentCount ?? totalIntervals, 1, totalIntervals);\n\n // Extract segment keyframes\n let segKfs: PxNormalizedKeyframe[];\n if (loop.repeatAt === PxLoopRepeatAt.start) {\n segKfs = keyframes.slice(0, segCount + 1);\n } else {\n segKfs = keyframes.slice(totalIntervals - segCount);\n }\n\n // Determine fill region\n const firstT = keyframes[0].t ?? 0;\n const lastT = keyframes[keyframes.length - 1].t ?? 0;\n\n let fillStart: number, fillEnd: number;\n if (loop.repeatAt === PxLoopRepeatAt.start) {\n fillStart = 0;\n fillEnd = firstT;\n } else {\n fillStart = lastT;\n fillEnd = duration;\n }\n\n const fillDuration = fillEnd - fillStart;\n if (fillDuration <= 0) return keyframes;\n\n // Segment timing\n const segStartT = segKfs[0].t ?? 0;\n const segEndT = segKfs[segKfs.length - 1].t ?? 0;\n const segDuration = segEndT - segStartT;\n if (segDuration <= 0) return keyframes;\n\n // Build template with relative offsets (0..1)\n const template: LoopTemplateEntry[] = segKfs.map(kf => ({\n relT: (kf.t! - segStartT) / segDuration,\n v: kf.v,\n e: kf.e as [number, number, number, number] | undefined,\n tangentIn: keyframeTangentIn(kf) as [number, number] | undefined,\n tangentOut: keyframeTangentOut(kf) as [number, number] | undefined\n }));\n\n const fullReps = Math.floor(fillDuration / segDuration);\n const remainder = fillDuration - fullReps * segDuration;\n const partialFraction = remainder / segDuration;\n\n const looped: PxNormalizedKeyframe[] = [];\n\n // A cycle's first keyframe lands at the SAME time as the previous repetition's\n // last one. Emitting both at that time makes the value at that instant depend on\n // the sampler's tie-break, and left the player disagreeing with the editor (B7).\n // Mirror `TLoop.toKeyframes` exactly:\n // - values EQUAL (pingpong turn, closed loop) → the duplicate says nothing, skip it;\n // - values DIFFER (a real cycle snap) → separate them by PX_LOOP_JUMP_SHIFT_MS.\n // The editor's shift is one 10ms frame (`smallFrameShift`), so the two sides\n // materialize identical keyframes. Anything smaller is blocked editor-side: a\n // fractional-frame shift was tried there and reverted (TKeyframeGroup mishandles it).\n // SCOPE: loopOut (`after`) only — see the note above. For loopIn the pair sits in\n // the opposite order in the array, so separating it means moving the EARLIER\n // keyframe earlier; done naively it inverts keyframe order and re-breaks the\n // loopIn `f0` regression (`appendRepTail`). Left as-is until it has its own\n // editor-CSS evidence; the two sides may still differ at a loopIn boundary.\n const separateBoundary = loop.repeatAt !== PxLoopRepeatAt.start;\n // The keyframe the FIRST repetition butts up against: loopOut tiles forward from\n // the last original keyframe (the originals are concatenated only at assembly).\n const originalTerminalKf: PxNormalizedKeyframe | undefined = keyframes[keyframes.length - 1];\n\n // Easing that a skipped boundary keyframe hands to the ORIGINAL terminal keyframe.\n // Easing describes the interval that FOLLOWS a keyframe, so when a pingpong turn's\n // duplicate is dropped (same value, nothing to say about position) its easing is NOT\n // redundant — it owns the return leg. Without this hand-off the return leg renders\n // LINEAR while the outbound is eased. The originals belong to the caller, so it is\n // applied by replacing the terminal with a copy at assembly, never by mutation.\n let terminalEasingOverride: PxNormalizedKeyframe['e'] | undefined;\n let hasTerminalEasingOverride = false;\n\n // Helper: append one full or partial repetition\n function appendRep(repStart: number, isReversed: boolean, partial?: number) {\n let entries: LoopTemplateEntry[];\n if (isReversed) {\n // Reverse keyframe order and reverse easings\n entries = [];\n for (let i = template.length - 1; i >= 0; i--) {\n entries.push({\n relT: 1 - template[i].relT,\n v: template[i].v,\n // Easing for reversed transition: use reversed easing from the forward \"from\" keyframe\n e: i > 0 ? reverseEasing(template[i - 1].e) : undefined,\n // Reversed traversal swaps each vertex's in/out spatial tangents\n // (geometry is identical, walked backwards), so curvature and\n // auto-orientation survive the reversed rep.\n tangentIn: template[i].tangentOut,\n tangentOut: template[i].tangentIn\n });\n }\n } else {\n entries = template;\n }\n\n const cutRelT = partial !== undefined ? partial : 1;\n\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n if (entry.relT > cutRelT + 1e-9) {\n // Past the cut point — insert interpolated keyframe\n const prev = entries[i - 1];\n const intervalSpan = entry.relT - prev.relT;\n const localFrac = (cutRelT - prev.relT) / intervalSpan;\n\n // Apply easing to get the eased progress for value interpolation\n const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;\n const cutValue = interpolateValue(propName, prev.v, entry.v, easedFrac);\n\n // Split easing — use left portion for the truncated interval\n const { left: leftEasing } = splitEasing(prev.e, localFrac);\n\n // Update previous keyframe's easing to the left portion\n if (looped.length > 0 && prev.relT <= cutRelT) {\n looped[looped.length - 1].e = leftEasing;\n }\n\n looped.push({ t: repStart + cutRelT * segDuration, v: cutValue, e: undefined });\n return;\n }\n\n // Repetition boundary: this entry coincides in time with whatever precedes\n // it. For the FIRST rep that neighbour is the last ORIGINAL keyframe (the\n // originals are concatenated only at assembly time), not a `looped` entry.\n const prevKf = looped.length > 0 ? looped[looped.length - 1] : originalTerminalKf;\n const isBoundary = separateBoundary && i === 0 && prevKf !== undefined\n && Math.abs((prevKf.t ?? 0) - (repStart + entry.relT * segDuration)) < 1e-9;\n if (isBoundary) {\n if (deepEqualValue(prevKf.v, entry.v)) {\n // Pingpong turn — the duplicate says nothing about VALUE, but it carries\n // the easing (and out-tangent) for the interval that follows it. Hand\n // those to the surviving neighbour instead of discarding them.\n if (looped.length > 0) {\n prevKf.e = entry.e;\n prevKf.tangentOut = entry.tangentOut;\n } else {\n terminalEasingOverride = entry.e;\n hasTerminalEasingOverride = true;\n }\n continue;\n }\n // Real snap: the jump segment must not carry motion-path tangents.\n // Only ours are safe to mutate — never the caller's originals.\n if (looped.length > 0) { delete prevKf.tangentIn; delete prevKf.tangentOut; }\n }\n\n const pushed: PxNormalizedKeyframe = {\n t: repStart + entry.relT * segDuration + (isBoundary ? PX_LOOP_JUMP_SHIFT_MS : 0),\n v: entry.v,\n e: i < entries.length - 1 ? entry.e : undefined\n };\n // Carry per-vertex spatial tangents so the repeated segment keeps its\n // motion-path curvature (reversed reps already have in/out swapped above).\n if (entry.tangentIn) pushed.tangentIn = entry.tangentIn;\n if (entry.tangentOut) pushed.tangentOut = entry.tangentOut;\n looped.push(pushed);\n }\n }\n\n // Like {@link appendRep} but emits only the TAIL of the segment — relT ∈\n // [1-tailFraction, 1] — over [repStart, repStart + tailFraction*segDuration].\n // Used for the leftover (partial) rep of a `before` loop: it sits at fillStart\n // and runs UP TO the segment-end value, so the full reps after it align to end\n // exactly at the first original keyframe (firstT). A head-truncated partial here\n // (as appendRep does) would land the leftover ADJACENT to firstT and desync the\n // whole backward fill — the loopIn `f0` bug.\n function appendRepTail(repStart: number, isReversed: boolean, tailFraction: number) {\n let entries: LoopTemplateEntry[];\n if (isReversed) {\n entries = [];\n for (let i = template.length - 1; i >= 0; i--) {\n entries.push({\n relT: 1 - template[i].relT,\n v: template[i].v,\n e: i > 0 ? reverseEasing(template[i - 1].e) : undefined,\n tangentIn: template[i].tangentOut,\n tangentOut: template[i].tangentIn\n });\n }\n } else {\n entries = template;\n }\n\n const startRelT = 1 - tailFraction;\n\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n if (entry.relT < startRelT - 1e-9) continue; // wholly before the tail window\n const prev = entries[i - 1];\n\n // If startRelT falls strictly inside (prev, entry), the tail begins\n // mid-interval — emit an interpolated keyframe at repStart carrying the\n // RIGHT split of prev's easing.\n if (prev && prev.relT < startRelT - 1e-9 && entry.relT > startRelT + 1e-9) {\n const intervalSpan = entry.relT - prev.relT;\n const localFrac = (startRelT - prev.relT) / intervalSpan;\n const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;\n const startValue = interpolateValue(propName, prev.v, entry.v, easedFrac);\n const { right: rightEasing } = splitEasing(prev.e, localFrac);\n looped.push({ t: repStart, v: startValue, e: rightEasing });\n }\n\n const pushed: PxNormalizedKeyframe = {\n t: repStart + (entry.relT - startRelT) * segDuration,\n v: entry.v,\n e: i < entries.length - 1 ? entry.e : undefined\n };\n if (entry.tangentIn) pushed.tangentIn = entry.tangentIn;\n if (entry.tangentOut) pushed.tangentOut = entry.tangentOut;\n looped.push(pushed);\n }\n }\n\n // Generate repetitions.\n // The rep closest to the original keyframes boundary must be reversed first\n // in pingpong mode (the animation just finished going forward, so the next\n // iteration goes backward).\n if (loop.repeatAt === PxLoopRepeatAt.start) {\n // loopIn — the fill must END exactly at the first keyframe (firstT), so the\n // reps tile BACKWARD from that boundary: the leftover (partial) rep sits at\n // fillStart showing the segment's tail, then `fullReps` full reps run up to\n // firstT. (Forward-tiling — as loopOut does — would push the partial next to\n // firstT and desync the fill: the loopIn `f0` regression.)\n if (partialFraction > 1e-9) {\n const isReversed = loop.direction === PxLoopDirection.alternate && (fullReps % 2 === 0);\n appendRepTail(fillStart, isReversed, partialFraction);\n }\n for (let rep = 0; rep < fullReps; rep++) {\n const distFromBoundary = fullReps - 1 - rep;\n const isReversed = loop.direction === PxLoopDirection.alternate && (distFromBoundary % 2 === 0);\n const repStart = fillStart + remainder + rep * segDuration;\n appendRep(repStart, isReversed);\n }\n } else {\n // loopOut — boundary is at fillStart (lastT); tile forward, partial at the end.\n for (let rep = 0; rep < fullReps; rep++) {\n const isReversed = loop.direction === PxLoopDirection.alternate && (rep % 2 === 0);\n const repStart = fillStart + rep * segDuration;\n appendRep(repStart, isReversed);\n }\n if (partialFraction > 1e-9) {\n const isReversed = loop.direction === PxLoopDirection.alternate && (fullReps % 2 === 0);\n const repStart = fillStart + fullReps * segDuration;\n appendRep(repStart, isReversed, partialFraction);\n }\n }\n\n // Assemble: looped keyframes go before or after the original keyframes.\n // No junction deduplication — cycle mode relies on value jumps at boundaries.\n if (loop.repeatAt === PxLoopRepeatAt.start) {\n return [...looped, ...keyframes];\n } else {\n if (hasTerminalEasingOverride && keyframes.length > 0) {\n const head = keyframes.slice(0, -1);\n const tail = { ...keyframes[keyframes.length - 1], e: terminalEasingOverride };\n return [...head, tail, ...looped];\n }\n return [...keyframes, ...looped];\n }\n}\n\n\n// ============================================================================\n// KEYFRAME NORMALIZATION\n// ============================================================================\n\n/**\n * Normalizes keyframes from the new API format (time in ms) to internal format (time as 0-1 fraction).\n * Resolves easing references, normalizes times, and converts path strings for 'd' attribute.\n * If a loop configuration is present, expands the keyframes to fill the global duration.\n * @param propName The property name (e.g., 'd' for path)\n * @param propAnim The property animation with keyframes\n * @param duration The total animation duration in ms\n * @param defs The definitions containing named easings\n * @returns Array of normalized keyframes (same structure, resolved refs, normalized times)\n */\nfunction normalizeKeyframes(\n propName: string,\n propAnim: PxPropertyAnimation,\n duration: number,\n defs?: PxDefinitions\n): PxNormalizedKeyframe[] {\n const keyframes = propAnim.keyframes || [];\n\n const normalized: PxNormalizedKeyframe[] = [];\n\n for (const kf of keyframes) {\n const timePct = keyframeTime(kf);\n let value = keyframeValue(kf);\n const easing = keyframeEasing(kf);\n\n // Normalize path values for 'd' attribute\n if (propName === 'd') {\n value = normalizePathValue(value);\n }\n\n // Normalize color values (hex/rgb/rgba strings to [0-1] vectors).\n // `PX_COLOR_ATTR_NAMES` is keyed in kebab-case (`stop-color`, `flood-color`,\n // `lighting-color`); the wire format uses both kebab AND camelCase\n // (`stopColor`) for these props. Without converting, frames-mode would\n // call `interpolateColor` on the raw strings and produce `NaN` channels\n // — the visible \"rgba(NaN,NaN,NaN,…)\" bug on stop-color animations.\n const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;\n if (PX_COLOR_ATTR_NAMES.has(propNameKebab)) {\n value = parseColor(value) ?? value;\n }\n\n const normKf: PxNormalizedKeyframe = {\n t: timePct,\n v: value,\n e: resolveEasing(easing, defs)\n };\n // Motion-along-path: keep the spatial tangents so the downstream\n // `materializeMotionPathInPropAnim` (called in `normalizeAnimationDefinition`) can\n // sample them into transform kfs. Short aliases `ti` / `to` collapse\n // into their canonical names.\n const tIn = keyframeTangentIn(kf);\n const tOut = keyframeTangentOut(kf);\n if (tIn) normKf.tangentIn = tIn;\n if (tOut) normKf.tangentOut = tOut;\n\n normalized.push(normKf);\n }\n\n // Sort by time\n normalized.sort((a, b) => (a.t ?? 0) - (b.t ?? 0));\n\n // Expand loop if configured (loop:true is shorthand for default PxLoop)\n const loopRaw = propAnim.loop;\n const loop: PxLoop | undefined = loopRaw === true ? {} : loopRaw || undefined;\n if (loop && normalized.length >= 2) {\n return expandLoopKeyframes(propName, normalized, loop, duration);\n }\n\n return normalized;\n}\n\n/**\n * Merges multiple animation definitions into a single combined definition.\n * Later definitions override earlier ones for the same property.\n */\nfunction mergeAnimationDefinitions(\n animations: PxAnimationDefinition[]\n): PxAnimationDefinition {\n const merged: PxAnimationDefinition = {};\n\n for (const anim of animations) {\n for (const [prop, propAnim] of Object.entries(anim)) {\n merged[prop] = propAnim;\n }\n }\n\n return merged;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Public loop-materialization API\n//\n// Used internally by `normalizeKeyframes` (where `expandLoopKeyframes` is\n// already called at the tail of normalization). Exposed here at the propAnim\n// and tree levels so the Editor (or any external caller) can compose:\n// root = materializeNodeEffects(root).root;\n// root = materializeInternalLoopsInTree(root, duration);\n// root = materializeMotionPathsInTree(root);\n// to produce a fully-flat document with no `loop`, no `effects`, no tangents.\n// ─────────────────────────────────────────────────────────────────────────────\n\n\n/**\n * Replaces `propAnim.loop` with explicit repeated keyframes via\n * `expandLoopKeyframes`. Returns the input by reference when no loop is\n * configured (no-op). Output drops the `loop` field (consumed).\n * @internal\n */\nexport function materializeInternalLoopsInPropAnim(\n propName: string,\n propAnim: PxPropertyAnimation,\n duration: number,\n): PxPropertyAnimation {\n const loopRaw = propAnim.loop;\n if (loopRaw === undefined || loopRaw === null || loopRaw === false) return propAnim;\n const loop: PxLoop = loopRaw === true ? {} : (loopRaw as PxLoop);\n const rawKfs = propAnim.keyframes as PxKeyframe[] | undefined;\n if (!Array.isArray(rawKfs) || rawKfs.length < 2) return propAnim;\n\n // `expandLoopKeyframes` reads kf.t / kf.v / kf.e (short form). When this\n // function is called from the materialization pipeline OUTSIDE the\n // binding-normalization path (e.g. by `materializeAllInTree`), the input\n // kfs may still be in long form (`time` / `value` / `easing`) AND the\n // values may be unparsed (hex color strings, raw path-`d`). Normalize\n // both here so `interpolateValue` (called by `expandLoopKeyframes` at the\n // loop seam) sees structured data — without this, a color boundary kf\n // ends up `[NaN,NaN,NaN,NaN]` and the bug stays visible until the\n // animation cycle restarts.\n const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;\n const isColor = PX_COLOR_ATTR_NAMES.has(propNameKebab);\n const kfs: PxNormalizedKeyframe[] = rawKfs.map(kf => {\n const t = keyframeTime(kf);\n let v: unknown = keyframeValue(kf);\n if (propName === 'd') v = normalizePathValue(v);\n if (isColor) v = parseColor(v) ?? v;\n const e = keyframeEasing(kf);\n const out: PxNormalizedKeyframe = { t, v, e };\n const tIn = keyframeTangentIn(kf);\n const tOut = keyframeTangentOut(kf);\n if (tIn) out.tangentIn = tIn;\n if (tOut) out.tangentOut = tOut;\n return out;\n });\n\n const expanded = expandLoopKeyframes(propName, kfs, loop, duration);\n const out: PxPropertyAnimation = { keyframes: expanded };\n if (propAnim.autoOrient !== undefined) (out as { autoOrient?: unknown }).autoOrient = propAnim.autoOrient;\n return out;\n}\n\n\n/**\n * Walks `root` and materializes every animated property's `loop` via\n * `materializeInternalLoopsInPropAnim`. Immutable — returns the input by\n * reference when no loop was found anywhere; otherwise clones along the path\n * to each affected node, sharing untouched sub-trees.\n * @internal\n */\nexport function materializeInternalLoopsInTree(\n root: PxNode,\n duration: number,\n): PxNode {\n const ret = walkAndMaterializeLoops(root, duration);\n return ret ?? root;\n}\n\nfunction walkAndMaterializeLoops(node: PxNode, duration: number): PxNode | null {\n let newChildren: Array<PxNode> | undefined;\n if (node.children) {\n for (let i = 0; i < node.children.length; i++) {\n const ret = walkAndMaterializeLoops(node.children[i], duration);\n if (ret !== null) {\n if (!newChildren) newChildren = node.children.slice();\n newChildren[i] = ret;\n }\n }\n }\n let newAnimate: Record<string, PxPropertyAnimation> | undefined;\n const animBucket = node.animate;\n if (animBucket && typeof animBucket === 'object' && !Array.isArray(animBucket)) {\n const animDef = animBucket as Record<string, PxPropertyAnimation>;\n for (const propName of Object.keys(animDef)) {\n const propAnim = animDef[propName];\n const materialized = materializeInternalLoopsInPropAnim(propName, propAnim, duration);\n if (materialized !== propAnim) {\n if (!newAnimate) newAnimate = { ...animDef };\n newAnimate[propName] = materialized;\n }\n }\n }\n if (!newChildren && !newAnimate) return null;\n const cloned: PxNode = { ...node };\n if (newChildren) cloned.children = newChildren;\n if (newAnimate) cloned.animate = newAnimate as PxNode['animate'];\n return cloned;\n}\n\n\n/**\n * Generates a unique element ID for internal tracking during DOM rendering.\n */\nlet _elementIdCounter = 0;\nexport function generateElementId(): string {\n return '_px_el_' + (++_elementIdCounter);\n}\n\n/**\n * Resets the element ID counter (useful for testing).\n */\nexport function resetElementIdCounter(): void {\n _elementIdCounter = 0;\n}\n\n/**\n * TRANSFORM PRECEDENCE (review §0.4/§1.6) — CSS's own composition rule, applied at READ:\n * a static `transform` on the element composes UNDER the animated transform, instead of\n * being silently clobbered by it. Implemented as a keyframe-value MERGE during\n * normalization, so both engines (and every consumer downstream) see complete parts:\n *\n * 1. `animate.transform` with PARTIAL parts records — every keyframe value (and the\n * base `value`) inherits the static parts it does not set:\n * static `{rotate: 45}` + kf `{translate: [80, 0]}` → kf `{rotate: 45, translate: [80, 0]}`.\n * 2. ONE individual channel (`translate` / `rotate` / `scale` / `skew`) and no\n * `transform` channel — the channel is REWRITTEN as a unified `transform` channel\n * whose values carry the static parts: the rect stays rotated 45° AND slides.\n *\n * The static transform may be a parts record or an attribute string (parsed by the\n * conservative {@link parseTransformParts} — unparseable strings skip the merge).\n * NOT merged (documented limitations): several individual channels animated at once\n * (they still last-write-wins against each other), and an individual channel next to an\n * animated `transform` (the `transform` channel wins, as before).\n * @internal\n */\nexport function mergeStaticTransformIntoAnimDef(\n animDef: PxAnimationDefinition,\n staticTransform: unknown,\n): PxAnimationDefinition {\n if (!animDef) return animDef;\n const staticParts: PxTransformParts | undefined =\n staticTransform && typeof staticTransform === 'object' && !Array.isArray(staticTransform)\n ? staticTransform as PxTransformParts\n : parseTransformParts(staticTransform as string);\n if (!staticParts || !Object.keys(staticParts).length) return animDef;\n\n const mergeKfValue = (v: unknown): unknown =>\n v && typeof v === 'object' && !Array.isArray(v) ? { ...staticParts, ...(v as PxTransformParts) } : v;\n\n const transformAnim = animDef[TRANSFORM_ATTR];\n if (transformAnim && typeof transformAnim === 'object') {\n const anim = transformAnim as PxPropertyAnimation;\n if (Array.isArray(anim.keyframes)) {\n const out: PxPropertyAnimation = {\n ...anim,\n keyframes: anim.keyframes.map(kf => ({ ...kf, value: mergeKfValue(kf.value) })),\n };\n if (out.value !== undefined) out.value = mergeKfValue(out.value) as PxPropertyAnimation['value'];\n return { ...animDef, transform: out };\n }\n return animDef;\n }\n\n const channels = Object.keys(animDef).filter(k => PX_TRANSFORM_FN_NAMES.has(k));\n if (channels.length !== 1) return animDef; // several channels: unchanged (documented)\n const ch = channels[0];\n const chAnim = animDef[ch] as PxPropertyAnimation;\n if (!chAnim || typeof chAnim !== 'object' || !Array.isArray(chAnim.keyframes)) return animDef;\n const lifted: PxPropertyAnimation = {\n ...chAnim,\n keyframes: chAnim.keyframes.map(kf => ({ ...kf, value: { ...staticParts, [ch]: kf.value } })),\n };\n if (lifted.value !== undefined) lifted.value = { ...staticParts, [ch]: lifted.value };\n const rest: PxAnimationDefinition = { ...animDef };\n delete rest[ch];\n return { ...rest, transform: lifted };\n}\n\n/**\n * Normalizes an animation definition by resolving easing references and normalizing keyframe times.\n * Keeps the key/value mapping structure. `engine` controls motion-along-path\n * handling — see {@link PxTimelineEngine}.\n */\nfunction normalizeAnimationDefinition(\n animDef: PxAnimationDefinition,\n duration: number,\n defs?: PxDefinitions,\n engine: PxTimelineEngine = PxTimelineEngine.native,\n): PxAnimationDefinition {\n const normalized: PxAnimationDefinition = {};\n\n for (const [propName, propAnim] of Object.entries(animDef)) {\n // `alongPathMode: 'offsetPath'` — this transform's motion is rendered by CSS\n // Motion Path (`offset-path` style on the element + an `offsetDistance` track in\n // the same dict). The tangented keyframes stay on the wire as the DESIGN source\n // (the editor round-trip reads them back), but the player must not ALSO drive\n // them: doing both moved the element to the path position and then translated it\n // again (double position). Skip the binding; `offsetDistance` owns the motion.\n if (propName === 'transform'\n && (propAnim as { alongPathMode?: string }).alongPathMode === 'offsetPath'\n // …but ONLY when the offset infrastructure actually exists (an `offsetDistance`\n // track in the same definition — the pre-rendered dict shape, or a lightweight\n // doc the offset materializer rewrote). A marked transform the materializer\n // BAILED on (e.g. rotate animated in the same keyframes — inexpressible as\n // offset-path) must fall through to the ordinary sampled pipeline; skipping it\n // unconditionally froze those elements at their base pose.\n && (animDef as Record<string, unknown>)['offsetDistance'] !== undefined) {\n continue;\n }\n const normalizedKfs = normalizeKeyframes(propName, propAnim, duration, defs);\n if (normalizedKfs.length > 0) {\n // Internal normalized form converges on `keyframes` too — the `kfs` alias\n // is gone from the format AND the runtime (review §1.2/§6.1).\n const out: PxPropertyAnimation = { keyframes: normalizedKfs };\n // Carry top-level animation flags through normalization — `materializeMotionPathInPropAnim`\n // and the runtime evaluators need `autoOrient` / `loop` to be present\n // alongside the kfs.\n if (propAnim.autoOrient !== undefined) out.autoOrient = propAnim.autoOrient;\n if (propAnim.loop !== undefined) out.loop = propAnim.loop;\n\n // Pipeline: loops are already expanded by `normalizeKeyframes` above.\n // For the `waapi` engine ONLY, materialize motion-along-path\n // (tangented `transform` kfs + autoOrient) into plain sampled\n // `{ translate, rotate? }` kfs — CSS WAAPI can't evaluate parametric\n // tangents at runtime. Frames-mode keeps the parametric form so the\n // frame-loop kernel `evaluateMotionPathSegment` can sample per frame\n // (better spatial fidelity than any finite sampling).\n // `materializeMotionPathInPropAnim` is a no-op for non-motion-path\n // animations, so non-transform props pay zero cost.\n normalized[propName] = (engine === PxTimelineEngine.native && propName === 'transform')\n ? materializeMotionPathInPropAnim(out)\n : out;\n }\n }\n\n return normalized;\n}\n\n/**\n * Normalizes a PxAnimatedSvgDocument to a PxAnimatorConfig for the animation engines.\n * This is the main entry point for converting the new API format to internal format.\n * Resolves animation/easing references. `engine` controls motion-along-path\n * handling — see {@link PxTimelineEngine}.\n * @public @advanced\n */\nexport function normalizeBindings(\n doc: PxAnimatedSvgDocument,\n engine: PxTimelineEngine = PxTimelineEngine.native,\n): PxNormalizedBinding[] {\n const animatorConfig = getAnimatorConfig(doc) || {};\n const defs = getDefinitions(doc);\n const duration = animatorConfig.duration || 1000; // FIXME - get rid of 1000 here\n\n const bindings: PxNormalizedBinding[] = [];\n\n // Helper to merge and normalize the resolved animation definitions of one element\n const processAnimation = (\n id: string,\n animDefs: PxAnimationDefinition[],\n staticTransform?: unknown,\n ): PxNormalizedBinding | null => {\n if (animDefs.length === 0) return null;\n\n // CSS transform precedence (review §0.4/§1.6): the node's static transform\n // composes under the animated one — merged BEFORE normalization so both\n // engines see complete parts records.\n const merged = mergeStaticTransformIntoAnimDef(mergeAnimationDefinitions(animDefs), staticTransform);\n const normalizedAnim = normalizeAnimationDefinition(merged, duration, defs, engine);\n\n if (Object.keys(normalizedAnim).length === 0) return null;\n\n return {\n id,\n animate: normalizedAnim\n };\n };\n\n // Process bindings (for pre-rendered DOM): `target` is `#id`-spelled on the wire (every\n // element reference carries the hash — review §3.2); the engines get the bare DOM id.\n const docBindings = getBindings(doc);\n if (docBindings) {\n for (const binding of docBindings) {\n const id = binding.target.startsWith('#') ? binding.target.slice(1) : binding.target;\n const animDefs = binding.animateWith\n .map(name => resolveAnimation(name, defs))\n .filter((d): d is PxAnimationDefinition => !!d);\n const normalized = processAnimation(id, animDefs);\n if (normalized) bindings.push(normalized);\n }\n }\n\n // Process children (for rendered DOM).\n //\n // Per-element animations live under the node's `animate` bucket, keyed by\n // SVG/CSS property name — a PxAnimationDefinition (`{ transform: {keyframes},\n // fill: {keyframes}, … }`). The static initial value of each animated\n // property is carried separately as a plain attribute on the element body.\n // On-disk locations: top-level `node.animate` (JSON form).\n const processNode = (node: PxNode) => {\n const inlineAnim = node.animate;\n if (inlineAnim && Object.keys(inlineAnim).length > 0) {\n const nodeId = node.id || generateElementId();\n node.id = nodeId; // Ensure the node has an ID\n const normalized = processAnimation(nodeId, resolveElementAnimation(inlineAnim, defs), node.transform);\n if (normalized) bindings.push(normalized);\n }\n\n // Process children\n if (node.children) {\n for (let i = 0; i < node.children.length; i++) {\n processNode(node.children[i]);\n }\n }\n };\n\n // Process children of the root\n if (doc.children) {\n for (let i = 0; i < doc.children.length; i++) {\n processNode(doc.children[i]);\n }\n }\n\n return bindings;\n}\n\n\n// ============================================================================\n// ATTRIBUTE VALUE CALCULATION (used by frame loop animator)\n// ============================================================================\n\n/**\n * Finds prev/next keyframes for a given progress.\n */\nfunction getKeyframesPair(keyframes: PxNormalizedKeyframe[], progress: number) {\n // Outside the keyframe range, clamp to the NEAREST REAL segment (the caller clamps\n // `localProgress` to 0/1, so that holds the boundary pose). Spanning first→last\n // instead would invent a segment that exists nowhere in the animation: for keyframes\n // that start part-way into the timeline, the pre-start frames used to interpolate\n // straight from the first to the LAST keyframe — skipping everything between, and\n // handing motion-path evaluation a chord it then cached (see `getSegmentCache`).\n const last = keyframes.length - 1;\n let prevKf = keyframes[0];\n let nextKf = keyframes[last > 0 ? 1 : 0];\n\n for (let j = 0; j < last; j++) {\n const aOff = (keyframes[j].t ?? 0);\n const bOff = (keyframes[j + 1].t ?? 0);\n if (aOff <= progress && progress <= bOff) {\n prevKf = keyframes[j];\n nextKf = keyframes[j + 1];\n break;\n }\n // Past this segment and not bracketed by any later one → hold the final segment.\n if (progress > bOff && j === last - 1) {\n prevKf = keyframes[last > 0 ? last - 1 : 0];\n nextKf = keyframes[last];\n }\n }\n return { prevKf, nextKf };\n}\n\n/**\n * Calculates interpolated value for a single property animation.\n */\nfunction calcPropertyValue(\n propName: string,\n propAnim: PxPropertyAnimation,\n progress: number\n): { k: string, v: string } | null {\n const keyframes = propAnim.keyframes || [];\n if (keyframes.length === 0) return null;\n\n const { prevKf, nextKf } = getKeyframesPair(keyframes, progress);\n\n // remap to local 0..1 within prevKf..nextKf\n let localProgress = (prevKf === nextKf) ? 0 : remap(progress, prevKf.t ?? 0, nextKf.t ?? 0, 0, 1);\n localProgress = clamp(localProgress, 0, 1);\n const easing = keyframeEasing(prevKf); // e is on the source keyframe: applied from this KF to the next\n if (easing && Array.isArray(easing)) {\n try {\n localProgress = cubicBezier(easing as [number, number, number, number])(localProgress);\n } catch (e) {\n // fallback: ignore easing if parsing fails\n }\n }\n\n let cssAttrName = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;\n let cssValue: string | number | null = null;\n\n const prevV = prevKf?.v;\n const nextV = nextKf?.v;\n\n if (cssAttrName === 'd') {\n // Extract paths from { paths: [...] } format\n const prevPaths = prevV?.paths ?? (Array.isArray(prevV) ? prevV : []);\n const nextPaths = nextV?.paths ?? (Array.isArray(nextV) ? nextV : []);\n cssValue = interpolateBeziers(\n prevPaths,\n nextPaths,\n localProgress\n ).map(bz => bezierToSvgPath(bz)).join('');\n } else if (PX_COLOR_ATTR_NAMES.has(cssAttrName)) {\n cssValue = toRGBA(interpolateColor(\n prevV || [0, 0, 0, 1],\n nextV || [0, 0, 0, 1],\n localProgress\n ));\n cssAttrName = propName;\n } else if (cssAttrName === 'stroke-dasharray') {\n cssValue = interpolateVec(\n prevV || [],\n nextV || [],\n localProgress\n ).join(' ');\n cssAttrName = propName;\n } else if (\n cssAttrName === 'transform' &&\n prevV !== null && typeof prevV === 'object' && !Array.isArray(prevV)\n ) {\n // Unified transform: keyframe values are PxTransformParts records.\n // Interpolate each present part separately, then compose into a transform\n // string for the SVG `transform` attribute (no units).\n const partKeys = new Set<string>([\n ...(prevV ? Object.keys(prevV) : []),\n ...(nextV ? Object.keys(nextV) : []),\n ]);\n const partsResult: PxTransformParts = {};\n for (const partKey of partKeys) {\n const prevPart = prevV?.[partKey];\n const nextPart = nextV?.[partKey];\n if (partKey === 'rotate' || partKey === 'skew') {\n partsResult[partKey] = interpolateNum(+(prevPart ?? 0), +(nextPart ?? 0), localProgress);\n } else if (partKey === 'translate' || partKey === 'scale' || partKey === 'origin') {\n const fallback = partKey === 'scale' ? [1, 1] : [0, 0];\n const interp = interpolateVec(prevPart || fallback, nextPart || fallback, localProgress);\n (partsResult as any)[partKey] = interp;\n }\n }\n // Motion-along-path override (frames-mode only). The WAAPI binding\n // pipeline materializes tangents + autoOrient into sampled\n // `{translate, rotate}` kfs upstream (in `normalizeAnimationDefinition`),\n // so for WAAPI this branch is a no-op (`propAnimIsMotionPath` returns\n // false on already-materialized kfs). Frames-mode preserves the\n // parametric form and we evaluate the Bezier per frame here — gives\n // exact spatial fidelity vs. any finite sampling.\n if (propAnimIsMotionPath(propAnim)) {\n const prevTr = (prevV as { translate?: [number, number] }).translate;\n const nextTr = (nextV as { translate?: [number, number] }).translate;\n if (Array.isArray(prevTr) && Array.isArray(nextTr)) {\n const sample = evaluateMotionPathSegment(\n prevKf, nextKf,\n [+prevTr[0], +prevTr[1]],\n [+nextTr[0], +nextTr[1]],\n localProgress,\n !!propAnim.autoOrient,\n );\n partsResult.translate = [sample.translate[0], sample.translate[1]];\n if (sample.rotateDeg !== undefined) partsResult.rotate = sample.rotateDeg;\n }\n }\n cssValue = composeTransformParts(partsResult, { withUnits: false });\n cssAttrName = 'transform';\n } else if (cssAttrName === 'translate') {\n const v = interpolateVec(\n prevV || [0, 0],\n nextV || [0, 0],\n localProgress\n );\n cssValue = 'translate(' + v.join(',') + ')';\n cssAttrName = 'transform';\n } else if (cssAttrName === 'rotate') {\n const v = interpolateNum(\n +(prevV || 0),\n +(nextV || 0),\n localProgress\n );\n cssValue = 'rotate(' + v + ')';\n cssAttrName = 'transform';\n } else if (cssAttrName === 'scale') {\n const v = interpolateVec(\n prevV || [1, 1],\n nextV || [1, 1],\n localProgress\n );\n cssValue = 'scale(' + v.join(',') + ')';\n cssAttrName = 'transform';\n } else {\n // numeric attr\n const num = interpolateNum(\n +(prevV || 0),\n +(nextV || 0),\n localProgress\n );\n cssValue = num;\n }\n\n if (PX_PCT_BASED_ATTR_NAMES.has(cssAttrName) && typeof cssValue === 'number') {\n cssValue = (cssValue * 100) + '%';\n }\n\n return { k: cssAttrName, v: cssValue === null ? '' : '' + cssValue };\n}\n\n/**\n * Calculates interpolated attribute values for an animation definition.\n * @param animDef The animation definition (with resolved refs and normalized times)\n * @param progress The current animation progress (0-1)\n * @returns Object with computed attribute name/value pairs\n * @public @advanced\n */\nexport function calcAnimationValues(\n animDef: PxAnimationDefinition,\n progress: number\n): Record<string, string> {\n const result: Record<string, string> = {};\n\n for (const [propName, propAnim] of Object.entries(animDef)) { \n const computed = calcPropertyValue(propName, propAnim, progress);\n if (computed) {\n result[computed.k] = computed.v;\n }\n }\n\n return result;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\n/**\n * Arc-length sampler for an SVG path `d` — the geometry backing along-path glyph\n * placement. Parses the path into cubic Bézier segments (lines and quadratics\n * up-converted), builds a per-segment arc-length LUT, and answers\n * `sampleAtDistance(dist) → { x, y, angle }` (angle = path tangent, radians).\n *\n * Supported commands: M/L/H/V/C/S/Q/T/Z (absolute + relative). Arcs (A/a) are\n * approximated as a straight line to the endpoint (editor paths never emit them;\n * this only guards odd imports). Multiple subpaths are concatenated by arc\n * length (gaps between them are ignored — text flows continuously).\n */\n\nimport { bezier2D_arcLengthLUT, bezier2D_derivativeAt, bezier2D_pointAt, bezier2D_tForDistance, type ArcLengthLUT } from '../../util/PxAnimatorUtil';\n\ntype Pt = [number, number];\n\ninterface Cubic { P0: Pt; P1: Pt; P2: Pt; P3: Pt; lut: ArcLengthLUT; len: number; }\n\n/** @internal */\nexport interface PathPoint { x: number; y: number; angle: number; }\n\n/** @internal */\nexport interface PathSampler {\n totalLength: number;\n /** True when the path loops back on itself (explicit `Z` or coincident ends) —\n * no open tip to run off, so overflow clamps/wraps rather than clipping. */\n closed: boolean;\n sampleAtDistance(dist: number): PathPoint;\n}\n\nconst LUT_STEPS = 48;\nconst CMD_RE = /[MmLlHhVvCcSsQqTtAaZz]/;\n\n/** Tokenises `d` into command letters and numbers, preserving order. */\nfunction tokenize(d: string): Array<string> {\n const tokens: Array<string> = [];\n const re = /([MmLlHhVvCcSsQqTtAaZz])|(-?\\d*\\.?\\d+(?:[eE][-+]?\\d+)?)/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(d)) !== null) tokens.push(m[0]);\n return tokens;\n}\n\nfunction quadToCubic(P0: Pt, Qc: Pt, P3: Pt): { P1: Pt; P2: Pt } {\n return {\n P1: [P0[0] + 2 / 3 * (Qc[0] - P0[0]), P0[1] + 2 / 3 * (Qc[1] - P0[1])],\n P2: [P3[0] + 2 / 3 * (Qc[0] - P3[0]), P3[1] + 2 / 3 * (Qc[1] - P3[1])],\n };\n}\n\n/** Parses `d` into cubic segments. Returns null when nothing usable was found. */\nfunction parseCubics(d: string): Array<Cubic> | null {\n const tokens = tokenize(d);\n const segs: Array<Cubic> = [];\n\n let i = 0;\n let cx = 0, cy = 0; // current point\n let sx = 0, sy = 0; // subpath start\n let pcx = 0, pcy = 0; // previous cubic control point (for S)\n let pqx = 0, pqy = 0; // previous quad control point (for T)\n let prevCmd = '';\n\n const num = (): number => parseFloat(tokens[i++]);\n const push = (P1: Pt, P2: Pt, P3: Pt): void => {\n const P0: Pt = [cx, cy];\n const lut = bezier2D_arcLengthLUT(P0, P1, P2, P3, LUT_STEPS);\n segs.push({ P0, P1, P2, P3, lut, len: lut.ds[lut.ds.length - 1] });\n cx = P3[0]; cy = P3[1];\n };\n // Straight line as a cubic with controls at 1/3 and 2/3 — this makes\n // `B(t) = P0 + t·(P3−P0)` EXACTLY, so arc length is linear in t and sampling\n // is precise (degenerate `P1=P0,P2=P3` controls would reparametrise it).\n const pushLine = (x: number, y: number): void => {\n push(\n [cx + (x - cx) / 3, cy + (y - cy) / 3],\n [cx + 2 * (x - cx) / 3, cy + 2 * (y - cy) / 3],\n [x, y],\n );\n };\n\n while (i < tokens.length) {\n let cmd = tokens[i];\n if (CMD_RE.test(cmd)) i++;\n else cmd = prevCmd === 'M' ? 'L' : prevCmd === 'm' ? 'l' : prevCmd; // implicit repeat\n const rel = cmd >= 'a';\n const U = cmd.toUpperCase();\n\n if (U === 'Z') { pushLine(sx, sy); cx = sx; cy = sy; prevCmd = cmd; continue; }\n if (U === 'M') {\n const x = num() + (rel ? cx : 0), y = num() + (rel ? cy : 0);\n cx = x; cy = y; sx = x; sy = y; prevCmd = cmd; continue;\n }\n if (U === 'L') {\n pushLine(num() + (rel ? cx : 0), num() + (rel ? cy : 0));\n } else if (U === 'H') {\n pushLine(num() + (rel ? cx : 0), cy);\n } else if (U === 'V') {\n pushLine(cx, num() + (rel ? cy : 0));\n } else if (U === 'C') {\n const p1: Pt = [num() + (rel ? cx : 0), num() + (rel ? cy : 0)];\n const p2: Pt = [num() + (rel ? cx : 0), num() + (rel ? cy : 0)];\n const p3: Pt = [num() + (rel ? cx : 0), num() + (rel ? cy : 0)];\n pcx = p2[0]; pcy = p2[1];\n push(p1, p2, p3);\n } else if (U === 'S') {\n const smooth = (prevCmd.toUpperCase() === 'C' || prevCmd.toUpperCase() === 'S');\n const p1: Pt = smooth ? [2 * cx - pcx, 2 * cy - pcy] : [cx, cy];\n const p2: Pt = [num() + (rel ? cx : 0), num() + (rel ? cy : 0)];\n const p3: Pt = [num() + (rel ? cx : 0), num() + (rel ? cy : 0)];\n pcx = p2[0]; pcy = p2[1];\n push(p1, p2, p3);\n } else if (U === 'Q') {\n const qc: Pt = [num() + (rel ? cx : 0), num() + (rel ? cy : 0)];\n const p3: Pt = [num() + (rel ? cx : 0), num() + (rel ? cy : 0)];\n pqx = qc[0]; pqy = qc[1];\n const { P1, P2 } = quadToCubic([cx, cy], qc, p3);\n push(P1, P2, p3);\n } else if (U === 'T') {\n const smooth = (prevCmd.toUpperCase() === 'Q' || prevCmd.toUpperCase() === 'T');\n const qc: Pt = smooth ? [2 * cx - pqx, 2 * cy - pqy] : [cx, cy];\n const p3: Pt = [num() + (rel ? cx : 0), num() + (rel ? cy : 0)];\n pqx = qc[0]; pqy = qc[1];\n const { P1, P2 } = quadToCubic([cx, cy], qc, p3);\n push(P1, P2, p3);\n } else if (U === 'A') {\n // Arc — skip rx ry rot large sweep, use endpoint as a straight line.\n i += 5;\n pushLine(num() + (rel ? cx : 0), num() + (rel ? cy : 0));\n } else { i++; continue; } // unknown — skip a token defensively\n\n prevCmd = cmd;\n }\n\n return segs.length ? segs : null;\n}\n\nfunction clamp(v: number, lo: number, hi: number): number {\n return v < lo ? lo : v > hi ? hi : v;\n}\n\n/** @internal */\nexport function createPathSampler(d: string): PathSampler | null {\n const segs = parseCubics(d);\n if (!segs) return null;\n\n // Cumulative start distance per segment; cum[segs.length] === total length.\n const cum = new Float64Array(segs.length + 1);\n for (let k = 0; k < segs.length; k++) cum[k + 1] = cum[k] + segs[k].len;\n const totalLength = cum[segs.length];\n\n // A path is CLOSED when it returns to its start (an explicit `Z`, or coincident\n // first/last points). Open paths get straight-line extrapolation past either end\n // (below); closed paths loop back on themselves, so there's no tip to run off.\n const start = segs[0].P0, end = segs[segs.length - 1].P3;\n const closed = Math.hypot(end[0] - start[0], end[1] - start[1]) < 1e-3;\n\n // Sample strictly within [0, totalLength].\n const sampleOn = (dist: number): PathPoint => {\n let k = 0;\n while (k < segs.length - 1 && dist > cum[k + 1]) k++;\n const seg = segs[k];\n const local = dist - cum[k];\n const t = seg.len > 0 ? bezier2D_tForDistance(seg.lut, local) : 0;\n const [x, y] = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, t);\n const [dx, dy] = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, t);\n return { x, y, angle: Math.atan2(dy, dx) };\n };\n\n return {\n totalLength,\n closed,\n sampleAtDistance(dist: number): PathPoint {\n // A CLOSED path loops: a distance outside [0, totalLength] wraps around\n // (modulo the length) rather than piling up at the seam. Without this a\n // negative startOffset collapses every pre-start glyph onto the path start\n // (e.g. text on a circle with startOffset < 0 stacking its first word).\n if (closed && totalLength > 0 && (dist < 0 || dist > totalLength)) {\n return sampleOn(((dist % totalLength) + totalLength) % totalLength);\n }\n // Past an end of an OPEN path: continue in a straight line along that\n // end's tangent, so along-path motion keeps going instead of piling up\n // at the tip. (The caller only asks for distances a glyph actually\n // reaches — startOffset + text length — so this never runs off forever.)\n if (!closed && (dist < 0 || dist > totalLength)) {\n const edge = dist < 0 ? 0 : totalLength;\n const p = sampleOn(edge);\n const over = dist - edge;\n return { x: p.x + Math.cos(p.angle) * over, y: p.y + Math.sin(p.angle) * over, angle: p.angle };\n }\n return sampleOn(clamp(dist, 0, totalLength));\n },\n };\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\n/**\n * Helpers for reading animatable transform parts and shaping them into the\n * player's `PxTransformParts` records. Shared by the transformation and\n * masked-by effects (the latter builds INVERSE parts).\n */\n\nimport type { PxAnimatable, PxKeyframe, PxLoop, PxNode, PxVec2 } from '../../format/PxAnimatorTypes';\nimport type { ApplyContext } from './types';\n\n\n/**\n * Names of the transform parts emitted into a `PxTransformParts` record. The\n * enum's STRING VALUES double as the wire-format object keys, so callers can\n * write `rec[TransformPart.Translate] = …` and produce `{translate: …}`.\n */\nexport enum TransformPart {\n Translate = 'translate',\n Rotate = 'rotate',\n Scale = 'scale',\n Skew = 'skew',\n Origin = 'origin',\n}\n\n\n/** Result kinds for `readAnimatable` — discriminator on `ReadPart<T>`. */\nexport enum ReadKind {\n Absent = 'absent',\n Static = 'static',\n Animated = 'animated',\n}\n\n\n/** Builds a player `PxTransformParts` record for one part (+ optional origin). */\nexport function partsRecord(part: TransformPart, value: any, origin: PxVec2 | undefined) {\n const rec: { translate?: PxVec2; rotate?: number; skew?: number; scale?: PxVec2; origin?: PxVec2 } = {};\n if (part === TransformPart.Translate) rec.translate = value;\n else if (part === TransformPart.Rotate) rec.rotate = value;\n else if (part === TransformPart.Skew) rec.skew = value;\n else rec.scale = value;\n if (origin && part !== TransformPart.Translate) rec.origin = origin;\n return rec;\n}\n\nexport type ReadPart<T> =\n | { kind: ReadKind.Absent }\n | { kind: ReadKind.Static; value: T }\n | { kind: ReadKind.Animated; keyframes: Array<PxKeyframe<T>>; autoOrient?: boolean; loop?: PxLoop | boolean; base?: T };\n\n/** Normalizes an animatable field into a static value or a keyframe list (with\n * `autoOrient` and `loop` propagated — the latter so timeline-level loop config\n * reaches the per-attribute `animate.X.loop` emitted by callers; without it,\n * effect-driven animations would silently ignore `loop.alternate`/cycle while\n * every non-effect property loops fine). A `value` present NEXT TO keyframes\n * is surfaced as `base` (the static baseline of the unified animatable form). */\nexport function readAnimatable<T>(raw: PxAnimatable<T> | undefined): ReadPart<T> {\n if (raw === undefined) return { kind: ReadKind.Absent };\n if (Array.isArray(raw)) return { kind: ReadKind.Static, value: raw as unknown as T };\n if (typeof raw === 'object') {\n const obj = raw as {\n value?: T; v?: T;\n keyframes?: Array<PxKeyframe<T>>;\n autoOrient?: boolean; loop?: PxLoop | boolean;\n };\n const kfs = obj.keyframes;\n if (kfs) {\n // Normalize the wire's short aliases ONCE, here, so every consumer\n // downstream (transformation, repeater, …) only ever sees the long\n // form. Without this a keyframe authored `{t, v}` lost both its time\n // and its value and the animation silently froze at frame 0.\n const out: ReadPart<T> = { kind: ReadKind.Animated, keyframes: kfs.map(normalizeKeyframe), autoOrient: obj.autoOrient, loop: obj.loop };\n const base = obj.value ?? obj.v;\n if (base !== undefined && out.kind === ReadKind.Animated) out.base = base;\n return out;\n }\n const staticValue = obj.value ?? obj.v;\n if (staticValue !== undefined) return { kind: ReadKind.Static, value: staticValue };\n }\n return { kind: ReadKind.Static, value: raw as T };\n}\n\n/**\n * Writes a normalized animatable (`ReadPart`) onto a node as attribute/animation —\n * the ONE emit path shared by the effect appliers (strokeTrim, textPath, …):\n * - Static → `node[attrName] = value` (stringified when `opts.asString`).\n * - Animated → `node.animate[attrName] = {keyframes, loop?, autoOrient?}` PLUS a\n * static baseline attr (`base` if present, else the first kf's value). The\n * animator only pushes animated values to the DOM on its first rAF tick — a\n * DOM snapshot between mount and that tick (visual-test live-mode sample at\n * t=0) must render the kf-at-time-0 state, not the un-styled default.\n */\nexport function writeAnimatableChannel(\n node: PxNode,\n attrName: string,\n read: ReadPart<any>,\n opts?: { asString?: boolean },\n): void {\n const toOut = (v: unknown): unknown => (opts?.asString && v !== undefined && v !== null) ? String(v) : v;\n if (read.kind === ReadKind.Absent) return;\n if (read.kind === ReadKind.Static) {\n node[attrName] = toOut(read.value);\n return;\n }\n const prevAnimate = node.animate && typeof node.animate === 'object' && !Array.isArray(node.animate) ? node.animate : undefined;\n const animate: Record<string, any> = { ...(prevAnimate || {}) };\n const block: { keyframes: Array<PxKeyframe>; loop?: PxLoop | boolean; autoOrient?: boolean } = { keyframes: read.keyframes };\n if (read.loop !== undefined) block.loop = read.loop;\n if (read.autoOrient !== undefined) block.autoOrient = read.autoOrient;\n animate[attrName] = block;\n node.animate = animate;\n const baseline = read.base ?? read.keyframes[0]?.value ?? (read.keyframes[0] as { v?: unknown } | undefined)?.v;\n if (baseline !== undefined) node[attrName] = toOut(baseline);\n}\n\n/** Rewrites a keyframe's short aliases (`t`/`v`/`e`/`to`/`ti`) to their long\n * names. Long names win when both are present, matching `PxMotionPath`'s\n * `tangentIn ?? ti` precedence. */\nfunction normalizeKeyframe<T>(kf: PxKeyframe<T>): PxKeyframe<T> {\n if (!kf || typeof kf !== 'object') return kf;\n const k = kf as PxKeyframe<T> & { t?: number; v?: T; e?: unknown; to?: PxVec2; ti?: PxVec2 };\n if (k.t === undefined && k.v === undefined && k.e === undefined && k.to === undefined && k.ti === undefined) {\n return kf; // already long-form — keep the same object\n }\n const out: any = { ...k };\n if (out.time === undefined && k.t !== undefined) out.time = k.t;\n if (out.value === undefined && k.v !== undefined) out.value = k.v;\n if (out.easing === undefined && k.e !== undefined) out.easing = k.e;\n if (out.tangentOut === undefined && k.to !== undefined) out.tangentOut = k.to;\n if (out.tangentIn === undefined && k.ti !== undefined) out.tangentIn = k.ti;\n return out as PxKeyframe<T>;\n}\n\n/** Origin used inside rotate/scale records. Animated origin falls back to frame 0. */\nexport function readStaticOrigin(raw: PxAnimatable<PxVec2> | undefined, ctx: ApplyContext): PxVec2 | undefined {\n const o = readAnimatable<PxVec2>(raw);\n if (o.kind === ReadKind.Absent) return undefined;\n if (o.kind === ReadKind.Static) return o.value;\n ctx.warnings.push('transformBy.origin: animated origin approximated by its first keyframe');\n return o.keyframes[0]?.value;\n}\n\n/** Copies a keyframe's time / easing / motion-path tangent handles onto a new parts-record value. */\nexport function keyframeWith(kf: PxKeyframe<any>, value: any): PxKeyframe<any> {\n const out: PxKeyframe<any> = { value };\n if (kf.time !== undefined) out.time = kf.time;\n if (kf.easing !== undefined) out.easing = kf.easing;\n if (kf.tangentOut !== undefined) out.tangentOut = kf.tangentOut;\n if (kf.tangentIn !== undefined) out.tangentIn = kf.tangentIn;\n return out;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n/**\n * Shared \"clone a subtree and rewire its internal references\" primitives.\n *\n * Used by:\n * - The retime effect's `<use>` chain materializer (`effects/util.ts`'s\n * `clone` + `regenerateIdsInClone`, which now delegate here).\n * - `PxAnimatorUseMaterializer` — the WAAPI-side post-pass that replaces\n * `<use>` instances referencing animated subtrees with deep clones.\n *\n * Both call sites need the exact same two operations: a structural deep clone\n * of a `PxNode` subtree, and a renumber-all-ids pass that also rewrites\n * internal `href=\"#X\"` / `url(#X)` references to the new ids while leaving\n * outward-facing references alone.\n */\n\nimport type { PxNode } from '../format/PxAnimatorTypes';\n\n\n/** Recursive deep clone for plain JSON-shaped `PxNode` data (objects, arrays,\n * primitives). Faster than `JSON.parse(JSON.stringify(x))` for large trees,\n * no behavior difference for our wire format (no `Date` / `Map` / functions).\n */\nexport function deepClonePxNode<T>(value: T): T {\n if (value === null || typeof value !== 'object') return value;\n if (Array.isArray(value)) return value.map(deepClonePxNode) as unknown as T;\n const out: { [k: string]: unknown } = {};\n for (const k of Object.keys(value as object)) out[k] = deepClonePxNode((value as { [k: string]: unknown })[k]);\n return out as T;\n}\n\n\n/**\n * Regenerates every `id` inside `root` (root included) using fresh ids from\n * `genId()`, then rewrites internal DOM references (`href=\"#oldId\"` and\n * `url(#oldId)` inside string attrs) to the new ids. References that point\n * OUTSIDE the cloned subtree are left untouched.\n *\n * `meta.*`, `effects.*`, `animate.*` are intentionally skipped — they don't\n * carry DOM-reference syntax, and walking them per-string is wasted work.\n *\n * Returns the old→new id map so callers can rewrite their own outward-facing\n * references (e.g. a `<use>` href pointing at the cloned subtree root).\n */\nexport function regenerateIdsAndRewriteRefs(\n root: PxNode,\n genId: () => string,\n): Map<string, string> {\n const oldToNew = new Map<string, string>();\n\n const walkAssign = (n: PxNode): void => {\n if (typeof n.id === 'string') {\n const newId = genId();\n oldToNew.set(n.id, newId);\n n.id = newId;\n }\n n.children?.forEach(walkAssign);\n };\n walkAssign(root);\n\n const rewriteUrl = (s: string): string => s.replace(/url\\(#([^)]+)\\)/g, (m, oldId) => {\n const newId = oldToNew.get(oldId);\n return newId ? 'url(#' + newId + ')' : m;\n });\n\n const walkRewrite = (n: PxNode): void => {\n if (typeof n.href === 'string' && n.href.startsWith('#')) {\n const newId = oldToNew.get(n.href.slice(1));\n if (newId) n.href = '#' + newId;\n }\n for (const k of Object.keys(n)) {\n if (k === 'children' || k === 'effects' || k === 'meta' || k === 'animate' || k === 'href' || k === 'id') continue;\n const v = (n as { [k: string]: unknown })[k];\n if (typeof v === 'string' && v.indexOf('url(#') !== -1) {\n (n as { [k: string]: unknown })[k] = rewriteUrl(v);\n }\n }\n n.children?.forEach(walkRewrite);\n };\n walkRewrite(root);\n\n return oldToNew;\n}\n\n\nfunction toFiniteNum(v: unknown): number {\n const n = typeof v === 'number' ? v : typeof v === 'string' ? parseFloat(v) : NaN;\n return Number.isFinite(n) ? n : 0;\n}\n\n/**\n * Applies a `<use>`'s `x`/`y` offset when it is materialized into a `<g>`.\n *\n * Per SVG 2, `<use x y>` is an extra `translate(x, y)` appended AFTER the use's\n * own `transform`, so it composes innermost (closest to the referenced content).\n * `<g>` has no `x`/`y`, so the offset must become a transform — and when the use\n * also carries a (possibly animated) `transform`, the two can't be merged into a\n * single attribute, so the content is wrapped in an inner offset `<g>`:\n *\n * <use transform=\"T\" x=\"X\" y=\"Y\">\n * → <g transform=\"T\"><g transform=\"translate(X,Y)\">…content…</g></g>\n *\n * With no transform/animation, a single `<g transform=\"translate(X,Y)\">` suffices.\n *\n * `gNode` is the `<g>` already derived from the use (type:'g', href dropped, the\n * use's `transform`/`animate`/etc. carried over, `x`/`y` still present). Mutates\n * and returns it; `x`/`y` are removed.\n */\nexport function applyUseOffsetToG(gNode: PxNode): PxNode {\n const x = toFiniteNum((gNode as { x?: unknown }).x);\n const y = toFiniteNum((gNode as { y?: unknown }).y);\n delete (gNode as { x?: unknown }).x;\n delete (gNode as { y?: unknown }).y;\n if (!x && !y) return gNode;\n\n const offset = 'translate(' + x + ',' + y + ')';\n\n // Any existing transform/animation on the outer <g> must apply OUTSIDE the\n // offset, so push the offset into an inner wrapper rather than risk\n // overwriting/conflicting with the carried-over `transform`/`animate`.\n const carriesTransform =\n (gNode as { transform?: unknown }).transform !== undefined ||\n (gNode as { animate?: unknown }).animate !== undefined;\n\n if (carriesTransform) {\n const inner: PxNode = { type: 'g', transform: offset, children: gNode.children ?? [] } as PxNode;\n gNode.children = [inner];\n } else {\n (gNode as { transform?: string }).transform = offset;\n }\n return gNode;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\n/** Generic, effect-agnostic helpers for the player-effects applier. */\n\nimport { deepClonePxNode, regenerateIdsAndRewriteRefs } from '../../util/PxNodeCloneUtil';\nimport type { PxNode, PxVec2 } from '../../format/PxAnimatorTypes';\nimport type { ApplyContext } from './types';\n\n/** Generates a deterministic id for a generated node (`<mask>`, retimed `<symbol>`, …). */\nexport function genId(ctx: ApplyContext, prefix: string): string {\n return '_lw_' + prefix + '_' + (ctx.nextId++);\n}\n\nexport function stripHash(href: any): string | undefined {\n return typeof href === 'string' ? href.replace(/^#/, '') : undefined;\n}\n\n/** Reads the leading `translate(x,y)` from an SVG transform string. */\nexport function readTranslateFromTransform(transform: any): PxVec2 | undefined {\n if (typeof transform !== 'string') return undefined;\n const m = transform.match(/translate\\(\\s*(-?[\\d.]+)\\s*,\\s*(-?[\\d.]+)\\s*\\)/);\n return m ? [Number(m[1]), Number(m[2])] : undefined;\n}\n\n/** Records every node carrying an `id` into the lookup map. */\nexport function indexById(node: PxNode, map: Map<string, PxNode>): void {\n if (typeof node.id === 'string') map.set(node.id, node);\n node.children?.forEach(child => indexById(child, map));\n}\n\n/** Inserts generated <defs> nodes at the front of the root's children. */\nexport function spliceDefs(root: PxNode, defs: Array<PxNode>): void {\n if (!defs.length) return;\n const existing = root.children || (root.children = []);\n existing.unshift({ type: 'defs', children: defs });\n}\n\n/** Deep-clone a `PxNode` subtree. Thin wrapper around the shared\n * {@link deepClonePxNode}; retained as `clone` for the existing call sites. */\nexport const clone = deepClonePxNode;\n\n/**\n * Regenerates every `id` inside `root` (root included) using `genId(ctx, 'retimed')`\n * and rewrites internal `href=\"#X\"` / `url(#X)` refs. Thin wrapper around the\n * shared {@link regenerateIdsAndRewriteRefs}; encapsulates the retime-specific\n * id-prefix convention so retime call sites stay unchanged.\n */\nexport function regenerateIdsInClone(root: PxNode, ctx: ApplyContext): Map<string, string> {\n return regenerateIdsAndRewriteRefs(root, () => genId(ctx, 'retimed'));\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\nimport type { PxAnimatable, PxKeyframe, PxLoop, PxNode, PxTextPathEffect } from '../../format/PxAnimatorTypes';\nimport type { ApplyContext } from '../shared/types';\nimport { createPathSampler } from './pathSampler';\nimport { ReadKind, readAnimatable, writeAnimatableChannel } from '../shared/transformParts';\nimport { genId } from '../shared/util';\n\n\nconst EXTEND_MARGIN_FRAC = 0.15; // slack (× path length) to absorb sampling/measure error\n\n\n/** {min,max} over a `PxAnimatable<number>`'s keyframe values (or its single static\n * value; {0,0} when absent). The tangent extension must cover the FULL animation\n * range: the MOST-NEGATIVE startOffset drives the START extension, and the LARGEST\n * startOffset(+textLength) drives the END — using only one value (e.g. the max)\n * misses the other end when startOffset is animated. */\nfunction numRange(v: PxAnimatable<number> | undefined): { min: number; max: number } {\n const read = readAnimatable<number>(v);\n if (read.kind === ReadKind.Static && typeof read.value === 'number') return { min: read.value, max: read.value };\n if (read.kind === ReadKind.Animated && read.keyframes.length) {\n const vals = read.keyframes.map(k => Number(k.value) || 0);\n return { min: Math.min(...vals), max: Math.max(...vals) };\n }\n return { min: 0, max: 0 };\n}\n\n/** Generous upper bound on the text's advance width: char-count × largest font\n * (~1em/char). Over-estimating is safe — the extension is an invisible tail of the\n * reference `<path>`, so extra length just goes unused. */\nfunction estimateTextAdvance(node: PxNode): number {\n let chars = 0, maxFont = 16;\n const walk = (el: PxNode): void => {\n const fs = parseFloat(String((el as any).fontSize ?? '')) || 0;\n if (fs) maxFont = Math.max(maxFont, fs);\n const t = el.textContent;\n if (typeof t === 'string') chars += t.length;\n if (el.children) for (const c of el.children) walk(c);\n };\n walk(node);\n return chars * maxFont;\n}\n\nconst r3 = (n: number): number => Math.round(n * 1000) / 1000;\n\n/** Inputs for {@link extendedPathForBrowser}. `advance` = the text run-width used to\n * size the end extension (caller-measured; the player estimates it from the node,\n * the editor from its text model — browser fonts have no glyph metrics available). * @internal\n */\nexport interface ExtendPathOptions {\n pathOverflow?: string;\n startOffset?: PxAnimatable<number>;\n textLength?: PxAnimatable<number>;\n advance?: number;\n}\n\n/** Result of {@link extendedPathForBrowser}: the (possibly) extended `d`, plus\n * `startShift` — the length of the prepended START lead-in. Because that lead-in\n * moves the `<textPath>` origin back by `startShift`, EVERY `startOffset` (all\n * keyframes) MUST be shifted by `+startShift` so the text lands where it would on\n * the un-extended path (`extend` only adds a tail, it must never move the text). * @internal\n */\nexport interface ExtendedPath {\n d: string;\n startShift: number;\n}\n\n/** Shift a `PxAnimatable<number>` by a constant (base + all keyframes). No-op for `0`.\n * Reads via the shared `readAnimatable` (kfs/loop aliases handled), emits the\n * normalized long form. * @internal\n */\nexport function shiftAnimatable(v: PxAnimatable<number> | undefined, by: number): PxAnimatable<number> | undefined {\n if (!by || v === undefined || v === null) return v;\n const read = readAnimatable<number>(v);\n if (read.kind === ReadKind.Absent) return v;\n if (read.kind === ReadKind.Static) return typeof v === 'number' ? read.value + by : { value: read.value + by };\n const out: { keyframes: Array<PxKeyframe<number>>; loop?: PxLoop | boolean; autoOrient?: boolean; value?: number } = {\n keyframes: read.keyframes.map(k => ({ ...k, value: (Number(k.value) || 0) + by })),\n };\n if (read.loop !== undefined) out.loop = read.loop;\n if (read.autoOrient !== undefined) out.autoOrient = read.autoOrient;\n if (read.base !== undefined) out.value = read.base + by;\n return out;\n}\n\n/** For `pathOverflow:'extend'` (browser-font): extend an OPEN path along its endpoint\n * tangents so the browser lays overflow glyphs onto the straight extension (matching\n * glyph-mode's tangent behavior) instead of dropping them. `'clip'`/closed paths are\n * returned unchanged (browser clips natively). Shared by the player's browser-font\n * applier and the editor's live/heavy `<textPath>` def generate (single source of truth).\n * Returns the extended `d` AND `startShift` — see {@link ExtendedPath}. * @internal\n */\nexport function extendedPathForBrowser(pathD: string, opts: ExtendPathOptions): ExtendedPath {\n if (opts.pathOverflow === 'clip') return { d: pathD, startShift: 0 };\n const sampler = createPathSampler(pathD);\n if (!sampler || sampler.closed || sampler.totalLength <= 0) return { d: pathD, startShift: 0 };\n\n const L = sampler.totalLength;\n const margin = EXTEND_MARGIN_FRAC * L;\n const so = numRange(opts.startOffset);\n const runWidth = numRange(opts.textLength).max || (opts.advance ?? 0);\n // START: how far the EARLIEST (most-negative) startOffset reaches before the path\n // start. END: how far the LATEST run (max startOffset + run width) reaches past the\n // path end. Only add the slack `margin` when actually extending, so a non-negative\n // startOffset that fits leaves the corresponding end alone (no phantom shift).\n const startOverflow = Math.max(0, -so.min);\n const endOverflow = Math.max(0, so.max + runWidth - L);\n const startExt = startOverflow > 0 ? startOverflow + margin : 0;\n const endExt = endOverflow > 0 ? endOverflow + margin : 0;\n if (endExt <= 0 && startExt <= 0) return { d: pathD, startShift: 0 };\n\n const s = sampler.sampleAtDistance(0);\n const e = sampler.sampleAtDistance(L);\n let d = pathD;\n if (startExt > 0) {\n const sx = s.x - Math.cos(s.angle) * startExt, sy = s.y - Math.sin(s.angle) * startExt;\n // Prepend a lead-in: M(extended start) L(original start) + the original path\n // minus its own leading `M x y` (we've re-stated the start via the `L`).\n const rest = pathD.replace(/^\\s*[Mm]\\s*-?[\\d.]+[\\s,]+-?[\\d.]+/, '');\n d = 'M' + r3(sx) + ',' + r3(sy) + 'L' + r3(s.x) + ',' + r3(s.y) + rest;\n }\n if (endExt > 0) {\n const ex = e.x + Math.cos(e.angle) * endExt, ey = e.y + Math.sin(e.angle) * endExt;\n d += 'L' + r3(ex) + ',' + r3(ey);\n }\n // The `<textPath>` distance origin moved back by exactly the start lead-in length.\n return { d, startShift: startExt };\n}\n\n\n/**\n * `effects.textPath` materializer (browser-font / non-glyph path).\n *\n * The path geometry is carried INLINE on the effect as `path` (an SVG `d`). SVG's\n * native rendering requires a `<textPath href=\"#…\">` wrapper referencing a `<path>`\n * def, so this applier generates that `<path>` def from the inline geometry, wraps the\n * text node's children in the `<textPath>`, and forwards the textPath SVG attrs\n * (`lengthAdjust`, `method`, `spacing`, `startOffset`, `textLength`).\n *\n * `startOffset` / `textLength` accept the full `PxAnimatable<number>` shape: a static\n * number is set as an attribute; the `{keyframes}` form is forwarded to\n * `<textPath>.animate.<attr>`; `{value}` is unwrapped to the static shape.\n *\n * `pathOverflow:'extend'` tangent-extends the generated `<path>` (see\n * {@link extendedPathForBrowser}) so the browser lays overflow glyphs onto the straight\n * extension; `'clip'` generates the geometry as-is and native `<textPath>` drops overflow.\n */\nexport function applyTextPathEffect(\n node: PxNode,\n fx: PxTextPathEffect | undefined,\n ctx: ApplyContext,\n): PxNode {\n if (!fx || typeof fx.pathData !== 'string' || !fx.pathData) return node;\n\n const pathId = genId(ctx, 'tpath');\n const { d, startShift } = extendedPathForBrowser(fx.pathData, {\n pathOverflow: fx.pathOverflow, startOffset: fx.startOffset,\n textLength: fx.textLength, advance: estimateTextAdvance(node),\n });\n ctx.defs.push({ type: 'path', id: pathId, d });\n\n const textPath: PxNode = {\n type: 'textPath',\n href: '#' + pathId,\n children: node.children ?? [],\n };\n if (fx.lengthAdjust !== undefined) textPath.lengthAdjust = fx.lengthAdjust;\n if (fx.method !== undefined) textPath.method = fx.method;\n if (fx.spacing !== undefined) textPath.spacing = fx.spacing;\n // Compensate the start lead-in: shift startOffset (all keyframes) by `startShift`\n // so `extend` doesn't move the text vs the un-extended path.\n applyAnimatableNumber(textPath, 'startOffset', shiftAnimatable(fx.startOffset, startShift));\n applyAnimatableNumber(textPath, 'textLength', fx.textLength);\n\n node.children = [textPath];\n return node;\n}\n\n\n/** Routes a `PxAnimatable<number>` onto a node via the shared reader + emit\n * path (`readAnimatable` → `writeAnimatableChannel`): statics land as string\n * attrs, animations as `animate[attrName]` blocks with `loop`/`autoOrient`\n * carried through and a static first-kf baseline attr. */\nfunction applyAnimatableNumber(node: PxNode, attrName: string, raw: PxAnimatable<number> | undefined): void {\n if (raw === undefined || raw === null) return;\n writeAnimatableChannel(node, attrName, readAnimatable<number>(raw), { asString: true });\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\n/**\n * Element-creation factory — abstracts WHAT an \"element\" is so the same\n * geometry/layout code (e.g. the glyph text materializer) can emit plain wire\n * nodes here, or the editor's React / px elements when called from the editor.\n *\n * The signature intentionally mirrors the editor's `createPxElement(type,\n * props, children, fixReactKeysIfNeeded?)` so the editor's own factory drops in\n * unchanged.\n * @internal\n */\nexport type PxCreateElement<E = any> = (\n type: string,\n props: { [k: string]: any },\n children?: Array<E> | E | null,\n fixReactKeysIfNeeded?: boolean,\n) => E;\n\n\n/**\n * Default factory → a plain wire node `{ type, ...props, children? }`.\n * Drops `undefined` props and empty `children` so output matches the shape the\n * effects pipeline (and `JSON.stringify`) expects.\n * @internal\n */\nexport const jsonElementFactory: PxCreateElement<any> = (type, props, children) => {\n const node: { [k: string]: any } = { type };\n for (const k in props) if (props[k] !== undefined) node[k] = props[k];\n const arr = Array.isArray(children) ? children.filter(c => c != null) : (children != null ? [children] : []);\n if (arr.length) node.children = arr;\n return node;\n};\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\n/**\n * Bakes an affine transform into an SVG path-`d` string so per-glyph outlines\n * (in em units) can be placed at their pen position and merged into a single\n * `<path>`. Parses the absolute `M`/`L`/`C`/`Q`/`Z` commands opentype's\n * `toPathData` emits — glyph outlines never use relative or arc commands.\n *\n * Matrix order matches SVG: `x' = a·x + c·y + e`, `y' = b·x + d·y + f`.\n * For horizontal text it's just scale+translate `[s,0,0,s,tx,ty]`; the full\n * 6-tuple leaves room for rotation (along-path, later).\n */\nexport type Affine = [a: number, b: number, c: number, d: number, e: number, f: number];\n\n\nfunction fmt(v: number, decimals: number): string {\n // Match opentype's `floatToString`: integers stay bare, else fixed decimals.\n return Math.round(v) === v ? '' + Math.round(v) : v.toFixed(decimals);\n}\n\n/** Packs numbers the way opentype does: a space separates a value from the\n * previous one only when it doesn't already start with a `-`. */\nfunction pack(nums: Array<number>, decimals: number): string {\n let s = '';\n for (let i = 0; i < nums.length; i++) {\n const str = fmt(nums[i], decimals);\n if (i > 0 && str.charCodeAt(0) !== 45 /* '-' */) s += ' ';\n s += str;\n }\n return s;\n}\n\nfunction apply(m: Affine, x: number, y: number): [number, number] {\n return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];\n}\n\nconst TOKEN_RE = /([MLCQZ])|(-?\\d*\\.?\\d+(?:e[-+]?\\d+)?)/gi;\n\n/**\n * Applies `m` to every coordinate in `d` (absolute M/L/C/Q/Z) and re-emits.\n * Unknown tokens are skipped defensively; the input is always opentype output.\n */\nexport function transformPathData(d: string, m: Affine, decimals: number = 2): string {\n const tokens: Array<string> = [];\n let match: RegExpExecArray | null;\n TOKEN_RE.lastIndex = 0;\n while ((match = TOKEN_RE.exec(d)) !== null) tokens.push(match[0]);\n\n let out = '';\n let i = 0;\n const num = (): number => parseFloat(tokens[i++]);\n\n while (i < tokens.length) {\n const cmd = tokens[i++];\n if (cmd === 'M' || cmd === 'L') {\n const [x, y] = apply(m, num(), num());\n out += cmd + pack([x, y], decimals);\n } else if (cmd === 'C') {\n const [x1, y1] = apply(m, num(), num());\n const [x2, y2] = apply(m, num(), num());\n const [x, y] = apply(m, num(), num());\n out += 'C' + pack([x1, y1, x2, y2, x, y], decimals);\n } else if (cmd === 'Q') {\n const [x1, y1] = apply(m, num(), num());\n const [x, y] = apply(m, num(), num());\n out += 'Q' + pack([x1, y1, x, y], decimals);\n } else if (cmd === 'Z' || cmd === 'z') {\n out += 'Z';\n }\n // else: stray number without a command — skip (never happens for glyph data).\n }\n return out;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\n/**\n * Glyph text materializer — turns a `<text>`/`<tspan>` subtree into `<path>`\n * outlines from `definitions.fonts`, so the text renders with no external font.\n *\n * - HORIZONTAL ({@link materializeGlyphTextHorizontal}) — left-to-right by\n * advance width; honors font-size, text-anchor, letter/word-spacing,\n * per-tspan x/y/dx/dy, fill/stroke, nested tspans.\n * - ALONG-PATH ({@link materializeGlyphTextAlongPath}) — each glyph placed and\n * rotated to the referenced path's tangent. Static `startOffset` → glyphs\n * bake+merge; animated `startOffset` → per-glyph `<path>` with sampled\n * `animate.transform`. Text-level `x`/`dx` add distance ALONG the path (≈\n * startOffset) and `dy` shifts PERPENDICULAR — matching native `<textPath>`\n * (see {@link alongPathNodeOffsets}); `y` and per-tspan positioning are ignored\n * (a single run).\n *\n * Element creation goes through an injected {@link PxCreateElement} factory, so\n * the SAME layout produces plain wire nodes here (the effects pipeline) or the\n * editor's React/px elements when the editor calls it — see\n * {@link materializeGlyphText}.\n *\n * v1 scope (see svga.text.design.md): keyframe-interval easing is linear;\n * kerning/ligatures, per-tspan opacity, text-level animated fill are out of scope.\n */\n\nimport { type PxAnimatable, type PxGlyphFont, type PxNode, type PxTextEffect } from '../../format/PxAnimatorTypes';\nimport { PX_TEXT_CONTENT_ATTR, CLASS_ATTR } from '../../format/PxAnimatorConstants';\nimport { jsonElementFactory, type PxCreateElement } from './elementFactory';\nimport { transformPathData, type Affine } from './glyphPathBake';\nimport { createPathSampler, type PathSampler } from './pathSampler';\nimport { unwrapAutoOrientRotations } from '../../materialize/PxMotionPath';\nimport { ReadKind, readAnimatable, TransformPart } from '../shared/transformParts';\nimport type { ApplyContext } from '../shared/types';\n\n\nconst DEFAULT_FONT_SIZE = 16;\n\n/** Text/tspan attribute keys that don't belong on the materialized `<g>`. */\nconst TEXT_ATTR_KEYS: ReadonlyArray<string> = [\n 'fontFamily', 'fontSize', 'fontWeight', 'fontStyle', 'textAnchor',\n 'letterSpacing', 'wordSpacing', 'textDecoration', 'textTransform',\n 'whiteSpace', 'x', 'y', 'dx', 'dy', 'lengthAdjust',\n 'fill', 'stroke', 'strokeWidth', 'effects',\n PX_TEXT_CONTENT_ATTR, 'xml:space',\n];\n\n/** Inputs for a glyph materialization, decoupled from the effects `ApplyContext`\n * so the editor can call the materializer directly. * @internal\n */\nexport interface GlyphMaterializeOptions<E = any> {\n /** Embedded glyph fonts, keyed by `font-family`. */\n glyphs: Record<string, PxGlyphFont>;\n /** Element factory — defaults to plain wire nodes ({@link jsonElementFactory}). */\n create?: PxCreateElement<E>;\n /** Optional diagnostics sink. */\n warnings?: Array<string>;\n}\n\n// All paint values are OPAQUE pass-throughs — copied verbatim onto the emitted element.\n// The wire uses plain values (hex strings, numbers, arrays); the editor threads its own\n// model VALUES through unchanged (the factory maps them back to shape paint).\n//\n// A SPAN's paint must fold into the baked paths — the span element itself disappears in\n// the bake, so anything left on it is silently lost. TEXT-level values are NOT part of\n// the paint: they ride the emitted `<g>` (via `toGroup`), so `resolveStyle` skips\n// `type:'text'` nodes' own values. `animate` is the span's animate bag filtered to\n// PAINT keys only (see `PAINT_ANIMATE_KEYS`) — forwarded verbatim onto the emitted\n// `<path>` (the wire's keyframe bags, or the editor's animated-value riders).\ninterface Paint {\n fill?: any; stroke?: any; strokeWidth?: any;\n opacity?: number;\n fillOpacity?: any; fillRule?: any;\n strokeOpacity?: any; strokeDasharray?: any; strokeDashoffset?: any;\n strokeLinecap?: any; strokeLinejoin?: any; strokeMiterlimit?: any;\n mixBlendMode?: any; filter?: any;\n animate?: { [k: string]: any };\n}\n/** The per-span statics that fold into `Paint` by simple nearest-wins lookup. */\nconst PAINT_STATIC_KEYS = [\n 'fillOpacity', 'fillRule', 'strokeOpacity', 'strokeDasharray', 'strokeDashoffset',\n 'strokeLinecap', 'strokeLinejoin', 'strokeMiterlimit', 'mixBlendMode', 'filter',\n] as const;\n/** `animate.<key>`s that are PAINT (ride the baked path). Geometry keys — x/dx/fontSize/\n * letterSpacing/… — are baked into the outlines and must NOT be forwarded. */\nconst PAINT_ANIMATE_KEYS = ['fill', 'stroke', 'strokeWidth', 'opacity', 'fillOpacity', 'strokeOpacity', 'strokeDasharray', 'strokeDashoffset'];\ninterface Style extends Paint { fontFamily?: string; fontSize: number; letterSpacing: number; wordSpacing: number; }\n/** A glyph ready to emit: its em outline + the affine placing it in the doc.\n * `isMissing` marks the □ placeholder — kept out of the merged real-glyph paths so a\n * consumer can style it (see `MISSING_GLYPH_CLASS_NAME`). */\ninterface Placement { glyphD: string; m: Affine; paint: Paint; isMissing?: boolean; }\n/** Minimal keyframe shape (avoids `PxKeyframe`'s non-generic type). Input\n * `startOffset` kfs carry a number `value`; emitted kfs carry transform parts. */\ninterface TransformKeyframe<V = any> { time?: number; value: V; }\n\n\nfunction parseLen(v: unknown): number | undefined {\n if (typeof v === 'number') return v;\n if (typeof v === 'string') { const n = parseFloat(v); return isNaN(n) ? undefined : n; }\n return undefined;\n}\n\nfunction str(v: unknown): string | undefined {\n return typeof v === 'string' ? v : undefined;\n}\n\n/** A span's paint-only `animate` bag, or undefined when it has none (see `PAINT_ANIMATE_KEYS`). */\nfunction paintAnimateOf(node: PxNode): { [k: string]: any } | undefined {\n const bag = node.animate as { [k: string]: any } | undefined;\n if (!bag || typeof bag !== 'object') return undefined;\n let out: { [k: string]: any } | undefined;\n for (const k of PAINT_ANIMATE_KEYS) {\n if (bag[k] !== undefined) (out ??= {})[k] = bag[k];\n }\n return out;\n}\n\nfunction resolveStyle(node: PxNode, parent: Style): Style {\n // SPAN paint only: the root `<text>`'s own values (and its whole `animate` bag) ride\n // the emitted `<g>` via `toGroup` — reading them here too would apply them twice.\n //\n // NEAREST WINS, deliberately NOT composed: the editor wire used to emit a collapsed\n // single-span line as a line-`<tspan>` carrying the folded span style AND the child\n // span with the same style — the same opacity on BOTH nesting levels; multiplying\n // would square it. Same override semantics as the other style props (fill, fontSize).\n const isTextRoot = node.type === 'text';\n const nodeStyle = node.style as { [k: string]: any } | undefined;\n const own = (key: string): any => isTextRoot ? undefined : ((node as { [k: string]: any })[key] ?? nodeStyle?.[key]);\n\n const res: Style = {\n fontFamily: str(node.fontFamily) ?? parent.fontFamily,\n fontSize: parseLen(node.fontSize) ?? parent.fontSize,\n fill: node.fill ?? parent.fill,\n stroke: node.stroke ?? parent.stroke,\n strokeWidth: node.strokeWidth ?? parent.strokeWidth,\n letterSpacing: parseLen(node.letterSpacing) ?? parent.letterSpacing,\n wordSpacing: parseLen(node.wordSpacing) ?? parent.wordSpacing,\n opacity: parseLen(own('opacity')) ?? parent.opacity,\n animate: (isTextRoot ? undefined : paintAnimateOf(node)) ?? parent.animate,\n };\n for (const key of PAINT_STATIC_KEYS) {\n const v = own(key) ?? parent[key];\n if (v !== undefined) res[key] = v;\n }\n return res;\n}\n\nfunction rootStyleOf(node: PxNode): Style {\n return {\n fontFamily: str(node.fontFamily),\n fontSize: parseLen(node.fontSize) ?? DEFAULT_FONT_SIZE,\n fill: node.fill,\n stroke: node.stroke,\n strokeWidth: node.strokeWidth,\n letterSpacing: parseLen(node.letterSpacing) ?? 0,\n wordSpacing: parseLen(node.wordSpacing) ?? 0,\n };\n}\n\nfunction paintOf(s: Style): Paint {\n // Field order is FIXED (it feeds the JSON.stringify merge key in `buildPaths`).\n const p: Paint = {};\n if (s.fill !== undefined) p.fill = s.fill;\n if (s.stroke !== undefined) p.stroke = s.stroke;\n if (s.strokeWidth !== undefined) p.strokeWidth = s.strokeWidth;\n if (s.opacity !== undefined && s.opacity !== 1) p.opacity = s.opacity;\n for (const key of PAINT_STATIC_KEYS) {\n if (s[key] !== undefined) p[key] = s[key];\n }\n if (s.animate !== undefined) p.animate = s.animate;\n return p;\n}\n\n/** The embedded outlines for a span. `font-family` holds the FACE name the author picked\n * (`Roboto-Light`), which is exactly the key the writer stored, so this is a direct lookup —\n * weight/slant are CSS attrs and never select a face. */\nfunction glyphFontFor(s: Style, glyphs: Record<string, PxGlyphFont>, soleFont: PxGlyphFont | undefined, warnings?: Array<string>): PxGlyphFont | undefined {\n const gf = s.fontFamily ? glyphs[s.fontFamily] : soleFont;\n if (!gf) warnings?.push('textGlyphs: no glyphs for font \"' + (s.fontFamily ?? '') + '\"');\n return gf;\n}\n\nfunction soleFontOf(glyphs: Record<string, PxGlyphFont>): PxGlyphFont | undefined {\n const names = Object.keys(glyphs);\n return names.length === 1 ? glyphs[names[0]] : undefined;\n}\n\n\n/** Default advance (in em units) for a MISSING glyph with no known width, so its\n * placeholder box gets a plausible letter-cell instead of collapsing to nothing. */\nconst MISSING_GLYPH_ADVANCE_EM = 0.6;\n\n/**\n * Class stamped on the `<path>` holding the □ placeholders — and ONLY on it: the missing\n * boxes are emitted separately from the real glyphs so a consumer can style them on their\n * own. The editor uses it to fade them in with a delay: a freshly typed character is\n * missing for a few frames until its glyph is fetched asynchronously, and a □ that flashes\n * for that long reads as a rendering glitch.\n * @internal\n */\nexport const MISSING_GLYPH_CLASS_NAME = 'px-missing-glyph';\n\n/**\n * A HOLLOW FRAME outline (em/glyph units, baseline at y=0 rising to y=-ascent) for a\n * MISSING glyph — so a font that can't supply a char renders a visible □ instead of\n * silently disappearing. Outer rect + a reversed inner rect → a plain nonzero `fill`\n * (the SAME paint a real glyph uses) leaves the middle empty, i.e. a thin outline (a\n * solid block reads as too brutal). Empty string for degenerate sizes.\n */\nfunction missingGlyphBoxEm(advanceEm: number, ascentEm: number): string {\n if (advanceEm <= 0 || ascentEm <= 0) return '';\n const inset = 0.08;\n const x0 = advanceEm * inset, x1 = advanceEm * (1 - inset);\n const y1 = -ascentEm * (1 - inset); // box top (baseline at 0)\n const bw = x1 - x0, bh = -y1;\n const t = Math.max(1, Math.min(bw, bh) * 0.12); // frame border thickness\n const outer = 'M' + x0 + ' 0L' + x1 + ' 0L' + x1 + ' ' + y1 + 'L' + x0 + ' ' + y1 + 'Z';\n if (bw <= 2 * t || bh <= 2 * t) return outer; // too small for a hole → solid\n const ix0 = x0 + t, ix1 = x1 - t, iyb = -t, iyt = y1 + t;\n // Inner rect wound OPPOSITE the outer → nonzero fill carves a hole (the outline).\n return outer + 'M' + ix0 + ' ' + iyb + 'L' + ix0 + ' ' + iyt + 'L' + ix1 + ' ' + iyt + 'L' + ix1 + ' ' + iyb + 'Z';\n}\n\n\n// ── HORIZONTAL ──────────────────────────────────────────────────────────────\n\n/** @internal */\nexport function materializeGlyphTextHorizontal<E = any>(node: PxNode, opts: GlyphMaterializeOptions<E>): E {\n const { glyphs, create = jsonElementFactory as PxCreateElement<E>, warnings } = opts;\n const soleFont = soleFontOf(glyphs);\n\n const pen = { x: parseLen(node.x) ?? 0, y: parseLen(node.y) ?? 0 };\n const placements: Array<Placement & { line: number; x: number; y: number; scale: number }> = [];\n const lines: Array<{ start: number; end: number }> = [{ start: pen.x, end: pen.x }];\n let line = 0;\n\n const renderChars = (content: string, s: Style): void => {\n const gf = glyphFontFor(s, glyphs, soleFont, warnings);\n const upm = gf?.unitsPerEm || 1000;\n const scale = s.fontSize / upm;\n const ascentEm = gf?.ascent ?? 0.9 * upm;\n const paint = paintOf(s);\n for (let i = 0; i < content.length; i++) {\n const ch = content.charAt(i);\n const g = gf?.glyphs[ch];\n if (g && g.pathData) {\n placements.push({ glyphD: g.pathData, m: [scale, 0, 0, scale, pen.x, pen.y], paint, line, x: pen.x, y: pen.y, scale });\n pen.x += g.width * scale;\n } else if (/\\S/.test(ch)) {\n // Missing glyph (font absent, or the char has no outline) → a visible □\n // placeholder box so the text doesn't just silently vanish.\n const advEm = (g && g.width > 0) ? g.width : MISSING_GLYPH_ADVANCE_EM * upm;\n placements.push({ glyphD: missingGlyphBoxEm(advEm, ascentEm), m: [scale, 0, 0, scale, pen.x, pen.y], paint, isMissing: true, line, x: pen.x, y: pen.y, scale });\n pen.x += advEm * scale;\n } else {\n pen.x += (g ? g.width : 0) * scale; // whitespace: advance only\n }\n pen.x += s.letterSpacing + (ch === ' ' ? s.wordSpacing : 0);\n lines[line].end = pen.x;\n }\n };\n\n const walk = (el: PxNode, parentStyle: Style): void => {\n const s = resolveStyle(el, parentStyle);\n const x = parseLen(el.x);\n const y = parseLen(el.y);\n if (x !== undefined) { pen.x = x; line = lines.length; lines.push({ start: pen.x, end: pen.x }); }\n if (y !== undefined) pen.y = y;\n pen.x += parseLen(el.dx) ?? 0;\n pen.y += parseLen(el.dy) ?? 0;\n const content = str(el[PX_TEXT_CONTENT_ATTR]);\n // Render a node's OWN text only when it has no element children. In the glyph\n // text model text lives on leaf spans; a container that ALSO carries folded\n // text — a single-span line collapsed onto its line-`<tspan>` — would\n // otherwise render its run twice (the fold AND the child span).\n if (content && !el.children?.length) renderChars(content, s);\n if (el.children) for (const ch of el.children) walk(ch, s);\n };\n\n const rootStyle = rootStyleOf(node);\n if (node.children) for (const ch of node.children) walk(ch, rootStyle);\n const rootContent = str(node[PX_TEXT_CONTENT_ATTR]);\n if (rootContent && !node.children?.length) renderChars(rootContent, rootStyle);\n\n // text-anchor: shift each line by its own advance width, then rebuild `m`.\n const anchor = str(node.textAnchor);\n if (anchor === 'middle' || anchor === 'end') {\n for (const p of placements) {\n const w = lines[p.line].end - lines[p.line].start;\n const shift = anchor === 'middle' ? -w / 2 : -w;\n p.m = [p.scale, 0, 0, p.scale, p.x + shift, p.y];\n }\n }\n\n return toGroup(node, buildPaths(placements, create, warnings), create);\n}\n\n\n/** Per-CHARACTER advance box (local, pre-transform coords). `x,y` = the char's baseline start,\n * `width` = its advance, `ascent`/`fontSize` size its bbox. * @internal\n */\nexport interface PxGlyphCharBox {\n x: number; y: number; width: number; ascent: number; fontSize: number;\n /** Along-path only: baseline END point (leading edge of the next char). Absent for\n * horizontal, where the end is `x + width` on the same baseline. */\n endX?: number; endY?: number;\n /** Along-path only: char rotation in DEGREES (path tangent; 0 = horizontal). */\n rotation?: number;\n}\n\n/** Optional along-path geometry for {@link layoutGlyphTextChars}: when given, chars are\n * placed + rotated along `pathD` (mirrors {@link materializeGlyphTextAlongPath}) at the\n * STATIC / frame-0 startOffset, so the editor caret follows the path. * @internal\n */\nexport interface GlyphCharBoxAlongPath { pathD?: string; startOffset?: PxAnimatable<number>; textLength?: PxAnimatable<number>; pathOverflow?: string; }\n\n/** Per-character layout boxes for a glyph text, in reading/DOM order INCLUDING spaces\n * (a space has no glyph but advances the pen) AND one zero-width filler box per EMPTY\n * line — the editor's edit canvas renders a zero-width filler char for an empty line\n * (so the caret has something to measure), and DOM char indices must stay aligned.\n * HORIZONTAL by default — mirrors `materializeGlyphTextHorizontal`'s pen-walk exactly\n * (same x/y/dx/dy, spacing and text-anchor). When `opts.alongPath` is given, mirrors\n * `materializeGlyphTextAlongPath` (each char placed + rotated to the path tangent). So\n * an editor caret built from these lands on the rendered glyphs. Empty for a text with\n * no glyph font / unparsable path. * @internal\n */\nexport function layoutGlyphTextChars(node: PxNode, opts: Pick<GlyphMaterializeOptions, 'glyphs' | 'warnings'> & { alongPath?: GlyphCharBoxAlongPath }): Array<PxGlyphCharBox> {\n if (opts.alongPath?.pathD) return layoutGlyphTextCharsAlongPath(node, opts.alongPath.pathD, opts);\n const { glyphs, warnings } = opts;\n const soleFont = soleFontOf(glyphs);\n\n const pen = { x: parseLen(node.x) ?? 0, y: parseLen(node.y) ?? 0 };\n const boxes: Array<PxGlyphCharBox & { line: number }> = [];\n const lines: Array<{ start: number; end: number }> = [{ start: pen.x, end: pen.x }];\n let line = 0;\n\n const renderChars = (content: string, s: Style): void => {\n const gf = glyphFontFor(s, glyphs, soleFont, warnings);\n const upm = gf?.unitsPerEm || 1000;\n const scale = s.fontSize / upm;\n const ascent = (gf?.ascent ?? 0.9 * upm) * scale;\n for (let i = 0; i < content.length; i++) {\n const ch = content.charAt(i);\n const g = gf?.glyphs[ch];\n const advance = (g ? g.width : 0) * scale + s.letterSpacing + (ch === ' ' ? s.wordSpacing : 0);\n boxes.push({ x: pen.x, y: pen.y, width: advance, ascent, fontSize: s.fontSize, line });\n pen.x += advance;\n lines[line].end = pen.x;\n }\n };\n\n const walk = (el: PxNode, parentStyle: Style): void => {\n const s = resolveStyle(el, parentStyle);\n const x = parseLen(el.x);\n const y = parseLen(el.y);\n if (x !== undefined) { pen.x = x; line = lines.length; lines.push({ start: pen.x, end: pen.x }); }\n if (y !== undefined) pen.y = y;\n pen.x += parseLen(el.dx) ?? 0;\n pen.y += parseLen(el.dy) ?? 0;\n const content = str(el[PX_TEXT_CONTENT_ATTR]);\n if (content && !el.children?.length) renderChars(content, s);\n if (el.children) for (const ch of el.children) walk(ch, s);\n };\n\n const rootStyle = rootStyleOf(node);\n if (node.children) for (const ch of node.children) {\n const before = boxes.length;\n walk(ch, rootStyle);\n // An EMPTY line (a line-tspan whose subtree yields no chars) still occupies ONE\n // DOM slot on the edit canvas — the zero-width filler the editor renders so the\n // caret has something to measure. Mirror it: one zero-width box at the line's\n // pen position, sized by the line's own resolved font (ascent for caret height,\n // and its hit quad extends the element bbox to include the empty line).\n if (boxes.length === before) {\n const s = resolveStyle(ch, rootStyle);\n const gf = glyphFontFor(s, glyphs, soleFont); // no warning — an empty line has nothing to render\n const upm = gf?.unitsPerEm || 1000;\n boxes.push({ x: pen.x, y: pen.y, width: 0, ascent: (gf?.ascent ?? 0.9 * upm) * (s.fontSize / upm), fontSize: s.fontSize, line });\n }\n }\n const rootContent = str(node[PX_TEXT_CONTENT_ATTR]);\n if (rootContent && !node.children?.length) renderChars(rootContent, rootStyle);\n\n // text-anchor: shift each line's chars by its own advance width (matches the placement shift).\n const anchor = str(node.textAnchor);\n if (anchor === 'middle' || anchor === 'end') {\n for (const b of boxes) {\n const w = lines[b.line].end - lines[b.line].start;\n b.x += anchor === 'middle' ? -w / 2 : -w;\n }\n }\n return boxes.map(({ line: _l, ...b }) => b);\n}\n\n/** Along-path variant of {@link layoutGlyphTextChars}: one box per DOM char (spaces\n * included), placed + rotated along `pathD` at the static / frame-0 startOffset.\n * Mirrors `collectAlongPathCells` + `materializeGlyphTextAlongPath`, but records EVERY\n * char (the materializer's cells skip glyph-less chars). `pStart`=char leading edge on\n * the path, `end`=trailing edge, `rotation`=tangent at the char midpoint. */\nfunction layoutGlyphTextCharsAlongPath(node: PxNode, pathD: string, opts: Pick<GlyphMaterializeOptions, 'glyphs' | 'warnings'> & { alongPath?: GlyphCharBoxAlongPath }): Array<PxGlyphCharBox> {\n const { glyphs, warnings, alongPath } = opts;\n const sampler = createPathSampler(pathD);\n if (!sampler) { warnings?.push('textGlyphs: unparsable along-path geometry (caret)'); return []; }\n const soleFont = soleFontOf(glyphs);\n\n // Reading-order pass over leaf text (positioning attrs ignored — along-path is a\n // single run), recording each char's [advStart, advEnd], its glyph advance (WITHOUT\n // spacing) for the rotation midpoint, and bbox metrics.\n const chars: Array<{ advStart: number; advEnd: number; glyphW: number; ascent: number; fontSize: number }> = [];\n let adv = 0;\n const walk = (el: PxNode, parentStyle: Style): void => {\n const s = resolveStyle(el, parentStyle);\n const content = str(el[PX_TEXT_CONTENT_ATTR]);\n if (content && !el.children?.length) {\n const gf = glyphFontFor(s, glyphs, soleFont, warnings);\n const upm = gf?.unitsPerEm || 1000;\n const scale = s.fontSize / upm;\n const ascent = (gf?.ascent ?? 0.9 * upm) * scale;\n for (let i = 0; i < content.length; i++) {\n const ch = content.charAt(i);\n const g = gf?.glyphs[ch];\n const glyphW = (g ? g.width : 0) * scale;\n const advance = glyphW + s.letterSpacing + (ch === ' ' ? s.wordSpacing : 0);\n chars.push({ advStart: adv, advEnd: adv + advance, glyphW, ascent, fontSize: s.fontSize });\n adv += advance;\n }\n }\n if (el.children) for (const ch of el.children) walk(ch, s);\n };\n walk(node, rootStyleOf(node));\n\n const width = adv;\n // textLength (lengthAdjust=spacing): scale positions so the run spans textLength.\n // Static / frame-0 read — the caret is a static-frame layout (like startOffset below).\n const tlr = readAnimatable<number>(alongPath?.textLength);\n const tlv = tlr.kind === ReadKind.Animated ? (Number(tlr.keyframes[0]?.value) || 0)\n : tlr.kind === ReadKind.Static ? (Number(tlr.value) || 0) : 0;\n const k = (tlv > 0 && width > 0) ? tlv / width : 1;\n // startOffset base — static or frame-0 keyframe (matches the materializer's static place).\n // x/dx add along-path distance; dy shifts perpendicular (both mirror the materializer).\n const so = readAnimatable<number>(alongPath?.startOffset);\n const { along: alongOffset, perp } = alongPathNodeOffsets(node);\n const base = alongOffset + (so.kind === ReadKind.Animated ? (Number(so.keyframes[0]?.value) || 0)\n : so.kind === ReadKind.Static ? (Number(so.value) || 0) : 0);\n\n // Shift a sampled point perpendicular to the path (left normal) by `perp`.\n const withPerp = (p: { x: number; y: number; angle: number }) => ({\n x: p.x - perp * Math.sin(p.angle), y: p.y + perp * Math.cos(p.angle), angle: p.angle,\n });\n\n return chars.map(c => {\n const dStart = base + c.advStart * k;\n const dEnd = base + c.advEnd * k;\n const p0 = withPerp(sampler.sampleAtDistance(dStart));\n const p1 = withPerp(sampler.sampleAtDistance(dEnd));\n // Caret rotation = tangent at the GLYPH's own midpoint (advStart + glyphW/2), which\n // DISREGARDS the char's letter/word spacing. This keeps the synthetic caret aligned\n // with the baked glyph outline (which is placed at its glyph center), so letter\n // spacing doesn't add extra tilt to the caret.\n const glyphMid = sampler.sampleAtDistance(base + (c.advStart + c.glyphW / 2) * k);\n return {\n x: p0.x, y: p0.y,\n width: c.advEnd - c.advStart,\n ascent: c.ascent, fontSize: c.fontSize,\n endX: p1.x, endY: p1.y,\n rotation: glyphMid.angle * 180 / Math.PI,\n };\n });\n}\n\n\n// ── ALONG-PATH ──────────────────────────────────────────────────────────────\n\n/** One glyph in path order: its outline + geometry, and `midBase` = the arc-\n * distance from the text start (startOffset 0) to the glyph's advance midpoint. */\ninterface AlongCell { glyphD: string; widthEm: number; scale: number; paint: Paint; midBase: number; isMissing?: boolean; }\n\n/** Walks the tspans in reading order, accumulating advance (whitespace included)\n * so each rendered glyph gets its `midBase`. Positioning attrs are ignored —\n * along-path text is a single run. */\nfunction collectAlongPathCells(node: PxNode, glyphs: Record<string, PxGlyphFont>, soleFont: PxGlyphFont | undefined, warnings?: Array<string>): { cells: Array<AlongCell>; width: number } {\n const cells: Array<AlongCell> = [];\n let adv = 0;\n const walk = (el: PxNode, parentStyle: Style): void => {\n const s = resolveStyle(el, parentStyle);\n const content = str(el[PX_TEXT_CONTENT_ATTR]);\n // Only leaf text (no children) — a single-span line folds its text onto the\n // line-`<tspan>` AND keeps the child span; rendering both would duplicate it.\n if (content && !el.children?.length) {\n const gf = glyphFontFor(s, glyphs, soleFont, warnings);\n if (gf) {\n const scale = s.fontSize / gf.unitsPerEm;\n const ascentEm = gf.ascent ?? 0.9 * gf.unitsPerEm;\n const paint = paintOf(s);\n for (let i = 0; i < content.length; i++) {\n const ch = content.charAt(i);\n const g = gf.glyphs[ch];\n if (g && g.pathData) {\n const glyphAdv = g.width * scale;\n cells.push({ glyphD: g.pathData, widthEm: g.width, scale, paint, midBase: adv + glyphAdv / 2 });\n adv += glyphAdv;\n } else if (/\\S/.test(ch)) {\n // Missing glyph → a visible □ placeholder box (see missingGlyphBoxEm).\n const wEm = (g && g.width > 0) ? g.width : MISSING_GLYPH_ADVANCE_EM * gf.unitsPerEm;\n const boxAdv = wEm * scale;\n cells.push({ glyphD: missingGlyphBoxEm(wEm, ascentEm), widthEm: wEm, scale, paint, isMissing: true, midBase: adv + boxAdv / 2 });\n adv += boxAdv;\n } else {\n adv += (g ? g.width : 0) * scale; // whitespace: advance only\n }\n adv += s.letterSpacing + (ch === ' ' ? s.wordSpacing : 0);\n }\n }\n }\n if (el.children) for (const ch of el.children) walk(ch, s);\n };\n walk(node, rootStyleOf(node));\n return { cells, width: adv };\n}\n\n/** Affine placing a glyph so its mid-advance baseline sits at path-distance\n * `dist`, rotated to the tangent (scale baked in). `perp` shifts the glyph\n * PERPENDICULAR to the path (SVG `dy` on text-on-a-path), along the left normal\n * (−sinθ, cosθ) — in path/user units, NOT scaled by the glyph size. */\nfunction alongAffine(sampler: PathSampler, dist: number, scale: number, widthEm: number, perp = 0): Affine {\n const { x, y, angle } = sampler.sampleAtDistance(dist);\n const cos = Math.cos(angle), sin = Math.sin(angle), hw = widthEm / 2;\n return [scale * cos, scale * sin, -scale * sin, scale * cos, x - scale * cos * hw - perp * sin, y - scale * sin * hw + perp * cos];\n}\n\n/** Text-on-a-path offsets from the `<text>`/`<tspan>` x/dx/dy attributes (horizontal\n * writing mode; see the SVG \"text on a path\" layout rules):\n * • `x` and `dx` shift ALONG the path (both add to the startpoint distance),\n * • `dy` shifts PERPENDICULAR to the path,\n * • `y` is IGNORED (the path, not `y`, sets the cross-axis position).\n * Matches the browser's native `<textPath>` so our baked glyphs line up with it. */\nfunction alongPathNodeOffsets(node: PxNode): { along: number; perp: number } {\n return {\n along: (parseLen(node.x) ?? 0) + (parseLen(node.dx) ?? 0),\n perp: parseLen(node.dy) ?? 0,\n };\n}\n\n/** @internal */\nexport function materializeGlyphTextAlongPath<E = any>(\n node: PxNode,\n pathD: string | undefined,\n startOffset: PxAnimatable<number> | undefined,\n opts: GlyphMaterializeOptions<E>,\n textLength?: PxAnimatable<number>,\n pathOverflow?: string,\n): E | null {\n const { glyphs, create = jsonElementFactory as PxCreateElement<E>, warnings } = opts;\n const sampler = pathD ? createPathSampler(pathD) : null;\n if (!sampler) { warnings?.push('textGlyphs: unparsable along-path geometry'); return null; }\n\n const soleFont = soleFontOf(glyphs);\n const { cells, width } = collectAlongPathCells(node, glyphs, soleFont, warnings);\n if (!cells.length) return toGroup(node, [], create);\n\n // x/dx → extra distance ALONG the path (added to startOffset); dy → perpendicular.\n const { along: alongOffset, perp } = alongPathNodeOffsets(node);\n\n // Both drivers as piecewise-linear tracks. textLength (lengthAdjust=spacing) scales\n // each glyph's position along the path so the run spans `textLength(t)` (glyph\n // outlines keep their natural size): distance(t) = startOffset(t) + k(t)·midBase.\n const soTrack = numTrackOf(startOffset);\n const tlTrack = numTrackOf(textLength);\n const kOf = (tl: number): number => (tl > 0 && width > 0) ? tl / width : 1;\n\n // pathOverflow 'clip' (open path): a glyph whose mid-advance point falls past an\n // end disappears (native <textPath> semantics), vs 'extend' (default) where the\n // sampler continues along the tangent. See svga.text.path-overflow.plan.md.\n const isClip = pathOverflow === 'clip' && !sampler.closed;\n\n if (soTrack.animated || tlTrack.animated) {\n // startOffset and/or textLength animate → each glyph slides along the path: its\n // own <path> with sampled translate+rotate keyframes (no merge). Per merged-\n // timeline interval both tracks are linear, so the glyph distance is linear too.\n const times = mergeTrackTimes(soTrack.times, tlTrack.times);\n const distOf = (c: AlongCell, t: number): number => alongOffset + soTrack.at(t) + kOf(tlTrack.at(t)) * c.midBase;\n const loop = soTrack.animated ? soTrack.loop : tlTrack.loop;\n return toGroup(node, buildAnimatedAlongPath(cells, sampler, distOf, times, loop, create, isClip, perp), create);\n }\n\n // Static (or single-keyframe): place + bake; glyphs sharing paint still merge.\n const k = kOf(tlTrack.at(0));\n const base = alongOffset + soTrack.at(0);\n const placeCells = isClip\n ? cells.filter(c => { const d = base + c.midBase * k; return d >= 0 && d <= sampler.totalLength; })\n : cells;\n const placements: Array<Placement> = placeCells.map(c => ({\n glyphD: c.glyphD, paint: c.paint, isMissing: c.isMissing,\n m: alongAffine(sampler, base + c.midBase * k, c.scale, c.widthEm, perp),\n }));\n return toGroup(node, buildPaths(placements, create, warnings), create);\n}\n\n/** A `PxAnimatable<number>` as a clamped piecewise-LINEAR sampler: constant for\n * static / absent / single-keyframe, keyframe-interpolated when animated — the same\n * linear-per-interval interpretation the along-path baking has always used. */\ninterface NumTrack { animated: boolean; times: Array<number>; loop?: unknown; at(t: number): number; }\n\nfunction numTrackOf(raw: PxAnimatable<number> | undefined): NumTrack {\n const r = readAnimatable<number>(raw);\n if (r.kind === ReadKind.Animated && r.keyframes.length >= 2) {\n const kfs = [...r.keyframes].sort((k1, k2) => (Number(k1.time) || 0) - (Number(k2.time) || 0));\n const times = kfs.map(kf => Number(kf.time) || 0);\n const vals = kfs.map(kf => Number(kf.value) || 0);\n return {\n animated: true, times, loop: r.loop,\n at(t: number): number {\n if (t <= times[0]) return vals[0];\n for (let i = 1; i < times.length; i++) {\n if (t <= times[i]) {\n const span = times[i] - times[i - 1];\n const f = span > 0 ? (t - times[i - 1]) / span : 1;\n return vals[i - 1] + f * (vals[i] - vals[i - 1]);\n }\n }\n return vals[vals.length - 1];\n },\n };\n }\n const v = r.kind === ReadKind.Animated ? (Number(r.keyframes[0]?.value) || 0)\n : r.kind === ReadKind.Static ? (Number(r.value) || 0) : 0;\n return { animated: false, times: [], at: () => v };\n}\n\n/** Sorted union of two keyframe-time lists (deduped) — the merged animation timeline. */\nfunction mergeTrackTimes(a: Array<number>, b: Array<number>): Array<number> {\n const all = [...a, ...b].sort((t1, t2) => t1 - t2);\n const out: Array<number> = [];\n for (const t of all) if (!out.length || t !== out[out.length - 1]) out.push(t);\n return out;\n}\n\n\n// Sampling density for a glyph's motion-path keyframes: aim for ~≤ this many\n// steps across the whole path so linear interpolation follows the curve.\nconst ALONG_PATH_MAX_STEPS = 48;\nconst ALONG_PATH_MAX_STEPS_PER_SEGMENT = 64;\n\nfunction roundN(v: number, n: number): number {\n const f = 10 ** n;\n return Math.round(v * f) / f;\n}\n\n/** Builds a separate glyph element per glyph, its outline baked centered at the\n * origin (mid-advance baseline) so `animate.transform` translate+rotate places\n * it along the path over time. `distOf` gives the glyph's along-path distance at a\n * time (startOffset(t) + textLength-scale(t)·midBase + x/dx — both drivers merged\n * into `times`); each interval is sub-sampled so the glyph tracks a curved path.\n * Interval interp is linear (per-keyframe easing shaping isn't reproduced — a v1\n * limitation). */\nfunction buildAnimatedAlongPath<E>(\n cells: Array<AlongCell>,\n sampler: PathSampler,\n distOf: (c: AlongCell, t: number) => number,\n times: Array<number>,\n loop: unknown,\n create: PxCreateElement<E>,\n isClip: boolean,\n perp = 0,\n): Array<E> {\n const step = Math.max(sampler.totalLength / ALONG_PATH_MAX_STEPS, 0.5);\n const onPath = (dist: number): boolean => dist >= 0 && dist <= sampler.totalLength;\n\n const out: Array<E> = [];\n for (const c of cells) {\n const centered: Affine = [c.scale, 0, 0, c.scale, -c.scale * (c.widthEm / 2), 0];\n const d = transformPathData(c.glyphD, centered);\n\n const sampleKf = (dist: number, time: number): TransformKeyframe => {\n const { x, y, angle } = sampler.sampleAtDistance(dist);\n const cos = Math.cos(angle), sin = Math.sin(angle);\n // dy shifts perpendicular to the path (left normal), in path units.\n return {\n time,\n value: {\n [TransformPart.Translate]: [roundN(x - perp * sin, 3), roundN(y + perp * cos, 3)],\n [TransformPart.Rotate]: roundN(angle * 180 / Math.PI, 3),\n },\n };\n };\n\n const kfs: Array<TransformKeyframe> = [];\n // Clip: opacity 1 while the glyph's center is on the path, 0 off — the\n // sampling is dense (`step`), so the linear fade across one step reads as a\n // near-sharp pop (a robust stand-in for the hard native-<textPath> drop).\n const opKfs: Array<TransformKeyframe<number>> = [];\n const pushKf = (dist: number, time: number): void => {\n kfs.push(sampleKf(dist, time));\n if (isClip) opKfs.push({ time, value: onPath(dist) ? 1 : 0 });\n };\n\n pushKf(distOf(c, times[0]), times[0]);\n for (let k = 1; k < times.length; k++) {\n const t0 = times[k - 1], t1 = times[k];\n const d0 = distOf(c, t0), d1 = distOf(c, t1);\n const n = Math.min(ALONG_PATH_MAX_STEPS_PER_SEGMENT, Math.max(1, Math.ceil(Math.abs(d1 - d0) / step)));\n for (let s = 1; s <= n; s++) {\n const f = s / n;\n const t = t0 + f * (t1 - t0);\n pushKf(distOf(c, t), t);\n }\n }\n\n // Tangent angles come from atan2 and wrap at ±180° — crossing that seam (e.g.\n // the bottom of a circle) would otherwise lerp the ~358° long way between two\n // samples and visibly flip the glyph. Same fix as motion-path auto-orient.\n unwrapAutoOrientRotations(kfs);\n\n const transform: { keyframes: Array<TransformKeyframe>; loop?: unknown } = { keyframes: kfs };\n if (loop !== undefined) transform.loop = loop;\n const animate: { [k: string]: any } = { transform };\n // Only add an opacity track when it actually toggles (a glyph fully on-path\n // the whole time needs none).\n // Span-level animated paint rides along (dasharray, fill, …).\n if (c.paint.animate) Object.assign(animate, c.paint.animate);\n if (isClip && opKfs.some(k => k.value === 0)) {\n // The CLIP visibility track owns the opacity slot — two opacity animations\n // aren't expressible on one element, and visibility is what makes clip mode\n // work at all, so it overrides a span's own animated opacity.\n const op: { keyframes: Array<TransformKeyframe<number>>; loop?: unknown } = { keyframes: opKfs };\n if (loop !== undefined) op.loop = loop;\n animate.opacity = op;\n }\n\n out.push(create('path', { d, ...paintProps(c.paint), ...missingGlyphProps(c.isMissing), animate }, []));\n }\n return out;\n}\n\n\n// ── shared emit ───────────────────────────────────────────────────────────────\n\nfunction paintProps(paint: Paint): { [k: string]: any } {\n const p: { [k: string]: any } = {};\n if (paint.fill !== undefined) p.fill = paint.fill;\n if (paint.stroke !== undefined) p.stroke = paint.stroke;\n if (paint.strokeWidth !== undefined) p.strokeWidth = paint.strokeWidth;\n if (paint.opacity !== undefined) p.opacity = paint.opacity;\n for (const key of PAINT_STATIC_KEYS) {\n if (paint[key] !== undefined) p[key] = paint[key];\n }\n // NOT `animate` — it merges into the emitted `animate` bag at each emit site.\n return p;\n}\n\n/** Marks a □-placeholder `<path>` for consumers; nothing at all for real glyphs. */\nfunction missingGlyphProps(isMissing: boolean | undefined): { [k: string]: any } {\n return isMissing ? { [CLASS_ATTR]: MISSING_GLYPH_CLASS_NAME } : {};\n}\n\n/** Merges placements sharing paint into baked `<path>` elements. MISSING-glyph boxes merge\n * only with each other, so they end up in their own classed `<path>` (see\n * `MISSING_GLYPH_CLASS_NAME`) instead of being indistinguishable subpaths of a real one. */\nfunction buildPaths<E>(placements: Array<Placement>, create: PxCreateElement<E>, warnings?: Array<string>): Array<E> {\n if (!placements.length) { warnings?.push('textGlyphs: nothing to render'); return []; }\n\n const byPaint = new Map<string, { paint: Paint; d: string; isMissing?: boolean }>();\n for (const p of placements) {\n // Stable key across any paint value type (hex string, [r,g,b], gradient object).\n // The WHOLE paint — statics AND the animate bag — is the key (`paintOf` builds it\n // with a fixed field order): spans that differ in ANY paint aspect must not merge.\n const key = JSON.stringify([p.paint, !!p.isMissing]);\n const baked = transformPathData(p.glyphD, p.m);\n const entry = byPaint.get(key);\n if (entry) entry.d += baked;\n else byPaint.set(key, { paint: p.paint, d: baked, isMissing: p.isMissing });\n }\n\n const out: Array<E> = [];\n for (const { paint, d, isMissing } of byPaint.values()) {\n out.push(create('path', {\n d, ...paintProps(paint), ...missingGlyphProps(isMissing),\n // A span's ANIMATED paint rides the merged path as a standard animate bag.\n ...(paint.animate !== undefined ? { animate: { ...paint.animate } } : {}),\n }, []));\n }\n return out;\n}\n\n/** Builds the `<g>` that replaces the `<text>`: keeps its transform / id /\n * animate / opacity, drops text-specific attributes, holds the glyph elements. */\nfunction toGroup<E>(node: PxNode, children: Array<E>, create: PxCreateElement<E>): E {\n const gProps: { [k: string]: any } = {};\n for (const k of Object.keys(node)) {\n if (k === 'type' || k === 'children' || TEXT_ATTR_KEYS.indexOf(k) !== -1) continue;\n gProps[k] = (node as { [k: string]: any })[k];\n }\n if (gProps.style && typeof gProps.style === 'object') {\n const style = { ...(gProps.style as Record<string, unknown>) };\n delete style['white-space'];\n if (Object.keys(style).length) gProps.style = style; else delete gProps.style;\n }\n return create('g', gProps, children);\n}\n\n\n// ── editor-facing convenience + pipeline adapters ──────────────────────────────\n\n/** Single entry the EDITOR calls: materializes a glyph `<text>` node into the\n * factory's element type, choosing along-path when `alongPath` is given. * @internal\n */\nexport function materializeGlyphText<E = any>(\n node: PxNode,\n opts: GlyphMaterializeOptions<E> & { alongPath?: { pathD?: string; startOffset?: PxAnimatable<number>; textLength?: PxAnimatable<number>; pathOverflow?: string } },\n): E | null {\n if (opts.alongPath) return materializeGlyphTextAlongPath(node, opts.alongPath.pathD, opts.alongPath.startOffset, opts, opts.alongPath.textLength, opts.alongPath.pathOverflow);\n return materializeGlyphTextHorizontal(node, opts);\n}\n\n/** Pipeline adapter (plain wire nodes) — `effects.text.useGlyphs`, horizontal. */\nexport function applyTextGlyphsEffect(node: PxNode, fx: PxTextEffect | undefined, ctx: ApplyContext): PxNode {\n if (!fx?.useGlyphs) return node;\n if (!ctx.glyphs) { ctx.warnings.push('textGlyphs: no definitions.fonts — left as native <text>'); return node; }\n return materializeGlyphTextHorizontal<PxNode>(node, { glyphs: ctx.glyphs, warnings: ctx.warnings });\n}\n\n/** Pipeline adapter (plain wire nodes) — glyph text along a referenced path. */\nexport function applyTextGlyphsAlongPath(node: PxNode, ctx: ApplyContext, pathD: string | undefined, startOffset: PxAnimatable<number> | undefined, textLength?: PxAnimatable<number>, pathOverflow?: string): PxNode | null {\n if (!ctx.glyphs) { ctx.warnings.push('textGlyphs: no definitions.fonts'); return null; }\n return materializeGlyphTextAlongPath<PxNode>(node, pathD, startOffset, { glyphs: ctx.glyphs, warnings: ctx.warnings }, textLength, pathOverflow);\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// ONE diagnostics channel for every player (API review §5, §25.1).\n//\n// Before this, only React Native had an error channel (`onError` + `fallback`). The web player\n// sent a failed fetch or an invalid document to `console.error` and then answered\n// `isReady() === false` for ever, and React and Vue could not offer anything at all because\n// the engine callbacks had no slot for it. Everything else — validation warnings, config\n// overrides, unsupported attributes — went straight to `console.warn`, where an embedding app\n// could neither see it nor quiet it.\n//\n// THE RULE — two severities, one meaning each, on every player:\n//\n// - `onError`: THIS INSTANCE WILL NOT PLAY. Nothing is rendered, `isReady()` stays false,\n// the component shows its `fallback`. The player reports it and stays inert rather than\n// throwing at the caller — a throw would land in a fetch callback or a render, where no\n// one can catch it. (A wrong CALL — `createAnimator()` with neither `src` nor `doc` — still\n// throws: that is a bug at the call site, found the moment the line runs.)\n// - `onWarn`: IT PLAYS, but something was ignored, degraded or misspelled — an unknown\n// easing, an override that could not apply, an attribute the platform will not animate.\n//\n// - a handler takes over from the console: give `onWarn` / `onError` and the console stays\n// out of it; give none and the console is the fallback, so nothing is lost by default;\n// - `muteWarn` / `muteError` switch that console fallback off — for a host that knows\n// about the warnings and is prepared to tolerate them. A handler you passed still fires.\n//\n// Severity (warn vs error) and SOURCE (`kind`) are separate axes on purpose: an invalid document\n// is a document problem AND fatal, while an effects-shape warning is a document problem that\n// still plays. Splitting the callbacks by source would have produced four handlers and forced\n// anyone who just wants everything to wire all of them.\n//\n// THE TEXT IS NOT SHIPPED. A diagnostic carries a NUMBER (`PxDiagnosticCode`) and the values the\n// site had (`data`); the words live in docs/diagnostics.md, generated from the enum's comments,\n// and every diagnostic links to it. See PxDiagnosticCode.ts for why, and for the rules on\n// adding one.\n\nimport type { PxDiagnosticCode } from './PxDiagnosticCode';\n\n/**\n * Who can do something about a diagnostic.\n *\n * A const object rather than a TypeScript `enum`: consumers compare against these values, so\n * they must accept plain string literals too (the same pattern as `PxControlMode`).\n * @public\n */\nexport const PxDiagnosticKind = {\n /** The document is wrong — regenerate or repair the file. */\n document: 'document',\n /** The page or app cannot provide what the document asks for — fix the mount. */\n host: 'host',\n /** The platform cannot do it and the player degraded — usually nothing to fix. */\n platform: 'platform',\n /** The call is wrong or self-contradictory — fix the options or props you passed. */\n usage: 'usage',\n /** The player failed where it did not expect to — report it to us. */\n internal: 'internal',\n} as const;\nexport type PxDiagnosticKind = typeof PxDiagnosticKind[keyof typeof PxDiagnosticKind];\n\n/** Where the words behind a code live. One string, shared by every diagnostic. */\nconst DOCS_URL = 'https://github.com/pixodesk/pixodesk-svg-animator/blob/main/docs/diagnostics.md';\n\n/** `PX1204 https://…/diagnostics.md#px1204` — the code, and where to read what it means. */\nfunction codeLine(code: PxDiagnosticCode): string {\n return 'PX' + code + ' ' + DOCS_URL + '#px' + code;\n}\n\n/**\n * One thing a player has to say.\n *\n * The TEXT is not here, and is not in the bundle: `code` identifies the diagnostic and\n * {@link https://github.com/pixodesk/pixodesk-svg-animator/blob/main/docs/diagnostics.md the\n * codes page} carries the description. That is the trade this library makes — a smaller\n * download, and a stable number you can switch on instead of matching a sentence that may be\n * reworded. The specifics are in `data`.\n * @public\n */\nexport interface PxDiagnostic {\n /** WHICH diagnostic this is — a permanent number; look it up on the codes page. */\n readonly code: PxDiagnosticCode;\n /** Who can act on it — see {@link PxDiagnosticKind}. */\n readonly kind: PxDiagnosticKind;\n /**\n * What the site had to hand, in the order the code's `@data` lists: a selector, a URL, the\n * offending binding, an inner problem. Everything a sentence would have interpolated.\n */\n readonly data?: ReadonlyArray<unknown>;\n /** The code and a link to its description — NOT the description itself, which is not shipped. */\n readonly message: string;\n /** Present on errors: the Error that stopped the player. */\n readonly error?: Error;\n}\n\n/**\n * Where a player sends what it wants to say. Every field is optional.\n *\n * The SHARED base of every callbacks object (dev-docs/reviews/api-surface-review.md §26.1): `createDiagnostics` reads it directly,\n * `PxEngineCallbacks` extends it with the playback lifecycle, `PxAnimatorCallbacks` adds `onStop`\n * on top — so the four diagnostics fields are spelled once, here.\n * @public\n */\nexport interface PxDiagnosticsConfig {\n\n /**\n * IT PLAYS, but something was ignored, degraded or misspelled — an unknown easing, an\n * override that could not apply, an attribute the platform will not animate. Each\n * diagnostic says WHAT happened via `code` (a number — look it up on the codes page) and\n * WHO can act on it via `kind` (`document` / `host` / `platform` / `usage` / `internal`);\n * `data` carries the values the site had. Without this: `console.warn`.\n */\n onWarn?: (diagnostic: PxDiagnostic) => void;\n\n /**\n * THIS INSTANCE WILL NOT PLAY — the document failed to load, parse or build, or the render\n * threw: nothing rendered, `isReady()` false, the component's `fallback` shown. The player\n * stays inert rather than throwing at the caller. `diagnostic.error` is the Error; on React\n * Native `diagnostic.data` carries the component stack when the error boundary caught it.\n * Without this: `console.error`.\n */\n onError?: (diagnostic: PxDiagnostic) => void;\n\n /**\n * Switch the `console.warn` fallback off. For a host that knows the player has something\n * to say about this document and is prepared to tolerate it — a chatty player is not what\n * an end user's console is for. A handler you passed (`onWarn`) still fires: mute is\n * about the console, not about you.\n */\n muteWarn?: boolean;\n\n /** The same switch for the `console.error` fallback. `onError` still fires. */\n muteError?: boolean;\n}\n\n/** The reporting channel a player writes to. @public */\nexport interface PxDiagnostics {\n /** Report something survivable — it plays. `data` is whatever the code's `@data` names. */\n warn(kind: PxDiagnosticKind, code: PxDiagnosticCode, ...data: Array<unknown>): void;\n /**\n * Report a failure that stopped this instance — it will not play. An `Error` anywhere in\n * `data` becomes the diagnostic's `error`, so a site can pass it wherever it reads best.\n */\n error(kind: PxDiagnosticKind, code: PxDiagnosticCode, ...data: Array<unknown>): void;\n}\n\n/** The Error a site passed, if it passed one — handlers get it on `error` as well as in `data`. */\nfunction errorIn(data: ReadonlyArray<unknown>): Error | undefined {\n return data.find((d): d is Error => d instanceof Error);\n}\n\n/**\n * Builds the channel a player reports through.\n *\n * `prefix` labels the console fallback (e.g. `'[PixodeskSvgAnimator]'`) and is NOT added to the\n * diagnostic handed to a handler — a caller that wants to prefix its own log can, and one\n * feeding a UI should not have to strip ours.\n * @internal\n */\nexport function createDiagnostics(config?: PxDiagnosticsConfig, prefix?: string): PxDiagnostics {\n const tag = prefix ? prefix + ' ' : '';\n return {\n warn: (kind: PxDiagnosticKind, code: PxDiagnosticCode, ...data: Array<unknown>): void => {\n const message = codeLine(code);\n if (config?.onWarn) { config.onWarn({ code, kind, data, message }); return; }\n if (config?.muteWarn) return;\n console.warn(tag + kind + ' ' + message, ...data);\n },\n error: (kind: PxDiagnosticKind, code: PxDiagnosticCode, ...data: Array<unknown>): void => {\n const message = codeLine(code);\n const error = errorIn(data);\n if (config?.onError) { config.onError({ code, kind, data, message, error }); return; }\n if (config?.muteError) return;\n console.error(tag + kind + ' ' + message, ...data);\n },\n };\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n/**\n * THE ENTRY DIAGNOSTIC — how a consumer whose build renamed our keys finds out.\n *\n * Property mangling across the document boundary fails SILENTLY: the keys no longer match, the\n * player reads an empty configuration rather than an invalid one, and the animation renders a\n * blank canvas with nothing in the console. That is the failure this exists to name.\n *\n * It is a DIAGNOSTIC, not a gate. Nothing is rejected — the player still does its best with\n * whatever it can read, exactly as before.\n *\n * What it can and cannot see (dev-docs/plans/minification-boundary.md §3):\n * - keyframes, the animator block and every effect are strict `px.object`, so an unexpected\n * key there IS detectable;\n * - nodes are `px.openObject`, so an unknown attribute on a node is indistinguishable from a\n * legitimate SVG attribute. The diagnostic therefore keys off the strict parts.\n */\nimport { validateDocument } from './PxAnimatorTypes';\n\n/** How many problems to print before summarizing the rest. A wall of text gets scrolled past. */\nconst MAX_REPORTED = 6;\n\n/**\n * A document's schema findings. The pre-2026-09 FLAT animator spelling is NOT a category of its\n * own: those keys are no longer read (`getAnimatorConfig` drops them), so a document still\n * carrying them is reported like any other unrecognized key — silence would hide a file that\n * plays with its playback settings ignored.\n * @public @advanced\n */\nexport interface PxDocumentDiagnosis {\n /** Findings worth showing — unrecognized keys and shape violations. */\n problems: Array<string>;\n}\n\n/** Pure: collect a document's schema findings. Never throws. @public @advanced */\nexport function diagnoseDocument(doc: unknown): PxDocumentDiagnosis {\n try {\n return { problems: validateDocument(doc) };\n } catch {\n return { problems: [] }; // a diagnostic must never be the thing that breaks\n }\n}\n\n/**\n * Runs the diagnosis and reports it on the console, naming property mangling as the likely cause\n * — a bare \"unexpected extra key\" leaves the reader no wiser, which is the whole point.\n *\n * `where` names the entry the document came in through, so the message says which call to look at.\n * @internal\n */\nexport function reportDocumentDiagnostics(doc: unknown, where: string): void {\n const { problems } = diagnoseDocument(doc);\n if (!problems.length) return;\n\n const shown = problems.slice(0, MAX_REPORTED);\n const more = problems.length - shown.length;\n console.warn(\n where + ': this document does not match the animation schema in '\n + problems.length + ' place' + (problems.length === 1 ? '' : 's') + '.\\n'\n + shown.map(p => ' - ' + p).join('\\n')\n + (more > 0 ? '\\n … and ' + more + ' more' : '')\n + '\\n\\nIf you did not author these keys, the usual cause is a build that MANGLES PROPERTY '\n + 'NAMES. An animation document is data loaded at runtime, so renaming the property reads '\n + 'inside the player stops them matching the keys in the JSON, and the animation silently '\n + 'does nothing. Feed the published reserved-name list to your minifier — '\n + '@pixodesk/svg-animator-web/mangle-reserved.json — see docs/library/minification.md.');\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\nimport { keyframeWith, partsRecord, ReadKind, readAnimatable, TransformPart } from '../shared/transformParts';\nimport type { PxAnimatable, PxNode, PxTransformByEffect, PxVec2 } from '../../format/PxAnimatorTypes';\nimport type { ApplyContext } from '../shared/types';\n\n\n/**\n * TRANSFORMATION → one `<g>` wrapper per part (translate/rotate/scale/skew),\n * with origin emitted as SEPARATE `+origin` / `-origin` wrappers flanking the\n * rotate/scale pair.\n *\n * Per-wrapper kfs:\n * - each wrapper carries ONE animatable quantity, so animated origin / rotate /\n * scale all play correctly per their own keyframe timelines — nothing is\n * baked into another part's wrapper.\n * - the `+o · ... · -o` sandwich pivots rotate+scale around the (possibly\n * animated) origin; translates compose flat (commute).\n *\n * Wrapper nesting (outer → inner):\n *\n * translate → +origin → rotate → scale → -origin → skew → element\n *\n * Composition: `M = translate(t) · +origin(t) · rotate(t) · scale(t) · -origin(t) · skew`\n * (skew is appended innermost and not composed here).\n */\nexport function applyTransformByEffect(node: PxNode, fx: PxTransformByEffect | undefined, ctx: ApplyContext): PxNode {\n if (!fx) return node;\n\n // The element's own `transform` string is the frame-0 baseline; wrappers\n // carry the full (animated) transform, so the baseline is dropped.\n delete node.transform;\n\n // Innermost first. Order outer→inner: [+o, t, -o, +o, r, skew, s, -o] for\n // auto-orient (translate carries motion-path rotation that must pivot around\n // origin too), else [t, +o, r, skew, s, -o] (translate composes flat).\n // Skew sits BETWEEN rotate and scale, inside the origin sandwich — the canonical\n // slot shared with the unified body transform and Lottie (see skew-support.plan.md).\n let n = node;\n n = wrapOrigin(n, fx.origin, /*invert=*/true); // -origin (r/k/s sandwich)\n n = wrapTransformPart(n, TransformPart.Scale, normalizeScale(fx.scale), ctx);\n n = wrapTransformPart(n, TransformPart.Skew, fx.skew, ctx);\n n = wrapTransformPart(n, TransformPart.Rotate, fx.rotate, ctx);\n n = wrapOrigin(n, fx.origin, /*invert=*/false); // +origin (r/k/s sandwich)\n\n if (translateHasAutoOrient(fx.translate)) {\n // Sandwich translate with its own +o/-o so the motion-path tangent\n // rotation built into the translate wrapper pivots around origin\n // (the `[+o, t_path_ao, -o]` branch).\n n = wrapOrigin(n, fx.origin, /*invert=*/true); // -origin (translate sandwich)\n n = wrapTransformPart(n, TransformPart.Translate, fx.translate, ctx);\n n = wrapOrigin(n, fx.origin, /*invert=*/false); // +origin (translate sandwich)\n } else {\n n = wrapTransformPart(n, TransformPart.Translate, fx.translate, ctx);\n }\n\n // The OUTERMOST wrapper takes the element's id (B4/A1): an id names the WHOLE\n // transformed unit — same law as `repeaterEffect`, the editor's heavy render,\n // and `ctx.idMap` (which already points at the outer wrapper). Leaving it on\n // the core makes a live `<use href>` / the maskedBy-generated `<use>` resolve to\n // the UNtransformed element in the DOM.\n if (n !== node && node.id) {\n n.id = node.id;\n delete node.id;\n }\n return n;\n}\n\n/** True when the translate animation carries motion-path tangent handles or\n * `autoOrient` — the path-tangent rotation needs origin-sandwich to pivot. */\nfunction translateHasAutoOrient(translate: PxAnimatable<PxVec2> | undefined): boolean {\n if (!translate || typeof translate !== 'object') return false;\n const obj = translate as { autoOrient?: boolean; keyframes?: Array<{ tangentOut?: PxVec2; tangentIn?: PxVec2 }> };\n if (obj.autoOrient) return true;\n return Array.isArray(obj.keyframes) && obj.keyframes.some(kf => kf.tangentOut || kf.tangentIn);\n}\n\n/**\n * The wire `effects.transformBy.scale` is a FACTOR (1.5 = 150%) in every form —\n * bare static, `{value:…}` and `{keyframes:…}` alike (one convention, see\n * dev-docs/schema-design.md I-3; the old bare-static PERCENT form is gone) — so no\n * normalization is needed any more. Kept as a named identity so the call site\n * still documents the convention decision.\n */\nfunction normalizeScale(raw: PxAnimatable<PxVec2> | undefined): PxAnimatable<PxVec2> | undefined {\n return raw;\n}\n\n/** Wraps `inner` in a `<g>` carrying a single transform part, static or animated. */\nfunction wrapTransformPart(\n inner: PxNode, part: TransformPart,\n raw: PxAnimatable<any> | undefined, ctx: ApplyContext\n): PxNode {\n if (raw === undefined) return inner;\n\n const v = readAnimatable<any>(raw);\n if (v.kind === ReadKind.Static) {\n return { type: 'g', transform: { value: partsRecord(part, v.value, undefined) }, children: [inner] };\n }\n if (v.kind === ReadKind.Animated) {\n const animTr: any = { keyframes: v.keyframes.map(kf => keyframeWith(kf, partsRecord(part, kf.value, undefined))) };\n if (v.autoOrient) animTr.autoOrient = true;\n if (v.loop !== undefined) animTr.loop = v.loop;\n return {\n type: 'g',\n animate: { transform: animTr },\n children: [inner],\n };\n }\n return inner;\n}\n\n/**\n * Wraps `inner` in a `<g translate>` that shifts by `+origin` (invert=false) or\n * `-origin` (invert=true). Origin is animatable — keyframes are carried through.\n *\n * Emitted as a `{translate}` PartsRecord (not the `{origin}` field) so a `+o`\n * wrapper is just a plain translate in the walker's eyes: the walker composes\n * the origin-sandwich rotation/scale around origin by stacking the wrappers,\n * NOT by reading `origin` off the rotate/scale wrapper's parts record.\n */\nfunction wrapOrigin(inner: PxNode, raw: PxAnimatable<PxVec2> | undefined, invert: boolean): PxNode {\n if (raw === undefined) return inner;\n const v = readAnimatable<PxVec2>(raw);\n const sign = (value: PxVec2): PxVec2 => invert ? [-value[0], -value[1]] : value;\n\n if (v.kind === ReadKind.Absent) return inner;\n if (v.kind === ReadKind.Static) {\n if (v.value[0] === 0 && v.value[1] === 0) return inner; // identity — skip\n return { type: 'g', transform: { value: { translate: sign(v.value) } }, children: [inner] };\n }\n if (v.kind === ReadKind.Animated) {\n const animTr: any = { keyframes: v.keyframes.map(kf => keyframeWith(kf, { translate: sign(kf.value as PxVec2) })) };\n if (v.loop !== undefined) animTr.loop = v.loop;\n return {\n type: 'g',\n animate: { transform: animTr },\n children: [inner],\n };\n }\n return inner;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\n/**\n * CONTENT-REF SOURCE SPLIT.\n *\n * When a `<use>` references another element with `clone:{without:'translate'}` it wants\n * to render the source EXCLUDING the source's own translate (and along-path\n * positioning for auto-orient). The heavy form achieves this by materializing the\n * source as a wrapper tree\n *\n * <g translate> ← outer, holds translate (+ along-path/origin for autoOrient)\n * <g rotate scale +o-o> ← inner, holds rotate/scale (origin sandwich)\n * <element /> ← bare shape, no transform\n * </g>\n * </g>\n *\n * and points the `<use>` at the INNER `<g>`. The use's reference therefore\n * renders rotate/scale around the use's position — never the source's translate.\n *\n * In the lightweight format the source is emitted as a flat element (translate\n * baked onto the body or into `effects.transformBy`). The applier's\n * `splitForContentRef` re-creates the multi-layer structure on the fly:\n * 1. extract translate parts from the source body (`transform` string,\n * `animate.transform.keyframes` PartsRecord) and from\n * `effects.transformBy` → outer wrapper\n * 2. keep rotate / scale (with origin sandwich) on the inner wrapper\n * 3. assign the ORIGINAL id to the outer, a fresh id to the inner\n * 4. `applyRefAndTransformationEffect` then rewrites the use's `href`\n * to the inner id (see `ctx.contentRefInnerIds`).\n *\n * Auto-orient / motion-path: when the translate animation carries tangent\n * handles (`tangentOut`/`tangentIn`) or `autoOrient`, the path tangent produces\n * a rotation at the OUTER level — so the origin moves to the outer too, so the\n * tangent rotation pivots around it (the `[+o, t_path_ao, -o]` branch).\n *\n * \"Always-split\" simplicity: even when a layer would be empty (e.g. source has\n * no rotate/scale), the inner wrapper is still emitted as an identity `<g>`.\n * The use needs a stable target regardless of which transform parts the source\n * carries, and an extra empty `<g>` is render-neutral.\n */\n\nimport { applyTransformByEffect } from '../transform/transformationEffect';\nimport type { PxAnimatable, PxAnimationDefinition, PxKeyframe, PxNode, PxTransformByEffect, PxVec2 } from '../../format/PxAnimatorTypes';\nimport type { ApplyContext } from '../shared/types';\nimport { stripHash } from '../shared/util';\n\n\n/** Walks the tree and collects every id referenced by a `<use>` with `clone:{without:'translate'}`. */\nexport function identifyContentRefTargets(node: PxNode, ctx: ApplyContext, allocator: (key: string) => string): void {\n if (node.type === 'use' && node.effects?.clone?.without === 'translate') {\n const sourceId = stripHash(node.effects.clone.source); // `#id` canonical, bare legacy (E-5)\n if (typeof sourceId === 'string' && sourceId && !ctx.contentRefInnerIds.has(sourceId)) {\n ctx.contentRefInnerIds.set(sourceId, allocator(sourceId));\n }\n }\n node.children?.forEach(c => identifyContentRefTargets(c, ctx, allocator));\n}\n\n\n/**\n * Splits `node` into outer-translate + inner-rest + bare element. `node` is\n * mutated in place: its body translate is moved to the outer wrapper, leaving\n * the rotate/scale parts (and the element body) inside the inner wrapper.\n *\n * Outer wrapper gets `originalId` (so `idMap` and whole-element refs still\n * resolve correctly). Inner wrapper gets `innerId` (the use's `ref:content`\n * target). The returned node is the OUTER wrapper.\n */\nexport function splitForContentRef(\n node: PxNode,\n transformBy: PxTransformByEffect | undefined,\n originalId: string,\n innerId: string,\n ctx: ApplyContext,\n): PxNode {\n\n // 1. Lift translate parts off the body (`transform` string + `animate.transform`)\n // onto a fresh outer-side bag. After this, `node` no longer carries them.\n // `transformBy` is passed in so that when its `translate` part will go to\n // the outer wrapper, the body's baked-in translate baseline is stripped too\n // (otherwise it'd double-up against the outer wrapper's first keyframe).\n const outerBody = liftBodyTranslate(node, transformBy);\n\n // Strip the bare element's `id` — the outer wrapper takes ownership of `originalId`,\n // and the inner wrapper carries `innerId`. The element itself doesn't need either;\n // leaving it would create a duplicate of `originalId` in the materialized tree.\n if (typeof node.id === 'string') delete node.id;\n\n // 2. Split `effects.transformBy` between outer (translate, plus origin for\n // auto-orient/motion-path) and inner (rotate / scale, with origin sandwich).\n const { outer: outerTr, inner: innerTr } = splitTransformByEffect(transformBy);\n\n // 3. Inner: apply inner transformation around the original element, then wrap\n // in an identity `<g>` with `innerId`. The extra wrapper guarantees the use\n // has a stable target regardless of what parts were present.\n let innerNode: PxNode = node;\n innerNode = applyTransformByEffect(innerNode, innerTr, ctx);\n const innerWrapper: PxNode = { type: 'g', id: innerId, children: [innerNode] };\n\n // 4. Outer: build a `<g>` carrying the lifted body translate, with `innerWrapper`\n // as its child. Then apply outer transformation (translate). Move id to the\n // outermost so whole-element refs still resolve to the full source.\n let outerWrapper: PxNode = { type: 'g', id: originalId, children: [innerWrapper] };\n if (outerBody.transform !== undefined) outerWrapper.transform = outerBody.transform;\n if (outerBody.animate !== undefined) outerWrapper.animate = outerBody.animate;\n if (outerTr) {\n delete outerWrapper.id;\n outerWrapper = applyTransformByEffect(outerWrapper, outerTr, ctx);\n outerWrapper.id = originalId;\n }\n return outerWrapper;\n}\n\n\n////////////////////////////////////////////////////////////////\n//// Body lifting (transform string + animate.transform)\n////////////////////////////////////////////////////////////////\n\ninterface OuterBody {\n transform?: string | { value?: any; keyframes?: Array<any> };\n animate?: { transform: any };\n}\n\n/** Removes translate parts from `node.transform` (string) and from `node.animate.transform`,\n * returning what was extracted (to live on the outer wrapper). When either\n * animate.transform is lifted OR `effects.transformBy.translate` will be lifted\n * via the outer wrapper, the body `transform` translate becomes a redundant t=0\n * baseline and is stripped from `node` (otherwise it'd compose on top of the\n * outer's first keyframe / static translate). */\nfunction liftBodyTranslate(node: PxNode, transformBy: PxTransformByEffect | undefined): OuterBody {\n const out: OuterBody = {};\n\n // 1. animate.transform — extract translate from every keyframe value.\n // `autoOrient` and `tangentOut`/`tangentIn` are motion-path metadata that\n // belong to translate; they go to the OUTER wrapper with translate, never\n // to the inner one (the inner has rotate/scale only).\n let didLiftAnimate = false;\n // In-place animations on a node body are always the record form\n // (`{propName: PxPropertyAnimation}`) at this point in the pipeline —\n // narrow the `PxElementAnimation` union accordingly.\n const animTr = (node.animate as PxAnimationDefinition | undefined)?.transform;\n if (animTr && typeof animTr === 'object' && Array.isArray(animTr.keyframes)) {\n const kfs: Array<PxKeyframe<any>> = animTr.keyframes;\n const hasTranslate = kfs.some(kf => kf.value && (kf.value as any).translate);\n if (hasTranslate) {\n const outerHasOrigin = needsOriginOnOuter(animTr as PxAnimatable<PxVec2>);\n const outerKfs = kfs.map(kf => {\n const v = (kf.value || {}) as any;\n const newValue: any = {};\n if (v.translate !== undefined) newValue.translate = v.translate;\n if (outerHasOrigin && v.origin !== undefined) newValue.origin = v.origin;\n const outerKf: any = { value: newValue };\n if (kf.time !== undefined) outerKf.time = kf.time;\n if (kf.easing !== undefined) outerKf.easing = kf.easing;\n if ((kf as any).tangentOut !== undefined) outerKf.tangentOut = (kf as any).tangentOut;\n if ((kf as any).tangentIn !== undefined) outerKf.tangentIn = (kf as any).tangentIn;\n return outerKf;\n });\n const outerAnimTr: any = { keyframes: outerKfs };\n if ((animTr as any).autoOrient) outerAnimTr.autoOrient = true;\n // Forward `loop` so the split outer/inner halves keep the source\n // alternate/cycle semantics; otherwise lifting a transform with\n // `loop.alternate` silently drops the loop on translate side.\n const srcLoop = (animTr as any).loop;\n if (srcLoop !== undefined) outerAnimTr.loop = srcLoop;\n out.animate = { transform: outerAnimTr };\n\n const innerHasPivotedPart = kfs.some(kf => {\n const v = (kf.value || {}) as any;\n return v.rotate !== undefined || v.scale !== undefined;\n });\n const innerKfs = kfs.map(kf => {\n const v = (kf.value || {}) as any;\n const newValue: any = {};\n if (v.rotate !== undefined) newValue.rotate = v.rotate;\n if (v.scale !== undefined) newValue.scale = v.scale;\n // Origin lives on inner kfs whenever rotate/scale need a pivot —\n // a separate origin sandwich at the inner layer (even when origin\n // is also on outer for the auto-orient sandwich).\n if (v.origin !== undefined && (!outerHasOrigin || innerHasPivotedPart)) newValue.origin = v.origin;\n // Inner kf intentionally drops tangentOut/tangentIn (translate-only).\n const innerKf: any = { value: newValue };\n if (kf.time !== undefined) innerKf.time = kf.time;\n if (kf.easing !== undefined) innerKf.easing = kf.easing;\n return innerKf;\n });\n const allInnerEmpty = innerKfs.every(kf => Object.keys(kf.value as object).length === 0);\n if (allInnerEmpty) {\n delete (node.animate as any).transform;\n if (node.animate && Object.keys(node.animate).length === 0) delete node.animate;\n } else {\n // Inner animate keeps non-translate kfs; autoOrient flag is intentionally dropped.\n // `loop` is forwarded so the inner rotate/scale half also alternates/cycles.\n const innerAnimTr: any = { keyframes: innerKfs };\n if (srcLoop !== undefined) innerAnimTr.loop = srcLoop;\n (node.animate as any).transform = innerAnimTr;\n }\n didLiftAnimate = true;\n }\n }\n\n // 2. Body `transform` — the composed STRING (pre-rendered forms / legacy /\n // foreign SVG) or the STRUCTURED STATIC `{value: partsRecord}` (the\n // lightweight wire since SCHEMA-DESIGN S1). `{keyframes}` bodies come\n // from the writer's transformation-effect path and are handled via\n // `effects.transformBy`, not here.\n const transformationHasTranslate = transformBy?.translate !== undefined;\n const stripBodyTranslateOnly = didLiftAnimate || transformationHasTranslate;\n // Autoorient / motion-path lift: when the lifted animate.transform carries\n // tangents or `autoOrient`, the body baseline is the FULL t=0 matrix\n // (translate × path-tangent rotation), not just a translate. The outer\n // wrapper recomputes both at every frame (including t=0) via the lifted\n // keyframes + autoOrient, so the body baseline is redundant and would\n // double-apply on top of it.\n const liftedAnimateIsAutoOriented = didLiftAnimate && needsOriginOnOuter((node.animate as PxAnimationDefinition | undefined)?.transform as any || undefined)\n || didLiftAnimate && needsOriginOnOuter((out.animate as PxAnimationDefinition | undefined)?.transform as any || undefined);\n if (typeof node.transform === 'string') {\n const split = splitTransformString(node.transform);\n if (stripBodyTranslateOnly) {\n // Outer wrapper will carry the translate (via animate.transform or\n // effects.transformBy); the body string is just a t=0 baseline.\n // Strip body translates so they don't double-up.\n if (split.translate !== undefined) {\n if (split.rest) node.transform = split.rest;\n else delete node.transform;\n } else if (isPureTranslateBody(node.transform)) {\n // Body is a single `matrix(...)` representing pure translate — same\n // redundancy as a `translate(...)` string. Wipe.\n delete node.transform;\n } else if (liftedAnimateIsAutoOriented && isSingleMatrixBody(node.transform)) {\n // Body is a non-pure `matrix(…)` — the autoOrient-materialized t=0\n // value baked by the writer. The outer wrapper reproduces it via\n // `animate.transform` + `autoOrient`; wipe to avoid double-apply.\n delete node.transform;\n }\n } else if (split.translate) {\n out.transform = split.translate;\n if (split.rest) node.transform = split.rest;\n else delete node.transform;\n }\n } else if (node.transform && typeof node.transform === 'object' && !Array.isArray(node.transform)\n && !(node.transform as any).keyframes) {\n // STATIC RECORD — bare `{translate, …}` (canonical) or the legacy\n // `{value: {…}}` wrapper; the rewrite keeps the incoming spelling.\n const wrapped = (node.transform as { value?: Record<string, unknown> }).value;\n const isWrapped = !!(wrapped && typeof wrapped === 'object');\n const value = (isWrapped ? wrapped : node.transform) as Record<string, unknown>;\n const rewrap = (rec: Record<string, unknown>) => (isWrapped ? { value: rec } : rec) as any;\n if (Array.isArray((value as any).translate)) {\n const rest: Record<string, unknown> = { ...value };\n delete rest.translate;\n const hasRest = Object.keys(rest).length > 0;\n if (stripBodyTranslateOnly) {\n // Same redundancy rule as the string branch: the outer wrapper\n // carries the translate; drop the body's copy.\n if (hasRest) node.transform = rewrap(rest);\n else delete node.transform;\n } else {\n out.transform = rewrap({ translate: (value as any).translate });\n if (hasRest) node.transform = rewrap(rest);\n else delete node.transform;\n }\n }\n }\n\n return out;\n}\n\n/** True when the body string is a single `matrix(...)` op (any 6-arg matrix —\n * pure-translate is a more specific case handled by `isPureTranslateBody`). */\nfunction isSingleMatrixBody(s: string): boolean {\n const re = /(translate|rotate|scale|matrix|skewX|skewY)\\(([^)]*)\\)/g;\n let count = 0;\n let isMatrix = false;\n let m: RegExpExecArray | null;\n while ((m = re.exec(s))) {\n count++;\n if (m[1] === 'matrix') isMatrix = true;\n }\n return count === 1 && isMatrix;\n}\n\n/** True when the body transform is a single op equivalent to pure translate\n * (either `translate(...)` or a `matrix(1,0,0,1,e,f)`). Used to decide whether\n * to wipe the body when `animate.transform.translate` has already been lifted. */\nfunction isPureTranslateBody(s: string): boolean {\n const re = /(translate|rotate|scale|matrix|skewX|skewY)\\(([^)]*)\\)/g;\n const ops: Array<{ name: string; full: string }> = [];\n let m: RegExpExecArray | null;\n while ((m = re.exec(s))) ops.push({ name: m[1], full: m[0] });\n if (ops.length !== 1) return false;\n if (ops[0].name === 'translate') return true;\n if (ops[0].name !== 'matrix') return false;\n const args = /matrix\\(([^)]*)\\)/.exec(ops[0].full);\n if (!args) return false;\n const nums = args[1].split(/[\\s,]+/).filter(Boolean).map(Number);\n return nums.length >= 4 && nums[0] === 1 && nums[1] === 0 && nums[2] === 0 && nums[3] === 1;\n}\n\n/**\n * Body-string lift heuristic. The body `transform=\"\"` is the t=0 baseline that\n * the WRITER bakes in the canonical order `translate · +origin · rotate · scale · -origin`.\n *\n * Lift LEADING `translate(...)` ops ONLY when the string does NOT end with a\n * `translate(...)`. A trailing translate is the `-origin` half of an origin\n * sandwich — leaving any translate inside the sandwich would break the pivot,\n * so we leave the whole body alone in that case (the corresponding\n * `effects.transformBy` is the structured source we lift from instead).\n *\n * (`matrix(...)` and `skewX/Y` are not lifted — they don't carry \"user\n * translate\" semantics. If the leading op isn't `translate`, nothing is lifted.)\n */\nfunction splitTransformString(s: string): { translate?: string; rest?: string } {\n const ops: Array<{ name: string; full: string }> = [];\n const re = /(translate|rotate|scale|matrix|skewX|skewY)\\(([^)]*)\\)/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(s))) ops.push({ name: m[1], full: m[0] });\n\n if (!ops.length) return { rest: s || undefined };\n\n // All-translate string — no origin-sandwich without rotate/scale in the\n // middle. Lift everything.\n if (ops.every(o => o.name === 'translate')) {\n return { translate: ops.map(o => o.full).join('') };\n }\n\n const leading = ops[0];\n const trailing = ops[ops.length - 1];\n\n // Origin sandwich: writer's canonical order is `translate · +origin ·\n // rotate · scale · -origin`, with `translate + +origin` typically FUSED\n // into one leading translate. We can recover the user-translate by reading\n // the trailing `-origin` value and subtracting `+origin` from the leading.\n if (trailing.name === 'translate' && leading.name === 'translate') {\n const trailingVec = parseTranslateArgs(trailing.full);\n const leadingVec = parseTranslateArgs(leading.full);\n const ox = -trailingVec[0]; // -origin → +origin\n const oy = -trailingVec[1];\n const userTx = leadingVec[0] - ox;\n const userTy = leadingVec[1] - oy;\n\n // Pure origin sandwich (user-translate cancels to 0) — leave the body alone.\n if (userTx === 0 && userTy === 0) return { rest: s };\n\n // Replace the leading translate with the bare `+origin` (=ox,oy), keep\n // the rest of the sandwich unchanged, and lift `translate(userT)`.\n const middleAndTrailing = 'translate(' + ox + ',' + oy + ')' + ops.slice(1).map(o => o.full).join('');\n return { translate: 'translate(' + userTx + ',' + userTy + ')', rest: middleAndTrailing };\n }\n\n // Trailing translate without a matching leading translate — unusual. Be\n // conservative and don't lift.\n if (trailing.name === 'translate') return { rest: s };\n\n // No trailing translate → no origin sandwich; lift all leading translates.\n const lifted: Array<string> = [];\n let i = 0;\n while (i < ops.length && ops[i].name === 'translate') {\n lifted.push(ops[i].full);\n i++;\n }\n if (!lifted.length) return { rest: s };\n\n const rest = ops.slice(i).map(o => o.full).join('');\n return {\n translate: lifted.join(''),\n rest: rest || undefined,\n };\n}\n\nfunction parseTranslateArgs(translateOp: string): [number, number] {\n const m = /translate\\(([^)]*)\\)/.exec(translateOp);\n if (!m) return [0, 0];\n const nums = m[1].split(/[\\s,]+/).filter(Boolean).map(Number);\n return [nums[0] || 0, nums[1] || 0];\n}\n\n\n////////////////////////////////////////////////////////////////\n//// effects.transformBy split\n////////////////////////////////////////////////////////////////\n\nfunction splitTransformByEffect(fx: PxTransformByEffect | undefined): {\n outer?: PxTransformByEffect;\n inner?: PxTransformByEffect;\n} {\n if (!fx) return {};\n const originOnOuter = needsOriginOnOuter(fx.translate);\n const innerHasPivotedPart = fx.rotate !== undefined || fx.scale !== undefined;\n\n const outer: PxTransformByEffect = {};\n const inner: PxTransformByEffect = {};\n\n if (fx.translate !== undefined) outer.translate = fx.translate;\n // Origin lives on outer when translate carries auto-orient (so the\n // path-tangent rotation pivots around origin too).\n if (originOnOuter && fx.origin !== undefined) outer.origin = fx.origin;\n\n if (fx.rotate !== undefined) inner.rotate = fx.rotate;\n if (fx.scale !== undefined) inner.scale = fx.scale;\n if (fx.skew !== undefined) inner.skew = fx.skew;\n // Origin also lives on inner whenever rotate/scale need a pivot — duplicate\n // origin across outer + inner is fine: two separate origin sandwiches\n // ([+o, t, -o] outer + [+o, r, s, -o] inner).\n if (fx.origin !== undefined && (!originOnOuter || innerHasPivotedPart)) inner.origin = fx.origin;\n\n return {\n outer: Object.keys(outer).length ? outer : undefined,\n inner: Object.keys(inner).length ? inner : undefined,\n };\n}\n\n\n////////////////////////////////////////////////////////////////\n//// Auto-orient / motion-path detection\n////////////////////////////////////////////////////////////////\n\n/** True when the translate animation produces rotation at the outer level\n * (tangent handles or `autoOrient`) — meaning origin must sit on the outer\n * with translate so the path-tangent rotation pivots around it. */\nfunction needsOriginOnOuter(translateAnim: PxAnimatable<PxVec2> | undefined): boolean {\n if (!translateAnim || typeof translateAnim !== 'object') return false;\n const obj = translateAnim as { autoOrient?: boolean; keyframes?: Array<PxKeyframe<PxVec2> & { tangentOut?: PxVec2; tangentIn?: PxVec2 }> };\n if (obj.autoOrient) return true;\n if (Array.isArray(obj.keyframes)) {\n return obj.keyframes.some(kf => kf.tangentOut || kf.tangentIn);\n }\n return false;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\nimport type { PxAnimatable, PxFillGradientEffect, PxGradientStop, PxKeyframe, PxLoop, PxNode, PxStrokeGradientEffect, PxVec2 } from '../../format/PxAnimatorTypes';\nimport { PxGradientType } from '../../format/PxAnimatorConstants';\nimport { ReadKind, readAnimatable, writeAnimatableChannel } from '../shared/transformParts';\nimport type { ApplyContext } from '../shared/types';\nimport { genId } from '../shared/util';\nimport { keyframeTime, keyframeValue, keyframeEasing } from '../../format/PxAnimatorTypes';\n\n\n/**\n * `effects.fillGradient` / `effects.strokeGradient` materializer.\n *\n * WHY AN EFFECT (not a `fill` value): no value of `fill` IS a gradient — the\n * browser can only render one through a `<linearGradient>`/`<radialGradient>`\n * def with `<stop>` children plus a `url(#id)` indirection. Per the format's\n * attribute-vs-effect law (see `_PxEffects`), anything that requires generating\n * structure is an effect; flat `fill` colors stay plain animated attributes.\n *\n * Mirrors `maskedByEffect`: generate a `<linearGradient>` or `<radialGradient>`\n * def into `ctx.defs`, push the host element's `fill` / `stroke` to\n * `url(#auto-id)`. Same shape used for both fill and stroke — the only\n * difference is which host attribute is rewritten.\n *\n * The wire shape is a gradient as one animatable stop timeline + static geometry\n * (see `_PxFillGradientEffect`). When materializing:\n * - geometry parts (`start`, `end`, `center`, `radius`, `focal`) become static body attrs\n * on the gradient def;\n * - the stops array is either static (each `<stop>` is bare) or animated\n * (each `<stop>` gets `animate.stopColor.keyframes` derived from the\n * single source timeline by SLICING each kf's full snapshot at this\n * stop's index).\n *\n * The per-stop slicing produces the standard `<linearGradient>` + `<stop>`\n * def chain, so the materialized tree round-trips through the usual reader.\n */\nexport function applyFillGradientEffect(node: PxNode, fx: PxFillGradientEffect | undefined, ctx: ApplyContext): PxNode {\n return applyGradient(node, fx, ctx, 'fill');\n}\n\nexport function applyStrokeGradientEffect(node: PxNode, fx: PxStrokeGradientEffect | undefined, ctx: ApplyContext): PxNode {\n return applyGradient(node, fx, ctx, 'stroke');\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internals\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction applyGradient(node: PxNode, fx: PxFillGradientEffect | undefined, ctx: ApplyContext, attr: 'fill' | 'stroke'): PxNode {\n if (!fx) return node;\n\n const id = genId(ctx, 'grad');\n const def = synthesiseGradientDef(fx, id, ctx);\n ctx.defs.push(def);\n node[attr] = 'url(#' + id + ')';\n return node;\n}\n\nfunction synthesiseGradientDef(fx: PxFillGradientEffect, id: string, ctx: ApplyContext): PxNode {\n const out: PxNode = {\n type: fx.type === PxGradientType.radial ? 'radialGradient' : 'linearGradient',\n id,\n };\n\n // Geometry — standard animatable slots. Static → body attrs; animated → the\n // def node's own `animate` channels under the real SVG attr names (a vec slot\n // splits into its two axis channels here, in the applier — the wire stays\n // `start: {keyframes:[{value:[x,y]}…]}`). The frames engine then drives the\n // def's attrs exactly like the stops' `stopColor` (CSS/WAAPI can't animate\n // gradient geometry, but this materializer feeds the JS frame loop).\n if (fx.type === PxGradientType.linear) {\n applyGeomVec(out, 'x1', 'y1', fx.start);\n applyGeomVec(out, 'x2', 'y2', fx.end);\n } else {\n applyGeomVec(out, 'cx', 'cy', fx.center);\n applyGeomNumber(out, 'r', fx.radius);\n applyGeomVec(out, 'fx', 'fy', fx.focal);\n }\n if (fx.gradientUnits) out.gradientUnits = fx.gradientUnits;\n if (fx.spreadMethod) out.spreadMethod = fx.spreadMethod;\n if (fx.gradientTransform) out.gradientTransform = fx.gradientTransform;\n\n // (The old per-scalar `fx.animate.gradientX1…` channels are NOT read any\n // more — geometry animates on the slots above. Backward compat dropped\n // deliberately; a leftover `animate` key is flagged by strict validation.)\n\n out.children = buildStopChildren(fx.stops, ctx);\n return out;\n}\n\n/** Animatable PxVec2 geometry slot → static `xAttr`/`yAttr` body attrs, or two\n * per-axis `animate` channels (times/easings preserved, `loop` carried) plus\n * static baseline attrs from the base/first kf. */\nfunction applyGeomVec(out: PxNode, xAttr: string, yAttr: string, raw: PxAnimatable<PxVec2> | undefined): void {\n const read = readAnimatable<PxVec2>(raw);\n if (read.kind === ReadKind.Absent) return;\n if (read.kind === ReadKind.Static) {\n out[xAttr] = String(read.value[0]);\n out[yAttr] = String(read.value[1]);\n return;\n }\n const axisChannel = (idx: 0 | 1): { keyframes: Array<PxKeyframe>; loop?: PxLoop | boolean } => {\n const block: { keyframes: Array<PxKeyframe>; loop?: PxLoop | boolean } = {\n keyframes: read.keyframes.map(kf => {\n const axisKf: PxKeyframe = { time: kf.time, value: Array.isArray(kf.value) ? kf.value[idx] : undefined };\n if (kf.easing !== undefined) axisKf.easing = kf.easing;\n return axisKf;\n }),\n };\n if (read.loop !== undefined) block.loop = read.loop;\n return block;\n };\n const animate = (out.animate as Record<string, unknown> | undefined) ?? {};\n animate[xAttr] = axisChannel(0);\n animate[yAttr] = axisChannel(1);\n out.animate = animate as PxNode['animate'];\n const baseline = read.base ?? read.keyframes[0]?.value;\n if (Array.isArray(baseline)) {\n out[xAttr] = String(baseline[0]);\n out[yAttr] = String(baseline[1]);\n }\n}\n\n/** Animatable number geometry slot (radial `r`) → static attr or `animate` channel. */\nfunction applyGeomNumber(out: PxNode, attrName: string, raw: PxAnimatable<number> | undefined): void {\n const read = readAnimatable<number>(raw);\n if (read.kind === ReadKind.Absent) return;\n writeAnimatableChannel(out, attrName, read, { asString: true });\n}\n\n/** Emits one `<stop>` per gradient stop. Stops come from EITHER the static\n * array form (`stops: [{offset, color}, …]`) or the animated form\n * (`stops: {keyframes:[{time, value:[stops], easing?}]}`). For the\n * animated form, each emitted `<stop>` gets `animate.stopColor.keyframes`\n * whose values are sliced out of the source timeline at this stop's\n * index — the standard `<stop>` def chain. */\nfunction buildStopChildren(stops: PxAnimatable<Array<PxGradientStop>> | undefined, ctx: ApplyContext): Array<PxNode> {\n if (!stops) return [];\n\n // Shared reader — bare array / `{value: […]}` statics, `{keyframes|kfs, loop?}` animated.\n const read = readAnimatable<Array<PxGradientStop>>(stops);\n if (read.kind === ReadKind.Absent) return [];\n if (read.kind === ReadKind.Static) return Array.isArray(read.value) ? read.value.map(staticStopNode) : [];\n\n const kfs = read.keyframes as Array<PxKeyframe>;\n if (!kfs.length) return [];\n // Per-stop animations inherit the timeline-level `loop` (alternate/cycle/etc.).\n // Without forwarding it, animating a gradient with `loop.alternate:true`\n // would slice each stop's colors into separate `animate.stopColor`\n // entries that lose the loop config → no reversal past the last kf,\n // even though every non-gradient animatable property loops fine. See\n // also: the gradient stop \"slice\" docstring above.\n const loopFromSource = read.loop;\n\n // Stop count: take the LARGEST across kfs (constraint says constant\n // count, but defensive — when missing, hold the last value).\n let stopCount = 0;\n for (const kf of kfs) {\n const v = keyframeValue(kf) as Array<PxGradientStop> | undefined;\n if (Array.isArray(v) && v.length > stopCount) stopCount = v.length;\n }\n if (!stopCount) return [];\n\n // Baseline stop info from kf[0] — offsets stay fixed across kfs, only\n // colors animate; offset rarely animates but if it does we sample at\n // each kf.\n const firstKfValue = keyframeValue(kfs[0]) as Array<PxGradientStop> | undefined;\n const baselineStops: Array<PxGradientStop> = [];\n for (let i = 0; i < stopCount; i++) {\n const s = firstKfValue?.[i] ?? prevDefinedStop(kfs, 0, i) ?? { offset: i / Math.max(1, stopCount - 1), color: '#000000' };\n baselineStops.push({ offset: s.offset, color: s.color });\n }\n\n return baselineStops.map((bs, i) => animatedStopNode(bs, kfs, i, ctx, loopFromSource));\n}\n\nfunction staticStopNode(s: PxGradientStop): PxNode {\n return {\n type: 'stop',\n offset: formatOffset(s.offset),\n stopColor: s.color,\n };\n}\n\nfunction animatedStopNode(baseline: PxGradientStop, kfs: Array<PxKeyframe>, stopIdx: number, _ctx: ApplyContext, loop: PxLoop | boolean | undefined): PxNode {\n const colorKfs: Array<PxKeyframe> = [];\n const offsetKfs: Array<PxKeyframe> = [];\n // Only emit an `offset` timeline when the offset actually moves across\n // kfs — most gradients animate color only, and a static offset attr is\n // cheaper than a runtime binding that recomputes the same value.\n let offsetVaries = false;\n for (const kf of kfs) {\n const t = keyframeTime(kf);\n const arr = keyframeValue(kf) as Array<PxGradientStop> | undefined;\n const sliced = arr?.[stopIdx] ?? prevDefinedStop(kfs, kfs.indexOf(kf), stopIdx);\n if (!sliced) continue;\n const easing = keyframeEasing(kf);\n\n const colorOut: PxKeyframe = { time: t, value: sliced.color };\n if (easing !== undefined) colorOut.easing = easing;\n colorKfs.push(colorOut);\n\n // Offset is a unitless 0..1 fraction (SVG `<stop offset>` accepts it\n // bare); the runtime interpolates it as a numeric attr.\n const offsetOut: PxKeyframe = { time: t, value: sliced.offset };\n if (easing !== undefined) offsetOut.easing = easing;\n offsetKfs.push(offsetOut);\n if (sliced.offset !== baseline.offset) offsetVaries = true;\n }\n\n const stop: PxNode = {\n type: 'stop',\n offset: formatOffset(baseline.offset),\n stopColor: baseline.color,\n };\n const animate: { [k: string]: { keyframes: Array<PxKeyframe>, loop?: PxLoop | boolean } } = {};\n if (colorKfs.length) {\n animate.stopColor = { keyframes: colorKfs };\n if (loop !== undefined) animate.stopColor.loop = loop;\n }\n if (offsetVaries && offsetKfs.length) {\n animate.offset = { keyframes: offsetKfs };\n if (loop !== undefined) animate.offset.loop = loop;\n }\n if (Object.keys(animate).length) stop.animate = animate;\n return stop;\n}\n\n/** Walks backwards from `fromIdx` looking for a kf whose stops array has\n * an entry at `stopIdx`. Used when a kf's stops array is shorter than the\n * global stop count (shouldn't happen if writer respects the constraint,\n * but degrades gracefully). */\nfunction prevDefinedStop(kfs: Array<PxKeyframe>, fromIdx: number, stopIdx: number): PxGradientStop | undefined {\n for (let i = fromIdx; i >= 0; i--) {\n const arr = keyframeValue(kfs[i]) as Array<PxGradientStop> | undefined;\n if (arr?.[stopIdx]) return arr[stopIdx];\n }\n for (let i = fromIdx + 1; i < kfs.length; i++) {\n const arr = keyframeValue(kfs[i]) as Array<PxGradientStop> | undefined;\n if (arr?.[stopIdx]) return arr[stopIdx];\n }\n return undefined;\n}\n\n/** `0.5` → `\"50%\"`; `1` → `\"100%\"`; `0.123` → `\"12.3%\"`. Matches SVG\n * convention for `<stop offset>`. */\nfunction formatOffset(o: number): string {\n const pct = Math.round(o * 1000) / 10;\n return pct + '%';\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\nimport type { PxClipPathEffect, PxNode } from '../../format/PxAnimatorTypes';\nimport { ReadKind, readAnimatable, writeAnimatableChannel } from '../shared/transformParts';\nimport type { ApplyContext } from '../shared/types';\nimport { genId } from '../shared/util';\n\n\n/**\n * CLIP-PATH → a `<clipPath>` in defs holding a single `<path>` built from the effect's\n * `d`, referenced via `clip-path=\"url(#…)\"` on the host element.\n *\n * Materializer pattern, mirroring `applyMaskedByEffect` (generate a def, set a URL ref on the\n * node) but far simpler — the clip geometry is a self-contained vector path (no ancestor\n * transform compensation, no source lookup).\n *\n * `pathData` is a standard animatable slot (static string / `{value}` / `{keyframes}` with\n * `{pathData}` values — same grammar as the body `d` attribute). An animated slot becomes the child\n * `<path>`'s `animate.d` block. The def is spliced into the walked tree, so `collectIds`\n * auto-assigns the animated path an id and the frame loop rewrites its `d` per frame.\n * `clip-path` is a live reference (verified: the browser re-clips on every `d` change\n * across SMIL/CSS/JS/WAAPI), so the clip animates without any per-frame re-binding on\n * the host.\n *\n */\nexport function applyClipPathEffect(\n node: PxNode,\n fx: PxClipPathEffect | undefined,\n ctx: ApplyContext,\n): PxNode {\n if (!fx?.pathData) return node;\n\n const clipId = genId(ctx, 'clip');\n const pathChild: PxNode = { type: 'path' };\n const read = readAnimatable<string>(fx.pathData);\n if (read.kind !== ReadKind.Absent) {\n // Static values may be the bare path string or a `{pathData}` object (the kf\n // value encoding) — normalize to the string for the body attr.\n if (read.kind === ReadKind.Static) {\n pathChild.d = pathString(read.value);\n } else {\n writeAnimatableChannel(pathChild, 'd', read);\n if (pathChild.d !== undefined) pathChild.d = pathString(pathChild.d as unknown);\n }\n }\n ctx.defs.push({ type: 'clipPath', id: clipId, children: [pathChild] });\n node.clipPath = 'url(#' + clipId + ')';\n return node;\n}\n\n/** Unwraps a `{pathData: \"M…\"}` kf-value object to its string; passes strings through. */\nfunction pathString(v: unknown): string | undefined {\n if (typeof v === 'string') return v;\n if (v && typeof v === 'object' && typeof (v as { pathData?: unknown }).pathData === 'string') return (v as { pathData: string }).pathData;\n return undefined;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\nimport type { PxAnimatable, PxMaskedByEffect, PxNode, PxTransformByEffect, PxVec2 } from '../../format/PxAnimatorTypes';\nimport { keyframeWith, partsRecord, ReadKind, readAnimatable, readStaticOrigin, TransformPart } from '../shared/transformParts';\nimport type { ApplyContext, MaskAncestorTransform } from '../shared/types';\nimport { genId, stripHash } from '../shared/util';\n\n\n/**\n * MASKED-BY → a `<mask>` in defs holding the source `<use>` wrapped in\n * (forward of mask source's ancestors) · (inverse of masked element's full\n * transform chain) · (inverse of masked element's own `effects.transformBy`)\n *\n * The mask source must paint at its ORIGINAL world position, but\n * `maskUnits=\"userSpaceOnUse\"` (default) interprets the mask in the masked\n * element's local coord system. The wrapper sequence above first cancels\n * out the masked element's accumulated ancestor / own transforms, then\n * re-applies the mask source's ancestor transforms so the `<use>` ends up\n * at the same world matrix it would have if rendered in place.\n *\n * Composes the masked element's own transform with the mask source's, so the\n * mask renders at the right world matrix.\n *\n * Implementation note — this first cut only composes TRANSLATE parts (static\n * and animated). Rotate / scale on the ancestor chains aren't supported yet\n * and will warn if present. `effects.transformBy` on the masked element\n * keeps working (passed in as `transformBy` and inverted separately).\n */\nexport function applyMaskedByEffect(\n node: PxNode,\n fx: PxMaskedByEffect | undefined,\n transformBy: PxTransformByEffect | undefined,\n ctx: ApplyContext,\n): PxNode {\n if (!fx) return node;\n // Canonical ref spelling is `#id` (SCHEMA-DESIGN §4 E-5); bare `id` is legacy.\n const sourceId = stripHash(fx.source);\n if (!sourceId) { ctx.errors.push('maskedBy.source missing — cannot build mask'); return node; }\n\n const maskId = genId(ctx, 'mask');\n\n let content: PxNode = { type: 'use', href: '#' + sourceId };\n // `effects.transformBy` (split-timing form) gets full per-part inversion\n // through `wrapInverseTransform`. With no `effects.transformBy` the\n // masked element's transform lives on the body — invert the STATIC part\n // (`node.transform` string) via the same per-part machinery, then on top\n // emit an animated inverse wrapper for `node.animate.transform.keyframes`\n // (per-kf parts-record inversion). Either way, the ancestor-chain\n // compensation runs on top with translate-only composition for now.\n if (transformBy) {\n content = wrapInverseTransform(content, transformBy, ctx);\n } else if (hasAnimateTransform(node)) {\n // `animate.transform` overrides `node.transform` per-frame in the\n // visualModel, so inverting BOTH would double-count. Animated wins.\n content = wrapInverseAnimatedBodyTransform(content, node, ctx);\n } else {\n const bodyStatic = readTransformationFromBody(node);\n if (bodyStatic) content = wrapInverseTransform(content, bodyStatic, ctx);\n }\n // Skip `targetOwn` translate when we already inverted the body (otherwise\n // the translate would be subtracted twice).\n const includeTargetOwn = transformBy === undefined && !nodeHasBodyTransform(node);\n content = wrapAncestorChainCompensation(content, node, sourceId, ctx, includeTargetOwn);\n\n const mask: PxNode = { type: 'mask', id: maskId, children: [content] };\n if (fx.maskType) mask.maskType = fx.maskType;\n if (fx.maskUnits) mask.maskUnits = fx.maskUnits;\n if (fx.maskContentUnits) mask.maskContentUnits = fx.maskContentUnits;\n // Explicit mask viewport (`x/y/width/height` in `maskUnits` space). Absent →\n // SVG's implicit −10%…120% region, same as the editor's defaults.\n if (fx.x !== undefined) mask.x = String(fx.x);\n if (fx.y !== undefined) mask.y = String(fx.y);\n if (fx.width !== undefined) mask.width = String(fx.width);\n if (fx.height !== undefined) mask.height = String(fx.height);\n ctx.defs.push(mask);\n\n node.mask = 'url(#' + maskId + ')';\n return node;\n}\n\n/** Wraps `inner` in inverse-transform `<g>`s built from the masked element's\n * own `effects.transformBy` payload (translate / rotate / scale). The\n * ancestor-chain wrappers are added separately by\n * `wrapAncestorChainCompensation`. */\nfunction wrapInverseTransform(inner: PxNode, fx: PxTransformByEffect | undefined, ctx: ApplyContext): PxNode {\n if (!fx) return inner;\n const origin = readStaticOrigin(fx.origin, ctx);\n\n let n = inner;\n n = wrapInversePart(n, TransformPart.Translate, fx.translate, undefined, ctx);\n n = wrapInversePart(n, TransformPart.Rotate, fx.rotate, origin, ctx);\n n = wrapInversePart(n, TransformPart.Scale, fx.scale, origin, ctx);\n return n;\n}\n\nfunction wrapInversePart(\n inner: PxNode, part: TransformPart,\n raw: PxAnimatable<any> | undefined, origin: PxVec2 | undefined, ctx: ApplyContext\n): PxNode {\n if (raw === undefined) return inner;\n // `fx.scale` in BARE-ARRAY form is PERCENT (150 = 1.5×), matching\n // `applyTransformByEffect`'s forward `normalizeScale`. Convert to\n // 1.0-units before reading, so `invertPartValue([1.5,1.5])` produces\n // the right `[2/3, 2/3]` instead of `[1/150, 1/150]`. Keyframe / {value}\n // forms already use 1.0-units per the wire convention.\n const normalizedRaw: PxAnimatable<any> | undefined = (part === TransformPart.Scale && Array.isArray(raw))\n ? [raw[0] / 100, raw[1] / 100] as unknown as PxAnimatable<any>\n : raw;\n const v = readAnimatable<any>(normalizedRaw);\n if (v.kind === ReadKind.Static) {\n return { type: 'g', transform: { value: partsRecord(part, invertPartValue(part, v.value), origin) }, children: [inner] };\n }\n if (v.kind === ReadKind.Animated) {\n const animTr: any = { keyframes: v.keyframes.map(kf => {\n const out = keyframeWith(kf, partsRecord(part, invertPartValue(part, kf.value), origin));\n return part === TransformPart.Translate ? { ...out, ...negatedSpatialTangents(kf) } : out;\n }) };\n if (v.loop !== undefined) animTr.loop = v.loop;\n return {\n type: 'g',\n animate: { transform: animTr },\n children: [inner],\n };\n }\n return inner;\n}\n\nfunction invertPartValue(part: TransformPart, value: any): any {\n if (part === TransformPart.Translate) return [-value[0], -value[1]];\n if (part === TransformPart.Rotate) return -value;\n return [1 / value[0], 1 / value[1]]; // scale\n}\n\n/**\n * Spatial tangents (`tangentOut`/`tangentIn`, wire aliases `to`/`ti`) are\n * RELATIVE control-point offsets (control = value + tangent), so an inverse\n * translate keyframe must negate them along with the value — copying them\n * verbatim keeps the ORIGINAL curve direction and the derived mask sags\n * mid-segment on a motion-along-path masked element (endpoints stay exact,\n * which is why only mid-frame sampling exposes it).\n */\nfunction negatedSpatialTangents(kf: Record<string, any>): Record<string, any> {\n const out: Record<string, any> = {};\n const to = kf.tangentOut ?? kf.to;\n const ti = kf.tangentIn ?? kf.ti;\n if (Array.isArray(to)) out.tangentOut = [-to[0], -to[1]];\n if (Array.isArray(ti)) out.tangentIn = [-ti[0], -ti[1]];\n return out;\n}\n\n\n/**\n * Wraps `inner` with the per-part INVERSE of the masked element's\n * `node.animate.transform.keyframes` records. Each kf carries a parts record\n * `{translate?, rotate?, scale?, origin?}` with matrix\n * `T(t)·T(o)·R·S·T(-o)`. The matrix inverse is\n * `T(o)·S^-1·R^-1·T(-o)·T(-t)` — which can't be expressed as ONE parts\n * record when both rotate and scale are present (S would have to precede R).\n *\n * Split into separate per-part wrappers, layered from innermost (translate)\n * outwards (rotate, then scale), so the overall composition matches the\n * matrix-level inverse:\n *\n * <g scale^-1> <g rotate^-1> <g translate^-1> {use} </g></g></g>\n *\n * Each wrapper carries an animated parts record over the input kf times.\n * The `origin` for rotate / scale wrappers comes from each kf (the wire emits\n * it alongside whenever rotate or scale is present), so an animated origin\n * sandwich pivots correctly per frame.\n */\nfunction wrapInverseAnimatedBodyTransform(inner: PxNode, node: PxNode, _ctx: ApplyContext): PxNode {\n const animate = node.animate && typeof node.animate === 'object' && !Array.isArray(node.animate)\n ? (node.animate as Record<string, any>) : undefined;\n const animTr = animate?.transform;\n const kfs = animTr && typeof animTr === 'object' && Array.isArray((animTr as Record<string, any>).keyframes)\n ? ((animTr as Record<string, any>).keyframes as Array<Record<string, any>>)\n : undefined;\n if (!kfs || !kfs.length) return inner;\n\n const translateKfs: Array<Record<string, any>> = [];\n const rotateKfs: Array<Record<string, any>> = [];\n const scaleKfs: Array<Record<string, any>> = [];\n\n for (const kf of kfs) {\n const v = (kf.value ?? kf.v) || {};\n const baseKf = keyframeWith(kf as any, undefined); // copies time / easing / tangents\n if (Array.isArray(v.translate)) {\n translateKfs.push({ ...baseKf, ...negatedSpatialTangents(kf), value: { translate: [-v.translate[0], -v.translate[1]] } });\n }\n if (typeof v.rotate === 'number') {\n const rec: Record<string, any> = { rotate: -v.rotate };\n if (Array.isArray(v.origin)) rec.origin = [v.origin[0], v.origin[1]];\n rotateKfs.push({ ...baseKf, value: rec });\n }\n if (Array.isArray(v.scale)) {\n const rec: Record<string, any> = { scale: [1 / v.scale[0], 1 / v.scale[1]] };\n if (Array.isArray(v.origin)) rec.origin = [v.origin[0], v.origin[1]];\n scaleKfs.push({ ...baseKf, value: rec });\n }\n }\n\n // Forward the source animate.transform's loop config (alternate/cycle/etc.)\n // onto each per-part inverse wrapper so a loop on the masked element's body\n // transform survives the inversion split.\n const srcLoop = (animTr as Record<string, any> | undefined)?.loop;\n const withLoop = (kfs: Array<Record<string, any>>): Record<string, any> => {\n const block: Record<string, any> = { keyframes: kfs };\n if (srcLoop !== undefined) block.loop = srcLoop;\n return block;\n };\n\n let n = inner;\n // Innermost = translate (matches `wrapInverseTransform`'s order).\n if (translateKfs.length) n = { type: 'g', animate: { transform: withLoop(translateKfs) }, children: [n] };\n if (rotateKfs.length) n = { type: 'g', animate: { transform: withLoop(rotateKfs) }, children: [n] };\n if (scaleKfs.length) n = { type: 'g', animate: { transform: withLoop(scaleKfs) }, children: [n] };\n return n;\n}\n\n\n/** True when the node carries any body-side transform (the static `transform`\n * string OR an `animate.transform` block). */\nfunction nodeHasBodyTransform(node: PxNode): boolean {\n if (typeof node.transform === 'string') return true;\n if (node.transform && typeof node.transform === 'object') return true;\n return hasAnimateTransform(node);\n}\n\n/** True when the node carries an `animate.transform` block (per-frame\n * override of `node.transform`). */\nfunction hasAnimateTransform(node: PxNode): boolean {\n const animate = node.animate && typeof node.animate === 'object' && !Array.isArray(node.animate)\n ? (node.animate as Record<string, any>) : undefined;\n return !!(animate && animate.transform);\n}\n\n/** Parses the masked element's body transform (`node.transform` string) into a\n * `PxTransformByEffect`-like static parts record so the existing\n * `wrapInverseTransform` machinery can produce its inverse `<g>` wrappers.\n *\n * ANIMATED body transforms (`node.animate.transform.keyframes`) aren't\n * supported yet — they'd require splitting a parts-record keyframe stream\n * into per-part animations to feed into `wrapInversePart`. Falls back with a\n * warning. (In practice the writer emits `effects.transformBy` whenever the\n * masked element is animated, so this caller path is reached only for static\n * cases anyway.) */\nfunction readTransformationFromBody(node: PxNode): PxTransformByEffect | undefined {\n if (typeof node.transform === 'string') {\n const parts = parseTransformStringToParts(node.transform);\n if (!parts) return undefined;\n const out: PxTransformByEffect = {};\n if (parts.translate) out.translate = parts.translate;\n if (parts.rotate !== undefined) out.rotate = parts.rotate;\n // Scale parsed from the string is in 1.0-units (e.g. `scale(1.5)`).\n // Pass it through the `{value}` form so `wrapInversePart`'s\n // bare-array → percent normalization doesn't re-divide by 100.\n if (parts.scale) out.scale = { value: parts.scale };\n if (parts.origin) out.origin = parts.origin;\n return Object.keys(out).length ? out : undefined;\n }\n // STATIC RECORD (SCHEMA-DESIGN S1): bare `{translate, …}` (canonical) or\n // the legacy `{value: partsRecord}` wrapper — the lightweight wire's static\n // form; values are already wire units (scale = factor), so scale passes\n // through `{value}` like the string branch.\n if (node.transform && typeof node.transform === 'object'\n && !(node.transform as any).keyframes) {\n const wrapped = (node.transform as { value?: Record<string, any> }).value;\n const value = (wrapped && typeof wrapped === 'object' ? wrapped : node.transform) as Record<string, any>;\n if (value && typeof value === 'object') {\n const out: PxTransformByEffect = {};\n if (Array.isArray(value.translate)) out.translate = value.translate as [number, number];\n if (typeof value.rotate === 'number') out.rotate = value.rotate;\n if (typeof value.skew === 'number') out.skew = value.skew;\n if (Array.isArray(value.scale)) out.scale = { value: value.scale as [number, number] };\n if (Array.isArray(value.origin)) out.origin = value.origin as [number, number];\n return Object.keys(out).length ? out : undefined;\n }\n }\n return undefined;\n}\n\n/** Parses the canonical body-transform string of the form\n * `translate(t)? translate(o)? rotate? scale? translate(-o)?`\n * — back into a `PxTransformParts`-style record. The origin sandwich\n * (`translate(o) … translate(-o)`) is recovered as `origin: o`; the leading\n * translate (if any) becomes `translate: t`. Returns `undefined` when no\n * recognized ops are found. */\nfunction parseTransformStringToParts(s: string): { translate?: PxVec2; rotate?: number; scale?: PxVec2; origin?: PxVec2 } | undefined {\n interface Op { name: string; args: Array<number>; }\n const re = /([a-zA-Z]+)\\s*\\(([^)]*)\\)/g;\n let m: RegExpExecArray | null;\n const ops: Array<Op> = [];\n while ((m = re.exec(s)) !== null) {\n const args = m[2].split(/[\\s,]+/).filter(a => a.length > 0).map(Number);\n ops.push({ name: m[1], args });\n }\n if (!ops.length) return undefined;\n\n // Detect origin sandwich: a trailing `translate(-ox,-oy)` matching an\n // earlier `translate(+ox,+oy)`. Recover origin / inner rotate / scale.\n const last = ops[ops.length - 1];\n if (last.name === 'translate') {\n for (let j = ops.length - 2; j >= 0; j--) {\n const cand = ops[j];\n if (cand.name !== 'translate') continue;\n const ox = cand.args[0] ?? 0;\n const oy = cand.args[1] ?? 0;\n const lx = last.args[0] ?? 0;\n const ly = last.args[1] ?? 0;\n if (lx !== -ox || ly !== -oy) continue;\n // `cand` = +origin, `last` = −origin. Anything BEFORE `cand` may\n // be a body translate; anything BETWEEN them is rotate / scale.\n const out: { translate?: PxVec2; rotate?: number; scale?: PxVec2; origin?: PxVec2 } = {};\n out.origin = [ox, oy];\n for (let k = 0; k < j; k++) {\n if (ops[k].name === 'translate') {\n const tx = ops[k].args[0] ?? 0;\n const ty = ops[k].args[1] ?? 0;\n out.translate = out.translate ? [out.translate[0] + tx, out.translate[1] + ty] : [tx, ty];\n }\n }\n for (let k = j + 1; k < ops.length - 1; k++) {\n const op = ops[k];\n if (op.name === 'rotate') out.rotate = (out.rotate ?? 0) + (op.args[0] ?? 0);\n else if (op.name === 'scale') {\n const sx = op.args[0] ?? 1;\n const sy = op.args.length > 1 ? op.args[1] : sx;\n out.scale = out.scale ? [out.scale[0] * sx, out.scale[1] * sy] : [sx, sy];\n }\n }\n return out;\n }\n }\n\n // No sandwich — flat sequence. `translate`s sum, `rotate`s sum, `scale`s\n // multiply. Order isn't preserved but it works for the cases emitted without\n // origin (translates commute; only one rotate or scale).\n let translate: PxVec2 | undefined;\n let rotate: number | undefined;\n let scale: PxVec2 | undefined;\n for (const op of ops) {\n if (op.name === 'translate') {\n const dx = op.args[0] ?? 0;\n const dy = op.args[1] ?? 0;\n translate = translate ? [translate[0] + dx, translate[1] + dy] : [dx, dy];\n } else if (op.name === 'rotate') {\n rotate = (rotate ?? 0) + (op.args[0] ?? 0);\n } else if (op.name === 'scale') {\n const sx = op.args[0] ?? 1;\n const sy = op.args.length > 1 ? op.args[1] : sx;\n scale = scale ? [scale[0] * sx, scale[1] * sy] : [sx, sy];\n }\n }\n const out: { translate?: PxVec2; rotate?: number; scale?: PxVec2 } = {};\n if (translate) out.translate = translate;\n if (rotate !== undefined) out.rotate = rotate;\n if (scale) out.scale = scale;\n return Object.keys(out).length ? out : undefined;\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Ancestor-chain compensation\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Wraps `inner` in a single `<g>` that carries\n * translate = sum(maskSource.ancestors) − sum(maskedElement.ancestors)\n * (separately for the static baseline + every keyframe time observed on any\n * animated ancestor in either chain).\n *\n * Translations commute, so a single wrapper suffices for the translate-only\n * case. When any chain entry has a non-translate part, this function emits a\n * warning and falls back to translate-only — visually wrong but a graceful\n * degradation until the rotate/scale composition is implemented.\n */\nfunction wrapAncestorChainCompensation(inner: PxNode, maskedNode: PxNode, sourceId: string, ctx: ApplyContext, includeTargetOwn: boolean): PxNode {\n const sourceNode = ctx.idMap.get(sourceId);\n\n // M_target = (ancestors) · (target's own). When `effects.transformBy`\n // is present, `wrapInverseTransform` already covers target's own; pass\n // `includeTargetOwn=false` to skip the duplicate. With no `effects.\n // transformation`, the baseline `node.transform` string is the element's\n // only transform — include it.\n const targetAncestors = ctx.maskAncestorChains.get(maskedNode) || [];\n const targetOwn = includeTargetOwn ? extractTranslateOnly(maskedNode, ctx) : undefined;\n const targetChain = targetOwn ? [...targetAncestors, targetOwn] : targetAncestors;\n const sourceChain = (sourceNode && ctx.maskAncestorChains.get(sourceNode)) || [];\n\n if (!targetChain.length && !sourceChain.length) return inner;\n\n // Union of all keyframe times across both chains. Static-only chains end\n // up with `times = []`, which short-circuits below to a static wrapper.\n const times = new Set<number>();\n for (const a of targetChain) if (a.translateKeyframes) for (const kf of a.translateKeyframes) times.add(kf.time);\n for (const a of sourceChain) if (a.translateKeyframes) for (const kf of a.translateKeyframes) times.add(kf.time);\n const animated = times.size > 0;\n\n if (!animated) {\n const tgt = sumStaticTranslate(targetChain);\n const src = sumStaticTranslate(sourceChain);\n const dx = src[0] - tgt[0];\n const dy = src[1] - tgt[1];\n if (dx === 0 && dy === 0) return inner;\n return { type: 'g', transform: 'translate(' + dx + ',' + dy + ')', children: [inner] };\n }\n\n const sortedTimes = Array.from(times).sort((a, b) => a - b);\n const keyframes = sortedTimes.map(t => {\n const tgt = sumTranslateAt(targetChain, t);\n const src = sumTranslateAt(sourceChain, t);\n return { time: t, value: { translate: [src[0] - tgt[0], src[1] - tgt[1]] as [number, number] } };\n });\n return { type: 'g', animate: { transform: { keyframes } }, children: [inner] };\n}\n\n/** Sums every translate (baseline) entry in the chain. Ignores animated kfs. */\nfunction sumStaticTranslate(chain: Array<MaskAncestorTransform>): [number, number] {\n let x = 0, y = 0;\n for (const a of chain) {\n if (a.translate) { x += a.translate[0]; y += a.translate[1]; }\n }\n return [x, y];\n}\n\n/** Sums every translate at time `t` in the chain. Animated entries are sampled\n * via linear interpolation between their kfs; static entries contribute their\n * baseline. */\nfunction sumTranslateAt(chain: Array<MaskAncestorTransform>, t: number): [number, number] {\n let x = 0, y = 0;\n for (const a of chain) {\n if (a.translateKeyframes && a.translateKeyframes.length) {\n const v = interpKfs(a.translateKeyframes, t);\n x += v[0]; y += v[1];\n } else if (a.translate) {\n x += a.translate[0]; y += a.translate[1];\n }\n }\n return [x, y];\n}\n\nfunction interpKfs(kfs: Array<{ time: number; value: [number, number] }>, t: number): [number, number] {\n if (t <= kfs[0].time) return kfs[0].value;\n if (t >= kfs[kfs.length - 1].time) return kfs[kfs.length - 1].value;\n for (let i = 1; i < kfs.length; i++) {\n if (t <= kfs[i].time) {\n const prev = kfs[i - 1];\n const cur = kfs[i];\n const a = (t - prev.time) / (cur.time - prev.time);\n return [prev.value[0] + (cur.value[0] - prev.value[0]) * a, prev.value[1] + (cur.value[1] - prev.value[1]) * a];\n }\n }\n return kfs[kfs.length - 1].value;\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Pre-pass: walk tree, record ancestor chains for every (target, source) pair\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Walks `root` top-down and records — for every element involved in an\n * `effects.maskedBy` pair (the masked element AND the mask source it\n * references) — the chain of translate transforms on its `<g>` ancestors.\n * Results land in `ctx.maskAncestorChains`.\n *\n * The walk runs BEFORE pass 1 so it sees the untouched lightweight tree:\n * ancestor `<g transform=\"...\">` strings + `animate.transform` kfs are still\n * intact and easy to parse.\n */\nexport function collectMaskAncestorChains(root: PxNode, ctx: ApplyContext): void {\n // Two-pass: first find the actual NODE references that are masked elements\n // or mask source elements (the source via idMap, the masked element by\n // walking the tree). Then on a second walk, store ancestor chains for\n // those nodes.\n const interestingNodes = new Set<PxNode>();\n const collectInterestingNodes = (n: PxNode): void => {\n const maskSourceId = stripHash(n.effects?.maskedBy?.source);\n if (typeof maskSourceId === 'string') {\n interestingNodes.add(n); // masked element\n const sourceNode = ctx.idMap.get(maskSourceId);\n if (sourceNode) interestingNodes.add(sourceNode); // mask source\n }\n if (Array.isArray(n.children)) for (const ch of n.children) collectInterestingNodes(ch);\n };\n collectInterestingNodes(root);\n if (interestingNodes.size === 0) return;\n\n const walk = (node: PxNode, chain: Array<MaskAncestorTransform>): void => {\n if (interestingNodes.has(node)) ctx.maskAncestorChains.set(node, chain);\n if (Array.isArray(node.children)) {\n const own = extractTranslateOnly(node, ctx);\n const next = own ? [...chain, own] : chain;\n for (const ch of node.children) walk(ch, next);\n }\n };\n walk(root, []);\n}\n\n/** Extracts only the TRANSLATE part from a node's `transform` + `animate.transform`.\n * Returns `undefined` when the node has no transform OR carries only\n * non-translate parts. Pushes a warning when a non-translate part is dropped. */\nfunction extractTranslateOnly(node: PxNode, ctx: ApplyContext): MaskAncestorTransform | undefined {\n const tr = node.transform;\n const animateBlock = node.animate && typeof node.animate === 'object' && !Array.isArray(node.animate)\n ? (node.animate as Record<string, any>).transform : undefined;\n if (tr === undefined && !animateBlock) return undefined;\n\n const out: MaskAncestorTransform = {};\n\n if (typeof tr === 'string') {\n const parts = parseTranslateOnlyFromString(tr, ctx);\n if (parts) out.translate = parts;\n } else if (tr && typeof tr === 'object' && !(tr as Record<string, any>).keyframes) {\n // bare parts record (canonical) or the legacy {value: record} wrapper\n const wrapped = (tr as Record<string, any>).value;\n const value = (wrapped && typeof wrapped === 'object') ? wrapped : (tr as Record<string, any>);\n if (value && typeof value === 'object' && Array.isArray(value.translate)) {\n out.translate = [value.translate[0] || 0, value.translate[1] || 0];\n }\n if (value && (value.rotate !== undefined || value.scale !== undefined || value.skew !== undefined)) {\n ctx.warnings.push('maskedBy ancestor: non-translate transform parts ignored (rotate/scale not yet supported)');\n }\n }\n\n if (animateBlock && Array.isArray((animateBlock as Record<string, any>).keyframes)) {\n const kfs = (animateBlock as Record<string, any>).keyframes as Array<Record<string, any>>;\n const translateKfs: Array<{ time: number; value: [number, number] }> = [];\n for (const kf of kfs) {\n const v = kf.value ?? kf.v;\n const t = (kf.time ?? kf.t ?? 0) as number;\n if (v && typeof v === 'object' && Array.isArray(v.translate)) {\n translateKfs.push({ time: t, value: [v.translate[0] || 0, v.translate[1] || 0] });\n if (v.rotate !== undefined || v.scale !== undefined || v.skew !== undefined) {\n ctx.warnings.push('maskedBy ancestor: animated non-translate parts ignored');\n }\n }\n }\n if (translateKfs.length) out.translateKeyframes = translateKfs;\n }\n\n return (out.translate || out.translateKeyframes) ? out : undefined;\n}\n\n/** Parses ONLY `translate(x[, y])` ops out of an SVG transform string, summing\n * multiple translates and ignoring anything else (with a one-shot warning).\n * The lightweight writer emits the masked / source ancestors' transforms as\n * these simple strings, so this stays a tiny single-purpose parser. */\nfunction parseTranslateOnlyFromString(s: string, ctx: ApplyContext): [number, number] | undefined {\n const re = /([a-zA-Z]+)\\s*\\(([^)]*)\\)/g;\n let m: RegExpExecArray | null;\n let x = 0, y = 0;\n let seen = false;\n let droppedNonTranslate = false;\n while ((m = re.exec(s)) !== null) {\n const name = m[1];\n const args = m[2].split(/[\\s,]+/).filter(a => a.length > 0).map(Number);\n if (name === 'translate') {\n x += args[0] || 0;\n y += args[1] || 0;\n seen = true;\n } else {\n droppedNonTranslate = true;\n }\n }\n if (droppedNonTranslate) ctx.warnings.push('maskedBy ancestor: non-translate transform in string ignored: ' + s);\n return seen ? [x, y] : undefined;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\n/**\n * REF + TRANSFORMATION (combined). The use's `href` is set BEFORE\n * `applyTransformByEffect` wraps the node so it lands on the actual `<use>`\n * rather than on an outer `<g>` wrapper.\n *\n * For `clone:{without:'translate'}` (a content-ref) the source is materialized as multi-layer by\n * `splitForContentRef` (see `contentRefSplit.ts`), and the use's `href` is\n * rewritten to point at the inner (no-translate) layer's id. No translate\n * cancellation is needed on the use side any more.\n */\n\nimport { PxCloneWithout } from '../../format/PxAnimatorConstants';\nimport { applyTransformByEffect } from '../transform/transformationEffect';\nimport type { PxCloneEffect, PxNode, PxTransformByEffect } from '../../format/PxAnimatorTypes';\nimport type { ApplyContext } from '../shared/types';\nimport { stripHash } from '../shared/util';\n\n\n/**\n * Rewrites `node.href` from the clone's reference part (`without`/`source`) —\n * content-ref → the inner-layer id generated by `splitForContentRef`, whole-element\n * ref → `source`. Pure href mutation (no wrapping), so it can run even when `node`\n * is ALSO a content-ref SOURCE that gets split: a `<use>` that both references\n * content (consumer) and is itself referenced (source) must get its own href\n * rewritten BEFORE the split moves its body inward — otherwise its body keeps the\n * editor-side content id, which doesn't exist in the lightweight tree (dangling\n * href → retime can't follow the chain → the retimed instance renders nothing).\n *\n * A direct-link clone (no `source`, e.g. `clone:{retime}`) keeps its existing\n * `href` — there's nothing to redirect; only content-ref REQUIRES a `source`.\n */\nexport function applyRefHref(\n node: PxNode,\n clone: PxCloneEffect | undefined,\n ctx: ApplyContext,\n): void {\n if (!clone) return;\n // Canonical ref spelling is `#id` (SCHEMA-DESIGN §4 E-5); bare `id` is legacy.\n const sourceId = stripHash(clone.source);\n if (!sourceId) {\n if (clone.without === PxCloneWithout.translate) ctx.errors.push('clone: content ref missing `source`');\n return; // direct link → href already correct, nothing to rewrite\n }\n // For content-ref, redirect href to the inner-layer id produced by\n // `splitForContentRef`. For whole-element ref (or when no split has\n // happened, e.g. target not in the tree), fall back to sourceId.\n const targetId = clone.without === PxCloneWithout.translate\n ? (ctx.contentRefInnerIds.get(sourceId) || sourceId)\n : sourceId;\n node.href = '#' + targetId;\n}\n\nexport function applyRefAndTransformationEffect(\n node: PxNode,\n clone: PxCloneEffect | undefined,\n transformBy: PxTransformByEffect | undefined,\n ctx: ApplyContext,\n): PxNode {\n applyRefHref(node, clone, ctx);\n return applyTransformByEffect(node, transformBy, ctx);\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\nimport { applyTransformByEffect } from './transformationEffect';\nimport type { PxAnimatable, PxAnimationDefinition, PxKeyframe, PxLoop, PxNode, PxRepeaterEffect, PxTransformByEffect, PxVec2 } from '../../format/PxAnimatorTypes';\nimport { ReadKind, readAnimatable } from '../shared/transformParts';\nimport type { ApplyContext } from '../shared/types';\nimport { clone } from '../shared/util';\n\n\n/**\n * REPEATER — \"clone this element N times, each copy stepped by a rule\".\n * Named `repeater`, not `repeat`: the repetition is SPATIAL, while in an animation\n * format `repeat` reads as TIME (`animator.iterations`, per-property `loop`,\n * SMIL `repeatCount`). See the naming note on `_PxRepeaterEffect`.\n *\n * REPEATER → N clones inside a `<g>` wrapper. Copy 0 is the unmodified base;\n * copies 1..N-1 are wrapped with a per-copy `PxTransformByEffect` synthesised\n * from the repeater parts:\n *\n * - translate × i\n * - rotate × i\n * - skew × i (skewX degrees)\n * - scale ^ i (per-axis geometric compound)\n * - origin CONSTANT — rotation/scale center stays put across copies\n *\n * The origin is the rotation/scale CENTER for the sandwich\n * `T(+o) · T(t·i) · R(r·i) · S(s^i) · T(-o)`. Scaling `o` by `i` would shift the\n * center per copy and produce a spiral instead of a repeated rotation.\n *\n * Each part is independently animatable:\n * - Static parts → emitted as structured `transform: {value:…}` on the per-copy wrapper.\n * - Animated parts → emitted as `animate.transform.keyframes` with each kf value\n * scaled by the rule above (time/easing preserved).\n *\n * Uses the same per-copy matrix formula as the heavy SVG render.\n */\nexport function applyRepeaterEffect(node: PxNode, fx: PxRepeaterEffect | undefined, ctx: ApplyContext): PxNode {\n if (!fx) return node;\n\n const copies = fx.copies ?? 1;\n if (copies < 1) { ctx.errors.push('repeater.copies invalid: ' + fx.copies); return node; }\n\n // The base element's SHARED transform (static baseline + any animation) is\n // lifted onto the wrapper so the per-copy increments compose in the wrapper's\n // coordinate space. Non-transform animations (opacity, fill) stay per copy.\n const sharedTransform = node.transform;\n // In-place animations on a node body are always the record form here —\n // narrow the `PxElementAnimation` union accordingly.\n const sharedAnimTransform = (node.animate as PxAnimationDefinition | undefined)?.transform;\n\n const base = clone(node);\n delete base.transform;\n if (base.animate) {\n delete (base.animate as PxAnimationDefinition).transform;\n if (Object.keys(base.animate).length === 0) delete base.animate;\n }\n // The WRAPPER owns the source id (assigned below): a whole-element `<use>` must\n // resolve to the FULL repeated result including the shared transform. Leaving the\n // id on the base would duplicate it across every copy-clone, and href resolution\n // would land on a bare, transform-stripped copy (rendered at the use's position\n // with no body translate — visibly mis-placed).\n delete base.id;\n\n const children: Array<PxNode> = [base];\n for (let i = 1; i < copies; i++) {\n const baseClone = clone(base);\n const synthFx = synthesisePerCopyFx(fx, i);\n // Run through the standard transformation-effect machinery — gets\n // origin sandwich, animated kfs, etc. for free.\n const wrapped = synthFx ? applyTransformByEffect(baseClone, synthFx, ctx) : baseClone;\n children.push(wrapped);\n }\n\n const wrapper: PxNode = { type: 'g', children };\n if (node.id) wrapper.id = node.id;\n if (sharedTransform !== undefined) wrapper.transform = sharedTransform;\n if (sharedAnimTransform !== undefined) wrapper.animate = { transform: sharedAnimTransform };\n return wrapper;\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// PER-COPY SYNTHESIS\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Builds a per-copy `PxTransformByEffect` for copy index `i`. */\nfunction synthesisePerCopyFx(fx: PxRepeaterEffect, i: number): PxTransformByEffect | undefined {\n const out: PxTransformByEffect = {};\n\n if (fx.translate !== undefined) {\n out.translate = mapAnimatable<PxVec2>(fx.translate, v => [v[0] * i, v[1] * i]);\n }\n if (fx.rotate !== undefined) {\n out.rotate = mapAnimatable<number>(fx.rotate, v => v * i);\n }\n if (fx.skew !== undefined) {\n out.skew = mapAnimatable<number>(fx.skew, v => v * i);\n }\n if (fx.scale !== undefined) {\n out.scale = synthesiseScale(fx.scale, i);\n }\n if (fx.origin !== undefined) {\n // CONSTANT — rotation/scale center stays put across copies. Scaling by `i`\n // would drift the center per copy (spiral instead of repeated rotation).\n out.origin = fx.origin;\n }\n\n return Object.keys(out).length ? out : undefined;\n}\n\n/**\n * Applies `fn` to every value of an animatable (base + all keyframes) via the\n * shared `readAnimatable` — one mapper for number and PxVec2 parts alike (the old\n * per-type copies missed the `kfs` alias, so an alias-authored part silently\n * skipped its ×i scaling). Re-emits the normalized unified form:\n * raw static in → raw static out (or `{value}` when `wrapStatic`); animated in →\n * `{keyframes, loop?, autoOrient?, value?}` with kf values mapped and\n * time/easing/tangents preserved.\n */\nfunction mapAnimatable<T>(raw: PxAnimatable<T>, fn: (v: T) => T, wrapStatic = false): PxAnimatable<T> {\n const read = readAnimatable<T>(raw);\n if (read.kind === ReadKind.Absent) return raw;\n if (read.kind === ReadKind.Static) {\n const mapped = fn(read.value);\n const wasRawStatic = typeof raw === 'number' || Array.isArray(raw);\n return (wasRawStatic && !wrapStatic) ? mapped : { value: mapped };\n }\n const out: { keyframes: Array<PxKeyframe<T>>; loop?: PxLoop | boolean; autoOrient?: boolean; value?: T } = {\n keyframes: read.keyframes.map(kf => kf && kf.value !== undefined ? { ...kf, value: fn(kf.value) } : kf),\n };\n if (read.loop !== undefined) out.loop = read.loop;\n if (read.autoOrient !== undefined) out.autoOrient = read.autoOrient;\n if (read.base !== undefined) out.value = fn(read.base);\n return out;\n}\n\n/**\n * Per-copy scale: per-axis geometric compounding `s^i`.\n *\n * The wire carries repeater.scale as a FACTOR (0.85 = 85%) in EVERY form — bare\n * static, `{value:…}` and `{keyframes:…}` alike (one convention, see\n * dev-docs/schema-design.md I-3; the old bare-static PERCENT form is gone). Output is\n * emitted as `{value:…}` / keyframes in the same 1.0-units, so it also bypasses\n * `applyTransformByEffect.normalizeScale` untouched.\n */\nfunction synthesiseScale(raw: PxAnimatable<PxVec2>, i: number): PxAnimatable<PxVec2> {\n const scalePower = (v: PxVec2): PxVec2 => [Math.pow(v[0], i), Math.pow(v[1], i)];\n return mapAnimatable<PxVec2>(raw, scalePower, true);\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\n/**\n * RETIME (`<use>` timeline remap) — runs AFTER every other pass-1 effect has\n * materialized, so the `<use>`'s `href` already points at the right post-pass-1\n * target (e.g. the inner-content layer that `contentRefSplit` produced).\n *\n * Retime lives nested under the merged `clone` effect (`effects.clone.retime`).\n * For every `<use>` carrying it we:\n * 1. follow `useNode.href` (no `sourceId` lookup — the editor-side id wouldn't\n * resolve in the lightweight tree);\n * 2. recursively materialize the chain target-side, preserving each\n * intermediate `<use>`'s offset/transform and accumulating retime at every\n * nested-retime hop (`concatRetime(child, parent)`);\n * 3. wire the chain root into the `<use>` site — see RETIME_MATERIALIZATION_MODE.\n *\n * Each chain link becomes its own defs entry with the right accumulated retime\n * applied to ITS OWN kfs, and the link's `href` rewritten to point at the\n * next-level materialization.\n *\n * NB: `clone.sourceId` is intentionally IGNORED by retime — it carries an\n * upstream core id that does not exist in the lightweight tree; retime\n * follows `href`.\n */\n\nimport type { PxNode, PxRetimeEffect } from '../../format/PxAnimatorTypes';\nimport type { ApplyContext } from '../shared/types';\nimport { applyUseOffsetToG } from '../../util/PxNodeCloneUtil';\nimport { clone, genId, indexById, regenerateIdsInClone, stripHash } from '../shared/util';\n\n\n/** Mode A (true): replace `<use>` with `<g>` whose child IS the chain-root clone.\n * Mode B (false): push the chain-root clone into `<defs>`, rewrite `<use>.href`.\n *\n * Mode B is the default — it preserves the `<use>`'s native semantics (x/y\n * positioning, width/height — none of which `<g>` interprets). Mode A would\n * need to bake those into the new `<g>`'s transform to stay equivalent. */\nconst RETIME_MATERIALIZATION_MODE_INLINE_G = false;\n\n\ninterface Retime { start: number; stretch: number; }\n\nfunction asRetime(r: PxRetimeEffect): Retime {\n return { start: r.start ?? 0, stretch: r.stretch ?? 1 };\n}\n\n/**\n * `timeCrop: [start, end]` (ms, DOCUMENT time) — a VISIBILITY WINDOW on this\n * instance: the `<use>` shows only inside it. Independent of the retime remap,\n * which shifts/stretches the target's own timeline; the crop is about when this\n * instance exists on stage.\n *\n * Implemented as an opacity animation on a wrapper `<g>` rather than by clipping\n * the timeline: the target's animation must keep running (a Lottie layer inside\n * its `ip`/`op` window is mid-motion when it appears, not restarted). The wrapper\n * is a PLAYER-SIDE artifact — it never round-trips, so it cannot disturb the wire.\n *\n * A wrapper is used instead of writing opacity onto the `<use>` itself so an\n * authored opacity on the instance survives (they would otherwise overwrite each\n * other) — the same reason the Lottie converter wraps, see `TimeAttrsPartMC`.\n *\n * Keyframe shape mirrors that converter exactly: `[s-1 → 0][s → 1][e → 1][e+1 → 0]`.\n * The 1 ms shoulders make the edges effectively instant while keeping the element\n * fully visible AT both boundaries.\n */\nconst CROP_EDGE_MS = 1;\n\nfunction applyTimeCrop(useNode: PxNode, crop: [number, number], ctx: ApplyContext): void {\n const [start, end] = crop;\n if (!Number.isFinite(start) || !Number.isFinite(end)) {\n ctx.warnings.push('retime: timeCrop is not a pair of finite numbers — ignored');\n return;\n }\n\n // Empty (or inverted) window ⇒ never visible. A Lottie layer with ip >= op is\n // exactly this; without the explicit 0 it would render always-visible.\n const keyframes = end <= start\n ? [{ time: 0, value: 0 }]\n : [\n ...(start > 0 ? [{ time: Math.max(0, start - CROP_EDGE_MS), value: 0 }] : []),\n { time: Math.max(0, start), value: 1 },\n { time: end, value: 1 },\n { time: end + CROP_EDGE_MS, value: 0 },\n ];\n\n // Wrap: the `<g>` takes over the crop, the original node stays untouched inside.\n const inner: PxNode = { ...useNode } as PxNode;\n for (const k of Object.keys(useNode)) delete (useNode as Record<string, unknown>)[k];\n useNode.type = 'g';\n useNode.children = [inner];\n (useNode as Record<string, unknown>).animate = { opacity: { keyframes } };\n}\n\n/** parent applied OUTSIDE, child INSIDE → `t = parent.start + parent.stretch * (child.start + child.stretch * t_local)`. */\nfunction concatRetime(child: Retime, parent: Retime): Retime {\n return {\n start: parent.start + parent.stretch * child.start,\n stretch: parent.stretch * child.stretch,\n };\n}\n\n/** Retime lives nested under the `clone` effect (`effects.clone.retime`). */\nfunction readCloneRetime(n: PxNode): PxRetimeEffect | undefined {\n return n.effects?.clone?.retime;\n}\n\n/** Removes the retime slice and prunes now-empty `clone` / `effects` buckets. */\nfunction clearCloneRetime(n: PxNode): void {\n const clone = n.effects?.clone;\n if (!clone) return;\n delete clone.retime;\n if (Object.keys(clone).length === 0) delete n.effects!.clone;\n if (n.effects && Object.keys(n.effects).length === 0) delete n.effects;\n}\n\n\n/** Pass-2 driver. Re-indexes ids (pass-1 generates new ones like `_lw_inner_0`),\n * then materializes retime at every site. Order-independent: each site\n * recursively expands its own chain via the original retime layout. */\nexport function applyAllRetimeEffects(root: PxNode, ctx: ApplyContext): void {\n ctx.idMap.clear();\n indexById(root, ctx.idMap);\n\n const sites: Array<PxNode> = [];\n const collect = (n: PxNode): void => {\n if (readCloneRetime(n)) sites.push(n);\n n.children?.forEach(collect);\n };\n collect(root);\n\n // OUTER-most sites first. Materializing a site CONSUMES its retime in place (see the\n // load-bearing note below) — so when an outer use's chain later clones a subtree whose\n // inner retimed use was already processed, the inner retime is gone and never composes\n // with the outer one: a doubly-retimed chain started at +inner instead of\n // +inner∘outer. Document order only happens to work when the outer use serializes\n // first; a symbol-heavy document (e.g. one imported from Lottie precomps) puts the\n // template's inner use ahead of the outer site. Order by reachability instead: a site\n // whose chain can reach other sites materializes before them.\n const reachCount = new Map<PxNode, number>();\n for (const site of sites) {\n let count = 0;\n const visited = new Set<string>();\n const walk = (n: PxNode | undefined): void => {\n if (!n) return;\n if (n !== site && readCloneRetime(n)) count++;\n if (n.type === 'use' && n.href) {\n const id = stripHash(n.href);\n if (id && !visited.has(id)) { visited.add(id); walk(ctx.idMap.get(id)); }\n }\n n.children?.forEach(walk);\n };\n const rootId = stripHash(site.href);\n if (rootId) { visited.add(rootId); walk(ctx.idMap.get(rootId)); }\n reachCount.set(site, count);\n }\n // Stable sort: deeper reach first; equal reach keeps document order.\n sites.sort((a, b) => (reachCount.get(b) ?? 0) - (reachCount.get(a) ?? 0));\n\n for (const useNode of sites) {\n const retime = readCloneRetime(useNode);\n if (!retime) continue;\n // Per-site delete is LOAD-BEARING, not just cleanup: once this use is\n // materialized its `href` is rewritten to an already-time-shifted clone, so\n // a downstream use that references THIS one must NOT re-compose this retime\n // (buildChainClone reads `target`'s clone.retime). Deleting it here makes\n // that read return undefined → no double-count. Moving cleanup to a single\n // end-of-pipeline strip regresses nested retime to +750 instead of +500.\n const crop = retime.timeCrop;\n clearCloneRetime(useNode);\n materializeRetime(useNode, asRetime(retime), ctx);\n // AFTER materialization: `materializeRetime` may rewrite `useNode` in place\n // (inline-`<g>` mode), so cropping last wraps whatever it ended up being.\n if (crop) applyTimeCrop(useNode, crop, ctx);\n }\n}\n\n\n/** Materializes `retime` on `useNode` by cloning the chain rooted at\n * `useNode.href` and wiring the clone into the `<use>` site. */\nfunction materializeRetime(useNode: PxNode, retime: Retime, ctx: ApplyContext): void {\n const targetId = stripHash(useNode.href);\n if (!targetId) { ctx.errors.push('retime: <use> has no href to follow'); return; }\n\n const chainRootId = buildChainClone(targetId, retime, ctx, new Set());\n if (!chainRootId) return;\n\n if (RETIME_MATERIALIZATION_MODE_INLINE_G) {\n const cloneNode = ctx.idMap.get(chainRootId)!;\n useNode.type = 'g';\n delete useNode.href;\n useNode.children = [cloneNode];\n // `<g>` ignores `x`/`y`; preserve the use's position offset as a\n // `translate(x,y)` applied AFTER any transform (nested inner `<g>`).\n applyUseOffsetToG(useNode);\n ctx.defs = ctx.defs.filter(d => d !== cloneNode); // un-defs it since it's inline now\n } else {\n useNode.href = '#' + chainRootId;\n }\n}\n\n\n/** Clones `targetId`'s node, remaps its OWN kfs by `accum`, then — if the target\n * is a `<use>` — recursively materializes ITS target (folding any nested retime\n * into accum via `concatRetime`). Returns the clone's new id, or undefined on\n * dangling refs / loops. The clone is pushed to `ctx.defs` and indexed in\n * `ctx.idMap` so siblings can resolve it. */\nfunction buildChainClone(targetId: string, accum: Retime, ctx: ApplyContext, chain: Set<string>): string | undefined {\n if (chain.has(targetId)) { ctx.errors.push('retime: loop via \"' + targetId + '\"'); return undefined; }\n const target = ctx.idMap.get(targetId);\n if (!target) { ctx.warnings.push('retime: target \"' + targetId + '\" not found'); return undefined; }\n\n const cloneNode = clone(target);\n regenerateIdsInClone(cloneNode, ctx);\n\n // Clone's OWN body kfs (intermediate-use's animated transform, ball's animation, …).\n // For a use, this is usually a no-op (no kfs on the use itself); for a leaf it\n // remaps the entire reachable subtree.\n if (target.type === 'use') {\n remapKeyframeTimesOnly(cloneNode, accum.start, accum.stretch);\n } else {\n remapKeyframeTimes(cloneNode, accum.start, accum.stretch);\n }\n\n // Strip any retime carried into the clone (already consumed via the chain).\n clearCloneRetime(cloneNode);\n\n // If the target is a `<use>`, recurse on ITS href. Nested retime on the\n // intermediate use folds in via concat.\n if (target.type === 'use' && target.href) {\n const subId = stripHash(target.href);\n if (subId) {\n const innerRetime = readCloneRetime(target);\n const subAccum = innerRetime ? concatRetime(asRetime(innerRetime), accum) : accum;\n const subChain = new Set(chain); subChain.add(targetId);\n const subId2 = buildChainClone(subId, subAccum, ctx, subChain);\n if (subId2) cloneNode.href = '#' + subId2;\n }\n } else {\n // Container target (e.g. a content-ref split wrapper) may HOLD nested\n // `<use>` children that themselves carry retime — the nested content-ref\n // case `use → <g> → use(retime) → …`. Those uses aren't in the pass-2\n // site list (they only exist inside this fresh clone), so materialize\n // them here, composing their retime with `accum` (recurse into children).\n // Without this the inner use's retime stays dangling and the subtree\n // renders un-shifted.\n materializeNestedRetimeUses(cloneNode, accum, ctx, chain, targetId);\n }\n\n ctx.defs.push(cloneNode);\n if (typeof cloneNode.id === 'string') ctx.idMap.set(cloneNode.id, cloneNode);\n return typeof cloneNode.id === 'string' ? cloneNode.id : undefined;\n}\n\n\n/** Walks a freshly-cloned container subtree and materializes every nested\n * `<use>` that carries retime: composes its retime with `accum` and rewires its\n * href to a fresh chain clone (then drops the now-consumed retime). The\n * container analogue of `buildChainClone`'s use-target recursion — needed for\n * nested content-ref retime where the inner `<use retime>` lives INSIDE the\n * cloned wrapper rather than at its href root. */\nfunction materializeNestedRetimeUses(node: PxNode, accum: Retime, ctx: ApplyContext, chain: Set<string>, parentTargetId: string): void {\n const visit = (n: PxNode): void => {\n const retime = readCloneRetime(n);\n if (n.type === 'use' && retime && n.href) {\n const subId = stripHash(n.href);\n if (subId) {\n const subAccum = concatRetime(asRetime(retime), accum);\n const subChain = new Set(chain); subChain.add(parentTargetId);\n const subId2 = buildChainClone(subId, subAccum, ctx, subChain);\n if (subId2) n.href = '#' + subId2;\n }\n clearCloneRetime(n);\n }\n n.children?.forEach(visit);\n };\n visit(node);\n}\n\n\n/** Remaps every keyframe `time` in `node` and its subtree: `t' = start + t·stretch`. */\nfunction remapKeyframeTimes(node: PxNode, start: number, stretch: number): void {\n remapKeyframeTimesOnly(node, start, stretch);\n node.children?.forEach(c => remapKeyframeTimes(c, start, stretch));\n}\n\nfunction remapKeyframeTimesOnly(node: PxNode, start: number, stretch: number): void {\n const remap = (kfs: Array<any>): void => {\n for (const kf of kfs) if (typeof kf.time === 'number') kf.time = start + kf.time * stretch;\n };\n if (node.transform && typeof node.transform === 'object' && Array.isArray((node.transform as any).keyframes)) {\n remap((node.transform as any).keyframes);\n }\n if (node.animate && typeof node.animate === 'object') {\n for (const prop of Object.keys(node.animate)) {\n const anim = (node.animate as any)[prop];\n if (anim && Array.isArray(anim.keyframes)) remap(anim.keyframes);\n }\n }\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\nimport { type PxAnimatable, type PxBezierPath, type PxKeyframe, type PxLoop, type PxNode, type PxVec2, type _PxStrokeTrimEffect, keyframeTime, keyframeValue, keyframeEasing } from '../../format/PxAnimatorTypes';\nimport { PxStrokeTrimSubPaths } from '../../format/PxAnimatorConstants';\nimport { bezier2D_arcLengthLUT, bezierToSvgPath, clamp } from '../../util/PxAnimatorUtil';\nimport { parseSvgPathToBezier } from '../../animation/PxDefinitions';\nimport { ReadKind, readAnimatable, writeAnimatableChannel, type ReadPart } from '../shared/transformParts';\nimport type { ApplyContext } from '../shared/types';\n\n\n/**\n * Applies a `strokeTrim` effect: collects every descendant shape leaf, slices each\n * leaf's `d` into sub-paths, measures each in px, and converts the parametric\n * `offset` + `range` to per-sub-path `stroke-dasharray` / `stroke-dashoffset` (and\n * `opacity` for empty-range hide).\n *\n * - `subPaths: 'combined'` chains every sub-path's length end-to-end so the\n * window slides across siblings (ONE trim window over the chained\n * `totalLength`); `'separate'` (default) trims each sub-path on its own.\n * - Animated `offset` becomes `animate.strokeDashoffset.keyframes`;\n * animated `range` becomes `animate.strokeDasharray.keyframes`.\n * - The dasharray emits a repeated pattern so any dashoffset shift lands on a\n * valid dash segment.\n * - Empty-range moments are hidden with `stroke-opacity` 0 (STROKE only — the\n * fill stays visible).\n *\n * COLLAPSE: when the trim host is itself a single shape leaf with exactly ONE\n * sub-path, the trim materializes directly onto that leaf's own `<path>` (no\n * `<g>` split). Multi-subpath (or group/descendant) trims still expand to\n * `<g>` + one bare `<path>` per sub-path.\n */\nexport function applyStrokeTrimEffect(\n node: PxNode,\n strokeTrim: _PxStrokeTrimEffect | undefined,\n ctx: ApplyContext,\n): PxNode {\n if (!strokeTrim) return node;\n\n const combined = strokeTrim.subPaths === PxStrokeTrimSubPaths.combined;\n\n // Pass 1a — collect leaves with subpath lengths (no chain offset yet).\n const leafEntries: Array<LeafEntry> = [];\n const measure = (n: PxNode): void => {\n if (Array.isArray(n.children) && n.children.length > 0) {\n for (const ch of n.children) measure(ch);\n return;\n }\n const d = typeof n.d === 'string' ? n.d : shapeToPathD(n);\n if (d === undefined) return;\n const subpaths = parseSvgPathToBezier(d);\n if (!subpaths.length) return;\n const entry: LeafEntry = { leaf: n, subpaths: [] };\n for (const sp of subpaths) {\n const lengthPx = pxBezierPathLength(sp);\n entry.subpaths.push({ subpath: sp, lengthPx, startOffsetPx: 0 });\n }\n leafEntries.push(entry);\n };\n measure(node);\n\n if (!leafEntries.length) return node;\n\n // Pass 1b — assign chain offsets.\n // `subPaths: 'combined'`: walk leaves in REVERSE doc order so the chain\n // starts at the visually-topmost leaf. Within each leaf, subpaths keep their\n // `d`-attribute order — without the reverse, a multi-leaf group emits the\n // leaves' dashoffsets SWAPPED, which renders the visible trim window on the\n // wrong subpath.\n // `subPaths: 'separate'`: each subpath gets its own independent chain\n // (offset 0), so the leaf iteration order doesn't matter.\n let acc = 0;\n const iterOrder = combined ? [...leafEntries].reverse() : leafEntries;\n for (const entry of iterOrder) {\n for (const sp of entry.subpaths) {\n if (!combined) acc = 0;\n sp.startOffsetPx = acc;\n acc += sp.lengthPx;\n }\n }\n\n const chainLengthPx = acc;\n if (combined && chainLengthPx < 0.001) return node;\n\n // Offset / range readers. Range is post-processed for cross-overs so the\n // dasharray emitter never sees `range[0] > range[1]`.\n //\n // Defaults: `offset = 0` and `range = [0, 1]`. Treating absent\n // inputs as those statics (rather than skipping the emit) keeps the\n // `+ SMALL_PADDING_PX` shift active — that 1-px buffer is what stops\n // `stroke-linecap=\"round\"` from painting a round dot at the zero-length\n // first dash. Missing-emit means no `stroke-dashoffset`, no shift, and\n // the dot reappears.\n const offsetReadRaw = readAnimatable<number>(strokeTrim.offset);\n const offsetRead: ReadPart<number> = offsetReadRaw.kind === ReadKind.Absent\n ? { kind: ReadKind.Static, value: 0 }\n : offsetReadRaw;\n const rangeReadRaw = readRangeWithCrossings(strokeTrim.range);\n const rangeRead: ReadPart<PxVec2> = rangeReadRaw.kind === ReadKind.Absent\n ? { kind: ReadKind.Static, value: [0, 1] }\n : rangeReadRaw;\n\n const offsetValues = readScalarValues(offsetRead);\n const minOffset = offsetValues.length ? Math.min(...offsetValues) : 0;\n const maxOffset = offsetValues.length ? Math.max(...offsetValues) : 0;\n const minMaxOffset: [number, number] = [minOffset, maxOffset];\n\n // COLLAPSE: the trim host is a single shape leaf with exactly one sub-path\n // → no `<g>` split. The trim stroke attrs go directly on the leaf's own\n // `<path>` (its `fill` / `stroke` / `transform` / `id` stay put).\n if (leafEntries.length === 1 && leafEntries[0].leaf === node && leafEntries[0].subpaths.length === 1) {\n const entry = leafEntries[0];\n const sp = entry.subpaths[0];\n const pathLengthPx = combined ? chainLengthPx : sp.lengthPx;\n if (pathLengthPx < 0.001) return node;\n const startOffsetPct = combined ? sp.startOffsetPx / pathLengthPx : 0;\n return collapseLeafWithTrim(entry.leaf, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead);\n }\n\n // Pass 2 — walk the tree and replace each measured leaf with a <g>\n // containing one bare <path> per sub-path.\n const replacements = new Map<PxNode, PxNode>();\n for (const entry of leafEntries) {\n const newChildren: Array<PxNode> = [];\n for (const sp of entry.subpaths) {\n const pathLengthPx = combined ? chainLengthPx : sp.lengthPx;\n if (pathLengthPx < 0.001) continue;\n const startOffsetPct = combined ? sp.startOffsetPx / pathLengthPx : 0;\n newChildren.push(...buildSubpathNodes(entry.leaf, sp.subpath, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead, ctx));\n }\n replacements.set(entry.leaf, wrapLeafAsGroup(entry.leaf, newChildren));\n }\n\n const swap = (n: PxNode): PxNode => {\n const r = replacements.get(n);\n if (r) return r;\n if (Array.isArray(n.children) && n.children.length > 0) {\n return { ...n, children: n.children.map(swap) };\n }\n return n;\n };\n return swap(node);\n}\n\n\n// ============================================================================\n// Leaf -> sub-path table\n// ============================================================================\n\ninterface LeafEntry {\n leaf: PxNode;\n subpaths: Array<{ subpath: PxBezierPath; lengthPx: number; startOffsetPx: number }>;\n}\n\n/** Wraps a leaf as a `<g>` with new sub-path children. Outer attrs of the leaf\n * move onto the wrapper (so its `id` / style anchors are preserved); `d`,\n * `strokeDasharray`, `strokeDashoffset`, and `effects` are stripped. */\nfunction wrapLeafAsGroup(leaf: PxNode, children: Array<PxNode>): PxNode {\n const wrapper: PxNode = { ...leaf, type: 'g', children };\n delete wrapper.d;\n delete wrapper.strokeDasharray;\n delete wrapper.strokeDashoffset;\n delete wrapper.effects;\n return wrapper;\n}\n\n\n// ============================================================================\n// Sub-path node builder\n// ============================================================================\n\nfunction buildSubpathNodes(\n leaf: PxNode,\n subpath: PxBezierPath,\n pathLengthPx: number,\n startOffsetPct: number,\n minMaxOffset: [number, number],\n offsetRead: ReadPart<number>,\n rangeRead: ReadPart<PxVec2>,\n ctx: ApplyContext,\n): Array<PxNode> {\n const offsetToDashOffset = makeOffsetToDashOffset(startOffsetPct, pathLengthPx, minMaxOffset);\n const rangeToDasharray = makeRangeToDasharray(pathLengthPx, minMaxOffset);\n\n const dashOffsetAttr = computeAnimAttr(offsetRead, offsetToDashOffset);\n const dashArrayAttr = computeAnimAttr(rangeRead, rangeToDasharray);\n const strokeOpacityAttr = computeOpacityFromRange(rangeRead);\n\n const dStr = bezierToSvgPath(subpath);\n\n // Bare sub-path under the trim wrapper — fill / stroke / stroke-width are\n // inherited from the wrapper <g>. The empty-range hide uses `stroke-opacity`\n // (STROKE only), so the fill stays visible without a fill-only twin.\n const base = makeBareSubpath(dStr);\n applyAttr(base, 'strokeDasharray', dashArrayAttr);\n applyAttr(base, 'strokeDashoffset', dashOffsetAttr);\n applyAttr(base, 'strokeOpacity', strokeOpacityAttr);\n\n return [base];\n}\n\n/** Collapsed single-subpath form: the trim host stays a single `<path>` (its own\n * `fill` / `stroke` / `transform` / `id` preserved); the trim stroke attrs are\n * applied directly. */\nfunction collapseLeafWithTrim(\n leaf: PxNode,\n pathLengthPx: number,\n startOffsetPct: number,\n minMaxOffset: [number, number],\n offsetRead: ReadPart<number>,\n rangeRead: ReadPart<PxVec2>,\n): PxNode {\n const offsetToDashOffset = makeOffsetToDashOffset(startOffsetPct, pathLengthPx, minMaxOffset);\n const rangeToDasharray = makeRangeToDasharray(pathLengthPx, minMaxOffset);\n\n const node: PxNode = { ...leaf };\n delete node.effects;\n applyAttr(node, 'strokeDasharray', computeAnimAttr(rangeRead, rangeToDasharray));\n applyAttr(node, 'strokeDashoffset', computeAnimAttr(offsetRead, offsetToDashOffset));\n applyAttr(node, 'strokeOpacity', computeOpacityFromRange(rangeRead));\n return node;\n}\n\n/** Fresh `<path>` carrying only the sub-path geometry. Presentation attrs\n * (`fill`, `stroke`, `stroke-width`, `transform`, `id`, …) stay on the wrapper\n * `<g>` and are inherited — bare `<path>`s under a single attrs-bearing `<g>`. */\nfunction makeBareSubpath(dStr: string): PxNode {\n return { type: 'path', d: dStr };\n}\n\n\n// ============================================================================\n// Math\n// ============================================================================\n\nconst SMALL_PADDING_PX = 1;\n\n/** Produces a px dashoffset from a parametric offset. */\nfunction makeOffsetToDashOffset(\n startOffsetPct: number,\n pathLengthPx: number,\n minMaxOffset: [number, number],\n): (offsetVal: number) => number {\n const [minIdx] = getOffsetIndexRange(minMaxOffset);\n return offsetVal => pathLengthPx * (-offsetVal - minIdx + startOffsetPct) + SMALL_PADDING_PX;\n}\n\n/** Produces a px dash pattern of the form `[0, gap, dash, gap, dash, ..., gap]`\n * repeated so the offset can wrap. */\nfunction makeRangeToDasharray(\n pathLengthPx: number,\n minMaxOffset: [number, number],\n): (rangeVal: PxVec2) => Array<number> {\n const [minIdx, maxIdx] = getOffsetIndexRange(minMaxOffset);\n const repeats = maxIdx - minIdx + 1;\n return (rangeVal: PxVec2) => {\n const a = clamp(rangeVal[0], 0, 1);\n const b = clamp(rangeVal[1], 0, 1);\n const minR = Math.min(a, b);\n const maxR = Math.max(a, b);\n const out: Array<number> = [0];\n let gap = SMALL_PADDING_PX;\n for (let i = 0; i < repeats; i++) {\n out.push(gap + minR * pathLengthPx); // GAP\n out.push((maxR - minR) * pathLengthPx); // DASH\n gap = (1 - maxR) * pathLengthPx;\n }\n out.push(gap + SMALL_PADDING_PX); // closing GAP\n return out;\n };\n}\n\n/** Floor/ceil of the negated min/max offset → integer dash-repeat index range. */\nfunction getOffsetIndexRange(minMaxOffset: [number, number]): [number, number] {\n return [\n Math.floor(Math.min(-minMaxOffset[0], -minMaxOffset[1])),\n Math.ceil(Math.max(-minMaxOffset[0], -minMaxOffset[1])),\n ];\n}\n\n\n// ============================================================================\n// Animatable plumbing\n// ============================================================================\n\ntype AnimAttrResult<TOut> =\n | { kind: ReadKind.Static; value: TOut }\n | { kind: ReadKind.Animated; keyframes: Array<PxKeyframe>; loop?: PxLoop | boolean }\n | undefined;\n\nfunction readScalarValues(r: ReadPart<number>): Array<number> {\n if (r.kind === ReadKind.Absent) return [];\n if (r.kind === ReadKind.Static) return [r.value];\n const out: Array<number> = [];\n for (const kf of r.keyframes) {\n const v = keyframeValue(kf);\n if (typeof v === 'number') out.push(v);\n }\n return out;\n}\n\nfunction computeAnimAttr<TIn, TOut>(read: ReadPart<TIn>, map: (v: TIn) => TOut): AnimAttrResult<TOut> {\n if (read.kind === ReadKind.Absent) return undefined;\n if (read.kind === ReadKind.Static) return { kind: ReadKind.Static, value: map(read.value) };\n return {\n kind: ReadKind.Animated,\n keyframes: read.keyframes.map(kf => ({\n time: keyframeTime(kf),\n value: map(keyframeValue(kf) as TIn),\n easing: keyframeEasing(kf),\n })),\n loop: read.loop,\n };\n}\n\n// Static + animated emit (incl. the first-kf static baseline for pre-tick DOM\n// correctness) is the shared `writeAnimatableChannel` — `AnimAttrResult` is a\n// `ReadPart` minus the Absent arm, so it passes straight through.\nfunction applyAttr<T>(node: PxNode, attrName: string, attr: AnimAttrResult<T>): void {\n if (!attr) return;\n writeAnimatableChannel(node, attrName, attr);\n}\n\n/** Width (ms) of the opacity transition emitted at each hide↔show boundary\n * (one SVGA frame = 10 ms at the 100 fps SVGA grid). */\nconst OPACITY_STEP_MS = 10;\n\n/** Empty-range opacity (hide when `startEnd[0] === startEnd[1]`).\n * Static: single 0 if hide always; undefined otherwise.\n * Animated: step-jump kfs at each hide↔show transition (~one SVGA frame\n * wide so the renderer doesn't briefly show the stroke between an empty\n * range and the first non-empty kf at wall-clock t≈0). */\nfunction computeOpacityFromRange(rangeRead: ReadPart<PxVec2>): AnimAttrResult<number> {\n const hide = (v: PxVec2): boolean => v[0] === v[1];\n\n if (rangeRead.kind === ReadKind.Absent) return undefined;\n if (rangeRead.kind === ReadKind.Static) return hide(rangeRead.value) ? { kind: ReadKind.Static, value: 0 } : undefined;\n\n const kfs = rangeRead.keyframes;\n let anyHide = false;\n let allHide = true;\n for (const kf of kfs) {\n if (hide(keyframeValue(kf) as PxVec2)) anyHide = true;\n else allHide = false;\n }\n if (!anyHide) return undefined;\n if (allHide) return { kind: ReadKind.Static, value: 0 };\n\n // Prev/next-aware emitter — only inserts kfs at the transitions to keep the\n // wire compact.\n const out: Array<PxKeyframe> = [];\n for (let i = 0; i < kfs.length; i++) {\n const kf = kfs[i];\n const prevKf = i > 0 ? kfs[i - 1] : undefined;\n const nextKf = i < kfs.length - 1 ? kfs[i + 1] : undefined;\n const t = keyframeTime(kf);\n const thisHide = hide(keyframeValue(kf) as PxVec2);\n const prevHide = prevKf ? thisHide && hide(keyframeValue(prevKf) as PxVec2) : thisHide;\n const nextHide = nextKf ? thisHide && hide(keyframeValue(nextKf) as PxVec2) : thisHide;\n\n if (prevHide && !nextHide) {\n out.push({ time: t, value: 0 });\n out.push({ time: t + OPACITY_STEP_MS, value: 1 });\n } else if (!prevHide && nextHide) {\n out.push({ time: t - OPACITY_STEP_MS, value: 1 });\n out.push({ time: t, value: 0 });\n }\n }\n if (out.length <= 1) return undefined;\n return { kind: ReadKind.Animated, keyframes: out };\n}\n\n\n// ============================================================================\n// Range cross-over\n// ============================================================================\n\ninterface SimpleKf { time: number; value: PxVec2; easing?: any; }\n\n/** Reads `range` and, when the kf sequence has `value[0] > value[1]` anywhere,\n * inserts crossing-point keyframes (bisection-located) and swaps any\n * remaining reversed kfs so every emitted range satisfies `range[0] ≤ range[1]`. */\nfunction readRangeWithCrossings(raw: PxAnimatable<PxVec2> | undefined): ReadPart<PxVec2> {\n const r = readAnimatable<PxVec2>(raw);\n if (r.kind !== ReadKind.Animated) return r;\n\n const kfs: Array<SimpleKf> = r.keyframes.map(kf => ({\n time: keyframeTime(kf),\n value: keyframeValue(kf) as PxVec2,\n easing: keyframeEasing(kf),\n }));\n\n const hasReverse = kfs.some(kf => kf.value[0] > kf.value[1]);\n if (!hasReverse) {\n return {\n kind: ReadKind.Animated,\n keyframes: kfs.map(kf => ({ time: kf.time, value: kf.value, easing: kf.easing })),\n };\n }\n\n const crossingTimes: Array<number> = [];\n for (let i = 1; i < kfs.length; i++) {\n const prev = kfs[i - 1];\n const cur = kfs[i];\n const dPrev = prev.value[1] - prev.value[0];\n const dCur = cur.value[1] - cur.value[0];\n if (dPrev * dCur < 0) {\n const t = bisectionForRangeCrossing(prev, cur);\n if (t !== null && t > prev.time && t < cur.time) {\n crossingTimes.push(Math.round(t));\n }\n }\n }\n const uniqueTs = Array.from(new Set(crossingTimes)).sort((a, b) => a - b);\n\n const out: Array<SimpleKf> = [];\n let j = 0;\n for (const kf of kfs) {\n while (j < uniqueTs.length && uniqueTs[j] < kf.time) {\n const t = uniqueTs[j++];\n const v = interpolateRangeAt(kfs, t);\n const m = (v[0] + v[1]) / 2;\n out.push({ time: t, value: [m, m] });\n }\n const v: PxVec2 = kf.value[0] > kf.value[1] ? [kf.value[1], kf.value[0]] : kf.value;\n out.push({ time: kf.time, value: v, easing: kf.easing });\n }\n while (j < uniqueTs.length) {\n const t = uniqueTs[j++];\n const v = interpolateRangeAt(kfs, t);\n const m = (v[0] + v[1]) / 2;\n out.push({ time: t, value: [m, m] });\n }\n\n return { kind: ReadKind.Animated, keyframes: out };\n}\n\nfunction bisectionForRangeCrossing(prev: SimpleKf, cur: SimpleKf): number | null {\n const f = (t: number): number => {\n const a = (t - prev.time) / (cur.time - prev.time);\n const v0 = prev.value[0] + (cur.value[0] - prev.value[0]) * a;\n const v1 = prev.value[1] + (cur.value[1] - prev.value[1]) * a;\n return v1 - v0;\n };\n let lo = prev.time, hi = cur.time;\n let fLo = f(lo);\n if (fLo === 0) return lo;\n const fHi = f(hi);\n if (fHi === 0) return hi;\n if (fLo * fHi > 0) return null;\n for (let i = 0; i < 100; i++) {\n const mid = (lo + hi) / 2;\n const fMid = f(mid);\n if (fMid === 0 || Math.abs(hi - lo) < 0.0001) return mid;\n if (fLo * fMid < 0) { hi = mid; }\n else { lo = mid; fLo = fMid; }\n }\n return (lo + hi) / 2;\n}\n\nfunction interpolateRangeAt(kfs: Array<SimpleKf>, t: number): PxVec2 {\n if (t <= kfs[0].time) return kfs[0].value;\n if (t >= kfs[kfs.length - 1].time) return kfs[kfs.length - 1].value;\n for (let i = 1; i < kfs.length; i++) {\n if (t <= kfs[i].time) {\n const prev = kfs[i - 1];\n const cur = kfs[i];\n const a = (t - prev.time) / (cur.time - prev.time);\n return [\n prev.value[0] + (cur.value[0] - prev.value[0]) * a,\n prev.value[1] + (cur.value[1] - prev.value[1]) * a,\n ];\n }\n }\n return kfs[kfs.length - 1].value;\n}\n\n\n// ============================================================================\n// Geometry helpers\n// ============================================================================\n\n/** Total arc length of a `PxBezierPath` (sum of per-segment LUT lengths). */\nfunction pxBezierPathLength(path: PxBezierPath): number {\n const v = path.v;\n if (!v || v.length < 2) return 0;\n let total = 0;\n for (let i = 0; i < v.length - 1; i++) {\n total += segmentLength(path, i, i + 1);\n }\n if (path.c && v.length > 1) {\n total += segmentLength(path, v.length - 1, 0);\n }\n return total;\n}\n\nfunction segmentLength(path: PxBezierPath, from: number, to: number): number {\n const v = path.v;\n const p0 = v[from] as [number, number];\n const p3 = v[to] as [number, number];\n const p1 = (path.o?.[from] ?? p0) as [number, number];\n const p2 = (path.i?.[to] ?? p3) as [number, number];\n const lut = bezier2D_arcLengthLUT(p0, p1, p2, p3);\n return lut.ds[lut.ds.length - 1];\n}\n\n/**\n * Cubic-bezier control-point ratio for a quarter arc — the standard circle\n * approximation (max radial error ≈ 0.02%, far below stroke-dash precision).\n */\nconst ARC_KAPPA = 0.5522847498307936;\n\n/**\n * A primitive shape -> outline path `d`, for shapes that carry no `d` of their own.\n * Returns undefined for unsupported types.\n *\n * Start point and winding MATCH the shape's own SVG parameterisation, because the\n * measured length feeds a `stroke-dasharray` that the browser then walks along that\n * very parameterisation — a mismatched start would put the visible trim window in\n * the wrong place.\n *\n * - `<rect>` from `(x+w, y)`, clockwise. Emits the explicit closing-line\n * vertex (`L x0,y0`) before `z`.\n * - `<ellipse>`/`<circle>` from `(cx+rx, cy)` clockwise as four cubic quarters — the\n * SVG-spec equivalent path, which the UA also uses for dashing.\n */\nfunction shapeToPathD(node: PxNode): string | undefined {\n if (node.type === 'rect') {\n const x = Number(node.x ?? 0), y = Number(node.y ?? 0);\n const w = Number(node.width ?? 0), h = Number(node.height ?? 0);\n return 'M' + (x + w) + ',' + y +\n 'L' + (x + w) + ',' + (y + h) +\n 'L' + x + ',' + (y + h) +\n 'L' + x + ',' + y +\n 'L' + (x + w) + ',' + y + 'z';\n }\n\n if (node.type === 'ellipse' || node.type === 'circle') {\n const cx = Number(node.cx ?? 0), cy = Number(node.cy ?? 0);\n const rx = node.type === 'circle' ? Number(node.r ?? 0) : Number(node.rx ?? 0);\n const ry = node.type === 'circle' ? Number(node.r ?? 0) : Number(node.ry ?? 0);\n if (!(rx > 0) || !(ry > 0)) return undefined;\n const kx = rx * ARC_KAPPA, ky = ry * ARC_KAPPA;\n const c = (x1: number, y1: number, x2: number, y2: number, x: number, y: number) =>\n 'C' + x1 + ',' + y1 + ' ' + x2 + ',' + y2 + ' ' + x + ',' + y;\n return 'M' + (cx + rx) + ',' + cy +\n c(cx + rx, cy + ky, cx + kx, cy + ry, cx, cy + ry) +\n c(cx - kx, cy + ry, cx - rx, cy + ky, cx - rx, cy) +\n c(cx - rx, cy - ky, cx - kx, cy - ry, cx, cy - ry) +\n c(cx + kx, cy - ry, cx + rx, cy - ky, cx + rx, cy) + 'z';\n }\n\n return undefined;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\n/**\n * Lightweight, dependency-free applier for \"player-effects\" SVGA JSON.\n *\n * Input: the JSON produced by `SvgaJsonWithPlayerEffectsSerializationUtil`, where\n * structure-creating effects are left UN-applied on `node.effects`. This module\n * reads those effects and MATERIALIZES them into a plain node tree (extra `<g>`\n * wrappers, copies, mask defs) that renders identically to the heavy editor path.\n *\n * Design goals (intentional): minimal, transparent, no merging/optimization. It\n * is fine to emit more nodes than strictly necessary — the only contract is \"same\n * result on screen\". Each effect lives in its own file; this module only wires\n * them into the recursion. Nothing here imports outside `effects/`, so the whole\n * folder can move into the Player codebase verbatim.\n */\n\nimport { identifyContentRefTargets, splitForContentRef } from './reference/contentRefSplit';\nimport { applyFillGradientEffect, applyStrokeGradientEffect } from './paint/gradientEffect';\nimport { applyClipPathEffect } from './clipping/clipPathEffect';\nimport { applyMaskedByEffect, collectMaskAncestorChains } from './clipping/maskedByEffect';\nimport { applyRefAndTransformationEffect, applyRefHref } from './reference/refEffect';\nimport { applyRepeaterEffect } from './transform/repeaterEffect';\nimport { applyAllRetimeEffects } from './reference/retimeEffect';\nimport { applyTextPathEffect } from './text/textPathEffect';\nimport { applyTextGlyphsAlongPath, applyTextGlyphsEffect } from './text/textGlyphsEffect';\nimport { applyStrokeTrimEffect } from './stroke/strokeTrimEffect';\nimport { getDefinitions } from '../format/PxAnimatorConstants';\nimport { resolveTimelineEngine, getAnimatorConfig } from '../format/PxAnimatorConstants';\nimport type { PxNode } from '../format/PxAnimatorTypes';\nimport type { ApplyContext, ApplyResult } from './shared/types';\nimport { clone, genId, indexById, spliceDefs } from './shared/util';\n\nexport type { PxNode } from '../format/PxAnimatorTypes';\nexport type { ApplyResult } from './shared/types';\n\n\n/**\n * Applies all player-effects in `root` and returns a materialized copy plus any\n * generated <defs> nodes, warnings and errors. `root` is not mutated.\n *\n * Two passes:\n * 1. `applyPlayerEffects_exceptRetime` — materializes every effect except retime.\n * 2. `applyPlayerEffects_retime` — applies retime, cloning the NOW-materialized\n * subtrees so retimed `<use>`s see the same wrappers/animations the heavy\n * editor path would produce.\n *\n * Pre-pass identifies every element that is the target of a `<use>` with\n * `ref:{type:'content'}` and allocates a fresh \"inner\" id for it; pass 1 then\n * splits those sources into outer-translate + inner-content layers so the use\n * can target the inner layer.\n * @public @advanced\n */\nexport function materializeNodeEffects(root: PxNode): ApplyResult {\n const ctx: ApplyContext = {\n defs: [], warnings: [], errors: [],\n idMap: new Map(), nextId: 0,\n contentRefInnerIds: new Map(),\n maskAncestorChains: new Map(),\n // Resolved engine: `frames` ONLY when explicitly set; auto/waapi/unset →\n // waapi (we're not 100% sure it's frames, and CSS/WAAPI need the inline form).\n engine: resolveTimelineEngine(getAnimatorConfig(root)?.engine),\n glyphs: getDefinitions(root)?.fonts,\n };\n\n const working = clone(root);\n indexById(working, ctx.idMap);\n identifyContentRefTargets(working, ctx, () => genId(ctx, 'inner'));\n collectMaskAncestorChains(working, ctx);\n\n const afterPass1 = applyPlayerEffects_exceptRetime(working, ctx);\n const out = applyPlayerEffects_retime(afterPass1, ctx);\n spliceDefs(out, ctx.defs);\n\n return { root: out, defs: ctx.defs, warnings: ctx.warnings, errors: ctx.errors };\n}\n\n/**\n * Pass 1 — materialize every effect except retime. Retime is preserved on the\n * original (now wrapped-inner) node so pass 2 can find and apply it.\n *\n * After wrapping, the outer-most wrapper for a node with `originalId` is written\n * back into `ctx.idMap` so retime's clone target picks up the FULL materialized\n * subtree, not the bare un-wrapped original.\n *\n * If the node is a content-ref target, `splitForContentRef` re-shapes the\n * materialized result into outer-translate + inner-rest layers — the outer keeps\n * the original id, the inner gets the pre-allocated inner id so the `<use>` can\n * target it.\n */\nfunction applyPlayerEffects_exceptRetime(node: PxNode, ctx: ApplyContext): PxNode {\n if (node.children) node.children = node.children.map(child => applyPlayerEffects_exceptRetime(child, ctx));\n\n const fx = node.effects;\n const originalId = typeof node.id === 'string' ? node.id : undefined;\n const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : undefined;\n\n if (!fx && !innerIdForContentRef) return node;\n\n const { transformBy, repeater, maskedBy, clipPath, strokeTrim, clone: cloneFx, fillGradient, strokeGradient, textPath, text } = fx ?? {};\n if (fx) delete node.effects;\n\n let n = node;\n // Glyph text: replace the <text>/<tspan> subtree with baked <path> outlines\n // BEFORE any wrapper. Along-path glyphs place+rotate each glyph to the\n // referenced path's tangent; plain glyphs lay out horizontally.\n let consumedByGlyphs = false;\n if (text?.useGlyphs) {\n if (textPath) {\n // Path geometry is INLINE (`textPath.pathData`) — no `<path>` def lookup.\n const pathD = typeof textPath.pathData === 'string' ? textPath.pathData : undefined;\n // textLength passes through as a full PxAnimatable — static stretches the\n // run; animated re-spaces the glyphs over time (same per-glyph sampled\n // keyframe machinery as animated startOffset).\n const glyphed = applyTextGlyphsAlongPath(n, ctx, pathD, textPath.startOffset, textPath.textLength, textPath.pathOverflow);\n if (glyphed) { n = glyphed; consumedByGlyphs = true; } // native textPath NOT applied\n } else {\n n = applyTextGlyphsEffect(n, text, ctx);\n consumedByGlyphs = true;\n }\n }\n // textPath wraps the host's own children in a `<textPath>` — must run BEFORE any\n // structural wrapper (trim/repeater/mask) so the wrapping happens on the un-cloned\n // content first. Skipped when glyphs consumed it.\n if (!consumedByGlyphs) n = applyTextPathEffect(n, textPath, ctx);\n // Paint-gradient defs are generated FIRST, before any structural wrapper —\n // the gradient effect sits on the innermost element (alongside its `fill`\n // / `stroke` body attrs), so it must materialize before trim/repeater/\n // mask wrap around it. `<linearGradient>` defs themselves don't get\n // wrapped — they live in `ctx.defs` independent of the structure walk.\n n = applyFillGradientEffect(n, fillGradient, ctx);\n n = applyStrokeGradientEffect(n, strokeGradient, ctx);\n n = applyStrokeTrimEffect(n, strokeTrim, ctx); // innermost shape\n n = applyRepeaterEffect(n, repeater, ctx);\n n = applyMaskedByEffect(n, maskedBy, transformBy, ctx); // mask sits on inner element\n n = applyClipPathEffect(n, clipPath, ctx); // clip-path ref on the element\n\n if (innerIdForContentRef) {\n // This node is the SOURCE of a content-ref `<use>` → split into\n // outer-translate + inner-rest + bare element so the use can target the\n // inner layer. But it may ALSO be a content-ref CONSUMER itself (a\n // `<use>` that both references content and is referenced — the nested\n // case): rewrite its OWN ref href first so the split moves a resolved\n // body inward, not the dangling editor-side content id.\n applyRefHref(n, cloneFx, ctx);\n n = splitForContentRef(n, transformBy, originalId!, innerIdForContentRef, ctx);\n } else {\n n = applyRefAndTransformationEffect(n, cloneFx, transformBy, ctx);\n }\n\n // Hand off the retime slice to pass 2 (keeps it nested under `clone`). The\n // ref part (type/source) was consumed above.\n if (cloneFx?.retime) node.effects = { clone: { retime: cloneFx.retime } };\n if (originalId) ctx.idMap.set(originalId, n); // outer wrapper is the clone target\n return n;\n}\n\n/** Pass 2 — apply retime to every `<use>` that carries it. Follows the\n * materialized `<use>.href` (not the editor-side `retime.source`). */\nfunction applyPlayerEffects_retime(node: PxNode, ctx: ApplyContext): PxNode {\n applyAllRetimeEffects(node, ctx);\n return node;\n}\n\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// Materializes `animate.transform` bindings marked `alongPathMode: 'offsetPath'` into\n// CSS Motion Path form:\n//\n// style: { offsetPath: \"path('M…C…')\", offsetAnchor: '0 0',\n// offsetRotate: 'auto' | '0deg', offsetDistance: '0%' }\n// animate.offsetDistance: { keyframes: [{ time, value: 0..1 arc-length fraction }] }\n//\n// Lightweight JSON is the DESIGN format — it carries only the tangented transform plus\n// the mode flag, exactly like `effects` carry recipes. The offset infrastructure the\n// Editor bakes into PRE-RENDERED SVG does not exist here, so the player must derive it,\n// the same way it derives effects. Without this stage the mode flag was silently ignored\n// (or, worse, the binding skipped) and a lightweight `offsetPath` document lost or\n// mis-rendered its motion.\n//\n// Mirrors the Editor's `TPositionVecValue.getOffsetAlongPathForCss`: same inline\n// `path('…')` syntax (the original Motion Path syntax — widest support), same\n// arc-length-fraction keyframe values, easing and loop carried over.\n\nimport { OFFSET_DISTANCE_ATTR, TRANSFORM_ATTR, TRANSFORM_PART } from '../format/PxAnimatorConstants';\nimport type { PxAnimatedSvgDocument, PxKeyframe, PxNode, PxPropertyAnimation } from '../format/PxAnimatorTypes';\nimport { keyframeTime, keyframeValue, keyframeEasing, keyframeTangentIn, keyframeTangentOut } from '../format/PxAnimatorTypes';\n\ntype PxVec2 = [number, number];\n\n\n/** Cubic-bezier point at parameter t. */\nfunction cubicAt(p0: PxVec2, c1: PxVec2, c2: PxVec2, p1: PxVec2, t: number): PxVec2 {\n const u = 1 - t;\n const a = u * u * u, b = 3 * u * u * t, c = 3 * u * t * t, d = t * t * t;\n return [a * p0[0] + b * c1[0] + c * c2[0] + d * p1[0],\n a * p0[1] + b * c1[1] + c * c2[1] + d * p1[1]];\n}\n\n/** Approximate cubic segment length by dense polyline sampling. */\nfunction cubicLength(p0: PxVec2, c1: PxVec2, c2: PxVec2, p1: PxVec2, steps = 64): number {\n let len = 0;\n let prev = p0;\n for (let i = 1; i <= steps; i++) {\n const pt = cubicAt(p0, c1, c2, p1, i / steps);\n len += Math.hypot(pt[0] - prev[0], pt[1] - prev[1]);\n prev = pt;\n }\n return len;\n}\n\nconst fmt = (n: number): string => {\n const r = Math.round(n * 10000) / 10000;\n return Object.is(r, -0) ? '0' : String(r);\n};\n\n/**\n * Attempts the rewrite for one transform binding. Returns undefined when this binding is\n * not an offset-path candidate (no explicit mode, no curve, or parts the encoding cannot\n * express) — the caller then leaves the binding for the ordinary pipeline.\n */\nfunction buildOffsetPath(propAnim: PxPropertyAnimation): {\n pathStr: string; distanceKfs: Array<PxKeyframe>; autoOrient: boolean; anchor: PxVec2;\n} | undefined {\n if ((propAnim as { alongPathMode?: string }).alongPathMode !== 'offsetPath') return undefined;\n\n const kfs = propAnim.keyframes as Array<PxKeyframe> | undefined;\n if (!kfs || kfs.length < 2) return undefined;\n\n // Every keyframe must supply a translate; other ANIMATED parts (rotate/scale\n // varying per keyframe) cannot ride the offset encoding — bail to the ordinary\n // pipeline rather than render them wrong. Static `origin` is tolerated: alone (no\n // rotate/scale around it) it composes to identity.\n // GEOMETRY: the model composes translate(t)·translate(o)·rotate·translate(-o), so the\n // point that RIDES the path — and the pivot `autoOrient` rotates about — is the ORIGIN\n // point of the element, located at t+o. Encode exactly that: the path traces t_i+o and\n // `offset-anchor` pins the element's own origin point (o, in its box) to the path.\n // Anchoring 0 0 on the raw translates put the element's CORNER on a corner-trajectory\n // and pivoted rotation about the corner — visibly off the path for centered origins.\n const first = keyframeValue(kfs[0]) as { origin?: PxVec2 } | undefined;\n const anchor: PxVec2 = first?.origin && first.origin.length >= 2\n ? [first.origin[0], first.origin[1]] : [0, 0];\n\n const points: Array<PxVec2> = [];\n for (const kf of kfs) {\n const v = keyframeValue(kf) as { translate?: PxVec2; origin?: PxVec2 } | undefined;\n const tr = v?.translate;\n if (!tr || tr.length < 2) return undefined;\n const parts = Object.keys(v as object);\n if (parts.some(p => p !== 'translate' && p !== 'origin')) return undefined;\n // An origin ANIMATED across keyframes shifts the pivot mid-flight — inexpressible\n // as a single offset-anchor; bail to the sampled pipeline.\n const o = v?.origin ?? [0, 0];\n if (o[0] !== anchor[0] || o[1] !== anchor[1]) return undefined;\n points.push([tr[0] + anchor[0], tr[1] + anchor[1]]);\n }\n // No tangent anywhere ⇒ straight lines ⇒ a plain translate animation renders this\n // everywhere with no support floor; the offset encoding buys nothing.\n if (!kfs.some(kf => keyframeTangentIn(kf) || keyframeTangentOut(kf))) return undefined;\n\n // Path string + per-segment arc lengths, in one pass.\n let d = 'M' + fmt(points[0][0]) + ',' + fmt(points[0][1]);\n const segLens: Array<number> = [];\n for (let i = 0; i < points.length - 1; i++) {\n const p0 = points[i], p1 = points[i + 1];\n const to = keyframeTangentOut(kfs[i]) ?? [0, 0];\n const ti = keyframeTangentIn(kfs[i + 1]) ?? [0, 0];\n const c1: PxVec2 = [p0[0] + to[0], p0[1] + to[1]];\n const c2: PxVec2 = [p1[0] + ti[0], p1[1] + ti[1]];\n d += 'C' + fmt(c1[0]) + ',' + fmt(c1[1]) + ',' + fmt(c2[0]) + ',' + fmt(c2[1]) + ',' + fmt(p1[0]) + ',' + fmt(p1[1]);\n segLens.push(cubicLength(p0, c1, c2, p1));\n }\n const total = segLens.reduce((a, b) => a + b, 0);\n if (!(total > 0)) return undefined;\n\n // `offset-distance` percentages are ARC-LENGTH fractions — each keyframe lands at its\n // cumulative share of the path length (mirrors the Editor's `segments.endPct`).\n const distanceKfs: Array<PxKeyframe> = [];\n let cum = 0;\n for (let i = 0; i < kfs.length; i++) {\n if (i > 0) cum += segLens[i - 1];\n const out: PxKeyframe = { t: keyframeTime(kfs[i]), v: cum / total } as never;\n const e = keyframeEasing(kfs[i]);\n if (e !== undefined) (out as { e?: unknown }).e = e;\n distanceKfs.push(out);\n }\n\n return { pathStr: d, distanceKfs, autoOrient: !!propAnim.autoOrient, anchor };\n}\n\n/**\n * Walks the tree and rewrites every `offsetPath`-marked transform binding into\n * offset-path styles + an `offsetDistance` binding. Non-candidates are left untouched.\n * Runs BEFORE loop expansion so a carried `loop` expands on the new binding.\n */\nexport function materializeOffsetPathsInTree(root: PxAnimatedSvgDocument): PxAnimatedSvgDocument {\n const walk = (node: PxNode): PxNode => {\n let out = node;\n const anim = node.animate as Record<string, PxPropertyAnimation> | undefined;\n const transform = anim?.['transform'];\n if (transform) {\n const built = buildOffsetPath(transform);\n if (built) {\n const newAnimate: Record<string, PxPropertyAnimation> = { ...anim };\n delete newAnimate[TRANSFORM_ATTR];\n const distance: PxPropertyAnimation = { keyframes: built.distanceKfs as never } as never;\n if (transform.loop !== undefined) distance.loop = transform.loop;\n newAnimate[OFFSET_DISTANCE_ATTR] = distance;\n\n // The element's position now comes from the path — a remaining static\n // `translate` (the design base value) would ADD to it. Other static parts\n // (rotate/scale/origin) survive; the candidate check above guarantees the\n // animation itself carried none.\n const staticTr = node.transform as Record<string, unknown> | string | undefined;\n let newTransform = staticTr;\n if (staticTr && typeof staticTr === 'object') {\n const t = { ...staticTr };\n delete t[TRANSFORM_PART.translate];\n delete t[TRANSFORM_PART.origin]; // pivot is offset-anchor now; alone it is identity\n newTransform = Object.keys(t).length ? t : undefined;\n }\n\n out = {\n ...node,\n animate: newAnimate,\n style: {\n ...(node.style as Record<string, unknown> | undefined),\n offsetPath: \"path('\" + built.pathStr + \"')\",\n offsetAnchor: fmt(built.anchor[0]) + 'px ' + fmt(built.anchor[1]) + 'px',\n offsetRotate: built.autoOrient ? 'auto' : '0deg',\n offsetDistance: '0%',\n },\n } as PxNode;\n if (newTransform !== undefined) (out as { transform?: unknown }).transform = newTransform;\n else delete (out as { transform?: unknown }).transform;\n }\n }\n if (out.children?.length) {\n const children = out.children.map(walk);\n if (children.some((c, i) => c !== out.children![i])) out = { ...out, children };\n }\n return out;\n };\n return walk(root) as PxAnimatedSvgDocument;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n/**\n * `<use>` instance materializer.\n *\n * WAAPI / CSS animations applied to an SVG source element don't reliably\n * render through `<use>` shadow trees in Chrome and Safari — the source\n * animates, the `<use>` shows static. This module fixes that by deep-cloning\n * every animated target into the corresponding `<use>` site (with fresh ids\n * and rewritten internal refs), and replacing the `<use>` with a `<g>` that\n * carries the use's transform/x/y attributes. Both engines (frames + waapi)\n * then animate the cloned nodes directly.\n *\n * Shares the deep-clone + id-regen + ref-rewrite primitives with the retime\n * effect (see `PxNodeCloneUtil`).\n *\n * Immutable: returns the input by reference when no `<use>` needs\n * materialization; otherwise returns a new tree sharing untouched sub-trees.\n */\n\nimport { applyUseOffsetToG, deepClonePxNode, regenerateIdsAndRewriteRefs } from '../util/PxNodeCloneUtil';\nimport type { PxNode } from '../format/PxAnimatorTypes';\n\n\n/**\n * Walks `root`. For every `<use>` whose `href` target subtree contains any\n * `animate` bucket (recursively, following further `<use>` chains and\n * children), replaces the `<use>` with a `<g>` carrying a deep clone of the\n * target. The clone gets a fresh id on every node, and any internal `href` /\n * `url(#…)` refs that pointed at ids inside the cloned subtree are rewritten\n * to the new ids. Refs that point OUTSIDE the cloned subtree (e.g. a sibling\n * gradient) are left untouched.\n *\n * Recursive: if the materialized clone itself contains a `<use>` that needs\n * materialization, that's handled in the same pass.\n * @internal\n */\nexport function materializeAnimatedUseInstances(root: PxNode): PxNode {\n const idMap = buildIdMap(root);\n const animatedIds = computeAnimatedSubtreeIds(root, idMap);\n if (animatedIds.size === 0) return root;\n\n let idCounter = 0;\n const genId = (): string => '_lw_use_mat_' + (++idCounter);\n\n // The use's default width/height resolves against its viewport (= the\n // root `<svg>`). We snapshot the root's viewBox dimensions once so the\n // symbol-rewrite below can compute the viewBox-to-use-viewport scaling\n // without re-walking the tree for each use.\n const rootViewport = readRootViewport(root);\n\n // Collects clipPath / other defs generated by the symbol-rewrite path. After\n // the walk completes, these are spliced into the root tree's `<defs>`.\n const defsCollector: Array<PxNode> = [];\n const walked = walkAndMaterialize(root, idMap, animatedIds, genId, defsCollector, rootViewport);\n if (defsCollector.length === 0) return walked;\n\n // Append a `<defs>` child carrying every collected def. Browsers honor\n // multiple `<defs>` on the same SVG root, so we don't need to splice into\n // an existing one (which would also be more invasive — defs may have been\n // shared by reference with the original tree).\n const defsNode: PxNode = { type: 'defs', children: defsCollector };\n const newChildren = [...(walked.children ?? []), defsNode];\n return { ...walked, children: newChildren };\n}\n\n\n/** Reads the root `<svg>`'s effective viewport dimensions. Used as the\n * default for `<use>` width/height (which default to 100% of viewport per\n * SVG spec). Tries `viewBox` first (the typical authored shape from our\n * serializer), then falls back to explicit `width`/`height` attrs. */\nfunction readRootViewport(root: PxNode): [number, number] {\n const vb = parseViewBox((root as { viewBox?: unknown }).viewBox);\n if (vb) return [vb[2], vb[3]];\n const w = numericAttr((root as { width?: unknown }).width);\n const h = numericAttr((root as { height?: unknown }).height);\n if (w !== undefined && h !== undefined) return [w, h];\n // No info — fall back to a 1:1 viewport so the scale degrades to \"identity\n // along the limiting axis\" (= the historical no-scale behavior). Better\n // than NaN; the failing case is \"root has no viewport info at all\".\n return [1, 1];\n}\n\nfunction numericAttr(v: unknown): number | undefined {\n if (typeof v === 'number') return v;\n if (typeof v === 'string') {\n // Strip a trailing `px` or `%` — for percentages we don't have an\n // outer container to resolve against, so we treat the raw number as\n // the absolute size (correct when the user only authored a viewBox\n // and width/height match it).\n const n = parseFloat(v);\n return Number.isFinite(n) ? n : undefined;\n }\n return undefined;\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internals\n// ─────────────────────────────────────────────────────────────────────────────\n\n\nfunction buildIdMap(root: PxNode): Map<string, PxNode> {\n const map = new Map<string, PxNode>();\n const visit = (n: PxNode): void => {\n if (typeof n.id === 'string') map.set(n.id, n);\n n.children?.forEach(visit);\n };\n visit(root);\n return map;\n}\n\n\n/** Returns the set of ids whose subtree (including descendants and any\n * `<use>`-referenced sub-subtrees) contains at least one `animate` bucket.\n * Uses a per-walk `visited` set to avoid infinite recursion on `<use>` cycles. */\nfunction computeAnimatedSubtreeIds(root: PxNode, idMap: Map<string, PxNode>): Set<string> {\n const cache = new WeakMap<PxNode, boolean>();\n const result = new Set<string>();\n\n const hasAnim = (n: PxNode, visiting: Set<PxNode>): boolean => {\n const cached = cache.get(n);\n if (cached !== undefined) return cached;\n if (visiting.has(n)) return false; // cycle guard\n visiting.add(n);\n\n let r = false;\n if (n.animate && typeof n.animate === 'object' && !Array.isArray(n.animate)) {\n for (const _ in n.animate) { r = true; break; }\n }\n if (!r && n.children) {\n for (const ch of n.children) {\n if (hasAnim(ch, visiting)) { r = true; break; }\n }\n }\n if (!r && n.type === 'use' && typeof n.href === 'string') {\n const targetId = stripHash(n.href);\n const target = targetId ? idMap.get(targetId) : undefined;\n if (target) r = hasAnim(target, visiting);\n }\n\n visiting.delete(n);\n cache.set(n, r);\n return r;\n };\n\n for (const [id, node] of idMap) {\n if (hasAnim(node, new Set())) result.add(id);\n }\n return result;\n}\n\n\nfunction stripHash(href: unknown): string | undefined {\n if (typeof href !== 'string') return undefined;\n return href.startsWith('#') ? href.slice(1) : href;\n}\n\n\n/** Builds the materialized `<g>` replacement for one `<use>` whose target's\n * subtree is animated. Carries the use's transform / position attributes. */\nfunction materializeOneUse(\n useNode: PxNode,\n target: PxNode,\n idMap: Map<string, PxNode>,\n animatedIds: Set<string>,\n genId: () => string,\n defsCollector: Array<PxNode>,\n rootViewport: [number, number],\n): PxNode {\n const clone = deepClonePxNode(target);\n regenerateIdsAndRewriteRefs(clone, genId);\n\n // Use's effective width/height — explicit attrs if set; else the referenced\n // `<symbol>`'s OWN viewBox size (a `<use>` on a symbol-with-viewBox renders at\n // the symbol's natural size, NOT 100% of the viewport — the latter scales the\n // symbol up, e.g. 80×80 → 139×139). Our serializer now always emits explicit\n // width/height, so this fallback is the safety net for inputs that omit them.\n // Falls back to the root viewport only when there's no viewBox to size from.\n const symbolViewBox = clone.type === 'symbol' ? parseViewBox((clone as { viewBox?: unknown }).viewBox) : undefined;\n const useW = numericAttr((useNode as { width?: unknown }).width) ?? symbolViewBox?.[2] ?? rootViewport[0];\n const useH = numericAttr((useNode as { height?: unknown }).height) ?? symbolViewBox?.[3] ?? rootViewport[1];\n\n // `<symbol>` is invisible unless instantiated by `<use>` — a bare clone\n // of it inside our wrapping `<g>` would render nothing. Rewrite the\n // clone-root `<symbol>` into a `<g>` carrying the symbol's viewBox-derived\n // transform + clip (mirroring the browser's `<use href=\"#symbol\">`\n // viewport mapping). Any clipPath defs created go into `defsCollector`.\n const rewrittenClone = clone.type === 'symbol'\n ? rewriteSymbolRootToGroup(clone, genId, defsCollector, useW, useH)\n : clone;\n\n // Recurse into the clone — any nested `<use>` it contains may itself\n // reference an animated subtree and need materialization.\n const materializedClone = walkAndMaterialize(rewrittenClone, idMap, animatedIds, genId, defsCollector, rootViewport);\n\n // Replace `<use>` with `<g>` wrapping the clone. Keep all of use's own\n // attrs except `href` (now meaningless). `<g>` has no `x`/`y`, so the use's\n // position offset is converted to a `translate(x,y)` (applied AFTER any\n // transform — see `applyUseOffsetToG`) rather than silently dropped.\n const newNode: PxNode = { ...useNode, type: 'g', children: [materializedClone] };\n delete (newNode as { href?: string }).href;\n // The viewport-mapping `<g>` we just emitted on the clone root already\n // carries its own width/height-derived scale/translate. Drop the use's\n // own `width`/`height` so they don't appear on the outer `<g>` (where\n // they'd be ignored anyway — `<g>` has no width/height — but emitting\n // them as attributes would be misleading).\n delete (newNode as { width?: unknown }).width;\n delete (newNode as { height?: unknown }).height;\n return applyUseOffsetToG(newNode);\n}\n\n\n/** `<symbol>` doesn't render directly. To keep the visual result when a\n * cloned `<symbol>` lands as the root of a materialized `<use>` target, we\n * rewrite it into a `<g>` whose transform + clip mirror the browser's\n * `<use href=\"#symbol-with-viewBox\">` viewport mapping:\n *\n * - `transform = \"translate(xOff, yOff) scale(s) translate(-vbX, -vbY)\"`\n * where `s` and the centering offsets are derived from the symbol's\n * `viewBox` and the use's effective width/height per `preserveAspectRatio`\n * (default `xMidYMid meet`). Without the scale the clone renders at 1:1\n * in symbol coords, which doesn't match `<use>`'s natural behavior.\n * - `clipPath = \"url(#<fresh-id>)\"` referencing a `<rect>` matching the\n * viewBox extent (in symbol coords). With the surrounding transform\n * above the clip rect maps to the visible viewport area, matching how\n * `<symbol>` viewport clipping works.\n *\n * Symbols without a `viewBox` rewrite to a plain `<g>` — no viewport\n * mapping to preserve.\n *\n * The created clipPath defs are appended to `defsCollector`; the caller\n * splices them into the root tree once materialization finishes.\n */\nfunction rewriteSymbolRootToGroup(\n symbolNode: PxNode,\n genId: () => string,\n defsCollector: Array<PxNode>,\n useW: number,\n useH: number,\n): PxNode {\n const viewBox = parseViewBox((symbolNode as { viewBox?: unknown }).viewBox);\n const g: PxNode = { ...symbolNode, type: 'g' };\n // Strip symbol-only attributes that don't belong on `<g>`.\n delete (g as { viewBox?: unknown }).viewBox;\n delete (g as { preserveAspectRatio?: unknown }).preserveAspectRatio;\n delete (g as { width?: unknown }).width;\n delete (g as { height?: unknown }).height;\n\n if (!viewBox) return g; // no viewBox → no transform / clip required\n\n const [vbX, vbY, vbW, vbH] = viewBox;\n // `xMidYMid meet` (SVG default) — uniform scale, fit the longer axis;\n // center the shorter axis. Mirrors what the browser does on `<use>` for\n // a symbol-with-viewBox without an explicit preserveAspectRatio override.\n const scale = vbW > 0 && vbH > 0 ? Math.min(useW / vbW, useH / vbH) : 1;\n const xOff = (useW - vbW * scale) / 2;\n const yOff = (useH - vbH * scale) / 2;\n\n const parts: Array<string> = [];\n if (xOff !== 0 || yOff !== 0) parts.push('translate(' + xOff + ',' + yOff + ')');\n if (scale !== 1) parts.push('scale(' + scale + ')');\n if (vbX !== 0 || vbY !== 0) parts.push('translate(' + (-vbX) + ',' + (-vbY) + ')');\n if (parts.length) (g as { transform?: string }).transform = parts.join('');\n\n const clipId = genId();\n defsCollector.push({\n type: 'clipPath',\n id: clipId,\n children: [{ type: 'rect', x: vbX, y: vbY, width: vbW, height: vbH }],\n } as PxNode);\n (g as { clipPath?: string }).clipPath = 'url(#' + clipId + ')';\n return g;\n}\n\n\nfunction parseViewBox(v: unknown): [number, number, number, number] | undefined {\n if (typeof v !== 'string') return undefined;\n const parts = v.trim().split(/[\\s,]+/).map(Number);\n if (parts.length < 4 || parts.some(n => !Number.isFinite(n))) return undefined;\n return [parts[0], parts[1], parts[2], parts[3]];\n}\n\n\nfunction walkAndMaterialize(\n node: PxNode,\n idMap: Map<string, PxNode>,\n animatedIds: Set<string>,\n genId: () => string,\n defsCollector: Array<PxNode>,\n rootViewport: [number, number],\n): PxNode {\n if (node.type === 'use' && typeof node.href === 'string') {\n const targetId = stripHash(node.href);\n if (targetId && animatedIds.has(targetId)) {\n const target = idMap.get(targetId);\n if (target) return materializeOneUse(node, target, idMap, animatedIds, genId, defsCollector, rootViewport);\n }\n }\n\n if (!node.children) return node;\n let changed = false;\n const newChildren = node.children.map(ch => {\n const m = walkAndMaterialize(ch, idMap, animatedIds, genId, defsCollector, rootViewport);\n if (m !== ch) changed = true;\n return m;\n });\n return changed ? { ...node, children: newChildren } : node;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n/**\n * Single-call materialization pipeline.\n *\n * Runs the full sequence of document-level transformations that turn the\n * wire-format `PxAnimatedSvgDocument` into a flat tree any renderer can\n * consume. The player itself calls this from `createAnimatorImpl`; the same\n * function is exported for the Editor (or any external caller) so the\n * Editor's flat-export path is GUARANTEED to be byte-identical to what the\n * player sees internally — no parallel pipeline to drift.\n *\n * 1. `materializeNodeEffects` — `node.effects` (ref / transformation / repeater /\n * maskedBy / strokeTrim / retime) materialized into wrappers, defs, clones.\n * 2. `materializeInternalLoopsInTree` — every `propAnim.loop` expanded into\n * repeated keyframes filling the duration.\n * 3. `materializeMotionPathsInTree` — `transform` kfs with tangents +\n * `autoOrient` flattened into sampled `{translate, rotate}` kfs. Only\n * for `engine === waapi` — frames-mode keeps the parametric form and\n * evaluates per frame for max spatial fidelity.\n * 4. `materializeAnimatedUseInstances` — `<use>` referencing an animated\n * subtree replaced with a `<g>` carrying a deep clone (fresh ids).\n * Only for `engine === waapi` — frames-mode updates source attrs\n * per frame, which propagate through `<use>` shadow trees natively.\n *\n * Immutable: input doc is never mutated. Steps that didn't apply (the engine\n * gating or \"nothing to do\" early-outs) return the input by reference.\n */\n\nimport { materializeNodeEffects } from '../effects/PlayerEffectsUtil';\nimport { PX_DEFAULT_DURATION_MS } from '../util/PxAnimatorUtil';\nimport { materializeInternalLoopsInTree } from '../animation/PxDefinitions';\nimport { materializeOffsetPathsInTree } from './PxOffsetPathMaterializer';\nimport { materializeMotionPathsInTree } from './PxMotionPath';\nimport type { MotionPathMaterializationOptions } from './PxMotionPath';\nimport { getAnimatorConfig, PxTimelineEngine } from '../format/PxAnimatorConstants';\nimport type { PxAnimatedSvgDocument, PxNode } from '../format/PxAnimatorTypes';\nimport { materializeAnimatedUseInstances } from './PxAnimatorUseMaterializer';\n\n\n/** Options accepted by {@link materializeAllInTree}. Mostly forwarded to the\n * per-stage materializers; ordering is fixed (see module doc). * @internal\n */\nexport interface PxMaterializeAllOptions {\n /** Knobs forwarded to `materializeMotionPathsInTree`. Only consulted for\n * `engine === waapi` — frames-mode skips that stage entirely. */\n motionPath?: MotionPathMaterializationOptions;\n}\n\n\n/** @public @advanced */\nexport function materializeAllInTree(\n doc: PxAnimatedSvgDocument,\n engine: PxTimelineEngine,\n options?: PxMaterializeAllOptions,\n): PxAnimatedSvgDocument {\n // 1. Effects → structural materialization. Always runs; returns a fresh root.\n let root = materializeNodeEffects(doc).root as PxAnimatedSvgDocument;\n\n // 1b. `alongPathMode: 'offsetPath'` transforms → CSS Motion Path (offset-path style\n // + `offsetDistance` binding). Both engines: frames drives `offset-distance` per\n // rAF, waapi animates it natively (percent values). BEFORE loop expansion so a\n // carried `loop` expands on the rewritten binding.\n root = materializeOffsetPathsInTree(root);\n\n // 2. Loops → flat repeated keyframes. Always runs (both engines need flat\n // kfs covering the duration; per-binding expansion in\n // `normalizeKeyframes` becomes a no-op once the loop field is consumed).\n const duration = getAnimatorConfig(root)?.duration ?? PX_DEFAULT_DURATION_MS;\n root = materializeInternalLoopsInTree(root, duration);\n\n if (engine === PxTimelineEngine.native) {\n // 3. Motion-along-path → sampled `{translate, rotate}` kfs. WAAPI can't\n // evaluate parametric tangents; frames-mode does that per frame so\n // we skip this for frames.\n root = materializeMotionPathsInTree(root, options?.motionPath);\n\n // 4. <use> referencing animated subtrees → <g> wrapping a fresh clone.\n // WAAPI / CSS animations don't reliably propagate through SVG <use>\n // shadow trees in Chrome / Safari; frames-mode updates source\n // attributes per frame and the shadow tree picks those up natively.\n root = materializeAnimatedUseInstances(root);\n\n // 5. Prune <defs> `<g>`/`<symbol>` entries that step 4 orphaned — i.e. no\n // `<use>` references them any more (the animated uses that did got\n // inlined into `<g>`+clones). waapi-only: frames keeps `<use href>`,\n // so nothing is orphaned there.\n root = pruneUnreferencedDefs(root);\n }\n\n return root;\n}\n\n\n/**\n * Removes orphaned `<defs>` entries: direct `<defs>` children of type `<g>` /\n * `<symbol>` whose `id` is no longer targeted by ANY `<use href>` in the tree.\n * Runs after step 4 (`materializeAnimatedUseInstances`), which inlines animated\n * `<use>`s and thereby leaves their former defs targets unreferenced.\n *\n * Loops to a fixpoint so chains collapse fully: pruning an entry can drop the\n * `<use>`s inside it, which in turn orphans the entries THOSE referenced. The\n * loop also drops a `<defs>` node once pruning has emptied it.\n *\n * Scope is intentionally limited to `<g>`/`<symbol>` (the `<use>`-target element\n * types) so `url(#…)`-referenced defs (gradients / masks / clipPaths / filters)\n * are never touched. Mutates `root` in place — safe, as it's a freshly\n * materialized tree owned by {@link materializeAllInTree}.\n */\nfunction pruneUnreferencedDefs(root: PxAnimatedSvgDocument): PxAnimatedSvgDocument {\n const stripHash = (h: string): string => (h.startsWith('#') ? h.slice(1) : h);\n const walk = (n: PxNode, fn: (n: PxNode) => void): void => { fn(n); n.children?.forEach(c => walk(c, fn)); };\n\n let changed = true;\n while (changed) {\n changed = false;\n const referenced = new Set<string>();\n walk(root, n => {\n if (n.type === 'use' && typeof n.href === 'string') referenced.add(stripHash(n.href));\n });\n walk(root, n => {\n if (!n.children) return;\n let kept = n.children;\n // (a) inside <defs>: drop <g>/<symbol> entries no <use> targets any more\n if (n.type === 'defs') {\n kept = kept.filter(c =>\n !((c.type === 'g' || c.type === 'symbol') && typeof c.id === 'string' && !referenced.has(c.id)));\n }\n // (b) anywhere: drop a now-empty <defs> child\n kept = kept.filter(c => !(c.type === 'defs' && (!c.children || c.children.length === 0)));\n if (kept.length !== n.children.length) { n.children = kept; changed = true; }\n });\n }\n return root;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// ONE time contract for every engine (API review §3). The same three calls used to mean three\n// different things:\n//\n// | engine | getCurrentTime() | setCurrentTime(t) | setPlaybackRate(0) |\n// |-----------------|-------------------------|-------------------|--------------------|\n// | web, WAAPI | ms across the whole run | NOT clamped | accepted |\n// | web, frame loop | ms across the whole run | clamped | rejected + warn |\n// | React Native | ms within ONE iteration | wrapped | rejected + warn |\n//\n// So a slider built on `getCurrentTime()` jumped back every iteration on React Native, and\n// `timeline.engine: 'auto'` meant two documents on one page could answer the same call\n// differently. The contract every player now implements:\n//\n// - time is ms from the start of the WHOLE run, iterations included — never per-iteration;\n// - a seek clamps to [0, seekCeilingMs];\n// - a rate of 0 is rejected everywhere, with this one message.\n\n/** The one message every engine prints for a rejected rate. @public @advanced */\nexport const PX_RATE_REJECTED = 'setPlaybackRate: rate must be finite and non-zero';\n\n/**\n * A playback rate is usable when it is finite and non-zero.\n *\n * 0 is rejected rather than accepted: it freezes the animation in a state indistinguishable\n * from a stuck player, and `pause()` already says that properly. Two of the three engines\n * rejected it already — this makes the third agree.\n * @public @advanced\n */\nexport function isValidPlaybackRate(rate: number): boolean {\n return Number.isFinite(rate) && rate !== 0;\n}\n\n/**\n * Highest seekable time, ms — `duration × iterations`.\n *\n * `Infinity` for an endless timeline, so callers must test `Number.isFinite` before using it\n * as an upper bound. This is the SEEK ceiling, which is deliberately not the same thing as the\n * span `progress` covers — see `progressSpanMs`.\n * @public @advanced\n */\nexport function seekCeilingMs(durationMs: number, iterations: number): number {\n if (!(durationMs > 0)) return 0;\n if (iterations === Infinity) return Infinity;\n return durationMs * (iterations > 0 ? iterations : 1);\n}\n\n/**\n * The span `progress` 0→1 covers, ms.\n *\n * An endless timeline maps progress onto ONE iteration — the rule the components already\n * document for the `progress` prop (\"0–1 of duration × iterations, one iteration when\n * iterations is 'infinite'\"). Always finite, so it is safe as a divisor.\n * @public @advanced\n */\nexport function progressSpanMs(durationMs: number, iterations: number): number {\n if (!(durationMs > 0)) return 0;\n if (iterations === Infinity) return durationMs;\n return durationMs * (iterations > 0 ? iterations : 1);\n}\n\n/**\n * Clamps a seek into [0, ceiling].\n *\n * `NaN` and anything below 0 land on 0. `Infinity` means \"the end\", so it clamps to the ceiling\n * like any other overshoot — except on an endless timeline, where there is no end to land on and\n * a non-finite playhead would poison every later read, so that reads as 0.\n * @public @advanced\n */\nexport function clampSeekMs(timeMs: number, ceilingMs: number): number {\n if (Number.isNaN(timeMs) || timeMs < 0) return 0;\n if (Number.isFinite(ceilingMs)) return timeMs > ceilingMs ? ceilingMs : timeMs;\n return Number.isFinite(timeMs) ? timeMs : 0;\n}\n\n/**\n * Whole-run ms → 0–1.\n *\n * A finite timeline clamps at both ends. An endless one wraps within the current iteration,\n * so the value stays meaningful however long it has been running. A zero-length span reads as\n * 0 rather than NaN.\n * @public @advanced\n */\nexport function timeToProgress(timeMs: number, durationMs: number, iterations: number): number {\n const span = progressSpanMs(durationMs, iterations);\n if (!(span > 0) || !Number.isFinite(timeMs)) return 0;\n if (timeMs <= 0) return 0;\n if (iterations === Infinity) return (timeMs % span) / span;\n return timeMs >= span ? 1 : timeMs / span;\n}\n\n/** 0–1 → whole-run ms, clamped into the span. A non-finite progress reads as 0. @public @advanced */\nexport function progressToTimeMs(progress: number, durationMs: number, iterations: number): number {\n const span = progressSpanMs(durationMs, iterations);\n if (!(span > 0) || !Number.isFinite(progress)) return 0;\n if (progress <= 0) return 0;\n return progress >= 1 ? span : progress * span;\n}\n\n/** A playhead that keeps whole-run time across iterations. See `createRunClock`. @public @advanced */\nexport interface PxRunClock {\n /** Whole-run position right now, ms, clamped to the ceiling. */\n now(): number;\n /** True while time is advancing. */\n isRunning(): boolean;\n /** Start or resume at `rate`, optionally from a given whole-run time. */\n start(rate: number, atMs?: number): void;\n /** Stop advancing, freezing the current position. */\n stop(): void;\n /** Jump to a whole-run time; keeps running if it already was. */\n seek(ms: number): void;\n}\n\n/**\n * A whole-run playhead that survives repetition.\n *\n * React Native drives playback with Reanimated's `withRepeat`, whose shared value only ever\n * holds the position WITHIN one iteration and which never reports how many iterations have\n * elapsed. Whole-run time therefore cannot be recovered from it: under `alternate` the value\n * runs backwards rather than wrapping, which is indistinguishable from a negative rate. So the\n * time is kept on a clock of its own — the same thing the frame-loop engine does inline.\n *\n * `nowFn` is injectable so this is testable without real time passing.\n * @public @advanced\n */\nexport function createRunClock(ceilingMs: number, nowFn: () => number = Date.now): PxRunClock {\n let baseMs = 0; // whole-run ms as of the last start/seek/stop\n let startedAt = 0; // nowFn() when running began\n let running = false; // a FLAG, not `startedAt !== 0`: a time source may legitimately\n let rate = 1; // read 0, and `Date.now()` never does — so the bug would hide.\n\n const value = (): number => running\n ? clampSeekMs(baseMs + (nowFn() - startedAt) * rate, ceilingMs)\n : clampSeekMs(baseMs, ceilingMs);\n\n return {\n now: value,\n isRunning: () => running,\n start: (r: number, atMs?: number) => {\n baseMs = clampSeekMs(atMs ?? value(), ceilingMs);\n rate = isValidPlaybackRate(r) ? r : 1;\n startedAt = nowFn();\n running = true;\n },\n stop: () => {\n baseMs = value();\n startedAt = 0;\n running = false;\n },\n seek: (ms: number) => {\n baseMs = clampSeekMs(ms, ceilingMs);\n if (running) startedAt = nowFn();\n },\n };\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { type PxAnimatedSvgDocument, type PxAnimatorApi, type PxEngineCallbacks } from '../format/PxAnimatorTypes';\nimport { getAnimatorConfig, PxTimelineEngine } from '../format/PxAnimatorConstants';\nimport { camelCaseToKebabWordIfNeeded, clamp, PX_DEFAULT_DURATION_MS, PX_STYLE_ATTR_NAMES } from '../util/PxAnimatorUtil';\nimport { calcAnimationValues, normalizeBindings } from '../animation/PxDefinitions';\nimport { clampSeekMs, isValidPlaybackRate, progressToTimeMs, PX_RATE_REJECTED, timeToProgress } from './PxPlaybackTime';\n\n\n// Frame scheduling — resolved LAZILY from globalThis on every call so test\n// harnesses that install fake timers (and hosts that polyfill rAF late) are\n// honored. Falls back to a ~60fps setTimeout when rAF is unavailable.\nfunction requestFrame(cb: () => void): number {\n const g: any = globalThis as any;\n if (typeof g.requestAnimationFrame === 'function') return g.requestAnimationFrame(cb);\n return g.setTimeout(cb, 16) as unknown as number;\n}\n\nfunction cancelFrame(handle: number): void {\n const g: any = globalThis as any;\n if (typeof g.cancelAnimationFrame === 'function') { g.cancelAnimationFrame(handle); return; }\n g.clearTimeout(handle);\n}\n\n/**\n * Platform adapter interface for abstracting platform-specific operations.\n * @public\n */\nexport interface PxPlatformAdapter {\n\n /** Check if the root element is still connected/mounted */\n isConnected(): boolean;\n\n /** Set an attribute on an element by id */\n setAttribute(id: string, attrName: string, value: string): void;\n}\n\n/**\n * Creates an animator instance that uses a frame loop for animations.\n * This is the abstract/platform-agnostic version.\n *\n * @param adapter Platform adapter for DOM/environment operations.\n * @param callbacks Optional lifecycle callbacks.\n * @returns A PxAnimatorApi instance.\n * @public @advanced\n */\nexport function createAdapterAnimator(\n doc: PxAnimatedSvgDocument,\n adapter: PxPlatformAdapter,\n callbacks?: PxEngineCallbacks\n): PxAnimatorApi {\n\n const config = getAnimatorConfig(doc) || {};\n\n const bindings = normalizeBindings(doc, PxTimelineEngine.js);\n\n // iterations: either number or Infinity\n const _iterations = config.iterations;\n let iterations: number = 1;\n if (typeof _iterations === 'number') iterations = _iterations || 1;\n if (_iterations === 'infinite') iterations = Infinity;\n if (iterations < 1) iterations = 1;\n\n const duration = +(config.duration || PX_DEFAULT_DURATION_MS); // per-iteration duration (ms), cannot be 0!\n const totalDuration = duration && iterations ?\n duration * (iterations === Infinity ? Infinity : iterations) :\n (duration ? (iterations ?? 1) * duration : 0);\n\n // direction handling\n const direction = config.direction || 'normal'; // 'normal' | 'reverse' | 'alternate' | 'alternate-reverse'\n\n // fill handling — mirrors the Web Animations API `fill` semantics as closely\n // as a frame-writer can. Default 'forwards' (hold final state) matches the\n // waapi engine and the config doc.\n const fill = config.fill ?? 'forwards';\n const fillsForwards = fill === 'forwards' || fill === 'both';\n const fillsBackwards = fill === 'backwards' || fill === 'both';\n\n ////////////////////////////////////////////////////////////////\n\n let timerId: number | null = null;\n let playing = false;\n\n // Accumulated logical time before the current run (ms). A positive\n // config.delay means \"wait before starting\" → the animation starts at\n // NEGATIVE logical time and reaches 0 after `delay` ms (same convention as\n // the Web Animations API). A negative config.delay means \"seek into the\n // animation\" → positive start time.\n let timeBeforeLastStartMs = -(config.delay || 0);\n let lastStartedTs = 0; // timestamp when last started/resumed\n let playbackRate = 1;\n\n let finishCalled = false; // ensures onFinish called once\n\n // Frame rate throttling\n const frameRate = config.frameRate;\n const minFrameIntervalMs = frameRate && frameRate > 0 ? 1000 / frameRate : 0;\n let lastRenderTs = 0; // timestamp of last render\n\n // Raw logical time — may be negative during the delay phase; not clamped\n // to the [0, totalDuration] range at the top end either. Internal use only.\n const getRawAnimTime = () => {\n // compute effective elapsed time (ms), taking playbackRate into account\n const runningElapsed = lastStartedTs ? (Date.now() - lastStartedTs) * playbackRate : 0;\n return timeBeforeLastStartMs + runningElapsed;\n };\n\n const getAnimCurrentTime = () => {\n let time = getRawAnimTime();\n\n // clamp to [0, totalDuration]\n if (Number.isFinite(totalDuration) && time > (totalDuration as number)) time = totalDuration as number;\n if (time < 0) time = 0;\n return time;\n };\n\n\n ////////////////////////////////////////////////////////////////\n\n\n // separate render function that uses a given currentTime (ms)\n function renderFrame(currentTimeMs: number) {\n\n function getEffectiveProgress() {\n // If no duration or duration === 0, set iteration/progress accordingly\n const safeDuration = duration > 0 ? duration : 1; // avoid division by zero\n\n let rawProgress = 0;\n let iteration = 0;\n if (duration > 0) {\n\n // rawProgress is normalized progress within the current iteration:\n // - first iteration: [0 .. 1]\n // - following iterations: (0 .. 1]\n //\n // iteration is the zero-based iteration index\n //\n // Examples (safeDuration = 3):\n // currentTimeMs = 0 → rawProgress = 0 , iteration = 0\n // currentTimeMs = 3 → rawProgress = 1 , iteration = 0\n // currentTimeMs = 3.0001 → rawProgress = 0.0001 , iteration = 1\n\n currentTimeMs = clamp(currentTimeMs, 0, iterations * safeDuration);\n\n iteration = Math.max(0, Math.ceil(currentTimeMs / safeDuration) - 1);\n iteration = Math.min(iteration, iterations - 1);\n\n // Time elapsed since the start of the current iteration\n const iterationTime = currentTimeMs - iteration * safeDuration;\n\n // Normalized progress (preserves fractional overflow after boundaries)\n rawProgress = clamp(iterationTime / safeDuration, 0, 1);\n } else {\n rawProgress = currentTimeMs; // We shouldn't be here\n }\n\n\n // compute per-iteration directional progress\n // start with baseProgress = rawProgress in [0,1)\n\n let baseProgress = rawProgress;\n // apply direction rules:\n // - normal: as is\n // - reverse: progress = 1 - baseProgress\n // - alternate: reverse on odd iterations\n // - alternate-reverse: reverse on even iterations\n const dir = direction || 'normal';\n let effectiveProgress = baseProgress;\n if (dir === 'reverse') {\n effectiveProgress = 1 - baseProgress;\n } else if (dir === 'alternate') {\n if (iteration % 2 === 1) effectiveProgress = 1 - baseProgress;\n } else if (dir === 'alternate-reverse') {\n // alternate-reverse: start reversed on iteration 0\n if (iteration % 2 === 0) effectiveProgress = 1 - baseProgress;\n } // else 'normal' -> keep\n return effectiveProgress;\n }\n let effectiveProgress = getEffectiveProgress(); // else 'normal' -> keep\n\n ////////////////////////////////////////////////////////////////\n\n // FIXME - pre-calc defs, then use\n\n for (const binding of bindings || []) {\n const animDef = binding.animate;\n if (!animDef || typeof animDef !== 'object' || Array.isArray(animDef)) {\n console.warn('Empty or unresolved binding', binding);\n continue;\n }\n\n // Calculate interpolated values for this binding's animation\n const computedValues = calcAnimationValues(animDef, effectiveProgress * duration);\n\n // Apply each computed attribute\n for (const [attrName, value] of Object.entries(computedValues)) {\n adapter.setAttribute(binding.id, attrName, value);\n }\n }\n }\n\n\n ////////////////////////////////////////////////////////////////\n\n const tick = () => {\n\n // if root element provided and detached, stop\n if (!adapter.isConnected()) {\n // stop animation loop (we'll let external code call play/resume)\n // do not call onFinish here. It's a detach situation.\n pauseAnim();\n return;\n }\n\n const currentTime = getAnimCurrentTime();\n\n // Delay phase (raw time < 0): the animation hasn't started yet.\n // With fill 'backwards'/'both' hold the first frame; otherwise leave\n // the element's static attributes untouched — mirrors WAAPI fill.\n // Checked BEFORE throttling so boundary states can't be skipped.\n const rawTime = getRawAnimTime();\n if (rawTime < 0 && playbackRate > 0) {\n if (fillsBackwards) renderFrame(0);\n return;\n }\n\n // Detect reverse playback reaching the start (natural end for rate < 0)\n // — mirrors WAAPI, where reverse playback fires `finish` at time 0.\n // pauseAnim runs FIRST (its trailing render must not clobber the\n // boundary frame rendered below).\n if (playbackRate < 0 && rawTime <= 0) {\n pauseAnim();\n renderFrame(0);\n if (!finishCalled) {\n finishCalled = true;\n callbacks?.onFinish?.();\n }\n return;\n }\n\n // Detect finished (natural end, forward playback)\n if (playbackRate > 0 && totalDuration && Number.isFinite(totalDuration) && currentTime >= (totalDuration as number)) {\n pauseAnim();\n // Render the end state — final frame when filling forwards, first\n // frame otherwise (closest frame-writer approximation of WAAPI\n // fill:'none'/'backwards', which reverts to the pre-animation state).\n renderFrame(fillsForwards ? (totalDuration as number) : 0);\n // call onFinish once\n if (!finishCalled) {\n finishCalled = true;\n callbacks?.onFinish?.();\n }\n return;\n }\n\n // Frame rate throttling: skip render if not enough time has passed.\n // Applies only to normal in-flight frames — boundary states above are\n // always rendered, so a throttled tick can never swallow the finish.\n if (minFrameIntervalMs > 0) {\n const now = Date.now();\n if (lastRenderTs && (now - lastRenderTs) < minFrameIntervalMs) {\n // Not enough time elapsed, skip this frame but continue the loop\n return;\n }\n lastRenderTs = now;\n }\n\n // Otherwise render normal frame\n renderFrame(currentTime);\n };\n\n\n ////////////////////////////////////////////////////////////////\n\n\n if (config.delay) {\n if (config.delay < 0) {\n // Negative delay = seek into the animation; render the seeked frame.\n renderFrame(getAnimCurrentTime());\n } else if (fillsBackwards) {\n // Positive delay = wait before start; hold the first frame only\n // when filling backwards (WAAPI convention).\n renderFrame(0);\n }\n }\n\n ////////////////////////////////////////////////////////////////\n\n const _isPlaying = () => {\n if (!playing) return false;\n if (!adapter.isConnected()) { return false; }\n if (playbackRate < 0) {\n // Reverse playback finishes when it reaches the start.\n return getRawAnimTime() > 0;\n }\n if (Number.isFinite(totalDuration) && getAnimCurrentTime() >= (totalDuration as number)) return false;\n return true;\n };\n\n const loopAnim = (isFirst?: boolean) => {\n if (timerId !== null) {\n cancelFrame(timerId);\n timerId = null;\n }\n\n if (!(isFirst || _isPlaying())) {\n // finished or not playing\n return;\n }\n\n timerId = requestFrame(() => {\n timerId = null;\n tick();\n loopAnim();\n });\n };\n\n const startAnim = () => {\n if (playing) return;\n\n // If the animation has already reached its natural end (for the current\n // playback direction), rewind to the opposite end before resuming —\n // mirrors the Web Animations API, where calling play() on a finished\n // animation auto-rewinds. Without this, resuming from the boundary\n // would re-finish on the first tick and leave the animation stuck.\n if (playbackRate >= 0) {\n if (Number.isFinite(totalDuration) && timeBeforeLastStartMs >= (totalDuration as number)) {\n timeBeforeLastStartMs = 0;\n }\n } else {\n // Reverse playback starting at (or before) the start: seek to the end.\n if (timeBeforeLastStartMs <= 0) {\n if (!Number.isFinite(totalDuration)) {\n // Cannot rewind to an infinite end (WAAPI throws here) —\n // warn and stay stopped instead of \"finishing\" instantly.\n console.warn('play: cannot start reverse playback of an infinite animation from time 0');\n return;\n }\n timeBeforeLastStartMs = totalDuration as number;\n }\n }\n\n // starting playback opens a new finish episode (mirrors WAAPI, where\n // play() always re-arms the finished promise / finish event)\n finishCalled = false;\n\n playing = true;\n lastStartedTs = Date.now();\n loopAnim(true);\n };\n\n const pauseAnim = () => {\n if (!playing) return;\n // Capture the RAW logical time — during the delay phase it is negative\n // and must stay negative, otherwise pausing would silently swallow the\n // remaining delay. Clamp only the top end.\n let raw = getRawAnimTime();\n if (Number.isFinite(totalDuration) && raw > (totalDuration as number)) raw = totalDuration as number;\n timeBeforeLastStartMs = raw;\n lastStartedTs = 0;\n playing = false;\n\n if (timerId !== null) {\n cancelFrame(timerId);\n timerId = null;\n }\n // Render a frame at the paused time to keep the DOM consistent —\n // except during the delay phase, where rendering would violate the\n // fill semantics (only 'backwards'/'both' show frame 0 before start).\n if (raw >= 0) {\n renderFrame(raw);\n } else if (fillsBackwards) {\n renderFrame(0);\n }\n };\n\n const cancelAnim = () => {\n pauseAnim();\n\n timeBeforeLastStartMs = 0;\n lastStartedTs = 0;\n playing = false;\n finishCalled = false;\n\n renderFrame(timeBeforeLastStartMs);\n\n callbacks?.onCancel?.();\n };\n\n const finishAnim = (callOnFinish = true) => {\n // jump to the end\n if (Number.isFinite(totalDuration)) {\n timeBeforeLastStartMs = totalDuration as number;\n } else {\n // infinity animations: set to current time (no-op)\n timeBeforeLastStartMs = getAnimCurrentTime();\n }\n lastStartedTs = 0;\n playing = false;\n // cancel any pending frame — pause/destroy early-return when not\n // playing, so a frame left queued here would never be cleaned up\n if (timerId !== null) {\n cancelFrame(timerId);\n timerId = null;\n }\n // Render the end state honoring `fill` (same rule as the natural\n // finish in `tick()`, and same as WAAPI where fill:'none' reverts even\n // after an explicit finish()).\n renderFrame(fillsForwards ? timeBeforeLastStartMs : 0);\n\n if (callOnFinish && !finishCalled) {\n finishCalled = true;\n callbacks?.onFinish?.();\n }\n };\n\n ////////////////////////////////////////////////////////////////\n\n const api: PxAnimatorApi = {\n\n \"isReady\": () => true,\n\n \"getRootElement\": () => null,\n\n \"isPlaying\": (): boolean => { return _isPlaying(); },\n\n \"play\": () => {\n startAnim();\n callbacks?.onPlay?.();\n },\n \"pause\": () => {\n pauseAnim();\n callbacks?.onPause?.();\n },\n \"cancel\": () => {\n cancelAnim();\n // onCancel already called in cancelAnim\n },\n \"finish\": () => {\n finishAnim(true);\n },\n\n \"setPlaybackRate\": (rate: number) => {\n if (!isValidPlaybackRate(rate)) {\n console.warn(PX_RATE_REJECTED);\n return;\n }\n // Preserve the RAW logical time when changing rate — during the\n // delay phase it is negative and clamping it to 0 would skip the\n // remaining delay. Clamp only the top end.\n let current = getRawAnimTime();\n if (Number.isFinite(totalDuration) && current > (totalDuration as number)) current = totalDuration as number;\n // a direction change opens a new finish episode\n if ((rate < 0) !== (playbackRate < 0)) finishCalled = false;\n playbackRate = rate;\n timeBeforeLastStartMs = current;\n if (playing) lastStartedTs = Date.now();\n },\n\n \"getCurrentTime\": (): number | null => { return getAnimCurrentTime(); },\n\n \"setCurrentTime\": (newTime: number) => {\n // One clamp rule for every engine (review §3).\n newTime = clampSeekMs(newTime, totalDuration as number);\n\n timeBeforeLastStartMs = newTime;\n if (playing) lastStartedTs = Date.now();\n // seeking re-arms the finish notification (mirrors WAAPI)\n if (!Number.isFinite(totalDuration) || newTime < (totalDuration as number)) finishCalled = false;\n // render immediately to reflect the change\n renderFrame(getAnimCurrentTime());\n },\n\n \"getCurrentProgress\": (): number | null => timeToProgress(getAnimCurrentTime(), duration, iterations),\n\n \"setCurrentProgress\": (progress: number) => {\n api.setCurrentTime(progressToTimeMs(progress, duration, iterations));\n },\n\n \"destroy\": () => {\n api.cancel();\n callbacks?.onRemove?.();\n }\n };\n\n ////////////////////////////////////////////////////////////////\n\n return api;\n}\n\n\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// EVERY THING A PLAYER CAN SAY, AS A NUMBER.\n//\n// The text of a diagnostic is not shipped. Each code's description lives in the comment on its\n// member and nowhere else, so a build carries the bare integer — `1204` — and not one byte of\n// prose. The descriptions are compiled into docs/diagnostics.md by\n// `node scripts/gen-diagnostics-md.mjs`, which `pnpm build` runs, and every diagnostic links there.\n//\n// A PLAIN `enum`, deliberately, and it costs almost nothing. Measured through this repo's own\n// esbuild, a plain enum and a `const enum` emit byte-identical output (`o(1301),o(1302)…`): in\n// bundle mode every member access is inlined to its number, and the object is then tree-shaken\n// out of any bundle that does not re-export it. A `const enum` would additionally force\n// `isolatedModules: false` on every package that imports it and would land in the published\n// `.d.ts`, where it breaks consumers who have `isolatedModules` on.\n//\n// Core's entry exports it as a VALUE — the players read members at runtime, and a host needs the\n// enum to switch on a code it was handed. What that costs, measured on the shipped build:\n//\n// - core's own ESM/CJS dist carries the object (that is what a host imports);\n// - the web UMD carries NONE of it — the inlined numbers and one link to the page, and not a\n// single member name — because web's entry deliberately does not re-export it. Re-exporting\n// it there would pin the object into the UMD, so don't.\n//\n// RULES FOR EDITING THIS FILE\n//\n// - A NUMBER IS FOREVER. Codes are a public identity: a host switches on them and users search\n// for them. Never renumber, never reuse. To retire one, keep the member and mark it\n// `@deprecated` in its comment — the page then says so too.\n// - The first line of the comment is the description the page shows. Keep it one sentence, in\n// the voice of the person who has to act on it.\n// - `@data` lines name the values the call site passes after the code, in order. They reach a\n// handler as `diagnostic.data` and are printed after the code on the console. Put the\n// specifics there (a selector, a URL, an inner message) — never in prose, which does not exist.\n// - Ranges group by area, so a reader can tell roughly where a code came from without the page.\n\n/**\n * Every diagnostic a player can report, as a number a host can switch on.\n *\n * The words are not in the build: each code's description lives in the comment on its member and\n * is compiled into `docs/diagnostics.md`, which every diagnostic's `message` links to.\n * @public\n */\nexport enum PxDiagnosticCode {\n\n // ── 1000 · building a player ─────────────────────────────────────────────────────────────\n\n /**\n * The player could not be built from this document — a document that passed validation but\n * still broke the builder, or a bug in the player. Report it.\n * @data the Error\n */\n buildFailed = 1001,\n\n /**\n * The file at `src` loaded, but it is not a Pixodesk animation document.\n * @data the `src` URL\n */\n invalidDocumentAtSrc = 1002,\n\n /**\n * The page could not fetch `src`. The file may be perfect — this is the request failing.\n * @data the `src` URL · the fetch error's message\n */\n loadFailed = 1003,\n\n /**\n * One element's animation could not be built, so that element stays static; the rest plays.\n * @data the Error\n */\n animationBuildFailed = 1004,\n\n // ── 1100 · what the document says ────────────────────────────────────────────────────────\n\n /**\n * An `effects` bucket does not match the schema, so that effect is ignored or degraded.\n * @data the problem, with the node's path\n */\n effectsShape = 1101,\n\n /**\n * Part of the per-instance `timeline` override could not be applied — most often clock-only\n * keys aimed at a scroll timeline.\n * @data what could not be applied\n */\n timelineOverrideIgnored = 1102,\n\n /**\n * An SVG tag that can execute or load remote content was dropped from the rendered tree.\n * @data the tag name\n */\n blockedTag = 1103,\n\n /**\n * The browser will not animate these attributes, so the document fell back to the frame loop.\n * @data the attribute names\n */\n unsupportedAnimatedAttrs = 1104,\n\n /** A bind-by-id document carries no `animator.bindings`, so nothing is animated. */\n noBindings = 1105,\n\n /**\n * A binding names no element, or names animations that `definitions.animations` does not have.\n * @data the binding\n */\n unresolvedBinding = 1106,\n\n // ── 1200 · the mount: elements the player could not find ─────────────────────────────────\n\n /** `setupAnimationTriggers` was given no root element, so no trigger was wired. */\n triggersNoRoot = 1201,\n\n /**\n * The container selector matched nothing, so there is nothing to render into.\n * @data the selector\n */\n noRootForSelector = 1202,\n\n /** No container was given and the document's `id` matched no element already on the page. */\n noRootElement = 1203,\n\n /**\n * A binding's selector matched no element, so that binding animates nothing.\n * @data the selector\n */\n noElementsForSelector = 1206,\n\n /**\n * An attribute write found no element for this id — the rendered SVG was probably replaced.\n * @data the id or selector\n */\n setAttributeNoElement = 1207,\n\n // ── 1300 · scroll-driven playback ────────────────────────────────────────────────────────\n\n /** `smoothing` needs the player's own driver, so the browser's scroll timeline was not used. */\n scrollSmoothingNeedsOwnDriver = 1301,\n\n /** The browser refused to build a native scroll timeline; the player measures progress itself. */\n scrollNativeUnavailable = 1302,\n\n /**\n * `scroll.subject` is not a valid CSS selector, so the SVG itself is measured instead.\n * @data the subject\n */\n scrollSubjectInvalid = 1303,\n\n /**\n * `scroll.subject` matched no element, so the SVG itself is measured instead.\n * @data the subject\n */\n scrollSubjectNoMatch = 1304,\n\n /** There is no root element to observe, so a scroll-driven animation stays on its first frame. */\n scrollNoRootToObserve = 1305,\n\n /** `animator.trigger` does not apply to a scroll timeline — the scrollbar is the playhead. */\n scrollTriggerIgnored = 1306,\n\n // ── 1400 · playback control ──────────────────────────────────────────────────────────────\n\n /** A playback rate of `0`, or a non-finite one, is rejected everywhere — use `pause()`. */\n rateRejected = 1401,\n\n // ── 1500 · the props a component was given ───────────────────────────────────────────────\n\n /**\n * Two control tiers were set at once. The higher one wins and the lower is ignored — see the\n * control-mode rule.\n * @data which props conflicted, and which won\n */\n controlPropsConflict = 1501,\n\n // ── 1600 · React Native ──────────────────────────────────────────────────────────────────\n\n /**\n * The document could not be compiled into animation tracks, so `fallback` is shown.\n * @data the Error\n */\n rnCompileFailed = 1601,\n\n /**\n * Rendering the compiled document threw, so `fallback` is shown.\n * @data the Error\n */\n rnRenderFailed = 1602,\n\n /**\n * The error boundary caught a render failure below this component.\n * @data the Error · the React component stack\n */\n rnBoundaryCaught = 1603,\n\n /**\n * `react-native-svg` cannot express part of this document, so it was left out or simplified.\n * @data what was left out\n */\n rnUnsupported = 1604,\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n/**\n * PER-INSTANCE CONFIG OVERRIDE.\n *\n * One document, played twice on a page with different timing — without editing the document.\n * The host supplies a partial `animator` config; this merges it over the document's own.\n *\n * Base semantics are JSON Merge Patch (RFC 7386): objects merge per level, primitives and\n * arrays REPLACE, and `null` DELETES. `null` is the right sentinel because it is not a legal\n * value anywhere in the schema, and deleting is the only way to restore a meaningful ABSENCE\n * (`timeline.type` absent = time, `mode` absent = auto, `fillMode` absent = forwards).\n *\n * Six places need more than that — see the RULE comments below.\n *\n * WHY THE WIRE FORM, not the flat runtime view: `nestAnimatorTimeline` is a no-op on anything\n * that already carries `timeline`, and the round-trip is lossy (`iterations:'infinite'` on the\n * scroll branch, `pin:false`, and an empty time block emitting no `timeline` at all). Merging\n * on the nested form is the only place those values mean what they say.\n */\nimport { PX_TIMELINE_SHARED_KEYS, PX_TIME_ONLY_TIMELINE_KEYS } from '../format/PxAnimatorConstants';\nimport type { PxTriggerStart } from '../format/PxAnimatorConstants';\nimport type { PxAnimatedSvgDocument, PxAnimatorConfig } from '../format/PxAnimatorTypes';\n\n/** A deep-partial of the WIRE animator config; `null` at any slot deletes it. @public */\nexport type PxAnimatorConfigPatch = Record<string, any> | null;\n\n/** @public */\nexport interface PxAnimatorConfigMergeResult {\n /** The merged WIRE config, or `undefined` when there is nothing left of it. */\n config: PxAnimatorConfig | undefined;\n /** Human-readable problems, `path: what is wrong`. Empty when the merge was clean. */\n warnings: Array<string>;\n}\n\n/** Keys that live only on the flat RUNTIME view and have no slot on the wire. */\nconst FLAT_ONLY_KEYS = [\n 'timelineSource', 'scroll', 'trigger', 'delay', 'iterations',\n 'direction', 'fill', 'resetOnFinish', 'duration', 'mode', 'frameRate',\n];\n\n/** The lookup tables and the bindings. They are animation CONTENT, not playback, and are never reset. */\nconst CONTENT_KEYS = ['definitions', 'bindings'];\n\nconst isPlainObject = (v: unknown): v is Record<string, any> =>\n !!v && typeof v === 'object' && !Array.isArray(v);\n\n/** Absent `type` means the time-driven timeline, so compare the normalized values (RULE 2). */\nconst timelineTypeOf = (t: unknown): string =>\n (isPlainObject(t) && typeof t.type === 'string') ? t.type : 'time';\n\nconst isScrollish = (type: string): boolean => type === 'scroll' || type === 'view';\n\n/**\n * RFC 7386 merge with no special cases. Used for every sub-object that has no rule of its own\n * (`trigger`, `range`, `definitions.fonts`, …), which is why patching one font leaves its\n * siblings alone. Arrays are values: a patched `bindings` list replaces the document's whole list.\n */\nfunction mergePlain(base: unknown, patch: Record<string, any>): Record<string, any> {\n const out: Record<string, any> = isPlainObject(base) ? { ...base } : {};\n for (const key of Object.keys(patch)) {\n const value = patch[key];\n if (value === null) { delete out[key]; continue; } // RULE 7: null deletes\n if (isPlainObject(value)) { out[key] = mergePlain(out[key], value); continue; }\n out[key] = value; // RULE 6: arrays/primitives replace\n }\n return out;\n}\n\n/**\n * The timeline, which is a discriminated union and therefore cannot be merged blindly.\n *\n * RULE 1 — the patch names a DIFFERENT type: replace the timeline outright, carrying over only\n * the keys both members share. Merging instead would strand `trigger`/`delay` on a\n * scroll timeline, where the format has no slot for them.\n * RULE 2 — same type, or the patch does not mention one: merge. A patch that spells\n * `type: 'time'` against a document with an ABSENT type must count as a match, hence\n * the normalization above.\n * RULE 3 — time-only keys landing on a scroll/view timeline are dropped with a warning, the\n * same way the wire has no slot for them.\n * RULE 4 — `iterations: 'infinite'` cannot map onto a scroll range.\n */\nfunction mergeTimeline(base: unknown, patch: Record<string, any>, warn: (m: string) => void): Record<string, any> {\n const baseType = timelineTypeOf(base);\n const patchNamesType = isPlainObject(patch) && typeof patch.type === 'string';\n const patchType = patchNamesType ? String(patch.type) : baseType;\n\n let merged: Record<string, any>;\n if (patchType !== baseType) {\n // RULE 1\n const carried: Record<string, any> = {};\n if (isPlainObject(base)) {\n for (const k of PX_TIMELINE_SHARED_KEYS) {\n if (base[k] !== undefined) carried[k] = base[k];\n }\n }\n merged = mergePlain(carried, patch);\n } else {\n merged = mergePlain(base, patch); // RULE 2\n }\n\n if (isScrollish(patchType)) {\n for (const k of PX_TIME_ONLY_TIMELINE_KEYS) { // RULE 3\n if (merged[k] === undefined) continue;\n warn('animator.timeline.' + k + \": no slot on a '\" + patchType + \"' timeline — dropped\");\n delete merged[k];\n }\n if (merged.iterations === 'infinite') { // RULE 4\n warn(\"animator.timeline.iterations: 'infinite' cannot map onto a scroll range — dropped\");\n delete merged.iterations;\n }\n }\n return merged;\n}\n\n/** A deep-partial of the WIRE `timeline` block; `null` at any slot deletes it. @public */\nexport type PxTimelinePatch = Record<string, any> | null;\n\n/** The four flat shortcuts every surface offers for the keys people reach for most. @public */\nexport interface PxTimelineShortcuts {\n /** Shortcut for `timeline.duration` — one iteration, ms. Wins over the same key in `timeline`. */\n duration?: number;\n /** Shortcut for `timeline.delay` — the wait before the first iteration, ms. */\n delay?: number;\n /** Shortcut for `timeline.iterations`; `'infinite'` never stops. */\n iterations?: number | 'infinite';\n /**\n * Shortcut for `timeline.trigger.start`. Typed from the WIRE, so it includes\n * `'none'` — \"nothing starts this but a `play()` call\".\n */\n start?: PxTriggerStart;\n}\n\n/**\n * The playback-override props every surface takes — `createAnimator` and the three components:\n * the document's `timeline` as a patch, the reset flag, and the four shortcuts above.\n *\n * ONE definition (review §9): React and React Native extend it, Vue derives its internal shape\n * from it, `createAnimator`'s options extend it.\n * @public\n */\nexport interface PxPlaybackOverride extends PxTimelineShortcuts {\n /**\n * Per-instance override of the document's `animator.timeline` — the same shape as `timeline`\n * in docs/format/README.md, deep-merged over what the document says, so one file can play twice on a page\n * with different timing. `null` at any slot DELETES that key, restoring the default its\n * absence means. Also accepts a JSON STRING, which survives a build that mangles object keys.\n *\n * `timeline` is the whole useful override surface: the rest of the `animator` block is\n * content (`definitions`, `bindings`), a version stamp and a debug handle — none of which\n * a per-instance override should touch. That is why this is not a wrapper object.\n */\n timeline?: PxTimelinePatch | string;\n /**\n * Ignore the document's own timeline and start from the player's DEFAULT timeline, with\n * `timeline` on top. `definitions` and `bindings` are content and are kept either way.\n */\n resetTimeline?: boolean;\n}\n\n/**\n * Folds the four shortcuts into the `timeline` patch, accepts the JSON-STRING form of the patch\n * (immune to property mangling — see docs/library/minification.md), and returns it in the shape\n * `applyAnimatorConfig` takes: an animator-level patch `{ timeline: … }`.\n *\n * A shortcut WINS over the same key inside the object: more specific beats more general, the\n * way an inline style beats a stylesheet. One implementation so every surface agrees.\n * @public\n */\nexport function foldTimelineOverride(\n timeline: PxTimelinePatch | string | undefined,\n shortcuts: PxTimelineShortcuts,\n): PxAnimatorConfigPatch | undefined {\n let base: PxTimelinePatch | undefined;\n if (typeof timeline === 'string') {\n try {\n base = JSON.parse(timeline);\n } catch (e) {\n console.warn('timeline override: not valid JSON — ignored', e);\n base = undefined;\n }\n } else {\n base = timeline ?? undefined;\n }\n\n const { duration, delay, iterations, start } = shortcuts;\n if (duration === undefined && delay === undefined && iterations === undefined && start === undefined) {\n return base === undefined ? undefined : { timeline: base };\n }\n\n const out: Record<string, any> = isPlainObject(base) ? { ...base } : {};\n if (duration !== undefined) out.duration = duration;\n if (delay !== undefined) out.delay = delay;\n if (iterations !== undefined) out.iterations = iterations;\n if (start !== undefined) {\n out.trigger = { ...(out.trigger as Record<string, any> | undefined), start };\n }\n return { timeline: out };\n}\n\n/**\n * Merge a patch over an animator config. PURE — neither argument is mutated, and the result is\n * always a NEW object, which also matters because `flattenAnimatorTimeline` memoises on config\n * identity: mutating in place would hand every later reader the pre-merge view.\n * @public\n */\nexport function mergeAnimatorConfig(\n base: PxAnimatorConfig | undefined,\n patch: PxAnimatorConfigPatch,\n): PxAnimatorConfigMergeResult {\n const warnings: Array<string> = [];\n const warn = (m: string) => warnings.push(m);\n\n if (patch === null) return { config: undefined, warnings };\n if (!isPlainObject(patch) || Object.keys(patch).length === 0) {\n return { config: base, warnings }; // nothing to do: same identity\n }\n\n // RULE 5 — a base in the flat runtime spelling cannot take a wire patch soundly:\n // `flattenAnimatorTimeline` copies the flat keys first and then overwrites them from\n // `timeline`, so a flat `delay` would survive a `timeline.delay: null` deletion.\n const flatInBase = isPlainObject(base) ? FLAT_ONLY_KEYS.filter(k => (base as any)[k] !== undefined) : [];\n if (flatInBase.length) {\n warn('animator: the base carries the flat runtime spelling (' + flatInBase.join(', ') + '); '\n + 'the patch merges the wire spelling only');\n }\n\n const out: Record<string, any> = isPlainObject(base) ? { ...base } : {};\n for (const key of Object.keys(patch)) {\n const value = patch[key];\n if (value === null) { delete out[key]; continue; }\n if (key === 'timeline') {\n out.timeline = isPlainObject(value) ? mergeTimeline(out.timeline, value, warn) : value;\n continue;\n }\n if (isPlainObject(value)) { out[key] = mergePlain(out[key], value); continue; }\n out[key] = value;\n }\n return { config: out as PxAnimatorConfig, warnings };\n}\n\n/**\n * Document level: resolves the two canonical addresses of the animator config and returns a NEW\n * document that shares every untouched subtree by reference.\n *\n * `resetTimeline` starts from the player's own defaults instead of the document's playback\n * settings — \"play this file as if it said nothing about timing\". The lookup tables are kept\n * either way: resetting `definitions`/`bindings` would leave an animation with nothing to\n * animate, which is never what a caller means.\n * @public\n */\nexport function applyAnimatorConfig(\n doc: PxAnimatedSvgDocument,\n patch: PxAnimatorConfigPatch,\n options?: { resetTimeline?: boolean },\n): { doc: PxAnimatedSvgDocument; warnings: Array<string> } {\n const reset = !!options?.resetTimeline;\n if (!doc || (patch === undefined || (!reset && (patch === null || !isPlainObject(patch) || !Object.keys(patch).length)))) {\n return { doc, warnings: [] };\n }\n\n const anyDoc = doc as any;\n const atRoot = isPlainObject(anyDoc.animator);\n const atMeta = !atRoot && isPlainObject(anyDoc.meta?.animator);\n const current: PxAnimatorConfig | undefined = atRoot ? anyDoc.animator\n : atMeta ? anyDoc.meta.animator\n : undefined;\n\n const warnings: Array<string> = [];\n if (atRoot && isPlainObject(anyDoc.meta?.animator)) {\n warnings.push('animator: doc.meta.animator is shadowed by doc.animator and was not patched');\n }\n\n let base = current;\n if (reset) {\n // Keep only the content tables; everything else starts from the player's defaults.\n const kept: Record<string, any> = {};\n for (const k of CONTENT_KEYS) {\n if (isPlainObject(current) && (current as any)[k] !== undefined) kept[k] = (current as any)[k];\n }\n base = kept as PxAnimatorConfig;\n }\n\n const merged = mergeAnimatorConfig(base, patch ?? {});\n warnings.push(...merged.warnings);\n if (merged.config === current) return { doc, warnings };\n\n if (atMeta) {\n return {\n doc: { ...anyDoc, meta: { ...anyDoc.meta, animator: merged.config } } as PxAnimatedSvgDocument,\n warnings,\n };\n }\n return { doc: { ...anyDoc, animator: merged.config } as PxAnimatedSvgDocument, warnings };\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\nimport { PX_TRIGGER_DEFAULTS, PxOffScreenAction, resolveTrigger } from '@pixodesk/svg-animator-core';\n\n/**\n * THE VISIBILITY GATE — permission to run, separate from whatever starts the animation.\n *\n * `trigger.start` says what STARTS an animation; this says whether it may RUN. The two are\n * independent, so \"start on click, but pause while scrolled away\" is sayable, and the common\n * document says neither and gets both defaults: start on load, pause off screen.\n *\n * Three rules decide everything here:\n * - OPEN at `visibilityThreshold`, CLOSE only at zero visibility. One value, two edges, so a\n * graphic resting on the boundary cannot flap between playing and paused.\n * - A pending open waits `visibilityDebounce` ms and is cancelled if visibility falls back below\n * the threshold, so scrolling straight past a graphic starts nothing.\n * - A hidden TAB is off screen. `IntersectionObserver` never fires for a hidden tab, so\n * `visibilitychange` feeds the same state.\n *\n * FAILS OPEN. No `IntersectionObserver` (jsdom, SSR, an old engine) means the animation plays as\n * if fully visible. A page that cannot measure is better off animating than frozen, and failing\n * closed would silently stop every animation in environments that never had the API.\n */\n\n/** The resolved trigger, as this module reads it — the shape `resolveTrigger` returns, named\n * here rather than exported from core so the public surface gains nothing. @internal */\nexport type PxGateTrigger = ReturnType<typeof resolveTrigger>;\n\n/** What the gate drives. The animator API, narrowed to what is actually needed. @internal */\nexport interface PxGateHost {\n isPlaying(): boolean;\n play(): void;\n pause(): void;\n cancel(): void;\n}\n\n/** Permission to run, wired by `setupAnimationTriggers` for every document. @internal */\nexport interface PxVisibilityGate {\n /** A trigger fired. `immediate` skips the gate for a direct interaction (a click or a hover\n * happens on something the reader can already see, and must never wait out a timer). */\n requestStart(immediate: boolean): void;\n /** Detaches the observer, the listener and any pending timer. */\n dispose(): void;\n}\n\n/** The gate's own half of `PX_TRIGGER_DEFAULTS`, for a caller that has props rather than a\n * document (the CSS-only React and Vue wrappers). @internal */\nexport const PLAY_WHEN_VISIBLE_DEFAULTS = {\n offScreen: PX_TRIGGER_DEFAULTS.offScreen,\n visibilityThreshold: PX_TRIGGER_DEFAULTS.visibilityThreshold,\n visibilityDebounce: PX_TRIGGER_DEFAULTS.visibilityDebounce,\n} as const;\n\n/** Granular steps, so the callback fires often enough to notice the threshold being crossed.\n * Registering only the raw threshold would mean no callback at all for some geometries. */\nconst THRESHOLD_STEPS: Array<number> = Array.from({ length: 21 }, (_, i) => i / 20);\n\n/**\n * Visible share of the graphic, normalized for a target TALLER than the viewport.\n *\n * `intersectionRatio` is measured against the TARGET's own size, so a target twice the height of\n * the viewport can never exceed 0.5 and a 0.5 threshold would be unsatisfiable — the animation\n * would never play at all. Dividing by what could possibly be visible removes that cap.\n */\nfunction effectiveRatio(entry: IntersectionObserverEntry): number {\n const target = entry.boundingClientRect;\n const visible = entry.intersectionRect;\n // Simplified entries (tests, older engines) may omit the rects — fall back to the browser's\n // own ratio rather than inventing one.\n if (!target?.height || !visible) return entry.intersectionRatio;\n // Use the SMALLER of `rootBounds` and the live viewport. `rootBounds` can be null (implicit\n // root in some embeddings) and can also report a box LARGER than the actual viewport, which\n // would reinstate the very cap this normalization exists to remove. `intersectionRect` is\n // already clipped to the real viewport, so the denominator must be too.\n const live = typeof window !== 'undefined' && window.innerHeight ? window.innerHeight : Infinity;\n const declared = entry.rootBounds?.height ?? Infinity;\n const viewport = Math.min(live, declared);\n const denom = Math.min(target.height, Number.isFinite(viewport) ? viewport : target.height);\n return denom > 0 ? visible.height / denom : entry.intersectionRatio;\n}\n\n/** A gate that is always open and observes nothing — `offScreen: 'continue'`, or no way to measure. */\nfunction openGate(host: PxGateHost): PxVisibilityGate {\n return {\n requestStart: () => host.play(),\n dispose: () => { /* nothing attached */ },\n };\n}\n\n/** Whether visibility governs this document at all. `continue` means \"run wherever it is\",\n * which also means it must not WAIT to be seen before starting (review: plan §4 example G). */\nfunction isGated(offScreen: PxGateTrigger['offScreen']): boolean {\n return offScreen !== PxOffScreenAction.continue;\n}\n\n/** Builds the gate for one document. Returns an always-open one when visibility does not govern\n * it, or when nothing can measure. @internal */\nexport function createVisibilityGate(root: Element, trigger: PxGateTrigger, host: PxGateHost): PxVisibilityGate {\n if (!isGated(trigger.offScreen)) return openGate(host);\n if (typeof IntersectionObserver === 'undefined') return openGate(host);\n\n const threshold = trigger.visibilityThreshold;\n const debounceMs = trigger.visibilityDebounce;\n\n /** Enough of it is on screen AND the dwell has elapsed. `undefined` until the first\n * measurement arrives, so that measurement always counts as a transition — an animation the\n * HOST already started through the API must be paused by the first report of \"off screen\",\n * not silently left running because the gate had never been open. */\n let isOpen: boolean | undefined = undefined;\n /** A trigger fired while the gate was shut; it is owed a play once the gate opens. */\n let startPending = false;\n /** The gate paused (or reset) a running animation, so it owes it a play. */\n let pausedByGate = false;\n /** The most recent measurement, kept so `visibilitychange` can re-decide without one. */\n let lastRatio = 0;\n let openTimer: ReturnType<typeof setTimeout> | undefined;\n\n const cancelPendingOpen = (): void => {\n if (openTimer !== undefined) {\n clearTimeout(openTimer);\n openTimer = undefined;\n }\n };\n\n const open = (): void => {\n openTimer = undefined;\n if (isOpen === true) return;\n isOpen = true;\n // Whichever of the two owes a play, exactly one play happens.\n if (startPending || pausedByGate) {\n startPending = false;\n pausedByGate = false;\n host.play();\n }\n };\n\n const close = (): void => {\n cancelPendingOpen();\n if (isOpen === false) return;\n isOpen = false;\n // Only act on something that is actually running — including an animation the HOST\n // started through the API, which is why this asks rather than tracking its own flag.\n if (!host.isPlaying()) return;\n if (trigger.offScreen === PxOffScreenAction.reset) {\n host.cancel(); // progress back to 0; the next open replays from the start\n } else {\n host.pause();\n }\n pausedByGate = true;\n };\n\n /** One measurement in, one decision out. The threshold governs OPENING (including its dwell);\n * only zero visibility CLOSES. Between the two nothing changes. */\n const apply = (ratio: number): void => {\n lastRatio = ratio;\n if (typeof document !== 'undefined' && document.visibilityState === 'hidden') {\n close();\n return;\n }\n if (ratio >= threshold) {\n if (isOpen === true || openTimer !== undefined) return;\n if (debounceMs > 0) openTimer = setTimeout(open, debounceMs);\n else open();\n return;\n }\n if (ratio <= 0) {\n close();\n return;\n }\n // The middle band: still visible, but no longer visible ENOUGH. An open gate stays open\n // (hysteresis); a pending open is abandoned, since the dwell is about staying visible.\n if (isOpen !== true) cancelPendingOpen();\n };\n\n const observer = new IntersectionObserver(entries => {\n const last = entries[entries.length - 1];\n if (last) apply(last.isIntersecting ? effectiveRatio(last) : 0);\n }, { threshold: THRESHOLD_STEPS });\n observer.observe(root);\n\n const onVisibilityChange = (): void => { apply(lastRatio); };\n const hasDocument = typeof document !== 'undefined';\n if (hasDocument) document.addEventListener('visibilitychange', onVisibilityChange);\n\n return {\n requestStart: (immediate: boolean) => {\n // A click or a hover is aimed at something the reader can see; it never waits.\n if (immediate || isOpen === true) {\n cancelPendingOpen();\n isOpen = true;\n startPending = false;\n pausedByGate = false;\n host.play();\n return;\n }\n startPending = true;\n },\n dispose: () => {\n cancelPendingOpen();\n observer.disconnect();\n if (hasDocument) document.removeEventListener('visibilitychange', onVisibilityChange);\n },\n };\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n/**\n * Wire keys shared by every entry point.\n *\n * These live here rather than in `PxAnimator.ts` on purpose: that module used to end with a\n * top-level `if (typeof window !== 'undefined')` block publishing `createAnimator` /\n * `loadTagAnimators` as globals. A module-level side effect cannot be tree-shaken, so\n * importing ANY symbol from `PxAnimator.ts` pulled the entire full player in with it —\n * which silently made the pre-rendered builds the same size as the full one until this\n * constant was moved out. See dev-docs/plans/prerendered-player-builds.md.\n *\n * That block is gone (API review §4) and the package now declares `\"sideEffects\": false`, but\n * keeping these here costs nothing and removes the trap for good.\n */\n\n/**\n * Key under which `createAnimator` options carry the inline animation document. The editor\n * writes it into every exported SVG+JS — `createAnimator({\"doc\": …})` — so it is part of the\n * export format, which is why it is a named constant and not a literal.\n * @internal\n */\nexport const PX_ANIMATOR_DOC_KEY = 'doc';\n\n/** Key under which `createAnimator` options carry the per-instance `timeline` override. */\nexport const PX_ANIMATOR_TIMELINE_KEY = 'timeline';\n\n/** Key that makes the override start from the player's default timeline instead of the document's. */\nexport const PX_ANIMATOR_RESET_KEY = 'resetTimeline';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CO,IAAM,uBAAuB;AAkEpC,SAAS,QAAQ,MAA6B;AAC1C,MAAI,CAAC,KAAK,OAAQ,QAAO;AACzB,MAAI,SAAS;AACb,aAAW,OAAO,MAAM;AACpB,QAAI,IAAI,WAAW,GAAG,EAAG,WAAU;QAC9B,YAAW,SAAS,MAAM,MAAM;EACzC;AACA,SAAO;AACX;AAQA,IAAe,OAAf,MAA8F;EAO1F,aAAa,KAAuB;AAAE,WAAO,KAAK,QAAQ,GAAG;EAAG;EAEhE,WAA0C;AAAE,WAAO,IAAI,SAAS,IAAI;EAAG;AAC3E;AAaA,IAAM,WAAN,cAA0B,KAA0B;EAEhD,YAA6B,OAAyB;AAAE,UAAM;AAAjC,SAAA,QAAA;AAD7B,SAAS,WAAW;EAC6C;EAEjE,SAAS,KAA6B;AAClC,QAAI,QAAQ,UAAa,QAAQ,KAAM,QAAO;AAC9C,WAAO,KAAK,MAAM,aAAa,GAAG,IAAI,KAAK,MAAM,SAAS,GAAG,IAAI;EACrE;EAEA,QAAQ,KAAc,KAA2B,MAA+B;AAC5E,QAAI,QAAQ,UAAa,QAAQ,KAAM,QAAO;AAC9C,WAAO,KAAK,MAAM,QAAQ,KAAK,KAAK,IAAI;EAC5C;EAES,aAAa,KAAuB;AACzC,WAAO,QAAQ,UAAa,QAAQ,QAAQ,KAAK,MAAM,aAAa,GAAG;EAC3E;AACJ;AAQA,IAAM,MAAN,cAAkB,KAAa;EAC3B,YAAqB,WAAmB,IAAI;AAAE,UAAM;AAA/B,SAAA,WAAA;EAAkC;EACvD,SAAS,KAAsB;AAAE,WAAO,OAAO,QAAQ,WAAW,MAAM,KAAK;EAAU;EACvF,QAAQ,KAAc,KAA2B,MAA+B;AAC5E,QAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,WAAA,OAAA,SAAA,IAAK,OAAO,KAAK,QAAQ,QAAA,OAAA,OAAQ,CAAC,CAAC,IAAI,4BAA4B,OAAO,GAAA;AAC1E,WAAO;EACX;AACJ;AAGA,IAAM,MAAN,cAAkB,KAAa;EAC3B,YAAqB,WAAmB,GAAG;AAAE,UAAM;AAA9B,SAAA,WAAA;EAAiC;EACtD,SAAS,KAAsB;AAC3B,WAAO,OAAO,QAAQ,YAAY,SAAS,GAAG,IAAI,MAAM,KAAK;EACjE;EACA,QAAQ,KAAc,KAA2B,MAA+B;AAC5E,QAAI,OAAO,QAAQ,YAAY,SAAS,GAAG,EAAG,QAAO;AACrD,WAAA,OAAA,SAAA,IAAK,OAAO,KAAK,QAAQ,QAAA,OAAA,OAAQ,CAAC,CAAC,IAAI,mCAAmC,KAAK,UAAU,GAAG,CAAA;AAC5F,WAAO;EACX;AACJ;AAGA,IAAM,OAAN,cAAmB,KAAc;EAC7B,YAAqB,WAAoB,OAAO;AAAE,UAAM;AAAnC,SAAA,WAAA;EAAsC;EAC3D,SAAS,KAAuB;AAAE,WAAO,OAAO,QAAQ,YAAY,MAAM,KAAK;EAAU;EACzF,QAAQ,KAAc,KAA2B,MAA+B;AAC5E,QAAI,OAAO,QAAQ,UAAW,QAAO;AACrC,WAAA,OAAA,SAAA,IAAK,OAAO,KAAK,QAAQ,QAAA,OAAA,OAAQ,CAAC,CAAC,IAAI,6BAA6B,OAAO,GAAA;AAC3E,WAAO;EACX;AACJ;AAQA,IAAM,UAAN,cAA2D,KAAQ;EAE/D,YAA6B,OAAU;AAAE,UAAM;AAAlB,SAAA,QAAA;AAAqB,SAAK,WAAW;EAAO;EACzE,SAAS,KAAiB;AAAE,WAAO,QAAQ,KAAK,QAAQ,KAAK,QAAQ,KAAK;EAAU;EACpF,QAAQ,KAAc,KAA2B,MAA+B;AAC5E,QAAI,QAAQ,KAAK,MAAO,QAAO;AAC/B,WAAA,OAAA,SAAA,IAAK,OAAO,KAAK,QAAQ,QAAA,OAAA,OAAQ,CAAC,CAAC,IAAI,gBAAgB,KAAK,UAAU,KAAK,KAAK,IAAI,WAAW,KAAK,UAAU,GAAG,CAAA;AACjH,WAAO;EACX;AACJ;AAQA,IAAM,OAAN,cAA8C,KAAQ;EAElD,YAA6B,QAAsB,YAAgB;AAC/D,UAAM;AADmB,SAAA,SAAA;AAEzB,SAAK,WAAW,cAAA,OAAA,aAAc,OAAO,CAAC;EAC1C;EACA,SAAS,KAAiB;AAAE,WAAO,KAAK,OAAO,SAAS,GAAQ,IAAK,MAAY,KAAK;EAAU;EAChG,QAAQ,KAAc,KAA2B,MAA+B;AAC5E,QAAI,KAAK,OAAO,SAAS,GAAQ,EAAG,QAAO;AAC3C,WAAA,OAAA,SAAA,IAAK,OAAO,KAAK,QAAQ,QAAA,OAAA,OAAQ,CAAC,CAAC,IAAI,uBAAuB,KAAK,OAAO,IAAI,CAAA,MAAK,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,WAAW,KAAK,UAAU,GAAG,CAAA;AACjJ,WAAO;EACX;AACJ;AAUA,IAAM,2BAA2B;AAGjC,IAAM,QAAN,cAAuB,KAAQ;EAI3B,YAA6B,SAAqC,YAAgB;AAC9E,UAAM;AADmB,SAAA,UAAA;AAF7B,SAAS,QAAQ;AAIb,SAAK,WAAW,cAAA,OAAA,aAAc,QAAQ,CAAC,EAAE;EAC7C;EACA,SAAS,KAAiB;AACtB,eAAW,KAAK,KAAK,SAAS;AAC1B,UAAI,EAAE,QAAQ,GAAG,EAAG,QAAO,EAAE,SAAS,GAAG;IAC7C;AACA,WAAO,KAAK;EAChB;EACA,QAAQ,KAAc,KAA2B,MAA+B;AAhRpF,QAAAA;AAsRQ,UAAM,QAAyC,OAAO,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAC,GAAG,QAAQ,IAAI,OAAO;AACrG,QAAI,KAAK,QAAQ,KAAK,CAAA,MAAK,EAAE,QAAQ,KAAK,OAAO,OAAO,CAAC,GAAG,IAAI,IAAI,MAAS,CAAC,EAAG,QAAO;AACxF,QAAI,CAAC,IAAK,QAAO;AAEjB,UAAM,OAAO,QAAQ,QAAA,OAAA,OAAQ,CAAC,CAAC;AAC/B,QAAI,OAAO,KAAK,OAAO,2CAA0CA,MAAA,KAAK,UAAU,GAAG,MAAlB,OAAAA,MAAuB,IAAI,MAAM,GAAG,GAAG,CAAC;AAWzG,QAAI;AACJ,QAAI,YAAY;AAChB,UAAM,mBAAkC,CAAC;AAEzC,eAAW,UAAU,KAAK,SAAS;AAC/B,YAAM,OAA4B,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAC,GAAG,QAAQ,IAAI,OAAO;AACjF,aAAO,QAAQ,KAAK,MAAM,OAAO,CAAC,GAAG,IAAI,IAAI,MAAS;AACtD,UAAI,CAAC,KAAK,OAAO,OAAQ;AAIzB,YAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,OAAO,IAAI,CAAA,MAAK,EAAE,MAAM,GAAG,EAAE,QAAQ,GAAG,CAAC,EAAE,MAAM,CAAC;AACjF,UAAI,QAAQ,aAAc,UAAU,aAAa,QAAQ,KAAK,OAAO,SAAS,KAAK,QAAS;AACxF,oBAAY;AACZ,eAAO,KAAK;MAChB;AAGA,UAAI,SAAS,KAAK,QAAQ;AACtB,mBAAW,KAAK,KAAK,QAAQ;AACzB,gBAAM,IAAI,yBAAyB,KAAK,CAAC;AACzC,cAAI,KAAK,CAAC,iBAAiB,SAAS,EAAE,CAAC,CAAC,EAAG,kBAAiB,KAAK,EAAE,CAAC,CAAC;QACzE;MACJ;IACJ;AAEA,QAAI,QAAQ,YAAY,KAAK,QAAQ;AAEjC,iBAAW,KAAK,KAAK,MAAM,GAAG,wBAAwB,GAAG;AACrD,YAAI,CAAC,IAAI,OAAO,SAAS,CAAC,EAAG,KAAI,OAAO,KAAK,CAAC;MAClD;IACJ,WAAW,iBAAiB,QAAQ;AAEhC,UAAI,OAAO,KAAK,OAAO,gBAAgB,iBAAiB,KAAK,KAAK,CAAC;IACvE;AACA,WAAO;EACX;EACS,aAAa,KAAuB;AAAE,WAAO,KAAK,QAAQ,KAAK,CAAA,MAAK,EAAE,aAAa,GAAG,CAAC;EAAG;AACvG;AAgCA,IAAM,qBAAN,cAAoC,KAAQ;EASxC,YACqB,MACA,UACjB,YACF;AAzXN,QAAAA;AA0XQ,UAAM;AAJW,SAAA,OAAA;AACA,SAAA,WAAA;AATrB,SAAS,QAAQ;AAab,SAAK,WAAW,cAAA,OAAA,aAAc,SAAS,CAAC,EAAE;AAC1C,SAAK,OAAO,oBAAI,IAAI;AACpB,eAAW,KAAK,UAAU;AACtB,YAAM,YAAY,EAAE,OAAO,IAAI;AAC/B,UAAI,CAAC,UAAW;AAGhB,YAAM,WAAUA,MAAA,UAAU,UAAV,OAAAA,MAAmB;AACnC,WAAK,KAAK,IAAI,QAAQ,UAAU,CAAC;AACjC,UAAI,UAAU,MAAO,MAAK,gBAAgB;IAC9C;EACJ;EAEQ,YAAY,KAAuC;AACvD,QAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,EAAG,QAAO;AAC1E,UAAM,MAAO,IAAgC,KAAK,IAAI;AACtD,QAAI,QAAQ,UAAa,QAAQ,KAAM,QAAO,KAAK;AACnD,WAAO,KAAK,KAAK,IAAI,GAAgC;EACzD;EAEA,SAAS,KAAiB;AA/Y9B,QAAAA;AAgZQ,aAAQA,MAAA,KAAK,YAAY,GAAG,MAApB,OAAAA,MAAyB,KAAK,SAAS,CAAC,GAAG,SAAS,GAAG;EACnE;EAEA,QAAQ,KAAc,KAA2B,MAA+B;AAC5E,UAAM,SAAS,KAAK,YAAY,GAAG;AACnC,QAAI,CAAC,QAAQ;AACT,YAAM,MAAO,QAAQ,QAAQ,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG,IACnE,IAAgC,KAAK,IAAI,IAAI;AACpD,aAAA,OAAA,SAAA,IAAK,OAAO,KAAK,QAAQ,QAAA,OAAA,OAAQ,CAAC,CAAC,IAAI,6CACjC,KAAK,OAAO,MAAM,KAAK,UAAU,GAAG,CAAA;AAC1C,aAAO;IACX;AACA,WAAO,OAAO,QAAQ,KAAK,KAAK,IAAI;EACxC;EAES,aAAa,KAAuB;AACzC,QAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,EAAG,QAAO;AAC1E,UAAM,SAAS,KAAK,YAAY,GAAG;AACnC,WAAO,SAAS,OAAO,aAAa,GAAG,IAAI,KAAK,SAAS,CAAC,EAAE,aAAa,GAAG;EAChF;AACJ;AAoBA,IAAM,MAAN,cAAsC,KAAoB;EAGtD,YAAqB,QAAW;AAC5B,UAAM;AADW,SAAA,SAAA;AAEjB,UAAM,IAAS,CAAC;AAChB,eAAW,OAAO,OAAO,KAAK,MAAM,EAAG,GAAE,GAAG,IAAI,OAAO,GAAG,EAAE;AAC5D,SAAK,WAAW;EACpB;EAEA,SAAS,KAA6B;AAClC,UAAM,MAAO,OAAO,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG,IAAK,MAAa,CAAC;AACpF,UAAM,MAAW,CAAC;AAClB,eAAW,OAAO,OAAO,KAAK,KAAK,MAAM,GAAG;AACxC,YAAM,IAAI,KAAK,OAAO,GAAG,EAAE,SAAS,IAAI,GAAG,CAAC;AAO5C,UAAI,MAAM,OAAW,KAAI,GAAG,IAAI;IACpC;AACA,WAAO;EACX;EAEA,QAAQ,KAAc,KAA2B,MAA+B;AAC5E,QAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,GAAG;AACvD,aAAA,OAAA,SAAA,IAAK,OAAO,KAAK,QAAQ,QAAA,OAAA,OAAQ,CAAC,CAAC,IAAI,6BAA6B,MAAM,QAAQ,GAAG,IAAI,UAAU,OAAO,IAAA;AAC1G,aAAO;IACX;AACA,UAAM,MAAM;AACZ,UAAM,IAAI,QAAA,OAAA,OAAQ,CAAC;AACnB,QAAI,KAAK;AACT,eAAW,OAAO,OAAO,KAAK,KAAK,MAAM,GAAG;AACxC,QAAE,KAAK,GAAG;AACV,UAAI,CAAC,KAAK,OAAO,GAAG,EAAE,QAAQ,IAAI,GAAG,GAAG,KAAK,CAAC,EAAG,MAAK;AACtD,QAAE,IAAI;IACV;AAGA,QAAI,OAAA,OAAA,SAAA,IAAK,QAAQ;AACb,iBAAW,OAAO,OAAO,KAAK,GAAG,GAAG;AAChC,YAAI,OAAO,KAAK,OAAQ;AAMxB,YAAI,IAAI,GAAG,MAAM,OAAW;AAC5B,UAAE,KAAK,GAAG;AACV,YAAI,OAAO,KAAK,QAAQ,CAAC,IAAI,OAAO,oBAAoB;AACxD,UAAE,IAAI;AACN,aAAK;MACT;IACJ;AACA,WAAO;EACX;EAES,aAAa,KAAuB;AACzC,WAAO,CAAC,CAAC,OAAO,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG;EACjE;AACJ;AAwBA,IAAM,UAAN,cAAmD,KAA2B;EAG1E,YAAqB,QAA4B,aAA2B;AACxE,UAAM;AADW,SAAA,SAAA;AAA4B,SAAA,cAAA;AAE7C,UAAM,IAAS,CAAC;AAChB,eAAW,OAAO,OAAO,KAAK,MAAM,EAAG,GAAE,GAAG,IAAI,OAAO,GAAG,EAAE;AAC5D,SAAK,WAAW;EACpB;EAEA,SAAS,KAAoC;AACzC,UAAM,MAAgC,OAAO,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG,IACpF,MACA,CAAC;AACP,UAAM,MAA+BC,gBAAA,CAAA,GAAK,GAAA;AAC1C,eAAW,OAAO,OAAO,KAAK,KAAK,MAAM,GAAG;AACxC,YAAM,IAAI,KAAK,OAAO,GAAG,EAAE,SAAS,IAAI,GAAG,CAAC;AAC5C,UAAI,MAAM,OAAW,KAAI,GAAG,IAAI;IACpC;AACA,QAAI,KAAK,aAAa;AAClB,iBAAW,OAAO,OAAO,KAAK,GAAG,GAAG;AAChC,YAAI,EAAE,OAAO,KAAK,QAAS,KAAI,GAAG,IAAI,KAAK,YAAY,SAAS,IAAI,GAAG,CAAC;MAC5E;IACJ;AACA,WAAO;EACX;EAEA,QAAQ,KAAc,KAA2B,MAA+B;AAC5E,QAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,GAAG;AACvD,aAAA,OAAA,SAAA,IAAK,OAAO,KAAK,QAAQ,QAAA,OAAA,OAAQ,CAAC,CAAC,IAAI,6BAA6B,MAAM,QAAQ,GAAG,IAAI,UAAU,OAAO,IAAA;AAC1G,aAAO;IACX;AACA,UAAM,MAAM;AACZ,UAAM,IAAI,QAAA,OAAA,OAAQ,CAAC;AACnB,QAAI,KAAK;AACT,eAAW,OAAO,OAAO,KAAK,KAAK,MAAM,GAAG;AACxC,QAAE,KAAK,GAAG;AACV,UAAI,CAAC,KAAK,OAAO,GAAG,EAAE,QAAQ,IAAI,GAAG,GAAG,KAAK,CAAC,EAAG,MAAK;AACtD,QAAE,IAAI;IACV;AACA,QAAI,KAAK,aAAa;AAClB,iBAAW,OAAO,OAAO,KAAK,GAAG,GAAG;AAChC,YAAI,OAAO,KAAK,OAAQ;AACxB,UAAE,KAAK,GAAG;AACV,YAAI,CAAC,KAAK,YAAY,QAAQ,IAAI,GAAG,GAAG,KAAK,CAAC,EAAG,MAAK;AACtD,UAAE,IAAI;MACV;IACJ;AACA,WAAO;EACX;EAES,aAAa,KAAuB;AACzC,WAAO,CAAC,CAAC,OAAO,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG;EACjE;AACJ;AASA,IAAM,MAAN,cAAqB,KAAe;EAEhC,YAA6B,MAAmB;AAAE,UAAM;AAA3B,SAAA,OAAA;AAD7B,SAAS,WAAqB,CAAC;EAC4B;EAE3D,SAAS,KAAwB;AAC7B,QAAI,CAAC,MAAM,QAAQ,GAAG,EAAG,QAAO,CAAC;AACjC,UAAM,MAAgB,CAAC;AACvB,eAAW,MAAM,KAAK;AAClB,UAAI,KAAK,KAAK,aAAa,EAAE,EAAG,KAAI,KAAK,KAAK,KAAK,SAAS,EAAE,CAAC;IACnE;AACA,WAAO;EACX;EAEA,QAAQ,KAAc,KAA2B,MAA+B;AAC5E,QAAI,CAAC,MAAM,QAAQ,GAAG,GAAG;AACrB,aAAA,OAAA,SAAA,IAAK,OAAO,KAAK,QAAQ,QAAA,OAAA,OAAQ,CAAC,CAAC,IAAI,2BAA2B,OAAO,GAAA;AACzE,aAAO;IACX;AACA,UAAM,IAAI,QAAA,OAAA,OAAQ,CAAC;AACnB,QAAI,KAAK;AACT,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACjC,QAAE,KAAK,MAAM,IAAI,GAAG;AACpB,UAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,GAAG,KAAK,CAAC,EAAG,MAAK;AAC7C,QAAE,IAAI;IACV;AACA,WAAO;EACX;EAES,aAAa,KAAuB;AAAE,WAAO,MAAM,QAAQ,GAAG;EAAG;AAC9E;AAQA,IAAM,MAAN,cAAqB,KAAwB;EAIzC,YAA6B,OAAoB;AAAE,UAAM;AAA5B,SAAA,QAAA;AAF7B,SAAS,QAAQ;AACjB,SAAS,WAA8B,CAAC;EACoB;EAE5D,SAAS,KAAiC;AACtC,QAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,EAAG,QAAO,CAAC;AACnE,UAAM,MAAyB,CAAC;AAChC,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAG,GAAG;AACtC,UAAI,KAAK,MAAM,aAAa,CAAC,EAAG,KAAI,CAAC,IAAI,KAAK,MAAM,SAAS,CAAC;IAClE;AACA,WAAO;EACX;EAEA,QAAQ,KAAc,KAA2B,MAA+B;AAC5E,QAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,GAAG;AACvD,aAAA,OAAA,SAAA,IAAK,OAAO,KAAK,QAAQ,QAAA,OAAA,OAAQ,CAAC,CAAC,IAAI,oCAAoC,MAAM,QAAQ,GAAG,IAAI,UAAU,OAAO,IAAA;AACjH,aAAO;IACX;AACA,UAAM,IAAI,QAAA,OAAA,OAAQ,CAAC;AACnB,QAAI,KAAK;AACT,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAa,GAAG;AAChD,QAAE,KAAK,CAAC;AACR,UAAI,CAAC,KAAK,MAAM,QAAQ,GAAG,KAAK,CAAC,EAAG,MAAK;AACzC,QAAE,IAAI;IACV;AACA,WAAO;EACX;EAES,aAAa,KAAuB;AACzC,WAAO,CAAC,CAAC,OAAO,OAAO,QAAQ,YAAY,CAAC,MAAM,QAAQ,GAAG;EACjE;AACJ;AAQA,IAAM,MAAN,cAAkB,KAAU;EAA5B,cAAA;AAAA,UAAA,GAAA,SAAA;AACI,SAAS,WAAgB;EAAA;EACzB,SAAS,KAAmB;AAAE,WAAO;EAAK;EAC1C,QAAQ,MAAe,MAA4B,OAAgC;AAAE,WAAO;EAAM;EACzF,aAAa,MAAwB;AAAE,WAAO;EAAM;AACjE;AAWA,IAAM,UAAN,cAAsB,KAAU;EAAhC,cAAA;AAAA,UAAA,GAAA,SAAA;AACI,SAAS,WAAgB;EAAA;EACzB,SAAS,KAAmB;AAAE,WAAO;EAAK;EAC1C,QAAQ,KAAc,KAA2B,MAA+B;AAC5E,QAAI,QAAQ,OAAW,QAAO;AAC9B,WAAA,OAAA,SAAA,IAAK,OAAO,KAAK,QAAQ,QAAA,OAAA,OAAQ,CAAC,CAAC,IAAI,6BAAA;AACvC,WAAO;EACX;EACS,aAAa,KAAuB;AAAE,WAAO,QAAQ;EAAW;AAC7E;AAQA,IAAM,OAAN,cAAsB,KAAQ;EAE1B,YAA6B,IAAgC,UAAa;AAAE,UAAM;AAArD,SAAA,KAAA;AAAgC,SAAA,WAAA;AAD7D,SAAQ,WAA+B;EAC8C;EAErF,IAAY,SAAsB;AAhsBtC,QAAAD;AAisBQ,YAAOA,MAAA,KAAK,aAAL,OAAAA,MAAkB,KAAK,WAAW,KAAK,GAAG;EACrD;EAEA,SAAS,KAAiB;AAAE,WAAO,KAAK,OAAO,SAAS,GAAG;EAAG;EAC9D,QAAQ,KAAc,KAA2B,MAA+B;AAAE,WAAO,KAAK,OAAO,QAAQ,KAAK,KAAK,IAAI;EAAG;EACrH,aAAa,KAAuB;AAAE,WAAO,KAAK,OAAO,aAAa,GAAG;EAAG;AACzF;AAaA,IAAM,QAAN,cAAiE,KAAoB;EAKjF,YAA6B,SAAY;AACrC,UAAM;AADmB,SAAA,UAAA;AAH7B,SAAS,QAAQ;AAKb,SAAK,WAAW,QAAQ,IAAI,CAAA,MAAK,EAAE,QAAQ;EAC/C;EAEA,SAAS,KAA6B;AAClC,QAAI,CAAC,MAAM,QAAQ,GAAG,KAAK,IAAI,WAAW,KAAK,QAAQ,OAAQ,QAAO,KAAK;AAC3E,WAAO,KAAK,QAAQ,IAAI,CAAC,GAAG,MAAM,EAAE,SAAU,IAAkB,CAAC,CAAC,CAAC;EACvE;EAEA,QAAQ,KAAc,KAA2B,MAA+B;AAC5E,QAAI,CAAC,MAAM,QAAQ,GAAG,KAAK,IAAI,WAAW,KAAK,QAAQ,QAAQ;AAC3D,aAAA,OAAA,SAAA,IAAK,OAAO,KAAK,QAAQ,QAAA,OAAA,OAAQ,CAAC,CAAC,IAAI,gCAAgC,KAAK,QAAQ,SAAS,YAAY,MAAM,QAAQ,GAAG,IAAI,WAAY,IAAkB,SAAS,MAAM,OAAO,IAAA;AAClL,aAAO;IACX;AACA,UAAM,IAAI,QAAA,OAAA,OAAQ,CAAC;AACnB,QAAI,KAAK;AACT,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,QAAQ,KAAK;AAC1C,QAAE,KAAK,MAAM,IAAI,GAAG;AACpB,UAAI,CAAE,KAAK,QAA8C,CAAC,EAAE,QAAS,IAAkB,CAAC,GAAG,KAAK,CAAC,EAAG,MAAK;AACzG,QAAE,IAAI;IACV;AACA,WAAO;EACX;;EAGS,aAAa,KAAuB;AACzC,WAAO,MAAM,QAAQ,GAAG,KAAK,IAAI,WAAW,KAAK,QAAQ;EAC7D;AACJ;AAqBO,SAAS,sBAAyB;AACrC,SAAO,CAAwB,WAAiB;AACpD;AAuFO,IAAM,KAAK;;EAEd,QAAS,CAAC,aAAa,OAA6B,IAAI,IAAI,UAAU;;EAGtE,QAAS,CAAC,aAAa,MAA6B,IAAI,IAAI,UAAU;;EAGtE,SAAS,CAAC,aAAa,UAA6B,IAAI,KAAK,UAAU;;EAGvE,SAAS,CAAsC,UAC3C,IAAI,QAAQ,KAAK;;EAGrB,MAAM,CAA4B,QAAsB,eACpD,IAAI,KAAK,QAAQ,UAAU;;;;;EAM/B,OAAO,CACH,SACA,eAEA,IAAI,MAAM,SAAgB,UAAU;;;;;;;EAQxC,oBAAoB,CAGlB,KAAQ,YACN,IAAI,mBAAmB,KAAK,OAAc;;EAG9C,QAAQ,CAAqB,UACzB,IAAI,IAAI,KAAK;;;;;EAMjB,YAAY,CACR,OACA,eAEA,IAAI,QAAQ,OAAO,UAAU;;;;;;;;EASjC,gBAAgB,CACZ,MACA,UAEA,IAAI,IAAIE,gBAAAA,gBAAA,CAAA,GAAK,KAAK,MAAA,GAAW,KAAA,CAAgB;;EAGjD,OAAO,CAAI,SACP,IAAI,IAAI,IAAI;;EAGhB,QAAQ,CAAI,UACR,IAAI,IAAI,KAAK;;EAGjB,KAAK,MAAqB,IAAI,IAAI;;EAGlC,SAAS,MAAqB,IAAI,QAAQ;;EAG1C,OAAO,CAA8C,YACjD,IAAI,MAAM,OAAO;;EAGrB,MAAM,CAAI,IAAuB,eAC7B,IAAI,KAAK,IAAI,UAAU;AAC/B;ACr6BO,IAAM,yBAAyB;ACqCtC,IAAM,aAAa;AAKZ,SAAS,iBAAiB,KAAyC;AACtE,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,IAAI,WAAW,KAAK,IAAI,KAAK,CAAC;AACpC,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,GAAG,GAAG,OAAO,EAAE,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,MAAM,SAAY,IAAI,OAAO,EAAE,CAAC,CAAC,EAAE;AACxF;AAtEA,IAAA;AA8EO,IAAM,mBACT,KAAA,iBAAiB,sBAAsB,MAAvC,OAAA,KAA4C,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE;AC3C5D,IAAM,aAAa;EACtB,UAAW;EACX,WAAW;EACX,MAAW;EACX,MAAW;AACf;AAKO,IAAM,sBAAsB;EAC/B,QAAmB;EACnB,SAAmB;EACnB,WAAmB;EACnB,kBAAmB;AACvB;AAMO,IAAM,wBAAwB;AAG9B,IAAM,oBAAoB;AAO1B,IAAM,iBAAiB;EAC1B,MAAW;EACX,WAAW;EACX,OAAW;EACX,MAAW;AACf;AAMO,IAAM,oBAAoB;EAC7B,OAAU;EACV,UAAU;EACV,OAAU;AACd;AAMO,IAAM,mBAAmB;EAC5B,UAAU;EACV,OAAU;EACV,OAAU;EACV,SAAU;AACd;AAKO,IAAM,iBAAiB;EAC1B,MAAO;EACP,OAAO;AACX;AAOO,IAAM,eAAe;EACxB,MAAQ;EACR,QAAQ;AACZ;AAKO,IAAM,eAAe;EACxB,OAAQ;EACR,QAAQ;EACR,GAAQ;EACR,GAAQ;AACZ;AAKO,IAAM,iBAAiB;EAC1B,SAAS;EACT,MAAS;AACb;AAKO,IAAM,aAAa;EACtB,KAAQ;EACR,QAAQ;EACR,QAAQ;AACZ;AAOO,IAAM,gBAAgB;EACzB,OAAe;EACf,SAAe;EACf,OAAe;EACf,MAAe;EACf,eAAe;EACf,cAAe;AACnB;AAKO,IAAM,kBAAkB;EAC3B,SAAY;EACZ,YAAY;AAChB;AAgBO,IAAM,0BAA0B,CAAC,YAAY,cAAc,UAAU,WAAW;AAIhF,IAAM,6BAA6B,CAAC,WAAW,SAAS,YAAY,WAAW;AAS/E,IAAM,4BAAmD;EAC5D,GAAG;EAAyB,GAAG;EAC/B;EAAQ;EAAiB;EAAkB;AAC/C;AAUO,IAAM,mBAAmB;EAC5B,QAAQ;EACR,IAAQ;AACZ;AAaO,IAAM,0BAA0BC,eAAAC,gBAAA,CAAA,GAChC,gBAAA,GADgC;EAEnC,MAAM;AACV,CAAA;AASO,SAAS,sBAAsB,QAA+D;AACjG,SAAO,WAAW,wBAAwB,KAAK,iBAAiB,KAAK,iBAAiB;AAC1F;AAGO,SAAS,eAAe,QAAsD;AACjF,SAAO,WAAW,wBAAwB;AAC9C;AAMO,SAAS,2BAA2B,QAAsD;AAC7F,SAAO,WAAW,wBAAwB;AAC9C;AAaO,IAAM,sBAAsB;EAC/B,OAAO;EACP,WAAW;EACX,UAAU;EACV,qBAAqB;EACrB,oBAAoB;AACxB;AAyHO,SAAS,eAAe,SAAmD;AAjYlF,MAAAC,KAAA,IAAA,IAAA,IAAA;AAkYI,SAAO;IACH,QAAOA,MAAA,WAAA,OAAA,SAAA,QAAS,UAAT,OAAAA,MAAkB,oBAAoB;IAC7C,YAAW,KAAA,WAAA,OAAA,SAAA,QAAS,cAAT,OAAA,KAAsB,oBAAoB;IACrD,WAAU,KAAA,WAAA,OAAA,SAAA,QAAS,aAAT,OAAA,KAAqB,oBAAoB;IACnD,sBAAqB,KAAA,WAAA,OAAA,SAAA,QAAS,wBAAT,OAAA,KAAgC,oBAAoB;IACzE,qBAAoB,KAAA,WAAA,OAAA,SAAA,QAAS,uBAAT,OAAA,KAA+B,oBAAoB;EAC3E;AACJ;AAWO,IAAM,iBAAiB;;;EAG1B,OAAO;;;EAGP,KAAK;AACT;AAOO,IAAM,kBAAkB;;EAE3B,QAAQ;;EAER,WAAW;AACf;AAKO,IAAM,aAAa;EACtB,WAAW;EACX,OAAW;AACf;AAKO,IAAM,UAAU;EACnB,gBAAmB;EACnB,mBAAmB;AACvB;AAcO,IAAM,iBAAiB;EAC1B,WAAW;;AAEf;AAOO,IAAM,iBAAiB;EAC1B,MAAQ;EACR,QAAQ;AACZ;AAKO,IAAM,iBAAiB;EAC1B,SAAkB;EAClB,kBAAkB;AACtB;AAKO,IAAM,mBAAmB;EAC5B,OAAS;EACT,SAAS;AACb;AAKO,IAAM,oBAAoB;EAC7B,MAAO;EACP,OAAO;AACX;AASO,IAAM,uBAAuB;EAChC,UAAU;EACV,UAAU;AACd;AASO,IAAM,uBAAuB;AAM7B,IAAM,aAAa;AAKnB,IAAM,iBAAiB;AAGvB,IAAM,uBAAuB;AAW7B,IAAM,iBAAiB,oBAAI,IAAI;EAClC;EAAQ;EAAY;EAAY;EAAQ;EAAW;EAAW;AAClE,CAAC;AAcM,IAAM,iBAAiB;EAC1B,WAAW;EACX,QAAQ;EACR,OAAO;EACP,QAAQ;AACZ;AAGO,IAAM,yBAAyB;EAClC,eAAe;EAAW,eAAe;EAAQ,eAAe;EAAO,eAAe;AAC1F;AA8BO,IAAM,yBAAyB;EAClC,KAAS;EACT,SAAS;EACT,QAAS;AACb;AAKO,IAAM,iBAAiB;EAC1B,QAAQ;EACR,QAAQ;AACZ;AASO,SAAS,aAAa,KAAwC;AACjE,MAAI,EACA,OACA,OAAO,QAAQ,YACf,CAAC,MAAM,QAAQ,GAAG,IACnB;AACC,WAAO;EACX;AAMA,SAAO,IAAI,SAAS;AACxB;AAcO,SAAS,kBAAkB,KAA0D;AAjoB5F,MAAAA;AAkoBI,QAAM,OAAM,OAAA,OAAA,SAAA,IAAK,eAAYA,MAAA,OAAA,OAAA,SAAA,IAAK,SAAL,OAAA,SAAAA,IAAW;AACxC,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,WAAW,aAAa,IAAI,GAAa;AAC/C,MAAI,SAAU,QAAO;AAKrB,QAAM,OAAO;AACb,QAAM,QAAQ,0BAA0B,OAAO,CAAA,MAAK,KAAK,CAAC,MAAM,MAAS;AACzE,QAAM,SAAS,MAAM,SAASC,gBAAA,CAAA,GAAK,IAAA,IAAS;AAC5C,aAAW,KAAK,MAAO,QAAQ,OAAmC,CAAC;AAInE,QAAM,OAAO,wBAAwB,MAA0B;AAG/D,eAAa,IAAI,KAAe,IAAI;AACpC,SAAO;AACX;AAGA,IAAM,eAAe,oBAAI,QAAkC;AAiB3D,IAAM,cAAc,oBAAI,QAAkC;AAOnD,SAAS,wBAAwB,KAAyC;AAC7E,QAAM,WAAiB,IAAY;AACnC,MAAI,aAAa,UAAa,aAAa,QAAQ,OAAO,aAAa,SAAU,QAAO;AAExF,QAAM,WAAW,YAAY,IAAI,GAAa;AAC9C,MAAI,SAAU,QAAO;AAErB,QAAwCD,MAAA,KAAhC,EAAA,UAAU,SAxrBtB,IAwrB4CA,KAAT,OAAAE,WAASF,KAAT,CAAvB,UAAA,CAAA;AAIR,MAAI,SAAS,WAAW,OAAW,MAAK,SAAS,SAAS;AAC1D,MAAI,SAAS,cAAc,OAAW,MAAK,YAAY,SAAS;AAEhE,MAAI,SAAS,SAAS,YAAY,SAAS,SAAS,QAAQ;AACxD,SAAK,iBAAiB;AACtB,QAAI,SAAS,aAAa,OAAW,MAAK,WAAW,SAAS;AAC9D,QAAI,SAAS,eAAe,OAAW,MAAK,aAAa,SAAS;AAClE,UAAM,SAAmBC,gBAAA,CAAA,GAAM,KAAK,UAAU,CAAC,CAAA;AAC/C,WAAO,OAAO,SAAS;AACvB,QAAI,SAAS,SAAS,OAAW,QAAO,OAAO,SAAS;AACxD,QAAI,SAAS,WAAW,OAAW,QAAO,SAAS,SAAS;AAC5D,QAAI,SAAS,YAAY,OAAW,QAAO,UAAU,SAAS;AAC9D,QAAI,SAAS,cAAc,OAAW,QAAO,YAAY,SAAS;AAClE,QAAI,SAAS,UAAU,OAAW,QAAO,QAAQ,SAAS;AAC1D,UAAM,MAAM,SAAS;AACrB,QAAI,OAAO,QAAQ,UAAW,QAAO,MAAM;aAClC,OAAO,OAAO,QAAQ,UAAU;AACrC,aAAO,MAAM;AACb,UAAI,IAAI,UAAU,OAAW,QAAO,WAAW,IAAI;AACnD,UAAI,IAAI,WAAW,OAAW,QAAO,YAAY,IAAI;AACrD,UAAI,IAAI,aAAa,OAAW,QAAO,cAAc,IAAI;IAC7D;AACA,SAAK,SAAS;EAClB,OAAO;AACH,QAAI,SAAS,aAAa,OAAW,MAAK,WAAW,SAAS;AAC9D,QAAI,SAAS,YAAY,QAAW;AAChC,YAAmC,KAAA,SAAS,SAApC,EAAA,OAttBpB,IAstB+C,IAAhB,cAAAC,WAAgB,IAAhB,CAAX,QAAA,CAAA;AACR,UAAI,OAAO,KAAK,WAAW,EAAE,OAAQ,MAAK,UAAU;AACpD,UAAI,WAAW,OAAW,MAAK,gBAAgB,WAAW;IAC9D;AACA,QAAI,SAAS,UAAU,OAAW,MAAK,QAAQ,SAAS;AACxD,QAAI,SAAS,eAAe,OAAW,MAAK,aAAa,SAAS;AAClE,QAAI,SAAS,cAAc,OAAW,MAAK,YAAY,SAAS;AAChE,QAAI,SAAS,aAAa,OAAW,MAAK,OAAO,SAAS;EAC9D;AAEA,cAAY,IAAI,KAAe,IAAI;AACnC,SAAO;AACX;AA4EO,SAAS,eAAe,KAAuD;AA9yBtF,MAAAC;AA+yBI,MAAI,CAAC,IAAK,QAAO;AACjB,UAAOA,MAAA,kBAAkB,GAAG,MAArB,OAAA,SAAAA,IAAwB;AACnC;AAGO,SAAS,YAAY,KAAqD;AApzBjF,MAAAA;AAqzBI,MAAI,CAAC,IAAK,QAAO;AACjB,UAAOA,MAAA,kBAAkB,GAAG,MAArB,OAAA,SAAAA,IAAwB;AACnC;ACnxBO,IAAM,sBAAsB,GAAG,MAAM;EACxC,GAAG,OAAO;EACV,GAAG,MAAM,CAAC,GAAG,OAAO,GAAG,GAAG,OAAO,GAAG,GAAG,OAAO,GAAG,GAAG,OAAO,CAAC,CAAU;AAC1E,CAAC;AAgGM,IAAM,wBAAwB,oBAAsC,EAAE,GAAG,MAAM;EAClF,GAAG,OAAO;;EACV,GAAG,OAAO;EACV,GAAG,MAAM,GAAG,OAAO,CAAC;;;;;;;EAOpB,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,EAAE,CAAC;;EAEnC,GAAG,KAA6B,MAAM,GAAG,MAAM,oBAAoB,GAAG,CAAC,CAAC;EACxE,GAAG,KAAuB,MAAM,wBAAwB,CAAC,CAAC;AAC9D,CAAC,CAAC;AAiBK,IAAM,mBAAmB,oBAAiC,EAAE,GAAG,OAAO;EACzE,MAAM,GAAG,OAAO,EAAE,SAAS;EAC3B,OAAO,sBAAsB,SAAS;EACtC,QAAQ,oBAAoB,SAAS;EACrC,YAAY,GAAG,MAAM,CAAC,GAAG,OAAO,GAAG,GAAG,OAAO,CAAC,CAAU,EAAE,SAAS;EACnE,WAAW,GAAG,MAAM,CAAC,GAAG,OAAO,GAAG,GAAG,OAAO,CAAC,CAAU,EAAE,SAAS;;;;AAItE,CAAC,CAAC;AAoCF,IAAM,QAAQ,CAAC,OAAsB;AAG9B,IAAM,eAAe,CAAC,OAA2B;AAtNxD,MAAAC,KAAA;AAsN2D,UAAA,MAAAA,MAAA,MAAM,EAAE,EAAE,SAAV,OAAAA,MAAkB,MAAM,EAAE,EAAE,MAA5B,OAAA,KAAiC;AAAA;AAErF,IAAM,gBAAgB,CAAC,OAAwB;AAxNtD,MAAAA;AAwNyD,UAAAA,MAAA,MAAM,EAAE,EAAE,UAAV,OAAAA,MAAmB,MAAM,EAAE,EAAE;AAAA;AAE/E,IAAM,iBAAiB,CAAC,OAA8C;AA1N7E,MAAAA;AA0NgF,UAAAA,MAAA,MAAM,EAAE,EAAE,WAAV,OAAAA,MAAoB,MAAM,EAAE,EAAE;AAAA;AAEvG,IAAM,oBAAoB,CAAC,OAAoD,MAAM,EAAE,EAAE;AAEzF,IAAM,qBAAqB,CAAC,OAAoD,MAAM,EAAE,EAAE;AAwF1F,IAAM,eAAe,oBAA6B,EAAE,GAAG,OAAO;EACjE,cAAc,GAAG,OAAO,EAAE,SAAS;EACnC,UAAU,GAAG,KAAK,CAAC,eAAe,OAAO,eAAe,GAAG,CAAU,EAAE,SAAS;EAChF,WAAW,GAAG,KAAK,CAAC,gBAAgB,QAAQ,gBAAgB,SAAS,CAAU,EAAE,SAAS;AAC9F,CAAC,CAAC;AAuEK,IAAM,4BAA4B,oBAA0C,EAAE,GAAG,OAAO;EAC3F,OAAO,sBAAsB,SAAS;EACtC,WAAW,GAAG,MAAM,gBAAgB,EAAE,SAAS;EAC/C,MAAM,GAAG,MAAM,CAAC,cAAc,GAAG,QAAQ,CAAC,CAAC,EAAE,SAAS;EACtD,YAAY,GAAG,QAAQ,EAAE,SAAS;EAClC,eAAe,GAAG,KAAK,CAAC,gBAAgB,SAAS,gBAAgB,UAAU,CAAU,EAAE,SAAS;AACpG,CAAC,CAAC;AA8CK,IAAM,yBAAyB,oBAAuC,EAAE,GAAG,OAAO;EACrF,WAAW,GAAG,MAAM,CAAC,GAAG,OAAO,GAAG,GAAG,OAAO,CAAC,CAAU,EAAE,SAAS;EAClE,QAAQ,GAAG,OAAO,EAAE,SAAS;EAC7B,MAAM,GAAG,OAAO,EAAE,SAAS;EAC3B,OAAO,GAAG,MAAM,CAAC,GAAG,OAAO,GAAG,GAAG,OAAO,CAAC,CAAU,EAAE,SAAS;EAC9D,QAAQ,GAAG,MAAM,CAAC,GAAG,OAAO,GAAG,GAAG,OAAO,CAAC,CAAU,EAAE,SAAS;AACnE,CAAC,CAAC;AA4BK,IAAM,yBAAyB,GAAG,MAAM;EAC3C,GAAG,OAAO;EACV;EACA,GAAG,OAAO,EAAE,OAAO,uBAAuB,CAAC;EAC3C;AACJ,CAAC;AAuBM,IAAM,8BAA8B,oBAA4C;EACnF,GAAG,OAAO,yBAAyB;AACvC;AAmCO,IAAM,2BAA2B,oBAAyC,EAAE,GAAG,MAAM;EACxF,GAAG,OAAO;EACV,GAAG,MAAM,GAAG,MAAM,CAAC,GAAG,OAAO,GAAG,2BAA2B,CAAC,CAAC;EAC7D;AACJ,CAAC,CAAC;AAuDK,IAAM,kBAAkB,oBAAgC,EAAE,GAAG,OAAO;EACvE,OAAO,GAAG,KAAK,CAAC,eAAe,MAAM,eAAe,WAAW,eAAe,OAAO,eAAe,IAAI,GAAY,oBAAoB,KAAK,EAAE,SAAS;EACxJ,WAAW,GAAG,KAAK,CAAC,kBAAkB,OAAO,kBAAkB,UAAU,kBAAkB,KAAK,GAAY,oBAAoB,SAAS,EAAE,SAAS;EACpJ,UAAU,GAAG,KAAK,CAAC,iBAAiB,UAAU,iBAAiB,OAAO,iBAAiB,OAAO,iBAAiB,OAAO,GAAY,oBAAoB,QAAQ,EAAE,SAAS;;;;EAIzK,QAAQ,GAAG,KAAK,CAAC,eAAe,MAAM,eAAe,KAAK,CAAU,EAAE,SAAS;EAC/E,qBAAqB,GAAG,OAAO,EAAE,SAAS;EAC1C,oBAAoB,GAAG,OAAO,EAAE,SAAS;AAC7C,CAAC,CAAC;AAyBK,IAAM,gBAAgB,oBAA8B,EAAE,GAAG,OAAO;EACnE,OAAO,GAAG,OAAO;EACjB,UAAU,GAAG,OAAO;AACxB,CAAC,CAAC;AAuBK,IAAM,oBAAoB,oBAAkC,EAAE,GAAG,OAAO;EAC3E,YAAY,GAAG,OAAO;EACtB,WAAW,GAAG,OAAO;EACrB,QAAQ,GAAG,OAAO;EAClB,YAAY,GAAG,OAAO;EACtB,QAAQ,GAAG,OAAO,aAAa;AACnC,CAAC,CAAC;AA0BK,IAAM,sBAAsB,oBAA6B,EAAE,GAAG,OAAO;EACxE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC,GAAG,OAAO,GAAG,GAAG,OAAO,GAAG,GAAG,OAAO,GAAG,GAAG,OAAO,CAAC,CAAU,CAAC,EAAE,SAAS;EACrG,YAAY,GAAG,OAAO,2BAA2B,EAAE,SAAS;EAC5D,OAAO,GAAG,OAAO,iBAAiB,EAAE,SAAS;AACjD,CAAC,CAAC;AA8BK,IAAM,2BAA2B,oBAAyC,EAAE,GAAG,OAAO;EACzF,OAAO,GAAG,KAAK;IAAC,cAAc;IAAO,cAAc;IAAS,cAAc;IAC1D,cAAc;IAAM,cAAc;IAAe,cAAc;EAAY,CAAU,EAAE,SAAS;EAChH,UAAU,GAAG,OAAO,EAAE,SAAS;AACnC,CAAC,CAAC;AAyFK,IAAM,sBAAsB,GAAG,OAAO;EACzC,OAAO,yBAAyB,SAAS;EACzC,KAAK,yBAAyB,SAAS;AAC3C,CAAC;AAGM,IAAM,iBAAiB,oBAA+B,EAAE,GAAG,OAAO;EACrE,MAAM,GAAG,KAAK,CAAC,aAAa,MAAM,aAAa,MAAM,CAAU,EAAE,SAAS;EAC1E,MAAM,GAAG,KAAK,CAAC,aAAa,OAAO,aAAa,QAAQ,aAAa,GAAG,aAAa,CAAC,CAAU,EAAE,SAAS;EAC3G,QAAQ,GAAG,KAAK,CAAC,eAAe,SAAS,eAAe,IAAI,CAAU,EAAE,SAAS;;EAEjF,SAAS,GAAG,OAAO,EAAE,SAAS;EAC9B,WAAW,GAAG,OAAO,EAAE,SAAS;EAChC,KAAK,GAAG,QAAQ,EAAE,SAAS;EAC3B,UAAU,GAAG,KAAK,CAAC,WAAW,KAAK,WAAW,QAAQ,WAAW,MAAM,CAAU,EAAE,SAAS;EAC5F,WAAW,GAAG,OAAO,EAAE,SAAS;EAChC,aAAa,GAAG,OAAO,EAAE,SAAS;EAClC,OAAO,oBAAoB,SAAS;AACxC,CAAC,CAAC;AAkCK,IAAM,sBAAsB,oBAAoC,EAAE,GAAG,OAAO;EAC/E,OAAO,GAAG,KAAK,CAAC,WAAW,KAAK,WAAW,QAAQ,WAAW,MAAM,CAAU,EAAE,SAAS;EACzF,QAAQ,GAAG,OAAO,EAAE,SAAS;EAC7B,UAAU,GAAG,OAAO,EAAE,SAAS;AACnC,CAAC,CAAC;AAWF,IAAM,yBAAyB,GAAG,KAAK,CAAC,wBAAwB,MAAM,wBAAwB,QAAQ,wBAAwB,EAAE,CAAU,EAAE,SAAS;AA8BrJ,IAAM,uBAAuB,oBAAqC,EAAE,GAAG,OAAO;EAC1E,MAAM,GAAG,QAAQ,MAAM,EAAE,SAAS;EAClC,QAAQ;EACR,WAAW,GAAG,OAAO,EAAE,SAAS;;EAEhC,UAAU,GAAG,OAAO,EAAE,SAAS;EAC/B,SAAS,gBAAgB,SAAS;EAClC,OAAO,GAAG,OAAO,EAAE,SAAS;EAC5B,YAAY,GAAG,MAAM,CAAC,GAAG,OAAO,GAAG,GAAG,QAAQ,UAAU,CAAC,CAAC,EAAE,SAAS;;;EAGrE,UAAU,GAAG,KAAK,CAAC,WAAW,UAAU,WAAW,WAAW,WAAW,MAAM,WAAW,IAAI,CAAU,EAAE,SAAS;EACnH,WAAW,GAAG,KAAK,CAAC,oBAAoB,QAAQ,oBAAoB,SAAS,oBAAoB,WAAW,oBAAoB,gBAAgB,CAAU,EAAE,SAAS;AACzK,CAAC,CAAC;AASF,IAAM,yBAAyB;;;EAG3B,UAAU,GAAG,OAAO,EAAE,SAAS;;;EAG/B,YAAY,GAAG,OAAO,EAAE,SAAS;EACjC,QAAQ;EACR,WAAW,GAAG,OAAO,EAAE,SAAS;EAChC,MAAM,GAAG,KAAK,CAAC,aAAa,OAAO,aAAa,QAAQ,aAAa,GAAG,aAAa,CAAC,CAAU,EAAE,SAAS;EAC3G,QAAQ,GAAG,KAAK,CAAC,eAAe,SAAS,eAAe,IAAI,CAAU,EAAE,SAAS;EACjF,SAAS,GAAG,OAAO,EAAE,SAAS;;EAC9B,WAAW,GAAG,OAAO,EAAE,SAAS;;EAChC,KAAK,GAAG,MAAM,CAAC,GAAG,QAAQ,GAAG,mBAAmB,CAAC,EAAE,SAAS;EAC5D,OAAO,oBAAoB,SAAS;AACxC;AA8BA,IAAM,yBAAyB,oBAAuC;EAClE,GAAG,OAAOC,gBAAA,EAAE,MAAM,GAAG,QAAQ,QAAQ,EAAA,GAAM,sBAAA,CAAwB;AAAC;AACxE,IAAM,uBAAuB,oBAAqC;EAC9D,GAAG,OAAOA,gBAAA,EAAE,MAAM,GAAG,QAAQ,MAAM,EAAA,GAAM,sBAAA,CAAwB;AAAC;AAK/D,IAAM,mBAAmB,GAAG,mBAAmB,QAAQ;EAC1D;;EACA;EACA;AACJ,CAAC;AA+IM,IAAM,kBAAkB,oBAAgC,EAAE,GAAG,OAAO;EACvE,QAAQ,GAAG,OAAO;EAClB,aAAa,GAAG,MAAM,GAAG,OAAO,CAAC;AACrC,CAAC,CAAC;AAsBK,IAAM,yBAAyB,oBAAuC,EAAE,GAAG,OAAO;;;;EAIrF,UAAU,iBAAiB,SAAS;EACpC,aAAa,oBAAoB,SAAS;EAC1C,UAAU,GAAG,MAAM,eAAe,EAAE,SAAS;EAC7C,iBAAiB,GAAG,OAAO,EAAE,SAAS;;;EAGtC,SAAS,GAAG,OAAO,EAAE,SAAS;AAClC,CAAC,CAAC;AAgDK,IAAM,oBAAoB,GAAG,MAAM;EACtC,GAAG,OAAO;EACV,GAAG,OAAO;EACV,GAAG,MAAM,GAAG,OAAO,CAAC;;;EAGpB,GAAG,OAAO,EAAE,OAAO,GAAG,QAAQ,EAAE,CAAC;;EAEjC;AACJ,CAAC;AA0HD,IAAM,2BAA2B,GAAG,MAAM;EACtC,GAAG,OAAO;EACV;EACA,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,CAAC;AACpC,CAAC;AAKD,IAAM,yBAAyB,GAAG,MAAM;EACpC,GAAG,MAAM,CAAC,GAAG,OAAO,GAAG,GAAG,OAAO,CAAC,CAAU;EAC5C;EACA,GAAG,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,GAAG,OAAO,GAAG,GAAG,OAAO,CAAC,CAAU,EAAE,CAAC;AACtE,CAAC;AAGD,IAAM,2BAA2B,GAAG,MAAM;EACtC,GAAG,OAAO;EACV;EACA,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,CAAC;AACpC,CAAC;AAsBM,IAAM,4BAA4B,oBAA0C,EAAE,GAAG,OAAO;EAC3F,WAAW,uBAAuB,SAAS;EAC3C,QAAQ,yBAAyB,SAAS;EAC1C,OAAO,uBAAuB,SAAS;EACvC,MAAM,yBAAyB,SAAS;EACxC,QAAQ,uBAAuB,SAAS;AAC5C,CAAC,CAAC;AA+BK,IAAM,yBAAyB,oBAAuC,EAAE,GAAG,OAAO;;;EAGrF,QAAQ,GAAG,OAAO,EAAE,SAAS;EAC7B,WAAW,uBAAuB,SAAS;EAC3C,QAAQ,yBAAyB,SAAS;EAC1C,MAAM,yBAAyB,SAAS;EACxC,OAAO,uBAAuB,SAAS;EACvC,QAAQ,uBAAuB,SAAS;AAC5C,CAAC,CAAC;AA2BK,IAAM,yBAAyB,oBAAuC,EAAE,GAAG,OAAO;EACrF,QAAQ,GAAG,OAAO,EAAE,SAAS;EAC7B,UAAU,GAAG,KAAK,CAAC,WAAW,WAAW,WAAW,KAAK,CAAU,EAAE,SAAS;EAC9E,WAAW,GAAG,KAAK,CAAC,QAAQ,gBAAgB,QAAQ,iBAAiB,CAAU,EAAE,SAAS;EAC1F,kBAAkB,GAAG,KAAK,CAAC,QAAQ,gBAAgB,QAAQ,iBAAiB,CAAU,EAAE,SAAS;EACjG,GAAG,GAAG,OAAO,EAAE,SAAS;EACxB,GAAG,GAAG,OAAO,EAAE,SAAS;EACxB,OAAO,GAAG,OAAO,EAAE,SAAS;EAC5B,QAAQ,GAAG,OAAO,EAAE,SAAS;AACjC,CAAC,CAAC;AAwBK,IAAM,yBAAyB,oBAAuC,EAAE,GAAG,OAAO;EACrF,UAAU,yBAAyB,SAAS;AAChD,CAAC,CAAC;AA0BK,IAAM,2BAA2B,oBAAyC,EAAE,GAAG,OAAO;EACzF,QAAQ,yBAAyB,SAAS;EAC1C,OAAO,uBAAuB,SAAS;EACvC,UAAU,GAAG,KAAK,CAAC,qBAAqB,UAAU,qBAAqB,QAAQ,CAAU,EAAE,SAAS;AACxG,CAAC,CAAC;AAqBK,IAAM,uBAAuB,oBAAqC,EAAE,GAAG,OAAO;EACjF,OAAO,GAAG,OAAO,EAAE,SAAS;EAC5B,SAAS,GAAG,OAAO,EAAE,SAAS;EAC9B,UAAU,GAAG,MAAM,CAAC,GAAG,OAAO,GAAG,GAAG,OAAO,CAAC,CAAU,EAAE,SAAS;AACrE,CAAC,CAAC;AAwBK,IAAM,sBAAsB,oBAAoC,EAAE,GAAG,OAAO;;;EAG/E,SAAS,GAAG,KAAK,CAAC,eAAe,SAAS,CAAU,EAAE,SAAS;EAC/D,QAAQ,GAAG,OAAO,EAAE,SAAS;EAC7B,QAAQ,qBAAqB,SAAS;AAC1C,CAAC,CAAC;AAaK,IAAM,uBAAuB,oBAAqC,EAAE,GAAG,OAAO;EACjF,QAAQ,GAAG,OAAO;EAClB,OAAQ,GAAG,OAAO;AACtB,CAAC,CAAC;AAWF,IAAM,kCAAkC,GAAG,MAAM;EAC7C,GAAG,MAAM,oBAAoB;EAC7B,GAAG,OAAO,EAAE,OAAO,GAAG,MAAM,oBAAoB,EAAE,CAAC;EACnD;AACJ,CAAC;AAwBM,IAAM,6BAA6B,oBAA2C,EAAE,GAAG,OAAO;;EAE7F,MAAM,GAAG,KAAK,CAAC,eAAe,QAAQ,eAAe,MAAM,CAAU;EACrE,OAAQ,uBAAuB,SAAS;EACxC,KAAQ,uBAAuB,SAAS;EACxC,QAAQ,uBAAuB,SAAS;EACxC,QAAQ,yBAAyB,SAAS;EAC1C,OAAQ,uBAAuB,SAAS;EACxC,OAAO,gCAAgC,SAAS;EAChD,eAAmB,GAAG,KAAK,CAAC,QAAQ,gBAAgB,QAAQ,iBAAiB,CAAU,EAAE,SAAS;EAClG,cAAmB,GAAG,KAAK,CAAC,uBAAuB,KAAK,uBAAuB,SAAS,uBAAuB,MAAM,CAAU,EAAE,SAAS;EAC1I,mBAAmB,GAAG,OAAO,EAAE,SAAS;AAC5C,CAAC,CAAC;AASK,IAAM,+BAA+B;AAyBrC,IAAM,yBAAyB,oBAAuC,EAAE,GAAG,OAAO;EACrF,UAAU,GAAG,OAAO;EACpB,cAAc,GAAG,KAAK,CAAC,eAAe,MAAM,eAAe,MAAM,CAAU,EAAE,SAAS;EACtF,cAAc,GAAG,KAAK,CAAC,eAAe,SAAS,eAAe,gBAAgB,CAAU,EAAE,SAAS;EACnG,QAAQ,GAAG,KAAK,CAAC,iBAAiB,OAAO,iBAAiB,OAAO,CAAU,EAAE,SAAS;EACtF,SAAS,GAAG,KAAK,CAAC,kBAAkB,MAAM,kBAAkB,KAAK,CAAU,EAAE,SAAS;EACtF,aAAa,yBAAyB,SAAS;EAC/C,YAAY,yBAAyB,SAAS;AAClD,CAAC,CAAC;AAiBK,IAAM,qBAAqB,oBAAmC,EAAE,GAAG,OAAO;EAC7E,WAAW,GAAG,QAAQ,EAAE,SAAS;AACrC,CAAC,CAAC;AA6CK,IAAM,kBAAkB,oBAAgC,EAAE,GAAG,OAAO;EACvE,aAAa,0BAA0B,SAAS;EAChD,UAAU,uBAAuB,SAAS;EAC1C,UAAU,uBAAuB,SAAS;EAC1C,UAAU,uBAAuB,SAAS;EAC1C,YAAY,yBAAyB,SAAS;EAC9C,OAAO,oBAAoB,SAAS;EACpC,cAAc,2BAA2B,SAAS;EAClD,gBAAgB,6BAA6B,SAAS;EACtD,UAAU,uBAAuB,SAAS;EAC1C,MAAM,mBAAmB,SAAS;AACtC,CAAC,CAAC;AAaK,SAAS,oBAAoB,MAAc,SAA+C;AAC7F,QAAM,WAA0B,CAAC;AAIjC,QAAM,OAAO,CAAC,MAAc,SAAuB;AAC/C,QAAI,QAAQ,KAAK,SAAS;AACtB,YAAM,MAA2B,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAC,GAAG,QAAQ,CAAC,EAAC,WAAA,OAAA,SAAA,QAAS,QAAO;AACvF,YAAM,KAAK,gBAAgB,QAAQ,KAAK,SAAS,KAAK,CAAC,OAAO,UAAU,CAAC;AACzE,UAAI,CAAC,IAAI;AACL,mBAAW,OAAO,IAAI,OAAQ,UAAS,KAAK,GAAG;MACnD;IACJ;AACA,QAAI,QAAQ,MAAM,QAAQ,KAAK,QAAQ,GAAG;AACtC,WAAK,SAAS,QAAQ,CAAC,GAAG,MAAM,KAAK,GAAG,OAAO,eAAe,IAAI,GAAG,CAAC;IAC1E;EACJ;AACA,OAAK,MAAM,MAAM;AACjB,SAAO;AACX;AAmBO,SAAS,sBAAsB,MAAc,OAAgE;AAChH,MAAI,CAAC,SAAS,CAAC,OAAO,KAAK,KAAK,EAAE,OAAQ,QAAO,CAAC;AAElD,QAAM,WAA0B,CAAC;AACjC,QAAM,OAAO,CAAC,MAAc,MAAc,WAA+B,gBAA+B;AA/xD5G,QAAAD,KAAA;AAgyDQ,QAAI,CAAC,KAAM;AAEX,UAAM,MAAM,OAAO,KAAK,eAAe,WAAW,KAAK,aAAa;AACpE,UAAM,SAAS,OAAA,OAAA,MAAO;AACtB,UAAM,cAAc,eAAgB,KAAK,SAAS,UAAU,CAAC,GAAC,MAAAA,MAAA,KAAK,YAAL,OAAA,SAAAA,IAAc,SAAd,OAAA,SAAA,GAAoB;AAIlF,QAAI,eAAe,OAAO,CAAC,OAAO,UAAU,eAAe,KAAK,OAAO,GAAG,GAAG;AACzE,YAAM,UAAU,OAAO,yCAAyC,MAC1D;AACN,UAAI,CAAC,SAAS,SAAS,OAAO,EAAG,UAAS,KAAK,OAAO;IAC1D;AACA,QAAI,MAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,WAAK,SAAS,QAAQ,CAAC,GAAG,MAAM,KAAK,GAAG,OAAO,eAAe,IAAI,KAAK,QAAQ,WAAW,CAAC;IAC/F;EACJ;AACA,OAAK,MAAM,QAAQ,QAAW,KAAK;AACnC,SAAO;AACX;AAcO,SAAS,mBAAmB,MAAc,SAAkE;AAC/G,QAAM,WAA0B,CAAC;AACjC,QAAM,OAAO,CAAC,MAAe,SAAuB;AAChD,QAAI,MAAM,QAAQ,IAAI,GAAG;AACrB,WAAK,QAAQ,CAAC,MAAM,MAAM,KAAK,MAAM,OAAO,MAAM,IAAI,GAAG,CAAC;AAC1D;IACJ;AACA,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AAEvC,UAAM,SAAU,KAA8B;AAC9C,QAAI,OAAO,WAAW,YAAY,EAAE,WAAW,OAAO,UAAU,eAAe,KAAK,SAAS,MAAM,IAAI;AACnG,YAAM,UAAU,OAAO,eAAe,SAChC;AACN,UAAI,CAAC,SAAS,SAAS,OAAO,EAAG,UAAS,KAAK,OAAO;IAC1D;AACA,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAA+B,GAAG;AACxE,UAAI,QAAQ,SAAU;AACtB,UAAI,SAAS,OAAO,UAAU,SAAU,MAAK,OAAO,OAAO,MAAM,GAAG;IACxE;EACJ;AACA,OAAK,MAAM,MAAM;AACjB,SAAO;AACX;AAWO,SAAS,qBAAqB,KAA2C;AAl2DhF,MAAAA;AAm2DI,QAAM,WAAUA,MAAA,kBAAkB,GAAG,MAArB,OAAA,SAAAA,IAAwB;AACxC,MAAI,YAAY,OAAW,QAAO,CAAC;AACnC,MAAI,iBAAiB,OAAO,MAAM,OAAW,QAAO,CAAC;AACrD,SAAO,CAAC,4BAA4B,KAAK,UAAU,OAAO,IACpD,yEAAoE;AAC9E;AAUO,SAAS,iBAAiB,KAAc,SAA+C;AAl3D9F,MAAAA;AAs3DI,QAAM,UAAS,WAAA,OAAA,SAAA,QAAS,YAAW;AACnC,QAAM,MAA2B,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAC,GAAG,OAAO;AACpE,QAAM,WAA0B,4BAA4B,QAAQ,KAAK,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,MAAM;AAC7G,MAAI,OAAO,OAAO,QAAQ,UAAU;AAChC,eAAW,KAAK,oBAAoB,KAAe,EAAE,OAAO,CAAC,GAAG;AAC5D,UAAI,CAAC,SAAS,SAAS,CAAC,EAAG,UAAS,KAAK,CAAC;IAC9C;AACA,UAAM,QAAOA,MAAA,kBAAkB,GAA4B,MAA9C,OAAA,SAAAA,IAAiD;AAC9D,eAAW,KAAK,sBAAsB,KAAe,QAAA,OAAA,SAAA,KAAM,KAAK,GAAG;AAC/D,UAAI,CAAC,SAAS,SAAS,CAAC,EAAG,UAAS,KAAK,CAAC;IAC9C;AACA,eAAW,KAAK,mBAAmB,KAAe,QAAA,OAAA,SAAA,KAAM,OAAO,GAAG;AAC9D,UAAI,CAAC,SAAS,SAAS,CAAC,EAAG,UAAS,KAAK,CAAC;IAC9C;AACA,eAAW,KAAK,qBAAqB,GAA4B,GAAG;AAChE,UAAI,CAAC,SAAS,SAAS,CAAC,EAAG,UAAS,KAAK,CAAC;IAC9C;EACJ;AACA,SAAO;AACX;AAgBO,IAAM,mBAAmB,GAAG,WAAW;;;;;;;;;EAS1C,MAAM,GAAG,OAAO;;;;;;;EAOhB,SAAS,GAAG,OAAO,EAAE,SAAS;;;EAG9B,aAAa,GAAG,OAAO,EAAE,SAAS;EAClC,IAAI,GAAG,OAAO,EAAE,SAAS;EACzB,MAAM,GAAG,IAAI,EAAE,SAAS;;;;EAIxB,SAAS,gBAAgB,SAAS;;;;EAIlC,SAAS,yBAAyB,SAAS;EAC3C,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC,GAAG,OAAO,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS;AACpE,GAAG,iBAAiB;AAMpB,IAAI,eAA8B,GAAG,WAAWE,eAAAD,gBAAA,CAAA,GACzC,iBAAiB,MAAA,GADwB;EAE5C,UAAU,GAAG,KAAK,MAAM,GAAG,MAAM,YAAY,GAAG,CAAC,CAAC,EAAE,SAAS;AACjE,CAAA,GAAG,iBAAiB;AAgDb,IAAM,sBAAsB,GAAG,OAAO;;;EAGzC,OAAO,GAAG,MAAM,CAAC,GAAG,OAAO,GAAG,GAAG,OAAO,CAAC,CAAC,EAAE,SAAS;EACrD,QAAQ,GAAG,MAAM,CAAC,GAAG,OAAO,GAAG,GAAG,OAAO,CAAC,CAAC,EAAE,SAAS;EACtD,SAAS,GAAG,OAAO,EAAE,SAAS;EAC9B,UAAU,uBAAuB,SAAS;AAC9C,CAAC;AA8BM,IAAM,8BAA8B,GAAG,WAAWC,eAAAD,gBAAAA,gBAAA,CAAA,GAClD,iBAAiB,MAAA,GACjB,oBAAoB,MAAA,GAF8B;EAGrD,MAAM,GAAG,QAAQ,KAAK;;EACtB,UAAU,GAAG,MAAM,YAAY,EAAE,SAAS;AAC9C,CAAA,GAAG,iBAAiB;AA4Fb,IAAM,qBAAqB,oBAAmC,EAAE,GAAG,OAAO;EAC7E,GAAG,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;EACjC,GAAG,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,SAAS;EAC5C,GAAG,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,SAAS;EAC5C,GAAG,GAAG,QAAQ,EAAE,SAAS;AAC7B,CAAC,CAAC;AChnEF,IAAI,aAAa;AAEV,SAAS,mBAA2B;AACvC,QAAM,YAAY,KAAK,IAAI,EAAE,SAAS,EAAE;AACxC,QAAM,WAAW,EAAE,YAAY,SAAS,EAAE;AAC1C,QAAM,SAAS,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC;AACxD,SAAO,SAAS,YAAY,UAAU;AAC1C;AAOO,SAAS,UAAa,OAAa;AACtC,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AACxD,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,IAAI,CAAA,SAAQ,UAAU,IAAI,CAAC;AAElE,QAAM,MAAM;AAEZ,QAAM,SAAkC,CAAC;AACzC,aAAW,OAAO,OAAO,KAAK,GAAG,GAAG;AAChC,WAAO,GAAG,IAAI,UAAU,IAAI,GAAG,CAAC;EACpC;AACA,SAAO;AACX;AAiBO,SAAS,eAAe,KAAmD;AAtDlF,MAAAE;AAwDI,QAAM,SAAgC,UAAU,GAAG;AAGnD,QAAM,QAAQ,oBAAI,IAAoB;AAGtC,QAAM,eAAe,oBAAI,IAAI,CAAC,QAAQ,YAAY,CAAC;AAGnD,QAAM,cAAc,oBAAI,IAAI;IACxB;IAAQ;IAAU;IAAa;IAAY;IAC3C;IAAU;IAAgB;IAAc;IACxC;IAAU;IAAe;EAC7B,CAAC;AAKD,QAAM,mBAAmB,oBAAI,IAAI,CAAC,YAAY,gBAAgB,CAAC;AAC/D,QAAM,oBAAoB,CAAC,KAAa,cACpC,QAAQ,aAAa,cAAc,cAAc,cAAc;AAGnE,WAAS,WAAW,MAAiB;AACjC,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AAEvC,QAAI,KAAK,MAAM,OAAO,KAAK,OAAO,UAAU;AACxC,YAAM,QAAQ,KAAK;AACnB,YAAM,QAAQ,iBAAiB;AAC/B,YAAM,IAAI,OAAO,KAAK;AACtB,WAAK,KAAK;IACd,WAAW,KAAK,SAAS;AAErB,WAAK,KAAK,iBAAiB;IAC/B;AAGA,QAAI,MAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,iBAAW,SAAS,KAAK,UAAU;AAC/B,mBAAW,KAAK;MACpB;IACJ;EACJ;AAGA,WAAS,WAAW,MAAW,WAA0B;AACrD,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AAEvC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC7C,UAAI,QAAQ,YAAY;AACpB,YAAI,MAAM,QAAQ,KAAK,GAAG;AACtB,qBAAW,SAAS,OAAO;AACvB,uBAAW,KAAK;UACpB;QACJ;AACA;MACJ;AAEA,UAAI,OAAO,UAAU,UAAU;AAE3B,YAAI,aAAa,IAAI,GAAG,KAAK,MAAM,WAAW,GAAG,GAAG;AAChD,gBAAM,QAAQ,MAAM,MAAM,CAAC;AAC3B,gBAAM,QAAQ,MAAM,IAAI,KAAK;AAC7B,cAAI,OAAO;AACP,iBAAK,GAAG,IAAI,MAAM;UACtB;QACJ,WAES,YAAY,IAAI,GAAG,GAAG;AAC3B,eAAK,GAAG,IAAI,eAAe,OAAO,KAAK;QAC3C,WAIS,iBAAiB,IAAI,GAAG,KAAK,kBAAkB,KAAK,SAAS,GAAG;AACrE,gBAAM,UAAU,MAAM,WAAW,GAAG;AACpC,gBAAM,QAAQ,MAAM,IAAI,UAAU,MAAM,MAAM,CAAC,IAAI,KAAK;AACxD,cAAI,OAAO;AACP,iBAAK,GAAG,IAAI,UAAU,MAAM,QAAQ;UACxC;QACJ,WAES,MAAM,SAAS,OAAO,GAAG;AAC9B,eAAK,GAAG,IAAI,eAAe,OAAO,KAAK;QAC3C;MACJ,WAES,QAAQ,WAAW,OAAO,UAAU,YAAY,UAAU,MAAM;AACrE,mBAAW,CAAC,WAAW,UAAU,KAAK,OAAO,QAAQ,KAAK,GAAG;AACzD,cAAI,OAAO,eAAe,UAAU;AAC/B,kBAAc,SAAS,IAAI,eAAe,YAAY,KAAK;UAChE;QACJ;MACJ,WAIS,OAAO,UAAU,YAAY,UAAU,MAAM;AAClD,mBAAW,OAAO,GAAG;MACzB;IACJ;EACJ;AAEA,aAAW,MAAM;AACjB,aAAW,MAAM;AAIjB,QAAM,eAAcA,MAAA,OAAO,aAAP,OAAA,SAAAA,IAAiB;AACrC,MAAI,MAAM,QAAQ,WAAW,GAAG;AAC5B,UAAM,kBAAkB,YAAY,IAAI,CAAA,YAAW;AAtK3D,UAAAA;AAuKY,YAAM,SAAS,QAAQ,OAAO,WAAW,GAAG;AAC5C,YAAM,KAAK,SAAS,QAAQ,OAAO,MAAM,CAAC,IAAI,QAAQ;AACtD,YAAM,SAAQA,MAAA,MAAM,IAAI,EAAE,MAAZ,OAAAA,MAAiB;AAC/B,aAAOC,eAAAC,gBAAA,CAAA,GAAK,OAAA,GAAL,EAAc,QAAQ,SAAS,MAAM,QAAQ,MAAM,CAAA;IAC9D,CAAC;AACD,WAAO,WAAWD,eAAAC,gBAAA,CAAA,GAAK,OAAO,QAAA,GAAZ,EAAsB,UAAU,gBAAgB,CAAA;EACtE;AAEA,SAAO;AACX;AAKA,SAAS,eAAe,OAAe,OAAoC;AACvE,SAAO,MAAM,QAAQ,oBAAoB,CAAC,OAAO,UAAU;AACvD,UAAM,QAAQ,MAAM,IAAI,KAAK;AAC7B,WAAO,QAAQ,UAAU,QAAQ,MAAM;EAC3C,CAAC;AACL;ACnKO,SAAS,gBAAgB,MAAoB,cAAc,OAAe;AAvBjF,MAAAF,KAAA,IAAA,IAAA;AAwBI,QAAM,IAAI,KAAK;AACf,QAAM,IAAI,KAAK;AACf,QAAM,IAAI,KAAK;AACf,QAAM,IAAI,KAAK;AAEf,MAAI,CAAC,EAAE,OAAQ,QAAO;AAEtB,QAAM,IAAmB,CAAC;AAC1B,QAAM,MAAM,EAAE;AACd,IAAE,KAAK,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;AAEpC,WAAS,MAAM,GAAG,MAAM,KAAK,OAAO;AAChC,UAAM,QAAQ,EAAE,MAAM,CAAC;AACvB,UAAM,SAAQA,MAAA,KAAA,OAAA,SAAA,EAAI,MAAM,CAAA,MAAV,OAAAA,MAAgB;AAC9B,UAAM,SAAQ,KAAA,KAAA,OAAA,SAAA,EAAI,GAAA,MAAJ,OAAA,KAAY,EAAE,GAAG;AAC/B,UAAM,QAAQ,EAAE,GAAG;AAGnB,UAAM,SAAS,CAAC,gBAAgB,MAAM,CAAC,MAAM,MAAM,CAAC,KAAK,MAAM,CAAC,MAAM,MAAM,CAAC,OACxE,MAAM,CAAC,MAAM,MAAM,CAAC,KAAK,MAAM,CAAC,MAAM,MAAM,CAAC;AAElD,QAAI,QAAQ;AACR,QAAE,KAAK,MAAM,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,CAAC;IAC1C,OAAO;AAEH,QAAE,KAAK,MAAM,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,CAAC;IAC9G;EACJ;AAEA,MAAI,KAAK,MAAM,GAAG;AACd,UAAM,QAAQ,EAAE,MAAM,CAAC;AACvB,UAAM,SAAQ,KAAA,KAAA,OAAA,SAAA,EAAI,MAAM,CAAA,MAAV,OAAA,KAAgB;AAC9B,UAAM,UAAS,KAAA,KAAA,OAAA,SAAA,EAAI,CAAA,MAAJ,OAAA,KAAU,EAAE,CAAC;AAC5B,UAAM,SAAS,EAAE,CAAC;AAGlB,UAAM,SAAS,CAAC,gBAAgB,MAAM,CAAC,MAAM,MAAM,CAAC,KAAK,MAAM,CAAC,MAAM,MAAM,CAAC,OACxE,OAAO,CAAC,MAAM,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,OAAO,CAAC;AAEtD,QAAI,CAAC,QAAQ;AACT,QAAE,KAAK,MAAM,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,IAAI,MAAM,OAAO,CAAC,IAAI,MAAM,OAAO,CAAC,IAAI,MAAM,OAAO,CAAC,IAAI,MAAM,OAAO,CAAC,CAAC;IAClH;AAEA,MAAE,KAAK,GAAG;EACd;AAEA,SAAO,EAAE,KAAK,EAAE;AACpB;AAQO,SAAS,eAAe,GAAW,GAAW,GAAmB;AACpE,SAAO,KAAK,IAAI,KAAK;AACzB;AASO,SAAS,eAAe,GAAkB,GAAkB,GAA0B;AACzF,QAAM,MAAqB,CAAC;AAC5B,QAAM,QAAQ,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM;AACzC,WAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC5B,QAAI,CAAC,IAAI,eAAe,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,GAAG,CAAC;EACnD;AACA,SAAO;AACX;AAMO,SAAS,iBAAiB,GAAkB,GAAkB,GAA0B;AAC3F,SAAO;IACH,eAAe,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,GAAG,CAAC;IACtC,eAAe,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,GAAG,CAAC;IACtC,eAAe,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,GAAG,CAAC;IACtC,eAAe,EAAE,CAAC,MAAM,SAAY,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,SAAY,IAAI,EAAE,CAAC,GAAG,CAAC;EAClF;AACJ;AASO,SAAS,mBACZ,QACA,QACA,UACmB;AACnB,QAAM,QAAQ,KAAK,IAAI,OAAO,QAAQ,OAAO,MAAM;AACnD,QAAM,MAA2B,CAAC;AAClC,WAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC5B,QAAI,KAAK,kBAAkB,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,QAAQ,CAAC;EAC9D;AACA,SAAO;AACX;AAWO,SAAS,kBACZ,OACA,OACA,UACY;AAjJhB,MAAAA,KAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA;AAkJI,MAAI,CAAC,SAAS,CAAC,MAAO,QAAO,SAAS,SAAS,EAAE,GAAG,CAAC,EAAE;AAEvD,QAAM,IAAI,KAAK,IAAI,KAAK,IAAI,UAAU,CAAC,GAAG,CAAC;AAC3C,QAAM,MAAM,KAAK,IAAI,MAAM,EAAE,QAAQ,MAAM,EAAE,MAAM;AAEnD,QAAM,IAA0B,CAAC;AACjC,QAAM,IAA0B,CAAC;AACjC,QAAM,IAA0B,CAAC;AAEjC,WAAS,MAAM,GAAG,MAAM,KAAK,OAAO;AAChC,UAAM,KAAK,MAAM,EAAE,GAAG;AACtB,UAAM,KAAK,MAAM,EAAE,GAAG;AACtB,MAAE,KAAK,eAAe,IAAI,IAAI,CAAC,CAAC;AAGhC,UAAM,MAAK,MAAAA,MAAA,MAAM,MAAN,OAAA,SAAAA,IAAU,GAAA,MAAV,OAAA,KAAkB;AAC7B,UAAM,MAAK,MAAA,KAAA,MAAM,MAAN,OAAA,SAAA,GAAU,GAAA,MAAV,OAAA,KAAkB;AAC7B,MAAE,KAAK,eAAe,IAAI,IAAI,CAAC,CAAC;AAEhC,UAAM,MAAK,MAAA,KAAA,MAAM,MAAN,OAAA,SAAA,GAAU,GAAA,MAAV,OAAA,KAAkB;AAC7B,UAAM,MAAK,MAAA,KAAA,MAAM,MAAN,OAAA,SAAA,GAAU,GAAA,MAAV,OAAA,KAAkB;AAC7B,MAAE,KAAK,eAAe,IAAI,IAAI,CAAC,CAAC;EACpC;AAEA,SAAO,EAAE,GAAG,GAAG,EAAE,SAAS,IAAI,QAAW,GAAG,EAAE,SAAS,IAAI,QAAW,IAAG,KAAA,MAAM,MAAN,OAAA,KAAW,MAAM,EAAE;AAChG;AAaO,SAAS,MACZ,OACA,OACA,OACA,QACA,QACM;AACN,MAAI,UAAU,MAAO,QAAO;AAC5B,QAAM,KAAK,QAAQ,UAAU,QAAQ;AACrC,SAAO,SAAS,KAAK,SAAS;AAClC;AAQO,SAAS,kBAAkB,KAAa,KAAa,GAAmB;AAC3E,MAAI,KAAK,EAAG,QAAO;AACnB,MAAI,KAAK,EAAG,QAAO;AAEnB,QAAM,KAAK,IAAI;AACf,QAAM,KAAK,KAAK,MAAM,OAAO;AAC7B,QAAM,KAAK,IAAI,KAAK;AAEpB,WAAS,QAAQ,GAAW;AAAE,aAAS,KAAK,IAAI,MAAM,IAAI,MAAM;EAAG;AACnE,WAAS,SAAS,GAAW;AAAE,YAAQ,IAAI,KAAK,IAAI,IAAI,MAAM,IAAI;EAAI;AAEtE,MAAI,KAAK;AACT,MAAI,KAAK;AACT,MAAI,KAAK;AAET,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AACxB,UAAM,KAAK,QAAQ,EAAE,IAAI;AACzB,QAAI,KAAK,IAAI,EAAE,IAAI,KAAM,QAAO;AAChC,UAAM,KAAK,SAAS,EAAE;AACtB,QAAI,KAAK,IAAI,EAAE,IAAI,KAAM;AACzB,UAAM,KAAK;EACf;AAEA,OAAK;AACL,SAAO,KAAK,IAAI;AACZ,UAAM,KAAK,QAAQ,EAAE;AACrB,QAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAM,QAAO;AACpC,QAAI,IAAI,GAAI,MAAK;QACZ,MAAK;AACV,UAAM,KAAK,MAAM;EACrB;AAEA,SAAO;AACX;AAQO,SAAS,YAAY,QAA0C;AAClE,QAAM,CAAC,KAAK,KAAK,KAAK,GAAG,IAAI;AAE7B,QAAM,KAAK,IAAI;AACf,QAAM,KAAK,KAAK,MAAM,OAAO;AAC7B,QAAM,KAAK,IAAI,KAAK;AAEpB,WAAS,aAAa,GAAW;AAAE,aAAS,KAAK,IAAI,MAAM,IAAI,MAAM;EAAG;AAExE,SAAO,SAAU,GAAW;AACxB,WAAO,aAAa,kBAAkB,KAAK,KAAK,CAAC,CAAC;EACtD;AACJ;AAIA,SAAS,MAAM,GAAW,GAAW,GAAmB;AACpD,SAAO,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;AAC9D;AAOO,SAAS,qBACZ,IAAY,IAAY,IAAY,IAAY,GACmC;AACnF,QAAM,KAAK,MAAM,IAAI,IAAI,CAAC;AAC1B,QAAM,KAAK,MAAM,IAAI,IAAI,CAAC;AAC1B,QAAM,KAAK,MAAM,IAAI,IAAI,CAAC;AAC1B,QAAM,KAAK,MAAM,IAAI,IAAI,CAAC;AAC1B,QAAM,KAAK,MAAM,IAAI,IAAI,CAAC;AAC1B,QAAM,IAAI,MAAM,IAAI,IAAI,CAAC;AACzB,SAAO;IACH,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC;IACpB,OAAO,CAAC,GAAG,IAAI,IAAI,EAAE;EACzB;AACJ;AAUO,SAAS,YACZ,QACA,WACuD;AACvD,MAAI,CAAC,OAAQ,QAAO,EAAE,MAAM,QAAW,OAAO,OAAU;AACxD,MAAI,aAAa,EAAG,QAAO,EAAE,MAAM,QAAW,OAAO,OAAO;AAC5D,MAAI,aAAa,EAAG,QAAO,EAAE,MAAM,QAAQ,OAAO,OAAU;AAE5D,QAAM,CAAC,IAAI,IAAI,IAAI,EAAE,IAAI;AACzB,QAAM,IAAI,kBAAkB,IAAI,IAAI,SAAS;AAE7C,QAAM,KAAa,CAAC,GAAG,CAAC;AACxB,QAAM,KAAa,CAAC,IAAI,EAAE;AAC1B,QAAM,KAAa,CAAC,IAAI,EAAE;AAC1B,QAAM,KAAa,CAAC,GAAG,CAAC;AAExB,QAAM,EAAE,MAAM,MAAM,IAAI,qBAAqB,IAAI,IAAI,IAAI,IAAI,CAAC;AAG9D,QAAM,KAAK,KAAK,CAAC,EAAE,CAAC;AACpB,QAAM,KAAK,KAAK,CAAC,EAAE,CAAC;AAEpB,MAAI;AACJ,MAAI,KAAK,QAAQ,KAAK,IAAI,EAAE,IAAI,MAAM;AAClC,iBAAa;MACT,KAAK,CAAC,EAAE,CAAC,IAAI;MAAI,KAAK,CAAC,EAAE,CAAC,IAAI;MAC9B,KAAK,CAAC,EAAE,CAAC,IAAI;MAAI,KAAK,CAAC,EAAE,CAAC,IAAI;IAClC;EACJ;AAEA,MAAI;AACJ,QAAM,KAAK,IAAI;AACf,QAAM,KAAK,IAAI;AACf,MAAI,KAAK,QAAQ,KAAK,IAAI,EAAE,IAAI,MAAM;AAClC,kBAAc;OACT,MAAM,CAAC,EAAE,CAAC,IAAI,MAAM;OAAK,MAAM,CAAC,EAAE,CAAC,IAAI,MAAM;OAC7C,MAAM,CAAC,EAAE,CAAC,IAAI,MAAM;OAAK,MAAM,CAAC,EAAE,CAAC,IAAI,MAAM;IAClD;EACJ;AAEA,SAAO,EAAE,MAAM,YAAY,OAAO,YAAY;AAClD;AAOO,SAAS,cAAc,QAAgD;AAC1E,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,CAAC,IAAI,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,CAAC;AACtE;AAOO,SAAS,OAAO,OAA8B;AACjD,QAAM,IAAI,KAAK,MAAM,MAAM,CAAC,IAAI,GAAG;AACnC,QAAM,IAAI,KAAK,MAAM,MAAM,CAAC,IAAI,GAAG;AACnC,QAAM,IAAI,KAAK,MAAM,MAAM,CAAC,IAAI,GAAG;AACnC,SAAO,MAAM,WAAW,IACpB,UAAU,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM,MAAM,CAAC,IAAI,MACnD,SAAS,IAAI,MAAM,IAAI,MAAM,IAAI;AACzC;AAGO,SAAS,UAAU,GAAqB;AAvW/C,MAAAA;AAwWI,QAAM,SAAQA,MAAA,EAAE,MAAM,eAAe,MAAvB,OAAA,SAAAA,IAA2B,CAAA;AACzC,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,yBAAyB;AACrD,QAAM,QAAQ,MAAM,MAAM,GAAG,EAAE,IAAI,CAAA,MAAK,CAAC,EAAE,KAAK,CAAC;AACjD,SAAO,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,KAAK,GAAI,MAAM,CAAC,MAAM,SAAY,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAE;AACzG;AAGA,SAAS,SAAS,GAAqB;AACnC,QAAM,MAAM,EAAE,MAAM,CAAC;AACrB,QAAM,UAAU,IAAI,UAAU;AAE9B,QAAM,IAAI,UAAU,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,MAAM,GAAG,CAAC;AACpD,QAAM,IAAI,UAAU,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,MAAM,GAAG,CAAC;AACpD,QAAM,IAAI,UAAU,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,MAAM,GAAG,CAAC;AACpD,QAAM,IAAI,IAAI,WAAW,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,WAAW,IAAI,IAAI,MAAM,GAAG,CAAC,IAAI;AAEpF,QAAM,SAAS;IACX,SAAS,GAAG,EAAE,IAAI;IAClB,SAAS,GAAG,EAAE,IAAI;IAClB,SAAS,GAAG,EAAE,IAAI;EACtB;AAEA,MAAI,MAAM,MAAM;AACZ,WAAO,KAAK,SAAS,GAAG,EAAE,IAAI,GAAG;EACrC;AAEA,SAAO;AACX;AAIO,SAAS,WAAW,GAA8B;AACrD,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,MAAM,QAAQ,CAAC,EAAG,QAAO;AAC7B,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,MAAI,EAAE,WAAW,GAAG,GAAG;AACnB,WAAO,SAAS,CAAC;EACrB,WAAW,EAAE,WAAW,KAAK,GAAG;AAC5B,WAAO,UAAU,CAAC;EACtB,OAAO;AAEH,YAAQ,KAAK,+BAA+B,CAAC;EACjD;AACA,SAAO;AACX;AAGO,IAAM,sBAAsB,oBAAI,IAAI,CAAC,SAAS,QAAQ,eAAe,kBAAkB,cAAc,QAAQ,CAAC;AAE9G,IAAM,wBAAwB,oBAAI,IAAI,CAAC,aAAa,UAAU,SAAS,MAAM,CAAC;AAE9E,IAAM,0BAA0B,oBAAI,IAAI,CAAC,mBAAmB,gBAAgB,CAAC;AAkB7E,SAAS,sBACZ,OACA,MACM;AAhbV,MAAAA;AAibI,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,aAAYA,MAAA,QAAA,OAAA,SAAA,KAAM,cAAN,OAAAA,MAAmB;AACrC,QAAM,OAAsB,CAAC;AAC7B,QAAM,IAAI,MAAM;AAChB,QAAM,IAAI,MAAM;AAChB,QAAM,IAAI,MAAM;AAChB,QAAM,IAAI,MAAM;AAChB,QAAM,IAAI,MAAM;AAChB,QAAM,KAAK,YAAY,OAAO;AAC9B,QAAM,KAAK,YAAY,QAAQ;AAC/B,MAAI,EAAG,MAAK,KAAK,eAAe,EAAE,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC,IAAI,KAAK,GAAG;AACjE,MAAI,EAAG,MAAK,KAAK,eAAe,EAAE,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC,IAAI,KAAK,GAAG;AACjE,MAAI,MAAM,UAAa,MAAM,KAAM,MAAK,KAAK,YAAY,IAAI,KAAK,GAAG;AAErE,MAAI,MAAM,UAAa,MAAM,KAAM,MAAK,KAAK,WAAW,IAAI,KAAK,GAAG;AACpE,MAAI,EAAG,MAAK,KAAK,WAAW,EAAE,CAAC,IAAI,MAAM,EAAE,CAAC,IAAI,GAAG;AACnD,MAAI,EAAG,MAAK,KAAK,eAAgB,CAAC,EAAE,CAAC,IAAK,KAAK,MAAO,CAAC,EAAE,CAAC,IAAK,KAAK,GAAG;AACvE,SAAO,KAAK,KAAK,EAAE;AACvB;AAYO,SAAS,oBAAoBG,MAA8D;AA/clG,MAAAH,KAAA;AAgdI,MAAI,CAACG,QAAO,OAAOA,SAAQ,SAAU,QAAO;AAC5C,QAAM,MAAwB,CAAC;AAC/B,QAAM,KAAK;AACX,QAAM,QAAQ,CAAC,aAAa,UAAU,SAAS,OAAO;AACtD,MAAI,UAAU;AACd,MAAI;AACJ,UAAQ,IAAI,GAAG,KAAKA,IAAG,OAAO,MAAM;AAChC,UAAM,KAAK,EAAE,CAAC;AACd,UAAM,MAAM,MAAM,QAAQ,EAAE;AAC5B,QAAI,MAAM,KAAK,OAAO,QAAS,QAAO;AACtC,cAAU;AACV,UAAM,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,EAAE,OAAO,OAAO,EAAE,IAAI,MAAM;AAC5D,QAAI,KAAK,KAAK,CAAA,MAAK,OAAO,MAAM,CAAC,CAAC,EAAG,QAAO;AAC5C,QAAI,OAAO,aAAa;AACpB,UAAI,KAAK,SAAS,KAAK,KAAK,SAAS,EAAG,QAAO;AAC/C,UAAI,YAAY,CAAC,KAAK,CAAC,IAAGH,MAAA,KAAK,CAAC,MAAN,OAAAA,MAAW,CAAC;IAC1C,WAAW,OAAO,UAAU;AACxB,UAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,UAAI,SAAS,KAAK,CAAC;IACvB,WAAW,OAAO,SAAS;AACvB,UAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,UAAI,OAAO,KAAK,CAAC;IACrB,OAAO;AACH,UAAI,KAAK,SAAS,KAAK,KAAK,SAAS,EAAG,QAAO;AAC/C,UAAI,QAAQ,CAAC,KAAK,CAAC,IAAG,KAAA,KAAK,CAAC,MAAN,OAAA,KAAW,KAAK,CAAC,CAAC;IAC5C;EACJ;AAEA,MAAIG,KAAI,QAAQ,8BAA8B,EAAE,EAAE,QAAQ,UAAU,EAAE,EAAE,OAAQ,QAAO;AACvF,SAAO,OAAO,KAAK,GAAG,EAAE,SAAS,MAAM;AAC3C;AAGO,IAAM,sBAAsB,oBAAI,IAAI,CAAC,mBAAmB,gBAAgB,CAAC;AAEzE,IAAM,yBAAyB;AAO/B,SAAS,qBAAqB,OAAuB;AACxD,SAAO,MAAM,SAAS,GAAG,IAAI,MAAM,QAAQ,aAAa,CAAC,GAAG,WAAW,OAAO,YAAY,CAAC,IAAI;AACnG;AAMO,SAAS,gBAAgB,MAAuB;AACnD,SAAO,CAAC,KAAK,SAAS,GAAG,KAAK,aAAa,KAAK,IAAI;AACxD;AAGA,IAAM,uBAAuB,oBAAI,IAAI;;EAEjC;EACA;;EAGA;EACA;EACA;;EAGA;EACA;EACA;;EAGA;EACA;EACA;;EAGA;EACA;EACA;EACA;EACA;;EAGA;EACA;EACA;;EAGA;EACA;EACA;;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;;;;;;;AAUJ,CAAC;AAOM,SAAS,6BAA6B,OAAuB;AAChE,SAAO,qBAAqB,IAAI,KAAK,IACjC,QACA,MAAM,QAAQ,sBAAsB,OAAO,EAAE,YAAY;AACjE;AAyBO,SAAS,MAAM,OAAe,KAAa,KAAqB;AACnE,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,OAAO,GAAG,CAAC;AAC7C;AAgCO,SAAS,iBACZ,IAAY,IAAY,IAAY,IACpC,GACM;AACN,MAAI,KAAK,EAAG,QAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;AAChC,MAAI,KAAK,EAAG,QAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;AAChC,QAAM,IAAI,IAAI;AACd,QAAM,KAAK,IAAI;AACf,QAAM,KAAK,KAAK;AAChB,QAAM,KAAK,IAAI;AACf,QAAM,KAAK,KAAK;AAChB,QAAM,KAAK;AACX,QAAM,KAAK,IAAI,IAAK;AACpB,QAAM,KAAK,IAAI,KAAK;AACpB,QAAM,KAAK;AACX,SAAO;IACH,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC;IAChD,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC;EACpD;AACJ;AAiBA,IAAM,iBAAiB;AAChB,SAAS,sBACZ,IAAY,IAAY,IAAY,IACpC,GACM;AACN,QAAM,SAAS,0BAA0B,IAAI,IAAI,IAAI,IAAI,CAAC;AAC1D,MAAI,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG;AAEpC,UAAM,UAAU,IAAI,MAAM,IAAI,iBAAiB,IAAI;AACnD,WAAO,0BAA0B,IAAI,IAAI,IAAI,IAAI,OAAO;EAC5D;AACA,SAAO;AACX;AAEA,SAAS,0BACL,IAAY,IAAY,IAAY,IACpC,GACM;AACN,QAAM,IAAI,IAAI;AACd,QAAM,IAAI,IAAI,IAAI;AAClB,QAAM,IAAI,IAAI,IAAI;AAClB,QAAM,IAAI,IAAI,IAAI;AAClB,SAAO;IACH,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC;IAC7D,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC;EACjE;AACJ;AA4BO,SAAS,sBACZ,IAAY,IAAY,IAAY,IACpC,QAAgB,KACJ;AACZ,QAAM,IAAI,QAAQ;AAClB,QAAM,KAAK,IAAI,aAAa,CAAC;AAC7B,QAAM,KAAK,IAAI,aAAa,CAAC;AAE7B,MAAI,OAAO,iBAAiB,IAAI,IAAI,IAAI,IAAI,CAAC;AAC7C,KAAG,CAAC,IAAI;AACR,KAAG,CAAC,IAAI;AAER,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AACxB,UAAM,IAAI,IAAI;AACd,UAAM,MAAM,iBAAiB,IAAI,IAAI,IAAI,IAAI,CAAC;AAC9C,UAAM,KAAK,IAAI,CAAC,IAAI,KAAK,CAAC;AAC1B,UAAM,KAAK,IAAI,CAAC,IAAI,KAAK,CAAC;AAC1B,WAAO,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE;AAClC,OAAG,CAAC,IAAI;AACR,OAAG,CAAC,IAAI;AACR,WAAO;EACX;AACA,SAAO,EAAE,IAAI,GAAG;AACpB;AAgBO,SAAS,sBAAsB,KAAmB,UAA0B;AAC/E,QAAM,EAAE,IAAI,GAAG,IAAI;AACnB,QAAM,OAAO,GAAG,SAAS;AACzB,MAAI,YAAY,EAAU,QAAO,GAAG,CAAC;AACrC,MAAI,YAAY,GAAG,IAAI,EAAG,QAAO,GAAG,IAAI;AAGxC,MAAI,KAAK;AACT,MAAI,KAAK;AACT,SAAO,KAAK,IAAI;AACZ,UAAM,MAAO,KAAK,OAAQ;AAC1B,QAAI,GAAG,GAAG,IAAI,SAAU,MAAK,MAAM;QACX,MAAK;EACjC;AAEA,QAAM,QAAQ,GAAG,KAAK,CAAC;AACvB,QAAM,OAAQ,GAAG,EAAE;AACnB,QAAM,OAAQ,OAAO;AACrB,QAAM,OAAQ,OAAO,KAAK,WAAW,SAAS,OAAO;AACrD,SAAO,GAAG,KAAK,CAAC,IAAI,QAAQ,GAAG,EAAE,IAAI,GAAG,KAAK,CAAC;AAClD;AAoBO,SAAS,gBAAgB,KAAmB,GAAmB;AAClE,QAAM,EAAE,IAAI,GAAG,IAAI;AACnB,QAAM,OAAO,GAAG,SAAS;AACzB,MAAI,KAAK,GAAG,CAAC,EAAM,QAAO,GAAG,CAAC;AAC9B,MAAI,KAAK,GAAG,IAAI,EAAG,QAAO,GAAG,IAAI;AACjC,MAAI,KAAK,GAAG,KAAK;AACjB,SAAO,KAAK,IAAI;AACZ,UAAM,MAAO,KAAK,OAAQ;AAC1B,QAAI,GAAG,GAAG,IAAI,EAAG,MAAK,MAAM;QACX,MAAK;EAC1B;AACA,QAAM,QAAQ,GAAG,KAAK,CAAC;AACvB,QAAM,OAAQ,GAAG,EAAE,IAAI;AACvB,QAAM,OAAQ,OAAO,KAAK,IAAI,SAAS,OAAO;AAC9C,SAAO,GAAG,KAAK,CAAC,IAAI,QAAQ,GAAG,EAAE,IAAI,GAAG,KAAK,CAAC;AAClD;AAYO,SAAS,aAAa,QAAmD;AAC5E,MAAI,CAAC,OAAQ,QAAO,CAAC,MAAM;AAC3B,QAAM,UAAkB,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC;AACnE,SAAO,YAAY,OAAO;AAC9B;AC1zBO,IAAM,+BAA+B,oBAAI,IAAI;EAChD;EACA;AACJ,CAAC;AAsBD,IAAM,wBAAwB,oBAAI,IAAI;EAClC;;EACA;;EACA;;EACA;;EACA;;EACA;;EACA;;EACA;;EACA;;AACJ,CAAC;AAMD,IAAM,wBAAwB,oBAAI,IAAI,CAAC,QAAQ,cAAc,KAAK,CAAC;AAM5D,IAAM,0BAA0B,oBAAI,IAAY,CAAC,gBAAgB,WAAW,CAAC;AAIpF,IAAM,uBAAuB;AAM7B,IAAM,oBAAoB;AAU1B,IAAM,+BAA+B;AACrC,IAAM,uBAAuB,CAAC,YAAY,QAAQ,UAAU,SAAS,IAAI;AACzE,SAAS,4BAA4BA,MAAsB;AACvD,QAAM,IAAI,6BAA6B,KAAKA,IAAG;AAC/C,SAAO,CAAC,CAAC,KAAK,qBAAqB,KAAK,CAAA,UAAS,EAAE,CAAC,EAAE,WAAW,KAAK,CAAC;AAC3E;AAUA,SAAS,oBAAoB,WAA4B;AAKrD,MAAI,UAAU,WAAW,IAAI,EAAG,QAAO;AACvC,SAAO;AACX;AAqBO,SAAS,uBAAuB,MAAc,OAA6B;AAC9E,QAAM,YAAY,KAAK,YAAY;AAEnC,MAAI,oBAAoB,SAAS,GAAG;AAChC,YAAQ,KAAK,mDAAmD,SAAS;AACzE,WAAO;EACX;AAEA,MAAI,cAAc,UAAU,cAAc,YAAY,cAAc,aAAa;AAC7E,UAAMA,OAAM,OAAO,KAAK;AACxB,QAAIA,KAAI,SAAS,MAAM,KAAK,CAAC,kBAAkB,KAAKA,IAAG,GAAG;AACtD,cAAQ,KAAK,gBAAgB,YAAY,oDAAoD,KAAK;AAClG,aAAO;IACX;AACA,WAAO;EACX;AAEA,MAAI,sBAAsB,IAAI,SAAS,GAAG;AACtC,UAAMA,OAAM,OAAO,KAAK;AACxB,QAAIA,KAAI,WAAW,GAAG,EAAG,QAAO;AAChC,QAAI,kBAAkB,KAAKA,IAAG,EAAG,QAAO;AAIxC,QAAI,sBAAsB,IAAI,SAAS,MAAM,qBAAqB,KAAKA,IAAG,KAAK,kBAAkB,KAAKA,IAAG,KAAK,4BAA4BA,IAAG,GAAI,QAAO;AACxJ,YAAQ,KAAK,gBAAgB,YAAY,oEAA+D,KAAK;AAC7G,WAAO;EACX;AAEA,SAAO;AACX;AAGO,SAAS,WAAW,OAA4B;AACnD,QAAM,YAAiC,CAAC;AAGxC,aAAW,UAAU,OAAO,KAAK,KAAK,GAAG;AAMrC,UAAM,MAAM,qBAAqB,MAAM;AACvC,QAAI,eAAe,IAAI,GAAG,EAAG;AAC7B,QAAI,QAAQ,QAAS;AAErB,QAAI,QAAQ,MAAM,MAAM;AAExB,QAAI,oBAAoB,IAAI,GAAG,KAAK,MAAM,QAAQ,KAAK,GAAG;AACtD,gBAAU,GAAG,IAAI,OAAO,KAAK;IACjC,WACI,QAAQ,eAAe,UAAU,QAAQ,OAAO,UAAU,YAC1D,CAAC,MAAM,QAAQ,KAAK,KAAK,CAAC,MAAM,WAClC;AAIE,YAAM,QAAQ,MAAM,SAAS,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ;AAC7E,gBAAU,cAAc,IAAI,sBAAsB,OAAO,EAAE,WAAW,MAAM,CAAC;IACjF,WAAW,sBAAsB,IAAI,GAAG,GAAG;AACvC,UAAI,MAAM,QAAQ,KAAK,GAAG;AACtB,YAAI,QAAQ,YAAa,SAAQ,MAAM,IAAI,CAAC,MAAc,IAAI,IAAI;AAClE,gBAAQ,MAAM,KAAK,GAAG;MAC1B;AACA,UAAI,QAAQ,SAAU,SAAQ,QAAQ;AACtC,gBAAU,cAAc,IAAI,MAAM,MAAM,QAAQ;IACpD,WAAW,MAAM,QAAQ,KAAK,GAAG;AAK7B,gBAAU,GAAG,IAAI,MAAM,KAAK,GAAG;IACnC,WAAW,UAAU,UAAa,UAAU,MAAM;AAC9C,gBAAU,GAAG,IAAI,OAAO,KAAK;IACjC;EACJ;AAEA,SAAO;AACX;AC7KA,SAAS,eAAe,IAAuC;AAC3D,QAAM,IAAI,cAAc,EAAE;AAC1B,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,MAAM,QAAQ,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,EAAE,CAAC,MAAM,YAAY,OAAO,EAAE,CAAC,MAAM,UAAU;AAE3F,WAAO,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;EACtB;AACA,QAAM,KAAM,EAAuB;AACnC,MAAI,MAAM,QAAQ,EAAE,KAAK,GAAG,UAAU,EAAG,QAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;AAC7D,SAAO;AACX;AAEA,SAAS,UAAU,IAA2B;AAC1C,SAAO,aAAa,EAAE;AAC1B;AAEA,SAAS,YAAY,IAAuC;AACxD,SAAO,eAAe,EAAE;AAC5B;AAUO,SAAS,qBAAqB,MAAoC;AACrE,QAAM,MAAwC,KAAK;AACnD,MAAI,CAAC,MAAM,QAAQ,GAAG,EAAG,QAAO;AAChC,MAAI,KAAK,WAAY,QAAO;AAC5B,aAAW,MAAM,KAAK;AAClB,QAAI,kBAAkB,EAAE,KAAK,mBAAmB,EAAE,EAAG,QAAO;EAChE;AACA,SAAO;AACX;AAuBA,IAAM,gBAAgB,oBAAI,QAAuE;AAEjG,SAAS,gBACL,QACA,QACA,SACA,SACsB;AACtB,MAAI,SAAS,cAAc,IAAI,MAAM;AACrC,QAAM,WAAW,UAAA,OAAA,SAAA,OAAQ,IAAI,MAAA;AAC7B,MAAI,SAAU,QAAO;AACrB,QAAM,KAAK,mBAAmB,MAAM;AACpC,QAAM,KAAK,kBAAkB,MAAM;AACnC,QAAM,KAAa,CAAC,QAAQ,CAAC,KAAK,KAAK,GAAG,CAAC,IAAI,IAAI,QAAQ,CAAC,KAAK,KAAK,GAAG,CAAC,IAAI,EAAE;AAChF,QAAM,KAAa,CAAC,QAAQ,CAAC,KAAK,KAAK,GAAG,CAAC,IAAI,IAAI,QAAQ,CAAC,KAAK,KAAK,GAAG,CAAC,IAAI,EAAE;AAChF,QAAM,MAAM,sBAAsB,SAAS,IAAI,IAAI,OAAO;AAC1D,QAAM,QAAgC;IAClC,IAAI;IAAS;IAAI;IAAI,IAAI;IACzB;IACA,UAAU,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;EACtC;AACA,MAAI,CAAC,QAAQ;AAAE,aAAS,oBAAI,QAA+C;AAAG,kBAAc,IAAI,QAAQ,MAAM;EAAG;AACjH,SAAO,IAAI,QAAQ,KAAK;AACxB,SAAO;AACX;AAiCO,SAAS,0BACZ,QACA,QACA,SACA,SACA,eACA,YACgB;AAChB,QAAM,MAAM,gBAAgB,QAAQ,QAAQ,SAAS,OAAO;AAC5D,QAAM,IAAI,IAAI,aAAa,IACrB,gBACA,iBAAiB,IAAI,KAAK,aAAa;AAC7C,QAAM,QAAQ,iBAAiB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC;AAChE,MAAI,CAAC,WAAY,QAAO,EAAE,WAAW,MAAM;AAC3C,QAAM,MAAM,sBAAsB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC;AACnE,QAAM,YAAY,KAAK,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,MAAM,KAAK;AAC1D,SAAO,EAAE,WAAW,OAAO,UAAU;AACzC;AAEA,SAAS,iBAAiB,KAAmB,SAAyB;AAClE,QAAM,QAAQ,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;AAEtC,QAAM,SAAS,UAAU;AACzB,QAAM,EAAE,IAAI,GAAG,IAAI;AACnB,QAAM,OAAO,GAAG,SAAS;AACzB,MAAI,UAAU,EAAU,QAAO,GAAG,CAAC;AACnC,MAAI,UAAU,GAAG,IAAI,EAAG,QAAO,GAAG,IAAI;AACtC,MAAI,KAAK,GAAG,KAAK;AACjB,SAAO,KAAK,IAAI;AACZ,UAAM,MAAO,KAAK,OAAQ;AAC1B,QAAI,GAAG,GAAG,IAAI,OAAQ,MAAK,MAAM;QACX,MAAK;EAC/B;AACA,QAAM,QAAQ,GAAG,KAAK,CAAC;AACvB,QAAM,OAAQ,GAAG,EAAE,IAAI;AACvB,QAAM,OAAQ,OAAO,KAAK,SAAS,SAAS,OAAO;AACnD,SAAO,GAAG,KAAK,CAAC,IAAI,QAAQ,GAAG,EAAE,IAAI,GAAG,KAAK,CAAC;AAClD;AAoBA,IAAM,uBAAuB;AAC7B,IAAM,uBAAuB;AAC7B,IAAM,sBAAuB;AAiBtB,SAAS,gCACZ,MACA,MACmB;AAzOvB,MAAAH,KAAA,IAAA;AA0OI,MAAI,CAAC,qBAAqB,IAAI,EAAG,QAAO;AACxC,QAAM,MAAM,KAAK;AACjB,MAAI,CAAC,MAAM,QAAQ,GAAG,KAAK,IAAI,SAAS,EAAG,QAAO;AAElD,QAAM,aAAe,CAAC,CAAC,KAAK;AAC5B,QAAM,eAAeA,MAAA,QAAA,OAAA,SAAA,KAAM,sBAAN,OAAAA,MAA4B;AACjD,QAAM,eAAe,KAAA,QAAA,OAAA,SAAA,KAAM,sBAAN,OAAA,KAA4B;AACjD,QAAM,cAAe,KAAA,QAAA,OAAA,SAAA,KAAM,yBAAN,OAAA,KAA8B;AAEnD,QAAM,MAAmC,CAAC;AAK1C,QAAM,WAAW,eAAe,IAAI,CAAC,CAAC;AACtC,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,cAAc,aAAa,qBAAqB,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI;AACxE,MAAI,KAAK;IACL,UAAU,IAAI,CAAC,CAAC;IAChB,gBAAgB,gBAAgB,IAAI,CAAC,CAAC,GAAG,gBAAgB,IAAI,CAAC,CAAC,GAAG,GAAG,UAAU,aAAa,UAAU;EAC1G,CAAC;AAED,WAAS,IAAI,GAAG,IAAI,IAAI,SAAS,GAAG,KAAK;AACrC,UAAM,SAAS,IAAI,CAAC;AACpB,UAAM,SAAS,IAAI,IAAI,CAAC;AACxB,UAAM,UAAU,eAAe,MAAM;AACrC,UAAM,UAAU,eAAe,MAAM;AACrC,QAAI,CAAC,WAAW,CAAC,SAAS;AAEtB,UAAI,KAAK;QACL,UAAU,MAAM;QAChB,gBAAgB,gBAAgB,MAAM,GAAG,gBAAgB,MAAM,GAAG,GAAG,WAAA,OAAA,UAAW,CAAC,GAAG,CAAC,GAAG,QAAW,UAAU;MACjH,CAAC;AACD;IACJ;AAYA,QAAI,cAAc,IAAI,GAAG;AACrB,sCAAgC,KAAK,QAAQ,QAAQ,SAAS,SAAS,WAAW;IACtF;AAEA,uBAAmB,KAAK,QAAQ,QAAQ,SAAS,SAAS,YAAY,aAAa,aAAa,UAAU;EAC9G;AAKA,QAAM,UAAU,YAAY,IAAI,IAAI,SAAS,CAAC,CAAC;AAC/C,MAAI,QAAS,KAAI,IAAI,SAAS,CAAC,EAAE,IAAI;AAMrC,MAAI,WAAY,2BAA0B,GAAG;AAG7C,QAAM,SAA8B,EAAE,WAAW,IAAI;AACrD,MAAI,KAAK,SAAS,OAAY,QAA8B,OAAO,KAAK;AACxE,SAAO;AACX;AAWO,SAAS,0BAA0B,KAAiC;AACvE,MAAI;AACJ,aAAW,MAAM,KAAK;AAClB,UAAM,IAAI,cAAc,EAAE;AAC1B,QAAI,CAAC,KAAK,OAAO,EAAE,WAAW,SAAU;AACxC,QAAI,SAAS,QAAW;AAAE,aAAO,EAAE;AAAQ;IAAU;AACrD,QAAI,IAAI,EAAE;AACV,WAAO,IAAI,OAAO,IAAM,MAAK;AAC7B,WAAO,IAAI,OAAO,KAAM,MAAK;AAC7B,MAAE,SAAS;AACX,WAAO;EACX;AACJ;AAGA,SAAS,UAAU,MAAc,OAA+C;AAC5E,SAAO,EAAE,GAAG,MAAM,GAAG,MAAM;AAC/B;AAIA,SAAS,gBAAgB,IAAiD;AACtE,QAAM,IAAI,cAAc,EAAE;AAC1B,MAAI,CAAC,KAAK,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,EAAG,QAAO;AAC5D,SAAO;AACX;AAKA,SAAS,gBAAgB,MAAe,MAAe,GAAoB;AACvE,MAAI,SAAS,OAAW,QAAO;AAC/B,MAAI,SAAS,OAAW,QAAO;AAC/B,MAAI,OAAO,SAAS,YAAY,OAAO,SAAS,UAAU;AACtD,WAAO,QAAQ,OAAO,QAAQ;EAClC;AACA,MAAI,MAAM,QAAQ,IAAI,KAAK,MAAM,QAAQ,IAAI,KAAK,KAAK,WAAW,KAAK,QAAQ;AAC3E,UAAM,MAAqB,IAAI,MAAM,KAAK,MAAM;AAChD,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AAClC,YAAM,IAAI,OAAO,KAAK,CAAC,MAAM,WAAW,KAAK,CAAC,IAAI;AAClD,YAAM,IAAI,OAAO,KAAK,CAAC,MAAM,WAAW,KAAK,CAAC,IAAI;AAClD,UAAI,CAAC,IAAI,KAAK,IAAI,KAAK;IAC3B;AACA,WAAO;EACX;AACA,SAAO,IAAI,MAAM,OAAO;AAC5B;AAUA,SAAS,gBACL,OACA,OACA,GACA,WACA,yBACA,YACgB;AAChB,QAAM,QAAkC,EAAE,UAAU;AACpD,QAAM,OAAO,oBAAI,IAAY;AAC7B,MAAI,MAAO,YAAW,KAAK,OAAO,KAAK,KAAK,EAAG,MAAK,IAAI,CAAC;AACzD,MAAI,MAAO,YAAW,KAAK,OAAO,KAAK,KAAK,EAAG,MAAK,IAAI,CAAC;AACzD,aAAW,KAAK,MAAM;AAClB,QAAI,MAAM,YAAa;AACvB,QAAI,MAAM,YAAY,WAAY;AAClC,UAAM,KAAM,SAAA,OAAA,SAAA,MAAgD,CAAA;AAC5D,UAAM,KAAM,SAAA,OAAA,SAAA,MAAgD,CAAA;AAC5D,QAAI,OAAO,UAAa,OAAO,OAAW;AAC1C,UAAM,CAAC,IAAI,gBAAgB,IAAI,IAAI,CAAC;EACxC;AACA,MAAI,4BAA4B,QAAW;AAGvC,UAAM,SAAS,0BAA0B,iBAAiB,OAAO,OAAO,CAAC;EAC7E;AACA,SAAO;AACX;AAIA,SAAS,iBACL,OACA,OACA,GACM;AACN,QAAM,KAAK,QAAO,SAAA,OAAA,SAAA,MAAO,YAAW,WAAW,MAAM,SAAS;AAC9D,QAAM,KAAK,QAAO,SAAA,OAAA,SAAA,MAAO,YAAW,WAAW,MAAM,SAAS;AAC9D,MAAI,OAAO,UAAa,OAAO,OAAW,QAAO;AACjD,QAAM,IAAI,gBAAgB,IAAI,IAAI,CAAC;AACnC,SAAO,OAAO,MAAM,WAAW,IAAI;AACvC;AAMA,SAAS,qBAAqB,KAAoB,KAA4B;AAC1E,QAAM,KAAK,eAAe,GAAG;AAC7B,QAAM,KAAK,eAAe,GAAG;AAC7B,MAAI,CAAC,MAAM,CAAC,GAAI,QAAO;AACvB,QAAM,MAAM,gBAAgB,KAAK,KAAK,IAAI,EAAE;AAC5C,QAAM,MAAM,sBAAsB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC;AACnE,SAAO,KAAK,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,MAAM,KAAK;AACnD;AAIA,SAAS,kBAAkB,GAAW,GAAmB;AACrD,MAAI,IAAI,IAAI;AACZ,SAAO,IAAI,IAAM,MAAK;AACtB,SAAO,IAAI,KAAM,MAAK;AACtB,SAAO;AACX;AAiBA,SAAS,gCACL,KACA,QAAuB,QACvB,SAAiB,SACjB,aACI;AACJ,QAAM,SAAS,IAAI,IAAI,SAAS,CAAC;AACjC,QAAM,QAAQ,cAAc,MAAM;AAClC,QAAM,WAAW,SAAA,OAAA,SAAA,MAAO;AACxB,MAAI,OAAO,aAAa,SAAU;AAKlC,QAAM,YAAY,gBAAgB,MAAM;AACxC,QAAM,kBAAkB,WAAW,iBAAiB,WAAW,WAAW,CAAC;AAE3E,QAAM,MAAM,gBAAgB,QAAQ,QAAQ,SAAS,OAAO;AAC5D,QAAM,aAAa,sBAAsB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC;AAC1E,QAAM,YAAY,KAAK,MAAM,WAAW,CAAC,GAAG,WAAW,CAAC,CAAC,IAAI,MAAM,KAAK;AACxE,QAAM,QAAQ,kBAAkB,WAAW,eAAe;AAC1D,MAAI,KAAK,IAAI,KAAK,KAAK,YAAa;AASpC,QAAM,WAAW,UAAU,MAAM;AACjC,QAAM,WAAW,KAAK,IAAI,WAAW,gCAAgC,WAAW,UAAU,MAAM,KAAK,CAAC;AACtG,QAAM,WAAW;IACb,gBAAgB,MAAM;IAAG,gBAAgB,MAAM;IAAG;IAClD;IAAS;IAAW;EACxB;AACA,MAAI,KAAK,UAAU,UAAU,QAAQ,CAAC;AAC1C;AAKA,IAAM,gCAAgC;AAMtC,SAAS,mBACL,KACA,QAAoB,QACpB,SAAiB,SACjB,YACA,aAAqB,aAAqB,YACtC;AACJ,QAAM,MAAM,gBAAgB,QAAQ,QAAQ,SAAS,OAAO;AAC5D,QAAM,WAAW,UAAU,MAAM;AACjC,QAAM,WAAW,UAAU,MAAM;AACjC,QAAM,aAAa,YAAY,MAAM;AACrC,QAAM,WAAW,aAAa,UAAU;AACxC,QAAM,QAAQ,gBAAgB,MAAM;AACpC,QAAM,QAAQ,gBAAgB,MAAM;AAGpC,QAAM,aAAa,gBAAgB,KAAK,YAAY,aAAa,aAAa,UAAU;AAOxF,QAAM,UAAyB,CAAC;AAChC,aAAW,KAAK,YAAY;AACxB,UAAM,MAAM,gBAAgB,IAAI,KAAK,CAAC;AACtC,UAAM,IAAI,MAAM,IAAI,WAAW,IAAI,MAAM,IAAI,WAAW,GAAG,GAAG,CAAC;AAC/D,UAAM,IAAI,MAAM,SAAS,CAAC,GAAG,GAAG,CAAC;AACjC,UAAM,MAAM,iBAAiB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC;AAC9D,UAAM,SAAiB,EAAE,GAAG,GAAG,IAAI;AACnC,QAAI,YAAY;AACZ,YAAM,MAAM,sBAAsB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC;AACnE,aAAO,YAAY,KAAK,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,MAAM,KAAK;IAC/D;AACA,YAAQ,KAAK,MAAM;EACvB;AAKA,MAAI,YAAY;AAChB,MAAI,QAAQ;AACZ,QAAM,WAAW,IAAI,SAAS;AAC9B,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACrC,UAAM,IAAI,QAAQ,CAAC;AACnB,UAAM,QAAQ,QAAQ,IAAI,OAAO,EAAE,IAAI,UAAU,IAAI,QAAQ,GAAG,CAAC,IAAI;AACrE,UAAM,EAAE,MAAM,MAAM,IAAI,YAAY,WAAW,KAAK;AAKpD,UAAM,WAAW,MAAM,IAAI,WAAW,IAAI,SAAS;AACnD,QAAI,KAAM,KAAI,QAAQ,EAAE,IAAI;QACvB,QAAO,IAAI,QAAQ,EAAE;AAE1B,UAAM,UAAU,WAAW,EAAE,KAAK,WAAW;AAC7C,UAAM,QAAQ,gBAAgB,OAAO,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,WAAW,UAAU;AAC/E,QAAI,KAAK,UAAU,SAAS,KAAK,CAAC;AAElC,gBAAY;AACZ,YAAQ,EAAE;EACd;AACJ;AAQA,SAAS,gBACL,KAA6B,YAC7B,aAAqB,aAAqB,YAC7B;AAEb,QAAM,WAA0B,CAAC;AACjC,kBAAgB,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,QAAQ;AACpE,kBAAgB,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,QAAQ;AACpE,WAAS,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAE7B,QAAM,WAA0B,CAAC,CAAC;AAClC,aAAW,KAAK,UAAU;AACtB,QAAI,IAAI,SAAS,SAAS,SAAS,CAAC,IAAI,QAAQ,IAAI,IAAI,MAAM;AAC1D,eAAS,KAAK,CAAC;IACnB;EACJ;AACA,WAAS,KAAK,CAAC;AAEf,QAAM,MAAqB,CAAC;AAC5B,QAAM,SAAS,EAAE,WAAW,aAAa,SAAS,OAAO;AACzD,WAAS,IAAI,GAAG,IAAI,SAAS,SAAS,GAAG,KAAK;AAC1C,WAAO,SAAS,CAAC,GAAG,SAAS,IAAI,CAAC,GAAG,KAAK,KAAK,YAAY,aAAa,aAAa,MAAM;EAC/F;AACA,SAAO;AACX;AAOA,SAAS,gBAAgB,IAAY,IAAY,IAAY,IAAY,KAA0B;AAC/F,QAAM,IAAI,KAAK;AACf,QAAM,IAAI,KAAK;AACf,QAAM,IAAI,KAAK;AACf,QAAM,IAAI,IAAI,IAAI,IAAI;AACtB,QAAM,IAAI,KAAK,IAAI;AACnB,QAAM,IAAI;AACV,MAAI,KAAK,IAAI,CAAC,IAAI,OAAO;AACrB,QAAI,KAAK,IAAI,CAAC,IAAI,OAAO;AACrB,YAAM,IAAI,CAAC,IAAI;AACf,UAAI,IAAI,QAAQ,IAAI,IAAI,KAAM,KAAI,KAAK,CAAC;IAC5C;AACA;EACJ;AACA,QAAM,OAAO,IAAI,IAAI,IAAI,IAAI;AAC7B,MAAI,OAAO,EAAG;AACd,QAAM,KAAK,KAAK,KAAK,IAAI;AACzB,QAAM,MAAM,CAAC,IAAI,OAAO,IAAI;AAC5B,QAAM,MAAM,CAAC,IAAI,OAAO,IAAI;AAC5B,MAAI,KAAK,QAAQ,KAAK,IAAI,KAAM,KAAI,KAAK,EAAE;AAC3C,MAAI,KAAK,QAAQ,KAAK,IAAI,KAAM,KAAI,KAAK,EAAE;AAC/C;AAWA,SAAS,OACL,IAAY,IACZ,KACA,KACA,YACA,aAAqB,aACrB,QACI;AACJ,QAAM,QAAQ,KAAK,MAAM;AACzB,QAAM,KAAK,iBAAiB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAC9D,QAAM,KAAK,iBAAiB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAC9D,QAAM,OAAO,KAAK;AAClB,QAAM,MAAM,iBAAiB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK,OAAO,IAAI;AAC7E,QAAM,MAAM,iBAAiB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI;AACjE,QAAM,MAAM,iBAAiB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK,OAAO,IAAI;AAE7E,QAAM,MAAM,KAAK;IACb,SAAS,KAAK,IAAI,EAAE;IACpB,SAAS,KAAK,IAAI,EAAE;IACpB,SAAS,KAAK,IAAI,EAAE;EACxB;AACA,MAAI,QAAQ;AACZ,MAAI,YAAY;AACZ,UAAM,OAAO,sBAAsB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AACrE,UAAM,OAAO,sBAAsB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AACrE,UAAM,OAAO,KAAK,MAAM,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,MAAM,KAAK;AACvD,UAAM,OAAO,KAAK,MAAM,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,MAAM,KAAK;AACvD,QAAI,QAAQ,KAAK,IAAI,OAAO,IAAI;AAChC,QAAI,QAAQ,IAAK,SAAQ,MAAM;AAC/B,QAAI,QAAQ,YAAa,SAAQ;EACrC;AAEA,MAAK,OAAO,eAAe,SAAU,OAAO,aAAa,KAAK,OAAO,MAAM;AACvE,QAAI,KAAK,EAAE;AACX;EACJ;AAEA,SAAO,aAAa;AACpB,SAAO,IAAI,MAAM,KAAK,KAAK,YAAY,aAAa,aAAa,MAAM;AACvE,SAAO,MAAM,IAAI,KAAK,KAAK,YAAY,aAAa,aAAa,MAAM;AAC3E;AAGA,SAAS,SAAS,GAAW,IAAY,IAAoB;AACzD,QAAM,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC;AACvB,QAAM,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC;AACvB,QAAM,OAAO,KAAK,KAAK,KAAK;AAC5B,MAAI,OAAO,OAAO;AACd,UAAM,MAAM,EAAE,CAAC,IAAI,GAAG,CAAC;AACvB,UAAM,MAAM,EAAE,CAAC,IAAI,GAAG,CAAC;AACvB,WAAO,KAAK,KAAK,MAAM,MAAM,MAAM,GAAG;EAC1C;AACA,QAAM,SAAS,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK,MAAM,EAAE,CAAC,IAAI,GAAG,CAAC,KAAK;AACrD,SAAO,KAAK,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI;AAC3C;AAYO,SAAS,6BACZ,MACA,MACM;AACN,QAAM,MAAM,mBAAmB,MAAM,IAAI;AACzC,SAAO,OAAA,OAAA,MAAO;AAClB;AAEA,SAAS,mBAAmB,MAAc,MAAwD;AAC9F,MAAI;AACJ,MAAI,KAAK,UAAU;AACf,aAAS,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK;AAC3C,YAAM,KAAK,KAAK,SAAS,CAAC;AAC1B,YAAM,MAAM,mBAAmB,IAAI,IAAI;AACvC,UAAI,QAAQ,MAAM;AACd,YAAI,CAAC,YAAa,eAAc,KAAK,SAAS,MAAM;AACpD,oBAAY,CAAC,IAAI;MACrB;IACJ;EACJ;AAEA,MAAI;AACJ,QAAM,aAAa,KAAK;AACxB,MAAI,cAAc,OAAO,eAAe,YAAY,CAAC,MAAM,QAAQ,UAAU,GAAG;AAC5E,UAAM,UAAU;AAChB,UAAM,gBAAgB,QAAQ;AAC9B,QAAI,iBAAiB,OAAO,kBAAkB,YAAY,qBAAqB,aAAa,GAAG;AAC3F,YAAM,eAAe,gCAAgC,eAAe,IAAI;AACxE,UAAI,iBAAiB,eAAe;AAChC,qBAAaC,eAAAC,gBAAA,CAAA,GAAK,OAAA,GAAL,EAAc,WAAW,aAAa,CAAA;MACvD;IACJ;EACJ;AAEA,MAAI,CAAC,eAAe,CAAC,WAAY,QAAO;AACxC,QAAM,SAAiBA,gBAAA,CAAA,GAAK,IAAA;AAC5B,MAAI,YAAa,QAAO,WAAW;AACnC,MAAI,WAAa,QAAO,UAAW;AACnC,SAAO;AACX;ACnsBO,IAAM,wBAAwB;AAGrC,SAAS,eAAe,GAAY,GAAqB;AACrD,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,OAAO,MAAM,OAAO,KAAK,MAAM,QAAQ,MAAM,QAAQ,OAAO,MAAM,SAAU,QAAO;AACvF,MAAI,MAAM,QAAQ,CAAC,MAAM,MAAM,QAAQ,CAAC,EAAG,QAAO;AAClD,QAAM,KAAK,OAAO,KAAK,CAAW;AAClC,QAAM,KAAK,OAAO,KAAK,CAAW;AAClC,MAAI,GAAG,WAAW,GAAG,OAAQ,QAAO;AACpC,SAAO,GAAG,MAAM,CAAA,MAAK,eAAgB,EAA8B,CAAC,GAAI,EAA8B,CAAC,CAAC,CAAC;AAC7G;AAiBA,SAAS,kBAAkB,GAA+B;AACtD,QAAM,SAAS,EAAE,MAAM,qBAAqB,EAAE,IAAI,CAAA,MAAK,EAAE,KAAK,CAAC,EAAE,OAAO,CAAA,MAAK,KAAK,MAAM,GAAG;AAE3F,QAAM,WAA+B,CAAC;AACtC,MAAI,iBAAqC;AAEzC,aAAW,SAAS,QAAQ;AACxB,QAAI,aAAa,KAAK,KAAK,GAAG;AAC1B,uBAAiB,EAAE,MAAM,OAAO,QAAQ,CAAC,EAAE;AAC3C,eAAS,KAAK,cAAc;IAChC,WAAW,gBAAgB;AACvB,YAAM,QAAQ,CAAC;AACf,qBAAe,OAAO,KAAK,OAAO,MAAM,KAAK,IAAI,IAAI,KAAK;IAC9D;EACJ;AAEA,SAAO;AACX;AAMO,SAAS,qBAAqB,GAAgC;AACjE,QAAM,MAA2B,CAAC;AAClC,MAAI;AAEJ,QAAM,WAAW,kBAAkB,CAAC;AAEpC,aAAW,WAAW,UAAU;AAC5B,UAAM,OAAO,QAAQ;AACrB,UAAM,SAAS,QAAQ;AAEvB,QAAI,SAAS,OAAO,SAAS,KAAK;AAC9B,YAAM,IAAI,OAAO,CAAC,KAAK;AACvB,YAAM,IAAI,OAAO,CAAC,KAAK;AACvB,oBAAc;QACV,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QACV,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QACV,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QACV,GAAG;MACP;AACA,UAAI,KAAK,WAAW;AACpB;IACJ;AAGA,QAAI,CAAC,aAAa;AACd,oBAAc;QACV,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QACV,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QACV,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QACV,GAAG;MACP;AACA,UAAI,KAAK,WAAW;IACxB;AAEA,QAAI,SAAS,KAAK;AACd,YAAM,IAAI,OAAO,CAAC,KAAK;AACvB,YAAM,IAAI,OAAO,CAAC,KAAK;AACvB,kBAAY,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;AACzB,kBAAY,EAAG,KAAK,CAAC,GAAG,CAAC,CAAC;AAC1B,kBAAY,EAAG,KAAK,CAAC,GAAG,CAAC,CAAC;IAE9B,WAAW,SAAS,KAAK;AACrB,YAAM,OAAO,OAAO,CAAC,KAAK;AAC1B,YAAM,OAAO,OAAO,CAAC,KAAK;AAC1B,YAAM,OAAO,OAAO,CAAC,KAAK;AAC1B,YAAM,OAAO,OAAO,CAAC,KAAK;AAC1B,YAAM,KAAK,OAAO,CAAC,KAAK;AACxB,YAAM,KAAK,OAAO,CAAC,KAAK;AAGxB,kBAAY,EAAG,YAAY,EAAG,SAAS,CAAC,IAAI,CAAC,MAAM,IAAI;AAGvD,kBAAY,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;AAC3B,kBAAY,EAAG,KAAK,CAAC,MAAM,IAAI,CAAC;AAChC,kBAAY,EAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAEhC,WAAW,SAAS,OAAO,SAAS,KAAK;AACrC,kBAAY,IAAI;IAEpB,OAAO;AACH,cAAQ,KAAK,+BAA+B,OAAO,GAAG;IAC1D;EACJ;AAEA,SAAO;AACX;AAOA,SAAS,gBAAgBC,MAAiC;AACtD,MAAIA,KAAI,WAAW,OAAO,KAAKA,KAAI,SAAS,GAAG,GAAG;AAC9C,WAAOA,KAAI,MAAM,GAAG,EAAE;EAC1B;AAEA,MAAI,0BAA0B,KAAKA,IAAG,GAAG;AACrC,WAAOA;EACX;AACA,SAAO;AACX;AAKA,SAAS,aAAa,OAA6B;AAC/C,SAAO,OAAO,UAAU,YAAY,gBAAgB,KAAK,MAAM;AACnE;AAcA,SAAS,mBAAmB,OAAkD;AAG1E,MAAI,SAAS,OAAO,UAAU,YAAY,OAAO,MAAM,aAAa,UAAU;AAC1E,UAAM,IAAI,gBAAgB,MAAM,QAAQ;AACxC,WAAO,IAAI,EAAE,OAAO,qBAAqB,CAAC,EAAE,IAAI;EACpD;AAGA,MAAI,SAAS,OAAO,UAAU,YAAY,WAAW,OAAO;AACxD,UAAM,aAAa,MAAM;AACzB,QAAI,MAAM,QAAQ,UAAU,KAAK,WAAW,SAAS,GAAG;AAEpD,UAAI,aAAa,WAAW,CAAC,CAAC,GAAG;AAC7B,cAAM,QAA6B,CAAC;AACpC,mBAAWC,YAAW,YAAY;AAC9B,gBAAM,IAAI,gBAAgBA,QAAO;AACjC,cAAI,GAAG;AACH,kBAAM,KAAK,GAAG,qBAAqB,CAAC,CAAC;UACzC;QACJ;AACA,eAAO,EAAE,MAAM;MACnB;IACJ;AAEA,WAAO;EACX;AAGA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACtB,QAAI,MAAM,SAAS,KAAK,aAAa,MAAM,CAAC,CAAC,GAAG;AAC5C,YAAM,QAA6B,CAAC;AACpC,iBAAWA,YAAW,OAAO;AACzB,cAAM,IAAI,gBAAgBA,QAAO;AACjC,YAAI,GAAG;AACH,gBAAM,KAAK,GAAG,qBAAqB,CAAC,CAAC;QACzC;MACJ;AACA,aAAO,EAAE,MAAM;IACnB;AAEA,WAAO,EAAE,OAAO,MAAM;EAC1B;AAGA,MAAI,aAAa,KAAK,GAAG;AACrB,UAAM,IAAI,gBAAgB,KAAK;AAC/B,WAAO,EAAE,OAAO,qBAAqB,CAAC,EAAE;EAC5C;AAEA,SAAO;AACX;AAaA,SAAS,cACL,QACA,MAC4C;AAzPhD,MAAAJ;AA0PI,MAAI,CAAC,OAAQ,QAAO;AAEpB,MAAI,MAAM,QAAQ,MAAM,GAAG;AACvB,WAAO;EACX;AAGA,OAAIA,MAAA,QAAA,OAAA,SAAA,KAAM,YAAN,OAAA,SAAAA,IAAgB,MAAA,GAAS;AACzB,WAAO,KAAK,QAAQ,MAAM;EAC9B;AAGA,UAAQ,KAAK,0BAA0B,MAAM;AAC7C,SAAO;AACX;AAQA,SAAS,iBACL,SACA,MACiC;AAnRrC,MAAAA;AAoRI,MAAI,OAAO,YAAY,UAAU;AAE7B,UAAM,YAAWA,MAAA,QAAA,OAAA,SAAA,KAAM,eAAN,OAAA,SAAAA,IAAmB,OAAA;AACpC,QAAI,CAAC,UAAU;AACX,cAAQ,KAAK,6BAA6B,OAAO;IACrD;AACA,WAAO;EACX;AAGA,SAAO;AACX;AAQA,SAAS,wBACL,SACA,MACuB;AACvB,MAAI,CAAC,QAAS,QAAO,CAAC;AAEtB,QAAM,UAAmC,CAAC;AAE1C,MAAI,OAAO,YAAY,UAAU;AAC7B,UAAM,WAAW,iBAAiB,SAAS,IAAI;AAC/C,QAAI,SAAU,SAAQ,KAAK,QAAQ;EACvC,WAAW,MAAM,QAAQ,OAAO,GAAG;AAC/B,eAAW,QAAQ,SAAS;AACxB,YAAM,WAAW,iBAAiB,MAAM,IAAI;AAC5C,UAAI,SAAU,SAAQ,KAAK,QAAQ;IACvC;EACJ,OAAO;AAEH,YAAQ,KAAK,OAAO;EACxB;AAEA,SAAO;AACX;AAgBO,SAAS,iBAAiB,UAAkB,GAAQ,GAAQ,GAAgB;AA7UnF,MAAAA,KAAA;AA8UI,MAAI,aAAa,KAAK;AAClB,UAAM,UAASA,MAAA,KAAA,OAAA,SAAA,EAAG,UAAH,OAAAA,MAAa,MAAM,QAAQ,CAAC,IAAI,IAAI,CAAC;AACpD,UAAM,UAAS,KAAA,KAAA,OAAA,SAAA,EAAG,UAAH,OAAA,KAAa,MAAM,QAAQ,CAAC,IAAI,IAAI,CAAC;AACpD,WAAO,EAAE,OAAO,mBAAmB,QAAQ,QAAQ,CAAC,EAAE;EAC1D;AACA,MAAI,oBAAoB,IAAI,QAAQ,GAAG;AACnC,WAAO,iBAAiB,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;EACnE;AAKA,MAAI,aAAa,eACV,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,MAAM,QAAQ,CAAC,KACvD,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,MAAM,QAAQ,CAAC,GAC5D;AACE,WAAO,0BAA0B,GAAuB,GAAuB,CAAC;EACpF;AAKA,MAAI,aAAa,YAAY,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU;AACzE,WAAO,eAAe,GAAG,GAAG,CAAC;EACjC;AACA,MAAI,sBAAsB,IAAI,QAAQ,KAAK,aAAa,sBAAsB,aAAa,mBAAmB;AAC1G,WAAO,eAAe,KAAK,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC;EAC7C;AACA,SAAO,eAAe,EAAE,KAAK,IAAI,EAAE,KAAK,IAAI,CAAC;AACjD;AAIA,SAAS,0BAA0B,GAAqB,GAAqB,GAA6B;AACtG,QAAM,OAAO,oBAAI,IAAY,CAAC,GAAG,OAAO,KAAK,KAAA,OAAA,IAAK,CAAC,CAAC,GAAG,GAAG,OAAO,KAAK,KAAA,OAAA,IAAK,CAAC,CAAC,CAAC,CAAC;AAC/E,QAAM,MAAgC,CAAC;AACvC,aAAW,KAAK,MAAM;AAClB,UAAM,KAAM,KAAA,OAAA,SAAA,EAAiC,CAAA;AAC7C,UAAM,KAAM,KAAA,OAAA,SAAA,EAAiC,CAAA;AAC7C,QAAI,MAAM,YAAY,MAAM,QAAQ;AAChC,UAAI,CAAC,IAAI,eAAe,EAAE,MAAA,OAAA,KAAM,IAAI,EAAE,MAAA,OAAA,KAAM,IAAI,CAAC;IACrD,WAAW,MAAM,eAAe,MAAM,WAAW,MAAM,UAAU;AAC7D,YAAM,WAA0B,MAAM,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;AAC9D,UAAI,CAAC,IAAI,eAAgB,MAAwB,UAAW,MAAwB,UAAU,CAAC;IACnG,OAAO;AAEH,UAAI,CAAC,IAAI,MAAA,OAAA,KAAM;IACnB;EACJ;AACA,SAAO;AACX;AAgBA,SAAS,oBACL,UACA,WACA,MACA,UACsB;AArZ1B,MAAAA,KAAA,IAAA,IAAA,IAAA;AAsZI,QAAM,iBAAiB,UAAU,SAAS;AAC1C,QAAM,WAAW,OAAMA,MAAA,KAAK,iBAAL,OAAAA,MAAqB,gBAAgB,GAAG,cAAc;AAG7E,MAAI;AACJ,MAAI,KAAK,aAAa,eAAe,OAAO;AACxC,aAAS,UAAU,MAAM,GAAG,WAAW,CAAC;EAC5C,OAAO;AACH,aAAS,UAAU,MAAM,iBAAiB,QAAQ;EACtD;AAGA,QAAM,UAAS,KAAA,UAAU,CAAC,EAAE,MAAb,OAAA,KAAkB;AACjC,QAAM,SAAQ,KAAA,UAAU,UAAU,SAAS,CAAC,EAAE,MAAhC,OAAA,KAAqC;AAEnD,MAAI,WAAmB;AACvB,MAAI,KAAK,aAAa,eAAe,OAAO;AACxC,gBAAY;AACZ,cAAU;EACd,OAAO;AACH,gBAAY;AACZ,cAAU;EACd;AAEA,QAAM,eAAe,UAAU;AAC/B,MAAI,gBAAgB,EAAG,QAAO;AAG9B,QAAM,aAAY,KAAA,OAAO,CAAC,EAAE,MAAV,OAAA,KAAe;AACjC,QAAM,WAAU,KAAA,OAAO,OAAO,SAAS,CAAC,EAAE,MAA1B,OAAA,KAA+B;AAC/C,QAAM,cAAc,UAAU;AAC9B,MAAI,eAAe,EAAG,QAAO;AAG7B,QAAM,WAAgC,OAAO,IAAI,CAAA,QAAO;IACpD,OAAO,GAAG,IAAK,aAAa;IAC5B,GAAG,GAAG;IACN,GAAG,GAAG;IACN,WAAW,kBAAkB,EAAE;IAC/B,YAAY,mBAAmB,EAAE;EACrC,EAAE;AAEF,QAAM,WAAW,KAAK,MAAM,eAAe,WAAW;AACtD,QAAM,YAAY,eAAe,WAAW;AAC5C,QAAM,kBAAkB,YAAY;AAEpC,QAAM,SAAiC,CAAC;AAgBxC,QAAM,mBAAmB,KAAK,aAAa,eAAe;AAG1D,QAAM,qBAAuD,UAAU,UAAU,SAAS,CAAC;AAQ3F,MAAI;AACJ,MAAI,4BAA4B;AAGhC,WAAS,UAAU,UAAkB,YAAqB,SAAkB;AAnehF,QAAAA;AAoeQ,QAAI;AACJ,QAAI,YAAY;AAEZ,gBAAU,CAAC;AACX,eAAS,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3C,gBAAQ,KAAK;UACT,MAAM,IAAI,SAAS,CAAC,EAAE;UACtB,GAAG,SAAS,CAAC,EAAE;;UAEf,GAAG,IAAI,IAAI,cAAc,SAAS,IAAI,CAAC,EAAE,CAAC,IAAI;;;;UAI9C,WAAW,SAAS,CAAC,EAAE;UACvB,YAAY,SAAS,CAAC,EAAE;QAC5B,CAAC;MACL;IACJ,OAAO;AACH,gBAAU;IACd;AAEA,UAAM,UAAU,YAAY,SAAY,UAAU;AAElD,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACrC,YAAM,QAAQ,QAAQ,CAAC;AACvB,UAAI,MAAM,OAAO,UAAU,MAAM;AAE7B,cAAM,OAAO,QAAQ,IAAI,CAAC;AAC1B,cAAM,eAAe,MAAM,OAAO,KAAK;AACvC,cAAM,aAAa,UAAU,KAAK,QAAQ;AAG1C,cAAM,YAAY,KAAK,IAAI,YAAY,KAAK,CAAC,EAAE,SAAS,IAAI;AAC5D,cAAM,WAAW,iBAAiB,UAAU,KAAK,GAAG,MAAM,GAAG,SAAS;AAGtE,cAAM,EAAE,MAAM,WAAW,IAAI,YAAY,KAAK,GAAG,SAAS;AAG1D,YAAI,OAAO,SAAS,KAAK,KAAK,QAAQ,SAAS;AAC3C,iBAAO,OAAO,SAAS,CAAC,EAAE,IAAI;QAClC;AAEA,eAAO,KAAK,EAAE,GAAG,WAAW,UAAU,aAAa,GAAG,UAAU,GAAG,OAAU,CAAC;AAC9E;MACJ;AAKA,YAAM,SAAS,OAAO,SAAS,IAAI,OAAO,OAAO,SAAS,CAAC,IAAI;AAC/D,YAAM,aAAa,oBAAoB,MAAM,KAAK,WAAW,UACtD,KAAK,MAAKA,MAAA,OAAO,MAAP,OAAAA,MAAY,MAAM,WAAW,MAAM,OAAO,YAAY,IAAI;AAC3E,UAAI,YAAY;AACZ,YAAI,eAAe,OAAO,GAAG,MAAM,CAAC,GAAG;AAInC,cAAI,OAAO,SAAS,GAAG;AACnB,mBAAO,IAAI,MAAM;AACjB,mBAAO,aAAa,MAAM;UAC9B,OAAO;AACH,qCAAyB,MAAM;AAC/B,wCAA4B;UAChC;AACA;QACJ;AAGA,YAAI,OAAO,SAAS,GAAG;AAAE,iBAAO,OAAO;AAAW,iBAAO,OAAO;QAAY;MAChF;AAEA,YAAM,SAA+B;QACjC,GAAG,WAAW,MAAM,OAAO,eAAe,aAAa,wBAAwB;QAC/E,GAAG,MAAM;QACT,GAAG,IAAI,QAAQ,SAAS,IAAI,MAAM,IAAI;MAC1C;AAGA,UAAI,MAAM,UAAW,QAAO,YAAY,MAAM;AAC9C,UAAI,MAAM,WAAY,QAAO,aAAa,MAAM;AAChD,aAAO,KAAK,MAAM;IACtB;EACJ;AASA,WAAS,cAAc,UAAkB,YAAqB,cAAsB;AAChF,QAAI;AACJ,QAAI,YAAY;AACZ,gBAAU,CAAC;AACX,eAAS,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3C,gBAAQ,KAAK;UACT,MAAM,IAAI,SAAS,CAAC,EAAE;UACtB,GAAG,SAAS,CAAC,EAAE;UACf,GAAG,IAAI,IAAI,cAAc,SAAS,IAAI,CAAC,EAAE,CAAC,IAAI;UAC9C,WAAW,SAAS,CAAC,EAAE;UACvB,YAAY,SAAS,CAAC,EAAE;QAC5B,CAAC;MACL;IACJ,OAAO;AACH,gBAAU;IACd;AAEA,UAAM,YAAY,IAAI;AAEtB,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACrC,YAAM,QAAQ,QAAQ,CAAC;AACvB,UAAI,MAAM,OAAO,YAAY,KAAM;AACnC,YAAM,OAAO,QAAQ,IAAI,CAAC;AAK1B,UAAI,QAAQ,KAAK,OAAO,YAAY,QAAQ,MAAM,OAAO,YAAY,MAAM;AACvE,cAAM,eAAe,MAAM,OAAO,KAAK;AACvC,cAAM,aAAa,YAAY,KAAK,QAAQ;AAC5C,cAAM,YAAY,KAAK,IAAI,YAAY,KAAK,CAAC,EAAE,SAAS,IAAI;AAC5D,cAAM,aAAa,iBAAiB,UAAU,KAAK,GAAG,MAAM,GAAG,SAAS;AACxE,cAAM,EAAE,OAAO,YAAY,IAAI,YAAY,KAAK,GAAG,SAAS;AAC5D,eAAO,KAAK,EAAE,GAAG,UAAU,GAAG,YAAY,GAAG,YAAY,CAAC;MAC9D;AAEA,YAAM,SAA+B;QACjC,GAAG,YAAY,MAAM,OAAO,aAAa;QACzC,GAAG,MAAM;QACT,GAAG,IAAI,QAAQ,SAAS,IAAI,MAAM,IAAI;MAC1C;AACA,UAAI,MAAM,UAAW,QAAO,YAAY,MAAM;AAC9C,UAAI,MAAM,WAAY,QAAO,aAAa,MAAM;AAChD,aAAO,KAAK,MAAM;IACtB;EACJ;AAMA,MAAI,KAAK,aAAa,eAAe,OAAO;AAMxC,QAAI,kBAAkB,MAAM;AACxB,YAAM,aAAa,KAAK,cAAc,gBAAgB,aAAc,WAAW,MAAM;AACrF,oBAAc,WAAW,YAAY,eAAe;IACxD;AACA,aAAS,MAAM,GAAG,MAAM,UAAU,OAAO;AACrC,YAAM,mBAAmB,WAAW,IAAI;AACxC,YAAM,aAAa,KAAK,cAAc,gBAAgB,aAAc,mBAAmB,MAAM;AAC7F,YAAM,WAAW,YAAY,YAAY,MAAM;AAC/C,gBAAU,UAAU,UAAU;IAClC;EACJ,OAAO;AAEH,aAAS,MAAM,GAAG,MAAM,UAAU,OAAO;AACrC,YAAM,aAAa,KAAK,cAAc,gBAAgB,aAAc,MAAM,MAAM;AAChF,YAAM,WAAW,YAAY,MAAM;AACnC,gBAAU,UAAU,UAAU;IAClC;AACA,QAAI,kBAAkB,MAAM;AACxB,YAAM,aAAa,KAAK,cAAc,gBAAgB,aAAc,WAAW,MAAM;AACrF,YAAM,WAAW,YAAY,WAAW;AACxC,gBAAU,UAAU,YAAY,eAAe;IACnD;EACJ;AAIA,MAAI,KAAK,aAAa,eAAe,OAAO;AACxC,WAAO,CAAC,GAAG,QAAQ,GAAG,SAAS;EACnC,OAAO;AACH,QAAI,6BAA6B,UAAU,SAAS,GAAG;AACnD,YAAM,OAAO,UAAU,MAAM,GAAG,EAAE;AAClC,YAAM,OAAOC,eAAAC,gBAAA,CAAA,GAAK,UAAU,UAAU,SAAS,CAAC,CAAA,GAAnC,EAAsC,GAAG,uBAAuB,CAAA;AAC7E,aAAO,CAAC,GAAG,MAAM,MAAM,GAAG,MAAM;IACpC;AACA,WAAO,CAAC,GAAG,WAAW,GAAG,MAAM;EACnC;AACJ;AAiBA,SAAS,mBACL,UACA,UACA,UACA,MACsB;AAnrB1B,MAAAF;AAorBI,QAAM,YAAY,SAAS,aAAa,CAAC;AAEzC,QAAM,aAAqC,CAAC;AAE5C,aAAW,MAAM,WAAW;AACxB,UAAM,UAAU,aAAa,EAAE;AAC/B,QAAI,QAAQ,cAAc,EAAE;AAC5B,UAAM,SAAS,eAAe,EAAE;AAGhC,QAAI,aAAa,KAAK;AAClB,cAAQ,mBAAmB,KAAK;IACpC;AAQA,UAAM,gBAAgB,gBAAgB,QAAQ,IAAI,6BAA6B,QAAQ,IAAI;AAC3F,QAAI,oBAAoB,IAAI,aAAa,GAAG;AACxC,eAAQA,MAAA,WAAW,KAAK,MAAhB,OAAAA,MAAqB;IACjC;AAEA,UAAM,SAA+B;MACjC,GAAG;MACH,GAAG;MACH,GAAG,cAAc,QAAQ,IAAI;IACjC;AAKA,UAAM,MAAM,kBAAkB,EAAE;AAChC,UAAM,OAAO,mBAAmB,EAAE;AAClC,QAAI,IAAK,QAAO,YAAY;AAC5B,QAAI,KAAM,QAAO,aAAa;AAE9B,eAAW,KAAK,MAAM;EAC1B;AAGA,aAAW,KAAK,CAAC,GAAG,MAAG;AA/tB3B,QAAAA,KAAA;AA+tB+B,aAAAA,MAAA,EAAE,MAAF,OAAAA,MAAO,OAAM,KAAA,EAAE,MAAF,OAAA,KAAO;EAAA,CAAE;AAGjD,QAAM,UAAU,SAAS;AACzB,QAAM,OAA2B,YAAY,OAAO,CAAC,IAAI,WAAW;AACpE,MAAI,QAAQ,WAAW,UAAU,GAAG;AAChC,WAAO,oBAAoB,UAAU,YAAY,MAAM,QAAQ;EACnE;AAEA,SAAO;AACX;AAMA,SAAS,0BACL,YACqB;AACrB,QAAM,SAAgC,CAAC;AAEvC,aAAW,QAAQ,YAAY;AAC3B,eAAW,CAAC,MAAM,QAAQ,KAAK,OAAO,QAAQ,IAAI,GAAG;AACjD,aAAO,IAAI,IAAI;IACnB;EACJ;AAEA,SAAO;AACX;AAqBO,SAAS,mCACZ,UACA,UACA,UACmB;AACnB,QAAM,UAAU,SAAS;AACzB,MAAI,YAAY,UAAa,YAAY,QAAQ,YAAY,MAAO,QAAO;AAC3E,QAAM,OAAe,YAAY,OAAO,CAAC,IAAK;AAC9C,QAAM,SAAS,SAAS;AACxB,MAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,SAAS,EAAG,QAAO;AAWxD,QAAM,gBAAgB,gBAAgB,QAAQ,IAAI,6BAA6B,QAAQ,IAAI;AAC3F,QAAM,UAAU,oBAAoB,IAAI,aAAa;AACrD,QAAM,MAA8B,OAAO,IAAI,CAAA,OAAM;AAtyBzD,QAAAA;AAuyBQ,UAAM,IAAI,aAAa,EAAE;AACzB,QAAI,IAAa,cAAc,EAAE;AACjC,QAAI,aAAa,IAAK,KAAI,mBAAmB,CAAC;AAC9C,QAAI,QAAS,MAAIA,MAAA,WAAW,CAAC,MAAZ,OAAAA,MAAiB;AAClC,UAAM,IAAI,eAAe,EAAE;AAC3B,UAAMK,OAA4B,EAAE,GAAG,GAAG,EAAE;AAC5C,UAAM,MAAM,kBAAkB,EAAE;AAChC,UAAM,OAAO,mBAAmB,EAAE;AAClC,QAAI,IAAKA,MAAI,YAAY;AACzB,QAAI,KAAMA,MAAI,aAAa;AAC3B,WAAOA;EACX,CAAC;AAED,QAAM,WAAW,oBAAoB,UAAU,KAAK,MAAM,QAAQ;AAClE,QAAM,MAA2B,EAAE,WAAW,SAAS;AACvD,MAAI,SAAS,eAAe,OAAY,KAAiC,aAAa,SAAS;AAC/F,SAAO;AACX;AAUO,SAAS,+BACZ,MACA,UACM;AACN,QAAM,MAAM,wBAAwB,MAAM,QAAQ;AAClD,SAAO,OAAA,OAAA,MAAO;AAClB;AAEA,SAAS,wBAAwB,MAAc,UAAiC;AAC5E,MAAI;AACJ,MAAI,KAAK,UAAU;AACf,aAAS,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK;AAC3C,YAAM,MAAM,wBAAwB,KAAK,SAAS,CAAC,GAAG,QAAQ;AAC9D,UAAI,QAAQ,MAAM;AACd,YAAI,CAAC,YAAa,eAAc,KAAK,SAAS,MAAM;AACpD,oBAAY,CAAC,IAAI;MACrB;IACJ;EACJ;AACA,MAAI;AACJ,QAAM,aAAa,KAAK;AACxB,MAAI,cAAc,OAAO,eAAe,YAAY,CAAC,MAAM,QAAQ,UAAU,GAAG;AAC5E,UAAM,UAAU;AAChB,eAAW,YAAY,OAAO,KAAK,OAAO,GAAG;AACzC,YAAM,WAAW,QAAQ,QAAQ;AACjC,YAAM,eAAe,mCAAmC,UAAU,UAAU,QAAQ;AACpF,UAAI,iBAAiB,UAAU;AAC3B,YAAI,CAAC,WAAY,cAAaH,gBAAA,CAAA,GAAK,OAAA;AACnC,mBAAW,QAAQ,IAAI;MAC3B;IACJ;EACJ;AACA,MAAI,CAAC,eAAe,CAAC,WAAY,QAAO;AACxC,QAAM,SAAiBA,gBAAA,CAAA,GAAK,IAAA;AAC5B,MAAI,YAAa,QAAO,WAAW;AACnC,MAAI,WAAa,QAAO,UAAW;AACnC,SAAO;AACX;AAMA,IAAI,oBAAoB;AACjB,SAAS,oBAA4B;AACxC,SAAO,YAAa,EAAE;AAC1B;AA6BO,SAAS,gCACZ,SACA,iBACqB;AACrB,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,cACF,mBAAmB,OAAO,oBAAoB,YAAY,CAAC,MAAM,QAAQ,eAAe,IAClF,kBACA,oBAAoB,eAAyB;AACvD,MAAI,CAAC,eAAe,CAAC,OAAO,KAAK,WAAW,EAAE,OAAQ,QAAO;AAE7D,QAAM,eAAe,CAAC,MAClB,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC,IAAIA,gBAAAA,gBAAA,CAAA,GAAK,WAAA,GAAiB,CAAA,IAA2B;AAEvG,QAAM,gBAAgB,QAAQ,cAAc;AAC5C,MAAI,iBAAiB,OAAO,kBAAkB,UAAU;AACpD,UAAM,OAAO;AACb,QAAI,MAAM,QAAQ,KAAK,SAAS,GAAG;AAC/B,YAAM,MAA2BD,eAAAC,gBAAA,CAAA,GAC1B,IAAA,GAD0B;QAE7B,WAAW,KAAK,UAAU,IAAI,CAAA,OAAOD,eAAAC,gBAAA,CAAA,GAAK,EAAA,GAAL,EAAS,OAAO,aAAa,GAAG,KAAK,EAAE,CAAA,CAAE;MAClF,CAAA;AACA,UAAI,IAAI,UAAU,OAAW,KAAI,QAAQ,aAAa,IAAI,KAAK;AAC/D,aAAOD,eAAAC,gBAAA,CAAA,GAAK,OAAA,GAAL,EAAc,WAAW,IAAI,CAAA;IACxC;AACA,WAAO;EACX;AAEA,QAAM,WAAW,OAAO,KAAK,OAAO,EAAE,OAAO,CAAA,MAAK,sBAAsB,IAAI,CAAC,CAAC;AAC9E,MAAI,SAAS,WAAW,EAAG,QAAO;AAClC,QAAM,KAAK,SAAS,CAAC;AACrB,QAAM,SAAS,QAAQ,EAAE;AACzB,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,OAAO,SAAS,EAAG,QAAO;AACtF,QAAM,SAA8BD,eAAAC,gBAAA,CAAA,GAC7B,MAAA,GAD6B;IAEhC,WAAW,OAAO,UAAU,IAAI,CAAA,OAAOD,eAAAC,gBAAA,CAAA,GAAK,EAAA,GAAL,EAAS,OAAOD,eAAAC,gBAAA,CAAA,GAAK,WAAA,GAAL,EAAkB,CAAC,EAAE,GAAG,GAAG,MAAM,CAAA,EAAE,CAAA,CAAE;EAChG,CAAA;AACA,MAAI,OAAO,UAAU,OAAW,QAAO,QAAQD,eAAAC,gBAAA,CAAA,GAAK,WAAA,GAAL,EAAkB,CAAC,EAAE,GAAG,OAAO,MAAM,CAAA;AACpF,QAAM,OAA8BA,gBAAA,CAAA,GAAK,OAAA;AACzC,SAAO,KAAK,EAAE;AACd,SAAOD,eAAAC,gBAAA,CAAA,GAAK,IAAA,GAAL,EAAW,WAAW,OAAO,CAAA;AACxC;AAOA,SAAS,6BACL,SACA,UACA,MACA,SAA2B,iBAAiB,QACvB;AACrB,QAAM,aAAoC,CAAC;AAE3C,aAAW,CAAC,UAAU,QAAQ,KAAK,OAAO,QAAQ,OAAO,GAAG;AAOxD,QAAI,aAAa,eACT,SAAwC,kBAAkB,gBAO1D,QAAoC,gBAAgB,MAAM,QAAW;AACzE;IACJ;AACA,UAAM,gBAAgB,mBAAmB,UAAU,UAAU,UAAU,IAAI;AAC3E,QAAI,cAAc,SAAS,GAAG;AAG1B,YAAM,MAA2B,EAAE,WAAW,cAAc;AAI5D,UAAI,SAAS,eAAe,OAAW,KAAI,aAAa,SAAS;AACjE,UAAI,SAAS,SAAS,OAAW,KAAI,OAAO,SAAS;AAWrD,iBAAW,QAAQ,IAAK,WAAW,iBAAiB,UAAU,aAAa,cACrE,gCAAgC,GAAG,IACnC;IACV;EACJ;AAEA,SAAO;AACX;AASO,SAAS,kBACZ,KACA,SAA2B,iBAAiB,QACvB;AACrB,QAAM,iBAAiB,kBAAkB,GAAG,KAAK,CAAC;AAClD,QAAM,OAAO,eAAe,GAAG;AAC/B,QAAM,WAAW,eAAe,YAAY;AAE5C,QAAM,WAAkC,CAAC;AAGzC,QAAM,mBAAmB,CACrB,IACA,UACA,oBAC6B;AAC7B,QAAI,SAAS,WAAW,EAAG,QAAO;AAKlC,UAAM,SAAS,gCAAgC,0BAA0B,QAAQ,GAAG,eAAe;AACnG,UAAM,iBAAiB,6BAA6B,QAAQ,UAAU,MAAM,MAAM;AAElF,QAAI,OAAO,KAAK,cAAc,EAAE,WAAW,EAAG,QAAO;AAErD,WAAO;MACH;MACA,SAAS;IACb;EACJ;AAIA,QAAM,cAAc,YAAY,GAAG;AACnC,MAAI,aAAa;AACb,eAAW,WAAW,aAAa;AAC/B,YAAM,KAAK,QAAQ,OAAO,WAAW,GAAG,IAAI,QAAQ,OAAO,MAAM,CAAC,IAAI,QAAQ;AAC9E,YAAM,WAAW,QAAQ,YACpB,IAAI,CAAA,SAAQ,iBAAiB,MAAM,IAAI,CAAC,EACxC,OAAO,CAAC,MAAkC,CAAC,CAAC,CAAC;AAClD,YAAM,aAAa,iBAAiB,IAAI,QAAQ;AAChD,UAAI,WAAY,UAAS,KAAK,UAAU;IAC5C;EACJ;AASA,QAAM,cAAc,CAAC,SAAiB;AAClC,UAAM,aAAa,KAAK;AACxB,QAAI,cAAc,OAAO,KAAK,UAAU,EAAE,SAAS,GAAG;AAClD,YAAM,SAAS,KAAK,MAAM,kBAAkB;AAC5C,WAAK,KAAK;AACV,YAAM,aAAa,iBAAiB,QAAQ,wBAAwB,YAAY,IAAI,GAAG,KAAK,SAAS;AACrG,UAAI,WAAY,UAAS,KAAK,UAAU;IAC5C;AAGA,QAAI,KAAK,UAAU;AACf,eAAS,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK;AAC3C,oBAAY,KAAK,SAAS,CAAC,CAAC;MAChC;IACJ;EACJ;AAGA,MAAI,IAAI,UAAU;AACd,aAAS,IAAI,GAAG,IAAI,IAAI,SAAS,QAAQ,KAAK;AAC1C,kBAAY,IAAI,SAAS,CAAC,CAAC;IAC/B;EACJ;AAEA,SAAO;AACX;AAUA,SAAS,iBAAiB,WAAmC,UAAkB;AAnlC/E,MAAAF,KAAA;AA0lCI,QAAM,OAAO,UAAU,SAAS;AAChC,MAAI,SAAS,UAAU,CAAC;AACxB,MAAI,SAAS,UAAU,OAAO,IAAI,IAAI,CAAC;AAEvC,WAAS,IAAI,GAAG,IAAI,MAAM,KAAK;AAC3B,UAAM,QAAQA,MAAA,UAAU,CAAC,EAAE,MAAb,OAAAA,MAAkB;AAChC,UAAM,QAAQ,KAAA,UAAU,IAAI,CAAC,EAAE,MAAjB,OAAA,KAAsB;AACpC,QAAI,QAAQ,YAAY,YAAY,MAAM;AACtC,eAAS,UAAU,CAAC;AACpB,eAAS,UAAU,IAAI,CAAC;AACxB;IACJ;AAEA,QAAI,WAAW,QAAQ,MAAM,OAAO,GAAG;AACnC,eAAS,UAAU,OAAO,IAAI,OAAO,IAAI,CAAC;AAC1C,eAAS,UAAU,IAAI;IAC3B;EACJ;AACA,SAAO,EAAE,QAAQ,OAAO;AAC5B;AAKA,SAAS,kBACL,UACA,UACA,UAC+B;AAtnCnC,MAAAA,KAAA,IAAA,IAAA;AAunCI,QAAM,YAAY,SAAS,aAAa,CAAC;AACzC,MAAI,UAAU,WAAW,EAAG,QAAO;AAEnC,QAAM,EAAE,QAAQ,OAAO,IAAI,iBAAiB,WAAW,QAAQ;AAG/D,MAAI,gBAAiB,WAAW,SAAU,IAAI,MAAM,WAAUA,MAAA,OAAO,MAAP,OAAAA,MAAY,IAAG,KAAA,OAAO,MAAP,OAAA,KAAY,GAAG,GAAG,CAAC;AAChG,kBAAgB,MAAM,eAAe,GAAG,CAAC;AACzC,QAAM,SAAS,eAAe,MAAM;AACpC,MAAI,UAAU,MAAM,QAAQ,MAAM,GAAG;AACjC,QAAI;AACA,sBAAgB,YAAY,MAA0C,EAAE,aAAa;IACzF,SAAS,GAAG;IAEZ;EACJ;AAEA,MAAI,cAAc,gBAAgB,QAAQ,IAAI,6BAA6B,QAAQ,IAAI;AACvF,MAAI,WAAmC;AAEvC,QAAM,QAAQ,UAAA,OAAA,SAAA,OAAQ;AACtB,QAAM,QAAQ,UAAA,OAAA,SAAA,OAAQ;AAEtB,MAAI,gBAAgB,KAAK;AAErB,UAAM,aAAY,KAAA,SAAA,OAAA,SAAA,MAAO,UAAP,OAAA,KAAiB,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;AACnE,UAAM,aAAY,KAAA,SAAA,OAAA,SAAA,MAAO,UAAP,OAAA,KAAiB,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;AACnE,eAAW;MACP;MACA;MACA;IACJ,EAAE,IAAI,CAAA,OAAM,gBAAgB,EAAE,CAAC,EAAE,KAAK,EAAE;EAC5C,WAAW,oBAAoB,IAAI,WAAW,GAAG;AAC7C,eAAW,OAAO;MACd,SAAS,CAAC,GAAG,GAAG,GAAG,CAAC;MACpB,SAAS,CAAC,GAAG,GAAG,GAAG,CAAC;MACpB;IACJ,CAAC;AACD,kBAAc;EAClB,WAAW,gBAAgB,oBAAoB;AAC3C,eAAW;MACP,SAAS,CAAC;MACV,SAAS,CAAC;MACV;IACJ,EAAE,KAAK,GAAG;AACV,kBAAc;EAClB,WACI,gBAAgB,eAChB,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GACrE;AAIE,UAAM,WAAW,oBAAI,IAAY;MAC7B,GAAI,QAAQ,OAAO,KAAK,KAAK,IAAI,CAAC;MAClC,GAAI,QAAQ,OAAO,KAAK,KAAK,IAAI,CAAC;IACtC,CAAC;AACD,UAAM,cAAgC,CAAC;AACvC,eAAW,WAAW,UAAU;AAC5B,YAAM,WAAW,SAAA,OAAA,SAAA,MAAQ,OAAA;AACzB,YAAM,WAAW,SAAA,OAAA,SAAA,MAAQ,OAAA;AACzB,UAAI,YAAY,YAAY,YAAY,QAAQ;AAC5C,oBAAY,OAAO,IAAI,eAAe,EAAE,YAAA,OAAA,WAAY,IAAI,EAAE,YAAA,OAAA,WAAY,IAAI,aAAa;MAC3F,WAAW,YAAY,eAAe,YAAY,WAAW,YAAY,UAAU;AAC/E,cAAM,WAAW,YAAY,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;AACrD,cAAM,SAAS,eAAe,YAAY,UAAU,YAAY,UAAU,aAAa;AACtF,oBAAoB,OAAO,IAAI;MACpC;IACJ;AAQA,QAAI,qBAAqB,QAAQ,GAAG;AAChC,YAAM,SAAU,MAA2C;AAC3D,YAAM,SAAU,MAA2C;AAC3D,UAAI,MAAM,QAAQ,MAAM,KAAK,MAAM,QAAQ,MAAM,GAAG;AAChD,cAAM,SAAS;UACX;UAAQ;UACR,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;UACvB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;UACvB;UACA,CAAC,CAAC,SAAS;QACf;AACA,oBAAY,YAAY,CAAC,OAAO,UAAU,CAAC,GAAG,OAAO,UAAU,CAAC,CAAC;AACjE,YAAI,OAAO,cAAc,OAAW,aAAY,SAAS,OAAO;MACpE;IACJ;AACA,eAAW,sBAAsB,aAAa,EAAE,WAAW,MAAM,CAAC;AAClE,kBAAc;EAClB,WAAW,gBAAgB,aAAa;AACpC,UAAM,IAAI;MACN,SAAS,CAAC,GAAG,CAAC;MACd,SAAS,CAAC,GAAG,CAAC;MACd;IACJ;AACA,eAAW,eAAe,EAAE,KAAK,GAAG,IAAI;AACxC,kBAAc;EAClB,WAAW,gBAAgB,UAAU;AACjC,UAAM,IAAI;MACN,EAAE,SAAS;MACX,EAAE,SAAS;MACX;IACJ;AACA,eAAW,YAAY,IAAI;AAC3B,kBAAc;EAClB,WAAW,gBAAgB,SAAS;AAChC,UAAM,IAAI;MACN,SAAS,CAAC,GAAG,CAAC;MACd,SAAS,CAAC,GAAG,CAAC;MACd;IACJ;AACA,eAAW,WAAW,EAAE,KAAK,GAAG,IAAI;AACpC,kBAAc;EAClB,OAAO;AAEH,UAAM,MAAM;MACR,EAAE,SAAS;MACX,EAAE,SAAS;MACX;IACJ;AACA,eAAW;EACf;AAEA,MAAI,wBAAwB,IAAI,WAAW,KAAK,OAAO,aAAa,UAAU;AAC1E,eAAY,WAAW,MAAO;EAClC;AAEA,SAAO,EAAE,GAAG,aAAa,GAAG,aAAa,OAAO,KAAK,KAAK,SAAS;AACvE;AASO,SAAS,oBACZ,SACA,UACsB;AACtB,QAAM,SAAiC,CAAC;AAExC,aAAW,CAAC,UAAU,QAAQ,KAAK,OAAO,QAAQ,OAAO,GAAG;AACxD,UAAM,WAAW,kBAAkB,UAAU,UAAU,QAAQ;AAC/D,QAAI,UAAU;AACV,aAAO,SAAS,CAAC,IAAI,SAAS;IAClC;EACJ;AAEA,SAAO;AACX;AC9uCA,IAAM,YAAY;AAClB,IAAM,SAAS;AAGf,SAAS,SAAS,GAA0B;AACxC,QAAM,SAAwB,CAAC;AAC/B,QAAM,KAAK;AACX,MAAI;AACJ,UAAQ,IAAI,GAAG,KAAK,CAAC,OAAO,KAAM,QAAO,KAAK,EAAE,CAAC,CAAC;AAClD,SAAO;AACX;AAEA,SAAS,YAAY,IAAQ,IAAQ,IAA4B;AAC7D,SAAO;IACH,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,EAAE;IACrE,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,EAAE;EACzE;AACJ;AAGA,SAAS,YAAY,GAAgC;AACjD,QAAM,SAAS,SAAS,CAAC;AACzB,QAAM,OAAqB,CAAC;AAE5B,MAAI,IAAI;AACR,MAAI,KAAK,GAAG,KAAK;AACjB,MAAI,KAAK,GAAG,KAAK;AACjB,MAAI,MAAM,GAAG,MAAM;AACnB,MAAI,MAAM,GAAG,MAAM;AACnB,MAAI,UAAU;AAEd,QAAM,MAAM,MAAc,WAAW,OAAO,GAAG,CAAC;AAChD,QAAM,OAAO,CAAC,IAAQ,IAAQ,OAAiB;AAC3C,UAAM,KAAS,CAAC,IAAI,EAAE;AACtB,UAAM,MAAM,sBAAsB,IAAI,IAAI,IAAI,IAAI,SAAS;AAC3D,SAAK,KAAK,EAAE,IAAI,IAAI,IAAI,IAAI,KAAK,KAAK,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;AACjE,SAAK,GAAG,CAAC;AAAG,SAAK,GAAG,CAAC;EACzB;AAIA,QAAM,WAAW,CAAC,GAAW,MAAoB;AAC7C;MACI,CAAC,MAAM,IAAI,MAAM,GAAG,MAAM,IAAI,MAAM,CAAC;MACrC,CAAC,KAAK,KAAK,IAAI,MAAM,GAAG,KAAK,KAAK,IAAI,MAAM,CAAC;MAC7C,CAAC,GAAG,CAAC;IACT;EACJ;AAEA,SAAO,IAAI,OAAO,QAAQ;AACtB,QAAI,MAAM,OAAO,CAAC;AAClB,QAAI,OAAO,KAAK,GAAG,EAAG;QACjB,OAAM,YAAY,MAAM,MAAM,YAAY,MAAM,MAAM;AAC3D,UAAM,MAAM,OAAO;AACnB,UAAM,IAAI,IAAI,YAAY;AAE1B,QAAI,MAAM,KAAK;AAAE,eAAS,IAAI,EAAE;AAAG,WAAK;AAAI,WAAK;AAAI,gBAAU;AAAK;IAAU;AAC9E,QAAI,MAAM,KAAK;AACX,YAAM,IAAI,IAAI,KAAK,MAAM,KAAK,IAAI,IAAI,IAAI,KAAK,MAAM,KAAK;AAC1D,WAAK;AAAG,WAAK;AAAG,WAAK;AAAG,WAAK;AAAG,gBAAU;AAAK;IACnD;AACA,QAAI,MAAM,KAAK;AACX,eAAS,IAAI,KAAK,MAAM,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,EAAE;IAC3D,WAAW,MAAM,KAAK;AAClB,eAAS,IAAI,KAAK,MAAM,KAAK,IAAI,EAAE;IACvC,WAAW,MAAM,KAAK;AAClB,eAAS,IAAI,IAAI,KAAK,MAAM,KAAK,EAAE;IACvC,WAAW,MAAM,KAAK;AAClB,YAAM,KAAS,CAAC,IAAI,KAAK,MAAM,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,EAAE;AAC9D,YAAM,KAAS,CAAC,IAAI,KAAK,MAAM,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,EAAE;AAC9D,YAAM,KAAS,CAAC,IAAI,KAAK,MAAM,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,EAAE;AAC9D,YAAM,GAAG,CAAC;AAAG,YAAM,GAAG,CAAC;AACvB,WAAK,IAAI,IAAI,EAAE;IACnB,WAAW,MAAM,KAAK;AAClB,YAAM,SAAU,QAAQ,YAAY,MAAM,OAAO,QAAQ,YAAY,MAAM;AAC3E,YAAM,KAAS,SAAS,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE;AAC9D,YAAM,KAAS,CAAC,IAAI,KAAK,MAAM,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,EAAE;AAC9D,YAAM,KAAS,CAAC,IAAI,KAAK,MAAM,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,EAAE;AAC9D,YAAM,GAAG,CAAC;AAAG,YAAM,GAAG,CAAC;AACvB,WAAK,IAAI,IAAI,EAAE;IACnB,WAAW,MAAM,KAAK;AAClB,YAAM,KAAS,CAAC,IAAI,KAAK,MAAM,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,EAAE;AAC9D,YAAM,KAAS,CAAC,IAAI,KAAK,MAAM,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,EAAE;AAC9D,YAAM,GAAG,CAAC;AAAG,YAAM,GAAG,CAAC;AACvB,YAAM,EAAE,IAAI,GAAG,IAAI,YAAY,CAAC,IAAI,EAAE,GAAG,IAAI,EAAE;AAC/C,WAAK,IAAI,IAAI,EAAE;IACnB,WAAW,MAAM,KAAK;AAClB,YAAM,SAAU,QAAQ,YAAY,MAAM,OAAO,QAAQ,YAAY,MAAM;AAC3E,YAAM,KAAS,SAAS,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE;AAC9D,YAAM,KAAS,CAAC,IAAI,KAAK,MAAM,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,EAAE;AAC9D,YAAM,GAAG,CAAC;AAAG,YAAM,GAAG,CAAC;AACvB,YAAM,EAAE,IAAI,GAAG,IAAI,YAAY,CAAC,IAAI,EAAE,GAAG,IAAI,EAAE;AAC/C,WAAK,IAAI,IAAI,EAAE;IACnB,WAAW,MAAM,KAAK;AAElB,WAAK;AACL,eAAS,IAAI,KAAK,MAAM,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,EAAE;IAC3D,OAAO;AAAE;AAAK;IAAU;AAExB,cAAU;EACd;AAEA,SAAO,KAAK,SAAS,OAAO;AAChC;AAEA,SAASM,OAAM,GAAW,IAAY,IAAoB;AACtD,SAAO,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK;AACvC;AAGO,SAAS,kBAAkB,GAA+B;AAC7D,QAAM,OAAO,YAAY,CAAC;AAC1B,MAAI,CAAC,KAAM,QAAO;AAGlB,QAAM,MAAM,IAAI,aAAa,KAAK,SAAS,CAAC;AAC5C,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,IAAK,KAAI,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,EAAE;AACpE,QAAM,cAAc,IAAI,KAAK,MAAM;AAKnC,QAAM,QAAQ,KAAK,CAAC,EAAE,IAAI,MAAM,KAAK,KAAK,SAAS,CAAC,EAAE;AACtD,QAAM,SAAS,KAAK,MAAM,IAAI,CAAC,IAAI,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI;AAGlE,QAAM,WAAW,CAAC,SAA4B;AAC1C,QAAI,IAAI;AACR,WAAO,IAAI,KAAK,SAAS,KAAK,OAAO,IAAI,IAAI,CAAC,EAAG;AACjD,UAAM,MAAM,KAAK,CAAC;AAClB,UAAM,QAAQ,OAAO,IAAI,CAAC;AAC1B,UAAM,IAAI,IAAI,MAAM,IAAI,sBAAsB,IAAI,KAAK,KAAK,IAAI;AAChE,UAAM,CAAC,GAAG,CAAC,IAAI,iBAAiB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC;AACjE,UAAM,CAAC,IAAI,EAAE,IAAI,sBAAsB,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC;AACxE,WAAO,EAAE,GAAG,GAAG,OAAO,KAAK,MAAM,IAAI,EAAE,EAAE;EAC7C;AAEA,SAAO;IACH;IACA;IACA,iBAAiB,MAAyB;AAKtC,UAAI,UAAU,cAAc,MAAM,OAAO,KAAK,OAAO,cAAc;AAC/D,eAAO,UAAW,OAAO,cAAe,eAAe,WAAW;MACtE;AAKA,UAAI,CAAC,WAAW,OAAO,KAAK,OAAO,cAAc;AAC7C,cAAM,OAAO,OAAO,IAAI,IAAI;AAC5B,cAAM,IAAI,SAAS,IAAI;AACvB,cAAM,OAAO,OAAO;AACpB,eAAO,EAAE,GAAG,EAAE,IAAI,KAAK,IAAI,EAAE,KAAK,IAAI,MAAM,GAAG,EAAE,IAAI,KAAK,IAAI,EAAE,KAAK,IAAI,MAAM,OAAO,EAAE,MAAM;MAClG;AACA,aAAO,SAASA,OAAM,MAAM,GAAG,WAAW,CAAC;IAC/C;EACJ;AACJ;AC9JO,SAAS,YAAY,MAAqB,OAAY,QAA4B;AACrF,QAAM,MAA+F,CAAC;AACtG,MAAI,SAAS,YAAyB,KAAI,YAAY;WAC7C,SAAS,SAAsB,KAAI,SAAS;WAC5C,SAAS,OAAoB,KAAI,OAAO;MAC5C,KAAI,QAAQ;AACjB,MAAI,UAAU,SAAS,YAAyB,KAAI,SAAS;AAC7D,SAAO;AACX;AAaO,SAAS,eAAkB,KAA+C;AA5DjF,MAAAN,KAAA;AA6DI,MAAI,QAAQ,OAAW,QAAO;IAAE,MAAM;;EAAgB;AACtD,MAAI,MAAM,QAAQ,GAAG,EAAG,QAAO,EAAE,MAAM,UAAiB,OAAO,IAAoB;AACnF,MAAI,OAAO,QAAQ,UAAU;AACzB,UAAM,MAAM;AAKZ,UAAM,MAAM,IAAI;AAChB,QAAI,KAAK;AAKL,YAAM,MAAmB,EAAE,MAAM,YAAmB,WAAW,IAAI,IAAI,iBAAiB,GAAG,YAAY,IAAI,YAAY,MAAM,IAAI,KAAK;AACtI,YAAM,QAAOA,MAAA,IAAI,UAAJ,OAAAA,MAAa,IAAI;AAC9B,UAAI,SAAS,UAAa,IAAI,SAAS,WAAmB,KAAI,OAAO;AACrE,aAAO;IACX;AACA,UAAM,eAAc,KAAA,IAAI,UAAJ,OAAA,KAAa,IAAI;AACrC,QAAI,gBAAgB,OAAW,QAAO,EAAE,MAAM,UAAiB,OAAO,YAAY;EACtF;AACA,SAAO,EAAE,MAAM,UAAiB,OAAO,IAAS;AACpD;AAYO,SAAS,uBACZ,MACA,UACA,MACA,MACI;AArGR,MAAAA,KAAA,IAAA,IAAA;AAsGI,QAAM,QAAQ,CAAC,OAAyB,QAAA,OAAA,SAAA,KAAM,aAAY,MAAM,UAAa,MAAM,OAAQ,OAAO,CAAC,IAAI;AACvG,MAAI,KAAK,SAAS,SAAiB;AACnC,MAAI,KAAK,SAAS,UAAiB;AAC/B,SAAK,QAAQ,IAAI,MAAM,KAAK,KAAK;AACjC;EACJ;AACA,QAAM,cAAc,KAAK,WAAW,OAAO,KAAK,YAAY,YAAY,CAAC,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,UAAU;AACtH,QAAM,UAA+BE,gBAAA,CAAA,GAAM,eAAe,CAAC,CAAA;AAC3D,QAAM,QAAyF,EAAE,WAAW,KAAK,UAAU;AAC3H,MAAI,KAAK,SAAS,OAAW,OAAM,OAAO,KAAK;AAC/C,MAAI,KAAK,eAAe,OAAW,OAAM,aAAa,KAAK;AAC3D,UAAQ,QAAQ,IAAI;AACpB,OAAK,UAAU;AACf,QAAM,YAAW,MAAA,KAAA,KAAK,SAAL,OAAA,MAAaF,MAAA,KAAK,UAAU,CAAC,MAAhB,OAAA,SAAAA,IAAmB,UAAhC,OAAA,MAA0C,KAAA,KAAK,UAAU,CAAC,MAAhB,OAAA,SAAA,GAAmD;AAC9G,MAAI,aAAa,OAAW,MAAK,QAAQ,IAAI,MAAM,QAAQ;AAC/D;AAKA,SAAS,kBAAqB,IAAkC;AAC5D,MAAI,CAAC,MAAM,OAAO,OAAO,SAAU,QAAO;AAC1C,QAAM,IAAI;AACV,MAAI,EAAE,MAAM,UAAa,EAAE,MAAM,UAAa,EAAE,MAAM,UAAa,EAAE,OAAO,UAAa,EAAE,OAAO,QAAW;AACzG,WAAO;EACX;AACA,QAAM,MAAWE,gBAAA,CAAA,GAAK,CAAA;AACtB,MAAI,IAAI,SAAS,UAAa,EAAE,MAAM,OAAW,KAAI,OAAO,EAAE;AAC9D,MAAI,IAAI,UAAU,UAAa,EAAE,MAAM,OAAW,KAAI,QAAQ,EAAE;AAChE,MAAI,IAAI,WAAW,UAAa,EAAE,MAAM,OAAW,KAAI,SAAS,EAAE;AAClE,MAAI,IAAI,eAAe,UAAa,EAAE,OAAO,OAAW,KAAI,aAAa,EAAE;AAC3E,MAAI,IAAI,cAAc,UAAa,EAAE,OAAO,OAAW,KAAI,YAAY,EAAE;AACzE,SAAO;AACX;AAGO,SAAS,iBAAiB,KAAuC,KAAuC;AA1I/G,MAAAF;AA2II,QAAM,IAAI,eAAuB,GAAG;AACpC,MAAI,EAAE,SAAS,SAAiB,QAAO;AACvC,MAAI,EAAE,SAAS,SAAiB,QAAO,EAAE;AACzC,MAAI,SAAS,KAAK,wEAAwE;AAC1F,UAAOA,MAAA,EAAE,UAAU,CAAC,MAAb,OAAA,SAAAA,IAAgB;AAC3B;AAGO,SAAS,aAAa,IAAqB,OAA6B;AAC3E,QAAM,MAAuB,EAAE,MAAM;AACrC,MAAI,GAAG,SAAS,OAAW,KAAI,OAAO,GAAG;AACzC,MAAI,GAAG,WAAW,OAAW,KAAI,SAAS,GAAG;AAC7C,MAAI,GAAG,eAAe,OAAW,KAAI,aAAa,GAAG;AACrD,MAAI,GAAG,cAAc,OAAW,KAAI,YAAY,GAAG;AACnD,SAAO;AACX;AC/HO,SAAS,gBAAmB,OAAa;AAC5C,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AACxD,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,IAAI,eAAe;AAC1D,QAAM,MAAgC,CAAC;AACvC,aAAW,KAAK,OAAO,KAAK,KAAe,EAAG,KAAI,CAAC,IAAI,gBAAiB,MAAmC,CAAC,CAAC;AAC7G,SAAO;AACX;AAeO,SAAS,4BACZ,MACAO,QACmB;AACnB,QAAM,WAAW,oBAAI,IAAoB;AAEzC,QAAM,aAAa,CAAC,MAAoB;AAtD5C,QAAAP;AAuDQ,QAAI,OAAO,EAAE,OAAO,UAAU;AAC1B,YAAM,QAAQO,OAAM;AACpB,eAAS,IAAI,EAAE,IAAI,KAAK;AACxB,QAAE,KAAK;IACX;AACA,KAAAP,MAAA,EAAE,aAAF,OAAA,SAAAA,IAAY,QAAQ,UAAA;EACxB;AACA,aAAW,IAAI;AAEf,QAAM,aAAa,CAAC,MAAsB,EAAE,QAAQ,oBAAoB,CAAC,GAAG,UAAU;AAClF,UAAM,QAAQ,SAAS,IAAI,KAAK;AAChC,WAAO,QAAQ,UAAU,QAAQ,MAAM;EAC3C,CAAC;AAED,QAAM,cAAc,CAAC,MAAoB;AArE7C,QAAAA;AAsEQ,QAAI,OAAO,EAAE,SAAS,YAAY,EAAE,KAAK,WAAW,GAAG,GAAG;AACtD,YAAM,QAAQ,SAAS,IAAI,EAAE,KAAK,MAAM,CAAC,CAAC;AAC1C,UAAI,MAAO,GAAE,OAAO,MAAM;IAC9B;AACA,eAAW,KAAK,OAAO,KAAK,CAAC,GAAG;AAC5B,UAAI,MAAM,cAAc,MAAM,aAAa,MAAM,UAAU,MAAM,aAAa,MAAM,UAAU,MAAM,KAAM;AAC1G,YAAM,IAAK,EAA+B,CAAC;AAC3C,UAAI,OAAO,MAAM,YAAY,EAAE,QAAQ,OAAO,MAAM,IAAI;AACnD,UAA+B,CAAC,IAAI,WAAW,CAAC;MACrD;IACJ;AACA,KAAAA,MAAA,EAAE,aAAF,OAAA,SAAAA,IAAY,QAAQ,WAAA;EACxB;AACA,cAAY,IAAI;AAEhB,SAAO;AACX;AAGA,SAAS,YAAY,GAAoB;AACrC,QAAM,IAAI,OAAO,MAAM,WAAW,IAAI,OAAO,MAAM,WAAW,WAAW,CAAC,IAAI;AAC9E,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AACpC;AAoBO,SAAS,kBAAkB,OAAuB;AAhHzD,MAAAA;AAiHI,QAAM,IAAI,YAAa,MAA0B,CAAC;AAClD,QAAM,IAAI,YAAa,MAA0B,CAAC;AAClD,SAAQ,MAA0B;AAClC,SAAQ,MAA0B;AAClC,MAAI,CAAC,KAAK,CAAC,EAAG,QAAO;AAErB,QAAM,SAAS,eAAe,IAAI,MAAM,IAAI;AAK5C,QAAM,mBACD,MAAkC,cAAc,UAChD,MAAgC,YAAY;AAEjD,MAAI,kBAAkB;AAClB,UAAM,QAAgB,EAAE,MAAM,KAAK,WAAW,QAAQ,WAAUA,MAAA,MAAM,aAAN,OAAAA,MAAkB,CAAC,EAAE;AACrF,UAAM,WAAW,CAAC,KAAK;EAC3B,OAAO;AACF,UAAiC,YAAY;EAClD;AACA,SAAO;AACX;AC1HO,SAAS,MAAM,KAAmB,QAAwB;AAC7D,SAAO,SAAS,SAAS,MAAO,IAAI;AACxC;AAEO,SAAS,UAAU,MAA+B;AACrD,SAAO,OAAO,SAAS,WAAW,KAAK,QAAQ,MAAM,EAAE,IAAI;AAC/D;AAUO,SAAS,UAAU,MAAc,KAAgC;AA7BxE,MAAAA;AA8BI,MAAI,OAAO,KAAK,OAAO,SAAU,KAAI,IAAI,KAAK,IAAI,IAAI;AACtD,GAAAA,MAAA,KAAK,aAAL,OAAA,SAAAA,IAAe,QAAQ,CAAA,UAAS,UAAU,OAAO,GAAG,CAAA;AACxD;AAGO,SAAS,WAAW,MAAc,MAA2B;AAChE,MAAI,CAAC,KAAK,OAAQ;AAClB,QAAM,WAAW,KAAK,aAAa,KAAK,WAAW,CAAC;AACpD,WAAS,QAAQ,EAAE,MAAM,QAAQ,UAAU,KAAK,CAAC;AACrD;AAIO,IAAM,QAAQ;AAQd,SAAS,qBAAqB,MAAc,KAAwC;AACvF,SAAO,4BAA4B,MAAM,MAAM,MAAM,KAAK,SAAS,CAAC;AACxE;ACxCA,IAAM,qBAAqB;AAQ3B,SAAS,SAAS,GAAmE;AACjF,QAAM,OAAO,eAAuB,CAAC;AACrC,MAAI,KAAK,SAAA,YAA4B,OAAO,KAAK,UAAU,SAAU,QAAO,EAAE,KAAK,KAAK,OAAO,KAAK,KAAK,MAAM;AAC/G,MAAI,KAAK,SAAA,cAA8B,KAAK,UAAU,QAAQ;AAC1D,UAAM,OAAO,KAAK,UAAU,IAAI,CAAA,MAAK,OAAO,EAAE,KAAK,KAAK,CAAC;AACzD,WAAO,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,IAAI,EAAE;EAC5D;AACA,SAAO,EAAE,KAAK,GAAG,KAAK,EAAE;AAC5B;AAKA,SAAS,oBAAoB,MAAsB;AAC/C,MAAI,QAAQ,GAAG,UAAU;AACzB,QAAM,OAAO,CAAC,OAAqB;AApCvC,QAAAA;AAqCQ,UAAM,KAAK,WAAW,QAAQA,MAAA,GAAW,aAAX,OAAAA,MAAuB,EAAE,CAAC,KAAK;AAC7D,QAAI,GAAI,WAAU,KAAK,IAAI,SAAS,EAAE;AACtC,UAAM,IAAI,GAAG;AACb,QAAI,OAAO,MAAM,SAAU,UAAS,EAAE;AACtC,QAAI,GAAG,SAAU,YAAW,KAAK,GAAG,SAAU,MAAK,CAAC;EACxD;AACA,OAAK,IAAI;AACT,SAAO,QAAQ;AACnB;AAEA,IAAM,KAAK,CAAC,MAAsB,KAAK,MAAM,IAAI,GAAI,IAAI;AA4BlD,SAAS,gBAAgB,GAAqC,IAA8C;AAC/G,MAAI,CAAC,MAAM,MAAM,UAAa,MAAM,KAAM,QAAO;AACjD,QAAM,OAAO,eAAuB,CAAC;AACrC,MAAI,KAAK,SAAA,SAA0B,QAAO;AAC1C,MAAI,KAAK,SAAA,SAA0B,QAAO,OAAO,MAAM,WAAW,KAAK,QAAQ,KAAK,EAAE,OAAO,KAAK,QAAQ,GAAG;AAC7G,QAAM,MAA+G;IACjH,WAAW,KAAK,UAAU,IAAI,CAAA,MAAMC,eAAAC,gBAAA,CAAA,GAAK,CAAA,GAAL,EAAQ,QAAQ,OAAO,EAAE,KAAK,KAAK,KAAK,GAAG,CAAA,CAAE;EACrF;AACA,MAAI,KAAK,SAAS,OAAW,KAAI,OAAO,KAAK;AAC7C,MAAI,KAAK,eAAe,OAAW,KAAI,aAAa,KAAK;AACzD,MAAI,KAAK,SAAS,OAAW,KAAI,QAAQ,KAAK,OAAO;AACrD,SAAO;AACX;AASO,SAAS,uBAAuB,OAAe,MAAuC;AAhG7F,MAAAF;AAiGI,MAAI,KAAK,iBAAiB,OAAQ,QAAO,EAAE,GAAG,OAAO,YAAY,EAAE;AACnE,QAAM,UAAU,kBAAkB,KAAK;AACvC,MAAI,CAAC,WAAW,QAAQ,UAAU,QAAQ,eAAe,EAAG,QAAO,EAAE,GAAG,OAAO,YAAY,EAAE;AAE7F,QAAM,IAAI,QAAQ;AAClB,QAAM,SAAS,qBAAqB;AACpC,QAAM,KAAK,SAAS,KAAK,WAAW;AACpC,QAAM,WAAW,SAAS,KAAK,UAAU,EAAE,SAAQA,MAAA,KAAK,YAAL,OAAAA,MAAgB;AAKnE,QAAM,gBAAgB,KAAK,IAAI,GAAG,CAAC,GAAG,GAAG;AACzC,QAAM,cAAc,KAAK,IAAI,GAAG,GAAG,MAAM,WAAW,CAAC;AACrD,QAAM,WAAW,gBAAgB,IAAI,gBAAgB,SAAS;AAC9D,QAAM,SAAS,cAAc,IAAI,cAAc,SAAS;AACxD,MAAI,UAAU,KAAK,YAAY,EAAG,QAAO,EAAE,GAAG,OAAO,YAAY,EAAE;AAEnE,QAAM,IAAI,QAAQ,iBAAiB,CAAC;AACpC,QAAM,IAAI,QAAQ,iBAAiB,CAAC;AACpC,MAAI,IAAI;AACR,MAAI,WAAW,GAAG;AACd,UAAM,KAAK,EAAE,IAAI,KAAK,IAAI,EAAE,KAAK,IAAI,UAAU,KAAK,EAAE,IAAI,KAAK,IAAI,EAAE,KAAK,IAAI;AAG9E,UAAM,OAAO,MAAM,QAAQ,qCAAqC,EAAE;AAClE,QAAI,MAAM,GAAG,EAAE,IAAI,MAAM,GAAG,EAAE,IAAI,MAAM,GAAG,EAAE,CAAC,IAAI,MAAM,GAAG,EAAE,CAAC,IAAI;EACtE;AACA,MAAI,SAAS,GAAG;AACZ,UAAM,KAAK,EAAE,IAAI,KAAK,IAAI,EAAE,KAAK,IAAI,QAAQ,KAAK,EAAE,IAAI,KAAK,IAAI,EAAE,KAAK,IAAI;AAC5E,SAAK,MAAM,GAAG,EAAE,IAAI,MAAM,GAAG,EAAE;EACnC;AAEA,SAAO,EAAE,GAAG,YAAY,SAAS;AACrC;AAoBO,SAAS,oBACZ,MACA,IACA,KACM;AA3JV,MAAAA;AA4JI,MAAI,CAAC,MAAM,OAAO,GAAG,aAAa,YAAY,CAAC,GAAG,SAAU,QAAO;AAEnE,QAAM,SAAS,MAAM,KAAK,OAAO;AACjC,QAAM,EAAE,GAAG,WAAW,IAAI,uBAAuB,GAAG,UAAU;IAC1D,cAAc,GAAG;IAAc,aAAa,GAAG;IAC/C,YAAY,GAAG;IAAY,SAAS,oBAAoB,IAAI;EAChE,CAAC;AACD,MAAI,KAAK,KAAK,EAAE,MAAM,QAAQ,IAAI,QAAQ,EAAE,CAAC;AAE7C,QAAM,WAAmB;IACrB,MAAM;IACN,MAAM,MAAM;IACZ,WAAUA,MAAA,KAAK,aAAL,OAAAA,MAAiB,CAAC;EAChC;AACA,MAAI,GAAG,iBAAiB,OAAW,UAAS,eAAe,GAAG;AAC9D,MAAI,GAAG,WAAW,OAAiB,UAAS,SAAS,GAAG;AACxD,MAAI,GAAG,YAAY,OAAgB,UAAS,UAAU,GAAG;AAGzD,wBAAsB,UAAU,eAAe,gBAAgB,GAAG,aAAa,UAAU,CAAC;AAC1F,wBAAsB,UAAU,cAAe,GAAG,UAAU;AAE5D,OAAK,WAAW,CAAC,QAAQ;AACzB,SAAO;AACX;AAOA,SAAS,sBAAsB,MAAc,UAAkB,KAA6C;AACxG,MAAI,QAAQ,UAAa,QAAQ,KAAM;AACvC,yBAAuB,MAAM,UAAU,eAAuB,GAAG,GAAG,EAAE,UAAU,KAAK,CAAC;AAC1F;AChKO,IAAM,qBAA2C,CAAC,MAAM,OAAO,aAAa;AAC/E,QAAM,OAA6B,EAAE,KAAK;AAC1C,aAAW,KAAK,MAAO,KAAI,MAAM,CAAC,MAAM,OAAW,MAAK,CAAC,IAAI,MAAM,CAAC;AACpE,QAAM,MAAM,MAAM,QAAQ,QAAQ,IAAI,SAAS,OAAO,CAAA,MAAK,KAAK,IAAI,IAAK,YAAY,OAAO,CAAC,QAAQ,IAAI,CAAC;AAC1G,MAAI,IAAI,OAAQ,MAAK,WAAW;AAChC,SAAO;AACX;ACjBA,SAAS,IAAI,GAAW,UAA0B;AAE9C,SAAO,KAAK,MAAM,CAAC,MAAM,IAAI,KAAK,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,QAAQ;AACxE;AAIA,SAAS,KAAK,MAAqB,UAA0B;AACzD,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AAClC,UAAMG,OAAM,IAAI,KAAK,CAAC,GAAG,QAAQ;AACjC,QAAI,IAAI,KAAKA,KAAI,WAAW,CAAC,MAAM,GAAc,MAAK;AACtD,SAAKA;EACT;AACA,SAAO;AACX;AAEA,SAAS,MAAM,GAAW,GAAW,GAA6B;AAC9D,SAAO,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;AAClE;AAEA,IAAM,WAAW;AAMV,SAAS,kBAAkB,GAAW,GAAW,WAAmB,GAAW;AAClF,QAAM,SAAwB,CAAC;AAC/B,MAAI;AACJ,WAAS,YAAY;AACrB,UAAQ,QAAQ,SAAS,KAAK,CAAC,OAAO,KAAM,QAAO,KAAK,MAAM,CAAC,CAAC;AAEhE,MAAI,MAAM;AACV,MAAI,IAAI;AACR,QAAM,MAAM,MAAc,WAAW,OAAO,GAAG,CAAC;AAEhD,SAAO,IAAI,OAAO,QAAQ;AACtB,UAAM,MAAM,OAAO,GAAG;AACtB,QAAI,QAAQ,OAAO,QAAQ,KAAK;AAC5B,YAAM,CAAC,GAAG,CAAC,IAAI,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;AACpC,aAAO,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,QAAQ;IACtC,WAAW,QAAQ,KAAK;AACpB,YAAM,CAAC,IAAI,EAAE,IAAI,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;AACtC,YAAM,CAAC,IAAI,EAAE,IAAI,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;AACtC,YAAM,CAAC,GAAG,CAAC,IAAI,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;AACpC,aAAO,MAAM,KAAK,CAAC,IAAI,IAAI,IAAI,IAAI,GAAG,CAAC,GAAG,QAAQ;IACtD,WAAW,QAAQ,KAAK;AACpB,YAAM,CAAC,IAAI,EAAE,IAAI,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;AACtC,YAAM,CAAC,GAAG,CAAC,IAAI,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;AACpC,aAAO,MAAM,KAAK,CAAC,IAAI,IAAI,GAAG,CAAC,GAAG,QAAQ;IAC9C,WAAW,QAAQ,OAAO,QAAQ,KAAK;AACnC,aAAO;IACX;EAEJ;AACA,SAAO;AACX;ACpCA,IAAM,oBAAoB;AAG1B,IAAM,iBAAwC;EAC1C;EAAc;EAAY;EAAc;EAAa;EACrD;EAAiB;EAAe;EAAkB;EAClD;EAAc;EAAK;EAAK;EAAM;EAAM;EACpC;EAAQ;EAAU;EAAe;EACjC;EAAsB;AAC1B;AAkCA,IAAM,oBAAoB;EACtB;EAAe;EAAY;EAAiB;EAAmB;EAC/D;EAAiB;EAAkB;EAAoB;EAAgB;AAC3E;AAGA,IAAM,qBAAqB,CAAC,QAAQ,UAAU,eAAe,WAAW,eAAe,iBAAiB,mBAAmB,kBAAkB;AAW7I,SAAS,SAAS,GAAgC;AAC9C,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,MAAI,OAAO,MAAM,UAAU;AAAE,UAAM,IAAI,WAAW,CAAC;AAAG,WAAO,MAAM,CAAC,IAAI,SAAY;EAAG;AACvF,SAAO;AACX;AAEA,SAAS,IAAI,GAAgC;AACzC,SAAO,OAAO,MAAM,WAAW,IAAI;AACvC;AAGA,SAAS,eAAe,MAAgD;AACpE,QAAM,MAAM,KAAK;AACjB,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,MAAI;AACJ,aAAW,KAAK,oBAAoB;AAChC,QAAI,IAAI,CAAC,MAAM,OAAW,EAAC,OAAA,OAAA,MAAA,MAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;EACrD;AACA,SAAO;AACX;AAEA,SAAS,aAAa,MAAc,QAAsB;AAzH1D,MAAAH,KAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA;AAiII,QAAM,aAAa,KAAK,SAAS;AACjC,QAAM,YAAY,KAAK;AACvB,QAAM,MAAM,CAAC,QAAkB;AAnInC,QAAAA;AAmIsC,WAAA,aAAa,UAAcA,MAAA,KAA8B,GAAG,MAAjC,OAAAA,MAAsC,aAAA,OAAA,SAAA,UAAY,GAAA;EAAA;AAE/G,QAAM,MAAa;IACf,aAAYA,MAAA,IAAI,KAAK,UAAU,MAAnB,OAAAA,MAAwB,OAAO;IAC3C,WAAU,KAAA,SAAS,KAAK,QAAQ,MAAtB,OAAA,KAA2B,OAAO;IAC5C,OAAM,KAAA,KAAK,SAAL,OAAA,KAAa,OAAO;IAC1B,SAAQ,KAAA,KAAK,WAAL,OAAA,KAAe,OAAO;IAC9B,cAAa,KAAA,KAAK,gBAAL,OAAA,KAAoB,OAAO;IACxC,gBAAe,KAAA,SAAS,KAAK,aAAa,MAA3B,OAAA,KAAgC,OAAO;IACtD,cAAa,KAAA,SAAS,KAAK,WAAW,MAAzB,OAAA,KAA8B,OAAO;IAClD,UAAS,KAAA,SAAS,IAAI,SAAS,CAAC,MAAvB,OAAA,KAA4B,OAAO;IAC5C,UAAU,KAAA,aAAa,SAAY,eAAe,IAAI,MAA5C,OAAA,KAAkD,OAAO;EACvE;AACA,aAAW,OAAO,mBAAmB;AACjC,UAAM,KAAI,KAAA,IAAI,GAAG,MAAP,OAAA,KAAY,OAAO,GAAG;AAChC,QAAI,MAAM,OAAW,KAAI,GAAG,IAAI;EACpC;AACA,SAAO;AACX;AAEA,SAAS,YAAY,MAAqB;AAvJ1C,MAAAA,KAAA,IAAA;AAwJI,SAAO;IACH,YAAY,IAAI,KAAK,UAAU;IAC/B,WAAUA,MAAA,SAAS,KAAK,QAAQ,MAAtB,OAAAA,MAA2B;IACrC,MAAM,KAAK;IACX,QAAQ,KAAK;IACb,aAAa,KAAK;IAClB,gBAAe,KAAA,SAAS,KAAK,aAAa,MAA3B,OAAA,KAAgC;IAC/C,cAAa,KAAA,SAAS,KAAK,WAAW,MAAzB,OAAA,KAA8B;EAC/C;AACJ;AAEA,SAAS,QAAQ,GAAiB;AAE9B,QAAM,IAAW,CAAC;AAClB,MAAI,EAAE,SAAS,OAAW,GAAE,OAAO,EAAE;AACrC,MAAI,EAAE,WAAW,OAAW,GAAE,SAAS,EAAE;AACzC,MAAI,EAAE,gBAAgB,OAAW,GAAE,cAAc,EAAE;AACnD,MAAI,EAAE,YAAY,UAAa,EAAE,YAAY,EAAG,GAAE,UAAU,EAAE;AAC9D,aAAW,OAAO,mBAAmB;AACjC,QAAI,EAAE,GAAG,MAAM,OAAW,GAAE,GAAG,IAAI,EAAE,GAAG;EAC5C;AACA,MAAI,EAAE,YAAY,OAAW,GAAE,UAAU,EAAE;AAC3C,SAAO;AACX;AAKA,SAAS,aAAa,GAAU,QAAqC,UAAmC,UAAmD;AApL3J,MAAAA;AAqLI,QAAM,KAAK,EAAE,aAAa,OAAO,EAAE,UAAU,IAAI;AACjD,MAAI,CAAC,GAAI,aAAA,OAAA,SAAA,SAAU,KAAK,uCAAsCA,MAAA,EAAE,eAAF,OAAAA,MAAgB,MAAM,GAAA;AACpF,SAAO;AACX;AAEA,SAAS,WAAW,QAA8D;AAC9E,QAAM,QAAQ,OAAO,KAAK,MAAM;AAChC,SAAO,MAAM,WAAW,IAAI,OAAO,MAAM,CAAC,CAAC,IAAI;AACnD;AAKA,IAAM,2BAA2B;AAU1B,IAAM,2BAA2B;AASxC,SAAS,kBAAkB,WAAmB,UAA0B;AACpE,MAAI,aAAa,KAAK,YAAY,EAAG,QAAO;AAC5C,QAAM,QAAQ;AACd,QAAM,KAAK,YAAY,OAAO,KAAK,aAAa,IAAI;AACpD,QAAM,KAAK,CAAC,YAAY,IAAI;AAC5B,QAAM,KAAK,KAAK,IAAI,KAAK,CAAC;AAC1B,QAAM,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,EAAE,IAAI,IAAI;AAC7C,QAAM,QAAQ,MAAM,KAAK,QAAQ,KAAK,QAAQ,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK;AACpF,MAAI,MAAM,IAAI,KAAK,MAAM,IAAI,EAAG,QAAO;AACvC,QAAM,MAAM,KAAK,GAAG,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,MAAM,KAAK;AAEvD,SAAO,QAAQ,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM;AACnH;AAMO,SAAS,+BAAwC,MAAc,MAAqC;AAvO3G,MAAAA,KAAA,IAAA;AAwOI,QAAM,EAAE,QAAQ,SAAS,oBAA0C,SAAS,IAAI;AAChF,QAAM,WAAW,WAAW,MAAM;AAElC,QAAM,MAAM,EAAE,IAAGA,MAAA,SAAS,KAAK,CAAC,MAAf,OAAAA,MAAoB,GAAG,IAAG,KAAA,SAAS,KAAK,CAAC,MAAf,OAAA,KAAoB,EAAE;AACjE,QAAM,aAAuF,CAAC;AAC9F,QAAM,QAA+C,CAAC,EAAE,OAAO,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;AAClF,MAAI,OAAO;AAEX,QAAM,cAAc,CAAC,SAAiB,MAAmB;AAhP7D,QAAAA;AAiPQ,UAAM,KAAK,aAAa,GAAG,QAAQ,UAAU,QAAQ;AACrD,UAAM,OAAM,MAAA,OAAA,SAAA,GAAI,eAAc;AAC9B,UAAM,QAAQ,EAAE,WAAW;AAC3B,UAAM,YAAWA,MAAA,MAAA,OAAA,SAAA,GAAI,WAAJ,OAAAA,MAAc,MAAM;AACrC,UAAM,QAAQ,QAAQ,CAAC;AACvB,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACrC,YAAM,KAAK,QAAQ,OAAO,CAAC;AAC3B,YAAM,IAAI,MAAA,OAAA,SAAA,GAAI,OAAO,EAAA;AACrB,UAAI,KAAK,EAAE,UAAU;AACjB,mBAAW,KAAK,EAAE,QAAQ,EAAE,UAAU,GAAG,CAAC,OAAO,GAAG,GAAG,OAAO,IAAI,GAAG,IAAI,CAAC,GAAG,OAAO,MAAM,GAAG,IAAI,GAAG,GAAG,IAAI,GAAG,MAAM,CAAC;AACrH,YAAI,KAAK,EAAE,QAAQ;MACvB,WAAW,KAAK,KAAK,EAAE,GAAG;AAGtB,cAAM,QAAS,KAAK,EAAE,QAAQ,IAAK,EAAE,QAAQ,2BAA2B;AACxE,mBAAW,KAAK,EAAE,QAAQ,kBAAkB,OAAO,QAAQ,GAAG,GAAG,CAAC,OAAO,GAAG,GAAG,OAAO,IAAI,GAAG,IAAI,CAAC,GAAG,OAAO,WAAW,MAAM,MAAM,GAAG,IAAI,GAAG,GAAG,IAAI,GAAG,MAAM,CAAC;AAC9J,YAAI,KAAK,QAAQ;MACrB,OAAO;AACH,YAAI,MAAM,IAAI,EAAE,QAAQ,KAAK;MACjC;AACA,UAAI,KAAK,EAAE,iBAAiB,OAAO,MAAM,EAAE,cAAc;AACzD,YAAM,IAAI,EAAE,MAAM,IAAI;IAC1B;EACJ;AAEA,QAAM,OAAO,CAAC,IAAY,gBAA6B;AA1Q3D,QAAAA,KAAAQ,KAAAC;AA2QQ,UAAM,IAAI,aAAa,IAAI,WAAW;AACtC,UAAM,IAAI,SAAS,GAAG,CAAC;AACvB,UAAM,IAAI,SAAS,GAAG,CAAC;AACvB,QAAI,MAAM,QAAW;AAAE,UAAI,IAAI;AAAG,aAAO,MAAM;AAAQ,YAAM,KAAK,EAAE,OAAO,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;IAAG;AACjG,QAAI,MAAM,OAAW,KAAI,IAAI;AAC7B,QAAI,MAAKT,MAAA,SAAS,GAAG,EAAE,MAAd,OAAAA,MAAmB;AAC5B,QAAI,MAAKQ,MAAA,SAAS,GAAG,EAAE,MAAd,OAAAA,MAAmB;AAC5B,UAAM,UAAU,IAAI,GAAG,oBAAoB,CAAC;AAK5C,QAAI,WAAW,GAACC,MAAA,GAAG,aAAH,OAAA,SAAAA,IAAa,QAAQ,aAAY,SAAS,CAAC;AAC3D,QAAI,GAAG,SAAU,YAAW,MAAM,GAAG,SAAU,MAAK,IAAI,CAAC;EAC7D;AAEA,QAAM,YAAY,YAAY,IAAI;AAClC,MAAI,KAAK,SAAU,YAAW,MAAM,KAAK,SAAU,MAAK,IAAI,SAAS;AACrE,QAAM,cAAc,IAAI,KAAK,oBAAoB,CAAC;AAClD,MAAI,eAAe,GAAC,KAAA,KAAK,aAAL,OAAA,SAAA,GAAe,QAAQ,aAAY,aAAa,SAAS;AAG7E,QAAM,SAAS,IAAI,KAAK,UAAU;AAClC,MAAI,WAAW,YAAY,WAAW,OAAO;AACzC,eAAW,KAAK,YAAY;AACxB,YAAM,IAAI,MAAM,EAAE,IAAI,EAAE,MAAM,MAAM,EAAE,IAAI,EAAE;AAC5C,YAAM,QAAQ,WAAW,WAAW,CAAC,IAAI,IAAI,CAAC;AAC9C,QAAE,IAAI,CAAC,EAAE,OAAO,GAAG,GAAG,EAAE,OAAO,EAAE,IAAI,OAAO,EAAE,CAAC;IACnD;EACJ;AAEA,SAAO,QAAQ,MAAM,WAAW,YAAY,QAAQ,QAAQ,GAAG,MAAM;AACzE;AAyLA,SAAS,sBAAsB,MAAc,QAAqC,UAAmC,UAAsE;AACvL,QAAM,QAA0B,CAAC;AACjC,MAAI,MAAM;AACV,QAAM,OAAO,CAAC,IAAY,gBAA6B;AAve3D,QAAAC,KAAA;AAweQ,UAAM,IAAI,aAAa,IAAI,WAAW;AACtC,UAAM,UAAU,IAAI,GAAG,oBAAoB,CAAC;AAG5C,QAAI,WAAW,GAACA,MAAA,GAAG,aAAH,OAAA,SAAAA,IAAa,SAAQ;AACjC,YAAM,KAAK,aAAa,GAAG,QAAQ,UAAU,QAAQ;AACrD,UAAI,IAAI;AACJ,cAAM,QAAQ,EAAE,WAAW,GAAG;AAC9B,cAAM,YAAW,KAAA,GAAG,WAAH,OAAA,KAAa,MAAM,GAAG;AACvC,cAAM,QAAQ,QAAQ,CAAC;AACvB,iBAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACrC,gBAAM,KAAK,QAAQ,OAAO,CAAC;AAC3B,gBAAM,IAAI,GAAG,OAAO,EAAE;AACtB,cAAI,KAAK,EAAE,UAAU;AACjB,kBAAM,WAAW,EAAE,QAAQ;AAC3B,kBAAM,KAAK,EAAE,QAAQ,EAAE,UAAU,SAAS,EAAE,OAAO,OAAO,OAAO,SAAS,MAAM,WAAW,EAAE,CAAC;AAC9F,mBAAO;UACX,WAAW,KAAK,KAAK,EAAE,GAAG;AAEtB,kBAAM,MAAO,KAAK,EAAE,QAAQ,IAAK,EAAE,QAAQ,2BAA2B,GAAG;AACzE,kBAAM,SAAS,MAAM;AACrB,kBAAM,KAAK,EAAE,QAAQ,kBAAkB,KAAK,QAAQ,GAAG,SAAS,KAAK,OAAO,OAAO,WAAW,MAAM,SAAS,MAAM,SAAS,EAAE,CAAC;AAC/H,mBAAO;UACX,OAAO;AACH,oBAAQ,IAAI,EAAE,QAAQ,KAAK;UAC/B;AACA,iBAAO,EAAE,iBAAiB,OAAO,MAAM,EAAE,cAAc;QAC3D;MACJ;IACJ;AACA,QAAI,GAAG,SAAU,YAAW,MAAM,GAAG,SAAU,MAAK,IAAI,CAAC;EAC7D;AACA,OAAK,MAAM,YAAY,IAAI,CAAC;AAC5B,SAAO,EAAE,OAAO,OAAO,IAAI;AAC/B;AAMA,SAAS,YAAY,SAAsB,MAAc,OAAe,SAAiB,OAAO,GAAW;AACvG,QAAM,EAAE,GAAG,GAAG,MAAM,IAAI,QAAQ,iBAAiB,IAAI;AACrD,QAAM,MAAM,KAAK,IAAI,KAAK,GAAG,MAAM,KAAK,IAAI,KAAK,GAAG,KAAK,UAAU;AACnE,SAAO,CAAC,QAAQ,KAAK,QAAQ,KAAK,CAAC,QAAQ,KAAK,QAAQ,KAAK,IAAI,QAAQ,MAAM,KAAK,OAAO,KAAK,IAAI,QAAQ,MAAM,KAAK,OAAO,GAAG;AACrI;AAQA,SAAS,qBAAqB,MAA+C;AA5hB7E,MAAAA,KAAA,IAAA;AA6hBI,SAAO;IACH,SAAQA,MAAA,SAAS,KAAK,CAAC,MAAf,OAAAA,MAAoB,OAAM,KAAA,SAAS,KAAK,EAAE,MAAhB,OAAA,KAAqB;IACvD,OAAM,KAAA,SAAS,KAAK,EAAE,MAAhB,OAAA,KAAqB;EAC/B;AACJ;AAGO,SAAS,8BACZ,MACA,OACA,aACA,MACA,YACA,cACQ;AACR,QAAM,EAAE,QAAQ,SAAS,oBAA0C,SAAS,IAAI;AAChF,QAAM,UAAU,QAAQ,kBAAkB,KAAK,IAAI;AACnD,MAAI,CAAC,SAAS;AAAE,gBAAA,OAAA,SAAA,SAAU,KAAK,4CAAA;AAA+C,WAAO;EAAM;AAE3F,QAAM,WAAW,WAAW,MAAM;AAClC,QAAM,EAAE,OAAO,MAAM,IAAI,sBAAsB,MAAM,QAAQ,UAAU,QAAQ;AAC/E,MAAI,CAAC,MAAM,OAAQ,QAAO,QAAQ,MAAM,CAAC,GAAG,MAAM;AAGlD,QAAM,EAAE,OAAO,aAAa,KAAK,IAAI,qBAAqB,IAAI;AAK9D,QAAM,UAAU,WAAW,WAAW;AACtC,QAAM,UAAU,WAAW,UAAU;AACrC,QAAM,MAAM,CAAC,OAAwB,KAAK,KAAK,QAAQ,IAAK,KAAK,QAAQ;AAKzE,QAAM,SAAS,iBAAiB,UAAU,CAAC,QAAQ;AAEnD,MAAI,QAAQ,YAAY,QAAQ,UAAU;AAItC,UAAM,QAAQ,gBAAgB,QAAQ,OAAO,QAAQ,KAAK;AAC1D,UAAM,SAAS,CAAC,GAAc,MAAsB,cAAc,QAAQ,GAAG,CAAC,IAAI,IAAI,QAAQ,GAAG,CAAC,CAAC,IAAI,EAAE;AACzG,UAAM,OAAO,QAAQ,WAAW,QAAQ,OAAO,QAAQ;AACvD,WAAO,QAAQ,MAAM,uBAAuB,OAAO,SAAS,QAAQ,OAAO,MAAM,QAAQ,QAAQ,IAAI,GAAG,MAAM;EAClH;AAGA,QAAM,IAAI,IAAI,QAAQ,GAAG,CAAC,CAAC;AAC3B,QAAM,OAAO,cAAc,QAAQ,GAAG,CAAC;AACvC,QAAM,aAAa,SACb,MAAM,OAAO,CAAA,MAAK;AAAE,UAAM,IAAI,OAAO,EAAE,UAAU;AAAG,WAAO,KAAK,KAAK,KAAK,QAAQ;EAAa,CAAC,IAChG;AACN,QAAM,aAA+B,WAAW,IAAI,CAAA,OAAM;IACtD,QAAQ,EAAE;IAAQ,OAAO,EAAE;IAAO,WAAW,EAAE;IAC/C,GAAG,YAAY,SAAS,OAAO,EAAE,UAAU,GAAG,EAAE,OAAO,EAAE,SAAS,IAAI;EAC1E,EAAE;AACF,SAAO,QAAQ,MAAM,WAAW,YAAY,QAAQ,QAAQ,GAAG,MAAM;AACzE;AAOA,SAAS,WAAW,KAAiD;AA/lBrE,MAAAA;AAgmBI,QAAM,IAAI,eAAuB,GAAG;AACpC,MAAI,EAAE,SAAA,cAA8B,EAAE,UAAU,UAAU,GAAG;AACzD,UAAM,MAAM,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,CAAC,IAAI,QAAQ,OAAO,GAAG,IAAI,KAAK,MAAM,OAAO,GAAG,IAAI,KAAK,EAAE;AAC7F,UAAM,QAAQ,IAAI,IAAI,CAAA,OAAM,OAAO,GAAG,IAAI,KAAK,CAAC;AAChD,UAAM,OAAO,IAAI,IAAI,CAAA,OAAM,OAAO,GAAG,KAAK,KAAK,CAAC;AAChD,WAAO;MACH,UAAU;MAAM;MAAO,MAAM,EAAE;MAC/B,GAAG,GAAmB;AAClB,YAAI,KAAK,MAAM,CAAC,EAAG,QAAO,KAAK,CAAC;AAChC,iBAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACnC,cAAI,KAAK,MAAM,CAAC,GAAG;AACf,kBAAM,OAAO,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC;AACnC,kBAAM,IAAI,OAAO,KAAK,IAAI,MAAM,IAAI,CAAC,KAAK,OAAO;AACjD,mBAAO,KAAK,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;UAClD;QACJ;AACA,eAAO,KAAK,KAAK,SAAS,CAAC;MAC/B;IACJ;EACJ;AACA,QAAM,IAAI,EAAE,SAAA,aAA8B,QAAOA,MAAA,EAAE,UAAU,CAAC,MAAb,OAAA,SAAAA,IAAgB,KAAK,KAAK,IACrE,EAAE,SAAA,WAA4B,OAAO,EAAE,KAAK,KAAK,IAAK;AAC5D,SAAO,EAAE,UAAU,OAAO,OAAO,CAAC,GAAG,IAAI,MAAM,EAAE;AACrD;AAGA,SAAS,gBAAgB,GAAkB,GAAiC;AACxE,QAAM,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,KAAK,CAAC,IAAI,OAAO,KAAK,EAAE;AACjD,QAAM,MAAqB,CAAC;AAC5B,aAAW,KAAK,IAAK,KAAI,CAAC,IAAI,UAAU,MAAM,IAAI,IAAI,SAAS,CAAC,EAAG,KAAI,KAAK,CAAC;AAC7E,SAAO;AACX;AAKA,IAAM,uBAAuB;AAC7B,IAAM,mCAAmC;AAEzC,SAAS,OAAO,GAAW,GAAmB;AAC1C,QAAM,IAAI,MAAA,IAAM,CAAA;AAChB,SAAO,KAAK,MAAM,IAAI,CAAC,IAAI;AAC/B;AASA,SAAS,uBACL,OACA,SACA,QACA,OACA,MACA,QACA,QACA,OAAO,GACC;AACR,QAAM,OAAO,KAAK,IAAI,QAAQ,cAAc,sBAAsB,GAAG;AACrE,QAAM,SAAS,CAAC,SAA0B,QAAQ,KAAK,QAAQ,QAAQ;AAEvE,QAAM,MAAgB,CAAC;AACvB,aAAW,KAAK,OAAO;AACnB,UAAM,WAAmB,CAAC,EAAE,OAAO,GAAG,GAAG,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,UAAU,IAAI,CAAC;AAC/E,UAAM,IAAI,kBAAkB,EAAE,QAAQ,QAAQ;AAE9C,UAAM,WAAW,CAAC,MAAc,SAAoC;AAChE,YAAM,EAAE,GAAG,GAAG,MAAM,IAAI,QAAQ,iBAAiB,IAAI;AACrD,YAAM,MAAM,KAAK,IAAI,KAAK,GAAG,MAAM,KAAK,IAAI,KAAK;AAEjD,aAAO;QACH;QACA,OAAO;UACH;YAAA;;UAAwB,GAAG,CAAC,OAAO,IAAI,OAAO,KAAK,CAAC,GAAG,OAAO,IAAI,OAAO,KAAK,CAAC,CAAC;UAChF;YAAA;;UAAqB,GAAG,OAAO,QAAQ,MAAM,KAAK,IAAI,CAAC;QAC3D;MACJ;IACJ;AAEA,UAAM,MAAgC,CAAC;AAIvC,UAAM,QAA0C,CAAC;AACjD,UAAM,SAAS,CAAC,MAAc,SAAuB;AACjD,UAAI,KAAK,SAAS,MAAM,IAAI,CAAC;AAC7B,UAAI,OAAQ,OAAM,KAAK,EAAE,MAAM,OAAO,OAAO,IAAI,IAAI,IAAI,EAAE,CAAC;IAChE;AAEA,WAAO,OAAO,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;AACpC,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACnC,YAAM,KAAK,MAAM,IAAI,CAAC,GAAG,KAAK,MAAM,CAAC;AACrC,YAAM,KAAK,OAAO,GAAG,EAAE,GAAG,KAAK,OAAO,GAAG,EAAE;AAC3C,YAAM,IAAI,KAAK,IAAI,kCAAkC,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC,CAAC;AACrG,eAAS,IAAI,GAAG,KAAK,GAAG,KAAK;AACzB,cAAM,IAAI,IAAI;AACd,cAAM,IAAI,KAAK,KAAK,KAAK;AACzB,eAAO,OAAO,GAAG,CAAC,GAAG,CAAC;MAC1B;IACJ;AAKA,8BAA0B,GAAG;AAE7B,UAAM,YAAqE,EAAE,WAAW,IAAI;AAC5F,QAAI,SAAS,OAAW,WAAU,OAAO;AACzC,UAAM,UAAgC,EAAE,UAAU;AAIlD,QAAI,EAAE,MAAM,QAAS,QAAO,OAAO,SAAS,EAAE,MAAM,OAAO;AAC3D,QAAI,UAAU,MAAM,KAAK,CAAA,MAAK,EAAE,UAAU,CAAC,GAAG;AAI1C,YAAM,KAAsE,EAAE,WAAW,MAAM;AAC/F,UAAI,SAAS,OAAW,IAAG,OAAO;AAClC,cAAQ,UAAU;IACtB;AAEA,QAAI,KAAK,OAAO,QAAQC,eAAAC,gBAAAA,gBAAA,EAAE,EAAA,GAAM,WAAW,EAAE,KAAK,CAAA,GAAM,kBAAkB,EAAE,SAAS,CAAA,GAA7D,EAAgE,QAAQ,CAAA,GAAG,CAAC,CAAC,CAAC;EAC1G;AACA,SAAO;AACX;AAKA,SAAS,WAAW,OAAoC;AACpD,QAAM,IAA0B,CAAC;AACjC,MAAI,MAAM,SAAS,OAAW,GAAE,OAAO,MAAM;AAC7C,MAAI,MAAM,WAAW,OAAW,GAAE,SAAS,MAAM;AACjD,MAAI,MAAM,gBAAgB,OAAW,GAAE,cAAc,MAAM;AAC3D,MAAI,MAAM,YAAY,OAAW,GAAE,UAAU,MAAM;AACnD,aAAW,OAAO,mBAAmB;AACjC,QAAI,MAAM,GAAG,MAAM,OAAW,GAAE,GAAG,IAAI,MAAM,GAAG;EACpD;AAEA,SAAO;AACX;AAGA,SAAS,kBAAkB,WAAsD;AAC7E,SAAO,YAAY,EAAE,CAAC,UAAU,GAAG,yBAAyB,IAAI,CAAC;AACrE;AAKA,SAAS,WAAc,YAA8B,QAA4B,UAAoC;AACjH,MAAI,CAAC,WAAW,QAAQ;AAAE,gBAAA,OAAA,SAAA,SAAU,KAAK,+BAAA;AAAkC,WAAO,CAAC;EAAG;AAEtF,QAAM,UAAU,oBAAI,IAA8D;AAClF,aAAW,KAAK,YAAY;AAIxB,UAAM,MAAM,KAAK,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC,EAAE,SAAS,CAAC;AACnD,UAAM,QAAQ,kBAAkB,EAAE,QAAQ,EAAE,CAAC;AAC7C,UAAM,QAAQ,QAAQ,IAAI,GAAG;AAC7B,QAAI,MAAO,OAAM,KAAK;QACjB,SAAQ,IAAI,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,WAAW,EAAE,UAAU,CAAC;EAC9E;AAEA,QAAM,MAAgB,CAAC;AACvB,aAAW,EAAE,OAAO,GAAG,UAAU,KAAK,QAAQ,OAAO,GAAG;AACpD,QAAI,KAAK,OAAO,QAAQA,gBAAAA,gBAAAA,gBAAA;MACpB;IAAA,GAAM,WAAW,KAAK,CAAA,GAAM,kBAAkB,SAAS,CAAA,GAEnD,MAAM,YAAY,SAAY,EAAE,SAASA,gBAAA,CAAA,GAAK,MAAM,OAAA,EAAU,IAAI,CAAC,CAAA,GACxE,CAAC,CAAC,CAAC;EACV;AACA,SAAO;AACX;AAIA,SAAS,QAAW,MAAc,UAAoB,QAA+B;AACjF,QAAM,SAA+B,CAAC;AACtC,aAAW,KAAK,OAAO,KAAK,IAAI,GAAG;AAC/B,QAAI,MAAM,UAAU,MAAM,cAAc,eAAe,QAAQ,CAAC,MAAM,GAAI;AAC1E,WAAO,CAAC,IAAK,KAA8B,CAAC;EAChD;AACA,MAAI,OAAO,SAAS,OAAO,OAAO,UAAU,UAAU;AAClD,UAAM,QAAQA,gBAAA,CAAA,GAAM,OAAO,KAAA;AAC3B,WAAO,MAAM,aAAa;AAC1B,QAAI,OAAO,KAAK,KAAK,EAAE,OAAQ,QAAO,QAAQ;QAAY,QAAO,OAAO;EAC5E;AACA,SAAO,OAAO,KAAK,QAAQ,QAAQ;AACvC;AAiBO,SAAS,sBAAsB,MAAc,IAA8B,KAA2B;AACzG,MAAI,EAAC,MAAA,OAAA,SAAA,GAAI,WAAW,QAAO;AAC3B,MAAI,CAAC,IAAI,QAAQ;AAAE,QAAI,SAAS,KAAK,+DAA0D;AAAG,WAAO;EAAM;AAC/G,SAAO,+BAAuC,MAAM,EAAE,QAAQ,IAAI,QAAQ,UAAU,IAAI,SAAS,CAAC;AACtG;AAGO,SAAS,yBAAyB,MAAc,KAAmB,OAA2B,aAA+C,YAAmC,cAAsC;AACzN,MAAI,CAAC,IAAI,QAAQ;AAAE,QAAI,SAAS,KAAK,kCAAkC;AAAG,WAAO;EAAM;AACvF,SAAO,8BAAsC,MAAM,OAAO,aAAa,EAAE,QAAQ,IAAI,QAAQ,UAAU,IAAI,SAAS,GAAG,YAAY,YAAY;AACnJ;AC7wBO,IAAM,mBAAmB;;EAE5B,UAAU;;EAEV,MAAM;;EAEN,UAAU;;EAEV,OAAO;;EAEP,UAAU;AACd;AAIA,IAAM,WAAW;AAGjB,SAAS,SAAS,MAAgC;AAC9C,SAAO,OAAO,OAAO,MAAM,WAAW,QAAQ;AAClD;AAgFA,SAAS,QAAQ,MAAiD;AAC9D,SAAO,KAAK,KAAK,CAAC,MAAkB,aAAa,KAAK;AAC1D;AAUO,SAAS,kBAAkB,QAA8B,QAAgC;AAC5F,QAAM,MAAM,SAAS,SAAS,MAAM;AACpC,SAAO;IACH,MAAM,CAAC,MAAwB,SAA2B,SAA+B;AACrF,YAAM,UAAU,SAAS,IAAI;AAC7B,UAAI,UAAA,OAAA,SAAA,OAAQ,QAAQ;AAAE,eAAO,OAAO,EAAE,MAAM,MAAM,MAAM,QAAQ,CAAC;AAAG;MAAQ;AAC5E,UAAI,UAAA,OAAA,SAAA,OAAQ,SAAU;AACtB,cAAQ,KAAK,MAAM,OAAO,MAAM,SAAS,GAAG,IAAI;IACpD;IACA,OAAO,CAAC,MAAwB,SAA2B,SAA+B;AACtF,YAAM,UAAU,SAAS,IAAI;AAC7B,YAAM,QAAQ,QAAQ,IAAI;AAC1B,UAAI,UAAA,OAAA,SAAA,OAAQ,SAAS;AAAE,eAAO,QAAQ,EAAE,MAAM,MAAM,MAAM,SAAS,MAAM,CAAC;AAAG;MAAQ;AACrF,UAAI,UAAA,OAAA,SAAA,OAAQ,UAAW;AACvB,cAAQ,MAAM,MAAM,OAAO,MAAM,SAAS,GAAG,IAAI;IACrD;EACJ;AACJ;ACzJA,IAAM,eAAe;AAed,SAAS,iBAAiB,KAAmC;AAChE,MAAI;AACA,WAAO,EAAE,UAAU,iBAAiB,GAAG,EAAE;EAC7C,SAAQ,GAAA;AACJ,WAAO,EAAE,UAAU,CAAC,EAAE;EAC1B;AACJ;AASO,SAAS,0BAA0B,KAAc,OAAqB;AACzE,QAAM,EAAE,SAAS,IAAI,iBAAiB,GAAG;AACzC,MAAI,CAAC,SAAS,OAAQ;AAEtB,QAAM,QAAQ,SAAS,MAAM,GAAG,YAAY;AAC5C,QAAM,OAAO,SAAS,SAAS,MAAM;AACrC,UAAQ;IACJ,QAAQ,4DACN,SAAS,SAAS,YAAY,SAAS,WAAW,IAAI,KAAK,OAAO,QAClE,MAAM,IAAI,CAAA,MAAK,SAAS,CAAC,EAAE,KAAK,IAAI,KACnC,OAAO,IAAI,oBAAe,OAAO,UAAU,MAC5C;EAIqF;AAC/F;;;ACxCO,SAAS,uBAAuB,MAAc,IAAqC,KAA2B;AACjH,MAAI,CAAC,GAAI,QAAO;AAIhB,SAAO,KAAK;AAOZ,MAAI,IAAI;AACR,MAAI;IAAW;IAAG,GAAG;;IAAmB;EAAI;AAC5C,MAAI,kBAAkB,GAAA,SAAwB,eAAe,GAAG,KAAK,GAAG,GAAG;AAC3E,MAAI,kBAAkB,GAAA,QAAuB,GAAG,MAAM,GAAG;AACzD,MAAI,kBAAkB,GAAA,UAAyB,GAAG,QAAQ,GAAG;AAC7D,MAAI;IAAW;IAAG,GAAG;;IAAmB;EAAK;AAE7C,MAAI,uBAAuB,GAAG,SAAS,GAAG;AAItC,QAAI;MAAW;MAAG,GAAG;;MAAmB;IAAI;AAC5C,QAAI,kBAAkB,GAAA,aAA4B,GAAG,WAAW,GAAG;AACnE,QAAI;MAAW;MAAG,GAAG;;MAAmB;IAAK;EACjD,OAAO;AACH,QAAI,kBAAkB,GAAA,aAA4B,GAAG,WAAW,GAAG;EACvE;AAOA,MAAI,MAAM,QAAQ,KAAK,IAAI;AACvB,MAAE,KAAK,KAAK;AACZ,WAAO,KAAK;EAChB;AACA,SAAO;AACX;AAIA,SAAS,uBAAuB,WAAsD;AAClF,MAAI,CAAC,aAAa,OAAO,cAAc,SAAU,QAAO;AACxD,QAAM,MAAM;AACZ,MAAI,IAAI,WAAY,QAAO;AAC3B,SAAO,MAAM,QAAQ,IAAI,SAAS,KAAK,IAAI,UAAU,KAAK,CAAA,OAAM,GAAG,cAAc,GAAG,SAAS;AACjG;AASA,SAAS,eAAe,KAAyE;AAC7F,SAAO;AACX;AAGA,SAAS,kBACL,OAAe,MACf,KAAoC,KAC9B;AACN,MAAI,QAAQ,OAAW,QAAO;AAE9B,QAAM,IAAI,eAAoB,GAAG;AACjC,MAAI,EAAE,SAAA,UAA0B;AAC5B,WAAO,EAAE,MAAM,KAAK,WAAW,EAAE,OAAO,YAAY,MAAM,EAAE,OAAO,MAAS,EAAE,GAAG,UAAU,CAAC,KAAK,EAAE;EACvG;AACA,MAAI,EAAE,SAAA,YAA4B;AAC9B,UAAM,SAAc,EAAE,WAAW,EAAE,UAAU,IAAI,CAAA,OAAM,aAAa,IAAI,YAAY,MAAM,GAAG,OAAO,MAAS,CAAC,CAAC,EAAE;AACjH,QAAI,EAAE,WAAY,QAAO,aAAa;AACtC,QAAI,EAAE,SAAS,OAAW,QAAO,OAAO,EAAE;AAC1C,WAAO;MACH,MAAM;MACN,SAAS,EAAE,WAAW,OAAO;MAC7B,UAAU,CAAC,KAAK;IACpB;EACJ;AACA,SAAO;AACX;AAWA,SAAS,WAAW,OAAe,KAAuC,QAAyB;AAC/F,MAAI,QAAQ,OAAW,QAAO;AAC9B,QAAM,IAAI,eAAuB,GAAG;AACpC,QAAM,OAAO,CAAC,UAA0B,SAAS,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI;AAE1E,MAAI,EAAE,SAAA,SAA0B,QAAO;AACvC,MAAI,EAAE,SAAA,UAA0B;AAC5B,QAAI,EAAE,MAAM,CAAC,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAG,QAAO;AACjD,WAAO,EAAE,MAAM,KAAK,WAAW,EAAE,OAAO,EAAE,WAAW,KAAK,EAAE,KAAK,EAAE,EAAE,GAAG,UAAU,CAAC,KAAK,EAAE;EAC9F;AACA,MAAI,EAAE,SAAA,YAA4B;AAC9B,UAAM,SAAc,EAAE,WAAW,EAAE,UAAU,IAAI,CAAA,OAAM,aAAa,IAAI,EAAE,WAAW,KAAK,GAAG,KAAe,EAAE,CAAC,CAAC,EAAE;AAClH,QAAI,EAAE,SAAS,OAAW,QAAO,OAAO,EAAE;AAC1C,WAAO;MACH,MAAM;MACN,SAAS,EAAE,WAAW,OAAO;MAC7B,UAAU,CAAC,KAAK;IACpB;EACJ;AACA,SAAO;AACX;AC7FO,SAAS,0BAA0B,MAAc,KAAmB,WAA0C;AApDrH,MAAAC,KAAA,IAAA;AAqDI,MAAI,KAAK,SAAS,WAAS,MAAAA,MAAA,KAAK,YAAL,OAAA,SAAAA,IAAc,UAAd,OAAA,SAAA,GAAqB,aAAY,aAAa;AACrE,UAAM,WAAW,UAAU,KAAK,QAAQ,MAAM,MAAM;AACpD,QAAI,OAAO,aAAa,YAAY,YAAY,CAAC,IAAI,mBAAmB,IAAI,QAAQ,GAAG;AACnF,UAAI,mBAAmB,IAAI,UAAU,UAAU,QAAQ,CAAC;IAC5D;EACJ;AACA,GAAA,KAAA,KAAK,aAAL,OAAA,SAAA,GAAe,QAAQ,CAAA,MAAK,0BAA0B,GAAG,KAAK,SAAS,CAAA;AAC3E;AAYO,SAAS,mBACZ,MACA,aACA,YACA,SACA,KACM;AAON,QAAM,YAAY,kBAAkB,MAAM,WAAW;AAKrD,MAAI,OAAO,KAAK,OAAO,SAAU,QAAO,KAAK;AAI7C,QAAM,EAAE,OAAO,SAAS,OAAO,QAAQ,IAAI,uBAAuB,WAAW;AAK7E,MAAI,YAAoB;AACxB,cAAY,uBAAuB,WAAW,SAAS,GAAG;AAC1D,QAAM,eAAuB,EAAE,MAAM,KAAK,IAAI,SAAS,UAAU,CAAC,SAAS,EAAE;AAK7E,MAAI,eAAuB,EAAE,MAAM,KAAK,IAAI,YAAY,UAAU,CAAC,YAAY,EAAE;AACjF,MAAI,UAAU,cAAc,OAAW,cAAa,YAAY,UAAU;AAC1E,MAAI,UAAU,YAAY,OAAW,cAAa,UAAU,UAAU;AACtE,MAAI,SAAS;AACT,WAAO,aAAa;AACpB,mBAAe,uBAAuB,cAAc,SAAS,GAAG;AAChE,iBAAa,KAAK;EACtB;AACA,SAAO;AACX;AAkBA,SAAS,kBAAkB,MAAc,aAAyD;AArIlG,MAAAA,KAAA,IAAA;AAsII,QAAM,MAAiB,CAAC;AAMxB,MAAI,iBAAiB;AAIrB,QAAM,UAAUA,MAAA,KAAK,YAAL,OAAA,SAAAA,IAAoD;AACpE,MAAI,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,OAAO,SAAS,GAAG;AACzE,UAAM,MAA8B,OAAO;AAC3C,UAAM,eAAe,IAAI,KAAK,CAAA,OAAM,GAAG,SAAU,GAAG,MAAc,SAAS;AAC3E,QAAI,cAAc;AACd,YAAM,iBAAiB,mBAAmB,MAA8B;AACxE,YAAM,WAAW,IAAI,IAAI,CAAA,OAAM;AAC3B,cAAM,IAAK,GAAG,SAAS,CAAC;AACxB,cAAM,WAAgB,CAAC;AACvB,YAAI,EAAE,cAAc,OAAW,UAAS,YAAY,EAAE;AACtD,YAAI,kBAAkB,EAAE,WAAW,OAAW,UAAS,SAAS,EAAE;AAClE,cAAM,UAAe,EAAE,OAAO,SAAS;AACvC,YAAI,GAAG,SAAS,OAAW,SAAQ,OAAO,GAAG;AAC7C,YAAI,GAAG,WAAW,OAAW,SAAQ,SAAS,GAAG;AACjD,YAAK,GAAW,eAAe,OAAW,SAAQ,aAAc,GAAW;AAC3E,YAAK,GAAW,cAAc,OAAW,SAAQ,YAAa,GAAW;AACzE,eAAO;MACX,CAAC;AACD,YAAM,cAAmB,EAAE,WAAW,SAAS;AAC/C,UAAK,OAAe,WAAY,aAAY,aAAa;AAIzD,YAAM,UAAW,OAAe;AAChC,UAAI,YAAY,OAAW,aAAY,OAAO;AAC9C,UAAI,UAAU,EAAE,WAAW,YAAY;AAEvC,YAAM,sBAAsB,IAAI,KAAK,CAAA,OAAM;AACvC,cAAM,IAAK,GAAG,SAAS,CAAC;AACxB,eAAO,EAAE,WAAW,UAAa,EAAE,UAAU;MACjD,CAAC;AACD,YAAM,WAAW,IAAI,IAAI,CAAA,OAAM;AAC3B,cAAM,IAAK,GAAG,SAAS,CAAC;AACxB,cAAM,WAAgB,CAAC;AACvB,YAAI,EAAE,WAAW,OAAW,UAAS,SAAS,EAAE;AAChD,YAAI,EAAE,UAAU,OAAW,UAAS,QAAQ,EAAE;AAI9C,YAAI,EAAE,WAAW,WAAc,CAAC,kBAAkB,qBAAsB,UAAS,SAAS,EAAE;AAE5F,cAAM,UAAe,EAAE,OAAO,SAAS;AACvC,YAAI,GAAG,SAAS,OAAW,SAAQ,OAAO,GAAG;AAC7C,YAAI,GAAG,WAAW,OAAW,SAAQ,SAAS,GAAG;AACjD,eAAO;MACX,CAAC;AACD,YAAM,gBAAgB,SAAS,MAAM,CAAA,OAAM,OAAO,KAAK,GAAG,KAAe,EAAE,WAAW,CAAC;AACvF,UAAI,eAAe;AACf,eAAQ,KAAK,QAAgB;AAC7B,YAAI,KAAK,WAAW,OAAO,KAAK,KAAK,OAAO,EAAE,WAAW,EAAG,QAAO,KAAK;MAC5E,OAAO;AAGH,cAAM,cAAmB,EAAE,WAAW,SAAS;AAC/C,YAAI,YAAY,OAAW,aAAY,OAAO;AAC7C,aAAK,QAAgB,YAAY;MACtC;AACA,uBAAiB;IACrB;EACJ;AAOA,QAAM,8BAA6B,eAAA,OAAA,SAAA,YAAa,eAAc;AAC9D,QAAM,yBAAyB,kBAAkB;AAOjD,QAAM,8BAA8B,kBAAkB,qBAAoB,KAAA,KAAK,YAAL,OAAA,SAAA,GAAoD,cAAoB,MAAS,KACpJ,kBAAkB,qBAAoB,KAAA,IAAI,YAAJ,OAAA,SAAA,GAAmD,cAAoB,MAAS;AAC7H,MAAI,OAAO,KAAK,cAAc,UAAU;AACpC,UAAM,QAAQ,qBAAqB,KAAK,SAAS;AACjD,QAAI,wBAAwB;AAIxB,UAAI,MAAM,cAAc,QAAW;AAC/B,YAAI,MAAM,KAAM,MAAK,YAAY,MAAM;YAClC,QAAO,KAAK;MACrB,WAAW,oBAAoB,KAAK,SAAS,GAAG;AAG5C,eAAO,KAAK;MAChB,WAAW,+BAA+B,mBAAmB,KAAK,SAAS,GAAG;AAI1E,eAAO,KAAK;MAChB;IACJ,WAAW,MAAM,WAAW;AACxB,UAAI,YAAY,MAAM;AACtB,UAAI,MAAM,KAAM,MAAK,YAAY,MAAM;UAClC,QAAO,KAAK;IACrB;EACJ,WAAW,KAAK,aAAa,OAAO,KAAK,cAAc,YAAY,CAAC,MAAM,QAAQ,KAAK,SAAS,KAClF,CAAE,KAAK,UAAkB,WAAW;AAG9C,UAAM,UAAW,KAAK,UAAkD;AACxE,UAAM,YAAY,CAAC,EAAE,WAAW,OAAO,YAAY;AACnD,UAAM,QAAS,YAAY,UAAU,KAAK;AAC1C,UAAM,SAAS,CAAC,QAAkC,YAAY,EAAE,OAAO,IAAI,IAAI;AAC/E,QAAI,MAAM,QAAS,MAAc,SAAS,GAAG;AACzC,YAAM,OAAgCC,gBAAA,CAAA,GAAK,KAAA;AAC3C,aAAO,KAAK;AACZ,YAAM,UAAU,OAAO,KAAK,IAAI,EAAE,SAAS;AAC3C,UAAI,wBAAwB;AAGxB,YAAI,QAAS,MAAK,YAAY,OAAO,IAAI;YACpC,QAAO,KAAK;MACrB,OAAO;AACH,YAAI,YAAY,OAAO,EAAE,WAAY,MAAc,UAAU,CAAC;AAC9D,YAAI,QAAS,MAAK,YAAY,OAAO,IAAI;YACpC,QAAO,KAAK;MACrB;IACJ;EACJ;AAEA,SAAO;AACX;AAIA,SAAS,mBAAmB,GAAoB;AAC5C,QAAM,KAAK;AACX,MAAI,QAAQ;AACZ,MAAI,WAAW;AACf,MAAI;AACJ,SAAQ,IAAI,GAAG,KAAK,CAAC,GAAI;AACrB;AACA,QAAI,EAAE,CAAC,MAAM,SAAU,YAAW;EACtC;AACA,SAAO,UAAU,KAAK;AAC1B;AAKA,SAAS,oBAAoB,GAAoB;AAC7C,QAAM,KAAK;AACX,QAAM,MAA6C,CAAC;AACpD,MAAI;AACJ,SAAQ,IAAI,GAAG,KAAK,CAAC,EAAI,KAAI,KAAK,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,CAAC;AAC5D,MAAI,IAAI,WAAW,EAAG,QAAO;AAC7B,MAAI,IAAI,CAAC,EAAE,SAAS,YAAa,QAAO;AACxC,MAAI,IAAI,CAAC,EAAE,SAAS,SAAU,QAAO;AACrC,QAAM,OAAO,oBAAoB,KAAK,IAAI,CAAC,EAAE,IAAI;AACjD,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,OAAO,KAAK,CAAC,EAAE,MAAM,QAAQ,EAAE,OAAO,OAAO,EAAE,IAAI,MAAM;AAC/D,SAAO,KAAK,UAAU,KAAK,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM;AAC9F;AAeA,SAAS,qBAAqB,GAAkD;AAC5E,QAAM,MAA6C,CAAC;AACpD,QAAM,KAAK;AACX,MAAI;AACJ,SAAQ,IAAI,GAAG,KAAK,CAAC,EAAI,KAAI,KAAK,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,CAAC;AAE5D,MAAI,CAAC,IAAI,OAAQ,QAAO,EAAE,MAAM,KAAK,OAAU;AAI/C,MAAI,IAAI,MAAM,CAAA,MAAK,EAAE,SAAS,WAAW,GAAG;AACxC,WAAO,EAAE,WAAW,IAAI,IAAI,CAAA,MAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;EACtD;AAEA,QAAM,UAAU,IAAI,CAAC;AACrB,QAAM,WAAW,IAAI,IAAI,SAAS,CAAC;AAMnC,MAAI,SAAS,SAAS,eAAe,QAAQ,SAAS,aAAa;AAC/D,UAAM,cAAc,mBAAmB,SAAS,IAAI;AACpD,UAAM,aAAa,mBAAmB,QAAQ,IAAI;AAClD,UAAM,KAAK,CAAC,YAAY,CAAC;AACzB,UAAM,KAAK,CAAC,YAAY,CAAC;AACzB,UAAM,SAAS,WAAW,CAAC,IAAI;AAC/B,UAAM,SAAS,WAAW,CAAC,IAAI;AAG/B,QAAI,WAAW,KAAK,WAAW,EAAG,QAAO,EAAE,MAAM,EAAE;AAInD,UAAM,oBAAoB,eAAe,KAAK,MAAM,KAAK,MAAM,IAAI,MAAM,CAAC,EAAE,IAAI,CAAA,MAAK,EAAE,IAAI,EAAE,KAAK,EAAE;AACpG,WAAO,EAAE,WAAW,eAAe,SAAS,MAAM,SAAS,KAAK,MAAM,kBAAkB;EAC5F;AAIA,MAAI,SAAS,SAAS,YAAa,QAAO,EAAE,MAAM,EAAE;AAGpD,QAAM,SAAwB,CAAC;AAC/B,MAAI,IAAI;AACR,SAAO,IAAI,IAAI,UAAU,IAAI,CAAC,EAAE,SAAS,aAAa;AAClD,WAAO,KAAK,IAAI,CAAC,EAAE,IAAI;AACvB;EACJ;AACA,MAAI,CAAC,OAAO,OAAQ,QAAO,EAAE,MAAM,EAAE;AAErC,QAAM,OAAO,IAAI,MAAM,CAAC,EAAE,IAAI,CAAA,MAAK,EAAE,IAAI,EAAE,KAAK,EAAE;AAClD,SAAO;IACH,WAAW,OAAO,KAAK,EAAE;IACzB,MAAM,QAAQ;EAClB;AACJ;AAEA,SAAS,mBAAmB,aAAuC;AAC/D,QAAM,IAAI,uBAAuB,KAAK,WAAW;AACjD,MAAI,CAAC,EAAG,QAAO,CAAC,GAAG,CAAC;AACpB,QAAM,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,EAAE,OAAO,OAAO,EAAE,IAAI,MAAM;AAC5D,SAAO,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;AACtC;AAOA,SAAS,uBAAuB,IAG9B;AACE,MAAI,CAAC,GAAI,QAAO,CAAC;AACjB,QAAM,gBAAgB,mBAAmB,GAAG,SAAS;AACrD,QAAM,sBAAsB,GAAG,WAAW,UAAa,GAAG,UAAU;AAEpE,QAAM,QAA6B,CAAC;AACpC,QAAM,QAA6B,CAAC;AAEpC,MAAI,GAAG,cAAc,OAAW,OAAM,YAAY,GAAG;AAGrD,MAAI,iBAAiB,GAAG,WAAW,OAAW,OAAM,SAAS,GAAG;AAEhE,MAAI,GAAG,WAAW,OAAW,OAAM,SAAS,GAAG;AAC/C,MAAI,GAAG,UAAU,OAAW,OAAM,QAAQ,GAAG;AAC7C,MAAI,GAAG,SAAS,OAAW,OAAM,OAAO,GAAG;AAI3C,MAAI,GAAG,WAAW,WAAc,CAAC,iBAAiB,qBAAsB,OAAM,SAAS,GAAG;AAE1F,SAAO;IACH,OAAO,OAAO,KAAK,KAAK,EAAE,SAAS,QAAQ;IAC3C,OAAO,OAAO,KAAK,KAAK,EAAE,SAAS,QAAQ;EAC/C;AACJ;AAUA,SAAS,mBAAmB,eAA0D;AAClF,MAAI,CAAC,iBAAiB,OAAO,kBAAkB,SAAU,QAAO;AAChE,QAAM,MAAM;AACZ,MAAI,IAAI,WAAY,QAAO;AAC3B,MAAI,MAAM,QAAQ,IAAI,SAAS,GAAG;AAC9B,WAAO,IAAI,UAAU,KAAK,CAAA,OAAM,GAAG,cAAc,GAAG,SAAS;EACjE;AACA,SAAO;AACX;ACxYO,SAAS,wBAAwB,MAAc,IAAsC,KAA2B;AACnH,SAAO,cAAc,MAAM,IAAI,KAAK,MAAM;AAC9C;AAEO,SAAS,0BAA0B,MAAc,IAAwC,KAA2B;AACvH,SAAO,cAAc,MAAM,IAAI,KAAK,QAAQ;AAChD;AAOA,SAAS,cAAc,MAAc,IAAsC,KAAmB,MAAiC;AAC3H,MAAI,CAAC,GAAI,QAAO;AAEhB,QAAM,KAAK,MAAM,KAAK,MAAM;AAC5B,QAAM,MAAM,sBAAsB,IAAI,IAAI,GAAG;AAC7C,MAAI,KAAK,KAAK,GAAG;AACjB,OAAK,IAAI,IAAI,UAAU,KAAK;AAC5B,SAAO;AACX;AAEA,SAAS,sBAAsB,IAA0B,IAAY,KAA2B;AAC5F,QAAM,MAAc;IAChB,MAAM,GAAG,SAAS,eAAe,SAAS,mBAAmB;IAC7D;EACJ;AAQA,MAAI,GAAG,SAAS,eAAe,QAAQ;AACnC,iBAAa,KAAK,MAAM,MAAM,GAAG,KAAK;AACtC,iBAAa,KAAK,MAAM,MAAM,GAAG,GAAG;EACxC,OAAO;AACH,iBAAa,KAAK,MAAM,MAAM,GAAG,MAAM;AACvC,oBAAgB,KAAK,KAAK,GAAG,MAAM;AACnC,iBAAa,KAAK,MAAM,MAAM,GAAG,KAAK;EAC1C;AACA,MAAI,GAAG,cAAmB,KAAI,gBAAgB,GAAG;AACjD,MAAI,GAAG,aAAmB,KAAI,eAAe,GAAG;AAChD,MAAI,GAAG,kBAAmB,KAAI,oBAAoB,GAAG;AAMrD,MAAI,WAAW,kBAAkB,GAAG,OAAO,GAAG;AAC9C,SAAO;AACX;AAKA,SAAS,aAAa,KAAa,OAAe,OAAe,KAA6C;AAlG9G,MAAAD,KAAA,IAAA;AAmGI,QAAM,OAAO,eAAuB,GAAG;AACvC,MAAI,KAAK,SAAA,SAA0B;AACnC,MAAI,KAAK,SAAA,UAA0B;AAC/B,QAAI,KAAK,IAAI,OAAO,KAAK,MAAM,CAAC,CAAC;AACjC,QAAI,KAAK,IAAI,OAAO,KAAK,MAAM,CAAC,CAAC;AACjC;EACJ;AACA,QAAM,cAAc,CAAC,QAA0E;AAC3F,UAAM,QAAmE;MACrE,WAAW,KAAK,UAAU,IAAI,CAAA,OAAM;AAChC,cAAM,SAAqB,EAAE,MAAM,GAAG,MAAM,OAAO,MAAM,QAAQ,GAAG,KAAK,IAAI,GAAG,MAAM,GAAG,IAAI,OAAU;AACvG,YAAI,GAAG,WAAW,OAAW,QAAO,SAAS,GAAG;AAChD,eAAO;MACX,CAAC;IACL;AACA,QAAI,KAAK,SAAS,OAAW,OAAM,OAAO,KAAK;AAC/C,WAAO;EACX;AACA,QAAM,WAAWA,MAAA,IAAI,YAAJ,OAAAA,MAAuD,CAAC;AACzE,UAAQ,KAAK,IAAI,YAAY,CAAC;AAC9B,UAAQ,KAAK,IAAI,YAAY,CAAC;AAC9B,MAAI,UAAU;AACd,QAAM,YAAW,KAAA,KAAK,SAAL,OAAA,MAAa,KAAA,KAAK,UAAU,CAAC,MAAhB,OAAA,SAAA,GAAmB;AACjD,MAAI,MAAM,QAAQ,QAAQ,GAAG;AACzB,QAAI,KAAK,IAAI,OAAO,SAAS,CAAC,CAAC;AAC/B,QAAI,KAAK,IAAI,OAAO,SAAS,CAAC,CAAC;EACnC;AACJ;AAGA,SAAS,gBAAgB,KAAa,UAAkB,KAA6C;AACjG,QAAM,OAAO,eAAuB,GAAG;AACvC,MAAI,KAAK,SAAA,SAA0B;AACnC,yBAAuB,KAAK,UAAU,MAAM,EAAE,UAAU,KAAK,CAAC;AAClE;AAQA,SAAS,kBAAkB,OAAwD,KAAkC;AA7IrH,MAAAA,KAAA;AA8II,MAAI,CAAC,MAAO,QAAO,CAAC;AAGpB,QAAM,OAAO,eAAsC,KAAK;AACxD,MAAI,KAAK,SAAA,SAA0B,QAAO,CAAC;AAC3C,MAAI,KAAK,SAAA,SAA0B,QAAO,MAAM,QAAQ,KAAK,KAAK,IAAI,KAAK,MAAM,IAAI,cAAc,IAAI,CAAC;AAExG,QAAM,MAAM,KAAK;AACjB,MAAI,CAAC,IAAI,OAAQ,QAAO,CAAC;AAOzB,QAAM,iBAAiB,KAAK;AAI5B,MAAI,YAAY;AAChB,aAAW,MAAM,KAAK;AAClB,UAAM,IAAI,cAAc,EAAE;AAC1B,QAAI,MAAM,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAW,aAAY,EAAE;EAChE;AACA,MAAI,CAAC,UAAW,QAAO,CAAC;AAKxB,QAAM,eAAe,cAAc,IAAI,CAAC,CAAC;AACzC,QAAM,gBAAuC,CAAC;AAC9C,WAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAChC,UAAM,KAAI,MAAAA,MAAA,gBAAA,OAAA,SAAA,aAAe,CAAA,MAAf,OAAAA,MAAqB,gBAAgB,KAAK,GAAG,CAAC,MAA9C,OAAA,KAAmD,EAAE,QAAQ,IAAI,KAAK,IAAI,GAAG,YAAY,CAAC,GAAG,OAAO,UAAU;AACxH,kBAAc,KAAK,EAAE,QAAQ,EAAE,QAAQ,OAAO,EAAE,MAAM,CAAC;EAC3D;AAEA,SAAO,cAAc,IAAI,CAAC,IAAI,MAAM,iBAAiB,IAAI,KAAK,GAAG,KAAK,cAAc,CAAC;AACzF;AAEA,SAAS,eAAe,GAA2B;AAC/C,SAAO;IACH,MAAM;IACN,QAAQ,aAAa,EAAE,MAAM;IAC7B,WAAW,EAAE;EACjB;AACJ;AAEA,SAAS,iBAAiB,UAA0B,KAAwB,SAAiB,MAAoB,MAA4C;AA7L7J,MAAAA;AA8LI,QAAM,WAA8B,CAAC;AACrC,QAAM,YAA+B,CAAC;AAItC,MAAI,eAAe;AACnB,aAAW,MAAM,KAAK;AAClB,UAAM,IAAI,aAAa,EAAE;AACzB,UAAM,MAAM,cAAc,EAAE;AAC5B,UAAM,UAASA,MAAA,OAAA,OAAA,SAAA,IAAM,OAAA,MAAN,OAAAA,MAAkB,gBAAgB,KAAK,IAAI,QAAQ,EAAE,GAAG,OAAO;AAC9E,QAAI,CAAC,OAAQ;AACb,UAAM,SAAS,eAAe,EAAE;AAEhC,UAAM,WAAuB,EAAE,MAAM,GAAG,OAAO,OAAO,MAAM;AAC5D,QAAI,WAAW,OAAW,UAAS,SAAS;AAC5C,aAAS,KAAK,QAAQ;AAItB,UAAM,YAAwB,EAAE,MAAM,GAAG,OAAO,OAAO,OAAO;AAC9D,QAAI,WAAW,OAAW,WAAU,SAAS;AAC7C,cAAU,KAAK,SAAS;AACxB,QAAI,OAAO,WAAW,SAAS,OAAQ,gBAAe;EAC1D;AAEA,QAAM,OAAe;IACjB,MAAM;IACN,QAAQ,aAAa,SAAS,MAAM;IACpC,WAAW,SAAS;EACxB;AACA,QAAM,UAAsF,CAAC;AAC7F,MAAI,SAAS,QAAQ;AACjB,YAAQ,YAAY,EAAE,WAAW,SAAS;AAC1C,QAAI,SAAS,OAAW,SAAQ,UAAU,OAAO;EACrD;AACA,MAAI,gBAAgB,UAAU,QAAQ;AAClC,YAAQ,SAAS,EAAE,WAAW,UAAU;AACxC,QAAI,SAAS,OAAW,SAAQ,OAAO,OAAO;EAClD;AACA,MAAI,OAAO,KAAK,OAAO,EAAE,OAAQ,MAAK,UAAU;AAChD,SAAO;AACX;AAMA,SAAS,gBAAgB,KAAwB,SAAiB,SAA6C;AAC3G,WAAS,IAAI,SAAS,KAAK,GAAG,KAAK;AAC/B,UAAM,MAAM,cAAc,IAAI,CAAC,CAAC;AAChC,QAAI,OAAA,OAAA,SAAA,IAAM,OAAA,EAAU,QAAO,IAAI,OAAO;EAC1C;AACA,WAAS,IAAI,UAAU,GAAG,IAAI,IAAI,QAAQ,KAAK;AAC3C,UAAM,MAAM,cAAc,IAAI,CAAC,CAAC;AAChC,QAAI,OAAA,OAAA,SAAA,IAAM,OAAA,EAAU,QAAO,IAAI,OAAO;EAC1C;AACA,SAAO;AACX;AAIA,SAAS,aAAa,GAAmB;AACrC,QAAM,MAAM,KAAK,MAAM,IAAI,GAAI,IAAI;AACnC,SAAO,MAAM;AACjB;ACjOO,SAAS,oBACZ,MACA,IACA,KACM;AACN,MAAI,EAAC,MAAA,OAAA,SAAA,GAAI,UAAU,QAAO;AAE1B,QAAM,SAAS,MAAM,KAAK,MAAM;AAChC,QAAM,YAAoB,EAAE,MAAM,OAAO;AACzC,QAAM,OAAO,eAAuB,GAAG,QAAQ;AAC/C,MAAI,KAAK,SAAA,UAA0B;AAG/B,QAAI,KAAK,SAAA,UAA0B;AAC/B,gBAAU,IAAI,WAAW,KAAK,KAAK;IACvC,OAAO;AACH,6BAAuB,WAAW,KAAK,IAAI;AAC3C,UAAI,UAAU,MAAM,OAAW,WAAU,IAAI,WAAW,UAAU,CAAY;IAClF;EACJ;AACA,MAAI,KAAK,KAAK,EAAE,MAAM,YAAY,IAAI,QAAQ,UAAU,CAAC,SAAS,EAAE,CAAC;AACrE,OAAK,WAAW,UAAU,SAAS;AACnC,SAAO;AACX;AAGA,SAAS,WAAW,GAAgC;AAChD,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,MAAI,KAAK,OAAO,MAAM,YAAY,OAAQ,EAA6B,aAAa,SAAU,QAAQ,EAA2B;AACjI,SAAO;AACX;AC3BO,SAAS,oBACZ,MACA,IACA,aACA,KACM;AACN,MAAI,CAAC,GAAI,QAAO;AAEhB,QAAM,WAAW,UAAU,GAAG,MAAM;AACpC,MAAI,CAAC,UAAU;AAAE,QAAI,OAAO,KAAK,kDAA6C;AAAG,WAAO;EAAM;AAE9F,QAAM,SAAS,MAAM,KAAK,MAAM;AAEhC,MAAI,UAAkB,EAAE,MAAM,OAAO,MAAM,MAAM,SAAS;AAQ1D,MAAI,aAAa;AACb,cAAU,qBAAqB,SAAS,aAAa,GAAG;EAC5D,WAAW,oBAAoB,IAAI,GAAG;AAGlC,cAAU,iCAAiC,SAAS,MAAM,GAAG;EACjE,OAAO;AACH,UAAM,aAAa,2BAA2B,IAAI;AAClD,QAAI,WAAY,WAAU,qBAAqB,SAAS,YAAY,GAAG;EAC3E;AAGA,QAAM,mBAAmB,gBAAgB,UAAa,CAAC,qBAAqB,IAAI;AAChF,YAAU,8BAA8B,SAAS,MAAM,UAAU,KAAK,gBAAgB;AAEtF,QAAM,OAAe,EAAE,MAAM,QAAQ,IAAI,QAAQ,UAAU,CAAC,OAAO,EAAE;AACrE,MAAI,GAAG,SAAU,MAAK,WAAW,GAAG;AACpC,MAAI,GAAG,UAAW,MAAK,YAAY,GAAG;AACtC,MAAI,GAAG,iBAAkB,MAAK,mBAAmB,GAAG;AAGpD,MAAI,GAAG,MAAM,OAAW,MAAK,IAAI,OAAO,GAAG,CAAC;AAC5C,MAAI,GAAG,MAAM,OAAW,MAAK,IAAI,OAAO,GAAG,CAAC;AAC5C,MAAI,GAAG,UAAU,OAAW,MAAK,QAAQ,OAAO,GAAG,KAAK;AACxD,MAAI,GAAG,WAAW,OAAW,MAAK,SAAS,OAAO,GAAG,MAAM;AAC3D,MAAI,KAAK,KAAK,IAAI;AAElB,OAAK,OAAO,UAAU,SAAS;AAC/B,SAAO;AACX;AAMA,SAAS,qBAAqB,OAAe,IAAqC,KAA2B;AACzG,MAAI,CAAC,GAAI,QAAO;AAChB,QAAM,SAAS,iBAAiB,GAAG,QAAQ,GAAG;AAE9C,MAAI,IAAI;AACR,MAAI,gBAAgB,GAAA,aAA4B,GAAG,WAAW,QAAW,GAAG;AAC5E,MAAI,gBAAgB,GAAA,UAAyB,GAAG,QAAQ,QAAQ,GAAG;AACnE,MAAI,gBAAgB,GAAA,SAAwB,GAAG,OAAO,QAAQ,GAAG;AACjE,SAAO;AACX;AAEA,SAAS,gBACL,OAAe,MACf,KAAoC,QAA4B,KAC1D;AACN,MAAI,QAAQ,OAAW,QAAO;AAM9B,QAAM,gBAAgD,SAAA,WAAgC,MAAM,QAAQ,GAAG,IACjG,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,GAAG,IAC3B;AACN,QAAM,IAAI,eAAoB,aAAa;AAC3C,MAAI,EAAE,SAAA,UAA0B;AAC5B,WAAO,EAAE,MAAM,KAAK,WAAW,EAAE,OAAO,YAAY,MAAM,gBAAgB,MAAM,EAAE,KAAK,GAAG,MAAM,EAAE,GAAG,UAAU,CAAC,KAAK,EAAE;EAC3H;AACA,MAAI,EAAE,SAAA,YAA4B;AAC9B,UAAM,SAAc,EAAE,WAAW,EAAE,UAAU,IAAI,CAAA,OAAM;AACnD,YAAM,MAAM,aAAa,IAAI,YAAY,MAAM,gBAAgB,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AACvF,aAAO,SAAA,cAAmCC,gBAAAA,gBAAA,CAAA,GAAK,GAAA,GAAQ,uBAAuB,EAAE,CAAA,IAAM;IAC1F,CAAC,EAAE;AACH,QAAI,EAAE,SAAS,OAAW,QAAO,OAAO,EAAE;AAC1C,WAAO;MACH,MAAM;MACN,SAAS,EAAE,WAAW,OAAO;MAC7B,UAAU,CAAC,KAAK;IACpB;EACJ;AACA,SAAO;AACX;AAEA,SAAS,gBAAgB,MAAqB,OAAiB;AAC3D,MAAI,SAAA,YAAkC,QAAO,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAClE,MAAI,SAAA,SAA+B,QAAO,CAAC;AAC3C,SAAO,CAAC,IAAI,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,CAAC;AACtC;AAUA,SAAS,uBAAuB,IAA8C;AAjJ9E,MAAAD,KAAA;AAkJI,QAAM,MAA2B,CAAC;AAClC,QAAM,MAAKA,MAAA,GAAG,eAAH,OAAAA,MAAiB,GAAG;AAC/B,QAAM,MAAK,KAAA,GAAG,cAAH,OAAA,KAAgB,GAAG;AAC9B,MAAI,MAAM,QAAQ,EAAE,EAAG,KAAI,aAAa,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACvD,MAAI,MAAM,QAAQ,EAAE,EAAG,KAAI,YAAY,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACtD,SAAO;AACX;AAsBA,SAAS,iCAAiC,OAAe,MAAc,MAA4B;AA9KnG,MAAAA;AA+KI,QAAM,UAAU,KAAK,WAAW,OAAO,KAAK,YAAY,YAAY,CAAC,MAAM,QAAQ,KAAK,OAAO,IACxF,KAAK,UAAkC;AAC9C,QAAM,SAAS,WAAA,OAAA,SAAA,QAAS;AACxB,QAAM,MAAM,UAAU,OAAO,WAAW,YAAY,MAAM,QAAS,OAA+B,SAAS,IACnG,OAA+B,YACjC;AACN,MAAI,CAAC,OAAO,CAAC,IAAI,OAAQ,QAAO;AAEhC,QAAM,eAA2C,CAAC;AAClD,QAAM,YAAwC,CAAC;AAC/C,QAAM,WAAuC,CAAC;AAE9C,aAAW,MAAM,KAAK;AAClB,UAAM,MAAKA,MAAA,GAAG,UAAH,OAAAA,MAAY,GAAG,MAAM,CAAC;AACjC,UAAM,SAAS,aAAa,IAAW,MAAS;AAChD,QAAI,MAAM,QAAQ,EAAE,SAAS,GAAG;AAC5B,mBAAa,KAAKE,eAAAD,gBAAAA,gBAAA,CAAA,GAAK,MAAA,GAAW,uBAAuB,EAAE,CAAA,GAAzC,EAA4C,OAAO,EAAE,WAAW,CAAC,CAAC,EAAE,UAAU,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC,CAAC,EAAE,EAAE,CAAA,CAAC;IAC5H;AACA,QAAI,OAAO,EAAE,WAAW,UAAU;AAC9B,YAAM,MAA2B,EAAE,QAAQ,CAAC,EAAE,OAAO;AACrD,UAAI,MAAM,QAAQ,EAAE,MAAM,EAAG,KAAI,SAAS,CAAC,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AACnE,gBAAU,KAAKC,eAAAD,gBAAA,CAAA,GAAK,MAAA,GAAL,EAAa,OAAO,IAAI,CAAA,CAAC;IAC5C;AACA,QAAI,MAAM,QAAQ,EAAE,KAAK,GAAG;AACxB,YAAM,MAA2B,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC,EAAE;AAC3E,UAAI,MAAM,QAAQ,EAAE,MAAM,EAAG,KAAI,SAAS,CAAC,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AACnE,eAAS,KAAKC,eAAAD,gBAAA,CAAA,GAAK,MAAA,GAAL,EAAa,OAAO,IAAI,CAAA,CAAC;IAC3C;EACJ;AAKA,QAAM,UAAW,UAAA,OAAA,SAAA,OAA4C;AAC7D,QAAM,WAAW,CAACE,SAAyD;AACvE,UAAM,QAA6B,EAAE,WAAWA,KAAI;AACpD,QAAI,YAAY,OAAW,OAAM,OAAO;AACxC,WAAO;EACX;AAEA,MAAI,IAAI;AAER,MAAI,aAAa,OAAQ,KAAI,EAAE,MAAM,KAAK,SAAS,EAAE,WAAW,SAAS,YAAY,EAAE,GAAG,UAAU,CAAC,CAAC,EAAE;AACxG,MAAI,UAAU,OAAW,KAAI,EAAE,MAAM,KAAK,SAAS,EAAE,WAAW,SAAS,SAAS,EAAK,GAAG,UAAU,CAAC,CAAC,EAAE;AACxG,MAAI,SAAS,OAAY,KAAI,EAAE,MAAM,KAAK,SAAS,EAAE,WAAW,SAAS,QAAQ,EAAM,GAAG,UAAU,CAAC,CAAC,EAAE;AACxG,SAAO;AACX;AAKA,SAAS,qBAAqB,MAAuB;AACjD,MAAI,OAAO,KAAK,cAAc,SAAU,QAAO;AAC/C,MAAI,KAAK,aAAa,OAAO,KAAK,cAAc,SAAU,QAAO;AACjE,SAAO,oBAAoB,IAAI;AACnC;AAIA,SAAS,oBAAoB,MAAuB;AAChD,QAAM,UAAU,KAAK,WAAW,OAAO,KAAK,YAAY,YAAY,CAAC,MAAM,QAAQ,KAAK,OAAO,IACxF,KAAK,UAAkC;AAC9C,SAAO,CAAC,EAAE,WAAW,QAAQ;AACjC;AAYA,SAAS,2BAA2B,MAA+C;AAC/E,MAAI,OAAO,KAAK,cAAc,UAAU;AACpC,UAAM,QAAQ,4BAA4B,KAAK,SAAS;AACxD,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,MAA2B,CAAC;AAClC,QAAI,MAAM,UAAW,KAAI,YAAY,MAAM;AAC3C,QAAI,MAAM,WAAW,OAAW,KAAI,SAAS,MAAM;AAInD,QAAI,MAAM,MAAO,KAAI,QAAQ,EAAE,OAAO,MAAM,MAAM;AAClD,QAAI,MAAM,OAAQ,KAAI,SAAS,MAAM;AACrC,WAAO,OAAO,KAAK,GAAG,EAAE,SAAS,MAAM;EAC3C;AAKA,MAAI,KAAK,aAAa,OAAO,KAAK,cAAc,YACzC,CAAE,KAAK,UAAkB,WAAW;AACvC,UAAM,UAAW,KAAK,UAA8C;AACpE,UAAM,QAAS,WAAW,OAAO,YAAY,WAAW,UAAU,KAAK;AACvE,QAAI,SAAS,OAAO,UAAU,UAAU;AACpC,YAAM,MAA2B,CAAC;AAClC,UAAI,MAAM,QAAQ,MAAM,SAAS,EAAG,KAAI,YAAY,MAAM;AAC1D,UAAI,OAAO,MAAM,WAAW,SAAU,KAAI,SAAS,MAAM;AACzD,UAAI,OAAO,MAAM,SAAS,SAAU,KAAI,OAAO,MAAM;AACrD,UAAI,MAAM,QAAQ,MAAM,KAAK,EAAG,KAAI,QAAQ,EAAE,OAAO,MAAM,MAA0B;AACrF,UAAI,MAAM,QAAQ,MAAM,MAAM,EAAG,KAAI,SAAS,MAAM;AACpD,aAAO,OAAO,KAAK,GAAG,EAAE,SAAS,MAAM;IAC3C;EACJ;AACA,SAAO;AACX;AAQA,SAAS,4BAA4B,GAAiG;AAnStI,MAAAH,KAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA;AAqSI,QAAM,KAAK;AACX,MAAI;AACJ,QAAM,MAAiB,CAAC;AACxB,UAAQ,IAAI,GAAG,KAAK,CAAC,OAAO,MAAM;AAC9B,UAAM,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,EAAE,OAAO,CAAA,MAAK,EAAE,SAAS,CAAC,EAAE,IAAI,MAAM;AACtE,QAAI,KAAK,EAAE,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC;EACjC;AACA,MAAI,CAAC,IAAI,OAAQ,QAAO;AAIxB,QAAM,OAAO,IAAI,IAAI,SAAS,CAAC;AAC/B,MAAI,KAAK,SAAS,aAAa;AAC3B,aAAS,IAAI,IAAI,SAAS,GAAG,KAAK,GAAG,KAAK;AACtC,YAAM,OAAO,IAAI,CAAC;AAClB,UAAI,KAAK,SAAS,YAAa;AAC/B,YAAM,MAAKA,MAAA,KAAK,KAAK,CAAC,MAAX,OAAAA,MAAgB;AAC3B,YAAM,MAAK,KAAA,KAAK,KAAK,CAAC,MAAX,OAAA,KAAgB;AAC3B,YAAM,MAAK,KAAA,KAAK,KAAK,CAAC,MAAX,OAAA,KAAgB;AAC3B,YAAM,MAAK,KAAA,KAAK,KAAK,CAAC,MAAX,OAAA,KAAgB;AAC3B,UAAI,OAAO,CAAC,MAAM,OAAO,CAAC,GAAI;AAG9B,YAAMI,OAAgF,CAAC;AACvFA,WAAI,SAAS,CAAC,IAAI,EAAE;AACpB,eAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AACxB,YAAI,IAAI,CAAC,EAAE,SAAS,aAAa;AAC7B,gBAAM,MAAK,KAAA,IAAI,CAAC,EAAE,KAAK,CAAC,MAAb,OAAA,KAAkB;AAC7B,gBAAM,MAAK,KAAA,IAAI,CAAC,EAAE,KAAK,CAAC,MAAb,OAAA,KAAkB;AAC7BA,eAAI,YAAYA,KAAI,YAAY,CAACA,KAAI,UAAU,CAAC,IAAI,IAAIA,KAAI,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;QAC5F;MACJ;AACA,eAAS,IAAI,IAAI,GAAG,IAAI,IAAI,SAAS,GAAG,KAAK;AACzC,cAAM,KAAK,IAAI,CAAC;AAChB,YAAI,GAAG,SAAS,SAAUA,MAAI,WAAU,KAAAA,KAAI,WAAJ,OAAA,KAAc,OAAM,KAAA,GAAG,KAAK,CAAC,MAAT,OAAA,KAAc;iBACjE,GAAG,SAAS,SAAS;AAC1B,gBAAM,MAAK,KAAA,GAAG,KAAK,CAAC,MAAT,OAAA,KAAc;AACzB,gBAAM,KAAK,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,CAAC,IAAI;AAC7CA,eAAI,QAAQA,KAAI,QAAQ,CAACA,KAAI,MAAM,CAAC,IAAI,IAAIA,KAAI,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;QAC5E;MACJ;AACA,aAAOA;IACX;EACJ;AAKA,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,aAAW,MAAM,KAAK;AAClB,QAAI,GAAG,SAAS,aAAa;AACzB,YAAM,MAAK,KAAA,GAAG,KAAK,CAAC,MAAT,OAAA,KAAc;AACzB,YAAM,MAAK,KAAA,GAAG,KAAK,CAAC,MAAT,OAAA,KAAc;AACzB,kBAAY,YAAY,CAAC,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;IAC5E,WAAW,GAAG,SAAS,UAAU;AAC7B,gBAAU,UAAA,OAAA,SAAU,OAAM,KAAA,GAAG,KAAK,CAAC,MAAT,OAAA,KAAc;IAC5C,WAAW,GAAG,SAAS,SAAS;AAC5B,YAAM,MAAK,KAAA,GAAG,KAAK,CAAC,MAAT,OAAA,KAAc;AACzB,YAAM,KAAK,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,CAAC,IAAI;AAC7C,cAAQ,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;IAC5D;EACJ;AACA,QAAM,MAA+D,CAAC;AACtE,MAAI,UAAW,KAAI,YAAY;AAC/B,MAAI,WAAW,OAAW,KAAI,SAAS;AACvC,MAAI,MAAO,KAAI,QAAQ;AACvB,SAAO,OAAO,KAAK,GAAG,EAAE,SAAS,MAAM;AAC3C;AAkBA,SAAS,8BAA8B,OAAe,YAAoB,UAAkB,KAAmB,kBAAmC;AAC9I,QAAM,aAAa,IAAI,MAAM,IAAI,QAAQ;AAOzC,QAAM,kBAAkB,IAAI,mBAAmB,IAAI,UAAU,KAAK,CAAC;AACnE,QAAM,YAAY,mBAAmB,qBAAqB,YAAY,GAAG,IAAI;AAC7E,QAAM,cAAc,YAAY,CAAC,GAAG,iBAAiB,SAAS,IAAI;AAClE,QAAM,cAAe,cAAc,IAAI,mBAAmB,IAAI,UAAU,KAAM,CAAC;AAE/E,MAAI,CAAC,YAAY,UAAU,CAAC,YAAY,OAAQ,QAAO;AAIvD,QAAM,QAAQ,oBAAI,IAAY;AAC9B,aAAW,KAAK,YAAa,KAAI,EAAE,mBAAoB,YAAW,MAAM,EAAE,mBAAoB,OAAM,IAAI,GAAG,IAAI;AAC/G,aAAW,KAAK,YAAa,KAAI,EAAE,mBAAoB,YAAW,MAAM,EAAE,mBAAoB,OAAM,IAAI,GAAG,IAAI;AAC/G,QAAM,WAAW,MAAM,OAAO;AAE9B,MAAI,CAAC,UAAU;AACX,UAAM,MAAM,mBAAmB,WAAW;AAC1C,UAAM,MAAM,mBAAmB,WAAW;AAC1C,UAAM,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC;AACzB,UAAM,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC;AACzB,QAAI,OAAO,KAAK,OAAO,EAAG,QAAO;AACjC,WAAO,EAAE,MAAM,KAAK,WAAW,eAAe,KAAK,MAAM,KAAK,KAAK,UAAU,CAAC,KAAK,EAAE;EACzF;AAEA,QAAM,cAAc,MAAM,KAAK,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC1D,QAAM,YAAY,YAAY,IAAI,CAAA,MAAK;AACnC,UAAM,MAAM,eAAe,aAAa,CAAC;AACzC,UAAM,MAAM,eAAe,aAAa,CAAC;AACzC,WAAO,EAAE,MAAM,GAAG,OAAO,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,EAAsB,EAAE;EACnG,CAAC;AACD,SAAO,EAAE,MAAM,KAAK,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,UAAU,CAAC,KAAK,EAAE;AACjF;AAGA,SAAS,mBAAmB,OAAuD;AAC/E,MAAI,IAAI,GAAG,IAAI;AACf,aAAW,KAAK,OAAO;AACnB,QAAI,EAAE,WAAW;AAAE,WAAK,EAAE,UAAU,CAAC;AAAG,WAAK,EAAE,UAAU,CAAC;IAAG;EACjE;AACA,SAAO,CAAC,GAAG,CAAC;AAChB;AAKA,SAAS,eAAe,OAAqC,GAA6B;AACtF,MAAI,IAAI,GAAG,IAAI;AACf,aAAW,KAAK,OAAO;AACnB,QAAI,EAAE,sBAAsB,EAAE,mBAAmB,QAAQ;AACrD,YAAM,IAAI,UAAU,EAAE,oBAAoB,CAAC;AAC3C,WAAK,EAAE,CAAC;AAAG,WAAK,EAAE,CAAC;IACvB,WAAW,EAAE,WAAW;AACpB,WAAK,EAAE,UAAU,CAAC;AAAG,WAAK,EAAE,UAAU,CAAC;IAC3C;EACJ;AACA,SAAO,CAAC,GAAG,CAAC;AAChB;AAEA,SAAS,UAAU,KAAuD,GAA6B;AACnG,MAAI,KAAK,IAAI,CAAC,EAAE,KAAM,QAAO,IAAI,CAAC,EAAE;AACpC,MAAI,KAAK,IAAI,IAAI,SAAS,CAAC,EAAE,KAAM,QAAO,IAAI,IAAI,SAAS,CAAC,EAAE;AAC9D,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACjC,QAAI,KAAK,IAAI,CAAC,EAAE,MAAM;AAClB,YAAM,OAAO,IAAI,IAAI,CAAC;AACtB,YAAM,MAAM,IAAI,CAAC;AACjB,YAAM,KAAK,IAAI,KAAK,SAAS,IAAI,OAAO,KAAK;AAC7C,aAAO,CAAC,KAAK,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,KAAK,GAAG,KAAK,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,KAAK,CAAC;IAClH;EACJ;AACA,SAAO,IAAI,IAAI,SAAS,CAAC,EAAE;AAC/B;AAiBO,SAAS,0BAA0B,MAAc,KAAyB;AAK7E,QAAM,mBAAmB,oBAAI,IAAY;AACzC,QAAM,0BAA0B,CAAC,MAAoB;AAhezD,QAAAJ,KAAA;AAieQ,UAAM,eAAe,WAAU,MAAAA,MAAA,EAAE,YAAF,OAAA,SAAAA,IAAW,aAAX,OAAA,SAAA,GAAqB,MAAM;AAC1D,QAAI,OAAO,iBAAiB,UAAU;AAClC,uBAAiB,IAAI,CAAC;AACtB,YAAM,aAAa,IAAI,MAAM,IAAI,YAAY;AAC7C,UAAI,WAAY,kBAAiB,IAAI,UAAU;IACnD;AACA,QAAI,MAAM,QAAQ,EAAE,QAAQ,EAAG,YAAW,MAAM,EAAE,SAAU,yBAAwB,EAAE;EAC1F;AACA,0BAAwB,IAAI;AAC5B,MAAI,iBAAiB,SAAS,EAAG;AAEjC,QAAM,OAAO,CAAC,MAAc,UAA8C;AACtE,QAAI,iBAAiB,IAAI,IAAI,EAAG,KAAI,mBAAmB,IAAI,MAAM,KAAK;AACtE,QAAI,MAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,YAAM,MAAM,qBAAqB,MAAM,GAAG;AAC1C,YAAM,OAAO,MAAM,CAAC,GAAG,OAAO,GAAG,IAAI;AACrC,iBAAW,MAAM,KAAK,SAAU,MAAK,IAAI,IAAI;IACjD;EACJ;AACA,OAAK,MAAM,CAAC,CAAC;AACjB;AAKA,SAAS,qBAAqB,MAAc,KAAsD;AA1flG,MAAAA,KAAA,IAAA;AA2fI,QAAM,KAAK,KAAK;AAChB,QAAM,eAAe,KAAK,WAAW,OAAO,KAAK,YAAY,YAAY,CAAC,MAAM,QAAQ,KAAK,OAAO,IAC7F,KAAK,QAAgC,YAAY;AACxD,MAAI,OAAO,UAAa,CAAC,aAAc,QAAO;AAE9C,QAAM,MAA6B,CAAC;AAEpC,MAAI,OAAO,OAAO,UAAU;AACxB,UAAM,QAAQ,6BAA6B,IAAI,GAAG;AAClD,QAAI,MAAO,KAAI,YAAY;EAC/B,WAAW,MAAM,OAAO,OAAO,YAAY,CAAE,GAA2B,WAAW;AAE/E,UAAM,UAAW,GAA2B;AAC5C,UAAM,QAAS,WAAW,OAAO,YAAY,WAAY,UAAW;AACpE,QAAI,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,MAAM,SAAS,GAAG;AACtE,UAAI,YAAY,CAAC,MAAM,UAAU,CAAC,KAAK,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC;IACrE;AACA,QAAI,UAAU,MAAM,WAAW,UAAa,MAAM,UAAU,UAAa,MAAM,SAAS,SAAY;AAChG,UAAI,SAAS,KAAK,2FAA2F;IACjH;EACJ;AAEA,MAAI,gBAAgB,MAAM,QAAS,aAAqC,SAAS,GAAG;AAChF,UAAM,MAAO,aAAqC;AAClD,UAAM,eAAiE,CAAC;AACxE,eAAW,MAAM,KAAK;AAClB,YAAM,KAAIA,MAAA,GAAG,UAAH,OAAAA,MAAY,GAAG;AACzB,YAAM,KAAK,MAAA,KAAA,GAAG,SAAH,OAAA,KAAW,GAAG,MAAd,OAAA,KAAmB;AAC9B,UAAI,KAAK,OAAO,MAAM,YAAY,MAAM,QAAQ,EAAE,SAAS,GAAG;AAC1D,qBAAa,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,UAAU,CAAC,KAAK,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;AAChF,YAAI,EAAE,WAAW,UAAa,EAAE,UAAU,UAAa,EAAE,SAAS,QAAW;AACzE,cAAI,SAAS,KAAK,yDAAyD;QAC/E;MACJ;IACJ;AACA,QAAI,aAAa,OAAQ,KAAI,qBAAqB;EACtD;AAEA,SAAQ,IAAI,aAAa,IAAI,qBAAsB,MAAM;AAC7D;AAMA,SAAS,6BAA6B,GAAW,KAAiD;AAC9F,QAAM,KAAK;AACX,MAAI;AACJ,MAAI,IAAI,GAAG,IAAI;AACf,MAAI,OAAO;AACX,MAAI,sBAAsB;AAC1B,UAAQ,IAAI,GAAG,KAAK,CAAC,OAAO,MAAM;AAC9B,UAAM,OAAO,EAAE,CAAC;AAChB,UAAM,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,EAAE,OAAO,CAAA,MAAK,EAAE,SAAS,CAAC,EAAE,IAAI,MAAM;AACtE,QAAI,SAAS,aAAa;AACtB,WAAK,KAAK,CAAC,KAAK;AAChB,WAAK,KAAK,CAAC,KAAK;AAChB,aAAO;IACX,OAAO;AACH,4BAAsB;IAC1B;EACJ;AACA,MAAI,oBAAqB,KAAI,SAAS,KAAK,mEAAmE,CAAC;AAC/G,SAAO,OAAO,CAAC,GAAG,CAAC,IAAI;AAC3B;ACthBO,SAAS,aACZ,MACAK,QACA,KACI;AACJ,MAAI,CAACA,OAAO;AAEZ,QAAM,WAAW,UAAUA,OAAM,MAAM;AACvC,MAAI,CAAC,UAAU;AACX,QAAIA,OAAM,YAAY,eAAe,UAAW,KAAI,OAAO,KAAK,qCAAqC;AACrG;EACJ;AAIA,QAAM,WAAWA,OAAM,YAAY,eAAe,YAC3C,IAAI,mBAAmB,IAAI,QAAQ,KAAK,WACzC;AACN,OAAK,OAAO,MAAM;AACtB;AAEO,SAAS,gCACZ,MACAA,QACA,aACA,KACM;AACN,eAAa,MAAMA,QAAO,GAAG;AAC7B,SAAO,uBAAuB,MAAM,aAAa,GAAG;AACxD;AC1BO,SAAS,oBAAoB,MAAc,IAAkC,KAA2B;AAxC/G,MAAAL,KAAA;AAyCI,MAAI,CAAC,GAAI,QAAO;AAEhB,QAAM,UAASA,MAAA,GAAG,WAAH,OAAAA,MAAa;AAC5B,MAAI,SAAS,GAAG;AAAE,QAAI,OAAO,KAAK,8BAA8B,GAAG,MAAM;AAAG,WAAO;EAAM;AAKzF,QAAM,kBAAkB,KAAK;AAG7B,QAAM,uBAAuB,KAAA,KAAK,YAAL,OAAA,SAAA,GAAoD;AAEjF,QAAM,OAAO,MAAM,IAAI;AACvB,SAAO,KAAK;AACZ,MAAI,KAAK,SAAS;AACd,WAAQ,KAAK,QAAkC;AAC/C,QAAI,OAAO,KAAK,KAAK,OAAO,EAAE,WAAW,EAAG,QAAO,KAAK;EAC5D;AAMA,SAAO,KAAK;AAEZ,QAAM,WAA0B,CAAC,IAAI;AACrC,WAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC7B,UAAM,YAAY,MAAM,IAAI;AAC5B,UAAM,UAAU,oBAAoB,IAAI,CAAC;AAGzC,UAAM,UAAU,UAAU,uBAAuB,WAAW,SAAS,GAAG,IAAI;AAC5E,aAAS,KAAK,OAAO;EACzB;AAEA,QAAM,UAAkB,EAAE,MAAM,KAAK,SAAS;AAC9C,MAAI,KAAK,GAAI,SAAQ,KAAK,KAAK;AAC/B,MAAI,oBAAoB,OAAW,SAAQ,YAAY;AACvD,MAAI,wBAAwB,OAAW,SAAQ,UAAU,EAAE,WAAW,oBAAoB;AAC1F,SAAO;AACX;AAQA,SAAS,oBAAoB,IAAsB,GAA4C;AAC3F,QAAM,MAA2B,CAAC;AAElC,MAAI,GAAG,cAAc,QAAW;AAC5B,QAAI,YAAY,cAAsB,GAAG,WAAW,CAAA,MAAK,CAAC,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;EACjF;AACA,MAAI,GAAG,WAAW,QAAW;AACzB,QAAI,SAAS,cAAsB,GAAG,QAAQ,CAAA,MAAK,IAAI,CAAC;EAC5D;AACA,MAAI,GAAG,SAAS,QAAW;AACvB,QAAI,OAAO,cAAsB,GAAG,MAAM,CAAA,MAAK,IAAI,CAAC;EACxD;AACA,MAAI,GAAG,UAAU,QAAW;AACxB,QAAI,QAAQ,gBAAgB,GAAG,OAAO,CAAC;EAC3C;AACA,MAAI,GAAG,WAAW,QAAW;AAGzB,QAAI,SAAS,GAAG;EACpB;AAEA,SAAO,OAAO,KAAK,GAAG,EAAE,SAAS,MAAM;AAC3C;AAWA,SAAS,cAAiB,KAAsB,IAAiB,aAAa,OAAwB;AAClG,QAAM,OAAO,eAAkB,GAAG;AAClC,MAAI,KAAK,SAAA,SAA0B,QAAO;AAC1C,MAAI,KAAK,SAAA,UAA0B;AAC/B,UAAM,SAAS,GAAG,KAAK,KAAK;AAC5B,UAAM,eAAe,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG;AACjE,WAAQ,gBAAgB,CAAC,aAAc,SAAS,EAAE,OAAO,OAAO;EACpE;AACA,QAAM,MAAqG;IACvG,WAAW,KAAK,UAAU,IAAI,CAAA,OAAM,MAAM,GAAG,UAAU,SAAYE,eAAAD,gBAAA,CAAA,GAAK,EAAA,GAAL,EAAS,OAAO,GAAG,GAAG,KAAK,EAAE,CAAA,IAAI,EAAE;EAC1G;AACA,MAAI,KAAK,SAAS,OAAW,KAAI,OAAO,KAAK;AAC7C,MAAI,KAAK,eAAe,OAAW,KAAI,aAAa,KAAK;AACzD,MAAI,KAAK,SAAS,OAAW,KAAI,QAAQ,GAAG,KAAK,IAAI;AACrD,SAAO;AACX;AAWA,SAAS,gBAAgB,KAA2B,GAAiC;AACjF,QAAM,aAAa,CAAC,MAAsB,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC;AAC/E,SAAO,cAAsB,KAAK,YAAY,IAAI;AACtD;AC/GA,IAAM,uCAAuC;AAK7C,SAAS,SAAS,GAA2B;AA9C7C,MAAAD,KAAA;AA+CI,SAAO,EAAE,QAAOA,MAAA,EAAE,UAAF,OAAAA,MAAW,GAAG,UAAS,KAAA,EAAE,YAAF,OAAA,KAAa,EAAE;AAC1D;AAqBA,IAAM,eAAe;AAErB,SAAS,cAAc,SAAiB,MAAwB,KAAyB;AACrF,QAAM,CAAC,OAAO,GAAG,IAAI;AACrB,MAAI,CAAC,OAAO,SAAS,KAAK,KAAK,CAAC,OAAO,SAAS,GAAG,GAAG;AAClD,QAAI,SAAS,KAAK,iEAA4D;AAC9E;EACJ;AAIA,QAAM,YAAY,OAAO,QACnB,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE,CAAC,IACtB;IACE,GAAI,QAAQ,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,QAAQ,YAAY,GAAG,OAAO,EAAE,CAAC,IAAI,CAAC;IAC3E,EAAE,MAAM,KAAK,IAAI,GAAG,KAAK,GAAG,OAAO,EAAE;IACrC,EAAE,MAAM,KAAK,OAAO,EAAE;IACtB,EAAE,MAAM,MAAM,cAAc,OAAO,EAAE;EACzC;AAGJ,QAAM,QAAgBC,gBAAA,CAAA,GAAK,OAAA;AAC3B,aAAW,KAAK,OAAO,KAAK,OAAO,EAAG,QAAQ,QAAoC,CAAC;AACnF,UAAQ,OAAO;AACf,UAAQ,WAAW,CAAC,KAAK;AACxB,UAAoC,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE;AAC5E;AAGA,SAAS,aAAa,OAAe,QAAwB;AACzD,SAAO;IACH,OAAO,OAAO,QAAQ,OAAO,UAAU,MAAM;IAC7C,SAAS,OAAO,UAAU,MAAM;EACpC;AACJ;AAGA,SAAS,gBAAgB,GAAuC;AA1GhE,MAAAD,KAAA;AA2GI,UAAO,MAAAA,MAAA,EAAE,YAAF,OAAA,SAAAA,IAAW,UAAX,OAAA,SAAA,GAAkB;AAC7B;AAGA,SAAS,iBAAiB,GAAiB;AA/G3C,MAAAA;AAgHI,QAAMK,UAAQL,MAAA,EAAE,YAAF,OAAA,SAAAA,IAAW;AACzB,MAAI,CAACK,OAAO;AACZ,SAAOA,OAAM;AACb,MAAI,OAAO,KAAKA,MAAK,EAAE,WAAW,EAAG,QAAO,EAAE,QAAS;AACvD,MAAI,EAAE,WAAW,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAG,QAAO,EAAE;AACnE;AAMO,SAAS,sBAAsB,MAAc,KAAyB;AACzE,MAAI,MAAM,MAAM;AAChB,YAAU,MAAM,IAAI,KAAK;AAEzB,QAAM,QAAuB,CAAC;AAC9B,QAAM,UAAU,CAAC,MAAoB;AAhIzC,QAAAL;AAiIQ,QAAI,gBAAgB,CAAC,EAAG,OAAM,KAAK,CAAC;AACpC,KAAAA,MAAA,EAAE,aAAF,OAAA,SAAAA,IAAY,QAAQ,OAAA;EACxB;AACA,UAAQ,IAAI;AAUZ,QAAM,aAAa,oBAAI,IAAoB;AAC3C,aAAW,QAAQ,OAAO;AACtB,QAAI,QAAQ;AACZ,UAAM,UAAU,oBAAI,IAAY;AAChC,UAAM,OAAO,CAAC,MAAgC;AAlJtD,UAAAA;AAmJY,UAAI,CAAC,EAAG;AACR,UAAI,MAAM,QAAQ,gBAAgB,CAAC,EAAG;AACtC,UAAI,EAAE,SAAS,SAAS,EAAE,MAAM;AAC5B,cAAM,KAAK,UAAU,EAAE,IAAI;AAC3B,YAAI,MAAM,CAAC,QAAQ,IAAI,EAAE,GAAG;AAAE,kBAAQ,IAAI,EAAE;AAAG,eAAK,IAAI,MAAM,IAAI,EAAE,CAAC;QAAG;MAC5E;AACA,OAAAA,MAAA,EAAE,aAAF,OAAA,SAAAA,IAAY,QAAQ,IAAA;IACxB;AACA,UAAM,SAAS,UAAU,KAAK,IAAI;AAClC,QAAI,QAAQ;AAAE,cAAQ,IAAI,MAAM;AAAG,WAAK,IAAI,MAAM,IAAI,MAAM,CAAC;IAAG;AAChE,eAAW,IAAI,MAAM,KAAK;EAC9B;AAEA,QAAM,KAAK,CAAC,GAAG,MAAG;AAhKtB,QAAAA,KAAA;AAgK0B,aAAAA,MAAA,WAAW,IAAI,CAAC,MAAhB,OAAAA,MAAqB,OAAM,KAAA,WAAW,IAAI,CAAC,MAAhB,OAAA,KAAqB;EAAA,CAAE;AAExE,aAAW,WAAW,OAAO;AACzB,UAAM,SAAS,gBAAgB,OAAO;AACtC,QAAI,CAAC,OAAQ;AAOb,UAAM,OAAO,OAAO;AACpB,qBAAiB,OAAO;AACxB,sBAAkB,SAAS,SAAS,MAAM,GAAG,GAAG;AAGhD,QAAI,KAAM,eAAc,SAAS,MAAM,GAAG;EAC9C;AACJ;AAKA,SAAS,kBAAkB,SAAiB,QAAgB,KAAyB;AACjF,QAAM,WAAW,UAAU,QAAQ,IAAI;AACvC,MAAI,CAAC,UAAU;AAAE,QAAI,OAAO,KAAK,qCAAqC;AAAG;EAAQ;AAEjF,QAAM,cAAc,gBAAgB,UAAU,QAAQ,KAAK,oBAAI,IAAI,CAAC;AACpE,MAAI,CAAC,YAAa;AAElB,MAAI,sCAAsC;AACtC,UAAM,YAAY,IAAI,MAAM,IAAI,WAAW;AAC3C,YAAQ,OAAO;AACf,WAAO,QAAQ;AACf,YAAQ,WAAW,CAAC,SAAS;AAG7B,sBAAkB,OAAO;AACzB,QAAI,OAAO,IAAI,KAAK,OAAO,CAAA,MAAK,MAAM,SAAS;EACnD,OAAO;AACH,YAAQ,OAAO,MAAM;EACzB;AACJ;AAQA,SAAS,gBAAgB,UAAkB,OAAe,KAAmB,OAAwC;AACjH,MAAI,MAAM,IAAI,QAAQ,GAAG;AAAE,QAAI,OAAO,KAAK,uBAAuB,WAAW,GAAG;AAAG,WAAO;EAAW;AACrG,QAAM,SAAS,IAAI,MAAM,IAAI,QAAQ;AACrC,MAAI,CAAC,QAAQ;AAAE,QAAI,SAAS,KAAK,qBAAqB,WAAW,aAAa;AAAG,WAAO;EAAW;AAEnG,QAAM,YAAY,MAAM,MAAM;AAC9B,uBAAqB,WAAW,GAAG;AAKnC,MAAI,OAAO,SAAS,OAAO;AACvB,2BAAuB,WAAW,MAAM,OAAO,MAAM,OAAO;EAChE,OAAO;AACH,uBAAmB,WAAW,MAAM,OAAO,MAAM,OAAO;EAC5D;AAGA,mBAAiB,SAAS;AAI1B,MAAI,OAAO,SAAS,SAAS,OAAO,MAAM;AACtC,UAAM,QAAQ,UAAU,OAAO,IAAI;AACnC,QAAI,OAAO;AACP,YAAM,cAAc,gBAAgB,MAAM;AAC1C,YAAM,WAAW,cAAc,aAAa,SAAS,WAAW,GAAG,KAAK,IAAI;AAC5E,YAAM,WAAW,IAAI,IAAI,KAAK;AAAG,eAAS,IAAI,QAAQ;AACtD,YAAM,SAAS,gBAAgB,OAAO,UAAU,KAAK,QAAQ;AAC7D,UAAI,OAAQ,WAAU,OAAO,MAAM;IACvC;EACJ,OAAO;AAQH,gCAA4B,WAAW,OAAO,KAAK,OAAO,QAAQ;EACtE;AAEA,MAAI,KAAK,KAAK,SAAS;AACvB,MAAI,OAAO,UAAU,OAAO,SAAU,KAAI,MAAM,IAAI,UAAU,IAAI,SAAS;AAC3E,SAAO,OAAO,UAAU,OAAO,WAAW,UAAU,KAAK;AAC7D;AASA,SAAS,4BAA4B,MAAc,OAAe,KAAmB,OAAoB,gBAA8B;AACnI,QAAM,QAAQ,CAAC,MAAoB;AAzQvC,QAAAA;AA0QQ,UAAM,SAAS,gBAAgB,CAAC;AAChC,QAAI,EAAE,SAAS,SAAS,UAAU,EAAE,MAAM;AACtC,YAAM,QAAQ,UAAU,EAAE,IAAI;AAC9B,UAAI,OAAO;AACP,cAAM,WAAW,aAAa,SAAS,MAAM,GAAG,KAAK;AACrD,cAAM,WAAW,IAAI,IAAI,KAAK;AAAG,iBAAS,IAAI,cAAc;AAC5D,cAAM,SAAS,gBAAgB,OAAO,UAAU,KAAK,QAAQ;AAC7D,YAAI,OAAQ,GAAE,OAAO,MAAM;MAC/B;AACA,uBAAiB,CAAC;IACtB;AACA,KAAAA,MAAA,EAAE,aAAF,OAAA,SAAAA,IAAY,QAAQ,KAAA;EACxB;AACA,QAAM,IAAI;AACd;AAIA,SAAS,mBAAmB,MAAc,OAAe,SAAuB;AA5RhF,MAAAA;AA6RI,yBAAuB,MAAM,OAAO,OAAO;AAC3C,GAAAA,MAAA,KAAK,aAAL,OAAA,SAAAA,IAAe,QAAQ,CAAA,MAAK,mBAAmB,GAAG,OAAO,OAAO,CAAA;AACpE;AAEA,SAAS,uBAAuB,MAAc,OAAe,SAAuB;AAChF,QAAMM,SAAQ,CAAC,QAA0B;AACrC,eAAW,MAAM,IAAK,KAAI,OAAO,GAAG,SAAS,SAAU,IAAG,OAAO,QAAQ,GAAG,OAAO;EACvF;AACA,MAAI,KAAK,aAAa,OAAO,KAAK,cAAc,YAAY,MAAM,QAAS,KAAK,UAAkB,SAAS,GAAG;AAC1G,IAAAA,OAAO,KAAK,UAAkB,SAAS;EAC3C;AACA,MAAI,KAAK,WAAW,OAAO,KAAK,YAAY,UAAU;AAClD,eAAW,QAAQ,OAAO,KAAK,KAAK,OAAO,GAAG;AAC1C,YAAM,OAAQ,KAAK,QAAgB,IAAI;AACvC,UAAI,QAAQ,MAAM,QAAQ,KAAK,SAAS,EAAG,CAAAA,OAAM,KAAK,SAAS;IACnE;EACJ;AACJ;AC3QO,SAAS,sBACZ,MACA,YACA,KACM;AACN,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,WAAW,WAAW,aAAa,qBAAqB;AAG9D,QAAM,cAAgC,CAAC;AACvC,QAAM,UAAU,CAAC,MAAoB;AACjC,QAAI,MAAM,QAAQ,EAAE,QAAQ,KAAK,EAAE,SAAS,SAAS,GAAG;AACpD,iBAAW,MAAM,EAAE,SAAU,SAAQ,EAAE;AACvC;IACJ;AACA,UAAM,IAAI,OAAO,EAAE,MAAM,WAAW,EAAE,IAAI,aAAa,CAAC;AACxD,QAAI,MAAM,OAAW;AACrB,UAAM,WAAW,qBAAqB,CAAC;AACvC,QAAI,CAAC,SAAS,OAAQ;AACtB,UAAM,QAAmB,EAAE,MAAM,GAAG,UAAU,CAAC,EAAE;AACjD,eAAW,MAAM,UAAU;AACvB,YAAM,WAAW,mBAAmB,EAAE;AACtC,YAAM,SAAS,KAAK,EAAE,SAAS,IAAI,UAAU,eAAe,EAAE,CAAC;IACnE;AACA,gBAAY,KAAK,KAAK;EAC1B;AACA,UAAQ,IAAI;AAEZ,MAAI,CAAC,YAAY,OAAQ,QAAO;AAUhC,MAAI,MAAM;AACV,QAAM,YAAY,WAAW,CAAC,GAAG,WAAW,EAAE,QAAQ,IAAI;AAC1D,aAAW,SAAS,WAAW;AAC3B,eAAW,MAAM,MAAM,UAAU;AAC7B,UAAI,CAAC,SAAU,OAAM;AACrB,SAAG,gBAAgB;AACnB,aAAO,GAAG;IACd;EACJ;AAEA,QAAM,gBAAgB;AACtB,MAAI,YAAY,gBAAgB,KAAO,QAAO;AAW9C,QAAM,gBAAgB,eAAuB,WAAW,MAAM;AAC9D,QAAM,aAA+B,cAAc,SAAA,WAC7C,EAAE,MAAA,UAAuB,OAAO,EAAE,IAClC;AACN,QAAM,eAAe,uBAAuB,WAAW,KAAK;AAC5D,QAAM,YAA8B,aAAa,SAAA,WAC3C,EAAE,MAAA,UAAuB,OAAO,CAAC,GAAG,CAAC,EAAE,IACvC;AAEN,QAAM,eAAe,iBAAiB,UAAU;AAChD,QAAM,YAAY,aAAa,SAAS,KAAK,IAAI,GAAG,YAAY,IAAI;AACpE,QAAM,YAAY,aAAa,SAAS,KAAK,IAAI,GAAG,YAAY,IAAI;AACpE,QAAM,eAAiC,CAAC,WAAW,SAAS;AAK5D,MAAI,YAAY,WAAW,KAAK,YAAY,CAAC,EAAE,SAAS,QAAQ,YAAY,CAAC,EAAE,SAAS,WAAW,GAAG;AAClG,UAAM,QAAQ,YAAY,CAAC;AAC3B,UAAM,KAAK,MAAM,SAAS,CAAC;AAC3B,UAAM,eAAe,WAAW,gBAAgB,GAAG;AACnD,QAAI,eAAe,KAAO,QAAO;AACjC,UAAM,iBAAiB,WAAW,GAAG,gBAAgB,eAAe;AACpE,WAAO,qBAAqB,MAAM,MAAM,cAAc,gBAAgB,cAAc,YAAY,SAAS;EAC7G;AAIA,QAAM,eAAe,oBAAI,IAAoB;AAC7C,aAAW,SAAS,aAAa;AAC7B,UAAM,cAA6B,CAAC;AACpC,eAAW,MAAM,MAAM,UAAU;AAC7B,YAAM,eAAe,WAAW,gBAAgB,GAAG;AACnD,UAAI,eAAe,KAAO;AAC1B,YAAM,iBAAiB,WAAW,GAAG,gBAAgB,eAAe;AACpE,kBAAY,KAAK,GAAG,kBAAkB,MAAM,MAAM,GAAG,SAAS,cAAc,gBAAgB,cAAc,YAAY,WAAW,GAAG,CAAC;IACzI;AACA,iBAAa,IAAI,MAAM,MAAM,gBAAgB,MAAM,MAAM,WAAW,CAAC;EACzE;AAEA,QAAM,OAAO,CAAC,MAAsB;AAChC,UAAM,IAAI,aAAa,IAAI,CAAC;AAC5B,QAAI,EAAG,QAAO;AACd,QAAI,MAAM,QAAQ,EAAE,QAAQ,KAAK,EAAE,SAAS,SAAS,GAAG;AACpD,aAAOJ,eAAAD,gBAAA,CAAA,GAAK,CAAA,GAAL,EAAQ,UAAU,EAAE,SAAS,IAAI,IAAI,EAAE,CAAA;IAClD;AACA,WAAO;EACX;AACA,SAAO,KAAK,IAAI;AACpB;AAeA,SAAS,gBAAgB,MAAc,UAAiC;AACpE,QAAM,UAAkBC,eAAAD,gBAAA,CAAA,GAAK,IAAA,GAAL,EAAW,MAAM,KAAK,SAAS,CAAA;AACvD,SAAO,QAAQ;AACf,SAAO,QAAQ;AACf,SAAO,QAAQ;AACf,SAAO,QAAQ;AACf,SAAO;AACX;AAOA,SAAS,kBACL,MACA,SACA,cACA,gBACA,cACA,YACA,WACA,KACa;AACb,QAAM,qBAAqB,uBAAuB,gBAAgB,cAAc,YAAY;AAC5F,QAAM,mBAAmB,qBAAqB,cAAc,YAAY;AAExE,QAAM,iBAAiB,gBAAgB,YAAY,kBAAkB;AACrE,QAAM,gBAAgB,gBAAgB,WAAW,gBAAgB;AACjE,QAAM,oBAAoB,wBAAwB,SAAS;AAE3D,QAAM,OAAO,gBAAgB,OAAO;AAKpC,QAAM,OAAO,gBAAgB,IAAI;AACjC,YAAU,MAAM,mBAAmB,aAAa;AAChD,YAAU,MAAM,oBAAoB,cAAc;AAClD,YAAU,MAAM,iBAAiB,iBAAiB;AAElD,SAAO,CAAC,IAAI;AAChB;AAKA,SAAS,qBACL,MACA,cACA,gBACA,cACA,YACA,WACM;AACN,QAAM,qBAAqB,uBAAuB,gBAAgB,cAAc,YAAY;AAC5F,QAAM,mBAAmB,qBAAqB,cAAc,YAAY;AAExE,QAAM,OAAeA,gBAAA,CAAA,GAAK,IAAA;AAC1B,SAAO,KAAK;AACZ,YAAU,MAAM,mBAAmB,gBAAgB,WAAW,gBAAgB,CAAC;AAC/E,YAAU,MAAM,oBAAoB,gBAAgB,YAAY,kBAAkB,CAAC;AACnF,YAAU,MAAM,iBAAiB,wBAAwB,SAAS,CAAC;AACnE,SAAO;AACX;AAKA,SAAS,gBAAgB,MAAsB;AAC3C,SAAO,EAAE,MAAM,QAAQ,GAAG,KAAK;AACnC;AAOA,IAAM,mBAAmB;AAGzB,SAAS,uBACL,gBACA,cACA,cAC6B;AAC7B,QAAM,CAAC,MAAM,IAAI,oBAAoB,YAAY;AACjD,SAAO,CAAA,cAAa,gBAAgB,CAAC,YAAY,SAAS,kBAAkB;AAChF;AAIA,SAAS,qBACL,cACA,cACmC;AACnC,QAAM,CAAC,QAAQ,MAAM,IAAI,oBAAoB,YAAY;AACzD,QAAM,UAAU,SAAS,SAAS;AAClC,SAAO,CAAC,aAAqB;AACzB,UAAM,IAAI,MAAM,SAAS,CAAC,GAAG,GAAG,CAAC;AACjC,UAAM,IAAI,MAAM,SAAS,CAAC,GAAG,GAAG,CAAC;AACjC,UAAM,OAAO,KAAK,IAAI,GAAG,CAAC;AAC1B,UAAM,OAAO,KAAK,IAAI,GAAG,CAAC;AAC1B,UAAM,MAAqB,CAAC,CAAC;AAC7B,QAAI,MAAM;AACV,aAAS,IAAI,GAAG,IAAI,SAAS,KAAK;AAC9B,UAAI,KAAK,MAAM,OAAO,YAAY;AAClC,UAAI,MAAM,OAAO,QAAQ,YAAY;AACrC,aAAO,IAAI,QAAQ;IACvB;AACA,QAAI,KAAK,MAAM,gBAAgB;AAC/B,WAAO;EACX;AACJ;AAGA,SAAS,oBAAoB,cAAkD;AAC3E,SAAO;IACH,KAAK,MAAM,KAAK,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;IACvD,KAAK,KAAK,KAAK,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;EAC1D;AACJ;AAYA,SAAS,iBAAiB,GAAoC;AAC1D,MAAI,EAAE,SAAA,SAA0B,QAAO,CAAC;AACxC,MAAI,EAAE,SAAA,SAA0B,QAAO,CAAC,EAAE,KAAK;AAC/C,QAAM,MAAqB,CAAC;AAC5B,aAAW,MAAM,EAAE,WAAW;AAC1B,UAAM,IAAI,cAAc,EAAE;AAC1B,QAAI,OAAO,MAAM,SAAU,KAAI,KAAK,CAAC;EACzC;AACA,SAAO;AACX;AAEA,SAAS,gBAA2B,MAAqB,KAA6C;AAClG,MAAI,KAAK,SAAA,SAA0B,QAAO;AAC1C,MAAI,KAAK,SAAA,SAA0B,QAAO,EAAE,MAAA,UAAuB,OAAO,IAAI,KAAK,KAAK,EAAE;AAC1F,SAAO;IACH,MAAA;IACA,WAAW,KAAK,UAAU,IAAI,CAAA,QAAO;MACjC,MAAM,aAAa,EAAE;MACrB,OAAO,IAAI,cAAc,EAAE,CAAQ;MACnC,QAAQ,eAAe,EAAE;IAC7B,EAAE;IACF,MAAM,KAAK;EACf;AACJ;AAKA,SAAS,UAAa,MAAc,UAAkB,MAA+B;AACjF,MAAI,CAAC,KAAM;AACX,yBAAuB,MAAM,UAAU,IAAI;AAC/C;AAIA,IAAM,kBAAkB;AAOxB,SAAS,wBAAwB,WAAqD;AAClF,QAAM,OAAO,CAAC,MAAuB,EAAE,CAAC,MAAM,EAAE,CAAC;AAEjD,MAAI,UAAU,SAAA,SAA0B,QAAO;AAC/C,MAAI,UAAU,SAAA,SAA0B,QAAO,KAAK,UAAU,KAAK,IAAI,EAAE,MAAA,UAAuB,OAAO,EAAE,IAAI;AAE7G,QAAM,MAAM,UAAU;AACtB,MAAI,UAAU;AACd,MAAI,UAAU;AACd,aAAW,MAAM,KAAK;AAClB,QAAI,KAAK,cAAc,EAAE,CAAW,EAAG,WAAU;QAC5C,WAAU;EACnB;AACA,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,QAAS,QAAO,EAAE,MAAA,UAAuB,OAAO,EAAE;AAItD,QAAM,MAAyB,CAAC;AAChC,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACjC,UAAM,KAAK,IAAI,CAAC;AAChB,UAAM,SAAS,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI;AACpC,UAAM,SAAS,IAAI,IAAI,SAAS,IAAI,IAAI,IAAI,CAAC,IAAI;AACjD,UAAM,IAAI,aAAa,EAAE;AACzB,UAAM,WAAW,KAAK,cAAc,EAAE,CAAW;AACjD,UAAM,WAAW,SAAS,YAAY,KAAK,cAAc,MAAM,CAAW,IAAI;AAC9E,UAAM,WAAW,SAAS,YAAY,KAAK,cAAc,MAAM,CAAW,IAAI;AAE9E,QAAI,YAAY,CAAC,UAAU;AACvB,UAAI,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,CAAC;AAC9B,UAAI,KAAK,EAAE,MAAM,IAAI,iBAAiB,OAAO,EAAE,CAAC;IACpD,WAAW,CAAC,YAAY,UAAU;AAC9B,UAAI,KAAK,EAAE,MAAM,IAAI,iBAAiB,OAAO,EAAE,CAAC;AAChD,UAAI,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,CAAC;IAClC;EACJ;AACA,MAAI,IAAI,UAAU,EAAG,QAAO;AAC5B,SAAO,EAAE,MAAA,YAAyB,WAAW,IAAI;AACrD;AAYA,SAAS,uBAAuB,KAAyD;AACrF,QAAM,IAAI,eAAuB,GAAG;AACpC,MAAI,EAAE,SAAA,WAA4B,QAAO;AAEzC,QAAM,MAAuB,EAAE,UAAU,IAAI,CAAA,QAAO;IAChD,MAAM,aAAa,EAAE;IACrB,OAAO,cAAc,EAAE;IACvB,QAAQ,eAAe,EAAE;EAC7B,EAAE;AAEF,QAAM,aAAa,IAAI,KAAK,CAAA,OAAM,GAAG,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;AAC3D,MAAI,CAAC,YAAY;AACb,WAAO;MACH,MAAA;MACA,WAAW,IAAI,IAAI,CAAA,QAAO,EAAE,MAAM,GAAG,MAAM,OAAO,GAAG,OAAO,QAAQ,GAAG,OAAO,EAAE;IACpF;EACJ;AAEA,QAAM,gBAA+B,CAAC;AACtC,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACjC,UAAM,OAAO,IAAI,IAAI,CAAC;AACtB,UAAM,MAAM,IAAI,CAAC;AACjB,UAAM,QAAQ,KAAK,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC;AAC1C,UAAM,OAAO,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC;AACvC,QAAI,QAAQ,OAAO,GAAG;AAClB,YAAM,IAAI,0BAA0B,MAAM,GAAG;AAC7C,UAAI,MAAM,QAAQ,IAAI,KAAK,QAAQ,IAAI,IAAI,MAAM;AAC7C,sBAAc,KAAK,KAAK,MAAM,CAAC,CAAC;MACpC;IACJ;EACJ;AACA,QAAM,WAAW,MAAM,KAAK,IAAI,IAAI,aAAa,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAExE,QAAM,MAAuB,CAAC;AAC9B,MAAI,IAAI;AACR,aAAW,MAAM,KAAK;AAClB,WAAO,IAAI,SAAS,UAAU,SAAS,CAAC,IAAI,GAAG,MAAM;AACjD,YAAM,IAAI,SAAS,GAAG;AACtB,YAAMM,KAAI,mBAAmB,KAAK,CAAC;AACnC,YAAM,KAAKA,GAAE,CAAC,IAAIA,GAAE,CAAC,KAAK;AAC1B,UAAI,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;IACvC;AACA,UAAM,IAAY,GAAG,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC,IAAI,GAAG;AAC9E,QAAI,KAAK,EAAE,MAAM,GAAG,MAAM,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;EAC3D;AACA,SAAO,IAAI,SAAS,QAAQ;AACxB,UAAM,IAAI,SAAS,GAAG;AACtB,UAAM,IAAI,mBAAmB,KAAK,CAAC;AACnC,UAAM,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK;AAC1B,QAAI,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;EACvC;AAEA,SAAO,EAAE,MAAA,YAAyB,WAAW,IAAI;AACrD;AAEA,SAAS,0BAA0B,MAAgB,KAA8B;AAC7E,QAAM,IAAI,CAAC,MAAsB;AAC7B,UAAM,KAAK,IAAI,KAAK,SAAS,IAAI,OAAO,KAAK;AAC7C,UAAM,KAAK,KAAK,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,KAAK;AAC5D,UAAM,KAAK,KAAK,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,KAAK;AAC5D,WAAO,KAAK;EAChB;AACA,MAAI,KAAK,KAAK,MAAM,KAAK,IAAI;AAC7B,MAAI,MAAM,EAAE,EAAE;AACd,MAAI,QAAQ,EAAG,QAAO;AACtB,QAAM,MAAM,EAAE,EAAE;AAChB,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,MAAM,MAAM,EAAG,QAAO;AAC1B,WAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC1B,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,OAAO,EAAE,GAAG;AAClB,QAAI,SAAS,KAAK,KAAK,IAAI,KAAK,EAAE,IAAI,KAAQ,QAAO;AACrD,QAAI,MAAM,OAAO,GAAG;AAAE,WAAK;IAAK,OAC3B;AAAE,WAAK;AAAK,YAAM;IAAM;EACjC;AACA,UAAQ,KAAK,MAAM;AACvB;AAEA,SAAS,mBAAmB,KAAsB,GAAmB;AACjE,MAAI,KAAK,IAAI,CAAC,EAAE,KAAM,QAAO,IAAI,CAAC,EAAE;AACpC,MAAI,KAAK,IAAI,IAAI,SAAS,CAAC,EAAE,KAAM,QAAO,IAAI,IAAI,SAAS,CAAC,EAAE;AAC9D,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACjC,QAAI,KAAK,IAAI,CAAC,EAAE,MAAM;AAClB,YAAM,OAAO,IAAI,IAAI,CAAC;AACtB,YAAM,MAAM,IAAI,CAAC;AACjB,YAAM,KAAK,IAAI,KAAK,SAAS,IAAI,OAAO,KAAK;AAC7C,aAAO;QACH,KAAK,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,KAAK;QACjD,KAAK,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,KAAK;MACrD;IACJ;EACJ;AACA,SAAO,IAAI,IAAI,SAAS,CAAC,EAAE;AAC/B;AAQA,SAAS,mBAAmB,MAA4B;AACpD,QAAM,IAAI,KAAK;AACf,MAAI,CAAC,KAAK,EAAE,SAAS,EAAG,QAAO;AAC/B,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,EAAE,SAAS,GAAG,KAAK;AACnC,aAAS,cAAc,MAAM,GAAG,IAAI,CAAC;EACzC;AACA,MAAI,KAAK,KAAK,EAAE,SAAS,GAAG;AACxB,aAAS,cAAc,MAAM,EAAE,SAAS,GAAG,CAAC;EAChD;AACA,SAAO;AACX;AAEA,SAAS,cAAc,MAAoB,MAAc,IAAoB;AAnf7E,MAAAP,KAAA,IAAA,IAAA;AAofI,QAAM,IAAI,KAAK;AACf,QAAM,KAAK,EAAE,IAAI;AACjB,QAAM,KAAK,EAAE,EAAE;AACf,QAAM,MAAM,MAAAA,MAAA,KAAK,MAAL,OAAA,SAAAA,IAAS,IAAA,MAAT,OAAA,KAAkB;AAC9B,QAAM,MAAM,MAAA,KAAA,KAAK,MAAL,OAAA,SAAA,GAAS,EAAA,MAAT,OAAA,KAAgB;AAC5B,QAAM,MAAM,sBAAsB,IAAI,IAAI,IAAI,EAAE;AAChD,SAAO,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;AACnC;AAMA,IAAM,YAAY;AAgBlB,SAAS,aAAa,MAAkC;AAjhBxD,MAAAA,KAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA;AAkhBI,MAAI,KAAK,SAAS,QAAQ;AACtB,UAAM,IAAI,QAAOA,MAAA,KAAK,MAAL,OAAAA,MAAU,CAAC,GAAG,IAAI,QAAO,KAAA,KAAK,MAAL,OAAA,KAAU,CAAC;AACrD,UAAM,IAAI,QAAO,KAAA,KAAK,UAAL,OAAA,KAAc,CAAC,GAAG,IAAI,QAAO,KAAA,KAAK,WAAL,OAAA,KAAe,CAAC;AAC9D,WAAO,OAAO,IAAI,KAAK,MAAM,IACzB,OAAO,IAAI,KAAK,OAAO,IAAI,KAC3B,MAAM,IAAI,OAAO,IAAI,KACrB,MAAM,IAAI,MAAM,IAChB,OAAO,IAAI,KAAK,MAAM,IAAI;EAClC;AAEA,MAAI,KAAK,SAAS,aAAa,KAAK,SAAS,UAAU;AACnD,UAAM,KAAK,QAAO,KAAA,KAAK,OAAL,OAAA,KAAW,CAAC,GAAG,KAAK,QAAO,KAAA,KAAK,OAAL,OAAA,KAAW,CAAC;AACzD,UAAM,KAAK,KAAK,SAAS,WAAW,QAAO,KAAA,KAAK,MAAL,OAAA,KAAU,CAAC,IAAI,QAAO,KAAA,KAAK,OAAL,OAAA,KAAW,CAAC;AAC7E,UAAM,KAAK,KAAK,SAAS,WAAW,QAAO,KAAA,KAAK,MAAL,OAAA,KAAU,CAAC,IAAI,QAAO,KAAA,KAAK,OAAL,OAAA,KAAW,CAAC;AAC7E,QAAI,EAAE,KAAK,MAAM,EAAE,KAAK,GAAI,QAAO;AACnC,UAAM,KAAK,KAAK,WAAW,KAAK,KAAK;AACrC,UAAM,IAAI,CAAC,IAAY,IAAY,IAAY,IAAY,GAAW,MAClE,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,IAAI,MAAM;AAChE,WAAO,OAAO,KAAK,MAAM,MAAM,KAC3B,EAAE,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI,KAAK,EAAE,IACjD,EAAE,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,EAAE,IACjD,EAAE,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI,KAAK,EAAE,IACjD,EAAE,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,EAAE,IAAI;EAC7D;AAEA,SAAO;AACX;ACnfO,SAAS,uBAAuB,MAA2B;AAzDlE,MAAAA,KAAA;AA0DI,QAAM,MAAoB;IACtB,MAAM,CAAC;IAAG,UAAU,CAAC;IAAG,QAAQ,CAAC;IACjC,OAAO,oBAAI,IAAI;IAAG,QAAQ;IAC1B,oBAAoB,oBAAI,IAAI;IAC5B,oBAAoB,oBAAI,IAAI;;;IAG5B,QAAQ,uBAAsBA,MAAA,kBAAkB,IAAI,MAAtB,OAAA,SAAAA,IAAyB,MAAM;IAC7D,SAAQ,KAAA,eAAe,IAAI,MAAnB,OAAA,SAAA,GAAsB;EAClC;AAEA,QAAM,UAAU,MAAM,IAAI;AAC1B,YAAU,SAAS,IAAI,KAAK;AAC5B,4BAA0B,SAAS,KAAK,MAAM,MAAM,KAAK,OAAO,CAAC;AACjE,4BAA0B,SAAS,GAAG;AAEtC,QAAM,aAAa,gCAAgC,SAAS,GAAG;AAC/D,QAAM,MAAM,0BAA0B,YAAY,GAAG;AACrD,aAAW,KAAK,IAAI,IAAI;AAExB,SAAO,EAAE,MAAM,KAAK,MAAM,IAAI,MAAM,UAAU,IAAI,UAAU,QAAQ,IAAI,OAAO;AACnF;AAeA,SAAS,gCAAgC,MAAc,KAA2B;AAC9E,MAAI,KAAK,SAAU,MAAK,WAAW,KAAK,SAAS,IAAI,CAAA,UAAS,gCAAgC,OAAO,GAAG,CAAC;AAEzG,QAAM,KAAK,KAAK;AAChB,QAAM,aAAa,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK;AAC3D,QAAM,uBAAuB,aAAa,IAAI,mBAAmB,IAAI,UAAU,IAAI;AAEnF,MAAI,CAAC,MAAM,CAAC,qBAAsB,QAAO;AAEzC,QAAM,EAAE,aAAa,UAAU,UAAU,UAAU,YAAY,OAAO,SAAS,cAAc,gBAAgB,UAAU,KAAK,IAAI,MAAA,OAAA,KAAM,CAAC;AACvI,MAAI,GAAI,QAAO,KAAK;AAEpB,MAAI,IAAI;AAIR,MAAI,mBAAmB;AACvB,MAAI,QAAA,OAAA,SAAA,KAAM,WAAW;AACjB,QAAI,UAAU;AAEV,YAAM,QAAQ,OAAO,SAAS,aAAa,WAAW,SAAS,WAAW;AAI1E,YAAM,UAAU,yBAAyB,GAAG,KAAK,OAAO,SAAS,aAAa,SAAS,YAAY,SAAS,YAAY;AACxH,UAAI,SAAS;AAAE,YAAI;AAAS,2BAAmB;MAAM;IACzD,OAAO;AACH,UAAI,sBAAsB,GAAG,MAAM,GAAG;AACtC,yBAAmB;IACvB;EACJ;AAIA,MAAI,CAAC,iBAAkB,KAAI,oBAAoB,GAAG,UAAU,GAAG;AAM/D,MAAI,wBAAwB,GAAG,cAAc,GAAG;AAChD,MAAI,0BAA0B,GAAG,gBAAgB,GAAG;AACpD,MAAI,sBAAsB,GAAG,YAAY,GAAG;AAC5C,MAAI,oBAAoB,GAAG,UAAU,GAAG;AACxC,MAAI,oBAAoB,GAAG,UAAU,aAAa,GAAG;AACrD,MAAI,oBAAoB,GAAG,UAAU,GAAG;AAExC,MAAI,sBAAsB;AAOtB,iBAAa,GAAG,SAAS,GAAG;AAC5B,QAAI,mBAAmB,GAAG,aAAa,YAAa,sBAAsB,GAAG;EACjF,OAAO;AACH,QAAI,gCAAgC,GAAG,SAAS,aAAa,GAAG;EACpE;AAIA,MAAI,WAAA,OAAA,SAAA,QAAS,OAAQ,MAAK,UAAU,EAAE,OAAO,EAAE,QAAQ,QAAQ,OAAO,EAAE;AACxE,MAAI,WAAY,KAAI,MAAM,IAAI,YAAY,CAAC;AAC3C,SAAO;AACX;AAIA,SAAS,0BAA0B,MAAc,KAA2B;AACxE,wBAAsB,MAAM,GAAG;AAC/B,SAAO;AACX;ACvIA,SAAS,QAAQ,IAAY,IAAY,IAAY,IAAY,GAAmB;AAChF,QAAM,IAAI,IAAI;AACd,QAAM,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI;AACvE,SAAO;IAAC,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC;IAC5C,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC;EAAC;AACzD;AAGA,SAAS,YAAY,IAAY,IAAY,IAAY,IAAY,QAAQ,IAAY;AACrF,MAAI,MAAM;AACV,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,KAAK,OAAO,KAAK;AAC7B,UAAM,KAAK,QAAQ,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK;AAC5C,WAAO,KAAK,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC;AAClD,WAAO;EACX;AACA,SAAO;AACX;AAEA,IAAMQ,OAAM,CAAC,MAAsB;AAC/B,QAAM,IAAI,KAAK,MAAM,IAAI,GAAK,IAAI;AAClC,SAAO,OAAO,GAAG,GAAG,EAAE,IAAI,MAAM,OAAO,CAAC;AAC5C;AAOA,SAAS,gBAAgB,UAEX;AA9Dd,MAAAR,KAAA,IAAA;AA+DI,MAAK,SAAwC,kBAAkB,aAAc,QAAO;AAEpF,QAAM,MAAM,SAAS;AACrB,MAAI,CAAC,OAAO,IAAI,SAAS,EAAG,QAAO;AAYnC,QAAM,QAAQ,cAAc,IAAI,CAAC,CAAC;AAClC,QAAM,UAAiB,SAAA,OAAA,SAAA,MAAO,WAAU,MAAM,OAAO,UAAU,IACzD,CAAC,MAAM,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;AAEhD,QAAM,SAAwB,CAAC;AAC/B,aAAW,MAAM,KAAK;AAClB,UAAM,IAAI,cAAc,EAAE;AAC1B,UAAM,KAAK,KAAA,OAAA,SAAA,EAAG;AACd,QAAI,CAAC,MAAM,GAAG,SAAS,EAAG,QAAO;AACjC,UAAM,QAAQ,OAAO,KAAK,CAAW;AACrC,QAAI,MAAM,KAAK,CAAA,MAAK,MAAM,eAAe,MAAM,QAAQ,EAAG,QAAO;AAGjE,UAAM,KAAIA,MAAA,KAAA,OAAA,SAAA,EAAG,WAAH,OAAAA,MAAa,CAAC,GAAG,CAAC;AAC5B,QAAI,EAAE,CAAC,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,MAAM,OAAO,CAAC,EAAG,QAAO;AACrD,WAAO,KAAK,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,GAAG,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC;EACtD;AAGA,MAAI,CAAC,IAAI,KAAK,CAAA,OAAM,kBAAkB,EAAE,KAAK,mBAAmB,EAAE,CAAC,EAAG,QAAO;AAG7E,MAAI,IAAI,MAAMQ,KAAI,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,MAAMA,KAAI,OAAO,CAAC,EAAE,CAAC,CAAC;AACxD,QAAM,UAAyB,CAAC;AAChC,WAAS,IAAI,GAAG,IAAI,OAAO,SAAS,GAAG,KAAK;AACxC,UAAM,KAAK,OAAO,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC;AACvC,UAAM,MAAK,KAAA,mBAAmB,IAAI,CAAC,CAAC,MAAzB,OAAA,KAA8B,CAAC,GAAG,CAAC;AAC9C,UAAM,MAAK,KAAA,kBAAkB,IAAI,IAAI,CAAC,CAAC,MAA5B,OAAA,KAAiC,CAAC,GAAG,CAAC;AACjD,UAAM,KAAa,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;AAChD,UAAM,KAAa,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;AAChD,SAAK,MAAMA,KAAI,GAAG,CAAC,CAAC,IAAI,MAAMA,KAAI,GAAG,CAAC,CAAC,IAAI,MAAMA,KAAI,GAAG,CAAC,CAAC,IAAI,MAAMA,KAAI,GAAG,CAAC,CAAC,IAAI,MAAMA,KAAI,GAAG,CAAC,CAAC,IAAI,MAAMA,KAAI,GAAG,CAAC,CAAC;AACnH,YAAQ,KAAK,YAAY,IAAI,IAAI,IAAI,EAAE,CAAC;EAC5C;AACA,QAAM,QAAQ,QAAQ,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AAC/C,MAAI,EAAE,QAAQ,GAAI,QAAO;AAIzB,QAAM,cAAiC,CAAC;AACxC,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACjC,QAAI,IAAI,EAAG,QAAO,QAAQ,IAAI,CAAC;AAC/B,UAAM,MAAkB,EAAE,GAAG,aAAa,IAAI,CAAC,CAAC,GAAG,GAAG,MAAM,MAAM;AAClE,UAAM,IAAI,eAAe,IAAI,CAAC,CAAC;AAC/B,QAAI,MAAM,OAAY,KAAwB,IAAI;AAClD,gBAAY,KAAK,GAAG;EACxB;AAEA,SAAO,EAAE,SAAS,GAAG,aAAa,YAAY,CAAC,CAAC,SAAS,YAAY,OAAO;AAChF;AAOO,SAAS,6BAA6B,MAAoD;AAC7F,QAAM,OAAO,CAAC,SAAyB;AAvI3C,QAAAR;AAwIQ,QAAI,MAAM;AACV,UAAM,OAAO,KAAK;AAClB,UAAM,YAAY,QAAA,OAAA,SAAA,KAAO,WAAA;AACzB,QAAI,WAAW;AACX,YAAM,QAAQ,gBAAgB,SAAS;AACvC,UAAI,OAAO;AACP,cAAM,aAAkDC,gBAAA,CAAA,GAAK,IAAA;AAC7D,eAAO,WAAW,cAAc;AAChC,cAAM,WAAgC,EAAE,WAAW,MAAM,YAAqB;AAC9E,YAAI,UAAU,SAAS,OAAW,UAAS,OAAO,UAAU;AAC5D,mBAAW,oBAAoB,IAAI;AAMnC,cAAM,WAAW,KAAK;AACtB,YAAI,eAAe;AACnB,YAAI,YAAY,OAAO,aAAa,UAAU;AAC1C,gBAAM,IAAIA,gBAAA,CAAA,GAAK,QAAA;AACf,iBAAO,EAAE,eAAe,SAAS;AACjC,iBAAO,EAAE,eAAe,MAAM;AAC9B,yBAAe,OAAO,KAAK,CAAC,EAAE,SAAS,IAAI;QAC/C;AAEA,cAAMC,eAAAD,gBAAA,CAAA,GACC,IAAA,GADD;UAEF,SAAS;UACT,OAAOC,eAAAD,gBAAA,CAAA,GACC,KAAK,KAAA,GADN;YAEH,YAAY,WAAW,MAAM,UAAU;YACvC,cAAcO,KAAI,MAAM,OAAO,CAAC,CAAC,IAAI,QAAQA,KAAI,MAAM,OAAO,CAAC,CAAC,IAAI;YACpE,cAAc,MAAM,aAAa,SAAS;YAC1C,gBAAgB;UACpB,CAAA;QACJ,CAAA;AACA,YAAI,iBAAiB,OAAY,KAAgC,YAAY;YACxE,QAAQ,IAAgC;MACjD;IACJ;AACA,SAAIR,MAAA,IAAI,aAAJ,OAAA,SAAAA,IAAc,QAAQ;AACtB,YAAM,WAAW,IAAI,SAAS,IAAI,IAAI;AACtC,UAAI,SAAS,KAAK,CAAC,GAAG,MAAM,MAAM,IAAI,SAAU,CAAC,CAAC,EAAG,OAAME,eAAAD,gBAAA,CAAA,GAAK,GAAA,GAAL,EAAU,SAAS,CAAA;IAClF;AACA,WAAO;EACX;AACA,SAAO,KAAK,IAAI;AACpB;AC/IO,SAAS,gCAAgC,MAAsB;AAxCtE,MAAAD;AAyCI,QAAM,QAAQ,WAAW,IAAI;AAC7B,QAAM,cAAc,0BAA0B,MAAM,KAAK;AACzD,MAAI,YAAY,SAAS,EAAG,QAAO;AAEnC,MAAI,YAAY;AAChB,QAAMS,SAAQ,MAAc,iBAAkB,EAAE;AAMhD,QAAM,eAAe,iBAAiB,IAAI;AAI1C,QAAM,gBAA+B,CAAC;AACtC,QAAM,SAASC,oBAAmB,MAAM,OAAO,aAAaD,QAAO,eAAe,YAAY;AAC9F,MAAI,cAAc,WAAW,EAAG,QAAO;AAMvC,QAAM,WAAmB,EAAE,MAAM,QAAQ,UAAU,cAAc;AACjE,QAAM,cAAc,CAAC,IAAIT,MAAA,OAAO,aAAP,OAAAA,MAAmB,CAAC,GAAI,QAAQ;AACzD,SAAOE,eAAAD,gBAAA,CAAA,GAAK,MAAA,GAAL,EAAa,UAAU,YAAY,CAAA;AAC9C;AAOA,SAAS,iBAAiB,MAAgC;AACtD,QAAM,KAAK,aAAc,KAA+B,OAAO;AAC/D,MAAI,GAAI,QAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;AAC5B,QAAM,IAAI,YAAa,KAA6B,KAAK;AACzD,QAAM,IAAI,YAAa,KAA8B,MAAM;AAC3D,MAAI,MAAM,UAAa,MAAM,OAAW,QAAO,CAAC,GAAG,CAAC;AAIpD,SAAO,CAAC,GAAG,CAAC;AAChB;AAEA,SAAS,YAAY,GAAgC;AACjD,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,MAAI,OAAO,MAAM,UAAU;AAKvB,UAAM,IAAI,WAAW,CAAC;AACtB,WAAO,OAAO,SAAS,CAAC,IAAI,IAAI;EACpC;AACA,SAAO;AACX;AAQA,SAAS,WAAW,MAAmC;AACnD,QAAM,MAAM,oBAAI,IAAoB;AACpC,QAAM,QAAQ,CAAC,MAAoB;AA3GvC,QAAAD;AA4GQ,QAAI,OAAO,EAAE,OAAO,SAAU,KAAI,IAAI,EAAE,IAAI,CAAC;AAC7C,KAAAA,MAAA,EAAE,aAAF,OAAA,SAAAA,IAAY,QAAQ,KAAA;EACxB;AACA,QAAM,IAAI;AACV,SAAO;AACX;AAMA,SAAS,0BAA0B,MAAc,OAAyC;AACtF,QAAM,QAAQ,oBAAI,QAAyB;AAC3C,QAAM,SAAS,oBAAI,IAAY;AAE/B,QAAM,UAAU,CAAC,GAAW,aAAmC;AAC3D,UAAM,SAAS,MAAM,IAAI,CAAC;AAC1B,QAAI,WAAW,OAAW,QAAO;AACjC,QAAI,SAAS,IAAI,CAAC,EAAG,QAAO;AAC5B,aAAS,IAAI,CAAC;AAEd,QAAI,IAAI;AACR,QAAI,EAAE,WAAW,OAAO,EAAE,YAAY,YAAY,CAAC,MAAM,QAAQ,EAAE,OAAO,GAAG;AACzE,iBAAW,KAAK,EAAE,SAAS;AAAE,YAAI;AAAM;MAAO;IAClD;AACA,QAAI,CAAC,KAAK,EAAE,UAAU;AAClB,iBAAW,MAAM,EAAE,UAAU;AACzB,YAAI,QAAQ,IAAI,QAAQ,GAAG;AAAE,cAAI;AAAM;QAAO;MAClD;IACJ;AACA,QAAI,CAAC,KAAK,EAAE,SAAS,SAAS,OAAO,EAAE,SAAS,UAAU;AACtD,YAAM,WAAWW,WAAU,EAAE,IAAI;AACjC,YAAM,SAAS,WAAW,MAAM,IAAI,QAAQ,IAAI;AAChD,UAAI,OAAQ,KAAI,QAAQ,QAAQ,QAAQ;IAC5C;AAEA,aAAS,OAAO,CAAC;AACjB,UAAM,IAAI,GAAG,CAAC;AACd,WAAO;EACX;AAEA,aAAW,CAAC,IAAI,IAAI,KAAK,OAAO;AAC5B,QAAI,QAAQ,MAAM,oBAAI,IAAI,CAAC,EAAG,QAAO,IAAI,EAAE;EAC/C;AACA,SAAO;AACX;AAGA,SAASA,WAAU,MAAmC;AAClD,MAAI,OAAO,SAAS,SAAU,QAAO;AACrC,SAAO,KAAK,WAAW,GAAG,IAAI,KAAK,MAAM,CAAC,IAAI;AAClD;AAKA,SAAS,kBACL,SACA,QACA,OACA,aACAF,QACA,eACA,cACM;AA5KV,MAAAT,KAAA,IAAA,IAAA;AA6KI,QAAMK,SAAQ,gBAAgB,MAAM;AACpC,8BAA4BA,QAAOI,MAAK;AAQxC,QAAM,gBAAgBJ,OAAM,SAAS,WAAW,aAAcA,OAAgC,OAAO,IAAI;AACzG,QAAM,QAAO,MAAAL,MAAA,YAAa,QAAgC,KAAK,MAAlD,OAAAA,MAAuD,iBAAA,OAAA,SAAA,cAAgB,CAAA,MAAvE,OAAA,KAA6E,aAAa,CAAC;AACxG,QAAM,QAAO,MAAA,KAAA,YAAa,QAAiC,MAAM,MAApD,OAAA,KAAyD,iBAAA,OAAA,SAAA,cAAgB,CAAA,MAAzE,OAAA,KAA+E,aAAa,CAAC;AAO1G,QAAM,iBAAiBK,OAAM,SAAS,WAChC,yBAAyBA,QAAOI,QAAO,eAAe,MAAM,IAAI,IAChEJ;AAIN,QAAM,oBAAoBK,oBAAmB,gBAAgB,OAAO,aAAaD,QAAO,eAAe,YAAY;AAMnH,QAAM,UAAkBP,eAAAD,gBAAA,CAAA,GAAK,OAAA,GAAL,EAAc,MAAM,KAAK,UAAU,CAAC,iBAAiB,EAAE,CAAA;AAC/E,SAAQ,QAA8B;AAMtC,SAAQ,QAAgC;AACxC,SAAQ,QAAiC;AACzC,SAAO,kBAAkB,OAAO;AACpC;AAwBA,SAAS,yBACL,YACAQ,QACA,eACA,MACA,MACM;AACN,QAAM,UAAU,aAAc,WAAqC,OAAO;AAC1E,QAAM,IAAYP,eAAAD,gBAAA,CAAA,GAAK,UAAA,GAAL,EAAiB,MAAM,IAAI,CAAA;AAE7C,SAAQ,EAA4B;AACpC,SAAQ,EAAwC;AAChD,SAAQ,EAA0B;AAClC,SAAQ,EAA2B;AAEnC,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,CAAC,KAAK,KAAK,KAAK,GAAG,IAAI;AAI7B,QAAM,QAAQ,MAAM,KAAK,MAAM,IAAI,KAAK,IAAI,OAAO,KAAK,OAAO,GAAG,IAAI;AACtE,QAAM,QAAQ,OAAO,MAAM,SAAS;AACpC,QAAM,QAAQ,OAAO,MAAM,SAAS;AAEpC,QAAM,QAAuB,CAAC;AAC9B,MAAI,SAAS,KAAK,SAAS,EAAG,OAAM,KAAK,eAAe,OAAO,MAAM,OAAO,GAAG;AAC/E,MAAI,UAAU,EAAG,OAAM,KAAK,WAAW,QAAQ,GAAG;AAClD,MAAI,QAAQ,KAAK,QAAQ,EAAG,OAAM,KAAK,eAAgB,CAAC,MAAO,MAAO,CAAC,MAAO,GAAG;AACjF,MAAI,MAAM,OAAS,GAA6B,YAAY,MAAM,KAAK,EAAE;AAEzE,QAAM,SAASQ,OAAM;AACrB,gBAAc,KAAK;IACf,MAAM;IACN,IAAI;IACJ,UAAU,CAAC,EAAE,MAAM,QAAQ,GAAG,KAAK,GAAG,KAAK,OAAO,KAAK,QAAQ,IAAI,CAAC;EACxE,CAAW;AACV,IAA4B,WAAW,UAAU,SAAS;AAC3D,SAAO;AACX;AAGA,SAAS,aAAa,GAA0D;AAC5E,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,QAAM,QAAQ,EAAE,KAAK,EAAE,MAAM,QAAQ,EAAE,IAAI,MAAM;AACjD,MAAI,MAAM,SAAS,KAAK,MAAM,KAAK,CAAA,MAAK,CAAC,OAAO,SAAS,CAAC,CAAC,EAAG,QAAO;AACrE,SAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;AAClD;AAGA,SAASC,oBACL,MACA,OACA,aACAD,QACA,eACA,cACM;AACN,MAAI,KAAK,SAAS,SAAS,OAAO,KAAK,SAAS,UAAU;AACtD,UAAM,WAAWE,WAAU,KAAK,IAAI;AACpC,QAAI,YAAY,YAAY,IAAI,QAAQ,GAAG;AACvC,YAAM,SAAS,MAAM,IAAI,QAAQ;AACjC,UAAI,OAAQ,QAAO,kBAAkB,MAAM,QAAQ,OAAO,aAAaF,QAAO,eAAe,YAAY;IAC7G;EACJ;AAEA,MAAI,CAAC,KAAK,SAAU,QAAO;AAC3B,MAAI,UAAU;AACd,QAAM,cAAc,KAAK,SAAS,IAAI,CAAA,OAAM;AACxC,UAAM,IAAIC,oBAAmB,IAAI,OAAO,aAAaD,QAAO,eAAe,YAAY;AACvF,QAAI,MAAM,GAAI,WAAU;AACxB,WAAO;EACX,CAAC;AACD,SAAO,UAAUP,eAAAD,gBAAA,CAAA,GAAK,IAAA,GAAL,EAAW,UAAU,YAAY,CAAA,IAAI;AAC1D;ACjQO,SAAS,qBACZ,KACA,QACA,SACqB;AA1DzB,MAAAD,KAAA;AA4DI,MAAI,OAAO,uBAAuB,GAAG,EAAE;AAMvC,SAAO,6BAA6B,IAAI;AAKxC,QAAM,YAAW,MAAAA,MAAA,kBAAkB,IAAI,MAAtB,OAAA,SAAAA,IAAyB,aAAzB,OAAA,KAAqC;AACtD,SAAO,+BAA+B,MAAM,QAAQ;AAEpD,MAAI,WAAW,iBAAiB,QAAQ;AAIpC,WAAO,6BAA6B,MAAM,WAAA,OAAA,SAAA,QAAS,UAAU;AAM7D,WAAO,gCAAgC,IAAI;AAM3C,WAAO,sBAAsB,IAAI;EACrC;AAEA,SAAO;AACX;AAkBA,SAAS,sBAAsB,MAAoD;AAC/E,QAAMW,aAAY,CAAC,MAAuB,EAAE,WAAW,GAAG,IAAI,EAAE,MAAM,CAAC,IAAI;AAC3E,QAAM,OAAO,CAAC,GAAW,OAAkC;AAlH/D,QAAAX;AAkHiE,OAAG,CAAC;AAAG,KAAAA,MAAA,EAAE,aAAF,OAAA,SAAAA,IAAY,QAAQ,CAAA,MAAK,KAAK,GAAG,EAAE,CAAA;EAAI;AAE3G,MAAI,UAAU;AACd,SAAO,SAAS;AACZ,cAAU;AACV,UAAM,aAAa,oBAAI,IAAY;AACnC,SAAK,MAAM,CAAA,MAAK;AACZ,UAAI,EAAE,SAAS,SAAS,OAAO,EAAE,SAAS,SAAU,YAAW,IAAIW,WAAU,EAAE,IAAI,CAAC;IACxF,CAAC;AACD,SAAK,MAAM,CAAA,MAAK;AACZ,UAAI,CAAC,EAAE,SAAU;AACjB,UAAI,OAAO,EAAE;AAEb,UAAI,EAAE,SAAS,QAAQ;AACnB,eAAO,KAAK,OAAO,CAAA,MACf,GAAG,EAAE,SAAS,OAAO,EAAE,SAAS,aAAa,OAAO,EAAE,OAAO,YAAY,CAAC,WAAW,IAAI,EAAE,EAAE,EAAE;MACvG;AAEA,aAAO,KAAK,OAAO,CAAA,MAAK,EAAE,EAAE,SAAS,WAAW,CAAC,EAAE,YAAY,EAAE,SAAS,WAAW,GAAG;AACxF,UAAI,KAAK,WAAW,EAAE,SAAS,QAAQ;AAAE,UAAE,WAAW;AAAM,kBAAU;MAAM;IAChF,CAAC;EACL;AACA,SAAO;AACX;AClHO,IAAM,mBAAmB;AAUzB,SAAS,oBAAoB,MAAuB;AACvD,SAAO,OAAO,SAAS,IAAI,KAAK,SAAS;AAC7C;AAUO,SAAS,cAAc,YAAoB,YAA4B;AAC1E,MAAI,EAAE,aAAa,GAAI,QAAO;AAC9B,MAAI,eAAe,SAAU,QAAO;AACpC,SAAO,cAAc,aAAa,IAAI,aAAa;AACvD;AAUO,SAAS,eAAe,YAAoB,YAA4B;AAC3E,MAAI,EAAE,aAAa,GAAI,QAAO;AAC9B,MAAI,eAAe,SAAU,QAAO;AACpC,SAAO,cAAc,aAAa,IAAI,aAAa;AACvD;AAUO,SAAS,YAAY,QAAgB,WAA2B;AACnE,MAAI,OAAO,MAAM,MAAM,KAAK,SAAS,EAAG,QAAO;AAC/C,MAAI,OAAO,SAAS,SAAS,EAAG,QAAO,SAAS,YAAY,YAAY;AACxE,SAAO,OAAO,SAAS,MAAM,IAAI,SAAS;AAC9C;AAUO,SAAS,eAAe,QAAgB,YAAoB,YAA4B;AAC3F,QAAM,OAAO,eAAe,YAAY,UAAU;AAClD,MAAI,EAAE,OAAO,MAAM,CAAC,OAAO,SAAS,MAAM,EAAG,QAAO;AACpD,MAAI,UAAU,EAAG,QAAO;AACxB,MAAI,eAAe,SAAU,QAAQ,SAAS,OAAQ;AACtD,SAAO,UAAU,OAAO,IAAI,SAAS;AACzC;AAGO,SAAS,iBAAiB,UAAkB,YAAoB,YAA4B;AAC/F,QAAM,OAAO,eAAe,YAAY,UAAU;AAClD,MAAI,EAAE,OAAO,MAAM,CAAC,OAAO,SAAS,QAAQ,EAAG,QAAO;AACtD,MAAI,YAAY,EAAG,QAAO;AAC1B,SAAO,YAAY,IAAI,OAAO,WAAW;AAC7C;ACtFA,SAAS,aAAa,IAAwB;AAC1C,QAAM,IAAS;AACf,MAAI,OAAO,EAAE,0BAA0B,WAAY,QAAO,EAAE,sBAAsB,EAAE;AACpF,SAAO,EAAE,WAAW,IAAI,EAAE;AAC9B;AAEA,SAAS,YAAY,QAAsB;AACvC,QAAM,IAAS;AACf,MAAI,OAAO,EAAE,yBAAyB,YAAY;AAAE,MAAE,qBAAqB,MAAM;AAAG;EAAQ;AAC5F,IAAE,aAAa,MAAM;AACzB;AAwBO,SAAS,sBACZ,KACA,SACA,WACa;AArDjB,MAAAC;AAuDI,QAAM,SAAS,kBAAkB,GAAG,KAAK,CAAC;AAE1C,QAAM,WAAW,kBAAkB,KAAK,iBAAiB,EAAE;AAG3D,QAAM,cAAc,OAAO;AAC3B,MAAI,aAAqB;AACzB,MAAI,OAAO,gBAAgB,SAAU,cAAa,eAAe;AACjE,MAAI,gBAAgB,WAAY,cAAa;AAC7C,MAAI,aAAa,EAAG,cAAa;AAEjC,QAAM,WAAW,EAAE,OAAO,YAAY;AACtC,QAAM,gBAAgB,YAAY,aAC9B,YAAY,eAAe,WAAW,WAAW,cAChD,YAAY,cAAA,OAAA,aAAc,KAAK,WAAW;AAG/C,QAAM,YAAY,OAAO,aAAa;AAKtC,QAAM,QAAOA,MAAA,OAAO,SAAP,OAAAA,MAAe;AAC5B,QAAM,gBAAgB,SAAS,cAAc,SAAS;AACtD,QAAM,iBAAiB,SAAS,eAAe,SAAS;AAIxD,MAAI,UAAyB;AAC7B,MAAI,UAAU;AAOd,MAAI,wBAAwB,EAAE,OAAO,SAAS;AAC9C,MAAI,gBAAgB;AACpB,MAAI,eAAe;AAEnB,MAAI,eAAe;AAGnB,QAAM,YAAY,OAAO;AACzB,QAAM,qBAAqB,aAAa,YAAY,IAAI,MAAO,YAAY;AAC3E,MAAI,eAAe;AAInB,QAAM,iBAAiB,MAAM;AAEzB,UAAM,iBAAiB,iBAAiB,KAAK,IAAI,IAAI,iBAAiB,eAAe;AACrF,WAAO,wBAAwB;EACnC;AAEA,QAAM,qBAAqB,MAAM;AAC7B,QAAI,OAAO,eAAe;AAG1B,QAAI,OAAO,SAAS,aAAa,KAAK,OAAQ,cAA0B,QAAO;AAC/E,QAAI,OAAO,EAAG,QAAO;AACrB,WAAO;EACX;AAOA,WAAS,YAAY,eAAuB;AAExC,aAAS,uBAAuB;AAE5B,YAAM,eAAe,WAAW,IAAI,WAAW;AAE/C,UAAI,cAAc;AAClB,UAAI,YAAY;AAChB,UAAI,WAAW,GAAG;AAad,wBAAgB,MAAM,eAAe,GAAG,aAAa,YAAY;AAEjE,oBAAY,KAAK,IAAI,GAAG,KAAK,KAAK,gBAAgB,YAAY,IAAI,CAAC;AACnE,oBAAY,KAAK,IAAI,WAAW,aAAa,CAAC;AAG9C,cAAM,gBAAgB,gBAAgB,YAAY;AAGlD,sBAAc,MAAM,gBAAgB,cAAc,GAAG,CAAC;MAC1D,OAAO;AACH,sBAAc;MAClB;AAMA,UAAI,eAAe;AAMnB,YAAM,MAAM,aAAa;AACzB,UAAIC,qBAAoB;AACxB,UAAI,QAAQ,WAAW;AACnBA,6BAAoB,IAAI;MAC5B,WAAW,QAAQ,aAAa;AAC5B,YAAI,YAAY,MAAM,EAAGA,sBAAoB,IAAI;MACrD,WAAW,QAAQ,qBAAqB;AAEpC,YAAI,YAAY,MAAM,EAAGA,sBAAoB,IAAI;MACrD;AACA,aAAOA;IACX;AACA,QAAI,oBAAoB,qBAAqB;AAM7C,eAAW,WAAW,YAAY,CAAC,GAAG;AAClC,YAAM,UAAU,QAAQ;AACxB,UAAI,CAAC,WAAW,OAAO,YAAY,YAAY,MAAM,QAAQ,OAAO,GAAG;AACnE,gBAAQ,KAAK,+BAA+B,OAAO;AACnD;MACJ;AAGA,YAAM,iBAAiB,oBAAoB,SAAS,oBAAoB,QAAQ;AAGhF,iBAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,cAAc,GAAG;AAC5D,gBAAQ,aAAa,QAAQ,IAAI,UAAU,KAAK;MACpD;IACJ;EACJ;AAKA,QAAM,OAAO,MAAM;AA/MvB,QAAAD,MAAA;AAkNQ,QAAI,CAAC,QAAQ,YAAY,GAAG;AAGxB,gBAAU;AACV;IACJ;AAEA,UAAM,cAAc,mBAAmB;AAMvC,UAAM,UAAU,eAAe;AAC/B,QAAI,UAAU,KAAK,eAAe,GAAG;AACjC,UAAI,eAAgB,aAAY,CAAC;AACjC;IACJ;AAMA,QAAI,eAAe,KAAK,WAAW,GAAG;AAClC,gBAAU;AACV,kBAAY,CAAC;AACb,UAAI,CAAC,cAAc;AACf,uBAAe;AACf,SAAAA,OAAA,aAAA,OAAA,SAAA,UAAW,aAAX,OAAA,SAAAA,KAAA,KAAA,SAAA;MACJ;AACA;IACJ;AAGA,QAAI,eAAe,KAAK,iBAAiB,OAAO,SAAS,aAAa,KAAK,eAAgB,eAA0B;AACjH,gBAAU;AAIV,kBAAY,gBAAiB,gBAA2B,CAAC;AAEzD,UAAI,CAAC,cAAc;AACf,uBAAe;AACf,SAAA,KAAA,aAAA,OAAA,SAAA,UAAW,aAAX,OAAA,SAAA,GAAA,KAAA,SAAA;MACJ;AACA;IACJ;AAKA,QAAI,qBAAqB,GAAG;AACxB,YAAM,MAAM,KAAK,IAAI;AACrB,UAAI,gBAAiB,MAAM,eAAgB,oBAAoB;AAE3D;MACJ;AACA,qBAAe;IACnB;AAGA,gBAAY,WAAW;EAC3B;AAMA,MAAI,OAAO,OAAO;AACd,QAAI,OAAO,QAAQ,GAAG;AAElB,kBAAY,mBAAmB,CAAC;IACpC,WAAW,gBAAgB;AAGvB,kBAAY,CAAC;IACjB;EACJ;AAIA,QAAM,aAAa,MAAM;AACrB,QAAI,CAAC,QAAS,QAAO;AACrB,QAAI,CAAC,QAAQ,YAAY,GAAG;AAAE,aAAO;IAAO;AAC5C,QAAI,eAAe,GAAG;AAElB,aAAO,eAAe,IAAI;IAC9B;AACA,QAAI,OAAO,SAAS,aAAa,KAAK,mBAAmB,KAAM,cAA0B,QAAO;AAChG,WAAO;EACX;AAEA,QAAM,WAAW,CAAC,YAAsB;AACpC,QAAI,YAAY,MAAM;AAClB,kBAAY,OAAO;AACnB,gBAAU;IACd;AAEA,QAAI,EAAE,WAAW,WAAW,IAAI;AAE5B;IACJ;AAEA,cAAU,aAAa,MAAM;AACzB,gBAAU;AACV,WAAK;AACL,eAAS;IACb,CAAC;EACL;AAEA,QAAM,YAAY,MAAM;AACpB,QAAI,QAAS;AAOb,QAAI,gBAAgB,GAAG;AACnB,UAAI,OAAO,SAAS,aAAa,KAAK,yBAA0B,eAA0B;AACtF,gCAAwB;MAC5B;IACJ,OAAO;AAEH,UAAI,yBAAyB,GAAG;AAC5B,YAAI,CAAC,OAAO,SAAS,aAAa,GAAG;AAGjC,kBAAQ,KAAK,0EAA0E;AACvF;QACJ;AACA,gCAAwB;MAC5B;IACJ;AAIA,mBAAe;AAEf,cAAU;AACV,oBAAgB,KAAK,IAAI;AACzB,aAAS,IAAI;EACjB;AAEA,QAAM,YAAY,MAAM;AACpB,QAAI,CAAC,QAAS;AAId,QAAI,MAAM,eAAe;AACzB,QAAI,OAAO,SAAS,aAAa,KAAK,MAAO,cAA0B,OAAM;AAC7E,4BAAwB;AACxB,oBAAgB;AAChB,cAAU;AAEV,QAAI,YAAY,MAAM;AAClB,kBAAY,OAAO;AACnB,gBAAU;IACd;AAIA,QAAI,OAAO,GAAG;AACV,kBAAY,GAAG;IACnB,WAAW,gBAAgB;AACvB,kBAAY,CAAC;IACjB;EACJ;AAEA,QAAM,aAAa,MAAM;AA3X7B,QAAAA;AA4XQ,cAAU;AAEV,4BAAwB;AACxB,oBAAgB;AAChB,cAAU;AACV,mBAAe;AAEf,gBAAY,qBAAqB;AAEjC,KAAAA,OAAA,aAAA,OAAA,SAAA,UAAW,aAAX,OAAA,SAAAA,KAAA,KAAA,SAAA;EACJ;AAEA,QAAM,aAAa,CAAC,eAAe,SAAS;AAxYhD,QAAAA;AA0YQ,QAAI,OAAO,SAAS,aAAa,GAAG;AAChC,8BAAwB;IAC5B,OAAO;AAEH,8BAAwB,mBAAmB;IAC/C;AACA,oBAAgB;AAChB,cAAU;AAGV,QAAI,YAAY,MAAM;AAClB,kBAAY,OAAO;AACnB,gBAAU;IACd;AAIA,gBAAY,gBAAgB,wBAAwB,CAAC;AAErD,QAAI,gBAAgB,CAAC,cAAc;AAC/B,qBAAe;AACf,OAAAA,OAAA,aAAA,OAAA,SAAA,UAAW,aAAX,OAAA,SAAAA,KAAA,KAAA,SAAA;IACJ;EACJ;AAIA,QAAM,MAAqB;IAEvB,WAAW,MAAM;IAEjB,kBAAkB,MAAM;IAExB,aAAa,MAAe;AAAE,aAAO,WAAW;IAAG;IAEnD,QAAQ,MAAM;AA7atB,UAAAA;AA8aY,gBAAU;AACV,OAAAA,OAAA,aAAA,OAAA,SAAA,UAAW,WAAX,OAAA,SAAAA,KAAA,KAAA,SAAA;IACJ;IACA,SAAS,MAAM;AAjbvB,UAAAA;AAkbY,gBAAU;AACV,OAAAA,OAAA,aAAA,OAAA,SAAA,UAAW,YAAX,OAAA,SAAAA,KAAA,KAAA,SAAA;IACJ;IACA,UAAU,MAAM;AACZ,iBAAW;IAEf;IACA,UAAU,MAAM;AACZ,iBAAW,IAAI;IACnB;IAEA,mBAAmB,CAAC,SAAiB;AACjC,UAAI,CAAC,oBAAoB,IAAI,GAAG;AAC5B,gBAAQ,KAAK,gBAAgB;AAC7B;MACJ;AAIA,UAAI,UAAU,eAAe;AAC7B,UAAI,OAAO,SAAS,aAAa,KAAK,UAAW,cAA0B,WAAU;AAErF,UAAK,OAAO,MAAQ,eAAe,EAAI,gBAAe;AACtD,qBAAe;AACf,8BAAwB;AACxB,UAAI,QAAS,iBAAgB,KAAK,IAAI;IAC1C;IAEA,kBAAkB,MAAqB;AAAE,aAAO,mBAAmB;IAAG;IAEtE,kBAAkB,CAAC,YAAoB;AAEnC,gBAAU,YAAY,SAAS,aAAuB;AAEtD,8BAAwB;AACxB,UAAI,QAAS,iBAAgB,KAAK,IAAI;AAEtC,UAAI,CAAC,OAAO,SAAS,aAAa,KAAK,UAAW,cAA0B,gBAAe;AAE3F,kBAAY,mBAAmB,CAAC;IACpC;IAEA,sBAAsB,MAAqB,eAAe,mBAAmB,GAAG,UAAU,UAAU;IAEpG,sBAAsB,CAAC,aAAqB;AACxC,UAAI,eAAe,iBAAiB,UAAU,UAAU,UAAU,CAAC;IACvE;IAEA,WAAW,MAAM;AAlezB,UAAAA;AAmeY,UAAI,OAAO;AACX,OAAAA,OAAA,aAAA,OAAA,SAAA,UAAW,aAAX,OAAA,SAAAA,KAAA,KAAA,SAAA;IACJ;EACJ;AAIA,SAAO;AACX;AC7bO,IAAK,mBAAL,kBAAKE,sBAAL;AASHA,oBAAAA,kBAAA,aAAA,IAAc,IAAA,IAAd;AAMAA,oBAAAA,kBAAA,sBAAA,IAAuB,IAAA,IAAvB;AAMAA,oBAAAA,kBAAA,YAAA,IAAa,IAAA,IAAb;AAMAA,oBAAAA,kBAAA,sBAAA,IAAuB,IAAA,IAAvB;AAQAA,oBAAAA,kBAAA,cAAA,IAAe,IAAA,IAAf;AAOAA,oBAAAA,kBAAA,yBAAA,IAA0B,IAAA,IAA1B;AAMAA,oBAAAA,kBAAA,YAAA,IAAa,IAAA,IAAb;AAMAA,oBAAAA,kBAAA,0BAAA,IAA2B,IAAA,IAA3B;AAGAA,oBAAAA,kBAAA,YAAA,IAAa,IAAA,IAAb;AAMAA,oBAAAA,kBAAA,mBAAA,IAAoB,IAAA,IAApB;AAKAA,oBAAAA,kBAAA,gBAAA,IAAiB,IAAA,IAAjB;AAMAA,oBAAAA,kBAAA,mBAAA,IAAoB,IAAA,IAApB;AAGAA,oBAAAA,kBAAA,eAAA,IAAgB,IAAA,IAAhB;AAMAA,oBAAAA,kBAAA,uBAAA,IAAwB,IAAA,IAAxB;AAMAA,oBAAAA,kBAAA,uBAAA,IAAwB,IAAA,IAAxB;AAKAA,oBAAAA,kBAAA,+BAAA,IAAgC,IAAA,IAAhC;AAGAA,oBAAAA,kBAAA,yBAAA,IAA0B,IAAA,IAA1B;AAMAA,oBAAAA,kBAAA,sBAAA,IAAuB,IAAA,IAAvB;AAMAA,oBAAAA,kBAAA,sBAAA,IAAuB,IAAA,IAAvB;AAGAA,oBAAAA,kBAAA,uBAAA,IAAwB,IAAA,IAAxB;AAGAA,oBAAAA,kBAAA,sBAAA,IAAuB,IAAA,IAAvB;AAKAA,oBAAAA,kBAAA,cAAA,IAAe,IAAA,IAAf;AASAA,oBAAAA,kBAAA,sBAAA,IAAuB,IAAA,IAAvB;AAQAA,oBAAAA,kBAAA,iBAAA,IAAkB,IAAA,IAAlB;AAMAA,oBAAAA,kBAAA,gBAAA,IAAiB,IAAA,IAAjB;AAMAA,oBAAAA,kBAAA,kBAAA,IAAmB,IAAA,IAAnB;AAMAA,oBAAAA,kBAAA,eAAA,IAAgB,IAAA,IAAhB;AA3JQ,SAAAA;AAAA,GAAA,oBAAA,CAAA,CAAA;ACPZ,IAAM,iBAAiB;EACnB;EAAkB;EAAU;EAAW;EAAS;EAChD;EAAa;EAAQ;EAAiB;EAAY;EAAQ;AAC9D;AAGA,IAAM,eAAe,CAAC,eAAe,UAAU;AAE/C,IAAM,gBAAgB,CAAC,MACnB,CAAC,CAAC,KAAK,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,CAAC;AAGpD,IAAM,iBAAiB,CAAC,MACnB,cAAc,CAAC,KAAK,OAAO,EAAE,SAAS,WAAY,EAAE,OAAO;AAEhE,IAAM,cAAc,CAAC,SAA0B,SAAS,YAAY,SAAS;AAO7E,SAAS,WAAW,MAAe,OAAiD;AAChF,QAAM,MAA2B,cAAc,IAAI,IAAIC,gBAAA,CAAA,GAAK,IAAA,IAAS,CAAC;AACtE,aAAW,OAAO,OAAO,KAAK,KAAK,GAAG;AAClC,UAAM,QAAQ,MAAM,GAAG;AACvB,QAAI,UAAU,MAAM;AAAE,aAAO,IAAI,GAAG;AAAG;IAAU;AACjD,QAAI,cAAc,KAAK,GAAG;AAAE,UAAI,GAAG,IAAI,WAAW,IAAI,GAAG,GAAG,KAAK;AAAG;IAAU;AAC9E,QAAI,GAAG,IAAI;EACf;AACA,SAAO;AACX;AAeA,SAAS,cAAc,MAAe,OAA4B,MAAgD;AAC9G,QAAM,WAAW,eAAe,IAAI;AACpC,QAAM,iBAAiB,cAAc,KAAK,KAAK,OAAO,MAAM,SAAS;AACrE,QAAM,YAAY,iBAAiB,OAAO,MAAM,IAAI,IAAI;AAExD,MAAI;AACJ,MAAI,cAAc,UAAU;AAExB,UAAM,UAA+B,CAAC;AACtC,QAAI,cAAc,IAAI,GAAG;AACrB,iBAAW,KAAK,yBAAyB;AACrC,YAAI,KAAK,CAAC,MAAM,OAAW,SAAQ,CAAC,IAAI,KAAK,CAAC;MAClD;IACJ;AACA,aAAS,WAAW,SAAS,KAAK;EACtC,OAAO;AACH,aAAS,WAAW,MAAM,KAAK;EACnC;AAEA,MAAI,YAAY,SAAS,GAAG;AACxB,eAAW,KAAK,4BAA4B;AACxC,UAAI,OAAO,CAAC,MAAM,OAAW;AAC7B,WAAK,uBAAuB,IAAI,qBAAqB,YAAY,2BAAsB;AACvF,aAAO,OAAO,CAAC;IACnB;AACA,QAAI,OAAO,eAAe,YAAY;AAClC,WAAK,wFAAmF;AACxF,aAAO,OAAO;IAClB;EACJ;AACA,SAAO;AACX;AAwDO,SAAS,qBACZ,UACA,WACiC;AACjC,MAAI;AACJ,MAAI,OAAO,aAAa,UAAU;AAC9B,QAAI;AACA,aAAO,KAAK,MAAM,QAAQ;IAC9B,SAAS,GAAG;AACR,cAAQ,KAAK,oDAA+C,CAAC;AAC7D,aAAO;IACX;EACJ,OAAO;AACH,WAAO,YAAA,OAAA,WAAY;EACvB;AAEA,QAAM,EAAE,UAAU,OAAO,YAAY,MAAM,IAAI;AAC/C,MAAI,aAAa,UAAa,UAAU,UAAa,eAAe,UAAa,UAAU,QAAW;AAClG,WAAO,SAAS,SAAY,SAAY,EAAE,UAAU,KAAK;EAC7D;AAEA,QAAM,MAA2B,cAAc,IAAI,IAAIA,gBAAA,CAAA,GAAK,IAAA,IAAS,CAAC;AACtE,MAAI,aAAa,OAAW,KAAI,WAAW;AAC3C,MAAI,UAAU,OAAW,KAAI,QAAQ;AACrC,MAAI,eAAe,OAAW,KAAI,aAAa;AAC/C,MAAI,UAAU,QAAW;AACrB,QAAI,UAAUC,eAAAD,gBAAA,CAAA,GAAM,IAAI,OAAA,GAAV,EAAuD,MAAM,CAAA;EAC/E;AACA,SAAO,EAAE,UAAU,IAAI;AAC3B;AAQO,SAAS,oBACZ,MACA,OAC2B;AAC3B,QAAM,WAA0B,CAAC;AACjC,QAAM,OAAO,CAAC,MAAc,SAAS,KAAK,CAAC;AAE3C,MAAI,UAAU,KAAM,QAAO,EAAE,QAAQ,QAAW,SAAS;AACzD,MAAI,CAAC,cAAc,KAAK,KAAK,OAAO,KAAK,KAAK,EAAE,WAAW,GAAG;AAC1D,WAAO,EAAE,QAAQ,MAAM,SAAS;EACpC;AAKA,QAAM,aAAa,cAAc,IAAI,IAAI,eAAe,OAAO,CAAA,MAAM,KAAa,CAAC,MAAM,MAAS,IAAI,CAAC;AACvG,MAAI,WAAW,QAAQ;AACnB,SAAK,2DAA2D,WAAW,KAAK,IAAI,IAAI,4CACzC;EACnD;AAEA,QAAM,MAA2B,cAAc,IAAI,IAAIA,gBAAA,CAAA,GAAK,IAAA,IAAS,CAAC;AACtE,aAAW,OAAO,OAAO,KAAK,KAAK,GAAG;AAClC,UAAM,QAAQ,MAAM,GAAG;AACvB,QAAI,UAAU,MAAM;AAAE,aAAO,IAAI,GAAG;AAAG;IAAU;AACjD,QAAI,QAAQ,YAAY;AACpB,UAAI,WAAW,cAAc,KAAK,IAAI,cAAc,IAAI,UAAU,OAAO,IAAI,IAAI;AACjF;IACJ;AACA,QAAI,cAAc,KAAK,GAAG;AAAE,UAAI,GAAG,IAAI,WAAW,IAAI,GAAG,GAAG,KAAK;AAAG;IAAU;AAC9E,QAAI,GAAG,IAAI;EACf;AACA,SAAO,EAAE,QAAQ,KAAyB,SAAS;AACvD;AAYO,SAAS,oBACZ,KACA,OACA,SACuD;AAlQ3D,MAAAH,KAAA;AAmQI,QAAM,QAAQ,CAAC,EAAC,WAAA,OAAA,SAAA,QAAS;AACzB,MAAI,CAAC,QAAQ,UAAU,UAAc,CAAC,UAAU,UAAU,QAAQ,CAAC,cAAc,KAAK,KAAK,CAAC,OAAO,KAAK,KAAK,EAAE,UAAW;AACtH,WAAO,EAAE,KAAK,UAAU,CAAC,EAAE;EAC/B;AAEA,QAAM,SAAS;AACf,QAAM,SAAS,cAAc,OAAO,QAAQ;AAC5C,QAAM,SAAS,CAAC,UAAU,eAAcA,MAAA,OAAO,SAAP,OAAA,SAAAA,IAAa,QAAQ;AAC7D,QAAM,UAAwC,SAAS,OAAO,WACxD,SAAS,OAAO,KAAK,WACrB;AAEN,QAAM,WAA0B,CAAC;AACjC,MAAI,UAAU,eAAc,KAAA,OAAO,SAAP,OAAA,SAAA,GAAa,QAAQ,GAAG;AAChD,aAAS,KAAK,6EAA6E;EAC/F;AAEA,MAAI,OAAO;AACX,MAAI,OAAO;AAEP,UAAM,OAA4B,CAAC;AACnC,eAAW,KAAK,cAAc;AAC1B,UAAI,cAAc,OAAO,KAAM,QAAgB,CAAC,MAAM,OAAW,MAAK,CAAC,IAAK,QAAgB,CAAC;IACjG;AACA,WAAO;EACX;AAEA,QAAM,SAAS,oBAAoB,MAAM,SAAA,OAAA,QAAS,CAAC,CAAC;AACpD,WAAS,KAAK,GAAG,OAAO,QAAQ;AAChC,MAAI,OAAO,WAAW,QAAS,QAAO,EAAE,KAAK,SAAS;AAEtD,MAAI,QAAQ;AACR,WAAO;MACH,KAAKI,eAAAD,gBAAA,CAAA,GAAK,MAAA,GAAL,EAAa,MAAMC,eAAAD,gBAAA,CAAA,GAAK,OAAO,IAAA,GAAZ,EAAkB,UAAU,OAAO,OAAO,CAAA,EAAE,CAAA;MACpE;IACJ;EACJ;AACA,SAAO,EAAE,KAAKC,eAAAD,gBAAA,CAAA,GAAK,MAAA,GAAL,EAAa,UAAU,OAAO,OAAO,CAAA,GAA4B,SAAS;AAC5F;;;ACvPO,IAAM,6BAA6B;AAAA,EACtC,WAAW,oBAAoB;AAAA,EAC/B,qBAAqB,oBAAoB;AAAA,EACzC,oBAAoB,oBAAoB;AAC5C;AAIA,IAAM,kBAAiC,MAAM,KAAK,EAAE,QAAQ,GAAG,GAAG,CAAC,GAAG,MAAM,IAAI,EAAE;AASlF,SAAS,eAAe,OAA0C;AAnElE,MAAAE,KAAA;AAoEI,QAAM,SAAS,MAAM;AACrB,QAAM,UAAU,MAAM;AAGtB,MAAI,EAAC,iCAAQ,WAAU,CAAC,QAAS,QAAO,MAAM;AAK9C,QAAM,OAAO,OAAO,WAAW,eAAe,OAAO,cAAc,OAAO,cAAc;AACxF,QAAM,YAAW,MAAAA,MAAA,MAAM,eAAN,gBAAAA,IAAkB,WAAlB,YAA4B;AAC7C,QAAM,WAAW,KAAK,IAAI,MAAM,QAAQ;AACxC,QAAM,QAAQ,KAAK,IAAI,OAAO,QAAQ,OAAO,SAAS,QAAQ,IAAI,WAAW,OAAO,MAAM;AAC1F,SAAO,QAAQ,IAAI,QAAQ,SAAS,QAAQ,MAAM;AACtD;AAGA,SAAS,SAAS,MAAoC;AAClD,SAAO;AAAA,IACH,cAAc,MAAM,KAAK,KAAK;AAAA,IAC9B,SAAS,MAAM;AAAA,IAAyB;AAAA,EAC5C;AACJ;AAIA,SAAS,QAAQ,WAAgD;AAC7D,SAAO,cAAc,kBAAkB;AAC3C;AAIO,SAAS,qBAAqB,MAAe,SAAwB,MAAoC;AAC5G,MAAI,CAAC,QAAQ,QAAQ,SAAS,EAAG,QAAO,SAAS,IAAI;AACrD,MAAI,OAAO,yBAAyB,YAAa,QAAO,SAAS,IAAI;AAErE,QAAM,YAAY,QAAQ;AAC1B,QAAM,aAAa,QAAQ;AAM3B,MAAI,SAA8B;AAElC,MAAI,eAAe;AAEnB,MAAI,eAAe;AAEnB,MAAI,YAAY;AAChB,MAAI;AAEJ,QAAM,oBAAoB,MAAY;AAClC,QAAI,cAAc,QAAW;AACzB,mBAAa,SAAS;AACtB,kBAAY;AAAA,IAChB;AAAA,EACJ;AAEA,QAAM,OAAO,MAAY;AACrB,gBAAY;AACZ,QAAI,WAAW,KAAM;AACrB,aAAS;AAET,QAAI,gBAAgB,cAAc;AAC9B,qBAAe;AACf,qBAAe;AACf,WAAK,KAAK;AAAA,IACd;AAAA,EACJ;AAEA,QAAM,QAAQ,MAAY;AACtB,sBAAkB;AAClB,QAAI,WAAW,MAAO;AACtB,aAAS;AAGT,QAAI,CAAC,KAAK,UAAU,EAAG;AACvB,QAAI,QAAQ,cAAc,kBAAkB,OAAO;AAC/C,WAAK,OAAO;AAAA,IAChB,OAAO;AACH,WAAK,MAAM;AAAA,IACf;AACA,mBAAe;AAAA,EACnB;AAIA,QAAMC,SAAQ,CAAC,UAAwB;AACnC,gBAAY;AACZ,QAAI,OAAO,aAAa,eAAe,SAAS,oBAAoB,UAAU;AAC1E,YAAM;AACN;AAAA,IACJ;AACA,QAAI,SAAS,WAAW;AACpB,UAAI,WAAW,QAAQ,cAAc,OAAW;AAChD,UAAI,aAAa,EAAG,aAAY,WAAW,MAAM,UAAU;AAAA,UACtD,MAAK;AACV;AAAA,IACJ;AACA,QAAI,SAAS,GAAG;AACZ,YAAM;AACN;AAAA,IACJ;AAGA,QAAI,WAAW,KAAM,mBAAkB;AAAA,EAC3C;AAEA,QAAM,WAAW,IAAI,qBAAqB,aAAW;AACjD,UAAM,OAAO,QAAQ,QAAQ,SAAS,CAAC;AACvC,QAAI,KAAM,CAAAA,OAAM,KAAK,iBAAiB,eAAe,IAAI,IAAI,CAAC;AAAA,EAClE,GAAG,EAAE,WAAW,gBAAgB,CAAC;AACjC,WAAS,QAAQ,IAAI;AAErB,QAAM,qBAAqB,MAAY;AAAE,IAAAA,OAAM,SAAS;AAAA,EAAG;AAC3D,QAAM,cAAc,OAAO,aAAa;AACxC,MAAI,YAAa,UAAS,iBAAiB,oBAAoB,kBAAkB;AAEjF,SAAO;AAAA,IACH,cAAc,CAAC,cAAuB;AAElC,UAAI,aAAa,WAAW,MAAM;AAC9B,0BAAkB;AAClB,iBAAS;AACT,uBAAe;AACf,uBAAe;AACf,aAAK,KAAK;AACV;AAAA,MACJ;AACA,qBAAe;AAAA,IACnB;AAAA,IACA,SAAS,MAAM;AACX,wBAAkB;AAClB,eAAS,WAAW;AACpB,UAAI,YAAa,UAAS,oBAAoB,oBAAoB,kBAAkB;AAAA,IACxF;AAAA,EACJ;AACJ;;;ACrLO,IAAM,sBAAsB;","names":["_a","__spreadValues","__spreadValues","__spreadProps","__spreadValues","_a","__spreadValues","__objRest","_a","_a","__spreadValues","__spreadProps","_a","__spreadProps","__spreadValues","str","pathStr","out","clamp","genId","_b","_c","_a","__spreadProps","__spreadValues","_a","__spreadValues","__spreadProps","kfs","out","clone","remap","v","fmt","genId","walkAndMaterialize","stripHash","_a","effectiveProgress","PxDiagnosticCode","__spreadValues","__spreadProps","_a","apply"]}