/* ============================================================
   J.A.M.E.S.H.A. — CAPTURE TREATMENTS
   Shared skin for every piece of recovered footage on the site.

   A capture is never "a video on a page". It is a thing some
   instrument recorded, and the instrument leaves marks. Six
   treatments, applied by data-treat on .cap:

     intercept   signals take, cyan, timecode + carrier lock
     seccam      fixed camera, washed, wide-angle, date burn
     chrono      Chronovisor plate, amber phosphor, sepia
     broadcast   commercial spot, clean, safe-area ticks
     overspill   a broadcast from a branch that is not ours
     projection  a 35mm print: grain, gate weave, changeover cues

   Everything is CSS so it costs no JS and survives with it off.
   The footage itself is never re-encoded for this - the look is
   applied at read time, which is also why RAW/TREATED can toggle.
   ============================================================ */

:root {
  --cap-chrono-ink:   #ffb000;   /* amber phosphor */
  --cap-chrono-deep:  #2a1806;
  --cap-chrono-paper: #fff1d4;
  --cap-int-ink:      #22d3ee;
  --cap-sec-ink:      #b8ffd0;
  --cap-red:          #e2483d;
  --cap-mono: "Share Tech Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* ---------------------------------------------------- the plate */
.cap {
  position: relative;
  display: block;
  overflow: hidden;
  background: #04070d;
  isolation: isolate;            /* keeps blend modes off the page */
  line-height: 0;
}
.cap video,
.cap img.cap-still {
  display: block;
  width: 100%;
  height: auto;
}

/* Overlay furniture sits above the picture but below any control. */
.cap-fx,
.cap-hud {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.cap-hud {
  line-height: 1.2;
  font-family: var(--cap-mono);
  font-size: clamp(9px, 1.5vw, 12px);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.cap-hud b { font-weight: 400; }

.cap-tl, .cap-tr, .cap-bl, .cap-br {
  position: absolute;
  padding: 0.5em 0.7em;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
}
.cap-tl { top: 0; left: 0; }
.cap-tr { top: 0; right: 0; text-align: right; }
.cap-bl { bottom: 0; left: 0; }
.cap-br { bottom: 0; right: 0; text-align: right; }

/* recording pip */
.cap-rec { display: inline-flex; align-items: center; gap: 0.45em; }
.cap-rec::before {
  content: "";
  width: 0.62em; height: 0.62em;
  border-radius: 50%;
  background: var(--cap-red);
  animation: cap-pip 1.6s steps(1, end) infinite;
}
@keyframes cap-pip { 0%, 55% { opacity: 1 } 56%, 100% { opacity: 0.15 } }

/* corner registration ticks - every instrument frames its own picture */
.cap-fx::before,
.cap-fx::after {
  content: "";
  position: absolute;
  width: 26px; height: 26px;
  border: 1px solid currentColor;
  opacity: 0.5;
}
.cap-fx::before { top: 8px; left: 8px; border-right: 0; border-bottom: 0; }
.cap-fx::after { bottom: 8px; right: 8px; border-left: 0; border-top: 0; }

/* ------------------------------------------------ scan + noise */
.cap-scan {
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    180deg, rgba(0, 0, 0, 0.22) 0 1px, transparent 1px 3px);
  mix-blend-mode: multiply;
  opacity: 0.55;
}
/* one bright line crawling down the picture, like a badly locked feed */
.cap-roll {
  position: absolute;
  left: 0; right: 0;
  height: 14%;
  background: linear-gradient(180deg,
    transparent, rgba(255, 255, 255, 0.055), transparent);
  animation: cap-roll 7s linear infinite;
}
@keyframes cap-roll { from { top: -14% } to { top: 100% } }

/* ================================================== INTERCEPT */
.cap[data-treat="intercept"] { color: var(--cap-int-ink); }
.cap[data-treat="intercept"] video,
.cap[data-treat="intercept"] .cap-still {
  filter: saturate(0.72) contrast(1.14) brightness(0.96)
          hue-rotate(-6deg);
}
.cap[data-treat="intercept"] .cap-fx {
  background:
    radial-gradient(ellipse at center, transparent 52%, rgba(0, 10, 18, 0.72) 100%);
  box-shadow: inset 0 0 0 1px rgba(34, 211, 238, 0.28);
}
.cap[data-treat="intercept"] .cap-hud { color: var(--cap-int-ink); }

/* ==================================================== SEC CAM */
.cap[data-treat="seccam"] { color: var(--cap-sec-ink); }
.cap[data-treat="seccam"] video,
.cap[data-treat="seccam"] .cap-still {
  filter: saturate(0.34) contrast(1.3) brightness(1.08);
}
.cap[data-treat="seccam"] .cap-fx {
  background:
    radial-gradient(ellipse 78% 62% at center, transparent 38%, rgba(0, 0, 0, 0.8) 100%);
}
/* the give-away of a cheap fixed camera: a fat soft vignette and a
   date stamp nobody ever set correctly */
.cap[data-treat="seccam"] .cap-hud { color: var(--cap-sec-ink); font-size: clamp(9px, 1.6vw, 13px); }
.cap[data-treat="seccam"] .cap-scan { opacity: 0.3; }

/* ================================================== CHRONOVISOR */
/* The instrument does not record colour. It records a plate, and the
   plate is amber. Duotone via two stacked layers: the picture is
   desaturated, then a colour wash is multiplied over it. */
.cap[data-treat="chrono"] { color: var(--cap-chrono-ink); background: var(--cap-chrono-deep); }
.cap[data-treat="chrono"] video,
.cap[data-treat="chrono"] .cap-still {
  filter: grayscale(1) contrast(1.22) brightness(1.04) sepia(0.62)
          saturate(2.1) hue-rotate(-12deg);
}
.cap[data-treat="chrono"] .cap-fx {
  background:
    linear-gradient(180deg, rgba(255, 176, 0, 0.10), transparent 22%, transparent 78%, rgba(255, 176, 0, 0.08)),
    radial-gradient(ellipse at center, transparent 46%, rgba(20, 10, 0, 0.86) 100%);
  box-shadow: inset 0 0 0 1px rgba(255, 176, 0, 0.3);
}
.cap[data-treat="chrono"] .cap-hud { color: var(--cap-chrono-ink); }
.cap[data-treat="chrono"] .cap-scan {
  background: repeating-linear-gradient(
    180deg, rgba(60, 30, 0, 0.3) 0 1px, transparent 1px 3px);
  opacity: 0.7;
}

/* ==================================================== BROADCAST */
.cap[data-treat="broadcast"] { color: #f0b429; }
.cap[data-treat="broadcast"] video,
.cap[data-treat="broadcast"] .cap-still { filter: saturate(1.08) contrast(1.04); }
.cap[data-treat="broadcast"] .cap-fx { box-shadow: inset 0 0 0 1px rgba(240, 180, 41, 0.25); }
.cap[data-treat="broadcast"] .cap-scan { display: none; }
.cap[data-treat="broadcast"] .cap-roll { display: none; }

/* ==================================================== OVERSPILL */
/* A broadcast that is not from here. It has to read as television —
   same furniture as BROADCAST — while being visibly the wrong
   television: the colour never quite lands, and the picture will not
   hold still for a whole spot. */
.cap[data-treat="overspill"] { color: #ff9d94; background: #0a0508; }
.cap[data-treat="overspill"] video,
.cap[data-treat="overspill"] .cap-still {
  filter: saturate(0.82) contrast(1.1) brightness(0.99) hue-rotate(8deg);
}
.cap[data-treat="overspill"] .cap-fx {
  background:
    linear-gradient(180deg, rgba(226, 72, 61, 0.08), transparent 26%, transparent 74%, rgba(168, 85, 247, 0.09)),
    radial-gradient(ellipse at center, transparent 54%, rgba(12, 4, 8, 0.7) 100%);
  box-shadow: inset 0 0 0 1px rgba(226, 72, 61, 0.3);
}
.cap[data-treat="overspill"] .cap-scan { opacity: 0.35; }
.cap[data-treat="overspill"] .cap-roll { animation-duration: 4.5s; }

/* =================================================== PROJECTION */
/* Not a feed and not a plate: a print, running through a projector.
   The tells are all mechanical - the gate never holds the frame
   perfectly still, the lamp is warmer than daylight, the emulsion has
   grain, and there are burns in the corner telling the projectionist
   to change over. Grain is an inline SVG turbulence so it costs no
   request and no image file. */
.cap[data-treat="projection"] { color: #ffd9a0; background: #0b0603; }
.cap[data-treat="projection"] video,
.cap[data-treat="projection"] .cap-still {
  filter: sepia(0.22) saturate(1.12) contrast(1.08) brightness(1.03);
  animation: cap-weave 2.7s ease-in-out infinite;
}
.cap[data-treat="projection"] .cap-fx {
  background:
    radial-gradient(ellipse 74% 66% at 50% 46%, rgba(255, 214, 160, 0.07) 0%, transparent 55%),
    radial-gradient(ellipse at center, transparent 44%, rgba(10, 5, 2, 0.78) 100%);
  box-shadow: inset 0 0 0 1px rgba(255, 200, 130, 0.16);
}
.cap[data-treat="projection"] .cap-scan {
  mix-blend-mode: overlay;
  opacity: 0.32;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
  animation: cap-grain 0.6s steps(4, end) infinite;
}
.cap[data-treat="projection"] .cap-roll { display: none; }
.cap[data-treat="projection"] .cap-hud { color: #ffd9a0; }

/* the projectionist's cue: two burns, seven seconds apart, top right */
.cap-cue {
  position: absolute; top: 6%; right: 5%;
  width: 8%; max-width: 34px; aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 248, 224, 0.92), rgba(255, 214, 160, 0.15) 62%, transparent 72%);
  opacity: 0; pointer-events: none; z-index: 5;
}
.cap.is-cueing .cap-cue { animation: cap-cue 0.28s steps(1, end) 4; }

/* the gate holds the print, but not perfectly */
@keyframes cap-weave {
  0%, 100% { transform: translate3d(0, 0, 0) }
  27%  { transform: translate3d(0.14%, -0.1%, 0) }
  54%  { transform: translate3d(-0.1%, 0.12%, 0) }
  78%  { transform: translate3d(0.06%, 0.05%, 0) }
}
@keyframes cap-grain {
  0%   { background-position: 0 0 }
  25%  { background-position: -40px 30px }
  50%  { background-position: 30px -25px }
  75%  { background-position: -25px -35px }
  100% { background-position: 0 0 }
}
@keyframes cap-cue { 0%, 49% { opacity: 0.85 } 50%, 100% { opacity: 0 } }

/* ============================================ DIVERGENCE / "off" */
/* How wrong a branch feels is a number, and the number drives the
   picture. Low divergence is almost clean. High divergence splits the
   colour channels apart and will not sit still. */
/* Divergence 1 adds nothing on purpose, so it gets no rule. It must NOT
   reset the filter here: these selectors sit after the treatment blocks
   at equal specificity, so anything they set on `video` beats the
   treatment instead of layering on top of it. That is also why the
   shiver keyframes below are written per treatment - a keyframe that
   animates `filter` has to restate that treatment's whole filter, and
   applying the wrong one would repaint the picture in the wrong skin. */
.cap[data-div="2"] .cap-fx { animation: cap-drift 9s ease-in-out infinite; }
.cap[data-div="3"] .cap-fx { animation: cap-drift 6s ease-in-out infinite; }
.cap[data-div="4"] .cap-fx { animation: cap-drift 4s ease-in-out infinite; }

.cap[data-treat="chrono"][data-div="3"] video { animation: cap-shiver 11s steps(1, end) infinite; }
.cap[data-treat="chrono"][data-div="4"] video { animation: cap-shiver 7s steps(1, end) infinite; }
.cap[data-treat="overspill"][data-div="2"] video { animation: cap-shiver-bc 13s steps(1, end) infinite; }
.cap[data-treat="overspill"][data-div="3"] video { animation: cap-shiver-bc 9s steps(1, end) infinite; }
.cap[data-treat="overspill"][data-div="4"] video { animation: cap-shiver-bc 6s steps(1, end) infinite; }

@keyframes cap-drift {
  0%, 100% { opacity: 1 }
  48% { opacity: 0.86 }
  50% { opacity: 1; transform: translateX(1px) }
  52% { opacity: 0.9; transform: none }
}
/* the channels come apart for a couple of frames, then recover */
@keyframes cap-shiver {
  0%, 92%, 100% { filter: grayscale(1) contrast(1.22) brightness(1.04) sepia(0.62) saturate(2.1) hue-rotate(-12deg); }
  93% { filter: grayscale(1) contrast(1.5) brightness(1.1) sepia(0.62) saturate(2.4) hue-rotate(-12deg); transform: translateX(-2px); }
  95% { filter: grayscale(1) contrast(1.05) brightness(0.94) sepia(0.62) saturate(1.8) hue-rotate(-12deg); transform: translateX(2px); }
  97% { transform: none; }
}
/* same failure, on a set that is still trying to show you colour */
@keyframes cap-shiver-bc {
  0%, 94%, 100% { filter: saturate(0.82) contrast(1.1) brightness(0.99) hue-rotate(8deg); }
  95% { filter: saturate(0.3) contrast(1.28) brightness(1.06) hue-rotate(-26deg); transform: translateX(-2px); }
  97% { filter: saturate(1.5) contrast(0.96) brightness(0.93) hue-rotate(34deg); transform: translateX(2px); }
  99% { transform: none; }
}

/* a hard tear across the picture, fired occasionally by JS */
.cap.is-tearing .cap-tear {
  position: absolute;
  left: 0; right: 0;
  height: 9%;
  background: rgba(255, 241, 212, 0.1);
  box-shadow: 0 0 24px rgba(255, 176, 0, 0.4);
  transform: translateX(6px);
}

/* --------------------------------------------------- RAW toggle */
/* Reused from the Junior Depth Chronograph switch: a reader can always
   see that the century-old-looking plate is this afternoon's footage. */
.cap.is-raw video,
.cap.is-raw .cap-still { filter: none !important; animation: none !important; }
.cap.is-raw .cap-scan,
.cap.is-raw .cap-roll,
.cap.is-raw .cap-cue,
.cap.is-raw .cap-tear { display: none; }
.cap.is-raw .cap-fx { background: none; animation: none; }

@media (prefers-reduced-motion: reduce) {
  .cap-roll, .cap-rec::before { animation: none !important; }
  .cap .cap-fx, .cap video, .cap .cap-scan, .cap .cap-cue { animation: none !important; }
}
