/* Colours are taken from the save-the-date film, so the video blends into the page.
   Fonts match the film: Courier Prime like the record's lettering, and Cedarville
   Cursive like its "Stay tuned...". All text is the same dark grey. */
:root {
  --paper: #d4c6b0;
  --paper-light: #e2d6c4;
  --ink: #333230;
  --terracotta: #c9604f; /* The small record's label, matching the film. */
  /* The main colour, for the Send button and highlights. Small text and thin lines on
     the paper use the deeper shade, which stays readable. */
  --olive: #888f4c;
  --olive-deep: #4a4f28;
  --line: #a99a82;
  --font-body: 'Courier Prime', 'Courier New', monospace;
  --font-script: 'Cedarville Cursive', cursive;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  /* The page colour sits behind the scrollbar too, so its track blends in. */
  background: var(--paper);
  /* A thin, translucent dark grey scrollbar instead of the browser's grey and white one. */
  scrollbar-width: thin;
  scrollbar-color: rgb(51 50 48 / .35) transparent;
}

/* Browsers without scrollbar-color, such as older Safari, use these instead. */
@supports not (scrollbar-color: auto) {
  ::-webkit-scrollbar { width: 6px; height: 6px; background: transparent; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb {
    border-radius: 999px;
    background: rgb(51 50 48 / .35);
  }
  ::-webkit-scrollbar-thumb:hover { background-color: rgb(51 50 48 / .55); }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 1.0625rem/1.55 var(--font-body);
}

.layout {
  display: grid;
  gap: 8px;
  max-width: 460px;
  margin: 0 auto;
  padding: 0 0 64px;
}

.film__frame {
  position: relative;
  max-width: 420px;
  margin: 0 auto;
}

.film__video {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: auto; /* From the width and height in the HTML. */
}

/* Browsers render video colours slightly differently from the page, so the film's
   plain edges would show as a lighter rectangle. A page-coloured fade laid over the
   edges hides it. (A mask on the video itself left it invisible in some browsers.) */
.film__frame::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    linear-gradient(to right, var(--paper), transparent 6%, transparent 94%, var(--paper)),
    linear-gradient(to bottom, var(--paper), transparent 4%, transparent 96%, var(--paper));
}

/* Music on/off, bottom left of the film. A crossed-out note while it's off. */
.film__music {
  position: absolute;
  z-index: 3;
  /* Beside the record player's arm, just past the record's edge. The film always
     keeps the same shape, so percentages land on the same spot at any size. */
  left: calc(90% - 22px);
  top: calc(85% - 22px);
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1.5px solid rgb(51 50 48 / .55);
  border-radius: 50%;
  background: rgb(226 214 196 / .88);
  color: var(--ink);
  cursor: pointer;
  transition: background-color .2s ease, border-color .2s ease;
}
.film__music svg {
  /* The mouse only ever lands on the button, so the pointer doesn't flicker. */
  pointer-events: none;
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.film__music-cross { stroke: var(--olive-deep); stroke-width: 2.2; }
.film__music[aria-pressed='true'] .film__music-cross { display: none; }
/* A gentle pulse while music plays. */
.film__music[aria-pressed='true'] svg { animation: music-pulse 1.6s ease-in-out infinite; }
@keyframes music-pulse { 50% { transform: scale(1.12); } }
@media (hover: hover) {
  .film__music:hover { background: var(--paper-light); border-color: var(--ink); }
}
.film__music:focus-visible { outline: 3px solid var(--olive-deep); outline-offset: 2px; }

.film__replay {
  position: absolute;
  z-index: 3;
  right: 12px;
  bottom: 12px;
  padding: 4px 10px;
  border: 0;
  background: none;
  color: var(--ink);
  font: inherit;
  font-size: .875rem;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.invite { padding: 0 24px; text-align: center; }

.greeting {
  margin: 24px 24px 0;
  font: 400 clamp(1.75rem, 8vw, 2.25rem)/1.4 var(--font-script);
  text-align: center;
  opacity: 0;
  transition: opacity .5s ease;
}
.greeting:empty { display: none; }
/* On invite links, keep the greeting's space while the name loads, so the film
   doesn't jump down when it appears. */
.greeting.is-reserved { display: block; min-height: 1.4em; }
.greeting.is-visible { opacity: 1; }

.letter { margin: 0 0 24px; }
.letter p { margin: 0 0 14px; }
.letter .signature {
  margin: 20px 0 0;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
}

.details {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
  gap: 16px;
  margin: 0 0 16px;
  padding: 16px 0;
  border-block: 1px solid var(--line);
}
.details dt {
  margin-bottom: 4px;
  font: 700 .8125rem/1.3 var(--font-body);
  letter-spacing: .14em;
  text-transform: uppercase;
}
.details dd { margin: 0; font-weight: 700; line-height: 1.35; }

.small { margin: 0; font-size: .9375rem; line-height: 1.5; }

.panel { margin-top: 32px; }
.panel__title { margin: 0 0 16px; font: 700 1.375rem/1.25 var(--font-body); }

.state { display: grid; gap: 12px; justify-items: center; }
#state-form { justify-items: stretch; gap: 20px; }
#state-form .panel__title { margin: 0; }

.choices { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }

.choice { position: relative; display: block; cursor: pointer; }
.choice input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
.choice span {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 60px;
  padding: 8px;
  border: 1.5px solid var(--ink);
  border-radius: 999px;
  font-weight: 700;
  line-height: 1.2;
  transition: background-color .15s ease, color .15s ease;
}
.choice input:checked + span { background: var(--ink); color: var(--paper-light); }
.choice input:focus-visible + span { outline: 3px solid var(--olive-deep); outline-offset: 2px; }
@media (hover: hover) {
  .choice:hover input:not(:checked) + span { background: var(--paper-light); }
}

.field { display: grid; gap: 8px; justify-items: center; }
.field__label { font-weight: 700; }
.required { color: var(--olive-deep); }

.identity { display: grid; gap: 16px; }
.field--wide { justify-items: stretch; text-align: left; }
.phone-row { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); gap: 12px; align-items: end; }
.trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

input[type='text'], input[type='email'], input[type='tel'], textarea {
  width: 100%;
  padding: 12px;
  border: 1.5px solid var(--ink);
  border-radius: 12px;
  background: var(--paper-light);
  color: var(--ink);
  font: inherit;
  font-size: 1rem; /* Below 16px, iPhones zoom in when the box is tapped. */
  resize: vertical;
}

.button { font: inherit; font-weight: 700; cursor: pointer; }
.button--primary {
  width: 100%;
  min-height: 56px;
  padding: 12px 24px;
  border: 0;
  border-radius: 999px;
  background: var(--olive);
  color: #fff;
}
.button--primary:disabled { opacity: .45; cursor: not-allowed; }
.button--link {
  min-height: 44px;
  padding: 8px 12px;
  border: 0;
  background: none;
  color: var(--ink);
  font-weight: 400;
  text-decoration: underline;
  text-underline-offset: 3px;
}
:is(button, textarea, input):focus-visible { outline: 3px solid var(--olive-deep); outline-offset: 2px; }

.error { margin: 0; color: var(--olive-deep); font-weight: 700; }

/* A small vinyl record, echoing the one in the film. */
.record {
  width: 88px;
  height: 88px;
  border-radius: 50%;
  background:
    radial-gradient(circle, var(--paper) 0 3px, var(--terracotta) 4px 26%, transparent 27%),
    repeating-radial-gradient(circle, #1b1a17 0 2px, #2c2a25 3px 4px);
}
.record--small { width: 44px; height: 44px; }
.record--spin { animation: spin 1.8s linear infinite; }
.record--muted { filter: grayscale(.7); }
@keyframes spin { to { transform: rotate(360deg); } }

.state--done .panel__title { margin: 4px 0 0; }

/* ---------- After sending: the screen grows out of the Send button ----------
   1. The screen starts as the button's own rectangle and colour, just underneath it,
      and grows to fill the screen, turning the beige of the couple video as it goes.
   2. Once it fills the screen, the button fades away.
   3. Then the couple drop into their card and the words wipe in. */

html.is-revealed { overflow: hidden; }

.reveal {
  /* The couple video's own background, so the video meets the screen without an edge. */
  --card: #d4c4ac;
  position: fixed;
  inset: 0;
  z-index: 10;
  display: grid;
  align-content: center;
  overflow-y: auto;
  background: var(--olive);
  color: var(--ink);
  /* --top, --right, --bottom, --left and --round are set from the button's position
     and shape, so the screen starts exactly where the button is. */
  clip-path: inset(var(--top, 50%) var(--right, 50%) var(--bottom, 50%) var(--left, 50%) round var(--round, 0px));
  transition: clip-path 1.4s cubic-bezier(.65, 0, .35, 1), background-color 1.4s ease;
}
/* Grows out in the button's green, then turns the video's own beige, so the couple
   stand on the page itself with no edge around them. */
.reveal.is-open { clip-path: inset(0 round 0); background-color: var(--card); }

/* The same beige as the screen, so only the couple show. The couple land in its lower
   part. */
.reveal__card {
  position: relative;
  width: min(56vw, 26vh, 210px);
  margin: 0 auto 4px;
  background: var(--card);
  opacity: 0;
}
.reveal.is-settled .reveal__card { animation: rise-in .6s ease both; }
.reveal__media { display: block; width: 100%; height: auto; }
/* Covers the video with the same frames in the page's own colours (see app.js). The
   video stays underneath, because iPhones pause videos they think are hidden. */
.reveal__canvas { position: absolute; inset: -1px; width: calc(100% + 2px); height: calc(100% + 2px); }

.reveal__content {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  padding: 40px 24px calc(40px + env(safe-area-inset-bottom));
  text-align: center;
  /* Fades out quickly before the screen closes. Coming in, each line wipes in below. */
  opacity: 0;
  transition: opacity .35s ease;
}
.reveal.is-settled .reveal__content { opacity: 1; }

/* Each line is uncovered from left to right, one after another. The extra room in
   the inset keeps the handwriting's loops from being cut off. */
@keyframes wipe-in {
  from { clip-path: inset(-20% 100% -20% -10%); opacity: .2; }
  to { clip-path: inset(-20% -10% -20% -10%); opacity: 1; }
}
/* The buttons arrive last, with a gentle fade and a short slide. */
@keyframes rise-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}
.reveal__name, .reveal__title, .reveal__text, .reveal__status, .reveal__actions { opacity: 0; }
.reveal.is-settled :is(.reveal__name, .reveal__title, .reveal__text, .reveal__status) {
  animation: wipe-in .9s cubic-bezier(.45, 0, .25, 1) both;
}
.reveal.is-settled .reveal__title { animation-delay: .25s; }
.reveal.is-settled :is(.reveal__text, .reveal__status) { animation-delay: .5s; animation-duration: 1.1s; }
/* Runs again whenever the buttons appear, including after a slow save. */
.reveal.is-settled .reveal__actions { animation: rise-in .7s ease 1.2s both; }

/* A copy of the Send button, held above the screen while it grows. */
.reveal-button {
  position: fixed;
  z-index: 11;
  margin: 0;
  transition: opacity .5s ease;
}
.reveal-button.is-leaving { opacity: 0; }

.reveal__status { margin: 0 0 8px; font-size: .9375rem; font-style: italic; }

.reveal__name { margin: 0 0 4px; font: 400 clamp(2rem, 9vw, 2.75rem)/1.3 var(--font-script); }
.reveal__name:empty { display: none; }
.reveal__title { margin: 0 0 12px; font: 700 1.5rem/1.25 var(--font-body); outline: none; }
.reveal__text { margin: 0 0 16px; font-size: 1rem; line-height: 1.55; }
.reveal__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 4px 16px; }
.reveal .button--link { color: var(--ink); font-weight: 700; }

@media (prefers-reduced-motion: reduce) {
  .record--spin, .film__music[aria-pressed='true'] svg { animation: none; }
  .greeting, .choice span { transition: none; }
  /* A plain fade instead of the growing screen. */
  .reveal, .reveal.is-open { clip-path: none; }
  .reveal { opacity: 0; transition: opacity .3s ease; }
  .reveal.is-open { opacity: 1; }
  .reveal__content { transition: none; }
  .reveal.is-settled :is(.reveal__card, .reveal__name, .reveal__title, .reveal__text, .reveal__status, .reveal__actions) {
    animation: none;
    opacity: 1;
  }
}

/* On wide screens the film sits beside the invitation. */
@media (min-width: 860px) {
  .layout {
    grid-template-columns: minmax(0, 420px) minmax(0, 440px);
    /* The empty first and last rows centre the greeting and letter vertically. */
    grid-template-rows: 1fr auto auto 1fr;
    grid-template-areas: "film ." "film greeting" "film invite" "film .";
    column-gap: 64px;
    row-gap: 0;
    justify-content: center;
    max-width: none;
    min-height: 100vh;
    padding: 32px;
  }
  .film { grid-area: film; align-self: center; }
  .greeting { grid-area: greeting; margin: 0 0 12px; text-align: left; }
  .invite { grid-area: invite; padding: 0; text-align: left; }
  .details { justify-items: start; }
  .state { justify-items: start; }
  .field { justify-items: start; }
}
