/* ============================================================
   SCREEN 02 — the invitation.

   The photograph's ground is #F9F2E9 measured off the file itself, and
   the page is set to the same value, so the picture has no edge: the
   ivory around the hands simply becomes the page. That is also why type
   may sit over the frame's empty upper and lower thirds — there is
   nothing there to sit on top of.

     1. Tokens
     2. The screen and its ground
     3. Language switch
     4. Type
     5. Composition — phone
     6. Composition — tablet
     7. Composition — desktop
     8. Entrance
     9. Reduced motion
   ============================================================ */

/* ---------- 1. Tokens ------------------------------------- */
:root {
  --ivory:     #F9F2E9;   /* sampled from the photograph */
  --ink-warm:  #17120D;
  --ink-soft:  #5C5347;   /* 6.0:1 on the ivory */
  --ink-faint: #6A6154;   /* 5.1:1 — the date is small, so it must still clear AA */

  /* Latin resolves in Bodoni; Cyrillic falls through to Old Standard. */
  --display: 'Bodoni Display', 'Old Standard Cyrillic', 'Didot', 'Bodoni MT', 'Times New Roman', serif;
  --ui:      'Manrope', ui-sans-serif, system-ui, sans-serif;

  --pad: clamp(1.5rem, 6vw, 5.5rem);
  --gut: clamp(1rem, 2.2vw, 2rem);

  --t-head:  clamp(1.4rem, 6.6vw, 4.75rem);   /* longest line / 12.586 */
  --t-name:  clamp(2.1rem, 9vw, 6rem);        /* longest name / 9.294  */
  --t-link:  clamp(1rem, 3.4vw, 1.9rem);
  --t-body:  clamp(0.9375rem, 0.86rem + 0.5vw, 1.125rem);
  --t-note:  clamp(0.625rem, 0.56rem + 0.28vw, 0.75rem);

  --step: 90ms;   /* one beat of the entrance */
}

/* Cyrillic sets wider than Latin at the same size, and the names may
   not wrap — the breaks are authored. On the narrowest phone the
   floor above is what overruns: 9vw would have fitted, but 2.1rem is
   larger than 9vw below 373px and holds the name too big, so
   МУХАММАД АЛИ runs off the window. Relaxing the floor for Russian
   alone lets the slope govern again. Everything from 340px up, and
   English at every width, is untouched. */
@media (max-width: 339px) {
  :root[data-lang="ru"] { --t-name: clamp(1.7rem, 9vw, 6rem); }
}

/* ---------- 2. The screen and its ground ------------------ */
/* Once the cover has gone, the whole page turns ivory — including the
   overscroll area, so a rubber-band pull never shows black beneath. */
html[data-ground="ivory"], html[data-ground="ivory"] body { background: var(--ivory); }
html { transition: background-color 820ms cubic-bezier(.32,.08,.24,1); }

.screen--invite {
  height: auto;
  min-height: 100svh;
  overflow: visible;
  cursor: default;
  background: var(--ivory);
  color: var(--ink-warm);
  padding: clamp(5rem, 15svh, 10rem) var(--pad) clamp(4rem, 12svh, 8rem);
}

.invite { max-width: 96rem; margin-inline: auto; }

/* ---------- 3. Language switch ---------------------------- */
/* The one persistent control, and the only sans on the screen. */
.lang {
  position: fixed;
  top: max(1.15rem, env(safe-area-inset-top));
  right: clamp(1.25rem, 5vw, 3.25rem);
  z-index: 50;
  display: flex; align-items: center; gap: .45em;
  font-family: var(--ui);
  font-size: var(--t-note);
  font-weight: 400;
  letter-spacing: .22em;
  color: var(--ink-warm);
  opacity: 0;
  pointer-events: none;
  transition: opacity 700ms ease 500ms;
}
html[data-opened] .lang { opacity: 1; pointer-events: auto; }

.lang__b {
  appearance: none; -webkit-appearance: none;
  background: none; border: 0; border-radius: 0; padding: .45em .15em; margin: 0;
  font: inherit; letter-spacing: inherit; color: inherit;
  cursor: pointer; opacity: .38;
  transition: opacity .4s ease;
  -webkit-tap-highlight-color: transparent;
  position: relative;
}
/* Two words set small on purpose, and a thumb that needs somewhere to
   land: each carries an invisible extension out to the 44px a finger
   reliably finds. It is centred on the word, so nothing moves, and it
   stops short of its neighbour so a tap is never ambiguous. */
.lang__b::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: calc(100% + .55rem);
  height: 2.75rem;
}
.lang__b:hover { opacity: .7; }
.lang__b[aria-pressed="true"] { opacity: 1; }
.lang__s { opacity: .3; }
.lang__b:focus-visible { outline: 1px solid var(--ink-warm); outline-offset: 4px; }

/* ---------- 4. Type --------------------------------------- */
/* Bodoni is variable on an optical-size axis: the display cuts take the
   96pt drawing, with its hairline serifs, and the reading size takes the
   11pt drawing, which is built to survive at that size. */
.invite__head, .names__a, .names__b {
  font-family: var(--display);
  font-weight: 400;
  font-variation-settings: 'opsz' 96;
  text-transform: uppercase;
  /* Breaks are authored, so nothing may re-balance them. */
  text-wrap: nowrap;
}

.invite__head {
  font-size: var(--t-head);
  line-height: 1.04;
  letter-spacing: .1em;
  text-indent: .1em;          /* offset the tracking's trailing space */
  margin: 0;
}

.invite__body {
  font-family: var(--display);
  font-weight: 400;
  font-variation-settings: 'opsz' 11;
  font-size: var(--t-body);
  line-height: 1.85;
  color: var(--ink-soft);
  max-width: 36ch;
  margin: clamp(1.5rem, 5vw, 2.75rem) 0 0;
}

.names__a, .names__b { font-size: var(--t-name); line-height: .94; letter-spacing: .03em; margin: 0; }

.names__link {
  font-family: var(--display);
  font-style: italic;
  font-weight: 400;
  font-variation-settings: 'opsz' 24;
  font-size: var(--t-link);
  line-height: 1;
  letter-spacing: .02em;
  color: var(--ink-soft);
  margin: 0;
}

/* Face, size and tracking come from the shared rule in screens.css —
   the day is written the same way on every screen. */
.invite__date {
  color: var(--ink-faint);
  margin: 0;
}

/* ---------- 5. Composition — phone ------------------------ */
/* The film is the window. It breaks out of the page's measure and out of
   the screen's padding to stand a whole viewport tall and a whole
   viewport wide, and the names climb back up into the empty ivory below
   the hands so the two still read as one block.

   The ivory the hands were filmed on was graded on to the page's own
   when the file was made, so what surrounds them is the invitation
   itself: there is no frame, no letterbox and no edge at any size. */
.invite__film {
  position: relative;
  margin: clamp(2rem, 7vw, 3.5rem) calc(50% - 50vw) 0;
  width: 100vw;
  height: 100svh;
  background: var(--ivory);
  display: grid;
  place-items: center;
  overflow: hidden;
  overflow: clip;
}
.invite__film-el {
  display: block;
  width: 100%;
  border: 0;
  background: var(--ivory);
  object-fit: cover;
  object-position: 50% 50%;
  /* Where the window is taller than the film, the film keeps its own
     shape and the page's ivory carries on above and below it; where the
     window is wide, it fills the whole of it. Either way its head and
     foot dissolve rather than end, so the last values of the grade can
     never meet the page as a line. */
  aspect-ratio: 1440 / 812;
  height: auto;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 10%, #000 90%, transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 10%, #000 90%, transparent 100%);
}
@media (min-aspect-ratio: 16/10) {
  .invite__film-el { aspect-ratio: auto; height: 100%; }
}

/* The film carries a transform, which paints it in the positioned layer
   — above ordinary block siblings. The type that overlaps it has to be
   lifted into that layer too, or the frame's ivory covers it. */
.invite__head, .invite__body, .names, .invite__date { position: relative; z-index: 1; }

.names {
  display: grid;
  justify-items: start;
  margin-top: -26vw;          /* into the frame's lower ivory */
}
.names__link { margin: .35em 0 .3em 24%; }
.names__b { justify-self: end; }

.invite__date { margin-top: clamp(2.5rem, 9vw, 4.5rem); }

/* ---------- 6. Composition — tablet ----------------------- */
@media (min-width: 700px) {
  .invite__film { margin-top: clamp(2.5rem, 6vw, 4.5rem); }
  .names { margin-top: -13vw; }
  .names__link { margin-left: 30%; }
  .invite__body { margin-top: clamp(2rem, 4vw, 3rem); }
}

/* ---------- 7. Composition — desktop ---------------------- */
/* The same art direction, re-cut for the horizontal space: the opening
   words hold the upper left, the hands stay the anchor in the middle,
   the message answers them on the right, and the names step down across
   the foot of the picture the way the two arms enter it. */
@media (min-width: 1100px) {
  /* Re-sized for the narrower grid columns, again against the Russian. */
  :root {
    --t-head: clamp(1.8rem, 3.25vw, 3.6rem);
    --t-name: clamp(2.1rem, 5.2vw, 5.6rem);
  }

  .screen--invite { padding-block: clamp(6rem, 13svh, 11rem) clamp(5rem, 11svh, 9rem); }

  .invite {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    column-gap: var(--gut);
    align-items: start;
  }

  /* Opening words upper left, picture the anchor beside them, and the names
     stepping across its foot. The message sits under the heading rather than
     opposite it, which leaves the lower right clear for the names to climb
     into — and keeps the longer Russian setting from ever meeting them. */
  .invite__head  { grid-column: 1 / 6;  grid-row: 1; margin-top: clamp(1rem, 4vw, 4rem); }
  .invite__body  { grid-column: 1 / 5;  grid-row: 2; margin-top: clamp(1.5rem, 3vw, 2.75rem); }
  /* The film is not a column of the grid — it is the window the grid is
     printed on, so it takes the whole width and its own row. */
  .invite__film {
    grid-column: 1 / 13; grid-row: 3;
    margin-top: clamp(3rem, 6vw, 5rem);
  }

  .names {
    grid-column: 1 / 13; grid-row: 4;
    grid-template-columns: repeat(12, 1fr);
    column-gap: var(--gut);
    align-self: end;
    margin-top: 0;
  }
  .names__a    { grid-column: 1 / 9;  grid-row: 1; justify-self: start; }
  .names__link { grid-column: 5 / 9;  grid-row: 2; justify-self: start; margin: .4em 0 .35em; }
  .names__b    { grid-column: 6 / 13; grid-row: 3; justify-self: end; }

  .invite__date { grid-column: 1 / 5; grid-row: 5; margin-top: clamp(2rem, 4vw, 3.5rem); }
}

@media (min-width: 1600px) {
  .invite { max-width: 100rem; }
  .invite__head  { grid-column: 1 / 5; }
  .invite__body  { grid-column: 1 / 4; }
}

/* ---------- 8. Entrance ----------------------------------- */
/* Out of the black, in the order the eye should take them:
   heading, message, hands, one name, the word between, the other, date. */
.screen--invite { opacity: 0; transition: opacity 820ms cubic-bezier(.32,.08,.24,1); }
html[data-opened] .screen--invite { opacity: 1; }

.screen--invite [data-r] {
  opacity: 0;
  transform: translateY(14px);
  transition:
    opacity 1000ms cubic-bezier(.32,.08,.24,1) calc(var(--i, 0) * var(--step)),
    transform 1000ms cubic-bezier(.32,.08,.24,1) calc(var(--i, 0) * var(--step));
}
.screen--invite.is-in [data-r] { opacity: 1; transform: none; }

/* The film settles rather than slides — a slow finish on the last two
   per cent of a scale, which reads as a lens coming to rest. It is the
   picture inside the frame that is scaled, so the frame itself never
   grows past the window. */
.invite__film-el {
  opacity: 0;
  transform: scale(1.022);
  transition:
    opacity 1500ms cubic-bezier(.32,.08,.24,1) calc(var(--i, 0) * var(--step)),
    transform 2200ms cubic-bezier(.22,.61,.24,1) calc(var(--i, 0) * var(--step));
}
.screen--invite.is-in .invite__film-el { opacity: 1; transform: scale(1); }

/* ---------- 9. Reduced motion ----------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { transition: none; }
  .screen--invite,
  .screen--invite [data-r],
  .invite__film-el {
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
    transform: none !important;
  }
  .screen--invite.is-in [data-r],
  .screen--invite.is-in .invite__film-el { opacity: 1; }
}
