/* ============================================================
   Balance Farm — story (hero + scroll-driven panels)

   One pinned text layer spans the whole run: it sticks to the
   viewport while the panels scroll beneath it, swapping copy and
   colour per panel, then releases so normal page flow resumes at
   the next section (Guest Reviews).

   All panels are one full viewport tall (--panel-h). svh, not dvh: the
   dynamic unit resizes as mobile browser UI shows/hides, which resizes
   every panel mid-scroll. svh is pinned to the smallest viewport, so the
   panels stay put and the URL bar simply overlaps the bottom edge.
   Desktop  the two middle panels put the image on the right third.
   Mobile   every panel is a full-bleed image, text always white.
   ============================================================ */

.story {
  --panel-h: 100vh;
  --panel-h: 100svh;                  /* fixed: never resizes with browser UI */
  /* the pinned layer tracks the VISIBLE area instead, so its centre and its
     bottom edge stay optically correct as the browser UI shows and hides */
  --pin-h: var(--panel-h);
  position: relative;
}

/* ---- pinned layer ---------------------------------------- */
.story__pin {
  position: sticky;
  top: 0;
  z-index: 5;
  height: var(--pin-h);
  margin-bottom: calc(var(--pin-h) * -1);   /* takes no space in the flow */
  pointer-events: none;
  color: var(--color-paper);
  transition: color 700ms ease;
}
.story__pin.is-dark { color: var(--color-ink); }

@media (min-width: 1025px) {
  /* white + difference on the paper panels, where the copy crosses both the
     sheet and a photograph and no single colour reads on both */
  .story__pin,
  .story__pin.is-dark {
    color: var(--color-paper);
    mix-blend-mode: difference;
  }
  /* a light-tone panel is full-bleed imagery (hero, wellness), so plain white
     over it is correct and blending would fight the footage */
  .story__pin:not(.is-dark) {
    color: var(--color-paper);
    mix-blend-mode: normal;
  }
}

/* Copy swap: the outgoing line lifts and clears while the incoming one
   rises into place at the same time, overlapping so there is never an
   empty frame. No mask — the copy can never be clipped, whatever its
   length — and the whole swap is under 400ms, so by the time the next
   section is in view its copy is already there. */
.story__copy {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(440px, calc(100vw - 32px));
}

/* The page thins type with -webkit-font-smoothing: antialiased, which only
   takes effect on macOS — iOS was showing the font's true weight all along.
   Everything set over the photography opts back into subpixel AA, so the
   heavier reading is the same on every platform. */
.story__text,
.story__label,
.story__scroll,
.story__place { -webkit-font-smoothing: subpixel-antialiased; white-space: nowrap; }
.story__text {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  text-align: center;
  font-size: var(--t-xl);
  font-weight: var(--w-semi);   /* semibold */
  line-height: 96%;
  letter-spacing: var(--t-lg-track);
  text-wrap: balance;   /* even line lengths at any measure */
  opacity: 0;
  transform: translateY(calc(-50% + 22px));
  transition: transform 560ms cubic-bezier(.16, 1, .3, 1), opacity 300ms ease;
}
.story__text.is-active {
  opacity: 1;
  transform: translateY(-50%);
}
.story__text.is-out {
  opacity: 0;
  transform: translateY(calc(-50% - 22px));
  transition-duration: 300ms, 240ms;
}
.story__text.no-anim { transition: none; }

.story__label,
.story__scroll,
.story__place {
  position: absolute;
  bottom: 24px;
  font-size: var(--t-base);
  font-weight: var(--w-semi);
  letter-spacing: var(--t-base-track);
  line-height: 100%;
  transition: opacity 520ms ease;
}
.story__label { left: 24px; }
.story__scroll { left: 50%; transform: translateX(-50%); }
.story__place { right: 24px; }
/* the caption carries the scroll cue on the hero and each panel's own line
   after it, so it is visible throughout */
.story__scroll { opacity: 1; }

/* ---- panels ---------------------------------------------- */
.story__panel { position: relative; }

.story__panel--hero .story__img {
  transform-origin: center 60%;         /* the treeline, not the sky */
}

/* ---- split hero: still on the left, moving image on the right ----
   Two halves of one frame, so the panel reads as a single picture with the
   copy pinned across the seam. On phones there is no room for two, and the
   video is the heavier asset, so the still carries it alone. */
.story__media--split {
  display: grid;
  /* one column: the video carries the hero, and the still is held back rather
     than removed, so restoring the split is a CSS change alone */
  grid-template-columns: 1fr;
  /* the row needs a definite height or the children's `height: 100%` has no
     basis to resolve against: it computes to auto, the images take their
     intrinsic height, and the panel overflows. minmax(0, 1fr) lets the row
     shrink below that intrinsic height instead of inflating the track. */
  height: 100%;
  grid-template-rows: minmax(0, 1fr);
}
.story__media--split .story__img { width: 100%; height: 100%; object-fit: cover; }
.story__media--split .story__img--right { display: none; }
/* the room reads from the ceiling down, so anchoring high keeps the pitched
   roof and window head in frame rather than cropping to the bed */
.story__media--split .story__img--right { object-position: center 35%; }
@media (max-width: 768px) {
  /* one half only: the video carries more of the brand, so it takes the frame
     and the still steps aside */
  .story__media--split { grid-template-columns: 1fr; }
  .story__media--split .story__img--right { display: none; }
}

.story__img {
  display: block;                       /* video defaults to inline */
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* full-bleed image panels */
.story__panel--full {
  height: var(--panel-h);
  overflow: hidden;
  background: var(--color-bg);
}


/* the closing panel frames a 4:3 still on the full-bleed backdrop */
.story__panel--ratio {
  display: grid;
  place-items: center;
  background: var(--color-bg);
}
.story__panel--ratio {
  height: auto;                         /* the ratio sets the height */
}
.story__panel--ratio .story__img {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

@media (max-width: 1024px) {
  .story__pin.is-wide .story__text,
  .story__pin.is-hero .story__text { color: var(--color-paper); }
}

/* two-thirds whitespace, image on the right third */
.story__panel--split {
  height: var(--panel-h);
  display: grid;
  grid-template-columns: 2fr 1fr;
  background: var(--color-paper);
}

/* A wider frame for the two landscape panels: the split's own tracks are
   2fr 1fr, so this variant re-lays the panel as 1fr 3fr 1fr — a 3fr image
   centred on paper, reading as a plate rather than a full-bleed backdrop. */
.story__panel--wide {
  grid-template-columns: 1fr 3fr 1fr;
  /* taller than a viewport, so the plate has room to breathe and the pin
     holds through the whole panel rather than clearing it immediately */
  height: calc(var(--panel-h) * 1.2);
}
.story__panel--wide .story__media { grid-column: 2; height: 100%; }
.story__panel--wide .story__img { width: 100%; height: 100%; object-fit: cover; }

/* the copy crosses the photograph rather than the paper, so it inverts —
   the captions keep the panel's own dark tone (js/ui/story.js sets .is-wide) */
.story__media {
  grid-column: 2;
  height: 100%;
  overflow: hidden;
}

@media (max-width: 1024px) {
  /* tablet and below: every panel is a full-bleed image, copy stays white */
  .story__pin,
  .story__pin.is-dark { color: var(--color-paper); }
  .story__panel--split { display: block; }
  .story__media { height: 100%; }
  .story__panel--ratio {
    display: block;                    /* the grid row can't stretch the img */
    height: var(--panel-h);
  }
  .story__panel--ratio .story__img {
    width: 100%;
    height: 100%;
    aspect-ratio: auto;                /* matches the other panels on mobile */
  }
  .story__copy { width: min(520px, calc(100vw - 48px)); }
  /* one fluid ramp from phone to tablet, capped at the desktop title size */
  .story__text { font-size: clamp(var(--t-lg), 5.2vw, var(--t-xl)); }
  .story__label { left: 24px; }
  .story__place { right: 24px; }
}

@media (max-width: 1024px) {
  /* Phones and tablets: the imagery is full height and full bleed on three
     sides, with a single 44px band down one edge — right for the first four
     panels, left for the last, so the run alternates at its close. Hero and the
     wellness video (--ratio) stay entirely full-bleed. */
  .story__panel:not(.story__panel--ratio):not(.story__panel--hero) {
    height: var(--panel-h);
  }
  .story__panel:not(.story__panel--ratio):not(.story__panel--hero) .story__media {
    padding: 0 44px 0 0;
    box-sizing: border-box;
  }
  /* the second (The Land) and the fifth (Evenings) turn the band to the left */
  .story__panel--split.story__panel--wide:not(.story__panel--ratio):not(.story__panel--hero) .story__media {
    padding: 0 0 0 44px;
  }

  /* the wide panels are 1.2x tall on desktop; on phones every panel is one
     screen, so the run reads as one rhythm */
  .story__panel--wide { height: var(--panel-h); }

  /* Past the hero the panel's own description takes the whole bottom row, so
     the label and the temperature clear out rather than colliding with it. */
  .story__pin:not(.is-hero) .story__label,
  .story__pin:not(.is-hero) .story__place { display: none; }
  .story__pin:not(.is-hero) .story__scroll {
    left: 50%;
    right: auto;
    bottom: 32px;
    transform: translateX(-50%);
    width: min(560px, calc(100vw - 96px));
    white-space: normal;
    text-align: center;
    text-wrap: normal;
  }
}

@media (max-width: 600px) {
  /* The panels stay svh so scroll geometry never jumps, but the pinned layer
     follows the visible area: its own 50% is then the optical centre of what
     is on screen, and its bottom edge sits on the real viewport bottom.
     (.story__text is positioned inside .story__copy, so it keeps 50%.) */
  .story { --pin-h: 100dvh; }

  /* phones: shorter measure so the copy keeps a comfortable line count */
  .story__copy { width: min(324px, calc(100vw - 40px)); }
  /* captions step down so the two corners fit either side */
  .story__label,
  .story__scroll,
  .story__place {
    font-size: var(--t-sm);
    /* the pin is 100dvh, so its own bottom edge already tracks the visible
       area — no offset needed */
    bottom: 24px;
  }
  /* phones: the hero's scroll cue takes the left corner, so the label
     hides and the real element moves — no duplicated or generated text */
  .story__scroll {
    left: 24px;
    right: auto;
    transform: none;
  }
  .story__pin.is-hero .story__label { display: none; }

  /* Past the hero the corners clear entirely and the panel's own description
     sits centred under the title, so title and note read as one block rather
     than three separate captions. */
  .story__pin:not(.is-hero) .story__label,
  .story__pin:not(.is-hero) .story__place { display: none; }
  .story__pin:not(.is-hero) .story__scroll {
    /* stays on the bottom row like the other captions; only the measure and
       wrapping change, since it now carries a sentence rather than a label */
    left: 50%;
    right: auto;
    transform: translateX(-50%);
    bottom: 32px;
    width: calc(100vw - 96px);
    white-space: normal;
    text-align: center;
    /* fills each line before wrapping — `balance` was evening the lines out
       instead, which left the measure looking short */
    text-wrap: normal;
  }
}

@media (prefers-reduced-motion: reduce) {
  .story__text,
  .story__label,
  .story__pin { transition-duration: 1ms; }
}
