/*
Theme Name: Frozen Meat SG
Theme URI: https://frozenmeat.sg
Template: kiosko
Author: Daniel Kam
Description: Child theme of Kiosko (Automattic WooCommerce block theme). Affiliate catalogue for frozenmeat.sg: every product is a WooCommerce External product linking out to Shopee (primary) and Lazada/RedMart (secondary). No cart, no checkout, no mini-cart. Reversible: switch back to the parent to undo.
Version: 0.1.38
Requires at least: 6.7
Tested up to: 7.0
Requires PHP: 7.2
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: frozenmeat
*/

/* -------------------------------------------------------------------------
 * Product cards (catalog mode).
 * White surface, 12px radius, hairline border, soft hover lift.
 * Applies to woocommerce/product-collection items carrying .fm-card.
 * ---------------------------------------------------------------------- */
.fm-card li.wp-block-woocommerce-product-template__responsive,
.fm-card .wc-block-product {
	background: var(--wp--preset--color--base);
	border: 1px solid var(--wp--preset--color--tertiary);
	border-radius: 12px;
	padding: 1rem;
	transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.fm-card li.wp-block-woocommerce-product-template__responsive:hover,
.fm-card .wc-block-product:hover {
	transform: translateY(-3px);
	box-shadow: 0 10px 24px rgba(24, 7, 16, 0.08);
}

/* Square product photos inside cards. */
.fm-card .wc-block-components-product-image img,
.fm-card .wp-block-woocommerce-product-image img {
	aspect-ratio: 1 / 1;
	object-fit: cover;
	border-radius: 8px;
	width: 100%;
}

/* Protein tag chip: post-terms product_cat rendered as a coloured chip. */
.fm-chip a,
.fm-chip {
	display: inline-block;
	font-size: 0.72rem;
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	text-decoration: none;
	color: var(--wp--preset--color--primary-dark);
	background: var(--wp--preset--color--primary-tint);
	border-radius: 999px;
	padding: 0.15rem 0.65rem;
}

.fm-chip a + a {
	margin-left: 0.35rem;
}

/* Muted "from S$X" reference price (also produced by the PHP price filter). */
.fm-from-price,
.fm-card .wp-block-woocommerce-product-price {
	color: var(--wp--preset--color--text-muted);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 400;
}

/* -------------------------------------------------------------------------
 * Badges. Amber is for deal flags ONLY, never buttons.
 * ---------------------------------------------------------------------- */
.fm-badge {
	display: inline-block;
	font-size: 0.72rem;
	font-weight: 600;
	letter-spacing: 0.04em;
	border-radius: 4px;
	padding: 0.15rem 0.5rem;
}

.fm-badge--deal {
	background: var(--wp--preset--color--amber);
	color: var(--wp--preset--color--contrast);
}

.fm-badge--halal {
	background: var(--wp--preset--color--success);
	color: var(--wp--preset--color--base);
}

.fm-badge--coldchain {
	background: var(--wp--preset--color--accent-blue);
	color: var(--wp--preset--color--base);
}

/* -------------------------------------------------------------------------
 * Retailer buy buttons.
 * ---------------------------------------------------------------------- */
/* Retailer buy buttons, in the marketplaces' own colours so each reads as
   "this takes you to that shop": Shopee orange (#EE4D2D), Lazada blue
   (#0F146D). Filled, square, full-width where they stack (the CTA caps the
   stack at 360px). One button shows when the product lists on one retailer,
   two when it lists on both. Logo marks drop in at affiliate signup from each
   programme's brand kit. */
/* The two buttons stack with a gap between them. */
.fm-buy-buttons {
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
}
.fm-buy-buttons .fm-btn-shopee,
.fm-buy-buttons .fm-btn-lazada,
.fm-btn-shopee,
.fm-btn-lazada {
	display: block;
	width: 100%;
	text-align: center;
	padding: 16px 24px;
	font-weight: 700;
	border: 0;
	border-radius: 0;
	color: #fff;
	text-decoration: none;
}
/* Sticky mobile buy bar: keep the same gap between its two buttons. */
.fm-buybar {
	gap: 0.5rem;
}
.fm-buy-buttons .fm-btn-shopee,
.fm-btn-shopee {
	background: #ee4d2d;
}
.fm-buy-buttons .fm-btn-shopee:hover,
.fm-btn-shopee:hover {
	background: #d8431f;
	color: #fff;
}
.fm-buy-buttons .fm-btn-lazada,
.fm-btn-lazada {
	background: #0f146d;
}
.fm-buy-buttons .fm-btn-lazada:hover,
.fm-btn-lazada:hover {
	background: #0a0e4f;
	color: #fff;
}

/* Any purple outline button (e.g. the hero secondary CTA) hovers purple,
   not the parent's contrast ink. */
.wp-block-button.is-style-outline > .wp-block-button__link.has-secondary-color:not(.has-background):hover {
	background: var(--wp--preset--color--secondary);
	border-color: var(--wp--preset--color--secondary);
	color: var(--wp--preset--color--base);
}

/* External-product buy button: match the primary button. Two wrappers exist:
   the theme's external.php override renders inside div.cart, and the
   frozenmeat-affiliate plugin (which supersedes it via
   woocommerce_external_add_to_cart) renders inside p.fm-buy. Cover both, or
   the primary CTA renders as a bare text link. */
.single-product .cart .single_add_to_cart_button,
.single-product .fm-buy .single_add_to_cart_button {
	display: block;
	width: 100%;
	text-align: center;
	background: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--base);
	border-radius: 0;
	padding: 16px 24px;
	font-weight: 700;
}

.single-product .cart .single_add_to_cart_button:hover,
.single-product .fm-buy .single_add_to_cart_button:hover {
	background: var(--wp--preset--color--primary-dark);
	color: var(--wp--preset--color--base);
}

/* -------------------------------------------------------------------------
 * Archive filter rail (WC 10.7+ product-filters collection).
 *
 * WooCommerce prints its own block styles AFTER this file, but its sheet is
 * written entirely in :where() (zero specificity), so a single
 * .wc-block-product-filters prefix wins every override below without
 * !important. Keep the prefix: drop it and WC's sheet takes back over.
 *
 * Ported from the asiamannequin rail, retoned to frozenmeat: blue accent
 * instead of champagne, Rubik inherited from theme.json rather than pinned.
 * There is deliberately no price or rating filter to style; this catalogue
 * shows no live price and hosts no reviews.
 * ---------------------------------------------------------------------- */
.wc-block-product-filters a {
	text-decoration: none;
}

/* Checkbox list: tight vertical rhythm, quiet labels. */
.wc-block-product-filters .wc-block-product-filter-checkbox-list__items {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.wc-block-product-filters .wc-block-product-filter-checkbox-list__label {
	display: flex;
	align-items: center;
	gap: 0.55rem;
	font-size: 0.85rem;
	line-height: 1.4;
	color: var(--wp--preset--color--contrast);
	cursor: pointer;
	/* 44px tap target on touch without stretching the desktop rhythm. */
	min-height: 32px;
}

.wc-block-product-filters .wc-block-product-filter-checkbox-list__label:hover .wc-block-product-filter-checkbox-list__text {
	color: var(--wp--preset--color--primary);
}

/* Hierarchy. The catalogue nests cuts under proteins (Beef > Beef Ribeye),
   so top-level terms read as group heads and children indent one step.
 *
 * Do NOT select on .has-depth-1 / .has-depth-2 here. Those classes live only
 * in WooCommerce's EDITOR bundle (product-filter-checkbox-list.js); the
 * front end never emits them. Verified on WC 10.9.4: the PHP renderer
 * ProductFilterCheckboxList.php writes no depth class, and asiamannequin
 * production serves zero "has-depth" in its rendered HTML, so the equivalent
 * rules there match nothing and every term renders bold. A
 * :not([class*="has-depth"]) selector is worse than useless: it matches
 * EVERY item and bolds the whole list.
 *
 * Depth IS server-rendered, inside each item's data-wp-context JSON
 * ({"item":{...,"depth":1,"parent":16}}), so select on that. It is correct
 * on first paint and needs no JS. If WooCommerce ever reformats that JSON
 * these selectors stop matching and the list degrades to a flat, evenly
 * weighted list, which is the safe direction to fail in.
 */
.wc-block-product-filters .wc-block-product-filter-checkbox-list__item:not([data-wp-context*='"depth":']) .wc-block-product-filter-checkbox-list__text {
	font-weight: 700;
	letter-spacing: 0.02em;
	font-size: 0.82rem;
}

.wc-block-product-filters .wc-block-product-filter-checkbox-list__item[data-wp-context*='"depth":1'] {
	padding-inline-start: 1rem;
}

.wc-block-product-filters .wc-block-product-filter-checkbox-list__item[data-wp-context*='"depth":1'] .wc-block-product-filter-checkbox-list__text {
	color: var(--wp--preset--color--text-muted);
}

.wc-block-product-filters .wc-block-product-filter-checkbox-list__item[data-wp-context*='"depth":2'] {
	padding-inline-start: 2rem;
}

/* Checkbox: blue when checked, white tick. WC paints the box via
   input[type=checkbox].__input plus a 10% currentColor :before wash on the
   wrapper, and exposes colour hooks as custom properties: use both. */
.wc-block-product-filters {
	--wc-product-filter-checkbox-list-option-element: var(--wp--preset--color--base);
	--wc-product-filter-checkbox-list-option-element-border: var(--wp--preset--color--contrast);
}

.wc-block-product-filters .wc-block-product-filter-checkbox-list__input-wrapper::before {
	display: none;
}

.wc-block-product-filters input[type="checkbox"].wc-block-product-filter-checkbox-list__input {
	width: 16px;
	height: 16px;
	min-width: 16px;
	border-radius: 4px;
	background: var(--wp--preset--color--base);
	border: 1px solid var(--wp--preset--color--tertiary);
}

.wc-block-product-filters input[type="checkbox"].wc-block-product-filter-checkbox-list__input:checked {
	background: var(--wp--preset--color--primary);
	border-color: var(--wp--preset--color--primary);
}

.wc-block-product-filters input[type="checkbox"].wc-block-product-filter-checkbox-list__input:focus-visible {
	outline: 2px solid var(--wp--preset--color--primary);
	outline-offset: 1px;
}

.wc-block-product-filters .wc-block-product-filter-checkbox-list__mark {
	color: var(--wp--preset--color--base);
}

/* Counts in muted grey. */
.wc-block-product-filters .wc-block-product-filter-checkbox-list__count {
	color: var(--wp--preset--color--text-muted);
	font-size: 0.78rem;
}

/* Show more / show less: quiet text control, blue on hover. */
.wc-block-product-filters .wc-block-product-filter-checkbox-list__show-more,
.wc-block-product-filters .wc-block-product-filter-checkbox-list__show-less {
	background: none;
	border: none;
	padding: 0;
	margin-top: 0.6rem;
	color: var(--wp--preset--color--text-muted);
	font-size: 0.78rem;
	text-decoration: underline;
	cursor: pointer;
}

.wc-block-product-filters .wc-block-product-filter-checkbox-list__show-more:hover,
.wc-block-product-filters .wc-block-product-filter-checkbox-list__show-less:hover {
	color: var(--wp--preset--color--primary);
}

/* Active filter chips: pill to match the card chips, blue on hover. */
.wc-block-product-filters .wc-block-product-filter-removable-chips__item {
	border: 1px solid var(--wp--preset--color--tertiary);
	border-radius: 999px;
	background: var(--wp--preset--color--base);
	font-size: 0.78rem;
}

.wc-block-product-filters .wc-block-product-filter-removable-chips__item:hover {
	border-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--primary);
}

/* Clear filters: quiet text button, blue on hover. */
.wc-block-product-filters .wc-block-product-filter-clear-button .wp-block-button__link {
	background: transparent;
	color: var(--wp--preset--color--text-muted);
	border: none;
	padding: 0.25rem 0;
	font-size: 0.78rem;
	text-decoration: underline;
}

.wc-block-product-filters .wc-block-product-filter-clear-button .wp-block-button__link:hover {
	color: var(--wp--preset--color--primary);
	background: transparent;
}

/* -------------------------------------------------------------------------
 * The 601-781px band.
 *
 * Two breakpoints disagree by design and neither is ours to move: core's
 * columns stack at <=781px, while WooCommerce's filter drawer only engages
 * at <=600px. Between them the rail is neither a sidebar nor a drawer, it
 * is a full-width block sitting above the grid.
 *
 * We keep the stack (see NOTES.md) rather than force the drawer up to
 * 781px, and make it read as a deliberate filter card instead of a stray
 * 30-item column shoving the products off the fold: the term list flows
 * into responsive columns and the whole rail gets a card surface.
 * ---------------------------------------------------------------------- */
@media (min-width: 601px) and (max-width: 781px) {
	.fm-archive__rail {
		border: 1px solid var(--wp--preset--color--tertiary);
		border-radius: 12px;
		padding: 1.25rem;
		background: var(--wp--preset--color--surface-tint);
		margin-bottom: var(--wp--preset--spacing--50);
	}

	/* Terms flow across the width instead of down it. Nested cuts stay with
	   their protein because column-break avoidance keeps each item whole. */
	.fm-archive__rail .wc-block-product-filter-checkbox-list__items {
		display: block;
		column-count: 3;
		column-gap: 1.5rem;
	}

	.fm-archive__rail .wc-block-product-filter-checkbox-list__item {
		break-inside: avoid;
		margin-bottom: 0.5rem;
	}
}

/* -------------------------------------------------------------------------
 * Archive pagination: 44px minimum tap targets.
 * ---------------------------------------------------------------------- */
.fm-pagination .wp-block-query-pagination-numbers a,
.fm-pagination .wp-block-query-pagination-numbers .current,
.fm-pagination .wp-block-query-pagination-previous,
.fm-pagination .wp-block-query-pagination-next {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 44px;
	min-height: 44px;
	padding: 0.25rem 0.75rem;
	border-radius: 8px;
	text-decoration: none;
}

.fm-pagination .wp-block-query-pagination-numbers .current {
	background: var(--wp--preset--color--primary-tint);
	color: var(--wp--preset--color--primary-dark);
	font-weight: 600;
}

/* -------------------------------------------------------------------------
 * Protein category tiles: the linked title must read white on the dim
 * overlay. WooCommerce only forces color:inherit on post-title and
 * category-title anchors, so a plain heading's link takes the theme's body
 * link colour (primary blue) and is unreadable on the photo. Inherit the
 * heading's white and drop the underline; the shadow keeps it legible over
 * bright parts of the photo at dimRatio 30.
 * ---------------------------------------------------------------------- */
.wp-block-woocommerce-featured-category .wp-block-heading {
	text-shadow: 0 1px 10px rgba(19, 2, 17, 0.65);
}

.wp-block-woocommerce-featured-category .wp-block-heading a {
	color: inherit;
	text-decoration: none;
}

.wp-block-woocommerce-featured-category .wp-block-heading a:hover,
.wp-block-woocommerce-featured-category .wp-block-heading a:focus {
	color: inherit;
	text-decoration: underline;
}

/* -------------------------------------------------------------------------
 * Protein category grid: 3-up desktop and tablet (2x3 with 6 tiles),
 * 2-up phone. The block uses grid layout columnCount 3.
 * ---------------------------------------------------------------------- */
@media (max-width: 1024px) {
	.fm-protein-grid {
		grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
	}
}

@media (max-width: 600px) {
	.fm-protein-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
	}
}

/* -------------------------------------------------------------------------
 * Sticky bottom buy bar on mobile (printed by functions.php on single
 * product pages). Hidden on desktop.
 * ---------------------------------------------------------------------- */
.fm-buybar {
	display: none;
}

@media (max-width: 782px) {
	.fm-buybar {
		display: flex;
		gap: 0.5rem;
		position: fixed;
		bottom: 0;
		left: 0;
		right: 0;
		z-index: 90;
		padding: 0.6rem max(0.75rem, env(safe-area-inset-left)) max(0.6rem, env(safe-area-inset-bottom));
		background: var(--wp--preset--color--base);
		border-top: 1px solid var(--wp--preset--color--tertiary);
		box-shadow: 0 -6px 18px rgba(24, 7, 16, 0.08);
	}

	.fm-buybar a {
		flex: 1;
		display: inline-flex;
		align-items: center;
		justify-content: center;
		min-height: 44px;
		border-radius: 0;
		font-weight: 700;
		text-decoration: none;
	}

	.fm-buybar .fm-buybar-shopee {
		background: #ee4d2d;
		color: #fff;
	}

	.fm-buybar .fm-buybar-lazada {
		background: #0f146d;
		color: #fff;
	}

	/* Keep the bar clear of the footer content. */
	.single-product .wp-site-blocks {
		padding-bottom: 68px;
	}

	/* iOS zooms the viewport when a focused input computes under 16px and
	   never zooms back out. WooCommerce's product search lands at ~12.7px via
	   has-small-font-size, whose preset class carries !important in core's
	   global styles, so matching !important here is required, not lazy. */
	input[type="text"],
	input[type="search"],
	input[type="email"],
	input[type="tel"],
	input[type="url"],
	input[type="number"],
	input[type="password"],
	select,
	textarea {
		font-size: 16px !important;
	}
}

/* -------------------------------------------------------------------------
 * Affiliate disclosure lines.
 * ---------------------------------------------------------------------- */
.fm-disclosure {
	color: var(--wp--preset--color--text-muted);
	font-size: var(--wp--preset--font-size--x-small);
}

/* The footer is Pansy Purple now, and the muted grey (#6B5F66) that reads
   correctly on the white product page collapses to 1.80:1 on purple. Scope a
   light tone to the footer only; the product-page disclosure keeps the grey. */
footer .fm-disclosure {
	color: rgba(255, 255, 255, 0.78);
}

/* The plugin's site-wide wp_footer disclosure line prints after the footer
   template part. Unstyled it floats as stray text on white below the dark
   footer; style it as a deliberate legal sub-footer band instead. */
.fm-disclosure--footer {
	background: var(--wp--preset--color--secondary);
	color: var(--wp--preset--color--base);
	opacity: 1;
	text-align: center;
	padding: 0.75rem 1.5rem 1rem;
	margin: 0;
}

.fm-disclosure--footer small {
	opacity: 0.65;
	font-size: var(--wp--preset--font-size--x-small);
}

/* Full-bleed colour bands must butt against each other and against the
   footer. The root flow layout and the main group both add a block gap
   between siblings, which shows as a white stripe between a cream FAQ
   band and the blue CTA band. Each band carries its own padding, so the
   gap is pure defect. Scoped to the front page: inner pages still want
   the normal rhythm. */
.home .wp-site-blocks > * + *,
.home main.wp-block-group > * + * {
	margin-block-start: 0;
}

/* FAQ accordion, ported from asiamannequin's am-faq-item (assets/tms.css).
   Hairline-separated rows, a +/- affordance instead of the browser's
   disclosure triangle, answer in muted text. Same shape on both sites so the
   two builds read as one hand. */
.fm-faq-item {
	border-top: 1px solid var(--wp--preset--color--border);
	padding: 1.15rem 0;
	margin: 0;
}
.fm-faq-item:last-of-type {
	border-bottom: 1px solid var(--wp--preset--color--border);
}
.fm-faq-item > summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1.5rem;
	cursor: pointer;
	list-style: none;
	font-weight: 700;
	font-size: 0.95rem;
	line-height: 1.4;
}
.fm-faq-item > summary::-webkit-details-marker {
	display: none;
}
.fm-faq-item > summary::marker {
	content: "";
}
.fm-faq-item > summary::after {
	content: "+";
	flex: 0 0 auto;
	font-weight: 400;
	font-size: 1.25rem;
	line-height: 1;
	color: var(--wp--preset--color--secondary);
	transition: transform 0.15s ease;
}
.fm-faq-item[open] > summary::after {
	content: "\2013";
}
.fm-faq-item > summary:hover,
.fm-faq-item > summary:focus-visible {
	color: var(--wp--preset--color--secondary);
}
.fm-faq-item > *:not(summary) {
	margin-top: 0.85rem;
	margin-bottom: 0;
	max-width: 72ch;
	color: var(--wp--preset--color--text-muted);
}
.fm-faq-item > *:not(summary) a {
	color: inherit;
	text-decoration: underline;
	text-underline-offset: 0.2em;
}
.fm-faq-item > *:not(summary) a:hover {
	color: var(--wp--preset--color--secondary);
}

/* Footer wordmark: the real logo, sized down. The site-title text read as a
   second, competing wordmark next to the header's logo. */
.fm-footer-logo img {
	width: 112px;
	height: auto;
}

/* -------------------------------------------------------------------------
 * Product CTA band (single product, after the description). Full-width cream
 * band; the primary buy button is #FF9900, the logo orange, so the buy action
 * pops on the soft background. Secondary (Lazada) keeps the default purple
 * outline. !important is scoped to .fm-product-cta and overrides the tangled
 * default button specificity without leaking anywhere else.
 * ---------------------------------------------------------------------- */
/* Full-bleed. On single-product WooCommerce wraps the content in a
   .woocommerce div and the kiosko parent constrains <main> to the wide size
   with auto side margins, so it is narrower than the viewport and WP's own
   alignfull can only reach main's edge (unlike the homepage, whose main is a
   direct child of .wp-site-blocks). Let main fill its .woocommerce wrapper,
   which spans the page content width, so the align:full band breaks out to
   the true edge natively, with no 100vw scrollbar hacks. */
.single-product main.wp-block-group {
	max-width: none;
	margin-left: 0;
	margin-right: 0;
}
/* Absorb any sub-pixel/scrollbar horizontal overflow from full-bleed bands.
   clip (not hidden/auto) does not create a scroll container, so the sticky
   header keeps working. Verified: header still travels on all templates. */
.wp-site-blocks {
	overflow-x: clip;
}

/* -------------------------------------------------------------------------
 * Marketplace listing cues (Lazada/Shopee feel), IN OUR BRAND.
 * Rendered by functions.php section 9. No live price anywhere here.
 * ---------------------------------------------------------------------- */

/* Image-badge overlay. The positioning context is .fm-card-media (cards) or
   .fm-gallery-col (single gallery). Badges never eat gallery/link clicks. */
.fm-card-media,
.fm-gallery-col {
	position: relative;
}

.fm-img-badges {
	position: absolute;
	top: 0.6rem;
	left: 0.6rem;
	right: 0.6rem;
	z-index: 3;
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 0.4rem;
	pointer-events: none;
}

/* Rounded pill badges. Frozen = cold-chain accent blue; pack = house purple,
   so the pair reads as ours (blue + purple), never Lazada pink. */
.fm-ibadge {
	display: inline-flex;
	align-items: center;
	gap: 0.28rem;
	font-size: 0.68rem;
	font-weight: 600;
	line-height: 1.2;
	letter-spacing: 0.01em;
	padding: 0.24rem 0.55rem;
	border-radius: 999px;
	color: #fff;
	white-space: nowrap;
	box-shadow: 0 1px 4px rgba(19, 2, 17, 0.18);
}

.fm-ibadge--frozen {
	background: var(--wp--preset--color--accent-blue);
}

.fm-ibadge__snow {
	flex: 0 0 auto;
	display: block;
}

.fm-ibadge--pack {
	background: var(--wp--preset--color--secondary);
	margin-left: auto;
}

/* Single gallery: keep the badges clear of the gallery's own fullscreen/zoom
   affordance, which sits bottom-right, and align to the large image only. */
.fm-gallery-col .fm-img-badges {
	top: 0.85rem;
	left: 0.85rem;
	right: 0.85rem;
}

/* "Sold by <brand>  ·  More from <brand>" line, under the product title. */
.fm-sold-by {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
	margin: 0.6rem 0 0;
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--text-muted);
}

.fm-sold-by__name {
	font-weight: 600;
	color: var(--wp--preset--color--secondary);
	text-decoration: none;
}

.fm-sold-by__name:hover {
	text-decoration: underline;
}

.fm-sold-by__sep {
	color: var(--wp--preset--color--tertiary);
}

.fm-sold-by__more {
	color: var(--wp--preset--color--primary);
	text-decoration: none;
}

.fm-sold-by__more:hover {
	text-decoration: underline;
}

/* Specifications: clean label:value rows, scannable, hairline separated. */
.fm-spec {
	margin-top: var(--wp--preset--spacing--50);
	border: 1px solid var(--wp--preset--color--tertiary);
	border-radius: 12px;
	background: var(--wp--preset--color--surface-tint);
	padding: 0.35rem 1.15rem;
}

.fm-spec__title {
	margin: 0.75rem 0 0.5rem;
	font-size: 0.72rem;
	font-weight: 700;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--wp--preset--color--secondary);
}

.fm-spec__row {
	display: flex;
	gap: 1rem;
	padding: 0.55rem 0;
	border-top: 1px solid var(--wp--preset--color--tertiary);
	font-size: var(--wp--preset--font-size--small);
	line-height: 1.4;
}

.fm-spec__label {
	flex: 0 0 38%;
	max-width: 38%;
	color: var(--wp--preset--color--text-muted);
}

.fm-spec__value {
	flex: 1 1 auto;
	font-weight: 500;
	color: var(--wp--preset--color--contrast);
}

/* Lazada-ish card polish: tighten the title/spec line rhythm on the archive
   and related grids. The vertical card stays; only the spacing tightens. */
.fm-card .wp-block-post-title {
	line-height: 1.3;
}

.fm-card .wp-block-post-excerpt {
	margin-top: 0.15rem;
}

.fm-card .wp-block-post-excerpt p {
	margin: 0;
}


/* -------------------------------------------------------------------------
 * Post head: title left, scrolling food wall right.
 *
 * Replaces the old per-post Canva hero. That image baked the title into an
 * 851x315 canvas which the template then upscaled into a 420px-tall wide slot,
 * so every post showed its title twice and the second one was soft.
 *
 * This is repeatable: no per-post artwork, and the same six photos serve every
 * post. The Canva images stay as the featured image so og:image share cards
 * still work, which is the one place a baked-in title is correct.
 * ---------------------------------------------------------------------- */

/* The band deliberately uses flow layout, not WordPress's constrained layout.
 * Constrained applies `max-width: contentSize` and auto margins to EVERY child,
 * which sized the absolutely-positioned wall to a 620px box centred at x=410
 * instead of covering the band, and gave the breadcrumb and the title different
 * max-widths (1340 vs 620) so they started at different x. Widths are set here
 * instead, where they can be reasoned about. */
.fm-posthead {
	position: relative;
	overflow: hidden; /* masks the rotated plane back to the band's rectangle */
	background: var(--wp--preset--color--ink-deep);
	display: flex;
	align-items: center;
	min-height: 420px;
	/* Normal padding: the header is solid and stacked above on posts and on the
	 * blog index. Only the front page overlays, and its cover carries its own
	 * clearance in patterns/hero.php. */
	padding-top: clamp(48px, 5vw, 72px);
	padding-bottom: clamp(48px, 5vw, 72px);
}

/* The wall is the band's background, not a column in it. */
.fm-posthead .fm-wall {
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	z-index: 0;
	max-width: none; /* nothing may constrain a background */
	margin: 0;
	background: var(--wp--preset--color--ink-deep);
}

/* Scrim. Heaviest on the left where the text sits, clearing toward the right so
 * the food still reads. Without this, white type over a moving photo wall fails
 * contrast against whichever tile happens to be pale. */
.fm-posthead::after {
	content: "";
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	z-index: 1;
	background: linear-gradient(
		90deg,
		rgba(19, 2, 17, 0.95) 0%,
		rgba(19, 2, 17, 0.92) 38%,
		rgba(19, 2, 17, 0.62) 72%,
		rgba(19, 2, 17, 0.42) 100%
	);
}

/* Text sits above both: a centred block with left-aligned text inside (the
 * look Daniel approved). The centring comes from shrink-wrap: the parent is
 * flex, this is a flex item with auto margins and NO width:100%, so it wraps
 * to its widest child (capped at 680 below) and centres in the band.
 * width:fit-content states the same intent without depending on the parent
 * staying flex. */
.fm-posthead__inner {
	position: relative;
	z-index: 2;
	width: fit-content;
	max-width: 1340px;
	margin: 0 auto;
	padding-left: clamp(20px, 3vw, 30px);
	padding-right: clamp(20px, 3vw, 30px);
}

/* Breadcrumb, title and lede share one left edge and one measure. This is the
 * whole point of dropping constrained layout: every child gets the same rule. */
.fm-posthead__inner > * {
	max-width: 680px;
	margin-left: 0;
	margin-right: auto;
	text-align: left;
}

/* Breadcrumbs invert on the dark band: the default primary blue is unreadable
 * here and the current-page text is near-black. */
.fm-posthead__inner .wc-block-breadcrumbs,
.fm-posthead__inner .wc-block-breadcrumbs a {
	color: rgba(255, 255, 255, 0.72);
}

.fm-posthead__inner .wc-block-breadcrumbs a:hover {
	color: var(--wp--preset--color--base);
}

@media (max-width: 781px) {
	.fm-posthead {
		min-height: 0;
	}

	/* Lighten the horizontal fade: at phone width the text crosses the whole
	 * band, so a right-clearing gradient would strand type on bare photo. */
	.fm-posthead::after {
		background: linear-gradient(
			180deg,
			rgba(19, 2, 17, 0.88) 0%,
			rgba(19, 2, 17, 0.93) 100%
		);
	}
}

/* The rotated plane, overscanned on every side so rotation never exposes a
 * corner of the container. -25% comfortably covers the ~23% a 12deg turn needs. */
.fm-wall__plane {
	position: absolute;
	top: -25%;
	right: -25%;
	bottom: -25%;
	left: -25%;
	display: flex;
	align-items: flex-start; /* REQUIRED: stretch would set column height to the
	                          * plane's, and the -50% loop below measures the
	                          * column, so the seam would drift. */
	gap: 10px;
	transform: rotate(-12deg);
	transform-origin: center center;
}

.fm-wall__col {
	flex: 1 1 0;
	display: flex;
	flex-direction: column;
	animation: fm-wall-scroll 44s linear infinite;
	will-change: transform;
}

.fm-wall__col--down {
	animation-direction: reverse;
}

/* Coprime-ish durations: the columns drift out of phase instead of marching in
 * step, which is what stops four copies of the same six photos reading as a grid. */
.fm-wall__col--s0 {
	animation-duration: 44s;
}

.fm-wall__col--s1 {
	animation-duration: 57s;
}

.fm-wall__col--s2 {
	animation-duration: 68s;
}

.fm-wall__col--s3 {
	animation-duration: 51s;
}

.fm-wall__item {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 1 / 1;
	object-fit: cover;
	border-radius: 8px;
	/* Margin, not the parent's gap: with N images duplicated to 2N, the column
	 * height must be exactly 2N*(h+m) for translateY(-50%) to land image N on
	 * image 0. A flex gap applies between items only (2N-1 of them), leaving the
	 * loop half a gap short and visibly jumping once per cycle. */
	margin-bottom: 10px;
}

/* Each column renders its images twice, so -50% returns to an identical frame. */
@keyframes fm-wall-scroll {
	from {
		transform: translateY(0);
	}
	to {
		transform: translateY(-50%);
	}
}

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

/* -------------------------------------------------------------------------
 * Overlay header, after asiamannequin: no background, sitting over the hero so
 * the artwork runs to the top of the page. Not sticky, by decision.
 *
 * SCOPED ON PURPOSE. Only the two templates that open on a dark hero get it:
 * the front page (cover) and single posts (.fm-posthead). Applying it site-wide
 * would put the header's white type on the white shop, archive, search, page
 * and product templates, which have no hero behind them.
 *
 * This reverses the deviation recorded in patterns/hero.php ("the header was
 * always solid #701356, never transparent over the hero"), which was a faithful
 * restoration of the 2025 original. Daniel called the overlay on 2026-07-17.
 * ---------------------------------------------------------------------- */
.home .wp-site-blocks {
	position: relative;
}

.home .wp-site-blocks > header {
	position: absolute;
	top: 0;
	right: 0;
	left: 0;
	z-index: 20;
}

/* Two things make this rule look stranger than it is.
 *
 * 1. The purple is on the INNER header. `wp:template-part {"tagName":"header"}`
 *    wraps header.html in its own <header class="wp-block-template-part">, so
 *    the markup is header > header: the outer is the positioning hook and never
 *    had a background, the inner carries .has-secondary-background-color.
 *    Targeting the outer looks right, changes nothing, and still reports
 *    "transparent" to getComputedStyle.
 *
 * 2. !important is required, not sloppiness. Core emits preset colour utilities
 *    as `:root :where(.has-secondary-background-color){background-color:...
 *    !important}`. No selector can out-specify an !important declaration, so
 *    this is the only way short of dropping backgroundColor from header.html
 *    and losing the solid header everywhere else.
 */
.home .wp-site-blocks > header > .wp-block-group.has-background {
	background-color: transparent !important;
}

/* The front-page cover clears the header with its own padding, set in
 * patterns/hero.php: the cover carries an inline padding-top, which no rule
 * here could override. */

/* -------------------------------------------------------------------------
 * The article's commercial argument closes on the alignfull CTA band. Optional
 * fine-print footnotes may follow it as the absolute final block. The single
 * template removes main's bottom margin and the root blockGap so whichever
 * block is final meets the footer cleanly.
 *
 * A CTA's trailing margin is an INLINE style that no stylesheet rule can
 * outrank. Remove it from each post instead of fighting it with !important;
 * otherwise it creates a false gap before final footnotes or the footer.
 * ---------------------------------------------------------------------- */
.fm-single-main {
	margin-bottom: 0;
}

.single-post .wp-site-blocks > footer {
	margin-block-start: 0;
}

/* -------------------------------------------------------------------------
 * Footnotes: optional fine print at the absolute end, after the CTA band.
 * ---------------------------------------------------------------------- */
.wp-block-footnotes {
	margin-top: var(--wp--preset--spacing--60);
	padding-top: 1.25rem;
	border-top: 1px solid var(--wp--preset--color--tertiary);
	font-size: 0.78rem;
	line-height: 1.65;
	color: var(--wp--preset--color--text-muted);
}

.wp-block-footnotes li {
	margin-bottom: 0.5rem;
}

.wp-block-footnotes a {
	color: var(--wp--preset--color--secondary);
	text-underline-offset: 2px;
}

.wp-block-footnotes a:hover {
	color: var(--wp--preset--color--secondary);
}

/* Reference markers in the body: small, raised, not competing with the prose. */
sup.fn {
	font-size: 0.68em;
	font-weight: 600;
	line-height: 0;
}

sup.fn a {
	text-decoration: none;
	color: var(--wp--preset--color--secondary);
	padding: 0 0.1em;
}

sup.fn a:hover {
	text-decoration: underline;
}

/* -------------------------------------------------------------------------
 * Blog index cards (home.html).
 *
 * Text only, no featured image, on purpose: the Canva post artwork bakes the
 * title into the image, so a card showing both would repeat the title exactly
 * the way the old post hero did. The artwork stays as the og:image, where a
 * baked-in title is correct.
 * ---------------------------------------------------------------------- */
.fm-postcard {
	background: var(--wp--preset--color--base);
	border: 1px solid var(--wp--preset--color--tertiary);
	border-radius: 12px;
	height: 100%;
	transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.fm-postcard:hover {
	transform: translateY(-3px);
	box-shadow: 0 10px 24px rgba(24, 7, 16, 0.08);
}

.fm-postcard .wp-block-post-title a {
	text-decoration: none;
	color: inherit;
}

.fm-postcard .wp-block-post-title a:hover {
	color: var(--wp--preset--color--secondary);
}

/* post-terms renders the category as plain links; .fm-chip turns them into the
 * same chip the product cards use. */
.fm-postcard .fm-chip a {
	text-decoration: none;
}
