/**
 * TiorAI carousel — shared horizontal scroller shell.
 *
 * The scrolling is entirely native: an `overflow-x: auto` viewport with
 * scroll-snap. That gives touch-drag with momentum, trackpad scroll, shift+wheel,
 * keyboard focus scroll-into-view and snapping, with no JavaScript at all.
 * assets/js/carousel.js adds only the arrow buttons and their disabled states —
 * see its docblock for why that division exists.
 *
 * With JavaScript off the arrows never appear (they are hidden until the script
 * adds `is-scrollable`) and the track stays a plain, fully usable scrollable
 * strip. That is the intended no-JS experience, not a degraded one.
 *
 * @package TiorAI
 */

.tr-carousel {
	position: relative;

	/* What the edge fades blend INTO. It has to name the colour actually behind
	   the track — CSS cannot sample a backdrop — so a consumer whose section sits
	   on a different surface overrides this one property rather than restyling
	   the fades. */
	--tiorai-carousel-fade: var(--color-bg-primary);
	--tiorai-carousel-fade-width: clamp(2rem, 5vw, 4rem);

	/*
	 * HOW MANY CARDS ARE ON SCREEN, as a number rather than as a card width.
	 *
	 * Every consumer of this shell had previously sized its item in pixels and
	 * left the visible count to arithmetic — "168px, and the container is 1216,
	 * so six fit and a 24px sliver of the seventh stays on screen". That is a
	 * count derived from a width, and it breaks the moment the container width
	 * moves: a sidebar, a different gutter or a new breakpoint silently changes
	 * how many cards the row shows and whether the last one is sliced.
	 *
	 * Inverted here. The consumer states the COUNT and the gap; the width falls
	 * out of them, so the row always ends on a card boundary at every width and a
	 * responsive step is one number.
	 *
	 * `--tiorai-carousel-item-width` is a formula, not a resolved length: a custom
	 * property is substituted where it is USED, so the `100%` inside it resolves
	 * against the item's containing block — the track — and not against the root
	 * this declares it on. Two consequences a consumer must honour:
	 *
	 *   1. the track carries `gap: var(--tiorai-carousel-gap)`, so the number in
	 *      the formula and the gap actually painted are one value, and
	 *   2. the track carries NO inline padding, because padding shrinks the box
	 *      the `100%` resolves against and would push a sliver of the next card
	 *      back into view — which is the exact artefact this replaces.
	 *
	 * A FRACTIONAL count is legal and meaningful: `2.35` deliberately leaves a
	 * third of a card showing, which is how a consumer asks for a peek affordance
	 * instead of a clean edge.
	 */
	--tiorai-carousel-visible: 7;
	--tiorai-carousel-gap: var(--space-4);
	--tiorai-carousel-item-width: calc(
		(100% - (var(--tiorai-carousel-visible) - 1) * var(--tiorai-carousel-gap))
		/ var(--tiorai-carousel-visible)
	);
}

/* ==========================================================================
   Edge fades
   Content dissolving under the arrows, rather than being abruptly clipped by
   the viewport edge. This is also what makes side-mounted arrows acceptable:
   without it they sit on top of a hard-cut card and look like an overlay
   pasted over the content.
   ========================================================================== */

.tr-carousel::before,
.tr-carousel::after {
	content: "";
	position: absolute;
	top: 0;
	bottom: 0;
	width: var(--tiorai-carousel-fade-width);
	z-index: 1;

	/* Never intercepts a click or a swipe: the fade is paint, and the track
	   underneath it must stay draggable right up to the edge. */
	pointer-events: none;

	opacity: 1;
	transition: opacity 0.2s ease;
}

.tr-carousel::before {
	left: 0;
	background: linear-gradient(to right, var(--tiorai-carousel-fade), transparent);
}

.tr-carousel::after {
	right: 0;
	background: linear-gradient(to left, var(--tiorai-carousel-fade), transparent);
}

/*
 * A fade means "there is more this way", so it is shown only when there is.
 *
 * Both are off until the script confirms the track overflows, and each end's
 * fade switches off exactly when that end's arrow becomes `disabled` — so the
 * fade and the arrow are driven by ONE state and cannot disagree. `:has()` reads
 * that state straight off the button, which is why this needs no extra
 * JavaScript and no second class to keep in sync.
 */
.tr-carousel:not(.is-scrollable)::before,
.tr-carousel:not(.is-scrollable)::after,
.tr-carousel:has([data-tiorai-carousel-prev]:disabled)::before,
.tr-carousel:has([data-tiorai-carousel-next]:disabled)::after {
	opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
	.tr-carousel::before,
	.tr-carousel::after {
		transition: none;
	}
}

/* ==========================================================================
   Viewport
   ========================================================================== */

.tr-carousel__viewport {
	overflow-x: auto;
	overflow-y: hidden;
	scroll-snap-type: x mandatory;

	/* Momentum scrolling on touch, and no rubber-banding of the PAGE when the
	   track hits its end. */
	overscroll-behavior-x: contain;

	/* The scrollbar is redundant next to the arrows and adds ~15px of dead space
	   under the cards on desktop. The track stays scrollable by every other
	   means, so this hides an affordance rather than a capability. */
	scrollbar-width: none;
	-ms-overflow-style: none;
}

.tr-carousel__viewport::-webkit-scrollbar {
	display: none;
}

/*
 * Focus ring on the track itself.
 *
 * A scroll container with overflowing content is keyboard-focusable in Firefox
 * and, increasingly, elsewhere — so it can receive focus and MUST show it. This
 * is the one case where a ring on a non-interactive element is correct.
 */
.tr-carousel__viewport:focus-visible {
	outline: var(--tiorai-focus-ring-width, 2px) solid var(--tiorai-focus-ring-color, var(--color-primary));
	outline-offset: 4px;
	border-radius: var(--radius-lg);
}

/* ==========================================================================
   Controls
   ========================================================================== */

/*
 * Hidden until the script measures actual overflow. Two arrows over content that
 * already fits describe a problem the visitor does not have, and a pair of
 * permanently disabled buttons is worse than none.
 *
 * When shown it is a full-bleed overlay rather than a row, so the two buttons
 * can sit against opposite edges of the track. `pointer-events: none` on the
 * layer is what keeps the cards underneath fully clickable and swipeable — only
 * the buttons themselves take input.
 */
.tr-carousel__controls {
	display: none;
}

.tr-carousel.is-scrollable .tr-carousel__controls {
	display: block;
	position: absolute;
	inset: 0;
	z-index: 2;
	pointer-events: none;
}

.tr-carousel__btn {
	/* Vertically centred on the track and pinned to its edges, sitting on top of
	   the fade so the cards dissolve beneath rather than being cut by a button. */
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	pointer-events: auto;

	display: inline-flex;
	align-items: center;
	justify-content: center;

	/* 40px visible, with the 44px minimum target met by the padding box below.
	   Larger would out-weigh the cards it sits over. */
	width: 40px;
	height: 40px;

	padding: 0;
	border: 1px solid var(--border-default);
	border-radius: 50%;
	background: var(--surface-base);
	color: var(--color-text-secondary);
	cursor: pointer;

	transition:
		background-color 0.18s ease,
		border-color 0.18s ease,
		color 0.18s ease;
}

/*
 * Opaque, not a translucent tint. The button overlays the track, so a 4.5%-white
 * fill would let whatever card is behind it show through and read as a smudge
 * rather than as a control. `--color-bg-secondary` is the nearest solid step up
 * from the page in dark.
 */
[data-theme="dark"] .tr-carousel__btn {
	background: var(--color-bg-secondary);
	border-color: rgba(255, 255, 255, 0.14);
	color: var(--color-text-primary);
}

.tr-carousel__btn:hover:not(:disabled) {
	border-color: var(--color-primary);
	color: var(--color-primary);
}

.tr-carousel__btn:focus-visible {
	outline: var(--tiorai-focus-ring-width, 2px) solid var(--tiorai-focus-ring-color, var(--color-primary));
	outline-offset: 2px;
}

/*
 * At an end. The button is genuinely `disabled`, so it also leaves the tab order
 * — the visual state and the interaction state cannot disagree.
 */
/*
 * At an end. The button is genuinely `disabled`, so it also leaves the tab
 * order, and it is faded out entirely rather than dimmed: it sits over content
 * now, and a half-visible disc floating on a card is worse than no disc. The
 * fade on that side disappears at the same moment, from the same state.
 */
.tr-carousel__btn:disabled {
	opacity: 0;
	pointer-events: none;
	cursor: default;
}

/* Pinned to the track edges, over the fade. Inset by a hair rather than flush,
   so the disc has a little air against the container edge. */
.tr-carousel__btn[data-tiorai-carousel-prev] {
	left: var(--space-1);
}

.tr-carousel__btn[data-tiorai-carousel-next] {
	right: var(--space-1);
}

/* Sitting on top of content, so it needs to read as a control rather than as a
   translucent smudge: a solid surface and a shadow separate it from whatever
   card happens to be behind it. */
.tr-carousel.is-scrollable .tr-carousel__btn {
	box-shadow: 0 2px 10px rgb(0 0 0 / 0.18);
}

.tr-carousel__btn svg {
	pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
	.tr-carousel__btn {
		transition: none;
	}
}
