/* Layout shell for the two mount points.
   =====================================

   This is the ONLY layout this plugin owns. Everything rendered *inside* the
   mounts -- result cards, facets, sorting, pagination -- is the Snap bundle's
   markup, and its baseline styling belongs in src/styles/custom.scss in the
   kneenandco-snap repo, next to the components that emit those class names.
   When an SDK update renames a class, the fix then lands in the same commit as
   the markup change instead of silently breaking a repo that didn't change.

   Note on the cascade, since it is easy to assume the opposite: measured on a
   rendered page, the SDK's emotion <style> tags occupy the FIRST ~112 style
   nodes in <head>, the bundle's own custom.scss lands just after them, and
   this file's <link> lands later still. So a rule here beats both at equal
   specificity, with no !important needed. That makes this file a viable place
   for a site-specific override or an emergency hotfix that cannot wait for a
   bundle deploy -- but it is deliberately NOT where the baseline look lives,
   because that ordering is incidental to how emotion inserts its tags and to
   WordPress enqueue order, and neither is a contract. Put a rule here only
   when it belongs to markup the plugin itself renders, or when you are
   overriding the bundle on purpose and have said why.

   Without these rules the sidebar and the grid are two block-level siblings,
   so the grid renders a full sidebar-height below the fold. Mirrors the
   production layout: fixed-width filter column on the left, product grid
   taking the rest.

   Deliberately no `display: none` on either mount at any breakpoint --
   `hideTarget` is false on both targeters (CONTRACT.md section 1), so the
   bundle never clears visibility styling the plugin applies, and a mount
   hidden here would stay hidden after Snap renders into it. Below the
   breakpoint the columns stack instead. */
.athos-layout {
	display: grid;
	grid-template-columns: 1fr;
	gap: 2rem;
	align-items: start;

	/* Kadence renders `main.site-main` as `display: flex; flex-wrap: wrap`, so
	   this container is a FLEX ITEM, not a block. Flex items size to their
	   content by default (`flex: 0 1 auto`), which left it 140px narrower than
	   its own siblings — the page header and, on brand archives, the Kadence
	   description row both took the full 1640px content box while the product
	   grid sat at 1500px and read as inset.

	   `flex-basis: 100%` in a wrapping row gives it a line of its own at the
	   container's full width. `min-width: 0` because a flex item's automatic
	   minimum size is its content, which would otherwise let a wide grid push
	   past the container instead of the columns narrowing.

	   Harmless if the parent is ever not a flex container: flex properties are
	   ignored there, and a grid container already fills its block width. */
	flex: 1 1 100%;
	min-width: 0;
}

@media (min-width: 1025px) {
	.athos-layout {
		grid-template-columns: 260px minmax(0, 1fr);
		gap: 2.5rem;
	}

	/* The filter column scrolls with the page, like the server-rendered sidebar
	   it replaces. It was briefly `position: sticky` — that pinned it to the
	   viewport, so once the facets were taller than the window there was no way
	   to reach the ones below the fold except by scrolling the entire result
	   grid past first. A sticky element cannot scroll its own overflow into
	   view. Natural flow is both the matching behaviour and the usable one. */
}

/* A zero-result search has no facets, so Sidebar.jsx renders nothing and this
   collapses to one column. Without it the empty #athos-sidebar mount still held
   its 260px track open and the no-results content sat inset beside a blank
   column, off the gridline the page heading uses.

   `:has()` rather than a class the bundle toggles: the state is already
   expressed in the DOM by .ss__no-results, and a class would be a second source
   of truth that has to be kept in step with it. Browsers without :has() keep the
   two-column layout, which is the pre-existing behaviour rather than a break. */
.athos-layout:has(.ss__no-results) {
	grid-template-columns: 1fr;
}

/* One column is not enough on its own: the sidebar mount is still a grid ITEM,
   so it takes row 1 and the content drops to row 2, separated by the 2.5rem
   gap — 40px of empty space between the page heading and the first line of
   copy, with nothing in it.

   This is the one place `display: none` on a mount is correct, and it is worth
   saying why given the warning at the top of this file. That warning is about
   hiding a mount unconditionally: `hideTarget` is false on both targeters, so
   the bundle never clears visibility the plugin applied, and a permanently
   hidden mount would stay hidden after Snap rendered into it. This rule is
   conditional on .ss__no-results being in the DOM, so the moment a search
   returns results the selector stops matching and the column comes back by
   itself. */
.athos-layout:has(.ss__no-results) > #athos-sidebar {
	display: none;
}

/* minmax(0, 1fr) above rather than a bare 1fr: grid items default to
   min-width:auto, so a wide result card or a long unbroken product title would
   otherwise push the column past its track and overflow the page horizontally
   instead of wrapping. */
.athos-layout > #athos-content {
	min-width: 0;
}

/* Skeletons: the mount points must never render empty, or the page reads as
   broken during the gap between first paint and Snap's first render. */
.athos-skeleton {
	background: linear-gradient(90deg, #f2f2f2 25%, #e8e8e8 37%, #f2f2f2 63%);
	background-size: 400% 100%;
	animation: athos-shimmer 1.4s ease infinite;
	border-radius: 2px;
}

.athos-skeleton--grid {
	min-height: 60vh;
}

.athos-skeleton--sidebar {
	min-height: 40vh;
}

@keyframes athos-shimmer {
	0% {
		background-position: 100% 50%;
	}
	100% {
		background-position: 0 50%;
	}
}

@media (prefers-reduced-motion: reduce) {
	.athos-skeleton {
		animation: none;
	}
}

/* The server-rendered page of the grid (Athos_Server_Grid). It is in the HTML
   for crawlers and visitors without JavaScript; while JavaScript runs it stays
   hidden so shoppers see the skeleton, then Snap, exactly as before. The
   <noscript> style in <head> shows it without JavaScript, and the watchdog
   adds --revealed when Snap fails. */
.athos-server-grid {
	display: none;
}

.athos-server-grid.athos-server-grid--revealed {
	display: block;
}

.athos-server-pagination ul {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin: 2rem 0 0;
	padding: 0;
	list-style: none;
}

.athos-server-pagination a,
.athos-server-pagination span {
	display: inline-block;
	min-width: 2.25rem;
	padding: 0.375rem 0.5rem;
	text-align: center;
}

.athos-server-pagination [aria-current="page"] {
	font-weight: 600;
	border-bottom: 2px solid currentColor;
}

.athos-degraded-notice {
	margin: 0 0 1rem;
	padding: 0.75rem 1rem;
	background: #fbf6e9;
	border-left: 3px solid #c9a227;
	font-size: 0.9375rem;
}

/* Shortcode-placed carousels carry no section chrome.

   The bundle's custom.scss gives .ss__recommendation a border-top and 48px of
   vertical padding, which is right for a carousel dropped onto a template hook
   with nothing around it — a product page, the 404. A carousel placed in the
   editor sits inside a block that already owns its spacing and framing, so the
   carousel supplying its own doubles it.

   The modifier is emitted by Athos_Recommendations::shortcode() and means
   "placed by shortcode", not "is a particular profile", so a profile later
   moved between a hook and the editor needs no change here.

   Lives in this file rather than in the bundle's custom.scss because the class
   is the plugin's own markup, which is the split the note at the top of this
   file asks for. It beats the bundle's rule on specificity as well as order. */
.athos-recs--bare .ss__recommendation {
	padding: 0;
	border-top: 0;
}

/* Trade price suppression -- defence in depth, not the primary mechanism.
   The Snap bundle's own src/styles/custom.scss already hides
   .ss__result__details__pricing, .ss__result__price and .ss__price under
   body:not(.athos-live-pricing-ready) for EVERY shopper -- observeLivePricing
   adds that class immediately (and again at DOMContentLoaded) for non-Trade
   shoppers, and only after a real `athos:live-pricing` `{ ok: true }` event
   for Trade shoppers. This rule duplicates that same selector set, scoped to
   Trade shoppers specifically, so a Trade customer still never sees a retail
   price standing in for one Live Pricing hasn't resolved even if the bundle's
   own stylesheet is ever missing (stale cache, failed build). Trade customers
   get role-based discounts applied by Athos Live Pricing at display time; the
   feed carries retail only, and showing a Trade customer a retail price is
   strictly worse than showing none -- see docs/CONTRACT.md section 8. Do NOT
   add anything here (or in JS) that sets .athos-live-pricing-ready from the
   plugin side: that class is the bundle's own positive-state signal, and only
   the real Live Pricing script dispatching athos:live-pricing may cause it to
   appear. The product page itself is still server-rendered by WooCommerce and
   still correct for the shopper's role, so it is unaffected by any of this. */
body.athos-trade:not(.athos-live-pricing-ready) .ss__result__details__pricing,
body.athos-trade:not(.athos-live-pricing-ready) .ss__result__price,
body.athos-trade:not(.athos-live-pricing-ready) .ss__price {
	visibility: hidden;
}

/* Recommendation carousels, for Trade shoppers, UNCONDITIONALLY.

   THIRD guard, and currently a redundant one. Kept deliberately, but do not
   mistake it for the thing keeping retail prices away from Trade shoppers in a
   carousel -- it is not, and the original version of this comment said it was.

   Carousels DO render a Trade shopper's own price now: Recs.jsx passes
   ResultCard as <Recommendation>'s resultComponent, so TradePrice renders from
   detailSlot exactly as it does in the grid, and Price.jsx returns null for a
   Trade shopper so the retail figure never appears beside it. Verified in a
   browser: a Trade card reads "Retail $80.00 | Your Trade Price $64.00".

   What this rule guards is the SDK's OWN price element, `.ss__price`, which is
   not the one ResultCard renders. Today that element is already hidden, because
   custom.scss sets `.ss__result .ss__result__details__pricing { display: none }`
   unconditionally and `.ss__price` lives inside that wrapper. So this rule is
   currently redundant -- and it is kept deliberately anyway, because it is the
   only price guard in the integration that is NOT gated on
   :not(.athos-live-pricing-ready). Every other one lapses the moment Live
   Pricing resolves for a Trade shopper. If the grid's price replacement is ever
   reworked and that wrapper stops being hidden, this is what still stands
   between a Trade shopper and a retail figure.

   Two earlier versions of this comment were wrong: the first claimed the retail
   price was reachable (it was not -- the wrapper is unconditionally hidden), the
   second claimed carousels had no ResultCard (they did, as of the same branch).
   Both were corrected after measuring rather than reasoning. */
body.athos-trade .ss__recommendation .ss__price,
body.athos-trade .ss__recommendation .ss__result__price,
body.athos-trade .ss__recommendation .ss__result__details__pricing {
	visibility: hidden;
}
