/**
 * Viora Hero slider.
 *
 * Built to the Viora hero frame: full-bleed cover image with a dark overlay,
 * an oversized display title, numbered pagination with a progress line and a
 * meta bar. Loaded by Elementor only where the widget is used.
 */

.viora-hero {
	--viora-hero-height: 100vh;
	--viora-hero-edge: var(--viora-layout-edge);
	/* 31px at the 1443px artboard — the meta bar sits tighter to the edge. */
	--viora-hero-footer-bottom: clamp(1rem, 2.15vw, 1.9375rem);
	--viora-hero-overlay: var(--viora-color-overlay);
	--viora-hero-title-y: 51.5%;
	--viora-hero-number-color: #ffffff;
	--viora-hero-number-idle: 0.4;
	--viora-hero-line: rgba(255, 255, 255, 0.3);
	--viora-hero-line-active: #ffffff;
	--viora-hero-page-gap: clamp(1rem, 2.2vw, 2rem);
	--viora-hero-intro-plate: 300px;
	--viora-hero-intro-ratio: 1.4;
	--viora-hero-intro-rise: 26vh;
	--viora-hero-intro-bg: var(--viora-color-surface, #ffffff);
	/* Stage lengths. The script overwrites these from the Timing controls;
	   the values here only matter for the instant before it runs. */
	--viora-hero-intro-rise-ms: 700ms;
	--viora-hero-intro-open-ms: 1100ms;
	--viora-hero-intro-reveal-ms: 800ms;
	--viora-hero-meta-color: #ffffff;
	--viora-hero-arrow-size: 20px;

	position: relative;
	width: 100%;
	height: var(--viora-hero-height);
	overflow: hidden;
	background-color: #000000;
	isolation: isolate;
}

/* -------------------------------------------------- *
 * Slides
 * -------------------------------------------------- */

.viora-hero__viewport {
	position: absolute;
	inset: 0;
	overflow: hidden;
}

.viora-hero__slides {
	position: absolute;
	inset: 0;
}

.viora-hero__slide {
	position: absolute;
	inset: 0;
	opacity: 0;
	will-change: transform;
	backface-visibility: hidden;
}

.viora-hero__slide[data-index="0"] {
	opacity: 1;
}

.viora-hero.is-ready .viora-hero__slide {
	opacity: 1;
}

.viora-hero__media {
	position: absolute;
	inset: 0;
	overflow: hidden;
}

.viora-hero__media img {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	will-change: transform;
	backface-visibility: hidden;
	user-select: none;
	-webkit-user-drag: none;
}

/* WebGL distortion layer ------------------------------------------- */

.viora-hero__canvas {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
}

/*
 * The DOM slides stay in the document so the browser keeps loading and
 * decoding the images — they are only hidden once WebGL actually paints.
 */
.viora-hero.is-webgl-painted .viora-hero__slides {
	opacity: 0;
}

.viora-hero__overlay {
	position: absolute;
	inset: 0;
	background-color: var(--viora-hero-overlay);
	pointer-events: none;
}

/* Drag affordance */

.viora-hero.is-draggable .viora-hero__viewport {
	cursor: grab;
	/*
	 * Touch devices: claim horizontal gestures for the slider and leave
	 * vertical ones to the page, so dragging never fights the scroll.
	 */
	touch-action: pan-y;
	-webkit-tap-highlight-color: transparent;
}

.viora-hero.is-dragging .viora-hero__viewport {
	cursor: grabbing;
}

/* -------------------------------------------------- *
 * Opening animation
 *
 * A window onto the first picture, not a scaled copy of it: the frame changes
 * size while the inner layer stays viewport-sized, so the photograph is never
 * stretched on the way out.
 *
 * Every stage is a class the script adds. Nothing here hides anything until
 * `is-intro` is set, so a failed or absent script leaves the hero as it was.
 * -------------------------------------------------- */

.viora-hero__intro {
	position: absolute;
	inset: 0;
	z-index: var(--viora-z-overlay);
	display: grid;
	place-items: center;
	/* The blank screen the opening starts on. */
	background-color: var(--viora-hero-intro-bg);
	opacity: 0;
	pointer-events: none;
	visibility: hidden;
}

.viora-hero.is-intro .viora-hero__intro {
	opacity: 1;
	visibility: visible;
}

/*
 * Stage 0 — blank. The plate is fully out of sight, so the first beat of the
 * opening is an empty screen and nothing else.
 */
.viora-hero__intro-frame {
	position: relative;
	width: var(--viora-hero-intro-plate);
	/* Height as an explicit length, not aspect-ratio: ratio cannot be animated
	   to `auto`, so the plate's height snapped to full the instant the open
	   began while only the width travelled. */
	height: calc(var(--viora-hero-intro-plate) / var(--viora-hero-intro-ratio));
	overflow: hidden;
	transform: translateY(var(--viora-hero-intro-rise));
	opacity: 0;
}

/*
 * The picture fills the plate, so the small version is the whole photograph
 * shrunk — not a little window cropped out of a full-size one. As the frame
 * grows the picture grows with it and object-fit re-crops; it is never
 * stretched at any size.
 */
.viora-hero__intro-inner {
	position: absolute;
	inset: 0;
}

.viora-hero__intro-inner img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Stage 1 — the plate rises from below into the centre. */
.viora-hero.is-intro-rise .viora-hero__intro-frame {
	opacity: 1;
	transform: translateY(0);
	transition:
		opacity calc(var(--viora-hero-intro-rise-ms) * 0.55) var(--viora-motion-easing),
		transform var(--viora-hero-intro-rise-ms) cubic-bezier(0.16, 1, 0.3, 1);
}

/*
 * Stage 3 — it opens to full bleed. Width leads height by a tenth of the
 * stage, so the picture draws apart like a curtain instead of inflating, and
 * the photograph inside eases back from a slight push as the frame settles.
 */
.viora-hero.is-intro-open .viora-hero__intro-frame {
	width: 100%;
	height: 100%;
	transition:
		width var(--viora-hero-intro-open-ms) cubic-bezier(0.76, 0, 0.24, 1),
		height var(--viora-hero-intro-open-ms) cubic-bezier(0.76, 0, 0.24, 1) calc(var(--viora-hero-intro-open-ms) * 0.1);
}

/* Stage 4 — the plate hands over to the slider underneath it. */
.viora-hero.is-intro-done .viora-hero__intro {
	opacity: 0;
	visibility: hidden;
	transition:
		opacity calc(var(--viora-hero-intro-reveal-ms) * 0.6) var(--viora-motion-easing),
		visibility 0s linear calc(var(--viora-hero-intro-reveal-ms) * 0.6);
}

/*
 * The furniture waits. Held back only while `is-intro` is on, so with the
 * animation switched off — or the script gone — none of this applies.
 */
.viora-hero.is-intro .viora-hero__captions,
.viora-hero.is-intro .viora-hero__footer {
	opacity: 0;
}

.viora-hero.is-intro-reveal .viora-hero__captions,
.viora-hero.is-intro-reveal .viora-hero__footer {
	opacity: 1;
	transition: opacity var(--viora-hero-intro-reveal-ms) var(--viora-motion-easing);
}

.viora-hero.is-intro-reveal .viora-hero__footer {
	transition-delay: calc(var(--viora-hero-intro-reveal-ms) * 0.2);
}

/*
 * The header is a separate template, so the flag lives on the document. The
 * script sets it and always clears it, including on its fail-safe.
 */
html.viora-hero-intro .viora-site-header {
	opacity: 0;
}

html.viora-hero-intro-reveal .viora-site-header {
	opacity: 1;
	transition: opacity 0.9s var(--viora-motion-easing) 0.1s;
}

/* Held still while the opening plays. */
html.viora-hero-intro-lock,
html.viora-hero-intro-lock body {
	overflow: hidden;
	overscroll-behavior: none;
}

@media (prefers-reduced-motion: reduce) {
	.viora-hero__intro {
		display: none;
	}
}

/* -------------------------------------------------- *
 * Titles
 * -------------------------------------------------- */

.viora-hero__captions {
	position: absolute;
	left: var(--viora-hero-edge);
	right: var(--viora-hero-edge);
	top: var(--viora-hero-title-y);
	display: grid;
	justify-items: center;
	transform: translateY(-50%);
	pointer-events: none;
}

.viora-hero__title {
	grid-area: 1 / 1;
	max-width: 100%;
	margin: 0;
	color: #ffffff;
	font-family: var(--viora-font-display);
	font-size: var(--viora-size-display);
	font-weight: 400;
	line-height: 1;
	letter-spacing: -0.05em;
	text-align: center;
	white-space: nowrap;
	opacity: 0;
	will-change: transform, opacity;
}

.viora-hero__title[data-index="0"] {
	opacity: 1;
}

.viora-hero.is-ready .viora-hero__title {
	opacity: 0;
}

.viora-hero__title-link {
	color: inherit;
	text-decoration: none;
	pointer-events: auto;
}

.viora-hero__title-link:hover {
	color: inherit;
	opacity: 0.85;
}

/* -------------------------------------------------- *
 * Footer: pagination + meta bar
 * -------------------------------------------------- */

.viora-hero__footer {
	position: absolute;
	right: var(--viora-hero-edge);
	bottom: var(--viora-hero-footer-bottom);
	left: var(--viora-hero-edge);
	z-index: var(--viora-z-base);
}

/*
 * The gap is what makes the rules read as one track per slide rather than as a
 * single line running the width of the page — without it the segments butt
 * together and the progress looks like it belongs to the whole row.
 */
.viora-hero__pagination {
	display: grid;
	grid-auto-flow: column;
	grid-auto-columns: 1fr;
	column-gap: var(--viora-hero-page-gap);
}

.viora-hero__page {
	display: block;
	padding: 0;
	border: 0;
	background: none;
	color: var(--viora-hero-number-color);
	text-align: left;
	cursor: pointer;
	appearance: none;
}

.viora-hero__page-number {
	display: block;
	font-family: var(--viora-font-display);
	font-size: clamp(1.5rem, 2.77vw, 2.5rem);
	font-weight: 400;
	line-height: 1;
	opacity: var(--viora-hero-number-idle);
	transition: opacity var(--viora-motion-base) var(--viora-motion-easing);
}

.viora-hero__page.is-active .viora-hero__page-number,
.viora-hero__page:hover .viora-hero__page-number {
	opacity: 1;
}

.viora-hero__page-line {
	position: relative;
	display: block;
	height: 1px;
	margin-top: clamp(1.25rem, 2.56vw, 2.3rem);
	background-color: var(--viora-hero-line);
	overflow: hidden;
}

/*
 * The progress. Scaled from the left by the script every frame — not
 * transitioned, because the value it is given is already continuous and a
 * transition would only lag behind it.
 */
.viora-hero__page-fill {
	position: absolute;
	inset: 0;
	background-color: var(--viora-hero-line-active);
	transform: scaleX(0);
	transform-origin: left center;
	will-change: transform;
}

.viora-hero__meta-bar {
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	align-items: center;
	gap: var(--viora-space-sm);
	margin-top: clamp(1.25rem, 2.4vw, 2.2rem);
	color: var(--viora-hero-meta-color);
	font-family: var(--viora-font-body);
	font-size: var(--viora-size-label);
	font-weight: var(--viora-weight-semibold);
	line-height: 1;
}

.viora-hero__meta-item {
	display: flex;
	align-items: center;
	min-width: 0;
}

.viora-hero__meta-item--center {
	justify-content: center;
}

.viora-hero__meta-item--end {
	justify-content: flex-end;
	text-align: right;
}

/*
 * The hint rolls rather than sits still.
 *
 * A one-line window with the label repeated underneath by a pseudo-element:
 * every few seconds the pair travels up exactly one line and lands on the copy,
 * so the loop has no seam and the whole thing is one element with no script.
 */
.viora-hero__hint {
	display: inline-block;
	height: 1.6em;
	overflow: hidden;
	line-height: 1.6;
	vertical-align: top;
}

.viora-hero__hint-roll {
	display: block;
	animation: viora-hero-hint-roll 3s cubic-bezier(0.76, 0, 0.24, 1) infinite;
}

.viora-hero__hint-roll::after {
	content: attr(data-text);
	display: block;
}

/* Rests for two thirds of the cycle, then travels the line in the last third. */
@keyframes viora-hero-hint-roll {
	0%,
	66% {
		transform: translateY(0);
	}

	100% {
		transform: translateY(-50%);
	}
}

/* The label becomes a link only when one is set; it keeps the meta bar's type
   and just gains a target and a hover. */
.viora-hero__meta-link {
	color: inherit;
	text-decoration: none;
	transition: opacity var(--viora-motion-base) var(--viora-motion-easing);
}

.viora-hero__meta-link:hover {
	opacity: 0.65;
}

.viora-hero__meta-link:focus-visible {
	outline: 1px solid currentColor;
	outline-offset: 5px;
}

.viora-hero__nav {
	display: flex;
	align-items: center;
	gap: clamp(0.75rem, 1.6vw, 1.4375rem);
}

.viora-hero__arrow {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: var(--viora-hero-arrow-size);
	height: var(--viora-hero-arrow-size);
	padding: 0;
	border: 0;
	background: none;
	color: inherit;
	cursor: pointer;
	appearance: none;
	transition: transform var(--viora-motion-base) var(--viora-motion-easing), opacity var(--viora-motion-fast) var(--viora-motion-easing);
}

.viora-hero__arrow:hover {
	opacity: 0.7;
}

.viora-hero__arrow--prev:hover {
	transform: translateX(-3px);
}

.viora-hero__arrow--next:hover {
	transform: translateX(3px);
}

.viora-hero__arrow svg,
.viora-hero__arrow-icon {
	display: block;
	width: 100%;
	height: 100%;
}

.viora-hero__arrow i {
	font-size: calc(var(--viora-hero-arrow-size) * 0.8);
	line-height: 1;
}

.viora-hero__page:focus-visible,
.viora-hero__arrow:focus-visible,
.viora-hero__title-link:focus-visible {
	outline: 1px solid currentColor;
	outline-offset: 6px;
}

/* -------------------------------------------------- *
 * Responsive
 * -------------------------------------------------- */

/* Tablet — matches Elementor's default tablet breakpoint. */
@media (max-width: 1024px) {
	.viora-hero__title {
		white-space: normal;
		text-wrap: balance;
		overflow-wrap: break-word;
	}
}

/* Mobile — matches Elementor's default mobile breakpoint. */
@media (max-width: 767px) {
	.viora-hero {
		--viora-hero-title-y: 46%;
	}

	.viora-hero__pagination {
		grid-auto-columns: minmax(0, 1fr);
	}

	.viora-hero__page-line {
		margin-top: clamp(0.75rem, 4vw, 1.5rem);
	}

	/*
	 * Two ends rather than a centred stack: the hint on the left, the slider
	 * controls on the right. Stacking all three centred cost three lines of
	 * height on the part of the screen with least of it to spare.
	 */
	.viora-hero__meta-bar {
		grid-template-columns: auto auto;
		justify-content: space-between;
		align-items: center;
		gap: var(--viora-space-sm);
		text-align: left;
	}

	.viora-hero__meta-item--start {
		justify-content: flex-start;
		text-align: left;
	}

	.viora-hero__meta-item--center {
		justify-content: flex-end;
		text-align: right;
	}

	/* The link is a duplicate of the portfolio section further down the page,
	   and there is no room for a third item across a phone. */
	.viora-hero__meta-item--end {
		display: none;
	}

	/* The bar is the tallest part on a phone — keep it off the safe area. */
	.viora-hero__footer {
		padding-bottom: env(safe-area-inset-bottom, 0);
	}
}

@media (prefers-reduced-motion: reduce) {
	.viora-hero__page-number,
	.viora-hero__arrow {
		transition: none;
	}

	/* Still one line tall, just no longer travelling. */
	.viora-hero__hint-roll {
		animation: none;
	}
}
