/* The kit's motion layer · TT-5911 A1
 *
 * One shared sheet for every moving thing the Cupertino register allows, the
 * same way assets/st-floor.css is the one shared sheet for its surfaces. There
 * is deliberately no per-pattern motion CSS: a sibling opts in with an
 * attribute and gets exactly the behaviour below.
 *
 * R7, the register's motion rule: motion only on ENTRY. A fade and an 8px rise,
 * once, 400ms. Sticky media that releases. A 2px hover lift. Nothing loops,
 * nothing autoplays, nothing moves under `prefers-reduced-motion`, and the page
 * is complete with no JavaScript at all.
 *
 *   data-st-reveal  / .st-reveal          fade + rise once, when first seen
 *   data-st-reveal="stagger" / .st-reveal-stagger
 *                                         the same, for this element's
 *                                         CHILDREN, 60ms apart
 *   data-st-sticky  / .st-sticky-media    the media column of a two-column
 *                                         feature holds still while the claims
 *                                         beside it scroll past
 *   data-st-compare / .st-compare         a comparison table whose header row
 *                                         and first column stay put
 *   .st-lift                              the one hover in the register: up 2px
 *
 * ⚠️ EACH CONTRACT HAS TWO SPELLINGS, AND A PATTERN MUST USE THE CLASS. A core
 * block cannot carry an arbitrary attribute: the editor compares a block's
 * saved HTML with what its own save() would produce, so a `data-st-reveal` on a
 * core/image's <figure> renders perfectly on the front end and shows "this
 * block contains unexpected or invalid content" the moment an author opens the
 * page. Every core block CAN carry `className`. So a pattern writes
 * `"className":"st-reveal"`, which is core-valid and survives a re-save, and
 * the attribute spelling stays for markup a module renders itself (inc/*.php,
 * a render_block filter, a template part), where an attribute is the natural
 * thing to write. One behaviour, two ways in, no bridge in PHP to go stale.
 *
 * ☠️ THE REVEAL IS GATED ON `html.st-motion`, WHICH ONLY assets/st-motion.js
 * ADDS. Written the obvious way — `[data-st-reveal] { opacity: 0 }` plus a
 * script that removes it — the section is INVISIBLE to anyone whose JS did not
 * run: a 404 on the asset, a parse error in an unrelated bundle, a blocked
 * CDN, a crawler that does not execute script. Nothing errors and nothing in
 * any log says a page shipped blank. So the start state lives behind a class
 * the script itself sets, and the no-JS page is simply the finished page.
 *
 * Three px literals and no others: the 8px rise, the 2px lift and the 1px
 * hairline on the comparison table. All three are the register's own numbers
 * (R7 names the first two, R4 the third) and the floor carries a token for
 * none of them — the two motion distances are not layout, and `stRule` is the
 * 2px RULE weight, a heavier divider than a hairline. Everything else here
 * reads a token: the sticky offset is the header-height token, the panel is
 * `stPanel`, the hairline's colour is `ti-line`. The 1024 is the house
 * breakpoint `assets/st-floor.css` already uses throughout.
 */

/* ── Reveal ────────────────────────────────────────────────────────────────
 *
 * Both halves are inside `no-preference`, so a visitor who asks for less
 * motion is served the finished page by the CASCADE and never depends on the
 * script reading the preference too (it does; this is the belt to that brace).
 */
@media ( prefers-reduced-motion: no-preference ) {
	.st-motion [data-st-reveal],
	.st-motion .st-reveal {
		opacity: 0;
		transform: translateY( 8px );
		transition: opacity 400ms ease-out, transform 400ms ease-out;
	}

	.st-motion [data-st-reveal].is-in,
	.st-motion .st-reveal.is-in {
		opacity: 1;
		transform: none;
	}

	/* A staggered parent does not move itself; its children do. */
	.st-motion [data-st-reveal="stagger"],
	.st-motion .st-reveal-stagger {
		opacity: 1;
		transform: none;
		transition: none;
	}

	.st-motion [data-st-reveal="stagger"] > *,
	.st-motion .st-reveal-stagger > * {
		opacity: 0;
		transform: translateY( 8px );
		transition: opacity 400ms ease-out, transform 400ms ease-out;
	}

	.st-motion [data-st-reveal="stagger"].is-in > *,
	.st-motion .st-reveal-stagger.is-in > * {
		opacity: 1;
		transform: none;
	}

	/* Six steps and no more: the seventh child of a staggered row would wait
	   420ms after the first, which reads as a page still loading. The script
	   caps its own delay the same way; this is the sheet's copy of the cap, so
	   a seventh child added to a pattern cannot introduce a longer wait. */
	.st-motion :is([data-st-reveal="stagger"], .st-reveal-stagger).is-in > :nth-child(1) { transition-delay: 0ms; }
	.st-motion :is([data-st-reveal="stagger"], .st-reveal-stagger).is-in > :nth-child(2) { transition-delay: 60ms; }
	.st-motion :is([data-st-reveal="stagger"], .st-reveal-stagger).is-in > :nth-child(3) { transition-delay: 120ms; }
	.st-motion :is([data-st-reveal="stagger"], .st-reveal-stagger).is-in > :nth-child(4) { transition-delay: 180ms; }
	.st-motion :is([data-st-reveal="stagger"], .st-reveal-stagger).is-in > :nth-child(5) { transition-delay: 240ms; }
	.st-motion :is([data-st-reveal="stagger"], .st-reveal-stagger).is-in > :nth-child(n+6) { transition-delay: 300ms; }
}

/* ── Sticky media ──────────────────────────────────────────────────────────
 *
 * No JavaScript. `position: sticky` travels inside its CONTAINING BLOCK, so a
 * sticky media column releases at the end of the section that holds it and
 * needs nobody to tell it to. Below the two-column width it is plain flow.
 *
 * ⚠️ A sticky element's containing block is its nearest scrolling ancestor's
 * box, and `overflow: hidden` ANYWHERE above it switches sticky off silently.
 * The register's sections do not clip; a sibling that needs to clip an image
 * clips the image, never the column (root CLAUDE.md, § the sticky header was
 * dead because neve-fse caps the page at 100%).
 */
@media ( min-width: 1024px ) {
	[data-st-sticky],
	.st-sticky-media {
		position: sticky;
		top: calc( var( --wp--custom--st-header-height ) + 2rem );
		align-self: start;
	}
}

/* ── Comparison table ─────────────────────────────────────────────────────
 *
 * No JavaScript either. The header row and the first cell of every row stay
 * put while the comparison scrolls sideways on a narrow screen, which is the
 * only way a table of four columns is readable at 375.
 */
[data-st-compare],
.st-compare {
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
}

:is([data-st-compare], .st-compare) table {
	width: 100%;
	border-collapse: collapse;
}

:is([data-st-compare], .st-compare) th,
:is([data-st-compare], .st-compare) td {
	border-bottom: 1px solid var( --wp--preset--color--ti-line );
	padding: 0.75rem 1rem;
	text-align: left;
	vertical-align: top;
}

:is([data-st-compare], .st-compare) thead th {
	position: sticky;
	top: 0;
	z-index: 2;
	background: var( --wp--custom--st-panel );
}

/* The criterion column. `tbody th` is the row header the pattern emits; a
   `td` first cell is left alone, because a table whose first column is DATA
   has nothing to pin. */
:is([data-st-compare], .st-compare) tbody th {
	position: sticky;
	left: 0;
	z-index: 1;
	background: var( --wp--custom--st-panel );
}

/* The corner belongs to both, so it outranks both. */
:is([data-st-compare], .st-compare) thead th:first-child {
	left: 0;
	z-index: 3;
}

/* ── The one hover ────────────────────────────────────────────────────────
 *
 * Transform only, so it composites and never reflows the grid around it.
 * `:focus-within` as well as `:hover`: a card reached by keyboard gets the
 * same affordance as one reached by a mouse.
 */
@media ( prefers-reduced-motion: no-preference ) {
	.st-lift {
		transition: transform 220ms ease-out;
	}

	.st-lift:hover,
	.st-lift:focus-within {
		transform: translateY( -2px );
	}
}
