/*
 * Geometry lives here so a resize needs no JS: max() picks whichever is larger --
 * the minimum side gap, or half the leftover space once the card hits its max width.
 * That is what makes wide screens travel further (240px/side at 1920, 560 at 2560).
 */
.tsf-morph {
	--tsf-p: 0;                       /* 0 = full bleed, 1 = card. JS writes this. */
	--tsf-maxw: 1440px;
	--tsf-gap-x: 10px;
	--tsf-gap-y: 10px;
	--tsf-radius: 33px;
	--tsf-bg: transparent;

	/* Percentages resolve against THIS element, not the viewport. 100vw includes the
	   scrollbar, so using it pushed the card ~15px off-centre on any real browser. */
	/* End insets, one per side. JS measures the CONTENT box and writes these, because
	   the finished card hugs its content -- on the real homepage it is 1420x757 inside
	   a 2560x1440 section, so the vertical travel is far bigger than the horizontal
	   gap. Deriving it from the content is the only way both axes land right. */
	--tsf-end-t: 0px;
	--tsf-end-r: 0px;
	--tsf-end-b: 0px;
	--tsf-end-l: 0px;
}

/*
 * The coloured plane is a LAYER BEHIND the content, not the element's own background.
 *
 * The obvious approach -- clip-path on the container -- also clips the content, so on
 * a wide screen the heading gets sliced the moment the card is narrower than the text.
 * Animating padding/width instead would reflow every child on each scroll frame.
 * A pseudo-element does neither: content never moves and never gets cut.
 */
.tsf-morph.is-morph-ready { background-color: transparent !important; position: relative; }

.tsf-morph.is-morph-ready::before {
	content: "";
	position: absolute;
	/* Position + explicit size, rather than left+right / top+bottom.
	   Chromium reported the four longhands correctly but still laid the box out at the
	   full 2560x1440, so the right/bottom insets never took effect. Sizing it directly
	   removes the ambiguity. */
	top:  calc( var(--tsf-p) * var(--tsf-end-t) );
	left: calc( var(--tsf-p) * var(--tsf-end-l) );
	width:  calc( 100% - var(--tsf-p) * ( var(--tsf-end-l) + var(--tsf-end-r) ) );
	height: calc( 100% - var(--tsf-p) * ( var(--tsf-end-t) + var(--tsf-end-b) ) );
	border-radius: calc( var(--tsf-p) * var(--tsf-radius) );
	background-color: var(--tsf-bg);
	background-image: var(--tsf-bgimg, none);
	background-size: cover;
	background-position: center;
	z-index: 0;
	pointer-events: none;
	will-change: top, left, width, height, border-radius;
}

/*
 * Second layer: the section's own background fades in underneath the card.
 * On the reference the full-bleed state shows the page gradient through a transparent
 * section, while the card state sits on navy -- so the background is part of the morph,
 * not a constant. Painted below the card plane, above nothing else.
 */
.tsf-morph.is-morph-ready::after {
	content: "";
	position: absolute;
	inset: 0;
	background-color: var(--tsf-endbg, transparent);
	opacity: var(--tsf-p);
	z-index: 0;
	pointer-events: none;
}
.tsf-morph.is-morph-ready::before { z-index: 1; }

/* Keep the real content above both layers. */
.tsf-morph.is-morph-ready > * { position: relative; z-index: 2; }

/*
 * Content has to sit inside the card, or on a wide screen the heading ends up out on
 * the bare page while the yellow has already pulled in. Relying on Elementor's "boxed"
 * setting is not enough -- its content width is a separate number that will drift from
 * the morph's max width. Cap the children to the SAME variable instead, so the two can
 * never disagree.
 */
/* The finished card hugs its content vertically. In the full-bleed state the columns
   stretch to the whole section height, so left alone there is nothing to hug -- centring
   them makes their natural height measurable, and looks identical either way because
   the content inside is centred regardless. */
/*
 * Only the WRAPPER hugs. Elementor boxes children inside an unclassed .e-con-inner;
 * centring that makes its height collapse to its content, which is what we want.
 *
 * Do NOT also centre the columns inside it: they are siblings that rely on
 * align-items:stretch to match each other's height. Centring them collapsed the
 * headphone column to its intrinsic 20px, so the canvas rendered 683x20 -- the
 * "tiny wireframe". Left alone, the columns stretch to the tallest (the text), which
 * is exactly how the reference card behaves.
 */
.tsf-morph--hug.is-morph-ready > * {
	/* !important because Elementor's own `.e-con-boxed > .e-con-inner` rule has equal
	   specificity and loads after this file, so it was winning and the wrapper stayed
	   section-tall (nothing hugged, and the card never shrank vertically). */
	align-self: center !important;
	height: auto !important;
	min-height: 0 !important;
}

.tsf-morph--boxed.is-morph-ready > * {
	width: 100%;
	max-width: var(--tsf-maxw);
	margin-inline: auto;
}

/* Pinned: the section holds still while the morph plays. JS adds a spacer after it
   to give the page the scroll room the sticky element consumes.
   Double class + !important because Elementor ships `.e-con { position: relative }`
   and its stylesheet loads after this one. */
.tsf-morph.tsf-morph--pin { position: sticky !important; top: 0; z-index: 0; }
.tsf-morph-spacer { pointer-events: none; }

/* A sticky element is "positioned", so it would paint ABOVE the plain sections that
   follow and cover them. Lift everything after it into its own layer instead. */
.tsf-morph.tsf-morph--pin ~ .e-con,
.tsf-morph.tsf-morph--pin ~ .elementor-section { position: relative; z-index: 1; }

/* No motion: show the finished card, no listeners, no animation. */
@media ( prefers-reduced-motion: reduce ) {
	.tsf-morph { --tsf-p: 1; }
	.tsf-morph.tsf-morph--pin { position: static !important; }
	.tsf-morph-spacer { display: none; }
}
