/* Contact sheet: one strip of film per section, laid on the set's colour, with a loupe as the selection tool.
   Film and AI run on 70mm double-perf stock, Stills on 35mm Portra 400 single-perf (Hayden, 2026-10-20). Every
   strip is the same height and sits at the same place on every section page; only the name moves. */
.sheet {
  --title-size: clamp(104px, 15vw, 248px);
  container-type: inline-size; display: grid; grid-template-rows: calc(var(--title-size) * .78) auto auto 1fr; align-items: start;
  grid-template-columns: minmax(0, 1fr);             /* the page's width, however long the strip in the reel */
  min-height: calc(100svh - 130px); padding: clamp(24px, 4.6vh, 56px) var(--gutter) 24px;
}
/* The page's name stands about 2.5x larger behind the strip, one shade darker than the page (critic round 6, gap 2;
   Hayden, 2026-10-20: "Go with B"): FILM off the top right, STILLS off the bottom left, and AI inside the page on the
   left ("move it into the page.. right"). Row 1 keeps its height, so the strip holds its place on every page. */
.sheet { position: relative; overflow: hidden; }
.sheet-title { position: absolute; z-index: -1; margin: 0; white-space: nowrap; pointer-events: none;
  font: 800 calc(var(--title-size) * 2.5)/.74 var(--font-display); letter-spacing: -.01em; text-transform: uppercase;
  color: color-mix(in srgb, var(--field) 84%, #000); }
.sheet--film .sheet-title { top: calc(var(--title-size) * -.19); right: calc(var(--title-size) * -.14); }
.sheet--ai .sheet-title { top: calc(var(--title-size) * -.19); left: var(--gutter); }
.sheet--stills .sheet-title { bottom: 0; left: calc(var(--title-size) * -.12); }  /* whole, above the footer (Hayden, 2026-10-20) */
/* Stills' strip sits higher, only grazing the top of STILLS: a strip over the tops of letters hides far more of a word
   than one over their feet (Hayden, 2026-10-20: "STILLS is still largely illegible"). STILLS sits whole above the
   footer, "not clipped on the bottom", and the strip rises with it. */
.sheet--stills .reel { margin-top: calc(var(--title-size) * -.56); }
.sheet--stills .strips { padding-bottom: calc(var(--label) + 40px); }
.sheet--stills { padding-bottom: 8px; }  /* the page fits one screen, footer where every page has it */

.reel {
  grid-row: 2; position: relative;
  --H: min(calc(100cqw / 5.43), 360px);               /* one strip height everywhere: five 35mm frames, margin to margin */
  --label: max(20px, calc(var(--H) * .1));
}
/* The strip runs in a reel that scrolls sideways when the section outgrows the page (Hayden, 2026-10-20). The reel
   reaches the screen's edges, so a long strip visibly runs on off the right; its padding keeps the first frame on the
   margin and holds the loupe's skirt and the captions inside it. */
.strips {
  display: flex; flex-direction: column; align-items: flex-start;
  margin-inline: calc(var(--gutter) * -1);
  padding: calc(var(--H) * .26) var(--gutter) calc(var(--label) + 64px);  /* room for the loupe's skirt above the strip */
  overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
  scroll-snap-type: x proximity; scroll-padding-inline: var(--gutter);
}
.strips::-webkit-scrollbar { display: none; }
.strip .frame { scroll-snap-align: start; scroll-margin-inline: calc(var(--gap) / 2); }  /* snaps on the strip's edge, frame line and all */
/* The arrow: a disc of the page's own colour punched over the film's right end, like the perforations, with an
   arrow in the display face. It pages the strip a screenful at a time; at the end it turns round. */
.reel-arrow {
  position: absolute; right: calc(var(--gutter) * -1 + max(10px, (var(--gutter) - 56px) / 2));
  top: calc(var(--H) * .26 + var(--H) / 2); transform: translateY(-50%);
  display: grid; place-items: center; width: 56px; height: 56px; padding: 0 0 .1em; border: 0; border-radius: 50%; cursor: pointer;
  background: var(--field); color: var(--on-field); box-shadow: 0 0 0 3px var(--film-base), 0 6px 18px rgba(0,0,0,.35);
  font: 800 34px/1 var(--font-display); transition: transform .25s, background-color .25s;
}
.reel-arrow { z-index: 2; }
.reel-arrow[hidden] { display: none; }
/* Where film has run out of view, the page's colour fades over that edge, so no sliver of a frame or stub of a
   caption hangs at the screen's edge. Overlays, not a mask: a mask would isolate the loupe's multiply blend. */
.reel::before, .reel::after { content: ""; position: absolute; z-index: 1; top: calc(var(--H) * .26); height: var(--H); width: var(--gutter); /* the film's band only, never over the page's name */
  pointer-events: none; opacity: 0; transition: opacity .3s; }
.reel::before { left: calc(var(--gutter) * -1); background: linear-gradient(to right, var(--field) 55%, transparent); }
.reel::after { right: calc(var(--gutter) * -1); background: linear-gradient(to left, var(--field) 55%, transparent); }
.reel.has-left::before, .reel.has-right::after { opacity: 1; }
.reel-arrow span { display: block; transition: transform .35s cubic-bezier(.3, .7, .2, 1); }
.reel-arrow.is-back span { transform: rotate(180deg); }
.reel-arrow:hover { transform: translateY(-50%) scale(1.07); }
.reel-arrow:focus-visible { outline: 3px solid var(--on-field); outline-offset: 4px; }
.strip { position: relative; display: flex; gap: var(--gap); padding: var(--rb) calc(var(--gap) / 2); }
/* 35mm: a 36 x 24 frame on 35mm film, a 2 mm frame line, 5.5 mm rebates, a perforation every 4.75 mm along the top
   edge only; the bottom edge carries the stock's edge printing and frame numbers. */
.strip[data-stock="35mm"] { --fh: calc(var(--H) * .686); --fw: calc(var(--fh) * 1.5); --rb: calc(var(--H) * .157); --gap: calc(var(--H) * .057);
  --pitch: calc(var(--fw) / 8); --perfs: url(/assets/design/sprockets.svg); --scratch: 55%; }
/* 70mm: frames 48.5 mm tall across 70 mm film, filling the sheet's width; 10.75 mm rebates with a perforation every
   4.75 mm on both edges and a magnetic sound stripe either side of the perforations. */
.strip[data-stock="70mm"] { --fh: calc(var(--H) * .693); --rb: calc(var(--H) * .1535); --gap: calc(var(--H) * .04);
  --fw: calc(100cqw / var(--frames) - var(--gap)); --pitch: calc(var(--H) * .0679); --perfs: url(/assets/design/perfs-70mm.svg); --scratch: 40%; }

.frame, .loupe-frame {
  position: relative; display: block; flex: none; width: var(--fw); height: var(--fh);
  background: var(--film-base);
  box-shadow: calc(var(--gap) / 2) 0 0 var(--film-base), calc(var(--gap) / -2) 0 0 var(--film-base);
}
/* Rebates with perforations; the holes are real holes, so the set's colour shows through. */
.frame::before, .frame::after, .loupe-frame::before, .loupe-frame::after {
  content: ""; position: absolute; left: calc(var(--gap) / -2); width: calc(100% + var(--gap)); height: var(--rb);
  background: var(--film-base); pointer-events: none;
  -webkit-mask: var(--perfs) 0 0 / var(--pitch) 100% round no-repeat;
          mask: var(--perfs) 0 0 / var(--pitch) 100% round no-repeat;
}
.frame::before, .loupe-frame::before { bottom: calc(100% - 1px); }  /* a pixel of overlap, so no seam opens between frame and rebate */
.frame::after, .loupe-frame::after { top: calc(100% - 1px); transform: scaleY(-1); }
/* 70mm prints carry magnetic sound stripes, one outside and one inside the perforations on each edge. */
.strip[data-stock="70mm"] :is(.frame, .loupe-frame)::before, .strip[data-stock="70mm"] :is(.frame, .loupe-frame)::after {
  background: linear-gradient(to bottom, var(--film-base) 5.5%, #4E3A26 5.5% 16.5%, var(--film-base) 16.5% 57%, #4E3A26 57% 66%, var(--film-base) 66%);
}
/* 35mm single-perf: no perforations on the bottom edge, only the stock's name and the frame numbers. */
.strip[data-stock="35mm"] :is(.frame, .loupe-frame)::after {
  -webkit-mask: none; mask: none; transform: none;
  content: attr(data-edge); display: flex; align-items: center; padding-left: calc(var(--fw) * .07); white-space: pre;
  font: 600 calc(var(--H) * .045)/1 var(--font-display); letter-spacing: .14em; color: #E2C27C;
}

.frame { color: var(--on-field); text-decoration: none; }
/* 70mm edge print, in the clear band between the frame and the bottom perforations (critic round 7). */
.frame-code { position: absolute; z-index: 1; left: 0; right: 0; top: 100%; height: calc(var(--rb) * .34); display: flex; align-items: center;
  padding-left: calc(var(--fw) * .05); white-space: pre; pointer-events: none;
  font: 600 calc(var(--H) * .034)/1 var(--font-display); letter-spacing: .16em; color: #E2C27C; }
.frame-img, .loupe-frame img { display: block; width: 100%; height: 100%; object-fit: cover; border-radius: calc(var(--H) * .006); }
/* Unexposed frames: black emulsion with a word scratched through it to the clear base (set in Scratched Letters, public domain; tools/scratch_font.py). */
.frame--blank, .loupe-frame--blank { background: var(--film-base) center / var(--scratch) auto no-repeat; } /* small enough to read whole inside the lens */
[data-scratch="coming-soon"] { background-image: url(/assets/design/scratch-coming-soon.png); }
[data-scratch="just-rendering"] { background-image: url(/assets/design/scratch-just-rendering.png); }

.frame-label {
  position: absolute; left: 0; top: calc(100% + var(--rb) + var(--label)); width: 100%;
  font: 500 15px/1.3 var(--font-text); color: var(--on-field);
  text-decoration: underline; text-decoration-color: transparent; text-underline-offset: .22em; text-decoration-thickness: 1.5px;
}
.frame:hover .frame-label, .frame:focus-visible .frame-label { text-decoration-color: currentColor; }
.frame.is-cut .frame-label { opacity: 0; }  /* a frame the screen edge cuts keeps its caption back (src/js/sheet.js) */
.frame-label { transition: opacity .3s; }
.frame:focus-visible { outline: 3px solid var(--white); outline-offset: 3px; }

.edge {
  position: absolute; z-index: 2; right: calc(var(--gap) / 2 + var(--fw) * .06); bottom: calc(var(--rb) * .07);
  font: 600 calc(var(--H) * .036)/1 var(--font-display); letter-spacing: .42em; color: var(--field);
}

/* The loupe: Hayden's photographed loupe (render 2026-10-20) laid over the strip with a multiply blend, so its
   clear skirt shows the strip through it. It sits on frame --at and slides when the selection moves; its lens
   holds a copy of the whole strip, magnified and offset so it shows the frame under it. */
.strip {
  --ld: calc(var(--H) * 1.4);                         /* the largest loupe whose skirt stays inside the margin at a strip's end */
  --lens: .539;                                       /* the photographed lens, as a share of the loupe's width */
  --mag: 1.3;
  --at: 0;
  --lx: calc(var(--gap) / 2 + var(--at) * (var(--fw) + var(--gap)) + var(--fw) / 2);
  --ly: calc(var(--rb) + var(--fh) / 2);
  --lr: calc(var(--ld) * var(--lens) / 2);
  --glide: .55s cubic-bezier(.3, .7, .2, 1);
}
/* No z-index, transform or filter on the loupe: any of them would isolate it, and the blend would lose the strip. */
.loupe { position: absolute; left: calc(var(--lx) - var(--ld) / 2); top: calc(var(--ly) - var(--ld) / 2); width: var(--ld); height: var(--ld);
  pointer-events: none; transition: left var(--glide); }
.loupe-barrel { position: absolute; inset: 0; }
.loupe-lens { position: absolute; inset: calc(var(--ld) * (1 - var(--lens)) / 2); border-radius: 50%; overflow: hidden; background: var(--field); isolation: isolate; }
.loupe::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: url(/assets/design/loupe.jpg) 0 0 / 100% 100% no-repeat; mix-blend-mode: multiply; }
.loupe-view {
  position: absolute; left: calc(var(--lr) - var(--lx)); top: calc(var(--lr) - var(--ly));
  display: flex; gap: var(--gap); padding: var(--rb) calc(var(--gap) / 2); width: max-content;
  transform-origin: var(--lx) var(--ly); transform: scale(var(--mag));
  transition: left var(--glide), transform-origin var(--glide);
}
@media (prefers-reduced-motion: reduce) { .loupe, .loupe-view, .reel-arrow, .reel-arrow span, .reel::before, .reel::after { transition: none; } }

@media (max-width: 699px) {
  .sheet { min-height: 0; grid-template-rows: auto auto auto; }
  .loupe { display: none; }  /* strips wrap on a phone; the loupe needs one row */
  .sheet-title, .sheet--stills .sheet-title { position: static; grid-row: auto; text-align: left; margin: 0; white-space: normal;
    font-size: var(--title-size); line-height: .78; color: var(--on-field); }  /* on a phone the name sits above the strip, as before */
  .reel { --H: calc(100cqw / 2.18); --label: 12px; }
  .sheet--stills .reel { margin-top: 0; }  /* the desktop lift would run the strip into STILLS on a phone */
  .strips { overflow: visible; margin: 24px 0 0; padding: 0 0 96px; }
  .reel-arrow { display: none; }
  .strip[data-stock="70mm"] { --fw: calc(100cqw / 2 - var(--gap)); }
  .strip { flex-wrap: wrap; row-gap: calc(var(--rb) * 2 + 96px); }
  .frame-label { font-size: 13px; }
}
