/* ---------- FilmPack: dark-first editorial ---------- */
.phero-film { padding: clamp(6.5rem, 11vh, 8.5rem) 0 clamp(2rem, 4vh, 3rem); }
.film-hero-win { max-width: 1120px; margin: 0 auto; box-shadow: var(--shadow-float); }
.triptych { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 3px; background: var(--border); height: clamp(260px, 40vw, 430px); }
.triptych img { width: 100%; height: 100%; object-fit: cover; display: block; filter: saturate(1.02); }
@media (max-width: 640px) { .triptych { grid-template-columns: 1fr 1fr; } .triptych figure:nth-child(3) { display: none; } }
.triptych figure { margin: 0; overflow: hidden; background: #101114; }
.film-hero-body { padding: clamp(2.2rem, 5vw, 3.4rem) clamp(1.5rem, 6vw, 4.5rem) clamp(2rem, 4vw, 2.8rem); text-align: center; }

/* ---------- the lab: hold to see the before ----------

   LAYOUT CONTRACT (rewritten 2026-08-03 after four failed patches).
   The window is a GRID of independent rows:

     .lab-win
       ├── .win-bar
       ├── .lab-meta        name + subtitle
       ├── .lab-stage       the photograph — a paint-contained row
       ├── .strip-rail      the film strip — a RESERVED, FIXED height
       └── .strip-caption

   Three rules make every film render identically:

   1. THE STRIP'S HEIGHT IS A FUNCTION OF ITSELF, never of the photo:
      `--frame-h + 2 * --strip-pad-y`, set as an explicit height. It is the
      same number for a portrait, a landscape or a square panel.
   2. THE STAGE IS A PAINT BOUNDARY: `contain: paint` + its own stacking
      context below the strip's. Even if a descendant escapes its box, it
      cannot be painted over the strip. This is what actually broke — an
      oversized positioned <img> whose `overflow: hidden` clip iOS dropped,
      landing on top of the later, static strip.
   3. NO PERCENTAGE SIZE EVER RESOLVES AGAINST AN `auto` OR SHRINK-TO-FIT
      ANCESTOR. The figure is a plain full-width block, so the viewport's
      `max-width: 100%` has a definite base; the viewport takes its size from
      ONE definite axis plus `aspect-ratio` (height on desktop, width on
      mobile) and never from `height: 100%` of an auto-height parent. Both of
      the earlier collapse bugs were that percentage chain. */
.lab-win {
  max-width: 1120px; margin: 0 auto;
  display: grid; grid-template-columns: minmax(0, 1fr);
  --frame-h: 62px;
  --strip-pad-y: 1.9rem;
  --photo-h: clamp(260px, 56vh, 620px);
}
.lab-stage {
  position: relative; z-index: 0; background: #0B0C0F;
  contain: paint; isolation: isolate;
}
/* a plain full-width block: it exists to be a definite containing block and
   to carry the press-and-hold interaction, nothing else */
.lab-figure {
  position: relative; display: block; width: 100%; margin: 0;
  cursor: pointer; user-select: none; -webkit-user-select: none;
  touch-action: manipulation;
}
/* the viewport shows ONE cell of a stitched pair: after | before.
   Moving the background by one panel reveals the unedited file.

   ⚠️ THE PHOTOGRAPH IS A BACKGROUND, NOT AN <img> — this is the fix for the
   bug that took four attempts (2026-08-03). It used to be an absolutely
   positioned <img> two to four times the viewport's width, held in by
   `overflow: hidden`. On iOS that clip is not honored, and because the image
   is *positioned* it then paints above the later, static film strip — so the
   photo ran on down the page and swallowed the strip's black band whole
   (Cesar's screenshot: the boat and the water continue seamlessly behind the
   frames). A background-image cannot leave its box. Panel k of n:
   `background-size: n*100% auto`, `background-position-x: 100*k/(n-1)%`. */
.lab-viewport {
  display: block; position: relative; overflow: hidden;
  margin-inline: auto;                    /* center the photo in its row */
  height: var(--photo-h);                 /* definite; width follows the ratio */
  aspect-ratio: var(--panel-ar, 3 / 4);   /* per film, set from JS */
  max-width: 100%;                        /* base is the full-width figure */
  background: #0B0C0F;
}
/* two stacked layers: the 670px thumb shows instantly, the full-size file
   fades in over it — so a big strip never leaves the viewer blank */
.lab-layer {
  position: absolute; inset: 0; pointer-events: none;
  background-repeat: no-repeat;
  background-size: var(--cell-size, 100%) auto;
  background-position: var(--cell-x, 0%) 50%;
}
.lab-full { opacity: 0; transition: opacity .28s ease; }
.lab-full.ready { opacity: 1; }
.lab-tag {
  position: absolute; top: .9rem; left: .9rem;
  font-size: .64rem; letter-spacing: .1em; text-transform: uppercase;
  color: rgba(255,255,255,.9); background: rgba(10,11,14,.6); border-radius: 7px; padding: .32rem .6rem;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  transition: background-color .2s ease, color .2s ease;
}
.lab-figure.before .lab-tag { background: rgba(214,43,51,.85); color: #fff; }
.lab-hint {
  position: absolute; bottom: .9rem; left: 50%; transform: translateX(-50%);
  font-size: .66rem; color: rgba(255,255,255,.75); background: rgba(10,11,14,.55);
  border-radius: 999px; padding: .4rem .95rem; white-space: nowrap;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.lab-hint b { color: #fff; font-weight: 600; }

/* mobile: drive the photo from WIDTH (height follows the aspect ratio) so it
   can never overflow or collapse, and stop the hint running off the screen —
   there is no backslash key on a phone, so that half of the line goes away */
@media (max-width: 760px) {
  /* flip the definite axis: width is known, height follows the ratio */
  .lab-viewport { width: 100%; height: auto; max-height: 78vh; }
  .lab-hint { white-space: normal; max-width: calc(100% - 1.8rem); text-align: center; line-height: 1.45; }
  .hint-desk { display: none; }
  /* the active frame is centered by centreFrame() in the JS — `margin: auto`
     inside a horizontal scroller would put the left overflow out of reach */
  .strip-rail { scroll-snap-type: x proximity; scroll-padding: 0 1.2rem; }
  .frame { scroll-snap-align: center; }
}

/* ---------- film strip explorer ----------
   The strip is a REEL: the black band with its sprocket rows must bleed from
   edge to edge of the window at every scroll position. It used to be a
   rounded card, auto-centered inside a rail that had its own side padding — so
   at either end of the scroll (films 01–02, films 08–09) the card's corner and
   the rail's padding showed as a gap, and the band stopped short of the
   window. Fixes: no side padding on the rail, no radius and no auto margin on
   the strip, and `min-width: 100%` so on a wide screen — where the nine frames
   are narrower than the window — the band still spans the full width with the
   frames centered inside it. */
/* ⚠️ THE FILM ITSELF IS PAINTED ON THE RAIL, NOT ON THE SCROLLING STRIP.
   It used to live on `.strip`, the max-content child that actually scrolls —
   and on iOS that child's background stops being painted past the initially
   visible area, so scrolling the strip revealed the PHOTOGRAPH behind it
   (Cesar's screenshots: films 02 and 09 show frames floating on the picture,
   film 05 in the middle of the scroll looks perfect). The rail's own box never
   moves, so its background always paints across the full visible width. Do not
   move the sprockets back onto `.strip`. */
.strip-rail {
  /* RESERVED HEIGHT — derived from the frames it holds, never from the photo.
     Identical for every film. z-index puts it above the stage's stacking
     context, so nothing from the photo row can ever paint over it. */
  position: relative; z-index: 1;
  box-sizing: border-box;
  height: calc(var(--frame-h) + var(--strip-pad-y) * 2);
  padding: var(--strip-pad-y) 0;
  border-top: 1px solid rgba(255,255,255,.06);
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none; scroll-behavior: smooth;
  background:
    repeating-linear-gradient(90deg, transparent 0 7px, rgba(255,255,255,.14) 7px 12px, transparent 12px 19px) 0 11px / 19px 6px repeat-x,
    repeating-linear-gradient(90deg, transparent 0 7px, rgba(255,255,255,.14) 7px 12px, transparent 12px 19px) 0 calc(100% - 17px) / 19px 6px repeat-x,
    #0B0C0F;
}
.strip {
  display: flex; gap: .55rem; width: max-content; min-width: 100%;
  justify-content: center; margin: 0;
  padding: 0 .8rem;
}
/* ⚠️ Each frame holds an image 2–4× its own width (the whole strip file) and
   crops it. `overflow: hidden` alone is not enough on iOS: with a border
   radius and a transformed child, Safari has long failed to clip, letting a
   thumbnail spill across the whole rail — which reads exactly as "you can see
   the picture behind the strip". Three belts: `contain: paint` (a hard,
   standards-based clip), `isolation` (own stacking context), and the classic
   -webkit-mask radial-gradient hack for older iOS. The horizontal panel offset
   also moved off `transform` and onto `left` (see the JS) so the crop is plain
   layout, not compositing. */
.frame {
  appearance: none; border: 2px solid transparent; padding: 0; cursor: pointer;
  width: 84px; height: var(--frame-h); border-radius: 4px; position: relative;
  overflow: hidden; contain: paint; isolation: isolate;
  -webkit-mask-image: -webkit-radial-gradient(white, black);
  background: #0B0C0F; flex: none;
  transition: border-color .2s ease, transform .2s ease;
}
/* ⚠️ The thumbnail is a BACKGROUND, not an <img>, and that is the whole point.
   Each frame shows one panel of a strip file 2–4× its own width. As an <img>
   that meant an element overflowing its parent by ~80px sideways and ~25px
   top and bottom, relying on `overflow: hidden` to hide it — and on iOS the
   clip is lost when the rail scrolls and the frames get composited onto new
   layers. That is why the strip looked right where it first painted (film 05)
   and showed photographs across the black band at the ends of the scroll
   (films 02, 09). A background-image cannot overflow its box, so there is
   nothing left to clip and nothing left to go wrong.
   Panel k of n: `background-size: n*100% auto` (one panel == one frame width,
   height follows so a landscape panel still fills) and background-position-x
   = 100*k/(n-1) %, which is how percentage positioning maps onto the
   overflow. */
.frame-shot {
  position: absolute; inset: 0;
  background-repeat: no-repeat; background-position-y: 50%;
}
.frame:hover { transform: translateY(-2px); }
.frame.active { border-color: var(--accent); }
.frame-num {
  position: absolute; bottom: 3px; right: 4px;
  font-size: .52rem; color: rgba(255,255,255,.85); background: rgba(10,11,14,.65);
  border-radius: 4px; padding: .12rem .3rem; letter-spacing: .05em;
}
.strip-rail::-webkit-scrollbar { display: none; }

/* on-device diagnostic overlay — inert until triple-tapped, touches no layout */
#aug-diag {
  position: fixed; inset: 0; z-index: 9999; overflow: auto;
  background: rgba(6,7,9,.97); color: #8FE8C0; padding: 12px 12px 40px;
  -webkit-overflow-scrolling: touch;
}
#aug-diag pre { margin: 0; font: 10px/1.35 ui-monospace, Menlo, Consolas, monospace; white-space: pre-wrap; word-break: break-word; }
#aug-diag button {
  position: sticky; top: 0; float: right; appearance: none; cursor: pointer;
  background: #D62B33; color: #fff; border: 0; border-radius: 6px;
  padding: .4rem .7rem; font: 600 11px/1 ui-monospace, monospace; letter-spacing: .08em;
}
.strip-caption { text-align: center; font-size: .66rem; color: var(--text-faint); padding: .8rem 1rem 1rem; }
.strip-caption b { color: var(--text-muted); font-weight: 600; }
.lab-meta { display: flex; align-items: baseline; justify-content: center; gap: 1rem; padding: 1rem 1.2rem .2rem; flex-wrap: wrap; }
.lab-name { font-size: clamp(1.4rem, 3vw, 2rem); }
.lab-sub { font-size: .74rem; color: var(--text-faint); }

/* ---------- workflow ---------- */
.flow-win { max-width: 1120px; margin: 0 auto; }
.flow { display: grid; grid-template-columns: repeat(5, 1fr); gap: 0; }
@media (max-width: 800px) { .flow { grid-template-columns: 1fr; } }
.flow-step { position: relative; padding: 1.6rem 1.1rem 1.5rem; text-align: center; border-right: 1px solid var(--border); transition: background-color .4s ease; }
.flow-step:last-child { border-right: 0; }
@media (max-width: 800px) { .flow-step { border-right: 0; border-bottom: 1px solid var(--border); } .flow-step:last-child { border-bottom: 0; } }
.flow-step.lit { background: var(--accent-soft); }
.flow-num { font-size: .62rem; color: var(--text-faint); letter-spacing: .1em; }
.flow-title { font-size: .9rem; font-weight: 650; margin: .35rem 0 .4rem; color: var(--text); }
.flow-desc { font-size: .72rem; color: var(--text-muted); line-height: 1.5; }
.flow-thumb-wrap { padding: 1.6rem 1.2rem .4rem; display: flex; justify-content: center; }
.flow-thumb { width: min(430px, 82vw); border-radius: 12px; overflow: hidden; border: 1px solid var(--border); }
.flow-thumb img { width: 100%; display: block; transition: filter 1s ease; }
.flow-thumb.f0 img { filter: saturate(.3) contrast(.8) brightness(1.15) blur(1px); }
.flow-thumb.f1 img { filter: saturate(.42) contrast(.84) brightness(1.12); }
.flow-thumb.f2 img { filter: saturate(1.04) contrast(1.05); }
.flow-thumb.f3 img { filter: saturate(1.08) contrast(1.08) brightness(1.02); }
.flow-thumb.f4 img { filter: saturate(1.08) contrast(1.08) brightness(1.02); }

/* ---------- installation ---------- */
.inst-win { max-width: 900px; margin: 0 auto; }
.inst { display: flex; flex-direction: column; }
.inst-step { display: flex; align-items: center; gap: 1.1rem; padding: 1.15rem 1.5rem; border-bottom: 1px solid var(--border); opacity: .45; transition: opacity .5s ease, background-color .5s ease; }
.inst-step:last-child { border-bottom: 0; }
.inst-step.lit { opacity: 1; background: var(--accent-soft); }
.inst-icon { flex: none; width: 44px; height: 44px; border-radius: 12px; border: 1px solid var(--border-strong); background: var(--surface-2); display: flex; align-items: center; justify-content: center; color: var(--accent); }
.inst-icon svg { width: 20px; height: 20px; }
.inst-text { font-size: .92rem; color: var(--text); font-weight: 550; }
.inst-text small { display: block; font-weight: 400; font-size: .74rem; color: var(--text-muted); margin-top: .2rem; line-height: 1.5; }
.inst-num { margin-left: auto; font-size: .66rem; color: var(--text-faint); }
