/*
 * Event album.
 *
 * The mosaic spans come from each photograph's real orientation, never from its
 * position in the grid. Rows are a fixed module so landscape and portrait tiles
 * interlock instead of drifting.
 */

/*
 * The row is a square module: exactly one column wide. A landscape tile (two columns by one
 * row) then reads about 2:1 and a portrait tile (one by two) about 1:2, each cropping a
 * quarter of a 3:2 photograph, and the two still interlock without holes.
 *
 * The row used to be its own clamp, unrelated to the column. Measured at 1440 with a 3:2
 * photograph: columns of 223px, rows of 128px, so a landscape tile rendered 457x128 — 3.6:1,
 * more than half of every photograph cut away. The column width is only known to the grid,
 * so the module is derived from the container's inline size.
 */
.ti-album {
	--ti-album-columns: 1;
	--ti-album-gap: 0.5rem;
	--ti-album-row: auto;

	container-type: inline-size;
	display: flex;
	flex-direction: column;
	gap: clamp( 1.25rem, 2.5vw, 2rem );
}

@media ( min-width: 48rem ) {
	.ti-album {
		--ti-album-columns: 4;
		--ti-album-gap: 0.65rem;
		--ti-album-row: calc( ( 100cqi - ( var( --ti-album-columns ) - 1 ) * var( --ti-album-gap ) ) / var( --ti-album-columns ) );
	}
}

@media ( min-width: 75rem ) {
	.ti-album {
		--ti-album-columns: 6;
	}
}
/* A short album is one row of equal cells — see `render.php`. */
@media ( min-width: 48rem ) {
	.ti-album--row {
		--ti-album-row: auto;
	}
	.ti-album--row-2 { --ti-album-columns: 2; }
	.ti-album--row-3 { --ti-album-columns: 3; }
	.ti-album--row-4 { --ti-album-columns: 4; }
	.ti-album--row-5 { --ti-album-columns: 5; }
	.ti-album--row .ti-album__tile {
		grid-column: auto;
		grid-row: auto;
		aspect-ratio: 4 / 5;
	}
}

.ti-album__grid {
	display: grid;
	grid-template-columns: repeat( var( --ti-album-columns ), minmax( 0, 1fr ) );
	grid-auto-rows: var( --ti-album-row );
	grid-auto-flow: dense;
	gap: var( --ti-album-gap );
	margin: 0;
	padding: 0;
	list-style: none;
}

.ti-album__tile {
	margin: 0;
	padding: 0;
	overflow: hidden;
	background: var( --ti-well );
}

/*
 * Below the first breakpoint the grid is a single column, so the mosaic spans
 * are dropped and each photograph keeps its own proportion.
 *
 * At two columns the spans produced visible holes: a landscape tile needs both
 * columns, so it cannot sit beside a portrait tile and dense packing leaves the
 * remaining cell empty. A one-up column reads as a roll of photographs and has
 * no holes by construction.
 */
.ti-album__tile--landscape {
	aspect-ratio: 3 / 2;
}

.ti-album__tile--portrait {
	aspect-ratio: 2 / 3;
}

.ti-album__tile--square {
	aspect-ratio: 1;
}

@media ( min-width: 48rem ) {
	.ti-album__tile--landscape,
	.ti-album__tile--portrait,
	.ti-album__tile--square {
		aspect-ratio: auto;
	}

	.ti-album__tile--landscape {
		grid-column: span 2;
	}

	.ti-album__tile--portrait {
		grid-row: span 2;
	}
}

.ti-album__link {
	display: block;
	width: 100%;
	height: 100%;
}

.ti-album__image {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: opacity 180ms ease;
}

.ti-album__link:hover .ti-album__image,
.ti-album__link:focus-visible .ti-album__image {
	opacity: 0.86;
}

/*
 * The pager is a centred column under the photographs: how far through the album the guest
 * is, as words and as a rule, and one button for the next page.
 *
 * It used to be a rule across the full width with the counter at the left end and "load more"
 * as a small uppercase link at the right end, 1,300px apart at 1440. The owner found the
 * action hidden, and it was: after the last row the eye returns to the left edge, where it
 * met a count, and the only thing a guest came to do was a footnote at the other margin.
 */
.ti-album__nav {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1rem;
	padding-block: clamp( 1.5rem, 4vw, 2.5rem ) 0.5rem;
}

.ti-album__more {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.75rem;
	width: min( 100%, 20rem );
}

/*
 * Progress as a hairline: 24 of 526 is a sliver, and a sliver is the honest picture of how
 * much of the night is still below. Scale and not width, so the bar grows on the compositor.
 */
.ti-album__progress {
	display: block;
	width: 100%;
	height: 2px;
	overflow: hidden;
	background: var( --ti-line );
}

.ti-album__progress-bar {
	display: block;
	width: 100%;
	height: 100%;
	background: var( --ti-strong );
	transform: scaleX( var( --ti-album-progress, 0 ) );
	transform-origin: left center;
	transition: transform 420ms cubic-bezier( 0.16, 1, 0.3, 1 );
}

.ti-album__more-button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.6rem;
	min-width: min( 100%, 16rem );
	min-height: 48px;
	margin-block-start: 0.25rem;
	padding: 0.85rem 1.75rem;
	color: var( --ti-strong );
	background: transparent;
	border: 1px solid var( --ti-strong );
	font-size: var( --ti-label-size, 0.8125rem );
	font-weight: 600;
	letter-spacing: 0.12em;
	text-decoration: none;
	text-transform: uppercase;
	transition:
		background-color 180ms ease,
		color 180ms ease;
}

.ti-album__more-button:hover {
	color: var( --ti-field );
	background: var( --ti-strong );
}

.ti-album__more-button:focus-visible {
	outline: 2px solid var( --ti-accent );
	outline-offset: 4px;
}

/* Loading: the label says so and a ring turns beside it. Reduced motion keeps only the words. */
.ti-album__more-button[aria-busy="true"] {
	cursor: progress;
}

.ti-album__more-button[aria-busy="true"]::before {
	content: "";
	width: 0.9rem;
	height: 0.9rem;
	border: 1.5px solid currentColor;
	border-inline-end-color: transparent;
	border-radius: 50%;
	animation: ti-album-spin 700ms linear infinite;
}

@keyframes ti-album-spin {
	to {
		transform: rotate( 1turn );
	}
}

.ti-album__nav-link {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	color: inherit;
	font-size: var( --ti-label-size, 0.8125rem );
	font-weight: 600;
	letter-spacing: 0.12em;
	text-decoration: none;
	text-transform: uppercase;
	border-bottom: 1px solid var( --ti-line );
}

.ti-album__nav-link:hover {
	border-bottom-color: var( --ti-accent );
}

.ti-album__counter {
	margin: 0;
	color: var( --ti-muted );
	font-size: var( --ti-label-size, 0.8125rem );
	font-weight: 600;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	font-variant-numeric: tabular-nums;
}

.ti-album--pending {
	margin: 0;
	color: var( --ti-muted );
}

.ti-album__notice {
	margin-block: 1rem 0;
	color: var( --ti-muted );
}

.ti-album__notice[hidden] {
	display: none;
}

/* Photo viewer ---------------------------------------------------------- */

/*
 * The photograph floats over the darkened page. Nothing behind it is a panel.
 *
 * It used to be `background: var( --ti-field )` on a dialog sized `100vh` by
 * `min( 100vw, 96rem )`, which means that below 96rem — 1440px included, measured — a cream
 * sheet covered the whole viewport and the backdrop was never visible. The veil was declared
 * and correct all along; there was simply an opaque panel in front of it.
 *
 * Transparent dialog, veil from `::backdrop`, and a literal fallback at the point of use so
 * the viewer still darkens the page in a theme that declares no tokens.
 */
.ti-album__lightbox {
	width: 100vw;
	max-width: 100vw;
	height: 100vh;
	max-height: 100vh;
	margin: 0;
	padding: 0;
	border: 0;
	background: transparent;
	color: var( --ti-on-image );
}

.ti-album__lightbox::backdrop {
	background: var( --ti-veil );
}

/*
 * The dark field around the photograph is a dismissal, so it says so under the pointer.
 * Without this the behaviour exists and nothing announces it: a guest only discovers that the
 * field closes the viewer by clicking it, which is a guess they make only if they already
 * expected it.
 */
.ti-album__lightbox {
	cursor: zoom-out;
}

.ti-album__viewer-image,
.ti-album__viewer-caption,
.ti-album__viewer-controls {
	cursor: auto;
}

.ti-album__viewer {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 0.75rem;
	width: 100%;
	height: 100%;
	margin: 0;
	padding: clamp( 1rem, 4vw, 2.5rem );
}

.ti-album__viewer-image {
	max-width: 100%;
	max-height: calc( 100vh - 8rem );
	width: auto;
	height: auto;
	object-fit: contain;
}

/*
 * The viewer is a dark surface wherever it opens, so it names its own colours instead of
 * reading the page's.
 *
 * The page tokens are the wrong ones here and it was measured: `--ti-muted` on the light
 * field is a dark grey, which on the veil is a caption nobody can read, and `--ti-line` is a
 * pale rule that all but disappears as a button border. These are on-image values, written
 * once on the dialog so every part inside inherits one decision.
 *
 * The two weaker values are mixed down from `--ti-on-image` rather than written as literal
 * whites. They were `rgba( 255, 255, 255, 0.72 )` and `rgba( 255, 255, 255, 0.3 )`, which is the
 * palette restated inside a plugin: correct today, and silently wrong the moment the contract
 * decides that what sits on an image is not pure white. Mixing keeps the one decision in one
 * place and the percentages here describe strength, not colour.
 *
 * `--ti-viewer-*` are the viewer's own names and deliberately not contract tokens: a dialog that
 * covers the viewport is its own surface, and nothing outside it has an opinion about them.
 */
.ti-album__lightbox {
	--ti-viewer-ink: var( --ti-on-image );
	--ti-viewer-quiet: color-mix( in srgb, var( --ti-on-image ) 72%, transparent );
	--ti-viewer-rule: color-mix( in srgb, var( --ti-on-image ) 30%, transparent );
}

.ti-album__viewer-caption {
	max-width: 70ch;
	margin: 0;
	color: var( --ti-viewer-quiet );
	font-size: 0.85rem;
	text-align: center;
}

.ti-album__viewer-caption[hidden] {
	display: none;
}

.ti-album__viewer-controls {
	position: absolute;
	inset: auto 0 0;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 0.5rem;
	padding: 1rem;
}

.ti-album__viewer-button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.5rem;
	min-width: 44px;
	min-height: 44px;
	padding: 0;
	color: var( --ti-viewer-ink );
	background: transparent;
	border: 1px solid var( --ti-viewer-rule );
	cursor: pointer;
	text-decoration: none;
}

.ti-album__viewer-button svg {
	width: 1rem;
	height: 1rem;
	flex: none;
}

.ti-album__viewer-button:hover,
.ti-album__viewer-button:focus-visible {
	border-color: var( --ti-accent );
}

.ti-album__viewer-button:focus-visible {
	outline: 2px solid var( --ti-accent );
	outline-offset: 3px;
}

/*
 * The download is the one control that says what it does in words. It is the reason a guest
 * came to the album, and an unlabelled arrow icon next to two other unlabelled icons is a
 * guess. The arrows and the close can stay iconic: those gestures are already understood.
 */
.ti-album__viewer-button--download {
	width: auto;
	padding-inline: 1rem;
}

.ti-album__viewer-buttonLabel {
	font-size: var( --ti-label-size, 0.8125rem );
	font-weight: 600;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	white-space: nowrap;
}

.ti-album__viewer-button--close {
	position: absolute;
	inset: 1rem 1rem auto auto;
}

/* The focus target the viewer opens on; the controls keep their own ring. */
.ti-album__viewer:focus {
	outline: none;
}

.ti-album__viewer-position {
	min-width: 5.5rem;
	margin: 0;
	color: var( --ti-viewer-quiet );
	font-size: var( --ti-label-size, 0.8125rem );
	font-weight: 600;
	font-variant-numeric: tabular-nums;
	letter-spacing: 0.08em;
	text-align: center;
}

.ti-album__viewer-position:empty {
	display: none;
}

@media ( prefers-reduced-motion: reduce ) {
	.ti-album__image,
	.ti-album__progress-bar {
		transition-duration: 0.01ms;
	}

	.ti-album__more-button[aria-busy="true"]::before {
		animation: none;
		border-inline-end-color: currentColor;
		opacity: 0.5;
	}
}
