/*
Theme Name: Medlife Child
Theme URI: https://medlifehealthcare.store/
Description: Child theme of Astra for Medlife Healthcare Store. Custom homepage sections built to the approved design. All customisation lives here - the Astra parent is never edited.
Author: Medlife Healthcare
Template: astra
Version: 1.0.0
Requires PHP: 8.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: medlife-child
*/

/* -------------------------------------------------------------------------
 * Brand tokens
 * Single source of truth for colour. Every section references these.
 * ---------------------------------------------------------------------- */

:root {
	--ml-cyan: #00AFF0;
	--ml-cyan-dark: #0098d1;

	/*
	 * Kept defined although nothing uses it after the flat-button pass. It is
	 * the end stop of the legacy CTA gradient
	 * `linear-gradient(135deg, var(--ml-cyan), #0790CF)`, which was hardcoded in
	 * 11 places; naming it means a future gradient reaches for the value the
	 * design already used instead of inventing a twelfth blue.
	 */
	--ml-cyan-gradient-end: #0790CF;

	/*
	 * COMPONENTS, NOT A COLOUR - use as `rgba(var(--ml-shadow-tint), .95)`.
	 *
	 * A custom property cannot carry a variable alpha, and every one of the 36
	 * hardcoded uses needed a different one, so this holds the raw channels and
	 * the caller supplies the opacity. That is the whole reason this could not
	 * simply be `--ml-shadow-tint: rgba(0, 145, 205, 0.95)`.
	 */
	--ml-shadow-tint: 0, 145, 205;

	/*
	 * The readable blue. Brand cyan measures 2.50:1 on white and is deliberately
	 * used anyway in four recorded places; THIS is the value for anything that
	 * has to pass - links in prose, focus rings, small accent text. 4.87:1.
	 */
	--ml-accent: #0077B6;

	/*
	 * Body copy. 5.69:1 on white. Distinct from --ml-muted (4.62:1), which is
	 * for secondary text; below that the greys this replaced were failing AA.
	 */
	--ml-body: #55697B;

	--ml-navy: #0A2540;
	--ml-navy-light: #123356;
	--ml-charcoal: #373536;

	--ml-white: #ffffff;
	--ml-muted: #6b7684;
	--ml-line: #e3e8ee;

	/* Promotional accent, used only by the Deal of the Day bar. */
	--ml-amber: #f4c74f;
	--ml-amber-dark: #e0b23a;

	/* Sale price and sale badge. Only ever painted when is_on_sale() is true. */
	--ml-sale: #d64031;

	/* Pale blue "stage" the nav bar and hero sit on. */
	--ml-stage: #e9f4fb;
	--ml-stage-soft: #f5fafd;

	/*
	 * MOTION TOKENS - one duration per interaction role, consolidated 2026-09-05.
	 *
	 * Before this the theme carried 23 distinct durations across 277 transitions,
	 * and 187 of them were either 0.18s or 0.2s - the same gesture written two
	 * ways, differing by an imperceptible 0.02s, decided by which file the
	 * component happened to land in.
	 *
	 * Use a token. A new literal duration is drift by definition.
	 */
	--ml-t-base: 180ms;    /* interaction: hovers, lifts, fills, colour states */
	--ml-t-panel: 280ms;   /* disclosure: accordions, dropdowns, chevrons      */
	--ml-t-enter: 420ms;   /* entrance: reveals, drops, first paint            */

	--ml-e-standard: ease;                              /* everything interactive */
	--ml-e-enter: cubic-bezier(0.22, 0.61, 0.36, 1);    /* reveals and entrances  */

	/*
	 * NAMED EXCEPTIONS - deliberately NOT tokenised, because they are their own
	 * thing rather than drift: the 4.2s top-bar marquee and pin float loops, the
	 * 0.62s pin drop, the 0.85s/0.95s long reveals, the per-item `calc()` stagger
	 * delays, and the `0s` visibility swaps that make the accordion's collapsed
	 * content unreachable at the right moment.
	 */

	--ml-container: 1360px;
	--ml-radius: 8px;

	--ml-font: 'Poppins', ui-sans-serif, system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* -------------------------------------------------------------------------
 * Blank front page
 *
 * The front page carries no post content on purpose - every section is
 * rendered through theme hooks. Astra still outputs its content wrapper for
 * the empty page, which would otherwise leave a band of vertical padding
 * between the last section and the trust strip.
 * ---------------------------------------------------------------------- */

.home .site-content > .ast-container {
	padding-top: 0;
	padding-bottom: 0;
}

.home #primary {
	margin-top: 0;
	margin-bottom: 0;
	padding-top: 0;
	padding-bottom: 0;
}

.home .ast-article-single {
	padding: 0;
	border: 0;
}

.home .entry-content {
	margin-top: 0;
	margin-bottom: 0;
}

/* -------------------------------------------------------------------------
 * Product compliance disclaimer
 *
 * Appended to product descriptions by the DRAP content pass. Small, muted,
 * and separated by a rule, so it reads as a regulatory footnote rather than
 * as the last line of marketing copy.
 *
 * Two classes deep on purpose. The description tab's own container carries
 * `entry-content`, so Astra's `.entry-content p` - specificity (0,1,1) -
 * outranks a bare `.ml-product-disclaimer` (0,1,0) whatever the load order,
 * and would supply its own margin and colour. Same trap as the header
 * search field; see the note in the nav CSS.
 *
 * The `em` reset is also deliberate. The markup wraps the sentence in <em>
 * because the disclaimer is set apart from the body copy semantically, but
 * a fully italic paragraph at 13px is harder to read than it is emphatic,
 * so the visual emphasis comes from size and colour instead.
 * ---------------------------------------------------------------------- */

.ml-product .ml-product-disclaimer,
.entry-content .ml-product-disclaimer {
	margin: 22px 0 0;
	padding-top: 16px;
	border-top: 1px solid var(--ml-line);
	color: var(--ml-muted);
	font-size: 13px;
	line-height: 1.65;
}

.ml-product .ml-product-disclaimer em,
.entry-content .ml-product-disclaimer em {
	font-style: normal;
}


/* -------------------------------------------------------------------------
 * Astra's site-wide pagination (.ast-pagination)
 *
 * This is a DIFFERENT COMPONENT from the shop pagination, and confusing the
 * two costs an afternoon - so, plainly:
 *
 *   shop / product archives  ->  <nav class="woocommerce-pagination">
 *                                <ul class="page-numbers"><li><a>
 *                                styled in assets/css/shop.css
 *
 *   search results, post     ->  <div class="ast-pagination">
 *   archives, blog               <nav class="navigation pagination">
 *                                <div class="nav-links"><span|a>
 *                                styled HERE
 *
 * Different markup - flat divs, no <ul>/<li> - and, decisively, **shop.css is
 * not enqueued on these pages at all**: `medlife-shop` loads only on shop and
 * product archives, so every `.ml-shop__main .woocommerce-pagination` rule is
 * simply absent here. That is why the current page kept Astra's #046bd2 fill
 * (`--ast-global-color-0`) no matter how the shop rules were adjusted.
 *
 * This block lives in style.css because that IS loaded site-wide; putting it in
 * shop.css would reproduce the same bug.
 * ---------------------------------------------------------------------- */

.ast-pagination .nav-links {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 8px;
	/* Astra sets `display: inline-block; width: 100%` here. */
	width: auto;
}

/*
 * Same pill as the shop pagination: 44px tap target, 8px radius, --ml-line
 * border, navy on white. Astra sizes these in em (2.33333em square) which is
 * why they were a different size from the shop's.
 */
.ast-pagination .nav-links .page-numbers {
	display: flex;
	align-items: center;
	justify-content: center;
	width: auto;
	min-width: 44px;
	height: 44px;
	margin: 0;
	padding: 0 12px;
	border: 1px solid var(--ml-line);
	border-radius: 8px;
	background: var(--ml-white);
	color: var(--ml-navy);
	font-family: var(--ml-font);
	font-size: 14px;
	font-weight: 600;
	line-height: 1;
	text-decoration: none;
	float: none;
	transition:
		transform 0.18s ease,
		box-shadow 0.18s ease,
		border-color 0.18s ease,
		background-color 0.18s ease,
		color 0.18s ease;
}

/*
 * Prev/Next need `!important`, which is not laziness - Astra ships
 * `.ast-pagination .next:focus, .ast-pagination .prev:focus { width: 100%
 * !important }`. Without matching it, tabbing to Next stretches it across the
 * whole row. The floats and the 100% width from its non-focus rules go here
 * too, so the links stay inline pills in the flex row.
 */
.ast-pagination .nav-links .prev.page-numbers,
.ast-pagination .nav-links .next.page-numbers,
.ast-pagination .nav-links .prev.page-numbers:focus,
.ast-pagination .nav-links .next.page-numbers:focus {
	width: auto !important;
	float: none;
	height: 44px;
	padding: 0 16px;
	line-height: 1;
}

/*
 * Anchors only - the current page is a <span>, so scoping hover to `a` makes it
 * structurally impossible for these states to repaint it. Same guard as the
 * shop pagination uses, and the same reason.
 */
.ast-pagination .nav-links a.page-numbers:hover,
.ast-pagination .nav-links a.page-numbers:focus-visible {
	transform: translateY(-2px);
	border-color: var(--ml-cyan);
	color: var(--ml-cyan);
	box-shadow: 0 12px 22px -14px rgba(0, 145, 205, 0.95);
}

.ast-pagination .nav-links a.page-numbers:focus-visible {
	outline: 3px solid var(--ml-cyan);
	outline-offset: 3px;
}

/*
 * The current page, locked white-on-cyan in every state.
 *
 * (0,4,0) against Astra's `.ast-pagination .page-numbers.current` at (0,3,0),
 * which is what paints #046bd2.
 */
.ast-pagination .nav-links .page-numbers.current,
.ast-pagination .nav-links .page-numbers.current:hover,
.ast-pagination .nav-links .page-numbers.current:focus-visible {
	background: var(--ml-cyan);
	border-color: var(--ml-cyan);
	color: var(--ml-white);
	transform: none;
	box-shadow: none;
}

/* The ellipsis is not a control and must not look like one. */
.ast-pagination .nav-links .page-numbers.dots {
	min-width: 0;
	padding: 0 4px;
	border: 0;
	background: none;
	color: #5f7183;
}

@media (prefers-reduced-motion: reduce) {
	.ast-pagination .nav-links .page-numbers {
		transition: none;
	}

	.ast-pagination .nav-links a.page-numbers:hover,
	.ast-pagination .nav-links a.page-numbers:focus-visible {
		transform: none;
	}
}

/*
 * POINTER FOCUS - ASTRA a:focus { outline: thin dotted } (main.min.css L3).
 * A mouse click leaves :focus set, so every anchor that names no outline of its
 * own kept a thin dotted ring until focus moved. Measured 2026-09-13 by the
 * sitewide anchor sweep (CLAUDE.md, "Anchor pointer-focus sweep").
 *
 * :not(:focus-visible) IS LOAD-BEARING: this only ever applies to pointer focus,
 * so the keyboard ring each family already has is untouched. Outline only -
 * nothing moves, so there is no reduced-motion twin.
 */
a.skip-link:focus:not(:focus-visible),
a.wa-float:focus:not(:focus-visible) {
	outline: none;
}

/*
 * KEYBOARD RINGS - 2026-09-14. These families had NO focus ring of their own:
 * keyboard focus showed only Astra's a:focus-visible 1px dotted in the text
 * colour, or nothing at all where a theme rule said outline: none. Measured by
 * the sitewide anchor sweep (CLAUDE.md, "Keyboard ring pass").
 *
 * Light surface: 3px --ml-cyan offset 3px. Navy surface: 2px. border-radius is
 * RESTATED on every ring - a rule wins only the properties it names, and a
 * shared focus rule has squared a round control on this site before. Text links
 * take 4px corners; boxed controls keep their own radius.
 *
 * The pointer pin (:focus:not(:focus-visible)) above is untouched - the two
 * selectors can never match the same state. Contrast: cyan on white 2.50:1 is
 * the owner's recorded decision (CLAUDE.md, "THE FOCUS-RING RULE").
 */
a.skip-link:focus-visible {
	outline: 3px solid var(--ml-cyan);
	outline-offset: 3px;
	border-radius: 4px;
}
a.wa-float:focus-visible {
	outline: 3px solid var(--ml-cyan);
	outline-offset: 3px;
	border-radius: 50%;
}

/*
 * RING FADE - ASTRA'S INLINE `a { transition: all .2s linear }`. "all" includes
 * outline, so these families' keyboard rings slid and faded in over 200ms
 * instead of appearing at once. Each family now names exactly what its hover /
 * focus-visible state really changes, measured 2026-09-14 (CLAUDE.md, "Ring fade
 * pass"): colour where Astra's a:hover repaints the ink, nothing at all where
 * only the (discrete) underline changes or nothing visible changes.
 * Outline is never in the list. Colour-only fades need no reduced-motion twin.
 */
a.skip-link {
	transition: none;
}

/* -------------------------------------------------------------------------
 * Breadcrumbs - .ml-shop__crumbs
 *
 * MOVED HERE FROM shop.css ON 2026-09-14, unchanged apart from the transition.
 * The component renders on shop, category and cart AND on the policy pages,
 * track-order, account and checkout - which never load shop.css. On those the
 * crumbs fell through to Astra: 16px instead of 13.5px, cyan-dark on hover, no
 * underline (typography audit, CLAUDE.md). Earlier passes had patched the
 * focus pin, ring and transition into policy/account/checkout.css one copy per
 * file; those copies are gone - this is the only home.
 * ---------------------------------------------------------------------- */
.ml-shop__crumbs {
	display: flex;
	align-items: center;
	gap: 8px;
	font-size: 13.5px;
	color: var(--ml-body);
	margin-bottom: 14px;
}

/* Colour only - Astra's a { transition: all } would otherwise fade the ring in. */
.ml-shop__crumbs a {
	color: var(--ml-cyan);
	text-decoration: none;
	transition: color var(--ml-t-base) var(--ml-e-standard);
}

/*
 * THE :focus PIN against Astra's a:focus { color; outline: thin dotted } (0,1,1).
 * :not(:focus-visible) so it can never swallow the keyboard ring.
 */
.ml-shop__crumbs a:focus:not(:focus-visible) {
	color: var(--ml-cyan);
	outline: none;
}

/*
 * Hover / keyboard focus: cyan + underline.
 * Astra's inline `.ast-single-post .entry-content .woocommerce a { text-decoration:
 * none }` is (0,3,1) and reaches every link inside a WooCommerce shortcode
 * wrapper - cart, checkout and all account screens. The doubled container
 * class plus [href] makes this (0,4,1), one tier above (2026-09-15).
 */
.ml-shop__crumbs.ml-shop__crumbs a[href]:hover,
.ml-shop__crumbs.ml-shop__crumbs a[href]:focus-visible {
	color: var(--ml-cyan);
	text-decoration: underline;
}

.ml-shop__crumbs a:focus-visible {
	outline: 3px solid var(--ml-cyan);
	outline-offset: 3px;
	border-radius: 4px;
}

/* -------------------------------------------------------------------------
 * "View cart" in the added-to-cart notice - the 50 / r8 / 15px/600 CTA
 *
 * WooCommerce prints <a class="button wc-forward"> inside .woocommerce-message
 * on whatever page follows a non-AJAX add to cart (cart, product, shop), so this
 * lives in style.css rather than one page's file. It rendered as Astra's
 * transparent 30px pill: `.woocommerce-js a.button` (0,2,1) at rest and
 * (0,3,1) on hover. Rest + lock here is (0,4,1)/(0,5,1), states (0,5,1) and
 * later in the file. Only transform / shadow / background are transitioned,
 * never outline (Astra's a { transition: all } would fade the ring in).
 * ---------------------------------------------------------------------- */
.woocommerce-notices-wrapper .woocommerce-message a.button.wc-forward,
.woocommerce-notices-wrapper .woocommerce-message a.button.wc-forward:focus,
.woocommerce-notices-wrapper .woocommerce-message a.button.wc-forward:active {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	height: 50px;
	padding: 0 24px;
	border: 0;
	border-radius: 8px;
	background: var(--ml-cyan);
	color: var(--ml-white);
	font-family: var(--ml-font);
	font-size: 15px;
	font-weight: 600;
	line-height: 1;
	text-decoration: none;
	box-shadow: 0 14px 26px -16px rgba(var(--ml-shadow-tint), 0.95);
	transition: background-color var(--ml-t-base) var(--ml-e-standard), transform var(--ml-t-base) var(--ml-e-standard), box-shadow var(--ml-t-base) var(--ml-e-standard);
}

/* THE :focus PIN - a click must not leave Astra's dotted ring. */
.woocommerce-notices-wrapper .woocommerce-message a.button.wc-forward:focus:not(:focus-visible) {
	outline: none;
}

.woocommerce-notices-wrapper .woocommerce-message a.button.wc-forward:hover,
.woocommerce-notices-wrapper .woocommerce-message a.button.wc-forward:focus-visible {
	background: var(--ml-cyan-dark);
	color: var(--ml-white);
	transform: translateY(-2px);
	box-shadow: 0 22px 34px -16px rgba(var(--ml-shadow-tint), 1);
}

.woocommerce-notices-wrapper .woocommerce-message a.button.wc-forward:focus-visible {
	outline: 3px solid var(--ml-cyan);
	outline-offset: 3px;
	border-radius: 8px;
}

@media (prefers-reduced-motion: reduce) {
	.woocommerce-notices-wrapper .woocommerce-message a.button.wc-forward,
	.woocommerce-notices-wrapper .woocommerce-message a.button.wc-forward:hover,
	.woocommerce-notices-wrapper .woocommerce-message a.button.wc-forward:focus-visible {
		transition: none;
		transform: none;
	}
}

/* -------------------------------------------------------------------------
 * Section title - .ml-section-title (2026-09-15)
 *
 * The homepage section heading: centred, uppercase, a --ml-muted rule running
 * out to each side. Extracted from .ml-cats__title in categories.css UNCHANGED
 * (measured at 360/768/1024/1440 before the move) so "Our Supplement
 * Categories" and "Trending Now" share one rule instead of two copies.
 *
 * The h2 is the flex container and the rules are its ::before / ::after, so
 * they are always the same length as each other and carry nothing into the
 * accessibility tree. TEXT-TRANSFORM, NOT TYPED CAPITALS: the source string
 * stays in title case for screen readers and translators.
 *
 * DOUBLED CLASS, (0,2,0): Astra ships .woocommerce-js h2 { margin-bottom: .7em }
 * at (0,1,1) - woocommerce-js is added by JS, so it is absent from view-source.
 * A single class lost the margin once already. The pseudo-elements are not
 * contested, so they stay single-class.
 * ---------------------------------------------------------------------- */
/* Title -> content is 22px at EVERY width - the homepage spacing scale (CLAUDE.md, 2026-09-15). No breakpoint restates it. */
.ml-section-title.ml-section-title {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 28px;
	width: 100%;
	max-width: var(--ml-section-title-span);
	margin: 0 auto 22px;
	padding: 0;
	font-family: var(--ml-font);
	font-size: 26px;
	font-weight: 600;
	line-height: 1.3;
	letter-spacing: 1px;
	text-transform: uppercase;
	text-align: center;
	color: var(--ml-navy);
}

/*
 * EQUAL SPAN, EQUAL GAP (2026-09-15, owner's choice).
 *
 * Every section title is a box of the SAME width, centred, per width tier; the
 * two --ml-muted rules fill whatever the text leaves. So all titles start and
 * end on the same x and keep the same 28/20/14px gap to their text, and a short
 * title simply draws longer rules. This replaced fixed-length rules (320/220/140/
 * 44px), which made the visible span follow the text: at 1440 "Our Supplement
 * Categories" spanned 1112.6px and "Trending Now" 909.9px (titlespan.js).
 *
 * Rules, span and gap cannot all be equal while the texts differ in width
 * (416.6px vs 213.9px at 26px) - span + gap was chosen over equal rule length.
 *
 * The span of each tier is the width "Our Supplement Categories" already had, so
 * that heading is pixel-for-pixel what it was, inside .ml-cats__inner:
 *
 *   >= 1220    1112px   (rules 320 around the 416.6px text)
 *   1013-1219   912px   (220)
 *    861-1012   752px   (140)
 *    761-860    676px   (140, text 22px)
 *    561-760    484px   (44)
 *   <= 560      the full title box - see categories.css for why that box is the
 *               same width in both sections
 *
 * min-width 44px keeps a rule visible when a title wraps (it did vanish at 360
 * once). A longer title (translation, new section) squeezes its own rules down
 * to 44px and then wraps - re-measure the spans if that ever happens.
 */
.ml-section-title {
	--ml-section-title-span: 1112px;
}

.ml-section-title::before,
.ml-section-title::after {
	content: '';
	flex: 1 1 0;
	min-width: 44px;
	height: 2px;
	background: var(--ml-muted);
}

@media (max-width: 1219px) {
	.ml-section-title {
		--ml-section-title-span: 912px;
	}
}

@media (max-width: 1012px) {
	.ml-section-title {
		--ml-section-title-span: 752px;
	}
}

@media (max-width: 860px) {
	.ml-section-title.ml-section-title {
		gap: 20px;
		font-size: 22px;
	}

	.ml-section-title {
		--ml-section-title-span: 676px;
	}
}

@media (max-width: 760px) {
	.ml-section-title {
		--ml-section-title-span: 484px;
	}
}

@media (max-width: 560px) {
	/*
	 * ONE LINE AT PHONE WIDTH (2026-09-21 mobile audit). This used to say the
	 * title "may wrap to two centred lines here rather than squeeze", and it
	 * did: "Our Supplement Categories" measured 19px over 2 lines in a 343px
	 * box at 375.
	 *
	 * Two lines is not merely taller. A wrapped text box is as wide as the
	 * space it is given rather than its longest line, so the flanking rules
	 * sit on the box edges and end up 32.5px from the glyphs while the
	 * one-line title beside it keeps 14px - the exact asymmetry recorded under
	 * "Section titles - equal span, equal gap". Fitting the longest title on
	 * one line fixes the height and the rules together.
	 *
	 * 17px is derived, not picked: the title needs ~370px at 19px against the
	 * 343px it gets, i.e. about 0.92 of the size, and 17px measures on one
	 * line at 375 AND at 360. Tracking drops with it so the two scale together.
	 */
	.ml-section-title.ml-section-title {
		gap: 0;
		font-size: 17px;
		letter-spacing: 0.6px;
	}

	/*
	 * THE FLANKING RULES COME OFF AT PHONE WIDTH, AND THAT IS WHAT ACTUALLY
	 * BUYS THE ONE LINE — the font size alone did not.
	 *
	 * Measured: at 375 the title box is 343px and the two rules are
	 * `flex: 0 0 44px` with a 14px gap either side, so the TEXT only ever got
	 * 343 - 116 = 227px. "Our Supplement Categories" needs ~331px at 17px, so
	 * it still wrapped at 17px, at 16px, and would have needed ~14.5px to fit
	 * that 227px slot — a section title smaller than the body copy under it.
	 *
	 * The rules were fixed at 44px precisely BECAUSE the title wrapped (a
	 * wrapping title leaves `flex: 1 1 auto` rules no free space and they
	 * collapse to nothing), so the two constraints were circular. Removing
	 * them here breaks the circle and gives the text the whole 343px, which
	 * 17px fits with room to spare.
	 *
	 * Every width from 561 up keeps its rules and its measured equal span —
	 * this is inside the 560 query.
	 */
	.ml-section-title.ml-section-title::before,
	.ml-section-title.ml-section-title::after {
		display: none;
	}

	/* none, not 100%: a percentage would clamp the widened categories box back down */
	.ml-section-title {
		--ml-section-title-span: none;
	}
}
