/* Doorway Moving Targets — activity styles.
   1em = 1/50th of activity width (set by doorway.js onResize).
   All sizing in em; no px except borders.
*/

/* --- Screen layout --- */

/* Flex-column skeleton comes from .dw-activity-screen in shared/doorway.css.
   `position: relative` establishes the containing block for absolute-positioned
   sub-activity scenes (fish, fireworks, footballer). */
.mt-screen {
    overflow: hidden;
    position: relative;
}

/* --- Sub-activity areas --- */

.mt-sub-activity {
    flex: 1;
    position: relative;
    width: 100%;
    overflow: hidden;
}

/* Hidden sub-activities */
.mt-sub-activity.hidden_panel {
    display: none;
}

/*
 * On-screen instruction prompt overlaid at the top of each sub-activity scene.
 * Flash renders these as static text fields in the centre-top of the stage.
 * Sits above the scene background but is non-interactive (pointer-events: none)
 * so it doesn't block clicks on targets that animate through the top area.
 */
.mt-prompt {
    position: absolute;
    top: 0.4em;
    left: 50%;
    transform: translateX(-50%);
    margin: 0;
    z-index: 5;
    font-size: 1.1em;
    color: #000;
    pointer-events: none;
    white-space: nowrap;
}

/* Light variant for dark scenes (e.g. fireworks night sky) */
.mt-prompt-light {
    color: #fff;
}

/* --- Welcome image --- */

/*
 * Welcome image: the Flash sprite (sprite_186) showing the framed underwater
 * scene with fish and seaweed. Capped at 18em ≈ 288px wide — the vector
 * trace stays crisp at this size and matches the Flash display size
 * (~290×200px). max-width/max-height keep the illustration from blowing
 * up vertically in flow-mode.
 */
.welcome_image {
    width: 50%;
    max-width: 18em;
    aspect-ratio: 350 / 260;
    max-height: 13em;
    margin: var(--dw-space-narrow) auto var(--dw-space-comfy);
    border: 3px solid #777;
    border-radius: 0.3em;
    overflow: hidden;
    flex-shrink: 0;
}

.mt-welcome-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    image-rendering: auto;
}

/* Vertical breathing room between the activity radios above and the
   Object Size subgroup nested inside the same fieldset. */
.mt-activity-group .mt-object-size-subgroup {
    margin-top: 1em;
}

/* --- Vans sub-activity --- */

/*
 * Vans scene background: the Flash reference screenshot (800×600 native)
 * stretched non-uniformly to fill the 800×450 HTML5 stage. Sky, grass,
 * roads and dashed centre lines are baked into the PNG.
 *
 * The Flash PNG also has some chrome baked in that the HTML5 shell paints
 * itself elsewhere: a "back" pencil at top-left, the prompt title across
 * the top sky, and a "v1.01" build marker in the bottom-left grass verge.
 * Two colour overlays repaint the sky strip and the bottom-left corner
 * to hide that chrome — the colours are sampled from clean patches of the
 * same PNG so the joins are invisible.
 *
 * Van slot positions are calibrated to the Flash road-band centres
 * (see moving-targets-render.js).
 */
.mt-vans-scene {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
    background:
        /* Puffy clouds — each cloud is 3–4 overlapping radial-gradient
           blobs at slightly different sizes and positions so the outline
           reads as a lumpy cumulus rather than one soft oval. Sharper
           falloff (solid to 55%, gone by 92%) keeps edges crisp. */

        /* Cloud A (~7em) */
        radial-gradient(ellipse 2em 1em at 5.2em 2.4em, #fff 0%, #fff 55%, rgba(255,255,255,0) 92%),
        radial-gradient(ellipse 2.6em 1.2em at 7em 2.2em, #fff 0%, #fff 55%, rgba(255,255,255,0) 92%),
        radial-gradient(ellipse 1.8em 0.9em at 8.6em 2.6em, #fff 0%, #fff 55%, rgba(255,255,255,0) 92%),

        /* Cloud B (~17em) */
        radial-gradient(ellipse 2em 1em at 15.2em 1.4em, #fff 0%, #fff 55%, rgba(255,255,255,0) 92%),
        radial-gradient(ellipse 2.8em 1.3em at 17em 1.7em, #fff 0%, #fff 55%, rgba(255,255,255,0) 92%),
        radial-gradient(ellipse 2em 1em at 19em 1.5em, #fff 0%, #fff 55%, rgba(255,255,255,0) 92%),
        radial-gradient(ellipse 1.6em 0.8em at 17em 0.9em, #fff 0%, #fff 55%, rgba(255,255,255,0) 92%),

        /* Cloud C (~29em) */
        radial-gradient(ellipse 2.2em 1em at 27em 3em, #fff 0%, #fff 55%, rgba(255,255,255,0) 92%),
        radial-gradient(ellipse 2.8em 1.3em at 29em 2.8em, #fff 0%, #fff 55%, rgba(255,255,255,0) 92%),
        radial-gradient(ellipse 2em 1em at 31em 3.1em, #fff 0%, #fff 55%, rgba(255,255,255,0) 92%),

        /* Cloud D (~40em) */
        radial-gradient(ellipse 1.8em 0.9em at 38.5em 1.9em, #fff 0%, #fff 55%, rgba(255,255,255,0) 92%),
        radial-gradient(ellipse 2.4em 1.2em at 40.2em 2.2em, #fff 0%, #fff 55%, rgba(255,255,255,0) 92%),
        radial-gradient(ellipse 1.8em 0.9em at 42em 2em, #fff 0%, #fff 55%, rgba(255,255,255,0) 92%),

        /* Cloud E (~46em) */
        radial-gradient(ellipse 1.8em 0.9em at 45em 3em, #fff 0%, #fff 55%, rgba(255,255,255,0) 92%),
        radial-gradient(ellipse 2.4em 1.1em at 46.5em 3.2em, #fff 0%, #fff 55%, rgba(255,255,255,0) 92%),
        radial-gradient(ellipse 1.6em 0.8em at 48em 3em, #fff 0%, #fff 55%, rgba(255,255,255,0) 92%),
        /* Repaint sky over the top 5.85em to hide the baked back pencil
           + prompt title. Gradient matches the Flash sky (light top, deep
           blue at horizon). */
        linear-gradient(to bottom, #ddebfa 0%, #bcd2fb 45%, #98b7fd 100%)
            0 0 / 100% 5.85em no-repeat,
        /* Repaint the bottom-left grass verge to hide the baked v1.01
           marker. Sampled grass green. */
        linear-gradient(#679b45, #679b45)
            0 100% / 4em 1.5em no-repeat,
        /* Flash reference PNG stretched to fill the stage. */
        url("pictures/vans-scene.png") 0 0 / 100% 100% no-repeat;
}

.mt-van-target {
    position: absolute;
    z-index: 2;
    /* JS sets top to each lane's centreline (see VAN_LANE_TOP_EM); the
       translate + origin below anchor the wheels at that y so the S/M/L
       scale grows the sprite upward only, keeping tyres on the road. */
    transform: translateY(-100%) scale(var(--mt-target-scale, 1));
    transform-origin: bottom center;
    background: transparent;
    border: none;
    cursor: pointer;
    padding: 0;
}

/*
 * Override shared button:hover translucent fill on every sprite target — the
 * sprite (van, ball, balloon, fish) is the visual hit target, not the bounding
 * rectangle. The shared `button:hover { background: rgba(128,128,128,0.2) }`
 * rule in doorway.css would otherwise paint a translucent rectangle behind
 * each sprite while the mouse hovers. (Launcher target has its own background
 * gradient and overrides hover separately to keep the colour pop.)
 */
@media (hover: hover) {
    .mt-van-target:hover,
.mt-ball-target:hover,
.mt-balloon-target:hover,
.mt-fish-target:hover {
        background: transparent;
    }
}

/* Shape-hugging scan ring and keyboard focus indicator for the transparent-
   alpha sprites in this activity (van, ball, balloon, fish, firework
   launcher). The shared .scan_highlight default is a rectangular box-shadow;
   on a PNG/SVG with transparent corners that reads as a bounding box, not a
   ring around the object. Override to a stacked blurred yellow drop-shadow
   glow — same pattern as supermarket's .sm-change-cluster-btn hover, which
   traces each alpha edge softly instead of drawing a hard four-direction
   outline.

   Applied to both `.scan_highlight` (switch-scan "you are here" cue) and
   `:focus-visible` (keyboard focus) so mouse-free users see the same glow
   through both paths. The invisible outline overrides the shared dotted
   focus-visible rule in doorway.css. */
.mt-van-target.scan_highlight,
.mt-ball-target.scan_highlight,
.mt-balloon-target.scan_highlight,
.mt-fish-target.scan_highlight,
.mt-launcher-target.scan_highlight,
.mt-van-target:focus-visible,
.mt-ball-target:focus-visible,
.mt-balloon-target:focus-visible,
.mt-fish-target:focus-visible,
.mt-launcher-target:focus-visible {
    outline: 0.4em solid transparent;
    outline-offset: 0.2em;
    box-shadow: none;
    filter: drop-shadow(0 0 0.25em #ffec3b)
            drop-shadow(0 0 0.15em #ffec3b)
            drop-shadow(0 0 0.1em  #ffec3b);
}

/* Balloon glow is slightly fatter — the thin balloon silhouette (esp. the
   string) needs more spread to read as a ring at the same distance. */
.mt-balloon-target.scan_highlight,
.mt-balloon-target:focus-visible {
    filter: drop-shadow(0 0 0.3em #ffec3b)
            drop-shadow(0 0 0.2em #ffec3b)
            drop-shadow(0 0 0.15em #ffec3b)
            drop-shadow(0 0 0.1em  #ffec3b);
}

/* Mute the yellow scan/focus glow on a balloon that isn't yet a fair
   target. Toggled by startBalloonCycle (on) and its 15%-of-float timer
   (off). Scanner ring state is independent — .scan_highlight may still
   be present on the element, this rule just makes it invisible. */
.mt-balloon-target.mt-balloon-suppress-glow.scan_highlight,
.mt-balloon-target.mt-balloon-suppress-glow:focus-visible {
    filter: none;
}

.mt-van-img {
    width: 8em;
    height: auto;
    display: block;
    /* Flip horizontally for vans that drive left (set via --mt-van-flip: -1 in JS) */
    transform: scaleX(var(--mt-van-flip, 1));
}

/* --- Fireworks sub-activity --- */

/*
 * Fireworks scene background:
 * Black night sky with a curved green hill along the bottom (peaks in the
 * middle, dips to grass-strip height at the corners — Flash reference has
 * the same perspective illusion). Sparklers plant on each side above the
 * dipped ground. Star count is deliberately sparse so it doesn't compete
 * with the fireworks visually.
 */
.mt-fireworks-scene {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
    background: #000000;
}

/* Sparse star field over the sky portion only. */
.mt-fireworks-scene::before {
    content: "";
    position: absolute;
    inset: 0 0 3em 0;
    pointer-events: none;
    z-index: 0;
    background-image:
        radial-gradient(circle, #fff 0.8px, transparent 0.8px),
        radial-gradient(circle, rgba(255,255,255,0.6) 0.8px, transparent 0.8px);
    background-size: 11em 9em, 14em 7em;
    background-position: 2em 1.5em, 5em 3em;
}

/* Curved ground — shallow arch, peak in the middle, sides slightly lower.
   Launchers sit at bottom:3em; the 5em peak buries their base ~2em (Flash
   reference has the same planted-in-grass look). Large horizontal border-
   radius + small vertical radius gives a horizon-curvature feel without an
   obvious dome. */
.mt-fireworks-scene::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 5em;
    background: linear-gradient(to bottom, #1a8a1a 0%, #0a5a0a 100%);
    border-top-left-radius: 100% 1em;
    border-top-right-radius: 100% 1em;
    pointer-events: none;
    z-index: 1;
}

/*
 * Sparklers — Flash sprite_256. Natural height at width 3.5em is 6.35em
 * (SVG aspect 53.8:97.35). Left sparkler lifts half its height, right a
 * quarter, so they float above the curved ground rather than plant into it.
 */
.mt-sparkler {
    position: absolute;
    width: 3.5em;
    height: 6.35em;
    pointer-events: none;
    z-index: 2;
}

/* Rotation origins at each sprite's own base corner keep the tips fanning
   inward evenly toward the launchers. */
.mt-sparkler-left {
    left: 0em;
    bottom: 3.625em;
    transform: rotate(30deg);
    transform-origin: bottom left;
}

/* Right sparkler uses `left` (not `right`) so both sparklers position on
   the same axis. In the nominal 50em-wide scene (1em = scene_width / 50),
   44.25em sits ~2em from the right edge. */
.mt-sparkler-right {
    left: 44.25em;
    bottom: 2.375em;
    transform: scaleX(-1) rotate(30deg);
    transform-origin: bottom right;
}

/*
 * Launcher "box" — slim cylinder with a domed top and rounded bottom and a
 * curly fuse curling out from the lower-left side. Mirrors Flash sprites
 * 248/251/254. The body silhouette + fuse path come from the extracted Flash
 * graphics; the button itself is a transparent hit area sized to wrap the
 * inline SVG. The fuse path has its own class so CSS can animate it (burn
 * down + glow) when the launcher fires.
 */
.mt-launcher-target {
    position: absolute;
    /* Button wraps the visible box body only. The fuse curl extends past
       the button bounds (into negative x and past the bottom); overflow:
       visible on the SVG lets it draw, and the SVG viewBox is cropped to
       the body so :focus-visible sits tight around the box rather than
       hanging 2em below it. */
    bottom: 3em;
    width: 2.8em;
    height: 4.1em;
    background: transparent;
    border: none;
    cursor: pointer;
    padding: 0;
    transform: scale(var(--mt-target-scale, 1));
    transform-origin: center bottom;
    z-index: 3;
    /* Horizontal position set by JS */
}

.mt-launcher-svg {
    width: 100%;
    height: 100%;
    /* Allow the fuse curl to escape the button bounds (the path extends
       below the box base into the grass-line area). */
    overflow: visible;
    display: block;
}

/* Hover feedback — gentle lift + glow so the target reads as clickable. */
@media (hover: hover) {
    .mt-launcher-target:hover {
        filter: brightness(1.15) drop-shadow(0 0.1em 0.3em rgba(255, 200, 80, 0.6));
        transform: scale(calc(var(--mt-target-scale, 1) * 1.05));
    }
}

/* Body colours — set per-modifier via fill + stroke on the body group's
   children. Using two consecutive `:nth-child` rules so we can give the
   fill path one colour and the outline path a darker stroke. */
.mt-launcher-green   .mt-launcher-body > path:first-child { fill: #33ff00; }
.mt-launcher-green   .mt-launcher-body-outline           { stroke: #336600; }
.mt-launcher-orange  .mt-launcher-body > path:first-child { fill: #ff9900; }
.mt-launcher-orange  .mt-launcher-body-outline           { stroke: #ff3300; }
.mt-launcher-magenta .mt-launcher-body > path:first-child { fill: #ff00ff; }
.mt-launcher-magenta .mt-launcher-body-outline           { stroke: #8e00b3; }

/* Fuse: idle = static black curl. While firing the fuse "burns down" via
   the stroke-dasharray trick below. */
.mt-launcher-fuse {
    stroke: #000;
    transition: stroke 0.2s linear;
}

/* When the button has the firing class, the fuse "burns down" — we draw it
   in two segments using stroke-dasharray + stroke-dashoffset so the visible
   portion shrinks from full length to 0 over 1s. Fuse path length is
   approximately 60 units; we use 80 to be safe. */
.mt-launcher-firing .mt-launcher-fuse {
    stroke: #ff7a1a;
    stroke-dasharray: 80;
    stroke-dashoffset: 0;
    animation: mt-fuse-burn 1s linear forwards;
    filter: drop-shadow(0 0 0.15em #ffae3a) drop-shadow(0 0 0.3em #ff5500);
}

@keyframes mt-fuse-burn {
    /* Burn from box-side outward → the dashoffset grows from 0 → 80 so the
       drawn portion shrinks and the tip "moves" along the path. We pulse
       opacity to add a flicker. */
    0%   { stroke-dashoffset: 0;  opacity: 1; }
    20%  { stroke-dashoffset: 16; opacity: 0.85; }
    40%  { stroke-dashoffset: 32; opacity: 1; }
    60%  { stroke-dashoffset: 48; opacity: 0.85; }
    80%  { stroke-dashoffset: 64; opacity: 1; }
    100% { stroke-dashoffset: 80; opacity: 0; }
}

.mt-explosions-area {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
}

/* --- Footballer sub-activity --- */

/*
 * Footballer scene background — Flash sprite shape 258 (footballer-scene.svg)
 * containing the sky, hills, row of trees on the horizon, and the white-lined
 * football pitch. The Flash decoration is trees, not goalposts.
 * Flash shape is 810.1×599.45px in an 800×600 stage; we stretch to fill the
 * HTML5 16:9 stage.
 */
.mt-footballer-scene {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
    background:
        url("pictures/footballer-scene.svg") 0 0 / 100% 100% no-repeat,
        /* Fallback sky gradient (visible if SVG fails to load) */
        linear-gradient(to bottom, #4265f0 0%, #c6ddfd 100%);
}

.mt-ball-target {
    position: absolute;
    background: transparent;
    border: none;
    cursor: pointer;
    padding: 0;
    /* Anchor inside the pitch's centre circle, not at scene-centre — Flash
       puts the ball in the centre-circle of the pitch, which sits in the
       lower half of the 16:9 stage (the trees row eats the top quarter).
       The centre-circle in footballer-scene.svg is at x≈412/800 = 51.5%
       (the path arc spans x=349.85 → 474.55), not the geometric stage
       centre — so left:51.5% sits the ball in the middle of the circle. */
    top: 60%;
    left: 51.5%;
    transform: translate(-50%, -50%) scale(var(--mt-target-scale, 1));
    transform-origin: center center;
}

.mt-ball-img {
    /* Ball half size so it fits inside the pitch's centre circle (matches Flash). */
    width: 3em;
    height: auto;
    display: block;
}

/*
 * Runner GIF — Flash sprite 273. Plays once when the ball is clicked,
 * showing the player jogging in from off-screen left. The figure inside
 * the GIF translates within its 1062×227 bounding box and arrives at the
 * ball at the last visible frame (~1.0 s at 50 fps).
 *
 * The transform positions the GIF so that the figure's final pose lands
 * at the ball position. Offsets and scale were tuned in the alignment
 * sandbox at notes/footballer-sandbox/sandbox.html (px → em via /16,
 * which is 1em = 1/50th of 800px stage width). The remaining ~30em of
 * GIF extends invisibly to the right of the ball and is clipped by the
 * scene's overflow:hidden.
 */
.mt-footballer-runner {
    position: absolute;
    left: 51.5%;
    top: 60%;
    width: 66.375em;
    height: 14.1875em;
    transform-origin: top left;
    transform: translate(-39.5em, -10.8em) scale(0.98);
    pointer-events: none;
    z-index: 1;
}

.mt-footballer-runner.hidden_panel {
    display: none;
}

/*
 * Kicker — Flash sprite 278 ("mango"). Three SVG poses extracted from the
 * mango clip cycle through as a single 250 ms kick: pose A (wind-up,
 * footballer-stand.svg) → pose B (contact, footballer-kick-contact.svg)
 * → pose C (follow-through, footballer-kick-followthrough.svg).
 *
 * Anchored so the feet line up with the runner's final pose at the ball,
 * tuned in the alignment sandbox.
 */
.mt-footballer-img {
    position: absolute;
    left: 51.5%;
    top: 60%;
    height: 13.5em;
    width: auto;
    transform: translate(-88%, -80%);
    pointer-events: none;
    z-index: 1;
}

.mt-footballer-img.hidden_panel {
    display: none;
}

/* Footballer is layered behind the ball */
.mt-ball-target {
    z-index: 2;
}

/* --- Popping sub-activity --- */

/*
 * Popping scene background — Flash sprite shape 289 (popping-scene.svg)
 * containing both the sky gradient, the two clouds, and the two green hills.
 * Flash shape is 800.95×600px. In the HTML5 stage (800×450), we stretch
 * the SVG to fill the scene — Flash had a 4:3 stage (800×600) and HTML5
 * uses 16:9 (800×450), so the full background scales to cover.
 */
.mt-popping-scene {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
    background:
        url("pictures/popping-scene.svg") 0 0 / 100% 100% no-repeat,
        /* Fallback sky gradient (visible if SVG fails to load) */
        linear-gradient(to bottom, #4265f0 0%, #c6ddfd 100%);
}

.mt-balloon-target {
    position: absolute;
    background: transparent;
    border: none;
    cursor: pointer;
    padding: 0;
    /* Position animated by JS */
    bottom: 2em;
    left: 50%;
    transform: translateX(-50%) scale(var(--mt-target-scale, 1));
    transform-origin: center bottom;
    /* CSS custom properties for animation */
    --mt-balloon-x: 50%;
    --mt-balloon-y: 80%;
}

/*
 * Balloon shape: inline SVG ported from Flash sprite_293.
 * `currentColor` on the body path picks up the CSS color, which JS sets via
 * --mt-balloon-colour each cycle (see nextBalloonColourIndex).
 */
.mt-balloon-shape {
    display: block;
    width: 5em;
    height: 8.25em;      /* aspect ratio of the Flash sprite, including string */
    color: var(--mt-balloon-colour, #ec0202);
    line-height: 0;
}

.mt-balloon-shape svg {
    width: 100%;
    height: 100%;
    display: block;
    overflow: visible;
}

/*
 * Pop effect — anchored at the balloon's centre (JS sets left/top inline).
 * The wrapper is zero-sized; both children (the bursting lines SVG and the
 * "pop!" text) are themselves centred on the wrapper via absolute positioning
 * and a -50% translate, so they always burst out from the balloon's centre
 * point regardless of layout zoom.
 */
.mt-pop-effect {
    position: absolute;
    pointer-events: none;
    width: 0;
    height: 0;
}

/* SVG starburst of black line segments fanning out from centre. */
.mt-pop-burst-lines {
    position: absolute;
    left: 0;
    top: 0;
    width: 7em;
    height: 7em;
    transform: translate(-50%, -50%);
    overflow: visible;
}

/* Spikes are filled tapered lens shapes (wide-in-middle, narrow at the tips)
   to mimic Flash's ink-brush look. Geometry is generated per-pop by JS
   (see regeneratePopSpikes in moving-targets-render.js) with jittered
   angles, radii and thickness so each click has fresh variability. */
.mt-pop-burst-lines .mt-pop-spikes path {
    fill: #000;
    stroke: none;
}

/* The bold "pop!" word centred over the burst. */
.mt-pop-text {
    position: absolute;
    left: 0;
    top: 0;
    transform: translate(-50%, -50%);
    font-weight: 900;
    font-size: 1.6em;
    color: #000;
    text-transform: lowercase;
    letter-spacing: 0.02em;
    text-shadow: 0 0 0.15em #fff, 0 0 0.3em #fff;
    line-height: 1;
    white-space: nowrap;
}

/* When the pop effect is bursting, both the lines and the text run their
   one-shot keyframes. Animations are short (~400ms) to match a balloon-pop
   beat — the burst should feel snappy, not lingering. */
.mt-pop-effect.mt-pop-bursting .mt-pop-burst-lines {
    animation: mt-pop-lines 0.4s ease-out forwards;
}

.mt-pop-effect.mt-pop-bursting .mt-pop-text {
    animation: mt-pop-text 0.4s ease-out forwards;
}

/* The burst-lines parent just holds visibility — it fades out at the end
   of the burst so any in-flight spike animations are clipped cleanly.
   Per-spike travel (.mt-pop-spike) does the actual outward motion. The
   animationend on this element is what render.js listens for to hide the
   pop overlay. */
@keyframes mt-pop-lines {
    0%   { transform: translate(-50%, -50%); opacity: 1; }
    80%  { transform: translate(-50%, -50%); opacity: 1; }
    100% { transform: translate(-50%, -50%); opacity: 0; }
}

/* Per-spike travel: each spike is a fixed thin eye-shape that slides
   outward along its own radial axis while fading in then out — gives a
   "tracer" / ink-streak feel that mimics the motion blur in Flash's
   multi-frame pop sprite. Angle and travel range come from inline custom
   properties set by regeneratePopSpikes (per-pop randomness). */
.mt-pop-burst-lines .mt-pop-spike {
    transform-origin: 0 0;
    animation: mt-pop-spike-travel 0.4s ease-out forwards;
}

@keyframes mt-pop-spike-travel {
    0%   { transform: rotate(var(--mt-pop-angle)) translateX(var(--mt-pop-r-start)); opacity: 0; }
    25%  { opacity: 1; }
    70%  { opacity: 1; }
    100% { transform: rotate(var(--mt-pop-angle)) translateX(var(--mt-pop-r-end)); opacity: 0; }
}

/* Text pops in, holds briefly, then fades — slightly slower scale than the
   lines so the word reads as the focal point of the burst. */
@keyframes mt-pop-text {
    0%   { transform: translate(-50%, -50%) scale(0.3); opacity: 0; }
    25%  { transform: translate(-50%, -50%) scale(1.15); opacity: 1; }
    70%  { transform: translate(-50%, -50%) scale(1.0); opacity: 1; }
    100% { transform: translate(-50%, -50%) scale(1.1); opacity: 0; }
}

/* --- Fish sub-activity --- */

/*
 * Fish scene background — composite Flash sprite (fish-scene.svg) containing
 * the underwater horizontal gradient, the seaweed sprites (charId 297, 299, 301)
 * at their original Flash placements, and the sandy floor (shape 295).
 * y-coordinates are scaled by 0.75 to fit the Flash 600px stage into HTML5 450px.
 */
.mt-fish-scene {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
    background:
        url("pictures/fish-scene.svg") 0 0 / 100% 100% no-repeat,
        /* Fallback underwater gradient (visible if SVG fails to load).
           Vertical to mirror the SVG gradient direction. */
        linear-gradient(to bottom,
            #86e8dd 0%,
            #a4ccf8 50%,
            #73e19e 100%);
}

.mt-fish-target {
    position: absolute;
    background: transparent;
    border: none;
    cursor: pointer;
    padding: 0;
    /* Position animated by JS */
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%) scale(var(--mt-target-scale, 1));
    transform-origin: center center;
    /* Horizontal flip via CSS custom property (set by JS for direction) */
    --mt-fish-facing: 1; /* 1 = right, -1 = left */
}

.mt-fish-img {
    /* 10.6em — sized for visibility against the busy weed/pebble backdrop. */
    width: 10.6em;
    height: auto;
    display: block;
    /* Two stacked transforms: facing flip (set by JS via --mt-fish-facing)
       and a slow vertical bob. Per-fin/tail flap lives inside fish.svg as
       SMIL animateTransform on tagged <g> wrappers (#fish-tail, #fish-dorsal,
       #fish-ventral, #fish-pectoral) plus a 5-frame discrete visibility swap
       on #fish-mouth (open/close cycle from Flash sprite 147) — SMIL animations
       run in img-loaded SVGs whereas host-page CSS cannot reach inside them. */
    transform: scaleX(var(--mt-fish-facing, 1)) translateY(var(--mt-fish-bob, 0));
    animation: mt-fish-swim-bob 1.4s ease-in-out infinite;
    transform-origin: center center;
}

/* Subtle swim cycle: vertical bob via the --mt-fish-bob custom property so the
   scaleX flip (driven by JS for facing direction) is preserved. Keeping the
   amplitude tiny (~0.18em) avoids destabilising the hit target. */
@keyframes mt-fish-swim-bob {
    0%, 100% { --mt-fish-bob: 0em; }
    25%      { --mt-fish-bob: -0.18em; }
    50%      { --mt-fish-bob: 0em; }
    75%      { --mt-fish-bob: 0.18em; }
}

/* Tail-sway: a second animation on a wrapper inside the button. Implemented
   via a child selector so the swim-bob on the img survives. */
.mt-fish-target.mt-fish-turning .mt-fish-img {
    /* Brief mid-turn squash — used during the JS-triggered turn-around. */
    animation: mt-fish-turn 0.25s ease-in-out;
}

@keyframes mt-fish-turn {
    0%   { transform: scaleX(var(--mt-fish-facing-prev, 1)) scaleY(1); }
    50%  { transform: scaleX(0.15) scaleY(0.95); }
    100% { transform: scaleX(var(--mt-fish-facing, 1)) scaleY(1); }
}

/* Initialise the custom property in the property registry so the keyframes
   animation can interpolate it (Chrome/Firefox treat unregistered custom
   props as discrete strings; without @property the bob would snap rather
   than ease). Falls back gracefully on browsers without @property. */
@property --mt-fish-bob {
    syntax: "<length>";
    initial-value: 0em;
    inherits: false;
}

.mt-bubbles-area {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
}

/* --- Rocket/bang animation elements (injected by JS) --- */

.mt-rocket-anim {
    position: absolute;
    /* 1em — the original Flash rocket sprite is a thin streak, not a fat
       firework. Smaller width reads as a streak and leaves more visual
       room for the explosion at the end of the trajectory. */
    width: 1em;
    height: auto;
    pointer-events: none;
    /* transform set by JS for rotation */
}

/* --- Launcher body pulse during firing (subtle brightness boost, not animated) --- */

.mt-launcher-firing .mt-launcher-body > path:first-child {
    filter: brightness(1.1);
}

/* --- Bubble animation elements (injected by JS) ---
 * Bubbles are decorative (carry no game state). They float upward via a CSS transition
 * on `top` and `left` (set inline by JS for randomised destination). A subtle wobble
 * keyframe is layered via `transform` so each bubble has a different horizontal sway.
 * Removed on `transitionend` (see spawnBubble).
 */

.mt-bubble-anim {
    position: absolute;
    pointer-events: none;
    opacity: 0.7;
    animation: mt-bubble-wobble 1.8s ease-in-out infinite;
}

@keyframes mt-bubble-wobble {
    0%, 100% { transform: translateX(-0.2em); }
    50%      { transform: translateX( 0.2em); }
}

/* --- Activity-choice thumbnails on the Options screen ---
 * Each radio label gets a small picture above its name, matching Flash
 * where the options page shows a thumbnail per sub-activity.
 */
.mt-activity-choice {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: var(--dw-space-tight);
    padding: var(--dw-space-snug) var(--dw-space-default) !important;
    vertical-align: top;
}

/* Each thumbnail is a crop of the Flash options-screen scene panel
   (roughly 4:3 aspect). Sized to match the Flash frame width. */
.mt-activity-thumb {
    display: block;
    width: 4em;
    height: 3em;
    background-size: cover;
    background-repeat: no-repeat;
    background-position: center;
}

.mt-thumb-vans      { background-image: url("pictures/thumbs/thumb-vans.png"); }
.mt-thumb-fireworks { background-image: url("pictures/thumbs/thumb-fireworks.png"); }
.mt-thumb-footballer{ background-image: url("pictures/thumbs/thumb-footballer.png"); }
.mt-thumb-popping   { background-image: url("pictures/thumbs/thumb-popping.png"); }
.mt-thumb-fish      { background-image: url("pictures/thumbs/thumb-fish.png"); }

.mt-activity-name {
    display: block;
    font-size: 0.9em;
    text-align: center;
    white-space: nowrap;
}

/* --- Options grid layout (replaces legacy <table class="options_layout_grid">) --- */

/* Object Size description sits below the small/medium/large radios
   (Flash options-screen sub-caption). */
.mt-difficulty-description {
    flex-basis: 100%;
    margin: var(--dw-space-snug) 0 0;
    font-style: italic;
}
