/* ============================================================
   Services wheel (service_wheel section)
   Loaded only on pages containing the section (inc/wheel-setup.php).

   Mobile / no-JS: plain stacked list. Desktop + JS (.is-enhanced,
   added by assets/js/conxept-wheel.js): the section wrapper is
   rows × 100vh tall, the panel pins, and a continuous scroll
   progress value scrubs the dial rotation and the text travel 1:1,
   while the image crossfades once the incoming slide is half
   visible. Muted tones derive from currentColor so every background
   variant (white / cream / ink / navy) works without extra rules.
   ============================================================ */
.cx-svc {
	--svc-muted: color-mix(in srgb, currentColor 58%, transparent);
	--svc-line: color-mix(in srgb, currentColor 22%, transparent);
}

/* Centred heading + subtext above the wheel. */
.cx-svc__head {
	max-width: 46rem;
	margin: 0 auto 3.5rem;
	text-align: center;
}

.cx-svc__heading {
	font-family: var(--cx-font-display);
	font-size: clamp(2rem, 4.5vw, 3.25rem);
	line-height: 1.08;
	letter-spacing: -0.02em;
	margin: 0;
}

.cx-svc__heading p { margin: 0; }
.cx-svc__heading em { font-style: italic; }
.cx-svc__heading a { color: inherit; text-underline-offset: 0.15em; }

.cx-svc__subtext {
	max-width: 40rem;
	margin: 1rem auto 0;
	color: var(--svc-muted);
	line-height: 1.55;
}

.cx-svc__numbers { display: none; }

.cx-svc__row {
	position: relative; /* containing block for the stretched link overlay */
	display: block;
	color: inherit;
	text-decoration: none;
	padding: 2rem 0;
	border-top: 1px solid var(--svc-line);
}

.cx-svc__title {
    font-family: var(--cx-font-display);
    line-height: 1.1;
    margin: 0 0 0.75rem;
    font-size: 24px;
    color: #12233D;
}

.cx-svc__desc {
    max-width: 42ch;
    margin: 0 0 1rem;
    line-height: 1.55;
    font-size: 18px;
    color: #2A2E35;
}

.cx-svc__tags {
	list-style: none;
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin: 0;
	padding: 0;
}

.cx-svc__tags li {
	font-size: 0.75rem;
	letter-spacing: 0.04em;
	padding: 0.35rem 0.85rem;
	border: 1px solid var(--svc-line);
	border-radius: 999px;
	color: var(--svc-muted);
}

/* Text + chevron link, below the pills. The ::after overlay stretches the
   click target over the whole row (the row is position:relative), so the
   full row is clickable through one valid anchor — no nested links. */
.cx-svc__link {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	margin-top: 1.1rem;
	font-weight: 600;
	font-size: 0.95rem;
	color: inherit;
	text-decoration: underline;
	text-underline-offset: 0.25em;
	text-decoration-thickness: 1px;
}

.cx-svc__tags + .cx-svc__link { margin-top: 1.1rem; }

.cx-svc__link::after {
	content: "";
	position: absolute;
	inset: 0;
}

.cx-svc__chevron { transition: transform 0.25s ease; }

.cx-svc__row:hover .cx-svc__chevron { transform: translateX(3px); }

.cx-svc__media { display: none; }

@media (min-width: 1024px) {
	/* Doubled class beats the theme's .cx-section--{bg} overflow:hidden
	   (which would otherwise become the sticky containing block and kill
	   the pin). clip crops to the rounded corners identically without
	   creating a scroll container. */
	.cx-svc.cx-svc.is-enhanced {
		--svc-r: clamp(15rem, 26vw, 24rem);
		--svc-step: 30deg;
		height: calc(var(--svc-count) * 100vh);
		padding-block: 0;
		overflow: clip;
	}

	.cx-svc.is-enhanced .cx-svc__pin {
		position: sticky;
		top: 0;
		height: 100vh;
		display: flex;
		flex-direction: column;
		justify-content: center;
		overflow: hidden;
	}

	/* Heading rests above the wheel with a generous gap (both visible), then
	   fades and lifts as --svc-intro goes 0 → 1 over the first slice of
	   scroll. It doesn't scrub with the services; it just frames the intro. */
	.cx-svc.is-enhanced .cx-svc__head {
		flex: 0 0 auto;
		margin-bottom: clamp(3rem, 8vh, 6rem);
		opacity: calc(1 - var(--svc-intro, 0));
		transform: translateY(calc(var(--svc-intro, 0) * -3rem));
		/* Once faded, stop it intercepting clicks over the wheel. */
		pointer-events: none;
		will-change: opacity, transform;
	}

	/* The stage slides up into the space the heading vacates, by the same
	   intro amount, so the wheel settles into its centred position exactly
	   as the heading finishes leaving. */
	.cx-svc.is-enhanced .cx-svc__stage {
		transform: translateY(calc(var(--svc-intro, 0) * -1 * clamp(1.5rem, 4vh, 3rem)));
		will-change: transform;
	}

	.cx-svc.is-enhanced .cx-wrap { width: 100%; }

	.cx-svc.is-enhanced .cx-svc__stage {
		display: grid;
		grid-template-columns: minmax(8rem, 0.5fr) 1.5fr minmax(18rem, 1fr);
		align-items: center;
		gap: clamp(2rem, 4vw, 4.5rem);
	}

	/* Number dial: the numbers sit around the circumference of a large
	   circle whose centre is off to the left; the dial's rotation is a
	   pure function of the continuous scroll progress, so it scrubs 1:1
	   with the scrollbar in both directions. Inactive numbers stay tilted
	   along the dashed arc; the section's overflow crops the arc at the
	   panel edges. */
	.cx-svc.is-enhanced .cx-svc__numbers {
		display: block;
		align-self: stretch;
		position: relative;
	}

	.cx-svc.is-enhanced .cx-svc__dial-box {
		position: absolute;
		top: 50%;
		right: 4.5rem;
		width: calc(var(--svc-r) * 2);
		height: calc(var(--svc-r) * 2);
		transform: translateY(-50%);
		border: 1px solid var(--svc-line);
		border-radius: 50%;
	}

	.cx-svc.is-enhanced .cx-svc__dial {
		position: absolute;
		inset: 0;
		transform: rotate(calc(var(--svc-progress, 0) * var(--svc-step) * -1));
		will-change: transform;
	}

	/* Marker dot: lives on the rotating dial, parked at the snapped slide's
	   angle. It rides the arc with the outgoing number as you scroll, then
	   hops to the incoming number at the halfway point (the same moment
	   the image swaps) and settles back at 3 o'clock as the slide centres. */
	.cx-svc.is-enhanced .cx-svc__dial::after {
        content: "";
        position: absolute;
        top: 50%;
        left: 50%;
        width: 14px;
        height: 14px;
        margin: -4px 0 0 -7px;
        border-radius: 50%;
        background: var(--cx-green);
        border: 1px solid #DADADA;
        transform: rotate(calc(var(--svc-snap, 0) * var(--svc-step))) translateX(var(--svc-r));
        transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
    }
.cx-svc__tags + .cx-svc__link:visited {
    color: #2F6F4E;
}
	.cx-svc.is-enhanced .cx-svc__number {
        position: absolute;
        top: 50%;
        left: 50%;
        transform-origin: 0 0;
        transform: rotate(calc(var(--n, 0) * var(--svc-step))) translateX(calc(var(--svc-r) + 1rem)) translateY(-50%);
        font-family: var(--cx-font-display);
        font-family: "IBM Plex Mono", monospace;
        font-size: 30px;
        color: #2A2E35;
        font-weight: 700;
        line-height: 1;
        letter-spacing: -0.02em;
        opacity: 0.3;
        transition: opacity 0.4s ease;
        white-space: nowrap;
    }

	.cx-svc.is-enhanced .cx-svc__number.is-active { opacity: 1; }

	/* Text rows: each slide's text sits (n − progress) viewport-heights
	   from the centre, so adjacent slides are a full screen apart and the
	   column travels continuously with the scroll at full opacity. There
	   is no local clipping — the outgoing text rides all the way up and
	   off the 100vh pinned panel (whose own overflow crops it at the
	   screen edge). */
	.cx-svc.is-enhanced .cx-svc__rows {
		position: relative;
		min-height: clamp(16rem, 42vh, 24rem);
	}

	.cx-svc.is-enhanced .cx-svc__row {
		position: absolute;
		inset: 0;
		display: flex;
		flex-direction: column;
		justify-content: center;
		align-items: flex-start;
		border: 0;
		padding: 0;
		transform: translateY(calc((var(--n, 0) - var(--svc-progress, 0)) * 70vh));
		pointer-events: none;
		will-change: transform;
	}

	.cx-svc.is-enhanced .cx-svc__row.is-active {
		pointer-events: auto;
	}

	/* Shared image frame: crossfade with a slight settle-in, switching at
	   the halfway handoff (driven by the same snap as the dot). */
	.cx-svc.is-enhanced .cx-svc__media {
		display: block;
		position: relative;
		aspect-ratio: 1 / 1;
		border-radius: var(--cx-radius);
		overflow: hidden;
	}

	.cx-svc.is-enhanced .cx-svc__img {
		position: absolute;
		inset: 0;
		width: 100%;
		height: 100%;
		object-fit: contain;
		opacity: 0;
		transform: scale(1.06);
		transition: opacity 0.5s ease, transform 0.8s cubic-bezier(0.22, 1, 0.36, 1);
	}

	.cx-svc.is-enhanced .cx-svc__img.is-active {
		opacity: 1;
		transform: scale(1);
	}
}

@media (prefers-reduced-motion: reduce) {
	/* Scroll-scrubbed movement can be vestibular-triggering, so fall back
	   to instant swaps: the dial and dot stay still with the active number
	   highlighted, and rows appear in place instead of travelling. */
	.cx-svc.is-enhanced .cx-svc__dial { transform: none !important; }

	.cx-svc.is-enhanced .cx-svc__dial::after {
		transform: translateX(var(--svc-r)) !important;
		transition: none;
	}

	.cx-svc.is-enhanced .cx-svc__row {
		transform: none !important;
		opacity: 0;
	}

	.cx-svc.is-enhanced .cx-svc__row.is-active { opacity: 1; }

	.cx-svc.is-enhanced .cx-svc__img {
		transition: opacity 0.2s linear;
		transform: none !important;
	}

	/* Keep the heading readable and the stage put — no intro motion. */
	.cx-svc.is-enhanced .cx-svc__head {
		opacity: 1 !important;
		transform: none !important;
	}

	.cx-svc.is-enhanced .cx-svc__stage {
		transform: none !important;
	}
}

/* ============================================================
   MOBILE scroll wheel (< 1024px) — rebuilt from frame-by-frame
   measurement of the reference:

   · Numbers: horizontal strip, gap --svc-gap, anchored so the ACTIVE
     number rests at 42% viewport width with the next one faded to its
     right. The strip slides LEFT continuously with scroll
     (x = (n − progress) · gap). Incoming numbers arrive from the right.
   · A subtle dashed arc line curves behind the strip; the dot rides
     above the active number and hops to the incoming one at the snap.
   · Text (title → desc → tags) swaps at the snap via the .is-active /
     .is-past classes the JS already toggles — a quick staggered
     cascade, so slides never visibly overlap.
   · Image block unchanged (already correct).

   REPLACE the previous mobile block entirely with this one.
   ============================================================ */
@media (max-width: 1023.98px) {

	.cx-svc.cx-svc.is-enhanced {
		--svc-gap: clamp( 80px, 24vw, 120px ); /* spacing between numbers */
		height: calc( var( --svc-count ) * 100vh );
		padding-block: 0;
		overflow: clip;
	}

	.cx-svc.is-enhanced .cx-svc__pin {
		position: sticky;
		top: 0;
		height: 100vh;
		display: flex;
		flex-direction: column;
		justify-content: center;
		overflow: hidden;
	}

	/* Intro heading hands off to the wheel, as on desktop. */
	.cx-svc.is-enhanced .cx-svc__head {
		flex: 0 0 auto;
		margin-bottom: clamp( 1rem, 4vh, 2rem );
		opacity: calc( 1 - var( --svc-intro, 0 ) );
		transform: translateY( calc( var( --svc-intro, 0 ) * -2rem ) );
		pointer-events: none;
		will-change: opacity, transform;
	}

	.cx-svc.is-enhanced .cx-svc__stage {
		display: flex;
		flex-direction: column;
		align-items: stretch;
		gap: clamp( 1rem, 3.5vh, 2rem );
	}

	/* ---------------- Numbers strip ---------------- */
	.cx-svc.is-enhanced .cx-svc__numbers {
		display: block;
		position: relative;
		order: 1;
		height: 80px;
		overflow: hidden;           /* clips numbers sliding out + the arc line */
	}

	/* Dashed arc line curving UPWARD (∪): the bottom slice of a big dashed
	   circle, its lowest point (the dip) at y = 30px inside the strip. */
	.cx-svc.is-enhanced .cx-svc__numbers::before {
		content: "";
		position: absolute;
		left: 50%;
		top: calc( 30px - 1100px );
		width: 1100px;
		height: 1100px;
		margin-left: -550px;
		border-radius: 50%;
		border: 1px solid rgba( 42, 46, 53, 0.25 );
		pointer-events: none;
	}

	/* Neutralise the desktop dial geometry. */
	.cx-svc.is-enhanced .cx-svc__dial-box,
	.cx-svc.is-enhanced .cx-svc__dial {
		position: absolute;
		inset: 0;
		width: auto;
		height: auto;
		transform: none;
		border: 0;
		border-radius: 0;
	}

	/* Each number: anchored at 42%, offset by (n − progress) · gap.
	   Rest state = active at 42%, next faded at ~66% (measured off the
	   reference). Scroll slides everything left; next arrives from right. */
	.cx-svc.is-enhanced .cx-svc__number {
		position: absolute;
		left: 42%;
		top: 38px;
		transform:
			translateX( calc( -50% + ( var( --n, 0 ) - var( --svc-progress, 0 ) ) * var( --svc-gap ) ) )
			translateY( calc( max( var( --n, 0 ) - var( --svc-progress, 0 ), ( var( --n, 0 ) - var( --svc-progress, 0 ) ) * -1 ) * -12px ) );
		font-family: "IBM Plex Mono", monospace;
		font-size: 18px;
		font-weight: 700;
		line-height: 1;
		letter-spacing: -0.02em;
		color: #2A2E35;
		opacity: 0.3;
		transition: opacity 0.3s ease, font-size 0.3s ease;
		white-space: nowrap;
		will-change: transform;
	}

	.cx-svc.is-enhanced .cx-svc__number.is-active {
		font-size: 30px;
		opacity: 1;
	}

	/* Dot: rides with the ACTIVE number (same offset formula, but using
	   --svc-snap), so it travels with it and hops to the incoming number
	   when the snap flips — smoothed by the short transition. */
	.cx-svc.is-enhanced .cx-svc__dial::after {
		content: "";
		position: absolute;
		left: 42%;
		top: 25px;
		width: 9px;
		height: 9px;
		margin-left: -4.5px;
		border-radius: 50%;
		background: var( --cx-green );
		transform: translateX( calc( ( var( --svc-snap, 0 ) - var( --svc-progress, 0 ) ) * var( --svc-gap ) ) );
		transition: transform 0.25s ease-out;
		will-change: transform;
	}

	/* ---------------- Text rows: crisp snap-cascade ----------------
	   Driven by the .is-active / .is-past classes the JS flips at the
	   halfway point — NOT by a scrubbed fade — so only one row is ever
	   meaningfully visible. Title leads, desc follows, tags last. */
	.cx-svc.is-enhanced .cx-svc__rows {
		position: relative;
		order: 2;
		min-height: clamp( 11rem, 26vh, 14rem );
		text-align: center;
	}

	.cx-svc.is-enhanced .cx-svc__row {
		position: absolute;
		inset: 0;
		display: flex;
		flex-direction: column;
		justify-content: flex-start;
		align-items: center;
		border: 0;
		padding: 0;
		margin: 0;
		pointer-events: none;
	}

	/* Per-element states: incoming waits below, active rests, past exits up. */
	.cx-svc.is-enhanced .cx-svc__row .cx-svc__title,
	.cx-svc.is-enhanced .cx-svc__row .cx-svc__desc,
	.cx-svc.is-enhanced .cx-svc__row .cx-svc__tags,
	.cx-svc.is-enhanced .cx-svc__row .cx-svc__link {
		opacity: 0;
		transform: translateY( 34px );
		transition: opacity 0.4s ease, transform 0.55s cubic-bezier( 0.22, 1, 0.36, 1 );
	}
	.cx-svc.is-enhanced .cx-svc__row.is-past .cx-svc__title,
	.cx-svc.is-enhanced .cx-svc__row.is-past .cx-svc__desc,
	.cx-svc.is-enhanced .cx-svc__row.is-past .cx-svc__tags,
	.cx-svc.is-enhanced .cx-svc__row.is-past .cx-svc__link {
		opacity: 0;
		transform: translateY( -34px );
	}
	.cx-svc.is-enhanced .cx-svc__row.is-active .cx-svc__title,
	.cx-svc.is-enhanced .cx-svc__row.is-active .cx-svc__desc,
	.cx-svc.is-enhanced .cx-svc__row.is-active .cx-svc__tags,
	.cx-svc.is-enhanced .cx-svc__row.is-active .cx-svc__link {
		opacity: 1;
		transform: translateY( 0 );
	}
	/* Stagger: title leads, desc then tags/link trail. */
	.cx-svc.is-enhanced .cx-svc__row .cx-svc__desc { transition-delay: 0.08s; }
	.cx-svc.is-enhanced .cx-svc__row .cx-svc__tags { transition-delay: 0.16s; }
	.cx-svc.is-enhanced .cx-svc__row .cx-svc__link { transition-delay: 0.22s; }

	.cx-svc.is-enhanced .cx-svc__row.is-active { pointer-events: auto; }

	.cx-svc.is-enhanced .cx-svc__title {
		font-size: clamp( 1.6rem, 7vw, 2.1rem );
		margin: 0 0 0.75rem;
	}
	.cx-svc.is-enhanced .cx-svc__desc {
		margin: 0 auto 1rem;
		font-size: 16px;
		max-width: 34ch;
	}
	.cx-svc.is-enhanced .cx-svc__tags { justify-content: center; }

	/* ---------------- Image (unchanged — already correct) ---------------- */
	.cx-svc.is-enhanced .cx-svc__media {
		display: block;
		position: relative;
		order: 3;
		width: clamp( 12rem, 62vw, 20rem );
		margin: 0 auto;
		aspect-ratio: 1 / 1;
		border-radius: var( --cx-radius );
		overflow: hidden;
	}
	.cx-svc.is-enhanced .cx-svc__img {
		position: absolute;
		inset: 0;
		width: 100%;
		height: 100%;
		object-fit: contain;
		opacity: 0;
		transform: scale( 1.06 );
		transition: opacity 0.5s ease, transform 0.8s cubic-bezier( 0.22, 1, 0.36, 1 );
	}
	.cx-svc.is-enhanced .cx-svc__img.is-active {
		opacity: 1;
		transform: scale( 1 );
	}
}