/* =========================================================== ROUND 8 ========
 * Naem, 2026-09-20. Five asks: the home DNA graphic, the storage block, the
 * closing band, the product description's position, and mobile craft.
 *
 * This sheet carries only the LAYOUT half. Copy changes live in the templates
 * and inc/round8-content.php; pictograms live in inc/round5-pictograms.php,
 * which already owns every other drawn mark on the site.
 * ========================================================================= */

/* ------------------------------------------------ #4 · description position --
 * The measured problem, at 1440 on the live page before this rule: the left
 * column ended with the Lab report status panel at y=1290, the right column ran
 * to y=1650 — a 360px void — and the real product description sat at y=3146,
 * 1856px further down. See the long note in woocommerce/single-product.php.
 *
 * `.pd` is declared in style.css (the lead's file, which round sheets override
 * rather than edit) as `grid-template-areas: "gallery info"` with
 * `gap: clamp(28px, 5vw, 64px)` and `align-items: start`. Three corrections:
 *
 * 1. A second row, so the description has somewhere to be. The `info` area
 *    repeats down that row, so the buy column spans both.
 * 2. `row-gap` alone, not `gap`. The 28-64px column gutter is what separates
 *    the gallery from the buy panel and must not change; the ROW gap is the
 *    "Space" under the report panel, so it drops to a tight 22-30px — enough to
 *    read as a new block, not enough to read as a hole.
 * 3. `align-self: stretch` on `.pd-info`. `align-items: start` sizes a grid item
 *    to its content, which would leave the sticky buy panel (style.css:654,
 *    `@media (min-width: 981px)`) with no containing block to travel down. With
 *    the info area spanning two rows and stretching, the panel now stays with
 *    the reader for the whole description instead of scrolling away at y=1650.
 */
.product-wrap .pd {
	grid-template-areas: "gallery info" "desc info";
	row-gap: clamp(22px, 2.4vw, 30px);
}
.product-wrap .pd > .pd-info { align-self: stretch; }
.product-wrap .pd > .pd-desc-incol {
	grid-area: desc;
	margin-top: 0;
	margin-bottom: 0;
}
.product-wrap .pd > .pd-desc-incol > :first-child { margin-top: 0; }

/* Phones and the 681-900 band keep the spec reading order — gallery, then the
 * buy panel, then the long copy. Putting the description inside `.pd-gal` in
 * the markup would have been simpler and would have produced the opposite:
 * price and Add to cart pushed below the full description on every phone. */
@media (max-width: 900px) {
	.product-wrap .pd { grid-template-areas: "gallery" "info" "desc"; }
	.product-wrap .pd > .pd-info { align-self: start; }
}

/* ------------------------------------------------------ #1 · the DNA field --
 * "DNA Main Page is not really visible we wanted to have it more visible
 * beside the product."
 *
 * Observed at 1200x760 on the live home page before this rule. The hero is three
 * bands: the copy column ends at ~30% of the viewport, the product card starts
 * at ~54%, and the braid is only ever SEEN in the ~24% strip between them —
 * everywhere to the right of 54% it is drawn behind an opaque card.
 *
 * round5-hero.css masks the field horizontally with
 *   transparent 0-28% -> .30 at 40% -> .82 at 50% -> #000 from 59% to 92%
 * so full weight begins at 59%: the braid is at its strongest exactly where the
 * card hides it, and runs at roughly a third of its weight across the only strip
 * where it can be seen. That is the whole of "not really visible" — it is not a
 * dim renderer (molecular-field.js draws at uOpacity .96-1.0, round6-field.js at
 * .9) and not a dim colour. It is a ramp aimed 20 points too far right.
 *
 * CORRECTION, after measuring the braid itself rather than only the mask. The
 * paragraph above is half the story and I had it as the whole story: raising the
 * mask weight over the strip does nothing if there is no braid in the strip, and
 * there very nearly was not. Measured at a 1000px viewport: `.hero-copy` ends at
 * x292, `.hero-stage` (the card) starts at x542, so the gap Naem means by
 * "beside the product" is x292-542, 250px wide. The braid is a projected 3D
 * object whose on-screen half-width is 0.2716 x canvasH (round5's own figure,
 * re-derived here): canvas 300+820 wide and 760 tall puts the braid at cx=710,
 * half-width 207, i.e. **x503-917**. Only x503-542 of that lands in the gap —
 * **39px of a 250px gap, 16%**. The other 84% of the only strip where the braid
 * can be seen at all had no braid in it to light up. round5's note says the same
 * thing from the other side: it centred the canvas so the braid "runs behind the
 * product plate's LEFT EDGE", which is precisely the boundary of visibility.
 *
 * So two changes, not one. The ramp moves left (full weight from 39%, not 59%),
 * AND the canvas moves left and grows so the braid's body is actually in the
 * gap: `left: 30% -> 18%`, `height: 116% -> 124%`.
 *
 * Why those two numbers. Height sets the braid's width, so 124% widens the braid
 * from 207 to 221px half-width at 1000px; round5 capped height at 116% because
 * at 160% only ~1.5 turns survive and it "reads as debris", and turns scale as
 * 1/height, so 124% keeps ~1.92 turns — a 4% loss off 2.05, nowhere near the
 * floor. `left: 18%` then puts cx at 590 and the braid at **x369-811**, which
 * covers 369-542 of the gap: **173px of 250, 69%**, up from 16%.
 *
 * What it costs: the braid's right end pulls back from 917 to 811. That is paid
 * in the region x542-966, which is *behind the card* — the least visible part of
 * the composition. The gain is spent in the one strip that is fully exposed.
 *
 * The copy is still clear by measurement, not by assumption: the h1 ends at x292
 * and the braid now starts at x369, 77px of clearance, and the mask is still
 * fully transparent to 28% (x280) with only .40 at 32%.
 *
 * The copy stays protected. The headline column measures x84-435 at 1440 (5.8%
 * to 30.2%), and the mask is still fully transparent to 28% and only .40 at 32%,
 * so nothing lands behind the text. The right edge keeps round5's own falloff
 * (.74 at 96%, .46 at 100%) so the field still reads as continuing off-frame.
 *
 * --r8-dna-start is exposed so the ramp can be re-aimed from one number if the
 * hero's proportions change: it is the point where the braid reaches full
 * weight, and it wants to sit just right of where the copy column ends.
 */
@media (min-width: 901px) {
	.hero-split .molecular-field {
		--r8-dna-start: 39%;
		-webkit-mask-image:
			linear-gradient(to right, transparent 0%, transparent 28%, rgba(0, 0, 0, .40) 32%, rgba(0, 0, 0, .90) 36%, #000 var(--r8-dna-start), #000 92%, rgba(0, 0, 0, .74) 96%, rgba(0, 0, 0, .46) 100%),
			linear-gradient(to bottom, rgba(0, 0, 0, .30) 0%, rgba(0, 0, 0, .88) 12%, #000 22%, #000 80%, rgba(0, 0, 0, .34) 100%),
			radial-gradient(86% 96% at 52% 46%, #000 0%, #000 70%, rgba(0, 0, 0, .90) 88%, rgba(0, 0, 0, .64) 100%);
		mask-image:
			linear-gradient(to right, transparent 0%, transparent 28%, rgba(0, 0, 0, .40) 32%, rgba(0, 0, 0, .90) 36%, #000 var(--r8-dna-start), #000 92%, rgba(0, 0, 0, .74) 96%, rgba(0, 0, 0, .46) 100%),
			linear-gradient(to bottom, rgba(0, 0, 0, .30) 0%, rgba(0, 0, 0, .88) 12%, #000 22%, #000 80%, rgba(0, 0, 0, .34) 100%),
			radial-gradient(86% 96% at 52% 46%, #000 0%, #000 70%, rgba(0, 0, 0, .90) 88%, rgba(0, 0, 0, .64) 100%);
	}

	/* The braid's geometry, not just its weight — see the CORRECTION above.
	   Width is unchanged at 82%: the braid's on-screen width is a function of
	   canvas HEIGHT alone, so 82% is only the particle field's extent and moving
	   `left` is what relocates the braid. */
	.hero-split .molecular-field > .molecular-canvas,
	.hero-split .molecular-field > .molecular-poster {
		inset: -8% auto -8% 18%;
		width: 82%;
		height: 124%;
	}

	/* The radial lobe moved with it — 68% x was a second multiplier centred
	   behind the card, so the strip was losing weight twice. Centred at 52% it
	   peaks over the gap instead. The atmosphere lobes behind the braid follow
	   the same correction; they are the same three lobes round5 tuned, re-aimed
	   left rather than re-coloured or strengthened. */
	.hero-split .molecular-field::before {
		background:
			radial-gradient(46% 62% at 44% 46%, color-mix(in srgb, var(--accent) 13%, transparent) 0%, transparent 78%),
			radial-gradient(40% 52% at 66% 54%, color-mix(in srgb, var(--accent) 9%, transparent) 0%, transparent 82%),
			radial-gradient(74% 92% at 56% 50%, color-mix(in srgb, var(--accent) 6%, transparent) 0%, transparent 84%);
	}
}

/* --------------------------------------------------- #2 · storage glyphs --
 * pharma-pass.css:269 makes `.storage-item` a grid of number / title / body
 * with an 8px gap under a hairline top border. The glyph becomes a fourth row
 * at the top of that stack rather than a new layout: the reference puts its
 * badge above the title too, so the rhythm is the one Naem pointed at.
 *
 * Why a plate and not a bare character: a colour emoji dropped straight onto a
 * dark background reads as pasted-in, which is the "high quality" half of the
 * request. The plate is the accent at 8% with a 22% border — the same two
 * values the step icons already use — so the glyph sits in the site's own
 * furniture instead of floating on it.
 *
 * Glyph choice is a contrast decision, not a taste one. Rendered at 64px and
 * looked at: 🌡 reads (red on white), ❄ reads (bright blue), 🏷 reads (pale
 * yellow) — but 🕶, the first pick for "keep out of direct light", is a
 * near-black frame on a near-black plate and simply disappeared. Swapped to
 * 💡, which is the brightest glyph in the set and names the hazard the rule is
 * about. Rule for any future glyph here: Twemoji artwork that is dark by
 * design cannot be used on this background, whatever it means.
 *
 * The font stack is emoji-only with a `sans-serif` tail. Without it a glyph
 * that has a text presentation (❄ and 🏷 both do) can fall back to the UI font
 * and render as a flat monochrome character on some Android and Windows
 * builds; each character is written with U+FE0F to ask for the colour form,
 * and `font-variant-emoji` states it again for engines that honour it.
 */
.storage-grid--r8 .storage-item { gap: 10px; }
.storage-grid--r8 .se {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	justify-self: start;
	width: 56px;
	height: 56px;
	margin-bottom: 2px;
	border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
	border-radius: 16px;
	background: color-mix(in srgb, var(--accent) 8%, transparent);
	font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Twemoji Mozilla", "EmojiOne Color", sans-serif;
	font-size: 28px;
	font-variant-emoji: emoji;
	line-height: 1;
}

/* Phones: same rule as the four points — one column means rows. `.storage-grid`
 * collapses to `minmax(0, 1fr)` at 680px in round2.css, so below that a stacked
 * card per item is four tall blocks of handling copy; as rows the glyph plate
 * holds the left edge and the number, title and body share a text column. */
@media (max-width: 680px) {
	.storage-grid--r8 .storage-item {
		grid-template-columns: auto minmax(0, 1fr);
		column-gap: 16px;
		row-gap: 2px;
		align-items: center;
	}
	.storage-grid--r8 .storage-item > .se {
		grid-column: 1;
		grid-row: 1 / span 3;
		margin-bottom: 0;
	}
	.storage-grid--r8 .storage-item > .si { grid-column: 2; grid-row: 1; }
	.storage-grid--r8 .storage-item > b   { grid-column: 2; grid-row: 2; }
	.storage-grid--r8 .storage-item > p   { grid-column: 2; grid-row: 3; }
}

/* WordPress core swaps every emoji character for a Twemoji SVG served from
 * s.w.org — verified in the rendered DOM, `.se` contains
 * `<img class="emoji" src=".../1f321.svg">`, not the character. That is the
 * best-quality outcome available without a licensing question: vector, and
 * identical on iOS, Android, Windows and Linux instead of four different house
 * styles. The cost is one third-party request per glyph, which is the trade to
 * put to Naem — turning it off (`remove_action('wp_head','print_emoji_detection_script')`)
 * gives native platform emoji and no request, at the price of ❄ and 🏷 falling
 * back to flat monochrome on some builds.
 *
 * Core's own rule is `img.emoji { height: 1em !important; width: 1em !important }`,
 * so `!important` here is not defensive habit, it is the only way to win: with
 * plain declarations the glyph measured **22px, not the 24px this block asked
 * for** — exactly 1em of the plate's font-size, i.e. core's rule was the one
 * rendering. The size is pinned rather than left to a font-size that no longer
 * renders any text.
 *
 * Sizing measured against the page's own icon scale rather than chosen: the
 * trust row below this section renders its `.r5-ico` at **64x64**, so a 46px
 * plate read as a timid outlier. 56px sits under the trust circle (this is a
 * denser 4-up list) without looking like a different design system. */
.storage-grid--r8 .se img.emoji {
	width: 28px !important;
	height: 28px !important;
	margin: 0;
	padding: 0;
	vertical-align: middle;
}

/* ------------------------------------------ #5 · the field on a phone ------
 * "on the phone version is the 3D background interaction not visible."
 * (Naem, 2026-09-20, after the Round 8 deploy.)
 *
 * He is right. Confirmed at 360 / 390 / 430 with touch emulation on, so the
 * load-time device gates ran the way they do on a handset.
 *
 * This is NOT the desktop bug repeated, and saying so matters, because the
 * desktop medicine would be wrong here. On desktop the braid was in the wrong
 * PLACE — it ran x503-917 against a x292-542 gap, 16% of the corridor. On a
 * phone the geometry is already right: the canvas is 390x918 at 390px wide and
 * the braid's on-screen half-width is 0.2716 x canvasH = 249px, so the braid is
 * wider than the viewport and its box covers the whole copy/plate seam. There
 * IS braid in the corridor. It is simply never lit.
 *
 * Two faults, both in the mask, both measured rather than guessed:
 *
 *   the corridor (copy bottom -> plate top), as % of field height
 *     360px   31.7% -> 42.1%     104px tall
 *     390px   32.0% -> 42.5%     104px
 *     430px   30.8% -> 41.3%     104px
 *
 *   round5-hero.css's phone mask ramps
 *     transparent 0%..37%  ->  .34 at 43%  ->  .90 at 51%  ->  #000 at 57%
 *
 * (1) The mask is still FULLY TRANSPARENT across the first two thirds of that
 * corridor and has reached about a quarter of its weight by the bottom of it.
 * Full weight arrives at 57% — fifteen points BELOW the top of the product
 * plate. The only part of the field drawn at full strength is the part the card
 * is sitting on top of.
 *
 * (2) The radial density lobe peaks `at 52% 72%`. With fieldH ~1000 and the
 * field starting at viewport y ~150-200, 72% lands at y ~870-920, below an
 * 800px fold. The brightest part of the whole field was never on the first
 * screen at all. This is the one that made the hero look dead rather than dim.
 *
 * The fix moves both to the corridor and changes nothing else — no new colour,
 * no raised alpha, no bigger canvas.
 *
 * The vertical ramp holds at ZERO until the copy's own measured bottom edge, so
 * the headline and the trust lines keep exactly the protection they have today.
 * That restraint matters more here than on desktop: .hero-split::after (the
 * contrast scrim) is spent by 22%, so the trust strip below it is already on
 * bare field, and a ramp that opened early would be dimming live text in order
 * to decorate it.
 *
 * CORRECTION — one ramp cannot serve this whole query. The first version of
 * this block held to 31% for everything up to 900px, on the strength of three
 * PHONE widths, and then applied that to a 900px-wide rule. Probing the rest of
 * the band showed the generalisation was wrong:
 *
 *     vw     last text ends     plate top
 *     360        31.7%            42.1%
 *     390        32.0%            42.5%
 *     430        30.8%            41.3%
 *     700        33.9%            48.5%
 *     768        33.8%            48.2%
 *     900        32.6%            46.4%
 *
 * At 768 the string "Research use only" genuinely reaches 33.8% — measured off
 * the text node, not the container, so it is not trailing padding — and a ramp
 * opening at 31% put .385 mask weight behind live, unscrimmed text. Exactly the
 * thing the paragraph above says not to do.
 *
 * So the ramp is three custom properties and the theme's real single-column
 * breakpoint (680, where round2.css collapses the grids) splits them. Each band
 * starts its ramp below the LOWEST text edge in that band, not the average.
 *
 * Result: on a phone the corridor's weight goes 0 -> .70 -> 1.0 across
 * 32.5-37.5%, against a plate top of 41.3% at worst, so the field reaches full
 * strength with ~4 points of corridor still to run — where before it was at
 * zero for two thirds of the corridor and only hit full weight at 57%, behind
 * the card. Comparable to the 69% of the gap the desktop fix won.
 */
@media (max-width: 900px) {
	.hero-split .molecular-field {
		/* Tablet band (681-900). Lowest text edge measured in it is 33.9% at
		   700px, so the ramp is still dark there and opens at 34.5%. */
		--r8-m-hold: 34.5%;
		--r8-m-half: 37.5%;
		--r8-m-full: 40.5%;

		-webkit-mask-image:
			linear-gradient(to bottom, transparent 0%, transparent var(--r8-m-hold), rgba(0, 0, 0, .70) var(--r8-m-half), #000 var(--r8-m-full), #000 86%, rgba(0, 0, 0, .42) 94%, transparent 99%),
			linear-gradient(to right, rgba(0, 0, 0, .55) 0%, #000 12%, #000 88%, rgba(0, 0, 0, .55) 100%),
			radial-gradient(120% 58% at 50% 39%, #000 0%, #000 60%, rgba(0, 0, 0, .84) 86%, rgba(0, 0, 0, .52) 100%);
		mask-image:
			linear-gradient(to bottom, transparent 0%, transparent var(--r8-m-hold), rgba(0, 0, 0, .70) var(--r8-m-half), #000 var(--r8-m-full), #000 86%, rgba(0, 0, 0, .42) 94%, transparent 99%),
			linear-gradient(to right, rgba(0, 0, 0, .55) 0%, #000 12%, #000 88%, rgba(0, 0, 0, .55) 100%),
			radial-gradient(120% 58% at 50% 39%, #000 0%, #000 60%, rgba(0, 0, 0, .84) 86%, rgba(0, 0, 0, .52) 100%);
		-webkit-mask-composite: source-in;
		mask-composite: intersect;
	}

	/* Phone band. 680 is the theme's real single-column breakpoint — round2.css
	   collapses .category-grid / .purchase-grid / .storage-grid to one column
	   here — so the hero's proportions change at the same place. Lowest text
	   edge in this band is 30.8% at 430px; the ramp opens at 32.5%, which is
	   below every measured text edge AND leaves the full-weight stop 3.8 points
	   clear of the nearest plate top (41.3%). */
	@media (max-width: 680px) {
		.hero-split .molecular-field {
			--r8-m-hold: 32.5%;
			--r8-m-half: 35%;
			--r8-m-full: 37.5%;
		}
	}

	/* The horizontal edges go .30 -> .55 for the same reason the desktop braid
	   moved left: on a phone the card is nearly full-bleed, so "beside the
	   product" is the thin margin each side of it. That strip was the only place
	   the helix was visible at all before this change, and it was visible at
	   less than a third weight. The vertical gate above still zeroes everything
	   over the copy, so a brighter edge cannot reach the headline. */

	/* Same two atmosphere lobes round5 tuned, re-aimed rather than recoloured or
	   strengthened (15% -> 14%, 8% -> 8%). They peaked at 70% and 82%, i.e. both
	   below the fold, for the same reason the radial mask did. */
	.hero-split .molecular-field::before {
		background:
			radial-gradient(76% 30% at 50% 38%, color-mix(in srgb, var(--accent) 14%, transparent) 0%, transparent 78%),
			radial-gradient(98% 44% at 50% 52%, color-mix(in srgb, var(--accent) 8%, transparent) 0%, transparent 84%);
	}

	/* --- the interaction itself ------------------------------------------
	   round7-points.css defines the hero's pointer glow — .molecular-field
	   ::after — inside `@media (hover: hover) and (pointer: fine) and
	   (prefers-reduced-motion: no-preference)`, and round7-flashlight.js tests
	   the same query before it will track anything. On a touch device the layer
	   is therefore not merely unlit: it is never created. That is the literal
	   sense in which the interaction is "not visible" on a phone — there is
	   nothing there for the field to respond with.
	   Gating a mouse-follow effect off touch was right. Leaving a touch device
	   with no interaction at all was the part that needed finishing.

	   The layer is declared here for coarse pointers and lit by
	   assets/js/round8-field-touch.js while a finger is down. Every safety
	   argument round7 made for the desktop version still holds unchanged,
	   because this is the same pseudo-element in the same place: it is INSIDE
	   the field, so it inherits the mask above and cannot appear behind the
	   headline no matter where the finger goes; the field is z-index 1, under
	   both contrast washes and under the copy/product grid; and it sits at
	   z-index 0, BEHIND the canvas, so the helix is lit from behind and never
	   fogged.

	   A separate class from round7's `is-r7-lit` on purpose: the two scripts
	   are then incapable of fighting over one flag on a hybrid device that
	   satisfies both queries.

	   Alpha is round7's own --r7-flash-hero-alpha, reused rather than re-picked,
	   because that 19% is a contrast-checked number and re-choosing it here
	   would put text contrast back in play. Only the radius is phone-specific:
	   the desktop clamp bottoms out at 240px, which on a 390px screen is a wash
	   over most of the width rather than a pool of light under a fingertip. */
	@media (pointer: coarse) and (prefers-reduced-motion: no-preference) {
		.hero-split .molecular-field {
			--fx: 50%;
			--fy: 38%;
			--r8-touch-radius: clamp(150px, 44vw, 250px);
		}

		.hero-split .molecular-field::after {
			content: "";
			position: absolute;
			inset: 0;
			z-index: 0;
			pointer-events: none;
			opacity: 0;
			transition: opacity var(--r7-flash-fade) var(--ease);
			background:
				radial-gradient(
					circle var(--r8-touch-radius) at var(--fx) var(--fy),
					color-mix(in srgb, var(--accent) var(--r7-flash-hero-alpha), transparent) 0%,
					color-mix(in srgb, var(--accent) calc(var(--r7-flash-hero-alpha) * .4), transparent) 44%,
					transparent 100%
				);
		}

		.hero-split .molecular-field.is-r8-touched::after { opacity: 1; }

		/* "Pause molecular motion" means the whole hero holds still, so the glow
		   goes out in CSS immediately as well as in the script. Same contract as
		   round7-points.css:312. */
		.hero-split .molecular-field.is-paused::after { opacity: 0; }
	}
}

/* Windows High Contrast and friends replace backgrounds; round7-points.css
   already drops its own glow there and this one is the same kind of layer. */
@media (forced-colors: active) {
	.hero-split .molecular-field::after { display: none; }
}
