.hero-pin-region {
  position: relative;
  /* 3 scroll states (film, deck, studio): 150vh of scroll, about half a
     screen per state. */
  height: 250vh;
}

.hero-pin {
  position: sticky;
  top: 0;
  height: 100vh;
  /* Phones: size to the screen with the browser bars showing, so the
     box sits in the middle of what is actually visible. */
  height: 100svh;
  overflow: hidden;
  background: #000;
}

/* Frames are stacked. The new frame fades in over the old one, which
   stays fully visible underneath (.is-leaving) until the fade is done,
   so the change is a true crossfade with no dip to black. */
.hero-frame {
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: 0;
  transition: opacity 1s ease;
  pointer-events: none;
  background: #000;
}

.hero-frame.is-leaving { z-index: 1; opacity: 1; transition: none; }

.hero-frame.is-active { z-index: 2; opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .hero-frame { transition: none; }
}

.hero-frame--video {
  display: flex;
  align-items: center;
  justify-content: center;
}

.hero-media {
  position: relative;
  flex: none;
  width: min(62vh, 110vw);
  aspect-ratio: 1 / 1;
  background: #000;
  -webkit-mask-image:
    linear-gradient(to right, transparent, #000 11%, #000 89%, transparent),
    linear-gradient(to bottom, transparent, #000 18%, #000 82%, transparent);
  -webkit-mask-composite: source-in;
          mask-image:
    linear-gradient(to right, transparent, #000 11%, #000 89%, transparent),
    linear-gradient(to bottom, transparent, #000 18%, #000 82%, transparent);
          mask-composite: intersect;
}

.hero-video,
.hero-media__still {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.hero-media__still {
  transition: opacity 0.3s ease;
}

.hero-media__still.is-hidden { opacity: 0; }

/* 2-4. Full-bleed stills. Each fills the screen and is cropped to
   fit; --pos on an img moves the crop (default centre). Within a
   state the photos are stacked and crossfade on a timer: the current
   one fades in over the previous, which stays visible underneath
   (.is-leaving) until the fade is done. */
.hero-full {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--pos, 50% 50%);
  opacity: 0;
  transition: opacity 0.9s ease;
}

/* Shown whole rather than cropped: the image sits on its own background
   colour (--fill), which matches the photo's backdrop so the frame still
   reads as full bleed. */
.hero-full--fit {
  object-fit: contain;
  background: var(--fill, #f6f6f6);
}

/* Contact sheets: kept clear of the header and the buy panel. */
.hero-full--sheet {
  box-sizing: border-box;
  padding: 84px 24px 32px;
}

@media (max-width: 640px) {
  /* Phones: a little bigger than the screen width, so about five of the
     six columns show, with the outer two cut through the middle; top and
     bottom rows run under the header and buy panel. */
  .hero-full--sheet {
    padding: 0;
    object-fit: cover;
    left: -10vw;
    right: auto;
    width: 120vw;
  }
}

/* Landscape screens (wider than tall) get the wide 3 x 11 sheets (the <source> in
   each <picture> uses the same media query). They fill the full width
   between the header and the buy panel and are cropped at the left and
   right edges, so the outer cards are always cut by the edge of the
   browser, never by white. */
@media (min-aspect-ratio: 1/1) {
  .hero-full--sheet {
    padding: 84px 0 32px;
    object-fit: cover;
    left: 0;
    right: auto;
    width: 100%;
  }
}

.hero-full.is-leaving { z-index: 1; opacity: 1; transition: none; }

.hero-full.is-current { z-index: 2; opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .hero-full { transition: none; }
}

/* Progress rail, right edge: four segments, one per scroll state
   (box film, deck, cards, studio), modelled on the archive rail. The
   track is a mid-grey line that shows on both the black film and the
   white photos; the progress fills in the site yellow. */
.hero-rail {
  position: absolute;
  right: clamp(20px, 3vw, 40px);
  top: 50%;
  transform: translateY(-50%);
  z-index: 5;
  height: 46vh;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.hero-rail__seg {
  position: relative;
  flex: 1;
  width: 20px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

.hero-rail__seg::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 1px;
  margin-left: -0.5px;
  background: rgba(128, 128, 128, 0.7);
}

.hero-rail__fill {
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 3px;
  margin-left: -1.5px;
  background: var(--accent-2);
  transform: scaleY(0);
  transform-origin: top;
}

.hero-rail__seg:focus-visible { outline: 1px solid var(--accent-2); outline-offset: 2px; }

@media (max-width: 640px) {
  .hero-rail {
    right: 10px;
    height: 26vh;
    gap: 6px;
  }
}

.buy-track {
  position: relative;
}

.buy-widget {
  position: sticky;
  bottom: clamp(24px, 6vw, 56px);
  z-index: 450;
  margin-left: auto;
  margin-right: clamp(16px, 4vw, 40px);
  margin-bottom: clamp(28px, 5vw, 48px);
  width: max-content;
  max-width: calc(100vw - clamp(32px, 8vw, 80px));
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  font-family: var(--font);
  font-size: 12px;
  letter-spacing: 0.02em;
  text-align: right;
}

/* While it floats over the images, the buy widget is a frosted card
   matching the solid header (same tint, blur and hairline border).
   Docked beside the product copy it drops the card and sits on the
   black. */
.buy-widget:not(.is-docked) {
  padding: 16px 18px;
  background: rgba(0, 0, 0, 0.55);
  -webkit-backdrop-filter: blur(10px);
          backdrop-filter: blur(10px);
  border: 1px solid var(--hairline);
}

.buy-widget.is-docked {
  position: static;
  margin: 0;
  width: auto;
  max-width: none;
  flex: 0 0 auto;
}

.product-panel {
  background: #000;
  border-top: 1px solid var(--hairline);
  padding: clamp(48px, 8vw, 96px) clamp(16px, 4vw, 40px);
}

.product-row {
  max-width: 1040px;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-end;
  gap: clamp(24px, 5vw, 64px);
}

.product-copy {
  flex: 1 1 320px;
}

.buy-widget-dock {
  display: contents;
}

@media (max-width: 640px) {
  .product-row {
    flex-direction: column;
    align-items: flex-start;
    gap: 28px;
  }
  
  .product-copy {
    flex: none;
  }
  .buy-widget.is-docked {
    
    order: -1;
    align-self: stretch;
    width: 100%;
    align-items: center;
    text-align: center;
  }
  
  .product-panel {
    padding-bottom: 24px;
  }
}

.product-description {
  font-size: 15px;
  line-height: 1.65;
  color: var(--fg);
  max-width: 46ch;
  margin: 0 0 10px;
}

.product-description:last-child {
  margin-bottom: 0;
}

.buy-line1 { margin: 0; }

.buy-line1 .sep { opacity: 0.5; color: var(--fg); }

.buy-title {
  font-weight: 700;
  color: var(--fg);
 
}

.buy-price {
  font-weight: 400;
  color: var(--fg);
 
}


.buy-button {
  font-family: var(--font);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fg);
  background: none;
  border: 1px solid var(--fg);
  padding: 8px 16px;
  margin-top: 26px;
  cursor: pointer;
  transition: opacity 0.15s ease;
 
}

.buy-button:hover { opacity: 0.6; }

.buy-button:focus-visible { outline: 2px solid var(--fg); outline-offset: 4px; }

.buy-button.is-soldout {
  opacity: 0.4;
  cursor: not-allowed;
}

.buy-button.is-soldout:hover { opacity: 0.4; }

/* Phones held upright: lift the box film and the open-box photo a
   little, so they sit centred between the header and the buy panel
   rather than the full screen. */
@media (max-width: 640px) and (orientation: portrait) {
  .hero-media { transform: translateY(-4svh); }
  .hero-full--fit:not(.hero-full--sheet) { --pos: 50% 38%; }
}
