/**
 * InviteMotion Renderer Styles
 * Lightweight styles for the invitation viewer
 */

.im-renderer {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #000;
    overflow: hidden;
    position: relative;
}

/* Stage — the invitation canvas.
   JS refines this to exact pixels via ResizeObserver, but these rules must
   stand alone: if the container ever reports a zero size (unlaid-out element,
   admin-bar CSS breaking the html/body height chain, etc.) the JS sizing bails
   and the stage would otherwise collapse to nothing — a black screen with no
   error. --im-stage-ratio is set inline per invitation. */
.im-stage {
    position: relative;
    height: 100%;
    width: auto;
    aspect-ratio: var(--im-stage-ratio, 1080 / 1920);
    max-width: 100%;
    max-height: 100%;
    overflow: hidden;
    margin: 0 auto;
    background: #0a0a0a;

    /* Establishes the container for cqw units, which is how element font sizes
       scale with the stage instead of staying at fixed design pixels.
       Deliberately inline-size (not size): it only contains the inline axis, so
       it can't reintroduce the zero-height collapse that blanked the viewer. */
    container-type: inline-size;
    container-name: im-stage;
}

/* If the viewport is too narrow for the height-driven width, flip to
   width-driven so the invitation still fits without distorting. */
@supports (aspect-ratio: 1 / 1) {
    .im-stage.im-fit-width {
        width: 100%;
        height: auto;
    }
}

/* Responsive: on mobile fill the viewport */
@media (max-width: 600px) {
    .im-controls { opacity: 1; padding: 12px 16px; }
    .im-ctrl { width: 40px; height: 40px; }
}

/* Play Again control — shown once the final summary slide is reached */
.im-play-again {
    position: absolute;
    /* The closing slide reserves the bottom 15% for this, so it sits clear of
       the action buttons rather than on top of them. */
    bottom: 4%;
    left: 50%;
    transform: translateX(-50%) translateY(8px);
    z-index: 60;
    display: none;
    align-items: center;
    gap: 8px;
    padding: 12px 24px;
    border-radius: 30px;
    border: none;
    background: rgba(255,255,255,0.15);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    color: #fff;
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.3px;
    cursor: pointer;
    opacity: 0;
    animation: im-play-again-in 0.5s ease forwards;
    transition: background 0.2s;
}

.im-play-again:hover {
    background: rgba(255,255,255,0.25);
}

@keyframes im-play-again-in {
    from { opacity: 0; transform: translateX(-50%) translateY(8px); }
    to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

.im-slide-container {
    position: absolute;
    inset: 0;
    overflow: hidden;
}

/* Slide */
.im-slide {
    position: absolute;
    inset: 0;
    overflow: hidden;
}

/* Elements */
.im-element {
    position: absolute;
    pointer-events: none;
}

/* Interactive elements opt back in and must clear the controls strip */
.im-el-rsvp,
.im-el-gift,
.im-el-video {
    pointer-events: auto;
    z-index: 60;
}

.im-el-text {
    line-height: 1.3;
    /* Long words (URLs, long names) must break rather than spill outside the
       element box and run into neighbouring text. */
    overflow-wrap: break-word;
    word-wrap: break-word;
    word-break: break-word;
    hyphens: auto;
    white-space: pre-wrap;
}

.im-el-image img {
    display: block;
    pointer-events: none;
    user-select: none;
    -webkit-user-drag: none;
}

.im-el-shape {
    pointer-events: none;
}

.im-divider-line {
    margin-top: 50%;
}

/* Playback Controls */
.im-controls {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 16px 20px;
    background: linear-gradient(transparent, rgba(0,0,0,0.6));
    opacity: 0;
    transition: opacity 0.3s ease;
    /* An invisible bar still swallows clicks. Without this, any button placed
       low on a slide (RSVP, Maps, Calendar all default near the bottom) was
       unclickable because this strip sat on top of it at z-index 50. */
    pointer-events: none;
}

/* Only the controls themselves should take clicks, not the whole strip */
.im-ctrl,
.im-progress,
.im-dot {
    pointer-events: auto;
}

.im-renderer:hover .im-controls,
.im-stage:hover .im-controls {
    opacity: 1;
}

.im-ctrl {
    background: rgba(255,255,255,0.15);
    border: none;
    color: #fff;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    cursor: pointer;
    font-size: 18px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.2s;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}

.im-ctrl:hover {
    background: rgba(255,255,255,0.3);
}

.im-ctrl-mute {
    position: absolute;
    right: 16px;
    font-size: 14px;
}

.im-ctrl-mute.muted {
    opacity: 0.5;
}

/* Progress Dots */
.im-progress {
    display: flex;
    gap: 6px;
    align-items: center;
}

.im-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: rgba(255,255,255,0.3);
    cursor: pointer;
    transition: all 0.3s ease;
}

.im-dot.active {
    background: #fff;
    transform: scale(1.3);
}

.im-dot:hover {
    background: rgba(255,255,255,0.6);
}

/* Action buttons (RSVP / gifts) — sized for tapping on a phone */
.im-action-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    /* cqw, not px: an absolute floor makes the button huge on the small
       editor preview while looking correct in the full-size viewer. */
    min-height: 7cqw;
    padding: 0 5%;
    border-radius: 999px;
    background: rgba(255,255,255,0.16);
    color: #fff;
    font-family: 'Inter', -apple-system, sans-serif;
    font-size: 3.2cqw;
    font-weight: 600;
    text-decoration: none;
    letter-spacing: 0.02em;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid rgba(255,255,255,0.22);
    text-align: center;
    line-height: 1.2;
    transition: transform 0.12s ease, filter 0.12s ease;
}
.im-action-btn:active { transform: scale(0.97); }

/* ─── Guest farewell bubble ─────────────────────────────────────────
   Floated over the closing slide, never part of its layout. That slide's
   spacing is measured to the percent, so an inline row would push the
   buttons into the Play Again band. */
.im-guest-bubble {
    position: absolute;
    left: 50%;
    top: 4.5%;
    transform: translateX(-50%);
    z-index: 70;
    max-width: 80%;
    padding: 2.2cqw 4cqw;
    border-radius: 999px;
    background: rgba(255,255,255,0.16);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border: 1px solid rgba(255,255,255,0.22);
    color: #fff;
    font-family: 'Inter', -apple-system, sans-serif;
    font-size: 3.1cqw;
    font-weight: 600;
    text-align: center;
    line-height: 1.3;
    pointer-events: none;
    box-shadow: 0 4px 18px rgba(0,0,0,0.25);
    animation: im-bubble-in 0.6s cubic-bezier(0.34, 1.4, 0.64, 1) 0.5s both;
}
@keyframes im-bubble-in {
    from { opacity: 0; transform: translateX(-50%) translateY(-10px) scale(0.94); }
    to   { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
    .im-guest-bubble { animation: none; }
}

/* ─── Countdown ─────────────────────────────────────────────────── */
.im-countdown {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4cqw;
    width: 100%;
    height: 100%;
    font-family: 'Inter', -apple-system, sans-serif;
    color: #fff;
    text-align: center;
}
.im-cd-unit { display: flex; flex-direction: column; align-items: center; gap: 0.4cqw; }
.im-cd-num {
    font-size: 1.9em;
    /* inherit so the element's own weight setting takes effect */
    font-weight: inherit;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    /* Fixed width so the layout doesn't jitter as digits change */
    min-width: 2ch;
}
.im-cd-label {
    font-size: 0.62em;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    opacity: 0.7;
    line-height: 1;
}
.im-cd-now { font-size: 1.4em; font-weight: 700; letter-spacing: 0.02em; }

/* Round trigger — the payment options live in a sheet behind it */
.im-gift-fab {
    display: flex;
    align-items: center;
    justify-content: center;
    /* Dimensions come from the renderer, which sizes the circle from the
       smaller side of the element box. aspect-ratio here would override that
       and let a wide box produce an oversized circle. */
    width: 100%;
    height: 100%;
    margin: auto;
    border-radius: 50%;
    border: 1px solid rgba(255,255,255,0.22);
    background: rgba(255,255,255,0.16);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    color: #fff;
    font-family: 'Inter', -apple-system, sans-serif;
    font-size: 5cqw;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    transition: transform 0.12s ease, filter 0.12s ease;
}
.im-gift-fab:hover { filter: brightness(1.12); }
.im-gift-fab:active { transform: scale(0.94); }

/* Coin-like glare.
   The sweep lives on a pseudo-element clipped to the circle, so the button
   keeps its blurred translucent fill rather than being painted over with a
   gradient. The symbol sits above the sweep. */
.im-gift-fab {
    position: relative;
    overflow: hidden;
    isolation: isolate;
}
.im-gift-fab::before {
    content: '';
    position: absolute;
    top: -60%;
    left: -120%;
    width: 60%;
    height: 220%;
    background: linear-gradient(
        100deg,
        rgba(255,255,255,0) 0%,
        rgba(255,255,255,0.55) 45%,
        rgba(255,255,255,0.85) 50%,
        rgba(255,255,255,0.55) 55%,
        rgba(255,255,255,0) 100%
    );
    transform: rotate(18deg);
    pointer-events: none;
    z-index: 1;
    animation: im-coin-glare 3.4s ease-in-out infinite;
}
/* A faint rim catch, so it reads as metal rather than glass */
.im-gift-fab::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 50%;
    pointer-events: none;
    z-index: 0;
    background: radial-gradient(
        circle at 32% 28%,
        rgba(255,255,255,0.28) 0%,
        rgba(255,255,255,0.06) 38%,
        rgba(0,0,0,0.10) 100%
    );
}
.im-gift-fab-glyph { position: relative; z-index: 2; }

@keyframes im-coin-glare {
    0%   { left: -120%; }
    /* Long pause between sweeps, so it catches the eye without nagging */
    55%  { left: 160%; }
    100% { left: 160%; }
}

@media (prefers-reduced-motion: reduce) {
    .im-gift-fab::before { animation: none; opacity: 0; }
}

/* Sheet with the payment options */
.im-gift-sheet {
    position: absolute;
    inset: 0;
    z-index: 200;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2.2cqw;
    padding: 7cqw;
    background: rgba(8,8,12,0.84);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    font-family: 'Inter', -apple-system, sans-serif;
}
.im-gift-sheet .im-gift-btn { width: 76%; }
.im-gift-sheet-title {
    color: #fff;
    font-size: 4.2cqw;
    font-weight: 700;
    letter-spacing: 0.01em;
    margin-bottom: 1cqw;
    text-align: center;
}
.im-gift-sheet-footer {
    color: rgba(255,255,255,0.7);
    font-size: 3cqw;
    margin-top: 1.5cqw;
    text-align: center;
}
.im-gift-sheet-close {
    background: transparent !important;
    border: 1px solid rgba(255,255,255,0.16);
    opacity: 0.75;
}

.im-gift-wrap {
    display: flex;
    flex-direction: column;
    gap: 2.2cqw;
    width: 100%;
    height: 100%;
    align-items: stretch;
    justify-content: center;
}

.im-gift-heading {
    text-align: center;
    color: #fff;
    font-family: 'Inter', -apple-system, sans-serif;
    font-size: 3.4cqw;
    font-weight: 600;
    margin-bottom: 0.6cqw;
}

.im-gift-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2.6cqw 3.4cqw;
    border-radius: 999px;
    border: none;
    color: #fff;
    font-family: 'Inter', -apple-system, sans-serif;
    font-size: 3cqw;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    text-align: center;
    line-height: 1.2;
    min-height: 6cqw;
    transition: transform 0.12s ease, filter 0.12s ease;
}
.im-gift-btn:hover { filter: brightness(1.1); }
.im-gift-btn:active { transform: scale(0.97); }

.im-gift-copy { font-variant-numeric: tabular-nums; }

.im-gift-empty {
    text-align: center;
    color: rgba(255,255,255,0.55);
    font-family: 'Inter', -apple-system, sans-serif;
    font-size: 2.8cqw;
    padding: 2cqw;
}

/* Calendar destination chooser */
.im-cal-sheet {
    position: absolute;
    inset: 0;
    z-index: 200;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2.2cqw;
    padding: 6cqw;
    background: rgba(8,8,12,0.82);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    font-family: 'Inter', -apple-system, sans-serif;
}
.im-cal-sheet-title {
    color: #fff;
    font-size: 3.4cqw;
    font-weight: 600;
    margin-bottom: 1cqw;
}
.im-cal-sheet-btn {
    width: 74%;
    padding: 3cqw;
    border: 1px solid rgba(255,255,255,0.22);
    border-radius: 999px;
    background: rgba(255,255,255,0.16);
    color: #fff;
    font-family: inherit;
    font-size: 3.1cqw;
    font-weight: 600;
    cursor: pointer;
}
.im-cal-sheet-btn:hover { background: rgba(255,255,255,0.26); }
.im-cal-sheet-cancel { background: transparent; border-color: rgba(255,255,255,0.12); opacity: 0.75; }

/* Fallback message shown in-stage when a scene can't be rendered */
.im-message {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    text-align: center;
    color: rgba(255,255,255,0.7);
    font-family: 'Inter', -apple-system, sans-serif;
    font-size: 14px;
    line-height: 1.6;
}

/* Embed container */
.im-invitation-embed {
    width: 100%;
    max-width: 500px;
    aspect-ratio: 9/16;
    margin: 0 auto;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 20px 60px rgba(0,0,0,0.4);
    background: #0a0a0a;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Start button shown before an embedded invitation begins playing */
.im-embed-start {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 14px 28px;
    border-radius: 30px;
    border: 1px solid rgba(255,255,255,0.25);
    background: rgba(255,255,255,0.12);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    color: #fff;
    font-family: 'Inter', -apple-system, sans-serif;
    font-size: 15px;
    font-weight: 600;
    letter-spacing: 0.3px;
    cursor: pointer;
    transition: background 0.2s, transform 0.15s;
}

.im-embed-start:hover {
    background: rgba(255,255,255,0.22);
    transform: translateY(-1px);
}


/* ═══════════════════════════════════════════════════════════════════
   Envelope opening screen

   Drawn entirely in CSS and SVG rather than with images: it stays sharp
   at any size, recolours from two variables, and adds nothing to load.
   Every shade is derived from --env-paper and --env-seal with layered
   gradients, so changing one colour restyles the whole thing coherently.
   ═══════════════════════════════════════════════════════════════════ */
.im-env {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    gap: 26px;
    /* Depth for the flap rotation; without it the flap only squashes */
    perspective: 1400px;
    perspective-origin: 50% 40%;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    container-type: inline-size;
}

.im-env-stage {
    position: relative;
    width: min(76vw, 330px);
    aspect-ratio: 3 / 4.2;
    transform-style: preserve-3d;
    transition: transform 0.9s cubic-bezier(0.4, 0, 0.2, 1),
                opacity 0.6s ease 0.55s;
    animation: im-env-idle 5s ease-in-out infinite;
}

/* The paper: base tint, a sheen from the top left, and a vignette so the
   lower edges sit back */
.im-env-body {
    position: absolute;
    inset: 0;
    border-radius: 4px;
    background:
        radial-gradient(120% 90% at 28% 18%,
            rgba(255,255,255,0.55) 0%, rgba(255,255,255,0) 55%),
        radial-gradient(140% 120% at 50% 110%,
            rgba(0,0,0,0.22) 0%, rgba(0,0,0,0) 60%),
        var(--env-paper, #f2e6dc);
    box-shadow:
        0 30px 60px -18px rgba(0,0,0,0.5),
        0 6px 16px rgba(0,0,0,0.22),
        inset 0 0 0 1px rgba(255,255,255,0.28);
    overflow: hidden;
}

/* Embossed motif. Two copies a pixel apart, one lightening and one
   darkening, read as raised relief rather than a printed pattern. */
.im-env-pattern, .im-env-pattern::after {
    position: absolute;
    inset: 0;
    background-repeat: repeat;
    /* Dense enough to read as an all-over damask rather than scattered
       motifs. Percentages keep the density constant at any envelope size. */
    background-size: 25% auto;
}
.im-env-pattern { opacity: 0.55; mix-blend-mode: multiply; }
.im-env-pattern::after {
    content: '';
    /* Offset by a hair, lightening where the shadow copy does not reach.
       The pair is what makes it read as pressed rather than printed. */
    transform: translate(-0.8px, -1px);
    opacity: 0.7;
    mix-blend-mode: screen;
}

/* The inside of the envelope, revealed as the flap swings away. Without
   this the opened flap exposed the same paper as the front, which read as
   a fold rather than an opening. */
.im-env-liner {
    position: absolute;
    left: 0; top: 0;
    width: 100%; height: 52%;
    clip-path: polygon(0 0, 100% 0, 50% 100%);
    background:
        linear-gradient(180deg,
            rgba(0,0,0,0.62) 0%, rgba(0,0,0,0.34) 55%, rgba(0,0,0,0.12) 100%),
        var(--env-paper, #f2e6dc);
    z-index: 1;
}

/* The flaps of an envelope back, cut so the folds meet at the centre */
.im-env-flap { position: absolute; inset: 0; backface-visibility: hidden; }
.im-env-flap-l {
    clip-path: polygon(0 0, 50% 50%, 0 100%);
    background: linear-gradient(100deg,
        rgba(0,0,0,0.11) 0%, rgba(0,0,0,0.02) 60%, rgba(255,255,255,0.10) 100%);
}
.im-env-flap-r {
    clip-path: polygon(100% 0, 50% 50%, 100% 100%);
    background: linear-gradient(260deg,
        rgba(0,0,0,0.11) 0%, rgba(0,0,0,0.02) 60%, rgba(255,255,255,0.10) 100%);
}
.im-env-flap-b {
    clip-path: polygon(0 100%, 50% 50%, 100% 100%);
    background: linear-gradient(0deg,
        rgba(0,0,0,0.17) 0%, rgba(0,0,0,0.03) 70%, rgba(255,255,255,0.06) 100%);
}
.im-env-folds { position: absolute; inset: 0; pointer-events: none; opacity: 0.45; }

/* The top flap opens. Its origin is the top edge so it swings away like
   a real one. */
.im-env-flap-t {
    position: absolute;
    left: 0; top: 0;
    width: 100%; height: 52%;
    transform-origin: 50% 0%;
    transform: rotateX(0deg);
    transition: transform 0.95s cubic-bezier(0.55, 0.06, 0.24, 1);
    transform-style: preserve-3d;
    z-index: 3;
}
.im-env-flap-face, .im-env-flap-back {
    position: absolute;
    inset: 0;
    clip-path: polygon(0 0, 100% 0, 50% 100%);
    backface-visibility: hidden;
}
.im-env-flap-face {
    background:
        radial-gradient(90% 130% at 50% -10%,
            rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 60%),
        linear-gradient(180deg, rgba(0,0,0,0) 40%, rgba(0,0,0,0.15) 100%),
        var(--env-paper, #f2e6dc);
}
/* The underside, seen once it swings past 90 degrees */
.im-env-flap-back {
    background:
        linear-gradient(0deg, rgba(0,0,0,0.26) 0%, rgba(0,0,0,0.06) 100%),
        var(--env-paper, #f2e6dc);
    transform: rotateX(180deg);
}

/* Wax seal */
.im-env-seal {
    position: absolute;
    left: 50%; top: 50%;
    width: 27%;
    aspect-ratio: 1;
    transform: translate(-50%, -50%);
    z-index: 5;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.5s cubic-bezier(0.34, 1.5, 0.64, 1), opacity 0.4s ease;
    filter: drop-shadow(0 3px 7px rgba(0,0,0,0.42));
}
.im-env-seal-disc {
    position: absolute;
    inset: 0;
    background:
        radial-gradient(circle at 34% 28%,
            rgba(255,255,255,0.45) 0%, rgba(255,255,255,0.08) 38%, rgba(0,0,0,0.10) 70%),
        radial-gradient(circle at 62% 78%,
            rgba(0,0,0,0.30) 0%, rgba(0,0,0,0) 55%),
        var(--env-seal, #b4553a);
    /* Scalloped rim, the way pressed wax spreads unevenly */
    clip-path: polygon(
        50% 0%, 62% 4%, 74% 3%, 82% 12%, 93% 18%, 95% 30%, 100% 41%,
        96% 52%, 99% 64%, 91% 72%, 88% 84%, 76% 88%, 66% 96%, 54% 94%,
        43% 99%, 33% 92%, 21% 90%, 14% 81%, 4% 74%, 3% 62%, 0% 50%,
        4% 38%, 1% 26%, 11% 19%, 15% 8%, 27% 6%, 38% 1%);
    box-shadow: inset 0 2px 4px rgba(255,255,255,0.30);
}
/* An inner ring, pressed by the stamp edge */
.im-env-seal-disc::after {
    content: '';
    position: absolute;
    inset: 13%;
    border-radius: 50%;
    box-shadow:
        inset 0 2px 5px rgba(0,0,0,0.38),
        inset 0 -1px 2px rgba(255,255,255,0.22);
}
.im-env-seal-initials {
    position: relative;
    z-index: 2;
    font-family: 'Cinzel', 'Playfair Display', serif;
    font-weight: 700;
    font-size: 5cqw;
    letter-spacing: 0.05em;
    color: rgba(255,255,255,0.88);
    /* Pressed into the wax rather than printed onto it */
    text-shadow: 0 1px 0 rgba(255,255,255,0.26), 0 -1px 2px rgba(0,0,0,0.55);
    mix-blend-mode: overlay;
    pointer-events: none;
}
/* A logo pressed into the wax.
   The PNG is used as a MASK, not as an image — only its transparency
   matters, so the artwork's own colours are irrelevant. A black logo, a
   white one and a full-colour one all come out as the same relief. Blending
   the image directly meant a dark logo on dark wax simply vanished. */
.im-env-seal-logo {
    position: relative;
    z-index: 2;
    width: 58%; height: 58%;
    pointer-events: none;
    -webkit-mask-position: center;
            mask-position: center;
    -webkit-mask-size: contain;
            mask-size: contain;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    /* Lit from the top left, matching the seal's own highlight */
    background-image: linear-gradient(
        160deg,
        rgba(255,255,255,0.55) 0%,
        rgba(255,255,255,0.16) 34%,
        rgba(0,0,0,0.34) 70%,
        rgba(0,0,0,0.52) 100%);
    filter:
        drop-shadow(0 1px 0 rgba(255,255,255,0.30))
        drop-shadow(0 -1px 1.5px rgba(0,0,0,0.55));
}

.im-env-hint {
    font-family: 'Inter', -apple-system, sans-serif;
    font-size: 12px;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: rgba(255,255,255,0.72);
    animation: im-env-pulse 2.6s ease-in-out infinite;
}
.im-env-title {
    font-family: 'Playfair Display', serif;
    font-size: 20px;
    color: #fff;
    text-align: center;
    margin-bottom: -8px;
    padding: 0 24px;
}
.im-env-greeting {
    font-family: 'Inter', sans-serif;
    font-size: 15px;
    font-weight: 600;
    color: rgba(255,255,255,0.9);
    text-align: center;
}

@keyframes im-env-pulse { 0%, 100% { opacity: 0.45; } 50% { opacity: 1; } }
/* A slow breath before it is touched, so it reads as interactive */
@keyframes im-env-idle {
    0%, 100% { transform: translateY(0) rotateX(0deg); }
    50%      { transform: translateY(-6px) rotateX(1.4deg); }
}

/* ── Opening ────────────────────────────────────────────────────
   Three beats in sequence: the seal releases, the flap swings back,
   then the whole envelope recedes as the invitation begins. The delays
   are what make it read as one motion rather than three at once. */
.im-env.opening .im-env-seal {
    transform: translate(-50%, -50%) scale(1.18) rotate(-9deg);
    opacity: 0;
    transition-delay: 0s;
}
.im-env.opening .im-env-flap-t {
    transform: rotateX(-168deg);
    transition-delay: 0.18s;
}
.im-env.opening .im-env-stage {
    /* One rule, not two: a separate `animation: none` above this was
       matching first and hiding the delay from anything reading the CSS. */
    animation: none;
    transform: translateY(12%) scale(1.9);
    opacity: 0;
    transition-delay: 0.5s;
}
.im-env.opening .im-env-hint,
.im-env.opening .im-env-title,
.im-env.opening .im-env-greeting {
    opacity: 0;
    transition: opacity 0.25s;
    animation: none;
}

@media (prefers-reduced-motion: reduce) {
    .im-env-stage, .im-env-hint { animation: none; }
    .im-env.opening .im-env-flap-t,
    .im-env.opening .im-env-stage { transition-duration: 0.3s; }
}
