/* The map replaces the grid entirely; only one of the two is ever populated. */
.viewOverview.mapMode .overviewList {
    display: none;
}

/* Viewport box. overflow:hidden is what produces the crop in 'cover' mode. */
.overviewMap {
    position: absolute;
    top: 0px;
    left: 0px;
    width: 100%;
    height: 100%;
    overflow: hidden;
}

/*
 * The padding frame is the box the background image is fitted into: sized and
 * positioned in px by layoutStage(), inset by the configured padding.
 *
 * A CSS padding on the viewport box would not do the job, twice over. overflow
 * clips at the border box rather than the padding box, so a 'cover' overscan
 * would bleed straight through the ring it is supposed to sit inside; and the
 * toolbar and the card are positioned against the viewport box, so they would
 * be pushed inwards with it. Like the stage inside it, I carry no border and
 * no padding of my own.
 */
.overviewMapFrame {
    position: absolute;
    border: 0;
    padding: 0;
}

/*
 * Only 'cover' has an overscan to clip. Under 'contain' the image already fits
 * inside me, and clipping would cost the pins near the edge exactly the label
 * and ripple room the padding was added to give them.
 */
.overviewMap.fitCover .overviewMapFrame {
    overflow: hidden;
}

/*
 * The stage IS the background image's box: sized in px by layoutStage().
 * It must carry no border and no padding — percentage offsets on the pins
 * resolve against the padding box, so either would break the position lock.
 */
.overviewMapStage {
    position: absolute;
    border: 0;
    padding: 0;
    background-color: var(--primary-bg-color);
}

.overviewMap.noBackground .overviewMapStage {
    outline: 2px dashed var(--secondary-fg-color);
    outline-offset: -2px;
}

.overviewMapBackground {
    position: absolute;
    top: 0px;
    left: 0px;
    width: 100%;
    height: 100%;
    display: block;
    /* Native image dragging would fight interact.js. */
    pointer-events: none;
    user-select: none;
    -webkit-user-drag: none;
}

.overviewMap:not(.noBackground) .overviewMapBackground {
    display: block;
}

.overviewMap.noBackground .overviewMapBackground {
    display: none;
}

/*
 * Pins. left/top are percentages of the stage, i.e. normalized image
 * coordinates; translate(-50%, -50%) makes that percentage the pin's centre,
 * so resizing a pin never moves it off the feature it marks.
 *
 * The pin is the anchor: position, hit area, resize handles, stacking. What it
 * LOOKS like is one element in, on .overviewMapMarkerBody. The split is what
 * lets an animation transform the pin's appearance without rotating the title
 * label with it, without replacing the centring translate above (an animation
 * replaces the whole property for its duration), and without moving the resize
 * handles out from under the cursor while the map is being arranged.
 */
.overviewMapMarker {
    position: absolute;
    transform: translate(-50%, -50%);
    cursor: pointer;
    z-index: 1;
}

.overviewMapMarkerBody {
    position: absolute;
    top: 0px;
    left: 0px;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    /*
     * The pin palette is the theme's the other way round: foreground where the
     * rest of the UI puts background, and vice versa. A pin sits on somebody
     * else's photograph or map artwork rather than on the app's own ground, so
     * taking the theme's background colour would camouflage it against exactly
     * the thing it has to stand out from. Every colour below — border, fill,
     * label, glyph, ripple, glow — is inverted together, so a pin reads as one
     * object rather than as a mix of the two palettes.
     */
    background-color: #f8f8f8;
    background-size: cover;
    background-position: center;
    border: 2px solid rgba(248, 248, 248, 0.7);
    box-sizing: border-box;
    /* The shadows stay dark throughout: they are depth, not a palette role. */
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
    transition-property: border-color, box-shadow, color, filter;
    transition-duration: 200ms;
    transition-timing-function: linear;
}

.overviewMapMarker:hover {
    z-index: 4;
}

/*
 * Hover deepens the shadow and changes nothing else.
 *
 * A pin stands on somebody else's artwork, so a hover that recolours it either
 * vanishes into that artwork or fights it, and the colour that reads best over
 * one background is the wrong one over the next. Lifting the pin off the map
 * instead says the same thing against any background, in both pin styles and
 * in every theme. The z-index above does the rest: the hovered pin and its
 * label come out from under their neighbours'.
 */
.overviewMapMarker:hover .overviewMapMarkerBody {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.55);
}

.overviewMapMarker.activeHypervideo {
    z-index: 3;
}

.overviewMapMarker.activeHypervideo .overviewMapMarkerBody {
    border-color: var(--primary-bg-color);
    box-shadow: 0 0 0 3px var(--primary-bg-color), 0 2px 6px rgba(0, 0, 0, 0.4);
}

/* The active pin's ring IS its box-shadow, and it ties with the hover rule on
   specificity — so hovering it would otherwise lose the lift to source order.
   It gets its own hover, keeping the ring and taking the deeper shadow. */
.overviewMapMarker.activeHypervideo:hover .overviewMapMarkerBody {
    box-shadow: 0 0 0 3px var(--primary-bg-color), 0 4px 12px rgba(0, 0, 0, 0.55);
}

/*
 * Titles are always on, not revealed by hover: on a map the whole point of a
 * pin is WHICH hypervideo it is, and a map of anonymous dots makes the reader
 * hunt for that one pin at a time. Hover still raises the pin's z-index, which
 * is what lifts a label out from under a neighbour's on a crowded map.
 */
.overviewMapMarkerLabel {
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    margin-top: 6px;
    padding: 2px 8px;
    border-radius: 4px;
    background: var(--primary-bg-color);
    color: var(--primary-fg-color);
    font-size: 12px;
    white-space: nowrap;
    max-width: 160px;
    overflow: hidden;
    text-overflow: ellipsis;
    pointer-events: none;
    transition: opacity 200ms linear;
}

/* A label on the pin being placed only hides what is being placed. */
.overviewMapMarker.dragging .overviewMapMarkerLabel,
.overviewMapMarker.resizing .overviewMapMarkerLabel {
    opacity: 0;
}

/*
 * Corner resize handles. Appearance and placement come from generic.css, the
 * same as the overlay canvas: they sit just outside the pin's bounding box, so
 * they stay clear of the thumbnail and of the pin's own `move` cursor.
 */
.overviewMapMarker.ui-resizable .ui-resizable-handle {
    display: none;
    z-index: 4;
}

.overviewMap.editActive .overviewMapMarker.editable {
    cursor: move;
}

/*
 * Shown for every editable pin, not only the hovered one: a hover-gated handle
 * is unreachable on touch, and makes it hard to see what is resizable.
 */
.overviewMap.editActive .overviewMapMarker.editable .ui-resizable-handle {
    display: block;
}

.overviewMapMarker.resizing {
    z-index: 5;
}

.overviewMapMarker.dragging {
    opacity: 0.85;
    z-index: 5;
}

/* ------------------------------------------------------------------------ *
 *  Icon pins
 *
 *  A bare glyph, with no disc behind it: a teardrop drawn inside a circle
 *  reads as a badge, not as a pin, and half the point of choosing an icon is
 *  that it looks like something dropped onto a map. The pin's box is still
 *  there and still square, so the hit area, the anchor point and the resize
 *  handles are exactly what they are for a thumbnail pin.
 * ------------------------------------------------------------------------ */

.overviewMap.pinStyle-icon .overviewMapMarkerBody {
    background-color: transparent;
    background-image: none;
    border: 0;
    border-radius: 0;
    /* drop-shadow follows the glyph's alpha; box-shadow would trace the
       invisible square box the glyph sits in. */
    box-shadow: none;
    filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55));
    color: var(--primary-bg-color);
    display: flex;
    align-items: center;
    justify-content: center;
}

/* The same lift as a thumbnail pin, through the shadow the glyph already
   carries. An icon pin has no border to sharpen and its glyph is drawn in the
   background colour at full opacity, so there is nothing to brighten either —
   the shadow is the whole affordance here. */
.overviewMap.pinStyle-icon .overviewMapMarker:hover .overviewMapMarkerBody {
    filter: drop-shadow(0 3px 7px rgba(0, 0, 0, 0.7));
}

/* The active pin has no border to carry its ring, so it is marked by a halo
   in the theme colour instead. */
.overviewMap.pinStyle-icon .overviewMapMarker.activeHypervideo .overviewMapMarkerBody {
    filter: drop-shadow(0 0 5px var(--primary-bg-color)) drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55));
}

/* Ties with the hover rule on specificity, as in thumbnail mode: the active
   icon pin keeps its halo and takes the deeper shadow. */
.overviewMap.pinStyle-icon .overviewMapMarker.activeHypervideo:hover .overviewMapMarkerBody {
    filter: drop-shadow(0 0 5px var(--primary-bg-color)) drop-shadow(0 3px 7px rgba(0, 0, 0, 0.7));
}

.overviewMapMarkerIcon {
    line-height: 1;
    /* The pin owns hover, drag and click; the glyph is decoration. */
    pointer-events: none;
    user-select: none;
}

/*
 * The pin's font-size is its diameter (layoutMarkers), so an em here is a
 * fixed fraction of the pin at every canvas size. The icon font's base rule
 * gives every glyph a 1em box with .2em side margins for inline use; both are
 * cleared, so a wide glyph still sits on the pin's middle. Only font-family
 * carries !important in that rule, so this wins.
 */
.overviewMapMarkerIcon::before {
    display: block;
    font-size: 1em;
    width: auto;
    margin: 0px;
    line-height: 1;
}

/* ------------------------------------------------------------------------ *
 *  Pin animations
 *
 *  All five are map-wide and switched by a single class on the viewport box,
 *  so no pin is ever re-classed to change how the map animates — and the
 *  settings dialog can preview one by putting a real pin inside a real,
 *  miniature viewport box, rather than keeping a second set of rules that
 *  could only drift from these.
 *
 *  They run in editing too. That costs nothing here because they animate the
 *  pin's body, while the resize handles hang off the pin itself: a breathing
 *  pin keeps its handles exactly where the cursor left them.
 *
 *  Nothing but transform and opacity is animated, and the two expensive
 *  effects live on pseudo-elements, so a map of fifty pins stays on the
 *  compositor.
 * ------------------------------------------------------------------------ */

/*
 * One cycle for every pin animation.
 *
 * Each plays its motion in the leading part of the cycle and holds still for
 * the rest, so a pin moves, settles, and moves again rather than never
 * standing still — a map of pins in perpetual motion is exhausting to read,
 * and an affordance that never stops stops being noticed.
 *
 * Where each one stops moving is set by its own keyframe percentages, so
 * changing this number rescales the motion and the pause together. To retune
 * only the pause, move the percentages instead.
 */
.overviewMap {
    --pin-animation-cycle: 3500ms;
}

/*
 * Ripple: two rings expanding out of the pin's outline, the second close
 * behind the first, then quiet until the cycle comes round again.
 *
 * Rings rather than the pin, because growing the pin would grow its hit area
 * and drag its resize handles outwards with it. They are on the pin and not on
 * its body so that the body's icon-mode drop-shadow does not blur them.
 */
.overviewMap.pinAnimation-ripple .overviewMapMarker::before,
.overviewMap.pinAnimation-ripple .overviewMapMarker::after {
    content: '';
    position: absolute;
    top: 0px;
    left: 0px;
    width: 100%;
    height: 100%;
    box-sizing: border-box;
    border: 2px solid var(--primary-bg-color);
    border-radius: 50%;
    transform-origin: center center;
    pointer-events: none;
    animation: overviewMapPinRipple var(--pin-animation-cycle) ease-out infinite;
}

/*
 * The trailing ring. `backwards` matters: without it the ring spends its delay
 * rendering the plain rule above — a hard, fully opaque circle sitting on the
 * pin — instead of holding the frame it is about to animate from.
 */
.overviewMap.pinAnimation-ripple .overviewMapMarker::before {
    animation-delay: 600ms;
    animation-fill-mode: backwards;
}

@keyframes overviewMapPinRipple {
    0% {
        transform: scale(1);
        opacity: 0.8;
    }
    57% {
        transform: scale(2.6);
        opacity: 0;
    }
    100% {
        transform: scale(2.6);
        opacity: 0;
    }
}

/* Pulse: opacity only, so the pin's geometry — and everything positioned
   against it — stays exactly where it is. */
.overviewMap.pinAnimation-pulse .overviewMapMarkerBody {
    animation: overviewMapPinPulse var(--pin-animation-cycle) ease-in-out infinite;
}

@keyframes overviewMapPinPulse {
    0%        { opacity: 1; }
    28%       { opacity: 0.45; }
    57%, 100% { opacity: 1; }
}

.overviewMap.pinAnimation-scale .overviewMapMarkerBody {
    animation: overviewMapPinScale var(--pin-animation-cycle) ease-in-out infinite;
}

@keyframes overviewMapPinScale {
    0%        { transform: scale(1); }
    28%       { transform: scale(1.14); }
    57%, 100% { transform: scale(1); }
}

/* A circle turning about its own centre shows nothing but its content
   rotating. Rocking it about its lowest point swings the whole pin, so it
   reads as a marker planted on the spot it marks. */
.overviewMap.pinAnimation-wobble .overviewMapMarkerBody {
    transform-origin: 50% 100%;
    animation: overviewMapPinWobble var(--pin-animation-cycle) ease-in-out infinite;
}

@keyframes overviewMapPinWobble {
    0%        { transform: rotate(0deg); }
    11%       { transform: rotate(-7deg); }
    21%       { transform: rotate(7deg); }
    32%       { transform: rotate(-4deg); }
    43%, 100% { transform: rotate(0deg); }
}

/*
 * Glow is a halo on ::after carrying a static shadow whose opacity animates,
 * not an animated box-shadow on the pin. An animation replaces the whole
 * property for its duration, and .activeHypervideo's ring IS a box-shadow — so
 * animating the body's would blank the "you are here" ring on the one pin that
 * needs it. Animating opacity also spares the compositor a blurred repaint
 * every frame. It sits behind the pin, so the halo surrounds it rather than
 * washing the thumbnail out.
 */
.overviewMap.pinAnimation-glow .overviewMapMarker::after {
    content: '';
    position: absolute;
    top: 0px;
    left: 0px;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    box-shadow: 0 0 14px 5px var(--primary-bg-color);
    pointer-events: none;
    z-index: -1;
    animation: overviewMapPinGlow var(--pin-animation-cycle) ease-in-out infinite;
}

@keyframes overviewMapPinGlow {
    0%        { opacity: 0.15; }
    28%       { opacity: 0.9; }
    57%, 100% { opacity: 0.15; }
}

/*
 * Stopping an animation is not enough for the two pseudo-element effects:
 * frozen, a ripple is a permanent ring and a glow a permanent halo.
 */
@media (prefers-reduced-motion: reduce) {

    .overviewMap.pinAnimation-pulse .overviewMapMarkerBody,
    .overviewMap.pinAnimation-scale .overviewMapMarkerBody,
    .overviewMap.pinAnimation-wobble .overviewMapMarkerBody {
        animation: none;
    }

    .overviewMap.pinAnimation-ripple .overviewMapMarker::before,
    .overviewMap.pinAnimation-ripple .overviewMapMarker::after,
    .overviewMap.pinAnimation-glow .overviewMapMarker::after {
        display: none;
    }

}

/* ------------------------------------------------------------------------ *
 *  Pin previews in the map settings dialog
 *
 *  The dialog previews a choice with a real .overviewMap holding a real pin,
 *  which is what stops a settings card from advertising something the canvas
 *  does not do. A card is not a map, though. A pin on the canvas stands on
 *  somebody's background image, which is exactly why its palette is the
 *  theme's inverted — while a preview stands on the dialog's own background
 *  colour, where that same inversion paints the pin in the colour already
 *  behind it, and the card goes blank.
 *
 *  So inside a preview, and only there, the pin wears the theme the right way
 *  up. Must stay last: the ripple rings match on specificity, so it is source
 *  order that settles them.
 * ------------------------------------------------------------------------ */

.overviewMap.overviewMapPreview .overviewMapMarkerBody {
    border-color: rgba(53, 57, 60, 0.7);
}

.overviewMap.overviewMapPreview.pinStyle-icon .overviewMapMarkerBody {
    color: var(--primary-fg-color);
}

.overviewMap.overviewMapPreview .overviewMapMarker::before,
.overviewMap.overviewMapPreview .overviewMapMarker::after {
    border-color: var(--primary-fg-color);
}

.overviewMap.overviewMapPreview.pinAnimation-glow .overviewMapMarker::after {
    box-shadow: 0 0 14px 5px var(--primary-fg-color);
}

/*
 * The card lives in the viewport box, not the stage, so it is never clipped
 * by the stage's overflow and never matches the [data-hypervideoid] lookups
 * ViewOverview uses to find an animation anchor.
 */
.overviewMapPopup {
    position: absolute;
    display: none;
    z-index: 10;
}

.overviewMapPopup.open {
    display: block;
}

.overviewMapPopup .hypervideoThumb.mapCard {
    float: none;
    margin: 0;
    width: 240px;
    height: 144px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
}

/*
 * Editing toolbar.
 *
 * On the canvas rather than in the sidebar, because everything on it acts on
 * the canvas: "place a hypervideo" needs somewhere to place it, and the map
 * settings are the ground the pins are placed against. It exists only while
 * the map is being arranged, so the controls appear exactly when they mean
 * something — and "Done" gives the mode a visible end, without which people
 * stay in it and hold the lock against everyone else.
 *
 * Sits in the viewport box, not the stage, so it stays put under both fit
 * modes and is never clipped by the stage's overflow.
 */
.overviewMapToolbar {
    position: absolute;
    top: 10px;
    left: 10px;
    /* Above the popup card, so a pin opened near the corner cannot bury Done. */
    z-index: 11;
    display: none;
    align-items: center;
    gap: 8px;
}

.overviewMap.editActive .overviewMapToolbar {
    display: flex;
}

.overviewMapToolbar button {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 40px;
    /* Opaque, and in the editing palette rather than the theme's: these act on
       the map, they do not present it. A translucent button over an arbitrary
       background image is also simply hard to see. */
    background-color: var(--editing-bg-color);
    border-color: var(--editing-fg-color);
    color: var(--editing-fg-color);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
}

.overviewMapToolbar button:hover {
    background-color: var(--editing-fg-color);
    border-color: var(--editing-bg-color);
    color: var(--editing-bg-color);
}

/* The two icon-only actions are round, matching the thumb option buttons. */
.overviewMapToolbar .overviewMapAddButton,
.overviewMapToolbar .overviewMapSettingsButton {
    width: 40px;
    padding: 0;
    border-radius: 50%;
}

/*
 * Centre the glyph, not the line box around it.
 *
 * The icon span is a flex item, and its line box carries the strut of the
 * inherited 14px UI font: about 2px of descender space below the baseline that
 * the 20px icon box never uses. align-items:center then centres a 22px box
 * holding a 20px glyph, which leaves the glyph 1px above the button's middle.
 * Letting the span centre the glyph itself takes the text baseline out of it,
 * rather than cancelling the gap with a hand-tuned margin that only holds for
 * this particular pairing of font and size.
 */
.overviewMapToolbar button > span[class^="icon-"],
.overviewMapToolbar button > span[class*=" icon-"] {
    display: flex;
    align-items: center;
    justify-content: center;
}

.overviewMapToolbar button [class^="icon-"]::before,
.overviewMapToolbar button [class*=" icon-"]::before {
    display: block;
    font-size: 20px;
    width: 22px;
    margin: 0;
}

/* Done ends the mode, so it is the one labelled action. It reads as primary by
   being the inverse of its neighbours — still the editing palette, no theme
   colour and no colour of its own. */
.overviewMapToolbar .overviewMapDoneButton {
    gap: 4px;
    padding: 0 14px 0 10px;
    border-radius: 20px;
    margin-left: 4px;
}

.overviewMapToolbar .overviewMapDoneButton [class^="icon-"]::before {
    font-size: 16px;
    width: 18px;
}
