/* GLOBAL — one line. position:sticky is silently dead inside any ancestor whose
   overflow is not visible, and the theme sets overflow-x:hidden on html/body.
   clip stops sideways scrolling exactly as hidden does but creates no scroll
   container. Same fix already applied site-wide; repeated so this section still
   works if that is ever reverted. */
html, body { overflow-x: clip; }

#fh-film{
  /* White, not the old #FAF6F2. Decided 12 Sept: the cream was the only thing
     on the page using it, and it was the entire reason the join above read as
     a cut. Removing the difference removes the cut - no gradient needed to
     hide a boundary that no longer exists. The run-in on .fhf-lead below is
     kept anyway because it tracks this variable, so it stays correct if the
     ground ever changes again. */
  --fhf-bg:#FFFFFF;
  /* The ground the picture sits in.

     It used to be rgb(162,159,161) - the mean of the footage's own top row -
     on the theory that matching the film hides the picture's edges. Measured
     properly on 12 Sept, across ten frames, that theory does not hold: the
     film is lit from the right, so its LEFT column averages 117 and its RIGHT
     column 175, fifty-eight levels apart, and the top row swings 123 to 204.
     No flat colour can meet all of that, and 162 was already 45 levels off the
     left edge. So matching buys nothing and the colour is a free choice.

     Ruba, 12 Sept: the dark grey reads heavy. It went light and exactly
     neutral at 206 - the value before that carried a faint magenta cast
     (R and B above G).

     Ruba, 14 Sept: "remove the light yellow behind it", meaning that 206.
     It was never yellow. 206,206,206 is neutral to the byte, and the
     footage's own backdrop measures neutral-to-faintly-PINK across ten
     frames (R about 8 above G at mid-film), never yellow. What she was
     seeing is what a large flat grey does sitting under pure white -
     simultaneous contrast turns it warm and dirty. Real effect, and the
     only fix is to not have the band. So: white.

     The cost, shown to her and accepted: the picture's left edge now steps
     255 to 106 instead of 206 to 106, so the film's own edges read HARDER,
     not softer. Softening them is the edge feather, and the feather was
     turned down on 13 Sept. 250 was offered as the compromise that keeps a
     ramp you can only just find; White was chosen.

     Held as three channels so the gradients below can take an alpha off it;
     change this ONE line and the run-in, the title block, the strip above the
     picture and the tail all follow. */
  --fhf-field-rgb:255,255,255;
  --fhf-field:rgb(var(--fhf-field-rgb));
  /* Declared here, not on .fhf-stage. The sticky top bar is the stage's
     SIBLING and has to read the same value; scoped to the stage it resolved
     to nothing and the bar collapsed to zero height. */
  --fhf-hdr:calc(clamp(72px,5.4vw,92px) + 10px);
  --fhf-pink:#F2537A;
  --fhf-ink:#1E1E1E;
  /* The reveal. Written by the scroll handler; see .fhf-filmwrap below for
     what they mean and why they default to 1. */
  --fhf-in:1;
  --fhf-tin:1;
  --fhf-mut:#4A4A4A;
  position:relative;
  background:var(--fhf-bg);
  color:var(--fhf-ink);
  /* Inherit the theme's face so the section matches the rest of the site. */
  font-family:inherit;
  -webkit-font-smoothing:antialiased;
}
#fh-film *,#fh-film *::before,#fh-film *::after{box-sizing:border-box;}

/* LOCAL PREVIEW ONLY. WordPress always puts classes on <body>, so this can
   never apply on staging or live. */
body:not([class]) #fh-film{
  font-family:system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
}

/* ---- the strip above the pinned stage ---------------------------------
   The stage pins at --fhf-hdr, so there is always a band between the top of
   the window and the top of the picture. It has to be the same grey as the
   field or it reads as a stripe laid across the film.

   It was a sticky bar of its own until 12 Sept. That was the bug the client
   kept reporting as "the video is being revealed": a sticky element sits in
   normal flow until its own top reaches 0, so for the ~90px of scroll before
   that the bar was still coming down the page - and with z-index 2 it was
   lying over the top of the picture and uncovering it on the way. Measured at
   the seam: the bar read 87 -> 39 -> 10 -> 0 while the canvas never moved off
   89.

   The track already spans the entire pinned run, so the colour goes here. It
   is part of the page's own background: there is nothing to pin, nothing to
   slide, and nothing above the picture at any scroll position. */
#fh-film .fhf-track{position:relative;width:100%;background:var(--fhf-field);}

/* The section's own title, in the run-in above the film. Set to match the
   packages title exactly - same size, weight, tracking - and worded exactly
   as the section it replaces: "Our Collection kit", lowercase k. */
/* Air above, none below. The title belongs to the film, so it sits on it;
   the space that separates this section from the one before it all goes
   above the words. */
/* ---- the run-in ---------------------------------------------------------
   Ruba, 12 Sept: the change from the white section above into this one reads
   as a cut. It is a cut - two flat colours meeting on a straight line, white
   against #FAF6F2.

   The fix is to give the change somewhere to happen. The title block is the
   first thing inside #fh-film and it sits directly on the join, so the
   gradient runs across it: white at its top edge, where it meets the section
   above, arriving at the section's own cream by its bottom edge, where the
   pinned stage takes over and carries the same colour down. No line, because
   there is no longer a boundary - and no vh arithmetic, because the title
   block's own height is the distance.

   The lab page carries its own overrides for comparing this against the flat
   cream and against dropping the cream entirely; they are deliberately not in
   the shipped stylesheet. */
/* ---- the run-in, reaching UPWARD --------------------------------------
   The white-to-grey change happens ABOVE this section, not inside it.

   Why: the space a fade needs is the same space the film slides through. Put
   the ramp inside the section and the film starts lower and rides up as you
   scroll, which reads as the picture being revealed. Put the ramp above the
   section and the film section is one flat grey from its first pixel - the
   picture's edges have nothing to travel against, so there is nothing to see
   moving.

   The negative margin is the whole trick: the band is pulled up over the
   bottom of whatever section precedes this one, so it costs no height here
   and needs no cooperation from that section. On the homepage that is the
   cord blood / cord tissue block, whose last 120px is padding - the veil
   lands on empty space, and it is transparent at its top edge in any case.
   pointer-events:none so it can never intercept a click up there. */
/* LENGTH. 10vh (~78px) was not enough room for white to reach a mid grey:
   the change was soft but compressed, and it read as a soft edge rather than
   as a transition. 34vh gives it ~265px, which is roughly what the version
   Ruba picked out as the good one had. Only the first 10vh is pulled up over
   the section above, so it still lands inside that section's own bottom
   padding and never touches its last paragraph; the other 24vh is real space
   inside this section, and it is the air above the title.

   SHAPE. Two flat colours joined by a plain linear ramp show a Mach line at
   BOTH ends - the eye reads the sudden change in slope as an edge even when
   the change in value is a single level. The stops below are a smoothstep:
   the slope starts at nothing, peaks in the middle and returns to nothing, so
   neither end has a detectable start or finish.

   WHERE IT ENDS. At the top of the title block, not at the picture. The last
   stretch of a ramp that ran on past here would be what fills the strip above
   the pinned picture, and that strip would then get lighter as the page
   scrolled - the moving grey that was rejected four times. Ending it here
   means everything from the title down is one flat grey that cannot move. */
#fh-film .fhf-runin{
  /* 14vh of the ramp is pulled up into the section above and ~96px of it is
     real height here, so the ramp is long WITHOUT pushing the title down the
     page. 34vh of it standing in this section opened a 365px hole between the
     paragraph above and the title; Ruba's reference has 245px. This is 239px.
     The pulled-up part lands inside that section's bottom padding, and the
     ramp is at zero alpha for its first fifth, so nothing tints the paragraph
     sitting above it. */
  height:calc(14vh + 96px);
  margin-top:-14vh;
  position:relative;
  z-index:1;
  pointer-events:none;
  background:linear-gradient(180deg,
    rgba(var(--fhf-field-rgb),0)     0%,
    rgba(var(--fhf-field-rgb),.018) 12%,
    rgba(var(--fhf-field-rgb),.075) 24%,
    rgba(var(--fhf-field-rgb),.185) 36%,
    rgba(var(--fhf-field-rgb),.340) 48%,
    rgba(var(--fhf-field-rgb),.520) 60%,
    rgba(var(--fhf-field-rgb),.700) 72%,
    rgba(var(--fhf-field-rgb),.850) 82%,
    rgba(var(--fhf-field-rgb),.950) 91%,
    var(--fhf-field)      100%);
}
#fh-film .fhf-lead{
  display:flex;align-items:center;justify-content:center;
  /* Top: the last of the air above the words, most of which is the run-in.
     Bottom: the gap from the words to the top of the picture - about 39px at
     a 780px-tall window, which is what the reference has.

     Why the ramp is NOT allowed to run down through the words, however much
     better that looks standing still: the strip above the pinned picture only
     ever shows the lead's BOTTOM --fhf-hdr pixels, so anything still fading
     down there would make that strip lighten as the page scrolled. That is
     the moving grey that was rejected four times. Ending the ramp at the top
     of this block keeps the whole block one flat value at every width. */
  padding-block:clamp(20px,3vh,34px) clamp(30px,5vh,52px);
  padding-inline:clamp(20px,4vw,48px);
  background:var(--fhf-field);
}

/* No fade on the way out. Asked for directly, 12 Sept: white under the film,
   flat. The grey therefore stops on the film's own bottom edge, which is a
   hard edge in the footage anyway and has the pink progress rail sitting on
   it, so the change happens on a line the design already draws. This is only
   a white spacer now - the section below brings its own padding. */
#fh-film .fhf-tail{
  height:clamp(40px,6vh,72px);
  background:var(--fhf-bg);
}
/* Lab only: keeps the heading on white and starts the ramp beneath it. */
#fh-film .fhf-title{
  /* h2's default 0.83em margins were adding ~96px of their own on top of the
     padding above, which is most of the gap that was too big. */
  margin:0;
  font-weight:600;
  font-size:clamp(30px,4.4vw,58px);
  line-height:1.05;letter-spacing:-0.025em;
  color:var(--fhf-ink);
  text-align:center;text-wrap:balance;
}
/* #E83466 is the site's heading pink, the one on "What Are stem cells?".
   NOT --fhf-pink (#F2537A), which is this section's own accent for the
   leader lines and the progress rail - a heading is not a leader line. */
#fh-film .fhf-title i{font-style:normal;color:#E83466;}

#fh-film .fhf-stage{
  position:sticky;
  width:100%;
  overflow:hidden;
  background:var(--fhf-field);
  display:flex;flex-direction:column;

  /* The stage pins BELOW the header rather than under it.
     top:0 with padding was the first attempt: it kept the header off the
     top callout, but the padding is there the whole time, so it also opened
     a gap between the title and the picture on the way in.

     Pinning at the header's height instead costs nothing on the approach -
     the stage sits in normal flow, and the picture starts immediately under
     the title - and while pinned the header simply cannot reach it. The
     strip above the pinned stage shows #fh-film's own background, the same
     colour as the stage, so there is nothing to see there.

     The +10px is air, so the top callout is not kissing the header's
     underside. It costs nothing on the approach: `top` only applies once
     pinned, and the shorter height just makes the picture fractionally
     smaller rather than opening a gap.

     Safe for the scrub: the engine reads progress from the TRACK's position
     (-track.top, clamped to the frame total), never from the stage, so
     moving and shortening the stage does not shift a single frame. */
  top:var(--fhf-hdr);
  height:calc(100svh - var(--fhf-hdr));
  height:calc(100vh - var(--fhf-hdr));
}

#fh-film .fhf-filmwrap{
  position:relative;
  flex:1 1 auto;
  min-height:0;
  display:flex;align-items:center;justify-content:center;
}


#fh-film .fhf-canvas{
  display:block;
  max-width:100%;max-height:100%;
  width:auto;height:auto;
  object-fit:contain;
}

/* ---- the reveal ---------------------------------------------------------
   Ruba, 13 Sept: the picture and the words should arrive together, instead of
   the words standing on the grey waiting for the film to turn up underneath
   them.

   ONE NUMBER EACH, written from the scroll handler: --fhf-in for the picture,
   --fhf-tin for the words. Both default to 1, which is the arrived state, so
   if the script never runs - blocked, thrown, cached stale - the section is
   simply finished. Nothing here can leave anything invisible.

   WHAT DRIVES IT, and what does not. --fhf-in is how far the stage still has
   to fall before it pins. It is NOT the scrub: the whole movement is over
   before the first frame is scrubbed, so the two can never fight for the same
   pixels, and --fhf-in is exactly 1 - identity transform, full opacity - at
   the moment the stage pins.

   WHY THE TRANSFORM IS ON .fhf-filmwrap AND NOT ON THE CANVAS. The callout
   layer is the canvas's sibling inside the wrap. Put it on the canvas and the
   picture moves while the twenty labels stay where they were. */
#fh-film .fhf-filmwrap{
  transform:translateY(calc((1 - var(--fhf-in)) * 58px))
            scale(calc(.974 + .026 * var(--fhf-in)));
  opacity:calc(.15 + .85 * var(--fhf-in));
}
#fh-film .fhf-title{
  transform:translateY(calc((1 - var(--fhf-tin)) * 22px));
  opacity:var(--fhf-tin);
}
@media (prefers-reduced-motion:reduce){
  #fh-film .fhf-filmwrap,
  #fh-film .fhf-title{transform:none;opacity:1;}
}

/* ---- callout layer -----------------------------------------------------
   Positioned by JS to sit exactly on the drawn frame, so every coordinate in
   ANNOS is a fraction of the picture and survives any window size.          */
#fh-film .fhf-anno{
  position:absolute;
  left:0;top:0;width:0;height:0;
  pointer-events:none;
  z-index:2;
}
#fh-film .fhf-leaders{
  position:absolute;left:0;top:0;
  overflow:visible;
}
#fh-film .fhf-leaders path{
  fill:none;
  stroke:var(--fhf-pink);
  stroke-width:1.25;
  stroke-linecap:round;
  stroke-linejoin:round;
}
#fh-film .fhf-dot{
  position:absolute;
  width:7px;height:7px;
  margin:-3.5px 0 0 -3.5px;
  border-radius:50%;
  background:var(--fhf-pink);
  opacity:0;
}
/* The label sits on a plate. Ruba, 12 Sept: "the text is not clear and
   overlay the stuff".

   It used to be bare type with a white text-shadow halo behind it. A halo
   is a guess about what is underneath, and on this film the guess is wrong
   half the time: the left column lands on the white cardboard of the lid
   and on the white box body, where grey-on-white at 3px of blur is barely
   type at all. Measured on frame 645 - "Alcohol wipes", "Vacutainer needle
   and adaptor" and "Tempmate datalogger" all run over the lit box side.

   A plate removes the guess. Whatever the frame is doing underneath, the
   words are on a near-white card at a known contrast, and the picture stays
   readable through the blur. It also reads as a deliberate object on top of
   the photograph rather than as type lost in it, which is the other half of
   what she is objecting to.

   backdrop-filter is progressive: without it the plate is simply a little
   more opaque, which still reads. Nothing depends on it. */
#fh-film .fhf-lab{
  position:absolute;
  width:var(--fhf-w,220px);
  opacity:0;
  /* em, not px, and that is load-bearing. tools/place-film-callouts.py packs
     each column using label heights expressed as a FRACTION of the picture,
     which only holds if everything inside the label scales with the picture
     the way the type already does. A fixed 21px of vertical padding is 2.9%
     of a 720px-tall frame and 4.9% of a 427px one, so the packer would have
     been reserving the wrong amount at every size but the one it measured. */
  font-size:var(--fhf-fs,15px);
  padding:.6em .8em .66em;
  border-radius:.6em;
  border:1px solid rgba(30,30,30,.07);
  background:rgba(251,249,247,.93);
  box-shadow:0 10px 26px -14px rgba(30,30,30,.55);
}
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  #fh-film .fhf-lab{
    background:rgba(251,249,247,.86);
    -webkit-backdrop-filter:blur(7px) saturate(1.08);
    backdrop-filter:blur(7px) saturate(1.08);
  }
}
#fh-film .fhf-lab b{
  display:block;
  font-size:1em;
  font-weight:700;
  letter-spacing:.06em;
  text-transform:uppercase;
  line-height:1.3;
  color:var(--fhf-ink);
}
#fh-film .fhf-lab i{
  display:block;
  margin-top:.4em;
  font-style:normal;font-weight:400;
  font-size:var(--fhf-fs2,13px);
  line-height:1.45;
  color:var(--fhf-mut);
}
/* Both columns read left to right now. The left column used to be set ragged
   left so the words ended on the leader; on a plate the plate's edge does
   that job, and ragged-left body copy is simply harder to read. */

/* ---- phone: dots carry a number, the words go under the film ------------
   A label set that reads at 1400px is unreadable at 375px, so on a phone the
   picture keeps the pointer and the reading moves to one line below it, one
   item at a time, in step with the dot that just lit. */
#fh-film .fhf-num{
  position:absolute;
  width:20px;height:20px;
  margin:-10px 0 0 -10px;
  border-radius:50%;
  background:var(--fhf-pink);
  color:#fff;
  font-size:11px;font-weight:700;
  line-height:20px;text-align:center;
  opacity:0;
  display:none;
}
#fh-film .fhf-mcap{
  display:none;
  position:relative;
  flex:0 0 auto;
  height:104px;
  padding:0 6vw;
  text-align:center;
  align-items:center;justify-content:center;
  flex-direction:column;
}
#fh-film .fhf-mcap b{
  display:block;
  font-size:.82rem;font-weight:700;
  letter-spacing:.08em;text-transform:uppercase;
  color:var(--fhf-ink);
}
#fh-film .fhf-mcap i{
  display:block;margin-top:.4rem;
  font-style:normal;font-size:.86rem;line-height:1.5;
  color:var(--fhf-mut);
}
#fh-film.is-small .fhf-lab{display:none;}
#fh-film.is-small .fhf-dot{display:none;}
#fh-film.is-small .fhf-num{display:block;}
#fh-film.is-small .fhf-mcap{display:flex;}

/* ---- progress rail ----------------------------------------------------- */
#fh-film .fhf-rail{
  position:absolute;left:0;right:0;bottom:0;height:3px;
  background:rgba(43,43,43,.09);
}
#fh-film .fhf-railfill{
  height:100%;width:0;
  background:var(--fhf-pink);
}

/* ---- loader ------------------------------------------------------------ */
#fh-film .fhf-loader{
  position:absolute;inset:0;
  background:var(--fhf-bg);
  display:flex;flex-direction:column;
  align-items:center;justify-content:center;
  gap:1.1rem;
  transition:opacity .5s ease;
  z-index:3;
}
#fh-film .fhf-loadword{
  font-size:.8rem;font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;
  color:var(--fhf-mut);
}
#fh-film .fhf-loadbar{
  width:min(220px,42vw);height:3px;
  background:rgba(43,43,43,.10);
  overflow:hidden;
}
#fh-film .fhf-loadfill{
  height:100%;width:0;
  background:var(--fhf-pink);
  transition:width .18s linear;
}

/* ---- scroll cue -------------------------------------------------------- */
#fh-film .fhf-cue{
  position:absolute;left:50%;bottom:1.4rem;
  transform:translateX(-50%);
  font-size:.68rem;font-weight:600;
  letter-spacing:.2em;text-transform:uppercase;
  color:var(--fhf-mut);
  opacity:0;transition:opacity .6s ease;
  pointer-events:none;
}
