/* ===========================================================================
   cube.css — the PBE mark as a rotating solid.

   A real 3D cube in CSS: six faces on a `preserve-3d` parent, each carrying the
   PBE logo. It sits in the card's top-centre grid cell — a slot the corner
   anchoring deliberately left empty — so it persists across every leaf and
   turns continuously as the page scrolls.

   No library and no canvas: six divs and two rotations. js/cube.js writes
   --cube-x / --cube-y from the damped scroll position, so the cube trails the
   scroll exactly like the backdrop does.

   The logo file is truecolor PNG with NO alpha — a solid white background —
   so each face image is composited with `mix-blend-mode: multiply`. White
   multiplies to the face beneath it and disappears; the dark mark survives.
   ======================================================================== */

/* The cube no longer sits in the card's chrome. It lives inside the About leaf
   (leaf 08), where it turns as you arrive and then dissolves as the company
   copy takes over — js/cube.js writes --cube-fade from the leaf's own scroll
   progress. */
.cube{
  width: var(--cube-size);
  height: var(--cube-size);
  perspective: 900px;
  pointer-events: none;
  opacity: var(--cube-fade, 1);
  justify-self: center;
}

:root{
  /* Big enough now that it is the subject of its own section, not a trinket. */
  --cube-size: clamp(120px, 17cqw, 260px);
}
@supports not (width: 1cqw){
  :root{ --cube-size: clamp(120px, 15vw, 260px) }
}

.cube__box{
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  /* Written every frame by js/cube.js. The resting pose is a three-quarter
     view so the solid reads as a cube before a single pixel has scrolled. The
     fallbacks here ARE js/cube.js's PITCH_REST and YAW_REST — keep the three
     poses in this file in step with them, or the cube jumps on the first frame
     and JS-off looks like a different object. */
  transform:
    rotateX(calc(var(--cube-x, -12) * 1deg))
    rotateY(calc(var(--cube-y, 32) * 1deg));
  will-change: transform;
}

.cube__face{
  position: absolute;
  inset: 0;
  display: block;
  overflow: hidden;
  background-color: var(--plate-foam);
  /* Drawn edges, in ink rather than a hairline tint. A faint rule read as a soft
     white block at this size; a black edge on every face is what makes the six
     planes read as the CORNERS of a box. --on-plate so it inverts with the
     plate rather than vanishing on carbon. */
  box-shadow: inset 0 0 0 1.5px var(--on-plate);
  backface-visibility: visible;
}

/* The mark fills the face edge to edge — the logo IS the surface of the solid,
   not a sticker on it. The source is a wide lockup on white, so it is cropped
   to the square face and the white multiplies away against the foam ground. */
.cube__face img{
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  mix-blend-mode: multiply;
}

/* On carbon the card is near-black: multiply would erase the mark entirely, so
   the face inverts to screen and the logo is lightened instead. */
[data-plate="carbon"] .cube__face img,
.card[data-plate="carbon"] .cube__face img{
  mix-blend-mode: screen;
  filter: invert(1);
}

/* Six faces, each pushed out by half the edge length. */
.cube__face[data-face="front"] { transform: translateZ(calc(var(--cube-size) / 2)) }
.cube__face[data-face="back"]  { transform: rotateY(180deg) translateZ(calc(var(--cube-size) / 2)) }
.cube__face[data-face="right"] { transform: rotateY(90deg)  translateZ(calc(var(--cube-size) / 2)) }
.cube__face[data-face="left"]  { transform: rotateY(-90deg) translateZ(calc(var(--cube-size) / 2)) }
.cube__face[data-face="top"]   { transform: rotateX(90deg)  translateZ(calc(var(--cube-size) / 2)) }
.cube__face[data-face="bottom"]{ transform: rotateX(-90deg) translateZ(calc(var(--cube-size) / 2)) }

/* Below 560px the top row already carries the wordmark, the Menu pill and the
   rotated mark. The cube is the one thing that can go. */
@media (max-width: 560px){
  .cube{ display: none }
}

/* No JS: the cube never turns, so it rests in its three-quarter pose. */
html:not(.js) .cube__box{ transform: rotateX(-12deg) rotateY(32deg) }

/* Reduced motion: a static solid, not a spinning one. js/cube.js also stops
   writing the custom properties, so this is the whole behaviour. */
@media (prefers-reduced-motion: reduce){
  .cube__box{
    transform: rotateX(-12deg) rotateY(32deg);
    will-change: auto;
  }
}

/* ===========================================================================
   The real solid (js/solid.js).

   Everything above this line is the FALLBACK now. js/solid.js builds a three.js
   canvas inside .cube and adds .cube--gl only once that canvas has a live
   WebGL context and a frame scheduled, so the six CSS faces are hidden solely
   on the success path. No WebGL, no JS, an old browser, or any throw in
   solid.js and none of this applies — the CSS cube above is simply what you
   get, still driven by js/cube.js, which does not know either version exists.

   .cube keeps its own box either way: the canvas is absolutely positioned
   inside it, so --cube-size still owns the grid cell and swapping renderers
   cannot move anything else on the leaf by a pixel.
   ======================================================================== */
.cube{ position: relative }

.cube__gl{
  position: absolute;
  /* NEGATIVE, and it has to be. The CSS cube's front face is exactly
     --cube-size, so at rotateY(32) its silhouette is 1.38x that and it spills
     out of the cell — nothing clips a div. A canvas DOES clip, so it is grown
     past the cell instead and js/solid.js pulls the camera back to suit, which
     lands one cube edge at exactly --cube-size. Same face size as the CSS
     version, same overflow, no jump when the renderer swaps in.

     -30% HERE MUST EQUAL OVERSCAN = 0.30 in js/solid.js. Two halves of one
     number: change this alone and the solid quietly renders at the wrong size.
     Absolutely positioned, so growing it moves nothing else on the leaf. */
  inset: -30%;
  display: block;
  width: 160%;   /* 1 + 2 x 30% */
  height: 160%;
  /* css/base.css:84 puts max-width:100% on every img, svg, canvas and video.
     That is right for media in the flow and wrong for this one: it clamped the
     canvas back to 100% while the height stayed at 160%, which over-constrained
     left/right/width so `right` was dropped, the canvas hung off to the LEFT of
     the cell, and the solid was clipped down both sides. Opt out explicitly. */
  max-width: none;
  /* three.js owns the drawing buffer; CSS owns the layout box. Stage.resize()
     calls setSize(w, h, false) precisely so these two never fight. */
  pointer-events: none;
}

/* Hidden, not removed: js/cube.js goes on writing --cube-x / --cube-y to
   .cube__box every frame, and js/solid.js reads them back off it. Removing the
   box, or display:none-ing it in a way that stopped those writes, would cut the
   wire the solid is driven by. visibility:hidden keeps the element and its
   inline styles alive while painting nothing. */
.cube--gl .cube__box{
  visibility: hidden;
  /* Nothing is painting it any more, so release the compositing layer the
     visible version asks for. */
  will-change: auto;
}
