/**
 * The interactive card surface - one answer to "what does a card do when you point at it".
 *
 * WHY THIS FILE EXISTS
 * --------------------
 * A card that navigates on click has to say so before the click. Four places on this site
 * draw such a card, and until this file each one answered that on its own:
 *
 *   the product grid    box-shadow `card-hover`, 150ms, gated on `@media (hover: hover)`
 *   the docs card       border-strong + box-shadow `lg` + translateY(-2px), 150ms, ungated
 *   the homepage tile   translateY(-3px) + a hand-written rgba shadow, 250ms, ungated
 *                       (deleted 2026-09-01 with templates/page-home.html, the placeholder
 *                       it was drawn for; the count below is the history, not the tally)
 *   the checkout tile   box-shadow `sm` -> `card-hover`, 150ms, ungated
 *
 * Three shadows, three lifts, three durations, and the `prefers-reduced-motion` guard
 * written in one of the four. Nothing was wrong with any single rule. What was wrong is
 * that the question was asked four times, so it got four answers.
 *
 * WHY IT COULD NOT BE theme.json, WHICH IS THE FIRST PLACE TO LOOK
 * ---------------------------------------------------------------
 * The REST state of these surfaces already is theme.json's: `settings.custom.surface`
 * holds the tokens, `styles/group-card.json` and its siblings register the block style
 * variations, and the editor can see all of it. That half was never the problem.
 *
 * theme.json CANNOT EXPRESS THE OTHER HALF. It supports `:hover` in exactly two places -
 * `elements.link` and a `core/button` variation - and this theme uses both, which is why
 * the three `:hover` keys in theme.json are all one of those two. There is no way to say
 * "a group card raises its shadow on hover" in theme.json at any version. So the
 * interaction half of every card had to fall into CSS, and CSS had nowhere shared to fall
 * into. One stylesheet per block is the right architecture for everything else and it is
 * the direct cause here: a sheet written alone answers alone.
 *
 * So the values stay in theme.json, where a store can see and change them, and the
 * BEHAVIOUR - which properties move, how fast, and the two guards - is stated here once.
 *
 * WHAT A CARD OPTS IN WITH, AND WHY THERE ARE TWO SPELLINGS OF ONE CONTRACT
 * -------------------------------------------------------------------------
 * `is-style-card` IS THE PRIMARY ONE. It is a BLOCK STYLE VARIATION - WordPress's own
 * mechanism for "this block looks like a card" - registered by styles/group-card.json for
 * core/group and declared as its default style by the sparklestock/product-card block. Two
 * things follow from using the platform's own name for this rather than a private class.
 * A person can apply the card look from the editor's Styles panel without a developer; and
 * a block can ship it as its default, so every instance of that block is a card from the
 * moment it is placed, in a template or on a page.
 *
 * `f32-card` IS THE SAME CONTRACT FOR MARKUP THE EDITOR NEVER SEES - the docs cards
 * (patterns/docs-category-cards.php) and the checkout's empty-cart tiles (inc/checkout.php)
 * are anchors composed in PHP, not blocks. `is-style-*` means "a block style variation" and
 * putting it on a hand-written `<a>` would be a claim that is not true.
 *
 * They are one contract with two audiences, not two contracts. Every rule below names both,
 * and any rule added here must name both or the two drift - which is the whole failure this
 * file exists to prevent, reproduced inside the fix.
 *
 * Nothing is applied by inspection of what a block happens to contain: a card is a card
 * because its author said so.
 *
 * WHAT IS DELIBERATELY NOT HERE: THE REST STATE. The cards look different at rest and
 * should - the checkout tile is a shadow with no border, the docs card is a hairline box,
 * the product card carries neither. Forcing one rest state would be a
 * redesign, and it is not what was broken. Each card keeps its own, from the same
 * theme.json presets it already used.
 *
 * NO LIFT, AND THAT IS A DECISION RATHER THAN AN OMISSION. Two of the four translated on
 * hover. A `translateY` reads well on four docs cards and badly on a grid of twenty-four
 * products, where a pointer crossing the grid sets off a row of them; it is also the
 * declaration that most often forgets its `prefers-reduced-motion` guard, which is exactly
 * what happened in homepage-sections.css. The shadow and the border say the same thing and
 * move nothing. A store that wants the lift back adds one token and one declaration, in
 * this file, once.
 *
 * THE TWO GUARDS, WRITTEN ONCE BECAUSE THAT IS THE POINT
 * -----------------------------------------------------
 * `@media (hover: hover)` keeps the hover paint off touch devices, where `:hover` sticks
 * to whatever was last tapped until something else is. One of the four had it.
 *
 * `prefers-reduced-motion` cuts the transition. One of the four had it. A guard that has to
 * be remembered in every sheet is a guard that will be missing from the next sheet, which
 * is the whole argument for this file over a convention.
 *
 * @package F32
 */

/*
 * THE LINE IS PASSED AS A CUSTOM PROPERTY AND THE SHADOW IS DECLARED DIRECTLY, and the
 * split is about specificity rather than taste.
 *
 * A card's resting border is written by the card's own sheet, at whatever weight that
 * sheet needs - `.docs-hub .docs-card` is 0,2,0, the same as `.f32-card:hover`. Two rules
 * of equal weight are settled by SOURCE ORDER, so a `border-color` declared here would win
 * or lose depending on which stylesheet the request happened to print second. That is not a
 * property a foundation file may have.
 *
 * A custom property has no such problem: it INHERITS, so the card's own rule reads
 * `var(--f32-card-line)` at its own specificity and this file only has to change what the
 * variable holds. It is the same seam assets/css/account-card.css opens with
 * `--f32-card-control-fill` for the same reason, and it is why a card that wants the border
 * escalation states `border-color: var(--f32-card-line)` in its own sheet.
 *
 * THE SHADOW IS DECLARED DIRECTLY AND WINS ON WEIGHT INSTEAD, and the reason it is NOT a
 * custom property is worth writing down, because the obvious symmetry with the line above
 * is a trap that cost half a day.
 *
 * FIRST, THE TIE. When this file was written no card declared a resting `box-shadow` at
 * 0,2,0, so a plain declaration could not lose. Turning the product card into a block
 * changed that: its resting shadow is `.wp-block-sparklestock-product-card.is-style-card`,
 * 0,2,0, the same weight as `.is-style-card:hover` - and block stylesheets load from a
 * `render_block` filter, which is after this sheet. Thirty cards on a category archive
 * computed the resting `sm` under the pointer.
 *
 * THE SECOND ATTEMPT WAS TO ROUTE THE SHADOW THROUGH `--f32-card-shadow`, the way the line
 * goes through `--f32-card-line`. It appeared not to work, and a long note used to sit here
 * explaining why: that a transition whose from-value comes from an unset custom property
 * never runs. THAT EXPLANATION WAS WRONG and it is corrected rather than deleted, because
 * the wrong version was committed and because the way it was wrong is worth knowing.
 *
 * THE MEASUREMENT WAS INVALID. Every reading was taken through a browser-automation tab
 * that was not the foreground tab. A hidden tab does not advance animations: measured
 * there, `document.visibilityState` is "hidden", `requestAnimationFrame` never fires, and a
 * running transition reports `currentTime: 0` for ever, so EVERY transitioned property
 * reads back as its start value. Forcing `transition: none` made the correct value appear,
 * which looked like proof of a cascade bug and was really just proof that the transition
 * was frozen. A control test settled it: a bare `<div>` transitioning `transform`, touching
 * no custom property at all, froze the same way.
 *
 * WHAT IS ACTUALLY TRUE, AND IT IS THE PLAIN VERSION: the block's resting shadow is 0,2,0
 * and so was the hover rule, and a block stylesheet loads from a `render_block` filter -
 * after this sheet. It was an ordinary specificity tie decided by load order. Doubling the
 * class settles it at 0,3,0, which no load order can change. The custom property was never
 * necessary and is not used for the shadow.
 *
 * `--f32-card-line` REMAINS A VARIABLE AND REMAINS CORRECT - not as a workaround for
 * anything, but because a card's resting border is written by the card's own sheet at
 * whatever weight that sheet needs, and an inherited property sidesteps the comparison
 * entirely.
 *
 * THE STANDING LESSON IS ABOUT MEASUREMENT, NOT CSS: never read a transitioned property
 * from an automated browser tab without checking `document.visibilityState` first.
 */
.f32-card,
.is-style-card {
	--f32-card-line: var(--wp--preset--color--border-subtle, #e0e0e0);

	transition:
		box-shadow var(--wp--custom--motion--hover, 150ms) var(--wp--custom--motion--easing, ease),
		border-color var(--wp--custom--motion--hover, 150ms) var(--wp--custom--motion--easing, ease);
}

/*
 * `@media (hover: hover)` AND NOT A BARE `:hover`. On a touch screen `:hover` latches: the
 * last thing tapped keeps the state until something else is tapped, so a customer who taps
 * a card, comes back, and scrolls past it finds one card lit for no reason. The query asks
 * whether the primary pointer can actually hover, and only then is the paint worth having.
 */
@media (hover: hover) {
	.f32-card.f32-card:hover,
	.is-style-card.is-style-card:hover {
		--f32-card-line: var(--wp--custom--surface--card--hover--line, #d1d5db);

		box-shadow: var(--wp--custom--surface--card--hover--shadow, 0 4px 3px rgba(0, 0, 0, 0.07), 0 2px 2px rgba(0, 0, 0, 0.06));
	}
}

/*
 * ONE RING FOR BOTH SHAPES A CARD ARRIVES AS, and the pair of selectors is what makes it
 * one rule rather than two.
 *
 * A card is sometimes the anchor itself (the docs card, the checkout
 * tile) and sometimes a group whose link is a stretched child (the product grid, through
 * plain-stretch-links). `:focus-visible` answers the first, `:has(:focus-visible)` the
 * second.
 *
 * `:has(:focus-visible)` AND NOT `:focus-within`, which is what the product grid used to
 * carry. `:focus-within` matches on a MOUSE click as well, so clicking a card left a ring
 * around it that stayed until the next click - a keyboard affordance drawn for somebody who
 * is not using a keyboard. `:focus-visible` inside `:has()` asks the browser the same
 * question it asks for the card that is its own anchor, which is why the two shapes now
 * behave identically.
 *
 * `accent` at 2px with a 2px offset, which is the ring the whole site already draws - the
 * docs card, the product grid, the account panels and the pagination all state exactly
 * this. It is written here so the next card does not have to know that.
 */
.f32-card:focus-visible,
.f32-card:has(:focus-visible),
.is-style-card:focus-visible,
.is-style-card:has(:focus-visible) {
	outline: 2px solid var(--wp--preset--color--accent, #1473e6);
	outline-offset: 2px;
}

/*
 * The transition is the only thing to cut. There is no `transform` left to freeze - see the
 * header - so this is one declaration rather than the two-rule shape the docs card needed.
 */
@media (prefers-reduced-motion: reduce) {
	.f32-card,
	.is-style-card {
		transition: none;
	}
}

/* The offer opts into a small arrow cue; the purchase surface stays stationary. */
@media (hover: hover) {
	.f32-pass__cta:hover span { transform: translateX(3px); }
}
.f32-pass__cta:focus-visible span { transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) {
	.f32-pass__cta:hover span,
	.f32-pass__cta:focus-visible span { transform: none; }
}
