@liustack/pptfast 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1934,6 +1934,11 @@ var ComponentSchema = z.discriminatedUnion("type", [
1934
1934
  chart_type: z.enum(["bar", "line", "pie", "funnel", "dumbbell"]),
1935
1935
  direction: z.enum(["horizontal", "vertical"]).optional(),
1936
1936
  style: z.enum(["donut"]).optional(),
1937
+ /** Renders only for `chart_type: "bar"` (either direction) and
1938
+ * `"line"` — a cartesian plot box with a real category/value axis pair
1939
+ * to title and grid against. Ignored (schema-legal, silently dropped
1940
+ * at render, warn-severity `chart_axes_ignored` validate finding) on
1941
+ * `pie`/`funnel`/`dumbbell`, which have no such plot box. */
1937
1942
  axes: z.object({
1938
1943
  x_title: z.string().optional(),
1939
1944
  y_title: z.string().optional(),
@@ -3844,7 +3849,7 @@ function normalizeComponentAliases(input) {
3844
3849
  }
3845
3850
 
3846
3851
  // src/pptx/generate.ts
3847
- import JSZip5 from "jszip";
3852
+ import JSZip6 from "jszip";
3848
3853
 
3849
3854
  // src/platform/inline-assets.ts
3850
3855
  async function responseToDataUrl(resp) {
@@ -4374,9 +4379,9 @@ function renderOps(slide, ops, slideIndex) {
4374
4379
  var SLIDE_PART_RE2 = /^ppt\/slides\/slide\d+\.xml$/;
4375
4380
  var SOLID_FILL_RE = /<a:solidFill>.*?<\/a:solidFill>/s;
4376
4381
  async function applyGradientFills(pptx, patches) {
4377
- const PPTX_MIME3 = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
4382
+ const PPTX_MIME4 = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
4378
4383
  if (patches.length === 0) {
4379
- return pptx instanceof Blob ? pptx : new Blob([pptx], { type: PPTX_MIME3 });
4384
+ return pptx instanceof Blob ? pptx : new Blob([pptx], { type: PPTX_MIME4 });
4380
4385
  }
4381
4386
  const zip = await JSZip2.loadAsync(pptx instanceof Blob ? await pptx.arrayBuffer() : pptx);
4382
4387
  const slidePaths = Object.keys(zip.files).filter(
@@ -4419,7 +4424,7 @@ async function applyGradientFills(pptx, patches) {
4419
4424
  );
4420
4425
  }
4421
4426
  const ab = await zip.generateAsync({ type: "arraybuffer", compression: "DEFLATE" });
4422
- return new Blob([ab], { type: PPTX_MIME3 });
4427
+ return new Blob([ab], { type: PPTX_MIME4 });
4423
4428
  }
4424
4429
 
4425
4430
  // src/svg/render-slide.tsx
@@ -4535,16 +4540,23 @@ function extractStroke(el) {
4535
4540
  function num3(el, name) {
4536
4541
  return parseFloat(el.getAttribute(name) ?? "0") || 0;
4537
4542
  }
4543
+ function floorAxis(origin, extent) {
4544
+ const lo = Math.min(origin, origin + extent);
4545
+ const hi = Math.max(Math.max(origin, origin + extent), lo + 0.75);
4546
+ return { origin: lo, extent: hi - lo };
4547
+ }
4538
4548
  function rectToOp(el, gradients) {
4539
4549
  const rx = num3(el, "rx");
4550
+ const xAxis = floorAxis(num3(el, "x"), num3(el, "width"));
4551
+ const yAxis = floorAxis(num3(el, "y"), num3(el, "height"));
4540
4552
  const op = {
4541
4553
  kind: "shape",
4542
4554
  text: "",
4543
4555
  shape: rx > 0 ? "roundRect" : "rect",
4544
- x: pxToIn(num3(el, "x")),
4545
- y: pxToIn(num3(el, "y")),
4546
- w: pxToIn(num3(el, "width")),
4547
- h: pxToIn(num3(el, "height"))
4556
+ x: pxToIn(xAxis.origin),
4557
+ y: pxToIn(yAxis.origin),
4558
+ w: pxToIn(xAxis.extent),
4559
+ h: pxToIn(yAxis.extent)
4548
4560
  };
4549
4561
  if (rx > 0) op.rectRadius = pxToIn(rx);
4550
4562
  applyFill(op, el, gradients);
@@ -5367,6 +5379,16 @@ function resolveFontFace(stack, role) {
5367
5379
  }
5368
5380
  return ROLE_DEFAULT[role];
5369
5381
  }
5382
+ var CJK_SAFE_FACES = new Set(
5383
+ ["Microsoft YaHei", "\u5FAE\u8F6F\u96C5\u9ED1", "SimSun", "\u5B8B\u4F53", "SimHei", "\u9ED1\u4F53", "KaiTi", "\u6977\u4F53", "FangSong", "\u4EFF\u5B8B"].map(
5384
+ (f) => f.toLowerCase()
5385
+ )
5386
+ );
5387
+ var EA_FALLBACK_FACE = ROLE_DEFAULT.heading;
5388
+ function eaFontFaceFor(latinFace) {
5389
+ const name = latinFace.replace(/['"]/g, "").trim();
5390
+ return CJK_SAFE_FACES.has(name.toLowerCase()) ? name : EA_FALLBACK_FACE;
5391
+ }
5370
5392
  var SERIF_SAFE_FACES = new Set(
5371
5393
  ["Georgia", "Times New Roman", "Cambria", "SimSun", "\u5B8B\u4F53", "FangSong", "\u4EFF\u5B8B", "KaiTi", "\u6977\u4F53"].map(
5372
5394
  (f) => f.toLowerCase()
@@ -5382,6 +5404,9 @@ function resolveFontStack(stack, role) {
5382
5404
  const fallback = role === "mono" ? PREVIEW_FALLBACK.mono : SERIF_SAFE_FACES.has(face.toLowerCase()) ? PREVIEW_FALLBACK.serif : PREVIEW_FALLBACK.sans;
5383
5405
  return `${face}, ${fallback}`;
5384
5406
  }
5407
+ function isMonoFontFamily(fontFamily) {
5408
+ return fontFamily.endsWith(`, ${PREVIEW_FALLBACK.mono}`);
5409
+ }
5385
5410
 
5386
5411
  // src/svg/gradient-bands.ts
5387
5412
  function parseHex(hex) {
@@ -5789,6 +5814,13 @@ function measureTextUnits(text) {
5789
5814
  return sum + 0.46;
5790
5815
  }, 0);
5791
5816
  }
5817
+ var MONO_ADVANCE_EM = 1126 / 2048;
5818
+ function measureMonoTextUnits(text) {
5819
+ return Array.from(text).reduce((sum, char) => {
5820
+ if (WIDE_CHAR_RE.test(char)) return sum + 1;
5821
+ return sum + MONO_ADVANCE_EM;
5822
+ }, 0);
5823
+ }
5792
5824
  function splitLongToken(token, maxUnits) {
5793
5825
  const chunks = [];
5794
5826
  let current = "";
@@ -5865,6 +5897,19 @@ function truncateToUnits(text, maxUnits) {
5865
5897
  }
5866
5898
  return `${out}\u2026`;
5867
5899
  }
5900
+ function truncateToMonoUnits(text, maxUnits) {
5901
+ if (measureMonoTextUnits(text) <= maxUnits) return text;
5902
+ const budget = maxUnits - 1;
5903
+ let out = "";
5904
+ for (const ch of Array.from(text)) {
5905
+ if (measureMonoTextUnits(out + ch) > budget) break;
5906
+ out += ch;
5907
+ }
5908
+ if (out === "") {
5909
+ return measureMonoTextUnits("\u2026") > maxUnits ? "" : "\u2026";
5910
+ }
5911
+ return `${out}\u2026`;
5912
+ }
5868
5913
  function fitSvgLine(text, opts) {
5869
5914
  const minFontSize = opts.minFontSize ?? 12;
5870
5915
  const letterSpacing = opts.letterSpacing ?? 0;
@@ -5886,7 +5931,7 @@ function layoutSvgText(text, options) {
5886
5931
  const maxLines = options.maxLines ?? 2;
5887
5932
  const lineHeightRatio = options.lineHeightRatio ?? 1.08;
5888
5933
  if (!content) {
5889
- return { lines: [], fontSize: options.fontSize, lineHeight: 0 };
5934
+ return { lines: [], fontSize: options.fontSize, lineHeight: 0, truncated: false };
5890
5935
  }
5891
5936
  const baseUnits = options.maxWidth / options.fontSize;
5892
5937
  let maxUnits = baseUnits;
@@ -5913,7 +5958,8 @@ function layoutSvgText(text, options) {
5913
5958
  return {
5914
5959
  lines,
5915
5960
  fontSize,
5916
- lineHeight: Math.round(fontSize * lineHeightRatio)
5961
+ lineHeight: Math.round(fontSize * lineHeightRatio),
5962
+ truncated: false
5917
5963
  };
5918
5964
  }
5919
5965
 
@@ -6075,21 +6121,30 @@ function layoutItems(component, w, baseFontSize) {
6075
6121
  const indent = style === "default" ? TEXT_INDENT : 0;
6076
6122
  const maxWidth = Math.max(60, w - indent);
6077
6123
  const prefixes = component.items.map((_, i) => itemPrefix(style, i));
6078
- const texts = component.items.map((item, i) => `${prefixes[i]}${stripEmphasis(item)}`);
6079
- const layouts = texts.map((t) => layoutSvgText(t, { maxWidth, fontSize: baseFontSize, maxLines: 2 }));
6124
+ const prefixUnits = prefixes.map((p) => measureTextUnits(p));
6125
+ const strippedTexts = component.items.map((item) => stripEmphasis(item));
6126
+ const contentMaxWidth = (fontSize2, i) => Math.max(1, maxWidth - prefixUnits[i] * fontSize2);
6127
+ const layouts = strippedTexts.map(
6128
+ (t, i) => layoutSvgText(t, { maxWidth: contentMaxWidth(baseFontSize, i), fontSize: baseFontSize, maxLines: 2 })
6129
+ );
6080
6130
  const fontSize = Math.max(MIN_FONT, Math.min(...layouts.map((l) => l.fontSize), baseFontSize));
6081
6131
  const lineHeight = Math.round(fontSize * 1.4);
6082
- const relaid = texts.map((t) => layoutSvgText(t, { maxWidth, fontSize, maxLines: 2 }));
6083
- const maxUnits = maxWidth / fontSize;
6132
+ const relaid = strippedTexts.map(
6133
+ (t, i) => layoutSvgText(t, { maxWidth: contentMaxWidth(fontSize, i), fontSize, maxLines: 2 })
6134
+ );
6084
6135
  let y = Math.round(fontSize * 1.1);
6085
6136
  const dividers = [];
6086
6137
  const items = relaid.map((l, i) => {
6087
- const segments = prefixes[i] ? [{ text: prefixes[i], emphasized: false }, ...parseEmphasis(component.items[i])] : parseEmphasis(component.items[i]);
6138
+ const segments = parseEmphasis(component.items[i]);
6088
6139
  const wrappedLineSegments = sliceEmphasisForLines(segments, l.lines);
6140
+ const maxUnits = contentMaxWidth(fontSize, i) / fontSize;
6089
6141
  const lineSegments = wrappedLineSegments.map((segs) => truncateEmphasisSegments(segs, maxUnits));
6090
6142
  const lineTruncated = wrappedLineSegments.map(
6091
6143
  (before, li) => before.map((s) => s.text).join("") !== lineSegments[li].map((s) => s.text).join("")
6092
6144
  );
6145
+ if (prefixes[i]) {
6146
+ lineSegments[0] = lineSegments.length > 0 ? [{ text: prefixes[i], emphasized: false }, ...lineSegments[0]] : [{ text: prefixes[i], emphasized: false }];
6147
+ }
6093
6148
  const lines = lineSegments.map((segs) => segs.map((s) => s.text).join(""));
6094
6149
  const item = { lines, lineSegments, lineTruncated, firstLineY: y };
6095
6150
  const gapAfter = style === "divided" ? Math.round(fontSize * 1.9) : ITEM_GAP;
@@ -6415,10 +6470,10 @@ var TEXT_COLOR = "#D4D4D4";
6415
6470
  var LINE_NUM_COLOR = "#6A737D";
6416
6471
  var BORDER_RADIUS = 6;
6417
6472
  var LINE_HEIGHT_RATIO = 1.45;
6418
- var MONO_WIDTH_SAFETY = 0.9;
6473
+ var MONO_WIDTH_SAFETY = 1;
6419
6474
  function resolveLayout(lines, w) {
6420
6475
  const contentW = (w - 2 * PADDING - LINE_NUM_COL) * MONO_WIDTH_SAFETY;
6421
- const longestUnits = Math.max(...lines.map(measureTextUnits), 0);
6476
+ const longestUnits = Math.max(...lines.map(measureMonoTextUnits), 0);
6422
6477
  let fontSize = BASE_FONT_SIZE;
6423
6478
  let lineHeight = BASE_LINE_HEIGHT;
6424
6479
  if (longestUnits * fontSize > contentW && longestUnits > 0) {
@@ -6427,7 +6482,7 @@ function resolveLayout(lines, w) {
6427
6482
  }
6428
6483
  const maxUnitsAtFloor = contentW / fontSize;
6429
6484
  const renderLines = lines.map(
6430
- (line) => measureTextUnits(line) > maxUnitsAtFloor ? truncateToUnits(line, maxUnitsAtFloor) : line
6485
+ (line) => measureMonoTextUnits(line) > maxUnitsAtFloor ? truncateToMonoUnits(line, maxUnitsAtFloor) : line
6431
6486
  );
6432
6487
  const textStartX = PADDING + LINE_NUM_COL;
6433
6488
  const height = lines.length * lineHeight + 2 * PADDING;
@@ -7047,6 +7102,10 @@ var LABEL_FONT_SIZE = 11;
7047
7102
  var LABEL_MIN_FONT_SIZE = 8;
7048
7103
  var LABEL_TOP_PAD = 14;
7049
7104
  var LABEL_BOTTOM_PAD = 18;
7105
+ var MAX_CHART_GEOMETRY_PX = 4800;
7106
+ function clampChartExtent(px) {
7107
+ return Math.max(-MAX_CHART_GEOMETRY_PX, Math.min(MAX_CHART_GEOMETRY_PX, px));
7108
+ }
7050
7109
  var BAR_GRADIENT_SHADE_FACTOR = 0.7;
7051
7110
  var GRIDLINE_COUNT = 3;
7052
7111
  var ENDPOINT_DOT_R = 4;
@@ -7101,7 +7160,27 @@ function renderGridlines(x0, w, plotTop, plotH, mutedColor) {
7101
7160
  i
7102
7161
  )) });
7103
7162
  }
7104
- function renderBar(series, _palette, x0, y0, w, h, mutedColor, textColor, accentColor2) {
7163
+ function renderGridlinesVertical(y0, h, plotX, plotW, mutedColor) {
7164
+ const xs = [];
7165
+ for (let i = 1; i <= GRIDLINE_COUNT; i++) {
7166
+ const x = plotX + plotW * i / (GRIDLINE_COUNT + 1);
7167
+ if (Math.abs(x - plotX) > 0.01) xs.push(x);
7168
+ }
7169
+ return /* @__PURE__ */ jsx14(Fragment7, { children: xs.map((x, i) => /* @__PURE__ */ jsx14(
7170
+ "line",
7171
+ {
7172
+ x1: x,
7173
+ y1: y0,
7174
+ x2: x,
7175
+ y2: y0 + h,
7176
+ stroke: mutedColor,
7177
+ strokeOpacity: 0.1,
7178
+ strokeWidth: 1
7179
+ },
7180
+ i
7181
+ )) });
7182
+ }
7183
+ function renderBar(series, _palette, x0, y0, w, h, mutedColor, textColor, accentColor2, showGrid = true) {
7105
7184
  const all = series.flatMap((s) => s.data.map((d) => d.y));
7106
7185
  const max = Math.max(...all, 1);
7107
7186
  const points = series[0]?.data ?? [];
@@ -7115,9 +7194,9 @@ function renderBar(series, _palette, x0, y0, w, h, mutedColor, textColor, accent
7115
7194
  /* @__PURE__ */ jsx14("stop", { offset: "0%", stopColor: accentColor2 }),
7116
7195
  /* @__PURE__ */ jsx14("stop", { offset: "100%", stopColor: gradientShade })
7117
7196
  ] }) }),
7118
- renderGridlines(x0, w, plotTop, plotH, mutedColor),
7197
+ showGrid && renderGridlines(x0, w, plotTop, plotH, mutedColor),
7119
7198
  points.map((d, i) => {
7120
- const barH = d.y / max * plotH;
7199
+ const barH = clampChartExtent(d.y / max * plotH);
7121
7200
  const barX = x0 + i * groupW + 4;
7122
7201
  const barW = groupW - 8;
7123
7202
  const barY = plotTop + plotH - barH;
@@ -7168,19 +7247,19 @@ function renderBar(series, _palette, x0, y0, w, h, mutedColor, textColor, accent
7168
7247
  })
7169
7248
  ] });
7170
7249
  }
7171
- function renderLine(series, palette, x0, y0, w, h, mutedColor, textColor, accentColor2) {
7250
+ function renderLine(series, palette, x0, y0, w, h, mutedColor, textColor, accentColor2, showGrid = true) {
7172
7251
  const plotTop = y0 + LABEL_TOP_PAD;
7173
7252
  const plotH = Math.max(0, h - LABEL_TOP_PAD - LABEL_BOTTOM_PAD);
7174
7253
  const baselineY2 = plotTop + plotH;
7175
7254
  const categoryPoints = series[0]?.data ?? [];
7176
7255
  const categoryMaxWidth = w / Math.max(categoryPoints.length - 1, 1);
7177
7256
  return /* @__PURE__ */ jsxs12(Fragment7, { children: [
7178
- renderGridlines(x0, w, plotTop, plotH, mutedColor),
7257
+ showGrid && renderGridlines(x0, w, plotTop, plotH, mutedColor),
7179
7258
  series.map((s, sIdx) => {
7180
7259
  const max = Math.max(...s.data.map((d) => d.y), 1);
7181
7260
  const coords = s.data.map((d, i) => ({
7182
7261
  x: x0 + i / Math.max(s.data.length - 1, 1) * w,
7183
- y: plotTop + plotH - d.y / max * plotH,
7262
+ y: plotTop + plotH - clampChartExtent(d.y / max * plotH),
7184
7263
  y_value: d.y
7185
7264
  }));
7186
7265
  const pts = coords.map((c) => `${c.x},${c.y}`).join(" ");
@@ -7274,7 +7353,7 @@ function renderLine(series, palette, x0, y0, w, h, mutedColor, textColor, accent
7274
7353
  })
7275
7354
  ] });
7276
7355
  }
7277
- function renderPie(series, palette, x0, y0, w, h, _mutedColor, _textColor, _accentColor) {
7356
+ function renderPie(series, palette, x0, y0, w, h, _mutedColor, _textColor, _accentColor, _showGrid) {
7278
7357
  const data = series[0]?.data ?? [];
7279
7358
  const total = data.reduce((s, d) => s + d.y, 0);
7280
7359
  if (total === 0) return /* @__PURE__ */ jsx14(Fragment7, {});
@@ -7301,13 +7380,13 @@ function renderPie(series, palette, x0, y0, w, h, _mutedColor, _textColor, _acce
7301
7380
  );
7302
7381
  }) });
7303
7382
  }
7304
- function renderFunnel(series, palette, x0, y0, w, h, _mutedColor, _textColor, _accentColor) {
7383
+ function renderFunnel(series, palette, x0, y0, w, h, _mutedColor, _textColor, _accentColor, _showGrid) {
7305
7384
  const data = series[0]?.data ?? [];
7306
7385
  const max = Math.max(...data.map((d) => d.y), 1);
7307
7386
  const stepH = h / Math.max(data.length, 1);
7308
7387
  return /* @__PURE__ */ jsx14(Fragment7, { children: data.map((d, i) => {
7309
7388
  const ratio = d.y / max;
7310
- const barW = w * ratio;
7389
+ const barW = clampChartExtent(w * ratio);
7311
7390
  const barX = x0 + (w - barW) / 2;
7312
7391
  return /* @__PURE__ */ jsx14(
7313
7392
  "rect",
@@ -7324,17 +7403,18 @@ function renderFunnel(series, palette, x0, y0, w, h, _mutedColor, _textColor, _a
7324
7403
  }
7325
7404
  var DUMBBELL_LABEL_W = 96;
7326
7405
  var DUMBBELL_DOT_R = 5;
7327
- function renderDumbbell(series, _palette, x0, y0, w, h, mutedColor, textColor, accentColor2) {
7406
+ function renderDumbbell(series, _palette, x0, y0, w, h, mutedColor, textColor, accentColor2, _showGrid) {
7328
7407
  const fromData = series[0]?.data ?? [];
7329
7408
  const toData = series[1]?.data ?? [];
7330
7409
  const rows = Math.min(fromData.length, toData.length);
7331
7410
  if (rows === 0) return /* @__PURE__ */ jsx14(Fragment7, {});
7332
7411
  const all = [...fromData, ...toData].map((d) => d.y);
7412
+ const min = Math.min(0, ...all);
7333
7413
  const max = Math.max(...all, 1);
7334
7414
  const plotX = x0 + DUMBBELL_LABEL_W + 12;
7335
7415
  const plotW = Math.max(1, w - DUMBBELL_LABEL_W - 12 - 56);
7336
7416
  const rowH = h / rows;
7337
- const vx = (v) => plotX + v / max * plotW;
7417
+ const vx = (v) => plotX + (v - min) / (max - min) * plotW;
7338
7418
  return /* @__PURE__ */ jsx14(Fragment7, { children: Array.from({ length: rows }, (_, i) => {
7339
7419
  const from = fromData[i];
7340
7420
  const to = toData[i];
@@ -7392,7 +7472,7 @@ function renderDumbbell(series, _palette, x0, y0, w, h, mutedColor, textColor, a
7392
7472
  }) });
7393
7473
  }
7394
7474
  var BAR_H_LABEL_W = 110;
7395
- function renderBarHorizontal(series, _palette, x0, y0, w, h, mutedColor, textColor, accentColor2) {
7475
+ function renderBarHorizontal(series, _palette, x0, y0, w, h, mutedColor, textColor, accentColor2, showGrid = false) {
7396
7476
  const points = series[0]?.data ?? [];
7397
7477
  if (points.length === 0) return /* @__PURE__ */ jsx14(Fragment7, {});
7398
7478
  const max = Math.max(...points.map((d) => d.y), 1);
@@ -7406,8 +7486,9 @@ function renderBarHorizontal(series, _palette, x0, y0, w, h, mutedColor, textCol
7406
7486
  /* @__PURE__ */ jsx14("stop", { offset: "0%", stopColor: gradientShade }),
7407
7487
  /* @__PURE__ */ jsx14("stop", { offset: "100%", stopColor: accentColor2 })
7408
7488
  ] }) }),
7489
+ showGrid && renderGridlinesVertical(y0, h, plotX, plotW, mutedColor),
7409
7490
  points.map((d, i) => {
7410
- const barW = d.y / max * plotW;
7491
+ const barW = clampChartExtent(d.y / max * plotW);
7411
7492
  const barY = y0 + i * rowH + 5;
7412
7493
  const barH = Math.max(4, rowH - 10);
7413
7494
  const isMax = d.y === max;
@@ -7459,7 +7540,7 @@ function renderBarHorizontal(series, _palette, x0, y0, w, h, mutedColor, textCol
7459
7540
  ] });
7460
7541
  }
7461
7542
  var DONUT_HOLE_RATIO = 0.62;
7462
- function renderDonut(series, palette, x0, y0, w, h, mutedColor, textColor, _accentColor) {
7543
+ function renderDonut(series, palette, x0, y0, w, h, mutedColor, textColor, _accentColor, _showGrid) {
7463
7544
  const data = series[0]?.data ?? [];
7464
7545
  const total = data.reduce((s, d) => s + d.y, 0);
7465
7546
  if (total === 0) return /* @__PURE__ */ jsx14(Fragment7, {});
@@ -7523,7 +7604,7 @@ function renderDonut(series, palette, x0, y0, w, h, mutedColor, textColor, _acce
7523
7604
  }
7524
7605
 
7525
7606
  // src/svg/components/chart.tsx
7526
- import { jsx as jsx15 } from "react/jsx-runtime";
7607
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
7527
7608
  var CHART_H = 240;
7528
7609
  var renderers = {
7529
7610
  bar: renderBar,
@@ -7541,30 +7622,93 @@ function resolveRenderer(component) {
7541
7622
  }
7542
7623
  return renderers[component.chart_type];
7543
7624
  }
7625
+ var AXES_APPLICABLE_TYPES = /* @__PURE__ */ new Set(["bar", "line"]);
7626
+ function axesApplicable(component) {
7627
+ return AXES_APPLICABLE_TYPES.has(component.chart_type);
7628
+ }
7629
+ var AXES_TITLE_SIZE = 11;
7630
+ var AXES_X_TITLE_H = 22;
7631
+ var AXES_Y_TITLE_BAND_W = 20;
7632
+ var AXES_Y_TITLE_GAP = 10;
7633
+ var AXES_Y_TITLE_W = AXES_Y_TITLE_BAND_W + AXES_Y_TITLE_GAP;
7634
+ var AXES_Y_CHAR_ADVANCE = AXES_TITLE_SIZE + 2;
7635
+ function maxYAxisChars(availH) {
7636
+ return Math.max(1, Math.floor((availH - AXES_TITLE_SIZE * 0.25) / AXES_Y_CHAR_ADVANCE));
7637
+ }
7638
+ function fitYAxisTitle(text, availH) {
7639
+ const chars = Array.from(text);
7640
+ if (chars.length === 0) return { chars, truncated: false };
7641
+ const maxChars = maxYAxisChars(availH);
7642
+ if (chars.length <= maxChars) return { chars, truncated: false };
7643
+ const kept = chars.slice(0, Math.max(0, maxChars - 1));
7644
+ return { chars: [...kept, "\u2026"], truncated: true };
7645
+ }
7544
7646
  var chart = {
7545
- measure() {
7546
- return CHART_H;
7647
+ measure(component) {
7648
+ const hasXTitle = axesApplicable(component) && !!component.axes?.x_title;
7649
+ return CHART_H + (hasXTitle ? AXES_X_TITLE_H : 0);
7547
7650
  },
7548
7651
  render(component, box, ctx) {
7549
7652
  const renderer = resolveRenderer(component);
7550
- return /* @__PURE__ */ jsx15("g", { transform: `translate(${box.x},${box.y})`, children: renderer(
7551
- component.series,
7552
- ctx.colors.chartPalette,
7553
- 0,
7554
- 0,
7555
- box.w,
7556
- CHART_H,
7557
- ctx.colors.muted,
7558
- ctx.colors.text,
7559
- ctx.colors.accent
7560
- ) });
7653
+ const axes = axesApplicable(component) ? component.axes : void 0;
7654
+ const hasXTitle = !!axes?.x_title;
7655
+ const hasYTitle = !!axes?.y_title;
7656
+ const yTitleW = hasYTitle ? AXES_Y_TITLE_W : 0;
7657
+ const plotW = box.w - yTitleW;
7658
+ const xTitleFit = hasXTitle ? fitSvgLine(axes.x_title, { maxWidth: plotW, fontSize: AXES_TITLE_SIZE, minFontSize: 9 }) : null;
7659
+ const yTitleFit = hasYTitle ? fitYAxisTitle(axes.y_title, CHART_H) : null;
7660
+ const yStackSpan = yTitleFit ? Math.max(0, yTitleFit.chars.length - 1) * AXES_Y_CHAR_ADVANCE : 0;
7661
+ const yFirstBaselineY = (CHART_H - yStackSpan) / 2;
7662
+ return /* @__PURE__ */ jsxs13("g", { transform: `translate(${box.x},${box.y})`, children: [
7663
+ renderer(
7664
+ component.series,
7665
+ ctx.colors.chartPalette,
7666
+ yTitleW,
7667
+ 0,
7668
+ plotW,
7669
+ CHART_H,
7670
+ ctx.colors.muted,
7671
+ ctx.colors.text,
7672
+ ctx.colors.accent,
7673
+ axes?.show_grid
7674
+ ),
7675
+ xTitleFit ? /* @__PURE__ */ jsx15(
7676
+ "text",
7677
+ {
7678
+ "data-truncated": xTitleFit.truncated ? "1" : void 0,
7679
+ x: yTitleW + plotW / 2,
7680
+ y: CHART_H + AXES_X_TITLE_H - 6,
7681
+ textAnchor: "middle",
7682
+ fontSize: xTitleFit.fontSize,
7683
+ fill: ctx.colors.muted,
7684
+ fontFamily: ctx.fonts.body,
7685
+ dominantBaseline: "alphabetic",
7686
+ children: xTitleFit.text
7687
+ }
7688
+ ) : null,
7689
+ yTitleFit ? yTitleFit.chars.map((chr, i) => /* @__PURE__ */ jsx15(
7690
+ "text",
7691
+ {
7692
+ "data-truncated": yTitleFit.truncated && i === yTitleFit.chars.length - 1 ? "1" : void 0,
7693
+ x: AXES_Y_TITLE_BAND_W / 2,
7694
+ y: yFirstBaselineY + i * AXES_Y_CHAR_ADVANCE,
7695
+ textAnchor: "middle",
7696
+ fontSize: AXES_TITLE_SIZE,
7697
+ fill: ctx.colors.muted,
7698
+ fontFamily: ctx.fonts.body,
7699
+ dominantBaseline: "alphabetic",
7700
+ children: chr
7701
+ },
7702
+ i
7703
+ )) : null
7704
+ ] });
7561
7705
  }
7562
7706
  };
7563
7707
 
7564
7708
  // src/svg/components/flowchart.tsx
7565
7709
  import { Fragment as Fragment8 } from "react";
7566
7710
  import dagre from "dagre";
7567
- import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
7711
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
7568
7712
  function toRankdir(d) {
7569
7713
  return d === "TD" ? "TB" : d;
7570
7714
  }
@@ -7758,10 +7902,10 @@ var flowchart = {
7758
7902
  const scaleX = scale;
7759
7903
  const scaleY = scale;
7760
7904
  const dx = Math.max(0, (box.w - layout.width * scale) / 2);
7761
- return /* @__PURE__ */ jsxs13("g", { transform: `translate(${box.x + dx},${box.y})`, children: [
7905
+ return /* @__PURE__ */ jsxs14("g", { transform: `translate(${box.x + dx},${box.y})`, children: [
7762
7906
  layout.edges.map((edge, i) => {
7763
7907
  const d = smoothEdgePath(edge.points, scaleX, scaleY);
7764
- return /* @__PURE__ */ jsxs13(Fragment8, { children: [
7908
+ return /* @__PURE__ */ jsxs14(Fragment8, { children: [
7765
7909
  /* @__PURE__ */ jsx16(
7766
7910
  "path",
7767
7911
  {
@@ -7792,7 +7936,7 @@ var flowchart = {
7792
7936
  const sharedFont = Math.min(...fits.map((f) => f.fontSize));
7793
7937
  const pitch = NODE_LINE_PITCH * scaleY;
7794
7938
  const firstLineY = ny + nh / 2 - (n.lines.length - 1) * pitch / 2;
7795
- return /* @__PURE__ */ jsxs13(
7939
+ return /* @__PURE__ */ jsxs14(
7796
7940
  "g",
7797
7941
  {
7798
7942
  "data-audit-box": `${box.x + dx + nx},${box.y + ny},${nw}`,
@@ -7841,7 +7985,7 @@ var flowchart = {
7841
7985
  layout.edges.map((edge, i) => {
7842
7986
  const label = computeEdgeLabel(edge, scale);
7843
7987
  if (!label) return null;
7844
- return /* @__PURE__ */ jsxs13(Fragment8, { children: [
7988
+ return /* @__PURE__ */ jsxs14(Fragment8, { children: [
7845
7989
  /* @__PURE__ */ jsx16(
7846
7990
  "rect",
7847
7991
  {
@@ -7875,7 +8019,7 @@ var flowchart = {
7875
8019
  };
7876
8020
 
7877
8021
  // src/svg/components/architecture.tsx
7878
- import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
8022
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
7879
8023
  var LAYER_H = 72;
7880
8024
  var GAP4 = 12;
7881
8025
  var TITLE_X = 16;
@@ -7905,7 +8049,7 @@ var architecture = {
7905
8049
  fontSize: ITEMS_FONT_SIZE,
7906
8050
  minFontSize: MIN_FONT_SIZE3
7907
8051
  });
7908
- return /* @__PURE__ */ jsxs14("g", { children: [
8052
+ return /* @__PURE__ */ jsxs15("g", { children: [
7909
8053
  /* @__PURE__ */ jsx17(
7910
8054
  "rect",
7911
8055
  {
@@ -7950,7 +8094,7 @@ var architecture = {
7950
8094
  };
7951
8095
 
7952
8096
  // src/svg/components/timeline.tsx
7953
- import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
8097
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
7954
8098
  var AXIS_Y = 100;
7955
8099
  var PAD2 = 20;
7956
8100
  var LABEL_GAP = 12;
@@ -8031,7 +8175,7 @@ function renderVertical(component, box, ctx) {
8031
8175
  }
8032
8176
  const axisTop = rowTops[0] + 8;
8033
8177
  const axisBottom = rowTops[rowTops.length - 1] + 8;
8034
- return /* @__PURE__ */ jsxs15("g", { transform: `translate(${box.x},${box.y})`, children: [
8178
+ return /* @__PURE__ */ jsxs16("g", { transform: `translate(${box.x},${box.y})`, children: [
8035
8179
  rows.length > 1 && /* @__PURE__ */ jsx18(
8036
8180
  "line",
8037
8181
  {
@@ -8053,7 +8197,7 @@ function renderVertical(component, box, ctx) {
8053
8197
  fontSize: 20,
8054
8198
  minFontSize: MIN_FONT_SIZE4
8055
8199
  });
8056
- return /* @__PURE__ */ jsxs15("g", { children: [
8200
+ return /* @__PURE__ */ jsxs16("g", { children: [
8057
8201
  /* @__PURE__ */ jsx18(
8058
8202
  "text",
8059
8203
  {
@@ -8123,7 +8267,7 @@ var timeline = {
8123
8267
  render(component, box, ctx) {
8124
8268
  if (component.layout === "vertical") return renderVertical(component, box, ctx);
8125
8269
  const rows = milestoneLayout(component, box.w);
8126
- return /* @__PURE__ */ jsxs15("g", { transform: `translate(${box.x},${box.y})`, children: [
8270
+ return /* @__PURE__ */ jsxs16("g", { transform: `translate(${box.x},${box.y})`, children: [
8127
8271
  /* @__PURE__ */ jsx18(
8128
8272
  "line",
8129
8273
  {
@@ -8142,7 +8286,7 @@ var timeline = {
8142
8286
  minFontSize: MIN_FONT_SIZE4
8143
8287
  });
8144
8288
  const descTop = TITLE_TOP + title.lines.length * title.lineHeight + 2;
8145
- return /* @__PURE__ */ jsxs15("g", { children: [
8289
+ return /* @__PURE__ */ jsxs16("g", { children: [
8146
8290
  /* @__PURE__ */ jsx18("circle", { cx: x, cy: AXIS_Y, r: 8, fill: ctx.colors.primary }),
8147
8291
  /* @__PURE__ */ jsx18(
8148
8292
  "text",
@@ -8195,7 +8339,7 @@ var timeline = {
8195
8339
 
8196
8340
  // src/svg/components/comparison.tsx
8197
8341
  import { Fragment as Fragment9 } from "react";
8198
- import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
8342
+ import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
8199
8343
  var ROW = 44;
8200
8344
  var MIN_COL_W = 80;
8201
8345
  var PAD_X = 12;
@@ -8304,7 +8448,7 @@ var comparison = {
8304
8448
  );
8305
8449
  const headerBaseline = Math.round(headerFontSize * 0.35);
8306
8450
  const cellBaseline = Math.round(cellFontSize * 0.35);
8307
- return /* @__PURE__ */ jsxs16("g", { transform: `translate(${box.x},${box.y})`, children: [
8451
+ return /* @__PURE__ */ jsxs17("g", { transform: `translate(${box.x},${box.y})`, children: [
8308
8452
  headers.map((title, c) => {
8309
8453
  if (!title) return null;
8310
8454
  const fitted = truncate(title, widths[c], headerFontSize);
@@ -8386,7 +8530,7 @@ var comparison = {
8386
8530
  };
8387
8531
 
8388
8532
  // src/svg/components/icon-cards.tsx
8389
- import { Fragment as Fragment10, jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
8533
+ import { Fragment as Fragment10, jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
8390
8534
  var GAP5 = 16;
8391
8535
  var ICON_SIZE2 = 24;
8392
8536
  var GAP_ICON_TITLE = 12;
@@ -8438,7 +8582,7 @@ function renderIconCardBody(item, box, ctx, opts = {}) {
8438
8582
  const titleTopY = box.y + iconSize + GAP_ICON_TITLE;
8439
8583
  const titleBaselineY = titleTopY + titleFontSize;
8440
8584
  const textTopY = titleTopY + titleLineHeight + GAP_TITLE_TEXT;
8441
- return /* @__PURE__ */ jsxs17(Fragment10, { children: [
8585
+ return /* @__PURE__ */ jsxs18(Fragment10, { children: [
8442
8586
  /* @__PURE__ */ jsx20(
8443
8587
  Icon,
8444
8588
  {
@@ -8505,7 +8649,7 @@ var iconCards = {
8505
8649
  return /* @__PURE__ */ jsx20("g", { transform: `translate(${box.x},${box.y})`, children: component.items.map((item, i) => {
8506
8650
  const cardX = i % cols * (cardW + GAP5);
8507
8651
  const cardY = Math.floor(i / cols) * (shellH + GAP5);
8508
- return /* @__PURE__ */ jsxs17("g", { "data-audit-box": `${box.x + cardX},${box.y + cardY},${cardW}`, children: [
8652
+ return /* @__PURE__ */ jsxs18("g", { "data-audit-box": `${box.x + cardX},${box.y + cardY},${cardW}`, children: [
8509
8653
  /* @__PURE__ */ jsx20(
8510
8654
  "rect",
8511
8655
  {
@@ -8540,7 +8684,7 @@ var iconCards = {
8540
8684
  };
8541
8685
 
8542
8686
  // src/svg/components/numbered-cards.tsx
8543
- import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
8687
+ import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
8544
8688
  var COL_GAP = 28;
8545
8689
  var ROW_GAP = 36;
8546
8690
  var INDENT = 22;
@@ -8603,7 +8747,7 @@ var numberedCards = {
8603
8747
  const numBaseline = cellY + NUM_SIZE;
8604
8748
  const titleBaseline = cellY + NUM_BLOCK_H + TITLE_SIZE2;
8605
8749
  const textTop = cellY + NUM_BLOCK_H + TITLE_BLOCK_H;
8606
- return /* @__PURE__ */ jsxs18("g", { children: [
8750
+ return /* @__PURE__ */ jsxs19("g", { children: [
8607
8751
  /* @__PURE__ */ jsx21(
8608
8752
  "line",
8609
8753
  {
@@ -8677,7 +8821,7 @@ var numberedCards = {
8677
8821
  };
8678
8822
 
8679
8823
  // src/svg/components/rings.tsx
8680
- import { jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
8824
+ import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
8681
8825
  var H_PER_RING = { 2: 300, 3: 340, 4: 380 };
8682
8826
  var PAD3 = 10;
8683
8827
  var LABEL_GAP2 = 40;
@@ -8703,7 +8847,7 @@ var rings = {
8703
8847
  render(component, box, ctx) {
8704
8848
  const { n, cx, cy, radii, textX: textX2, textW, h } = geometry(component, box.w);
8705
8849
  const rowStep = n > 1 ? (h - 2 * PAD3 - 56) / (n - 1) : 0;
8706
- return /* @__PURE__ */ jsxs19("g", { transform: `translate(${box.x},${box.y})`, children: [
8850
+ return /* @__PURE__ */ jsxs20("g", { transform: `translate(${box.x},${box.y})`, children: [
8707
8851
  [...component.items.keys()].reverse().map((idx) => {
8708
8852
  const r = radii[idx];
8709
8853
  if (idx === 0) {
@@ -8764,7 +8908,7 @@ var rings = {
8764
8908
  maxLines: 2,
8765
8909
  lineHeightRatio: 1.35
8766
8910
  }) : null;
8767
- return /* @__PURE__ */ jsxs19("g", { children: [
8911
+ return /* @__PURE__ */ jsxs20("g", { children: [
8768
8912
  /* @__PURE__ */ jsx22(
8769
8913
  "path",
8770
8914
  {
@@ -8810,7 +8954,7 @@ var rings = {
8810
8954
  };
8811
8955
 
8812
8956
  // src/svg/components/row-cards.tsx
8813
- import { jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
8957
+ import { jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
8814
8958
  var CARD_GAP = 14;
8815
8959
  var PAD_Y2 = 16;
8816
8960
  var NUM_CX = 46;
@@ -8863,7 +9007,7 @@ var rowCards = {
8863
9007
  }
8864
9008
  const hidden = component.items.length - visible;
8865
9009
  let cursor = 0;
8866
- return /* @__PURE__ */ jsxs20("g", { transform: `translate(${box.x},${box.y})`, children: [
9010
+ return /* @__PURE__ */ jsxs21("g", { transform: `translate(${box.x},${box.y})`, children: [
8867
9011
  component.items.slice(0, visible).map((item, i) => {
8868
9012
  const { title, text, sub, contentH, cardH } = layouts[i];
8869
9013
  const shellH = cardH + perCardGrow;
@@ -8874,7 +9018,7 @@ var rowCards = {
8874
9018
  const numCy = cardY + shellH / 2;
8875
9019
  const titleBaseline = contentTop + TITLE_SIZE3;
8876
9020
  const textTop = contentTop + TITLE_LH;
8877
- return /* @__PURE__ */ jsxs20("g", { "data-audit-box": `${box.x},${box.y + cardY},${box.w}`, children: [
9021
+ return /* @__PURE__ */ jsxs21("g", { "data-audit-box": `${box.x},${box.y + cardY},${box.w}`, children: [
8878
9022
  /* @__PURE__ */ jsx23(
8879
9023
  "rect",
8880
9024
  {
@@ -8985,7 +9129,7 @@ var rowCards = {
8985
9129
  };
8986
9130
 
8987
9131
  // src/svg/components/steps.tsx
8988
- import { Fragment as Fragment11, jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
9132
+ import { Fragment as Fragment11, jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
8989
9133
  var PAD_X3 = 24;
8990
9134
  var PAD_TOP2 = 24;
8991
9135
  var PAD_BOTTOM2 = 20;
@@ -9033,7 +9177,7 @@ function layoutStepItem(item, contentW) {
9033
9177
  return { title, text };
9034
9178
  }
9035
9179
  function renderBadge(cx, cy, n, ctx) {
9036
- return /* @__PURE__ */ jsxs21(Fragment11, { children: [
9180
+ return /* @__PURE__ */ jsxs22(Fragment11, { children: [
9037
9181
  /* @__PURE__ */ jsx24("circle", { cx, cy, r: BADGE_R, fill: ctx.colors.primary }),
9038
9182
  /* @__PURE__ */ jsx24(
9039
9183
  "text",
@@ -9062,7 +9206,7 @@ function renderStepCardBody(item, index, box, ctx) {
9062
9206
  const titleTopY = box.y + BADGE_BAND + GAP_BADGE_TITLE;
9063
9207
  const titleBaselineY = titleTopY + TITLE_FONT_SIZE3;
9064
9208
  const textTopY = titleTopY + TITLE_LINE_HEIGHT + GAP_TITLE_TEXT2;
9065
- return /* @__PURE__ */ jsxs21(Fragment11, { children: [
9209
+ return /* @__PURE__ */ jsxs22(Fragment11, { children: [
9066
9210
  renderBadge(badgeCx, badgeCy, index + 1, ctx),
9067
9211
  /* @__PURE__ */ jsx24(
9068
9212
  "text",
@@ -9105,7 +9249,7 @@ function cardGeometry2(component, w) {
9105
9249
  function renderArrow(x, y, color) {
9106
9250
  const tipX = x + ARROW_LEN;
9107
9251
  const baseX = tipX - ARROW_HEAD_LEN;
9108
- return /* @__PURE__ */ jsxs21(Fragment11, { children: [
9252
+ return /* @__PURE__ */ jsxs22(Fragment11, { children: [
9109
9253
  /* @__PURE__ */ jsx24("line", { x1: x, y1: y, x2: baseX, y2: y, stroke: color, strokeWidth: ARROW_STROKE_W }),
9110
9254
  /* @__PURE__ */ jsx24(
9111
9255
  "polygon",
@@ -9133,7 +9277,7 @@ function renderVertical2(component, box, ctx) {
9133
9277
  const { contentW, rowH } = verticalRowGeometry(component, box.w);
9134
9278
  const badgeCx = PAD_X3 + BADGE_R;
9135
9279
  const badgeCy = (i) => i * rowH + TITLE_LINE_HEIGHT / 2;
9136
- return /* @__PURE__ */ jsxs21("g", { transform: `translate(${box.x},${box.y})`, children: [
9280
+ return /* @__PURE__ */ jsxs22("g", { transform: `translate(${box.x},${box.y})`, children: [
9137
9281
  component.items.slice(1).map((_, i) => /* @__PURE__ */ jsx24(
9138
9282
  "line",
9139
9283
  {
@@ -9151,7 +9295,7 @@ function renderVertical2(component, box, ctx) {
9151
9295
  const { title, text } = layoutStepItem(item, contentW);
9152
9296
  const titleBaselineY = rowTop + TITLE_FONT_SIZE3;
9153
9297
  const textTopY = rowTop + TITLE_LINE_HEIGHT + GAP_TITLE_TEXT_VERTICAL;
9154
- return /* @__PURE__ */ jsxs21("g", { "data-audit-box": `${box.x},${box.y + rowTop},${box.w}`, children: [
9298
+ return /* @__PURE__ */ jsxs22("g", { "data-audit-box": `${box.x},${box.y + rowTop},${box.w}`, children: [
9155
9299
  renderBadge(badgeCx, badgeCy(i), i + 1, ctx),
9156
9300
  /* @__PURE__ */ jsx24(
9157
9301
  "text",
@@ -9198,10 +9342,10 @@ var steps = {
9198
9342
  return renderVertical2(component, box, ctx);
9199
9343
  }
9200
9344
  const { cardW, contentW, cardH } = cardGeometry2(component, box.w);
9201
- return /* @__PURE__ */ jsxs21("g", { transform: `translate(${box.x},${box.y})`, children: [
9345
+ return /* @__PURE__ */ jsxs22("g", { transform: `translate(${box.x},${box.y})`, children: [
9202
9346
  component.items.map((item, i) => {
9203
9347
  const cardX = i * (cardW + GAP6);
9204
- return /* @__PURE__ */ jsxs21("g", { "data-audit-box": `${box.x + cardX},${box.y},${cardW}`, children: [
9348
+ return /* @__PURE__ */ jsxs22("g", { "data-audit-box": `${box.x + cardX},${box.y},${cardW}`, children: [
9205
9349
  /* @__PURE__ */ jsx24(
9206
9350
  "rect",
9207
9351
  {
@@ -9227,7 +9371,7 @@ var steps = {
9227
9371
  };
9228
9372
 
9229
9373
  // src/svg/components/roadmap.tsx
9230
- import { jsx as jsx25, jsxs as jsxs22 } from "react/jsx-runtime";
9374
+ import { jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
9231
9375
  var GAP7 = 24;
9232
9376
  var PAD_X4 = 22;
9233
9377
  var PAD_BOTTOM3 = 18;
@@ -9295,7 +9439,7 @@ function renderCard(layout, index, x, y, cardW, cardH, ctx) {
9295
9439
  const num8 = String(index + 1).padStart(2, "0");
9296
9440
  const titleBaseline = y + BADGE_TOP + BADGE_R2 * 2 + GAP_BADGE_TITLE2 + TITLE_SIZE4;
9297
9441
  let rowY = titleBaseline + GAP_TITLE_ROWS;
9298
- return /* @__PURE__ */ jsxs22("g", { children: [
9442
+ return /* @__PURE__ */ jsxs23("g", { children: [
9299
9443
  /* @__PURE__ */ jsx25(
9300
9444
  "rect",
9301
9445
  {
@@ -9366,7 +9510,7 @@ function renderCard(layout, index, x, y, cardW, cardH, ctx) {
9366
9510
  layout.rows.map((row, ri) => {
9367
9511
  const rowTop = rowY;
9368
9512
  rowY += row.height + ROW_GAP3;
9369
- return /* @__PURE__ */ jsxs22("g", { children: [
9513
+ return /* @__PURE__ */ jsxs23("g", { children: [
9370
9514
  /* @__PURE__ */ jsx25(
9371
9515
  "text",
9372
9516
  {
@@ -9433,7 +9577,7 @@ function parseHex2(h) {
9433
9577
  }
9434
9578
 
9435
9579
  // src/svg/components/matrix.tsx
9436
- import { jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
9580
+ import { jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
9437
9581
  var CARD_GAP2 = 16;
9438
9582
  var PAD_X5 = 18;
9439
9583
  var PAD_TOP3 = 16;
@@ -9457,6 +9601,26 @@ function toneFill(tone, ctx) {
9457
9601
  return mixHex(ctx.colors.surface, ctx.colors.muted, 0.08);
9458
9602
  }
9459
9603
  }
9604
+ var Y_TITLE_START_Y = 20;
9605
+ var Y_TITLE_CHAR_ADVANCE = AXIS_SIZE + 2;
9606
+ function yTitleStackHeight(charCount) {
9607
+ if (charCount <= 0) return 0;
9608
+ return Y_TITLE_START_Y + (charCount - 1) * Y_TITLE_CHAR_ADVANCE + AXIS_SIZE * 0.25;
9609
+ }
9610
+ function maxYTitleChars(availH) {
9611
+ return Math.max(
9612
+ 1,
9613
+ Math.floor((availH - Y_TITLE_START_Y - AXIS_SIZE * 0.25) / Y_TITLE_CHAR_ADVANCE) + 1
9614
+ );
9615
+ }
9616
+ function fitYTitleStack(text, availH) {
9617
+ const chars = Array.from(text);
9618
+ if (chars.length === 0) return { chars, truncated: false };
9619
+ const maxChars = maxYTitleChars(availH);
9620
+ if (chars.length <= maxChars) return { chars, truncated: false };
9621
+ const kept = chars.slice(0, Math.max(0, maxChars - 1));
9622
+ return { chars: [...kept, "\u2026"], truncated: true };
9623
+ }
9460
9624
  function cellLayout2(item, cardW) {
9461
9625
  const contentW = cardW - PAD_X5 * 2;
9462
9626
  const title = fitSvgLine(item.title, { maxWidth: contentW, fontSize: TITLE_SIZE5, minFontSize: 12 });
@@ -9475,38 +9639,48 @@ function gridGeom(component, w) {
9475
9639
  TITLE_LH3
9476
9640
  );
9477
9641
  const cardH = PAD_TOP3 + contentH + PAD_BOTTOM4;
9478
- return { cols, rows, gridX0, cardW, cardH };
9642
+ const gridH = rows * cardH + (rows - 1) * CARD_GAP2;
9643
+ const yTitleH = component.y_title ? yTitleStackHeight(Array.from(component.y_title).length) : 0;
9644
+ return { cols, rows, gridX0, cardW, cardH, gridH, yTitleH };
9479
9645
  }
9480
9646
  var matrix = {
9481
9647
  measure(component, w) {
9482
- const { rows, cardH } = gridGeom(component, w);
9483
- return (component.x_title ? X_TITLE_H : 0) + rows * cardH + (rows - 1) * CARD_GAP2;
9648
+ const { gridH, yTitleH } = gridGeom(component, w);
9649
+ return (component.x_title ? X_TITLE_H : 0) + Math.max(gridH, yTitleH);
9484
9650
  },
9485
9651
  render(component, box, ctx) {
9486
- const { cols, rows, gridX0, cardW, cardH } = gridGeom(component, box.w);
9652
+ const { cols, rows, gridX0, cardW, cardH, gridH, yTitleH } = gridGeom(component, box.w);
9487
9653
  const gridTop = box.y + (component.x_title ? X_TITLE_H : 0);
9488
- const measuredGridH = rows * cardH + (rows - 1) * CARD_GAP2;
9489
- const availGridH = (box.h ?? measuredGridH) - (component.x_title ? X_TITLE_H : 0);
9654
+ const measuredFallbackH = Math.max(gridH, yTitleH);
9655
+ const availGridH = box.h !== void 0 ? box.h - (component.x_title ? X_TITLE_H : 0) : measuredFallbackH;
9490
9656
  const rowH = Math.max(cardH, (availGridH - (rows - 1) * CARD_GAP2) / rows);
9491
9657
  const r = ctx.shape?.radius ?? CARD_RADIUS4;
9492
- return /* @__PURE__ */ jsxs23("g", { children: [
9493
- component.x_title ? /* @__PURE__ */ jsx26(
9658
+ const xTitleFit = component.x_title ? fitSvgLine(`${component.x_title} \u2192`, {
9659
+ maxWidth: box.w - gridX0,
9660
+ fontSize: AXIS_SIZE,
9661
+ minFontSize: 10
9662
+ }) : null;
9663
+ const yTitleFit = component.y_title ? fitYTitleStack(component.y_title, availGridH) : null;
9664
+ return /* @__PURE__ */ jsxs24("g", { children: [
9665
+ xTitleFit ? /* @__PURE__ */ jsx26(
9494
9666
  "text",
9495
9667
  {
9668
+ "data-truncated": xTitleFit.truncated ? "1" : void 0,
9496
9669
  x: box.x + gridX0,
9497
9670
  y: box.y + AXIS_SIZE + 4,
9498
- fontSize: AXIS_SIZE,
9671
+ fontSize: xTitleFit.fontSize,
9499
9672
  fill: ctx.colors.muted,
9500
9673
  fontFamily: ctx.fonts.body,
9501
9674
  dominantBaseline: "alphabetic",
9502
- children: `${component.x_title} \u2192`
9675
+ children: xTitleFit.text
9503
9676
  }
9504
9677
  ) : null,
9505
- component.y_title ? component.y_title.split("").map((chr, i) => /* @__PURE__ */ jsx26(
9678
+ yTitleFit ? yTitleFit.chars.map((chr, i) => /* @__PURE__ */ jsx26(
9506
9679
  "text",
9507
9680
  {
9681
+ "data-truncated": yTitleFit.truncated && i === yTitleFit.chars.length - 1 ? "1" : void 0,
9508
9682
  x: box.x + Y_TITLE_W / 2,
9509
- y: gridTop + 20 + i * (AXIS_SIZE + 2),
9683
+ y: gridTop + Y_TITLE_START_Y + i * Y_TITLE_CHAR_ADVANCE,
9510
9684
  textAnchor: "middle",
9511
9685
  fontSize: AXIS_SIZE,
9512
9686
  fill: ctx.colors.muted,
@@ -9523,7 +9697,7 @@ var matrix = {
9523
9697
  const y = gridTop + row * (rowH + CARD_GAP2);
9524
9698
  const cell = cellLayout2(item, cardW);
9525
9699
  const titleBaseline = y + PAD_TOP3 + TITLE_SIZE5;
9526
- return /* @__PURE__ */ jsxs23("g", { children: [
9700
+ return /* @__PURE__ */ jsxs24("g", { children: [
9527
9701
  /* @__PURE__ */ jsx26(
9528
9702
  "rect",
9529
9703
  {
@@ -9570,7 +9744,7 @@ var matrix = {
9570
9744
  };
9571
9745
 
9572
9746
  // src/svg/components/insight_panel.tsx
9573
- import { jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
9747
+ import { jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
9574
9748
  var PAD_X6 = 22;
9575
9749
  var PAD_TOP4 = 20;
9576
9750
  var PAD_BOTTOM5 = 18;
@@ -9632,7 +9806,7 @@ var insightPanel = {
9632
9806
  const panelH = Math.max(layout.contentH, box.h ?? layout.contentH);
9633
9807
  const titleBaseline = box.y + PAD_TOP4 + TITLE_SIZE6;
9634
9808
  let rowY = titleBaseline + GAP_TITLE_ROWS2;
9635
- return /* @__PURE__ */ jsxs24("g", { children: [
9809
+ return /* @__PURE__ */ jsxs25("g", { children: [
9636
9810
  /* @__PURE__ */ jsx27(
9637
9811
  "rect",
9638
9812
  {
@@ -9663,7 +9837,7 @@ var insightPanel = {
9663
9837
  layout.rows.map((row, ri) => {
9664
9838
  const rowTop = rowY;
9665
9839
  rowY += row.height + ROW_GAP4;
9666
- return /* @__PURE__ */ jsxs24("g", { children: [
9840
+ return /* @__PURE__ */ jsxs25("g", { children: [
9667
9841
  /* @__PURE__ */ jsx27(
9668
9842
  "text",
9669
9843
  {
@@ -9711,7 +9885,7 @@ var insightPanel = {
9711
9885
  };
9712
9886
 
9713
9887
  // src/svg/components/verdict-banner.tsx
9714
- import { jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
9888
+ import { jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
9715
9889
  var RX2 = 10;
9716
9890
  var PAD_X7 = 24;
9717
9891
  var ICON_SIZE4 = 20;
@@ -9776,7 +9950,7 @@ var verdictBanner = {
9776
9950
  const tx = textX(hasIcon);
9777
9951
  const textComponentH = lineSegments.length * LINE_HEIGHT;
9778
9952
  const textTopY = (height - textComponentH) / 2;
9779
- return /* @__PURE__ */ jsxs25(
9953
+ return /* @__PURE__ */ jsxs26(
9780
9954
  "g",
9781
9955
  {
9782
9956
  transform: `translate(${box.x},${box.y})`,
@@ -9833,7 +10007,7 @@ var verdictBanner = {
9833
10007
  };
9834
10008
 
9835
10009
  // src/svg/components/citation.tsx
9836
- import { jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
10010
+ import { jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
9837
10011
  var ROW2 = 28;
9838
10012
  var LABEL_FONT_SIZE2 = 18;
9839
10013
  var LABEL_MIN_FONT_SIZE2 = 13;
@@ -9855,7 +10029,7 @@ var citation = {
9855
10029
  const labelWidth = measureTextUnits(fittedLabel.text) * fittedLabel.fontSize;
9856
10030
  const remainingWidth = box.w - labelWidth;
9857
10031
  const fittedUrl = source.url ? truncateToUnits(source.url, remainingWidth / URL_FONT_SIZE) : null;
9858
- return /* @__PURE__ */ jsxs26(
10032
+ return /* @__PURE__ */ jsxs27(
9859
10033
  "text",
9860
10034
  {
9861
10035
  "data-truncated": fittedLabel.truncated ? "1" : void 0,
@@ -9884,7 +10058,7 @@ var citation = {
9884
10058
  };
9885
10059
 
9886
10060
  // src/svg/components/swot.tsx
9887
- import { jsx as jsx30, jsxs as jsxs27 } from "react/jsx-runtime";
10061
+ import { jsx as jsx30, jsxs as jsxs28 } from "react/jsx-runtime";
9888
10062
  var DEFAULT_LABELS = {
9889
10063
  strengths: "Strengths",
9890
10064
  weaknesses: "Weaknesses",
@@ -9961,7 +10135,7 @@ function renderQuadrant(q, layout, x, y, w, h, ctx, r) {
9961
10135
  const badgeX = x + PAD_X8;
9962
10136
  const headerBaseline = y + PAD_TOP5 + Math.round(BADGE_FONT2 * 0.86);
9963
10137
  let itemY = y + PAD_TOP5 + Math.max(BADGE, TITLE_SIZE7) + GAP_HEADER_ITEMS;
9964
- return /* @__PURE__ */ jsxs27("g", { children: [
10138
+ return /* @__PURE__ */ jsxs28("g", { children: [
9965
10139
  /* @__PURE__ */ jsx30("rect", { x, y, width: w, height: h, rx: r, fill: panel }),
9966
10140
  /* @__PURE__ */ jsx30(
9967
10141
  "text",
@@ -9994,7 +10168,7 @@ function renderQuadrant(q, layout, x, y, w, h, ctx, r) {
9994
10168
  const rowY = itemY;
9995
10169
  itemY += ITEM_LH + ITEM_GAP2;
9996
10170
  const dotCy = rowY + ITEM_SIZE * 0.65;
9997
- return /* @__PURE__ */ jsxs27("g", { children: [
10171
+ return /* @__PURE__ */ jsxs28("g", { children: [
9998
10172
  /* @__PURE__ */ jsx30("circle", { cx: x + PAD_X8 + BULLET_R, cy: dotCy, r: BULLET_R, fill: itemInk }),
9999
10173
  /* @__PURE__ */ jsx30(
10000
10174
  "text",
@@ -10034,7 +10208,7 @@ var swot = {
10034
10208
  };
10035
10209
 
10036
10210
  // src/svg/components/bmc.tsx
10037
- import { jsx as jsx31, jsxs as jsxs28 } from "react/jsx-runtime";
10211
+ import { jsx as jsx31, jsxs as jsxs29 } from "react/jsx-runtime";
10038
10212
  var BLOCK_LABELS = {
10039
10213
  key_partners: "Key Partners",
10040
10214
  key_activities: "Key Activities",
@@ -10155,7 +10329,7 @@ function renderBlock(cell, layout, ctx, ox, oy, r) {
10155
10329
  const y = oy + cell.y;
10156
10330
  const titleBaseline = y + layout.padTop + layout.titleSize;
10157
10331
  let itemY = y + layout.padTop + layout.titleLH + layout.gapTitleItems;
10158
- return /* @__PURE__ */ jsxs28("g", { children: [
10332
+ return /* @__PURE__ */ jsxs29("g", { children: [
10159
10333
  /* @__PURE__ */ jsx31(
10160
10334
  "rect",
10161
10335
  {
@@ -10186,7 +10360,7 @@ function renderBlock(cell, layout, ctx, ox, oy, r) {
10186
10360
  const rowY = itemY;
10187
10361
  itemY += layout.itemLH + layout.itemGap;
10188
10362
  const dotCy = rowY + layout.itemSize * 0.6;
10189
- return /* @__PURE__ */ jsxs28("g", { children: [
10363
+ return /* @__PURE__ */ jsxs29("g", { children: [
10190
10364
  /* @__PURE__ */ jsx31("circle", { cx: x + PAD_X9 + layout.bulletR, cy: dotCy, r: layout.bulletR, fill: itemInk }),
10191
10365
  /* @__PURE__ */ jsx31(
10192
10366
  "text",
@@ -10228,7 +10402,7 @@ var bmc = {
10228
10402
  };
10229
10403
 
10230
10404
  // src/svg/components/waterfall.tsx
10231
- import { jsx as jsx32, jsxs as jsxs29 } from "react/jsx-runtime";
10405
+ import { jsx as jsx32, jsxs as jsxs30 } from "react/jsx-runtime";
10232
10406
  var LABEL_TOP_PAD2 = 32;
10233
10407
  var LABEL_BOTTOM_PAD2 = 50;
10234
10408
  var BAR_INSET_RATIO = 0.18;
@@ -10300,7 +10474,7 @@ var waterfall = {
10300
10474
  const g = geom(component, box.w, h);
10301
10475
  const bg = ctx.defaultBg ?? ctx.colors.bg;
10302
10476
  const baselineY2 = box.y + g.valueToY(0);
10303
- return /* @__PURE__ */ jsxs29("g", { children: [
10477
+ return /* @__PURE__ */ jsxs30("g", { children: [
10304
10478
  /* @__PURE__ */ jsx32(
10305
10479
  "line",
10306
10480
  {
@@ -10361,7 +10535,7 @@ var waterfall = {
10361
10535
  });
10362
10536
  const valueInk = accessibleInk(ctx.colors.text, bg, valueText.fontSize);
10363
10537
  const categoryInk = accessibleInk(ctx.colors.text, bg, categoryText.fontSize);
10364
- return /* @__PURE__ */ jsxs29("g", { children: [
10538
+ return /* @__PURE__ */ jsxs30("g", { children: [
10365
10539
  /* @__PURE__ */ jsx32("rect", { x: barX, y: yTop, width: barW, height: barH, fill: fillFor(bar.kind, ctx) }),
10366
10540
  /* @__PURE__ */ jsx32(
10367
10541
  "text",
@@ -10399,7 +10573,7 @@ var waterfall = {
10399
10573
  };
10400
10574
 
10401
10575
  // src/svg/components/gantt.tsx
10402
- import { Fragment as Fragment12, jsx as jsx33, jsxs as jsxs30 } from "react/jsx-runtime";
10576
+ import { Fragment as Fragment12, jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
10403
10577
  var ROW_H_NATURAL = 52;
10404
10578
  var ROW_GAP5 = 10;
10405
10579
  var LABEL_W = 160;
@@ -10442,7 +10616,7 @@ var gantt = {
10442
10616
  const vx = (v) => plotX + (v - axisMin) / (axisMax - axisMin) * plotW;
10443
10617
  const axisLabels = component.axis_labels ?? [];
10444
10618
  const axisY = box.y + rowsH + AXIS_LINE_GAP;
10445
- return /* @__PURE__ */ jsxs30("g", { children: [
10619
+ return /* @__PURE__ */ jsxs31("g", { children: [
10446
10620
  component.items.map((item, i) => {
10447
10621
  const rowY = box.y + i * (rowH + ROW_GAP5);
10448
10622
  const cy = rowY + rowH / 2;
@@ -10456,7 +10630,7 @@ var gantt = {
10456
10630
  const barY = rowY + BAR_INSET_Y;
10457
10631
  const barH = Math.max(1, rowH - BAR_INSET_Y * 2);
10458
10632
  const r = Math.min(4, barH / 2);
10459
- return /* @__PURE__ */ jsxs30("g", { children: [
10633
+ return /* @__PURE__ */ jsxs31("g", { children: [
10460
10634
  /* @__PURE__ */ jsx33(
10461
10635
  "text",
10462
10636
  {
@@ -10475,7 +10649,7 @@ var gantt = {
10475
10649
  /* @__PURE__ */ jsx33("rect", { x: barX, y: barY, width: barW, height: barH, rx: r, fill: ctx.colors.accent })
10476
10650
  ] }, i);
10477
10651
  }),
10478
- hasAxisLabels && axisLabels.length > 0 && /* @__PURE__ */ jsxs30(Fragment12, { children: [
10652
+ hasAxisLabels && axisLabels.length > 0 && /* @__PURE__ */ jsxs31(Fragment12, { children: [
10479
10653
  /* @__PURE__ */ jsx33(
10480
10654
  "line",
10481
10655
  {
@@ -10874,11 +11048,11 @@ function findImageComponent(slide) {
10874
11048
  }
10875
11049
 
10876
11050
  // src/svg/ImagePages.tsx
10877
- import { Fragment as Fragment14, jsx as jsx35, jsxs as jsxs31 } from "react/jsx-runtime";
11051
+ import { Fragment as Fragment14, jsx as jsx35, jsxs as jsxs32 } from "react/jsx-runtime";
10878
11052
  var W2 = CANVAS_W_PX;
10879
11053
  var H2 = CANVAS_H_PX;
10880
11054
  function DarkScrim() {
10881
- return /* @__PURE__ */ jsxs31(Fragment14, { children: [
11055
+ return /* @__PURE__ */ jsxs32(Fragment14, { children: [
10882
11056
  /* @__PURE__ */ jsx35("rect", { x: 0, y: 0, width: W2, height: H2, fill: "#0A0E14", fillOpacity: 0.3 }),
10883
11057
  /* @__PURE__ */ jsx35("rect", { x: 0, y: Math.round(H2 * 0.55), width: W2, height: Math.round(H2 * 0.45), fill: "#0A0E14", fillOpacity: 0.28 }),
10884
11058
  /* @__PURE__ */ jsx35("rect", { x: 0, y: Math.round(H2 * 0.78), width: W2, height: Math.round(H2 * 0.22), fill: "#0A0E14", fillOpacity: 0.3 })
@@ -10914,7 +11088,7 @@ function ImageCoverPage({
10914
11088
  for (let i = 0; i <= index && i < ir.slides.length; i++) {
10915
11089
  if (ir.slides[i].type === "chapter") chapterNo++;
10916
11090
  }
10917
- return /* @__PURE__ */ jsxs31("g", { children: [
11091
+ return /* @__PURE__ */ jsxs32("g", { children: [
10918
11092
  /* @__PURE__ */ jsx35(DarkScrim, {}),
10919
11093
  isChapter && /* @__PURE__ */ jsx35(
10920
11094
  "text",
@@ -11034,7 +11208,7 @@ function ImageSplitPage({
11034
11208
  { x: textX2, y: componentsTop, w: SPLIT_TEXT_W, h: Math.max(120, componentsH) },
11035
11209
  ctx
11036
11210
  );
11037
- return /* @__PURE__ */ jsxs31("g", { children: [
11211
+ return /* @__PURE__ */ jsxs32("g", { children: [
11038
11212
  src ? /* @__PURE__ */ jsx35(
11039
11213
  "image",
11040
11214
  {
@@ -11052,7 +11226,7 @@ function ImageSplitPage({
11052
11226
  fontSize: 15,
11053
11227
  minFontSize: 12
11054
11228
  });
11055
- return /* @__PURE__ */ jsxs31(Fragment14, { children: [
11229
+ return /* @__PURE__ */ jsxs32(Fragment14, { children: [
11056
11230
  /* @__PURE__ */ jsx35("rect", { x: imgX, y: SPLIT_IMG_H - 44, width: SPLIT_IMG_W, height: 44, fill: "#0A0E14", fillOpacity: 0.62 }),
11057
11231
  /* @__PURE__ */ jsx35(
11058
11232
  "text",
@@ -11162,7 +11336,7 @@ function ImageTopPage({
11162
11336
  const { placed } = layoutContentFit("single", [b], rect, ctx);
11163
11337
  return placed;
11164
11338
  });
11165
- return /* @__PURE__ */ jsxs31("g", { children: [
11339
+ return /* @__PURE__ */ jsxs32("g", { children: [
11166
11340
  src ? /* @__PURE__ */ jsx35(
11167
11341
  "image",
11168
11342
  {
@@ -11256,7 +11430,7 @@ function ImageAnnotatePage({
11256
11430
  minFontSize: 14
11257
11431
  });
11258
11432
  const caption = imageComponent.caption ? fitSvgLine(imageComponent.caption, { maxWidth: 620, fontSize: 14, minFontSize: 12 }) : null;
11259
- return /* @__PURE__ */ jsxs31("g", { children: [
11433
+ return /* @__PURE__ */ jsxs32("g", { children: [
11260
11434
  title.lines.map((line, i) => /* @__PURE__ */ jsx35(
11261
11435
  "text",
11262
11436
  {
@@ -11338,7 +11512,7 @@ function ImageAnnotatePage({
11338
11512
  maxLines: desc ? 1 : 2,
11339
11513
  lineHeightRatio: 1.2
11340
11514
  });
11341
- return /* @__PURE__ */ jsxs31("g", { children: [
11515
+ return /* @__PURE__ */ jsxs32("g", { children: [
11342
11516
  /* @__PURE__ */ jsx35(
11343
11517
  "line",
11344
11518
  {
@@ -11433,7 +11607,7 @@ function ImageBottomPage({
11433
11607
  H2 - MIN_BOTTOM_IMG
11434
11608
  );
11435
11609
  const imgH = H2 - imgTop;
11436
- return /* @__PURE__ */ jsxs31("g", { children: [
11610
+ return /* @__PURE__ */ jsxs32("g", { children: [
11437
11611
  title.lines.map((line, i) => /* @__PURE__ */ jsx35(
11438
11612
  "text",
11439
11613
  {
@@ -11482,7 +11656,7 @@ function ImageBottomPage({
11482
11656
  fontSize: 15,
11483
11657
  minFontSize: 12
11484
11658
  });
11485
- return /* @__PURE__ */ jsxs31(Fragment14, { children: [
11659
+ return /* @__PURE__ */ jsxs32(Fragment14, { children: [
11486
11660
  /* @__PURE__ */ jsx35("rect", { x: 0, y: captionBottom - 40, width: W2, height: 40, fill: "#0A0E14", fillOpacity: 0.55 }),
11487
11661
  /* @__PURE__ */ jsx35(
11488
11662
  "text",
@@ -11505,7 +11679,7 @@ function ImageBottomPage({
11505
11679
  }
11506
11680
 
11507
11681
  // src/svg/archetypes/cover-banner-title.tsx
11508
- import { Fragment as Fragment15, jsx as jsx36, jsxs as jsxs32 } from "react/jsx-runtime";
11682
+ import { Fragment as Fragment15, jsx as jsx36, jsxs as jsxs33 } from "react/jsx-runtime";
11509
11683
  function BannerTitleCover({ ir, slide, ctx }) {
11510
11684
  const title = layoutSvgText(slide.heading, {
11511
11685
  maxWidth: 1088,
@@ -11531,8 +11705,8 @@ function BannerTitleCover({ ir, slide, ctx }) {
11531
11705
  const version = ir.meta.version;
11532
11706
  const metaDividerY = subtitleY + (subtitle.lines.length > 0 ? subtitle.lines.length * subtitle.lineHeight + 24 : 60);
11533
11707
  const metaTextY = metaDividerY + 48;
11534
- return /* @__PURE__ */ jsxs32(Fragment15, { children: [
11535
- /* @__PURE__ */ jsxs32("g", { transform: "translate(96, 136)", children: [
11708
+ return /* @__PURE__ */ jsxs33(Fragment15, { children: [
11709
+ /* @__PURE__ */ jsxs33("g", { transform: "translate(96, 136)", children: [
11536
11710
  /* @__PURE__ */ jsx36("circle", { cx: "12", cy: "-12", r: "12", fill: ctx.colors.accent }),
11537
11711
  org && /* @__PURE__ */ jsx36(
11538
11712
  "text",
@@ -11548,7 +11722,7 @@ function BannerTitleCover({ ir, slide, ctx }) {
11548
11722
  }
11549
11723
  )
11550
11724
  ] }),
11551
- confLabel && /* @__PURE__ */ jsxs32("g", { children: [
11725
+ confLabel && /* @__PURE__ */ jsxs33("g", { children: [
11552
11726
  /* @__PURE__ */ jsx36(
11553
11727
  "rect",
11554
11728
  {
@@ -11606,7 +11780,7 @@ function BannerTitleCover({ ir, slide, ctx }) {
11606
11780
  },
11607
11781
  i
11608
11782
  )),
11609
- (authorText || date || version) && /* @__PURE__ */ jsxs32(Fragment15, { children: [
11783
+ (authorText || date || version) && /* @__PURE__ */ jsxs33(Fragment15, { children: [
11610
11784
  /* @__PURE__ */ jsx36(
11611
11785
  "line",
11612
11786
  {
@@ -11618,7 +11792,7 @@ function BannerTitleCover({ ir, slide, ctx }) {
11618
11792
  strokeWidth: "1.4"
11619
11793
  }
11620
11794
  ),
11621
- /* @__PURE__ */ jsxs32(
11795
+ /* @__PURE__ */ jsxs33(
11622
11796
  "text",
11623
11797
  {
11624
11798
  x: "96",
@@ -11653,18 +11827,19 @@ function fitHeadingLines(text, opts) {
11653
11827
  });
11654
11828
  if (!content.trim() || first.fontSize >= minPt) return first;
11655
11829
  const budget = maxWidth / minPt * maxLines;
11656
- const truncated = truncateToUnits(content, budget);
11657
- return layoutSvgText(truncated, {
11830
+ const truncatedContent = truncateToUnits(content, budget);
11831
+ const result = layoutSvgText(truncatedContent, {
11658
11832
  maxWidth,
11659
11833
  fontSize: minPt,
11660
11834
  maxLines,
11661
11835
  lineHeightRatio,
11662
11836
  balanceLines: true
11663
11837
  });
11838
+ return { ...result, truncated: truncatedContent !== content };
11664
11839
  }
11665
11840
 
11666
11841
  // src/svg/archetypes/cover-poster-center.tsx
11667
- import { Fragment as Fragment16, jsx as jsx37, jsxs as jsxs33 } from "react/jsx-runtime";
11842
+ import { Fragment as Fragment16, jsx as jsx37, jsxs as jsxs34 } from "react/jsx-runtime";
11668
11843
  var CENTER_X = 640;
11669
11844
  var ACCENT_BAR_W = 60;
11670
11845
  var ACCENT_BAR_H = 4;
@@ -11702,10 +11877,11 @@ function PosterCenterCover({ ir, slide, ctx }) {
11702
11877
  minFontSize: 14
11703
11878
  }) : null;
11704
11879
  const metaY = Math.max(650, subtitleLastY + 56);
11705
- return /* @__PURE__ */ jsxs33(Fragment16, { children: [
11880
+ return /* @__PURE__ */ jsxs34(Fragment16, { children: [
11706
11881
  title.lines.map((line, i) => /* @__PURE__ */ jsx37(
11707
11882
  "text",
11708
11883
  {
11884
+ "data-truncated": title.truncated && i === title.lines.length - 1 ? "1" : void 0,
11709
11885
  x: CENTER_X,
11710
11886
  y: COVER_TITLE_Y + i * title.lineHeight,
11711
11887
  textAnchor: "middle",
@@ -11764,7 +11940,7 @@ function PosterCenterCover({ ir, slide, ctx }) {
11764
11940
  }
11765
11941
 
11766
11942
  // src/svg/archetypes/cover-split-diagonal.tsx
11767
- import { Fragment as Fragment17, jsx as jsx38, jsxs as jsxs34 } from "react/jsx-runtime";
11943
+ import { Fragment as Fragment17, jsx as jsx38, jsxs as jsxs35 } from "react/jsx-runtime";
11768
11944
  var BLOCK_TOP_W = 560;
11769
11945
  var BLOCK_BOTTOM_W = 460;
11770
11946
  var BLOCK_PATH = `M 0,0 L ${BLOCK_TOP_W},0 L ${BLOCK_BOTTOM_W},720 L 0,720 Z`;
@@ -11803,7 +11979,7 @@ function SplitDiagonalCover({ ir, slide, ctx }) {
11803
11979
  fontSize: 19,
11804
11980
  minFontSize: 14
11805
11981
  }) : null;
11806
- return /* @__PURE__ */ jsxs34(Fragment17, { children: [
11982
+ return /* @__PURE__ */ jsxs35(Fragment17, { children: [
11807
11983
  /* @__PURE__ */ jsx38("path", { d: BLOCK_PATH, fill: ctx.colors.primary }),
11808
11984
  org && /* @__PURE__ */ jsx38(
11809
11985
  "text",
@@ -11823,6 +11999,7 @@ function SplitDiagonalCover({ ir, slide, ctx }) {
11823
11999
  title.lines.map((line, i) => /* @__PURE__ */ jsx38(
11824
12000
  "text",
11825
12001
  {
12002
+ "data-truncated": title.truncated && i === title.lines.length - 1 ? "1" : void 0,
11826
12003
  x: TITLE_X2,
11827
12004
  y: TITLE_Y2 + i * title.lineHeight,
11828
12005
  fontFamily: ctx.fonts.heading,
@@ -11865,7 +12042,7 @@ function SplitDiagonalCover({ ir, slide, ctx }) {
11865
12042
  }
11866
12043
 
11867
12044
  // src/svg/archetypes/cover-left-anchor.tsx
11868
- import { Fragment as Fragment18, jsx as jsx39, jsxs as jsxs35 } from "react/jsx-runtime";
12045
+ import { Fragment as Fragment18, jsx as jsx39, jsxs as jsxs36 } from "react/jsx-runtime";
11869
12046
  var COVER_BLOCK_W = 512;
11870
12047
  var COVER_TITLE_X = 64;
11871
12048
  var COVER_TITLE_MAX_W = 360;
@@ -11903,12 +12080,13 @@ function LeftAnchorCover({ ir, slide, ctx }) {
11903
12080
  const subtitleLastY = subtitleY + Math.max(0, subtitle.lines.length - 1) * subtitle.lineHeight;
11904
12081
  const metaDividerY = subtitle.lines.length > 0 ? subtitleLastY + subtitle.lineHeight + 24 : orgY + 56;
11905
12082
  const metaTextY = metaDividerY + 44;
11906
- return /* @__PURE__ */ jsxs35(Fragment18, { children: [
12083
+ return /* @__PURE__ */ jsxs36(Fragment18, { children: [
11907
12084
  /* @__PURE__ */ jsx39("rect", { x: "0", y: "0", width: COVER_BLOCK_W, height: "720", fill: colors.primary }),
11908
12085
  /* @__PURE__ */ jsx39("polygon", { points: "0,720 0,520 200,720", fill: TRIANGLE_DEEP }),
11909
12086
  title.lines.map((line, i) => /* @__PURE__ */ jsx39(
11910
12087
  "text",
11911
12088
  {
12089
+ "data-truncated": title.truncated && i === title.lines.length - 1 ? "1" : void 0,
11912
12090
  x: COVER_TITLE_X,
11913
12091
  y: titleFirstY + i * title.lineHeight,
11914
12092
  fontFamily: fonts.heading,
@@ -11920,7 +12098,7 @@ function LeftAnchorCover({ ir, slide, ctx }) {
11920
12098
  },
11921
12099
  i
11922
12100
  )),
11923
- /* @__PURE__ */ jsxs35("g", { transform: `translate(${COVER_RIGHT_X}, ${orgY})`, children: [
12101
+ /* @__PURE__ */ jsxs36("g", { transform: `translate(${COVER_RIGHT_X}, ${orgY})`, children: [
11924
12102
  /* @__PURE__ */ jsx39("circle", { cx: "12", cy: "-12", r: "12", fill: colors.accent }),
11925
12103
  org && /* @__PURE__ */ jsx39(
11926
12104
  "text",
@@ -11936,7 +12114,7 @@ function LeftAnchorCover({ ir, slide, ctx }) {
11936
12114
  }
11937
12115
  )
11938
12116
  ] }),
11939
- confLabel && /* @__PURE__ */ jsxs35("g", { children: [
12117
+ confLabel && /* @__PURE__ */ jsxs36("g", { children: [
11940
12118
  /* @__PURE__ */ jsx39(
11941
12119
  "rect",
11942
12120
  {
@@ -11978,7 +12156,7 @@ function LeftAnchorCover({ ir, slide, ctx }) {
11978
12156
  },
11979
12157
  i
11980
12158
  )),
11981
- (authorText || date || version) && /* @__PURE__ */ jsxs35(Fragment18, { children: [
12159
+ (authorText || date || version) && /* @__PURE__ */ jsxs36(Fragment18, { children: [
11982
12160
  /* @__PURE__ */ jsx39(
11983
12161
  "line",
11984
12162
  {
@@ -11990,7 +12168,7 @@ function LeftAnchorCover({ ir, slide, ctx }) {
11990
12168
  strokeWidth: "1.4"
11991
12169
  }
11992
12170
  ),
11993
- /* @__PURE__ */ jsxs35(
12171
+ /* @__PURE__ */ jsxs36(
11994
12172
  "text",
11995
12173
  {
11996
12174
  x: COVER_RIGHT_X,
@@ -12010,7 +12188,7 @@ function LeftAnchorCover({ ir, slide, ctx }) {
12010
12188
  }
12011
12189
 
12012
12190
  // src/svg/archetypes/cover-constellation.tsx
12013
- import { Fragment as Fragment19, jsx as jsx40, jsxs as jsxs36 } from "react/jsx-runtime";
12191
+ import { Fragment as Fragment19, jsx as jsx40, jsxs as jsxs37 } from "react/jsx-runtime";
12014
12192
  var BENTO_CARD_STROKE_WIDTH = "1";
12015
12193
  var BENTO_KPI_GLOW_RING1_OPACITY = "0.18";
12016
12194
  var BENTO_KPI_GLOW_RING2_OPACITY = "0.07";
@@ -12046,7 +12224,7 @@ function ConstellationCover({ ir, slide, ctx }) {
12046
12224
  fontSize: 30,
12047
12225
  minFontSize: 18
12048
12226
  }) : null;
12049
- return /* @__PURE__ */ jsxs36(Fragment19, { children: [
12227
+ return /* @__PURE__ */ jsxs37(Fragment19, { children: [
12050
12228
  org && /* @__PURE__ */ jsx40(
12051
12229
  "text",
12052
12230
  {
@@ -12060,7 +12238,7 @@ function ConstellationCover({ ir, slide, ctx }) {
12060
12238
  children: org
12061
12239
  }
12062
12240
  ),
12063
- subtitle && /* @__PURE__ */ jsxs36(Fragment19, { children: [
12241
+ subtitle && /* @__PURE__ */ jsxs37(Fragment19, { children: [
12064
12242
  /* @__PURE__ */ jsx40("rect", { x: "96", y: "548", width: "84", height: "4", fill: colors.accent }),
12065
12243
  /* @__PURE__ */ jsx40(
12066
12244
  "text",
@@ -12079,6 +12257,7 @@ function ConstellationCover({ ir, slide, ctx }) {
12079
12257
  title.lines.map((line, i) => /* @__PURE__ */ jsx40(
12080
12258
  "text",
12081
12259
  {
12260
+ "data-truncated": title.truncated && i === title.lines.length - 1 ? "1" : void 0,
12082
12261
  x: "96",
12083
12262
  y: titleY + i * title.lineHeight,
12084
12263
  fontFamily: fonts.heading,
@@ -12165,7 +12344,7 @@ function ConstellationCover({ ir, slide, ctx }) {
12165
12344
  }
12166
12345
 
12167
12346
  // src/svg/archetypes/cover-editorial-masthead.tsx
12168
- import { Fragment as Fragment20, jsx as jsx41, jsxs as jsxs37 } from "react/jsx-runtime";
12347
+ import { Fragment as Fragment20, jsx as jsx41, jsxs as jsxs38 } from "react/jsx-runtime";
12169
12348
  function EditorialMastheadCover({ ir, slide, ctx }) {
12170
12349
  const { colors, fonts } = ctx;
12171
12350
  const org = ir.meta.organization;
@@ -12185,10 +12364,11 @@ function EditorialMastheadCover({ ir, slide, ctx }) {
12185
12364
  const underlineY = headingLastY + 56;
12186
12365
  const subtitleY = underlineY + 52;
12187
12366
  const subtitle = slide.subheading ? fitSvgLine(slide.subheading, { maxWidth: 900, fontSize: 28, minFontSize: 16 }) : null;
12188
- return /* @__PURE__ */ jsxs37(Fragment20, { children: [
12367
+ return /* @__PURE__ */ jsxs38(Fragment20, { children: [
12189
12368
  title.lines.map((line, i) => /* @__PURE__ */ jsx41(
12190
12369
  "text",
12191
12370
  {
12371
+ "data-truncated": title.truncated && i === title.lines.length - 1 ? "1" : void 0,
12192
12372
  x: "640",
12193
12373
  y: titleY + i * title.lineHeight,
12194
12374
  fontFamily: fonts.heading,
@@ -12245,7 +12425,7 @@ function EditorialMastheadCover({ ir, slide, ctx }) {
12245
12425
  }
12246
12426
 
12247
12427
  // src/svg/archetypes/cover-tone-adaptive-header.tsx
12248
- import { Fragment as Fragment21, jsx as jsx42, jsxs as jsxs38 } from "react/jsx-runtime";
12428
+ import { Fragment as Fragment21, jsx as jsx42, jsxs as jsxs39 } from "react/jsx-runtime";
12249
12429
  function hasBgImage(ir, slide) {
12250
12430
  if (slide.background?.kind !== "asset") return false;
12251
12431
  const assetId = slide.background.asset_id;
@@ -12277,7 +12457,7 @@ function ToneAdaptiveHeaderCover({ ir, slide, ctx }) {
12277
12457
  const titleLastY = titleY + Math.max(0, title.lines.length - 1) * title.lineHeight;
12278
12458
  const subtitleY = titleLastY + 58;
12279
12459
  const subtitle = slide.subheading ? fitSvgLine(slide.subheading, { maxWidth: 1120, fontSize: 34, minFontSize: 18 }) : null;
12280
- return /* @__PURE__ */ jsxs38(Fragment21, { children: [
12460
+ return /* @__PURE__ */ jsxs39(Fragment21, { children: [
12281
12461
  withBg && /* @__PURE__ */ jsx42("rect", { width: "1280", height: "720", fill: "#000000", opacity: "0.38" }),
12282
12462
  org && /* @__PURE__ */ jsx42(
12283
12463
  "text",
@@ -12293,7 +12473,7 @@ function ToneAdaptiveHeaderCover({ ir, slide, ctx }) {
12293
12473
  children: org
12294
12474
  }
12295
12475
  ),
12296
- confLabel && /* @__PURE__ */ jsxs38("g", { children: [
12476
+ confLabel && /* @__PURE__ */ jsxs39("g", { children: [
12297
12477
  /* @__PURE__ */ jsx42(
12298
12478
  "rect",
12299
12479
  {
@@ -12351,7 +12531,7 @@ function ToneAdaptiveHeaderCover({ ir, slide, ctx }) {
12351
12531
  children: subtitle.text
12352
12532
  }
12353
12533
  ),
12354
- !withBg && /* @__PURE__ */ jsxs38(Fragment21, { children: [
12534
+ !withBg && /* @__PURE__ */ jsxs39(Fragment21, { children: [
12355
12535
  /* @__PURE__ */ jsx42(
12356
12536
  "line",
12357
12537
  {
@@ -12407,7 +12587,7 @@ function ToneAdaptiveHeaderCover({ ir, slide, ctx }) {
12407
12587
  }
12408
12588
 
12409
12589
  // src/svg/archetypes/cover-fashion-masthead.tsx
12410
- import { Fragment as Fragment22, jsx as jsx43, jsxs as jsxs39 } from "react/jsx-runtime";
12590
+ import { Fragment as Fragment22, jsx as jsx43, jsxs as jsxs40 } from "react/jsx-runtime";
12411
12591
  function FashionMastheadCover({ ir, slide, ctx }) {
12412
12592
  const org = ir.meta.organization;
12413
12593
  const date = ir.meta.date;
@@ -12434,7 +12614,7 @@ function FashionMastheadCover({ ir, slide, ctx }) {
12434
12614
  const subtitleY = bandY + BAND_H + 58;
12435
12615
  const metaParts = [org, confLabel, date, version].filter((v) => Boolean(v));
12436
12616
  const metaLine = metaParts.length > 0 ? fitSvgLine(metaParts.join(" \xB7 "), { maxWidth: 1100, fontSize: 19, minFontSize: 14 }) : null;
12437
- return /* @__PURE__ */ jsxs39(Fragment22, { children: [
12617
+ return /* @__PURE__ */ jsxs40(Fragment22, { children: [
12438
12618
  /* @__PURE__ */ jsx43("rect", { x: 0, y: 0, width: 1280, height: 720, fill: ctx.colors.primary }),
12439
12619
  org && /* @__PURE__ */ jsx43(
12440
12620
  "text",
@@ -12455,6 +12635,7 @@ function FashionMastheadCover({ ir, slide, ctx }) {
12455
12635
  title.lines.map((line, i) => /* @__PURE__ */ jsx43(
12456
12636
  "text",
12457
12637
  {
12638
+ "data-truncated": title.truncated && i === title.lines.length - 1 ? "1" : void 0,
12458
12639
  x: 56,
12459
12640
  y: TITLE_Y2 + i * title.lineHeight,
12460
12641
  fontFamily: ctx.fonts.heading,
@@ -12537,7 +12718,7 @@ function contentIndexInChapter(slides, index) {
12537
12718
  }
12538
12719
 
12539
12720
  // src/svg/archetypes/chapter-masthead-chapter.tsx
12540
- import { Fragment as Fragment23, jsx as jsx44, jsxs as jsxs40 } from "react/jsx-runtime";
12721
+ import { Fragment as Fragment23, jsx as jsx44, jsxs as jsxs41 } from "react/jsx-runtime";
12541
12722
  function MastheadChapter({ ir, slide, index, ctx }) {
12542
12723
  const { colors, fonts } = ctx;
12543
12724
  const defaultBg = ctx.defaultBg ?? colors.bg;
@@ -12558,7 +12739,7 @@ function MastheadChapter({ ir, slide, index, ctx }) {
12558
12739
  const headingLastY = HEADING_BASELINE + Math.max(0, heading.lines.length - 1) * heading.lineHeight;
12559
12740
  const subheading = slide.subheading ? fitSvgLine(slide.subheading, { maxWidth: HEADING_MAX_WIDTH, fontSize: 24, minFontSize: 14 }) : null;
12560
12741
  const subheadingY = headingLastY + 48;
12561
- return /* @__PURE__ */ jsxs40(Fragment23, { children: [
12742
+ return /* @__PURE__ */ jsxs41(Fragment23, { children: [
12562
12743
  /* @__PURE__ */ jsx44(
12563
12744
  "line",
12564
12745
  {
@@ -12588,6 +12769,7 @@ function MastheadChapter({ ir, slide, index, ctx }) {
12588
12769
  heading.lines.map((line, i) => /* @__PURE__ */ jsx44(
12589
12770
  "text",
12590
12771
  {
12772
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
12591
12773
  x: HEADING_X,
12592
12774
  y: HEADING_BASELINE + i * heading.lineHeight,
12593
12775
  fontFamily: fonts.heading,
@@ -12628,7 +12810,7 @@ function MastheadChapter({ ir, slide, index, ctx }) {
12628
12810
  }
12629
12811
 
12630
12812
  // src/svg/archetypes/chapter-constellation-chapter.tsx
12631
- import { Fragment as Fragment24, jsx as jsx45, jsxs as jsxs41 } from "react/jsx-runtime";
12813
+ import { Fragment as Fragment24, jsx as jsx45, jsxs as jsxs42 } from "react/jsx-runtime";
12632
12814
  function ConstellationChapter({ ir, slide, index, ctx }) {
12633
12815
  const { colors, fonts } = ctx;
12634
12816
  const defaultBg = ctx.defaultBg ?? colors.bg;
@@ -12653,7 +12835,7 @@ function ConstellationChapter({ ir, slide, index, ctx }) {
12653
12835
  minFontSize: 16
12654
12836
  }) : null;
12655
12837
  const subheadingY = headingLastY + 56;
12656
- return /* @__PURE__ */ jsxs41(Fragment24, { children: [
12838
+ return /* @__PURE__ */ jsxs42(Fragment24, { children: [
12657
12839
  /* @__PURE__ */ jsx45(
12658
12840
  "text",
12659
12841
  {
@@ -12670,6 +12852,7 @@ function ConstellationChapter({ ir, slide, index, ctx }) {
12670
12852
  heading.lines.map((line, i) => /* @__PURE__ */ jsx45(
12671
12853
  "text",
12672
12854
  {
12855
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
12673
12856
  x: HEADING_X,
12674
12857
  y: HEADING_BASELINE + i * heading.lineHeight,
12675
12858
  fontFamily: fonts.heading,
@@ -12709,7 +12892,7 @@ function ConstellationChapter({ ir, slide, index, ctx }) {
12709
12892
  }
12710
12893
 
12711
12894
  // src/svg/archetypes/chapter-rail-chapter.tsx
12712
- import { Fragment as Fragment25, jsx as jsx46, jsxs as jsxs42 } from "react/jsx-runtime";
12895
+ import { Fragment as Fragment25, jsx as jsx46, jsxs as jsxs43 } from "react/jsx-runtime";
12713
12896
  var CH_DOT_Y = 600;
12714
12897
  var CH_DOT_SPACING = 40;
12715
12898
  function RailChapter({ ir, slide, index, ctx }) {
@@ -12731,7 +12914,7 @@ function RailChapter({ ir, slide, index, ctx }) {
12731
12914
  const subheadingOpacity = subheading ? accessibleOpacity(ink, defaultBg, subheading.fontSize, 0.7) : 0.7;
12732
12915
  const dotsWidth = Math.max(0, totalChapters - 1) * CH_DOT_SPACING;
12733
12916
  const dotsStartX = 640 - dotsWidth / 2;
12734
- return /* @__PURE__ */ jsxs42(Fragment25, { children: [
12917
+ return /* @__PURE__ */ jsxs43(Fragment25, { children: [
12735
12918
  /* @__PURE__ */ jsx46(
12736
12919
  "text",
12737
12920
  {
@@ -12750,6 +12933,7 @@ function RailChapter({ ir, slide, index, ctx }) {
12750
12933
  heading.lines.map((line, i) => /* @__PURE__ */ jsx46(
12751
12934
  "text",
12752
12935
  {
12936
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
12753
12937
  x: "640",
12754
12938
  y: headingY + i * heading.lineHeight,
12755
12939
  fontFamily: ctx.fonts.heading,
@@ -12805,7 +12989,7 @@ function RailChapter({ ir, slide, index, ctx }) {
12805
12989
  }
12806
12990
 
12807
12991
  // src/svg/archetypes/chapter-banner-chapter.tsx
12808
- import { Fragment as Fragment26, jsx as jsx47, jsxs as jsxs43 } from "react/jsx-runtime";
12992
+ import { Fragment as Fragment26, jsx as jsx47, jsxs as jsxs44 } from "react/jsx-runtime";
12809
12993
  function BannerChapter({ ir, slide, index, ctx }) {
12810
12994
  const chNum = chapterNumberFor(ir.slides, index);
12811
12995
  const label = String(chNum).padStart(2, "0");
@@ -12823,7 +13007,7 @@ function BannerChapter({ ir, slide, index, ctx }) {
12823
13007
  const subheadingY = headingLastY + 56;
12824
13008
  const subheadingOpacity = subheading ? accessibleOpacity(ink, defaultBg, subheading.fontSize, 0.7) : 0.7;
12825
13009
  const hairlineY = headingLastY + 48;
12826
- return /* @__PURE__ */ jsxs43(Fragment26, { children: [
13010
+ return /* @__PURE__ */ jsxs44(Fragment26, { children: [
12827
13011
  /* @__PURE__ */ jsx47(
12828
13012
  "text",
12829
13013
  {
@@ -12842,6 +13026,7 @@ function BannerChapter({ ir, slide, index, ctx }) {
12842
13026
  heading.lines.map((line, i) => /* @__PURE__ */ jsx47(
12843
13027
  "text",
12844
13028
  {
13029
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
12845
13030
  x: "640",
12846
13031
  y: headingY + i * heading.lineHeight,
12847
13032
  fontFamily: ctx.fonts.heading,
@@ -12885,7 +13070,7 @@ function BannerChapter({ ir, slide, index, ctx }) {
12885
13070
  }
12886
13071
 
12887
13072
  // src/svg/archetypes/chapter-poster-chapter.tsx
12888
- import { Fragment as Fragment27, jsx as jsx48, jsxs as jsxs44 } from "react/jsx-runtime";
13073
+ import { Fragment as Fragment27, jsx as jsx48, jsxs as jsxs45 } from "react/jsx-runtime";
12889
13074
  function PosterChapter({ ir, slide, index, ctx }) {
12890
13075
  const chNum = chapterNumberFor(ir.slides, index);
12891
13076
  const label = String(chNum).padStart(2, "0");
@@ -12900,7 +13085,7 @@ function PosterChapter({ ir, slide, index, ctx }) {
12900
13085
  const headingY = heading.lines.length > 1 ? 500 : 532;
12901
13086
  const headingLastY = headingY + Math.max(0, heading.lines.length - 1) * heading.lineHeight;
12902
13087
  const dividerY = headingLastY + 110;
12903
- return /* @__PURE__ */ jsxs44(Fragment27, { children: [
13088
+ return /* @__PURE__ */ jsxs45(Fragment27, { children: [
12904
13089
  org && /* @__PURE__ */ jsx48(
12905
13090
  "text",
12906
13091
  {
@@ -12943,6 +13128,7 @@ function PosterChapter({ ir, slide, index, ctx }) {
12943
13128
  heading.lines.map((line, i) => /* @__PURE__ */ jsx48(
12944
13129
  "text",
12945
13130
  {
13131
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
12946
13132
  x: "56",
12947
13133
  y: headingY + i * heading.lineHeight,
12948
13134
  fontFamily: ctx.fonts.heading,
@@ -12969,7 +13155,7 @@ function PosterChapter({ ir, slide, index, ctx }) {
12969
13155
  }
12970
13156
 
12971
13157
  // src/svg/archetypes/chapter-roman-chapter.tsx
12972
- import { Fragment as Fragment28, jsx as jsx49, jsxs as jsxs45 } from "react/jsx-runtime";
13158
+ import { Fragment as Fragment28, jsx as jsx49, jsxs as jsxs46 } from "react/jsx-runtime";
12973
13159
  var ROMAN_PAIRS = [
12974
13160
  [1e3, "M"],
12975
13161
  [900, "CM"],
@@ -13023,21 +13209,21 @@ function RomanChapter({ ir, slide, index, ctx }) {
13023
13209
  const accent = ctx.colors.primary;
13024
13210
  const defaultBg = ctx.defaultBg ?? ctx.colors.bg;
13025
13211
  const romanInk = accessibleInk(ctx.colors.primary, defaultBg, 176);
13026
- return /* @__PURE__ */ jsxs45(Fragment28, { children: [
13027
- variant === "eclipse" && /* @__PURE__ */ jsxs45(Fragment28, { children: [
13212
+ return /* @__PURE__ */ jsxs46(Fragment28, { children: [
13213
+ variant === "eclipse" && /* @__PURE__ */ jsxs46(Fragment28, { children: [
13028
13214
  /* @__PURE__ */ jsx49("circle", { cx: 990, cy: 392, r: 218, fill: "none", stroke: ctx.colors.border, strokeWidth: 0.8, strokeOpacity: 0.35 }),
13029
13215
  /* @__PURE__ */ jsx49("path", { d: arcPath(990, 392, 246, -160, 65), fill: "none", stroke: accent, strokeWidth: 2, strokeOpacity: 0.85, strokeLinecap: "round" }),
13030
13216
  /* @__PURE__ */ jsx49("path", { d: arcPath(990, 392, 246, 65, 88), fill: "none", stroke: accent, strokeWidth: 1, strokeOpacity: 0.3, strokeLinecap: "round" }),
13031
13217
  /* @__PURE__ */ jsx49("circle", { cx: 990 + 246 * Math.cos(65 * Math.PI / 180), cy: 392 + 246 * Math.sin(65 * Math.PI / 180), r: 5, fill: accent })
13032
13218
  ] }),
13033
- variant === "grooves" && /* @__PURE__ */ jsxs45(Fragment28, { children: [
13219
+ variant === "grooves" && /* @__PURE__ */ jsxs46(Fragment28, { children: [
13034
13220
  /* @__PURE__ */ jsx49("circle", { cx: 1e3, cy: 392, r: 252, fill: "none", stroke: ctx.colors.border, strokeWidth: 0.8, strokeOpacity: 0.4 }),
13035
13221
  /* @__PURE__ */ jsx49("circle", { cx: 1e3, cy: 392, r: 224, fill: "none", stroke: ctx.colors.border, strokeWidth: 0.8, strokeOpacity: 0.3 }),
13036
13222
  /* @__PURE__ */ jsx49("circle", { cx: 1e3, cy: 392, r: 206, fill: "none", stroke: ctx.colors.muted, strokeWidth: 0.6, strokeOpacity: 0.25 }),
13037
13223
  /* @__PURE__ */ jsx49("circle", { cx: 1e3, cy: 392, r: 162, fill: "none", stroke: ctx.colors.border, strokeWidth: 0.8, strokeOpacity: 0.35 }),
13038
13224
  /* @__PURE__ */ jsx49("path", { d: arcPath(1e3, 392, 238, -74, -18), fill: "none", stroke: accent, strokeWidth: 2.5, strokeOpacity: 0.9, strokeLinecap: "round" })
13039
13225
  ] }),
13040
- variant === "chord" && /* @__PURE__ */ jsxs45(Fragment28, { children: [
13226
+ variant === "chord" && /* @__PURE__ */ jsxs46(Fragment28, { children: [
13041
13227
  /* @__PURE__ */ jsx49("path", { d: arcPath(1385, 392, 360, 128, 232), fill: "none", stroke: ctx.colors.border, strokeWidth: 1, strokeOpacity: 0.45 }),
13042
13228
  /* @__PURE__ */ jsx49("path", { d: arcPath(1385, 392, 322, 132, 228), fill: "none", stroke: ctx.colors.muted, strokeWidth: 0.7, strokeOpacity: 0.3 }),
13043
13229
  /* @__PURE__ */ jsx49("path", { d: arcPath(1385, 392, 360, 154, 176), fill: "none", stroke: accent, strokeWidth: 2.2, strokeOpacity: 0.85, strokeLinecap: "round" })
@@ -13056,7 +13242,7 @@ function RomanChapter({ ir, slide, index, ctx }) {
13056
13242
  children: org
13057
13243
  }
13058
13244
  ),
13059
- /* @__PURE__ */ jsxs45(
13245
+ /* @__PURE__ */ jsxs46(
13060
13246
  "text",
13061
13247
  {
13062
13248
  x: "56",
@@ -13075,6 +13261,7 @@ function RomanChapter({ ir, slide, index, ctx }) {
13075
13261
  heading.lines.map((line, i) => /* @__PURE__ */ jsx49(
13076
13262
  "text",
13077
13263
  {
13264
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
13078
13265
  x: "56",
13079
13266
  y: headingY + i * heading.lineHeight,
13080
13267
  fontFamily: ctx.fonts.heading,
@@ -13086,7 +13273,7 @@ function RomanChapter({ ir, slide, index, ctx }) {
13086
13273
  },
13087
13274
  i
13088
13275
  )),
13089
- slide.subheading && /* @__PURE__ */ jsxs45(Fragment28, { children: [
13276
+ slide.subheading && /* @__PURE__ */ jsxs46(Fragment28, { children: [
13090
13277
  /* @__PURE__ */ jsx49(
13091
13278
  "text",
13092
13279
  {
@@ -13116,7 +13303,7 @@ function RomanChapter({ ir, slide, index, ctx }) {
13116
13303
  }
13117
13304
 
13118
13305
  // src/svg/archetypes/chapter-tone-adaptive-chapter.tsx
13119
- import { Fragment as Fragment29, jsx as jsx50, jsxs as jsxs46 } from "react/jsx-runtime";
13306
+ import { Fragment as Fragment29, jsx as jsx50, jsxs as jsxs47 } from "react/jsx-runtime";
13120
13307
  function hasBgImage2(ir, slide) {
13121
13308
  if (slide.background?.kind !== "asset") return false;
13122
13309
  const assetId = slide.background.asset_id;
@@ -13135,7 +13322,7 @@ function ToneAdaptiveChapter({ ir, slide, index, ctx }) {
13135
13322
  });
13136
13323
  const headingY = heading.lines.length > 1 ? 368 : 408;
13137
13324
  const textFg = withBg ? "#FFFFFF" : accessibleInk(ctx.colors.text, ctx.defaultBg ?? ctx.colors.bg, heading.fontSize);
13138
- return /* @__PURE__ */ jsxs46(Fragment29, { children: [
13325
+ return /* @__PURE__ */ jsxs47(Fragment29, { children: [
13139
13326
  withBg && /* @__PURE__ */ jsx50("rect", { width: "1280", height: "720", fill: "#000000", opacity: "0.32" }),
13140
13327
  /* @__PURE__ */ jsx50(
13141
13328
  "text",
@@ -13155,6 +13342,7 @@ function ToneAdaptiveChapter({ ir, slide, index, ctx }) {
13155
13342
  heading.lines.map((line, i) => /* @__PURE__ */ jsx50(
13156
13343
  "text",
13157
13344
  {
13345
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
13158
13346
  x: "640",
13159
13347
  y: headingY + i * heading.lineHeight,
13160
13348
  fontFamily: ctx.fonts.heading,
@@ -13171,7 +13359,7 @@ function ToneAdaptiveChapter({ ir, slide, index, ctx }) {
13171
13359
  }
13172
13360
 
13173
13361
  // src/svg/archetypes/chapter-fashion-chapter.tsx
13174
- import { Fragment as Fragment30, jsx as jsx51, jsxs as jsxs47 } from "react/jsx-runtime";
13362
+ import { Fragment as Fragment30, jsx as jsx51, jsxs as jsxs48 } from "react/jsx-runtime";
13175
13363
  function mixHex2(a, b, t) {
13176
13364
  const pa = parseInt(a.replace("#", ""), 16);
13177
13365
  const pb = parseInt(b.replace("#", ""), 16);
@@ -13193,7 +13381,7 @@ function FashionChapter({ ir, slide, index, ctx }) {
13193
13381
  maxLines: 2,
13194
13382
  minPt: 30
13195
13383
  });
13196
- return /* @__PURE__ */ jsxs47(Fragment30, { children: [
13384
+ return /* @__PURE__ */ jsxs48(Fragment30, { children: [
13197
13385
  /* @__PURE__ */ jsx51("rect", { x: 0, y: 0, width: 1280, height: 720, fill: ctx.colors.accent }),
13198
13386
  /* @__PURE__ */ jsx51(
13199
13387
  "text",
@@ -13226,6 +13414,7 @@ function FashionChapter({ ir, slide, index, ctx }) {
13226
13414
  heading.lines.map((line, i) => /* @__PURE__ */ jsx51(
13227
13415
  "text",
13228
13416
  {
13417
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
13229
13418
  x: 56,
13230
13419
  y: 420 + i * heading.lineHeight,
13231
13420
  fontFamily: ctx.fonts.heading,
@@ -13269,7 +13458,7 @@ var CHAPTER_ARCHETYPES = {
13269
13458
 
13270
13459
  // src/svg/AssertionEvidence.tsx
13271
13460
  import { Fragment as Fragment31 } from "react";
13272
- import { Fragment as Fragment32, jsx as jsx52, jsxs as jsxs48 } from "react/jsx-runtime";
13461
+ import { Fragment as Fragment32, jsx as jsx52, jsxs as jsxs49 } from "react/jsx-runtime";
13273
13462
  function AssertionEvidence({
13274
13463
  components,
13275
13464
  rect,
@@ -13285,7 +13474,7 @@ function AssertionEvidence({
13285
13474
  }
13286
13475
  if (!evidence) {
13287
13476
  const { placed: placed2, dropped: dropped2 } = layoutContentFit("single", components, rect, ctx);
13288
- return /* @__PURE__ */ jsxs48(Fragment32, { children: [
13477
+ return /* @__PURE__ */ jsxs49(Fragment32, { children: [
13289
13478
  placed2.map((p, i) => /* @__PURE__ */ jsx52(Fragment31, { children: renderComponent(p.component, p.box, ctx) }, i)),
13290
13479
  dropped2 > 0 && /* @__PURE__ */ jsx52(
13291
13480
  "text",
@@ -13329,7 +13518,7 @@ function AssertionEvidence({
13329
13518
  { x: rect.x, y: supportY, w: rect.w, h: supportH },
13330
13519
  ctx
13331
13520
  );
13332
- return /* @__PURE__ */ jsxs48(Fragment32, { children: [
13521
+ return /* @__PURE__ */ jsxs49(Fragment32, { children: [
13333
13522
  renderComponent(evidence, { x: rect.x, y: centredY, w: rect.w }, ctx),
13334
13523
  placed.map((p, i) => /* @__PURE__ */ jsx52(Fragment31, { children: renderComponent(p.component, p.box, ctx) }, i)),
13335
13524
  dropped > 0 && /* @__PURE__ */ jsx52(
@@ -13350,7 +13539,7 @@ function AssertionEvidence({
13350
13539
 
13351
13540
  // src/svg/BigNumber.tsx
13352
13541
  import { Fragment as Fragment33 } from "react";
13353
- import { Fragment as Fragment34, jsx as jsx53, jsxs as jsxs49 } from "react/jsx-runtime";
13542
+ import { Fragment as Fragment34, jsx as jsx53, jsxs as jsxs50 } from "react/jsx-runtime";
13354
13543
  var HERO_SIZE = 200;
13355
13544
  var HERO_MIN_FONT_SIZE = 48;
13356
13545
  var LABEL_FONT_SIZE3 = 28;
@@ -13396,9 +13585,9 @@ function BigNumber({
13396
13585
  fontSize: LABEL_FONT_SIZE3,
13397
13586
  minFontSize: LABEL_MIN_FONT_SIZE3
13398
13587
  }) : null;
13399
- return /* @__PURE__ */ jsxs49(Fragment34, { children: [
13400
- hero && fittedValue && /* @__PURE__ */ jsxs49("g", { children: [
13401
- /* @__PURE__ */ jsxs49(
13588
+ return /* @__PURE__ */ jsxs50(Fragment34, { children: [
13589
+ hero && fittedValue && /* @__PURE__ */ jsxs50("g", { children: [
13590
+ /* @__PURE__ */ jsxs50(
13402
13591
  "text",
13403
13592
  {
13404
13593
  "data-truncated": fittedValue.truncated ? "1" : void 0,
@@ -13448,7 +13637,7 @@ function BigNumber({
13448
13637
  }
13449
13638
 
13450
13639
  // src/svg/SvgContent.tsx
13451
- import { jsx as jsx54, jsxs as jsxs50 } from "react/jsx-runtime";
13640
+ import { jsx as jsx54, jsxs as jsxs51 } from "react/jsx-runtime";
13452
13641
  function SvgContent({ arrangement, components, rect, ctx }) {
13453
13642
  const auditRect = `${rect.x},${rect.y},${rect.w},${rect.h}`;
13454
13643
  if (components.length === 1 && FULL_BODY_TYPES.has(components[0].type)) {
@@ -13467,7 +13656,7 @@ function SvgContent({ arrangement, components, rect, ctx }) {
13467
13656
  dy = Math.max(0, (rect.h - h) * 0.38);
13468
13657
  }
13469
13658
  const asideDivider = arrangement === "aside" && components.length >= 2 ? asideSplit(rect).dividerX : null;
13470
- return /* @__PURE__ */ jsxs50("g", { "data-audit-rect": auditRect, children: [
13659
+ return /* @__PURE__ */ jsxs51("g", { "data-audit-rect": auditRect, children: [
13471
13660
  asideDivider != null && /* @__PURE__ */ jsx54(
13472
13661
  "line",
13473
13662
  {
@@ -13499,7 +13688,7 @@ function SvgContent({ arrangement, components, rect, ctx }) {
13499
13688
  }
13500
13689
 
13501
13690
  // src/svg/archetypes/content-narrow-column.tsx
13502
- import { Fragment as Fragment35, jsx as jsx55, jsxs as jsxs51 } from "react/jsx-runtime";
13691
+ import { Fragment as Fragment35, jsx as jsx55, jsxs as jsxs52 } from "react/jsx-runtime";
13503
13692
  function NarrowColumnContent({ ir, slide, index, ctx }) {
13504
13693
  const { colors, fonts } = ctx;
13505
13694
  const section = sectionNameFor(ir.slides, index);
@@ -13529,7 +13718,7 @@ function NarrowColumnContent({ ir, slide, index, ctx }) {
13529
13718
  const pageLabel = String(index + 1).padStart(2, "0");
13530
13719
  const kicker = section ? fitSvgLine(section, { maxWidth: COLUMN_W, fontSize: 16, minFontSize: 12 }) : null;
13531
13720
  const footnote = slide.footnote ? fitSvgLine(slide.footnote, { maxWidth: 980, fontSize: 20, minFontSize: 13 }) : null;
13532
- return /* @__PURE__ */ jsxs51(Fragment35, { children: [
13721
+ return /* @__PURE__ */ jsxs52(Fragment35, { children: [
13533
13722
  /* @__PURE__ */ jsx55(
13534
13723
  "line",
13535
13724
  {
@@ -13558,6 +13747,7 @@ function NarrowColumnContent({ ir, slide, index, ctx }) {
13558
13747
  heading.lines.map((line, i) => /* @__PURE__ */ jsx55(
13559
13748
  "text",
13560
13749
  {
13750
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
13561
13751
  x: "96",
13562
13752
  y: HEADING_BASELINE + i * heading.lineHeight,
13563
13753
  fontFamily: fonts.heading,
@@ -13623,7 +13813,7 @@ function NarrowColumnContent({ ir, slide, index, ctx }) {
13623
13813
  }
13624
13814
 
13625
13815
  // src/svg/archetypes/content-two-column.tsx
13626
- import { Fragment as Fragment36, jsx as jsx56, jsxs as jsxs52 } from "react/jsx-runtime";
13816
+ import { Fragment as Fragment36, jsx as jsx56, jsxs as jsxs53 } from "react/jsx-runtime";
13627
13817
  function TwoColumnContent({ ir, slide, index, ctx }) {
13628
13818
  const { colors, fonts } = ctx;
13629
13819
  const section = sectionNameFor(ir.slides, index);
@@ -13644,8 +13834,8 @@ function TwoColumnContent({ ir, slide, index, ctx }) {
13644
13834
  const ruleY = accentY + 22;
13645
13835
  const contentY = ruleY + 34;
13646
13836
  const contentH = 640 - contentY;
13647
- return /* @__PURE__ */ jsxs52(Fragment36, { children: [
13648
- kicker && /* @__PURE__ */ jsxs52(Fragment36, { children: [
13837
+ return /* @__PURE__ */ jsxs53(Fragment36, { children: [
13838
+ kicker && /* @__PURE__ */ jsxs53(Fragment36, { children: [
13649
13839
  /* @__PURE__ */ jsx56("rect", { x: 96, y: KICKER_Y2 - 13, width: 13, height: 13, fill: colors.accent }),
13650
13840
  /* @__PURE__ */ jsx56(
13651
13841
  "text",
@@ -13665,6 +13855,7 @@ function TwoColumnContent({ ir, slide, index, ctx }) {
13665
13855
  heading.lines.map((line, i) => /* @__PURE__ */ jsx56(
13666
13856
  "text",
13667
13857
  {
13858
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
13668
13859
  x: 96,
13669
13860
  y: HEADING_BASELINE + i * heading.lineHeight,
13670
13861
  fontFamily: fonts.heading,
@@ -13704,7 +13895,7 @@ function TwoColumnContent({ ir, slide, index, ctx }) {
13704
13895
  }
13705
13896
 
13706
13897
  // src/svg/archetypes/content-rail-numbered.tsx
13707
- import { Fragment as Fragment37, jsx as jsx57, jsxs as jsxs53 } from "react/jsx-runtime";
13898
+ import { Fragment as Fragment37, jsx as jsx57, jsxs as jsxs54 } from "react/jsx-runtime";
13708
13899
  var BASELINE_FUDGE_RATIO3 = 0.32;
13709
13900
  var RAIL_X = 48;
13710
13901
  var RAIL_Y = 96;
@@ -13763,7 +13954,7 @@ function RailNumberedContent({ ir, slide, index, ctx }) {
13763
13954
  h: Math.max(0, CONTENT_BOTTOM - contentRectY)
13764
13955
  };
13765
13956
  const footnote = slide.footnote ? fitSvgLine(slide.footnote, { maxWidth: CONTENT_W, fontSize: 14, minFontSize: 11 }) : null;
13766
- return /* @__PURE__ */ jsxs53(Fragment37, { children: [
13957
+ return /* @__PURE__ */ jsxs54(Fragment37, { children: [
13767
13958
  /* @__PURE__ */ jsx57("rect", { x: RAIL_X, y: RAIL_Y, width: RAIL_W, height: RAIL_H, fill: colors.primary }),
13768
13959
  /* @__PURE__ */ jsx57("circle", { cx: RAIL_X + RAIL_W / 2, cy: railNodeCy, r: RAIL_NODE_R, fill: colors.primary }),
13769
13960
  /* @__PURE__ */ jsx57(
@@ -13795,6 +13986,7 @@ function RailNumberedContent({ ir, slide, index, ctx }) {
13795
13986
  heading.lines.map((line, i) => /* @__PURE__ */ jsx57(
13796
13987
  "text",
13797
13988
  {
13989
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
13798
13990
  x: TITLE_X3,
13799
13991
  y: BADGE_CENTER_Y - (heading.lines.length - 1) * heading.lineHeight / 2 + i * heading.lineHeight + headingFudge,
13800
13992
  fontFamily: fonts.heading,
@@ -13837,7 +14029,7 @@ function RailNumberedContent({ ir, slide, index, ctx }) {
13837
14029
  }
13838
14030
 
13839
14031
  // src/svg/archetypes/content-banner-heading.tsx
13840
- import { Fragment as Fragment38, jsx as jsx58, jsxs as jsxs54 } from "react/jsx-runtime";
14032
+ import { Fragment as Fragment38, jsx as jsx58, jsxs as jsxs55 } from "react/jsx-runtime";
13841
14033
  var BANNER_X = 96;
13842
14034
  var BANNER_Y = 72;
13843
14035
  var BANNER_W = 1088;
@@ -13889,7 +14081,7 @@ function BannerHeadingContent({ ir, slide, index, ctx }) {
13889
14081
  h: Math.max(0, CONTENT_RECT_BOTTOM - contentRectY)
13890
14082
  };
13891
14083
  const footnote = slide.footnote ? fitSvgLine(slide.footnote, { maxWidth: BANNER_W, fontSize: 14, minFontSize: 11 }) : null;
13892
- return /* @__PURE__ */ jsxs54(Fragment38, { children: [
14084
+ return /* @__PURE__ */ jsxs55(Fragment38, { children: [
13893
14085
  kicker && /* @__PURE__ */ jsx58(
13894
14086
  "text",
13895
14087
  {
@@ -13918,6 +14110,7 @@ function BannerHeadingContent({ ir, slide, index, ctx }) {
13918
14110
  heading.lines.map((line, i) => /* @__PURE__ */ jsx58(
13919
14111
  "text",
13920
14112
  {
14113
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
13921
14114
  x: BANNER_TITLE_X,
13922
14115
  y: bannerCenterY - (heading.lines.length - 1) * heading.lineHeight / 2 + i * heading.lineHeight + baselineFudge,
13923
14116
  fontFamily: fonts.heading,
@@ -13960,7 +14153,7 @@ function BannerHeadingContent({ ir, slide, index, ctx }) {
13960
14153
  }
13961
14154
 
13962
14155
  // src/svg/archetypes/content-stacked-poster.tsx
13963
- import { Fragment as Fragment39, jsx as jsx59, jsxs as jsxs55 } from "react/jsx-runtime";
14156
+ import { Fragment as Fragment39, jsx as jsx59, jsxs as jsxs56 } from "react/jsx-runtime";
13964
14157
  var CENTER_X2 = 640;
13965
14158
  var ACCENT_BAR_W2 = 60;
13966
14159
  var ACCENT_BAR_H2 = 4;
@@ -14024,7 +14217,7 @@ function renderStackedContent({ ir, slide, index, ctx }) {
14024
14217
  const subheadingFill = subheading ? accessibleInk(ctx.colors.accent, ctx.defaultBg ?? ctx.colors.bg, subheading.fontSize) : ctx.colors.accent;
14025
14218
  const contentRectY = 180 + headingExtra + subheadingBudget;
14026
14219
  const contentRectH = Math.max(120, contentH - headingExtra - subheadingBudget);
14027
- return /* @__PURE__ */ jsxs55(Fragment39, { children: [
14220
+ return /* @__PURE__ */ jsxs56(Fragment39, { children: [
14028
14221
  sectionLabel && /* @__PURE__ */ jsx59(
14029
14222
  "text",
14030
14223
  {
@@ -14054,6 +14247,7 @@ function renderStackedContent({ ir, slide, index, ctx }) {
14054
14247
  heading.lines.map((line, i) => /* @__PURE__ */ jsx59(
14055
14248
  "text",
14056
14249
  {
14250
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
14057
14251
  x: "56",
14058
14252
  y: 150 + i * heading.lineHeight,
14059
14253
  fontFamily: ctx.fonts.heading,
@@ -14146,7 +14340,7 @@ function StackedPosterContent(props) {
14146
14340
  letterSpacing: 3
14147
14341
  }) : null;
14148
14342
  const footnote = slide.footnote ? fitSvgLine(slide.footnote, { maxWidth: 1e3, fontSize: 20, minFontSize: 14 }) : null;
14149
- return /* @__PURE__ */ jsxs55(Fragment39, { children: [
14343
+ return /* @__PURE__ */ jsxs56(Fragment39, { children: [
14150
14344
  sectionLabel && /* @__PURE__ */ jsx59(
14151
14345
  "text",
14152
14346
  {
@@ -14176,6 +14370,7 @@ function StackedPosterContent(props) {
14176
14370
  heading.lines.map((line, i) => /* @__PURE__ */ jsx59(
14177
14371
  "text",
14178
14372
  {
14373
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
14179
14374
  x: CENTER_X2,
14180
14375
  y: TITLE_Y + i * heading.lineHeight,
14181
14376
  textAnchor: "middle",
@@ -14202,7 +14397,7 @@ function StackedPosterContent(props) {
14202
14397
  }
14203
14398
  ),
14204
14399
  renderPosterSlot(slide.components[0], heroRect, ctx),
14205
- isPair && /* @__PURE__ */ jsxs55(Fragment39, { children: [
14400
+ isPair && /* @__PURE__ */ jsxs56(Fragment39, { children: [
14206
14401
  /* @__PURE__ */ jsx59(
14207
14402
  "line",
14208
14403
  {
@@ -14235,7 +14430,7 @@ function StackedPosterContent(props) {
14235
14430
  }
14236
14431
 
14237
14432
  // src/svg/archetypes/content-tone-adaptive-content.tsx
14238
- import { Fragment as Fragment40, jsx as jsx60, jsxs as jsxs56 } from "react/jsx-runtime";
14433
+ import { Fragment as Fragment40, jsx as jsx60, jsxs as jsxs57 } from "react/jsx-runtime";
14239
14434
  function hasBgImage3(ir, slide) {
14240
14435
  if (slide.background?.kind !== "asset") return false;
14241
14436
  const assetId = slide.background.asset_id;
@@ -14286,7 +14481,7 @@ function ToneAdaptiveContent({ ir, slide, index, ctx }) {
14286
14481
  }
14287
14482
  };
14288
14483
  const footerFill = accessibleInk(colors.muted, "#FFFFFF", 20);
14289
- return /* @__PURE__ */ jsxs56(Fragment40, { children: [
14484
+ return /* @__PURE__ */ jsxs57(Fragment40, { children: [
14290
14485
  /* @__PURE__ */ jsx60(
14291
14486
  "rect",
14292
14487
  {
@@ -14315,6 +14510,7 @@ function ToneAdaptiveContent({ ir, slide, index, ctx }) {
14315
14510
  heading2.lines.map((line, i) => /* @__PURE__ */ jsx60(
14316
14511
  "text",
14317
14512
  {
14513
+ "data-truncated": heading2.truncated && i === heading2.lines.length - 1 ? "1" : void 0,
14318
14514
  x: "92",
14319
14515
  y: 168 + i * heading2.lineHeight,
14320
14516
  fontFamily: fonts.heading,
@@ -14413,7 +14609,7 @@ function ToneAdaptiveContent({ ir, slide, index, ctx }) {
14413
14609
  const dividerY = 162 + headingExtra + subheadingBudget;
14414
14610
  const contentRectY = 180 + headingExtra + subheadingBudget;
14415
14611
  const contentRectH = Math.max(120, contentH - headingExtra - subheadingBudget);
14416
- return /* @__PURE__ */ jsxs56(Fragment40, { children: [
14612
+ return /* @__PURE__ */ jsxs57(Fragment40, { children: [
14417
14613
  sectionLabel && /* @__PURE__ */ jsx60(
14418
14614
  "text",
14419
14615
  {
@@ -14431,6 +14627,7 @@ function ToneAdaptiveContent({ ir, slide, index, ctx }) {
14431
14627
  heading.lines.map((line, i) => /* @__PURE__ */ jsx60(
14432
14628
  "text",
14433
14629
  {
14630
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
14434
14631
  x: "64",
14435
14632
  y: 130 + i * heading.lineHeight,
14436
14633
  fontFamily: fonts.heading,
@@ -14670,7 +14867,7 @@ function sortUnitsByHeroWeight(units) {
14670
14867
  }
14671
14868
 
14672
14869
  // src/svg/archetypes/content-bento-panel.tsx
14673
- import { Fragment as Fragment41, jsx as jsx61, jsxs as jsxs57 } from "react/jsx-runtime";
14870
+ import { Fragment as Fragment41, jsx as jsx61, jsxs as jsxs58 } from "react/jsx-runtime";
14674
14871
  var BENTO_CARD_RADIUS = 6;
14675
14872
  var BENTO_CARD_PAD = 20;
14676
14873
  var BENTO_CARD_STROKE_OPACITY = "0.3";
@@ -14751,7 +14948,7 @@ function renderKpiCardBody(item, box, ctx) {
14751
14948
  glowMaxCx
14752
14949
  );
14753
14950
  const glowCy = valueBaselineY - valueSize * 0.75;
14754
- return /* @__PURE__ */ jsxs57(Fragment41, { children: [
14951
+ return /* @__PURE__ */ jsxs58(Fragment41, { children: [
14755
14952
  item.icon && /* @__PURE__ */ jsx61(
14756
14953
  Icon,
14757
14954
  {
@@ -14762,7 +14959,7 @@ function renderKpiCardBody(item, box, ctx) {
14762
14959
  color: ctx.colors.primary
14763
14960
  }
14764
14961
  ),
14765
- /* @__PURE__ */ jsxs57(
14962
+ /* @__PURE__ */ jsxs58(
14766
14963
  "text",
14767
14964
  {
14768
14965
  "data-truncated": fittedValue.truncated ? "1" : void 0,
@@ -14840,7 +15037,7 @@ function renderKpiCardBody(item, box, ctx) {
14840
15037
  ] });
14841
15038
  }
14842
15039
  function renderKpiCard(item, box, ctx, colors) {
14843
- return /* @__PURE__ */ jsxs57(Fragment41, { children: [
15040
+ return /* @__PURE__ */ jsxs58(Fragment41, { children: [
14844
15041
  /* @__PURE__ */ jsx61(
14845
15042
  "rect",
14846
15043
  {
@@ -14872,7 +15069,7 @@ function renderIconCard(item, box, ctx, colors) {
14872
15069
  iconSize: BENTO_ICON_CARD_ICON_SIZE
14873
15070
  });
14874
15071
  const innerY = box.y + BENTO_CARD_TOP_PAD + Math.max(0, (budgetH - contentH) / 2);
14875
- return /* @__PURE__ */ jsxs57(Fragment41, { children: [
15072
+ return /* @__PURE__ */ jsxs58(Fragment41, { children: [
14876
15073
  /* @__PURE__ */ jsx61(
14877
15074
  "rect",
14878
15075
  {
@@ -14950,7 +15147,7 @@ function renderCell2(cell, i, ctx, colors) {
14950
15147
  const innerY = box.y + BENTO_CARD_TOP_PAD + centerOffset;
14951
15148
  const scale = SCALABLE_TYPES.has(component.type) && measured > budgetH && measured > 0 ? budgetH / measured : 1;
14952
15149
  const passthroughShell = PASSTHROUGH_SHELL_TYPES.has(component.type);
14953
- return /* @__PURE__ */ jsxs57("g", { ...auditAttrs, children: [
15150
+ return /* @__PURE__ */ jsxs58("g", { ...auditAttrs, children: [
14954
15151
  !passthroughShell && /* @__PURE__ */ jsx61(
14955
15152
  "rect",
14956
15153
  {
@@ -15061,7 +15258,7 @@ function BentoPanelContent({ ir, slide, index, ctx }) {
15061
15258
  }
15062
15259
  );
15063
15260
  }
15064
- return /* @__PURE__ */ jsxs57(Fragment41, { children: [
15261
+ return /* @__PURE__ */ jsxs58(Fragment41, { children: [
15065
15262
  kicker && /* @__PURE__ */ jsx61(
15066
15263
  "text",
15067
15264
  {
@@ -15079,6 +15276,7 @@ function BentoPanelContent({ ir, slide, index, ctx }) {
15079
15276
  heading.lines.map((line, i) => /* @__PURE__ */ jsx61(
15080
15277
  "text",
15081
15278
  {
15279
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
15082
15280
  x: "96",
15083
15281
  y: HEADING_BASELINE + i * heading.lineHeight,
15084
15282
  fontFamily: fonts.heading,
@@ -15135,7 +15333,7 @@ var CONTENT_ARCHETYPES = {
15135
15333
  };
15136
15334
 
15137
15335
  // src/svg/archetypes/ending-masthead-ending.tsx
15138
- import { Fragment as Fragment42, jsx as jsx62, jsxs as jsxs58 } from "react/jsx-runtime";
15336
+ import { Fragment as Fragment42, jsx as jsx62, jsxs as jsxs59 } from "react/jsx-runtime";
15139
15337
  function MastheadEnding({ ir, slide, ctx }) {
15140
15338
  const { colors, fonts } = ctx;
15141
15339
  const HEADING_LAST_BASELINE = 340;
@@ -15158,10 +15356,11 @@ function MastheadEnding({ ir, slide, ctx }) {
15158
15356
  const contactText = contact ? [contact.name, contact.email].filter(Boolean).join(" \xB7 ") : null;
15159
15357
  const date = ir.meta.date;
15160
15358
  const metaParts = [org, contactText, date].filter((v) => Boolean(v));
15161
- return /* @__PURE__ */ jsxs58(Fragment42, { children: [
15359
+ return /* @__PURE__ */ jsxs59(Fragment42, { children: [
15162
15360
  heading.lines.map((line, i) => /* @__PURE__ */ jsx62(
15163
15361
  "text",
15164
15362
  {
15363
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
15165
15364
  x: "640",
15166
15365
  y: headingY + i * heading.lineHeight,
15167
15366
  fontFamily: fonts.heading,
@@ -15207,7 +15406,7 @@ function MastheadEnding({ ir, slide, ctx }) {
15207
15406
  }
15208
15407
 
15209
15408
  // src/svg/archetypes/ending-constellation-ending.tsx
15210
- import { Fragment as Fragment43, jsx as jsx63, jsxs as jsxs59 } from "react/jsx-runtime";
15409
+ import { Fragment as Fragment43, jsx as jsx63, jsxs as jsxs60 } from "react/jsx-runtime";
15211
15410
  function splitTrailingPeriod(line) {
15212
15411
  if (line.endsWith("\u3002") || line.endsWith(".")) {
15213
15412
  return { rest: line.slice(0, -1), period: line.slice(-1) };
@@ -15251,7 +15450,7 @@ function ConstellationEnding({ ir, slide, ctx }) {
15251
15450
  const META_GAP = 40;
15252
15451
  const META_LINE_HEIGHT = 22;
15253
15452
  const metaFirstBaselineY = BAR_Y + BAR_H3 + META_GAP;
15254
- return /* @__PURE__ */ jsxs59(Fragment43, { children: [
15453
+ return /* @__PURE__ */ jsxs60(Fragment43, { children: [
15255
15454
  heading.lines.map((line, i) => {
15256
15455
  if (i !== lastLineIndex) {
15257
15456
  return /* @__PURE__ */ jsx63(
@@ -15271,9 +15470,10 @@ function ConstellationEnding({ ir, slide, ctx }) {
15271
15470
  );
15272
15471
  }
15273
15472
  const { rest, period } = splitTrailingPeriod(line);
15274
- return /* @__PURE__ */ jsxs59(
15473
+ return /* @__PURE__ */ jsxs60(
15275
15474
  "text",
15276
15475
  {
15476
+ "data-truncated": heading.truncated ? "1" : void 0,
15277
15477
  x: "640",
15278
15478
  y: headingY + i * heading.lineHeight,
15279
15479
  fontFamily: fonts.heading,
@@ -15304,7 +15504,7 @@ function ConstellationEnding({ ir, slide, ctx }) {
15304
15504
  children: subheading.text
15305
15505
  }
15306
15506
  ),
15307
- metaLines.length > 0 && /* @__PURE__ */ jsxs59(Fragment43, { children: [
15507
+ metaLines.length > 0 && /* @__PURE__ */ jsxs60(Fragment43, { children: [
15308
15508
  /* @__PURE__ */ jsx63(
15309
15509
  "rect",
15310
15510
  {
@@ -15334,7 +15534,7 @@ function ConstellationEnding({ ir, slide, ctx }) {
15334
15534
  }
15335
15535
 
15336
15536
  // src/svg/archetypes/ending-rail-ending.tsx
15337
- import { Fragment as Fragment44, jsx as jsx64, jsxs as jsxs60 } from "react/jsx-runtime";
15537
+ import { Fragment as Fragment44, jsx as jsx64, jsxs as jsxs61 } from "react/jsx-runtime";
15338
15538
  var ENDING_HEADING_LAST_BASELINE = 356;
15339
15539
  var ENDING_TWO_LINE_SHIFT_MAX = 88;
15340
15540
  var ENDING_TWO_LINE_HAIRLINE_GAP = 100;
@@ -15357,10 +15557,10 @@ function RailEnding({ ir, slide, ctx }) {
15357
15557
  const subheading = slide.subheading ? fitSvgLine(slide.subheading, { maxWidth: 768, fontSize: 40, minFontSize: 20 }) : null;
15358
15558
  const subheadingY = headingLastY + 68;
15359
15559
  const hairlineY = headingLastY + (isTwoLine ? ENDING_TWO_LINE_HAIRLINE_GAP : 120);
15360
- return /* @__PURE__ */ jsxs60(Fragment44, { children: [
15560
+ return /* @__PURE__ */ jsxs61(Fragment44, { children: [
15361
15561
  /* @__PURE__ */ jsx64("rect", { x: "0", y: "480", width: "280", height: "240", fill: colors.primary }),
15362
15562
  /* @__PURE__ */ jsx64("rect", { x: "0", y: "600", width: "140", height: "120", fill: colors.accent }),
15363
- /* @__PURE__ */ jsxs60("g", { transform: "translate(96, 144)", children: [
15563
+ /* @__PURE__ */ jsxs61("g", { transform: "translate(96, 144)", children: [
15364
15564
  /* @__PURE__ */ jsx64("circle", { cx: "12", cy: "-12", r: "12", fill: colors.accent }),
15365
15565
  org && /* @__PURE__ */ jsx64(
15366
15566
  "text",
@@ -15379,6 +15579,7 @@ function RailEnding({ ir, slide, ctx }) {
15379
15579
  heading.lines.map((line, i) => /* @__PURE__ */ jsx64(
15380
15580
  "text",
15381
15581
  {
15582
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
15382
15583
  x: "400",
15383
15584
  y: headingY + i * heading.lineHeight,
15384
15585
  fontFamily: fonts.heading,
@@ -15415,7 +15616,7 @@ function RailEnding({ ir, slide, ctx }) {
15415
15616
  strokeWidth: "1.4"
15416
15617
  }
15417
15618
  ),
15418
- contactText && /* @__PURE__ */ jsxs60(Fragment44, { children: [
15619
+ contactText && /* @__PURE__ */ jsxs61(Fragment44, { children: [
15419
15620
  /* @__PURE__ */ jsx64(
15420
15621
  "text",
15421
15622
  {
@@ -15458,7 +15659,7 @@ function RailEnding({ ir, slide, ctx }) {
15458
15659
  }
15459
15660
 
15460
15661
  // src/svg/archetypes/ending-banner-ending.tsx
15461
- import { Fragment as Fragment45, jsx as jsx65, jsxs as jsxs61 } from "react/jsx-runtime";
15662
+ import { Fragment as Fragment45, jsx as jsx65, jsxs as jsxs62 } from "react/jsx-runtime";
15462
15663
  var ENDING_HEADING_LAST_BASELINE2 = 356;
15463
15664
  var ENDING_TWO_LINE_SHIFT_MAX2 = 85;
15464
15665
  var ENDING_TWO_LINE_DIVIDER_GAP = 128;
@@ -15486,8 +15687,8 @@ function BannerEnding({ ir, slide, ctx }) {
15486
15687
  });
15487
15688
  const subheadingY = headingLastY + 80;
15488
15689
  const dividerY = headingLastY + (isTwoLine ? ENDING_TWO_LINE_DIVIDER_GAP : 164);
15489
- return /* @__PURE__ */ jsxs61(Fragment45, { children: [
15490
- /* @__PURE__ */ jsxs61("g", { transform: "translate(96, 136)", children: [
15690
+ return /* @__PURE__ */ jsxs62(Fragment45, { children: [
15691
+ /* @__PURE__ */ jsxs62("g", { transform: "translate(96, 136)", children: [
15491
15692
  /* @__PURE__ */ jsx65("circle", { cx: "12", cy: "-12", r: "12", fill: colors.accent }),
15492
15693
  org && /* @__PURE__ */ jsx65(
15493
15694
  "text",
@@ -15506,6 +15707,7 @@ function BannerEnding({ ir, slide, ctx }) {
15506
15707
  heading.lines.map((line, i) => /* @__PURE__ */ jsx65(
15507
15708
  "text",
15508
15709
  {
15710
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
15509
15711
  x: "96",
15510
15712
  y: headingY + i * heading.lineHeight,
15511
15713
  fontFamily: fonts.heading,
@@ -15542,7 +15744,7 @@ function BannerEnding({ ir, slide, ctx }) {
15542
15744
  strokeWidth: "1.4"
15543
15745
  }
15544
15746
  ),
15545
- contactText && /* @__PURE__ */ jsxs61(Fragment45, { children: [
15747
+ contactText && /* @__PURE__ */ jsxs62(Fragment45, { children: [
15546
15748
  /* @__PURE__ */ jsx65(
15547
15749
  "text",
15548
15750
  {
@@ -15585,7 +15787,7 @@ function BannerEnding({ ir, slide, ctx }) {
15585
15787
  }
15586
15788
 
15587
15789
  // src/svg/archetypes/ending-poster-ending.tsx
15588
- import { Fragment as Fragment46, jsx as jsx66, jsxs as jsxs62 } from "react/jsx-runtime";
15790
+ import { Fragment as Fragment46, jsx as jsx66, jsxs as jsxs63 } from "react/jsx-runtime";
15589
15791
  var CENTER_X3 = 640;
15590
15792
  var ACCENT_BAR_W3 = 60;
15591
15793
  var ACCENT_BAR_H3 = 4;
@@ -15621,10 +15823,11 @@ function PosterEnding({ ir, slide, ctx }) {
15621
15823
  minFontSize: 16
15622
15824
  }) : null;
15623
15825
  const metaY = dividerY + 52;
15624
- return /* @__PURE__ */ jsxs62(Fragment46, { children: [
15826
+ return /* @__PURE__ */ jsxs63(Fragment46, { children: [
15625
15827
  heading.lines.map((line, i) => /* @__PURE__ */ jsx66(
15626
15828
  "text",
15627
15829
  {
15830
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
15628
15831
  x: CENTER_X3,
15629
15832
  y: headingY + i * heading.lineHeight,
15630
15833
  textAnchor: "middle",
@@ -15694,7 +15897,7 @@ function PosterEnding({ ir, slide, ctx }) {
15694
15897
  }
15695
15898
 
15696
15899
  // src/svg/archetypes/ending-tone-adaptive-ending.tsx
15697
- import { Fragment as Fragment47, jsx as jsx67, jsxs as jsxs63 } from "react/jsx-runtime";
15900
+ import { Fragment as Fragment47, jsx as jsx67, jsxs as jsxs64 } from "react/jsx-runtime";
15698
15901
  function hasBgImage4(ir, slide) {
15699
15902
  if (slide.background?.kind !== "asset") return false;
15700
15903
  const assetId = slide.background.asset_id;
@@ -15729,7 +15932,7 @@ function ToneAdaptiveEnding({ ir, slide, ctx }) {
15729
15932
  const contactLabelY = dividerY + 52;
15730
15933
  const contactTextY = dividerY + 88;
15731
15934
  const copyrightY = dividerY + 164;
15732
- return /* @__PURE__ */ jsxs63(Fragment47, { children: [
15935
+ return /* @__PURE__ */ jsxs64(Fragment47, { children: [
15733
15936
  withBg && /* @__PURE__ */ jsx67("rect", { width: "1280", height: "720", fill: "#000000", opacity: "0.32" }),
15734
15937
  org && /* @__PURE__ */ jsx67(
15735
15938
  "text",
@@ -15748,6 +15951,7 @@ function ToneAdaptiveEnding({ ir, slide, ctx }) {
15748
15951
  heading.lines.map((line, i) => /* @__PURE__ */ jsx67(
15749
15952
  "text",
15750
15953
  {
15954
+ "data-truncated": heading.truncated && i === heading.lines.length - 1 ? "1" : void 0,
15751
15955
  x: "64",
15752
15956
  y: headingY + i * heading.lineHeight,
15753
15957
  fontFamily: fonts.heading,
@@ -15772,7 +15976,7 @@ function ToneAdaptiveEnding({ ir, slide, ctx }) {
15772
15976
  strokeWidth: "1.6"
15773
15977
  }
15774
15978
  ),
15775
- contactText && /* @__PURE__ */ jsxs63(Fragment47, { children: [
15979
+ contactText && /* @__PURE__ */ jsxs64(Fragment47, { children: [
15776
15980
  /* @__PURE__ */ jsx67(
15777
15981
  "text",
15778
15982
  {
@@ -15817,7 +16021,7 @@ function ToneAdaptiveEnding({ ir, slide, ctx }) {
15817
16021
  }
15818
16022
 
15819
16023
  // src/svg/archetypes/ending-fashion-ending.tsx
15820
- import { Fragment as Fragment48, jsx as jsx68, jsxs as jsxs64 } from "react/jsx-runtime";
16024
+ import { Fragment as Fragment48, jsx as jsx68, jsxs as jsxs65 } from "react/jsx-runtime";
15821
16025
  function FashionEnding({ ir, slide, ctx }) {
15822
16026
  const org = ir.meta.organization;
15823
16027
  const date = ir.meta.date;
@@ -15842,7 +16046,7 @@ function FashionEnding({ ir, slide, ctx }) {
15842
16046
  const subtitleY = bandY + BAND_H + 54;
15843
16047
  const metaParts = [org, date].filter((v) => Boolean(v));
15844
16048
  const metaLine = metaParts.length > 0 ? fitSvgLine(metaParts.join(" \xB7 "), { maxWidth: 1100, fontSize: 19, minFontSize: 14 }) : null;
15845
- return /* @__PURE__ */ jsxs64(Fragment48, { children: [
16049
+ return /* @__PURE__ */ jsxs65(Fragment48, { children: [
15846
16050
  /* @__PURE__ */ jsx68("rect", { x: 0, y: 0, width: 1280, height: 720, fill: ctx.colors.primary }),
15847
16051
  org && /* @__PURE__ */ jsx68(
15848
16052
  "text",
@@ -15862,6 +16066,7 @@ function FashionEnding({ ir, slide, ctx }) {
15862
16066
  title.lines.map((line, i) => /* @__PURE__ */ jsx68(
15863
16067
  "text",
15864
16068
  {
16069
+ "data-truncated": title.truncated && i === title.lines.length - 1 ? "1" : void 0,
15865
16070
  x: 56,
15866
16071
  y: TITLE_Y2 + i * title.lineHeight,
15867
16072
  fontFamily: ctx.fonts.heading,
@@ -15920,7 +16125,7 @@ var ENDING_ARCHETYPES = {
15920
16125
  };
15921
16126
 
15922
16127
  // src/svg/archetypes/motif-corner-ornament-motif.tsx
15923
- import { Fragment as Fragment49, jsx as jsx69, jsxs as jsxs65 } from "react/jsx-runtime";
16128
+ import { Fragment as Fragment49, jsx as jsx69, jsxs as jsxs66 } from "react/jsx-runtime";
15924
16129
  var ORNAMENT_MARGIN = 40;
15925
16130
  var ORNAMENT_LEN = 20;
15926
16131
  var ORNAMENT_GAP = 4;
@@ -15935,7 +16140,7 @@ function CornerOrnament({
15935
16140
  const outerY = cy + signY * ORNAMENT_MARGIN;
15936
16141
  const innerX = outerX + signX * ORNAMENT_GAP;
15937
16142
  const innerY = outerY + signY * ORNAMENT_GAP;
15938
- return /* @__PURE__ */ jsxs65(Fragment49, { children: [
16143
+ return /* @__PURE__ */ jsxs66(Fragment49, { children: [
15939
16144
  /* @__PURE__ */ jsx69("line", { x1: outerX, y1: outerY, x2: outerX + signX * ORNAMENT_LEN, y2: outerY, stroke, strokeWidth: "1" }),
15940
16145
  /* @__PURE__ */ jsx69("line", { x1: outerX, y1: outerY, x2: outerX, y2: outerY + signY * ORNAMENT_LEN, stroke, strokeWidth: "1" }),
15941
16146
  /* @__PURE__ */ jsx69("line", { x1: innerX, y1: innerY, x2: innerX + signX * ORNAMENT_LEN, y2: innerY, stroke, strokeWidth: "1" }),
@@ -15951,18 +16156,18 @@ function CornerOrnamentMotif({ ir, slide, ctx }) {
15951
16156
  const bottomLeft = /* @__PURE__ */ jsx69(CornerOrnament, { cx: 0, cy: 720, signX: 1, signY: -1, stroke });
15952
16157
  const bottomRight = /* @__PURE__ */ jsx69(CornerOrnament, { cx: 1280, cy: 720, signX: -1, signY: -1, stroke });
15953
16158
  if (variant === "b") {
15954
- return /* @__PURE__ */ jsxs65(Fragment49, { children: [
16159
+ return /* @__PURE__ */ jsxs66(Fragment49, { children: [
15955
16160
  topLeft,
15956
16161
  bottomRight
15957
16162
  ] });
15958
16163
  }
15959
16164
  if (variant === "c") {
15960
- return /* @__PURE__ */ jsxs65(Fragment49, { children: [
16165
+ return /* @__PURE__ */ jsxs66(Fragment49, { children: [
15961
16166
  topRight,
15962
16167
  bottomLeft
15963
16168
  ] });
15964
16169
  }
15965
- return /* @__PURE__ */ jsxs65(Fragment49, { children: [
16170
+ return /* @__PURE__ */ jsxs66(Fragment49, { children: [
15966
16171
  topLeft,
15967
16172
  topRight,
15968
16173
  bottomLeft,
@@ -15991,7 +16196,7 @@ function RailMotif({ ir, slide, ctx }) {
15991
16196
  }
15992
16197
 
15993
16198
  // src/svg/archetypes/motif-banner-motif.tsx
15994
- import { Fragment as Fragment51, jsx as jsx71, jsxs as jsxs66 } from "react/jsx-runtime";
16199
+ import { Fragment as Fragment51, jsx as jsx71, jsxs as jsxs67 } from "react/jsx-runtime";
15995
16200
  var GRID_STROKE_OPACITY = 0.25;
15996
16201
  var GRID_STROKE_OPACITY_CHAPTER = 0.05;
15997
16202
  var GRID_V_XS = [128, 384, 640, 896, 1152];
@@ -16009,7 +16214,7 @@ function BannerMotif({ ir, slide, ctx }) {
16009
16214
  const isChapter = slide.type === "chapter";
16010
16215
  const gridStroke = isChapter ? "#FFFFFF" : ctx.colors.border ?? ctx.colors.muted;
16011
16216
  const gridOpacity = isChapter ? GRID_STROKE_OPACITY_CHAPTER : GRID_STROKE_OPACITY;
16012
- return /* @__PURE__ */ jsxs66(Fragment51, { children: [
16217
+ return /* @__PURE__ */ jsxs67(Fragment51, { children: [
16013
16218
  gridXsFor(variant).map((x) => /* @__PURE__ */ jsx71(
16014
16219
  "line",
16015
16220
  {
@@ -16042,7 +16247,7 @@ function BannerMotif({ ir, slide, ctx }) {
16042
16247
  }
16043
16248
 
16044
16249
  // src/svg/archetypes/motif-poster-motif.tsx
16045
- import { Fragment as Fragment52, jsx as jsx72, jsxs as jsxs67 } from "react/jsx-runtime";
16250
+ import { Fragment as Fragment52, jsx as jsx72, jsxs as jsxs68 } from "react/jsx-runtime";
16046
16251
  var GLOW_MOTIF_CX = 200;
16047
16252
  var GLOW_MOTIF_CY = 560;
16048
16253
  function PosterMotif({ ir, slide, ctx }) {
@@ -16050,7 +16255,7 @@ function PosterMotif({ ir, slide, ctx }) {
16050
16255
  const variant = pickBySeed(cachedDeckSeed(ir), "poster-decor", ["a", "b", "c"]);
16051
16256
  const dotCx = variant === "b" ? 1280 - GLOW_MOTIF_CX : GLOW_MOTIF_CX;
16052
16257
  if (slide.type !== "cover") return null;
16053
- return /* @__PURE__ */ jsxs67(Fragment52, { children: [
16258
+ return /* @__PURE__ */ jsxs68(Fragment52, { children: [
16054
16259
  /* @__PURE__ */ jsx72("circle", { cx: dotCx, cy: GLOW_MOTIF_CY, r: "6", fill: colors.accent }),
16055
16260
  /* @__PURE__ */ jsx72(
16056
16261
  "circle",
@@ -16078,7 +16283,7 @@ function PosterMotif({ ir, slide, ctx }) {
16078
16283
  }
16079
16284
 
16080
16285
  // src/svg/archetypes/motif-tone-adaptive-motif.tsx
16081
- import { Fragment as Fragment53, jsx as jsx73, jsxs as jsxs68 } from "react/jsx-runtime";
16286
+ import { Fragment as Fragment53, jsx as jsx73, jsxs as jsxs69 } from "react/jsx-runtime";
16082
16287
  function hasBgImage5(ir, slide) {
16083
16288
  if (slide.background?.kind !== "asset") return false;
16084
16289
  const assetId = slide.background.asset_id;
@@ -16095,8 +16300,8 @@ var GRADIENT_FIELD_ID = "decor-custom-field";
16095
16300
  var BG_MIXED_6PCT_BLACK = "#F0F0F0";
16096
16301
  function ToneAdaptiveMotif({ ir, slide, ctx }) {
16097
16302
  if (hasExplicitBackground(ir, slide)) return /* @__PURE__ */ jsx73(Fragment53, {});
16098
- return /* @__PURE__ */ jsxs68(Fragment53, { children: [
16099
- /* @__PURE__ */ jsx73("defs", { children: /* @__PURE__ */ jsxs68("linearGradient", { id: GRADIENT_FIELD_ID, x1: "0", y1: "0", x2: "0", y2: "1", children: [
16303
+ return /* @__PURE__ */ jsxs69(Fragment53, { children: [
16304
+ /* @__PURE__ */ jsx73("defs", { children: /* @__PURE__ */ jsxs69("linearGradient", { id: GRADIENT_FIELD_ID, x1: "0", y1: "0", x2: "0", y2: "1", children: [
16100
16305
  /* @__PURE__ */ jsx73("stop", { offset: "0%", stopColor: ctx.colors.bg }),
16101
16306
  /* @__PURE__ */ jsx73("stop", { offset: "100%", stopColor: BG_MIXED_6PCT_BLACK })
16102
16307
  ] }) }),
@@ -16105,7 +16310,7 @@ function ToneAdaptiveMotif({ ir, slide, ctx }) {
16105
16310
  }
16106
16311
 
16107
16312
  // src/svg/archetypes/motif-constellation-motif.tsx
16108
- import { Fragment as Fragment54, jsx as jsx74, jsxs as jsxs69 } from "react/jsx-runtime";
16313
+ import { Fragment as Fragment54, jsx as jsx74, jsxs as jsxs70 } from "react/jsx-runtime";
16109
16314
  function hasExplicitBackground2(ir, slide) {
16110
16315
  const bg = slide.background;
16111
16316
  if (!bg) return false;
@@ -16143,8 +16348,8 @@ function ConstellationMotif({ ir, slide, ctx }) {
16143
16348
  const withBg = hasExplicitBackground2(ir, slide);
16144
16349
  if (withBg) return /* @__PURE__ */ jsx74(Fragment54, {});
16145
16350
  const showEndingMotif = slide.type === "ending";
16146
- return /* @__PURE__ */ jsxs69(Fragment54, { children: [
16147
- /* @__PURE__ */ jsx74("defs", { children: /* @__PURE__ */ jsxs69("linearGradient", { id: GRADIENT_FIELD_ID2, x1: "0", y1: "0", x2: "1", y2: "1", children: [
16351
+ return /* @__PURE__ */ jsxs70(Fragment54, { children: [
16352
+ /* @__PURE__ */ jsx74("defs", { children: /* @__PURE__ */ jsxs70("linearGradient", { id: GRADIENT_FIELD_ID2, x1: "0", y1: "0", x2: "1", y2: "1", children: [
16148
16353
  /* @__PURE__ */ jsx74("stop", { offset: "0%", stopColor: GRADIENT_STOP_START }),
16149
16354
  /* @__PURE__ */ jsx74("stop", { offset: "100%", stopColor: GRADIENT_STOP_END })
16150
16355
  ] }) }),
@@ -16158,7 +16363,7 @@ function ConstellationMotif({ ir, slide, ctx }) {
16158
16363
  fill: `url(#${GRADIENT_FIELD_ID2})`
16159
16364
  }
16160
16365
  ),
16161
- showEndingMotif && /* @__PURE__ */ jsxs69(Fragment54, { children: [
16366
+ showEndingMotif && /* @__PURE__ */ jsxs70(Fragment54, { children: [
16162
16367
  /* @__PURE__ */ jsx74(
16163
16368
  "polyline",
16164
16369
  {
@@ -16175,7 +16380,7 @@ function ConstellationMotif({ ir, slide, ctx }) {
16175
16380
  }
16176
16381
 
16177
16382
  // src/svg/archetypes/motif-campaign-motif.tsx
16178
- import { Fragment as Fragment55, jsx as jsx75, jsxs as jsxs70 } from "react/jsx-runtime";
16383
+ import { Fragment as Fragment55, jsx as jsx75, jsxs as jsxs71 } from "react/jsx-runtime";
16179
16384
  function lcg(seed) {
16180
16385
  let s = seed >>> 0;
16181
16386
  return () => {
@@ -16268,14 +16473,14 @@ function CampaignMotif({ ir, slide, ctx }) {
16268
16473
  }
16269
16474
  }
16270
16475
  }
16271
- return /* @__PURE__ */ jsxs70("g", { children: [
16476
+ return /* @__PURE__ */ jsxs71("g", { children: [
16272
16477
  dark ? /* @__PURE__ */ jsx75("path", { d: dark, fill: "#000000", opacity: 0.18 }) : null,
16273
16478
  light ? /* @__PURE__ */ jsx75("path", { d: light, fill: white, opacity: 0.1 }) : null,
16274
16479
  sweep ? /* @__PURE__ */ jsx75("path", { d: sweep, fill: "#000000", opacity: 0.1 }) : null
16275
16480
  ] });
16276
16481
  };
16277
16482
  if (slide.type === "content") {
16278
- return /* @__PURE__ */ jsxs70(Fragment55, { children: [
16483
+ return /* @__PURE__ */ jsxs71(Fragment55, { children: [
16279
16484
  grainBase(601),
16280
16485
  crayon(1140, 42, 8, 190, 44, pink, 61),
16281
16486
  crayon(-40, 678, -8, 200, 46, blue, 62, 0.7),
@@ -16297,7 +16502,7 @@ function CampaignMotif({ ir, slide, ctx }) {
16297
16502
  }
16298
16503
  return /* @__PURE__ */ jsx75("g", { children: dots });
16299
16504
  };
16300
- const scatter = /* @__PURE__ */ jsxs70(Fragment55, { children: [
16505
+ const scatter = /* @__PURE__ */ jsxs71(Fragment55, { children: [
16301
16506
  /* @__PURE__ */ jsx75("path", { d: "M 890 120 L 906 102 L 918 124 Z", fill: white, opacity: 0.55 }),
16302
16507
  /* @__PURE__ */ jsx75("path", { d: "M 380 590 L 394 574 L 404 596 Z", fill: white, opacity: 0.5 }),
16303
16508
  splat(300, 170, 120, 51, 8),
@@ -16305,7 +16510,7 @@ function CampaignMotif({ ir, slide, ctx }) {
16305
16510
  /* @__PURE__ */ jsx75("path", { d: "M 1050 640 l 14 -8 M 240 110 l -12 9", stroke: white, strokeWidth: 3, strokeLinecap: "round", opacity: 0.4 })
16306
16511
  ] });
16307
16512
  if (variant === "b") {
16308
- return /* @__PURE__ */ jsxs70(Fragment55, { children: [
16513
+ return /* @__PURE__ */ jsxs71(Fragment55, { children: [
16309
16514
  grainBase(701),
16310
16515
  crayon(-60, 26, 6, 420, 88, pink, 71, 0.95),
16311
16516
  crayon(300, -8, -4, 380, 64, blue, 72, 0.8),
@@ -16319,7 +16524,7 @@ function CampaignMotif({ ir, slide, ctx }) {
16319
16524
  ] });
16320
16525
  }
16321
16526
  if (variant === "c") {
16322
- return /* @__PURE__ */ jsxs70(Fragment55, { children: [
16527
+ return /* @__PURE__ */ jsxs71(Fragment55, { children: [
16323
16528
  grainBase(801),
16324
16529
  crayon(-50, 90, 14, 320, 78, pink, 81, 0.9),
16325
16530
  crayon(-30, 170, 24, 240, 44, yellow, 82, 0.75),
@@ -16331,7 +16536,7 @@ function CampaignMotif({ ir, slide, ctx }) {
16331
16536
  scatter
16332
16537
  ] });
16333
16538
  }
16334
- return /* @__PURE__ */ jsxs70(Fragment55, { children: [
16539
+ return /* @__PURE__ */ jsxs71(Fragment55, { children: [
16335
16540
  grainBase(901),
16336
16541
  crayon(-70, 130, -24, 440, 96, pink, 91, 0.95),
16337
16542
  crayon(-50, 250, -32, 350, 62, blue, 92, 0.8),
@@ -16344,7 +16549,7 @@ function CampaignMotif({ ir, slide, ctx }) {
16344
16549
  }
16345
16550
 
16346
16551
  // src/svg/archetypes/motif-bloom-motif.tsx
16347
- import { Fragment as Fragment56, jsx as jsx76, jsxs as jsxs71 } from "react/jsx-runtime";
16552
+ import { Fragment as Fragment56, jsx as jsx76, jsxs as jsxs72 } from "react/jsx-runtime";
16348
16553
  function ridgeLine(a, b, nx, ny, depth, seed) {
16349
16554
  let sd = seed * 2654435761 + 13 >>> 0;
16350
16555
  const rnd = () => (sd = sd * 1664525 + 1013904223 >>> 0, sd / 4294967296);
@@ -16554,7 +16759,7 @@ function BloomMotif({ ir, slide, ctx }) {
16554
16759
  dryD ? /* @__PURE__ */ jsx76("path", { d: dryD, stroke: anchorColor, strokeWidth: 2.4, strokeLinecap: "round", fill: "none", opacity: 0.32 }, "dry") : null,
16555
16760
  broomD ? /* @__PURE__ */ jsx76("path", { d: broomD, stroke: anchorColor, strokeWidth: 1.2, strokeLinecap: "round", fill: "none", opacity: 0.3 }, "broom") : null
16556
16761
  ];
16557
- return /* @__PURE__ */ jsxs71("g", { children: [
16762
+ return /* @__PURE__ */ jsxs72("g", { children: [
16558
16763
  layers,
16559
16764
  grains
16560
16765
  ] });
@@ -16562,7 +16767,7 @@ function BloomMotif({ ir, slide, ctx }) {
16562
16767
  const cornerWash = (a, b, nx, ny, depth, corners, seed, tiers) => {
16563
16768
  let sd = seed * 69069 + 5 >>> 0;
16564
16769
  const rnd = () => (sd = sd * 1664525 + 1013904223 >>> 0, sd / 4294967296);
16565
- return /* @__PURE__ */ jsxs71("g", { children: [
16770
+ return /* @__PURE__ */ jsxs72("g", { children: [
16566
16771
  tiers.map((t, ti) => {
16567
16772
  const L = 2;
16568
16773
  const layers = [];
@@ -16601,21 +16806,21 @@ function BloomMotif({ ir, slide, ctx }) {
16601
16806
  if (rnd() > 0.35) d0 += d;
16602
16807
  else d1 += d;
16603
16808
  }
16604
- return /* @__PURE__ */ jsxs71("g", { children: [
16809
+ return /* @__PURE__ */ jsxs72("g", { children: [
16605
16810
  d0 ? /* @__PURE__ */ jsx76("path", { d: d0, fill: anchorColor, opacity: 0.3 }) : null,
16606
16811
  d1 ? /* @__PURE__ */ jsx76("path", { d: d1, fill: tiers[0].c, opacity: 0.22 }) : null
16607
16812
  ] });
16608
16813
  })()
16609
16814
  ] });
16610
16815
  };
16611
- const sprig = (x, y, s, color, o) => /* @__PURE__ */ jsxs71(Fragment56, { children: [
16816
+ const sprig = (x, y, s, color, o) => /* @__PURE__ */ jsxs72(Fragment56, { children: [
16612
16817
  /* @__PURE__ */ jsx76("path", { d: `M ${x} ${y} Q ${x + 14 * s} ${y - 30 * s} ${x + 10 * s} ${y - 62 * s}`, stroke: color, strokeWidth: 1.2, fill: "none", opacity: o }),
16613
16818
  /* @__PURE__ */ jsx76("path", { d: `M ${x + 7 * s} ${y - 20 * s} q -14 ${-4 * s} -20 ${-16 * s} q 16 ${-2 * s} 20 ${16 * s} Z`, fill: color, opacity: o * 0.8 }),
16614
16819
  /* @__PURE__ */ jsx76("path", { d: `M ${x + 11 * s} ${y - 36 * s} q 15 ${-8 * s} 24 ${-4 * s} q -8 ${12 * s} -24 ${4 * s} Z`, fill: color, opacity: o * 0.7 }),
16615
16820
  /* @__PURE__ */ jsx76("path", { d: `M ${x + 10 * s} ${y - 52 * s} q -12 ${-6 * s} -14 ${-18 * s} q 14 0 14 ${18 * s} Z`, fill: color, opacity: o * 0.75 })
16616
16821
  ] });
16617
16822
  if (slide.type === "content") {
16618
- return /* @__PURE__ */ jsxs71(Fragment56, { children: [
16823
+ return /* @__PURE__ */ jsxs72(Fragment56, { children: [
16619
16824
  wash(1355, 60, 170, 11, apricot, rose, 0.7, 1.55),
16620
16825
  wash(1350, 280, 140, 13, rose, apricot, 0.55, 1.5),
16621
16826
  wash(-75, 620, 180, 12, wisteria, anchorColor, 0.55, 1.45),
@@ -16624,7 +16829,7 @@ function BloomMotif({ ir, slide, ctx }) {
16624
16829
  }
16625
16830
  const variant = pickBySeed(cachedDeckSeed(ir), "bloom-decor", ["a", "b", "c"]);
16626
16831
  if (variant === "b") {
16627
- return /* @__PURE__ */ jsxs71(Fragment56, { children: [
16832
+ return /* @__PURE__ */ jsxs72(Fragment56, { children: [
16628
16833
  wash(-85, 300, 230, 24, wisteria, anchorColor, 0.9, 1.4),
16629
16834
  wash(-60, 560, 190, 27, apricot, rose, 0.8, 1.5),
16630
16835
  wash(-75, 710, 160, 28, rose, apricot, 0.7, 1.45),
@@ -16636,7 +16841,7 @@ function BloomMotif({ ir, slide, ctx }) {
16636
16841
  ] });
16637
16842
  }
16638
16843
  if (variant === "c") {
16639
- return /* @__PURE__ */ jsxs71(Fragment56, { children: [
16844
+ return /* @__PURE__ */ jsxs72(Fragment56, { children: [
16640
16845
  wash(-80, 220, 250, 31, wisteria, anchorColor, 1, 1.35),
16641
16846
  wash(-50, 500, 220, 32, apricot, rose, 0.85, 1.6),
16642
16847
  wash(-70, 690, 180, 36, rose, apricot, 0.75, 1.5),
@@ -16648,7 +16853,7 @@ function BloomMotif({ ir, slide, ctx }) {
16648
16853
  [560, 588, 616, 644, 672, 700].map((x) => /* @__PURE__ */ jsx76("line", { x1: x, y1: 44, x2: x + 12, y2: 44, stroke: wisteria, strokeWidth: 1.4, opacity: 0.35 }, x))
16649
16854
  ] });
16650
16855
  }
16651
- return /* @__PURE__ */ jsxs71(Fragment56, { children: [
16856
+ return /* @__PURE__ */ jsxs72(Fragment56, { children: [
16652
16857
  wash(-80, 220, 250, 31, wisteria, anchorColor, 1, 1.35),
16653
16858
  wash(-50, 500, 220, 32, apricot, rose, 0.85, 1.6),
16654
16859
  wash(-70, 690, 180, 36, rose, apricot, 0.75, 1.5),
@@ -16671,7 +16876,7 @@ function BloomMotif({ ir, slide, ctx }) {
16671
16876
  }
16672
16877
 
16673
16878
  // src/svg/archetypes/motif-classroom-motif.tsx
16674
- import { Fragment as Fragment57, jsx as jsx77, jsxs as jsxs72 } from "react/jsx-runtime";
16879
+ import { Fragment as Fragment57, jsx as jsx77, jsxs as jsxs73 } from "react/jsx-runtime";
16675
16880
  function blobPath(cx, cy, r, s) {
16676
16881
  const f = (v) => Math.round(v * 10) / 10;
16677
16882
  if (s === 1)
@@ -16699,7 +16904,7 @@ function ClassroomMotif({ ir, slide, ctx }) {
16699
16904
  const a = angle * Math.PI / 180;
16700
16905
  const dx = Math.cos(a) * 14;
16701
16906
  const dy = Math.sin(a) * 14;
16702
- return /* @__PURE__ */ jsxs72("g", { stroke: color, strokeWidth: 2.4, strokeLinecap: "round", opacity: 0.55, children: [
16907
+ return /* @__PURE__ */ jsxs73("g", { stroke: color, strokeWidth: 2.4, strokeLinecap: "round", opacity: 0.55, children: [
16703
16908
  /* @__PURE__ */ jsx77("path", { d: `M ${x} ${y} l ${dx} ${dy}`, fill: "none" }),
16704
16909
  /* @__PURE__ */ jsx77("path", { d: `M ${x + 10} ${y - 6} l ${dx} ${dy}`, fill: "none" }),
16705
16910
  /* @__PURE__ */ jsx77("path", { d: `M ${x + 20} ${y - 12} l ${dx} ${dy}`, fill: "none" })
@@ -16712,7 +16917,7 @@ function ClassroomMotif({ ir, slide, ctx }) {
16712
16917
  };
16713
16918
  const ring = (cx, cy, r, color, o = 0.5) => /* @__PURE__ */ jsx77("circle", { cx, cy, r, fill: "none", stroke: color, strokeWidth: 2, opacity: o });
16714
16919
  if (slide.type === "content") {
16715
- return /* @__PURE__ */ jsxs72(Fragment57, { children: [
16920
+ return /* @__PURE__ */ jsxs73(Fragment57, { children: [
16716
16921
  blob(1246, 26, 90, sage, 0.35, 1),
16717
16922
  blob(1280, 90, 60, coral, 0.25, 2),
16718
16923
  blob(20, 700, 84, latte, 0.35, 0),
@@ -16722,7 +16927,7 @@ function ClassroomMotif({ ir, slide, ctx }) {
16722
16927
  }
16723
16928
  const variant = pickBySeed(cachedDeckSeed(ir), "classroom-decor", ["a", "b", "c"]);
16724
16929
  if (variant === "b") {
16725
- return /* @__PURE__ */ jsxs72(Fragment57, { children: [
16930
+ return /* @__PURE__ */ jsxs73(Fragment57, { children: [
16726
16931
  blob(70, 60, 190, blue, 0.3, 0),
16727
16932
  blob(220, 10, 120, sage, 0.35, 2),
16728
16933
  blob(20, 210, 90, coral, 0.28, 1),
@@ -16736,7 +16941,7 @@ function ClassroomMotif({ ir, slide, ctx }) {
16736
16941
  ] });
16737
16942
  }
16738
16943
  if (variant === "c") {
16739
- return /* @__PURE__ */ jsxs72(Fragment57, { children: [
16944
+ return /* @__PURE__ */ jsxs73(Fragment57, { children: [
16740
16945
  blob(140, -20, 150, sage, 0.35, 1),
16741
16946
  blob(420, -50, 180, latte, 0.4, 0),
16742
16947
  blob(760, -30, 150, coral, 0.26, 2),
@@ -16749,7 +16954,7 @@ function ClassroomMotif({ ir, slide, ctx }) {
16749
16954
  ring(1180, 150, 12, latte, 0.55)
16750
16955
  ] });
16751
16956
  }
16752
- return /* @__PURE__ */ jsxs72(Fragment57, { children: [
16957
+ return /* @__PURE__ */ jsxs73(Fragment57, { children: [
16753
16958
  blob(60, 50, 160, sage, 0.35, 0),
16754
16959
  blob(210, 0, 110, coral, 0.25, 2),
16755
16960
  blob(1230, 40, 170, coral, 0.3, 1),
@@ -16769,29 +16974,29 @@ function ClassroomMotif({ ir, slide, ctx }) {
16769
16974
  }
16770
16975
 
16771
16976
  // src/svg/archetypes/motif-ink-motif.tsx
16772
- import { Fragment as Fragment58, jsx as jsx78, jsxs as jsxs73 } from "react/jsx-runtime";
16977
+ import { Fragment as Fragment58, jsx as jsx78, jsxs as jsxs74 } from "react/jsx-runtime";
16773
16978
  function InkMotif({ slide, ir, ctx }) {
16774
16979
  const { colors } = ctx;
16775
16980
  const strong = slide.type === "cover" || slide.type === "chapter";
16776
16981
  const variant = pickBySeed(cachedDeckSeed(ir), "ink-decor", ["a", "b", "c"]);
16777
16982
  const sealText = (ir.meta.organization ?? "").slice(0, 6);
16778
- return /* @__PURE__ */ jsxs73(Fragment58, { children: [
16779
- slide.type !== "chapter" && /* @__PURE__ */ jsxs73(Fragment58, { children: [
16983
+ return /* @__PURE__ */ jsxs74(Fragment58, { children: [
16984
+ slide.type !== "chapter" && /* @__PURE__ */ jsxs74(Fragment58, { children: [
16780
16985
  /* @__PURE__ */ jsx78("line", { x1: 48, y1: 34, x2: 1232, y2: 34, stroke: colors.border, strokeWidth: 1.5 }),
16781
16986
  /* @__PURE__ */ jsx78("line", { x1: 48, y1: 664, x2: 1232, y2: 664, stroke: colors.border, strokeWidth: 1.5 })
16782
16987
  ] }),
16783
- strong && /* @__PURE__ */ jsxs73(Fragment58, { children: [
16784
- variant === "a" && /* @__PURE__ */ jsxs73(Fragment58, { children: [
16988
+ strong && /* @__PURE__ */ jsxs74(Fragment58, { children: [
16989
+ variant === "a" && /* @__PURE__ */ jsxs74(Fragment58, { children: [
16785
16990
  /* @__PURE__ */ jsx78("path", { d: "M -60 720 Q 150 545 380 630 Q 560 692 700 720 Z", fill: colors.primary, opacity: 0.1 }),
16786
16991
  /* @__PURE__ */ jsx78("path", { d: "M -60 720 Q 110 590 300 655 Q 440 700 540 720 Z", fill: colors.primary, opacity: 0.14 }),
16787
16992
  /* @__PURE__ */ jsx78("path", { d: "M 180 720 Q 430 585 660 665 Q 800 710 900 720 Z", fill: colors.primary, opacity: 0.06 })
16788
16993
  ] }),
16789
- variant === "b" && /* @__PURE__ */ jsxs73(Fragment58, { children: [
16994
+ variant === "b" && /* @__PURE__ */ jsxs74(Fragment58, { children: [
16790
16995
  /* @__PURE__ */ jsx78("path", { d: "M 1340 720 Q 1130 545 900 630 Q 720 692 580 720 Z", fill: colors.primary, opacity: 0.1 }),
16791
16996
  /* @__PURE__ */ jsx78("path", { d: "M 1340 720 Q 1170 590 980 655 Q 840 700 740 720 Z", fill: colors.primary, opacity: 0.14 }),
16792
16997
  /* @__PURE__ */ jsx78("path", { d: "M 1100 720 Q 850 585 620 665 Q 480 710 380 720 Z", fill: colors.primary, opacity: 0.06 })
16793
16998
  ] }),
16794
- variant === "c" && /* @__PURE__ */ jsxs73(Fragment58, { children: [
16999
+ variant === "c" && /* @__PURE__ */ jsxs74(Fragment58, { children: [
16795
17000
  /* @__PURE__ */ jsx78("path", { d: "M -60 720 Q 320 618 720 668 Q 1040 704 1340 690 L 1340 720 Z", fill: colors.primary, opacity: 0.09 }),
16796
17001
  /* @__PURE__ */ jsx78("path", { d: "M -60 720 Q 240 650 560 690 Q 800 716 980 720 Z", fill: colors.primary, opacity: 0.13 })
16797
17002
  ] }),
@@ -16809,7 +17014,7 @@ function InkMotif({ slide, ir, ctx }) {
16809
17014
  },
16810
17015
  i
16811
17016
  )),
16812
- slide.type === "cover" && /* @__PURE__ */ jsxs73(Fragment58, { children: [
17017
+ slide.type === "cover" && /* @__PURE__ */ jsxs74(Fragment58, { children: [
16813
17018
  /* @__PURE__ */ jsx78("rect", { x: 1170, y: 608, width: 32, height: 32, rx: 3, fill: colors.accent }),
16814
17019
  /* @__PURE__ */ jsx78(
16815
17020
  "rect",
@@ -16829,7 +17034,7 @@ function InkMotif({ slide, ir, ctx }) {
16829
17034
  }
16830
17035
 
16831
17036
  // src/svg/archetypes/motif-luxe-motif.tsx
16832
- import { Fragment as Fragment59, jsx as jsx79, jsxs as jsxs74 } from "react/jsx-runtime";
17037
+ import { Fragment as Fragment59, jsx as jsx79, jsxs as jsxs75 } from "react/jsx-runtime";
16833
17038
  function LuxeMotif({ ir, slide, ctx }) {
16834
17039
  const gold = ctx.colors.primary;
16835
17040
  if (slide.type === "chapter") return null;
@@ -16839,21 +17044,21 @@ function LuxeMotif({ ir, slide, ctx }) {
16839
17044
  return /* @__PURE__ */ jsx79("line", { x1: 1160, y1: 48, x2: 1232, y2: 48, stroke: gold, strokeWidth: 1.2, opacity: 0.65 });
16840
17045
  }
16841
17046
  if (variant === "b") {
16842
- return /* @__PURE__ */ jsxs74(Fragment59, { children: [
17047
+ return /* @__PURE__ */ jsxs75(Fragment59, { children: [
16843
17048
  /* @__PURE__ */ jsx79("line", { x1: 96, y1: 44, x2: 1184, y2: 44, stroke: gold, strokeWidth: 1, opacity: 0.5 }),
16844
17049
  /* @__PURE__ */ jsx79("circle", { cx: 96, cy: 44, r: 3.5, fill: gold }),
16845
17050
  /* @__PURE__ */ jsx79("circle", { cx: 1184, cy: 44, r: 3.5, fill: gold })
16846
17051
  ] });
16847
17052
  }
16848
17053
  if (variant === "c") {
16849
- return /* @__PURE__ */ jsxs74(Fragment59, { children: [
17054
+ return /* @__PURE__ */ jsxs75(Fragment59, { children: [
16850
17055
  /* @__PURE__ */ jsx79("path", { d: "M 56 72 H 128 M 56 72 V 144", stroke: gold, strokeWidth: 1.2, fill: "none", opacity: 0.6 }),
16851
17056
  /* @__PURE__ */ jsx79("path", { d: "M 1224 72 H 1152 M 1224 72 V 144", stroke: gold, strokeWidth: 1.2, fill: "none", opacity: 0.6 }),
16852
17057
  /* @__PURE__ */ jsx79("path", { d: "M 56 648 H 128 M 56 648 V 576", stroke: gold, strokeWidth: 1.2, fill: "none", opacity: 0.6 }),
16853
17058
  /* @__PURE__ */ jsx79("path", { d: "M 1224 648 H 1152 M 1224 648 V 576", stroke: gold, strokeWidth: 1.2, fill: "none", opacity: 0.6 })
16854
17059
  ] });
16855
17060
  }
16856
- return /* @__PURE__ */ jsxs74(Fragment59, { children: [
17061
+ return /* @__PURE__ */ jsxs75(Fragment59, { children: [
16857
17062
  /* @__PURE__ */ jsx79("path", { d: "M 1224 56 H 1080 M 1224 56 V 200", stroke: gold, strokeWidth: 1.2, fill: "none", opacity: 0.6 }),
16858
17063
  /* @__PURE__ */ jsx79("path", { d: "M 1208 72 H 1096 M 1208 72 V 184", stroke: gold, strokeWidth: 0.8, fill: "none", opacity: 0.35 }),
16859
17064
  /* @__PURE__ */ jsx79("circle", { cx: 64, cy: 648, r: 3.5, fill: gold }),
@@ -16863,7 +17068,7 @@ function LuxeMotif({ ir, slide, ctx }) {
16863
17068
  }
16864
17069
 
16865
17070
  // src/svg/archetypes/motif-enterprise-motif.tsx
16866
- import { Fragment as Fragment60, jsx as jsx80, jsxs as jsxs75 } from "react/jsx-runtime";
17071
+ import { Fragment as Fragment60, jsx as jsx80, jsxs as jsxs76 } from "react/jsx-runtime";
16867
17072
  function EnterpriseMotif({ ir, slide, ctx }) {
16868
17073
  const ikb = ctx.colors.primary;
16869
17074
  if (slide.type === "chapter") return null;
@@ -16873,13 +17078,13 @@ function EnterpriseMotif({ ir, slide, ctx }) {
16873
17078
  return /* @__PURE__ */ jsx80("rect", { x: 1216, y: 44, width: 10, height: 10, fill: ikb, opacity: 0.85 });
16874
17079
  }
16875
17080
  if (variant === "b") {
16876
- return /* @__PURE__ */ jsxs75(Fragment60, { children: [
17081
+ return /* @__PURE__ */ jsxs76(Fragment60, { children: [
16877
17082
  /* @__PURE__ */ jsx80("rect", { x: 0, y: 180, width: 6, height: 360, fill: ikb }),
16878
17083
  /* @__PURE__ */ jsx80("rect", { x: 1200, y: 624, width: 24, height: 24, fill: ikb })
16879
17084
  ] });
16880
17085
  }
16881
17086
  if (variant === "c") {
16882
- return /* @__PURE__ */ jsxs75(Fragment60, { children: [
17087
+ return /* @__PURE__ */ jsxs76(Fragment60, { children: [
16883
17088
  /* @__PURE__ */ jsx80("rect", { x: 56, y: 56, width: 28, height: 28, fill: ikb }),
16884
17089
  /* @__PURE__ */ jsx80("rect", { x: 96, y: 56, width: 12, height: 12, fill: ikb, opacity: 0.45 }),
16885
17090
  /* @__PURE__ */ jsx80("rect", { x: 1196, y: 632, width: 28, height: 28, fill: ikb }),
@@ -16903,21 +17108,21 @@ function EnterpriseMotif({ ir, slide, ctx }) {
16903
17108
  }
16904
17109
 
16905
17110
  // src/svg/archetypes/motif-heritage-motif.tsx
16906
- import { Fragment as Fragment61, jsx as jsx81, jsxs as jsxs76 } from "react/jsx-runtime";
17111
+ import { Fragment as Fragment61, jsx as jsx81, jsxs as jsxs77 } from "react/jsx-runtime";
16907
17112
  function HeritageMotif({ ir, slide, ctx }) {
16908
17113
  const wine = ctx.colors.primary;
16909
17114
  const caramel = ctx.colors.accent;
16910
17115
  if (slide.type === "chapter") return null;
16911
17116
  const variant = pickBySeed(cachedDeckSeed(ir), "heritage-decor", ["a", "b", "c"]);
16912
17117
  if (slide.type !== "cover") {
16913
- return /* @__PURE__ */ jsxs76(Fragment61, { children: [
17118
+ return /* @__PURE__ */ jsxs77(Fragment61, { children: [
16914
17119
  /* @__PURE__ */ jsx81("path", { d: "M 1216 44 L 1230 58 L 1216 72 L 1202 58 Z", fill: "none", stroke: wine, strokeWidth: 1.2, opacity: 0.55 }),
16915
17120
  /* @__PURE__ */ jsx81("path", { d: "M 1216 51 L 1223 58 L 1216 65 L 1209 58 Z", fill: "none", stroke: caramel, strokeWidth: 0.9, opacity: 0.5 })
16916
17121
  ] });
16917
17122
  }
16918
17123
  if (variant === "b") {
16919
17124
  const stud = (cx, cy) => `M ${cx} ${cy - 9} L ${cx + 9} ${cy} L ${cx} ${cy + 9} L ${cx - 9} ${cy} Z`;
16920
- return /* @__PURE__ */ jsxs76(Fragment61, { children: [
17125
+ return /* @__PURE__ */ jsxs77(Fragment61, { children: [
16921
17126
  /* @__PURE__ */ jsx81("path", { d: stud(56, 56), fill: caramel, opacity: 0.7 }),
16922
17127
  /* @__PURE__ */ jsx81("path", { d: stud(1224, 56), fill: caramel, opacity: 0.7 }),
16923
17128
  /* @__PURE__ */ jsx81("path", { d: stud(56, 664), fill: caramel, opacity: 0.7 }),
@@ -16925,12 +17130,12 @@ function HeritageMotif({ ir, slide, ctx }) {
16925
17130
  ] });
16926
17131
  }
16927
17132
  if (variant === "c") {
16928
- return /* @__PURE__ */ jsxs76(Fragment61, { children: [
17133
+ return /* @__PURE__ */ jsxs77(Fragment61, { children: [
16929
17134
  /* @__PURE__ */ jsx81("path", { d: "M 40 96 V 624 M 48 96 V 624", stroke: caramel, strokeWidth: 1, fill: "none", opacity: 0.5 }),
16930
17135
  /* @__PURE__ */ jsx81("path", { d: "M 1232 96 V 624 M 1240 96 V 624", stroke: caramel, strokeWidth: 1, fill: "none", opacity: 0.5 })
16931
17136
  ] });
16932
17137
  }
16933
- return /* @__PURE__ */ jsxs76(Fragment61, { children: [
17138
+ return /* @__PURE__ */ jsxs77(Fragment61, { children: [
16934
17139
  /* @__PURE__ */ jsx81("path", { d: "M 640 36 L 664 58 L 640 80 L 616 58 Z", fill: "none", stroke: wine, strokeWidth: 1.4, opacity: 0.6 }),
16935
17140
  /* @__PURE__ */ jsx81("path", { d: "M 640 46 L 654 58 L 640 70 L 626 58 Z", fill: "none", stroke: caramel, strokeWidth: 1, opacity: 0.5 }),
16936
17141
  /* @__PURE__ */ jsx81("line", { x1: 480, y1: 58, x2: 600, y2: 58, stroke: caramel, strokeWidth: 1, opacity: 0.4 }),
@@ -17029,7 +17234,7 @@ function resolveEffectiveLayoutBodyCapacity(ir, slide, index) {
17029
17234
  }
17030
17235
 
17031
17236
  // src/svg/FullSlideSvg.tsx
17032
- import { jsx as jsx82, jsxs as jsxs77 } from "react/jsx-runtime";
17237
+ import { jsx as jsx82, jsxs as jsxs78 } from "react/jsx-runtime";
17033
17238
  function resolveBackgroundHex(spec, surfaceFallback) {
17034
17239
  if (spec.kind === "color") return spec.value;
17035
17240
  if (spec.kind === "gradient") return spec.from;
@@ -17110,7 +17315,7 @@ function FullSlideSvg({
17110
17315
  strategy,
17111
17316
  previousEffectiveLayoutId
17112
17317
  );
17113
- return /* @__PURE__ */ jsxs77(
17318
+ return /* @__PURE__ */ jsxs78(
17114
17319
  "svg",
17115
17320
  {
17116
17321
  viewBox: `0 0 ${CANVAS_W_PX} ${CANVAS_H_PX}`,
@@ -17203,8 +17408,42 @@ function fnv1a(bytes) {
17203
17408
  return (h >>> 0).toString(16);
17204
17409
  }
17205
17410
 
17206
- // src/pptx/package-audit.ts
17411
+ // src/pptx/pptx-ea-fonts.ts
17207
17412
  import JSZip4 from "jszip";
17413
+ var PPTX_MIME2 = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
17414
+ var SLIDE_PART_RE3 = /^ppt\/slides\/slide\d+\.xml$/;
17415
+ var LATIN_EA_RE = /<a:latin typeface="([^"]*)"([^>]*?)\/>(?:<a:ea typeface="[^"]*"([^>]*?)\/>)?/g;
17416
+ function patchEaFontsInXml(xml) {
17417
+ return xml.replace(
17418
+ LATIN_EA_RE,
17419
+ (_full, latinFace, latinAttrs, eaAttrs) => `<a:latin typeface="${latinFace}"${latinAttrs}/><a:ea typeface="${eaFontFaceFor(latinFace)}"${eaAttrs ?? ""}/>`
17420
+ );
17421
+ }
17422
+ async function applyEaFontFaces(pptx) {
17423
+ const asBlob = pptx instanceof Blob ? pptx : new Blob([pptx], { type: PPTX_MIME2 });
17424
+ try {
17425
+ const zip = await JSZip4.loadAsync(await asBlob.arrayBuffer());
17426
+ const slidePaths = Object.keys(zip.files).filter((p) => SLIDE_PART_RE3.test(p) && !zip.files[p].dir);
17427
+ if (slidePaths.length === 0) return asBlob;
17428
+ let changed = false;
17429
+ for (const path of slidePaths) {
17430
+ const raw = await zip.files[path].async("string");
17431
+ const patched = patchEaFontsInXml(raw);
17432
+ if (patched !== raw) {
17433
+ zip.file(path, patched);
17434
+ changed = true;
17435
+ }
17436
+ }
17437
+ if (!changed) return asBlob;
17438
+ const ab = await zip.generateAsync({ type: "arraybuffer", compression: "DEFLATE" });
17439
+ return new Blob([ab], { type: PPTX_MIME2 });
17440
+ } catch {
17441
+ return asBlob;
17442
+ }
17443
+ }
17444
+
17445
+ // src/pptx/package-audit.ts
17446
+ import JSZip5 from "jszip";
17208
17447
 
17209
17448
  // src/pptx/package-reader.ts
17210
17449
  function normalizeZipPath(path) {
@@ -17298,7 +17537,7 @@ var ROOT_RELS_PART = "_rels/.rels";
17298
17537
  var PRESENTATION_PART = "ppt/presentation.xml";
17299
17538
  var CORE_PARTS = [CONTENT_TYPES_PART, ROOT_RELS_PART, PRESENTATION_PART];
17300
17539
  var SLIDE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide";
17301
- var SLIDE_PART_RE3 = /^ppt\/slides\/slide\d+\.xml$/;
17540
+ var SLIDE_PART_RE4 = /^ppt\/slides\/slide\d+\.xml$/;
17302
17541
  function violation(rule, message) {
17303
17542
  return { rule, message };
17304
17543
  }
@@ -17344,7 +17583,7 @@ async function checkSlideListConsistency(reader) {
17344
17583
  const presRels = await reader.readRelationships(PRESENTATION_PART);
17345
17584
  const slideRelsById = /* @__PURE__ */ new Map();
17346
17585
  for (const rel of presRels) if (rel.type === SLIDE_REL_TYPE) slideRelsById.set(rel.id, rel);
17347
- const slideParts = reader.listParts().filter((p) => SLIDE_PART_RE3.test(p));
17586
+ const slideParts = reader.listParts().filter((p) => SLIDE_PART_RE4.test(p));
17348
17587
  const slidePartSet = new Set(slideParts);
17349
17588
  if (sldRIds.length !== slideRelsById.size) {
17350
17589
  violations.push(
@@ -17520,7 +17759,7 @@ function checkAnimationTargets(doc, slidePart) {
17520
17759
  }
17521
17760
  async function checkSlideParts(reader) {
17522
17761
  const violations = [];
17523
- const slideParts = reader.listParts().filter((p) => SLIDE_PART_RE3.test(p)).sort();
17762
+ const slideParts = reader.listParts().filter((p) => SLIDE_PART_RE4.test(p)).sort();
17524
17763
  for (const slidePart of slideParts) {
17525
17764
  let doc;
17526
17765
  try {
@@ -17562,12 +17801,12 @@ ${lines.join("\n")}`;
17562
17801
  }
17563
17802
  async function auditPptxPackage(input) {
17564
17803
  let zip;
17565
- if (input instanceof JSZip4) {
17804
+ if (input instanceof JSZip5) {
17566
17805
  zip = input;
17567
17806
  } else {
17568
17807
  try {
17569
17808
  const bytes = input instanceof Blob ? await input.arrayBuffer() : input;
17570
- zip = await JSZip4.loadAsync(bytes);
17809
+ zip = await JSZip5.loadAsync(bytes);
17571
17810
  } catch (e) {
17572
17811
  throw new PptfastError(
17573
17812
  `pptx package audit failed \u2014 invariant "zip-unreadable": the generated package is not a readable zip archive (${e.message})`
@@ -17580,7 +17819,7 @@ async function auditPptxPackage(input) {
17580
17819
  }
17581
17820
 
17582
17821
  // src/pptx/generate.ts
17583
- var PPTX_MIME2 = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
17822
+ var PPTX_MIME3 = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
17584
17823
  async function generatePptxBlob(input) {
17585
17824
  const { kind: _kind, ...irInput } = input;
17586
17825
  const ir = await inlinePptxAssets(PptxIRSchema.parse(irInput));
@@ -17598,8 +17837,9 @@ async function generatePptxBlob(input) {
17598
17837
  });
17599
17838
  const rawBlob = await pptx.write({ outputType: "blob" });
17600
17839
  const gradientBlob = await applyGradientFills(rawBlob, gradientPatches);
17840
+ const eaFontBlob = await applyEaFontFaces(gradientBlob);
17601
17841
  const transitionBlob = await applySlideTransitions(
17602
- gradientBlob,
17842
+ eaFontBlob,
17603
17843
  ir.meta.animation?.transition ?? "fade"
17604
17844
  );
17605
17845
  const elementAnimBlob = ir.meta.animation?.elements === "auto" ? await applyElementAnimations(
@@ -17608,7 +17848,7 @@ async function generatePptxBlob(input) {
17608
17848
  ) : transitionBlob;
17609
17849
  let zip;
17610
17850
  try {
17611
- zip = await JSZip5.loadAsync(await elementAnimBlob.arrayBuffer());
17851
+ zip = await JSZip6.loadAsync(await elementAnimBlob.arrayBuffer());
17612
17852
  } catch (e) {
17613
17853
  throw new PptfastError(
17614
17854
  `pptx package audit failed \u2014 invariant "zip-unreadable": the generated package is not a readable zip archive (${e.message})`
@@ -17623,7 +17863,7 @@ async function generatePptxBlob(input) {
17623
17863
  await auditPptxPackage(zip);
17624
17864
  if (!changed) return elementAnimBlob;
17625
17865
  const ab = await zip.generateAsync({ type: "arraybuffer", compression: "DEFLATE" });
17626
- return new Blob([ab], { type: PPTX_MIME2 });
17866
+ return new Blob([ab], { type: PPTX_MIME3 });
17627
17867
  }
17628
17868
 
17629
17869
  // src/svg/audit/capacity.ts
@@ -17639,6 +17879,118 @@ var CAPACITY = {
17639
17879
  * headingMaxChars = floor(58.71 / 1.2) = floor(48.93) = 48
17640
17880
  */
17641
17881
  headingMaxChars: 48,
17882
+ /**
17883
+ * bullets 几何硬截断上界(借鉴波任务 2,2026-07-21)。区别于
17884
+ * `PACING_BUDGETS[pacing].bullets.maxUnitsPerItem`(30/40/48,编辑性「精简
17885
+ * 到 2 行内」建议值,本任务不动)——这是与 pacing 无关的物理硬界:过线真的
17886
+ * 会被渲染器截断(省略号丢字),不是「显得啰嗦」。
17887
+ *
17888
+ * 起因:借鉴波事实报告 Q3 节边界扫描证明,旧的「任意 finding 都硬阻断」
17889
+ * 设计下,validateIr 在 44 CJK 字处拒绝生成,而 6 组 主题×排布 组合下真实
17890
+ * 渲染首次截断都在 156 字——约 3.5 倍差距,且该差距是结构性的(不分
17891
+ * CJK/Latin),源头是编辑性预算从未对齐 `bullets.tsx` 真正的渲染安全网。
17892
+ *
17893
+ * `bullets.tsx` 的安全网是两个与 pacing 无关的定值:`MIN_FONT = 14`(收缩
17894
+ * 地板)与 `maxLines: 2`(`layoutItems` 传给 `layoutSvgText` 的换行上限)。
17895
+ * 起始字号由 pacing 决定(`bodyBaselinePx` 20/24/32),但一旦某条要点把
17896
+ * 同组件内共享字号压到地板,容量就只由「地板字号 + 可用宽度」决定,与起始
17897
+ * 字号无关——这正是任务简报「2 行 × 24→14 收缩 × 实测盒宽」公式的来源。
17898
+ *
17899
+ * 实测盒宽(本文件既有「内容区最窄矩形」基准的复用,非新测):
17900
+ * `content-narrow-column.tsx`(magazine 主题窄栏版式)`COLUMN_W = 880`,
17901
+ * 是本文件已确认的全局最窄单栏内容区(见文件头「magazine 窄栏复核」)。
17902
+ * two_column 对半分(`layout.ts` `COLUMN_GAP = 32`):
17903
+ * twoColumnW = (880 - 32) / 2 = 424px
17904
+ * (1/3 宽的 `aside` 侧栏更窄,但该 arrangement 的 schema 注释明确写着侧栏
17905
+ * 留给 callout/quote/kpi「大号观点」,不是 bullets 的常规去处——按下面
17906
+ * 「已知缺口」记录,不并入最坏宽度,否则会把上界拖到比 pacing 编辑预算
17907
+ * 还紧,违背「error 应严格宽于每档 warn」的设计目标。)
17908
+ *
17909
+ * `bullets.tsx` `TEXT_INDENT = 26`("default" 样式项目符号圆点的左缩进,
17910
+ * 是三种缩进约定里最窄的一档——"numbered"/"checklist" 不占用额外
17911
+ * maxWidth,只在文本本身吃掉 1-2 个前缀 unit,量级相近但更难封闭推导,
17912
+ * 取 "default" 已是更保守的一侧):
17913
+ * maxWidth = 424 - 26 = 398px
17914
+ * capacity = floor(2 行 * 398 / MIN_FONT(14)) = floor(56.86) = 56 units
17915
+ *
17916
+ * 实测复核(非纯公式外推):`installNodePlatform()` +
17917
+ * `renderSlideSvg()` 对 magazine 主题、`layout: "narrow-column"`、
17918
+ * `arrangement: "two_column"`(两个真实 bullets 组件,避免 <2 块退化单栏
17919
+ * 的陷阱)、"default" 样式,逐字符扫描 CJK 长度:56 字零 `data-truncated`,
17920
+ * 57 字首次出现——与上面 floor(56.86)=56 的推导一致(在 1 unit 内)。
17921
+ *
17922
+ * 安全余量(简报明确要求「留安全余量」):套用当时全仓唯一的既有渲染安全
17923
+ * 折扣先例(`code.tsx` 的 `MONO_WIDTH_SAFETY`,同一种「给估算器打折」
17924
+ * 思路,不另造新系数)取值 0.9:
17925
+ * itemOverflowUnits = floor(56 * 0.9) = floor(50.4) = 50
17926
+ *
17927
+ * 与三档 pacing 的 `bullet_item_long` warn 阈值(30/40/48)比较:50 严格
17928
+ * 大于全部三档,包括最紧的 dense(48)——dual-threshold 的「error 恒宽于
17929
+ * warn」在最紧档也成立,只是余量最小(2 units),符合 dense pacing 本身
17930
+ * 就是「更贴边」这一档位语义。
17931
+ *
17932
+ * **借用值澄清(borrow-wave Task 3,2026-07-21,修复轮更新)**:`code.tsx`
17933
+ * 的 `MONO_WIDTH_SAFETY` 经过两轮变动。首轮(同日稍早)用真机 Consolas
17934
+ * 数据重新校准为 0.82(原 0.9 的校准对象是 Menlo,非真身导出字体)。
17935
+ * 修复轮(审校发现首轮的「比例加权估算 × 安全系数」方案对深缩进代码行
17936
+ * 有结构性失效——8/16/24/32 空格缩进的真实偏差 +44.69%~+52.97%,远超
17937
+ * 0.82 系数留出的 ~22% 余量,见 code.tsx 自身推导注释与
17938
+ * task-3-review.md Important-2)换成精确模型:`resolveLayout` 直接按
17939
+ * Consolas 实测的逐字符统一前进宽度(0.5498em/字,见 svg-text-layout.ts
17940
+ * `measureMonoTextUnits` 推导注释)计算,不再有比例估算误差需要安全系数
17941
+ * 去覆盖,`MONO_WIDTH_SAFETY` 因此改为 1.0(不留系数——本任务的替身字体
17942
+ * 变异量测数据不支持任何非零残余量,见 code.tsx 该常量自己的推导注释)。
17943
+ * 上面这条 bullets 容量推导借用的始终只是「0.9 这个具体数字」作为一次性
17944
+ * 安全折扣,不是对 `MONO_WIDTH_SAFETY` 常量的引用——CJK bullets 容量与
17945
+ * mono 字体度量是两件不相关的事,只是曾经共用过同一个圆整系数。
17946
+ * `itemOverflowUnits = 50` 这个结论本身不随 `MONO_WIDTH_SAFETY` 任何一轮
17947
+ * 改变而改变,此处不随之重算,如实记录来源以免误读为仍在引用 `code.tsx`
17948
+ * 的当前值(该常量现在已不再是「打折系数」语义,借用关系至此彻底脱钩)。
17949
+ *
17950
+ * **Truncation-visibility 修复轮复核(2026-07-22,上面这处已知缺口已修)**:
17951
+ * 缺口根因是 `bullets.tsx` 把前缀("1. "/"☐ ")和条目正文拼成同一个字符串
17952
+ * 再喂给 `layoutSvgText`——前缀里那一个空格会把 `svg-text-layout.ts`
17953
+ * `tokenize()` 从逐字符分词误判成空格分词(纯 CJK 正文没有其他空格,拼接
17954
+ * 后整段只切出「前缀」「一整块正文」两个"词"),贪心换行随即把整行预算
17955
+ * 耗在 1-2 字符的前缀上,浪费掉一整行——真实截断边界因此腰斩到约
17956
+ * plain/default/divided 家族(约 57-61)的一半:numbered 约 30-31 units,
17957
+ * checklist 约 23-24 units(探针测法同下)。
17958
+ *
17959
+ * 修复选在组合层(composition seam,`bullets.tsx` 自己),不动共享的
17960
+ * `tokenize()`:前缀不再进入 wrap/truncate 数学,只用条目正文本身走
17961
+ * `layoutSvgText`(纯 CJK 正文因此照常落回逐字符分词),预留出前缀宽度后
17962
+ * 单独拼回首行渲染——`bullets.tsx` 是全仓唯一在 `layoutSvgText` 调用前
17963
+ * 拼接「短、带空格字面量前缀 + 任意(可能无空格的 CJK)调用方内容」的
17964
+ * 组件,改这里不影响 heading/paragraph/kpi/citation/icon-cards/steps/
17965
+ * verdict-banner 共享的同一套 wrap 引擎。
17966
+ *
17967
+ * 同一测法(`installNodePlatform()` + `renderSlideSvg()`,magazine 主题、
17968
+ * `layout: "narrow-column"`、`arrangement: "two_column"`)复测五种样式,
17969
+ * 逐字符扫描 CJK 长度(数字为「零 `data-truncated` 的最长字数 / 首次出现
17970
+ * 的字数」):
17971
+ * - plain: 60 / 61(不变)
17972
+ * - default: 56 / 57(不变,与上面 floor(56.86)=56 的公式推导一致)
17973
+ * - divided: 60 / 61(不变)
17974
+ * - numbered: 56 / 57(原 30 / 31)
17975
+ * - checklist: 58 / 59(原 23 / 24)
17976
+ *
17977
+ * numbered/checklist 的真实边界从「约为 plain 家族的一半」收敛到「贴着
17978
+ * plain 家族」——差距即前缀自身的预留宽度(1-2 units 量级:numbered
17979
+ * "1. " ≈1.37 units、checklist "☐ " ≈0.81 units,乘以 MIN_FONT=14 后
17980
+ * 换算成 px 从 `maxWidth` 里扣掉),不再是结构性缺口。**结论:五种样式的
17981
+ * 边界现在全部 ≥56,严格高于 `itemOverflowUnits=50`——本常量对全部 5 种
17982
+ * bullets 样式现在都是滴水不漏的硬界**,`ir-quality.ts` 的
17983
+ * `bullet_item_overflow` 硬校验错误(本就对全部样式统一套用同一个
17984
+ * `measureTextUnits(item)` 上界,从未按样式分支)现在真正兑现了它的承诺:
17985
+ * 只要某条要点没触发这条 error,它就不会在渲染时被截断,不分样式——
17986
+ * README.md/README.zh-CN.md/skills/pptfast/SKILL.md 已同步删除
17987
+ * numbered/checklist「可能在 validate 报错前先被截断,靠 `pptfast audit`
17988
+ * 兜底」的例外措辞。`bullets.tsx` 对全部样式无条件都会设置的
17989
+ * `data-truncated="1"`(真正超出新边界的条目照常触发)作为渲染期兜底信号
17990
+ * 保留,未受这次改动影响——具体机制见该文件 `layoutItems` 自己的推导
17991
+ * 注释。
17992
+ */
17993
+ bullets: { itemOverflowUnits: 50 },
17642
17994
  kpi: {
17643
17995
  /**
17644
17996
  * `kpi_focus` 变体单行铺满最窄 rect.w=1096(`layout.ts` kpi_focus 分支
@@ -17782,6 +18134,10 @@ function charLen(s) {
17782
18134
  function hasKpiCardsComponent(slide) {
17783
18135
  return slide.components.some((b) => b.type === "kpi_cards");
17784
18136
  }
18137
+ var AXES_APPLICABLE_CHART_TYPES = /* @__PURE__ */ new Set(["bar", "line"]);
18138
+ function hasAnyAxesSetting(axes) {
18139
+ return axes.x_title !== void 0 || axes.y_title !== void 0 || axes.show_grid !== void 0;
18140
+ }
17785
18141
  function isBackgroundImageOnly(slide) {
17786
18142
  return !slide.heading && slide.components.length === 1 && slide.components[0].type === "image";
17787
18143
  }
@@ -17827,7 +18183,8 @@ function checkSlide(ir, slide, index, resolvedAxes) {
17827
18183
  });
17828
18184
  }
17829
18185
  for (const item of component.items) {
17830
- if (measureTextUnits(item) > budget.bullets.maxUnitsPerItem) {
18186
+ const units = measureTextUnits(item);
18187
+ if (units > budget.bullets.maxUnitsPerItem) {
17831
18188
  issues.push({
17832
18189
  slide: index,
17833
18190
  severity: "warn",
@@ -17836,8 +18193,28 @@ function checkSlide(ir, slide, index, resolvedAxes) {
17836
18193
  bulletsBudget: { pacing: resolvedAxes.pacing, ...budget.bullets }
17837
18194
  });
17838
18195
  }
18196
+ if (units > CAPACITY.bullets.itemOverflowUnits) {
18197
+ issues.push({
18198
+ slide: index,
18199
+ severity: "error",
18200
+ code: "bullet_item_overflow",
18201
+ message: "\u5355\u6761\u8981\u70B9\u8D85\u51FA\u6E32\u67D3\u5B89\u5168\u4E0A\u9650\uFF0C\u4F1A\u88AB\u622A\u65AD\u663E\u793A"
18202
+ });
18203
+ }
17839
18204
  }
17840
18205
  }
18206
+ for (const component of slide.components) {
18207
+ if (component.type !== "chart" || !component.axes) continue;
18208
+ if (AXES_APPLICABLE_CHART_TYPES.has(component.chart_type)) continue;
18209
+ if (!hasAnyAxesSetting(component.axes)) continue;
18210
+ issues.push({
18211
+ slide: index,
18212
+ severity: "warn",
18213
+ code: "chart_axes_ignored",
18214
+ message: `\u56FE\u8868\u7C7B\u578B "${component.chart_type}" \u4E0D\u652F\u6301\u5750\u6807\u8F74\u6807\u9898/\u7F51\u683C\u7EBF\uFF0Caxes \u5B57\u6BB5\u5C06\u88AB\u5FFD\u7565\uFF08\u4EC5 bar \u4E0E line \u652F\u6301\uFF09`,
18215
+ chartAxesIgnored: { chartType: component.chart_type }
18216
+ });
18217
+ }
17841
18218
  const needsHeading = ["cover", "chapter", "content"];
17842
18219
  if (needsHeading.includes(slide.type) && !slide.heading && !isBackgroundImageOnly(slide)) {
17843
18220
  issues.push({
@@ -17909,8 +18286,14 @@ function describeQualityIssue(issue) {
17909
18286
  const b = issue.bulletsBudget;
17910
18287
  return b ? `a bullet item is too long for ${b.pacing} pacing \u2014 keep it within about 2 lines` : "a bullet item is too long \u2014 keep it within about 2 lines";
17911
18288
  }
18289
+ case "bullet_item_overflow":
18290
+ return `a bullet item exceeds the render-safety limit (${CAPACITY.bullets.itemOverflowUnits} width units) and can truncate \u2014 shorten it substantially or split the point across two items`;
17912
18291
  case "big_number_no_kpi":
17913
18292
  return "big_number arrangement is missing a kpi_cards component";
18293
+ case "chart_axes_ignored": {
18294
+ const chartType = issue.chartAxesIgnored?.chartType;
18295
+ return chartType ? `axes settings (x_title/y_title/show_grid) are not supported for "${chartType}" charts and are ignored \u2014 only bar and line charts render them` : "chart axes settings (x_title/y_title/show_grid) are not supported for this chart type and are ignored \u2014 only bar and line charts render them";
18296
+ }
17914
18297
  default:
17915
18298
  return `content quality issue (${issue.code})`;
17916
18299
  }
@@ -18019,7 +18402,7 @@ function validateIr(input) {
18019
18402
  const withNormalized = (result) => normalized.length > 0 ? { ...result, normalized } : result;
18020
18403
  const r = PptxIRSchema.safeParse(normalizedInput);
18021
18404
  if (!r.success) {
18022
- const errors2 = r.error.issues.map((issue) => {
18405
+ const errors = r.error.issues.map((issue) => {
18023
18406
  const path = issue.path.join(".");
18024
18407
  const m = /^slides\.(\d+)/.exec(path);
18025
18408
  let message = issue.message;
@@ -18028,7 +18411,7 @@ function validateIr(input) {
18028
18411
  }
18029
18412
  return { path, message, page: m ? Number(m[1]) + 1 : void 0 };
18030
18413
  });
18031
- return withNormalized({ ok: false, errors: errors2 });
18414
+ return withNormalized({ ok: false, errors });
18032
18415
  }
18033
18416
  const installedThemeIds = getInstalledThemeIds();
18034
18417
  if (!installedThemeIds.includes(r.data.theme.id)) {
@@ -18059,15 +18442,19 @@ function validateIr(input) {
18059
18442
  }
18060
18443
  const quality = checkIrQuality(r.data, resolvedAxes);
18061
18444
  if (quality.length === 0) return withNormalized({ ok: true, ir: r.data, errors: [] });
18062
- const errors = quality.map(
18063
- (issue) => issue.code === "empty_deck" ? { path: "slides", message: describeQualityIssue(issue) } : {
18064
- path: `slides.${issue.slide}`,
18065
- message: describeQualityIssue(issue),
18066
- page: issue.slide + 1,
18067
- ...r.data.slides[issue.slide].id !== void 0 ? { slideId: r.data.slides[issue.slide].id } : {}
18068
- }
18069
- );
18070
- return withNormalized({ ok: false, errors });
18445
+ const toIssue = (issue) => issue.code === "empty_deck" ? { path: "slides", message: describeQualityIssue(issue) } : {
18446
+ path: `slides.${issue.slide}`,
18447
+ message: describeQualityIssue(issue),
18448
+ page: issue.slide + 1,
18449
+ ...r.data.slides[issue.slide].id !== void 0 ? { slideId: r.data.slides[issue.slide].id } : {}
18450
+ };
18451
+ const warnFindings = quality.filter((issue) => issue.severity === "warn");
18452
+ const warnings = warnFindings.length > 0 ? warnFindings.map(toIssue) : void 0;
18453
+ const errorFindings = quality.filter((issue) => issue.severity === "error");
18454
+ if (errorFindings.length > 0) {
18455
+ return withNormalized({ ok: false, errors: errorFindings.map(toIssue), ...warnings ? { warnings } : {} });
18456
+ }
18457
+ return withNormalized({ ok: true, ir: r.data, errors: [], ...warnings ? { warnings } : {} });
18071
18458
  }
18072
18459
  function formatIssues(errors) {
18073
18460
  return errors.map((e) => {
@@ -18076,6 +18463,9 @@ function formatIssues(errors) {
18076
18463
  return `page ${e.page}${idSuffix} \u2014 ${e.path}: ${e.message}`;
18077
18464
  }).join("\n");
18078
18465
  }
18466
+ function formatWarnings(warnings) {
18467
+ return warnings.map((w) => `warning: ${formatIssues([w])}`).join("\n");
18468
+ }
18079
18469
  function renderSlideSvg(ir, slideIndex) {
18080
18470
  const slide = ir.slides[slideIndex];
18081
18471
  if (!slide) {
@@ -18160,7 +18550,9 @@ function auditSvgMarkup(markup) {
18160
18550
  const fontSize = Number(el.getAttribute("font-size") ?? 16) * as;
18161
18551
  const tx = ax + Number(el.getAttribute("x") ?? 0) * as;
18162
18552
  const ty = ay + Number(el.getAttribute("y") ?? 0) * as;
18163
- const width = measureTextUnits(content) * fontSize;
18553
+ const fontFamily = el.getAttribute("font-family") ?? "";
18554
+ const measure = isMonoFontFamily(fontFamily) ? measureMonoTextUnits : measureTextUnits;
18555
+ const width = measure(content) * fontSize;
18164
18556
  const anchor = el.getAttribute("text-anchor") ?? "start";
18165
18557
  const left = anchor === "end" ? tx - width : anchor === "middle" ? tx - width / 2 : tx;
18166
18558
  const right = left + width;
@@ -18278,6 +18670,23 @@ function ellipseShape(cx, cy, rx, ry, fill) {
18278
18670
  }
18279
18671
  };
18280
18672
  }
18673
+ function sectorShape(sector, fill) {
18674
+ const { cx, cy, ri, ro, startA, span } = sector;
18675
+ const EPS = 1e-6;
18676
+ return {
18677
+ fill,
18678
+ contains: (px, py) => {
18679
+ const dx = px - cx;
18680
+ const dy = py - cy;
18681
+ const dist = Math.hypot(dx, dy);
18682
+ if (dist < ri - EPS || dist > ro + EPS) return false;
18683
+ if (span >= 2 * Math.PI - EPS) return true;
18684
+ let delta = (Math.atan2(dy, dx) - startA) % (2 * Math.PI);
18685
+ if (delta < -EPS) delta += 2 * Math.PI;
18686
+ return delta >= -EPS && delta <= span + EPS;
18687
+ }
18688
+ };
18689
+ }
18281
18690
  function arcToCenter(x1, y1, rx0, ry0, rotDeg, largeArc, sweep, x2, y2) {
18282
18691
  if (x1 === x2 && y1 === y2) return null;
18283
18692
  let rx = Math.abs(rx0);
@@ -18625,6 +19034,83 @@ function pathBoundingBoxByTokenMinMax(d) {
18625
19034
  function pathBoundingBox(d) {
18626
19035
  return pathBoundingBoxByGrammar(d) ?? pathBoundingBoxByTokenMinMax(d);
18627
19036
  }
19037
+ function parseWedgePath(d) {
19038
+ const tokens = tokenizePathD(d);
19039
+ const num8 = (i) => {
19040
+ const t = tokens[i];
19041
+ if (t === void 0) return null;
19042
+ const n = Number(t);
19043
+ return Number.isFinite(n) ? n : null;
19044
+ };
19045
+ const isFlag = (i) => tokens[i] === "0" || tokens[i] === "1";
19046
+ const DEGENERATE_EPS = 1e-6;
19047
+ const RADIUS_ROUNDTRIP_EPS = 1e-3;
19048
+ const resolveSpan = (startA, endA, large) => {
19049
+ let raw = (endA - startA) % (2 * Math.PI);
19050
+ if (raw < 0) raw += 2 * Math.PI;
19051
+ if (raw < DEGENERATE_EPS) return large === "1" ? 2 * Math.PI : 0;
19052
+ const expectedLarge = raw > Math.PI ? "1" : "0";
19053
+ if (large !== expectedLarge) return null;
19054
+ return raw;
19055
+ };
19056
+ const onCircle = (px, py, cx, cy, r) => Math.abs(Math.hypot(px - cx, py - cy) - r) <= RADIUS_ROUNDTRIP_EPS;
19057
+ if (tokens.length === 15 && tokens[0] === "M" && tokens[3] === "L" && tokens[6] === "A" && tokens[9] === "0" && // x-axis-rotation, always 0 in this renderer's own arcs
19058
+ isFlag(10) && tokens[11] === "1" && // sweep-flag, always positive in this renderer
19059
+ tokens[14] === "Z") {
19060
+ const cx = num8(1);
19061
+ const cy = num8(2);
19062
+ const x1 = num8(4);
19063
+ const y1 = num8(5);
19064
+ const r = num8(7);
19065
+ const rCheck = num8(8);
19066
+ const x2 = num8(12);
19067
+ const y2 = num8(13);
19068
+ if (cx === null || cy === null || x1 === null || y1 === null || r === null || rCheck === null || x2 === null || y2 === null) {
19069
+ return null;
19070
+ }
19071
+ if (r <= 0 || Math.abs(r - rCheck) > 1e-6) return null;
19072
+ if (!onCircle(x1, y1, cx, cy, r) || !onCircle(x2, y2, cx, cy, r)) return null;
19073
+ const startA = Math.atan2(y1 - cy, x1 - cx);
19074
+ const endA = Math.atan2(y2 - cy, x2 - cx);
19075
+ const wedgeSpan = resolveSpan(startA, endA, tokens[10]);
19076
+ if (wedgeSpan === null) return null;
19077
+ return { cx, cy, ri: 0, ro: r, startA, span: wedgeSpan };
19078
+ }
19079
+ if (tokens.length === 23 && tokens[0] === "M" && tokens[3] === "A" && tokens[6] === "0" && isFlag(7) && tokens[8] === "1" && tokens[11] === "L" && tokens[14] === "A" && tokens[17] === "0" && isFlag(18) && tokens[19] === "0" && tokens[22] === "Z") {
19080
+ const ox1 = num8(1);
19081
+ const oy1 = num8(2);
19082
+ const r = num8(4);
19083
+ const rCheck = num8(5);
19084
+ const ox2 = num8(9);
19085
+ const oy2 = num8(10);
19086
+ const ix1 = num8(12);
19087
+ const iy1 = num8(13);
19088
+ const ri = num8(15);
19089
+ const riCheck = num8(16);
19090
+ const ix2 = num8(20);
19091
+ const iy2 = num8(21);
19092
+ if (ox1 === null || oy1 === null || r === null || rCheck === null || ox2 === null || oy2 === null || ix1 === null || iy1 === null || ri === null || riCheck === null || ix2 === null || iy2 === null) {
19093
+ return null;
19094
+ }
19095
+ if (r <= 0 || ri < 0 || Math.abs(r - rCheck) > 1e-6 || Math.abs(ri - riCheck) > 1e-6) return null;
19096
+ const denom = r - ri;
19097
+ if (Math.abs(denom) < 1e-6) return null;
19098
+ const cx = (r * ix2 - ri * ox1) / denom;
19099
+ const cy = (r * iy2 - ri * oy1) / denom;
19100
+ if (!onCircle(ox1, oy1, cx, cy, r) || !onCircle(ox2, oy2, cx, cy, r) || !onCircle(ix1, iy1, cx, cy, ri) || !onCircle(ix2, iy2, cx, cy, ri)) {
19101
+ return null;
19102
+ }
19103
+ const startA = Math.atan2(oy1 - cy, ox1 - cx);
19104
+ const endA = Math.atan2(oy2 - cy, ox2 - cx);
19105
+ const checkA = Math.atan2(iy1 - cy, ix1 - cx);
19106
+ const angleDiff = Math.abs((checkA - endA + Math.PI) % (2 * Math.PI) - Math.PI);
19107
+ if (angleDiff > 1e-3) return null;
19108
+ const wedgeSpan = resolveSpan(startA, endA, tokens[7]);
19109
+ if (wedgeSpan === null) return null;
19110
+ return { cx, cy, ri, ro: r, startA, span: wedgeSpan };
19111
+ }
19112
+ return null;
19113
+ }
18628
19114
  function directText(el) {
18629
19115
  let s = "";
18630
19116
  for (const node of Array.from(el.childNodes)) {
@@ -18674,14 +19160,17 @@ function runContrastWalk(markup) {
18674
19160
  let y = 0;
18675
19161
  let localW = 0;
18676
19162
  let localH = 0;
19163
+ let localWedge = null;
18677
19164
  if (tag === "path") {
18678
- const bbox = pathBoundingBox(el.getAttribute("d") ?? "");
19165
+ const dAttr = el.getAttribute("d") ?? "";
19166
+ const bbox = pathBoundingBox(dAttr);
18679
19167
  if (bbox) {
18680
19168
  x = bbox.x;
18681
19169
  y = bbox.y;
18682
19170
  localW = bbox.w;
18683
19171
  localH = bbox.h;
18684
19172
  }
19173
+ localWedge = parseWedgePath(dAttr);
18685
19174
  } else {
18686
19175
  x = Number(el.getAttribute("x") ?? 0);
18687
19176
  y = Number(el.getAttribute("y") ?? 0);
@@ -18703,7 +19192,23 @@ function runContrastWalk(markup) {
18703
19192
  const shapeFill = el.getAttribute("fill");
18704
19193
  const opaqueEnough = currentFillOpacity * currentOpacityProduct >= MIN_BG_OPACITY;
18705
19194
  if (shapeFill?.startsWith("#") && opaqueEnough) {
18706
- paintedShapes.push(rectShape(absX, absY, w, h, shapeFill));
19195
+ if (localWedge) {
19196
+ paintedShapes.push(
19197
+ sectorShape(
19198
+ {
19199
+ cx: ax + localWedge.cx * as,
19200
+ cy: ay + localWedge.cy * as,
19201
+ ri: localWedge.ri * as,
19202
+ ro: localWedge.ro * as,
19203
+ startA: localWedge.startA,
19204
+ span: localWedge.span
19205
+ },
19206
+ shapeFill
19207
+ )
19208
+ );
19209
+ } else {
19210
+ paintedShapes.push(rectShape(absX, absY, w, h, shapeFill));
19211
+ }
18707
19212
  if (w * h >= MIN_BG_REGION_AREA) {
18708
19213
  regions.push({ x: absX, y: absY, w, h, fill: shapeFill });
18709
19214
  }
@@ -18811,6 +19316,14 @@ function collectLeafBoxes(root) {
18811
19316
  if (bottom > top.bottom) top.bottom = bottom;
18812
19317
  if (label && !top.label) top.label = label;
18813
19318
  };
19319
+ const extendX = (left, right) => {
19320
+ const top = stack[stack.length - 1];
19321
+ if (!top) return;
19322
+ const newLeft = Math.min(top.x, left);
19323
+ const newRight = Math.max(top.x + top.w, right);
19324
+ top.x = newLeft;
19325
+ top.w = newRight - newLeft;
19326
+ };
18814
19327
  const visit = (el, ox, oy, os) => {
18815
19328
  const { dx, dy, scale } = parseTransform2(el);
18816
19329
  const ax = ox + os * dx;
@@ -18844,6 +19357,13 @@ function collectLeafBoxes(root) {
18844
19357
  const fontSize = Number(el.getAttribute("font-size") ?? DEFAULT_FONT_SIZE) * as;
18845
19358
  const y = Number(el.getAttribute("y") ?? 0);
18846
19359
  extend(ay + y * as + fontSize * TEXT_DESCENT_RATIO, content.slice(0, 24));
19360
+ const tx = ax + Number(el.getAttribute("x") ?? 0) * as;
19361
+ const fontFamily = el.getAttribute("font-family") ?? "";
19362
+ const measure = isMonoFontFamily(fontFamily) ? measureMonoTextUnits : measureTextUnits;
19363
+ const width = measure(content) * fontSize;
19364
+ const anchor = el.getAttribute("text-anchor") ?? "start";
19365
+ const left = anchor === "end" ? tx - width : anchor === "middle" ? tx - width / 2 : tx;
19366
+ extendX(left, left + width);
18847
19367
  }
18848
19368
  }
18849
19369
  for (const child of Array.from(el.children)) visit(child, ax, ay, as);
@@ -18960,7 +19480,7 @@ function auditDeck(ir, opts = {}) {
18960
19480
  return runPixelPass(ir, report);
18961
19481
  }
18962
19482
  async function runPixelPass(ir, report) {
18963
- const { runPixelContrastAudit } = await import("./pixel-audit-4DXKLFBV.js");
19483
+ const { runPixelContrastAudit } = await import("./pixel-audit-ZRFTV4V7.js");
18964
19484
  const pixelFindings = await runPixelContrastAudit(ir);
18965
19485
  return {
18966
19486
  ...report,
@@ -19000,6 +19520,7 @@ export {
19000
19520
  CAPACITY,
19001
19521
  validateIr,
19002
19522
  formatIssues,
19523
+ formatWarnings,
19003
19524
  renderSlideSvg,
19004
19525
  generatePptx,
19005
19526
  listThemes,
@@ -19010,4 +19531,4 @@ export {
19010
19531
  __collectImageBackedTextRuns,
19011
19532
  auditDeck
19012
19533
  };
19013
- //# sourceMappingURL=chunk-7N4HGSMW.js.map
19534
+ //# sourceMappingURL=chunk-7V73LHUQ.js.map