/* ===== Schriften, lokal mitgeliefert (Etappe 0) =====
   Früher kamen Luckiest Guy, Nunito Sans und Permanent Marker von fonts.googleapis.com.
   Damit hing das Aussehen an fremder Erreichbarkeit — ohne Netz fiel alles auf eine
   Systemschrift zurück, und das gesamte Layout ist auf die Metriken dieser drei
   Schriften eingemessen (Rekordschild, Sprach-Aufkleber, sämtliche Canvas-Texte).
   ⚠ Nunito Sans ist eine Variable Font: Google liefert für 400, 700 und 800 DIESELBE
   Datei und unterscheidet nur über font-weight. Genau so steht es hier — drei Regeln,
   eine Datei je Subset. Wer hier etwas ändert, darf das nicht auseinanderziehen.
   Nur latin und latin-ext (deutsch + englisch); Kyrillisch und Vietnamesisch wären
   toter Ballast. Der Kursiv-Schnitt von Nunito Sans ist raus — nirgends verwendet.
   Lizenzen: assets/fonts/LIZENZ.txt */
/* Luckiest Guy 400 · latin-ext */
@font-face{font-family:'Luckiest Guy';font-style:normal;font-weight:400;font-display:swap;
  src:url('../assets/fonts/luckiest-guy-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
/* Luckiest Guy 400 · latin */
@font-face{font-family:'Luckiest Guy';font-style:normal;font-weight:400;font-display:swap;
  src:url('../assets/fonts/luckiest-guy-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
/* Nunito Sans 400 · latin-ext */
@font-face{font-family:'Nunito Sans';font-style:normal;font-weight:400;font-display:swap;
  src:url('../assets/fonts/nunito-sans-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
/* Nunito Sans 400 · latin */
@font-face{font-family:'Nunito Sans';font-style:normal;font-weight:400;font-display:swap;
  src:url('../assets/fonts/nunito-sans-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
/* Nunito Sans 700 · latin-ext */
@font-face{font-family:'Nunito Sans';font-style:normal;font-weight:700;font-display:swap;
  src:url('../assets/fonts/nunito-sans-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
/* Nunito Sans 700 · latin */
@font-face{font-family:'Nunito Sans';font-style:normal;font-weight:700;font-display:swap;
  src:url('../assets/fonts/nunito-sans-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
/* Nunito Sans 800 · latin-ext */
@font-face{font-family:'Nunito Sans';font-style:normal;font-weight:800;font-display:swap;
  src:url('../assets/fonts/nunito-sans-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
/* Nunito Sans 800 · latin */
@font-face{font-family:'Nunito Sans';font-style:normal;font-weight:800;font-display:swap;
  src:url('../assets/fonts/nunito-sans-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
/* Permanent Marker 400 · latin */
@font-face{font-family:'Permanent Marker';font-style:normal;font-weight:400;font-display:swap;
  src:url('../assets/fonts/permanent-marker-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}

/* KIPPEL — Design-Identität „Umzugstag" (kippel-art).
   Palette fest; Luckiest Guy für Titel + Hero-Score, Nunito Sans (tabellarisch) für kleine
   Zahlen, Permanent Marker für Krakel-Labels. HUD bewusst ohne Kästchen — der Header liest
   sich wie mit Marker aufs Bild geschrieben, nicht wie ein Dashboard. */
:root{
  --abend-top:#2B2430; --abend-bot:#4A3A50;
  --karton:#C89B6C; --kreide:#F2EDE4; --orange:#E8722C;
  --werkbank:#3E3A45; --frost:#9CC7DE;
  --dim:#a99fb0; --danger:#e0603f; --ok:#8cc59a;

  --font-display:'Luckiest Guy', system-ui, sans-serif;
  --font-ui:'Nunito Sans', -apple-system, 'Segoe UI', system-ui, sans-serif;
  --font-marker:'Permanent Marker', var(--font-ui);
  --radius:14px;
  font-synthesis:none;
}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{height:100%}
body{
  background:linear-gradient(var(--abend-top),var(--abend-bot));
  color:var(--kreide);
  font-family:var(--font-ui);
  display:flex; flex-direction:column; align-items:center;
  overflow:hidden; touch-action:none; user-select:none;-webkit-user-select:none;
}
#wrap{width:100%;max-width:480px;height:100%;display:flex;flex-direction:column;padding:calc(8px + env(safe-area-inset-top)) 10px calc(10px + env(safe-area-inset-bottom))}

/* Stage — HUD wird direkt ins Canvas gezeichnet (ui/hud.js), kein DOM-Balken mehr */
#stage{position:relative;flex:1;border-radius:var(--radius);overflow:hidden;background:linear-gradient(var(--abend-top),var(--abend-bot));box-shadow:0 6px 24px rgba(0,0,0,.35)}
canvas{display:block;width:100%;height:100%}

/* Overlays */
.overlay{
  position:absolute;inset:0;background:rgba(20,16,26,.86);backdrop-filter:blur(6px);
  display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center;
  padding:24px;gap:22px;
}
/* Titel als roter Gummistempel — wie auf einem Umzugskarton aufgedruckt.
   pointer-events:none, weil er beim Einschlag mit scale(2.6) startet und sonst Taps abfängt. */
.overlay h1.title-stamp{
  pointer-events:none;
  font-family:var(--font-display);font-size:44px;letter-spacing:.06em;line-height:1;
  color:var(--orange);text-transform:uppercase;
  border:4px solid var(--orange);border-radius:10px;padding:8px 18px 2px;
  transform:rotate(-5deg);
  box-shadow:inset 0 0 0 2px rgba(232,114,44,.35);
  text-shadow:0 0 14px rgba(232,114,44,.35);
  opacity:.94;
  margin:14px 0 10px;
}
.overlay p{color:var(--kreide);opacity:.82;font-size:14px;line-height:1.7;max-width:300px}
.overlay .big{font-family:var(--font-display);font-size:58px;color:var(--kreide);text-shadow:0 3px 0 rgba(0,0,0,.4)}
.tag{font-family:var(--font-marker);font-size:14px;letter-spacing:.04em;color:var(--orange);transform:rotate(-2deg)}
.hidden{display:none !important}
/* iOS-Tap-Fix: das body-weite touch-action:none (fürs Canvas-Ziehen) schluckt auf iPhone
   gelegentlich den Tap→Click der Overlay-Bedienelemente — der Button „drückt", aber nichts
   passiert. `manipulation` stellt den Tap sicher (und killt Doppeltipp-Zoom). Canvas bleibt none. */
.overlay button, .overlay input, #lbTab{touch-action:manipulation}
/* Scrollbare Listen brauchen pan-y, sonst schluckt das body-weite touch-action:none das
   Finger-Scrollen (am Desktop mit der Maus unauffaellig, am iPhone tot — s. #shopList). */
#shopList, #lbList{touch-action:pan-y}

/* ===== Homescreen: Heckklappe + Lebende Ladefläche =====
   Kein Scrim — die Demo-Szene (selbststapelnder Turm) scheint durch. Lesbarkeit kommt
   aus den Karton-Buttons selbst. */
.overlay.menu{
  background:none;backdrop-filter:none;
  justify-content:flex-start;padding-top:8vh;gap:0;
  overflow:hidden; /* das hochfahrende Rolltor bleibt in der Stage */
}
.menu-stack{display:flex;flex-direction:column;align-items:center;gap:0}
/* Titel knallt nach der Toröffnung als Stempel rein (Wumms-Sound folgt in Phase D) */
#menuTitle{
  margin:0 0 54px;
  filter:drop-shadow(0 0 12px rgba(232,114,44,.3));
  animation:stampSlam .5s cubic-bezier(.2,2.4,.4,1) .1s both;
}
@keyframes stampSlam{
  0%{transform:scale(2.6) rotate(-14deg);opacity:0}
  60%{transform:scale(.92) rotate(-4deg);opacity:1}
  100%{transform:scale(1) rotate(-5deg);opacity:.94}
}
.menu-buttons{display:flex;flex-direction:column;align-items:center;gap:30px}

/* Menü-Buttons = Frachtaufkleber (Inline-SVG #etikettDef in index.html): Kreide-Papier mit
   gerissenen Kanten und handgestempeltem Orange-Rahmen — dieselbe Papier-Sprache wie
   Lieferschein, Katalog und Rangliste. Das frühere Holz war eine Fremdfarbe (nicht in der
   kippel-art-Palette) und ist deshalb raus. Lebendig statt stumpf: jedes Etikett hat eigene
   Neigung, eigene Klebeband-Winkel und eine eigene Wiege-Phase; SPIELEN atmet zusätzlich. */
.etikett-btn{
  position:relative;cursor:pointer;
  width:264px;height:84px;padding:0;border:none;background:none;
  display:flex;align-items:center;justify-content:center;
  transform:rotate(var(--rot,0deg));
  filter:drop-shadow(2px 4px 0 rgba(0,0,0,.42));   /* crisp, 0 Blur */
  transition:transform .07s, translate .07s, filter .07s;
  animation:
    etikettRein .52s cubic-bezier(.2,1.5,.4,1) var(--delay,0s) both,
    etikettWiegen var(--sway,5.5s) ease-in-out var(--swaydelay,3s) infinite;
}
.et-papier{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
.et-wort{
  position:relative;z-index:1;line-height:1;
  font-family:var(--font-display);font-size:29px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--orange);
  text-shadow:0 1px 0 rgba(120,50,10,.28);
  transform:rotate(-.8deg);
}
/* Klebeband-Ecken (Frost, wie an Lieferschein/Katalog) — je Button anders gedreht */
.etikett-btn::before,.etikett-btn::after{
  content:"";position:absolute;width:52px;height:17px;z-index:2;pointer-events:none;
  background:rgba(156,199,222,.42);border:1px solid rgba(255,255,255,.34);
}
.etikett-btn::before{left:-9px;top:-5px;transform:rotate(var(--tapeA,-20deg))}
.etikett-btn::after{right:-9px;bottom:-5px;transform:rotate(var(--tapeB,18deg))}
/* kleiner Gebrauchsfleck — nur auf einem Etikett, damit es keine Klone sind */
.et-fleck{
  position:absolute;right:30px;top:13px;width:17px;height:11px;z-index:1;pointer-events:none;
  background:rgba(150,110,60,.2);border-radius:52% 38% 56% 44%;transform:rotate(12deg);
}
/* Beta-Stempel am Duell-Etikett: das Fernduell läuft über einen fremden Gratis-Dienst und
   klappt nicht in jedem Mobilfunknetz — das soll man sehen, BEVOR man tippt. Als schräger
   Gummistempel in Stempel-Rot (#B5342B, dieselbe Farbe wie der Aufdruck auf dem Wellpappe-
   Deck und im App-Symbol) — keine neue Farbfamilie, und er hebt sich klar vom Orange ab.
   Sitzt bewusst leicht ÜBER der oberen Etikettkante: so wirkt er nachträglich aufgedrückt
   und überdeckt kein einziges Zeichen des Wortes darunter.
   ⚠ pointer-events:none — reine Deko darf in diesem Projekt niemals einen Tap schlucken
   (dokumentierter Bug „erst beim 5. Mal passiert was"). */
.et-beta{
  position:absolute;right:8px;top:-5px;z-index:2;pointer-events:none;font-style:normal;
  font-family:var(--font-display);font-size:12px;letter-spacing:.09em;text-transform:uppercase;
  color:#B5342B;background:rgba(242,237,228,.92);
  border:2.5px solid #B5342B;border-radius:5px;padding:3px 7px 1px;
  box-shadow:inset 0 0 0 1px rgba(181,52,43,.35);
  transform:rotate(-11deg);
}
/* nur die Custom-Props pro Etikett — so bleibt die transform-Regel eindeutig */
/* ⚠ Der Homescreen steht KOMPLETT, BEVOR das Tor losfährt (Alens Befund ×2 „Tor fährt hoch
   und man sieht erst komischen Hintergrund"): alle Auftritte laufen mit Mini-Delays
   (0,1–0,3 s) unsichtbar hinter dem geschlossenen Tor ab — das Tor startet erst per
   `.bereit` (main.js, nach dem ersten gezeichneten Canvas-Frame) und gibt damit immer den
   FERTIGEN Homescreen frei. Blind-Taps fängt #torSchild bis zum Tor-animationend. */
.etikett-btn:nth-child(1){--rot:-1.6deg;--delay:.1s;--sway:5.4s;--swaydelay:2.5s;--swayamt:.55deg;--tapeA:-22deg;--tapeB:16deg}
.etikett-btn:nth-child(2){--rot:1.2deg;--delay:.2s;--sway:6.3s;--swaydelay:3.1s;--swayamt:-.5deg;--tapeA:14deg;--tapeB:-19deg}
.etikett-btn:nth-child(3){--rot:-.8deg;--delay:.3s;--sway:5.9s;--swaydelay:2.8s;--swayamt:.45deg;--tapeA:-17deg;--tapeB:21deg}
/* Auftritt: flattert wie Papier rein (Fall + Ausdrehen) */
@keyframes etikettRein{
  /* pointer-events erst ab dem sichtbaren Teil: vorher sitzt der Button unsichtbar und 52px
     zu hoch — ein Blind-Tap dort soll nicht schon SPIELEN auslösen */
  0%{opacity:0;pointer-events:none;transform:rotate(calc(var(--rot,0deg) - 7deg)) translateY(-52px)}
  60%{opacity:1;pointer-events:auto;transform:rotate(calc(var(--rot,0deg) + 3deg)) translateY(5px)}
  80%{transform:rotate(calc(var(--rot,0deg) - 1.5deg)) translateY(-2px)}
  100%{opacity:1;transform:rotate(var(--rot,0deg)) translateY(0)}
}
/* Idle „Papier im Luftzug": winzig, je Button andere Dauer/Phase → nie synchron */
@keyframes etikettWiegen{
  0%,100%{transform:rotate(var(--rot,0deg)) translateY(0)}
  50%{transform:rotate(calc(var(--rot,0deg) + var(--swayamt,.5deg))) translateY(-1.5px)}
}
/* SPIELEN ist die Hauptaktion: die Tinte atmet, der Blick landet zuerst dort */
.etikett-btn:nth-child(1) .et-wort{animation:tinteAtmen 2.8s ease-in-out 3.2s infinite}
@keyframes tinteAtmen{
  0%,100%{text-shadow:0 1px 0 rgba(120,50,10,.28)}
  50%{text-shadow:0 1px 0 rgba(120,50,10,.28), 0 0 13px rgba(232,114,44,.6)}
}
/* ⚠ :active darf die animation-Property ÜBERHAUPT NICHT anfassen — auch kein
   animation-play-state! iOS Safari setzt die Animationen beim :active-Wechsel neu auf
   (Desktop pausiert korrekt — deshalb war der Bug dort unsichtbar): beim Loslassen spielt
   etikettRein (~2 s Delay, 0%-Frame opacity 0 / −52px / pointer-events:none) erneut, und
   der auf iOS erst NACH touchend per Hit-Test synthetisierte Click trifft ins Leere →
   „Button verschwindet kurz, kommt wieder, nichts passiert" (Alens toter Homescreen).
   Press-Effekt deshalb über die SEPARATE translate-Property: die wird von den
   transform-Keyframes nicht überschrieben und lässt die Animationen völlig in Ruhe. */
.etikett-btn:active{
  translate:0 4px;
  filter:drop-shadow(1px 1px 0 rgba(0,0,0,.42)) brightness(.96);
}
.etikett-btn:active .et-wort{color:#c25a17}   /* Tinte dunkelt beim Aufdrücken nach */
/* Shake-Feedback: ADDITIV zur Basis-Liste — etikettRein bleibt namensgleich in der Liste und
   damit „finished" (kein Neustart beim Klassen-Toggle), nur der Shake kommt dazu und geht. */
.etikett-btn.wiggle{
  animation:
    etikettRein .52s cubic-bezier(.2,1.5,.4,1) var(--delay,0s) both,
    etikettWiegen var(--sway,5.5s) ease-in-out var(--swaydelay,3s) infinite,
    wiggle .4s ease;
}
/* Zweite Verteidigungslinie: nach dem ersten Auftritt setzt main.js (animationend) .drin —
   etikettRein ist dann DAUERHAFT aus der Liste, kein :active-/Klassen-Wechsel kann den
   Auftritt (und seine unsichtbare, untappbare 0%-Phase) je wieder abspielen. */
.etikett-btn.drin{animation:etikettWiegen var(--sway,5.5s) ease-in-out var(--swaydelay,3s) infinite}
.etikett-btn.drin.wiggle{animation:etikettWiegen var(--sway,5.5s) ease-in-out var(--swaydelay,3s) infinite, wiggle .4s ease}
@keyframes wiggle{
  0%,100%{transform:rotate(var(--rot,0deg))}
  25%{transform:rotate(calc(var(--rot,0deg) - 2.5deg))}
  50%{transform:rotate(calc(var(--rot,0deg) + 2.5deg))}
  75%{transform:rotate(calc(var(--rot,0deg) - 1.5deg))}
}
/* Rekord = Emailleschild (Alens Wahl): glänzendes Firmen-Emailschild wie an alten Lastern
   (SVG #emailDef). Hebt sich bewusst von der Papier-Welt ab. Der WOW-Moment ist der AUFTRITT:
   das Schild montiert sich beim Menü-Erscheinen ein, das Abendlicht flammt auf (Glow) und ein
   Specular-Glanz wischt übers Email — und das spielt bei JEDEM Menü-Erscheinen (Start UND
   Rückkehr). `--rs-delay` steuert das Timing (lang beim Erststart als Finale, kurz bei Rückkehr). */
.record-sign{
  /* Erststart: Montage läuft komplett hinter dem geschlossenen Tor; das Schild sitzt unten
     und wird vom Tor als Erstes freigegeben — fertig montiert, das Glühen läuft schon. */
  --rs-delay:.15s;
  position:relative;pointer-events:none;    /* Deko, startet groß → nie Taps schlucken */
  width:210px;height:90px;margin-top:46px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;
  border-radius:16px;overflow:hidden;       /* klippt den Glanz-Sweep aufs Schild */
  transform:rotate(var(--rot,-3deg));
  box-shadow:0 4px 0 rgba(0,0,0,.32);
  animation:
    signMount .6s cubic-bezier(.2,2,.4,1) var(--rs-delay) both,
    signIgnite 1.2s ease-out var(--rs-delay) both,
    signGlow 3.4s ease-in-out calc(var(--rs-delay) + 1.2s) infinite;
}
.rs-email{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:0}
.rs-label{position:relative;z-index:1;font-family:var(--font-display);font-size:13px;
  letter-spacing:.26em;text-transform:uppercase;color:var(--kreide);line-height:1;
  text-shadow:0 1px 0 rgba(120,50,10,.55);margin-left:.26em}
/* Luckiest Guy sitzt tief → −2px trimmt die Zahl auf die optische Mitte (im Browser einmessen) */
.rs-num{position:relative;z-index:1;font-family:var(--font-display);font-size:40px;font-weight:400;
  color:var(--kreide);line-height:1;font-variant-numeric:tabular-nums;transform:translateY(-2px);
  text-shadow:0 2px 0 rgba(120,50,10,.5), 0 0 10px rgba(0,0,0,.22)}
/* Specular-Glanz übers Email — einmal kräftig nach der Montage, danach gelegentlich */
.record-sign::after{
  content:"";position:absolute;top:-15%;left:0;height:130%;width:46px;z-index:2;pointer-events:none;
  background:linear-gradient(90deg, transparent, rgba(255,255,255,.5), transparent);
  transform:skewX(-20deg) translateX(-80px);
  animation:
    signFlash .6s ease-out calc(var(--rs-delay) + .5s) both,
    signSheen 6s ease-in-out calc(var(--rs-delay) + 4s) infinite;
}
/* Montage: das Schild schnappt mit leichtem Überschwingen in seine Neigung */
@keyframes signMount{
  0%{opacity:0;transform:rotate(calc(var(--rot,-3deg) + 11deg)) scale(.5) translateY(-26px)}
  55%{opacity:1;transform:rotate(calc(var(--rot,-3deg) - 2deg)) scale(1.06) translateY(3px)}
  75%{transform:rotate(calc(var(--rot,-3deg) + 1deg)) scale(.98)}
  100%{opacity:1;transform:rotate(var(--rot,-3deg)) scale(1) translateY(0)}
}
/* Zünden: das Abendlicht erwischt das Email → Aufflammen, dann Grundglühen. Läuft EINMAL und
   geht nahtlos in signGlow über (sequenziert → kein Strobe-Effekt).
   ⚠ Glühen als box-shadow, NICHT als filter:drop-shadow — iOS rendert den ANIMIERTEN
   drop-shadow auf diesem Element (overflow:hidden + border-radius + eigener Compositing-
   Layer) als ORANGES RECHTECK statt der Schild-Silhouette zu folgen (Alens „als ob nicht
   richtig ausgeschnitten"). box-shadow folgt dem border-radius überall; der crispe
   4px-Schlagschatten bleibt als erster Schatten in jedem Frame erhalten. */
@keyframes signIgnite{
  0%{box-shadow:0 4px 0 rgba(0,0,0,.32), 0 0 0 rgba(232,114,44,0)}
  28%{box-shadow:0 4px 0 rgba(0,0,0,.32), 0 0 30px rgba(232,114,44,.95)}
  100%{box-shadow:0 4px 0 rgba(0,0,0,.32), 0 0 14px rgba(232,114,44,.5)}
}
@keyframes signGlow{
  0%,100%{box-shadow:0 4px 0 rgba(0,0,0,.32), 0 0 12px rgba(232,114,44,.42)}
  50%{box-shadow:0 4px 0 rgba(0,0,0,.32), 0 0 22px rgba(232,114,44,.8)}
}
@keyframes signFlash{
  0%{transform:skewX(-20deg) translateX(-80px)}
  100%{transform:skewX(-20deg) translateX(250px)}
}
@keyframes signSheen{
  0%{transform:skewX(-20deg) translateX(-80px)}
  14%,100%{transform:skewX(-20deg) translateX(250px)}
}

/* Rolltor des Umzugstrucks: liegt über allem, rattert nach kurzem Moment hoch.
   (Ratter-Sound folgt in Phase D.) */
#rolltor{
  /* pointer-events:none ist PFLICHT: das Tor liegt mit z-index:10 über der ganzen Bühne und
     hat sonst ~2 s lang (0.8 s Delay + 1.1 s Fahrt) jeden Tap auf die Menü-Buttons geschluckt
     — Alens „erst beim 5. Mal passiert was". Rein dekoratives Element, nie interaktiv. */
  position:absolute;inset:0;z-index:10;pointer-events:none;
  /* KEINE Animation im Basiszustand: das Tor bleibt zu, bis main.js `.bereit` setzt
     (erst nach dem ersten gezeichneten Canvas-Frame) — es kann so nie eine leere oder
     halbfertige Szene freigeben, egal wie langsam das Gerät lädt. */
  display:flex;flex-direction:column;justify-content:center;align-items:center;
  background:
    repeating-linear-gradient(180deg,
      #55505e 0 26px, #47424f 26px 30px, #5d5866 30px 34px),
    var(--werkbank);
  border-bottom:6px solid #2c2933;
}
/* Tor-Start kommt aus main.js (`.bereit` nach dem ersten Canvas-Frame + kurzer Atempause) */
#rolltor.bereit{animation:rolltorAuf 1.1s .5s both}
/* Tap-Schild überm Tor (z 11 > 10): fängt Blind-Taps während des Intros — die Buttons
   dahinter sind längst tappbar. main.js entfernt es beim Tor-animationend endgültig. */
#torSchild{position:absolute;inset:0;z-index:11}
.rt-stamp{
  font-family:var(--font-display);font-size:56px;letter-spacing:.06em;line-height:1;
  color:var(--orange);text-transform:uppercase;
  border:5px solid var(--orange);border-radius:12px;padding:12px 22px 4px;
  transform:rotate(-6deg);opacity:.9;
  box-shadow:inset 0 0 0 2px rgba(232,114,44,.35);
  text-shadow:0 0 18px rgba(232,114,44,.3);
}
.rt-griff{width:110px;height:12px;border-radius:6px;background:#2c2933;
  position:absolute;bottom:16px;left:50%;transform:translateX(-50%);
  box-shadow:inset 0 2px 3px rgba(0,0,0,.6), 0 1px 0 rgba(255,255,255,.08)}
/* Rattern: ruckelig in Etappen hoch, wie ein echtes Rolltor */
@keyframes rolltorAuf{
  0%{transform:translateY(0)}
  18%{transform:translateY(-9%)}
  24%{transform:translateY(-7.5%)}
  44%{transform:translateY(-38%)}
  50%{transform:translateY(-36%)}
  72%{transform:translateY(-74%)}
  78%{transform:translateY(-72%)}
  100%{transform:translateY(-104%);visibility:hidden}
}
/* !important: muss auch gegen `#rolltor.bereit` (Normal- UND Reduced-Motion-Regel, letztere
   steht später im File) gewinnen — sonst spielt das Tor bei jeder Menü-Rückkehr erneut,
   weil #ovStart aus display:none kommt und CSS-Animationen dann neu starten. */
#rolltor.rt-skip{animation:none !important;visibility:hidden}
/* Menü-Rückkehr (z. B. vom Lieferschein): keine Intro-Animationen nochmal abspielen */
.overlay.menu.instant #menuTitle{animation:none;opacity:1}
/* Etiketten: kein erneutes Reinflattern, aber das ruhige Wiegen läuft weiter */
.overlay.menu.instant .etikett-btn{
  animation:etikettWiegen var(--sway,5.5s) ease-in-out infinite;opacity:1;
}
/* Menü-Rückkehr: der Auftritt spielt ERNEUT, nur schnell (Alens Kern-Wunsch — der Rekord soll
   auch beim Zurückkommen ins Menü groß auftauchen). Neustart passiert automatisch, weil #ovStart
   beim Zurückkommen von display:none kommt → CSS-Animationen starten neu. */
.overlay.menu.instant .record-sign{--rs-delay:.2s}

/* ===== Abgang beim Start (SPIELEN gedrückt), „Abfahrt!": der Truck ruckt an und alle
   Menü-Papiere werden vom FAHRTWIND erfasst — kurzer Zupfer nach rechts (der Windstoß),
   dann wehen sie flatternd nach links aus dem Bild, während die Szene rechts rausfährt
   (main.js, state 'launch'). Nach .instant platziert, damit die Abgangs-Animation auch
   bei Menü-Rückkehr gewinnt. */
.overlay.menu.leaving .etikett-btn{
  animation:fahrtwind .8s cubic-bezier(.55,0,.8,.4) var(--ldelay,0s) forwards;
  pointer-events:none;
}
.overlay.menu.leaving .etikett-btn:nth-child(1){--ldelay:.05s}
.overlay.menu.leaving .etikett-btn:nth-child(2){--ldelay:.13s}
.overlay.menu.leaving .etikett-btn:nth-child(3){--ldelay:0s}
@keyframes fahrtwind{
  18%{transform:rotate(calc(var(--rot,0deg) + 5deg)) translate(16px, -4px)}
  55%{transform:rotate(calc(var(--rot,0deg) - 14deg)) translate(-40vw, -26px);opacity:1}
  100%{transform:rotate(calc(var(--rot,0deg) - 32deg)) translate(-135vw, -12px);opacity:1}
}
.overlay.menu.leaving #menuTitle{animation:fahrtwindGross .7s cubic-bezier(.55,0,.8,.4) .09s forwards}
.overlay.menu.leaving .record-sign{animation:fahrtwindGross .7s cubic-bezier(.55,0,.8,.4) .16s forwards}
@keyframes fahrtwindGross{
  20%{transform:translate(12px, -3px) rotate(2deg)}
  100%{transform:translate(-130vw, -8vh) rotate(-20deg);opacity:.9}
}
/* Der Klemmbrett-Tab-Abgang (tabWind) steht unten im #lbTab-Block: seine .instant-Regel
   kommt erst dort — die .leaving-Regel muss in der Reihenfolge NACH ihr stehen. */

@media (prefers-reduced-motion: reduce){
  #rolltor.bereit{animation:rolltorFade .6s .3s both}
  #menuTitle{animation:none;opacity:.94}
  .etikett-btn{animation:none;opacity:1}
  .etikett-btn.wiggle{animation:none}
  .etikett-btn:nth-child(1) .et-wort{animation:none}
  .record-sign{animation:none;opacity:1}   /* Schlagschatten kommt aus der Basis-box-shadow */
  .record-sign::after{animation:none;opacity:0}
  .katalog{animation:none}
  .gekauft-stamp{animation:none;transform:translate(-50%,-50%) rotate(-8deg);opacity:.95}
  .shop-card.wiggle{animation:none}
  .rangliste{animation:none}
  .lb-row.climbing{animation:none}
  .lb-climb-badge{animation:none}
  .namensschild{animation:none}
  #lbTab{animation:none}
  .overlay.menu.leaving .etikett-btn,
  .overlay.menu.leaving #menuTitle,
  .overlay.menu.leaving #lbTab,
  .overlay.menu.leaving .record-sign{animation:none;opacity:0}
}
@keyframes rolltorFade{from{opacity:1}to{opacity:0;visibility:hidden}}

/* Game-Over „Lieferschein" — heller Zettel auf dunklem Scrim für maximalen Kontrast.
   Eigenes, knapperes Padding: Schein + 4 Aktions-Zettel müssen auf 375×667 komplett passen. */
#ovOver{padding:12px;gap:20px}
#ovOver .ov-actions{margin-top:14px}
.receipt{
  position:relative;width:min(92%,320px);background:var(--kreide);color:var(--abend-top);
  border-radius:6px;padding:24px 22px 20px;transform:rotate(-1.6deg);
  box-shadow:0 18px 44px rgba(0,0,0,.55), 0 2px 0 rgba(0,0,0,.25);
  background-image:repeating-linear-gradient(0deg, rgba(43,36,48,.04) 0 1px, transparent 1px 22px);
}
/* perforierte Ober-/Unterkante wie an einer Quittung */
.receipt::before,.receipt::after{content:"";position:absolute;left:0;right:0;height:8px;
  background:radial-gradient(circle at 6px 8px, transparent 0 5px, var(--kreide) 5px) repeat-x;background-size:12px 8px}
.receipt::before{top:-7px;transform:scaleY(-1)}
.receipt::after{bottom:-7px}
.tape{position:absolute;width:64px;height:22px;top:-9px;background:rgba(156,199,222,.38);border:1px solid rgba(255,255,255,.35)}
.tape-l{left:-12px;transform:rotate(-24deg)}
.tape-r{right:-12px;transform:rotate(20deg)}
.r-brand{font-family:var(--font-marker);font-size:13px;color:#6f5334;text-align:center;margin-bottom:10px}
.stamp{
  font-family:var(--font-display);font-size:32px;text-align:center;color:var(--orange);
  border:3px solid var(--orange);border-radius:8px;padding:3px 14px;margin:14px auto 22px;width:max-content;
  transform:rotate(-7deg);opacity:.9;letter-spacing:.03em;text-transform:uppercase;
  box-shadow:inset 0 0 0 1px rgba(232,114,44,.35)
}
.stamp{margin:12px auto 18px}
.stamp.is-record{color:#3f9b5c;border-color:#3f9b5c;box-shadow:inset 0 0 0 1px rgba(63,155,92,.35)}
.r-score{display:flex;flex-direction:column;align-items:center;gap:4px;margin-bottom:18px}
.r-score span{font-size:11px;text-transform:uppercase;letter-spacing:.14em;color:#8a7d70}
.r-score b{font-family:var(--font-display);font-size:46px;color:var(--abend-top);line-height:1;font-variant-numeric:tabular-nums}
.r-lines{display:flex;flex-direction:column;gap:9px}
.r-lines>div{display:flex;justify-content:space-between;align-items:baseline;font-size:14px;
  border-bottom:1px dashed rgba(43,36,48,.25);padding-bottom:7px}
.r-lines span{color:#6f5334}
.r-lines b{font-weight:800;font-variant-numeric:tabular-nums;color:var(--abend-top)}
/* kompakt genug, dass Lieferschein + 4 Aktionen auf 375×667 komplett passen */
.ov-actions{display:flex;flex-direction:column;gap:11px;width:min(92%,320px);margin-top:18px}

/* ===== Shop „Der Umzugskatalog" =====
   Papierner Katalog über der weiterlaufenden Demo-Szene (Overlay ohne satten Scrim,
   nur leicht abgedunkelt — man sieht den gekauften Himmel sofort dahinter wirken). */
.overlay.shop{background:rgba(20,16,26,.5);backdrop-filter:blur(2px);padding:14px}
.katalog{
  position:relative;width:min(96%,372px);max-height:88vh;
  display:flex;flex-direction:column;
  background:var(--kreide);color:var(--abend-top);
  border-radius:8px;padding:20px 16px 16px;transform:rotate(-1deg);
  box-shadow:0 18px 44px rgba(0,0,0,.55);
  background-image:repeating-linear-gradient(0deg, rgba(43,36,48,.035) 0 1px, transparent 1px 26px);
  animation:katalogAuf .45s cubic-bezier(.2,1.5,.4,1) both;
}
.katalog::before,.katalog::after{content:"";position:absolute;left:0;right:0;height:8px;
  background:radial-gradient(circle at 6px 8px, transparent 0 5px, var(--kreide) 5px) repeat-x;background-size:12px 8px}
.katalog::before{top:-7px;transform:scaleY(-1)}
.katalog::after{bottom:-7px}
@keyframes katalogAuf{
  0%{transform:translateY(46vh) rotate(5deg) scale(.85);opacity:0}
  100%{transform:translateY(0) rotate(-1deg) scale(1);opacity:1}
}
/* Kopf: Marker-Titel mit Tusche-Unterstrich + Coin-Token */
.kat-kopf{display:flex;justify-content:space-between;align-items:flex-end;margin-bottom:14px;
  padding-bottom:8px;border-bottom:2.5px solid #d8c7a4}
.kat-titel{font-family:var(--font-display);font-size:20px;letter-spacing:.02em;color:var(--orange);
  text-transform:uppercase;line-height:1;transform:rotate(-1.5deg)}
.kat-coins{display:flex;align-items:center;gap:6px}
.kat-coins svg{width:22px;height:22px;filter:drop-shadow(0 1px 1px rgba(0,0,0,.25))}
.kat-coins b{font-size:19px;font-weight:800;font-variant-numeric:tabular-nums;color:#a24e18}

/* Register-Reiter (frei vom Aktions-Button-Look) */
.kat-tabs{display:flex;gap:7px;margin-bottom:16px}
.kat-tab{
  flex:1;min-width:0;padding:11px 4px 9px;cursor:pointer;
  font-family:var(--font-display);font-size:15px;letter-spacing:.02em;color:#8a7256;
  /* Papierfaser + Registerblatt-Form (oben rund, unten fast eckig) */
  background:
    repeating-linear-gradient(0deg, rgba(120,90,50,.055) 0 1px, transparent 1px 6px),
    #e7ddc8;
  border:2px solid #d0bd98;border-radius:9px 9px 4px 4px;
  box-shadow:0 2px 0 rgba(120,90,50,.22);
  transition:transform .08s, background .12s, color .12s, box-shadow .12s;
}
/* aktives Register steht sichtbar vorn */
.kat-tab.active{
  background:
    repeating-linear-gradient(0deg, rgba(255,255,255,.08) 0 1px, transparent 1px 6px),
    linear-gradient(180deg,#f6a869,var(--orange));
  color:#25160a;border-color:#b8551c;
  box-shadow:0 4px 0 #a6491a;transform:translateY(-2px);
}
.kat-tab:active{transform:translateY(1px);box-shadow:0 1px 0 rgba(120,90,50,.22)}

/* Musterkarten — die Liste ist BEWUSST auf dreieinhalb Karten gedeckelt (Alens Wunsch:
   „3-4 Items auf einmal, dann scrollen"). Die angeschnittene vierte Karte ist die
   Scroll-Einladung: man SIEHT, dass unten weitergeht, ohne Pfeil oder Hinweistext.
   Rechnung: Karte 90px + 11px Abstand = 101px Raster; 3×101 + 45 = 348px.
   ⚠ touch-action:pan-y ist PFLICHT — body traegt touch-action:none (noetig, damit das
   Canvas-Ziehen die Seite nicht scrollt), und das unterbindet auf iOS auch das
   Finger-Scrollen in dieser Liste. Ohne diese Zeile laesst sich der Katalog am iPhone
   NUR mit der Maus scrollen (gleiche Falle wie die dokumentierten iOS-Tap-Killer).
   overscroll-behavior:contain verhindert, dass das Weiterscrollen am Listenende die
   Seite dahinter mitzieht. */
#shopList{
  display:flex;flex-direction:column;gap:11px;overflow-y:auto;overflow-x:hidden;
  max-height:min(348px, 52vh);padding:2px 4px 2px 2px;margin-right:-4px;
  touch-action:pan-y;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;
  scrollbar-width:thin;scrollbar-color:rgba(111,83,52,.45) transparent;
  /* zarter Auslauf an der Unterkante: die angeschnittene Karte verliert die harte Kappung */
  -webkit-mask-image:linear-gradient(180deg,#000 calc(100% - 26px),transparent 100%);
  mask-image:linear-gradient(180deg,#000 calc(100% - 26px),transparent 100%);
}
/* Ist alles sichtbar (kurze Liste), waere der Auslauf ein Fehler — dann volle Deckkraft.
   :has() faellt auf Safari 15.4+ / iOS 15.4+; aeltere Geraete sehen nur den Auslauf. */
#shopList:not(:has(.shop-card:nth-child(4))){-webkit-mask-image:none;mask-image:none}
#shopList::-webkit-scrollbar{width:6px}
#shopList::-webkit-scrollbar-thumb{background:rgba(111,83,52,.4);border-radius:3px}
.shop-card{
  position:relative;display:flex;align-items:center;gap:11px;text-align:left;width:100%;
  padding:11px 12px;cursor:pointer;font:inherit;color:var(--abend-top);
  background:
    repeating-linear-gradient(0deg, rgba(120,90,50,.045) 0 1px, transparent 1px 6px),
    linear-gradient(180deg,#fbf5e9,#efe4cf);
  border:2px solid #d8c7a4;border-radius:12px;
  box-shadow:0 3px 0 rgba(120,90,50,.28);          /* crisp, kein Blur */
  transition:transform .08s, box-shadow .08s, border-color .12s;
}
.shop-card:active{transform:translateY(2px);box-shadow:0 1px 0 rgba(120,90,50,.28)}
.shop-card.selected{
  border-color:var(--orange);
  background:linear-gradient(180deg,#fdeede,#f7dcbf);
  box-shadow:0 0 0 2px rgba(232,114,44,.35), 0 3px 0 rgba(120,90,50,.28);
}
/* Schaufenster: gerahmtes Fenster, clippt die Vorschau — nichts kann rausschauen */
.card-prev{
  width:62px;height:56px;flex:0 0 62px;border-radius:8px;overflow:hidden;
  background:#2B2430;border:2px solid #cdb892;
  box-shadow:inset 0 2px 6px rgba(0,0,0,.35);
}
.card-prev svg{display:block;width:100%;height:100%}
/* Zweizeiliges Layout: Name oben über die volle Restbreite, darunter Beschreibung + Tag.
   Dadurch stehen Name und Preis/Status nie in derselben Zeile → keine Kollision, kein Überlauf. */
.card-main{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:5px}
.card-name{font-family:var(--font-display);font-size:16px;letter-spacing:.01em;line-height:1;
  color:#3a2a17;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.card-row2{display:flex;align-items:center;justify-content:space-between;gap:8px}
.card-sub{flex:1 1 auto;min-width:0;font-family:var(--font-marker);font-size:11px;color:#8a7256;
  line-height:1.1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.card-price{
  flex:0 0 auto;font-family:var(--font-display);font-size:14px;color:#25160a;white-space:nowrap;
  background:linear-gradient(180deg,#f6a869,var(--orange));padding:4px 9px 2px;border-radius:4px;
  transform:rotate(2deg);box-shadow:0 2px 0 #a6491a;
}
.card-price.zu-teuer{filter:grayscale(.7);opacity:.6}
.card-pill{flex:0 0 auto;font-family:var(--font-marker);font-size:11px;white-space:nowrap;padding:4px 10px;border-radius:20px}
.card-pill.aktiv{color:#eafff0;background:#3f9b5c;box-shadow:0 2px 0 #2c6e40}
.card-pill.anlegen{color:#6f5334;background:#e7ddc8;border:1.5px solid #cdb892}
.shop-card.wiggle{animation:wiggle .4s ease}

/* Kauf-Stempel knallt auf die Karte (scharf umrandet, ohne Weichzeichner).
   ⚠ Er bekommt PLATZ, statt Text zu überdecken (Alens Befund „der Stempel überlappt die
   Schrift"): gemessen deckte er 68 % des Namens und 47 % der Beschreibung zu — bei 281 px
   Kartenbreite und 118 px Stempelbreite passt daneben schlicht kein Text mehr. Deshalb
   treten Name/Beschreibung/Preis für den Moment des Stempelns zurück (nur die Deckkraft,
   das Layout bleibt stehen), und der Abdruck landet mittig im frei gewordenen Feld. Das
   Schaufenster bleibt sichtbar — man sieht also weiter, was man gerade gekauft hat. */
/* ⚠ BEWUSST OHNE transition: eine Blende steht im Hintergrund-Tab und im iOS-Stromsparmodus
   bei ihrem Startbild — der Text bliebe dann sichtbar und der Stempel überlappte wieder,
   also genau der Fehler, der hier behoben werden soll (dieselbe Familie wie das Paketband
   am Ton-Schalter). Der Wechsel ist hart und damit immer richtig; er fällt nicht auf, weil
   im selben Moment der Stempel einschlägt und den Blick ohnehin auf sich zieht. */
.shop-card.bought .card-main{opacity:0}
.gekauft-stamp{
  position:absolute;top:50%;pointer-events:none;
  /* Mitte des Textfelds statt Kartenmitte: das Schaufenster belegt links 62 px + 11 px
     Abstand, also liegt die freie Mitte um die Hälfte davon (36 px) rechts versetzt. */
  left:calc(50% + 36px);
  font-family:var(--font-display);font-size:21px;color:#3f9b5c;text-transform:uppercase;
  background:rgba(242,237,228,.9);
  border:3px solid #3f9b5c;border-radius:8px;padding:5px 12px 2px;
  box-shadow:inset 0 0 0 1px rgba(63,155,92,.4);
  animation:gekauftSlam .45s cubic-bezier(.18,2.6,.36,1) both;
}
/* ⚠ translate(-50%,-50%) in JEDEM Keyframe — der Stempel ist absolut positioniert und
   zentriert sich über beide Achsen selbst (Präzedenz: einsatzSlam in der Duell-Lobby). */
@keyframes gekauftSlam{
  0%{transform:translate(-50%,-50%) scale(2.6) rotate(14deg);opacity:0}
  55%{transform:translate(-50%,-50%) scale(.9) rotate(-7deg);opacity:1}
  100%{transform:translate(-50%,-50%) scale(1) rotate(-8deg);opacity:.97}
}
/* (Der Sonderfracht-Reiter samt Kauf-Show ist raus — Alens Entscheidung: die Kisten tauchen
   jetzt gratis und zufällig im Spiel auf. Die Effekt-Optik lebt komplett im Canvas.) */

/* ===== Aktions-Buttons = gerissene Zettel mit aufgestempeltem Wort =====
   Alens Befund: der orange Plastik-Tag brach krass mit dem Papier-Homescreen. Jetzt dieselbe
   Papier-Welt wie Etikett / Lieferschein / Katalog / Rangliste — nur kleiner.
   Silhouette per clip-path (KEINE SVG-Kinder: gameover.js überschreibt btnContinue.innerHTML!),
   der Stempel liegt als ::after mit z-index:-1 hinter dem Text (der Button bildet über `filter`
   einen Stacking-Context). Gilt für alle .ov-actions-Buttons inkl. beider „Zurück". */
.ov-actions button{
  position:relative;cursor:pointer;
  font-family:var(--font-display);font-size:19px;letter-spacing:.03em;
  /* feste line-height = vorhersagbare Textbox (auch bevor der Webfont geladen ist);
     Padding minimal asymmetrisch, weil Luckiest Guy tief in seiner Box sitzt */
  line-height:1.25;
  /* 18/16 statt 17/17: im Browser eingemessen, damit die Schrift optisch mittig im Stempel
     sitzt (sonst klebt sie oben an der Tintenlinie — Alens Befund auf dem iPhone) */
  width:100%;padding:18px 24px 16px 44px;border:none;border-radius:0;
  color:var(--orange);
  /* Kreide-Papier mit Faser */
  background:
    repeating-linear-gradient(0deg, rgba(120,90,50,.05) 0 1px, transparent 1px 6px),
    linear-gradient(180deg,#faf5ea,#efe7d7);
  clip-path:var(--tear);
  filter:drop-shadow(2px 5px 0 rgba(0,0,0,.4));
  transform:rotate(var(--trot,0deg));            /* von Hand gelegt, nicht gerastert */
  transition:transform .07s, translate .07s, filter .07s, color .1s;
  animation:zettelRein .38s cubic-bezier(.2,1.4,.4,1) var(--adelay,0s) both;
}
/* drei Riss-Varianten im Wechsel + eigene Neigung/Verzögerung → keine Klone */
.ov-actions button:nth-child(3n+1){
  --tear:polygon(0% 0%, 97% 0%, 100% 9%, 97% 18%, 100% 27%, 97% 36%, 100% 45%, 97% 55%, 100% 64%, 97% 73%, 100% 82%, 97% 91%, 100% 100%, 0% 100%);
  --trot:-.5deg;
}
.ov-actions button:nth-child(3n+2){
  --tear:polygon(0% 0%, 100% 0%, 97.3% 8%, 100% 16%, 97.3% 25%, 100% 33%, 97.3% 42%, 100% 50%, 97.3% 58%, 100% 67%, 97.3% 75%, 100% 84%, 97.3% 92%, 100% 100%, 0% 100%);
  --trot:.45deg;
}
.ov-actions button:nth-child(3n){
  --tear:polygon(0% 0%, 96.6% 0%, 100% 11%, 96.6% 22%, 100% 33%, 96.6% 44%, 100% 56%, 96.6% 67%, 100% 78%, 96.6% 89%, 100% 100%, 0% 100%);
  --trot:-.35deg;
}
.ov-actions > button:nth-child(1){--adelay:0s}
.ov-actions > button:nth-child(2){--adelay:.06s}

/* ===== Navigation paarweise in einer Zeile (Rangliste | Zum Menü) =====
   Spart eine ganze Zeile — dadurch bleiben alle Buttons hoch und der Lieferschein passt
   trotzdem komplett auf 375×667. Kleinerer Schriftgrad + engeres Padding, damit die Wörter
   in die halbe Breite passen. Höhere Spezifität als .ov-actions button → gewinnt sicher. */
.ov-row{display:flex;gap:11px}
.ov-actions .ov-row button{
  flex:1 1 0;min-width:0;
  font-size:16px;padding-left:32px;padding-right:14px;
}
.ov-actions .ov-row button::before{left:11px;width:10px;height:10px}
.ov-actions .ov-row button::after{left:23px;right:10px}
.ov-actions .ov-row button:nth-child(1){
  --tear:polygon(0% 0%, 96.6% 0%, 100% 11%, 96.6% 22%, 100% 33%, 96.6% 44%, 100% 56%, 96.6% 67%, 100% 78%, 96.6% 89%, 100% 100%, 0% 100%);
  --trot:-.4deg;--adelay:.12s;
}
.ov-actions .ov-row button:nth-child(2){
  --tear:polygon(0% 0%, 97% 0%, 100% 9%, 97% 18%, 100% 27%, 97% 36%, 100% 45%, 97% 55%, 100% 64%, 97% 73%, 100% 82%, 97% 91%, 100% 100%, 0% 100%);
  --trot:.5deg;--adelay:.18s;
}
/* die Zettel landen gestaffelt, sobald das Overlay aufgeht (display:none → sichtbar
   startet CSS-Animationen neu, also spielt es bei jedem Öffnen) */
@keyframes zettelRein{
  0%{opacity:0;transform:rotate(calc(var(--trot,0deg) - 4deg)) translateY(-14px)}
  60%{opacity:1;transform:rotate(calc(var(--trot,0deg) + 1.5deg)) translateY(3px)}
  100%{opacity:1;transform:rotate(var(--trot,0deg)) translateY(0)}
}
/* der aufgestempelte Bereich */
/* Stempel-Inset knapper (6px statt 7px) → mehr Luft zwischen Tintenlinie und Schrift */
.ov-actions button::after{
  content:"";position:absolute;z-index:-1;
  left:30px;right:13px;top:6px;bottom:6px;border-radius:5px;
}
/* Hauptaktion: voller Signal-Orange-Balken, Schrift in Kreide */
.ov-actions button:not(.secondary){
  color:#fdf3e6;
  animation:
    zettelRein .38s cubic-bezier(.2,1.4,.4,1) var(--adelay,0s) both,
    tagAtmen 2.9s ease-in-out 1s infinite;
}
.ov-actions button:not(.secondary)::after{
  background:linear-gradient(180deg,#f6a869,var(--orange));
  box-shadow:inset 0 0 0 1px rgba(166,73,26,.55);
}
/* Nebenaktion: nur Stempel-Kontur — mit Tintenlücken, also gestempelt statt gedruckt */
.ov-actions button.secondary::after{
  box-shadow:inset 0 0 0 3px var(--orange);
  background-image:linear-gradient(#faf5ea,#faf5ea),linear-gradient(#faf5ea,#faf5ea);
  background-size:15px 5px, 5px 13px;
  background-position:34% -1px, calc(100% + 1px) 42%;
  background-repeat:no-repeat;
}
/* nur die Hauptaktion atmet — sonst flimmert der ganze Lieferschein */
@keyframes tagAtmen{
  0%,100%{filter:drop-shadow(2px 5px 0 rgba(0,0,0,.4))}
  50%{filter:drop-shadow(2px 5px 0 rgba(0,0,0,.4)) brightness(1.06)}
}
/* gestanztes Loch mit Metall-Öse */
.ov-actions button::before{
  content:"";position:absolute;left:15px;top:50%;transform:translateY(-50%);z-index:1;
  width:12px;height:12px;border-radius:50%;background:rgba(43,36,48,.6);
  box-shadow:
    inset 0 1.5px 2px rgba(0,0,0,.5),
    0 0 0 2.5px rgba(255,255,255,.5),
    0 0 0 3.6px rgba(150,120,80,.45);
}
/* ⚠ kein animation:none und kein transform-Override im :active (iOS-Neustart-Falle wie bei
   .etikett-btn — zettelRein würde beim Loslassen neu spielen und den Tap fressen):
   Press über die separate translate-Property, die Rotation hält zettelRein (fill both). */
.ov-actions button:active{
  translate:0 4px;
  filter:drop-shadow(1px 1px 0 rgba(0,0,0,.4));
}
.ov-actions button:not(.secondary):active{color:#f7e6d5}
.ov-actions button.secondary:active{color:#c25a17}   /* Tinte dunkelt beim Aufdrücken nach */
.ov-actions button:disabled{
  filter:none;opacity:.45;animation:none;transform:rotate(var(--trot,0deg));
}
.ov-actions button:disabled::after{filter:grayscale(.75)}

@media (prefers-reduced-motion: reduce){
  .ov-actions button{transition:none;animation:none}
}

/* ===== Bestenliste „Umzugshelfer-Rangliste" =====
   Papierne Rangliste über der weiterlaufenden Demo (leichter Scrim wie der Shop), damit die
   Szene dahinter sichtbar bleibt. Papier/Perforation/Tape = Sprache von Lieferschein & Katalog. */
/* z-index:20 → liegt IMMER oben, auch über dem Lieferschein (#ovOver kommt später im DOM);
   so ist die Rangliste beim Öffnen aus dem Game-Over wirklich sichtbar. */
.overlay.lb{background:rgba(20,16,26,.55);backdrop-filter:blur(2px);padding:14px;z-index:20}
.rangliste{
  position:relative;width:min(96%,372px);max-height:84vh;display:flex;flex-direction:column;
  background:var(--kreide);color:var(--abend-top);border-radius:8px;padding:20px 16px 16px;
  transform:rotate(-1deg);box-shadow:0 18px 44px rgba(0,0,0,.55);
  background-image:repeating-linear-gradient(0deg, rgba(43,36,48,.035) 0 1px, transparent 1px 26px);
  /* Öffnet mit DERSELBEN Animation wie der Shop-Katalog (Alens Wunsch: konsistent). */
  animation:katalogAuf .45s cubic-bezier(.2,1.5,.4,1) both;
}
.rangliste::before,.rangliste::after{content:"";position:absolute;left:0;right:0;height:8px;
  background:radial-gradient(circle at 6px 8px, transparent 0 5px, var(--kreide) 5px) repeat-x;background-size:12px 8px}
.rangliste::before{top:-7px;transform:scaleY(-1)}
.rangliste::after{bottom:-7px}
.lb-kopf{display:flex;justify-content:space-between;align-items:flex-end;gap:10px;margin-bottom:10px;
  padding-bottom:8px;border-bottom:2.5px solid #d8c7a4}
/* Die Liste ist seit August 2026 online. Zwei ruhige Zustandszeilen dafür — kein Alarm,
   sondern eine handschriftliche Notiz auf dem Klemmbrett:
   .lb-offline = kein Netz, der angezeigte Stand kann alt sein
   .lb-leer    = es steht noch niemand außer dir drin (Einladung statt Fehlermeldung) */
.lb-offline{font-family:var(--font-marker);font-size:11.5px;color:#a08a6a;text-align:center;
  margin:-4px 0 8px}
.lb-leer{font-family:var(--font-marker);font-size:12.5px;color:#8a7256;text-align:center;
  line-height:1.5;padding:14px 10px 6px}
.lb-titel{font-family:var(--font-display);font-size:18px;letter-spacing:.02em;color:var(--orange);
  text-transform:uppercase;line-height:1.05;transform:rotate(-1.5deg);max-width:58%}
.lb-you-tag{display:flex;flex-direction:column;align-items:flex-end;gap:2px;text-align:right;flex:0 0 auto}
.lb-you-rank b{font-family:var(--font-display);font-size:24px;color:var(--abend-top);line-height:1}
.lb-you-rank i{font-style:normal;font-size:11px;color:#8a7d70;font-variant-numeric:tabular-nums}

#lbList{display:flex;flex-direction:column;gap:8px;overflow-y:auto;overflow-x:hidden;
  max-height:56vh;padding:2px 4px 6px 2px;margin-right:-4px;
  scrollbar-width:thin;scrollbar-color:rgba(111,83,52,.45) transparent}
#lbList::-webkit-scrollbar{width:6px}
#lbList::-webkit-scrollbar-thumb{background:rgba(111,83,52,.4);border-radius:3px}
.lb-row{position:relative;display:flex;align-items:center;gap:10px;padding:9px 11px;
  background:linear-gradient(180deg,#fbf5e9,#efe4cf);border:2px solid #d8c7a4;border-radius:11px;
  box-shadow:0 2px 0 rgba(120,90,50,.22)}
.lb-row.is-you{border-color:var(--orange);background:linear-gradient(180deg,#fdeede,#f7dcbf);
  box-shadow:0 0 0 2px rgba(232,114,44,.35), 0 2px 0 rgba(120,90,50,.28)}
.lb-rank{flex:0 0 30px;width:30px;height:30px;display:flex;align-items:center;justify-content:center}
.lb-num{font-family:var(--font-display);font-size:17px;color:#8a7256}
.lb-row.is-you .lb-num{color:var(--orange)}
.lb-medal-svg{width:30px;height:30px;display:block}
.lb-main{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.lb-name{font-family:var(--font-display);font-size:15px;letter-spacing:.01em;line-height:1;color:#3a2a17;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.lb-name b{font-family:var(--font-ui);font-size:10px;font-weight:800;letter-spacing:.08em;color:#fff;
  background:var(--orange);padding:2px 5px;border-radius:4px;margin-left:5px}
.lb-score{flex:0 0 auto;text-align:right;font-family:var(--font-display);font-size:16px;color:var(--abend-top);
  font-variant-numeric:tabular-nums;display:flex;flex-direction:column;line-height:1;gap:2px}
.lb-score small{font-family:var(--font-ui);font-size:10px;font-weight:700;color:#8a7d70}
.lb-next{position:absolute;left:46px;bottom:-8px;z-index:2;
  font-family:var(--font-marker);font-size:10px;color:#eafff0;background:#3f9b5c;
  padding:2px 8px;border-radius:10px;box-shadow:0 2px 4px rgba(0,0,0,.28);white-space:nowrap}
/* ⚠ Kein ::after mit Text mehr — der ganze Satz (inkl. Pfeil) kommt aus t('lb.noch'),
   sonst bräuchte auch das CSS eine Sprachweiche. */

/* Aufstiegs-Enthüllung: die eigene Zeile klettert hoch, „Aufgestiegen!"-Stempel knallt drauf */
.lb-row.climbing{animation:lbClimb .55s cubic-bezier(.2,1.4,.4,1) both;z-index:3}
@keyframes lbClimb{
  0%{transform:translateY(28px)}
  100%{transform:translateY(0)}
}
.lb-climb-badge{position:absolute;right:8px;top:50%;pointer-events:none;text-align:center;
  font-family:var(--font-display);font-size:14px;color:#3f9b5c;text-transform:uppercase;line-height:1;
  background:rgba(242,237,228,.95);border:3px solid #3f9b5c;border-radius:8px;padding:5px 10px 3px;
  box-shadow:inset 0 0 0 1px rgba(63,155,92,.4);
  transform:translateY(-50%);
  animation:climbSlam .6s cubic-bezier(.18,2.6,.36,1) .3s both}
.lb-climb-badge b{display:block;font-size:11px;color:#25160a;font-weight:400;margin-top:1px}
/* eigener Slam für den Aufstiegs-Stempel (früher das jetzt entfernte recordSlam) */
@keyframes climbSlam{
  0%{transform:translateY(-50%) scale(2.6) rotate(12deg);opacity:0}
  55%{transform:translateY(-50%) scale(.9) rotate(-4deg);opacity:1}
  100%{transform:translateY(-50%) scale(1) rotate(-3deg);opacity:1}
}

/* ===== Duell „Funkraum" ===== */
.funkzettel{
  position:relative;width:min(92%,340px);background:var(--kreide);color:var(--abend-top);
  border-radius:8px;padding:20px 18px 18px;transform:rotate(-1deg);
  box-shadow:0 18px 44px rgba(0,0,0,.55);
  background-image:repeating-linear-gradient(0deg, rgba(43,36,48,.035) 0 1px, transparent 1px 26px);
  animation:katalogAuf .45s cubic-bezier(.2,1.5,.4,1) both;
}
.funkzettel::before,.funkzettel::after{content:"";position:absolute;left:0;right:0;height:8px;
  background:radial-gradient(circle at 6px 8px, transparent 0 5px, var(--kreide) 5px) repeat-x;background-size:12px 8px}
.funkzettel::before{top:-7px;transform:scaleY(-1)}
.funkzettel::after{bottom:-7px}
.fz-kopf{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;
  padding-bottom:8px;border-bottom:2.5px solid #d8c7a4}
.fz-titel{font-family:var(--font-display);font-size:20px;color:var(--orange);text-transform:uppercase;
  transform:rotate(-1.5deg)}
/* Funk-Lampe blinkt im Kopf — der Kanal lebt */
.fz-lampe{width:11px;height:11px;border-radius:50%;background:#3f9b5c;box-shadow:0 0 8px #3f9b5c;
  animation:funkBlink 1.6s ease-in-out infinite}
@keyframes funkBlink{0%,100%{opacity:.35}50%{opacity:1}}
.fz-text{font-size:13px;line-height:1.5;color:#6f5334;margin:0 0 8px;text-align:center}
.fz-label{font-family:var(--font-marker);font-size:13px;color:#8a7256;margin:4px 0 9px;text-align:center;
  display:flex;align-items:center;justify-content:center;gap:6px}
.fz-coin-ico{width:19px;height:19px;flex:0 0 auto;filter:drop-shadow(0 1px 1px rgba(0,0,0,.25))}
/* Der ERSTE Duell-Screen (Kanal öffnen / beitreten) ist NUR IN DER HÖHE kompakt — die Breite
   bleibt wie bei allen anderen Abschnitten (Alens Vorgabe). Die beiden Buttons stehen dabei
   mittig statt über die volle Breite. duellview setzt `.kompakt` nur für diesen Abschnitt. */
/* Der Button-Block sitzt bewusst HOCH: wenig Luft oben, viel unten (Alens Wunsch) */
.funkzettel.kompakt{width:min(90%,298px);padding:10px 16px 34px}
.funkzettel.kompakt .fz-kopf{margin-bottom:6px;padding-bottom:6px}
/* einzeilig halten: der Satz ist der einzige nennenswerte Höhen-Posten zwischen Strich und Buttons */
.funkzettel.kompakt .fz-text{margin:0 0 6px;font-size:12.5px;white-space:nowrap}
.funkzettel.kompakt .fz-btn{width:min(76%,210px);margin:9px auto 0}
.fz-btn{
  display:block;width:100%;margin:9px 0 0;cursor:pointer;
  font-family:var(--font-display);font-size:18px;letter-spacing:.03em;color:#fdf3e6;text-transform:uppercase;
  background:linear-gradient(180deg,#f6a869,var(--orange));border:none;border-radius:9px;padding:13px 10px 11px;
  box-shadow:0 4px 0 #a6491a;transition:transform .07s, box-shadow .07s;touch-action:manipulation}
.fz-btn:active{transform:translateY(3px);box-shadow:0 1px 0 #a6491a}
.fz-btn.sekundaer{background:linear-gradient(180deg,#dcb488,var(--karton));color:#3a2a17;box-shadow:0 4px 0 #8a6a44}
.fz-btn:disabled{filter:grayscale(.6);opacity:.5}
/* Einsatz-Münzen: echte geprägte Münzen (fester Durchmesser statt Grid-Zellen — die alten
   aspect-ratio-Zellen quetschten und der Kreis saß nicht mittig um die Zahl). */
.fz-chips{display:flex;flex-wrap:wrap;justify-content:center;gap:10px;margin-bottom:12px}
.fz-chip{
  position:relative;flex:0 0 auto;width:64px;height:64px;padding:0;cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  font-family:var(--font-display);font-size:19px;line-height:1;color:#4a2a0c;
  /* Riffelrand (geprägte Kerben) + Gold-Körper */
  background:
    repeating-conic-gradient(from 0deg, #c9752e 0deg 6deg, #a24e18 6deg 12deg),
    radial-gradient(circle at 50% 34%, #ffd79a, #f6b26a 52%, #dd8c3c 78%);
  background-clip:padding-box, padding-box;
  border:4px solid transparent;border-radius:50%;
  box-shadow:
    inset 0 0 0 3px rgba(255,232,190,.75),      /* Prägering innen */
    inset 0 3px 6px rgba(255,255,255,.5),
    inset 0 -4px 7px rgba(120,60,15,.35),
    0 4px 0 #8f3f13;                            /* crisper Münz-Rand unten */
  transition:transform .08s, box-shadow .08s;touch-action:manipulation;overflow:hidden}
/* Zahl optisch mittig: Luckiest Guy sitzt tief (im Browser eingemessen) */
.fz-chip span{position:relative;z-index:1;transform:translateY(-1px);
  text-shadow:0 1px 0 rgba(255,236,205,.8)}
.fz-chip.lang span{font-size:16px;letter-spacing:-.02em}
/* geprägtes „C" oben im Bogen — macht klar: das sind MÜNZEN */
.fz-chip::before{content:"C";position:absolute;top:5px;left:50%;transform:translateX(-50%);
  font-family:var(--font-display);font-size:10px;color:rgba(90,45,10,.55);
  text-shadow:0 1px 0 rgba(255,236,205,.7)}
.fz-chip:active{transform:translateY(3px);box-shadow:inset 0 0 0 3px rgba(255,232,190,.75), 0 1px 0 #8f3f13}
/* gewählte Münze: Signal-Orange-Prägering + Hüpfer + Glanz-Sweep */
.fz-chip.gewaehlt{
  box-shadow:inset 0 0 0 3px rgba(255,232,190,.8), 0 0 0 3px var(--orange), 0 4px 0 #8f3f13;
  animation:muenzHuepf .42s cubic-bezier(.2,1.8,.4,1)}
.fz-chip.gewaehlt::after{
  content:"";position:absolute;top:-20%;left:-40%;width:36%;height:140%;
  background:linear-gradient(90deg, transparent, rgba(255,255,255,.65), transparent);
  transform:skewX(-18deg);animation:muenzGlanz 2.6s ease-in-out infinite}
@keyframes muenzHuepf{0%{transform:translateY(0) scale(1)}45%{transform:translateY(-7px) scale(1.1)}100%{transform:translateY(0) scale(1.05)}}
@keyframes muenzGlanz{0%{left:-40%}55%,100%{left:120%}}
/* nicht leistbar: angelaufene Münze */
.fz-chip.zu-teuer{filter:grayscale(.8) brightness(.85);opacity:.5}
.fz-chip.wiggle{animation:wiggle .4s ease}
/* „0" ist keine Münze, sondern ein Kreide-Zettel: Freundschaftsduell */
.fz-chip.gratis{
  background:linear-gradient(180deg,#fbf5e9,#efe4cf);
  border:3px dashed #c9a06a;border-radius:12px;font-size:13px;color:#8a7256;
  box-shadow:0 3px 0 rgba(120,90,50,.28)}
.fz-chip.gratis::before{content:""}
.fz-chip.gratis span{transform:none}
.fz-chip.gratis.gewaehlt{box-shadow:0 0 0 3px var(--orange), 0 3px 0 rgba(120,90,50,.28)}
/* Funkcode groß auf dem Zettel */
.funk-code{
  font-family:var(--font-display);font-size:52px;letter-spacing:.18em;text-align:center;
  color:var(--abend-top);margin:6px 0 10px;padding:10px 6px 4px;
  border:3px dashed #c9a06a;border-radius:10px;background:rgba(255,255,255,.45)}
.fz-warte{display:flex;align-items:center;justify-content:center;gap:9px;
  font-family:var(--font-marker);font-size:13px;color:#8a7256;margin-top:6px}
.funk-puls{width:10px;height:10px;border-radius:50%;background:var(--orange);
  box-shadow:0 0 0 0 rgba(232,114,44,.5);animation:funkPuls 1.4s ease-out infinite}
@keyframes funkPuls{0%{box-shadow:0 0 0 0 rgba(232,114,44,.55)}100%{box-shadow:0 0 0 14px rgba(232,114,44,0)}}
/* Code-Eingabe: eine ruhige gestrichelte Schreiblinie, wie auf dem Namensschild.
   ⚠ Hier lag früher ZUSÄTZLICH ein Streifenmuster (repeating-linear-gradient) als
   Trennmarken für die fünf Zeichen. Bei 266 px Feldbreite wurden daraus fünf Stummel von
   je 2,3 × 3 px, die genau auf dem 3-px-Rand saßen — Alens Befund „so kleine Punkte
   drüber, die sind buggy". Zwei Linien-Ebenen an derselben Kante ergeben nie ein sauberes
   Bild; deshalb bleibt nur der gestrichelte Rand. */
.join-input{
  display:block;width:100%;margin:0 0 6px;
  font-family:var(--font-display);font-size:42px;letter-spacing:.42em;text-align:center;text-transform:uppercase;
  color:var(--abend-top);background:transparent;border:none;outline:none;padding:8px 0 4px;
  border-bottom:3px dashed #c9a06a;
  user-select:text;-webkit-user-select:text;touch-action:auto}
.join-input::placeholder{color:#cbb995;letter-spacing:.42em}
/* Lobby: zwei Frachtaufkleber gegenüber */
.fz-vs{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:6px 0 12px}
.fz-name{flex:1;min-width:0;font-family:var(--font-display);font-size:17px;color:#3a2a17;
  background:#fbf5e9;border:2.5px solid var(--orange);border-radius:8px;padding:9px 6px 6px;
  text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transform:rotate(-1deg)}
.fz-name:last-child{transform:rotate(1.2deg)}
.fz-gegen{font-family:var(--font-marker);font-size:12px;color:var(--orange);flex:0 0 auto}
.fz-einsatz-stempel{
  /* margin-top 19px: kollabiert mit den 12px margin-bottom von .fz-vs zu max(19,12)=19px —
     kleinere Werte verpuffen im Collapsing! Die −3°-Drehung hebt die linke Stempel-Ecke
     ~6px an; ohne die Extra-Luft berührte sie fast die Namens-Kachel (0,7px gemessen). */
  width:max-content;max-width:100%;margin:19px auto 10px;
  font-family:var(--font-display);font-size:20px;color:#3f9b5c;
  border:3px solid #3f9b5c;border-radius:8px;padding:5px 14px 2px;transform:rotate(-3deg);
  box-shadow:inset 0 0 0 1px rgba(63,155,92,.35);text-transform:uppercase;
  /* ⚠ EIGENE Slam-Keyframes — gekauftSlam (Shop) trägt translateY(-50%) für seinen absolut
     positionierten Stempel; hier im Fluss schob das den Stempel dauerhaft um die halbe Höhe
     nach oben ÜBER die Namens-Kacheln (Alens Überlapp-Bug in der Lobby). */
  animation:einsatzSlam .45s cubic-bezier(.18,2.6,.36,1) both}
@keyframes einsatzSlam{
  0%{transform:scale(2.6) rotate(14deg);opacity:0}
  55%{transform:scale(.9) rotate(-6deg);opacity:1}
  100%{transform:scale(1) rotate(-3deg);opacity:.97}
}
.fz-info{font-family:var(--font-marker);font-size:13.5px;color:#a24e18;text-align:center;
  margin-top:10px;line-height:1.5}
/* Ehrlicher Beta-Hinweis — steht in den Abschnitten „Code zeigen" und „Beitreten", also
   genau dort, wo gewartet wird und es schiefgehen kann. NICHT im Start-Abschnitt: der ist
   als .funkzettel.kompakt auf 298×241 px eingemessen (Alens drei Nachjustier-Runden). */
.fz-beta{font-family:var(--font-marker);font-size:11.5px;line-height:1.45;color:#9a7b58;
  text-align:center;margin:11px 6px 0}
/* Countdown-Stempel überm Spielfeld */
#duellCount{
  position:absolute;inset:0;z-index:30;pointer-events:none;
  display:flex;align-items:center;justify-content:center}
#duellCount span{
  font-family:var(--font-display);font-size:110px;color:var(--orange);text-transform:uppercase;
  text-shadow:0 4px 0 rgba(0,0,0,.4);
  animation:countSlam .82s cubic-bezier(.18,2.2,.36,1) both}
#duellCount span.go{color:#3f9b5c;font-size:96px}
@keyframes countSlam{
  0%{transform:scale(3) rotate(-9deg);opacity:0}
  35%{transform:scale(.9) rotate(2deg);opacity:1}
  70%{transform:scale(1) rotate(-1deg);opacity:1}
  100%{transform:scale(1.05) rotate(-1deg);opacity:0}
}
/* Sieger-Show */
.duell-ende{display:flex;flex-direction:column;align-items:center;gap:18px}
.de-stamp{
  font-family:var(--font-display);font-size:64px;letter-spacing:.04em;text-transform:uppercase;
  color:#3f9b5c;border:6px solid #3f9b5c;border-radius:14px;padding:12px 30px 4px;
  transform:rotate(-6deg);box-shadow:inset 0 0 0 3px rgba(63,155,92,.35);
  background:rgba(242,237,228,.08);
  animation:recordSlamDe .65s cubic-bezier(.18,2.6,.36,1) both;
  position:relative}
/* „Verloren" ist das längste Verdikt: 64px sprengen die 375er-Breite (394px gemessen) —
   kleinerer Grad, die Niederlage darf ruhig kleiner gestempelt sein als SIEG! */
.de-stamp.verloren{color:var(--orange);border-color:var(--orange);box-shadow:inset 0 0 0 3px rgba(232,114,44,.35);
  font-size:44px;padding:12px 24px 4px}
.de-stamp.remis{color:#9CC7DE;border-color:#9CC7DE;box-shadow:inset 0 0 0 3px rgba(156,199,222,.35)}
.de-stamp::before{content:"";position:absolute;inset:-12px;border-radius:20px;border:5px solid currentColor;
  opacity:0;animation:recordShockDe .8s cubic-bezier(.1,.7,.3,1) .18s both}
@keyframes recordSlamDe{
  0%{transform:scale(3.6) rotate(14deg);opacity:0}
  50%{transform:scale(.85) rotate(-8deg);opacity:1}
  70%{transform:scale(1.08) rotate(-4deg)}
  100%{transform:scale(1) rotate(-6deg);opacity:1}
}
@keyframes recordShockDe{0%{transform:scale(.7);opacity:.9}100%{transform:scale(2);opacity:0}}
.de-scores{display:flex;flex-direction:column;gap:10px;width:min(88%,300px)}
.de-zeile{display:flex;justify-content:space-between;align-items:center;
  background:var(--kreide);color:var(--abend-top);border-radius:10px;padding:11px 16px;
  font-family:var(--font-display);font-size:18px;box-shadow:0 3px 0 rgba(0,0,0,.3)}
.de-zeile b{font-size:26px;font-variant-numeric:tabular-nums}
.de-zeile.gegner{opacity:.82;transform:scale(.96)}
.de-coins{font-family:var(--font-marker);font-size:17px;color:#f6b26a;text-shadow:0 2px 0 rgba(0,0,0,.4)}
/* Münz-Konfetti (DOM, nur Sieg) */
#deKonfetti{position:absolute;inset:0;overflow:hidden;pointer-events:none}
#deKonfetti i{
  position:absolute;top:-16px;width:11px;height:11px;border-radius:50%;
  background:radial-gradient(circle at 35% 30%, #ffe9b0, #f6b26a 60%, #c9752e);
  animation:muenzFall var(--dauer,2.2s) linear var(--verz,0s) infinite}
@keyframes muenzFall{
  0%{transform:translateY(-20px) rotate(0)}
  100%{transform:translateY(110vh) rotate(720deg)}
}
@media (prefers-reduced-motion: reduce){
  .funkzettel{animation:none}
  .fz-lampe,.funk-puls{animation:none}
  .fz-chip.gewaehlt{animation:none;transform:scale(1.05)}
  .fz-chip.gewaehlt::after{animation:none;display:none}
  #duellCount span{animation:none;opacity:1}
  .de-stamp{animation:none}
  .de-stamp::before{animation:none}
  .fz-einsatz-stempel{animation:none}
  #deKonfetti{display:none}
}

/* Angepinntes Klemmbrett-Tab am Homescreen (öffnet die Rangliste).
   KEIN z-index → liegt in DOM-Reihenfolge hinter Rolltor und allen Overlays (Shop/Rangliste/
   Game-Over decken es ab → dort nicht anklickbar). Scharf: crisper 0-Blur-Schlagschatten +
   harter Text-Outline (kein Weichzeichner). WOW: Gold-Stern im SVG + sanftes Wippen. */
/* ⚠ KEIN env(safe-area-inset-*) hier: das Tab sitzt in #stage, und #stage steckt in #wrap,
   das die Sicherheitsabstände bereits als Innenabstand abzieht. Solange viewport-fit=cover
   fehlte, lieferte env() überall 0 und der doppelte Abzug fiel nicht auf — seit der
   Homescreen-Verpackung (Aug 2026) würde er das Tab auf einem iPhone mit Notch um weitere
   59 px nach unten schieben. Die Abstände gehören an GENAU EINE Stelle: #wrap. */
#lbTab{
  position:absolute;top:6px;right:4px;
  display:flex;flex-direction:column;align-items:center;gap:2px;cursor:pointer;
  background:none;border:none;padding:2px;transform:rotate(4deg);
  filter:drop-shadow(2px 3px 0 rgba(0,0,0,.42));
  /* Beim Homescreen-Start fällt das Tab rein wie die Menü-Etiketten (gleiches Timing),
     danach geht es ins ruhige Dauer-Wippen über. */
  animation:
    lbTabIn .5s cubic-bezier(.2,1.6,.4,1) .3s both,
    lbTabBob 2.8s ease-in-out .9s infinite}
@keyframes lbTabIn{
  0%{opacity:0;transform:rotate(4deg) translateY(-54px)}
  70%{opacity:1;transform:rotate(4deg) translateY(5px)}
  100%{opacity:1;transform:rotate(4deg) translateY(0)}
}
/* Menü-Rückkehr: kein erneutes Reinfallen, nur ruhig weiterwippen (sofort sichtbar) */
.overlay.menu.instant #lbTab{animation:lbTabBob 2.8s ease-in-out infinite}
/* Nach dem ersten Reinfallen (animationend → .drin in main.js): nur noch Wippen — lbTabIn
   kann durch keinen :active-/Klassen-Wechsel mehr neu abspielen (iOS-Tap-Schutz). */
#lbTab.drin{animation:lbTabBob 2.8s ease-in-out .9s infinite}
/* Abgang „Abfahrt!": das Tab weht mit den anderen Papieren (Alens Befund „hat keinen
   Übergang wie die anderen Sachen"). Eigene Keyframes, weil die 4°-Ruhelage in der
   Basis-transform steckt (kein --rot). ⚠ MUSS in der Reihenfolge NACH .instant/.drin
   stehen (gleiche bzw. geringere Spezifität) — sonst wippt das Tab beim Rückkehr-Start
   einfach weiter und verschwindet hart (im Verifikations-Test genau so passiert). */
.overlay.menu.leaving #lbTab{animation:tabWind .7s cubic-bezier(.55,0,.8,.4) .12s forwards;pointer-events:none}
@keyframes tabWind{
  20%{transform:rotate(8deg) translate(10px, -3px)}
  100%{transform:rotate(-16deg) translate(-130vw, -4vh);opacity:.9}
}
#lbTab svg{width:56px;height:72px;display:block;pointer-events:none}
.lb-tab-label{
  font-family:var(--font-display);font-size:9px;letter-spacing:0;line-height:1;
  color:var(--orange);text-transform:uppercase;pointer-events:none;
  /* harter Sticker-Outline, 0 Blur = knackscharf */
  text-shadow:1px 1px 0 #3a2412,-1px 1px 0 #3a2412,1px -1px 0 #3a2412,-1px -1px 0 #3a2412,0 2px 0 rgba(0,0,0,.4)}
/* ⚠ kein animation:none im :active — iOS spielt sonst lbTabIn (2,15 s Delay, opacity 0,
   −54px) beim Loslassen neu: Tab unsichtbar + verschoben, der Click nach touchend verpufft
   (Alens „Rangliste tot"). Press über die separate translate-Property. */
#lbTab:active{translate:0 2px;filter:drop-shadow(1px 1px 0 rgba(0,0,0,.42))}
@keyframes lbTabBob{0%,100%{transform:rotate(4deg) translateY(0)}50%{transform:rotate(4deg) translateY(-3px)}}
.overlay.menu.leaving #lbTab{opacity:0;transition:opacity .2s}

/* ===== Die zwei Eck-Aufkleber: Sprache (unten links) und Ton (unten rechts) =====
   Ein PAAR, deshalb dieselbe Form (Alens Regel „je Gruppe einheitlich"): ein kleiner
   Frachtaufkleber mit Stempelrahmen, wie die Menü-Etiketten, nur im Westentaschenformat.
   Nur der Inhalt unterscheidet sie — links das gestempelte Sprachkürzel, rechts der
   Lautsprecher. Wie das Klemmbrett-Tab bewusst OHNE z-index (liegt hinter Rolltor und
   allen Overlays) und mit crispem 0-Blur-Schatten.
   Arbeitsteilung der Elemente, damit sich zwei Animationen nie in die Quere kommen:
   der BUTTON trägt Auftritt und Abgang, die KARTE darin Neigung, Schatten und Flip. */
/* ⚠ KEIN env(safe-area-inset-*) — s. #lbTab: #wrap zieht die Abstände schon ab, sonst
   säßen die beiden Ecken auf einem iPhone mit Home-Anzeige 34 px zu hoch. */
.eck-btn{
  position:absolute;bottom:9px;
  width:54px;height:54px;padding:0;border:none;background:none;cursor:pointer;
  animation:ecknopfRein .45s cubic-bezier(.2,1.6,.4,1) var(--ein,.15s) both;
  transition:translate .07s}
/* Auftritt hinter dem geschlossenen Tor: das Tor fährt von unten auf, die beiden Ecken
   werden also als ERSTES freigegeben (~0,7 s) — sie stehen bei 0,60/0,67 s fertig da. */
.eck-links{left:9px;--ein:.15s;--wind:.07s}
.eck-rechts{right:9px;--ein:.22s;--wind:.11s}
.eck-karte{
  display:block;width:100%;height:100%;position:relative;
  transform:rotate(var(--rot,0deg)) perspective(240px) rotateY(0deg);
  filter:drop-shadow(2px 3px 0 rgba(0,0,0,.42));
  transition:transform .13s ease-in}
.eck-links .eck-karte{--rot:-5deg}
.eck-rechts .eck-karte{--rot:5deg}
.eck-btn svg{display:block;width:100%;height:100%;pointer-events:none}
.eck-wort{
  position:absolute;left:0;right:0;top:50%;translate:0 -50%;
  font-family:var(--font-display);font-size:19px;line-height:1;letter-spacing:.04em;
  color:var(--orange);text-align:center;pointer-events:none}
/* Der Aufkleber KLAPPT UM (halber Flip): main.js setzt .flip, tauscht auf halbem Weg das
   Kürzel und nimmt die Klasse wieder ab. Bewusst zwei Halbdrehungen statt Rückseite —
   backface-visibility ist auf iOS zusammen mit drop-shadow glitch-anfällig. Die
   Funktionsliste ist in beiden Zuständen identisch, damit sauber interpoliert wird. */
.eck-btn.flip .eck-karte{transform:rotate(var(--rot,0deg)) perspective(240px) rotateY(84deg)}
/* ⚠ :active fasst die animation NIE an (iOS spielt sie sonst beim Loslassen neu ab und der
   Click trifft ins Leere — die dokumentierte Tap-Falle). Druck über translate. */
.eck-btn:active{translate:0 2px}
.eck-btn:active .eck-karte{filter:drop-shadow(1px 1px 0 rgba(0,0,0,.42))}
@keyframes ecknopfRein{
  0%{opacity:0;transform:scale(1.55);pointer-events:none}
  60%{opacity:1;pointer-events:auto}
  72%{transform:scale(.94)}
  100%{opacity:1;transform:scale(1)}
}
/* Nach dem ersten Auftritt (animationend → .drin in main.js) sowie bei Menü-Rückkehr
   stehen sie einfach da — kein Klassen-Wechsel kann den Auftritt neu auslösen. */
.eck-btn.drin{animation:none}
.overlay.menu.instant .eck-btn{animation:none;opacity:1}
/* Ton aus: ein Streifen Paketband wird quer über den Aufkleber geklatscht, die
   Schallwellen verblassen darunter. ⚠ NACH .instant/.drin, damit der Abgang gewinnt. */
.eck-band{opacity:0;transform-box:fill-box;transform-origin:50% 50%;transition:opacity .14s ease}
.eck-welle{transition:opacity .18s ease}
#btnTon.aus .eck-band{opacity:1;animation:bandSlam .24s cubic-bezier(.2,1.7,.5,1)}
#btnTon.aus .eck-welle{opacity:.3}
/* ⚠ Die Sichtbarkeit hängt allein an der Regel oben (opacity 0→1 über die transition), NICHT
   an diesen Keyframes: eine pausierte Animation (Hintergrund-Tab, iOS-Stromsparmodus) würde
   das Band sonst auf ihrem 0-%-Bild festhalten und es bliebe unsichtbar. Hier nur Bewegung. */
@keyframes bandSlam{
  0%{transform:scale(1.4) rotate(-7deg)}
  55%{transform:scale(.96) rotate(1.5deg)}
  100%{transform:scale(1) rotate(0)}
}
/* Abgang „Abfahrt!": die beiden Ecken wehen mit den anderen Papieren davon. Eigene
   Keyframes (der Button trägt keine Grundneigung — die steckt in .eck-karte). */
.overlay.menu.leaving .eck-btn{
  animation:eckWind .7s cubic-bezier(.55,0,.8,.4) var(--wind,.07s) forwards;pointer-events:none}
@keyframes eckWind{
  20%{transform:translate(11px,-3px) rotate(5deg)}
  100%{transform:translate(-130vw,-7vh) rotate(-24deg);opacity:.9}
}
@media (prefers-reduced-motion: reduce){
  .eck-btn{animation:none;opacity:1}
  .eck-karte{transition:none}
  .eck-btn.flip .eck-karte{transform:rotate(var(--rot,0deg)) perspective(240px) rotateY(0deg)}
  #btnTon.aus .eck-band{animation:none}
  .overlay.menu.leaving .eck-btn{animation:none;opacity:0}
}

/* Namens-Gate: thematisches Namensschild, einmalige Eingabe (danach unveränderlich).
   Erscheint beim Erststart erst NACH dem Rolltor (Timing in main.js) — der Scrim fadet
   weich auf (Opacity-only, bleibt darum auch unter Reduced Motion), das Schild darin
   bringt sein katalogAuf mit. */
#ovName{animation:ovNameAuf .45s ease both}
@keyframes ovNameAuf{from{opacity:0}to{opacity:1}}
#lbNameGate{display:flex;flex-direction:column;align-items:center}
.namensschild{
  position:relative;width:min(88%,300px);background:var(--kreide);color:var(--abend-top);
  border-radius:9px;padding:0 0 20px;overflow:hidden;transform:rotate(-1.2deg);
  box-shadow:0 16px 40px rgba(0,0,0,.55);
  animation:katalogAuf .45s cubic-bezier(.2,1.5,.4,1) both}
.ns-banner{
  background:linear-gradient(180deg,#f6a869,var(--orange));color:#25160a;
  font-family:var(--font-display);font-size:22px;letter-spacing:.03em;text-transform:uppercase;
  text-align:center;padding:12px 10px 9px;box-shadow:inset 0 -3px 0 rgba(0,0,0,.16)}
.ns-sub{font-family:var(--font-marker);font-size:13px;color:#6f5334;text-align:center;margin:15px 18px 2px;line-height:1.4}
/* Rückmeldung beim Einstempeln: „Name schon vergeben" bzw. „dafür braucht es kurz
   Internet". Sitzt zwischen Eingabezeile und Stempel, damit der Blick sie auf dem Weg zum
   Knopf ohnehin streift. In Stempel-Rot, aber ohne Alarm-Geste. */
/* Fehlerzeile im Namensschild — sitzt ÜBER dem Eingabefeld (iOS-Tastatur verdeckte sie
   unterhalb, s. leaderboard.js). Auftritt nur als Bewegung: die Sichtbarkeit hängt an der
   Basis-Regel, NIE an den Keyframes (Hintergrund-Tab-Falle). */
.ns-fehler{font-family:var(--font-marker);font-size:13.5px;line-height:1.45;color:#B5342B;
  text-align:center;margin:10px 18px 0;animation:nsFehlerAuf .22s cubic-bezier(.2,1.4,.4,1)}
@keyframes nsFehlerAuf{0%{transform:scale(.86)}100%{transform:scale(1)}}
.ns-input{
  display:block;width:calc(100% - 44px);margin:14px auto 2px;
  font-family:var(--font-marker);font-size:20px;color:var(--abend-top);text-align:center;
  background:transparent;border:none;border-bottom:3px dashed #c9a06a;padding:7px 4px;outline:none;
  user-select:text;-webkit-user-select:text;touch-action:auto}
.ns-input::placeholder{color:#bcac96}
.ns-input.wiggle{animation:wiggle .4s ease}
/* „Einstempeln" ist jetzt ein echter Stempelabdruck (passt zum Wort) statt eines Knopfs */
.ns-stempel{
  display:block;margin:20px auto 0;cursor:pointer;
  font-family:var(--font-display);font-size:20px;letter-spacing:.05em;color:#3f9b5c;text-transform:uppercase;
  background:transparent;
  border:3.5px solid #3f9b5c;border-radius:9px;padding:9px 26px 6px;
  box-shadow:inset 0 0 0 2px rgba(63,155,92,.3);
  transform:rotate(-3deg);
  transition:transform .08s, box-shadow .08s, color .12s}
.ns-stempel:active{transform:rotate(-3deg) scale(.93);color:#2c6e40}
.ns-stempel:disabled{opacity:.38;filter:grayscale(.5);transform:rotate(-3deg)}

/* .drin-Dauerlisten unter Reduced-Motion ebenfalls stilllegen. Bewusst am DATEI-ENDE:
   die .drin-Selektoren sind spezifischer als die Reduced-Regeln weiter oben (.etikett-btn /
   #lbTab) — nur die spätere Position im File lässt diese Gegenregel sicher gewinnen. */
@media (prefers-reduced-motion: reduce){
  .etikett-btn.drin,.etikett-btn.drin.wiggle,#lbTab.drin{animation:none}
  .ns-fehler{animation:none}
}
