miriad-viz 0.9.3 → 0.9.4

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.
package/dist-cli/index.js CHANGED
@@ -525,6 +525,7 @@ function computeNext(progress, existingFiles, logTails, flags = {}, dataSummary,
525
525
  return {
526
526
  ...chainResult,
527
527
  action: "completed_and_chained",
528
+ completedSteps: ["script", ...chainResult.completedSteps ?? []],
528
529
  output: [...output, ...chainResult.output]
529
530
  };
530
531
  }
@@ -550,7 +551,7 @@ function computeNext(progress, existingFiles, logTails, flags = {}, dataSummary,
550
551
  output.push(" \u2502 \u2502");
551
552
  output.push(" \u2502 When approved: npx miriad-viz next --script-approved \u2502");
552
553
  output.push(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F");
553
- return { action: "creative_stop", step, progress, output };
554
+ return { action: "creative_stop", step, progress, output, completedSteps: [step] };
554
555
  }
555
556
  output.push("", "\u270F\uFE0F Script writing in progress.");
556
557
  output.push("", " Write your script to: data/script.json");
@@ -648,6 +649,7 @@ function computeNext(progress, existingFiles, logTails, flags = {}, dataSummary,
648
649
  return {
649
650
  ...chainResult,
650
651
  action: "completed_and_chained",
652
+ completedSteps: ["curate", ...chainResult.completedSteps ?? []],
651
653
  output: [...output, ...chainResult.output]
652
654
  };
653
655
  }
@@ -673,7 +675,7 @@ function computeNext(progress, existingFiles, logTails, flags = {}, dataSummary,
673
675
  output.push(" \u2502 \u2502");
674
676
  output.push(" \u2502 When approved: npx miriad-viz next --curate-approved \u2502");
675
677
  output.push(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F");
676
- return { action: "creative_stop", step, progress, output };
678
+ return { action: "creative_stop", step, progress, output, completedSteps: [step] };
677
679
  }
678
680
  const existing = CURATE_OUTPUT_FILES.filter((f) => fileSet.has(f));
679
681
  const missing = CURATE_OUTPUT_FILES.filter((f) => !fileSet.has(f));
@@ -738,6 +740,66 @@ function computeNext(progress, existingFiles, logTails, flags = {}, dataSummary,
738
740
  output.push(" - Add standout quotes with speaker, text, and timestamp");
739
741
  output.push(" 5. Present to human and iterate");
740
742
  output.push("");
743
+ output.push(" \u2501\u2501\u2501 SCHEMA EXAMPLES (copy these as starting templates) \u2501\u2501\u2501");
744
+ output.push("");
745
+ output.push(" \u{1F4C4} timeline-events.json:");
746
+ output.push(" ```json");
747
+ output.push(" [");
748
+ output.push(' { "type": "message", "t": "2026-02-20T14:30:00Z", "from": "bob",');
749
+ output.push(' "to": "snorre", "content": "Chat pill label shown on screen" },');
750
+ output.push(' { "type": "beam", "t": "2026-02-20T15:00:00Z", "from": "lead",');
751
+ output.push(' "to": "bob", "content": "Task assignment beam" },');
752
+ output.push(' { "type": "milestone", "t": "2026-02-20T16:00:00Z",');
753
+ output.push(' "label": "PR #42 merged", "agent": "bob", "style": "achievement" }');
754
+ output.push(" ]");
755
+ output.push(" ```");
756
+ output.push(
757
+ ' \u26A0\uFE0F type MUST be "message" or "beam" for chat pills. "milestone" renders differently.'
758
+ );
759
+ output.push("");
760
+ output.push(" \u{1F4C4} retro-page-data.json:");
761
+ output.push(" ```json");
762
+ output.push(" {");
763
+ output.push(' "meta": {');
764
+ output.push(' "title": "Project Name",');
765
+ output.push(' "subtitle": "A story of building something",');
766
+ output.push(' "startDate": "2026-02-20T00:00:00Z",');
767
+ output.push(' "endDate": "2026-02-21T00:00:00Z"');
768
+ output.push(" },");
769
+ output.push(' "teamAssembly": [');
770
+ output.push(' { "agent": "bob", "role": "Builder", "time": "2026-02-20T01:00:00Z" }');
771
+ output.push(" ],");
772
+ output.push(' "phases": [');
773
+ output.push(' { "id": "discovery", "label": "Discovery",');
774
+ output.push(' "startTime": "2026-02-20T00:00:00Z", "endTime": "2026-02-20T12:00:00Z" }');
775
+ output.push(" ],");
776
+ output.push(' "milestones": [');
777
+ output.push(
778
+ ' { "label": "First commit", "time": "2026-02-20T02:00:00Z", "phase": "discovery" }'
779
+ );
780
+ output.push(" ],");
781
+ output.push(' "narration": []');
782
+ output.push(" }");
783
+ output.push(" ```");
784
+ output.push(
785
+ " \u26A0\uFE0F meta.startDate and meta.endDate are REQUIRED. phases need startTime/endTime."
786
+ );
787
+ output.push(' \u26A0\uFE0F teamAssembly uses "agent" (not "name") and must have "time".');
788
+ output.push("");
789
+ output.push(" \u{1F4C4} retro-page-quotes.json:");
790
+ output.push(" ```json");
791
+ output.push(" {");
792
+ output.push(' "good": [');
793
+ output.push(
794
+ ' { "text": "Quote text here", "author": "bob", "timestamp": "2026-02-20T14:30:00Z" }'
795
+ );
796
+ output.push(" ],");
797
+ output.push(' "bad": [],');
798
+ output.push(' "funny": []');
799
+ output.push(" }");
800
+ output.push(" ```");
801
+ output.push(" \u2501\u2501\u2501 END SCHEMA EXAMPLES \u2501\u2501\u2501");
802
+ output.push("");
741
803
  output.push(" \u2705 Curation checklist:");
742
804
  output.push(" \u25A1 Every narration line has at least one labeled event in its time range");
743
805
  output.push(" \u25A1 Milestone labels are short (< 60 chars) and make sense as chat pills");
@@ -770,6 +832,7 @@ function computeNext(progress, existingFiles, logTails, flags = {}, dataSummary,
770
832
  return {
771
833
  ...chainResult,
772
834
  action: "completed_and_chained",
835
+ completedSteps: ["voices", ...chainResult.completedSteps ?? []],
773
836
  output: [...output, ...chainResult.output]
774
837
  };
775
838
  }
@@ -799,6 +862,7 @@ function computeNext(progress, existingFiles, logTails, flags = {}, dataSummary,
799
862
  return {
800
863
  ...chainResult,
801
864
  action: "completed_and_chained",
865
+ completedSteps: ["voices", ...chainResult.completedSteps ?? []],
802
866
  output: [...output, ...chainResult.output]
803
867
  };
804
868
  }
@@ -906,6 +970,7 @@ function computeNext(progress, existingFiles, logTails, flags = {}, dataSummary,
906
970
  return {
907
971
  ...chainResult,
908
972
  action: "completed_and_chained",
973
+ completedSteps: ["viz-timing", ...chainResult.completedSteps ?? []],
909
974
  output: [...output, ...chainResult.output]
910
975
  };
911
976
  }
@@ -938,6 +1003,7 @@ function computeNext(progress, existingFiles, logTails, flags = {}, dataSummary,
938
1003
  return {
939
1004
  ...chainResult,
940
1005
  action: "completed_and_chained",
1006
+ completedSteps: ["sound-design", ...chainResult.completedSteps ?? []],
941
1007
  output: [...output, ...chainResult.output]
942
1008
  };
943
1009
  }
@@ -977,7 +1043,7 @@ function computeNext(progress, existingFiles, logTails, flags = {}, dataSummary,
977
1043
  output.push("");
978
1044
  output.push(" \u26D4 CREATIVE STOP \u2014 Present results to the human and wait for approval.");
979
1045
  output.push(" Only run `npx miriad-viz next` after human approves.");
980
- return { action: "creative_stop", step, progress, output };
1046
+ return { action: "creative_stop", step, progress, output, completedSteps: [step] };
981
1047
  }
982
1048
  output.push(
983
1049
  "",
@@ -987,6 +1053,7 @@ function computeNext(progress, existingFiles, logTails, flags = {}, dataSummary,
987
1053
  return {
988
1054
  ...chainResult,
989
1055
  action: "completed_and_chained",
1056
+ completedSteps: [step, ...chainResult.completedSteps ?? []],
990
1057
  output: [...output, ...chainResult.output]
991
1058
  };
992
1059
  }
@@ -1649,7 +1716,10 @@ async function runNext(flags) {
1649
1716
  const dataDir2 = resolve3(projectDir, result.progress.project.dataDir);
1650
1717
  const outputDir = resolve3(projectDir, result.progress.project.outputDir);
1651
1718
  if (result.step && (result.action === "completed_and_chained" || result.action === "creative_stop")) {
1652
- await syncStepOutputs(syncConfig, result.step, dataDir2, outputDir);
1719
+ const stepsToSync = result.completedSteps?.length ? result.completedSteps : [result.step];
1720
+ for (const syncStep of new Set(stepsToSync)) {
1721
+ await syncStepOutputs(syncConfig, syncStep, dataDir2, outputDir);
1722
+ }
1653
1723
  } else {
1654
1724
  const progressPath = resolve3(projectDir, ".miriad-viz.json");
1655
1725
  if (existsSync3(progressPath)) {
@@ -1716,7 +1786,7 @@ async function main() {
1716
1786
  case "transform": {
1717
1787
  const { projectDir, progress } = requireProject();
1718
1788
  const { parseDuration } = await import("./parse-duration-NVLCEFAF.js");
1719
- const { runTransform } = await import("./transform-L7OKE35T.js");
1789
+ const { runTransform } = await import("./transform-3YUJZORK.js");
1720
1790
  const padding = {};
1721
1791
  if (typeof flags["pad-start"] === "string") {
1722
1792
  padding.padStartMs = parseDuration(flags["pad-start"]);
@@ -1744,7 +1814,7 @@ async function main() {
1744
1814
  console.log("");
1745
1815
  console.log(chainResult.previewTable);
1746
1816
  }
1747
- const { runTransform } = await import("./transform-L7OKE35T.js");
1817
+ const { runTransform } = await import("./transform-3YUJZORK.js");
1748
1818
  await runTransform({ projectDir, progress });
1749
1819
  const { runPreview: runPreview2 } = await import("./preview-7AGBBMPI.js");
1750
1820
  await runPreview2({ projectDir, progress, port, noOpen: flags["no-open"] === true });
@@ -52,12 +52,6 @@ function deriveTimeRange(rawData, curationData, options) {
52
52
  }
53
53
  for (const m of rawData.messages) timestamps.push(m.timestamp);
54
54
  for (const a of rawData.agents) timestamps.push(a.joinedAt);
55
- for (const p of curationData.phases) {
56
- timestamps.push(p.startTime);
57
- timestamps.push(p.endTime);
58
- }
59
- for (const m of curationData.milestones) timestamps.push(m.timestamp);
60
- for (const q of curationData.quotes) timestamps.push(q.timestamp);
61
55
  if (timestamps.length === 0) {
62
56
  throw new Error("processData: no timestamps found in data \u2014 cannot derive time range");
63
57
  }
@@ -477,11 +471,25 @@ function parseInformalTimestamp(timeStr, referenceYear) {
477
471
  return date.getTime();
478
472
  }
479
473
  function findPhaseForTimestamp(timestamp, phases) {
480
- return phases.find((p) => {
474
+ if (phases.length === 0) return void 0;
475
+ const exact = phases.find((p) => {
481
476
  const start = new Date(p.start).getTime();
482
477
  const end = new Date(p.end).getTime();
483
478
  return timestamp >= start && timestamp <= end;
484
479
  });
480
+ if (exact) return exact;
481
+ let nearest;
482
+ let minDistance = Number.POSITIVE_INFINITY;
483
+ for (const p of phases) {
484
+ const start = new Date(p.start).getTime();
485
+ const end = new Date(p.end).getTime();
486
+ const distance = Math.min(Math.abs(timestamp - start), Math.abs(timestamp - end));
487
+ if (distance < minDistance) {
488
+ minDistance = distance;
489
+ nearest = p;
490
+ }
491
+ }
492
+ return nearest;
485
493
  }
486
494
 
487
495
  // src/cli/pipeline.ts
@@ -859,12 +859,6 @@ function deriveTimeRange(rawData, curationData, options) {
859
859
  }
860
860
  for (const m of rawData.messages) timestamps.push(m.timestamp);
861
861
  for (const a of rawData.agents) timestamps.push(a.joinedAt);
862
- for (const p of curationData.phases) {
863
- timestamps.push(p.startTime);
864
- timestamps.push(p.endTime);
865
- }
866
- for (const m of curationData.milestones) timestamps.push(m.timestamp);
867
- for (const q of curationData.quotes) timestamps.push(q.timestamp);
868
862
  if (timestamps.length === 0) {
869
863
  throw new Error("processData: no timestamps found in data \u2014 cannot derive time range");
870
864
  }
@@ -1498,5 +1492,5 @@ exports.computePRPeakRows = computePRPeakRows;
1498
1492
  exports.enrichVizData = enrichVizData;
1499
1493
  exports.getFrameState = getFrameState;
1500
1494
  exports.processData = processData;
1501
- //# sourceMappingURL=chunk-AQ5ZU7P2.cjs.map
1502
- //# sourceMappingURL=chunk-AQ5ZU7P2.cjs.map
1495
+ //# sourceMappingURL=chunk-DZYFC24M.cjs.map
1496
+ //# sourceMappingURL=chunk-DZYFC24M.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/engine/pack-rows.ts","../src/engine/get-frame-state.ts","../src/processing/process-data.ts","../src/processing/agent-layout.ts","../src/processing/enrich.ts","../src/renderer/band-sizing.ts"],"names":["SIM_HOURS_PER_SECOND","AGENT_SHADER_NODE_RADIUS","p","PILL_GAP_RATIO","PILL_GAP_BASE"],"mappings":";;;;;AAqBA,IAAM,kBAAA,GAAqB,IAAA;AAM3B,IAAM,YAAA,GAAe,IAAA;AAMrB,IAAM,kBAAA,GAAqB,IAAA;AAmBpB,SAAS,gBAAA,CACd,IAAA,EACA,QAAA,EACA,UAAA,EACA,eAAA,EACe;AACf,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,YAAA,EAAc,cAAc,YAAY,CAAA;AAC7D,EAAA,MAAM,iBAAA,GAAoB,IAAA,CAAK,GAAA,CAAI,kBAAA,EAAoB,mBAAmB,kBAAkB,CAAA;AAE5F,EAAA,MAAM,UAAoB,EAAC;AAE3B,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,MAAM,QAAQ,GAAA,CAAI,cAAA;AAClB,IAAA,IAAI,KAAA,GAAQ,CAAA,IAAK,KAAA,GAAQ,CAAA,EAAG;AAC1B,MAAA,GAAA,CAAI,GAAA,GAAM,CAAA;AACV,MAAA;AAAA,IACF;AAGA,IAAA,MAAM,SAAS,KAAA,GAAQ,iBAAA;AACvB,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,QAAQ,CAAA;AAGrC,IAAA,IAAI,GAAA,GAAM,CAAA;AACV,IAAA,KAAK,GAAA,GAAM,CAAA,EAAG,GAAA,GAAM,OAAA,CAAQ,QAAQ,GAAA,EAAA,EAAO;AACzC,MAAA,IAAI,OAAA,CAAQ,GAAG,CAAA,IAAK,KAAA,EAAO;AAAA,IAC7B;AAEA,IAAA,IAAI,GAAA,IAAO,QAAQ,MAAA,EAAQ;AACzB,MAAA,OAAA,CAAQ,KAAK,EAAE,CAAA;AAAA,IACjB;AACA,IAAA,OAAA,CAAQ,GAAG,IAAI,GAAA,GAAM,GAAA;AACrB,IAAA,GAAA,CAAI,GAAA,GAAM,GAAA;AAAA,EACZ;AAEA,EAAA,OAAO,IAAA;AACT;AAiBO,SAAS,UAAA,CACd,IAAA,EACA,QAAA,EACA,UAAA,EACA,eAAA,EACS;AACT,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,YAAA,EAAc,cAAc,YAAY,CAAA;AAC7D,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,kBAAA,EAAoB,mBAAmB,kBAAkB,CAAA;AAGnF,EAAA,IAAA,CAAK,KAAK,CAAC,CAAA,EAAG,MAAM,CAAA,CAAE,gBAAA,GAAmB,EAAE,gBAAgB,CAAA;AAG3D,EAAA,MAAM,UAAoB,EAAC;AAE3B,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,MAAM,QAAQ,GAAA,CAAI,gBAAA;AAClB,IAAA,IAAI,KAAA,GAAQ,CAAA,IAAK,KAAA,GAAQ,CAAA,EAAG;AAC1B,MAAA,GAAA,CAAI,GAAA,GAAM,CAAA;AACV,MAAA;AAAA,IACF;AAKA,IAAA,MAAM,MAAA,GAAS,IAAI,gBAAA,IAAoB,QAAA;AACvC,IAAA,MAAM,KAAA,GAAQ,KAAK,GAAA,CAAI,kBAAA,EAAoB,KAAK,GAAA,CAAI,QAAA,EAAU,MAAA,GAAS,KAAK,CAAC,CAAA;AAC7E,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,KAAA,GAAQ,OAAO,QAAQ,CAAA;AAG5C,IAAA,IAAI,GAAA,GAAM,CAAA;AACV,IAAA,KAAK,GAAA,GAAM,CAAA,EAAG,GAAA,GAAM,OAAA,CAAQ,QAAQ,GAAA,EAAA,EAAO;AACzC,MAAA,IAAI,OAAA,CAAQ,GAAG,CAAA,IAAK,KAAA,EAAO;AAAA,IAC7B;AAEA,IAAA,IAAI,GAAA,IAAO,QAAQ,MAAA,EAAQ;AACzB,MAAA,OAAA,CAAQ,KAAK,EAAE,CAAA;AAAA,IACjB;AACA,IAAA,OAAA,CAAQ,GAAG,IAAI,GAAA,GAAM,GAAA;AACrB,IAAA,GAAA,CAAI,GAAA,GAAM,GAAA;AAAA,EACZ;AAEA,EAAA,OAAO,IAAA;AACT;;;ACzGA,IAAM,mBAAA,GAAsB,IAAA;AAM5B,IAAM,eAAA,GAAkB,KAAA;AAMxB,IAAM,oBAAA,GAAuB,CAAA;AAG7B,IAAM,eAAA,GAAkB,IAAA;AAGxB,IAAM,qBAAA,GAAwB,GAAA;AAO9B,IAAM,WAAA,GAAc,KAAA;AAWpB,IAAM,YAAA,GAAuC;AAAA,EAC3C,MAAA,EAAQ,CAAA;AAAA,EACR,UAAA,EAAY,CAAA;AAAA,EACZ,SAAA,EAAW,CAAA;AAAA,EACX,OAAA,EAAS,CAAA;AAAA,EACT,QAAA,EAAU;AACZ,CAAA;AAMA,IAAM,kBAAA,GAAqB,EAAA;AAG3B,IAAM,gBAAA,GAAmB,CAAA;AAEzB,IAAM,kBAAA,GAAqB,KAAA;AAE3B,IAAM,uBAAA,GAA0B,CAAA;AAShC,SAAS,mBAAA,CAAoB,aAAqB,UAAA,EAA4B;AAC5E,EAAA,MAAM,aAAa,UAAA,GAAa,IAAA;AAChC,EAAA,MAAM,kBAAkB,UAAA,GAAaA,sCAAA;AACrC,EAAA,OAAO,WAAA,GAAc,eAAA;AACvB;AAaA,SAAS,mBAAmB,UAAA,EAK1B;AAEA,EAAA,MAAM,eAAA,GAAkB,mBAAA,CAAoB,gBAAA,EAAkB,UAAU,CAAA;AACxE,EAAA,MAAM,uBAAuB,eAAA,GAAkB,kBAAA;AAC/C,EAAA,MAAM,4BAA4B,eAAA,GAAkB,uBAAA;AAGpD,EAAA,MAAM,cAAA,GAAiB,mBAAA,CAAoB,oBAAA,EAAsB,UAAU,CAAA;AAE3E,EAAA,OAAO,EAAE,eAAA,EAAiB,oBAAA,EAAsB,yBAAA,EAA2B,cAAA,EAAe;AAC5F;AAGA,IAAM,iBAAA,GAAoB,IAAA;AAK1B,IAAM,kBAAA,GAAqB,IAAA;AAM3B,IAAM,cAAA,GAAiB,CAAA;AAavB,IAAM,eAAA,GAAkB,CAAA;AAGxB,IAAM,iBAAA,GAAoB,IAAA;AAM1B,IAAM,eAAA,GAAkBC,0CAAA;AAGxB,IAAM,qBAAA,GAAwB,GAAA;AAG9B,IAAM,eAAA,GAAkB,CAAA;AAGxB,IAAM,cAAA,GAAiB,GAAA;AAGvB,IAAM,eAAA,GAAkB,IAAA;AAcxB,SAAS,kBAAA,CAAmB,SAAiB,MAAA,EAAmC;AAC9E,EAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACtB,IAAA,IAAI,CAAA,CAAE,KAAA,KAAU,OAAA,IAAW,CAAA,CAAE,gBAAgB,OAAA,EAAS;AACpD,MAAA,OAAO,CAAA,CAAE,cAAA;AAAA,IACX;AAAA,EACF;AACA,EAAA,OAAO,IAAA;AACT;AAaA,SAAS,mBAAmB,aAAA,EAA+B;AACzD,EAAA,IAAI,aAAA,GAAgB,GAAG,OAAO,CAAA;AAE9B,EAAA,MAAM,IAAI,aAAA,GAAgB,eAAA;AAC1B,EAAA,IAAI,CAAA,IAAK,GAAG,OAAO,CAAA;AAEnB,EAAA,IAAI,CAAA,GAAI,IAAI,CAAA,EAAG;AAEb,IAAA,MAAMC,KAAI,CAAA,GAAI,CAAA;AACd,IAAA,OAAO,GAAA,IAAO,CAAA,GAAA,CAAK,CAAA,GAAIA,EAAAA,KAAM,CAAA,CAAA;AAAA,EAC/B;AACA,EAAA,IAAI,CAAA,GAAI,IAAI,CAAA,EAAG;AAEb,IAAA,MAAMA,EAAAA,GAAAA,CAAK,CAAA,GAAI,CAAA,GAAI,CAAA,IAAK,CAAA;AACxB,IAAA,OAAO,MAAM,IAAA,GAAOA,EAAAA;AAAA,EACtB;AAEA,EAAA,MAAM,CAAA,GAAA,CAAK,CAAA,GAAI,CAAA,GAAI,CAAA,IAAK,CAAA;AACxB,EAAA,OAAO,OAAO,IAAA,GAAO,CAAA;AACvB;AAOA,SAAS,oBAAA,CAAqB,OAAA,EAAiB,QAAA,EAAkB,MAAA,EAA4B;AAC3F,EAAA,MAAM,cAAc,QAAA,GAAW,WAAA;AAC/B,EAAA,IAAI,SAAA,GAAY,CAAA;AAEhB,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,KAAA,CAAM,iBAAiB,QAAA,EAAU;AACrC,IAAA,IAAI,KAAA,CAAM,iBAAiB,WAAA,EAAa;AACxC,IAAA,IAAI,KAAA,CAAM,UAAU,OAAA,EAAS;AAE7B,IAAA,MAAM,MAAA,GAAS,YAAA,CAAa,KAAA,CAAM,IAAI,CAAA;AACtC,IAAA,IAAI,WAAW,MAAA,EAAW;AAE1B,IAAA,MAAM,GAAA,GAAM,WAAW,KAAA,CAAM,cAAA;AAC7B,IAAA,MAAM,UAAU,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,MAAmB,GAAG,CAAA;AACxD,IAAA,SAAA,IAAa,OAAA;AAAA,EACf;AAGA,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,KAAA,CAAM,iBAAiB,QAAA,EAAU;AACrC,IAAA,IAAI,KAAA,CAAM,iBAAiB,WAAA,EAAa;AACxC,IAAA,IAAI,KAAA,CAAM,gBAAgB,OAAA,EAAS;AAEnC,IAAA,MAAM,MAAA,GAAS,YAAA,CAAa,KAAA,CAAM,IAAI,CAAA;AACtC,IAAA,IAAI,WAAW,MAAA,EAAW;AAE1B,IAAA,MAAM,GAAA,GAAM,WAAW,KAAA,CAAM,cAAA;AAC7B,IAAA,MAAM,UAAU,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,MAAmB,GAAG,CAAA;AACxD,IAAA,SAAA,IAAa,OAAA;AAAA,EACf;AAEA,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,SAAA,GAAY,kBAAkB,CAAA;AACnD;AAOA,IAAM,yCAAyB,IAAI,GAAA,CAAI,CAAC,QAAA,EAAU,YAAA,EAAc,WAAW,CAAC,CAAA;AAcrE,SAAS,gBAAA,CAAiB,OAAA,EAAiB,QAAA,EAAkB,MAAA,EAA4B;AAC9F,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,KAAA,CAAM,iBAAiB,QAAA,EAAU;AACrC,IAAA,IAAI,KAAA,CAAM,UAAU,OAAA,EAAS;AAC7B,IAAA,IAAI,sBAAA,CAAuB,GAAA,CAAI,KAAA,CAAM,IAAI,CAAA,EAAG;AAC1C,MAAA,KAAA,EAAA;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,iBAAA,CACP,KAAA,EACA,QAAA,EACA,MAAA,EACA,cACA,aAAA,EACmB;AACnB,EAAA,MAAM,UAAA,GAAa,kBAAA,CAAmB,KAAA,CAAM,EAAA,EAAI,MAAM,CAAA;AAGtD,EAAA,IAAI,UAAA,KAAe,IAAA,IAAQ,QAAA,GAAW,UAAA,EAAY;AAChD,IAAA,OAAO,IAAA;AAAA,EACT;AAGA,EAAA,MAAM,gBAAgB,QAAA,GAAW,UAAA;AACjC,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,gBAAgB,mBAAmB,CAAA;AAG/D,EAAA,MAAM,KAAA,GAAQ,mBAAmB,aAAa,CAAA;AAG9C,EAAA,MAAM,eAAe,MAAA,CAAO,MAAA;AAAA,IAC1B,CAAC,CAAA,KACC,CAAA,CAAE,KAAA,KAAU,KAAA,CAAM,EAAA,IAClB,CAAA,CAAE,cAAA,IAAkB,QAAA,IACpB,CAAA,CAAE,cAAA,GAAiB,QAAA,GAAW;AAAA,GAClC;AACA,EAAA,MAAM,iBAAiB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,YAAA,CAAa,SAAS,CAAC,CAAA;AAC1D,EAAA,MAAM,gBAAA,GAAmB,CAAA,GAAI,cAAA,IAAkB,qBAAA,GAAwB,CAAA,CAAA;AAGvE,EAAA,MAAM,aAAA,GAAgB,oBAAA,CAAqB,KAAA,CAAM,EAAA,EAAI,UAAU,MAAM,CAAA;AAGrE,EAAA,MAAM,SAAA,GAAY,gBAAA,CAAiB,KAAA,CAAM,EAAA,EAAI,UAAU,MAAM,CAAA;AAK7D,EAAA,MAAM,OAAA,GAAU,MAAM,IAAA,KAAS,OAAA;AAC/B,EAAA,MAAM,QAAA,GAAW,UAAU,qBAAA,GAAwB,CAAA;AACnD,EAAA,MAAM,eAAe,SAAA,GAAY,YAAA;AACjC,EAAA,MAAM,aAAA,GAAgB,IAAI,aAAA,GAAgB,cAAA;AAC1C,EAAA,MAAM,UAAA,GAAa,eAAe,UAAA,IAAc,eAAA;AAChD,EAAA,MAAM,cAAc,UAAA,GAAa,eAAA;AACjC,EAAA,MAAM,YACJ,QAAA,IAAY,CAAA,GAAI,YAAA,GAAe,eAAA,CAAA,GAAmB,gBAAgB,KAAA,GAAQ,WAAA;AAK5E,EAAA,MAAM,WAAA,GAAc,aAAA,EAAe,WAAA,IAAe,UAAA,GAAa,GAAA;AAC/D,EAAA,MAAM,eAAe,UAAA,GAAa,SAAA;AAClC,EAAA,MAAM,WAAW,YAAA,GAAe,eAAA;AAChC,EAAA,MAAM,YAAA,GAAe,EAAE,YAAA,GAAe,QAAA,GAAW,WAAA,GAAc,CAAA,CAAA;AAE/D,EAAA,OAAO;AAAA,IACL,IAAI,KAAA,CAAM,EAAA;AAAA,IACV,CAAA,EAAG,MAAM,QAAA,CAAS,CAAA;AAAA,IAClB,CAAA,EAAG,MAAM,QAAA,CAAS,CAAA;AAAA,IAClB,MAAA,EAAQ,MAAM,MAAA,GAAS,gBAAA;AAAA,IACvB,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,OAAA;AAAA,IACA,aAAA,EAAe,cAAA;AAAA,IACf,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,YAAA,EAAc,OAAA;AAAA,IACd,KAAA;AAAA,IACA,aAAA;AAAA,IACA,SAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAM,KAAA,CAAM,IAAA;AAAA,IACZ,SAAA;AAAA,IACA;AAAA,GACF;AACF;AAMA,IAAM,cAAA,mBAAiB,IAAI,GAAA,CAAI,CAAC,SAAS,CAAC,CAAA;AAI1C,IAAM,wBAAA,GAA2B,CAAA;AAEjC,SAAS,gBAAA,CACP,MAAA,EACA,MAAA,EACA,QAAA,EACA,cAAA,EACiB;AACjB,EAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,EAAA,EAAI,CAAC,CAAC,CAAC,CAAA;AACrD,EAAA,MAAM,YAA6B,EAAC;AAEpC,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAE1B,IAAA,IAAI,KAAA,CAAM,iBAAiB,QAAA,EAAU;AACrC,IAAA,IAAI,CAAC,cAAA,CAAe,GAAA,CAAI,KAAA,CAAM,IAAI,CAAA,EAAG;AAErC,IAAA,MAAM,GAAA,GAAM,WAAW,KAAA,CAAM,cAAA;AAC7B,IAAA,IAAI,MAAM,cAAA,EAAgB;AAE1B,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,GAAA,CAAI,KAAA,CAAM,KAAK,CAAA;AACvC,IAAA,IAAI,CAAC,MAAA,EAAQ;AAGb,IAAA,MAAM,WAAW,KAAA,CAAM,WAAA,GAAc,SAAS,GAAA,CAAI,KAAA,CAAM,WAAW,CAAA,GAAI,IAAA;AACvE,IAAA,IAAI,CAAC,QAAA,EAAU;AAEf,IAAA,MAAM,IAAI,GAAA,GAAM,cAAA;AAChB,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,CAAA,GAAI,0BAA0B,CAAG,CAAA;AACzD,IAAA,MAAM,OACJ,CAAA,IAAK,wBAAA,GACD,IACA,CAAA,GAAA,CAAO,CAAA,GAAI,6BAA6B,CAAA,GAAI,wBAAA,CAAA;AAElD,IAAA,MAAM,EAAA,GAAK,OAAO,QAAA,CAAS,CAAA,GAAA,CAAK,SAAS,QAAA,CAAS,CAAA,GAAI,MAAA,CAAO,QAAA,CAAS,CAAA,IAAK,MAAA;AAC3E,IAAA,MAAM,EAAA,GAAK,OAAO,QAAA,CAAS,CAAA,GAAA,CAAK,SAAS,QAAA,CAAS,CAAA,GAAI,MAAA,CAAO,QAAA,CAAS,CAAA,IAAK,MAAA;AAQ3E,IAAA,SAAA,CAAU,IAAA,CAAK;AAAA,MACb,EAAA,EAAI,YAAY,KAAA,CAAM,IAAI,IAAI,KAAA,CAAM,cAAc,CAAA,CAAA,EAAI,KAAA,CAAM,KAAK,CAAA,CAAA;AAAA,MACjE,IAAA,EAAM,SAAA;AAAA,MACN,CAAA,EAAG,EAAA;AAAA,MACH,CAAA,EAAG,EAAA;AAAA,MACH,OAAA,EAAS,IAAA;AAAA,MACT,IAAA,EAAM,CAAA;AAAA,MACN,OAAO,MAAA,CAAO;AAAA,KACf,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,SAAA;AACT;AAMA,IAAM,qBAAA,GAAwBC,gCAAA;AAC9B,IAAM,oBAAA,GAAuBC,+BAAA;AAO7B,IAAM,kBAAA,GAAqB,EAAA;AAE3B,IAAM,uBAAA,GAA0B,GAAA;AAEhC,IAAM,0BAAA,GAA6B,IAAA;AAEnC,IAAM,qBAAA,GAAwB,CAAA;AAE9B,IAAM,cAAA,GAAiB,GAAA;AAEvB,IAAM,sBAAA,GAAyB,EAAA;AAE/B,IAAM,gBAAA,GAAmB,IAAA;AAEzB,IAAM,iBAAA,GAAoB,GAAA;AAMnB,SAAS,gBAAA,CAAiB,KAAa,MAAA,EAAwB;AACpE,EAAA,MAAM,CAAA,GAAI,GAAA,CAAI,OAAA,CAAQ,GAAA,EAAK,EAAE,CAAA;AAC7B,EAAA,MAAM,IAAI,IAAA,CAAK,GAAA;AAAA,IACb,sBAAA;AAAA,IACA,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,QAAA,CAAS,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,EAAG,EAAE,CAAA,GAAI,MAAM;AAAA,GACxD;AACA,EAAA,MAAM,IAAI,IAAA,CAAK,GAAA;AAAA,IACb,sBAAA;AAAA,IACA,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,QAAA,CAAS,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,EAAG,EAAE,CAAA,GAAI,MAAM;AAAA,GACxD;AACA,EAAA,MAAM,IAAI,IAAA,CAAK,GAAA;AAAA,IACb,sBAAA;AAAA,IACA,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,QAAA,CAAS,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,EAAG,EAAE,CAAA,GAAI,MAAM;AAAA,GACxD;AACA,EAAA,OAAO,CAAA,CAAA,EAAI,CAAA,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,EAAG,CAAA,CAAE,QAAA,CAAS,EAAE,EAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,EAAG,CAAA,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAA;AAChH;AAYO,SAAS,iBAAA,CACd,YAAA,EACA,IAAA,EACA,YAAA,EACA,aAAA,EAiBA;AACA,EAAA,MAAM,UAAA,GAAa,eAAe,UAAA,IAAc,eAAA;AAChD,EAAA,MAAM,GAAA,GAAM,aAAA,GAAgB,aAAA,CAAc,eAAA,GAAkB,cAAc,UAAA,GAAa,GAAA;AACvF,EAAA,MAAM,IAAA,GAAO,eAAe,aAAA,IAAiB,IAAA;AAC7C,EAAA,MAAM,IAAA,GAAO,eAAe,aAAA,IAAiB,IAAA;AAC7C,EAAA,MAAM,QAAA,GAAW,eAAe,iBAAA,IAAqB,CAAA;AAIrD,EAAA,MAAM,QAAA,GAAW,kBAAA;AACjB,EAAA,MAAM,eAAA,GAAkB,IAAA,CAAK,KAAA,CAAM,QAAA,GAAW,uBAAuB,CAAA;AAGrE,EAAA,MAAM,iBAAiB,QAAA,GAAW,GAAA;AAGlC,EAAA,MAAM,gBAAA,GAAmB,aAAA,GACpB,aAAA,CAAc,eAAA,GAAkB,6BAA8B,GAAA,GAC/D,QAAA;AACJ,EAAA,MAAM,YAAA,GAAe,IAAA,CAAK,GAAA,CAAI,QAAA,EAAU,gBAAgB,CAAA;AAGxD,EAAA,MAAM,aAAA,GAAgB,eAAe,IAAA,GAAO,CAAA;AAI5C,EAAA,MAAM,aAAA,GAAqD,EAAA;AAC3D,EAAA,MAAM,iBAAA,GAAoB,aAAA,CAAc,MAAA,IAAU,eAAA,GAAkB,GAAA,CAAA,GAAO,gBAAA;AAC3E,EAAA,MAAM,QAAA,GAAW,IAAA;AACjB,EAAA,MAAM,kBAAA,GAAqB,QAAA,CAAS,MAAA,GAAS,cAAA,GAAiB,gBAAA;AAC9D,EAAA,MAAM,iBAAiB,iBAAA,GAAoB,kBAAA;AAK3C,EAAA,IAAI,KAAA;AACJ,EAAA,MAAM,iBAAiB,cAAA,GAAiB,gBAAA;AAExC,EAAA,IAAI,kBAAkB,aAAA,EAAe;AAEnC,IAAA,KAAA,GAAQ,CAAC,QAAQ,CAAA;AAAA,EACnB,CAAA,MAAO;AAEL,IAAA,MAAM,iBAAiB,aAAA,GAAgB,iBAAA;AACvC,IAAA,MAAM,YAAA,GAAe,KAAK,GAAA,CAAI,EAAA,EAAI,KAAK,KAAA,CAAM,aAAA,GAAgB,cAAc,CAAC,CAAA;AAC5E,IAAA,MAAM,cAAA,GAAiB,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,cAAA,GAAiB,cAAc,CAAC,CAAA;AAE9E,IAAA,KAAA,GAAQ,EAAC;AACT,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA;AAChC,IAAA,IAAI,WAAA,GAAc,EAAA;AAClB,IAAA,IAAI,YAAA,GAAe,CAAA;AAEnB,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,MAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,MAAA,KAAW,CAAA,GAAI,cAAA,GAAiB,YAAA;AACpD,MAAA,IAAI,WAAA,IAAe,YAAA,GAAe,CAAA,GAAI,IAAA,CAAK,SAAS,KAAA,EAAO;AACzD,QAAA,KAAA,CAAM,KAAK,WAAW,CAAA;AACtB,QAAA,WAAA,GAAc,IAAA;AACd,QAAA,YAAA,GAAe,IAAA,CAAK,MAAA;AAAA,MACtB,CAAA,MAAO;AACL,QAAA,WAAA,GAAc,WAAA,GAAc,CAAA,EAAG,WAAW,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,GAAK,IAAA;AACvD,QAAA,YAAA,GAAe,WAAA,CAAY,MAAA;AAAA,MAC7B;AAAA,IACF;AACA,IAAA,IAAI,WAAA,EAAa,KAAA,CAAM,IAAA,CAAK,WAAW,CAAA;AAGvC,IAAA,IAAI,KAAA,CAAM,SAAS,CAAA,EAAG;AACpB,MAAA,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA;AACxB,MAAA,KAAA,CAAM,CAAC,IAAI,CAAA,EAAG,KAAA,CAAM,CAAC,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAC,CAAA,GAAA,CAAA;AAAA,IACrC;AAAA,EACF;AAIA,EAAA,MAAM,kBAAkB,cAAA,GAAiB,iBAAA;AACzC,EAAA,MAAM,UAAA,GAAa,kBAAkB,KAAA,CAAM,MAAA;AAC3C,EAAA,MAAM,cAAA,GAAiB,aAAa,IAAA,GAAO,CAAA;AAC3C,EAAA,MAAM,YAAY,UAAA,GAAa,qBAAA;AAC/B,EAAA,MAAM,eAAA,GAAkB,IAAA,CAAK,GAAA,CAAI,cAAA,EAAgB,SAAS,CAAA;AAI1D,EAAA,MAAM,cAAA,GACJ,KAAA,CAAM,MAAA,KAAW,CAAA,GAAI,IAAA,CAAK,IAAI,cAAA,GAAiB,IAAA,GAAO,CAAA,EAAG,YAAY,CAAA,GAAI,YAAA;AAE3E,EAAA,MAAM,WAAA,GAAc,gBAAA,CAAiB,YAAA,EAAc,cAAc,CAAA;AAEjE,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,IAAA;AAAA,IACA,IAAA;AAAA,IACA,YAAA;AAAA,IACA,cAAA;AAAA,IACA,eAAA;AAAA,IACA,YAAA;AAAA,IACA,YAAA;AAAA,IACA;AAAA,GACF;AACF;AAMA,SAAS,yBACP,KAAA,EACA,aAAA,EACA,MAAA,EACA,aAAA,EACA,iBACA,cAAA,EACkF;AAClF,EAAA,MAAM,UAAA,GAAa,eAAe,UAAA,IAAc,eAAA;AAEhD,EAAA,MAAM,GAAA,GAAM,aAAa,qBAAA,GAAwB,oBAAA;AAEjD,EAAA,MAAM,OAAA,GAAU,cAAc,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,EAAA,KAAO,MAAM,OAAO,CAAA;AAChE,EAAA,MAAM,YAAA,GAAe,OAAO,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,EAAA,KAAO,MAAM,OAAO,CAAA;AAE9D,EAAA,IAAI,WAAW,YAAA,EAAc;AAE3B,IAAA,MAAM,gBAAA,GAAmB,QAAQ,SAAA,IAAa,CAAA;AAC9C,IAAA,MAAM,aAAa,UAAA,GAAa,gBAAA;AAGhC,IAAA,IAAI,OAAA,GAAU,UAAA,GAAa,GAAA,GAAM,eAAA,GAAkB,CAAA;AAInD,IAAA,IAAI,aAAA,EAAe,YAAA,IAAgB,aAAA,CAAc,aAAA,KAAkB,MAAA,EAAW;AAC5E,MAAA,MAAM,cAAc,aAAA,CAAc,YAAA;AAAA,QAChC,aAAa,QAAA,CAAS,CAAA;AAAA,QACtB,aAAa,QAAA,CAAS;AAAA,OACxB,CAAE,CAAA;AACF,MAAA,MAAM,OAAA,GAAU,WAAA,GAAc,OAAA,GAAU,eAAA,GAAkB,CAAA;AAC1D,MAAA,MAAM,MAAA,GAAS,IAAA;AACf,MAAA,MAAM,UAAA,GAAa,cAAc,aAAA,GAAgB,MAAA;AACjD,MAAA,IAAI,UAAU,UAAA,EAAY;AACxB,QAAA,OAAA,IAAW,OAAA,GAAU,UAAA;AAAA,MACvB;AAAA,IACF;AAMA,IAAA,IAAI,OAAA,GAAU,CAAA;AACd,IAAA,IAAI,eAAe,YAAA,EAAc;AAC/B,MAAA,MAAM,cAAc,aAAA,CAAc,YAAA;AAAA,QAChC,aAAa,QAAA,CAAS,CAAA;AAAA,QACtB,aAAa,QAAA,CAAS;AAAA,OACxB,CAAE,CAAA;AACF,MAAA,MAAM,MAAA,GAAS,IAAA;AACf,MAAA,MAAM,OAAA,GAAU,cAAc,SAAA,GAAY,MAAA;AAC1C,MAAA,MAAM,QAAA,GAAW,cAAc,UAAA,GAAa,MAAA;AAC5C,MAAA,MAAM,aAAa,cAAA,GAAiB,GAAA;AACpC,MAAA,MAAM,QAAA,GAAW,cAAc,UAAA,GAAa,CAAA;AAC5C,MAAA,MAAM,SAAA,GAAY,cAAc,UAAA,GAAa,CAAA;AAE7C,MAAA,IAAI,WAAW,OAAA,EAAS;AAEtB,QAAA,OAAA,GAAU,OAAA,GAAU,QAAA;AAAA,MACtB,CAAA,MAAA,IAAW,YAAY,QAAA,EAAU;AAE/B,QAAA,OAAA,GAAU,QAAA,GAAW,SAAA;AAAA,MACvB;AAAA,IACF;AAEA,IAAA,OAAO;AAAA,MACL,YAAA,EAAc,aAAa,QAAA,CAAS,CAAA;AAAA,MACpC,YAAA,EAAc,aAAa,QAAA,CAAS,CAAA;AAAA,MACpC,OAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAGA,EAAA,OAAO,EAAE,cAAc,GAAA,EAAK,YAAA,EAAc,KAAK,OAAA,EAAS,CAAA,EAAG,SAAS,CAAA,EAAE;AACxE;AAUA,SAAS,mBAAA,CACP,MAAA,EACA,eAAA,EACA,oBAAA,EACA,yBAAA,EACyD;AACzD,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAwD;AAG7E,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAqD;AAC3E,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,MAAA,CAAO,QAAQ,CAAA,EAAA,EAAK;AACtC,IAAA,MAAM,CAAA,GAAI,OAAO,CAAC,CAAA;AAClB,IAAA,IAAI,KAAA,GAAQ,SAAA,CAAU,GAAA,CAAI,CAAA,CAAE,OAAO,CAAA;AACnC,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA,KAAA,GAAQ,EAAC;AACT,MAAA,SAAA,CAAU,GAAA,CAAI,CAAA,CAAE,OAAA,EAAS,KAAK,CAAA;AAAA,IAChC;AACA,IAAA,KAAA,CAAM,KAAK,EAAE,GAAA,EAAK,CAAA,EAAG,KAAA,EAAO,GAAG,CAAA;AAAA,EACjC;AAEA,EAAA,KAAA,MAAW,KAAA,IAAS,SAAA,CAAU,MAAA,EAAO,EAAG;AAEtC,IAAA,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,EAAE,KAAA,CAAM,cAAA,GAAiB,CAAA,CAAE,KAAA,CAAM,cAAc,CAAA;AAEpE,IAAA,IAAI,OAAA,GAAU,EAAA;AAEd,IAAA,KAAA,IAAS,EAAA,GAAK,CAAA,EAAG,EAAA,GAAK,KAAA,CAAM,QAAQ,EAAA,EAAA,EAAM;AACxC,MAAA,MAAM,EAAE,GAAA,EAAK,KAAA,EAAM,GAAI,MAAM,EAAE,CAAA;AAC/B,MAAA,MAAM,eAAe,KAAA,CAAM,cAAA;AAI3B,MAAA,MAAM,SAAA,GAAY,KAAK,CAAA,GAAI,KAAA,CAAM,SAAS,KAAA,CAAM,EAAA,GAAK,CAAC,CAAA,CAAE,KAAA,GAAQ,IAAA;AAChE,MAAA,MAAM,OAAA,GACJ,SAAA,KAAc,IAAA,IAAQ,SAAA,CAAU,iBAAiB,YAAA,GAAe,yBAAA;AAElE,MAAA,MAAM,MAAA,GAAS,UAAU,oBAAA,GAAuB,eAAA;AAKhD,MAAA,IAAI,cAAA,GAAiB,IAAA,CAAK,GAAA,CAAI,YAAA,EAAc,OAAO,CAAA;AACnD,MAAA,MAAM,YAAY,MAAA,GAAS,GAAA;AAC3B,MAAA,IAAI,cAAA,GAAiB,YAAY,CAAA,EAAK;AAEpC,QAAA,cAAA,GAAiB,IAAA,CAAK,GAAA,CAAI,cAAA,EAAgB,CAAA,GAAM,SAAS,CAAA;AAAA,MAC3D;AACA,MAAA,MAAM,eAAA,GAAkB,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,IAAM,cAAc,CAAA;AAE7D,MAAA,QAAA,CAAS,GAAA,CAAI,GAAA,EAAK,EAAE,cAAA,EAAgB,MAAA,EAAQ,KAAK,GAAA,CAAI,eAAA,EAAiB,SAAS,CAAA,EAAG,CAAA;AAClF,MAAA,OAAA,GAAU,cAAA,GAAiB,IAAA,CAAK,GAAA,CAAI,eAAA,EAAiB,SAAS,CAAA;AAAA,IAChE;AAAA,EACF;AAEA,EAAA,OAAO,QAAA;AACT;AAEA,SAAS,iBAAA,CACP,QACA,QAAA,EACA,MAAA,EACA,eACA,aAAA,EACA,eAAA,EACA,sBACA,yBAAA,EACkB;AAClB,EAAA,MAAM,SAA2B,EAAC;AAClC,EAAA,MAAM,QAAA,GAAW,mBAAA;AAAA,IACf,MAAA;AAAA,IACA,eAAA;AAAA,IACA,oBAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,MAAA,CAAO,QAAQ,CAAA,EAAA,EAAK;AACtC,IAAA,MAAM,KAAA,GAAQ,OAAO,CAAC,CAAA;AACtB,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,GAAA,CAAI,CAAC,CAAA;AAC7B,IAAA,IAAI,CAAC,MAAA,EAAQ;AAEb,IAAA,MAAM,EAAE,cAAA,EAAgB,MAAA,EAAO,GAAI,MAAA;AACnC,IAAA,MAAM,MAAM,cAAA,GAAiB,MAAA;AAE7B,IAAA,IAAI,QAAA,GAAW,cAAA,IAAkB,QAAA,GAAW,GAAA,EAAK;AAEjD,IAAA,MAAM,UAAU,QAAA,GAAW,cAAA;AAC3B,IAAA,MAAM,SAAS,MAAA,GAAS,iBAAA;AACxB,IAAA,MAAM,UAAU,MAAA,GAAS,kBAAA;AAEzB,IAAA,IAAI,OAAA;AACJ,IAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,MAAA,OAAA,GAAU,OAAA,GAAU,MAAA;AAAA,IACtB,CAAA,MAAA,IAAW,OAAA,GAAU,MAAA,GAAS,OAAA,EAAS;AACrC,MAAA,OAAA,GAAA,CAAW,SAAS,OAAA,IAAW,OAAA;AAAA,IACjC,CAAA,MAAO;AACL,MAAA,OAAA,GAAU,CAAA;AAAA,IACZ;AAGA,IAAA,MAAM,YAAA,GAAe,cAAc,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,EAAA,KAAO,MAAM,OAAO,CAAA;AACrE,IAAA,MAAM,YAAA,GAAe,cAAc,KAAA,IAAS,SAAA;AAC5C,IAAA,MAAM,YAAA,GAAe,EAAA;AAGrB,IAAA,MAAM,OAAO,iBAAA,CAAkB,YAAA,EAAc,KAAA,CAAM,IAAA,EAAM,cAAc,aAAa,CAAA;AAEpF,IAAA,MAAM,EAAE,YAAA,EAAc,YAAA,EAAc,OAAA,EAAS,SAAQ,GAAI,wBAAA;AAAA,MACvD,KAAA;AAAA,MACA,aAAA;AAAA,MACA,MAAA;AAAA,MACA,aAAA;AAAA,MACA,IAAA,CAAK,eAAA;AAAA,MACL,IAAA,CAAK;AAAA,KACP;AAEA,IAAA,MAAA,CAAO,IAAA,CAAK;AAAA,MACV,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,SAAS,KAAA,CAAM,OAAA;AAAA,MACf,OAAA,EAAS,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,GAAA,CAAI,CAAA,EAAG,OAAO,CAAC,CAAA;AAAA,MACzC,YAAA;AAAA,MACA,YAAA;AAAA,MACA,OAAA;AAAA,MACA,OAAA;AAAA,MACA,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,cAAc,IAAA,CAAK,YAAA;AAAA,MACnB,gBAAgB,IAAA,CAAK,cAAA;AAAA,MACrB,cAAc,IAAA,CAAK,YAAA;AAAA,MACnB,cAAc,IAAA,CAAK,YAAA;AAAA,MACnB,aAAa,IAAA,CAAK,WAAA;AAAA,MAClB,GAAI,KAAA,CAAM,KAAA,IAAS,EAAE,KAAA,EAAO,MAAM,KAAA,EAAM;AAAA,MACxC,GAAI,KAAA,CAAM,IAAA,IAAQ,EAAE,IAAA,EAAM,MAAM,IAAA;AAAK,KACtC,CAAA;AAAA,EACH;AAKA,EAAA,OAAO,MAAA;AACT;AAQO,SAAS,yBAAA,CACd,OACA,QAAA,EACgC;AAChC,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AAG/B,EAAA,IAAI,EAAA,GAAK,CAAA;AACT,EAAA,IAAI,EAAA,GAAK,MAAM,MAAA,GAAS,CAAA;AACxB,EAAA,IAAI,GAAA,GAAM,EAAA;AAEV,EAAA,OAAO,MAAM,EAAA,EAAI;AACf,IAAA,MAAM,GAAA,GAAO,KAAK,EAAA,KAAQ,CAAA;AAC1B,IAAA,IAAI,KAAA,CAAM,GAAG,CAAA,CAAE,cAAA,IAAkB,QAAA,EAAU;AACzC,MAAA,GAAA,GAAM,GAAA;AACN,MAAA,EAAA,GAAK,GAAA,GAAM,CAAA;AAAA,IACb,CAAA,MAAO;AACL,MAAA,EAAA,GAAK,GAAA,GAAM,CAAA;AAAA,IACb;AAAA,EACF;AAGA,EAAA,IAAI,GAAA,GAAM,GAAG,OAAO,IAAA;AAEpB,EAAA,MAAM,KAAA,GAAQ,MAAM,GAAG,CAAA;AAGvB,EAAA,MAAM,WAAA,GAAA,CACH,GAAA,GAAM,KAAA,CAAM,MAAA,GAAS,CAAA,GAAI,KAAA,CAAM,GAAA,GAAM,CAAC,CAAA,CAAE,cAAA,GAAiB,CAAA,IAAK,KAAA,CAAM,cAAA;AACvE,EAAA,MAAM,aAAA,GAAgB,MAAM,kBAAA,IAAsB,WAAA;AAGlD,EAAA,MAAM,OAAA,GAAU,WAAW,KAAA,CAAM,cAAA;AACjC,EAAA,IAAI,KAAA,CAAM,kBAAA,IAAsB,IAAA,IAAQ,OAAA,GAAU,aAAA,EAAe;AAC/D,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,cAAA,EAAgB,gBAAgB,GAAG,CAAA;AAC3D,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,cAAA,EAAgB,gBAAgB,GAAG,CAAA;AAE5D,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,IAAA,OAAA,GAAU,MAAA,GAAS,CAAA,GAAI,OAAA,GAAU,MAAA,GAAS,CAAA;AAAA,EAC5C,CAAA,MAAA,IAAW,OAAA,GAAU,aAAA,GAAgB,OAAA,EAAS;AAC5C,IAAA,OAAA,GAAU,OAAA,GAAU,CAAA,GAAA,CAAK,aAAA,GAAgB,OAAA,IAAW,OAAA,GAAU,CAAA;AAAA,EAChE,CAAA,MAAO;AACL,IAAA,OAAA,GAAU,CAAA;AAAA,EACZ;AAEA,EAAA,OAAO;AAAA,IACL,YAAY,KAAA,CAAM,UAAA;AAAA,IAClB,YAAY,KAAA,CAAM,UAAA;AAAA,IAClB,MAAM,KAAA,CAAM,IAAA;AAAA,IACZ,OAAA,EAAS,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,GAAA,CAAI,CAAA,EAAG,OAAO,CAAC,CAAA;AAAA;AAAA,IAEzC,SAAS,KAAA,CAAM,OAAA;AAAA,IACf,cAAc,KAAA,CAAM,YAAA;AAAA,IACpB,OAAO,KAAA,CAAM;AAAA,GACf;AACF;AAIA,SAAS,YAAA,CAAa,QAAoB,QAAA,EAA8B;AAEtE,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,QAAA,IAAY,KAAA,CAAM,eAAA,IAAmB,QAAA,IAAY,MAAM,aAAA,EAAe;AACxE,MAAA,MAAM,iBACH,QAAA,GAAW,KAAA,CAAM,eAAA,KAAoB,KAAA,CAAM,gBAAgB,KAAA,CAAM,eAAA,CAAA;AACpE,MAAA,OAAO;AAAA,QACL,IAAI,KAAA,CAAM,EAAA;AAAA,QACV,MAAM,KAAA,CAAM,IAAA;AAAA,QACZ,QAAA,EAAU,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,GAAA,CAAI,CAAA,EAAG,aAAa,CAAC,CAAA;AAAA,QAChD,OAAO,KAAA,CAAM;AAAA,OACf;AAAA,IACF;AAAA,EACF;AAGA,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,MAAA,CAAO,MAAA,GAAS,CAAC,CAAA;AAC1C,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,OAAO;AAAA,MACL,IAAI,SAAA,CAAU,EAAA;AAAA,MACd,MAAM,SAAA,CAAU,IAAA;AAAA,MAChB,QAAA,EAAU,QAAA,IAAY,SAAA,CAAU,aAAA,GAAgB,CAAA,GAAI,CAAA;AAAA,MACpD,OAAO,SAAA,CAAU;AAAA,KACnB;AAAA,EACF;AAGA,EAAA,OAAO,EAAE,IAAI,SAAA,EAAW,IAAA,EAAM,WAAW,QAAA,EAAU,CAAA,EAAG,OAAO,SAAA,EAAU;AACzE;AAIA,SAAS,YAAA,CACP,MAAA,EACA,MAAA,EACA,QAAA,EACqB;AACrB,EAAA,IAAI,aAAA,GAAgB,CAAA;AACpB,EAAA,IAAI,SAAA,GAAY,CAAA;AAChB,EAAA,IAAI,cAAA,GAAiB,CAAA;AAErB,EAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACtB,IAAA,IAAI,CAAA,CAAE,iBAAiB,QAAA,EAAU;AACjC,IAAA,IAAI,CAAA,CAAE,SAAS,QAAA,EAAU,aAAA,EAAA;AAAA,SAAA,IAChB,CAAA,CAAE,SAAS,WAAA,EAAa,SAAA,EAAA;AAAA,SAAA,IACxB,CAAA,CAAE,SAAS,SAAA,EAAW,cAAA,EAAA;AAAA,EACjC;AAEA,EAAA,OAAO;AAAA,IACL,aAAA;AAAA,IACA,SAAA;AAAA,IACA,cAAA;AAAA,IACA,cAAc,MAAA,CAAO;AAAA,GACvB;AACF;AAQA,IAAM,kBAAA,GAA6C;AAAA,EACjD,IAAA,EAAM,SAAA;AAAA;AAAA,EACN,GAAA,EAAK,SAAA;AAAA;AAAA,EACL,MAAA,EAAQ,SAAA;AAAA;AAAA,EACR,MAAA,EAAQ,SAAA;AAAA;AAAA,EACR,KAAA,EAAO,SAAA;AAAA;AAAA,EACP,EAAA,EAAI,SAAA;AAAA;AAAA,EACJ,QAAA,EAAU,SAAA;AAAA;AAAA,EACV,QAAA,EAAU,SAAA;AAAA;AAAA,EACV,IAAA,EAAM,SAAA;AAAA;AAAA,EACN,IAAA,EAAM,SAAA;AAAA;AAAA,EACN,IAAA,EAAM,SAAA;AAAA;AAAA,EACN,KAAA,EAAO,SAAA;AAAA;AAAA,EACP,KAAA,EAAO,SAAA;AAAA;AAAA,EACP,KAAA,EAAO,SAAA;AAAA;AAAA,EACP,KAAA,EAAO;AAAA;AACT,CAAA;AAEA,IAAM,oBAAA,GAAuB,SAAA;AAE7B,SAAS,iBAAA,CAAkB,QAAoB,QAAA,EAA+B;AAE5E,EAAA,MAAM,UAQD,EAAC;AACN,EAAA,IAAI,OAAA,GAAU,CAAA;AAEd,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,KAAA,CAAM,iBAAiB,QAAA,EAAU;AACrC,IAAA,IAAI,KAAA,CAAM,SAAS,QAAA,EAAU;AAE7B,IAAA,MAAM,UAAA,GAAc,KAAA,CAAM,QAAA,CAAS,UAAA,IAAyB,CAAA;AAC5D,IAAA,MAAM,SAAA,GAAa,KAAA,CAAM,QAAA,CAAS,SAAA,IAAwB,CAAA;AAC1D,IAAA,MAAM,UAAA,GAAc,KAAA,CAAM,QAAA,CAAS,UAAA,IAAyB,OAAA;AAC5D,IAAA,MAAM,OAAO,UAAA,GAAa,SAAA;AAC1B,IAAA,IAAI,IAAA,GAAO,SAAS,OAAA,GAAU,IAAA;AAE9B,IAAA,OAAA,CAAQ,IAAA,CAAK;AAAA,MACX,gBAAgB,KAAA,CAAM,cAAA;AAAA,MACtB,UAAA;AAAA,MACA,SAAA;AAAA,MACA,OAAO,KAAA,CAAM,KAAA;AAAA,MACb,UAAA;AAAA,MACA,KAAA,EAAO,kBAAA,CAAmB,UAAU,CAAA,IAAK,oBAAA;AAAA,MACzC;AAAA,KACD,CAAA;AAAA,EACH;AAKA,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,IAAA,CAAK,OAAO,CAAA;AACjC,EAAA,MAAM,IAAA,GAAoB,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IAC5C,gBAAgB,CAAA,CAAE,cAAA;AAAA,IAClB,YAAY,CAAA,CAAE,UAAA;AAAA,IACd,WAAW,CAAA,CAAE,SAAA;AAAA,IACb,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,YAAY,CAAA,CAAE,UAAA;AAAA,IACd,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,UAAA,EAAY,UAAU,CAAA,GAAI,IAAA,CAAK,KAAK,CAAA,CAAE,IAAI,IAAI,OAAA,GAAU;AAAA,GAC1D,CAAE,CAAA;AAEF,EAAA,OAAO,IAAA;AACT;AAIA,SAAS,aAAA,CAAc,MAAA,EAAuB,MAAA,EAAoB,QAAA,EAA2B;AAC3F,EAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,EAAA,EAAI,CAAC,CAAC,CAAC,CAAA;AAGrD,EAAA,MAAM,UAAA,uBAAiB,GAAA,EAAsB;AAE7C,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAAsB;AAE5C,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,KAAA,CAAM,iBAAiB,QAAA,EAAU;AACrC,IAAA,MAAM,QAAA,GAAW,MAAM,QAAA,CAAS,QAAA;AAChC,IAAA,IAAI,aAAa,MAAA,EAAW;AAE5B,IAAA,IAAI,KAAA,CAAM,SAAS,YAAA,EAAc;AAC/B,MAAA,UAAA,CAAW,GAAA,CAAI,UAAU,KAAK,CAAA;AAAA,IAChC,CAAA,MAAA,IAAW,KAAA,CAAM,IAAA,KAAS,WAAA,EAAa;AACrC,MAAA,SAAA,CAAU,GAAA,CAAI,UAAU,KAAK,CAAA;AAAA,IAC/B;AAAA,EACF;AAEA,EAAA,MAAM,OAAgB,EAAC;AAEvB,EAAA,KAAA,MAAW,CAAC,QAAA,EAAU,OAAO,CAAA,IAAK,UAAA,EAAY;AAC5C,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,GAAA,CAAI,OAAA,CAAQ,KAAK,CAAA;AACxC,IAAA,IAAI,CAAC,KAAA,EAAO;AAEZ,IAAA,MAAM,MAAA,GAAS,SAAA,CAAU,GAAA,CAAI,QAAQ,CAAA;AACrC,IAAA,MAAM,SAAA,GAAa,OAAA,CAAQ,QAAA,CAAS,SAAA,IAAwB,CAAA;AAC5D,IAAA,MAAM,SAAA,GAAa,OAAA,CAAQ,QAAA,CAAS,SAAA,IAAwB,CAAA;AAE5D,IAAA,IAAA,CAAK,IAAA,CAAK;AAAA,MACR,QAAA;AAAA,MACA,kBAAkB,OAAA,CAAQ,cAAA;AAAA,MAC1B,gBAAA,EAAkB,MAAA,GAAS,MAAA,CAAO,cAAA,GAAiB,IAAA;AAAA,MACnD,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,OAAO,KAAA,CAAM,KAAA;AAAA,MACb,SAAA;AAAA,MACA,SAAA;AAAA,MACA,GAAA,EAAK;AAAA;AAAA,KACN,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,IAAA;AACT;AAKA,IAAM,oBAAA,GAA+C;AAAA,EACnD,IAAA,EAAM,SAAA;AAAA,EACN,QAAA,EAAU,SAAA;AAAA,EACV,GAAA,EAAK,SAAA;AAAA,EACL,IAAA,EAAM,SAAA;AAAA,EACN,KAAA,EAAO;AACT,CAAA;AACA,IAAM,sBAAA,GAAyB,SAAA;AAE/B,SAAS,mBAAA,CACP,OAAA,EACA,MAAA,EACA,QAAA,EACe;AACf,EAAA,MAAM,OAAsB,EAAC;AAE7B,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,KAAA,CAAM,iBAAiB,QAAA,EAAU;AACrC,IAAA,IAAI,KAAA,CAAM,SAAS,UAAA,EAAY;AAE/B,IAAA,MAAM,YAAA,GAAgB,KAAA,CAAM,QAAA,CAAS,IAAA,IAAmB,KAAA;AAExD,IAAA,IAAA,CAAK,IAAA,CAAK;AAAA,MACR,gBAAgB,KAAA,CAAM,cAAA;AAAA,MACtB,IAAA,EAAM,YAAA;AAAA,MACN,OAAO,KAAA,CAAM,KAAA;AAAA,MACb,KAAA,EAAO,oBAAA,CAAqB,YAAY,CAAA,IAAK,sBAAA;AAAA,MAC7C,GAAA,EAAK;AAAA;AAAA,KACN,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,IAAA;AACT;AAIA,SAAS,YAAA,CAAa,MAAA,EAAuB,MAAA,EAAoB,QAAA,EAA+B;AAC9F,EAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,EAAA,EAAI,CAAC,CAAC,CAAC,CAAA;AAGrD,EAAA,MAAM,aAAA,uBAAoB,GAAA,EAAoB;AAE9C,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,KAAA,CAAM,iBAAiB,QAAA,EAAU;AACrC,IAAA,IAAI,KAAA,CAAM,IAAA,KAAS,SAAA,IAAa,CAAC,MAAM,WAAA,EAAa;AACpD,IAAA,IAAI,KAAA,CAAM,KAAA,KAAU,KAAA,CAAM,WAAA,EAAa;AAGvC,IAAA,MAAM,OACJ,KAAA,CAAM,KAAA,GAAQ,KAAA,CAAM,WAAA,GAChB,GAAG,KAAA,CAAM,KAAK,CAAA,CAAA,EAAI,KAAA,CAAM,WAAW,CAAA,CAAA,GACnC,CAAA,EAAG,MAAM,WAAW,CAAA,CAAA,EAAI,MAAM,KAAK,CAAA,CAAA;AAEzC,IAAA,MAAM,GAAA,GAAM,WAAW,KAAA,CAAM,cAAA;AAC7B,IAAA,MAAM,eAAA,GAAkB,IAAA,CAAK,GAAA,CAAI,KAAmB,GAAG,CAAA;AAEvD,IAAA,aAAA,CAAc,IAAI,IAAA,EAAA,CAAO,aAAA,CAAc,IAAI,IAAI,CAAA,IAAK,KAAK,eAAe,CAAA;AAAA,EAC1E;AAEA,EAAA,IAAI,aAAA,CAAc,IAAA,KAAS,CAAA,EAAG,OAAO,EAAC;AAKtC,EAAA,MAAM,SAAS,IAAA,CAAK,GAAA,CAAI,GAAG,aAAA,CAAc,MAAA,IAAU,CAAC,CAAA;AAEpD,EAAA,MAAM,QAAqB,EAAC;AAE5B,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,GAAG,CAAA,IAAK,aAAA,EAAe;AACvC,IAAA,MAAM,WAAW,IAAA,CAAK,GAAA,CAAI,GAAA,GAAM,MAAA,EAAQ,MAAM,eAAe,CAAA;AAC7D,IAAA,IAAI,WAAW,iBAAA,EAAmB;AAElC,IAAA,MAAM,CAAC,MAAA,EAAQ,IAAI,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AACrC,IAAA,MAAM,SAAA,GAAY,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA;AACrC,IAAA,MAAM,OAAA,GAAU,QAAA,CAAS,GAAA,CAAI,IAAI,CAAA;AACjC,IAAA,IAAI,CAAC,SAAA,EAAW;AAEhB,IAAA,MAAM,YAAY,SAAA,CAAU,KAAA;AAC5B,IAAA,MAAM,OAAA,GAAU,SAAS,KAAA,IAAS,SAAA;AAElC,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,MAAA;AAAA,MACA,IAAA;AAAA,MACA,QAAA;AAAA,MACA,KAAA,EAAO,SAAA;AAAA,MACP,SAAA;AAAA,MACA;AAAA,KACD,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,KAAA;AACT;AAIA,SAAS,iBAAA,CAAkB,SAAkB,QAAA,EAAoC;AAC/E,EAAA,OAAO,OAAA,CAAQ,UAAA,CAAW,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IACpC,gBAAgB,CAAA,CAAE,cAAA;AAAA,IAClB,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,OAAA,EAAS,YAAY,CAAA,CAAE;AAAA,GACzB,CAAE,CAAA;AACJ;AAKA,IAAM,eAAA,GAAkB,IAAA;AAUjB,SAAS,wBAAA,CACd,QAAA,EACA,eAAA,EACA,aAAA,EACA,SAAS,KAAA,EACD;AACR,EAAA,MAAM,UAAA,GAAa,SAAS,CAAA,GAAM,aAAA;AAClC,EAAA,IAAI,QAAA,GAAW,eAAA,IAAmB,QAAA,GAAW,UAAA,EAAY,OAAO,CAAA;AAChE,EAAA,MAAM,SAAS,IAAA,CAAK,GAAA,CAAA,CAAK,QAAA,GAAW,eAAA,IAAmB,iBAAiB,CAAG,CAAA;AAC3E,EAAA,MAAM,OAAA,GAAU,SAAS,CAAA,GAAM,IAAA,CAAK,KAAK,aAAA,GAAgB,QAAA,IAAY,iBAAiB,CAAG,CAAA;AACzF,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,OAAO,CAAA;AACjC;AAEA,SAAS,gBAAA,CAAiB,QAAoB,QAAA,EAAmC;AAC/E,EAAA,OAAO,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,MAAO;AAAA,IAC3B,IAAI,CAAA,CAAE,EAAA;AAAA,IACN,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,iBAAiB,CAAA,CAAE,eAAA;AAAA,IACnB,eAAe,CAAA,CAAE,aAAA;AAAA,IACjB,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,YAAA,EAAc,wBAAA;AAAA,MACZ,QAAA;AAAA,MACA,CAAA,CAAE,eAAA;AAAA,MACF,CAAA,CAAE,aAAA;AAAA,MACF,CAAA,KAAM,OAAO,MAAA,GAAS;AAAA;AACxB,GACF,CAAE,CAAA;AACJ;AAQA,SAAS,WAAW,CAAA,EAAmB;AACrC,EAAA,MAAM,CAAA,GAAI,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,GAAA,CAAI,CAAA,EAAG,CAAC,CAAC,CAAA;AACpC,EAAA,OAAO,CAAA,GAAI,CAAA,IAAK,CAAA,GAAI,CAAA,GAAI,CAAA,CAAA;AAC1B;AAQA,SAAS,iBAAA,CAAkB,WAA+B,QAAA,EAA0B;AAClF,EAAA,IAAI,SAAA,CAAU,MAAA,KAAW,CAAA,EAAG,OAAO,GAAA;AAGnC,EAAA,IAAI,QAAA,IAAY,SAAA,CAAU,CAAC,CAAA,CAAE,cAAA,EAAgB;AAC3C,IAAA,OAAO,SAAA,CAAU,CAAC,CAAA,CAAE,KAAA;AAAA,EACtB;AAGA,EAAA,IAAI,YAAY,SAAA,CAAU,SAAA,CAAU,MAAA,GAAS,CAAC,EAAE,cAAA,EAAgB;AAC9D,IAAA,OAAO,SAAA,CAAU,SAAA,CAAU,MAAA,GAAS,CAAC,CAAA,CAAE,KAAA;AAAA,EACzC;AAGA,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,SAAA,CAAU,MAAA,GAAS,GAAG,CAAA,EAAA,EAAK;AAC7C,IAAA,MAAM,CAAA,GAAI,UAAU,CAAC,CAAA;AACrB,IAAA,MAAM,CAAA,GAAI,SAAA,CAAU,CAAA,GAAI,CAAC,CAAA;AAEzB,IAAA,IAAI,QAAA,IAAY,CAAA,CAAE,cAAA,IAAkB,QAAA,IAAY,EAAE,cAAA,EAAgB;AAChE,MAAA,MAAM,KAAK,QAAA,GAAW,CAAA,CAAE,cAAA,KAAmB,CAAA,CAAE,iBAAiB,CAAA,CAAE,cAAA,CAAA;AAChE,MAAA,OAAO,EAAE,KAAA,GAAA,CAAS,CAAA,CAAE,QAAQ,CAAA,CAAE,KAAA,IAAS,WAAW,CAAC,CAAA;AAAA,IACrD;AAAA,EACF;AAGA,EAAA,OAAO,SAAA,CAAU,SAAA,CAAU,MAAA,GAAS,CAAC,CAAA,CAAE,KAAA;AACzC;AASO,SAAS,mBAAA,CACd,MAAA,EACA,QAAA,EACA,MAAA,EACQ;AACR,EAAA,IAAI,GAAA,GAAM,CAAA;AACV,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,MAAM,IAAA,GAAO,gBAAA,CAAiB,KAAA,CAAM,EAAA,EAAI,UAAU,MAAM,CAAA;AACxD,IAAA,IAAI,IAAA,GAAO,KAAK,GAAA,GAAM,IAAA;AAAA,EACxB;AACA,EAAA,OAAO,GAAA;AACT;AAEO,SAAS,aAAA,CACd,QAAA,EACA,OAAA,EACA,aAAA,EACY;AAEZ,EAAA,MAAM,CAAA,GAAI,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,GAAA,CAAI,CAAA,EAAG,QAAQ,CAAC,CAAA;AAG3C,EAAA,MAAM,EAAA,GAAK,kBAAA,CAAmB,OAAA,CAAQ,SAAA,CAAU,UAAU,CAAA;AAG1D,EAAA,MAAM,eAAe,mBAAA,CAAoB,OAAA,CAAQ,MAAA,EAAQ,CAAA,EAAG,QAAQ,MAAM,CAAA;AAG1E,EAAA,MAAM,SAAuB,EAAC;AAC9B,EAAA,KAAA,MAAW,KAAA,IAAS,QAAQ,MAAA,EAAQ;AAClC,IAAA,MAAM,QAAQ,iBAAA,CAAkB,KAAA,EAAO,GAAG,OAAA,CAAQ,MAAA,EAAQ,cAAc,aAAa,CAAA;AACrF,IAAA,IAAI,KAAA,EAAO,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA;AAAA,EAC9B;AAEA,EAAA,MAAM,SAAA,GAAY,iBAAiB,OAAA,CAAQ,MAAA,EAAQ,QAAQ,MAAA,EAAQ,CAAA,EAAG,GAAG,cAAc,CAAA;AACvF,EAAA,MAAM,UAAA,GAAa,iBAAA;AAAA,IACjB,OAAA,CAAQ,MAAA;AAAA,IACR,CAAA;AAAA,IACA,OAAA,CAAQ,MAAA;AAAA,IACR,MAAA;AAAA,IACA,aAAA;AAAA,IACA,EAAA,CAAG,eAAA;AAAA,IACH,EAAA,CAAG,oBAAA;AAAA,IACH,EAAA,CAAG;AAAA,GACL;AACA,EAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,OAAA,CAAQ,MAAA,EAAQ,CAAC,CAAA;AAC5C,EAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,OAAA,CAAQ,MAAA,EAAQ,QAAQ,CAAC,CAAA;AAGpD,EAAA,MAAM,UAAA,GAAa,iBAAA,CAAkB,OAAA,CAAQ,MAAA,EAAQ,CAAC,CAAA;AAKtD,EAAA,MAAM,UAAA,GAAa,CAAA;AACnB,EAAA,MAAM,YAAY,aAAA,EAAe,SAAA;AACjC,EAAA,MAAM,UAAA,GACJ,aAAA,IAAiB,aAAA,CAAc,eAAA,GAAkB,CAAA,IAAK,SAAA,IAAa,SAAA,GAAY,CAAA,GAC3E,UAAA,IAAe,aAAA,CAAc,eAAA,GAAkB,aAAA,CAAc,aAAc,SAAA,CAAA,GAC3E,MAAA;AAMN,EAAA,MAAM,kBAAkB,aAAA,EAAe,eAAA;AAEvC,EAAA,MAAM,MAAA,GAAS,UAAA;AAAA,IACb,aAAA,CAAc,OAAA,CAAQ,MAAA,EAAQ,OAAA,CAAQ,QAAQ,CAAC,CAAA;AAAA,IAC/C,CAAA;AAAA,IACA,UAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,MAAM,YAAA,GAAe,gBAAA;AAAA,IACnB,mBAAA,CAAoB,OAAA,CAAQ,MAAA,EAAQ,OAAA,CAAQ,QAAQ,CAAC,CAAA;AAAA,IACrD,CAAA;AAAA,IACA,UAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,MAAM,QAAQ,YAAA,CAAa,OAAA,CAAQ,MAAA,EAAQ,OAAA,CAAQ,QAAQ,CAAC,CAAA;AAC5D,EAAA,MAAM,iBAAA,GAAoB,iBAAA,CAAkB,OAAA,EAAS,CAAC,CAAA;AACtD,EAAA,MAAM,SAAA,GAAY,gBAAA,CAAiB,OAAA,CAAQ,MAAA,EAAQ,CAAC,CAAA;AAGpD,EAAA,MAAM,qBAAqB,yBAAA,CAA0B,OAAA,CAAQ,kBAAA,IAAsB,IAAI,CAAC,CAAA;AAGxF,EAAA,MAAM,aAAa,iBAAA,CAAkB,OAAA,CAAQ,cAAA,IAAkB,IAAI,CAAC,CAAA;AAGpE,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,SAAA,CAAU,UAAA,IAAc,MAAO,EAAA,GAAK,EAAA,CAAA;AAC/D,EAAA,MAAM,eAAe,UAAA,GAAa,CAAA;AAElC,EAAA,OAAO;AAAA,IACL,YAAA,EAAc,QAAQ,YAAA,IAAgB,YAAA;AAAA,IACtC,QAAA,EAAU,CAAA;AAAA,IACV,MAAA;AAAA,IACA,SAAA;AAAA,IACA,UAAA;AAAA,IACA,kBAAA;AAAA,IACA,KAAA;AAAA,IACA,KAAA;AAAA,IACA,UAAA;AAAA,IACA,MAAA;AAAA,IACA,YAAA;AAAA,IACA,KAAA;AAAA,IACA,iBAAA;AAAA,IACA,SAAA;AAAA,IACA,UAAA;AAAA,IACA,UAAA;AAAA,IACA;AAAA,GACF;AACF;;;AC52CO,SAAS,iBAAiB,OAAA,EAAyB;AACxD,EAAA,IAAI,CAAC,SAAS,OAAO,OAAA;AACrB,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,CAAM,uBAAuB,CAAA;AACnD,EAAA,OAAO,KAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,CAAE,aAAY,GAAI,OAAA;AAC1C;AAgBO,IAAM,sBAAA,GAAyB,IAAA;AAqBtC,SAAS,eAAA,CACP,OAAA,EACA,YAAA,EACA,OAAA,EACoD;AAGpD,EAAA,MAAM,gBAAA,GACJ,YAAA,CAAa,cAAA,KAAmB,MAAA,IAAa,aAAa,YAAA,KAAiB,MAAA;AAE7E,EAAA,IAAI,KAAA;AACJ,EAAA,IAAI,GAAA;AAEJ,EAAA,IAAI,gBAAA,EAAkB;AACpB,IAAA,KAAA,GAAQ,YAAA,CAAa,cAAA;AACrB,IAAA,GAAA,GAAM,YAAA,CAAa,YAAA;AAAA,EACrB,CAAA,MAAO;AAKL,IAAA,MAAM,aAAuB,EAAC;AAE9B,IAAA,KAAA,MAAW,KAAK,OAAA,CAAQ,OAAA,EAAS,UAAA,CAAW,IAAA,CAAK,EAAE,SAAS,CAAA;AAC5D,IAAA,KAAA,MAAW,CAAA,IAAK,QAAQ,GAAA,EAAK;AAC3B,MAAA,UAAA,CAAW,IAAA,CAAK,EAAE,SAAS,CAAA;AAC3B,MAAA,IAAI,CAAA,CAAE,QAAA,EAAU,UAAA,CAAW,IAAA,CAAK,EAAE,QAAQ,CAAA;AAAA,IAC5C;AACA,IAAA,KAAA,MAAW,KAAK,OAAA,CAAQ,QAAA,EAAU,UAAA,CAAW,IAAA,CAAK,EAAE,SAAS,CAAA;AAC7D,IAAA,KAAA,MAAW,KAAK,OAAA,CAAQ,MAAA,EAAQ,UAAA,CAAW,IAAA,CAAK,EAAE,QAAQ,CAAA;AAE1D,IAAA,IAAI,UAAA,CAAW,WAAW,CAAA,EAAG;AAC3B,MAAA,MAAM,IAAI,MAAM,0EAAqE,CAAA;AAAA,IACvF;AAEA,IAAA,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,GAAG,UAAU,CAAA;AAC9B,IAAA,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,GAAG,UAAU,CAAA;AAAA,EAC9B;AAGA,EAAA,MAAM,UAAA,GAAa,SAAS,UAAA,IAAc,CAAA;AAC1C,EAAA,MAAM,QAAA,GAAW,SAAS,QAAA,IAAY,CAAA;AACtC,EAAA,KAAA,GAAQ,KAAA,GAAQ,UAAA;AAChB,EAAA,GAAA,GAAM,GAAA,GAAM,QAAA;AAEZ,EAAA,MAAM,cAAc,GAAA,GAAM,KAAA;AAG1B,EAAA,MAAM,WAAA,GAAc,IAAA;AACpB,EAAA,MAAM,iBAAA,GAAoB,WAAA,GAAc,CAAA,GAAI,WAAA,GAAc,WAAA;AAG1D,EAAA,MAAM,UAAA,GAAa,qBAAqB,CAAA,GAAI,sBAAA,CAAA;AAE5C,EAAA,OAAO,EAAE,KAAA,EAAO,GAAA,EAAK,UAAA,EAAW;AAClC;AAQA,SAAS,YAAY,OAAA,EAAoC;AACvD,EAAA,OAAO,OAAA,CAAQ,MAAA,CAAO,GAAA,CAAI,CAAC,KAAA,MAAW;AAAA,IACpC,IAAI,KAAA,CAAM,EAAA;AAAA,IACV,MAAM,KAAA,CAAM,IAAA;AAAA,IACZ,UAAU,KAAA,CAAM,QAAA;AAAA,IAChB,KAAA,EAAO,CAAA,CAAA,EAAI,KAAA,CAAM,EAAE,CAAA;AAAA,GACrB,CAAE,CAAA;AACJ;AAgBA,SAAS,WAAA,CAAY,SAAkB,YAAA,EAA2C;AAChF,EAAA,MAAM,SAAwB,EAAC;AAG/B,EAAA,KAAA,MAAW,KAAA,IAAS,QAAQ,MAAA,EAAQ;AAClC,IAAA,MAAA,CAAO,IAAA,CAAK;AAAA,MACV,WAAW,KAAA,CAAM,QAAA;AAAA,MACjB,IAAA,EAAM,YAAA;AAAA,MACN,OAAO,KAAA,CAAM,EAAA;AAAA,MACb,QAAA,EAAU,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA;AAAK,KAC9B,CAAA;AAAA,EACH;AAGA,EAAA,KAAA,MAAW,MAAA,IAAU,QAAQ,OAAA,EAAS;AACpC,IAAA,MAAA,CAAO,IAAA,CAAK;AAAA,MACV,WAAW,MAAA,CAAO,SAAA;AAAA,MAClB,IAAA,EAAM,QAAA;AAAA,MACN,OAAO,MAAA,CAAO,KAAA;AAAA,MACd,QAAA,EAAU;AAAA,QACR,KAAK,MAAA,CAAO,GAAA;AAAA,QACZ,cAAc,MAAA,CAAO,YAAA;AAAA,QACrB,YAAY,MAAA,CAAO,UAAA;AAAA,QACnB,WAAW,MAAA,CAAO,SAAA;AAAA,QAClB,UAAU,MAAA,CAAO,QAAA;AAAA,QACjB,UAAA,EAAY,gBAAA,CAAiB,MAAA,CAAO,OAAO;AAAA;AAC7C,KACD,CAAA;AAAA,EACH;AAGA,EAAA,KAAA,MAAW,EAAA,IAAM,QAAQ,GAAA,EAAK;AAC5B,IAAA,MAAA,CAAO,IAAA,CAAK;AAAA,MACV,WAAW,EAAA,CAAG,SAAA;AAAA,MACd,IAAA,EAAM,YAAA;AAAA,MACN,OAAO,EAAA,CAAG,KAAA;AAAA,MACV,QAAA,EAAU;AAAA,QACR,UAAU,EAAA,CAAG,MAAA;AAAA,QACb,OAAO,EAAA,CAAG,KAAA;AAAA,QACV,WAAW,EAAA,CAAG,SAAA;AAAA,QACd,WAAW,EAAA,CAAG;AAAA;AAChB,KACD,CAAA;AAED,IAAA,IAAI,GAAG,QAAA,EAAU;AACf,MAAA,MAAA,CAAO,IAAA,CAAK;AAAA,QACV,WAAW,EAAA,CAAG,QAAA;AAAA,QACd,IAAA,EAAM,WAAA;AAAA,QACN,OAAO,EAAA,CAAG,KAAA;AAAA,QACV,QAAA,EAAU;AAAA,UACR,UAAU,EAAA,CAAG,MAAA;AAAA,UACb,OAAO,EAAA,CAAG;AAAA;AACZ,OACD,CAAA;AAAA,IACH;AAAA,EACF;AAGA,EAAA,KAAA,MAAW,GAAA,IAAO,QAAQ,QAAA,EAAU;AAClC,IAAA,MAAA,CAAO,IAAA,CAAK;AAAA,MACV,WAAW,GAAA,CAAI,SAAA;AAAA,MACf,IAAA,EAAM,SAAA;AAAA,MACN,OAAO,GAAA,CAAI,MAAA;AAAA,MACX,WAAA,EAAa,GAAA,CAAI,QAAA,CAAS,CAAC,CAAA;AAAA;AAAA,MAC3B,QAAA,EAAU;AAAA,QACR,UAAU,GAAA,CAAI;AAAA;AAChB,KACD,CAAA;AAAA,EACH;AAGA,EAAA,KAAA,MAAW,SAAA,IAAa,aAAa,UAAA,EAAY;AAC/C,IAAA,MAAA,CAAO,IAAA,CAAK;AAAA,MACV,WAAW,SAAA,CAAU,SAAA;AAAA,MACrB,IAAA,EAAM,WAAA;AAAA,MACN,KAAA,EAAO,EAAA;AAAA;AAAA,MACP,QAAA,EAAU;AAAA,QACR,OAAO,SAAA,CAAU,KAAA;AAAA,QACjB,OAAO,SAAA,CAAU;AAAA;AACnB,KACD,CAAA;AAAA,EACH;AAGA,EAAA,KAAA,MAAW,QAAA,IAAY,QAAQ,SAAA,EAAW;AACxC,IAAA,MAAA,CAAO,IAAA,CAAK;AAAA,MACV,WAAW,QAAA,CAAS,SAAA;AAAA,MACpB,IAAA,EAAM,UAAA;AAAA,MACN,KAAA,EAAO,SAAS,KAAA,IAAS,EAAA;AAAA,MACzB,QAAA,EAAU;AAAA,QACR,MAAM,QAAA,CAAS,IAAA;AAAA,QACf,MAAM,QAAA,CAAS;AAAA;AACjB,KACD,CAAA;AAAA,EACH;AAGA,EAAA,MAAA,CAAO,KAAK,CAAC,CAAA,EAAG,MAAM,CAAA,CAAE,SAAA,GAAY,EAAE,SAAS,CAAA;AAE/C,EAAA,OAAO,MAAA;AACT;AASA,IAAM,YAAA,GAAe;AAAA,EACnB,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA;AAAA;AACF,CAAA;AAWA,SAAS,SAAA,CACP,cACA,SAAA,EACe;AACf,EAAA,MAAM,SAAS,YAAA,CAAa,MAAA;AAC5B,EAAA,IAAI,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAEjC,EAAA,OAAO,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,MAAO;AAAA,IAC3B,IAAI,CAAA,CAAE,EAAA;AAAA,IACN,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,SAAA,EAAW,CAAA,KAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,EAAE,SAAA,EAAW,SAAA,CAAU,KAAK,CAAA,GAAI,CAAA,CAAE,SAAA;AAAA,IAChE,OAAA,EAAS,CAAA,KAAM,MAAA,CAAO,MAAA,GAAS,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,OAAA,EAAS,SAAA,CAAU,GAAG,CAAA,GAAI,CAAA,CAAE,OAAA;AAAA,IAC1E,KAAA,EAAO,YAAA,CAAa,CAAA,GAAI,YAAA,CAAa,MAAM;AAAA,GAC7C,CAAE,CAAA;AACJ;AAIA,SAAS,UAAU,YAAA,EAA2C;AAC5D,EAAA,OAAO,YAAA,CAAa,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IACrC,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,SAAS,CAAA,CAAE,OAAA;AAAA,IACX,WAAW,CAAA,CAAE,SAAA;AAAA,IACb,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,GAAI,CAAA,CAAE,KAAA,IAAS,EAAE,KAAA,EAAO,EAAE,KAAA,EAAM;AAAA,IAChC,GAAI,CAAA,CAAE,IAAA,IAAQ,EAAE,IAAA,EAAM,EAAE,IAAA;AAAK,GAC/B,CAAE,CAAA;AACJ;AAIA,SAAS,cAAc,YAAA,EAA+C;AACpE,EAAA,OAAO,YAAA,CAAa,UAAA,CAAW,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IACzC,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,WAAW,CAAA,CAAE,SAAA;AAAA,IACb,OAAO,CAAA,CAAE;AAAA,GACX,CAAE,CAAA;AACJ;AAIA,SAAS,kBAAkB,YAAA,EAAmD;AAC5E,EAAA,OAAO,YAAA,CAAa,cAAA,CAAe,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IAC7C,WAAW,CAAA,CAAE,SAAA;AAAA,IACb,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,OAAO,CAAA,CAAE;AAAA,GACX,CAAE,CAAA;AACJ;AAOA,IAAM,mBAAA,GAAsB,SAAA;AAerB,SAAS,uBAAA,CACd,YACA,YAAA,EAC2B;AAC3B,EAAA,IAAI,UAAA,IAAc,CAAA,EAAG,OAAO,EAAC;AAG7B,EAAA,IAAI,YAAA,CAAa,kBAAA,IAAsB,YAAA,CAAa,kBAAA,CAAmB,SAAS,CAAA,EAAG;AACjF,IAAA,MAAM,cAAA,GAAiB,qBAAqB,YAAY,CAAA;AACxD,IAAA,OAAO,YAAA,CAAa,kBAAA,CACjB,GAAA,CAAI,CAAC,KAAA,MAAW;AAAA,MACf,cAAA,EAAgB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,CAAA,EAAA,CAAI,KAAA,CAAM,SAAA,GAAY,cAAA,IAAkB,UAAU,CAAC,CAAA;AAAA,MACxF,YAAY,KAAA,CAAM,UAAA;AAAA,MAClB,YAAY,KAAA,CAAM,UAAA;AAAA,MAClB,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,MAAM,KAAA,CAAM;AAAA,KACd,CAAE,EACD,IAAA,CAAK,CAAC,GAAG,CAAA,KAAM,CAAA,CAAE,cAAA,GAAiB,CAAA,CAAE,cAAc,CAAA;AAAA,EACvD;AAGA,EAAA,OAAO,qBAAA,CAAsB,YAAY,YAAY,CAAA;AACvD;AAMA,SAAS,qBAAqB,YAAA,EAAoC;AAChE,EAAA,IAAI,YAAA,CAAa,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,OAAO,CAAA;AAC7C,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,GAAG,YAAA,CAAa,MAAA,CAAO,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,CAAC,CAAA;AAChE;AAMA,SAAS,qBAAA,CACP,YACA,YAAA,EAC2B;AAC3B,EAAA,IAAI,YAAA,CAAa,MAAA,CAAO,MAAA,KAAW,CAAA,SAAU,EAAC;AAE9C,EAAA,MAAM,cAAA,GAAiB,qBAAqB,YAAY,CAAA;AAExD,EAAA,OAAO,YAAA,CAAa,MAAA,CACjB,GAAA,CAAI,CAAC,KAAA,MAAW;AAAA,IACf,cAAA,EAAgB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,CAAA,EAAA,CAAI,KAAA,CAAM,SAAA,GAAY,cAAA,IAAkB,UAAU,CAAC,CAAA;AAAA,IACxF,UAAA,EAAY,KAAA,CAAM,IAAA,CAAK,WAAA,EAAY;AAAA,IACnC,UAAA,EAAY,mBAAA;AAAA,IACZ,MAAM,KAAA,CAAM,IAAA;AAAA,IACZ,IAAA,EAAM;AAAA,GACR,CAAE,EACD,IAAA,CAAK,CAAC,GAAG,CAAA,KAAM,CAAA,CAAE,cAAA,GAAiB,CAAA,CAAE,cAAc,CAAA;AACvD;AAWO,SAAS,WAAA,CACd,OAAA,EACA,YAAA,EACA,OAAA,EACY;AACZ,EAAA,MAAM,SAAA,GAAY,eAAA,CAAgB,OAAA,EAAS,YAAA,EAAc,OAAO,CAAA;AAChE,EAAA,MAAM,MAAA,GAAS,YAAY,OAAO,CAAA;AAClC,EAAA,MAAM,MAAA,GAAS,WAAA,CAAY,OAAA,EAAS,YAAY,CAAA;AAChD,EAAA,MAAM,MAAA,GAAS,SAAA,CAAU,YAAA,EAAc,SAAS,CAAA;AAChD,EAAA,MAAM,MAAA,GAAS,UAAU,YAAY,CAAA;AACrC,EAAA,MAAM,UAAA,GAAa,cAAc,YAAY,CAAA;AAC7C,EAAA,MAAM,cAAA,GAAiB,kBAAkB,YAAY,CAAA;AAErD,EAAA,MAAM,kBAAA,GAAqB,uBAAA,CAAwB,SAAA,CAAU,UAAA,EAAY,YAAY,CAAA;AAErF,EAAA,OAAO;AAAA,IACL,YAAA,EAAc,QAAQ,OAAA,CAAQ,IAAA;AAAA,IAC9B,MAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA,cAAA;AAAA,IACA,kBAAA;AAAA,IACA,SAAA;AAAA,IACA,KAAA,EAAO;AAAA,MACL,YAAA,EAAc,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAC9B,QAAA,EAAU,QAAQ,GAAA,CAAI,MAAA;AAAA,MACtB,aAAA,EAAe,QAAQ,QAAA,CAAS,MAAA;AAAA,MAChC,WAAA,EAAa,QAAQ,MAAA,CAAO;AAAA;AAC9B,GACF;AACF;;;ACjZA,IAAM,MAAA,GAAS,IAAA;AAEf,IAAM,SAAA,GAAY,IAAA;AAElB,IAAM,SAAA,GAAY,IAAA;AAElB,IAAM,mBAAA,GAAsB,CAAA;AAE5B,IAAM,GAAA,GAAA,CAAO,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,IAAK,CAAA;AAEjC,IAAM,UAAA,GAAa,CAAA;AAQZ,SAAS,qBAAA,CACd,MAAA,EACA,QAAA,EACA,aAAA,EAC4B;AAE5B,EAAA,MAAM,IAAA,GACJ,OAAO,aAAA,KAAkB,QAAA,GAAW,EAAE,WAAA,EAAa,aAAA,EAAc,GAAK,aAAA,IAAiB,EAAC;AAE1F,EAAA,IAAI,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,2BAAW,GAAA,EAAI;AAGxC,EAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACvB,IAAA,uBAAO,IAAI,GAAA,CAAI,CAAC,CAAC,OAAO,CAAC,CAAA,CAAE,EAAA,EAAI,EAAE,GAAG,GAAA,EAAK,CAAA,EAAG,GAAA,EAAK,CAAC,CAAC,CAAA;AAAA,EACrD;AAGA,EAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACvB,IAAA,MAAM,CAAC,CAAA,EAAG,CAAC,CAAA,GAAI,MAAA;AACf,IAAA,MAAM,aAAa,CAAA,CAAE,IAAA,KAAS,OAAA,GAAU,CAAC,GAAG,CAAC,CAAA,GAAI,CAAA,CAAE,IAAA,KAAS,UAAU,CAAC,CAAA,EAAG,CAAC,CAAA,GAAI,CAAC,GAAG,CAAC,CAAA;AACpF,IAAA,2BAAW,GAAA,CAAI;AAAA,MACb,CAAC,UAAA,CAAW,CAAC,CAAA,CAAE,EAAA,EAAI,EAAE,CAAA,EAAG,IAAA,EAAM,CAAA,EAAG,IAAA,EAAM,CAAA;AAAA,MACvC,CAAC,UAAA,CAAW,CAAC,CAAA,CAAE,EAAA,EAAI,EAAE,CAAA,EAAG,IAAA,EAAM,CAAA,EAAG,IAAA,EAAM;AAAA,KACxC,CAAA;AAAA,EACH;AAEA,EAAA,MAAM,UAAA,GAAa,KAAK,UAAA,IAAc,mBAAA;AACtC,EAAA,MAAM,UAAU,IAAA,CAAK,GAAA,CAAI,SAAA,EAAW,IAAA,CAAK,eAAe,SAAS,CAAA;AAIjE,EAAA,MAAM,UAAU,IAAA,CAAK,WAAA,IAAe,KAAK,GAAA,CAAI,SAAA,EAAW,UAAU,UAAU,CAAA;AAG5E,EAAA,MAAM,SAAA,GAAY,cAAA,CAAe,MAAA,EAAQ,QAAQ,CAAA;AAGjD,EAAA,MAAM,OAAA,GAAU,WAAA,CAAY,MAAA,EAAQ,SAAS,CAAA;AAG7C,EAAA,MAAM,EAAE,MAAM,IAAA,EAAK,GAAI,gBAAgB,OAAA,CAAQ,MAAA,EAAQ,SAAS,OAAO,CAAA;AAGvE,EAAA,MAAM,YAAY,mBAAA,CAAoB,OAAA,EAAS,IAAA,EAAM,IAAA,EAAM,SAAS,OAAO,CAAA;AAE3E,EAAA,OAAO,SAAA;AACT;AAYA,SAAS,WAAA,CAAY,QAAsB,SAAA,EAAuC;AAEhF,EAAA,MAAM,SAAuB,EAAC;AAC9B,EAAA,MAAM,eAA6B,EAAC;AACpC,EAAA,MAAM,UAAwB,EAAC;AAC/B,EAAA,MAAM,WAAyB,EAAC;AAEhC,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,QAAQ,MAAM,IAAA;AAAM,MAClB,KAAK,OAAA;AACH,QAAA,MAAA,CAAO,KAAK,KAAK,CAAA;AACjB,QAAA;AAAA,MACF,KAAK,MAAA;AAAA,MACL,KAAK,IAAA;AAAA,MACL,KAAK,UAAA;AACH,QAAA,YAAA,CAAa,KAAK,KAAK,CAAA;AACvB,QAAA;AAAA,MACF,KAAK,UAAA;AAAA,MACL,KAAK,YAAA;AAAA,MACL,KAAK,QAAA;AAAA,MACL,KAAK,SAAA;AACH,QAAA,OAAA,CAAQ,KAAK,KAAK,CAAA;AAClB,QAAA;AAAA,MACF;AACE,QAAA,QAAA,CAAS,KAAK,KAAK,CAAA;AACnB,QAAA;AAAA;AACJ,EACF;AAGA,EAAA,MAAM,YAAA,GAAe,cAAA,CAAe,YAAA,EAAc,SAAS,CAAA;AAC3D,EAAA,MAAM,cAAA,GAAiB,cAAA,CAAe,OAAA,EAAS,SAAS,CAAA;AACxD,EAAA,MAAM,eAAA,GAAkB,cAAA,CAAe,QAAA,EAAU,SAAS,CAAA;AAI1D,EAAA,OAAO,CAAC,GAAG,MAAA,EAAQ,GAAG,cAAc,GAAG,cAAA,EAAgB,GAAG,eAAe,CAAA;AAC3E;AAMA,SAAS,cAAA,CAAe,QAAsB,SAAA,EAAuC;AACnF,EAAA,IAAI,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AACjC,EAAA,MAAM,OAAA,GAAU,WAAA,CAAY,MAAA,EAAQ,SAAS,CAAA;AAC7C,EAAA,OAAO,QAAQ,IAAA,EAAK;AACtB;AAeA,SAAS,eAAA,CACP,CAAA,EACA,OAAA,EACA,OAAA,EACgC;AAChC,EAAA,MAAM,MAAA,GAAS,IAAI,CAAA,GAAI,MAAA;AAGvB,EAAA,MAAM,OAAA,GAAU,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,MAAA,GAAS,OAAO,CAAA,GAAI,CAAC,CAAA;AAC5D,EAAA,MAAM,OAAA,GAAU,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,MAAA,GAAS,OAAO,CAAA,GAAI,CAAC,CAAA;AAK5D,EAAA,IAAI,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,OAAA,EAAS,CAAC,CAAA;AAClC,EAAA,IAAI,QAAA,GAAW,CAAA;AACf,EAAA,IAAI,YAAY,MAAA,CAAO,iBAAA;AAEvB,EAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,IAAK,IAAA,CAAK,IAAI,OAAA,EAAS,CAAC,GAAG,CAAA,EAAA,EAAK;AAC9C,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,CAAA,GAAI,CAAC,CAAA;AACzB,IAAA,IAAI,IAAI,OAAA,EAAS;AACjB,IAAA,IAAI,CAAA,GAAI,IAAI,CAAA,EAAG;AAEf,IAAA,MAAM,KAAA,GAAQ,IAAI,CAAA,GAAI,CAAA;AACtB,IAAA,MAAM,WAAA,GAAc,CAAA,GAAI,CAAA,KAAM,CAAA,GAAI,CAAA,GAAI,CAAA;AAGtC,IAAA,MAAM,QAAQ,CAAA,GAAI,CAAA;AAClB,IAAA,MAAM,YAAA,GAAe,CAAC,IAAA,CAAK,GAAA,CAAI,KAAK,GAAA,CAAI,KAAK,CAAC,CAAA,GAAI,CAAA;AAClD,IAAA,MAAM,KAAA,GAAQ,cAAc,YAAA,GAAe,KAAA;AAE3C,IAAA,IAAI,QAAQ,SAAA,EAAW;AACrB,MAAA,SAAA,GAAY,KAAA;AACZ,MAAA,QAAA,GAAW,CAAA;AACX,MAAA,QAAA,GAAW,CAAA;AAAA,IACb;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,IAAA,EAAM,QAAA,EAAS;AAC1C;AAkBA,SAAS,mBAAA,CACP,MAAA,EACA,IAAA,EACA,IAAA,EACA,SACA,OAAA,EAC4B;AAC5B,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAA2B;AACjD,EAAA,MAAM,MAAA,GAAS,IAAI,CAAA,GAAI,MAAA;AAGvB,EAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,IAAA,EAAM,IAAI,CAAA;AASpC,EAAA,MAAM,YAAA,GAAe,GAAA;AACrB,EAAA,MAAM,aAAA,GAAgB,IAAA,GAAO,CAAA,IAAK,IAAA,GAAO,CAAA,GAAI,IAAA,GAAO,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,GAAO,CAAC,CAAA;AAC9E,EAAA,MAAM,WAAA,GAAc,IAAA,GAAO,CAAA,GAAI,MAAA,GAAS,aAAA,GAAgB,CAAA;AACxD,EAAA,MAAM,WAAA,GAAc,IAAA,GAAO,CAAA,GAAI,MAAA,IAAU,OAAO,CAAA,CAAA,GAAK,CAAA;AACrD,EAAA,MAAM,KAAA,GAAQ,OAAO,CAAA,GAAI,IAAA,CAAK,IAAI,WAAA,EAAa,OAAA,GAAU,YAAY,CAAA,GAAI,CAAA;AAEzE,EAAA,MAAM,KAAA,GAAQ,OAAO,CAAA,GAAI,IAAA,CAAK,IAAI,WAAA,EAAa,OAAA,GAAU,YAAY,CAAA,GAAI,CAAA;AAGzE,EAAA,MAAM,MAAA,GAAS,IAAA,GAAO,CAAA,IAAK,IAAA,GAAO,CAAA;AAClC,EAAA,MAAM,QAAA,GAAW,MAAA,GAAS,KAAA,GAAQ,GAAA,GAAM,CAAA;AAGxC,EAAA,MAAM,YAAY,IAAA,GAAO,CAAA,GAAA,CAAK,IAAA,GAAO,CAAA,IAAK,QAAQ,QAAA,GAAW,CAAA;AAC7D,EAAA,MAAM,UAAA,GAAa,IAAA,GAAO,CAAA,GAAA,CAAK,IAAA,GAAO,KAAK,KAAA,GAAQ,CAAA;AAGnD,EAAA,MAAM,UAAU,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,GAAA,GAAM,YAAY,CAAC,CAAA;AACpD,EAAA,MAAM,UAAU,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,GAAA,GAAM,aAAa,CAAC,CAAA;AAKrD,EAAA,MAAM,UAAA,GAAa,OAAO,CAAA,GAAI,IAAA,CAAK,IAAI,CAAA,EAAA,CAAI,KAAA,GAAQ,OAAA,IAAW,GAAG,CAAA,GAAI,CAAA;AACrE,EAAA,MAAM,UAAA,GAAa,OAAO,CAAA,GAAI,IAAA,CAAK,IAAI,CAAA,EAAA,CAAI,KAAA,GAAQ,OAAA,IAAW,GAAG,CAAA,GAAI,CAAA;AAErE,EAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,MAAA,CAAO,UAAU,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AAC1D,IAAA,MAAM,EAAE,GAAA,EAAK,GAAA,EAAI,GAAI,MAAM,CAAC,CAAA;AAG5B,IAAA,IAAI,CAAA,GAAI,IAAA,GAAO,CAAA,GAAI,OAAA,GAAU,MAAM,KAAA,GAAQ,GAAA;AAC3C,IAAA,IAAI,CAAA,GAAI,IAAA,GAAO,CAAA,GAAI,OAAA,GAAU,MAAM,KAAA,GAAQ,GAAA;AAG3C,IAAA,IAAI,GAAA,GAAM,CAAA,KAAM,CAAA,IAAK,IAAA,GAAO,CAAA,EAAG;AAC7B,MAAA,CAAA,IAAK,QAAA;AAAA,IACP;AAKA,IAAA,MAAM,SAAA,GAAY,IAAA;AAClB,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,GAAA,CAAA,CAAK,CAAA,GAAI,MAAA,IAAU,YAAY,CAAA,GAAI,MAAA,GAAS,CAAA,IAAK,SAAA,EAAW,CAAC,CAAA;AACpF,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,GAAA,CAAA,CAAK,CAAA,GAAI,MAAA,IAAU,YAAY,CAAA,GAAI,MAAA,GAAS,CAAA,IAAK,SAAA,EAAW,CAAC,CAAA;AACpF,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,SAAS,CAAA;AACnC,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,SAAS,CAAA;AAEnC,IAAA,MAAM,WAAa,CAAA,GAAI,GAAA,GAAO,IAAK,GAAA,IAAO,CAAA,GAAI,aAAa,UAAA,GAAa,KAAA;AACxE,IAAA,MAAM,OAAA,GAAA,CAAa,IAAI,GAAA,GAAM,GAAA,GAAO,IAAK,GAAA,IAAO,CAAA,GAAI,aAAa,UAAA,GAAa,KAAA;AAC9E,IAAA,CAAA,IAAK,OAAA;AACL,IAAA,CAAA,IAAK,OAAA;AAGL,IAAA,CAAA,GAAI,IAAA,CAAK,IAAI,MAAA,EAAQ,IAAA,CAAK,IAAI,CAAA,GAAI,MAAA,EAAQ,CAAC,CAAC,CAAA;AAC5C,IAAA,CAAA,GAAI,IAAA,CAAK,IAAI,MAAA,EAAQ,IAAA,CAAK,IAAI,CAAA,GAAI,MAAA,EAAQ,CAAC,CAAC,CAAA;AAE5C,IAAA,SAAA,CAAU,GAAA,CAAI,OAAO,CAAC,CAAA,CAAE,IAAI,EAAE,CAAA,EAAG,GAAG,CAAA;AAAA,EACtC;AAEA,EAAA,OAAO,SAAA;AACT;AAOA,SAAS,WAAA,CAAY,MAAc,IAAA,EAAmD;AACpF,EAAA,MAAM,SAAA,GAAA,CAAa,OAAO,CAAA,IAAK,CAAA;AAC/B,EAAA,MAAM,SAAA,GAAA,CAAa,OAAO,CAAA,IAAK,CAAA;AAG/B,EAAA,MAAM,QAA0E,EAAC;AACjF,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,IAAA,EAAM,CAAA,EAAA,EAAK;AAC7B,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,IAAA,EAAM,CAAA,EAAA,EAAK;AAC7B,MAAA,MAAM,KAAK,CAAA,GAAI,SAAA;AACf,MAAA,MAAM,KAAK,CAAA,GAAI,SAAA;AACf,MAAA,MAAM,OAAO,IAAA,CAAK,IAAA,CAAK,EAAA,GAAK,EAAA,GAAK,KAAK,EAAE,CAAA;AACxC,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,EAAA,EAAI,EAAE,CAAA;AAC/B,MAAA,KAAA,CAAM,IAAA,CAAK,EAAE,GAAA,EAAK,CAAA,EAAG,KAAK,CAAA,EAAG,IAAA,EAAM,OAAO,CAAA;AAAA,IAC5C;AAAA,EACF;AAGA,EAAA,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM;AACnB,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,IAAA,GAAO,CAAA,CAAE,IAAI,CAAA,GAAI,IAAA,EAAM,OAAO,CAAA,CAAE,IAAA,GAAO,CAAA,CAAE,IAAA;AACxD,IAAA,OAAO,CAAA,CAAE,QAAQ,CAAA,CAAE,KAAA;AAAA,EACrB,CAAC,CAAA;AAED,EAAA,OAAO,KAAA;AACT;AAUA,SAAS,cAAA,CAAe,QAAsB,QAAA,EAAwC;AACpF,EAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,MAAA,CAAO,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,EAAE,CAAC,CAAA;AAChD,EAAA,MAAM,GAAA,uBAAwB,GAAA,EAAI;AAElC,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,GAAA,CAAI,GAAA,CAAI,KAAA,CAAM,EAAA,kBAAI,IAAI,KAAK,CAAA;AAAA,EAC7B;AAEA,EAAA,KAAA,MAAW,OAAO,QAAA,EAAU;AAC1B,IAAA,IAAI,CAAC,QAAA,CAAS,GAAA,CAAI,GAAA,CAAI,MAAM,CAAA,EAAG;AAE/B,IAAA,KAAA,MAAW,OAAA,IAAW,IAAI,QAAA,EAAU;AAClC,MAAA,IAAI,CAAC,QAAA,CAAS,GAAA,CAAI,OAAO,CAAA,EAAG;AAC5B,MAAA,IAAI,OAAA,KAAY,IAAI,MAAA,EAAQ;AAE5B,MAAA,MAAM,SAAA,GAAY,GAAA,CAAI,GAAA,CAAI,GAAA,CAAI,MAAM,CAAA;AACpC,MAAA,SAAA,CAAU,IAAI,OAAA,EAAA,CAAU,SAAA,CAAU,IAAI,OAAO,CAAA,IAAK,KAAK,CAAC,CAAA;AAExD,MAAA,MAAM,UAAA,GAAa,GAAA,CAAI,GAAA,CAAI,OAAO,CAAA;AAClC,MAAA,UAAA,CAAW,GAAA,CAAI,IAAI,MAAA,EAAA,CAAS,UAAA,CAAW,IAAI,GAAA,CAAI,MAAM,CAAA,IAAK,CAAA,IAAK,CAAC,CAAA;AAAA,IAClE;AAAA,EACF;AAEA,EAAA,OAAO,GAAA;AACT;AAUA,SAAS,WAAA,CAAY,QAAsB,SAAA,EAAyC;AAClF,EAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,MAAA,CAAO,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,EAAE,CAAC,CAAA;AAChD,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAY;AAChC,EAAA,MAAM,UAA0B,EAAC;AACjC,EAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,EAAA,EAAI,CAAC,CAAC,CAAC,CAAA;AAGrD,EAAA,MAAM,MAAA,GAAS,CAAC,GAAG,MAAM,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,QAAA,GAAW,CAAA,CAAE,QAAQ,CAAA;AAEjE,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,EAAE,CAAA,EAAG;AAE3B,IAAA,MAAM,SAAuB,EAAC;AAC9B,IAAA,MAAM,KAAA,GAAQ,CAAC,KAAA,CAAM,EAAE,CAAA;AACvB,IAAA,OAAA,CAAQ,GAAA,CAAI,MAAM,EAAE,CAAA;AAEpB,IAAA,OAAO,KAAA,CAAM,SAAS,CAAA,EAAG;AACvB,MAAA,MAAM,OAAA,GAAU,MAAM,KAAA,EAAM;AAC5B,MAAA,MAAA,CAAO,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,OAAO,CAAE,CAAA;AAElC,MAAA,MAAM,SAAA,GAAY,SAAA,CAAU,GAAA,CAAI,OAAO,CAAA;AACvC,MAAA,IAAI,CAAC,SAAA,EAAW;AAEhB,MAAA,KAAA,MAAW,CAAC,UAAU,CAAA,IAAK,SAAA,EAAW;AACpC,QAAA,IAAI,CAAC,QAAA,CAAS,GAAA,CAAI,UAAU,CAAA,EAAG;AAC/B,QAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,UAAU,CAAA,EAAG;AAC7B,QAAA,OAAA,CAAQ,IAAI,UAAU,CAAA;AACtB,QAAA,KAAA,CAAM,KAAK,UAAU,CAAA;AAAA,MACvB;AAAA,IACF;AAEA,IAAA,OAAA,CAAQ,KAAK,MAAM,CAAA;AAAA,EACrB;AAGA,EAAA,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM;AACrB,IAAA,IAAI,EAAE,MAAA,KAAW,CAAA,CAAE,QAAQ,OAAO,CAAA,CAAE,SAAS,CAAA,CAAE,MAAA;AAC/C,IAAA,OAAO,IAAA,CAAK,IAAI,GAAG,CAAA,CAAE,IAAI,CAAC,CAAA,KAAM,EAAE,QAAQ,CAAC,IAAI,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,QAAQ,CAAC,CAAA;AAAA,EACrF,CAAC,CAAA;AAGD,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM;AACpB,MAAA,MAAM,EAAA,GAAK,WAAA,CAAY,CAAA,CAAE,EAAA,EAAI,SAAS,CAAA;AACtC,MAAA,MAAM,EAAA,GAAK,WAAA,CAAY,CAAA,CAAE,EAAA,EAAI,SAAS,CAAA;AACtC,MAAA,IAAI,EAAA,KAAO,EAAA,EAAI,OAAO,EAAA,GAAK,EAAA;AAC3B,MAAA,OAAO,CAAA,CAAE,WAAW,CAAA,CAAE,QAAA;AAAA,IACxB,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,OAAA;AACT;AAEA,SAAS,WAAA,CAAY,SAAiB,SAAA,EAAiC;AACrE,EAAA,MAAM,SAAA,GAAY,SAAA,CAAU,GAAA,CAAI,OAAO,CAAA;AACvC,EAAA,IAAI,CAAC,WAAW,OAAO,CAAA;AACvB,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,KAAA,MAAW,GAAG,MAAM,CAAA,IAAK,SAAA,EAAW;AAClC,IAAA,KAAA,IAAS,MAAA;AAAA,EACX;AACA,EAAA,OAAO,KAAA;AACT;AAWO,SAAS,sBAAA,CACd,KACA,aAAA,EAC4B;AAC5B,EAAA,MAAM,MAAA,GAAS,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IACpC,IAAI,CAAA,CAAE,EAAA;AAAA,IACN,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,UAAU,CAAA,CAAE;AAAA,GACd,CAAE,CAAA;AAGF,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,MAAA,CAClB,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,SAAA,IAAa,CAAA,CAAE,WAAW,CAAA,CACnD,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IACX,QAAQ,CAAA,CAAE,KAAA;AAAA,IACV,QAAA,EAAU,CAAC,CAAA,CAAE,WAAY;AAAA,GAC3B,CAAE,CAAA;AAEJ,EAAA,OAAO,qBAAA,CAAsB,MAAA,EAAQ,QAAA,EAAU,aAAa,CAAA;AAC9D;;;ACndA,IAAM,WAAA,GAAc,SAAA;AASpB,IAAM,aAAA,GAAgB;AAAA,EACpB,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA;AAAA;AACF,CAAA;AAKO,SAAS,aAAA,CAAc,GAAA,EAAa,IAAA,EAAc,KAAA,EAAuB;AAC9E,EAAA,IAAI,IAAA,KAAS,SAAS,OAAO,WAAA;AAC7B,EAAA,OAAO,aAAA,CAAc,KAAA,GAAQ,aAAA,CAAc,MAAM,CAAA;AACnD;AAEA,SAAS,eAAe,IAAA,EAAsB;AAC5C,EAAA,OAAO,IAAA,KAAS,UAAU,IAAA,GAAO,IAAA;AACnC;AAEA,SAAS,SAAA,CAAU,SAAA,EAAmB,KAAA,EAAe,UAAA,EAA4B;AAC/E,EAAA,OAAA,CAAQ,YAAY,KAAA,IAAS,UAAA;AAC/B;AAWO,SAAS,aAAA,CAAc,KAAiB,UAAA,EAA8C;AAC3F,EAAA,MAAM,EAAE,KAAA,EAAO,GAAA,EAAI,GAAI,GAAA,CAAI,SAAA;AAC3B,EAAA,MAAM,cAAc,GAAA,GAAM,KAAA;AAM1B,EAAA,MAAM,gBAAA,GAAmB,eAAe,CAAA,GAAI,sBAAA,CAAA;AAC5C,EAAA,MAAM,UAAA,GACJ,IAAI,SAAA,CAAU,UAAA,GAAa,mBAAmB,IAAA,GAC1C,gBAAA,GACA,IAAI,SAAA,CAAU,UAAA;AAGpB,EAAA,MAAM,SAAA,GAAY,sBAAA,CAAuB,GAAA,EAAK,UAAU,CAAA;AAGxD,EAAA,MAAM,SAAwB,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,CAAC,GAAG,CAAA,MAAO;AAAA,IACtD,IAAI,CAAA,CAAE,EAAA;AAAA,IACN,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,OAAO,aAAA,CAAc,CAAA,CAAE,EAAA,EAAI,CAAA,CAAE,MAAM,CAAC,CAAA;AAAA,IACpC,QAAA,EAAU,SAAA,CAAU,GAAA,CAAI,CAAA,CAAE,EAAE,KAAK,EAAE,CAAA,EAAG,GAAA,EAAK,CAAA,EAAG,GAAA,EAAI;AAAA,IAClD,MAAA,EAAQ,cAAA,CAAe,CAAA,CAAE,IAAI,CAAA;AAAA,IAC7B,OAAO,CAAA,CAAE;AAAA,GACX,CAAE,CAAA;AAGF,EAAA,MAAM,MAAA,GAAqB,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IAChD,GAAG,CAAA;AAAA,IACH,cAAA,EAAgB,SAAA,CAAU,CAAA,CAAE,SAAA,EAAW,OAAO,UAAU;AAAA,GAC1D,CAAE,CAAA;AAGF,EAAA,MAAM,MAAA,GAAqB,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IAChD,IAAI,CAAA,CAAE,EAAA;AAAA,IACN,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,eAAA,EAAiB,SAAA,CAAU,CAAA,CAAE,SAAA,EAAW,OAAO,UAAU,CAAA;AAAA,IACzD,aAAA,EAAe,SAAA,CAAU,CAAA,CAAE,OAAA,EAAS,OAAO,UAAU,CAAA;AAAA,IACrD,OAAO,CAAA,CAAE;AAAA,GACX,CAAE,CAAA;AAGF,EAAA,MAAM,MAAA,GAAqB,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IAChD,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,SAAS,CAAA,CAAE,OAAA;AAAA,IACX,cAAA,EAAgB,SAAA,CAAU,CAAA,CAAE,SAAA,EAAW,OAAO,UAAU,CAAA;AAAA,IACxD,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,GAAI,CAAA,CAAE,KAAA,IAAS,EAAE,KAAA,EAAO,EAAE,KAAA,EAAM;AAAA,IAChC,GAAI,CAAA,CAAE,IAAA,IAAQ,EAAE,IAAA,EAAM,EAAE,IAAA;AAAK,GAC/B,CAAE,CAAA;AAGF,EAAA,MAAM,UAAA,GAA6B,GAAA,CAAI,UAAA,CAAW,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IAC5D,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,cAAA,EAAgB,SAAA,CAAU,CAAA,CAAE,SAAA,EAAW,OAAO,UAAU,CAAA;AAAA,IACxD,OAAO,CAAA,CAAE;AAAA,GACX,CAAE,CAAA;AAGF,EAAA,MAAM,cAAA,GAAqC,GAAA,CAAI,cAAA,CAAe,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IACxE,cAAA,EAAgB,SAAA,CAAU,CAAA,CAAE,SAAA,EAAW,OAAO,UAAU,CAAA;AAAA,IACxD,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,OAAO,CAAA,CAAE;AAAA,GACX,CAAE,CAAA;AAKF,EAAA,MAAM,eAAA,GAAkB,UAAA,KAAe,GAAA,CAAI,SAAA,CAAU,UAAA;AACrD,EAAA,MAAM,kBAAA,GACJ,mBAAmB,GAAA,CAAI,kBAAA,GACnB,IAAI,kBAAA,CAAmB,GAAA,CAAI,CAAC,KAAA,MAAW;AAAA,IACrC,GAAG,KAAA;AAAA,IACH,cAAA,EAAgB,KAAA,CAAM,cAAA,IAAkB,GAAA,CAAI,UAAU,UAAA,GAAa,UAAA;AAAA,GACrE,CAAE,CAAA,GACD,GAAA,CAAI,kBAAA,IAAsB,EAAC;AAElC,EAAA,OAAO;AAAA,IACL,YAAA,EAAc,IAAI,YAAA,IAAgB,YAAA;AAAA,IAClC,MAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA,cAAA;AAAA,IACA,kBAAA;AAAA,IACA,SAAA,EAAW,EAAE,KAAA,EAAO,GAAA,EAAK,UAAA,EAAW;AAAA,IACpC,OAAO,GAAA,CAAI;AAAA,GACb;AACF;;;AC1JA,IAAM,QAAA,GAAW,CAAA;AAQV,SAAS,wBAAwB,MAAA,EAA4B;AAElE,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACtB,IAAA,IAAI,CAAA,CAAE,SAAS,UAAA,EAAY;AACzB,MAAA,KAAA,CAAM,IAAA,CAAK,EAAE,cAAc,CAAA;AAAA,IAC7B;AAAA,EACF;AACA,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,QAAA;AAE/B,EAAA,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,IAAI,CAAC,CAAA;AAK1B,EAAA,MAAM,QAAA,GAAW,IAAA;AACjB,EAAA,MAAM,GAAA,GAAM,IAAA;AAEZ,EAAA,MAAM,OAAiB,EAAC;AACxB,EAAA,IAAI,OAAA,GAAU,CAAA;AAEd,EAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACrB,IAAA,IAAI,MAAA,GAAS,KAAA;AACb,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,IAAA,CAAK,QAAQ,CAAA,EAAA,EAAK;AACpC,MAAA,IAAI,IAAA,CAAK,CAAC,CAAA,IAAK,CAAA,EAAG;AAChB,QAAA,IAAA,CAAK,CAAC,CAAA,GAAI,CAAA,GAAI,QAAA,GAAW,GAAA;AACzB,QAAA,MAAA,GAAS,IAAA;AACT,QAAA;AAAA,MACF;AAAA,IACF;AACA,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,IAAA,CAAK,IAAA,CAAK,CAAA,GAAI,QAAA,GAAW,GAAG,CAAA;AAAA,IAC9B;AACA,IAAA,IAAI,IAAA,CAAK,SAAS,OAAA,EAAS;AACzB,MAAA,OAAA,GAAU,IAAA,CAAK,MAAA;AAAA,IACjB;AAAA,EACF;AAEA,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,QAAA,EAAU,OAAO,CAAA;AACnC;AAQO,SAAS,kBAAkB,MAAA,EAA4B;AAE5D,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAoB;AACxC,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAoB;AAEvC,EAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACtB,IAAA,MAAM,QAAA,GAAW,EAAE,QAAA,CAAS,QAAA;AAC5B,IAAA,IAAI,aAAa,MAAA,EAAW;AAE5B,IAAA,IAAI,CAAA,CAAE,SAAS,YAAA,EAAc;AAC3B,MAAA,OAAA,CAAQ,GAAA,CAAI,QAAA,EAAU,CAAA,CAAE,cAAc,CAAA;AAAA,IACxC,CAAA,MAAA,IAAW,CAAA,CAAE,IAAA,KAAS,WAAA,EAAa;AACjC,MAAA,MAAA,CAAO,GAAA,CAAI,QAAA,EAAU,CAAA,CAAE,cAAc,CAAA;AAAA,IACvC;AAAA,EACF;AAEA,EAAA,IAAI,OAAA,CAAQ,IAAA,KAAS,CAAA,EAAG,OAAO,QAAA;AAG/B,EAAA,MAAM,YAA+C,EAAC;AACtD,EAAA,KAAA,MAAW,CAAC,QAAA,EAAU,QAAQ,CAAA,IAAK,OAAA,EAAS;AAC1C,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,GAAA,CAAI,QAAQ,CAAA,IAAK,CAAA;AAC1C,IAAA,SAAA,CAAU,KAAK,EAAE,IAAA,EAAM,QAAA,EAAU,KAAA,EAAO,WAAW,CAAA;AAAA,EACrD;AACA,EAAA,SAAA,CAAU,KAAK,CAAC,CAAA,EAAG,MAAM,CAAA,CAAE,IAAA,GAAO,EAAE,IAAI,CAAA;AAGxC,EAAA,MAAM,OAAiB,EAAC;AACxB,EAAA,IAAI,OAAA,GAAU,CAAA;AAEd,EAAA,KAAA,MAAW,MAAM,SAAA,EAAW;AAC1B,IAAA,IAAI,MAAA,GAAS,KAAA;AACb,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,IAAA,CAAK,QAAQ,CAAA,EAAA,EAAK;AACpC,MAAA,IAAI,IAAA,CAAK,CAAC,CAAA,IAAK,EAAA,CAAG,IAAA,EAAM;AACtB,QAAA,IAAA,CAAK,CAAC,CAAA,GAAI,EAAA,CAAG,KAAA,GAAQ,IAAA;AACrB,QAAA,MAAA,GAAS,IAAA;AACT,QAAA;AAAA,MACF;AAAA,IACF;AACA,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,IAAA,CAAK,IAAA,CAAK,EAAA,CAAG,KAAA,GAAQ,IAAK,CAAA;AAAA,IAC5B;AACA,IAAA,IAAI,IAAA,CAAK,SAAS,OAAA,EAAS;AACzB,MAAA,OAAA,GAAU,IAAA,CAAK,MAAA;AAAA,IACjB;AAAA,EACF;AAEA,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,QAAA,EAAU,OAAO,CAAA;AACnC","file":"chunk-DZYFC24M.cjs","sourcesContent":["/**\n * Row packing for artifact and PR bars — greedy first-fit algorithm.\n *\n * Pure functions operating in normalized time [0,1]. No world coordinates,\n * no Layout dependency. The engine computes row assignments; the renderer\n * maps row index → Y position using layout.artifactBarHeight / layout.prBarHeight.\n *\n * Algorithm: for each bar (sorted by start time), find the first row where\n * the bar doesn't overlap the last bar in that row. If no row fits, add a new one.\n * This is the same greedy algorithm previously in ArtifactBand.tsx / PRBand.tsx,\n * but operating in normalized time instead of world X coordinates.\n */\n\nimport type { ArtifactBar, PRBar } from '../types/frame-state.js';\n\n/**\n * Minimum bar width in normalized time — prevents zero-width bars from\n * stacking infinitely in a single row. Matches the world-space MIN_BAR_WIDTH\n * (0.02) relative to typical timeline width (~8.8 world units) ≈ 0.0023.\n * Using 0.003 as conservative estimate (same as band-sizing.ts).\n */\nconst MIN_BAR_WIDTH_NORM = 0.003;\n\n/**\n * Gap between bars in the same row (normalized time).\n * Prevents bars from visually touching. Small enough to not waste space.\n */\nconst ROW_GAP_NORM = 0.001;\n\n/**\n * Maximum bar width in normalized time — prevents rogue geometry from bad data.\n * A PR spanning the entire timeline would be 1.0; cap at 0.95 for sanity.\n */\nconst MAX_BAR_WIDTH_NORM = 0.95;\n\n/**\n * Assign row indices to artifact bars using greedy first-fit packing.\n *\n * Artifacts are point events with a small fixed width. Each bar gets a\n * `row` field (0 = top row) based on temporal overlap with other bars.\n *\n * Bars that would extend past `progress` (the playhead) are clamped.\n *\n * @param bars - artifact bars from computeArtifactBars() (already filtered to progress)\n * @param progress - current playhead position in normalized time [0,1]\n * @param minGapNorm - minimum gap between bars in normalized time (default: ROW_GAP_NORM).\n * Pass a value derived from MIN_GAP_PX / (ppu * timeWidth) to ensure visible separation.\n * @param minBarWidthNorm - minimum bar width in normalized time (from layout.minBarWidth / timeWidth).\n * The renderer expands short bars to this width — packing must account for it\n * or expanded bars will eat the gap between neighbors.\n * @returns the same bars with `row` field populated (mutates in place for perf)\n */\nexport function packArtifactRows(\n bars: ArtifactBar[],\n progress: number,\n minGapNorm?: number,\n minBarWidthNorm?: number,\n): ArtifactBar[] {\n const gap = Math.max(ROW_GAP_NORM, minGapNorm ?? ROW_GAP_NORM);\n const effectiveBarWidth = Math.max(MIN_BAR_WIDTH_NORM, minBarWidthNorm ?? MIN_BAR_WIDTH_NORM);\n // Row end positions — tracks the right edge of the last bar in each row\n const rowEnds: number[] = [];\n\n for (const bar of bars) {\n const start = bar.normalizedTime;\n if (start < 0 || start > 1) {\n bar.row = 0;\n continue;\n }\n\n // Use the rendered width (max of actual and minimum) for packing\n const rawEnd = start + effectiveBarWidth;\n const end = Math.min(rawEnd, progress);\n\n // Find first row where this bar fits (no overlap)\n let row = 0;\n for (row = 0; row < rowEnds.length; row++) {\n if (rowEnds[row] <= start) break;\n }\n\n if (row >= rowEnds.length) {\n rowEnds.push(-1);\n }\n rowEnds[row] = end + gap;\n bar.row = row;\n }\n\n return bars;\n}\n\n/**\n * Assign row indices to PR bars using greedy first-fit packing.\n *\n * PRs are duration bars (opened → merged). Width is the time span between\n * open and merge (or open to current progress if still open).\n *\n * @param bars - PR bars from computePRBars() (already filtered to progress)\n * @param progress - current playhead position in normalized time [0,1]\n * @param minGapNorm - minimum gap between bars in normalized time (default: ROW_GAP_NORM).\n * Pass a value derived from MIN_GAP_PX / (ppu * timeWidth) to ensure visible separation.\n * @param minBarWidthNorm - minimum bar width in normalized time (from layout.minBarWidth / timeWidth).\n * The renderer expands short bars to this width — packing must account for it\n * or expanded bars will eat the gap between neighbors.\n * @returns the same bars with `row` field populated (mutates in place for perf)\n */\nexport function packPRRows(\n bars: PRBar[],\n progress: number,\n minGapNorm?: number,\n minBarWidthNorm?: number,\n): PRBar[] {\n const gap = Math.max(ROW_GAP_NORM, minGapNorm ?? ROW_GAP_NORM);\n const minWidth = Math.max(MIN_BAR_WIDTH_NORM, minBarWidthNorm ?? MIN_BAR_WIDTH_NORM);\n // Sort by open time for greedy packing — sort in place since engine\n // produces bars in event order (not necessarily open-time order)\n bars.sort((a, b) => a.openedNormalized - b.openedNormalized);\n\n // Row end positions\n const rowEnds: number[] = [];\n\n for (const bar of bars) {\n const start = bar.openedNormalized;\n if (start < 0 || start > 1) {\n bar.row = 0;\n continue;\n }\n\n // End is merge time, or current progress if still open.\n // Use max of actual duration and minBarWidthNorm — the renderer expands\n // short bars to minBarWidth, so packing must reserve that space.\n const rawEnd = bar.mergedNormalized ?? progress;\n const width = Math.min(MAX_BAR_WIDTH_NORM, Math.max(minWidth, rawEnd - start));\n const end = Math.min(start + width, progress);\n\n // Find first row where this bar fits\n let row = 0;\n for (row = 0; row < rowEnds.length; row++) {\n if (rowEnds[row] <= start) break;\n }\n\n if (row >= rowEnds.length) {\n rowEnds.push(-1);\n }\n rowEnds[row] = end + gap;\n bar.row = row;\n }\n\n return bars;\n}\n","/**\n * State Engine — getFrameState(progress, vizData) → FrameState\n *\n * Pure function. No side effects. Deterministic.\n * Given the same progress + vizData, always returns the same FrameState.\n * Scrubbable by definition — no accumulated state.\n */\n\nimport {\n AGENT_SHADER_NODE_RADIUS,\n PILL_GAP_BASE,\n PILL_GAP_RATIO,\n SIM_HOURS_PER_SECOND,\n} from '../shared-constants.js';\nimport type {\n AgentState,\n AllPhaseState,\n ArtifactBar,\n CommitDot,\n EdgeState,\n EditorialNarrationState,\n FrameState,\n LayoutContext,\n MilestoneState,\n PRBar,\n ParticleState,\n PhaseState,\n QuotePillState,\n} from '../types/frame-state.js';\nimport type {\n AgentLayout,\n EditorialNarrationEntry,\n VizData,\n VizEvent,\n VizPhase,\n VizQuote,\n VizTrustKeyframe,\n} from '../types/viz-data.js';\nimport { packArtifactRows, packPRRows } from './pack-rows.js';\n\n// --- Configuration ---\n\n/** How long (in normalized time) an agent takes to fade in */\nconst AGENT_FADE_DURATION = 0.01;\n\n/**\n * Duration (in normalized time) for the 3-phase join bounce.\n * 0.015 ≈ 1.5% of timeline. See computeBounceScale() for phase breakdown.\n */\nconst BOUNCE_DURATION = 0.015;\n\n/** Minimum real seconds a particle should be visible.\n * Inflated 2× to account for spatial exclusion near sender/receiver\n * (particles within agent radius are hidden, eating ~30-40% of travel time\n * depending on agent spacing and activity boost). */\nconst MIN_PARTICLE_SECONDS = 3.0;\n\n/** Time window (normalized) for activity pulse radius boost */\nconst ACTIVITY_WINDOW = 0.01;\n\n/** Maximum radius multiplier from activity */\nconst ACTIVITY_RADIUS_BOOST = 1.5;\n\n/**\n * Work intensity sliding window (normalized time).\n * ~4.2% of timeline. Events within this window contribute to workIntensity\n * with exponential decay.\n */\nconst WORK_WINDOW = 0.042;\n\n/**\n * Work decay rate per normalized time unit.\n * half-life = ln(2)/50 ≈ 0.014 normalized ≈ 1% of timeline.\n */\nconst WORK_DECAY_RATE = 50;\n\n/**\n * Work event weights — how much each event type contributes to workIntensity.\n */\nconst WORK_WEIGHTS: Record<string, number> = {\n commit: 3,\n pr_created: 5,\n pr_merged: 5,\n message: 1,\n artifact: 2,\n};\n\n/**\n * Work intensity normalization threshold.\n * An agent with 25+ weighted work units in the window is at full intensity.\n */\nconst WORK_INTENSITY_MAX = 25;\n\n/** Minimum real seconds a pill should be on screen */\nconst MIN_PILL_SECONDS = 5;\n/** Dense cluster window is this fraction of the standard window */\nconst DENSE_WINDOW_RATIO = 0.625; // 0.025/0.04 from original tuning\n/** Cluster detection: next same-agent quote within 2× window = dense */\nconst CLUSTER_THRESHOLD_RATIO = 2.0;\n\n// --- Duration-scaled window computation ---\n\n/**\n * Convert real seconds to normalized time for a given timeline.\n * playbackSeconds = totalHours / SIM_HOURS_PER_SECOND\n * normalizedWindow = realSeconds / playbackSeconds\n */\nfunction secondsToNormalized(realSeconds: number, durationMs: number): number {\n const totalHours = durationMs / 3_600_000;\n const playbackSeconds = totalHours / SIM_HOURS_PER_SECOND;\n return realSeconds / playbackSeconds;\n}\n\n/** Compute duration-scaled time windows from timeline span.\n *\n * The key insight: all windows are normalized (0-1) but the human needs\n * real seconds on screen. We directly compute the normalized window that\n * gives exactly the target real seconds, with floor and cap for safety.\n *\n * secondsToNormalized(S, D) = S / (D_hours / SIM_HOURS_PER_SECOND)\n *\n * For short timelines (10h): playback = 16.7s, so 5s pill = 0.30 normalized\n * For long timelines (500h): playback = 833s, so 5s pill = 0.006 normalized\n */\nfunction computeTimeWindows(durationMs: number): {\n narrationWindow: number;\n narrationWindowDense: number;\n narrationClusterThreshold: number;\n particleWindow: number;\n} {\n // Pill: target MIN_PILL_SECONDS of real screen time, cap at MAX_PILL_SECONDS\n const narrationWindow = secondsToNormalized(MIN_PILL_SECONDS, durationMs);\n const narrationWindowDense = narrationWindow * DENSE_WINDOW_RATIO;\n const narrationClusterThreshold = narrationWindow * CLUSTER_THRESHOLD_RATIO;\n\n // Particle: target MIN_PARTICLE_SECONDS of real screen time\n const particleWindow = secondsToNormalized(MIN_PARTICLE_SECONDS, durationMs);\n\n return { narrationWindow, narrationWindowDense, narrationClusterThreshold, particleWindow };\n}\n\n/** Narration fade-in as fraction of window (5% = quick appear). */\nconst NARRATION_FADE_IN = 0.05;\n\n/** Narration fade-out as fraction of window (8% = gradual exit).\n * Asymmetric: fade-out is ~1.6× longer than fade-in for graceful exit.\n */\nconst NARRATION_FADE_OUT = 0.08;\n\n// --- Editorial Narration ---\n\n/** Fade duration (normalized time) for editorial narration transitions.\n * Set to 0 for instant swap — no crossfade between entries. */\nconst EDITORIAL_FADE = 0;\n\n/** Edge decay rate — exponential decay per normalized time unit.\n * half-life = ln(2)/5 ≈ 0.14 normalized (≈14% of timeline).\n * A single message fades to ~1% strength after age 0.92 normalized. */\nconst EDGE_DECAY_RATE = 5.0;\n\n/** Raw accumulated strength at which an edge reaches full width/alpha.\n * With EDGE_DECAY_RATE=5.0, a single undecayed message has raw≈1.0.\n * 10 recent messages accumulate to raw≈5-8. So EDGE_SATURATION=8.0 means:\n * 1 message = 0.125 strength (thin + dim), 8+ messages = 1.0 (full width).\n * Prevents per-frame max normalization from showing the first message at\n * full width (raw/maxRaw = 1.0 when it's the only edge). */\nconst EDGE_SATURATION = 8.0;\n\n/** Minimum edge strength to include (below this, prune) */\nconst EDGE_MIN_STRENGTH = 0.01;\n\n// --- Agent Visual Sizing (moved from AgentGlowInstances.tsx + AgentNode.tsx) ---\n\n/** Reference radius used by the GLSL shader. coreScale is computed relative to this. */\n// NODE_RADIUS_REF → imported as AGENT_SHADER_NODE_RADIUS from shared-constants\nconst NODE_RADIUS_REF = AGENT_SHADER_NODE_RADIUS;\n\n/** Human agents are 1.5× base size */\nconst HUMAN_SIZE_MULTIPLIER = 1.5;\n\n/** Work-based growth: at peak totalWork, node is 2× base */\nconst MAX_WORK_GROWTH = 1.0;\n\n/** Instantaneous activity pulse: at peak workIntensity, node pulses 30% bigger */\nconst ACTIVITY_PULSE = 0.3;\n\n/** Gap between core circle and label, as fraction of nodeRadius */\nconst LABEL_GAP_RATIO = 0.65;\n\n// --- Agent Visibility ---\n\n/**\n * Find the earliest event involving this agent — either as actor or target.\n * This determines when the agent first appears in the visualization.\n *\n * Uses any event type (agent_join, message, commit, pr_created, pr_merged,\n * artifact, milestone). The agent appears when they first DO something or\n * have something done TO them.\n *\n * Events are sorted by normalizedTime, so the first match is the earliest.\n */\nfunction getAgentAppearTime(agentId: string, events: VizEvent[]): number | null {\n for (const e of events) {\n if (e.agent === agentId || e.targetAgent === agentId) {\n return e.normalizedTime;\n }\n }\n return null;\n}\n\n/**\n * 3-phase bounce scale for agent join animation.\n *\n * Phase 1 (0-33%): 0 → 1.2 — ease-out cubic (fast start, decelerates)\n * Phase 2 (33-66%): 1.2 → 0.95 — linear settle (overshoot correction)\n * Phase 3 (66-100%): 0.95 → 1.0 — linear ease back (final rest)\n * After: 1.0\n *\n * @param timeSinceJoin - normalized time elapsed since agent joined\n * @returns scale factor (0 to ~1.2, settling at 1.0)\n */\nfunction computeBounceScale(timeSinceJoin: number): number {\n if (timeSinceJoin < 0) return 0;\n\n const t = timeSinceJoin / BOUNCE_DURATION;\n if (t >= 1) return 1.0;\n\n if (t < 1 / 3) {\n // Phase 1: 0 → 1.2, ease-out cubic\n const p = t * 3; // 0→1 within phase\n return 1.2 * (1 - (1 - p) ** 3);\n }\n if (t < 2 / 3) {\n // Phase 2: 1.2 → 0.95, linear settle\n const p = (t - 1 / 3) * 3;\n return 1.2 - 0.25 * p;\n }\n // Phase 3: 0.95 → 1.0, linear ease back\n const p = (t - 2 / 3) * 3;\n return 0.95 + 0.05 * p;\n}\n\n/**\n * Compute work intensity for an agent at a given progress.\n * Sliding window with exponential decay.\n * Pure function of (agentId, progress, events).\n */\nfunction computeWorkIntensity(agentId: string, progress: number, events: VizEvent[]): number {\n const windowStart = progress - WORK_WINDOW;\n let totalWork = 0;\n\n for (const event of events) {\n if (event.normalizedTime > progress) break; // events are sorted\n if (event.normalizedTime < windowStart) continue;\n if (event.agent !== agentId) continue;\n\n const weight = WORK_WEIGHTS[event.type];\n if (weight === undefined) continue;\n\n const age = progress - event.normalizedTime;\n const decayed = weight * Math.exp(-WORK_DECAY_RATE * age);\n totalWork += decayed;\n }\n\n // Also count events where this agent is the target (messages received)\n for (const event of events) {\n if (event.normalizedTime > progress) break;\n if (event.normalizedTime < windowStart) continue;\n if (event.targetAgent !== agentId) continue;\n\n const weight = WORK_WEIGHTS[event.type];\n if (weight === undefined) continue;\n\n const age = progress - event.normalizedTime;\n const decayed = weight * Math.exp(-WORK_DECAY_RATE * age);\n totalWork += decayed;\n }\n\n return Math.min(1, totalWork / WORK_INTENSITY_MAX);\n}\n\n/**\n * Event types that count toward cumulative work volume.\n * Commits and PRs (created + merged) represent shipped work.\n * Messages are excluded — they're communication, not output.\n */\nconst TOTAL_WORK_EVENT_TYPES = new Set(['commit', 'pr_created', 'pr_merged']);\n\n/**\n * Compute cumulative work volume for an agent up to a given progress.\n * Simple count of commits + PRs — no decay, no weighting.\n * This only grows over time, unlike workIntensity which is instantaneous.\n *\n * Pure function of (agentId, progress, events).\n *\n * @param agentId - agent to count work for\n * @param progress - current normalized progress (0-1)\n * @param events - sorted VizEvent array\n * @returns count of work events (commits + PRs created + PRs merged)\n */\nexport function computeTotalWork(agentId: string, progress: number, events: VizEvent[]): number {\n let count = 0;\n for (const event of events) {\n if (event.normalizedTime > progress) break; // events are sorted\n if (event.agent !== agentId) continue;\n if (TOTAL_WORK_EVENT_TYPES.has(event.type)) {\n count++;\n }\n }\n return count;\n}\n\nfunction computeAgentState(\n agent: AgentLayout,\n progress: number,\n events: VizEvent[],\n maxTotalWork: number,\n layoutContext: LayoutContext | undefined,\n): AgentState | null {\n const appearTime = getAgentAppearTime(agent.id, events);\n\n // Agent has no events yet — not visible\n if (appearTime === null || progress < appearTime) {\n return null;\n }\n\n // Fade in over AGENT_FADE_DURATION\n const timeSinceJoin = progress - appearTime;\n const opacity = Math.min(1, timeSinceJoin / AGENT_FADE_DURATION);\n\n // Bounce scale on join\n const scale = computeBounceScale(timeSinceJoin);\n\n // Activity pulse: count recent events for this agent\n const recentEvents = events.filter(\n (e) =>\n e.agent === agent.id &&\n e.normalizedTime <= progress &&\n e.normalizedTime > progress - ACTIVITY_WINDOW,\n );\n const activityFactor = Math.min(1, recentEvents.length / 5);\n const radiusMultiplier = 1 + activityFactor * (ACTIVITY_RADIUS_BOOST - 1);\n\n // Work intensity: sliding window with decay\n const workIntensity = computeWorkIntensity(agent.id, progress, events);\n\n // Cumulative work volume: total commits + PRs shipped so far\n const totalWork = computeTotalWork(agent.id, progress, events);\n\n // --- Core scale (moved from AgentGlowInstances.tsx + AgentNode.tsx) ---\n // Folds in: human multiplier, work growth, activity pulse, bounce, layout scale.\n // The renderer reads this directly — zero math.\n const isHuman = agent.role === 'human';\n const humanMul = isHuman ? HUMAN_SIZE_MULTIPLIER : 1.0;\n const growthFactor = totalWork / maxTotalWork;\n const activityPulse = 1 + workIntensity * ACTIVITY_PULSE;\n const nodeRadius = layoutContext?.nodeRadius ?? NODE_RADIUS_REF;\n const layoutScale = nodeRadius / NODE_RADIUS_REF;\n const coreScale =\n humanMul * (1 + growthFactor * MAX_WORK_GROWTH) * activityPulse * scale * layoutScale;\n\n // --- Label offset (moved from AgentNode.tsx) ---\n // Label sits below the visual node edge with a proportional gap.\n // Gap scales with coreScale so it stays proportional to the node's visual size.\n const labelHeight = layoutContext?.labelHeight ?? nodeRadius * 1.6;\n const visualRadius = nodeRadius * coreScale;\n const labelGap = visualRadius * LABEL_GAP_RATIO;\n const labelOffsetY = -(visualRadius + labelGap + labelHeight / 2);\n\n return {\n id: agent.id,\n x: agent.position.x,\n y: agent.position.y,\n radius: agent.radius * radiusMultiplier,\n color: agent.color,\n opacity,\n glowIntensity: activityFactor,\n label: agent.label,\n labelOpacity: opacity,\n scale,\n workIntensity,\n totalWork,\n isHuman,\n role: agent.role,\n coreScale,\n labelOffsetY,\n };\n}\n\n// --- Particles (commits, PRs, artifacts) ---\n\n/** Particles fire for agent-to-agent messages.\n * Each particle travels from sender → receiver along the comm line. */\nconst PARTICLE_TYPES = new Set(['message']);\n\n/** Fraction of particle lifetime spent traveling (vs fading at destination).\n * 1.0 = 100% travel, particles disappear at receiver via depth occlusion. */\nconst PARTICLE_TRAVEL_FRACTION = 1.0;\n\nfunction computeParticles(\n agents: AgentLayout[],\n events: VizEvent[],\n progress: number,\n particleWindow: number,\n): ParticleState[] {\n const agentMap = new Map(agents.map((a) => [a.id, a]));\n const particles: ParticleState[] = [];\n\n for (const event of events) {\n // Early termination: events are sorted, skip future events\n if (event.normalizedTime > progress) break;\n if (!PARTICLE_TYPES.has(event.type)) continue;\n\n const age = progress - event.normalizedTime;\n if (age > particleWindow) continue;\n\n const sender = agentMap.get(event.agent);\n if (!sender) continue;\n\n // Message particles travel from sender → receiver\n const receiver = event.targetAgent ? agentMap.get(event.targetAgent) : null;\n if (!receiver) continue; // skip messages without a target\n\n const t = age / particleWindow; // 0→1 over lifetime\n const travel = Math.min(t / PARTICLE_TRAVEL_FRACTION, 1.0); // 0→1 position along path\n const fade =\n t <= PARTICLE_TRAVEL_FRACTION\n ? 1.0\n : 1.0 - (t - PARTICLE_TRAVEL_FRACTION) / (1 - PARTICLE_TRAVEL_FRACTION);\n\n const px = sender.position.x + (receiver.position.x - sender.position.x) * travel;\n const py = sender.position.y + (receiver.position.y - sender.position.y) * travel;\n\n // No engine-side spatial exclusion. The renderer handles occlusion:\n // - Agent core (depthWrite=true, z=0.25) occludes particles (depthTest=true, z=0.08-0.11)\n // - Agent glow (depthWrite=false) does NOT occlude particles\n // Previously, engine exclusion at a.radius made particles invisible between\n // close agents (distance < 2×radius = 0.3), which is most agent pairs.\n\n particles.push({\n id: `particle-${event.type}-${event.normalizedTime}-${event.agent}`,\n type: 'message' as const,\n x: px,\n y: py,\n opacity: fade,\n size: 4,\n color: sender.color,\n });\n }\n\n return particles;\n}\n\n// --- Narration ---\n\n/** Narration offset above agent — clears the node at any sizing */\n// NARRATION_ABOVE_RATIO/BASE → imported as PILL_GAP_RATIO/BASE from shared-constants\nconst NARRATION_ABOVE_RATIO = PILL_GAP_RATIO;\nconst NARRATION_ABOVE_BASE = PILL_GAP_BASE;\n\n/** Vertical spacing between stacked pills (multiple of nodeRadius) */\n\n/** Target font size in screen pixels for quote text.\n * Pills are screen-space UI — fixed pixel size, NOT scaled by textScale.\n * textScale is for world-space text (agent labels) that need to grow at low PPU. */\nconst PILL_QUOTE_FONT_PX = 16;\n/** Speaker name font is slightly larger */\nconst PILL_SPEAKER_FONT_RATIO = 1.2;\n/** Maximum pill width as fraction of viewport */\nconst PILL_MAX_VIEWPORT_FRACTION = 0.75;\n/** Minimum pill height as multiple of nodeRadius */\nconst PILL_MIN_HEIGHT_RATIO = 2.0;\n/** Darken factor for pill background */\nconst PILL_BG_DARKEN = 0.3;\n/** Minimum RGB channel value for pill background (prevents invisible pills on black) */\nconst PILL_BG_MIN_BRIGHTNESS = 25;\n/** Approximate character width as fraction of font size (Inter font average) */\nconst CHAR_WIDTH_RATIO = 0.75;\n/** Line height as multiple of font size */\nconst LINE_HEIGHT_RATIO = 1.4;\n\n/**\n * Darken a hex color for pill background, with minimum brightness floor.\n * Pure function — no DOM dependencies.\n */\nexport function darkenHexForPill(hex: string, factor: number): string {\n const h = hex.replace('#', '');\n const r = Math.max(\n PILL_BG_MIN_BRIGHTNESS,\n Math.round(Number.parseInt(h.slice(0, 2), 16) * factor),\n );\n const g = Math.max(\n PILL_BG_MIN_BRIGHTNESS,\n Math.round(Number.parseInt(h.slice(2, 4), 16) * factor),\n );\n const b = Math.max(\n PILL_BG_MIN_BRIGHTNESS,\n Math.round(Number.parseInt(h.slice(4, 6), 16) * factor),\n );\n return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;\n}\n\n/**\n * Compute pill content — text wrapping + styling.\n *\n * Engine provides: text content (wrapped lines), padding, styling, and\n * estimated dimensions for Y positioning. Renderer owns ALL actual sizing\n * via ctx.measureText() — text drives the size, not the other way around.\n *\n * Text wrapping uses CHAR_WIDTH_RATIO heuristic. The renderer may re-wrap\n * if actual font metrics differ, but in practice the heuristic is close enough.\n */\nexport function computePillLayout(\n speakerLabel: string,\n text: string,\n speakerColor: string,\n layoutContext: LayoutContext | undefined,\n): {\n /** Text wrapped into lines (word-wrap heuristic). */\n lines: string[];\n /** Horizontal padding in world units. Renderer: canvasWidth = measuredText + padX * 2 */\n padX: number;\n /** Vertical padding in world units. Renderer: canvasHeight = measuredTextHeight + padY * 2 */\n padY: number;\n /** Max pill width in world units — ceiling for X clamping. NOT for canvas/geometry sizing. */\n maxPillWidth: number;\n /** Estimated actual pill width in world units — for X clamping. Single-line: content + padding. Multi-line: maxPillWidth. */\n estimatedWidth: number;\n /** Estimated pill height in world units — for Y positioning ONLY. NOT for canvas/geometry. */\n estimatedHeight: number;\n speakerLabel: string;\n speakerColor: string;\n pillBgColor: string;\n} {\n const nodeRadius = layoutContext?.nodeRadius ?? NODE_RADIUS_REF;\n const ppu = layoutContext ? layoutContext.viewportPxWidth / layoutContext.worldWidth : 128;\n const padX = layoutContext?.narrationPadX ?? 0.25;\n const padY = layoutContext?.narrationPadY ?? 0.14;\n const maxWidth = layoutContext?.narrationMaxWidth ?? 4.0;\n\n // Font size in screen pixels — used for word-wrap heuristic only.\n // Renderer owns the actual font size constant.\n const fontSize = PILL_QUOTE_FONT_PX;\n const speakerFontSize = Math.round(fontSize * PILL_SPEAKER_FONT_RATIO);\n\n // Convert font size to world units for layout math\n const fontWorldUnits = fontSize / ppu;\n\n // Max pill width: min of layout max and viewport fraction\n const viewportMaxWorld = layoutContext\n ? (layoutContext.viewportPxWidth * PILL_MAX_VIEWPORT_FRACTION) / ppu\n : maxWidth;\n const maxPillWidth = Math.min(maxWidth, viewportMaxWorld);\n\n // Available text width inside pill (world units)\n const textAreaWidth = maxPillWidth - padX * 2;\n\n // Approximate text width using character count × average char width\n // Speaker prefix: \"speaker \" (with 2 spaces) — skip if no label\n const speakerPrefix = speakerLabel ? `${speakerLabel} ` : '';\n const speakerWidthWorld = speakerPrefix.length * (speakerFontSize / ppu) * CHAR_WIDTH_RATIO;\n const fullText = text;\n const fullTextWidthWorld = fullText.length * fontWorldUnits * CHAR_WIDTH_RATIO;\n const totalTextWidth = speakerWidthWorld + fullTextWidthWorld;\n\n // Wrap text into lines if needed.\n // CHAR_WIDTH_RATIO is used for line-break heuristic only — renderer measures\n // actual text with ctx.measureText() and sizes canvas/plane from measurements.\n let lines: string[];\n const charWidthWorld = fontWorldUnits * CHAR_WIDTH_RATIO;\n\n if (totalTextWidth <= textAreaWidth) {\n // Single line — speaker + text fits\n lines = [fullText];\n } else {\n // Multi-line: first line has speaker prefix eating into available width\n const firstLineAvail = textAreaWidth - speakerWidthWorld;\n const charsPerLine = Math.max(10, Math.floor(textAreaWidth / charWidthWorld));\n const charsFirstLine = Math.max(5, Math.floor(firstLineAvail / charWidthWorld));\n\n lines = [];\n const words = fullText.split(' ');\n let currentLine = '';\n let currentChars = 0;\n\n for (const word of words) {\n const limit = lines.length === 0 ? charsFirstLine : charsPerLine;\n if (currentLine && currentChars + 1 + word.length > limit) {\n lines.push(currentLine);\n currentLine = word;\n currentChars = word.length;\n } else {\n currentLine = currentLine ? `${currentLine} ${word}` : word;\n currentChars = currentLine.length;\n }\n }\n if (currentLine) lines.push(currentLine);\n\n // Cap at 3 lines max — truncate with ellipsis\n if (lines.length > 3) {\n lines = lines.slice(0, 3);\n lines[2] = `${lines[2].slice(0, -3)}...`;\n }\n }\n\n // Estimated pill height — for Y positioning ONLY (coreRadius + gap + height/2).\n // Renderer computes actual height from measured text metrics.\n const lineHeightWorld = fontWorldUnits * LINE_HEIGHT_RATIO;\n const textHeight = lineHeightWorld * lines.length;\n const computedHeight = textHeight + padY * 2;\n const minHeight = nodeRadius * PILL_MIN_HEIGHT_RATIO;\n const estimatedHeight = Math.max(computedHeight, minHeight);\n\n // Estimated actual pill width — for X clamping.\n // Single-line: content width + padding. Multi-line: fills maxPillWidth.\n const estimatedWidth =\n lines.length === 1 ? Math.min(totalTextWidth + padX * 2, maxPillWidth) : maxPillWidth;\n\n const pillBgColor = darkenHexForPill(speakerColor, PILL_BG_DARKEN);\n\n return {\n lines,\n padX,\n padY,\n maxPillWidth,\n estimatedWidth,\n estimatedHeight,\n speakerLabel,\n speakerColor,\n pillBgColor,\n };\n}\n\n/** Compute normalized position + world-space Y offset for a narration pill.\n * Returns speaker's normalized coords so the renderer can call layout.agentToWorld()\n * directly — same code path as AgentNode. Eliminates divergence between\n * App.tsx's layoutContext.agentToWorld and renderer's layout.agentToWorld. */\nfunction computeQuotePillPosition(\n quote: VizQuote,\n visibleAgents: AgentState[],\n agents: AgentLayout[],\n layoutContext: LayoutContext | undefined,\n estimatedHeight: number,\n estimatedWidth: number,\n): { speakerNormX: number; speakerNormY: number; yOffset: number; xOffset: number } {\n const nodeRadius = layoutContext?.nodeRadius ?? NODE_RADIUS_REF;\n // glowRadiusRatio intentionally NOT used — pill positioning uses core radius only\n const gap = nodeRadius * NARRATION_ABOVE_RATIO + NARRATION_ABOVE_BASE;\n\n const speaker = visibleAgents.find((a) => a.id === quote.speaker);\n const speakerAgent = agents.find((a) => a.id === quote.speaker);\n\n if (speaker && speakerAgent) {\n // Compute speaker's solid core radius (pill can overlap glow ring per @snorre)\n const speakerCoreScale = speaker.coreScale ?? 1.0;\n const coreRadius = nodeRadius * speakerCoreScale;\n\n // Y offset in world units: core clearance + gap + half estimated pill height\n let yOffset = coreRadius + gap + estimatedHeight / 2;\n\n // Clamp yOffset so pill top edge stays within agents band.\n // If pill would clip at top, push it down (overlapping agent is fine per @snorre).\n if (layoutContext?.agentToWorld && layoutContext.agentsBandTop !== undefined) {\n const agentWorldY = layoutContext.agentToWorld(\n speakerAgent.position.x,\n speakerAgent.position.y,\n ).y;\n const pillTop = agentWorldY + yOffset + estimatedHeight / 2;\n const margin = 0.02;\n const maxPillTop = layoutContext.agentsBandTop - margin;\n if (pillTop > maxPillTop) {\n yOffset -= pillTop - maxPillTop;\n }\n }\n\n // X offset: clamp pill edges within world bounds.\n // Pill is centered on agent's world X — shift if edges extend beyond viewport.\n // Use slightly wider estimate (1.1×) because the renderer may expand the pill\n // geometry when canvas measureText() returns wider than engine's char-width estimate.\n let xOffset = 0;\n if (layoutContext?.agentToWorld) {\n const agentWorldX = layoutContext.agentToWorld(\n speakerAgent.position.x,\n speakerAgent.position.y,\n ).x;\n const margin = 0.02;\n const minLeft = layoutContext.worldLeft + margin;\n const maxRight = layoutContext.worldRight - margin;\n const clampWidth = estimatedWidth * 1.1; // 10% buffer for renderer canvas expansion\n const pillLeft = agentWorldX - clampWidth / 2;\n const pillRight = agentWorldX + clampWidth / 2;\n\n if (pillLeft < minLeft) {\n // Pill extends past left edge — shift right\n xOffset = minLeft - pillLeft;\n } else if (pillRight > maxRight) {\n // Pill extends past right edge — shift left\n xOffset = maxRight - pillRight;\n }\n }\n\n return {\n speakerNormX: speakerAgent.position.x,\n speakerNormY: speakerAgent.position.y,\n yOffset,\n xOffset,\n };\n }\n\n // Speaker not visible — position at center, will be hidden by opacity\n return { speakerNormX: 0.5, speakerNormY: 0.5, yOffset: 0, xOffset: 0 };\n}\n\n/**\n * Pre-compute effective start time and window duration for each quote.\n * Same-agent quotes that would overlap get shifted forward (queued).\n * Dense clusters get shorter windows so queued quotes cycle faster.\n *\n * Returns a Map from quote index → { effectiveStart, window }.\n * Pure function — no side effects, fully deterministic.\n */\nfunction computePillSchedule(\n quotes: VizQuote[],\n narrationWindow: number,\n narrationWindowDense: number,\n narrationClusterThreshold: number,\n): Map<number, { effectiveStart: number; window: number }> {\n const schedule = new Map<number, { effectiveStart: number; window: number }>();\n\n // Group quotes by speaker, preserving original indices\n const bySpeaker = new Map<string, Array<{ idx: number; quote: VizQuote }>>();\n for (let i = 0; i < quotes.length; i++) {\n const q = quotes[i];\n let group = bySpeaker.get(q.speaker);\n if (!group) {\n group = [];\n bySpeaker.set(q.speaker, group);\n }\n group.push({ idx: i, quote: q });\n }\n\n for (const group of bySpeaker.values()) {\n // Sort by normalizedTime within each agent's quotes\n group.sort((a, b) => a.quote.normalizedTime - b.quote.normalizedTime);\n\n let lastEnd = -1; // End time of previous pill for this agent\n\n for (let gi = 0; gi < group.length; gi++) {\n const { idx, quote } = group[gi];\n const naturalStart = quote.normalizedTime;\n\n // Determine if this quote is in a dense cluster:\n // next same-agent quote is within cluster threshold\n const nextQuote = gi + 1 < group.length ? group[gi + 1].quote : null;\n const isDense =\n nextQuote !== null && nextQuote.normalizedTime - naturalStart < narrationClusterThreshold;\n\n const window = isDense ? narrationWindowDense : narrationWindow;\n\n // Effective start: natural time, or after previous pill ends (whichever is later).\n // Cap so the pill fits before timeline end (progress=1.0).\n // Use a minimum window of 50% to keep the pill readable even when compressed.\n let effectiveStart = Math.max(naturalStart, lastEnd);\n const minWindow = window * 0.5;\n if (effectiveStart + minWindow > 1.0) {\n // Can't fit even a compressed pill — skip it\n effectiveStart = Math.min(effectiveStart, 1.0 - minWindow);\n }\n const effectiveWindow = Math.min(window, 1.0 - effectiveStart);\n\n schedule.set(idx, { effectiveStart, window: Math.max(effectiveWindow, minWindow) });\n lastEnd = effectiveStart + Math.max(effectiveWindow, minWindow);\n }\n }\n\n return schedule;\n}\n\nfunction computeQuotePills(\n quotes: VizQuote[],\n progress: number,\n agents: AgentLayout[],\n visibleAgents: AgentState[],\n layoutContext: LayoutContext | undefined,\n narrationWindow: number,\n narrationWindowDense: number,\n narrationClusterThreshold: number,\n): QuotePillState[] {\n const result: QuotePillState[] = [];\n const schedule = computePillSchedule(\n quotes,\n narrationWindow,\n narrationWindowDense,\n narrationClusterThreshold,\n );\n\n for (let i = 0; i < quotes.length; i++) {\n const quote = quotes[i];\n const timing = schedule.get(i);\n if (!timing) continue;\n\n const { effectiveStart, window } = timing;\n const end = effectiveStart + window;\n\n if (progress < effectiveStart || progress > end) continue;\n\n const elapsed = progress - effectiveStart;\n const fadeIn = window * NARRATION_FADE_IN;\n const fadeOut = window * NARRATION_FADE_OUT;\n\n let opacity: number;\n if (elapsed < fadeIn) {\n opacity = elapsed / fadeIn;\n } else if (elapsed > window - fadeOut) {\n opacity = (window - elapsed) / fadeOut;\n } else {\n opacity = 1;\n }\n\n // Find speaker color from visible agents\n const speakerAgent = visibleAgents.find((a) => a.id === quote.speaker);\n const speakerColor = speakerAgent?.color ?? '#3b82f6';\n const speakerLabel = '';\n\n // Compute pill content — position needs estimated height\n const pill = computePillLayout(speakerLabel, quote.text, speakerColor, layoutContext);\n\n const { speakerNormX, speakerNormY, yOffset, xOffset } = computeQuotePillPosition(\n quote,\n visibleAgents,\n agents,\n layoutContext,\n pill.estimatedHeight,\n pill.estimatedWidth,\n );\n\n result.push({\n text: quote.text,\n speaker: quote.speaker,\n opacity: Math.max(0, Math.min(1, opacity)),\n speakerNormX,\n speakerNormY,\n yOffset,\n xOffset,\n lines: pill.lines,\n padX: pill.padX,\n padY: pill.padY,\n maxPillWidth: pill.maxPillWidth,\n estimatedWidth: pill.estimatedWidth,\n speakerLabel: pill.speakerLabel,\n speakerColor: pill.speakerColor,\n pillBgColor: pill.pillBgColor,\n ...(quote.emoji && { emoji: quote.emoji }),\n ...(quote.mood && { mood: quote.mood }),\n });\n }\n\n // One pill per speaking agent at a time — dedup ensures no stacking.\n // Multiple DIFFERENT agents can speak simultaneously, each gets their own pill.\n\n return result;\n}\n\n// --- Editorial Narration ---\n\n/** Find the active editorial narration entry at current progress.\n * Queue is pre-sorted by normalizedTime. Each entry is active from its\n * normalizedTime until the next entry's normalizedTime. Fade in/out at\n * transitions for smooth crossfade. */\nexport function computeEditorialNarration(\n queue: EditorialNarrationEntry[],\n progress: number,\n): EditorialNarrationState | null {\n if (queue.length === 0) return null;\n\n // Binary search: find the last entry where normalizedTime <= progress\n let lo = 0;\n let hi = queue.length - 1;\n let idx = -1;\n\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1;\n if (queue[mid].normalizedTime <= progress) {\n idx = mid;\n lo = mid + 1;\n } else {\n hi = mid - 1;\n }\n }\n\n // Before first entry — nothing to show\n if (idx < 0) return null;\n\n const entry = queue[idx];\n\n // Use normalizedDuration when present (from timing.json), otherwise infer from gap\n const gapDuration =\n (idx < queue.length - 1 ? queue[idx + 1].normalizedTime : 1) - entry.normalizedTime;\n const entryDuration = entry.normalizedDuration ?? gapDuration;\n\n // When using normalizedDuration, check if progress is past the entry's end\n const elapsed = progress - entry.normalizedTime;\n if (entry.normalizedDuration != null && elapsed > entryDuration) {\n return null;\n }\n const fadeIn = Math.min(EDITORIAL_FADE, entryDuration * 0.3);\n const fadeOut = Math.min(EDITORIAL_FADE, entryDuration * 0.3);\n\n let opacity: number;\n if (elapsed < fadeIn) {\n opacity = fadeIn > 0 ? elapsed / fadeIn : 1;\n } else if (elapsed > entryDuration - fadeOut) {\n opacity = fadeOut > 0 ? (entryDuration - elapsed) / fadeOut : 1;\n } else {\n opacity = 1;\n }\n\n return {\n phaseTitle: entry.phaseTitle,\n phaseColor: entry.phaseColor,\n text: entry.text,\n opacity: Math.max(0, Math.min(1, opacity)),\n // Pass through extended fields (undefined when absent — backward compat)\n speaker: entry.speaker,\n speakerColor: entry.speakerColor,\n style: entry.style,\n };\n}\n\n// --- Phase ---\n\nfunction computePhase(phases: VizPhase[], progress: number): PhaseState {\n // Find the active phase\n for (const phase of phases) {\n if (progress >= phase.startNormalized && progress <= phase.endNormalized) {\n const phaseProgress =\n (progress - phase.startNormalized) / (phase.endNormalized - phase.startNormalized);\n return {\n id: phase.id,\n name: phase.name,\n progress: Math.max(0, Math.min(1, phaseProgress)),\n color: phase.color,\n };\n }\n }\n\n // Fallback: if between phases or past all phases, use the last applicable\n const lastPhase = phases[phases.length - 1];\n if (lastPhase) {\n return {\n id: lastPhase.id,\n name: lastPhase.name,\n progress: progress >= lastPhase.endNormalized ? 1 : 0,\n color: lastPhase.color,\n };\n }\n\n // No phases at all — shouldn't happen with valid data\n return { id: 'unknown', name: 'Unknown', progress: 0, color: '#888888' };\n}\n\n// --- Running Stats ---\n\nfunction computeStats(\n events: VizEvent[],\n agents: AgentState[],\n progress: number,\n): FrameState['stats'] {\n let commitsToDate = 0;\n let prsToDate = 0;\n let messagesToDate = 0;\n\n for (const e of events) {\n if (e.normalizedTime > progress) break; // events are sorted\n if (e.type === 'commit') commitsToDate++;\n else if (e.type === 'pr_merged') prsToDate++;\n else if (e.type === 'message') messagesToDate++;\n }\n\n return {\n commitsToDate,\n prsToDate,\n messagesToDate,\n activeAgents: agents.length,\n };\n}\n\n// --- Commit Dots ---\n\n/**\n * Commit type color palette.\n * Primary color is by commit type (nature of work), not agent.\n */\nconst COMMIT_TYPE_COLORS: Record<string, string> = {\n feat: '#00e5cc', // cyan — new features\n fix: '#ffaa33', // orange — bug fixes\n hotfix: '#ffaa33', // orange — urgent fixes\n rework: '#ffaa33', // orange — rework\n infra: '#4488ff', // blue — infrastructure\n ci: '#4488ff', // blue — CI/CD\n refactor: '#4488ff', // blue — refactoring\n scaffold: '#4488ff', // blue — scaffolding\n test: '#aa66ff', // purple — tests\n perf: '#00e5cc', // cyan — performance\n docs: '#6bcb77', // green — documentation\n chore: '#555555', // dim — chores\n tweak: '#555555', // dim — tweaks\n merge: '#333333', // very dim — merges\n other: '#555555', // dim — unclassified\n};\n\nconst DEFAULT_COMMIT_COLOR = '#555555';\n\nfunction computeCommitDots(events: VizEvent[], progress: number): CommitDot[] {\n // First pass: collect raw dots and find max commit size\n const rawDots: Array<{\n normalizedTime: number;\n insertions: number;\n deletions: number;\n agent: string;\n commitType: string;\n color: string;\n size: number;\n }> = [];\n let maxSize = 0;\n\n for (const event of events) {\n if (event.normalizedTime > progress) break;\n if (event.type !== 'commit') continue;\n\n const insertions = (event.metadata.insertions as number) ?? 0;\n const deletions = (event.metadata.deletions as number) ?? 0;\n const commitType = (event.metadata.commitType as string) ?? 'other';\n const size = insertions + deletions;\n if (size > maxSize) maxSize = size;\n\n rawDots.push({\n normalizedTime: event.normalizedTime,\n insertions,\n deletions,\n agent: event.agent,\n commitType,\n color: COMMIT_TYPE_COLORS[commitType] ?? DEFAULT_COMMIT_COLOR,\n size,\n });\n }\n\n // Second pass: compute sqrt-scaled height normalization.\n // sqrt gives clear differentiation at the low end, compression at the top.\n // 10 vs 100 lines = 3× visual difference. 1000 vs 10000 = 2× visual difference.\n const sqrtMax = Math.sqrt(maxSize);\n const dots: CommitDot[] = rawDots.map((d) => ({\n normalizedTime: d.normalizedTime,\n insertions: d.insertions,\n deletions: d.deletions,\n agent: d.agent,\n commitType: d.commitType,\n color: d.color,\n heightNorm: sqrtMax > 0 ? Math.sqrt(d.size) / sqrtMax : 0,\n }));\n\n return dots;\n}\n\n// --- PR Bars ---\n\nfunction computePRBars(agents: AgentLayout[], events: VizEvent[], progress: number): PRBar[] {\n const agentMap = new Map(agents.map((a) => [a.id, a]));\n\n // Index: pr_created events by prNumber\n const createdMap = new Map<number, VizEvent>();\n // Index: pr_merged events by prNumber\n const mergedMap = new Map<number, VizEvent>();\n\n for (const event of events) {\n if (event.normalizedTime > progress) break;\n const prNumber = event.metadata.prNumber as number | undefined;\n if (prNumber === undefined) continue;\n\n if (event.type === 'pr_created') {\n createdMap.set(prNumber, event);\n } else if (event.type === 'pr_merged') {\n mergedMap.set(prNumber, event);\n }\n }\n\n const bars: PRBar[] = [];\n\n for (const [prNumber, created] of createdMap) {\n const agent = agentMap.get(created.agent);\n if (!agent) continue;\n\n const merged = mergedMap.get(prNumber);\n const additions = (created.metadata.additions as number) ?? 0;\n const deletions = (created.metadata.deletions as number) ?? 0;\n\n bars.push({\n prNumber,\n openedNormalized: created.normalizedTime,\n mergedNormalized: merged ? merged.normalizedTime : null,\n agent: created.agent,\n color: agent.color,\n additions,\n deletions,\n row: 0, // populated by packPRRows()\n });\n }\n\n return bars;\n}\n\n// --- Artifact Bars ---\n\n/** Artifact type → color from FEATURES.md spec */\nconst ARTIFACT_TYPE_COLORS: Record<string, string> = {\n task: '#00e5cc',\n decision: '#ffd93d',\n doc: '#4d96ff',\n code: '#c084fc',\n asset: '#ff8c42',\n};\nconst DEFAULT_ARTIFACT_COLOR = '#4d96ff'; // fallback to doc color\n\nfunction computeArtifactBars(\n _agents: AgentLayout[],\n events: VizEvent[],\n progress: number,\n): ArtifactBar[] {\n const bars: ArtifactBar[] = [];\n\n for (const event of events) {\n if (event.normalizedTime > progress) break;\n if (event.type !== 'artifact') continue;\n\n const artifactType = (event.metadata.type as string) ?? 'doc';\n\n bars.push({\n normalizedTime: event.normalizedTime,\n type: artifactType,\n agent: event.agent,\n color: ARTIFACT_TYPE_COLORS[artifactType] ?? DEFAULT_ARTIFACT_COLOR,\n row: 0, // populated by packArtifactRows()\n });\n }\n\n return bars;\n}\n\n// --- Edges (persistent communication lines with decay) ---\n\nfunction computeEdges(agents: AgentLayout[], events: VizEvent[], progress: number): EdgeState[] {\n const agentMap = new Map(agents.map((a) => [a.id, a]));\n\n // Accumulate message strength per agent pair with exponential decay\n const edgeStrengths = new Map<string, number>();\n\n for (const event of events) {\n if (event.normalizedTime > progress) break;\n if (event.type !== 'message' || !event.targetAgent) continue;\n if (event.agent === event.targetAgent) continue;\n\n // Canonical key: sorted pair\n const pair =\n event.agent < event.targetAgent\n ? `${event.agent}:${event.targetAgent}`\n : `${event.targetAgent}:${event.agent}`;\n\n const age = progress - event.normalizedTime;\n const decayedStrength = Math.exp(-EDGE_DECAY_RATE * age);\n\n edgeStrengths.set(pair, (edgeStrengths.get(pair) ?? 0) + decayedStrength);\n }\n\n if (edgeStrengths.size === 0) return [];\n\n // Dual normalization: per-frame max prevents outliers from dominating,\n // saturation cap prevents first-message-at-full-width.\n // Takes the stricter (lower) of the two caps.\n const maxRaw = Math.max(...edgeStrengths.values(), 1);\n\n const edges: EdgeState[] = [];\n\n for (const [pair, raw] of edgeStrengths) {\n const strength = Math.min(raw / maxRaw, raw / EDGE_SATURATION);\n if (strength < EDGE_MIN_STRENGTH) continue;\n\n const [fromId, toId] = pair.split(':');\n const fromAgent = agentMap.get(fromId);\n const toAgent = agentMap.get(toId);\n if (!fromAgent) continue;\n\n const fromColor = fromAgent.color;\n const toColor = toAgent?.color ?? fromColor;\n\n edges.push({\n fromId,\n toId,\n strength,\n color: fromColor,\n fromColor,\n toColor,\n });\n }\n\n return edges;\n}\n\n// --- Milestones ---\n\nfunction computeMilestones(vizData: VizData, progress: number): MilestoneState[] {\n return vizData.milestones.map((m) => ({\n normalizedTime: m.normalizedTime,\n label: m.label,\n visible: progress >= m.normalizedTime,\n }));\n}\n\n// --- All Phases ---\n\n/** Fade zone for phase labels: 0.5% of total timeline at each boundary */\nconst LABEL_FADE_ZONE = 0.005;\n\n/**\n * Compute label opacity for a single phase at a given progress.\n * Sequential fade: old label fully out before new label fades in.\n * The last phase never fades out — there's nothing after it.\n * When isLast=true, the upper bound extends to 1.0 regardless of\n * endNormalized (production data may have endNormalized < 1.0).\n * Exported for unit testing.\n */\nexport function computePhaseLabelOpacity(\n progress: number,\n startNormalized: number,\n endNormalized: number,\n isLast = false,\n): number {\n const upperBound = isLast ? 1.0 : endNormalized;\n if (progress < startNormalized || progress > upperBound) return 0;\n const fadeIn = Math.min((progress - startNormalized) / LABEL_FADE_ZONE, 1.0);\n const fadeOut = isLast ? 1.0 : Math.min((endNormalized - progress) / LABEL_FADE_ZONE, 1.0);\n return Math.min(fadeIn, fadeOut);\n}\n\nfunction computeAllPhases(phases: VizPhase[], progress: number): AllPhaseState[] {\n return phases.map((p, i) => ({\n id: p.id,\n name: p.name,\n startNormalized: p.startNormalized,\n endNormalized: p.endNormalized,\n color: p.color,\n labelOpacity: computePhaseLabelOpacity(\n progress,\n p.startNormalized,\n p.endNormalized,\n i === phases.length - 1,\n ),\n }));\n}\n\n// --- Trust Level ---\n\n/**\n * Smoothstep interpolation — cubic Hermite (smooth start and end).\n * Maps t from [0,1] to [0,1] with zero first-derivative at boundaries.\n */\nfunction smoothstep(t: number): number {\n const x = Math.max(0, Math.min(1, t));\n return x * x * (3 - 2 * x);\n}\n\n/**\n * Compute trust level at a given progress via smoothstep interpolation\n * between keyframes. Before first keyframe: use first keyframe's level.\n * After last keyframe: use last keyframe's level.\n * Between keyframes: smoothstep interpolation.\n */\nfunction computeTrustLevel(keyframes: VizTrustKeyframe[], progress: number): number {\n if (keyframes.length === 0) return 0.5; // neutral default\n\n // Before first keyframe\n if (progress <= keyframes[0].normalizedTime) {\n return keyframes[0].level;\n }\n\n // After last keyframe\n if (progress >= keyframes[keyframes.length - 1].normalizedTime) {\n return keyframes[keyframes.length - 1].level;\n }\n\n // Find surrounding keyframes\n for (let i = 0; i < keyframes.length - 1; i++) {\n const a = keyframes[i];\n const b = keyframes[i + 1];\n\n if (progress >= a.normalizedTime && progress <= b.normalizedTime) {\n const t = (progress - a.normalizedTime) / (b.normalizedTime - a.normalizedTime);\n return a.level + (b.level - a.level) * smoothstep(t);\n }\n }\n\n // Shouldn't reach here with valid sorted keyframes\n return keyframes[keyframes.length - 1].level;\n}\n\n// --- Main Entry Point ---\n\n/**\n * Compute the maximum totalWork across all agents at a given progress.\n * Used for normalizing work-based growth (growthFactor = totalWork / maxTotalWork).\n * Exported for testing.\n */\nexport function computeMaxTotalWork(\n agents: AgentLayout[],\n progress: number,\n events: VizEvent[],\n): number {\n let max = 1; // floor of 1 prevents division by zero\n for (const agent of agents) {\n const work = computeTotalWork(agent.id, progress, events);\n if (work > max) max = work;\n }\n return max;\n}\n\nexport function getFrameState(\n progress: number,\n vizData: VizData,\n layoutContext?: LayoutContext,\n): FrameState {\n // Clamp progress to [0, 1]\n const p = Math.max(0, Math.min(1, progress));\n\n // Compute duration-scaled time windows\n const tw = computeTimeWindows(vizData.timeRange.durationMs);\n\n // Pre-compute max total work for normalization across all agents\n const maxTotalWork = computeMaxTotalWork(vizData.agents, p, vizData.events);\n\n // Compute agent states (only visible agents)\n const agents: AgentState[] = [];\n for (const agent of vizData.agents) {\n const state = computeAgentState(agent, p, vizData.events, maxTotalWork, layoutContext);\n if (state) agents.push(state);\n }\n\n const particles = computeParticles(vizData.agents, vizData.events, p, tw.particleWindow);\n const quotePills = computeQuotePills(\n vizData.quotes,\n p,\n vizData.agents,\n agents,\n layoutContext,\n tw.narrationWindow,\n tw.narrationWindowDense,\n tw.narrationClusterThreshold,\n );\n const phase = computePhase(vizData.phases, p);\n const stats = computeStats(vizData.events, agents, p);\n\n // New data layers\n const commitDots = computeCommitDots(vizData.events, p);\n\n // Compute minimum gap in normalized time to ensure at least 3px between bars.\n // 2px was getting eaten by WebGL anti-aliasing at larger viewports — 3px\n // survives AA blending and sub-pixel edge rounding on all DPR values.\n const MIN_GAP_PX = 3;\n const timeWidth = layoutContext?.timeWidth;\n const minGapNorm =\n layoutContext && layoutContext.viewportPxWidth > 0 && timeWidth && timeWidth > 0\n ? MIN_GAP_PX / ((layoutContext.viewportPxWidth / layoutContext.worldWidth) * timeWidth)\n : undefined;\n\n // Minimum bar width in normalized time — the renderer expands short bars to\n // layout.minBarWidth, so packing must account for this expanded width.\n // Without this, the engine packs bars tightly but the renderer draws them wider,\n // eating the gap between adjacent bars.\n const minBarWidthNorm = layoutContext?.minBarWidthNorm;\n\n const prBars = packPRRows(\n computePRBars(vizData.agents, vizData.events, p),\n p,\n minGapNorm,\n minBarWidthNorm,\n );\n const artifactBars = packArtifactRows(\n computeArtifactBars(vizData.agents, vizData.events, p),\n p,\n minGapNorm,\n minBarWidthNorm,\n );\n const edges = computeEdges(vizData.agents, vizData.events, p);\n const visibleMilestones = computeMilestones(vizData, p);\n const allPhases = computeAllPhases(vizData.phases, p);\n\n // Editorial narration\n const editorialNarration = computeEditorialNarration(vizData.editorialNarration ?? [], p);\n\n // Trust level\n const trustLevel = computeTrustLevel(vizData.trustKeyframes ?? [], p);\n\n // Time context\n const totalHours = vizData.timeRange.durationMs / (1000 * 60 * 60);\n const currentHours = totalHours * p;\n\n return {\n projectTitle: vizData.projectTitle ?? 'miriad-viz',\n progress: p,\n agents,\n particles,\n quotePills,\n editorialNarration,\n phase,\n stats,\n commitDots,\n prBars,\n artifactBars,\n edges,\n visibleMilestones,\n allPhases,\n trustLevel,\n totalHours,\n currentHours,\n };\n}\n","/**\n * Processing layer — processData(rawData, curationData) → VizDataRaw\n *\n * Pure function. Deterministic. No side effects.\n * Bridges Contract A (RawData + CurationData) → Contract B (VizDataRaw).\n *\n * Responsibilities:\n * - Derive time range from data\n * - Merge all event types into unified sorted VizEventRaw[]\n * - Map phases, quotes, milestones with raw timestamps\n * - Compute aggregate stats\n *\n * Does NOT compute: positions, colors, radius, normalizedTime.\n * Those are derived at runtime by enrichVizData().\n */\n\nimport type { CurationData, RawData } from '../types/raw-data.js';\nimport type {\n AgentLayoutRaw,\n EditorialNarrationEntry,\n VizDataRaw,\n VizEventRaw,\n VizMilestoneRaw,\n VizPhaseRaw,\n VizQuoteRaw,\n VizTrustKeyframeRaw,\n} from '../types/viz-data.js';\n\n// --- Commit Type ---\n\n/**\n * Derive commit type from conventional commit message prefix.\n * Extracts the prefix before ':' (ignoring scope in parens).\n * Returns lowercase type string matching POC's COMMIT_COLORS keys.\n */\nexport function deriveCommitType(message: string): string {\n if (!message) return 'other';\n const match = message.match(/^(\\w+)(?:\\([^)]*\\))?:/);\n return match ? match[1].toLowerCase() : 'other';\n}\n\n// --- Time ---\n\n/**\n * Tail padding fraction — extends timeline duration so all animations\n * (particles, pills, comm line decay) settle before progress=1.0.\n *\n * The last ~5% of the timeline is \"settling time\" — no new events fire,\n * existing animations complete their fade/decay.\n *\n * Coverage at 5%:\n * - Particles (PARTICLE_WINDOW=0.005): settled ✓ (0.05 > 0.005)\n * - Pills (NARRATION_WINDOW=0.04): settled ✓ (0.05 > 0.04)\n * - Comm lines: no new inputs, exponential decay continues naturally\n */\nexport const TIMELINE_TAIL_FRACTION = 0.02;\n\n/**\n * Derive the project time range from all available timestamps.\n * Uses the earliest and latest timestamps across commits, PRs, messages,\n * agent joins, and curation data.\n *\n * Adds tail padding (TIMELINE_TAIL_FRACTION) so the last events map to\n * ~0.95 normalizedTime, leaving headroom for animations to settle.\n */\n/**\n * Options for timeline padding — extends boundaries before/after actual data.\n * Creates breathing room for narration intro/outro.\n */\nexport interface ProcessOptions {\n /** Milliseconds to pad before the first event. */\n padStartMs?: number;\n /** Milliseconds to pad after the last event. */\n padEndMs?: number;\n}\n\nfunction deriveTimeRange(\n rawData: RawData,\n curationData: CurationData,\n options?: ProcessOptions,\n): { start: number; end: number; durationMs: number } {\n // If curated bounds exist, use them as authoritative start/end.\n // Raw data outside the window is still included but doesn't expand the timeline.\n const hasCuratedBounds =\n curationData.curatedStartMs !== undefined && curationData.curatedEndMs !== undefined;\n\n let start: number;\n let end: number;\n\n if (hasCuratedBounds) {\n start = curationData.curatedStartMs!;\n end = curationData.curatedEndMs!;\n } else {\n // Fallback: derive from raw event data only.\n // Curation timestamps (phases, milestones, quotes) are derived from the same\n // events and should NOT expand the range — they can have hand-edited boundaries\n // that extend beyond actual data, inflating durationMs.\n const timestamps: number[] = [];\n\n for (const c of rawData.commits) timestamps.push(c.timestamp);\n for (const p of rawData.prs) {\n timestamps.push(p.createdAt);\n if (p.mergedAt) timestamps.push(p.mergedAt);\n }\n for (const m of rawData.messages) timestamps.push(m.timestamp);\n for (const a of rawData.agents) timestamps.push(a.joinedAt);\n\n if (timestamps.length === 0) {\n throw new Error('processData: no timestamps found in data — cannot derive time range');\n }\n\n start = Math.min(...timestamps);\n end = Math.max(...timestamps);\n }\n\n // Apply padding — extends timeline before first event / after last event\n const padStartMs = options?.padStartMs ?? 0;\n const padEndMs = options?.padEndMs ?? 0;\n start = start - padStartMs;\n end = end + padEndMs;\n\n const rawDuration = end - start;\n\n // Single-event or unedited scaffold → default to 1 hour\n const ONE_HOUR_MS = 3_600_000;\n const effectiveDuration = rawDuration > 0 ? rawDuration : ONE_HOUR_MS;\n\n // Extend duration so animations settle before progress=1.0\n const durationMs = effectiveDuration * (1 + TIMELINE_TAIL_FRACTION);\n\n return { start, end, durationMs };\n}\n\n// --- Agents ---\n\n/**\n * Build raw agent entries (id, role, joinedAt, label).\n * No visual properties — those are computed at runtime by enrichVizData().\n */\nfunction buildAgents(rawData: RawData): AgentLayoutRaw[] {\n return rawData.agents.map((agent) => ({\n id: agent.id,\n role: agent.role,\n joinedAt: agent.joinedAt,\n label: `@${agent.id}`,\n }));\n}\n\n// --- Events ---\n\n/**\n * Merge all event sources into a unified VizEventRaw[] sorted by timestamp.\n *\n * Event types:\n * - agent_join: from RawAgent.joinedAt\n * - commit: from RawCommit\n * - pr_created: from RawPR.createdAt\n * - pr_merged: from RawPR.mergedAt (if merged)\n * - message: from RawMessage (one event per mention)\n * - milestone: from CurationMilestone\n * - artifact: from RawArtifact\n */\nfunction mergeEvents(rawData: RawData, curationData: CurationData): VizEventRaw[] {\n const events: VizEventRaw[] = [];\n\n // Agent joins\n for (const agent of rawData.agents) {\n events.push({\n timestamp: agent.joinedAt,\n type: 'agent_join',\n agent: agent.id,\n metadata: { role: agent.role },\n });\n }\n\n // Commits\n for (const commit of rawData.commits) {\n events.push({\n timestamp: commit.timestamp,\n type: 'commit',\n agent: commit.agent,\n metadata: {\n sha: commit.sha,\n filesChanged: commit.filesChanged,\n insertions: commit.insertions,\n deletions: commit.deletions,\n prNumber: commit.prNumber,\n commitType: deriveCommitType(commit.message),\n },\n });\n }\n\n // PRs — two events per PR: created and merged\n for (const pr of rawData.prs) {\n events.push({\n timestamp: pr.createdAt,\n type: 'pr_created',\n agent: pr.agent,\n metadata: {\n prNumber: pr.number,\n title: pr.title,\n additions: pr.additions,\n deletions: pr.deletions,\n },\n });\n\n if (pr.mergedAt) {\n events.push({\n timestamp: pr.mergedAt,\n type: 'pr_merged',\n agent: pr.agent,\n metadata: {\n prNumber: pr.number,\n title: pr.title,\n },\n });\n }\n }\n\n // Messages — one event per message, targetAgent from first mention\n for (const msg of rawData.messages) {\n events.push({\n timestamp: msg.timestamp,\n type: 'message',\n agent: msg.sender,\n targetAgent: msg.mentions[0], // primary target\n metadata: {\n mentions: msg.mentions,\n },\n });\n }\n\n // Milestones\n for (const milestone of curationData.milestones) {\n events.push({\n timestamp: milestone.timestamp,\n type: 'milestone',\n agent: '', // milestones aren't agent-specific\n metadata: {\n label: milestone.label,\n phase: milestone.phase,\n },\n });\n }\n\n // Artifacts\n for (const artifact of rawData.artifacts) {\n events.push({\n timestamp: artifact.createdAt,\n type: 'artifact',\n agent: artifact.agent ?? '',\n metadata: {\n slug: artifact.slug,\n type: artifact.type,\n },\n });\n }\n\n // Sort by timestamp, stable (preserves insertion order for ties)\n events.sort((a, b) => a.timestamp - b.timestamp);\n\n return events;\n}\n\n// --- Phases ---\n\n/**\n * Phase color palette — assigned by index.\n * CurationPhase doesn't carry color (that's a viz concern, not editorial).\n * These match the clock project's phase colors from the POC.\n */\nconst PHASE_COLORS = [\n '#3b82f6', // blue\n '#ef4444', // red\n '#f59e0b', // amber\n '#8b5cf6', // purple\n '#10b981', // emerald\n '#ec4899', // pink\n '#f97316', // orange\n '#06b6d4', // cyan\n];\n\n/**\n * Map curation phases to viz phases, extending boundaries to cover the full\n * time range. Without this, the renderer extends the last phase's background\n * to 1.0 creating a spurious unlabeled colored region at the tail.\n *\n * - First phase startTime is pulled back to timeRange.start (if events exist before it)\n * - Last phase endTime is pushed forward to timeRange.end (if events exist after it)\n * - Interior phase boundaries are never modified\n */\nfunction mapPhases(\n curationData: CurationData,\n timeRange: { start: number; end: number },\n): VizPhaseRaw[] {\n const phases = curationData.phases;\n if (phases.length === 0) return [];\n\n return phases.map((p, i) => ({\n id: p.id,\n name: p.name,\n startTime: i === 0 ? Math.min(p.startTime, timeRange.start) : p.startTime,\n endTime: i === phases.length - 1 ? Math.max(p.endTime, timeRange.end) : p.endTime,\n color: PHASE_COLORS[i % PHASE_COLORS.length],\n }));\n}\n\n// --- Quotes ---\n\nfunction mapQuotes(curationData: CurationData): VizQuoteRaw[] {\n return curationData.quotes.map((q) => ({\n text: q.text,\n speaker: q.speaker,\n timestamp: q.timestamp,\n phase: q.phase,\n ...(q.emoji && { emoji: q.emoji }),\n ...(q.mood && { mood: q.mood }),\n }));\n}\n\n// --- Milestones ---\n\nfunction mapMilestones(curationData: CurationData): VizMilestoneRaw[] {\n return curationData.milestones.map((m) => ({\n label: m.label,\n timestamp: m.timestamp,\n phase: m.phase,\n }));\n}\n\n// --- Trust Keyframes ---\n\nfunction mapTrustKeyframes(curationData: CurationData): VizTrustKeyframeRaw[] {\n return curationData.trustKeyframes.map((k) => ({\n timestamp: k.timestamp,\n level: k.level,\n label: k.label,\n }));\n}\n\n// --- Main Entry Point ---\n\n// --- Editorial Narration ---\n\n/** Default color for phases that don't specify one. */\nconst DEFAULT_PHASE_COLOR = '#6b7280';\n\n/**\n * Build the editorial narration queue from curation data.\n *\n * Two modes:\n * 1. **Curated narration** — if curationData.editorialNarration exists, convert\n * timestamps to normalizedTime and return as-is. This is the authored path.\n * 2. **Generic fallback** — if no narration is provided, generate phase-based\n * entries from curationData.phases. Each phase gets one entry at its start\n * time with the phase name as title and a generic description.\n *\n * @param durationMs — total time range in ms (for normalizedTime conversion)\n * @param curationData — editorial content (phases, and optionally narration)\n */\nexport function buildEditorialNarration(\n durationMs: number,\n curationData: CurationData,\n): EditorialNarrationEntry[] {\n if (durationMs <= 0) return [];\n\n // Path 1: curated narration exists — convert timestamps to normalizedTime\n if (curationData.editorialNarration && curationData.editorialNarration.length > 0) {\n const timeRangeStart = deriveTimeRangeStart(curationData);\n return curationData.editorialNarration\n .map((entry) => ({\n normalizedTime: Math.max(0, Math.min(1, (entry.timestamp - timeRangeStart) / durationMs)),\n phaseTitle: entry.phaseTitle,\n phaseColor: entry.phaseColor,\n text: entry.text,\n type: entry.type,\n }))\n .sort((a, b) => a.normalizedTime - b.normalizedTime);\n }\n\n // Path 2: no narration — generate generic entries from phases\n return buildGenericNarration(durationMs, curationData);\n}\n\n/**\n * Derive the earliest timestamp from curation phases.\n * Used to convert absolute timestamps to normalizedTime.\n */\nfunction deriveTimeRangeStart(curationData: CurationData): number {\n if (curationData.phases.length === 0) return 0;\n return Math.min(...curationData.phases.map((p) => p.startTime));\n}\n\n/**\n * Generate generic phase-based narration from curation phases.\n * Each phase gets one entry at its start time with the phase name.\n */\nfunction buildGenericNarration(\n durationMs: number,\n curationData: CurationData,\n): EditorialNarrationEntry[] {\n if (curationData.phases.length === 0) return [];\n\n const timeRangeStart = deriveTimeRangeStart(curationData);\n\n return curationData.phases\n .map((phase) => ({\n normalizedTime: Math.max(0, Math.min(1, (phase.startTime - timeRangeStart) / durationMs)),\n phaseTitle: phase.name.toUpperCase(),\n phaseColor: DEFAULT_PHASE_COLOR,\n text: phase.name,\n type: 'phase' as const,\n }))\n .sort((a, b) => a.normalizedTime - b.normalizedTime);\n}\n\n/**\n * Process raw + curation data into VizDataRaw (JSON-ready).\n *\n * Pure function. Deterministic. No side effects.\n * Given the same inputs, always produces the same output.\n *\n * Call enrichVizData() on the result to compute derived visual properties\n * (positions, colors, radius, normalizedTime) at runtime.\n */\nexport function processData(\n rawData: RawData,\n curationData: CurationData,\n options?: ProcessOptions,\n): VizDataRaw {\n const timeRange = deriveTimeRange(rawData, curationData, options);\n const agents = buildAgents(rawData);\n const events = mergeEvents(rawData, curationData);\n const phases = mapPhases(curationData, timeRange);\n const quotes = mapQuotes(curationData);\n const milestones = mapMilestones(curationData);\n const trustKeyframes = mapTrustKeyframes(curationData);\n\n const editorialNarration = buildEditorialNarration(timeRange.durationMs, curationData);\n\n return {\n projectTitle: rawData.project.name,\n agents,\n events,\n phases,\n quotes,\n milestones,\n trustKeyframes,\n editorialNarration,\n timeRange,\n stats: {\n totalCommits: rawData.commits.length,\n totalPRs: rawData.prs.length,\n totalMessages: rawData.messages.length,\n totalAgents: rawData.agents.length,\n },\n };\n}\n","/**\n * agent-layout — staggered clique positioning for agent nodes.\n *\n * Pure functions:\n * - computeAgentPositions(agents, messages, opts?) → Map<id, {x, y}> (low-level)\n * - computeLayoutPositions(rawVizData, opts?) → Map<id, {x, y}> (convenience)\n *\n * Algorithm:\n * 1. Build weighted communication adjacency from messages\n * 2. Classify agents into tiers by role (coordination/support/builder)\n * 3. Within each tier, detect cliques via greedy BFS on comm edges\n * 4. Compute a non-overlapping grid layout that respects bounding box sizes\n * 5. Assign grid slots to agents preserving tier ordering and clique grouping\n * 6. Apply golden-ratio jitter for organic feel\n *\n * Design goals:\n * - ZERO bounding-box overlaps at any viewport\n * - Uses FULL width and height of agent band\n * - Staggered — NOT a rigid grid (jitter applied)\n * - Cliques emerge naturally: communicating agents get adjacent slots\n * - Human always centered\n * - Works for 1-30 agents, any data\n * - Deterministic: same input → same output\n */\n\ninterface AgentInput {\n id: string;\n role: string;\n joinedAt: number;\n}\n\ninterface MessageInput {\n sender: string;\n mentions: string[];\n}\n\nexport interface AgentPosition {\n x: number;\n y: number;\n}\n\nexport interface LayoutOptions {\n /** Minimum Y gap between agent centers in normalized 0-1 space. */\n minYGapNorm?: number;\n /** Minimum X gap between agent centers in normalized 0-1 space. */\n minXGapNorm?: number;\n /**\n * Band aspect ratio (width / height) in world space.\n * Used as fallback to derive minXGapNorm from minYGapNorm if minXGapNorm not provided.\n */\n bandAspect?: number;\n}\n\n// --- Configuration ---\n\n/** Margin from edges (0-1 space) */\nconst MARGIN = 0.04;\n/** Minimum Y gap between any two agents (prevents label overlap) — fallback */\nconst MIN_Y_GAP = 0.08;\n/** Minimum X gap between any two agents — fallback (assumes ~3:1 label aspect) */\nconst MIN_X_GAP = 0.12;\n/** Default band aspect if not provided */\nconst DEFAULT_BAND_ASPECT = 5.0;\n/** Golden ratio for jitter */\nconst PHI = (1 + Math.sqrt(5)) / 2;\n/** Jitter amplitude (fraction of cell size) */\nconst JITTER_AMP = 3.0;\n\n// --- Public API ---\n\n/**\n * Compute agent positions from communication graph.\n * Returns a Map of agent ID → {x, y} in normalized 0-1 space.\n */\nexport function computeAgentPositions(\n agents: AgentInput[],\n messages: MessageInput[],\n optsOrMinYGap?: LayoutOptions | number,\n): Map<string, AgentPosition> {\n // Support legacy call signature: computeAgentPositions(agents, msgs, minYGapNorm)\n const opts: LayoutOptions =\n typeof optsOrMinYGap === 'number' ? { minYGapNorm: optsOrMinYGap } : (optsOrMinYGap ?? {});\n\n if (agents.length === 0) return new Map();\n\n // Single agent → center\n if (agents.length === 1) {\n return new Map([[agents[0].id, { x: 0.5, y: 0.5 }]]);\n }\n\n // Two agents → spread apart\n if (agents.length === 2) {\n const [a, b] = agents;\n const humanFirst = a.role === 'human' ? [a, b] : b.role === 'human' ? [b, a] : [a, b];\n return new Map([\n [humanFirst[0].id, { x: 0.35, y: 0.45 }],\n [humanFirst[1].id, { x: 0.65, y: 0.55 }],\n ]);\n }\n\n const bandAspect = opts.bandAspect ?? DEFAULT_BAND_ASPECT;\n const minYGap = Math.max(MIN_Y_GAP, opts.minYGapNorm ?? MIN_Y_GAP);\n // X gap: use explicit value if provided, otherwise use fallback.\n // The fallback MIN_X_GAP is conservative — real minXGapNorm from layout.ts\n // accounts for actual label width at the current viewport.\n const minXGap = opts.minXGapNorm ?? Math.max(MIN_X_GAP, minYGap / bandAspect);\n\n // Build pairwise communication adjacency\n const adjacency = buildAdjacency(agents, messages);\n\n // Order agents: tier classification → clique grouping → human centered\n const ordered = orderAgents(agents, adjacency);\n\n // Compute grid dimensions that guarantee no overlap\n const { cols, rows } = computeGridDims(ordered.length, minXGap, minYGap);\n\n // Assign grid positions with jitter\n const positions = assignGridPositions(ordered, cols, rows, minXGap, minYGap);\n\n return positions;\n}\n\n// --- Agent Ordering ---\n\n/**\n * Order agents for grid placement: tiers → cliques → human centered.\n *\n * Returns agents in the order they should fill grid slots (left-to-right,\n * top-to-bottom). Tier ordering ensures coordination agents are at top,\n * builders at bottom. Clique grouping ensures communicating agents are adjacent.\n * Human is inserted at the center position.\n */\nfunction orderAgents(agents: AgentInput[], adjacency: AdjacencyMap): AgentInput[] {\n // Classify into tiers\n const humans: AgentInput[] = [];\n const coordination: AgentInput[] = [];\n const support: AgentInput[] = [];\n const builders: AgentInput[] = [];\n\n for (const agent of agents) {\n switch (agent.role) {\n case 'human':\n humans.push(agent);\n break;\n case 'lead':\n case 'pm':\n case 'reviewer':\n coordination.push(agent);\n break;\n case 'ideation':\n case 'researcher':\n case 'tester':\n case 'auditor':\n support.push(agent);\n break;\n default:\n builders.push(agent);\n break;\n }\n }\n\n // Within each tier, order by cliques (BFS connected components)\n const orderedCoord = orderByCliques(coordination, adjacency);\n const orderedSupport = orderByCliques(support, adjacency);\n const orderedBuilders = orderByCliques(builders, adjacency);\n\n // Human FIRST (gets center of spiral grid), then tiers radiate outward.\n // Coordination closest to human, then support, then builders at edges.\n return [...humans, ...orderedCoord, ...orderedSupport, ...orderedBuilders];\n}\n\n/**\n * Order agents within a tier by clique membership.\n * Cliques sorted by size (largest first), agents within cliques by comm weight.\n */\nfunction orderByCliques(agents: AgentInput[], adjacency: AdjacencyMap): AgentInput[] {\n if (agents.length === 0) return [];\n const cliques = findCliques(agents, adjacency);\n return cliques.flat();\n}\n\n// --- Grid Layout ---\n\n/**\n * Compute grid dimensions (cols × rows) that fit N agents without overlap.\n *\n * The grid must satisfy:\n * - (cols-1) × minXGap ≤ usable width (1 - 2*MARGIN)\n * - (rows-1) × minYGap ≤ usable height (1 - 2*MARGIN)\n * - cols × rows ≥ N\n *\n * We prefer odd row counts (for center placement of human) and try to\n * balance the grid shape rather than maximizing one axis.\n */\nfunction computeGridDims(\n n: number,\n minXGap: number,\n minYGap: number,\n): { cols: number; rows: number } {\n const usable = 1 - 2 * MARGIN;\n\n // Maximum columns and rows that fit\n const maxCols = Math.max(1, Math.floor(usable / minXGap) + 1);\n const maxRows = Math.max(1, Math.floor(usable / minYGap) + 1);\n\n // Try all valid (cols, rows) combinations and pick the best one.\n // \"Best\" = balanced grid (cols ≈ rows), odd rows for centering, minimal waste.\n // With grid compression, we want a balanced shape — not a tall narrow column.\n let bestCols = Math.min(maxCols, n);\n let bestRows = 1;\n let bestScore = Number.NEGATIVE_INFINITY;\n\n for (let r = 1; r <= Math.min(maxRows, n); r++) {\n const c = Math.ceil(n / r);\n if (c > maxCols) continue;\n if (c * r < n) continue;\n\n const waste = c * r - n;\n const oddRowBonus = r % 2 === 1 ? 5 : 0;\n // Prefer balanced grids: penalize extreme aspect ratios.\n // A 5×3 grid (ratio 1.67) is better than 2×7 (ratio 0.29).\n const ratio = c / r;\n const balanceScore = -Math.abs(Math.log(ratio)) * 5;\n const score = oddRowBonus + balanceScore - waste;\n\n if (score > bestScore) {\n bestScore = score;\n bestCols = c;\n bestRows = r;\n }\n }\n\n return { cols: bestCols, rows: bestRows };\n}\n\n/**\n * Assign agents to grid positions with honeycomb stagger and compression.\n *\n * Two improvements over a plain grid:\n * 1. **Honeycomb stagger** — even rows are offset by half a cell width.\n * This ensures no two agents share the same X column, preventing\n * comm line overlap when lines are drawn between agents.\n * 2. **Grid compression** — instead of stretching to fill the full band,\n * the grid uses the smaller of (a) even spacing across the full band\n * or (b) minimum gap × padding factor. This pulls agents toward center\n * when the band is much wider than needed.\n *\n * The grid is filled using a spiral pattern from the center outward,\n * so the first agent (human) gets the center cell and subsequent agents\n * radiate outward. Golden-ratio jitter adds organic feel.\n */\nfunction assignGridPositions(\n agents: AgentInput[],\n cols: number,\n rows: number,\n minXGap: number,\n minYGap: number,\n): Map<string, AgentPosition> {\n const positions = new Map<string, AgentPosition>();\n const usable = 1 - 2 * MARGIN;\n\n // Generate grid slots in spiral order from center\n const slots = spiralOrder(cols, rows);\n\n // --- Grid compression + honeycomb stagger ---\n // The honeycomb offsets even rows by half a cell width, so the effective\n // grid width is (cols - 1) * xStep + xStep/2 = (cols - 0.5) * xStep.\n // We must ensure this fits within the usable space.\n //\n // Compression: use the smaller of full-spread or padded minimum gap.\n // Full-spread accounts for stagger: usable / (cols - 0.5) for X.\n const COMPRESS_PAD = 1.4;\n const effectiveCols = cols > 1 && rows > 1 ? cols - 0.5 : Math.max(1, cols - 1);\n const fullSpreadX = cols > 1 ? usable / effectiveCols : 0;\n const fullSpreadY = rows > 1 ? usable / (rows - 1) : 0;\n const xStep = cols > 1 ? Math.min(fullSpreadX, minXGap * COMPRESS_PAD) : 0;\n\n const yStep = rows > 1 ? Math.min(fullSpreadY, minYGap * COMPRESS_PAD) : 0;\n\n // Honeycomb: even rows offset by half cell width in X\n const useHex = cols > 1 && rows > 1;\n const staggerX = useHex ? xStep * 0.5 : 0;\n\n // Total grid extent including X stagger\n const gridWidth = cols > 1 ? (cols - 1) * xStep + staggerX : 0;\n const gridHeight = rows > 1 ? (rows - 1) * yStep : 0;\n\n // Center the grid in [0, 1] space, clamped to MARGIN\n const xOrigin = Math.max(MARGIN, 0.5 - gridWidth / 2);\n const yOrigin = Math.max(MARGIN, 0.5 - gridHeight / 2);\n\n // Maximum jitter that won't cause overlap.\n // With stagger, closest neighbors are diagonal (adjacent rows).\n // Jitter budget uses the smaller axis gap.\n const maxJitterX = cols > 1 ? Math.max(0, (xStep - minXGap) * 0.6) : 0;\n const maxJitterY = rows > 1 ? Math.max(0, (yStep - minYGap) * 0.6) : 0;\n\n for (let i = 0; i < agents.length && i < slots.length; i++) {\n const { col, row } = slots[i];\n\n // Base position: compressed grid centered at 0.5\n let x = cols > 1 ? xOrigin + col * xStep : 0.5;\n let y = rows > 1 ? yOrigin + row * yStep : 0.5;\n\n // Honeycomb: offset even rows by half cell width in X\n if (row % 2 === 0 && cols > 1) {\n x += staggerX;\n }\n\n // Golden-ratio jitter for organic feel.\n // Edge dampening: agents near margins get less jitter to avoid\n // being pushed into the boundary. Full jitter in center, zero at edge.\n const EDGE_ZONE = 0.15;\n const edgeDampX = Math.min((x - MARGIN) / EDGE_ZONE, (1 - MARGIN - x) / EDGE_ZONE, 1);\n const edgeDampY = Math.min((y - MARGIN) / EDGE_ZONE, (1 - MARGIN - y) / EDGE_ZONE, 1);\n const dampX = Math.max(0, edgeDampX);\n const dampY = Math.max(0, edgeDampY);\n\n const jitterX = (((i * PHI) % 1) - 0.5) * 2 * maxJitterX * JITTER_AMP * dampX;\n const jitterY = (((i * PHI * PHI) % 1) - 0.5) * 2 * maxJitterY * JITTER_AMP * dampY;\n x += jitterX;\n y += jitterY;\n\n // Hard clamp as safety net\n x = Math.max(MARGIN, Math.min(1 - MARGIN, x));\n y = Math.max(MARGIN, Math.min(1 - MARGIN, y));\n\n positions.set(agents[i].id, { x, y });\n }\n\n return positions;\n}\n\n/**\n * Generate grid cell coordinates in spiral order from center.\n * First slot is the center cell, subsequent slots spiral outward.\n * Uses fractional center for even dimensions (e.g., 2 rows → center at 0.5).\n */\nfunction spiralOrder(cols: number, rows: number): Array<{ col: number; row: number }> {\n const centerCol = (cols - 1) / 2;\n const centerRow = (rows - 1) / 2;\n\n // Generate all cells with distance from center\n const cells: Array<{ col: number; row: number; dist: number; angle: number }> = [];\n for (let r = 0; r < rows; r++) {\n for (let c = 0; c < cols; c++) {\n const dx = c - centerCol;\n const dy = r - centerRow;\n const dist = Math.sqrt(dx * dx + dy * dy);\n const angle = Math.atan2(dy, dx);\n cells.push({ col: c, row: r, dist, angle });\n }\n }\n\n // Sort by distance from center, then by angle for deterministic spiral\n cells.sort((a, b) => {\n if (Math.abs(a.dist - b.dist) > 0.01) return a.dist - b.dist;\n return a.angle - b.angle;\n });\n\n return cells;\n}\n\n// --- Adjacency ---\n\ntype AdjacencyMap = Map<string, Map<string, number>>;\n\n/**\n * Build bidirectional adjacency from messages.\n * Weight = number of messages between a pair.\n */\nfunction buildAdjacency(agents: AgentInput[], messages: MessageInput[]): AdjacencyMap {\n const agentIds = new Set(agents.map((a) => a.id));\n const adj: AdjacencyMap = new Map();\n\n for (const agent of agents) {\n adj.set(agent.id, new Map());\n }\n\n for (const msg of messages) {\n if (!agentIds.has(msg.sender)) continue;\n\n for (const mention of msg.mentions) {\n if (!agentIds.has(mention)) continue;\n if (mention === msg.sender) continue;\n\n const senderMap = adj.get(msg.sender)!;\n senderMap.set(mention, (senderMap.get(mention) ?? 0) + 1);\n\n const mentionMap = adj.get(mention)!;\n mentionMap.set(msg.sender, (mentionMap.get(msg.sender) ?? 0) + 1);\n }\n }\n\n return adj;\n}\n\n// --- Clique Detection ---\n\n/**\n * Find connected components within a set of agents using BFS on comm edges.\n * Only considers edges between agents in the given set (intra-tier).\n * Returns groups sorted by size (largest first), agents within groups\n * sorted by total comm weight then joinedAt.\n */\nfunction findCliques(agents: AgentInput[], adjacency: AdjacencyMap): AgentInput[][] {\n const agentSet = new Set(agents.map((a) => a.id));\n const visited = new Set<string>();\n const cliques: AgentInput[][] = [];\n const agentMap = new Map(agents.map((a) => [a.id, a]));\n\n // Sort agents deterministically for BFS start order\n const sorted = [...agents].sort((a, b) => a.joinedAt - b.joinedAt);\n\n for (const agent of sorted) {\n if (visited.has(agent.id)) continue;\n\n const clique: AgentInput[] = [];\n const queue = [agent.id];\n visited.add(agent.id);\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n clique.push(agentMap.get(current)!);\n\n const neighbors = adjacency.get(current);\n if (!neighbors) continue;\n\n for (const [neighborId] of neighbors) {\n if (!agentSet.has(neighborId)) continue;\n if (visited.has(neighborId)) continue;\n visited.add(neighborId);\n queue.push(neighborId);\n }\n }\n\n cliques.push(clique);\n }\n\n // Sort cliques: largest first, then by earliest joinedAt\n cliques.sort((a, b) => {\n if (b.length !== a.length) return b.length - a.length;\n return Math.min(...a.map((x) => x.joinedAt)) - Math.min(...b.map((x) => x.joinedAt));\n });\n\n // Sort agents within each clique by total comm weight (desc) then joinedAt\n for (const clique of cliques) {\n clique.sort((a, b) => {\n const wA = totalWeight(a.id, adjacency);\n const wB = totalWeight(b.id, adjacency);\n if (wB !== wA) return wB - wA;\n return a.joinedAt - b.joinedAt;\n });\n }\n\n return cliques;\n}\n\nfunction totalWeight(agentId: string, adjacency: AdjacencyMap): number {\n const neighbors = adjacency.get(agentId);\n if (!neighbors) return 0;\n let total = 0;\n for (const [, weight] of neighbors) {\n total += weight;\n }\n return total;\n}\n\n// ─── Convenience: compute positions from VizDataRaw ──────────────────────────\n\nimport type { VizDataRaw } from '../types/viz-data.js';\n\n/**\n * Compute agent layout positions from raw viz data.\n * Derives the communication graph from message events (agent → targetAgent).\n * Called by enrichVizData() at viewer init.\n */\nexport function computeLayoutPositions(\n raw: VizDataRaw,\n optsOrMinYGap?: LayoutOptions | number,\n): Map<string, AgentPosition> {\n const agents = raw.agents.map((a) => ({\n id: a.id,\n role: a.role,\n joinedAt: a.joinedAt,\n }));\n\n // Build message-like inputs from message events\n const messages = raw.events\n .filter((e) => e.type === 'message' && e.targetAgent)\n .map((e) => ({\n sender: e.agent,\n mentions: [e.targetAgent!],\n }));\n\n return computeAgentPositions(agents, messages, optsOrMinYGap);\n}\n","/**\n * enrichVizData — compute all derived visual properties at runtime.\n *\n * Takes VizDataRaw (what the JSON contains) and produces VizData\n * (what engine/renderer consume). Called once at viewer init.\n *\n * Derived fields:\n * - Agent positions (force-directed layout from communication graph)\n * - Agent colors (palette lookup by id, fallback by role)\n * - Agent radius (role-based: human=0.04, agent=0.03)\n * - normalizedTime on events, phases, quotes, milestones, trust keyframes\n */\n\nimport type {\n AgentLayout,\n VizData,\n VizDataRaw,\n VizEvent,\n VizMilestone,\n VizPhase,\n VizQuote,\n VizTrustKeyframe,\n} from '../types/viz-data.js';\nimport { type LayoutOptions, computeLayoutPositions } from './agent-layout.js';\nimport { TIMELINE_TAIL_FRACTION } from './process-data.js';\n\n// ─── Color Palette ───────────────────────────────────────────────────────────\n\n/** Warm orange for the human agent — always stands out as the director. */\nconst HUMAN_COLOR = '#ff922b';\n\n/**\n * High-saturation palette for agent colors. Assigned by index, not by name,\n * so any channel's agents get distinct colors automatically.\n *\n * Palette chosen for: high saturation, dark-bg visibility, mutual distinction.\n * 13 colors covers most teams; wraps with modulo for larger ones.\n */\nconst AGENT_PALETTE = [\n '#ff6b6b', // coral red\n '#ff4da6', // hot pink\n '#ffd93d', // gold\n '#4d96ff', // sky blue\n '#ff8c42', // tangerine\n '#00e5cc', // cyan\n '#7c5cfc', // purple\n '#22d3ee', // light cyan\n '#10b981', // emerald\n '#a78bfa', // lavender\n '#6bcb77', // green\n '#c084fc', // violet\n '#f472b6', // pink\n];\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/** Resolve agent color by role and palette index. Exported for timing-narration integration. */\nexport function getAgentColor(_id: string, role: string, index: number): string {\n if (role === 'human') return HUMAN_COLOR;\n return AGENT_PALETTE[index % AGENT_PALETTE.length];\n}\n\nfunction getAgentRadius(role: string): number {\n return role === 'human' ? 0.04 : 0.03;\n}\n\nfunction normalize(timestamp: number, start: number, durationMs: number): number {\n return (timestamp - start) / durationMs;\n}\n\n// ─── Main ────────────────────────────────────────────────────────────────────\n\n/**\n * Enrich raw viz data with all derived visual properties.\n * Pure function. ~1ms for typical datasets (13 agents, 1700 events).\n *\n * @param layoutOpts - Layout options for agent positioning (minYGapNorm, bandAspect).\n * Also accepts a plain number for backwards compatibility (treated as minYGapNorm).\n */\nexport function enrichVizData(raw: VizDataRaw, layoutOpts?: LayoutOptions | number): VizData {\n const { start, end } = raw.timeRange;\n const rawDuration = end - start;\n\n // Enforce tail padding — if durationMs doesn't include the tail fraction,\n // extend it so events compress into [0, ~0.952] with settling headroom.\n // This is belt-and-suspenders: processData() should already apply it,\n // but data generated before the tail padding feature won't have it.\n const expectedDuration = rawDuration * (1 + TIMELINE_TAIL_FRACTION);\n const durationMs =\n raw.timeRange.durationMs < expectedDuration * 0.99\n ? expectedDuration\n : raw.timeRange.durationMs;\n\n // Compute agent positions from communication graph\n const positions = computeLayoutPositions(raw, layoutOpts);\n\n // Enrich agents — index-based palette so any channel gets distinct colors\n const agents: AgentLayout[] = raw.agents.map((a, i) => ({\n id: a.id,\n role: a.role,\n color: getAgentColor(a.id, a.role, i),\n position: positions.get(a.id) ?? { x: 0.5, y: 0.5 },\n radius: getAgentRadius(a.role),\n label: a.label,\n }));\n\n // Enrich events with normalizedTime\n const events: VizEvent[] = raw.events.map((e) => ({\n ...e,\n normalizedTime: normalize(e.timestamp, start, durationMs),\n }));\n\n // Enrich phases\n const phases: VizPhase[] = raw.phases.map((p) => ({\n id: p.id,\n name: p.name,\n startNormalized: normalize(p.startTime, start, durationMs),\n endNormalized: normalize(p.endTime, start, durationMs),\n color: p.color,\n }));\n\n // Enrich quotes\n const quotes: VizQuote[] = raw.quotes.map((q) => ({\n text: q.text,\n speaker: q.speaker,\n normalizedTime: normalize(q.timestamp, start, durationMs),\n phase: q.phase,\n ...(q.emoji && { emoji: q.emoji }),\n ...(q.mood && { mood: q.mood }),\n }));\n\n // Enrich milestones\n const milestones: VizMilestone[] = raw.milestones.map((m) => ({\n label: m.label,\n normalizedTime: normalize(m.timestamp, start, durationMs),\n phase: m.phase,\n }));\n\n // Enrich trust keyframes\n const trustKeyframes: VizTrustKeyframe[] = raw.trustKeyframes.map((k) => ({\n normalizedTime: normalize(k.timestamp, start, durationMs),\n level: k.level,\n label: k.label,\n }));\n\n // Editorial narration has pre-baked normalizedTime values.\n // If we extended durationMs (data lacked tail padding), rescale them\n // so they stay consistent with the new normalizedTime space.\n const durationChanged = durationMs !== raw.timeRange.durationMs;\n const editorialNarration =\n durationChanged && raw.editorialNarration\n ? raw.editorialNarration.map((entry) => ({\n ...entry,\n normalizedTime: entry.normalizedTime * (raw.timeRange.durationMs / durationMs),\n }))\n : (raw.editorialNarration ?? []);\n\n return {\n projectTitle: raw.projectTitle ?? 'miriad-viz',\n agents,\n events,\n phases,\n quotes,\n milestones,\n trustKeyframes,\n editorialNarration,\n timeRange: { start, end, durationMs },\n stats: raw.stats,\n };\n}\n","/**\n * Band bar sizing — compute peak row counts for artifact and PR bands.\n *\n * Peak rows are computed once from the full event list at init time.\n * This ensures bar heights stay constant during playback (no jarring resizes).\n *\n * Pure functions — no React, no Three.js, fully testable.\n */\n\nimport type { VizEvent } from '../types/viz-data.js';\n\n/** Gap between rows as a ratio of bar height (0.2 = 20% gap) */\nconst GAP_RATIO = 0.2;\n\n/** Minimum rows to assume — prevents bars from being comically tall with sparse data */\nconst MIN_ROWS = 3;\n\n/**\n * Compute peak concurrent artifact rows across the full timeline.\n *\n * Artifacts are point events with a small fixed width. We simulate greedy\n * row packing in normalized time [0,1] to find the maximum row count.\n */\nexport function computeArtifactPeakRows(events: VizEvent[]): number {\n // Collect all artifact normalized times\n const times: number[] = [];\n for (const e of events) {\n if (e.type === 'artifact') {\n times.push(e.normalizedTime);\n }\n }\n if (times.length === 0) return MIN_ROWS;\n\n times.sort((a, b) => a - b);\n\n // Simulate row packing with a small fixed width in normalized space\n // MIN_BAR_WIDTH in world units ≈ 0.02, timeline width ≈ 8-10 units\n // So normalized width ≈ 0.002-0.003. Use 0.003 as conservative estimate.\n const barWidth = 0.003;\n const gap = 0.001;\n\n const rows: number[] = []; // tracks right edge of last bar in each row\n let maxRows = 0;\n\n for (const t of times) {\n let placed = false;\n for (let r = 0; r < rows.length; r++) {\n if (rows[r] <= t) {\n rows[r] = t + barWidth + gap;\n placed = true;\n break;\n }\n }\n if (!placed) {\n rows.push(t + barWidth + gap);\n }\n if (rows.length > maxRows) {\n maxRows = rows.length;\n }\n }\n\n return Math.max(MIN_ROWS, maxRows);\n}\n\n/**\n * Compute peak concurrent PR rows across the full timeline.\n *\n * PRs are duration bars (opened → merged). We find the maximum number\n * of overlapping PR intervals at any point.\n */\nexport function computePRPeakRows(events: VizEvent[]): number {\n // Build intervals: [openedNormalized, mergedNormalized]\n const created = new Map<number, number>(); // prNumber → normalizedTime\n const merged = new Map<number, number>(); // prNumber → normalizedTime\n\n for (const e of events) {\n const prNumber = e.metadata.prNumber as number | undefined;\n if (prNumber === undefined) continue;\n\n if (e.type === 'pr_created') {\n created.set(prNumber, e.normalizedTime);\n } else if (e.type === 'pr_merged') {\n merged.set(prNumber, e.normalizedTime);\n }\n }\n\n if (created.size === 0) return MIN_ROWS;\n\n // Build sorted intervals\n const intervals: { open: number; close: number }[] = [];\n for (const [prNumber, openTime] of created) {\n const closeTime = merged.get(prNumber) ?? 1.0; // still open → extends to end\n intervals.push({ open: openTime, close: closeTime });\n }\n intervals.sort((a, b) => a.open - b.open);\n\n // Simulate greedy row packing (same algorithm as PRBand.packPRRows)\n const rows: number[] = [];\n let maxRows = 0;\n\n for (const iv of intervals) {\n let placed = false;\n for (let r = 0; r < rows.length; r++) {\n if (rows[r] <= iv.open) {\n rows[r] = iv.close + 0.001;\n placed = true;\n break;\n }\n }\n if (!placed) {\n rows.push(iv.close + 0.001);\n }\n if (rows.length > maxRows) {\n maxRows = rows.length;\n }\n }\n\n return Math.max(MIN_ROWS, maxRows);\n}\n\n/**\n * Derive bar height from band height and peak row count.\n *\n * Band labels are in dedicated rows above bands, so full band height\n * is usable for content. Divides space among rows with gaps.\n *\n * At peak density, bars fill the band. At lower density, bars are the\n * same size with empty space (showing relative density — correct behavior).\n */\nexport function deriveBarHeight(bandHeight: number, maxRows: number): number {\n return bandHeight / (maxRows * (1 + GAP_RATIO));\n}\n"]}