In this site’s opening scene, four product surfaces sit around a three-dimensional mark. Each surface had its own z-index: 3, 4, 5, and 6. The mark had none. Intuitively, the cards should have sat in front.
They did not. The mark’s arms cut through the cards and their text.
Why z-index did not fix it
With transform-style: preserve-3d on a parent, its children no longer stack on a single flat plane. The browser places them in three-dimensional space and lets them intersect. Raising z-index does not remove that geometric intersection.
Our mark was rotated 22 degrees around Y in a 480-pixel box:
480 / 2 × sin(22°) ≈ 90 pixels
Its edges therefore reached about 90 pixels toward the viewer, while the cards remained at z = 0. An intersection was inevitable.
What did not work
We first reduced the mark to 76% of its previous size. Its arms still crossed the receipt and media cards. Reducing the width did not provide enough depth separation.
What worked
We moved the whole mark backward, farther than its nearest arm:
.x-depth {
position: absolute;
inset: 0;
transform: translateZ(-160px) scale(1.145);
transform-style: preserve-3d;
}The scale is not decorative. The parent’s perspective is 1100px, so moving an object back 160 pixels makes it appear smaller by 1100 / (1100 + 160). Multiplying by the inverse, 1260 / 1100 = 1.145, restores its apparent size.
One final detail: this transform lives on a separate wrapper. GSAP animates the mark itself and decomposes its existing 3D transform matrix. Keeping the depth offset separate prevents that decomposition from changing the placement.


