/*
 * Hero carousel.
 *
 * Sizing is aspect-ratio driven so the space is reserved before the image
 * loads — this element is the LCP candidate and a late-arriving hero is the
 * classic source of layout shift (CLS).
 */

.jmns-slider {
	position: relative;
	background-color: var(--wp--preset--color--text-primary, #1F2925);
}

.jmns-slider__viewport {
	overflow: hidden;
}

.jmns-slider__track {
	display: grid;
	margin: 0;
	padding: 0;
	list-style: none;
}

/* Slides stack in one grid cell; only the active one is visible. */
.jmns-slider__slide {
	grid-area: 1 / 1;
	margin: 0;
	opacity: 0;
	visibility: hidden;
	/* §11 asks for 500-700ms. 600 sits in the middle and is slow enough to
	   read as a crossfade rather than a cut. */
	transition: opacity 600ms ease-out;
}

.jmns-slider__slide.is-active {
	opacity: 1;
	visibility: visible;
}

/*
 * Height, per client brief §6: 460-520 desktop, 380-440 tablet, 420-560 mobile.
 *
 * This was `aspect-ratio: 1240 / 300` — the shape of the artwork the department
 * happened to supply — which rendered a 310px band on a 1280px screen and made
 * the hero read as a banner pasted onto the page rather than part of it. The
 * ratio also meant the hero got shorter as the window narrowed, which is
 * backwards: a phone has less width to say anything in, not less to say.
 *
 * So the box is now a height, clamped, and the image covers it. Reserving that
 * height up front still avoids the layout shift the ratio was there to prevent.
 */
.jmns-slider__viewport,
.jmns-slider__track {
	min-block-size: clamp(420px, 44vw, 520px);
}

@media (min-width: 48rem) and (max-width: 64rem) {
	.jmns-slider__viewport,
	.jmns-slider__track {
		min-block-size: clamp(380px, 42vw, 440px);
	}
}

.jmns-slider__image {
	display: block;
	inline-size: 100%;
	block-size: 100%;
	min-block-size: inherit;
	object-fit: cover;
	object-position: var(--jmns-slide-focus, center);
}

.jmns-slider__link {
	display: block;
	text-decoration: none;
}

/* ------------------------------------------------------------- caption -- */

/*
 * The words sit *on* the image, not in a band beneath it.
 *
 * The old caption was a solid ink strip across the bottom — honest about
 * contrast, and the reason the hero looked like a stock photo with a label
 * gummed to it. Brief §9 asks for an overlay that "should feel invisible and
 * functional", which is a harder promise: the text has to stay readable over
 * artwork nobody has seen yet.
 *
 * Hence a gradient that is strongest where the words are and clears away from
 * them, plus an editor-set strength (§7). Four named steps rather than a
 * percentage box, because an editor asked for a number picks one that suits
 * their own image and leaves the text failing on somebody else's screen.
 */
.jmns-slider__caption {
	position: absolute;
	inset: 0;
	display: grid;
	align-items: center;
	padding: clamp(1.5rem, 5vw, 4rem);
	color: var(--wp--preset--color--background, #FAF8F2);
}

/*
 * The overlay is a pseudo-element so it sits between image and text without a
 * wrapper, and so `none` genuinely paints nothing.
 */
.jmns-slider__caption::before {
	content: "";
	position: absolute;
	inset: 0;
	/*
	 * The strong stop is held across the whole text column, then cleared.
	 *
	 * The first version faded from the edge and reached the gradient's midpoint
	 * at roughly half strength, which measured **3.22:1** for white body text
	 * over a white image — the subheading ran straight into it. A gradient that
	 * is only legible at its darkest end is not an overlay, it is a vignette.
	 *
	 * `--jmns-overlay-hold` is therefore just past where the text column ends:
	 * the column is capped at 34rem and starts at the shell's edge, so on a
	 * 1280px viewport it finishes near 46%.
	 */
	background: linear-gradient(
		var(--jmns-overlay-angle, 90deg),
		rgb(var(--jmns-primary-hover-rgb, 23 61 50) / var(--jmns-overlay-alpha, 0.82)) 0%,
		rgb(var(--jmns-primary-hover-rgb, 23 61 50) / var(--jmns-overlay-alpha, 0.82)) var(--jmns-overlay-hold, 48%),
		rgb(var(--jmns-primary-hover-rgb, 23 61 50) / 0) 84%
	);
}

/*
 * Measured against a pure white image, which is the worst case a photograph
 * can present: 0.675 alpha is the floor at which white body text still clears
 * 4.5:1, so `light` sits just above it rather than at a round number that
 * happens to look nice on one banner.
 */
[data-overlay="none"] .jmns-slider__caption::before { --jmns-overlay-alpha: 0; }
[data-overlay="light"] .jmns-slider__caption::before { --jmns-overlay-alpha: 0.70; }  /* 4.84:1 */
[data-overlay="medium"] .jmns-slider__caption::before { --jmns-overlay-alpha: 0.82; } /* 6.92:1 */
[data-overlay="strong"] .jmns-slider__caption::before { --jmns-overlay-alpha: 0.92; } /* 9.38:1 */

/* Text to the right means the gradient has to come from the right. */
[data-align="end"] .jmns-slider__caption::before { --jmns-overlay-angle: 270deg; }

/* Centred text has no clear side, so the fall-off runs bottom to top instead. */
[data-align="center"] .jmns-slider__caption::before {
	background: linear-gradient(
		180deg,
		rgb(var(--jmns-primary-hover-rgb, 23 61 50) / 0) 0%,
		rgb(var(--jmns-primary-hover-rgb, 23 61 50) / var(--jmns-overlay-alpha, 0.82)) 38%,
		rgb(var(--jmns-primary-hover-rgb, 23 61 50) / var(--jmns-overlay-alpha, 0.82)) 100%
	);
}

.jmns-slider__caption-inner {
	position: relative;
	inline-size: min(100%, 34rem);
	/* The shell, so hero text lines up with the page's own left edge rather
	   than floating at an arbitrary inset. */
	margin-inline: max(0px, calc((100% - var(--wp--style--global--wide-size, 75rem)) / 2));
}

[data-align="center"] .jmns-slider__caption-inner {
	margin-inline: auto;
	text-align: center;
}

[data-align="end"] .jmns-slider__caption-inner {
	margin-inline-start: auto;
	text-align: end;
}

.jmns-slider__eyebrow {
	margin: 0 0 0.75rem;
	font-size: var(--wp--preset--font-size--caption, 0.8125rem);
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	opacity: 0.92;
}

.jmns-slider__heading {
	margin: 0;
	font-family: var(--wp--preset--font-family--heading, inherit);
	/* §3: 48-56px at the top end. */
	font-size: clamp(2rem, 4.4vw + 0.5rem, 3.5rem);
	font-weight: 700;
	line-height: 1.1;
	text-wrap: balance;
}

.jmns-slider__subheading {
	margin: 1rem 0 0;
	max-inline-size: 46ch;
	font-size: clamp(1rem, 0.5vw + 0.9rem, 1.125rem);
	line-height: 1.55;
	opacity: 0.94;
}

[data-align="center"] .jmns-slider__subheading { margin-inline: auto; }
[data-align="end"] .jmns-slider__subheading { margin-inline-start: auto; }

/*
 * The button is the link on a content slide. The whole slide is not wrapped in
 * an anchor when there is a caption — that would nest one control in another —
 * so this is the only thing to press, and it is sized like it.
 */
.jmns-slider__cta {
	display: inline-flex;
	align-items: center;
	min-block-size: 48px;
	margin-block-start: 1.75rem;
	padding-inline: var(--wp--preset--spacing--40, 1.5rem);
	border-radius: var(--r-pill, 999px);
	background-color: var(--wp--preset--color--background, #FAF8F2);
	color: var(--wp--preset--color--primary-hover, #173D32);
	font-weight: 600;
	text-decoration: none;
	transition: background-color var(--t-fast, 180ms) var(--ease, ease),
		transform var(--t-fast, 180ms) var(--ease, ease);
}

.jmns-slider__cta:hover {
	/* The button rests on the page ground and lifts to the surface on hover, so
	   this is `surface` rather than a white that happens to match it today. */
	background-color: var(--wp--preset--color--surface, #FFFFFF);
	/* §25: 2-4px, no more. */
	transform: translateY(-2px);
}

@media (prefers-reduced-motion: reduce) {
	.jmns-slider__cta:hover {
		transform: none;
	}
}

/* ------------------------------------------------------------ controls -- */

/*
 * Floating on the slide, not a bar under it.
 *
 * The controls were a full-width ink strip below the viewport — a permanent
 * 60px band of furniture that read as part of the page rather than part of the
 * carousel, and on the homepage it sat directly above the service tiles.
 *
 * The cluster now hovers over the bottom-centre of the slide, quiet by default:
 * it is an indicator first and a control second.
 */
.jmns-slider__controls {
	position: absolute;
	inset-block-end: var(--wp--preset--spacing--20, 0.5rem);
	inset-inline: 0;
	z-index: 2;
	display: flex;
	align-items: center;
	justify-content: center;
	inline-size: fit-content;
	margin-inline: auto;
	/* No inline padding: the 26px targets already carry their own breathing
	   room, so padding here only widened the pill around them. */
	padding: 0;
	border-radius: var(--r-pill, 999px);
	/*
	 * A quieter scrim than the cluster started with. It is not decoration and
	 * cannot go: the dots have to stay findable over any banner an editor
	 * uploads, including a white one, and SC 1.4.11 asks 3:1 of a control. This
	 * is the least ink that still guarantees it.
	 */
	background-color: rgb(var(--jmns-shade-rgb, 38 51 42) / 0.32);
	/*
	 * No opacity fade. It used to sit at 0.75 and rise to 1 on hover, which
	 * faded the dots and the scrim toward the banner together — so on a pale
	 * banner the active dot measured 1.01:1 against the strip it sits on, where
	 * SC 1.4.11 asks 3:1 of a control. Fading a control below the contrast floor
	 * was never a legitimate way to make it quiet; the quiet comes from the dots
	 * being 6px and the scrim being light.
	 */
	transition: background-color var(--t-fast, 150ms) var(--ease, ease);
}

/*
 * Reachable, not prominent. The cluster is an indicator first — it says which
 * of three banners you are looking at — and only becomes a control when someone
 * goes for it. Hover and keyboard focus both bring it back to full.
 */
/* Deepens on approach rather than un-fading, so contrast only ever improves. */
.jmns-slider:hover .jmns-slider__controls,
.jmns-slider__controls:focus-within {
	background-color: rgb(var(--jmns-shade-rgb, 38 51 42) / 0.48);
}

/*
 * The ring on this band is the scrim's own ink.
 *
 * It was `primary-light`, which is a brand tint rather than a statement about
 * what can be seen on a scrim: under `kontras` it is #0B2E22 and the ring
 * disappears into the band it is drawn on. `--jmns-on-scrim` stays light under
 * every scheme, which is what a control over a photograph needs. It is also the
 * stronger ring on the band as it stands — measured over a mid-tone frame,
 * white is 5.78:1 at rest and 7.01:1 on approach where primary-light was 2.42:1
 * and 2.94:1, both of them under the 3:1 SC 1.4.11 asks of a focus indicator.
 */
.jmns-slider :focus-visible {
	outline: 3px solid var(--jmns-on-scrim, #FFFFFF);
	outline-offset: 2px;
}

/*
 * Counter and arrows (client brief §11).
 *
 * "01 / 04" instead of bullets. It says the same two things a dot row said —
 * where you are, how many there are — in a vocabulary that is not shared with
 * every template on the internet, which is the brief's actual complaint.
 */
.jmns-slider__counter {
	display: inline-flex;
	align-items: baseline;
	gap: 0.4rem;
	margin: 0;
	/* Tabular, or the arrows either side shift as 09 becomes 10. */
	font-variant-numeric: tabular-nums;
	font-size: var(--wp--preset--font-size--caption, 0.8125rem);
	font-weight: 600;
	letter-spacing: 0.08em;
	color: var(--wp--preset--color--background, #FAF8F2);
}

.jmns-slider__counter-current {
	font-size: var(--wp--preset--font-size--small, 0.875rem);
}

.jmns-slider__counter-sep,
.jmns-slider__counter-total {
	opacity: 0.7;
}

.jmns-slider__btn {
	inline-size: 24px;
	block-size: 24px;
	/* Same override as the dots: the theme floors every button at 48px. */
	min-inline-size: 24px;
	min-block-size: 24px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 0;
	border: 0;
	background: transparent;
	color: var(--wp--preset--color--background, #FAF8F2);
	font-size: 0.5rem;
	line-height: 1;
	cursor: pointer;
	transition: opacity var(--t-fast, 150ms) var(--ease, ease);
}

/*
 * Stated so a theme's shared `button:hover` cannot fill these.
 *
 * Neither control is a filled button: the pause is clipped at rest and the dot
 * is a 6px ring inside a 24px target, so a background on either is wrong at any
 * strength. The dot brightens its own ring instead, which is the part anyone is
 * actually aiming at.
 */
.jmns-slider__btn:hover {
	background-color: transparent;
	box-shadow: none;
}

/*
 * Hidden at rest, and still a real control.
 *
 * Clipped rather than display:none, which is the same technique the skip link
 * uses: display:none would drop it from the tab order and take the mechanism
 * away from everyone, which is the one thing SC 2.2.2 forbids.
 *
 * The criterion is satisfied three ways, which is what makes hiding it safe:
 *   - pointer: slider.js stops rotation on mouseenter, so hovering pauses
 *   - keyboard: focusin stops it too, and Tab brings this button back visible
 *   - touch: neither fires reliably, so the button stays on screen there
 */
.jmns-slider__btn--pause {
	position: absolute;
	inline-size: 1px;
	block-size: 1px;
	min-inline-size: 0;
	min-block-size: 0;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

/* Tabbed to: full size, back in the cluster, where it can be seen and used. */
.jmns-slider__btn--pause:focus,
.jmns-slider__btn--pause:focus-visible {
	position: static;
	inline-size: 24px;
	block-size: 24px;
	min-inline-size: 24px;
	min-block-size: 24px;
	overflow: visible;
	clip-path: none;
	opacity: 1;
}

/*
 * Touch has no hover, so the JS pause-on-hover never fires and a clipped button
 * would leave those visitors with no mechanism at all. It stays visible there.
 */
@media (hover: none) {
	.jmns-slider__btn--pause {
		position: static;
		inline-size: 24px;
		block-size: 24px;
		min-inline-size: 24px;
		min-block-size: 24px;
		overflow: visible;
		clip-path: none;
		opacity: 0.55;
	}
}

.jmns-slider.is-single .jmns-slider__controls {
	display: none;
}

/* --------------------------------------------------------------- motion -- */

@media (prefers-reduced-motion: reduce) {
	.jmns-slider__slide {
		transition: none;
	}
}

@media (max-width: 40rem) {
	/*
	 * The banner keeps its own proportions on a phone.
	 *
	 * It used to be forced to 16/9 and cropped to fill, on the reasoning that a
	 * wide banner becomes an unreadable letterbox. That is true of a photograph
	 * with a subject in the middle and wrong for these: all three slides are
	 * 1240x300 graphics with their message set as type across the full width, so
	 * cropping to 16/9 threw away 57% of it and left "…nyaan / isu atau /
	 * agama?" on screen.
	 *
	 * Neither choice makes a 1240px-wide banner comfortable at 390px. Letterboxed
	 * it is small; cropped it is gone. Small is recoverable — the whole graphic
	 * is there to pinch, and every slide carries alt text (ADR-025) — so this is
	 * the lesser of the two.
	 *
	 * The real fix is a banner authored for a narrow screen. Recorded in
	 * HANDOVER.md §11 as content for JMNS.
	 *
	 * The ratio is the file's own, published by the renderer. It was hardcoded
	 * to 1240/300 here, which is the shape of no banner currently in use, so
	 * every one of them was cropped to a shape it was not drawn in.
	 */
	.jmns-slider__image {
		/* `block-size: auto` so the ratio decides, not the stretched slide. */
		aspect-ratio: var(--jmns-slide-ratio, 1240 / 300);
		block-size: auto;
		flex: none;
		min-block-size: 0;
	}

	/*
	 * And the band is only as tall as what is in it.
	 *
	 * The viewport keeps a `min-block-size` of 420-520px from the desktop rule
	 * above, which was written for a hero the image fills. On a phone it does
	 * not fill it: a letterboxed banner is 130px and the caption under it is
	 * 194px, so the rule reserved 450px for 324px of content and left 126px of
	 * empty green between the subheading and the slide counter. Reserving space
	 * for an image is what `aspect-ratio` above is for; reserving it twice just
	 * makes a hole.
	 */
	.jmns-slider__viewport,
	.jmns-slider__track {
		min-block-size: 0;
	}

	/*
	 * A slide that has a phone cut keeps the shape it was cut to.
	 *
	 * This used to pin the band to 260px and `cover` it, on the reasoning that
	 * the mobile brief asks for 240-280 and an editor uploading a phone image
	 * would be cutting one to fit that band. That held while no phone image
	 * existed. The three that were made are portrait — the wide banner split in
	 * two and stacked, so the type is twice the size and none of the message is
	 * lost — and a 260px `cover` threw away two thirds of each one, which is the
	 * same failure as the 16/9 crop the note above exists to prevent, arriving
	 * by a different route.
	 *
	 * So the image is shown at its own proportions. A cut made for this slot is
	 * already the right shape by definition; the only thing worth enforcing is
	 * that it cannot swallow the screen, hence the cap.
	 *
	 * `object-position` still comes from the slide, for the one case that does
	 * crop: an image taller than the cap.
	 */
	.jmns-slider__slide--framed .jmns-slider__image {
		/*
		 * The ratio comes from the slide, which got it from the file. Left to
		 * `auto`, the two slides that are not the first have no decoded image to
		 * measure and the browser fell back on whatever the stylesheet allowed —
		 * 60vh, which is most of a phone.
		 */
		aspect-ratio: var(--jmns-slide-ratio, 1240 / 300);
		block-size: auto;
		object-fit: cover;
		object-position: center var(--jmns-slide-focus, center);
	}

	/*
	 * Let a phone cut stand at its own height.
	 *
	 * Every slide sits in the same grid cell so they can cross-fade, and a grid
	 * item stretches to its row by default — which, once the images were allowed
	 * `block-size: auto`, made each one resolve against the 60vh cap instead of
	 * its own proportions. The banner came out 506px tall on a 844px screen and
	 * pushed the announcements off it. Aligned to the start, the row is as tall
	 * as the tallest cut and no taller.
	 */
	/*
	 * Every slide fills the row, and its caption fills whatever the banner
	 * leaves.
	 *
	 * This used to be `align-self: start`, added when the images had no ratio
	 * of their own and would otherwise resolve against a 60vh cap. They all
	 * publish one now, so the row no longer decides their height and there is
	 * nothing left for `start` to protect against — while the gap it caused is
	 * plainly visible: all three slides share one grid cell so they can
	 * cross-fade, which makes the cell as tall as the longest caption. The
	 * shortest is 194px against the longest at 356px, so slide one ended below
	 * 162px of bare green with the counter stranded under it.
	 *
	 * Stretching the slide and letting the dark caption take the slack turns
	 * that hole into padding. The panel is continuous, the text is centred in
	 * it, and each slide reads the same whichever one is showing.
	 */
	.jmns-slider__slide {
		align-self: stretch;
		display: flex;
		flex-direction: column;
	}

	/*
	 * The caption goes back over the picture.
	 *
	 * It had been lifted out into a solid block below the image. The client's
	 * first version put the words on the artwork and relied on the overlay
	 * strength to carry them, which is what the per-slide `light` / `medium` /
	 * `strong` setting is for, and stacking them threw that away.
	 *
	 * Two changes from the desktop treatment, because the crop is a different
	 * shape. On a wide banner the scrim runs left to right and the text sits in
	 * the covered half; at 390px the image is portrait — 390x487 — so the scrim
	 * runs bottom to top and the text sits in the covered end. Same mechanism,
	 * same setting, turned ninety degrees to match the picture it is over.
	 */
	.jmns-slider__caption {
		align-items: end;
		background-color: transparent;
		/*
		 * Room at the foot for the pager, which sits inside the picture too.
		 * With the caption's even 24px the last line of the subheading ran 8px
		 * under "01 / 03" and lost its descenders. The controls are 24px tall
		 * and want clear air around them, not a shared row.
		 */
		padding-block-end: 4rem;
	}

	.jmns-slider__caption::before {
		/* 0deg is bottom-up: densest where the words are. */
		--jmns-overlay-angle: 0deg;
		/*
		 * Held further than the desktop 48%. A portrait crop gives the gradient
		 * less room to resolve, and a scrim that starts fading immediately
		 * under the first line is the one that fails on a bright photograph.
		 */
		--jmns-overlay-hold: 58%;
	}
}

/* --------------------------------------------------------------- touch -- */

/*
 * 44px of target on a touch screen, without a 44px dot.
 *
 * The arrows stay visually small so the cluster reads as an indicator rather
 * than a control strip; the pseudo-element underneath is what the thumb
 * actually hits. §23 asks for 44px targets and §11 for restraint, and those are
 * only compatible if the target is not the mark.
 */
@media (pointer: coarse) {
	.jmns-slider__btn {
		position: relative;
	}

	.jmns-slider__btn::after {
		content: "";
		position: absolute;
		inset: 50% auto auto 50%;
		translate: -50% -50%;
		inline-size: 44px;
		block-size: 44px;
	}
}

/*
 * The pause glyph was 8px — below the 12px floor, and it is the one control on
 * the banner that has to be operable. It is drawn text, not an icon font, so it
 * simply has to be bigger.
 */
.jmns-slider__btn-icon {
	font-size: 12px;
	line-height: 1;
}
