I first implemented a two-phase ‘outdent’: a tiny amount on narrow screens, a larger amount on wider screens. I didn’t like how cramped the second step looked. I could have solved it by tweaking the numbers, but I theorized I could do it in a continuous manner where the images started with a minimum negative margin on narrow screens and gradually increased that based on the available space until they were all the way outside the box. The constraints were that I wanted a gutter between the content and the images in all cases, as well as a margin between the images and the edge of the viewport.

View CSS
.img {
  --max-outdent: var(--image-width);
  --available-space: calc(var(--viewport-width) - var(--ideal-width) - var(--padding));
  --content-gutter: clamp(0px, var(--available-space) / 2, var(--max-content-gutter));
  --viewport-gutter: calc(
    var(--padding) * 4 - clamp(0px, var(--available-space) / 2, var(--padding))
  );
  --outdent: calc(
    clamp(
        var(--min-outdent),
        var(--available-space) / 2 - var(--viewport-gutter),
        var(--max-outdent) + var(--content-gutter)
      ) * -1
  );
  width: var(--image-width);
  height: var(--image-width);
}

.right {
  float: right;
  margin-right: var(--outdent);
  transform-origin: center right;
  margin-left: var(--max-content-gutter);
}

:root {
  --container-colour: rgb(255, 0, 255, 0.2);
  --image-colour: rgb(255, 220, 0);
  --content-gutter-colour: rgb(255, 255, 0, 1);
  --viewport-colour: rgb(0, 255, 255, 0.1);
}

.container-bg {
  background: var(--container-colour);
}

.image-bg {
  background: var(--image-colour);
}

.content-gutter-bg {
  background: var(--content-gutter-colour);
}

.viewport-bg {
  background: var(--viewport-colour);
}

.container {
  --padding: 1rem;
  /* Constrain the width to the container. */
  width: var(--ideal-width);
  padding: var(--padding);
  border: 1px solid;
  flex: 0 0 var(--ideal-width);

  background: var(--container-colour);
}

.container > * {
  background: var(--main-bg);
}

.viewport {
  --viewport-width: min(40em, 80vw);
  display: flex;
  flex-flow: column nowrap;
  border: 1px solid red;
  border-radius: 0;
  width: var(--viewport-width);
  background: var(--viewport-colour);
}

.img {
  /* Anchor the `:before` element for demonstration purposes. */
  position: relative;
}

.img > span {
  display: block;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  border: 1px solid gray;
  z-index: 1;
  position: relative;
  background: var(--image-colour);
}

.img:before {
  display: block;
  content: "";
  background: var(--content-gutter-colour);
  position: absolute;
  top: 0;
  right: 100%;
  bottom: 0;
  left: calc(var(--max-content-gutter) * -1);
  z-index: 0;
}