/*
 * @minify none
 *
 * That marker is load-bearing, not a note. mycreon.tapestry.services.CssCompressor is YUI's, and it
 * corrupted this file three ways at once: hsl(0 0% ...) lost its percent sign, linear-gradient(0deg
 * ...) lost its unit, and one calc(var(--lqip-stop10)) came back as calc(var(- - lqip - stop10)).
 * Each on its own makes background-image invalid at computed-value time, so nothing painted. The
 * damage was positional -- one of six identical declarations, the last of two dozen identical ones
 * -- so there is no way to write this defensively.
 *
 * ---------------------------------------------------------------------------------------------
 *
 * CSS-only low quality image placeholders.
 *
 * One 20-bit integer in an inline custom property, unpacked and painted here. No JavaScript, no
 * wrapper element, no data URL, no second request — the placeholder appears during the first style
 * pass because there is nothing to fetch or decode.
 *
 * Emitted by mycreon.mediapool.frontend.components.Media, its DivMedia subclass and the backend
 * media browser. All of them write the .lqip class beside the property, so the two always travel
 * together and the class is not a condition anyone evaluates. It is the handle
 * async-renderer.coffee removes once the image has loaded, which stops the browser compositing
 * thirteen layers nobody can see any more.
 *
 * The [loading=lazy] and [preload=none] arms are the reference technique's, kept for anything
 * outside mycreon that renders a lazy tag directly. Nothing in here relies on them, because Media
 * writes 'loading' only for async="true" and a background image is never lazy in the browser's
 * sense at all.
 *
 * THE ATTRIBUTE SELECTOR IS REPEATED IN EVERY RULE BELOW, AND THAT IS NOT A TYPO. It buys a
 * specificity step that :where() cannot: everything inside :where() counts as zero, so the gate
 * contributes nothing and a single [style*="--lqip:"] leaves the rule at one class. Tabler's
 * .avatar sets the background shorthand, which resets background-image to none, and at equal
 * specificity the winner is whichever stylesheet the page emitted last -- not something a component
 * can decide. Verified in a browser: with one attribute selector .avatar wins and nothing paints.
 *
 * ---------------------------------------------------------------------------------------------
 *
 * The technique and this renderer are Lean Rada's, published at
 * https://leanrada.com/notes/css-only-lqip/ (30 March 2025). Source of record is
 * github.com/Kalabasa/leanrada.com, branch src, main/site/common.css — the shipped stylesheet,
 * which differs from the simplified version printed in the article. Code samples and algorithms
 * from that site are MIT licensed.
 *
 * The gradient list below is copied VERBATIM, so what ships here is exactly what was verified
 * upstream. Do not reformat it and do not hand-tune a stop: the ~120 stops approximate a quadratic
 * opacity falloff that CSS cannot express directly, and that curve is what blends the six ellipses
 * into each other. With a linear falloff the six circles and their centres are plainly visible. It
 * is the "blur" in this BlurHash alternative and it is not optional.
 *
 * One thing is not verbatim: the list lands in --lqip-image and the blend modes in
 * --lqip-blend-modes, and background-image reads them back. Every layer and every stop is
 * unchanged; the indirection is what lets an element whose real image is a background compose that
 * image over the placeholder rather than replacing it. See the second rule.
 *
 * The divisors have to match mycreon.mediapool.api.Lqip: 524288 is Lqip.OFFSET, the six cell
 * divisors are Lqip.cellDivisor(0..5), 64 is Lqip.L_DIVISOR and 8 is Lqip.A_DIVISOR. CSS cannot
 * import a constant, so this is the third place they appear and the only one nothing checks.
 *
 * Literals rather than pow(2, 19), which is what keeps pow() out of the required feature set for
 * nothing in return.
 *
 * A browser that cannot render this paints no background at all and shows what it shows today. The
 * layers reach background-image through custom properties, so an unsupported function is caught at
 * computed-value time and takes the whole declaration with it. There is no partial-support path
 * that produces wrong colours: if oklab() alone fails, the base layer is invalid and everything
 * else goes too.
 *
 * docs/specs/lqip-placeholders/ has the fixture this was looked at with.
 */

/* Low quality image placeholders in pure CSS in a single element.
--lqip: (20-bit number) - 2^19
:98765432109876543210:
:aaBBccDDeeFFllAAAbbb:
- 6 2-bit grayscale area values dividing the area in 3x2
- 2 bits L channel of base Oklab color for full area
- 3 bits a channel of base Oklab color for full area
- 3 bits b channel of base Oklab color for full area */
/* prettier-ignore */
[style*="--lqip:"][style*="--lqip:"]:where([loading=lazy],[preload=none],.lqip) {
  --lqip-ca: mod(round(down, calc((var(--lqip) + 524288) / 262144)), 4);
  --lqip-cb: mod(round(down, calc((var(--lqip) + 524288) / 65536)), 4);
  --lqip-cc: mod(round(down, calc((var(--lqip) + 524288) / 16384)), 4);
  --lqip-cd: mod(round(down, calc((var(--lqip) + 524288) / 4096)), 4);
  --lqip-ce: mod(round(down, calc((var(--lqip) + 524288) / 1024)), 4);
  --lqip-cf: mod(round(down, calc((var(--lqip) + 524288) / 256)), 4);
  --lqip-ll: mod(round(down, calc((var(--lqip) + 524288) / 64)), 4);
  --lqip-aaa: mod(round(down, calc((var(--lqip) + 524288) / 8)), 8);
  --lqip-bbb: mod(calc(var(--lqip) + 524288), 8);

  --lqip-ca-clr: hsl(0 0% calc(var(--lqip-ca) / 3 * 100%));
  --lqip-cb-clr: hsl(0 0% calc(var(--lqip-cb) / 3 * 100%));
  --lqip-cc-clr: hsl(0 0% calc(var(--lqip-cc) / 3 * 100%));
  --lqip-cd-clr: hsl(0 0% calc(var(--lqip-cd) / 3 * 100%));
  --lqip-ce-clr: hsl(0 0% calc(var(--lqip-ce) / 3 * 100%));
  --lqip-cf-clr: hsl(0 0% calc(var(--lqip-cf) / 3 * 100%));
  --lqip-base-clr: oklab(
    calc(var(--lqip-ll) / 3 * 0.6 + 0.2)
      calc(var(--lqip-aaa) / 8 * 0.7 - 0.35)
      calc((var(--lqip-bbb) + 1) / 8 * 0.7 - 0.35)
  );

  --lqip-stop10: 2%;
  --lqip-stop20: 8%;
  --lqip-stop30: 18%;
  --lqip-stop40: 32%;
  --lqip-blend-modes:
    hard-light, hard-light, hard-light, hard-light, hard-light, hard-light,
    overlay, overlay, overlay, overlay, overlay, overlay,
    normal;
  --lqip-image: radial-gradient(
      50% 75% at 16.67% 25%,
      rgb(from var(--lqip-ca-clr) r g b / 50%),
      rgb(from var(--lqip-ca-clr) r g b / calc(50% - var(--lqip-stop10) / 2)) 10%,
      rgb(from var(--lqip-ca-clr) r g b / calc(50% - var(--lqip-stop20) / 2)) 20%,
      rgb(from var(--lqip-ca-clr) r g b / calc(50% - var(--lqip-stop30) / 2)) 30%,
      rgb(from var(--lqip-ca-clr) r g b / calc(50% - var(--lqip-stop40) / 2)) 40%,
      rgb(from var(--lqip-ca-clr) r g b / calc(var(--lqip-stop40) / 2)) 60%,
      rgb(from var(--lqip-ca-clr) r g b / calc(var(--lqip-stop30) / 2)) 70%,
      rgb(from var(--lqip-ca-clr) r g b / calc(var(--lqip-stop20) / 2)) 80%,
      rgb(from var(--lqip-ca-clr) r g b / calc(var(--lqip-stop10) / 2)) 90%,
      transparent
    ),
    radial-gradient(
      50% 75% at 83.33% 25%,
      rgb(from var(--lqip-cc-clr) r g b / 50%),
      rgb(from var(--lqip-cc-clr) r g b / calc(50% - var(--lqip-stop10) / 2)) 10%,
      rgb(from var(--lqip-cc-clr) r g b / calc(50% - var(--lqip-stop20) / 2)) 20%,
      rgb(from var(--lqip-cc-clr) r g b / calc(50% - var(--lqip-stop30) / 2)) 30%,
      rgb(from var(--lqip-cc-clr) r g b / calc(50% - var(--lqip-stop40) / 2)) 40%,
      rgb(from var(--lqip-cc-clr) r g b / calc(var(--lqip-stop40) / 2)) 60%,
      rgb(from var(--lqip-cc-clr) r g b / calc(var(--lqip-stop30) / 2)) 70%,
      rgb(from var(--lqip-cc-clr) r g b / calc(var(--lqip-stop20) / 2)) 80%,
      rgb(from var(--lqip-cc-clr) r g b / calc(var(--lqip-stop10) / 2)) 90%,
      transparent
    ),
    radial-gradient(
      50% 75% at 50% 25%,
      rgb(from var(--lqip-cb-clr) r g b / 50%),
      rgb(from var(--lqip-cb-clr) r g b / calc(50% - var(--lqip-stop10) / 2)) 10%,
      rgb(from var(--lqip-cb-clr) r g b / calc(50% - var(--lqip-stop20) / 2)) 20%,
      rgb(from var(--lqip-cb-clr) r g b / calc(50% - var(--lqip-stop30) / 2)) 30%,
      rgb(from var(--lqip-cb-clr) r g b / calc(50% - var(--lqip-stop40) / 2)) 40%,
      rgb(from var(--lqip-cb-clr) r g b / calc(var(--lqip-stop40) / 2)) 60%,
      rgb(from var(--lqip-cb-clr) r g b / calc(var(--lqip-stop30) / 2)) 70%,
      rgb(from var(--lqip-cb-clr) r g b / calc(var(--lqip-stop20) / 2)) 80%,
      rgb(from var(--lqip-cb-clr) r g b / calc(var(--lqip-stop10) / 2)) 90%,
      transparent
    ),
    radial-gradient(
      50% 75% at 16.67% 75%,
      rgb(from var(--lqip-cd-clr) r g b / 50%),
      rgb(from var(--lqip-cd-clr) r g b / calc(50% - var(--lqip-stop10) / 2)) 10%,
      rgb(from var(--lqip-cd-clr) r g b / calc(50% - var(--lqip-stop20) / 2)) 20%,
      rgb(from var(--lqip-cd-clr) r g b / calc(50% - var(--lqip-stop30) / 2)) 30%,
      rgb(from var(--lqip-cd-clr) r g b / calc(50% - var(--lqip-stop40) / 2)) 40%,
      rgb(from var(--lqip-cd-clr) r g b / calc(var(--lqip-stop40) / 2)) 60%,
      rgb(from var(--lqip-cd-clr) r g b / calc(var(--lqip-stop30) / 2)) 70%,
      rgb(from var(--lqip-cd-clr) r g b / calc(var(--lqip-stop20) / 2)) 80%,
      rgb(from var(--lqip-cd-clr) r g b / calc(var(--lqip-stop10) / 2)) 90%,
      transparent
    ),
    radial-gradient(
      50% 75% at 83.33% 75%,
      rgb(from var(--lqip-cf-clr) r g b / 50%),
      rgb(from var(--lqip-cf-clr) r g b / calc(50% - var(--lqip-stop10) / 2)) 10%,
      rgb(from var(--lqip-cf-clr) r g b / calc(50% - var(--lqip-stop20) / 2)) 20%,
      rgb(from var(--lqip-cf-clr) r g b / calc(50% - var(--lqip-stop30) / 2)) 30%,
      rgb(from var(--lqip-cf-clr) r g b / calc(50% - var(--lqip-stop40) / 2)) 40%,
      rgb(from var(--lqip-cf-clr) r g b / calc(var(--lqip-stop40) / 2)) 60%,
      rgb(from var(--lqip-cf-clr) r g b / calc(var(--lqip-stop30) / 2)) 70%,
      rgb(from var(--lqip-cf-clr) r g b / calc(var(--lqip-stop20) / 2)) 80%,
      rgb(from var(--lqip-cf-clr) r g b / calc(var(--lqip-stop10) / 2)) 90%,
      transparent
    ),
    radial-gradient(
      50% 75% at 50% 75%,
      rgb(from var(--lqip-ce-clr) r g b / 50%),
      rgb(from var(--lqip-ce-clr) r g b / calc(50% - var(--lqip-stop10) / 2)) 10%,
      rgb(from var(--lqip-ce-clr) r g b / calc(50% - var(--lqip-stop20) / 2)) 20%,
      rgb(from var(--lqip-ce-clr) r g b / calc(50% - var(--lqip-stop30) / 2)) 30%,
      rgb(from var(--lqip-ce-clr) r g b / calc(50% - var(--lqip-stop40) / 2)) 40%,
      rgb(from var(--lqip-ce-clr) r g b / calc(var(--lqip-stop40) / 2)) 60%,
      rgb(from var(--lqip-ce-clr) r g b / calc(var(--lqip-stop30) / 2)) 70%,
      rgb(from var(--lqip-ce-clr) r g b / calc(var(--lqip-stop20) / 2)) 80%,
      rgb(from var(--lqip-ce-clr) r g b / calc(var(--lqip-stop10) / 2)) 90%,
      transparent
    ),
    radial-gradient(
      50% 75% at 16.67% 25%,
      var(--lqip-ca-clr),
      rgb(from var(--lqip-ca-clr) r g b / calc(100% - var(--lqip-stop10))) 10%,
      rgb(from var(--lqip-ca-clr) r g b / calc(100% - var(--lqip-stop20))) 20%,
      rgb(from var(--lqip-ca-clr) r g b / calc(100% - var(--lqip-stop30))) 30%,
      rgb(from var(--lqip-ca-clr) r g b / calc(100% - var(--lqip-stop40))) 40%,
      rgb(from var(--lqip-ca-clr) r g b / calc(var(--lqip-stop40))) 60%,
      rgb(from var(--lqip-ca-clr) r g b / calc(var(--lqip-stop30))) 70%,
      rgb(from var(--lqip-ca-clr) r g b / calc(var(--lqip-stop20))) 80%,
      rgb(from var(--lqip-ca-clr) r g b / calc(var(--lqip-stop10))) 90%,
      transparent
    ),
    radial-gradient(
      50% 75% at 50% 25%,
      var(--lqip-cb-clr),
      rgb(from var(--lqip-cb-clr) r g b / calc(100% - var(--lqip-stop10))) 10%,
      rgb(from var(--lqip-cb-clr) r g b / calc(100% - var(--lqip-stop20))) 20%,
      rgb(from var(--lqip-cb-clr) r g b / calc(100% - var(--lqip-stop30))) 30%,
      rgb(from var(--lqip-cb-clr) r g b / calc(100% - var(--lqip-stop40))) 40%,
      rgb(from var(--lqip-cb-clr) r g b / calc(var(--lqip-stop40))) 60%,
      rgb(from var(--lqip-cb-clr) r g b / calc(var(--lqip-stop30))) 70%,
      rgb(from var(--lqip-cb-clr) r g b / calc(var(--lqip-stop20))) 80%,
      rgb(from var(--lqip-cb-clr) r g b / calc(var(--lqip-stop10))) 90%,
      transparent
    ),
    radial-gradient(
      50% 75% at 83.33% 25%,
      var(--lqip-cc-clr),
      rgb(from var(--lqip-cc-clr) r g b / calc(100% - var(--lqip-stop10))) 10%,
      rgb(from var(--lqip-cc-clr) r g b / calc(100% - var(--lqip-stop20))) 20%,
      rgb(from var(--lqip-cc-clr) r g b / calc(100% - var(--lqip-stop30))) 30%,
      rgb(from var(--lqip-cc-clr) r g b / calc(100% - var(--lqip-stop40))) 40%,
      rgb(from var(--lqip-cc-clr) r g b / calc(var(--lqip-stop40))) 60%,
      rgb(from var(--lqip-cc-clr) r g b / calc(var(--lqip-stop30))) 70%,
      rgb(from var(--lqip-cc-clr) r g b / calc(var(--lqip-stop20))) 80%,
      rgb(from var(--lqip-cc-clr) r g b / calc(var(--lqip-stop10))) 90%,
      transparent
    ),
    radial-gradient(
      50% 75% at 16.67% 75%,
      var(--lqip-cd-clr),
      rgb(from var(--lqip-cd-clr) r g b / calc(100% - var(--lqip-stop10))) 10%,
      rgb(from var(--lqip-cd-clr) r g b / calc(100% - var(--lqip-stop20))) 20%,
      rgb(from var(--lqip-cd-clr) r g b / calc(100% - var(--lqip-stop30))) 30%,
      rgb(from var(--lqip-cd-clr) r g b / calc(100% - var(--lqip-stop40))) 40%,
      rgb(from var(--lqip-cd-clr) r g b / calc(var(--lqip-stop40))) 60%,
      rgb(from var(--lqip-cd-clr) r g b / calc(var(--lqip-stop30))) 70%,
      rgb(from var(--lqip-cd-clr) r g b / calc(var(--lqip-stop20))) 80%,
      rgb(from var(--lqip-cd-clr) r g b / calc(var(--lqip-stop10))) 90%,
      transparent
    ),
    radial-gradient(
      50% 75% at 50% 75%,
      var(--lqip-ce-clr),
      rgb(from var(--lqip-ce-clr) r g b / calc(100% - var(--lqip-stop10))) 10%,
      rgb(from var(--lqip-ce-clr) r g b / calc(100% - var(--lqip-stop20))) 20%,
      rgb(from var(--lqip-ce-clr) r g b / calc(100% - var(--lqip-stop30))) 30%,
      rgb(from var(--lqip-ce-clr) r g b / calc(100% - var(--lqip-stop40))) 40%,
      rgb(from var(--lqip-ce-clr) r g b / calc(var(--lqip-stop40))) 60%,
      rgb(from var(--lqip-ce-clr) r g b / calc(var(--lqip-stop30))) 70%,
      rgb(from var(--lqip-ce-clr) r g b / calc(var(--lqip-stop20))) 80%,
      rgb(from var(--lqip-ce-clr) r g b / calc(var(--lqip-stop10))) 90%,
      transparent
    ),
    radial-gradient(
      50% 75% at 83.33% 75%,
      var(--lqip-cf-clr),
      rgb(from var(--lqip-cf-clr) r g b / calc(100% - var(--lqip-stop10))) 10%,
      rgb(from var(--lqip-cf-clr) r g b / calc(100% - var(--lqip-stop20))) 20%,
      rgb(from var(--lqip-cf-clr) r g b / calc(100% - var(--lqip-stop30))) 30%,
      rgb(from var(--lqip-cf-clr) r g b / calc(100% - var(--lqip-stop40))) 40%,
      rgb(from var(--lqip-cf-clr) r g b / calc(var(--lqip-stop40))) 60%,
      rgb(from var(--lqip-cf-clr) r g b / calc(var(--lqip-stop30))) 70%,
      rgb(from var(--lqip-cf-clr) r g b / calc(var(--lqip-stop20))) 80%,
      rgb(from var(--lqip-cf-clr) r g b / calc(var(--lqip-stop10))) 90%,
      transparent
    ),
    linear-gradient(0deg, var(--lqip-base-clr), var(--lqip-base-clr));

  background-blend-mode: var(--lqip-blend-modes);
  background-image: var(--lqip-image);
}

/*
 * Surfaces whose real image is a background rather than an <img>. They hand the URL over in
 * --media-image (see mycreon.mediapool.util.LqipMarkup) instead of writing background-image
 * themselves, because an inline background-image beats this stylesheet's and would hide the
 * placeholder entirely.
 *
 * The plain rule comes first and always applies, so the image shows whatever the browser can do.
 * The composed rule adds the placeholder underneath it. 'normal' leads the blend list because a
 * background-blend-mode list shorter than the layer list repeats, which would otherwise blend the
 * real image with hard-light.
 *
 * THE @supports GUARD IS LOAD-BEARING. background-image inside it carries the real image, and a
 * declaration whose var() substitution does not parse is invalid at computed-value time -- so a
 * browser missing one of these functions would lose the image, not just the placeholder. The four
 * tests are every feature the rule above needs beyond plain gradients: relative colour syntax,
 * oklab(), mod() and round(). Safari below 16.4 on a staff iPad fails them and is ordinary.
 *
 * The repeated attribute selector is not a typo. It buys a specificity step, so that a theme's own
 * class beneath these elements cannot take the image away: Tabler's .avatar, for one, sets the
 * background shorthand, which resets background-image to none. At equal specificity the winner
 * would be whichever stylesheet the page happened to emit last, and that is not something a
 * component can decide.
 */
[style*="--media-image:"][style*="--media-image:"] {
  background-image: var(--media-image);
}

@supports (background-image: radial-gradient(50% 75% at 50% 25%, rgb(from red r g b / 50%), transparent)) and
          (color: oklab(0.5 0 0)) and
          (width: mod(3px, 2px)) and
          (width: round(down, 3px, 2px)) {
  [style*="--lqip:"][style*="--lqip:"]:where([loading=lazy],[preload=none],.lqip)[style*="--media-image:"][style*="--media-image:"] {
    background-image: var(--media-image), var(--lqip-image);
    background-blend-mode: normal, var(--lqip-blend-modes);
  }
}

/*
 * The medium's intrinsic ratio, handed over in --media-aspect-ratio (see
 * mycreon.mediapool.util.LqipMarkup) rather than written as an inline aspect-ratio.
 *
 * It reserves the box before the image arrives, which is what the placeholder needs to paint on and
 * what stops the page reflowing when the image lands. An element with no size and no ratio collapses
 * to zero height and neither happens.
 *
 * A ratio rather than width and height attributes, because it is the one number that survives
 * scaling: vipsthumbnail fits within the requested box and never crops, so the source ratio is the
 * ratio of every derivative. A call site that asks for 500x300 without knowing the source therefore
 * does not have to work out what it will get back. It also applies to a <div> whose image is a
 * background, which has no size attributes to write.
 *
 * :where() drops the specificity to zero, so this loses to any rule a design writes -- a card that
 * wants to crop to 16:9 just says aspect-ratio and needs no !important. That is what makes it safe
 * to emit unconditionally.
 */
:where([style*="--media-aspect-ratio:"]) {
  aspect-ratio: var(--media-aspect-ratio);
}
