/* ======================================== */
/* CUSTOM CURSOR */
/* ======================================== */

/*
 * The cursor blends with mix-blend-mode: exclusion, and an element can only
 * blend with what is painted beneath it *inside its own stacking context*.
 *
 * The theme never painted a background: html, body and the sections were all
 * transparent, and the white came from the browser's default canvas — which is
 * painted below the root stacking context, outside the blend group. Exclusion
 * had nothing to mix with, so the dot stayed plain white and vanished on any
 * white section.
 *
 * Both rules are needed. A background on body alone would propagate up to the
 * canvas while html is transparent, landing outside the blend group again;
 * giving html one keeps the propagation busy so body paints as a real element.
 * Visually this is a no-op — it repaints the same white the canvas gave for
 * free — so it stays here, with the feature that requires it.
 */
html,
body {
    background-color: var(--surface, #fff);
}

/*
 * On coarse pointers js/cursor.js bails out and never positions the layers, so
 * without this they would fall back into normal flow and pile up at the end of
 * the page. Kept out of the document entirely until a fine pointer is present.
 */
.eg-cursor {
    display: none;
}

/*
 * Everything lives behind (pointer: fine): on touch and other coarse pointers
 * there is nothing to follow, so the layers are never rendered at all.
 *
 * Positioning uses physical left/top on purpose, against the logical-property
 * rule: clientX/clientY are always measured from the physical viewport edges,
 * while inset-inline-start would flip to the right edge in an RTL language and
 * mirror the cursor. Sizes stay logical.
 */

@media (pointer: fine) {
    .eg-cursor {
        display: block;
    }

    /* Applied by js/cursor.js, never statically: if the script fails to run,
       the user keeps the system cursor instead of having none at all.

       The descendant rule is not redundant. cursor inherits, but the theme
       declares cursor: pointer outright on links, buttons and fields, and an
       explicit declaration on a descendant always beats an inherited value —
       the real pointer resurfaced on exactly the elements the dot reacts to.
       Those rules are scattered and specific, so !important is what it takes. */
    body.eg-cursor-active,
    body.eg-cursor-active * {
        cursor: none !important;
    }

    .eg-cursor > * {
        position: fixed;
        left: 0;
        top: 0;
        z-index: 11000;
        transform: translate(-50%, -50%);
        will-change: transform;
        pointer-events: none;
        mix-blend-mode: exclusion;
        opacity: 0;
    }

    /* The layers stay invisible until the first real mouse move, so nothing
       sits parked at 0,0 on load. */
    .eg-cursor.is-visible .eg-cursor-dot,
    .eg-cursor.is-visible .eg-cursor-label[data-cursor-text],
    .eg-cursor.is-visible .eg-cursor-play.is-active,
    .eg-cursor.is-visible .eg-cursor-timer.is-active {
        opacity: 1;
    }

    /* ---------- Dot ---------- */

    .eg-cursor-dot {
        inline-size: 24px;
        block-size: 24px;
        border-radius: 99999px;
        background-color: #fff;
        backdrop-filter: blur(8px);
        transition:
            inline-size 0.2s cubic-bezier(0.4, 0, 0.2, 1),
            block-size 0.2s cubic-bezier(0.4, 0, 0.2, 1),
            opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);
    }

    /* Only size and opacity animate. Position never does: that is what makes the
       dot track the pointer exactly instead of lagging behind it. */

    .eg-cursor.is-visible .eg-cursor-dot[data-cursor-state="shrunk"],
    .eg-cursor.is-visible .eg-cursor-dot[data-cursor-state="enlarged"] {
        opacity: 0.5;
    }

    .eg-cursor-dot[data-cursor-state="shrunk"] {
        inline-size: 6px;
        block-size: 6px;
    }

    .eg-cursor-dot[data-cursor-state="enlarged"] {
        inline-size: 30px;
        block-size: 30px;
    }

    /* Pill: js sets the inline-size from the measured label. */
    .eg-cursor-dot[data-cursor-state="text"] {
        border-radius: 12px;
    }

    /* Scoped through .is-visible to outweigh the rule that reveals the dot:
       .eg-cursor.is-visible .eg-cursor-dot would otherwise win on specificity
       and the dot would punch an exclusion hole through the play icon. */
    .eg-cursor.is-visible .eg-cursor-dot.is-hidden {
        opacity: 0;
    }

    /* ---------- Label ---------- */

    .eg-cursor-label {
        padding-inline: 0.75rem;
        color: #fff;
        white-space: nowrap;
        transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1);
    }

    .eg-cursor-label span {
        display: block;
        inline-size: max-content;
        transform: scale(0.5);
        transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
    }

    .eg-cursor-label[data-cursor-text] span {
        transform: scale(1);
    }

    /* ---------- Play ---------- */

    .eg-cursor-play,
    .eg-cursor-timer {
        inline-size: 100px;
        block-size: 100px;
        border-radius: 99999px;
        color: #fff;
        transition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1);
    }

    .eg-cursor-play {
        backdrop-filter: blur(8px);
    }

    .eg-cursor-play svg {
        inline-size: 100%;
        block-size: 100%;
        transform: scale(0.25);
        transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
    }

    .eg-cursor-play.is-active svg {
        transform: scale(1);
    }

    /* ---------- Timer ---------- */

    /* translate(-50%, -50%) already occupies the transform, so the scale is
       folded into it rather than set on its own. */
    .eg-cursor-timer {
        transform: translate(-50%, -50%) scale(0.25);
        transition:
            transform 0.4s cubic-bezier(0.4, 0, 0.2, 1),
            opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1);
    }

    .eg-cursor-timer.is-active {
        transform: translate(-50%, -50%) scale(1);
    }

    .eg-cursor-timer svg {
        inline-size: 100%;
        block-size: 100%;
        transform: rotate(-90deg);
    }

    /* r=25 → circumference 2π × 25 ≈ 157.08. Fully offset means an empty pie. */
    .eg-cursor-timer circle {
        stroke-dasharray: 157.08;
        stroke-dashoffset: 157.08;
    }

    .eg-cursor-timer.is-active circle {
        animation: eg-cursor-timer-fill 5s linear forwards;
    }

    @keyframes eg-cursor-timer-fill {
        from {
            stroke-dashoffset: 157.08;
        }
        to {
            stroke-dashoffset: 0;
        }
    }

    /* Sizing and fading are decoration, so they snap. The timer sweep is left
       alone: it reports how much time is left, and a pie that fills instantly
       reports nothing. */
    @media (prefers-reduced-motion: reduce) {
        .eg-cursor > *,
        .eg-cursor-label span,
        .eg-cursor-play svg {
            transition-duration: 0.01ms;
        }
    }
}
