/* EchoReveal (D-005). Hidden with `opacity`, so that keyboard and screen reader users reach everything. Without a script
   and in print nothing is hidden, and if the script fails the failsafe shows it. Hidden is what the script will bring in,
   as the nearest `ebx-animate-` class says: a scope ends at the next class inside it (D-028). Without a class, it is
   what the visitor's system lets move. */
@media screen and (scripting: enabled) and (prefers-reduced-motion: no-preference) {
  html:not(.ebx-reveal) :is(.reveal, [data-reveal]):not(:is(.ebx-animate-auto, .ebx-animate-off, .ebx-animate-reduced, .ebx-animate-always), :is(.ebx-animate-auto, .ebx-animate-off, .ebx-animate-reduced, .ebx-animate-always) *) {
    opacity: 0;
    animation: ebx-failsafe 1ms 4s forwards;
  }

  @scope (.ebx-animate-auto:not(.ebx-reveal, .ebx-reveal *)) to (.ebx-animate-off) {
    :scope:is(.reveal, [data-reveal]),
    :is(.reveal, [data-reveal]) {
      opacity: 0;
      animation: ebx-failsafe 1ms 4s forwards;
    }
  }
}

@media screen and (scripting: enabled) {
  @scope (:is(.ebx-animate-reduced, .ebx-animate-always):not(.ebx-reveal, .ebx-reveal *)) to (.ebx-animate-auto, .ebx-animate-off) {
    :scope:is(.reveal, [data-reveal]),
    :is(.reveal, [data-reveal]) {
      opacity: 0;
      animation: ebx-failsafe 1ms 4s forwards;
    }
  }
}

@media screen {
  [data-ebx-wait] {
    opacity: 0;
  }
}

@keyframes ebx-failsafe {
  to {
    opacity: 1;
  }
}

/* EchoCounter: digits of one width, so that the number does not jitter while it counts. */
[data-count] {
  font-variant-numeric: tabular-nums;
}

/* EchoSlider. Before the script, and without one, a row that scrolls: the layout is already right, and every slide can
   still be reached. Widths come from `--per-view`, which a site can also set in its own CSS for the first paint. */
[data-slider] {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}

[data-slider] > * {
  flex: 0 0 calc(100% / var(--per-view, 1));
  min-width: 0;
  scroll-snap-align: start;
}

@media (scripting: none) {
  [data-slider] {
    scrollbar-width: auto;
  }
}

/* A slide's own image or video, also inside a link or a <picture>, fills the slide and is cropped to it: slides are as
   tall as the tallest, so one taller video leaves no gap under the photos. Strong enough to win over a site's global
   `img { height: auto }`; a site changes it with a rule that names its slider. */
:is([data-slider], .ebx-slider) > :not(.ebx-control) > :is(img, video),
:is([data-slider], .ebx-slider) > :not(.ebx-control) > :is(a, picture) > :is(img, video) {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

:is([data-slider], .ebx-slider) > :not(.ebx-control) > :is(a, picture):has(> img, > video) {
  display: block;
  height: 100%;
}

/* Centred before the script too: the first slide starts in the middle, and each one stops there (D-030). */
[data-slider*='"centerMode":true']:not(.ebx-slider) > * {
  scroll-snap-align: center;
}

[data-slider*='"centerMode":true']:not(.ebx-slider) > :first-child {
  margin-inline-start: calc((100% - 100% / var(--per-view, 1)) / 2);
}

[data-slider*='"centerMode":true']:not(.ebx-slider) > :last-child {
  margin-inline-end: calc((100% - 100% / var(--per-view, 1)) / 2);
}

/* The first paint with several in view, under any policy. In this order, the largest wins over a smaller breakpoint's. */
[data-slider*='"slidesToShow":2'] { --per-view: 2; }
[data-slider*='"slidesToShow":3'] { --per-view: 3; }
[data-slider*='"slidesToShow":4'] { --per-view: 4; }
[data-slider*='"slidesToShow":5'] { --per-view: 5; }
[data-slider*='"slidesToShow":6'] { --per-view: 6; }

/* `clip` rather than `hidden`: nothing may scroll this box, not even the browser looking for a focused element. The
   z-index of the slides and controls stays inside it, so a sticky header of the site with any z-index above 0 covers
   them all. */
.ebx-slider {
  position: relative;
  isolation: isolate;
  display: flex;
  overflow: clip;
  scroll-snap-type: none;
}

.ebx-slider > :not(.ebx-control) {
  flex: 0 0 calc(100% / var(--per-view, 1));
  box-sizing: border-box;
  min-width: 0;
  min-height: 0;
}

.ebx-vertical {
  flex-direction: column;
}

.ebx-stacked {
  display: grid;
}

.ebx-stacked > :not(.ebx-control) {
  grid-area: 1 / 1;
}

.ebx-grab {
  -webkit-user-select: none;
  user-select: none;
  touch-action: pan-y;
}

.ebx-grab.ebx-vertical {
  touch-action: pan-x;
}

.ebx-dragging {
  cursor: grabbing;
}

/* The controls sit on the slides and carry their own contrast. `:where` keeps them easy for a site to restyle. */
:where(button.ebx-control) {
  display: grid;
  place-items: center;
  box-sizing: border-box;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 2px solid #fff;
  border-radius: 50%;
  background: rgb(0 0 0 / .65);
  color: #fff;
  cursor: pointer;
}

/* Never what the pointer is on: replacing an icon under it would lose a click, and in WebKit the mouseleave that follows. */
:where(.ebx-control, .ebx-lightbox-thumb) svg {
  pointer-events: none;
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

:where(.ebx-dots) {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px;
}

:where(.ebx-dot) {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

:where(.ebx-dot)::before {
  content: '';
  box-sizing: border-box;
  width: 14px;
  height: 14px;
  border: 2px solid #fff;
  border-radius: 50%;
  background: rgb(0 0 0 / .65);
}

:where(.ebx-dot[aria-current])::before {
  border-color: #000;
  background: #fff;
}

:where(.ebx-control:focus-visible, .ebx-dot:focus-visible) {
  outline: 3px solid #fff;
  outline-offset: 2px;
  box-shadow: 0 0 0 2px #000;
}

:where(.ebx-control[aria-disabled='true']) {
  opacity: .4;
  cursor: default;
}

/* Only inside the slider: `appendArrows` and `appendDots` leave the place to the site. Above an effect's slide, at 2. */
.ebx-slider > .ebx-control {
  position: absolute;
  z-index: 3;
}

.ebx-slider > .ebx-prev,
.ebx-slider > .ebx-next {
  inset-block-start: 50%;
  translate: 0 -50%;
}

.ebx-slider > .ebx-prev {
  inset-inline-start: 8px;
}

.ebx-slider > .ebx-next {
  inset-inline-end: 8px;
}

.ebx-vertical > .ebx-prev,
.ebx-vertical > .ebx-next {
  inset-inline: 50% auto;
  translate: -50% 0;
}

.ebx-vertical > .ebx-prev {
  inset-block: 8px auto;
}

.ebx-vertical > .ebx-next {
  inset-block: auto 8px;
}

.ebx-vertical > :is(.ebx-prev, .ebx-next) svg {
  rotate: 90deg;
}

.ebx-slider > .ebx-pause {
  inset: auto auto 8px 8px;
}

.ebx-slider > .ebx-dots {
  inset: auto 60px 8px;
}

/* The arrows take the middle of the top and the bottom here, so the dots go to the side. */
.ebx-vertical > .ebx-dots {
  inset: 60px 8px 60px auto;
  flex-direction: column;
}

/* EchoLightbox: a dialog over the whole viewport. The page behind keeps its place and, where it had a scroll bar, its
   width. */
html.ebx-locked {
  overflow: hidden;
}

html.ebx-gutter {
  scrollbar-gutter: stable;
}

.ebx-lightbox {
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  overflow: clip;
  background: rgb(0 0 0 / .92);
  color: #fff;
  /* Every gesture is its own: a swipe, a pinch and a pan. */
  touch-action: none;
}

.ebx-lightbox::backdrop {
  background: none;
}

/* What shows: one element, as only an element inside the dialog can go full screen. */
.ebx-lightbox-view {
  position: absolute;
  inset: 0;
}

.ebx-lightbox-track {
  position: absolute;
  inset: 0;
  display: flex;
  overflow: clip;
}

.ebx-lightbox-item {
  position: relative;
  display: flex;
  flex: 0 0 100%;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  min-width: 0;
  margin: 0;
  padding: 64px 16px 16px;
}

/* What an item holds fits in it. */
.ebx-lightbox-item > img,
.ebx-lightbox-item > video,
.ebx-lightbox-content {
  min-height: 0;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

/* A page or a player takes all the room an image could. */
.ebx-lightbox-item > iframe {
  flex: 1;
  width: 100%;
  min-height: 0;
  border: 0;
  background: #fff;
}

/* Content of the page, as a page of its own that scrolls. */
.ebx-lightbox-content {
  box-sizing: border-box;
  overflow: auto;
  padding: 24px;
  background: #fff;
  color: #000;
  overscroll-behavior: contain;
  touch-action: pan-y;
}

.ebx-lightbox-item > figcaption {
  max-width: 60em;
  padding-block-start: 8px;
  text-align: center;
}

.ebx-lightbox-error {
  margin: 0;
}

.ebx-lightbox-item[aria-busy='true']::before {
  content: '';
  position: absolute;
  inset-block-start: calc(50% - 20px);
  inset-inline-start: calc(50% - 20px);
  box-sizing: border-box;
  width: 40px;
  height: 40px;
  border: 3px solid rgb(255 255 255 / .3);
  border-block-start-color: #fff;
  border-radius: 50%;
}

/* Above a zoomed image, with its own contrast. */
.ebx-lightbox-counter {
  inset-block-start: 14px;
  inset-inline-start: 12px;
  margin: 0;
  padding: 2px 10px;
  border-radius: 14px;
  background: rgb(0 0 0 / .65);
}

.ebx-lightbox-view > .ebx-control,
.ebx-lightbox-counter,
.ebx-lightbox-bar,
.ebx-lightbox-thumbs {
  position: absolute;
  z-index: 1;
}

.ebx-lightbox-bar {
  inset-block-start: 8px;
  inset-inline-end: 8px;
  display: flex;
  gap: 8px;
}

.ebx-lightbox .ebx-control[aria-pressed='true'] {
  background: #fff;
  color: #000;
}

.ebx-lightbox .ebx-control[aria-disabled='true'] {
  opacity: .4;
  cursor: default;
}

/* The images end above the strip, as its height, so a zoomed one never goes under it. */
.ebx-with-thumbs .ebx-lightbox-track {
  inset-block-end: 88px;
}

.ebx-lightbox-thumbs {
  inset: auto 0 0;
  display: flex;
  gap: 8px;
  padding: 12px 16px;
  overflow-x: auto;
  scrollbar-width: none;
  touch-action: pan-x;
}

.ebx-lightbox-thumbs[hidden] {
  display: none;
}

/* Centred while they fit, and from the start when they do not. */
.ebx-lightbox-thumb:first-child {
  margin-inline-start: auto;
}

.ebx-lightbox-thumb:last-child {
  margin-inline-end: auto;
}

.ebx-lightbox-thumb {
  flex: none;
  color: #fff;
  box-sizing: border-box;
  width: 96px;
  height: 64px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 4px;
  overflow: hidden;
  background: none;
  opacity: .6;
  cursor: pointer;
}

.ebx-lightbox-thumb[aria-current='true'] {
  border-color: #fff;
  opacity: 1;
}

.ebx-lightbox-thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
}

.ebx-zoomable {
  cursor: zoom-in;
}

.ebx-zoomed {
  cursor: grab;
}

.ebx-lightbox-view > .ebx-prev,
.ebx-lightbox-view > .ebx-next {
  inset-block-start: 50%;
  translate: 0 -50%;
}

.ebx-lightbox-view > .ebx-prev {
  inset-inline-start: 8px;
}

.ebx-lightbox-view > .ebx-next {
  inset-inline-end: 8px;
}

/* The motion the script chose (D-028): with all of it the background fades, the wait turns and the thumbnails scroll
   smoothly, with fades only the background fades, and without any it opens at once. Only the background fades, so that
   the first image can grow from its thumbnail in full view. */
.ebx-lightbox:is(.ebx-moves, .ebx-fades)[open] {
  transition: background-color .25s;
}

@starting-style {
  .ebx-lightbox:is(.ebx-moves, .ebx-fades)[open] {
    background-color: transparent;
  }
}

.ebx-moves .ebx-lightbox-item[aria-busy='true']::before {
  animation: ebx-spin 1s linear infinite;
}

.ebx-moves .ebx-lightbox-thumbs {
  scroll-behavior: smooth;
}

@keyframes ebx-spin {
  to {
    rotate: 1turn;
  }
}
