/*
 * home.css — the landing page: full-height hero, the manifesto reel, then the
 * work strip. Everything is in normal flow; nothing is pinned to the viewport
 * except the reel's own stage, which has to be (see the manifesto notes).
 */

/* ----------------------------------------------------------------- hero */

.home-hero {
  display: flex;
  flex-direction: column;
  min-height: calc(100dvh - var(--nav-h));
  border-bottom: var(--rule);
  background: var(--paper);
}

/* The wordmark is centred in the stage; the crowd walks along its floor. The
   canvas is painted under the wordmark and is purely decorative. */
.home-hero__stage {
  position: relative;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
  padding: var(--pad-hero);
  overflow: hidden;
}

/* Lifted off the centre line so the crowd has the whole lower half to itself. */
/*
 * Layer order in the hero stage, and it is the whole trick:
 *   1  the logo — travels, and is occluded by the crowd it passes behind
 *   2  the crowd — must out-rank the logo, or the mark crosses in front of the
 *      figures and the two black artworks read as tangled
 *
 * There is no gradient and no fade. The mark is hidden by the crowd while they
 * overlap, and revealed when the crowd scrolls away with the hero.
 */
.home-hero__logo {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: min(100%, 34rem);
  margin-bottom: clamp(3rem, 16vh, 11rem);
}

/*
 * wordmark.js lifts the logo out of flow and drives it down the page to the
 * manifesto. Fixed at the origin with a top-left transform origin, so a single
 * translate + scale can put it anywhere; the script writes both.
 *
 * z-index stays below the crowd (2) and below the nav (20). It must stay *above*
 * the opaque backgrounds of the sections it crosses, which it does for free:
 * they are z-index auto, and a positioned z:1 box paints over them.
 */
.home-hero__logo.is-flying {
  position: fixed;
  top: 0;
  left: 0;
  margin: 0;
  max-width: none;
  transform-origin: 0 0;
  will-change: transform;
  pointer-events: none;
}

.home-hero__lottie {
  display: block;
  width: 100%;
}

/* crowd.js sizes the backing store to the element's box times devicePixelRatio;
   this rule owns the CSS box. Height is the peeps' headroom, not the sprite's. */
.home-hero__crowd {
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 2;
  width: 100%;
  height: min(62%, 28rem);
  pointer-events: none;
}

/* ------------------------------------------------------------ manifesto */

/*
 * Two voices. The lead is the studio's one-line definition, set in the display
 * face — it states, it doesn't roll. Everything under it is the elaboration,
 * smaller and in the body face, and that is what turns on the drum.
 *
 * BASE STATE (no JS, or prefers-reduced-motion): a plain centred block of text
 * in normal flow. Every rule below that makes it a drum is gated behind
 * .is-reel, which manifesto.js adds only when it is going to drive it. Never
 * put drum geometry outside that gate — the lines are absolutely positioned on
 * top of each other, so an ungated drum with no script is an unreadable pile.
 *
 * REEL STATE: the lines are faces on one cylinder. Each is pushed out to the
 * same radius along Z and rotated to its own angle around a shared X axis.
 * manifesto.js turns the whole drum by one angle; the lines never move relative
 * to each other. The stage is sticky so the drum spins in place.
 */
.manifesto {
  position: relative;
  background-color: var(--paper);
  border-bottom: var(--rule);
}

.manifesto__stage {
  --stage-gap: clamp(1.75rem, 5vh, 3.5rem);

  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--stage-gap);
  padding: clamp(5rem, 14vh, 10rem) var(--pad-hero);
  text-align: center;
}

/*
 * The mark and the lead are one sentence, so the stage's gap — which exists to
 * separate them from the drum — is cancelled between just these two. The small
 * positive term is the only space left: the Lottie's ink reaches its box's
 * bottom edge exactly, so any negative margin here overlaps the type.
 */
.manifesto.has-wordmark .manifesto__mark + .manifesto__lead {
  margin-top: calc(0.08em - var(--stage-gap));
}

/*
 * The wordmark, sitting directly on top of the lead so the two read as one
 * sentence. The negative margin closes the gap that the Lottie's own transparent
 * margin opens up — the artwork does not touch its artboard's bottom edge.
 *
 * Zero height until wordmark.js claims the section, so a page without JS shows
 * "Oka Studios" as type instead of a hole where a Lottie never rendered.
 */
.manifesto__mark {
  width: 0;
  height: 0;
  overflow: hidden;
}

/* Same max-width as the hero logo: the scale term then works out to 1 and the
   mark arrives at the size it left. */
.manifesto.has-wordmark .manifesto__mark {
  width: min(100%, 34rem);
  height: auto;
  aspect-ratio: var(--wordmark-ratio, 1.78);
  overflow: visible;
}

.manifesto__lead {
  font-family: var(--font-display);
  font-size: var(--size-manifesto-lead);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.02em;
}

/* One line. The clamp's floor is set so it still fits at 320px. */
.manifesto__lead-rest {
  display: block;
  white-space: nowrap;
}

.manifesto__text {
  max-width: 36ch;
  margin: 0 auto;
  font-size: var(--size-manifesto);
  font-weight: 400;
  line-height: 1.35;
  letter-spacing: -0.01em;
}

.manifesto__line {
  display: block;
}

/* ---------------------------------------------------------------- reel */

/*
 * Runway. The drum turns through this height minus the padding and the sticky
 * stage's own height; raising it slows the reel.
 *
 * The padding is a lead-in and a lead-out. Without it the stage's flow position
 * is the section's very top edge, so the lead crashes into the hero above it
 * before the stage has anything to stick to. manifesto.js reads this padding
 * back out — the drum's progress is measured from where the stage actually
 * pins, not from where the section starts.
 */
.manifesto.is-reel {
  height: 215vh;
  padding-top: clamp(6rem, 18vh, 12rem);
  padding-bottom: clamp(4rem, 12vh, 8rem);
}

/*
 * Sized to hold the lead plus the drum, then centred in the space *below the
 * nav* — not in the viewport. Centring on the viewport pushes the lead up under
 * the sticky bar, which is where it looked cramped.
 */
.manifesto.is-reel .manifesto__stage {
  --reel-stage-h: min(94dvh, 52rem);

  position: sticky;
  top: calc(var(--nav-h) + (100dvh - var(--nav-h) - var(--reel-stage-h)) / 2);
  height: var(--reel-stage-h);
  padding: 0 var(--pad-hero);
  perspective: 2600px;
  perspective-origin: 50% 50%;
  overflow: hidden;
}

/*
 * The drum's faces sweep a radius either side of .manifesto__text, which is
 * only one line tall. Padding by that radius — set from JS, since it is derived
 * from the rendered line box — reserves the space so the topmost face cannot
 * ride up over the lead.
 */
.manifesto.is-reel .manifesto__drum {
  width: 100%;
  padding: var(--reel-radius, 0px) 0;
  transform-style: preserve-3d;
}

/* Collapsed to exactly one line tall: it is the drum's axis, not a text block.
   Every line is absolutely positioned onto it and then rotated out. */
.manifesto.is-reel .manifesto__text {
  position: relative;
  height: 1.35em;
  transform-style: preserve-3d;
}

.manifesto.is-reel .manifesto__line {
  position: absolute;
  inset: 0;
  white-space: nowrap;
  backface-visibility: hidden;
  will-change: transform, opacity;
}

/* ---------------------------------------------------------------- fade */

/*
 * The section fades up as you scroll into it. manifesto.js adds .is-fade only
 * when it intends to drive the reveal, then .is-in when the section arrives —
 * so a page with the CSS but no script never hides its own copy.
 *
 * The lead rises; the drum only fades. A transform on .manifesto__drum would
 * sit between the stage's `perspective` and the faces' `preserve-3d`, and the
 * cylinder flattens. Move the lead, not the drum.
 */
.manifesto.is-fade .manifesto__lead {
  opacity: 0;
  transform: translateY(1.5rem);
  transition:
    opacity 0.7s ease,
    transform 0.8s var(--ease-out-quint);
}

.manifesto.is-fade .manifesto__drum {
  opacity: 0;
  transition: opacity 0.7s ease 0.15s;
}

.manifesto.is-fade.is-in .manifesto__lead {
  opacity: 1;
  transform: none;
}

.manifesto.is-fade.is-in .manifesto__drum {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .manifesto__line {
    transform: none !important;
    opacity: 1 !important;
  }

  .manifesto__lead,
  .manifesto__drum {
    opacity: 1 !important;
    transform: none !important;
  }
}

/* ----------------------------------------------------------- work strip */

/*
 * The whole effect is one transition on flex-grow. Panels have flex-basis 0,
 * so their widths are pure ratios of the row: the active panel grows to
 * --grow-active, the rest to 1. They always sum to the full width, at any
 * viewport, with no measurement in JS.
 *
 * work-strip.js moves .is-active on hover, focus, or click. Exactly one panel
 * carries it at all times, so the row never collapses to a state with nothing
 * open.
 */
#works {
  scroll-margin-top: var(--nav-h);
}

.work-strip {
  --panel-height: clamp(22rem, 68vh, 36rem);
  --caption-h: 7.5rem;
  --grow-active: 6;

  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
  border-bottom: var(--rule);
  overflow: hidden;
}

.work-panel {
  position: relative;
  flex: 1 1 0;
  height: var(--panel-height);
  min-width: 0;
  border-right: var(--rule);
  overflow: hidden;
  transition: flex-grow var(--dur-reveal) var(--ease-out-quint);
}

.work-panel:last-child {
  border-right: none;
}

.work-panel.is-active {
  flex-grow: var(--grow-active);
}

/* Image on top, caption box beneath it — a card, not an overlay. */
.work-panel__link {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
}

.work-panel__image {
  flex: 1 1 auto;
  width: 100%;
  min-height: 0;
  object-fit: cover;
  object-position: center;
  filter: grayscale(1);
  transition: filter var(--dur-tint) ease;
}

.work-panel.is-active .work-panel__image {
  filter: grayscale(0);
}

/*
 * Every caption box is exactly --caption-h tall, so the images across the strip
 * share one baseline no matter how long a description runs. The box itself is
 * always drawn — collapsed panels show an empty white sill under their sliver,
 * which keeps the strip's bottom rule unbroken. Only the text fades.
 *
 * The min-width is the expanded panel's width, so the text is laid out once and
 * never reflows mid-animation; the panel's overflow:hidden clips the excess
 * while the panel is narrow. The caption stays in the DOM and in the
 * accessibility tree at every width.
 */
.work-panel__caption {
  flex: 0 0 auto;
  height: var(--caption-h);
  min-width: 22rem;
  padding: var(--pad-row);
  background: var(--paper);
  border-top: var(--rule);
  overflow: hidden;
  opacity: 0;
  transition: opacity 0.24s ease;
}

.work-panel.is-active .work-panel__caption {
  opacity: 1;
  transition: opacity 0.32s ease 0.12s;
}

.work-panel__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
}

.work-panel__title {
  font-size: var(--size-work-title);
  font-weight: 400;
}

.work-panel__year {
  flex: none;
  font-size: 1rem;
}

.work-panel__desc {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  max-width: 52ch;
  margin-top: 0.35rem;
  font-size: var(--size-body);
  line-height: 1.55;
  overflow: hidden;
}

.work-panel__link:focus-visible {
  outline: 1px solid var(--ink);
  outline-offset: -4px;
}

/* --------------------------------------------------------------- narrow */

@media (max-width: 768px) {
  .home-hero__crowd {
    height: min(46%, 15rem);
  }

  /* Smaller type means a tighter drum, so the stage shrinks with it. */
  .manifesto.is-reel .manifesto__stage {
    --reel-stage-h: min(90dvh, 40rem);
  }

  /* No room to expand sideways: stack the panels and show every caption. */
  .work-strip {
    display: block;
  }

  .work-panel,
  .work-panel.is-active {
    height: auto;
    border-right: none;
    border-bottom: var(--rule);
  }

  .work-panel:last-child {
    border-bottom: none;
  }

  .work-panel__image {
    flex: 0 0 auto;
    height: auto;
    aspect-ratio: 16 / 10;
    filter: grayscale(0);
  }

  .work-panel__caption {
    height: auto;
    min-width: 0;
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .manifesto__line {
    transform: none !important;
    opacity: 1 !important;
  }

  .work-panel,
  .work-panel__image,
  .work-panel__caption {
    transition-duration: 0.01ms;
    transition-delay: 0s;
  }
}
