/*
 * Carousel — a Grid-layout Group or Post Template that becomes a scrolling
 * row below a breakpoint. Classes are written by the Carousel panel
 * (inc/carousel.php); see md/parent/block-editor.md §14.
 *
 *   carousel              on. Scrolls below mobile, grid above.
 *   carousel--tablet      scrolls below tablet instead.
 *   carousel--always      scrolls at every width.
 *   carousel--card-s / -l card width while scrolling (default is medium).
 *   carousel--contained   row stops at the content edge. Default is to run
 *                         to the viewport edge, first card on the content edge.
 *   carousel--no-arrows   no arrow buttons.
 *   carousel--scrollbar   drag-able scroll bar under the row.
 *
 * Every rule requires `.is-layout-grid`: core's Grid layout supplies the
 * desktop columns, so the grid state is untouched core output.
 *
 * The scroll state is flex, not grid. Core's Grid layout writes its columns as
 * grid-template-columns (and Post Template forces one column below 600px at
 * (0,3,0)); under flex those properties do nothing, so there is no
 * specificity contest with core. `.carousel.is-layout-grid` at (0,2,0) only
 * has to beat the global `body .is-layout-grid { display: grid }` at (0,1,1).
 *
 * Everything the row does is native CSS: overflow scrolling, touch swiping,
 * scroll-snap, scroll-padding for the bleed, overscroll containment and a
 * hidden scrollbar. assets/js/carousel.js adds only the arrow buttons.
 */

.carousel {
	--carousel-card: min(80%, 20rem);
	--carousel-bleed: var(--wp--preset--spacing--gutter, 0px);
}

/* Room above and below the cards so a shadow or hover lift is not cut off.
   Pulled back by a matching negative margin, so it takes no space. Set on the
   parent because the row's previous sibling reads it too (inc/carousel.php).
   :where() keeps it at zero weight so a child theme can override it. */
:where(:has(> .carousel)) {
	--carousel-room: var(--wp--preset--spacing--s);
}

.carousel--card-s { --carousel-card: min(70%, 14rem); }
.carousel--card-l { --carousel-card: min(85%, 28rem); }
.carousel--contained { --carousel-bleed: 0px; }

/* Declarations that only act while the row overflows. Harmless in the grid
   state, so they sit here once instead of in each breakpoint below. */
.carousel.is-layout-grid {
	scroll-snap-type: x mandatory;
	scroll-padding-inline: var(--carousel-bleed);
	scroll-behavior: smooth;
	overscroll-behavior-x: contain;
	scrollbar-width: none;
}

/* Flex basis and snap alignment are ignored by a grid, so the cards need no
   per-breakpoint rule either. */
.carousel.is-layout-grid > * {
	flex: 0 0 var(--carousel-card);
	min-width: 0;
	scroll-snap-align: start;
}

/*
 * The scroll state. Repeated per breakpoint range because a media query cannot
 * be part of a selector list; the rules are identical in all three.
 *
 * overflow-y: hidden keeps the row horizontal-only, so a vertical swipe on it
 * scrolls the page instead of rubber-banding the row.
 *
 * Bleed: the row is padded by the page gutter and pulled out by the same
 * amount. `!important` on that margin is the only one here: inside a
 * constrained layout, core centres every child with `margin-left/right: auto
 * !important`. Post Template's own `max-width: 100%` would cap it, so it is
 * lifted.
 *
 * Room for shadows: a scroller clips at its padding box, so the row is padded
 * above and below and pulled back by the same amount. The pull never uses the
 * margin slot core gives the block gap: the bottom is the row's
 * margin-block-end (core leaves it 0); the top is the previous sibling's
 * margin-block-end, which collapses with the gap in a flow layout and
 * subtracts from it in flex/grid, or the row's own when it is first. The
 * arrows (assets/js/carousel.js) sit just before the row and are absolutely
 * positioned, so the selectors look past them.
 */
@media (width <= 48rem) {
	.carousel.is-layout-grid {
		display: flex;
		max-width: none;
		overflow-x: auto;
		overflow-y: hidden;
		padding: var(--carousel-room) var(--carousel-bleed);
		margin-inline: calc(-1 * var(--carousel-bleed)) !important;
		margin-block-end: calc(-1 * var(--carousel-room));
	}

	.carousel.is-layout-grid:first-child,
	.carousel-nav:first-child + .carousel.is-layout-grid {
		margin-block-start: calc(-1 * var(--carousel-room));
	}

	:not(.carousel-nav):has(+ .carousel.is-layout-grid),
	:has(+ .carousel-nav + .carousel.is-layout-grid) {
		margin-block-end: calc(-1 * var(--carousel-room));
	}
}

@media (48rem < width <= 64rem) {
	.carousel--tablet.is-layout-grid,
	.carousel--always.is-layout-grid {
		display: flex;
		max-width: none;
		overflow-x: auto;
		overflow-y: hidden;
		padding: var(--carousel-room) var(--carousel-bleed);
		margin-inline: calc(-1 * var(--carousel-bleed)) !important;
		margin-block-end: calc(-1 * var(--carousel-room));
	}

	.carousel--tablet.is-layout-grid:first-child,
	.carousel-nav:first-child + .carousel--tablet.is-layout-grid,
	.carousel--always.is-layout-grid:first-child,
	.carousel-nav:first-child + .carousel--always.is-layout-grid {
		margin-block-start: calc(-1 * var(--carousel-room));
	}

	:not(.carousel-nav):has(+ .carousel--tablet.is-layout-grid),
	:has(+ .carousel-nav + .carousel--tablet.is-layout-grid),
	:not(.carousel-nav):has(+ .carousel--always.is-layout-grid),
	:has(+ .carousel-nav + .carousel--always.is-layout-grid) {
		margin-block-end: calc(-1 * var(--carousel-room));
	}
}

@media (width > 64rem) {
	.carousel--always.is-layout-grid {
		display: flex;
		max-width: none;
		overflow-x: auto;
		overflow-y: hidden;
		padding: var(--carousel-room) var(--carousel-bleed);
		margin-inline: calc(-1 * var(--carousel-bleed)) !important;
		margin-block-end: calc(-1 * var(--carousel-room));
	}

	.carousel--always.is-layout-grid:first-child,
	.carousel-nav:first-child + .carousel--always.is-layout-grid {
		margin-block-start: calc(-1 * var(--carousel-room));
	}

	:not(.carousel-nav):has(+ .carousel--always.is-layout-grid),
	:has(+ .carousel-nav + .carousel--always.is-layout-grid) {
		margin-block-end: calc(-1 * var(--carousel-room));
	}
}

@media (prefers-reduced-motion: reduce) {
	.carousel.is-layout-grid {
		scroll-behavior: auto;
	}
}

/* ---- Row focus (tabindex added by assets/js/carousel.js) ---------------- */

.carousel.is-layout-grid:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: -2px;
}

/* ---- Arrows (added by assets/js/carousel.js) ---------------------------- */

/* The arrows overlay the row, so its parent is their positioning box.
   :where() keeps this at zero weight so core's sticky position still wins. */
:where(:has(> .carousel.is-layout-grid)) {
	position: relative;
}

.carousel-nav {
	position: absolute;
	box-sizing: border-box;
	z-index: 2;
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding-inline: var(--wp--preset--spacing--xs);
	pointer-events: none;
}

.carousel-nav[hidden] {
	display: none;
}

.carousel-nav__btn {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 2.5rem;
	height: 2.5rem;
	padding: 0;
	border: none;
	border-radius: 50%;
	cursor: pointer;
	pointer-events: auto;
	/* Roles, not white: both flip with the section's scheme and palette, so the
	   icon always contrasts with its disc. */
	background-color: var(--wp--preset--color--foreground);
	color: var(--wp--preset--color--bg);
	box-shadow: var(--wp--custom--shadow--s);
	transition: opacity var(--wp--custom--transition--fast), box-shadow var(--wp--custom--transition--fast);
}

.carousel-nav__btn svg {
	inline-size: 1.25rem;
	block-size: 1.25rem;
}

.carousel-nav--no-arrows .carousel-nav__btn {
	display: none;
}

/* aria-disabled rather than disabled, so a focused button keeps focus at the
   end of the row. It stays visible, dimmed, while it has focus. */
.carousel-nav__btn[aria-disabled="true"] {
	opacity: 0;
	pointer-events: none;
}

.carousel-nav__btn[aria-disabled="true"]:focus-visible {
	opacity: 0.4;
}

.carousel-nav__btn:focus-visible {
	outline: 2px solid var(--wp--preset--color--foreground);
	outline-offset: 2px;
}

/* Desktop: arrows stay visible while the row scrolls (the nav is hidden
   otherwise), so a mouse user can see there is more without hovering first.
   Only the end that cannot move is hidden, via aria-disabled above. */
@media (hover: hover) {
	.carousel-nav__btn:hover {
		box-shadow: var(--wp--custom--shadow--l);
	}
}

/* Touch: swipe, no arrows. */
@media (hover: none) {
	.carousel-nav {
		display: none;
	}
}

/* ---- Scroll bar (carousel--scrollbar, added by assets/js/carousel.js) ---- */

/* Pointer only and aria-hidden: the row is the keyboard stop. The gap is set
   inline from --carousel-scrollbar-gap so a flow parent's blockGap cannot
   push the bar away from its row. */
:where(:has(> .carousel)) {
	--carousel-scrollbar-gap: var(--wp--preset--spacing--xs);
}

.carousel-scrollbar {
	position: relative;
	block-size: 0.5rem;
	border-radius: 100px;
	background-color: color-mix(in oklch, currentColor 15%, transparent);
	cursor: pointer;
}

.carousel-scrollbar[hidden] {
	display: none;
}

/* The visible bar is thin; the thumb's hit area is not. */
.carousel-scrollbar__thumb {
	position: absolute;
	inset-block: 0;
	border-radius: inherit;
	background-color: currentColor;
	opacity: 0.6;
	cursor: grab;
	touch-action: none;
	transition: opacity var(--wp--custom--transition--fast);
}

.carousel-scrollbar__thumb::before {
	content: "";
	position: absolute;
	inset: -0.5rem 0;
}

.carousel-scrollbar__thumb:hover,
.carousel-scrollbar.is-dragging .carousel-scrollbar__thumb {
	opacity: 1;
}

.carousel-scrollbar.is-dragging .carousel-scrollbar__thumb {
	cursor: grabbing;
}
