/* solanodangwedding.com — palette and type match the printed suite. */

:root {
  --accent: #6B7A5E;
  --accent-dark: #4E5A44;
  --cream: #FBF8F1;
  --white: #FFFFFF;
  --ink: #3B3A33;
  --ink-2: #5A574C;
  --muted: #8A8474;
  --line: #E4DCC9;
  --line-map: #DDD5C2;
  --script: 'Pinyon Script', cursive;
  --serif: 'Cormorant Garamond', Georgia, 'Times New Roman', serif;
}

* { box-sizing: border-box; }

/* .f sets display:flex, which would otherwise beat the UA rule for [hidden]
   and leave the plus-one block on screen for everyone. */
[hidden] { display: none !important; }

html { scroll-behavior: smooth; scroll-padding-top: 76px; }

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--serif);
  font-size: 18px;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); }
a:hover { color: var(--accent-dark); }

img { max-width: 100%; }

/* ---------- nav ---------- */

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(251, 248, 241, 0.92);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 48px;
}

.nav-mark {
  font-family: var(--script);
  font-size: 56px;
  color: var(--accent);
  line-height: 1;
  text-decoration: none;
  flex: 0 0 auto;
  white-space: nowrap;   /* narrow phones were breaking it after the ampersand */
}

.nav-links { display: flex; gap: 36px; flex-wrap: wrap; }

.nav-links a {
  font-size: 14px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--muted);
  white-space: nowrap;
}

.nav-links a:hover { color: var(--accent); }

/* ---------- shared ---------- */

.section { padding: 88px 48px; }

.band {
  background: var(--white);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: 88px 48px;
}

.wrap { max-width: 980px; margin: 0 auto; }
.col { display: flex; flex-direction: column; gap: 48px; }

.head { text-align: center; display: flex; flex-direction: column; gap: 8px; }

.script {
  font-family: var(--script);
  font-weight: 400;
  font-size: 48px;
  color: var(--accent);
  line-height: 1;
  margin: 0;
}

.script.sm { font-size: 34px; }

.label {
  font-size: 15px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
}

.rule { width: 56px; height: 1px; background: var(--accent); }

.frame {
  border: 1px solid var(--line);
  padding: 14px;
  background: var(--white);
}

.frame img, .cell img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A photo that has not been dropped in yet renders as a quiet cream panel
   with its label, instead of a broken-image icon. */
img.missing {
  object-fit: contain;
  background: var(--cream) url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1' height='1'%3E%3C/svg%3E") center/0 no-repeat;
  position: relative;
}

.slot {
  width: 100%;
  height: 100%;
  background: var(--cream);
  border: 1px dashed var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 16px;
  font-size: 13px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---------- hero ---------- */

.hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 88px 32px 72px;
  gap: 22px;
}

.eyebrow {
  font-size: 14px;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--muted);
}

.names { display: flex; flex-direction: column; gap: 4px; align-items: center; }

.name {
  font-family: var(--script);
  font-size: clamp(56px, 8vw, 96px);
  line-height: 1.1;
  color: var(--accent);
}

.conj { font-size: 26px; font-style: italic; color: var(--muted); }

.datel {
  font-size: clamp(16px, 3.2vw, 22px);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink);
}

.subline { font-size: 17px; font-style: italic; color: var(--muted); }

/* The hero band. It is a direct child of the page, so 100% is already the full
   width -- no 100vw, no negative margins, and nothing for the body to clip. */
.hero-frame {
  width: 100%;
  height: min(82vh, 780px);
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
}

.hero-frame img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: 100%;
  object-fit: contain;
  display: block;
}

/* ---------- our story ---------- */

/* The portrait sits at the top of the column, not floating in the middle of
   it: Our Story is a long read now, and centring a 400px picture against 450
   words of prose leaves it stranded halfway down. */
.story { display: flex; gap: 56px; align-items: flex-start; flex-wrap: wrap; }

.story-frame {
  width: 320px;
  height: 400px;
  flex-shrink: 0;
  padding: 12px;
  background: var(--cream);
  /* Our Story runs about 450 words. Pinned at the top, the portrait would
     leave a tall empty column beside most of them, so it travels down with
     the reader instead. 96px clears the fixed nav. */
  position: sticky;
  top: 96px;
}

.story-text {
  flex: 1;
  min-width: 300px;
  max-width: 62ch;          /* a readable measure; the column is wider than one */
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.story-text p { margin: 0; font-size: 18px; line-height: 1.72; color: var(--ink-2); }

/* Betty, in the middle of the paragraph she belongs to. */
.story-figure { margin: 6px 0; }

.story-figure img {
  width: 100%;
  display: block;
  border-radius: 2px;
  border: 1px solid rgba(120, 100, 70, 0.16);
}

.story-figure figcaption {
  margin-top: 9px;
  font-size: 14px;
  font-style: italic;
  letter-spacing: 0.04em;
  color: var(--muted);
}

.placeholder { font-style: italic; }

/* ---------- details ---------- */

/* Four cards, so 2x2 rather than the 3+1 an auto-fit grid gives at 980px. */
.cards {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
}

.card {
  background: var(--white);
  border: 1px solid var(--line);
  padding: 36px 32px;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.band .card { background: var(--cream); }

.card-label {
  font-size: 14px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--accent);
}

.card-big { font-size: 22px; color: var(--ink); }
.card-mid { font-size: 18px; color: var(--ink-2); }
.card-em { font-size: 16px; font-style: italic; color: var(--muted); }

.finding {
  display: flex;
  gap: 32px;
  align-items: center;
  background: var(--white);
  border: 1px solid var(--line);
  padding: 32px;
  flex-wrap: wrap;
}

.finding-text { flex: 1; min-width: 280px; display: flex; flex-direction: column; gap: 10px; }
.finding-text p { margin: 0; font-size: 18px; line-height: 1.65; color: var(--ink-2); }
.finding-text .fine { font-size: 16px; color: var(--muted); }

.map { width: 340px; max-width: 100%; border: 1px solid var(--line-map); }

/* ---------- the day ---------- */

.timeline {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: 620px;
  width: 100%;
}

.timeline li {
  display: flex;
  gap: 24px;
  align-items: baseline;
  padding: 16px 0;
  border-bottom: 1px solid var(--line);
}

.timeline li:last-child { border-bottom: 0; }

.timeline .t {
  flex: 0 0 130px;
  font-size: 14px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
}

.timeline .e { font-size: 19px; color: var(--ink-2); }

.note { font-size: 16px; font-style: italic; color: var(--muted); margin: 0; }
.center { text-align: center; }

/* ---------- photos ---------- */

.grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
}

.cell { height: 300px; }

/* ---------- the photo gallery ----------
   One horizontal run rather than nine stacked grids. Everything is present
   from the start, so nothing has to be revealed: you either slide the strip or
   pick a year from the tabs above it, which scroll the strip to that year.

   Every tile is the same height with its natural width. That is what a contact
   strip looks like, and it lets a portrait and a landscape sit side by side
   without cropping either. */
.pg { position: relative; }

/* ---- the years ---- */
.pg-years {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
  margin: 0 0 22px;
}

.pg-yr {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font: inherit;
  font-size: 14px;
  letter-spacing: 0.12em;
  color: var(--muted);
  background: none;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 8px 16px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.16s ease, border-color 0.16s ease, background 0.16s ease;
}

.pg-yr em { font-style: normal; font-size: 11px; opacity: 0.6; }

.pg-yr:hover { color: var(--ink); border-color: var(--line); }

.pg-yr.is-on {
  color: var(--white);
  background: var(--accent);
  border-color: var(--accent);
}

.pg-yr.is-on em { opacity: 0.75; }
.pg-yr:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* ---- the strip ---- */
.pg-rail { position: relative; }

.pg-track {
  display: flex;
  gap: 10px;
  /* The offsetParent for every tile. Without this their offsetLeft is measured
     from .pg instead, which is not the scroll origin, so jumping to a year
     landed a screen short and the wrong tab lit up. */
  position: relative;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-behavior: smooth;
  scroll-snap-type: x proximity;
  padding: 2px 0 16px;
  /* The scrollbar is left out; the arrows and the tabs say which way to go. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.pg-track::-webkit-scrollbar { display: none; }
.pg-track:focus-visible { outline: 3px solid var(--accent); outline-offset: 4px; }

.pg-cell {
  position: relative;
  flex: 0 0 auto;
  height: 400px;
  width: auto;
  margin: 0;
  padding: 0;
  border: 0;
  background: var(--cream);
  overflow: hidden;
  border-radius: 2px;
  cursor: zoom-in;
  scroll-snap-align: start;
}

.pg-cell img {
  height: 100%;
  width: auto;
  max-width: none;
  display: block;
  /* With the intrinsic size on the tag, the browser can work out the width
     from the height before the file arrives. Without it a lazily-loaded strip
     measures as zero wide and the arrows switch themselves off. */
  transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.pg-cell:hover img,
.pg-cell:focus-visible img { transform: scale(1.04); }
.pg-cell:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }

.pg-when {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 26px 12px 9px;
  font-size: 12px;
  letter-spacing: 0.06em;
  color: #fff;
  text-align: left;
  background: linear-gradient(to top, rgba(20, 16, 10, 0.72), transparent);
  opacity: 0;
  transition: opacity 0.28s ease;
  pointer-events: none;
}

.pg-cell:hover .pg-when,
.pg-cell:focus-visible .pg-when { opacity: 1; }

/* ---- the arrows ---- */
.pg-nav {
  position: absolute;
  top: 200px;
  transform: translateY(-50%);
  z-index: 3;
  width: 46px; height: 46px;
  border-radius: 50%;
  border: 0;
  background: rgba(251, 248, 241, 0.94);
  color: var(--ink);
  font-size: 21px;
  line-height: 1;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 4px 16px rgba(60, 50, 35, 0.3);
  transition: opacity 0.2s ease;
}

.pg-prev { left: -10px; }
.pg-next { right: -10px; }
.pg-nav:disabled { opacity: 0; pointer-events: none; }
.pg-nav:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }

@media (max-width: 760px) {
  .pg-years { gap: 4px; margin-bottom: 16px; }
  .pg-yr { font-size: 12px; padding: 7px 12px; letter-spacing: 0.08em; }
  .pg-yr em { display: none; }
  .pg-cell { height: 260px; }
  .pg-cell:hover img { transform: none; }
  .pg-when { display: none; }
  .pg-nav { top: 130px; width: 40px; height: 40px; font-size: 19px; }
  .pg-prev { left: -6px; }
  .pg-next { right: -6px; }
}

@media (prefers-reduced-motion: reduce) {
  .pg-track { scroll-behavior: auto; }
  .pg-cell img { transition: none; }
}

/* ---------- the lightbox ---------- */

.lb { position: fixed; inset: 0; z-index: 100; display: none; }
.lb.is-on { display: block; }

.lb-veil { position: absolute; inset: 0; background: rgba(22, 18, 12, 0.95); }

.lb-stage {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 60px 104px 84px;
}

.lb-stage img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: 2px;
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.55);
}

.lb-bar {
  position: absolute;
  left: 0; right: 0; bottom: 26px;
  text-align: center;
  color: rgba(255, 253, 248, 0.82);
  font-size: 14px;
  letter-spacing: 0.06em;
  pointer-events: none;
}

.lb-when { font-style: italic; }
.lb-count { margin-left: 14px; color: rgba(255, 253, 248, 0.5); }

.lb-btn {
  position: absolute;
  width: 52px; height: 52px;
  border-radius: 50%;
  border: 0;
  background: rgba(251, 248, 241, 0.92);
  color: var(--ink);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
}

.lb-btn:disabled { opacity: 0.28; cursor: default; }
.lb-btn:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }

.lb-prev { left: 22px; top: 50%; transform: translateY(-50%); }
.lb-next { right: 22px; top: 50%; transform: translateY(-50%); }
.lb-close { right: 22px; top: 22px; font-size: 21px; }

@media (max-width: 760px) {
  .pg { gap: 34px; }
  .pg-grid {
    grid-template-columns: repeat(auto-fill, minmax(108px, 1fr));
    grid-auto-rows: 92px;
    gap: 8px;
  }
  .pg-year-name { font-size: 27px; }
  .pg-toggle em { font-size: 11px; }
  .pg-cell:hover img { transform: none; }
  .pg-when { display: none; }

  .lb-stage { padding: 58px 14px 76px; }
  .lb-btn { width: 46px; height: 46px; font-size: 21px; }
  .lb-prev { left: 8px; }
  .lb-next { right: 8px; }
  .lb-close { right: 8px; top: 12px; }
}

/* No entrance animation for anyone who asked for less motion. */
@media (prefers-reduced-motion: reduce) {
  .pg-cell { opacity: 1; transform: none; transition: none; }
  .pg-cell img { transition: none; }
}

/* ---------- rsvp ---------- */

.rsvp {
  max-width: 640px;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 18px;
  align-items: center;
}

.lede { font-size: 19px; line-height: 1.7; color: var(--ink-2); margin: 0; }

.form {
  width: 100%;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 22px;
  margin: 6px 0 0;
}

.f { display: flex; flex-direction: column; gap: 8px; }
.f.sub { margin-top: 14px; }

.form fieldset { border: 0; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }

.flabel {
  font-size: 13px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0;
}

.form input[type="text"], .form textarea {
  font-family: var(--serif);
  font-size: 18px;
  color: var(--ink);
  background: var(--cream);
  border: 1px solid var(--line);
  padding: 12px 14px;
  width: 100%;
  border-radius: 0;
}

.band .form input[type="text"], .band .form textarea { background: var(--white); }

.form input[type="text"]:focus, .form textarea:focus {
  outline: none;
  border-color: var(--accent);
}

.form textarea { resize: vertical; }

.radio { display: flex; align-items: center; gap: 10px; font-size: 19px; color: var(--ink-2); cursor: pointer; }
.radio input { accent-color: var(--accent); width: 17px; height: 17px; }

.guest {
  border: 1px solid var(--line);
  background: var(--white);
  padding: 22px 24px;
}

.kitchen { margin: 0; font-size: 16px; line-height: 1.6; color: var(--ink-2); }
.kitchen strong { color: var(--ink); }

.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* The two steps of the reply form. Each has to repeat the form's own column
   layout: wrapping the fields in a div made them one flex child, and the 22px
   gap that separates every field stopped applying inside it. */
#rsvp-gate,
#rsvp-reply {
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.gate-note {
  margin: -8px 0 0;
  font-size: 16px;
  line-height: 1.6;
  color: var(--muted);
}

.greeting {
  margin: 0;
  font-size: 17px;
  line-height: 1.7;
  color: var(--ink-2);
  padding-bottom: 4px;
  border-bottom: 1px solid var(--line);
}

.greeting strong { color: var(--ink); font-weight: 600; }

/* A button that reads as a line of prose, because it sits inside a sentence
   rather than beside one. */
.linkish {
  font: inherit;
  color: var(--muted);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.linkish:hover { color: var(--ink); }

.form button {
  font-family: var(--serif);
  font-size: 14px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--white);
  background: var(--accent);
  border: 1px solid var(--accent);
  padding: 16px 28px;
  cursor: pointer;
  border-radius: 0;
  align-self: flex-start;
}

.form button:hover { background: var(--accent-dark); border-color: var(--accent-dark); }
.form button[disabled] { opacity: 0.55; cursor: default; }

.form-msg { margin: 0; font-size: 17px; font-style: italic; color: var(--ink-2); min-height: 1em; }
.form-msg.err { color: #8C4A3F; }

.rsvp-done {
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 14px;
  align-items: center;
  padding: 20px 0;
}

.rsvp-done p { margin: 0; font-size: 19px; line-height: 1.7; color: var(--ink-2); }

.addr { margin: 0; font-size: 17px; color: var(--muted); line-height: 1.7; }

/* ---------- footer ---------- */

.footer {
  background: var(--ink);
  padding: 40px 48px;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.footer-names { font-family: var(--script); font-size: 34px; color: #D9D2BF; line-height: 1; }

.footer-line {
  font-size: 13px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---------- responsive ---------- */

@media (max-width: 860px) {
  /* On a phone the seven links will not sit beside the monogram, so stack:
     Q & A on its own line, the links centred underneath. */
  .nav {
    flex-direction: column;
    justify-content: center;
    gap: 10px;
    padding: 12px 16px;
  }
  .nav-mark { font-size: 44px; }
  .nav-links {
    justify-content: center;
    gap: 14px 18px;
    width: 100%;
  }
  .nav-links a { font-size: 12px; letter-spacing: 0.14em; }
  .section, .band { padding: 64px 20px; }
  .hero { padding: 64px 20px 56px; }
  .hero-frame { height: 56vh; }
  .story { gap: 32px; justify-content: center; }
  .story-frame { width: 100%; max-width: 320px; height: 380px; }
  .cards { grid-template-columns: 1fr; }
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
  .cell { height: 220px; }
  .finding { padding: 24px; }
  .map { width: 100%; }
  .timeline li { flex-direction: column; gap: 4px; }
  .timeline .t { flex: none; }
}

@media (max-width: 520px) {
  body { font-size: 17px; }
  .grid { grid-template-columns: 1fr; }
  .cell { height: 240px; }
  .hero-frame { height: 48vh; }
  .form button { align-self: stretch; text-align: center; }
}

/* ---------- how we got here ---------- */

.story-line { list-style: none; margin: 0; padding: 0; max-width: 760px; }

.story-line li {
  display: flex;
  gap: 28px;
  padding: 0 0 34px 26px;
  border-left: 1px solid var(--rule);
  position: relative;
}

.story-line li::before {
  content: "";
  position: absolute;
  left: -5px; top: 7px;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--accent);
}

.story-line .sl-last { border-left-color: transparent; padding-bottom: 0; }

.sl-when {
  flex: 0 0 170px;
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  padding-top: 3px;
}

.sl-what h3 { margin: 0 0 6px; font-size: 21px; font-weight: 500; color: var(--ink); }
.sl-what p  { margin: 0; color: var(--ink-2); line-height: 1.65; }

.sl-photo { margin: 16px 0 0; max-width: 260px; }
.sl-photo img { width: 100%; display: block; border-radius: 2px; }
.sl-photo figcaption {
  margin-top: 7px;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---------- film ---------- */

/* Both films are vertical but different shapes (9:16 and 3:4), so pin them to
   one height and let each width follow from its own aspect ratio. That way the
   two players line up top and bottom and the captions sit on the same line. */
.film-row {
  display: flex;
  gap: 44px;
  align-items: flex-start;
  justify-content: center;
  flex-wrap: wrap;
}

.film-item { margin: 0; text-align: center; }

.v-frame {
  height: clamp(320px, 46vh, 460px);
  width: auto;
  background: #000;
  border-radius: 3px;
  overflow: hidden;
}

.v-frame.v-916 { aspect-ratio: 9 / 16; }
.v-frame.v-34  { aspect-ratio: 3 / 4; }

.v-frame video, .v-frame iframe {
  width: 100%; height: 100%; display: block; border: 0;
}

.film-item figcaption {
  margin-top: 11px;
  font-size: 13px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
}

@media (max-width: 760px) {
  .film-row { gap: 28px; }
  /* Same height as each other here too. Capped so the wider 3:4 player still
     fits a narrow screen: at 400px tall it is 300px wide. */
  .v-frame { height: min(46vh, 400px); }
}

/* ---------- the way in ----------
   A door rather than a play button. Opening it is the gesture every browser
   demands before it will make a sound, so the song starts with the page
   instead of waiting to be asked for. */
.gate {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: var(--cream);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 30px;
  text-align: center;
  transition: opacity 0.75s ease;
}

.gate.is-going { opacity: 0; pointer-events: none; }

.gate-inner { max-width: 560px; animation: gate-rise 1s ease both; }

@keyframes gate-rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

.gate-eyebrow {
  margin: 0 0 22px;
  font-size: 13px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--muted);
}

.gate-name {
  margin: 0;
  font-size: clamp(38px, 8vw, 62px);
  line-height: 1.15;
  color: var(--ink);
}

.gate-amp {
  margin: 4px 0;
  font-size: 19px;
  font-style: italic;
  color: var(--muted);
}

.gate-rule {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin: 26px auto 20px;
  max-width: 260px;
}

.gate-rule::before,
.gate-rule::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}

.gate-rule span {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--accent);
}

.gate-date {
  margin: 0 0 34px;
  font-size: 15px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.gate-btn {
  font: inherit;
  font-size: 14px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--white);
  background: var(--accent);
  border: 0;
  border-radius: 999px;
  padding: 16px 42px;
  cursor: pointer;
  box-shadow: 0 6px 20px rgba(60, 76, 52, 0.28);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.gate-btn:hover { transform: translateY(-1px); box-shadow: 0 9px 26px rgba(60, 76, 52, 0.36); }
.gate-btn:focus-visible { outline: 3px solid var(--accent-dark); outline-offset: 4px; }

.gate-note {
  margin: 14px 0 0;
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Nothing behind the door scrolls until it is open. */
html.gate-shut, html.gate-shut body { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  .gate { transition: none; }
  .gate-inner { animation: none; }
}

/* ---------- the music disc ---------- */

.disc {
  position: fixed;
  right: 22px;
  /* env() reads 0 without viewport-fit=cover, which is what we want on every
     other browser; on an iPhone it keeps the button clear of the home bar. */
  bottom: calc(22px + env(safe-area-inset-bottom, 0px));
  z-index: 50;

  /* iOS repositions a fixed element only once a scroll has finished, so during
     a momentum scroll the button drifts up the page and then snaps back to the
     corner -- it reads as though it will not stay put. Its own compositor
     layer keeps it pinned the whole way down. */
  transform: translateZ(0);
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 8px 18px 8px 8px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--cream);
  color: var(--ink-2);
  font: inherit;
  font-size: 13px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.13);
}

.disc:hover { color: var(--ink); }
.disc:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.disc-art {
  width: 34px; height: 34px;
  border-radius: 50%;
  flex: none;
  background:
    radial-gradient(circle at 50% 50%, var(--cream) 0 4px, transparent 4px),
    radial-gradient(circle at 50% 50%, #3a352f 0 7px, transparent 7px),
    repeating-radial-gradient(circle at 50% 50%, #1c1a18 0 2px, #262320 2px 4px);
}

/* Only spins while the song is actually playing. */
.disc.is-playing .disc-art { animation: disc-spin 3.2s linear infinite; }

@keyframes disc-spin { to { transform: rotate(360deg); } }

/* A guest who prefers no motion still gets the button, just no spin. */
@media (prefers-reduced-motion: reduce) {
  .disc.is-playing .disc-art { animation: none; }
}

@media (max-width: 700px) {
  .story-line li { flex-direction: column; gap: 6px; }
  .sl-when { flex: none; }
  .film-row { gap: 32px; }
  .disc {
    right: 14px;
    bottom: calc(14px + env(safe-area-inset-bottom, 0px));
    padding: 7px 15px 7px 7px;
    font-size: 12px;
  }
  .disc-art { width: 30px; height: 30px; }
}

/* ---------- the engagement story: paper ---------- */

/* A sheet of writing paper for the story to sit on. The texture is built from
   layered gradients rather than an image so it costs nothing to load and stays
   crisp on any screen. */
.es-paper {
  background-color: #ffffff;
  background-image:
    radial-gradient(circle at 18% 22%, rgba(120, 110, 90, 0.03) 0, transparent 44%),
    radial-gradient(circle at 82% 74%, rgba(120, 110, 90, 0.028) 0, transparent 48%);
  border: 1px solid rgba(120, 100, 70, 0.16);
  border-radius: 2px;
  padding: 40px 44px 34px;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.9) inset,
    0 12px 34px rgba(80, 62, 34, 0.10);
  position: relative;
}

/* A faint fold down the middle, the way a letter arrives. */
.es-paper::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0; left: 50%;
  width: 24px;
  transform: translateX(-50%);
  background: linear-gradient(
    to right,
    transparent,
    rgba(90, 80, 60, 0.07) 46%,
    rgba(255, 255, 255, 0.95) 50%,
    rgba(90, 80, 60, 0.07) 54%,
    transparent);
  pointer-events: none;
}

.es-paper .es,
.es-paper .es-dots,
.es-paper .es-coda { position: relative; z-index: 1; }

/* ---------- the engagement story: reader ---------- */

/* The byline belongs to the title, not to the page: it sits inside .head so it
   stays centred directly beneath it and does not pick up the 48px column gap. */
.es-intro {
  margin: 0;
  color: var(--muted);
  font-style: italic;
  font-size: 15px;
  letter-spacing: 0.04em;
}

/* This section carries a lot of vertical air otherwise. */
#engagement .col { gap: 26px; }
#engagement .head { gap: 4px; }

.es { position: relative; width: 100%; }

.es-slide { display: none; }

/* No fixed height and no inner scrollbar: the chapters are written short
   enough to fit, and the photograph stretches to whatever height the words
   need. Words on the left page, picture on the right. */
/* Every page is the same height: --es-h is measured from the tallest chapter
   once the fonts and photographs have settled, so the book never resizes as you
   turn. Falling back to auto means an unmeasured book still reads correctly. */
.es-slide.is-on {
  display: flex;
  gap: 52px;
  align-items: stretch;
  min-height: 340px;
  height: var(--es-h, auto);
}

.es-text {
  order: 1;
  flex: 0 0 calc(50% - 26px);
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.es-num {
  margin: 0 0 4px;
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
}

.es-text h3 { margin: 0 0 10px; font-size: 23px; font-weight: 500; color: var(--ink); }

.es-body { flex: 1; }
.es-body p { margin: 0 0 11px; line-height: 1.6; font-size: 17px; color: var(--ink-2); }
.es-body p:last-child { margin-bottom: 0; }
.es-tease { font-style: italic; color: var(--muted); }

.es-photo {
  order: 2;
  flex: 0 0 calc(50% - 26px);
  align-self: stretch;
  min-height: 340px;
  margin: 0;
  position: relative;
  overflow: hidden;
  border-radius: 2px;
  background: var(--cream);
}

.es-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }


.es-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 58px; height: 58px;
  border-radius: 50%;
  border: 0;
  background: var(--accent);
  color: #fff;
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 4px 14px rgba(60, 76, 52, 0.32);
  z-index: 3;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.es-arrow:hover:not(:disabled) {
  transform: translateY(-50%) scale(1.07);
  box-shadow: 0 6px 20px rgba(60, 76, 52, 0.42);
}
.es-arrow:active:not(:disabled) { transform: translateY(-50%) scale(0.97); }
.es-arrow:focus-visible { outline: 3px solid var(--accent); outline-offset: 4px; }
.es-arrow:disabled { opacity: 0.25; cursor: default; box-shadow: none; }
.es-prev { left: -72px; }
.es-next { right: -72px; }

.es-dots {
  display: flex;
  gap: 7px;
  justify-content: center;
  align-items: center;
  margin-top: 22px;
  flex-wrap: wrap;
}

/* Numbered pagination -- desktop. */
.es-page {
  min-width: 30px;
  height: 30px;
  padding: 0 6px;
  border-radius: 2px;
  border: 1px solid rgba(120, 96, 58, 0.3);
  background: transparent;
  color: var(--ink-2);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.es-page:hover { border-color: var(--accent); color: var(--accent); }
.es-page.is-on {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.es-page:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The plain counter replaces the numbers on a phone. */
.es-counter {
  display: none;
  margin: 0;
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The closing photo, left aligned under the reader. */
.es-coda { margin: 26px auto 0 0; max-width: 240px; }
.es-coda img { width: 100%; display: block; border-radius: 2px; }
.es-coda figcaption {
  margin-top: 8px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--muted);
  font-style: italic;
}

@media (max-width: 1120px) {
  .es-prev { left: -8px; }
  .es-next { right: -8px; }

}

@media (max-width: 760px) {
  .story-frame { position: static; }

  /* One whole page, not a spread. A phone shows a single column, so the fold
     down the middle -- which is the seam between two facing pages -- was
     drawing a crease straight through the middle of the words. A book held
     open has two pages; a book on a phone has one. */
  .es-paper::after { display: none; }
  .es-paper { padding: 26px 18px 24px; }

  .es-slide.is-on { flex-direction: column; gap: 16px; min-height: 0; }
  .es-photo { order: 2; flex: none; width: 100%; height: 280px; min-height: 0; }
  .es-text { order: 1; flex: none; height: auto; }
  .es-body { padding-right: 0; }
  .es-body p { font-size: 16px; }
  .es-coda { max-width: 100%; margin-top: 22px; }
}

/* On a phone the arrows stay on the sides of the page, halfway down it. Under
   the book they read as page furniture and guests never worked out that the
   story turned at all; on the edge of the page at its middle, they are
   unmistakably a way through.

   The page is inset by exactly the width they need, so an arrow sits in a
   gutter of its own and never lands on a word.

   The numbered pagination is still put away -- seventeen targets in a row
   cannot be tapped reliably. */
@media (max-width: 760px) {
  .es-page { display: none; }
  .es-counter { display: block; }

  .es { padding-bottom: 0; }

  /* The gutter the arrows live in. */
  .es-slide.is-on { padding: 0 24px; }

  .es-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 48px;
    height: 48px;
    border-radius: 50%;
    font-size: 23px;
    box-shadow: 0 3px 14px rgba(40, 32, 18, 0.42);
  }
  .es-arrow:hover:not(:disabled),
  .es-arrow:active:not(:disabled) { transform: translateY(-50%); }

  .es-prev { left: -24px; }
  .es-next { right: -24px; }

  .es-dots {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 12px;
    margin-top: 18px;
  }
}

/* ---------- the page turn ---------- */

/* A full 180-degree turn. The leaf is hinged on the spine and sweeps the whole
   way across onto the facing page, the way a real page does; a partial tilt
   reads as a stutter rather than a turn.

   It is a clone laid exactly over the photograph, so the page underneath is
   the real destination and the reader sees actual content the moment the leaf
   clears. Because both halves of the book are the same width, a leaf hinged at
   the spine lands exactly on the opposite page: it cannot reach the background.
   Its vertical extent never changes either, since the axis is vertical. */
.es { perspective: 2400px; perspective-origin: 50% 45%; }

.es-leaf {
  position: absolute;
  z-index: 6;
  transform-style: preserve-3d;
  transform-origin: left center;
  pointer-events: none;
  will-change: transform;
  backface-visibility: visible;
}

.es-leaf-face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  overflow: hidden;
  border-radius: 2px;
  box-shadow: 0 6px 26px rgba(60, 50, 35, 0.22);
}

.es-leaf-face img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The reverse of the sheet: paper, shaded towards the spine. */
.es-leaf-back {
  transform: rotateY(180deg);
  background:
    linear-gradient(to right, rgba(92, 80, 58, 0.13), rgba(255, 255, 255, 0) 55%),
    #fdfcf9;
}

.es-leaf.turn-fwd  { animation: leaf-fwd  900ms cubic-bezier(0.42, 0.02, 0.35, 1) forwards; }
.es-leaf.turn-back { animation: leaf-back 900ms cubic-bezier(0.42, 0.02, 0.35, 1) forwards; }

@keyframes leaf-fwd {
  0%   { transform: rotateY(0deg); }
  100% { transform: rotateY(-180deg); }
}

@keyframes leaf-back {
  0%   { transform: rotateY(-180deg); }
  100% { transform: rotateY(0deg); }
}

/* The words fade rather than cut, timed to be gone before the leaf uncovers
   them and back once it has passed. */
.es-text.turn-out { animation: words-out 300ms ease-in forwards; }
.es-text.turn-in  { animation: words-in  620ms ease-out backwards; }

@keyframes words-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes words-in  { 0% { opacity: 0; } 45% { opacity: 0.1; } 100% { opacity: 1; } }

/* Anyone who has asked for less motion gets the plain change with no turn. */
@media (prefers-reduced-motion: reduce) {
  .es-leaf { display: none !important; }
  .es-text[class*="turn-"] { animation: none !important; }
}

/* While measuring, every chapter is laid out at once, out of flow and hidden,
   so each can be measured at its natural height without a flicker. */
.es.is-measuring .es-slide {
  display: flex !important;
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: auto !important;
  visibility: hidden;
  animation: none !important;
}

/* ---------- song captions ----------

   One line at a time along the foot of the page, beside the disc. It never
   takes a click: the bar sits over whatever the guest is reading, so it must
   not be able to intercept a tap meant for the page underneath. */

#lyrics {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(24px + env(safe-area-inset-bottom, 0px));
  z-index: 49;              /* under the disc, which is 50 */
  pointer-events: none;
  display: flex;
  justify-content: center;

  /* Room for the disc in the bottom-right corner, so a long line never runs
     underneath it. The left side matches, to keep the words centred on the
     page rather than centred in the space left over. */
  padding: 0 clamp(16px, 22vw, 260px);
}

.ly-line {
  margin: 0;

  /* Wide enough for one whole lyric line. At 44ch, 71 of the song's 78 lines
     fitted on one row and 7 wrapped onto a second - and a caption that wraps
     has stopped being one line at a time. The longest line is 52 characters.
     There is room: at the narrowest width this rule still applies (901px,
     where the side padding is 22vw) the bar has 505px to spend and wants
     466px. Below 900px the phone rules take over and this is 100%. */
  max-width: 56ch;
  text-align: center;
  font-family: var(--serif);
  font-size: 19px;
  line-height: 1.5;
  font-style: italic;
  color: var(--ink);

  /* The bar floats over cream, over photographs and over the film strip, so
     the words carry their own ground rather than trusting what is behind. */
  background: rgba(250, 246, 240, 0.92);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 9px 22px;

  opacity: 0;
  transform: translateY(4px);
  transition: opacity 260ms ease, transform 260ms ease;
}

.ly-line.on { opacity: 1; transform: none; }

/* Below 900px there is no longer room to sit beside the disc: the side padding
   that keeps the words clear of it would squeeze the line to nothing. So the
   caption stops sharing the corner and sits above the disc instead.

   The two offsets differ because the disc does. At full size it stands about
   50px tall on a 22px margin, so its top edge is 72px up; on a phone it is
   46px on a 14px margin, so 60px up. Each offset clears its own disc by 12px. */
@media (max-width: 900px) {
  #lyrics {
    bottom: calc(84px + env(safe-area-inset-bottom, 0px));
    padding: 0 16px;
  }
}

@media (max-width: 700px) {
  #lyrics {
    bottom: calc(72px + env(safe-area-inset-bottom, 0px));
    padding: 0 14px;
  }

  .ly-line {
    font-size: 17px;
    padding: 8px 16px;
    max-width: 100%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ly-line { transition: opacity 120ms linear; transform: none; }
  .ly-line.on { transform: none; }
}

