/* box3d.css: TAPE. The 3D cassette in the hand, its reels and sheen, the slot hand-off, and cosmetic light on
   the turning box. Classes bx-*, vars --bx-*. Reaches only into .free, .case.box3d descendants (cosmetic),
   and .slot-tape.half/.in children, as the plan allows. */

/* ================================================================ the cassette you carry */
/* the carried node must keep its children in 3D (a filter would flatten the cuboid), so the drop shadow
   moves onto our own .bx-shade layer */
.free:has(> .bx-cass) { filter: none; transform-style: preserve-3d; }

.bx-cass {
  --bx-d: calc(var(--tw, 240px) * .1337);           /* 25mm deep for a 187mm wide shell */
  --bx-h: calc(var(--tw, 240px) * 4.1 / 7.4);
  position: relative; width: var(--tw, 240px); height: var(--bx-h); transform-style: preserve-3d;
}
.bx-cass .bx-face { position: absolute; backface-visibility: hidden; -webkit-backface-visibility: hidden; overflow: hidden; }
.bx-cass .bx-front { left: 0; top: 0; width: var(--tw); height: var(--bx-h); border-radius: calc(var(--tw) * .014);
  transform: translateZ(calc(var(--bx-d) / 2)); }
.bx-cass .bx-front > .tape { position: absolute; left: 0; top: 0; }
.bx-cass .bx-back { left: 0; top: 0; width: var(--tw); height: var(--bx-h); border-radius: calc(var(--tw) * .014);
  transform: rotateY(180deg) translateZ(calc(var(--bx-d) / 2));
  background:
    radial-gradient(120% 90% at 30% 10%, rgba(255,255,255,.06), transparent 60%),
    repeating-linear-gradient(90deg, transparent 0 3px, rgba(255,255,255,.012) 3px 4px),
    linear-gradient(180deg, #1d1d20, #0e0e10);
  box-shadow: inset 0 0 0 1px #2c2c30, inset 0 -6px 10px rgba(0,0,0,.5); }
.bx-cass .bx-top, .bx-cass .bx-bot { left: 0; width: var(--tw); height: var(--bx-d); top: calc((var(--bx-h) - var(--bx-d)) / 2); }
.bx-cass .bx-top { transform: rotateX(90deg) translateZ(calc(var(--bx-h) / 2));
  background: repeating-linear-gradient(90deg, #0a0a0b 0 2px, #1f1f22 2px 5px), #141416;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.14), inset 0 -2px 3px rgba(0,0,0,.7); }
.bx-cass .bx-bot { transform: rotateX(-90deg) translateZ(calc(var(--bx-h) / 2));
  background: linear-gradient(180deg, #18181a, #0c0c0d); box-shadow: inset 0 1px 0 rgba(255,255,255,.12), inset 0 -1px 0 rgba(0,0,0,.8); }
.bx-cass .bx-bot > .slot-tape-face { inset: 9% 1.5%; }
.bx-cass .bx-l, .bx-cass .bx-r { top: 0; height: var(--bx-h); width: var(--bx-d); left: calc((var(--tw) - var(--bx-d)) / 2);
  background: linear-gradient(90deg, #0b0b0c, #202023 45%, #0f0f11); box-shadow: inset 0 0 0 1px rgba(0,0,0,.6); }
.bx-cass .bx-l { transform: rotateY(-90deg) translateZ(calc(var(--tw) / 2)); }
.bx-cass .bx-r { transform: rotateY(90deg) translateZ(calc(var(--tw) / 2)); }
.bx-cass .bx-notch { position: absolute; left: 22%; right: 22%; top: 18%; height: 14%; border-radius: 2px; background: #050506;
  box-shadow: inset 0 1px 2px rgba(0,0,0,.9), 0 1px 0 rgba(255,255,255,.07); }
/* the dust door on the leading edge, with its pivot ridge */
.bx-cass .bx-door { position: absolute; left: 1.5%; right: 1.5%; top: 12%; bottom: 12%; border-radius: 2px;
  background: linear-gradient(180deg, #222226, #101012); box-shadow: inset 0 1px 0 rgba(255,255,255,.12), 0 0 0 1px #050506; }
.bx-cass .bx-scr { position: absolute; width: 4.2%; aspect-ratio: 1; border-radius: 50%;
  background: radial-gradient(circle, #9a9aa0 0 18%, #3c3c42 22% 55%, #050506 60%); box-shadow: inset 0 1px 1px rgba(0,0,0,.8); }
.bx-cass .bx-scr.a { left: 4%; top: 8%; } .bx-cass .bx-scr.b { right: 4%; top: 8%; } .bx-cass .bx-scr.c { left: 4%; bottom: 8%; }
.bx-cass .bx-scr.d { right: 4%; bottom: 8%; } .bx-cass .bx-scr.e { left: 48%; top: 46%; }
.bx-cass .bx-win { position: absolute; left: 30%; right: 30%; top: 58%; height: 18%; border-radius: 4px;
  background: linear-gradient(180deg, rgba(60,50,44,.8), rgba(10,8,7,.95)); box-shadow: inset 0 0 0 1px #000, inset 0 3px 6px rgba(0,0,0,.8); }
.bx-cass .bx-emb { position: absolute; left: 0; right: 0; top: 30%; text-align: center; font: 700 calc(var(--tw) * .028) 'Archivo Narrow', Archivo, sans-serif;
  letter-spacing: .3em; color: #2a2a2e; text-shadow: 0 1px 0 rgba(255,255,255,.05); font-style: normal; }

/* contact shadow: a soft dark card well behind the shell, fading as it tips down over the slot */
.bx-cass .bx-shade { position: absolute; left: 6%; right: 6%; top: 10%; bottom: 0; border-radius: 12%;
  transform: translateZ(calc(var(--bx-d) * -2.6)) translateY(8%); background: rgba(0,0,0,.34);
  box-shadow: 0 18px 34px 14px rgba(0,0,0,.42); opacity: calc(1 - var(--bx-near, 0) * 1.4); pointer-events: none; will-change: opacity; }

/* reels: spokes turn with --bx-reel (left reel slower when it's carrying the tape), the pack shimmers,
   and they shiver on hard moves */
.bx-cass .reel { transform: translate(var(--bx-wob, 0px), calc(var(--bx-wob, 0px) * -.5)); will-change: transform; }
.bx-cass .reel::after { transform: rotate(calc(var(--bx-reel, 0deg) * var(--bx-rl, 1))); will-change: transform; }
.bx-cass .reel.r::after { transform: rotate(calc(var(--bx-reel, 0deg) * var(--bx-rr, 1))); }
.bx-cass .reel::before { content: ''; position: absolute; inset: 0; border-radius: 50%; pointer-events: none;
  background: conic-gradient(from 0deg, rgba(255,226,190,.0) 0 20deg, rgba(255,226,190,.16) 40deg, rgba(255,226,190,0) 70deg 200deg, rgba(255,226,190,.1) 225deg, rgba(255,226,190,0) 250deg);
  -webkit-mask: radial-gradient(circle, transparent 0 16.5%, #000 17% var(--r, 30%), transparent calc(var(--r, 30%) + .6%));
  mask: radial-gradient(circle, transparent 0 16.5%, #000 17% var(--r, 30%), transparent calc(var(--r, 30%) + .6%));
  transform: rotate(calc(var(--bx-reel, 0deg) * .7)); will-change: transform; }
.bx-cass.bx-fast .reel::after { background: conic-gradient(rgba(214,210,202,.7), rgba(140,136,130,.62), rgba(214,210,202,.7), rgba(140,136,130,.62), rgba(214,210,202,.7)); }
.bx-cass.bx-fast .reel::before { opacity: .55; }

/* a specular band that slides across the window glass as you tilt it */
.bx-cass .bx-sheen { position: absolute; inset: 0; overflow: hidden; pointer-events: none; mix-blend-mode: screen; border-radius: inherit; }
.bx-cass .bx-sheen > i { position: absolute; top: -20%; bottom: -20%; left: -60%; width: 46%;
  background: linear-gradient(100deg, transparent, rgba(255,240,220,.05) 30%, rgba(255,250,240,.32) 48%, rgba(255,255,255,.5) 50%, rgba(255,240,220,.12) 58%, transparent);
  transform: translateX(calc(var(--bx-sx, 50%) * 3.2)) skewX(-8deg); will-change: transform; }
.bx-cass .bx-front::after { /* light from the room: lit side warmer, far side falling off */
  content: ''; position: absolute; inset: 0; pointer-events: none; border-radius: inherit;
  background: linear-gradient(90deg, rgba(255,190,120,.10), transparent 40%, rgba(0,0,0,.32));
  opacity: calc(1 - var(--bx-lit, .5)); will-change: opacity; }
.bx-cass .bx-l::after, .bx-cass .bx-r::after { content: ''; position: absolute; inset: 0; background: #ffd6a0; mix-blend-mode: soft-light; }
.bx-cass .bx-l::after { opacity: calc(.15 + var(--bx-lit, .5) * .6); }
.bx-cass .bx-r::after { opacity: calc(.75 - var(--bx-lit, .5) * .6); }

/* ================================================================ the slot hand-off */
/* the cassette's dark top edge rides above the label as it goes in, then a glint off the window as it vanishes */
.slot-tape.half > .slot-tape-face::before, .slot-tape.in > .slot-tape-face::before {
  content: ''; position: absolute; left: .6%; right: .6%; top: -16%; height: 18%; border-radius: .4cqw .4cqw 0 0; pointer-events: none;
  background: repeating-linear-gradient(90deg, #070708 0 .5cqw, #1b1b1e .5cqw 1.1cqw); box-shadow: inset 0 .15cqw 0 rgba(255,255,255,.14); }
.slot-tape.half > .slot-tape-face { animation: bx-seat 90ms 170ms steps(2, end) 1; }
.slot-tape.in > .slot-tape-face::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; mix-blend-mode: screen;
  background: linear-gradient(100deg, transparent 30%, rgba(255,250,235,.55) 48%, transparent 62%);
  background-size: 260% 100%; animation: bx-glint .42s cubic-bezier(.3,0,.4,1) both; }
@keyframes bx-seat { 0% { translate: 0 2px; } 50% { translate: 0 -1px; } 100% { translate: 0 0; } }
@keyframes bx-glint { from { background-position: 120% 0; } to { background-position: -40% 0; } }

/* ================================================================ the box you're holding: spin feel */
/* a glancing glare band that crosses with the turn, and faces that fall off as they turn from the light */
.case.box3d .bx-glare { position: absolute; top: -10%; bottom: -10%; left: -70%; width: 80%; pointer-events: none; z-index: 6; mix-blend-mode: screen;
  background: linear-gradient(100deg, transparent 0 32%, rgba(255,226,180,.08) 42%, rgba(255,248,236,.34) 49%, rgba(255,255,255,.5) 50%, rgba(255,236,206,.16) 54%, transparent 64%);
  transform: translateX(calc((1.6 - var(--spec, .5) * 1.4) * 100%));
  opacity: calc(.1 + (1 - var(--facing, 1)) * 1.1); }
.case.box3d.slip .bx-glare { opacity: calc(.04 + (1 - var(--facing, 1)) * .5); }
.case.box3d .f-back .bx-glare { transform: translateX(calc((.2 + var(--spec, .5) * 1.4) * 100%)); }
.case.box3d .bx-dim { position: absolute; inset: 0; pointer-events: none; z-index: 6; background: linear-gradient(90deg, rgba(0,0,0,.5), rgba(0,0,0,.2));
  opacity: calc((1 - var(--facing, 1)) * .75); }
.case.box3d .bx-edge { position: absolute; inset: 0; pointer-events: none; z-index: 6;
  box-shadow: inset 1px 0 0 rgba(255,236,210,.32), inset -1px 0 0 rgba(0,0,0,.45);
  background: linear-gradient(90deg, rgba(255,226,186,.0), rgba(255,226,186,.22) 50%, rgba(255,226,186,0));
  opacity: calc(.25 + (1 - var(--facing, 1)) * .75); }
.case.box3d.turning .bx-edge { opacity: calc(.4 + (1 - var(--facing, 1)) * .6); }
/* cardboard edges: printed ink over fibre, worn pale at the corners, so mid-spin never shows a flat card */
.case.box3d.slip .f-top .bx-edge, .case.box3d.slip .f-bottom .bx-edge, .case.box3d.slip .f-edge:not(.bx-sp2) .bx-edge {
  background:
    radial-gradient(ellipse 9% 70% at 0% 50%, rgba(226,210,180,.42), transparent 75%),
    radial-gradient(ellipse 9% 70% at 100% 50%, rgba(226,210,180,.34), transparent 75%),
    repeating-linear-gradient(90deg, rgba(255,255,255,.035) 0 1px, transparent 1px 3px),
    linear-gradient(180deg, rgba(0,0,0,.28), transparent 32%, transparent 68%, rgba(0,0,0,.36)); }
.case.box3d.slip .f-edge:not(.bx-sp2) .bx-edge {
  background:
    radial-gradient(ellipse 70% 6% at 50% 0%, rgba(226,210,180,.42), transparent 75%),
    radial-gradient(ellipse 70% 6% at 50% 100%, rgba(226,210,180,.42), transparent 75%),
    linear-gradient(90deg, rgba(0,0,0,.3), transparent 30% 70%, rgba(0,0,0,.3)); }
.case.box3d.slip .f-spine .bx-edge, .case.box3d.slip .f-edge.bx-sp2 .bx-edge { background:
    radial-gradient(ellipse 80% 4% at 50% 0%, rgba(230,214,184,.4), transparent 80%),
    radial-gradient(ellipse 80% 4% at 50% 100%, rgba(230,214,184,.4), transparent 80%),
    linear-gradient(90deg, rgba(255,226,186,0), rgba(255,226,186,.18) 50%, rgba(255,226,186,0)); }
.case.box3d.clam .bx-edge { box-shadow: inset 1px 0 0 rgba(255,255,255,.4), inset -1px 0 0 rgba(0,0,0,.35); }

@media (prefers-reduced-motion: reduce) {
  .bx-cass .reel, .bx-cass .reel::after, .bx-cass .reel::before { transform: none !important; }
  .slot-tape.half > .slot-tape-face, .slot-tape.in > .slot-tape-face::after { animation: none !important; }
}

/* the flight layer: a clone of the case flies above everything, then hands back to the real one */
.bx-fly { position: fixed; z-index: 950; pointer-events: none; contain: layout style; }
.bx-fly > .case.box3d { margin: 0; }

/* the slot's top lip: the cassette goes in under it during the machine's pull */
.bx-clip { position: fixed; inset: 0; z-index: 1000; pointer-events: none; }

/* ================================================================ the slip sleeve, as a real one
   Five sides of printed card and an open bottom (box3d.js buildSleeve re-skins art.js's faces in place):
   .f-edge.bx-sp2 is the second printed spine, .f-top the closed cap in the card's own colour (--bx-cap,
   sampled off the scan), .f-bottom.bx-mouth the open end with its cut rim (--bx-rim), .bx-in-* the unprinted
   inside walls, .bx-sl the standing cassette that slides out of the mouth. */
.case.box3d.slip.bx-sleeve .stage { transform: none; transition: transform .38s cubic-bezier(.3,1.1,.5,1); }
.case.box3d.slip.bx-sleeve.bx-peek .stage { transform: translateY(calc(var(--cH) * -.04)) rotateX(9deg); transition-duration: .32s; }
.case.box3d.slip.bx-sleeve.bx-pulling .stage { transform: translateY(calc(var(--cH) * -.06)) rotateX(7deg); transition-duration: .3s; }
.case.box3d.bx-sleeve .sleeve-tape { visibility: hidden; pointer-events: none; }

/* the second spine: the same print as the first, same reading direction */
.box3d.slip .f-edge.bx-sp2 { background: var(--sp, #222); }
.box3d.slip .f-edge.bx-sp2::after { content: ''; position: absolute; inset: 0; pointer-events: none;
  background: url(../assets/art/card-grain.png) 0 0 / 180px 180px; opacity: .22; mix-blend-mode: overlay; }

/* the closed top: a folded cap in the card's colour, two score lines and worn corners */
.box3d.slip.bx-sleeve .f-top { background:
    radial-gradient(ellipse 7% 80% at 0% 50%, rgba(232,216,186,.35), transparent 80%),
    radial-gradient(ellipse 7% 80% at 100% 50%, rgba(232,216,186,.3), transparent 80%),
    linear-gradient(90deg, rgba(0,0,0,.22), transparent 18% 82%, rgba(0,0,0,.26)),
    linear-gradient(180deg, rgba(255,255,255,.08), transparent 45%, rgba(0,0,0,.18)),
    var(--bx-cap, var(--sp, #222)); }
.box3d.slip.bx-sleeve .f-top::after { content: ''; position: absolute; inset: 0; pointer-events: none;
  background: url(../assets/art/card-grain.png) 0 0 / 120px 120px; opacity: .3; mix-blend-mode: overlay; }

/* the open bottom: no face, just the cut edge of the card all round (and a hint of the glue flap) */
.box3d.slip .f-bottom.bx-mouth { background: none !important; overflow: visible;
  box-shadow: inset 0 0 0 max(1.5px, calc(var(--cw) * .011)) var(--bx-rim, var(--sp, #222)),
              inset 0 0 0 max(2.5px, calc(var(--cw) * .019)) rgba(214,198,170,.35); }
.box3d.slip .bx-mouth .bx-lip { position: absolute; left: 0; right: 0; height: max(1.5px, calc(var(--cw) * .011)); background: var(--bx-rim, var(--sp, #222)); pointer-events: none; }
.box3d.slip .bx-mouth .bx-lip.a { top: 0; box-shadow: 0 1px 0 rgba(255,255,255,.18); }
.box3d.slip .bx-mouth .bx-lip.b { bottom: 0; box-shadow: 0 -1px 0 rgba(255,255,255,.12); }

/* inside: plain grey-brown card, lit near the mouth and falling to black deeper in */
.box3d.slip .face.bx-in { display: block; pointer-events: none; background-color: transparent;
  background: linear-gradient(0deg, #8a7c6a, #4e4438 22%, #1c1814 60%, #0b0a08); }
.box3d.slip .bx-in-f, .box3d.slip .bx-in-b { left: 0; top: 0; width: var(--cw); height: var(--cH); }
.box3d.slip .bx-in-f { transform: translateZ(calc(var(--cd) / 2 - 1px)) rotateY(180deg); }
.box3d.slip .bx-in-b { transform: translateZ(calc(var(--cd) / -2 + 1px)); }
.box3d.slip .bx-in-l, .box3d.slip .bx-in-r { top: 0; height: var(--cH); width: var(--cd); left: calc((var(--cw) - var(--cd)) / 2); }
.box3d.slip .bx-in-l { transform: rotateY(90deg) translateZ(calc(var(--cw) / -2 + 1px)); }
.box3d.slip .bx-in-r { transform: rotateY(-90deg) translateZ(calc(var(--cw) / -2 + 1px)); }
.box3d.slip .bx-in-t { left: 0; width: var(--cw); height: var(--cd); top: calc((var(--cH) - var(--cd)) / 2);
  transform: rotateX(-90deg) translateZ(calc(var(--cH) / -2 + 1px)); background: #0b0a08; }

/* the cassette standing inside: label spine toward the main spine, its short end just up inside the mouth */
.box3d.slip .bx-sl { --th2: calc(var(--tw) * 4.1 / 7.4); position: absolute; width: var(--th2); height: var(--tw);
  left: calc((var(--cw) - var(--th2)) / 2); top: calc(var(--cH) - var(--tw) - var(--cH) * .035);
  transform-style: preserve-3d; cursor: grab; touch-action: none; }
.box3d.slip .bx-sl-mv { position: absolute; inset: 0; transform-style: preserve-3d; will-change: transform; }
.box3d.slip .bx-stand { --bx-d: min(calc(var(--tw) * .1337), calc(var(--cd) * .74)); position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%) rotate(90deg); pointer-events: auto; }
.box3d.slip .bx-stand .bx-front > .tape { position: absolute; left: 0; top: 0; }
.box3d.slip .bx-stand .bx-front::after { opacity: .5; }
.box3d.slip .bx-stand .bx-r { background: linear-gradient(90deg, #070708, #1a1a1d 45%, #0a0a0b); box-shadow: inset 0 0 0 1px #000; }
.box3d.slip .bx-stand .bx-r::after { opacity: .12; }
.case.bx-sleeve:has(.tape-v.hidden) .bx-sl { visibility: hidden; }
.case.box3d.bx-pulling { cursor: grabbing; }
.case.box3d.bx-pulling .bx-sl { cursor: grabbing; }

@media (prefers-reduced-motion: reduce) {
  .case.box3d.slip.bx-sleeve .stage, .case.box3d.slip.bx-sleeve.bx-peek .stage { transform: translateY(calc(var(--cH) * -.04)) rotateX(9deg); transition-duration: .32s; }
.case.box3d.slip.bx-sleeve.bx-pulling .stage { transform: none; transition: none; }
}
/* the tape leaving the sleeve's bottom must pass in front of the hint line under the box (the hint's opacity
   gives it its own layer), so the box's holder rides one layer up while it holds a sleeve */
#holder:has(> .case.bx-sleeve) { z-index: 2; }
