/* TCB ACF Repeater Cards
 * Base layout only. Colours, spacing, typography and hover shadows are set by
 * the widget's Elementor controls so each client site can differ without a
 * plugin change. Anything hard-coded here is structural.
 */

/* The Row Align control switches the whole layout between two modes by setting
 * --tcb-arc-display and --tcb-arc-basis (see the control's
 * selectors_dictionary). Defaults here reproduce the grid, so a widget saved
 * before the control existed renders exactly as it did.
 *
 *   Stretch (grid) — `columns` is the number of tracks, each 1fr, filling the
 *                    row. flex-wrap/justify-content/flex-basis below are inert:
 *                    a grid item is not a flex item, so it ignores flex-basis.
 *
 *   Center  (flex) — `columns` becomes a DIVISOR: each card is 1/N of the row
 *                    INCLUDING its share of the gap, so N cards can never fit
 *                    and the row holds N-1, centred. That is the point of the
 *                    mode — five step cards at 1/4 width give 3 + 2, both rows
 *                    centred, which no grid track count can express.
 */
.tcb-arc {
	display: var(--tcb-arc-display, grid);
	grid-template-columns: repeat(4, minmax(0, 1fr));
	gap: 24px;
	align-items: stretch;
	flex-wrap: wrap;
	justify-content: center;
}

.tcb-arc__item {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: 12px;
	min-width: 0;
	/* Padding and border must come out of the grid track, not get added to it.
	 * Without this a padded card is 50px wider than its column, the row overflows
	 * the container, and the right-hand border is clipped off the page — which
	 * reads as "the border is missing" rather than "the card is too wide". */
	box-sizing: border-box;
	/* Inert in Stretch (grid items ignore flex-basis); in Center they are what
	 * makes each card a fixed 1/N of the row instead of growing to fill it. */
	flex-grow: 0;
	flex-shrink: 0;
	flex-basis: var(--tcb-arc-basis, auto);
	transition: transform .3s cubic-bezier(.2, .7, .3, 1),
	            box-shadow .3s cubic-bezier(.2, .7, .3, 1),
	            background-color .3s ease;
}

/* --- Stat -------------------------------------------------------------- */

.tcb-arc--stat .tcb-arc__item {
	justify-content: center;
	gap: 8px;
}

.tcb-arc__num {
	line-height: 1;
	white-space: nowrap;
}

.tcb-arc__lbl {
	line-height: 1.35;
}

/* --- Icon card --------------------------------------------------------- */

.tcb-arc__chip {
	display: flex;
	align-items: center;
	justify-content: center;
	flex: none;
	width: 64px;
	height: 64px;
	border-radius: 50%;
	transition: background-color .3s ease, color .3s ease;
}

.tcb-arc__chip img {
	width: 30px;
	height: 30px;
	object-fit: contain;
}

/* --- Step card --------------------------------------------------------- */

/* The image runs edge to edge, so the card itself carries no padding and no
 * gap — the text block below the number circle supplies both. overflow:hidden
 * is what clips the image to the card's border radius. */
.tcb-arc--step-card .tcb-arc__item {
	padding: 0;
	gap: 0;
	overflow: hidden;
	justify-content: flex-start;
}

.tcb-arc__media {
	width: 100%;
	height: 170px;
	flex: none;
	overflow: hidden;
	background: #E9F1F5;
}

.tcb-arc__media img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform .5s cubic-bezier(.2, .7, .3, 1);
}

/* Sits astride the bottom edge of the image. The negative top margin is half
 * the circle size and is emitted by the Circle Size control, so the two can
 * never drift apart. */
.tcb-arc__step {
	display: flex;
	align-items: center;
	justify-content: center;
	flex: none;
	position: relative;
	z-index: 1;
	width: 52px;
	height: 52px;
	margin-top: -26px;
	border-radius: 50%;
	border-style: solid;
	border-width: 3px;
	border-color: #FFFFFF;
	box-sizing: border-box;
}

.tcb-arc__body {
	display: flex;
	flex-direction: column;
	gap: 10px;
	width: 100%;
	padding: 16px 22px 26px;
	box-sizing: border-box;
}

/* A step card whose repeater has no image sub-field. Nothing for the number to
 * straddle, so it sits inline at the top of the card and the card itself takes
 * the padding (via the Padding control, which step-card can now reach).
 * Alignment stays with the Alignment control; only the gap is restored here,
 * because step-card zeroes it for the edge-to-edge image it no longer has.
 * Keyed off the missing block rather than a modifier class so an older saved
 * widget picks it up without being re-saved. */
.tcb-arc--step-card .tcb-arc__item:not(:has(> .tcb-arc__media)) {
	gap: 12px;
}

.tcb-arc--step-card .tcb-arc__item:not(:has(> .tcb-arc__media)) .tcb-arc__step {
	margin-top: 0;
}

.tcb-arc--step-card .tcb-arc__item:not(:has(> .tcb-arc__media)) .tcb-arc__body {
	padding: 0;
}

/* --- Timeline ---------------------------------------------------------- */

/* A vertical run of numbered dots with the copy to their right. Overrides the
 * grid the other layouts share, so it must come after them. Every custom
 * property below is emitted by a control; the fallbacks only matter for a
 * marquee saved before those controls existed. */
.tcb-arc--timeline {
	display: flex;
	flex-direction: column;
	row-gap: var(--tcb-tl-gap, 40px);
	max-width: 640px;
	margin-inline: auto;
}

.tcb-arc--timeline .tcb-arc__item {
	position: relative;
	flex-direction: row;
	align-items: flex-start;
	text-align: left;
	column-gap: 24px;
	padding: 0;
	transition: none;
}

/* The connector, drawn from the bottom of this dot to the top of the next.
 * bottom is the negative row gap, so the line crosses the gap and lands exactly
 * on the following dot however tall either row turns out to be. */
.tcb-arc--timeline .tcb-arc__item:not(:last-child)::before {
	content: "";
	position: absolute;
	top: var(--tcb-tl-dot, 50px);
	bottom: calc(var(--tcb-tl-gap, 40px) * -1);
	left: calc(var(--tcb-tl-dot, 50px) / 2);
	width: var(--tcb-tl-line-w, 1px);
	transform: translateX(-50%);
	background: var(--tcb-tl-line, #E9E9E9);
}

.tcb-arc--timeline .tcb-arc__step {
	flex: none;
	width: var(--tcb-tl-dot, 50px);
	height: var(--tcb-tl-dot, 50px);
	margin-top: 0;
	border-width: 1px;
	line-height: 1;
}

.tcb-arc--timeline .tcb-arc__body {
	width: auto;
	padding: 0;
	/* Nudges the copy onto the dot's optical centre line rather than its top. */
	padding-top: 2px;
}

/* --- Shared text ------------------------------------------------------- */

.tcb-arc__title {
	margin: 0;
}

.tcb-arc__desc {
	margin: 0;
	text-wrap: pretty;
}

@media (prefers-reduced-motion: reduce) {
	.tcb-arc__item,
	.tcb-arc__chip,
	.tcb-arc__media img {
		transition: none;
	}

	.tcb-arc__item:hover .tcb-arc__media img {
		transform: none;
	}
}

/* --- Label card ---------------------------------------------------------- */

/* Number / word, title, then the text pinned to the bottom behind a hairline —
 * so a row of cards keeps its rules on one line however long the titles run. */
.tcb-arc--label-card .tcb-arc__item {
	justify-content: flex-start;
	overflow: hidden;
}

.tcb-arc__meta {
	display: flex;
	align-items: center;
	gap: 8px;
}

.tcb-arc--label-card .tcb-arc__desc {
	margin-top: auto;
	width: 100%;
	border-top-style: solid;
	border-top-width: 1px;
	border-top-color: rgba(0, 0, 0, .08);
}

/* --- Stat card ----------------------------------------------------------- */

/* Figure, label, then the elaboration behind a hairline. Left-aligned by
 * default because the text is prose, not a caption. */
.tcb-arc--stat-card .tcb-arc__item {
	justify-content: flex-start;
	gap: 6px;
}

.tcb-arc--stat-card .tcb-arc__desc {
	width: 100%;
	margin-top: 10px;
	padding-top: 14px;
	border-top-style: solid;
	border-top-width: 1px;
	border-top-color: rgba(0, 0, 0, .08);
}

/* --- Stat card: direction pill ------------------------------------------- */

/* The figure and its arrow share a baseline row so the pill reads as part of the
 * number rather than a badge floating beside the card. */
.tcb-arc__figure {
	display: flex;
	align-items: center;
	gap: 10px;
	flex-wrap: wrap;
	width: 100%;
}

.tcb-arc__trend {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: none;
	width: 28px;
	height: 28px;
	border-radius: 100px;
	line-height: 1;
	/* One control sets the colour; the tint is mixed from it here, so the pill
	 * background can never drift out of step with its arrow. */
	background: color-mix(in srgb, currentColor 12%, transparent);
}

/* Visually hidden, still read aloud — the arrow alone does not say whether the
 * movement is the good news the colour is claiming. */
.tcb-arc__sr {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* --- Gallery ------------------------------------------------------------- */

/* Screenshots of the work, masonry over the grid.
 *
 * The grid comes from .tcb-arc, so the Columns control drives the track count
 * like every other layout and the items stay in source order — reading left to
 * right. tcb-acf-repeater-cards.js then pulls each item up under its neighbour
 * in the same column, which is what closes the ragged gaps.
 *
 * Not CSS columns: those pour to a target height rather than placing one item
 * per column, so a tall item leaves a column empty while another takes two.
 */
/* The fallback if the script never runs: ordinary grid cells that still honour
 * the span. Rows end up as tall as their tallest item — not masonry, but nothing
 * overlaps and nothing is hidden. The script overrides all of this. */
.tcb-arc--gallery .tcb-arc__item[data-span="2"] { grid-column: span 2; }
.tcb-arc--gallery .tcb-arc__item[data-span="3"] { grid-column: span 3; }

.tcb-arc--gallery .tcb-arc__item {
	/* Each item is only as tall as its content, so the script has real heights to
	 * measure rather than stretched grid cells. */
	align-self: start;
	text-align: left;
	transition: none;
	/* The shared card gap has to go, or it stacks on top of the Caption Distance
	 * control and that control can never reach the value it is set to: at 0 the
	 * caption still sat 12px off the image. In this layout the caption and the
	 * shot are the only two children, so their spacing belongs entirely to the
	 * control. */
	gap: 0;
}

.tcb-arc__shot {
	margin: 0;
	overflow: hidden;
	line-height: 0;
}

.tcb-arc__shot img {
	display: block;
	width: 100%;
	height: auto;
}

/* Spacing follows the caption wherever it sits, so switching the control does
 * not leave the gap on the wrong side. */
.tcb-arc__caption {
	display: block;
	margin: 0;
	text-wrap: pretty;
	/* Browsers italicise figcaption by default in some stylesheets, and several
	 * themes do it deliberately. The caption here is a plain label, and the
	 * Caption Typography control should be the only thing deciding its style. */
	font-style: normal;
}

.tcb-arc__shot + .tcb-arc__caption { margin-top: 10px; }
.tcb-arc__caption + .tcb-arc__shot { margin-top: 10px; }

/* A border belongs to each image, not to the run. Without the radius clipping
 * the picture, a bordered card shows the image's square corners poking through
 * the rounded ones. */
.tcb-arc--gallery .tcb-arc__shot {
	border-radius: inherit;
}

/* --- Gallery: clamped shots, Expand, and the lightbox ---------------------
 *
 * REBUILT. These rules previously existed only in the Local copy of this file
 * and were never committed; a sync from the repo overwrote them. Reconstructed
 * against the markup in acf-repeater-cards.php and the behaviour in
 * tcb-acf-repeater-cards.js, both of which survived.
 *
 * A page-length screenshot at natural height is most of a scroll on its own,
 * and everything below it in that masonry column falls off the fold. So a shot
 * is cut at Maximum Image Height, given a fade so the cut reads as "continues"
 * rather than "exported badly", and an Expand button that opens it full size.
 *
 * The script decides which shots are cut by comparing rendered heights, adds
 * .is-clamped to those, and unhides their button. Shots that fit keep the
 * button's `hidden` attribute and show nothing.
 */

.tcb-arc--gallery .tcb-arc__shot {
	position: relative;
	max-height: var(--tcb-arc-shot-max, 400px);
}

/* 0 in the control means "leave them alone", which max-height: 0 would read as
 * "hide everything". */
.tcb-arc--gallery .tcb-arc__shot[style*="--tcb-arc-shot-max: 0"],
.tcb-arc--gallery[style*="--tcb-arc-shot-max: 0px"] .tcb-arc__shot {
	max-height: none;
}

/* Only a cut shot gets the fade and the pointer. */
.tcb-arc--gallery .tcb-arc__shot.is-clamped {
	cursor: zoom-in;
}

.tcb-arc--gallery .tcb-arc__shot.is-clamped::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	bottom: 0;
	height: var(--tcb-arc-fade-h, 96px);
	pointer-events: none;
	background: linear-gradient(
		to bottom,
		rgba(255, 255, 255, 0) 0%,
		var(--tcb-arc-fade, #FFFFFF) 88%
	);
}

/* Sits on the fade, centred. Colour, background and type all come from
 * controls; only the placement is structural. */
.tcb-arc__expand {
	position: absolute;
	left: 50%;
	bottom: 14px;
	transform: translateX(-50%);
	z-index: 2;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 7px 16px;
	border: 0;
	border-radius: 100px;
	background-color: #121212;
	color: #FFFFFF;
	font: inherit;
	line-height: 1.2;
	cursor: pointer;
	white-space: nowrap;
}

/* The attribute is how the script hides a button on a shot that fits, so the
 * display above must not win against it. */
.tcb-arc__expand[hidden] {
	display: none;
}

.tcb-arc__expand:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 2px;
}

/* --- Lightbox ------------------------------------------------------------
 * Built in JS on open and removed on close, so there is no hidden markup in
 * the page. The image is shown at full width inside a scroller rather than
 * shrunk to fit — the whole point is to read the thing that was too tall.
 */
.tcb-arc-lightbox {
	position: fixed;
	inset: 0;
	z-index: 99999;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 14px;
	padding: 32px 20px;
	box-sizing: border-box;
	background: rgba(18, 18, 18, .82);
	overscroll-behavior: contain;
}

.tcb-arc-lightbox__scroll {
	max-width: min(1100px, 100%);
	max-height: 100%;
	overflow: auto;
	border-radius: 8px;
	background-color: #FFF;
	-webkit-overflow-scrolling: touch;
}

.tcb-arc-lightbox__scroll img {
	display: block;
	width: 100%;
	height: auto;
}

.tcb-arc-lightbox__close {
	position: absolute;
	top: 16px;
	right: 20px;
	width: 40px;
	height: 40px;
	display: flex;
	align-items: center;
	justify-content: center;
	border: 0;
	border-radius: 50%;
	background: rgba(255, 255, 255, .14);
	color: #FFF;
	font-size: 26px;
	line-height: 1;
	cursor: pointer;
}

.tcb-arc-lightbox__close:hover {
	background: rgba(255, 255, 255, .26);
}

.tcb-arc-lightbox__caption {
	max-width: min(1100px, 100%);
	margin: 0;
	color: #FFF;
	font-size: 14px;
	line-height: 1.5;
	text-align: center;
	text-wrap: pretty;
}

@media (prefers-reduced-motion: reduce) {
	.tcb-arc__expand {
		transition: none;
	}
}

/* A gallery card with no image is a prose card: the challenges on a case study
 * are the same shape as the work screenshots minus the picture, and giving them
 * their own layout would mean fixing everything twice. Spacing comes off the
 * caption gap control so both kinds of card stay in step. */
.tcb-arc--gallery .tcb-arc__title {
	margin: 0;
}

.tcb-arc--gallery .tcb-arc__title + .tcb-arc__caption,
.tcb-arc--gallery .tcb-arc__title + .tcb-arc__shot {
	margin-top: 8px;
}

/* The gallery pill names the kind of card — Challenge, Highlight.
 *
 * inline-flex and align-self are what make it a pill rather than a full-width
 * strip: .tcb-arc__meta is a flex child of the card, so left to itself it
 * stretches the whole width and a background colour paints the entire row.
 * Everything else — fill, padding, radius, border, spacing, alignment — is a
 * control. */
.tcb-arc--gallery .tcb-arc__meta {
	display: inline-flex;
	align-self: flex-start;
	width: auto;
	max-width: 100%;
	margin-bottom: 10px;
}
