/* =================================================================
 * Editorial product catalogue — tile + grid styles.
 *
 * Companion to grid-ui.js::buildProductCard(). The redesign is
 * presentation-only: filter form submission, wishlist handler, and
 * REST endpoints are untouched. See:
 *   /plans/2026-04-25-catalogue-filters-rollout.md
 *
 * Tokens come from `editorial-type.css` + the site's :root vars.
 * Local fallbacks are provided so this file renders correctly even
 * if loaded before the shell tokens.
 * ================================================================= */

:root {
	--ae-cat-canvas: #ffffff;
	--ae-cat-ink: #111111;
	--ae-cat-ink-muted: rgba(17, 17, 17, 0.6);
	--ae-cat-ink-faint: rgba(17, 17, 17, 0.4);
	--ae-cat-line: rgba(17, 17, 17, 0.08);
	--ae-cat-line-strong: rgba(17, 17, 17, 0.14);
	--ae-cat-accent: #8a6f4e;
	--ae-cat-markdown: #8b2e1f;
	--ae-cat-ease: cubic-bezier(0.2, 0.7, 0.2, 1);
	--ae-cat-font-editorial: var(--ae-font-editorial, "Fraunces", Georgia, serif);
	--ae-cat-font-ui: var(--ae-font-ui, "Inter", "Helvetica Neue", Arial, sans-serif);
}

/* The grid container uses tokens already wired site-wide. */
.ProductGrid52 {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: 32px 16px;
	padding: 24px 16px;
	background: var(--ae-cat-canvas);
}

@media (min-width: 768px) {
	.ProductGrid52 {
		grid-template-columns: repeat(3, 1fr);
		gap: 48px 24px;
		padding: 40px 24px;
	}
}

@media (min-width: 1100px) {
	body.ae-shop-editorial .ProductListingPage52__layoutGrid .ProductGrid52,
	body.ae-shop-editorial .ProductListingPage52__layoutGrid .ProductGrid52[data-columns="3"],
	body.ae-shop-editorial .ProductListingPage52__layoutGrid .ProductGrid52.ProductListWithLoadMore52__listingGrid {
		grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
		grid-column-gap: 36px !important;
		grid-row-gap: 60px !important;
		gap: 60px 36px !important;
		padding: 32px 32px 12px;
	}
}

/* When the master Filter toggle is collapsed, the grid runs wider:
   reflow to 4 columns earlier so the freed space is meaningful. */
@media (min-width: 1100px) {
	body.ae-shop-editorial .ProductListingPage52__layoutGridWrapper.is-collapsed .ProductListingPage52__layoutGrid .ProductGrid52,
	body.ae-shop-editorial .ProductListingPage52__layoutGridWrapper.ae-desktop-filters-collapsed .ProductListingPage52__layoutGrid .ProductGrid52 {
		grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
		grid-column-gap: 36px !important;
		grid-row-gap: 60px !important;
		gap: 60px 36px !important;
	}
}

/* =====  Article shell  ===== */
.card.grid-product {
	position: relative;
	display: block;
	width: 100%;
	min-width: 0;
	margin: 0;
	background: transparent;
	cursor: pointer;
	transition: transform 200ms var(--ae-cat-ease);
	color: var(--ae-cat-ink);
}

.card.grid-product:hover {
	transform: translateY(-2px);
}

.card-link {
	display: block;
	width: 100%;
	color: inherit;
	text-decoration: none;
}

.card-link:focus-visible {
	outline: 2px solid var(--ae-cat-ink);
	outline-offset: 4px;
}

/* =====  Media (image well + hairline frame)  ===== */
.card-media {
	position: relative;
	aspect-ratio: 3 / 4;
	background: var(--ae-cat-canvas);
	margin-bottom: 14px;
	overflow: hidden;
	/* Hairline frame — single self-contained pair of rules; deletable in §11.1. */
	outline: 0.5px solid var(--ae-cat-line);
	outline-offset: -0.5px;
}

.card-media picture,
.card-media picture > img,
.card-media .ae-mobileCarousel,
.card-media .ae-mobileCarousel-track,
.card-media .ae-mobileCarousel-slide,
.card-media .AspectRatio18,
.card-media .AspectRatio18__content,
.card-media .Image18__imageContainer {
	display: block;
	width: 100%;
	height: 100%;
}

.card-image {
	position: absolute;
	inset: 0;
	transition: opacity 180ms var(--ae-cat-ease);
}

.card-image--primary {
	opacity: 1;
}

.card-image--secondary {
	opacity: 0;
}

.card-media img {
	object-fit: contain;
	object-position: center center;
	background: var(--ae-cat-canvas);
}

@media (hover: hover) and (pointer: fine) {
	.card-media.has-secondary:hover .card-image--primary {
		opacity: 0;
	}

	.card-media.has-secondary:hover .card-image--secondary {
		opacity: 1;
	}
}

.card-media .ae-mobileCarousel {
	display: none !important;
	position: absolute;
	inset: 0;
	overflow: hidden;
}

.card-media .ae-mobileCarousel-track {
	display: flex;
	overflow-x: auto;
	overflow-y: hidden;
	scroll-snap-type: x mandatory;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
	touch-action: pan-x pan-y;
	overscroll-behavior-x: contain;
	overscroll-behavior-y: auto;
}

.card-media .ae-mobileCarousel-track::-webkit-scrollbar {
	display: none;
}

.card-media .ae-mobileCarousel-slide {
	flex: 0 0 100%;
	scroll-snap-align: start;
}

.card-media .ae-mobileCarousel img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	-webkit-user-drag: none;
	user-select: none;
	pointer-events: none;
}

@media (hover: none), (pointer: coarse) {
	body.ae-shop-editorial .ProductGrid52 .card-media.has-mobile-carousel .card-image {
		display: none !important;
	}

	body.ae-shop-editorial .ProductGrid52 .card-media.has-mobile-carousel .ae-mobileCarousel {
		display: block !important;
		touch-action: pan-x pan-y;
	}
}

@media (min-width: 768px) {
	.card-media {
		margin-bottom: 16px;
	}
}

/* =====  Wishlist heart (top-right)  =====
   Bumped up + out: top/right offset increased so the icon sits flush with the
   editorial corner. Icon size raised for premium presence on retina; the 44×44
   button keeps the WCAG hit target. */
.card-wish {
	position: absolute;
	top: 10px;
	right: 10px;
	z-index: 2;
	width: 44px;
	height: 44px;
	background: transparent;
	border: 0;
	padding: 0;
	cursor: pointer;
	display: grid;
	place-items: center;
	transition: transform 160ms var(--ae-cat-ease);
}

@media (min-width: 768px) {
	.card-wish {
		top: 14px;
		right: 14px;
	}
}

.card-wish:hover {
	transform: scale(1.08);
}

.card-wish:focus-visible {
	outline: 2px solid var(--ae-cat-ink);
	outline-offset: 2px;
}

.card-wish-icon {
	width: 30px;
	height: 30px;
	color: var(--ae-cat-ink);
	stroke: currentColor;
	stroke-width: 1.5;
	fill: none;
	transition: fill 120ms var(--ae-cat-ease), stroke 120ms var(--ae-cat-ease);
	/* Subtle paper halo so the icon reads cleanly on any retailer image
	   background — pale skin tones, dark fabrics, busy prints. */
	filter: drop-shadow(0 1px 2px rgba(255, 255, 255, 0.7));
}

@media (min-width: 768px) {
	.card-wish-icon {
		width: 32px;
		height: 32px;
	}
}

/* Saved state — `wishlist.js` toggles `wishlist whislist` on the
   parent `.grid-product`; that flips the heart fill via this rule.
   The hidden `<i class="fa-heart">` inside the button is a legacy
   compatibility hook for the same toggle and is invisible. */
.card.grid-product.wishlist .card-wish-icon {
	fill: var(--ae-cat-ink);
}

.card-wish > i.fa-heart {
	display: none !important;
}

/* =====  Badge (top-left)  ===== */
.card-badge {
	position: absolute;
	top: 8px;
	left: 8px;
	z-index: 2;
	background: var(--ae-cat-canvas);
	color: var(--ae-cat-ink);
	border: 1px solid var(--ae-cat-line);
	padding: 4px 8px;
	font-family: var(--ae-cat-font-ui);
	font-size: 9px;
	font-weight: 600;
	letter-spacing: 0.2em;
	line-height: 1;
	text-transform: uppercase;
}

@media (min-width: 768px) {
	.card-badge {
		top: 12px;
		left: 12px;
		padding: 5px 10px;
		font-size: 10px;
	}
}

.card-badge--best {
	background: var(--ae-cat-ink);
	color: var(--ae-cat-canvas);
	border-color: var(--ae-cat-ink);
	font-family: var(--ae-cat-font-editorial);
	font-style: italic;
	font-weight: 400;
	letter-spacing: 0.08em;
	text-transform: none;
	font-size: 10px;
	padding: 5px 9px;
}

@media (min-width: 768px) {
	.card-badge--best {
		font-size: 11px;
		letter-spacing: 0.12em;
		padding: 6px 12px;
	}
}

/* =====  Card text — three editorial beats  ===== */
.card-brand {
	margin: 0 0 3px;
	color: var(--ae-cat-ink);
	font-family: var(--ae-cat-font-editorial);
	font-weight: 400;
	font-size: 13px;
	line-height: 1.3;
	letter-spacing: 0;
}

@media (min-width: 768px) {
	.card-brand {
		font-size: 15px;
	}
}

.card-name {
	margin: 0 0 8px;
	color: var(--ae-cat-ink-muted);
	font-family: var(--ae-cat-font-ui);
	font-size: 11px;
	font-weight: 400;
	line-height: 1.45;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

@media (min-width: 768px) {
	.card-name {
		font-size: 13px;
		margin-bottom: 10px;
		line-height: 1.5;
	}
}

.card-price {
	margin: 0 0 4px;
	color: var(--ae-cat-ink);
	font-family: var(--ae-cat-font-ui);
	font-size: 11px;
	font-weight: 500;
	line-height: 1.3;
}

@media (min-width: 768px) {
	.card-price {
		font-size: 13px;
		margin-bottom: 6px;
	}
}

.card-price .was {
	margin-right: 8px;
	margin-left: 0;
	color: var(--ae-cat-ink-faint);
	font-weight: 400;
	text-decoration: line-through;
}

.card-price--drop {
	color: var(--ae-cat-ink);
}

.card-price--drop .was {
	color: var(--ae-cat-ink-faint);
}

.card-price .sale {
	color: var(--ae-cat-markdown);
	font-weight: 600;
}

.card-price > [itemprop="price"] {
	display: inline-flex;
	align-items: baseline;
	gap: 8px;
}

.card-retailers {
	margin: 0;
	color: var(--ae-cat-ink-muted);
	font-family: var(--ae-cat-font-ui);
	font-size: 9px;
	font-weight: 500;
	letter-spacing: 0.14em;
	line-height: 1.5;
	text-transform: uppercase;
}

@media (min-width: 768px) {
	.card-retailers {
		font-size: 10px;
	}
}

/* =====  Unavailable state  ===== */
.card.is-unavailable .card-brand,
.card.is-unavailable .card-name,
.card.is-unavailable .card-price {
	color: var(--ae-cat-ink-faint);
}

/* The image stays full opacity by spec — only text fades. No filter,
   no opacity adjustment on the image itself. */

/* =====  Reduced motion  ===== */
@media (prefers-reduced-motion: reduce) {
	.card.grid-product,
	.card-wish,
	.card-wish-icon {
		transition: none;
	}
	.card.grid-product:hover {
		transform: none;
	}
	.card-wish:hover {
		transform: none;
	}
}

@media (min-width: 1100px) {
	body.ae-shop-editorial .ProductListingPage52__layoutGridWrapper.is-collapsed .ProductListingPage52__layoutGrid .ProductGrid52,
	body.ae-shop-editorial .ProductListingPage52__layoutGridWrapper.ae-desktop-filters-collapsed .ProductListingPage52__layoutGrid .ProductGrid52,
	body.ae-shop-editorial.ae-desktop-filters-collapsed .ProductListingPage52__layoutGrid .ProductGrid52 {
		grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
	}
}

@media (hover: none), (pointer: coarse) {
	body.ae-shop-editorial .ProductGrid52 .ae-mobileCarousel-track {
		overflow-y: hidden !important;
		touch-action: pan-x pan-y !important;
		overscroll-behavior-x: contain;
		overscroll-behavior-y: auto;
	}

	body.ae-shop-editorial .ProductGrid52 .ae-mobileCarousel-slide,
	body.ae-shop-editorial .ProductGrid52 .ae-mobileCarousel-slide * {
		touch-action: pan-x pan-y !important;
	}

	body.ae-shop-editorial .ProductGrid52 .card-media .ae-mobileCarousel .AspectRatio18--ratio2-3 {
		height: 100% !important;
		padding-top: 0 !important;
	}

	body.ae-shop-editorial .ProductGrid52 .ae-mobileCarousel img {
		object-fit: contain !important;
		-webkit-user-drag: none;
		user-select: none;
		pointer-events: none;
	}
}

/* =================================================================
 * Legacy ProductItem24 markup — explicitly hidden. The old grid
 * could occasionally inject empty skeleton rows alongside the new
 * `.card` markup, which produced large bands of empty space at the
 * bottom of the page (visible right above the pagination). The
 * previous rule used `display: revert` here, which was actually
 * un-hiding those skeletons. Switch to `display: none` so any stale
 * residue of the old markup is suppressed completely.
 * ================================================================= */
.ProductGrid52 .ProductItem24,
.ProductGrid52 .ProductItem24__skeletonContainer,
.ProductGrid52 .ProductItem24__details,
.ProductGrid52 .ProductItem24__lineBreakHack,
.ProductGrid52 .ProductItem24__price,
.ProductGrid52 .ProductItem24__imageContainer {
	display: none !important;
}

/* The new card grid should size itself to its content, not a min-height
   reserved by legacy skeleton rules in shop-listing.css. */
.ProductGrid52:not(:empty) {
	background: none !important;
	min-height: 0 !important;
	animation: none !important;
}

/* The new markup doesn't render `del.text-danger`; the rule below is a
   defensive fallback so a stray sale price from any cached payload still
   reads as muted strikethrough rather than red. */
.card-price del.text-danger {
	color: var(--ae-cat-ink-faint);
	font-weight: 400;
	text-decoration: line-through;
	margin-right: 6px;
}
