/* Photos while they load (at the end), and when they fail.
   A photo that failed to load (js/components/image-fallback.js): instead of the browser's broken
   image icon, the seller's image description is set on primed canvas inside the artwork's mat, like
   a label pinned where the painting should hang. */

@layer components {
  .image-fallback {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-4);
    overflow: hidden;
    text-align: center;
    color: var(--bp-ink-2);
    background-color: var(--bp-paper);
    background-image: repeating-linear-gradient(135deg, var(--ph-sand-a) 0 12px, var(--ph-sand-b) 12px 24px);
  }

  /* A frame that keeps its shape without the image, but isn't positioned: the stand-in fills it. */
  .has-image-fallback { position: relative; }

  /* A container that only took its size from the image: the stand-in takes the image's place. */
  .image-fallback--inline {
    position: relative;
    inset: auto;
    width: 100%;
    min-height: 100%;
  }

  .image-fallback__icon {
    font-size: 28px;
    color: var(--bp-brand-800);
    opacity: .45;
  }

  .image-fallback__text {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 4;
    line-clamp: 4;
    overflow: hidden;
    max-width: 22ch;
    font-family: var(--font-display);
    font-size: var(--fs-md);
    font-style: italic;
    font-weight: 400;
    line-height: 1.3;
    color: var(--bp-ink);
  }

  .image-fallback__note {
    font-size: var(--fs-xs);
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--bp-ink-3);
  }

  /* Small thumbnails (basket, order lines): the mark alone. */
  .image-fallback--compact .image-fallback__text,
  .image-fallback--compact .image-fallback__note { display: none; }
}

/* A photo while it loads (js/components/image-reveal.js, ADR-0040): invisible until it has fully
   downloaded and decoded, then faded in whole, so it never paints from the top down. Meanwhile its
   frame shows a wash of wet paint sweeping across a softly breathing canvas. Only with JavaScript on
   (scripting: enabled): without it, photos show as they arrive. In the utilities layer, so it wins over
   each frame's own background. The overrides layer stills the animation for reduced motion. */
@layer utilities {
  @media (scripting: enabled) {
    img[src*="/uploads/"] {
      opacity: 0;
      transition: opacity var(--dur-slow) var(--ease);
    }

    img[src*="/uploads/"].is-shown { opacity: 1; }

    :not(dialog):has(> img[src*="/uploads/"]:not(.is-shown)) {
      background-color: var(--bp-paper-2);
      background-image: linear-gradient(105deg,
        transparent 35%,
        color-mix(in srgb, var(--bp-brand-600) 7%, transparent) 46%,
        color-mix(in srgb, var(--bp-mat) 55%, transparent) 50%,
        color-mix(in srgb, var(--bp-brand-600) 7%, transparent) 54%,
        transparent 65%);
      background-size: 250% 100%;
      background-repeat: no-repeat;
      animation: paint-wash 1.8s var(--ease) infinite, canvas-breathe 1.8s ease-in-out infinite;
    }
  }

  @keyframes paint-wash {
    from { background-position: 120% 0; }
    to { background-position: -20% 0; }
  }

  @keyframes canvas-breathe {
    50% { background-color: var(--bp-line); }
  }
}
