@property --pulse-mix {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

:root {
  --cube-size: 4.5rem;
  --iso-scale: calc(sqrt(3) / 2);
  --cube-empty-alpha: 0.4;
  --face-step: 0.033;
}

.cube {
  --cube-width: calc(var(--cube-size) * 2 * var(--iso-scale));
  --cube-height: calc(var(--cube-size) * 2);

  --cube-face-base: var(--border-color);
  --face-top: light-dark(
    var(--cube-face-base),
    oklch(from var(--cube-face-base) calc(l + var(--face-step) * 2) c h)
  );
  --face-right: light-dark(
    oklch(from var(--cube-face-base) calc(l - var(--face-step) * 2) c h),
    var(--cube-face-base)
  );
  --face-left: light-dark(
    oklch(from var(--cube-face-base) calc(l - var(--face-step)) c h),
    oklch(from var(--cube-face-base) calc(l + var(--face-step)) c h)
  );
  --face-bottom: oklch(
    from var(--cube-face-base) calc(l - var(--face-step) * 3) c h
  );

  --is-full: round(down, var(--fill), 1);
  --is-empty: round(down, calc(1 - var(--fill)), 1);

  flex-shrink: 0;
  position: relative;
  width: var(--cube-width);
  height: var(--cube-height);
  /* .cube can be an <a>; reset anchor styles that would clip the iso
     silhouette or underline the empty link. */
  overflow: visible;
  text-decoration: none;
  color: var(--color);
  user-select: none;
  pointer-events: none;

  /* Hover/active styling is gated on the anchor tag: only <a> cubes
     (confirmed blocks) react to pointer state. Plain <div> projected
     previews stay visually inert. --face-color-base is resolved lazily,
     so the orange override below feeds the face declarations too. */
  &:is(a):hover,
  &:is(a):active,
  &.selected {
    color: var(--background-color);
    --face-color-base: var(--inv-border-color);
    --face-top: var(--face-color-base);
    --face-right: oklch(
      from var(--face-color-base) calc(l - var(--face-step) * 2) c h
    );
    --face-left: oklch(
      from var(--face-color-base) calc(l - var(--face-step)) c h
    );
    --face-bottom: oklch(
      from var(--face-color-base) calc(l - var(--face-step) * 3) c h
    );
  }

  &:is(a):active,
  &.selected {
    color: var(--black);
    --face-color-base: var(--orange);
  }

  &.projected {
    animation: cube-pulse 4s ease-in-out infinite;
    --cube-face-base: color-mix(
      in oklch,
      var(--border-color),
      var(--background-color) calc(var(--pulse-mix) * 100%)
    );
  }

  /* visibility (not color:transparent) so child <img> hides too */
  &.skeleton .face-text {
    visibility: hidden;
  }

  .face {
    position: absolute;
    transform-origin: 0 0;
    box-sizing: border-box;
    width: var(--cube-size);
    height: var(--cube-size);
    transform: translateY(50%) var(--face-orient)
      translate(
        calc(var(--cube-size) * var(--face-x)),
        calc(var(--cube-size) * var(--face-y))
      )
      scale(var(--face-scale-x, 1), var(--face-scale-y));
    pointer-events: auto;
  }

  /* will-change on painted roles only so each gets its own compositor
     layer for snappy hover/select repaints. */
  .liquid,
  .glass {
    will-change: background-color;
  }
  .liquid {
    background: var(--face-color);
    opacity: calc(1 - var(--is-empty));
    --face-scale-y: calc(var(--iso-scale) * var(--fill));
    --face-stack-shift: calc(var(--iso-scale) * (1 - var(--fill)));
  }
  .glass {
    background: oklch(from var(--face-color) l c h / var(--cube-empty-alpha));
    --face-scale-y: calc(var(--iso-scale) * (1 - var(--fill)));
    --face-stack-shift: 0;
  }

  .face-text {
    --face-scale-y: var(--iso-scale);
    --face-stack-shift: 0;
    pointer-events: none;
    padding: 0.1rem;
    font-family: var(--font-mono);
    font-size: var(--font-size-xs);
    font-weight: 450;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;

    &.top {
      justify-content: center;
      text-transform: uppercase;
    }
    &.right {
      justify-content: space-between;
    }
    &.left {
      justify-content: center;
    }

    p {
      margin: 0;
    }
  }

  .top,
  .bottom {
    --face-orient: rotate(30deg) skewX(-30deg);
    --face-scale-y: var(--iso-scale);
  }
  .right,
  .rear-left {
    --face-orient: rotate(-30deg) skewX(-30deg);
  }
  .left,
  .rear-right {
    --face-orient: rotate(30deg) skewX(30deg);
  }
  .top,
  .rear-right {
    --face-y: calc(var(--face-stack-shift) - var(--iso-scale));
  }
  .left,
  .rear-left {
    --face-y: var(--face-stack-shift);
  }
  .right {
    --face-y: calc(var(--face-stack-shift) + var(--iso-scale));
  }
  .bottom {
    --face-y: 0;
  }
  .top {
    --face-color: var(--face-top);
    --face-x: 0;
  }
  .bottom {
    --face-color: var(--face-bottom);
    --face-x: 1;
  }
  .right {
    --face-color: var(--face-right);
    --face-x: 1;
  }
  .left {
    --face-color: var(--face-left);
    --face-x: 0;
  }
  .rear-right {
    --face-color: var(--face-left);
    --face-x: 1;
  }
  .rear-left {
    --face-color: var(--face-top);
    --face-x: 1;
    --face-scale-x: -1;
  }
  .liquid.top {
    --face-x: calc(1 - var(--fill));
  }
}

@keyframes cube-pulse {
  0%,
  100% {
    --pulse-mix: 0.5;
  }
  50% {
    --pulse-mix: 1;
  }
}
