/* ==========================================================================
   RANDOM EVENT ANIMATION LAYER
   --------------------------------------------------------------------------
   Random event "effects" are HTML/CSS/JS strings built in
   dailies/helpers/random_events.py and injected raw via {{ effect|safe }} in
   core/templates/core/_page_event.html.

   Historically each effect positioned its sprites with `position: absolute`
   against <body> and hardcoded offsets tuned for the 955px desktop canvas
   (margin-left up to 825px, translate() out to -950px). Anything landing
   outside the canvas widened the document, so effects individually patched
   around it with `html, body { width: 100vw; overflow-x: hidden }` — which
   mutates global layout for the whole page, and on desktop `100vw` includes
   the scrollbar gutter and so causes the very overflow it is meant to stop.

   Everything now renders inside .re-fx-stage instead: fixed to the viewport,
   clipped, and click-through. A sprite can no longer change page layout or
   scroll extents no matter where its animation sends it, so none of the
   global overrides are needed.
   ========================================================================== */

.re-fx-stage {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    /* Clipping is the whole point: it replaces every per-effect
       `html, body { overflow-x: hidden }` hack. */
    overflow: hidden;
    /* Effects are decoration — they must never swallow a click or tap. */
    pointer-events: none;
    z-index: 2000;
}

/* Sprites are positioned relative to the stage, not the document. Effect code
   sets its own top/left/margin-left, which now resolves against the viewport
   box rather than the page. */
.re-fx-stage > * {
    position: absolute;
    pointer-events: none;
}

/* Tracks the random event box (see reFx.anchoredStage). Effects that belong to
   the event rather than to the page — musical notes lifting off it, a ghost
   hovering beside it — position their sprites against this instead of against
   the viewport, so they keep the same relationship to the box whatever width it
   is laid out at. Deliberately not clipped: sprites are meant to drift outside
   the box, and the stage above is already doing the clipping that matters. */
.re-fx-anchor {
    position: absolute;
    pointer-events: none;
}

.re-fx-anchor > * {
    position: absolute;
    pointer-events: none;
}

/* --------------------------------------------------------------------------
   SNOW
   The snowflakes are text glyphs at font-size: 1em, positioned with fixed and
   percentage offsets, so they already track any viewport correctly. At the
   scaled-down 955px canvas a 1em glyph is barely visible on a handset though.

   Scaling these up is safe where scaling a sprite is not: font-size changes the
   glyph, while the flake's motion comes from `top` percentages and a
   translateX() shake that font-size does not touch. So the flakes get bigger
   and fall exactly the same way.
   -------------------------------------------------------------------------- */
@media only screen and (max-width: 992px) {
    .snowflake {
        font-size: 2.25em;
    }

    /* Same trick for the drifting clouds: every part of a cloud is sized in em
       off its own font-size, so scaling that scales the shape and its puffs
       without touching the drift transform. Applied as a multiplier rather than
       a flat size so each cloud keeps the individual scale that gives the drift
       its sense of depth. */
    .re-cloud {
        --re-cloud-boost: 1.7;
    }
}

/* --------------------------------------------------------------------------
   A NOTE ON MOBILE SIZING
   There is deliberately no mobile sprite up-scaling here. The site pins the
   layout viewport to 955px (see the viewport meta in core/base.html), so a
   handset renders exactly the same 955px canvas as desktop, scaled down to
   fit its screen. Sprites therefore already land in the right place and are
   shrunk by precisely the same factor as the text, buttons and images around
   them — they are small on a phone because the whole page is.

   Scaling only the sprites back up was tried and reverted: the CSS `scale`
   property composes into the same matrix as the `transform` these effects
   animate, so it multiplies every translate() distance too and distorts the
   motion path rather than just enlarging the sprite.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   REDUCED MOTION
   The site already ships a photosensitivity warning for the earthquake effect
   (see core/templates/core/sitepreferences.html), so honouring the OS-level
   setting is the same intent applied site-wide. Hiding the stage outright is
   better than `animation: none`, which would strand every sprite mid-flight
   at its starting offset instead of removing it.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    .re-fx-stage {
        display: none !important;
    }

    /* The earthquake effect shakes <body> directly rather than rendering a
       sprite, so hiding the stage does not reach it. This is the effect the
       site already warns about for photosensitivity, which makes it the one
       that most needs to respect the OS setting. */
    body {
        animation: none !important;
    }
}
