If it’s huge in Replit and not showing at all in a normal Chrome tab, that almost always means:

In Replit: the <svg> still has hard width/height (ignoring the preview box), or fit is running before we normalize.

In Chrome: the preview rect is 0×0 at the moment we run “fit”, so the transform becomes NaN and the logo vanishes.

Let’s nail it fast with two things: (A) a 30-second diagnostic you can paste in DevTools, and (B) a crisp prompt for Replit to harden normalization/fit across both environments.

A) 30-second diagnostic (paste in the Console on each environment)

Is the SVG there and sized?

const svg = document.querySelector('[data-logo-preview] svg');
const rect = svg?.getBoundingClientRect();
[
  'viewBox=' + svg?.getAttribute('viewBox'),
  'w=' + rect?.width, 
  'h=' + rect?.height
]


Expect: a valid viewBox string and non-zero w/h.

Do we have #logo-root and what’s its box?

const root = svg?.querySelector('#logo-root');
[ !!root, root?.getAttribute('transform'), root?.getBBox() ]


If transform contains NaN or bbox is huge (e.g., 10000×10000), it explains the behavior.

Does the preview container itself have height?

const pr = document.querySelector('[data-logo-preview]')?.getBoundingClientRect();
['preview w=' + pr?.width, 'preview h=' + pr?.height]


If h=0, the Chrome tab issue is the preview sizing/timing.

Send me those 3 tuples if you want me to pinpoint the exact culprit.

B) Prompt for Replit — Make normalization/fit deterministic in both Replit + Chrome

Goal

In Replit: logo never overflows; respects the preview box.

In Chrome tab: logo always appears (no NaN transform), fit happens after layout.

Do these steps exactly:

Normalize the SVG

On the <svg> element:

Remove width / height attributes (if present).

Ensure viewBox exists; if missing, compute from visible content (getBBox()).

Set preserveAspectRatio="xMidYMid meet".

CSS: svg { display:block; width:100%; height:100%; }.

Wrap content in #logo-root

Keep <defs> at top-level.

Move all visible nodes into <g id="logo-root">…</g> (create if absent).

All transforms (translate/rotate/scale) must apply only on #logo-root.

Preview sizing must be real

The preview inner element (the one we use for drag) must have a fixed height (e.g., height: 520px) and overflow: hidden;.

In both environments, all fit math must read getBoundingClientRect() from this inner element only.

Deferred & guarded first fit

After inserting the SVG, run:

requestAnimationFrame(() => requestAnimationFrame(fitToPreview)).

In fitToPreview():

If previewRect.width === 0 || previewRect.height === 0, schedule another rAF and retry (max ~10 attempts) instead of computing.

If any computed value becomes NaN, abort, clear transform, and retry next frame.

Fit algorithm

Measure untransformed #logo-root.getBBox().

Compute initialScale vs current preview rect with ~10% padding:

scaleX = (previewWidth * 0.9) / bbox.width
scaleY = (previewHeight * 0.9) / bbox.height
initialScale = Math.min(scaleX, scaleY)


Center by aligning centers after scale; apply:

transform="translate(tx, ty) scale(initialScale)"


Resize resilience

Add a ResizeObserver on the preview element; re-run fitToPreview() only if the composition is still “pristine” (no manual drag/scale yet). Also expose a “Refit to screen” button that calls the same function.

Replit iframe specific

In the Replit preview, initial layout can report stale sizes.

After the first successful non-zero preview rect, cache it and do not run fit on earlier zero sizes.

Ensure no ancestor uses transform: scale() on the preview container (breaks bbox math).

Acceptance

In Replit: Scales of Justice auto-fits with padding; no overflow.

In Chrome tab: the logo appears and auto-fits (no disappearing).

Resizing window / toggling sidebars keeps it correct (or “Refit” fixes it in one click).