/* ============================================================================
   The PATH — scroll motion layer (progressive enhancement)
   ----------------------------------------------------------------------------
   Enqueued front page only, alongside assets/js/path-motion.js. Every rule in
   this file falls into exactly one of two buckets:

     1. Static rules for DOM the script injects (#jk-pathline, .jk-dims,
        .jk-rail) and a stacking fix that keeps real section content above
        that injected line. None of this exists without the script, so none
        of it can hide anything when the script hasn't run.

     2. Reveal rules for REAL section content (headings, lede paragraphs).
        These are gated behind `html.jk-motion` — a class only path-motion.js
        adds, and only after it has decided prefers-reduced-motion is NOT set.
        Without that class every selector below is inert and the page renders
        exactly as it does with the script absent: full opacity, no offset.

   Colours are not touched here — the SVG strokes are set directly from
   getComputedStyle() in JS (gold / moss / ink tokens), so this file never
   duplicates a hex value.
   ========================================================================== */

/* ---------- 1. Injected overlay: line, dims, rail ---------- */

#jk-pathline {
	position: absolute;
	inset: 0;
	width: 100%;
	pointer-events: none;
	z-index: 1; /* above section backgrounds (painted at step 3, non-positioned),
	               below promoted content wrappers (§2, z-index: 2) */
}
#jk-pathline path { fill: none; }

.jk-line-main   { stroke-width: 2.5; opacity: .95; }
.jk-line-branch { stroke-width: 1.5; opacity: 0; transition: opacity .6s linear; }
.jk-line-close  { stroke-width: 2.5; opacity: .9; }
.jk-line-ghost  { stroke-width: 1;   opacity: .14; stroke-dasharray: 2 7; }

.jk-dims {
	position: fixed;
	inset: 0;
	pointer-events: none;
	z-index: 0; /* behind the line and behind content */
	overflow: hidden;
}
.jk-dim {
	position: absolute;
	font-family: var(--font-mono, "IBM Plex Mono", ui-monospace, monospace);
	font-size: 11px;
	letter-spacing: .35em;
	text-transform: uppercase;
	color: var(--text-4, #858570); /* --wp--custom--action--moss, the accessible-variant alias */
	opacity: .24;
	white-space: nowrap;
	will-change: transform;
}
.jk-dim b { font-weight: 400; color: var(--text-2, #C9C7B4); }

.jk-rail {
	position: fixed;
	right: 26px;
	top: 50%;
	transform: translateY(-50%);
	z-index: 5;
	font-family: var(--font-mono, "IBM Plex Mono", ui-monospace, monospace);
	font-size: 10px;
	letter-spacing: .25em;
	display: flex;
	flex-direction: column;
	gap: 22px;
	color: var(--text-4, #858570);
}
.jk-rail span { opacity: .4; transition: opacity .3s linear, color .3s linear; }
.jk-rail span.is-on { opacity: 1; color: var(--accent, #D9A24A); }

/* Hidden at the site's own nav breakpoint (inc/blocks header rule, §9 of
   design-system.css: .jk-header .jk-nav__links collapses at 900px). */
@media (max-width: 900px) { .jk-rail { display: none; } }

/* ---------- 2. Stacking fix ----------
   Every jk-* section paints its own background early (non-positioned block
   box). The line overlay paints later, above every section background
   (z-index: 1). These wrappers hold the section's real, readable content and
   are promoted above the line in turn. Position/z-index only — no offset is
   introduced, so nothing shifts layout. */

.jk-wrap,
.jk-path__inner,
.jk-split__media,
.jk-split__body,
.jk-thesis__inner,
.jk-ctaband__inner,
.jk-marquee__track {
	position: relative;
	z-index: 2;
}

/* ---------- 3. Reveals: gated behind html.jk-motion ----------
   var(--p, 1) defaults to fully visible/settled if the custom property is
   ever absent, and the ancestor gate means none of this applies at all
   without JS. transform/opacity only, per section heading + first
   paragraph — deliberately narrower than the prototype's per-section
   decorative widgets, which had no equivalent in real theme markup. */

html.jk-motion .jk-hero .jk-hero__title,
html.jk-motion .jk-path .jk-path__title,
html.jk-motion .jk-work .jk-section__head,
html.jk-motion .jk-split .jk-split__eyebrow,
html.jk-motion .jk-thesis .jk-thesis__title,
html.jk-motion .jk-ctaband .jk-ctaband__title {
	opacity: var(--p, 1);
	transform: translateY(calc((1 - var(--p, 1)) * 28px));
	transition: opacity .15s linear, transform .15s linear;
	will-change: transform, opacity;
}

html.jk-motion .jk-hero .jk-lede,
html.jk-motion .jk-path .jk-path__pull,
html.jk-motion .jk-work .jk-work__lede,
html.jk-motion .jk-split .jk-split__lede,
html.jk-motion .jk-thesis .jk-thesis__pull,
html.jk-motion .jk-ctaband .jk-ctaband__meta {
	opacity: var(--p, 1);
	transform: translateY(calc((1 - var(--p, 1)) * 20px));
	transition: opacity .15s linear, transform .15s linear;
	will-change: transform, opacity;
}

/* Second and later lede paragraphs in the hero grid settle immediately —
   only the first paragraph per section reveals, per the brief. */
html.jk-motion .jk-hero .jk-lede ~ .jk-lede {
	opacity: 1;
	transform: none;
}

/* ---------- 3b. PATH movement stages: reveal + unresolved/resolved cycle ----------
   blocks/path/style.css renders every stage visual in its RESOLVED state
   (veil open, chips aligned, grid regular, circle complete) with no
   dependency on --p. The rules below are the only place any of that gets
   pushed back to an unresolved starting point — and only once html.jk-motion
   is present, i.e. only once path-motion.js has decided motion is safe to
   run and has started setting --p on each .jk-path__stage every tick.

   All four stages resolve at --p:1: veil open, chips aligned, grid
   coherent, circle complete. Transform's cells start scattered and settle
   into the regular grid — the movement toward coherence IS the statement,
   and the motion end-state equals the resolved no-JS default. */

html.jk-motion .jk-path .jk-path__stage-name {
	opacity: var(--pt, 1);
	transform: translateY(calc((1 - var(--pt, 1)) * 40px));
	transition: opacity .15s linear, transform .15s linear;
	will-change: transform, opacity;
}
html.jk-motion .jk-path .jk-path__stage-text {
	opacity: var(--pt, 1);
	transform: translateY(calc((1 - var(--pt, 1)) * 20px));
	transition: opacity .15s linear, transform .15s linear;
	will-change: transform, opacity;
}

/* Protect: the veil closes toward the viewer and opens as --p rises. */
html.jk-motion .jk-path .jk-path__veil {
	transform: scaleX(calc(1 - var(--pt, 0)));
}
html.jk-motion .jk-path .jk-path__stage--0 .jk-path__stage-text {
	opacity: calc(.4 + .6 * var(--p, 0));
}

/* Adapt: chips start scattered at --drift and settle into place. */
html.jk-motion .jk-path .jk-path__frag {
	transform: translateX(calc((1 - var(--p, 0)) * var(--drift, 0px)));
}

/* Transform: the statement runs TOWARD coherence — the cells start
   scattered (amplified offsets) and settle into the regular grid as --p
   rises. The motion end-state is therefore identical to the resolved
   no-JS default: a coherent grid. */
html.jk-motion .jk-path .jk-path__cell {
	transform:
		translate(
			calc((1 - var(--p, 1)) * var(--tx, 0px) * 2.2),
			calc((1 - var(--p, 1)) * var(--ty, 0px) * 2.2)
		)
		rotate(calc((1 - var(--p, 1)) * var(--rot, 0deg) * 1.6));
	transition: transform .15s linear;
	will-change: transform;
}

/* Heal: each shard flies out to --fly and returns home as --p rises. */
html.jk-motion .jk-path .jk-path__arc {
	transform: rotate(var(--home, 0deg)) translate(calc((1 - var(--p, 0)) * var(--fly, 90px)));
}
html.jk-motion .jk-path .jk-path__core {
	opacity: var(--p, 0);
}

/* ---------- 3c. Footer CTA emphasis seal ----------
   Injected only for a .jk-ctaband.is-seal band (see path-motion.js). Ink
   stroke, drawn via stroke-dashoffset from that band's own --p — the same
   mechanism as the primary seal. Larger than the button and offset to the
   right so the button overlaps the circle's arc, echoing how the primary
   seal lands beside its band. */
/* The emphasis circle may run past the band's right edge by design; clip
   it at the band boundary so it can never create horizontal page scroll.
   overflow-x: clip (not hidden) clips without creating a scroll container,
   and leaves vertical overflow alone. */
html.jk-motion .jk-ctaband.is-seal {
	overflow-x: clip;
}

/* Belt for every scroll-driven transform (scattered cells, drifting chips):
   nothing the motion layer moves may widen the page. Gated on jk-motion so
   the no-JS/reduced-motion render is untouched. */
html.jk-motion,
html.jk-motion body {
	overflow-x: clip;
}

.jk-ctaband__seal-holder {
	position: relative;
	display: inline-block;
}
.jk-ctaband__seal {
	position: absolute;
	inset: -84px -64px;
	transform: translate(28%, 10%);
	pointer-events: none;
	z-index: 0;
}
.jk-ctaband__seal svg { display: block; width: 100%; height: 100%; }
.jk-ctaband__seal circle { fill: none; stroke-width: 2; opacity: .85; }
.jk-ctaband__seal-holder .jk-cta { position: relative; z-index: 1; }

/* ---------- 4. Reduced motion: belt and suspenders ----------
   path-motion.js already exits before injecting any DOM or adding
   html.jk-motion when prefers-reduced-motion is set, so in the ordinary case
   none of the above ever engages. This guard additionally covers a
   mid-session change to the OS motion preference, which a media query (but
   not a one-time JS check) reacts to live. */
/* ---------- Mobile: quiet the ambient layer ----------
   The parallax background words and the moss branch lines add depth on
   desktop; on narrow viewports they overprint the copy and read as noise.
   The gold line, rail (already hidden <=900), and seals remain. */
@media (max-width: 900px) {
	.jk-dims,
	.jk-line-branch {
		display: none !important;
	}
}

@media (prefers-reduced-motion: reduce) {
	#jk-pathline, .jk-dims, .jk-rail, .jk-ctaband__seal { display: none !important; }

	html.jk-motion .jk-hero,
	html.jk-motion .jk-path,
	html.jk-motion .jk-work,
	html.jk-motion .jk-split,
	html.jk-motion .jk-thesis,
	html.jk-motion .jk-ctaband {
		--p: 1 !important;
		--pt: 1 !important;
	}

	/* The PATH stages resolve to their static values directly rather than
	   through --p:1 — Transform's grid resolves at --p:0 while the other
	   three resolve at --p:1, so no single forced --p is correct for all
	   four. Matching blocks/path/style.css's resolved rules exactly here
	   keeps this guard correct independent of the value above. */
	html.jk-motion .jk-path .jk-path__stage-name,
	html.jk-motion .jk-path .jk-path__stage-text {
		opacity: 1 !important;
		transform: none !important;
	}
	html.jk-motion .jk-path .jk-path__veil { transform: scaleX(0) !important; }
	html.jk-motion .jk-path .jk-path__frag { transform: translateX(0) !important; }
	html.jk-motion .jk-path .jk-path__cell { transform: translate(0, 0) rotate(0deg) !important; }
	html.jk-motion .jk-path .jk-path__arc { transform: rotate(var(--home, 0deg)) translate(0) !important; }
	html.jk-motion .jk-path .jk-path__core { opacity: 1 !important; }
	html.jk-motion .jk-path .jk-path__stage--0 .jk-path__stage-text { opacity: 1 !important; }
}
