/* M1 — the WebGL stage.

   Sits inside .envelope-content alongside the 2D SVG envelope, hidden until
   the model has actually loaded and drawn. The swap is one class on the
   overlay (.is-3d), so the fallback is not a separate code path — it is simply
   what happens when that class never arrives. */

.envelope-3d {
  position: absolute;
  inset: 0;
  /* The trigger button and the tap hint sit above this. */
  z-index: 0;
}

.envelope-3d canvas {
  display: block;
  width: 100%;
  height: 100%;
  /* The canvas is decoration; the button over it takes the interaction. */
  pointer-events: none;
}

/* Only once 3D is confirmed running does the drawn envelope stand down.
   Written as "hide the 2D" rather than "show the 3D" on purpose: if the class
   is never added, the page is in its known-good state. */
   Hiding only the SVG was not enough. The drawn tap-to-open button and the 2D
   scratch card kept running on their own timer, on top of the 3D sequence -
   which is what made the old animation look like it had simply replaced the
   new one. The whole drawn intro goes. */
.envelope-intro.is-3d .envelope-stage,
.envelope-intro.is-3d #envelopeReveal {
  display: none;
}

/* Set the moment we know 3D is coming, before the model has downloaded. The
   drawn envelope goes immediately rather than sitting there for a second and
   a half looking like the final answer. Removed again only if 3D fails, which
   hands the drawn version straight back. */
.envelope-intro.is-3d-pending .envelope-stage,
.envelope-intro.is-3d-pending #envelopeReveal {
  display: none;
}

/* The weave — beats 2-3. An SVG layer over the WebGL canvas, transparent, so
   the monogram is drawn in front of the envelope rather than beside it.
   `transform` is written from JS every frame during the handoff, so there is
   deliberately no CSS transition on it: a transition here would fight the
   per-frame value and lag the monogram behind the seal it is flying into. */
.weave-layer {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  transform-origin: 50% 50%;
  will-change: transform, opacity;
}

.weave-layer svg {
  width: 100%;
  height: 100%;
  display: block;
  overflow: visible;
}

/* `revealed` belongs to the DRAWN timeline. In 3D mode that timeline is inert,
   so this must not fire - it would fade the WebGL canvas out mid-sequence. */
.envelope-intro.revealed:not(.is-3d) .envelope-3d {
  opacity: 0;
  transition: opacity 0.5s ease;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  /* The module refuses to boot under reduced motion, so this is belt and
     braces for a preference that changes after load. */
  .envelope-3d { display: none; }
}

/* ---------- beat 5: turning the card ---------- */
.spin-surface {
  position: absolute;
  inset: 0;
  z-index: 3;
  background: none;
  border: none;
  padding: 0;
  cursor: grab;
  /* The drag has to belong to the card, not to the page scroll underneath. */
  touch-action: none;
}

.spin-surface:active { cursor: grabbing; }

.spin-hint {
  position: absolute;
  left: 50%;
  bottom: 14%;
  transform: translateX(-50%);
  z-index: 4;
  margin: 0;
  font-size: var(--text-xs);
  letter-spacing: 0.14em;
  text-transform: lowercase;
  color: var(--blue-deep);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s ease;
}

.spin-hint.visible { opacity: 0.85; }

/* ---------- beats 6-7: the venue scratch, over the card ---------- */
.venue-reveal {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* JS writes `transform` every frame to follow the projected card, so there
     is deliberately no transition on it — a transition would lag the overlay
     behind the thing it is supposed to be stuck to. */
  will-change: transform, opacity;
}

.venue-card {
  width: min(78vw, 23rem);
  aspect-ratio: 4 / 3;
}

/* The photograph fills the whole scratched area rather than sitting as a
   thumbnail with text under it — the reveal should feel like the foil was
   hiding a window onto the place, not a caption. */
.venue-card .scratch-under {
  padding: 0;
  display: block;
}

.venue-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Softened just enough that white type sits on it comfortably. Any more and
     it stops reading as a real place. */
  filter: blur(2px) saturate(1.05);
  transform: scale(1.06);   /* hides the blur's soft edge against the frame */
}

/* The name sits in a white panel along the bottom, so it never depends on
   what happens to be behind it in the photograph. */
.venue-plate {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 0.7rem 1rem 0.85rem;
  background: rgba(255, 255, 255, 0.9);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  text-align: center;
}

.venue-card .scratch-eyebrow {
  margin: 0 0 0.15rem;
  color: var(--blue-deep);
}

.venue-name {
  font-family: var(--font-display);
  font-size: var(--text-md);
  font-weight: 600;
  letter-spacing: 0.04em;
  margin: 0;
  color: var(--ink);
}

/* ---------- the dock ---------- */
.intro-dock {
  position: fixed;
  right: max(1rem, env(safe-area-inset-right, 0px));
  bottom: max(1rem, env(safe-area-inset-bottom, 0px));
  z-index: 900;
  width: 76px;
  height: 76px;
  min-width: 44px;
  min-height: 44px;
  padding: 6px;
  background: var(--ivory);
  border: 1px solid var(--gold);
  border-radius: 10px;
  box-shadow: 0 10px 26px rgba(44, 42, 38, 0.22);
  cursor: pointer;
  opacity: 0;
  transform: translateY(14px) scale(0.9);
  transition: opacity 0.45s var(--ease-out), transform 0.45s var(--ease-out);
}

.intro-dock.visible { opacity: 1; transform: none; }
.intro-dock:hover { border-color: var(--gold-deep); }
.intro-dock img { width: 100%; height: 100%; object-fit: contain; }

/* ---------- the save-the-date panel ---------- */
.save-panel {
  position: fixed;
  right: max(1rem, env(safe-area-inset-right, 0px));
  bottom: calc(76px + max(1rem, env(safe-area-inset-bottom, 0px)) + 0.6rem);
  z-index: 901;
  width: min(92vw, 22rem);
  padding: 1.4rem 1.3rem 1.5rem;
  background: var(--ivory);
  border: 1px solid var(--gold);
  border-radius: 10px;
  box-shadow: 0 18px 44px rgba(44, 42, 38, 0.26);
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.25s ease, transform 0.25s var(--ease-out);
}

.save-panel.open { opacity: 1; transform: none; }

.save-panel-close {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 44px;
  height: 44px;
  background: none;
  border: none;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--blue-deep);
  cursor: pointer;
}

.save-panel-title {
  font-family: var(--font-display);
  font-size: var(--text-md);
  margin: 0 0 0.4rem;
  padding-right: 2rem;
}

.save-panel-note {
  font-size: var(--text-sm);
  margin: 0 0 1rem;
  opacity: 0.85;
}

.save-label {
  display: block;
  font-size: var(--text-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--blue-deep);
  margin-bottom: 0.35rem;
}

.save-input {
  width: 100%;
  min-height: 44px;
  padding: 0.6rem 0.75rem;
  font-family: inherit;
  font-size: var(--text-base);
  color: var(--ink);
  background: #fff;
  border: 1px solid var(--blue-mist);
  border-radius: 4px;
}

.save-input:focus-visible { outline: 2px solid var(--blue-deep); outline-offset: 2px; }
.save-input[aria-invalid='true'] { border-color: #a3392f; }

.save-error {
  font-size: var(--text-xs);
  color: #a3392f;
  margin: 0.5rem 0 0;
}

.save-submit {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  width: 100%;
  min-height: 44px;
  margin-top: 0.9rem;
  padding: 0.7rem 1rem;
  font-family: inherit;
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ivory);
  background: var(--blue-deep);
  border: none;
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.2s ease, opacity 0.2s ease;
}

.save-submit:hover:not(:disabled) { background: #3a5568; }
.save-submit:disabled { opacity: 0.65; cursor: progress; }

.save-thanks {
  font-size: var(--text-sm);
  color: var(--blue-deep);
  margin: 0;
}

@media (max-width: 420px) {
  .intro-dock { width: 62px; height: 62px; }
  .save-panel { bottom: calc(62px + 1.6rem); }
}
