/**
 * Upgrade Agenda List
 *
 * Layout and hover state. Every tunable value is a custom property written by
 * an Elementor style control, so the panel drives the layout without this file
 * needing per-widget rules.
 */

.ual-agenda {
	--ual-row-gap: 24px;
	--ual-media-w: 96px;
	--ual-media-maxw: 100%;
	--ual-media-justify: center;
	--ual-media-h: 130px;
	--ual-media-h-rest: 0px;
	--ual-media-gap: 16px;
	--ual-date-w: 150px;
	--ual-day-w: 1.5em;
	--ual-day-align: center;
	--ual-transition: 0.35s;
	--ual-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
	--ual-date-min-h: 0px;
	--ual-sep-w: 1px;
	--ual-sep-style: solid;
	--ual-zoom-size: 48px;
	--ual-zoom-icon-size: 20px;
	--ual-zoom-radius: 50%;
	--ual-zoom-blur: 8px;
	--ual-zoom-color: #ffffff;
	--ual-zoom-bg: rgba(255, 255, 255, 0.18);
	--ual-lb-scrim: rgba(0, 0, 0, 0.55);
	--ual-lb-caption: rgba(255, 255, 255, 0.86);
	--ual-lb-radius: 4px;
}

.ual-agenda__heading {
	margin: 0 0 40px;
}

.ual-agenda__empty {
	margin: 0;
	opacity: 0.7;
}

/* Three columns: lead (image + date), text, button. The image lives inside the
   lead rather than in a column of its own, so growing it on hover rearranges
   that one fixed-width column instead of pushing the title and the button
   across the row.

   The column spacing is a margin on each column rather than the grid's own
   gap, and it sits inside its track — so the lead track carries the gap on top
   of the width asked for in the panel, and the lead comes out exactly as wide
   as its control says. */
.ual-row {
	display: grid;
	grid-template-columns: calc(var(--ual-date-w) + var(--ual-row-gap)) 1fr auto;
	align-items: center;
	border: 0 var(--ual-sep-style) currentColor;
	transition: background-color var(--ual-transition) var(--ual-ease);
}

.ual-row__lead,
.ual-row__text {
	margin-inline-end: var(--ual-row-gap);
}

.ual-row__lead {
	display: flex;
	align-items: center;
	min-width: 0;
}

/* Separator variants. "between" leaves the first row's top edge alone; "all"
   closes the list off at both ends the way the reference layout does. */
.ual-agenda--sep-between .ual-row + .ual-row,
.ual-agenda--sep-all .ual-row,
.ual-agenda--sep-top .ual-row {
	border-top-width: var(--ual-sep-w);
}

.ual-agenda--sep-all .ual-row:last-child,
.ual-agenda--sep-bottom .ual-row {
	border-bottom-width: var(--ual-sep-w);
}

.ual-agenda--sep-none .ual-row {
	border-width: 0;
}

/* Media */

/* Collapsed in both axes at rest, so a row is only as tall as its own text and
   the image is what makes it grow — the same way the reference layout behaves.
   A row that should stay tall regardless gets a minimum height from the
   panel. */
.ual-row__media {
	flex: 0 0 auto;
	display: flex;
	justify-content: var(--ual-media-justify);
	width: 0;
	height: var(--ual-media-h-rest);
	overflow: hidden;
	opacity: 0;
	transition:
		width var(--ual-transition) var(--ual-ease),
		height var(--ual-transition) var(--ual-ease),
		margin var(--ual-transition) var(--ual-ease),
		opacity var(--ual-transition) var(--ual-ease);
}

/* --open is the tap's equivalent of the hover: see the touch branch further
   down, and the pointer handling in the script that sets it. */
.ual-row:hover .ual-row__media,
.ual-row--open .ual-row__media {
	width: var(--ual-media-w);
	height: var(--ual-media-h);
	margin-inline-end: var(--ual-media-gap);
	opacity: 1;
}

/* The keyboard's equivalent of the hover, and deliberately not :focus-within.
   Closing the lightbox hands focus back to the magnifier it was opened from,
   which is inside the row: under :focus-within that click alone would hold the
   row open until the visitor clicked somewhere else. :focus-visible matches
   only where the browser is drawing a focus ring, which after a mouse click it
   is not — so the row closes with the overlay, and someone tabbing to the same
   link still gets the photo.

   In its own block, not merged with the pair above: a browser without :has()
   throws out the whole selector list, and the hover would go with it. */
.ual-row:has( :focus-visible ) .ual-row__media {
	width: var(--ual-media-w);
	height: var(--ual-media-h);
	margin-inline-end: var(--ual-media-gap);
	opacity: 1;
}

/* Sized against its own box rather than the panel's width value: the box
   already carries that width, and a percentage set here would resolve against
   the box and compound with it — half of a half-width column being a quarter of
   it. The maximum width then caps the image inside the box, which is what gives
   the alignment above something to move. */
.ual-row__media img {
	display: block;
	width: 100%;
	max-width: var(--ual-media-maxw);
	height: 100%;
	object-fit: cover;
}

/* Magnifier. The link fills the media box so the whole image is the target,
   and the chip centres on it. */
.ual-row__zoom {
	position: relative;
	display: block;
	/* The theme styles links; this one is a picture, not a piece of prose. */
	text-decoration: none;
	border: 0;
	background: none;
	width: 100%;
	max-width: var(--ual-media-maxw);
	height: 100%;
	cursor: zoom-in;
	touch-action: manipulation;
}

.ual-row__zoom-chip {
	position: absolute;
	top: 50%;
	left: 50%;
	display: flex;
	align-items: center;
	justify-content: center;
	width: var(--ual-zoom-size);
	height: var(--ual-zoom-size);
	/* A font icon sizes itself from font-size and an SVG from its own box, so
	   one control feeds both and the two libraries land the same size. */
	font-size: var(--ual-zoom-icon-size);
	line-height: 1;
	color: var(--ual-zoom-color);
	background: var(--ual-zoom-bg);
	border: 1px solid rgba(255, 255, 255, 0.35);
	border-radius: var(--ual-zoom-radius);
	/* Only transform and opacity animate, so the reveal never reflows the row
	   the image is busy resizing. */
	opacity: 0;
	transform: translate(-50%, -50%) scale(0.92);
	transition:
		opacity 0.18s var(--ual-ease) 0.06s,
		transform 0.18s var(--ual-ease) 0.06s,
		background-color 0.18s var(--ual-ease);
	pointer-events: none;
}

/* backdrop-filter is a progressive enhancement: without it the chip is still
   a legible translucent disc, so nothing is gated on support for it. */
@supports (backdrop-filter: blur(8px)) or (-webkit-backdrop-filter: blur(8px)) {
	.ual-row__zoom-chip {
		-webkit-backdrop-filter: blur(var(--ual-zoom-blur));
		backdrop-filter: blur(var(--ual-zoom-blur));
	}
}

/* Covers all three shapes an icon arrives in: the built-in magnifier, an
   uploaded SVG, and a library icon on an Elementor build that inlines its icon
   fonts as SVG.

   Those last two are drawn with fill, and an SVG with no fill of its own is
   black — the icon colour control sets `color`, which they never read. A
   presentation attribute loses to a stylesheet, so this reaches an uploaded
   file's own `fill="#000"` as well; anything painted onto a child element
   inside it keeps whatever colour it was given, exactly as it does in
   Elementor's own widgets. */
.ual-row__zoom-chip svg {
	width: var(--ual-zoom-icon-size);
	height: var(--ual-zoom-icon-size);
	fill: currentColor;
}

/* And the rule above would fill in the built-in magnifier, which is drawn in
   outline. Its own attributes say as much and are overruled by that selector,
   so they are said again here where they win. */
.ual-row__zoom-chip .ual-zoom-default {
	fill: none;
	stroke: currentColor;
}

.ual-row:hover .ual-row__zoom-chip,
.ual-row--open .ual-row__zoom-chip {
	opacity: 1;
	transform: translate(-50%, -50%) scale(1);
}

.ual-row:has( :focus-visible ) .ual-row__zoom-chip {
	opacity: 1;
	transform: translate(-50%, -50%) scale(1);
}

.ual-row__zoom:hover .ual-row__zoom-chip {
	background: rgba(255, 255, 255, 0.28);
	transform: translate(-50%, -50%) scale(1.06);
}

.ual-row__zoom:focus-visible {
	outline: 2px solid var(--ual-zoom-color);
	outline-offset: 2px;
}

/* Touch has no hover to reveal anything, so the image and its magnifier are
   simply always there. Without this the feature is unreachable on a phone.

   `any-hover`, not `hover`: `hover` describes the device's primary pointer, so
   a laptop with a touchscreen answers "yes, I hover" and never takes this
   branch — leaving a finger on that machine with a zero-width image and a
   magnifier it cannot hit. `any-hover: none` is true only when nothing
   attached to the device can hover, which is the case this rule is for. The
   hybrid machine is handled the other way round, by the script: a tap opens
   the row it landed on. */
@media (any-hover: none) {
	.ual-row__media {
		width: var(--ual-media-w);
		height: var(--ual-media-h);
		margin-inline-end: var(--ual-media-gap);
		opacity: 1;
	}

	.ual-row__zoom-chip {
		opacity: 1;
		transform: translate(-50%, -50%) scale(1);
	}

	/* The photo gets a column of its own instead of sharing the date's.

	   Sharing is what makes the hover work: the image grows inside a track
	   whose width is already reserved, so the title and the button do not move
	   when a row is hovered. Nothing here hovers, so there is nothing to hold
	   still — and sharing turns into a permanent squeeze. At the default 150px
	   the image and its gap take 112 of the track, the date is left 38, and
	   "NOVEMBRO" runs straight across the event title for as long as the page
	   is open.

	   Split apart, the date has the whole width the panel gave it and the row
	   reads exactly as a desktop row does at rest, which is the state a touch
	   device is permanently in. */
	.ual-row {
		grid-template-columns: auto calc(var(--ual-date-w) + var(--ual-row-gap)) minmax(0, 1fr) auto;
	}

	/* The lead hands its two children straight to the row's grid so they can
	   take a track each. */
	.ual-row__lead {
		display: contents;
	}

	.ual-row__media {
		grid-column: 1;
	}

	.ual-row__date {
		grid-column: 2;
	}

	.ual-row__text {
		grid-column: 3;
	}

	.ual-row__button {
		grid-column: 4;
	}
}

/* Date. At rest the day sits large on the left with the month above the year
   to its right; on hover all three stack and centre. grid-template-areas
   cannot be transitioned, so the switch is discrete and masked by the fade
   below. */
.ual-row__date {
	/* Takes whatever the image leaves, so the stacked hover state centres in
	   the space beside the image instead of drifting across the row. */
	flex: 1 1 auto;
	min-width: 0;
	display: grid;
	grid-template-columns: auto auto;
	grid-template-areas:
		"day month"
		"day year";
	align-items: center;
	justify-content: start;
	gap: 0 8px;
	margin: 0;
	/* Reserving the stacked layout's height here keeps the row from stepping up
	   when hovered, at the cost of taller rows at rest. The script does better:
	   it measures both layouts and fills in the two heights below, so the block
	   grows into its stacked size alongside the image instead of snapping. Both
	   fall back to auto, which is the unaided behaviour. */
	min-height: var(--ual-date-min-h);
	height: var(--ual-date-h, auto);
	transition: height var(--ual-transition) var(--ual-ease);
}

/* Both reads have to be of settled layout, so the measuring state drops the
   tweened height and the transition with it. */
.ual-row--measuring .ual-row__date {
	height: auto;
	transition: none;
}

/* Centred inside a floor width, so the month and the year start at the same
   place whether the day is "6" or "28", and a short day still sits under the
   middle of a long one. The width is in em by default, so it tracks the day's
   own type size. */
.ual-row__day {
	grid-area: day;
	line-height: 1;
	min-width: var(--ual-day-w);
	text-align: var(--ual-day-align);
}

/* A month name wider than its column is left to overflow rather than to wrap or
   to be clipped, and the row gap is what absorbs it. This is load-bearing, not
   an oversight: on a hovered row the image takes its width out of the same lead
   the date sits in, and a layout tuned for a spelled-out "Novembro" is tuned by
   widening the gap rather than the column. Breaking the word instead — "Novemb"
   over "ro" — is worse in every case, and reserving a minimum for the date
   would shrink an image whose width the panel was explicit about. */
.ual-row__month {
	grid-area: month;
	align-self: end;
}

.ual-row__year {
	grid-area: year;
	align-self: start;
}

/* Gated on a class so the restack can be switched off entirely: without it the
   row grows only by the image, which does tween, and the hover is completely
   smooth. */
.ual-agenda--restack .ual-row:hover .ual-row__date,
.ual-agenda--restack .ual-row--open .ual-row__date,
.ual-agenda--restack .ual-row--stacked .ual-row__date {
	grid-template-columns: auto;
	grid-template-areas:
		"month"
		"day"
		"year";
	justify-content: center;
	justify-items: center;
	text-align: center;
	animation: ual-date-settle var(--ual-transition) var(--ual-ease);
}

.ual-agenda--restack .ual-row:has( :focus-visible ) .ual-row__date {
	grid-template-columns: auto;
	grid-template-areas:
		"month"
		"day"
		"year";
	justify-content: center;
	justify-items: center;
	text-align: center;
	animation: ual-date-settle var(--ual-transition) var(--ual-ease);
}

.ual-agenda--restack .ual-row:hover .ual-row__date,
.ual-agenda--restack .ual-row--open .ual-row__date {
	height: var(--ual-date-h-hover, auto);
}

.ual-agenda--restack .ual-row:has( :focus-visible ) .ual-row__date {
	height: var(--ual-date-h-hover, auto);
}

@keyframes ual-date-settle {
	from {
		opacity: 0.25;
	}

	to {
		opacity: 1;
	}
}

/* Text */

.ual-row__text {
	min-width: 0;
}

/* Event titles are editorial content, not a fixed vocabulary: a URL pasted as a
   title, a German compound, or CJK with no spaces at all would otherwise run
   straight through the button. */
.ual-row__subtitle,
.ual-row__title,
.ual-row__venue {
	overflow-wrap: break-word;
}

.ual-row__subtitle {
	margin: 0;
	transition: color var(--ual-transition) var(--ual-ease);
}

.ual-row__title {
	margin: 0 0 6px;
	transition: color var(--ual-transition) var(--ual-ease);
}

.ual-row__venue {
	margin: 0;
	transition: color var(--ual-transition) var(--ual-ease);
}

/* Button */

.ual-row__button {
	display: inline-block;
	justify-self: end;
	/* A translated label is routinely a third longer than the English it was
	   drawn for, and a sold-out label longer again. It keeps its one line while
	   the row can afford it and stops there. */
	max-width: 100%;
	white-space: nowrap;
	text-decoration: none;
	transition:
		background-color var(--ual-transition) var(--ual-ease),
		color var(--ual-transition) var(--ual-ease),
		box-shadow var(--ual-transition) var(--ual-ease);
}

/* Phone.
   =====

   The desktop row works because the image is closed until something hovers it:
   image and date share one column, and at rest that column holds only the
   date. On a phone nothing hovers, so the image is open the whole time — and
   the column is asked to carry a 96px photo, a 16px gap and the word
   "NOVEMBRO" at once. In 360px of screen that leaves the date around 40px and
   the month breaks across two lines. Widening the column only takes the space
   off the event title, which is the one thing on the row nobody can guess.

   So the phone is given a different composition rather than a narrower one.
   The photo leaves the date's column and becomes a thumbnail down the left of
   the whole row; the date gives up its stacked block for a single line above
   the title. That is the shape a phone already uses for a list of things with
   pictures, it reads at arm's length, and it hands the full remaining width to
   the title, the venue and the button in turn.

   Elementor's own mobile breakpoint, so a builder tuning the responsive width
   controls at "mobile" is tuning the same layout this rule describes. */
@media (max-width: 767px) {
	.ual-row {
		/* Three explicit rows — date, text, button — rather than letting them
		   be implicit. The photo spans the full height of the column beside it,
		   and `grid-row: 1 / -1` counts against the explicit grid only: with no
		   rows declared here, -1 resolves back to line 1 and the photo silently
		   spans nothing. A row whose button is hidden simply leaves the third
		   track empty at zero height.

		   Both gaps are zero and the spacing is carried by margins instead, so
		   an empty track and an absent photo cost nothing. A grid gap would
		   indent every row by the width of a photo that is not there. */
		grid-template-columns: auto minmax(0, 1fr);
		grid-template-rows: auto auto auto;
		column-gap: 0;
		row-gap: 0;
		/* The photo hangs from the top of the row rather than centring against
		   a text column whose height it does not know. */
		align-items: start;
	}

	/* The lead stops being a box of its own and hands its two children straight
	   to the row's grid. Without this the photo could only ever be as tall as
	   the date beside it; with it, it spans the title and the button too. */
	.ual-row__lead {
		display: contents;
	}

	.ual-row__text {
		grid-column: 2;
		margin-inline-end: 0;
	}

	/* Every state at once, because the hover pair further up is the more
	   specific selector and would otherwise put the desktop width back on a
	   phone-width window that does have a mouse. */
	.ual-row__media,
	.ual-row:hover .ual-row__media,
	.ual-row--open .ual-row__media {
		grid-column: 1;
		grid-row: 1 / -1;
		align-self: start;
		/* The panel's own size, capped against the screen it is on. Both
		   controls are tuned on a desktop canvas, so a 300px photo set there
		   would take most of a phone; wherever the chosen size already fits, it
		   is used unchanged. */
		width: min(var(--ual-media-w), 28vw);
		height: min(var(--ual-media-h), 34vw);
		/* The panel's own image gap, capped: it is the space between the photo
		   and the date on a desktop row and between the photo and everything on
		   a phone, and a value tuned for the first is often too wide for the
		   second. */
		margin-inline-end: min(var(--ual-media-gap), 16px);
		opacity: 1;
	}

	/* The keyboard state answered separately, for the same reason it is set
	   separately further up. */
	.ual-row:has( :focus-visible ) .ual-row__media {
		grid-column: 1;
		grid-row: 1 / -1;
		align-self: start;
		width: min(var(--ual-media-w), 28vw);
		height: min(var(--ual-media-h), 34vw);
		margin-inline-end: min(var(--ual-media-gap), 16px);
		opacity: 1;
	}

	/* One line — "28 Novembro 2026" — instead of the stacked block. The two
	   heights the script measures describe the desktop block and mean nothing
	   here, so the element goes back to sizing itself. */
	.ual-row__date {
		grid-column: 2;
		display: flex;
		flex-wrap: wrap;
		align-items: baseline;
		gap: 0 0.4em;
		height: auto;
		min-height: 0;
		/* The row's own gaps are zero, so the spacing down the second column is
		   carried here and on the button. */
		margin-bottom: 6px;
	}

	/* The floor width exists so a "6" sits under the middle of a "28" in the
	   stacked block. On one line it is just a gap after the day. */
	.ual-row__day {
		min-width: 0;
		text-align: start;
	}

	/* The restack is a hover behaviour and there is no hover here. Its rules
	   are more specific than the ones above, so they are answered rather than
	   avoided: display:flex has already made the grid template inert, and these
	   are what is left of it. */
	.ual-agenda--restack .ual-row:hover .ual-row__date,
	.ual-agenda--restack .ual-row--open .ual-row__date,
	.ual-agenda--restack .ual-row--stacked .ual-row__date {
		justify-content: flex-start;
		text-align: start;
		height: auto;
		animation: none;
	}

	.ual-agenda--restack .ual-row:has( :focus-visible ) .ual-row__date {
		justify-content: flex-start;
		text-align: start;
		height: auto;
		animation: none;
	}

	.ual-row__button {
		grid-column: 2;
		justify-self: start;
		margin-top: 10px;
		/* Room has run out; a label that cannot fit on one line takes two
		   rather than pushing out of the row. */
		white-space: normal;
	}

	/* A month name longer than the width left beside the day wraps onto a
	   second line of the date rather than pushing the line out of the row, and
	   a single word longer than the whole column breaks.

	   The alignments are the stacked block's: the month hangs from the bottom
	   of its grid area and the year from the top, which is what puts them
	   either side of the day. On one line those become cross-axis alignments
	   and lift the year clean off the baseline the other two sit on. */
	.ual-row__month,
	.ual-row__year {
		align-self: baseline;
		min-width: 0;
		overflow-wrap: break-word;
	}
}

/* Phone held sideways. Wider than the rule above and shorter than anything the
   desktop row was drawn for: 844 by 390. Three columns are right at that width
   — the height is not. A 130px photo, held open the whole time because the
   device cannot hover, leaves room for two events on the entire screen. So the
   photo is capped against the viewport it is actually in rather than the one
   the control was set on. Coarse pointer as well, because a desktop window
   dragged short is not a phone and has scrolling the phone does not. */
@media (orientation: landscape) and (max-height: 520px) and (any-pointer: coarse) {
	.ual-row__media,
	.ual-row:hover .ual-row__media,
	.ual-row--open .ual-row__media {
		max-height: 30vh;
		max-height: 30dvh;
	}

	.ual-row:has( :focus-visible ) .ual-row__media {
		max-height: 30vh;
		max-height: 30dvh;
	}
}

/* Anything a finger has to land on. The button's size comes from panel padding
   tuned under a mouse, and 32px of it is comfortable there and a miss under a
   thumb. The coarsest input the device has sets the floor — which includes a
   laptop with a touchscreen, where the same button serves both. */
@media (any-pointer: coarse) {
	.ual-row__button {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		/* Named so the floor is 44px of button rather than 44px plus whatever
		   padding the panel added: under a theme that leaves boxes
		   content-box, the same declaration comes out as 62. */
		box-sizing: border-box;
		min-height: 44px;
	}
}

/* Lightbox.

   Lives on <body>, not inside the widget: an Elementor section with a
   transform, filter or perspective becomes the containing block for its fixed
   descendants, which would pin the overlay inside that section instead of the
   viewport. The script copies the widget's own custom properties across when
   it opens, so each agenda still themes its own overlay. */
.ual-lightbox {
	position: fixed;
	inset: 0;
	z-index: 999999;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 20px;
	/* All four sides, not just top and bottom: a phone held in landscape puts
	   its notch and its home indicator on the left and right edges. */
	padding:
		max(24px, env(safe-area-inset-top))
		max(24px, env(safe-area-inset-right))
		max(24px, env(safe-area-inset-bottom))
		max(24px, env(safe-area-inset-left));
	opacity: 0;
	transition: opacity 0.2s var(--ual-ease);
}

.ual-lightbox[hidden] {
	display: none;
}

.ual-lightbox--open {
	opacity: 1;
}

/* The blur is what tells you the page behind is dismissable rather than
   merely dimmed. */
.ual-lightbox__scrim {
	position: absolute;
	inset: 0;
	background: var(--ual-lb-scrim);
	cursor: zoom-out;
}

@supports (backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px)) {
	.ual-lightbox__scrim {
		-webkit-backdrop-filter: blur(20px);
		backdrop-filter: blur(20px);
	}
}

.ual-lightbox__figure {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 16px;
	max-width: min(1200px, 100%);
	max-height: 100%;
	margin: 0;
}

.ual-lightbox__img {
	display: block;
	max-width: 100%;
	/* Leaves room for the caption without measuring it. The dynamic unit is the
	   one that matters on a phone: with 100vh the image is sized against the
	   viewport the browser would have if its address bar were hidden, so the
	   bottom of the picture sits under the bar until the visitor scrolls — and
	   the overlay locks scrolling, so they cannot. Declared twice because a
	   browser without dvh must still get a working value. */
	max-height: calc(100vh - 180px);
	max-height: calc(100dvh - 180px);
	width: auto;
	height: auto;
	border-radius: var(--ual-lb-radius);
	box-shadow: 0 24px 80px rgba(0, 0, 0, 0.45);
	transition:
		filter 0.3s var(--ual-ease),
		opacity 0.3s var(--ual-ease);
}

/* The thumbnail already on screen stands in until the original arrives, so the
   overlay never opens onto an empty frame on a slow connection. Blurred, so the
   upscale reads as a deliberate placeholder rather than a bad photo, and the
   dimensions come from the original — the swap changes the pixels, never the
   layout. */
.ual-lightbox--loading .ual-lightbox__img {
	filter: blur(12px);
}

/* Nothing to show. The frame collapses rather than presenting a broken-image
   glyph the size of the screen; the message below says what happened. */
.ual-lightbox--error .ual-lightbox__img {
	display: none;
}

/* Set by the script for the opening flight from the thumbnail, and cleared as
   soon as it lands so a resized window does not keep a stale transform. */
.ual-lightbox--flying .ual-lightbox__img {
	transition:
		transform 0.28s cubic-bezier(0.22, 0.68, 0.28, 1),
		filter 0.3s var(--ual-ease);
}

/* Leaving is quicker than arriving — a dismissal that takes as long as the
   reveal feels unresponsive. */
.ual-lightbox--closing .ual-lightbox__img {
	transition-duration: 0.18s;
}

.ual-lightbox--closing {
	transition-duration: 0.18s;
}

/* Themes commonly italicise figcaption. Left inherited, the caption would
   change character from site to site. */
.ual-lightbox .ual-lightbox__caption {
	display: flex;
	flex-direction: column;
	gap: 4px;
	margin: 0;
	text-align: center;
	font-style: normal;
	color: var(--ual-lb-caption);
}

.ual-lightbox__title {
	font-size: 1.0625rem;
	font-weight: 600;
	line-height: 1.4;
}

.ual-lightbox__date {
	font-size: 0.875rem;
	line-height: 1.5;
	opacity: 0.72;
}

/* Says what went wrong, in the page's language, in the place the picture was
   going to be. Empty and out of the accessibility tree until there is something
   to say — an announced-but-blank live region is worse than none. */
.ual-lightbox__status {
	display: none;
	max-width: 34ch;
	font-size: 0.9375rem;
	line-height: 1.5;
	text-align: center;
	color: var(--ual-lb-caption);
}

.ual-lightbox--error .ual-lightbox__status {
	display: block;
}

/* Doubled up on purpose. The overlay is a child of <body>, so it inherits the
   active theme's button styling — on the site this was built for that meant a
   pink fill over the glass. One class is not enough to win that, and neither
   is a custom property the theme's own rule simply overrides. */
.ual-lightbox .ual-lightbox__close {
	-webkit-appearance: none;
	appearance: none;
	min-width: 0;
	min-height: 0;
	margin: 0;
	font: inherit;
	letter-spacing: normal;
	text-transform: none;
	box-shadow: none;
	background-image: none;
	position: absolute;
	top: max(16px, env(safe-area-inset-top));
	/* Logical, so the button follows the page's direction. The safe-area values
	   are physical and the button is on whichever side the direction puts it,
	   so it clears the larger of the two rather than guessing. */
	inset-inline-end: max(16px, env(safe-area-inset-right), env(safe-area-inset-left));
	display: flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	padding: 0;
	color: var(--ual-zoom-color);
	background-color: var(--ual-zoom-bg);
	border: 1px solid rgba(255, 255, 255, 0.35);
	border-radius: 50%;
	cursor: pointer;
	transition:
		background-color 0.18s var(--ual-ease),
		transform 0.18s var(--ual-ease);
}

.ual-lightbox .ual-lightbox__close:hover {
	background-color: rgba(255, 255, 255, 0.28);
	transform: scale(1.06);
}

.ual-lightbox .ual-lightbox__close:focus-visible {
	outline: 2px solid var(--ual-zoom-color);
	outline-offset: 2px;
}

.ual-lightbox .ual-lightbox__close svg {
	width: 20px;
	height: 20px;
}

/* Compensating for the scrollbar the lock removes, so the page underneath does
   not shift sideways as the overlay opens. Logical, because a right-to-left
   page puts its scrollbar on the left and padding the wrong edge doubles the
   shift instead of cancelling it. */
.ual-lightbox-open {
	overflow: hidden;
	padding-inline-end: var(--ual-lb-gutter, 0);
}

@media (max-width: 767px) {
	.ual-lightbox {
		padding: max(16px, env(safe-area-inset-top)) max(16px, env(safe-area-inset-right)) max(16px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
	}

	.ual-lightbox__img {
		max-height: calc(100vh - 200px);
		max-height: calc(100dvh - 200px);
	}
}

/* A phone turned sideways has around 380px of height in total. The reservation
   above would leave the picture a strip; the caption gives up its own room
   first, because the photo is what was asked for. */
@media (max-height: 520px) and (orientation: landscape) {
	.ual-lightbox {
		gap: 10px;
	}

	.ual-lightbox__img {
		max-height: calc(100vh - 84px);
		max-height: calc(100dvh - 84px);
	}

	.ual-lightbox__date {
		display: none;
	}
}

/* Windows high contrast throws away every colour here and substitutes its own,
   so the scrim becomes an opaque system background and the picture loses the
   edge its shadow was drawing. These give the borders back. */
@media (forced-colors: active) {
	.ual-lightbox__img {
		border: 1px solid CanvasText;
	}

	.ual-row__zoom-chip,
	.ual-lightbox .ual-lightbox__close {
		border-color: currentColor;
	}

	/* Forced colours drop the translucent fill the chip relied on to separate
	   itself from the photo behind it. */
	.ual-row__zoom-chip {
		background-color: Canvas;
	}
}

/* Someone who has asked their system to stop animating things still gets every
   state change — it just arrives immediately. */
@media (prefers-reduced-motion: reduce) {
	.ual-agenda {
		--ual-transition: 0s;
	}

	.ual-row,
	.ual-row__date,
	.ual-row__media,
	.ual-row__zoom-chip,
	.ual-row__title,
	.ual-row__venue,
	.ual-row__button,
	.ual-lightbox__close {
		transition: none;
	}

	/* The overlay still fades — an image appearing from nowhere at full size is
	   more disorienting than a 200ms opacity change — but it no longer flies in
	   from the thumbnail, and the background is dimmed rather than blurred. */
	.ual-lightbox--flying .ual-lightbox__img {
		transition: none;
	}

	.ual-lightbox__scrim {
		-webkit-backdrop-filter: none;
		backdrop-filter: none;
	}

	.ual-agenda--restack .ual-row:hover .ual-row__date {
		animation: none;
	}

	.ual-agenda--restack .ual-row:has( :focus-visible ) .ual-row__date {
		animation: none;
	}
}
