/* ============================================================
   The sealed invitation — the intro overlay.

   It sits above the page, not in it: fixed, out of the flow, and
   removed from the document the moment it has played, so it can
   never take a scroll or a tap again. It is the only thing on the
   invitation a guest has to open.

   The supplied artwork is one sheet, 853 x 1844, with a vertical
   seam and a wax seal pressed over it. Three things are measured
   from that file and nothing here is guessed:

     seam   x = 455.13 / 853  = 53.356%   (centroid of the groove,
                                           averaged over 430 rows)
     seal   x = 241 / 853,  y = 698 / 1844,
            w = 399 / 853,  h = 414 / 1844
     sheet  853 / 1844 = 0.4625813

   The seal is pressed into the supplied image, so it cannot lift
   off it. It is carried instead as its own sprite over a copy of
   the sheet whose paper has been rebuilt underneath — the two put
   back together are the supplied artwork to within one value per
   channel, so nothing has been redrawn.

     1. Geometry
     2. The overlay
     3. The two leaves
     4. The seal
     5. The control
     6. Phases
     7. Reduced motion
   ============================================================ */

/* ---------- 1. Geometry ----------------------------------- */
/* Both leaves draw the same sheet at the same place in the
   viewport and differ only in where they are cut, so the seam
   cannot drift: it is the cut, not a second copy of a line.

   The four lengths below are the whole arithmetic. Script settles
   the same sums on to whole pixels and writes them back over these
   — a cut on a fraction is antialiased, and the two halves round on
   to their layers separately — so what is here is what the sheet
   falls back to before that runs. */
.intro {
  --ratio:  .4625813;          /* 853 / 1844 */
  --invert: 2.1617820;         /* 1844 / 853 */
  --seam:   .5335638;          /* 455.13 / 853 */

  --seal-x: .2825322;          /* 241 / 853  */
  --seal-y: .3785249;          /* 698 / 1844 */
  --seal-w: .4677608;          /* 399 / 853  */
  --seal-h: .2245119;          /* 414 / 1844 */

  /* The whole sheet is always shown: the prompt at 88-93% down is
     the affordance, and `cover` on a landscape window would crop
     it away entirely. */
  --sheet-h: min(100vh, calc(100vw * var(--invert)));
  --sheet-w: calc(var(--sheet-h) * var(--ratio));
  --sheet-x: calc((100vw - var(--sheet-w)) / 2);
  --sheet-y: calc((100vh - var(--sheet-h)) / 2);
  --seam-x:  calc(var(--sheet-x) + var(--sheet-w) * var(--seam));

  --paper-ink: 20, 15, 10;
}
@supports (height: 100svh) {
  .intro {
    --sheet-h: min(100svh, calc(100vw * var(--invert)));
    --sheet-y: calc((100svh - var(--sheet-h)) / 2);
  }
}

/* ---------- 2. The overlay -------------------------------- */
.intro {
  position: fixed;
  inset: 0;
  z-index: 90;
  overflow: hidden;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  /* Nothing of its own: the black beneath is the void, and once that
     clears it is screen 01 the leaves are opening on to. */
  background: none;
}

/* Black behind the leaves. It is what shows through the seam as it
   parts, and it clears to let screen 01 up as they swing. */
.intro__void {
  position: absolute;
  inset: 0;
  background: var(--ink);
}

.intro__stage {
  position: absolute;
  inset: 0;
  perspective: 1700px;
  perspective-origin: var(--seam-x) 50%;
  transform-origin: 50% 50%;
  /* Held back until the sheet has decoded, so the paper is never
     preceded by a flat rectangle of its own average colour. */
  opacity: 0;
  transition: opacity 620ms ease, transform 190ms cubic-bezier(.4, 0, .2, 1);
}
.intro[data-ready] .intro__stage { opacity: 1; }

/* ---------- 3. The two leaves ----------------------------- */
.intro__leaf {
  position: absolute;
  top: 0;
  bottom: 0;
  overflow: hidden;
}
.intro__leaf--l { left: 0;             width: var(--seam-x); transform-origin: 100% 50%; }
.intro__leaf--r { left: var(--seam-x); right: 0;             transform-origin: 0 50%; }

/* A window taller than the sheet is answered the same way as a wider
   one. Each half carries its own top-to-bottom paper, read off that
   half's own first and last rows, so the band above and below the
   sheet meets it without a step — and swings away with the leaf it
   belongs to rather than staying behind as a lit rectangle. */
.intro__leaf--l { background: linear-gradient(#E0D8CE, #CDC4B8); }
.intro__leaf--r { background: linear-gradient(#D6CDC2, #C7BCAF); }

/* One sheet, drawn twice at one address. */
.intro__sheet {
  position: absolute;
  top: var(--sheet-y);
  width: var(--sheet-w);
  height: var(--sheet-h);
  display: block;
}
.intro__leaf--l .intro__sheet { left: var(--sheet-x); }
.intro__leaf--r .intro__sheet { left: calc(var(--sheet-x) - var(--seam-x)); }
.intro__sheet img { display: block; width: 100%; height: 100%; border: 0; }

/* A window wider than the sheet is not a card floating on a ground:
   the paper simply carries on. Each strip is that edge of the sheet
   read row by row — the same paper and the same fall of light down
   the window — drawn once and stretched, never tiled, so there is
   no width at which a repeat can show. */
.intro__edge {
  position: absolute;
  top: var(--sheet-y);
  height: var(--sheet-h);
  background-repeat: no-repeat;
  background-size: 100% 100%;
}
.intro__leaf--l .intro__edge {
  left: 0;
  width: var(--sheet-x);
  background-image: url('../img/intro-edge-l.jpg');
}
.intro__leaf--r .intro__edge {
  left: calc(var(--sheet-x) + var(--sheet-w) - var(--seam-x));
  right: 0;
  background-image: url('../img/intro-edge-r.jpg');
}

/* The fold falls into shadow as the leaf turns. */
.intro__inner {
  position: absolute;
  top: 0; bottom: 0;
  width: 44%;
  opacity: 0;
}
.intro__leaf--l .intro__inner {
  right: 0;
  background: linear-gradient(to right, rgba(var(--paper-ink), 0), rgba(var(--paper-ink), .46));
}
.intro__leaf--r .intro__inner {
  left: 0;
  background: linear-gradient(to left, rgba(var(--paper-ink), 0), rgba(var(--paper-ink), .46));
}

/* ---------- 4. The seal ----------------------------------- */
.intro__sealwrap {
  position: absolute;
  left:   calc(var(--sheet-x) + var(--sheet-w) * var(--seal-x));
  top:    calc(var(--sheet-y) + var(--sheet-h) * var(--seal-y));
  width:  calc(var(--sheet-w) * var(--seal-w));
  height: calc(var(--sheet-h) * var(--seal-h));
}
.intro__sealwrap img,
.intro__sealwrap picture {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}
/* The cast shadow is the seal itself, blurred once and never again:
   only its opacity and its offset move, so nothing re-rasterises. */
.intro__sealshadow {
  filter: blur(9px) brightness(0);
  opacity: 0;
  transform: translateY(2%) scale(1.01);
}

/* ---------- 5. The control -------------------------------- */
/* The artwork already carries its own prompt, in both languages.
   The control is placed over it rather than drawn on top of it. */
.intro__open {
  position: absolute;
  left:   calc(var(--sheet-x) + var(--sheet-w) * .27356);
  top:    calc(var(--sheet-y) + var(--sheet-h) * .868);
  width:  calc(var(--sheet-w) * .52);
  height: calc(var(--sheet-h) * .075);

  appearance: none; -webkit-appearance: none;
  background: none; border: 0; border-radius: 0; padding: 0; margin: 0;
  color: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.intro__open:focus-visible {
  outline: 1px solid rgba(var(--paper-ink), .55);
  outline-offset: max(6px, .8vh);
}

/* ---------- 6. Phases ------------------------------------- */
/* Phase 1 — touch. The sheet gives a little under the finger and
   comes back; no glow, no bounce. */
.intro[data-phase="press"] .intro__stage { transform: scale(.978); }

/* The opening is driven from script: the sheet fades while the veil
   closes over it, and the sheet is taken out of the document behind
   the black. Nothing here turns, flips or is clipped — the veil does
   all of the moving, on one axis. */

.intro[data-phase="opening"] { cursor: default; }
.intro[data-phase="opening"] .intro__open { pointer-events: none; visibility: hidden; }

/* ---------- 7. Reduced motion ----------------------------- */
/* No press, no lift, no door: the sheet simply gives way. */
@media (prefers-reduced-motion: reduce) {
  .intro__stage { transition: opacity 300ms linear; }
  .intro[data-phase="press"] .intro__stage { transform: none; }
}
