/*
 * Shopify surface styling.
 *
 * Enqueued by inc/shopify.php with a filemtime() version and a dependency on
 * bumblechutes-style. Deliberately outside the @import chain in src/style.css.
 *
 * Every rule is scoped below a Shopify container class, so this file coexists
 * with the plugin's own stylesheets and the rest of the theme.
 *
 * Contents:
 *   1. Utilities
 *   2. Rich text imported from Shopify
 *   3. Collection description
 *   4. Catalogue card accessibility
 *   5. Product detail page accessibility
 *   6. Product image gallery
 *   7. Navigation cart entry
 *   8. "From" price label on cards
 */

/* ------------------------------------------------------------------ *
 * 1. Utilities
 * ------------------------------------------------------------------ */

/*
 * Visually hidden but available to assistive technology. Used to give the
 * catalogue card's quick-shop trigger an accessible name.
 */
.bc-visually-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* ------------------------------------------------------------------ *
 * 2. Rich text imported from Shopify
 *
 * The descriptions were pasted into the Shopify admin from Microsoft Word, so
 * the HTML arrives carrying MsoNormal classes, mso- properties, page-break
 * rules, and inline font-size, line-height and colour on almost every element
 * - including body copy at 9pt, below an accessible minimum.
 *
 * Inline styles beat classes, so neutralising them needs !important. It is
 * confined to the properties Word actually sets. The durable fix is cleaning
 * the descriptions in Shopify; this keeps the page readable until then.
 *
 * The container carries the typography and everything below inherits, so the
 * description resolves to one set of values whatever Word decorated.
 *
 * Colour and family come from the theme tokens. Leading does not: the theme
 * sets body copy at 1.25, which suits the short paragraphs it was chosen for
 * but not descriptions this long. WCAG 1.4.12 takes 1.5 as its reference, so
 * 1.5 is applied here and nowhere else.
 * ------------------------------------------------------------------ */

.shopify-product-richtext,
.shopify-collection-richtext {
	color: var(--charcoal);
	font-family: var(--font);
	font-size: 1rem;
	line-height: 1.5;
}

/*
 * Reset the inline typography Word left behind, whatever tag it landed on.
 *
 * `[style]` alone is not enough. Word emits paragraphs carrying no style
 * attribute but holding styled spans, and a span set to `line-height: inherit`
 * still resolves against the paragraph Word left alone. The tag list covers
 * what Word actually emits. Headings, images and tables are excluded - they
 * are styled on their own terms below.
 *
 * `text-indent` is reset because of the paragraph rule below. Word fakes a
 * list with a hanging indent: `margin-left: 27pt` paired with
 * `text-indent: -13.5pt`, so the literal "1." sits in the gutter it just
 * opened. The paragraph rule zeroes the margin and left the indent, which is
 * half a matched pair and hung the numbers outside the content column.
 * Zeroing the indent restores the pair and lands those paragraphs flush.
 */
.shopify-product-richtext [style],
.shopify-product-richtext p,
.shopify-product-richtext li,
.shopify-product-richtext span,
.shopify-product-richtext b,
.shopify-product-richtext strong,
.shopify-product-richtext i,
.shopify-product-richtext em,
.shopify-product-richtext u,
.shopify-collection-richtext [style],
.shopify-collection-richtext p,
.shopify-collection-richtext li,
.shopify-collection-richtext span,
.shopify-collection-richtext b,
.shopify-collection-richtext strong,
.shopify-collection-richtext i,
.shopify-collection-richtext em,
.shopify-collection-richtext u {
	font-size: inherit !important;
	line-height: inherit !important;
	font-family: inherit !important;
	color: inherit !important;
	background-color: transparent !important;
	text-indent: 0 !important;
	page-break-before: auto !important;
	page-break-after: auto !important;
}

/*
 * Paragraph spacing has to be !important as well, because Word writes its own
 * margin-bottom inline on most of these paragraphs and leaves it off others.
 */
.shopify-product-richtext p,
.shopify-collection-richtext p {
	margin: 0 0 1em !important;
}

.shopify-product-richtext p:last-child,
.shopify-collection-richtext p:last-child {
	margin-bottom: 0 !important;
}

/*
 * Word emits empty paragraphs full of non-breaking spaces as spacers. Collapse
 * them rather than letting them open gaps in the layout.
 */
.shopify-product-richtext p:empty,
.shopify-collection-richtext p:empty {
	display: none;
}

/*
 * Headings need !important for the same reason the paragraphs do. Word styles
 * some of them and not others, and without this the reset above flattens the
 * styled ones to body size.
 *
 * The `[style]` variants are not redundant. Both this rule and the reset are
 * !important, so specificity decides between them, and a bare `h3` loses to
 * `[style]`. Naming the attribute here wins it back.
 *
 * `font-family` is named for the same reason as the size. The reset is keyed
 * partly on `[style]`, which does not care what tag it lands on, so a heading
 * Word styled fell through to the body family while an unstyled one kept the
 * heading family - two heading fonts in one description.
 *
 * All six levels get one treatment rather than a size hierarchy. The source is
 * a word-processor paste, so the heading level that arrives carries no
 * reliable meaning about rank.
 */
.shopify-product-richtext h1,
.shopify-product-richtext h2,
.shopify-product-richtext h3,
.shopify-product-richtext h4,
.shopify-product-richtext h5,
.shopify-product-richtext h6,
.shopify-product-richtext h1[style],
.shopify-product-richtext h2[style],
.shopify-product-richtext h3[style],
.shopify-product-richtext h4[style],
.shopify-product-richtext h5[style],
.shopify-product-richtext h6[style],
.shopify-collection-richtext h1,
.shopify-collection-richtext h2,
.shopify-collection-richtext h3,
.shopify-collection-richtext h4,
.shopify-collection-richtext h5,
.shopify-collection-richtext h6,
.shopify-collection-richtext h1[style],
.shopify-collection-richtext h2[style],
.shopify-collection-richtext h3[style],
.shopify-collection-richtext h4[style],
.shopify-collection-richtext h5[style],
.shopify-collection-richtext h6[style] {
	font-family: var(--head) !important;
	font-size: 1.15rem !important;
	line-height: 1.3 !important;
	margin: 1.5em 0 0.5em !important;
}

.shopify-product-richtext h1:first-child,
.shopify-product-richtext h2:first-child,
.shopify-product-richtext h3:first-child,
.shopify-product-richtext h4:first-child,
.shopify-product-richtext h5:first-child,
.shopify-product-richtext h6:first-child,
.shopify-collection-richtext h1:first-child,
.shopify-collection-richtext h2:first-child,
.shopify-collection-richtext h3:first-child,
.shopify-collection-richtext h4:first-child,
.shopify-collection-richtext h5:first-child,
.shopify-collection-richtext h6:first-child {
	margin-top: 0 !important;
}

.shopify-product-richtext ul,
.shopify-product-richtext ol,
.shopify-collection-richtext ul,
.shopify-collection-richtext ol {
	margin: 0 0 1em;
	padding-left: 1.5em;
}

.shopify-product-richtext img,
.shopify-collection-richtext img {
	max-width: 100%;
	height: auto;
}

.shopify-product-richtext table,
.shopify-collection-richtext table {
	display: block;
	max-width: 100%;
	overflow-x: auto;
}

/* ------------------------------------------------------------------ *
 * 3. Collection description
 * ------------------------------------------------------------------ */

.collection-layout .collection-layout__description,
.collection-grid__container .collection-layout__description {
	margin: 0 0 2rem;
	max-width: 70ch;
}

/*
 * The description sits inside a Shopify context template. Before hydration,
 * and on any collection whose Shopify description is blank, there is nothing
 * to show, so suppress the box rather than reserve blank space.
 *
 * `:empty` on the wrapper never matches: it always holds a <shopify-data>
 * child, so it is not empty in the CSS sense even with no text. Test the child
 * instead - <shopify-data> stays empty until the plugin injects the
 * description client-side.
 */
.collection-layout .collection-layout__description:has(> shopify-data:empty),
.collection-grid__container
	.collection-layout__description:has(> shopify-data:empty) {
	display: none;
	margin: 0;
}

/* ------------------------------------------------------------------ *
 * 4. Catalogue card accessibility
 *
 * The quick-shop trigger used to be a button wrapping a quick-add anchor,
 * which is invalid nested interactive content and confusing to a screen
 * reader. They are now siblings inside the image container: the trigger is a
 * transparent overlay, and quick-add keeps the plugin's own z-index of 10 so
 * it stays clickable above it.
 * ------------------------------------------------------------------ */

.collection-layout .collection-grid__product__open,
.collection-grid__container .collection-grid__product__open {
	position: absolute;
	inset: 0;
	width: 100%;
	padding: 0;
	border: 0;
	background: transparent;
	cursor: pointer;
	z-index: 5;
}

/*
 * Keyboard focus has to be visible. The overlay is transparent, so without
 * this there is no indication of where focus is.
 */
.collection-layout .collection-grid__product__open:focus-visible,
.collection-grid__container .collection-grid__product__open:focus-visible,
.collection-layout .single-product__hover-button:focus-visible,
.collection-grid__container .single-product__hover-button:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 2px;
}

/*
 * Quick-add is revealed on hover by the plugin's stylesheet. Reveal it on
 * keyboard focus too, otherwise it is unreachable without a mouse.
 */
.collection-layout .single-product__hover-button:focus-visible,
.collection-grid__container .single-product__hover-button:focus-visible {
	opacity: 1;
	transform: translate(-50%, -50%) translateY(0);
}

.collection-layout .collection-grid__product__open[disabled],
.collection-grid__container .collection-grid__product__open[disabled] {
	cursor: default;
}

/*
 * Reveal the catalogue grid's quick-shop trigger on keyboard focus. This is a
 * different control from the two above: those style the recommendation cards
 * from pdp.php, this one the collection cards from shopify/product-card.php,
 * where the vendor class is .product-card__hover-button.
 *
 * It has to be revealed, not just ringed. The vendor base rule is
 * `opacity: 0` with `transform: translateY(20px)`, undone only by
 * `.product-card:hover`, and an outline on a transparent element computes but
 * cannot be seen. The values here match that hover rule, so focus and hover
 * land the control in the same place.
 *
 * `:focus-within` on the card gives hover parity: hovering anywhere on a card
 * reveals its trigger, so tabbing to the card's title link should too,
 * otherwise the trigger appears out of nowhere at the next tab stop. Disabled
 * cards are excluded - the vendor hides their trigger because the product is
 * not available for sale.
 */
.collection-layout .product-card__hover-button:focus-visible,
.collection-grid__container .product-card__hover-button:focus-visible,
.collection-grid__grid .product-card__hover-button:focus-visible,
.collection-layout .product-card:not([disabled]):focus-within .product-card__hover-button,
.collection-grid__container .product-card:not([disabled]):focus-within .product-card__hover-button,
.collection-grid__grid .product-card:not([disabled]):focus-within .product-card__hover-button {
	opacity: 1;
	transform: translateY(0);
}

/*
 * The focus ring itself. `currentColor` resolves to black here, and the button
 * is a black pill sitting on product photography, so a single dark ring can
 * disappear against a dark image. The white ring fills the 2px offset gap and
 * the black outline sits immediately outside it, so one of the two always has
 * contrast whatever the artwork behind it.
 *
 * The outline is what carries this in Windows High Contrast Mode, where
 * box-shadow is dropped and outline is not. Keep both.
 */
.collection-layout .product-card__hover-button:focus-visible,
.collection-grid__container .product-card__hover-button:focus-visible,
.collection-grid__grid .product-card__hover-button:focus-visible {
	outline: 2px solid #000;
	outline-offset: 2px;
	box-shadow: 0 0 0 2px #fff;
}

/*
 * Target size. The trigger renders at 36 by 36 (16px icon, 8px padding, 2px
 * border), which clears the 24 by 24 of WCAG 2.5.8 but not the 44 by 44 the QA
 * brief asks for. This centres a 44 by 44 hit area on the control without
 * changing how it looks or moving anything around it, and grows with the
 * button when the label expands on hover.
 */
.product-card__hover-button::after {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	width: 100%;
	height: 100%;
	min-width: 44px;
	min-height: 44px;
}

/*
 * Room for the focus ring in the narrow card layout.
 *
 * The image container is `overflow: hidden` and the vendor's container query
 * puts the trigger flush right (`right: 0`) under 300px, clipping 4px of the
 * ring. At 301px and wider the vendor sets `right: 25px` and there is nothing
 * to fix.
 *
 * The offset applies at rest as well as on focus, so the control does not jump
 * sideways when it takes focus. The breakpoint and container match the
 * vendor's exactly.
 */
@container (max-width: 300px) {
	.collection-layout .product-card__hover-button,
	.collection-grid__container .product-card__hover-button,
	.collection-grid__grid .product-card__hover-button {
		right: 4px;
	}
}

/* ------------------------------------------------------------------ *
 * 5. Product detail page accessibility
 * ------------------------------------------------------------------ */

/*
 * The accordion header is a button now rather than a div. Buttons carry UA
 * styling that the plugin's .single-product__accordion__header rules do not
 * expect, so reset it back to what the original div rendered as.
 */
.shopify-product-details .single-product__accordion__header {
	display: block;
	width: 100%;
	text-align: left;
	font-family: inherit;
	color: inherit;
	background: none;
	border: 0;
	border-radius: 0;
	appearance: none;
}

/*
 * The accordion header needs its ring drawn inwards. The plugin clips the
 * surrounding item with overflow: hidden, and the header's border box is
 * exactly that clipping box, so an outline offset outwards lands entirely
 * outside the clip and is erased: the header looked unfocused while it held
 * focus. Removing the clip is not an option, because the item's border-radius
 * depends on it to round the expanded description. -3px keeps the ring clear
 * of the rounded corners, which would otherwise nip its own corners off.
 */
.shopify-product-details .single-product__accordion__header:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: -3px;
}

.shopify-product-details .collection__slider-button:focus-visible,
.shopify-product-details .single-product__incrementor button:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 2px;
}

/* ------------------------------------------------------------------ *
 * 6. Product image gallery
 *
 * shopify/pdp.php renders the product images as slides and
 * src/js/shopify-pdp-gallery.js decides which one is showing. This section is
 * only the presentation of that.
 *
 * The slides are a carousel, following the store's own product template, which
 * is configured media_presentation: carousel with a counter on the wide layout
 * and dots on a phone. There is no thumbnail strip: the arrows, the swipe and
 * the counter on the photograph are the whole gallery, as on the store.
 * ------------------------------------------------------------------ */

/*
 * The slides are a horizontal scroll-snap track, which is how the store's own
 * theme presents this product: swipe on a phone, arrows on a wide screen.
 * Swiping is the native scroller, so nothing here depends on a drag handler.
 *
 * The gate is a class the script sets on <html> as it loads. Without it the
 * slides stack into a column, which is every product photo visible and
 * scrollable in the ordinary way, rather than a track a visitor with no
 * JavaScript would have no controls for. The arrows and the position
 * indicator carry `hidden` in the markup and the script removes it, so they
 * never appear where they would not work.
 */
.single-product__slider {
	position: relative;
}

/*
 * Both the arrows and the indicator are printed with `hidden` and revealed by
 * the script. `hidden` is only a UA rule, so any display declaration on the
 * same element outranks it, and both of these set one. The attribute selector
 * puts the attribute back in charge, which is what keeps controls that do
 * nothing off a page where the script has not run.
 */
.single-product__slider-button[hidden],
.single-product__slider-status[hidden] {
	display: none;
}

/*
 * The slides are clones inside a <shopify-list-context>, and their parent is a
 * flex container. Without this the flex item is that element and the clones are
 * laid out inside it, which is one column of photographs rather than a track.
 * The component is a data wrapper with nothing to draw, so taking its box away
 * costs nothing, and it stops the layout depending on a display value the
 * vendor library sets.
 */
.single-product__media .single-product__main-image > shopify-list-context {
	display: contents;
}

.js-shopify-gallery .single-product__main-image {
	display: flex;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;

	/*
	 * The arrows and the dots are the position indicator, as on the store. A
	 * scrollbar under the photo would be a second one, drawn by the platform
	 * in a style nothing else on the page uses.
	 */
	scrollbar-width: none;
}

.js-shopify-gallery .single-product__main-image::-webkit-scrollbar {
	display: none;
}

/*
 * One frame for every slide, whatever shape the photograph is.
 *
 * A product's images are not all one shape - this one has 1:1, 3:2 and 2:3 in
 * the same set. Left to their natural heights the track is as tall as the
 * tallest of them, so a landscape photo sits above a band of empty space, the
 * page reflows as the visitor moves along, and the arrows, which are centred on
 * the track, fall below the picture rather than on it. A fixed frame with the
 * whole image fitted inside it is what the store's own product template does,
 * so the two read as the same gallery.
 */
.js-shopify-gallery .single-product__main-image .single-product__image-slide {
	display: flex;
	flex: 0 0 100%;
	align-items: center;
	justify-content: center;
	aspect-ratio: 1;
	scroll-snap-align: center;
}

/*
 * <shopify-media> injects its own stylesheet per image, and everything in it
 * carries !important: a cover crop, a full-width rule, and a background holding
 * the low-resolution placeholder it paints while the real file loads. Fitting
 * the image inside the frame means overriding the first two at the same weight,
 * and the placeholder has to go with the crop, or it shows through the bars.
 */
.js-shopify-gallery .single-product__main-image .single-product__image-slide img {
	width: 100% !important;
	height: 100%;
	background-image: none !important;
	object-fit: contain !important;
}

/* The track is focusable because it scrolls, so it needs a visible ring. */
.js-shopify-gallery .single-product__main-image:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 2px;
}

/*
 * Mirrors .collection__slider-button in the plugin's single-product.css, so
 * these read as the same control as the arrows on the recommendations row
 * further down the page. The one deliberate difference is the background: the
 * vendor's inherits, and these sit on top of a photograph.
 */
.single-product__slider-button {
	position: absolute;
	top: 50%;
	z-index: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	min-width: 2.25rem;
	min-height: 2.25rem;
	padding: 0.5rem 0.875rem;
	transform: translateY(-50%);
	border: 1px solid #ccc;
	border-radius: 50px;
	background: rgba(255, 255, 255, 0.9);
	color: #7d7d7d;
	font: inherit;
	line-height: 1;
	cursor: pointer;
	appearance: none;
	transition: opacity 0.3s ease;
}

.single-product__slider-button--prev {
	left: 0.5rem;
}

.single-product__slider-button--next {
	right: 0.5rem;
}

.single-product__slider-button:hover {
	background: #f3f3f3;
	color: #627059;
}

.single-product__slider-button:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 2px;
}

.single-product__slider-button:disabled {
	opacity: 0.3;
	cursor: not-allowed;
}

/*
 * A pill on the photograph, centred along the bottom edge, as the store draws
 * it. The background is what makes it readable: it sits on whatever the
 * photograph happens to be there, so a colour alone could land on anything.
 * White on this dark fill is about 10:1, and the pill travels with the frame
 * rather than reserving a row of its own below it.
 */
.single-product__slider-status {
	position: absolute;
	bottom: 0.75rem;
	left: 50%;
	z-index: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0.375rem;
	padding: 0.25rem 0.75rem;
	transform: translateX(-50%);
	border-radius: 50px;
	background: rgba(0, 0, 0, 0.6);
	color: #fff;
	font-size: 0.875rem;
	line-height: 1.4;
}

.single-product__slider-dots {
	display: none;
	gap: 0.375rem;
}

.single-product__slider-dot {
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: currentColor;
	opacity: 0.3;
}

.single-product__slider-dot.is-selected {
	opacity: 1;
}

/*
 * The store shows a counter on the wide layout and dots on a phone. 768px is
 * the breakpoint the plugin's own stylesheet already uses for this gallery.
 *
 * The arrows go with the counter. On a touch screen the swipe is the control
 * and an arrow sitting on top of the photo only covers part of it.
 *
 * The counter is taken off the screen rather than out of the document, because
 * it is the live region that says which image is showing. Here it is the only
 * thing that says so at all: the dots are decorative and the swipe is the only
 * control. display: none would stop it being announced.
 */
@media (max-width: 768px) {
	.single-product__slider-button {
		display: none;
	}

	.single-product__slider-counter {
		position: absolute;
		width: 1px;
		height: 1px;
		overflow: hidden;
		clip-path: inset(50%);
		white-space: nowrap;
	}

	.single-product__slider-dots {
		display: flex;
	}
}

@media (prefers-reduced-motion: reduce) {
	.js-shopify-gallery .single-product__main-image {
		scroll-behavior: auto;
	}
}

/*
 * The vendor's 420px is a ceiling here, not a width. Below that it is the
 * viewport that decides, which is what stops the gallery overflowing on a
 * phone. .single-product__media is already a centred flex column, so the
 * narrower box stays centred under the same rules as before.
 */
.single-product__media .single-product__main-image {
	width: 100%;
	max-width: 420px;
}

.single-product__media .single-product__main-image img {
	border-radius: 8px;
	max-width: 100%;
	height: auto;
}

/* ------------------------------------------------------------------ *
 * 7. Navigation cart entry
 *
 * Printed by inc/shopify.php into the primary menu and opened by
 * src/js/shopify-cart-link.js. The plugin's own cart toggle block is a block
 * theme's Block Hook, which never fires here, so this is the only way back
 * into the cart drawer on a classic theme.
 *
 * <shopify-store> is an unknown element to the browser until the Shopify
 * bundle defines it, which means it is display: inline and collapses the
 * button's box. Giving it a box of its own is what keeps the entry laid out
 * whether or not that bundle ever arrives.
 * ------------------------------------------------------------------ */

shopify-store.bumblechutes-cart {
	display: inline-flex;
	align-items: center;
}

/*
 * A button inside a nav-menu li inherits none of the link styling, because the
 * theme scopes that to `a`. These two rules mirror it: pine on the desktop bar,
 * ivory on the pine background of the open mobile menu.
 */
.main-navigation .nav-menu .bumblechutes-cart__toggle {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	margin: 0;
	padding: 0;
	border: 0;
	background: none;
	font: inherit;
	color: var(--pine);
	text-transform: capitalize;
	cursor: pointer;
	appearance: none;
}

.main-navigation.toggled .nav-menu .bumblechutes-cart__toggle {
	color: var(--ivory);
}

/*
 * The label underlines like a nav link; the icon and the count do not. Below
 * the desktop breakpoint only, since the label is not on screen above it.
 */
.bumblechutes-cart__toggle:hover .bumblechutes-cart__label,
.bumblechutes-cart__toggle:focus-visible .bumblechutes-cart__label {
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

.bumblechutes-cart__toggle:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 3px;
}

.bumblechutes-cart__icon {
	flex: none;
}

/*
 * The count is hidden until the script has seen a number above zero in it.
 * <shopify-context type="cart"> renders nothing at all before it resolves and
 * renders a bare 0 for an empty cart, so without this the navigation would
 * carry either a flash of nothing or a permanent zero.
 *
 * currentColor for the ring rather than a fixed colour, because this sits on
 * ivory in the desktop bar and on pine in the open mobile menu.
 */
.bumblechutes-cart__count {
	display: none;
	min-width: 1.5em;
	padding: 0 0.35em;
	border: 1px solid currentColor;
	border-radius: 999px;
	font-size: 0.75rem;
	line-height: 1.5;
	text-align: center;
}

.bumblechutes-cart__toggle--filled .bumblechutes-cart__count {
	display: inline-block;
}

/*
 * Desktop: the icon carries the entry on its own.
 *
 * The label stays in the markup rather than being removed, for two reasons. It
 * is what gives the button its accessible name, and the open mobile menu is a
 * vertical list of words where a lone glyph would read as a stray mark, so the
 * text is still on screen below this breakpoint.
 *
 * 1090px is where src/css/navigation.css switches from the toggled menu to the
 * horizontal bar.
 */
@media screen and (min-width: 1090px) {
	.main-navigation .nav-menu .bumblechutes-cart__label {
		position: absolute;
		width: 1px;
		height: 1px;
		padding: 0;
		margin: -1px;
		overflow: hidden;
		clip: rect(0, 0, 0, 0);
		white-space: nowrap;
		border: 0;
	}

	/*
	 * With the label out of the box the icon is 20px square, under the 24px
	 * minimum target size. The button is floored instead of the icon being
	 * redrawn, so it still matches the stroke weight of the vendor chevrons.
	 */
	.main-navigation .nav-menu .bumblechutes-cart__toggle {
		justify-content: center;
		min-width: 24px;
		min-height: 24px;
	}

	/* No visible text left to underline, so the affordance moves to the icon. */
	.main-navigation .nav-menu .bumblechutes-cart__toggle:hover .bumblechutes-cart__icon,
	.main-navigation .nav-menu .bumblechutes-cart__toggle:focus-visible .bumblechutes-cart__icon {
		opacity: 0.7;
	}
}

/* ------------------------------------------------------------------ *
 * 8. "From" price label on cards
 *
 * Cards show the range minimum, and shopify/product-card.php and
 * shopify/pdp.php put a "From" span in front of it. The Shopify component
 * sets `hidden` on that span when the product's minimum and maximum prices
 * are the same, so a single-price product renders the bare figure.
 *
 * The label is deliberately quieter than the figure. Without this it inherits
 * the price's own weight and size and reads as part of the number rather than
 * as a qualifier on it - the vendor sets the collection card price at 700 and
 * 17.6px, which is too loud for a word.
 *
 * `em` rather than `rem`, because the same span sits in four price contexts
 * the vendor sizes differently: 14px on the catalogue card, 17.6px inside
 * .shopify-collection-content, and 0.875rem on both recommendation cards.
 *
 * No margin. The recommendation card's `p` is `display: flex` with `gap: 8px`,
 * which already spaces the label, and on the catalogue card the whitespace
 * between the span and <shopify-money> in the markup supplies an ordinary
 * word space. A margin here would double one of the two.
 * ------------------------------------------------------------------ */

.product-card__price-from,
.single-product__price-from {
	font-weight: 400;
	font-size: 0.875em;
}

/*
 * The price is one unit and must never break across lines.
 *
 * .product-card__details is `display: flex` with `justify-content:
 * space-between`, so the title and the price share one row. Measured on the
 * kitchen collection grid: 323.3px of content width, a 224.3px title and a
 * 103.2px price, which overflows by 4.2px. Both items shrink by default, and
 * because the price can break at the space after "From" its min-content is
 * just the figure - so flex squeezed the price and left "From" stranded on a
 * line of its own above "$84.99".
 *
 * `nowrap` raises the price's min-content to the whole string, so flex takes
 * the width from the title instead, which is the item that can absorb it. It
 * cannot overflow the card: flex will not shrink an item below min-content,
 * and the title floors at 70px, leaving 253px for a price that needs 103px.
 *
 * This goes on the card price only. The PDP and modal prices at
 * shopify/pdp.php and shopify/modal.php sit beside a compareAtPrice
 * strike-through and have no "From" label, so they keep the vendor's wrapping.
 */
.product-card__price {
	white-space: nowrap;
}

/*
 * 13px, not 0.875em, inside the collection grid.
 *
 * collection.css:82 sets that context's price to 17.6px, where the ratio gives
 * a 15.4px label. That is louder than the word needs to be beside a 700-weight
 * figure, and 2.4px wider on the row that has the least room to spare.
 *
 * A fixed px rather than a smaller ratio, because the ratio also serves the
 * 14px catalogue card and the 0.875rem recommendation cards, where 0.75em
 * would put the label at 10.5px.
 */
.shopify-collection-content .product-card__price-from {
	font-size: 13px;
}

/*
 * Keep the title and the price apart.
 *
 * Once the price stops breaking it stays at full width, and on the longest
 * names the two items meet in the middle of the row: measured on the local VM
 * at 373.3px cards, "Camp Kitchen & Gear Care System" left 0.2px between the
 * last letter and the figure and "Complete Kitchen Care System" left 1.7px.
 * The old behaviour hid that collision behind the broken price. Before the
 * "From" label existed the price was ~41px narrower and the gap was never
 * this tight, so this is our collision to clear.
 *
 * 12px is free. Measured across all 12 cards at 8px, 12px and 16px, the same
 * two titles take a second line and no third card is affected, so the larger
 * value buys separation at no extra cost. The two long names then read as a
 * wrapped title beside a whole price, which is ordinary product-card layout.
 *
 * `column-gap`, not `gap`: the same element turns into a column flex container
 * at some grid widths - measured stacked at a 1440px viewport and on the
 * homepage, in a row at 1280px and 768px - and there the title and price are
 * already on separate lines, so a row gap would only push them further apart.
 */
.product-card__details {
	column-gap: 12px;
}

/*
 * The UA stylesheet's `[hidden] { display: none }` is the weakest rule there
 * is, so any later `display` on the span - a vendor update, a future flex
 * rule on the price - would leave "From" showing on a single-price product.
 * This pins it. `!important` is what makes it immune to that, and `hidden`
 * means hidden, so there is no case where something legitimately wants to
 * override it.
 */
.product-card__price-from[hidden],
.single-product__price-from[hidden] {
	display: none !important;
}

/*
 * The main price on the product page shows "From" until the customer chooses a
 * variant, and never again afterwards: from that point the figure is that one
 * variant's exact price and "From" in front of it would be false.
 *
 * The class is set by src/js/shopify-pdp-from-price.js and lives on the
 * wrapper, not the label. The label's own `hidden` is bound with
 * shopify-attr--hidden and the component recomputes it on every variant
 * change, so anything written onto the label itself is undone by the next
 * render. This is the half the component does not touch.
 */
.single-product__price.is-variant-chosen .single-product__price-from {
	display: none !important;
}
