/*
 * The easter eggs (on top of style.css, on every page): Sir Glancelot with his
 * quotes & somersaults, and the spells, which are cast by searching for them.
 */

/* Sir Glancelot, peeking over the castle wall in the bottom right corner. */

.peeker {
    position: fixed;
    right: 1.5rem;
    bottom: calc(var(--castle-wall) - 12px);
    z-index: -2;
    width: 112px;
    height: 112px;
    transform: translateY(70%);
    transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
    cursor: pointer;
}

.peeker:hover {
    transform: translateY(15%);
}

.peeker-avatar {
    display: block;
    transform-origin: bottom center;
}

/* The size of the corner (the one in a page's title has its own). */
.peeker .peeker-avatar {
    width: 100%;
    height: 100%;
}

.peeker:hover .peeker-avatar {
    animation: wave 0.9s ease-in-out;
}

/* A little wiggle, as if Sir Glancelot waves. */
@keyframes wave {
    0%, 100% {
        transform: rotate(0);
    }

    20%, 60% {
        transform: rotate(-7deg);
    }

    40%, 80% {
        transform: rotate(7deg);
    }
}

/* The easter egg: a somersault on every fifth click. */
.peeker.is-somersaulting .peeker-avatar {
    transform-origin: center;
    animation: somersault 0.9s ease-in-out;
}

@keyframes somersault {
    0% {
        transform: translateY(0) rotate(0);
    }

    50% {
        transform: translateY(-60px) rotate(180deg);
    }

    100% {
        transform: translateY(0) rotate(360deg);
    }
}

.peeker-bubble {
    position: absolute;
    right: 85%;
    bottom: 75%;
    padding: 0.35rem 0.7rem;
    border: 1px solid var(--border);
    border-radius: 10px 10px 0;
    background: var(--surface);
    box-shadow: 0 8px 24px color-mix(in srgb, var(--cd-black) 45%, transparent);
    color: var(--text);
    font-size: 0.8rem;
    white-space: nowrap;
    opacity: 0;
    transform: translateY(4px);
    transition: opacity 0.2s, transform 0.2s;
    pointer-events: none;
}

.peeker:hover .peeker-bubble {
    opacity: 1;
    transform: translateY(0);
    transition-delay: 0.25s;
}

@media (prefers-reduced-motion: reduce) {
    .peeker,
    .peeker-bubble {
        transition: none;
    }

    .peeker:hover .peeker-avatar {
        animation: none;
    }
}

/* Easter eggs: spells, which are cast by searching for them. */

/* The messages of the spells, big in the middle of the screen. */
.spell-message {
    position: fixed;
    top: 50%;
    left: 50%;
    z-index: 40;
    width: max-content;
    max-width: min(60rem, calc(100% - 2rem));
    color: var(--cd-orange-80);
    font-size: clamp(1.25rem, 3vw, 2.6rem);
    font-weight: 600;
    line-height: 1.25;
    text-align: center;
    text-shadow: 0 0 24px color-mix(in srgb, var(--cd-orange-70) 60%, transparent), 0 2px 4px color-mix(in srgb, var(--cd-black) 80%, transparent);
    white-space: pre-line;
    text-wrap: balance;
    transform: translate(-50%, -50%);
    pointer-events: none;
    animation: message-fade var(--duration, 3s) forwards;
}

@keyframes message-fade {
    0% {
        opacity: 0;
    }

    15%, 80% {
        opacity: 1;
    }

    100% {
        opacity: 0;
        visibility: hidden;
    }
}

/* A dark overlay, which dims everything except the spell's effect & messages. */
.spell-overlay {
    position: fixed;
    inset: 0;
    z-index: 25;
    background: color-mix(in srgb, var(--cd-black) 60%, transparent);
    pointer-events: none;
    animation: overlay-fade var(--duration, 4s) forwards;
}

@keyframes overlay-fade {
    0% {
        opacity: 0;
    }

    10%, 85% {
        opacity: 1;
    }

    100% {
        opacity: 0;
        visibility: hidden;
    }
}

/* Excalibur: the sword is pulled from the stone, and triumphantly wiggled. */
.excalibur {
    position: fixed;
    top: 20%;
    left: 50%;
    z-index: 30;
    display: flex;
    flex-direction: column;
    align-items: center;
    font-size: 9rem;
    line-height: 1;
    transform: translateX(-50%);
    pointer-events: none;
    animation: spell-fade 4s forwards;
}

.excalibur-sword {
    display: block;

    /* The blade of the emoji points to the top right, so it's turned into the stone. */
    rotate: -45deg;
    animation: sword-pull 3s ease-in-out forwards;
}

/* In front of the (rotated, thus layered) sword. */
.excalibur-stone {
    position: relative;
    z-index: 1;
    margin-top: -3rem;
}

/* Stuck at first, then pulled out & wiggled in the air. */
@keyframes sword-pull {
    0%, 8% {
        translate: 0 0;
        rotate: -45deg;
    }

    13% {
        rotate: -49deg;
    }

    18% {
        rotate: -41deg;
    }

    23% {
        rotate: -48deg;
    }

    28% {
        translate: 0 0;
        rotate: -45deg;
    }

    42% {
        translate: 0 -0.9em;
        rotate: -45deg;
    }

    52% {
        rotate: -57deg;
    }

    62% {
        rotate: -33deg;
    }

    72% {
        rotate: -54deg;
    }

    82% {
        rotate: -36deg;
    }

    100% {
        translate: 0 -0.9em;
        rotate: -45deg;
    }
}

@keyframes spell-fade {
    0%, 80% {
        opacity: 1;
    }

    100% {
        opacity: 0;
        visibility: hidden;
    }
}

/*
 * The holy grail: all bookmarks (or watchtower rows) shine. They're lifted
 * above the dark overlay, like in a spotlight.
 */
.bookmark.is-shining,
.watchtower-row.is-shining {
    position: relative;
    z-index: 26;
    animation: shine 1.2s ease-out var(--shine-delay, 0s);
}

@keyframes shine {
    0%, 100% {
        box-shadow: none;
    }

    30% {
        background: color-mix(in srgb, var(--cd-yellow-80) 12%, transparent);
        box-shadow: 0 0 0 1px var(--cd-yellow-70), 0 0 24px color-mix(in srgb, var(--cd-yellow-80) 45%, transparent);
    }
}

/* The holy hand grenade: a flash, a shaking page & a burst of sparks. */
.grenade-flash {
    position: fixed;
    inset: 0;
    z-index: 29;
    background: radial-gradient(
        circle at 50% 50%,
        var(--cd-white),
        color-mix(in srgb, var(--cd-orange-90) 95%, transparent) 12%,
        color-mix(in srgb, var(--cd-orange-70) 70%, transparent) 30%,
        color-mix(in srgb, var(--cd-orange-60) 35%, transparent) 55%,
        transparent 80%
    );
    pointer-events: none;
    animation: flash 1s ease-out forwards;
}

@keyframes flash {
    0% {
        opacity: 0;
        transform: scale(0.4);
    }

    8% {
        opacity: 1;
        transform: scale(1);
    }

    100% {
        opacity: 0;
        transform: scale(1.3);
    }
}

/* The shock wave, a ring which expands from the middle of the viewport. */
.grenade-wave {
    position: fixed;
    top: 50%;
    left: 50%;
    z-index: 30;
    width: 40vmin;
    height: 40vmin;
    margin: -20vmin 0 0 -20vmin;
    border: 6px solid var(--cd-orange-90);
    border-radius: 50%;
    box-shadow: 0 0 40px 10px color-mix(in srgb, var(--cd-orange-70) 70%, transparent), inset 0 0 40px 10px color-mix(in srgb, var(--cd-orange-70) 50%, transparent);
    pointer-events: none;
    animation: wave-expand 0.9s ease-out forwards;
}

@keyframes wave-expand {
    0% {
        opacity: 1;
        transform: scale(0.1);
    }

    100% {
        opacity: 0;
        transform: scale(3.5);
    }
}

/*
 * The page shakes, but not the body itself, as a transformed body would move
 * the fixed effects & messages (it would be their containing block).
 */
body.is-shaking > :not(.spell-message, .spell-overlay, .grenade-flash, .grenade-wave, .burst-spark) {
    animation: shake 0.5s;
}

@keyframes shake {
    0%, 100% {
        translate: 0 0;
    }

    20% {
        translate: -8px 4px;
    }

    40% {
        translate: 7px -5px;
    }

    60% {
        translate: -5px 3px;
    }

    80% {
        translate: 4px -2px;
    }
}

.burst-spark {
    position: fixed;
    top: var(--y);
    left: var(--x);
    z-index: 30;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--cd-orange-100);
    box-shadow: 0 0 10px 3px color-mix(in srgb, var(--cd-orange-70) 90%, transparent), 0 0 22px 6px color-mix(in srgb, var(--cd-orange-60) 50%, transparent);
    pointer-events: none;
    animation: burst var(--duration) ease-out forwards;
}

@keyframes burst {
    0% {
        opacity: 1;
        transform: translate(0, 0) scale(1);
    }

    100% {
        opacity: 0;
        transform: translate(var(--dx), var(--dy)) scale(0.3);
    }
}
