/* ===========================================================================
   XNight UI kit styles.

   Companion to xnight-ui.js: icons, confetti canvas, the purchase-confirmation
   dialog and the gift-upgrade sequence. Uses the same custom properties as
   app.html so light and dark themes are inherited rather than duplicated.
   =========================================================================== */

/* ===========================================================================
   Scrollbars.

   The app previously styled only #giftModalItemsGrid, so everywhere else fell
   back to the platform default — a wide grey bar on Windows that clashed with
   the dark theme and, on light backgrounds, did not read as part of the UI.

   Approach follows Telegram WebK: the bar is invisible until the container is
   hovered, sits inside the padding box so nothing reflows, and picks up its
   colour from a custom property that follows the theme.
   =========================================================================== */
:root {
    --scrollbar-color: rgba(255, 255, 255, 0.18);
    --scrollbar-color-hover: rgba(255, 255, 255, 0.32);
    --scrollbar-size: 6px;
}

body.light-theme {
    --scrollbar-color: rgba(15, 20, 25, 0.18);
    --scrollbar-color-hover: rgba(15, 20, 25, 0.34);
}

/* Firefox */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--scrollbar-color) transparent;
}

/* WebKit / Blink */
::-webkit-scrollbar {
    width: var(--scrollbar-size);
    height: var(--scrollbar-size);
    background: transparent;
}

::-webkit-scrollbar-track {
    background: transparent;
    border: none;
}

::-webkit-scrollbar-corner {
    background: transparent;
}

::-webkit-scrollbar-button {
    width: 0;
    height: 0;
    display: none;
}

::-webkit-scrollbar-thumb {
    background-color: transparent;
    border-radius: 999px;
    /* Inset the thumb without changing the track width, so content does not
       shift when the thumb appears. */
    border: 1px solid transparent;
    background-clip: padding-box;
    transition: background-color 0.2s ease;
    min-height: 40px;
}

/* Reveal on hover over the scrolling container, and while actively dragging. */
:hover::-webkit-scrollbar-thumb {
    background-color: var(--scrollbar-color);
}

::-webkit-scrollbar-thumb:hover,
::-webkit-scrollbar-thumb:active {
    background-color: var(--scrollbar-color-hover);
}

/* The page itself keeps a permanently visible thumb — a document-level bar that
   only appears on hover is disorienting. */
html::-webkit-scrollbar-thumb,
body::-webkit-scrollbar-thumb {
    background-color: var(--scrollbar-color);
}

/* Horizontal strips (gift rows, tab bars) scroll by drag and swipe, so the bar
   is pure noise there. */
.xnight-no-scrollbar,
.xnight-hscroll {
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.xnight-no-scrollbar::-webkit-scrollbar,
.xnight-hscroll::-webkit-scrollbar {
    width: 0;
    height: 0;
    display: none;
}

/* --- icons --------------------------------------------------------------- */
.xnight-icon {
    display: inline-block;
    vertical-align: middle;
    flex-shrink: 0;
    line-height: 1;
}

/* An icon sitting next to a label in a button or heading. */
.xnight-icon-inline {
    margin-right: 6px;
    transform: translateY(-1px);
}

/* --- confetti ------------------------------------------------------------ */
.xnight-confetti-canvas {
    position: fixed;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    z-index: 2147483600;
}

/* --- shared modal shell -------------------------------------------------- */
.xnight-modal-overlay {
    position: fixed;
    inset: 0;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgba(0, 0, 0, 0.72);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    z-index: 2147483500;
    opacity: 0;
    transition: opacity 0.22s ease;
}

.xnight-modal-overlay.visible {
    display: flex;
    opacity: 1;
}

.xnight-modal-card {
    position: relative;
    width: 100%;
    max-width: 400px;
    box-sizing: border-box;
    background: var(--card-bg, #0c0c0e);
    border: 1px solid var(--border-color, #161616);
    border-radius: 26px;
    padding: 26px 24px 22px;
    box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
    text-align: center;
    transform: translateY(14px) scale(0.97);
    opacity: 0;
    transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.22s ease;
    max-height: calc(100vh - 40px);
    overflow-y: auto;
}

.xnight-modal-overlay.visible .xnight-modal-card {
    transform: translateY(0) scale(1);
    opacity: 1;
}

.xnight-modal-close {
    position: absolute;
    top: 14px;
    right: 14px;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 50%;
    background: rgba(128, 128, 128, 0.12);
    color: var(--text-muted, #8b8b8b);
    cursor: pointer;
    transition: background 0.2s ease, color 0.2s ease;
    padding: 0;
}

.xnight-modal-close:hover {
    background: rgba(128, 128, 128, 0.22);
    color: var(--text-main, #fff);
}

/* --- purchase confirmation ---------------------------------------------- */
.xnight-purchase-art {
    position: relative;
    width: 128px;
    height: 128px;
    margin: 6px auto 16px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.xnight-purchase-art img,
.xnight-purchase-art lottie-player,
.xnight-purchase-art .xnight-purchase-anim {
    position: relative;
    z-index: 1;
    width: 112px;
    height: 112px;
    object-fit: contain;
    filter: drop-shadow(0 10px 22px rgba(0, 0, 0, 0.35));
}

/* The animation is mounted into this host once the player exists, so the box
   must not add layout of its own beyond centring what lands inside it. */
.xnight-purchase-art .xnight-purchase-anim {
    display: flex;
    align-items: center;
    justify-content: center;
}

.xnight-purchase-art .xnight-purchase-anim lottie-player {
    filter: none;
}

.xnight-purchase-glow {
    position: absolute;
    inset: -12px;
    border-radius: 50%;
    background: radial-gradient(circle,
            rgba(29, 155, 240, 0.32) 0%,
            rgba(139, 92, 246, 0.16) 45%,
            transparent 72%);
    animation: xnightGlowPulse 3.2s ease-in-out infinite;
}

@keyframes xnightGlowPulse {
    0%, 100% { transform: scale(1);    opacity: 0.85; }
    50%      { transform: scale(1.09); opacity: 1;    }
}

.xnight-purchase-art.art-pop {
    animation: xnightArtPop 0.55s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes xnightArtPop {
    0%   { transform: scale(1);    }
    35%  { transform: scale(1.16); }
    100% { transform: scale(1);    }
}

.xnight-purchase-title {
    font-size: 20px;
    font-weight: 800;
    color: var(--text-main, #fff);
    letter-spacing: -0.3px;
}

.xnight-purchase-sub {
    font-size: 14.5px;
    font-weight: 600;
    color: var(--text-muted, #8b8b8b);
    margin-top: 4px;
}

.xnight-purchase-rows {
    margin: 18px 0 4px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    text-align: left;
}

.xnight-purchase-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 11px 14px;
    border-radius: 13px;
    background: rgba(128, 128, 128, 0.07);
    font-size: 14px;
}

.xnight-purchase-row span {
    color: var(--text-muted, #8b8b8b);
    font-weight: 600;
}

.xnight-purchase-row strong {
    color: var(--text-main, #fff);
    font-weight: 800;
    white-space: nowrap;
}

.xnight-purchase-row strong.accent {
    color: #1d9bf0;
}

.xnight-purchase-success {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 16px 14px;
    border-radius: 14px;
    background: rgba(16, 185, 129, 0.12);
    color: #10b981;
    font-weight: 800;
    font-size: 15px;
    animation: xnightFadeUp 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes xnightFadeUp {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0);   }
}

.xnight-purchase-comment-wrap {
    margin-top: 14px;
}

.xnight-purchase-comment-wrap textarea {
    width: 100%;
    box-sizing: border-box;
    min-height: 62px;
    max-height: 140px;
    resize: vertical;
    padding: 12px 14px;
    border-radius: 14px;
    border: 1px solid var(--border-color, #161616);
    background: var(--input-bg, #121216);
    color: var(--text-main, #fff);
    font-family: inherit;
    font-size: 14px;
    outline: none;
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.xnight-purchase-comment-wrap textarea:focus {
    border-color: #1d9bf0;
    box-shadow: 0 0 0 3px rgba(29, 155, 240, 0.18);
}

.xnight-purchase-error {
    display: none;
    margin-top: 12px;
    padding: 10px 14px;
    border-radius: 12px;
    background: rgba(239, 68, 68, 0.12);
    color: #ef4444;
    font-size: 13.5px;
    font-weight: 700;
    text-align: left;
}

.xnight-purchase-actions {
    display: flex;
    gap: 10px;
    margin-top: 18px;
}

.xnight-btn-primary,
.xnight-btn-ghost {
    flex: 1;
    padding: 13px 18px;
    border-radius: 15px;
    font-size: 15px;
    font-weight: 800;
    font-family: inherit;
    cursor: pointer;
    border: none;
    transition: transform 0.18s ease, opacity 0.2s ease, background 0.2s ease;
}

.xnight-btn-primary {
    background: linear-gradient(135deg, #1d9bf0, #0044ff);
    color: #fff;
    box-shadow: 0 8px 22px rgba(29, 155, 240, 0.28);
}

.xnight-btn-primary:hover:not(:disabled) {
    transform: translateY(-1px);
}

.xnight-btn-primary:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    box-shadow: none;
}

.xnight-btn-ghost {
    flex: 0 0 auto;
    padding: 13px 22px;
    background: rgba(128, 128, 128, 0.12);
    color: var(--text-main, #fff);
}

.xnight-btn-ghost:hover {
    background: rgba(128, 128, 128, 0.2);
}

/* --- gift transfer -------------------------------------------------------
   After transferStarGift.module.scss in webk: the collectible, an arrow, and
   the recipient's avatar, then a sentence and the attribute table. */
.xnight-transfer-card {
    max-width: 360px;
    text-align: center;
}

.xnight-transfer-graph {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    margin-top: 6px;
}

/* Clips the painted backdrop, which is a canvas and would otherwise square off
   the corners. */
.xnight-transfer-gift {
    position: relative;
    width: 60px;
    height: 60px;
    border-radius: 12px;
    overflow: hidden;
    flex-shrink: 0;
}

.xnight-transfer-gift-backdrop {
    position: absolute;
    inset: 0;
}

.xnight-transfer-gift-backdrop .xnight-gift-backdrop {
    width: 100%;
    height: 100%;
    display: block;
}

/* The model sits inside the 60px box with a 6px margin, so the backdrop stays
   visible around it. */
.xnight-transfer-gift-art {
    position: absolute;
    top: 6px;
    left: 6px;
    width: 48px;
    height: 48px;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
}

.xnight-transfer-gift-art img,
.xnight-transfer-gift-art lottie-player {
    width: 48px;
    height: 48px;
    object-fit: contain;
}

.xnight-transfer-arrow {
    display: flex;
    align-items: center;
    color: var(--text-muted, #8b8b8b);
    opacity: 0.4;
}

.xnight-transfer-peer {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.xnight-transfer-text {
    margin-top: 20px;
    padding: 0 6px;
    font-size: 15px;
    line-height: 21px;
    color: var(--text-muted, #8b8b8b);
    text-align: center;
    text-wrap: balance;
}

.xnight-transfer-text strong {
    color: var(--text-main, #fff);
    font-weight: 700;
}

.xnight-transfer-table {
    margin-top: 14px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    text-align: left;
}

.xnight-transfer-table:empty {
    display: none;
}

/* --- gift upgrade ------------------------------------------------------- */
.xnight-upgrade-card {
    max-width: 440px;
}

.xnight-upgrade-stage {
    position: relative;
    height: 180px;
    margin: 8px auto 20px;
    width: 100%;
    max-width: 360px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border-radius: 24px;
    border: 1px solid rgba(255, 255, 255, 0.18);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35), inset 0 1px 1px rgba(255, 255, 255, 0.18);
    background: rgba(15, 23, 42, 0.6);
    transition: border-color 0.3s ease, box-shadow 0.3s ease;
}

.xnight-upgrade-stage.revealed-stage {
    border-color: rgba(255, 255, 255, 0.32);
    box-shadow: 0 14px 38px rgba(0, 0, 0, 0.45), 0 0 24px rgba(29, 155, 240, 0.25), inset 0 1px 1px rgba(255, 255, 255, 0.25);
}

/* Registered so the browser interpolates the colours instead of swapping them.
   Unregistered custom properties are strings and cannot be transitioned. */
@property --xnight-backdrop-center-color {
    syntax: '<color>';
    inherits: true;
    initial-value: #1d4ed8;
}

@property --xnight-backdrop-edge-color {
    syntax: '<color>';
    inherits: true;
    initial-value: #0b1e4d;
}

.xnight-upgrade-backdrop {
    position: absolute;
    inset: 0;
    overflow: hidden;
    opacity: 0;
    border-radius: inherit;
    background-color: var(--xnight-backdrop-edge-color);
    transition: background-color 0.5s ease-in-out, opacity 0.35s ease;
    pointer-events: none;
}

.xnight-upgrade-stage.previewing-stage .xnight-upgrade-backdrop,
.xnight-upgrade-stage.spinning-stage .xnight-upgrade-backdrop,
.xnight-upgrade-stage.revealed-stage .xnight-upgrade-backdrop {
    opacity: 1;
}

/* The bright core of the backdrop: an oversized radial gradient anchored above
   centre, so the falloff reaches the edges of a wide stage. */
.xnight-backdrop-halo {
    position: absolute;
    left: 50%;
    top: -100px;
    width: 400px;
    height: 400px;
    transform: translateX(-50%);
    background: radial-gradient(var(--xnight-backdrop-center-color) 0%,
            var(--xnight-backdrop-edge-color) 50%);
    transition: --xnight-backdrop-center-color 0.5s ease-in-out,
        --xnight-backdrop-edge-color 0.5s ease-in-out;
}

.xnight-backdrop-halo.is-small {
    width: 200px;
    height: 200px;
}

/* The tiled pattern sits over the gradient and cross-fades when it changes. */
.xnight-backdrop-pattern {
    position: absolute;
    top: 0;
    left: 50%;
    z-index: 1;
    transform: translateX(-50%) scale(1);
    opacity: 1;
    transition: opacity 0.25s ease-in-out, transform 0.25s ease-in-out;
    will-change: transform, opacity;
    pointer-events: none;
}

/* Entering and leaving states for the cross-fade. */
.xnight-backdrop-pattern.is-entering,
.xnight-backdrop-pattern.is-leaving {
    opacity: 0;
    transform: translateX(-50%) scale(0.9);
}

/* The preview shown before confirming: one random model at a time over its
   random backdrop, cycling when the animation ends. Ported from webk's
   createStarGiftUpgradePopup, where this stands in for a reel entirely. */
.xnight-upgrade-preview {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: none;
    align-items: center;
    justify-content: center;
}

.xnight-upgrade-stage.previewing-stage .xnight-upgrade-preview {
    display: flex;
}

.xnight-upgrade-preview img,
.xnight-upgrade-preview lottie-player {
    width: 132px;
    height: 132px;
    object-fit: contain;
    filter: drop-shadow(0 12px 26px rgba(0, 0, 0, 0.4));
}

.xnight-upgrade-preview .xnight-sticker-skeleton {
    width: 132px;
    height: 132px;
}

/* While previewing, the reel is out of the way; it belongs to the roll. */
.xnight-upgrade-stage.previewing-stage .xnight-upgrade-reel-mask {
    opacity: 0;
}

/* The reel is masked to a viewport with a soft edge, so slots fade in and out
   instead of popping at the boundary. */
.xnight-upgrade-reel-mask {
    position: relative;
    z-index: 1;
    width: 100%;
    height: 132px;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(to right,
            transparent 0%, #000 18%, #000 82%, transparent 100%);
    mask-image: linear-gradient(to right,
            transparent 0%, #000 18%, #000 82%, transparent 100%);
}

.xnight-upgrade-reel {
    display: flex;
    align-items: center;
    height: 100%;
    will-change: transform;
}

.xnight-upgrade-slot {
    width: 132px;
    height: 132px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

.xnight-upgrade-slot img {
    width: 104px;
    height: 104px;
    object-fit: contain;
    filter: drop-shadow(0 8px 18px rgba(0, 0, 0, 0.35));
}

/* Centre marker, visible only while spinning. */
.xnight-upgrade-reel-mask::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 118px;
    height: 118px;
    transform: translate(-50%, -50%);
    border-radius: 24px;
    border: 2px solid rgba(29, 155, 240, 0);
    transition: border-color 0.3s ease;
    pointer-events: none;
}

.xnight-upgrade-stage.spinning-stage .xnight-upgrade-reel-mask::after {
    border-color: rgba(29, 155, 240, 0.4);
}

.xnight-upgrade-result {
    position: absolute;
    inset: 0;
    display: none;
    align-items: center;
    justify-content: center;
    /* Above the backdrop layer so the collectible is shown on its own colour. */
    z-index: 2;
}

.xnight-upgrade-result.revealed {
    display: flex;
    animation: xnightRevealPop 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.xnight-upgrade-result img {
    width: 132px;
    height: 132px;
    object-fit: contain;
    filter: drop-shadow(0 14px 30px rgba(29, 155, 240, 0.35));
}

/* The reveal shows the model's Lottie animation, so the player needs the same
   footprint as the static fallback. */
.xnight-upgrade-result lottie-player {
    width: 132px;
    height: 132px;
    filter: drop-shadow(0 14px 30px rgba(29, 155, 240, 0.35));
}

.xnight-upgrade-result .xnight-sticker-skeleton {
    width: 132px;
    height: 132px;
}

@keyframes xnightRevealPop {
    0%   { opacity: 0; transform: scale(0.55) rotate(-12deg); }
    55%  { opacity: 1; transform: scale(1.14) rotate(3deg);   }
    100% { opacity: 1; transform: scale(1) rotate(0);         }
}

/* Once revealed, the reel is hidden so the two do not overlap. */
.xnight-upgrade-stage.revealed-stage .xnight-upgrade-reel-mask {
    opacity: 0;
    transition: opacity 0.3s ease;
}

.xnight-upgrade-title {
    font-size: 21px;
    font-weight: 800;
    color: var(--text-main, #fff);
    letter-spacing: -0.3px;
}

.xnight-upgrade-sub {
    font-size: 14px;
    font-weight: 600;
    color: var(--text-muted, #8b8b8b);
    margin-top: 6px;
    line-height: 1.45;
}

.xnight-upgrade-attrs {
    margin-top: 18px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    text-align: left;
}

.xnight-upgrade-attrs .xnight-purchase-row {
    animation: xnightFadeUp 0.4s cubic-bezier(0.16, 1, 0.3, 1) backwards;
}

.xnight-upgrade-attrs .xnight-purchase-row:nth-child(1) { animation-delay: 0.05s; }
.xnight-upgrade-attrs .xnight-purchase-row:nth-child(2) { animation-delay: 0.12s; }
.xnight-upgrade-attrs .xnight-purchase-row:nth-child(3) { animation-delay: 0.19s; }
.xnight-upgrade-attrs .xnight-purchase-row:nth-child(4) { animation-delay: 0.26s; }

/* Corner tick on an already-owned shop card. Replaces the old behaviour of
   overwriting the price with a checkmark, which looked like a price change. */
.gift-owned-mark {
    position: absolute;
    top: 10px;
    left: 10px;
    width: 22px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: #10b981;
    color: #fff;
    box-shadow: 0 3px 10px rgba(16, 185, 129, 0.4);
    z-index: 3;
}

/* --- collectible gift card (profile grid) ------------------------------- */
/* An upgraded gift is drawn over its own painted backdrop, so it reads as a
   collectible at a glance rather than looking like the original item. */
.xnight-collectible-card {
    position: relative;
    flex-shrink: 0;
    border-radius: 22px;
    overflow: hidden;
    cursor: pointer;
    user-select: none;
    border: 1px solid rgba(255, 255, 255, 0.16);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
    transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.xnight-collectible-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 14px 32px rgba(0, 0, 0, 0.26);
}

/* The rarest backdrops get a coloured rim, matching the attribute tiers. */
.xnight-collectible-card.xnight-tier-epic {
    border-color: rgba(168, 85, 247, 0.55);
}

.xnight-collectible-card.xnight-tier-legendary {
    border-color: rgba(245, 158, 11, 0.65);
    box-shadow: 0 8px 24px rgba(245, 158, 11, 0.22);
}

/* The backdrop canvas sits behind everything; `backdropHtml` emits it. */
.xnight-collectible-card .xnight-gift-backdrop {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 0;
}

.xnight-collectible-inner {
    position: relative;
    z-index: 2;
    height: 100%;
    padding: 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
}

.xnight-collectible-number {
    position: absolute;
    top: 9px;
    right: 10px;
    z-index: 3;
    padding: 2px 7px;
    border-radius: 8px;
    background: rgba(0, 0, 0, 0.34);
    color: #fff;
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: 0.2px;
}

.xnight-collectible-art {
    flex: 1;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 12px;
}

.xnight-collectible-art img,
.xnight-collectible-art lottie-player {
    width: 88px;
    height: 88px;
    object-fit: contain;
    filter: drop-shadow(0 8px 18px rgba(0, 0, 0, 0.32));
}

.xnight-collectible-art .xnight-sticker-skeleton {
    width: 88px;
    height: 88px;
    /* Lighter than the default so it reads against a dark backdrop. */
    background: linear-gradient(100deg,
            rgba(255, 255, 255, 0.08) 20%,
            rgba(255, 255, 255, 0.2) 42%,
            rgba(255, 255, 255, 0.08) 64%);
    background-size: 240% 100%;
}

.xnight-collectible-name {
    width: 100%;
    text-align: center;
    font-size: 12.5px;
    font-weight: 800;
    line-height: 1.2;
    color: #fff;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* --- saved messages ("Избранное") -------------------------------------- */
.xnight-saved-avatar {
    width: 52px;
    height: 52px;
    border-radius: 50%;
    background: linear-gradient(135deg, #1d9bf0, #0044ff);
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    flex-shrink: 0;
}

/* The self-chat is pinned above the real conversations. */
.xnight-saved-row {
    padding: 14px 18px;
    margin-bottom: 10px;
    border-radius: 20px;
    background: var(--card-bg);
    border: 1px solid var(--border-color);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.06);
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 14px;
}

/* --- sticker skeleton --------------------------------------------------- */
.xnight-sticker-skeleton {
    border-radius: 18px;
    background: linear-gradient(100deg,
            rgba(128, 128, 128, 0.08) 20%,
            rgba(128, 128, 128, 0.18) 42%,
            rgba(128, 128, 128, 0.08) 64%);
    background-size: 240% 100%;
    animation: xnightShimmer 1.25s linear infinite;
}

@keyframes xnightShimmer {
    from { background-position: 130% 0; }
    to   { background-position: -130% 0; }
}

/* --- gift service message ---------------------------------------------- */
.xnight-gift-message {
    position: relative;
    max-width: 280px;
    margin: 10px auto;
    border-radius: 20px;
    overflow: hidden;
    background: linear-gradient(160deg, rgba(29, 155, 240, 0.14), rgba(139, 92, 246, 0.12));
    border: 1px solid rgba(29, 155, 240, 0.22);
    text-align: center;
}

/* An upgraded gift is drawn over its own backdrop, so the flat tint is dropped
   and the text switches to white for contrast. */
.xnight-gift-message.is-unique {
    background: transparent;
    border-color: rgba(255, 255, 255, 0.14);
    max-width: 300px;
}

.xnight-gift-backdrop {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    z-index: 0;
}

.xnight-gift-message-inner {
    position: relative;
    z-index: 1;
    padding: 18px 20px 16px;
}

.xnight-gift-message.is-unique .xnight-gift-message-title,
.xnight-gift-message.is-unique .xnight-gift-message-sub {
    color: #fff;
}

.xnight-gift-message.is-unique .xnight-gift-message-sub {
    opacity: 0.72;
}

.xnight-gift-message-art {
    width: 96px;
    height: 96px;
    margin: 0 auto 10px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.xnight-gift-message-art img,
.xnight-gift-message-art lottie-player {
    width: 96px;
    height: 96px;
    object-fit: contain;
    filter: drop-shadow(0 8px 18px rgba(0, 0, 0, 0.3));
}

.xnight-gift-message-title {
    font-size: 15.5px;
    font-weight: 800;
    color: var(--text-main);
}

.xnight-gift-message-sub {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-muted);
    margin-top: 3px;
    line-height: 1.4;
}

.xnight-gift-message-comment {
    margin-top: 10px;
    padding: 9px 12px;
    border-radius: 12px;
    background: rgba(0, 0, 0, 0.22);
    font-size: 13.5px;
    color: var(--text-main);
    word-break: break-word;
}

.xnight-gift-message.is-unique .xnight-gift-message-comment {
    color: #fff;
}

.xnight-gift-message-btn {
    margin-top: 12px;
    width: 100%;
    padding: 10px 14px;
    border: none;
    border-radius: 13px;
    background: rgba(29, 155, 240, 0.18);
    color: #1d9bf0;
    font-family: inherit;
    font-size: 13.5px;
    font-weight: 800;
    cursor: pointer;
    transition: background 0.2s ease;
}

.xnight-gift-message-btn:hover {
    background: rgba(29, 155, 240, 0.3);
}

/* --- collectible attributes -------------------------------------------- */
.xnight-gift-attrs {
    margin-top: 12px;
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.xnight-gift-attr-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 11px;
    border-radius: 11px;
    background: rgba(0, 0, 0, 0.22);
    font-size: 12.5px;
    text-align: left;
}

.xnight-gift-attr-row span {
    color: rgba(255, 255, 255, 0.6);
    font-weight: 600;
    flex-shrink: 0;
}

.xnight-gift-attr-row strong {
    color: #fff;
    font-weight: 800;
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Rarity is shown as a percentage, coloured by tier — the rarer the attribute,
   the warmer the colour, matching how Telegram surfaces it. */
.xnight-gift-attr-row em {
    font-style: normal;
    font-weight: 800;
    font-size: 11.5px;
    padding: 2px 6px;
    border-radius: 7px;
    flex-shrink: 0;
}

.xnight-tier-common em   { background: rgba(148, 163, 184, 0.25); color: #cbd5e1; }
.xnight-tier-rare em     { background: rgba(56, 189, 248, 0.25);  color: #7dd3fc; }
.xnight-tier-epic em     { background: rgba(168, 85, 247, 0.28);  color: #d8b4fe; }
.xnight-tier-legendary em{ background: rgba(245, 158, 11, 0.3);   color: #fcd34d; }

/* ===========================================================================
   Gift info dialog
   =========================================================================== */
.xnight-gift-info-card {
    /* Shared so the card, the header and the backdrop cannot drift apart.
       28px matches .modal-card in app.html. */
    --xnight-gift-info-radius: 28px;
    max-width: 420px;
    width: 92%;
    padding: 0;
    border-radius: var(--xnight-gift-info-radius);
    /* The header's backdrop reaches the edges, so nothing may spill out. */
    overflow: hidden;
    text-align: center;
}

.xnight-gift-info-container {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.xnight-gift-info-header {
    position: relative;
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 26px 16px 16px;
    /* Rounded here as well as on the card: a canvas child is not clipped by an
       ancestor's radius as dependably as a plain box is. */
    border-top-left-radius: var(--xnight-gift-info-radius, 28px);
    border-top-right-radius: var(--xnight-gift-info-radius, 28px);
    overflow: hidden;
}

/* The painted backdrop, behind everything in the header. */
.xnight-gift-info-backdrop {
    position: absolute;
    inset: 0;
    display: none;
    border-top-left-radius: inherit;
    border-top-right-radius: inherit;
    overflow: hidden;
}

/* The canvas itself, so the painted gradient follows the same corners. */
.xnight-gift-info-backdrop .xnight-gift-backdrop {
    border-top-left-radius: inherit;
    border-top-right-radius: inherit;
}

.xnight-gift-info-container.is-collectible .xnight-gift-info-backdrop {
    display: block;
}

.xnight-gift-info-close {
    position: absolute;
    top: 12px;
    right: 12px;
    z-index: 10;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    margin: 0;
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.2);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    color: var(--text-muted, #8b8b8b);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
    transition: background 0.2s ease, color 0.2s ease, transform 0.15s ease;
}

.xnight-gift-info-close svg {
    display: block;
    width: 16px;
    height: 16px;
    pointer-events: none;
}

.xnight-gift-info-close:hover {
    background: rgba(0, 0, 0, 0.35);
    color: var(--text-main, #fff);
    transform: scale(1.06);
}

/* Over a backdrop the control has to be light to stay legible. */
.xnight-gift-info-container.is-collectible .xnight-gift-info-close {
    background: rgba(255, 255, 255, 0.18);
    color: #ffffff;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}

.xnight-gift-info-container.is-collectible .xnight-gift-info-close:hover {
    background: rgba(255, 255, 255, 0.32);
    color: #ffffff;
    transform: scale(1.06);
}

.xnight-gift-info-sticker {
    position: relative;
    z-index: 1;
    width: 120px;
    height: 120px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.xnight-gift-info-sticker img,
.xnight-gift-info-sticker lottie-player {
    width: 120px;
    height: 120px;
    object-fit: contain;
}

.xnight-gift-info-container.is-collectible .xnight-gift-info-sticker img,
.xnight-gift-info-container.is-collectible .xnight-gift-info-sticker lottie-player {
    filter: drop-shadow(0 10px 22px rgba(0, 0, 0, 0.4));
}

.xnight-gift-info-sticker .xnight-sticker-skeleton {
    width: 120px;
    height: 120px;
}

.xnight-gift-info-title {
    position: relative;
    z-index: 1;
    margin-top: 16px;
    font-size: 24px;
    line-height: 28px;
    font-weight: 700;
    color: var(--text-main, #fff);
}

.xnight-gift-info-container.is-collectible .xnight-gift-info-title {
    color: #fff;
}

.xnight-gift-info-subtitle {
    position: relative;
    z-index: 1;
    margin-top: 6px;
    font-size: 16px;
    line-height: 22px;
    font-weight: 500;
    color: var(--text-muted, #8b8b8b);
}

.xnight-gift-info-container.is-collectible .xnight-gift-info-subtitle {
    color: rgba(255, 255, 255, 0.8);
}

.xnight-gift-info-subtitle:empty {
    display: none;
}

/* --- collectible actions (Wear / Transfer / Sell) ------------------------
   Three equal tiles on the header, over the painted backdrop, each an icon
   above its label — the layout webk uses in PopupStarGiftInfo. */
.xnight-gift-info-actions {
    position: relative;
    z-index: 1;
    margin-top: 16px;
    display: flex;
    flex-direction: row;
    gap: 10px;
    width: 100%;
}

.xnight-gift-info-action {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 6px 4px 8px;
    border: none;
    border-radius: 10px;
    /* Translucent white rather than a solid colour: the tiles sit on a rolled
       backdrop whose colours are not known ahead of time. */
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
    font-family: inherit;
    font-size: 12px;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    transition: background 0.2s ease;
}

.xnight-gift-info-action:hover {
    background: rgba(255, 255, 255, 0.16);
}

.xnight-gift-info-action svg {
    padding: 4px;
    box-sizing: content-box;
}

.xnight-gift-info-action > span {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* --- attribute table ---------------------------------------------------- */
.xnight-gift-info-table {
    width: 100%;
    padding: 16px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    text-align: left;
}

.xnight-gift-info-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 11px;
    border-radius: 10px;
    background: rgba(128, 128, 128, 0.1);
    font-size: 14px;
}

.xnight-gift-info-row-label {
    flex-shrink: 0;
    min-width: 92px;
    color: var(--text-muted, #8b8b8b);
    font-weight: 600;
}

.xnight-gift-info-row-value {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 6px;
    color: var(--text-main, #fff);
    font-weight: 600;
}

.xnight-gift-info-row-value > span:first-child {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Rarity badge, coloured by tier: the rarer the attribute the warmer the
   colour, following how Telegram surfaces it. */
.xnight-gift-info-rarity {
    flex-shrink: 0;
    padding: 2px 6px;
    border-radius: 7px;
    font-size: 12px;
    font-weight: 700;
}

.xnight-gift-info-row.xnight-tier-common .xnight-gift-info-rarity {
    background: rgba(148, 163, 184, 0.22);
    color: #94a3b8;
}

.xnight-gift-info-row.xnight-tier-rare .xnight-gift-info-rarity {
    background: rgba(37, 163, 185, 0.22);
    color: #25a3b9;
}

.xnight-gift-info-row.xnight-tier-epic .xnight-gift-info-rarity {
    background: rgba(175, 82, 222, 0.22);
    color: #af52de;
}

.xnight-gift-info-row.xnight-tier-legendary .xnight-gift-info-rarity {
    background: rgba(213, 126, 50, 0.22);
    color: #d57e32;
}

/* The sender's avatar inside a row. */
.xnight-gift-info-peer {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.xnight-gift-info-peer-avatar {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    overflow: hidden;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
}

/* The comment, shown as the table's footer the way webk does. */
.xnight-gift-info-note {
    width: 100%;
    padding: 10px 12px;
    margin: 0 16px 4px;
    border-radius: 10px;
    background: rgba(128, 128, 128, 0.1);
    font-size: 14px;
    line-height: 19px;
    font-style: italic;
    color: var(--text-main, #fff);
    text-align: center;
    box-sizing: border-box;
    width: calc(100% - 32px);
    word-break: break-word;
}

/* --- footer ------------------------------------------------------------- */
.xnight-gift-info-footer {
    width: 100%;
    padding: 0 16px 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.xnight-gift-info-btn {
    width: 100%;
    height: 48px;
    border: none;
    border-radius: 12px;
    background: var(--button-bg, #1d9bf0);
    color: #fff;
    font-size: 15px;
    font-weight: 700;
    font-family: inherit;
    cursor: pointer;
    transition: opacity 0.2s ease;
}

.xnight-gift-info-btn:hover {
    opacity: 0.88;
}

.xnight-gift-info-btn.is-upgrade {
    background: linear-gradient(135deg, #8b5cf6, #ec4899);
}

@media (max-width: 640px) {
    .xnight-gift-info-title {
        font-size: 21px;
        line-height: 25px;
    }

    .xnight-gift-info-row-label {
        min-width: 78px;
    }

    /* Three tiles on a narrow screen: the labels have to shrink before they
       start eating the icons. */
    .xnight-gift-info-actions {
        gap: 6px;
    }

    .xnight-gift-info-action {
        font-size: 11px;
        padding: 5px 2px 7px;
    }
}

/* --- responsive -------------------------------------------------------- */
@media (max-width: 640px) {
    .xnight-modal-card {
        max-width: 100%;
        padding: 22px 18px 18px;
        border-radius: 22px;
    }

    .xnight-upgrade-stage {
        margin: 6px auto 16px;
        height: 156px;
        max-width: 100%;
        border-radius: 20px;
    }

    .xnight-upgrade-slot {
        width: 112px;
        height: 112px;
    }

    .xnight-upgrade-slot img {
        width: 92px;
        height: 92px;
    }

    .xnight-purchase-actions {
        flex-direction: column-reverse;
    }

    .xnight-btn-ghost {
        flex: 1 1 auto;
    }
}

/* Users who prefer reduced motion get the outcome without the theatrics. */
@media (prefers-reduced-motion: reduce) {
    .xnight-upgrade-rays,
    .xnight-purchase-glow {
        animation: none;
    }

    .xnight-upgrade-reel {
        transition: none !important;
    }

    .xnight-upgrade-result.revealed,
    .xnight-purchase-success,
    .xnight-upgrade-attrs .xnight-purchase-row {
        animation: none;
    }
}

/* ---------------------------------------------------------------------------
   Hover-to-play stickers

   A .tgs holds on its first frame and only animates while the pointer is over
   it, so a long list of gifts is not running dozens of Lottie loops at once.
   The player itself paints frame 0, so it stays visible at all times; only the
   cursor and a subtle lift signal that it is interactive.
   --------------------------------------------------------------------------- */
.xnight-sticker-hover {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}

.xnight-sticker-hover lottie-player {
    display: block;
    transition: transform 0.18s cubic-bezier(0.16, 1, 0.3, 1);
}

.xnight-sticker-hover:hover lottie-player {
    transform: scale(1.06);
}

/* The reveal inside the upgrade dialog animates on its own, so it must not
   pick up the hover affordance. */
.xnight-upgrade-result .xnight-sticker-hover,
.xnight-upgrade-result.xnight-sticker-hover {
    cursor: default;
}

.xnight-upgrade-result .xnight-sticker-hover:hover lottie-player,
.xnight-upgrade-result.xnight-sticker-hover:hover lottie-player {
    transform: none;
}

/* --- WebK-style Animated Attribute Roller --- */
.xnight-attr-roller {
    position: relative;
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1;
    height: 19px;
    overflow: hidden;
}

.xnight-attr-roller-track {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    /* Transformation and timing applied inline from JS */
    will-change: transform;
}

.xnight-attr-roller-item {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 19px;
    line-height: 19px;
    opacity: 0;
    transform: translateY(14px) scaleY(0.5);
    white-space: nowrap;
    overflow: hidden;
}

.xnight-attr-roller-item.is-entering {
    opacity: 1;
    transform: translateY(0) scaleY(1);
    transition: opacity 0.15s ease, transform 0.25s cubic-bezier(0.12, 0.72, 0.05, 1);
}

.xnight-attr-roller-item.is-leaving {
    opacity: 0;
    transform: translateY(-14px) scaleY(0.5);
    transition: opacity 0.15s ease, transform 0.25s cubic-bezier(0.12, 0.72, 0.05, 1);
}

/* Collectible Number Animation */
.xnight-number-roller-digit {
    display: inline-block;
    vertical-align: top;
    text-align: center;
    position: relative;
    height: 1.4em;
    overflow: hidden;
}

.xnight-number-roller-track {
    position: absolute;
    top: 0;
    left: 0;
    display: flex;
    flex-direction: column;
    will-change: transform;
    transition: transform 0.1s linear;
}

.xnight-number-roller-val {
    height: 1.4em;
    line-height: 1.4em;
}

/* WebK-style 3D Model Roll */
.xnight-upgrade-models-3d-wrap {
    position: absolute;
    inset: 0;
    display: none;
    z-index: 1;
    overflow: hidden;
}
.xnight-upgrade-stage.spinning-stage .xnight-upgrade-models-3d-wrap {
    display: block;
}

.xnight-upgrade-models-3d-track {
    display: flex;
    flex-direction: row;
    gap: 100px;
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
}

.xnight-upgrade-models-3d-item {
    width: 120px;
    height: 120px;
    flex-shrink: 0;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 1;
    transition: opacity 0.2s ease-in;
}

.xnight-upgrade-models-3d-item img,
.xnight-upgrade-models-3d-item lottie-player {
    width: 120px;
    height: 120px;
    object-fit: contain;
    filter: drop-shadow(0 10px 22px rgba(0, 0, 0, 0.4));
}

.xnight-upgrade-models-3d-track.finishing .xnight-upgrade-models-3d-item:not(.last) {
    opacity: 0 !important;
}

/* WebK-style Scrolling Backdrop Strip */
.xnight-upgrade-backdrop-strip {
    position: absolute;
    inset: 0;
    display: none;
    z-index: 0;
    opacity: 1;
    transition: opacity 0.2s ease-in;
}
.xnight-upgrade-stage.spinning-stage .xnight-upgrade-backdrop-strip {
    display: block;
}

.xnight-upgrade-backdrop-strip.finishing {
    opacity: 0;
}

/* ===========================================================================
   Gift Info Upgrade 3D Animation Roll
   =========================================================================== */
.xnight-gift-info-upgrade-backdrops {
    position: absolute;
    inset: 0;
    z-index: 0;
    opacity: 1;
    transition: opacity 0.2s ease-in;
    will-change: background-position, opacity;
}

.xnight-gift-info-upgrade-backdrops.finishing {
    opacity: 0;
}

/* Mounted on .xnight-gift-info-header, not on the 120px sticker box: the reel is
   thousands of pixels wide and has to span the full dialog to be visible at
   all. Absolute so it overlays the sticker slot without disturbing layout.

   The box is taller than a model on purpose. Clipping is needed horizontally, to
   hide the slots waiting off-screen, but `overflow-x: hidden` with a visible Y
   is not expressible in CSS — the visible axis computes to auto. So the box
   clips both axes and is given room above and below instead, or the models'
   drop-shadow would be sliced off top and bottom.

   Centred on the sticker slot it covers: 26px of header padding + half a model,
   minus half of this box's own height. */
.xnight-gift-info-upgrade-models-container {
    --xnight-reel-model: 120px;
    --xnight-reel-bleed: 20px;
    position: absolute;
    left: 0;
    right: 0;
    top: calc(26px - var(--xnight-reel-bleed));
    height: calc(var(--xnight-reel-model) + var(--xnight-reel-bleed) * 2);
    overflow: hidden;
    z-index: 2;
}

.xnight-gift-info-upgrade-models {
    display: flex;
    flex-direction: row;
    /* Centred in the padded box, so the bleed sits evenly above and below. */
    align-items: center;
    gap: 100px;
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    will-change: transform;
    /* Hidden until every slot has loaded. Half-loaded slots sliding past is
       what made the reveal look abrupt, so the reel only appears once it can
       actually move — mirroring webk's preloader gate. */
    opacity: 0;
}

.xnight-gift-info-upgrade-models-container.is-ready .xnight-gift-info-upgrade-models {
    opacity: 1;
    transition: opacity 0.2s ease-out;
}

.xnight-gift-info-upgrade-model {
    width: var(--xnight-reel-model, 120px);
    height: var(--xnight-reel-model, 120px);
    flex-shrink: 0;
    position: relative;
    opacity: 1;
    display: flex;
    align-items: center;
    justify-content: center;
}

.xnight-gift-info-upgrade-model img,
.xnight-gift-info-upgrade-model lottie-player {
    width: var(--xnight-reel-model, 120px);
    height: var(--xnight-reel-model, 120px);
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    transform-origin: center center;
    filter: drop-shadow(0 10px 22px rgba(0, 0, 0, 0.4));
}

.xnight-gift-info-upgrade-models.finishing .xnight-gift-info-upgrade-model:not(.last) {
    transition: opacity 0.2s ease-in;
    opacity: 0 !important;
}

/* Attributes slot machine.

   The rolling value is clipped vertically so an arriving name slides in from
   below and the outgoing one leaves upwards. The box therefore has to be as tall
   as the tallest thing in a row — the rarity badge, which is 12px text plus 2px
   of padding either side — or the badge and the descenders of the name are
   sliced off. 24px also matches the avatar in the owner row, so an animated row
   is exactly as tall as a static one. */
.xnight-animated-attr-val {
    --xnight-attr-roll-h: 24px;
    position: relative;
    flex: 1;
    min-width: 0;
    height: var(--xnight-attr-roll-h);
    overflow: hidden;
}

/* Stacked, not laid out in a row.

   The outgoing and the incoming value are both in the box during a step. As flex
   children they would sit side by side, so each step shoved the text further
   right — and the settled value stayed pushed over by the invisible one it
   replaced. Absolute positioning puts them on top of each other, which is what
   makes the vertical slide read as one value being swapped for the next. */
.xnight-animated-attr-item {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    display: flex;
    align-items: center;
    gap: 6px;
    /* Fills the clip box, so a translate of 100% moves exactly one row. */
    height: 100%;
    opacity: 0;
    transform: translateY(100%) scaleY(0.5);
}

.xnight-animated-attr-item.active {
    opacity: 1;
    transform: translateY(0) scaleY(1);
    transition: opacity 0.2s ease, transform 0.2s cubic-bezier(0.12,0.72,0.05,1);
}

.xnight-animated-attr-item.leaving {
    opacity: 0;
    transform: translateY(-100%) scaleY(0.5);
    transition: opacity 0.2s ease, transform 0.2s cubic-bezier(0.12,0.72,0.05,1);
}

/* The name itself, so a long model name is cut with an ellipsis rather than
   pushing the rarity badge out of the clipped box. */
.xnight-animated-attr-item > span:first-child {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Number slot machine */
.xnight-animated-num-digit {
    display: inline-block;
    vertical-align: top;
    text-align: center;
    position: relative;
    height: 1.2em;
    overflow: hidden;
    width: 0.6em; 
}

.xnight-animated-num-mover {
    position: absolute;
    top: 0;
    left: 0;
    display: flex;
    flex-direction: column;
    will-change: transform;
    transition: transform 0.1s linear;
}

.xnight-animated-num-val {
    height: 1.2em;
    line-height: 1.2em;
}

/* ===========================================================================
   High Performance Rendering & GPU Acceleration Optimizations.
   =========================================================================== */

/* Hardware accelerated smooth scroll across containers */
html, body {
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-y: auto;
    text-rendering: optimizeLegibility;
    -webkit-font-smoothing: antialiased;
}

/* Scrollable containers GPU promotion & boundary containment */
.modal-card,
.xnight-modal-card,
.modal-body,
.xnight-gift-info-table,
.xnight-hscroll,
#myGiftsTimeline,
#giftModalItemsGrid,
.feed-container,
.direct-messages-list {
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
}

/* GPU promotion for animated cards, stickers, & interactive hover elements */
.telegram-gift-card-box,
.xnight-collectible-card,
.xnight-purchase-art,
.xnight-upgrade-stage,
.xnight-purchase-glow,
.gift-card-img,
.gift-rose-tgs-container,
.xnight-collectible-art,
lottie-player,
.xnight-modal-overlay,
.modal-overlay,
.post-card,
.public-card,
.user-card {
    transform: translate3d(0, 0, 0);
    backface-visibility: hidden;
}

/* Contain layout paint reflows on repetitive grid cards */
.telegram-gift-card-box,
.xnight-collectible-card,
.post-card {
    contain: layout paint;
}

/* Optimize backdrop blur GPU layer */
.xnight-modal-overlay,
.modal-overlay,
.glass-header,
.glass-panel {
    transform: translateZ(0);
}

/* Optimize hover transitions: target hardware properties */
.telegram-gift-card-box,
.xnight-collectible-card,
.xnight-btn-primary,
.xnight-gift-info-btn {
    will-change: transform, opacity;
}

