/*!
 * Section: Hero
 *
 * Scoped CSS for inc/sections/hero.php. Stub partial — visual composition
 * lands during page-migration WPs (RENDER-WP5–WP11) when each hero is wired up.
 *
 * @package Mystwood_Studios_AB
 * @since   Topic 17 RENDER-WP1
 */

/* Topic 23 REDESIGN-WP7 — section rhythm tokens applied.
 * Hero padding-block anchors to --mw-space-section (120px) with a responsive
 * floor so mobile heroes still breathe at half-rhythm. Headline-to-subhead
 * spacing wires --mw-space-headline-to-paragraph (24px); subhead-to-CTA wires
 * --mw-space-paragraph-to-cta (36px). H1 clamp ceiling raised to 4.5rem (72px)
 * to match the new type scale (H1 hero = 72px) settled in WP-1. */
.mw-section--hero {
	padding-block: clamp(3rem, 8vw, var(--mw-space-section, 120px));
}

.mw-section--hero .mw-section__inner {
	max-width: var(--mw-content-width, 72rem);
	margin-inline: auto;
	padding-inline: clamp(1rem, 4vw, 2rem);
	text-align: center;
}

.mw-section--hero .mw-section__eyebrow {
	font-size: 0.875rem;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	margin-block-end: 0.75rem;
	opacity: 0.85;
}

.mw-section--hero .mw-section__headline {
	font-family: var(--mw-font-display, system-ui, sans-serif);
	font-size: clamp(2rem, 5vw, 4.5rem);
	line-height: 1.1;
	margin-block: 0 var(--mw-space-headline-to-paragraph, 24px);
}

.mw-section--hero .mw-section__subhead {
	font-size: clamp(1rem, 1.5vw, 1.25rem);
	line-height: 1.5;
	max-width: 48rem;
	margin-inline: auto;
	margin-block-end: var(--mw-space-paragraph-to-cta, 36px);
}

.mw-section--hero .mw-section__cta {
	margin-block-start: var(--mw-space-paragraph-to-cta, 36px);
}

/* -----------------------------------------------------------------------
 * K-World brand variant (Topic 17 RENDER-WP7)
 *
 * Section background painting by data-bg-token, plus the dark-glass cascade
 * override from Topic 13 KWORLD-WP4-B: on kw-castle-stone hero (the /k-world
 * cover backdrop), body text and eyebrow remain readable through a slightly
 * cooled foreground. Tone-locked to Topic 13 Figma 90:6.
 * ----------------------------------------------------------------------- */
.mw-section--hero[data-bg-token="kw-sky"]          { background-color: var(--kw-sky-surface);  color: #040420; }
.mw-section--hero[data-bg-token="kw-gold"]         { background-color: var(--kw-gold-surface); color: #040420; }
.mw-section--hero[data-bg-token="kw-cream"]        { background-color: var(--kw-cream);        color: #040420; }
.mw-section--hero[data-bg-token="kw-sunset"]       { background-color: var(--kw-sunset);       color: #ffffff; }
.mw-section--hero[data-bg-token="kw-castle-stone"] { background-color: var(--kw-castle-stone); color: #ffffff; }

/* -----------------------------------------------------------------------
 * Imageless-hero boundary (Topic 36 CORR-WP1 Item 2)
 *
 * `--kw-sky-surface` / `--kw-gold-surface` flipped to white. No live
 * template composes an IMAGELESS hero on either token today (every kw-sky /
 * kw-gold hero call site passes `image` — verified sitewide), so this is a
 * forward-only repair, not a rendered-page fix. Recorded rather than
 * skipped: an imageless hero on either token would now be a plain white
 * band sitting directly under `.site-header`, which is `position: sticky`
 * and `background: transparent` until scrolled — its light (#FAF8FF) nav
 * text would read against white with no separation at the top of the page.
 * A thin ink hairline gives the band an edge to end on if one ever ships,
 * without touching the header component (out of this WP's file list).
 * ----------------------------------------------------------------------- */
.mw-section--hero[data-bg-token="kw-sky"]:not([data-has-image]),
.mw-section--hero[data-bg-token="kw-gold"]:not([data-has-image]) {
	box-shadow: inset 0 1px 0 rgba(4, 4, 32, 0.14);
}

/* Topic 13 KWORLD-WP4-B dark-glass cascade override — load-bearing. */
.mw-section--hero[data-bg-token="kw-castle-stone"] .mw-section__eyebrow,
.mw-section--hero[data-bg-token="kw-castle-stone"] .mw-section__subhead {
	color: rgba(255, 255, 255, 0.92);
}

/* K-World hub hero keeps the partial's native centered layout. The book-product
 * "product hero" composition (kw-sky cover + dual-CTA) is implemented via the
 * media-text section partial in book-product.php so we do not extend the hero
 * partial's markup contract.
 * Topic 13 KWORLD-WP4-B dark-glass cascade is preserved above; the AGES 6+
 * pill is embedded inline in the hero headline via wp_kses_post-safe HTML
 * in hub.php (anchor stub falls back to plain text if KSES strips). */

/* -----------------------------------------------------------------------
 * Lore brand variant (Topic 17 RENDER-WP8)
 *
 * Topic 14 manuscript/codex identity — parchment / vellum backgrounds,
 * ink foreground, system-serif headlines (Georgia stack, zero KB). All
 * three Kaeros landing heroes (hub / cosmology / timeline) sit on
 * lore-parchment so the per-page accent shows up on the *CTA band* at
 * the foot of each page, not on the hero. The eyebrow carries the
 * page-specific KAEROS · {SECTION} label.
 * ----------------------------------------------------------------------- */
.mw-section--hero[data-bg-token="lore-parchment"] { background-color: var(--lore-parchment); color: var(--lore-ink); }
.mw-section--hero[data-bg-token="lore-vellum"]    { background-color: var(--lore-vellum);    color: var(--lore-ink); }
.mw-section--hero[data-bg-token="lore-hub"]       { background-color: var(--lore-hub);       color: var(--lore-parchment); }
.mw-section--hero[data-bg-token="lore-cosmology"] { background-color: var(--lore-cosmology); color: var(--lore-parchment); }
.mw-section--hero[data-bg-token="lore-timeline"]  { background-color: var(--lore-timeline);  color: var(--lore-parchment); }

/* Topic 14 design language — system-serif H1 on lore-tier heroes. */
.mw-section--hero.mw-section--brand-lore .mw-section__headline {
	font-family: var(--mw-font-lore-serif, Georgia, "Times New Roman", serif);
	font-style: normal;
	letter-spacing: -0.005em;
}

.mw-section--hero.mw-section--brand-lore .mw-section__eyebrow {
	font-family: var(--mw-font-lore-serif, Georgia, "Times New Roman", serif);
	font-style: italic;
	letter-spacing: 0.12em;
	opacity: 0.78;
}

/* -----------------------------------------------------------------------
 * Eye-tree scene (Topic 23 REDESIGN-WP5)
 *
 * Layered SVG background composition behind the hero content stack. Opt-in
 * via background_scene="eye-tree" in inc/sections/hero.php. Only paints when
 * data-scene="eye-tree" is present, so existing heroes are unaffected.
 *
 * z-order back → front:
 *   1. tree         — gentle sway @keyframes, transform-origin bottom-center
 *   2. particles    — staggered opacity pulse via per-particle animation-delay
 *   3. eyes         — pupils cursor-follow via CSS custom props from home-scene.js
 *   4. hills        — static silhouette band at bottom
 *
 * Reduced-motion: @keyframes disabled below; JS never attaches the mousemove
 * listener (assets/js/home-scene.js gates on the same media query).
 *
 * Tuning knobs (overridable per page via inline style if ever needed):
 *   --mw-eye-cursor-strength       (default 0.02 — per-pixel cursor offset)
 *   --mw-tree-sway-angle           (default 1.2deg — max sway each direction)
 *   --mw-tree-sway-duration        (default 8s)
 *   --mw-particle-pulse-opacity-min (default 0.3)
 *   --mw-particle-pulse-duration    (default 4s)
 * ----------------------------------------------------------------------- */
.mw-section--hero[data-scene="eye-tree"] {
	--mw-eye-cursor-strength: 0.02;
	--mw-tree-sway-angle: 1.2deg;
	--mw-tree-sway-duration: 8s;
	--mw-particle-pulse-opacity-min: 0.3;
	--mw-particle-pulse-duration: 4s;

	position: relative;
	overflow: hidden;
	isolation: isolate;
	min-block-size: clamp(560px, 75vh, 860px); /* Topic 24 WP-9 fixup-2 — expanded after tree+eyes went DORMANT; the WP-1 tighten (56vh/640px) was eyes-overlap mitigation that no longer applies. */
}

.mw-section--hero[data-scene="eye-tree"] .mw-section__scene {
	position: absolute;
	inset: 0;
	pointer-events: none;
	z-index: 0;
}

.mw-section--hero[data-scene="eye-tree"] .mw-section__inner {
	position: relative;
	z-index: 1;
}

.mw-section--hero[data-scene="eye-tree"] .mw-section__scene-layer {
	position: absolute;
	display: block;
	max-inline-size: none;
}

/* DORMANT — Topic 24 WP-9 fixup (2026-05-28). Tree silhouette layer parked per
 * user visual review. <img> in hero.php is also DORMANT-wrapped. Re-enable both
 * together. Behind-tree glow gradient below now carries the visual interest.
 *
.mw-section--hero[data-scene="eye-tree"] .mw-section__scene-layer--tree {
	inset-block-start: 0;
	inset-inline-start: 50%;
	inline-size: clamp(520px, 60vw, 880px);
	block-size: auto;
	transform-origin: 50% 100%;
	translate: -50% 0;
	animation: mw-home-tree-sway var(--mw-tree-sway-duration) ease-in-out infinite;
	z-index: 1;
}
 */

/* Layer 2 — particles, mid. Spread across the upper canopy band. */
.mw-section--hero[data-scene="eye-tree"] .mw-section__scene-layer--particles {
	inset-block-start: 6%;
	inset-inline-start: 50%;
	inline-size: clamp(520px, 60vw, 880px);
	block-size: auto;
	translate: -50% 0;
	z-index: 2;
}

.mw-section--hero[data-scene="eye-tree"] .mw-particle {
	animation: mw-home-particle-pulse var(--mw-particle-pulse-duration) ease-in-out infinite;
	transform-origin: center;
	/* Topic 24 FIXUP-WP1 — glow now visible because particles are inline SVG;
	 * filter on a shape applies via the rendered fill. */
	filter: drop-shadow(0 0 6px var(--mw-color-primary-glow, #A8A7FF));
}

/* Behind-tree glow (Topic 24 WP-9). Gives the dark tree silhouette
 * (#040420..#05052B Figma-parity art) something to read against without
 * recoloring the SVG paths. Soft indigo radial-gradient sized to the
 * canopy zone, centred slightly above the tree's vertical midpoint.
 * Composes with the position rules in the .mw-section__scene block above. */
.mw-section--hero[data-scene="eye-tree"] .mw-section__scene {
	/* Topic 24 WP-9 fixup — glow boosted (alpha 0.35→0.5, ellipse 60×70 → 70×80)
	 * after tree silhouette went DORMANT. The glow is now the hero's primary
	 * visual element; needs more presence to carry the composition. */
	background-image: radial-gradient(
		ellipse 70% 80% at 50% 55%,
		rgba(74, 71, 144, 0.5) 0%,
		rgba(74, 71, 144, 0.25) 35%,
		transparent 75%
	);
}

/* DORMANT — Topic 24 WP-9 (2026-05-28). Eyes overlay parked after post-close
 * visual review. Re-enable steps in inc/sections/hero.php DORMANT block.
 *
.mw-section--hero[data-scene="eye-tree"] .mw-section__scene-layer--eyes {
	inset-block-start: 18%;
	inset-inline-start: 50%;
	inline-size: clamp(360px, 42vw, 620px);
	block-size: auto;
	translate: -50% 0;
	z-index: 3;
}

.mw-section--hero[data-scene="eye-tree"] .mw-eye__pupil {
	transform: translate(
		calc((var(--mw-eye-x, 0) - var(--eye-cx, 0)) * var(--mw-eye-cursor-strength) * 1px),
		calc((var(--mw-eye-y, 0) - var(--eye-cy, 0)) * var(--mw-eye-cursor-strength) * 1px)
	);
	transition: transform 0.18s ease-out;
}

.mw-section--hero[data-scene="eye-tree"] .mw-eye__sclera {
	transform-box: fill-box;
	transform-origin: center;
	animation: mw-home-eye-blink 5s ease-in-out infinite;
}
.mw-section--hero[data-scene="eye-tree"] svg.mw-section__scene-layer--eyes g:nth-child(3n+1) .mw-eye__sclera { animation-delay: 0.8s; }
.mw-section--hero[data-scene="eye-tree"] svg.mw-section__scene-layer--eyes g:nth-child(3n+2) .mw-eye__sclera { animation-delay: 2.4s; }
.mw-section--hero[data-scene="eye-tree"] svg.mw-section__scene-layer--eyes g:nth-child(3n+3) .mw-eye__sclera { animation-delay: 4.1s; }
 */

/* Layer 4 — hills, foreground floor. Spans full width, slammed to bottom. */
/* Topic 24 WP-9 fixup — hills pushed down + capped to a thin band so the
 * (now dominant) glow gradient + particles are uncovered at the lower hero. */
.mw-section--hero[data-scene="eye-tree"] .mw-section__scene-layer--hills {
	inset-block-end: -8%;
	inset-inline-start: 0;
	inline-size: 100%;
	block-size: 140px;
	object-fit: cover;
	object-position: top center;
	z-index: 4;
}

/* DORMANT — Topic 24 WP-9 fixup. Re-enable with the tree layer rule above.
@keyframes mw-home-tree-sway {
	0%, 100% { transform: rotate(calc(var(--mw-tree-sway-angle) * -1)); }
	50%      { transform: rotate(var(--mw-tree-sway-angle)); }
}
 */

@keyframes mw-home-particle-pulse {
	0%, 100% { opacity: var(--mw-particle-pulse-opacity-min); }
	50%      { opacity: 0.85; }
}

/* DORMANT — Topic 24 WP-9. Re-enable with Region 1.
@keyframes mw-home-eye-blink {
	0%, 92%, 100% { transform: scaleY(1); }
	95%           { transform: scaleY(0.05); }
}
 */

/* Reduced-motion fallback: freeze the scene to a static composite. JS never
 * attaches the mousemove listener (home-scene.js gates on the same media
 * query) so pupils render at center. */
@media (prefers-reduced-motion: reduce) {
	.mw-section--hero[data-scene="eye-tree"] .mw-section__scene-layer--tree,
	.mw-section--hero[data-scene="eye-tree"] .mw-particle /* ,
	.mw-section--hero[data-scene="eye-tree"] .mw-eye__sclera DORMANT WP-9 */ {
		animation: none;
	}
	/* DORMANT — Topic 24 WP-9
	.mw-section--hero[data-scene="eye-tree"] .mw-eye__pupil {
		transform: none;
		transition: none;
	}
	*/
}

/* -----------------------------------------------------------------------
 * Photographic hero (Topic 27 BRAND-WP4 Foundation, S1)
 *
 * Opt-in via image="" on inc/sections/hero.php → data-has-image="true".
 * Full-bleed cover image + gradient scrim behind the centered content stack,
 * mirroring the Claude Design CinematicHero. Only paints when the attr is
 * present, so text-only heroes are unaffected. Independent of the home
 * eye-tree `data-scene` path (a page passes one or the other, never both).
 * ----------------------------------------------------------------------- */
.mw-section--hero[data-has-image] {
	position: relative;
	isolation: isolate;
	overflow: hidden;
	min-block-size: clamp(420px, 60vh, 640px);
	display: flex;
	flex-direction: column;
	justify-content: center;
	/* Full-bleed up under the transparent sticky header so the cover art reaches
	 * the very top of the viewport and the frosted menu floats over it — the
	 * Claude Design cinematic-hero move (.chero { margin-top: calc(-1 *
	 * --mw-header-h) }, site.css). Without it the hero starts below the ~88px
	 * header, leaving a dark body-bg strip above the art. The inner stack gets a
	 * compensating top pad below so the breadcrumb/title still clear the menu. */
	margin-block-start: calc(-1 * var(--mw-header-h));

	/* Topic 34 SEAM-WP1 floor. Always defined so `var(--mw-seam-line)` can
	 * never be invalid at computed-value time — see the SEAM LINE block
	 * beside the :has() table. (0,2,0), so both the painters (0,4,0) and
	 * the :has() rows (0,5,0) override it on weight, which is the intent. */
	--mw-seam-line: transparent;

	/* Companion floor for the line's IMAGE (2026-08-22 games ruling). The
	 * games hub's gilt is a gradient, not a flat bar: it fades out at both
	 * ends and is brightest just right of centre so it reads as firelight
	 * rather than as a UI rule (games.css, FIX-WP21). A pairing that wants
	 * that look sets this; every other pairing leaves it at `none`, which
	 * is background-image's own initial value, so the flat --mw-seam-line
	 * shows through untouched. Floored for the same reason as the colour
	 * above: an undefined custom property is invalid at computed-value
	 * time, and that failure is invisible to screenshot QA. */
	--mw-seam-line-image: none;
}

.mw-section--hero[data-has-image] .mw-section__bg {
	position: absolute;
	inset: 0;
	z-index: -2;
}

.mw-section--hero[data-has-image] .mw-section__bg img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
	display: block;
}

/*
 * Crop framing — `image_position` on the hero partial (data-image-pos).
 *
 * `cover` keeps the centre of the art, which is only correct when the subject
 * is in the centre. It is not on /about/: the workshop's sketchbook sits lower
 * left and the familiar lower right, so a narrow viewport kept the empty stone
 * archway between them and the hero read as a plain dark wall.
 *
 * These rules carry no default — a hero without data-image-pos never matches
 * one, so it keeps `object-position: center` from the rule above and renders
 * exactly as it did before this block existed. Values are allowlisted in
 * hero.php, so a typo cannot reach here.
 */
.mw-section--hero[data-has-image][data-image-pos="left-bottom"] .mw-section__bg img {
	object-position: 28% bottom;
}

.mw-section--hero[data-has-image][data-image-pos="right-bottom"] .mw-section__bg img {
	object-position: 72% bottom;
}

.mw-section--hero[data-has-image][data-image-pos="top"] .mw-section__bg img {
	object-position: center top;
}

.mw-section--hero[data-has-image][data-image-pos="bottom"] .mw-section__bg img {
	object-position: center bottom;
}

.mw-section--hero[data-has-image] .mw-section__scrim {
	position: absolute;
	inset: 0;
	z-index: -1;
	background: linear-gradient(180deg, rgba(4, 4, 32, 0.35) 0%, rgba(4, 4, 32, 0.72) 100%);
}

.mw-section--hero[data-has-image] .mw-section__inner {
	position: relative;
	/* 2, not 1, since Topic 34 SEAM-WP2. The seam layer needs to paint ABOVE the
	 * tone bleed and there is no integer between 0 and 1. Content stays above
	 * every decorative layer, which is the invariant that actually matters. */
	z-index: 2;
	color: #ffffff;
	/* Clear the floating header the section now underlaps (see margin-block-start
	 * above) so the breadcrumb/headline never sit beneath the menu — mirrors the
	 * Claude Design .chero__inner top padding. */
	padding-block-start: var(--mw-header-h);
}

/* Breadcrumb trail (restores the .mw-breadcrumb base styling dropped when
 * patterns.css was removed in WP-2 — inc/breadcrumbs.php renders
 * .mw-breadcrumb__inner > ol > li, with the current crumb as
 * [aria-current="page"]). Without these the <ol> falls back to a raw numbered
 * list. Kept unscoped so any breadcrumb resolves; used in-hero today. */
.mw-breadcrumb {
	font-size: 0.8125rem;
	letter-spacing: 0.02em;
}

.mw-breadcrumb__inner ol {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.4rem;
}

.mw-breadcrumb li {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
}

.mw-breadcrumb li + li::before {
	content: "\203A"; /* rsaquo separator */
	opacity: 0.45;
}

.mw-breadcrumb a {
	color: inherit;
	opacity: 0.7;
	text-decoration: none;
}

.mw-breadcrumb a:hover,
.mw-breadcrumb a:focus-visible {
	opacity: 1;
	text-decoration: underline;
}

.mw-breadcrumb [aria-current="page"] {
	opacity: 1;
	font-weight: 600;
}

/* In-hero breadcrumb (S7) — delegates to mystwood_studios_ab_breadcrumbs();
 * trim the default block margin and center the trail in the hero's stack. */
.mw-section--hero .mw-breadcrumb {
	margin: 0 0 1rem;
	text-align: center;
}

.mw-section--hero .mw-breadcrumb__inner ol {
	justify-content: center;
}

/* Hero badge chip (S1) — "Demo soon" / "In development" / "Book 1" pill above
 * the headline. Translucent-white default reads on dark/image heroes. */
.mw-hero__badge {
	margin: 0 0 0.75rem;
}

.mw-hero__badge-chip {
	display: inline-block;
	padding: 0.3rem 0.9rem;
	border-radius: 999px;
	font-family: var(--mw-font-display, system-ui, sans-serif);
	font-size: 0.8rem;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	background: rgba(255, 255, 255, 0.16);
	border: 1px solid rgba(255, 255, 255, 0.28);
	color: #ffffff;
}

/* On a photographic hero the scrim is dark — force the breadcrumb trail to the
 * light foreground so it reads against the image (Topic 27 BRAND-WP4 Iter 3). */
.mw-section--hero[data-has-image] .mw-breadcrumb,
.mw-section--hero[data-has-image] .mw-breadcrumb a,
.mw-section--hero[data-has-image] .mw-breadcrumb span {
	color: rgba(255, 255, 255, 0.85);
}

.mw-section--hero[data-has-image] .mw-breadcrumb a:hover,
.mw-section--hero[data-has-image] .mw-breadcrumb a:focus-visible {
	color: #ffffff;
}

/* -----------------------------------------------------------------------
 * K-World hero → outgoing-tone bleed (Topic 33 FIX-WP7 Task E)
 *
 * Defect 7: the hero art stops at a hard horizontal edge and the torn-paper
 * seam below reads as a guillotine cut rather than paper laid over the
 * scene. Do NOT mask .mw-section__bg — the scrim above (linear-gradient
 * rgba(4,4,32,.35)→.72) is DARKEST at the bottom, so fading the art there
 * would yield navy-over-tone mud, not a clean bleed. Instead this layers a
 * gradient ABOVE the scrim and BELOW the content stack:
 *
 *   .mw-section__bg     z-index -2  (art — unmodified)
 *   .mw-section__scrim  z-index -1  (legibility gradient — unmodified)
 *   ::after (this rule)  z-index  0  (outgoing-tone fade)
 *   ::before            z-index  1  (SEAM shape, Topic 34 - absent unless [data-seam])
 *   .mw-section__inner  z-index  2  (content)
 *
 * [data-has-image]'s overflow:hidden clips the pseudo-element safely.
 *
 * The fade terminus is each tone's *surface* token (kw-sky-surface etc.),
 * mirroring the K-World painter block above — NOT the raw accent
 * (kw-sky etc.). Post-FIX-WP6 those are different colours (#8FA8DC vs
 * #3FA7D6): the band the hero leads into paints in the surface tone, so
 * bleeding to the accent would reproduce the seam mismatch this system's
 * outgoing-tone divider convention exists to prevent.
 *
 * Scoped to .mw-section--brand-k-world so the /clear-the-skies/ studio hero
 * (is_brand "studio", bg_token "studio-dark") is unreachable and stays
 * byte-identical to its FIX-2..FIX-5 sign-off.
 * ----------------------------------------------------------------------- */
/* -----------------------------------------------------------------------
 * Topic 33 FIX-WP24 Task A — the fade is EASED, and that is the whole fix.
 *
 * Owner directive 2026-07-30 on /k-world/: "Make the Hero image bleed
 * seamless — now you see the cut, so a bit more and better gradient control".
 *
 * MEASURED, not eyeballed. Sampling rendered pixels down the seam at 1440
 * proved there is NO colour step at the join — the old 2-stop ramp lands
 * exactly on #8FA8DC and the band continues it. The visible cut was a MACH
 * BAND at the fade's own terminus: luminance was still climbing at
 * +0.0060/px at y=684 and hit exactly 0.000000/px by y=702. The sharpest
 * curvature anywhere on the page profile sat at y=696 — 4x anything inside
 * the fade. A 2-stop linear ramp is steepest AT its endpoint by
 * construction, so it always ends on a corner, and the eye draws the line
 * the pixels do not contain. Lengthening alone cannot fix that; only easing
 * the ends to zero slope can. Hence "better gradient control", exactly.
 *
 * The ramp is an 11-stop smoothstep, s(t) = t^2 * (3 - 2t) — zero slope at
 * BOTH t=0 and t=1, so neither end leaves a corner. Measured after:
 * corner 0.001349 -> 0.000536 (2.5x gentler), peak slope 0.006324 ->
 * 0.004136, and slope at the old corner 0.005975 -> 0.001311.
 *
 * WHY color-mix AND a plain 2-stop declaration above it: the eased ramp
 * needs the tone at intermediate alphas, and the tone is a var() we must not
 * duplicate as a literal. `color-mix()` keeps the token as the single source
 * of truth. The 2-stop `background-image` on the line before is a real
 * fallback, not decoration — if an engine cannot parse color-mix the whole
 * eased declaration is dropped and the cascade falls back to TODAY'S render
 * (a soft cut), never to nothing and never to a slab.
 *
 * REJECTED, and worth recording: doing the ease as a `mask-image` with a
 * flat `background-color`. It measures the same, but it inverts the failure
 * mode — any engine that drops the mask paints an OPAQUE tone slab over the
 * bottom quarter of the hero art, which is far worse than the defect being
 * fixed and is invisible to a screenshot-only QA pass. Also rejected:
 * keeping the old gradient AND adding an eased mask; the two ramps multiply
 * (d/dt[t*s(t)] = 1 at t=1), which puts the corner straight back.
 *
 * `inset-block-end: -1px` matches the FIX-WP23 rule below — it guards the
 * sub-pixel sliver that a fractional layout height can leave at the join.
 *
 * DIVERGENCE, deliberate and logged: /clear-the-skies/ (FIX-WP23, below)
 * keeps a 4-stop rgba ramp rather than this smoothstep. It is NOT worth
 * re-deriving there — that hero terminates on #0e0841, and a corner in the
 * dark is far below the visibility this one had on #8FA8DC / #F2D072.
 * Recorded in FIX-WP24's Results so the FIX-WP9 sweep reads two mechanisms
 * as an intentional split, not drift.
 * ----------------------------------------------------------------------- */
/* FIX-WP27 broadened this from K-World-only to K-World + studio. The ramp body
 * is unchanged — only the selector list and the painter set below grew, so every
 * K-World hero renders byte-identically. Generalising it here (rather than
 * copying it) is deliberate: FIX-WP35 consumes the same --mw-bleed-tone
 * machinery for the band->footer fade.
 *
 * /clear-the-skies/ is ALSO brand-studio + data-has-image and so matches this
 * rule — but its own page-scoped block further down this file overrides it at
 * EQUAL specificity by source order, keeping FIX-WP23's 4-stop haze whose stops
 * FIX-WP25 re-derived and measured landing on the premise band at ratio 1.0000.
 * That is an intentional two-mechanism split, recorded as close-memo follow-up
 * #13, NOT drift. If you ever reorder this file, that override must stay after
 * this rule or Clear the Skies silently changes. */
/* GEOMETRY half (hero-only). FIX-WP35 split this rule in two so the ramp
 * *paint* below could be shared with a consumer that is not a pseudo-element
 * and needs none of these properties. Nothing here changed. */
.mw-section--hero[data-has-image].mw-section--brand-k-world::after,
.mw-section--hero[data-has-image].mw-section--brand-studio::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	inset-block-end: -1px;
	block-size: clamp(140px, 24%, 300px);
	z-index: 0;
	pointer-events: none;
}

/* -----------------------------------------------------------------------
 * THE SHARED SMOOTHSTEP RAMP — ONE definition, two consumers (FIX-WP35)
 *
 * Consumer 1: the hero bleeds above (K-World + studio), as a ::after.
 * Consumer 2: the band->footer fade, painted as a background-image directly
 *             on the page's LAST band. Declared here rather than copied into
 *             _footer.css because this project has twice been bitten by two
 *             rival copies of one visual mechanism drifting apart (the two
 *             hero bleeds FIX-WP27 reconciled; the divider convention that
 *             sat documented backwards until 2026-07-31).
 *
 * REJECTED, and worth recording so nobody retries it: hoisting these stops
 * into a `:root { --mw-ramp: ... }` custom property so both consumers could
 * say `background-image: var(--mw-ramp)`. It does not work. A custom
 * property's nested var()s are substituted at computed-value time ON THE
 * ELEMENT WHERE IT IS DECLARED, and the already-substituted value is what
 * inherits. Declared at :root, the inner `var(--mw-bleed-tone)` would resolve
 * against :root — where it is undefined — and every descendant would inherit
 * that guaranteed-invalid value. A shared SELECTOR LIST is the only way to
 * keep one definition here, which is why the rule is split rather than
 * tokenised.
 *
 * Consumer 2's terminus is the footer's PAINTED top row, not any of the three
 * `[data-brand]` background-colors, which are dead pixels — footer-tree-bg.png
 * is opaque at `cover` (measured: alpha 255, and rgb(4,3,33) on 2522 of 2560
 * px of row 0), and `background-position: center top` pins row 0 to the
 * footer's top edge at every viewport. See _footer.css for the full block.
 *
 * CORRECTED HERE (Topic 34 SEAM-WP1). This paragraph used to read: "The two
 * consumers can never fight over an element: consumer 1 only ever matches a
 * ::after, consumer 2 only ever an element." The first half is still true of
 * the PAINT — the ramp below is still painted on a ::after for consumer 1 —
 * but SEAM-WP1 moved the TONE declaration (the painters and the :has() table
 * further down) off ::after and onto the element, so the two consumers now
 * meet on the same element and the old guarantee no longer holds. Leaving
 * that sentence standing is exactly the failure this file's comment
 * discipline exists to stop.
 *
 * WHERE THEY MEET, and who wins. Only on a page whose LAST section in
 * .site-main is a hero carrying an image. No template does that today —
 * asserted in SEAM-WP1's gate, not assumed — but if one ever did:
 *
 *   _footer.css bare pair    .site-main > .mw-section:last-child[...]
 *                            (0,4,0) — TIES the relocated painters. This file
 *                            wins, because functions.php enqueues the
 *                            components loop BEFORE the sections loop, so
 *                            _hero.css prints later.
 *   _footer.css wrapped pair .site-main > *:last-child > .mw-section:last-child[...]
 *                            (0,5,0) — BEATS the painters on WEIGHT. Enqueue
 *                            order is irrelevant there, and the footer tone
 *                            #040321 would win instead.
 *
 * So the same footer rule resolves two different ways depending on whether the
 * band is wrapped. Any assertion about this must cover BOTH shapes; one that
 * only checks the direct-child case misses the dangerous half.
 *
 * AND THERE IS A THIRD CONSUMER, which this block never named: games.css
 * declares --mw-bleed-tone on `.chapter--angled-bleed` (an ELEMENT) and reads
 * it from `.chapter--angled-bleed::after` with a literal fallback,
 * var(--mw-bleed-tone, #0E0841). That is the same element-declares/pseudo-reads
 * shape SEAM-WP1 just adopted here — this hoist converges on a pattern the
 * codebase already used, rather than inventing one. No collision: that element
 * is neither a hero nor a hero descendant.
 *
 * NEW INHERITANCE SURFACE, worth knowing before writing a var() inside a hero.
 * On ::after the property reached nothing. On the element it inherits through
 * the whole hero subtree — .mw-section__bg, __scrim, __inner, the breadcrumb,
 * every nested partial. Nothing reads it there today (checked repo-wide), but
 * it means a future `var(--mw-bleed-tone, X)` authored inside a hero will
 * silently get the hero's tone instead of X. Write the fallback expecting that.
 * ----------------------------------------------------------------------- */
.mw-section--hero[data-has-image].mw-section--brand-k-world::after,
.mw-section--hero[data-has-image].mw-section--brand-studio::after,
.site-main > .mw-section:last-child[data-bg-token="studio-violet"],
.site-main > .mw-section:last-child[data-bg-token="studio-dark"],
.site-main > *:last-child > .mw-section:last-child[data-bg-token="studio-violet"],
.site-main > *:last-child > .mw-section:last-child[data-bg-token="studio-dark"] {
	/* Fallback — today's ramp. Kept so a color-mix-less engine degrades to
	 * the previous render rather than losing the bleed entirely. */
	background-image: linear-gradient(to bottom, transparent, var(--mw-bleed-tone));
	/* Eased ramp — smoothstep alphas of the SAME tone. */
	background-image: linear-gradient(
		to bottom,
		color-mix(in srgb, var(--mw-bleed-tone) 0%,    transparent)   0%,
		color-mix(in srgb, var(--mw-bleed-tone) 2.8%,  transparent)  10%,
		color-mix(in srgb, var(--mw-bleed-tone) 10.4%, transparent)  20%,
		color-mix(in srgb, var(--mw-bleed-tone) 21.6%, transparent)  30%,
		color-mix(in srgb, var(--mw-bleed-tone) 35.2%, transparent)  40%,
		color-mix(in srgb, var(--mw-bleed-tone) 50%,   transparent)  50%,
		color-mix(in srgb, var(--mw-bleed-tone) 64.8%, transparent)  60%,
		color-mix(in srgb, var(--mw-bleed-tone) 78.4%, transparent)  70%,
		color-mix(in srgb, var(--mw-bleed-tone) 89.6%, transparent)  80%,
		color-mix(in srgb, var(--mw-bleed-tone) 97.2%, transparent)  90%,
		var(--mw-bleed-tone) 100%
	);
}

/* Per-tone painters — FALLBACK LAYER ONLY as of FIX-WP41. These now set ONE
 * custom property instead of restating the gradient five times — the ramp
 * above is the single definition. A divider paints the OUTGOING tone (Topic 13
 * PORT-WP1), so these resolve to the FIX-WP6 surface siblings.
 *
 * READ THE FIX-WP41 BLOCK BELOW BEFORE EDITING THESE. They key off the hero's
 * OWN token, which is a *guess* at the tone of the band beneath — right on 9
 * pages, wrong on 12. The `:has()` table below reads the real band and wins at
 * higher specificity wherever it matches; this set survives only for the two
 * layouts where the next band is not an adjacent sibling (see below), and as
 * the no-`:has()` degrade path. Do not "fix" a wrong terminus by editing a line
 * here — the tone belongs to the band, not to the hero. */
.mw-section--hero[data-has-image].mw-section--brand-k-world[data-bg-token="kw-sky"]                 { --mw-bleed-tone: var(--kw-sky-surface); }
.mw-section--hero[data-has-image].mw-section--brand-k-world[data-bg-token="kw-gold"]                { --mw-bleed-tone: var(--kw-gold-surface); }
.mw-section--hero[data-has-image].mw-section--brand-k-world[data-bg-token="kw-cream"]               { --mw-bleed-tone: var(--kw-cream); }
.mw-section--hero[data-has-image].mw-section--brand-k-world[data-bg-token="kw-sunset"]              { --mw-bleed-tone: var(--kw-sunset); }
.mw-section--hero[data-has-image].mw-section--brand-k-world[data-bg-token="kw-castle-stone"]        { --mw-bleed-tone: var(--kw-castle-stone); }

/* Studio painters (FIX-WP27). Same contract as the K-World set above: name the
 * tone of the band the hero LEADS INTO, which for these three equals the hero's
 * own token because the section beneath carries the same one. */
.mw-section--hero[data-has-image].mw-section--brand-studio[data-bg-token="studio-dark"]          { --mw-bleed-tone: var(--studio-dark); }
.mw-section--hero[data-has-image].mw-section--brand-studio[data-bg-token="studio-violet"]        { --mw-bleed-tone: var(--studio-violet); }

/* -----------------------------------------------------------------------
 * THE TERMINUS COMES FROM THE BAND BENEATH (Topic 33 FIX-WP41)
 *
 * Owner report 2026-08-09, /bedtime-stories/: a hard horizontal line where
 * the hero art stops. Same defect named on /coloring-book-product/ and
 * /bedtime-tales-vol-1/, then swept across every page on the site.
 *
 * The contract for this fade has always been the one written above the
 * painters — "name the tone of the band the hero LEADS INTO". The painters
 * could not honour it: a selector keyed on the hero's own `data-bg-token`
 * cannot see the section beneath, so it substituted the hero's tone and
 * hoped the two matched. On /bedtime-stories/ the hero is kw-sky and the
 * band is kw-gold, so the art dissolved to periwinkle #8FA8DC and the band
 * painted butter #F2D072 — a step of 150 in RGB distance, exactly the line
 * that was reported.
 *
 * MEASURED across every rendering surface that paints a photographic hero —
 * 21 Pages plus the devblog single (editorial/devblog-post.php) — at 1440,
 * comparing the ramp's final colour stop against the first painted section
 * below the hero. 12 were wrong:
 *
 *   /team/, /publishing/                       dark   -> studio-light  (359)
 *   /bedtime-stories/, /coloring-book-product/,
 *   /bedtime-tales-vol-1/                      kw-sky -> kw-gold       (150)
 *   /griffin-roost-manager/                    kw-sky -> kw-cream      (134)
 *   /press/, /roadmap/, /articles/, /press-kit/,
 *   /products/, /a-balance-lost/               dark   -> studio-violet  (23)
 *
 * Note /team/ and /publishing/: no studio-light painter existed at all, so
 * those two faded to studio-dark against an #E1E8F9 band — the worst pair on
 * the site, and unreachable by adding another token line to the old table.
 *
 * The fix reads the real band via `:has(+ ...)` instead of guessing. After,
 * every one of those surfaces measures a terminus IDENTICAL to the band
 * beneath (delta 0) at both 1440 and 390, including the ones already correct.
 *
 * Not reachable and so not measured: the lore-entry singles
 * (lore/_runtime/render.php) and mw_job_opening pass no `image`, so they
 * never get [data-has-image] and never paint a bleed at all.
 *
 * WHY THIS REPLACES THE /careers/ OVERRIDE THAT USED TO SIT HERE. That
 * override existed because /careers/ is studio-dark over a studio-violet
 * card-grid — one page hand-patched for the general defect. It is deleted,
 * not kept: this rule covers /careers/ by the same mechanism as everything
 * else, and a second rule saying the same thing for one page is how the two
 * rival hero bleeds that FIX-WP27 had to reconcile got started. Its hard-won
 * specificity lesson is preserved in the paragraph below, which is the part
 * that was actually worth keeping.
 *
 * SPECIFICITY, deliberately a strict win rather than a tie. Each selector
 * here scores (0,5,0) — 2 classes (.mw-section--hero + the .mw-section inside
 * :has()) and 3 attributes ([data-has-image], [data-bg-token], and the
 * :has() argument's token). The painters above score (0,4,0), so these win
 * on weight and NOT on source order. That matters: the FIX-WP23 note further
 * down this file already warns that reordering the file would silently change
 * Clear the Skies, because that override wins on source order alone. This
 * table must not add a second instance of that hazard, so the extra
 * [data-bg-token] on the subject — which matches every hero the painters
 * match, and so filters nothing — is load-bearing. Do not "tidy" it away.
 *
 * THOSE TWO FIGURES ARE NOW LITERALLY TRUE (Topic 34 SEAM-WP1). They always
 * described the intended relationship, but until SEAM-WP1 every one of these
 * selectors also carried ::after, so the real scores were (0,5,1) and (0,4,1)
 * — a pseudo-ELEMENT adds one to the third component. The hoist dropped the
 * ::after from both sides together, so each lost exactly one from that
 * component and the 5-over-4 margin was never disturbed. Do not "correct"
 * these numbers, and do not read the old mismatch as evidence the contract
 * ever broke.
 *
 * THE HOIST MUST STAY COMPLETE. If any tone declaration is ever moved back
 * onto a ::after while its siblings stay on the element, this whole table
 * stops meaning anything — an ::after-declared value beats an element-
 * inherited one regardless of weight, so the loser of that comparison wins
 * anyway. The invariant is mechanical and worth checking on sight: in this
 * file, no single line may carry both --mw-bleed-tone and ::after.
 *
 * ADJACENCY, and the two pages this deliberately does not reach. `+` requires
 * the band to be the hero's next element sibling. On /clear-the-skies/ and
 * /kaeros-hub/ the following band is wrapped in a <div>, so these rules do not
 * match and those heroes keep the painters above — both measure delta 0 today
 * and are byte-identical after this change. If either page is ever unwrapped,
 * this table starts governing it, which is the correct outcome and needs no
 * edit here.
 *
 * DEGRADE PATH: an engine without :has() drops these rules entirely and falls
 * back to the painters above — i.e. to today's render. Never to nothing, and
 * never to a wrong-but-new colour.
 *
 * A divider is not reachable by this table: inc/sections/divider.php emits a
 * plain <div> with no data-bg-token and no .mw-section class. So if a hero
 * seam ever regains a divider, these rules stop matching rather than
 * terminating the fade on the divider's tone.
 *
 * THE 2026-08-02 RULING, AMENDED 2026-08-22 (Topic 34 SEAM-WP6). The ruling
 * carried in k-world/hub.php and seven sibling templates reads "no divider at
 * a hero join, anywhere", and until this topic that was read here as "a hero
 * join is a bare bleed and nothing else". Both halves need writing down now,
 * because only one of them changed.
 *
 * Still true, unchanged: no divider band is emitted at any hero join. This
 * topic emits none either.
 *
 * New: five hero joins now carry a SHAPE on the hero's own bottom edge - the
 * angled cut, the ridgeline, the Stormhold skyline, further down this file.
 * A shape is not the divider the ruling forbade. It is a pseudo-element of
 * the hero, it emits no band, it occupies no vertical space of its own, and
 * critically it inserts NOTHING between the hero and the section below -
 * which is what keeps the table above matching. A rail element at that join
 * would look like a tidy refactor and would silently take the colour with it.
 * ----------------------------------------------------------------------- */
/* The skyline layer's height, named once so both the shape and the content
 * clearance below read the same number. Doubled 2026-08-22 on the owner's
 * "increase the size of the siluetes by a factor of 2". */
.mw-section--hero[data-has-image][data-seam="skyline"] {
	--mw-skyline-h: clamp(144px, 16vw, 232px);
}

.mw-section--hero[data-has-image][data-bg-token]:has(+ .mw-section[data-bg-token="kw-sky"]) {
	--mw-bleed-tone: var(--kw-sky-surface);
	/* Topic 36 CORR-WP1 Item 2: was `var(--kw-gold)` ("gold reads on
	 * periwinkle"). `--kw-sky-surface` flipped to white, so gold (#F4C430)
	 * measures ~1.6:1 on it and goes invisible — the same defect the sibling
	 * row below already solved for the butter band. Matches it: ink. */
	--mw-seam-line:  var(--studio-dark); /* ink reads on white */
}
.mw-section--hero[data-has-image][data-bg-token]:has(+ .mw-section[data-bg-token="kw-gold"]) {
	--mw-bleed-tone: var(--kw-gold-surface);
	--mw-seam-line:  var(--studio-dark); /* ink reads on butter */
}
.mw-section--hero[data-has-image][data-bg-token]:has(+ .mw-section[data-bg-token="kw-cream"]) {
	--mw-bleed-tone: var(--kw-cream);
	/* Ink, not gilt (2026-08-22 games ruling). Griffin Roost Manager is
	 * under Games and takes the same angled cut, but it is the cozy
	 * K-World tier and its band is cream: the hub's gold would sit on a
	 * near-gold field and disappear. Ink is the same answer this table
	 * already gives for a butter band, one row down. */
	--mw-seam-line: var(--studio-dark); /* ink reads on cream */
}
.mw-section--hero[data-has-image][data-bg-token]:has(+ .mw-section[data-bg-token="kw-sunset"])              { --mw-bleed-tone: var(--kw-sunset); }
.mw-section--hero[data-has-image][data-bg-token]:has(+ .mw-section[data-bg-token="kw-castle-stone"])        { --mw-bleed-tone: var(--kw-castle-stone); }
.mw-section--hero[data-has-image][data-bg-token]:has(+ .mw-section[data-bg-token="studio-dark"])            { --mw-bleed-tone: var(--studio-dark); }
.mw-section--hero[data-has-image][data-bg-token]:has(+ .mw-section[data-bg-token="studio-violet"]) {
	--mw-bleed-tone: var(--studio-violet);
	/* The games hub's gilt, ratified 2026-08-22 when the owner ruled that
	 * every page under Games ends its hero the way the hub does. Colour and
	 * gradient are games.css FIX-WP21's, verbatim - this is a match, not a
	 * new decision. Dark art over a dark violet band: gold is the only one
	 * of the three tier accents that separates them. */
	--mw-seam-line:       var(--kw-gold);
	--mw-seam-line-image: linear-gradient(92deg, rgba(244, 196, 48, 0) 3%, rgba(244, 196, 48, 0.7) 34%, rgba(255, 232, 160, 0.95) 58%, rgba(244, 196, 48, 0.4) 84%, rgba(244, 196, 48, 0) 97%);
}
.mw-section--hero[data-has-image][data-bg-token]:has(+ .mw-section[data-bg-token="studio-light"])           { --mw-bleed-tone: var(--studio-light); }

/* -----------------------------------------------------------------------
 * SEAM LINE BASE DEFAULT (Topic 34 SEAM-WP1)
 *
 * Four rows above name a --mw-seam-line, because only four tone pairings
 * have a designed line colour. The remaining rows and all seven painters
 * deliberately leave it unset: inventing a colour for a pairing nobody has
 * drawn a seam for would be fabricating a design decision.
 *
 * It was two rows until 2026-08-22, when the owner ruled that every page
 * under Games ends its hero the way the games hub does. That ruling is what
 * makes studio-violet and kw-cream designed pairings rather than invented
 * ones: it names the reference (games.css FIX-WP21) and the pages. Add a
 * row here only when a ruling does the same, not when a page merely looks
 * like it could use a line.
 *
 * But "unset" is not a safe resting state. A later rule writing
 * `background: var(--mw-seam-line)` against an undefined property is invalid
 * at computed-value time, and on the `background` SHORTHAND that resets every
 * background longhand to its initial value — the line renders transparent AND
 * any sibling background declaration in the same rule is wiped. It would fail
 * silently and look identical to "no line intended". Screenshot QA cannot tell
 * those two apart.
 *
 * So the property is always defined. `transparent` is rgba(0,0,0,0): it cannot
 * paint, and color-mix() is premultiplied per spec so it produces no grey
 * fringe. "No line" becomes a design choice a probe can assert, rather than a
 * parse failure a probe cannot see.
 *
 * Specificity (0,2,0) — 1 class + 1 attribute. Loses to the painters (0,4,0)
 * and to the :has() rows (0,5,0), which is the intent: this is the floor.
 *
 * DECLARED WITH THE HERO'S OTHER BASELINE PROPERTIES, not as a second rule
 * carrying this same selector. Two rules with an identical selector in one
 * file is a drift surface: an editor changing one never sees the other. See
 * the hero layout block near the top of this file for where it actually
 * lives.
 *
 * ONE STRAY COMMENT-CLOSE IN THIS BLOCK SHIPPED A REGRESSION (2026-08-22, commit 97cbe35,
 * fixed same day). The paragraph above was appended after a a comment-close instead of
 * before it, so five lines of prose sat outside any comment and a second a comment-close
 * followed. The apostrophe in HERO-S then opened an unterminated string, the
 * parser consumed everything up to the next `{` looking for a selector, and it
 * swallowed and discarded the @media (max-width: 720px) K-World bleed override
 * below. Result: every K-World photographic hero rendered the DESKTOP bleed on
 * mobile - measured 148.5px instead of 123.8px at 390.
 *
 * It survived a delta-0 gate because that gate measured colour at 1440 and
 * argued 390 was identical since no tone declaration sits inside a @media
 * block. That argument was true about colour and blind to a @media rule being
 * destroyed. Counting a comment-open against a comment-close does not catch it either - the counts
 * were balanced at 49/50 only because the stray close made them so.
 *
 * The check that DOES catch it is the browser: read document.styleSheets for
 * this file and assert the parsed rule count. Broken = 2 @media rules, fixed =
 * 3. Assert the count, not the delimiters.
 *
 * AND NOTE THE TRAP THIS NOTE ITSELF FELL INTO, twice in one hour: writing a
 * literal comment-close INSIDE a comment ends it early. CSS comments do not
 * nest. Never put the two-character close sequence in prose here - spell it
 * out in words, as this block now does.
 * ----------------------------------------------------------------------- */

/* -----------------------------------------------------------------------
 * SEAM LAYER - the shared contract (Topic 34 SEAM-WP2)
 *
 * Scaffolding only. No shape geometry here: the angled shear lands in
 * SEAM-WP3, the ridgeline in SEAM-WP4, the Stormhold skyline in SEAM-WP5.
 * This block fixes WHERE a seam paints, WHAT colour it takes, and how it
 * fails - the three things all three shapes must agree on.
 *
 * MECHANISM: mask-image, not an injected SVG. The shape comes from a file in
 * assets/img/decorative/, the COLOUR comes from var(--mw-bleed-tone), which
 * SEAM-WP1 put on the element and which already resolves per page from the
 * band beneath (periwinkle on /coloring-books/ and /k-world/, butter on the
 * three product pages, cream on /griffin-roost-manager/). One shape file
 * serves every tone, exactly as the divider system in that same folder has
 * always worked - see _studio.css "Divider - Studio-tier shape variants".
 *
 * THIS OVERRIDES THE TOPIC 34 MASTER DOC, deliberately and with a reason.
 * That doc says "Inline SVG, never mask-image, for B and C", citing FIX-WP24's
 * rejection above: an engine that drops the mask paints an OPAQUE TONE SLAB
 * over the bottom of the hero art, worse than the defect being fixed and
 * invisible to screenshot-only QA. That objection is CORRECT and is the reason
 * the paint below sits inside an @supports guard. With the guard the failure
 * mode inverts: no mask support means no background at all, so the seam
 * degrades to today's dissolve - never to a slab. Fixing the failure mode is
 * the direct answer; avoiding masks entirely would have cost a third
 * inline-SVG helper in a codebase already carrying two that have drifted apart
 * once (FIX-WP40 fixed the copy in inc/svg.php and left the copy in
 * inc/sections/hero.php still blind-splicing).
 *
 * mask-mode: alpha is not optional. The decorative SVGs are near-black filled
 * paths; WebKit defaults to luminance and collapses them to a flat bar. Same
 * trap _studio.css and _divider.css both record.
 *
 * LAYER ORDER, and why .mw-section__inner moved to 2. Both the seam and the
 * tone bleed are pseudo-elements of the same box. At equal z-index, painting
 * order is TREE order, so ::after always paints over ::before - and the
 * bleed's final stop is fully opaque var(--mw-bleed-tone). A seam left at
 * z-index 0 would be painted out by the very ramp it is meant to terminate.
 * Hence seam at 1, content at 2, bleed unchanged at 0.
 *
 * AND THE BLEED MUST SHORTEN WHERE A SEAM EXISTS. Painting above the ramp is
 * necessary but not sufficient: a full-length ramp still reaches the band
 * tone before the shape does, so the silhouette reads against a background of
 * its own colour. Each shape WP sets its own lead-in - the owner ratified a
 * 70px lead-in for the angled cut on 2026-08-22 ("A2"). Stated here because
 * it is a contract between three WPs, not a detail of any one of them.
 *
 * overflow: hidden on [data-has-image] means a seam CANNOT overhang into the
 * band below; shapes must be drawn to sit inside the hero box. And the bleed
 * uses inset-block-end: -1px to hide a sub-pixel sliver at fractional zoom -
 * seam layers take the same -1px for the same reason.
 * ----------------------------------------------------------------------- */
.mw-section--hero[data-has-image][data-seam]::before {
	content: "";
	position: absolute;
	inset-inline: 0;
	inset-block-end: -1px;
	z-index: 1;
	pointer-events: none;
	/* Shape and size are per-seam - SEAM-WP3/WP4/WP5. */
}

/* The paint lives behind a support guard so a dropped mask renders NOTHING
 * rather than an opaque slab over the art. See the failure-mode note above. */
@supports ((-webkit-mask-image: url("../../img/decorative/divider-torn-paper-down.svg")) or (mask-image: url("../../img/decorative/divider-torn-paper-down.svg"))) {
	.mw-section--hero[data-has-image][data-seam="ridgeline"]::before,
	.mw-section--hero[data-has-image][data-seam="skyline"]::before {
		/* NO background-color HERE, deliberately. It belongs in the same rule as
		 * mask-image, in SEAM-WP4/WP5. Setting it here would paint a SOLID
		 * RECTANGLE over the hero art for as long as the shape file does not
		 * exist - which is precisely the opaque-slab failure the guard around
		 * this block exists to prevent, reintroduced one WP early. Caught in
		 * WP-2's own verification: with mask-image resolving to `none`, a forced
		 * data-seam="ridgeline" painted rgb(242,208,114) across the full box.
		 * Shape and paint ship together or neither ships. */
		-webkit-mask-repeat: no-repeat;
		        mask-repeat: no-repeat;
		-webkit-mask-position: center bottom;
		        mask-position: center bottom;
		-webkit-mask-size: 100% 100%;
		        mask-size: 100% 100%;
		-webkit-mask-mode: alpha;
		        mask-mode: alpha;
	}
	/* -------------------------------------------------------------------
	 * SEAM B - the ridgeline (Topic 34 SEAM-WP4)
	 *
	 * A rolling landscape horizon rising out of the band into the hero art,
	 * for the two BROWSE pages. Where Seam A announces an edge, this one
	 * settles: /coloring-books/ is a shelf and /bedtime-stories/ is bedtime,
	 * and a hard gold diagonal is tonally wrong for both.
	 *
	 * THIS IS THE TONE-RESOLUTION PROOF. One shape file and the rule below
	 * paint PERIWINKLE on /coloring-books/ and BUTTER on /bedtime-stories/,
	 * with no per-page CSS anywhere. Both heroes carry the SAME token; the
	 * whole difference is the band each one leads into, which is what the
	 * FIX-WP41 table SEAM-WP1 hoisted onto the element reads. If a page ever
	 * seems to need a per-page override here, the table is wrong and the
	 * table is what to fix.
	 *
	 * THE WP SAID "INLINE SVG, NEVER mask-image". Recorded rather than
	 * quietly ignored. That instruction was written against FIX-WP24's
	 * objection: an engine that drops the mask paints an opaque tone slab
	 * over the bottom of the hero art, worse than the defect being fixed and
	 * invisible to a screenshot-only QA pass. The objection is correct. It is
	 * also already answered, by the support guard this rule sits inside,
	 * which inverts the failure mode: no mask support means no paint at all
	 * and the page degrades to today's dissolve. Two further reasons the
	 * guard beats the ban. First, mask-image IS this site's divider
	 * mechanism already, not a new idea introduced here: all ten shapes in
	 * this same folder are consumed exactly this way by _studio.css and
	 * _divider.css. Second, inlining would have needed a THIRD inline-SVG
	 * helper in a codebase already carrying two that drifted apart once,
	 * which is the failure this file warns about twice.
	 *
	 * THE SHAPE IS DRAWN FOR THIS BOX, not borrowed. divider-ridgeline-up.svg
	 * is proportioned for a 28 to 56px strip; the ballot stretched it across a
	 * hero edge and it flattened into a shallow wave with almost no relief.
	 * This one is drawn at 1440 by 116 with three peaks and three troughs of
	 * deliberately unequal height, so it cannot read as a repeat.
	 *
	 * FILLED WHITE, unlike the ten near-black dividers beside it. That is the
	 * one place this asset departs from the folder and it is deliberate: a
	 * white fill masks identically whether the engine honours mask-mode alpha
	 * or falls back to luminance, so the WebKit trap recorded in _studio.css
	 * and _divider.css cannot reach this file even if the mask-mode
	 * declaration is ever lost. The alpha declaration above still stands;
	 * this is belt and braces, not a replacement for it.
	 *
	 * NO LEAD-IN, and the reason is structural rather than aesthetic. The
	 * ramp is bottom-anchored and reaches full tone at the hero's bottom
	 * edge, which is exactly where this shape's troughs sit. Any lead-in at
	 * all paints the troughs in the same tone as the peaks and the silhouette
	 * stops existing. The shape IS the transition here, which is also what
	 * the preview the owner approved shows: Seam B was balloted with the fade
	 * already removed. Seam A's ratified 70px does not carry over.
	 *
	 * The kill sits INSIDE this guard on purpose. Without mask support there
	 * is no shape, so the lead-in must survive to keep today's dissolve.
	 * ------------------------------------------------------------------- */
	.mw-section--hero[data-has-image][data-seam="ridgeline"]::before {
		block-size: clamp(72px, 8vw, 116px);
		background-color: var(--mw-bleed-tone);
		-webkit-mask-image: url("../../img/decorative/seam-ridgeline.svg");
		        mask-image: url("../../img/decorative/seam-ridgeline.svg");

		/* PROPORTIONS ARE HELD, NOT STRETCHED - and this is the difference
		 * between a landscape and a doily. The shared rule above stretches a
		 * shape to the box with mask-size 100% 100%, which is right for a
		 * divider strip whose height barely changes. It is wrong here.
		 * MEASURED at 390: the whole 1440-wide horizon squeezed into 390px
		 * against a 72px band, and three long hills became a corrugated
		 * scalloped ripple - a decorative border in a different design
		 * language, arguably worse than the dissolve this replaces. Sizing
		 * `auto 100%` scales the shape by its HEIGHT and lets the width fall
		 * out of the drawn ratio, so a hill is the same shape of hill at
		 * every viewport and narrow screens simply see less of the horizon.
		 *
		 * repeat-x is the safety net that choice needs, not a tiling effect.
		 * At the 116px ceiling the shape is 1440 wide, so any viewport wider
		 * than that would run out of mask and the tone would vanish at the
		 * edges, dropping the art onto the band with no seam at all. The SVG
		 * is drawn with both ends at the same height and a level tangent so
		 * the repeat is seamless; the join cannot be found by eye. */
		-webkit-mask-size: auto 100%;
		        mask-size: auto 100%;
		-webkit-mask-repeat: repeat-x;
		        mask-repeat: repeat-x;
	}

	/* (0,4,1) — one class, three attributes, one pseudo-element. Beats the
	 * base geometry rule and the 720px mobile override, both (0,3,1), on
	 * weight rather than on source order. Same arithmetic as Seam A. */
	.mw-section--hero[data-has-image][data-seam="ridgeline"][data-bg-token]::after {
		block-size: 0;
	}

	/* -------------------------------------------------------------------
	 * SEAM C - the Stormhold skyline (Topic 34 SEAM-WP5)
	 *
	 * The hub only. Same layer as Seam B with a different drawing, which is
	 * the whole point: a third mechanism here is how this file gets bitten a
	 * third time. Everything below is either a size or a path.
	 *
	 * CANON, and it is the reason this seam belongs on /kworld/ and nowhere
	 * else. Two landmarks have to read in silhouette or it is generic
	 * rooftops: the Royal Palace ridge broken by the notch of the rooftop
	 * hole pierced when Waldor was taken and left unrepaired as his
	 * memorial, and the great lighthouse of Kaedral over the harbour.
	 * Cross-checked before drawing rather than taken from the location entry
	 * alone: the pierced roof is corroborated in the Grand Deception event
	 * entry, and the lighthouse in the Bedtime Tales bible and the 2026-08-16
	 * Tale 1 ratification. The hero image above it is Adam and Solara looking
	 * out over that same city.
	 *
	 * ONE LAYER, REPEATING - and both halves of that are 2026-08-22 changes
	 * with a reason, because this rule used to say the opposite.
	 *
	 * It was two layers: the city drawn once and centred, plus a plain strip a
	 * fifth of the box tall repeating underneath it, because a no-repeat shape
	 * runs out of mask on any viewport wider than itself and would drop the
	 * tone at both edges. The ban on repeating was CANON: two lighthouses is
	 * not a wider city, it is a mistake.
	 *
	 * That canon argument died when the shape moved. This seam was drawn for
	 * /kworld/ and bound to it by the Royal Palace notch and the lighthouse of
	 * Kaedral; /kworld/ now takes the ridgeline, and the city serves the five
	 * pages under About, where the landmarks read as skyline rather than as
	 * places. So the shape may tile, and tiling is the honest fix for the
	 * owner's "stretch the city divider all the way end to end" - it reaches
	 * both edges at ANY width, where centring left bare wall at each end of
	 * anything wider than the drawing (about 250px a side at 1700).
	 *
	 * It tiles cleanly with no work: both ends of the path already sit level
	 * at the wall base and no building straddles either edge. It was drawn to
	 * tile and simply had never been asked to.
	 *
	 * Removing layer 2 also removes a defect. The strip painted a wall at 20%
	 * of the box while the SVG's own wall base sat at 27.6%, so the wall STEPPED
	 * by about 9px exactly where the drawing ended - introduced when the city
	 * was redrawn and its wall raised. One layer cannot disagree with itself.
	 *
	 * Still sized by HEIGHT, not stretched, and that has not changed: squeezing
	 * a 1200-wide city into 390 turns a slender lighthouse into a squat block.
	 * ------------------------------------------------------------------- */
	.mw-section--hero[data-has-image][data-seam="skyline"]::before {
		/* Double the 72-116 every other seam uses. Owner directive 2026-08-22:
		 * "increase the size of the siluetes by a factor of 2". Because the
		 * shape is sized by HEIGHT, doubling this doubles the rendered width
		 * too - which is half of why the city now reaches both edges.
		 *
		 * Named once, on the element, because the content has to know it: see
		 * the clearance rule below. */
		block-size: var(--mw-skyline-h);
		background-color: var(--mw-bleed-tone);
		-webkit-mask-image: url("../../img/decorative/seam-stormhold-skyline.svg");
		        mask-image: url("../../img/decorative/seam-stormhold-skyline.svg");
		-webkit-mask-size: auto 100%;
		        mask-size: auto 100%;
		-webkit-mask-repeat: repeat-x;
		        mask-repeat: repeat-x;
		-webkit-mask-position: left bottom;
		        mask-position: left bottom;
	}

	.mw-section--hero[data-has-image][data-seam="skyline"][data-bg-token]::after {
		block-size: 0;
	}

	/* THE CONTENT HAS TO CLEAR THE SPIRES, and this is not optional at the
	 * size the owner asked for. MEASURED on /publishing/ the moment the layer
	 * doubled: the hero is 600px, the tallest spire tip reaches 196px above
	 * its bottom edge, and the subhead's last line stopped at 163px - so the
	 * city ran 33px up into the sentence. Legible, because content is z-index
	 * 2 and the seam is 1, but plainly wrong.
	 *
	 * The tip sits at 98/116 of the box: 16 of wall plus 82 of spire, read off
	 * the drawing rather than guessed. Twelve pixels of air on top of that.
	 * Derived from the same custom property as the layer's height, so the two
	 * cannot drift apart the next time the size is tuned - the whole reason
	 * that height is a named property and not a literal.
	 *
	 * The tallest spire is the one that matters here, not the median. A rule
	 * written for the median would leave three or four towers still crossing
	 * the text, which is worse than no rule at all: it would look deliberate. */
	.mw-section--hero[data-has-image][data-seam="skyline"] > .mw-section__inner {
		padding-block-end: calc(var(--mw-skyline-h) * 98 / 116 + 12px);
	}

}

/* -----------------------------------------------------------------------
 * SEAM A - the angled cut (Topic 34 SEAM-WP3)
 *
 * Owner directive 2026-08-22: the soft dissolve is the wrong gesture on the
 * K-World product pages; announce the edge the way /games/ does. This is that
 * treatment, ported - the shear values and the polygon math are copied from
 * assets/css/games.css (the Topic 33 FIX-WP21 block), not re-derived. A
 * numerically different cut would still look angled at a glance and would
 * fail the constant-thickness tell below.
 *
 * ONE SHEAR, THREE PARTICIPANTS. The hero clip, the line and the band clip
 * must share one shear value and one box origin. If the line ever TAPERS
 * instead of holding a constant thickness end to end, those three have
 * drifted apart. That is the documented failure signature from the games
 * handoff and it is the cheapest thing to check when this seam looks wrong.
 *
 * WHAT WAS NOT PORTED, and why. games.css carries the line on a dedicated
 * zero-height rail element between hero and flagship. There is no rail here.
 * FIX-WP41 resolves this hero's tone with :has(+ .mw-section[...]) - the
 * ADJACENT-SIBLING combinator - so any element inserted between the hero and
 * the band breaks that adjacency, drops the hero back to the older painters,
 * and silently restores a 150-RGB-step colour defect on exactly these two
 * pages. It would look plausible in a screenshot of the wrong colour. So the
 * line lives on the hero as a pseudo-element and the band is reached with the
 * plus combinator; nothing is ever inserted at this join.
 *
 * That relocation buys one thing the rail could not have. Sitting inside the
 * hero, the line is subject to the hero's own clip, so its LOWER edge is not
 * computed at all - it is cut by the same path that cuts the art. Line and
 * art therefore share one antialiased edge by construction rather than by
 * arithmetic agreeing. Only the line's upper edge is its own clip, which is
 * why the polygon below differs from the rail's four-cornered one.
 *
 * THE LEAD-IN STAYS AT 70px. The ballot built this seam both ways on the live
 * page and its own written finding recommended dropping the fade entirely
 * (variant A1) because at 70px the residual tone combines with the hero scrim,
 * which is heaviest at the bottom, and muddies the left third of the cut. The
 * owner saw both stills and chose A2, the 70px lead-in, with that mud visible
 * in the image they picked. Do not quietly re-apply the removal.
 *
 * A note on what 70px means under a diagonal. The ramp is bottom-anchored and
 * runs top-to-bottom, so the cut eats its last shear pixels on the right: the
 * left end gets the whole ramp, the right end only its first 16px. The lead-in
 * is therefore a wedge, not a uniform band, and that asymmetry IS the mud the
 * ballot named. Making it uniform would need the ramp angled to the cut, whose
 * angle depends on viewport width, which would mean restating the shared
 * 11-stop smoothstep as a second, angled copy. Two rival copies of one visual
 * mechanism is the drift this file has already been bitten by twice. Recorded
 * as understood and accepted, not as unnoticed.
 *
 * GUARDED, like the mask block above. An engine without clip-path would leave
 * the hero unclipped, the band pulled up by a bare negative margin, and the
 * line painting as a solid ink bar across the bottom of the art - the same
 * opaque-slab failure FIX-WP24 rejected masks for. Inside this guard the
 * failure mode inverts: no clip-path support means no seam at all, and the
 * page degrades to today's dissolve. The guard tests the unprefixed property
 * only, because only the unprefixed property is emitted; testing with an "or"
 * against a WebKit-prefixed form would let an engine pass the test and then
 * get the slab.
 * ----------------------------------------------------------------------- */
@supports (clip-path: polygon(0 0, 100% 0, 0 100%)) {

	/* The shear. Declared on the hero AND on the band beneath it, because a
	 * sibling inherits nothing from a sibling. Edit these as ONE set - the
	 * whole seam is the agreement between them. */
	.mw-section--hero[data-has-image][data-seam="angled"],
	.mw-section--hero[data-has-image][data-seam="angled"] + .mw-section {
		--seam-shear: 54px;
	}

	/* Shallower diagonal on phones. Keep adjacent to the declaration above. */
	@media (max-width: 620px) {
		.mw-section--hero[data-has-image][data-seam="angled"],
		.mw-section--hero[data-has-image][data-seam="angled"] + .mw-section {
			--seam-shear: 30px;
		}
	}

	/* 1 - the hero. Its bottom edge becomes the diagonal, rising to the right. */
	.mw-section--hero[data-has-image][data-seam="angled"] {
		/* One place, so the line's height and its polygon can never disagree
		 * about how thick the line is. games.css writes 3px twice. */
		--seam-line-w: 3px;
		clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--seam-shear)), 0 100%);
	}

	/* 2 - the line, riding the cut. Colour comes from the seam-line property,
	 * which the FIX-WP41 table resolves from the band BENEATH: ink on butter
	 * for both of this WP's pages, gold on periwinkle elsewhere. A gold line on
	 * a gold band would be invisible, which is why this is never hardcoded.
	 *
	 * inset-block-end is 0 here, overriding the seam layer's -1px. That -1px
	 * exists to hide a sub-pixel sliver where a layer meets the hero's bottom
	 * border edge; here the bottom edge is defined by the hero's clip instead,
	 * and a 1px offset would shift this polygon off the shear the other two
	 * participants use.
	 *
	 * The polygon cuts only the TOP edge, parallel to the cut and one line
	 * thickness above it. The bottom is left to the hero's clip - see the
	 * shared-edge note in the block comment. */
	.mw-section--hero[data-has-image][data-seam="angled"]::before {
		inset-block-end: 0;
		block-size: calc(var(--seam-shear) + var(--seam-line-w));
		clip-path: polygon(0 var(--seam-shear), 100% 0, 100% 100%, 0 100%);
		/* LONGHANDS, deliberately. `background:` is a shorthand and would
		 * reset every other background longhand to its initial value, so a
		 * pairing that sets only the colour would have its image wiped and
		 * vice versa. Both floors are defined on the element, so neither var
		 * can be invalid at computed-value time. Image over colour: a pairing
		 * that wants the games hub's firelight gradient sets both and the
		 * gradient paints; one that wants a flat line sets only the colour
		 * and the image stays `none`. */
		background-color: var(--mw-seam-line);
		background-image: var(--mw-seam-line-image);
	}

	/* 3 - the lead-in, cut from clamp(140px, 24%, 300px) to the ratified 70px.
	 * A full-length ramp would reach the band tone long before the cut does, so
	 * the diagonal would read against a background of its own colour and there
	 * would be no visible edge at all - the seam would cost work and change
	 * nothing.
	 *
	 * Specificity, deliberately: 1 class + 3 attributes + 1 pseudo-element is
	 * (0,4,1). The base geometry rule and the 720px mobile override are both
	 * (0,3,1), so this wins on WEIGHT and needs no help from source order.
	 * data-bg-token is present on every hero that paints an image and filters
	 * nothing - it is here to buy the fourth attribute, the same trick SEAM-WP1
	 * used for the Clear the Skies correction. Scoping to
	 * mw-section--brand-k-world would also have scored (0,4,1) but would
	 * silently exclude the studio-tier pages this system is going to reach. */
	.mw-section--hero[data-has-image][data-seam="angled"][data-bg-token]::after {
		block-size: 70px;
	}

	/* 4 - the band. Pulled up by one shear and clipped on the same diagonal, so
	 * its clipped top edge IS the hero's bottom edge. No gap, and no horizontal
	 * edge anywhere at this join.
	 *
	 * The pull is on the band and NOTHING else. The ballot's first attempt put
	 * it on the line as well; the two diagonals stopped coinciding and the dark
	 * page background showed through the difference as a wedge. */
	.mw-section--hero[data-has-image][data-seam="angled"] + .mw-section {
		margin-block-start: calc(-1 * var(--seam-shear));
		clip-path: polygon(0 var(--seam-shear), 100% 0, 100% 100%, 0 100%);
	}

	/* Give the band back exactly what the pull took, so its CONTENT does not
	 * move at all and only its background reaches up into the cut. Deliberately
	 * one shear and not a rounder number: minus one shear plus one shear is
	 * zero net movement, which is the whole point and is what makes this seam
	 * safe to drop onto a page whose composition is already signed off.
	 *
	 * Applied to the inner rather than the section because the section's own
	 * padding-block is a shorthand - overriding its start half here would
	 * discard the section's padding instead of adding to it. */
	.mw-section--hero[data-has-image][data-seam="angled"] + .mw-section > .mw-section__inner {
		padding-block-start: var(--seam-shear);
	}
}

/* Narrow viewports: keep the bleed PROPORTIONATE. At this width the hero's
 * copy stack (eyebrow/badge/headline/subhead/CTA) routinely exceeds
 * min-block-size, so the box is content-driven and a quarter of it is a lot
 * of vertical real estate; 20% reads as haze rather than as a wash.
 *
 * CORRECTED HERE (FIX-WP24): this comment used to claim the override existed
 * "so it cannot reach the CTA", citing /coloring-book-product/ at 375px.
 * That reason is FALSE and the file already contains the disproof — FIX-WP23
 * ran the A/B and the CTA renders byte-identically with the bleed on and off
 * (9.31:1 both ways), because `::after` is z-index 0 and `.mw-section__inner`
 * is z-index 2 (1 before Topic 34): the bleed paints BEHIND the content, always. Overlap with the
 * CTA row is not a defect and never was. The 720px breakpoint still matches
 * this stylesheet family's mobile cutoff (_footer.css:110,
 * _media-text.css:460). Changing the value while leaving a disproved reason
 * in place is precisely the drift this file's comment discipline exists to
 * stop. */
@media (max-width: 720px) {
	.mw-section--hero[data-has-image].mw-section--brand-k-world::after {
		block-size: clamp(90px, 20%, 170px);
	}
}

/* -----------------------------------------------------------------------
 * Clear the Skies hero → premise dissolve (Topic 33 FIX-WP23)
 *
 * Owner directive 2026-07-30: "Big Hero image needs to bleed through the
 * first paragraph". The siege art stopped dead on a hard horizontal line at
 * the hero's bottom edge, so the page read as picture-then-text.
 *
 * This is a PORT, not an invention. The design system already specifies this
 * exact treatment for this exact hero — `ClearTheSkies.jsx` passes `bleed` to
 * CinematicHero, which emits `.chero--bleed` + a `.chero__haze` element
 * (site.css `.chero__haze`, "§6.4 / §6A.4 bleed pattern"). Our theme ported
 * the hero but never the bleed, which is why the edge was hard here and not
 * in the design. Gradient stops and the clamp() are the design's, verbatim.
 *
 * Rendered as ::after rather than a markup element so inc/sections/hero.php
 * needs no new arg — the layering is identical, and it reuses the slot the
 * FIX-WP7 K-World bleed already established:
 *
 *   .mw-section__bg     z-index -2  (art)
 *   .mw-section__scrim  z-index -1  (legibility gradient)
 *   ::after (this rule)  z-index  0  (dissolve into the incoming tone)
 *   ::before            z-index  1  (SEAM shape, Topic 34 - absent unless [data-seam])
 *   .mw-section__inner  z-index  2  (content)
 *
 * The two ::after rules can never collide: FIX-WP7's is scoped to
 * .mw-section--brand-k-world, and this hero is brand-studio.
 *
 * The terminus is the INCOMING band's token (the premise media-text band is
 * studio-violet at template-page-clear-the-skies.php:265), not the hero's own
 * studio-dark, so the dissolve lands on the colour that actually follows it.
 *
 * The alpha stops below are DERIVED FROM THAT SAME TOKEN and must stay that way.
 * They read rgba(14, 10, 42, *) = #0e0a2a = studio-violet.
 *
 * History, because this trap already sprang once. FIX-WP23 shipped these stops
 * as rgba(14, 8, 65, *) — studio-DARK — which was correct only because FIX-WP19
 * had collapsed studio-violet onto studio-dark, making the two interchangeable.
 * That WP left the precondition in writing ("if the violet tone is ever restored,
 * re-derive the alpha stops from it as well as the terminus"), and FIX-WP25
 * restored it on 2026-08-01, so they are re-derived here.
 *
 * Do NOT "simplify" this by pointing the terminus at var(--studio-dark) to match
 * the old stops. That was proposed at FIX-WP25's sign-off gate and is the wrong
 * branch: the hero would then dissolve to dark while the band beneath it paints
 * violet, reintroducing a hard edge at the hero seam — the exact defect FIX-WP23
 * existed to remove. If the token moves again, move these stops with it.
 *
 * NOT ported: the design's `.chero--bleed .chero__scrim` override, which adds a
 * left-weighted gradient for bottom-left-aligned copy. Our hero centres its
 * content stack (justify-content: center) where the design bottom-aligns it, so
 * that override would darken the top and thin the middle exactly where our copy
 * sits. Our existing scrim already measures clean and is left alone.
 * ----------------------------------------------------------------------- */
.mw-page-clear-the-skies .mw-section--hero[data-has-image]::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	inset-block-end: -1px; /* -1px hides a sub-pixel seam at fractional zooms */
	block-size: clamp(130px, 24%, 280px);
	z-index: 0;
	pointer-events: none;
	background: linear-gradient(
		180deg,
		rgba(14, 10, 42, 0) 0%,
		rgba(14, 10, 42, 0.45) 52%,
		rgba(14, 10, 42, 0.86) 82%,
		var(--studio-violet, #0e0a2a) 100%
	);
}

/* Narrow viewports: cap the dissolve so it stays proportionate on a short hero
 * (626px at 390x844, where an unclamped 24% would eat a quarter of the scene).
 *
 * NOT for CTA contrast. At 390 the CTA row's bottom sits 70px *inside* this
 * band, which is the geometry the K-World override above was written to avoid —
 * but measured off the rendered pixels, the button is byte-identical with the
 * dissolve on and off: fill rgb(242,195,48), label rgb(48,32,35), 9.31:1 both
 * ways. It cannot dull the CTA, because ::after is z-index 0 and
 * .mw-section__inner is z-index 2 (1 before Topic 34) — the dissolve paints BEHIND the content, not
 * over it. Same layering applies to the K-World rule above, whose comment
 * asserted the CTA reason outright; FIX-WP24 rewrote it to the real one
 * (proportionality) when it changed that clamp. Both overrides now state the
 * same, correct justification. */
@media (max-width: 720px) {
	.mw-page-clear-the-skies .mw-section--hero[data-has-image]::after {
		block-size: clamp(80px, 14vh, 150px);
	}
}

