/**
 * Upgrade Agenda Slider — frontend styles.
 *
 * Registered by Plugin::register_assets() under the handle
 * `upgrade-agenda-slider` and declared by the widget through
 * get_style_depends(), so Elementor enqueues it only on pages that
 * actually contain the widget.
 *
 * Every value a style control will later set is declared here as a --uas-*
 * custom property with a working default, so a rule is never inert until
 * somebody drags a slider. The motion layer — same-cell stacking, both
 * transition modes, touch-action and the reduced-motion guarantee — arrives
 * in phase 4 and is driven by the data-uas-* attributes the widget writes
 * onto .uas. Navigation rules arrive in phase 5; the style controls that
 * write to these properties arrive in phase 6.
 */

.uas {
	position: relative;
	/* Every slide occupies the same grid cell (see .uas__slide below), so the
	   track's height auto-sizes to the tallest slide's own content. Absolute
	   stacking would take them all out of flow and collapse this element to
	   zero height at the 767px breakpoint, where --uas-slide-min-h is auto —
	   and putting that back would need JS measurement. This is the reason the
	   engine has no ResizeObserver: it never measures anything. */
	display: grid;
	/* Clips the neighbour parked one slide-width away in horizontal-slide
	   mode. Without it a wide page shows the next slide sitting beside this
	   one. */
	overflow: hidden;
	/* MOT-08. Vertical page scroll belongs to the browser unconditionally;
	   only a horizontal gesture ever reaches the swipe handler. Without this
	   a full-bleed 100svh slide is exactly the kind of element that swallows
	   a scroll on touch. */
	touch-action: pan-y;

	/* MOT-12's backstop, the second of two layers. The first layer is
	   structural - no transition ever fades its outgoing slide, so the
	   composite alpha cannot dip. This is what catches the case where one
	   somehow does: what shows through is #111111, not the theme's white page
	   background. It is also what the `slide` mode reveals between two slides
	   mid-travel, which is a real gap even though that mode never dips
	   opacity. .uas__slide carries no background of its own - the dark colour
	   lives on .uas__media INSIDE each slide and goes wherever the slide goes.
	   This one is on the container and never moves. */
	background-color: var(--uas-media-bg);

	/* Frame. The mockup's content box measures x 132 -> 1018 of 1150 — 77.0%
	   of the frame with symmetric 11.5% insets — so the inset is a percentage
	   of the frame, not a fixed 48px that drifts with viewport width. A
	   percentage padding resolves against width on every side, which is also
	   what gives the frame its vertical breathing room: 11.5% of a 1728px
	   viewport is 199px, and the mockup's own bottom inset scales to 132px of
	   1150. The content box is the padding box now, so the max-width that used
	   to hold it to 720px becomes a full-width pass-through. */
	--uas-slide-min-h: 100svh;
	--uas-slide-pad: 11.5%;
	--uas-content-maxw: 100%;
	/* The vertical rhythm between bands, and the horizontal rhythm between
	   band 2's columns. Measured: title bottom 410 -> rule 429 -> date top 450,
	   so ~20px; date block end 268 -> venue icon 322, and meta end 515 ->
	   excerpt 565, so ~50px. */
	--uas-content-gap: 20px;
	--uas-band-gap: 48px;
	--uas-head-gap: 8px;
	--uas-content-align: flex-start;
	--uas-text-color: #ffffff;

	/* Media layer. Every sampled subject pixel in desktop.jpeg has an R/G/B
	   spread of exactly 0 — (400,150)=(77,77,77), (430,250)=(80,80,80),
	   (300,400)=(24,24,24) — so the photo is fully desaturated, not merely
	   dimmed.

	   The scrim is separate from the gradient rather than folded into it. The
	   bottom gradient alone left the top of the photo bright: sampled down the
	   subject at x=400 the mockup reads y=60 -> (24,24,24), y=120 -> (59,59,59),
	   y=180 -> (98,98,98), and 98 is the brightest point of the whole photo —
	   38% luminance. Two layers keep the two jobs separable for Phase 6's
	   STY-02, which controls the gradient's colours and direction without
	   having to re-derive the flat darkening underneath. */
	--uas-media-bg: #111111;
	--uas-media-fit: cover;
	--uas-media-position: 50% 50%;
	--uas-media-scrim: rgba(0, 0, 0, 0.55);
	/* grayscale(0) is a no-op — images ship in ORIGINAL colour by default,
	   honouring the client's original-colour-images decision. This is Phase
	   6's STY-02 surface: a builder can dial the amount in from the panel,
	   but the shipped default never re-introduces the Phase-3 greyscale
	   look. */
	--uas-media-grayscale: grayscale(0);
	/* Decomposed into direction + two colour stops so Phase 6's STY-02 has
	   three separate sub-tokens to write — Elementor cannot compose a
	   multi-part gradient string from separate controls into one property.
	   Recomposed below into the identical gradient value. */
	--uas-media-gradient-dir: to top;
	--uas-media-gradient-from: rgba(0, 0, 0, 0.85);
	--uas-media-gradient-to: rgba(0, 0, 0, 0);
	--uas-media-gradient: linear-gradient(var(--uas-media-gradient-dir), var(--uas-media-gradient-from) 0%, var(--uas-media-gradient-to) 60%);

	/* The rules between bands. Desktop's is sampled rgb(125,125,125) on a
	   rgb(15,15,15) ground; mobile's two read rgb(232,232,232), far brighter,
	   which the breakpoint answers. */
	--uas-rule-size: 1px;
	--uas-rule-color: rgba(255, 255, 255, 0.45);
	/* Phase 6's STY-09 surface — defaults to the shipped solid rule. */
	--uas-rule-style: solid;

	/* Date, venue and type, excerpt, button. The day-to-month ratio is the one
	   type-scale value this phase owns: sampled cap heights are 31px for "28"
	   against ~11px for the month and year, a 2.8x ratio that no amount of
	   spacing reproduces. Both are properties so STY-05 has a surface.

	   The button is the same pill as the site header's "Contact us". Sampled
	   along a text-free row at y=381: x=900 -> #E80F54, x=940 -> #CE29CD,
	   x=1000 -> #48AEF0. */
	--uas-date-gap: 12px;
	--uas-day-size: 42px;
	--uas-month-size: 15px;
	--uas-meta-gap: 8px;
	--uas-icon-size: 20px;
	--uas-icon-gap: 8px;
	--uas-excerpt-basis: 45%;
	--uas-excerpt-maxw: 100%;
	--uas-button-pad: 14px 28px;
	--uas-button-radius: 999px;
	--uas-button-bg: linear-gradient(90deg, #E80F54 0%, #CE29CD 50%, #48AEF0 100%);
	--uas-button-color: #ffffff;

	/* Navigation. Every value Phase 6's STY-10 through STY-13 will write to,
	   declared with a working default so a rule is never inert until somebody
	   drags a slider — the same discipline as the twenty properties above.

	   Sampled from desktop.jpeg: the nav sits 76px above the frame's foot in a
	   1150px-wide render, which is 6.6% of the width, so it is a percentage like
	   --uas-slide-pad rather than a fixed pixel value that drifts with viewport.
	   The tab hairlines and the arrow border reuse --uas-rule-color's own
	   rgba(255, 255, 255, 0.45), so the nav's rules read as the same family as
	   the slide's. The progress line takes #E80F54, the left stop of the ticket
	   button's gradient already declared above — the accent is borrowed from the
	   mockup's palette rather than invented, since neither mockup shows a
	   progress line or a counter at all. */
	--uas-nav-inset: 6.6%;
	--uas-nav-gap: 24px;
	--uas-tab-pad: 8px 16px;
	--uas-tab-size: 13px;
	--uas-tab-color: rgba(255, 255, 255, 0.75);
	--uas-tab-active-color: #ffffff;
	--uas-tab-active-bg: rgba(255, 255, 255, 0.12);
	--uas-tab-sep-color: rgba(255, 255, 255, 0.35);
	--uas-tab-progress-color: #E80F54;
	--uas-tab-progress-size: 2px;
	--uas-dot-size: 10px;
	--uas-dot-gap: 30px;
	--uas-dot-color: rgba(255, 255, 255, 0.45);
	--uas-dot-active-color: #ffffff;
	--uas-arrow-size: 36px;
	--uas-arrow-color: #ffffff;
	--uas-arrow-border: 1px solid rgba(255, 255, 255, 0.45);
	--uas-arrow-radius: 4px;
	--uas-arrow-gap: 8px;
	--uas-counter-size: 13px;
	--uas-counter-color: rgba(255, 255, 255, 0.75);

	/* NAV-02. How many tab titles fit the strip. Overwritten per device on
	   .uas__tabs by the tabs_visible responsive control's own selectors — 4 on
	   desktop, 3 from tablet down. Declared here so the strip sizes correctly
	   before a builder has touched the control. */
	--uas-tabs-visible: 4;

	/* NAV-03. The autoplay period the progress line fills over. Overwritten
	   inline by the engine with the builder's own value, exactly the way
	   --uas-transition-duration already is, so the bar and the setInterval are
	   built from ONE number and cannot drift apart. */
	--uas-autoplay-interval: 3000ms;

	/* Motion. The duration is overwritten inline by the engine with the
	   builder's own value, and with 0ms when reduced motion is requested; this
	   declaration is what the rules below resolve against until then. */
	--uas-transition-duration: 600ms;
	--uas-transition-easing: ease;

	/* MOT-11. How far the incoming image drifts. A hint, not a journey - the
	   image never traverses the frame. 48px is the design intent settled with
	   the client; it is a custom property rather than a builder control on
	   purpose (YAGNI until somebody asks), so only a developer overrides it. */
	--uas-fade-slide-distance: 48px;
}

.uas__slide {
	/* Every slide in the same cell. The grid track sizes to the tallest of
	   them, so a mobile slide with no minimum height still gives .uas a real
	   height with no JS involved. */
	grid-area: 1 / 1;
	/* Hidden by default and revealed by state below. visibility rather than
	   display: none, so the slide keeps contributing its height to the track
	   and the container does not jump on every change; and rather than
	   opacity alone, because visibility also takes the slide's ticket-button
	   <a> out of hit-testing and out of the tab order, which is what makes
	   the engine's aria-hidden="true" on the same element correct. */
	visibility: hidden;
	position: relative;
	display: flex;
	align-items: flex-end;
	box-sizing: border-box;
	overflow: hidden;
	/* MOT-12's stacking half. Read this before touching any z-index here.

	   This element is position: relative with no z-index of its own, so it
	   establishes NO stacking context. Its descendants already carry z-indexes
	   - .uas__content at 1, and .uas__nav at 2 one level up - and without a
	   containing context those compete GLOBALLY across slides, not within
	   each slide. Raise the active slide alone and the OUTGOING slide's content
	   (z 1) paints above the INCOMING slide's media (z auto): text ghosting
	   over the wrong image, which reads to a viewer as exactly the flash this
	   phase is removing.

	   The declaration below makes each slide contain its own layering first.
	   Only then is raising the active slide (below) safe. */
	isolation: isolate;
	/* Static first. A browser that does not know svh drops the declaration
	   below, and without this one it would get no minimum height at all. */
	min-height: 100vh;
	min-height: var(--uas-slide-min-h, 100svh);
	padding: var(--uas-slide-pad);
	color: var(--uas-text-color);
}

/* Before the footer script runs — and on any page where it never runs at all —
   the first slide is the one that shows. Without this the whole widget would
   render blank until mount, which is a worse failure than no slider at all.
   transform and opacity are reset here too, because the transition-mode rules
   below start every slide off-screen or transparent. */
.uas:not([data-uas-ready]) .uas__slide:first-child {
	visibility: visible;
	opacity: 1;
	transform: none;
}

/* The mounted state. The engine sets data-uas-ready on .uas once, and moves
   data-uas-active between slides on every change. */
.uas[data-uas-ready] .uas__slide[data-uas-active] {
	visibility: visible;
	/* The incoming slide fades in OVER a fully opaque outgoing one, so it has
	   to sit above it whatever the DOM order says. 1, not something larger:
	   .uas__nav is z-index 2 in this same stacking context and must stay on
	   top of whichever slide is active. */
	z-index: 1;
}

/* MOT-06, mode A: crossfade. Still shipped, still selectable, and no longer
   the default - MOT-11's fade-slide is.

   Both modes share one index-swap core in the engine - all it ever does is
   move [data-uas-active] and write --uas-slide-offset. Which of those the
   browser animates is decided here, by the data-uas-transition attribute the
   widget writes onto .uas.

   MOT-12, the fix. As shipped through Phase 5 this rule animated BOTH slides'
   opacity at once - outgoing 1->0 and incoming 0->1 on the same easing - so at
   the midpoint both sat near 0.5 and the composite alpha was 1 - 0.5*0.5 =
   0.75. A quarter of the theme's page background showed through, white. That
   is the flash the client reported, and it was a real defect in shipped code.

   The fix is structural rather than a tuned easing curve: the outgoing slide
   never fades at all. `opacity 0s <duration>` holds it at 1 for the whole
   transition and then snaps it to 0 instantly, at the same instant the delayed
   `visibility` hides it - so the snap is never seen. The incoming slide fades
   0->1 on top of a fully opaque backdrop. Composite alpha is 1 on every frame,
   by construction; it is not tuned away and cannot regress by someone changing
   an easing function. */
.uas[data-uas-transition="crossfade"] .uas__slide {
	opacity: 0;
	transition: opacity 0s var(--uas-transition-duration),
		visibility 0s var(--uas-transition-duration);
}

.uas[data-uas-transition="crossfade"] .uas__slide[data-uas-active] {
	opacity: 1;
	transition: opacity var(--uas-transition-duration) var(--uas-transition-easing),
		visibility 0s 0s;
}

/* MOT-06, mode B: horizontal slide.

   --uas-slide-offset is the slide's own distance from the active one, in
   whole slides, written by the engine as a plain integer. Multiplying by 100%
   means "that many of MY OWN widths", which the browser recomputes on every
   layout pass — so no width is ever measured and no ResizeObserver is needed
   to keep the offsets correct across a resize or a device-preview switch.

   The `, 0` fallback is mandatory: this property is written by JavaScript and
   never declared in CSS, and tests/smoke-css.php's declared-vs-used check
   exempts fallback reads only. */
.uas[data-uas-transition="slide"] .uas__slide {
	transform: translateX(calc(var(--uas-slide-offset, 0) * 100%));
	transition: transform var(--uas-transition-duration) var(--uas-transition-easing),
		visibility 0s var(--uas-transition-duration);
}

.uas[data-uas-transition="slide"] .uas__slide[data-uas-active] {
	transform: translateX(0%);
	transition: transform var(--uas-transition-duration) var(--uas-transition-easing),
		visibility 0s 0s;
}

/* MOT-11, mode C: fade-slide. The default.

   The incoming image fades in while drifting a short distance horizontally in
   the direction of travel. Movement is a hint, not a journey: the image never
   traverses the frame. Only the background image moves - the title, excerpt,
   icons and ticket button crossfade in place with no movement at all, so text
   stays readable mid-transition. That is why these rules target .uas__media
   and never .uas__content: the renderer already emits them as separate
   elements (class-slide-renderer.php's .uas__slide > .uas__media + .uas__content),
   so nothing needed restructuring for this.

   Direction comes from --uas-slide-offset, which the engine already writes on
   every slide on every index change, CLAMPED and SIGN-INVERTED:

     clamp(-1, ..., 1) caps the drift at one distance-unit. Without it a tab
     click that jumps four slides would offset the media by 4 x 48px.

     The `-1 *` inversion IS INTENTIONAL AND WILL LOOK WRONG next to the
     `slide` mode above. In `slide`, the next slide sits to the right and
     travels leftward - filmstrip convention. The client asked for the
     opposite: next enters from the LEFT and drifts rightward. So next
     (offset +1) starts at -48px and prev (offset -1) starts at +48px.
     DO NOT "FIX" THIS.

     The `, 0` fallback is mandatory. --uas-slide-offset is written only by
     JavaScript and never declared in CSS, and tests/smoke-css.php's
     declared-vs-used cross-check exempts fallback reads only. Same rule the
     `slide` mode above already follows.

   Timing reuses --uas-transition-duration and --uas-transition-easing. The
   widget already exposes a duration control; a second one would be clutter. */
.uas[data-uas-transition="fade-slide"] .uas__slide {
	opacity: 0;
	/* MOT-12, same structural guarantee as crossfade above: the outgoing slide
	   never fades. It holds opacity 1 for the whole duration and snaps out at
	   the same instant the delayed visibility hides it. */
	transition: opacity 0s var(--uas-transition-duration),
		visibility 0s var(--uas-transition-duration);
}

.uas[data-uas-transition="fade-slide"] .uas__slide[data-uas-active] {
	opacity: 1;
	transition: opacity var(--uas-transition-duration) var(--uas-transition-easing),
		visibility 0s 0s;
}

.uas[data-uas-transition="fade-slide"] .uas__media {
	/* Edge bleed. A 48px drift on a 100%-wide image would expose an empty
	   strip at the frame edge mid-transition. The media box is widened by one
	   distance on each side and pulled back by one, so there is always image
	   under the frame edge whichever way it drifts. .uas__slide already has
	   overflow: hidden, which clips the overhang.

	   WIDENING, not scaling. A scale would crop the image and change its
	   framing at rest, which is a visible regression against the agency
	   mockups Phase 3 was signed off against. The right inset below is set
	   back to auto explicitly, because .uas__media's own `inset: 0` set all
	   four sides, and left + right + width together is over-constrained. */
	width: calc(100% + 2 * var(--uas-fade-slide-distance));
	left: calc(-1 * var(--uas-fade-slide-distance));
	right: auto;
	transform: translateX(calc(-1 * clamp(-1, var(--uas-slide-offset, 0), 1) * var(--uas-fade-slide-distance)));
	/* MOT-12's consequence: ONLY the incoming media drifts. The outgoing sits
	   still underneath at translateX(0) for the whole duration - the same
	   delayed-and-instant trick the opacity above uses - and re-parks at its
	   own offset only once the slide is already hidden. An outgoing image
	   jumping 48px at full opacity would read as exactly the artefact this
	   phase removes. At 48px the missing counter-drift is barely perceptible,
	   and it is what makes the no-flash guarantee structural rather than a
	   matter of easing curves. */
	transition: transform 0s var(--uas-transition-duration);
}

.uas[data-uas-transition="fade-slide"] .uas__slide[data-uas-active] .uas__media {
	transform: translateX(0);
	transition: transform var(--uas-transition-duration) var(--uas-transition-easing);
}

.uas__media {
	position: absolute;
	inset: 0;
	overflow: hidden;
	background-color: var(--uas-media-bg);
}

/* The overlay is a pseudo-element rather than a second div: it is decoration,
   not content, and it must never intercept a click meant for the button. */
.uas__media::after {
	content: "";
	position: absolute;
	inset: 0;
	/* Two layers: the bottom gradient sits over a flat scrim across the whole
	   frame. The first layer in the list paints on top. */
	background-image: var(--uas-media-gradient),
		linear-gradient(to bottom, var(--uas-media-scrim), var(--uas-media-scrim));
	pointer-events: none;
}

.uas__media-img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: var(--uas-media-fit);
	object-position: var(--uas-media-position);
	filter: var(--uas-media-grayscale);
}

.uas__content {
	/* Positioned, so it stacks above the absolutely positioned media layer
	   without either of them needing a z-index of its own. */
	position: relative;
	z-index: 1;
	/* A wrapping flex row, not a column. The mockup is two bands — a title row
	   with the ticket pill at its right, then a row of date, venue/type and
	   excerpt — and a full-width rule between them is what forces the break.
	   A column could not put the button beside the title without a second
	   wrapper the mobile order would then be unable to reach into. */
	display: flex;
	flex-wrap: wrap;
	align-items: var(--uas-content-align);
	/* The rhythm is the parent's gap, never a margin on a child. Any of these
	   children can be absent from the DOM entirely, and a margin belonging to
	   an element that is not there leaves exactly the hole SLD-09 rules out.
	   Row gap separates the bands; column gap separates band 2's blocks. */
	gap: var(--uas-content-gap) var(--uas-band-gap);
	width: 100%;
	max-width: var(--uas-content-maxw);
	box-sizing: border-box;
}

/* The bands are emitted by the renderer, which is the only thing that knows
   whether a band has any children at all. A band with nothing in it is never
   printed, so there is no empty box here to collapse. */
.uas__band {
	display: flex;
	box-sizing: border-box;
	min-width: 0;
}

/* Band 1's text. It takes the row and lets the ticket pill sit to its right. */
.uas__band--head {
	flex: 1 1 auto;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--uas-head-gap);
}

/* Band 2's first two blocks: the date, then venue over type. The excerpt is a
   sibling of this band rather than a child, so the breakpoint can drop the
   ticket pill between them. */
.uas__band--meta {
	flex: 0 0 auto;
	flex-wrap: wrap;
	align-items: flex-start;
	gap: var(--uas-band-gap);
}

/* An element, not a border. A border belongs to a box that may not be there,
   and would underline nothing when the band beside it is absent; the renderer
   gates this element on band 2 having content, so it can never draw alone.
   flex-basis 100% is also what breaks the flex line, which is what puts band 2
   under band 1 rather than beside it. */
.uas__rule {
	flex: 0 0 100%;
	width: 100%;
	height: 0;
	margin: 0;
	border: 0;
	border-top: var(--uas-rule-size) var(--uas-rule-style, solid) var(--uas-rule-color);
}

/* The mockup draws one rule on desktop and two on a phone. Both are always in
   the DOM — one markup path — and this is where the second one goes away. */
.uas__rule--foot {
	display: none;
}

/* Present for symmetry with --foot and as Phase 6's hook onto the head rule
   specifically; the base .uas__rule block above already covers its default,
   always-visible appearance. */
.uas__rule--head {
}

.uas__subtitle,
.uas__title,
.uas__excerpt {
	margin: 0;
	/* A pasted URL, a German compound or CJK with no spaces would otherwise
	   run straight out of the frame. */
	overflow-wrap: break-word;
}

.uas__date {
	display: flex;
	flex-wrap: wrap;
	/* Optically centred, not baseline-aligned. Sampled: "28" spans y453-484 and
	   the stack spans y453-487, so their centres land within 2px of each other
	   while their baselines are 20px apart. */
	align-items: center;
	gap: var(--uas-date-gap);
}

/* The month over the year, beside the day rather than after it. */
.uas__date-stack {
	display: flex;
	flex-direction: column;
	font-size: var(--uas-month-size);
	line-height: 1.35;
}

.uas__day {
	font-size: var(--uas-day-size);
	line-height: 1;
}

.uas__day,
.uas__month,
.uas__year {
	display: inline-block;
}

/* G-07. Venue over type at every width. The shipped version had this the other
   way round — a row by default, a column only under the breakpoint — and the
   mockup stacks them on desktop too, inside band 2's second block. */
.uas__meta {
	display: flex;
	flex-direction: column;
	flex-wrap: wrap;
	align-items: flex-start;
	gap: var(--uas-meta-gap);
}

.uas__venue,
.uas__event-type {
	display: inline-flex;
	align-items: center;
	gap: var(--uas-icon-gap);
	margin: 0;
}

.uas__venue-icon,
.uas__event-type-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 auto;
	width: var(--uas-icon-size);
	height: var(--uas-icon-size);
}

/* A library or uploaded icon is drawn with fill, and an SVG given no fill is
   black however the icon colour is set. */
.uas__venue-icon svg,
.uas__event-type-icon svg {
	width: 100%;
	height: 100%;
	fill: currentColor;
}

.uas__venue-text,
.uas__event-type-text {
	overflow-wrap: break-word;
}

/* Band 2's third block. It grows to the content box's right inset — measured
   x 565 -> 1018 of a box ending at 1018 — so the basis is a floor, not a cap. */
.uas__excerpt {
	flex: 1 1 var(--uas-excerpt-basis);
	min-width: 0;
	max-width: var(--uas-excerpt-maxw);
	/* SLD-10. Descriptions of different lengths wrap to different numbers of
	   lines, which changes each slide's content height — and because the
	   content is bottom-anchored (.uas__slide is align-items: flex-end), a
	   taller block pushes the title UP. So the title, date and rule sit at a
	   different height on every slide and the whole text zone jumps as the
	   slider advances. The engine measures the tallest description across all
	   slides and writes it here, so every description reserves that one height
	   and the layout above it holds still. The `, 0` fallback is mandatory:
	   this property is written by JS and never declared in CSS, exactly like
	   --uas-slide-offset, and smoke-css.php's declared-vs-used check exempts
	   only fallback reads. Before mount, and on any page where the script never
	   runs, it resolves to 0 and nothing is reserved — the original,
	   content-sized behaviour. */
	min-height: var(--uas-excerpt-min-h, 0);
}

.uas__button {
	display: inline-block;
	box-sizing: border-box;
	/* Band 1's right-hand item. margin-inline-start: auto pushes it to the
	   content box's right edge; align-self centres it against the title rather
	   than the top of the row, which is where the mockup puts it. This is
	   alignment, not rhythm — the rhythm is still the parent's gap. */
	flex: 0 0 auto;
	align-self: center;
	margin-inline-start: auto;
	padding: var(--uas-button-pad);
	border-radius: var(--uas-button-radius);
	background: var(--uas-button-bg);
	color: var(--uas-button-color);
	text-decoration: none;
	text-align: center;
	overflow-wrap: break-word;
}

/* ---------------------------------------------------------------------------
   Phase 5 — navigation.

   The nav is ONE element for the whole widget, not one per slide, and it must
   sit on top of whichever slide is active without joining the same-cell grid
   the slides occupy or changing the height that grid computes. Giving it a
   grid-area of its own would put it in the slides' auto-placement contest for
   no benefit; absolute against .uas's own box — which is already
   position: relative — takes it out of grid flow entirely.

   Its horizontal insets match the slide content's own --uas-slide-pad so the
   strip lines up with the title above it, and it sits inside the frame's bottom
   padding band rather than below it.
   --------------------------------------------------------------------------- */

.uas__nav {
	position: absolute;
	left: var(--uas-slide-pad);
	right: var(--uas-slide-pad);
	bottom: var(--uas-nav-inset);
	z-index: 2;
	display: flex;
	align-items: center;
	gap: var(--uas-nav-gap);
	color: var(--uas-text-color);
}

/* NAV-02. Every kept slide always has a tab in the DOM; how many are VISIBLE
   is sizing math, not a counted subset. Each tab takes 1/N of the strip and
   whatever does not fit scrolls — native overflow, no measurement, no resize
   listener, and no JavaScript involvement of any kind. This is the same
   "let the browser recompute on every layout pass" discipline that already
   kept a ResizeObserver out of this engine.

   min-width: 0 on a flex child is what allows it to be narrower than its own
   content, without which the strip would refuse to scroll and would instead
   push the arrows off the frame. */
.uas__tabs {
	flex: 1 1 auto;
	min-width: 0;
	display: flex;
	overflow-x: auto;
	/* The strip's own trailing hairline. Each tab carries its leading one, so
	   the pair produces the mockup's rule at both ends of the strip and one
	   between every neighbouring pair. */
	border-right: var(--uas-rule-size) solid var(--uas-tab-sep-color);
	/* A scrollbar drawn across the photo would be louder than the tabs. The
	   remainder is still reachable by drag, wheel and keyboard. */
	scrollbar-width: none;
}

.uas__tabs::-webkit-scrollbar {
	display: none;
}

.uas__tab {
	/* NAV-02's whole mechanism, in one declaration. */
	flex: 0 0 calc(100% / var(--uas-tabs-visible));
	min-width: 0;
	/* The progress line is absolutely positioned against this box. */
	position: relative;
	box-sizing: border-box;
	padding: var(--uas-tab-pad);
	border: 0;
	border-left: var(--uas-rule-size) solid var(--uas-tab-sep-color);
	border-radius: 0;
	background: transparent;
	color: var(--uas-tab-color);
	font: inherit;
	font-size: var(--uas-tab-size);
	text-align: center;
	cursor: pointer;
	transition: color var(--uas-transition-duration) var(--uas-transition-easing),
		background-color var(--uas-transition-duration) var(--uas-transition-easing);
}

/* The mockup's chip: the current tab is lighter text on a faint panel. Both
   attributes are written together by the engine, and the aria one is what a
   screen reader reads — styling on the visual one alone would let the two
   drift apart unnoticed. */
.uas__tab[data-uas-active] {
	background: var(--uas-tab-active-bg);
	color: var(--uas-tab-active-color);
}

.uas__tab-label {
	display: block;
	/* A long event title must not widen its tab past its 1/N share, or the
	   count a builder asked for stops being the count they get. */
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* NAV-03. The progress line is a CSS animation, not a JavaScript loop.

   Its duration reads the SAME number the engine's own setInterval was built
   from — the engine writes --uas-autoplay-interval onto .uas in init() from the
   identical this.interval it passes to setInterval — so the bar and the clock
   are two views of one value and cannot drift apart. A requestAnimationFrame
   loop tracking elapsed time would be a second, independent clock, which is
   exactly what NAV-03's "driven by the same clock as autoplay" rules out, and
   would reintroduce the class of bug Phase 4's composed pause gate exists to
   prevent.

   Restarting needs no JavaScript animation code either: a fresh animation
   begins whenever an element newly matches the animating selector, and the
   engine already moves [data-uas-active] between tabs on every real index
   change. The one case that is NOT a selector change — resuming after a pause,
   where the same tab stays active — is handled by the engine's start() forcing
   a genuine restart, because start() always begins a fresh full-duration
   countdown and never resumes a partial one. */
.uas__tab-progress {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: var(--uas-tab-progress-size);
	background: var(--uas-tab-progress-color);
	transform: scaleX(0);
	/* From the left edge. A default centre origin would grow outwards from the
	   middle, which reads as a pulse rather than as a countdown. */
	transform-origin: left center;
	pointer-events: none;
}

@keyframes uas-tab-progress {
	from {
		transform: scaleX(0);
	}

	to {
		transform: scaleX(1);
	}
}

.uas__tab[data-uas-active] .uas__tab-progress {
	animation: uas-tab-progress var(--uas-autoplay-interval) linear both;
}

/* Every pause reason — hover, focus, off-screen, reduced motion — reaches this
   one attribute, because the engine's stop() is the single place the clock ever
   halts and its start() is the single place it ever runs. Freezing here rather
   than resetting is what makes a hovering visitor see the countdown hold rather
   than snap back. */
.uas[data-uas-paused] .uas__tab-progress {
	animation-play-state: paused;
}

/* Autoplay off means there is no interval to fill against, so the bar renders
   inert rather than frozen at whatever width it happened to reach. The markup
   is still emitted — Phase 6's STY-10 targets it unconditionally. */
.uas:not([data-uas-autoplay="1"]) .uas__tab-progress {
	animation: none;
	transform: scaleX(0);
}

/* NAV-04's other half. Both strips are declared visible here; which one SHOWS
   at a given width is decided by the per-instance, builder-set-breakpoint
   <style> block Widget::nav_breakpoint_style() prints, because Elementor's
   own device system cannot express an arbitrary pixel threshold. A
   display: none default here would break NAV-07: a builder who switches the tab
   strip off and the dots on must get dots at every width. */
.uas__dots {
	display: flex;
	align-items: center;
	gap: var(--uas-dot-gap);
}

.uas__dot {
	width: var(--uas-dot-size);
	height: var(--uas-dot-size);
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: var(--uas-dot-color);
	cursor: pointer;
	transition: background-color var(--uas-transition-duration) var(--uas-transition-easing);
}

.uas__dot[data-uas-active] {
	background: var(--uas-dot-active-color);
}

/* The arrows and the counter travel together at the right of the nav, so the
   tab strip takes the remaining width and the arrows are never pulled into its
   own horizontal scroll. margin-left: auto rather than justify-content on the
   parent, so the mobile block can centre the whole nav with one declaration. */
.uas__controls {
	flex: 0 0 auto;
	margin-left: auto;
	display: flex;
	align-items: center;
	gap: var(--uas-arrow-gap);
}

.uas__arrow {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	width: var(--uas-arrow-size);
	height: var(--uas-arrow-size);
	padding: 0;
	border: var(--uas-arrow-border);
	border-radius: var(--uas-arrow-radius);
	background: transparent;
	color: var(--uas-arrow-color);
	cursor: pointer;
	transition: background-color var(--uas-transition-duration) var(--uas-transition-easing);
}

/* Structural only, so the mockup's square pair keeps its shape whatever Phase 6
   does to the two buttons independently. */
.uas__arrow--prev {
	order: 1;
}

.uas__arrow--next {
	order: 2;
}

/* currentColor on the path in the markup is what makes this inherit from the
   button, so STY-12's single colour control moves the glyph and the border
   together. */
.uas__arrow-icon {
	width: 60%;
	height: 60%;
	display: block;
}

.uas__counter {
	order: 3;
	margin: 0;
	color: var(--uas-counter-color);
	font-size: var(--uas-counter-size);
	white-space: nowrap;
	/* Tabular figures so 09 → 10 does not jog the layout sideways. */
	font-variant-numeric: tabular-nums;
}

.uas__counter-current,
.uas__counter-total {
	display: inline-block;
}

/* 44px is the smallest comfortable touch target, and this is the only way to
   the ticket page on a phone. */
@media (any-pointer: coarse) {
	.uas__button {
		display: inline-flex;
		align-items: center;
		min-height: 44px;
	}
}

/* Elementor's own mobile breakpoint, taken from the agency's existing
   sibling widget stylesheet. Local precedent across the suite rather than a
   value re-verified against Elementor's breakpoint settings in this milestone. */
@media (max-width: 767px) {
	.uas {
		--uas-slide-min-h: auto;
		/* Content box measured x 55 -> 536 of 588: 81.8% wide, 9.35% insets. */
		--uas-slide-pad: 9.35%;
		--uas-content-maxw: 100%;
		--uas-content-gap: 12px;
		--uas-band-gap: 24px;
		--uas-excerpt-maxw: 100%;
		--uas-excerpt-basis: 100%;
		/* The mobile rules are sampled rgb(232,232,232) against desktop's
		   rgb(125,125,125) — far brighter, and the one value that changes
		   between the two arrangements. */
		--uas-rule-color: rgba(255, 255, 255, 0.85);
	}

	.uas__slide {
		/* The static fallback from the desktop rule has to be answered too,
		   or a phone keeps a full-viewport frame whatever the property says. */
		min-height: 0;
		min-height: var(--uas-slide-min-h);
		/* With no minimum height the content is what gives the frame its
		   height, so the media layer needs something to fill: the top padding
		   is what leaves the image visible above the text. The percentage
		   inset above still governs left and right. */
		padding-block: 200px 24px;
		align-items: flex-end;
	}

	/* The phone slide's height comes from its stacked content, not a viewport
	   minimum, so .uas__media is a top:0/bottom:0 box whose COMPUTED height is
	   auto. .uas__media-img's height:100% then has nothing definite to resolve
	   against, and a replaced <img> with an auto height falls back to the aspect
	   ratio its width/height attributes imply — a short letterboxed strip that
	   reads as "contain", worst on the widest source. A flex column hands the img
	   a definite main-axis size from the media box's USED height, which
	   percentage resolution cannot see, so object-fit: cover fills again. Desktop
	   keeps its min-height:100svh path and never reaches this rule.

	   flex-direction: column is load-bearing, not decoration. Without it the box
	   is a default-row flex and the img only ever reaches full height through
	   align-items: stretch — and WebKit (real iOS Safari) refuses to stretch a
	   replaced <img> the way Blink does, so on an actual iPhone the image kept
	   its intrinsic aspect ratio and rendered letterboxed as "contain" while
	   Chrome's device-mode (a Blink engine, not WebKit) filled as "cover" and
	   hid the bug. A column makes height the MAIN axis, where flex: 1 1 auto on
	   the img grows it to the box's used height — a definite size WebKit does
	   honour — so object-fit: cover fills on both engines. Width stays 100% from
	   the base .uas__media-img rule, so it never depends on cross-axis stretch. */
	.uas__media {
		display: flex;
		flex-direction: column;
	}

	.uas__media-img {
		flex: 1 1 auto;
		min-height: 0;
		height: auto;
	}

	/* G-09. One DOM order, two arrangements. On a phone everything stacks, and
	   the ticket button drops out of band 1 to sit between the meta row and the
	   excerpt. That is a reorder of the markup the renderer already emits — a
	   second markup branch would duplicate the escaping surface and break every
	   single-contract assertion in the smoke suite.

	   Order 0 items keep their DOM order: head band, rule, meta band. Then the
	   three below follow, in the order the mockup shows them. */
	.uas__content {
		flex-direction: column;
		flex-wrap: nowrap;
		align-items: stretch;
	}

	.uas__button {
		order: 1;
		/* Auto width, left aligned — the mockup's pill is not full-bleed, and
		   the margin that pushed it right in band 1 has no job here. */
		align-self: flex-start;
		margin-inline-start: 0;
		width: auto;
	}

	.uas__excerpt {
		order: 2;
		flex: 0 0 auto;
	}

	/* The second rule, closing band 2 under the excerpt. */
	.uas__rule--foot {
		order: 3;
		display: block;
	}

	/* The mockup puts a centred dot row where the desktop nav puts a left-aligned
	   strip and a right-aligned control cluster. The tab strip AND the arrow/
	   counter cluster are both hidden by the per-instance breakpoint block, not
	   here, because that threshold is builder-set and this one is not — the phone
	   nav is the dot row alone. This block only reflows what remains. */
	.uas__nav {
		/* Out of the slides' shared 1/1 cell and into a row of its own beneath
		   the frame. Absolute-bottom drops the dots onto the excerpt now that the
		   phone layout stacks the content into the tallest thing in the cell;
		   position: static hands the nav back to grid auto-placement, which lands
		   it in row 2 because row 1 is full — the mockup's centred dot band under
		   the foot rule. */
		position: static;
		justify-content: center;
		padding-block: 28px 4px;
	}

	.uas__controls {
		margin-left: 0;
	}
}

/* MOT-05, the CSS half. The engine also zeroes --uas-transition-duration from
   JavaScript when matchMedia reports reduced motion, and stops the autoplay
   timer — which a media query cannot reach. This block is the guarantee that
   holds anyway: no transition, even if agenda-slider.js never loads, fails to
   parse, or is blocked.

   Slides still change. Nothing in the engine's goTo()/advance() path consults
   the pause state, so a swipe or an arrow key still moves the slider — it
   simply does so instantly. */
@media (prefers-reduced-motion: reduce) {
	/* MOT-13. MOT-05 already promises a reduced-motion visitor "no autoplay and
	   no transition animation", and the shipped code delivers that as an
	   instant swap. MOT-13 is that same guarantee extended to cover MOT-11's
	   new drift - NOT a softer version of it. Under reduced motion the slide
	   changes instantly: no drift, no fade. The drift lives on .uas__media,
	   which the .uas__slide selector does not reach, so without naming it here
	   the drift would survive the guarantee.

	   The no-flash guarantee holds trivially in this branch: an instant swap
	   has no intermediate frame to dip in. */
	.uas__media,
	.uas__slide {
		transition: none !important;
	}

	/* MOT-05 has to survive NAV-03. The engine's own paused.motion already stops
	   the clock, and the CSS half must hold even if the script never loads — so
	   the progress animation is killed here by the same block that already kills
	   every transition, rather than left running against a timer that is not. */
	.uas__tab-progress {
		animation: none !important;
		transform: scaleX(0) !important;
	}
}
