/* Digital invitations — the shared motion engine (ADR-0069), loaded next to invitation.css on every invitation page.
   invitation.css is the structure; this file is everything that moves and the wide layout, for all collections:
   - the particles of the first screen (24 masked <i>) with one keyframe per kind of particle, and a dust of light in
     two depths over the first screen;
   - the hero text choreography: the kicker, the names letter by letter, the date, the place, one after another;
   - the intro: the envelope (seal that scales and cracks in two, flap that turns in perspective, card that lifts out)
     or, for a collection of the kind "cover" (inv-k--cover), a cover that fills the screen and leaves in one of four
     ways (zoom, curtain, doors, bloom); invitation.js reads --pv-open-ms and --pv-reveal-ms here;
   - the scene of a cover collection: a sky, a far silhouette and a near one, as wide as the page;
   - parallax while scrolling: scroll-driven animations where the browser has them, else --pv-scroll (0..1 over the
     first screen, published by invitation.js) moves the layers;
   - the sections, which come in from alternating sides with what is inside a step behind;
   - the signature loops a collection switches on: light rays, a sheen that sweeps the first screen, an ornament
     that breathes or sways, Ken Burns on the photographs;
   - the flip of a countdown cell when it changes;
   - from 900 px, the layout of a site: full-width sections on alternating bands, two columns, grids.
   Collections set the --pv-* variables (historically "Poveste", the first collection with this engine) and switch
   slots on; they never redefine these rules. Keyframes here are inv-*; a collection's own are {collection}-*.
   Every keyframe animates only transform, opacity, translate, rotate and scale (test). Contract: docs/invitation-templates.md. */

/* ================= variables of the engine (defaults) ================= */

.inv {
    /* the scene of a cover collection: a sky, a far silhouette and a near one standing on the page colour */
    --pv-sky: linear-gradient(180deg, var(--inv-accent), var(--inv-bg));
    --pv-far: none;
    --pv-far-fill: transparent;
    --pv-far-h: clamp(120px, 40cqi, 250px);
    --pv-far-size: auto 100%;
    --pv-far-bottom: 0px;
    --pv-near: none;
    --pv-near-fill: transparent;
    --pv-near-h: clamp(70px, 22cqi, 140px);
    --pv-near-size: auto 100%;
    --pv-horizon: calc(100% - var(--pv-far-h) * .6);
    --pv-ink: var(--inv-text);
    --pv-night: color-mix(in srgb, var(--inv-bg) 68%, #000);

    /* the sections, as they come in: from below, or from alternating sides (odd / even sections) */
    --pv-in: translateY(46px);
    --pv-in-odd: translateX(-56px);
    --pv-in-even: translateX(56px);
    --pv-in-time: 1.1s;
    --pv-in-wide-time: calc(var(--pv-in-time) + .5s);

    /* the hero text: how far apart the letters of the names come in (0 = all at once) */
    --pv-letter-step: 42ms;
    --pv-letter-anim: inv-letter-in;

    /* the intro: when invitation.js lets the first screen appear and when it unlocks the page (milliseconds) */
    --pv-open-ms: 2300;
    /* the envelope stays opaque until --pv-open-ms: the first screen appears just before, so the guest sees the
       names come in letter by letter as the envelope fades, not behind it */
    --pv-reveal-ms: 2000;

    /* the cover, as it leaves (cover collections) */
    --pv-open: scale(1.16);
    --pv-open-opacity: 0;
    --pv-door-display: none;
    --pv-door-fill: var(--inv-bg);
    --pv-bloom-display: none;
    --pv-bloom: color-mix(in srgb, var(--inv-primary) 70%, #fff);
    --pv-cover-ink: var(--pv-ink);
    --pv-cover-accent: var(--inv-primary);
    --pv-cover-anim: inv-bokeh;

    /* the dust of light over the first screen: fine glowing points that rise (or fall, from -50% to 0%) */
    --pv-dust: var(--inv-primary);
    --pv-dust-display: none;
    --pv-dust-opacity: .5;
    --pv-dust-from: 0%;
    --pv-dust-to: -50%;
    --pv-dust-time: 38s;

    /* signature loops, off until a collection switches them on */
    --pv-rays-display: none;
    --pv-rays: var(--inv-primary);
    --pv-rays-opacity: .2;
    --pv-rays-time: 70s;
    --pv-sheen-display: none;
    --pv-sheen: #fff;
    --pv-sheen-opacity: .4;
    --pv-sheen-time: 12s;
    --pv-orn-anim: none;
    --pv-orn-time: 9s;
    --pv-kb-time: 26s;
    --pv-kb-origin: 50% 40%;
    --pv-kb-hero: none;
    /* the photograph in its frame; a template whose frame is a paper border (--photo-pad) or a shape cut out of
       the photo sets none, since the frame clips only at its outer edge */
    --pv-kb-photo: inv-ken-burns;

    /* a wide screen: every other section lies on a band of colour; a flourish turns in at a corner of each section */
    --pv-band: color-mix(in srgb, var(--inv-accent) 50%, var(--inv-surface));
    --pv-deco: none;
    --pv-deco-display: none;
    --pv-deco-ratio: 360 / 130;
    --pv-deco-opacity: .3;
}

.inv.inv--dark {
    --pv-band: color-mix(in srgb, var(--inv-surface) 60%, var(--inv-bg));
    --pv-dust: color-mix(in srgb, var(--inv-primary) 45%, #fff);
    --pv-dust-opacity: .8;
}

/* A cover collection: the scene stands behind the names, the card is transparent on the page, the intro is a cover. */
.inv-k--cover {
    --pv-dust-display: block;
    --pv-open-ms: 1800;
    --pv-reveal-ms: 900;
    --hero-halo: none;
    --hero-pad: 64px 8px calc(var(--pv-far-h) * .58 + 58px);
    --names-color: var(--pv-ink);
    --names-shadow: 0 2px 30px color-mix(in srgb, var(--inv-bg) 72%, transparent);
    --kicker-color: var(--inv-primary);
    --date-short-display: none;
    --date-long-display: block;
    --card-bg: transparent;
    --card-shadow: none;
    --card-border: 0 solid transparent;
    --card-radius: 0;
    --card-pad: 30px 6px 36px;
    --card-pad-wide: 40px 24px 52px;
    --divider-rule-display: none;
    --pv-band: color-mix(in srgb, var(--inv-surface) 55%, var(--inv-bg));
}

.inv-k--cover.inv--dark {
    --names-shadow: 0 2px 34px color-mix(in srgb, #000 55%, transparent);
    --pv-band: color-mix(in srgb, var(--inv-surface) 65%, var(--inv-bg));
}

/* ================= particles and dust of the first screen ================= */

/* The particle layer is as wide as the invitation (the page, or the phone of the preview), not as the column. A
   particle is visible only when its collection gives it a shape (--amb-image); without one the mask is empty. */
.inv__ambient {
    display: block;
    left: 50%;
    right: auto;
    width: 100cqi;
    margin-left: -50cqi;
    translate: 0 calc(var(--pv-scroll, 0) * 34px);
}

/* The dust: two screens tall, repeating every screen, so moving by one screen (half the height) loops seamlessly.
   The first layer: fine points, slow. The second: larger points closer to the eye, faster, twinkling. */
.inv__ambient::before,
.inv__ambient::after {
    --dot: var(--pv-dust) 0 1.2px, color-mix(in srgb, var(--pv-dust) 45%, transparent) 2px, transparent 3px;
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 200%;
    display: var(--pv-dust-display);
    background:
        radial-gradient(circle at 4% 9%, var(--dot)), radial-gradient(circle at 13% 63%, var(--dot)),
        radial-gradient(circle at 21% 31%, var(--dot)), radial-gradient(circle at 27% 84%, var(--dot)),
        radial-gradient(circle at 36% 18%, var(--dot)), radial-gradient(circle at 44% 52%, var(--dot)),
        radial-gradient(circle at 51% 7%, var(--dot)), radial-gradient(circle at 58% 71%, var(--dot)),
        radial-gradient(circle at 66% 38%, var(--dot)), radial-gradient(circle at 73% 92%, var(--dot)),
        radial-gradient(circle at 81% 24%, var(--dot)), radial-gradient(circle at 88% 58%, var(--dot)),
        radial-gradient(circle at 94% 12%, var(--dot)), radial-gradient(circle at 97% 77%, var(--dot)),
        radial-gradient(circle at 8% 47%, var(--dot)), radial-gradient(circle at 17% 4%, var(--dot)),
        radial-gradient(circle at 31% 57%, var(--dot)), radial-gradient(circle at 40% 96%, var(--dot)),
        radial-gradient(circle at 55% 33%, var(--dot)), radial-gradient(circle at 69% 64%, var(--dot)),
        radial-gradient(circle at 77% 3%, var(--dot)), radial-gradient(circle at 91% 42%, var(--dot));
    background-size: 100% 50%;
    opacity: var(--pv-dust-opacity);
    pointer-events: none;
    animation: inv-dust var(--pv-dust-time) linear infinite;
}

.inv__ambient::after {
    --dot: var(--pv-dust) 0 1.8px, color-mix(in srgb, var(--pv-dust) 40%, transparent) 3px, transparent 7px;
    background:
        radial-gradient(circle at 9% 41%, var(--dot)), radial-gradient(circle at 23% 12%, var(--dot)),
        radial-gradient(circle at 34% 68%, var(--dot)), radial-gradient(circle at 47% 29%, var(--dot)),
        radial-gradient(circle at 62% 86%, var(--dot)), radial-gradient(circle at 71% 55%, var(--dot)),
        radial-gradient(circle at 86% 33%, var(--dot)), radial-gradient(circle at 92% 79%, var(--dot));
    background-size: 100% 50%;
    animation:
        inv-dust calc(var(--pv-dust-time) * .62) linear infinite,
        inv-dust-twinkle 3.4s ease-in-out infinite;
}

/* ================= the scene of a cover collection ================= */

.inv-k--cover .inv-hero::before,
.inv-k--cover .inv-hero::after,
.inv-k--cover .inv-hero__frame {
    left: 50%;
    right: auto;
    width: 100cqi;
    margin-left: -50cqi;
}

.inv-k--cover .inv-hero::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    background: var(--pv-sky);
    pointer-events: none;
}

.inv-k--cover .inv-hero__frame {
    display: block;
    top: auto;
    bottom: var(--pv-far-bottom);
    height: var(--pv-far-h);
    border: 0;
    border-radius: 0;
    box-shadow: none;
    background: var(--pv-far-fill);
    -webkit-mask: var(--pv-far) bottom center / var(--pv-far-size) repeat-x;
    mask: var(--pv-far) bottom center / var(--pv-far-size) repeat-x;
}

.inv-k--cover .inv-hero::after {
    content: "";
    position: absolute;
    bottom: -1px;
    height: var(--pv-near-h);
    background: var(--pv-near-fill);
    -webkit-mask: var(--pv-near) bottom center / var(--pv-near-size) repeat-x;
    mask: var(--pv-near) bottom center / var(--pv-near-size) repeat-x;
    pointer-events: none;
}

/* Particles of a cover collection live all over the first screen; a template that lets them fall or rise puts them
   back at an edge. */
.inv-k--cover .inv__ambient i {
    top: var(--amb-y);
}

.inv-k--cover .inv-hero__scroll span {
    border-color: var(--pv-ink);
}

.inv-k--cover .inv-countdown > div {
    min-width: 0;
}

/* ================= signature loops ================= */

/* Light rays behind the first screen: a wheel of soft beams turning slowly, fading at the rim. */
.inv__backdrop::before {
    content: "";
    position: absolute;
    top: calc(var(--hero-min-height) * .42);
    left: 50%;
    display: var(--pv-rays-display);
    width: 190cqi;
    aspect-ratio: 1;
    translate: -50% -50%;
    background: repeating-conic-gradient(from 0deg, transparent 0deg 9deg, var(--pv-rays) 13deg 18deg, transparent 22deg 30deg);
    -webkit-mask: radial-gradient(circle, #000 0, rgba(0, 0, 0, .5) 28%, transparent 58%);
    mask: radial-gradient(circle, #000 0, rgba(0, 0, 0, .5) 28%, transparent 58%);
    opacity: var(--pv-rays-opacity);
    pointer-events: none;
    animation: inv-rays var(--pv-rays-time) linear infinite;
}

/* A sheen that sweeps the first screen every few seconds, like light on satin. */
.inv__backdrop::after {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    display: var(--pv-sheen-display);
    width: 100%;
    height: var(--hero-min-height);
    background: linear-gradient(105deg, transparent 36%, var(--pv-sheen) 50%, transparent 64%);
    opacity: var(--pv-sheen-opacity);
    mix-blend-mode: soft-light;
    pointer-events: none;
    animation: inv-sheen var(--pv-sheen-time) ease-in-out infinite;
}

/* The ornaments of the first screen can breathe (inv-breathe) or sway (inv-sway-soft). */
.inv-hero .orn {
    animation: var(--pv-orn-anim) var(--pv-orn-time) ease-in-out infinite;
}

/* Ken Burns: the photographs drift and grow slowly inside their frames. The hero photograph only when the template
   asks (--pv-kb-hero: inv-ken-burns), because the hero does not clip what grows beyond it. */
.inv-envelope__photo {
    transform-origin: var(--pv-kb-origin);
    animation: inv-ken-burns var(--pv-kb-time) ease-in-out infinite alternate;
}

.inv-photo__frame img {
    transform-origin: var(--pv-kb-origin);
    animation: var(--pv-kb-photo) var(--pv-kb-time) ease-in-out infinite alternate;
}

.inv-hero__image {
    transform-origin: var(--pv-kb-origin);
    animation: var(--pv-kb-hero) var(--pv-kb-time) ease-in-out infinite alternate;
}

/* ================= the hero text ================= */

.inv-hero__letters,
.inv-word,
.inv-letter {
    display: inline-block;
}

/* A word keeps its letters on one line (a double name wraps at its hyphen, which ends a word), and the letters are
   not selected: the plain name beside them (.inv-sr) is what a guest copies. */
.inv-word {
    white-space: nowrap;
}

.inv-hero__letters {
    -webkit-user-select: none;
    user-select: none;
}

.inv-hero__scroll {
    animation: inv-bounce 2.4s ease-in-out infinite;
}

/* ================= the intro: the envelope ================= */

html.inv-js .inv-envelope {
    transition: opacity .8s ease;
}

.inv-envelope__body {
    animation: inv-float 3.2s ease-in-out infinite;
}

.inv-envelope__letter {
    transition: transform 1s cubic-bezier(.2, .7, .2, 1) 1.15s;
}

.inv-envelope__flap {
    transition: transform .8s cubic-bezier(.45, 0, .2, 1) .55s, z-index 0s .95s;
}

.inv-envelope__seal {
    transition: color .25s ease .3s;
}

/* The second copy of the seal waits, invisible, under the first. */
.inv-envelope__seal--b {
    opacity: 0;
    /* the monogram of the right half fades with the left one, it does not vanish at the tap */
    transition: color .25s ease .3s;
}

.inv-envelope.is-opening .inv-envelope__body {
    animation: none;
}

/* The seal swells, then cracks along a jagged line; the two pieces fly off and the flap turns under them. */
.inv-envelope.is-opening .inv-envelope__seal {
    color: transparent;
    clip-path: polygon(0 0, 54% 0, 44% 22%, 58% 48%, 46% 74%, 52% 100%, 0 100%);
    animation: inv-seal-left 1.1s cubic-bezier(.4, 0, .3, 1) both;
}

.inv-envelope.is-opening .inv-envelope__seal--b {
    opacity: 1;
    clip-path: polygon(54% 0, 100% 0, 100% 100%, 52% 100%, 46% 74%, 58% 48%, 44% 22%);
    animation-name: inv-seal-right;
}

.inv-envelope.is-opening .inv-envelope__flap {
    z-index: 0;
    transform: perspective(900px) rotateX(180deg);
}

.inv-envelope.is-opening .inv-envelope__letter {
    z-index: 2;
    transform: translateY(-66%) scale(1.04);
}

.inv-envelope.is-open {
    opacity: 0;
    pointer-events: none;
}

/* ================= the intro: the cover ================= */

html.inv-js .inv-k--cover .inv-envelope {
    gap: 0;
    padding: 0 22px;
    overflow: hidden;
    background: var(--inv-bg);
    color: var(--pv-cover-ink);
    text-align: center;
    transition: transform 1.3s cubic-bezier(.65, 0, .2, 1) .3s, opacity .9s ease .75s;
}

.inv-k--cover .inv-envelope::before {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--pv-sky);
}

.inv-k--cover .inv-envelope::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: var(--pv-near-h);
    background: var(--pv-near-fill);
    -webkit-mask: var(--pv-near) bottom center / var(--pv-near-size) repeat-x;
    mask: var(--pv-near) bottom center / var(--pv-near-size) repeat-x;
    pointer-events: none;
}

.inv-k--cover .inv-envelope__body {
    display: none;
}

.inv-k--cover .inv-envelope__scene {
    container-type: size;
    position: absolute;
    inset: 0;
    display: block;
    overflow: hidden;
    pointer-events: none;
}

.inv-k--cover .inv-envelope__scene::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: var(--pv-far-bottom);
    height: var(--pv-far-h);
    background: var(--pv-far-fill);
    -webkit-mask: var(--pv-far) bottom center / var(--pv-far-size) repeat-x;
    mask: var(--pv-far) bottom center / var(--pv-far-size) repeat-x;
}

/* The light that floods the screen when the cover blooms. */
.inv-k--cover .inv-envelope__scene::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: 13%;
    z-index: 3;
    display: var(--pv-bloom-display);
    width: 44px;
    height: 44px;
    margin-left: -22px;
    border-radius: 50%;
    background: radial-gradient(circle, var(--pv-bloom) 0 34%, color-mix(in srgb, var(--pv-bloom) 60%, transparent) 60%, transparent 72%);
    opacity: 0;
    transform: scale(0);
    transition: transform 1.25s cubic-bezier(.5, 0, .8, .4) .15s, opacity .35s ease .15s;
}

.inv-k--cover .inv-envelope__scene i {
    position: absolute;
    width: var(--amb-size);
    aspect-ratio: var(--amb-ratio, 1);
    background: var(--amb-fill);
    -webkit-mask: var(--amb-image) center / contain no-repeat;
    mask: var(--amb-image) center / contain no-repeat;
    opacity: var(--amb-opacity);
    animation: var(--pv-cover-anim) var(--amb-duration) linear infinite;
}

.inv-k--cover .inv-envelope__scene i:nth-child(1) { left: 8%; top: 16%; animation-delay: 0s; scale: .9; }
.inv-k--cover .inv-envelope__scene i:nth-child(2) { left: 24%; top: 58%; animation-delay: -4s; scale: .6; }
.inv-k--cover .inv-envelope__scene i:nth-child(3) { left: 39%; top: 30%; animation-delay: -9s; scale: 1.15; }
.inv-k--cover .inv-envelope__scene i:nth-child(4) { left: 55%; top: 76%; animation-delay: -2s; scale: .7; }
.inv-k--cover .inv-envelope__scene i:nth-child(5) { left: 68%; top: 12%; animation-delay: -6s; scale: 1; }
.inv-k--cover .inv-envelope__scene i:nth-child(6) { left: 82%; top: 44%; animation-delay: -11s; scale: .8; }
.inv-k--cover .inv-envelope__scene i:nth-child(7) { left: 91%; top: 68%; animation-delay: -7s; scale: 1.2; }
.inv-k--cover .inv-envelope__scene i:nth-child(8) { left: 15%; top: 84%; animation-delay: -12s; scale: .75; }
.inv-k--cover .inv-envelope__scene i:nth-child(9) { left: 47%; top: 50%; animation-delay: -3s; scale: .55; }
.inv-k--cover .inv-envelope__scene i:nth-child(10) { left: 61%; top: 26%; animation-delay: -8s; scale: .95; }
.inv-k--cover .inv-envelope__scene i:nth-child(11) { left: 31%; top: 8%; animation-delay: -10s; scale: .7; }
.inv-k--cover .inv-envelope__scene i:nth-child(12) { left: 76%; top: 90%; animation-delay: -5s; scale: 1.05; }

/* With a photograph the cover is the photograph, under a veil that keeps the words readable. */
.inv-k--cover .inv-envelope__photo {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.inv-k--cover .inv-envelope:has(.inv-envelope__photo) {
    --pv-cover-ink: #fff;
    --pv-cover-accent: #fff;
}

.inv-k--cover .inv-envelope:has(.inv-envelope__photo)::before,
.inv-k--cover .inv-envelope:has(.inv-envelope__photo) .inv-envelope__scene::before {
    display: none;
}

.inv-k--cover .inv-envelope:has(.inv-envelope__photo)::after {
    top: 0;
    height: auto;
    -webkit-mask: none;
    mask: none;
    background: linear-gradient(180deg, rgba(0, 0, 0, .34), rgba(0, 0, 0, .16) 34%, rgba(0, 0, 0, .5) 62%, rgba(0, 0, 0, .78));
}

.inv-k--cover .inv-envelope__door {
    position: absolute;
    top: 0;
    bottom: 0;
    z-index: 1;
    display: var(--pv-door-display);
    width: 50.2%;
    background: var(--pv-door-fill);
    transition: transform 1.25s cubic-bezier(.7, 0, .2, 1) .35s;
}

.inv-k--cover .inv-envelope__door--l {
    left: 0;
    box-shadow: inset -1px 0 0 color-mix(in srgb, var(--inv-ornament) 60%, transparent);
}

.inv-k--cover .inv-envelope__door--r {
    right: 0;
    scale: -1 1;
}

.inv-k--cover .inv-envelope__kicker,
.inv-k--cover .inv-envelope__names,
.inv-k--cover .inv-envelope__date,
.inv-k--cover .inv-envelope .inv-button {
    position: relative;
    z-index: 2;
    transition: opacity .5s ease, transform .5s ease;
}

.inv-k--cover .inv-envelope__kicker {
    margin: 0 0 20px;
    font: 500 .66rem/1.5 var(--f-body);
    letter-spacing: .42em;
    color: var(--pv-cover-accent);
}

.inv-k--cover .inv-envelope__names {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin: 0;
    font-family: var(--names-font);
    font-weight: var(--names-weight);
    font-style: var(--names-style);
    font-size: var(--pv-cover-names, clamp(2.6rem, 13.5cqi, 5rem));
    line-height: 1.06;
    letter-spacing: var(--names-spacing);
    text-transform: var(--names-transform);
    color: var(--pv-cover-ink);
    text-shadow: 0 2px 26px rgba(0, 0, 0, .28);
    overflow-wrap: anywhere;
}

.inv-k--cover .inv-envelope__amp {
    margin: .12em 0;
    font-family: var(--amp-font);
    font-size: .5em;
    font-style: var(--amp-style);
    color: var(--pv-cover-accent);
}

.inv-k--cover .inv-envelope__date {
    display: block;
    margin: 0 0 30px;
    padding-top: 58px;
    font: 400 .68rem/1.5 var(--f-body);
    letter-spacing: .34em;
    text-transform: uppercase;
    background: linear-gradient(180deg, color-mix(in srgb, var(--pv-cover-ink) 60%, transparent), color-mix(in srgb, var(--pv-cover-ink) 60%, transparent)) 50% 18px / 1px 26px no-repeat;
}

.inv-k--cover .inv-envelope .inv-button {
    min-height: 50px;
    padding: 14px 26px;
    border: 1px solid color-mix(in srgb, var(--pv-cover-ink) 56%, transparent);
    border-radius: 0;
    background: color-mix(in srgb, #000 12%, transparent);
    color: var(--pv-cover-ink);
    font-size: .66rem;
    letter-spacing: .3em;
}

.inv-k--cover .inv-envelope .inv-button::before,
.inv-k--cover .inv-envelope .inv-button::after {
    content: "";
    width: 18px;
    height: 1px;
    margin: 0 14px;
    background: currentColor;
    opacity: .7;
    animation: inv-hint 2.6s ease-in-out infinite;
}

/* The words of the cover come in one after another when the page opens. "backwards" hands them back to their own
   style once in, so the opening can still move them away. */
html.inv-js .inv-k--cover .inv-envelope__kicker,
html.inv-js .inv-k--cover .inv-envelope__names,
html.inv-js .inv-k--cover .inv-envelope__date,
html.inv-js .inv-k--cover .inv-envelope .inv-button {
    animation: inv-cover-in 1.4s cubic-bezier(.2, .7, .2, 1) backwards;
}

html.inv-js .inv-k--cover .inv-envelope__kicker { animation-delay: .25s; }
html.inv-js .inv-k--cover .inv-envelope__names { animation-delay: .55s; animation-duration: 1.9s; }
html.inv-js .inv-k--cover .inv-envelope__date { animation-delay: 1.2s; }
html.inv-js .inv-k--cover .inv-envelope .inv-button { animation-delay: 1.55s; }

/* A short screen (a phone held sideways): the cover is pressed together, so the button stays within reach. */
@media (max-height: 620px) {
    .inv-k--cover .inv-envelope__names {
        flex-direction: row;
        flex-wrap: wrap;
        justify-content: center;
        gap: 0 .3em;
        font-size: clamp(1.7rem, 9svh, 3rem);
    }

    .inv-k--cover .inv-envelope__kicker {
        margin-bottom: 10px;
    }

    .inv-k--cover .inv-envelope__date {
        margin-bottom: 16px;
        padding-top: 30px;
        background-position: 50% 8px;
        background-size: 1px 16px;
    }

    .inv-k--cover .inv-envelope .inv-button {
        min-height: 44px;
        padding: 10px 22px;
    }
}

.inv-k--cover .inv-envelope.is-opening {
    transform: var(--pv-open);
    opacity: var(--pv-open-opacity);
}

.inv-k--cover .inv-envelope.is-opening .inv-envelope__kicker,
.inv-k--cover .inv-envelope.is-opening .inv-envelope__names,
.inv-k--cover .inv-envelope.is-opening .inv-envelope__date,
.inv-k--cover .inv-envelope.is-opening .inv-button {
    opacity: 0;
    transform: translateY(-18px);
}

/* A tap before the words have come in: their entrance stops, otherwise it would keep them visible (an animation
   wins over the opening's opacity) while the cover goes away. */
html.inv-js .inv-k--cover .inv-envelope.is-opening .inv-envelope__kicker,
html.inv-js .inv-k--cover .inv-envelope.is-opening .inv-envelope__names,
html.inv-js .inv-k--cover .inv-envelope.is-opening .inv-envelope__date,
html.inv-js .inv-k--cover .inv-envelope.is-opening .inv-button {
    animation: none;
}

.inv-k--cover .inv-envelope.is-opening .inv-envelope__door--l,
.inv-k--cover .inv-envelope.is-opening .inv-envelope__door--r {
    transform: translateX(-101%);
}

.inv-k--cover .inv-envelope.is-opening .inv-envelope__scene::after {
    opacity: 1;
    transform: scale(64);
}

/* With a photograph the cover never splits into doors: it is the photograph, and it leaves with the slow zoom. */
.inv-k--cover .inv-envelope:has(.inv-envelope__photo) {
    --pv-open: scale(1.16);
    --pv-open-opacity: 0;
    --pv-bloom-display: none;
}

.inv-k--cover .inv-envelope:has(.inv-envelope__photo) .inv-envelope__door {
    display: none;
}

/* ================= the sections, as they come in ================= */

html.inv-js .inv [data-reveal] {
    opacity: 0;
    transform: var(--pv-in);
    transition: opacity var(--pv-in-time) ease, transform var(--pv-in-time) cubic-bezier(.2, .7, .2, 1);
}

html.inv-js .inv-card > .inv-section:nth-of-type(odd) {
    transform: var(--pv-in-odd);
}

html.inv-js .inv-card > .inv-section:nth-of-type(even) {
    transform: var(--pv-in-even);
}

html.inv-js .inv-hero__content[data-reveal] {
    transform: translateY(26px) scale(.97);
    transition-duration: 1.6s;
}

html.inv-js .inv-program__item[data-reveal] {
    transform: translateY(34px);
}

html.inv-js .inv-program__item:nth-child(2) { transition-delay: .12s; }
html.inv-js .inv-program__item:nth-child(3) { transition-delay: .24s; }
html.inv-js .inv-program__item:nth-child(4) { transition-delay: .36s; }

html.inv-js .inv-card > .inv-section.is-in,
html.inv-js .inv-hero__content[data-reveal].is-in,
html.inv-js .inv-program__item[data-reveal].is-in,
html.inv-js .inv [data-reveal].is-in {
    opacity: 1;
    transform: none;
}

/* What is inside follows a step behind: the kicker, the names, the date, the place; a family after the other; the
   cells of the countdown. */
html.inv-js .inv .is-in .inv-hero__kicker,
html.inv-js .inv .is-in .inv-hero__date,
html.inv-js .inv .is-in .inv-hero__place,
html.inv-js .inv .is-in .inv-family__item,
html.inv-js .inv .is-in .inv-countdown > div {
    animation: inv-rise-in 1s cubic-bezier(.2, .7, .2, 1) both;
}

html.inv-js .inv .is-in .inv-hero__kicker { animation-delay: .15s; }
/* The names: the entrance, then the template's own loop on them, if it has one (--pv-names-loop, e.g. Neon's
   flicker), which the entrance would otherwise replace. */
html.inv-js .inv .is-in .inv-hero__names {
    animation: inv-rise-in 1.2s cubic-bezier(.2, .7, .2, 1) .3s both, var(--pv-names-loop, none);
}

html.inv-js .inv .is-in .inv-hero__date { animation-delay: 1.05s; }
html.inv-js .inv .is-in .inv-hero__place { animation-delay: 1.25s; }
html.inv-js .inv .is-in .inv-family__item:nth-child(2) { animation-delay: .14s; }
html.inv-js .inv .is-in .inv-family__item:nth-child(3) { animation-delay: .28s; }
html.inv-js .inv .is-in .inv-family__item:nth-child(4) { animation-delay: .42s; }
html.inv-js .inv .is-in .inv-countdown > div:nth-child(2) { animation-delay: .1s; }
html.inv-js .inv .is-in .inv-countdown > div:nth-child(3) { animation-delay: .2s; }
html.inv-js .inv .is-in .inv-countdown > div:nth-child(4) { animation-delay: .3s; }

/* The letters of the names, one after another (--pv-letter-anim: none keeps the whole name together, for a template
   that paints its names with background-clip: text). */
html.inv-js .inv .is-in .inv-letter {
    animation: var(--pv-letter-anim) .9s cubic-bezier(.2, .7, .2, 1) calc(.35s + var(--i, 0) * var(--pv-letter-step)) both;
}

/* A cell of the countdown flips when its number changes (invitation.js marks it). */
.inv-countdown span {
    backface-visibility: hidden;
}

.inv-countdown > div.is-tick span {
    animation: inv-flip .55s cubic-bezier(.2, .7, .2, 1);
}

/* ================= parallax while scrolling ================= */

/* Where the browser can tie an animation to scrolling: the names fade and the layers of the scene part. */
@supports (animation-timeline: scroll()) {
    .inv-hero__content {
        animation: inv-hero-out linear both;
        animation-timeline: scroll(nearest block);
        animation-range: 40px 640px;
    }

    .inv-k--cover .inv-hero::before {
        animation: inv-sky-out linear both;
        animation-timeline: scroll(nearest block);
        animation-range: 0px 900px;
    }

    .inv-k--cover .inv-hero__frame {
        animation: inv-far-out linear both;
        animation-timeline: scroll(nearest block);
        animation-range: 0px 900px;
    }
}

/* Elsewhere invitation.js publishes --pv-scroll (0 at the top, 1 when the first screen has passed) and the same
   layers move with it; "translate" leaves "transform" to the entrances. */
@supports not (animation-timeline: scroll()) {
    .inv-hero__content {
        translate: 0 calc(var(--pv-scroll, 0) * -64px);
    }

    .inv-k--cover .inv-hero::before {
        translate: 0 calc(var(--pv-scroll, 0) * 110px);
    }

    .inv-k--cover .inv-hero__frame {
        translate: 0 calc(var(--pv-scroll, 0) * 54px);
    }
}

/* The large illustration at the top of the page drifts a little slower than the page. */
.inv__deco--a {
    translate: 0 calc(var(--pv-scroll, 0) * 40px);
}

/* ================= a wide screen (a computer) ================= */

/* From 900 px the invitation is as wide as the page, like a site: the sections lie across it, every other one on a band
   of colour, and what they hold is set out side by side (the families, the programme) up to 1120 px. A section's band
   stays still while what is inside comes in, one piece after another, and a flourish turns in at one of its corners.
   Phones keep the single column. ADR-0059 gave this to the cover collection; ADR-0069 gives it to every collection. */
@container (min-width: 900px) {
    .inv__column {
        max-width: none;
        padding: 0;
    }

    /* An envelope collection keeps its first screen in the column it was drawn for (its frames and corners belong
       to that width); the backdrop, the particles and the signature loops already fill the page. */
    .inv.inv-k--envelope .inv-hero {
        /* .inv as well: a template that bleeds its hero with margin-inline (Noir, Editorial, Poster) must not move it
           off the middle */
        width: 100%;
        max-width: var(--column-width);
        margin-inline: auto;
    }

    .inv-card {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        margin-top: 0;
        padding: 0 0 72px;
        border-left: 0;
        border-right: 0;
        border-radius: 0;
    }

    .inv-card > * {
        grid-column: 1 / -1;
    }

    .inv-corner--tl,
    .inv-corner--bl {
        left: max(0px, calc(50% - 720px));
    }

    .inv-corner--tr,
    .inv-corner--br {
        right: max(0px, calc(50% - 720px));
    }

    .inv-card > .inv-section {
        isolation: isolate;
        padding: clamp(72px, 8cqi, 116px) max(48px, calc(50cqi - 560px));
    }

    .inv-card > .inv-section:nth-of-type(even) {
        background: var(--pv-band);
    }

    /* The message and the photograph side by side, like a page of a story. */
    .inv-card > .inv-message:has(+ .inv-photo) {
        grid-column: 1;
        align-self: center;
        padding-right: 40px;
    }

    .inv-card > .inv-message:has(+ .inv-photo) p {
        font-size: calc(var(--message-size) * 1.5);
    }

    .inv-card > .inv-message + .inv-photo {
        grid-column: 2;
        padding-left: 40px;
        background: none;
    }

    .inv-card > .inv-divider {
        padding: 18px 0;
    }

    .inv-heading {
        font-size: calc(var(--heading-size) * 1.3);
    }

    .orn--heading {
        --orn-max: 190px;
    }

    .inv-message p {
        max-width: 34em;
        font-size: calc(var(--message-size) * 1.25);
    }

    .inv-photo__frame {
        width: min(100%, 440px);
    }

    .inv-family__grid {
        grid-template-columns: repeat(auto-fit, minmax(240px, 300px));
        justify-content: center;
        gap: 28px;
    }

    .inv-family__item {
        padding: 30px 22px;
    }

    .inv-family__names {
        font-size: calc(var(--family-size, 1.22rem) * 1.25);
    }

    .inv-when__date {
        font-size: 3.2rem;
    }

    .inv-countdown {
        max-width: 760px;
        gap: 22px;
        margin-top: 40px;
    }

    .inv-countdown > div {
        padding: 28px 8px 20px;
    }

    .inv-countdown span {
        font-size: 3.8rem;
    }

    .inv-countdown small {
        font-size: .72rem;
    }

    .inv-program__list {
        grid-template-columns: repeat(auto-fit, minmax(240px, 340px));
        justify-content: center;
        gap: 40px 28px;
        padding-top: 28px;
    }

    .inv-program__item {
        padding: 42px 24px 30px;
    }

    .inv-dress__text {
        font-size: 1.8rem;
    }

    .rsvp > * {
        max-width: 680px;
        margin-inline: auto;
    }

    .inv-closing__names {
        font-size: calc(var(--closing-size, 2.5rem) * 1.45);
    }

    /* The flourish in a corner of each section (left at one, right at the next), from --pv-deco. */
    .inv-card > .inv-section:not(.inv-photo)::after {
        content: "";
        position: absolute;
        top: 30px;
        left: max(20px, calc(50% - 700px));
        z-index: -1;
        display: var(--pv-deco-display);
        width: clamp(170px, 19cqi, 270px);
        aspect-ratio: var(--pv-deco-ratio);
        background: var(--inv-ornament);
        -webkit-mask: var(--pv-deco) center / contain no-repeat;
        mask: var(--pv-deco) center / contain no-repeat;
        opacity: var(--pv-deco-opacity);
        pointer-events: none;
    }

    .inv-card > .inv-section:nth-of-type(even):not(.inv-photo)::after {
        top: auto;
        left: auto;
        right: max(20px, calc(50% - 700px));
        bottom: 30px;
        scale: -1 -1;
    }

    .inv-card > .inv-section:nth-of-type(odd) > * { --pv-enter: var(--pv-in-odd); }
    .inv-card > .inv-section:nth-of-type(even) > * { --pv-enter: var(--pv-in-even); }
    .inv-family__item:nth-child(odd) { --pv-enter: translateX(-90px); }
    .inv-family__item:nth-child(even) { --pv-enter: translateX(90px); }

    html.inv-js .inv-card > .inv-section[data-reveal] {
        opacity: 1;
        transform: none;
        transition: none;
    }

    html.inv-js .inv-card > .inv-section > :not(.inv-family__grid, .inv-countdown, .inv-program__list),
    html.inv-js .inv-card > .inv-section:not(.inv-photo)::after {
        opacity: 0;
    }

    html.inv-js .inv-card > .inv-section.is-in > :not(.inv-family__grid, .inv-countdown, .inv-program__list) {
        animation: inv-enter var(--pv-in-wide-time) cubic-bezier(.16, .8, .24, 1) both;
    }

    html.inv-js .inv-card > .inv-section.is-in > :nth-child(2) { animation-delay: .14s; }
    html.inv-js .inv-card > .inv-section.is-in > :nth-child(3) { animation-delay: .28s; }
    html.inv-js .inv-card > .inv-section.is-in > :nth-child(4) { animation-delay: .42s; }
    html.inv-js .inv-card > .inv-section.is-in > :nth-child(5) { animation-delay: .56s; }
    html.inv-js .inv-card > .inv-section.is-in > :nth-child(n+6) { animation-delay: .7s; }

    html.inv-js .inv-card > .inv-section.is-in:not(.inv-photo)::after {
        animation: inv-deco-in 2.6s cubic-bezier(.2, .8, .2, 1) .25s both;
    }

    html.inv-js .inv .is-in .inv-family__item {
        animation-name: inv-enter;
        animation-duration: var(--pv-in-wide-time);
        animation-delay: .3s;
    }

    html.inv-js .inv .is-in .inv-family__item:nth-child(2) { animation-delay: .5s; }
    html.inv-js .inv .is-in .inv-family__item:nth-child(3) { animation-delay: .7s; }
    html.inv-js .inv .is-in .inv-family__item:nth-child(4) { animation-delay: .9s; }

    html.inv-js .inv .is-in .inv-countdown > div {
        animation-name: inv-pop;
        animation-duration: 1.3s;
    }

    html.inv-js .inv .is-in .inv-countdown > div:nth-child(1) { animation-delay: .4s; }
    html.inv-js .inv .is-in .inv-countdown > div:nth-child(2) { animation-delay: .55s; }
    html.inv-js .inv .is-in .inv-countdown > div:nth-child(3) { animation-delay: .7s; }
    html.inv-js .inv .is-in .inv-countdown > div:nth-child(4) { animation-delay: .85s; }

    html.inv-js .inv-program__item[data-reveal] {
        transform: translateY(80px) scale(.95);
        transition-duration: 1.5s;
    }

    html.inv-js .inv-program__item[data-reveal].is-in {
        transform: none;
    }
}

/* ================= keyframes: entrances and the intro ================= */

@keyframes inv-rise-in {
    from { opacity: 0; transform: translateY(20px); }
    to { opacity: 1; transform: none; }
}

@keyframes inv-letter-in {
    from { opacity: 0; transform: translateY(.35em) rotate(4deg); }
    to { opacity: 1; transform: none; }
}

@keyframes inv-cover-in {
    from { opacity: 0; transform: translateY(34px) scale(.94); }
    to { opacity: 1; transform: none; }
}

/* A piece of a section comes in from where the template says (--pv-in-odd / --pv-in-even, a family from its side). */
/* No transform at the end: an element keeps its own (a tilted photograph, a turned postmark) once it has come in. */
@keyframes inv-enter {
    from { opacity: 0; transform: var(--pv-enter, translateY(46px)); }
    to { opacity: 1; }
}

/* A flourish slides in from its side, turning. */
@keyframes inv-deco-in {
    from { opacity: 0; transform: translateX(-110px) rotate(-45deg); }
    to { opacity: var(--pv-deco-opacity); transform: none; }
}

@keyframes inv-pop {
    from { opacity: 0; transform: translateY(36px) scale(.7); }
    60% { opacity: 1; transform: translateY(-4px) scale(1.04); }
    to { opacity: 1; transform: none; }
}

/* A number of the countdown turns in from above, like a flip clock. */
@keyframes inv-flip {
    from { opacity: .2; transform: perspective(320px) rotateX(-92deg); }
    60% { opacity: 1; transform: perspective(320px) rotateX(14deg); }
    to { transform: none; }
}

@keyframes inv-hint {
    0%, 100% { transform: scaleX(1); opacity: .7; }
    50% { transform: scaleX(1.7); opacity: 1; }
}

/* The two pieces of the seal: they swell together, then part and fall away. */
@keyframes inv-seal-left {
    0% { opacity: 1; transform: none; }
    34% { opacity: 1; transform: scale(1.18); }
    48% { opacity: 1; transform: scale(1.14) translate(-4px, 2px) rotate(-5deg); }
    100% { opacity: 0; transform: scale(1) translate(-52px, 38px) rotate(-40deg); }
}

@keyframes inv-seal-right {
    0% { opacity: 1; transform: none; }
    34% { opacity: 1; transform: scale(1.18); }
    48% { opacity: 1; transform: scale(1.14) translate(4px, 3px) rotate(5deg); }
    100% { opacity: 0; transform: scale(1) translate(50px, 42px) rotate(36deg); }
}

@keyframes inv-float {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-6px); }
}

@keyframes inv-bounce {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(8px); }
}

/* ================= keyframes: scrolling ================= */

@keyframes inv-hero-out {
    to { opacity: 0; transform: translateY(-64px) scale(.96); }
}

@keyframes inv-sky-out {
    to { transform: translateY(110px) scale(1.07); }
}

@keyframes inv-far-out {
    to { transform: translateY(54px); }
}

/* ================= keyframes: signature loops ================= */

@keyframes inv-ken-burns {
    from { transform: scale(1) translate(0, 0); }
    to { transform: scale(1.09) translate(-1.6%, 1.2%); }
}

@keyframes inv-rays {
    to { transform: rotate(360deg); }
}

@keyframes inv-sheen {
    0%, 55% { transform: translateX(-100%); }
    100% { transform: translateX(100%); }
}

@keyframes inv-breathe {
    0%, 100% { scale: 1; }
    50% { scale: 1.06; }
}

@keyframes inv-sway-soft {
    0%, 100% { rotate: -3deg; }
    50% { rotate: 3deg; }
}

@keyframes inv-shine {
    0%, 55% { transform: translateX(-120%); }
    100% { transform: translateX(120%); }
}

@keyframes inv-aurora {
    0%, 100% { transform: translateX(-6%) skewX(-8deg); opacity: .55; }
    50% { transform: translateX(6%) skewX(6deg); opacity: 1; }
}

@keyframes inv-pulse {
    0%, 100% { opacity: .62; }
    50% { opacity: 1; }
}

@keyframes inv-spin {
    to { transform: rotate(360deg); }
}

/* A strip of waves or clouds that tiles: it slides by the width of one tile (--pv-tile) and starts again. */
@keyframes inv-slide {
    to { transform: translateX(var(--pv-tile)); }
}

/* ================= keyframes: the dust ================= */

/* The dust layer moves by one screen (half its height) and starts again. */
@keyframes inv-dust {
    from { transform: translateY(var(--pv-dust-from)); }
    to { transform: translateY(var(--pv-dust-to)); }
}

@keyframes inv-dust-twinkle {
    0%, 100% { opacity: var(--pv-dust-opacity); }
    50% { opacity: calc(var(--pv-dust-opacity) * .3); }
}

/* ================= keyframes: particles (--amb-anim) ================= */

/* Petals: they fall, turning, pushed sideways by the wind. */
@keyframes inv-fall {
    0% { transform: translate(0, 0) rotate(0deg); }
    50% { transform: translate(18px, 55cqh) rotate(160deg); }
    100% { transform: translate(-12px, 118cqh) rotate(320deg); }
}

@keyframes inv-petal {
    0% { transform: translate(0, -6cqh) rotate(0deg); opacity: 0; }
    8% { opacity: var(--amb-opacity, .7); }
    30% { transform: translate(26px, 28cqh) rotate(140deg); }
    55% { transform: translate(-16px, 58cqh) rotate(250deg); }
    80% { transform: translate(20px, 88cqh) rotate(380deg); }
    100% { transform: translate(-6px, 114cqh) rotate(460deg); opacity: 0; }
}

/* Confetti: it falls tumbling, and flashes as it turns edge-on. */
@keyframes inv-confetti {
    0% { transform: translate(0, -8cqh) rotate(0deg) scaleX(1); }
    25% { transform: translate(14px, 24cqh) rotate(200deg) scaleX(.25); }
    50% { transform: translate(-16px, 56cqh) rotate(380deg) scaleX(1); }
    75% { transform: translate(12px, 86cqh) rotate(560deg) scaleX(.25); }
    100% { transform: translate(-8px, 118cqh) rotate(720deg) scaleX(1); }
}

/* Snow: it falls and swings. */
@keyframes inv-snow {
    0% { transform: translate(0, -6cqh); }
    25% { transform: translate(16px, 24cqh); }
    50% { transform: translate(-10px, 56cqh); }
    75% { transform: translate(14px, 88cqh); }
    100% { transform: translate(0, 122cqh); }
}

/* Balloons, lanterns: they rise straight up. */
@keyframes inv-rise {
    0% { transform: translateY(120cqh); }
    100% { transform: translateY(-20cqh); }
}

/* Bubbles, sparks: they rise from below and swing. */
@keyframes inv-bubble {
    0% { transform: translate(0, 112cqh); opacity: 0; }
    10% { opacity: var(--amb-opacity, .7); }
    35% { transform: translate(14px, 70cqh); }
    65% { transform: translate(-12px, 30cqh); }
    90% { opacity: var(--amb-opacity, .7); }
    100% { transform: translate(8px, -14cqh); opacity: 0; }
}

/* Stars that twinkle in place (top: var(--amb-y)). */
@keyframes inv-twinkle {
    0%, 100% { opacity: .15; transform: scale(.7); }
    50% { opacity: var(--amb-opacity, .8); transform: scale(1); }
}

/* A sparkle: it flashes, turning, and goes out. */
@keyframes inv-sparkle {
    0%, 100% { opacity: 0; transform: scale(.3) rotate(0deg); }
    40% { opacity: var(--amb-opacity, .8); transform: scale(1) rotate(45deg); }
    60% { opacity: var(--amb-opacity, .8); transform: scale(1.1) rotate(60deg); }
}

/* A firefly: it wanders, goes dark, lights up somewhere else. */
@keyframes inv-firefly {
    0% { transform: translate(0, 0); opacity: 0; }
    10% { opacity: var(--amb-opacity, .7); }
    24% { transform: translate(22px, -16px); opacity: .25; }
    38% { transform: translate(40px, 6px); opacity: var(--amb-opacity, .7); }
    55% { transform: translate(12px, 26px); opacity: .2; }
    70% { transform: translate(-22px, 8px); opacity: var(--amb-opacity, .7); }
    86% { transform: translate(-30px, -20px); opacity: .3; }
    100% { transform: translate(0, 0); opacity: 0; }
}

/* Bokeh, dust, sparks: a light that drifts up and fades. */
@keyframes inv-bokeh {
    0% { transform: translate(0, 0) scale(.9); opacity: 0; }
    14% { opacity: var(--amb-opacity, .7); }
    50% { transform: translate(16px, -9cqh) scale(1.12); }
    86% { opacity: var(--amb-opacity, .7); }
    100% { transform: translate(-10px, -19cqh) scale(.92); opacity: 0; }
}

/* Leaves and pampas: they sway in place. */
@keyframes inv-sway {
    0%, 100% { transform: rotate(-6deg); }
    50% { transform: rotate(6deg); }
}

/* Birds: they cross the sky. */
@keyframes inv-glide {
    0% { transform: translate(-60cqw, 0); }
    50% { transform: translate(10cqw, -3cqh); }
    100% { transform: translate(110cqw, 2cqh); }
}

/* Seeds on the wind: sideways, a little down, turning. */
@keyframes inv-breeze {
    0% { transform: translate(-30cqw, 0) rotate(0deg); opacity: 0; }
    12% { opacity: var(--amb-opacity, .7); }
    50% { transform: translate(30cqw, 9cqh) rotate(190deg); }
    88% { opacity: var(--amb-opacity, .7); }
    100% { transform: translate(96cqw, -4cqh) rotate(380deg); opacity: 0; }
}

/* Fireworks: they open and fade. */
@keyframes inv-burst {
    0% { transform: scale(.08); opacity: 0; }
    10% { opacity: 1; }
    45% { transform: scale(1); opacity: .9; }
    100% { transform: scale(1.14); opacity: 0; }
}

/* A shooting star: a fast streak, then a long dark. */
@keyframes inv-shoot {
    0% { transform: translate(0, 0) rotate(152deg) scaleX(.2); opacity: 0; }
    3% { opacity: 1; }
    16% { transform: translate(-64cqw, 34cqh) rotate(152deg) scaleX(1); opacity: 0; }
    100% { transform: translate(-64cqw, 34cqh) rotate(152deg) scaleX(1); opacity: 0; }
}

/* A butterfly's path: it never goes dark, and it leaves "scale" to the beating of the wings (inv-flap). */
@keyframes inv-flutter {
    0%, 100% { translate: 0 0; rotate: -8deg; }
    20% { translate: 9cqw -6cqh; rotate: 10deg; }
    40% { translate: 21cqw 2cqh; rotate: -4deg; }
    60% { translate: 9cqw 9cqh; rotate: 12deg; }
    80% { translate: -8cqw 3cqh; rotate: -12deg; }
}

@keyframes inv-flap {
    0%, 100% { scale: 1 1; }
    50% { scale: .25 1; }
}
