/* =====================================================================
   bunker.css — DIE ATOMBOMBE UND KAPITEL 4 (A.1.48.0)

   Zwei Sachen, und sie gehoeren zusammen: was der rote Knopf ausloest,
   und der Raum, in dem man danach steht.

   ⚠️⚠️ DER UEBERGANG LAEUFT UEBER VOLLE DECKUNG, nicht ueber eine weiche
   Blende: wer eine Landschaft in eine andere blendet, zeigt einen
   Augenblick lang beide. Nach der Bombe soll von der Wiese nichts mehr
   zu sehen sein — das ist die ganze Aussage.
   ===================================================================== */

/* =====================================================================
   DER EINSCHLAG (A.1.49.0) — seine Ansage vom 22.09.: „die Atomexplosion
   ist mir zu wild und gleichzeitig unspektakulaer. Die Bombe soll hinten
   im Bild spektakulaer einschlagen und typisch explodieren."

   ⚠️⚠️⚠️ „ZU WILD UND GLEICHZEITIG UNSPEKTAKULAER" HATTE EINE URSACHE:
   ES GAB KEINEN ORT. A.1.48.0 blitzte sofort das ganze Bild weiss, und
   was dahinter geschah, war nicht zu sehen. Ein Ereignis ohne Ort ist
   immer beides — es ueberfaehrt einen und zeigt nichts.
   ▶ Alles hier haengt darum an EINER Stelle (`--ex` / `--ey`, von
   atom.js gesetzt): die Bombe faellt dorthin, der Feuerball steht dort,
   der Pilz steigt dort, die Welle geht von dort aus. Wer eine dieser
   Zeilen an eine andere Stelle setzt, macht aus dem Ereignis wieder
   einen Bildfehler.
   ===================================================================== */

/* ---- DIE BOMBE FAELLT ----
   ⚠️ Sie ist der GRUND, warum der Einschlag ein Ereignis ist. Ein
   Feuerball, der einfach da ist, ist ein Bildfehler; einer, auf den man
   etwas zufallen sieht, ist ein Einschlag. Sie faellt schraeg (von
   links oben), weil senkrecht aussaehe, als haette jemand sie fallen
   lassen — und wird beim Fallen laenger: das ist die Geschwindigkeit. */
.atom-bombe {
  position: fixed;
  left: 0;
  top: 0;
  width: 7px;
  height: 26px;
  margin: -13px 0 0 -3.5px;
  border-radius: 50% 50% 46% 46%;
  background: linear-gradient(180deg, #ffe9b0 0%, #e8a53c 40%, #8d4a12 100%);
  box-shadow: 0 0 14px 3px rgba(255, 190, 90, 0.7);
  pointer-events: none;
  z-index: 11;
  animation: atom-faellt 800ms cubic-bezier(.5, 0, .85, .5) forwards;
}
@keyframes atom-faellt {
  0%   { transform: translate(calc(var(--ex) - 190px), -60px) scaleY(1); opacity: 0; }
  12%  { opacity: 1; }
  100% { transform: translate(var(--ex), var(--ey)) scaleY(2.6); opacity: 1; }
}

/* ---- DER FEUERBALL ----
   ⚠️⚠️ „TYPISCH EXPLODIEREN" HEISST: WEISS INNEN, ORANGE AUSSEN, UND ER
   STEIGT. Ein Feuerball, der an seiner Stelle bleibt und verblasst, ist
   eine Explosion aus dem Zeichentrick. Er blaeht in 260 ms auf, zieht
   sich dann zusammen und geht nach oben — genau dort steigt der Pilz aus
   ihm heraus (1500 ms, atom.js). */
.atom-feuerball {
  position: fixed;
  left: 0;
  top: 0;
  width: 26vmax;
  height: 26vmax;
  margin: -13vmax 0 0 -13vmax;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 54%,
    #fffdf2 0%, #fff0b4 14%, #ffc247 30%, #f07a12 48%,
    rgba(180, 62, 8, 0.66) 64%, rgba(90, 30, 6, 0) 78%);
  pointer-events: none;
  z-index: 10;
  mix-blend-mode: screen;
  animation: atom-feuer 3.1s cubic-bezier(.06, .8, .3, 1) forwards;
}
@keyframes atom-feuer {
  0%   { transform: translate(var(--ex), var(--ey)) scale(0.04); opacity: 1; }
  9%   { transform: translate(var(--ex), var(--ey)) scale(1); opacity: 1; }
  26%  { transform: translate(var(--ex), calc(var(--ey) - 3vmax)) scale(0.82); opacity: 0.95; }
  60%  { transform: translate(var(--ex), calc(var(--ey) - 9vmax)) scale(0.6); opacity: 0.5; }
  100% { transform: translate(var(--ex), calc(var(--ey) - 15vmax)) scale(0.42); opacity: 0; }
}

/* ---- DER KRAGEN AM BODEN ----
   ⚠️ Was am Einschlag aufgeworfen wird, laeuft FLACH nach aussen, nicht
   rund: der Boden ist im Weg. Darum eine flachgedrueckte Ellipse — sie
   ist der einzige Hinweis darauf, dass die Bombe den BODEN getroffen hat
   und nicht in der Luft stand. */
.atom-kragen {
  position: fixed;
  left: 0;
  top: 0;
  width: 30vmax;
  height: 30vmax;
  margin: -15vmax 0 0 -15vmax;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
    rgba(214, 196, 158, 0) 38%, rgba(196, 174, 132, 0.75) 54%,
    rgba(140, 120, 88, 0.4) 70%, rgba(110, 96, 70, 0) 84%);
  pointer-events: none;
  z-index: 9;
  animation: atom-kragen 2.5s cubic-bezier(.1, .8, .3, 1) forwards;
}
@keyframes atom-kragen {
  0%   { transform: translate(var(--ex), var(--ey)) scale(0.05, 0.02); opacity: 0.9; }
  40%  { opacity: 0.8; }
  100% { transform: translate(var(--ex), var(--ey)) scale(1, 0.24); opacity: 0; }
}

/* ---- DAS ANLEUCHTEN ----
   ⚠️⚠️⚠️ DIE LANDSCHAFT WIRD ANGELEUCHTET, NICHT ZUGEDECKT. Das ist die
   ganze Reparatur an seinem „zu wild": statt eines weissen Dings VOR der
   Wiese ein Filter AUF ihr. Man sieht die ganze Zeit, wo man ist — und
   genau darum sieht man ueberhaupt, was passiert. */
:root.atom-leuchtet .natur {
  filter: brightness(2.6) saturate(0.45) contrast(1.1);
  transition: filter 90ms linear;
}

/* =====================================================================
   DIE DRUCKWELLE — „Dann soll eine Druckwelle auf den Spieler zurasen."

   ⚠️⚠️⚠️ SIE IST EINE WAND, KEINE SCHEIBE — UND BIS A.1.50.0 WAR SIE
   EINE SCHEIBE (A.1.51.0, sein Befund „insbesondere die Druckwelle die
   auf einen zukommt"). Angesehen am 22.09. bei 1920x1080, angehalten:
   man sah den GANZEN Ring, oberen und unteren Bogen, samt seinen
   seitlichen Enden mitten im Bild. **Eine Form, deren Enden man sieht,
   ist ein Ding** — und dieses Ding las sich zwingend als flach liegende
   Scheibe, die man von schraeg oben sieht. Eine fliegende Untertasse,
   keine Druckwelle.
   ▶ DIE REPARATUR IST GEOMETRIE, NICHT FARBE: die Ellipse ist von
   Anfang an BREITER ALS DAS FENSTER (150vmax) und waechst um den
   Einschlag, statt zu wandern. Damit liegen ihre seitlichen Enden immer
   ausserhalb, und sichtbar sind nur noch die zwei Linien, die eine Wand
   ueberhaupt hat:
     · die BODENLINIE (unterer Bogen) — dort steht die Wand auf dem
       Boden, und sie rast auf einen zu. Was UNTER ihr liegt, ist naeher
       als die Wand und bleibt sichtbar. Darum sieht man die Wiese bis
       zuletzt.
     · die OBERKANTE (oberer Bogen) — sie steigt, je naeher die Wand
       kommt, und loest sich im Staub auf. Eine scharfe Oberkante waere
       ein Vorhang.
   ⚠️⚠️ DAS WANDERN IST WEG, UND DAS IST DER PUNKT. Die alte Fassung
   schob die Form mit `translate` nach unten, damit sie „naeher kommt" —
   aber eine Kreiswelle wandert nicht, sie WAECHST. Wer sie schiebt,
   nimmt ihr den Mittelpunkt, und ohne Mittelpunkt kommt sie von
   nirgendwo. Der Mittelpunkt bleibt jetzt auf dem Einschlag; die Naehe
   entsteht allein daraus, dass die Bodenlinie nach unten und die
   Oberkante nach oben laeuft.
   ⚠️ ZWEI FRONTEN BLEIBEN, mit geschaerften Rollen: die Schockfront ist
   nur noch die BODENLINIE (ein heller Bogen, der vorauslaeuft), die
   Staubwand ist die FLAECHE dahinter. Vorher waren es zwei Ringe — und
   zwei Ringe sind zwei Untertassen.
   ===================================================================== */

/* ---- DIE SCHOCKFRONT: nur die Bodenlinie, und sie laeuft voraus ----
   ⚠️⚠️ SIE WIRD UEBER `width`/`height` ANIMIERT, NICHT UEBER `scale`,
   und das ist kein Geschmack: `scale` dehnt die Randbreite mit. Bei der
   Streckung, die eine nahe Front braucht (Faktor 14 und mehr), wuerde
   aus einer 3-px-Linie ein 42-px-Balken — aus der scharfen Front waere
   auf halbem Weg ein Streifen geworden. Ein Element ohne Kinder, drei
   Sekunden lang: das kostet nichts.
   ⚠️ `border-bottom` allein: der obere Bogen gehoert der Wand, nicht
   ihr. Die Stellen, an denen CSS von unten nach links und rechts
   uebergeht, liegen bei dieser Breite weit ausserhalb des Fensters. */
.atom-schock {
  position: fixed;
  left: var(--ex);
  top: var(--ey);
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border-bottom: 0.3vmax solid rgba(255, 244, 214, 0.9);
  box-shadow: 0 0.4vmax 2.4vmax -0.2vmax rgba(255, 226, 168, 0.5);
  pointer-events: none;
  z-index: 12;
  animation: atom-schock 3.9s cubic-bezier(.28, .42, .55, 1) forwards;
}
/* ⚠️ SIE LAEUFT DER STAUBWAND VORAUS, ABER NUR UM EINE WAGENLAENGE. Beim
   Sprung (62 %) steht ihre Linie bei rund 810 px, die der Wand bei 710 —
   ein Vorsprung von einem knappen Hundertstel Bildhoehe. Waere er
   groesser, waeren es zwei Wellen; waere er null, waere es eine. */
@keyframes atom-schock {
  0%   { width: 150vmax; height: 3vmax;   opacity: 0; }
  7%   { opacity: 0.95; }
  30%  { width: 168vmax; height: 16vmax;  opacity: 0.95; }
  62%  { width: 190vmax; height: 44vmax;  opacity: 0.9; }
  77%  { width: 215vmax; height: 72vmax;  opacity: 0.7; }
  100% { width: 280vmax; height: 190vmax; opacity: 0; }
}

/* ---- DIE STAUBWAND ----
   ⚠️⚠️⚠️ DER VERLAUF LAEUFT VON UNTEN NACH OBEN, NICHT VON INNEN NACH
   AUSSEN. Ein radialer Verlauf hat seinen dichtesten Punkt in der MITTE
   der Form — und die Mitte einer Wand, die auf einen zukommt, liegt
   hinter dem Horizont. Er machte die Wand dort dicht, wo sie am
   weitesten weg ist. Jetzt ist sie unten dicht (dort steht sie auf dem
   Boden und reisst ihn auf, darum der helle Saum) und loest sich nach
   oben auf.
   ⚠️ Die 150vmax Breite sind die ganze Aussage dieser Regel: sie
   garantieren, dass die seitlichen Enden auch auf einem breiten Schirm
   ausserhalb liegen. Wer sie kleiner macht, holt die Untertasse zurueck. */
.atom-front {
  position: fixed;
  /* ⚠️⚠️ DER ORT STEHT IN `left`/`top`, NICHT IM `transform` — und das
     musste er, sobald das `translate` weg war: die Keyframes setzen
     `transform` auf `scale(...)`, und ein zweites `transform` auf
     demselben Element gibt es nicht. Das `margin` holt die Ellipse um
     ihre halbe Groesse zurueck, damit ihr MITTELPUNKT auf dem Einschlag
     sitzt — und nur dann waechst sie um ihn herum. */
  left: var(--ex);
  top: var(--ey);
  width: 150vmax;
  height: 8vmax;
  margin: -4vmax 0 0 -75vmax;
  border-radius: 50%;
  background: linear-gradient(to top,
    rgba(255, 238, 202, 0.95) 0%,
    rgba(206, 172, 124, 0.97) 4%,
    rgba(154, 132, 100, 0.97) 16%,
    rgba(132, 114, 88, 0.94) 46%,
    rgba(142, 124, 96, 0.62) 74%,
    rgba(150, 132, 104, 0.22) 90%,
    rgba(150, 132, 104, 0) 100%);
  pointer-events: none;
  z-index: 11;
  animation: atom-front 4.2s cubic-bezier(.3, .32, .6, 1) forwards;
}
/* ⚠️⚠️⚠️ SIE WAR ZU SCHNELL, UND ZWAR GENAU DORT, WO ES ZAEHLT (A.1.53.0,
   seine Ansage vom 22.09.: „Die Druckwelle ist zu schnell. Sie ist schon
   da wenn man herab steigt.").
   ▶ DAS WAR KEINE FRAGE DER DAUER, SONDERN DER KURVE. Bis A.1.52.0 stand
   bei 83 % der Massstab 8 — und 83 % von 3,5 s sind 2905 ms, also
   Fenstermitte des Sprungs. Gerechnet: beim Sprung (4500 ms, das sind
   2500 ms nach dem Start, 71 % der Animation) stand die Bodenlinie
   schon bei rund 980 px von 1080. **Die Wand stand beim Einsteigen
   bereits auf der Luke**, und was er sehen wollte — hinabsteigen,
   waehrend sie heranrollt —, gab es nur noch als Behauptung.
   ⚠️⚠️ DIE ZAHLEN SIND GEGEN DEN HORIZONT GERECHNET, nicht geschaetzt.
   Der Einschlag liegt bei 36 % der Hoehe (`HORIZONT`), die halbe
   Grundhoehe sind 4vmax. Bei 1920x1080 heisst das: Bodenlinie
   = 389 px + 77 px x Massstab. Die Animation faengt bei 2000 ms an
   (`T.welle`), dauert 4,2 s, und die drei Augenblicke, auf die es
   ankommt, liegen darin:
     30 % = 3260 ms   Massstab 2,2 -> Bodenlinie  558 px (hinten)
     60 % = 4520 ms   Massstab 4,3 -> Bodenlinie  720 px  ▶ DER SPRUNG
                      (4500 ms): sie steht auf der halben Wiese und
                      rollt heran — man sieht sie KOMMEN, waehrend man
                      hinabsteigt. Das ist sein Satz.
     76 % = 5192 ms   Massstab 7,6 -> Bodenlinie  974 px  ▶ DIE LUKE
                      SCHLAEGT ZU (5200 ms): sie ist DA, im selben
                      Augenblick, in dem der Deckel faellt.
     88 % = 5696 ms   Massstab 10  -> Bodenlinie 1159 px (durch)
   ▶ Wer eine dieser drei Zahlen verschiebt, verschiebt das „in letzter
   Sekunde" — und zwar in beide Richtungen: zu frueh ist sie schon da
   (sein Befund), zu spaet war die Gefahr keine. */
@keyframes atom-front {
  0%   { transform: scale(1, 0.35);  opacity: 0; }
  8%   { opacity: 0.85; }
  30%  { transform: scale(1.18, 2.2); opacity: 1; }
  60%  { transform: scale(1.4, 4.3); opacity: 1; }
  76%  { transform: scale(1.66, 7.6); opacity: 1; }
  88%  { transform: scale(1.8, 10);  opacity: 1; }
  100% { transform: scale(1.95, 14); opacity: 1; }
}

/* ⚠️ Waehrend die Welle laeuft, LEGT SICH DAS GRAS: eine sehr kleine
   Scherung auf der ganzen Wiese. Man bemerkt sie nicht einzeln — man
   bemerkt nur, dass das Bild unter Druck steht. */
:root.atom-welle .natur {
  transform: skewX(-0.9deg) scale(1.03);
  transition: transform 2.4s cubic-bezier(.3, .6, .4, 1);
}

/* ---- DAS LAND VERBRENNT ----
   ⚠️ Kein neues Bild: dieselbe Wiese, durch einen Filter gesehen. Was
   eben noch gruen war, ist in zwei Sekunden verkohlt — und man sieht,
   dass es DASSELBE Land ist. Ein zweites, verbranntes Bild daneben waere
   ein Schnitt, und ein Schnitt erzaehlt nichts.
   ⚠️⚠️ `:root.atom-leuchtet` steht WEITER OBEN und gewinnt darum nicht:
   beide setzen `filter` auf `.natur`, und die spaetere Regel bei
   gleicher Spezifitaet schlaegt. Darum ist das Anleuchten hier noch
   einmal genannt — es dauert 1,4 s und muss in dieser Zeit gelten, auch
   wenn das Verbrennen schon begonnen hat. */
/* ⚠️ Der Uebergang ist so lang wie die Welle braucht (3,7 s,
   `WELLE_DAUER` in atom.js) und faengt mit ihr an — sie sind EIN
   Vorgang, und zwei Zahlen, die dasselbe meinen, laufen sonst
   auseinander. ▶ Er ist mit der Welle mitgewachsen (A.1.53.0): wer die
   Wand langsamer macht und das Verkohlen nicht, hat das alte Bild
   zurueck, in dem die Farbe das Ende vor dem Ende erzaehlt. */
:root.atom-verbrannt .natur {
  filter: sepia(0.85) saturate(0.55) brightness(0.52) contrast(1.25);
  transition: filter 3.7s ease-in;
}
:root.atom-verbrannt.atom-leuchtet .natur {
  filter: brightness(2.6) saturate(0.45) contrast(1.1);
  transition: filter 90ms linear;
}
/* ⚠⚠⚠ UNSICHTBAR IST NICHT AUS DEM WEG. Deckkraft 0 nimmt nur das
   BILD — die Flaeche liegt weiter da und faengt jeden Zeiger. Der
   Atomknopf ist ein echter `button`, steht `fixed` unten in der MITTE mit
   z-index 14 — also genau dort, wo 2,6 s spaeter die LUKE aufgeht —, und
   `istBedienung` in flinte.js zaehlt ihn weiter als Bedienstelle: eine
   Bedienstelle SCHLUCKT den Schuss. Heute traegt das nichts aus (`knall()`
   ist nach dem ersten Mal durch `A.gelaufen` gesperrt, das Fadenkreuz ist
   aus, die Trefferfelder sind still, der Systemzeiger haengt an
   `html.flinte-in-der-hand * { cursor: none !important }`) — aber das sind
   VIER fremde Sperren, die sich zufaellig decken, und keine davon steht
   hier. Faellt eine weg, liegt hier ein unsichtbarer Knopf auf der Luke.
   ▶ Dieselbe Lehre wie beim Vorhang der Pflichtseiten (`css/pflicht.css`):
   WEG heisst weg, und das sagt man an der Stelle, die es ausblendet. */
:root.atom-laeuft .schiessbude,
:root.atom-laeuft .waffe,
:root.atom-laeuft .atomknopf { opacity: 0; transition: opacity 400ms ease;
                               pointer-events: none; }
:root.atom-laeuft .fadenkreuz { visibility: hidden !important; }
/* ⚠️ Nach dem Knopf wird nicht mehr geschossen: die Wiese ist kein Stand
   mehr, sondern ein Nachruf. */
:root.atom-laeuft .n-ziel .n-trefferfeld,
:root.atom-laeuft .n-flinte .n-trefferfeld { pointer-events: none !important; }
/* ⚠️ Der Abwurf geht mit. Eine Kiste, die waehrend der Bombe noch am
   Schirm schaukelt, ist der Fehler, der die ganze Vorstellung kippt. */
:root.atom-laeuft .n-airdrop { opacity: 0; transition: opacity 500ms ease; }

/* =====================================================================
   DIE LUKE — „Man sieht nun (bevor die Druckwelle den Spieler erreicht)
   wie eine Luke aufgeht und der Spieler ‚in letzter Sekunde' es noch in
   den Bunker schafft."

   ⚠️⚠️⚠️ SIE LIEGT UNTEN IN DER MITTE, also genau dort, wo man selbst
   steht. Das ist die Stelle, an der das Bild einen Koerper bekommt: die
   ganze Wiese ueber war der Spieler ein Fadenkreuz — jemand, der sieht
   und schiesst, aber nirgends steht. Wer eine Luke vor seinen Fuessen
   aufgehen sieht, weiss, wo seine Fuesse sind.
   ===================================================================== */
/* ⚠️⚠️⚠️ SIE MUSS GROSS SEIN, UND DAS WAR SIE IM ERSTEN WURF NICHT.
   Gemessen auf dem Bild: mit `min(46vw, 560px)` und `bottom: -4vh` stand
   sie zur Haelfte unter dem Fensterrand, hinter dem Gras, und war bei
   1920 x 1080 kaum zu finden — **ausgerechnet das Ding, auf das die
   ganze Szene zulaeuft.** Kein Pruefpunkt hat das gemeldet: „die Luke ist
   da" war die ganze Zeit gruen.
   ▶ Jetzt steht sie mit ihrer Unterkante AUF dem Fensterrand und ist
   halb so breit wie das Bild. Das ist kein Geschmack, das ist die
   Aussage: was man in letzter Sekunde erreichen soll, muss man sehen. */
.atom-luke {
  position: fixed;
  left: 50%;
  bottom: 0;
  width: min(70vw, 980px);
  z-index: 13;
  pointer-events: none;
  transform-origin: 50% 100%;
  /* ⚠️⚠️⚠️ DIE LAGE STEHT HIER, NICHT NUR IN DEN KEYFRAMES. Die
     Animation ist `backwards` — sie gilt VOR ihrem Start und ist nach
     700 ms zu Ende; ohne diese Zeile faellt `transform` dann auf `none`
     zurueck, und die Luke steht ab da mit ihrer LINKEN Kante auf der
     Bildmitte statt mittig darunter. Gemessen auf dem Bild: sie sass um
     ihre halbe Breite nach rechts versetzt.
     ▶ Die Umkehrung der bekannten Falle: sonst schlaegt eine Animation
     die Lage aus dem Blatt — hier gibt es ohne Blatt ueberhaupt keine
     Lage mehr, sobald die Animation durch ist. */
  transform: translateX(-50%);
  animation: luke-kommt 700ms cubic-bezier(.2, .9, .3, 1) backwards;
}
.atom-luke .lk-bild { display: block; width: 100%; height: auto; overflow: visible; }
@keyframes luke-kommt {
  0%   { transform: translateX(-50%) translateY(34px) scale(0.9); opacity: 0; }
  100% { transform: translateX(-50%) translateY(0) scale(1); opacity: 1; }
}

/* ⚠️⚠️⚠️ DER DECKEL DREHT SICH UM SEIN SCHARNIER, und das ist die
   HINTERKANTE. `transform-box: fill-box` macht aus `50% 100%` die
   Unterkante DES DECKELS — ohne diese Zeile rechneten die Prozente
   gegen die ganze viewBox, und er klappte um einen Punkt irgendwo im
   Bild. Das ist die Falle, die auf dieser Seite schon viermal
   zugeschnappt ist; hier steht sie mit ihrem Gegenmittel. */
.atom-luke .lk-deckel {
  transform-box: fill-box;
  transform-origin: 50% 100%;
  animation: luke-klappt 820ms cubic-bezier(.25, 1.25, .4, 1) 120ms backwards;
}
@keyframes luke-klappt {
  0%   { transform: rotateX(0deg) translateY(0); }
  100% { transform: translateY(-34px) scaleY(0.18) skewX(-6deg); }
}
/* aus dem offenen Loch faellt ein Rest Licht nach oben — ohne ihn ist
   das Loch ein Aufkleber und kein Raum */
.atom-luke .lk-loch { animation: luke-glimmt 900ms ease-out 260ms backwards; }
@keyframes luke-glimmt {
  0%   { opacity: 0; }
  100% { opacity: 1; }
}

/* ---- DER SPRUNG HINEIN ----
   ⚠️⚠️ DREI SACHEN ZUGLEICH, und alle drei muessen sein: das BILD sackt
   und kippt (man laesst sich fallen), die LUKE waechst heran (man kommt
   ihr naeher) und das DUNKEL schliesst sich (man ist drin). Faellt eins
   davon aus, liest man es als Bildfehler — eine Landschaft, die wegzoomt,
   ist ein Schnitt; eine Luke, die aufwaechst, ist ein Aufkleber; und
   Dunkel allein ist ein Stromausfall. */
:root.atom-sprung .natur {
  transform-origin: 50% 96%;
  animation: sprung-bild 800ms cubic-bezier(.5, 0, .75, .4) forwards;
}
@keyframes sprung-bild {
  0%   { transform: skewX(-0.9deg) scale(1.03) translateY(0) rotate(0deg); }
  100% { transform: scale(2.3) translateY(-24%) rotate(2.4deg); }
}
/* ⚠️⚠️⚠️ MAN FAELLT IN DAS LOCH — UND BIS A.1.50.0 FUHR MAN DARAN
   VORBEI (A.1.51.0, sein Befund „und der Einstig waehrend sie auf die
   Zurollt"). Die Luke wuchs um ihre UNTERKANTE (`transform-origin:
   50% 100%`) und wanderte dabei nach unten: das Loch lief also nach
   unten aus dem Bild heraus, waehrend der DECKEL und die LEITER gross
   wurden. Angesehen am 22.09. bei 4900 ms: man sah ein riesiges Rad und
   eine riesige Leiter — und das Loch nie.
   ▶ Der Drehpunkt gehoert auf das LOCH, nicht auf den Rand der
   Zeichnung. In der viewBox (`0 128 420 120`) liegt es zwischen y 196
   und 228, seine Mitte also bei 70 % der Bildhoehe. Um DIESEN Punkt
   waechst sie jetzt, und er wandert nach oben ins Blickzentrum: das
   Loch kommt einem entgegen und verschluckt das Bild.
   ⚠️ `scale(11)` ist gegen die Zeichnung gerechnet, nicht geraten: das
   Loch ist 64 % der Bildbreite (980 px bei 1920) = 627 px; elffach sind
   6900 px, also sicher breiter als jeder Schirm. Wer den Wert senkt,
   laesst einen Rand stehen — und ein Loch mit Rand ist wieder ein
   Aufkleber. */
:root.atom-sprung .atom-luke {
  transform-origin: 50% 70%;
  animation: sprung-luke 800ms cubic-bezier(.5, 0, .75, .4) forwards;
}
@keyframes sprung-luke {
  0%   { transform: translateX(-50%) translateY(0) scale(1); opacity: 1; }
  60%  { transform: translateX(-50%) translateY(-16vh) scale(4.2); opacity: 1; }
  100% { transform: translateX(-50%) translateY(-30vh) scale(11); opacity: 1; }
}
/* das Dunkel schlaegt zusammen — von den Raendern her, nicht als Blende:
   eine Luke, die zufaellt, macht es dunkel, indem sie den Himmel
   wegnimmt */
:root.atom-sprung body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 30;
  pointer-events: none;
  /* ⚠️ Der offene Punkt des Verlaufs liegt dort, wo das LOCH beim
     Sprung steht (62 % der Hoehe, siehe `sprung-luke`) — nicht mehr bei
     88 %, wo die Luke VOR dem Sprung lag. Das Dunkel schliesst sich um
     die Stelle, in die man faellt; um einen anderen Punkt herum waere es
     eine Blende. */
  background: radial-gradient(ellipse 120% 90% at 50% 62%,
    rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 26%, #050706 64%);
  animation: sprung-dunkel 900ms cubic-bezier(.4, 0, .6, 1) forwards;
}
@keyframes sprung-dunkel {
  0%   { opacity: 0; transform: scale(1.9); }
  62%  { opacity: 1; transform: scale(1.1); }
  100% { opacity: 1; transform: scale(1); }
}

/* ---- DRAUSSEN DONNERT ES UEBER EINEN HINWEG ----
   ⚠️ Der Bunker ist ab dem Zuschlagen da, aber er ist DUNKEL: das
   Grollen muss aus einem Raum kommen, in dem man schon steht. Wer den
   Raum erst mit dem Licht aufbaut, laesst den Donner im Nichts
   stattfinden. */
/* ⚠️⚠️ DAS DUNKEL IST DIE EINZIGE STELLE, DIE ES DUNKEL MACHT. Ein
   zweiter Griff daneben (etwa ein `brightness`-Filter auf dem Bunker)
   waere eine zweite Wahrheit ueber dieselbe Sache — und beim naechsten
   Umbau blendet der eine auf, waehrend der andere noch zu ist.
   ⚠️ Das Rieseln und das Beben laufen HINTER dem Dunkel weiter: man
   hoert und spuert, dass draussen etwas ueber einen hinweggeht, und
   sieht es erst, wenn das Licht angeht — genau in der Reihenfolge, in
   der man es im Bunker erlebte. */
:root.bunker-licht body::after {
  animation: bunker-auf 1.6s cubic-bezier(.3, .7, .4, 1) forwards;
}
@keyframes bunker-auf {
  0%   { opacity: 1; }
  100% { opacity: 0; }
}

/* ---- DER PILZ ----
   ⚠️ Er steigt UND waechst zugleich, und er wird dabei langsamer: eine
   Wolke, die gleichmaessig steigt, ist ein Ballon.
   ⚠️⚠️ SEIN DREHPUNKT STEHT NICHT MEHR HIER (A.1.49.0). Er lag auf
   festen 800px/380px — der Bildmitte, als die Bombe noch keinen Ort
   hatte. Seit sie hinten einschlaegt und die Stelle mit dem Fenster
   wandert, setzt `atom.js` ihn inline. Eine Zahl, die sich aendert,
   gehoert nicht ins Blatt. */
.n-pilz {
  transform-box: view-box;
  animation: pilz-steigt 4.6s cubic-bezier(.12, .75, .3, 1) forwards;
}
@keyframes pilz-steigt {
  0%   { transform: translateY(300px) scale(0.12); opacity: 0.2; }
  14%  { transform: translateY(180px) scale(0.42); opacity: 0.95; }
  55%  { transform: translateY(40px) scale(0.88); opacity: 0.95; }
  100% { transform: translateY(-40px) scale(1.12); opacity: 0.88; }
}
.n-pilz .n-glut { animation: pilz-glut 3.4s ease-out forwards; }
@keyframes pilz-glut {
  0%   { opacity: 1; r: 70; }
  40%  { opacity: 0.6; }
  100% { opacity: 0; r: 130; }
}

/* =====================================================================
   KAPITEL 4 — DER SCHUTZBUNKER
   ===================================================================== */

/* ⚠️⚠️ Er liegt auf Ebene 1 — UEBER der Wiese (die liegt seit
   `ist-draussen` auf 1, darunter auf −2), UNTER den Pflichtseiten (10)
   und unter allem, was in der Hand ist. Impressum und Datenschutz
   bleiben anklickbar, auch hier unten. */
.bunker {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
  pointer-events: none;
  display: block;
}
:root.ist-im-bunker .natur { display: none; }
:root.ist-im-bunker body { background: #14181a; }

/* die Lampe schwingt, und ihr Schein schwingt MIT (beide in derselben
   Gruppe — ein stehender Schein unter einer schwingenden Lampe ist der
   Fehler, den man nicht benennen kann, aber sieht) */
.b-lampe {
  transform-box: view-box;
  animation: lampe-schwingt 6.4s ease-in-out infinite;
}
@keyframes lampe-schwingt {
  0%, 100% { transform: rotate(-1.7deg); }
  50%      { transform: rotate(1.7deg); }
}
/* ⚠️ Das Flackern ist UNREGELMAESSIG — ein Takt ist eine Lichtorgel.
   Die Stufen stehen dicht beieinander und springen verschieden weit. */
.b-birne, .b-schein { animation: lampe-flackert 7.3s steps(1, end) infinite; }
@keyframes lampe-flackert {
  0%, 38%, 41%, 63%, 66%, 79%, 100% { opacity: 1; }
  39%, 64.5% { opacity: 0.35; }
  40%, 80%   { opacity: 0.75; }
  79.5%      { opacity: 0.2; }
}

/* draussen glueht es noch — sehr langsam, sehr leise */
.b-draussen { animation: glut-atmet 9s ease-in-out infinite; }
@keyframes glut-atmet {
  0%, 100% { opacity: 0.78; }
  50%      { opacity: 1; }
}

/* der Staub rieselt */
.b-staub circle {
  animation: staub-faellt var(--dauer, 12s) linear var(--warte, 0s) infinite;
}
@keyframes staub-faellt {
  0%   { transform: translate(0, 0); opacity: 0; }
  10%  { opacity: 0.5; }
  90%  { opacity: 0.3; }
  100% { transform: translate(var(--seit, 0px), 620px); opacity: 0; }
}

/* ⚠️ Der Bunker faehrt NICHT auf: er ist da, und das Weiss geht weg
   (aufgedeckt, nicht eingeblendet — die Regel der ganzen Seite). */

@media (prefers-reduced-motion: reduce) {
  .n-pilz { animation-duration: 1.2s; animation-timing-function: linear; }
  .b-lampe { animation: none; }
  .b-birne, .b-schein { animation: none; }
  .b-draussen { animation: none; }
  .b-staub { display: none; }
  :root.atom-verbrannt .natur { transition: none; }
  :root.atom-leuchtet .natur, :root.atom-verbrannt.atom-leuchtet .natur { transition: none; }

  /* ⚠️⚠️⚠️ DIE EREIGNISSE BLEIBEN, DIE KAMERA GEHT. Wer hier alles
     abschaltet, dem passiert nach dem roten Knopf gar nichts und er
     steht ploetzlich im Bunker — das waere kein ruhigeres Ende, sondern
     gar keins. Also: der Einschlag, der Feuerball, der Pilz, die Front
     und die Luke bleiben ALLE sichtbar, nur gerade und kurz. Was
     wegfaellt, ist das, was den KOPF bewegt: der Sturz durch die Luke,
     das Kippen des Bildes und das Schaukeln des Schirms.
     ⚠️ Das Dunkel bleibt — es ist die AUSKUNFT „die Luke ist zu", nicht
     die Zierde. Es kommt nur ohne Zoom. */
  .atom-bombe { animation-duration: 420ms; animation-timing-function: linear; }
  .atom-feuerball { animation-duration: 1.6s; animation-timing-function: linear; }
  .atom-kragen { animation-duration: 1.2s; animation-timing-function: linear; }
  .atom-schock, .atom-front { animation-timing-function: linear; }
  :root.atom-welle .natur { transform: none; transition: none; }
  .atom-luke { animation: none; }
  .atom-luke .lk-deckel { animation-duration: 400ms; animation-timing-function: linear; }
  :root.atom-sprung .natur { animation: none; }
  :root.atom-sprung .atom-luke { animation: sprung-luke-still 500ms linear forwards; }
  @keyframes sprung-luke-still { 0% { opacity: 1; } 100% { opacity: 0; } }
  :root.atom-sprung body::after {
    animation: sprung-dunkel-still 500ms linear forwards;
  }
  @keyframes sprung-dunkel-still {
    0%   { opacity: 0; transform: none; }
    100% { opacity: 1; transform: none; }
  }
  .n-airdrop .ad-kiste { animation: none; }
  .ad-pendel { animation: none; }
  /* ⚠️ Das SINKEN bleibt: ohne es faellt der Abwurf nicht vom Himmel,
     sondern erscheint — und „Airdrop" waere ein Wort ohne Bild. Es geht
     nur gerade und schneller. */
  .ad-sinkt { animation-duration: 4s; }
}

/* =====================================================================
   DAS GRABEN — Kapitel 4, seine Ansage vom 22.09.: „Man beginnt im
   ersten Schritt mit Graben"

   ⚠️⚠️ DREI ZUSTAENDE, DREI BILDER: die Stelle liegt da (nichts) · sie
   ist unter dem Zeiger und die Hacke ist in der Hand (`bereit`) · sie
   ist durch (`durch`). Ein vierter, `schlaegt`, dauert 180 ms und ist
   die Antwort auf den Handgriff.
   ===================================================================== */

/* ⚠️⚠️⚠️ OHNE DIESE ZEILE IST DIE GANZE STELLE ZU SEHEN UND NICHT
   ANZUFASSEN. `.bunker` traegt `pointer-events: none` — ein SVG faengt
   Zeiger nur dort, wo ein Element sie ausdruecklich annimmt. Genau die
   Falle, an der 98 von 134 Zielen auf der Wiese haengengeblieben sind
   (A.1.48.0): sie trugen ihre Klasse, leuchteten nie und nahmen nie
   einen Schuss an. */
.b-bodenfeld, .b-hackenfeld { pointer-events: all; }
:root.hacke-in-der-hand .b-bodenfeld { cursor: none; }
.b-hackenfeld { cursor: grab; }

/* ---- DIE STELLE UNTER DEM ZEIGER ----
   ⚠️⚠️⚠️ SIE IST DIE EINZIGE ANKUENDIGUNG, DIE ES NOCH GIBT (A.1.53.0).
   Solange drei Grabstellen vorgezeichnet waren, sagte das BILD, wo man
   graben kann. Seit der Boden ueberall nachgibt (seine Ansage: „Ich
   möchte gerne an einer beliebigen Stelle auf dem Boden graben
   können"), sagt es nichts mehr — und eine Flaeche, die auf jeden Klick
   reagiert, ohne vorher etwas zu versprechen, ist von einer toten
   Flaeche nicht zu unterscheiden (A.1.10.0).
   ⚠️ KEINE Wanderungsanimation auf `cx`/`cy`: der Fleck sitzt unter dem
   Zeiger, und ein Fleck, der dem Zeiger hinterherlaeuft, ist ein
   zweites Ding im Bild. Nur das Auftauchen hat eine Zeit. */
.b-marke {
  opacity: 0;
  pointer-events: none;
  transition: opacity 130ms ease;
}
.b-marke.an { opacity: 1; }
/* ⚠️ Ueber einem angefangenen Loch heller: dort geht es weiter, und das
   ist eine andere Aussage als „hier koenntest du anfangen". */
.b-marke.weiter { stroke: rgba(255, 246, 220, 0.8); stroke-width: 4; }

/* ---- DAS LOCH ----
   ⚠️⚠️ ZWEI GRUPPEN, ZWEI AUFGABEN: `.b-grube` ruettelt (Animation auf
   `transform`), `.b-loch` waechst (Inline-`transform`, von `graben.js`
   bei jedem Schlag neu geschrieben). An EINEM Element wuerde die
   Animation den Massstab 180 ms lang mitnehmen, und das Loch spraenge
   bei jedem Schlag auf Null — dieselbe Falle wie beim Rueckstoss des
   Bolzenschussgeraets (A.1.25.0).
   ⚠️⚠️ DER DREHPUNKT STEHT INLINE IN `graben.js`, NICHT HIER. `.b-loch`
   liegt in einem SVG mit eigener viewBox — Prozente rechneten gegen die
   ganze Box und nicht gegen das Loch. Die Falle ist auf dieser Seite
   schon viermal zugeschnappt (`transform-box: view-box`). */
.b-loch {
  transform: scale(0);
  transition: transform 260ms cubic-bezier(.2, .9, .3, 1);
}
/* der Schlag: die Grube zuckt, kurz und hart */
.b-grube.schlaegt { animation: grab-schlag 180ms cubic-bezier(.3, .8, .4, 1); }
@keyframes grab-schlag {
  0%   { transform: translate(0, 0); }
  35%  { transform: translate(1px, 3px); }
  100% { transform: translate(0, 0); }
}
/* ⚠️⚠️ DURCH HEISST: DER GRUND DER GRUBE IST WEG. Es wird nichts
   eingeblendet — das Dunkel lag die ganze Zeit darunter und war
   zugedeckt (Regel: alles wird AUFGEDECKT). Der Grund schrumpft auf
   seinen eigenen Mittelpunkt zu, also bricht er nach INNEN weg. */
.b-grube .b-grund { transition: transform 320ms cubic-bezier(.5, 0, .75, .4); }
.b-grube.durch .b-grund { transform: scale(0); }

/* die Hacke in der Hand: sie holt beim Schlag aus und faehrt herunter.
   ⚠️ Ueber `rotate`, NICHT ueber `transform`: im transform steht die
   Lage, die die Physik in jedem Bild neu schreibt (dieselbe Falle wie
   beim Rueckstoss des Bolzenschussgeraets, A.1.25.0). */
.hacke-vorlage { display: none; }
/* ⚠️⚠️ BREITE UND HOEHE SIND DAS SEITENVERHAELTNIS DER `viewBox`, und
   zwar genau (176 zu 230 = 0,765). Ein SVG mit falschem Verhaeltnis
   dehnt sich nicht — es LAESST LUFT (`preserveAspectRatio: meet`), und
   die Zeichnung schrumpft in die Mitte ihres Kastens. Da die Spitze
   ueber `spitze` in Anteilen DES KASTENS unter den Zeiger gesetzt wird,
   zielt man dann mit einer Stelle daneben, die es im Bild nicht gibt —
   dieselbe Falle wie die 41 px von A.1.8.0, nur eine Ebene tiefer. */
/* ⚠️⚠️⚠️ SIE MISST SICH AM FENSTER, NICHT AN DER PLATTE — und das ist
   der Unterschied zwischen einem Werkzeug und einem Anhaenger. Jedes
   andere Werkzeug dieser Seite haengt an `--platte-b`, weil es in der
   MASCHINE liegt und mit ihr wachsen muss. Die Hacke liegt im KELLER, und
   die Platte gibt es dort nicht mehr: gemessen am 22.09. stand
   `--platte-b` im Bunker noch auf ihrem letzten Wert (1057 px), und die
   Hacke war damit **89 px hoch** — neben einer gezeichneten Hacke von
   299 px an der Wand. Man hob eine Spitzhacke auf und hielt einen Pinsel.
   ▶ Der Keller fuellt das Fenster (`slice`), also misst sich, was in ihm
   liegt, am Fenster. `vmin` und nicht `vh`: auf einem flachen breiten
   Schirm waere `vh` winzig. */
/* ⚠️ Breite und Hoehe sind das Seitenverhaeltnis der viewBox, GENAU
   (A.1.53.1: 168 zu 300 = 0,56; davor 176 zu 230). Stimmt es nicht,
   dehnt sich ein SVG nicht — es LAESST LUFT, die Zeichnung schrumpft in
   die Mitte ihres Kastens, und weil die `spitze` in Anteilen DES
   KASTENS unter den Zeiger gesetzt wird, zielt man mit einer Stelle
   daneben, die es im Bild nicht gibt. */
.hacke-ding {
  position: absolute;
  width: 14vmin;
  height: 25vmin;
  filter: drop-shadow(3px 5px 6px rgba(0, 0, 0, 0.75));
  cursor: grab;
}
/* ⚠️⚠️⚠️ KEIN `pointer-events: auto` HIER — und genau das stand im
   ersten Wurf, aus dem CSS der KURBEL kopiert. Die Kurbel ist kein
   Werkzeug: sie wird getragen und muss anfassbar bleiben. Ein WERKZEUG
   haengt mit seiner Spitze genau unter dem Zeiger, und wenn es Zeiger
   faengt, trifft jeder Schlag das Werkzeug selbst. Gemessen am 22.09.:
   `elementFromPoint` ueber der Grabstelle meldete `svg.hacke-ding`, und
   von zwoelf Schlaegen kam KEINER an — ohne eine Zeile in der Konsole.
   ▶ Die allgemeine Regel `.gegenstand.in-der-hand { pointer-events:
   none }` erledigt es; sie darf nur nicht ueberschrieben werden.
   ⚠️ Dieselbe Falle steht seit A.1.25.0 in der CLAUDE.md („Was in der
   Hand liegt, darf keine Klicks abfangen") — und ist trotzdem wieder
   zugeschnappt, weil kopiertes CSS keinen Kommentar mitbringt. */
.hacke-ding.gegenstand { inset: auto; margin: 0; }
.hacke-ding.schlaegt { animation: hacke-schlag 180ms cubic-bezier(.25, .9, .35, 1); }
@keyframes hacke-schlag {
  0%   { rotate: 0deg; }
  30%  { rotate: -17deg; }
  100% { rotate: 0deg; }
}

/* ---- DIE ERDBROCKEN ----
   ⚠️ Die Farbe steht HIER und nicht in `graben.js` — dieselbe Regel wie
   bei Blut, Laub, Fetzen und Holzsplittern: der Aufrufer sagt, aus
   welchem STOFF die Splitter sind, das Blatt sagt, wie er aussieht. */
.spaene i.erde {
  border-radius: 2px;
  background-image: linear-gradient(112deg, #6b573a 0%, #4a3a26 42%, #2e2416 100%);
  box-shadow: 0 0 1px rgba(14, 10, 4, 0.85);
}

@media (prefers-reduced-motion: reduce) {
  .b-grube.schlaegt { animation: none; }
  .hacke-ding.schlaegt { animation: none; }
  .b-loch { transition-duration: 60ms; }
  .b-grube .b-grund { transition-duration: 80ms; }
}

/* =====================================================================
   A.1.54.0 — DIE TIEFE, DIE ZEIT UND DER SCHLAG

   Seine vier Ansagen vom 22.09.:
     „Ausserdem fuehlt sich das Hacken nicht gut an."
     „Es muss nun Stueck fuer Stueck nach unten gehen. Dabei muss der
      Spieler die Moeglichkeit haben immer wieder nach oben zu kommen."
     „Nach dem ersten Schlag muessen bitte Oben Rechts 2 Balken
      Auftauchen. 1. Hunger 2. Muedigkeit"
     „Anzeige ‚Tage im Bunker:' Startet bei 0 …"
   ===================================================================== */

/* ---- DIE KAMERA ----
   ⚠️⚠️⚠️ SIE SITZT AUF `.b-welt`, DAS BEBEN AUF `.b-beben`. Zwei
   Gruppen, zwei Aufgaben — stuenden beide am selben Element, spraenge
   die Kamera bei jedem Schlag auf Ebene 0 zurueck (dieselbe Falle wie
   beim Loch in A.1.52.0). `ruettel()` schreibt ausserdem `translate`
   und nicht `transform`, also vertragen sich die beiden auch dann,
   wenn sie einmal doch zusammenkommen sollten.
   ⚠️ 1100 ms stehen auch in `tiefe.js` (`FAHRT`) — sie sind ein PAAR.
   Wer hier dreht und dort nicht, laesst den Boden frueher Schlaege
   annehmen, als das Bild steht. */
.b-welt {
  transform: translateY(calc(var(--ebene, 0) * -900px));
  transition: transform 1100ms cubic-bezier(.45, .02, .3, 1);
}
:root.faehrt .b-bodenfeld,
:root.faehrt .b-mund { pointer-events: none; }
:root.faehrt .b-marke { opacity: 0; }

/* ---- DER WEG HINUNTER UND DER WEG ZURUECK ----
   ⚠️ Kein Knopf, kein Pfeil, kein Wort: der Weg IST das Loch. Unter dem
   Zeiger bekommt es einen Ring — dieselbe Sprache wie der Fleck auf dem
   Boden (`.b-marke`), damit man nicht zweimal lernen muss, was
   „hier kann man etwas tun" heisst. */
.b-mund {
  pointer-events: all;
  cursor: pointer;
  stroke: rgba(255, 232, 178, 0);
  stroke-width: 4;
  transition: stroke 150ms ease, stroke-width 150ms ease;
}
.b-mund:hover { stroke: rgba(255, 232, 178, .58); }
/* ⚠️⚠️ WENN DER SCHLAG ABGEWIESEN WIRD, RUFT DER WEG NACH OBEN. Eine
   Abweisung muss eine begehbare Tuer nennen — hier ist sie das Loch in
   der Decke, durch das man gekommen ist. */
:root.zeig-hoch .b-mund.b-hoch {
  animation: mund-ruft 800ms ease-in-out 3;
}
@keyframes mund-ruft {
  0%, 100% { stroke: rgba(255, 232, 178, .25); stroke-width: 4; }
  50%      { stroke: rgba(255, 244, 205, .95); stroke-width: 9; }
}

/* ---- DER STAUB EINES SCHLAGS ----
   ⚠️⚠️ EINE FLAECHE, KEINE PUNKTE (die Lehre der Blutwolke, A.1.49.0):
   einzelne Teilchen liest das Auge als Konfetti, erst eine Wolke liest
   sich als Einschlag.
   ⚠️⚠️⚠️ `transform-box: fill-box` — ohne das rechnet `scale` gegen die
   ganze viewBox und die Wolke waechst quer durch den Keller. Die Falle
   steht seit A.1.46.0 in der CLAUDE.md, und sie schnappt bei jeder
   neuen Form wieder zu. */
.b-puff {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: b-puff 640ms cubic-bezier(.16, .7, .3, 1) forwards;
  pointer-events: none;
}
@keyframes b-puff {
  0%   { opacity: .58; scale: .3 .5; }
  30%  { opacity: .46; }
  100% { opacity: 0;   scale: 1.6 1.25; }
}

/* ---- DIE HACKE SCHLAEGT WIRKLICH ZU ----
   ⚠️⚠️⚠️ SEIN BEFUND „das Hacken fuehlt sich nicht gut an" steckt zur
   Haelfte hier. Bis A.1.53.1 war der Schlag EINE Drehung um 17 Grad in
   180 ms: ein Zucken. Ein Schlag hat DREI Teile, und man braucht alle
   drei — AUSHOLEN (jetzt passiert gleich etwas), EINSCHLAG (hart und
   schnell, die kuerzeste Strecke), ABPRALL (der Boden gibt etwas
   zurueck). Ohne das Ausholen gibt es keinen Augenblick, in dem etwas
   bevorsteht; ohne den Abprall schlaegt man in Watte.
   ⚠️⚠️ ES LAEUFT UEBER `rotate` UND `translate`, NICHT UEBER
   `transform` — dort steht die Lage, die `physik.js` in JEDEM Bild neu
   schreibt (die Falle von A.1.43.1). Beides sind eigene Eigenschaften;
   eine Animation darauf kaempft mit niemandem.
   ⚠️ 260 ms sind derselbe Wert wie `TAKT` in `graben.js`: der naechste
   Schlag kann erst kommen, wenn dieser zu Ende ist. Sie sind ein PAAR. */
.hacke-ding.schlaegt { animation: hacke-schlag 260ms cubic-bezier(.26, .72, .36, 1); }
@keyframes hacke-schlag {
  0%   { rotate: 0deg;   translate: 0 0; }
  28%  { rotate: 24deg;  translate: .5vmin -1.5vmin; }
  54%  { rotate: -32deg; translate: -.7vmin 1.3vmin; }
  64%  { rotate: -25deg; translate: -.5vmin .8vmin; }
  100% { rotate: 0deg;   translate: 0 0; }
}
/* ⚠️ Wer nicht mehr kann, schlaegt nicht daneben — die Hacke sackt.
   Das ist die Antwort auf einen Schlag, der nicht kommt. */
.hacke-ding.zu-schwer { animation: hacke-schwer 700ms cubic-bezier(.3, .1, .3, 1); }
@keyframes hacke-schwer {
  0%   { rotate: 0deg; translate: 0 0; }
  40%  { rotate: 7deg; translate: 0 1.1vmin; }
  100% { rotate: 0deg; translate: 0 0; }
}

/* ---- DER VORRAT UND DAS BETT: die zwei Handgriffe oben ---- */
.b-dosenfeld, .b-bettfeld { pointer-events: all; }
.b-dose, .b-bett {
  transform-box: fill-box;
  transform-origin: 50% 100%;
  transition: opacity 240ms ease, scale 240ms ease, filter 180ms ease;
}
:root.will-essen .b-dose { cursor: pointer; }
:root.will-essen .b-dose:hover { filter: brightness(1.55); }
:root.will-schlafen .b-bett { cursor: pointer; }
:root.will-schlafen .b-bett:hover { filter: brightness(1.35); }
/* ⚠️⚠️⚠️ EINE DOSE, NICHT DREIUNDDREISSIG. Der erste Wurf liess den
   ganzen Vorrat atmen, sobald der Hunger voll war — und das war zweimal
   falsch: **gemessen kostete es 16 % der Bildrate** (33 Gruppen mit
   einem Filter, 38 auf 32 Bilder je Sekunde im kopflosen Chrome), und
   auf dem Bild ist ein Regal, in dem alles blinkt, ein ALARM und kein
   Hinweis. ▶ Genau EINE Dose bekommt einen ruhigen warmen Rand — das
   liest sich als „nimm die", und es kostet nichts, weil nichts
   animiert wird. Welche es ist, sagt `bunker.js`. */
.b-dose.dran { filter: brightness(1.38) drop-shadow(0 0 4px rgba(255, 226, 160, .85)); }
:root.ist-muede .b-bett { animation: vorrat-ruft 2.4s ease-in-out infinite; }
@keyframes vorrat-ruft { 0%, 100% { filter: brightness(1); } 50% { filter: brightness(1.28); } }
.b-dose.leer { opacity: 0; scale: .62; }
.b-dose.stoesst, .b-bett.stoesst { animation: dose-stoesst 240ms ease-out; }
@keyframes dose-stoesst {
  0%, 100% { translate: 0 0; }
  35%      { translate: 0 -3px; }
  70%      { translate: 0 1px; }
}

/* ---- DIE NACHT ----
   ⚠️⚠️ ACHT STUNDEN SIND KEIN AUGENBLICK. 2,6 s sind der laengste
   Uebergang dieser Seite, und das ist Absicht: eine Nacht, die in
   200 ms vorbei ist, ist ein Blinzeln. Die Zahl steht auch in
   `bunker.js` — sie sind ein PAAR.
   ⚠️ Ebene 9: ueber dem Keller und ueber der Anzeige, aber UNTER den
   Pflichtseiten (10). Impressum und Datenschutz muessen in jedem
   Zustand anklickbar bleiben, auch nachts. */
.b-nacht {
  position: fixed;
  inset: 0;
  z-index: 9;
  background: #04060a;
  opacity: 0;
  pointer-events: none;
}
:root.schlaeft .b-nacht { animation: nacht 2600ms ease-in-out forwards; }
@keyframes nacht {
  0%   { opacity: 0; }
  16%  { opacity: 1; }
  74%  { opacity: 1; }
  100% { opacity: 0; }
}

/* ---- DIE ANZEIGE OBEN RECHTS ----
   Seine Ansage: zwei Balken und „Tage im Bunker:", nach dem ersten
   Schlag.
   ⚠️⚠️ HIER STEHEN WORTE, und das ist eine Ausnahme von Regel 6 („kein
   Wort auf der Seite"). Sie ist SEINE: er hat den Wortlaut genannt.
   ⚠️ Sie faengt keine Zeiger — eine Anzeige ueber einer Bedienstelle
   nimmt sie weg (die Lehre vom 22.09., A.1.48.0, als die Waffenanzeige
   den Busch zugedeckt hat). Oben rechts ist im Keller nichts, aber das
   war beim Busch auch mal so.
   ⚠️⚠️ SIE WIRD AUFGEDECKT, NICHT EINGEBLENDET (Regel der Seite): der
   Vorhang faehrt von oben auf. Eine Deckkraft von 0 auf 1 waere ein
   Einblenden, und davon gibt es hier keines. */
.bunkeruhr {
  position: fixed;
  top: 2.2vmin;
  right: 2.2vmin;
  z-index: 8;
  pointer-events: none;
  min-width: 25vmin;
  padding: 1.5vmin 1.8vmin 1.7vmin;
  background: linear-gradient(160deg, rgba(31, 36, 34, .93), rgba(17, 20, 19, .95));
  border: 1px solid rgba(255, 232, 178, .17);
  border-radius: 3px;
  box-shadow: 0 1.4vmin 3vmin rgba(0, 0, 0, .55), inset 0 1px 0 rgba(255, 255, 255, .06);
  font-family: "Helvetica Neue", Arial, sans-serif;
  color: #e7dcc4;
  clip-path: inset(0 0 100% 0);
}
.bunkeruhr.an {
  clip-path: inset(0 0 0 0);
  transition: clip-path 320ms cubic-bezier(.2, .85, .3, 1);
}
.bu-tage { display: flex; align-items: baseline; justify-content: space-between; gap: 1.2vmin; }
.bu-wort {
  font-size: 1.28vmin;
  letter-spacing: .22em;
  color: rgba(231, 220, 196, .62);
  text-transform: uppercase;
}
.bu-zahl {
  font-size: 3.4vmin;
  font-weight: 800;
  line-height: 1;
  color: #ffe9b4;
  font-variant-numeric: tabular-nums;
}
/* ⚠️ Die Uhrzeit ist MEINE Zugabe, und sie ist eine Zeile: ohne sie
   sind „ein halber Tag" und „16 Stunden" zwei Balken ohne Mass. */
.bu-uhr {
  margin-top: .3vmin;
  font-size: 1.5vmin;
  letter-spacing: .16em;
  color: rgba(231, 220, 196, .5);
  font-variant-numeric: tabular-nums;
}
.bu-balken { margin-top: 1.25vmin; }
.bu-name {
  display: block;
  font-size: 1.14vmin;
  letter-spacing: .2em;
  color: rgba(231, 220, 196, .62);
  margin-bottom: .45vmin;
}
.bu-bahn {
  display: block;
  height: 1.1vmin;
  background: rgba(0, 0, 0, .55);
  border: 1px solid rgba(255, 232, 178, .14);
  overflow: hidden;
}
/* ⚠️⚠️ DER BALKEN WAECHST, ER LEERT SICH NICHT. Er heisst HUNGER, nicht
   VORRAT — ein Balken, der das Gegenteil seines Namens zeigt, ist die
   sicherste Art, jemanden verhungern zu lassen. */
.bu-bahn > i {
  display: block;
  height: 100%;
  width: calc(var(--voll, 0) * 100%);
  background: linear-gradient(90deg, #7d8a52, #b3a24a);
  transition: width 200ms linear, background 400ms ease;
}
.bu-balken.warnt .bu-bahn > i { background: linear-gradient(90deg, #b3a24a, #c8762a); }
.bu-balken.leer .bu-bahn > i { background: linear-gradient(90deg, #c8762a, #b5342a); }
.bu-balken.leer .bu-name { color: #e4a05a; }
.bu-balken.pocht { animation: bu-pocht 620ms ease-out 2; }
@keyframes bu-pocht {
  0%, 100% { transform: translateX(0); }
  20%      { transform: translateX(-.7vmin); }
  55%      { transform: translateX(.5vmin); }
}

/* ⚠️ Wer keine Bewegung will, bekommt die Auskunft trotzdem — nur ohne
   Schwung. Was hier ausfaellt, ist nie das, was man wissen muss. */
@media (prefers-reduced-motion: reduce) {
  .b-welt { transition-duration: 1ms; }
  .b-puff { animation-duration: 1ms; }
  .hacke-ding.schlaegt, .hacke-ding.zu-schwer { animation: none; }
  :root.ist-muede .b-bett { animation: none; }
  .b-dose.stoesst, .b-bett.stoesst, .bu-balken.pocht { animation: none; }
  :root.zeig-hoch .b-mund.b-hoch { animation: none; stroke: rgba(255, 244, 205, .9); }
  .bunkeruhr.an { transition: none; }
  :root.schlaeft .b-nacht { animation: nacht 900ms linear forwards; }
}
