/* capture-object — a REAL PRODUCT CAPTURE, mounted as a TRUE OBJECT.
   Spec: docs/ops/2026-08-02-HANDS-OBJECTS-MASTER-SPEC.md · DESIGN-BIBLE Part 6 (DEPTHFIELD)
         + Part 9.1 (real product pixels) · PHOTO-SLOT-REGISTRY.json 2026-08-01.

   WHY THIS FILE EXISTS. Product faces were shipping bare coloured rectangles, hatched
   "sample swatches", 300px generic checkmarks and empty stages where the product belongs,
   with a full-page astronomy photograph behind them answering no question. The bible's
   claim is the cure and it is not decorative: "Objects in our pages are TRUE objects. They
   cast shadow. They occlude each other... Pages are not decorated with images; they are
   built out of objects." And: "Occlusion is the biggest depth cue - bigger than shadow,
   bigger than blur, bigger than parallax. If objects never overlap, the page is flat no
   matter how many shadows it wears."

   So this engine gives a capture the four things that make it an object rather than a
   picture in a frame, and every one of them is required, not optional:
     1. THICKNESS  - a stacked hard-edge stack down-right of a fixed upper-left key light.
                     The visible edge IS the proof of objecthood; without it this is a
                     bordered rectangle, which is the founder's explicit ban.
     2. CONTACT    - a shadow pool anchored to the object's BASE, inside the object's own
                     positioned block. Anchored to the wrong block it floats, which is a
                     lighting lie (the same bug door.css records at .hf-plate-shadow).
     3. OCCLUSION  - a second REAL object overlaps it. Both must be real; an occluder
                     invented to make depth would be a forgery wearing a depth cue.
     4. HONESTY    - the capture is never colour-graded (`filter:none`). A screenshot that
                     has been re-lit stops being a receipt. It carries the chip and its
                     provenance rides on the <img> as data-capture-src / data-capture-date.

   ONE ENGINE. Faces set --cap-accent and nothing else. No per-face copy of these rules.
   §XXX: below 900px the objects STACK - occlusion is a depth cue, never a reason to put
   one piece of a product on top of another where there is no room for both. */

.cap-stage {
  position: relative;
  min-width: 0;
  --cap-accent: var(--fam-ink, #5B4FCF);
  /* ROUND 2. A vision seat called the cohortcollab object a PICTURE - "it lacks any visible
     thickness, real edge, or shadow on the surrounding surface" - and it was right: these
     values were tuned against white, and on a light lavender page ground the whole material
     sat inside the ground's own value range. Deepened until the edge and the contact pool
     read as material rather than as a hairline. */
  --cap-key: rgba(24, 22, 32, .30);
  --cap-edge-0: #CFCDDA;
  --cap-edge-1: #B4B2C4;
  --cap-edge-2: #9795AC;
  --cap-edge-3: #7C7A93;
  --cap-edge-4: #63617A;
}

/* the object's own positioned block: the contact pool's containing box, so the shadow
   lands at the OBJECT's base and not somewhere further down the page. */
.cap-obj {
  position: relative;
  min-width: 0;
  /* the camera. One perspective for the whole object so the frame and its edge quad are
     rendered by the same lens - two lenses is how a 3D object stops looking like one. */
  perspective: 1800px;
}

/* ROUND 3. Two rounds of deepening the shadow stack did not move a vision seat off PICTURE,
   and its reasoning said why: a stacked box-shadow simulates thickness, it is not thickness.
   The bible is literal about this - "a two-sided card genuinely has a back and a visible
   EDGE - thickness is proof of objecthood". So the plate is now a real box: turned a couple
   of degrees on Y so the RIGHT side face comes toward the viewer, with that face built as an
   actual quad rotated into depth rather than painted on. The turn is small because the
   product UI inside has to stay readable, which is the whole reason the object exists. */
.cap-3d {
  position: relative;
  transform-style: preserve-3d;
  transform: rotateY(-2.4deg) rotateX(0.9deg);
  transform-origin: 50% 100%;
}
/* the side face itself: one paper quad standing off the right edge, lit from the upper left
   like everything else on the page, so it is the shaded face. */
.cap-3d::after {
  content: "";
  position: absolute;
  left: 100%;
  top: 0;
  width: var(--cap-thick, 13px);
  height: 100%;
  transform-origin: left center;
  transform: rotateY(90deg);
  background: linear-gradient(180deg, var(--cap-edge-1), var(--cap-edge-4));
  border-radius: 0 3px 3px 0;
  pointer-events: none;
}

/* THE FRAME IS THE CROP, AND THE CROP IS CSS.
   A 4320x2700 master of a 1440px-wide desktop UI, shown at 480px in a hero column, renders
   its 14px type at 5px. That is not a product shot, it is a grey rectangle, and it fails the
   only question the media has to answer. So the object shows LESS of the product, LARGER -
   one legible region, framed.
   The crop lives here rather than in the file so the capture on disk stays byte-identical to
   what came off the live domain. Cropping a receipt is honest; re-encoding one to crop it
   quietly makes it a derivative.
   A face sets four numbers, from the source region it wants (sx, sy, sw, sh in master px,
   master W x H):
     --cap-ratio : sw / sh
     --cap-scale : W / sw          as a percentage
     --cap-x     : -sx / sw        as a percentage (resolves against frame WIDTH)
     --cap-y     : -sy / H * (H/W * W/sw) / (sh/sw)  -> simply  -sy / sh  as a percentage
                                                       (resolves against frame HEIGHT) */
.cap-frame {
  position: relative;
  overflow: hidden;
  border-radius: 12px;
  aspect-ratio: var(--cap-ratio, 16 / 10);
  background: var(--cap-frame-bg, #fff);
  z-index: 1;
}
.cap-frame img {
  position: absolute;
  display: block;
  left: var(--cap-x, 0);
  top: var(--cap-y, 0);
  width: var(--cap-scale, 100%);
  height: auto;
  max-width: none;
  /* a capture is a receipt: never re-lit, never graded, never tinted to match a palette. */
  filter: none;
}

/* THICKNESS, on the frame - a fixed upper-left key means the material shows on the
   lower-right, so the stack steps down and right. This edge is why the object is not a card,
   and the founder's ban is on exactly what it would be without it: a bordered rectangle. */
.cap-frame {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .70),
    0 1px 0 var(--cap-edge-0),
    0 2px 0 var(--cap-edge-1),
    1px 3px 0 var(--cap-edge-2),
    2px 5px 0 var(--cap-edge-3),
    3px 7px 0 var(--cap-edge-4),
    7px 22px 40px -12px rgba(24, 22, 32, .42);
}

/* CONTACT. Anchored to the object's base, and it TOUCHES - no gap between the object and
   its own shadow, which is the tell that separates a placed object from a floating image. */
.cap-contact {
  position: absolute;
  left: 5%;
  right: 3%;
  bottom: -14px;
  height: 44px;
  z-index: 0;
  background: radial-gradient(62% 100% at 50% 0%, var(--cap-key), transparent 72%);
  filter: blur(11px);
  pointer-events: none;
}

/* OCCLUSION. A second real object overlaps ONE CORNER. Corner, not middle: the depth cue is
   the overlap, and covering the working area to get it would trade away the only thing the
   media is there to show. It is a real product panel, never a shape drawn to make depth. */
.cap-occluder {
  position: absolute;
  left: var(--cap-occ-left, -30px);
  right: var(--cap-occ-right, auto);
  bottom: var(--cap-occ-bottom, -30px);
  width: min(248px, 50%);
  z-index: 2;
}

/* the chip. Honest provenance, 11px floor, and NEVER white-space:nowrap - §XXX bans that on
   copy that can exceed its container, so it wraps instead. It clears the occluder's overhang
   rather than sliding under it - a provenance mark you cannot read is not provenance. */
.cap-chip {
  display: inline-block;
  position: relative;
  z-index: 3;
  margin-top: 52px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--cap-accent);
  border: 1px solid color-mix(in srgb, var(--cap-accent) 34%, transparent);
  border-radius: 6px;
  padding: 3px 8px;
  overflow-wrap: anywhere;
}

/* §XXX. Below 900px the pieces stack in the flow: the occluder leaves the corner, the
   capture keeps its thickness and its contact pool, and nothing lands on top of anything. */
@media (max-width: 900px) {
  .cap-occluder {
    position: static;
    width: auto;
    margin-top: 26px;
  }
  /* §XXX: at 320 the turn costs more readable pixels than the depth cue is worth, so the
     plate faces front and keeps its edge as a thinner quad. */
  .cap-3d { transform: none; --cap-thick: 7px; }
  .cap-chip { margin-top: 14px; }

  /* A SECOND CROP FOR SMALL SCREENS, because the desktop one stops answering the question
     here. The stage collapses to ~288px at 320, and the desktop region rendered into it puts
     the product's 14px type at 6px - a recognisable shape that says nothing. So a face may
     name a tighter region for narrow viewports and the object shows even less, even larger.
     !important because the desktop numbers ride as an inline style on the stage, and inline
     outranks a stylesheet: the -sm values are read off that same inline style by var(). */
  .cap-stage {
    --cap-ratio: var(--cap-ratio-sm, var(--cap-ratio-none, 16 / 10)) !important;
    --cap-scale: var(--cap-scale-sm, 100%) !important;
    --cap-x: var(--cap-x-sm, 0%) !important;
    --cap-y: var(--cap-y-sm, 0%) !important;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cap-frame, .cap-3d { transition: none; }
}

/* NIGHT. Same object, one key light, a deeper pool - never a second palette. */
html[data-theme="dark"] .cap-stage {
  --cap-key: rgba(0, 0, 0, .58);
  --cap-edge-0: #3A3846;
  --cap-edge-1: #2F2D3A;
  --cap-edge-2: #26242F;
  --cap-edge-3: #1D1B25;
  --cap-edge-4: #141219;
}
html[data-theme="dark"] .cap-frame {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .10),
    0 1px 0 var(--cap-edge-0),
    0 2px 0 var(--cap-edge-1),
    1px 3px 0 var(--cap-edge-2),
    2px 5px 0 var(--cap-edge-3),
    3px 7px 0 var(--cap-edge-4),
    8px 26px 52px -16px rgba(0, 0, 0, .78);
}
