/* ============================================================================
   Chassidish Choir — home.css
   Home page only. Everything here works WITHOUT JavaScript: the three "pinned"
   sections are ordinary full-height sections until motion.js adds `.has-pins`
   to <html>, at which point they grow tall and their stage sticks.
   ========================================================================== */

/* ============================================================== 1. HERO ==== */

.hero {
  position: relative;
  height: 100svh;
  min-height: 640px;
}
/* ⭐ SCROLL LENGTH. This number is the whole "how many scrolls to get past the
   hero" feeling — it is not a layout value, it is a FEEL value. He counted five
   wheel-scrolls at 320vh and asked for two. The timeline in motion.js is
   scrubbed, so shortening this plays the same animation over less distance;
   nothing is cut.

   Calibrated against HIS OWN counts rather than guessed: 5 pushes to clear 320vh
   and 3 to clear 240vh both work out to ~430px of travel per push (416 and 441 —
   two independent numbers that agree, so the unit is real). Clearing a sticky
   section costs `height - 1 viewport`, so 2 pushes = 100vh + 2×430px ≈ 190vh. */
html.has-pins .hero { height: 190vh; min-height: 0; }

/* Flex column, not a stacked grid: the circle and the type block must never
   overlap on a short laptop screen, and flex makes that impossible by
   construction. GSAP only writes transforms here, so it cannot disturb it. */
.hero__stage {
  /* ONE number drives the whole lockup. The circle used to be sized from the
     viewport HEIGHT (17vh) while the words were sized from the WIDTH (560px /
     78vw) — so the two never tracked each other, and on a short wide screen the
     circle shrank while the words stayed big. That is the "the words just
     doesn't fit" he was looking at. Both now derive from this. */
  --hero-mark-w: min(560px, 78vw);

  position: sticky;
  top: 0;
  height: 100svh;
  min-height: 560px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* Was clamp(1.1rem, 3.2vh, 2.6rem) — the circle's bottom edge nearly touched
     the top of the letters, so they read as two pieces that happen to be near
     each other instead of one mark. */
  gap: clamp(1.6rem, 5.6vh, 4rem);
  isolation: isolate;
}

/* -- the video layer -- */
.hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  transform: scale(1.06);
  will-change: transform;
}
/* The source clips are shot under bright hall lighting against white drapes.
   Left alone the frame is brighter than the type, and the gold reads as grey.
   Grade the footage down here, then veil it — one without the other is not
   enough. */
.hero__media video,
.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 34%;
  filter: brightness(0.62) contrast(1.06) saturate(0.88);
}
/* Two veils: a plum wash that keeps the video on-palette, and a bottom
   gradient so the type never sits on a busy frame. */
.hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(112% 84% at 50% 38%, rgba(21, 12, 27, 0.22), rgba(21, 12, 27, 0.88) 80%),
    linear-gradient(180deg, rgba(21, 12, 27, 0.74) 0%, rgba(21, 12, 27, 0.42) 28%, rgba(21, 12, 27, 0.88) 74%, rgba(21, 12, 27, 0.99) 100%);
}
.hero__veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--plum-950);
  opacity: 0.26;
  pointer-events: none;
}

/* -- the circle -- */
.hero__seal {
  position: relative;
  z-index: 3;
  flex: 0 0 auto;
  /* Proportional to the words, with a height cap so a short laptop still fits
     the whole lockup on screen. 0.34 is the classical mark-to-wordmark ratio —
     big enough to lead, small enough not to compete. */
  width: min(calc(var(--hero-mark-w) * 0.34), 22vh);
  min-width: 96px;
  aspect-ratio: 1;
  filter: drop-shadow(0 24px 60px rgba(0, 0, 0, 0.6));
  will-change: transform;
}
.hero__seal img { width: 100%; height: 100%; object-fit: contain; }

/* A thin gold ring orbiting the seal — the scrollwork motif, reduced. */
.hero__ring {
  position: absolute;
  inset: -9%;
  border-radius: 50%;
  border: 1px solid rgba(227, 200, 119, 0.34);
  pointer-events: none;
}
.hero__ring--2 { inset: -17%; border-color: rgba(227, 200, 119, 0.16); }

/* -- the type block -- */
.hero__type {
  position: relative;
  z-index: 3;
  flex: 0 0 auto;
  width: 100%;
  max-width: 900px;
  padding-inline: var(--page-pad);
  text-align: center;
  will-change: transform, opacity;
}
.hero__h1 { margin: 0; line-height: 0; }
.hero__wordmark {
  width: var(--hero-mark-w);
  margin-inline: auto;
  filter: drop-shadow(0 10px 34px rgba(0, 0, 0, 0.55));
}
.hero__name {
  font-family: var(--font-display);
  font-size: clamp(2.6rem, 1.2rem + 6.4vw, 5.6rem);
  line-height: 0.98;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin: 0;
}
.hero__tagline {
  margin-top: var(--sp-4);
  font-size: clamp(0.68rem, 0.56rem + 0.46vw, 0.92rem);
  letter-spacing: 0.42em;
  text-indent: 0.42em; /* balances the trailing letter-space so it reads centered */
}
/* In the client's own artwork the Yiddish line runs 52.5% of the word's width,
   tucked under its right half. Driving it off --hero-mark-w — the one variable
   the whole lockup is sized from — keeps that proportion at every screen size
   instead of letting the two drift apart the way the circle and the words did. */
/* In the client's artwork the Yiddish line is NOT centred under the word — its
   right edge is flush with the right edge of the ח (x=2082 against x=2086, on a
   word 1987px wide) and it hangs under the right half only. Centring it was
   wrong and he caught it: "it should match with the ח."
   The word is centred at width --hero-mark-w, so its right edge sits
   (50% + mark/2) from the left; putting that much minus the mark on the RIGHT
   margin lands this element's right edge in the same place, at any width. */
.hero__tagline-yi {
  width: calc(var(--hero-mark-w) * 0.525);
  margin-top: var(--sp-4);
  margin-left: auto;
  margin-right: calc(50% - var(--hero-mark-w) / 2);
}
/* On a phone the word itself is only ~304px, so the designed 52.5% lands the
   Yiddish at 160px — the letters drop to ~12px and start losing against the
   video behind them. Text has a floor that artwork proportions don't. */
@media (max-width: 640px) {
  .hero__tagline-yi { width: calc(var(--hero-mark-w) * 0.68); }
}
.hero__line {
  margin: var(--sp-5) auto 0;
  max-width: 34ch;
  font-family: var(--font-display);
  font-size: clamp(1.15rem, 0.95rem + 1.05vw, 1.85rem);
  font-style: italic;
  font-weight: 300;
  line-height: 1.35;
  color: rgba(245, 230, 192, 0.86);
}
.hero__actions {
  margin-top: var(--sp-6);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  justify-content: center;
}

/* -- scroll cue -- */
.hero__cue {
  position: absolute;
  z-index: 4;
  left: 50%;
  bottom: clamp(1.25rem, 3vh, 2.5rem);
  translate: -50% 0;
  display: grid;
  justify-items: center;
  gap: 0.6rem;
  font-size: 0.64rem;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg-faint);
  pointer-events: none;
}
.hero__cue i {
  display: block;
  width: 1px;
  height: 46px;
  background: linear-gradient(180deg, var(--gold-300), transparent);
  transform-origin: top;
  animation: cue 2.4s var(--ease-in-out) infinite;
}
@keyframes cue {
  0% { transform: scaleY(0); opacity: 0; }
  35% { transform: scaleY(1); opacity: 1; }
  100% { transform: scaleY(1) translateY(46px); opacity: 0; }
}

/* Short laptops and phones now shrink the lockup by moving ONE number, so the
   circle and the words stay in proportion instead of drifting apart. Overriding
   the two widths separately is what broke the relationship in the first place. */
@media (max-height: 720px) {
  .hero__stage {
    --hero-mark-w: min(430px, 72vw);
    gap: clamp(1.1rem, 4vh, 2.4rem);
  }
}
@media (max-width: 640px) {
  .hero__stage { --hero-mark-w: min(360px, 76vw); }
  .hero__seal { min-width: 88px; }
  .hero__actions { flex-direction: column; align-items: stretch; }
  .hero__actions .btn { width: 100%; }
}

/* ============================================================= 2. ABOUT ==== */

.about {
  position: relative;
  background: var(--paper);
  color: var(--ink);
  /* The medallion is 108vw and centred, so it hangs 4vw off each edge. Its
     containing block is THIS element (`.about__stage` is static when there are
     no pins), so the stage's own `overflow: hidden` never clipped it and on a
     phone it produced real horizontal page scroll.
     `clip`, not `hidden` — hidden would make this a scroll container and move
     the sticky containing block for `.about__stage`, breaking the pin. */
  overflow-x: clip;
}
/* ⭐ SCROLL LENGTH — see the note on .hero for how this number was derived.
   He counted three here and asked for one and a half: 100vh + 1.5×430px ≈ 168vh. */
html.has-pins .about { height: 168vh; }

.about__stage {
  position: sticky;
  top: 0;
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding-block: var(--section-y);
  overflow: hidden;
}
html:not(.has-pins) .about__stage { position: static; min-height: 0; }

/* The medallion sits behind the copy, huge and faint — the shape motif doing
   the work instead of a decorative background image. */
.about__medallion {
  position: absolute;
  z-index: 0;
  top: 50%;
  left: 50%;
  width: min(760px, 108vw);
  aspect-ratio: 1;
  translate: -50% -50%;
  opacity: 0.07;
  pointer-events: none;
  will-change: transform;
}
/* Flatten it to a single warm tint. At full colour the scrollwork competes
   with the paragraph sitting on top of it and the copy becomes hard to read. */
.about__medallion img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: grayscale(1) contrast(0.75) brightness(0.55) sepia(0.45);
}

.about__inner {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 820px;
  margin-inline: auto;
  padding-inline: var(--page-pad);
  text-align: center;
}
.about__inner .label { color: var(--gold-700); justify-content: center; }

/* -- the owner, cut out ---------------------------------------------------- *
   A black coat on the one cream section of the site. He is gesturing to the
   viewer's right, so he is placed LEFT and points into the copy; mirroring him
   would have him facing off the edge of the page.
   `contain` and a max-height, never `cover`: cropping a person's head or feet to
   fill a box is the one thing that would make this look worse than not using the
   photo at all. */
.about__portrait {
  margin: 0 auto var(--sp-6);
  display: flex;
  justify-content: center;
  align-items: flex-end;
}
.about__portrait img {
  width: auto;
  max-width: 100%;
  max-height: min(38svh, 320px);
  object-fit: contain;

  /* There used to be a `mask-image` here dissolving him away at shin height,
     because the shoes could not be keyed and the strip below was ragged. The
     client's read of it was exactly right: "the bottom of the person, it's like
     white, it's not good blending."
     The shoes key cleanly now (tools/cutout.mjs — the fix was positional, not a
     threshold), so there is nothing left to hide and he stands on his own feet.
     The only softening left is a 19px hairline inside the asset itself, which
     stops the soles ending on a razor line. */
}

/* The centring used to live in an inline `style` on this row. It is a real rule
   now so the two-column block can override it. */
.about__copy .cta-row { justify-content: center; }

.about h2 {
  font-size: var(--t-h2);
  color: var(--ink);
}
.about__body {
  margin: var(--sp-6) auto 0;
  max-width: 56ch;
  font-size: var(--t-lead);
  line-height: 1.6;
  color: rgba(36, 21, 40, 0.78);
}
.about__body > span {
  display: block;
  will-change: opacity, transform;
}
.about__body > span + span { margin-top: 0.55em; }

.about__facts {
  margin-top: var(--sp-7);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-5);
  border-top: 1px solid rgba(140, 101, 32, 0.28);
  padding-top: var(--sp-6);
}
@media (max-width: 640px) {
  .about__facts { grid-template-columns: 1fr; gap: var(--sp-4); }
}
.about__fact strong {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 1.2rem + 1.6vw, 2.5rem);
  font-weight: 500;
  line-height: 1;
  color: var(--gold-700);
}
/* Direct child only. `.about__fact span` also matched the <span> INSIDE the
   <strong>, which set the big number to label size and made its .todo underline
   run the full column width. */
.about__fact > span {
  display: block;
  margin-top: 0.5em;
  font-size: 0.7rem;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: rgba(36, 21, 40, 0.55);
}

/* -- two columns: the owner on the left, the copy on the right ------------- *
   Only once there is real width for them. Below this he sits above the copy at
   a modest height — a full-length figure beside a paragraph on a phone leaves
   both of them too narrow to read.

   ⚠️ This block MUST stay below `.about__body` and `.about__inner`. It overrides
   them at equal specificity, so it only wins by coming later — and `.about__body`
   sets the `margin` SHORTHAND, which silently resets the `margin-inline` set
   here if the order is the other way round. */
/* ⭐ Lowered 900 -> 700. At 900 an 820px tablet fell to the stacked layout and
   the client's note was "he is sitting on top" — the portrait stacked ABOVE the
   copy instead of standing beside it. 700 is also the gate motion.js now uses
   for the pinned scenes, so "two-column" and "animated" start together instead
   of at two different widths. */
@media (min-width: 700px) {
  .about__inner {
    max-width: 1180px;
    display: grid;
    grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
    align-items: center;
    gap: clamp(1.5rem, 4vw, 4.5rem);
    text-align: left;
  }
  .about__inner .label { justify-content: flex-start; }
  .about__portrait { margin: 0; align-self: end; }
  .about__portrait img {
    /* Capped against the stage, not the image: .about__stage is 100svh with
       section padding, and anything taller is clipped by its `overflow: hidden`
       with no warning at all. */
    max-height: min(68svh, 620px);
  }
  .about__body { margin-left: 0; margin-right: 0; }
  .about__copy .cta-row { justify-content: flex-start; }
}

/* A value the client has not given us yet. Visible on purpose, so nobody ships
   a made-up number. Delete the <span class="todo"> wrapper when the real value
   lands — nothing else changes. */
.todo {
  border-bottom: 1px dashed var(--blush-500);
  color: var(--blush-500);
  font-style: normal;
  white-space: nowrap;
}

/* ============================================= 3. WATCH + 4. GALLERY ======= */

.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-5);
  margin-bottom: var(--sp-7);
}
.section-head__text { max-width: 44ch; }
.section-head h2 { font-size: var(--t-h2); }
.section-head .lead { margin-top: var(--sp-3); }

.watch { background: var(--plum-950); }

/* The home page shows the four newest. `auto-fill` gives three across at 1440
   and strands the fourth alone on its own row. Two-up is deliberate: bigger
   frames, no orphan, and it reads as a feature rather than a contact sheet. */
@media (min-width: 760px) {
  .watch .grid-media { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.gallery-strip { background: var(--plum-900); }
/* A deliberately uneven, editorial strip — not the flat even grid that makes
   the reference site read cheap. First tile spans two rows on wide screens. */
/* Column count is FIXED per breakpoint rather than auto-fit, because the first
   tile spans 2x2 and the strip must still end on a flush bottom edge. The lead
   tile eats 4 slots, so 9 items = 12 slots, which divides evenly by 2, 3 and 4.
   `auto-fit` made the count depend on the window width and left a single tile
   hanging in a half-empty final row. Keep the item count at 9 (index.html
   data-limit) if you change these numbers. */
.strip {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-auto-rows: clamp(140px, 24vw, 240px);
  gap: clamp(0.5rem, 0.9vw, 0.9rem);
}
@media (min-width: 700px) { .strip { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1040px) { .strip { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.strip .card { border-radius: var(--radius); }
.strip .card__frame { aspect-ratio: auto; height: 100%; }
.strip .card__meta { display: none; }
.strip > *:first-child { grid-row: span 2; grid-column: span 2; }

/* =========================================================== 5. BOOKING ==== */

.booking {
  position: relative;
  background: var(--plum-950);
}
/* ⭐ SCROLL LENGTH — see .hero. He said this one "didn't look that bad" but to
   bring it down too; left long it becomes the slowest pin on the page. */
html.has-pins .booking { height: 150vh; }

.booking__stage {
  position: sticky;
  top: 0;
  min-height: 100svh;
  display: grid;
  place-items: center;
  overflow: hidden;
  padding-block: var(--section-y);
}
html:not(.has-pins) .booking__stage { position: static; min-height: 0; }

.booking__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
}
.booking__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(0.35) brightness(0.42) saturate(0.8);
  transform: scale(1.08);
  will-change: transform;
}
.booking__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(90% 70% at 50% 50%, rgba(21, 12, 27, 0.55), rgba(21, 12, 27, 0.94));
}

.booking__inner {
  position: relative;
  z-index: 2;
  display: grid;
  place-items: center;
  text-align: center;
  padding-inline: var(--page-pad);
}

/* The gold ring that draws itself as you scroll. */
.booking__ring {
  position: absolute;
  z-index: 1;
  top: 50%;
  left: 50%;
  width: min(620px, 88vw);
  aspect-ratio: 1;
  translate: -50% -50%;
  pointer-events: none;
}
/* ⭐ "It's out of the place where it needs to be."
   The ring is capped at 620px but the copy inside it is not, so between roughly
   700 and 1000px the headline grew to the full column and ran straight through
   both sides of the circle — measured at 820px: ring 105..700, headline
   105..700. Identical. The text was sitting ON the ring, not inside it.
   Capping the copy makes the headline wrap to two lines the way it already does
   on a phone, and everything lands inside the circle. */
@media (max-width: 1000px) {
  .booking__inner { max-width: 30rem; margin-inline: auto; }
}
.booking__ring circle {
  fill: none;
  stroke: var(--gold-400);
  stroke-width: 0.6;
  opacity: 0.7;
  transform: rotate(-90deg);
  transform-origin: 50% 50%;
}
.booking__inner h2 { font-size: var(--t-h1); }
.booking__inner .lead { margin-top: var(--sp-4); }
.booking__inner .btn { margin-top: var(--sp-6); }

.booking__contacts {
  margin-top: var(--sp-7);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-5) var(--sp-7);
  justify-content: center;
  font-size: 0.78rem;
  letter-spacing: var(--track-nav);
  text-transform: uppercase;
  color: var(--fg-dim);
}
.booking__contacts a { transition: color var(--dur) var(--ease-out); }
.booking__contacts a:hover { color: var(--gold-200); }

/* ============================================================== Shared ===== */

.cta-row {
  margin-top: var(--sp-7);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  align-items: center;
}

.loadmore { margin: var(--sp-6) auto 0; display: flex; }

/* Card frame with no usable image — the seal fills the hole rather than a
   broken-image icon. */
.card__frame--noimg {
  background:
    radial-gradient(circle at 50% 45%, rgba(184, 137, 47, 0.22), transparent 62%),
    var(--plum-800);
}
.card__frame--noimg::after {
  content: "";
  position: absolute;
  inset: 22%;
  background: url("../img/seal.webp") center / contain no-repeat;
  opacity: 0.28;
  filter: grayscale(1) brightness(1.5);
}

@media (prefers-reduced-motion: reduce) {
  .hero__cue i { animation: none; opacity: 0.7; }
}
