/* TSF — Circle Grid */

.tsf-cg {
	--tsf-cg-cols: 4;
	--tsf-cg-gap: 16px;
	--tsf-cg-gap-row: var( --tsf-cg-gap );
	--tsf-cg-speed: .4s;

	display: grid;
	grid-template-columns: repeat( var( --tsf-cg-cols ), 1fr );
	column-gap: var( --tsf-cg-gap );
	row-gap: var( --tsf-cg-gap-row );
	width: 100%;
}

/*
 * Same idea as the fixed-column mode, from the other end: columns are at least
 * --tsf-cg-min wide, and 1fr shares out the remainder so they still reach both edges.
 * auto-fit rather than auto-fill, or a half-empty row would keep phantom tracks and the
 * circles would come out too small.
 */
.tsf-cg--auto {
	grid-template-columns: repeat( auto-fit, minmax( var( --tsf-cg-min, 160px ), 1fr ) );
}

/*
 * Dense packing so a large cell does not leave a hole behind it: later small cells
 * backfill the gaps instead of the grid stepping past them.
 */
.tsf-cg--dense {
	grid-auto-flow: row dense;
}

.tsf-cg__cell {
	position: relative;
	aspect-ratio: 1 / 1;
	border-radius: 50%;
	overflow: hidden;
}

/*
 * A cell spanning 2x2 lands exactly on a circle of twice the diameter plus one gap,
 * because the rows are sized by the 1:1 small cells. No maths needed, and it stays
 * correct at every breakpoint.
 */
.tsf-cg__cell--big {
	grid-column: span 2;
	grid-row: span 2;
}

/*
 * The iris is a clip-path on the media, so everything is stacked and the one whose turn it
 * is opens. Not an opacity crossfade: two half-transparent photos over each other go milky
 * in the overlap, where a clip keeps both fully opaque.
 *
 * circle(75%) is "fully open" for a square box — larger than the 70.7% half-diagonal — so
 * the visible edge is the cell's border-radius, never the clip.
 *
 * Two class names in the selector on purpose. Elementor ships `.elementor img { height:
 * auto }`, which is specificity (0,1,1) and therefore BEATS a single class: the images kept
 * their intrinsic ratio, so a 212px cell held a 212x141 photo and the circle was never
 * covered. Raising this to (0,2,0) wins on specificity rather than on load order.
 */
.tsf-cg__cell > .tsf-cg__img,
.tsf-cg__cell > .tsf-cg__video {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
	display: block;
	max-width: none;   /* the same reset rules cap images at 100% of a different box */
}

.tsf-cg__cell > .tsf-cg__img {
	clip-path: circle( 0% at 50% 50% );   /* shut until the animation opens it */
	animation-iteration-count: infinite;
	animation-fill-mode: both;
	transition: transform var( --tsf-cg-speed ) ease, filter var( --tsf-cg-speed ) ease;
}

.tsf-cg__cell > .tsf-cg__video {
	transition: transform var( --tsf-cg-speed ) ease, filter var( --tsf-cg-speed ) ease;
}

/* A single image has nothing to take turns with. */
.tsf-cg__cell > .tsf-cg__img.is-static {
	clip-path: circle( 75% at 50% 50% );
	animation: none;
}

/* Full-cell click target, above the media but below nothing else. */
.tsf-cg__link {
	position: absolute;
	inset: 0;
	z-index: 2;
	border-radius: inherit;
}

.tsf-cg--pause:hover .tsf-cg__img {
	animation-play-state: paused;
}

@media ( prefers-reduced-motion: reduce ) {
	/* Hold the first image open and drop the rest rather than cycling. */
	.tsf-cg__cell > .tsf-cg__img {
		animation: none !important;
		clip-path: circle( 0% at 50% 50% );
	}
	.tsf-cg__cell > .tsf-cg__img:first-of-type,
	.tsf-cg__cell > .tsf-cg__img.is-static {
		clip-path: circle( 75% at 50% 50% );
	}
}
