/************************************************************************
 ************************************************************************
 * OBT Wall - front end stylesheet - grid families, detail box positions,
 * hover box effects, filters, sortings, pagination and the zoom modal
 *
 * @package     OBT
 * @subpackage  com_OBT_Wall
 * @copyright   © 2026 CS GROUP
 * @license     GNU General Public License version 2 or later
 ************************************************************************
 ************************************************************************/

.obtwall {
    --obtwall-gutter: 20px;
    --obtwall-radius: 6px;
    --obtwall-border-size: 0px;
    --obtwall-border-color: rgba(0, 0, 0, 0.12);
    --obtwall-img-hover-speed: 300ms;
    --obtwall-img-hover-easing: ease;
    --obtwall-img-hover-opacity: 1;
    --obtwall-img-hover-scale: 1;
    --obtwall-box-speed: 300ms;
    --obtwall-box-easing: ease;
    --obtwall-db-speed: 300ms;
    --obtwall-db-easing: ease;
    --obtwall-filter-bg: transparent;
    --obtwall-filter-color: inherit;
    --obtwall-filter-radius: 999px;
    --obtwall-pg-bg: transparent;
    --obtwall-pg-color: inherit;
    --obtwall-pg-radius: 999px;
    --obtwall-cols: 3;
    position: relative;
}

/************************************************************************
 * Grid containers
 ************************************************************************/
.obtwall-grid {
    display: grid;
    grid-template-columns: repeat(var(--obtwall-cols), minmax(0, 1fr));
    gap: var(--obtwall-gutter);
    /* Leaving tiles are pinned in absolute coordinates measured against
       the grid, whatever family laid them out */
    position: relative;
}

.obtwall-grid-list {
    grid-template-columns: minmax(0, 1fr);
}

.obtwall-grid.is-positioned {
    display: block;
    position: relative;
}

.obtwall-grid.is-positioned .obtwall-item {
    position: absolute;
    top: 0;
    left: 0;
    margin: 0;
}

/************************************************************************
 * The tile is placed through the standalone translate property and the
 * entrance and exit effects go through transform: the two compose, so
 * the WHOLE card - border, shadow, contents - animates as one object
 * while the engine keeps moving it around the wall.
 ************************************************************************/
.obtwall-item {
    min-width: 0;
    --obtwall-fx-duration: 0ms;
    --obtwall-fx-delay: 0ms;
    transition:
        translate 0.4s cubic-bezier(0.37, 0.61, 0.59, 0.87),
        transform var(--obtwall-fx-duration) cubic-bezier(0.37, 0.61, 0.59, 0.87) var(--obtwall-fx-delay),
        opacity var(--obtwall-fx-duration) cubic-bezier(0.37, 0.61, 0.59, 0.87) var(--obtwall-fx-delay);
    will-change: translate, transform;
}

.obtwall-item-inner {
    position: relative;
    height: 100%;
    overflow: hidden;
    border-radius: var(--obtwall-radius);
    border: var(--obtwall-border-size) solid var(--obtwall-border-color);
    background: #fff;
    display: flex;
    flex-direction: column;
}

/************************************************************************
 * Media
 ************************************************************************/
.obtwall-media {
    display: block;
    position: relative;
    overflow: hidden;
    line-height: 0;
    flex: 0 0 auto;
    /* An image that fits rather than covers leaves space around itself,
       and that space is what shows through here. It has a variable of its
       own: the tile background of an item with no image at all is a
       different thing and must not follow this setting. */
    background: var(--obtwall-empty-space, transparent);
}

.obtwall-media-link {
    display: block;
    width: 100%;
    height: 100%;
    line-height: 0;
}

.obtwall-media img,
.obtwall-media iframe {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: var(--obtwall-fit, cover);
    border: 0;
    transition:
        transform var(--obtwall-img-hover-speed) var(--obtwall-img-hover-easing),
        opacity var(--obtwall-img-hover-speed) var(--obtwall-img-hover-easing);
}

/************************************************************************
 * Where the layout engine has not fixed the tile height - equal columns,
 * the vertical list and the no script fallback - a row still levels its
 * cards, so a short image would sit in a band of empty space. The image
 * fills whatever room the card gives it instead, unless the layout asks
 * for the aspect ratio to be preserved, in which case each card is only
 * as tall as its own picture.
 ************************************************************************/
.obtwall-media img {
    height: auto;
}

.obtwall-grid:not(.is-positioned) .obtwall-media {
    flex: 1 1 auto;
    min-height: 0;
    /* The only family where the media needs a shape of its own: everywhere
       else the layout engine has already fixed the tile height */
    aspect-ratio: var(--obtwall-ratio, 3 / 2);
}

.obtwall-grid:not(.is-positioned) .obtwall-media img,
.obtwall-grid:not(.is-positioned) .obtwall-media iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

.obtwall-preserve-ratio .obtwall-grid:not(.is-positioned) .obtwall-item {
    align-self: start;
}

.obtwall-preserve-ratio .obtwall-grid:not(.is-positioned) .obtwall-media {
    flex: 0 0 auto;
}

.obtwall-preserve-ratio .obtwall-grid:not(.is-positioned) .obtwall-media img,
.obtwall-preserve-ratio .obtwall-grid:not(.is-positioned) .obtwall-media iframe {
    position: static;
    height: auto;
}

/************************************************************************
 * A picture whose height the script pinned while levelling a row. The
 * image scales, ratio kept, until it fills the whole zone, so a picture
 * shorter than the row is enlarged rather than left over a band of
 * empty space. Wins over the preserve ratio rules above by specificity,
 * because a levelled row has already traded each picture's own height
 * for the row's shared one.
 ************************************************************************/
.obtwall .obtwall-grid .obtwall-media.obtwall-media-pinned img,
.obtwall .obtwall-grid .obtwall-media.obtwall-media-pinned iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: var(--obtwall-fit, cover);
}

.obtwall-grid.is-positioned .obtwall-media {
    height: 100%;
}

.obtwall-grid.is-positioned .obtwall-media img {
    height: 100%;
}

/************************************************************************
 * A full size image only leaves the flow where the wall has a height to
 * fill: the JavaScript positioned families. Everywhere else - equal
 * columns, vertical list, and the no script fallback - it stays in the
 * flow, otherwise the tile would collapse to the height of its text.
 *
 * It also only applies where the detail box floats over the image. A box
 * that sits outside the image needs its own room, so there the image is
 * given whatever is left rather than the whole tile.
 ************************************************************************/
.obtwall-grid.is-positioned .obtwall-fullbleed.obtwall-db-inside-bottom .obtwall-media,
.obtwall-grid.is-positioned .obtwall-fullbleed.obtwall-db-inside-top .obtwall-media,
.obtwall-grid.is-positioned .obtwall-fullbleed.obtwall-db-cover .obtwall-media,
.obtwall-grid.is-positioned .obtwall-fullbleed.obtwall-db-none .obtwall-media {
    position: absolute;
    inset: 0;
    height: 100%;
}

.obtwall-grid:not(.is-positioned) .obtwall-db-inside-bottom .obtwall-item-inner,
.obtwall-grid:not(.is-positioned) .obtwall-db-inside-top .obtwall-item-inner,
.obtwall-grid:not(.is-positioned) .obtwall-db-cover .obtwall-item-inner {
    position: relative;
    min-height: 180px;
}

/************************************************************************
 * A tile with no image would otherwise be mostly empty space above its
 * text, so a floating detail box fills the whole tile instead
 ************************************************************************/
.obtwall-nomedia.obtwall-db-inside-bottom .obtwall-detail,
.obtwall-nomedia.obtwall-db-inside-top .obtwall-detail,
.obtwall-nomedia.obtwall-db-cover .obtwall-detail {
    inset: 0;
    justify-content: flex-end;
}

.obtwall-nomedia .obtwall-item-inner {
    background: var(--obtwall-empty-space, #f2f3f5);
}

.obtwall-hide-images .obtwall-media {
    display: none;
}

/************************************************************************
 * Detail box positions
 ************************************************************************/
.obtwall-detail {
    padding: 16px 18px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    position: relative;
    z-index: 2;
}

/************************************************************************
 * Inside bottom, inside top and cover float the box over the image. The
 * first two hug one edge; cover fills the image and pins the text to the
 * bottom of it, which is what makes it read as a caption over a picture
 * rather than a block of text hanging in the middle.
 ************************************************************************/
.obtwall-db-inside-bottom .obtwall-detail {
    position: absolute;
    inset: auto 0 0 0;
}

.obtwall-db-inside-top .obtwall-detail {
    position: absolute;
    inset: 0 0 auto 0;
}

.obtwall-db-cover .obtwall-detail {
    position: absolute;
    inset: 0;
    justify-content: flex-end;
}

/* Read more normally pushes itself to the foot of the box; in a covering
   box that would spread the text over the whole picture instead of
   keeping it together at the bottom */
.obtwall-db-cover .obtwall-readmore,
.obtwall-nomedia .obtwall-readmore {
    margin-top: 6px;
}

/************************************************************************
 * Outside below and outside above give the box its own room. The image
 * takes what is left of the tile, down to nothing, rather than pushing
 * the box out through the bottom of a tile whose height the layout
 * engine has already fixed.
 ************************************************************************/
.obtwall-db-below .obtwall-detail,
.obtwall-db-above .obtwall-detail {
    position: relative;
    flex: 0 0 auto;
}

.obtwall-db-below .obtwall-media,
.obtwall-db-above .obtwall-media {
    flex: 1 1 auto;
    min-height: 0;
}

/* In a tile whose height the layout engine fixed, the image fills exactly
   the room the box left it, which a percentage height cannot express
   against a flexible parent */
.obtwall-grid.is-positioned .obtwall-db-below .obtwall-media,
.obtwall-grid.is-positioned .obtwall-db-above .obtwall-media {
    height: auto;
    flex: 1 1 auto;
    /* A masonry tile has a height the engine fixed, so a long text could
       squeeze the picture down to a sliver. Both keep a share of the tile:
       whichever is longer, the item still reads as a picture with a
       caption rather than as one or the other. */
    min-height: 40%;
}

.obtwall-grid.is-positioned .obtwall-db-below .obtwall-detail,
.obtwall-grid.is-positioned .obtwall-db-above .obtwall-detail {
    max-height: 60%;
    overflow: hidden;
}

.obtwall-grid.is-positioned .obtwall-db-below .obtwall-media img,
.obtwall-grid.is-positioned .obtwall-db-below .obtwall-media iframe,
.obtwall-grid.is-positioned .obtwall-db-above .obtwall-media img,
.obtwall-grid.is-positioned .obtwall-db-above .obtwall-media iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

.obtwall-db-above .obtwall-detail {
    order: -1;
}

.obtwall-db-left .obtwall-item-inner,
.obtwall-db-right .obtwall-item-inner {
    flex-direction: row;
}

.obtwall-db-left .obtwall-detail {
    order: -1;
    flex: 1 1 50%;
}

.obtwall-db-right .obtwall-detail {
    flex: 1 1 50%;
}

.obtwall-db-left .obtwall-media,
.obtwall-db-right .obtwall-media {
    flex: 1 1 50%;
    position: relative;
}

.obtwall-align-center .obtwall-detail,
.obtwall-detail.obtwall-align-center {
    text-align: center;
    align-items: center;
}

.obtwall-detail.obtwall-align-right {
    text-align: right;
    align-items: flex-end;
}

.obtwall-category,
.obtwall-hover-category {
    font-size: 0.75em;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    opacity: 0.8;
}

.obtwall-date,
.obtwall-hits,
.obtwall-author {
    font-size: 0.8em;
    opacity: 0.7;
}

.obtwall-title {
    margin: 0 0 4px;
    /* Stated in rem so the template's local font sizing cannot compound
       into it: 0.875rem is 14px at the browser's default root size */
    font-size: 0.875rem;
    font-weight: 600;
    line-height: 1.3;
}

/************************************************************************
 * Links inside a detail or hover box take the box's own text colour, so
 * they stay readable on a coloured overlay
 ************************************************************************/
.obtwall-detail a,
.obtwall-hover a {
    color: inherit;
}

.obtwall-title a {
    color: inherit;
    text-decoration: none;
}

.obtwall-category a {
    text-decoration: none;
}

.obtwall-category a:hover,
.obtwall-category a:focus-visible {
    text-decoration: underline;
}

/* A hovered title keeps its colour and stays bare - stated explicitly so
   the template's own link hover styles cannot reach it */
.obtwall-title a:hover,
.obtwall-title a:focus-visible {
    color: inherit;
    text-decoration: none;
}

/* The weight is stated rather than inherited: a site template that
   emboldens the surrounding text would otherwise reach the short intro */
.obtwall-introtext,
.obtwall-hover-text {
    /* 0.75rem is 12px at the browser's default root size */
    font-size: 0.75rem;
    font-weight: 400;
    line-height: 1.5;
}

.obtwall-introtext > :last-child {
    margin-bottom: 0;
}

.obtwall-tags {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    font-size: 0.75em;
}

.obtwall-tags li {
    border: 1px solid currentColor;
    border-radius: 999px;
    padding: 1px 8px;
    opacity: 0.8;
}

.obtwall-readmore {
    margin-top: auto;
    align-self: flex-start;
    font-size: 0.85em;
    font-weight: 600;
    text-decoration: none;
    color: inherit;
}

.obtwall-readmore::after {
    content: " \2192";
}

/************************************************************************
 * Hover box
 ************************************************************************/
.obtwall-hover {
    position: absolute;
    inset: 0;
    z-index: 4;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 18px;
    opacity: 0;
    pointer-events: none;
    transition:
        opacity var(--obtwall-box-speed) var(--obtwall-box-easing),
        transform var(--obtwall-box-speed) var(--obtwall-box-easing);
}

/************************************************************************
 * The hover box belongs to whichever part of the tile it was placed in,
 * so the pointer has to be over that part - the whole tile, the picture
 * or the detail box - for it to appear.
 *
 * The rule is structural rather than keyed to the chosen area, because an
 * item may not have the part the layout named: a video, an item with no
 * picture, or a size whose detail box is switched off. There the box
 * falls back to the tile, and this still reveals it.
 ************************************************************************/
.obtwall-item-inner:hover > .obtwall-hover,
.obtwall-item-inner:focus-within > .obtwall-hover,
.obtwall-media:hover > .obtwall-hover,
.obtwall-media:focus-within > .obtwall-hover,
.obtwall-detail:hover > .obtwall-hover,
.obtwall-detail:focus-within > .obtwall-hover {
    opacity: 1;
    pointer-events: auto;
    transform: none;
}

.obtwall-hover-slide-up { transform: translateY(100%); }
.obtwall-hover-slide-down { transform: translateY(-100%); }
.obtwall-hover-slide-left { transform: translateX(100%); }
.obtwall-hover-slide-right { transform: translateX(-100%); }
.obtwall-hover-zoom-in { transform: scale(0.85); }
.obtwall-hover-zoom-out { transform: scale(1.15); }
.obtwall-hover-flip { transform: rotateY(90deg); }

.obtwall-hover-body {
    text-align: center;
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-height: 100%;
    /* Not "auto": an icon that grows under the pointer would push against
       the edge of a scrolling box and make scrollbars appear around itself */
    overflow: hidden;
}

.obtwall-hover-title {
    margin: 0;
    font-size: 1em;
}

/************************************************************************
 * The hover box icons. Their size, colours and shape all come from the
 * layout, so the same markup gives a filled badge, a thin ring or a bare
 * glyph without the template knowing which was chosen.
 ************************************************************************/
.obtwall-hover-icons {
    display: flex;
    gap: 12px;
    justify-content: center;
    margin-top: 4px;
    /* Room for the icon to grow into. Without it the row is exactly the
       size of the icon at rest and the growth is clipped by the box
       around it. */
    padding: 4px;
    flex: 0 0 auto;
}

.obtwall-icon {
    box-sizing: border-box;
    width: var(--obtwall-icon-size, 56px);
    height: var(--obtwall-icon-size, 56px);
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--obtwall-icon-color, #ffffff);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    font: inherit;
    /* The glyph is drawn at 1em, so this is what sets its size */
    font-size: calc(var(--obtwall-icon-size, 56px) * 0.42);
    line-height: 1;
    text-decoration: none;
    transition:
        transform var(--obtwall-box-speed, 300ms) var(--obtwall-box-easing, ease),
        color var(--obtwall-box-speed, 300ms) var(--obtwall-box-easing, ease),
        background-color var(--obtwall-box-speed, 300ms) var(--obtwall-box-easing, ease);
}

.obtwall-icons-badge .obtwall-icon {
    background: var(--obtwall-icon-bg, #111111);
}

.obtwall-icons-outline .obtwall-icon {
    border: 2px solid currentColor;
}

.obtwall-icon:hover,
.obtwall-icon:focus-visible {
    transform: scale(1.08);
    color: var(--obtwall-icon-hover-color);
    background: var(--obtwall-icon-hover-bg);
}

.obtwall-glyph {
    display: block;
    width: 1em;
    height: 1em;
}

/************************************************************************
 * What happens to the image itself while the pointer is over the tile.
 * Both values come from the layout, so a setting of 1 leaves the image
 * untouched and anything else animates at the configured speed.
 ************************************************************************/
.obtwall-item-inner:hover .obtwall-media img,
.obtwall-item-inner:focus-within .obtwall-media img {
    opacity: var(--obtwall-img-hover-opacity);
    transform: scale(var(--obtwall-img-hover-scale));
}

/************************************************************************
 * The detail box hover effect: how the text panel itself reacts to the
 * pointer. Each variant is applied through a class on the wall.
 ************************************************************************/
.obtwall-dbfx-fade .obtwall-detail,
.obtwall-dbfx-slide-up .obtwall-detail,
.obtwall-dbfx-slide-down .obtwall-detail,
.obtwall-dbfx-slide-left .obtwall-detail,
.obtwall-dbfx-slide-right .obtwall-detail,
.obtwall-dbfx-zoom-in .obtwall-detail,
.obtwall-dbfx-zoom-out .obtwall-detail,
.obtwall-dbfx-flip .obtwall-detail {
    transition:
        opacity var(--obtwall-db-speed) var(--obtwall-db-easing),
        transform var(--obtwall-db-speed) var(--obtwall-db-easing);
}

.obtwall-dbfx-fade .obtwall-detail { opacity: 0; }
/* Sliding only makes sense for a box that floats over the image; one that
   sits outside it would slide over its own neighbour */
.obtwall-dbfx-slide-up .obtwall-db-cover .obtwall-detail,
.obtwall-dbfx-slide-up .obtwall-db-inside-bottom .obtwall-detail,
.obtwall-dbfx-slide-up .obtwall-db-inside-top .obtwall-detail { transform: translateY(100%); }
.obtwall-dbfx-slide-down .obtwall-db-cover .obtwall-detail,
.obtwall-dbfx-slide-down .obtwall-db-inside-bottom .obtwall-detail,
.obtwall-dbfx-slide-down .obtwall-db-inside-top .obtwall-detail { transform: translateY(-100%); }
.obtwall-dbfx-slide-left .obtwall-db-cover .obtwall-detail,
.obtwall-dbfx-slide-left .obtwall-db-inside-bottom .obtwall-detail,
.obtwall-dbfx-slide-left .obtwall-db-inside-top .obtwall-detail { transform: translateX(100%); }
.obtwall-dbfx-slide-right .obtwall-db-cover .obtwall-detail,
.obtwall-dbfx-slide-right .obtwall-db-inside-bottom .obtwall-detail,
.obtwall-dbfx-slide-right .obtwall-db-inside-top .obtwall-detail { transform: translateX(-100%); }
.obtwall-dbfx-zoom-in .obtwall-detail { transform: scale(0.85); opacity: 0; }
.obtwall-dbfx-zoom-out .obtwall-detail { transform: scale(1.15); opacity: 0; }
.obtwall-dbfx-flip .obtwall-detail { transform: rotateX(90deg); opacity: 0; }

[class*="obtwall-dbfx-"] .obtwall-item-inner:hover .obtwall-detail,
[class*="obtwall-dbfx-"] .obtwall-item-inner:focus-within .obtwall-detail {
    opacity: 1;
    transform: none;
}

/************************************************************************
 * Filters and sortings
 ************************************************************************/
.obtwall-filters,
.obtwall-sortings {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-bottom: calc(var(--obtwall-gutter) * 0.8);
}

.obtwall-filter-group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}

.obtwall-filter-label,
.obtwall-sortings-label {
    font-size: 0.85em;
    font-weight: 600;
    opacity: 0.7;
    margin-right: 4px;
}

.obtwall-filter,
.obtwall-sort,
.obtwall-sort-dir,
.obtwall-reset,
.obtwall-filter-toggle {
    border: 1px solid rgba(0, 0, 0, 0.18);
    background: var(--obtwall-filter-bg);
    color: var(--obtwall-filter-color);
    border-radius: var(--obtwall-filter-radius);
    padding: 6px 16px;
    font: inherit;
    font-size: 0.88em;
    cursor: pointer;
    transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

/************************************************************************
 * The active state paints its own background and label, so the label can
 * never disappear into the colour behind it
 ************************************************************************/
.obtwall-filter.is-active,
.obtwall-sort.is-active,
.obtwall-sort-dir.is-desc {
    background: var(--obtwall-filter-accent, #1a1a1a);
    border-color: var(--obtwall-filter-accent, #1a1a1a);
    color: var(--obtwall-filter-accent-text, #fff);
}

.obtwall-filter-count {
    font-size: 0.85em;
    opacity: 0.6;
    margin-left: 5px;
}

.obtwall-filter-dropdown {
    position: relative;
}

.obtwall-filter-menu {
    position: absolute;
    z-index: 20;
    top: calc(100% + 4px);
    left: 0;
    min-width: 200px;
    background: #fff;
    border: 1px solid rgba(0, 0, 0, 0.15);
    border-radius: 6px;
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.12);
    padding: 6px;
    display: flex;
    flex-direction: column;
    max-height: 320px;
    overflow: auto;
}

.obtwall-filter-menu .obtwall-filter {
    border: 0;
    border-radius: 4px;
    text-align: left;
}

.obtwall-search {
    border: 1px solid rgba(0, 0, 0, 0.18);
    border-radius: var(--obtwall-filter-radius);
    padding: 6px 16px;
    font: inherit;
    font-size: 0.88em;
}

/************************************************************************
 * A wall is dropped into somebody else's template, whose own button rules
 * apply to these controls as much as ours do - and a template that
 * reshapes a button on hover would otherwise override the radius the
 * layout asked for. Every interactive state restates it, from the same
 * custom property, so the setting always wins and nothing moves under
 * the pointer.
 ************************************************************************/
.obtwall .obtwall-filter,
.obtwall .obtwall-filter:hover,
.obtwall .obtwall-filter:focus,
.obtwall .obtwall-filter:focus-visible,
.obtwall .obtwall-filter:active,
.obtwall .obtwall-sort,
.obtwall .obtwall-sort:hover,
.obtwall .obtwall-sort:focus,
.obtwall .obtwall-sort:focus-visible,
.obtwall .obtwall-sort:active,
.obtwall .obtwall-sort-dir,
.obtwall .obtwall-sort-dir:hover,
.obtwall .obtwall-sort-dir:focus,
.obtwall .obtwall-sort-dir:focus-visible,
.obtwall .obtwall-sort-dir:active,
.obtwall .obtwall-reset,
.obtwall .obtwall-reset:hover,
.obtwall .obtwall-reset:focus,
.obtwall .obtwall-reset:focus-visible,
.obtwall .obtwall-reset:active,
.obtwall .obtwall-filter-toggle,
.obtwall .obtwall-filter-toggle:hover,
.obtwall .obtwall-filter-toggle:focus,
.obtwall .obtwall-filter-toggle:focus-visible,
.obtwall .obtwall-filter-toggle:active,
.obtwall .obtwall-search,
.obtwall .obtwall-search:hover,
.obtwall .obtwall-search:focus,
.obtwall .obtwall-search:focus-visible {
    border-radius: var(--obtwall-filter-radius);
}

/* A button inside the dropdown keeps the panel's own smaller radius */
.obtwall .obtwall-filter-menu .obtwall-filter,
.obtwall .obtwall-filter-menu .obtwall-filter:hover,
.obtwall .obtwall-filter-menu .obtwall-filter:focus,
.obtwall .obtwall-filter-menu .obtwall-filter:focus-visible,
.obtwall .obtwall-filter-menu .obtwall-filter:active {
    border-radius: 4px;
}

.obtwall .obtwall-loadmore,
.obtwall .obtwall-loadmore:hover,
.obtwall .obtwall-loadmore:focus,
.obtwall .obtwall-loadmore:focus-visible,
.obtwall .obtwall-loadmore:active,
.obtwall .obtwall-arrow,
.obtwall .obtwall-arrow:hover,
.obtwall .obtwall-arrow:focus,
.obtwall .obtwall-arrow:focus-visible,
.obtwall .obtwall-arrow:active,
.obtwall .obtwall-page,
.obtwall .obtwall-page:hover,
.obtwall .obtwall-page:focus,
.obtwall .obtwall-page:focus-visible,
.obtwall .obtwall-page:active {
    border-radius: var(--obtwall-pg-radius);
}

.obtwall .obtwall-icon,
.obtwall .obtwall-icon:hover,
.obtwall .obtwall-icon:focus,
.obtwall .obtwall-icon:focus-visible,
.obtwall .obtwall-icon:active {
    border-radius: 50%;
}

.obtwall .obtwall-item-inner,
.obtwall .obtwall-item-inner:hover,
.obtwall .obtwall-item-inner:focus-within {
    border-radius: var(--obtwall-radius);
}

/************************************************************************
 * A fixed picture height, so that every tile in a row shows the same
 * amount of image whatever shape the picture itself is
 ************************************************************************/
.obtwall-fixed-media .obtwall-grid:not(.is-positioned) .obtwall-media {
    height: var(--obtwall-media-height);
    flex: 0 0 auto;
    aspect-ratio: auto;
}

/* The zone height is imposed here too, so the image fills it the same
   way a script pinned one does - after the preserve ratio rules, which
   it has to win against */
.obtwall-fixed-media .obtwall-grid:not(.is-positioned) .obtwall-media img,
.obtwall-fixed-media .obtwall-grid:not(.is-positioned) .obtwall-media iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: var(--obtwall-fit, cover);
}

/************************************************************************
 * The tile shadow. It is drawn on the tile rather than on its inner box,
 * because the inner box clips its own contents and a shadow cast from
 * inside a clipped element would be trimmed with them.
 ************************************************************************/
.obtwall-has-shadow .obtwall-item {
    box-shadow: var(--obtwall-shadow);
    border-radius: var(--obtwall-radius);
    transition:
        translate 0.4s cubic-bezier(0.37, 0.61, 0.59, 0.87),
        transform var(--obtwall-fx-duration) cubic-bezier(0.37, 0.61, 0.59, 0.87) var(--obtwall-fx-delay),
        opacity var(--obtwall-fx-duration) cubic-bezier(0.37, 0.61, 0.59, 0.87) var(--obtwall-fx-delay),
        box-shadow var(--obtwall-shadow-speed, 300ms) var(--obtwall-shadow-easing, ease);
}

.obtwall-has-shadow-hover .obtwall-item:hover,
.obtwall-has-shadow-hover .obtwall-item:focus-within {
    box-shadow: var(--obtwall-shadow-hover);
}

/************************************************************************
 * Pagination
 ************************************************************************/
.obtwall-pagination {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 10px;
    margin-top: calc(var(--obtwall-gutter) * 1.2);
    flex-wrap: wrap;
}

.obtwall-loadmore,
.obtwall-arrow,
.obtwall-page {
    border: 1px solid rgba(0, 0, 0, 0.22);
    background: var(--obtwall-pg-bg);
    color: var(--obtwall-pg-color);
    border-radius: var(--obtwall-pg-radius);
    padding: 9px 26px;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.2s ease, color 0.2s ease;
}

.obtwall-arrow,
.obtwall-page {
    padding: 8px 14px;
    min-width: 40px;
}

.obtwall-arrow[disabled] {
    opacity: 0.4;
    cursor: default;
}

.obtwall-page.is-active,
.obtwall-loadmore:hover,
.obtwall-loadmore:focus-visible {
    background: var(--obtwall-pg-accent, #1a1a1a);
    border-color: var(--obtwall-pg-accent, #1a1a1a);
    color: var(--obtwall-pg-accent-text, #fff);
}

.obtwall-loadmore-count {
    display: inline-block;
    margin-left: 8px;
    font-size: 0.85em;
    opacity: 0.7;
}

.obtwall-loading {
    font-size: 0.85em;
    opacity: 0.7;
}

.obtwall.is-loading .obtwall-grid {
    opacity: 0.6;
}

.obtwall-empty {
    padding: 20px 0;
    opacity: 0.7;
    font-style: italic;
}

.obtwall-description {
    margin-bottom: var(--obtwall-gutter);
}

/************************************************************************
 * Transition effects applied when a batch enters or leaves the wall.
 *
 * The tile's own transform is what places it on the wall, so the effects
 * are drawn on the inner box instead: the two can never fight, and an
 * effect works the same in every layout family. The engine sets the
 * duration and the per tile stagger through the two custom properties.
 ************************************************************************/
.obtwall-item.obtwall-leave {
    pointer-events: none;
}

.obtwall-fx-fade.obtwall-enter,
.obtwall-fx-fade.obtwall-leave { opacity: 0; }
.obtwall-fx-scale-up.obtwall-enter,
.obtwall-fx-scale-up.obtwall-leave { transform: scale(0.9); }
.obtwall-fx-scale-down.obtwall-enter,
.obtwall-fx-scale-down.obtwall-leave { transform: scale(1.1); }
.obtwall-fx-translate-x.obtwall-enter,
.obtwall-fx-translate-x.obtwall-leave { transform: translateX(40px); }
.obtwall-fx-translate-y.obtwall-enter,
.obtwall-fx-translate-y.obtwall-leave { transform: translateY(40px); }
.obtwall-fx-translate-z.obtwall-enter,
.obtwall-fx-translate-z.obtwall-leave { transform: perspective(600px) translateZ(-80px); }
.obtwall-fx-rotate-x.obtwall-enter,
.obtwall-fx-rotate-x.obtwall-leave { transform: perspective(600px) rotateX(45deg); }
.obtwall-fx-rotate-y.obtwall-enter,
.obtwall-fx-rotate-y.obtwall-leave { transform: perspective(600px) rotateY(45deg); }
.obtwall-fx-rotate-z.obtwall-enter,
.obtwall-fx-rotate-z.obtwall-leave { transform: rotate(8deg); }

/* The mosaic preset - tiles grow in while straightening up, and shrink
   away spinning the other way. scale(0.001) rather than scale(0): a zero
   scale is a non invertible matrix some engines refuse to animate.

   The top left pivot sits on the class that stays on the tile, not on
   the enter state: an origin declared only on the start state would
   snap back to the centre the moment the movement begins, and the card
   would straighten around its middle instead of hanging from its
   corner. The exit pivots from the centre, as the original does. */
.obtwall-fx-mosaic {
    transform-origin: 0 0;
}

.obtwall-fx-mosaic.obtwall-enter {
    opacity: 0;
    transform: scale(0.5) rotate(-10deg);
}

.obtwall-fx-mosaic.obtwall-leave {
    opacity: 0;
    transform-origin: 50% 50%;
    transform: scale(0.001) rotate(10deg);
}

/************************************************************************
 * Zoom modal
 ************************************************************************/
.obtwall-modal {
    position: fixed;
    inset: 0;
    z-index: 1080;
    background: rgba(0, 0, 0, 0.85);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 5vh 5vw;
}

.obtwall-modal figure {
    margin: 0;
    max-width: 100%;
    max-height: 100%;
    text-align: center;
}

.obtwall-modal img {
    max-width: 100%;
    max-height: 80vh;
    object-fit: contain;
}

.obtwall-modal figcaption {
    color: #fff;
    margin-top: 12px;
}

.obtwall-modal-close {
    position: absolute;
    top: 16px;
    right: 20px;
    background: transparent;
    border: 0;
    color: #fff;
    font-size: 2.4rem;
    line-height: 1;
    cursor: pointer;
}

/************************************************************************
 * Reduced motion
 ************************************************************************/
@media (prefers-reduced-motion: reduce) {

    .obtwall-item,
    .obtwall-item-inner,
    .obtwall-media img,
    .obtwall-hover,
    .obtwall-detail,
    .obtwall-filter,
    .obtwall-loadmore {
        transition: none !important;
    }
}
