/* acts.css — per-act styles, namespaced. Act builders fill their own block ONLY.
 *
 * Layout law (P1): `.act[data-current]{display:block}` wins unless an act
 * out-specifies it. Every act below that needs flex says so explicitly.
 */

/* --- act 1: chat (P4) — WhatsApp-adjacent, not a clone --- */
/* NB the P1 trap (see head of file): never a bare `.act-chat{display:...}`. */
.act-chat[data-current] { display: flex; }
.act-chat {
  flex-direction: column;
  height: 100svh;
  max-height: 100svh;
  overflow: hidden;
  background: var(--sumi);
  color: var(--washi);
  font-family: var(--font-ui);
}

/* header */
.ch-bar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: calc(var(--safe-t) + 16px) var(--s4) 11px;
  background: #131009;
  border-bottom: 1px solid var(--sumi-line);
}
.ch-ava {
  flex: 0 0 auto;
  width: 38px; height: 38px;
  border-radius: 50%;
  display: grid; place-items: center;
  font-family: var(--font-display);
  font-size: 0.72rem;
  color: #17120D;
  background: linear-gradient(150deg, var(--amber), #C8892C);
}
.ch-who { display: flex; flex-direction: column; min-width: 0; gap: 2px; }
.ch-name { font-size: var(--t-sm); font-weight: 600; }
.ch-sub {
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--washi-faint);
}

/* the thread — the ONLY scroller in this act, body never moves */
.ch-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scroll-behavior: smooth;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: var(--s4) var(--s3) var(--s4);
  background: linear-gradient(180deg, #100D09 0%, var(--sumi-soft) 100%);
}
/* a real thread sits ON the composer and grows upward. margin-top:auto rather than
   justify-content:flex-end — the latter clips the top rows once the list overflows. */
.ch-list > :first-child { margin-top: auto; }

/* bubbles — they arrive WHOLE, they never type themselves out */
.ch-row { display: flex; animation: chIn 170ms var(--ease) both; }
.ch-row.r-y { justify-content: flex-end; }
.ch-row.r-a { justify-content: flex-start; }
.ch-row.is-burst { margin-top: -4px; }
.ch-b {
  position: relative;
  max-width: 79%;
  padding: 7px 10px 6px;
  border-radius: 15px;
  font-size: var(--t-sm);
  line-height: 1.42;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
}
.r-y .ch-b { background: #3D2E15; border-bottom-right-radius: 5px; }
.r-a .ch-b { background: #241D15; border-bottom-left-radius: 5px; }
.is-burst.r-y .ch-b { border-top-right-radius: 5px; }
.is-burst.r-a .ch-b { border-top-left-radius: 5px; }

/* float is the classic trick: the stamp tucks onto the last line, or drops below */
.ch-meta {
  float: right;
  margin: 3px 0 0 9px;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 0.6rem;
  color: var(--washi-faint);
}
/* read ticks live on ONE side only — this thread is read from Yannic's phone */
.ch-tick {
  width: 15px; height: 9px;
  fill: none;
  stroke: #6FB0E0;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* photo drops — blur up as they decode, and they are NOT tappable viewers */
.ch-b-img { padding: 4px; overflow: hidden; }
.ch-img {
  display: block;
  width: min(62vw, 250px);
  height: 186px;
  object-fit: cover;
  border-radius: 12px;
  filter: blur(15px);
  transform: scale(1.05);
  transition: filter 520ms var(--ease), transform 520ms var(--ease);
}
.ch-img.is-loaded { filter: none; transform: none; }
.ch-b-img .ch-meta {
  position: absolute;
  right: 12px; bottom: 10px;
  float: none;
  margin: 0;
  color: #fff;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.95);
}
.ch-b-img .ch-tick { stroke: #CFE6F7; }

/* system line */
.ch-sys {
  align-self: center;
  margin: 9px 0;
  padding: 5px 13px;
  border-radius: 9px;
  background: rgba(242, 169, 59, 0.11);
  border: 1px solid rgba(242, 169, 59, 0.22);
  color: var(--amber);
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  text-align: center;
  animation: chIn 170ms var(--ease) both;
}

/* typing indicator — runs BEFORE the bubble, never during it */
.ch-type {
  display: flex;
  align-items: center;
  gap: 4px;
  width: -moz-fit-content;
  width: fit-content;
  padding: 12px 14px;
  border-radius: 15px;
  background: #241D15;
  border-bottom-left-radius: 5px;
}
.ch-type[data-from="y"] {
  margin-left: auto;
  background: #3D2E15;
  border-bottom-left-radius: 15px;
  border-bottom-right-radius: 5px;
}
.ch-type i {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--washi-faint);
  animation: chDot 1.25s var(--ease) infinite;
}
.ch-type i:nth-child(2) { animation-delay: 0.16s; }
.ch-type i:nth-child(3) { animation-delay: 0.32s; }

/* the composer slot, held empty until the thread is done — no layout jump */
.ch-foot {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 15px var(--s4) calc(var(--safe-b) + 17px);
  background: #131009;
  border-top: 1px solid var(--sumi-line);
  font-size: var(--t-xs);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--amber);
  opacity: 0;
  transition: opacity 420ms var(--ease);
}
.act-chat.is-cue .ch-foot { opacity: 1; animation: chCue 2.4s var(--ease) 500ms infinite; }

@keyframes chIn { from { opacity: 0; transform: translateY(7px); } }
@keyframes chDot {
  0%, 60%, 100% { opacity: 0.28; transform: translateY(0); }
  30% { opacity: 0.95; transform: translateY(-3px); }
}
@keyframes chCue { 0%, 100% { opacity: 0.5; } 50% { opacity: 1; } }

/* fast-forward AND reduced motion (same class, so neither path can rot alone).
   The two infinite animations are killed outright, not shortened — at 1ms they
   would strobe (the .env-hint lesson from Akt II). */
.act-chat.is-done .ch-list { scroll-behavior: auto; }
.act-chat.is-done .ch-row,
.act-chat.is-done .ch-sys { animation-duration: 1ms !important; }
.act-chat.is-done .ch-img { filter: none; transform: none; transition: none; }
.act-chat.is-done .ch-type i { animation: none; opacity: 0.6; }
.act-chat.is-done .ch-foot { animation: none !important; transition: none; }

/* ------------------------------------------------------------------ */
/* --- act 2: letter — sealed envelope, Behördenbrief, Ausnahme --- */
/* ------------------------------------------------------------------ */
/* NB the P1/P2 trap: never a bare `.act-letter{display:...}` — it would tie with
   `.act{display:none}` on specificity, win on order, and pin this act on screen. */
.act-letter[data-current] { display: flex; }
.act-letter {
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background:
    linear-gradient(rgba(14, 11, 8, .76), rgba(14, 11, 8, .88)),
    url('/assets/letter-paper.webp') center / cover no-repeat,
    #0E0B08;
}

/* --- beat 1: the envelope --- */
/* z-index 3: the cracked halves must fall IN FRONT of the letter unfolding behind them */
.env {
  position: relative;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s4);
  padding: calc(var(--safe-t) + var(--s5)) var(--s4) calc(var(--safe-b) + var(--s4));
}
.env-stage { position: relative; }

.env-card {
  position: relative;
  width: min(320px, 84vw);
  aspect-ratio: 1.36 / 1;
  padding: 18px 18px 0;
  border-radius: 3px;
  background: linear-gradient(168deg, #F7F1E4 0%, #E7DCC4 100%);
  color: #241B12;
  box-shadow: 0 26px 58px rgba(0, 0, 0, .62), 0 1px 0 rgba(255, 255, 255, .42) inset;
  transition: opacity .34s ease .18s, transform .34s var(--ease) .18s;
}
.env-abs { display: grid; gap: 2px; }
.env-abs span {
  font-family: var(--font-ui);
  font-size: .58rem;
  letter-spacing: .04em;
  line-height: 1.35;
  color: #6E5B41;
}
.env-abs span:first-child { font-family: var(--font-display); font-size: .72rem; color: #3A2C1C; }
/* bottom-left, out of the seal's way — sender top-left, reference bottom-left, seal on the fold */
.env-az {
  position: absolute;
  left: 18px; bottom: 13px;
  font-family: var(--font-mono);
  font-size: .52rem;
  letter-spacing: .12em;
  color: #A0342A;
}
/* the fold the seal is stamped across */
.env-fold {
  position: absolute;
  left: 0; right: 0; top: 58%;
  border-top: 1px solid rgba(36, 27, 18, .22);
  box-shadow: 0 -7px 12px -8px rgba(36, 27, 18, .34) inset;
}

.seal {
  position: absolute;
  left: 50%; top: 58%;
  width: 124px; height: 124px;
  margin: 0; padding: 0;
  border: 0; background: none;
  transform: translate(-50%, -50%);
  filter: drop-shadow(0 8px 14px rgba(0, 0, 0, .55));
  cursor: pointer;
}
/* two halves of the same disc, split on a jagged seam — the crack IS the split */
.seal-h { position: absolute; inset: 0; overflow: hidden; border-radius: 50%; }
.sh-l { clip-path: polygon(0 0, 53% 0, 47% 34%, 55% 62%, 49% 100%, 0 100%); }
.sh-r { clip-path: polygon(53% 0, 100% 0, 100% 100%, 49% 100%, 55% 62%, 47% 34%); }
/* crops a 187px-radius circle out of the 1024px source, centred on the wax disc
   (centre ~505,555, half-height ~195) — inside the wax, so no envelope paper shows at the rim */
.seal-h img { position: absolute; width: 340px; height: 340px; left: -106px; top: -122px; }

.env-hint {
  font-family: var(--font-mono);
  font-size: .62rem;
  letter-spacing: .26em;
  color: var(--amber);
  animation: seal-cue 2.6s ease-in-out .8s infinite;
}
@keyframes seal-cue { 0%, 100% { opacity: 1; } 50% { opacity: .38; } }
.act-letter:not([data-phase="0"]) .env-hint { animation: none; }

/* --- the crack: halves rotate apart and fall off the fold --- */
.act-letter:not([data-phase="0"]) .sh-l { animation: crack-l .95s cubic-bezier(.4, .05, .7, 1) forwards; }
.act-letter:not([data-phase="0"]) .sh-r { animation: crack-r .95s cubic-bezier(.4, .05, .7, 1) forwards; }
@keyframes crack-l {
  0%   { transform: none; opacity: 1; }
  16%  { transform: translate(-6px, -3px) rotate(-6deg); opacity: 1; }
  100% { transform: translate(-44px, 78px) rotate(-46deg); opacity: 0; }
}
@keyframes crack-r {
  0%   { transform: none; opacity: 1; }
  16%  { transform: translate(6px, -3px) rotate(6deg); opacity: 1; }
  100% { transform: translate(44px, 84px) rotate(52deg); opacity: 0; }
}
.act-letter:not([data-phase="0"]) .env { pointer-events: none; }
.act-letter:not([data-phase="0"]) .env-card { opacity: 0; transform: translateY(-8px) scale(.97); }
.act-letter:not([data-phase="0"]) .env-hint,
.act-letter:not([data-phase="0"]) .env .act-kicker { opacity: 0; transition: opacity .3s ease .1s; }

/* --- beat 2: the Behördenbrief, unfolding along the same fold line --- */
.brief {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(var(--safe-t) + 20px) 13px calc(var(--safe-b) + 18px);
  opacity: 0;
  pointer-events: none;
  transform: scaleY(.05);
  transform-origin: 50% 58%;
  transition: transform .72s var(--ease) .60s, opacity .4s ease .62s;
}
.act-letter:not([data-phase="0"]) .brief { opacity: 1; transform: none; pointer-events: auto; }

.brief-scroll {
  max-height: 100%;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  width: 100%;
  max-width: 372px;
  padding: 22px 21px 26px;
  border-radius: 2px;
  background: linear-gradient(178deg, #F8F3E7 0%, #EDE3CD 100%);
  color: #241B12;
  box-shadow: 0 24px 56px rgba(0, 0, 0, .62);
}
.brief-scroll::-webkit-scrollbar { display: none; }

/* Amtsdeutsch: left-aligned blocks, hairline rules, no warmth anywhere */
.lb-kopf { border-bottom: 1px solid rgba(36, 27, 18, .3); padding-bottom: 9px; }
.lb-marke { font-family: var(--font-display); font-size: 1.02rem; line-height: 1.2; }
.lb-amt { display: grid; gap: 1px; margin-top: 5px; }
.lb-amt span {
  font-family: var(--font-ui);
  font-size: .6rem;
  letter-spacing: .05em;
  line-height: 1.4;
  color: #6E5B41;
}

.lb-meta {
  display: flex;
  justify-content: space-between;
  gap: var(--s3);
  margin-top: 11px;
  font-family: var(--font-mono);
  font-size: .53rem;
  letter-spacing: .1em;
  color: #7A6446;
}
.lb-az { color: #A0342A; }

.lb-betreff {
  margin: 16px 0 0;
  font-family: var(--font-ui);
  font-size: .77rem;
  font-weight: 700;
  line-height: 1.45;
}
.lb-anrede,
.lb-p {
  margin: 12px 0 0;
  font-family: var(--font-ui);
  font-size: .735rem;
  line-height: 1.62;
  color: #3A2C1C;
}
.lb-anrede { margin-top: 15px; }

.vg-list {
  list-style: none;
  margin: 11px 0 0 13px;
  padding: 0;
  display: grid;
  gap: 12px;
}
.vg { border-left: 1px solid rgba(160, 52, 42, .32); padding-left: 12px; }
.vg-par {
  font-family: var(--font-mono);
  font-size: .55rem;
  letter-spacing: .12em;
  color: #A0342A;
}
.vg-titel {
  margin: 3px 0 0;
  font-family: var(--font-ui);
  font-size: .715rem;
  font-weight: 700;
  line-height: 1.4;
  color: #241B12;
}
.vg-text {
  margin: 4px 0 0;
  font-family: var(--font-ui);
  font-size: .695rem;
  line-height: 1.6;
  color: #4A3A28;
}

.lb-ergebnis { margin-top: 15px; border-top: 1px solid rgba(36, 27, 18, .22); padding-top: 13px; }

.lb-sig { display: grid; gap: 2px; margin-top: 22px; }
.lb-sig-line { font-family: var(--font-display); font-size: .78rem; line-height: 1.4; color: #3A2C1C; }
.lb-sig-line:first-child { border-top: 1px solid rgba(36, 27, 18, .34); padding-top: 7px; width: 148px; }
.lb-sig-line:last-child { font-family: var(--font-ui); font-size: .6rem; color: #7A6446; }

/* --- beat 3: the Ausnahmegenehmigung, stamped over the letter --- */
.ausnahme {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 2;
  padding: var(--s6) var(--s4) calc(var(--safe-b) + var(--s4));
  background: linear-gradient(180deg, transparent 0%, rgba(14, 11, 8, .74) 26%, rgba(14, 11, 8, .95) 62%);
  transform: translateY(112%);
  transition: transform .8s var(--ease);
}
.act-letter[data-phase="2"] .ausnahme { transform: none; }

.au-stempel {
  max-width: 360px;
  margin: 0 auto;
  padding: 16px 18px 17px;
  border: 3px double var(--shoyu);
  border-radius: 3px;
  background: rgba(248, 243, 231, .97);
  color: #241B12;
  transform: rotate(-1.6deg);
  box-shadow: 0 18px 40px rgba(0, 0, 0, .6);
}
.act-letter[data-phase="2"] .au-stempel {
  animation: au-slam .5s cubic-bezier(.2, 1.45, .4, 1) .5s backwards;
}
@keyframes au-slam {
  0%   { opacity: 0; transform: rotate(-7deg) scale(1.9); }
  55%  { opacity: 1; }
  100% { opacity: 1; transform: rotate(-1.6deg) scale(1); }
}

.au-titel {
  margin: 0;
  font-family: var(--font-mono);
  font-size: .82rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: #A0342A;
}
.au-text {
  margin: 10px 0 0;
  font-family: var(--font-display);
  font-size: .93rem;
  line-height: 1.5;
  color: #241B12;
}
.au-schluss {
  display: grid;
  gap: 2px;
  margin: 13px 0 0;
  border-top: 1px solid rgba(36, 27, 18, .26);
  padding-top: 9px;
}
.au-schluss-line {
  font-family: var(--font-ui);
  font-size: .64rem;
  line-height: 1.45;
  color: #7A6446;
}

/* tap mid-animation, or prefers-reduced-motion: land every beat on its end state.
   One rule, so the two paths cannot drift apart. */
.act-letter.is-done *,
.act-letter.is-done *::before,
.act-letter.is-done *::after {
  animation-duration: 1ms !important;
  animation-delay: 0ms !important;
  transition-duration: 1ms !important;
  transition-delay: 0ms !important;
}
/* the one infinite animation in this act — 1ms + infinite would strobe, so kill it outright */
.act-letter.is-done .env-hint { animation: none !important; }

/* ------------------------------------------------------------------ */
/* --- act 3: reise — stamped polaroid strip + the two-line beat --- */
/* ------------------------------------------------------------------ */
.act-reise[data-current] { display: flex; }
.act-reise {
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
  background: radial-gradient(130% 70% at 50% 12%, #1B1410 0%, #0E0B08 68%);
}

.act-reise[data-phase="0"] .reise-outro { display: none; }
.act-reise:not([data-phase="0"]) .reise-wrap { display: none; }

.reise-wrap {
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  width: 100%;
  padding: calc(var(--safe-t) + var(--s5)) 0 calc(var(--safe-b) + var(--s4));
}
.reise-head { text-align: center; padding: 0 var(--s4); }
.reise-titel {
  font-family: var(--font-display);
  font-size: var(--t-lg);
  line-height: 1.25;
  margin: var(--s2) 0 0;
  color: var(--washi);
}

.reise-strip {
  display: flex;
  gap: var(--s4);
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: var(--s3) max(var(--s4), calc(50vw - 134px));
}
.reise-strip::-webkit-scrollbar { display: none; }

.pola {
  flex: 0 0 258px;
  scroll-snap-align: center;
  margin: 0;
  padding: 11px 11px 14px;
  border-radius: 3px;
  background: linear-gradient(178deg, #F7F1E4 0%, #E9DEC7 100%);
  color: #241B12;
  box-shadow: 0 18px 38px rgba(0, 0, 0, .58), 0 1px 0 rgba(255, 255, 255, .4) inset;
  transform: rotate(var(--rot, 0deg));
}
.pola-win {
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: #1B1410;
}
.pola-win img { width: 100%; height: 100%; object-fit: cover; display: block; }

.pola figcaption { padding-top: 10px; }
.pola-stamp {
  display: inline-block;
  transform: rotate(-2.6deg);
  font-family: var(--font-mono);
  font-size: .56rem;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: #A0342A;
  border: 1.5px solid rgba(160, 52, 42, .6);
  border-radius: 2px;
  padding: 3px 7px;
  opacity: .92;
}
.pola-line {
  margin: 9px 0 0;
  font-family: var(--font-display);
  font-size: .8rem;
  line-height: 1.45;
  color: #3A2C1C;
}

.reise-hint {
  text-align: center;
  font-family: var(--font-mono);
  font-size: .6rem;
  letter-spacing: .26em;
  color: var(--washi-faint);
}

.reise-outro {
  flex: 1;
  display: grid;
  place-items: center;
  padding: calc(var(--safe-t) + var(--s6)) var(--s5) calc(var(--safe-b) + var(--s6));
  text-align: center;
}
.outro-line {
  grid-area: 1 / 1;
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--t-xl);
  line-height: 1.3;
  color: var(--washi);
  opacity: 0;
  transition: opacity .6s var(--ease);
}
.act-reise[data-phase="1"] .o1 { opacity: 1; }
.act-reise[data-phase="2"] .o2 { opacity: 1; }

/* ------------------------------------------------------------------ */
/* --- act 4: menu — the omakase gift card, the one scrolling act --- */
/* ------------------------------------------------------------------ */
.act-menu { background: radial-gradient(120% 55% at 50% 0%, #1B1410 0%, #0E0B08 72%); }

.menu-wrap {
  max-width: 430px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  padding: calc(var(--safe-t) + var(--s5)) var(--s4) calc(var(--safe-b) + var(--s3));
}
.menu-head { text-align: center; }
.menu-titel {
  font-family: var(--font-display);
  font-size: var(--t-xl);
  line-height: 1.15;
  margin: var(--s2) 0 0;
  color: var(--washi);
}

.menu-row {
  display: grid;
  gap: var(--s2);
  border-top: 1px solid var(--sumi-line);
  padding-top: var(--s3);
}
.menu-label {
  font-family: var(--font-mono);
  font-size: .62rem;
  letter-spacing: .32em;
  color: var(--amber);
}
.menu-text {
  font-family: var(--font-display);
  font-size: var(--t-md);
  line-height: 1.5;
  color: var(--washi);
}

.brigade { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s3); }
.brig {
  margin: 0;
  overflow: hidden;
  border: 1px solid var(--sumi-line);
  border-radius: 4px;
  background: rgba(245, 237, 221, .045);
}
.brig-win { aspect-ratio: 1 / 1; background: #1B1410; }
.brig-win img {
  width: 100%; height: 100%; display: block;
  object-fit: cover;
  filter: saturate(.85) contrast(1.04);
}
.brig figcaption { padding: var(--s2) var(--s3) var(--s3); }
.brig-name { font-family: var(--font-mono); font-size: .66rem; letter-spacing: .18em; color: var(--washi); }
.brig-pos { font-family: var(--font-display); font-size: .92rem; color: var(--amber); margin-top: 3px; }
.brig-spec { font-family: var(--font-ui); font-size: .71rem; line-height: 1.35; color: var(--washi-dim); margin-top: 6px; }

.koch {
  display: flex;
  gap: var(--s3);
  align-items: center;
  padding: var(--s3);
  border: 1px solid var(--sumi-line);
  border-radius: 4px;
  background: linear-gradient(180deg, rgba(242, 169, 59, .07), transparent 70%);
}
.koch-art {
  flex: 0 0 auto;
  width: 92px; height: 122px;
  object-fit: cover;
  border-radius: 3px;
}
.koch-name { font-family: var(--font-display); font-size: 1rem; line-height: 1.3; color: var(--washi); margin-top: 5px; }
.koch-line { font-family: var(--font-ui); font-size: .76rem; line-height: 1.45; color: var(--washi-dim); margin-top: 7px; }

.gaenge {
  list-style: none;
  margin: 0;
  padding: var(--s3) 0 0;
  display: grid;
  gap: var(--s3);
  border-top: 1px solid var(--sumi-line);
}
.gang { display: flex; gap: var(--s3); align-items: center; }
.gang-art {
  flex: 0 0 auto;
  width: 62px; height: 62px;
  object-fit: cover;
  border-radius: 50%;
  border: 1px solid rgba(242, 169, 59, .3);
}
.gang-kopf { font-family: var(--font-display); font-size: 1rem; color: var(--washi); }
.gang-nr { font-family: var(--font-mono); font-size: .58rem; letter-spacing: .2em; color: var(--amber); margin-right: 9px; }
.gang-note { font-family: var(--font-ui); font-size: .74rem; line-height: 1.4; color: var(--washi-dim); margin-top: 4px; }

/* sticky, never fixed — fixed elements must be direct children of <body> */
.weiter {
  position: sticky;
  bottom: calc(var(--safe-b) + 14px);
  align-self: center;
  margin-top: var(--s3);
  padding: 10px 24px;
  border-radius: 999px;
  border: 1px solid rgba(242, 169, 59, .45);
  background: rgba(14, 11, 8, .74);
  color: var(--amber);
  font-family: var(--font-mono);
  font-size: .64rem;
  letter-spacing: .24em;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

/* ------------------------------------------------------------------ */
/* --- act 5: finale — noren parts, real Kyoto photo, three lines --- */
/* ------------------------------------------------------------------ */
.act-finale { overflow: hidden; background: #0E0B08; }

.fin-photo {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  transform: scale(1.06);
  /* starts before the curtain finishes parting, so the reveal shows Kyoto, not black */
  animation: fin-in 1.3s ease .35s forwards, fin-kb 24s linear .35s forwards;
}
@keyframes fin-in { to { opacity: 1; } }
@keyframes fin-kb {
  from { transform: scale(1.06) translate3d(0, 0, 0); }
  to   { transform: scale(1.24) translate3d(-2%, -3%, 0); }
}

.fin-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(180deg,
    rgba(14, 11, 8, .58) 0%, rgba(14, 11, 8, .12) 30%,
    rgba(14, 11, 8, .78) 72%, rgba(14, 11, 8, .96) 100%);
}

.noren {
  position: absolute; top: 0; bottom: 0;
  width: 51%;
  z-index: 3;
  background:
    repeating-linear-gradient(90deg, transparent 0 36px, rgba(14, 11, 8, .5) 36px 38px),
    linear-gradient(180deg, #2A1C15 0 22px, #A8342A 22px, #7E2519 100%);
  box-shadow: 0 0 44px rgba(0, 0, 0, .65);
}
.n-l { left: 0;  animation: noren-l 1.5s cubic-bezier(.72, 0, .18, 1) .45s forwards; }
.n-r { right: 0; animation: noren-r 1.5s cubic-bezier(.72, 0, .18, 1) .45s forwards; }
@keyframes noren-l { to { transform: translateX(-101%); } }
@keyframes noren-r { to { transform: translateX(101%); } }

.fin-text {
  position: absolute; inset: 0;
  z-index: 4;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: var(--s3);
  text-align: center;
  padding: calc(var(--safe-t) + var(--s6)) var(--s5) calc(var(--safe-b) + var(--s6));
}
.fin-line {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--t-lg);
  line-height: 1.4;
  color: var(--washi);
  text-shadow: 0 2px 20px rgba(0, 0, 0, .92);
  opacity: 0;
  animation: fin-line 1.1s var(--ease) forwards;
}
.fin-line:nth-of-type(1) { animation-delay: 3.6s; }
.fin-line:nth-of-type(2) { animation-delay: 5.6s; }
.fin-line:nth-of-type(3) { animation-delay: 7.6s; }
.fin-line-key { font-size: var(--t-xl); }
@keyframes fin-line { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

.fin-sign {
  margin-top: var(--s4);
  opacity: 0;
  animation: fin-line 1.2s var(--ease) 9.9s forwards;
  text-shadow: 0 2px 20px rgba(0, 0, 0, .92);
}
.fin-reprise { margin: 0 0 var(--s3); font-family: var(--font-mono); font-size: .6rem; letter-spacing: .16em; color: var(--washi-faint); }
.fin-gruss { margin: 0; font-family: var(--font-display); font-size: var(--t-md); color: var(--washi); }
.fin-von { margin: 6px 0 0; font-family: var(--font-mono); font-size: .66rem; letter-spacing: .2em; color: var(--amber); }

/* tap mid-run, or prefers-reduced-motion: land every keyframe on its end state */
.act-finale.is-done .noren { animation: none; transform: translateX(-101%); }
.act-finale.is-done .n-r { transform: translateX(101%); }
.act-finale.is-done .fin-photo { animation: none; opacity: 1; transform: scale(1.16); }
.act-finale.is-done .fin-line,
.act-finale.is-done .fin-sign { animation: none; opacity: 1; transform: none; }

/* ------------------------------------------------------------------ */
/* --- act 6: rsvp — ticket recap, one button, the stamp --- */
/* ------------------------------------------------------------------ */
.act-rsvp[data-current] { display: flex; }
.act-rsvp {
  align-items: center;
  justify-content: center;
  background: radial-gradient(120% 80% at 50% 20%, #1B1410 0%, #0E0B08 66%);
}

.rsvp-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s4);
  padding: calc(var(--safe-t) + var(--s5)) var(--s4) calc(var(--safe-b) + var(--s5));
}

/* The flip is deliberately FLAT (no preserve-3d, no backface-visibility): WebKit
 * drops the text layer of a backface-hidden child inside a preserve-3d parent, so
 * the card painted blank. Two flattened rotateY(180deg) cancel out, which gives the
 * same read: the card turns edge-on at the halfway point and the faces swap there. */
.rsvp-card {
  position: relative;
  width: min(322px, 84vw);
  height: 292px;
  transition: transform 1.05s cubic-bezier(.62, .02, .2, 1);
}
.act-rsvp[data-state="accepted"] .rsvp-card { transform: rotateY(180deg); }

.rsvp-face {
  position: absolute; inset: 0;
  display: flex;
  flex-direction: column;
  padding: 22px 20px 18px;
  border-radius: 6px;
  background: linear-gradient(178deg, #F7F1E4 0%, #EDE2CC 100%);
  color: #241B12;
  box-shadow: 0 22px 52px rgba(0, 0, 0, .6);
  transition: opacity 0s linear .52s;
}
.rsvp-back {
  transform: rotateY(180deg);
  align-items: center;
  justify-content: center;
  gap: var(--s4);
  opacity: 0;
}
.act-rsvp[data-state="accepted"] .rsvp-front { opacity: 0; }
.act-rsvp[data-state="accepted"] .rsvp-back { opacity: 1; }

.rsvp-title {
  font-family: var(--font-display);
  font-size: 2rem;
  line-height: 1.1;
  text-align: center;
  margin: 16px 0 18px;
}
.rsvp-rows {
  display: grid;
  gap: 10px;
  justify-items: center;
  border-top: 1px dashed rgba(36, 27, 18, .3);
  padding-top: 14px;
  flex: 1;
  align-content: start;
}
.rsvp-rows span { font-family: var(--font-display); font-size: 1.05rem; color: #3A2C1C; }

.rsvp-stamp {
  font-family: var(--font-mono);
  font-size: 1.5rem;
  letter-spacing: .18em;
  color: #A0342A;
  border: 4px double rgba(160, 52, 42, .72);
  border-radius: 4px;
  padding: 10px 16px;
  opacity: 0;
  transform: rotate(-9deg);
}
.act-rsvp[data-state="accepted"] .rsvp-stamp {
  opacity: 1;
  animation: stamp .5s cubic-bezier(.2, 1.5, .4, 1) .9s backwards;
}
@keyframes stamp {
  0%   { opacity: 0; transform: rotate(-16deg) scale(2.7); }
  55%  { opacity: 1; }
  100% { opacity: 1; transform: rotate(-9deg) scale(1); }
}
.rsvp-success {
  margin: 0;
  font-family: var(--font-display);
  font-size: .95rem;
  text-align: center;
  color: #3A2C1C;
  opacity: 0;
  transition: opacity .5s var(--ease) 1.3s;
}
.act-rsvp[data-state="accepted"] .rsvp-success { opacity: 1; }

.rsvp-btn {
  font-family: var(--font-mono);
  font-size: .72rem;
  letter-spacing: .22em;
  padding: 15px 28px;
  border-radius: 999px;
  border: 1px solid rgba(242, 169, 59, .6);
  background: rgba(242, 169, 59, .1);
  color: var(--amber);
  cursor: pointer;
}
.act-rsvp[data-state="sending"] .rsvp-btn { opacity: .45; }
.act-rsvp[data-state="accepted"] .rsvp-btn { display: none; }

.rsvp-error {
  display: none;
  margin: 0;
  max-width: 280px;
  text-align: center;
  font-family: var(--font-ui);
  font-size: .78rem;
  line-height: 1.4;
  color: var(--shoyu);
}
.act-rsvp[data-state="error"] .rsvp-error { display: block; }

.rsvp-replay {
  display: none;
  font-family: var(--font-mono);
  font-size: .62rem;
  letter-spacing: .24em;
  color: var(--washi-faint);
  text-decoration: none;
  border-bottom: 1px solid var(--washi-faint);
  padding-bottom: 2px;
}
.act-rsvp[data-state="accepted"] .rsvp-replay { display: block; }

@media (prefers-reduced-motion: reduce) {
  .rsvp-card, .rsvp-success { transition-duration: 1ms; }
  .rsvp-face, .rsvp-success { transition-delay: 0s; }
  .act-rsvp[data-state="accepted"] .rsvp-stamp { animation: none; }
  .outro-line { transition-duration: 1ms; }
}

/* --- shared act furniture (P1) --- */
.act-body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--s4);
  padding: calc(var(--safe-t) + var(--s6)) var(--s4) calc(var(--safe-b) + var(--s5));
  min-height: 100svh;
  text-align: center;
}

.act-kicker {
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--washi-faint);
}

.act-line {
  font-family: var(--font-display);
  font-size: var(--t-lg);
  line-height: 1.45;
  color: var(--washi);
}

/* ------------------------------------------------------------------ */
/* --- P5: decorative video loops (js/loop.js) --- */
/* ------------------------------------------------------------------ */
/* Every loop is near-black footage, so `screen` drops the black and leaves only
   the light — steam, lanterns, flame — over whatever is already there. The
   poster frame gets the identical treatment, which is why a blocked autoplay
   looks like a still of the same shot rather than a missing panel. */
.vloop {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
  mix-blend-mode: screen;
  z-index: 0;
}

/* act 0 — steam behind the ticket. Injected only after the ticket has filled
   (loader.js), so act 0 still makes zero image requests before it is ready. */
.vloop-steam { opacity: .22; }
.act-loader .ticket, .act-loader .enter-cue { position: relative; z-index: 1; }
.act-loader.is-done .lantern { animation: none; }

/* act 2 — the wok flame under the Ausnahmegenehmigung stamp, beat 3 only */
.vloop-flame {
  opacity: .5;
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 34%);
  mask-image: linear-gradient(180deg, transparent 0%, #000 34%);
}
.au-stempel { position: relative; z-index: 1; }

/* act 4 — lanterns in the header band of the gift card */
.menu-head {
  position: relative;
  overflow: hidden;
  padding: var(--s4) var(--s3) var(--s5);
  border-radius: 4px;
}
.menu-head > :not(.vloop) { position: relative; z-index: 1; }
.vloop-lantern {
  opacity: .3;
  -webkit-mask-image: linear-gradient(180deg, #000 0%, transparent 96%);
  mask-image: linear-gradient(180deg, #000 0%, transparent 96%);
}

/* ------------------------------------------------------------------ */
/* --- P5: one act-to-act crossfade, same timing everywhere --- */
/* ------------------------------------------------------------------ */
/* Act 0 is excluded on purpose: it paints from the inline critical CSS long
   before this sheet lands, so a fade here would flash it and push LCP. */
.act[data-current]:not(.act-loader) { animation: act-in 300ms var(--ease) both; }
@keyframes act-in { from { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .act[data-current]:not(.act-loader) { animation: none; }
}
