/* How-it-works reel — the five beats of the booth, played out.
 *
 * Landing-page only, so it is a separate sheet from site.css (which every page on
 * the site loads). Three rules hold this together:
 *
 *  1. NO-JS / REDUCED-MOTION IS THE BASE STATE. Without the .is-ready class that
 *     howitworks.js adds, this is a plain illustrated five-step list: scenes stack
 *     in a grid, nothing is absolutely positioned, nothing animates. Everything an
 *     animation does is written as a keyframe INTO the element's resting state, so
 *     the resting state is always the readable one.
 *  2. THE CARD ART IS REAL. .rc composites the actual overlay PNG the render engine
 *     produces (frame art, photo window punched transparent) over the actual subject
 *     photo — the same layering the kiosk does over its live preview. Nothing here
 *     is a drawing of a card.
 *  3. THE PHOTO WINDOW IS MEASURED, NOT GUESSED. --win-* come from frames.json
 *     (written by scripts/render_showcase.py off the overlay's alpha). The values
 *     below are the wedding frame's, as a fallback if that fetch fails.
 */

.reel {
  --win-x: 14.17%; --win-y: 11.31%; --win-w: 71.67%; --win-h: 45.12%;
  --card-shadow: 0 22px 50px rgba(0, 0, 0, .5);
  margin-top: 26px;
}

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

.reel-stage {
  background: linear-gradient(168deg, #212a37, #171d26);
  border: 1px solid var(--line); border-radius: var(--radius);
  /* Base (no JS): the five scenes are just a grid of illustrated steps. */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(175px, 1fr));
  gap: 8px; padding: 12px;
}
.reel.is-ready .reel-stage {
  display: block; position: relative; overflow: hidden; padding: 0;
  aspect-ratio: 16 / 9;
}

.scene { display: flex; flex-direction: column; padding: 22px 18px 18px; }
.reel.is-ready .scene {
  position: absolute; inset: 0; padding: 26px 26px 20px;
  opacity: 0; visibility: hidden;
  transition: opacity .45s ease, visibility .45s;
}
.reel.is-ready .scene.is-live { opacity: 1; visibility: visible; }

/* min-height:0 lets the art area actually shrink inside the flex column, instead
   of forcing the caption out of the bottom of the stage on short viewports. */
.scene-art { flex: 1 1 auto; min-height: 0; position: relative;
  display: grid; place-items: center; }
/* Grid items get min-height:auto by default, which is their MIN-CONTENT height —
   for anything holding a 840px-tall card image that means the item ignores its own
   height:100% and blows past the stage. Every direct child of .scene-art therefore
   opts out explicitly; this is what keeps the art inside the frame. Unconditional:
   the static fallback needs it just as much as the reel does. */
.scene-art > * { min-height: 0; min-width: 0; max-height: 100%; }

/* The static fallback (no JS, or reduced motion) is a five-up illustrated list, and
   the art inside every scene sizes itself off a percentage of .scene-art — which in
   the reel is a flex row that has a height, and here would have none. Give it one,
   and drop the pieces that only make sense in motion: the countdown, the callout
   tags, the printer's timing chip, and scene 5's second half. */
.reel:not(.is-ready) .scene-art { height: 190px; }
.reel:not(.is-ready) :is(.rc-count, .tag, .chip, .beam, .phone-sm) { display: none; }
/* The QR's cells are built by howitworks.js, so in the static fallback .pqr is an
   empty white square. Show the form underneath it instead — it is pure CSS, and
   "filling your card out in the line" is the step this scene is about anyway. */
.reel:not(.is-ready) .pqr { display: none; }
/* The printer is the only square piece of art here, so at 190px tall it is also
   190px wide and spills into the next column of the fallback grid. */
.reel:not(.is-ready) .printer { height: 150px; }
/* A fixed caption height keeps the art area the same size on every beat, so the
   card doesn't resize as the reel advances. */
.scene-cap { flex: 0 0 auto; text-align: center; margin-top: 14px; }
.reel.is-ready .scene-cap { min-height: 5.9rem; }
.scene-cap h3 { margin: 0 0 5px; font-size: 1.12rem; }
.scene-cap p { margin: 0 auto; max-width: 50ch; color: var(--muted); font-size: .95rem;
  line-height: 1.5; }
.scene-cap em { color: var(--accent); font-style: normal; font-weight: 700; }

/* ---- the rail ------------------------------------------------------------- */

.reel-rail { display: none; }
.reel.is-ready .reel-rail {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px;
  list-style: none; margin: 12px 0 0; padding: 0;
}
.rail-item {
  width: 100%; display: block; text-align: left; cursor: pointer;
  background: transparent; color: var(--muted); border: none;
  border-top: 2px solid var(--line); border-radius: 0;
  padding: 12px 4px 0; font-size: .84rem; font-weight: 700; line-height: 1.35;
  position: relative;
}
.rail-item:hover { color: var(--ink); }
.rail-item[aria-selected="true"] { color: var(--ink); }
.rail-n { display: block; font-size: .7rem; letter-spacing: .14em; color: var(--muted);
  margin-bottom: 3px; }
.rail-item[aria-selected="true"] .rail-n { color: var(--accent); }
/* The progress line is the rail's own top border, redrawn left-to-right over the
   active beat's duration — so the rail doubles as the "how long is left" cue. */
.rail-item::after {
  content: ""; position: absolute; left: 0; top: -2px; height: 2px; width: 0;
  background: var(--accent);
}
.rail-item[aria-selected="true"]::after { width: 100%; }
.reel.is-playing .rail-item[aria-selected="true"]::after {
  animation: rail-fill var(--dur, 4s) linear both;
}
@keyframes rail-fill { from { width: 0; } to { width: 100%; } }

/* ---- a real card: subject photo under the real punched frame overlay ------- */

.rc { position: relative; aspect-ratio: 2.5 / 3.5; height: 100%; max-height: 100%;
  filter: drop-shadow(0 22px 40px rgba(0, 0, 0, .55)); }
.rc-frame { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* Positioned to the measured window so the browser's cover-crop lands where the
   renderer's did — that is what makes the live preview and the printed card the
   same image rather than two similar ones. */
.rc-win { position: absolute; overflow: hidden;
  left: var(--win-x); top: var(--win-y); width: var(--win-w); height: var(--win-h); }
.rc-subject { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---- scene 1: the line ---------------------------------------------------- */

.phone { position: relative; height: 100%; aspect-ratio: 10 / 19; max-height: 100%;
  background: #0d1117; border: 2px solid #39445600; border-radius: 26px;
  box-shadow: 0 0 0 2px #38445a, var(--card-shadow); padding: 16px 12px; }
.phone::before { content: ""; position: absolute; top: 8px; left: 50%; width: 34%; height: 5px;
  transform: translateX(-50%); background: #38445a; border-radius: 3px; }
.phone-screen { position: relative; height: 100%; overflow: hidden; }

.pf { position: absolute; inset: 0; padding-top: 6px; }
.reel.is-ready .scene.is-live .pf { animation: pf-out .5s ease 2.2s both; }
@keyframes pf-out { to { opacity: 0; transform: translateY(-10px); } }
.pf-label { display: block; font-size: .58rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); margin: 12px 0 5px; }
.pf-bar { display: block; height: 11px; width: var(--w); border-radius: 3px;
  background: linear-gradient(90deg, var(--accent), #e0b877); }
.reel.is-ready .scene.is-live .pf-bar { animation: type-in .5s ease var(--d) backwards; }
@keyframes type-in { from { width: 0; } }
.pf-chips { display: flex; gap: 5px; margin-top: 2px; }
.pf-chips i { flex: 1; height: 22px; border-radius: 4px; background: #1d2532;
  border: 1px solid var(--line); }
.pf-chips i.on { background: rgba(201, 138, 58, .22); border-color: var(--accent); }
.reel.is-ready .scene.is-live .pf-chips i { animation: pop .3s ease var(--d) backwards; }
@keyframes pop { from { opacity: 0; transform: scale(.6); } }

.pqr { position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 10px; background: #0d1117; }
.reel.is-ready .scene.is-live .pqr { animation: pqr-in .55s cubic-bezier(.2,.9,.3,1.2) 2.5s backwards; }
@keyframes pqr-in { from { opacity: 0; transform: translateY(40%); } }
.pqr span { font-size: .6rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); text-align: center; }
.qr { display: grid; grid-template-columns: repeat(21, 1fr); gap: 0;
  width: 92%; aspect-ratio: 1; padding: 7%; background: var(--ink); border-radius: 6px; }
.qr i { background: transparent; }
.qr i.on { background: #0d1117; }
.reel.is-ready .scene.is-live .qr i.on { animation: qr-cell .28s ease var(--d) backwards; }
@keyframes qr-cell { from { opacity: 0; transform: scale(.2); } }

/* ---- scene 2: live preview + countdown ------------------------------------ */

/* Slow drift, so the window reads as a live feed and not a still. */
.reel.is-ready .scene.is-live .rc-live .rc-subject { animation: drift 6s ease-in-out infinite alternate; }
@keyframes drift { from { transform: scale(1.06) translate(-1.2%, .8%); }
                   to   { transform: scale(1.12) translate(1.2%, -.8%); } }
.rc-scan { position: absolute; inset: 0; pointer-events: none;
  background: repeating-linear-gradient(0deg, rgba(255,255,255,.05) 0 1px, transparent 1px 4px); }

.rc-count { position: absolute; display: grid; place-items: center; opacity: 0;
  left: var(--win-x); top: var(--win-y); width: var(--win-w); height: var(--win-h); }
.reel.is-ready .rc-count { opacity: 1; }
.rc-count b { grid-area: 1 / 1; font-size: clamp(3rem, 12vw, 6rem); font-weight: 900;
  color: #fff; text-shadow: 0 4px 26px rgba(0, 0, 0, .6); opacity: 0; }
.reel.is-ready .scene.is-live .rc-count b { animation: tick .85s ease var(--d) both; }
@keyframes tick {
  0% { opacity: 0; transform: scale(1.7); }
  22% { opacity: 1; transform: scale(1); }
  78% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(.82); }
}
/* Flash lives INSIDE the window, under the frame: the shutter lights the subject,
   it does not wash out the printed frame around them. */
.rc-flash { position: absolute; inset: 0; background: #fff; opacity: 0; }
.reel.is-ready .scene.is-live .rc-flash { animation: flash .5s ease 3.1s both; }
@keyframes flash { 0% { opacity: 0; } 12% { opacity: .92; } 100% { opacity: 0; } }

/* ---- scene 3: numbered, and a one-of-one ---------------------------------- */

.reel.is-ready .scene.is-live .rc-settle { animation: settle .75s cubic-bezier(.2,.9,.3,1.15) both; }
@keyframes settle { from { opacity: 0; transform: scale(.9) rotate(-2.5deg); } }

.tag { position: absolute; z-index: 2; white-space: nowrap;
  background: rgba(20, 24, 31, .93); border: 1px solid var(--accent);
  color: var(--ink); border-radius: 999px; padding: 7px 13px;
  font-size: .76rem; font-weight: 800; box-shadow: 0 8px 22px rgba(0, 0, 0, .5); }
.tag b { color: var(--accent); }
/* Anchored to the card's own edges at the height of the printed serial line, so each
   tag labels the thing directly beside it instead of covering the guest's name. */
.tag-no { right: 100%; margin-right: 16px; bottom: 5%; }
.tag-ed { left: 100%; margin-left: 16px; bottom: 5%; }
.tag::after { content: ""; position: absolute; top: 50%; width: 16px;
  border-top: 1px dashed var(--accent); }
.tag-no::after { left: 100%; }
.tag-ed::after { right: 100%; }
.reel.is-ready .scene.is-live .tag-no { animation: tag-in .5s cubic-bezier(.2,.9,.3,1.3) 1s backwards; }
.reel.is-ready .scene.is-live .tag-ed { animation: tag-in .5s cubic-bezier(.2,.9,.3,1.3) 1.6s backwards; }
@keyframes tag-in { from { opacity: 0; transform: translateY(8px) scale(.85); } }

/* ---- scene 4: it prints --------------------------------------------------- */

/* Absolutely positioned children on purpose: as a grid, the row height was sized by
   the card inside it and the 74%/26% split resolved against nothing, so the printer
   grew past the stage and sat on top of the caption. */
.printer { position: relative; height: 100%; aspect-ratio: 1 / 1; }
.printer-out { position: absolute; top: 0; left: 0; right: 0; margin-inline: auto;
  height: 74%; aspect-ratio: 2.5 / 3.5;
  box-shadow: var(--card-shadow); border-radius: 3px; overflow: hidden; }
.printer-out img { width: 100%; height: 100%; display: block; }
.reel.is-ready .scene.is-live .printer-out { animation: eject 2.1s cubic-bezier(.3,.7,.2,1) .5s backwards; }
@keyframes eject { from { transform: translateY(78%); } }
/* Drawn after the card in the DOM so it covers the slot the card climbs out of. */
.printer-body { position: absolute; left: 8%; right: 8%; bottom: 0; height: 26%;
  background: linear-gradient(180deg, #2a3342, #1b222d); border: 1px solid #3a4658;
  border-radius: 10px; box-shadow: 0 14px 30px rgba(0,0,0,.5); }
.printer-slot { position: absolute; top: 10px; left: 12%; width: 76%; height: 7px;
  background: #0d1117; border-radius: 4px; box-shadow: inset 0 2px 4px rgba(0,0,0,.8); }
.printer-body::after { content: ""; position: absolute; left: 6%; right: 6%; bottom: 0;
  height: 34%; background: rgba(0, 0, 0, .22); border-radius: 0 0 9px 9px; }
.printer-led { position: absolute; bottom: 12px; right: 16px; width: 8px; height: 8px;
  border-radius: 50%; background: #58c07a; box-shadow: 0 0 10px #58c07a; }
.reel.is-ready .scene.is-live .printer-led { animation: blink 1.1s ease-in-out infinite; }
@keyframes blink { 50% { opacity: .25; box-shadow: none; } }
/* Rides on the printer body like a status readout, instead of floating over
   the card that is still coming out of the slot. */
.chip { position: absolute; left: 12%; bottom: 9%; z-index: 3;
  background: rgba(20,24,31,.93); border: 1px solid var(--line); border-radius: 999px;
  padding: 6px 12px; font-size: .74rem; font-weight: 800; color: var(--muted); }
.chip b { color: var(--accent); }

/* ---- scene 5: and a digital one ------------------------------------------- */

.deliver { display: flex; align-items: center; justify-content: center; gap: clamp(8px, 3vw, 30px);
  height: 100%; width: 100%; }
/* min-height:0 again — as a flex item the card would otherwise refuse to shrink below
   the 840px natural height of the image inside it. */
.flip { height: 100%; max-height: 100%; min-height: 0; flex: 0 1 auto;
  aspect-ratio: 2.5 / 3.5; perspective: 1100px; }
.flip-inner { position: relative; width: 100%; height: 100%; transform-style: preserve-3d;
  transform: rotateY(180deg); }
.reel.is-ready .scene.is-live .flip-inner { animation: flip 1s cubic-bezier(.5,0,.2,1) .35s backwards; }
@keyframes flip { from { transform: rotateY(0deg); } }
.flip-face { position: absolute; inset: 0; backface-visibility: hidden;
  border-radius: 4px; overflow: hidden; box-shadow: var(--card-shadow); }
.flip-face img { width: 100%; height: 100%; display: block; }
.flip-face.is-back { transform: rotateY(180deg); }
/* Over the QR the wedding back actually prints (template rect 1.96in,2.88in,0.42in
   -> minus the 0.125in bleed -> 73.4%,78.7% of a 2.5x3.5 trim), plus quiet zone. */
.qr-ring { position: absolute; left: 71%; top: 76.2%; width: 21.2%; height: 15.4%;
  border: 2px solid var(--accent); border-radius: 5px; }
.reel.is-ready .scene.is-live .qr-ring { animation: ring 1.6s ease 1.35s infinite backwards; }
@keyframes ring { 0% { opacity: 0; transform: scale(1.5); }
                  25% { opacity: 1; transform: scale(1); }
                  70% { opacity: 1; } 100% { opacity: .5; } }
.beam { flex: 0 0 auto; width: clamp(20px, 6vw, 54px); height: 2px; background: var(--line);
  position: relative; }
.beam::after { content: ""; position: absolute; inset: 0; background: var(--accent);
  transform-origin: left; }
.reel.is-ready .scene.is-live .beam::after { animation: beam .7s ease 1.9s backwards; }
@keyframes beam { from { transform: scaleX(0); } }
.phone-sm { height: 88%; max-height: 88%; min-height: 0; flex: 0 1 auto;
  padding: 12px 9px; border-radius: 20px; }
.phone-sm .phone-screen { display: flex; flex-direction: column; gap: 8px; align-items: center;
  justify-content: center; }
.phone-sm img { width: 74%; border-radius: 4px; display: block; }
.reel.is-ready .scene.is-live .phone-sm .phone-screen > * { animation: pop .45s ease 2.15s backwards; }
.share-row { display: flex; gap: 7px; }
.share-row i { width: 22px; height: 22px; border-radius: 6px; background: #1d2532;
  border: 1px solid var(--line); }

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

@media (max-width: 820px) {
  .reel.is-ready .reel-stage { aspect-ratio: 4 / 5; }
  .reel.is-ready .scene { padding: 18px 14px 14px; }
  .scene-cap h3 { font-size: 1rem; }
  .scene-cap p { font-size: .88rem; }
  /* Titles would wrap to three lines in a 5-up rail on a phone, so the rail
     becomes numbers only and the scene caption carries the title. */
  .reel.is-ready .reel-rail { gap: 6px; }
  .rail-item { text-align: center; font-size: 0; padding-top: 9px; }
  .rail-n { font-size: .72rem; margin: 0; }
  .tag { font-size: .66rem; padding: 4px 9px; }
  .tag::after { display: none; }
  /* No room to flank a phone-sized card, so they sit ON it — over the photo and the
     divider, never over the name or the serial line they are pointing at. */
  .tag-no { right: auto; left: -8px; margin: 0; bottom: 54%; }
  .tag-ed { left: auto; right: -8px; margin: 0; bottom: 39%; }
  .flip { height: 74%; }
  .phone-sm { height: 58%; max-height: 58%; }
  .beam { width: 18px; }
  .chip { font-size: .68rem; padding: 4px 9px; left: 10%; }
}

@media (prefers-reduced-motion: reduce) {
  /* howitworks.js also declines to start, but keep the CSS honest on its own:
     the reel falls all the way back to the static illustrated grid. */
  .reel.is-ready .reel-stage { display: grid; position: static; aspect-ratio: auto;
    padding: 12px; }
  .reel.is-ready .scene { position: static; opacity: 1; visibility: visible;
    transition: none; padding: 22px 18px 18px; }
  .reel.is-ready .reel-rail { display: none; }
  .reel.is-ready .rc-count { opacity: 0; }
  .reel.is-ready .scene * { animation: none !important; }
}
