@pagenflow/email 1.4.2 → 1.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs.js CHANGED
@@ -17,24 +17,42 @@ function Body({ children, config = {} }) {
17
17
  const bgSize = ((_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.size) || "cover";
18
18
  const bgPosition = ((_d = config.backgroundImage) === null || _d === void 0 ? void 0 : _d.position) || "center";
19
19
  // 1. Style for the <body> tag inline
20
- const bodyStyle = Object.assign({ backgroundColor: globalBackgroundColor, color: globalColor, fontSize: globalFontSize, lineHeight: globalLineHeight, padding: "0", margin: "0", WebkitTextSizeAdjust: "100%", overflowX: "hidden", ["msTextSizeAdjust"]: "100%", ["msoLineHeightRule"]: "exactly", fontFamily: globalFontFamily }, (bgImage && {
21
- backgroundImage: `url(${bgImage})`,
22
- backgroundRepeat: bgRepeat,
23
- backgroundSize: bgSize,
24
- backgroundPosition: bgPosition,
25
- }));
20
+ const bodyStyle = {
21
+ backgroundColor: globalBackgroundColor,
22
+ color: globalColor,
23
+ fontSize: globalFontSize,
24
+ lineHeight: globalLineHeight,
25
+ padding: "0",
26
+ margin: "0",
27
+ WebkitTextSizeAdjust: "100%",
28
+ overflowX: "hidden",
29
+ ["msTextSizeAdjust"]: "100%",
30
+ ["msoLineHeightRule"]: "exactly",
31
+ fontFamily: globalFontFamily,
32
+ // Background image support (if provided)
33
+ ...(bgImage && {
34
+ backgroundImage: `url(${bgImage})`,
35
+ backgroundRepeat: bgRepeat,
36
+ backgroundSize: bgSize,
37
+ backgroundPosition: bgPosition,
38
+ }),
39
+ };
26
40
  // 2. Style for the top-level <table> wrapper
27
41
  const outerTableStyle = {
28
42
  width: "100%",
29
43
  ["msoLineHeightRule"]: "exactly",
30
44
  borderCollapse: "collapse",
31
45
  };
32
- return (jsxRuntime.jsxs("body", { style: bodyStyle, children: [jsxRuntime.jsx("center", { style: Object.assign({ width: "100%", background: globalBackgroundColor }, (bgImage && {
33
- backgroundImage: `url(${bgImage})`,
34
- backgroundRepeat: bgRepeat,
35
- backgroundSize: bgSize,
36
- backgroundPosition: bgPosition,
37
- })), children: jsxRuntime.jsx("table", { role: "presentation", border: 0, cellPadding: 0, cellSpacing: 0, align: "center", width: "100%", style: outerTableStyle, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { align: "center", style: { padding: "0", margin: "0" }, children: children }) }) }) }) }), jsxRuntime.jsx("div", { style: {
46
+ return (jsxRuntime.jsxs("body", { style: bodyStyle, children: [jsxRuntime.jsx("center", { style: {
47
+ width: "100%",
48
+ background: globalBackgroundColor,
49
+ ...(bgImage && {
50
+ backgroundImage: `url(${bgImage})`,
51
+ backgroundRepeat: bgRepeat,
52
+ backgroundSize: bgSize,
53
+ backgroundPosition: bgPosition,
54
+ }),
55
+ }, children: jsxRuntime.jsx("table", { role: "presentation", border: 0, cellPadding: 0, cellSpacing: 0, align: "center", width: "100%", style: outerTableStyle, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { align: "center", style: { padding: "0", margin: "0" }, children: children }) }) }) }) }), jsxRuntime.jsx("div", { style: {
38
56
  display: "none",
39
57
  whiteSpace: "nowrap",
40
58
  font: "15px courier",
@@ -1521,40 +1539,26 @@ const justifyMap$3 = {
1521
1539
  center: "center",
1522
1540
  end: "right",
1523
1541
  };
1524
- function getBorderStyle$6(border) {
1525
- if (!border)
1526
- return {};
1527
- const style = {};
1528
- // If a full border is specified, apply it
1529
- if (border.width && border.style && border.color) {
1530
- style.border = `${border.width} ${border.style} ${border.color}`;
1531
- }
1532
- else {
1533
- // If only individual borders are specified, explicitly set others to 'none'
1534
- // to prevent Outlook Classic from showing black borders
1535
- const hasIndividualBorders = border.top || border.right || border.bottom || border.left;
1536
- if (hasIndividualBorders) {
1537
- // Default all borders to none
1538
- style.borderTop = "none";
1539
- style.borderRight = "none";
1540
- style.borderBottom = "none";
1541
- style.borderLeft = "none";
1542
- }
1543
- }
1544
- // Override with specific borders if provided
1545
- if (border.top) {
1546
- style.borderTop = `${border.top.width} ${border.top.style} ${border.top.color}`;
1547
- }
1548
- if (border.right) {
1549
- style.borderRight = `${border.right.width} ${border.right.style} ${border.right.color}`;
1550
- }
1551
- if (border.bottom) {
1552
- style.borderBottom = `${border.bottom.width} ${border.bottom.style} ${border.bottom.color}`;
1553
- }
1554
- if (border.left) {
1555
- style.borderLeft = `${border.left.width} ${border.left.style} ${border.left.color}`;
1542
+ // Helper to build link href based on innerLink type (mirrors Icon component)
1543
+ function buildLinkHref$2(innerLink) {
1544
+ if (!innerLink || innerLink.type === "none")
1545
+ return null;
1546
+ switch (innerLink.type) {
1547
+ case "url":
1548
+ return innerLink.url || null;
1549
+ case "email":
1550
+ return innerLink.email ? `mailto:${innerLink.email}` : null;
1551
+ case "phone":
1552
+ return innerLink.phone ? `tel:${innerLink.phone}` : null;
1553
+ case "anchor":
1554
+ return innerLink.anchor ? `#${innerLink.anchor}` : null;
1555
+ case "page_top":
1556
+ return "#top";
1557
+ case "page_bottom":
1558
+ return "#bottom";
1559
+ default:
1560
+ return null;
1556
1561
  }
1557
- return style;
1558
1562
  }
1559
1563
  function getBorderStyleString$2(border) {
1560
1564
  if (!border)
@@ -1591,38 +1595,48 @@ function getBorderStyleString$2(border) {
1591
1595
  return styles.join(" ");
1592
1596
  }
1593
1597
  function Button({ config, devMode }) {
1594
- const { href, children, backgroundColor = "#007bff", // Default blue
1595
- color = "#ffffff", padding = "12px 24px", borderRadius = "3px", border, width, maxWidth, justifyContent = "center", textAlign = "center", fontSize = "16px", fontWeight = "500", fontStyle, fontFamily = "Arial, sans-serif", lineHeight = "1.2", letterSpacing, textTransform, textDecoration = "none", direction, verticalAlign, opacity, whiteSpace = "normal", wordBreak = "break-word", } = config;
1598
+ const { innerLink, children, backgroundColor, color, padding, borderRadius, border, width, maxWidth, justifyContent, textAlign, fontSize, fontWeight, fontStyle, fontFamily, lineHeight, letterSpacing, textTransform, textDecoration, direction, verticalAlign, opacity, whiteSpace, wordBreak, } = config;
1599
+ // Resolve href from innerLink
1600
+ const href = buildLinkHref$2(innerLink);
1601
+ const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_blank";
1596
1602
  // Sanitize fontFamily early so safeFontFamily is available for all paths below.
1597
1603
  const safeFontFamily = fontFamily
1598
1604
  ? fontFamily.replace(/['"]/g, "")
1599
1605
  : fontFamily;
1600
- // 2. Outer TD Style for Background and Border Radius (no border)
1601
- const backgroundTdStyle = Object.assign(Object.assign({ backgroundColor: backgroundColor, borderRadius: borderRadius, width: width || "auto" }, (maxWidth && { maxWidth: maxWidth })), (borderRadius && { overflow: "hidden" }));
1602
- // 3. Border styles
1603
- getBorderStyle$6(border);
1606
+ // Outer TD Style for Background and Border Radius (no border)
1607
+ const backgroundTdStyle = {
1608
+ backgroundColor: backgroundColor,
1609
+ borderRadius: borderRadius,
1610
+ width: width || "auto",
1611
+ ...(maxWidth && { maxWidth: maxWidth }),
1612
+ // Overflow hidden to clip background to border-radius
1613
+ ...(borderRadius && { overflow: "hidden" }),
1614
+ };
1615
+ // Border styles
1604
1616
  const borderStyleString = getBorderStyleString$2(border);
1605
1617
  // --- Determine Button Approach Based on Width ---
1606
1618
  // Check if width is percentage-based or not defined
1607
1619
  const isPercentageWidth = !width || width.includes("%");
1608
1620
  const useSimpleOutlookApproach = isPercentageWidth;
1609
- const align = justifyMap$3[justifyContent];
1621
+ const align = justifyContent ? justifyMap$3[justifyContent] : undefined;
1610
1622
  // --- VML Calculation and Code for Outlook Compatibility (Fixed Width Only) ---
1611
1623
  let vmlButton = "";
1612
1624
  if (!useSimpleOutlookApproach) {
1613
1625
  // VML needs fixed pixel height. We estimate it based on padding and potential wrapping.
1614
- const numericPadding = parseInt(padding.split(" ")[0] || "12", 10);
1615
- const numericFontSize = parseInt(fontSize, 10);
1616
- const numericLineHeight = lineHeight.includes("px")
1617
- ? parseInt(lineHeight, 10)
1618
- : numericFontSize * parseFloat(lineHeight);
1626
+ const numericPadding = padding ? parseInt(padding.split(" ")[0] || "12", 10) : 12;
1627
+ const numericFontSize = fontSize ? parseInt(fontSize, 10) : 0;
1628
+ const numericLineHeight = lineHeight
1629
+ ? lineHeight.includes("px")
1630
+ ? parseInt(lineHeight, 10)
1631
+ : numericFontSize * parseFloat(lineHeight)
1632
+ : numericFontSize;
1619
1633
  // Trust user's explicit pixel width - no calculation needed
1620
1634
  const vmlWidth = parseInt(width, 10);
1621
1635
  // Calculate VML height - trust user's padding and let text wrap naturally
1622
1636
  // VML v:textbox will handle text wrapping automatically
1623
1637
  const textContent = typeof children === "string" ? children : "";
1624
1638
  // Estimate number of lines based on text length and button width
1625
- const horizontalPadding = padding.split(" ")[1]
1639
+ const horizontalPadding = (padding === null || padding === void 0 ? void 0 : padding.split(" ")[1])
1626
1640
  ? parseInt(padding.split(" ")[1], 10) * 2
1627
1641
  : numericPadding * 2;
1628
1642
  const availableTextWidth = vmlWidth - horizontalPadding;
@@ -1635,15 +1649,17 @@ function Button({ config, devMode }) {
1635
1649
  // Add extra 4px buffer to prevent bottom cropping in VML
1636
1650
  const vmlHeight = Math.max(numericPadding * 2 + textHeight + 4, 40);
1637
1651
  // VML colors must use the full hex format (e.g., #000000)
1638
- const vmlFillColor = backgroundColor.startsWith("#")
1639
- ? backgroundColor
1640
- : `#${backgroundColor}`;
1652
+ const vmlFillColor = backgroundColor
1653
+ ? backgroundColor.startsWith("#")
1654
+ ? backgroundColor
1655
+ : `#${backgroundColor}`
1656
+ : undefined;
1641
1657
  // VML stroke color for border
1642
1658
  const vmlStrokeColor = (border === null || border === void 0 ? void 0 : border.color) || vmlFillColor;
1643
1659
  const vmlStrokeWeight = (border === null || border === void 0 ? void 0 : border.width) ? parseInt(border.width, 10) : 0;
1644
1660
  const hasVmlStroke = vmlStrokeWeight > 0;
1645
1661
  // Build VML font styles - consistent with other rendering paths
1646
- const vmlFontWeight = fontWeight || "500";
1662
+ const vmlFontWeight = fontWeight;
1647
1663
  const vmlFontStyle = fontStyle === "italic" ? "font-style:italic;" : "";
1648
1664
  const vmlLetterSpacing = letterSpacing
1649
1665
  ? `letter-spacing:${letterSpacing};`
@@ -1654,12 +1670,12 @@ function Button({ config, devMode }) {
1654
1670
  const vmlTextDecoration = textDecoration && textDecoration !== "none"
1655
1671
  ? `text-decoration:${textDecoration};`
1656
1672
  : "";
1657
- const vmlWhiteSpace = whiteSpace !== "normal" ? `white-space:${whiteSpace};` : "";
1673
+ const vmlWhiteSpace = whiteSpace ? `white-space:${whiteSpace};` : "";
1658
1674
  const vmlDirection = direction ? `direction:${direction};` : "";
1659
1675
  const vmlOpacity = opacity !== undefined ? `opacity:${opacity};` : "";
1660
1676
  // VML code uses MSO conditional comments to render only in Outlook
1661
1677
  // Use table with explicit MSO height for vertical centering
1662
- const horizontalPaddingValue = padding.split(" ")[1]
1678
+ const horizontalPaddingValue = (padding === null || padding === void 0 ? void 0 : padding.split(" ")[1])
1663
1679
  ? parseInt(padding.split(" ")[1], 10)
1664
1680
  : numericPadding;
1665
1681
  // For VML, we need to use a table inside to properly apply padding and centering
@@ -1668,7 +1684,7 @@ function Button({ config, devMode }) {
1668
1684
  if (textAlign === "center") {
1669
1685
  vmlAlignAttr = 'align="center"';
1670
1686
  }
1671
- else {
1687
+ else if (textAlign) {
1672
1688
  vmlAlignStyle = `text-align:${textAlign};`;
1673
1689
  }
1674
1690
  // Border radius is intentionally omitted (arcsize="0%") for Outlook Classic.
@@ -1676,12 +1692,12 @@ function Button({ config, devMode }) {
1676
1692
  // is inconsistent, so we render sharp corners there instead.
1677
1693
  vmlButton = `
1678
1694
  <!--[if mso]>
1679
- <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="${href}" style="height:${vmlHeight}px;width:${vmlWidth}px;" arcsize="0%" strokecolor="${vmlStrokeColor}" ${hasVmlStroke ? `strokeweight="${vmlStrokeWeight}px"` : 'stroke="f"'} fillcolor="${vmlFillColor}">
1695
+ <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" ${href ? `href="${href}"` : ""} style="height:${vmlHeight}px;width:${vmlWidth}px;" arcsize="0%" ${vmlStrokeColor ? `strokecolor="${vmlStrokeColor}"` : ""} ${hasVmlStroke ? `strokeweight="${vmlStrokeWeight}px"` : 'stroke="f"'} ${vmlFillColor ? `fillcolor="${vmlFillColor}"` : ""}>
1680
1696
  <w:anchorlock/>
1681
1697
  <v:textbox inset="${horizontalPaddingValue}px,${numericPadding}px,${horizontalPaddingValue}px,${numericPadding}px">
1682
1698
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse:collapse;">
1683
1699
  <tr>
1684
- <td ${vmlAlignAttr} valign="middle" style="${vmlAlignStyle}color:${color};font-family:${safeFontFamily};font-size:${fontSize};font-weight:${vmlFontWeight};${vmlFontStyle}${vmlLetterSpacing}${vmlTextTransform}${vmlTextDecoration}${vmlWhiteSpace}${vmlDirection}${vmlOpacity}line-height:${lineHeight};mso-line-height-rule:exactly;">
1700
+ <td ${vmlAlignAttr} valign="middle" style="${vmlAlignStyle}${color ? `color:${color};` : ""}${safeFontFamily ? `font-family:${safeFontFamily};` : ""}${fontSize ? `font-size:${fontSize};` : ""}${vmlFontWeight ? `font-weight:${vmlFontWeight};` : ""}${vmlFontStyle}${vmlLetterSpacing}${vmlTextTransform}${vmlTextDecoration}${vmlWhiteSpace}${vmlDirection}${vmlOpacity}${lineHeight ? `line-height:${lineHeight};` : ""}mso-line-height-rule:exactly;">
1685
1701
  ${typeof children === "string" ? children : ""}
1686
1702
  </td>
1687
1703
  </tr>
@@ -1705,10 +1721,10 @@ function Button({ config, devMode }) {
1705
1721
  const textTransformProp = textTransform
1706
1722
  ? `text-transform: ${textTransform};`
1707
1723
  : "";
1708
- const whiteSpaceProp = whiteSpace !== "normal" ? `white-space: ${whiteSpace};` : "";
1724
+ const whiteSpaceProp = whiteSpace ? `white-space: ${whiteSpace};` : "";
1709
1725
  const directionProp = direction ? `direction: ${direction};` : "";
1710
1726
  const opacityProp = opacity !== undefined ? `opacity: ${opacity};` : "";
1711
- const wordBreakProp = wordBreak !== "break-word" ? `word-break: ${wordBreak};` : "";
1727
+ const wordBreakProp = wordBreak ? `word-break: ${wordBreak};` : "";
1712
1728
  // Border radius is intentionally omitted from the Outlook Classic table cell.
1713
1729
  // Outlook Classic ignores border-radius on table cells anyway, and including it
1714
1730
  // can cause unexpected rendering artifacts, so we explicitly leave it out.
@@ -1716,13 +1732,17 @@ function Button({ config, devMode }) {
1716
1732
  <!--[if mso]>
1717
1733
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse;">
1718
1734
  <tr>
1719
- <td align="${align}" style="padding: 0;">
1735
+ <td ${align ? `align="${align}"` : ""} style="padding: 0;">
1720
1736
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" width="${width || "auto"}" style="border-collapse: collapse;">
1721
1737
  <tr>
1722
- <td bgcolor="${backgroundColor}" align="${textAlign}" style="padding: ${padding}; text-align: ${textAlign}; ${borderStyleString}">
1723
- <a href="${href}" target="_blank" rel="noopener noreferrer" style="color: ${color}; ${textDecorationStyle} display: block; font-family: ${safeFontFamily}; font-size: ${fontSize}; font-weight: ${fontWeight}; ${fontStyleProp} line-height: ${lineHeight}; ${letterSpacingProp} ${textTransformProp} text-align: ${textAlign}; ${whiteSpaceProp} ${directionProp} ${opacityProp} ${wordBreakProp} mso-line-height-rule: exactly;">
1724
- ${typeof children === "string" ? children : ""}
1725
- </a>
1738
+ <td ${backgroundColor ? `bgcolor="${backgroundColor}"` : ""} ${textAlign ? `align="${textAlign}"` : ""} style="${padding ? `padding: ${padding};` : ""} ${textAlign ? `text-align: ${textAlign};` : ""} ${borderStyleString}">
1739
+ ${href
1740
+ ? `<a href="${href}" target="${target}" rel="noopener noreferrer" style="${color ? `color: ${color};` : ""} ${textDecorationStyle} display: block; ${safeFontFamily ? `font-family: ${safeFontFamily};` : ""} ${fontSize ? `font-size: ${fontSize};` : ""} ${fontWeight ? `font-weight: ${fontWeight};` : ""} ${fontStyleProp} ${lineHeight ? `line-height: ${lineHeight};` : ""} ${letterSpacingProp} ${textTransformProp} ${textAlign ? `text-align: ${textAlign};` : ""} ${whiteSpaceProp} ${directionProp} ${opacityProp} ${wordBreakProp} mso-line-height-rule: exactly;">
1741
+ ${typeof children === "string" ? children : ""}
1742
+ </a>`
1743
+ : `<span style="${color ? `color: ${color};` : ""} ${textDecorationStyle} display: block; ${safeFontFamily ? `font-family: ${safeFontFamily};` : ""} ${fontSize ? `font-size: ${fontSize};` : ""} ${fontWeight ? `font-weight: ${fontWeight};` : ""} ${fontStyleProp} ${lineHeight ? `line-height: ${lineHeight};` : ""} ${letterSpacingProp} ${textTransformProp} ${textAlign ? `text-align: ${textAlign};` : ""} ${whiteSpaceProp} ${directionProp} ${opacityProp} ${wordBreakProp} mso-line-height-rule: exactly;">
1744
+ ${typeof children === "string" ? children : ""}
1745
+ </span>`}
1726
1746
  </td>
1727
1747
  </tr>
1728
1748
  </table>
@@ -1736,7 +1756,7 @@ function Button({ config, devMode }) {
1736
1756
  // fontFamily uses the sanitized value so embedded quotes never break the
1737
1757
  // style attribute string (which is always wrapped in double quotes).
1738
1758
  const sharedTextStyles = [
1739
- `color: ${color};`,
1759
+ color ? `color: ${color};` : "",
1740
1760
  safeFontFamily ? `font-family: ${safeFontFamily};` : "",
1741
1761
  fontSize ? `font-size: ${fontSize};` : "",
1742
1762
  fontWeight ? `font-weight: ${fontWeight};` : "",
@@ -1749,7 +1769,7 @@ function Button({ config, devMode }) {
1749
1769
  : "",
1750
1770
  direction ? `direction: ${direction};` : "",
1751
1771
  opacity !== undefined ? `opacity: ${opacity};` : "",
1752
- whiteSpace !== "normal" ? `white-space: ${whiteSpace};` : "",
1772
+ whiteSpace ? `white-space: ${whiteSpace};` : "",
1753
1773
  ]
1754
1774
  .filter(Boolean)
1755
1775
  .join(" ");
@@ -1764,33 +1784,43 @@ function Button({ config, devMode }) {
1764
1784
  padding: 0,
1765
1785
  }, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { align: align, style: {
1766
1786
  padding: 0,
1767
- }, children: jsxRuntime.jsx("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: Object.assign(Object.assign({
1787
+ }, children: jsxRuntime.jsx("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
1768
1788
  // --- Start dev
1769
- position: "relative",
1789
+ position: "relative",
1770
1790
  // --- End dev
1771
- width: width || "auto" }, (maxWidth && { maxWidth: maxWidth })), { borderCollapse: "collapse",
1791
+ width: width || "auto",
1792
+ ...(maxWidth && { maxWidth: maxWidth }),
1793
+ borderCollapse: "collapse",
1772
1794
  // base
1773
- boxSizing: "border-box", border: 0, margin: 0, padding: 0 }), onClick: devMode ? (e) => e.preventDefault() : undefined, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { dangerouslySetInnerHTML: {
1795
+ boxSizing: "border-box",
1796
+ border: 0,
1797
+ margin: 0,
1798
+ padding: 0,
1799
+ }, onClick: devMode ? (e) => e.preventDefault() : undefined, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { dangerouslySetInnerHTML: {
1774
1800
  __html: `
1775
- ${devMode ? "" : useSimpleOutlookApproach ? simpleOutlookButton : vmlButton}
1801
+ ${useSimpleOutlookApproach ? simpleOutlookButton : vmlButton}
1776
1802
  <!--[if !mso]><!-->
1777
1803
  <table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse: collapse; width: 100%;">
1778
1804
  <tbody>
1779
1805
  <tr>
1780
- <td style="background-color: ${backgroundTdStyle.backgroundColor}; border-radius: ${backgroundTdStyle.borderRadius}; width: ${backgroundTdStyle.width}; ${maxWidth ? `max-width: ${maxWidth};` : ""} ${borderRadius ? "overflow: hidden;" : ""}">
1781
- <table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse: separate; border-spacing: 0; border-radius: ${borderRadius}; width: 100%; ${borderStyleString}">
1806
+ <td style="${backgroundTdStyle.backgroundColor ? `background-color: ${backgroundTdStyle.backgroundColor};` : ""} ${backgroundTdStyle.borderRadius ? `border-radius: ${backgroundTdStyle.borderRadius};` : ""} width: ${backgroundTdStyle.width}; ${maxWidth ? `max-width: ${maxWidth};` : ""} ${borderRadius ? "overflow: hidden;" : ""}">
1807
+ <table role="presentation" cellpadding="0" cellspacing="0" border="0" style="border-collapse: separate; border-spacing: 0; ${borderRadius ? `border-radius: ${borderRadius};` : ""} width: 100%; ${borderStyleString}">
1782
1808
  <tbody>
1783
1809
  <tr>
1784
1810
  <td style="padding: 0;">
1785
1811
  ${devMode
1786
- ? `<span style="${sharedTextStyles} display: block; text-align: ${textAlign}; word-break: ${wordBreak}; padding: ${padding};">
1812
+ ? `<span style="${sharedTextStyles} ${textDecoration && textDecoration !== "none" ? "" : "text-decoration: none;"} display: block; ${wordBreak ? `word-break: ${wordBreak};` : ""} ${textAlign ? `text-align: ${textAlign};` : ""} ${padding ? `padding: ${padding};` : ""}">
1787
1813
  ${typeof children === "string" ? children : ""}
1788
1814
  </span>`
1789
- : `<a href="${href}" target="_blank" rel="noopener noreferrer" style="${sharedTextStyles} ${textDecoration && textDecoration !== "none" ? "" : "text-decoration: none;"} display: block; word-break: ${wordBreak}; text-align: ${textAlign}; padding: ${padding};">
1790
- <span style="${sharedTextStyles}">
1815
+ : href
1816
+ ? `<a href="${href}" target="${target}" rel="noopener noreferrer" style="${sharedTextStyles} ${textDecoration && textDecoration !== "none" ? "" : "text-decoration: none;"} display: block; ${wordBreak ? `word-break: ${wordBreak};` : ""} ${textAlign ? `text-align: ${textAlign};` : ""} ${padding ? `padding: ${padding};` : ""}">
1817
+ <span>
1818
+ ${typeof children === "string" ? children : ""}
1819
+ </span>
1820
+ </a>`
1821
+ : `<span style="${sharedTextStyles} ${textDecoration && textDecoration !== "none" ? "" : "text-decoration: none;"} display: block; ${wordBreak ? `word-break: ${wordBreak};` : ""} ${textAlign ? `text-align: ${textAlign};` : ""} ${padding ? `padding: ${padding};` : ""}">
1791
1822
  ${typeof children === "string" ? children : ""}
1792
- </span>
1793
- </a>`}
1823
+ </span>`}
1794
1824
  </td>
1795
1825
  </tr>
1796
1826
  </tbody>
@@ -1867,14 +1897,31 @@ function Column({ children, config, devNode }) {
1867
1897
  };
1868
1898
  // 2. Outer TD style: Background and Border Radius (no border here).
1869
1899
  // height is set so the TD occupies the full declared height.
1870
- const outerTdStyle = Object.assign({ width: config.width, height: config.height, backgroundColor: config.backgroundColor, borderRadius: config.borderRadius,
1900
+ const outerTdStyle = {
1901
+ width: config.width,
1902
+ height: config.height,
1903
+ backgroundColor: config.backgroundColor,
1904
+ borderRadius: config.borderRadius,
1871
1905
  // Background Image styles
1872
1906
  backgroundImage: config.backgroundImage
1873
1907
  ? `url(${config.backgroundImage.src})`
1874
- : undefined, backgroundRepeat: (_a = config.backgroundImage) === null || _a === void 0 ? void 0 : _a.repeat, backgroundSize: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.size, backgroundPosition: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.position }, (config.borderRadius && { overflow: "hidden" }));
1908
+ : undefined,
1909
+ backgroundRepeat: (_a = config.backgroundImage) === null || _a === void 0 ? void 0 : _a.repeat,
1910
+ backgroundSize: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.size,
1911
+ backgroundPosition: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.position,
1912
+ // Overflow hidden to clip background to border-radius
1913
+ ...(config.borderRadius && { overflow: "hidden" }),
1914
+ };
1875
1915
  // 2b. Inner table style: Border and Border Radius.
1876
1916
  // height: 100% so it stretches to fill the outer TD's declared height.
1877
- const innerTableStyle = Object.assign({ width: "100%", height: "100%", borderCollapse: "separate", borderSpacing: 0, borderRadius: config.borderRadius }, getBorderStyle$5(config.border));
1917
+ const innerTableStyle = {
1918
+ width: "100%",
1919
+ height: "100%", // fill the outer TD rather than re-declaring the pixel value
1920
+ borderCollapse: "separate",
1921
+ borderSpacing: 0,
1922
+ borderRadius: config.borderRadius,
1923
+ ...getBorderStyle$5(config.border),
1924
+ };
1878
1925
  // 3. Inner TD style: Padding and Vertical Alignment only.
1879
1926
  // *** No height here. ***
1880
1927
  // The outer TD/table owns the height; padding is purely inner spacing,
@@ -1906,7 +1953,10 @@ function Column({ children, config, devNode }) {
1906
1953
  : "top", align: config.alignItems
1907
1954
  ? alignMap$2[config.alignItems]
1908
1955
  : "left", children: child }) }), index < numChildren - 1 && (jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: gapSpacerStyle, children: "\u00A0" }) }))] }, `col-child-${index}`))) }) })) : (children) }) }) }) }));
1909
- return (jsxRuntime.jsxs("table", Object.assign({ "aria-label": "Column Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: Object.assign({ position: "relative" }, outerTableStyle) }, (config.height && { height: config.height }), { children: [jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", Object.assign({ style: outerTdStyle }, (config.width && { width: config.width }), (config.height && { height: config.height }), { children: renderContent() })) }) }), devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] })));
1956
+ return (jsxRuntime.jsxs("table", { "aria-label": "Column Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
1957
+ position: "relative",
1958
+ ...outerTableStyle,
1959
+ }, ...(config.height && { height: config.height }), children: [jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: outerTdStyle, ...(config.width && { width: config.width }), ...(config.height && { height: config.height }), children: renderContent() }) }) }), devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] }));
1910
1960
  }
1911
1961
  var Column_default = React.memo(Column, arePropsEqual);
1912
1962
 
@@ -2011,11 +2061,27 @@ function Container({ children, config, devMode, devNode }) {
2011
2061
  borderCollapse: "collapse",
2012
2062
  };
2013
2063
  // 1. Background TD Style - Background color, border radius, background image
2014
- const backgroundTdStyle = Object.assign({ backgroundColor: config.backgroundColor, borderRadius: config.borderRadius, maxWidth: widthType === "fixed" ? config.width || "600px" : undefined, backgroundImage: config.backgroundImage
2064
+ const backgroundTdStyle = {
2065
+ backgroundColor: config.backgroundColor,
2066
+ borderRadius: config.borderRadius,
2067
+ maxWidth: widthType === "fixed" ? config.width || "600px" : undefined,
2068
+ backgroundImage: config.backgroundImage
2015
2069
  ? `url(${config.backgroundImage.src})`
2016
- : undefined, backgroundRepeat: (_a = config.backgroundImage) === null || _a === void 0 ? void 0 : _a.repeat, backgroundSize: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.size, backgroundPosition: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.position }, (config.borderRadius && { overflow: "hidden" }));
2070
+ : undefined,
2071
+ backgroundRepeat: (_a = config.backgroundImage) === null || _a === void 0 ? void 0 : _a.repeat,
2072
+ backgroundSize: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.size,
2073
+ backgroundPosition: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.position,
2074
+ // Overflow hidden to clip background to border-radius
2075
+ ...(config.borderRadius && { overflow: "hidden" }),
2076
+ };
2017
2077
  // 2. Border Table Style - Border and border radius
2018
- const borderTableStyle = Object.assign({ width: "100%", borderCollapse: "separate", borderSpacing: 0, borderRadius: config.borderRadius }, getBorderStyle$4(config.border));
2078
+ const borderTableStyle = {
2079
+ width: "100%",
2080
+ borderCollapse: "separate",
2081
+ borderSpacing: 0,
2082
+ borderRadius: config.borderRadius,
2083
+ ...getBorderStyle$4(config.border),
2084
+ };
2019
2085
  // 3. Padding TD Style
2020
2086
  const innerTdStyle = {
2021
2087
  padding: config.padding,
@@ -2055,7 +2121,10 @@ function Container({ children, config, devMode, devNode }) {
2055
2121
  }
2056
2122
  return (jsxRuntime.jsx("td", { className: isStacking ? "stack-td" : undefined, width: getChildWidths[index], style: childTdStyle, children: child }, `child-${index}`));
2057
2123
  });
2058
- return (jsxRuntime.jsx("table", { "aria-label": `Container | Table Outer`, cellPadding: 0, cellSpacing: 0, role: "presentation", border: 0, style: Object.assign({ position: "relative" }, outerTableStyle), children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsxs("td", { align: justifyAlign, children: [jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: msoFixedWrapper } }), jsxRuntime.jsxs("table", { className: [
2124
+ return (jsxRuntime.jsx("table", { "aria-label": `Container | Table Outer`, cellPadding: 0, cellSpacing: 0, role: "presentation", border: 0, style: {
2125
+ position: "relative",
2126
+ ...outerTableStyle,
2127
+ }, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsxs("td", { align: justifyAlign, children: [jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: msoFixedWrapper } }), jsxRuntime.jsxs("table", { className: [
2059
2128
  widthType === "fixed" ? "container-fixed-width" : undefined,
2060
2129
  devMode ? "main-wrapper relative" : undefined,
2061
2130
  ]
@@ -2070,40 +2139,48 @@ var Container_default = React.memo(Container, arePropsEqual);
2070
2139
 
2071
2140
  function Divider({ config, devNode }) {
2072
2141
  const { height = "1px", color = "#cccccc", width = "100%", margin = "20px 0", align = "center", hideOnMobile, } = config;
2073
- // 1. Outer TD Style: Applies the vertical spacing (margin)
2074
- const outerTdStyle = {
2075
- padding: margin,
2076
- fontSize: "0",
2077
- lineHeight: "0",
2078
- width: "100%",
2079
- };
2080
- // 2. Divider Table Style: Applies the line properties
2081
- const dividerTableStyle = {
2082
- width: width,
2083
- height: height,
2084
- backgroundColor: color,
2085
- borderCollapse: "collapse",
2086
- border: "0",
2087
- // ✅ FIX 1: Use string literal indexing for MSO properties
2088
- // ["mso-table-lspace" as string]: "0pt",
2089
- ["msoTableLspace"]: "0pt",
2090
- // ["mso-table-rspace" as string]: "0pt",
2091
- ["msoTableRspace"]: "0pt",
2092
- };
2093
- // Parse height for the HTML attribute
2094
- const dividerHeightAttribute = parseInt(height, 10) || 1;
2095
- return (jsxRuntime.jsxs("table", { "aria-label": "Divider Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2096
- // --- Start dev
2097
- position: "relative",
2098
- // --- End dev
2142
+ const heightPx = parseInt(height, 10) || 1;
2143
+ // Parse margin into paddingTop / paddingBottom for the outer TD.
2144
+ // Outlook ignores shorthand "20px 0" on TDs — must be explicit.
2145
+ const [marginTopRaw = "0", marginRightRaw = "0", marginBottomRaw, marginLeftRaw] = margin.trim().split(/\s+/);
2146
+ const marginTop = marginTopRaw;
2147
+ const marginBottom = marginBottomRaw !== null && marginBottomRaw !== void 0 ? marginBottomRaw : marginTopRaw; // "20px 0" → top=20px, bottom=20px
2148
+ // Outlook requires align on the outer TD to correctly position
2149
+ // a fixed-width inner table (e.g. width="300px").
2150
+ const alignAttr = align === "left" ? "left" : align === "right" ? "right" : "center";
2151
+ return (jsxRuntime.jsxs("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2152
+ position: "relative", // dev overlay anchor
2099
2153
  width: "100%",
2100
2154
  borderCollapse: "collapse",
2101
- }, className: hideOnMobile ? "hide-on-mobile" : undefined, children: [jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: outerTdStyle, align: align, children: jsxRuntime.jsx("table", Object.assign({ "aria-label": "Divider Line", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, align: align, style: dividerTableStyle }, { height: dividerHeightAttribute }, { children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: {
2155
+ border: "0",
2156
+ }, className: hideOnMobile ? "hide-on-mobile" : undefined, children: [jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { align: alignAttr, style: {
2157
+ paddingTop: marginTop,
2158
+ paddingBottom: marginBottom,
2159
+ paddingLeft: "0",
2160
+ paddingRight: "0",
2161
+ fontSize: "0",
2162
+ lineHeight: "0",
2163
+ }, children: jsxRuntime.jsx("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, align: alignAttr, style: {
2164
+ width: width,
2165
+ borderCollapse: "collapse",
2166
+ border: "0",
2167
+ }, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { ...{ height: heightPx }, ref: (el) => {
2168
+ if (!el)
2169
+ return;
2170
+ el.setAttribute("style", `height:${height};` +
2171
+ `line-height:${height};` +
2172
+ `font-size:0;` +
2173
+ `padding:0;` +
2174
+ `background-color:${color};` +
2175
+ `mso-line-height-rule:exactly;`);
2176
+ }, style: {
2177
+ // Fallback for non-Outlook clients (React-rendered style object).
2102
2178
  height: height,
2179
+ lineHeight: height,
2103
2180
  fontSize: "0",
2104
- lineHeight: "0",
2105
2181
  padding: "0",
2106
- }, children: "\u00A0" }) }) }) })) }) }) }), devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] }));
2182
+ backgroundColor: color,
2183
+ } }) }) }) }) }) }) }), devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] }));
2107
2184
  }
2108
2185
  var Divider_default = React.memo(Divider, arePropsEqual);
2109
2186
 
@@ -2281,7 +2358,7 @@ function Head({ children, backgroundColor = "#ffffff", title = "Email Preview",
2281
2358
  }
2282
2359
  h1, h2, h3, h4, h5, h6 { margin: 0; padding: 0; font-weight: inherit; }
2283
2360
  `;
2284
- return (jsxRuntime.jsxs("head", { children: [jsxRuntime.jsx("meta", { httpEquiv: "Content-Type", content: "text/html; charset=utf-8" }), jsxRuntime.jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0" }), jsxRuntime.jsx("meta", { httpEquiv: "X-UA-Compatible", content: "IE=edge" }), jsxRuntime.jsx("title", { children: title }), fonts.flatMap((resolved) => resolved.fontProps.map((props, i) => (jsxRuntime.jsx(Font, Object.assign({}, props), `${resolved.family}-${props.fontWeight}-${props.fontStyle}-${i}`)))), children, jsxRuntime.jsx("style", { type: "text/css", dangerouslySetInnerHTML: { __html: msoResetStyles } }), jsxRuntime.jsx("style", { type: "text/css", dangerouslySetInnerHTML: { __html: globalStyles } })] }));
2361
+ return (jsxRuntime.jsxs("head", { children: [jsxRuntime.jsx("meta", { httpEquiv: "Content-Type", content: "text/html; charset=utf-8" }), jsxRuntime.jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0" }), jsxRuntime.jsx("meta", { httpEquiv: "X-UA-Compatible", content: "IE=edge" }), jsxRuntime.jsx("title", { children: title }), fonts.flatMap((resolved) => resolved.fontProps.map((props, i) => (jsxRuntime.jsx(Font, { ...props }, `${resolved.family}-${props.fontWeight}-${props.fontStyle}-${i}`)))), children, jsxRuntime.jsx("style", { type: "text/css", dangerouslySetInnerHTML: { __html: msoResetStyles } }), jsxRuntime.jsx("style", { type: "text/css", dangerouslySetInnerHTML: { __html: globalStyles } })] }));
2285
2362
  }
2286
2363
 
2287
2364
  function Heading({ config, devMode, children }) {
@@ -2348,6 +2425,27 @@ function Html({ children, backgroundColor = "#ffffff", }) {
2348
2425
  );
2349
2426
  }
2350
2427
 
2428
+ // Helper to build link href based on innerLink type
2429
+ function buildLinkHref$1(innerLink) {
2430
+ if (!innerLink || innerLink.type === "none")
2431
+ return null;
2432
+ switch (innerLink.type) {
2433
+ case "url":
2434
+ return innerLink.url || null;
2435
+ case "email":
2436
+ return innerLink.email ? `mailto:${innerLink.email}` : null;
2437
+ case "phone":
2438
+ return innerLink.phone ? `tel:${innerLink.phone}` : null;
2439
+ case "anchor":
2440
+ return innerLink.anchor ? `#${innerLink.anchor}` : null;
2441
+ case "page_top":
2442
+ return "#top";
2443
+ case "page_bottom":
2444
+ return "#bottom";
2445
+ default:
2446
+ return null;
2447
+ }
2448
+ }
2351
2449
  function getBorderStyle$3(border) {
2352
2450
  if (!border)
2353
2451
  return {};
@@ -2389,8 +2487,11 @@ function getBorderStyleString$1(border) {
2389
2487
  return styles.join(" ");
2390
2488
  }
2391
2489
  function Image({ config, devNode, devMode }) {
2392
- var _a;
2393
- const { src, alt, href, target, mobile } = config;
2490
+ var _a, _b;
2491
+ const { src, alt, innerLink, mobile } = config;
2492
+ // Resolve href and target from innerLink
2493
+ const href = buildLinkHref$1(innerLink);
2494
+ const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_blank";
2394
2495
  const seed = src + (alt || "");
2395
2496
  const instanceId = seed
2396
2497
  .split("")
@@ -2405,38 +2506,89 @@ function Image({ config, devNode, devMode }) {
2405
2506
  // Determine the table's "initial" width.
2406
2507
  // If it's 300px, the table should be 300px, not 100%.
2407
2508
  const tableWidth = isPercent ? desktopWidth : `${widthAttr}px`;
2408
- // 2. Mobile Overrides (Every property used)
2509
+ // When width is a percentage, Outlook ignores CSS and renders the image at
2510
+ // its intrinsic pixel size. Setting a concrete `width` HTML attribute gives
2511
+ // Outlook a value to constrain against while modern clients continue to use
2512
+ // the CSS `width: 100%` for fluid rendering.
2513
+ //
2514
+ // If `maxWidth` is a pixel value (e.g. "600px"), we extract the number and
2515
+ // use it as the HTML `width` attribute so Outlook enforces that cap.
2516
+ // Other clients ignore the attribute and rely on CSS styles instead.
2517
+ // If `maxWidth` is not set or is not a pixel value (e.g. "100%"), we fall
2518
+ // back to the original behaviour (numeric string for px widths, undefined
2519
+ // for % widths).
2520
+ const maxWidthPx = ((_b = config.maxWidth) === null || _b === void 0 ? void 0 : _b.endsWith("px"))
2521
+ ? parseInt(config.maxWidth, 10)
2522
+ : undefined;
2523
+ const imgWidthAttr = isPercent ? (maxWidthPx !== null && maxWidthPx !== void 0 ? maxWidthPx : undefined) : widthAttr;
2524
+ // 2. Mobile Overrides — only emit CSS properties that are explicitly set,
2525
+ // so unspecified properties are left untouched (no forced defaults).
2409
2526
  let mobileCss = "";
2410
2527
  if (mobile) {
2528
+ // .wrap-${imgClass} rules
2529
+ const wrapRules = [
2530
+ // Always reset min-width so the px lock from desktop can be overridden
2531
+ "min-width: 0 !important;",
2532
+ ];
2533
+ if (mobile.width !== undefined)
2534
+ wrapRules.push(`width: ${mobile.width} !important;`);
2535
+ if (mobile.maxWidth !== undefined)
2536
+ wrapRules.push(`max-width: ${mobile.maxWidth} !important;`);
2537
+ // .td-${imgClass} rules
2538
+ const tdRules = [];
2539
+ if (mobile.padding !== undefined)
2540
+ tdRules.push(`padding: ${mobile.padding} !important;`);
2541
+ if (mobile.backgroundColor !== undefined)
2542
+ tdRules.push(`background-color: ${mobile.backgroundColor} !important;`);
2543
+ // .${imgClass} rules
2544
+ const imgRules = [];
2545
+ if (mobile.width !== undefined)
2546
+ imgRules.push(`width: ${mobile.width} !important;`);
2547
+ if (mobile.height !== undefined)
2548
+ imgRules.push(`height: ${mobile.height} !important;`);
2549
+ if (mobile.maxWidth !== undefined)
2550
+ imgRules.push(`max-width: ${mobile.maxWidth} !important;`);
2551
+ if (mobile.maxHeight !== undefined)
2552
+ imgRules.push(`max-height: ${mobile.maxHeight} !important;`);
2553
+ if (mobile.borderRadius !== undefined)
2554
+ imgRules.push(`border-radius: ${mobile.borderRadius} !important;`);
2555
+ if (mobile.hidden !== undefined)
2556
+ imgRules.push(`display: ${mobile.hidden ? "none" : "block"} !important;`);
2557
+ if (mobile.objectFit !== undefined)
2558
+ imgRules.push(`object-fit: ${mobile.objectFit} !important;`);
2559
+ if (mobile.objectPosition !== undefined)
2560
+ imgRules.push(`object-position: ${mobile.objectPosition} !important;`);
2561
+ if (mobile.border !== undefined)
2562
+ imgRules.push(getBorderStyleString$1(mobile.border));
2411
2563
  mobileCss = `
2412
2564
  @media screen and (max-width: 768px) {
2413
2565
  .wrap-${imgClass} {
2414
2566
  /* This breaks the px lock from desktop and makes it fluid */
2415
- width: ${mobile.width || "100%"} !important;
2416
- max-width: ${mobile.maxWidth || "100%"} !important;
2417
- min-width: 0 !important;
2567
+ ${wrapRules.join("\n ")}
2418
2568
  }
2419
2569
  .td-${imgClass} {
2420
- padding: ${mobile.padding || "0"} !important;
2421
- background-color: ${mobile.backgroundColor || "transparent"} !important;
2422
- width: 100% !important;
2570
+ ${tdRules.join("\n ")}
2423
2571
  }
2424
2572
  .${imgClass} {
2425
- width: ${mobile.width || "100%"} !important;
2426
- height: ${mobile.height || "auto"} !important;
2427
- max-width: ${mobile.maxWidth || "100%"} !important;
2428
- max-height: ${mobile.maxHeight || "none"} !important;
2429
- border-radius: ${mobile.borderRadius || "0"} !important;
2430
- display: ${mobile.hidden ? "none" : "block"} !important;
2431
- object-fit: ${mobile.objectFit || "fill"} !important;
2432
- object-position: ${mobile.objectPosition || "center"} !important;
2433
- ${getBorderStyleString$1(mobile.border)}
2573
+ ${imgRules.join("\n ")}
2434
2574
  }
2435
2575
  }
2436
2576
  `;
2437
2577
  }
2438
- const imgStyle = Object.assign(Object.assign({ display: "block", width: isPercent ? "100%" : desktopWidth, height: config.height || "auto", maxWidth: config.maxWidth || "100%", maxHeight: config.maxHeight || "none", borderRadius: config.borderRadius || "0" }, getBorderStyle$3(config.border)), { outline: "none", textDecoration: "none", objectFit: config.objectFit, objectPosition: config.objectPosition });
2439
- const imageElement = (jsxRuntime.jsx("img", { src: src, alt: alt, width: !isPercent ? widthAttr : undefined, height: heightAttr !== "auto" ? heightAttr : undefined, className: imgClass, style: imgStyle }));
2578
+ const imgStyle = {
2579
+ display: "block",
2580
+ width: isPercent ? "100%" : desktopWidth,
2581
+ height: config.height || "auto",
2582
+ maxWidth: config.maxWidth || "100%",
2583
+ maxHeight: config.maxHeight || "none",
2584
+ borderRadius: config.borderRadius || "0",
2585
+ ...getBorderStyle$3(config.border),
2586
+ outline: "none",
2587
+ textDecoration: "none",
2588
+ objectFit: config.objectFit,
2589
+ objectPosition: config.objectPosition,
2590
+ };
2591
+ const imageElement = (jsxRuntime.jsx("img", { src: src, alt: alt, width: imgWidthAttr, height: heightAttr !== "auto" ? heightAttr : undefined, className: imgClass, style: imgStyle, draggable: !devMode }));
2440
2592
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [mobile && jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: mobileCss } }), jsxRuntime.jsxs("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, className: `wrap-${imgClass}`, align: "center" // Ensures a 300px image stays centered in its parent
2441
2593
  , style: {
2442
2594
  width: tableWidth, // Fixed px here prevents the 100% "ghost space"
@@ -2449,7 +2601,9 @@ function Image({ config, devNode, devMode }) {
2449
2601
  fontSize: "0",
2450
2602
  lineHeight: "0",
2451
2603
  width: tableWidth, // Lock the cell as well
2452
- }, children: href && !devMode ? (jsxRuntime.jsx("a", { href: href, target: target, style: { display: "block", width: "100%" }, children: imageElement })) : (imageElement) }) }) }), devMode && !!devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] })] }));
2604
+ }, children: href && !devMode ? (jsxRuntime.jsx("a", { href: href, target: target, ...(target === "_blank"
2605
+ ? { rel: "noopener noreferrer" }
2606
+ : {}), style: { display: "block", width: "100%" }, children: imageElement })) : (imageElement) }) }) }), devMode && !!devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] })] }));
2453
2607
  }
2454
2608
  var Image_default = React.memo(Image, arePropsEqual);
2455
2609
 
@@ -2523,13 +2677,30 @@ function Row({ children, config, devNode, devMode }) {
2523
2677
  const childrenArray = (Array.isArray(children) ? children : [children]).filter((child) => child != null);
2524
2678
  const numChildren = childrenArray.length;
2525
2679
  const href = getHrefFromInnerLink(config.innerLink);
2526
- const target = (_a = config.innerLink) === null || _a === void 0 ? void 0 : _a.target;
2680
+ const target = ((_a = config.innerLink) === null || _a === void 0 ? void 0 : _a.target) || "_blank";
2527
2681
  // 1. Outer TD: Background, Border Radius, Width, Height.
2528
- const backgroundTdStyle = Object.assign({ backgroundColor: config.backgroundColor, borderRadius: config.borderRadius, width: config.width || "100%", height: config.height, backgroundImage: config.backgroundImage
2682
+ const backgroundTdStyle = {
2683
+ backgroundColor: config.backgroundColor,
2684
+ borderRadius: config.borderRadius,
2685
+ width: config.width || "100%",
2686
+ height: config.height,
2687
+ backgroundImage: config.backgroundImage
2529
2688
  ? `url(${config.backgroundImage.src})`
2530
- : undefined, backgroundRepeat: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.repeat, backgroundSize: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.size, backgroundPosition: (_d = config.backgroundImage) === null || _d === void 0 ? void 0 : _d.position }, (config.borderRadius && { overflow: "hidden" }));
2689
+ : undefined,
2690
+ backgroundRepeat: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.repeat,
2691
+ backgroundSize: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.size,
2692
+ backgroundPosition: (_d = config.backgroundImage) === null || _d === void 0 ? void 0 : _d.position,
2693
+ ...(config.borderRadius && { overflow: "hidden" }),
2694
+ };
2531
2695
  // 2. Inner Table: Border and Border Radius.
2532
- const borderTableStyle = Object.assign({ width: "100%", height: "100%", borderCollapse: "separate", borderSpacing: 0, borderRadius: config.borderRadius }, getBorderStyle$2(config.border));
2696
+ const borderTableStyle = {
2697
+ width: "100%",
2698
+ height: "100%",
2699
+ borderCollapse: "separate",
2700
+ borderSpacing: 0,
2701
+ borderRadius: config.borderRadius,
2702
+ ...getBorderStyle$2(config.border),
2703
+ };
2533
2704
  // 3. Padding TD.
2534
2705
  const paddingTdStyle = {
2535
2706
  padding: config.padding,
@@ -2547,7 +2718,13 @@ function Row({ children, config, devNode, devMode }) {
2547
2718
  // Content table fills available space, giving Outlook Classic a hard
2548
2719
  // boundary so text children get a constrained box and line wrapping
2549
2720
  // triggers correctly. Use for rows containing text + image layouts.
2550
- const contentTableStyle = Object.assign({ width: config.fillWidth ? "100%" : "auto", height: "100%", borderCollapse: "collapse", minWidth: "1px" }, (!config.fillWidth && { maxWidth: config.width || "100%" }));
2721
+ const contentTableStyle = {
2722
+ width: config.fillWidth ? "100%" : "auto",
2723
+ height: "100%",
2724
+ borderCollapse: "collapse",
2725
+ minWidth: "1px",
2726
+ ...(!config.fillWidth && { maxWidth: config.width || "100%" }),
2727
+ };
2551
2728
  // 5. Gap TD.
2552
2729
  const gapTdStyle = {
2553
2730
  width: config.gap || "0",
@@ -2558,28 +2735,28 @@ function Row({ children, config, devNode, devMode }) {
2558
2735
  ? justifyMap$1[config.justifyContent]
2559
2736
  : "left";
2560
2737
  const tdValign = config.alignItems ? alignMap[config.alignItems] : "top";
2561
- const content = (jsxRuntime.jsxs("table", Object.assign({ "aria-label": "Row Outer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2738
+ const content = (jsxRuntime.jsxs("table", { "aria-label": "Row Outer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2562
2739
  position: "relative",
2563
2740
  width: config.width || "100%",
2564
2741
  height: config.height,
2565
2742
  borderCollapse: "collapse",
2566
- } }, (config.height && { height: config.height }), { children: [jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", Object.assign({ style: backgroundTdStyle }, (config.height && { height: config.height }), { children: jsxRuntime.jsx("table", { "aria-label": "Row Border Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: borderTableStyle, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: paddingTdStyle, children: jsxRuntime.jsx("table", { "aria-label": "Row Justification Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2743
+ }, ...(config.height && { height: config.height }), children: [jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: backgroundTdStyle, ...(config.height && { height: config.height }), children: jsxRuntime.jsx("table", { "aria-label": "Row Border Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: borderTableStyle, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: paddingTdStyle, children: jsxRuntime.jsx("table", { "aria-label": "Row Justification Wrapper", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2567
2744
  width: "100%",
2568
2745
  height: "100%",
2569
2746
  borderCollapse: "collapse",
2570
- }, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { align: tdAlign, width: "100%", style: { width: "100%" }, children: jsxRuntime.jsx("table", Object.assign({ "aria-label": "Row Content", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: contentTableStyle }, (config.height && { height: config.height }), { className: "content-table row-content-table", "data-mobile-justify": (_e = config.mobile) === null || _e === void 0 ? void 0 : _e.justifyContent, "data-mobile-align": (_f = config.mobile) === null || _f === void 0 ? void 0 : _f.alignItems, "data-mobile-wrap": ((_g = config.mobile) === null || _g === void 0 ? void 0 : _g.wrap) ? "true" : undefined, "data-gap": config.gap, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { className: "content-tr", children: childrenArray.map((child, index) => (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx("td", { valign: tdValign, style: {
2747
+ }, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { align: tdAlign, width: "100%", style: { width: "100%" }, children: jsxRuntime.jsx("table", { "aria-label": "Row Content", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: contentTableStyle, ...(config.height && { height: config.height }), className: "content-table row-content-table", "data-mobile-justify": (_e = config.mobile) === null || _e === void 0 ? void 0 : _e.justifyContent, "data-mobile-align": (_f = config.mobile) === null || _f === void 0 ? void 0 : _f.alignItems, "data-mobile-wrap": ((_g = config.mobile) === null || _g === void 0 ? void 0 : _g.wrap) ? "true" : undefined, "data-gap": config.gap, children: jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { className: "content-tr", children: childrenArray.map((child, index) => (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx("td", { valign: tdValign, style: {
2571
2748
  verticalAlign: tdValign,
2572
2749
  textAlign: "left",
2573
2750
  padding: "0",
2574
2751
  margin: "0",
2575
2752
  }, className: "child-cell", children: child }), index < numChildren - 1 &&
2576
- config.gap && (jsxRuntime.jsx("td", { width: config.gap, style: gapTdStyle, className: "row-gap-td", children: "\u00A0" }, `row-gap-${index}`))] }, `row-child-${index}`))) }) }) })) }) }) }) }) }) }) }) }) })) }) }), devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] })));
2753
+ config.gap && (jsxRuntime.jsx("td", { width: config.gap, style: gapTdStyle, className: "row-gap-td", children: "\u00A0" }, `row-gap-${index}`))] }, `row-child-${index}`))) }) }) }) }) }) }) }) }) }) }) }) }) }) }), devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] }));
2577
2754
  if (href && !devMode) {
2578
- return (jsxRuntime.jsx("a", Object.assign({ href: href }, (target && { target }), { style: {
2755
+ return (jsxRuntime.jsx("a", { href: href, ...({ target }), style: {
2579
2756
  textDecoration: "none",
2580
2757
  color: "inherit",
2581
2758
  display: "block",
2582
- }, children: content })));
2759
+ }, children: content }));
2583
2760
  }
2584
2761
  return content;
2585
2762
  }
@@ -2623,9 +2800,18 @@ function getBorderStyle$1(border) {
2623
2800
  const Section = ({ config, children, devNode, }) => {
2624
2801
  var _a, _b, _c;
2625
2802
  const { sectionType, padding } = config;
2626
- return (jsxRuntime.jsxs("table", { "aria-label": `Section |Table | ${sectionType}`, role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: Object.assign(Object.assign({ position: "relative", width: "100%", backgroundColor: config.backgroundColor }, getBorderStyle$1(config.border)), { backgroundImage: config.backgroundImage
2803
+ return (jsxRuntime.jsxs("table", { "aria-label": `Section |Table | ${sectionType}`, role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2804
+ position: "relative",
2805
+ width: "100%",
2806
+ backgroundColor: config.backgroundColor,
2807
+ ...getBorderStyle$1(config.border),
2808
+ backgroundImage: config.backgroundImage
2627
2809
  ? `url(${config.backgroundImage.src})`
2628
- : undefined, backgroundRepeat: (_a = config.backgroundImage) === null || _a === void 0 ? void 0 : _a.repeat, backgroundSize: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.size, backgroundPosition: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.position }), children: [jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: {
2810
+ : undefined,
2811
+ backgroundRepeat: (_a = config.backgroundImage) === null || _a === void 0 ? void 0 : _a.repeat,
2812
+ backgroundSize: (_b = config.backgroundImage) === null || _b === void 0 ? void 0 : _b.size,
2813
+ backgroundPosition: (_c = config.backgroundImage) === null || _c === void 0 ? void 0 : _c.position,
2814
+ }, children: [jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: {
2629
2815
  padding: padding,
2630
2816
  }, children: children }) }) }), devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsxs("td", { children: [jsxRuntime.jsxs("span", { style: {
2631
2817
  backgroundColor: "black",
@@ -2666,11 +2852,14 @@ function Spacer({ config, devNode }) {
2666
2852
  const spacerHeightAttribute = parseInt(height, 10) || 1;
2667
2853
  return (
2668
2854
  // Outer table ensures the spacer spans the full width of its container
2669
- jsxRuntime.jsxs("table", Object.assign({ "aria-label": "Vertical Spacer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: Object.assign({
2855
+ jsxRuntime.jsxs("table", { "aria-label": "Vertical Spacer", role: "presentation", cellPadding: 0, cellSpacing: 0, border: 0, style: {
2670
2856
  // --- Start dev
2671
- position: "relative" }, spacerTableStyle) }, { height: spacerHeightAttribute }, { className: hideOnMobile ? "hide-on-mobile" : undefined, children: [jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: spacerTdStyle,
2857
+ position: "relative",
2858
+ // --- End dev
2859
+ ...spacerTableStyle,
2860
+ }, ...{ height: spacerHeightAttribute }, className: hideOnMobile ? "hide-on-mobile" : undefined, children: [jsxRuntime.jsx("tbody", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { style: spacerTdStyle,
2672
2861
  // Explicit height attribute
2673
- height: spacerHeightAttribute, children: "\u00A0" }) }) }), devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] })));
2862
+ height: spacerHeightAttribute, children: "\u00A0" }) }) }), devNode && (jsxRuntime.jsx("tfoot", { children: jsxRuntime.jsx("tr", { children: jsxRuntime.jsx("td", { children: devNode }) }) }))] }));
2674
2863
  }
2675
2864
  var Spacer_default = React.memo(Spacer, arePropsEqual);
2676
2865
 
@@ -2809,7 +2998,7 @@ function buildIconifyUrl(config) {
2809
2998
  const template = process.env.ICONIFY_API_IMAGE_URI ||
2810
2999
  "https://iconify.pagenflow.com/api/image/{{height}}/{{color}}/{{rotate}}-{{rotate-orientation}}/{{icon-full-name}}.png";
2811
3000
  return template
2812
- .replace("{{height}}", String(numericHeight * 2))
3001
+ .replace("{{height}}", String(numericHeight * 4))
2813
3002
  .replace("{{color}}", cleanColor)
2814
3003
  .replace("{{rotate}}", String(rotate))
2815
3004
  .replace("{{rotate-orientation}}", rotateOrientation)
@@ -2843,7 +3032,7 @@ function Icon({ config, devNode, devMode, children }) {
2843
3032
  // Determine icon source
2844
3033
  const iconSrc = buildIconifyUrl(config);
2845
3034
  const href = buildLinkHref(innerLink);
2846
- const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_self";
3035
+ const target = (innerLink === null || innerLink === void 0 ? void 0 : innerLink.target) || "_blank";
2847
3036
  const align = justifyMap[justifyContent];
2848
3037
  // Get border styles
2849
3038
  const borderStyle = getBorderStyle(border);
@@ -2886,7 +3075,13 @@ function Icon({ config, devNode, devMode, children }) {
2886
3075
  lineHeight: "0",
2887
3076
  };
2888
3077
  // 4. Inner Table Style: Apply border here with border-collapse: separate
2889
- const innerTableStyle = Object.assign({ width: "100%", borderCollapse: "separate", borderSpacing: 0, borderRadius: borderRadius }, borderStyle);
3078
+ const innerTableStyle = {
3079
+ width: "100%",
3080
+ borderCollapse: "separate",
3081
+ borderSpacing: 0,
3082
+ borderRadius: borderRadius,
3083
+ ...borderStyle,
3084
+ };
2890
3085
  // 5. Inner TD Style: Padding
2891
3086
  const innerTdStyle = {
2892
3087
  padding: padding,
@@ -2934,7 +3129,7 @@ function Icon({ config, devNode, devMode, children }) {
2934
3129
  // Icon image element
2935
3130
  const iconElement = devMode && !!children ? (children) : iconSrc ? (jsxRuntime.jsx("img", { draggable: false, src: iconSrc, alt: "", style: imgStyle, width: widthNum, height: heightNum, border: 0 })) : (jsxRuntime.jsx(jsxRuntime.Fragment, {}));
2936
3131
  // Wrap in link if href exists and not in dev mode
2937
- const content = href && !devMode ? (jsxRuntime.jsx("a", Object.assign({ href: href, target: target, style: linkStyle }, (target === "_blank" ? { rel: "noopener noreferrer" } : {}), { children: iconElement }))) : (iconElement);
3132
+ const content = href && !devMode ? (jsxRuntime.jsx("a", { href: href, target: target, style: linkStyle, ...(target === "_blank" ? { rel: "noopener noreferrer" } : {}), children: iconElement })) : (iconElement);
2938
3133
  // Build the HTML content with VML support (only when NOT in dev mode)
2939
3134
  const useVML = !devMode && backgroundColor && numericBorderRadius > 0;
2940
3135
  const htmlContent = useVML