/* Luke's 3rd Birthday — "Seasonal Sighting"
   Figma: MacBook Air - 1     (node 32:588),  1280 x 2810
          iPhone 13 & 14 - 1  (node 32:1502),  390 x 4308

   Both artboards are fixed-coordinate collages, so both are built the same
   way: one .stage sized to the artboard, children pinned at the drawn
   positions, and the whole thing scaled to the viewport (factor from
   script.js). The breakpoint swaps which artboard's numbers are in play —
   the markup is identical either way. */

:root {
  --paper: #eeeae0;
  --ink: #433c36;
  --tape: #ab7f53;
  --marker: #e2dd8c;

  /* Everything pinned to the paper casts the same hand-drawn shadow — see
     the #pencil-shadow filter in index.html. */
  --shadow: url(#pencil-shadow);

  /* The artboard in play. script.js reads these to work out --scale. */
  --design-w: 1280;
  --design-h: 2995;
  /* Drawn at 1280, but it keeps growing with the window up to 1920 rather
     than sitting at 1:1 in a sea of paper on a large display. */
  --max-scale: 1.5;
  --scale: 1;

  color-scheme: light;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

/* Artwork bleeding past the right edge must never add a horizontal scrollbar.
   This has to sit on <html>: overflow set on <body> is propagated up to the
   viewport instead of clipping anything. */
html {
  overflow-x: hidden;
  overflow-x: clip;
}

body {
  background: var(--paper);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  /* The scaled stage is narrower than the viewport once --scale is capped. */
  display: flex;
  justify-content: center;
}

img {
  display: block;
  max-width: 100%;
}

/* Selection picks up the marker yellow rather than the browser's blue. */
::selection {
  background-color: var(--marker);
  color: var(--ink);
}

/* The field guide card is that same yellow, so selection on it would be
   invisible — a shade deeper there keeps it readable. */
.field-guide ::selection {
  background-color: #c0bc77;
}

/* Holds the SVG filter the poster's edge references. */
.filter-defs {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------- stage --- */

.stage {
  position: relative;
  isolation: isolate; /* gives mix-blend-mode a backdrop to blend against */
  flex: none;
  width: calc(var(--design-w) * 1px);
  height: calc(var(--design-h) * 1px);
  overflow: hidden; /* the Figma frame clips its artwork at the edges */
  background: var(--paper);
  /* Paper carried past the frame edge. The transform makes .stage a blending
     group, so art running outside the frame would lose its backdrop and show
     the scan's own off-white as a pale rectangle — this keeps paper under it.
     A shadow rather than a box, because shadows add no scrollable overflow. */
  box-shadow: 0 0 0 400px var(--paper);
  transform: scale(var(--scale));
  transform-origin: top left;
  /* Reclaim the layout space the transform no longer occupies. */
  margin-right: calc(var(--design-w) * 1px * (var(--scale) - 1));
  margin-bottom: calc(var(--design-h) * 1px * (var(--scale) - 1));
}

/* ----------------------------------------------------------- decoration --- */

.deco {
  position: absolute;
}

/* Wrapper for artwork the design crops rather than fits. The inner <img> is
   sized in percentages so the crop survives any wrapper size. */
.crop {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.crop img {
  position: absolute;
  max-width: none;
}

/* Field guide card. The torn outline is a single exported shape; the contents
   are live text pinned at the drawn coordinates inside the tilted card. */
.field-guide {
  left: 83px;
  top: 239.214px;
  width: 308.755px;
  height: 567.129px;
  transform: rotate(-1deg);
  transform-origin: 0 0;
  font-family: "Goudy Bookletter 1911", Georgia, serif;
}

.field-guide__card {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  filter: var(--shadow);
}

.field-guide > :not(.field-guide__card) {
  position: absolute;
  margin: 0;
}

.field-guide__title {
  left: 20px;
  top: 36px;
  width: 235px;
  font-family: "Heebo", system-ui, sans-serif;
  font-weight: 900;
  font-size: 26px;
  line-height: 28px;
  letter-spacing: 1.3px;
  text-transform: uppercase;
}

.fg-label {
  left: 22px;
  font-family: "Heebo", system-ui, sans-serif;
  font-weight: 900;
  font-size: 11px;
  line-height: normal;
  letter-spacing: 0.55px;
  text-transform: uppercase;
  white-space: nowrap;
}

.fg-value {
  left: 22px;
  width: 265px;
  font-size: 20px;
  line-height: 20px;
}

.fg-label--species  { top: 112px; }
.fg-value--species  { top: 130px; }
.fg-label--height   { top: 165px; }
.fg-value--height   { top: 183px; }
.fg-label--habitat  { top: 218px; }
.fg-value--habitat  { top: 236px; }
.fg-label--behavior { top: 291px; }
.fg-value--behavior { top: 309px; }
.fg-label--diet     { top: 384px; }
.fg-value--diet     { top: 402px; }
.fg-label--phrases  { top: 437px; }
.fg-value--phrases  { top: 455px; }
.fg-label--audio    { top: 490px; }

/* "last recorded audio" row — plays the field recording. The glyph sits on
   the same left edge as every other value on the card. */
.fg-play {
  left: 22px;
  top: 508px;
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  font-size: 20px;
  line-height: 20px;
  cursor: pointer;
}

.fg-play__icon {
  display: block;
  width: 8.28626px;
  height: 7.58591px;
}

.fg-play__glyph {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
  fill: var(--ink);
}

/* Only one glyph shows at a time; the button swaps them while playing. */
.fg-play__glyph--stop,
.fg-play[aria-pressed="true"] .fg-play__glyph--play {
  display: none;
}

.fg-play[aria-pressed="true"] .fg-play__glyph--stop {
  display: block;
}

.fg-play:hover .fg-play__label {
  text-decoration: underline;
  text-underline-offset: 2px;
}

.fg-play:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

/* Snapshot. The frame itself stays unrotated so the taped strips inside can
   still blend with the paper; only the photo turns. */
.deco--photo {
  left: 446.53px;
  top: 93.99px;
  width: 288px;
  height: 383px;
}

.deco--photo > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: rotate(1.58deg);
  filter: var(--shadow);
}

/* Cut ends, as if snipped with pinking shears — the sides stay straight, the
   top and bottom zigzag. Eight fine teeth across the strip with a 2px bite. */
.tape {
  position: absolute;
  width: 12.153%;
  background: var(--tape);
  mix-blend-mode: multiply;
  clip-path: polygon(
    0 0, 6.25% 2px, 12.5% 0, 18.75% 2px, 25% 0,
    31.25% 2px, 37.5% 0, 43.75% 2px, 50% 0, 56.25% 2px, 62.5% 0,
    68.75% 2px, 75% 0, 81.25% 2px, 87.5% 0, 93.75% 2px, 100% 0, 100% 100%,
    93.75% calc(100% - 2px), 87.5% 100%, 81.25% calc(100% - 2px), 75% 100%, 68.75% calc(100% - 2px), 62.5% 100%,
    56.25% calc(100% - 2px), 50% 100%, 43.75% calc(100% - 2px), 37.5% 100%, 31.25% calc(100% - 2px), 25% 100%,
    18.75% calc(100% - 2px), 12.5% 100%, 6.25% calc(100% - 2px), 0 100%
  );
}

.tape--top {
  left: 45.93%;
  top: -8.925%;
  height: 17.494%;
  transform: rotate(-4deg);
}

.tape--bottom {
  left: 44.32%;
  top: 93.901%;
  height: 12.745%;
  transform: rotate(5.74deg);
}

/* Pencil portrait */
.deco--portrait {
  left: -43px;
  top: 849px;
  width: 491px;
  height: 608px;
  mix-blend-mode: darken;
}

.deco--portrait .crop img {
  left: 0;
  top: -4.81%;
  width: 106.5%;
  height: 114.54%;
}

/* The scans sit on warmer paper than the page (blue 214 against 224), so
   `darken` keeps their yellow cast and the crop reads as a tinted rectangle.
   Lifting the scan puts its paper above the page on every channel, and
   feathering the crop dissolves what's left of the edge into the background. */
.deco--portrait .crop img,
.deco--legs .crop img {
  filter: brightness(1.05);
}

.deco--portrait .crop,
.deco--legs .crop {
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}

.deco--portrait .crop {
  -webkit-mask-image:
    linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%),
    linear-gradient(180deg, transparent 0, #000 4%, #000 96%, transparent 100%);
  mask-image:
    linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%),
    linear-gradient(180deg, transparent 0, #000 4%, #000 96%, transparent 100%);
}

/* Shallower at the foot — the shoes run right to the bottom of the crop. */
.deco--legs .crop {
  -webkit-mask-image:
    linear-gradient(90deg, transparent 0, #000 8%, #000 92%, transparent 100%),
    linear-gradient(180deg, transparent 0, #000 3%, #000 98.5%, transparent 100%);
  mask-image:
    linear-gradient(90deg, transparent 0, #000 8%, #000 92%, transparent 100%),
    linear-gradient(180deg, transparent 0, #000 3%, #000 98.5%, transparent 100%);
}

/* Driftwood, top left — the source art is portrait, laid on its side. */
.deco--driftwood-top {
  left: -13px;
  top: 24px;
  width: 367.389px;
  height: 188px;
  mix-blend-mode: darken;
}

.deco--driftwood-top img {
  width: 188px;
  height: 367.389px;
  object-fit: cover;
  transform: translate(89.695px, -89.695px) rotate(-90deg) scaleY(-1);
}

/* Driftwood, bottom — same trick, turned the other way. */
/* Sits bottom-right in the redrawn footer, at its full drawn size. */
.deco--driftwood-bottom {
  left: 916px;
  top: 2812px;
  width: 328.691px;
  height: 156px;
  opacity: 0.9;
  mix-blend-mode: darken;
}

.deco--driftwood-bottom .crop {
  inset: auto;
  left: 86.346px;
  top: -86.346px;
  width: 156px;
  height: 328.691px;
  transform: rotate(90deg);
}

.deco--driftwood-bottom .crop img {
  left: -42.18%;
  top: 0;
  width: 184.36%;
  height: 100%;
}

/* Bare legs, top right */
.deco--legs {
  left: 1009px;
  top: -144px;
  width: 179px;
  height: 508px;
  mix-blend-mode: darken;
}

.deco--legs .crop img {
  left: -92.67%;
  top: -12.26%;
  width: 282.05%;
  height: 132.13%;
}

/* --------------------------------------------------------------- card ---- */

.card {
  position: absolute;
  left: 464px;
  top: 555px;
  width: 729px;
  /* 34px shorter than drawn: the habitat line is one line now, not two. */
  height: 693px;
  font-family: "Goudy Bookletter 1911", Georgia, serif;
}

/* Printer paper: a roughened edge from the SVG filter, one crease down the
   middle and one across it where the sheet was folded into quarters, and a
   shadow that follows the ragged outline rather than a clean rectangle. */
.card::before {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0.8;
  background-image:
    linear-gradient(90deg, transparent, rgba(67, 60, 54, 0.06) 45%, rgba(255, 255, 255, 0.65) 55%, transparent),
    linear-gradient(180deg, transparent, rgba(67, 60, 54, 0.05) 45%, rgba(255, 255, 255, 0.6) 55%, transparent),
    linear-gradient(#fff, #fff);
  background-size: 28px 100%, 100% 32px, 100% 100%;
  background-position: 50% 0, 0 50%, 0 0;
  background-repeat: no-repeat;
  filter: url(#paper-edge) var(--shadow);
}

.card > * {
  position: absolute;
  margin: 0;
}

.card__title {
  left: 54px;
  top: 50px;
  font-family: "Heebo", system-ui, sans-serif;
  font-weight: 900;
  font-size: 58px;
  line-height: normal;
  letter-spacing: 2.9px;
  text-transform: uppercase;
  white-space: nowrap;
}

.label {
  left: 54px;
  font-family: "Heebo", system-ui, sans-serif;
  font-weight: 900;
  font-size: 22px;
  line-height: normal;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  white-space: nowrap;
}

.body {
  left: 54px;
  font-size: 32px;
  line-height: 34px;
}

.label--what  { top: 165px; }
.body--what   { top: 208px; width: 628px; }
.label--when  { top: 301px; }
.body--when   { top: 344px; width: 628px; }
.label--where { top: 403px; }
.body--where  { top: 446px; width: 593px; }
.label--bring { top: 505px; }
.body--bring  { top: 548px; width: 620px; }

/* Hand-drawn marker stroke under "Luke's 3rd Birthday". */
.underline {
  left: 389.51px;
  top: 245.23px;
  width: 257.281px;
  height: 31.4475px;
}

/* --------------------------------------------------------- guest list ---- */

.related {
  position: absolute;
  left: 0;
  top: 1527px;
  width: 100%;
  font-family: "Goudy Bookletter 1911", Georgia, serif;
}

.related__title {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  margin: 0;
  font-family: "Heebo", system-ui, sans-serif;
  font-weight: 900;
  font-size: 35px;
  line-height: normal;
  letter-spacing: 1.75px;
  text-transform: uppercase;
  text-align: center;
}

/* Two columns whose centres land on 415.5 and 864, as drawn.

   Multi-column rather than a grid: it fills column-first and balances the
   split by itself, so the list can gain or lose names without anyone having
   to keep a row count in the CSS in step with the markup. Getting that wrong
   silently spills the list into a third and fourth column. */
.names {
  position: absolute;
  left: 191.5px;
  width: 897px;
  top: 105px;
  column-count: 2;
  column-gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 32px;
  line-height: 34px;
  text-align: center;
}

.names li {
  height: 55px;
  break-inside: avoid;
}

/* --------------------------------------------------------------- star ---- */

.star {
  position: absolute;
  width: 23px;
  height: 23px;
}

.star--right { left: 1204px; top: 324px; }
.star--left  { left: 40px;   top: 1373px; }

/* -------------------------------------------------------- field notes ---- */

.note {
  position: absolute;
  margin: 0;
  font-family: "Lacquer", system-ui, sans-serif;
  font-size: 15px;
  line-height: normal;
  opacity: 0.9;
}

/* Set right-aligned in the file, so it ranges under the star. */
.note--right {
  left: 1062px;
  top: 357px;
  width: 165px;
  text-align: right;
}

.note--left {
  left: 40px;
  top: 1406px;
  width: 129px;
}

/* ------------------------------------------------------------- ticket ---- */

/* The torn marker ticket is drawn identically wherever it appears — on the
   page as "RSVP", in the dialog as "CONFIRM" — so only its origin moves. */
.ticket {
  position: absolute;
  width: 240px;
  height: 90px;
}

/* The guest list runs 15 rows per column now against the 7 drawn, so the
   ticket and the footer below it sit 385px lower than the artboard has them. */
.ticket--open {
  left: 520px;
  top: 2485px;
}

.ticket__hit {
  position: absolute;
  inset: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-decoration: none;
  cursor: pointer;
}

/* One subtracted shape exported from Figma (node 64:3109). The ridges are cut
   out of it rather than faked with paper-coloured circles, so whatever sits
   behind shows through — paper on the page, white in the dialog — and the tilt
   is baked into the path. */
.ticket__tape,
.ticket__shadow {
  position: absolute;
  left: 13.794px;
  top: 7.12px;
  width: 213.412px;
  height: 77.6465px;
}

/* The shadow is its own layer, outside the part that turns, so it stays put
   while the ticket lifts on hover — the way a shadow behaves when the object
   moves rather than the light. */
.ticket__shadow {
  filter: url(#pencil-shadow-only);
  pointer-events: none;
}

.ticket__text {
  position: absolute;
  left: 0;
  right: 0;
  top: 23px;
  font-family: "Heebo", system-ui, sans-serif;
  font-weight: 900;
  font-size: 35px;
  line-height: normal;
  letter-spacing: 1.75px;
  text-transform: uppercase;
  text-align: center;
  transform: rotate(2.09deg);
}

/* Ticket and letters turn together, like nudging the real thing. The rotation
   sits on the button rather than the block so the shadow layer beside it is
   left behind. */
.ticket__hit {
  transition: transform 160ms ease;
}

.ticket:hover .ticket__hit,
.ticket:focus-within .ticket__hit {
  transform: rotate(-4deg);
}

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

/* No focus ring: the 4° lift already fires on :focus-within, so keyboard
   users still get a clear indicator without a box drawn round the ticket. */
.ticket__hit:focus-visible {
  outline: none;
}


/* ------------------------------------------------------------ closing ---- */

/* Bottom-left, with its star riding inline so the pair stays together
   whichever way the line is aligned.

   Sizes divide by min(1, --scale) so the line holds its drawn size while the
   artboard is scaling down — dividing by the shrink cancels it out — but goes
   back to scaling normally once the page is growing past 1:1. Small type is
   the first thing to become unreadable when the window narrows, and this line
   is the smallest on the page. */
.closing {
  position: absolute;
  left: 64px;
  top: 2911px;
  display: flex;
  align-items: center;
  gap: calc(9px / min(1, var(--scale)));
  margin: 0;
  font-family: "Syne Tactile", cursive;
  font-size: calc(22px / min(1, var(--scale)));
  line-height: normal;
  white-space: nowrap;
}

.closing .star {
  position: static;
  flex: none;
  width: calc(23px / min(1, var(--scale)));
  height: calc(23px / min(1, var(--scale)));
}


/* ====================================================== phone artboard ===
   iPhone 13 & 14 - 1 (node 32:1502), 390 x 4308. Same elements, redrawn
   coordinates. Anything not listed here is identical across both artboards.

   The phone artboard still carries the older, shorter field guide card. The
   new one is 119px taller, so everything below it shifts down 120px and the
   artboard is 4428 tall rather than 4308. */

@media (max-width: 860px) {
  :root {
    --design-w: 390;
    --design-h: 5023;
    /* Past ~470px wide the phone layout stops gaining anything, so it caps
       and sits centred on the paper instead. */
    --max-scale: 1.2;
  }

  /* --------------------------------------------------------- decoration -- */

  /* Once the poster is narrower than the window it sits centred, which left
     the log stranded mid-page. --gutter (from script.js) is the paper either
     side of the poster, so subtracting it keeps the log hanging off the
     window's left edge at every width, the way it does on desktop. */
  .deco--driftwood-top {
    left: calc(-20px - var(--gutter, 0px));
    top: -21px;
    width: 367.389px;
    height: 188px;
  }

  .deco--driftwood-top img {
    width: 188px;
    height: 367.389px;
    transform: translate(89.695px, -89.695px) rotate(-90deg) scaleY(-1);
  }

  /* The log is drawn over the card on the desktop artboard; on the phone it
     tucks behind it, and the full-body drawing tucks behind the snapshot. */
  .field-guide,
  .deco--photo {
    z-index: 1;
  }

  .field-guide {
    left: 40px;
    top: 128px;
  }

  .deco--photo {
    left: 47.22px;
    top: 757.66px;
  }

  /* One strip on the phone, turned with the photo and a cooler brown. */
  .tape--top {
    left: 42.46%;
    background: #7e6d5c;
    transform: rotate(1.58deg);
  }

  .tape--bottom {
    display: none;
  }

  .deco--legs {
    left: 171px;
    top: 998px;
  }

  .deco--portrait {
    left: -37px;
    top: 2384px;
  }

  .deco--driftwood-bottom {
    left: 109.24px;
    top: 4859.96px;
    width: 164.514px;
    height: 78.08px;
  }

  .deco--driftwood-bottom .crop {
    left: 43.217px;
    top: -43.217px;
    width: 78.08px;
    height: 164.514px;
  }

  /* -------------------------------------------------------------- card -- */

  .card {
    left: 0;
    top: 1548px;
    width: 390px;
    height: 811px;
  }

  .card::before {
    opacity: 1; /* the phone artboard fills the card solid */
    /* One fold across, none down — the sheet is too narrow to read as folded
       in quarters. Drops the two vertical crease layers. */
    background-image:
      linear-gradient(180deg, transparent, rgba(67, 60, 54, 0.05) 45%, rgba(255, 255, 255, 0.6) 55%, transparent),
      linear-gradient(#fff, #fff);
    background-size: 100% 32px, 100% 100%;
    background-position: 0 50%, 0 0;
  }

  .card__title {
    left: 29px;
    top: 33.5px;
    width: 336px;
    font-size: 38px;
    line-height: 40px;
    letter-spacing: 1.9px;
    white-space: normal;
  }

  .label {
    left: 29px;
  }

  .body {
    left: 29px;
    font-size: 28px;
  }

  .label--what  { top: 135px; }
  .body--what   { top: 178px; width: 284px; }
  .label--when  { top: 331px; }
  .body--when   { top: 374px; width: 260px; }
  .label--where { top: 472px; }
  .body--where  { top: 514px; width: 336px; }
  .label--bring { top: 612px; }
  .body--bring  { top: 646px; width: 332px; }

  .underline {
    left: 18.864px;
    top: 284.044px;
    width: 242.136px;
    height: 32.933px;
  }

  /* -------------------------------------------------------- field notes -- */

  .star--right { left: 136px; top: 1161px; }
  .star--left  { left: 46px;  top: 2908px; }

  /* Widths force the drawn line breaks rather than leaving them to chance.
     Measured against the text at its rendered 15px, the right note wraps as
     intended anywhere from 90 to 99px and the left from 113 to 131px; these
     sit mid-range so a font tweak won't tip a word onto the wrong line. Right
     edge held at 158 so the block stays ranged under its star and clear of
     the drawing. */
  .note--right {
    left: 63px;
    top: 1201px;
    width: 95px;
  }

  .note--left {
    left: 46px;
    top: 2941px;
    width: 122px;
  }

  /* --------------------------------------------------------- guest list -- */

  .related {
    top: 3111px;
  }

  .related__title {
    left: 27px;
    right: auto;
    width: 336px;
    font-size: 38px;
    line-height: 40px;
    letter-spacing: 1.9px;
  }

  .names {
    left: 0;
    width: 100%;
    top: 162px;
    column-count: 1;
    font-size: 28px;
  }

  .names li {
    height: 46px;
  }

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

  /* The ticket is drawn identically on both artboards, so only its origin
     moves — every strip, notch and letter inside stays put. */
  .ticket--open {
    left: 74.18px;
    top: 4639px;
  }

  /* ------------------------------------------------------------ closing -- */

  /* 75% of the drawn size here only — desktop keeps it as drawn. */
  .closing {
    left: -7px; /* centres the line on 191.5, as drawn */
    right: 0;
    top: 4963px;
    justify-content: center;
    gap: 6.75px;
    font-size: 16.5px;
  }

  .closing .star {
    width: 17.25px;
    height: 17.25px;
  }
}

/* ========================================================= room to bleed ===
   While the poster fills the viewport, art running off the edge reads as a
   bleed — that's how both artboards are drawn. Once the viewport is wider
   than the scaled poster, the frame edge sits in open paper and the same
   bleed reads as a cut instead, so the clip comes off and the artwork runs
   on into the surrounding paper.

   Thresholds are the point where gutters appear: 1280 x 1.5 desktop,
   390 x 1.2 phone. */

@media (min-width: 1921px) {
  .stage {
    overflow: visible;
  }
}

@media (min-width: 469px) and (max-width: 860px) {
  .stage {
    overflow: visible;
  }

  /* The phone artboard hangs the log 21px above the top edge too, and nothing
     is scrollable above the document start — so drop the poster by that much
     to give it room. Pure paper, so the shift doesn't read. */
  body {
    padding-top: calc(21px * var(--scale));
  }
}

/* ============================================================== dialog ===
   RSVP (nodes 36:2641 empty / 42:3088 filled). A 560 x 617 panel drawn at
   desktop scale. It sits outside .stage — a dialog should stay put and stay
   legible — and takes its own scale factor, computed in script.js to fit the
   viewport. At full size it is pixel-exact; on a phone it shrinks to fit. */

.modal[hidden] {
  display: none;
}

.modal {
  position: fixed;
  inset: 0;
  z-index: 10;
}

.modal__scrim {
  position: absolute;
  inset: 0;
  background: var(--ink);
  opacity: 0.2;
}

/* Centred by hand rather than by the parent: at full size the panel is wider
   than a phone viewport, and centring layout would clamp it to the left edge
   before the scale had a chance to shrink it. */
.modal__panel {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 560px;
  height: 617px;
  background: #fff;
  color: var(--ink);
  font-family: "Goudy Bookletter 1911", Georgia, serif;
  transform: translate(-50%, -50%) scale(var(--modal-scale, 1));
}

.modal__panel > *,
.rsvp > *,
.rsvp-done > * {
  position: absolute;
  margin: 0;
}

.modal__title {
  left: 54px;
  top: 50px;
  font-family: "Heebo", system-ui, sans-serif;
  font-weight: 900;
  font-size: 58px;
  line-height: normal;
  letter-spacing: 2.9px;
  text-transform: uppercase;
}

/* Dismisses the dialog. A button, not an <a> — it performs an action rather
   than going anywhere — but it carries a link's affordances, and the padding
   gives it a proper target without shifting the glyph off its drawn spot. */
.modal__close {
  left: 459px;
  top: -3px;
  /* Above the form: that spans the whole panel to position its own fields,
     and being later in the DOM it would otherwise swallow every click here. */
  z-index: 1;
  /* Padding only — it grows the target without moving the glyph off the spot
     it's drawn on, and holds ~44px even once the dialog scales down. */
  padding: 20px;
  border: 0;
  background: none;
  color: inherit;
  font-family: "Lacquer", system-ui, sans-serif;
  font-size: 55px;
  line-height: 1;
  cursor: pointer;
  user-select: none; /* repeated clicks shouldn't select the glyph */
}

.modal__close:focus-visible,
.stepper__btn:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

.rsvp {
  position: absolute;
  inset: 0;
}

.rsvp__label {
  left: 54px;
  font-family: "Heebo", system-ui, sans-serif;
  font-weight: 900;
  font-size: 22px;
  line-height: normal;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  white-space: nowrap;
}

.rsvp__label--name   { top: 165px; }
.rsvp__label--email  { top: 267px; }
.rsvp__label--guests { top: 369px; }

/* Drawn as bare text with no field chrome, so focus has to carry the
   affordance — otherwise there is nothing to say these are typeable. */
.rsvp__input {
  left: 54px;
  width: 452px;
  padding: 0 0 4px;
  border: 0;
  border-bottom: 1px solid transparent;
  border-radius: 0;
  background: none;
  color: var(--ink);
  font-family: inherit;
  font-size: 32px;
  line-height: 34px;
}

.rsvp__input::placeholder {
  color: var(--ink);
  opacity: 0.3;
}

.rsvp__input:focus {
  outline: none;
  border-bottom-color: var(--ink);
}

.rsvp__input:user-invalid {
  border-bottom-color: #a8443a;
}

.rsvp__input--name  { top: 208px; }
.rsvp__input--email { top: 310px; }

.stepper {
  left: 54px;
  top: 412px;
  display: flex;
  align-items: baseline;
  gap: 12px;
  font-size: 32px;
  line-height: 34px;
}

.stepper__btn {
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.stepper__btn:disabled {
  opacity: 0.3;
  cursor: default;
}

.stepper__count {
  min-width: 1.2em;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.ticket--confirm {
  left: 159.23px;
  top: 479.92px;
}

.rsvp__message {
  left: 54px;
  top: 576px;
  width: 452px;
  font-size: 18px;
  line-height: 22px;
  color: #a8443a;
}

.rsvp__message[hidden] {
  display: none;
}

/* No confirmation screen was drawn — this reuses the dialog's own type. */
.rsvp-done {
  position: absolute;
  inset: 0;
}

.rsvp-done[hidden] {
  display: none;
}

/* Sits where the first field label does, so the confirmed panel keeps the
   dialog's rhythm. */
.rsvp-done__body {
  left: 54px;
  top: 165px;
  width: 452px;
  font-size: 32px;
  line-height: 34px;
}

/* Blocks a second submit while the first is in flight. Deliberately invisible
   — the ticket keeps its normal appearance throughout. */
.modal--sending .ticket__hit {
  pointer-events: none;
}
