/* =========================================================
   NO JAZZ TODAY — EASTER EGGS

   One stylesheet for optional / playful scene interactions.
   Core layout and responsive rules stay in desktop.css/mobile.css.
   ========================================================= */

/* ---------------------------------------------------------
   DISCOVERY HINTS — TOUCH / NO-HOVER DEVICES

   Scene-bound *_soft.png layers may briefly pulse after the core page has
   loaded. JS schedules only ONE hint at a time; this CSS owns only the visual
   pulse. The same class is reusable for future gadgets.
   --------------------------------------------------------- */

.easter-discovery-soft.is-discovery-hint {
    animation: easterDiscoveryHintPulse 1800ms ease-in-out both;
}

@keyframes easterDiscoveryHintPulse {
    0%, 100% { opacity: 0; }
    27%      { opacity: .72; }
    52%      { opacity: .32; }
    77%      { opacity: .62; }
}

/* ---------------------------------------------------------
   ASTRONAUT HOVER — DESKTOP ONLY
   Existing full-scene alternate remains scene-bound.
   --------------------------------------------------------- */

.layer-foreground-gadget {
    position: absolute;
    inset: 0;
    z-index: 41;
    width: 1672px;
    height: 941px;
    max-width: none;
    pointer-events: none;
    user-select: none;
    opacity: 0;
    transition: opacity 180ms ease-out;
}

.scene-hotspot {
    position: absolute;
    z-index: 70;
    background: transparent;
}

.hotspot-astronaut {
    left: 158px;
    top: 380px;
    width: 255px;
    height: 165px;
    cursor: pointer;
}

.hotspot-astronaut:hover ~ .layer-foreground-gadget {
    opacity: 1;
}

.layer-bottle-gadget-soft,
.layer-bottle-gadget {
    position: absolute;
    inset: 0;
    width: 1672px;
    height: 941px;
    max-width: none;
    pointer-events: none;
    user-select: none;
    opacity: 0;
}

.layer-bottle-gadget-soft {
    z-index: 42;
    transition: opacity 150ms ease-out;
}

.layer-bottle-gadget {
    z-index: 43;
    transition: none;
}

/* Existing desktop bottle/hand hotspot — values intentionally preserved. */
.hotspot-bottle {
    left: 484px;
    top: 520px;
    width: 110px;
    height: 156px;
    margin: 0;
    padding: 0;
    border: 0;
    appearance: none;
    -webkit-appearance: none;
    cursor: pointer;
}

/* Desktop hover = soft visual hint only. No sound. */
.hotspot-bottle:hover ~ .layer-bottle-gadget-soft {
    opacity: 1;
}

/* Strong click/tap effect. JS holds it, then adds is-fading. */
.layer-bottle-gadget.is-active,
.mobile-layer-bottle-gadget.is-active {
    opacity: 1;
    transition: none;
}

.layer-bottle-gadget.is-fading,
.mobile-layer-bottle-gadget.is-fading {
    opacity: 0;
    transition: opacity var(--bottle-fade-duration, 2000ms) ease-out;
}

/* Mobile bottle target on the 941 × 1672 logical scene.
   Touch remains tap-only. A desktop/fine-pointer browser which happens to use
   this mobile artwork receives the dedicated soft hover overlay as well. */
.mobile-bottle-hotspot {
    position: absolute;
    z-index: 70;
    left: 252px;
    top: 1179px;
    width: 78px;
    height: 128px;
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    appearance: none;
    -webkit-appearance: none;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}

.mobile-layer-bottle-gadget-soft,
.mobile-layer-bottle-gadget {
    position: absolute;
    inset: 0;
    width: 941px;
    height: 1672px;
    max-width: none;
    pointer-events: none;
    user-select: none;
    opacity: 0;
}

.mobile-layer-bottle-gadget-soft {
    z-index: 42;
    transition: opacity 150ms ease-out;
}

.mobile-layer-bottle-gadget {
    z-index: 43;
    transition: none;
}

@media (any-hover: hover) and (any-pointer: fine) {
    .mobile-bottle-hotspot:hover ~ .mobile-layer-bottle-gadget-soft {
        opacity: 1;
    }
}

/* Never let bottle controls interfere with open menus. */
.desktop-app.compact-nav:has(.holo.desktop-menu-open) .hotspot-bottle,
#mobile-app:has(.mobile-holo.mobile-menu-open) .mobile-bottle-hotspot {
    pointer-events: none;
}

#mobile-app:has(.mobile-holo.mobile-menu-open) .mobile-layer-bottle-gadget-soft {
    opacity: 0 !important;
}

.desktop-app.compact-nav:has(.holo.desktop-menu-open) .layer-bottle-gadget-soft {
    opacity: 0 !important;
}

@media (hover: none) {
    .scene-hotspot {
        display: none;
    }
}

/* Compact desktop: keep the existing hover, but never let its hotspot
   steal clicks while the Holo menu is open. */
.desktop-app.compact-nav .scene-hotspot {
    display: block;
}

.desktop-app.compact-nav:has(.holo.desktop-menu-open) .scene-hotspot {
    pointer-events: none;
}

.desktop-app.compact-nav .layer-foreground-gadget {
    pointer-events: none;
}

/* ---------------------------------------------------------
   BLUE MOON / JAZZ MODE
   Full-scene moon PNGs remain in scene/desktop and scene/mobile.
   --------------------------------------------------------- */

.moon-gadget-layer {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    max-width: none;
    object-fit: fill;
    pointer-events: none;
    user-select: none;
    opacity: 0;
    z-index: 41;
    transition: opacity 220ms ease;
}

/* Moon artwork remains on its established scene layer. Discovery hinting only
   changes opacity; it must never pull an asset in front of scene elements that
   normally cover it. */
.moon-gadget-soft,
.moon-gadget-strong {
    z-index: 4;
}

.moon-gadget-layer.is-visible {
    opacity: 1;
}

.moon-gadget-layer.is-pulsing {
    animation: moonGadgetStrongPulse 620ms ease-out forwards;
}

@keyframes moonGadgetStrongPulse {
    0%   { opacity: 0; }
    18%  { opacity: 1; }
    58%  { opacity: 0.92; }
    100% { opacity: 0; }
}

.jazz-moon-hotspot {
    position: absolute;
    z-index: 56;
    display: block;
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}

.jazz-moon-hotspot:focus {
    outline: none;
}

/* Mobile artwork: the visible blue moon starts at the TOP scene edge,
   continues much farther right than the old 190 px patch and also remains
   visible behind the upper-left Holo edge. Keep the target elliptical so the
   enlarged area follows the moon instead of becoming a broad rectangular
   blocker over the Holo. Coordinates are on the fixed 941 × 1672 scene. */
.jazz-moon-hotspot.is-mobile-visible-moon {
    left: 0;
    top: 0;
    width: 340px;
    height: 165px;
    border-radius: 50% 50% 46% 42%;
}

.jazz-easter-host {
    position: absolute;
    inset: 14px 16px 16px;
    z-index: 120;
    overflow: hidden;
    pointer-events: none;
    border-radius: 18px;
    border: 1px solid rgba(151, 229, 255, 0.18);
    background: linear-gradient(
        180deg,
        rgba(3, 28, 62, 0.965),
        rgba(2, 21, 49, 0.975)
    );
    box-shadow:
        inset 0 0 24px rgba(92, 210, 255, 0.055),
        0 0 10px rgba(74, 198, 242, 0.08);
    opacity: 0;
    animation: jazzHostIn 0.18s linear forwards;
}

#mobile-holo > .jazz-easter-host {
    inset: 22px 24px 26px;
    border-radius: 26px;
}

.jazz-easter-host.is-closing {
    animation: jazzHostOut 0.22s linear forwards;
}

@keyframes jazzHostIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes jazzHostOut {
    from { opacity: 1; }
    to   { opacity: 0; }
}

#desktop-holo.jazz-mode-running > .holo-nav,
#desktop-holo.jazz-mode-running > .desktop-menu-toggle,
#desktop-holo.jazz-mode-running > .desktop-compact-menu,
#desktop-holo.jazz-mode-running > .desktop-compact-home-logo,
#mobile-holo.jazz-mode-running > .mobile-menu-toggle,
#mobile-holo.jazz-mode-running > .mobile-menu,
#mobile-holo.jazz-mode-running > .mobile-home-logo {
    visibility: hidden !important;
    pointer-events: none !important;
}

.desktop-app.jazz-mode-active .navigation-bar {
    pointer-events: none;
}

/* ---------------------------------------------------------
   LIVE TOUR BUS — DESKTOP + MOBILE

   Independent Easter Egg. Both source PNGs use the exact same 2048×875
   canvas and are therefore always registered at inset:0 inside one shared
   stack. The stack owns horizontal travel only. Bus and dust get separate,
   small vertical/scale motion so their prepared canvas registration is never
   replaced by ad-hoc left/top offsets.
   --------------------------------------------------------- */

.live-bus-overlay {
    position: absolute;
    inset: 0;
    pointer-events: none;
    opacity: 0;
    overflow: hidden;
}

.live-bus-overlay--desktop {
    z-index: 60;
}

.live-bus-overlay--mobile {
    z-index: 28;
}

.live-bus-motion {
    position: absolute;
    aspect-ratio: 2048 / 875;
    transform: translateX(-118%);
    transform-origin: 50% 100%;
    will-change: transform;
}

.live-bus-overlay--desktop .live-bus-motion {
    left: 30px;
    bottom: -70px;
    width: 900px;
}

.live-bus-overlay--mobile .live-bus-motion {
    left: -20px;
    bottom: -50px;
    width: 800px;
}

.live-bus-image,
.live-bus-dust {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    max-width: none;
    object-fit: contain;
    pointer-events: none;
    user-select: none;
    transform-origin: 50% 72%;
}

.live-bus-image {
    z-index: 2;
    opacity: 1;
    filter: drop-shadow(0 6px 10px rgba(0, 0, 0, 0.25));
}

.live-bus-dust {
    z-index: 3;
    opacity: 1;
}

.live-bus-overlay.is-playing {
    opacity: 1;
}

.live-bus-overlay.is-playing .live-bus-motion {
    animation: liveBusTravel 6600ms linear forwards;
}

.live-bus-overlay.is-playing .live-bus-image {
    animation: liveBusBounce 6600ms ease-in-out forwards;
}

.live-bus-overlay.is-playing .live-bus-dust {
    animation: liveBusDust 6600ms ease-in-out forwards;
}

/* Smooth travel: enter -> stand still -> leave. The horizontal movement is
   deliberately independent from the small vertical motion below. */
@keyframes liveBusTravel {
    0% {
        transform: translateX(-118%);
        animation-timing-function: cubic-bezier(.20,.70,.24,1);
    }
    30% {
        transform: translateX(0);
        animation-timing-function: linear;
    }
    68% {
        transform: translateX(0);
        animation-timing-function: cubic-bezier(.30,.03,.25,1);
    }
    100% {
        transform: translateX(120%);
    }
}

/* Bus body: small road/suspension movement while driving; perfectly still
   during the pause. No horizontal component lives here. */
@keyframes liveBusBounce {
    0%   { opacity: 1; transform: translateY(1.5px); }
    6%   { opacity: 1; transform: translateY(-1px); }
    12%  { opacity: 1; transform: translateY(1px); }
    18%  { opacity: 1; transform: translateY(-0.7px); }
    24%  { opacity: 1; transform: translateY(0.6px); }
    30%  { opacity: 1; transform: translateY(0); }
    68%  { opacity: 1; transform: translateY(0); }
    75%  { opacity: 1; transform: translateY(-1px); }
    82%  { opacity: 1; transform: translateY(1px); }
    89%  { opacity: 1; transform: translateY(-0.7px); }
    95%  { opacity: 1; transform: translateY(0.6px); }
    100% { opacity: 1; transform: translateY(0); }
}

/* Dust: travels on the same registered canvas, but breathes faster and with a
   slightly different vertical rhythm. At the stop it fades slowly to 25%,
   drifts a little, then comes back to 100% more quickly as the bus departs. */
@keyframes liveBusDust {
    0%   { opacity: 1;    transform: translateY(1px) scale(1.000, 1.000); }
    6%   { opacity: 1;    transform: translateY(-2.5px) scale(1.010, 1.016); }
    12%  { opacity: 1;    transform: translateY(1.8px) scale(1.002, 1.009); }
    18%  { opacity: 1;    transform: translateY(-2px) scale(1.013, 1.020); }
    24%  { opacity: 1;    transform: translateY(1.2px) scale(1.006, 1.012); }
    30%  { opacity: 1;    transform: translateY(0) scale(1.000, 1.000); }
    42%  { opacity: .78;  transform: translateY(-1px) scale(1.006, 1.010); }
    55%  { opacity: .52;  transform: translateY(.7px) scale(1.010, 1.014); }
    68%  { opacity: .25;  transform: translateY(-1px) scale(1.014, 1.018); }
    76%  { opacity: 1;    transform: translateY(1.8px) scale(1.007, 1.013); }
    82%  { opacity: 1;    transform: translateY(-2.5px) scale(1.015, 1.021); }
    88%  { opacity: 1;    transform: translateY(1.8px) scale(1.004, 1.011); }
    94%  { opacity: 1;    transform: translateY(-1.5px) scale(1.012, 1.018); }
    100% { opacity: 1;    transform: translateY(0) scale(1.006, 1.012); }
}

/* Firefox Desktop uses the accepted smaller 760×370 Holo plane. Keep the
   same registered PNG stack, only adapt the shared box to that Holo size. */
.desktop-app.firefox-old-holo-test #desktop-holo .live-bus-overlay--desktop {
    z-index: 120;
}

.desktop-app.firefox-old-holo-test #desktop-holo .live-bus-overlay--desktop .live-bus-motion {
    left: 0;
    bottom: -61px;
    width: 760px;
}

@media (prefers-reduced-motion: reduce) {
    .live-bus-overlay.is-playing .live-bus-motion,
    .live-bus-overlay.is-playing .live-bus-image,
    .live-bus-overlay.is-playing .live-bus-dust {
        animation-duration: 1ms !important;
    }
}

/* ---------------------------------------------------------
   CPT. JELLYFISH NAME-PLATE TRIGGER — SCENE-BOUND

   These full-scene overlays are deliberately kept in desktop/mobile because
   they are registered to those logical canvases. Soft = hover/discovery hint.
   Strong = pulsing active state while the Jellyfish audio is running.
   --------------------------------------------------------- */

.layer-jellyfish-gadget-soft,
.layer-jellyfish-gadget {
    position: absolute;
    inset: 0;
    width: 1672px;
    height: 941px;
    max-width: none;
    pointer-events: none;
    user-select: none;
    opacity: 0;
}

.layer-jellyfish-gadget-soft {
    z-index: 44;
    transition: opacity 220ms ease-out;
}

.layer-jellyfish-gadget {
    z-index: 45;
}

.mobile-layer-jellyfish-gadget-soft,
.mobile-layer-jellyfish-gadget {
    position: absolute;
    inset: 0;
    width: 941px;
    height: 1672px;
    max-width: none;
    pointer-events: none;
    user-select: none;
    opacity: 0;
}

.mobile-layer-jellyfish-gadget-soft {
    z-index: 44;
    transition: opacity 220ms ease-out;
}

.mobile-layer-jellyfish-gadget {
    z-index: 45;
}

.hotspot-jellyfish:hover ~ .layer-jellyfish-gadget-soft {
    opacity: 1;
}

@media (any-hover: hover) and (any-pointer: fine) {
    .mobile-jellyfish-hotspot:hover ~ .mobile-layer-jellyfish-gadget-soft {
        opacity: 1;
    }
}

.layer-jellyfish-gadget-soft.is-suppressed,
.mobile-layer-jellyfish-gadget-soft.is-suppressed {
    opacity: 0 !important;
    animation: none !important;
}

.layer-jellyfish-gadget.is-audio-active,
.mobile-layer-jellyfish-gadget.is-audio-active {
    animation: jellyfishNameplateActivePulse 2200ms ease-in-out infinite;
}

@keyframes jellyfishNameplateActivePulse {
    0%, 100% { opacity: .30; }
    31%      { opacity: .96; }
    58%      { opacity: .44; }
    79%      { opacity: .82; }
}

#mobile-app:has(.mobile-holo.mobile-menu-open) .mobile-layer-jellyfish-gadget-soft,
.desktop-app.compact-nav:has(.holo.desktop-menu-open) .layer-jellyfish-gadget-soft {
    opacity: 0 !important;
    animation: none !important;
}

/* ---------------------------------------------------------
   CPT. JELLYFISH — DESKTOP + MOBILE

   Trigger = Cpt. Jellyfish name plate in the scene.
   Visual = Holo-level overlay so the character is clipped by the screen and
   automatically follows the existing desktop/mobile perspective.

   The three PNGs are deliberately registered in one identical box. The
   wrapper owns all travel; the frames only cross-fade. This keeps the notes,
   tie and office props stable while the bell/tentacles pulse subtly.
   --------------------------------------------------------- */

.hotspot-jellyfish,
.mobile-jellyfish-hotspot {
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    appearance: none;
    -webkit-appearance: none;
    cursor: pointer;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}

/* Desktop logical scene 1672 × 941. The fixed values are a proven fallback;
   JS refines them from the alpha bounds of desktop/jellyfish-gadget.png after
   that deferred scene-bound layer has loaded. */
.hotspot-jellyfish {
    left: 382px;
    top: 548px;
    width: 92px;
    height: 62px;
    z-index: 90;
    pointer-events: auto;
}

/* The Jellyfish name plate is intentionally independent from the astronaut
   hover target. Hovering the shoulder/name plate must not trigger the helmet
   foreground effect. */

/* Portrait mobile logical scene 941 × 1672. These values are only a safe
   fallback. easter-eggs.js refines the target from the alpha bounds of the
   scene-bound mobile/jellyfish-gadget.png once that deferred asset is loaded. */
.mobile-jellyfish-hotspot {
    position: absolute;
    z-index: 92;
    left: 132px;
    top: 1124px;
    width: 106px;
    height: 72px;
}

#mobile-app:has(.mobile-holo.mobile-menu-open) .mobile-jellyfish-hotspot {
    pointer-events: none;
}

.jellyfish-easter-egg {
    position: absolute;
    inset: 0;
    z-index: 54;
    overflow: hidden;
    pointer-events: none;
    opacity: 0;
}

.jellyfish-easter-egg.is-playing {
    opacity: 1;
}

.jellyfish-motion {
    position: absolute;
    aspect-ratio: 560 / 700;
    transform-origin: 50% 58%;
    will-change: transform;

    /* Shared rise path. Keep the default values identical to the established
       Desktop/Mobile animation; browser-specific Holo planes may override only
       these logical coordinates without touching the animation itself. */
    --jelly-x-16: 8px;
    --jelly-y-16: -150px;
    --jelly-x-30: 23px;
    --jelly-y-30: -345px;
    --jelly-late-x: 62px;
}

.jellyfish-easter-egg--desktop .jellyfish-motion {
    left: 590px;
    top: 520px;
    width: 318px;
    --jelly-mid-x: 34px;
    --jelly-mid-y: -445px;
    --jelly-late-y: -710px;
    --jelly-end-x: 92px;
    --jelly-end-y: -1035px;
}

/* Windows/Desktop Firefox renders the accepted fallback Holo on a real
   760×370 logical plane instead of the normal 1050×500 plane. The moving
   consultant therefore needs the same proportional coordinate conversion as
   the established Firefox UI/bus fallback; otherwise the normal 1050×500
   dimensions make it appear oversized and clip it at the right edge.

   Horizontal factor: 760/1050 = 0.723809524
   Vertical factor:   370/500  = 0.740000000

   This changes Jellyfish geometry ONLY. The frozen Firefox Holo matrix and
   core Desktop layout remain untouched. */
.desktop-app.firefox-old-holo-test #desktop-holo
.jellyfish-easter-egg--desktop .jellyfish-motion {
    left: 427.048px;
    top: 384.800px;
    width: 230.171px;

    --jelly-x-16: 5.790px;
    --jelly-y-16: -111.000px;
    --jelly-x-30: 16.648px;
    --jelly-y-30: -255.300px;
    --jelly-mid-x: 24.610px;
    --jelly-mid-y: -329.300px;
    --jelly-late-x: 44.876px;
    --jelly-late-y: -525.400px;
    --jelly-end-x: 66.590px;
    --jelly-end-y: -765.900px;
}

.jellyfish-easter-egg--mobile .jellyfish-motion {
    left: 212px;
    top: 1025px;
    width: 350px;
    --jelly-mid-x: 38px;
    --jelly-mid-y: -665px;
    --jelly-late-y: -1120px;
    --jelly-end-x: 92px;
    --jelly-end-y: -1585px;
}

.jellyfish-easter-egg--mobile {
    z-index: 28;
}

.jellyfish-frame {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    max-width: none;
    object-fit: contain;
    pointer-events: none;
    user-select: none;
    opacity: 0;
    filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.18));
    will-change: opacity;
}

.jellyfish-frame--base {
    opacity: 1;
}

.jellyfish-easter-egg.is-playing .jellyfish-motion {
    animation: jellyfishRise 11000ms linear forwards;
}

.jellyfish-easter-egg.is-playing .jellyfish-frame--base {
    animation: jellyfishBaseFrames 11000ms ease-in-out forwards;
}

.jellyfish-easter-egg.is-playing .jellyfish-frame--pulse1 {
    animation: jellyfishPulseOne 11000ms ease-in-out forwards;
}

.jellyfish-easter-egg.is-playing .jellyfish-frame--pulse2 {
    animation: jellyfishPulseTwo 11000ms ease-in-out forwards;
}

/* The consultant rises more slowly into the Holo, pauses slightly lower than
   before and then continues upward. During the stop the DRAWING itself remains
   the base frame only; there is no pulse-frame cross-fade. The wrapper performs
   just a very small, slow local hover so the character does not look frozen.

   Total travel is deliberately slower (11.0 s). 43% -> 53% is ~1.1 s of
   quiet hover. Pulse1 belongs to the first ascent. Pulse2 begins only AFTER
   that stop, synchronized with the second ascent. The 15.2 s bass track may
   continue naturally for several seconds after the character has left. */
@keyframes jellyfishRise {
    0% {
        transform: translate3d(0, 0, 0) rotate(-1.10deg) scale(.965);
    }
    16% {
        transform: translate3d(var(--jelly-x-16), var(--jelly-y-16), 0) rotate(-.55deg) scale(.982);
    }
    30% {
        transform: translate3d(var(--jelly-x-30), var(--jelly-y-30), 0) rotate(.42deg) scale(1.000);
    }
    43% {
        transform: translate3d(var(--jelly-mid-x), var(--jelly-mid-y), 0)
                   rotate(.55deg) scale(1.006);
    }

    /* ~1.1 second stop: base artwork stays fixed; only a tiny slow hover. */
    48% {
        transform: translate3d(var(--jelly-mid-x), var(--jelly-mid-y), 0)
                   translate3d(1px, 1px, 0) rotate(.48deg) scale(1.006);
    }
    49% {
        transform: translate3d(var(--jelly-mid-x), var(--jelly-mid-y), 0)
                   translate3d(-1px, -2px, 0) rotate(.60deg) scale(1.006);
    }
    53% {
        transform: translate3d(var(--jelly-mid-x), var(--jelly-mid-y), 0)
                   translate3d(1px, 0, 0) rotate(.52deg) scale(1.006);
    }

    76% {
        transform: translate3d(var(--jelly-late-x), var(--jelly-late-y), 0)
                   rotate(-.30deg) scale(1.016);
    }
    100% {
        transform: translate3d(var(--jelly-end-x), var(--jelly-end-y), 0)
                   rotate(.70deg) scale(1.024);
    }
}

/* Smooth A -> B -> A before the stop. During the entire stop only A/base is
   visible. After the stop the second ascent starts A -> C -> A. */
@keyframes jellyfishBaseFrames {
    0%, 10%   { opacity: 1; }
    18%, 27%  { opacity: 0; }
    36%, 55%  { opacity: 1; }
    64%, 75%  { opacity: 0; }
    84%, 100% { opacity: 1; }
}

@keyframes jellyfishPulseOne {
    0%, 10%   { opacity: 0; }
    18%, 27%  { opacity: 1; }
    36%, 100% { opacity: 0; }
}

@keyframes jellyfishPulseTwo {
    0%, 55%   { opacity: 0; }
    64%, 75%  { opacity: 1; }
    84%, 100% { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .jellyfish-easter-egg.is-playing .jellyfish-motion,
    .jellyfish-easter-egg.is-playing .jellyfish-frame,
    .layer-jellyfish-gadget.is-audio-active,
    .mobile-layer-jellyfish-gadget.is-audio-active,
    .easter-discovery-soft.is-discovery-hint {
        animation-duration: 1ms !important;
    }
}

/* ---------------------------------------------------------
   CONTACT ALIEN
   Scene-level overlay, not clipped by the Holo top edge.
   It sits above the scene / Holo graphics but below menu controls,
   and pointer-events:none guarantees that navigation stays clickable.
   --------------------------------------------------------- */

.contact-easter-egg {
    position: absolute;
    z-index: 54;
    display: block;
    height: auto;
    max-width: none;
    pointer-events: none;
    user-select: none;
    opacity: 0;
    transform: translate3d(0, -118%, 0);
    transform-origin: top center;
    transition:
        transform 760ms cubic-bezier(0.22, 0.78, 0.24, 1.08),
        opacity 140ms linear;
    will-change: transform, opacity;
}

.contact-easter-egg.is-visible {
    opacity: 1;
    transform: translate3d(0, 0, 0);
}

/* Desktop: right-biased, but safely left of the hamburger rail. */
#desktop-scene > .contact-easter-egg {
    top: -16px;
    right: 232px;
    width: 298px;
}

/* Compact desktop: move the Contact alien into the free lane left of
   hamburger/menu/scroll rail. Normal desktop positioning stays untouched. */
.desktop-app.compact-nav #desktop-scene > .contact-easter-egg {
    right: 560px;
}

/* Mobile: right-biased with a fixed safety lane for the hamburger. */
#mobile-scene > .contact-easter-egg {
    top: -10px;
    right: 250px;
    width: 262px;
}

/* Menus stay visually dominant if open. */
.desktop-app.compact-nav:has(.holo.desktop-menu-open) #desktop-scene > .contact-easter-egg,
#mobile-app:has(.mobile-holo.mobile-menu-open) #mobile-scene > .contact-easter-egg {
    opacity: 0.26;
}

@media (prefers-reduced-motion: reduce) {
    .layer-foreground-gadget,
    .layer-bottle-gadget-soft,
    .layer-bottle-gadget,
    .mobile-layer-bottle-gadget-soft,
    .mobile-layer-bottle-gadget,
    .moon-gadget-layer,
    .moon-gadget-layer.is-pulsing,
    .jazz-easter-host,
    .jazz-easter-host.is-closing,
    .contact-easter-egg {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
    }
}

/* =========================================================
   TOUCH EQUIVALENT FOR DESKTOP-SCENE HOVER GAGS
   Phone landscape uses the desktop scene, so hover-only astronaut feedback
   receives a short tap state. Fine-pointer desktop hover remains unchanged.
   ========================================================= */

.layer-foreground-gadget.is-touch-active {
    opacity: 1;
}

.desktop-app.phone-landscape-scroll .hotspot-astronaut {
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}

@media (hover: none) {
    /* Prevent sticky :hover states after taps on touch browsers. */
    .hotspot-astronaut:hover ~ .layer-foreground-gadget,
    .hotspot-bottle:hover ~ .layer-bottle-gadget-soft {
        opacity: 0;
    }

    .layer-foreground-gadget.is-touch-active {
        opacity: 1;
    }
}


/* =========================================================
   V38 — DESKTOP EASTER EGGS STAY ACTIVE WITH COMPACT MENU OPEN

   The hotspots are on the scene, outside the Holo DOM. The compact menu
   occupies the right-hand Holo area; the playful scene hotspots remain
   usable instead of being globally disabled by menu state.
   Mobile menu blocking is intentionally unchanged.
   ========================================================= */
.desktop-app.compact-nav:has(.holo.desktop-menu-open) .scene-hotspot,
.desktop-app.compact-nav:has(.holo.desktop-menu-open) .hotspot-bottle,
.desktop-app.compact-nav:has(.holo.desktop-menu-open) .jazz-moon-hotspot {
    pointer-events: auto !important;
}

/* Keep the existing desktop bottle hover hint available as well. */
.desktop-app.compact-nav:has(.holo.desktop-menu-open)
.hotspot-bottle:hover ~ .layer-bottle-gadget-soft {
    opacity: 1 !important;
}

/* =========================================================
   PROJECTOR TEST MODE + BLACK HOLE — GADGETS 8

   Scene rule:
   - projector-effects-gadget.png is registered to the Desktop/Mobile scene
     and therefore stays on its normal projector layer.
   - testbild-gadget.png lives inside the already transformed Holo plane.
   - black-hole-gadget.png is one shared viewport transition asset.

   Browser rule:
   Test image and its transparent HTML hit areas share the SAME wrapper.
   Firefox therefore applies the existing Holo fallback perspective to both;
   no second perspective calculation or Firefox-only button geometry exists.
   ========================================================= */

.layer-projector-gadget-soft,
.mobile-layer-projector-gadget-soft {
    pointer-events: none;
    user-select: none;
    opacity: 0;
    transition: opacity 220ms ease-out;
}

.layer-projector-gadget-soft {
    z-index: 31;
    width: 1672px;
    height: 941px;
}

.mobile-layer-projector-gadget-soft {
    z-index: 31;
    width: 941px;
    height: 1672px;
}

.projector-hotspot {
    position: absolute;
    z-index: 89;
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    appearance: none;
    -webkit-appearance: none;
    cursor: pointer;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}

/* Safe fallbacks only. easter-eggs.js replaces these after the scene-bound
   projector gadget PNG has loaded by scanning its alpha bounds and keeping
   the lower portion (projector body + lowest rays). */
.projector-hotspot--desktop {
    left: 1070px;
    top: 678px;
    width: 220px;
    height: 150px;
}

.projector-hotspot--mobile {
    left: 585px;
    top: 1260px;
    width: 185px;
    height: 205px;
}

@media (any-hover: hover) and (any-pointer: fine) {
    .projector-hotspot--desktop:hover + .layer-projector-gadget-soft,
    .projector-hotspot--mobile:hover + .mobile-layer-projector-gadget-soft {
        opacity: 1;
    }
}

#mobile-app:has(.mobile-holo.mobile-menu-open) .projector-hotspot--mobile {
    pointer-events: none;
}

#mobile-app:has(.mobile-holo.mobile-menu-open) .mobile-layer-projector-gadget-soft {
    opacity: 0 !important;
    animation: none !important;
}

/* Test mode is an overlay WITHIN the Holo. Its child card keeps the natural
   image aspect ratio instead of stretching the image to 1050x500 / 760x1000.
   That keeps the generated artwork and the clickable button rectangles locked
   together in all responsive states, including the 760x370 Firefox fallback. */
.projector-test-overlay {
    position: absolute;
    inset: 0;
    z-index: 160;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    box-sizing: border-box;
    padding: 0;
    /* PNG carries the CRT glass. Transparent outer area shows the Holo beneath. */
    background: transparent;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 150ms linear, visibility 0s linear 150ms;
}

.projector-test-overlay.is-active {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity 150ms linear;
}

.projector-test-card {
    position: relative;
    flex: 0 0 auto;
    height: 100%;
    width: auto;
    max-width: 100%;
    max-height: 100%;
    overflow: hidden;
    background: transparent;
    transform: translateZ(0);
    transform-origin: center center;
}

.projector-test-overlay--desktop .projector-test-card {
    aspect-ratio: 1672 / 941;
}

.projector-test-overlay--mobile .projector-test-card {
    aspect-ratio: 941 / 1672;
}

.projector-test-image {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    max-width: none;
    object-fit: fill;
    pointer-events: none;
    user-select: none;
}

.projector-test-button {
    position: absolute;
    z-index: 4;
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    appearance: none;
    -webkit-appearance: none;
    cursor: pointer;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}


/* GADGETS 10: harden projector test transparency.
   The test screen should sit directly on the Holo. No wrapper/card/frame
   around it may contribute a black fill. Keep this Firefox-safe:
   transparency only, no geometry changes. */
.projector-test-overlay,
.projector-test-card,
.projector-test-image,
.projector-test-button {
    background-color: transparent !important;
}

.projector-test-overlay::before,
.projector-test-overlay::after,
.projector-test-card::before,
.projector-test-card::after {
    content: none !important;
    background: transparent !important;
    box-shadow: none !important;
}

.projector-test-overlay,
.projector-test-card {
    box-shadow: none !important;
    outline: none !important;
}

/* Desktop generated test artwork: visible controls are in the lower band.
   Hit areas are intentionally a little larger than the printed rectangles. */
.projector-test-overlay--desktop .projector-test-exit {
    left: 25%;
    top: 75.5%;
    width: 25%;
    height: 18%;
}

.projector-test-overlay--desktop .projector-test-blacklevel {
    left: 49.5%;
    top: 75.5%;
    width: 32.5%;
    height: 18%;
}

/* Mobile generated test artwork: the two large touch controls are stacked. */
.projector-test-overlay--mobile .projector-test-exit {
    left: 5.5%;
    top: 66.5%;
    width: 89%;
    height: 12.5%;
}

.projector-test-overlay--mobile .projector-test-blacklevel {
    left: 5.5%;
    top: 78%;
    width: 89%;
    height: 12.5%;
}

.projector-test-overlay.is-active .projector-test-card {
    /* Slower CRT acquisition; only opacity/filter/transform for Firefox safety. */
    animation: projectorTestEnter 1180ms linear both;
}

.projector-test-overlay.is-corrupting .projector-test-card {
    animation: projectorTestCorrupt 620ms steps(2, end) both;
}

@keyframes projectorTestEnter {
    0%   { opacity: 0;   filter: brightness(.22) contrast(1.75); transform: translate3d(-2px, 0, 0) scaleY(.965); }
    8%   { opacity: .88; filter: brightness(1.65) contrast(1.24); transform: translate3d(3px, -1px, 0) scaleY(1.010); }
    16%  { opacity: .28; filter: brightness(.44) contrast(1.55); transform: translate3d(-2px, 1px, 0) scaleY(.990); }
    28%  { opacity: .78; filter: brightness(1.18) contrast(1.30); transform: translate3d(2px, 0, 0) scaleY(1.004); }
    41%  { opacity: .46; filter: brightness(.64) contrast(1.48); transform: translate3d(-1px, 1px, 0); }
    57%  { opacity: .93; filter: brightness(1.24) contrast(1.18); transform: translate3d(1px, -1px, 0); }
    71%  { opacity: .68; filter: brightness(.82) contrast(1.32); transform: translate3d(-1px, 0, 0); }
    86%  { opacity: .96; filter: brightness(1.07) contrast(1.08); transform: none; }
    100% { opacity: 1;   filter: none; transform: none; }
}

@keyframes projectorTestCorrupt {
    0%   { filter: none; transform: none; opacity: 1; }
    14%  { filter: contrast(1.75) brightness(1.25) hue-rotate(18deg); transform: translate3d(4px, -2px, 0) scaleX(1.008); }
    31%  { filter: contrast(.7) brightness(.42) saturate(2); transform: translate3d(-6px, 2px, 0) scaleY(.985); }
    49%  { filter: contrast(2.0) brightness(1.5) hue-rotate(-22deg); transform: translate3d(3px, 0, 0) scaleY(1.012); }
    67%  { filter: brightness(.2) contrast(2.3); transform: translate3d(-2px, 1px, 0); }
    83%  { filter: brightness(1.6) contrast(1.45); transform: translate3d(2px, -1px, 0); }
    100% { filter: brightness(.68) contrast(1.65); transform: none; opacity: .92; }
}

/* Full viewport bridge to /black-hole/. The image itself starts at the
   BLACK LEVEL button centre. JS owns the non-linear scale keyframes so the
   growth can accelerate much more strongly than a normal CSS ease. */
.black-hole-transition {
    position: fixed;
    inset: 0;
    z-index: 2147483000;
    overflow: hidden;
    pointer-events: all;
    background: transparent;
}

.black-hole-transition-backdrop {
    position: absolute;
    inset: 0;
    background: #000;
    opacity: 0;
    pointer-events: none;
}

.black-hole-transition-image {
    position: fixed;
    display: block;
    width: min(42vmin, 380px);
    height: auto;
    max-width: none;
    pointer-events: none;
    user-select: none;
    transform: translate(-50%, -50%) scale(.04) rotate(0deg);
    transform-origin: 50% 50%;
    will-change: transform, opacity;
}

@media (prefers-reduced-motion: reduce) {
    .projector-test-overlay.is-active .projector-test-card,
    .projector-test-overlay.is-corrupting .projector-test-card {
        animation-duration: 1ms !important;
    }
}

.layer-projector-gadget-soft.is-suppressed,
.mobile-layer-projector-gadget-soft.is-suppressed {
    opacity: 0 !important;
    animation: none !important;
}



/* ---------------------------------------------------------
   SHIP FOLLOW-UP — EMPTY BEER / SPACE-TANKE DELIVERY
   Separate from the frozen launch animation below.
   --------------------------------------------------------- */

#desktop-scene.bottle-overlay-active .hotspot-astronaut {
    pointer-events: none !important;
}

#desktop-scene.bottle-overlay-active .layer-foreground-gadget {
    opacity: 0 !important;
}

.layer-bierkiste-gadget {
    position: absolute;
    inset: 0;
    z-index: 88; /* above normal scene, below front dust at z90 */
    width: 1672px;
    height: 941px;
    max-width: none;
    pointer-events: none;
    user-select: none;
    opacity: 0;
    visibility: hidden;
    transition:
        opacity var(--beer-crate-fade-in, 2300ms) ease-out,
        visibility 0s linear var(--beer-crate-fade-in, 2300ms);
}

.layer-bierkiste-gadget.is-visible {
    opacity: 1;
    visibility: visible;
    transition: opacity var(--beer-crate-fade-in, 2300ms) ease-out;
}

.layer-bierkiste-gadget.is-fading-out {
    opacity: 0;
    visibility: visible;
    transition: opacity var(--beer-crate-fade-out, 2100ms) ease-in-out;
}

.layer-bierkiste-gadget.is-hidden-hard {
    opacity: 0 !important;
    visibility: hidden !important;
    transition: none !important;
}

.beercrate-hotspot {
    position: absolute;
    z-index: 89; /* below front dust; active only after launch dust is gone */
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    appearance: none;
    -webkit-appearance: none;
    cursor: pointer;
    opacity: 0;
    pointer-events: none;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}

.beercrate-hotspot.is-active {
    pointer-events: auto;
}

.ship-beer-overlay {
    position: absolute;
    inset: 0;
    z-index: 520;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 5% 6%;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    background: rgba(2, 13, 25, .62);
    transition: opacity 180ms linear, visibility 0s linear 180ms;
}

.ship-beer-overlay.is-active {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity 180ms linear;
}

.ship-beer-card {
    width: min(88%, 860px);
    padding: 5% 5.5%;
    border: 1px solid rgba(126, 230, 255, .48);
    background: rgba(4, 20, 37, .88);
    box-shadow: 0 0 26px rgba(72, 207, 255, .18), inset 0 0 24px rgba(105, 233, 255, .07);
    color: rgba(231, 251, 255, .98);
    font-family: "Courier New", Courier, monospace;
    text-shadow: 0 0 9px rgba(116, 226, 255, .42);
}

.ship-beer-status {
    margin: 0 0 13px;
    font-size: 29px;
    line-height: 1.15;
    letter-spacing: .035em;
}

.ship-beer-status--alert,
.ship-beer-status--empty {
    font-weight: 700;
}

.ship-beer-status--inbound {
    display: none;
    min-height: 160px;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 16px;
    text-align: center;
}

.ship-beer-status--inbound span {
    font-size: 28px;
}

.ship-beer-status--inbound strong {
    font-size: 42px;
    letter-spacing: .08em;
    animation: deliveryInboundPulse 780ms ease-in-out infinite alternate;
}

.ship-beer-overlay.is-inbound .ship-beer-status:not(.ship-beer-status--inbound),
.ship-beer-overlay.is-inbound .ship-beer-actions {
    display: none;
}

.ship-beer-overlay.is-inbound .ship-beer-status--inbound {
    display: flex;
}

.ship-beer-actions {
    display: grid;
    grid-template-columns: 1fr;
    gap: 12px;
    margin-top: 28px;
}

.ship-beer-button {
    width: 100%;
    padding: 15px 17px;
    border: 1px solid rgba(128, 231, 255, .46);
    background: rgba(7, 29, 48, .74);
    color: rgba(236, 252, 255, .98);
    font: inherit;
    font-size: 19px;
    line-height: 1.25;
    text-align: left;
    cursor: pointer;
}

.ship-beer-button:hover,
.ship-beer-button:focus-visible {
    outline: none;
    border-color: rgba(172, 245, 255, .82);
    background: rgba(13, 48, 76, .88);
    box-shadow: 0 0 18px rgba(83, 218, 255, .18);
}

@keyframes deliveryInboundPulse {
    from { opacity: .58; text-shadow: 0 0 8px rgba(116, 226, 255, .36); }
    to   { opacity: 1; text-shadow: 0 0 18px rgba(116, 226, 255, .82); }
}

/* Landing is intentionally NOT the takeoff animation in reverse. It comes in
   briskly, slows strongly near the pad, hovers briefly, then settles. */
.layer-ship-gadget.is-landing {
    opacity: 1;
    visibility: visible !important;
    transition: none !important;
    animation: shipBeerLanding 5900ms linear forwards;
}

.layer-ship-gadget.is-landing-cleared {
    opacity: 0 !important;
    visibility: hidden !important;
    animation: none !important;
    transition: none !important;
}

@keyframes shipBeerLanding {
    0%   { transform: translate3d(172px, -1040px, 0) scale(.30); opacity: .92; }
    16%  { transform: translate3d(142px, -785px, 0) scale(.43); opacity: 1; }
    34%  { transform: translate3d(103px, -500px, 0) scale(.62); }
    52%  { transform: translate3d(64px, -270px, 0) scale(.78); }
    68%  { transform: translate3d(36px, -125px, 0) scale(.90); }
    80%  { transform: translate3d(19px, -57px, 0) scale(.96); }
    87%  { transform: translate3d(12px, -34px, 0) scale(.978); }
    92%  { transform: translate3d(10px, -32px, 0) scale(.980); }
    96%  { transform: translate3d(7px, -18px, 0) scale(.989); }
    100% { transform: translate3d(0, 0, 0) scale(1); }
}

.layer-ship-gadget-dust.is-landing-dust {
    opacity: 0;
    visibility: visible !important;
    animation: shipLandingRearDust 4050ms ease-out forwards !important;
}

.layer-ship-front-dust.is-landing-dust {
    opacity: 0;
    visibility: visible !important;
    animation: shipLandingFrontDust 4050ms ease-out forwards !important;
}

.layer-ship-gadget-dust.is-landing-fading {
    animation: shipLandingRearDustFade 3700ms ease-in-out forwards !important;
}

.layer-ship-front-dust.is-landing-fading {
    animation: shipLandingFrontDustFade 4300ms cubic-bezier(.20,.42,.22,1) forwards !important;
}

@keyframes shipLandingRearDust {
    0%   { opacity: 0; transform: scale(.99); }
    30%  { opacity: .38; transform: scale(1.02); }
    66%  { opacity: .84; transform: scale(1.065); }
    100% { opacity: .96; transform: scale(1.10); }
}

@keyframes shipLandingFrontDust {
    0%   { opacity: 0; transform: translate3d(0, 185px, 0) scale(.99); }
    32%  { opacity: .34; transform: translate3d(0, 110px, 0) scale(1.02); }
    66%  { opacity: .76; transform: translate3d(0, 38px, 0) scale(1.055); }
    100% { opacity: .97; transform: translate3d(0, 0, 0) scale(1.09); }
}

@keyframes shipLandingRearDustFade {
    0%   { opacity: .96; transform: scale(1.10); }
    45%  { opacity: .66; transform: scale(1.12); }
    76%  { opacity: .28; transform: scale(1.14); }
    100% { opacity: 0; transform: scale(1.16); }
}

@keyframes shipLandingFrontDustFade {
    0%   { opacity: .97; transform: translate3d(0, 0, 0) scale(1.09); }
    30%  { opacity: .80; transform: translate3d(0, -4px, 0) scale(1.105); }
    62%  { opacity: .48; transform: translate3d(0, -9px, 0) scale(1.12); }
    84%  { opacity: .20; transform: translate3d(0, -13px, 0) scale(1.135); }
    100% { opacity: 0; transform: translate3d(0, -17px, 0) scale(1.15); }
}

/* ---------------------------------------------------------
   SHIP LAUNCH — DESKTOP SCENE ONLY

   The launch artwork uses a wider 2200 × 941 source canvas. Inspection of the
   supplied PNGs shows that the craft itself is registered at the LEFT side of
   that canvas; the additional transparent area extends to the right. Keep all
   ship-state layers on the same x=0 registration so normal/hover/ignition/dust
   remain pixel-locked without touching desktop/Firefox layout rules.
   --------------------------------------------------------- */

/* Normal ship + launch states share the same extended 2200 × 941 canvas.
   The two diagnostic placements were x=-528 px (right:0: too far left) and
   x=0 (too far right). Use one shared registration halfway between them.
   Keeping this as a single scene-coordinate offset makes later fine-tuning a
   one-value change without editing any PNG or Firefox/viewport geometry. */
.layer-ship,
.ship-scene-layer {
    position: absolute;
    left: -264px !important;
    right: auto !important;
    top: 0;
    bottom: auto;
    display: block;
    width: 2200px !important;
    height: 941px !important;
    max-width: none;
    pointer-events: none;
    user-select: none;
    transform-origin: 17% 42%;
}

.layer-ship {
    z-index: 5;
}

.layer-ship.is-departed {
    opacity: 0;
}

.layer-ship-gadget-soft {
    z-index: 6;
    opacity: 0;
    transition: opacity 190ms ease-out;
}

.layer-ship-gadget-soft.is-visible {
    opacity: 1;
}

/* During the launch countdown the soft version pulses several times.
   This is deliberately opacity-only so normal scene registration and the
   Firefox layout path stay untouched. */
.layer-ship-gadget-soft.is-countdown-pulsing {
    opacity: 0;
    animation: shipSoftCountdownPulse 1650ms ease-in-out infinite;
}

.layer-ship-gadget {
    z-index: 6;
    opacity: 0;
    will-change: transform, opacity;
    transition: opacity 2400ms cubic-bezier(.32, 0, .24, 1);
}

/* Starts at countdown 2 and reaches full ignition around ZERO. */
.layer-ship-gadget.is-countdown-igniting,
.layer-ship-gadget.is-ignited {
    opacity: 1;
}

.layer-ship-gadget.is-launching {
    opacity: 1;
    animation: shipTakeoff 5200ms cubic-bezier(.36,.02,.34,1) forwards;
}

/* Permanent end state after lift-off. Apply this BEFORE removing the launch
   animation so the ignition artwork cannot snap back to its start position
   while the normal opacity transition is still alive. */
.layer-ship-gadget.is-cleared,
.layer-ship-gadget-soft.is-cleared,
.layer-ship-gadget-dust.is-cleared {
    opacity: 0 !important;
    visibility: hidden !important;
    animation: none !important;
    transition: none !important;
    pointer-events: none !important;
}

/* Ground/engine dust stays below the Holo and the rest of the scene stack. */
.layer-ship-gadget-dust {
    z-index: 7;
    opacity: 0;
    transform-origin: 21% 72%;
    will-change: transform, opacity;
}

.layer-ship-gadget-dust.is-active {
    animation: shipRearDustBuild 4700ms ease-out forwards;
}

.layer-ship-gadget-dust.is-fading {
    animation: shipRearDustFade 3200ms cubic-bezier(.22,.46,.26,1) forwards !important;
}

/* Full-scene foreground dust intentionally sits above ALL scene layers,
   but remains inside the scene stacking context so site navigation stays usable. */
.layer-ship-front-dust {
    z-index: 90;
    opacity: 0;
    transform: translate3d(0, 220px, 0) scale(1.00);
    transform-origin: center bottom;
    pointer-events: none;
    user-select: none;
    will-change: transform, opacity;
}

.layer-ship-front-dust.is-active {
    animation: shipFrontDustBuild 4700ms ease-out forwards;
}

.layer-ship-front-dust.is-fading {
    animation: shipFrontDustFade 4200ms cubic-bezier(.20,.42,.22,1) forwards !important;
}

/* The logo hotspot intentionally sits below the astronaut hotspot (z70).
   Where the two areas touch, the established astronaut control therefore wins. */
.ship-hotspot {
    position: absolute;
    /* Expanded hit area around the visible Nø Jazz Today logo on the ship.
       Source-art logo is around x 406..509 / y 184..226 on the 2200 canvas;
       with the shared -264 px ship registration this maps here. */
    left: 75px;
    top: 185px;
    z-index: 65;
    width: 190px;
    height: 122px;
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    appearance: none;
    -webkit-appearance: none;
    cursor: pointer;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}

.ship-hotspot.is-disabled {
    pointer-events: none;
}

/* Countdown lives INSIDE the established Holo plane, hence inherits its exact
   perspective transform and the existing Firefox fallback automatically. */
.ship-countdown-overlay {
    position: absolute;
    inset: 0;
    z-index: 500;
    display: flex !important;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    color: rgba(232, 252, 255, .99);
    font-family: "Courier New", Courier, monospace;
    text-align: center;
    text-shadow:
        0 0 12px rgba(150, 238, 255, .88),
        0 0 34px rgba(70, 190, 255, .48);
    background: rgba(2, 16, 28, .62);
    transform: translateZ(0);
    transition: opacity 160ms linear, visibility 0s linear 160ms;
}

.ship-countdown-overlay.is-active {
    opacity: 1 !important;
    visibility: visible !important;
    transition: opacity 160ms linear;
}

.ship-countdown-number {
    min-width: 4ch;
    font-size: 138px;
    font-weight: 700;
    line-height: .90;
    letter-spacing: .02em;
}

.ship-countdown-number.is-zero {
    font-size: 78px;
    letter-spacing: .12em;
}

@keyframes shipSoftCountdownPulse {
    0%, 100% { opacity: 0; }
    18%      { opacity: .22; }
    46%      { opacity: 1; }
    72%      { opacity: .34; }
}

@keyframes shipTakeoff {
    0%   { transform: translate3d(0, 0, 0) scale(1); }
    10%  { transform: translate3d(8px, -18px, 0) scale(.985); }
    28%  { transform: translate3d(34px, -150px, 0) scale(.91); }
    50%  { transform: translate3d(74px, -365px, 0) scale(.75); }
    72%  { transform: translate3d(118px, -620px, 0) scale(.57); }
    88%  { transform: translate3d(154px, -825px, 0) scale(.42); }
    100% { transform: translate3d(188px, -1080px, 0) scale(.28); }
}

@keyframes shipRearDustBuild {
    0%   { opacity: 0; transform: scale(1); }
    16%  { opacity: .82; transform: scale(1.018); }
    48%  { opacity: 1; transform: scale(1.055); }
    100% { opacity: .94; transform: scale(1.095); }
}

@keyframes shipFrontDustBuild {
    0%   { opacity: 0; transform: translate3d(0, 220px, 0) scale(1); }
    18%  { opacity: .46; transform: translate3d(0, 135px, 0) scale(1.018); }
    48%  { opacity: .82; transform: translate3d(0, 48px, 0) scale(1.045); }
    100% { opacity: .96; transform: translate3d(0, 0, 0) scale(1.075); }
}

@keyframes shipRearDustFade {
    /* Start EXACTLY where shipRearDustBuild ends: no scale snap. */
    0%   { opacity: .94; transform: scale(1.095); }
    42%  { opacity: .62; transform: scale(1.102); }
    72%  { opacity: .28; transform: scale(1.116); }
    100% { opacity: 0;   transform: scale(1.130); }
}

@keyframes shipFrontDustFade {
    /* Start EXACTLY where shipFrontDustBuild ends. The old fade began at
       translateY(8px) / scale(1.068), which made the full-screen dust layer
       jump downward and looked like the whole background moved. */
    0%   { opacity: .96; transform: translate3d(0, 0, 0) scale(1.075); }
    28%  { opacity: .76; transform: translate3d(0, -2px, 0) scale(1.082); }
    58%  { opacity: .47; transform: translate3d(0, -5px, 0) scale(1.095); }
    82%  { opacity: .18; transform: translate3d(0, -9px, 0) scale(1.105); }
    100% { opacity: 0;   transform: translate3d(0, -13px, 0) scale(1.115); }
}

@media (prefers-reduced-motion: reduce) {
    .layer-ship-gadget.is-launching {
        animation-duration: 1900ms;
    }
    .layer-ship-gadget-dust.is-active,
    .layer-ship-front-dust.is-active {
        animation-duration: 1800ms;
    }
    .layer-ship-gadget-dust.is-fading,
    .layer-ship-front-dust.is-fading {
        animation-duration: 1800ms !important;
    }
}
