/* Task 7B — the composite.

   The room plate is a still image, and the product capture is pinned onto the
   laptop's own screen with a matrix3d homography, then travels to the centre
   of the frame and straightens as scroll progress runs 0 -> 1. That progress
   is a plain JS variable computed each frame in js/cinematic.js — not a CSS
   custom property; nothing here sets or reads one named --p. The laptop in
   the plate supplies the bezel, so there is no drawn bezel here any more: a
   rectangle stroked over a keystoned capture lines up with nothing.

   EVERYTHING below is inert until js/cinematic.js adds .cin--live, and that is
   the whole degradation story:

     - script absent  -> .cin is an ordinary block, .cin__screen is an ordinary
                         centred image, and no plate is ever requested (the
                         plate elements are empty divs with no background).
     - this file absent -> the script sees no --cin-css sentinel and does
                         nothing at all, so the same resting page is served.
     - reduced motion -> the script never adds .cin--live at all, so .cin
                         stays the plain resting block above and the capture
                         is the ordinary centred image. The @media block below
                         targets .cin--live defensively, in case that class
                         were ever present under reduced motion; in the
                         shipped flow it never is, so that block does not fire.

   No rule here sets any element's resting state to hidden. */

.cin {
  position: relative;
  /* Task 12: was clamp(2rem, 5vw, 3rem). The act now follows a title card and
     a short copy block inside the same dark band, so a band's worth of air
     above the plate read as a gap rather than as separation. */
  margin-top: clamp(0.75rem, 2vw, 1.25rem);
  /* The sentinel js/cinematic.js reads before it touches anything. A page
     served without this stylesheet must not get a half-applied effect. */
  --cin-css: 1;
}
.cin__frame { display: grid; place-items: center; }
.cin__screen {
  width: min(78vw, 1100px);
  height: auto;
  border-radius: 0.5rem;
  box-shadow: 0 30px 80px rgb(0 0 0 / 0.45);
}

/* ---- live: the pinned act ------------------------------------------------ */
/* Task 12: 320vh was sized for an act that opened the page, where a long run-up
   was the reader's introduction to it. With a title card in front, the reader
   arrives already inside the room, and 66vh of that was scroll spent on a
   picture that had not started moving. See the data-travel note in
   js/cinematic.js for the matching change to the hold.
   280 rather than 240: the room now has to stay up until the capture is
   frontal and then dissolve on its own, which is 47vh of scroll at the end
   that used to overlap the travel. Still 40vh shorter than it was, with 52vh
   of dead opening removed from the front of it. */
.cin--live { height: 300vh; }
.cin--live .cin__frame {
  position: sticky;
  top: 0;
  height: 100vh;
  /* Full bleed to the section's padding edge without 100vw, which would
     include the scrollbar and put the page into horizontal overflow. */
  width: calc(100% + 2 * var(--gutter));
  margin-left: calc(-1 * var(--gutter));
}
.cin--live .cin__plate {
  position: absolute;
  inset: 0;
  background-position: 50% 50%;
  background-size: cover;
  background-repeat: no-repeat;
  opacity: 0;
}
.cin--live .cin__screen {
  position: absolute;
  left: 0;
  top: 0;
  transform-origin: 0 0;
}
/* ---- the composed slide -------------------------------------------------- */
/* Owner storyboard, Task 13 fix round: the #cost heading is not a caption
   hovering above the shot — it is the TOP OF THE SAME SURFACE. A dark panel
   carrying the sentence sits flush on the capture and the two keystone
   together as one plane: edge-on early in the act, frontal and large at the
   end.

   Nothing here positions it. js/cinematic.js builds this element, sets its
   width to the capture's own width, and writes ONE transform: the capture's
   own homography, then a translate of exactly this panel's height, so its
   bottom edge lands on the capture's top edge in the plane's own
   coordinates. Same matrix, same quad, same frame — they cannot drift,
   because there is only one of them.

   The element does not exist unless that script ran, so the resting page has
   nothing to hide and nothing to undo, and the real <h2> never leaves
   #cost's normal flow at any point.

   Illegible while edge-on is the design, exactly as the capture is
   unreadable when it is a sliver of a laptop screen. What has to hold is the
   frontal state, where the transform is the identity and the type is at full
   size — measured there and through the last third of the ride. */
.cin__title {
  box-sizing: border-box;
  /* Vertical padding only. At the end of the handoff below, this panel's
     sentence has to land on the real <h2> in #cost, and that <h2> starts at
     its block's own left edge — which is the capture's left edge, because
     #cost .inner and .cin__screen are laid out at the same width. Horizontal
     padding here would inset the sentence by that amount and the swap would
     read as a sideways jump. Vertical padding costs nothing: it only shifts
     the panel up the plane, and js/cinematic.js measures it (titlePadTop)
     rather than assuming it is zero. */
  padding: clamp(0.5rem, 1.1vw, 0.85rem) 0;
  background: var(--surface-capture);
  color: var(--on-dark);
  font-size: var(--cost-heading-size);
  font-weight: 700;
  line-height: 1.15;
  /* Deliberately NOT tightened. page.css:32 gives h1/h2/h3 no letter-spacing,
     so the -0.01em this used to carry set the panel's sentence a few pixels
     narrower than the same sentence in #cost — invisible on its own, and a
     visible crawl at the moment the two swap. */
  /* The same balance page.css:32 gives every h1/h2/h3. Without it the panel
     broke the sentence one word before the <h2> in #cost does, and the slide
     and the block are meant to read as the same heading arriving twice, not
     as two different settings of it. */
  text-wrap: balance;
  border-radius: 0.5rem 0.5rem 0 0;
}
.cin--live .cin__title {
  position: absolute;
  left: 0;
  top: 0;
  transform-origin: 0 0;
}
/* The panel carries the slide's top corners while it is live, so the capture
   carries only the bottom two and the pair reads as one card rather than two
   stacked rectangles. Without .cin--live the capture keeps its own full
   radius, which is the resting page. */
.cin--live .cin__screen { border-radius: 0 0 0.5rem 0.5rem; }

/* ---- the handoff: the storyboard's fourth frame --------------------------
   "The heading stays, the capture gives way, and the four body paragraphs run
   beneath it on the same dark navy."

   Sticky releases the frame the moment .cin's bottom reaches the viewport
   bottom, which would carry the heading off the top of the screen while
   #cost's own copy is still a screenful below — the reader would see the
   sentence twice, in two places, with a gap between them. Instead
   js/cinematic.js pins the whole frame at the same top: 0 it was already
   sticky at, so nothing moves at the swap-over, and holds it there while
   #cost scrolls up underneath. The capture dissolves in place; the paragraphs
   arrive in the space it leaves; the sentence does not move at all.

   left, width and margin-left are written inline by the script from the box
   the frame measured one frame earlier, because `width: calc(100% + 2 *
   var(--gutter))` resolves against the viewport once the element is fixed
   rather than against .cin's content box, and the plate would jump sideways.

   The frame is a stacking context either way (sticky and fixed both make
   one), and it is a positioned element while #cost's band and copy are not,
   so the held frame paints above them without a z-index. Nothing here is a
   resting state: without the script the class is never added. */
.cin--live .cin__frame--held { position: fixed; top: 0; }

/* .cin--active is toggled by js/cinematic.js only while the pinned frame is
   near the viewport, so the compositing hint does not sit on the element for
   the rest of the page's life once the act is behind the reader. */
.cin--active .cin__screen,
.cin--active .cin__title { will-change: transform; }

/* ---- degradation --------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .cin--live { height: auto; }
  .cin--live .cin__frame { position: static; height: auto; width: 100%; margin-left: 0; }
  .cin--live .cin__plate { display: none; }
  .cin--live .cin__screen { position: static; transform: none; border-radius: 0.5rem; }
  /* Defensive, the same way the plate rule above is: js/cinematic.js never
     runs under reduced motion, so this panel is never built and .cin--live is
     never added. If either were ever true, the slide would put a second copy
     of a sentence already on the page in #cost. */
  .cin--live .cin__title { display: none; }
}
@media (max-width: 63.99rem) {
  .cin--live { height: 230vh; }
  .cin__screen { width: 92vw; }
  /* A 16:9 plate covered into a tall narrow viewport is cropped so hard that
     the laptop leaves the frame — at 375x812 only the right sliver of the
     screen is still on screen, and a capture pinned to it is pinned to
     something the visitor cannot see. Letterboxing the plate to a band keeps
     the whole desk in view. js/cinematic.js measures the plate's own box, so
     this needs no matching change there. */
  .cin--live .cin__plate { inset: 27vh 0; }
}
