@keyframes FadeInDisplayFlexNone {
    0% { display: none; opacity: 0; }
    1% { display: flex; opacity: 0; }
    100% { opacity: 1; }
}

@keyframes FadeOutDisplayFlexNone {
    0% { display: flex; opacity: 1; }
    99% { display: flex; opacity: 0; }
    100% { display: none; opacity: 0; }
}

.gallery-item {
    position: relative;
    width: 100%;
    border: 1px solid var(--gray-200);
    /* Opaque on purpose: a see-through fill would show whatever a tile happens to overlap */
    background-color: var(--gray-100);
    .square-tile-content {
        cursor: pointer;
        aspect-ratio: 1; /* Make sure the block is always at least square size (1/1 ratio) */
    }
    > * {
        position: relative;
    }
    a[data-fancybox="gallery"] {
        text-decoration: initial;
    }
    .bg-image-wrapper {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        img {
            width: 100%;
            height: 100%;
            object-fit: cover;
        }
    }
    .gallery-tile-content {
        position: relative;
        display: grid;
        grid-template-rows: auto 1fr auto;
        width: 100%;
        min-height: 100%;
        overflow: hidden;
        align-items: center;
    }
    .gallery-tile-middle {
        display: block; /* Fixes "word-wrap: break-word;" for elements inside */
        width: 100%;
        .description {
            max-width: 100%;
            text-align: center;
            overflow: hidden;
            word-wrap: break-word;
        }
    }
    .gallery-tile-action-overlay {
        align-items: center;
        justify-content: center;
        position: absolute;
        min-width: 100%;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        display: none; /** Important to keep "display: none" for Symfony Turbo loading="lazy" to work properly */
        animation: FadeOutDisplayFlexNone 500ms ease;
        z-index: 1;
    }
    /*
     * A corner of the photo to drag it around by. The size below is what makes it findable with a
     * finger: bigger than a button would be, and against the two edges so the corner itself counts
     * as well. A tap landing here opens neither the photo nor its long press actions, which is what
     * keeps it to one corner.
     *
     * The icon is a fixed square pinned to that same corner. It cannot be a percentage of the
     * handle: these tiles keep their photo ratio, so a 40% × 40% handle is a different rectangle
     * on every photo, and an icon sized from that would sit somewhere else each time.
     */
    .gallery-tile-drag-handle {
        --drag-icon-size: 4.5rem;
        --drag-icon-inset: .85rem;
        position: absolute;
        bottom: 0;
        left: 0;
        width: 40%;
        height: 40%;
        touch-action: none; /* Dragging from the handle must not be read as a scroll gesture */
        z-index: 2;
        svg {
            position: absolute;
            width: var(--drag-icon-size);
            height: var(--drag-icon-size);
            /*
             * mdi:drag keeps empty viewBox around the dots (7/24 on the sides, 5/24 under them).
             * Pulling the box back by that empty space is what puts the dots, not the box, at
             * the same inset from the left as from the bottom.
             */
            left: calc(var(--drag-icon-inset) - 7 / 24 * var(--drag-icon-size));
            bottom: calc(var(--drag-icon-inset) - 5 / 24 * var(--drag-icon-size));
            /* Faded, so a photo stays a photo; the shadow keeps the dots off a white one */
            color: var(--white);
            opacity: .7;
            filter: drop-shadow(0 0 2px rgba(0, 0, 0, .85));
            /* A marking, not the target: every drag and press belongs to the handle around it */
            pointer-events: none;
        }
    }
    .file-type-badge {
        color: var(--text-color);
        border: 1px solid var(--gray-200);
        background-color: var(--blue-100);
        padding: .2rem .5rem;
        border-radius: var(--bs-border-radius);
    }
    /*
     * The field grows along with what is typed (see the textarea-autoresize controller), so it has
     * no handle to drag. The margin keeps it off the edges of the tile it stands in.
     */
    .file-description-field {
        resize: none;
        margin: 0 .25rem .25rem;
        width: calc(100% - .5rem);
    }
    .gallery-tile-sequence {
        /* A little in from the corner, and see-through enough to read the photo underneath */
        margin: .15rem;
        opacity: .85;
    }
    &.is-hidden-file {
        &:after {
            display: flex;
            align-items: center;
            justify-content: center;
            content: url("data:image/svg+xml;charset=UTF-8, <svg xmlns='http://www.w3.org/2000/svg' width='2.5rem' height='2.5rem' viewBox='0 0 640 512'><path fill='white' d='M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2s-6.3 25.5 4.1 33.7l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7l-105.2-82.4c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8zm151 118.3C226 97.7 269.5 80 320 80c65.2 0 118.8 29.6 159.9 67.7C518.4 183.5 545 226 558.6 256c-12.6 28-36.6 66.8-70.9 100.9l-53.8-42.2c9.1-17.6 14.2-37.5 14.2-58.7c0-70.7-57.3-128-128-128c-32.2 0-61.7 11.9-84.2 31.5zm205.1 160.8l-81.5-63.9c4.2-8.5 6.6-18.2 6.6-28.3c0-5.5-.7-10.9-2-16h2c44.2 0 80 35.8 80 80c0 9.9-1.8 19.4-5.1 28.2m9.4 130.3C378.8 425.4 350.7 432 320 432c-65.2 0-118.8-29.6-159.9-67.7C121.6 328.5 95 286 81.4 256c8.3-18.4 21.5-41.5 39.4-64.8l-37.7-29.7c-22.8 29.7-39.1 59.3-48.6 82.2c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1c47 43.8 111.7 80.6 192.5 80.6c47.8 0 89.9-12.9 126.2-32.5zM192 256c0 70.7 57.3 128 128 128c13.3 0 26.1-2 38.2-5.8L302 334c-23.5-5.4-43.1-21.2-53.7-42.3l-56.1-44.2c-.2 2.8-.3 5.6-.3 8.5z'/></svg>");
            height: 100%;
            width: 100%;
            position: absolute;
            top: 0;
            left: 0;
            margin: auto;
            text-align: center;
            background-color: rgba(255, 0, 0, .5);
            pointer-events: none;
            opacity: .5;
        }
        &:hover:after {
            content: '';
            opacity: .25;
        }
    }
}

/**
 * Every tile gets the same height, the width follows the ratio of the photo (no cropping).
 * Combine this with a thumb filter that scales towards a fixed height, like "liip_thumb_large".
 */
.grid-with-columns.file-grid-keep-ratio {
    display: flex;
    flex-wrap: wrap;
    /*
     * Tiles on one line differ in height, a description is as long as it is typed. Stretched to the
     * tallest of them, the shorter tiles show their background below the photo. That is also why the
     * grey appears to go away while dragging: the tallest tile leaves the line for as long as it is held.
     */
    align-items: flex-start;
    .gallery-item {
        width: auto;
        /*
         * A tile is as wide as its photo is at the set height, and a landscape photo is easily
         * wider than a narrow column. Such a tile has to give in to the column instead of running
         * past it, over whatever stands next to it.
         */
        max-width: 100%;
    }
    .square-tile-content {
        aspect-ratio: auto;
        height: var(--grid-with-columns-min);
    }
    /*
     * A photo that ran into the width of the column above is less high than the others. Letting the
     * tile follow its photo is what keeps the grey of the tile from showing under it.
     */
    .square-tile-content:has(> a[data-type="image"]) {
        height: auto;
    }
    a[data-fancybox="gallery"] {
        display: block;
        /* A file without a photo has nothing filling its tile, this keeps its badge in the middle */
        height: 100%;
    }
    /* Files without a photo have no image to take their width from, they stay square */
    a[data-type="iframe"] {
        width: var(--grid-with-columns-min);
    }
    /* The image stays in the flow, that way the tile is exactly as wide as the photo */
    .bg-image-wrapper {
        position: relative;
        width: auto;
        img {
            width: auto;
            /*
             * The height is what sizes the photo, so every tile ends up on one height, and the
             * width follows from the ratio. Written as a maximum, because the width of the column
             * is a limit as well: a photo too wide for it scales down on both sides at once.
             */
            height: auto;
            max-height: var(--grid-with-columns-min);
            max-width: 100%;
            object-fit: contain;
        }
    }
    a[data-type="image"] .gallery-tile-content {
        position: absolute;
        inset: 0;
    }
    .sub-gallery-tile {
        /* "width: 0" keeps the text below the tile from stretching it, the photo decides the width */
        width: 0;
        min-width: 100%;
        overflow-wrap: break-word;
    }
}

/**
 * Two photos per row on a phone, both exactly as high as one another.
 *
 * The row breaks from the template close a flex line after every second tile, and the two tiles on
 * that line divide it in proportion to their photo ratio, measured by the gallery-tile-ratio
 * controller. Dividing the width that way is what leaves both photos on one height: a wide photo
 * claims the bigger share of the row, a narrow one the smaller share, and since each photo keeps
 * its own ratio at the width it ends up with, those heights are equal. Nothing is cropped and no
 * tile is padded out to a size its photo does not have.
 */
.grid-with-columns.file-grid-keep-ratio.file-grid-rows-of-two {
    /* How far the photos sit apart, sideways as well as above one another */
    --file-grid-tile-gap: 2px;

    column-gap: var(--file-grid-tile-gap);
    /* The break between two rows is a flex line of its own, and it carries the spacing itself.
       A row gap on top of that would count double, once above and once below that line. */
    row-gap: 0;
    .file-grid-row-break {
        /* Has to be wider than whatever is left on the line, or it joins that line instead of
           ending it. The tiles themselves start from nothing, so a full row would just fit. */
        flex-basis: calc(100% + 1px);
        height: var(--file-grid-tile-gap);
    }
    .gallery-item {
        flex-grow: var(--tile-ratio, 1);
        /* Starting from nothing is what keeps a pair together on one line: a thumb of its own is
           already wider than a phone screen, which would have flex wrap after every single tile */
        flex-basis: 0;
        /* Without this a flex item refuses to become narrower than the photo it holds */
        min-width: 0;
        /* No frame around a photo here, the gap between two of them is separation enough */
        border: 0;
    }
    .square-tile-content {
        height: auto;
    }
    .bg-image-wrapper img {
        /* Inline is the default, and would reserve room for text descenders under the photo */
        display: block;
        width: 100%;
        height: auto;
        /* Here the row divides the width, a ceiling on the height would break up the pair */
        max-height: none;
    }
    /* A document has no photo to be measured, it keeps the square share it falls back to */
    a[data-type="iframe"] {
        width: auto;
        aspect-ratio: 1;
    }
    /* A last photo without a partner would otherwise be stretched over the full width */
    .gallery-item:first-child:last-child,
    .file-grid-row-break + .gallery-item:last-child {
        max-width: 50%;
    }
    /* The description sits under the photo; black/white keeps it readable on any image */
    .sub-gallery-tile {
        background-color: var(--black);
        color: var(--white);
    }
}

.file-action-overlay-buttons {
    min-width: 100%;
    display: inline-flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: .15rem;
}

.square-tile-content:not(.has-long-press-actions):hover .gallery-tile-action-overlay,
.square-tile-content.actions-visible .gallery-tile-action-overlay {
    display: flex;
    animation: FadeInDisplayFlexNone 500ms ease;
}

.square-tile-content.has-long-press-actions {
    /**
     * Keep the browser from selecting text or opening the native link menu while the tile is held down
     */
    -webkit-touch-callout: none;
    user-select: none;
}

/**
 * A tile is half a phone screen wide, which is not enough room for buttons a finger can hit. So on a
 * phone the actions are not put over the photo at all, but read as a bar over the full width of the
 * screen. Which photo it acts on is told by the outline on the tile, further down.
 *
 * The offset keeps the bar clear of whatever the page marked as having to stay reachable, the
 * actions of a mobile form for instance. That height is measured in long_press_actions_controller.js:
 * such a strip is a wrapping flex row, so how many lines it takes is only known on opening.
 */
.square-tile-content.has-long-press-actions .gallery-tile-action-overlay {
    position: fixed;
    /* Undoes the placement of a tile overlay, which is centred over its photo and as wide as it */
    top: auto;
    transform: none;
    min-width: 0;
    left: 0;
    right: 0;
    bottom: calc(var(--pwa-footer-height) + var(--gallery-tile-action-bar-offset, 0px));
    padding: .5rem;
    border-top: 1px solid var(--gray-300);
    background-color: var(--white);
    box-shadow: 0 -2px 6px rgba(0, 0, 0, .1);
    /* Must stay below modals/popovers/tooltips and below the sticky footer (1020) */
    z-index: 999;
}

/*
 * While the bar is open, this covers the rest of the screen so a tap meant to dismiss it cannot
 * land on another photo and open Fancybox. The dimming is what tells the bar has the focus; the
 * ring on the photo still shows through, so it is clear which file the bar belongs to. It has to
 * sit under the bar (999) and must not live inside it: a tap inside the bar is an action, a tap
 * on this is only a close.
 */
.square-tile-content.has-long-press-actions .gallery-tile-action-mask {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 998;
    background-color: rgba(0, 0, 0, .4);
}

.square-tile-content.actions-visible .gallery-tile-action-mask {
    display: block;
}

/*
 * The bar is as wide as the screen and hands that width to its content, which does not happen by
 * itself: the frame is an inline element, so it shrinks to fit and the description field inside it
 * is left with no parent width to resolve its percentage against, falling back to the intrinsic
 * width of a textarea (about twenty characters). Turning the frame into a block hands the width on,
 * and everything below it is block-level and follows. Placing the content is the bar's own business,
 * so nothing loaded into the frame has to know it is being shown in one.
 */
.square-tile-content.has-long-press-actions .gallery-tile-action-overlay > turbo-frame {
    display: block;
    width: 100%;
}

/* Until the actions arrive the frame holds nothing but a spinner, which has no width to fill */
.square-tile-content.has-long-press-actions .gallery-tile-action-overlay > turbo-frame:not([complete]) {
    display: flex;
    justify-content: center;
}

.square-tile-content.has-long-press-actions .file-action-overlay-buttons {
    gap: .5rem;
}

.square-tile-content.has-long-press-actions .file-action-overlay-buttons .btn {
    align-items: center;
    justify-content: center;
    min-width: 2.75rem;
    min-height: 2.75rem;
}

/* The icons are rendered at 16px through svg attributes, which this overrules */
.square-tile-content.has-long-press-actions .file-action-overlay-buttons .btn svg {
    width: 1.5rem;
    height: 1.5rem;
}

/*
 * The bar stands down at the screen edge, so this ring is the only thing left telling which photo it
 * acts on. It has to be a pseudo-element rather than an outline on the tile: the photo is absolutely
 * positioned and paints over the box decorations of the tile itself, which would leave the tile
 * looking untouched. Being last in tree order puts the ring over the photo without a z-index of its
 * own, and the bar keeps its own 999 so it still stands above this.
 *
 * What must not be set here is a z-index on the tile: that would turn the tile into a stacking
 * context and trap the bar inside it, under the very strip it is meant to stay above.
 */
.square-tile-content.actions-visible::after {
    content: '';
    position: absolute;
    inset: 0;
    border: 3px solid var(--cibor-blue);
    /* The white hairline keeps the ring readable on a photo that is blue itself */
    box-shadow: inset 0 0 0 1px var(--white);
    /* Nothing to hit: taps belong to the photo underneath */
    pointer-events: none;
}

/* The handle would otherwise sit on top of the bar, and reordering is not what actions are for */
.square-tile-content.actions-visible .gallery-tile-drag-handle {
    display: none;
}

body.is-scrolling .square-tile-content {
    /**
     * Prevent unnecessary backend ajax calls when just scrolling (and hovering over file blocks)
     */
    pointer-events: none;
}
