﻿/*
Theme Name: Woolboo
Theme URI: https://www.woolboo.com
Description: A warm, lightweight WooCommerce child theme for Woolboo.
Author: Woolboo
Author URI: https://www.woolboo.com
Template: twentytwentyfive
Version: 0.4.2
Requires at least: 6.7
Tested up to: 6.8
Requires PHP: 8.2
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: woolboo
*/

:root {
	--woolboo-cream: #faf7f0;
	--woolboo-white: #fffdfc;
	--woolboo-charcoal: #2f332e;
	--woolboo-sage: #71806a;
	--woolboo-sage-dark: #4e5e49;
	--woolboo-terracotta: #b96f52;
	--woolboo-blush: #d8b7a8;
	--woolboo-oat: #e7ded0;
	/* Pressed and hover steps for the primary green, one shade either side. */
	--woolboo-sage-deeper: #3f4d3b;
	--woolboo-shadow: rgba(47, 51, 46, 0.09);
}

body {
	background: var(--woolboo-cream);
	color: var(--woolboo-charcoal);
}

html,
body {
	overflow-x: clip;
}

a {
	text-underline-offset: 0.18em;
}

.wp-element-button,
.wp-block-button__link,
.woocommerce a.button,
.woocommerce button.button,
.woocommerce input.button {
	border-radius: 999px;
	font-weight: 650;
	letter-spacing: 0.01em;
	padding: 0.85rem 1.35rem;
}

/*
 * Nothing on the site moved or responded to being pressed. Every button was
 * a flat rectangle of colour with no hover, no press and no transition — the
 * whole stylesheet contained two `:hover` rules and no `:active` at all, so
 * the primary call to action gave no feedback that a click had registered.
 *
 * Durations are deliberately short. 120ms is under the threshold where a
 * state change reads as a delay rather than a response, and the press uses a
 * 1px translate rather than a scale so text never resamples mid-press.
 */
.wp-element-button,
.wp-block-button__link,
.woocommerce a.button,
.woocommerce button.button,
.woocommerce input.button,
.wc-block-components-button {
	transition:
		background-color 120ms ease,
		border-color 120ms ease,
		box-shadow 120ms ease,
		transform 120ms ease;
}

/*
 * Darkening by filter rather than by setting a background colour. The site
 * has three different transparent buttons — the outline style, the mini-cart
 * "View my cart" and the cart drawer's close control — sharing no class
 * between them, and painting a blanket dark green behind them would have put
 * charcoal text on a dark green fill. Brightness is derived from whatever the
 * button already is, so it cannot invert a contrast ratio.
 */
.wp-element-button:hover,
.wp-block-button__link:hover,
.woocommerce a.button:hover,
.woocommerce button.button:hover,
.woocommerce input.button:hover,
.wc-block-components-button:hover {
	filter: brightness(0.93);
}

/* An outline button has no fill to darken, so it gains a faint one. */
.wp-block-button.is-style-outline .wp-block-button__link:hover,
.wc-block-components-button.is-style-outline:hover {
	background-color: var(--woolboo-oat);
	filter: none;
}

.wp-element-button:active,
.wp-block-button__link:active,
.woocommerce a.button:active,
.woocommerce button.button:active,
.woocommerce input.button:active,
.wc-block-components-button:active {
	transform: translateY(1px);
}

/* A card that leads somewhere should say so under the pointer. */
.woocommerce ul.products li.product,
li.wc-block-product {
	transition:
		box-shadow 140ms ease,
		transform 140ms ease;
}

.woocommerce ul.products li.product:hover,
li.wc-block-product:hover {
	box-shadow: 0 6px 18px var(--woolboo-shadow);
	transform: translateY(-2px);
}

/*
 * Everything above is motion, so it needs an opt-out. Honouring the OS
 * setting is a WCAG 2.2 requirement, not a nicety.
 */
@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		scroll-behavior: auto !important;
		transition-duration: 0.01ms !important;
	}

	.woocommerce ul.products li.product:hover,
	li.wc-block-product:hover,
	.wp-element-button:active,
	.wp-block-button__link:active {
		transform: none;
	}
}

.woolboo-announcement {
	background: var(--woolboo-sage-dark);
	color: #fff;
	font-size: 0.84rem;
	letter-spacing: 0.02em;
	padding: 0.55rem 1rem;
	text-align: center;
}

.woolboo-header {
	background: rgba(250, 247, 240, 0.96);
	border-bottom: 1px solid var(--woolboo-oat);
}

.woolboo-hero {
	background: linear-gradient(135deg, #f7ecde 0%, #faf7f0 55%, #eef0e8 100%);
	border-radius: 1.5rem;
	overflow: hidden;
}

.woolboo-kicker {
	color: var(--woolboo-terracotta);
	font-size: 0.78rem;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
}

/*
 * 6vw put roughly 200px of nothing between the category row and the next
 * heading on a desktop screen, which reads as the page having ended.
 */
.woolboo-section {
	padding-block: clamp(2rem, 4vw, 3.5rem);
}

.woolboo-card {
	background: var(--woolboo-white);
	border: 1px solid var(--woolboo-oat);
	border-radius: 1.1rem;
	padding: clamp(1.25rem, 3vw, 2rem);
}

.woolboo-trust-row {
	border-block: 1px solid var(--woolboo-oat);
	color: var(--woolboo-sage-dark);
	font-size: 0.92rem;
	font-weight: 650;
	padding-block: 1rem;
}

.woolboo-newsletter {
	background: var(--woolboo-sage-dark);
	border-radius: 1.5rem;
	color: #fff;
}

/*
 * The button inherits the site's primary green, which is the same green this
 * panel is painted in, so the only visible part was its white label — the one
 * call to action on the homepage looked like a bold sentence.
 */
.woolboo-newsletter .wp-block-button__link {
	background-color: var(--woolboo-cream);
	color: var(--woolboo-sage-dark);
}

.woolboo-newsletter input[type="email"] {
	border: 0;
	border-radius: 999px;
	min-height: 3rem;
	padding-inline: 1rem;
}

/*
 * Footer link columns are list blocks, so they arrived with bullets and
 * underlines. Neither belongs in a footer nav.
 */
.woolboo-footer .wp-block-list {
	list-style: none;
	margin: 0;
	padding: 0;
}

.woolboo-footer .wp-block-list li + li {
	margin-top: 0.45rem;
}

.woolboo-footer .wp-block-list a {
	text-decoration: none;
}

.woolboo-footer .wp-block-list a:hover,
.woolboo-footer .wp-block-list a:focus-visible {
	text-decoration: underline;
}

.woolboo-pattern-details {
	border-block: 1px solid var(--woolboo-oat);
	margin-block: 1.5rem;
	padding-block: 1rem;
}

.woolboo-pattern-details dl {
	display: grid;
	gap: 0.65rem 1.25rem;
	grid-template-columns: minmax(8rem, 0.8fr) 2fr;
	margin: 0;
}

.woolboo-pattern-details dt {
	font-weight: 700;
}

.woolboo-pattern-details dd {
	margin: 0;
}

.woolboo-page-head {
	padding-block: clamp(2rem, 5vw, 3.5rem) 0;
}

/*
 * The post template asks for three fixed columns, which strands an empty slot
 * whenever the section holds two posts. auto-fit lets the row fill the width it
 * actually has. Two classes to outweigh the single-class rule core prints.
 */
.woolboo-post-grid.is-layout-grid {
	gap: clamp(1.25rem, 3vw, 2rem);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
}

.woolboo-post-card {
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
	height: 100%;
}

/*
 * A featured image carries its own intrinsic width, and `max-width: 100%` is
 * measured against the constrained layout's content size rather than the card,
 * so a 510px image rendered 510px wide inside a 308px card and hung out of it.
 * Both the wrapper and the image have to be told to fit the card.
 */
.woolboo-post-card .wp-block-post-featured-image,
.woolboo-post-card .wp-block-post-featured-image img {
	max-width: 100%;
	width: 100%;
}

.woolboo-post-card .wp-block-post-excerpt {
	margin-block-end: auto;
}

.woolboo-post-meta {
	color: var(--woolboo-sage);
}

/*
 * Affiliate disclosure. Required at the top of any post carrying a partner
 * link — see docs/11-blog-and-affiliate-plan.md.
 */
.woolboo-disclosure {
	background: var(--woolboo-oat);
	border-radius: 0.75rem;
	font-size: 0.92rem;
	margin-block: 1.5rem;
	padding: 0.9rem 1.1rem;
}

/*
 * WooCommerce lays product and category loops out with floats and percentage
 * widths, which the block theme's constrained layout collapses into two
 * uneven columns. Grid restores the requested column count at every width.
 */
.woocommerce ul.products {
	display: grid;
	gap: clamp(1rem, 2.5vw, 1.75rem);
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr));
	list-style: none;
	margin: 0;
	padding: 0;
}

.woocommerce ul.products::before,
.woocommerce ul.products::after {
	content: none;
}

.woocommerce ul.products li.product,
.woocommerce ul.products li.product-category,
.woocommerce-page ul.products li.product,
.woocommerce-page ul.products li.product-category {
	float: none;
	margin: 0;
	width: 100%;
}

/*
 * WooCommerce's own .columns-N rules carry one more class than the reset above,
 * so they win on specificity even when this file loads later. The attribute
 * selector matches that weight and restores full-width grid items.
 */
.woocommerce ul.products[class*="columns-"] li.product,
.woocommerce ul.products[class*="columns-"] li.product-category,
.woocommerce-page ul.products[class*="columns-"] li.product,
.woocommerce-page ul.products[class*="columns-"] li.product-category {
	float: none;
	margin: 0;
	width: 100%;
}

@media (min-width: 782px) {
	.woocommerce.columns-2 ul.products {
		grid-template-columns: repeat(2, 1fr);
	}

	.woocommerce.columns-3 ul.products {
		grid-template-columns: repeat(3, 1fr);
	}

	.woocommerce.columns-4 ul.products {
		grid-template-columns: repeat(4, 1fr);
	}
}

.woocommerce ul.products li.product {
	background: var(--woolboo-white);
	border: 1px solid var(--woolboo-oat);
	border-radius: 1rem;
	overflow: hidden;
	padding: 0 0 1.1rem;
}

.woocommerce ul.products li.product-category {
	background: var(--woolboo-white);
	border: 1px solid var(--woolboo-oat);
	border-radius: 1rem;
	overflow: hidden;
	text-align: center;
}

.woocommerce ul.products li.product-category > a {
	display: block;
	padding: 1.35rem 1rem;
	text-decoration: none;
}

/*
 * Categories have no artwork yet. WooCommerce renders its placeholder without
 * a class, so match it by file name; a real category image still renders once
 * one is uploaded.
 */
.woocommerce ul.products li.product-category img[src*="woocommerce-placeholder"] {
	display: none;
}

.woocommerce ul.products li.product-category img {
	aspect-ratio: 4 / 5;
	margin-bottom: 0.75rem;
	object-fit: cover;
	width: 100%;
}

.woocommerce ul.products li.product-category h2,
.woocommerce ul.products li.product-category .woocommerce-loop-category__title {
	font-size: 1.05rem;
	margin: 0;
}

.woocommerce ul.products li.product-category mark.count {
	background: none;
	color: var(--woolboo-sage);
	font-weight: 500;
}

.woocommerce ul.products li.product .woocommerce-loop-product__title,
.woocommerce ul.products li.product .price,
.woocommerce ul.products li.product .button {
	margin-inline: 1rem;
}

/*
 * A two-line product name pushed that card's price and button 20px below its
 * neighbours', so a row of four buttons sat at four different heights. The
 * card becomes a column, the title reserves two lines whether it needs them or
 * not, and the button is pinned to the bottom of whatever height the row
 * settles on. `:not(.product-category)` because WooCommerce puts `product` on
 * category tiles too.
 */
.woocommerce ul.products li.product:not(.product-category) {
	display: flex;
	flex-direction: column;
}

.woocommerce ul.products li.product:not(.product-category) .woocommerce-loop-product__title {
	min-height: 2.9em;
}

.woocommerce ul.products li.product:not(.product-category) .button {
	align-self: flex-start;
	margin-top: auto;
}

/*
 * Homepage category row. With no artwork uploaded these tiles were 130px-tall
 * empty boxes holding one line of text; as pills they say the same thing in a
 * third of the height. Uploading category images and dropping this class
 * returns them to cards.
 */
.woolboo-category-row .woocommerce ul.products {
	display: flex;
	flex-wrap: wrap;
	gap: 0.6rem;
}

/*
 * Four separate rules pad this tile, and a pill has to undo all of them or it
 * comes out 103px tall and egg-shaped: the li keeps `li.product`'s bottom
 * padding, the anchor holds an empty `<p>` whose margins do not collapse
 * through the anchor's own padding, and WooCommerce gives the category title
 * `padding: .5em 0`.
 */
.woolboo-category-row .woocommerce ul.products li.product-category,
.woolboo-category-row .woocommerce ul.products[class*="columns-"] li.product-category {
	border-radius: 999px;
	padding: 0;
	width: auto;
}

.woolboo-category-row .woocommerce ul.products li.product-category > a {
	align-items: baseline;
	display: flex;
	gap: 0.35rem;
	padding: 0.45rem 1.1rem;
}

.woolboo-category-row .woocommerce ul.products li.product-category > a > * {
	margin: 0;
}

.woolboo-category-row .woocommerce ul.products li.product-category img,
.woolboo-category-row .woocommerce ul.products li.product-category p {
	display: none;
}

.woolboo-category-row .woocommerce ul.products li.product-category h2,
.woolboo-category-row .woocommerce ul.products li.product-category .woocommerce-loop-category__title {
	font-size: 0.98rem;
	line-height: 1.4;
	padding: 0;
}

.woolboo-category-row .woocommerce ul.products li.product-category:hover {
	border-color: var(--woolboo-sage);
}

.woocommerce ul.products li.product img {
	aspect-ratio: 4 / 5;
	object-fit: cover;
}

/*
 * The homepage builds its grids with the `[products]` shortcode, which emits
 * `ul.products li.product`. Shop and the category archives are the block
 * Product Collection, which emits `ul.wc-block-product-template
 * li.wc-block-product` — different markup for the same card, so none of the
 * card styling above reached them and those pages showed bare images on the
 * page background. Same chrome, same alignment, same bottom-pinned button.
 */
li.wc-block-product {
	background: var(--woolboo-white);
	border: 1px solid var(--woolboo-oat);
	border-radius: 1rem;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	padding: 0 0 1.1rem;
}

li.wc-block-product .wc-block-components-product-image {
	margin: 0 0 0.9rem;
}

li.wc-block-product > :not(.wc-block-components-product-image) {
	margin-inline: 1rem;
}

li.wc-block-product .wp-block-post-title {
	min-height: 2.9em;
}

li.wc-block-product .wp-block-post-title,
li.wc-block-product .has-text-align-center {
	text-align: left;
}

li.wc-block-product .wc-block-components-product-button {
	margin-top: auto;
}

li.wc-block-product .wc-block-components-product-button > .wp-block-button__link {
	align-self: flex-start;
	margin-inline: 0;
}

/* The only browser-default control left on the site. */
.woocommerce-ordering select.orderby {
	background: var(--woolboo-white);
	border: 1px solid var(--woolboo-oat);
	border-radius: 999px;
	color: inherit;
	font: inherit;
	padding: 0.5rem 1rem;
}

/*
 * Gallery thumbnails are floated edge to edge, so four photos of the same doll
 * read as one wide smear. Grid with a gap makes them four thumbnails again.
 */
.woocommerce-product-gallery ol.flex-control-thumbs,
.woocommerce-product-gallery ul.flex-control-thumbs {
	display: grid;
	gap: 0.5rem;
	grid-template-columns: repeat(4, 1fr);
	margin: 0.5rem 0 0;
	padding: 0;
}

.woocommerce-product-gallery .flex-control-thumbs > li {
	display: block;
	float: none;
	margin: 0;
	width: 100%;
}

.woocommerce-product-gallery .flex-control-thumbs > li > img {
	border-radius: 0.5rem;
	display: block;
	height: auto;
	width: 100%;
}

/*
 * Every text field on the site was still browser and WooCommerce default
 * chrome. Measured on My Account: a 1px rgba(32, 7, 7, 0.8) border — a
 * near-black — with a 4px radius, pure #fff, and 14px text, sitting directly
 * above a fully rounded deep-sage pill button. The 404 search field was worse
 * still, a pure #000 hairline, and it is the one control that page exists to
 * offer. Nothing else on the site draws a border in anything but oat.
 *
 * Same treatment the sort control above already gets. 14px also sat three
 * points under the 17px body size, which is what makes iOS zoom the page on
 * focus; `font: inherit` fixes both at once.
 *
 * Checkout and the account-details form are the same core markup, so they are
 * carried by this rule too. Checkboxes and radios are deliberately not listed.
 */
input[type="text"],
input[type="email"],
input[type="password"],
input[type="search"],
input[type="tel"],
input[type="url"],
input[type="number"],
textarea {
	background: var(--woolboo-white);
	border: 1px solid var(--woolboo-oat);
	border-radius: 999px;
	color: inherit;
	font: inherit;
	min-height: 3rem;
	padding: 0.65rem 1.1rem;
}

/* A pill is wrong for a box that grows to many lines. */
textarea {
	border-radius: 0.9rem;
}

input[type="text"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
input[type="search"]:focus,
input[type="tel"]:focus,
input[type="url"]:focus,
input[type="number"]:focus,
textarea:focus {
	border-color: var(--woolboo-sage);
	outline: 2px solid var(--woolboo-sage);
	outline-offset: 1px;
}

/*
 * The rule above does not reach a WooCommerce field: Woo draws those with
 * `.woocommerce form .form-row .input-text`, which outranks a bare type
 * selector and simply wins. Its border, though, comes from three custom
 * properties on :root, so that half is a variable swap rather than a
 * specificity fight.
 */
:root {
	--wc-form-border-color: var(--woolboo-oat);
	--wc-form-border-radius: 999px;
}

/*
 * Background and type size have no such variable and have to be taken on
 * Woo's own selectors. Both `.woocommerce` and `.woocommerce-page` are listed
 * because the font size is set on the `-page` variant alone, at equal weight
 * to ours — matching both puts this rule past it on document order without
 * reaching for `!important`.
 */
.woocommerce form .form-row input.input-text,
.woocommerce form .form-row textarea.input-text,
.woocommerce form .form-row select,
.woocommerce-page form .form-row input.input-text,
.woocommerce-page form .form-row textarea.input-text,
.woocommerce-page form .form-row select {
	background: var(--woolboo-white);
	font-size: 1rem;
	min-height: 3rem;
	padding: 0.65rem 1.1rem;
}

.woocommerce form .form-row textarea.input-text,
.woocommerce-page form .form-row textarea.input-text {
	border-radius: 0.9rem;
}

/*
 * `blockGap` is 1.25rem and it is applied between every pair of blocks
 * regardless of what they are — so a section heading sat exactly 20px below
 * the preceding paragraph, the same distance as one paragraph from the next.
 * Measured across the first post: thirteen consecutive gaps, every one 20px.
 * Six sections, and nothing but font size to mark where one ended.
 *
 * Child combinator on purpose: the related-products block at the foot of a
 * post titles each card with an h2 as well, and those must not be pushed off
 * their cards. It sits outside the content wrapper today, but it costs
 * nothing to say so here.
 */
.wp-block-post-content > h2 {
	margin-block-start: 2.75rem;
}

.wp-block-post-content > h3 {
	margin-block-start: 2rem;
}

/*
 * The buy form sat 1316px down a 720px viewport. The product column ran
 * title → price → a 449px pattern-details table → a 307px delivery notice →
 * excerpt → *then* the format selector and Add to cart, so the only control
 * that takes money was nearly two screens below the fold.
 *
 * Reordered to title → price → excerpt → buy form, with the reference
 * material below it. Measured on the live page: the form moves from 1316px
 * to 512px. The column grows about 60px because a flex container stops
 * adjacent margins collapsing, which is a fair trade.
 *
 * `:has()` scopes this to the one column that actually contains a buy form,
 * so no other column layout on the site is touched. Where `:has()` is
 * unsupported the whole block is skipped and the page renders as it does
 * today — the degradation is the current layout, not a broken one.
 */
.wp-block-column:has(> .wp-block-add-to-cart-form) {
	display: flex;
	flex-direction: column;
}

.wp-block-column:has(> .wp-block-add-to-cart-form) > * {
	order: 5;
}

.wp-block-column:has(> .wp-block-add-to-cart-form) > .wp-block-post-title {
	order: 1;
}

.wp-block-column:has(> .wp-block-add-to-cart-form) > :has(.wc-block-components-product-price) {
	order: 2;
}

.wp-block-column:has(> .wp-block-add-to-cart-form) > .wp-block-post-excerpt {
	order: 3;
}

.wp-block-column:has(> .wp-block-add-to-cart-form) > .wp-block-add-to-cart-form {
	order: 4;
}

/*
 * A catalogue card reading "$7.00 – $69.90" does not say that the low end is
 * a PDF download and the high end is a hand-crocheted toy. The plugin appends
 * the two labelled figures; this is how they sit under the range.
 */
.woolboo-price-formats {
	color: var(--woolboo-sage);
	display: block;
	font-size: 0.82rem;
	line-height: 1.45;
	margin-top: 0.15rem;
}

@media (max-width: 781px) {
	.woolboo-hero {
		border-radius: 1rem;
	}

	.woolboo-pattern-details dl {
		grid-template-columns: 1fr;
	}

	.woolboo-pattern-details dd + dt {
		margin-top: 0.45rem;
	}
}
