/* FILM page: a 1980s television, a tape deck under it, and the films as VHS tapes.
   Sizes are all derived from --tvw (width of the television photograph).
   The television, the deck and the tape faces are photographs (images/FILM/); labels, controls and the screen are live. */
@font-face { font-family: Geist; src: url(../fonts/geist/Geist-Variable.woff2) format("woff2"); font-weight: 100 900; }
:root { --ink: #f2f2ef; --mute: #8a8a86; --line: #232323; --phosphor: #a8f5ab; --mono: "SFMono-Regular", Menlo, Consolas, monospace; }
* { box-sizing: border-box; margin: 0; }
html, body { height: 100%; overflow: hidden; }
body { background: #000; color: var(--ink); font-family: Geist, Arial, sans-serif; -webkit-font-smoothing: antialiased; }
button { font: inherit; color: inherit; }

.film-page {
  --tvw: min(1100px, calc((100vh - 232px) / .72), calc((100vw - 80px) / 1.06));
  --u: calc(var(--tvw) / 1000);
  --W: calc(var(--tvw) * .32);            /* tape: 187 mm */
  --H: calc(var(--W) * .13);              /* 25 mm (matches the spine photograph) */
  --D: calc(var(--W) * .5014);            /* 103 mm (matches the top photograph) */
}

/* Same header as the rest of the site (css/portfolio-shell.css), on black */
.pf-header { position: absolute; top: 0; left: 0; right: 0; z-index: 30; background: transparent; backdrop-filter: none; color: var(--ink); }
.pf-header a:hover { color: var(--mute); }

.set { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
.set img { -webkit-user-drag: none; user-drag: none; }
.set { position: absolute; left: 50%; top: calc(50% - 29px); transform: translate(-50%, -50%); display: flex; align-items: flex-end; gap: calc(var(--tvw) * .05); }
.rig { transition: filter .45s ease; }
.set.showcase .rig { filter: brightness(.45) saturate(.8); }
.rig { position: relative; display: flex; flex-direction: column-reverse; align-items: center; }
.shelf { position: relative; }

/* ---------- Television ---------- */
.tv { position: relative; width: var(--tvw); aspect-ratio: 4 / 3; margin: calc(var(--tvw) * -.084) calc(var(--tvw) * -.176) calc(var(--tvw) * -.106) calc(var(--tvw) * -.172); }
.tv, .tv > img { pointer-events: none; }
.tv .screen, .tv .knob, .tv .tvkey { pointer-events: auto; }
.tv > img { width: 100%; height: 100%; display: block; user-select: none; }
.spill { position: absolute; left: 18.5%; top: 15%; width: 52.5%; height: 59%; pointer-events: none; mix-blend-mode: screen; --sp: 40, 60, 90;
  background: radial-gradient(ellipse closest-side, rgba(var(--sp), .5), rgba(var(--sp), .16) 58%, rgba(var(--sp), 0) 100%); }
body.is-off .spill { opacity: 0; }
.screen { isolation: isolate; position: absolute; left: 20.5%; top: 19.3%; width: 48.45%; height: 52.55%; border-radius: 2.2% / 2.8%; overflow: hidden; background: #050607; transform-origin: center; cursor: pointer; --osd: calc(var(--u) * 15);
  --look-filter: saturate(.9) contrast(1.08) brightness(1.15); --scan: .7; --bulge: 1; --grain: .05; }
.screen video, .screen .vframe { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; border: 0; filter: var(--look-filter); }
.screen .vframe { left: 50%; width: 177.78%; inset: 0 auto 0 50%; transform: translateX(-50%); pointer-events: none; display: none; }
.screen canvas { position: absolute; inset: 0; width: 100%; height: 100%; image-rendering: pixelated; opacity: 0; mix-blend-mode: screen; z-index: 3; pointer-events: none; }
.screen .blue { position: absolute; inset: 0; background: #0b24c9; display: none; place-content: center; text-align: center; gap: .6em; color: #fff; font: 700 var(--osd)/1.2 var(--mono); letter-spacing: .1em; z-index: 1; }
.screen .blue small { font-size: .62em; font-weight: 600; opacity: .85; }
.screen .blue > * { filter: blur(.5px); text-shadow: 0 0 .4em rgba(255,255,255,.7); }
.screen[data-mode="blue"] .blue { display: grid; }
.screen[data-mode="blue"] video, .screen[data-mode="blue"] .vframe { visibility: hidden; }
.screen .scan { position: absolute; inset: 0; background: repeating-linear-gradient(0deg, rgba(0,0,0,.28) 0 1px, rgba(0,0,0,0) 1px 3px); pointer-events: none; z-index: 4; opacity: var(--scan); }
.screen .bulge { position: absolute; inset: 0; border-radius: inherit; pointer-events: none; z-index: 4;
  background: radial-gradient(115% 100% at 50% 50%, rgba(0,0,0,0) 55%, rgba(0,0,0,calc(.65 * var(--bulge))) 100%);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.85), inset 0 calc(var(--u) * 16) calc(var(--u) * 22) calc(var(--u) * -8) rgba(0,0,0,.75), inset calc(var(--u) * 10) 0 calc(var(--u) * 18) calc(var(--u) * -8) rgba(0,0,0,.5), inset calc(var(--u) * -8) 0 calc(var(--u) * 16) calc(var(--u) * -8) rgba(0,0,0,.4), inset 0 calc(var(--u) * -8) calc(var(--u) * 14) calc(var(--u) * -8) rgba(0,0,0,.4); }
.screen .glass { position: absolute; inset: -12%; pointer-events: none; z-index: 4; mix-blend-mode: screen; will-change: transform;
  transform: translate(calc(var(--rx, 0) * -16px), calc(var(--ry, 0) * -12px));
  background:
    radial-gradient(75% 50% at 22% 4%, rgba(255,255,255,.17), rgba(255,255,255,0) 70%),
    linear-gradient(112deg, rgba(255,255,255,0) 26%, rgba(255,255,255,.045) 26.6%, rgba(255,255,255,.045) 33%, rgba(255,255,255,0) 33.6%),
    linear-gradient(112deg, rgba(255,255,255,0) 38%, rgba(255,255,255,.022) 38.5%, rgba(255,255,255,.022) 41%, rgba(255,255,255,0) 41.5%),
    radial-gradient(90% 70% at 88% 100%, rgba(140,170,255,.1), rgba(0,0,0,0) 60%); }
.screen .rim { position: absolute; inset: 0; border-radius: inherit; pointer-events: none; z-index: 4; box-shadow: inset 0 1px 0 rgba(255,255,255,.22), inset 1px 0 0 rgba(255,255,255,.1); }
.screen .grain { will-change: translate; position: absolute; inset: -10%; z-index: 3; pointer-events: none; mix-blend-mode: overlay; opacity: var(--grain); animation: grain .6s steps(5) infinite;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .5 0 0 0 0 .5 0 0 0 0 .5 0 0 0 1.6 -.25'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E") 0 0 / 180px 180px; }
@keyframes grain { 0% { translate: 0 0; } 20% { translate: -23px 11px; } 40% { translate: 17px -29px; } 60% { translate: -9px -17px; } 80% { translate: 27px 19px; } 100% { translate: 0 0; } }

/* TUNE: the picture texture, from a clean signal to a badly tuned one */
.screen[data-look="clean"] { --look-filter: saturate(1) contrast(1.03) brightness(1.06); --scan: 0; --bulge: .3; --grain: 0; }
.screen[data-look="crt"]   { --look-filter: saturate(.9) contrast(1.08) brightness(1.15); --scan: .7; --bulge: 1; --grain: .05; }
.screen[data-look="vhs"]   { --look-filter: url(#f-vhs) saturate(.82) contrast(1.1) brightness(1.1) sepia(.14) blur(.35px); --scan: 1; --bulge: 1; --grain: .16; }
.screen[data-look="ghost"] { --look-filter: url(#f-ghost) saturate(.72) contrast(1.05) brightness(1.1) hue-rotate(-8deg) blur(.3px); --scan: .8; --bulge: 1; --grain: .3; }
.screen[data-look="mono"]  { --look-filter: grayscale(1) contrast(1.3) brightness(1.1); --scan: .8; --bulge: 1; --grain: .22; }

/* VHS tracking noise while a tape loads (and now and then in VHS mode) */
.screen .band { position: absolute; left: 0; right: 0; height: 16%; top: 100%; z-index: 3; pointer-events: none; opacity: 0;
  background: repeating-linear-gradient(90deg, rgba(255,255,255,.55) 0 2px, rgba(255,255,255,.1) 2px 5px, rgba(0,0,0,.4) 5px 7px); mix-blend-mode: screen; }
.screen.tracking .band { animation: band 1s linear; }
.screen[data-look="vhs"]:not(.tracking) .band { animation: band-loop 9s linear infinite; }
.screen.tracking video, .screen.tracking .vframe { animation: jitter .9s steps(9); }
@keyframes band { 0% { top: 105%; opacity: .9; } 85% { opacity: .9; } 100% { top: -20%; opacity: 0; } }
@keyframes band-loop { 0%, 82% { top: 105%; opacity: 0; } 84% { opacity: .55; } 100% { top: -20%; opacity: 0; } }
@keyframes jitter { 0% { translate: 0 0; } 20% { translate: 5px 0; } 40% { translate: -4px 0; } 60% { translate: 7px 0; } 80% { translate: -2px 0; } 100% { translate: 0 0; } }

/* On-screen display: green characters, like a set from the eighties */
.osd { position: absolute; z-index: 2; font: 700 var(--osd)/1 var(--mono); color: #b9ffbd; letter-spacing: .08em; pointer-events: none; opacity: 0; transition: opacity .25s; white-space: nowrap;
  text-shadow: 0 0 .16em rgba(168,255,176,.95), 0 0 .55em rgba(110,230,130,.6), 0 0 1.3em rgba(80,200,110,.3);
  filter: var(--look-filter) blur(.55px); -webkit-font-smoothing: none; text-rendering: optimizeSpeed; }
.osd.on { opacity: 1; }
.osd.state { top: 7%; left: 6%; }
.osd.time { top: 7%; right: 6%; }
.osd.low { bottom: 8%; left: 6%; }
.osd.low small { display: block; font-size: .72em; opacity: .85; margin-top: .5em; }
.osd.low .bars { display: flex; gap: .22em; margin-top: .55em; }
.osd.low .bars i { display: block; width: .62em; height: 1.05em; background: #b9ffbd; box-shadow: 0 0 .3em rgba(168,255,176,.9), 0 0 .9em rgba(110,230,130,.5); }
.osd.low .bars i.off { background: transparent; box-shadow: none; border: 1px solid rgba(168,245,171,.45); }
.osd.low .bars.five i { width: 1.4em; }
.osd.blink { animation: blink 1s steps(2) infinite; }
@keyframes blink { 50% { opacity: 0; } }

.screen.off { animation: power-off .45s cubic-bezier(.6,0,.4,1) forwards; }
.screen.on { animation: power-on .5s cubic-bezier(.2,.8,.2,1); }
@keyframes power-off { 0% { transform: scale(1); filter: brightness(1); } 55% { transform: scale(1, .006); filter: brightness(3); } 100% { transform: scale(0, .006); filter: brightness(0); } }
@keyframes power-on { 0% { transform: scale(0, .006); filter: brightness(3); } 45% { transform: scale(1, .006); } 100% { transform: scale(1); filter: brightness(1); } }
/* The power lamp when the TV is off: the lamp from the photograph itself, dimmed and drained of colour, so it is the same lens, unlit */
.led-off { position: absolute; left: 76.39%; top: 66.29%; width: 2.6%; aspect-ratio: 1; transform: translate(-50%, -50%); pointer-events: none; opacity: 0; transition: opacity .25s; background-repeat: no-repeat;
  filter: saturate(.35) brightness(.27) contrast(1.05);
  -webkit-mask-image: radial-gradient(circle, #000 52%, transparent 71%); mask-image: radial-gradient(circle, #000 52%, transparent 71%); }
body.is-off .led-off { opacity: 1; }
/* with the lamp off, its red reflection on the neighbouring keys goes too */
#vol-down, #tv-power { transition: filter .25s; }
body.is-off #vol-down, body.is-off #tv-power { filter: saturate(.2); }

/* Controls on the set. Their names are printed on the set itself. */
.knob { position: absolute; left: 76.43%; top: 47.62%; width: 7.4%; aspect-ratio: 1; translate: -50% -50%; border-radius: 50%; border: 0; padding: 0; cursor: grab; background-repeat: no-repeat; transition: rotate .3s cubic-bezier(.3,1.4,.5,1); touch-action: none; }
.knob:active { cursor: grabbing; }
.tune-ring { position: absolute; left: 76.43%; top: 47.62%; width: 13.5%; aspect-ratio: 1; translate: -50% -50%; pointer-events: none; overflow: visible; }
.tune-ring line { stroke: rgba(235,235,230,.45); stroke-width: 1; stroke-linecap: round; }
.tune-ring line.lit { stroke: rgba(235,235,230,.98); }
.print { position: absolute; left: 76.43%; translate: -50% -50%; font: 600 calc(var(--u) * 6.4)/1 var(--mono); letter-spacing: .12em; color: rgba(235,235,230,.55); pointer-events: none; white-space: nowrap; }
.tvkey { position: absolute; left: 76.43%; width: 4.8%; height: 3.5%; translate: -50% -50%; border: 0; padding: 0; border-radius: 3px; background: transparent no-repeat; cursor: pointer; display: grid; place-items: center; user-select: none; transition: translate .09s, scale .09s, filter .09s; }
.tvkey span { font: 600 calc(var(--u) * 7)/1 var(--mono); letter-spacing: .04em; color: rgba(235,235,230,.6); pointer-events: none; white-space: nowrap; }
.tvkey:hover, .deck-btn:hover, .knob:hover { filter: brightness(1.2); }
.tvkey:active, .tvkey.down { translate: -50% calc(-50% + 1.5px); scale: .965; filter: brightness(.72); }
.deck-btn:active, .deck-btn.down { translate: 0 1px; scale: .96; filter: brightness(.72); }
.knob:active { scale: .965; }
.knob:focus-visible, .tvkey:focus-visible, .deck-btn:focus-visible, .tape:focus-visible, .slot:focus-visible { outline: 1px solid var(--phosphor); outline-offset: 3px; }

/* ---------- Tape deck (photograph; controls and display are live on top of it) ---------- */
.deck { position: relative; width: calc(var(--tvw) * .64); aspect-ratio: 1800 / 396; margin-top: calc(var(--tvw) * -.0107); }
/* contact shadow: the TV feet sit on the top edge of the deck */
.deck::after { content: ""; position: absolute; left: 3%; right: 3%; top: .5%; height: 9%; pointer-events: none; background: radial-gradient(ellipse 50% 100% at 50% 0, rgba(0,0,0,.7), rgba(0,0,0,0) 72%); z-index: 1; }
.deck-img { width: 100%; height: 100%; display: block; user-select: none; }
.slot { position: absolute; left: 9.3%; top: 18.6%; width: 63%; height: 45.8%; border: 0; padding: 0; background: transparent; cursor: pointer; border-radius: 3px; }
.door-glass { position: absolute; z-index: 12; left: 9.3%; top: 18.6%; width: 63%; height: 45.8%; pointer-events: none; border-radius: 3px; transition: opacity .35s;
  background: linear-gradient(172deg, rgba(255,255,255,.1), rgba(255,255,255,0) 42%), rgba(6,6,7,.42); }
.deck.open .door-glass { opacity: 0; }
.deck-display { position: absolute; left: 74.4%; top: 18.6%; width: 21.9%; height: 31.2%; padding: 0 3%; display: flex; align-items: center; justify-content: space-between; pointer-events: none;
  font: 700 calc(var(--u) * 17)/1 var(--mono); color: #8ff0b2; text-shadow: 0 0 6px rgba(143,240,178,.6); letter-spacing: .06em; }
.deck-display .st { font-size: .6em; }
.deck-btn { position: absolute; top: 58.6%; width: 5%; height: 10.9%; border: 0; padding: 0; border-radius: 2px; background: transparent no-repeat; cursor: pointer; display: grid; place-items: center; transition: translate .09s, scale .09s, filter .09s; }
.deck-btn b { font: 600 calc(var(--u) * 9)/1 var(--mono); color: rgba(235,235,230,.55); pointer-events: none; }

/* ---------- Tapes: photographed faces on a real 3D box ---------- */
/* The pile of tapes, right of the set. Every tape sits in the same base box at the
   bottom of the pile; js/film.js moves them into place with transforms, so tapes can slide out and drop. */
.shelf { list-style: none; padding: 0; position: relative; width: var(--W); height: calc(var(--H) * 1.1 * var(--n, 4)); margin-bottom: calc(var(--tvw) * .004); visibility: hidden; }
.shelf.ready { visibility: visible; }
.holder { position: absolute; left: 0; bottom: 0; width: var(--W); height: var(--H); }
.tape { position: absolute; inset: 0; border: 0; padding: 0; background: none; cursor: pointer; perspective: calc(var(--W) * 3.2); transition: translate .25s cubic-bezier(.3,1.3,.5,1); z-index: 2; }
.tape::after { content: ""; position: absolute; inset: 0; border-radius: 2px; background: rgba(0,0,0,.42); opacity: 0; transition: opacity .3s; pointer-events: none; }
.tape.in-deck::after { opacity: 1; }
.tape.is-rest:hover, .tape.is-rest:focus-visible { translate: calc(var(--W) * .06) 0; }
.tape.moving { z-index: 20; will-change: transform; }
.cube { will-change: transform; }
.tape.in-deck { z-index: 10; cursor: default; }
.cube { position: absolute; inset: 0; transform-style: preserve-3d; display: block; }
.face { position: absolute; left: 50%; top: 50%; overflow: hidden; background: linear-gradient(180deg, #1d1d1f, #0b0b0c); display: block; }
.face > img { width: 100%; height: 100%; display: block; object-fit: fill; user-select: none; }
.f-spine { width: var(--W); height: var(--H); margin: calc(var(--H) / -2) 0 0 calc(var(--W) / -2); transform: translateZ(calc(var(--D) / 2)); }
.f-back { width: var(--W); height: var(--H); margin: calc(var(--H) / -2) 0 0 calc(var(--W) / -2); transform: rotateY(180deg) translateZ(calc(var(--D) / 2)); }
.f-top, .f-bottom { width: var(--W); height: var(--D); margin: calc(var(--D) / -2) 0 0 calc(var(--W) / -2); }
.f-top { transform: rotateX(90deg) translateZ(calc(var(--H) / 2)); }
.f-bottom { transform: rotateX(-90deg) translateZ(calc(var(--H) / 2)); }
.f-left, .f-right { width: var(--D); height: var(--H); margin: calc(var(--H) / -2) 0 0 calc(var(--D) / -2); }
.f-left { transform: rotateY(-90deg) translateZ(calc(var(--W) / 2)); }
.f-right { transform: rotateY(90deg) translateZ(calc(var(--W) / 2)); }

/* Labels: positions measured from the photographs. The paper and the ink are printed by tools/film-labels.html (js/film-labels.js) and saved in images/FILM/labels/; they are and multiplied onto the photographed blank label. */
.spine-label, .top-label { position: absolute; mix-blend-mode: multiply; }
.spine-label { left: 13.1%; width: 73.8%; top: 13%; height: 75.5%; }
.top-label { left: 28.8%; width: 42.2%; top: 30.8%; height: 44.7%; }
.spine-label img, .top-label img { width: 100%; height: 100%; display: block; opacity: .93; }

/* ---------- Bottom left: which tape is in, its title and year ---------- */
.info { position: absolute; left: var(--pf-edge); bottom: 28px; display: flex; gap: 32px; font-size: 12px; line-height: 1.45; z-index: 5; }
.info b { font-weight: 400; }
.info a { color: var(--mute); text-decoration: none; }
.info a:hover { color: var(--ink); }

@media (max-width: 700px) {
  html, body { height: auto; overflow: visible; }
  /* the TV photograph is wider than a phone: cut it at the edge of the screen so the page never slides sideways */
  .set { overflow-x: clip; }
  html { overflow-x: clip; overscroll-behavior-x: none; }
  .film-page { --tvw: calc(100vw * 1.36); }
  .set { position: relative; left: auto; top: auto; transform: none; margin-top: 150px; flex-direction: column; align-items: center; gap: calc(var(--tvw) * .04); }
  .shelf { align-self: center; margin-bottom: 0; }
  .info { position: relative; left: auto; bottom: auto; margin: 32px 20px 28px; gap: 20px; flex-wrap: wrap; font-size: 11px; }
}
@media (prefers-reduced-motion: reduce) { .screen.off, .screen.on, .screen.tracking video, .screen.tracking .vframe, .screen.tracking .band, .screen .grain, .osd.blink { animation: none; } .knob, .tape, .door-glass { transition: none; } }
