/* ==========================================================================
   Scroll Photo Band 1.0 — a band of prints that slides into place on scroll
   --------------------------------------------------------------------------
   Plain CSS. No framework, no build step, no external request. Fonts and
   photographs live next to this file.

   The section pins itself while five prints travel in from the right, one
   after the other, and settle at a slight angle. Without JavaScript the
   prints are simply already in place, so the section reads the same — it just
   does not move.

   PROPORTIONS
   Every metric derives from --spb-font-size, the size of the body copy, and
   is written as calc(ratio * var(--spb-font-size)) rather than in em: a
   custom property written in em resolves against whoever reads it, and the
   title reads at 3.75x. Measured on manafi.webflow.io at 1915 px:

     body copy       1                (the reference: 16 px)
     title          3.75    em        60 px, line-height 116%
     print         21.875   em        350 px wide, ratio 450 / 433
     band gap       0                 prints touch, five of them, centred
     section pad    8.125   em        130 px top and bottom
     page gutter    7.5     em        120 px
     header margin  3.75    em        60 px below the title block
     band margin    4.375   em        70 px below the prints
     copy width    36       em        576 px
     button pad     0.9375 x 1.5 em   15 px / 24 px, 2 px border

   The travel distances are the one exception, and they are viewport-relative
   in the original too: the prints start 50, 55, 60, 65 and 70 vw to the right.

   Order of this file:
     1. Fonts
     2. Design tokens and scale steps
     3. Rail, sticky stage, background
     4. Header: eyebrow and title
     5. The band of prints
     6. Copy and button
     7. Scroll-driven motion
     8. Entrance animation
     9. Fallbacks: no JavaScript, reduced motion, forced colours

   Naming follows BEM: .scroll-photo-band, .scroll-photo-band__part.
   ========================================================================== */

/* ==========================================================================
   1. Fonts

   Subset as served to the reference site, so the metrics match glyph for
   glyph. Both families are SIL Open Font Licence.
   ========================================================================== */

@font-face {
  font-family: "Bricolage Grotesque SPB";
  src: url("BricolageGrotesque-variable-latin.woff2") format("woff2");
  font-weight: 200 800;
  font-stretch: 75% 100%;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Bricolage Grotesque SPB";
  src: url("BricolageGrotesque-variable-latin-ext.woff2") format("woff2");
  font-weight: 200 800;
  font-stretch: 75% 100%;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Instrument Serif SPB";
  src: url("InstrumentSerif-400-latin.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Instrument Serif SPB";
  src: url("InstrumentSerif-400-latin-ext.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* The reference site never loads a real italic: its <em> is a slanted copy of
   the roman, synthesised by the browser, and its emphasis runs 18 px wider
   than it should at 60 px. The genuine italic ships here instead; the
   original behaviour is one attribute away, data-italic="synthetic", and the
   family below is what makes it reachable — same two files, no italic face,
   so the browser has to lean the roman.

   The family is only ever named by that attribute. */
@font-face {
  font-family: "Instrument Serif Roman SPB";
  src: url("InstrumentSerif-400-latin.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Instrument Serif Roman SPB";
  src: url("InstrumentSerif-400-latin-ext.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}


@font-face {
  font-family: "Instrument Serif SPB";
  src: url("InstrumentSerif-400italic-latin.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Instrument Serif SPB";
  src: url("InstrumentSerif-400italic-latin-ext.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ==========================================================================
   2. Design tokens and scale steps
   --------------------------------------------------------------------------
   Four properties are registered, and that is not decoration:
   getPropertyValue on a plain custom property hands back the string that was
   written — "50vw" — whereas one registered as a <length> comes back computed,
   in pixels. The script needs the number, and this is what saves it from
   parsing CSS by hand.
   ========================================================================== */

@property --spb-start {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

@property --spb-angle {
  syntax: "<angle>";
  inherits: true;
  initial-value: 0deg;
}

@property --spb-course {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

@property --spb-hold {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

.scroll-photo-band {
  /* The one number that rescales everything. */
  --spb-font-size: 1rem;

  /* Scale. These ten ratios are the only values that change with the viewport
     — nine settings, the button's two paddings counting as one — and they
     change exactly where the reference changes them. */
  --spb-title-ratio: 3.125; /* 50 px at 16 px body copy */
  --spb-print-ratio: 18.75; /* 300 px */
  --spb-section-ratio: 6.25; /* 100 px of padding above and below */
  --spb-gutter-ratio: 3.75; /* 60 px of page gutter */
  --spb-band-gap-ratio: 3.125; /* 50 px under the band */
  --spb-actions-ratio: 1.875; /* 30 px above the button */
  --spb-button-pad-y-ratio: 0.625; /* 10 px */
  --spb-button-pad-x-ratio: 1; /* 16 px */
  --spb-title-width-ratio: 37.5; /* 600 px */
  --spb-header-gap-ratio: 3.75; /* 60 px under the header */

  /* Resolved measurements. Absolute on purpose: a custom property written in
     em would be resolved by whoever consumes it, and the title reads at
     3.75x — the same string would then mean four different lengths. */
  --spb-title-size: calc(var(--spb-title-ratio) * var(--spb-font-size));
  --spb-print-width: calc(var(--spb-print-ratio) * var(--spb-font-size));
  --spb-section-pad: calc(var(--spb-section-ratio) * var(--spb-font-size));
  --spb-gutter: calc(var(--spb-gutter-ratio) * var(--spb-font-size));
  --spb-band-gap: calc(var(--spb-band-gap-ratio) * var(--spb-font-size));
  --spb-actions-gap: calc(var(--spb-actions-ratio) * var(--spb-font-size));
  --spb-header-gap: calc(var(--spb-header-gap-ratio) * var(--spb-font-size));
  --spb-title-width: calc(var(--spb-title-width-ratio) * var(--spb-font-size));
  --spb-copy-width: calc(36 * var(--spb-font-size)); /* 576 px */
  --spb-eyebrow-gap: calc(1.875 * var(--spb-font-size)); /* 30 px */
  --spb-eyebrow-pad: calc(1 * var(--spb-font-size)); /* 16 px left and right */
  --spb-button-pad-y: calc(var(--spb-button-pad-y-ratio) * var(--spb-font-size));
  --spb-button-pad-x: calc(var(--spb-button-pad-x-ratio) * var(--spb-font-size));
  --spb-button-border: calc(0.125 * var(--spb-font-size)); /* 2 px */
  --spb-pill: calc(3.125 * var(--spb-font-size)); /* 50 px, i.e. a pill */

  /* Ink and paper. The background is the colour the reference actually
     renders: a cream veil at 95% over a video, measured at rgb(243 238 234).
     See README, "A background video you cannot see". */
  --spb-paper: #f3eeea;
  --spb-ink: #1f0f09; /* the title */
  --spb-ink-soft: #555; /* the paragraph */
  --spb-eyebrow-ink: #121212;
  --spb-eyebrow-paper: #fcf2e4;
  --spb-button-ink: #fff;
  --spb-button-paper: #3b534b;
  --spb-focus: #1f0f09;
  --spb-print-shadow: 0 calc(0.25 * var(--spb-font-size))
    calc(0.875 * var(--spb-font-size)) 0 rgb(18 18 18 / 0.1);

  /* Motion. --spb-course is the scroll the band consumes to arrive: in the
     reference the five prints are home after 160vh, and that is kept. What is
     dropped is the 240vh the original then holds the section pinned for
     nothing — set --spb-hold to 240vh to get that back. */
  --spb-course: 160vh;
  --spb-hold: 0vh;
  --spb-enter: 500ms;
  --spb-hover: 300ms;

  /* The five starting offsets, and the angle each print settles at. Both are
     read by the script; the angles alone are enough without it. */
  --spb-start-1: 50vw;
  --spb-start-2: 55vw;
  --spb-start-3: 60vw;
  --spb-start-4: 65vw;
  --spb-start-5: 70vw;
  --spb-angle-1: 3.43deg;
  --spb-angle-2: -7.12deg;
  --spb-angle-3: 0deg;
  --spb-angle-4: 4.44deg;
  --spb-angle-5: -2.63deg;

  box-sizing: border-box;
  position: relative;
  background: var(--spb-paper);
  font-family: "Bricolage Grotesque SPB", system-ui, sans-serif;
  font-size: var(--spb-font-size);
  font-weight: 500;
  line-height: 1.625; /* 26 px at 16 px */
  color: var(--spb-ink-soft);
  /* The component assumes no reset of its own. */
  text-align: left;
}

.scroll-photo-band *,
.scroll-photo-band *::before,
.scroll-photo-band *::after {
  box-sizing: inherit;
}

/* Scale steps, in the order the reference sets them. Only the ratios move. */
@media (min-width: 1280px) {
  .scroll-photo-band {
    --spb-section-ratio: 7.5; /* 120 px */
    --spb-gutter-ratio: 5; /* 80 px */
    --spb-actions-ratio: 3.125; /* 50 px */
    --spb-button-pad-y-ratio: 0.9375; /* 15 px */
    --spb-button-pad-x-ratio: 1.5; /* 24 px */
  }
}

@media (min-width: 1440px) {
  .scroll-photo-band {
    --spb-title-ratio: 3.75; /* 60 px */
    --spb-print-ratio: 21.875; /* 350 px */
    --spb-section-ratio: 8.125; /* 130 px */
    --spb-gutter-ratio: 7.5; /* 120 px */
    --spb-band-gap-ratio: 4.375; /* 70 px */
    --spb-title-width-ratio: 45; /* 720 px */
  }
}

@media (min-width: 1920px) {
  .scroll-photo-band {
    --spb-print-ratio: 28.125; /* 450 px */
    --spb-section-ratio: 9.375; /* 150 px */
    --spb-gutter-ratio: 9.375; /* 150 px */
  }
}

@media (max-width: 991px) {
  .scroll-photo-band {
    --spb-title-ratio: 2.85; /* 45.6 px */
    --spb-print-ratio: 12.5; /* 200 px */
    --spb-section-ratio: 5; /* 80 px */
    --spb-gutter-ratio: 2.5; /* 40 px */
    --spb-header-gap-ratio: 2.5; /* 40 px */
  }
}

@media (max-width: 767px) {
  .scroll-photo-band {
    --spb-title-ratio: 2.75; /* 44 px */
    --spb-print-ratio: 7.5; /* 120 px */
    --spb-section-ratio: 3.75; /* 60 px */
    --spb-gutter-ratio: 1.25; /* 20 px */
    --spb-button-pad-y-ratio: 0.375; /* 6 px */
    --spb-button-pad-x-ratio: 0.75; /* 12 px */
  }
}

@media (max-width: 479px) {
  .scroll-photo-band {
    --spb-title-ratio: 2.5; /* 40 px */
    --spb-print-ratio: 5; /* 80 px */
  }
}

/* ==========================================================================
   3. Rail, sticky stage
   --------------------------------------------------------------------------
   The rail is as tall as its contents plus the course. The stage sticks to
   the top of the viewport for exactly that course, which is the scroll the
   prints need — so the section is never pinned on nothing.
   ========================================================================== */

/* The room the stage sticks through is added as an empty block after it, not
   as padding on the rail: what a sticky box may travel through is its
   parent's CONTENT box. Give the rail its height with padding-bottom and the
   content box stops at the stage — nothing sticks, and no style says why. */
.scroll-photo-band__rail::after {
  content: "";
  display: block;
  height: calc(var(--spb-course) + var(--spb-hold));
}

.scroll-photo-band__stage {
  position: sticky;
  top: 0;
  /* The prints start up to 70vw off to the right, and the band is wider than
     the window even once they have landed — that is the drawing, and the
     original does the same. The clip goes here rather than on the section:
     an element that clips becomes the containing block its own descendants
     stick to, so clipping the section would have unstuck this very stage.
     Clipping from the stage itself is safe — a box does not stick to itself
     — and nothing ever escapes it.

     Vertically there is room to spare: the tilted corners reach some twenty
     pixels past the band, and the section's own padding is a hundred and
     thirty. */
  overflow: clip;
}

.scroll-photo-band__inner {
  padding-top: var(--spb-section-pad);
  padding-bottom: var(--spb-section-pad);
}

/* ==========================================================================
   4. Header: eyebrow and title
   ========================================================================== */

.scroll-photo-band__header {
  margin: 0 0 var(--spb-header-gap);
  padding-inline: var(--spb-gutter);
  text-align: center;
}

.scroll-photo-band__heading {
  max-width: var(--spb-title-width);
  margin-inline: auto;
}

.scroll-photo-band__eyebrow {
  display: inline-block;
  margin: 0 0 var(--spb-eyebrow-gap);
  padding-inline: var(--spb-eyebrow-pad);
  border-radius: var(--spb-pill);
  background: var(--spb-eyebrow-paper);
  color: var(--spb-eyebrow-ink);
  font-size: var(--spb-font-size);
  font-weight: 600;
  line-height: 1.625;
  text-transform: uppercase;
}

.scroll-photo-band__title {
  /* An <h2> keeps a browser margin of its own; the reference clears it. */
  margin: 0;
  font-family: "Instrument Serif SPB", Georgia, serif;
  font-size: var(--spb-title-size);
  font-weight: 400;
  line-height: 1.16;
  letter-spacing: calc(-0.0125 * var(--spb-font-size)); /* -0.2 px */
  text-transform: capitalize;
  color: var(--spb-ink);
}

.scroll-photo-band__title em {
  font-style: italic;
}

/* The reference's emphasis, for whoever wants it back. */
.scroll-photo-band[data-italic="synthetic"] .scroll-photo-band__title em {
  font-family: "Instrument Serif Roman SPB", Georgia, serif;
  font-synthesis-style: auto;
}

/* ==========================================================================
   5. The band of prints
   --------------------------------------------------------------------------
   Five prints, edge to edge, centred. They are wider than the viewport on a
   large screen and that is the point: the row is centred, so the middle print
   sits on the axis of the page and the outer two run off the sides.
   ========================================================================== */

.scroll-photo-band__band {
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 0 0 var(--spb-band-gap);
  /* No list bullets, no indent: the band is a <ul>. */
  padding: 0;
  list-style: none;
}

.scroll-photo-band__print {
  flex: none;
  width: var(--spb-print-width);
  margin: 0;
  aspect-ratio: 450 / 433;
  box-shadow: var(--spb-print-shadow);
  /* Each print carries its own rotation, and the script adds the travel on
     top of it. Without the script this is the whole transform, which is why
     the band reads correctly with JavaScript off. */
  transform: translate3d(var(--spb-travel, 0px), 0, 0)
    rotate(var(--spb-tilt, var(--spb-angle)));
}

.scroll-photo-band__print:nth-child(1) {
  --spb-angle: var(--spb-angle-1);
  --spb-start: var(--spb-start-1);
}
.scroll-photo-band__print:nth-child(2) {
  --spb-angle: var(--spb-angle-2);
  --spb-start: var(--spb-start-2);
}
.scroll-photo-band__print:nth-child(3) {
  --spb-angle: var(--spb-angle-3);
  --spb-start: var(--spb-start-3);
}
.scroll-photo-band__print:nth-child(4) {
  --spb-angle: var(--spb-angle-4);
  --spb-start: var(--spb-start-4);
}
.scroll-photo-band__print:nth-child(5) {
  --spb-angle: var(--spb-angle-5);
  --spb-start: var(--spb-start-5);
}

.scroll-photo-band__photo {
  display: block; /* otherwise the baseline adds a few pixels under the image */
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ==========================================================================
   6. Copy and button
   ========================================================================== */

.scroll-photo-band__copy {
  /* The gutter is added to the ceiling, not taken out of it: with
     box-sizing: border-box a plain max-width would have left the paragraph
     240 px narrower than the reference, which is a whole extra line. */
  max-width: calc(var(--spb-copy-width) + 2 * var(--spb-gutter));
  margin-inline: auto;
  padding-inline: var(--spb-gutter);
  text-align: center;
  line-height: 1.87; /* 29.92 px at 16 px */
}

.scroll-photo-band__lead {
  margin: 0;
  color: var(--spb-ink-soft);
}

.scroll-photo-band__actions {
  margin: var(--spb-actions-gap) 0 0;
}

.scroll-photo-band__link {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  overflow: hidden;
  padding: var(--spb-button-pad-y) var(--spb-button-pad-x);
  border: var(--spb-button-border) solid transparent;
  border-radius: var(--spb-pill);
  background: var(--spb-button-paper);
  color: var(--spb-button-ink);
  font: inherit;
  font-weight: 600;
  line-height: 1.87;
  letter-spacing: calc(0.05 * var(--spb-font-size)); /* 0.8 px */
  text-transform: uppercase;
  text-decoration: none;
}

.scroll-photo-band__link:focus-visible {
  outline: calc(0.125 * var(--spb-font-size)) solid var(--spb-focus);
  outline-offset: calc(0.125 * var(--spb-font-size));
}

/* The label slides up on hover and a copy of it follows from below. The copy
   is decorative: only one of the two is ever read out. */
.scroll-photo-band__label {
  position: relative;
  display: inline-flex;
  overflow: hidden;
}

.scroll-photo-band__label span {
  transition: transform var(--spb-hover) ease, opacity var(--spb-hover) ease;
}

.scroll-photo-band__label span + span {
  position: absolute;
  inset: 0;
  transform: translateY(100%);
}

.scroll-photo-band__link:hover .scroll-photo-band__label span,
.scroll-photo-band__link:focus-visible .scroll-photo-band__label span {
  transform: translateY(-100%);
  opacity: 0;
}

.scroll-photo-band__link:hover .scroll-photo-band__label span + span,
.scroll-photo-band__link:focus-visible .scroll-photo-band__label span + span {
  transform: translateY(0);
  opacity: 1;
}

/* ==========================================================================
   7. Scroll-driven motion
   --------------------------------------------------------------------------
   The script writes --spb-travel and --spb-tilt on each print, frame by
   frame. Nothing here transitions: the position follows the scroll, and the
   softness comes from the script's own easing (see data-smoothing).
   ========================================================================== */

.scroll-photo-band[data-spb-ready] .scroll-photo-band__print {
  will-change: transform;
}

/* ==========================================================================
   8. Entrance animation

   The eyebrow and the title rise, sharpen and fade in when the section first
   comes into view. The starting state is only applied once the script has
   said so, so a page without JavaScript never hides them.
   ========================================================================== */

/* The waiting state is set, not animated: the transition is carried by the
   arrival alone. Put it on both and the script's very first write plays
   backwards — the title fades OUT over half a second, then back in once the
   observer fires. A second of blinking, and every screenshot taken in that
   second shows a blurred, empty header. */
.scroll-photo-band__eyebrow[data-spb-enter="waiting"],
.scroll-photo-band__title[data-spb-enter="waiting"] {
  transform: translateY(calc(2.5 * var(--spb-font-size))); /* 40 px */
  opacity: 0;
  filter: blur(calc(0.625 * var(--spb-font-size))); /* 10 px */
}

.scroll-photo-band__eyebrow[data-spb-enter="done"],
.scroll-photo-band__title[data-spb-enter="done"] {
  transition: transform var(--spb-enter) ease, opacity var(--spb-enter) ease,
    filter var(--spb-enter) ease;
}

/* ==========================================================================
   9. Fallbacks
   ========================================================================== */

/* Reduced motion: no travel, no rail to scroll through, no entrance. The
   prints keep their angles, which are drawing rather than movement. */
@media (prefers-reduced-motion: reduce) {
  .scroll-photo-band {
    --spb-course: 0vh;
    --spb-hold: 0vh;
    --spb-enter: 0ms;
    --spb-hover: 0ms;
  }

  .scroll-photo-band__stage {
    position: static;
  }

  .scroll-photo-band__eyebrow[data-spb-enter="waiting"],
  .scroll-photo-band__title[data-spb-enter="waiting"] {
    transform: none;
    opacity: 1;
    filter: none;
  }
}

/* Forced colours: keep the shapes, let the system own the palette. The
   photographs stay, the decorative shadow goes. */
@media (forced-colors: active) {
  .scroll-photo-band {
    background: Canvas;
    color: CanvasText;
  }

  .scroll-photo-band__title,
  .scroll-photo-band__lead {
    color: CanvasText;
  }

  .scroll-photo-band__eyebrow {
    border: 1px solid CanvasText;
    background: Canvas;
    color: CanvasText;
  }

  .scroll-photo-band__link {
    border-color: CanvasText;
    background: Canvas;
    color: LinkText;
    forced-color-adjust: none;
  }

  .scroll-photo-band__print {
    box-shadow: none;
    outline: 1px solid CanvasText;
  }
}
