/* CozyWhiskers – warme, entspannte Grundstimmung. Mobile-first. */

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* Alles Farbige läuft über Variablen, weil die Tageszeit sie umschaltet
   (siehe „Tageszeiten“ weiter unten). Die Werte hier sind der Tag. */
:root {
  --bg-top: #FDF6EA;
  --bg-bottom: #F6E3C4;
  --card: rgba(255, 255, 255, 0.72);
  --card-strong: rgba(255, 255, 255, 0.82);
  --sheet-top: #FFFCF5;
  --sheet-bottom: #F9EDD9;
  --text: #4A3728;
  --text-soft: #8A7460;
  --hunger: #E8963E;
  --happiness: #E77B9D;
  --energy: #E9B824;
  /* Wissen bekommt eine eigene, ruhigere Farbe – es gehört nicht zu den
     Pflegewerten der Katze, sondern zum Menschen davor. */
  --know: #7FA88B;
  --know-ink: #3F5A48;     /* Schrift auf grünem Grund */
  --wrong: #C9705C;

  /* Die Szene hinter der Katze. Die Wandfarbe überbrückt nur die Zeit,
     bis das Zimmerbild geladen ist – gemalt ist alles andere. */
  --wall-top: #F9EDD8;
  --wall-bottom: #F3DFC0;
  --mote: rgba(255, 250, 226, 0.95);
  --mote-opacity: 0.5;
  --mote-glow: none;

  /* Seit die Bühne bis an die Bildschirmkanten reicht, liegt sie auch
     unter der Statusleiste und dem Balken am unteren Rand. Alles, was
     dort aufliegt, rechnet diese beiden Werte mit. */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);

  /* Der Knopf zur Spielekiste. Vorher trug er das Pink des Glückswerts –
     das war willkürlich (die Spiele füllen alle drei Werte) und zugleich
     das einzige kräftig gesättigte Feld auf dem Schirm, sodass es alles
     an sich zog. Jetzt ein warmes Holzbraun aus derselben Familie wie
     Fensterrahmen und Dielen: Er führt über Größe und Ort, nicht über
     Lautstärke.

     Leicht durchscheinend, und das ist kein Effekt: Seit die Bühne bis an
     die Kanten reicht, liegt der Knopf über dem Teppich, und auf einem
     kleinen Telefon reichen die Pfoten der Katze dahinter. Undurchsichtig
     schnitte er sie ab; als Glas steht sie dahinter. Dieselbe Sprache wie
     Statuskarte und Spielbedienung. */
  --action-bg: rgba(138, 90, 59, 0.93);
  --action-ink: #FFF6E8;
}

/* ---------- Tageszeiten ----------
   Gesetzt von js/scene.js aus der echten Uhrzeit. Der Tag steht oben in
   :root und braucht deshalb keinen eigenen Block. */

body[data-phase="morning"] {
  --wall-top: #FDECD8;
  --wall-bottom: #F7DEC0;
}

body[data-phase="evening"] {
  --bg-top: #FBEADB;
  --bg-bottom: #F0D2B4;
  --wall-top: #F5DFC6;
  --wall-bottom: #EBCBA8;
}

/* Nachts dunkelt die ganze Oberfläche mit. Ein cremeweißes Bedienfeld
   über einem nächtlichen Zimmer wäre der Bruch, den die App gerade
   vermeiden soll. */
body[data-phase="night"] {
  --bg-top: #2C2A3E;
  --bg-bottom: #413B57;
  --card: rgba(62, 57, 82, 0.68);
  --card-strong: rgba(74, 68, 96, 0.88);
  --sheet-top: #35304A;
  --sheet-bottom: #272338;
  --text: #F6EDE0;
  --text-soft: #C0B2C6;
  --know-ink: #CDE9D6;     /* dunkles Grün wäre nachts nicht zu lesen */
  --wall-top: #453F5C;
  --wall-bottom: #372F4B;
  --mote: #FFE9A8;         /* aus Staub werden Glühwürmchen */
  --mote-opacity: 0.85;
  --mote-glow: 0 0 6px rgba(255, 233, 168, 0.9);
  /* Holzbraun vor einer nächtlichen Wand wird Matsch – nachts ein
     gedämpftes Violett aus derselben Familie wie die Wand. */
  --action-bg: rgba(107, 90, 134, 0.93);
}

html, body {
  height: 100%;
}

body {
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  color: var(--text);
  background: linear-gradient(180deg, var(--bg-top), var(--bg-bottom));
  /* Der Wechsel der Tageszeit soll überblenden, nicht umspringen. */
  transition: background 0.9s ease, color 0.9s ease;
  -webkit-font-smoothing: antialiased;
  user-select: none;
  -webkit-user-select: none;
}

/* Die Ränder liegen in den Ansichten und nicht hier: Zuhause soll die
   Bühne bis an die Kanten reichen, der Designer nicht. */
.app {
  max-width: 430px;
  margin: 0 auto;
  height: 100dvh;
}

/* Zuhause und Designer teilen sich den Platz – es ist immer genau
   eine Ansicht sichtbar (die andere trägt das hidden-Attribut). */
.view {
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: calc(12px + var(--safe-top)) 16px calc(16px + var(--safe-bottom));
}

/* ---------- Zuhause: die Bühne IST der Bildschirm ----------

   Die Kulisse füllt alles, Statuskarte und Spielekiste liegen als
   durchscheinende Plättchen darauf. Vorher waren es drei Platten mit
   Fugen dazwischen – Karte, Bühne, Knopf –, jede mit eigener Rundung und
   eigenem Schatten, und die Bedienung war eine zweite Fläche neben dem
   Bild.

   Das Vorbild steht schon in der App: Während eines Spiels liegt die
   Bedienung längst auf der Szene (`.game-hud`, die Namensplättchen, der
   Zähler). Der Startbildschirm war der einzige Ort, an dem beides
   getrennt war – und genau daher stammt die Regel weiter oben, dass
   nachts die *ganze* Oberfläche mitdunkeln muss. Als eine Fläche stellt
   sich die Frage gar nicht mehr. */
#view-home {
  position: relative;
  padding: 0;
}

#view-home > .stats-card {
  position: relative;
  z-index: 2;
  margin: calc(10px + var(--safe-top)) 12px 0;
}

/* Die untere Karte: Regal und Spielekiste in EINEM Behälter, gebaut wie
   die Statuskarte oben – gleicher Grund, gleiche Rundung, gleicher
   Schatten, gleicher Weichzeichner. Vorher lagen die beiden als lose
   Teile auf der Bühne, während oben eine geschlossene Karte stand; genau
   das war der Bruch zwischen oben und unten.

   `margin-top: auto` schiebt sie an den unteren Rand – die Bühne
   darunter liegt absolut und trägt nichts zum Fluss bei. */
#view-home > .dock {
  position: relative;
  z-index: 2;
  margin: auto 12px calc(12px + var(--safe-bottom));
  display: grid;
  gap: 10px;
  background: var(--card);
  border-radius: 20px;
  padding: 12px;
  box-shadow: 0 4px 20px rgba(120, 84, 40, 0.10);
  backdrop-filter: blur(8px);
}

/* In der Karte braucht der Knopf keinen eigenen Schlagschatten mehr –
   den wirft schon die Karte. */
.dock .action {
  box-shadow: none;
}

.view[hidden] {
  display: none;
}

/* ---------- Statusanzeige ---------- */

.stats-card {
  background: var(--card);
  border-radius: 20px;
  padding: 14px 16px;
  box-shadow: 0 4px 20px rgba(120, 84, 40, 0.10);
  backdrop-filter: blur(8px);
}

.name-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}

.stats-card h1 {
  flex: 1;
  font-size: 1.25rem;
  text-align: center;
  /* Links der Steckbrief, rechts der Designer – gleich breite Knöpfe,
     der Name sitzt von allein mittig. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 44 px, nicht 38: Das ist die übliche Mindestgröße für ein Fingerziel –
   und erst ab dieser Größe ist ein gemaltes Symbol darin mehr als ein
   Fleck. Bei 28 px verliert jede Illustration gegen ein Emoji, das für
   genau diese Größe entworfen wurde. */
.icon-btn {
  position: relative;
  flex: none;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: none;
  border-radius: 50%;
  background: var(--card-strong);
  font-size: 1.15rem;
  font-family: inherit;
  color: var(--text);
  line-height: 1;
  cursor: pointer;
  touch-action: manipulation;
  transition: transform 0.1s ease;
}

/* Gemaltes Symbol statt Emoji. Beide liegen übereinander; lädt das Bild,
   verschwindet das Emoji darunter – lädt es nicht, bleibt die App
   bedienbar (js/main.js, paintButton). */
.btn-icon {
  width: 30px;
  height: 30px;
  object-fit: contain;
  pointer-events: none;
}

.has-icon .btn-emoji {
  display: none;
}

/* In den flachen Knöpfen (Regal, Spielekiste) ist weniger Platz. */
.shelf-btn .btn-icon {
  width: 24px;
  height: 24px;
}

.action .btn-icon {
  width: 28px;
  height: 28px;
  vertical-align: -7px;
  margin-right: 6px;
}

.icon-btn:active {
  transform: scale(0.92);
}

/* Ein runder Knopf mit Wort daneben: aus dem Kreis wird eine Pille.
   Gebraucht wird das dort, wo ein Symbol allein nicht als Knopf gelesen
   wird – „ⓘ" kann auch bloß ein Hinweiszeichen sein, und die Palette
   sagt nicht von selbst „hier gestaltest du deine Katze".

   Unten in der Karte brauchen Tageskarte und Kulisse kein Wort: Sie
   stehen neben der Spielekiste und sind schon durch ihren Ort als
   Bedienung erkennbar. */
.label-btn {
  width: auto;
  height: 40px;
  padding: 0 12px;
  border-radius: 999px;
  display: flex;
  gap: 6px;
  font-size: 0.85rem;
  font-weight: 700;
}

.btn-word {
  line-height: 1;
}

/* ---------- Die drei Werte als Ringe ----------

   Vorher waren es drei liegende Balken mit einer Zahl daneben – eine
   Zeile je Wert, und die Zahlen waren das Auffälligste daran. Jetzt
   stehen sie nebeneinander unter dem Namen: ein Ring, der sich füllt,
   das Symbol in der Mitte, das Wort darunter. „satt" liest sich
   schneller als eine 96 neben einem Balken, und die Kopfzeile wird
   dabei flacher.

   Der Ring ist ein `conic-gradient` – kein Bild, kein SVG. Der
   Füllstand ist damit eine Zahl in einer Custom Property (`--p`), und
   die ganze Anzeige besteht aus einem Element mit einem Loch. */
.vitals {
  display: flex;
  gap: 6px;
}

.vital {
  --p: 0;
  flex: 1;
  display: grid;
  justify-items: center;
  gap: 5px;
}

.vital-dial {
  position: relative;
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  /* Der gefüllte Teil in der Farbe des Werts, der Rest als blasse Spur –
     so sieht man auch, wie viel fehlt. */
  background: conic-gradient(
    var(--tint) calc(var(--p) * 1%),
    color-mix(in srgb, var(--tint) 20%, transparent) 0
  );
}

/* Das Loch in der Mitte macht aus der Scheibe einen Ring. */
.vital-dial::before {
  content: "";
  position: absolute;
  inset: 5px;
  border-radius: 50%;
  background: var(--card);
}

.vital-icon {
  position: relative;   /* über dem Loch */
  font-size: 1.25rem;
  line-height: 1;
}

/* Das gemalte Symbol über dem Emoji – 28 statt 24 Punkte.
   Dieselbe Rechnung wie bei den runden Knöpfen (38 → 44): Ein Emoji ist
   für seine Größe entworfen, eine Illustration braucht ein paar Punkte
   mehr, um dagegen zu bestehen. Vier Punkte reichen hier, weil jedes der
   drei Symbole aus einer einzigen Form besteht. */
.vital-dial .btn-icon {
  width: 28px;
  height: 28px;
}

.vital-label {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--text-soft);
}

.vital-hunger { --tint: var(--hunger); }
.vital-happiness { --tint: var(--happiness); }
.vital-energy { --tint: var(--energy); }

/* ---------- Zweite Fassung: liegende Balken statt Ringe ----------

   Zum Vergleichen gedacht (`cozyDebug.vitals("balken")` / `("ringe")`),
   deshalb liegen beide Fassungen nebeneinander im Stylesheet.

   Der Einwand gegen den Ring ist kein Geschmack, sondern Ablesbarkeit:
   Neun Prozentpunkte sind auf einem Kreis 32 Grad, und ohne feste
   Bezugskante schätzt das niemand – 60 und 69 sehen gleich aus. Ein
   Balken hat ein sichtbares Ende, und drei Balken nebeneinander teilen
   sich ihre Kanten. Genau das braucht man hier: „welches Bedürfnis ist
   gerade das dringendste" ist die Frage, für die man hinschaut. */
.vitals.is-bars .vital-dial {
  width: auto;
  height: auto;
  background: none;
}

.vitals.is-bars .vital-dial::before {
  display: none;
}

/* Ohne Ring darf das Symbol größer werden – es trägt jetzt allein. */
.vitals.is-bars .vital-icon {
  font-size: 1.5rem;
}

/* Kein Wort zwischen Symbol und Balken.

   In der Ring-Fassung war „satt" eine Bildunterschrift und stand unter
   einem geschlossenen Kreis. Hier stünde es zwischen dem Symbol und
   seiner Messung und triebe die beiden auseinander – dazu drei
   verschieden lange Wörter, die die Zeile ausfransen lassen. Das Symbol
   beschriftet den Balken direkt darüber; das Wort steht weiterhin im
   aria-label, wo es gebraucht wird. */
.vitals.is-bars .vital-label {
  display: none;
}

.vitals.is-bars {
  gap: 10px;
}

.vitals.is-bars .vital {
  gap: 7px;
}

.vitals.is-bars .vital-bar {
  display: block;
}

.vital-bar {
  display: none;
  width: 100%;
  height: 10px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--tint) 20%, transparent);
  overflow: hidden;
}

.vital-bar-fill {
  display: block;
  height: 100%;
  width: calc(var(--p) * 1%);
  border-radius: 999px;
  background: var(--tint);
  transition: width 0.4s ease-out;
}

/* Der liegende Balken lebt weiter – aber nur noch für den
   Wissens-Fortschritt im Steckbrief (.bar-know). Die drei Werte der
   Katze sind seit den Ringen keine Balken mehr. */
.bar {
  flex: 1;
  height: 10px;
  border-radius: 999px;
  overflow: hidden;
}

.bar-fill {
  height: 100%;
  border-radius: 999px;
  width: 0%;
  transition: width 0.4s ease-out;
}

/* ---------- Geldbeutel und Vorratsschrank ---------- */

/* Die letzte Zeile der Statuskarte: Geld und Album nebeneinander. Der
   Geldbeutel ist zugleich der Weg in den Schrank – deshalb ein Knopf
   und keine Anzeige. */
.shelf {
  display: flex;
  gap: 8px;
}

/* Beide Regal-Knöpfe teilen sich die Zeile halb und halb, und ihr Inhalt
   sitzt mittig in der eigenen Fläche.

   Vorher nahm der Vorratsschrank den ganzen Rest (`flex: 1`), das Album
   nur seine Inhaltsbreite (`flex: none`) – der eine wirkte überdehnt, der
   andere angeklemmt, obwohl beide gleichrangig sind. Und der Inhalt
   klebte am linken Rand, während die Fläche nach rechts weiterlief.

   `flex: 1 1 0` statt `flex: 1`: Nur mit Basis 0 teilen sich zwei Knöpfe
   wirklich hälftig – sonst geht der Überschuss vom unterschiedlich
   breiten Inhalt aus, und der längere Text bekäme wieder mehr. */
.shelf-btn {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 8px 10px;
  border: none;
  border-radius: 12px;
  background: var(--card-strong);
  color: var(--text);
  font-family: inherit;
  font-size: 0.85rem;
  cursor: pointer;
  touch-action: manipulation;
  transition: transform 0.1s ease;
}

.shelf-btn:active {
  transform: scale(0.98);
}

.purse-coins {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Kein `flex: 1` mehr: Das trieb die Beschriftung nach links und den
   Talerstand an den rechten Rand. Jetzt stehen Symbol, Wort und Zahl als
   eine Gruppe zusammen und die Gruppe sitzt mittig (justify-content am
   Knopf). Bei sehr schmalen Geräten wird gekürzt statt umgebrochen. */
.purse-label {
  min-width: 0;
  color: var(--text-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Die Katze, die den Laden hält.
   Sie sitzt INNEN am oberen Rand und nicht darüber: `.sheet` schneidet
   ab (overflow: hidden), damit die runden Ecken sauber bleiben – ein
   Stück Katze über der Kante wäre ein Stück abgeschnittene Katze.
   Der negative untere Abstand schiebt die Kopfzeile unter ihre Pfoten,
   sodass sie die Zeile wirklich hält, statt darüber zu schweben. */
.shop-cat {
  display: block;
  width: 104px;
  height: auto;
  margin: -8px auto -14px;
  pointer-events: none;
}

.shop-head {
  text-align: center;
  margin-bottom: 14px;
}

.shop-head h2 {
  font-size: 1.2rem;
}

.shop-purse {
  margin-top: 4px;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--text-soft);
  font-variant-numeric: tabular-nums;
}

/* Die drei Abteilungen. Sie liegen als Reiter über der Liste und nicht
   als Knöpfe daneben: Es sind Ansichten derselben Sache, kein Menü. */
.shop-tabs {
  display: flex;
  gap: 6px;
  margin: 14px 0 12px;
}

.shop-tab {
  flex: 1;
  display: grid;
  justify-items: center;
  gap: 3px;
  padding: 7px 4px 8px;
  border: none;
  border-radius: 12px;
  background: color-mix(in srgb, var(--card-strong) 60%, transparent);
  color: var(--text-soft);
  font-family: inherit;
  font-size: 0.82rem;
  font-weight: 700;
  cursor: pointer;
  touch-action: manipulation;
}

.shop-tab[aria-selected="true"] {
  background: var(--card-strong);
  color: var(--text);
}

/* Der Symbolplatz des Reiters – wie bei den Zeilen: Emoji als Text, das
   gemalte Bild darüber, und fehlt es, bleibt das Emoji stehen. */
.tab-emoji {
  position: relative;
  display: block;
  width: 26px;
  height: 26px;
  font-size: 1.25rem;
  line-height: 26px;
}

.tab-fallback[hidden] {
  display: none;
}

.tab-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* Das unausgewählte Symbol tritt mit seiner Beschriftung zurück, statt
   in voller Farbe neben ihr zu stehen – sonst zögen drei kräftige
   Bildchen mehr Aufmerksamkeit auf sich als die Ware darunter. */
.shop-tab:not([aria-selected="true"]) .tab-emoji {
  opacity: 0.55;
}

.tab-label {
  white-space: nowrap;
}

.shop-list {
  display: grid;
  gap: 8px;
}

/* Der Laden behält seine Größe, egal welcher Reiter offen ist.
   Nachgemessen sprang das Blatt vorher um 88 Punkte – genau eine Zeile –,
   wenn man von den vier Happen auf die drei Räume wechselte, und der
   Schließen-Knopf wanderte unter dem Daumen weg. Die Höhe kommt aus der
   längsten Abteilung; `--shop-rows` setzt js/shop.js aus den Katalogen,
   damit ein fünftes Halsband sie von selbst mitzieht.

   `auto` und nicht `scroll`: Ab der Obergrenze (fünf Zeilen) wird
   gerollt, darunter steht keine Leiste im Weg. Die kurze Abteilung
   bekommt unten Luft – das ist der Preis für ein Fenster, das stillhält,
   und er ist kleiner als ein springendes Blatt. */
#shop-list {
  --shop-rows: 4;
  height: calc(var(--shop-rows) * 80px + (var(--shop-rows) - 1) * 8px);
  align-content: start;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.snack {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 10px 12px;
  border: none;
  border-radius: 16px;
  background: var(--card-strong);
  color: var(--text);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  touch-action: manipulation;
  transition: transform 0.1s ease;
}

.snack:active:not(:disabled) {
  transform: scale(0.98);
}

/* Zu Teures bleibt stehen, statt zu verschwinden: Man soll sehen,
   worauf man hinspielt. */
.snack:disabled {
  opacity: 0.45;
  cursor: default;
}

/* Der Symbolplatz hält beides: das Emoji als Text, ein gemaltes Bild
   darin. Feste Breite, damit die Beschriftungen aller Zeilen auf einer
   Kante beginnen – gemischte Emoji und Bilder liefen sonst aus. */
.snack-emoji {
  position: relative;
  flex: none;
  width: 2.1rem;
  font-size: 1.7rem;
  line-height: 1;
  text-align: center;
}

/* Eine Zeile ohne Griff: Sie ist stummgeschaltet, damit niemand ins
   Leere tippt, behält aber ihre Farbe. „Hier gibt es nichts zu tun" ist
   etwas anderes als „das kannst du dir nicht leisten"; beides gleich
   blass zu zeigen verwischte den Unterschied. */
.snack.is-static {
  opacity: 1;
}

/* ---- Vorschaubilder statt Symbolen ----

   Jede Ware zeigt ihr eigenes Bild in einem kleinen Rahmen: der Happen
   sein gemaltes Symbol, das Halsband sich selbst, der Raum sein
   Kulissenbild in der aktuellen Jahres- und Tageszeit.

   Alle drei haben dieselbe HÖHE (60), damit die Zeilen jeder Abteilung
   gleich hoch sind. Die Breite richtet sich nach der Ware, denn die drei
   Formen sind sehr verschieden – Halsband 2,19:1, Happen 1:1, Kulisse
   0,6. Eine gemeinsame Breite gäbe es nur mit Leerraum: Das Halsband
   schrumpfte auf ein Fädchen, oder die Kulisse verlöre oben und unten
   genau das, woran man sie erkennt. Dass die Beschriftungen dadurch je
   Abteilung an einer anderen Kante beginnen, ist kein Bruch – zwei
   Abteilungen sieht man nie nebeneinander. */
.snack-shot-item .snack-emoji,
.snack-shot-band .snack-emoji,
.snack-shot-room .snack-emoji {
  height: 60px;
  border-radius: 10px;
  overflow: hidden;
  background: var(--card);
  font-size: 1.5rem;
  line-height: 60px;
}

.snack-shot-item .snack-emoji { width: 56px; }
.snack-shot-band .snack-emoji { width: 64px; }
.snack-shot-room .snack-emoji { width: 36px; }

/* Freigestellte Ware bekommt Luft zum Rahmen; ein Kulissenbild füllt ihn
   ganz – es IST der Ausschnitt und kein Gegenstand darin. */
.snack-shot-item .snack-img,
.snack-shot-band .snack-img {
  padding: 5px;
}

.snack-shot-room .snack-img {
  object-fit: cover;
}

/* Das Emoji als Rückfall. Es wird ausgeblendet, sobald das gemalte Bild
   geladen ist – und bleibt stehen, wenn die Datei fehlt. */
.snack-fallback[hidden] {
  display: none;
}

/* Ein Halsband ist breit und flach, ein Raumsymbol quadratisch – beide
   sollen denselben Platz füllen, ohne sich zu verzerren. */
.snack-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.snack-text {
  flex: 1;
  display: grid;
  gap: 2px;
}

.snack-label {
  font-weight: 700;
  font-size: 0.98rem;
}

.snack-gain {
  font-size: 0.8rem;
  color: var(--text-soft);
  font-variant-numeric: tabular-nums;
  /* Die Wirkung eines Happens steht als Symbol-Zahl-Paare nebeneinander.
     Flex statt Leerzeichen, weil zwischen einem Bild und seiner Zahl ein
     Wortabstand zu wenig und ein Umbruch zu viel wäre. */
  display: flex;
  align-items: center;
  gap: 10px;
}

.gain-part {
  display: inline-flex;
  align-items: center;
  gap: 3px;
}

/* 16 Punkte – klein, aber es steht eine Zahl daneben, die dasselbe noch
   einmal sagt. Hier trägt das Symbol nur die Zuordnung zum Balken oben,
   nicht die Information allein. */
.gain-emoji {
  position: relative;
  display: inline-block;
  width: 16px;
  height: 16px;
  font-size: 0.85rem;
  line-height: 16px;
  text-align: center;
}

.gain-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.gain-fallback[hidden] {
  display: none;
}

.snack-price {
  font-weight: 700;
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Die Lehrzeile zum gerade Gefressenen – dieselbe Farbe wie das Wissen
   im Steckbrief, aber bewusst kein gezählter Wissenshappen: Wissen soll
   man sich erspielen oder erlesen, nicht kaufen. */
.shop-note {
  margin-top: 14px;
  padding: 12px 14px;
  border-radius: 14px;
  background: color-mix(in srgb, var(--know) 16%, transparent);
  color: var(--text);
  font-size: 0.88rem;
  line-height: 1.45;
}

.shop-note[hidden] {
  display: none;
}

/* ---------- Katzen-Bühne ---------- */

/* Randlos über die ganze Ansicht – kein Rahmen mehr, weil die Bühne
   nicht mehr *im* Bildschirm liegt, sondern *der* Bildschirm ist. */
#cat-stage {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
}

/* Die Standfläche der Katze. Sie hängt an einem Prozentwert der
   Bühnenhöhe statt am unteren Rand: Das Zimmerbild ist unten verankert,
   der gemalte Teppich liegt damit immer auf derselben Höhe – und die
   Katze steht darauf statt davor. */
.cat-spot {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 11%;
  z-index: 1;
  display: flex;
  justify-content: center;
}

/* Katze und Blinzel-Flicken in einer Hülle: Sie müssen gemeinsam atmen,
   sonst wandern die geschlossenen Augen neben die offenen. */
.cat-body {
  position: relative;
  width: min(80%, 330px);
}

/* Die Bilder sind freigestellt und unten bündig ausgerichtet
   (siehe tools/process-cats.mjs) – dadurch sitzt jede Pose auf der
   gleichen Grundlinie und der Schatten passt für alle. */
.cat-img {
  width: 100%;
  height: auto;
  display: block;
}

/* Der Blinzel-Flicken: ein fast durchsichtiges Bild, das nur die
   geschlossene Augenpartie enthält (tools/process-cats.mjs). Er liegt
   deckungsgleich über dem Grundbild – der Körper bleibt beim Blinzeln
   Pixel für Pixel derselbe. */
.cat-blink {
  position: absolute;
  inset: 0;
  width: 100%;
  height: auto;
  opacity: 0;
  transition: opacity 90ms ease-out;
  pointer-events: none;
}

.cat-blink.is-blinking {
  opacity: 1;
}

/* Das Halsband. Position und Breite kommen aus dem gemessenen Anker und
   stehen deshalb als Stilangabe am Element (js/collars.js) – hier steht
   nur, was für jedes Halsband gleich gilt.

   Es liegt in derselben Hülle wie Katze und Blinzel-Flicken und atmet
   damit mit: Läge es daneben, hübe und senkte sich die Katze unter einem
   stehenden Band. */
.cat-collar {
  position: absolute;
  height: auto;
  pointer-events: none;
}

.cat-shadow {
  position: absolute;
  bottom: -6px;
  left: 50%;
  width: min(52%, 215px);
  height: 18px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: rgba(120, 84, 40, 0.13);
  filter: blur(3px);
}

/* Atmen: das Tier hebt und senkt sich dezent.
   Die Dauer steckt in einer Variablen, weil die Katze beim Wechsel
   zwei Animationen gleichzeitig fährt (atmen + einblenden). Über
   animation-duration ließe sich die eine nicht ohne die andere
   verstellen – die schlafende Katze würde 4,2 Sekunden lang einblenden. */
/* Beide Regeln zielen auf dasselbe Element (#cat-stage trägt das
   data-mood). Der Selektor unten muss die ID deshalb mitnehmen –
   sonst gewinnt sie nach Spezifität und der Wert ändert sich nie. */
#cat-stage {
  --breathe: 2.6s;
}

#cat-stage[data-mood="sleepy"] {
  --breathe: 4.2s;   /* schlafende Katze atmet langsamer */
}

.cat-body {
  transform-origin: 50% 100%;
  animation: breathe var(--breathe) ease-in-out infinite alternate;
}

/* Sanftes Einblenden beim Wechsel von Stimmung oder Look. Liegt auf dem
   Bild, nicht auf der Hülle – sonst müsste es sich die Eigenschaft mit
   dem Atmen teilen. */
.cat-img.is-swapping {
  animation: cat-swap 0.35s ease-out;
}

.cat-shadow {
  transform-origin: 50% 100%;
  /* Eigene Keyframes, weil der Schatten zusätzlich zentriert bleiben
     muss – ein transform überschreibt das translateX sonst. */
  animation: breathe-shadow var(--breathe) ease-in-out infinite alternate;
}

@keyframes breathe {
  from { transform: scale(1); }
  to   { transform: scale(1.025); }
}

@keyframes breathe-shadow {
  from { transform: translateX(-50%) scale(1); }
  to   { transform: translateX(-50%) scale(0.96); }
}

@keyframes cat-swap {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Das zZz liegt IN der Katze (.cat-body) und nicht auf der Bühne.
   Vorher stand es bei `top: 18%` der Bühne und damit oben am
   Bildschirmrand – auf einem hohen Telefon eine halbe Kulisse über ihrem
   Kopf.

   Die Werte kommen aus dem Sprite: Die Schlafpose ist eingerollt, breit
   und flach; ihr Umriss beginnt erst bei 36 % der Sprite-Höhe, darüber
   ist alles durchsichtig. Der Kopf liegt links, seine Mitte bei etwa
   25 %. 32 % / 20 % setzt das Symbol also knapp über die Ohren und
   leicht daneben, statt ihr ins Gesicht zu rutschen.

   Weil es in `.cat-body` hängt, atmet es mit ihr und sitzt in jeder
   Bildschirmgröße richtig. */
.zzz {
  position: absolute;
  left: 32%;
  top: 20%;
  font-size: 1.8rem;
  line-height: 1;
  visibility: hidden;
  animation: zzz-float 2.4s ease-in-out infinite;
}

[data-mood="sleepy"] .zzz {
  visibility: visible;
}

@keyframes zzz-float {
  0%, 100% { transform: translateY(0); opacity: 0.65; }
  50%      { transform: translateY(-10px); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .cat-body,
  .cat-shadow,
  .cat-img.is-swapping,
  .zzz,
  .drift,
  .mote {
    animation: none;
  }
  .cat-shadow {
    transform: translateX(-50%);
  }
  /* Die Tageszeit wechselt weiterhin, nur ohne Bewegung dabei. */
  .scene-layer {
    transform: none;
    transition: none;
  }
  /* Die Pfote schüttelt nicht mehr; der Zähler bleibt und sagt weiterhin
     an, wann aufgedeckt wird. Das Aufdecken selbst ist ein Bildwechsel
     und funktioniert ohne Bewegung. */
  .rps-hand.is-priming .rps-hand-img,
  .rps-hand.is-thrown .rps-hand-img {
    animation: none;
  }
}

/* ---------- Die Szene ---------- */

.scene {
  position: absolute;
  inset: 0;
  /* Überbrückt die Zeit, bis das Zimmerbild geladen ist. */
  background: linear-gradient(180deg, var(--wall-top), var(--wall-bottom));
  transition: background 0.9s ease;
}

/* Das Zimmer bewegt sich gegen die stehende Katze – daraus entsteht die
   Tiefe. Der Zeiger schiebt es (--px/--py aus scene.js), die
   Eigenbewegung in .drift läuft auch ohne Maus weiter. */
.scene-layer {
  position: absolute;
  inset: 0;
  transform: translate3d(
    calc(var(--px, 0) * var(--depth)),
    calc(var(--py, 0) * var(--depth) * 0.35),
    0
  );
  transition: transform 0.3s ease-out;
}

.scene-far { --depth: 9px; }

.drift {
  position: absolute;
  /* Etwas größer als die Bühne: Sonst liefe beim Verschieben ein leerer
     Streifen an der Kante mit. */
  inset: -16px;
  animation: drift-far 19s ease-in-out infinite;
}

@keyframes drift-far {
  0%, 100% { transform: translateX(-4px); }
  50%      { transform: translateX(4px); }
}

/* Vier Zimmer übereinander – sichtbar ist das der aktuellen Tageszeit,
   der Wechsel blendet über. Unten verankert: Wenn etwas wegfällt, dann
   oben das Fenster und nie der Teppich, auf dem die Katze steht.

   Welches Bild wann welche Klasse trägt, entscheidet js/scene.js. Der
   Grund für die zwei Klassen statt eines gegenläufigen Auf- und
   Abblendens steht dort bei showPhase(): Gestapelte Bilder addieren
   ihre Deckung nicht, und in der Lücke schlägt der Wandverlauf durch. */
.room {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 100%;
  opacity: 0;
  transition: opacity 1.2s ease;
}

/* Das neue Bild blendet oben auf … */
.room.is-on {
  opacity: 1;
  z-index: 2;
}

/* … und das alte liegt so lange deckend darunter. Es blendet nicht ab,
   es wird verdeckt – deshalb bleibt die Bühne durchgehend zu 100 %
   bedeckt und nichts scheint zwischendurch durch. */
.room.was-on {
  opacity: 1;
  z-index: 1;
}

.scene-motes {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

/* ---------- Der Spielraum ---------- */

/* Eigene Kulisse für die Spiele, die mit der Wohnung nichts zu tun haben
   (tools/playroom.mjs). Sie liegt über der Kulisse und unter allem, was
   das Spiel selbst zeichnet – deshalb steht sie im DOM als Letztes in
   .scene und nicht daneben.

   Unten verankert wie die Kulissen: Fällt auf einem flachen Display etwas
   weg, dann oben die Wand und nie der Teppich. */
.playroom {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 100%;
  opacity: 0;
  transition: opacity 0.35s ease;
  pointer-events: none;
}

/* Zwei Spiele stehen bewusst NICHT in dieser Liste.

   Das **Leckerli-Fangen**: Dort fällt das Futter vor der Katze herunter,
   und sie wird beim Fressen satt. Dieses Spiel ist die Bühne.

   **Schere, Stein, Papier**: Kartons stehen auf einem Boden und Karten
   liegen auf einem Teppich – eine Pfote tut beides nicht. Sie ist kein
   Gegenstand im Zimmer, sondern eine Geste zu dir, und im Raum stand sie
   ausgerechnet auf der Fußleiste, mit einer halben Wand Leere darüber.
   Dieses Spiel bekommt seine eigene ruhige Fläche (siehe `.paws`). */
#view-home[data-game="boxes"] .playroom,
#view-home[data-game="memory"] .playroom,
#view-home[data-game="laser"] .playroom {
  opacity: 1;
}

/* Nachts dunkelt der Spielraum mit dem Rest der Oberfläche mit. Anders
   als bei den Kulissen ist das hier ein Filter und kein gemaltes Licht –
   der Raum liegt hinter Spielsteinen und ist nie das Motiv. Ein
   cremeweißer Raum hinter einem nächtlichen Bedienfeld wäre derselbe
   Bruch, den die App sonst vermeidet. */
body[data-phase="morning"] .playroom {
  filter: brightness(1.01) saturate(0.97);
}

body[data-phase="evening"] .playroom {
  filter: brightness(0.95) sepia(0.14);
}

body[data-phase="night"] .playroom {
  filter: brightness(0.5) saturate(0.7) hue-rotate(200deg);
}

.mote {
  position: absolute;
  left: var(--x);
  bottom: -6%;
  width: var(--size);
  height: var(--size);
  border-radius: 50%;
  background: var(--mote);
  box-shadow: var(--mote-glow);
  opacity: 0;
  animation: mote-rise var(--rise) linear var(--delay) infinite;
}

@keyframes mote-rise {
  0%        { transform: translate(0, 0); opacity: 0; }
  12%, 80%  { opacity: var(--mote-opacity); }
  100%      { transform: translate(var(--sway), -60vh); opacity: 0; }
}
/* ---------- Reaktionen (🐟/❤️ beim Füttern/Spielen) ---------- */

.reaction {
  position: absolute;
  left: 50%;
  bottom: 58%;
  transform: translateX(calc(-50% + var(--dx, 0px)));
  font-size: 2rem;
  pointer-events: none;
  animation: reaction-float 1s ease-out forwards;
}

@keyframes reaction-float {
  from { opacity: 1; translate: 0 0; }
  to   { opacity: 0; translate: 0 -70px; }
}

/* ---------- Aktions-Buttons ---------- */

/* Die Spielekiste zwischen den beiden runden Bühnen-Knöpfen. Der große
   Knopf nimmt den Rest (`.action` hat flex: 1), die runden bleiben bei
   ihren 44 Punkten. */
.actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

.action {
  flex: 1;
  border: none;
  border-radius: 999px;
  padding: 15px 0;
  font-size: 1.05rem;
  font-weight: 700;
  font-family: inherit;
  color: #FFFFFF;
  cursor: pointer;
  touch-action: manipulation;
  box-shadow: 0 4px 14px rgba(120, 84, 40, 0.22);
  transition: transform 0.1s ease;
}

.action:active {
  transform: scale(0.95);
}

.action-feed { background: var(--hunger); }
.action-play {
  background: var(--action-bg);
  color: var(--action-ink);
  backdrop-filter: blur(10px);
}

/* Kurze Bildschirme: Der Teppich, auf dem die Katze sitzt, und die
   Leiste am unteren Rand rücken dort zwangsläufig zusammen – beide
   hängen am selben Bildschirmboden. Ein paar Punkte weniger Polster
   überall halten sie auseinander. */
@media (max-height: 700px) {
  #view-home > .stats-card {
    margin-top: calc(6px + var(--safe-top));
  }
  #view-home > .dock {
    margin-bottom: calc(8px + var(--safe-bottom));
    gap: 8px;
    padding: 10px;
  }
  .action {
    padding: 12px 0;
  }
}

/* Der zurückhaltende Knopf: gleiche Form, aber ohne Farbe – für das,
   was nur wegräumt statt etwas auszulösen. */
.action-quiet {
  background: var(--card-strong);
  color: var(--text);
  box-shadow: none;
}

/* ---------- Karte des Tages ---------- */

/* Links in der unteren Karte, gegenüber dem Kulissen-Wechsler – die
   Spielekiste dazwischen. Der Punkt unten braucht einen Bezugspunkt. */
.daily-btn {
  position: relative;
}

/* Der Punkt: Es liegt eine ungelesene Karte bereit. Bewusst nur ein
   Punkt und keine Zahl – die App erinnert, sie mahnt nicht. */
.daily-btn.has-news::after {
  content: "";
  position: absolute;
  top: 4px;
  right: 4px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--know);
  box-shadow: 0 0 0 2px var(--card-strong);
}

.daily-kicker {
  text-align: center;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--know-ink);
}

.daily-icon {
  margin-top: 6px;
  text-align: center;
  font-size: 3rem;
  line-height: 1;
}

/* Das gemalte Themen-Symbol tritt an die Stelle des Emojis; die Regel
   `.has-icon .btn-emoji` weiter oben blendet dieses dann aus. */
.daily-icon .btn-icon {
  width: 56px;
  height: 56px;
}

#daily-title {
  margin-top: 10px;
  text-align: center;
  font-size: 1.2rem;
}

.daily-text {
  margin-top: 12px;
  font-size: 0.95rem;
  line-height: 1.55;
}

.daily-count {
  margin-top: 18px;
  text-align: center;
  font-size: 0.78rem;
  color: var(--text-soft);
  font-variant-numeric: tabular-nums;
}

/* ---------- Was war, während du weg warst ---------- */

.homecoming {
  position: absolute;
  top: 58px;
  left: 12px;
  right: 12px;
  z-index: 2;
  padding: 10px 14px;
  border-radius: 16px;
  background: var(--card-strong);
  backdrop-filter: blur(8px);
  box-shadow: 0 6px 18px rgba(120, 84, 40, 0.18);
  font-size: 0.88rem;
  line-height: 1.4;
  text-align: center;
  cursor: pointer;
  animation: tip-in 0.4s ease-out;
}

.homecoming[hidden] {
  display: none;
}

/* ---------- Album ---------- */

.album-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 20px 0 8px;
}

.album-head .section-title {
  flex: 1;
  margin: 0;
}

.album-count {
  font-size: 0.78rem;
  color: var(--text-soft);
  font-variant-numeric: tabular-nums;
}

.album-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
}

.album-cat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 4px 2px;
  border-radius: 12px;
  background: var(--card);
  font-size: 0.6rem;
  line-height: 1.15;
  text-align: center;
  color: var(--text-soft);
}

.album-cat img {
  width: 100%;
  height: auto;
  display: block;
}

/* Was noch fehlt, bleibt sichtbar – blass. Ein leeres Feld verriete
   nicht, worauf man hinspielt. */
.album-cat.is-missing img {
  opacity: 0.3;
  filter: grayscale(0.7);
}

.album-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.album-chip {
  padding: 6px 12px;
  border-radius: 999px;
  background: var(--card-strong);
  font-size: 0.82rem;
}

.album-chip.is-current {
  background: color-mix(in srgb, var(--know) 25%, transparent);
  color: var(--know-ink);
  font-weight: 700;
}

.album-list {
  list-style: none;
  display: grid;
  gap: 6px;
}

.album-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 0.9rem;
}

/* ---------- Freischalten im Designer ---------- */

.designer-purse {
  flex: none;
  padding: 6px 12px;
  border-radius: 999px;
  background: var(--card-strong);
  font-size: 0.85rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.designer-hint {
  margin-bottom: 12px;
  padding: 9px 14px;
  border-radius: 14px;
  background: color-mix(in srgb, var(--know) 18%, transparent);
  color: var(--text);
  font-size: 0.87rem;
  line-height: 1.4;
  text-align: center;
}

.designer-hint[hidden] {
  display: none;
}

/* Noch nicht freigeschaltet: Das Tier bleibt sichtbar, nur blasser –
   man soll sehen, worauf man hinspielt. Ein leeres Feld mit Schloss
   verriete nicht, ob sich das Sparen lohnt. */
.look-tile.is-locked img {
  opacity: 0.42;
  filter: grayscale(0.5);
}

/* Dieselbe Haltung im Album: Ein Raum, in dem man noch nicht gewohnt
   hat, tritt zurück statt zu fehlen. Eine Sammlung zeigt auch die
   Lücken – sonst wüsste niemand, dass es welche gibt. */
.album-chip.is-locked {
  opacity: 0.45;
}

.look-price {
  position: absolute;
  left: 50%;
  top: 38%;
  transform: translate(-50%, -50%);
  padding: 4px 9px;
  border-radius: 999px;
  background: var(--card-strong);
  box-shadow: 0 2px 8px rgba(90, 60, 25, 0.25);
  font-size: 0.75rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Der zweite Tipp kauft – deshalb sieht die Kachel dazwischen anders
   aus als vorher. */
.look-tile.is-confirming {
  outline: 2px solid var(--know);
  outline-offset: 2px;
}

.look-tile.is-confirming .look-price {
  background: var(--know);
  color: var(--know-ink);
}

/* ---------- Minispiel: Leckerli-Fangen ---------- */

/* Während der Runde weichen Statuskarte, der Spielekiste-Knopf und der
   Kulissen-Wechsler. Ohne das bliebe der Bühne zu wenig Höhe – und ohne
   Fallhöhe gibt es nichts zu fangen. */
#view-home[data-game] .stats-card,
#view-home[data-game] .dock {
  display: none;
}

.game {
  position: absolute;
  inset: 0;
  /* Über der Katze: Das Futter fällt vor ihr herunter, sie schaut zu. */
  z-index: 3;
}

.game-hud {
  position: absolute;
  /* Die Bühne reicht seit dem randlosen Startbildschirm bis unter die
     Statusleiste – der Schließen-Knopf darf nicht hinter die Uhr. */
  top: calc(10px + var(--safe-top));
  left: 10px;
  right: 10px;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Die verbleibende Zeit als schrumpfender Balken statt als Zahl: Ein
   herunterzählender Zähler macht Druck – die App will das Gegenteil. */
.game-clock {
  flex: 1;
  height: 8px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--hunger) 22%, var(--card));
  overflow: hidden;
}

.game-clock-fill {
  height: 100%;
  width: 100%;
  border-radius: 999px;
  background: var(--hunger);
}

.game-score {
  min-width: 62px;
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--card-strong);
  backdrop-filter: blur(8px);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: center;
}

/* Der Zähler ist beiden Spielen gemeinsam, sein Sinnbild nicht: Beim
   Leckerli-Fangen zählt er Gefangenes, beim Kartonspiel Gefundenes. */
.game-score.is-catch::before { content: "🐟 "; }
.game-score.is-right::before { content: "✓ "; }

.game-field {
  position: absolute;
  inset: 0;
  /* Der Finger zieht den Napf – dabei soll nichts scrollen oder zoomen. */
  touch-action: none;
}

/* Groß genug, um neben der Katze zu bestehen: Sie ist rund 80 % der
   Bühnenbreite: ein zierliches Emoji davor sieht aus wie Konfetti,
   nicht wie eine Mahlzeit. */
/* 3,4rem sind rund 54 px – gut ein Fünftel der Bühnenbreite und knapp
   die halbe Napfbreite. Vorher waren es 2,8rem; bei fallendem Futter
   entscheidet die Größe darüber, ob man rechtzeitig erkennt, was da
   kommt, und bei Milch gegen Leckerli ist genau das die Aufgabe. Die
   Fangbreite hängt nicht daran – sie wird aus dem gemessenen Napf
   abgeleitet (game-treats.js). */
.treat {
  position: absolute;
  top: 0;
  left: 0;
  font-size: 3.4rem;
  line-height: 1;
  will-change: transform;
  filter: drop-shadow(0 3px 5px rgba(90, 60, 25, 0.3));
}

/* .is-good und .is-bad bekommen bewusst KEINE eigene Gestaltung: Milch
   und Schokolade sollen daran erkennbar sein, was sie sind. Eine
   Warnfarbe nähme dem Spiel genau das Wissen, um das es geht. */

/* Gefangen: in den Napf schrumpfen. Über scale/opacity statt transform,
   weil die Position des Happens inline im transform steht. */
.treat.is-caught {
  animation: treat-caught 0.22s ease-in forwards;
}

@keyframes treat-caught {
  from { opacity: 1; scale: 1; }
  to   { opacity: 0; scale: 0.4; }
}

/* Der Napf, von Hand gebaut statt als Emoji: 🥣 sieht auf jedem System
   anders aus, und die Fanglinie im Spiel hängt an seiner Höhe. */
.bowl {
  position: absolute;
  left: 50%;
  bottom: 7%;
  /* Anteil der Bühne statt fester Pixel, damit er auf dem schmalen
     iPhone und im breiten Fenster gleich proportioniert wirkt. */
  width: 29%;
  max-width: 120px;
  aspect-ratio: 21 / 10;
  transform: translateX(-50%);
  border-radius: 10px 10px 46% 46% / 8px 8px 92% 92%;
  background: linear-gradient(180deg, #E8A85C, #C0722C);
  box-shadow: inset 0 -8px 12px rgba(90, 45, 10, 0.25),
              0 6px 12px rgba(90, 60, 25, 0.25);
}

/* Der Rand – ohne ihn wäre es ein Eimer, keine Schale. */
.bowl::before {
  content: "";
  position: absolute;
  left: -7px;
  right: -7px;
  top: -9px;
  height: 19px;
  border-radius: 50%;
  background: linear-gradient(180deg, #F6CE94, #D9944B);
}

/* Die Lehrzeile bei Milch und Schokolade. Sitzt oben unter der Anzeige
   und nicht unten: Dort läge sie über dem Napf. */
.game-tip {
  position: absolute;
  top: 58px;
  left: 12px;
  right: 12px;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border-radius: 16px;
  background: var(--card-strong);
  backdrop-filter: blur(8px);
  box-shadow: 0 6px 18px rgba(120, 84, 40, 0.18);
  font-size: 0.9rem;
  line-height: 1.35;
}

.game-tip[hidden] {
  display: none;
}

.game-tip.is-in {
  animation: tip-in 0.25s ease-out;
}

@keyframes tip-in {
  from { opacity: 0; translate: 0 -10px; }
  to   { opacity: 1; translate: 0 0; }
}

.tip-emoji {
  font-size: 1.4rem;
}

/* ---------- Minispiel: Wo ist die Katze? ---------- */

/* Hier steckt die Katze in den Kartons – die auf der Bühne muss weichen,
   sonst stünde sie doppelt im Zimmer. */
#view-home[data-game="boxes"] .cat-spot,
#view-home[data-game="boxes"] .zzz {
  display: none;
}

.boxgame {
  position: absolute;
  inset: 0;
  z-index: 3;
}

.box-round {
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--card-strong);
  backdrop-filter: blur(8px);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}

/* Der Platz, auf dem die Kartons stehen. Nimmt die ganze Bühne ein,
   damit die Standplätze in Prozent der Breite rechnen können. */
.box-field {
  position: absolute;
  inset: 0;
}

.box-slot {
  position: absolute;
  bottom: 14%;
  width: 30%;
  max-width: 132px;
  transform: translateX(-50%);
  /* Nur `left` wandert – das ist die ganze Bewegung des Spiels. */
  transition: left 0.34s cubic-bezier(0.45, 0, 0.35, 1);
}

/* Beim Tauschen geht einer der beiden vorn und leicht angehoben vorbei.
   Laufen sie stur gerade durcheinander, sieht man nicht mehr, welcher
   welcher war – und genau das soll man ja verfolgen können. */
.box-slot.is-front {
  z-index: 2;
  transform: translateX(-50%) translateY(-14px);
}

.box-field.is-asking .box-slot {
  cursor: pointer;
}

/* Der Karton, von Hand gebaut wie der Napf: 📦 sieht auf jedem System
   anders aus, und er muss sich anheben lassen. */
/* Quadratisch, und das ist keine Geschmacksfrage: Die Katzensprites sind
   512×512, und der Karton muss sie ganz verdecken können. Ein flacherer
   Karton ließe oben Ohren herausschauen – dann wäre das Spiel keins. */
.box {
  position: relative;
  height: 0;
  padding-bottom: 100%;
  border-radius: 6px 6px 3px 3px;
  background: linear-gradient(170deg, #D8A96B 0%, #C08E4E 55%, #A87438 100%);
  box-shadow: 0 8px 16px rgba(90, 60, 25, 0.3);
  transition: transform 0.3s ease-out;
}

/* Die beiden Deckelklappen – ohne sie wäre es eine Kiste ohne Deckel. */
.box::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 22%;
  border-radius: 6px 6px 0 0;
  background: linear-gradient(180deg, #E7BE82, #CE9A57);
}

/* Der Klebestreifen in der Mitte. */
.box::after {
  content: "";
  position: absolute;
  top: 22%;
  left: 50%;
  width: 3px;
  height: 78%;
  transform: translateX(-50%);
  background: rgba(120, 80, 35, 0.35);
}

/* Weiter als die Katze hoch ist – sonst bliebe beim Aufdecken ihr Kopf
   hinter dem Kartonboden. */
.box.is-lifted {
  transform: translateY(-96%);
}

/* Die Katze sitzt im selben Element wie ihr Karton und wandert deshalb
   beim Tauschen mit, ohne dass jemand sie bewegen müsste. Sie liegt
   dahinter: Hebt sich der Karton, kommt sie zum Vorschein. */
/* Schmaler als der Karton: Die Sprites sind quadratisch, also ist die
   Bildbreite zugleich die Bildhöhe – bei voller Slotbreite ragte sie
   oben heraus. */
.box-cat {
  position: absolute;
  bottom: 0;
  left: 50%;
  width: 86%;
  transform: translateX(-50%);
  pointer-events: none;
}

.box-cat[hidden] {
  display: none;
}

/* Die Ansage über den Kartons – sie führt durch den Durchgang, damit
   niemand raten muss, ob gerade gemischt oder gefragt wird. */
.box-say {
  position: absolute;
  top: 58px;
  left: 12px;
  right: 12px;
  padding: 8px 14px;
  border-radius: 16px;
  background: var(--card-strong);
  backdrop-filter: blur(8px);
  text-align: center;
  font-size: 0.95rem;
  font-weight: 600;
}

/* ---------- Minispiel: Katzen-Memory ---------- */

/* Das Brett füllt die Bühne, die Katze tritt so lange ab – sie ist auf
   den Karten ohnehin sechzehnmal zu sehen.

   Bei Schere-Stein-Papier tritt sie aus einem anderen Grund ab: Ihre
   Pfote steht dort groß in der Mitte, und darunter säße sonst noch
   einmal sie selbst. Eine Katze mit einer zweiten, körperlosen Pfote
   über sich – das liest sich falsch. */
#view-home[data-game="laser"] .cat-spot,
#view-home[data-game="laser"] .zzz,
#view-home[data-game="memory"] .cat-spot,
#view-home[data-game="memory"] .zzz,
#view-home[data-game="paws"] .cat-spot,
#view-home[data-game="paws"] .zzz {
  display: none;
}

.memory,
/* Eine ruhige cremefarbene Fläche statt einer Kulisse – derselbe
   Wandverlauf, den die Bühne benutzt, also wandert er mit der Tageszeit
   mit. Der Grund steht oben bei `.playroom`: Eine Pfote steht auf nichts,
   sie kommt ins Bild. Vor einem Zimmer sah sie aus wie auf der Fußleiste
   abgestellt; vor einer leeren Fläche ist sie einfach eine Hand. */
.paws {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  /* Unten deutlich mehr Luft als früher: Die Karten lagen auf der
     Kante, und was man mit dem Daumen trifft, gehört nicht an den
     äußersten Rand. Nach oben schieben darf man sie aber nur wenig –
     zu weit weg vom Rand wird der Daumen lang. */
  padding: calc(62px + var(--safe-top)) 12px clamp(28px, 6vh, 72px);
  background: linear-gradient(180deg, var(--wall-top), var(--wall-bottom));
}

.memory[hidden],
.paws[hidden] {
  display: none;
}

.memory-levels {
  margin: auto 0;
  display: grid;
  gap: 10px;
}

.memory-levels[hidden] {
  display: none;
}

.memory-hint {
  text-align: center;
  font-size: 0.95rem;
  font-weight: 600;
  margin-bottom: 4px;
}

/* Die Spaltenzahl kommt aus der Schwierigkeit (state.js) und nicht von
   hier: Zwölf Paare in drei Spalten wären eine sehr lange Wand. */
.memory-board {
  margin: auto 0;
  display: grid;
  grid-template-columns: repeat(var(--columns, 4), 1fr);
  gap: 8px;
}

.memory-board[hidden] {
  display: none;
}

.memo {
  aspect-ratio: 1;
  border: none;
  padding: 0;
  background: none;
  cursor: pointer;
  touch-action: manipulation;
  perspective: 600px;
}

/* Die beiden Kartenseiten liegen Rücken an Rücken; gedreht wird die
   Hülle. Ein reines Ein-/Ausblenden sähe aus wie ein Bildwechsel, nicht
   wie eine Karte. */
.memo-inner {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  transition: transform 0.35s ease;
  transform-style: preserve-3d;
}

.memo.is-open .memo-inner,
.memo.is-done .memo-inner {
  transform: rotateY(180deg);
}

.memo-back,
.memo-face {
  position: absolute;
  inset: 0;
  border-radius: 14px;
  backface-visibility: hidden;
  box-shadow: 0 2px 8px rgba(120, 84, 40, 0.18);
}

.memo-back {
  background: var(--card-strong)
    url("../assets/memory/back.svg") no-repeat center / 58%;
  opacity: 0.9;
}

.memo-face {
  transform: rotateY(180deg);
  background: var(--card);
  display: grid;
  place-items: center;
  overflow: hidden;
}

.memo-face img {
  width: 96%;
  height: 96%;
  object-fit: contain;
}

/* Gefundene Paare bleiben liegen, treten aber zurück – sonst sucht das
   Auge weiter dort, wo nichts mehr zu holen ist. */
.memo.is-done {
  cursor: default;
}

.memo.is-done .memo-face {
  opacity: 0.45;
}

.memo-count {
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--card-strong);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}

.game-score#memory-moves::before {
  content: "Züge ";
  font-weight: 400;
  font-size: 0.75rem;
}

.memo-say {
  min-height: 1.5em;
  padding-top: 8px;
  text-align: center;
  font-size: 0.92rem;
  font-weight: 600;
}

/* ---------- Minispiel: Schere, Stein, Papier ---------- */

/* Die Seitenbeschriftung steht über ihrer Reihe und bekommt einen
   eigenen Grund: ohne ihn verschwindet sie in der Kulisse dahinter. */
.rps-side {
  justify-self: center;
  width: fit-content;
  margin: 4px auto;
  padding: 2px 12px;
  border-radius: 999px;
  background: var(--card-strong);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-soft);
}

.rps-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

/* „Du" schließt den Block ab, mit deutlichem Abstand zur Ergebniszeile –
   nah genug, um dazuzugehören, weit genug, um nicht ins Wort zu fallen. */
.paws-you {
  margin-top: clamp(16px, 5vh, 64px);
}

/* Deine Karten bleiben unten am Rand erreichbar; der Abstand nach oben
   ist nur ein Abstand und kein Loch mehr. Vorher stand hier ein sehr
   großer Wert, weil „Du" mit hochgezogen werden sollte – seit es zum
   Block gehört, braucht es das nicht mehr. */
.paws .rps-row.is-mine {
  margin-top: clamp(16px, 4vh, 48px);
}

.rps-card {
  position: relative;
  display: grid;
  place-items: center;
  gap: 2px;
  padding: 8px 4px 6px;
  border: 2px solid transparent;
  border-radius: 16px;
  background: var(--card-strong);
  backdrop-filter: blur(6px);
  font-family: inherit;
  color: var(--text-soft);
  box-shadow: 0 2px 10px rgba(120, 84, 40, 0.10);
  transition: transform 0.1s ease, border-color 0.15s ease;
}

.rps-row.is-mine .rps-card {
  cursor: pointer;
  touch-action: manipulation;
}

.rps-row.is-mine .rps-card:active {
  transform: scale(0.95);
}

.rps-card img {
  width: 100%;
  max-width: 74px;
  height: auto;
}

.rps-label {
  font-size: 0.72rem;
  font-weight: 600;
}

/* Deine Wahl steht fest.

   Ein Rahmen allein war zu leise: Während der Zähler lief, wusste man
   nicht sicher, ob der Tipp überhaupt angekommen ist – und nachfassen
   kostet die Runde. Jetzt treten die beiden anderen Karten zurück
   (blass und entfärbt) und die gewählte tritt vor. Das Zurücktreten
   trägt die Aussage: Eine hervorgehobene Karte übersieht man, zwei
   verschwundene nicht. */
.rps-row.is-mine.has-pick .rps-card {
  opacity: 0.38;
  filter: grayscale(0.8);
}

.rps-row.is-mine.has-pick .rps-card.is-picked {
  opacity: 1;
  filter: none;
}

.rps-card.is-picked {
  border-color: var(--text);
  background: var(--card-strong);
  box-shadow: 0 4px 16px rgba(120, 84, 40, 0.22);
}

/* Der kurze Stoß beim Antippen – die Karte nimmt die Wahl an. */
.rps-row.is-mine .rps-card.is-picked {
  animation: rps-pick 0.24s cubic-bezier(0.3, 1.5, 0.5, 1);
}

@keyframes rps-pick {
  0%   { transform: scale(1); }
  55%  { transform: scale(1.08); }
  100% { transform: scale(1.03); }
}

/* Nach dem Aufdecken bleibt die Wahl stehen, aber die Auszeichnung
   „gewonnen/verloren" bekommt das letzte Wort. */
.rps-row.is-mine.has-pick .rps-card.is-win,
.rps-row.is-mine.has-pick .rps-card.is-lose {
  opacity: 1;
  filter: none;
}

.rps-card.is-win {
  border-color: var(--know);
  background: color-mix(in srgb, var(--know) 18%, var(--card));
}

.rps-card.is-lose {
  opacity: 0.5;
}

/* Der Stempel fällt auf die Karte der Katze: groß herein, klein werdend.
   Ein reines Einblenden sähe aus wie ein Aufkleber, nicht wie ein
   Aufsetzen. */
/* Der freie Platz zwischen Kopfzeile und Ergebnis gehört ihrer Pfote.
   `flex: 1` plus zentrierter Inhalt heißt: Auf einem hohen Display steht
   sie weiter unten, auf einem flachen rückt alles zusammen – die Karten
   bleiben immer am unteren Rand erreichbar. */
/* Der freie Platz gehört ihrer Pfote: Name, Pfote, Zähler und die
   Ergebniszeile stehen als EIN Block in der Mitte. Zuerst lag die
   Ergebniszeile darunter außerhalb – dazwischen klaffte dann eine leere
   Hälfte, und die Pfote schwebte für sich allein. */
.paws-arena {
  flex: 1;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 6px;
}

/* Ihre Pfote. Sie ist das Einzige, worauf man während des Zählens
   schaut, und darf deshalb den Platz nehmen. Die Höhe hängt an der
   Bildschirmhöhe: Auf einem kleinen Telefon soll sie die Karten nicht
   nach unten drücken. */
.rps-hand {
  --paw: clamp(104px, 19vh, 176px);
  display: grid;
  place-items: end center;
  height: var(--paw);
  /* Beim Schütteln taucht sie unten aus dem Rahmen – wie eine Hand, die
     nach unten schwingt. */
  overflow: hidden;
}

.rps-hand-img {
  width: auto;
  height: var(--paw);
  /* Der Drehpunkt sitzt am Handgelenk, nicht in der Bildmitte: Nur so
     schwingt die Pfote wie ein Arm, statt zu rutschen. */
  transform-origin: bottom center;
}

/* Das Schütteln vor dem Aufdecken – im Wettkampf heißt es „priming":
   dreimal die geballte Pfote im Takt, aufgedeckt wird auf dem vierten
   Schlag. Genau die drei Ziffern des Zählers.

   Bewusst EIN Bild, das bewegt wird, und keine gerenderte Bilderfolge:
   Zwei Renderläufe derselben Pose unterscheiden sich um 13 bis 17 % der
   Pixel (siehe tools/README.md) – eine Bilderfolge würde kochen statt zu
   schütteln. Eine schüttelnde Faust ändert ihre Form aber gar nicht, sie
   schwingt. Das kann eine Transformation exakt, und pixelgenau. */
@keyframes paw-prime {
  0%   { transform: translateY(0) rotate(0deg); }
  30%  { transform: translateY(16%) rotate(-5deg); }
  55%  { transform: translateY(-3%) rotate(2deg); }
  100% { transform: translateY(0) rotate(0deg); }
}

.rps-hand.is-priming .rps-hand-img {
  animation: paw-prime 800ms ease-in-out 3;
}

/* Das Aufdecken: kurz größer werden. Der Tausch des Bildes allein wäre
   ein Schnitt, der Stoß macht daraus einen Wurf. */
@keyframes paw-throw {
  0%   { transform: translateY(10%) scale(0.94); }
  60%  { transform: translateY(-4%) scale(1.06); }
  100% { transform: translateY(0) scale(1); }
}

.rps-hand.is-thrown .rps-hand-img {
  animation: paw-throw 0.34s cubic-bezier(0.3, 1.4, 0.5, 1);
}

.rps-hand.is-lose .rps-hand-img {
  opacity: 0.5;
}

.rps-count {
  margin: 10px 0 2px;
  text-align: center;
  font-size: 2.6rem;
  font-weight: 700;
  line-height: 1;
  min-height: 1em;
  font-variant-numeric: tabular-nums;
}

/* Jede Ziffer bekommt einen kurzen Schlag – ohne ihn zählt es nicht
   sichtbar herunter, es steht nur eine andere Zahl da. */
.rps-count.is-tick {
  animation: count-tick 0.3s ease-out;
}

@keyframes count-tick {
  from { scale: 1.5; opacity: 0.3; }
  to   { scale: 1; opacity: 1; }
}

/* Die Punkte stehen IN der Beschriftung der jeweiligen Seite – ihre
   neben ihrem Namen, deine neben „Du". Vorher lagen sie in einem eigenen
   Balken; der war ein zweiter Ort für dieselbe Zahl, und oben in der
   Kopfzeile las er sich obendrein wie ihre Überschrift.

   Die Zahl darf größer sein als die Beschriftung: Sie ändert sich, der
   Name nicht. */
.rps-points {
  margin-left: 2px;
  padding-left: 8px;
  border-left: 1px solid color-mix(in srgb, currentColor 30%, transparent);
  font-size: 1.05rem;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

/* Ein sehr langer Katzenname darf die Zahl nicht aus der Kachel drängen. */
#paws-side-cat {
  display: inline-block;
  max-width: 14ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}

.paws-say {
  min-height: 1.6em;
  margin-bottom: 12px;
  text-align: center;
  font-size: 0.98rem;
  font-weight: 600;
}

/* ---------- Spielekiste ---------- */

.game-entry {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 12px 14px;
  border: none;
  border-radius: 16px;
  background: var(--card-strong);
  color: var(--text);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  touch-action: manipulation;
  transition: transform 0.1s ease;
}

.game-entry:active {
  transform: scale(0.98);
}

/* Symbol und Rückfall-Emoji liegen übereinander: Das gemalte Bild deckt
   das Emoji ab. Fehlt das Bild, entfernt games.js es – und darunter
   steht wieder das Emoji. So bleibt die Kiste auch ohne Assets bedienbar. */
.game-entry-badge {
  position: relative;
  flex: none;
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
}

.game-entry-emoji {
  font-size: 1.8rem;
  line-height: 1;
}

.game-entry-icon {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.game-entry-text {
  display: grid;
  gap: 3px;
}

.game-entry-label {
  font-weight: 700;
  font-size: 1rem;
}

.game-entry-tagline {
  font-size: 0.82rem;
  line-height: 1.35;
  color: var(--text-soft);
}

/* Was die Runde der Katze bringt. Seit die Pflegeknöpfe weg sind, ist
   das keine Nebeninformation mehr, sondern der Grund, dieses Spiel zu
   wählen und kein anderes. */
.game-entry-gives {
  margin-top: 2px;
  font-size: 0.78rem;
  font-weight: 600;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
}

.gives-part {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

/* Der Trenner steht in der CSS und nicht im DOM: Er gehört zwischen zwei
   Teile und nicht zu einem von beiden – bei Umbruch verschwindet er
   damit von selbst nicht an die falsche Stelle. */
.gives-part + .gives-part::before {
  content: "·";
  color: var(--text-soft);
}

.gives-icon {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  line-height: 1;
}

.gives-icon .btn-icon {
  width: 16px;
  height: 16px;
}

/* ---------- Ergebnis der Runde ---------- */

.game-result {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: grid;
  place-items: center;
  padding: 14px;
  background: rgba(40, 26, 12, 0.35);
  backdrop-filter: blur(3px);
}

.game-result[hidden] {
  display: none;
}

.game-card {
  width: 100%;
  max-height: 100%;
  overflow-y: auto;
  padding: 18px;
  border-radius: 22px;
  background: linear-gradient(180deg, var(--sheet-top), var(--sheet-bottom));
  box-shadow: 0 12px 40px rgba(60, 40, 15, 0.3);
  animation: card-in 0.28s ease-out;
}

@keyframes card-in {
  from { opacity: 0; scale: 0.94; }
  to   { opacity: 1; scale: 1; }
}

.game-card h2 {
  font-size: 1.15rem;
  text-align: center;
}

.game-tally {
  list-style: none;
  display: grid;
  gap: 6px;
  margin: 12px 0;
}

.game-tally li {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 0.95rem;
}

.tally-icon {
  width: 22px;
  text-align: center;
}

/* Das Ernährungswissen trägt die Wissensfarbe, nicht die der Pflege –
   es gehört dem Menschen davor, wie der Steckbrief. */
.learned-count {
  padding: 7px 12px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--know) 20%, transparent);
  color: var(--know-ink);
  font-size: 0.85rem;
  text-align: center;
}

.learned-item {
  margin-top: 10px;
  padding: 12px 14px;
  border-radius: 14px;
  background: color-mix(in srgb, var(--know) 14%, transparent);
  font-size: 0.88rem;
  line-height: 1.45;
}

.learned-title {
  margin-bottom: 4px;
  font-weight: 700;
}

.game-card-actions {
  display: flex;
  gap: 12px;
  margin-top: 16px;
}

/* ---------- Designer ---------- */

.designer-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}

.designer-head h2 {
  flex: 1;
  font-size: 1.15rem;
  text-align: center;
  margin-right: 38px;   /* gleicht den Zurück-Button aus */
}

.designer-head .icon-btn {
  font-size: 1.6rem;
  /* Das Chevron sitzt optisch zu tief. */
  padding-bottom: 4px;
}

/* Der Rollbereich bekommt seitlich vier Punkte Luft – und holt sie sich
   mit einem negativen Außenabstand aus der Ansicht zurück, damit optisch
   nichts wandert.

   Warum überhaupt: Ein Element mit `overflow-y: auto` schneidet auch
   waagerecht ab. Alles, was ein Kind AUSSERHALB seiner Box zeichnet,
   verschwindet an der Kante – und genau das tut ein Fokus-Ring
   (`outline` mit positivem `outline-offset`). Das Namensfeld ist genau
   so breit wie der Bereich, sein Ring lag also einen Hauch daneben und
   wurde rechts abgeschnitten. Dieselbe Falle hätten die äußeren
   Rassenkacheln beim Kaufen gehabt.

   Die vier Punkte decken die zwei Punkte Ring plus einen Punkt Abstand
   beider Stellen ab. */
.designer-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-inline: 4px;
  margin-inline: -4px;
}

.name-field {
  display: block;
  margin-bottom: 18px;
}

.name-field span,
.field-label {
  display: block;
  margin-bottom: 6px;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-soft);
}

.name-field input {
  width: 100%;
  border: none;
  border-radius: 14px;
  padding: 13px 15px;
  font-family: inherit;
  font-size: 1.05rem;
  color: var(--text);
  background: var(--card);
  box-shadow: 0 2px 10px rgba(120, 84, 40, 0.08);
  user-select: text;
  -webkit-user-select: text;
}

.name-field input:focus {
  outline: 2px solid var(--hunger);
  outline-offset: 1px;
}

.look-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  padding-bottom: 8px;
}

/* Kachel und Steckbrief-Knopf liegen nebeneinander, nicht ineinander
   (siehe main.js) – die Zelle hält sie zusammen. */
.look-cell {
  position: relative;
}

.look-tile {
  /* Bezugsrahmen für das Preisschild auf gesperrten Kacheln. */
  position: relative;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  border: 2px solid transparent;
  border-radius: 16px;
  padding: 8px 4px 6px;
  background: var(--card);
  font-family: inherit;
  font-size: 0.72rem;
  color: var(--text-soft);
  cursor: pointer;
  touch-action: manipulation;
  box-shadow: 0 2px 10px rgba(120, 84, 40, 0.08);
  transition: transform 0.1s ease, border-color 0.15s ease;
}

.look-tile img {
  width: 100%;
  height: auto;
  display: block;
}

.look-tile:active {
  transform: scale(0.96);
}

.look-tile.is-selected {
  border-color: var(--hunger);
  color: var(--text);
  font-weight: 600;
}

/* Die Lehrzeile unter der Farbwahl – der interessanteste Satz zu genau
   dieser Farbe (state.js, `note`). */
.color-note {
  margin: 10px 0 4px;
  padding: 11px 13px;
  border-radius: 14px;
  background: var(--card);
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--text-soft);
}

.color-note:empty {
  display: none;
}

.look-info {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: 50%;
  background: var(--card-strong);
  font-family: inherit;
  font-size: 0.95rem;
  line-height: 1;
  color: var(--text-soft);
  cursor: pointer;
  touch-action: manipulation;
  box-shadow: 0 1px 4px rgba(120, 84, 40, 0.15);
}

.look-info:active {
  transform: scale(0.9);
}

/* ---------- Steckbrief (Wissens-Sheet) ---------- */

.sheet {
  position: fixed;
  inset: auto 0 0 0;
  width: 100%;
  max-width: 430px;
  max-height: 88dvh;
  margin: 0 auto;
  border: none;
  padding: 0;
  border-radius: 26px 26px 0 0;
  background: linear-gradient(180deg, var(--sheet-top), var(--sheet-bottom));
  color: var(--text);
  box-shadow: 0 -10px 40px rgba(120, 84, 40, 0.28);
  overflow: hidden;
}

.sheet::backdrop {
  background: rgba(74, 55, 40, 0.4);
}

.sheet-inner {
  max-height: 88dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 20px 18px calc(22px + env(safe-area-inset-bottom));
}

.sheet-close {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 2;
  background: var(--card-strong);
  font-size: 0.95rem;
}

.sheet-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding-right: 42px;   /* Platz für den Schließen-Knopf */
}

.sheet-img {
  width: 82px;
  height: auto;
  flex: none;
}

.sheet-heading h2 {
  font-size: 1.3rem;
  line-height: 1.2;
}

.sheet-meta {
  margin: 5px 0 6px;
}

.kind-badge {
  display: inline-block;
  border-radius: 999px;
  padding: 3px 10px;
  background: rgba(138, 116, 96, 0.16);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-soft);
}

/* Eine echte Rasse ist die Ausnahme unter den Looks – sie darf sich
   auch optisch von den bloßen Fellzeichnungen abheben. */
.kind-badge.is-breed {
  background: color-mix(in srgb, var(--know) 28%, transparent);
  color: var(--know-ink);
}

.sheet-tagline {
  font-size: 0.86rem;
  line-height: 1.4;
  color: var(--text-soft);
}

/* ---------- Fortschritt ---------- */

.know-progress {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 16px 0 4px;
}

.bar-know {
  background: color-mix(in srgb, var(--know) 20%, transparent);
}

.bar-know .bar-fill {
  background: var(--know);
}

.know-count {
  flex: none;
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  color: var(--text-soft);
}

/* ---------- Steckbrief-Zeilen ---------- */

.profile {
  list-style: none;
  margin: 14px 0 4px;
}

.profile li {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: 2px 8px;
  padding: 9px 0;
  border-bottom: 1px solid rgba(138, 116, 96, 0.14);
}

.profile-icon {
  grid-row: span 2;
  font-size: 0.95rem;
  text-align: center;
}

.profile-label {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-soft);
}

.profile-value {
  font-size: 0.88rem;
  line-height: 1.4;
}

/* ---------- Fakten zum Aufklappen ---------- */

.section-title {
  margin: 20px 0 8px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-soft);
}

.fact {
  border-radius: 16px;
  margin-bottom: 8px;
  background: var(--card);
  box-shadow: 0 2px 8px rgba(120, 84, 40, 0.07);
  overflow: hidden;
}

.fact-head {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  border: none;
  padding: 12px 13px;
  background: none;
  font-family: inherit;
  font-size: 0.92rem;
  font-weight: 600;
  text-align: left;
  color: var(--text);
  cursor: pointer;
  touch-action: manipulation;
}

.fact-icon {
  flex: none;
  font-size: 1.05rem;
}

.fact-title {
  flex: 1;
}

.fact-check {
  flex: none;
  font-size: 0.8rem;
  color: var(--know);
}

.fact-chevron {
  flex: none;
  font-size: 1.2rem;
  color: var(--text-soft);
  transition: transform 0.18s ease;
}

.fact-head[aria-expanded="true"] .fact-chevron {
  transform: rotate(90deg);
}

.fact-body {
  padding: 0 13px 13px;
  font-size: 0.87rem;
  line-height: 1.55;
  color: var(--text-soft);
}

/* ---------- Wahr oder falsch ---------- */

.quiz {
  border-radius: 16px;
  margin-bottom: 8px;
  padding: 13px;
  background: var(--card);
  box-shadow: 0 2px 8px rgba(120, 84, 40, 0.07);
}

.quiz-claim {
  font-size: 0.92rem;
  line-height: 1.45;
}

.quiz-actions {
  display: flex;
  gap: 9px;
  margin-top: 11px;
}

.quiz-btn {
  flex: 1;
  border: 2px solid rgba(138, 116, 96, 0.25);
  border-radius: 999px;
  padding: 9px 0;
  background: none;
  font-family: inherit;
  font-size: 0.88rem;
  font-weight: 700;
  color: var(--text-soft);
  cursor: pointer;
  touch-action: manipulation;
  transition: transform 0.1s ease;
}

.quiz-btn:active:not(:disabled) {
  transform: scale(0.96);
}

.quiz-btn:disabled {
  cursor: default;
}

/* Nach der Antwort: Die Wahrheit wird immer markiert, der Fehlgriff
   zusätzlich – so bleibt sichtbar, was richtig gewesen wäre. */
.quiz-btn.is-truth {
  border-color: var(--know);
  background: color-mix(in srgb, var(--know) 18%, transparent);
  color: var(--know-ink);
}

.quiz-btn.is-wrong {
  border-color: var(--wrong);
  color: var(--wrong);
}

.quiz-result {
  margin-top: 10px;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--text-soft);
}

.sheet-foot {
  margin-top: 18px;
  font-size: 0.74rem;
  line-height: 1.5;
  text-align: center;
  color: var(--text-soft);
  opacity: 0.85;
}

/* Die Widmung. Leiser als alles andere im Sheet und durch eine feine
   Linie abgesetzt: Sie gehört nicht zum Steckbrief, sie steht darunter. */
.dedication {
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid color-mix(in srgb, var(--text-soft) 25%, transparent);
  font-size: 0.72rem;
  text-align: center;
  color: var(--text-soft);
  opacity: 0.7;
}

@media (prefers-reduced-motion: no-preference) {
  .sheet[open] {
    animation: sheet-rise 0.3s cubic-bezier(0.2, 0.8, 0.25, 1);
  }
  .sheet[open]::backdrop {
    animation: sheet-fade 0.3s ease;
  }
}

@keyframes sheet-rise {
  from { transform: translateY(100%); }
}

@keyframes sheet-fade {
  from { opacity: 0; }
}

/* ---------- Kulissen-Auswahl (js/rooms.js) ---------- */

/* Vorschaubilder statt einer Liste mit Wörtern: Bei der einzigen Ware,
   die man vor dem Kauf nicht sehen kann, ist das Aussehen die Auskunft,
   auf die es ankommt. Drei Kacheln passen bei 96 Punkten Mindestbreite
   nebeneinander auf ein schmales Telefon; ab der vierten Kulisse bricht
   die Reihe von selbst um. */
.rooms-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 12px;
}

.room-tile {
  display: grid;
  gap: 6px;
  padding: 0;
  border: none;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  text-align: center;
}

.room-tile:active {
  transform: scale(0.97);
}

/* Hochkant wie die Bühne (0,6) – ein Kulissenbild im Querformat wäre
   ein anderer Ausschnitt als der, in dem die Katze später sitzt. */
.room-shot {
  position: relative;
  display: block;
  aspect-ratio: 3 / 5;
  border-radius: 14px;
  overflow: hidden;
  background: var(--card-strong);
  box-shadow: 0 1px 4px rgb(0 0 0 / 0.18);
}

.room-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.room-fallback {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 1.8rem;
}

/* Wo man wohnt, sagt der Rahmen und kein Wort: Auf 96 Punkten Breite
   verdrängte ein „hier" den Namen. Vorgelesen wird es über
   aria-current. */
.room-tile.is-here .room-shot {
  outline: 3px solid var(--know, #c98a5a);
  outline-offset: 2px;
}

.room-tile.is-here .room-name {
  font-weight: 800;
}

/* Gesperrt: blass, aber sichtbar – dieselbe Regel wie bei den
   Rassenkacheln im Designer. Man soll sehen, worauf man hinspielt. */
.room-tile.is-locked .room-shot img {
  opacity: 0.45;
  filter: grayscale(0.4);
}

.room-price {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  padding: 3px 8px;
  border-radius: 999px;
  background: rgb(0 0 0 / 0.55);
  color: #fff;
  font-size: 0.78rem;
  font-weight: 700;
  white-space: nowrap;
}

.room-name {
  font-size: 0.82rem;
  font-weight: 600;
  line-height: 1.2;
}

/* ---------- Für Tom (js/tom.js) ---------- */

/* Das Zeichen neben dem Namen. Klein, ungesättigt, ohne Rahmen und ohne
   Fläche: Es soll dastehen wie ein Satzzeichen und nicht wie ein Knopf.
   Ein Ostergeschenk, das blinkt, ist ein Werbebanner. */
.tom-mark {
  border: none;
  background: none;
  padding: 0 0 0 6px;
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
  opacity: 0.75;
  filter: saturate(0.6);
}

.tom-mark:active {
  transform: scale(0.9);
}

.tom-mark[hidden] {
  display: none;
}

.tom-inner {
  text-align: center;
}

/* Das Foto füllt die Blattbreite und bleibt hochkant. Kein Zuschnitt auf
   ein Quadrat: Es ist das einzige echte Bild in dieser App, und es soll
   ganz zu sehen sein. */
.tom-photo {
  display: block;
  width: 100%;
  max-width: 340px;
  height: auto;
  margin: 4px auto 18px;
  border-radius: 20px;
  box-shadow: 0 6px 20px rgba(74, 55, 40, 0.22);
}

#tom-title {
  font-size: 1.3rem;
}

.tom-text {
  margin-top: 10px;
  font-size: 0.95rem;
  line-height: 1.5;
}


/* ---------- Roter Punkt (js/game-laser.js) ---------- */

.lasergame {
  position: absolute;
  inset: 0;
  z-index: 3;
}

.laser-field {
  position: absolute;
  inset: 0;
  touch-action: none;   /* sonst scrollt die Seite unter dem Finger mit */
  cursor: none;
}

/* Der Punkt ist LICHT und kein gemaltes Ding: ein Verlauf mit Schein.
   Ein gerendertes rotes Kreischen sähe an dieser Stelle schlechter aus
   als drei Zeilen CSS – und wäre das erste Bild im Projekt, das etwas
   darstellt, das gar keine Farbe hat. */
.laser-dot {
  position: absolute;
  width: 22px;
  height: 22px;
  margin: -11px 0 0 -11px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%, #fff 0%, #ff4b4b 38%, #d61f1f 70%);
  box-shadow: 0 0 14px 6px rgba(255, 60, 60, 0.55);
  pointer-events: none;
}

.laser-dot[hidden] {
  display: none;
}

/* Der Ring zeigt, wie lange der Punkt noch da ist – die Uhr des
   einzelnen Ziels, ohne dass eine Zahl mitlaufen muss. Er schrumpft in
   genau der Zeit, die bleibt (--dwell setzt js/game-laser.js). */
/* Der Ring gehört zum STEHEN und nicht zum Fahren: Er zeigt, wie lange
   die Chance noch da ist. Unterwegs wäre er eine Uhr ohne Frist –
   deshalb unsichtbar, bis is-ticking ihn anwirft. */
.laser-dot::after {
  content: "";
  position: absolute;
  inset: -18px;
  border-radius: 50%;
  border: 3px solid rgba(255, 75, 75, 0.75);
  opacity: 0;
}

.laser-dot.is-ticking::after {
  animation: laser-tick var(--dwell, 1200ms) linear forwards;
}

@keyframes laser-tick {
  from { transform: scale(1);    opacity: 0.85; }
  to   { transform: scale(0.34); opacity: 0; }
}

/* Der Schluss der Runde: etwas Echtes zum Fangen. Warm statt grell –
   es ist Beute und kein Licht mehr. */
.laser-dot.is-treat {
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px;
  background: radial-gradient(circle at 42% 38%, #ffd9a6 0%, #e8963e 55%, #c9702c 100%);
  box-shadow: 0 0 16px 7px rgba(232, 150, 62, 0.5);
}

/* Die Pfote läuft VOR dem Finger (PAW_LEAD in js/game-laser.js) – läge
   sie darunter, deckte die eigene Hand genau das Ziel zu. */
.laser-paw {
  position: absolute;
  width: 104px;
  height: auto;
  margin: -34px 0 0 -52px;
  pointer-events: none;
  transition: transform 110ms ease-out;
  filter: drop-shadow(0 5px 9px rgba(74, 55, 40, 0.32));
}

.laser-paw.is-slapping {
  transform: scale(0.86);
}

.laser-streak {
  position: absolute;
  left: 0;
  right: 0;
  top: 74px;
  text-align: center;
  font-size: 0.95rem;
  font-weight: 800;
  color: var(--hunger);
  pointer-events: none;
  text-shadow: 0 1px 3px rgba(255, 255, 255, 0.85);
}
