/**
 * Wedwebs Envelope Opener
 *
 * Layer order inside .ww-envelope__env (a preserve-3d box), back to front:
 * ground shadow (z -2px), back/liner (0), card (1px), pocket (2px),
 * flap (3px, folds back to -1px), seal (4px). z-index does nothing inside a
 * 3D context, so every layer is placed with translateZ.
 *
 * Stages are classes on .ww-envelope added by the script:
 * is-seal > is-flap > is-card > is-lift > is-reveal > is-revealed.
 * Travel distances (--ww-rise, --ww-lift-*, --ww-reveal-*) are measured in
 * px by the script so the card always clears the pocket before it moves
 * forward, whatever the card and envelope proportions.
 */

.ww-envelope {
	/* palette (overridden by the Color Preset control) */
	--ww-paper: #2f4a3e;
	--ww-paper-shade: #263d33;
	--ww-paper-img: none;
	/* How strongly the folds are shaded. Colour alone separates the panels
	   until a texture image covers it, so the shading has to carry the folds. */
	--ww-fold: 0.16;
	--ww-liner: #1f3129;
	--ww-liner-line: #b89a5e;
	--ww-liner-img: none;
	--ww-liner-tile: 160px;
	/* How the liner image sits on the sheet. The vector patterns below repeat
	   at their own scale, so these defaults suit them; the Image liner sets
	   them from the Image Fit control. */
	--ww-liner-size: auto;
	--ww-liner-pos: 0 0;
	--ww-liner-repeat: repeat;
	--ww-seal: #8a2a2e;
	--ww-seal-hi: #b24a4c;
	--ww-frame: rgba(43, 42, 38, 0.28);
	--ww-frame-inset: 12px;
	--ww-ground-shadow: rgba(30, 28, 22, 0.38);
	--ww-hint-dot: var(--ww-seal);
	--ww-hint-gap: 28px;
	--ww-hint-dot-size: 8px;
	--ww-hint-dot-gap: 8px;
	--ww-hint-pad: 16px;
	/* Written as a set by the Position control, for the reason the Size
	   Mode control gives: a device that left one of them alone would take
	   it from the device above and end up with a half flipped hint. */
	--ww-hint-dir: column;
	--ww-hint-top: auto;
	--ww-hint-bottom: calc(var(--ww-hint-gap) + env(safe-area-inset-bottom, 0px));
	--ww-hint-max: none;
	--ww-hint-nudge: 0px;

	/*
	 * geometry
	 *
	 * These match the panel's defaults, so reading this file tells the same
	 * story the controls do. Elementor's generated CSS overrides all of them,
	 * and the size mode writes the three env values below as a set, which is
	 * why they stay at the Keep proportions geometry here.
	 */
	--ww-ratio: 1.7;
	--ww-env-w: 100vw;
	--ww-env-max: min(100%, 400px);
	--ww-env-h: auto;
	--ww-seal-offset: 0%;
	--ww-split: 0;
	/* The pocket follows the flap: side seams meet where the flap ends, and
	   the bottom panel reaches above that line so the two overlap. */
	--ww-pocket-y: var(--ww-flap-depth);
	--ww-pocket-apex: calc(var(--ww-flap-depth) - 14%);
	--ww-card-w: 100%;
	--ww-card-h: 100%;
	--ww-card-r: 2px;
	--ww-card-arch: 28%;
	--ww-tilt: 0deg;
	--ww-forward: 0px;
	--ww-flap-depth: 57%;
	/* How much of each fold is given to the curve at its point. A share, not a
	   length, so the rounding keeps its proportion at every size. */
	--ww-point: 0.2;
	/* Where the unfold card starts before it settles. Never above 1. */
	--ww-unfold-rest: 0.94;

	/* measured by the script */
	--ww-rise: 0px;
	--ww-lift-card: 0px;
	--ww-lift-env: 0px;
	--ww-reveal-card: 0px;
	--ww-reveal-env: 0px;
	--ww-zoom: 1;
	--ww-design-w: 360px;
	--ww-seal-w: 21%;
	--ww-panel-travel: 120%;
	--ww-content-h: 100%;

	/* stage durations in ms, set by the script */
	--ww-d-seal: 380ms;
	--ww-d-flap: 760ms;
	--ww-d-card: 850ms;
	--ww-d-lift: 600ms;
	--ww-d-reveal: 950ms;

	--ww-ease: cubic-bezier(0.62, 0.04, 0.24, 1);
	--ww-noise: none;
	--ww-card-noise: none;

	--ww-liner-pattern: none;

	position: relative;
}

.ww-envelope-grain-yes .ww-envelope,
.ww-envelope-card-grain-yes .ww-envelope {
	--ww-grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 .11 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
}
.ww-envelope-grain-yes .ww-envelope { --ww-noise: var(--ww-grain); }
.ww-envelope-card-grain-yes .ww-envelope { --ww-card-noise: var(--ww-grain); }

/* liner presets */
.ww-envelope--liner-diamond {
	--ww-liner-pattern:
		repeating-linear-gradient(45deg, transparent 0 9px, color-mix(in srgb, var(--ww-liner-line) 55%, transparent) 9px 10px),
		repeating-linear-gradient(-45deg, transparent 0 9px, color-mix(in srgb, var(--ww-liner-line) 55%, transparent) 9px 10px);
}
.ww-envelope--liner-stripes {
	--ww-liner-pattern: repeating-linear-gradient(90deg, transparent 0 7px, color-mix(in srgb, var(--ww-liner-line) 50%, transparent) 7px 8px);
}
.ww-envelope--liner-image {
	--ww-liner-pattern: var(--ww-liner-img);
	--ww-liner-pos: center;
}

/*
 * Image Fit, the same three choices a background image gets anywhere else.
 * Written against both liner classes so the vector patterns above keep their
 * own repeat whatever the fit control was last left at.
 */
.ww-envelope--liner-image.ww-envelope--linerfit-cover {
	--ww-liner-size: cover;
	--ww-liner-repeat: no-repeat;
}

.ww-envelope--liner-image.ww-envelope--linerfit-contain {
	--ww-liner-size: contain;
	--ww-liner-repeat: no-repeat;
}

.ww-envelope--liner-image.ww-envelope--linerfit-tile {
	--ww-liner-size: var(--ww-liner-tile);
	--ww-liner-repeat: repeat;
}

/* ---------- scene ---------- */

.ww-envelope__scene {
	position: relative;
	display: flex;
	/* The scene holds the envelope and the hint, so reversing the column is
	   all it takes to move the hint above the envelope. */
	flex-direction: var(--ww-hint-dir, column);
	align-items: center;
	gap: var(--ww-hint-gap);
	width: var(--ww-stage-w, auto);
	min-height: var(--ww-stage-h, 0px);
	justify-content: center;
	/* Clips the full screen paper until the script has switched Fill screen
	   to its own layout (see "fill screen" below). */
	overflow: var(--ww-stage-clip, visible);
	perspective: 1100px;
	perspective-origin: 50% 30%;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
	outline: none;
}

.ww-envelope.is-revealed .ww-envelope__scene {
	cursor: auto;
}

.ww-envelope__env {
	position: relative;
	width: var(--ww-env-w);
	max-width: var(--ww-env-max);
	height: var(--ww-env-h);
	aspect-ratio: var(--ww-ratio);
	transform-style: preserve-3d;
	-webkit-transform-style: preserve-3d;
	transform: rotateX(var(--ww-tilt));
	transition: transform var(--ww-d-reveal) var(--ww-ease);
}

.ww-envelope__scene:focus-visible .ww-envelope__env {
	outline: 2px dashed currentColor;
	outline-offset: 10px;
}

.ww-envelope__env > * {
	position: absolute;
	inset: 0;
}

/*
 * A spare copy of the layer order, in paint order terms.
 *
 * While the browser keeps the 3D context these do nothing: translateZ decides
 * what is in front, as the note at the top of this file says. They are here
 * for the engines that quietly flatten the context instead, which a filter or
 * a texture image on one of the layers is enough to trigger. A flattened
 * context falls back to document order, and document order puts the flap over
 * the card, so the card would dive under the opened flap on its way out.
 *
 * The opened flap is the one layer whose place changes: it folds back behind
 * the envelope, so it drops behind the card with it. It changes place halfway
 * through the fold, not at the start of it, which is what is-flap-over marks.
 * is-flap goes on while the flap is still standing over the card, so a flap
 * that gave up its place on that class would be swallowed by the card for the
 * rest of the rotation and reappear only once it cleared the envelope's top
 * edge. The faces trade places on the same beat, for the same reason.
 */
.ww-envelope__shadow { z-index: -2; }
.ww-envelope__back { z-index: 0; }
.ww-envelope__card { z-index: 1; }
.ww-envelope__pocket { z-index: 2; }
.ww-envelope__flap { z-index: 3; }
.ww-envelope__seal { z-index: 4; }

.ww-envelope.is-flap-over .ww-envelope__flap { z-index: -1; }

.ww-envelope__face--front { z-index: 1; }
.ww-envelope__face--back { z-index: 0; }
.ww-envelope.is-flap-over .ww-envelope__face--front { z-index: 0; }
.ww-envelope.is-flap-over .ww-envelope__face--back { z-index: 1; }

.ww-envelope__shadow {
	inset: auto 4% -9% 4%;
	height: 22%;
	background: radial-gradient(50% 50% at 50% 50%, var(--ww-ground-shadow), transparent 72%);
	transform: translateY(0) translateZ(-2px);
	filter: blur(6px);
	transition:
		transform var(--ww-d-lift) var(--ww-ease),
		opacity var(--ww-d-lift) var(--ww-ease);
	pointer-events: none;
}

/*
 * The liner is one sheet. A real envelope lines the back panel and carries the
 * lining on across the inside of the flap, so both surfaces are painted from
 * the same background here. They can be: each is a full envelope sized box
 * (the flap and its faces are inset: 0 like everything else, and only a
 * clip-path decides what shows), so one `cover` lands the same crop on both
 * and the halves meet instead of each cropping the image for itself.
 */
.ww-envelope__back,
.ww-envelope__face--back i {
	background: var(--ww-noise), var(--ww-liner-pattern), var(--ww-liner);
	background-size: auto, var(--ww-liner-size), auto;
	background-position: 0 0, var(--ww-liner-pos), 0 0;
	background-repeat: repeat, var(--ww-liner-repeat), repeat;
}

.ww-envelope__back {
	border-radius: 3px;
	box-shadow: inset 0 18px 30px rgba(0, 0, 0, 0.35);
}

/* ---------- card ---------- */

.ww-envelope__card {
	inset: auto;
	left: calc((100% - var(--ww-card-w)) / 2);
	top: calc((100% - var(--ww-card-h)) / 2);
	width: var(--ww-card-w);
	height: var(--ww-card-h);
	overflow: hidden;
	background-color: #fbf8f1;
	border-radius: var(--ww-card-r);
	box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18), 0 10px 30px rgba(0, 0, 0, 0.12);
	transform: translateY(0) translateZ(1px);
	transition: transform var(--ww-d-card) var(--ww-ease);
}

.ww-envelope__card--arch {
	border-radius:
		50% 50% var(--ww-card-r) var(--ww-card-r) /
		var(--ww-card-arch) var(--ww-card-arch) var(--ww-card-r) var(--ww-card-r);
}

.ww-envelope__card::after {
	content: "";
	position: absolute;
	inset: 0;
	background: var(--ww-card-noise);
	pointer-events: none;
	border-radius: inherit;
}

.ww-envelope__card.has-frame::before {
	content: "";
	position: absolute;
	inset: var(--ww-frame-inset);
	border: 1px solid var(--ww-frame);
	border-radius: inherit;
	pointer-events: none;
	z-index: 1;
}

/*
 * Two ways to size the card content. Responsive (the default) lets it fill
 * the card at the card's real width, like any other section on the page.
 * Scaled lays it out at a fixed design width and zooms it to fit, for a card
 * that must look identical on every screen.
 */
.ww-envelope--fit-natural .ww-envelope__content {
	width: 100%;
	height: 100%;
	zoom: 1;
	transform: none;
}

.ww-envelope__content {
	position: absolute;
	left: 0;
	top: 0;
	width: var(--ww-design-w);
	height: var(--ww-content-h);
	zoom: var(--ww-zoom);
	display: flex;
	flex-direction: column;
	/*
	 * Safe: content taller than the card starts at the top instead of
	 * spilling past both edges, so what is cut off is the end of it, not the
	 * heading.
	 */
	justify-content: center;
	justify-content: safe center;
	pointer-events: none;
}

.ww-envelope.is-no-zoom .ww-envelope__content {
	zoom: 1;
	transform: scale(var(--ww-zoom));
	transform-origin: 0 0;
}

.ww-envelope.is-revealed .ww-envelope__content {
	pointer-events: auto;
}

.ww-envelope__content > * {
	width: 100%;
}

/*
 * The card's height is the single source of truth: the content grows to fill
 * it, so nothing needs a height of its own and the card background never
 * shows through above or below the content.
 *
 * Not in Fill screen, where it is the other way round: the card takes the
 * height of the content, so the content keeps the height it was designed with.
 * It still grows into any room the card has left (see "fill screen" below),
 * but by flex-grow, which leaves its own min-height alone.
 */
.ww-envelope-stretch-yes .ww-envelope:not(.is-fill) .ww-envelope__content > * {
	flex: 1 0 auto;
	min-height: 100%;
}

.ww-envelope-stretch-yes .ww-envelope.is-fill .ww-envelope__content > * {
	flex: 1 0 auto;
}

.ww-envelope__placeholder {
	padding: 24px;
	text-align: center;
	font: 13px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;
	color: #6b665c;
}

/* ---------- pocket ---------- */

.ww-envelope__pocket {
	--ww-layer-z: 2px;
	transform: translateY(0) translateZ(2px);
	filter: drop-shadow(0 -1px 2px rgba(0, 0, 0, calc(var(--ww-fold) * 1.4)));
}

.ww-envelope__pocket i,
.ww-envelope__face i {
	position: absolute;
	inset: 0;
}

.ww-envelope__pocket-left {
	clip-path: polygon(0 0, 54% var(--ww-pocket-y), 0 100%);
	background:
		var(--ww-noise),
		linear-gradient(90deg, rgba(0, 0, 0, 0) 55%, rgba(0, 0, 0, calc(var(--ww-fold) * 0.75))),
		var(--ww-paper-img) center / cover,
		var(--ww-paper);
}

.ww-envelope__pocket-right {
	clip-path: polygon(100% 0, 46% var(--ww-pocket-y), 100% 100%);
	background:
		var(--ww-noise),
		linear-gradient(270deg, rgba(0, 0, 0, 0) 55%, rgba(0, 0, 0, calc(var(--ww-fold) * 0.75))),
		var(--ww-paper-img) center / cover,
		var(--ww-paper);
}

.ww-envelope__pocket-bottom {
	clip-path: polygon(0 100%, 50% var(--ww-pocket-apex), 100% 100%);
	/*
	 * Flat tone, not a gradient. A gradient this wide reads as haze over a
	 * light texture instead of as a fold; what makes the flap above read as
	 * paper is its edge, so the bottom panel gets the same treatment: an even
	 * tone barely apart from the sides, and a real shadow along its fold.
	 */
	background:
		var(--ww-noise),
		linear-gradient(rgba(0, 0, 0, calc(var(--ww-fold) * 0.35)) 0 0),
		var(--ww-paper-img) center / cover,
		var(--ww-paper-shade);
	filter:
		drop-shadow(0 -1px 0 rgba(0, 0, 0, calc(var(--ww-fold) * 1.6)))
		drop-shadow(0 -3px 7px rgba(0, 0, 0, calc(var(--ww-fold) * 1.1)));
}

/* ---------- flap ---------- */

.ww-envelope__flap {
	transform-origin: 50% 0;
	transform-style: preserve-3d;
	-webkit-transform-style: preserve-3d;
	transform: translateY(0) translateZ(3px) rotateX(0deg);
	transition: transform var(--ww-d-flap) var(--ww-ease);
}

/*
 * Each face is half a millimetre of paper in front of its own side of the
 * flap, so whichever side is turned towards the viewer is also the nearer of
 * the two. backface-visibility alone would do it, but a face carrying a filter
 * or a texture image is rendered as a group on some engines, and a grouped
 * face can lose its backface test and print the front of the flap, mirrored,
 * on the inside. The offset settles the order without that test.
 */
.ww-envelope__face {
	position: absolute;
	inset: 0;
	backface-visibility: hidden;
	-webkit-backface-visibility: hidden;
	transform: translateZ(0.5px);
}

.ww-envelope__face--front {
	filter: drop-shadow(0 2px 3px rgba(0, 0, 0, calc(var(--ww-fold) * 2)));
}

.ww-envelope__face--front i {
	clip-path: var(--ww-flap-clip-front, none);
	-webkit-mask: var(--ww-flap-mask-front, none) 0 0 / 100% 100% no-repeat;
	mask: var(--ww-flap-mask-front, none) 0 0 / 100% 100% no-repeat;
	background:
		var(--ww-noise),
		linear-gradient(180deg, rgba(255, 255, 255, calc(var(--ww-fold) * 0.5)), rgba(0, 0, 0, 0) 70%),
		var(--ww-paper-img) center / cover,
		var(--ww-paper);
}

.ww-envelope__face--back {
	transform: rotateX(180deg) translateZ(0.5px);
}

/* Background: the shared liner rule above paints this face. */
.ww-envelope__face--back i {
	clip-path: var(--ww-flap-clip-back, none);
	-webkit-mask: var(--ww-flap-mask-back, none) 0 0 / 100% 100% no-repeat;
	mask: var(--ww-flap-mask-back, none) 0 0 / 100% 100% no-repeat;
}

/* ---------- rounded points ---------- */

/*
 * A polygon() corner is always a cut, and real paper never folds to a knife
 * point. shape() draws the same outlines with a curve at each point, and it
 * accepts percentages, so the rounding stays proportional the way the rest of
 * the geometry does. Every point is trimmed by --ww-point of its own two
 * edges, which puts the cut on the edge itself and keeps the curve tangent.
 *
 * Browsers without shape() keep the polygons above, sharp as before.
 */
@supports (clip-path: shape(from 0 0, line to 100% 0)) {

	.ww-envelope--flap-pointed .ww-envelope__face--front i {
		clip-path: var(
			--ww-flap-shape-front,
			shape(
				from 0 0,
				line to 100% 0,
				line to
					calc(50% + var(--ww-point) * 50%)
					calc(var(--ww-flap-depth) - var(--ww-point) * var(--ww-flap-depth)),
				curve to
					calc(50% - var(--ww-point) * 50%)
					calc(var(--ww-flap-depth) - var(--ww-point) * var(--ww-flap-depth))
					with 50% var(--ww-flap-depth),
				close
			)
		);
	}

	.ww-envelope--flap-pointed .ww-envelope__face--back i {
		clip-path: var(
			--ww-flap-shape-back,
			shape(
				from 0 100%,
				line to 100% 100%,
				line to
					calc(50% + var(--ww-point) * 50%)
					calc(100% - var(--ww-flap-depth) + var(--ww-point) * var(--ww-flap-depth)),
				curve to
					calc(50% - var(--ww-point) * 50%)
					calc(100% - var(--ww-flap-depth) + var(--ww-point) * var(--ww-flap-depth))
					with 50% calc(100% - var(--ww-flap-depth)),
				close
			)
		);
	}

	.ww-envelope__pocket-left {
		clip-path: shape(
			from 0 0,
			line to
				calc(54% - var(--ww-point) * 54%)
				calc(var(--ww-pocket-y) - var(--ww-point) * var(--ww-pocket-y)),
			curve to
				calc(54% - var(--ww-point) * 54%)
				calc(var(--ww-pocket-y) + var(--ww-point) * (100% - var(--ww-pocket-y)))
				with 54% var(--ww-pocket-y),
			line to 0 100%,
			close
		);
	}

	.ww-envelope__pocket-right {
		clip-path: shape(
			from 100% 0,
			line to
				calc(46% + var(--ww-point) * 54%)
				calc(var(--ww-pocket-y) - var(--ww-point) * var(--ww-pocket-y)),
			curve to
				calc(46% + var(--ww-point) * 54%)
				calc(var(--ww-pocket-y) + var(--ww-point) * (100% - var(--ww-pocket-y)))
				with 46% var(--ww-pocket-y),
			line to 100% 100%,
			close
		);
	}

	.ww-envelope__pocket-bottom {
		clip-path: shape(
			from 0 100%,
			line to
				calc(50% - var(--ww-point) * 50%)
				calc(var(--ww-pocket-apex) + var(--ww-point) * (100% - var(--ww-pocket-apex))),
			curve to
				calc(50% + var(--ww-point) * 50%)
				calc(var(--ww-pocket-apex) + var(--ww-point) * (100% - var(--ww-pocket-apex)))
				with 50% var(--ww-pocket-apex),
			line to 100% 100%,
			close
		);
	}
}

/* ---------- seal ---------- */

.ww-envelope__seal {
	inset: auto;
	left: 50%;
	top: calc(var(--ww-flap-depth) + var(--ww-seal-offset));
	width: var(--ww-seal-w);
	aspect-ratio: 1;
	transform: translate(-50%, -50%) translateZ(4px);
	display: grid;
	place-items: center;
	container-type: inline-size;
	transition:
		transform var(--ww-d-seal) var(--ww-ease),
		opacity calc(var(--ww-d-seal) * 0.85) ease-out;
}

.ww-envelope__seal--wax {
	border-radius: 47% 53% 50% 50% / 52% 46% 54% 48%;
	background:
		radial-gradient(60% 60% at 38% 32%, var(--ww-seal-hi), transparent 62%),
		radial-gradient(circle at 50% 50%, var(--ww-seal) 58%, color-mix(in srgb, var(--ww-seal) 70%, #000) 72%, var(--ww-seal) 76%);
	box-shadow: 0 3px 6px rgba(0, 0, 0, 0.35), inset 0 -3px 5px rgba(0, 0, 0, 0.3);
}

.ww-envelope__seal-mark {
	width: 70%;
	aspect-ratio: 1;
	border-radius: 50%;
	display: grid;
	place-items: center;
	box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.45), 0 1px 0 rgba(255, 255, 255, 0.18);
	font-family: "Bodoni Moda", "Didot", "Bodoni 72", Georgia, serif;
	font-style: italic;
	font-size: 26cqi;
	line-height: 1;
	white-space: nowrap;
	color: color-mix(in srgb, var(--ww-seal) 60%, #000);
	text-shadow: 0 1px 0 rgba(255, 255, 255, 0.22);
}

.ww-envelope__seal--image img {
	width: 100%;
	height: 100%;
	object-fit: contain;
	filter: drop-shadow(0 3px 4px rgba(0, 0, 0, 0.35));
}

/* ---------- hint ---------- */

.ww-envelope__hint {
	margin: 0;
	position: var(--ww-hint-abs, static);
	left: 0;
	right: 0;
	top: var(--ww-hint-top, auto);
	bottom: var(--ww-hint-bottom, calc(var(--ww-hint-gap) + env(safe-area-inset-bottom, 0px)));
	padding: 0 var(--ww-hint-pad);
	font-size: 13px;
	letter-spacing: 0.04em;
	text-align: center;
	color: #6b665c;
	transition: opacity 300ms ease;
}

/*
 * The words get their own box so the dot can sit outside it. A dot that rides
 * in the line pushes the words along by its own width, and a line centred as
 * a whole then reads as off-centre by half that much, which is the illusion
 * the Dot Placement control is really choosing about. Every placement below
 * takes the dot out of flow and, where it needs the room, reserves it with
 * padding on this box instead.
 */
.ww-envelope__hint-text {
	position: relative;
	display: inline-block;
	max-width: var(--ww-hint-max, none);
	margin-left: var(--ww-hint-nudge, 0px);
	margin-right: calc(var(--ww-hint-nudge, 0px) * -1);
}

/*
 * The pulse animates transform, so the dot is placed with offsets and margins
 * only. top is set against the first line rather than the box, so a hint that
 * wraps keeps the dot beside its opening words.
 */
.ww-envelope__hint-text::before {
	content: "";
	position: absolute;
	/* Half a line down, so the dot meets the middle of the first line.
	   The em value is the guess a browser without lh falls back to. */
	top: 0.75em;
	top: calc(0.5lh);
	right: 100%;
	width: var(--ww-hint-dot-size);
	height: var(--ww-hint-dot-size);
	margin: calc(var(--ww-hint-dot-size) / -2) var(--ww-hint-dot-gap) 0 0;
	border-radius: 50%;
	background: var(--ww-hint-dot);
	animation: ww-envelope-pulse 1.6s ease-in-out infinite;
}

/* Dot inside the line: the old behaviour, kept for anyone who wants it. */
.ww-envelope--hint-dot-inline .ww-envelope__hint-text {
	padding-left: calc(var(--ww-hint-dot-size) + var(--ww-hint-dot-gap));
}

.ww-envelope--hint-dot-inline .ww-envelope__hint-text::before {
	right: auto;
	left: 0;
	margin-right: 0;
}

.ww-envelope--hint-dot-after .ww-envelope__hint-text::before {
	right: auto;
	left: 100%;
	margin-right: 0;
	margin-left: var(--ww-hint-dot-gap);
}

.ww-envelope--hint-dot-above .ww-envelope__hint-text {
	padding-top: calc(var(--ww-hint-dot-size) + var(--ww-hint-dot-gap));
}

.ww-envelope--hint-dot-above .ww-envelope__hint-text::before {
	top: 0;
	right: auto;
	left: 50%;
	margin: 0 0 0 calc(var(--ww-hint-dot-size) / -2);
}

.ww-envelope--hint-dot-none .ww-envelope__hint-text::before,
.ww-envelope--hint-still .ww-envelope__hint-text::before {
	animation: none;
}

.ww-envelope--hint-dot-none .ww-envelope__hint-text::before {
	display: none;
}

@keyframes ww-envelope-pulse {
	50% {
		transform: scale(1.7);
		opacity: 0.35;
	}
}

/* ---------- stages ---------- */

.ww-envelope.is-seal .ww-envelope__seal {
	transform: translate(-50%, -50%) translateZ(40px) scale(1.12) rotate(-8deg);
	opacity: 0;
}

.ww-envelope.is-seal .ww-envelope__hint {
	opacity: 0;
}

.ww-envelope.is-flap .ww-envelope__flap {
	transform: translateY(0) translateZ(-1px) rotateX(180deg);
}

.ww-envelope.is-card .ww-envelope__card {
	transform: translateY(var(--ww-rise)) translateZ(1px);
}

/*
 * Pull apart: the card keeps rising while the envelope drops until the
 * card's bottom edge is above the envelope's top edge. The card stays behind
 * the pocket (1px < 2px) until then, so it never passes through the paper.
 */
.ww-envelope.is-lift .ww-envelope__card {
	transform: translateY(var(--ww-lift-card)) translateZ(1px);
	transition-duration: var(--ww-d-lift);
}

.ww-envelope.is-lift .ww-envelope__back,
.ww-envelope.is-lift .ww-envelope__pocket {
	transform: translateY(var(--ww-lift-env)) translateZ(var(--ww-layer-z, 0px));
}

.ww-envelope.is-lift .ww-envelope__flap {
	transform: translateY(var(--ww-lift-env)) translateZ(-1px) rotateX(180deg);
}

.ww-envelope.is-lift .ww-envelope__back,
.ww-envelope.is-lift .ww-envelope__pocket,
.ww-envelope.is-lift .ww-envelope__flap {
	transition:
		transform var(--ww-d-lift) var(--ww-ease),
		opacity var(--ww-d-lift) var(--ww-ease);
}

.ww-envelope.is-lift .ww-envelope__shadow {
	transform: translateY(160%) translateZ(-2px);
	opacity: 0.6;
}

/*
 * Only now does the card come forward. The envelope falls further than the
 * card settles (the script guarantees it), and both use the same easing, so
 * the gap between them only grows.
 */
.ww-envelope.is-reveal .ww-envelope__env {
	transform: rotateX(0deg);
}

/*
 * Forward is drawn as a 2D scale about the vanishing point, not translateZ.
 * On a flat card facing the viewer the two land on the same picture, but a
 * translateZ grows the card through the perspective divide, which the browser
 * cannot size ahead of time: it keeps re-rasterising the card at each new
 * scale on the way, and the text and frame visibly snap from one pixel grid
 * to the next. A scale is known end to end, so the card is rasterised once
 * and glides. The script measures --ww-forward-scale from --ww-forward and
 * --ww-forward-origin from the perspective origin.
 */
.ww-envelope.is-reveal .ww-envelope__card {
	transform: translateY(var(--ww-reveal-card)) translateZ(1px) scale(var(--ww-forward-scale, 1));
	transform-origin: var(--ww-forward-origin, 50% 50%);
	transition-duration: var(--ww-d-reveal);
}

.ww-envelope.is-reveal .ww-envelope__back,
.ww-envelope.is-reveal .ww-envelope__pocket {
	transform: translateY(var(--ww-reveal-env)) translateZ(var(--ww-layer-z, 0px));
	opacity: 0;
}

.ww-envelope.is-reveal .ww-envelope__flap {
	transform: translateY(var(--ww-reveal-env)) translateZ(-1px) rotateX(180deg);
	opacity: 0;
}

.ww-envelope.is-reveal .ww-envelope__back,
.ww-envelope.is-reveal .ww-envelope__pocket,
.ww-envelope.is-reveal .ww-envelope__flap {
	transition:
		transform var(--ww-d-reveal) var(--ww-ease),
		opacity calc(var(--ww-d-reveal) * 0.55) ease-in calc(var(--ww-d-reveal) * 0.19);
}

.ww-envelope.is-reveal .ww-envelope__shadow {
	opacity: 0;
}

.ww-envelope.is-revealed .ww-envelope__back,
.ww-envelope.is-revealed .ww-envelope__pocket,
.ww-envelope.is-revealed .ww-envelope__flap,
.ww-envelope.is-revealed .ww-envelope__seal,
.ww-envelope.is-revealed .ww-envelope__shadow,
.ww-envelope.is-revealed .ww-envelope__hint {
	visibility: hidden;
}

/* The dot is out of sight by now, but a composited animation left running is
   still work the compositor repeats forever. */
.ww-envelope.is-revealed .ww-envelope__hint-text::before {
	animation: none;
}

/* ---------- unfold style ----------
 *
 * The card is not inside the envelope here: it lies behind the paper at the
 * full envelope size, so the opening flap reveals it through the hole, and
 * the panels then fold away to clear it. Stages read as:
 * seal > flap opens onto the card > card settles > panels fold away > reveal.
 */

.ww-envelope--unfold .ww-envelope__back {
	display: none;
}

.ww-envelope--unfold .ww-envelope__card {
	left: 0;
	top: 0;
	width: 100%;
	height: 100%;
	/*
	 * The card settles out of a slight zoom. It has to zoom *in* to its place,
	 * never out of a larger one: the card is exactly envelope sized here, so a
	 * resting scale above 1 hangs outside the paper on every side, which the
	 * closed envelope shows as a rim wherever the envelope does not fill the
	 * screen.
	 */
	transform: translateZ(0) scale(var(--ww-unfold-rest));
	box-shadow: none;
	transition:
		transform var(--ww-d-card) var(--ww-ease),
		opacity var(--ww-d-card) var(--ww-ease);
}

.ww-envelope--unfold .ww-envelope__pocket i,
.ww-envelope--unfold .ww-envelope__flap {
	transition:
		transform var(--ww-d-lift) var(--ww-ease),
		opacity var(--ww-d-lift) var(--ww-ease);
}

/* The card is already in place; it only settles out of its slight zoom. */
.ww-envelope--unfold.is-card .ww-envelope__card {
	transform: translateZ(0) scale(1);
}

/* The card never travels in this style, so hold it against the draw rules. */
.ww-envelope--unfold.is-lift .ww-envelope__card {
	transform: translateZ(0) scale(1);
}

/* Panels fold away: sides outward, bottom down, flap further up and out. */
.ww-envelope--unfold.is-lift .ww-envelope__pocket-left {
	transform: translateX(calc(var(--ww-panel-travel) * -1)) rotate(-4deg);
}

.ww-envelope--unfold.is-lift .ww-envelope__pocket-right {
	transform: translateX(var(--ww-panel-travel)) rotate(4deg);
}

.ww-envelope--unfold.is-lift .ww-envelope__pocket-bottom {
	transform: translateY(var(--ww-panel-travel));
}

.ww-envelope--unfold.is-lift .ww-envelope__pocket {
	transform: translateZ(2px);
}

.ww-envelope--unfold.is-lift .ww-envelope__flap {
	transform: translateY(calc(var(--ww-panel-travel) * -1)) translateZ(-1px) rotateX(180deg);
}

.ww-envelope--unfold.is-lift .ww-envelope__shadow {
	opacity: 0;
	transform: translateY(0) translateZ(-2px);
}

/* Nothing is drawn forward at the end: the card is the page. */
.ww-envelope--unfold.is-reveal .ww-envelope__card {
	transform: translateZ(0) scale(1);
	transition-duration: var(--ww-d-reveal);
}

.ww-envelope--unfold.is-reveal .ww-envelope__pocket,
.ww-envelope--unfold.is-reveal .ww-envelope__flap {
	opacity: 0;
	transition:
		transform var(--ww-d-reveal) var(--ww-ease),
		opacity calc(var(--ww-d-reveal) * 0.6) ease-in;
}

.ww-envelope--unfold.is-reveal .ww-envelope__env {
	transform: rotateX(0deg);
}

/* ---------- holding the content ----------
 *
 * Elementor starts an entrance animation when its element comes into view,
 * and behind a cover every element is in view from the first frame, so its
 * animations play and their delays expire before the guest opens anything.
 * Pausing the animation is not enough, because the delay is a setTimeout, not
 * animation-delay.
 *
 * Taking the content out of the layout solves both at once: an element that
 * is display: none never comes into view, so Elementor starts nothing at all
 * for it. When it is put back, its observers fire for the first time and
 * every delay counts from that moment. The class is printed server side, so
 * this holds from the first painted frame.
 */

.ww-envelope--content-hidden .ww-envelope__content,
.ww-envelope-content-hidden {
	display: none !important;
}

/* ---------- slot source ----------
 *
 * A container marked as a card slot is hidden until the script moves it into
 * the envelope, so it never flashes in its original place. In the editor it
 * stays visible and editable where the author put it.
 */

body:not(.elementor-editor-active) .ww-envelope-slot {
	visibility: hidden;
}

/* ---------- cover overlays ----------
 *
 * Anything the author floats above the envelope inside the cover (a guest
 * name, the couple's names, a wedding title) is a sibling of the envelope,
 * not a part of it, so it would sit still while the envelope opens and then
 * stay on top of the card underneath. Give the widget the class
 * ww-cover-fade in Elementor (Advanced > CSS Classes) and it leaves with the
 * envelope instead: the script marks the cover on the lift, one stage before
 * the envelope falls away.
 *
 * The easing is written out rather than read from --ww-ease, because the
 * cover is the envelope's ancestor and inherits nothing from it. The script
 * copies --ww-d-lift up, so the fade still follows the panel's Speed.
 */

.ww-cover-fade {
	transition:
		opacity var(--ww-d-lift, 600ms) ease,
		transform var(--ww-d-lift, 600ms) cubic-bezier(0.62, 0.04, 0.24, 1);
}

.ww-cover--opening .ww-cover-fade {
	opacity: 0;
	transform: translateY(-0.75rem);
	/* Cleared straight away: a faded overlay would still swallow taps meant
	   for the card underneath for the length of the transition. */
	pointer-events: none;
}

/*
 * Held hidden once the transition is over. Visibility rather than display, so
 * the overlay keeps its place and nothing around it reflows as it goes.
 */
.ww-cover--revealed .ww-cover-fade {
	opacity: 0;
	pointer-events: none;
	visibility: hidden;
}

/*
 * For an overlay that sits in the cover's flow rather than over it. With
 * After Opening set to keep the card as the first screen, the cover becomes
 * the top of the page, and a hidden widget still in the flow would leave a
 * blank gap there.
 */
.ww-cover--revealed .ww-cover-fade.ww-cover-fade-flow {
	display: none;
}

/*
 * Only while the timeline runs. Each panel carries a clip-path and a
 * drop-shadow, so without its own compositor layer the browser re-rasterises
 * that work on every frame of the opening. Promoting them for good would keep
 * the memory for a page that has finished moving, so the class is added when
 * the envelope opens and dropped when it settles.
 */
.ww-envelope.is-running .ww-envelope__env,
.ww-envelope.is-running .ww-envelope__card,
.ww-envelope.is-running .ww-envelope__pocket,
.ww-envelope.is-running .ww-envelope__pocket i,
.ww-envelope.is-running .ww-envelope__flap,
.ww-envelope.is-running .ww-envelope__shadow {
	will-change: transform, opacity;
}

/* ---------- fill screen ----------
 *
 * The card is the page from the first frame. It sits in the normal flow at
 * whatever height its content gives it, and nothing about it changes when
 * the envelope opens: no travel, no clipping to the screen, no swap at the
 * end. The paper is laid over the screen instead, fixed to the viewport, and
 * opening it is a split. The seal lifts, then the flap leaves through the
 * top of the screen while the pocket leaves through the bottom, and the card
 * is simply there underneath. There is no back panel: the flap and the
 * pocket cover the screen between them, and the lining would only show over
 * the card as they part.
 *
 * Nothing on the card's side is sized to the viewport, so the address bar
 * sliding in and out cannot resize it either. The script adds is-fill
 * wherever Size Mode is Fill screen, which it reads from --ww-fill because
 * the mode is set per device.
 *
 * Fixed paper needs every ancestor between it and the page to leave
 * position: fixed alone, so the 3D setup (perspective, preserve-3d, the
 * resting tilt, will-change) is dropped here. The split is flat and needs
 * none of it.
 */

/*
 * A definite width on Elementor's wrapper for the widget. Without one, a
 * cover container that wraps its items (Elementor's default on phones)
 * measures the widget before the card's 100% width means anything, gets a
 * height for a card laid out at some other width, and keeps it: the cover
 * then centres a widget shorter than the card, which shows the cover's own
 * background in a strip above the card and lets the card hang over the
 * section below.
 */
.elementor-widget:has(.ww-envelope.is-fill) {
	width: 100%;
}

.ww-envelope.is-fill .ww-envelope__scene {
	width: 100%;
	min-height: 0;
	overflow: visible;
	perspective: none;
}

.ww-envelope.is-fill .ww-envelope__env,
.ww-envelope.is-fill.is-running .ww-envelope__env {
	width: 100%;
	max-width: none;
	height: auto;
	aspect-ratio: auto;
	transform: none;
	transform-style: flat;
	-webkit-transform-style: flat;
	transition: none;
	will-change: auto;
}

/*
 * At least one screen tall, so a card designed a little shorter than the
 * screen (a container at 95vh, say) still covers it. A cover that centres
 * its content would otherwise show its own background in a strip above and
 * below the card. svh, not dvh: it is the screen with the address bar
 * showing, and it does not change as the bar slides in and out, so neither
 * does the card. The content grows into the room (see Fill the Card above).
 */
.ww-envelope.is-fill .ww-envelope__card,
.ww-envelope.is-fill.is-running .ww-envelope__card {
	position: relative;
	inset: auto;
	display: flex;
	flex-direction: column;
	width: 100%;
	height: auto;
	min-height: 100vh;
	min-height: 100svh;
	z-index: auto;
	overflow: hidden;
	overflow: clip;
	box-shadow: none;
	transform: none;
	transition: none;
	will-change: auto;
}

/*
 * Keeps whatever the card content stacks with its own z-index under the
 * paper while the envelope is shut. Dropped once it is open, so the content
 * stacks against the rest of the page like any other section, and a card
 * container at z-index 8 is not trapped under a fixed header at 6.
 */
.ww-envelope.is-fill:not(.is-revealed) .ww-envelope__card {
	isolation: isolate;
}

.ww-envelope.is-fill .ww-envelope__content {
	position: relative;
	flex: 1 0 auto;
	height: auto;
}

.ww-envelope.is-fill .ww-envelope__shadow,
.ww-envelope.is-fill .ww-envelope__back {
	display: none;
}

.ww-envelope.is-fill .ww-envelope__pocket,
.ww-envelope.is-fill .ww-envelope__flap {
	position: fixed;
	inset: 0;
	opacity: 1;
	transform: none;
	transition: transform var(--ww-d-flap) var(--ww-ease);
}

.ww-envelope.is-fill .ww-envelope__seal,
.ww-envelope.is-fill .ww-envelope__hint {
	position: fixed;
}

/* Document order, as z-index, so it holds against the isolated card. */
.ww-envelope.is-fill .ww-envelope__pocket { z-index: 2; }
.ww-envelope.is-fill .ww-envelope__flap { z-index: 3; }
.ww-envelope.is-fill .ww-envelope__seal { z-index: 4; }
.ww-envelope.is-fill .ww-envelope__hint { z-index: 5; }

.ww-envelope.is-fill.is-flap .ww-envelope__flap {
	transform: translateY(-100%);
}

.ww-envelope.is-fill.is-flap .ww-envelope__pocket {
	transform: translateY(100%);
}

/* Out of the way for good. Nothing here is in the flow, so nothing moves. */
.ww-envelope.is-fill.is-revealed .ww-envelope__pocket,
.ww-envelope.is-fill.is-revealed .ww-envelope__flap,
.ww-envelope.is-fill.is-revealed .ww-envelope__seal,
.ww-envelope.is-fill.is-revealed .ww-envelope__hint {
	display: none;
}

/*
 * In the editor a paper fixed to the screen would cover every section below
 * it, so the paper covers one screen at the top of the envelope instead.
 */
body.elementor-editor-active .ww-envelope.is-fill:not(.is-revealed) .ww-envelope__env {
	min-height: var(--ww-stage-h, 100vh);
}

body.elementor-editor-active .ww-envelope.is-fill .ww-envelope__pocket,
body.elementor-editor-active .ww-envelope.is-fill .ww-envelope__flap,
body.elementor-editor-active .ww-envelope.is-fill .ww-envelope__seal,
body.elementor-editor-active .ww-envelope.is-fill .ww-envelope__hint {
	position: absolute;
}

/* Jump to a stage without animating (reset, editor preview, reduced motion). */
.ww-envelope.is-instant,
.ww-envelope.is-instant * {
	transition: none !important;
}

@media (prefers-reduced-motion: reduce) {
	.ww-envelope__hint-text::before {
		animation: none;
	}
}
