let’s add a true vector “composed logo” SVG (icon + wordmark), so designers get a scalable lockup in the ZIP.

Below is copy-paste code that:

Inlines your generated icon SVG into a new composed SVG

Supports side-by-side and top/bottom layouts

Aligns baselines properly

Adds both normal and reverse (light-on-dark) variants to the ZIP

1) Add a tiny SVG composer util

Put this in src/services/export/composeSvg.ts (or inside your page if you prefer):

// src/services/export/composeSvg.ts
export type ComposeSvgOpts = {
  name: string;
  fontFamily: string;       // e.g., "Inter"
  textSize: number;         // px
  textColor: string;        // hex
  layout: "side-by-side" | "top-bottom";
  iconSize: number;         // px square to render the icon
  pad?: number;             // px spacing between icon and text (default 48)
  iconSvgText: string;      // raw SVG string of the icon
  background?: string | null; // hex or null (transparent). For "reverse" variant, pass dark hex
  textColorOverride?: string; // optional override (e.g., light text for reverse)
};

// quick viewBox parser (fallback to 1000x1000 if missing)
function parseViewBox(svg: string) {
  const m = svg.match(/viewBox="([\d.\-]+)\s+([\d.\-]+)\s+([\d.\-]+)\s+([\d.\-]+)"/i);
  if (!m) return { x: 0, y: 0, w: 1000, h: 1000 };
  return { x: parseFloat(m[1]), y: parseFloat(m[2]), w: parseFloat(m[3]), h: parseFloat(m[4]) };
}

// very small sanitizer: strip outer <svg> and keep inner content
function extractInner(svg: string) {
  const start = svg.indexOf(">") + 1;
  const end = svg.lastIndexOf("</svg>");
  return start > 0 && end > start ? svg.slice(start, end) : svg;
}

// measure text width with canvas (browser)
function measureTextWidth(text: string, fontFamily: string, px: number) {
  const c = document.createElement("canvas");
  const ctx = c.getContext("2d")!;
  ctx.font = `bold ${px}px ${fontFamily}, system-ui, sans-serif`;
  const m = ctx.measureText(text);
  return m.width;
}

export function composeLogoSvg(opts: ComposeSvgOpts) {
  const {
    name, fontFamily, textSize, textColor, layout, iconSize,
    pad = 48, iconSvgText, background = null, textColorOverride,
  } = opts;

  const vb = parseViewBox(iconSvgText);
  const innerIcon = extractInner(iconSvgText);

  // scale icon viewBox to iconSize
  const scaleX = iconSize / vb.w;
  const scaleY = iconSize / vb.h;

  const textW = measureTextWidth(name, fontFamily, textSize);
  const textH = textSize;

  let w = 0, h = 0;
  if (layout === "side-by-side") {
    w = iconSize + pad + textW;
    h = Math.max(iconSize, textH * 1.2);
  } else {
    w = Math.max(iconSize, textW);
    h = iconSize + pad + textH * 1.2;
  }

  const bgRect = background
    ? `<rect x="0" y="0" width="${w}" height="${h}" fill="${background}"/>`
    : "";

  const iconGroup =
    `<g transform="translate(${layout === "side-by-side" ? 0 : (w - iconSize) / 2}, ${layout === "side-by-side" ? (h - iconSize) / 2 : 0}) scale(${scaleX}, ${scaleY}) translate(${-vb.x}, ${-vb.y})">
      ${innerIcon}
    </g>`;

  const textFill = textColorOverride ?? textColor;

  const textEl =
    layout === "side-by-side"
      ? `<text x="${iconSize + pad}" y="${(h + textH * 0.85) / 2}" font-family="${fontFamily}, system-ui, sans-serif" font-weight="700" font-size="${textSize}" fill="${textFill}">${escapeXml(name)}</text>`
      : `<text x="${w / 2}" y="${iconSize + pad + textH * 0.85}" text-anchor="middle" font-family="${fontFamily}, system-ui, sans-serif" font-weight="700" font-size="${textSize}" fill="${textFill}">${escapeXml(name)}</text>`;

  return `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">
  ${bgRect}
  ${iconGroup}
  ${textEl}
</svg>`.trim();
}

function escapeXml(s: string) {
  return s.replace(/[&<>"']/g, (ch) =>
    ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&apos;" } as any)[ch]
  );
}


This keeps the text live (not outlined), so designers can substitute fonts if needed. If you want an outlined wordmark later, I can add an optional text-to-path step using the Canvas glyph metrics.