gantt-renderer 0.1.0

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,"file":"index.mjs","names":["toTask","MIN_PANE_WIDTH","#container","#opts","#height","#locale","#timelineMinWidth","#columns","#leftPaneDefaultWidth","#weekendDays","#specialDaysByDate","#expandedIds","#cbs","#input","#scale","#taskIndex","#selectedId","#scheduleRender","#patchTask","#buildDom","#wireEvents","#root","#applyTheme","#applyResponsivePaneStyles","#setupResizeObserver","#render","#assertAlive","#rafPending","#rafId","#destroyed","#scrollEl","#onScroll","#resizeObserver","#columnResizeCleanup","#rightPaneRefs","#handleGridClick","#lastGridClick","#scrollTop","#userSplitWidth","#leftPane","#rightPane","#computeState","#rightHeader","#leftBody"],"sources":["../src/gantt-chart/validation/schemas.ts","../src/gantt-chart/errors.ts","../src/gantt-chart/domain/tree.ts","../src/gantt-chart/domain/dependencies.ts","../src/gantt-chart/locale.ts","../src/gantt-chart/domain/dateMath.ts","../src/gantt-chart/timeline/scale.ts","../src/gantt-chart/timeline/pixelMapper.ts","../src/gantt-chart/timeline/layoutEngine.ts","../src/gantt-chart/rendering/linkRouter.ts","../src/gantt-chart/vanilla/dom/helpers.ts","../src/gantt-chart/vanilla/dom/timeHeader.ts","../src/gantt-chart/vanilla/dom/gridColumns.ts","../src/gantt-chart/vanilla/dom/leftPane.ts","../src/gantt-chart/vanilla/dom/dependencyLayer.ts","../src/gantt-chart/vanilla/interaction/drag.ts","../src/gantt-chart/vanilla/interaction/linkCreation.ts","../src/gantt-chart/vanilla/dom/rightPane.ts","../src/gantt-chart/vanilla/utils.ts","../src/gantt-chart/vanilla/splitter.ts","../src/gantt-chart/vanilla/responsive.ts","../src/gantt-chart/vanilla/gantt-chart.ts"],"sourcesContent":["import {z} from 'zod';\n\nexport const LinkTypeSchema = z.enum(['FS', 'SS', 'FF', 'SF']);\nexport const TaskTypeSchema = z.enum(['task', 'project', 'milestone']);\nexport const SpecialDayKindSchema = z.enum(['holiday', 'custom']);\n\nexport const SpecialDaySchema = z.object({\n\t/** ISO date: YYYY-MM-DD */\n\tdate: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, 'Expected YYYY-MM-DD'),\n\tkind: SpecialDayKindSchema,\n\tlabel: z.string().min(1).optional(),\n\tclassName: z.string().min(1).optional(),\n});\n\nexport const TaskSchema = z.object({\n\tid: z.number().int().positive(),\n\ttext: z.string().min(1),\n\t/** ISO date: YYYY-MM-DD */\n\tstart_date: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, 'Expected YYYY-MM-DD'),\n\t/** Duration in days; 0 = milestone */\n\tduration: z.number().int().min(0),\n\tparent: z.number().int().positive().optional(),\n\t/** 0–1 completion ratio */\n\tprogress: z.number().min(0).max(1).default(0),\n\ttype: TaskTypeSchema.default('task'),\n\t/** Initial expanded state (only relevant for parent nodes) */\n\topen: z.boolean().default(true),\n\tcolor: z.string().optional(),\n});\n\nexport const LinkSchema = z.object({\n\tid: z.number().int().positive(),\n\tsource: z.number().int().positive(),\n\ttarget: z.number().int().positive(),\n\ttype: LinkTypeSchema.default('FS'),\n});\n\nexport const GanttInputSchema = z.object({\n\ttasks: z.array(TaskSchema).min(1),\n\tlinks: z.array(LinkSchema).default([]),\n});\n\nexport type LinkType = z.infer<typeof LinkTypeSchema>;\nexport type TaskType = z.infer<typeof TaskTypeSchema>;\nexport type SpecialDayKind = z.infer<typeof SpecialDayKindSchema>;\nexport type SpecialDay = z.infer<typeof SpecialDaySchema>;\nexport type Task = z.infer<typeof TaskSchema>;\nexport type Link = z.infer<typeof LinkSchema>;\nexport type GanttInput = z.infer<typeof GanttInputSchema>;\n\n/** Parses raw external data. Throws ZodError on failure. */\nexport function parseGanttInput(raw: unknown): GanttInput {\n\treturn GanttInputSchema.parse(raw);\n}\n\n/** Returns null instead of throwing. */\nexport function safeParseGanttInput(raw: unknown): GanttInput | null {\n\tconst result = GanttInputSchema.safeParse(raw);\n\treturn result.success ? result.data : null;\n}\n","export type GanttErrorCode = 'PARENT_REFERENCE' | 'LINK_REFERENCE' | 'DEPENDENCY_CYCLE' | 'INSTANCE_DESTROYED';\n\nexport class GanttError extends Error {\n\tpublic readonly code: GanttErrorCode;\n\n\tpublic constructor(code: GanttErrorCode, message: string) {\n\t\tsuper(message);\n\t\tthis.name = 'GanttError';\n\t\tthis.code = code;\n\t}\n}\n","import {type Task} from '../validation/schemas.ts';\nimport {GanttError} from '../errors.ts';\n\nexport type TaskNode = Task & {\n\tchildren: TaskNode[];\n\t/** 0 = root */\n\tdepth: number;\n};\n\n/**\n * Builds a typed tree from a flat task array.\n * Order of tasks[] is irrelevant — parents need not precede children.\n * Throws if any parent reference is unresolvable.\n */\nexport function buildTaskTree(tasks: Task[]): TaskNode[] {\n\tconst map = new Map<number, TaskNode>();\n\tconst roots: TaskNode[] = [];\n\n\t// Pass 1: allocate nodes\n\tfor (const task of tasks) {\n\t\tmap.set(task.id, {...task, children: [], depth: 0});\n\t}\n\n\t// Pass 2: wire parent→child edges\n\tfor (const task of tasks) {\n\t\tconst node = map.get(task.id);\n\t\tif (node === undefined) {\n\t\t\tcontinue;\n\t\t} // unreachable\n\t\tif (task.parent !== undefined) {\n\t\t\tconst parent = map.get(task.parent);\n\t\t\tif (parent === undefined) {\n\t\t\t\tthrow new GanttError('PARENT_REFERENCE', `Task id=${task.id} references non-existent parent id=${task.parent}`);\n\t\t\t}\n\t\t\tparent.children.push(node);\n\t\t} else {\n\t\t\troots.push(node);\n\t\t}\n\t}\n\n\t// Pass 3: compute depths via DFS from roots\n\t(function setDepths(nodes: TaskNode[], d: number): void {\n\t\tfor (const n of nodes) {\n\t\t\tn.depth = d;\n\t\t\tsetDepths(n.children, d + 1);\n\t\t}\n\t})(roots, 0);\n\n\treturn roots;\n}\n\n/**\n * Flattens a tree into a visible row list.\n * A node's children are included only when its id is in expandedIds.\n */\nexport function flattenTree(roots: TaskNode[], expandedIds: ReadonlySet<number>): TaskNode[] {\n\tconst rows: TaskNode[] = [];\n\tfunction walk(node: TaskNode): void {\n\t\trows.push(node);\n\t\tif (node.children.length > 0 && expandedIds.has(node.id)) {\n\t\t\tfor (const child of node.children) {\n\t\t\t\twalk(child);\n\t\t\t}\n\t\t}\n\t}\n\tfor (const root of roots) {\n\t\twalk(root);\n\t}\n\treturn rows;\n}\n\n/** Returns true when a node has children in the tree. */\nexport function isParent(node: TaskNode): boolean {\n\treturn node.children.length > 0;\n}\n","import {type Link, type Task} from '../validation/schemas.ts';\nimport {GanttError} from '../errors.ts';\n\n/**\n * Detects circular dependencies in the link graph using DFS tri-colour marking.\n * Throws with a human-readable cycle path on detection.\n * Silent return = no cycles.\n */\nexport function detectCycles(tasks: Task[], links: Link[]): void {\n\t// Build adjacency list (directed: source → target)\n\tconst adj = new Map<number, number[]>();\n\tfor (const task of tasks) {\n\t\tadj.set(task.id, []);\n\t}\n\tfor (const link of links) {\n\t\tconst neighbors = adj.get(link.source);\n\t\tif (neighbors !== undefined) {\n\t\t\tneighbors.push(link.target);\n\t\t}\n\t}\n\n\tconst WHITE = 0,\n\t\tGRAY = 1,\n\t\tBLACK = 2;\n\tconst color = new Map<number, 0 | 1 | 2>();\n\tconst parent = new Map<number, number>();\n\n\tfor (const id of adj.keys()) {\n\t\tcolor.set(id, WHITE);\n\t}\n\n\tconst dfs = (u: number): void => {\n\t\tcolor.set(u, GRAY);\n\t\tfor (const v of adj.get(u) ?? []) {\n\t\t\tconst vc = color.get(v) ?? WHITE;\n\t\t\tif (vc === GRAY) {\n\t\t\t\t// Back-edge found — reconstruct cycle\n\t\t\t\tconst path: number[] = [v, u];\n\t\t\t\tlet cur = u;\n\t\t\t\twhile (cur !== v) {\n\t\t\t\t\tconst p = parent.get(cur);\n\t\t\t\t\tif (p === undefined) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tpath.push(p);\n\t\t\t\t\tcur = p;\n\t\t\t\t}\n\t\t\t\tthrow new GanttError('DEPENDENCY_CYCLE', `Circular dependency detected: ${[...path].reverse().join(' -> ')}`);\n\t\t\t}\n\t\t\tif (vc === WHITE) {\n\t\t\t\tparent.set(v, u);\n\t\t\t\tdfs(v);\n\t\t\t}\n\t\t}\n\t\tcolor.set(u, BLACK);\n\t};\n\n\tfor (const id of adj.keys()) {\n\t\tif ((color.get(id) ?? WHITE) === WHITE) {\n\t\t\tdfs(id);\n\t\t}\n\t}\n}\n\n/**\n * Returns true when all link source/target ids reference existing tasks.\n * Throws with details on failure.\n */\nexport function validateLinkRefs(tasks: Task[], links: Link[]): void {\n\tconst ids = new Set(tasks.map((t) => t.id));\n\tfor (const link of links) {\n\t\tif (!ids.has(link.source)) {\n\t\t\tthrow new GanttError('LINK_REFERENCE', `Link id=${link.id}: source=${link.source} not found`);\n\t\t}\n\t\tif (!ids.has(link.target)) {\n\t\t\tthrow new GanttError('LINK_REFERENCE', `Link id=${link.id}: target=${link.target} not found`);\n\t\t}\n\t}\n}\n","export type LocaleLabelKey =\n\t| 'aria_task'\n\t| 'aria_milestone'\n\t| 'add_subtask_title'\n\t| 'column_task_name'\n\t| 'column_start_time'\n\t| 'column_duration'\n\t| 'column_quarter';\n\nexport type ChartLocale = {\n\tcode: string;\n\tlabels?: Partial<Record<LocaleLabelKey, string>>;\n\tweekStartsOn?: 0 | 1 | 6;\n\tweekNumbering?: 'iso' | 'us' | 'simple';\n\tweekendDays?: number[];\n};\n\nexport const EN_US_LABELS: Record<LocaleLabelKey, string> = {\n\taria_task: 'Task {0}',\n\taria_milestone: 'Milestone {0}',\n\tadd_subtask_title: 'Add subtask',\n\tcolumn_task_name: 'Task name',\n\tcolumn_start_time: 'Start time',\n\tcolumn_duration: 'Duration',\n\tcolumn_quarter: 'Q',\n};\n\nexport const CHART_LOCALE_EN_US: ChartLocale = {\n\tcode: 'en-US',\n\tlabels: EN_US_LABELS,\n\tweekStartsOn: 0,\n\tweekNumbering: 'iso',\n\tweekendDays: [0, 6],\n};\n\n/**\n * Resolves a ChartLocale from either a full ChartLocale object or a BCP 47 string.\n * When given a string, derives weekStartsOn, weekNumbering, and weekendDays from CLDR conventions.\n */\nexport function resolveChartLocale(raw: ChartLocale | string | undefined): ChartLocale {\n\tif (raw === undefined) {\n\t\treturn CHART_LOCALE_EN_US;\n\t}\n\tif (typeof raw !== 'string') {\n\t\tconst locale: ChartLocale = {\n\t\t\tcode: raw.code,\n\t\t\tweekStartsOn: raw.weekStartsOn ?? deriveWeekStartsOn(raw.code),\n\t\t\tweekNumbering: raw.weekNumbering ?? deriveWeekNumbering(raw.code),\n\t\t\tweekendDays: raw.weekendDays ?? deriveWeekendDays(raw.code),\n\t\t};\n\t\tif (raw.labels !== undefined) {\n\t\t\tlocale.labels = raw.labels;\n\t\t}\n\t\treturn locale;\n\t}\n\tconst code = raw;\n\treturn {\n\t\tcode,\n\t\tweekStartsOn: deriveWeekStartsOn(code),\n\t\tweekNumbering: deriveWeekNumbering(code),\n\t\tweekendDays: deriveWeekendDays(code),\n\t};\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype IntlLocaleWithWeekInfo = Intl.Locale & {getWeekInfo?: () => {firstDay: number; weekend: number[]; minimalDays: number}};\n\nfunction tryGetWeekInfo(code: string): {firstDay: number; weekend: number[]; minimalDays: number} | undefined {\n\ttry {\n\t\tif (typeof Intl !== 'undefined' && typeof Intl.Locale === 'function') {\n\t\t\tconst locale = new Intl.Locale(code) as IntlLocaleWithWeekInfo;\n\t\t\tconst fn = locale.getWeekInfo;\n\t\t\tif (typeof fn === 'function') {\n\t\t\t\treturn fn.call(locale);\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Not available — use fallback mapping table\n\t}\n\treturn undefined;\n}\n\n/**\n * Derives the first day of week (0=Sun, 1=Mon, 6=Sat) from a BCP 47 code.\n * Uses Intl.Locale.getWeekInfo() where available (Chromium, Safari 15.4+),\n * with a CLDR-based fallback table for Firefox and older runtimes.\n */\nexport function deriveWeekStartsOn(code: string): 0 | 1 | 6 {\n\tconst primary = code.split('-')[0]?.toLowerCase() ?? 'en';\n\tconst region = code.split('-')[1]?.toUpperCase();\n\n\tif (region !== undefined) {\n\t\tconst fromRegion = WEEK_START_REGION[region];\n\t\tif (fromRegion !== undefined) {\n\t\t\treturn fromRegion;\n\t\t}\n\t}\n\tconst fromLang = WEEK_START_LANG[primary];\n\tif (fromLang !== undefined) {\n\t\treturn fromLang;\n\t}\n\n\tconst info = tryGetWeekInfo(code);\n\tif (info !== undefined) {\n\t\tconst day = info.firstDay;\n\t\treturn (day === 7 ? 0 : day) as 0 | 1 | 6;\n\t}\n\n\treturn 1;\n}\n\nconst WEEK_START_REGION: Record<string, 0 | 1 | 6> = {\n\tUS: 0,\n\tCA: 0,\n\tMX: 0,\n\tJP: 0,\n\tPH: 0,\n\tBR: 0,\n\tCO: 0,\n\tVE: 0,\n\tPE: 0,\n\tEC: 0,\n\tCL: 0,\n\tAR: 0,\n\tUY: 0,\n\tPY: 0,\n\tBO: 0,\n\tGT: 0,\n\tHN: 0,\n\tSV: 0,\n\tNI: 0,\n\tCR: 0,\n\tPA: 0,\n\tDO: 0,\n\tPR: 0,\n\tIL: 0,\n\tSA: 0,\n\tKW: 0,\n\tQA: 0,\n\tBH: 0,\n\tOM: 0,\n\tYE: 0,\n\tMA: 0,\n\tDZ: 0,\n\tTN: 0,\n\tLY: 0,\n\tEG: 0,\n\tIQ: 0,\n\tJO: 0,\n\tSD: 0,\n\tSY: 0,\n\tLB: 0,\n\tPS: 0,\n\tAE: 6,\n\tIR: 6,\n\tAF: 6,\n\tDJ: 6,\n\tSO: 6,\n};\n\nconst WEEK_START_LANG: Record<string, 0 | 1 | 6> = {\n\tar: 6,\n\tfa: 6,\n};\n\n/**\n * Derives the week numbering scheme from a BCP 47 code.\n * Europe and ISO-aligned regions default to 'iso'; Americas and others to 'us'.\n */\nexport function deriveWeekNumbering(code: string): 'iso' | 'us' | 'simple' {\n\tconst region = code.split('-')[1]?.toUpperCase();\n\tif (region !== undefined) {\n\t\tconst fromRegion = WEEK_NUMBERING_REGION[region];\n\t\tif (fromRegion !== undefined) {\n\t\t\treturn fromRegion;\n\t\t}\n\t\tif (region in WEEK_START_REGION) {\n\t\t\treturn 'us';\n\t\t}\n\t}\n\n\tconst info = tryGetWeekInfo(code);\n\tif (info !== undefined) {\n\t\tif (info.minimalDays >= 4 && info.firstDay === 1) {\n\t\t\treturn 'iso';\n\t\t}\n\t\treturn 'us';\n\t}\n\n\treturn 'iso';\n}\n\nconst WEEK_NUMBERING_REGION: Record<string, 'iso' | 'us'> = {\n\tUS: 'us',\n\tCA: 'us',\n\tMX: 'us',\n\tBR: 'us',\n\tAR: 'us',\n\tCL: 'us',\n\tCO: 'us',\n\tPE: 'us',\n\tJP: 'us',\n\tKR: 'us',\n\tCN: 'us',\n\tTW: 'us',\n\tIN: 'us',\n\tPH: 'us',\n\tIL: 'us',\n\tSA: 'us',\n\tAE: 'us',\n\tIR: 'us',\n\tZA: 'us',\n\tAU: 'us',\n\tNZ: 'us',\n};\n\n/**\n * Derives weekend days (0=Sun … 6=Sat) from a BCP 47 code.\n * Uses Intl.Locale.getWeekInfo() where available, with a CLDR-based fallback table.\n */\nexport function deriveWeekendDays(code: string): number[] {\n\tconst region = code.split('-')[1]?.toUpperCase();\n\tif (region !== undefined) {\n\t\tconst fromRegion = WEEKEND_REGION[region];\n\t\tif (fromRegion !== undefined) {\n\t\t\tconst days = [...fromRegion];\n\t\t\tdays.sort((a, b) => a - b);\n\t\t\treturn days;\n\t\t}\n\t}\n\n\tconst info = tryGetWeekInfo(code);\n\tif (info !== undefined) {\n\t\tconst days = info.weekend.map((d: number) => (d === 7 ? 0 : d));\n\t\tdays.sort((a, b) => a - b);\n\t\treturn days;\n\t}\n\n\treturn [0, 6];\n}\n\nconst WEEKEND_REGION: Record<string, number[]> = {\n\tAE: [5, 6],\n\tAF: [4, 5],\n\tDZ: [5, 6],\n\tBH: [5, 6],\n\tBD: [5, 6],\n\tEG: [5, 6],\n\tIQ: [5, 6],\n\tIL: [5, 6],\n\tJO: [5, 6],\n\tKW: [5, 6],\n\tLY: [5, 6],\n\tMV: [5, 6],\n\tMR: [5, 6],\n\tMA: [5, 6],\n\tOM: [5, 6],\n\tPK: [5, 6],\n\tPS: [5, 6],\n\tQA: [5, 6],\n\tSA: [5, 6],\n\tSD: [5, 6],\n\tSY: [5, 6],\n\tTN: [5, 6],\n\tYE: [5, 6],\n\tBN: [5, 0],\n\tIN: [0],\n\tUG: [0],\n\tNP: [6],\n\tIR: [5],\n\tDJ: [4, 5],\n\tSO: [5],\n\tMY: [5, 0],\n};\n\n/**\n * Formats a week number according to the specified scheme.\n *\n * - `'iso'`: ISO 8601 (week 1 contains the first Thursday; Monday start).\n * - `'us'`: Week 1 contains January 1; Sunday start.\n * - `'simple'`: `Math.ceil(dayOfYear / 7)`.\n */\nexport function formatWeekNumber(date: Date, scheme: 'iso' | 'us' | 'simple'): number {\n\tswitch (scheme) {\n\t\tcase 'iso': {\n\t\t\treturn isoWeek(date);\n\t\t}\n\t\tcase 'us': {\n\t\t\treturn usWeek(date);\n\t\t}\n\t\tcase 'simple': {\n\t\t\treturn simpleWeek(date);\n\t\t}\n\t}\n}\n\nfunction isoWeek(date: Date): number {\n\tconst d = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));\n\tconst dayNum = d.getUTCDay() || 7;\n\td.setUTCDate(d.getUTCDate() + 4 - dayNum);\n\tconst yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));\n\treturn Math.ceil(((d.getTime() - yearStart.getTime()) / 86_400_000 + 1) / 7);\n}\n\nfunction usWeek(date: Date): number {\n\tconst d = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));\n\tconst yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));\n\tconst dayOfYear = Math.floor((d.getTime() - yearStart.getTime()) / 86_400_000);\n\tconst jan1Dow = yearStart.getUTCDay();\n\tconst daysToFirstWeekStart = jan1Dow === 0 ? 0 : -jan1Dow;\n\tconst weekStartDayOfYear = daysToFirstWeekStart;\n\tif (dayOfYear < weekStartDayOfYear) {\n\t\treturn 0;\n\t}\n\treturn Math.floor((dayOfYear - weekStartDayOfYear) / 7) + 1;\n}\n\nfunction simpleWeek(date: Date): number {\n\tconst d = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));\n\tconst yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));\n\tconst dayOfYear = Math.floor((d.getTime() - yearStart.getTime()) / 86_400_000);\n\treturn Math.ceil((dayOfYear + 1) / 7);\n}\n\n/**\n * Formats a label template by replacing `{0}` with the given argument.\n */\nexport function formatLabel(template: string, arg: string): string {\n\treturn template.replaceAll('{0}', arg);\n}\n","import {type TimeScale} from '../timeline/scale.ts';\nimport {type ChartLocale, EN_US_LABELS, formatWeekNumber} from '../locale.ts';\n\n/** Parses YYYY-MM-DD → UTC midnight Date. Throws on invalid input. */\nexport function parseDate(dateStr: string): Date {\n\tconst d = new Date(`${dateStr}T00:00:00.000Z`);\n\tif (isNaN(d.getTime())) {\n\t\tthrow new Error(`Invalid date: \"${dateStr}\"`);\n\t}\n\treturn d;\n}\n\n/** Returns date + n days (exact ms arithmetic). */\nexport function addDays(date: Date, days: number): Date {\n\treturn new Date(date.getTime() + days * 86_400_000);\n}\n\n/** Difference in days (float). Positive when b > a. */\nexport function diffDays(a: Date, b: Date): number {\n\treturn (b.getTime() - a.getTime()) / 86_400_000;\n}\n\n/** UTC start-of-day. */\nexport function startOfDay(date: Date): Date {\n\treturn new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));\n}\n\n/**\n * UTC start-of-week. Respects locale's weekStartsOn (0=Sun, 1=Mon, 6=Sat).\n * Defaults to Monday when locale is omitted.\n */\nexport function startOfWeek(date: Date, weekStartsOn: 0 | 1 | 6 = 1): Date {\n\tconst d = startOfDay(date);\n\tconst dow = d.getUTCDay();\n\tconst offset = (((dow - weekStartsOn) % 7) + 7) % 7;\n\treturn addDays(d, -offset);\n}\n\n/** UTC start-of-month. */\nexport function startOfMonth(date: Date): Date {\n\treturn new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1));\n}\n\n/** UTC start-of-quarter. */\nexport function startOfQuarter(date: Date): Date {\n\tconst month = date.getUTCMonth();\n\tconst quarterStartMonth = Math.floor(month / 3) * 3;\n\treturn new Date(Date.UTC(date.getUTCFullYear(), quarterStartMonth, 1));\n}\n\n/** UTC start-of-year. */\nexport function startOfYear(date: Date): Date {\n\treturn new Date(Date.UTC(date.getUTCFullYear(), 0, 1));\n}\n\n/** UTC start-of-hour. */\nexport function startOfHour(date: Date): Date {\n\treturn new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours()));\n}\n\n/**\n * Formats a Date for the time-header label given the active scale.\n */\nexport function formatHeaderLabel(date: Date, scale: TimeScale, locale: ChartLocale): string {\n\tconst {code, weekNumbering: weekNumScheme = 'iso'} = locale;\n\tswitch (scale) {\n\t\tcase 'hour': {\n\t\t\treturn `${String(date.getUTCHours()).padStart(2, '0')}:00`;\n\t\t}\n\t\tcase 'day': {\n\t\t\tconst day = date.toLocaleDateString(code, {weekday: 'short', timeZone: 'UTC'});\n\t\t\treturn `${date.getUTCDate()} ${day}`;\n\t\t}\n\t\tcase 'week': {\n\t\t\tconst wn = formatWeekNumber(date, weekNumScheme);\n\t\t\treturn `W${wn}`;\n\t\t}\n\t\tcase 'month': {\n\t\t\treturn date.toLocaleDateString(code, {month: 'short', year: 'numeric', timeZone: 'UTC'});\n\t\t}\n\t\tcase 'quarter': {\n\t\t\treturn `${resolveQuarterLabel(locale)}${Math.floor(date.getUTCMonth() / 3) + 1} ${date.getUTCFullYear()}`;\n\t\t}\n\t\tcase 'year': {\n\t\t\treturn `${date.getUTCFullYear()}`;\n\t\t}\n\t}\n}\n\n/**\n * Returns the upper-level (month/year) label for a given scale column.\n * Used in the top header row.\n */\nexport function formatUpperLabel(date: Date, scale: TimeScale, locale: ChartLocale): string {\n\tconst {code} = locale;\n\tswitch (scale) {\n\t\tcase 'hour': {\n\t\t\treturn date.toLocaleDateString(code, {month: 'long', day: 'numeric', year: 'numeric', timeZone: 'UTC'});\n\t\t}\n\t\tcase 'day':\n\t\tcase 'week': {\n\t\t\treturn date.toLocaleDateString(code, {month: 'long', year: 'numeric', timeZone: 'UTC'});\n\t\t}\n\t\tcase 'month': {\n\t\t\treturn `${date.getUTCFullYear()}`;\n\t\t}\n\t\tcase 'quarter': {\n\t\t\treturn `${date.getUTCFullYear()}`;\n\t\t}\n\t\tcase 'year': {\n\t\t\treturn `${date.getUTCFullYear()}`;\n\t\t}\n\t}\n}\n\nfunction resolveQuarterLabel(locale: ChartLocale): string {\n\tif (locale.labels?.column_quarter !== undefined) {\n\t\treturn locale.labels.column_quarter;\n\t}\n\treturn EN_US_LABELS.column_quarter;\n}\n\n/** Formats YYYY-MM-DD for display in the grid. */\nexport function formatDisplayDate(dateStr: string, locale: ChartLocale): string {\n\tconst d = parseDate(dateStr);\n\treturn d.toLocaleDateString(locale.code, {year: 'numeric', month: '2-digit', day: '2-digit', timeZone: 'UTC'});\n}\n","export type TimeScale = 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year';\n\nexport type ScaleConfig = {\n\t/** Pixel width of one column unit */\n\tcolumnWidth: number;\n\t/** Milliseconds per column unit */\n\tmsPerColumn: number;\n\theaderFormat: TimeScale;\n};\n\nconst H = 3_600_000;\nconst D = 86_400_000;\n\nexport const SCALE_CONFIGS: Record<TimeScale, ScaleConfig> = {\n\thour: {columnWidth: 60, msPerColumn: H, headerFormat: 'hour'},\n\tday: {columnWidth: 72, msPerColumn: D, headerFormat: 'day'},\n\tweek: {columnWidth: 120, msPerColumn: 7 * D, headerFormat: 'week'},\n\tmonth: {columnWidth: 160, msPerColumn: 30 * D, headerFormat: 'month'},\n\tquarter: {columnWidth: 220, msPerColumn: 91 * D, headerFormat: 'quarter'},\n\tyear: {columnWidth: 280, msPerColumn: 365 * D, headerFormat: 'year'},\n};\n\n/**\n * Snaps a date to the column boundary for the provided scale.\n * All operations use UTC semantics.\n * The week boundary respects the optional weekStartsOn override (0=Sun, 1=Mon, 6=Sat).\n * Defaults to Monday (1) when omitted.\n */\nexport function snapToScaleBoundary(date: Date, scale: TimeScale, weekStartsOn: 0 | 1 | 6 = 1): Date {\n\tswitch (scale) {\n\t\tcase 'hour': {\n\t\t\treturn new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours()));\n\t\t}\n\t\tcase 'day': {\n\t\t\treturn new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));\n\t\t}\n\t\tcase 'week': {\n\t\t\tconst d = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));\n\t\t\tconst dow = d.getUTCDay();\n\t\t\tconst offset = (((dow - weekStartsOn) % 7) + 7) % 7;\n\t\t\td.setUTCDate(d.getUTCDate() - offset);\n\t\t\treturn d;\n\t\t}\n\t\tcase 'month': {\n\t\t\treturn new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1));\n\t\t}\n\t\tcase 'quarter': {\n\t\t\tconst month = date.getUTCMonth();\n\t\t\tconst quarterStartMonth = Math.floor(month / 3) * 3;\n\t\t\treturn new Date(Date.UTC(date.getUTCFullYear(), quarterStartMonth, 1));\n\t\t}\n\t\tcase 'year': {\n\t\t\treturn new Date(Date.UTC(date.getUTCFullYear(), 0, 1));\n\t\t}\n\t}\n}\n\n/**\n * Returns the next column boundary from a boundary-aligned date.\n * Month/quarter/year use true calendar stepping (not fixed-day approximations).\n */\nexport function nextScaleBoundary(date: Date, scale: TimeScale): Date {\n\tswitch (scale) {\n\t\tcase 'hour': {\n\t\t\treturn new Date(date.getTime() + H);\n\t\t}\n\t\tcase 'day': {\n\t\t\treturn new Date(date.getTime() + D);\n\t\t}\n\t\tcase 'week': {\n\t\t\treturn new Date(date.getTime() + 7 * D);\n\t\t}\n\t\tcase 'month': {\n\t\t\treturn new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 1));\n\t\t}\n\t\tcase 'quarter': {\n\t\t\treturn new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 3, 1));\n\t\t}\n\t\tcase 'year': {\n\t\t\treturn new Date(Date.UTC(date.getUTCFullYear() + 1, 0, 1));\n\t\t}\n\t}\n}\n","import {SCALE_CONFIGS} from './scale.ts';\nimport {type TimeScale} from './scale.ts';\n\nexport type PixelMapper = {\n\t/** Date → x pixel offset from viewport start */\n\ttoX: (date: Date) => number;\n\t/** x pixel offset → Date */\n\ttoDate: (x: number) => Date;\n\t/** Days → pixel width */\n\tdurationToWidth: (days: number) => number;\n\t/** Pixel width → days (float) */\n\twidthToDuration: (px: number) => number;\n\t/** The origin timestamp used for this mapper */\n\toriginMs: number;\n\t/** Pixel width of one column unit */\n\tcolumnWidth: number;\n};\n\n/**\n * Creates a stateless pixel mapper for the given scale and viewport start.\n * All conversions are O(1) arithmetic — safe to call in tight loops.\n */\nexport function createPixelMapper(scale: TimeScale, viewportStart: Date): PixelMapper {\n\tconst {columnWidth, msPerColumn} = SCALE_CONFIGS[scale];\n\tconst originMs = viewportStart.getTime();\n\tconst pxPerMs = columnWidth / msPerColumn;\n\tconst msPerPx = msPerColumn / columnWidth;\n\tconst msPerDay = 86_400_000;\n\n\treturn {\n\t\toriginMs,\n\t\tcolumnWidth,\n\t\ttoX(date: Date): number {\n\t\t\treturn (date.getTime() - originMs) * pxPerMs;\n\t\t},\n\t\ttoDate(x: number): Date {\n\t\t\treturn new Date(originMs + x * msPerPx);\n\t\t},\n\t\tdurationToWidth(days: number): number {\n\t\t\treturn days * msPerDay * pxPerMs;\n\t\t},\n\t\twidthToDuration(px: number): number {\n\t\t\treturn (px * msPerPx) / msPerDay;\n\t\t},\n\t};\n}\n","import {type TaskNode} from '../domain/tree.ts';\nimport {type PixelMapper} from './pixelMapper.ts';\nimport {parseDate, addDays} from '../domain/dateMath.ts';\n\nexport const DENSITY = {\n\trowHeight: 44,\n\tbarHeight: 28,\n\tmilestoneSize: 20,\n} as const;\n\nexport const ROW_HEIGHT = DENSITY.rowHeight;\nexport const BAR_HEIGHT = DENSITY.barHeight;\nexport const BAR_Y_OFFSET = (ROW_HEIGHT - BAR_HEIGHT) / 2;\nexport const MILESTONE_SIZE = DENSITY.milestoneSize;\n/** Half-width of a milestone diamond */\nexport const MILESTONE_HALF = MILESTONE_SIZE / 2;\n\nexport type BarLayout = {\n\ttaskId: number;\n\t/** Left edge x in timeline coordinates */\n\tx: number;\n\t/** Top edge y in content coordinates */\n\ty: number;\n\twidth: number;\n\theight: number;\n\tprogressWidth: number;\n\ttype: 'task' | 'project' | 'milestone';\n\trowIndex: number;\n\t/** Center x; identical to x + width/2 or x for milestones */\n\tcenterX: number;\n\tcenterY: number;\n};\n\n/**\n * Computes pixel-space layout for all visible task rows.\n * Returns a map keyed by task id for O(1) lookup during link routing.\n */\nexport function computeLayout(rows: TaskNode[], mapper: PixelMapper): Map<number, BarLayout> {\n\tconst result = new Map<number, BarLayout>();\n\n\tfor (let i = 0; i < rows.length; i++) {\n\t\tconst task = rows[i];\n\t\tif (task === undefined) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst start = parseDate(task.start_date);\n\t\tconst x = mapper.toX(start);\n\t\tconst y = i * ROW_HEIGHT + BAR_Y_OFFSET;\n\t\tconst centerY = i * ROW_HEIGHT + ROW_HEIGHT / 2;\n\n\t\tconst type = task.type ?? 'task';\n\n\t\tif (type === 'milestone') {\n\t\t\tresult.set(task.id, {\n\t\t\t\ttaskId: task.id,\n\t\t\t\tx,\n\t\t\t\ty,\n\t\t\t\twidth: 0,\n\t\t\t\theight: BAR_HEIGHT,\n\t\t\t\tprogressWidth: 0,\n\t\t\t\ttype: 'milestone',\n\t\t\t\trowIndex: i,\n\t\t\t\tcenterX: x,\n\t\t\t\tcenterY,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst width = Math.max(mapper.durationToWidth(task.duration), 4);\n\t\tconst progressWidth = width * Math.min(1, Math.max(0, task.progress ?? 0));\n\n\t\tresult.set(task.id, {\n\t\t\ttaskId: task.id,\n\t\t\tx,\n\t\t\ty,\n\t\t\twidth,\n\t\t\theight: BAR_HEIGHT,\n\t\t\tprogressWidth,\n\t\t\ttype,\n\t\t\trowIndex: i,\n\t\t\tcenterX: x + width / 2,\n\t\t\tcenterY,\n\t\t});\n\t}\n\n\treturn result;\n}\n\n/**\n * Computes the total pixel height of all rows.\n */\nexport function totalContentHeight(rowCount: number): number {\n\treturn rowCount * ROW_HEIGHT;\n}\n\n/**\n * Derives viewport bounds from task data with padding.\n * Returns [start, end] as UTC midnight dates.\n */\nexport function deriveViewport(tasks: TaskNode[], paddingDays = 2): [Date, Date] {\n\tif (tasks.length === 0) {\n\t\tconst now = new Date();\n\t\treturn [now, addDays(now, 30)];\n\t}\n\n\tlet minMs = Infinity;\n\tlet maxMs = -Infinity;\n\n\tfor (const task of tasks) {\n\t\tconst start = parseDate(task.start_date);\n\t\tconst end = addDays(start, task.duration);\n\t\tif (start.getTime() < minMs) {\n\t\t\tminMs = start.getTime();\n\t\t}\n\t\tif (end.getTime() > maxMs) {\n\t\t\tmaxMs = end.getTime();\n\t\t}\n\t}\n\n\treturn [addDays(new Date(minMs), -paddingDays), addDays(new Date(maxMs), paddingDays)];\n}\n","import {type Link} from '../validation/schemas.ts';\nimport {type BarLayout} from '../timeline/layoutEngine.ts';\nimport {MILESTONE_HALF} from '../timeline/layoutEngine.ts';\n\nexport type Point = {x: number; y: number};\n\nexport type RoutedLink = {\n\tlinkId: number;\n\tsourceTaskId: number;\n\ttargetTaskId: number;\n\t/** Ordered vertices of the orthogonal polyline (source → target). */\n\tpoints: Point[];\n};\n\nconst TURN_MARGIN = 12; // px gap before/after bar for routing clearance\n\n/**\n * Computes orthogonal routing for all dependency links.\n * Links whose source or target is not in the layout map are skipped silently\n * (e.g. when the row is collapsed).\n */\nexport function routeLinks(links: Link[], layouts: Map<number, BarLayout>): RoutedLink[] {\n\treturn links\n\t\t.map((link) => {\n\t\t\tconst src = layouts.get(link.source);\n\t\t\tconst tgt = layouts.get(link.target);\n\t\t\tif (src === undefined || tgt === undefined) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tlinkId: link.id,\n\t\t\t\tsourceTaskId: link.source,\n\t\t\t\ttargetTaskId: link.target,\n\t\t\t\tpoints: route(link.type, src, tgt),\n\t\t\t};\n\t\t})\n\t\t.filter((r): r is RoutedLink => r !== null);\n}\n\n/**\n * Produces the vertex list for an orthogonal connector between src and tgt.\n *\n * Link semantics:\n * FS = source.finish → target.start (most common)\n * SS = source.start → target.start\n * FF = source.finish → target.finish\n * SF = source.start → target.finish\n */\nfunction route(type: Link['type'], src: BarLayout, tgt: BarLayout): Point[] {\n\tlet sx: number, tx: number;\n\tconst sy = src.centerY;\n\tconst ty = tgt.centerY;\n\n\tswitch (type) {\n\t\tcase 'FS': {\n\t\t\tsx = src.type === 'milestone' ? src.x + MILESTONE_HALF : src.x + src.width;\n\t\t\ttx = tgt.type === 'milestone' ? tgt.x - MILESTONE_HALF : tgt.x;\n\t\t\tbreak;\n\t\t}\n\t\tcase 'SS': {\n\t\t\tsx = src.type === 'milestone' ? src.x - MILESTONE_HALF : src.x;\n\t\t\ttx = tgt.type === 'milestone' ? tgt.x - MILESTONE_HALF : tgt.x;\n\t\t\tbreak;\n\t\t}\n\t\tcase 'FF': {\n\t\t\tsx = src.type === 'milestone' ? src.x + MILESTONE_HALF : src.x + src.width;\n\t\t\ttx = tgt.type === 'milestone' ? tgt.x + MILESTONE_HALF : tgt.x + tgt.width;\n\t\t\tbreak;\n\t\t}\n\t\tcase 'SF': {\n\t\t\tsx = src.type === 'milestone' ? src.x - MILESTONE_HALF : src.x;\n\t\t\ttx = tgt.type === 'milestone' ? tgt.x + MILESTONE_HALF : tgt.x + tgt.width;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Same row: direct horizontal\n\tif (Math.abs(sy - ty) < 1) {\n\t\treturn [\n\t\t\t{x: sx, y: sy},\n\t\t\t{x: tx, y: ty},\n\t\t];\n\t}\n\n\t// Forward path: midpoint between sx and tx\n\tif (sx <= tx) {\n\t\tconst midX = sx + Math.max(TURN_MARGIN, (tx - sx) / 2);\n\t\treturn [\n\t\t\t{x: sx, y: sy},\n\t\t\t{x: midX, y: sy},\n\t\t\t{x: midX, y: ty},\n\t\t\t{x: tx, y: ty},\n\t\t];\n\t}\n\n\t// Backward path: loop around via margins\n\tconst loopX = tx - TURN_MARGIN;\n\treturn [\n\t\t{x: sx, y: sy},\n\t\t{x: sx + TURN_MARGIN, y: sy},\n\t\t{x: sx + TURN_MARGIN, y: (sy + ty) / 2},\n\t\t{x: loopX, y: (sy + ty) / 2},\n\t\t{x: loopX, y: ty},\n\t\t{x: tx, y: ty},\n\t];\n}\n","/**\n * Typed element factory. Avoids littering `as HTMLElement` casts everywhere.\n *\n * @example\n * const div = el('div', {className: 'gantt-bar'});\n * const svg = el('svg', {}, 'http://www.w3.org/2000/svg');\n */\nexport function el<K extends keyof HTMLElementTagNameMap>(tag: K, props?: Partial<HTMLElementTagNameMap[K]>, ns?: never): HTMLElementTagNameMap[K];\nexport function el(tag: string, props?: Record<string, unknown>, ns?: string): Element;\nexport function el(tag: string, props?: Record<string, unknown>, ns?: string): Element {\n\tconst elem = ns ? document.createElementNS(ns, tag) : document.createElement(tag);\n\tif (props !== undefined) {\n\t\tfor (const [k, v] of Object.entries(props)) {\n\t\t\tif (k === 'style' && typeof v === 'object' && v !== null) {\n\t\t\t\tcss(elem as HTMLElement, v as Partial<CSSStyleDeclaration>);\n\t\t\t} else if (k in elem) {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\t\t(elem as any)[k] = v;\n\t\t\t} else {\n\t\t\t\telem.setAttribute(k, String(v));\n\t\t\t}\n\t\t}\n\t}\n\treturn elem;\n}\n\n/** Batches style assignments; avoids repeated style recalculations. */\nexport function css(elem: HTMLElement, styles: Partial<CSSStyleDeclaration>): void {\n\tfor (const [k, v] of Object.entries(styles)) {\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t(elem.style as any)[k] = v ?? '';\n\t}\n}\n\n/** Removes all child nodes from elem. Faster than innerHTML = '' for large subtrees. */\nexport function clearChildren(elem: Element): void {\n\twhile (elem.firstChild !== null) {\n\t\telem.removeChild(elem.firstChild);\n\t}\n}\n\n/** Appends all nodes from an array/fragment into parent in one pass. */\nexport function appendAll(parent: Element, children: (Element | Text)[]): void {\n\tconst frag = document.createDocumentFragment();\n\tfor (const c of children) {\n\t\tfrag.appendChild(c);\n\t}\n\tparent.append(frag);\n}\n\n/** Creates an SVG element in the SVG namespace. */\nexport function svgEl<K extends keyof SVGElementTagNameMap>(tag: K, attrs?: Record<string, string | number>): SVGElementTagNameMap[K] {\n\tconst NS = 'http://www.w3.org/2000/svg';\n\tconst elem = document.createElementNS(NS, tag);\n\tif (attrs !== undefined) {\n\t\tfor (const [k, v] of Object.entries(attrs)) {\n\t\t\telem.setAttribute(k, String(v));\n\t\t}\n\t}\n\treturn elem;\n}\n\n/** Sets multiple SVG attributes in one call. */\nexport function setAttrs(elem: Element, attrs: Record<string, string | number>): void {\n\tfor (const [k, v] of Object.entries(attrs)) {\n\t\telem.setAttribute(k, String(v));\n\t}\n}\n","import {el, clearChildren, appendAll} from './helpers.ts';\nimport {type GanttState} from '../state.ts';\nimport {nextScaleBoundary, snapToScaleBoundary} from '../../timeline/scale.ts';\nimport {formatHeaderLabel, formatUpperLabel} from '../../domain/dateMath.ts';\n\ntype Cell = {label: string; x: number; width: number};\n\n/**\n * Fully replaces the content of `container` with two header rows.\n * Called on scale change or viewport change only — not on scroll.\n */\nexport function renderTimeHeader(container: HTMLElement, state: GanttState): void {\n\tconst {scale, viewportStart, viewportEnd, mapper, totalWidth, locale} = state;\n\tconst weekStartsOn = locale.weekStartsOn ?? 1;\n\n\tconst upperCells: Cell[] = [];\n\tconst lowerCells: Cell[] = [];\n\n\tlet cur = snapToScaleBoundary(viewportStart, scale, weekStartsOn);\n\tlet prevUpperLabel = '';\n\tlet upperStart = 0;\n\tlet upperWidth = 0;\n\n\twhile (cur < viewportEnd) {\n\t\tconst next = nextScaleBoundary(cur, scale);\n\t\tconst x = mapper.toX(cur);\n\t\tconst w = mapper.toX(next) - x;\n\t\tlowerCells.push({label: formatHeaderLabel(cur, scale, locale), x, width: w});\n\n\t\tconst uLabel = formatUpperLabel(cur, scale, locale);\n\t\tif (uLabel !== prevUpperLabel) {\n\t\t\tif (prevUpperLabel !== '') {\n\t\t\t\tupperCells.push({label: prevUpperLabel, x: upperStart, width: upperWidth});\n\t\t\t}\n\t\t\tprevUpperLabel = uLabel;\n\t\t\tupperStart = x;\n\t\t\tupperWidth = w;\n\t\t} else {\n\t\t\tupperWidth += w;\n\t\t}\n\t\tcur = next;\n\t}\n\tif (prevUpperLabel !== '') {\n\t\tupperCells.push({label: prevUpperLabel, x: upperStart, width: upperWidth});\n\t}\n\n\t// ── Upper row ──────────────────────────────────────────────────────────\n\tconst upperRow = el('div');\n\tcss_(upperRow, {\n\t\tposition: 'relative',\n\t\theight: '24px',\n\t\twidth: `${totalWidth}px`,\n\t\tbackground: 'var(--gantt-header-bg)',\n\t\tborderBottom: '1px solid var(--gantt-border)',\n\t});\n\n\tconst upperNodes = upperCells.map((cell) => {\n\t\tconst d = el('div');\n\t\tcss_(d, {\n\t\t\tposition: 'absolute',\n\t\t\tleft: `${cell.x}px`,\n\t\t\twidth: `${cell.width}px`,\n\t\t\theight: '100%',\n\t\t\tborderRight: '1px solid var(--gantt-border)',\n\t\t\tdisplay: 'flex',\n\t\t\talignItems: 'center',\n\t\t\tpaddingLeft: '8px',\n\t\t\tfontSize: 'var(--gantt-font-size-xs)',\n\t\t\tfontWeight: 'var(--gantt-font-weight-bold)',\n\t\t\tcolor: 'var(--gantt-text)',\n\t\t\toverflow: 'hidden',\n\t\t\twhiteSpace: 'nowrap',\n\t\t\tletterSpacing: 'var(--gantt-letter-spacing-tight)',\n\t\t\ttextTransform: 'uppercase',\n\t\t});\n\t\td.textContent = cell.label;\n\t\treturn d;\n\t});\n\n\t// ── Lower row ──────────────────────────────────────────────────────────\n\tconst lowerRow = el('div');\n\tcss_(lowerRow, {\n\t\tposition: 'relative',\n\t\theight: '28px',\n\t\twidth: `${totalWidth}px`,\n\t\tbackground: 'var(--gantt-header-bg)',\n\t\tborderBottom: '1px solid var(--gantt-border)',\n\t});\n\n\tconst lowerNodes = lowerCells.map((cell) => {\n\t\tconst d = el('div');\n\t\tcss_(d, {\n\t\t\tposition: 'absolute',\n\t\t\tleft: `${cell.x}px`,\n\t\t\twidth: `${cell.width}px`,\n\t\t\theight: '100%',\n\t\t\tborderRight: '1px solid var(--gantt-border)',\n\t\t\tdisplay: 'flex',\n\t\t\talignItems: 'center',\n\t\t\tjustifyContent: 'center',\n\t\t\tfontSize: 'var(--gantt-font-size-xs)',\n\t\t\tcolor: 'var(--gantt-text-secondary)',\n\t\t\toverflow: 'hidden',\n\t\t\twhiteSpace: 'nowrap',\n\t\t});\n\t\td.textContent = cell.label;\n\t\treturn d;\n\t});\n\n\tappendAll(upperRow, upperNodes);\n\tappendAll(lowerRow, lowerNodes);\n\n\tclearChildren(container);\n\tcontainer.append(upperRow);\n\tcontainer.append(lowerRow);\n}\n\n/** Inline style helper local to this module. */\nfunction css_(elem: HTMLElement, styles: Partial<CSSStyleDeclaration>): void {\n\tfor (const [k, v] of Object.entries(styles)) {\n\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t(elem.style as any)[k] = v ?? '';\n\t}\n}\n","import {formatDisplayDate} from '../../domain/dateMath.ts';\nimport {type ChartLocale, EN_US_LABELS} from '../../locale.ts';\nimport {type Task} from '../../validation/schemas.ts';\nimport {type TaskNode} from '../../domain/tree.ts';\n\nexport type GridColumn = {\n\tid: string;\n\theader: string;\n\twidth: string;\n\talign?: 'left' | 'center' | 'right';\n\tvisible?: boolean;\n\tfield?: keyof Task;\n\tformat?: (value: unknown, task: Task, row: TaskNode, locale: ChartLocale) => string;\n};\n\nexport const DEFAULT_GRID_COLUMNS: GridColumn[] = [\n\t{\n\t\tid: 'name',\n\t\theader: 'Task name',\n\t\twidth: '1fr',\n\t},\n\t{\n\t\tid: 'start_date',\n\t\theader: 'Start time',\n\t\twidth: '90px',\n\t\tfield: 'start_date',\n\t\tformat: (value, _task, _row, locale) => formatDisplayDate(String(value), locale),\n\t},\n\t{\n\t\tid: 'duration',\n\t\theader: 'Duration',\n\t\twidth: '68px',\n\t\tfield: 'duration',\n\t\tformat: (value) => ((value as number) > 0 ? String(value) : '—'),\n\t},\n\t{\n\t\tid: 'actions',\n\t\theader: '',\n\t\twidth: '28px',\n\t},\n];\n\n/**\n * Returns a localized default grid column schema.\n * Column headers use locale label overrides with EN_US_LABELS fallback.\n */\nexport function gridColumnDefaults(locale: ChartLocale): GridColumn[] {\n\treturn [\n\t\t{\n\t\t\tid: 'name',\n\t\t\theader: locale.labels?.column_task_name ?? EN_US_LABELS.column_task_name,\n\t\t\twidth: '1fr',\n\t\t},\n\t\t{\n\t\t\tid: 'start_date',\n\t\t\theader: locale.labels?.column_start_time ?? EN_US_LABELS.column_start_time,\n\t\t\twidth: '90px',\n\t\t\tfield: 'start_date',\n\t\t\tformat: (value, _task, _row, loc) => formatDisplayDate(String(value), loc),\n\t\t},\n\t\t{\n\t\t\tid: 'duration',\n\t\t\theader: locale.labels?.column_duration ?? EN_US_LABELS.column_duration,\n\t\t\twidth: '68px',\n\t\t\tfield: 'duration',\n\t\t\tformat: (value) => ((value as number) > 0 ? String(value) : '—'),\n\t\t},\n\t\t{\n\t\t\tid: 'actions',\n\t\t\theader: '',\n\t\t\twidth: '28px',\n\t\t},\n\t];\n}\n\nexport function gridTemplateColumns(columns: GridColumn[]): string {\n\treturn columns\n\t\t.filter((c) => c.visible !== false)\n\t\t.map((c) => c.width)\n\t\t.join(' ');\n}\n\nexport function visibleColumns(columns: GridColumn[]): GridColumn[] {\n\treturn columns.filter((c) => c.visible !== false);\n}\n\nexport const GRID_COLUMN_FR_MIN_WIDTH = 120;\n\nconst PX_RE = /^(\\d+(?:\\.\\d+)?)px$/;\nconst FR_RE = /^(\\d+(?:\\.\\d+)?)fr$/;\n\nfunction parseColumnMinWidth(width: string): number {\n\tconst trimmed = width.trim();\n\tconst pxMatch = PX_RE.exec(trimmed);\n\tif (pxMatch) {\n\t\treturn parseFloat(pxMatch[1] ?? '0');\n\t}\n\tconst frMatch = FR_RE.exec(trimmed);\n\tif (frMatch) {\n\t\treturn parseFloat(frMatch[1] ?? '0') * GRID_COLUMN_FR_MIN_WIDTH;\n\t}\n\treturn 0;\n}\n\nexport function gridNaturalWidth(columns: GridColumn[]): number {\n\tlet total = 0;\n\tfor (const col of visibleColumns(columns)) {\n\t\ttotal += parseColumnMinWidth(col.width);\n\t}\n\treturn total;\n}\n","import {el, clearChildren, css} from './helpers.ts';\nimport {type GanttState} from '../state.ts';\nimport {type TaskNode} from '../../domain/tree.ts';\nimport {type Task} from '../../validation/schemas.ts';\nimport {isParent} from '../../domain/tree.ts';\nimport {ROW_HEIGHT} from '../../timeline/layoutEngine.ts';\nimport {type GridColumn, gridTemplateColumns, visibleColumns} from './gridColumns.ts';\nimport {type ChartLocale, EN_US_LABELS} from '../../locale.ts';\n\nconst INDENT = 16;\nconst COLUMN_MIN_WIDTH = 30;\n\nexport type LeftPaneCallbacks = {\n\tonToggle: (id: number) => void;\n\tonSelect: (id: number) => void;\n\tonRowClick: (payload: {id: number; task: Task}) => void;\n\tonTaskEditIntent: (payload: {id: number; source: 'grid'; trigger: 'double_click'; task: Task}) => void;\n\tonAdd: (id: number) => void;\n};\n\n/** Renders the left grid pane. */\nexport function renderLeftPane(container: HTMLElement, state: GanttState, cbs: LeftPaneCallbacks, columns: GridColumn[]): void {\n\tconst {allRows, selectedId, expandedIds, startIndex, endIndex, paddingTop, paddingBottom, locale} = state;\n\n\tconst frag = document.createDocumentFragment();\n\n\tif (paddingTop > 0) {\n\t\tconst spacer = el('div');\n\t\tspacer.style.height = `${paddingTop}px`;\n\t\tfrag.append(spacer);\n\t}\n\n\tfor (const row of allRows.slice(startIndex, endIndex + 1)) {\n\t\tfrag.append(buildRow(row, selectedId, expandedIds, cbs, columns, locale));\n\t}\n\n\tif (paddingBottom > 0) {\n\t\tconst spacer = el('div');\n\t\tspacer.style.height = `${paddingBottom}px`;\n\t\tfrag.append(spacer);\n\t}\n\n\tclearChildren(container);\n\tcontainer.append(frag);\n}\n\nfunction buildRow(\n\trow: TaskNode,\n\tselectedId: number | null,\n\texpandedIds: Set<number>,\n\tcbs: LeftPaneCallbacks,\n\tcolumns: GridColumn[],\n\tlocale: ChartLocale,\n): HTMLElement {\n\tconst selected = row.id === selectedId;\n\n\tconst wrapper = el('div');\n\twrapper.className = 'gantt-row';\n\tcss(wrapper, {\n\t\tdisplay: 'grid',\n\t\tgridTemplateColumns: gridTemplateColumns(columns),\n\t\theight: `${ROW_HEIGHT}px`,\n\t\talignItems: 'center',\n\t\tpaddingLeft: '8px',\n\t\tbackground: selected ? 'var(--gantt-row-selected)' : 'var(--gantt-bg)',\n\t\tborderBottom: '1px solid var(--gantt-border)',\n\t\tcursor: 'default',\n\t\tboxSizing: 'border-box',\n\t});\n\twrapper.tabIndex = 0;\n\twrapper.setAttribute('role', 'row');\n\twrapper.setAttribute('aria-selected', String(selected));\n\twrapper.dataset['taskId'] = String(row.id);\n\twrapper.addEventListener('click', () => {\n\t\tconst task = toTask(row);\n\t\tcbs.onRowClick({id: row.id, task});\n\t});\n\twrapper.addEventListener('keydown', (event) => {\n\t\tif (event.key === 'Enter' || event.key === ' ') {\n\t\t\tevent.preventDefault();\n\t\t\tcbs.onSelect(row.id);\n\t\t}\n\t});\n\n\tfor (const column of visibleColumns(columns)) {\n\t\twrapper.append(buildCell(column, row, expandedIds, cbs, locale));\n\t}\n\n\treturn wrapper;\n}\n\nfunction buildCell(column: GridColumn, row: TaskNode, expandedIds: Set<number>, cbs: LeftPaneCallbacks, locale: ChartLocale): HTMLElement {\n\tswitch (column.id) {\n\t\tcase 'name': {\n\t\t\treturn buildTreeNameCell(row, expandedIds, cbs);\n\t\t}\n\t\tcase 'actions': {\n\t\t\treturn buildAddButton(row, cbs, locale);\n\t\t}\n\t\tdefault: {\n\t\t\treturn buildDataCell(row, column, locale);\n\t\t}\n\t}\n}\n\nfunction buildTreeNameCell(row: TaskNode, expandedIds: Set<number>, cbs: LeftPaneCallbacks): HTMLElement {\n\tconst hasChildren = isParent(row);\n\tconst expanded = expandedIds.has(row.id);\n\n\tconst cell = el('div');\n\tcss(cell, {\n\t\tdisplay: 'flex',\n\t\talignItems: 'center',\n\t\tpaddingLeft: `${row.depth * INDENT}px`,\n\t\tgap: '4px',\n\t\toverflow: 'hidden',\n\t});\n\n\tif (hasChildren) {\n\t\tconst btn = el('button');\n\t\tbtn.className = 'gantt-toggle';\n\t\tbtn.textContent = expanded ? '▾' : '▸';\n\t\tcss(btn, {\n\t\t\twidth: '16px',\n\t\t\theight: '16px',\n\t\t\tdisplay: 'flex',\n\t\t\talignItems: 'center',\n\t\t\tjustifyContent: 'center',\n\t\t\tbackground: 'none',\n\t\t\tborder: 'none',\n\t\t\tcursor: 'pointer',\n\t\t\tcolor: 'var(--gantt-text-secondary)',\n\t\t\tpadding: '0',\n\t\t\tflexShrink: '0',\n\t\t});\n\t\tbtn.addEventListener('click', (e) => {\n\t\t\te.stopPropagation();\n\t\t\tcbs.onToggle(row.id);\n\t\t});\n\t\tcell.append(btn);\n\t} else {\n\t\tconst spacer = el('span');\n\t\tspacer.style.width = '16px';\n\t\tspacer.style.flexShrink = '0';\n\t\tcell.append(spacer);\n\t}\n\n\tconst label = el('span');\n\tcss(label, {\n\t\tfontSize: 'var(--gantt-font-size-md)',\n\t\tfontWeight: row.type === 'project' ? 'var(--gantt-font-weight-bold)' : 'var(--gantt-font-weight-normal)',\n\t\tcolor: 'var(--gantt-text)',\n\t\toverflow: 'hidden',\n\t\ttextOverflow: 'ellipsis',\n\t\twhiteSpace: 'nowrap',\n\t});\n\tlabel.textContent = row.text;\n\tcell.append(label);\n\n\treturn cell;\n}\n\nfunction buildDataCell(row: TaskNode, column: GridColumn, locale: ChartLocale): HTMLElement {\n\tconst cell = el('span');\n\tconst styles: Partial<CSSStyleDeclaration> = {\n\t\tfontSize: 'var(--gantt-font-size-sm)',\n\t\tcolor: 'var(--gantt-text-secondary)',\n\t\tpaddingRight: '8px',\n\t\toverflow: 'hidden',\n\t\ttextOverflow: 'ellipsis',\n\t\twhiteSpace: 'nowrap',\n\t};\n\tif (column.align !== undefined) {\n\t\tstyles.textAlign = column.align;\n\t}\n\tcss(cell, styles);\n\n\tconst task = toTask(row);\n\tif (column.field !== undefined) {\n\t\tconst rawValue = task[column.field];\n\t\tif (column.format !== undefined) {\n\t\t\tcell.textContent = column.format(rawValue, task, row, locale);\n\t\t} else {\n\t\t\tcell.textContent = rawValue !== null && rawValue !== undefined ? String(rawValue) : '';\n\t\t}\n\t}\n\n\treturn cell;\n}\n\nfunction buildAddButton(row: TaskNode, cbs: LeftPaneCallbacks, locale: ChartLocale): HTMLElement {\n\tconst btn = el('button');\n\tbtn.className = 'gantt-add-btn';\n\tbtn.textContent = '+';\n\tbtn.title = locale.labels?.add_subtask_title ?? EN_US_LABELS.add_subtask_title;\n\tcss(btn, {\n\t\tbackground: 'none',\n\t\tborder: 'none',\n\t\tcursor: 'pointer',\n\t\tcolor: 'var(--gantt-text-secondary)',\n\t\tfontSize: 'var(--gantt-font-size-lg)',\n\t\tlineHeight: '1',\n\t});\n\tbtn.addEventListener('click', (event) => {\n\t\tevent.stopPropagation();\n\t\tcbs.onAdd(row.id);\n\t});\n\n\treturn btn;\n}\n\nfunction toTask(row: TaskNode): Task {\n\treturn {\n\t\tid: row.id,\n\t\ttext: row.text,\n\t\tstart_date: row.start_date,\n\t\tduration: row.duration,\n\t\tprogress: row.progress,\n\t\ttype: row.type,\n\t\topen: row.open,\n\t\t...(row.parent === undefined ? {} : {parent: row.parent}),\n\t\t...(row.color === undefined ? {} : {color: row.color}),\n\t};\n}\n\n/** Builds the header row for the left pane. */\nexport function buildLeftPaneHeader(columns: GridColumn[]): HTMLElement {\n\tconst header = el('div');\n\tcss(header, {\n\t\tdisplay: 'grid',\n\t\tgridTemplateColumns: gridTemplateColumns(columns),\n\t\theight: '52px',\n\t\tbackground: 'var(--gantt-header-bg)',\n\t\tborderBottom: '1px solid var(--gantt-border)',\n\t\tpaddingLeft: '8px',\n\t\talignItems: 'flex-end',\n\t\tpaddingBottom: '4px',\n\t\tboxSizing: 'border-box',\n\t});\n\n\tconst visible = visibleColumns(columns);\n\tfor (let i = 0; i < visible.length; i++) {\n\t\tconst column = visible[i];\n\t\tif (column === undefined) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst wrapper = el('div');\n\t\tcss(wrapper, {position: 'relative', display: 'flex', alignItems: 'flex-end'});\n\n\t\tconst cell = el('span');\n\t\tcss(cell, {\n\t\t\tfontSize: 'var(--gantt-font-size-xs)',\n\t\t\tfontWeight: 'var(--gantt-font-weight-bold)',\n\t\t\tcolor: 'var(--gantt-text-secondary)',\n\t\t\tletterSpacing: 'var(--gantt-letter-spacing-wide)',\n\t\t\ttextTransform: 'uppercase',\n\t\t\tpaddingRight: '8px',\n\t\t});\n\t\tif (column.align !== undefined) {\n\t\t\tcell.style.textAlign = column.align;\n\t\t}\n\t\tcell.textContent = column.header;\n\t\twrapper.append(cell);\n\n\t\tif (i < visible.length - 1) {\n\t\t\tconst handle = el('div');\n\t\t\thandle.className = 'gantt-col-resize-handle';\n\t\t\tcss(handle, {\n\t\t\t\tposition: 'absolute',\n\t\t\t\tright: '-3px',\n\t\t\t\ttop: '0',\n\t\t\t\tbottom: '0',\n\t\t\t\twidth: '6px',\n\t\t\t\tcursor: 'col-resize',\n\t\t\t\tzIndex: '1',\n\t\t\t});\n\t\t\twrapper.append(handle);\n\t\t}\n\n\t\theader.append(wrapper);\n\t}\n\n\treturn header;\n}\n\nexport const COLUMN_RESIZE_MIN_WIDTH = COLUMN_MIN_WIDTH;\n\n/**\n * Wires up column resize interactions on header handles.\n * Must be called after the header is in the DOM (so getBoundingClientRect works).\n * Returns a cleanup function.\n */\nexport function setupColumnResize(headerEl: HTMLElement, bodyEl: HTMLElement, columns: GridColumn[], onChange?: (columns: GridColumn[]) => void): () => void {\n\tconst handles = headerEl.querySelectorAll<HTMLElement>('.gantt-col-resize-handle');\n\tconst cleanups: (() => void)[] = [];\n\n\tfor (let colIndex = 0; colIndex < handles.length; colIndex++) {\n\t\tconst handle = handles.item(colIndex);\n\t\tif (handle === null) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst capturedColIndex = colIndex;\n\n\t\tconst onPointerDown = (e: PointerEvent): void => {\n\t\t\tif (e.button !== 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\n\t\t\tconst startX = e.clientX;\n\t\t\tconst cells = [...headerEl.children] as HTMLElement[];\n\t\t\tconst startWidths = cells.map((c) => c.getBoundingClientRect().width);\n\n\t\t\tconst onMove = (me: PointerEvent): void => {\n\t\t\t\tconst dx = me.clientX - startX;\n\t\t\t\tconst newWidths = [...startWidths];\n\n\t\t\t\tnewWidths[capturedColIndex] = Math.max(COLUMN_MIN_WIDTH, (startWidths[capturedColIndex] ?? 0) + dx);\n\n\t\t\t\tif (capturedColIndex + 1 < newWidths.length) {\n\t\t\t\t\tnewWidths[capturedColIndex + 1] = Math.max(COLUMN_MIN_WIDTH, (startWidths[capturedColIndex + 1] ?? 0) - dx);\n\t\t\t\t}\n\n\t\t\t\tconst template = newWidths.map((w) => `${Math.round(w)}px`).join(' ');\n\t\t\t\theaderEl.style.gridTemplateColumns = template;\n\n\t\t\t\tconst rows = bodyEl.querySelectorAll<HTMLElement>('[role=\"row\"]');\n\t\t\t\tfor (const row of rows) {\n\t\t\t\t\trow.style.gridTemplateColumns = template;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst onUp = (): void => {\n\t\t\t\twindow.removeEventListener('pointermove', onMove);\n\t\t\t\twindow.removeEventListener('pointerup', onUp);\n\n\t\t\t\tconst finalCells = [...headerEl.children] as HTMLElement[];\n\t\t\t\tconst visible = visibleColumns(columns);\n\t\t\t\tfor (let i = 0; i < visible.length && i < finalCells.length; i++) {\n\t\t\t\t\tconst col = visible[i];\n\t\t\t\t\tconst cell = finalCells[i];\n\t\t\t\t\tif (col !== undefined && cell !== undefined) {\n\t\t\t\t\t\tconst w = cell.getBoundingClientRect().width;\n\t\t\t\t\t\tcol.width = `${Math.round(w)}px`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tonChange?.([...columns]);\n\t\t\t};\n\n\t\t\twindow.addEventListener('pointermove', onMove);\n\t\t\twindow.addEventListener('pointerup', onUp);\n\t\t};\n\n\t\thandle.addEventListener('pointerdown', onPointerDown);\n\t\tcleanups.push(() => {\n\t\t\thandle.removeEventListener('pointerdown', onPointerDown);\n\t\t});\n\t}\n\n\treturn () => {\n\t\tfor (const cleanup of cleanups) {\n\t\t\tcleanup();\n\t\t}\n\t};\n}\n","import {setAttrs} from './helpers.ts';\nimport {type RoutedLink} from '../../rendering/linkRouter.ts';\n\nconst NS = 'http://www.w3.org/2000/svg';\nconst ARROW_SIZE = 6;\n\n/**\n * Creates the SVG overlay element. Call once; pass to updateDependencyLayer on each render.\n * Also creates a hidden ghost-line path used during link-creation drags.\n */\nexport function createDependencyLayer(totalWidth: number, totalHeight: number): SVGSVGElement {\n\tconst svg = document.createElementNS(NS, 'svg');\n\tsetAttrs(svg, {\n\t\twidth: totalWidth,\n\t\theight: totalHeight,\n\t});\n\tObject.assign(svg.style, {\n\t\tposition: 'absolute',\n\t\ttop: '0',\n\t\tleft: '0',\n\t\tpointerEvents: 'none',\n\t\toverflow: 'visible',\n\t\tzIndex: '1',\n\t});\n\n\t// Arrow markers (created once, reused by all paths via xlink/href)\n\tconst defs = document.createElementNS(NS, 'defs');\n\n\tfor (const [id, color] of [\n\t\t['gantt-arrow', 'var(--gantt-link)'],\n\t\t['gantt-arrow-hi', 'var(--gantt-link-hi)'],\n\t] as const) {\n\t\tconst marker = document.createElementNS(NS, 'marker');\n\t\tsetAttrs(marker, {\n\t\t\tid,\n\t\t\tviewBox: '0 0 10 10',\n\t\t\trefX: '9',\n\t\t\trefY: '5',\n\t\t\tmarkerWidth: ARROW_SIZE,\n\t\t\tmarkerHeight: ARROW_SIZE,\n\t\t\torient: 'auto',\n\t\t});\n\t\tconst path = document.createElementNS(NS, 'path');\n\t\tsetAttrs(path, {d: 'M 0 1 L 10 5 L 0 9 Z', fill: color});\n\t\tmarker.append(path);\n\t\tdefs.append(marker);\n\t}\n\n\tsvg.append(defs);\n\n\t// Ghost line for link-creation drag (hidden by default)\n\tconst ghostPath = document.createElementNS(NS, 'path');\n\tsetAttrs(ghostPath, {\n\t\td: '',\n\t\tfill: 'none',\n\t\tstroke: 'var(--gantt-link)',\n\t\t'stroke-width': '1.5',\n\t\t'stroke-dasharray': '5 3',\n\t});\n\tghostPath.classList.add('gantt-ghost-line');\n\tghostPath.style.display = 'none';\n\tsvg.append(ghostPath);\n\n\treturn svg;\n}\n\n/**\n * Shows or updates the ghost line drawn during a link-creation drag.\n * Pass valid=true when the pointer is over a valid target bar.\n */\nexport function showGhostLine(svg: SVGSVGElement, x1: number, y1: number, x2: number, y2: number, valid: boolean): void {\n\tconst ghost = svg.querySelector<SVGPathElement>('path.gantt-ghost-line');\n\tif (ghost === null) {\n\t\treturn;\n\t}\n\tsetAttrs(ghost, {\n\t\td: `M ${x1},${y1} L ${x2},${y2}`,\n\t\t'stroke-dasharray': valid ? 'none' : '5 3',\n\t});\n\tif (valid) {\n\t\tghost.setAttribute('marker-end', 'url(#gantt-arrow)');\n\t} else {\n\t\tghost.removeAttribute('marker-end');\n\t}\n\tghost.style.display = '';\n}\n\n/**\n * Hides the ghost line after a link-creation drag completes or is cancelled.\n */\nexport function hideGhostLine(svg: SVGSVGElement): void {\n\tconst ghost = svg.querySelector<SVGPathElement>('path.gantt-ghost-line');\n\tif (ghost !== null) {\n\t\tghost.style.display = 'none';\n\t\tghost.removeAttribute('marker-end');\n\t}\n}\n\n/**\n * Replaces all path elements in the SVG to reflect the current link set.\n * The `<defs>` node (first child) is preserved.\n */\nexport function updateDependencyLayer(\n\tsvg: SVGSVGElement,\n\tlinks: RoutedLink[],\n\ttotalWidth: number,\n\ttotalHeight: number,\n\tselectedTaskId: number | null,\n\thighlightLinkedDependenciesOnSelect: boolean,\n): void {\n\tsetAttrs(svg, {width: totalWidth, height: totalHeight});\n\n\t// Remove existing paths (keep defs at index 0, and keep ghost path)\n\tconst toRemove: Element[] = [];\n\tfor (let i = 1; i < svg.children.length; i++) {\n\t\tconst child = svg.children[i];\n\t\tif (child !== undefined && !child.classList.contains('gantt-ghost-line')) {\n\t\t\ttoRemove.push(child);\n\t\t}\n\t}\n\tfor (const node of toRemove) {\n\t\tsvg.removeChild(node);\n\t}\n\n\tfor (const link of links) {\n\t\tconst {points} = link;\n\t\tif (points.length === 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet d = `M ${points[0]?.x ?? 0},${points[0]?.y ?? 0}`;\n\t\tfor (let i = 1; i < points.length; i++) {\n\t\t\tconst p = points[i];\n\t\t\tif (p !== undefined) {\n\t\t\t\td += ` L ${p.x},${p.y}`;\n\t\t\t}\n\t\t}\n\n\t\tconst isRelated =\n\t\t\thighlightLinkedDependenciesOnSelect && selectedTaskId !== null && (link.sourceTaskId === selectedTaskId || link.targetTaskId === selectedTaskId);\n\n\t\tconst path = document.createElementNS(NS, 'path');\n\t\tsetAttrs(path, {\n\t\t\td,\n\t\t\tfill: 'none',\n\t\t\tstroke: isRelated ? 'var(--gantt-link-hi)' : 'var(--gantt-link)',\n\t\t\t'stroke-width': isRelated ? '1.8' : '1.5',\n\t\t\t'stroke-linejoin': 'round',\n\t\t\t'marker-end': isRelated ? 'url(#gantt-arrow-hi)' : 'url(#gantt-arrow)',\n\t\t});\n\t\tsvg.append(path);\n\t}\n}\n","import {type TaskNode} from '../../domain/tree.ts';\nimport {type PixelMapper} from '../../timeline/pixelMapper.ts';\nimport {parseDate, addDays} from '../../domain/dateMath.ts';\nimport {type Task} from '../../validation/schemas.ts';\nimport {type GanttCallbacks} from '../gantt-chart.ts';\n\n/**\n * Attaches drag-to-move and resize listeners to a bar element.\n * Returns a cleanup function that removes all listeners.\n *\n * Design: all mutable state lives in closure variables captured at mousedown.\n * No global state; multiple bars can be dragged independently (one at a time).\n */\nexport function attachDrag(barEl: HTMLElement, resizeHandleEl: HTMLElement, task: TaskNode, getMapper: () => PixelMapper, cbs: GanttCallbacks): () => void {\n\t// ── Move ───────────────────────────────────────────────────────────────\n\tfunction onBarDown(e: PointerEvent): void {\n\t\tif (e.button !== 0) {\n\t\t\treturn;\n\t\t}\n\t\te.preventDefault();\n\t\ttry {\n\t\t\tbarEl.setPointerCapture(e.pointerId);\n\t\t} catch {\n\t\t\t// Browsers/tests may reject synthetic pointer ids.\n\t\t}\n\t\tcbs.onSelect?.(task.id);\n\n\t\tconst startX = e.clientX;\n\t\tconst originDate = parseDate(task.start_date);\n\t\tconst mapper = getMapper(); // snapshot at mousedown\n\n\t\tfunction onMove(me: PointerEvent): void {\n\t\t\tconst dx = me.clientX - startX;\n\t\t\tconst days = Math.round(mapper.widthToDuration(dx));\n\t\t\tcbs.onMove?.({id: task.id, startDate: addDays(originDate, days)});\n\t\t}\n\n\t\tfunction onUp(): void {\n\t\t\twindow.removeEventListener('pointermove', onMove);\n\t\t\twindow.removeEventListener('pointerup', onUp);\n\t\t\tbarEl.style.cursor = 'grab';\n\t\t}\n\n\t\tbarEl.style.cursor = 'grabbing';\n\t\twindow.addEventListener('pointermove', onMove);\n\t\twindow.addEventListener('pointerup', onUp);\n\t}\n\n\t// ── Resize ─────────────────────────────────────────────────────────────\n\tfunction onResizeDown(e: PointerEvent): void {\n\t\tif (e.button !== 0) {\n\t\t\treturn;\n\t\t}\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\ttry {\n\t\t\tresizeHandleEl.setPointerCapture(e.pointerId);\n\t\t} catch {\n\t\t\t// Browsers/tests may reject synthetic pointer ids.\n\t\t}\n\n\t\tconst startX = e.clientX;\n\t\tconst origDur = task.duration;\n\t\tconst mapper = getMapper();\n\n\t\tfunction onMove(me: PointerEvent): void {\n\t\t\tconst dx = me.clientX - startX;\n\t\t\tconst daysDelta = Math.round(mapper.widthToDuration(dx));\n\t\t\tcbs.onResize?.({id: task.id, duration: Math.max(1, origDur + daysDelta)});\n\t\t}\n\n\t\tfunction onUp(): void {\n\t\t\twindow.removeEventListener('pointermove', onMove);\n\t\t\twindow.removeEventListener('pointerup', onUp);\n\t\t}\n\n\t\twindow.addEventListener('pointermove', onMove);\n\t\twindow.addEventListener('pointerup', onUp);\n\t}\n\n\tfunction onBarClick(event: MouseEvent): void {\n\t\tif (event.detail !== 2) {\n\t\t\treturn;\n\t\t}\n\t\tcbs.onTaskEditIntent?.({id: task.id, source: 'bar', trigger: 'double_click', task: toTask(task)});\n\t}\n\n\tbarEl.addEventListener('pointerdown', onBarDown);\n\tbarEl.addEventListener('click', onBarClick);\n\tresizeHandleEl.addEventListener('pointerdown', onResizeDown);\n\n\treturn () => {\n\t\tbarEl.removeEventListener('pointerdown', onBarDown);\n\t\tbarEl.removeEventListener('click', onBarClick);\n\t\tresizeHandleEl.removeEventListener('pointerdown', onResizeDown);\n\t};\n}\n\n/**\n * Attaches click-to-select on a milestone diamond.\n * Returns cleanup.\n */\nexport function attachMilestoneClick(diamondEl: HTMLElement, taskId: number, cbs: GanttCallbacks): () => void {\n\tfunction onClick(): void {\n\t\tcbs.onSelect?.(taskId);\n\t}\n\tfunction onDoubleClick(event: MouseEvent): void {\n\t\tif (event.detail === 2) {\n\t\t\tconst task = (diamondEl as HTMLElement & {__task?: Task}).__task;\n\t\t\tif (task === undefined) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcbs.onTaskEditIntent?.({id: taskId, source: 'milestone', trigger: 'double_click', task});\n\t\t}\n\t}\n\tdiamondEl.addEventListener('click', onClick);\n\tdiamondEl.addEventListener('click', onDoubleClick);\n\treturn () => {\n\t\tdiamondEl.removeEventListener('click', onClick);\n\t\tdiamondEl.removeEventListener('click', onDoubleClick);\n\t};\n}\n\nexport function bindMilestoneTask(diamondEl: HTMLElement, task: Task): void {\n\t(diamondEl as HTMLElement & {__task?: Task}).__task = task;\n}\n\nfunction toTask(row: TaskNode): Task {\n\treturn {\n\t\tid: row.id,\n\t\ttext: row.text,\n\t\tstart_date: row.start_date,\n\t\tduration: row.duration,\n\t\tprogress: row.progress,\n\t\ttype: row.type,\n\t\topen: row.open,\n\t\t...(row.parent === undefined ? {} : {parent: row.parent}),\n\t\t...(row.color === undefined ? {} : {color: row.color}),\n\t};\n}\n","import {type GanttCallbacks} from '../gantt-chart.ts';\nimport {showGhostLine, hideGhostLine} from '../dom/dependencyLayer.ts';\n\n/**\n * Attaches a link-creation drag listener to an endpoint handle.\n * Returns a cleanup function that removes all listeners.\n */\nexport function attachLinkEndpointHandle(\n\thandle: HTMLElement,\n\tsourceTaskId: number,\n\tanchorX: number,\n\tanchorY: number,\n\tsvgLayer: SVGSVGElement,\n\tabsoluteLayer: HTMLElement,\n\tcbs: GanttCallbacks,\n): () => void {\n\tfunction onPointerDown(e: PointerEvent): void {\n\t\tif (e.button !== 0) {\n\t\t\treturn;\n\t\t}\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\ttry {\n\t\t\thandle.setPointerCapture(e.pointerId);\n\t\t} catch {\n\t\t\t// Browsers/tests may reject synthetic pointer ids.\n\t\t}\n\n\t\tlet validTargetId: number | null = null;\n\n\t\tfunction onMove(me: PointerEvent): void {\n\t\t\tconst layerRect = absoluteLayer.getBoundingClientRect();\n\t\t\tconst x = me.clientX - layerRect.left;\n\t\t\tconst y = me.clientY - layerRect.top;\n\n\t\t\t// Hit-test for bars and milestones\n\t\t\tconst el = document.elementFromPoint(me.clientX, me.clientY);\n\t\t\tconst barEl = el?.closest<HTMLElement>('[data-task-id]');\n\t\t\tconst targetId = barEl !== null && barEl !== undefined ? Number(barEl.dataset['taskId']) : null;\n\n\t\t\tvalidTargetId = targetId !== null && targetId !== sourceTaskId ? targetId : null;\n\n\t\t\tshowGhostLine(svgLayer, anchorX, anchorY, x, y, validTargetId !== null);\n\t\t}\n\n\t\tfunction onUp(): void {\n\t\t\twindow.removeEventListener('pointermove', onMove);\n\t\t\twindow.removeEventListener('pointerup', onUp);\n\t\t\thideGhostLine(svgLayer);\n\n\t\t\tif (validTargetId !== null) {\n\t\t\t\tcbs.onLinkCreate?.({sourceTaskId, targetTaskId: validTargetId, type: 'FS'});\n\t\t\t}\n\t\t}\n\n\t\twindow.addEventListener('pointermove', onMove);\n\t\twindow.addEventListener('pointerup', onUp);\n\t}\n\n\thandle.addEventListener('pointerdown', onPointerDown);\n\thandle.tabIndex = 0;\n\thandle.setAttribute('role', 'button');\n\thandle.setAttribute('aria-label', `Create link from task ${sourceTaskId}`);\n\n\tfunction onKeyDown(event: KeyboardEvent): void {\n\t\tif (event.key === 'Enter' || event.key === ' ') {\n\t\t\tevent.preventDefault();\n\t\t}\n\t}\n\thandle.addEventListener('keydown', onKeyDown);\n\n\treturn () => {\n\t\thandle.removeEventListener('pointerdown', onPointerDown);\n\t\thandle.removeEventListener('keydown', onKeyDown);\n\t};\n}\n\n/**\n * Creates an endpoint handle DOM element.\n * The caller must position it with inline styles and append it to the layer.\n */\nexport function createEndpointHandle(): HTMLElement {\n\tconst handle = document.createElement('div');\n\thandle.className = 'gantt-link-endpoint';\n\thandle.style.position = 'absolute';\n\thandle.style.width = '10px';\n\thandle.style.height = '10px';\n\thandle.style.borderRadius = '50%';\n\thandle.style.background = 'var(--gantt-link)';\n\thandle.style.border = '2px solid var(--gantt-bg)';\n\thandle.style.cursor = 'crosshair';\n\thandle.style.zIndex = '4';\n\thandle.style.opacity = '0';\n\thandle.style.transition = 'opacity 0.15s ease, transform 0.1s ease';\n\thandle.style.transform = 'translate(-50%, -50%) scale(0.8)';\n\thandle.style.pointerEvents = 'auto';\n\thandle.style.touchAction = 'none';\n\treturn handle;\n}\n","import {el, css, clearChildren} from './helpers.ts';\nimport {createDependencyLayer, updateDependencyLayer, hideGhostLine} from './dependencyLayer.ts';\nimport {attachDrag, attachMilestoneClick, bindMilestoneTask} from '../interaction/drag.ts';\nimport {attachLinkEndpointHandle, createEndpointHandle} from '../interaction/linkCreation.ts';\nimport {type GanttState} from '../state.ts';\nimport {type TaskNode} from '../../domain/tree.ts';\nimport {type BarLayout} from '../../timeline/layoutEngine.ts';\nimport {ROW_HEIGHT, MILESTONE_HALF, totalContentHeight} from '../../timeline/layoutEngine.ts';\nimport {nextScaleBoundary, snapToScaleBoundary} from '../../timeline/scale.ts';\nimport {type GanttCallbacks} from '../gantt-chart.ts';\nimport {startOfDay} from '../../domain/dateMath.ts';\nimport {type ChartLocale, EN_US_LABELS, formatLabel} from '../../locale.ts';\n\nconst BAR_COLOR: Record<string, string> = {\n\ttask: 'var(--gantt-task)',\n\tproject: 'var(--gantt-project)',\n\tmilestone: 'var(--gantt-milestone)',\n};\n\n/**\n * Persistent DOM references for the right pane.\n * Created once in mount.ts; updated cheaply on each render.\n */\nexport type RightPaneRefs = {\n\tscrollContainer: HTMLElement;\n\tstripeContainer: HTMLElement;\n\tabsoluteLayer: HTMLElement;\n\tsvgLayer: SVGSVGElement;\n\t/** Map of taskId → {bar, resizeHandle, cleanupDrag} for update-in-place */\n\tbarRegistry: Map<\n\t\tnumber,\n\t\t{\n\t\t\tbar: HTMLElement;\n\t\t\tresizeHandle: HTMLElement;\n\t\t\tcleanupDrag: () => void;\n\t\t\tcleanupLinkHandles?: () => void;\n\t\t}\n\t>;\n};\n\n/** Creates the skeleton DOM structure for the right pane. Call once. */\nexport function createRightPaneRefs(): RightPaneRefs {\n\tconst scrollContainer = el('div');\n\tconst stripeContainer = el('div');\n\tconst absoluteLayer = el('div');\n\tconst svgLayer = createDependencyLayer(0, 0);\n\n\tcss(stripeContainer, {position: 'relative'});\n\tcss(absoluteLayer, {position: 'absolute', top: '0', left: '0'});\n\n\tscrollContainer.append(stripeContainer);\n\tscrollContainer.append(absoluteLayer);\n\tabsoluteLayer.append(svgLayer);\n\n\treturn {\n\t\tscrollContainer,\n\t\tstripeContainer,\n\t\tabsoluteLayer,\n\t\tsvgLayer,\n\t\tbarRegistry: new Map(),\n\t};\n}\n\n/**\n * Full render of the right pane.\n * Grid lines and stripes are rebuilt each call (cheap — no event listeners).\n * Bars are rebuilt each call with fresh drag listeners (old ones cleaned up first).\n */\nexport function renderRightPane(refs: RightPaneRefs, state: GanttState, cbs: GanttCallbacks): void {\n\tconst {\n\t\tallRows,\n\t\tlayouts,\n\t\tlinks,\n\t\tmapper,\n\t\tscale,\n\t\tviewportStart,\n\t\tviewportEnd,\n\t\ttotalWidth,\n\t\tselectedId,\n\t\thighlightLinkedDependenciesOnSelect,\n\t\tpaddingTop,\n\t\tpaddingBottom,\n\t\tstartIndex,\n\t} = state;\n\n\tconst {stripeContainer, absoluteLayer, svgLayer, barRegistry} = refs;\n\tconst rowCount = allRows.length;\n\tconst contentHeight = totalContentHeight(rowCount);\n\n\t// ── Stripe rows (virtual slice only) ───────────────────────────────────\n\tconst visibleRows = allRows.slice(state.startIndex, state.endIndex + 1);\n\tclearChildren(stripeContainer);\n\tcss(stripeContainer, {width: `${totalWidth}px`});\n\n\t// Top spacer\n\tif (paddingTop > 0) {\n\t\tconst s = el('div');\n\t\ts.style.height = `${paddingTop}px`;\n\t\tstripeContainer.append(s);\n\t}\n\n\tfor (let i = 0; i < visibleRows.length; i++) {\n\t\tconst rowIdx = startIndex + i;\n\t\tconst stripe = el('div');\n\t\tcss(stripe, {\n\t\t\theight: `${ROW_HEIGHT}px`,\n\t\t\tbackground: rowIdx % 2 === 0 ? 'var(--gantt-bg)' : 'var(--gantt-stripe)',\n\t\t\tborderBottom: '1px solid var(--gantt-border)',\n\t\t});\n\t\tstripeContainer.append(stripe);\n\t}\n\n\t// Bottom spacer\n\tif (paddingBottom > 0) {\n\t\tconst s = el('div');\n\t\ts.style.height = `${paddingBottom}px`;\n\t\tstripeContainer.append(s);\n\t}\n\n\t// ── Absolute layer: grid lines + today + bars ──────────────────────────\n\tcss(absoluteLayer, {width: `${totalWidth}px`, height: `${contentHeight}px`});\n\n\t// Remove all children except svgLayer (last child)\n\tconst toRemove: Element[] = [];\n\tfor (const child of [...absoluteLayer.children]) {\n\t\tif (child !== svgLayer) {\n\t\t\ttoRemove.push(child);\n\t\t}\n\t}\n\tfor (const node of toRemove) {\n\t\tabsoluteLayer.removeChild(node);\n\t}\n\n\t// Clean up orphaned ghost line from interrupted drags\n\thideGhostLine(svgLayer);\n\n\t// Clean up previous drag listeners\n\tfor (const {cleanupDrag, cleanupLinkHandles} of barRegistry.values()) {\n\t\tcleanupDrag();\n\t\tcleanupLinkHandles?.();\n\t}\n\tbarRegistry.clear();\n\n\t// Grid lines\n\tif (scale === 'day') {\n\t\trenderSpecialDayBackgrounds(absoluteLayer, svgLayer, state, contentHeight);\n\t}\n\n\t// Grid lines\n\tlet gridCur = snapToScaleBoundary(viewportStart, scale);\n\twhile (gridCur <= viewportEnd) {\n\t\tconst x = mapper.toX(gridCur);\n\t\tconst line = el('div');\n\t\tcss(line, {\n\t\t\tposition: 'absolute',\n\t\t\tleft: `${x}px`,\n\t\t\ttop: '0',\n\t\t\twidth: '1px',\n\t\t\theight: `${contentHeight}px`,\n\t\t\tbackground: 'var(--gantt-grid-line)',\n\t\t\tpointerEvents: 'none',\n\t\t});\n\t\tabsoluteLayer.insertBefore(line, svgLayer);\n\t\tgridCur = nextScaleBoundary(gridCur, scale);\n\t}\n\n\t// Today marker (render only when within timeline bounds)\n\tconst todayX = mapper.toX(new Date());\n\tconst todayLineWidth = 2;\n\tif (todayX >= 0 && todayX <= totalWidth - todayLineWidth) {\n\t\tconst todayLine = el('div');\n\t\ttodayLine.className = 'gantt-today-marker';\n\t\tcss(todayLine, {\n\t\t\tposition: 'absolute',\n\t\t\tleft: `${todayX}px`,\n\t\t\ttop: '0',\n\t\t\twidth: `${todayLineWidth}px`,\n\t\t\theight: `${contentHeight}px`,\n\t\t\tbackground: 'var(--gantt-today)',\n\t\t\tpointerEvents: 'none',\n\t\t\tzIndex: '5',\n\t\t});\n\t\tabsoluteLayer.insertBefore(todayLine, svgLayer);\n\t}\n\n\tconst visibleTaskIds = new Set(visibleRows.map((task) => task.id));\n\n\t// Bars (virtual slice only)\n\tfor (const task of visibleRows) {\n\t\tconst layout = layouts.get(task.id);\n\t\tif (layout === undefined) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (layout.type === 'milestone') {\n\t\t\trenderMilestone(absoluteLayer, svgLayer, task, layout, selectedId, barRegistry, cbs, state);\n\t\t} else {\n\t\t\trenderBar(absoluteLayer, svgLayer, task, layout, selectedId, barRegistry, state, cbs);\n\t\t}\n\t}\n\n\t// SVG dependency overlay (visible rows only)\n\tconst visibleLinks = links.filter((link) => visibleTaskIds.has(link.sourceTaskId) && visibleTaskIds.has(link.targetTaskId));\n\tupdateDependencyLayer(svgLayer, visibleLinks, totalWidth, contentHeight, selectedId, highlightLinkedDependenciesOnSelect);\n}\n\nfunction renderSpecialDayBackgrounds(layer: HTMLElement, beforeNode: Element, state: GanttState, contentHeight: number): void {\n\tconst {mapper, viewportStart, viewportEnd, showWeekends, weekendDays, specialDaysByDate} = state;\n\tlet cur = startOfDay(viewportStart);\n\n\twhile (cur < viewportEnd) {\n\t\tconst next = new Date(cur.getTime() + 86_400_000);\n\t\tconst x = mapper.toX(cur);\n\t\tconst width = Math.max(1, mapper.toX(next) - x);\n\t\tconst dateKey = cur.toISOString().slice(0, 10);\n\t\tconst specialDay = specialDaysByDate.get(dateKey);\n\t\tconst isWeekend = weekendDays.has(cur.getUTCDay());\n\n\t\tlet kind: 'weekend' | 'holiday' | 'custom' | null = null;\n\t\tif (specialDay !== undefined) {\n\t\t\tconst {kind: specialKind} = specialDay;\n\t\t\tkind = specialKind;\n\t\t} else if (showWeekends && isWeekend) {\n\t\t\tkind = 'weekend';\n\t\t}\n\n\t\tif (kind !== null) {\n\t\t\tconst dayCell = el('div');\n\t\t\tdayCell.className = `gantt-day-cell gantt-day-cell--${kind}`;\n\t\t\tif (specialDay?.className !== undefined) {\n\t\t\t\tdayCell.classList.add(specialDay.className);\n\t\t\t}\n\t\t\tdayCell.dataset['date'] = dateKey;\n\t\t\tif (specialDay?.label !== undefined) {\n\t\t\t\tdayCell.dataset['label'] = specialDay.label;\n\t\t\t\tdayCell.title = specialDay.label;\n\t\t\t}\n\t\t\tcss(dayCell, {\n\t\t\t\tposition: 'absolute',\n\t\t\t\tleft: `${x}px`,\n\t\t\t\ttop: '0',\n\t\t\t\twidth: `${width}px`,\n\t\t\t\theight: `${contentHeight}px`,\n\t\t\t\tpointerEvents: 'none',\n\t\t\t\tzIndex: '1',\n\t\t\t});\n\t\t\tlayer.insertBefore(dayCell, beforeNode);\n\t\t}\n\n\t\tcur = next;\n\t}\n}\n\n// ─── Bar ─────────────────────────────────────────────────────────────────────\n\nfunction renderBar(\n\tlayer: HTMLElement,\n\tsvgLayer: SVGSVGElement,\n\ttask: TaskNode,\n\tlayout: BarLayout,\n\tselectedId: number | null,\n\tregistry: RightPaneRefs['barRegistry'],\n\tstate: GanttState,\n\tcbs: GanttCallbacks,\n): void {\n\tconst selected = task.id === selectedId;\n\tconst color = BAR_COLOR[layout.type] ?? BAR_COLOR['task'];\n\n\tconst bar = el('div');\n\tbar.className = `gantt-bar${selected ? ' gantt-bar--selected gantt-shape--selected' : ''}`;\n\tcss(bar, {\n\t\tposition: 'absolute',\n\t\tleft: `${layout.x}px`,\n\t\ttop: `${layout.y}px`,\n\t\twidth: `${layout.width}px`,\n\t\theight: `${layout.height}px`,\n\t\t...(color === undefined ? {} : {background: color}),\n\t\tborderRadius: layout.type === 'project' ? '3px' : '4px',\n\t\tcursor: 'grab',\n\t\tuserSelect: 'none',\n\t\toverflow: 'hidden',\n\t\tzIndex: selected ? '3' : '2',\n\t\ttouchAction: 'none',\n\t});\n\n\t// Progress overlay\n\tif (layout.progressWidth > 0) {\n\t\tconst prog = el('div');\n\t\tcss(prog, {\n\t\t\tposition: 'absolute',\n\t\t\tleft: '0',\n\t\t\ttop: '0',\n\t\t\twidth: `${layout.progressWidth}px`,\n\t\t\theight: '100%',\n\t\t\tbackground: 'rgba(0,0,0,0.18)',\n\t\t\tpointerEvents: 'none',\n\t\t});\n\t\tbar.append(prog);\n\t}\n\n\t// Label\n\tconst label = el('span');\n\tcss(label, {\n\t\tposition: 'absolute',\n\t\tleft: '8px',\n\t\tright: '8px',\n\t\ttop: '50%',\n\t\ttransform: 'translateY(-50%)',\n\t\toverflow: 'hidden',\n\t\ttextOverflow: 'ellipsis',\n\t\tcolor: 'var(--gantt-bar-label-color)',\n\t\tfontSize: 'var(--gantt-font-size-sm)',\n\t\tfontWeight: 'var(--gantt-font-weight-semibold)',\n\t\twhiteSpace: 'nowrap',\n\t\tpointerEvents: 'none',\n\t\ttextShadow: '0 1px 2px rgba(0,0,0,0.25)',\n\t});\n\tlabel.textContent = task.text;\n\tbar.append(label);\n\tbar.tabIndex = 0;\n\tbar.setAttribute('role', 'button');\n\tbar.setAttribute('aria-label', ariaLabel(state.locale, 'aria_task', task.text));\n\tbar.setAttribute('aria-pressed', String(selected));\n\tbar.dataset['taskId'] = String(task.id);\n\tbar.addEventListener('click', () => {\n\t\tcbs.onSelect?.(task.id);\n\t});\n\tbar.addEventListener('keydown', (event) => {\n\t\tif (event.key === 'Enter' || event.key === ' ') {\n\t\t\tevent.preventDefault();\n\t\t\tcbs.onSelect?.(task.id);\n\t\t}\n\t});\n\n\t// Resize handle\n\tconst handle = el('div');\n\thandle.className = 'gantt-resize-handle';\n\tcss(handle, {\n\t\tposition: 'absolute',\n\t\tright: '0',\n\t\ttop: '0',\n\t\twidth: '8px',\n\t\theight: '100%',\n\t\tcursor: 'ew-resize',\n\t\tzIndex: '1',\n\t\ttouchAction: 'none',\n\t});\n\tbar.append(handle);\n\n\tlayer.insertBefore(bar, svgLayer);\n\n\tconst cleanupDrag = attachDrag(bar, handle, task, () => state.mapper, cbs);\n\n\t// Link-creation endpoint handles\n\tlet cleanupLinkHandles: (() => void) | undefined;\n\tif (state.linkCreationEnabled) {\n\t\tconst barCenterY = layout.y + layout.height / 2;\n\t\tconst leftHandle = createEndpointHandle();\n\t\tleftHandle.style.left = `${layout.x}px`;\n\t\tleftHandle.style.top = `${barCenterY}px`;\n\t\tlayer.insertBefore(leftHandle, svgLayer);\n\n\t\tconst rightHandle = createEndpointHandle();\n\t\trightHandle.style.left = `${layout.x + layout.width}px`;\n\t\trightHandle.style.top = `${barCenterY}px`;\n\t\tlayer.insertBefore(rightHandle, svgLayer);\n\n\t\tconst cleanupLeft = attachLinkEndpointHandle(leftHandle, task.id, layout.x, barCenterY, svgLayer, layer, cbs);\n\t\tconst cleanupRight = attachLinkEndpointHandle(rightHandle, task.id, layout.x + layout.width, barCenterY, svgLayer, layer, cbs);\n\n\t\t// Show handles on bar hover\n\t\tconst onBarEnter = (): void => {\n\t\t\tleftHandle.style.opacity = '1';\n\t\t\trightHandle.style.opacity = '1';\n\t\t\tleftHandle.style.transform = 'translate(-50%, -50%) scale(1)';\n\t\t\trightHandle.style.transform = 'translate(-50%, -50%) scale(1)';\n\t\t};\n\t\tconst onBarLeave = (): void => {\n\t\t\tleftHandle.style.opacity = '0';\n\t\t\trightHandle.style.opacity = '0';\n\t\t\tleftHandle.style.transform = 'translate(-50%, -50%) scale(0.8)';\n\t\t\trightHandle.style.transform = 'translate(-50%, -50%) scale(0.8)';\n\t\t};\n\t\tbar.addEventListener('mouseenter', onBarEnter);\n\t\tbar.addEventListener('mouseleave', onBarLeave);\n\n\t\tcleanupLinkHandles = (): void => {\n\t\t\tcleanupLeft();\n\t\t\tcleanupRight();\n\t\t\tbar.removeEventListener('mouseenter', onBarEnter);\n\t\t\tbar.removeEventListener('mouseleave', onBarLeave);\n\t\t};\n\t}\n\n\tconst entry: {\n\t\tbar: HTMLElement;\n\t\tresizeHandle: HTMLElement;\n\t\tcleanupDrag: () => void;\n\t\tcleanupLinkHandles?: () => void;\n\t} = {bar, resizeHandle: handle, cleanupDrag};\n\tif (cleanupLinkHandles !== undefined) {\n\t\tentry.cleanupLinkHandles = cleanupLinkHandles;\n\t}\n\tregistry.set(task.id, entry);\n}\n\n// ─── Milestone ────────────────────────────────────────────────────────────────\n\nfunction renderMilestone(\n\tlayer: HTMLElement,\n\tsvgLayer: SVGSVGElement,\n\ttask: TaskNode,\n\tlayout: BarLayout,\n\tselectedId: number | null,\n\tregistry: RightPaneRefs['barRegistry'],\n\tcbs: GanttCallbacks,\n\tstate: GanttState,\n): void {\n\tconst selected = task.id === selectedId;\n\tconst size = MILESTONE_HALF * 2;\n\n\tconst diamond = el('div');\n\tdiamond.className = `gantt-milestone${selected ? ' gantt-shape--selected' : ''}`;\n\tcss(diamond, {\n\t\tposition: 'absolute',\n\t\tleft: `${layout.x - MILESTONE_HALF}px`,\n\t\ttop: `${layout.y + (layout.height - size) / 2}px`,\n\t\twidth: `${size}px`,\n\t\theight: `${size}px`,\n\t\tbackground: 'var(--gantt-milestone)',\n\t\ttransform: 'rotate(45deg)',\n\t\tcursor: 'pointer',\n\t\tzIndex: '4',\n\t});\n\tdiamond.tabIndex = 0;\n\tdiamond.setAttribute('role', 'button');\n\tdiamond.setAttribute('aria-label', ariaLabel(state.locale, 'aria_milestone', task.text));\n\tdiamond.setAttribute('aria-pressed', String(selected));\n\tdiamond.dataset['taskId'] = String(task.id);\n\tdiamond.addEventListener('keydown', (event) => {\n\t\tif (event.key === 'Enter' || event.key === ' ') {\n\t\t\tevent.preventDefault();\n\t\t\tcbs.onSelect?.(task.id);\n\t\t}\n\t});\n\tconst labelEl = el('span');\n\tcss(labelEl, {\n\t\tposition: 'absolute',\n\t\tleft: '50%',\n\t\ttop: '110%',\n\t\ttransform: 'translate(-50%, 0) rotate(-45deg)',\n\t\tfontSize: 'var(--gantt-font-size-xs)',\n\t\tfontWeight: 'var(--gantt-font-weight-semibold)',\n\t\tcolor: 'var(--gantt-milestone)',\n\t\twhiteSpace: 'nowrap',\n\t\tpointerEvents: 'none',\n\t});\n\tlabelEl.textContent = task.text;\n\tdiamond.append(labelEl);\n\n\tlayer.insertBefore(diamond, svgLayer);\n\tbindMilestoneTask(diamond, task);\n\n\t// Milestones have no resize handle — use a dummy div for the registry interface\n\tconst dummy = el('div');\n\tconst cleanupDrag = attachMilestoneClick(diamond, task.id, cbs);\n\n\t// Link-creation endpoint handle for milestones (single handle at center)\n\tlet cleanupLinkHandles: (() => void) | undefined;\n\tif (state.linkCreationEnabled) {\n\t\tconst diamondCenterY = layout.y + layout.height / 2;\n\t\tconst linkHandle = createEndpointHandle();\n\t\tlinkHandle.style.left = `${layout.x}px`;\n\t\tlinkHandle.style.top = `${diamondCenterY}px`;\n\t\tlinkHandle.style.background = 'var(--gantt-milestone)';\n\t\tlayer.insertBefore(linkHandle, svgLayer);\n\n\t\tconst cleanupLink = attachLinkEndpointHandle(linkHandle, task.id, layout.x, diamondCenterY, svgLayer, layer, cbs);\n\n\t\tconst onDiamondEnter = (): void => {\n\t\t\tlinkHandle.style.opacity = '1';\n\t\t\tlinkHandle.style.transform = 'translate(-50%, -50%) scale(1)';\n\t\t};\n\t\tconst onDiamondLeave = (): void => {\n\t\t\tlinkHandle.style.opacity = '0';\n\t\t\tlinkHandle.style.transform = 'translate(-50%, -50%) scale(0.8)';\n\t\t};\n\t\tdiamond.addEventListener('mouseenter', onDiamondEnter);\n\t\tdiamond.addEventListener('mouseleave', onDiamondLeave);\n\n\t\tcleanupLinkHandles = (): void => {\n\t\t\tcleanupLink();\n\t\t\tdiamond.removeEventListener('mouseenter', onDiamondEnter);\n\t\t\tdiamond.removeEventListener('mouseleave', onDiamondLeave);\n\t\t};\n\t}\n\n\tconst entry: {\n\t\tbar: HTMLElement;\n\t\tresizeHandle: HTMLElement;\n\t\tcleanupDrag: () => void;\n\t\tcleanupLinkHandles?: () => void;\n\t} = {bar: diamond, resizeHandle: dummy, cleanupDrag};\n\tif (cleanupLinkHandles !== undefined) {\n\t\tentry.cleanupLinkHandles = cleanupLinkHandles;\n\t}\n\tregistry.set(task.id, entry);\n}\n\nfunction ariaLabel(locale: ChartLocale, key: 'aria_task' | 'aria_milestone', arg: string): string {\n\tconst template = locale.labels?.[key] ?? EN_US_LABELS[key];\n\treturn formatLabel(template, arg);\n}\n","import {SpecialDaySchema, type GanttInput, type SpecialDay} from '../validation/schemas.ts';\nimport {parseDate} from '../domain/dateMath.ts';\nimport {buildTaskTree} from '../domain/tree.ts';\nimport {type ResolvedSpecialDay} from './state.ts';\n\nexport function buildTaskIndex(tasks: GanttInput['tasks']): Map<number, number> {\n\tconst index = new Map<number, number>();\n\tfor (let i = 0; i < tasks.length; i++) {\n\t\tconst task = tasks[i];\n\t\tif (task !== undefined) {\n\t\t\tindex.set(task.id, i);\n\t\t}\n\t}\n\treturn index;\n}\n\nexport function buildSpecialDayIndex(specialDays: SpecialDay[]): Map<string, ResolvedSpecialDay> {\n\tconst map = new Map<string, ResolvedSpecialDay>();\n\tfor (const specialDay of specialDays) {\n\t\tconst parsed = SpecialDaySchema.parse(specialDay);\n\t\tconst isoDate = toIsoDate(parseDate(parsed.date));\n\t\tmap.set(isoDate, {\n\t\t\tkind: parsed.kind,\n\t\t\t...(parsed.label === undefined ? {} : {label: parsed.label}),\n\t\t\t...(parsed.className === undefined ? {} : {className: parsed.className}),\n\t\t});\n\t}\n\treturn map;\n}\n\nexport function toIsoDate(date: Date): string {\n\treturn date.toISOString().slice(0, 10);\n}\n\nexport function normalizeWeekendDays(days: number[] | undefined): Set<number> {\n\tif (days === undefined) {\n\t\treturn new Set([0, 6]);\n\t}\n\n\tconst normalized = new Set<number>();\n\tfor (const day of days) {\n\t\tif (!Number.isInteger(day) || day < 0 || day > 6) {\n\t\t\tthrow new Error('weekendDays must contain integers in range 0..6');\n\t\t}\n\t\tnormalized.add(day);\n\t}\n\treturn normalized;\n}\n\nexport function getExpandableTaskIds(tasks: GanttInput['tasks']): Set<number> {\n\tconst roots = buildTaskTree(tasks);\n\tconst expandableIds = new Set<number>();\n\tconst stack = [...roots];\n\twhile (stack.length > 0) {\n\t\tconst node = stack.pop();\n\t\tif (node === undefined) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (node.children.length > 0) {\n\t\t\texpandableIds.add(node.id);\n\t\t}\n\t\tfor (const child of node.children) {\n\t\t\tstack.push(child);\n\t\t}\n\t}\n\treturn expandableIds;\n}\n\nexport function getInitialExpandedIds(tasks: GanttInput['tasks']): Set<number> {\n\tconst expandableIds = getExpandableTaskIds(tasks);\n\tconst expandedIds = new Set<number>();\n\tfor (const task of tasks) {\n\t\tif (task.open && expandableIds.has(task.id)) {\n\t\t\texpandedIds.add(task.id);\n\t\t}\n\t}\n\treturn expandedIds;\n}\n","const MIN_PANE_WIDTH = 96;\n\nexport function attachSplitter(\n\tsplitterHandle: HTMLElement,\n\tleftPane: HTMLElement,\n\tcontainer: HTMLElement,\n\ttimelineMinWidth: number,\n\tonDragEnd: (width: number) => void,\n): void {\n\tsplitterHandle.addEventListener('pointerdown', (e: PointerEvent) => {\n\t\tif (e.button !== 0) {\n\t\t\treturn;\n\t\t}\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\n\t\tconst startX = e.clientX;\n\t\tconst startWidth = Number.parseFloat(leftPane.style.width) || 0;\n\n\t\tfunction onMove(me: PointerEvent): void {\n\t\t\tconst dx = me.clientX - startX;\n\t\t\tlet newWidth = startWidth + dx;\n\t\t\tconst hostWidth = container.clientWidth;\n\t\t\tif (hostWidth > 0) {\n\t\t\t\tnewWidth = Math.max(MIN_PANE_WIDTH, Math.min(newWidth, hostWidth - timelineMinWidth));\n\t\t\t}\n\t\t\tnewWidth = Math.max(MIN_PANE_WIDTH, newWidth);\n\t\t\tleftPane.style.width = `${newWidth}px`;\n\t\t\tleftPane.style.minWidth = `${newWidth}px`;\n\t\t\tleftPane.style.maxWidth = `${newWidth}px`;\n\t\t}\n\n\t\tfunction onUp(): void {\n\t\t\twindow.removeEventListener('pointermove', onMove);\n\t\t\twindow.removeEventListener('pointerup', onUp);\n\t\t\tconst finalWidth = Number.parseFloat(leftPane.style.width);\n\t\t\tonDragEnd(finalWidth);\n\t\t}\n\n\t\twindow.addEventListener('pointermove', onMove);\n\t\twindow.addEventListener('pointerup', onUp);\n\t});\n}\n","export const MOBILE_BREAKPOINT = 768;\nexport const MOBILE_LEFT_PANE_MIN_WIDTH = 140;\nexport const MOBILE_LEFT_PANE_MAX_RATIO = 0.45;\nexport const TIMELINE_MIN_WIDTH = 220;\nexport const DESKTOP_MIN_RATIO = 0.25;\nexport const DESKTOP_MAX_RATIO = 0.4;\nconst MIN_PANE_WIDTH = 96;\n\nexport type ComputeLeftPaneWidthOptions = {\n\thostWidth: number;\n\tdefaultWidth: number;\n\tuserSplitWidth: number | null;\n\texplicitOptWidth: number | undefined;\n\tresponsiveSplitPane: boolean;\n\tmobileBreakpoint: number;\n\tmobileLeftPaneMinWidth: number;\n\tmobileLeftPaneMaxRatio: number;\n\ttimelineMinWidth: number;\n};\n\nexport function computeLeftPaneWidth(options: ComputeLeftPaneWidthOptions): number {\n\tconst {\n\t\thostWidth,\n\t\tdefaultWidth,\n\t\tuserSplitWidth,\n\t\texplicitOptWidth,\n\t\tresponsiveSplitPane,\n\t\tmobileBreakpoint,\n\t\tmobileLeftPaneMinWidth,\n\t\tmobileLeftPaneMaxRatio,\n\t\ttimelineMinWidth,\n\t} = options;\n\n\tlet width = defaultWidth;\n\n\tif (hostWidth <= 0) {\n\t\treturn width;\n\t}\n\n\tif (userSplitWidth !== null) {\n\t\twidth = userSplitWidth;\n\t} else if (explicitOptWidth !== undefined) {\n\t\twidth = explicitOptWidth;\n\t} else if (responsiveSplitPane && hostWidth <= mobileBreakpoint) {\n\t\tconst ratioWidth = Math.floor(hostWidth * mobileLeftPaneMaxRatio);\n\t\twidth = Math.min(defaultWidth, Math.max(mobileLeftPaneMinWidth, ratioWidth));\n\t} else {\n\t\tconst minProportional = Math.floor(hostWidth * DESKTOP_MIN_RATIO);\n\t\tconst maxProportional = Math.floor(hostWidth * DESKTOP_MAX_RATIO);\n\t\twidth = Math.min(maxProportional, Math.max(defaultWidth, minProportional));\n\t}\n\n\tconst maxAllowed = Math.max(MIN_PANE_WIDTH, hostWidth - timelineMinWidth);\n\twidth = Math.min(width, maxAllowed);\n\n\treturn Math.max(MIN_PANE_WIDTH, Math.floor(width));\n}\n","import {type GanttInput, type SpecialDay, type Task} from '../validation/schemas.ts';\nimport {validateLinkRefs, detectCycles} from '../domain/dependencies.ts';\nimport {buildTaskTree, flattenTree} from '../domain/tree.ts';\nimport {createPixelMapper} from '../timeline/pixelMapper.ts';\nimport {computeLayout, deriveViewport, ROW_HEIGHT} from '../timeline/layoutEngine.ts';\nimport {routeLinks} from '../rendering/linkRouter.ts';\nimport {type TimeScale} from '../timeline/scale.ts';\nimport {type GanttState, type ResolvedSpecialDay} from './state.ts';\nimport {el, css, clearChildren} from './dom/helpers.ts';\nimport {renderTimeHeader} from './dom/timeHeader.ts';\nimport {renderLeftPane, buildLeftPaneHeader, setupColumnResize} from './dom/leftPane.ts';\nimport {createRightPaneRefs, renderRightPane} from './dom/rightPane.ts';\nimport {type RightPaneRefs} from './dom/rightPane.ts';\nimport {type GridColumn, gridNaturalWidth, gridColumnDefaults} from './dom/gridColumns.ts';\nimport {GanttError} from '../errors.ts';\nimport {buildTaskIndex, buildSpecialDayIndex, normalizeWeekendDays, getExpandableTaskIds, getInitialExpandedIds} from './utils.ts';\nimport {attachSplitter} from './splitter.ts';\nimport {computeLeftPaneWidth, MOBILE_BREAKPOINT, MOBILE_LEFT_PANE_MIN_WIDTH, MOBILE_LEFT_PANE_MAX_RATIO, TIMELINE_MIN_WIDTH} from './responsive.ts';\nimport {type ChartLocale, resolveChartLocale} from '../locale.ts';\n\nexport type OnTaskSelect = (taskId: number | null) => void;\nexport type OnTaskMove = (payload: {id: number; startDate: Date}) => void;\nexport type OnTaskResize = (payload: {id: number; duration: number}) => void;\nexport type OnTaskAdd = (payload: {parentId: number}) => void;\nexport type OnTaskDoubleClick = (payload: {id: number; source: 'grid' | 'bar' | 'milestone'}) => void;\nexport type OnTaskEditIntent = (payload: {id: number; source: 'grid' | 'bar' | 'milestone'; trigger: 'double_click'; task: Task}) => void;\nexport type OnLinkCreate = (payload: {sourceTaskId: number; targetTaskId: number; type: 'FS'}) => void;\n\nexport type GanttCallbacks = {\n\tonSelect?: OnTaskSelect;\n\tonMove?: OnTaskMove;\n\tonResize?: OnTaskResize;\n\tonAdd?: OnTaskAdd;\n\tonTaskDoubleClick?: OnTaskDoubleClick;\n\tonTaskEditIntent?: OnTaskEditIntent;\n\tonLinkCreate?: OnLinkCreate;\n\tonLeftPaneWidthChange?: (width: number) => void;\n\tonGridColumnsChange?: (columns: GridColumn[]) => void;\n};\n\nexport type ThemeMode = 'light' | 'dark' | 'system';\n\nexport type GanttOptions = {\n\tscale?: TimeScale;\n\thighlightLinkedDependenciesOnSelect?: boolean;\n\tlinkCreationEnabled?: boolean;\n\tleftPaneWidth?: number;\n\tresponsiveSplitPane?: boolean;\n\tmobileBreakpoint?: number;\n\tmobileLeftPaneMinWidth?: number;\n\tmobileLeftPaneMaxRatio?: number;\n\ttimelineMinWidth?: number;\n\theight?: number;\n\tviewportStart?: Date;\n\tviewportEnd?: Date;\n\tlocale?: ChartLocale | string;\n\tshowWeekends?: boolean;\n\tweekendDays?: number[];\n\tspecialDays?: SpecialDay[];\n\tgridColumns?: GridColumn[];\n\ttheme?: ThemeMode;\n} & GanttCallbacks;\n\nexport type GanttInstance = {\n\tupdate: (input: GanttInput) => void;\n\tsetScale: (scale: TimeScale) => void;\n\tselect: (id: number | null) => void;\n\tcollapseAll: () => void;\n\texpandAll: () => void;\n\tdestroy: () => void;\n};\n\nconst HEADER_H = 52;\nconst OVERSCAN = 4;\nexport class GanttChart implements GanttInstance {\n\treadonly #container: HTMLElement;\n\treadonly #opts: GanttOptions;\n\t#input: GanttInput;\n\t#scale: TimeScale;\n\t#selectedId: number | null = null;\n\t#scrollTop = 0;\n\t#rafPending = false;\n\t#rafId: number | null = null;\n\t#destroyed = false;\n\t#taskIndex: Map<number, number>;\n\t#lastGridClick: {id: number; atMs: number} | null = null;\n\t#userSplitWidth: number | null = null;\n\n\treadonly #height: number;\n\treadonly #locale: ChartLocale;\n\treadonly #timelineMinWidth: number;\n\treadonly #columns: GridColumn[];\n\treadonly #leftPaneDefaultWidth: number;\n\treadonly #weekendDays: Set<number>;\n\treadonly #specialDaysByDate: Map<string, ResolvedSpecialDay>;\n\treadonly #expandedIds: Set<number>;\n\n\t// oxlint-disable typescript-eslint(prefer-readonly)\n\t#root!: HTMLElement;\n\t#scrollEl!: HTMLElement;\n\t#leftPane!: HTMLElement;\n\t#leftBody!: HTMLElement;\n\t#rightPane!: HTMLElement;\n\t#rightHeader!: HTMLElement;\n\t#rightPaneRefs!: RightPaneRefs;\n\treadonly #cbs: GanttCallbacks;\n\n\t#resizeObserver: ResizeObserver | null = null;\n\t#columnResizeCleanup!: () => void;\n\n\tpublic constructor(container: HTMLElement, input: GanttInput, opts: GanttOptions = {}) {\n\t\tthis.#container = container;\n\n\t\tvalidateLinkRefs(input.tasks, input.links);\n\t\tdetectCycles(input.tasks, input.links);\n\n\t\tthis.#input = input;\n\t\tthis.#scale = opts.scale ?? 'day';\n\t\tthis.#opts = opts;\n\t\tthis.#taskIndex = buildTaskIndex(input.tasks);\n\t\tthis.#locale = resolveChartLocale(opts.locale);\n\t\tthis.#columns = opts.gridColumns ?? gridColumnDefaults(this.#locale);\n\t\tthis.#leftPaneDefaultWidth = opts.leftPaneWidth ?? gridNaturalWidth(this.#columns);\n\t\tthis.#height = opts.height ?? 500;\n\t\tthis.#timelineMinWidth = opts.timelineMinWidth ?? TIMELINE_MIN_WIDTH;\n\t\tthis.#weekendDays = normalizeWeekendDays(opts.weekendDays ?? this.#locale.weekendDays);\n\t\tthis.#specialDaysByDate = buildSpecialDayIndex(opts.specialDays ?? []);\n\t\tthis.#expandedIds = getInitialExpandedIds(input.tasks);\n\n\t\tthis.#cbs = {\n\t\t\tonSelect: (id): void => {\n\t\t\t\tif (this.#selectedId === id) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.#selectedId = id;\n\t\t\t\topts.onSelect?.(this.#selectedId);\n\t\t\t\tthis.#scheduleRender();\n\t\t\t},\n\t\t\tonTaskDoubleClick: (payload): void => {\n\t\t\t\topts.onTaskDoubleClick?.(payload);\n\t\t\t},\n\t\t\tonTaskEditIntent: (payload): void => {\n\t\t\t\topts.onTaskEditIntent?.(payload);\n\t\t\t\topts.onTaskDoubleClick?.({id: payload.id, source: payload.source});\n\t\t\t},\n\t\t\tonMove: (payload): void => {\n\t\t\t\tconst iso = payload.startDate.toISOString().slice(0, 10);\n\t\t\t\tthis.#patchTask(payload.id, {start_date: iso});\n\t\t\t\topts.onMove?.(payload);\n\t\t\t\tthis.#scheduleRender();\n\t\t\t},\n\t\t\tonResize: (payload): void => {\n\t\t\t\tthis.#patchTask(payload.id, {duration: payload.duration});\n\t\t\t\topts.onResize?.(payload);\n\t\t\t\tthis.#scheduleRender();\n\t\t\t},\n\t\t\tonLeftPaneWidthChange: (width): void => {\n\t\t\t\topts.onLeftPaneWidthChange?.(width);\n\t\t\t},\n\t\t\tonGridColumnsChange: (updatedColumns): void => {\n\t\t\t\topts.onGridColumnsChange?.(updatedColumns);\n\t\t\t},\n\t\t\tonLinkCreate: (payload): void => {\n\t\t\t\topts.onLinkCreate?.(payload);\n\t\t\t},\n\t\t};\n\n\t\tthis.#buildDom();\n\t\tthis.#wireEvents();\n\n\t\tcontainer.append(this.#root);\n\n\t\tthis.#applyTheme();\n\n\t\tthis.#applyResponsivePaneStyles();\n\t\tthis.#setupResizeObserver();\n\n\t\tthis.#render();\n\t}\n\n\tpublic update(newInput: GanttInput): void {\n\t\tthis.#assertAlive();\n\t\tvalidateLinkRefs(newInput.tasks, newInput.links);\n\t\tdetectCycles(newInput.tasks, newInput.links);\n\t\tthis.#input = newInput;\n\t\tthis.#taskIndex = buildTaskIndex(newInput.tasks);\n\t\tthis.#scheduleRender();\n\t}\n\n\tpublic setScale(scale: TimeScale): void {\n\t\tthis.#assertAlive();\n\t\tthis.#scale = scale;\n\t\tthis.#scheduleRender();\n\t}\n\n\tpublic select(id: number | null): void {\n\t\tthis.#assertAlive();\n\t\tthis.#selectedId = id;\n\t\tthis.#opts.onSelect?.(id);\n\t\tif (this.#rafPending && this.#rafId !== null) {\n\t\t\tcancelAnimationFrame(this.#rafId);\n\t\t\tthis.#rafId = null;\n\t\t\tthis.#rafPending = false;\n\t\t}\n\t\tthis.#render();\n\t}\n\n\tpublic collapseAll(): void {\n\t\tthis.#assertAlive();\n\t\tthis.#expandedIds.clear();\n\t\tif (this.#rafPending && this.#rafId !== null) {\n\t\t\tcancelAnimationFrame(this.#rafId);\n\t\t\tthis.#rafId = null;\n\t\t\tthis.#rafPending = false;\n\t\t}\n\t\tthis.#render();\n\t}\n\n\tpublic expandAll(): void {\n\t\tthis.#assertAlive();\n\t\tthis.#expandedIds.clear();\n\t\tfor (const id of getExpandableTaskIds(this.#input.tasks)) {\n\t\t\tthis.#expandedIds.add(id);\n\t\t}\n\t\tif (this.#rafPending && this.#rafId !== null) {\n\t\t\tcancelAnimationFrame(this.#rafId);\n\t\t\tthis.#rafId = null;\n\t\t\tthis.#rafPending = false;\n\t\t}\n\t\tthis.#render();\n\t}\n\n\tpublic destroy(): void {\n\t\tif (this.#destroyed) {\n\t\t\treturn;\n\t\t}\n\t\tthis.#destroyed = true;\n\t\tthis.#scrollEl.removeEventListener('scroll', this.#onScroll);\n\t\tif (this.#resizeObserver !== null) {\n\t\t\tthis.#resizeObserver.disconnect();\n\t\t} else {\n\t\t\twindow.removeEventListener('resize', this.#applyResponsivePaneStyles);\n\t\t}\n\t\tif (this.#rafId !== null) {\n\t\t\tcancelAnimationFrame(this.#rafId);\n\t\t}\n\t\tthis.#columnResizeCleanup();\n\t\tfor (const {cleanupDrag, cleanupLinkHandles} of this.#rightPaneRefs.barRegistry.values()) {\n\t\t\tcleanupDrag();\n\t\t\tcleanupLinkHandles?.();\n\t\t}\n\t\tclearChildren(this.#container);\n\t}\n\n\t#patchTask(id: number, patch: Partial<GanttInput['tasks'][number]>): void {\n\t\tconst index = this.#taskIndex.get(id);\n\t\tif (index === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tconst target = this.#input.tasks[index];\n\t\tif (target === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tthis.#input.tasks[index] = {...target, ...patch};\n\t}\n\n\treadonly #handleGridClick = (payload: {id: number; task: Task}): void => {\n\t\tconst now = Date.now();\n\t\tconst prev = this.#lastGridClick;\n\t\tif (prev !== null && prev.id === payload.id && now - prev.atMs <= 350) {\n\t\t\tthis.#lastGridClick = null;\n\t\t\tthis.#cbs.onTaskEditIntent?.({id: payload.id, source: 'grid', trigger: 'double_click', task: payload.task});\n\t\t\treturn;\n\t\t}\n\t\tthis.#lastGridClick = {id: payload.id, atMs: now};\n\t\tthis.#cbs.onSelect?.(payload.id);\n\t};\n\n\treadonly #onScroll = (): void => {\n\t\t({scrollTop: this.#scrollTop} = this.#scrollEl);\n\t\tthis.#scheduleRender();\n\t};\n\n\treadonly #applyResponsivePaneStyles = (): void => {\n\t\tconst computedWidth = computeLeftPaneWidth({\n\t\t\thostWidth: Math.max(0, this.#container.clientWidth),\n\t\t\tdefaultWidth: this.#leftPaneDefaultWidth,\n\t\t\tuserSplitWidth: this.#userSplitWidth,\n\t\t\texplicitOptWidth: this.#opts.leftPaneWidth,\n\t\t\tresponsiveSplitPane: this.#opts.responsiveSplitPane ?? true,\n\t\t\tmobileBreakpoint: this.#opts.mobileBreakpoint ?? MOBILE_BREAKPOINT,\n\t\t\tmobileLeftPaneMinWidth: this.#opts.mobileLeftPaneMinWidth ?? MOBILE_LEFT_PANE_MIN_WIDTH,\n\t\t\tmobileLeftPaneMaxRatio: this.#opts.mobileLeftPaneMaxRatio ?? MOBILE_LEFT_PANE_MAX_RATIO,\n\t\t\ttimelineMinWidth: this.#timelineMinWidth,\n\t\t});\n\t\tthis.#leftPane.style.width = `${computedWidth}px`;\n\t\tthis.#leftPane.style.minWidth = `${computedWidth}px`;\n\t\tthis.#leftPane.style.maxWidth = `${computedWidth}px`;\n\t\tthis.#rightPane.style.minWidth = `${this.#timelineMinWidth}px`;\n\t};\n\n\t#computeState(): GanttState {\n\t\tconst roots = buildTaskTree(this.#input.tasks);\n\t\tconst allRows = flattenTree(roots, this.#expandedIds);\n\t\tconst [vpStart, vpEnd] =\n\t\t\tthis.#opts.viewportStart !== undefined && this.#opts.viewportEnd !== undefined\n\t\t\t\t? [this.#opts.viewportStart, this.#opts.viewportEnd]\n\t\t\t\t: deriveViewport(allRows, 2);\n\n\t\tconst mapper = createPixelMapper(this.#scale, vpStart);\n\t\tconst totalWidth = Math.ceil(mapper.toX(vpEnd)) + 1;\n\t\tconst layouts = computeLayout(allRows, mapper);\n\t\tconst links = routeLinks(this.#input.links, layouts);\n\n\t\tconst containerH = this.#height - HEADER_H;\n\t\tconst rowCount = allRows.length;\n\t\tconst startIndex = Math.max(0, Math.floor(this.#scrollTop / ROW_HEIGHT) - OVERSCAN);\n\t\tconst endIndex = Math.min(rowCount - 1, Math.ceil((this.#scrollTop + containerH) / ROW_HEIGHT) + OVERSCAN - 1);\n\t\tconst paddingTop = startIndex * ROW_HEIGHT;\n\t\tconst paddingBottom = Math.max(0, (rowCount - 1 - endIndex) * ROW_HEIGHT);\n\n\t\treturn {\n\t\t\tinput: this.#input,\n\t\t\tscale: this.#scale,\n\t\t\thighlightLinkedDependenciesOnSelect: this.#opts.highlightLinkedDependenciesOnSelect ?? false,\n\t\t\tlinkCreationEnabled: this.#opts.linkCreationEnabled ?? false,\n\t\t\texpandedIds: this.#expandedIds,\n\t\t\tselectedId: this.#selectedId,\n\t\t\tscrollTop: this.#scrollTop,\n\t\t\tallRows,\n\t\t\tmapper,\n\t\t\tviewportStart: vpStart,\n\t\t\tviewportEnd: vpEnd,\n\t\t\ttotalWidth,\n\t\t\tlayouts,\n\t\t\tlinks,\n\t\t\tstartIndex,\n\t\t\tendIndex,\n\t\t\tpaddingTop,\n\t\t\tpaddingBottom,\n\t\t\tshowWeekends: this.#opts.showWeekends ?? true,\n\t\t\tweekendDays: this.#weekendDays,\n\t\t\tspecialDaysByDate: this.#specialDaysByDate,\n\t\t\tlocale: this.#locale,\n\t\t};\n\t}\n\n\treadonly #render = (): void => {\n\t\tthis.#rafPending = false;\n\t\tconst state = this.#computeState();\n\n\t\trenderTimeHeader(this.#rightHeader, state);\n\t\trenderLeftPane(\n\t\t\tthis.#leftBody,\n\t\t\tstate,\n\t\t\t{\n\t\t\t\tonToggle: (id) => {\n\t\t\t\t\tif (this.#expandedIds.has(id)) {\n\t\t\t\t\t\tthis.#expandedIds.delete(id);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.#expandedIds.add(id);\n\t\t\t\t\t}\n\t\t\t\t\tthis.#scheduleRender();\n\t\t\t\t},\n\t\t\t\tonSelect: (id) => this.#cbs.onSelect?.(id),\n\t\t\t\tonRowClick: (payload) => {\n\t\t\t\t\tthis.#handleGridClick(payload);\n\t\t\t\t},\n\t\t\t\tonTaskEditIntent: (payload) => this.#cbs.onTaskEditIntent?.(payload),\n\t\t\t\tonAdd: (id) => this.#cbs.onAdd?.({parentId: id}),\n\t\t\t},\n\t\t\tthis.#columns,\n\t\t);\n\t\trenderRightPane(this.#rightPaneRefs, state, this.#cbs);\n\t};\n\n\t#scheduleRender(): void {\n\t\tif (this.#rafPending || this.#destroyed) {\n\t\t\treturn;\n\t\t}\n\t\tthis.#rafPending = true;\n\t\tthis.#rafId = requestAnimationFrame(this.#render);\n\t}\n\n\t#applyTheme(): void {\n\t\tconst theme = this.#opts.theme ?? 'system';\n\t\tthis.#container.dataset['theme'] = theme;\n\t}\n\n\t#assertAlive(): void {\n\t\tif (this.#destroyed) {\n\t\t\tthrow new GanttError('INSTANCE_DESTROYED', 'Gantt instance was destroyed');\n\t\t}\n\t}\n\n\t#buildDom(): void {\n\t\tconst root = el('div');\n\t\troot.className = 'gantt-root';\n\t\tcss(root, {\n\t\t\theight: `${this.#height}px`,\n\t\t\toverflow: 'hidden',\n\t\t\tdisplay: 'flex',\n\t\t\tflexDirection: 'column',\n\t\t\tfontFamily: 'var(--gantt-font)',\n\t\t\tbackground: 'var(--gantt-bg)',\n\t\t});\n\t\tthis.#root = root;\n\n\t\tconst scrollEl = el('div');\n\t\tcss(scrollEl, {flex: '1', overflow: 'auto', position: 'relative', display: 'flex'});\n\t\troot.append(scrollEl);\n\t\tthis.#scrollEl = scrollEl;\n\n\t\tconst leftPane = el('div');\n\t\tleftPane.dataset['pane'] = 'left';\n\t\tcss(leftPane, {\n\t\t\twidth: `${this.#leftPaneDefaultWidth}px`,\n\t\t\tflexShrink: '0',\n\t\t\tposition: 'sticky',\n\t\t\tleft: '0',\n\t\t\tzIndex: '10',\n\t\t\tbackground: 'var(--gantt-bg)',\n\t\t\tborderRight: '1px solid var(--gantt-border)',\n\t\t});\n\t\tthis.#leftPane = leftPane;\n\n\t\tconst leftHeader = el('div');\n\t\tcss(leftHeader, {position: 'sticky', top: '0', zIndex: '11', background: 'var(--gantt-header-bg)'});\n\t\tconst headerEl = buildLeftPaneHeader(this.#columns);\n\t\tleftHeader.append(headerEl);\n\t\tleftPane.append(leftHeader);\n\n\t\tconst leftBody = el('div');\n\t\tleftPane.append(leftBody);\n\t\tthis.#leftBody = leftBody;\n\n\t\tthis.#columnResizeCleanup = setupColumnResize(headerEl, leftBody, this.#columns, (updated) => {\n\t\t\tthis.#cbs.onGridColumnsChange?.(updated);\n\t\t});\n\n\t\tscrollEl.append(leftPane);\n\n\t\tconst rightPane = el('div');\n\t\trightPane.dataset['pane'] = 'right';\n\t\tcss(rightPane, {flexShrink: '0', position: 'relative', minWidth: `${this.#timelineMinWidth}px`});\n\t\tthis.#rightPane = rightPane;\n\n\t\tconst rightHeader = el('div');\n\t\tcss(rightHeader, {position: 'sticky', top: '0', zIndex: '9', background: 'var(--gantt-header-bg)'});\n\t\trightPane.append(rightHeader);\n\t\tthis.#rightHeader = rightHeader;\n\n\t\tthis.#rightPaneRefs = createRightPaneRefs();\n\t\trightPane.append(this.#rightPaneRefs.scrollContainer);\n\t\tscrollEl.append(rightPane);\n\n\t\tconst splitterHandle = el('div');\n\t\tsplitterHandle.className = 'gantt-splitter-handle';\n\t\tcss(splitterHandle, {\n\t\t\tposition: 'absolute',\n\t\t\tright: '0',\n\t\t\ttop: '0',\n\t\t\tbottom: '0',\n\t\t\twidth: '4px',\n\t\t\tcursor: 'col-resize',\n\t\t\tzIndex: '20',\n\t\t});\n\t\tleftPane.append(splitterHandle);\n\n\t\tattachSplitter(splitterHandle, leftPane, this.#container, this.#timelineMinWidth, (finalWidth) => {\n\t\t\tthis.#userSplitWidth = finalWidth;\n\t\t\tthis.#cbs.onLeftPaneWidthChange?.(finalWidth);\n\t\t});\n\t}\n\n\t#wireEvents(): void {\n\t\tthis.#rightPaneRefs.absoluteLayer.addEventListener('click', (event) => {\n\t\t\tconst target = event.target as HTMLElement;\n\t\t\tif (target.closest('.gantt-bar, .gantt-milestone, .gantt-resize-handle')) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.#cbs.onSelect?.(null);\n\t\t});\n\n\t\tthis.#root.addEventListener('keydown', (event) => {\n\t\t\tif (event.key === 'Escape' && this.#selectedId !== null) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.#cbs.onSelect?.(null);\n\t\t\t}\n\t\t});\n\n\t\tthis.#scrollEl.addEventListener('scroll', this.#onScroll);\n\t}\n\n\t#setupResizeObserver(): void {\n\t\tif (typeof ResizeObserver !== 'undefined') {\n\t\t\tthis.#resizeObserver = new ResizeObserver(() => {\n\t\t\t\tthis.#applyResponsivePaneStyles();\n\t\t\t});\n\t\t\tthis.#resizeObserver.observe(this.#container);\n\t\t} else {\n\t\t\twindow.addEventListener('resize', this.#applyResponsivePaneStyles);\n\t\t}\n\t}\n}\n"],"mappings":";;AAEA,MAAa,iBAAiB,EAAE,KAAK;CAAC;CAAM;CAAM;CAAM;CAAK,CAAC;AAC9D,MAAa,iBAAiB,EAAE,KAAK;CAAC;CAAQ;CAAW;CAAY,CAAC;AACtE,MAAa,uBAAuB,EAAE,KAAK,CAAC,WAAW,SAAS,CAAC;AAEjE,MAAa,mBAAmB,EAAE,OAAO;;CAExC,MAAM,EAAE,QAAQ,CAAC,MAAM,uBAAuB,sBAAsB;CACpE,MAAM;CACN,OAAO,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,UAAU;CACnC,WAAW,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,UAAU;CACvC,CAAC;AAEF,MAAa,aAAa,EAAE,OAAO;CAClC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;CAC/B,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE;;CAEvB,YAAY,EAAE,QAAQ,CAAC,MAAM,uBAAuB,sBAAsB;;CAE1E,UAAU,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE;CACjC,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU;;CAE9C,UAAU,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;CAC7C,MAAM,eAAe,QAAQ,OAAO;;CAEpC,MAAM,EAAE,SAAS,CAAC,QAAQ,KAAK;CAC/B,OAAO,EAAE,QAAQ,CAAC,UAAU;CAC5B,CAAC;AAEF,MAAa,aAAa,EAAE,OAAO;CAClC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;CAC/B,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;CACnC,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU;CACnC,MAAM,eAAe,QAAQ,KAAK;CAClC,CAAC;AAEF,MAAa,mBAAmB,EAAE,OAAO;CACxC,OAAO,EAAE,MAAM,WAAW,CAAC,IAAI,EAAE;CACjC,OAAO,EAAE,MAAM,WAAW,CAAC,QAAQ,EAAE,CAAC;CACtC,CAAC;;AAWF,SAAgB,gBAAgB,KAA0B;AACzD,QAAO,iBAAiB,MAAM,IAAI;;;AAInC,SAAgB,oBAAoB,KAAiC;CACpE,MAAM,SAAS,iBAAiB,UAAU,IAAI;AAC9C,QAAO,OAAO,UAAU,OAAO,OAAO;;;;ACxDvC,IAAa,aAAb,cAAgC,MAAM;CACrC;CAEA,YAAmB,MAAsB,SAAiB;AACzD,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,OAAO;;;;;;;;;;ACMd,SAAgB,cAAc,OAA2B;CACxD,MAAM,sBAAM,IAAI,KAAuB;CACvC,MAAM,QAAoB,EAAE;AAG5B,MAAK,MAAM,QAAQ,MAClB,KAAI,IAAI,KAAK,IAAI;EAAC,GAAG;EAAM,UAAU,EAAE;EAAE,OAAO;EAAE,CAAC;AAIpD,MAAK,MAAM,QAAQ,OAAO;EACzB,MAAM,OAAO,IAAI,IAAI,KAAK,GAAG;AAC7B,MAAI,SAAS,KAAA,EACZ;AAED,MAAI,KAAK,WAAW,KAAA,GAAW;GAC9B,MAAM,SAAS,IAAI,IAAI,KAAK,OAAO;AACnC,OAAI,WAAW,KAAA,EACd,OAAM,IAAI,WAAW,oBAAoB,WAAW,KAAK,GAAG,qCAAqC,KAAK,SAAS;AAEhH,UAAO,SAAS,KAAK,KAAK;QAE1B,OAAM,KAAK,KAAK;;AAKlB,EAAC,SAAS,UAAU,OAAmB,GAAiB;AACvD,OAAK,MAAM,KAAK,OAAO;AACtB,KAAE,QAAQ;AACV,aAAU,EAAE,UAAU,IAAI,EAAE;;IAE3B,OAAO,EAAE;AAEZ,QAAO;;;;;;AAOR,SAAgB,YAAY,OAAmB,aAA8C;CAC5F,MAAM,OAAmB,EAAE;CAC3B,SAAS,KAAK,MAAsB;AACnC,OAAK,KAAK,KAAK;AACf,MAAI,KAAK,SAAS,SAAS,KAAK,YAAY,IAAI,KAAK,GAAG,CACvD,MAAK,MAAM,SAAS,KAAK,SACxB,MAAK,MAAM;;AAId,MAAK,MAAM,QAAQ,MAClB,MAAK,KAAK;AAEX,QAAO;;;AAIR,SAAgB,SAAS,MAAyB;AACjD,QAAO,KAAK,SAAS,SAAS;;;;;;;;;ACjE/B,SAAgB,aAAa,OAAe,OAAqB;CAEhE,MAAM,sBAAM,IAAI,KAAuB;AACvC,MAAK,MAAM,QAAQ,MAClB,KAAI,IAAI,KAAK,IAAI,EAAE,CAAC;AAErB,MAAK,MAAM,QAAQ,OAAO;EACzB,MAAM,YAAY,IAAI,IAAI,KAAK,OAAO;AACtC,MAAI,cAAc,KAAA,EACjB,WAAU,KAAK,KAAK,OAAO;;CAI7B,MAAM,QAAQ,GACb,OAAO,GACP,QAAQ;CACT,MAAM,wBAAQ,IAAI,KAAwB;CAC1C,MAAM,yBAAS,IAAI,KAAqB;AAExC,MAAK,MAAM,MAAM,IAAI,MAAM,CAC1B,OAAM,IAAI,IAAI,MAAM;CAGrB,MAAM,OAAO,MAAoB;AAChC,QAAM,IAAI,GAAG,KAAK;AAClB,OAAK,MAAM,KAAK,IAAI,IAAI,EAAE,IAAI,EAAE,EAAE;GACjC,MAAM,KAAK,MAAM,IAAI,EAAE,IAAI;AAC3B,OAAI,OAAO,MAAM;IAEhB,MAAM,OAAiB,CAAC,GAAG,EAAE;IAC7B,IAAI,MAAM;AACV,WAAO,QAAQ,GAAG;KACjB,MAAM,IAAI,OAAO,IAAI,IAAI;AACzB,SAAI,MAAM,KAAA,EACT;AAED,UAAK,KAAK,EAAE;AACZ,WAAM;;AAEP,UAAM,IAAI,WAAW,oBAAoB,iCAAiC,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,OAAO,GAAG;;AAE9G,OAAI,OAAO,OAAO;AACjB,WAAO,IAAI,GAAG,EAAE;AAChB,QAAI,EAAE;;;AAGR,QAAM,IAAI,GAAG,MAAM;;AAGpB,MAAK,MAAM,MAAM,IAAI,MAAM,CAC1B,MAAK,MAAM,IAAI,GAAG,IAAI,WAAW,MAChC,KAAI,GAAG;;;;;;AASV,SAAgB,iBAAiB,OAAe,OAAqB;CACpE,MAAM,MAAM,IAAI,IAAI,MAAM,KAAK,MAAM,EAAE,GAAG,CAAC;AAC3C,MAAK,MAAM,QAAQ,OAAO;AACzB,MAAI,CAAC,IAAI,IAAI,KAAK,OAAO,CACxB,OAAM,IAAI,WAAW,kBAAkB,WAAW,KAAK,GAAG,WAAW,KAAK,OAAO,YAAY;AAE9F,MAAI,CAAC,IAAI,IAAI,KAAK,OAAO,CACxB,OAAM,IAAI,WAAW,kBAAkB,WAAW,KAAK,GAAG,WAAW,KAAK,OAAO,YAAY;;;;;AC1DhG,MAAa,eAA+C;CAC3D,WAAW;CACX,gBAAgB;CAChB,mBAAmB;CACnB,kBAAkB;CAClB,mBAAmB;CACnB,iBAAiB;CACjB,gBAAgB;CAChB;AAED,MAAa,qBAAkC;CAC9C,MAAM;CACN,QAAQ;CACR,cAAc;CACd,eAAe;CACf,aAAa,CAAC,GAAG,EAAE;CACnB;;;;;AAMD,SAAgB,mBAAmB,KAAoD;AACtF,KAAI,QAAQ,KAAA,EACX,QAAO;AAER,KAAI,OAAO,QAAQ,UAAU;EAC5B,MAAM,SAAsB;GAC3B,MAAM,IAAI;GACV,cAAc,IAAI,gBAAgB,mBAAmB,IAAI,KAAK;GAC9D,eAAe,IAAI,iBAAiB,oBAAoB,IAAI,KAAK;GACjE,aAAa,IAAI,eAAe,kBAAkB,IAAI,KAAK;GAC3D;AACD,MAAI,IAAI,WAAW,KAAA,EAClB,QAAO,SAAS,IAAI;AAErB,SAAO;;CAER,MAAM,OAAO;AACb,QAAO;EACN;EACA,cAAc,mBAAmB,KAAK;EACtC,eAAe,oBAAoB,KAAK;EACxC,aAAa,kBAAkB,KAAK;EACpC;;AAMF,SAAS,eAAe,MAAsF;AAC7G,KAAI;AACH,MAAI,OAAO,SAAS,eAAe,OAAO,KAAK,WAAW,YAAY;GACrE,MAAM,SAAS,IAAI,KAAK,OAAO,KAAK;GACpC,MAAM,KAAK,OAAO;AAClB,OAAI,OAAO,OAAO,WACjB,QAAO,GAAG,KAAK,OAAO;;SAGjB;;;;;;;AAWT,SAAgB,mBAAmB,MAAyB;CAC3D,MAAM,UAAU,KAAK,MAAM,IAAI,CAAC,IAAI,aAAa,IAAI;CACrD,MAAM,SAAS,KAAK,MAAM,IAAI,CAAC,IAAI,aAAa;AAEhD,KAAI,WAAW,KAAA,GAAW;EACzB,MAAM,aAAa,kBAAkB;AACrC,MAAI,eAAe,KAAA,EAClB,QAAO;;CAGT,MAAM,WAAW,gBAAgB;AACjC,KAAI,aAAa,KAAA,EAChB,QAAO;CAGR,MAAM,OAAO,eAAe,KAAK;AACjC,KAAI,SAAS,KAAA,GAAW;EACvB,MAAM,MAAM,KAAK;AACjB,SAAQ,QAAQ,IAAI,IAAI;;AAGzB,QAAO;;AAGR,MAAM,oBAA+C;CACpD,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ;AAED,MAAM,kBAA6C;CAClD,IAAI;CACJ,IAAI;CACJ;;;;;AAMD,SAAgB,oBAAoB,MAAuC;CAC1E,MAAM,SAAS,KAAK,MAAM,IAAI,CAAC,IAAI,aAAa;AAChD,KAAI,WAAW,KAAA,GAAW;EACzB,MAAM,aAAa,sBAAsB;AACzC,MAAI,eAAe,KAAA,EAClB,QAAO;AAER,MAAI,UAAU,kBACb,QAAO;;CAIT,MAAM,OAAO,eAAe,KAAK;AACjC,KAAI,SAAS,KAAA,GAAW;AACvB,MAAI,KAAK,eAAe,KAAK,KAAK,aAAa,EAC9C,QAAO;AAER,SAAO;;AAGR,QAAO;;AAGR,MAAM,wBAAsD;CAC3D,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ;;;;;AAMD,SAAgB,kBAAkB,MAAwB;CACzD,MAAM,SAAS,KAAK,MAAM,IAAI,CAAC,IAAI,aAAa;AAChD,KAAI,WAAW,KAAA,GAAW;EACzB,MAAM,aAAa,eAAe;AAClC,MAAI,eAAe,KAAA,GAAW;GAC7B,MAAM,OAAO,CAAC,GAAG,WAAW;AAC5B,QAAK,MAAM,GAAG,MAAM,IAAI,EAAE;AAC1B,UAAO;;;CAIT,MAAM,OAAO,eAAe,KAAK;AACjC,KAAI,SAAS,KAAA,GAAW;EACvB,MAAM,OAAO,KAAK,QAAQ,KAAK,MAAe,MAAM,IAAI,IAAI,EAAG;AAC/D,OAAK,MAAM,GAAG,MAAM,IAAI,EAAE;AAC1B,SAAO;;AAGR,QAAO,CAAC,GAAG,EAAE;;AAGd,MAAM,iBAA2C;CAChD,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,EAAE;CACP,IAAI,CAAC,EAAE;CACP,IAAI,CAAC,EAAE;CACP,IAAI,CAAC,EAAE;CACP,IAAI,CAAC,GAAG,EAAE;CACV,IAAI,CAAC,EAAE;CACP,IAAI,CAAC,GAAG,EAAE;CACV;;;;;;;;AASD,SAAgB,iBAAiB,MAAY,QAAyC;AACrF,SAAQ,QAAR;EACC,KAAK,MACJ,QAAO,QAAQ,KAAK;EAErB,KAAK,KACJ,QAAO,OAAO,KAAK;EAEpB,KAAK,SACJ,QAAO,WAAW,KAAK;;;AAK1B,SAAS,QAAQ,MAAoB;CACpC,MAAM,IAAI,IAAI,KAAK,KAAK,IAAI,KAAK,gBAAgB,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,CAAC,CAAC;CAC1F,MAAM,SAAS,EAAE,WAAW,IAAI;AAChC,GAAE,WAAW,EAAE,YAAY,GAAG,IAAI,OAAO;CACzC,MAAM,YAAY,IAAI,KAAK,KAAK,IAAI,EAAE,gBAAgB,EAAE,GAAG,EAAE,CAAC;AAC9D,QAAO,KAAK,OAAO,EAAE,SAAS,GAAG,UAAU,SAAS,IAAI,QAAa,KAAK,EAAE;;AAG7E,SAAS,OAAO,MAAoB;CACnC,MAAM,IAAI,IAAI,KAAK,KAAK,IAAI,KAAK,gBAAgB,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,CAAC,CAAC;CAC1F,MAAM,YAAY,IAAI,KAAK,KAAK,IAAI,EAAE,gBAAgB,EAAE,GAAG,EAAE,CAAC;CAC9D,MAAM,YAAY,KAAK,OAAO,EAAE,SAAS,GAAG,UAAU,SAAS,IAAI,MAAW;CAC9E,MAAM,UAAU,UAAU,WAAW;CAErC,MAAM,qBADuB,YAAY,IAAI,IAAI,CAAC;AAElD,KAAI,YAAY,mBACf,QAAO;AAER,QAAO,KAAK,OAAO,YAAY,sBAAsB,EAAE,GAAG;;AAG3D,SAAS,WAAW,MAAoB;CACvC,MAAM,IAAI,IAAI,KAAK,KAAK,IAAI,KAAK,gBAAgB,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,CAAC,CAAC;CAC1F,MAAM,YAAY,IAAI,KAAK,KAAK,IAAI,EAAE,gBAAgB,EAAE,GAAG,EAAE,CAAC;CAC9D,MAAM,YAAY,KAAK,OAAO,EAAE,SAAS,GAAG,UAAU,SAAS,IAAI,MAAW;AAC9E,QAAO,KAAK,MAAM,YAAY,KAAK,EAAE;;;;;AAMtC,SAAgB,YAAY,UAAkB,KAAqB;AAClE,QAAO,SAAS,WAAW,OAAO,IAAI;;;;;ACpUvC,SAAgB,UAAU,SAAuB;CAChD,MAAM,oBAAI,IAAI,KAAK,GAAG,QAAQ,gBAAgB;AAC9C,KAAI,MAAM,EAAE,SAAS,CAAC,CACrB,OAAM,IAAI,MAAM,kBAAkB,QAAQ,GAAG;AAE9C,QAAO;;;AAIR,SAAgB,QAAQ,MAAY,MAAoB;AACvD,QAAO,IAAI,KAAK,KAAK,SAAS,GAAG,OAAO,MAAW;;;AAIpD,SAAgB,SAAS,GAAS,GAAiB;AAClD,SAAQ,EAAE,SAAS,GAAG,EAAE,SAAS,IAAI;;;AAItC,SAAgB,WAAW,MAAkB;AAC5C,QAAO,IAAI,KAAK,KAAK,IAAI,KAAK,gBAAgB,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,CAAC,CAAC;;;;;AAuCxF,SAAgB,kBAAkB,MAAY,OAAkB,QAA6B;CAC5F,MAAM,EAAC,MAAM,eAAe,gBAAgB,UAAS;AACrD,SAAQ,OAAR;EACC,KAAK,OACJ,QAAO,GAAG,OAAO,KAAK,aAAa,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC;EAEvD,KAAK,OAAO;GACX,MAAM,MAAM,KAAK,mBAAmB,MAAM;IAAC,SAAS;IAAS,UAAU;IAAM,CAAC;AAC9E,UAAO,GAAG,KAAK,YAAY,CAAC,GAAG;;EAEhC,KAAK,OAEJ,QAAO,IADI,iBAAiB,MAAM,cACrB;EAEd,KAAK,QACJ,QAAO,KAAK,mBAAmB,MAAM;GAAC,OAAO;GAAS,MAAM;GAAW,UAAU;GAAM,CAAC;EAEzF,KAAK,UACJ,QAAO,GAAG,oBAAoB,OAAO,GAAG,KAAK,MAAM,KAAK,aAAa,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,gBAAgB;EAExG,KAAK,OACJ,QAAO,GAAG,KAAK,gBAAgB;;;;;;;AASlC,SAAgB,iBAAiB,MAAY,OAAkB,QAA6B;CAC3F,MAAM,EAAC,SAAQ;AACf,SAAQ,OAAR;EACC,KAAK,OACJ,QAAO,KAAK,mBAAmB,MAAM;GAAC,OAAO;GAAQ,KAAK;GAAW,MAAM;GAAW,UAAU;GAAM,CAAC;EAExG,KAAK;EACL,KAAK,OACJ,QAAO,KAAK,mBAAmB,MAAM;GAAC,OAAO;GAAQ,MAAM;GAAW,UAAU;GAAM,CAAC;EAExF,KAAK,QACJ,QAAO,GAAG,KAAK,gBAAgB;EAEhC,KAAK,UACJ,QAAO,GAAG,KAAK,gBAAgB;EAEhC,KAAK,OACJ,QAAO,GAAG,KAAK,gBAAgB;;;AAKlC,SAAS,oBAAoB,QAA6B;AACzD,KAAI,OAAO,QAAQ,mBAAmB,KAAA,EACrC,QAAO,OAAO,OAAO;AAEtB,QAAO,aAAa;;;AAIrB,SAAgB,kBAAkB,SAAiB,QAA6B;AAE/E,QADU,UAAU,QACZ,CAAC,mBAAmB,OAAO,MAAM;EAAC,MAAM;EAAW,OAAO;EAAW,KAAK;EAAW,UAAU;EAAM,CAAC;;;;ACnH/G,MAAM,IAAI;AACV,MAAM,IAAI;AAEV,MAAa,gBAAgD;CAC5D,MAAM;EAAC,aAAa;EAAI,aAAa;EAAG,cAAc;EAAO;CAC7D,KAAK;EAAC,aAAa;EAAI,aAAa;EAAG,cAAc;EAAM;CAC3D,MAAM;EAAC,aAAa;EAAK,aAAa,IAAI;EAAG,cAAc;EAAO;CAClE,OAAO;EAAC,aAAa;EAAK,aAAa,KAAK;EAAG,cAAc;EAAQ;CACrE,SAAS;EAAC,aAAa;EAAK,aAAa,KAAK;EAAG,cAAc;EAAU;CACzE,MAAM;EAAC,aAAa;EAAK,aAAa,MAAM;EAAG,cAAc;EAAO;CACpE;;;;;;;AAQD,SAAgB,oBAAoB,MAAY,OAAkB,eAA0B,GAAS;AACpG,SAAQ,OAAR;EACC,KAAK,OACJ,QAAO,IAAI,KAAK,KAAK,IAAI,KAAK,gBAAgB,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,CAAC,CAAC;EAE5G,KAAK,MACJ,QAAO,IAAI,KAAK,KAAK,IAAI,KAAK,gBAAgB,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,CAAC,CAAC;EAExF,KAAK,QAAQ;GACZ,MAAM,IAAI,IAAI,KAAK,KAAK,IAAI,KAAK,gBAAgB,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,CAAC,CAAC;GAE1F,MAAM,WADM,EAAE,WACO,GAAG,gBAAgB,IAAK,KAAK;AAClD,KAAE,WAAW,EAAE,YAAY,GAAG,OAAO;AACrC,UAAO;;EAER,KAAK,QACJ,QAAO,IAAI,KAAK,KAAK,IAAI,KAAK,gBAAgB,EAAE,KAAK,aAAa,EAAE,EAAE,CAAC;EAExE,KAAK,WAAW;GACf,MAAM,QAAQ,KAAK,aAAa;GAChC,MAAM,oBAAoB,KAAK,MAAM,QAAQ,EAAE,GAAG;AAClD,UAAO,IAAI,KAAK,KAAK,IAAI,KAAK,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;;EAEvE,KAAK,OACJ,QAAO,IAAI,KAAK,KAAK,IAAI,KAAK,gBAAgB,EAAE,GAAG,EAAE,CAAC;;;;;;;AASzD,SAAgB,kBAAkB,MAAY,OAAwB;AACrE,SAAQ,OAAR;EACC,KAAK,OACJ,QAAO,IAAI,KAAK,KAAK,SAAS,GAAG,EAAE;EAEpC,KAAK,MACJ,QAAO,IAAI,KAAK,KAAK,SAAS,GAAG,EAAE;EAEpC,KAAK,OACJ,QAAO,IAAI,KAAK,KAAK,SAAS,GAAG,IAAI,EAAE;EAExC,KAAK,QACJ,QAAO,IAAI,KAAK,KAAK,IAAI,KAAK,gBAAgB,EAAE,KAAK,aAAa,GAAG,GAAG,EAAE,CAAC;EAE5E,KAAK,UACJ,QAAO,IAAI,KAAK,KAAK,IAAI,KAAK,gBAAgB,EAAE,KAAK,aAAa,GAAG,GAAG,EAAE,CAAC;EAE5E,KAAK,OACJ,QAAO,IAAI,KAAK,KAAK,IAAI,KAAK,gBAAgB,GAAG,GAAG,GAAG,EAAE,CAAC;;;;;;;;;ACzD7D,SAAgB,kBAAkB,OAAkB,eAAkC;CACrF,MAAM,EAAC,aAAa,gBAAe,cAAc;CACjD,MAAM,WAAW,cAAc,SAAS;CACxC,MAAM,UAAU,cAAc;CAC9B,MAAM,UAAU,cAAc;CAC9B,MAAM,WAAW;AAEjB,QAAO;EACN;EACA;EACA,IAAI,MAAoB;AACvB,WAAQ,KAAK,SAAS,GAAG,YAAY;;EAEtC,OAAO,GAAiB;AACvB,UAAO,IAAI,KAAK,WAAW,IAAI,QAAQ;;EAExC,gBAAgB,MAAsB;AACrC,UAAO,OAAO,WAAW;;EAE1B,gBAAgB,IAAoB;AACnC,UAAQ,KAAK,UAAW;;EAEzB;;;;ACxCF,MAAa,UAAU;CACtB,WAAW;CACX,WAAW;CACX,eAAe;CACf;AAED,MAAa,aAAa,QAAQ;AAClC,MAAa,aAAa,QAAQ;AAClC,MAAa,gBAAgB,aAAa,cAAc;AACxD,MAAa,iBAAiB,QAAQ;;AAEtC,MAAa,iBAAiB,iBAAiB;;;;;AAsB/C,SAAgB,cAAc,MAAkB,QAA6C;CAC5F,MAAM,yBAAS,IAAI,KAAwB;AAE3C,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACrC,MAAM,OAAO,KAAK;AAClB,MAAI,SAAS,KAAA,EACZ;EAGD,MAAM,QAAQ,UAAU,KAAK,WAAW;EACxC,MAAM,IAAI,OAAO,IAAI,MAAM;EAC3B,MAAM,IAAI,IAAI,aAAa;EAC3B,MAAM,UAAU,IAAI,aAAa,aAAa;EAE9C,MAAM,OAAO,KAAK,QAAQ;AAE1B,MAAI,SAAS,aAAa;AACzB,UAAO,IAAI,KAAK,IAAI;IACnB,QAAQ,KAAK;IACb;IACA;IACA,OAAO;IACP,QAAQ;IACR,eAAe;IACf,MAAM;IACN,UAAU;IACV,SAAS;IACT;IACA,CAAC;AACF;;EAGD,MAAM,QAAQ,KAAK,IAAI,OAAO,gBAAgB,KAAK,SAAS,EAAE,EAAE;EAChE,MAAM,gBAAgB,QAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,YAAY,EAAE,CAAC;AAE1E,SAAO,IAAI,KAAK,IAAI;GACnB,QAAQ,KAAK;GACb;GACA;GACA;GACA,QAAQ;GACR;GACA;GACA,UAAU;GACV,SAAS,IAAI,QAAQ;GACrB;GACA,CAAC;;AAGH,QAAO;;;;;AAMR,SAAgB,mBAAmB,UAA0B;AAC5D,QAAO,WAAW;;;;;;AAOnB,SAAgB,eAAe,OAAmB,cAAc,GAAiB;AAChF,KAAI,MAAM,WAAW,GAAG;EACvB,MAAM,sBAAM,IAAI,MAAM;AACtB,SAAO,CAAC,KAAK,QAAQ,KAAK,GAAG,CAAC;;CAG/B,IAAI,QAAQ;CACZ,IAAI,QAAQ;AAEZ,MAAK,MAAM,QAAQ,OAAO;EACzB,MAAM,QAAQ,UAAU,KAAK,WAAW;EACxC,MAAM,MAAM,QAAQ,OAAO,KAAK,SAAS;AACzC,MAAI,MAAM,SAAS,GAAG,MACrB,SAAQ,MAAM,SAAS;AAExB,MAAI,IAAI,SAAS,GAAG,MACnB,SAAQ,IAAI,SAAS;;AAIvB,QAAO,CAAC,QAAQ,IAAI,KAAK,MAAM,EAAE,CAAC,YAAY,EAAE,QAAQ,IAAI,KAAK,MAAM,EAAE,YAAY,CAAC;;;;AC1GvF,MAAM,cAAc;;;;;;AAOpB,SAAgB,WAAW,OAAe,SAA+C;AACxF,QAAO,MACL,KAAK,SAAS;EACd,MAAM,MAAM,QAAQ,IAAI,KAAK,OAAO;EACpC,MAAM,MAAM,QAAQ,IAAI,KAAK,OAAO;AACpC,MAAI,QAAQ,KAAA,KAAa,QAAQ,KAAA,EAChC,QAAO;AAER,SAAO;GACN,QAAQ,KAAK;GACb,cAAc,KAAK;GACnB,cAAc,KAAK;GACnB,QAAQ,MAAM,KAAK,MAAM,KAAK,IAAI;GAClC;GACA,CACD,QAAQ,MAAuB,MAAM,KAAK;;;;;;;;;;;AAY7C,SAAS,MAAM,MAAoB,KAAgB,KAAyB;CAC3E,IAAI,IAAY;CAChB,MAAM,KAAK,IAAI;CACf,MAAM,KAAK,IAAI;AAEf,SAAQ,MAAR;EACC,KAAK;AACJ,QAAK,IAAI,SAAS,cAAc,IAAI,IAAI,iBAAiB,IAAI,IAAI,IAAI;AACrE,QAAK,IAAI,SAAS,cAAc,IAAI,IAAI,iBAAiB,IAAI;AAC7D;EAED,KAAK;AACJ,QAAK,IAAI,SAAS,cAAc,IAAI,IAAI,iBAAiB,IAAI;AAC7D,QAAK,IAAI,SAAS,cAAc,IAAI,IAAI,iBAAiB,IAAI;AAC7D;EAED,KAAK;AACJ,QAAK,IAAI,SAAS,cAAc,IAAI,IAAI,iBAAiB,IAAI,IAAI,IAAI;AACrE,QAAK,IAAI,SAAS,cAAc,IAAI,IAAI,iBAAiB,IAAI,IAAI,IAAI;AACrE;EAED,KAAK;AACJ,QAAK,IAAI,SAAS,cAAc,IAAI,IAAI,iBAAiB,IAAI;AAC7D,QAAK,IAAI,SAAS,cAAc,IAAI,IAAI,iBAAiB,IAAI,IAAI,IAAI;AACrE;;AAKF,KAAI,KAAK,IAAI,KAAK,GAAG,GAAG,EACvB,QAAO,CACN;EAAC,GAAG;EAAI,GAAG;EAAG,EACd;EAAC,GAAG;EAAI,GAAG;EAAG,CACd;AAIF,KAAI,MAAM,IAAI;EACb,MAAM,OAAO,KAAK,KAAK,IAAI,cAAc,KAAK,MAAM,EAAE;AACtD,SAAO;GACN;IAAC,GAAG;IAAI,GAAG;IAAG;GACd;IAAC,GAAG;IAAM,GAAG;IAAG;GAChB;IAAC,GAAG;IAAM,GAAG;IAAG;GAChB;IAAC,GAAG;IAAI,GAAG;IAAG;GACd;;CAIF,MAAM,QAAQ,KAAK;AACnB,QAAO;EACN;GAAC,GAAG;GAAI,GAAG;GAAG;EACd;GAAC,GAAG,KAAK;GAAa,GAAG;GAAG;EAC5B;GAAC,GAAG,KAAK;GAAa,IAAI,KAAK,MAAM;GAAE;EACvC;GAAC,GAAG;GAAO,IAAI,KAAK,MAAM;GAAE;EAC5B;GAAC,GAAG;GAAO,GAAG;GAAG;EACjB;GAAC,GAAG;GAAI,GAAG;GAAG;EACd;;;;AC/FF,SAAgB,GAAG,KAAa,OAAiC,IAAsB;CACtF,MAAM,OAAO,KAAK,SAAS,gBAAgB,IAAI,IAAI,GAAG,SAAS,cAAc,IAAI;AACjF,KAAI,UAAU,KAAA,EACb,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,MAAM,CACzC,KAAI,MAAM,WAAW,OAAO,MAAM,YAAY,MAAM,KACnD,KAAI,MAAqB,EAAkC;UACjD,KAAK,KAEd,MAAa,KAAK;KAEnB,MAAK,aAAa,GAAG,OAAO,EAAE,CAAC;AAIlC,QAAO;;;AAIR,SAAgB,IAAI,MAAmB,QAA4C;AAClF,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,OAAO,CAEzC,MAAK,MAAc,KAAK,KAAK;;;AAKhC,SAAgB,cAAc,MAAqB;AAClD,QAAO,KAAK,eAAe,KAC1B,MAAK,YAAY,KAAK,WAAW;;;AAKnC,SAAgB,UAAU,QAAiB,UAAoC;CAC9E,MAAM,OAAO,SAAS,wBAAwB;AAC9C,MAAK,MAAM,KAAK,SACf,MAAK,YAAY,EAAE;AAEpB,QAAO,OAAO,KAAK;;;AAgBpB,SAAgB,SAAS,MAAe,OAA8C;AACrF,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,MAAM,CACzC,MAAK,aAAa,GAAG,OAAO,EAAE,CAAC;;;;;;;;ACtDjC,SAAgB,iBAAiB,WAAwB,OAAyB;CACjF,MAAM,EAAC,OAAO,eAAe,aAAa,QAAQ,YAAY,WAAU;CACxE,MAAM,eAAe,OAAO,gBAAgB;CAE5C,MAAM,aAAqB,EAAE;CAC7B,MAAM,aAAqB,EAAE;CAE7B,IAAI,MAAM,oBAAoB,eAAe,OAAO,aAAa;CACjE,IAAI,iBAAiB;CACrB,IAAI,aAAa;CACjB,IAAI,aAAa;AAEjB,QAAO,MAAM,aAAa;EACzB,MAAM,OAAO,kBAAkB,KAAK,MAAM;EAC1C,MAAM,IAAI,OAAO,IAAI,IAAI;EACzB,MAAM,IAAI,OAAO,IAAI,KAAK,GAAG;AAC7B,aAAW,KAAK;GAAC,OAAO,kBAAkB,KAAK,OAAO,OAAO;GAAE;GAAG,OAAO;GAAE,CAAC;EAE5E,MAAM,SAAS,iBAAiB,KAAK,OAAO,OAAO;AACnD,MAAI,WAAW,gBAAgB;AAC9B,OAAI,mBAAmB,GACtB,YAAW,KAAK;IAAC,OAAO;IAAgB,GAAG;IAAY,OAAO;IAAW,CAAC;AAE3E,oBAAiB;AACjB,gBAAa;AACb,gBAAa;QAEb,eAAc;AAEf,QAAM;;AAEP,KAAI,mBAAmB,GACtB,YAAW,KAAK;EAAC,OAAO;EAAgB,GAAG;EAAY,OAAO;EAAW,CAAC;CAI3E,MAAM,WAAW,GAAG,MAAM;AAC1B,MAAK,UAAU;EACd,UAAU;EACV,QAAQ;EACR,OAAO,GAAG,WAAW;EACrB,YAAY;EACZ,cAAc;EACd,CAAC;CAEF,MAAM,aAAa,WAAW,KAAK,SAAS;EAC3C,MAAM,IAAI,GAAG,MAAM;AACnB,OAAK,GAAG;GACP,UAAU;GACV,MAAM,GAAG,KAAK,EAAE;GAChB,OAAO,GAAG,KAAK,MAAM;GACrB,QAAQ;GACR,aAAa;GACb,SAAS;GACT,YAAY;GACZ,aAAa;GACb,UAAU;GACV,YAAY;GACZ,OAAO;GACP,UAAU;GACV,YAAY;GACZ,eAAe;GACf,eAAe;GACf,CAAC;AACF,IAAE,cAAc,KAAK;AACrB,SAAO;GACN;CAGF,MAAM,WAAW,GAAG,MAAM;AAC1B,MAAK,UAAU;EACd,UAAU;EACV,QAAQ;EACR,OAAO,GAAG,WAAW;EACrB,YAAY;EACZ,cAAc;EACd,CAAC;CAEF,MAAM,aAAa,WAAW,KAAK,SAAS;EAC3C,MAAM,IAAI,GAAG,MAAM;AACnB,OAAK,GAAG;GACP,UAAU;GACV,MAAM,GAAG,KAAK,EAAE;GAChB,OAAO,GAAG,KAAK,MAAM;GACrB,QAAQ;GACR,aAAa;GACb,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,UAAU;GACV,OAAO;GACP,UAAU;GACV,YAAY;GACZ,CAAC;AACF,IAAE,cAAc,KAAK;AACrB,SAAO;GACN;AAEF,WAAU,UAAU,WAAW;AAC/B,WAAU,UAAU,WAAW;AAE/B,eAAc,UAAU;AACxB,WAAU,OAAO,SAAS;AAC1B,WAAU,OAAO,SAAS;;;AAI3B,SAAS,KAAK,MAAmB,QAA4C;AAC5E,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,OAAO,CAEzC,MAAK,MAAc,KAAK,KAAK;;;;AC1GhC,MAAa,uBAAqC;CACjD;EACC,IAAI;EACJ,QAAQ;EACR,OAAO;EACP;CACD;EACC,IAAI;EACJ,QAAQ;EACR,OAAO;EACP,OAAO;EACP,SAAS,OAAO,OAAO,MAAM,WAAW,kBAAkB,OAAO,MAAM,EAAE,OAAO;EAChF;CACD;EACC,IAAI;EACJ,QAAQ;EACR,OAAO;EACP,OAAO;EACP,SAAS,UAAY,QAAmB,IAAI,OAAO,MAAM,GAAG;EAC5D;CACD;EACC,IAAI;EACJ,QAAQ;EACR,OAAO;EACP;CACD;;;;;AAMD,SAAgB,mBAAmB,QAAmC;AACrE,QAAO;EACN;GACC,IAAI;GACJ,QAAQ,OAAO,QAAQ,oBAAoB,aAAa;GACxD,OAAO;GACP;EACD;GACC,IAAI;GACJ,QAAQ,OAAO,QAAQ,qBAAqB,aAAa;GACzD,OAAO;GACP,OAAO;GACP,SAAS,OAAO,OAAO,MAAM,QAAQ,kBAAkB,OAAO,MAAM,EAAE,IAAI;GAC1E;EACD;GACC,IAAI;GACJ,QAAQ,OAAO,QAAQ,mBAAmB,aAAa;GACvD,OAAO;GACP,OAAO;GACP,SAAS,UAAY,QAAmB,IAAI,OAAO,MAAM,GAAG;GAC5D;EACD;GACC,IAAI;GACJ,QAAQ;GACR,OAAO;GACP;EACD;;AAGF,SAAgB,oBAAoB,SAA+B;AAClE,QAAO,QACL,QAAQ,MAAM,EAAE,YAAY,MAAM,CAClC,KAAK,MAAM,EAAE,MAAM,CACnB,KAAK,IAAI;;AAGZ,SAAgB,eAAe,SAAqC;AACnE,QAAO,QAAQ,QAAQ,MAAM,EAAE,YAAY,MAAM;;AAGlD,MAAa,2BAA2B;AAExC,MAAM,QAAQ;AACd,MAAM,QAAQ;AAEd,SAAS,oBAAoB,OAAuB;CACnD,MAAM,UAAU,MAAM,MAAM;CAC5B,MAAM,UAAU,MAAM,KAAK,QAAQ;AACnC,KAAI,QACH,QAAO,WAAW,QAAQ,MAAM,IAAI;CAErC,MAAM,UAAU,MAAM,KAAK,QAAQ;AACnC,KAAI,QACH,QAAO,WAAW,QAAQ,MAAM,IAAI,GAAA;AAErC,QAAO;;AAGR,SAAgB,iBAAiB,SAA+B;CAC/D,IAAI,QAAQ;AACZ,MAAK,MAAM,OAAO,eAAe,QAAQ,CACxC,UAAS,oBAAoB,IAAI,MAAM;AAExC,QAAO;;;;ACpGR,MAAM,SAAS;AACf,MAAM,mBAAmB;;AAWzB,SAAgB,eAAe,WAAwB,OAAmB,KAAwB,SAA6B;CAC9H,MAAM,EAAC,SAAS,YAAY,aAAa,YAAY,UAAU,YAAY,eAAe,WAAU;CAEpG,MAAM,OAAO,SAAS,wBAAwB;AAE9C,KAAI,aAAa,GAAG;EACnB,MAAM,SAAS,GAAG,MAAM;AACxB,SAAO,MAAM,SAAS,GAAG,WAAW;AACpC,OAAK,OAAO,OAAO;;AAGpB,MAAK,MAAM,OAAO,QAAQ,MAAM,YAAY,WAAW,EAAE,CACxD,MAAK,OAAO,SAAS,KAAK,YAAY,aAAa,KAAK,SAAS,OAAO,CAAC;AAG1E,KAAI,gBAAgB,GAAG;EACtB,MAAM,SAAS,GAAG,MAAM;AACxB,SAAO,MAAM,SAAS,GAAG,cAAc;AACvC,OAAK,OAAO,OAAO;;AAGpB,eAAc,UAAU;AACxB,WAAU,OAAO,KAAK;;AAGvB,SAAS,SACR,KACA,YACA,aACA,KACA,SACA,QACc;CACd,MAAM,WAAW,IAAI,OAAO;CAE5B,MAAM,UAAU,GAAG,MAAM;AACzB,SAAQ,YAAY;AACpB,KAAI,SAAS;EACZ,SAAS;EACT,qBAAqB,oBAAoB,QAAQ;EACjD,QAAQ,GAAG,WAAW;EACtB,YAAY;EACZ,aAAa;EACb,YAAY,WAAW,8BAA8B;EACrD,cAAc;EACd,QAAQ;EACR,WAAW;EACX,CAAC;AACF,SAAQ,WAAW;AACnB,SAAQ,aAAa,QAAQ,MAAM;AACnC,SAAQ,aAAa,iBAAiB,OAAO,SAAS,CAAC;AACvD,SAAQ,QAAQ,YAAY,OAAO,IAAI,GAAG;AAC1C,SAAQ,iBAAiB,eAAe;EACvC,MAAM,OAAOA,SAAO,IAAI;AACxB,MAAI,WAAW;GAAC,IAAI,IAAI;GAAI;GAAK,CAAC;GACjC;AACF,SAAQ,iBAAiB,YAAY,UAAU;AAC9C,MAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAC/C,SAAM,gBAAgB;AACtB,OAAI,SAAS,IAAI,GAAG;;GAEpB;AAEF,MAAK,MAAM,UAAU,eAAe,QAAQ,CAC3C,SAAQ,OAAO,UAAU,QAAQ,KAAK,aAAa,KAAK,OAAO,CAAC;AAGjE,QAAO;;AAGR,SAAS,UAAU,QAAoB,KAAe,aAA0B,KAAwB,QAAkC;AACzI,SAAQ,OAAO,IAAf;EACC,KAAK,OACJ,QAAO,kBAAkB,KAAK,aAAa,IAAI;EAEhD,KAAK,UACJ,QAAO,eAAe,KAAK,KAAK,OAAO;EAExC,QACC,QAAO,cAAc,KAAK,QAAQ,OAAO;;;AAK5C,SAAS,kBAAkB,KAAe,aAA0B,KAAqC;CACxG,MAAM,cAAc,SAAS,IAAI;CACjC,MAAM,WAAW,YAAY,IAAI,IAAI,GAAG;CAExC,MAAM,OAAO,GAAG,MAAM;AACtB,KAAI,MAAM;EACT,SAAS;EACT,YAAY;EACZ,aAAa,GAAG,IAAI,QAAQ,OAAO;EACnC,KAAK;EACL,UAAU;EACV,CAAC;AAEF,KAAI,aAAa;EAChB,MAAM,MAAM,GAAG,SAAS;AACxB,MAAI,YAAY;AAChB,MAAI,cAAc,WAAW,MAAM;AACnC,MAAI,KAAK;GACR,OAAO;GACP,QAAQ;GACR,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,YAAY;GACZ,QAAQ;GACR,QAAQ;GACR,OAAO;GACP,SAAS;GACT,YAAY;GACZ,CAAC;AACF,MAAI,iBAAiB,UAAU,MAAM;AACpC,KAAE,iBAAiB;AACnB,OAAI,SAAS,IAAI,GAAG;IACnB;AACF,OAAK,OAAO,IAAI;QACV;EACN,MAAM,SAAS,GAAG,OAAO;AACzB,SAAO,MAAM,QAAQ;AACrB,SAAO,MAAM,aAAa;AAC1B,OAAK,OAAO,OAAO;;CAGpB,MAAM,QAAQ,GAAG,OAAO;AACxB,KAAI,OAAO;EACV,UAAU;EACV,YAAY,IAAI,SAAS,YAAY,kCAAkC;EACvE,OAAO;EACP,UAAU;EACV,cAAc;EACd,YAAY;EACZ,CAAC;AACF,OAAM,cAAc,IAAI;AACxB,MAAK,OAAO,MAAM;AAElB,QAAO;;AAGR,SAAS,cAAc,KAAe,QAAoB,QAAkC;CAC3F,MAAM,OAAO,GAAG,OAAO;CACvB,MAAM,SAAuC;EAC5C,UAAU;EACV,OAAO;EACP,cAAc;EACd,UAAU;EACV,cAAc;EACd,YAAY;EACZ;AACD,KAAI,OAAO,UAAU,KAAA,EACpB,QAAO,YAAY,OAAO;AAE3B,KAAI,MAAM,OAAO;CAEjB,MAAM,OAAOA,SAAO,IAAI;AACxB,KAAI,OAAO,UAAU,KAAA,GAAW;EAC/B,MAAM,WAAW,KAAK,OAAO;AAC7B,MAAI,OAAO,WAAW,KAAA,EACrB,MAAK,cAAc,OAAO,OAAO,UAAU,MAAM,KAAK,OAAO;MAE7D,MAAK,cAAc,aAAa,QAAQ,aAAa,KAAA,IAAY,OAAO,SAAS,GAAG;;AAItF,QAAO;;AAGR,SAAS,eAAe,KAAe,KAAwB,QAAkC;CAChG,MAAM,MAAM,GAAG,SAAS;AACxB,KAAI,YAAY;AAChB,KAAI,cAAc;AAClB,KAAI,QAAQ,OAAO,QAAQ,qBAAqB,aAAa;AAC7D,KAAI,KAAK;EACR,YAAY;EACZ,QAAQ;EACR,QAAQ;EACR,OAAO;EACP,UAAU;EACV,YAAY;EACZ,CAAC;AACF,KAAI,iBAAiB,UAAU,UAAU;AACxC,QAAM,iBAAiB;AACvB,MAAI,MAAM,IAAI,GAAG;GAChB;AAEF,QAAO;;AAGR,SAASA,SAAO,KAAqB;AACpC,QAAO;EACN,IAAI,IAAI;EACR,MAAM,IAAI;EACV,YAAY,IAAI;EAChB,UAAU,IAAI;EACd,UAAU,IAAI;EACd,MAAM,IAAI;EACV,MAAM,IAAI;EACV,GAAI,IAAI,WAAW,KAAA,IAAY,EAAE,GAAG,EAAC,QAAQ,IAAI,QAAO;EACxD,GAAI,IAAI,UAAU,KAAA,IAAY,EAAE,GAAG,EAAC,OAAO,IAAI,OAAM;EACrD;;;AAIF,SAAgB,oBAAoB,SAAoC;CACvE,MAAM,SAAS,GAAG,MAAM;AACxB,KAAI,QAAQ;EACX,SAAS;EACT,qBAAqB,oBAAoB,QAAQ;EACjD,QAAQ;EACR,YAAY;EACZ,cAAc;EACd,aAAa;EACb,YAAY;EACZ,eAAe;EACf,WAAW;EACX,CAAC;CAEF,MAAM,UAAU,eAAe,QAAQ;AACvC,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACxC,MAAM,SAAS,QAAQ;AACvB,MAAI,WAAW,KAAA,EACd;EAED,MAAM,UAAU,GAAG,MAAM;AACzB,MAAI,SAAS;GAAC,UAAU;GAAY,SAAS;GAAQ,YAAY;GAAW,CAAC;EAE7E,MAAM,OAAO,GAAG,OAAO;AACvB,MAAI,MAAM;GACT,UAAU;GACV,YAAY;GACZ,OAAO;GACP,eAAe;GACf,eAAe;GACf,cAAc;GACd,CAAC;AACF,MAAI,OAAO,UAAU,KAAA,EACpB,MAAK,MAAM,YAAY,OAAO;AAE/B,OAAK,cAAc,OAAO;AAC1B,UAAQ,OAAO,KAAK;AAEpB,MAAI,IAAI,QAAQ,SAAS,GAAG;GAC3B,MAAM,SAAS,GAAG,MAAM;AACxB,UAAO,YAAY;AACnB,OAAI,QAAQ;IACX,UAAU;IACV,OAAO;IACP,KAAK;IACL,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,CAAC;AACF,WAAQ,OAAO,OAAO;;AAGvB,SAAO,OAAO,QAAQ;;AAGvB,QAAO;;;;;;;AAUR,SAAgB,kBAAkB,UAAuB,QAAqB,SAAuB,UAAwD;CAC5J,MAAM,UAAU,SAAS,iBAA8B,2BAA2B;CAClF,MAAM,WAA2B,EAAE;AAEnC,MAAK,IAAI,WAAW,GAAG,WAAW,QAAQ,QAAQ,YAAY;EAC7D,MAAM,SAAS,QAAQ,KAAK,SAAS;AACrC,MAAI,WAAW,KACd;EAED,MAAM,mBAAmB;EAEzB,MAAM,iBAAiB,MAA0B;AAChD,OAAI,EAAE,WAAW,EAChB;AAED,KAAE,gBAAgB;AAClB,KAAE,iBAAiB;GAEnB,MAAM,SAAS,EAAE;GAEjB,MAAM,cAAc,CADL,GAAG,SAAS,SACF,CAAC,KAAK,MAAM,EAAE,uBAAuB,CAAC,MAAM;GAErE,MAAM,UAAU,OAA2B;IAC1C,MAAM,KAAK,GAAG,UAAU;IACxB,MAAM,YAAY,CAAC,GAAG,YAAY;AAElC,cAAU,oBAAoB,KAAK,IAAI,mBAAmB,YAAY,qBAAqB,KAAK,GAAG;AAEnG,QAAI,mBAAmB,IAAI,UAAU,OACpC,WAAU,mBAAmB,KAAK,KAAK,IAAI,mBAAmB,YAAY,mBAAmB,MAAM,KAAK,GAAG;IAG5G,MAAM,WAAW,UAAU,KAAK,MAAM,GAAG,KAAK,MAAM,EAAE,CAAC,IAAI,CAAC,KAAK,IAAI;AACrE,aAAS,MAAM,sBAAsB;IAErC,MAAM,OAAO,OAAO,iBAA8B,iBAAe;AACjE,SAAK,MAAM,OAAO,KACjB,KAAI,MAAM,sBAAsB;;GAIlC,MAAM,aAAmB;AACxB,WAAO,oBAAoB,eAAe,OAAO;AACjD,WAAO,oBAAoB,aAAa,KAAK;IAE7C,MAAM,aAAa,CAAC,GAAG,SAAS,SAAS;IACzC,MAAM,UAAU,eAAe,QAAQ;AACvC,SAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,UAAU,IAAI,WAAW,QAAQ,KAAK;KACjE,MAAM,MAAM,QAAQ;KACpB,MAAM,OAAO,WAAW;AACxB,SAAI,QAAQ,KAAA,KAAa,SAAS,KAAA,GAAW;MAC5C,MAAM,IAAI,KAAK,uBAAuB,CAAC;AACvC,UAAI,QAAQ,GAAG,KAAK,MAAM,EAAE,CAAC;;;AAG/B,eAAW,CAAC,GAAG,QAAQ,CAAC;;AAGzB,UAAO,iBAAiB,eAAe,OAAO;AAC9C,UAAO,iBAAiB,aAAa,KAAK;;AAG3C,SAAO,iBAAiB,eAAe,cAAc;AACrD,WAAS,WAAW;AACnB,UAAO,oBAAoB,eAAe,cAAc;IACvD;;AAGH,cAAa;AACZ,OAAK,MAAM,WAAW,SACrB,UAAS;;;;;ACvWZ,MAAM,KAAK;AACX,MAAM,aAAa;;;;;AAMnB,SAAgB,sBAAsB,YAAoB,aAAoC;CAC7F,MAAM,MAAM,SAAS,gBAAgB,IAAI,MAAM;AAC/C,UAAS,KAAK;EACb,OAAO;EACP,QAAQ;EACR,CAAC;AACF,QAAO,OAAO,IAAI,OAAO;EACxB,UAAU;EACV,KAAK;EACL,MAAM;EACN,eAAe;EACf,UAAU;EACV,QAAQ;EACR,CAAC;CAGF,MAAM,OAAO,SAAS,gBAAgB,IAAI,OAAO;AAEjD,MAAK,MAAM,CAAC,IAAI,UAAU,CACzB,CAAC,eAAe,oBAAoB,EACpC,CAAC,kBAAkB,uBAAuB,CAC1C,EAAW;EACX,MAAM,SAAS,SAAS,gBAAgB,IAAI,SAAS;AACrD,WAAS,QAAQ;GAChB;GACA,SAAS;GACT,MAAM;GACN,MAAM;GACN,aAAa;GACb,cAAc;GACd,QAAQ;GACR,CAAC;EACF,MAAM,OAAO,SAAS,gBAAgB,IAAI,OAAO;AACjD,WAAS,MAAM;GAAC,GAAG;GAAwB,MAAM;GAAM,CAAC;AACxD,SAAO,OAAO,KAAK;AACnB,OAAK,OAAO,OAAO;;AAGpB,KAAI,OAAO,KAAK;CAGhB,MAAM,YAAY,SAAS,gBAAgB,IAAI,OAAO;AACtD,UAAS,WAAW;EACnB,GAAG;EACH,MAAM;EACN,QAAQ;EACR,gBAAgB;EAChB,oBAAoB;EACpB,CAAC;AACF,WAAU,UAAU,IAAI,mBAAmB;AAC3C,WAAU,MAAM,UAAU;AAC1B,KAAI,OAAO,UAAU;AAErB,QAAO;;;;;;AAOR,SAAgB,cAAc,KAAoB,IAAY,IAAY,IAAY,IAAY,OAAsB;CACvH,MAAM,QAAQ,IAAI,cAA8B,wBAAwB;AACxE,KAAI,UAAU,KACb;AAED,UAAS,OAAO;EACf,GAAG,KAAK,GAAG,GAAG,GAAG,KAAK,GAAG,GAAG;EAC5B,oBAAoB,QAAQ,SAAS;EACrC,CAAC;AACF,KAAI,MACH,OAAM,aAAa,cAAc,oBAAoB;KAErD,OAAM,gBAAgB,aAAa;AAEpC,OAAM,MAAM,UAAU;;;;;AAMvB,SAAgB,cAAc,KAA0B;CACvD,MAAM,QAAQ,IAAI,cAA8B,wBAAwB;AACxE,KAAI,UAAU,MAAM;AACnB,QAAM,MAAM,UAAU;AACtB,QAAM,gBAAgB,aAAa;;;;;;;AAQrC,SAAgB,sBACf,KACA,OACA,YACA,aACA,gBACA,qCACO;AACP,UAAS,KAAK;EAAC,OAAO;EAAY,QAAQ;EAAY,CAAC;CAGvD,MAAM,WAAsB,EAAE;AAC9B,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,SAAS,QAAQ,KAAK;EAC7C,MAAM,QAAQ,IAAI,SAAS;AAC3B,MAAI,UAAU,KAAA,KAAa,CAAC,MAAM,UAAU,SAAS,mBAAmB,CACvE,UAAS,KAAK,MAAM;;AAGtB,MAAK,MAAM,QAAQ,SAClB,KAAI,YAAY,KAAK;AAGtB,MAAK,MAAM,QAAQ,OAAO;EACzB,MAAM,EAAC,WAAU;AACjB,MAAI,OAAO,WAAW,EACrB;EAGD,IAAI,IAAI,KAAK,OAAO,IAAI,KAAK,EAAE,GAAG,OAAO,IAAI,KAAK;AAClD,OAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;GACvC,MAAM,IAAI,OAAO;AACjB,OAAI,MAAM,KAAA,EACT,MAAK,MAAM,EAAE,EAAE,GAAG,EAAE;;EAItB,MAAM,YACL,uCAAuC,mBAAmB,SAAS,KAAK,iBAAiB,kBAAkB,KAAK,iBAAiB;EAElI,MAAM,OAAO,SAAS,gBAAgB,IAAI,OAAO;AACjD,WAAS,MAAM;GACd;GACA,MAAM;GACN,QAAQ,YAAY,yBAAyB;GAC7C,gBAAgB,YAAY,QAAQ;GACpC,mBAAmB;GACnB,cAAc,YAAY,yBAAyB;GACnD,CAAC;AACF,MAAI,OAAO,KAAK;;;;;;;;;;;;ACzIlB,SAAgB,WAAW,OAAoB,gBAA6B,MAAgB,WAA8B,KAAiC;CAE1J,SAAS,UAAU,GAAuB;AACzC,MAAI,EAAE,WAAW,EAChB;AAED,IAAE,gBAAgB;AAClB,MAAI;AACH,SAAM,kBAAkB,EAAE,UAAU;UAC7B;AAGR,MAAI,WAAW,KAAK,GAAG;EAEvB,MAAM,SAAS,EAAE;EACjB,MAAM,aAAa,UAAU,KAAK,WAAW;EAC7C,MAAM,SAAS,WAAW;EAE1B,SAAS,OAAO,IAAwB;GACvC,MAAM,KAAK,GAAG,UAAU;GACxB,MAAM,OAAO,KAAK,MAAM,OAAO,gBAAgB,GAAG,CAAC;AACnD,OAAI,SAAS;IAAC,IAAI,KAAK;IAAI,WAAW,QAAQ,YAAY,KAAK;IAAC,CAAC;;EAGlE,SAAS,OAAa;AACrB,UAAO,oBAAoB,eAAe,OAAO;AACjD,UAAO,oBAAoB,aAAa,KAAK;AAC7C,SAAM,MAAM,SAAS;;AAGtB,QAAM,MAAM,SAAS;AACrB,SAAO,iBAAiB,eAAe,OAAO;AAC9C,SAAO,iBAAiB,aAAa,KAAK;;CAI3C,SAAS,aAAa,GAAuB;AAC5C,MAAI,EAAE,WAAW,EAChB;AAED,IAAE,gBAAgB;AAClB,IAAE,iBAAiB;AACnB,MAAI;AACH,kBAAe,kBAAkB,EAAE,UAAU;UACtC;EAIR,MAAM,SAAS,EAAE;EACjB,MAAM,UAAU,KAAK;EACrB,MAAM,SAAS,WAAW;EAE1B,SAAS,OAAO,IAAwB;GACvC,MAAM,KAAK,GAAG,UAAU;GACxB,MAAM,YAAY,KAAK,MAAM,OAAO,gBAAgB,GAAG,CAAC;AACxD,OAAI,WAAW;IAAC,IAAI,KAAK;IAAI,UAAU,KAAK,IAAI,GAAG,UAAU,UAAU;IAAC,CAAC;;EAG1E,SAAS,OAAa;AACrB,UAAO,oBAAoB,eAAe,OAAO;AACjD,UAAO,oBAAoB,aAAa,KAAK;;AAG9C,SAAO,iBAAiB,eAAe,OAAO;AAC9C,SAAO,iBAAiB,aAAa,KAAK;;CAG3C,SAAS,WAAW,OAAyB;AAC5C,MAAI,MAAM,WAAW,EACpB;AAED,MAAI,mBAAmB;GAAC,IAAI,KAAK;GAAI,QAAQ;GAAO,SAAS;GAAgB,MAAM,OAAO,KAAK;GAAC,CAAC;;AAGlG,OAAM,iBAAiB,eAAe,UAAU;AAChD,OAAM,iBAAiB,SAAS,WAAW;AAC3C,gBAAe,iBAAiB,eAAe,aAAa;AAE5D,cAAa;AACZ,QAAM,oBAAoB,eAAe,UAAU;AACnD,QAAM,oBAAoB,SAAS,WAAW;AAC9C,iBAAe,oBAAoB,eAAe,aAAa;;;;;;;AAQjE,SAAgB,qBAAqB,WAAwB,QAAgB,KAAiC;CAC7G,SAAS,UAAgB;AACxB,MAAI,WAAW,OAAO;;CAEvB,SAAS,cAAc,OAAyB;AAC/C,MAAI,MAAM,WAAW,GAAG;GACvB,MAAM,OAAQ,UAA4C;AAC1D,OAAI,SAAS,KAAA,EACZ;AAED,OAAI,mBAAmB;IAAC,IAAI;IAAQ,QAAQ;IAAa,SAAS;IAAgB;IAAK,CAAC;;;AAG1F,WAAU,iBAAiB,SAAS,QAAQ;AAC5C,WAAU,iBAAiB,SAAS,cAAc;AAClD,cAAa;AACZ,YAAU,oBAAoB,SAAS,QAAQ;AAC/C,YAAU,oBAAoB,SAAS,cAAc;;;AAIvD,SAAgB,kBAAkB,WAAwB,MAAkB;AAC1E,WAA4C,SAAS;;AAGvD,SAAS,OAAO,KAAqB;AACpC,QAAO;EACN,IAAI,IAAI;EACR,MAAM,IAAI;EACV,YAAY,IAAI;EAChB,UAAU,IAAI;EACd,UAAU,IAAI;EACd,MAAM,IAAI;EACV,MAAM,IAAI;EACV,GAAI,IAAI,WAAW,KAAA,IAAY,EAAE,GAAG,EAAC,QAAQ,IAAI,QAAO;EACxD,GAAI,IAAI,UAAU,KAAA,IAAY,EAAE,GAAG,EAAC,OAAO,IAAI,OAAM;EACrD;;;;;;;;ACnIF,SAAgB,yBACf,QACA,cACA,SACA,SACA,UACA,eACA,KACa;CACb,SAAS,cAAc,GAAuB;AAC7C,MAAI,EAAE,WAAW,EAChB;AAED,IAAE,gBAAgB;AAClB,IAAE,iBAAiB;AACnB,MAAI;AACH,UAAO,kBAAkB,EAAE,UAAU;UAC9B;EAIR,IAAI,gBAA+B;EAEnC,SAAS,OAAO,IAAwB;GACvC,MAAM,YAAY,cAAc,uBAAuB;GACvD,MAAM,IAAI,GAAG,UAAU,UAAU;GACjC,MAAM,IAAI,GAAG,UAAU,UAAU;GAIjC,MAAM,QADK,SAAS,iBAAiB,GAAG,SAAS,GAAG,QACpC,EAAE,QAAqB,iBAAiB;GACxD,MAAM,WAAW,UAAU,QAAQ,UAAU,KAAA,IAAY,OAAO,MAAM,QAAQ,UAAU,GAAG;AAE3F,mBAAgB,aAAa,QAAQ,aAAa,eAAe,WAAW;AAE5E,iBAAc,UAAU,SAAS,SAAS,GAAG,GAAG,kBAAkB,KAAK;;EAGxE,SAAS,OAAa;AACrB,UAAO,oBAAoB,eAAe,OAAO;AACjD,UAAO,oBAAoB,aAAa,KAAK;AAC7C,iBAAc,SAAS;AAEvB,OAAI,kBAAkB,KACrB,KAAI,eAAe;IAAC;IAAc,cAAc;IAAe,MAAM;IAAK,CAAC;;AAI7E,SAAO,iBAAiB,eAAe,OAAO;AAC9C,SAAO,iBAAiB,aAAa,KAAK;;AAG3C,QAAO,iBAAiB,eAAe,cAAc;AACrD,QAAO,WAAW;AAClB,QAAO,aAAa,QAAQ,SAAS;AACrC,QAAO,aAAa,cAAc,yBAAyB,eAAe;CAE1E,SAAS,UAAU,OAA4B;AAC9C,MAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,IAC1C,OAAM,gBAAgB;;AAGxB,QAAO,iBAAiB,WAAW,UAAU;AAE7C,cAAa;AACZ,SAAO,oBAAoB,eAAe,cAAc;AACxD,SAAO,oBAAoB,WAAW,UAAU;;;;;;;AAQlD,SAAgB,uBAAoC;CACnD,MAAM,SAAS,SAAS,cAAc,MAAM;AAC5C,QAAO,YAAY;AACnB,QAAO,MAAM,WAAW;AACxB,QAAO,MAAM,QAAQ;AACrB,QAAO,MAAM,SAAS;AACtB,QAAO,MAAM,eAAe;AAC5B,QAAO,MAAM,aAAa;AAC1B,QAAO,MAAM,SAAS;AACtB,QAAO,MAAM,SAAS;AACtB,QAAO,MAAM,SAAS;AACtB,QAAO,MAAM,UAAU;AACvB,QAAO,MAAM,aAAa;AAC1B,QAAO,MAAM,YAAY;AACzB,QAAO,MAAM,gBAAgB;AAC7B,QAAO,MAAM,cAAc;AAC3B,QAAO;;;;ACpFR,MAAM,YAAoC;CACzC,MAAM;CACN,SAAS;CACT,WAAW;CACX;;AAwBD,SAAgB,sBAAqC;CACpD,MAAM,kBAAkB,GAAG,MAAM;CACjC,MAAM,kBAAkB,GAAG,MAAM;CACjC,MAAM,gBAAgB,GAAG,MAAM;CAC/B,MAAM,WAAW,sBAAsB,GAAG,EAAE;AAE5C,KAAI,iBAAiB,EAAC,UAAU,YAAW,CAAC;AAC5C,KAAI,eAAe;EAAC,UAAU;EAAY,KAAK;EAAK,MAAM;EAAI,CAAC;AAE/D,iBAAgB,OAAO,gBAAgB;AACvC,iBAAgB,OAAO,cAAc;AACrC,eAAc,OAAO,SAAS;AAE9B,QAAO;EACN;EACA;EACA;EACA;EACA,6BAAa,IAAI,KAAK;EACtB;;;;;;;AAQF,SAAgB,gBAAgB,MAAqB,OAAmB,KAA2B;CAClG,MAAM,EACL,SACA,SACA,OACA,QACA,OACA,eACA,aACA,YACA,YACA,qCACA,YACA,eACA,eACG;CAEJ,MAAM,EAAC,iBAAiB,eAAe,UAAU,gBAAe;CAChE,MAAM,WAAW,QAAQ;CACzB,MAAM,gBAAgB,mBAAmB,SAAS;CAGlD,MAAM,cAAc,QAAQ,MAAM,MAAM,YAAY,MAAM,WAAW,EAAE;AACvE,eAAc,gBAAgB;AAC9B,KAAI,iBAAiB,EAAC,OAAO,GAAG,WAAW,KAAI,CAAC;AAGhD,KAAI,aAAa,GAAG;EACnB,MAAM,IAAI,GAAG,MAAM;AACnB,IAAE,MAAM,SAAS,GAAG,WAAW;AAC/B,kBAAgB,OAAO,EAAE;;AAG1B,MAAK,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;EAC5C,MAAM,SAAS,aAAa;EAC5B,MAAM,SAAS,GAAG,MAAM;AACxB,MAAI,QAAQ;GACX,QAAQ,GAAG,WAAW;GACtB,YAAY,SAAS,MAAM,IAAI,oBAAoB;GACnD,cAAc;GACd,CAAC;AACF,kBAAgB,OAAO,OAAO;;AAI/B,KAAI,gBAAgB,GAAG;EACtB,MAAM,IAAI,GAAG,MAAM;AACnB,IAAE,MAAM,SAAS,GAAG,cAAc;AAClC,kBAAgB,OAAO,EAAE;;AAI1B,KAAI,eAAe;EAAC,OAAO,GAAG,WAAW;EAAK,QAAQ,GAAG,cAAc;EAAI,CAAC;CAG5E,MAAM,WAAsB,EAAE;AAC9B,MAAK,MAAM,SAAS,CAAC,GAAG,cAAc,SAAS,CAC9C,KAAI,UAAU,SACb,UAAS,KAAK,MAAM;AAGtB,MAAK,MAAM,QAAQ,SAClB,eAAc,YAAY,KAAK;AAIhC,eAAc,SAAS;AAGvB,MAAK,MAAM,EAAC,aAAa,wBAAuB,YAAY,QAAQ,EAAE;AACrE,eAAa;AACb,wBAAsB;;AAEvB,aAAY,OAAO;AAGnB,KAAI,UAAU,MACb,6BAA4B,eAAe,UAAU,OAAO,cAAc;CAI3E,IAAI,UAAU,oBAAoB,eAAe,MAAM;AACvD,QAAO,WAAW,aAAa;EAC9B,MAAM,IAAI,OAAO,IAAI,QAAQ;EAC7B,MAAM,OAAO,GAAG,MAAM;AACtB,MAAI,MAAM;GACT,UAAU;GACV,MAAM,GAAG,EAAE;GACX,KAAK;GACL,OAAO;GACP,QAAQ,GAAG,cAAc;GACzB,YAAY;GACZ,eAAe;GACf,CAAC;AACF,gBAAc,aAAa,MAAM,SAAS;AAC1C,YAAU,kBAAkB,SAAS,MAAM;;CAI5C,MAAM,SAAS,OAAO,oBAAI,IAAI,MAAM,CAAC;CACrC,MAAM,iBAAiB;AACvB,KAAI,UAAU,KAAK,UAAU,aAAa,gBAAgB;EACzD,MAAM,YAAY,GAAG,MAAM;AAC3B,YAAU,YAAY;AACtB,MAAI,WAAW;GACd,UAAU;GACV,MAAM,GAAG,OAAO;GAChB,KAAK;GACL,OAAO,GAAG,eAAe;GACzB,QAAQ,GAAG,cAAc;GACzB,YAAY;GACZ,eAAe;GACf,QAAQ;GACR,CAAC;AACF,gBAAc,aAAa,WAAW,SAAS;;CAGhD,MAAM,iBAAiB,IAAI,IAAI,YAAY,KAAK,SAAS,KAAK,GAAG,CAAC;AAGlE,MAAK,MAAM,QAAQ,aAAa;EAC/B,MAAM,SAAS,QAAQ,IAAI,KAAK,GAAG;AACnC,MAAI,WAAW,KAAA,EACd;AAGD,MAAI,OAAO,SAAS,YACnB,iBAAgB,eAAe,UAAU,MAAM,QAAQ,YAAY,aAAa,KAAK,MAAM;MAE3F,WAAU,eAAe,UAAU,MAAM,QAAQ,YAAY,aAAa,OAAO,IAAI;;AAMvF,uBAAsB,UADD,MAAM,QAAQ,SAAS,eAAe,IAAI,KAAK,aAAa,IAAI,eAAe,IAAI,KAAK,aAAa,CAC9E,EAAE,YAAY,eAAe,YAAY,oCAAoC;;AAG1H,SAAS,4BAA4B,OAAoB,YAAqB,OAAmB,eAA6B;CAC7H,MAAM,EAAC,QAAQ,eAAe,aAAa,cAAc,aAAa,sBAAqB;CAC3F,IAAI,MAAM,WAAW,cAAc;AAEnC,QAAO,MAAM,aAAa;EACzB,MAAM,OAAO,IAAI,KAAK,IAAI,SAAS,GAAG,MAAW;EACjD,MAAM,IAAI,OAAO,IAAI,IAAI;EACzB,MAAM,QAAQ,KAAK,IAAI,GAAG,OAAO,IAAI,KAAK,GAAG,EAAE;EAC/C,MAAM,UAAU,IAAI,aAAa,CAAC,MAAM,GAAG,GAAG;EAC9C,MAAM,aAAa,kBAAkB,IAAI,QAAQ;EACjD,MAAM,YAAY,YAAY,IAAI,IAAI,WAAW,CAAC;EAElD,IAAI,OAAgD;AACpD,MAAI,eAAe,KAAA,GAAW;GAC7B,MAAM,EAAC,MAAM,gBAAe;AAC5B,UAAO;aACG,gBAAgB,UAC1B,QAAO;AAGR,MAAI,SAAS,MAAM;GAClB,MAAM,UAAU,GAAG,MAAM;AACzB,WAAQ,YAAY,kCAAkC;AACtD,OAAI,YAAY,cAAc,KAAA,EAC7B,SAAQ,UAAU,IAAI,WAAW,UAAU;AAE5C,WAAQ,QAAQ,UAAU;AAC1B,OAAI,YAAY,UAAU,KAAA,GAAW;AACpC,YAAQ,QAAQ,WAAW,WAAW;AACtC,YAAQ,QAAQ,WAAW;;AAE5B,OAAI,SAAS;IACZ,UAAU;IACV,MAAM,GAAG,EAAE;IACX,KAAK;IACL,OAAO,GAAG,MAAM;IAChB,QAAQ,GAAG,cAAc;IACzB,eAAe;IACf,QAAQ;IACR,CAAC;AACF,SAAM,aAAa,SAAS,WAAW;;AAGxC,QAAM;;;AAMR,SAAS,UACR,OACA,UACA,MACA,QACA,YACA,UACA,OACA,KACO;CACP,MAAM,WAAW,KAAK,OAAO;CAC7B,MAAM,QAAQ,UAAU,OAAO,SAAS,UAAU;CAElD,MAAM,MAAM,GAAG,MAAM;AACrB,KAAI,YAAY,YAAY,WAAW,+CAA+C;AACtF,KAAI,KAAK;EACR,UAAU;EACV,MAAM,GAAG,OAAO,EAAE;EAClB,KAAK,GAAG,OAAO,EAAE;EACjB,OAAO,GAAG,OAAO,MAAM;EACvB,QAAQ,GAAG,OAAO,OAAO;EACzB,GAAI,UAAU,KAAA,IAAY,EAAE,GAAG,EAAC,YAAY,OAAM;EAClD,cAAc,OAAO,SAAS,YAAY,QAAQ;EAClD,QAAQ;EACR,YAAY;EACZ,UAAU;EACV,QAAQ,WAAW,MAAM;EACzB,aAAa;EACb,CAAC;AAGF,KAAI,OAAO,gBAAgB,GAAG;EAC7B,MAAM,OAAO,GAAG,MAAM;AACtB,MAAI,MAAM;GACT,UAAU;GACV,MAAM;GACN,KAAK;GACL,OAAO,GAAG,OAAO,cAAc;GAC/B,QAAQ;GACR,YAAY;GACZ,eAAe;GACf,CAAC;AACF,MAAI,OAAO,KAAK;;CAIjB,MAAM,QAAQ,GAAG,OAAO;AACxB,KAAI,OAAO;EACV,UAAU;EACV,MAAM;EACN,OAAO;EACP,KAAK;EACL,WAAW;EACX,UAAU;EACV,cAAc;EACd,OAAO;EACP,UAAU;EACV,YAAY;EACZ,YAAY;EACZ,eAAe;EACf,YAAY;EACZ,CAAC;AACF,OAAM,cAAc,KAAK;AACzB,KAAI,OAAO,MAAM;AACjB,KAAI,WAAW;AACf,KAAI,aAAa,QAAQ,SAAS;AAClC,KAAI,aAAa,cAAc,UAAU,MAAM,QAAQ,aAAa,KAAK,KAAK,CAAC;AAC/E,KAAI,aAAa,gBAAgB,OAAO,SAAS,CAAC;AAClD,KAAI,QAAQ,YAAY,OAAO,KAAK,GAAG;AACvC,KAAI,iBAAiB,eAAe;AACnC,MAAI,WAAW,KAAK,GAAG;GACtB;AACF,KAAI,iBAAiB,YAAY,UAAU;AAC1C,MAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAC/C,SAAM,gBAAgB;AACtB,OAAI,WAAW,KAAK,GAAG;;GAEvB;CAGF,MAAM,SAAS,GAAG,MAAM;AACxB,QAAO,YAAY;AACnB,KAAI,QAAQ;EACX,UAAU;EACV,OAAO;EACP,KAAK;EACL,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,aAAa;EACb,CAAC;AACF,KAAI,OAAO,OAAO;AAElB,OAAM,aAAa,KAAK,SAAS;CAEjC,MAAM,cAAc,WAAW,KAAK,QAAQ,YAAY,MAAM,QAAQ,IAAI;CAG1E,IAAI;AACJ,KAAI,MAAM,qBAAqB;EAC9B,MAAM,aAAa,OAAO,IAAI,OAAO,SAAS;EAC9C,MAAM,aAAa,sBAAsB;AACzC,aAAW,MAAM,OAAO,GAAG,OAAO,EAAE;AACpC,aAAW,MAAM,MAAM,GAAG,WAAW;AACrC,QAAM,aAAa,YAAY,SAAS;EAExC,MAAM,cAAc,sBAAsB;AAC1C,cAAY,MAAM,OAAO,GAAG,OAAO,IAAI,OAAO,MAAM;AACpD,cAAY,MAAM,MAAM,GAAG,WAAW;AACtC,QAAM,aAAa,aAAa,SAAS;EAEzC,MAAM,cAAc,yBAAyB,YAAY,KAAK,IAAI,OAAO,GAAG,YAAY,UAAU,OAAO,IAAI;EAC7G,MAAM,eAAe,yBAAyB,aAAa,KAAK,IAAI,OAAO,IAAI,OAAO,OAAO,YAAY,UAAU,OAAO,IAAI;EAG9H,MAAM,mBAAyB;AAC9B,cAAW,MAAM,UAAU;AAC3B,eAAY,MAAM,UAAU;AAC5B,cAAW,MAAM,YAAY;AAC7B,eAAY,MAAM,YAAY;;EAE/B,MAAM,mBAAyB;AAC9B,cAAW,MAAM,UAAU;AAC3B,eAAY,MAAM,UAAU;AAC5B,cAAW,MAAM,YAAY;AAC7B,eAAY,MAAM,YAAY;;AAE/B,MAAI,iBAAiB,cAAc,WAAW;AAC9C,MAAI,iBAAiB,cAAc,WAAW;AAE9C,6BAAiC;AAChC,gBAAa;AACb,iBAAc;AACd,OAAI,oBAAoB,cAAc,WAAW;AACjD,OAAI,oBAAoB,cAAc,WAAW;;;CAInD,MAAM,QAKF;EAAC;EAAK,cAAc;EAAQ;EAAY;AAC5C,KAAI,uBAAuB,KAAA,EAC1B,OAAM,qBAAqB;AAE5B,UAAS,IAAI,KAAK,IAAI,MAAM;;AAK7B,SAAS,gBACR,OACA,UACA,MACA,QACA,YACA,UACA,KACA,OACO;CACP,MAAM,WAAW,KAAK,OAAO;CAC7B,MAAM,OAAO,iBAAiB;CAE9B,MAAM,UAAU,GAAG,MAAM;AACzB,SAAQ,YAAY,kBAAkB,WAAW,2BAA2B;AAC5E,KAAI,SAAS;EACZ,UAAU;EACV,MAAM,GAAG,OAAO,IAAI,eAAe;EACnC,KAAK,GAAG,OAAO,KAAK,OAAO,SAAS,QAAQ,EAAE;EAC9C,OAAO,GAAG,KAAK;EACf,QAAQ,GAAG,KAAK;EAChB,YAAY;EACZ,WAAW;EACX,QAAQ;EACR,QAAQ;EACR,CAAC;AACF,SAAQ,WAAW;AACnB,SAAQ,aAAa,QAAQ,SAAS;AACtC,SAAQ,aAAa,cAAc,UAAU,MAAM,QAAQ,kBAAkB,KAAK,KAAK,CAAC;AACxF,SAAQ,aAAa,gBAAgB,OAAO,SAAS,CAAC;AACtD,SAAQ,QAAQ,YAAY,OAAO,KAAK,GAAG;AAC3C,SAAQ,iBAAiB,YAAY,UAAU;AAC9C,MAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAC/C,SAAM,gBAAgB;AACtB,OAAI,WAAW,KAAK,GAAG;;GAEvB;CACF,MAAM,UAAU,GAAG,OAAO;AAC1B,KAAI,SAAS;EACZ,UAAU;EACV,MAAM;EACN,KAAK;EACL,WAAW;EACX,UAAU;EACV,YAAY;EACZ,OAAO;EACP,YAAY;EACZ,eAAe;EACf,CAAC;AACF,SAAQ,cAAc,KAAK;AAC3B,SAAQ,OAAO,QAAQ;AAEvB,OAAM,aAAa,SAAS,SAAS;AACrC,mBAAkB,SAAS,KAAK;CAGhC,MAAM,QAAQ,GAAG,MAAM;CACvB,MAAM,cAAc,qBAAqB,SAAS,KAAK,IAAI,IAAI;CAG/D,IAAI;AACJ,KAAI,MAAM,qBAAqB;EAC9B,MAAM,iBAAiB,OAAO,IAAI,OAAO,SAAS;EAClD,MAAM,aAAa,sBAAsB;AACzC,aAAW,MAAM,OAAO,GAAG,OAAO,EAAE;AACpC,aAAW,MAAM,MAAM,GAAG,eAAe;AACzC,aAAW,MAAM,aAAa;AAC9B,QAAM,aAAa,YAAY,SAAS;EAExC,MAAM,cAAc,yBAAyB,YAAY,KAAK,IAAI,OAAO,GAAG,gBAAgB,UAAU,OAAO,IAAI;EAEjH,MAAM,uBAA6B;AAClC,cAAW,MAAM,UAAU;AAC3B,cAAW,MAAM,YAAY;;EAE9B,MAAM,uBAA6B;AAClC,cAAW,MAAM,UAAU;AAC3B,cAAW,MAAM,YAAY;;AAE9B,UAAQ,iBAAiB,cAAc,eAAe;AACtD,UAAQ,iBAAiB,cAAc,eAAe;AAEtD,6BAAiC;AAChC,gBAAa;AACb,WAAQ,oBAAoB,cAAc,eAAe;AACzD,WAAQ,oBAAoB,cAAc,eAAe;;;CAI3D,MAAM,QAKF;EAAC,KAAK;EAAS,cAAc;EAAO;EAAY;AACpD,KAAI,uBAAuB,KAAA,EAC1B,OAAM,qBAAqB;AAE5B,UAAS,IAAI,KAAK,IAAI,MAAM;;AAG7B,SAAS,UAAU,QAAqB,KAAqC,KAAqB;AAEjG,QAAO,YADU,OAAO,SAAS,QAAQ,aAAa,MACzB,IAAI;;;;AC1flC,SAAgB,eAAe,OAAiD;CAC/E,MAAM,wBAAQ,IAAI,KAAqB;AACvC,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACtC,MAAM,OAAO,MAAM;AACnB,MAAI,SAAS,KAAA,EACZ,OAAM,IAAI,KAAK,IAAI,EAAE;;AAGvB,QAAO;;AAGR,SAAgB,qBAAqB,aAA4D;CAChG,MAAM,sBAAM,IAAI,KAAiC;AACjD,MAAK,MAAM,cAAc,aAAa;EACrC,MAAM,SAAS,iBAAiB,MAAM,WAAW;EACjD,MAAM,UAAU,UAAU,UAAU,OAAO,KAAK,CAAC;AACjD,MAAI,IAAI,SAAS;GAChB,MAAM,OAAO;GACb,GAAI,OAAO,UAAU,KAAA,IAAY,EAAE,GAAG,EAAC,OAAO,OAAO,OAAM;GAC3D,GAAI,OAAO,cAAc,KAAA,IAAY,EAAE,GAAG,EAAC,WAAW,OAAO,WAAU;GACvE,CAAC;;AAEH,QAAO;;AAGR,SAAgB,UAAU,MAAoB;AAC7C,QAAO,KAAK,aAAa,CAAC,MAAM,GAAG,GAAG;;AAGvC,SAAgB,qBAAqB,MAAyC;AAC7E,KAAI,SAAS,KAAA,EACZ,QAAO,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;CAGvB,MAAM,6BAAa,IAAI,KAAa;AACpC,MAAK,MAAM,OAAO,MAAM;AACvB,MAAI,CAAC,OAAO,UAAU,IAAI,IAAI,MAAM,KAAK,MAAM,EAC9C,OAAM,IAAI,MAAM,kDAAkD;AAEnE,aAAW,IAAI,IAAI;;AAEpB,QAAO;;AAGR,SAAgB,qBAAqB,OAAyC;CAC7E,MAAM,QAAQ,cAAc,MAAM;CAClC,MAAM,gCAAgB,IAAI,KAAa;CACvC,MAAM,QAAQ,CAAC,GAAG,MAAM;AACxB,QAAO,MAAM,SAAS,GAAG;EACxB,MAAM,OAAO,MAAM,KAAK;AACxB,MAAI,SAAS,KAAA,EACZ;AAED,MAAI,KAAK,SAAS,SAAS,EAC1B,eAAc,IAAI,KAAK,GAAG;AAE3B,OAAK,MAAM,SAAS,KAAK,SACxB,OAAM,KAAK,MAAM;;AAGnB,QAAO;;AAGR,SAAgB,sBAAsB,OAAyC;CAC9E,MAAM,gBAAgB,qBAAqB,MAAM;CACjD,MAAM,8BAAc,IAAI,KAAa;AACrC,MAAK,MAAM,QAAQ,MAClB,KAAI,KAAK,QAAQ,cAAc,IAAI,KAAK,GAAG,CAC1C,aAAY,IAAI,KAAK,GAAG;AAG1B,QAAO;;;;AC5ER,MAAMC,mBAAiB;AAEvB,SAAgB,eACf,gBACA,UACA,WACA,kBACA,WACO;AACP,gBAAe,iBAAiB,gBAAgB,MAAoB;AACnE,MAAI,EAAE,WAAW,EAChB;AAED,IAAE,gBAAgB;AAClB,IAAE,iBAAiB;EAEnB,MAAM,SAAS,EAAE;EACjB,MAAM,aAAa,OAAO,WAAW,SAAS,MAAM,MAAM,IAAI;EAE9D,SAAS,OAAO,IAAwB;GAEvC,IAAI,WAAW,cADJ,GAAG,UAAU;GAExB,MAAM,YAAY,UAAU;AAC5B,OAAI,YAAY,EACf,YAAW,KAAK,IAAIA,kBAAgB,KAAK,IAAI,UAAU,YAAY,iBAAiB,CAAC;AAEtF,cAAW,KAAK,IAAIA,kBAAgB,SAAS;AAC7C,YAAS,MAAM,QAAQ,GAAG,SAAS;AACnC,YAAS,MAAM,WAAW,GAAG,SAAS;AACtC,YAAS,MAAM,WAAW,GAAG,SAAS;;EAGvC,SAAS,OAAa;AACrB,UAAO,oBAAoB,eAAe,OAAO;AACjD,UAAO,oBAAoB,aAAa,KAAK;AAE7C,aADmB,OAAO,WAAW,SAAS,MAAM,MAChC,CAAC;;AAGtB,SAAO,iBAAiB,eAAe,OAAO;AAC9C,SAAO,iBAAiB,aAAa,KAAK;GACzC;;ACrCH,MAAa,oBAAoB;AACjC,MAAa,oBAAoB;AACjC,MAAM,iBAAiB;AAcvB,SAAgB,qBAAqB,SAA8C;CAClF,MAAM,EACL,WACA,cACA,gBACA,kBACA,qBACA,kBACA,wBACA,wBACA,qBACG;CAEJ,IAAI,QAAQ;AAEZ,KAAI,aAAa,EAChB,QAAO;AAGR,KAAI,mBAAmB,KACtB,SAAQ;UACE,qBAAqB,KAAA,EAC/B,SAAQ;UACE,uBAAuB,aAAa,kBAAkB;EAChE,MAAM,aAAa,KAAK,MAAM,YAAY,uBAAuB;AACjE,UAAQ,KAAK,IAAI,cAAc,KAAK,IAAI,wBAAwB,WAAW,CAAC;QACtE;EACN,MAAM,kBAAkB,KAAK,MAAM,YAAY,kBAAkB;EACjE,MAAM,kBAAkB,KAAK,MAAM,YAAY,kBAAkB;AACjE,UAAQ,KAAK,IAAI,iBAAiB,KAAK,IAAI,cAAc,gBAAgB,CAAC;;CAG3E,MAAM,aAAa,KAAK,IAAI,gBAAgB,YAAY,iBAAiB;AACzE,SAAQ,KAAK,IAAI,OAAO,WAAW;AAEnC,QAAO,KAAK,IAAI,gBAAgB,KAAK,MAAM,MAAM,CAAC;;;;ACiBnD,MAAM,WAAW;AACjB,MAAM,WAAW;AACjB,IAAa,aAAb,MAAiD;CAChD;CACA;CACA;CACA;CACA,cAA6B;CAC7B,aAAa;CACb,cAAc;CACd,SAAwB;CACxB,aAAa;CACb;CACA,iBAAoD;CACpD,kBAAiC;CAEjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,kBAAyC;CACzC;CAEA,YAAmB,WAAwB,OAAmB,OAAqB,EAAE,EAAE;AACtF,QAAA,YAAkB;AAElB,mBAAiB,MAAM,OAAO,MAAM,MAAM;AAC1C,eAAa,MAAM,OAAO,MAAM,MAAM;AAEtC,QAAA,QAAc;AACd,QAAA,QAAc,KAAK,SAAS;AAC5B,QAAA,OAAa;AACb,QAAA,YAAkB,eAAe,MAAM,MAAM;AAC7C,QAAA,SAAe,mBAAmB,KAAK,OAAO;AAC9C,QAAA,UAAgB,KAAK,eAAe,mBAAmB,MAAA,OAAa;AACpE,QAAA,uBAA6B,KAAK,iBAAiB,iBAAiB,MAAA,QAAc;AAClF,QAAA,SAAe,KAAK,UAAU;AAC9B,QAAA,mBAAyB,KAAK,oBAAA;AAC9B,QAAA,cAAoB,qBAAqB,KAAK,eAAe,MAAA,OAAa,YAAY;AACtF,QAAA,oBAA0B,qBAAqB,KAAK,eAAe,EAAE,CAAC;AACtE,QAAA,cAAoB,sBAAsB,MAAM,MAAM;AAEtD,QAAA,MAAY;GACX,WAAW,OAAa;AACvB,QAAI,MAAA,eAAqB,GACxB;AAED,UAAA,aAAmB;AACnB,SAAK,WAAW,MAAA,WAAiB;AACjC,UAAA,gBAAsB;;GAEvB,oBAAoB,YAAkB;AACrC,SAAK,oBAAoB,QAAQ;;GAElC,mBAAmB,YAAkB;AACpC,SAAK,mBAAmB,QAAQ;AAChC,SAAK,oBAAoB;KAAC,IAAI,QAAQ;KAAI,QAAQ,QAAQ;KAAO,CAAC;;GAEnE,SAAS,YAAkB;IAC1B,MAAM,MAAM,QAAQ,UAAU,aAAa,CAAC,MAAM,GAAG,GAAG;AACxD,UAAA,UAAgB,QAAQ,IAAI,EAAC,YAAY,KAAI,CAAC;AAC9C,SAAK,SAAS,QAAQ;AACtB,UAAA,gBAAsB;;GAEvB,WAAW,YAAkB;AAC5B,UAAA,UAAgB,QAAQ,IAAI,EAAC,UAAU,QAAQ,UAAS,CAAC;AACzD,SAAK,WAAW,QAAQ;AACxB,UAAA,gBAAsB;;GAEvB,wBAAwB,UAAgB;AACvC,SAAK,wBAAwB,MAAM;;GAEpC,sBAAsB,mBAAyB;AAC9C,SAAK,sBAAsB,eAAe;;GAE3C,eAAe,YAAkB;AAChC,SAAK,eAAe,QAAQ;;GAE7B;AAED,QAAA,UAAgB;AAChB,QAAA,YAAkB;AAElB,YAAU,OAAO,MAAA,KAAW;AAE5B,QAAA,YAAkB;AAElB,QAAA,2BAAiC;AACjC,QAAA,qBAA2B;AAE3B,QAAA,QAAc;;CAGf,OAAc,UAA4B;AACzC,QAAA,aAAmB;AACnB,mBAAiB,SAAS,OAAO,SAAS,MAAM;AAChD,eAAa,SAAS,OAAO,SAAS,MAAM;AAC5C,QAAA,QAAc;AACd,QAAA,YAAkB,eAAe,SAAS,MAAM;AAChD,QAAA,gBAAsB;;CAGvB,SAAgB,OAAwB;AACvC,QAAA,aAAmB;AACnB,QAAA,QAAc;AACd,QAAA,gBAAsB;;CAGvB,OAAc,IAAyB;AACtC,QAAA,aAAmB;AACnB,QAAA,aAAmB;AACnB,QAAA,KAAW,WAAW,GAAG;AACzB,MAAI,MAAA,cAAoB,MAAA,UAAgB,MAAM;AAC7C,wBAAqB,MAAA,MAAY;AACjC,SAAA,QAAc;AACd,SAAA,aAAmB;;AAEpB,QAAA,QAAc;;CAGf,cAA2B;AAC1B,QAAA,aAAmB;AACnB,QAAA,YAAkB,OAAO;AACzB,MAAI,MAAA,cAAoB,MAAA,UAAgB,MAAM;AAC7C,wBAAqB,MAAA,MAAY;AACjC,SAAA,QAAc;AACd,SAAA,aAAmB;;AAEpB,QAAA,QAAc;;CAGf,YAAyB;AACxB,QAAA,aAAmB;AACnB,QAAA,YAAkB,OAAO;AACzB,OAAK,MAAM,MAAM,qBAAqB,MAAA,MAAY,MAAM,CACvD,OAAA,YAAkB,IAAI,GAAG;AAE1B,MAAI,MAAA,cAAoB,MAAA,UAAgB,MAAM;AAC7C,wBAAqB,MAAA,MAAY;AACjC,SAAA,QAAc;AACd,SAAA,aAAmB;;AAEpB,QAAA,QAAc;;CAGf,UAAuB;AACtB,MAAI,MAAA,UACH;AAED,QAAA,YAAkB;AAClB,QAAA,SAAe,oBAAoB,UAAU,MAAA,SAAe;AAC5D,MAAI,MAAA,mBAAyB,KAC5B,OAAA,eAAqB,YAAY;MAEjC,QAAO,oBAAoB,UAAU,MAAA,0BAAgC;AAEtE,MAAI,MAAA,UAAgB,KACnB,sBAAqB,MAAA,MAAY;AAElC,QAAA,qBAA2B;AAC3B,OAAK,MAAM,EAAC,aAAa,wBAAuB,MAAA,cAAoB,YAAY,QAAQ,EAAE;AACzF,gBAAa;AACb,yBAAsB;;AAEvB,gBAAc,MAAA,UAAgB;;CAG/B,WAAW,IAAY,OAAmD;EACzE,MAAM,QAAQ,MAAA,UAAgB,IAAI,GAAG;AACrC,MAAI,UAAU,KAAA,EACb;EAED,MAAM,SAAS,MAAA,MAAY,MAAM;AACjC,MAAI,WAAW,KAAA,EACd;AAED,QAAA,MAAY,MAAM,SAAS;GAAC,GAAG;GAAQ,GAAG;GAAM;;CAGjD,oBAA6B,YAA4C;EACxE,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,OAAO,MAAA;AACb,MAAI,SAAS,QAAQ,KAAK,OAAO,QAAQ,MAAM,MAAM,KAAK,QAAQ,KAAK;AACtE,SAAA,gBAAsB;AACtB,SAAA,IAAU,mBAAmB;IAAC,IAAI,QAAQ;IAAI,QAAQ;IAAQ,SAAS;IAAgB,MAAM,QAAQ;IAAK,CAAC;AAC3G;;AAED,QAAA,gBAAsB;GAAC,IAAI,QAAQ;GAAI,MAAM;GAAI;AACjD,QAAA,IAAU,WAAW,QAAQ,GAAG;;CAGjC,kBAAiC;AAChC,GAAC,CAAC,WAAW,MAAA,aAAmB,MAAA;AAChC,QAAA,gBAAsB;;CAGvB,mCAAkD;EACjD,MAAM,gBAAgB,qBAAqB;GAC1C,WAAW,KAAK,IAAI,GAAG,MAAA,UAAgB,YAAY;GACnD,cAAc,MAAA;GACd,gBAAgB,MAAA;GAChB,kBAAkB,MAAA,KAAW;GAC7B,qBAAqB,MAAA,KAAW,uBAAuB;GACvD,kBAAkB,MAAA,KAAW,oBAAA;GAC7B,wBAAwB,MAAA,KAAW,0BAAA;GACnC,wBAAwB,MAAA,KAAW,0BAAA;GACnC,kBAAkB,MAAA;GAClB,CAAC;AACF,QAAA,SAAe,MAAM,QAAQ,GAAG,cAAc;AAC9C,QAAA,SAAe,MAAM,WAAW,GAAG,cAAc;AACjD,QAAA,SAAe,MAAM,WAAW,GAAG,cAAc;AACjD,QAAA,UAAgB,MAAM,WAAW,GAAG,MAAA,iBAAuB;;CAG5D,gBAA4B;EAE3B,MAAM,UAAU,YADF,cAAc,MAAA,MAAY,MACP,EAAE,MAAA,YAAkB;EACrD,MAAM,CAAC,SAAS,SACf,MAAA,KAAW,kBAAkB,KAAA,KAAa,MAAA,KAAW,gBAAgB,KAAA,IAClE,CAAC,MAAA,KAAW,eAAe,MAAA,KAAW,YAAY,GAClD,eAAe,SAAS,EAAE;EAE9B,MAAM,SAAS,kBAAkB,MAAA,OAAa,QAAQ;EACtD,MAAM,aAAa,KAAK,KAAK,OAAO,IAAI,MAAM,CAAC,GAAG;EAClD,MAAM,UAAU,cAAc,SAAS,OAAO;EAC9C,MAAM,QAAQ,WAAW,MAAA,MAAY,OAAO,QAAQ;EAEpD,MAAM,aAAa,MAAA,SAAe;EAClC,MAAM,WAAW,QAAQ;EACzB,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,MAAM,MAAA,YAAkB,WAAW,GAAG,SAAS;EACnF,MAAM,WAAW,KAAK,IAAI,WAAW,GAAG,KAAK,MAAM,MAAA,YAAkB,cAAc,WAAW,GAAG,WAAW,EAAE;EAC9G,MAAM,aAAa,aAAa;EAChC,MAAM,gBAAgB,KAAK,IAAI,IAAI,WAAW,IAAI,YAAY,WAAW;AAEzE,SAAO;GACN,OAAO,MAAA;GACP,OAAO,MAAA;GACP,qCAAqC,MAAA,KAAW,uCAAuC;GACvF,qBAAqB,MAAA,KAAW,uBAAuB;GACvD,aAAa,MAAA;GACb,YAAY,MAAA;GACZ,WAAW,MAAA;GACX;GACA;GACA,eAAe;GACf,aAAa;GACb;GACA;GACA;GACA;GACA;GACA;GACA;GACA,cAAc,MAAA,KAAW,gBAAgB;GACzC,aAAa,MAAA;GACb,mBAAmB,MAAA;GACnB,QAAQ,MAAA;GACR;;CAGF,gBAA+B;AAC9B,QAAA,aAAmB;EACnB,MAAM,QAAQ,MAAA,cAAoB;AAElC,mBAAiB,MAAA,aAAmB,MAAM;AAC1C,iBACC,MAAA,UACA,OACA;GACC,WAAW,OAAO;AACjB,QAAI,MAAA,YAAkB,IAAI,GAAG,CAC5B,OAAA,YAAkB,OAAO,GAAG;QAE5B,OAAA,YAAkB,IAAI,GAAG;AAE1B,UAAA,gBAAsB;;GAEvB,WAAW,OAAO,MAAA,IAAU,WAAW,GAAG;GAC1C,aAAa,YAAY;AACxB,UAAA,gBAAsB,QAAQ;;GAE/B,mBAAmB,YAAY,MAAA,IAAU,mBAAmB,QAAQ;GACpE,QAAQ,OAAO,MAAA,IAAU,QAAQ,EAAC,UAAU,IAAG,CAAC;GAChD,EACD,MAAA,QACA;AACD,kBAAgB,MAAA,eAAqB,OAAO,MAAA,IAAU;;CAGvD,kBAAwB;AACvB,MAAI,MAAA,cAAoB,MAAA,UACvB;AAED,QAAA,aAAmB;AACnB,QAAA,QAAc,sBAAsB,MAAA,OAAa;;CAGlD,cAAoB;EACnB,MAAM,QAAQ,MAAA,KAAW,SAAS;AAClC,QAAA,UAAgB,QAAQ,WAAW;;CAGpC,eAAqB;AACpB,MAAI,MAAA,UACH,OAAM,IAAI,WAAW,sBAAsB,+BAA+B;;CAI5E,YAAkB;EACjB,MAAM,OAAO,GAAG,MAAM;AACtB,OAAK,YAAY;AACjB,MAAI,MAAM;GACT,QAAQ,GAAG,MAAA,OAAa;GACxB,UAAU;GACV,SAAS;GACT,eAAe;GACf,YAAY;GACZ,YAAY;GACZ,CAAC;AACF,QAAA,OAAa;EAEb,MAAM,WAAW,GAAG,MAAM;AAC1B,MAAI,UAAU;GAAC,MAAM;GAAK,UAAU;GAAQ,UAAU;GAAY,SAAS;GAAO,CAAC;AACnF,OAAK,OAAO,SAAS;AACrB,QAAA,WAAiB;EAEjB,MAAM,WAAW,GAAG,MAAM;AAC1B,WAAS,QAAQ,UAAU;AAC3B,MAAI,UAAU;GACb,OAAO,GAAG,MAAA,qBAA2B;GACrC,YAAY;GACZ,UAAU;GACV,MAAM;GACN,QAAQ;GACR,YAAY;GACZ,aAAa;GACb,CAAC;AACF,QAAA,WAAiB;EAEjB,MAAM,aAAa,GAAG,MAAM;AAC5B,MAAI,YAAY;GAAC,UAAU;GAAU,KAAK;GAAK,QAAQ;GAAM,YAAY;GAAyB,CAAC;EACnG,MAAM,WAAW,oBAAoB,MAAA,QAAc;AACnD,aAAW,OAAO,SAAS;AAC3B,WAAS,OAAO,WAAW;EAE3B,MAAM,WAAW,GAAG,MAAM;AAC1B,WAAS,OAAO,SAAS;AACzB,QAAA,WAAiB;AAEjB,QAAA,sBAA4B,kBAAkB,UAAU,UAAU,MAAA,UAAgB,YAAY;AAC7F,SAAA,IAAU,sBAAsB,QAAQ;IACvC;AAEF,WAAS,OAAO,SAAS;EAEzB,MAAM,YAAY,GAAG,MAAM;AAC3B,YAAU,QAAQ,UAAU;AAC5B,MAAI,WAAW;GAAC,YAAY;GAAK,UAAU;GAAY,UAAU,GAAG,MAAA,iBAAuB;GAAI,CAAC;AAChG,QAAA,YAAkB;EAElB,MAAM,cAAc,GAAG,MAAM;AAC7B,MAAI,aAAa;GAAC,UAAU;GAAU,KAAK;GAAK,QAAQ;GAAK,YAAY;GAAyB,CAAC;AACnG,YAAU,OAAO,YAAY;AAC7B,QAAA,cAAoB;AAEpB,QAAA,gBAAsB,qBAAqB;AAC3C,YAAU,OAAO,MAAA,cAAoB,gBAAgB;AACrD,WAAS,OAAO,UAAU;EAE1B,MAAM,iBAAiB,GAAG,MAAM;AAChC,iBAAe,YAAY;AAC3B,MAAI,gBAAgB;GACnB,UAAU;GACV,OAAO;GACP,KAAK;GACL,QAAQ;GACR,OAAO;GACP,QAAQ;GACR,QAAQ;GACR,CAAC;AACF,WAAS,OAAO,eAAe;AAE/B,iBAAe,gBAAgB,UAAU,MAAA,WAAiB,MAAA,mBAAyB,eAAe;AACjG,SAAA,iBAAuB;AACvB,SAAA,IAAU,wBAAwB,WAAW;IAC5C;;CAGH,cAAoB;AACnB,QAAA,cAAoB,cAAc,iBAAiB,UAAU,UAAU;AAEtE,OADe,MAAM,OACV,QAAQ,qDAAqD,CACvE;AAED,SAAA,IAAU,WAAW,KAAK;IACzB;AAEF,QAAA,KAAW,iBAAiB,YAAY,UAAU;AACjD,OAAI,MAAM,QAAQ,YAAY,MAAA,eAAqB,MAAM;AACxD,UAAM,gBAAgB;AACtB,UAAA,IAAU,WAAW,KAAK;;IAE1B;AAEF,QAAA,SAAe,iBAAiB,UAAU,MAAA,SAAe;;CAG1D,uBAA6B;AAC5B,MAAI,OAAO,mBAAmB,aAAa;AAC1C,SAAA,iBAAuB,IAAI,qBAAqB;AAC/C,UAAA,2BAAiC;KAChC;AACF,SAAA,eAAqB,QAAQ,MAAA,UAAgB;QAE7C,QAAO,iBAAiB,UAAU,MAAA,0BAAgC"}
@@ -0,0 +1,238 @@
1
+ /* src/styles/gantt.css */
2
+
3
+ /* ─── Design tokens ──────────────────────────────────────────────────────── */
4
+ :root {
5
+ --gantt-font: "Inter", "Segoe UI", system-ui, sans-serif;
6
+
7
+ /* Bar colours */
8
+ --gantt-task: #4c6ef5;
9
+ --gantt-project: #2ecc97;
10
+ --gantt-milestone: #9b59b6;
11
+ --gantt-selection: #ff6b35;
12
+ --gantt-selection-ring: rgba(255, 107, 53, 0.32);
13
+ --gantt-selection-glow: rgba(255, 107, 53, 0.16);
14
+
15
+ /* Connector colours */
16
+ --gantt-link: #adb5bd;
17
+ --gantt-link-hi: #ff6b35;
18
+
19
+ /* Structure */
20
+ --gantt-row-height: 44px;
21
+ --gantt-bar-height: 28px;
22
+ --gantt-milestone-size: 20px;
23
+ --gantt-bg: #ffffff;
24
+ --gantt-header-bg: #f8f9fa;
25
+ --gantt-stripe: #fafbfc;
26
+ --gantt-border: #e9ecef;
27
+ --gantt-grid-line: #f1f3f5;
28
+
29
+ /* Container framing */
30
+ --gantt-container-border-radius: 6px;
31
+ --gantt-container-border: 1px solid var(--gantt-border);
32
+ --gantt-container-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
33
+ --gantt-today: rgba(255, 107, 53, 0.35);
34
+ --gantt-row-selected: #edf2ff;
35
+ --gantt-day-weekend: rgba(73, 80, 87, 0.06);
36
+ --gantt-day-holiday: rgba(250, 82, 82, 0.1);
37
+ --gantt-day-custom: rgba(34, 139, 230, 0.1);
38
+
39
+ /* Typography scale */
40
+ --gantt-font-size-xs: 11px;
41
+ --gantt-font-size-sm: 12px;
42
+ --gantt-font-size-md: 13px;
43
+ --gantt-font-size-lg: 16px;
44
+ --gantt-font-weight-normal: 400;
45
+ --gantt-font-weight-semibold: 600;
46
+ --gantt-font-weight-bold: 700;
47
+ --gantt-letter-spacing-tight: 0.04em;
48
+ --gantt-letter-spacing-wide: 0.05em;
49
+
50
+ /* Text */
51
+ --gantt-text: #212529;
52
+ --gantt-text-secondary: #868e96;
53
+ --gantt-bar-label-color: #ffffff;
54
+ }
55
+
56
+ /* ─── Root container framing ─────────────────────────────────────────────── */
57
+ .gantt-root {
58
+ border: var(--gantt-container-border);
59
+ border-radius: var(--gantt-container-border-radius);
60
+ box-shadow: var(--gantt-container-shadow);
61
+ }
62
+
63
+ /* ─── Dark mode (explicit data-theme attribute) ──────────────────────────── */
64
+ [data-theme="dark"] {
65
+ --gantt-bg: #1a1b23;
66
+ --gantt-header-bg: #212231;
67
+ --gantt-stripe: #1e1f2a;
68
+ --gantt-border: #2d3044;
69
+ --gantt-grid-line: #252738;
70
+ --gantt-text: #e2e4e9;
71
+ --gantt-text-secondary: #8b8fa3;
72
+ --gantt-container-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
73
+ --gantt-today: rgba(255, 107, 53, 0.45);
74
+ --gantt-row-selected: #252844;
75
+ --gantt-day-weekend: rgba(73, 80, 87, 0.15);
76
+ --gantt-day-holiday: rgba(250, 82, 82, 0.15);
77
+ --gantt-day-custom: rgba(34, 139, 230, 0.15);
78
+ --gantt-link: #4a4d66;
79
+ --gantt-bar-label-color: #e2e4e9;
80
+ }
81
+
82
+ /* ─── Dark mode (prefers-color-scheme fallback for system theme) ─────────── */
83
+ @media (prefers-color-scheme: dark) {
84
+ [data-theme="system"] {
85
+ --gantt-bg: #1a1b23;
86
+ --gantt-header-bg: #212231;
87
+ --gantt-stripe: #1e1f2a;
88
+ --gantt-border: #2d3044;
89
+ --gantt-grid-line: #252738;
90
+ --gantt-text: #e2e4e9;
91
+ --gantt-text-secondary: #8b8fa3;
92
+ --gantt-container-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
93
+ --gantt-today: rgba(255, 107, 53, 0.45);
94
+ --gantt-row-selected: #252844;
95
+ --gantt-day-weekend: rgba(73, 80, 87, 0.15);
96
+ --gantt-day-holiday: rgba(250, 82, 82, 0.15);
97
+ --gantt-day-custom: rgba(34, 139, 230, 0.15);
98
+ --gantt-link: #4a4d66;
99
+ --gantt-bar-label-color: #e2e4e9;
100
+ }
101
+ }
102
+
103
+ /* ─── Bar states ─────────────────────────────────────────────────────────── */
104
+ .gantt-bar {
105
+ transition:
106
+ filter 0.1s ease,
107
+ transform 0.05s ease;
108
+ will-change: transform;
109
+ }
110
+
111
+ .gantt-bar:hover {
112
+ filter: brightness(1.07);
113
+ }
114
+
115
+ .gantt-bar:active,
116
+ .gantt-bar--selected {
117
+ filter: brightness(1.1);
118
+ }
119
+
120
+ .gantt-shape--selected {
121
+ box-shadow:
122
+ 0 0 0 1px var(--gantt-selection-ring),
123
+ 0 1px 4px var(--gantt-selection-glow);
124
+ }
125
+
126
+ .gantt-bar--selected span {
127
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
128
+ }
129
+
130
+ .gantt-resize-handle:hover {
131
+ background: rgba(255, 255, 255, 0.35);
132
+ }
133
+
134
+ /* ─── Milestone ──────────────────────────────────────────────────────────── */
135
+ .gantt-milestone {
136
+ transition:
137
+ transform 0.1s ease,
138
+ filter 0.1s ease;
139
+ }
140
+
141
+ .gantt-milestone:hover {
142
+ filter: brightness(1.12);
143
+ transform: rotate(45deg) scale(1.1) !important;
144
+ }
145
+
146
+ /* ─── Row affordances (toggle + add) ─────────────────────────────────────── */
147
+ .gantt-toggle,
148
+ .gantt-add-btn {
149
+ opacity: 0.4;
150
+ transition:
151
+ opacity 0.15s ease,
152
+ color 0.1s ease,
153
+ background 0.15s ease;
154
+ border-radius: 3px;
155
+ }
156
+
157
+ .gantt-row:hover .gantt-toggle,
158
+ .gantt-row:hover .gantt-add-btn {
159
+ opacity: 1;
160
+ }
161
+
162
+ .gantt-toggle:hover,
163
+ .gantt-add-btn:hover {
164
+ opacity: 1 !important;
165
+ color: var(--gantt-task) !important;
166
+ background: var(--gantt-row-selected) !important;
167
+ }
168
+
169
+ .gantt-toggle:focus-visible,
170
+ .gantt-add-btn:focus-visible {
171
+ opacity: 1;
172
+ color: var(--gantt-task) !important;
173
+ background: var(--gantt-row-selected) !important;
174
+ outline: 2px solid var(--gantt-task);
175
+ outline-offset: 1px;
176
+ }
177
+
178
+ /* ─── Scrollbar styling (webkit) ─────────────────────────────────────────── */
179
+ .gantt-root ::-webkit-scrollbar {
180
+ width: 8px;
181
+ height: 8px;
182
+ }
183
+ .gantt-root ::-webkit-scrollbar-track {
184
+ background: var(--gantt-header-bg);
185
+ }
186
+ .gantt-root ::-webkit-scrollbar-thumb {
187
+ background: #ced4da;
188
+ border-radius: 4px;
189
+ }
190
+ .gantt-root ::-webkit-scrollbar-thumb:hover {
191
+ background: #adb5bd;
192
+ }
193
+
194
+ /* ─── Timeline day backgrounds ──────────────────────────────────────────── */
195
+ .gantt-day-cell--weekend {
196
+ background: var(--gantt-day-weekend);
197
+ }
198
+
199
+ .gantt-day-cell--holiday {
200
+ background: var(--gantt-day-holiday);
201
+ }
202
+
203
+ .gantt-day-cell--custom {
204
+ background: var(--gantt-day-custom);
205
+ }
206
+
207
+ /* ─── Splitter handle ───────────────────────────────────────────────────── */
208
+ .gantt-splitter-handle {
209
+ transition: background 0.15s ease;
210
+ }
211
+
212
+ .gantt-splitter-handle:hover {
213
+ background: var(--gantt-task) !important;
214
+ }
215
+
216
+ /* ─── Column resize handle ──────────────────────────────────────────────── */
217
+ .gantt-col-resize-handle {
218
+ transition: background 0.15s ease;
219
+ }
220
+
221
+ .gantt-col-resize-handle:hover {
222
+ background: var(--gantt-task) !important;
223
+ }
224
+
225
+ /* ─── Link-creation endpoint handles ─────────────────────────────────────── */
226
+ .gantt-link-endpoint:hover {
227
+ opacity: 1 !important;
228
+ transform: translate(-50%, -50%) scale(1.4) !important;
229
+ background: var(--gantt-link-hi) !important;
230
+ box-shadow: 0 0 0 2px var(--gantt-selection-ring);
231
+ }
232
+
233
+ .gantt-link-endpoint:focus-visible {
234
+ opacity: 1;
235
+ transform: translate(-50%, -50%) scale(1) !important;
236
+ outline: 2px solid var(--gantt-task);
237
+ outline-offset: 2px;
238
+ }