I pulled your guest_icon.svg and it’s an IcoMoon export (plain <path>s with no explicit fill/stroke). Those sometimes disappear if your SVG colorizer/sanitizer expects explicit fill="currentColor" or stroke and ends up returning an empty/transparent result for that one file.

Here’s a safe, drop-in fix: make the colorizer tolerant of IcoMoon SVGs and add a fallback in the renderer so nothing ever shows blank.

1) Update utils/svg.ts

Replace your applySvgColor and sanitizeSvgClient with this more robust pair:

// utils/svg.ts
export function applySvgColor(svg: string, hex: string) {
  try {
    const doc = new DOMParser().parseFromString(svg, "image/svg+xml");
    const root = doc.documentElement;

    // Ensure the root will propagate color
    root.setAttribute("style", `color:${hex}`);
    // Give every shape a sensible default if none set
    const shapeTags = ["path","rect","circle","ellipse","polygon","polyline","line"];
    shapeTags.forEach(tag => {
      Array.from(doc.getElementsByTagName(tag)).forEach((el:any) => {
        const hasFill = el.hasAttribute("fill");
        const hasStroke = el.hasAttribute("stroke");
        // If neither fill nor stroke declared, default to fill currentColor
        if (!hasFill && !hasStroke) el.setAttribute("fill","currentColor");
        // If explicitly "fill:none" and no stroke, show stroke currentColor
        if (el.getAttribute("fill")==="none" && !hasStroke) {
          el.setAttribute("stroke","currentColor");
          el.setAttribute("stroke-width", el.getAttribute("stroke-width") || "2");
        }
      });
    });

    // Add a tiny style to avoid hairlines when scaled
    const STYLE = doc.createElementNS("http://www.w3.org/2000/svg","style");
    STYLE.textContent = `*{vector-effect:non-scaling-stroke}`;
    root.prepend(STYLE);

    return new XMLSerializer().serializeToString(doc);
  } catch {
    // If anything fails, just return original SVG (still visible, just not recolored)
    return svg;
  }
}

export function sanitizeSvgClient(svg: string) {
  try {
    const doc = new DOMParser().parseFromString(svg, "image/svg+xml");
    const allowedTags = new Set([
      "svg","g","path","rect","circle","ellipse","polygon","polyline","line","defs","title","desc"
    ]);
    const allowedAttrs = new Set([
      "viewBox","width","height","fill","stroke","stroke-width","stroke-linecap","stroke-linejoin",
      "d","points","rx","ry","cx","cy","r","x","y","transform","class","style","color","xmlns","version",
      "aria-hidden","role","focusable"
    ]);

    const walk = (el: Element) => {
      // strip disallowed tags
      if (!allowedTags.has(el.tagName)) { el.remove(); return; }
      // strip dangerous/on* attributes & keep only allowed list
      Array.from(el.attributes).forEach(a => {
        const name = a.name;
        if (name.startsWith("on")) el.removeAttribute(name);
        else if (!allowedAttrs.has(name) && !name.startsWith("aria-")) el.removeAttribute(name);
      });
      Array.from(el.children).forEach(ch => walk(ch as Element));
    };
    walk(doc.documentElement);
    return new XMLSerializer().serializeToString(doc);
  } catch {
    return svg;
  }
}

2) Harden the renderer fallback

In your IconRenderer, change the inline render to never show blank if coloring/sanitizing hiccups:

// inside: if (inlineSvg) { ... }
try {
  const colored = applySvgColor(inlineSvg, color);
  const safe = sanitizeSvgClient(colored || inlineSvg);
  return <div className={size} dangerouslySetInnerHTML={{ __html: safe || inlineSvg }} />;
} catch {
  return <div className={size} dangerouslySetInnerHTML={{ __html: inlineSvg }} />;
}


That’s it. This keeps your other icons exactly as-is, and the IcoMoon one will render (filled or stroked) instead of disappearing. If you still see a white square, it means the shape genuinely has fill="none" and no stroke; the rule above adds a default stroke so it becomes visible.