:root {
  color-scheme: light;
  --bg: #f7f3ff;
  --card: #ffffff;
  --ink: #243047;
  --muted: #667085;
  --primary: #6c5ce7;
  --primary-dark: #5141c9;
  --line: #d9d6ee;
  --shadow: 0 20px 50px rgba(50, 44, 109, 0.16);
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

/* --- Kein Zoom ---------------------------------------------------------------
   Die App füllt das Bild und rechnet damit, dass sie es ganz hat: der Zug wird
   auf die Fensterbreite gemessen, die Spielfelder auf die Fensterhöhe. Ein
   Kind, das mit zwei Fingern hineinzieht, sieht danach einen Ausschnitt, in dem
   die Hälfte der Knöpfe ausserhalb liegt – und findet ohne Hilfe nicht mehr
   heraus. Deshalb: kein Zwei-Finger-Zoom und kein Doppeltipp-Zoom.

   Zwei Zeilen, weil kein Browser beides allein abdeckt. pan-x pan-y erlaubt das
   Wischen und Scrollen und nimmt nur das Auseinanderziehen weg; das <meta
   viewport> in jeder Seite sagt dasselbe noch einmal für die Browser, die auf
   touch-action am Wurzelelement nicht hören. Safari auf dem iPhone hört auf
   keines von beiden – dort fängt kids.js die gesture-Ereignisse ab. */
html {
  min-height: 100%;
  touch-action: pan-x pan-y;
  /* Safari auf dem iPhone und iPad vergrössert Text von sich aus, wenn es die
     Seite für zu klein hält – hier ist alles bewusst gross, und nichts soll
     hinter seinem Rahmen hinauswachsen. */
  -webkit-text-size-adjust: 100%;
  /* Und beim langen Drücken auf einen Link oder ein Bild kein Vorschau-Blatt:
     ein Kind hält den Finger länger auf dem Bild, nicht kürzer. */
  -webkit-touch-callout: none;
  /* Der graue Kasten, den ein Handy beim Antippen über das Getippte legt, hat
     hier nichts zu suchen. Bei einem Knopf sieht er nur unschön aus; über
     einer Station der Reisekarte oder einem Bauteil der Lok legt er sich als
     dunkler Balken über das halbe Bild – und zwar genau dann, wenn der Tipp
     sitzt. Ein Kind hält das für einen Fehler. Jede Fläche sagt ohnehin
     selbst, dass sie getroffen wurde: der Punkt wächst, das Bild wächst, der
     Bereich geht auf. */
  -webkit-tap-highlight-color: transparent;
}

/* --- Kein Rahmen nach einem Tipp ----------------------------------------------
   Browser malen um das zuletzt angetippte Ding einen fast schwarzen Rahmen.
   Bei einem Knopf sähe er nur unschön aus; die antippbaren Teile dieser App
   sind aber zum grossen Teil SVG-Gruppen – ein Wagen des Zugs, eine Station
   der Reisekarte, ein Bauteil der Lok –, und dort legt sich der Rahmen als
   Kasten über das halbe Bild und bleibt dort stehen, bis irgendwo anders
   hingetippt wird. Ein Kind hält das für einen Fehler.

   Zwei Regeln, weil eine allein nicht reicht. Diese hier nimmt den Rahmen
   überall weg, wo der Browser selbst weiss, dass nicht mit der Tastatur
   bedient wurde. Die zweite steht ganz unten und greift auch dort, wo er es
   nicht weiss: bei SVG-Gruppen malen die Browser den Rahmen schon auf :focus,
   obwohl :focus-visible nicht zutrifft. Was die Tastatur bekommt, bleibt in
   beiden Fällen stehen – die App setzt ihre eigenen Rahmen auf
   :focus-visible, und die sind genauer als diese Regel. */
:focus:not(:focus-visible) { outline: none; }

* { box-sizing: border-box; }

body {
  min-height: 100vh;
  min-height: 100dvh;
  margin: 0;
  touch-action: pan-x pan-y;
  padding: var(--safe-top) var(--safe-right) var(--safe-bottom) var(--safe-left);
  color: var(--ink);
  overflow-x: hidden;
  background:
    radial-gradient(circle at top left, rgba(255, 209, 102, 0.45), transparent 32rem),
    radial-gradient(circle at bottom right, rgba(108, 92, 231, 0.20), transparent 28rem),
    var(--bg);
}

button,
select { font: inherit; }

.app-shell {
  width: calc(100% - 32px);
  max-width: 980px;
  margin: 0 auto;
  /* Oben bleibt ein Streifen für die festen Knöpfe frei: Hilfe-Lautsprecher
     links, Ton und Konto rechts. */
  padding: calc(var(--safe-top) + 78px) 0 40px;
}

@media (display-mode: standalone), (display-mode: fullscreen) {
  .app-shell {
    padding-top: calc(var(--safe-top) + 72px);
    padding-bottom: 24px;
  }
}

html.standalone-mode .app-shell {
  padding-top: calc(var(--safe-top) + 72px);
  padding-bottom: 24px;
}

.hero {
  text-align: center;
  margin-bottom: 28px;
}

h1 {
  text-wrap: balance;
}

.eyebrow {
  margin: 0 0 8px;
  color: var(--primary-dark);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

h1 {
  margin: 0;
  font-size: clamp(2.2rem, 8vw, 4.8rem);
  line-height: 0.95;
}

.intro {
  max-width: 620px;
  margin: 18px auto 0;
  color: var(--muted);
  font-size: 1.12rem;
}

.controls,
.game-card {
  background: rgba(255, 255, 255, 0.84);
  border: 1px solid rgba(255, 255, 255, 0.75);
  box-shadow: var(--shadow);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}

.controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  border-radius: 24px;
  padding: 16px;
  margin-bottom: 20px;
}

.controls label {
  font-weight: 800;
}

select,
button {
  min-height: 44px;
  border-radius: 999px;
  border: 2px solid transparent;
  padding: 0 18px;
}

select {
  background: #fff;
  border-color: var(--line);
  color: var(--ink);
}

button {
  background: var(--primary);
  color: white;
  font-weight: 800;
  cursor: pointer;
  transition: transform 0.15s ease, background 0.15s ease;
}

:where(button:hover:not(:disabled)) {
  background: var(--primary-dark);
  transform: translateY(-1px);
}

button:disabled {
  cursor: not-allowed;
  opacity: 0.48;
}

.game-card {
  border-radius: 32px;
  padding: clamp(18px, 4vw, 32px);
}

.status-row {
  display: flex;
  gap: 16px;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 20px;
}

.small-label {
  margin: 0 0 4px;
  color: var(--muted);
  font-size: 0.86rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

h2 {
  margin: 0;
  font-size: clamp(1.5rem, 4vw, 2rem);
}

.status {
  max-width: 360px;
  margin: 0;
  color: var(--primary-dark);
  font-weight: 800;
  text-align: right;
}

.board {
  --size: 5;
  display: grid;
  grid-template-columns: repeat(var(--size), minmax(44px, 1fr));
  gap: 8px;
  max-width: min(680px, 100%);
  margin: 0 auto;
  touch-action: manipulation;
}

.cell {
  position: relative;
  aspect-ratio: 1;
  border: 2px solid var(--line);
  border-radius: 18px;
  background: #fbfaff;
  color: var(--ink);
  display: grid;
  place-items: center;
  font-size: clamp(1.2rem, 5vw, 2.3rem);
  font-weight: 900;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  transition: transform 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}

.cell:hover,
.cell:focus-visible {
  border-color: var(--primary);
  outline: none;
  transform: scale(1.02);
}

.arukone-board {
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

.arukone-cell {
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
}

.cell.endpoint {
  background: color-mix(in srgb, var(--pair-color) 18%, white);
  border-color: var(--pair-color);
}

.cell.filled {
  background: color-mix(in srgb, var(--pair-color) 30%, white);
  border-color: var(--pair-color);
}

.cell.active {
  box-shadow: inset 0 0 0 4px white, 0 0 0 4px var(--pair-color);
}

.cell.completed::after,
.cell.filled::after {
  content: "";
  position: absolute;
  inset: 40%;
  border-radius: 999px;
  background: var(--pair-color);
  opacity: 0.45;
}

.cell.endpoint::after { display: none; }

.success-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(36, 48, 71, 0.55);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  animation: fade-in 0.25s ease;
}

.success-overlay.hidden {
  display: none;
}

.success-modal {
  position: relative;
  overflow: hidden;
  width: min(420px, 100%);
  padding: clamp(30px, 6vw, 44px);
  border-radius: 28px;
  background:
    radial-gradient(circle at top left, rgba(255, 209, 102, 0.22), transparent 34%),
    radial-gradient(circle at bottom right, rgba(17, 138, 178, 0.16), transparent 38%),
    #fff;
  border: 3px solid #06d6a0;
  box-shadow: 0 24px 60px rgba(6, 214, 160, 0.28);
  text-align: center;
  animation: pop-in 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
}

#success-title {
  position: relative;
  z-index: 1;
  margin: 4px 0 12px;
  font-size: clamp(2.2rem, 9vw, 3.4rem);
  color: var(--ink);
}

.success-content {
  position: relative;
  z-index: 1;
  display: grid;
  gap: 8px;
  margin-bottom: 28px;
  color: var(--primary-dark);
  font-weight: 900;
}

.success-content p {
  margin: 0;
}

.success-content .success-summary {
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.success-content .success-summary.revealed {
  opacity: 1;
  transform: translateY(0);
}

.success-content .success-summary {
  color: var(--primary-dark);
  font-size: 1.1rem;
  line-height: 1.35;
}

.fireworks {
  position: absolute;
  inset: 14px 18px auto;
  height: 120px;
  pointer-events: none;
}

.fireworks span,
.fireworks span::before,
.fireworks span::after {
  position: absolute;
  width: 9px;
  height: 9px;
  border-radius: 999px;
  background: var(--spark-color, #ffd166);
  box-shadow:
    0 -30px 0 var(--spark-color, #ffd166),
    22px -22px 0 var(--spark-color, #ffd166),
    30px 0 0 var(--spark-color, #ffd166),
    22px 22px 0 var(--spark-color, #ffd166),
    0 30px 0 var(--spark-color, #ffd166),
    -22px 22px 0 var(--spark-color, #ffd166),
    -30px 0 0 var(--spark-color, #ffd166),
    -22px -22px 0 var(--spark-color, #ffd166);
  animation: firework-burst 1.35s ease-out infinite;
}

.fireworks span::before,
.fireworks span::after {
  content: "";
  inset: 0;
  transform: rotate(22deg);
}

.fireworks span::after {
  transform: rotate(45deg);
}

.fireworks span:nth-child(1) { left: 18%; top: 54px; --spark-color: #ef476f; }
.fireworks span:nth-child(2) { left: 48%; top: 26px; --spark-color: #ffd166; animation-delay: 0.22s; }
.fireworks span:nth-child(3) { right: 16%; top: 58px; --spark-color: #118ab2; animation-delay: 0.42s; }
.fireworks span:nth-child(4) { left: 32%; top: 82px; --spark-color: #06d6a0; animation-delay: 0.62s; }
.fireworks span:nth-child(5) { right: 32%; top: 88px; --spark-color: #8338ec; animation-delay: 0.82s; }
.fireworks span:nth-child(6) { left: 66%; top: 38px; --spark-color: #ff9f1c; animation-delay: 1s; }

.success-actions {
  position: relative;
  z-index: 1;
  display: flex;
  justify-content: space-between;
  gap: 18px;
}

.success-icon-button {
  width: 72px;
  height: 72px;
  border-radius: 24px;
  font-size: 2rem;
  line-height: 1;
  background: #fff;
  border: 3px solid var(--line);
  color: var(--primary-dark);
  box-shadow: 0 10px 22px rgba(36, 48, 71, 0.12);
}

.success-icon-button:hover:not(:disabled) {
  background: #f4f1ff;
  transform: translateY(-2px);
}

.success-icon-button.primary {
  margin-left: auto;
  background: #06d6a0;
  border-color: #06d6a0;
  color: #fff;
}

.success-icon-button.primary:hover:not(:disabled) {
  background: #05b888;
}

.cell.board-won.completed,
.cell.board-won.filled {
  animation: pulse-win 1.2s ease infinite;
}

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes pop-in {
  from {
    opacity: 0;
    transform: scale(0.85) translateY(12px);
  }
  to {
    opacity: 1;
    transform: scale(1) translateY(0);
  }
}

@keyframes pulse-win {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.03); }
}

@keyframes firework-burst {
  0% {
    opacity: 0;
    transform: scale(0.12);
  }
  18% {
    opacity: 1;
  }
  70% {
    opacity: 0.8;
  }
  100% {
    opacity: 0;
    transform: scale(1.05);
  }
}

@media (prefers-reduced-motion: reduce) {
  .success-overlay,
  .success-modal,
  .fireworks span,
  .fireworks span::before,
  .fireworks span::after {
    animation: none;
  }

  .success-content .success-summary,
  .success-content .success-summary.revealed {
    transform: none;
    transition: none;
  }
}

.start-panel {
  display: grid;
  gap: 18px;
  width: 100%;
  max-width: min(820px, 100%);
  margin: 0 auto;
  padding: clamp(22px, 5vw, 34px);
  border-radius: 28px;
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid rgba(255, 255, 255, 0.75);
  box-shadow: var(--shadow);
  text-align: center;
}

.start-copy {
  display: grid;
  gap: 12px;
}

.start-copy h2,
.start-copy p {
  margin: 0;
}

.start-panel label {
  font-size: clamp(1.25rem, 4vw, 1.65rem);
  font-weight: 900;
}

.start-panel select {
  width: 100%;
  min-height: 54px;
  text-align: center;
  font-weight: 800;
}

.start-panel p,
.puzzle-description {
  margin: 0;
  color: var(--muted);
}

.puzzle-description {
  max-width: 520px;
  margin-top: 6px;
  font-weight: 700;
}

body.puzzle-active {
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}

body.puzzle-active .app-shell {
  width: 100%;
  height: 100vh;
  height: 100dvh;
  padding: 10px;
  display: flex;
  flex-direction: column;
}

body.puzzle-active .hero,
body.puzzle-active #start-panel {
  display: none;
}

.game-panel {
  min-height: 0;
  flex: 1;
  display: grid;
  grid-template-rows: minmax(0, 1fr);
}

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

body.puzzle-active .controls {
  display: none;
}

body.puzzle-active .game-card {
  min-height: 0;
  height: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  /* Zwei feste Zeilen: die Titelzeile und das Spielfeld. Das Zahlenfeld
     kommt, wenn es da ist, als dritte Zeile von selbst dazu. Vorher standen
     hier vier Zeilen, zwei davon leer – und der Abstand zwischen den Zeilen
     zählt auch bei leeren. Das Spielfeld war dadurch ein paar Bildpunkte zu
     hoch für die Karte, und die Karte bekam einen Rollbalken. */
  grid-template-rows: max-content minmax(0, 1fr);
  grid-auto-rows: max-content;
  gap: clamp(6px, 1.2vh, 10px);
  justify-items: center;
  align-items: start;
  padding: clamp(8px, 1.8vh, 18px);
  /* Kein Rollbalken, nirgends: das Brett wird in die Karte eingepasst, und
     was trotzdem nicht passt, wird abgeschnitten statt gerollt. */
  overflow: hidden;
  overscroll-behavior: contain;
  border-radius: 22px;
}

body.puzzle-active .status-row {
  grid-row: 1;
  width: 100%;
  position: relative;
  z-index: 2;
  justify-content: center;
  margin-bottom: 0;
  text-align: center;
}

body.puzzle-active .small-label,
body.puzzle-active .puzzle-description,
body.puzzle-active .status {
  display: none;
}

body.puzzle-active #puzzle-title {
  display: inline-block;
  padding: 6px 12px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.92);
  color: var(--muted);
  font-size: clamp(0.86rem, 2.5vw, 1.05rem);
  font-weight: 900;
  letter-spacing: 0.03em;
  box-shadow: 0 6px 18px rgba(50, 44, 109, 0.08);
}

body.puzzle-active .board {
  grid-row: 2;
  width: var(--active-board-size, min(calc(100vw - 36px), calc(100vh - 78px)));
  width: var(--active-board-size, min(calc(100vw - 36px), calc(100dvh - 78px)));
  max-width: 860px;
  margin-top: var(--active-board-offset, 0px);
  /* Mittig im freien Bereich: quadratische Bretter kleben sonst oben und
     lassen auf hohen Handys die halbe Karte leer. Die Zahlentastatur liegt in
     einer eigenen Zeile darunter und wandert dadurch nicht mit. */
  align-self: center;
  touch-action: manipulation;
  grid-template-columns: repeat(var(--size), minmax(0, 1fr));
}

body.puzzle-active .arukone-board,
body.puzzle-active .hidoku-board,
body.puzzle-active .shikaku-board {
  touch-action: none;
}

body.puzzle-active .cell {
  min-width: 0;
  min-height: 0;
  padding: 0;
}

body.puzzle-active .number-pad {
  grid-row: 3;
  position: static;
  z-index: auto;
  align-self: start;
  max-width: 100%;
  max-height: 100%;
  overflow: hidden;
}
body.puzzle-active .task-board {
  width: min(calc(100vw - 36px), 780px);
  max-width: 100%;
  min-height: 0;
  max-height: 100%;
  overflow: hidden;
  overscroll-behavior: contain;
  margin-top: 0;
  /* Mittig statt oben angeheftet – sonst klafft auf hohen Handys eine
     riesige leere Fläche unter der Aufgabe. */
  align-self: center;
}

@media (max-width: 640px) {
  .app-shell { width: calc(100% - 20px); max-width: 980px; padding: calc(var(--safe-top) + 66px) 0 20px; }
  .controls { align-items: stretch; flex-direction: column; }
  .controls > * { width: 100%; }
  .status-row { flex-direction: column; }
  .status { text-align: left; }
  .board { gap: 5px; }
  .cell { border-radius: 12px; }

  body.puzzle-active .app-shell {
    padding: 6px;
  }

  body.puzzle-active .game-card {
    padding: 8px;
  }

  body.puzzle-active .board {
    width: var(--active-board-size, min(calc(100vw - 28px), calc(100vh - 64px)));
    width: var(--active-board-size, min(calc(100vw - 28px), calc(100dvh - 64px)));
    gap: 5px;
  }

}

.selection-grid {
  display: grid;
  gap: 16px;
}

.selection-grid > div {
  display: grid;
  gap: 8px;
}

.icon-button {
  width: 52px;
  min-width: 52px;
  min-height: 52px;
  padding: 0;
  display: inline-grid;
  place-items: center;
  font-size: 1.55rem;
  line-height: 1;
  box-shadow: 0 10px 24px rgba(108, 92, 231, 0.2);
}

body.puzzle-active .game-panel {
  grid-template-rows: auto minmax(0, 1fr);
  gap: 8px;
}

body.puzzle-active .controls {
  display: flex;
  justify-content: center;
  width: fit-content;
  max-width: calc(100vw - 20px);
  margin: 0 auto 4px;
  padding: 8px;
  border-radius: 999px;
  gap: 8px;
}

body.puzzle-active .icon-button {
  width: clamp(44px, 10vw, 54px);
  min-width: clamp(44px, 10vw, 54px);
  min-height: clamp(44px, 10vw, 54px);
  font-size: clamp(1.2rem, 5vw, 1.55rem);
}

body.puzzle-active .board {
  width: var(--active-board-size, min(calc(100vw - 36px), calc(100vh - 164px)));
  width: var(--active-board-size, min(calc(100vw - 36px), calc(100dvh - 164px)));
}

@media (max-width: 640px) {
  body.puzzle-active .controls {
    flex-direction: row;
    align-items: center;
  }

  body.puzzle-active .controls > * {
    width: clamp(44px, 10vw, 54px);
  }

  body.puzzle-active .board {
    width: var(--active-board-size, min(calc(100vw - 28px), calc(100vh - 154px)));
    width: var(--active-board-size, min(calc(100vw - 28px), calc(100dvh - 154px)));
  }
}

@media (orientation: landscape) and (max-height: 540px) {
  body.puzzle-active .app-shell {
    padding: 4px;
  }

  body.puzzle-active .game-panel {
    gap: 4px;
  }

  body.puzzle-active .controls {
    padding: 5px;
    gap: 6px;
    margin-bottom: 0;
  }

  body.puzzle-active .icon-button {
    width: clamp(38px, 8vh, 44px);
    min-width: clamp(38px, 8vh, 44px);
    min-height: clamp(38px, 8vh, 44px);
    font-size: clamp(1.05rem, 4vh, 1.28rem);
  }

  body.puzzle-active .game-card {
    padding: 6px;
    gap: 5px;
    border-radius: 18px;
  }

  body.puzzle-active #puzzle-title {
    padding: 4px 10px;
    font-size: 0.82rem;
  }

}

.line-dot {
  position: relative;
  background: #ffe4ec !important;
  border-color: #ef476f !important;
}

.line-dot::before {
  content: "";
  width: 70%;
  height: 8px;
  border-radius: 999px;
  background: #ef476f;
}

.line-dot.blue {
  background: #edf9fd !important;
  border-color: #118ab2 !important;
}

.line-dot.blue::before { background: #118ab2; }
.line-dot.green {
  background: #eafff9 !important;
  border-color: #06d6a0 !important;
}
.line-dot.green::before { background: #06d6a0; }

.level-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
}

.level-tile {
  flex: 0 1 132px;
  min-width: 108px;
  min-height: 112px;
  border-radius: 22px;
  display: grid;
  place-items: center;
  gap: 4px;
  padding: 12px;
  background: #fff;
  color: var(--ink);
  border: 3px solid var(--line);
  box-shadow: 0 12px 26px rgba(50, 44, 109, 0.10);
}

.level-tile span {
  display: grid;
  place-items: center;
  font-size: clamp(1.2rem, 5vw, 1.75rem);
  font-weight: 950;
  line-height: 1.05;
}

.level-tile small {
  color: var(--muted);
  font-weight: 850;
}

.level-tile small.lock-icon {
  font-size: 1.15rem;
  line-height: 1;
}

.level-tile.locked {
  border-style: dashed;
  background: #f7f7fb;
  box-shadow: none;
}

.level-tile.locked span,
.level-tile.locked small {
  color: #7b8190;
}

.level-tile.starter { border-color: #f4a7c3; }
.level-tile.easy { border-color: #06d6a0; }
.level-tile.medium { border-color: #ffd166; }
.level-tile.hard { border-color: #ef476f; }
.level-tile.extreme { border-color: #8338ec; }

.level-tile:hover:not(:disabled),
.level-tile:focus-visible {
  background: #f7f7fb;
  color: var(--ink);
  transform: translateY(-2px);
  outline: 4px solid color-mix(in srgb, var(--primary) 25%, transparent);
  outline-offset: 2px;
}

.number-pad[hidden] {
  display: none;
}

.number-pad {
  position: fixed;
  z-index: 20;
  display: grid;
  /* Spaltenzahl kommt aus dem Spiel (--pad-cols), damit z. B. vier Ziffern in
     einer Reihe stehen statt 3 + 1 mit Lücke. */
  grid-template-columns: repeat(var(--pad-cols, 3), minmax(48px, 62px));
  justify-content: center;
  gap: 8px;
  margin: 0;
  padding: 10px;
  border: 2px solid var(--primary);
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.97);
  box-shadow: 0 16px 36px rgba(36, 48, 71, 0.22);
}

.number-pad button {
  min-height: 52px;
  border-radius: 14px;
  font-size: 1.45rem;
}

.number-pad .clear-number-button {
  grid-column: 1 / -1;
  background: #ef476f;
  font-size: 1rem;
}

.number-pad .clear-number-button:hover:not(:disabled) {
  background: #b0002a;
}

body.puzzle-active .number-pad {
  margin: 0;
}

body.puzzle-active #level-panel{
  display: none;
}

@media (max-width: 640px) {
  .level-tile {
    flex: 0 1 132px;
    min-height: 94px;
  }
}

[hidden] {
  display: none !important;
}

a.icon-button {
  min-height: 44px;
  border-radius: 999px;
  border: 2px solid transparent;
  padding: 0 18px;
  background: var(--primary);
  color: white;
  font-weight: 800;
  cursor: pointer;
  text-decoration: none;
  transition: transform 0.15s ease, background 0.15s ease;
}

a.icon-button:hover,
a.icon-button:focus-visible {
  background: var(--primary-dark);
  transform: translateY(-1px);
  outline: none;
}

a.icon-button {
  padding: 0;
}

/* Erweiterungen für die grosse Rätselsammlung */

.difficulty-group { display: grid; gap: 10px; text-align: left; }
.difficulty-group h3 { margin: 8px 0 0; color: var(--primary-dark); font-size: 1.2rem; }
.difficulty-levels { display: grid; grid-template-columns: repeat(auto-fit, minmax(112px, 1fr)); gap: 12px; }
.level-tile.solved { position: relative; background: #f0fff9; }
.count-board {
  grid-template-columns: repeat(var(--size), minmax(34px, 1fr));
  gap: 5px;
  align-items: stretch;
}
.bimaru-count-board {
  --bimaru-size: 5;
  grid-template-columns: minmax(28px, 0.55fr) repeat(var(--bimaru-size), minmax(30px, 1fr)) minmax(52px, 0.9fr);
  grid-template-rows: minmax(28px, auto) repeat(var(--bimaru-size), minmax(30px, 1fr)) minmax(52px, auto);
  gap: 4px;
  align-items: stretch;
  justify-items: stretch;
  max-width: min(780px, 100%);
}
.count-label, .count-corner {
  min-height: 38px;
  display: grid;
  place-items: center;
  padding: 4px;
  border-radius: 12px;
  background: #fff4cf;
  border: 2px solid #ffd166;
  color: #8a5a00;
  font-weight: 950;
  font-size: clamp(0.82rem, 2.6vw, 1.15rem);
  text-align: center;
}
.count-label.complete {
  background: #e8f8ef;
  border-color: #34d399;
  color: #047857;
}
.count-label.over {
  background: #fff1f1;
  border-color: #ff7675;
  color: #b42318;
}
.bimaru-count-board .bimaru-cell {
  border-radius: 4px;
  border: 1px solid #d7dce8;
  background: #ffffff;
  color: #1f2937;
  padding: 0;
}
.bimaru-count-board .bimaru-cell:hover,
.bimaru-count-board .bimaru-cell:focus-visible {
  border-color: #111827;
  transform: none;
}
.bimaru-cell.ship {
  background: #f6f7f9;
  border-color: #9ca3af;
}
.bimaru-cell.given {
  background: #eef2f7;
  box-shadow: inset 0 0 0 2px #ffffff, 0 0 0 1px rgba(31, 41, 55, 0.18);
}
.bimaru-cell.conflict {
  background: #fff1f1;
  border-color: #ff7675;
}
.bimaru-cell.water::before,
.bimaru-cell.water::after {
  content: "";
  position: absolute;
  width: 70%;
  height: 2px;
  border-radius: 999px;
  background: #475569;
}
.bimaru-cell.water::before { transform: rotate(45deg); }
.bimaru-cell.water::after { transform: rotate(-45deg); }
.bimaru-ship-mark {
  display: block;
  width: 74%;
  height: 74%;
  place-self: center;
  background: #202124;
}
.bimaru-ship-mark.ship-single {
  border-radius: 999px;
}
.bimaru-ship-mark.ship-left,
.bimaru-ship-mark.ship-right,
.bimaru-ship-mark.ship-middle-horizontal {
  width: 92%;
  height: 52%;
}
.bimaru-ship-mark.ship-left {
  justify-self: end;
  border-radius: 999px 0 0 999px;
}
.bimaru-ship-mark.ship-right {
  justify-self: start;
  border-radius: 0 999px 999px 0;
}
.bimaru-ship-mark.ship-middle-horizontal {
  width: 100%;
  border-radius: 0;
}
.bimaru-ship-mark.ship-top,
.bimaru-ship-mark.ship-bottom,
.bimaru-ship-mark.ship-middle-vertical {
  width: 52%;
  height: 92%;
}
.bimaru-ship-mark.ship-top {
  align-self: end;
  border-radius: 999px 999px 0 0;
}
.bimaru-ship-mark.ship-bottom {
  align-self: start;
  border-radius: 0 0 999px 999px;
}
.bimaru-ship-mark.ship-middle-vertical {
  height: 100%;
  border-radius: 0;
}
.bimaru-ship-mark.ship-invalid {
  width: 72%;
  height: 72%;
  border-radius: 8px;
}
.bimaru-fleet-panel {
  display: flex;
  gap: 7px;
  align-items: center;
  justify-content: center;
  min-width: 0;
  min-height: 0;
}
.bimaru-fleet-panel.vertical {
  flex-direction: column;
}
.bimaru-fleet-panel.horizontal {
  flex-wrap: wrap;
  align-content: center;
}
.bimaru-fleet-ship {
  display: flex;
  gap: 2px;
  opacity: 0.58;
  transition: opacity 0.15s ease, filter 0.15s ease;
}
.bimaru-fleet-ship.vertical {
  flex-direction: column;
}
.bimaru-fleet-ship.placed {
  opacity: 1;
  filter: drop-shadow(0 0 0.35rem rgba(52, 211, 153, 0.36));
}
.bimaru-fleet-segment {
  display: block;
  width: clamp(11px, 2.6vw, 17px);
  height: clamp(11px, 2.6vw, 17px);
  background: #8a8d93;
}
.bimaru-fleet-segment.ship-single { border-radius: 999px; }
.bimaru-fleet-segment.ship-left { border-radius: 999px 0 0 999px; }
.bimaru-fleet-segment.ship-right { border-radius: 0 999px 999px 0; }
.bimaru-fleet-segment.ship-top { border-radius: 999px 999px 0 0; }
.bimaru-fleet-segment.ship-bottom { border-radius: 0 0 999px 999px; }
.bimaru-fleet-segment.ship-middle-horizontal,
.bimaru-fleet-segment.ship-middle-vertical { border-radius: 0; }

body.puzzle-active .count-board,
body.puzzle-active .bimaru-count-board {
  width: var(--active-board-size, min(calc(100vw - 36px), calc(100vh - 170px), 760px));
  width: var(--active-board-size, min(calc(100vw - 36px), calc(100dvh - 170px), 760px));
}

body.puzzle-active .bimaru-count-board {
  grid-template-columns: minmax(18px, 0.55fr) repeat(var(--bimaru-size), minmax(0, 1fr)) minmax(30px, 0.9fr);
  grid-template-rows: minmax(18px, auto) repeat(var(--bimaru-size), minmax(0, 1fr)) minmax(30px, auto);
  gap: 3px;
}

body.puzzle-active .count-label,
body.puzzle-active .count-corner {
  min-height: 0;
  padding: 2px;
  border-radius: 8px;
  font-size: clamp(0.64rem, 2.2vw, 1rem);
}

@media (max-width: 640px) {
  .difficulty-levels { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .count-board,
  .bimaru-count-board { gap: 3px; }
  .count-label, .count-corner { min-height: 30px; border-radius: 9px; padding: 2px; }
  .cell { min-width: 0; }
  .bimaru-count-board {
    grid-template-columns: minmax(24px, 0.5fr) repeat(var(--bimaru-size), minmax(26px, 1fr)) minmax(42px, 0.8fr);
    grid-template-rows: minmax(24px, auto) repeat(var(--bimaru-size), minmax(26px, 1fr)) minmax(44px, auto);
  }
  .bimaru-fleet-panel { gap: 4px; }
}

.selection-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
}

.selection-back-button {
  display: inline-grid;
  place-items: center;
  min-height: 50px;
  border-radius: 999px;
  border: 3px solid var(--primary);
  padding: 0 22px;
  background: #fff;
  color: var(--primary-dark);
  font-weight: 950;
  text-decoration: none;
  box-shadow: 0 10px 22px rgba(50, 44, 109, 0.12);
}

.selection-back-button:hover,
.selection-back-button:focus-visible {
  background: #f4f1ff;
  transform: translateY(-1px);
  outline: none;
}

.home-back-button {
  border-color: #06d6a0;
  color: #047c5d;
}

.difficulty-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(10px, 1.6vw, 18px);
}

/* Mit dem Garten sind es fünf Welten – nur auf der Stufe "leicht" (app.js,
   worldsFor). */
.difficulty-grid.has-garten { grid-template-columns: repeat(5, minmax(0, 1fr)); }

.difficulty-card {
  min-width: 0;
  min-height: 172px;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 6px;
  padding: clamp(10px, 1.6vw, 18px);
  border-radius: 26px;
  background: #fff;
  color: var(--ink);
  border: 5px solid var(--line);
  box-shadow: 0 18px 32px rgba(50, 44, 109, 0.12);
  text-align: center;
  overflow: hidden;
}

.difficulty-card.starter { border-color: #f4a7c3; background: #fff2f7; }
.difficulty-card.easy { border-color: #06d6a0; background: #effff8; }
.difficulty-card.medium { border-color: #ffd166; background: #fff8df; }
.difficulty-card.hard { border-color: #ef476f; background: #fff0f4; }
.difficulty-card.extreme { border-color: #8338ec; background: #f4edff; }

.difficulty-card.locked {
  border-style: dashed;
  background: #f7f7fb;
}

.difficulty-card:hover:not(:disabled),
.difficulty-card:focus-visible {
  color: var(--ink);
  filter: brightness(1.03);
  transform: translateY(-4px) scale(1.02);
  outline: 4px solid rgba(108, 92, 231, 0.22);
}

.difficulty-icon {
  width: clamp(56px, 11vw, 76px);
  height: clamp(56px, 11vw, 76px);
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.72);
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.9);
  font-size: clamp(2rem, 5vw, 3rem);
  line-height: 1;
}

.difficulty-name {
  min-width: 0;
  max-width: 100%;
  font-size: clamp(1.05rem, 2.6vw, 1.6rem);
  font-weight: 950;
  line-height: 1.08;
  overflow-wrap: anywhere;
  -webkit-hyphens: auto;
  hyphens: auto;
}

.difficulty-card small {
  min-width: 0;
  max-width: 100%;
  color: var(--muted);
  font-weight: 900;
  font-size: clamp(0.7rem, 1.5vw, 0.9rem);
  line-height: 1.2;
  overflow-wrap: anywhere;
}

@media (max-width: 760px) {
  .difficulty-grid {
    grid-template-columns: 1fr;
  }

  /* Quer statt hoch: Symbol links, Text rechts – und das Symbol über beide
     Textzeilen zentriert, damit nichts schief hängt. */
  .difficulty-card {
    min-height: 104px;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-rows: auto auto;
    column-gap: 16px;
    row-gap: 2px;
    justify-items: start;
    align-content: center;
    text-align: left;
    padding: 14px 18px;
  }

  .difficulty-icon {
    grid-row: 1 / span 2;
    grid-column: 1;
    align-self: center;
  }

  .difficulty-name {
    grid-column: 2;
    grid-row: 1;
    min-width: 0;
    align-self: end;
    overflow-wrap: anywhere;
  }

  .difficulty-card small {
    grid-column: 2;
    grid-row: 2;
    align-self: start;
  }
}

@media (max-width: 520px) {
  .selection-actions,
  .selection-back-button {
    width: 100%;
  }
}

button.selection-back-button:hover:not(:disabled),
button.selection-back-button:focus-visible {
  background: #f4f1ff;
  color: var(--primary-dark);
}

.kakuro-board {
  gap: 0;
  max-width: min(720px, 100%);
  border: 2px solid #222;
  background: #222;
}

.kakuro-cell {
  border: 1px solid #222;
  border-radius: 0;
  font-size: clamp(1.15rem, 4vw, 2rem);
}

.kakuro-entry {
  background: #fffdf8;
  color: var(--ink);
}

.kakuro-entry.selected {
  background: #fff1c2;
  box-shadow: inset 0 0 0 4px #ffd166;
}

.kakuro-entry.conflict {
  background: #ffe2e2;
  color: #b00020;
}

.kakuro-clue {
  cursor: default;
  background: #aeb3b0;
  overflow: hidden;
}

.kakuro-clue.has-clue::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(45deg, transparent calc(50% - 1px), #4a4a4a 50%, transparent calc(50% + 1px));
}

.kakuro-down,
.kakuro-across {
  position: absolute;
  z-index: 1;
  color: #111;
  font-size: clamp(0.62rem, 2.2vw, 1.05rem);
  font-weight: 800;
  line-height: 1;
}

.kakuro-down {
  left: 18%;
  bottom: 14%;
}

.kakuro-across {
  right: 14%;
  top: 14%;
}

.kakuro-cell:hover {
  transform: none;
}

.hidoku-board {
  gap: 0;
  max-width: min(720px, 100%);
  border: 3px solid #243047;
  background: #243047;
  border-radius: 18px;
  overflow: hidden;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

.hidoku-cell {
  border: 1px solid #243047;
  border-radius: 0;
  background: #ffffff;
  color: var(--ink);
  font-size: clamp(1.25rem, 5vw, 2.35rem);
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
}

.hidoku-cell.given {
  background: #eaf2ff;
  border-color: #3a86ff;
  color: #1f4ea3;
  cursor: default;
}

.hidoku-cell.selected {
  background: #fff1c2;
  box-shadow: inset 0 0 0 5px #ffd166;
}

.hidoku-cell.conflict {
  background: #ffe5ea;
  color: #b0002a;
}

.hidoku-cell:hover {
  transform: none;
}

.number-pad {
  grid-template-columns: repeat(var(--pad-cols, 3), minmax(48px, 62px));
}

.number-pad.hidoku-pad {
  grid-template-columns: repeat(var(--pad-cols, 4), minmax(42px, 56px));
  max-width: min(94vw, 420px);
  max-height: min(52vh, 360px);
  overflow: auto;
}

.number-pad.hidoku-pad button {
  min-height: 44px;
  font-size: 1.05rem;
}

body.puzzle-active .hidoku-board {
  width: var(--active-board-size, min(calc(100vw - 36px), calc(100vh - 190px), 760px));
  width: var(--active-board-size, min(calc(100vw - 36px), calc(100dvh - 190px), 760px));
}

@media (max-width: 640px) {
  .number-pad.hidoku-pad {
    grid-template-columns: repeat(var(--pad-cols, 4), minmax(36px, 1fr));
    gap: 5px;
    padding: 7px;
  }

  .number-pad.hidoku-pad button {
    min-height: 38px;
    padding: 0 8px;
    font-size: 0.95rem;
  }

  body.puzzle-active .hidoku-board {
    width: var(--active-board-size, min(calc(100vw - 28px), calc(100vh - 178px)));
    width: var(--active-board-size, min(calc(100vw - 28px), calc(100dvh - 178px)));
  }
}

.shikaku-board {
  gap: 4px;
  max-width: min(720px, 100%);
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

.shikaku-cell {
  border-radius: 12px;
  border-width: 2px;
  background: #fffdf7;
  font-size: clamp(0.95rem, 3.5vw, 1.55rem);
  display: flex;
  flex-direction: column;
  gap: 1px;
  align-items: center;
  justify-content: center;
  min-width: 0;
  -webkit-tap-highlight-color: transparent;
}

.shikaku-cell.clue {
  border-color: #5141c9;
  color: var(--ink);
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.86);
}

.shikaku-cell.selected {
  outline: 4px solid #ff9f1c;
  outline-offset: 2px;
  transform: scale(1.02);
}

.shikaku-cell.region {
  border-color: rgba(36, 48, 71, 0.46);
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.72);
}

.shikaku-animal {
  line-height: 1;
  font-size: 0.95em;
}

.shikaku-number {
  line-height: 1;
  padding: 1px 7px 2px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.86);
  font-weight: 950;
}

.shikaku-region-1 { background: #fff0c7; }
.shikaku-region-2 { background: #dff8ef; }
.shikaku-region-3 { background: #e6f0ff; }
.shikaku-region-4 { background: #ffe0e8; }
.shikaku-region-5 { background: #eee8ff; }
.shikaku-region-6 { background: #e5f9dc; }
.shikaku-region-7 { background: #ffead7; }
.shikaku-region-8 { background: #dff7fb; }
.shikaku-region-9 { background: #f9e4ff; }
.shikaku-region-10 { background: #edf2d4; }
.shikaku-region-11 { background: #f4e7d3; }
.shikaku-region-12 { background: #e3ecff; }

/* Während des Ziehens: das Rechteck, das gerade entsteht, ist orange, und
   sobald es genau so gross ist wie die Zahl verlangt, grün. So sieht ein Kind
   beim Ziehen, ob es loslassen kann. */
.shikaku-cell.shikaku-drag-preview {
  background: #ffe9bf;
  border-color: #ff9f1c;
  box-shadow: inset 0 0 0 3px rgba(255, 159, 28, 0.55);
}

.shikaku-cell.shikaku-drag-preview.valid {
  background: #c9f4e0;
  border-color: #06d6a0;
  box-shadow: inset 0 0 0 3px rgba(6, 214, 160, 0.65);
}

body.puzzle-active .shikaku-board {
  width: var(--active-board-size, min(calc(100vw - 32px), calc(100vh - 172px)));
  width: var(--active-board-size, min(calc(100vw - 32px), calc(100dvh - 172px)));
}

body.puzzle-active .shikaku-cell {
  border-radius: clamp(8px, 2vw, 14px);
}

@media (max-width: 640px) {
  .shikaku-board { gap: 3px; }
  body.puzzle-active .shikaku-board {
    width: var(--active-board-size, min(calc(100vw - 24px), calc(100vh - 160px)));
    width: var(--active-board-size, min(calc(100vw - 24px), calc(100dvh - 160px)));
  }
  .shikaku-cell {
    border-radius: 9px;
    font-size: clamp(0.8rem, 4vw, 1.2rem);
  }
  .shikaku-number { padding-inline: 5px; }
}

.task-board {
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px;
  max-width: min(780px, 100%);
  width: 100%;
}

body.puzzle-active .task-board {
  width: min(calc(100vw - 52px), 780px);
}

.practice-progress {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 10px;
}

.practice-progress span {
  min-height: 42px;
  display: grid;
  place-items: center;
  padding: 8px 10px;
  border-radius: 14px;
  background: #f4f1ff;
  border: 2px solid #d9d6ee;
  color: var(--primary-dark);
  font-weight: 900;
  text-align: center;
}

.practice-task {
  display: grid;
  justify-items: center;
  gap: 14px;
  text-align: center;
}

.practice-options {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(var(--option-count, 3), minmax(0, 1fr));
  gap: 12px;
}

.readingPuzzle-board .practice-options {
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}

.practice-option {
  --practice-option-min-font-size: 16px;
  display: grid;
  place-items: center;
  min-height: 74px;
  border-radius: 22px;
  border: 4px solid var(--line);
  padding: 10px 14px;
  background: #fff;
  color: var(--ink);
  box-shadow: 0 12px 24px rgba(36, 48, 71, 0.10);
  font-size: clamp(1.25rem, 4vw, var(--practice-option-max-font-size, 2rem));
  line-height: 1.12;
  white-space: normal;
  overflow-wrap: normal;
  word-break: normal;
  -webkit-hyphens: none;
  hyphens: none;
}

.readingPuzzle-board .practice-option {
  --practice-option-min-font-size: 13px;
  --practice-option-max-font-size: 1.7rem;
}

.practice-option:hover:not(:disabled),
.practice-option:focus-visible {
  background: #f4f1ff;
  color: var(--ink);
  outline: 4px solid rgba(108, 92, 231, 0.22);
}

.practice-option.correct,
.practice-option.correct:disabled {
  background: #dff8ef;
  border-color: #06d6a0;
  color: #047c5d;
}

.practice-option.wrong,
.practice-option.wrong:disabled {
  background: #ffe5ea;
  border-color: #ef476f;
  color: #9b1234;
}

.practice-option:disabled {
  opacity: 1;
}

.practice-feedback {
  min-height: 58px;
  display: grid;
  justify-items: center;
  gap: 10px;
  color: var(--primary-dark);
  font-weight: 900;
  text-align: center;
}

.practice-feedback p {
  margin: 0;
  font-size: clamp(1.1rem, 3.5vw, 1.45rem);
}

.practice-feedback strong {
  color: var(--ink);
  font-size: clamp(1.3rem, 4vw, 1.8rem);
}

/* Die Antwortkarten sind der Zweck der Aufgabe – sie dürfen nie unter dem
   Bildschirmrand liegen. Punktreihe, Karten und Rückmeldung stehen deshalb
   fest, und der Würfelbau oben nimmt, was übrig bleibt. */
.spatialPuzzle-board {
  gap: 16px;
  grid-template-rows: auto minmax(0, 1fr) auto auto;
  grid-template-areas:
    "punkte"
    "frage"
    "antworten"
    "hinweis";
}

.spatial-task {
  gap: 14px;
}

.spatialPuzzle-board .practice-progress-row { grid-area: punkte; }
.spatialPuzzle-board .spatial-task { grid-area: frage; }
.spatialPuzzle-board .spatial-options { grid-area: antworten; }
.spatialPuzzle-board .spatial-feedback { grid-area: hinweis; }

/* Die folgenden Regeln stehen mit body.puzzle-active davor, weil der
   Querformat-Block weiter unten dieselben Klassen anspricht: ohne das gewönne
   dort seine feste Mindesthöhe und der Würfelbau fiele aus dem Bild. */
body.puzzle-active .spatialPuzzle-board .spatial-task {
  min-height: 0;
  grid-template-rows: auto minmax(0, 1fr);
  width: 100%;
}

/* Der Rahmen legt sich um das Bild statt daneben: er ist so hoch wie die Zeile
   und nimmt sich die Breite dazu, statt als breites Band mit einem Würfelchen
   in der Mitte dazustehen. */
body.puzzle-active .spatialPuzzle-board .spatial-main-graphic {
  min-height: 0;
  height: 100%;
  width: auto;
  max-width: min(100%, 520px);
  aspect-ratio: 4 / 3;
  justify-self: center;
}

/* Die Antwortbilder bekommen eine feste, an der Bildschirmhöhe hängende Höhe:
   so bleibt die Frage oben immer das grösste Bild – sie ist das, was verglichen
   werden muss – und auf einem flachen Gerät bleibt trotzdem Platz für beides. */
body.puzzle-active .spatialPuzzle-board .spatial-option-graphic {
  min-height: 0;
  height: clamp(64px, 15vh, 140px);
}

/* Die Zeichnungen passen sich ihrer Kachel an, ohne sich zu verzerren: das
   viewBox sorgt mit dem voreingestellten "meet" dafür, dass sie hineinpassen. */
body.puzzle-active .spatialPuzzle-board .spatial-main-graphic > svg,
body.puzzle-active .spatialPuzzle-board .spatial-option-graphic > svg {
  width: 100%;
  height: 100%;
  max-height: none;
}

.spatial-prompt {
  margin: 0;
  color: var(--primary-dark);
  font-size: clamp(1.2rem, 3.8vw, 1.55rem);
  font-weight: 950;
  line-height: 1.2;
}

.spatial-main-graphic {
  width: min(100%, 520px);
  min-height: 220px;
  display: grid;
  place-items: center;
  padding: 18px;
  border: 4px solid #cfe3f4;
  border-radius: 22px;
  background:
    linear-gradient(180deg, #f9fbff 0%, #eef8ff 100%);
  box-shadow: 0 14px 28px rgba(36, 48, 71, 0.10);
}

.spatial-iso-svg {
  width: min(100%, 430px);
  max-height: 320px;
}

.spatial-iso-svg.compact {
  width: min(100%, 170px);
  max-height: 126px;
}

.spatial-colored-cube {
  width: min(100%, 300px);
  max-height: 230px;
}

.spatial-projection-svg,
.spatial-net-svg {
  width: min(100%, 170px);
  height: auto;
}

.spatial-face-mark {
  fill: #243047;
  font-weight: 950;
  paint-order: stroke;
  stroke: rgba(255, 255, 255, 0.72);
  stroke-width: 4px;
  stroke-linejoin: round;
}

.spatial-options {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(142px, 1fr));
  gap: 12px;
}

.spatial-option {
  min-height: 154px;
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  place-items: center;
  gap: 8px;
  border-radius: 18px;
  padding: 12px;
}

.spatial-option-graphic {
  width: 100%;
  min-height: 112px;
  display: grid;
  place-items: center;
}

.spatial-option-letter {
  min-width: 34px;
  min-height: 34px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: #f4f1ff;
  color: var(--primary-dark);
  border: 2px solid #d9d6ee;
  font-size: 1rem;
  font-weight: 950;
}

.spatial-feedback {
  min-height: 64px;
}

.practice-next-button {
  min-width: min(220px, 100%);
  min-height: 58px;
  border-radius: 18px;
}

@keyframes item-pack {
  0% { opacity: 1; transform: translateY(0) scale(1); }
  65% { opacity: 0.9; transform: translateY(-76px) scale(0.72); }
  100% { opacity: 0; transform: translateY(-140px) scale(0.32); }
}

@keyframes item-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-8px); }
  40% { transform: translateX(8px); }
  60% { transform: translateX(-6px); }
  80% { transform: translateX(6px); }
}

@media (max-width: 640px) {
}

@media (prefers-reduced-motion: reduce) {
}

@media (max-width: 640px) {
}

@media (max-width: 640px) {
}

@media (prefers-reduced-motion: reduce) {
}

.reading-image {
  width: min(180px, 42vw);
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  border-radius: 28px;
  background: #fff8ed;
  border: 4px solid #ffd166;
  box-shadow: 0 14px 26px rgba(36, 48, 71, 0.10);
  font-size: clamp(3.6rem, 16vw, 6.2rem);
  line-height: 1;
}

.reading-prompt {
  margin: 0;
  color: var(--primary-dark);
  font-size: clamp(1.15rem, 4vw, 1.55rem);
  font-weight: 900;
}

.letter-row,
.reading-display {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  max-width: 100%;
}

.letter-row span,
.reading-display span {
  min-width: 44px;
  min-height: 54px;
  display: grid;
  place-items: center;
  padding: 6px 10px;
  border-radius: 14px;
  background: #fff;
  border: 3px solid #d9d6ee;
  color: var(--ink);
  font-size: clamp(1.45rem, 6vw, 2.45rem);
  font-weight: 950;
}

.reading-display.sentence-gap span {
  min-width: 0;
  font-size: clamp(1.2rem, 4.6vw, 1.9rem);
}

@media (max-width: 640px) {
  body.puzzle-active .task-board {
    width: min(calc(100vw - 24px), 100%);
  }

  .mathPuzzle-board .practice-options {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .readingPuzzle-board .practice-options {
    grid-template-columns: 1fr;
  }

  .spatial-main-graphic {
    min-height: 190px;
    padding: 14px;
  }

  .spatial-options {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
  }

  .spatial-option {
    min-height: 140px;
    padding: 10px 8px;
  }

  .spatial-option-graphic {
    min-height: 96px;
  }

  .spatial-net-svg,
  .spatial-projection-svg {
    width: min(100%, 138px);
  }

  .spatial-iso-svg.compact {
    width: min(100%, 142px);
  }
}

@media (orientation: landscape) and (max-height: 540px) {
  body.puzzle-active.number-pad-open .game-card {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: max-content minmax(0, 1fr);
    column-gap: 8px;
  }

  body.puzzle-active.number-pad-open .status-row {
    grid-column: 1 / -1;
  }

  body.puzzle-active.number-pad-open .board {
    grid-column: 1;
    grid-row: 2;
    justify-self: end;
  }

  body.puzzle-active.number-pad-open .number-pad {
    grid-column: 2;
    grid-row: 2;
    align-self: start;
    justify-self: start;
    grid-template-columns: repeat(var(--pad-cols, 3), minmax(34px, 42px));
    gap: 5px;
    padding: 6px;
    border-radius: 14px;
  }

  body.puzzle-active.number-pad-open .number-pad button {
    min-height: 36px;
    padding: 0 6px;
    border-radius: 10px;
    font-size: 1rem;
  }

  body.puzzle-active.number-pad-open .number-pad .clear-number-button {
    font-size: 0.8rem;
  }

  body.puzzle-active .task-board {
    width: min(calc(100vw - 24px), 760px);
    gap: 10px;
  }

  body.puzzle-active .practice-progress {
    gap: 6px;
  }

  body.puzzle-active .practice-progress span {
    min-height: 30px;
    padding: 4px 8px;
    border-radius: 10px;
    font-size: 0.86rem;
  }

  body.puzzle-active .practice-task {
    gap: 8px;
  }

  body.puzzle-active .practice-options,
  body.puzzle-active .spatial-options {
    gap: 8px;
  }

  body.puzzle-active .practice-option {
    min-height: 54px;
    border-width: 3px;
    border-radius: 16px;
    padding: 8px 10px;
    font-size: clamp(1rem, 5vh, 1.35rem);
  }

  body.puzzle-active .practice-feedback {
    min-height: 36px;
    gap: 6px;
  }

  body.puzzle-active .practice-feedback p {
    font-size: 0.95rem;
  }

  body.puzzle-active .practice-feedback strong {
    font-size: 1.1rem;
  }

  body.puzzle-active .spatialPuzzle-board,
  body.puzzle-active .spatial-task {
    gap: 8px;
  }

  body.puzzle-active .spatial-prompt {
    font-size: 0.98rem;
  }

  body.puzzle-active .spatial-main-graphic {
    min-height: 130px;
    padding: 8px;
    border-width: 3px;
    border-radius: 16px;
  }

  /* Auf einem flachen Bildschirm ist die Höhe knapp, die Breite nicht: der
     Würfelbau steht links, die Antwortkarten daneben. Untereinander bliebe für
     das Bild, um das es geht, kaum etwas übrig. */
  body.puzzle-active .spatialPuzzle-board {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
    grid-template-rows: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "punkte  punkte"
      "frage   antworten"
      "hinweis hinweis";
    align-items: stretch;
  }

  body.puzzle-active .spatial-options {
    grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
    align-content: center;
  }

  body.puzzle-active .spatial-option {
    min-height: 98px;
    padding: 6px;
    border-radius: 14px;
  }

  body.puzzle-active .spatial-option-graphic {
    min-height: 62px;
  }

  body.puzzle-active .spatial-iso-svg {
    width: min(100%, 260px);
    max-height: 150px;
  }

  body.puzzle-active .spatial-colored-cube {
    width: min(100%, 180px);
    max-height: 135px;
  }

  body.puzzle-active .spatial-net-svg,
  body.puzzle-active .spatial-projection-svg,
  body.puzzle-active .spatial-iso-svg.compact {
    width: min(100%, 112px);
  }

  body.puzzle-active .reading-image {
    width: 90px;
    border-width: 3px;
    border-radius: 18px;
    font-size: 3rem;
  }

  body.puzzle-active .letter-row,
  body.puzzle-active .reading-display {
    gap: 5px;
  }

  body.puzzle-active .letter-row span,
  body.puzzle-active .reading-display span {
    min-width: 32px;
    min-height: 38px;
    padding: 4px 7px;
    border-width: 2px;
    border-radius: 10px;
    font-size: clamp(1.05rem, 5vh, 1.45rem);
  }
}

.sound-toggle,
.account-button {
  position: fixed;
  top: calc(var(--safe-top) + 14px);
  z-index: 90;
  width: 52px;
  min-width: 52px;
  min-height: 52px;
  padding: 0;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: #fff;
  border: 3px solid var(--primary);
  color: var(--primary-dark);
  box-shadow: 0 12px 28px rgba(36, 48, 71, 0.18);
}

.account-button {
  right: calc(var(--safe-right) + 14px);
}

.sound-toggle {
  right: calc(var(--safe-right) + 76px);
  z-index: 91;
}

.sound-toggle svg {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.sound-toggle .sound-off-line {
  opacity: 0;
  transition: opacity 0.18s ease;
}

.sound-toggle .sound-wave {
  transition: opacity 0.18s ease;
}

.sound-toggle.muted {
  background: #fff7f7;
  border-color: #ef476f;
  color: #b4234a;
}

.sound-toggle.muted .sound-off-line {
  opacity: 1;
}

.sound-toggle.muted .sound-wave {
  opacity: 0.22;
}

.sound-toggle:hover:not(:disabled),
.sound-toggle:focus-visible,
.account-button:hover:not(:disabled),
.account-button:focus-visible {
  background: #f4f1ff;
  color: var(--primary-dark);
  outline: none;
}

.sound-toggle.muted:hover:not(:disabled),
.sound-toggle.muted:focus-visible {
  background: #ffe7ec;
  color: #9f1738;
}

.account-button svg,
.account-close svg {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.account-button.signed-in {
  background: #06d6a0;
  border-color: #06d6a0;
  color: #fff;
}

.account-button.signed-in svg {
  opacity: 0;
}

.account-initial {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 1.1rem;
  font-weight: 950;
}

.account-modal {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: grid;
  place-items: center;
  padding: calc(var(--safe-top) + 18px) calc(var(--safe-right) + 18px) calc(var(--safe-bottom) + 18px) calc(var(--safe-left) + 18px);
  background: rgba(36, 48, 71, 0.56);
  -webkit-backdrop-filter: blur(7px);
  backdrop-filter: blur(7px);
}

.account-modal.hidden {
  display: none;
}

.account-panel {
  position: relative;
  width: min(720px, 100%);
  max-height: min(820px, calc(100vh - var(--safe-top) - var(--safe-bottom) - 36px));
  max-height: min(820px, calc(100dvh - var(--safe-top) - var(--safe-bottom) - 36px));
  overflow: auto;
  padding: clamp(22px, 4vw, 34px);
  border-radius: 28px;
  background: rgba(255, 255, 255, 0.98);
  border: 1px solid rgba(255, 255, 255, 0.78);
  box-shadow: 0 26px 70px rgba(36, 48, 71, 0.28);
}

.account-panel.has-admin {
  width: min(1120px, 100%);
}

.account-close {
  position: absolute;
  top: 14px;
  right: 14px;
  width: 42px;
  min-width: 42px;
  min-height: 42px;
  padding: 0;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: #fff;
  color: var(--muted);
  border: 2px solid var(--line);
}

.account-close:hover:not(:disabled),
.account-close:focus-visible {
  background: #f4f1ff;
  color: var(--primary-dark);
}

.account-content {
  display: grid;
  gap: 18px;
}

.account-content h2,
.account-content h3,
.account-content p {
  margin: 0;
}

.account-muted {
  color: var(--muted);
  font-weight: 700;
}

/* Zwei Wege ins Konto: das Kind mit Name und Passwort, die Eltern mit ihrer
   E-Mail-Adresse. Die Reiter stehen über dem Formular; der aktive ist gefüllt.
   Gross genug für einen Daumen, und die Rolle als Tab, damit ein Screenreader
   die Wahl versteht. */
.auth-tabs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  padding: 6px;
  border-radius: 18px;
  background: #f1eefc;
  margin-bottom: 14px;
}
.auth-tab {
  min-height: 46px;
  border: 0;
  border-radius: 13px;
  background: transparent;
  color: var(--primary-dark);
  font: inherit;
  font-weight: 900;
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
}
.auth-tab[aria-selected="true"] {
  background: #fff;
  color: var(--primary-dark);
  box-shadow: 0 4px 12px rgba(36, 48, 71, 0.12);
}
.auth-tab:focus-visible {
  outline: 4px solid rgba(108, 92, 231, 0.2);
}
.auth-tab small {
  display: block;
  font-weight: 700;
  font-size: 0.78rem;
  color: var(--muted);
}
.auth-pane[hidden] { display: none; }
.auth-hint {
  margin: 0;
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.4;
}
.auth-hint button {
  background: none;
  border: 0;
  padding: 0;
  color: var(--primary-dark);
  font: inherit;
  font-weight: 900;
  text-decoration: underline;
  cursor: pointer;
  min-height: 0;
}
.auth-link {
  background: none;
  border: 0;
  padding: 6px 0;
  color: var(--primary-dark);
  font: inherit;
  font-weight: 800;
  text-decoration: underline;
  cursor: pointer;
  justify-self: start;
  min-height: 0;
}
.auth-status.is-ok { color: #1e7f4e; }
/* "Weiterleitung zur Zahlung..." ist keine Fehlermeldung. In der roten
   Grundfarbe von .auth-status sah sie aber aus wie eine – mitten im Kauf,
   genau dort, wo man am ehesten glaubt, es sei etwas schiefgegangen.
   Was gerade läuft, steht deshalb ruhig da. */
.auth-status.is-laeuft { color: var(--muted, #667085); }

.auth-form {
  display: grid;
  gap: 14px;
}

.auth-form label {
  display: grid;
  gap: 7px;
  color: var(--muted);
  font-weight: 850;
}

.auth-form input {
  width: 100%;
  min-height: 50px;
  border-radius: 16px;
  border: 2px solid var(--line);
  padding: 0 14px;
  color: var(--ink);
  font: inherit;
  font-weight: 800;
  background: #fff;
}

.auth-form input:focus-visible {
  border-color: var(--primary);
  outline: 4px solid rgba(108, 92, 231, 0.16);
}

.auth-actions,
.profile-summary {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
}

.secondary-action,
.google-action {
  background: #fff;
  color: var(--primary-dark);
  border-color: var(--line);
}

.secondary-action:hover:not(:disabled),
.secondary-action:focus-visible,
.google-action:hover:not(:disabled),
.google-action:focus-visible {
  background: #f4f1ff;
  color: var(--primary-dark);
}

.google-action {
  width: 100%;
}

.auth-status {
  min-height: 1.4em;
  color: #b0002a;
  font-weight: 800;
}

/* Am Fuss der Anmeldung: die Willkommensseite und die Rechtstexte. Klein,
   weil es die Eltern lesen, nicht das Kind – aber von jeder Seite der App
   aus erreichbar, wie es das Impressum verlangt. */
.auth-rechtliches {
  margin: 6px 0 0;
  text-align: center;
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.6;
}

.auth-rechtliches a {
  color: var(--primary-dark);
  font-weight: 800;
}

.profile-summary {
  padding: 16px;
  border-radius: 20px;
  background: #fbfaff;
  border: 2px solid var(--line);
}

.profile-summary > div {
  display: grid;
  gap: 4px;
  min-width: 0;
}

.profile-summary strong {
  overflow-wrap: anywhere;
  font-size: 1.05rem;
}

.profile-summary small {
  color: var(--muted);
  font-weight: 800;
}

.unlock-mode-card {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  border-radius: 18px;
  background: #fff;
  border: 2px solid var(--line);
  box-shadow: 0 10px 22px rgba(50, 44, 109, 0.08);
}

.unlock-mode-card.active {
  border-color: #06d6a0;
  background: #f0fff9;
}

.unlock-mode-card > div {
  display: grid;
  gap: 4px;
  min-width: 0;
}

.unlock-mode-card strong {
  color: var(--ink);
}

.unlock-mode-card span {
  color: var(--muted);
  font-weight: 800;
}

/* Der Kauf. Zurück von der Kasse steht ein Satz über den Karten: grün, wenn
   die Zahlung angekommen ist, sonst gelb – die Kasse wurde nur geschlossen. */
.kauf-hinweis {
  padding: 12px 16px;
  border-radius: 16px;
  background: #fff6d6;
  border: 2px solid #ffd166;
  color: var(--ink);
  font-weight: 800;
  line-height: 1.4;
}

.kauf-hinweis.is-ok {
  background: #f0fff9;
  border-color: #06d6a0;
}

/* Der Verkaufsbildschirm hinter dem Elterntor (firebase.js, renderKaufSeite).
   Aufgebaut wie die Preiskarte auf willkommen.html, nur in ein Fenster
   gefaltet: oben der Betrag, dann fünf Zeilen, was dazugehört, dann drei
   Versprechen – und darunter, abgetrennt, der Weg an die Kasse. Wer vor der
   Schranke stand, soll in einem Blick sehen, was es kostet, und im zweiten,
   wo er tippt. */
.kauf-seite {
  display: grid;
  gap: 14px;
}

.kauf-preis {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 12px;
  margin: 0;
}

.kauf-preis strong {
  font-size: clamp(2rem, 7vw, 2.6rem);
  font-weight: 950;
  line-height: 1;
  color: var(--ink);
}

.kauf-preis span {
  color: var(--muted);
  font-weight: 800;
}

/* Haken statt Punkte: Was dazugehört, ist eine Liste von Zusagen, keine
   Aufzählung. Zwei Spalten, sobald das Fenster sie hergibt – fünf Zeilen
   untereinander schöben die Anmeldung aus dem Bild. */
.kauf-vorteile {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 4px 18px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.kauf-vorteile li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 9px;
  align-items: baseline;
  color: var(--ink);
  font-size: 0.97rem;
  font-weight: 800;
}

.kauf-vorteile li::before {
  content: "✓";
  color: #1e7f4e;
  font-weight: 950;
}

.kauf-versprechen {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
}

.kauf-versprechen span {
  padding: 6px 12px;
  border-radius: 999px;
  background: #f4f1ff;
  border: 2px solid var(--line);
  color: var(--primary-dark);
  font-size: 0.9rem;
  font-weight: 850;
}

/* Die Anmeldung: derselbe Block wie sonst, nur mit einem Strich darüber –
   hier hört das Angebot auf und fängt das Tun an. */
.kauf-anmeldung {
  display: grid;
  gap: 12px;
  margin-top: 4px;
  padding-top: 16px;
  border-top: 2px solid var(--line);
}

/* Der Hauptknopf zuerst und so breit wie möglich: Er ist der Weg, der
   zweite Knopf nur die Abzweigung für die, die schon ein Konto haben. */
.kauf-aktionen {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 10px;
  justify-content: stretch;
}

.kauf-aktionen button {
  width: 100%;
}

.kauf-knopf {
  width: 100%;
}

.kauf-frei {
  margin: 0;
  color: var(--muted);
  font-weight: 800;
}

/* Die Kinder. Die Karte baut untereinander statt nebeneinander: der Kopf,
   die Liste, darunter der Knopf oder das Formular. Eine Zeile je Kind, mit dem
   Namen links und "Passwort neu" rechts; wer tippt, bekommt an derselben
   Stelle das Feld. */
.kinder-karte {
  display: grid;
  gap: 12px;
}

.kinder-kopf,
.karten-kopf {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 12px;
}

.kinder-liste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 8px;
}

.kind-zeile {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 12px;
  padding: 10px 12px;
  border-radius: 14px;
  background: #f7f6ff;
}

.kind-zeile.kind-leer {
  color: var(--muted);
  font-weight: 800;
  line-height: 1.4;
}

.kind-name {
  min-width: 0;
  overflow-wrap: anywhere;
  color: var(--ink);
  font-weight: 900;
}

.kind-form {
  flex: 1 1 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.kind-form input {
  flex: 1 1 150px;
  min-width: 0;
  min-height: 46px;
  border-radius: 14px;
  border: 2px solid var(--line);
  padding: 0 12px;
  color: var(--ink);
  font: inherit;
  font-weight: 800;
  background: #fff;
}

.kind-form input:focus-visible {
  border-color: var(--primary);
  outline: 4px solid rgba(108, 92, 231, 0.16);
}

/* Das Formular für ein neues Kind: zwei Felder untereinander, die Knöpfe
   rechts darunter – wie die Anmeldung, nur kleiner. */
.kind-form-neu {
  display: grid;
  gap: 12px;
}

.kind-form-neu label {
  display: grid;
  gap: 7px;
  color: var(--muted);
  font-weight: 850;
}

.kind-form-neu input {
  width: 100%;
}

.kind-form-neu > .card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  justify-content: flex-end;
}

/* Die Altersgruppe beim Anlegen: drei Kacheln mit Radio, eine ist gewählt.
   Der Kreis bleibt sichtbar – so ist die Wahl auch ohne :has() zu sehen. */
.kind-stufe-wahl { margin: 0; padding: 0; border: 0; min-width: 0; }
.kind-stufe-wahl legend { padding: 0; margin-bottom: 8px; color: var(--muted); font-weight: 850; }
.kind-stufe-knoepfe { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.kind-form-neu .kind-stufe-wahl label {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 2px solid var(--line);
  border-radius: 14px;
  background: #fff;
  color: var(--ink);
  cursor: pointer;
}
.kind-form-neu .kind-stufe-wahl input {
  width: 22px;
  height: 22px;
  min-height: 0;
  margin: 0;
  padding: 0;
  flex: none;
  accent-color: var(--primary);
}
.kind-stufe-wahl label:has(input:checked) { border-color: var(--primary); background: rgba(108, 92, 231, 0.08); }
.kind-stufe-wahl label:has(input:focus-visible) { outline: 4px solid rgba(108, 92, 231, 0.16); }
.kind-stufe-wahl label > span { display: grid; gap: 2px; min-width: 0; }
.kind-stufe-wahl strong { font-size: 1rem; font-weight: 900; }
.kind-stufe-wahl small { color: var(--muted); font-weight: 750; font-size: 0.8rem; }
@media (max-width: 460px) { .kind-stufe-knoepfe { grid-template-columns: 1fr; } }

/* Die Rückmeldung einer Karte: in der Karte, unter dem, was sie betrifft.
   Leer nimmt sie keinen Platz. */
.karten-status {
  flex: 1 1 100%;
  margin: 0;
}

.karten-status:empty {
  display: none;
}

/* Zurücksetzen lässt sich nicht rückgängig machen. Rot statt Violett, damit
   die Karte nicht wie der Levelmodus direkt darüber aussieht – und in der
   Rückfrage steht der Satz über den Knöpfen, nicht neben ihnen: gelesen werden
   soll er, nicht überklickt. */
.danger-action {
  background: #ef476f;
  color: #fff;
  border-color: transparent;
}

.danger-action:hover:not(:disabled),
.danger-action:focus-visible {
  background: #d42a55;
}

.admin-reset {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  border-radius: 18px;
  background: #fff;
  border: 2px solid var(--line);
  box-shadow: 0 10px 22px rgba(50, 44, 109, 0.08);
}

.admin-reset > div {
  display: grid;
  gap: 4px;
  min-width: 0;
}

.admin-reset strong {
  color: var(--ink);
}

.admin-reset span {
  color: var(--muted);
  font-weight: 800;
}

/* Die Schwierigkeitsstufe: derselbe Kasten, drei Knöpfe, der gültige ist markiert. */
.admin-stufe .card-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.admin-stufe button[aria-pressed="true"] { box-shadow: inset 0 0 0 3px rgba(255, 255, 255, 0.5); }

.reset-card.is-confirming,
.admin-reset.is-confirming {
  align-items: stretch;
  flex-direction: column;
  border-color: #ef476f;
  background: #fff5f7;
}

.reset-card.is-confirming span,
.admin-reset.is-confirming span {
  color: var(--ink);
  font-weight: 700;
  line-height: 1.45;
}

/* --- Das Wagen-Set ---
   Zwei Karten, je ein Zug: das gültige Set ist markiert, das andere lässt
   sich wählen. Die Rückfrage steht wie beim Zurücksetzen über den Knöpfen und
   in Rot – ein Wechsel setzt alle Wagen aller Konten zurück. */
.admin-sets {
  display: grid;
  gap: 12px;
}

.admin-set {
  display: grid;
  gap: 10px;
  padding: 14px 16px;
  border-radius: 18px;
  background: #fff;
  border: 2px solid var(--line);
  box-shadow: 0 10px 22px rgba(50, 44, 109, 0.08);
}

.admin-set.is-active { border-color: #06d6a0; }
.admin-set.is-confirming { border-color: #ef476f; background: #fff5f7; }

.admin-set header > div {
  display: grid;
  gap: 4px;
}

.admin-set header strong { color: var(--ink); }
.admin-set header span { color: var(--muted); font-weight: 800; font-size: 0.86rem; }

.admin-set-preview {
  padding: 8px 10px 2px;
  border-radius: 14px;
  background: #eef6fb;
}
.admin-set-preview svg { display: block; width: 100%; height: auto; }

.admin-set-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
}

.admin-set-active {
  padding: 6px 14px;
  border-radius: 999px;
  background: #e3fff5;
  color: #0c8f6a;
  font-weight: 900;
  font-size: 0.86rem;
}

.admin-set-confirm {
  width: 100%;
  display: grid;
  gap: 8px;
}
.admin-set-confirm strong { color: var(--ink); }
.admin-set-confirm span { color: var(--ink); font-weight: 700; line-height: 1.45; }
.admin-set-confirm > .card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: flex-end;
}

.admin-set-done { color: #0c8f6a; }

/* Die Knopfzeile steht als div in der Karte und liefe sonst in
   ".unlock-mode-card > div" – dort gilt Grid, und die beiden Knöpfe stünden
   untereinander. */
.unlock-mode-card > .card-actions,
.admin-reset > .card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  justify-content: flex-end;
}

/* Die Gruppe eines Kontos: mit wem sein Zug das Startbild teilt und unter
   welchem Namen er dort steht. Dieselbe Karte wie beim Zurücksetzen, nur mit
   zwei Feldern statt einer Rückfrage. */
.admin-group {
  display: grid;
  gap: 12px;
  padding: 14px 16px;
  border-radius: 18px;
  background: #fff;
  border: 2px solid var(--line);
  box-shadow: 0 10px 22px rgba(50, 44, 109, 0.08);
}

.admin-group > div:first-child {
  display: grid;
  gap: 4px;
  min-width: 0;
}

.admin-group strong { color: var(--ink); }
.admin-group span { color: var(--muted); font-weight: 800; }

.admin-group-fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 10px;
}

.admin-group-fields label {
  display: grid;
  gap: 4px;
  min-width: 0;
}

.admin-group-fields label span {
  font-size: 0.82rem;
  color: var(--muted);
}

.admin-group-fields input {
  width: 100%;
  min-width: 0;
  padding: 10px 12px;
  border-radius: 12px;
  border: 2px solid var(--line);
  background: #fff;
  color: var(--ink);
  font: inherit;
  font-weight: 700;
}

.admin-group-fields input:focus-visible {
  outline: none;
  border-color: var(--primary);
}

.admin-group > .card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  justify-content: flex-end;
}

.stat-strip {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
}

.stat-strip > div,
.progress-card,
.session-item {
  border-radius: 18px;
  background: #fff;
  border: 2px solid var(--line);
  box-shadow: 0 10px 22px rgba(50, 44, 109, 0.08);
}

.stat-strip > div {
  display: grid;
  gap: 4px;
  min-height: 84px;
  place-items: center;
  padding: 12px;
  text-align: center;
}

.stat-strip strong {
  font-size: 1.35rem;
  line-height: 1;
  color: var(--primary-dark);
}

.stat-strip span,
.progress-card span,
.progress-card small,
.session-item span,
.session-item small {
  color: var(--muted);
  font-weight: 800;
}

.progress-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}

.progress-card {
  display: grid;
  gap: 10px;
  padding: 14px;
}

.progress-card > div:first-child,
.session-item {
  display: flex;
  justify-content: space-between;
  gap: 12px;
}

.progress-card strong,
.session-item strong {
  color: var(--ink);
}

.progress-bar {
  height: 12px;
  overflow: hidden;
  border-radius: 999px;
  background: #ece8fb;
}

.progress-bar span {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, #06d6a0, #118ab2);
}

.session-list {
  display: grid;
  gap: 10px;
}

.session-list h3 {
  font-size: 1.05rem;
}

.session-item {
  align-items: center;
  padding: 12px 14px;
}

.session-item > div {
  display: grid;
  gap: 3px;
  min-width: 0;
}

.session-item small {
  text-align: right;
}

.admin-columns h4,
.admin-top-levels h4,
.admin-train-detail h4 {
  margin: 0;
}

/* --- Die Konten zum Aufklappen ---
   Zugeklappt steht je Konto eine Zeile: Name, Gruppe, der Zug als fünf Balken
   und die drei Zahlen, nach denen man zuerst schaut. Alles Weitere erscheint
   erst beim Aufklappen. Vorher lagen alle Konten nebeneinander und alle
   Einzelheiten daneben – bei einem Dutzend Kindern war das keine Übersicht. */

.admin-accordion,
.admin-data-list {
  display: grid;
  gap: 10px;
}

.admin-entry {
  border-radius: 16px;
  background: #fff;
  border: 2px solid var(--line);
  box-shadow: 0 10px 22px rgba(50, 44, 109, 0.08);
  overflow: hidden;
}

.admin-entry.is-open {
  border-color: #06d6a0;
}

/* Oben die Zeile: Pfeil, Name, Gruppe, die drei Zahlen. Darunter über die
   ganze Breite der Zug. Die Plätze stehen fest statt sich zu ergeben – ohne
   Gruppe rutschten die Zahlen sonst eine Spalte nach links und stünden bei
   jedem Konto woanders. */
.admin-entry-head {
  width: 100%;
  min-height: 0;
  display: grid;
  grid-template-columns: 22px minmax(120px, 1fr) auto auto;
  align-items: center;
  gap: 10px 14px;
  padding: 12px;
  border: none;
  border-radius: 0;
  background: transparent;
  color: var(--ink);
  text-align: left;
}

.admin-entry-caret { grid-column: 1; grid-row: 1; }
.admin-entry-name { grid-column: 2; grid-row: 1; }
.admin-entry-group { grid-column: 3; grid-row: 1; }
.admin-entry-head .admin-user-summary { grid-column: 4; grid-row: 1; }
.admin-entry-head .admin-train { grid-column: 1 / -1; grid-row: 2; }

.admin-entry-head:hover:not(:disabled),
.admin-entry-head:focus-visible {
  background: #f0fff9;
  color: var(--ink);
  outline: none;
}

.admin-entry-caret {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  justify-self: center;
  color: var(--muted);
  transition: transform 0.18s ease;
}
.admin-entry-caret svg { width: 18px; height: 18px; display: block; }
.admin-entry.is-open .admin-entry-caret { transform: rotate(90deg); }

.admin-entry-name {
  min-width: 0;
  display: grid;
  gap: 2px;
}

.admin-entry-name strong {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.admin-entry-name span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--muted);
  font-size: 0.8rem;
  font-weight: 800;
}

.admin-entry-group {
  justify-self: start;
  padding: 3px 10px;
  border-radius: 999px;
  background: #eef0ff;
  color: var(--primary-dark);
  font-size: 0.72rem;
  font-weight: 900;
}

.admin-entry-body {
  min-width: 0;
  display: grid;
  gap: 14px;
  padding: 4px 12px 14px;
  border-top: 2px solid var(--line);
}

/* Der Zug als fünf Balken: dieselbe Reihenfolge und dieselben Farben wie auf
   dem Startbild. Die Zahl über dem Balken ist die Ausbaustufe des Wagens, der
   Balken selbst der Anteil im Bereich – die Stufe allein sagt nicht, ob bis
   zur nächsten zwei Level fehlen oder zwanzig. */
.admin-train {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 6px;
}

.admin-train-car {
  min-width: 0;
  display: grid;
  justify-items: center;
  gap: 3px;
}

.admin-train-car b {
  color: var(--wagen);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
}

.admin-train-car i {
  width: 100%;
  height: 7px;
  border-radius: 999px;
  background: rgba(36, 48, 71, 0.12);
  overflow: hidden;
}

.admin-train-car s {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--wagen);
}

.admin-train-car em {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--muted);
  font-style: normal;
  font-size: 0.62rem;
  font-weight: 800;
}

.admin-train-detail {
  display: grid;
  gap: 10px;
  padding: 12px;
  border-radius: 16px;
  background: #fff;
  border: 2px solid var(--line);
  box-shadow: 0 10px 22px rgba(50, 44, 109, 0.08);
}

.admin-train-detail > div {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 8px;
}

.admin-train-detail span {
  min-width: 0;
  display: grid;
  gap: 4px;
  padding: 9px 10px;
  border-radius: 12px;
  background: #fbfaff;
}

.admin-train-detail b { color: var(--ink); font-size: 0.86rem; }
.admin-train-detail i {
  height: 8px;
  border-radius: 999px;
  background: rgba(36, 48, 71, 0.12);
  overflow: hidden;
}
.admin-train-detail s { display: block; height: 100%; border-radius: 999px; background: var(--wagen); }
.admin-train-detail em {
  color: var(--muted);
  font-style: normal;
  font-size: 0.74rem;
  font-weight: 800;
}

.admin-user-summary,
.admin-data-card > span {
  color: var(--muted);
  font-weight: 800;
}

.admin-user-summary {
  display: grid;
  grid-template-columns: repeat(3, minmax(68px, auto));
  gap: 6px;
}

.admin-user-summary > span {
  display: grid;
  gap: 2px;
  padding: 7px;
  border-radius: 10px;
  background: #fbfaff;
  color: var(--ink);
  font-size: 0.82rem;
  text-align: center;
}

.admin-user-summary b {
  color: var(--muted);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.admin-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  color: var(--muted);
  font-weight: 800;
  font-size: 0.82rem;
  overflow-wrap: anywhere;
}

/* --- Auswertung je Spiel ---
   Nicht "was hat dieses Kind gemacht", sondern "was ist mit diesem Spiel los".
   Sortiert nach gespielten Runden: die erste Frage an so eine Liste ist,
   welches Spiel überhaupt angekommen ist. */

.admin-game-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(252px, 1fr));
  gap: 10px;
}

/* "Abgeschlossen" passt knapp nicht in eine halbe Karte. Getrennt wird es mit
   Bindestrich statt mitten im Wort umbrochen – die Seite ist auf Deutsch
   ausgezeichnet, der Browser weiss also, wo. */
.admin-game-card .admin-data-grid span {
  overflow-wrap: normal;
  -webkit-hyphens: auto;
  hyphens: auto;
}

.admin-game-card {
  min-width: 0;
  display: grid;
  align-content: start;
  gap: 8px;
  padding: 12px;
  border-radius: 16px;
  background: #fff;
  border: 2px solid var(--line);
  box-shadow: 0 10px 22px rgba(50, 44, 109, 0.08);
}

/* Ein Spiel, das noch niemand angefasst hat, tritt zurück – es steht in der
   Liste, damit sichtbar bleibt, dass es niemand anfasst. */
.admin-game-card.is-quiet { opacity: 0.66; }

.admin-game-card header {
  display: grid;
  gap: 2px;
}

.admin-game-card header strong { color: var(--ink); }
.admin-game-card header span {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.admin-game-best {
  margin: 0;
  padding: 8px 10px;
  border-radius: 12px;
  background: #f0fff9;
  color: var(--ink);
  font-size: 0.86rem;
  font-weight: 800;
}

.admin-game-best b {
  display: block;
  color: var(--muted);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.admin-game-best em {
  color: var(--muted);
  font-style: normal;
  font-weight: 800;
}

.admin-game-card small { color: var(--muted); font-weight: 800; font-size: 0.76rem; }

.admin-note { font-size: 0.78rem; line-height: 1.5; }

.admin-stat-strip > div {
  min-height: 72px;
}

.admin-top-levels {
  display: grid;
  gap: 10px;
  padding: 12px;
  border-radius: 16px;
  background: #fff;
  border: 2px solid var(--line);
  box-shadow: 0 10px 22px rgba(50, 44, 109, 0.08);
}

.admin-top-levels > div {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}

.admin-top-levels span {
  min-width: 0;
  display: grid;
  gap: 3px;
  padding: 9px 10px;
  border-radius: 12px;
  background: #f0fff9;
  color: var(--muted);
  font-weight: 800;
}

.admin-top-levels b {
  color: var(--ink);
  overflow-wrap: anywhere;
}

.admin-game-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.admin-game-filter button {
  min-height: 38px;
  padding: 0 12px;
  border-radius: 999px;
  background: #fff;
  color: var(--primary-dark);
  border-color: var(--line);
  font-size: 0.9rem;
}

.admin-game-filter button.active,
.admin-game-filter button:hover:not(:disabled),
.admin-game-filter button:focus-visible {
  background: var(--primary);
  color: #fff;
  border-color: var(--primary);
  outline: none;
}

.admin-columns {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
}

.admin-columns section {
  min-width: 0;
  display: grid;
  align-content: start;
  gap: 10px;
}

.admin-data-list {
  max-height: 560px;
  overflow: auto;
  padding-right: 2px;
}

.admin-data-card {
  display: grid;
  gap: 6px;
  padding: 12px;
  border-radius: 16px;
  background: #fff;
  border: 2px solid var(--line);
  box-shadow: 0 10px 22px rgba(50, 44, 109, 0.08);
}

.admin-data-card.solved {
  border-color: #06d6a0;
  background: #f0fff9;
}

.admin-data-card.abandoned {
  border-color: #ffd166;
  background: #fff8df;
}

.admin-data-card.open {
  border-style: dashed;
}

.admin-data-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px;
}

.admin-data-grid span {
  min-width: 0;
  display: grid;
  gap: 2px;
  padding: 7px 8px;
  border-radius: 10px;
  background: #fbfaff;
  color: var(--ink);
  font-weight: 850;
  overflow-wrap: anywhere;
}

.admin-data-grid b {
  color: var(--muted);
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

@media (max-width: 900px) {
  .account-panel.has-admin .admin-columns,
  .account-panel.has-admin .admin-top-levels > div,
  .account-panel.has-admin .admin-game-grid {
    grid-template-columns: 1fr;
  }

  .account-panel.has-admin .admin-data-list {
    max-height: none;
    padding-right: 0;
  }

  /* Eng wird die Zeile untereinander gelesen: Name, Gruppe, Zahlen, Zug.
     Der Zug bleibt dabei fünfspaltig – fünf Wagen untereinander wären keine
     Zeile mehr, sondern eine zweite Liste. */
  .account-panel.has-admin .admin-entry-head {
    grid-template-columns: 22px minmax(0, 1fr);
  }

  .account-panel.has-admin .admin-entry-group,
  .account-panel.has-admin .admin-entry-head .admin-user-summary,
  .account-panel.has-admin .admin-entry-head .admin-train {
    grid-column: 1 / -1;
    grid-row: auto;
  }

  .account-panel.has-admin .admin-user-summary {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (max-width: 640px) {
  .sound-toggle,
  .account-button {
    top: calc(var(--safe-top) + 10px);
    width: 46px;
    min-width: 46px;
    min-height: 46px;
  }

  .account-button {
    right: calc(var(--safe-right) + 10px);
  }

  .sound-toggle {
    right: calc(var(--safe-right) + 64px);
  }

  .account-panel {
    border-radius: 22px;
  }

  .auth-actions,
  .profile-summary,
  .unlock-mode-card,
  .admin-reset,
  .progress-card > div:first-child,
  .session-item {
    align-items: stretch;
    flex-direction: column;
  }

  .auth-actions > *,
  .profile-summary > button,
  .unlock-mode-card > button,
  .admin-reset > button,
  .card-actions > button {
    width: 100%;
  }

  .stat-strip,
  .progress-list {
    grid-template-columns: 1fr;
  }

  .admin-columns,
  .admin-data-grid,
  .admin-top-levels > div,
  .admin-game-grid {
    grid-template-columns: 1fr;
  }

  .admin-data-list {
    max-height: none;
    padding-right: 0;
  }

  .admin-train-car em { display: none; }

  .session-item small {
    text-align: left;
  }
}

/* =====================================================================
   Kinder-Erweiterungen (Sterne, Feier, Zoo, Abenteuer, neue Spiele …)
   ===================================================================== */

/* --- Sterne --- */
.star-row { display: inline-flex; gap: 6px; justify-content: center; align-items: center; }
.star-row .star { font-size: 2rem; line-height: 1; color: #ffc93c; }
.star-row .star.empty { color: #d9d6ee; }
.star-row-small { gap: 2px; }
.star-row-small .star { font-size: 1rem; }
.level-tile .star-row .star { font-size: 1rem; font-weight: 400; }
.star.to-reveal { opacity: 0; transform: scale(0.2) rotate(-40deg); }
.star.to-reveal.pop { animation: star-pop 0.5s cubic-bezier(0.2, 1.4, 0.5, 1) forwards; }
@keyframes star-pop {
  0% { opacity: 0; transform: scale(0.2) rotate(-40deg); }
  70% { opacity: 1; transform: scale(1.35) rotate(8deg); }
  100% { opacity: 1; transform: scale(1) rotate(0); }
}

/* --- Konfetti --- */
.confetti-layer { position: absolute; inset: 0; overflow: visible; pointer-events: none; z-index: 5; }
.confetti-piece {
  position: absolute; left: 50%; top: 42%;
  width: 11px; height: 14px; border-radius: 3px;
  opacity: 0; will-change: transform, opacity;
  animation: confetti-fly var(--dur, 1.4s) ease-out var(--delay, 0s) forwards;
}
@keyframes confetti-fly {
  0% { opacity: 1; transform: translate(0, 0) rotate(0); }
  100% { opacity: 0; transform: translate(var(--dx, 0), calc(var(--dy, -140px) * -1 + 220px)) rotate(var(--rot, 360deg)); }
}

/* --- Maskottchen --- */
.mascot { width: 100%; height: 100%; display: block; }
.success-mascot { width: clamp(76px, 22vw, 108px); margin: 0 auto; }
.success-mascot .mascot { animation: mascot-bounce 1.1s ease-in-out infinite; }
@keyframes mascot-bounce { 0%, 100% { transform: translateY(0) rotate(-2deg); } 50% { transform: translateY(-8px) rotate(2deg); } }
.kids-modal-mascot { width: clamp(70px, 20vw, 96px); margin: 0 auto 6px; }

/* --- Erfolgsdialog Zusatz --- */
.success-note { margin: 4px 0 0; font-weight: 800; color: var(--primary-dark); }
.success-stars { margin-top: 2px; }
.success-text-button {
  flex: 1; min-height: 60px; padding: 0 14px; border-radius: 20px; border: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  white-space: nowrap; line-height: 1;
  font-size: clamp(1rem, 3.6vw, 1.15rem); font-weight: 900; color: var(--primary-dark);
  background: #eee9ff; transition: transform 0.1s ease, filter 0.15s ease;
}
.success-text-button.primary { background: linear-gradient(120deg, #6c5ce7, #8338ec); color: #fff; }
.success-text-button:hover:not(:disabled) { filter: brightness(1.05); }
.success-text-button:active { transform: scale(0.96); }

/* --- Generische Kinder-Modals (Tutorial, Bestätigung, Truhe) --- */
.kids-modal-overlay {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center; padding: 20px;
  background: rgba(36, 30, 60, 0.55); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  animation: kids-fade 0.2s ease;
}
@keyframes kids-fade { from { opacity: 0; } to { opacity: 1; } }
.kids-modal {
  position: relative; width: min(440px, 100%); max-height: 90vh; overflow: auto;
  background: #fff; border-radius: 28px; padding: clamp(22px, 5vw, 34px);
  text-align: center; box-shadow: 0 26px 60px rgba(50, 44, 109, 0.34);
  border: 3px solid #6c5ce7; animation: kids-pop 0.28s cubic-bezier(0.2, 1.3, 0.5, 1);
}
@keyframes kids-pop { from { transform: scale(0.85); opacity: 0; } to { transform: scale(1); opacity: 1; } }
.kids-modal h2 { margin: 0 0 10px; font-size: clamp(1.4rem, 5vw, 1.9rem); }
.kids-modal p { margin: 6px 0; color: var(--muted); font-weight: 700; }
.kids-modal-actions { display: flex; gap: 12px; margin-top: 18px; justify-content: center; flex-wrap: wrap; }
.kids-modal-primary, .kids-modal-secondary {
  min-height: 58px; padding: 0 22px; border-radius: 18px; border: none; cursor: pointer;
  font-size: 1.1rem; font-weight: 900; flex: 1; min-width: 130px; text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center;
}
.kids-modal-primary { background: linear-gradient(120deg, #6c5ce7, #8338ec); color: #fff; }
.kids-modal-secondary { background: #eee9ff; color: var(--primary-dark); }
.kids-modal-primary:hover:not(:disabled) { filter: brightness(1.07); }
.kids-modal-secondary:hover:not(:disabled) { background: #e3daff; }
.kids-modal-primary:active, .kids-modal-secondary:active { transform: scale(0.96); }

/* Tutorial */

/* Truhe / Zoo-Reveal */

/* --- Home: Topbar und Kacheln --- */
/* Rundes Feld hinter dem Emoji: alle vier Karten bekommen damit denselben
   optischen Anker, egal wie breit das Zeichen ist. */

/* --- Vorlesen / Aufgaben-Fortschritt --- */
.practice-progress-row { display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: wrap; }
.practice-dots { display: inline-flex; gap: 6px; }
.practice-dot { width: 14px; height: 14px; border-radius: 50%; background: #e2def4; }
.practice-dot.done { background: #06d6a0; }
.practice-dot.current { background: #6c5ce7; box-shadow: 0 0 0 4px rgba(108, 92, 231, 0.18); }

/* --- Hinweis: ausgegraute / blinkende Antwortkarten --- */
.practice-option.dimmed { opacity: 0.28; filter: grayscale(0.6); }
.practice-option.hint { animation: option-hint 0.9s ease-in-out infinite; border-color: #06d6a0 !important; }
@keyframes option-hint { 0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(6, 214, 160, 0.5); } 50% { transform: scale(1.04); box-shadow: 0 0 0 8px rgba(6, 214, 160, 0); } }

/* --- Zählzauber --- */
.count-task { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.count-question { font-size: 1.4rem; font-weight: 900; margin: 0; color: var(--primary-dark); }
.count-objects {
  display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; align-items: center;
  max-width: 460px; padding: 10px; background: #faf8ff; border-radius: 20px; border: 2px dashed var(--line);
}
.count-object { font-size: clamp(2rem, 9vw, 3rem); line-height: 1; animation: count-pop 0.3s ease both; }
@keyframes count-pop { from { transform: scale(0.4); opacity: 0; } to { transform: scale(1); opacity: 1; } }

/* --- Buchstaben-Jagd --- */
.letter-task { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.letter-image { font-size: clamp(3.4rem, 16vw, 5.4rem); line-height: 1; }
/* Zum Buchstaben das Wort: der Buchstabe steht gross, wo sonst das Bild ist. */
.letter-image.is-letter { font-weight: 950; color: var(--primary-dark); letter-spacing: 0.02em; }
.letter-word { font-size: 1.6rem; font-weight: 900; margin: 0; letter-spacing: 0.06em; color: var(--ink); }
.letter-question { font-size: 1.15rem; font-weight: 800; margin: 0; color: var(--primary-dark); }
.letterPuzzle-options .practice-option { font-size: 2rem; font-weight: 950; }

/* --- Home-Vorschaubilder für neue Spiele --- */

/* --- Mein Zoo --- */

/* --- Abenteuer-Modus --- */
@keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-6px); } 75% { transform: translateX(6px); } }

@media (prefers-reduced-motion: reduce) {
  .success-mascot .mascot, .count-object, .star.to-reveal.pop,
  .practice-option.hint,
  .confetti-piece, .kids-modal { animation: none; }
  .star.to-reveal { opacity: 1; transform: none; }
}

/* ===========================================================================
   Tier-Sprung – Jump-and-Run
   =========================================================================== */

/* --- Startkarte, Levelkarte und Hilfe (normale Seite) --- */
.runner-start-card { max-width: 640px; margin: 0 auto 22px; text-align: center; }
.runner-tagline { margin: 0 0 18px; color: var(--muted); font-size: 1.05rem; line-height: 1.55; }
.runner-start {
  display: inline-flex; align-items: center; gap: 14px; padding: 15px 26px;
  border: none; border-radius: 24px; cursor: pointer; color: #fff;
  background: linear-gradient(120deg, #ff9f1c, #ef476f);
  font-weight: 900; font-size: 1.1rem; line-height: 1.15;
  box-shadow: 0 16px 34px rgba(239, 71, 111, 0.32);
  transition: transform 0.12s ease;
}
.runner-start:active { transform: scale(0.97); }
.runner-start-emoji { font-size: 2.1rem; }
.runner-start-text { display: flex; flex-direction: column; align-items: flex-start; }
.runner-start-text small { font-weight: 700; opacity: 0.92; }
.runner-summary { margin: 14px 0 0; color: var(--muted); font-weight: 700; }
.runner-summary strong { color: var(--primary-dark); }

.runner-map {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: 12px; max-width: 720px; margin: 0 auto 24px;
}
.runner-level {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 16px 10px 12px; border-radius: 22px; text-align: center;
  background: #fff; border: 2px solid var(--line); color: var(--ink); font: inherit;
  box-shadow: 0 12px 26px rgba(50, 44, 109, 0.1);
  transition: transform 0.12s ease;
}
button.runner-level { cursor: pointer; }
button.runner-level:active { transform: scale(0.97); }
.runner-level.locked { background: #f4f2fb; border-color: #e6e2f4; box-shadow: none; color: var(--muted); }
.runner-level.fresh {
  border-color: #ffd166;
  box-shadow: 0 0 0 4px rgba(255, 209, 102, 0.28), 0 12px 26px rgba(50, 44, 109, 0.12);
}
.runner-level-badge {
  position: absolute; top: 8px; left: 10px; width: 26px; height: 26px; border-radius: 50%;
  background: var(--primary); color: #fff; font-size: 0.82rem; font-weight: 900;
  display: grid; place-items: center;
}
.runner-level.locked .runner-level-badge { background: #c6c2dd; }
.runner-level-art-slot { display: block; height: 84px; }
.runner-level-art { display: block; }
.runner-level-name { font-weight: 900; font-size: 1.05rem; }
.runner-level-place { font-size: 0.78rem; font-weight: 700; color: var(--muted); }
.runner-level-stars { letter-spacing: 2px; font-size: 1.05rem; color: #e2def4; }
.runner-level-star.filled { color: #ffd166; }
.runner-level-treat { font-size: 0.8rem; font-weight: 800; color: var(--primary-dark); min-height: 1.1em; }

.runner-help {
  max-width: 640px; margin: 0 auto; padding: 18px 24px; border-radius: 24px;
  background: rgba(255, 255, 255, 0.84); border: 1px solid rgba(255, 255, 255, 0.75);
  box-shadow: var(--shadow);
}
.runner-help h2 { margin: 0 0 10px; font-size: 1.15rem; }
.runner-help ul { margin: 0; padding-left: 20px; color: var(--muted); line-height: 1.65; }
.runner-help strong { color: var(--ink); }

/* --- Spielfläche (deckt beim Spielen den ganzen Bildschirm) --- */
body.runner-playing { overflow: hidden; }
.runner-stage {
  position: fixed; inset: 0; z-index: 200; background: #8fd6ff; outline: none;
  touch-action: none; overscroll-behavior: none;
  user-select: none; -webkit-user-select: none; -webkit-tap-highlight-color: transparent;
}
.runner-stage[hidden] { display: none; }
.runner-canvas { display: block; width: 100%; height: 100%; touch-action: none; }

.runner-hud {
  position: absolute; top: 0; left: 0; right: 0; z-index: 2;
  display: flex; flex-direction: column; gap: 6px;
  padding: calc(10px + env(safe-area-inset-top, 0px)) calc(14px + env(safe-area-inset-right, 0px))
           10px calc(14px + env(safe-area-inset-left, 0px));
  pointer-events: none;
}
.runner-hud-row { display: flex; align-items: center; gap: 10px; }
.runner-hud-button {
  pointer-events: auto; width: 42px; height: 42px; flex: none;
  border: none; border-radius: 14px; background: rgba(255, 255, 255, 0.86);
  color: #2f3b52; font-size: 1rem; font-weight: 900; line-height: 1; cursor: pointer;
  box-shadow: 0 6px 18px rgba(30, 40, 60, 0.2);
}
.runner-hud-button:active { transform: scale(0.94); }
.runner-hud-button + .runner-hud-button { margin-left: 0; }
#runner-fullscreen { margin-left: auto; }
.runner-hearts { display: inline-flex; gap: 2px; font-size: 1.2rem; line-height: 1; }
.runner-heart.lost { opacity: 0.55; }
.runner-treats {
  margin: 0; display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 13px; border-radius: 999px; background: rgba(255, 255, 255, 0.86);
  font-weight: 900; color: #2f3b52; box-shadow: 0 6px 18px rgba(30, 40, 60, 0.16);
}

.runner-progress { position: relative; padding: 4px 14px 18px; }
.runner-progress-track {
  position: relative; display: block; height: 10px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.65);
  box-shadow: inset 0 1px 3px rgba(30, 40, 60, 0.16);
}
.runner-progress-fill {
  display: block; height: 100%; width: 0; border-radius: 999px;
  background: linear-gradient(90deg, #06d6a0, #ffd166);
}
.runner-progress-marker {
  position: absolute; top: -9px; left: 0; transform: translateX(-50%);
  font-size: 1.2rem; line-height: 1; pointer-events: none;
}
.runner-stage-label {
  position: absolute; left: 16px; top: 19px; font-size: 0.76rem; font-weight: 800;
  color: rgba(35, 45, 65, 0.75); text-shadow: 0 1px 0 rgba(255, 255, 255, 0.55);
}

/* --- Dialoge im Spiel --- */
.runner-overlay {
  position: absolute; inset: 0; z-index: 3; display: grid; place-items: center;
  padding: 20px; background: rgba(24, 30, 48, 0.52); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
}
.runner-overlay[hidden] { display: none; }
.runner-dialog {
  position: relative; width: min(430px, 100%); padding: 24px 22px;
  border-radius: 28px; background: #fff; text-align: center;
  box-shadow: 0 26px 64px rgba(20, 20, 50, 0.38);
  animation: runner-dialog-in 0.28s cubic-bezier(0.2, 1.2, 0.4, 1);
}
@keyframes runner-dialog-in { from { transform: translateY(18px) scale(0.94); opacity: 0; } to { transform: none; opacity: 1; } }
.runner-dialog h2 { margin: 4px 0 0; font-size: clamp(1.4rem, 6vw, 1.9rem); line-height: 1.15; }
.runner-dialog-eyebrow {
  margin: 0; color: var(--primary-dark); font-weight: 800;
  letter-spacing: 0.08em; text-transform: uppercase; font-size: 0.78rem;
}
.runner-dialog-sub { margin: 10px 0 0; color: var(--muted); font-weight: 700; }
.runner-mascot { width: clamp(72px, 22vw, 100px); margin: 0 auto 2px; }
.runner-grow { margin: 12px 0 0; font-weight: 800; }
.runner-grow strong { color: var(--primary-dark); }
.runner-stars { display: flex; justify-content: center; gap: 10px; margin: 14px 0 2px; }
.runner-star { font-size: 2.5rem; line-height: 1; color: #e6e2f4; }
.runner-star.filled {
  color: #ffd166; text-shadow: 0 4px 12px rgba(255, 176, 32, 0.4);
  animation: runner-star-pop 0.5s cubic-bezier(0.2, 1.5, 0.4, 1) backwards;
  animation-delay: calc(var(--i) * 0.17s);
}
@keyframes runner-star-pop { from { transform: scale(0.2) rotate(-28deg); opacity: 0; } to { transform: none; opacity: 1; } }
.runner-dialog-actions { display: flex; flex-direction: column; gap: 10px; margin-top: 20px; }
.runner-primary, .runner-secondary {
  padding: 14px 18px; border-radius: 18px; font-weight: 900; font-size: 1.02rem;
  cursor: pointer; line-height: 1.2; transition: transform 0.12s ease;
}
.runner-primary { border: none; color: #fff; background: linear-gradient(120deg, #6c5ce7, #8338ec); box-shadow: 0 12px 26px rgba(108, 92, 231, 0.34); }
.runner-secondary { border: 2px solid var(--line); background: #fff; color: var(--ink); }
.runner-primary:active, .runner-secondary:active { transform: scale(0.97); }

@media (prefers-reduced-motion: reduce) {
  .runner-dialog, .runner-star.filled { animation: none; }
  .runner-start, .runner-level, .runner-primary, .runner-secondary { transition: none; }
}

/* =====================================================================
   Hilfe-Lautsprecher: "Was mache ich hier?"
   Ein fester Platz oben links auf jedem Bildschirm ausser der Startseite.
   Die App spricht nie von selbst – erst ein Tipp hier lässt sie erzählen.
   ===================================================================== */
.help-voice {
  position: fixed;
  top: calc(var(--safe-top) + 14px);
  left: calc(var(--safe-left) + 14px);
  z-index: 110;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  max-width: min(420px, calc(100vw - 28px));
  pointer-events: none;
}

/* Kräftiges Lila mit Fragezeichen – so ist der Hilfeknopf nie mit dem weissen
   Ton-an/aus-Knopf oben rechts zu verwechseln. */
.help-voice-button {
  pointer-events: auto;
  flex: 0 0 auto;
  min-height: 52px;
  padding: 0 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border-radius: 999px;
  border: 3px solid #5a4bd4;
  background: linear-gradient(120deg, #6c5ce7, #8338ec);
  color: #fff;
  font-weight: 950;
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 12px 28px rgba(76, 62, 175, 0.34);
  transition: transform 0.12s ease, filter 0.18s ease;
}

.help-voice-mark {
  display: inline-block;
  line-height: 1;
  transform: translateY(-1px);
}

.help-voice-button svg {
  width: 24px;
  height: 24px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.help-voice-button .help-voice-core { fill: currentColor; stroke-width: 1.6; }
.help-voice-button .help-voice-wave { opacity: 0.9; }

.help-voice-button:hover:not(:disabled),
.help-voice-button:focus-visible {
  filter: brightness(1.08);
  outline: none;
}

.help-voice-button:active { transform: scale(0.95); }

/* Während des Vorlesens wandern die Schallwellen – so sieht man, dass es läuft. */
.help-voice-button.speaking {
  background: linear-gradient(120deg, #06d6a0, #05b98c);
  border-color: #05a97f;
}

.help-voice-button.speaking .help-voice-wave {
  animation: help-wave 1.1s ease-in-out infinite;
}

.help-voice-button.speaking .help-voice-wave-wide {
  animation-delay: 0.18s;
}

@keyframes help-wave {
  0%, 100% { opacity: 0.25; }
  50% { opacity: 1; }
}

/* Der Text erscheint zusätzlich als Sprechblase – der Knopf hilft damit auch
   auf Geräten ohne Sprachausgabe. */
.help-voice-bubble {
  pointer-events: auto;
  margin: 0;
  padding: 12px 16px;
  border-radius: 18px;
  border: 2px solid var(--line);
  background: #fff;
  color: var(--ink);
  font-weight: 700;
  font-size: 0.98rem;
  line-height: 1.45;
  text-wrap: pretty;
  box-shadow: 0 16px 34px rgba(36, 48, 71, 0.2);
  animation: help-bubble-in 0.22s cubic-bezier(0.2, 1.3, 0.5, 1);
}

@keyframes help-bubble-in {
  from { opacity: 0; transform: translateY(-6px) scale(0.96); }
  to { opacity: 1; transform: none; }
}

/* Im Vollbild-Sprungspiel stört der Knopf, dort führt das eigene HUD. */
body.runner-playing .help-voice { display: none; }

@media (max-width: 640px) {
  .help-voice {
    top: calc(var(--safe-top) + 10px);
    left: calc(var(--safe-left) + 10px);
    right: calc(var(--safe-right) + 10px);
    flex-direction: column;
    max-width: none;
  }

  .help-voice-button {
    align-self: flex-start;
    min-height: 46px;
    padding: 0 12px;
  }

  .help-voice-bubble { font-size: 0.92rem; padding: 10px 14px; }
}

/* --- Richtig-Signal statt Lob-Satz --- */
.practice-feedback.correct { color: #067a5c; }

.correct-badge {
  width: 54px;
  height: 54px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: linear-gradient(135deg, #06d6a0, #05b98c);
  color: #fff;
  font-size: 1.9rem;
  font-weight: 950;
  line-height: 1;
  box-shadow: 0 10px 22px rgba(6, 214, 160, 0.35);
  animation: correct-pop 0.45s cubic-bezier(0.2, 1.4, 0.5, 1);
}

@keyframes correct-pop {
  0% { opacity: 0; transform: scale(0.3) rotate(-25deg); }
  60% { opacity: 1; transform: scale(1.18) rotate(6deg); }
  100% { opacity: 1; transform: scale(1) rotate(0); }
}

@media (prefers-reduced-motion: reduce) {
  .help-voice-bubble,
  .correct-badge { animation: none; }
  .help-voice-button.speaking .help-voice-wave { animation: none; opacity: 1; }
}

/* =====================================================================
   Gehirntrainer: Karten-Merker, Schwarm-Fokus, Strand-Schätze,
   Weichen-Wirrwarr. Gemeinsame Bausteine zuerst.
   ===================================================================== */
.brain-board {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  align-content: center;
  gap: clamp(10px, 2.4vh, 20px);
  width: 100%;
  max-width: min(620px, 100%);
}

.brain-head {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  flex-wrap: wrap;
}

.brain-dots { display: inline-flex; gap: 5px; flex-wrap: wrap; justify-content: center; }
.brain-dot {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: #e2def4;
}
.brain-dot.done { background: #06d6a0; }
.brain-dot.current { background: #6c5ce7; box-shadow: 0 0 0 4px rgba(108, 92, 231, 0.18); }

.brain-note {
  font-size: 0.82rem;
  font-weight: 900;
  color: var(--muted);
  padding: 4px 12px;
  border-radius: 999px;
  background: #f2f0fb;
}

.brain-prompt {
  margin: 0;
  font-size: clamp(1.15rem, 4.6vw, 1.55rem);
  font-weight: 950;
  color: var(--primary-dark);
  text-align: center;
  text-wrap: balance;
}

.brain-hint {
  margin: 0;
  font-size: 0.9rem;
  font-weight: 800;
  color: var(--muted);
  text-align: center;
}

.brain-primary-button {
  min-height: 62px;
  padding: 0 28px;
  border: none;
  border-radius: 20px;
  background: linear-gradient(120deg, #6c5ce7, #8338ec);
  color: #fff;
  font-size: 1.15rem;
  font-weight: 900;
  cursor: pointer;
  transition: transform 0.1s ease, filter 0.15s ease;
}
.brain-primary-button:hover:not(:disabled) { filter: brightness(1.07); }
.brain-primary-button:active { transform: scale(0.96); }

.brain-feedback {
  min-height: 62px;
  display: grid;
  justify-items: center;
  align-content: center;
  gap: 8px;
  text-align: center;
  font-weight: 800;
  color: var(--muted);
  opacity: 0;
  transition: opacity 0.15s ease;
}
.brain-feedback.visible { opacity: 1; }
.brain-feedback p { margin: 0; }

/* Zeitbalken: läuft leer, ohne Countdown-Stress zu erzeugen. */
.brain-timebar {
  width: min(280px, 80%);
  height: 10px;
  border-radius: 999px;
  background: #ece9f8;
  overflow: hidden;
}
.brain-timebar-fill {
  display: block;
  height: 100%;
  width: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, #06d6a0, #ffd166);
  transform-origin: left center;
  animation-name: brain-timebar-run;
  animation-timing-function: linear;
  animation-fill-mode: forwards;
}
@keyframes brain-timebar-run { from { transform: scaleX(1); } to { transform: scaleX(0); } }

/* --- Schwarm-Fokus --- */
.flanker-row {
  --fish-count: 5;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(2px, 1vw, 8px);
  width: 100%;
  min-height: clamp(88px, 24vw, 120px);
  padding: 10px;
  border-radius: 24px;
  background: linear-gradient(180deg, #eaf7ff, #d9efff);
  border: 3px solid #b7ddf0;
}
.flanker-row.demo { min-height: 96px; }
.flanker-row.hidden-swarm {
  background: #f6f4ff;
  border-style: dashed;
  border-color: var(--line);
}
.flanker-memory { font-weight: 900; color: var(--primary-dark); }
/* Quadratisch, damit auch ein um 90 Grad gedrehter Fisch ganz hineinpasst. */
.flanker-slot {
  display: grid;
  place-items: center;
  width: calc((100% - 8px * var(--fish-count)) / var(--fish-count));
  max-width: 74px;
  aspect-ratio: 1;
}
.flanker-slot.target-slot {
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.95) 55%, rgba(255, 255, 255, 0) 72%);
}
.flanker-fish { width: 100%; height: auto; display: block; }
.flanker-fish .flanker-body { fill: #7fb2d9; }
.flanker-fish .flanker-tail, .flanker-fish .flanker-fin { fill: #6699c4; }
.flanker-fish .flanker-eye-white { fill: #fff; }
.flanker-fish .flanker-eye { fill: #243047; }
.flanker-fish.target .flanker-body { fill: #ff9f1c; }
.flanker-fish.target .flanker-tail, .flanker-fish.target .flanker-fin { fill: #ef7d0a; }

.flanker-pad { display: grid; gap: 12px; justify-content: center; }
.flanker-pad.row { grid-template-columns: repeat(2, minmax(0, 1fr)); width: min(360px, 100%); }
.flanker-pad.cross {
  grid-template-columns: repeat(3, 74px);
  grid-template-rows: repeat(2, 74px);
}
.flanker-pad.cross .key-up { grid-area: 1 / 2; }
.flanker-pad.cross .key-left { grid-area: 2 / 1; }
.flanker-pad.cross .key-down { grid-area: 2 / 2; }
.flanker-pad.cross .key-right { grid-area: 2 / 3; }
.flanker-key {
  min-height: 74px;
  display: grid;
  place-items: center;
  border-radius: 20px;
  border: 4px solid var(--line);
  background: #fff;
  color: var(--primary-dark);
  cursor: pointer;
  transition: transform 0.1s ease, background 0.15s ease;
}
.flanker-key:hover:not(:disabled) { background: #f2eeff; }
.flanker-key:active:not(:disabled) { transform: scale(0.94); }
.flanker-key:disabled { opacity: 0.4; cursor: default; }
.flanker-key-arrow { font-size: 2.1rem; line-height: 1; font-weight: 900; }

/* --- Strand-Schätze --- */
.treasure-grid {
  --treasure-count: 4;
  display: grid;
  /* Feste Spaltenbreite plus zentriertes Raster: bei zwei Schätzen stehen sie
     beieinander statt weit auseinander. */
  grid-template-columns: repeat(auto-fit, clamp(56px, 16vw, 82px));
  justify-content: center;
  gap: clamp(6px, 1.8vw, 12px);
  width: 100%;
  padding: clamp(10px, 2.4vw, 16px);
  border-radius: 26px;
  background: linear-gradient(180deg, #fff6e3, #ffeccd);
  border: 3px solid #ffd8a1;
}
.treasure-tile {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  aspect-ratio: 1;
  min-height: 56px;
  padding: 6px;
  border-radius: 18px;
  border: 3px solid rgba(255, 255, 255, 0.9);
  background: rgba(255, 255, 255, 0.72);
  cursor: pointer;
  transition: transform 0.12s ease, border-color 0.15s ease, background 0.15s ease;
}
.treasure-tile:hover:not(:disabled) { background: #fff; border-color: #ffc46b; }
.treasure-tile:active:not(:disabled) { transform: scale(0.94); }
.treasure-tile.known {
  border-color: #ff9f1c;
  background: #fff;
  animation: treasure-nudge 0.4s ease;
}
.treasure-svg { width: 100%; height: 100%; display: block; }
.treasure-known-mark {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 4px;
  font-size: 0.62rem;
  font-weight: 900;
  color: #a35a00;
}
@keyframes treasure-nudge {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  75% { transform: translateX(5px); }
}

.treasure-chest {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 8px 18px;
  border-radius: 999px;
  background: #fff;
  border: 2px solid var(--line);
  font-weight: 900;
  color: var(--primary-dark);
}

/* --- Weichen-Wirrwarr --- */
.track-board { gap: clamp(8px, 2vh, 14px); }
.track-canvas {
  width: 100%;
  max-width: 620px;
  border-radius: 26px;
  background: linear-gradient(180deg, #f4f8ff, #e8eeff);
  border: 3px solid #cfdcf7;
  touch-action: manipulation;
  cursor: pointer;
}
.track-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
}
.track-legend-chip {
  --chip-color: #6c5ce7;
  --chip-ink: #3b1273;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--chip-color) 18%, #fff);
  border: 2px solid var(--chip-color);
  color: var(--chip-ink);
  font-weight: 900;
  font-size: 0.85rem;
}
.track-legend-symbol { font-size: 1rem; line-height: 1; }

@media (prefers-reduced-motion: reduce) {
  .treasure-tile.known { animation: none; }
}

/* ===========================================================================
   Startbild: der Zug
   ---------------------------------------------------------------------------
   Die Bühne füllt den ganzen Bildschirm. Der Hintergrund wandert, der Zug steht
   still – so muss die Lok nicht bei jedem Bild neu gezeichnet werden, und das
   Kind sieht trotzdem eine Fahrt. Alle Bewegungen laufen über transform, damit
   sie der Compositor übernimmt und auch ein älteres Tablet sie schafft.
   =========================================================================== */

body[data-page="train"] {
  overflow: hidden;
  background: #a8ddf0;
}

body[data-page="train"].list-open { overflow: auto; }

.train-stage {
  position: fixed;
  inset: 0;
  overflow: hidden;
  touch-action: manipulation;
}

.train-stage[hidden] { display: none; }

/* --- Landschaft --- */

.scene {
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, var(--sky-top) 0%, var(--sky-bottom) 62%, var(--sky-bottom) 100%);
}

/* Der Boden liegt unter allen Ebenen und schliesst die Lücke unter dem Gleis. */
.scene::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 0;
  height: 42%;
  background: linear-gradient(to bottom, var(--ground) 0%, var(--ground-dark) 100%);
}

.scene-sun {
  position: absolute;
  top: 9%; right: 16%;
  width: clamp(56px, 10vw, 116px);
  aspect-ratio: 1;
}
.scene-sun svg { width: 100%; height: 100%; display: block; }

.scene-layer {
  position: absolute;
  left: 0;
  z-index: 1;
  width: 100%;
  overflow: hidden;
  pointer-events: none;
}

/* Die Nahebene gehört vor den Zug: Gras, das an den Rädern vorbeizieht. Das
   gilt aber nur, solange der Zug gross ist. Klein auf der Bühne verschwände er
   halb darin, also rückt das Gras dort hinter ihn. */
.scene-layer.is-front { z-index: 3; }
.train-stage:not([data-view="home"]) .scene-layer.is-front { z-index: 1; }

/* Tiefenstaffelung im Querformat. Die Werte gehören ins CSS und nicht in die
   Szenendaten, weil sich nur hier fürs Hochformat etwas anderes einstellen
   lässt – und dort ist die Landschaft viel höher und viel schmaler. */
.scene-layer-clouds { top: 4%; height: 34%; }
.scene-layer-far { bottom: 36%; height: 40%; }
.scene-layer-mid { bottom: 24%; height: 34%; }
/* Der Streifen ganz unten. Die Kachel dafür hat einen eigenen, flachen
   Ausschnitt – mit dem hohen der übrigen Ebenen wurde die Wiese darin
   waagrecht gestaucht und senkrecht gezogen. */
.scene-layer-near { bottom: 0; height: 13%; }

.scene-strip {
  display: flex;
  width: 200%;
  height: 100%;
  animation: scene-drift var(--speed, 60s) linear infinite;
  will-change: transform;
}

.scene-strip > svg {
  width: 50%;
  height: 100%;
  display: block;
  flex: 0 0 50%;
}

@keyframes scene-drift {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-50%, 0, 0); }
}

.scene-birds { position: absolute; inset: 0; pointer-events: none; }
.scene-bird {
  position: absolute;
  width: clamp(22px, 3.4vw, 40px);
  opacity: 0.65;
  animation: bird-fly 26s linear infinite;
}
.scene-bird svg { width: 100%; height: auto; display: block; }
.scene-bird-1 { top: 14%; animation-duration: 30s; }
.scene-bird-2 { top: 21%; animation-duration: 24s; animation-delay: -9s; }
.scene-bird-3 { top: 10%; animation-duration: 36s; animation-delay: -18s; }

@keyframes bird-fly {
  from { transform: translate3d(105vw, 0, 0); }
  to { transform: translate3d(-15vw, 0, 0); }
}

/* --- Der Zug --- */

/* Der Zug ist nur einmal da. Zwischen den Ansichten schrumpft er in die untere
   linke Ecke – nicht ausgetauscht, sondern bewegt, damit die Verkleinerung
   sichtbar wird. transform-origin unten links hält ihn dabei auf dem Gleis. */
.train-band {
  position: absolute;
  left: 0; right: 0;
  bottom: 9%;
  z-index: 2;
  display: flex;
  justify-content: center;
  pointer-events: none;
  /* Der Drehpunkt liegt auf der Schienenoberkante des Zugs: sie sitzt bei
     170 von 200 Einheiten, also auf 85 Prozent der Bandhöhe. Damit bleibt der
     Zug beim Verkleinern auf dem Gleis stehen, statt darüber zu schweben – und
     der gemessene Höhenausgleich gilt für alle Ansichten gleich, statt für
     jede neu ermittelt werden zu müssen. */
  transform-origin: 0 85%;
  transition: transform 0.72s cubic-bezier(0.32, 0.72, 0.24, 1);
}

.train-stage[data-view="home"] .train-band {
  transform: translate(0, var(--train-lift, 0px)) scale(1);
}

.train-stage[data-view="areas"] .train-band,
.train-stage[data-view="games"] .train-band {
  transform: translate(2%, var(--train-lift, 0px)) scale(0.42);
}

/* Der Zug ist das Menü: Ein Tipp auf einen Wagen zeigt die fünf Spiele seines
   Bereichs (train-home.js, showWagon). Seit der grüne Knopf gleich ins
   Abenteuer fährt, ist das der Weg dorthin – und ein Zug, der stillsteht,
   sieht nicht nach Knopf aus. Also wippen die Wagen beim Erscheinen dreimal
   kurz nacheinander, solange noch nie einer angetippt wurde; danach nie
   wieder (data-hint-wagons).

   Erst nach der Einfahrt: Solange data-entering steht, wartet der Zug am
   linken Rand oder rollt gerade herein. Ein Wippen dort wäre ein Wippen
   ausserhalb des Bilds – die drei Runden wären vorbei, bevor ein Kind den Zug
   überhaupt sieht. Die Regel greift erst, wenn das Attribut weg ist, und da
   beginnt auch die Animation.

   translate statt transform: Jeder Wagen trägt schon ein transform-Attribut,
   das ihn an seinen Platz im Zug setzt. Eine Animation auf transform
   überschriebe es, und die Wagen lägen alle übereinander in der Ecke. */
.train-stage[data-view="home"][data-hint-wagons="1"]:not([data-entering]) [data-area] {
  animation: wagen-winkt 1.9s ease-in-out var(--wagen-verzug, 0s) 3 both;
}

/* Die Werte sind Einheiten des Zug-Bilds, keine Bildschirmpixel: Am Laptop
   sind das rund 10 px Hub, am Handy rund 7 – dort ist das Bild kleiner, und
   ein kleinerer Zug darf auch kleiner hüpfen. Zweimal, das zweite Mal
   kleiner: So federt ein Ball aus, und so liest sich die Bewegung als Absicht
   und nicht als Ruckler. */
@keyframes wagen-winkt {
  0%, 55%, 100% { translate: 0 0; }
  62% { translate: 0 -9px; }
  70% { translate: 0 0; }
  77% { translate: 0 -5px; }
  85% { translate: 0 0; }
}

@media (prefers-reduced-motion: reduce) {
  .train-stage[data-view="home"][data-hint-wagons="1"]:not([data-entering]) [data-area] { animation: none; }
}

/* Die Fahrt zum Tor, in zwei Abschnitten: anrollen bis zur Weiche, dann die
   Rampe hinauf in die Halle. In translate() vor scale() geschriebene Werte
   werden nicht mitverkleinert – deshalb passen hier gemessene Bildschirmpixel
   unverändert hinein. */
.train-stage[data-driving] .train-band {
  transform: translate(calc(2% + var(--drive-x, 0px)), calc(var(--train-lift, 0px) + var(--drive-y, 0px))) scale(0.42);
}
.train-stage[data-driving="1"] .train-band {
  transition: transform 0.62s cubic-bezier(0.4, 0, 0.7, 0.7);
}
/* Am Ende verschwindet der Zug in der Halle, statt einfach auszublenden. */
.train-stage[data-driving="2"] .train-band {
  opacity: 0;
  transition: transform 1s cubic-bezier(0.2, 0, 0.55, 1), opacity 0.34s ease-in 0.66s;
}

/* Aus dem Bild fahren und wieder hereinkommen. Beides sind reine
   Verschiebungen auf der bestehenden Verkleinerung. */
.train-stage[data-moving="out"] .train-band {
  transform: translate(120%, var(--train-lift, 0px)) scale(0.42);
  transition-duration: 0.6s;
  transition-timing-function: cubic-bezier(0.4, 0, 0.9, 0.6);
}
.train-stage[data-moving="in"] .train-band {
  transform: translate(-110%, var(--train-lift, 0px)) scale(0.42);
  transition: none;
}

/* Auf dem Startbild wird die Verkleinerung nicht gebraucht, aber die Fahrt
   aus dem Bild schon – etwa beim Wechsel in ein Spiel. */
.train-stage[data-view="home"][data-moving="out"] .train-band {
  transform: translate(120%, var(--train-lift, 0px)) scale(1);
}

/* Die Einfahrt beim Laden: erst steht nur die Landschaft mit ihrem Gleis da,
   dann kommt der Zug von links herein. Zwei Zustände, weil eine Animation von
   selbst wieder von vorn beginnen würde, wenn die Bühne neu gezeichnet wird. */
.train-stage[data-entering="1"] .train-band {
  transform: translate(-118%, var(--train-lift, 0px)) scale(1);
  transition: none;
}
.train-stage[data-entering="0"] .train-band {
  transition-duration: 1.6s;
  transition-timing-function: cubic-bezier(0.22, 0.64, 0.24, 1);
}
.train-stage[data-entering] .train-start { opacity: 0; pointer-events: none; }

.train-band .train-svg {
  width: 100%;
  height: auto;
  display: block;
  pointer-events: auto;
}

/* Der Zug führt überall, wo er zu sehen ist, an dieselben zwei Orte: die Lok in
   die Werkstatt, jeder Wagen in seine Grossansicht.

   Neben dem Startbild steht er aber klein unten links, und sein Kasten reicht
   über die halbe Bühne – die unterste Halle liegt teilweise darunter. Deshalb
   nimmt dort nicht mehr das ganze SVG Tipps entgegen, sondern nur noch die
   gezeichneten Wagen und die Lok selbst. Zwischen ihnen und um sie herum geht
   der Tipp hindurch zur Halle. */
.train-stage:not([data-view="home"]) .train-band .train-svg { pointer-events: none; }
.train-stage[data-view="games"] .train-band .train-wagon,
.train-stage[data-view="games"] .train-band .train-loco { pointer-events: auto; }
/* Vor den Toren nimmt der Zug keine Tipps entgegen: er wartet genau unter dem
   ersten Tor, und ein Tipp dorthin soll das Tor treffen, nicht die Lok. */
.train-stage[data-view="areas"] .train-band .train-wagon,
.train-stage[data-view="areas"] .train-band .train-loco { pointer-events: none; cursor: default; }

.train-wagon, .train-loco { cursor: pointer; }

/* Dampf steigt aus dem Kamin und verweht nach vorn. Alle drei Wolken starten an
   derselben Stelle; nur der Versatz macht daraus eine Fahne. */
@keyframes train-puff {
  0%   { transform: translate(0, 0) scale(0.6); opacity: 0; }
  15%  { opacity: 0.9; }
  100% { transform: translate(30px, -54px) scale(1.7); opacity: 0; }
}

.train-steam-puff {
  animation: train-puff 3.6s ease-out infinite;
  transform-origin: center;
  transform-box: fill-box;
  opacity: 0;
}
.train-steam-2 { animation-delay: 1.2s; }
.train-steam-3 { animation-delay: 2.4s; }

/* --- Das Startsignal --- */
/* Schwebt vor der Lok, statt auf dem Gleis zu stehen: das Gleis liegt jetzt
   fest im Bild, und ein Signal darauf wäre ein Hindernis, das der Zug beim
   Losfahren umfahren müsste. Gesetzt wird es aus dem Skript – wie breit der Zug
   im Bild steht, hängt am Seitenverhältnis des Fensters. */

.train-start {
  position: absolute;
  z-index: 4;
  /* Nur der Ausgangswert. Die endgültige Grösse misst positionStart() aus dem
     Platz vor der Lok: der Knopf soll vor ihr schweben, nicht auf ihr, und
     dafür wird er auf einem schmalen Bild lieber kleiner als dass er
     zurückweicht. */
  width: clamp(44px, 11vw, 74px);
  height: clamp(44px, 11vw, 74px);
  padding: 0;
  border: none;
  border-radius: 50%;
  background: none;
  cursor: pointer;
  filter: drop-shadow(0 8px 14px rgba(36, 48, 71, 0.28));
  transition: opacity 0.45s ease 0.1s, transform 0.15s ease;
}
.train-start[hidden] { display: none; }
/* Vor der ersten Messung hat der Knopf keinen Platz: ohne left/top stünde er
   in der oberen linken Ecke der Bühne, und genau dort gehört er nicht hin.
   Gesetzt wird die Marke in positionStart(). */
.train-start[data-placed="0"] { opacity: 0; pointer-events: none; }
/* Geschwebt wird die Zeichnung, nicht der Knopf: ein Ziel, das wandert, trifft
   ein Kind schlechter. Der Knopf selbst steht still. */
.train-start svg {
  width: 100%; height: 100%; display: block; overflow: visible;
  animation: start-float 3.4s ease-in-out infinite;
}
.train-start:active { transform: scale(0.93); }
.train-start:hover .train-start-lamp,
.train-start:focus-visible .train-start-lamp { fill: #55d98c; }
.train-start:focus-visible { outline: 3px solid #ffffff; outline-offset: 4px; border-radius: 50%; }

/* Ein ruhiges Auf und Ab: der Knopf schwebt, er hängt nicht. */
@keyframes start-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-7px); }
}

/* Der Ring pulst, damit klar ist: hier geht es weiter. */
@keyframes start-pulse {
  0%, 100% { transform: scale(1); opacity: 0.5; }
  50% { transform: scale(1.26); opacity: 0.12; }
}

.train-start-ring {
  animation: start-pulse 2.2s ease-in-out infinite;
  transform-origin: center;
  transform-box: fill-box;
}

/* --- Räder --- */
/* Gedreht wird die innere Gruppe; die äussere stellt das Rad an seinen Platz.
   Beides an einem Element ginge nicht – eine CSS-Drehung überschriebe das
   transform-Attribut, mit dem das Rad positioniert ist.

   Gedreht wird genau dann, wenn der Zug wirklich fährt: auf dem Startbild, das
   eine Fahrt durch die Landschaft ist, und während jeder Bewegung. Steht der
   Zug – an der Weiche, vor den Gebäuden, in der Werkstatt –, stehen auch die
   Räder und die Landschaft. */
.train-wheel {
  transform-box: fill-box;
  transform-origin: center;
}

@keyframes wheel-roll { to { transform: rotate(1turn); } }

.train-stage[data-view="home"] .train-wheel,
.train-stage[data-moving] .train-wheel,
.train-stage[data-entering] .train-wheel,
.train-stage[data-driving] .train-wheel {
  animation: wheel-roll var(--turn, 1.5s) linear infinite;
}

/* Die Landschaft zieht nur vorbei, wenn der Zug fährt. Ein Hintergrund, der
   wandert, während der Zug an der Weiche wartet, wäre eine Fahrt ohne Zug. */
.train-stage .scene-strip { animation-play-state: paused; }
.train-stage[data-view="home"] .scene-strip,
.train-stage[data-moving] .scene-strip,
.train-stage[data-entering] .scene-strip,
.train-stage[data-driving] .scene-strip { animation-play-state: running; }

/* --- Bühnen-Ebene: Tore und Gebäude --- */

/* Die Gebäude stehen hinter dem Gleis, der Zug fährt davor vorbei. Läge die
   Ebene vor dem Zug, würde sie ihn in Stücke schneiden. */
.stage-layer {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: grid;
  place-items: center;
  pointer-events: none;
}

.stage-layer .stage-svg {
  width: 100%;
  height: 100%;
  /* Ohne das hier gilt die Höhe darüber nicht. Ein Rasterfeld gibt seinem Kind
     von sich aus eine Mindestgrösse aus dessen Inhalt, und ein SVG bringt aus
     seinem Ausschnitt ein festes Seitenverhältnis mit: auf einem sehr breiten
     Bildschirm wurde die Ebene daraus höher gerechnet als die Bühne und hing
     unten hinaus. Das Gleis rutschte mit, und der Zug, der darauf steht,
     stand halb unter dem Bildrand. */
  min-height: 0;
  min-width: 0;
  pointer-events: none;
  /* Das Gleis ist über den Ausschnitt hinaus gezeichnet und darf hinaus:
     der Ausschnitt wird eingepasst, nicht beschnitten, und bei einem sehr
     breiten Fenster bliebe links und rechts sonst ein Stück Gleis fehlend. */
  overflow: visible;
  /* backwards, nicht both: Der Endzustand ist der Ruhezustand. Mit "both"
     hielte der Browser die fertige Animation für immer fest – und hält dafür
     die ganze Ebene als eigene Compositing-Schicht auf der Grafikkarte, mit
     allem, was über die Ränder hinaus gezeichnet ist. Auf einem Tablet mit
     hoher Auflösung sind das Dutzende Megapixel für nichts. */
  animation: stage-layer-in 0.5s ease backwards;
}

/* Auf einem flachen Bildschirm wird die Ebene in der Breite eingepasst und
   reicht damit fast bis an den oberen Rand – das oberste Tor lag dort unter
   den Knöpfen für Ton und Konto. Deshalb bekommt sie unter der Leiste ihren
   Platz: sie wird etwas kleiner und sitzt weiter unten, das Gleis bleibt am
   unteren Rand. */
@media (max-height: 600px) {
  .stage-layer .stage-svg {
    height: calc(100% - var(--safe-top) - 68px);
    align-self: end;
  }
}

.stage-layer .train-gate,
.stage-layer .train-building { pointer-events: auto; cursor: pointer; }

/* Nur die feste Trefferfläche nimmt Tipps entgegen; die Zeichnung darunter
   nicht. So bleibt das Ziel gross und ändert sich nicht beim Hovern. */
.train-gate > *:not(.train-gate-hit),
.train-building > *:not(.train-building-hit) { pointer-events: none; }

/* --- Gesperrt: blass, mit Schloss --- */

/* Was das Kind noch nicht spielen darf, sieht aus wie im Nebel: die Farbe
   raus, etwas aufgehellt. Nur die Zeichnung – das gelbe Schloss liegt
   daneben in einer eigenen Gruppe und behält seine Farbe, sonst wäre das
   Zeichen selbst kaum zu sehen.

   Blass statt unsichtbar, und antippbar bleibt es auch: Tippt das Kind
   darauf, kommt das Tor mit dem Satz, warum hier Schluss ist. Ein Haus, das
   auf nichts reagiert, sähe nach kaputt aus. */
.train-building.is-locked .train-building-art,
.wagon-crate.is-locked .train-building-art,
.train-gate.is-locked .train-gate-art {
  filter: grayscale(0.85) brightness(1.12) opacity(0.55);
}

/* Die Kiste am Wagen führt ins selbe Spiel wie das Haus auf der Bühne –
   also trägt sie dasselbe Schloss, und ihre Bänder werden blass mit. */
.wagon-crate.is-locked .crate-bands { opacity: 0.45; }

/* Das Schloss soll auffallen: es wippt einmal alle paar Sekunden, wie ein
   Schild im Wind. Kurz genug, dass es nicht nervt, und lang genug, dass ein
   Kind es aus dem Augenwinkel sieht. */
@keyframes lock-badge-nudge {
  0%, 88%, 100% { transform: none; }
  92% { transform: rotate(-7deg); }
  96% { transform: rotate(5deg); }
}
.train-lock-badge {
  transform-box: fill-box;
  transform-origin: center;
  animation: lock-badge-nudge 5s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .train-lock-badge { animation: none; }
}

@keyframes stage-layer-in {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}

/* Dieselbe Ansicht, nur neu gezeichnet – etwa weil der Fortschritt aus der
   Cloud kam: dann steht das Bild schon und schwebt nicht noch einmal herein. */
.stage-layer.is-quiet .stage-svg { animation: none; }

.train-gate, .train-building {
  transform-box: fill-box;
  transform-origin: center bottom;
  transition: transform 0.16s ease;
}
.train-gate:hover, .train-building:hover,
.train-gate:focus-visible, .train-building:focus-visible { transform: scale(1.05); }
.train-gate:active, .train-building:active { transform: scale(0.97); }
/* Der voreingestellte Rahmen des Browsers ist ein schwarzer Kasten um die
   unsichtbare Trefferfläche – er sieht aus wie ein Fehler. Stattdessen ein
   weisser Ring am Tor selbst. */
.train-gate:focus, .train-building:focus { outline: none; }
.train-gate:focus-visible, .train-building:focus-visible {
  outline: 4px solid #ffffff;
  outline-offset: 3px;
  border-radius: 12px;
}

/* --- Spiel-Symbole: leichte Bewegung --- */

/* Jedes Bild eines Spiels hat ein, zwei Teile, die sich rühren: der Fisch
   wippt, die Karte dreht sich um, der Turm wächst Block um Block. Nur
   transform und opacity – das zeichnet der Browser günstig, und vier Bilder
   nebeneinander bleiben auch auf einem alten Tablet flüssig.

   transform-box: fill-box, damit sich ein Teil um seinen eigenen Kasten dreht
   und nicht um die Ecke der Bühne. Der Ruhezustand ohne Bewegung ist immer das
   fertige Bild: wer weniger Bewegung eingestellt hat, sieht alles, nur still. */
.gi { transform-box: fill-box; transform-origin: center; }

.gi-bob { animation: gi-bob 2.8s ease-in-out infinite; }
@keyframes gi-bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}

/* Die Maus springt: erst ducken, dann hoch, dann weich landen. */
.gi-hop { transform-origin: center bottom; animation: gi-hop 1.7s ease-in-out infinite; }
@keyframes gi-hop {
  0%, 100% { transform: translateY(0) scale(1, 1); }
  12% { transform: translateY(0) scale(1.08, 0.9); }
  50% { transform: translateY(-30px) scale(0.96, 1.06); }
  84% { transform: translateY(0) scale(1, 1); }
  92% { transform: translateY(0) scale(1.06, 0.94); }
}
.gi-hop-shadow { animation: gi-hop-shadow 1.7s ease-in-out infinite; }
@keyframes gi-hop-shadow {
  0%, 84%, 100% { transform: scaleX(1); opacity: 0.3; }
  50% { transform: scaleX(0.55); opacity: 0.14; }
}

.gi-sway { transform-origin: center bottom; animation: gi-sway 3.8s ease-in-out infinite; }
@keyframes gi-sway {
  0%, 100% { transform: rotate(-3deg); }
  50% { transform: rotate(3deg); }
}

.gi-twinkle { animation: gi-twinkle 2.4s ease-in-out infinite; }
@keyframes gi-twinkle {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.4; transform: scale(0.8); }
}

/* Eine Karte dreht sich um: die Rückseite wird schmal, dann kommt die
   Vorderseite aus der Kante heraus – und nach einer Weile wieder zurück. */
.gi-flip-back { animation: gi-flip-back 4.4s ease-in-out infinite; }
.gi-flip-front { animation: gi-flip-front 4.4s ease-in-out infinite; }
@keyframes gi-flip-back {
  0%, 24% { transform: scaleX(1); }
  32%, 68% { transform: scaleX(0); }
  76%, 100% { transform: scaleX(1); }
}
@keyframes gi-flip-front {
  0%, 32% { transform: scaleX(0); }
  40%, 60% { transform: scaleX(1); }
  68%, 100% { transform: scaleX(0); }
}

.gi-tail { transform-origin: right center; animation: gi-tail 0.9s ease-in-out infinite; }
@keyframes gi-tail {
  0%, 100% { transform: rotate(-12deg); }
  50% { transform: rotate(12deg); }
}

.gi-bubble { animation: gi-bubble 3s ease-in infinite; }
@keyframes gi-bubble {
  0% { transform: translateY(0); opacity: 0; }
  20% { opacity: 0.9; }
  100% { transform: translateY(-34px); opacity: 0; }
}

/* Drei Bahnen im Teich, jede anders – sonst schwämmen alle im Gleichschritt. */
.gi-swim-1 { animation: gi-swim-1 4.6s ease-in-out infinite; }
.gi-swim-2 { animation: gi-swim-2 5.4s ease-in-out infinite; }
.gi-swim-3 { animation: gi-swim-3 3.9s ease-in-out infinite; }
@keyframes gi-swim-1 {
  0%, 100% { transform: translate(0, 0); }
  30% { transform: translate(14px, -6px); }
  65% { transform: translate(-8px, 5px); }
}
@keyframes gi-swim-2 {
  0%, 100% { transform: translate(0, 0); }
  40% { transform: translate(-16px, 4px); }
  70% { transform: translate(6px, -7px); }
}
@keyframes gi-swim-3 {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(10px, 7px); }
}

/* Die Weiche schlägt um, und die beiden Wagen rücken auf ihren Ästen weiter. */
.gi-switch { transform-origin: center bottom; animation: gi-switch 2.8s ease-in-out infinite; }
@keyframes gi-switch {
  0%, 42% { transform: rotate(-45deg); }
  50%, 92% { transform: rotate(45deg); }
  100% { transform: rotate(-45deg); }
}
.gi-nudge-l { animation: gi-nudge-l 2.8s ease-in-out infinite; }
.gi-nudge-r { animation: gi-nudge-r 2.8s ease-in-out infinite; animation-delay: -1.4s; }
@keyframes gi-nudge-l {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(-6px, -6px); }
}
@keyframes gi-nudge-r {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(6px, -6px); }
}

/* Freie Fahrt: der Zug fährt durch die Lücke, wartet kurz, kommt zurück. */
.gi-slide { animation: gi-slide 3.6s ease-in-out infinite; }
@keyframes gi-slide {
  0%, 14% { transform: translateX(0); }
  56%, 72% { transform: translateX(48px); }
  100% { transform: translateX(0); }
}

/* Blätter im Strom: jedes Blatt hat seinen Platz auf dem Bach und treibt von
   dort einmal über die ganze Länge. Drei Bahnen nach rechts, zwei nach unten;
   die Startpunkte sind so gewählt, dass die Blätter gleich weit auseinander
   liegen – und ohne Bewegung verteilt stehen bleiben. */
.gi-flow-x-a { animation: gi-flow-x-a 5s linear infinite; }
.gi-flow-x-b { animation: gi-flow-x-b 5s linear infinite; animation-delay: -1.7s; }
.gi-flow-x-c { animation: gi-flow-x-c 5s linear infinite; animation-delay: -3.4s; }
.gi-flow-y-a { animation: gi-flow-y-a 5.6s linear infinite; }
.gi-flow-y-b { animation: gi-flow-y-b 5.6s linear infinite; animation-delay: -2.8s; }
@keyframes gi-flow-x-a {
  0% { transform: translateX(-26px); opacity: 0; }
  12% { opacity: 1; }
  88% { opacity: 1; }
  100% { transform: translateX(82px); opacity: 0; }
}
@keyframes gi-flow-x-b {
  0% { transform: translateX(-56px); opacity: 0; }
  12% { opacity: 1; }
  88% { opacity: 1; }
  100% { transform: translateX(52px); opacity: 0; }
}
@keyframes gi-flow-x-c {
  0% { transform: translateX(-86px); opacity: 0; }
  12% { opacity: 1; }
  88% { opacity: 1; }
  100% { transform: translateX(22px); opacity: 0; }
}
@keyframes gi-flow-y-a {
  0% { transform: translateY(-22px); opacity: 0; }
  12% { opacity: 1; }
  88% { opacity: 1; }
  100% { transform: translateY(100px); opacity: 0; }
}
@keyframes gi-flow-y-b {
  0% { transform: translateY(-104px); opacity: 0; }
  12% { opacity: 1; }
  88% { opacity: 1; }
  100% { transform: translateY(18px); opacity: 0; }
}

/* Der Turm wächst: Block um Block fällt von oben auf den Stapel, dann ist er
   fertig, dann fängt er von vorn an. Fünf eigene Abläufe statt eines mit
   Verzögerung: so verschwinden alle Blöcke im selben Moment, statt dass der
   unterste vor dem obersten weg wäre. */
.gi-swing { animation: gi-swing 1.6s ease-in-out infinite alternate; }
@keyframes gi-swing {
  from { transform: translateX(-22px); }
  to { transform: translateX(22px); }
}
.gi-build-1 { animation: gi-build-1 6s ease-out infinite; }
.gi-build-2 { animation: gi-build-2 6s ease-out infinite; }
.gi-build-3 { animation: gi-build-3 6s ease-out infinite; }
.gi-build-4 { animation: gi-build-4 6s ease-out infinite; }
.gi-build-5 { animation: gi-build-5 6s ease-out infinite; }
@keyframes gi-build-1 {
  0% { opacity: 0; transform: translateY(-18px); }
  6%, 90% { opacity: 1; transform: translateY(0); }
  94%, 100% { opacity: 0; transform: translateY(0); }
}
@keyframes gi-build-2 {
  0%, 12% { opacity: 0; transform: translateY(-18px); }
  18%, 90% { opacity: 1; transform: translateY(0); }
  94%, 100% { opacity: 0; transform: translateY(0); }
}
@keyframes gi-build-3 {
  0%, 24% { opacity: 0; transform: translateY(-18px); }
  30%, 90% { opacity: 1; transform: translateY(0); }
  94%, 100% { opacity: 0; transform: translateY(0); }
}
@keyframes gi-build-4 {
  0%, 36% { opacity: 0; transform: translateY(-18px); }
  42%, 90% { opacity: 1; transform: translateY(0); }
  94%, 100% { opacity: 0; transform: translateY(0); }
}
@keyframes gi-build-5 {
  0%, 48% { opacity: 0; transform: translateY(-18px); }
  54%, 90% { opacity: 1; transform: translateY(0); }
  94%, 100% { opacity: 0; transform: translateY(0); }
}

/* Ein Weg zeichnet sich: der Pfad trägt pathLength="1", also ist ein Strich
   von 1 der ganze Weg, und der Versatz schiebt ihn Stück für Stück ins Bild. */
.gi-draw { stroke-dasharray: 1; stroke-dashoffset: 0; animation: gi-draw 5s ease-in-out infinite; }
@keyframes gi-draw {
  0% { stroke-dashoffset: 1; opacity: 1; }
  50%, 86% { stroke-dashoffset: 0; opacity: 1; }
  92% { stroke-dashoffset: 0; opacity: 0; }
  93%, 100% { stroke-dashoffset: 1; opacity: 0; }
}

.gi-spin { transform-origin: center bottom; animation: gi-spin 4s linear infinite; }
@keyframes gi-spin {
  to { transform: rotate(360deg); }
}

.gi-hover { animation: gi-hover 4.2s ease-in-out infinite; }
@keyframes gi-hover {
  0%, 100% { transform: translate(0, 0); }
  25% { transform: translate(5px, -4px); }
  50% { transform: translate(0, -7px); }
  75% { transform: translate(-5px, -3px); }
}

.gi-rock { animation: gi-rock 3.2s ease-in-out infinite; }
@keyframes gi-rock {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50% { transform: translateY(-3px) rotate(2deg); }
}
.gi-wave { animation: gi-wave 2.4s ease-in-out infinite; }
@keyframes gi-wave {
  0%, 100% { transform: translateX(0); }
  50% { transform: translateX(-7px); }
}

.gi-net { transform-origin: right top; animation: gi-net 2.8s ease-in-out infinite; }
@keyframes gi-net {
  0%, 100% { transform: rotate(-7deg); }
  50% { transform: rotate(6deg); }
}

.gi-fade { animation: gi-fade 4s ease-in-out infinite; }
@keyframes gi-fade {
  0%, 30% { opacity: 0; transform: scale(0.6); }
  45%, 85% { opacity: 1; transform: scale(1); }
  95%, 100% { opacity: 0; transform: scale(0.6); }
}
.gi-pulse { animation: gi-pulse 2s ease-in-out infinite; }
@keyframes gi-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.14); }
}

@media (prefers-reduced-motion: reduce) {
  .gi { animation: none !important; }
}

/* --- Die Gestalten des zweiten Wagen-Sets ---
   Aus Fracht wird ein Einhorn, ein Wal, ein Roboter, ein Drache, ein Schiff –
   und fertig gebaut leben sie: der Wal wippt und spritzt, der Roboter winkt,
   die Flügel schlagen. Dieselbe Regel wie bei den Spielbildern: nur transform
   und opacity, und wer weniger Bewegung eingestellt hat, sieht alles still.
   Der Drehpunkt steht je Bewegung dabei, wo das Teil am Körper hängt. */
.wa { transform-box: fill-box; transform-origin: center; }

/* Das Einhorn wirft alle paar Sekunden den Kopf – es wiehert. */
.wa-whinny { transform-origin: 10% 100%; animation: wa-whinny 5.2s ease-in-out infinite; }
@keyframes wa-whinny {
  0%, 74%, 100% { transform: rotate(0deg); }
  80% { transform: rotate(-14deg); }
  86% { transform: rotate(-9deg); }
  91% { transform: rotate(-15deg); }
}
.wa-tail { transform-origin: 100% 0%; animation: wa-tail 2.8s ease-in-out infinite; }
@keyframes wa-tail {
  0%, 100% { transform: rotate(0deg); }
  50% { transform: rotate(9deg); }
}

/* Der Wal hebt sich und wippt, die Flosse rudert, die Fontäne steigt. */
.wa-float { animation: wa-float 3.6s ease-in-out infinite; }
@keyframes wa-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}
.wa-fin { transform-origin: 0% 0%; animation: wa-fin 2.2s ease-in-out infinite; }
@keyframes wa-fin {
  0%, 100% { transform: rotate(0deg); }
  50% { transform: rotate(10deg); }
}
.wa-spout { transform-origin: 50% 100%; animation: wa-spout 2.6s ease-out infinite; }
@keyframes wa-spout {
  0% { transform: translateY(6px) scale(0.6); opacity: 0; }
  20% { opacity: 1; }
  70% { opacity: 0.9; }
  100% { transform: translateY(-14px) scale(1.15); opacity: 0; }
}
.wa-roll { animation: wa-roll 2.4s ease-in-out infinite; }
@keyframes wa-roll {
  0%, 100% { transform: translateX(0); }
  50% { transform: translateX(-7px); }
}

/* Der Roboter: die Augen leuchten, er winkt, und ab und zu stampft er. */
.wa-glow { animation: wa-glow 1.8s ease-in-out infinite; }
@keyframes wa-glow {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}
.wa-wave-arm { transform-origin: 0% 0%; animation: wa-wave-arm 4.4s ease-in-out infinite; }
@keyframes wa-wave-arm {
  0%, 55%, 100% { transform: rotate(0deg); }
  65% { transform: rotate(-150deg); }
  75% { transform: rotate(-120deg); }
  85% { transform: rotate(-150deg); }
}
.wa-stomp { animation: wa-stomp 4.4s ease-in-out infinite; }
@keyframes wa-stomp {
  0%, 30%, 44%, 100% { transform: translateY(0); }
  36% { transform: translateY(3px); }
  40% { transform: translateY(-2px); }
}

/* Der Drache schlägt mit den Flügeln, das Feuer flackert. */
.wa-flap { transform-origin: 100% 100%; animation: wa-flap 4s ease-in-out infinite; }
@keyframes wa-flap {
  0%, 60%, 100% { transform: rotate(0deg); }
  70% { transform: rotate(-22deg); }
  80% { transform: rotate(6deg); }
  90% { transform: rotate(-14deg); }
}
.wa-fire { transform-origin: 50% 100%; animation: wa-fire 0.9s ease-in-out infinite alternate; }
@keyframes wa-fire {
  from { transform: scale(0.92, 0.9); opacity: 0.85; }
  to { transform: scale(1.06, 1.12); opacity: 1; }
}

/* Das Schiff: die Flagge weht, die Wellen laufen, die Möwe zieht vorbei. */
.wa-flag { transform-origin: 0% 50%; animation: wa-flag 1.6s ease-in-out infinite; }
@keyframes wa-flag {
  0%, 100% { transform: skewY(0deg) scaleX(1); }
  50% { transform: skewY(-6deg) scaleX(0.92); }
}
.wa-gull { animation: wa-gull 9s linear infinite; }
@keyframes wa-gull {
  0% { transform: translate(0, 0); opacity: 0; }
  8% { opacity: 1; }
  50% { transform: translate(50px, -8px); }
  92% { opacity: 1; }
  100% { transform: translate(100px, 2px); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .wa { animation: none !important; }
}

/* --- Zurück-Knopf der Bühne --- */

/* Oben links, an der Stelle, an der auf dem Startbild der Landschafts-Knopf
   steht – die beiden gibt es nie gleichzeitig. Unten links stünde er dem Zug im
   Weg, der dort klein auf seinem Gleis wartet. */
.stage-back {
  position: absolute;
  top: calc(var(--safe-top) + 14px);
  left: calc(var(--safe-left) + 14px);
  z-index: 6;
  width: 60px;
  height: 60px;
  border-radius: 50%;
  border: 3px solid #fff;
  background: #6c5ce7;
  color: #fff;
  box-shadow: 0 6px 16px rgba(36, 48, 71, 0.24);
  display: grid;
  place-items: center;
  cursor: pointer;
  padding: 0;
  transition: transform 0.15s ease;
}
.stage-back[hidden] { display: none; }
.stage-back svg { width: 30px; height: 30px; }
.stage-back:active { transform: scale(0.93); }
.stage-back:focus-visible { outline: 3px solid #243047; outline-offset: 3px; }

/* --- Landschafts-Knopf oben links --- */
/* Ein Guckloch auf die Landschaft, die gerade läuft, mit zwei Pfeilen daneben.
   Steht dort, wo auf den Spielseiten der Vorlese-Knopf sitzt. */

.scene-button {
  position: absolute;
  top: calc(var(--safe-top) + 14px);
  left: calc(var(--safe-left) + 14px);
  z-index: 6;
  width: 58px;
  height: 58px;
  padding: 0;
  border: 3px solid rgba(255, 255, 255, 0.95);
  border-radius: 50%;
  background: #cfe9f5;
  box-shadow: 0 6px 16px rgba(36, 48, 71, 0.24);
  cursor: pointer;
  transition: transform 0.15s ease;
}
.scene-button[hidden] { display: none; }
.scene-button:active { transform: scale(0.93); }
.scene-button:focus-visible { outline: 3px solid #6c5ce7; outline-offset: 3px; }

.scene-button-view {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  overflow: hidden;
  display: block;
}
.scene-button-view .scene-thumb-art { width: 100%; height: 100%; }

.scene-button-badge {
  position: absolute;
  right: -6px;
  bottom: -6px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 2px solid #fff;
  background: #6c5ce7;
  color: #fff;
  display: grid;
  place-items: center;
}
.scene-button-badge svg { width: 17px; height: 17px; display: block; }

/* ---------------------------------------------------------------------------
   Dreh-Hinweis
   ---------------------------------------------------------------------------
   Nur auf Geräten mit Finger-Bedienung und nur hochkant. Auf einem Desktop mit
   schmalem Fenster wäre er nur im Weg, deshalb die Abfrage auf pointer: coarse.
   --------------------------------------------------------------------------- */

/* Auf dem Startbild gibt es keinen Vorlese-Knopf. Auch nicht, wenn ein Dialog
   darüber – etwa "Wer spielt?" – einen Hilfetext anmeldet: dort soll wirklich
   nur der Zug stehen. In den Spielen bleibt der Knopf unverändert. */
body[data-page="train"] .help-voice { display: none; }

.rotate-hint { display: none; }

@media (orientation: portrait) and (pointer: coarse) {
  .rotate-hint {
    position: fixed;
    inset: 0;
    z-index: 300;
    display: grid;
    place-items: center;
    background: #243047;
    padding: 10vw;
  }

  .rotate-hint svg {
    width: min(58vw, 320px);
    height: auto;
    overflow: visible;
  }

  .rotate-hint-device {
    fill: none;
    stroke: #ffffff;
    stroke-width: 5;
    transform-origin: 60px 39px;
    animation: rotate-hint-turn 3s ease-in-out infinite;
  }
  .rotate-hint-dot {
    fill: #ffffff;
    transform-origin: 60px 39px;
    animation: rotate-hint-turn 3s ease-in-out infinite;
  }
  .rotate-hint-arrow { fill: none; stroke: #6c5ce7; stroke-width: 6; stroke-linecap: round; }
  .rotate-hint-tip { fill: #6c5ce7; }

  /* Hinter dem Hinweis muss nichts laufen: die Bühne pausiert, damit das
     Gerät nicht sinnlos animiert, was niemand sieht. Die Selektoren sind so
     genau wie die weiter oben, die Landschaft und Räder in Gang setzen –
     sonst gewönnen jene und es liefe weiter. */
  .scene-bird { animation-play-state: paused; }
  .train-stage[data-view] .scene-strip,
  .train-stage[data-view] .train-wheel { animation-play-state: paused; }
}

@keyframes rotate-hint-turn {
  0%, 20% { transform: rotate(0deg); }
  55%, 80% { transform: rotate(-90deg); }
  100% { transform: rotate(0deg); }
}

@media (prefers-reduced-motion: reduce) {
  .scene-strip, .scene-bird, .train-start-ring { animation: none; }
  .train-steam-puff { animation: none; opacity: 0.5; }
  .rotate-hint-device, .rotate-hint-dot { animation: none; transform: rotate(-90deg); }
  .train-band { transition: none; }
  .stage-layer .stage-svg { animation: none; }
  .train-gate, .train-building { transition: none; }
}

/* ===========================================================================
   Lok-Werkstatt
   ---------------------------------------------------------------------------
   Die Lok allein, gross. Ein Tipp auf ein Bauteil fährt die Kamera darauf zu
   und blendet darunter die Varianten ein. Gezoomt wird eine Gruppe im SVG und
   nicht dessen Ausschnitt: nur eine Transformation lässt sich weich animieren.
   =========================================================================== */

.train-stage[data-view="loco"] .train-band { opacity: 0; pointer-events: none; }
.train-stage[data-view="loco"] .stage-layer { z-index: 4; }

.loco-workshop {
  width: 100%;
  height: 100%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(8px, 2vh, 20px);
  padding: calc(var(--safe-top) + 76px) 16px calc(var(--safe-bottom) + 16px);
  pointer-events: auto;
}

.loco-view {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  overflow: hidden;
}

/* Die Lok füllt die Bühne. Ist ein Bauteil gewählt, macht sie unten Platz für
   die Varianten – das steuert allein die Höhe des Geschwisters.
   Kein overflow: visible: beim Zoom liefe die Lok sonst über die ganze Seite
   hinaus und schöbe die Varianten aus dem Fenster. */
.loco-svg {
  width: 100%;
  height: 100%;
  display: block;
}

.loco-camera {
  transform-box: view-box;
  transform-origin: 0 0;
  transition: transform 0.55s cubic-bezier(0.32, 0.72, 0.24, 1);
}

/* Antippbare Stellen an der Lok. Ein kleiner Punkt zeigt sie dauerhaft an,
   damit ein Kind erkennt, wo etwas zu holen ist; die Trefferfläche darunter ist
   viel grösser als der Punkt und unsichtbar. */
.loco-hotspot { cursor: pointer; }
.loco-hotspot > *:not(.loco-hotspot-hit) { pointer-events: none; }
.loco-hotspot-dot,
.loco-hotspot-halo {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform 0.16s ease, opacity 0.16s ease;
}
.loco-hotspot-halo { animation: loco-dot-pulse 2.6s ease-in-out infinite; }
.loco-hotspot:hover .loco-hotspot-dot,
.loco-hotspot:focus-visible .loco-hotspot-dot { transform: scale(1.45); }
.loco-hotspot:hover .loco-hotspot-halo,
.loco-hotspot:focus-visible .loco-hotspot-halo { opacity: 0.6; transform: scale(1.5); }
.loco-hotspot:active .loco-hotspot-dot { transform: scale(1.1); }
.loco-hotspot:focus-visible { outline: none; }

@keyframes loco-dot-pulse {
  0%, 100% { transform: scale(1); opacity: 0.35; }
  50% { transform: scale(1.35); opacity: 0.12; }
}

.loco-choices {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: center;
  width: 100%;
}

/* Links unten sitzt der Zurück-Knopf; die Auswahl darf nicht darunter laufen. */
.loco-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  max-width: 100%;
  padding-inline: 84px;
}

.loco-choice {
  width: clamp(56px, 9vh, 84px);
  height: clamp(56px, 9vh, 84px);
  padding: 0;
  border: 3px solid rgba(255, 255, 255, 0.85);
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.62);
  box-shadow: 0 4px 12px rgba(36, 48, 71, 0.18);
  cursor: pointer;
  overflow: hidden;
  display: grid;
  place-items: center;
  transition: transform 0.14s ease, border-color 0.14s ease;
}
.loco-choice:hover { transform: translateY(-2px); }
.loco-choice:active { transform: scale(0.94); }
.loco-choice:focus-visible { outline: 3px solid #6c5ce7; outline-offset: 3px; }
.loco-choice.is-current { border-color: #6c5ce7; background: rgba(255, 255, 255, 0.95); }

.part-preview { width: 100%; height: 100%; display: block; }

@media (prefers-reduced-motion: reduce) {
  .loco-camera, .loco-choice, .loco-hotspot-dot, .loco-hotspot-halo { transition: none; }
  .loco-hotspot-halo { animation: none; }
}

/* ===========================================================================
   Wagen-Grossansicht
   ---------------------------------------------------------------------------
   Der Wagen gross, darunter für jedes Spiel eine Kiste mit einem Band je Welt.
   Die Bänder füllen sich von links – ein leeres Band heisst "hier war ich noch
   nie", ein volles "hier ist alles gelöst".
   =========================================================================== */

.train-stage[data-view="wagon"] .train-band { opacity: 0; pointer-events: none; }
.train-stage[data-view="wagon"] .stage-layer { z-index: 4; }

.wagon-detail {
  width: 100%;
  height: 100%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(10px, 3vh, 28px);
  padding: calc(var(--safe-top) + 76px) 16px calc(var(--safe-bottom) + 16px);
  pointer-events: auto;
}

.wagon-hero { flex: 1 1 auto; min-height: 0; width: 100%; display: grid; place-items: center; }
.wagon-hero-svg { width: auto; height: 100%; max-width: 100%; display: block; }

.wagon-shelf {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: clamp(10px, 2vw, 22px);
  padding-inline: 84px;
  max-width: 100%;
}

/* Die Kiste ist ein Knopf: ein Tipp startet ihr Spiel. Sie sieht aus wie
   vorher – der Kasten war immer schon so gross wie eine Tippfläche. */
.wagon-crate {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 10px 12px 12px;
  border: none;
  font: inherit;
  color: inherit;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.72);
  box-shadow: 0 4px 12px rgba(36, 48, 71, 0.16);
  cursor: pointer;
  transition: transform 0.12s ease, background 0.16s ease;
  -webkit-tap-highlight-color: transparent;
}

.wagon-crate:hover { background: rgba(255, 255, 255, 0.92); }
.wagon-crate:active { transform: scale(0.96); }
.wagon-crate:focus-visible { outline: 3px solid #243047; outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .wagon-crate { transition: none; }
}

/* Etwas grösser als die Bänder breit sind: die Bilder tragen mehr als ein
   Haus mit einem Zeichen, und an der Kiste sollen sie ebenso schnell zu
   erkennen sein wie auf der Bühne. */
.crate-icon { width: clamp(52px, 8.5vh, 80px); height: clamp(52px, 8.5vh, 80px); display: block; }

.crate-bands { display: flex; flex-direction: column; gap: 3px; width: clamp(64px, 10vh, 96px); }

.crate-band {
  height: clamp(7px, 1.3vh, 11px);
  border-radius: 999px;
  background: rgba(36, 48, 71, 0.14);
  overflow: hidden;
}
.crate-band.is-full { box-shadow: 0 0 0 1.5px rgba(240, 180, 41, 0.9); }

.crate-fill { display: block; height: 100%; border-radius: 999px; transition: width 0.5s ease; }

/* ===========================================================================
   Die Züge der Gruppe
   ---------------------------------------------------------------------------
   Über dem eigenen Zug, jeder auf seinem eigenen Gleis und mit seinem Namen
   davor. Nur auf dem Startbild: unterwegs geht es um den eigenen Weg.

   Wie hoch und wie breit die Züge stehen, rechnet train-home.js aus und setzt
   es hier ein – das hängt daran, wie hoch der eigene Zug im Fenster steht und
   wie viele Züge oben Platz finden müssen.
   =========================================================================== */

.train-friends {
  position: absolute;
  left: 0;
  right: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  pointer-events: none;
  /* Erst sichtbar, wenn train-home.js Platz und Grösse gemessen hat – sonst
     stünden die Züge für einen Wimpernschlag irgendwo im Bild. */
  opacity: 0;
  transition: opacity 0.4s ease;
}

.train-friends[hidden] { display: none; }
.train-friends[data-ready="1"] { opacity: 1; }

/* Nur auf dem Startbild. Beim Einfahren des eigenen Zugs bleiben sie noch weg:
   erst der eigene Zug, dann die anderen. */
.train-stage:not([data-view="home"]) .train-friends { display: none; }
.train-stage[data-entering] .train-friends { opacity: 0; }

.friend-train {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  max-width: 98%;
  padding: 0;
  border: none;
  /* Ohne die Mindesthöhe für Knöpfe: wie hoch eine Zeile werden darf, sagt
     hier der Platz über dem eigenen Zug. Getroffen wird sie trotzdem – die
     Zeile ist so breit wie ein halbes Bild, und je weniger Züge oben stehen,
     desto höher wird jeder. */
  min-height: 0;
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
  pointer-events: auto;
  border-radius: 14px;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.16s ease;
}

.friend-train:hover { transform: scale(1.02); }
.friend-train:active { transform: scale(0.97); }
.friend-train:focus-visible { outline: 3px solid #fff; outline-offset: 4px; }

/* Der Name steht vor dem Zug, wie das Schild an einem Bahnsteig. Auf dem Bild
   sonst kein Wort – hier eines, weil genau darum geht es: wessen Zug das ist.

   Er hängt neben der Zeile statt in ihr: sonst bestimmte auf einem flachen
   Bildschirm die Höhe des Schilds die Höhe der Zeile, und der Stapel wäre
   höher geworden als der Platz, den train-home.js dafür ausgerechnet hat. */
.friend-name {
  position: absolute;
  right: calc(100% + clamp(6px, 1.2vw, 12px));
  top: 50%;
  transform: translateY(-50%);
  max-width: min(8em, 22vw);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 4px 10px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.88);
  color: #243047;
  font-size: clamp(0.7rem, 1.9vh, 1rem);
  font-weight: 800;
  line-height: 1.3;
  box-shadow: 0 3px 10px rgba(36, 48, 71, 0.2);
}

.friend-svg {
  flex: 0 0 auto;
  display: block;
  height: auto;
  max-width: 100%;
}

@media (prefers-reduced-motion: reduce) {
  .train-friends { transition: none; }
  .friend-train { transition: none; }
  .friend-train:hover { transform: none; }
}

/* --- Der Zug eines anderen, gross --- */

.train-stage[data-view="friend"] .train-band { opacity: 0; pointer-events: none; }
.train-stage[data-view="friend"] .stage-layer { z-index: 4; }

.friend-detail {
  width: 100%;
  height: 100%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(8px, 2.4vh, 24px);
  padding: calc(var(--safe-top) + 76px) 16px calc(var(--safe-bottom) + 16px);
  pointer-events: auto;
}

.friend-detail-name {
  flex: 0 0 auto;
  margin: 0;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 6px 18px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.9);
  color: #243047;
  font-size: clamp(1rem, 2.6vh, 1.45rem);
  font-weight: 900;
  box-shadow: 0 4px 14px rgba(36, 48, 71, 0.2);
}

/* Der Zug ist hier die Auswahl: ein Tipp auf einen Wagen zeigt dessen Spiele.
   Der gewählte Wagen steht voll da, die anderen treten zurück – ohne Rahmen
   und ohne Pfeil, denn beides wäre ein Zeichen mehr, das erklärt werden will. */
.friend-detail-train {
  flex: 0 1 auto;
  min-height: 0;
  width: 100%;
  display: grid;
  place-items: center;
}

.friend-detail-train .train-svg {
  width: min(100%, 760px);
  height: auto;
  max-height: 100%;
  display: block;
}

.friend-detail-train [data-area] {
  cursor: pointer;
  transition: opacity 0.18s ease;
}
.friend-detail-train [data-area]:not(.is-current) { opacity: 0.5; }
.friend-detail-train [data-area]:focus-visible { outline: 3px solid #fff; }

@media (prefers-reduced-motion: reduce) {
  .friend-detail-train [data-area] { transition: none; }
}

/* Am fremden Zug führt die Kiste nicht ins Spiel, sondern in die Bestenliste.
   Die Zahl darunter bleibt: sie beantwortet die einfachere Frage – wie viele
   sind es –, während die Liste die schwierigere beantwortet: wer ist vorn. */
.crate-count {
  font-size: clamp(0.68rem, 1.6vh, 0.86rem);
  font-weight: 800;
  color: #4a5568;
  font-variant-numeric: tabular-nums;
}

/* ===========================================================================
   Die Bestenliste der Gruppe
   ---------------------------------------------------------------------------
   Erreichbar über den Zug eines anderen: Wagen antippen, Kiste antippen. Eine
   Liste, kein Podest – die ersten drei bekommen ihre Farbe, alle anderen
   stehen gleich da. Wer noch nichts gespielt hat, bleibt in der Liste und
   steht blass am Ende: ein Kind, das seinen Namen nicht findet, glaubt, es sei
   nicht dabei.
   =========================================================================== */

.train-stage[data-view="highscore"] .train-band { opacity: 0; pointer-events: none; }
.train-stage[data-view="highscore"] .stage-layer { z-index: 4; }

.highscore {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(6px, 1.6vh, 16px);
  padding: calc(var(--safe-top) + 76px) 16px calc(var(--safe-bottom) + 16px);
  pointer-events: auto;
  overflow: hidden;
}

.highscore-head {
  flex: 0 0 auto;
  max-width: 100%;
  display: flex;
  align-items: center;
  gap: clamp(8px, 2vw, 16px);
  padding: 6px 18px 6px 10px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.9);
  box-shadow: 0 4px 14px rgba(36, 48, 71, 0.2);
}

.highscore-icon {
  flex: 0 0 auto;
  width: clamp(38px, 6vh, 58px);
  height: clamp(38px, 6vh, 58px);
  display: block;
}

.highscore-kicker {
  margin: 0;
  color: #6b7a90;
  font-size: clamp(0.62rem, 1.5vh, 0.78rem);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.highscore-title {
  margin: 0;
  color: #243047;
  font-size: clamp(0.98rem, 2.5vh, 1.4rem);
  font-weight: 900;
  line-height: 1.15;
}

/* Die Level als Auswahl. Sie liegen in einer Reihe und dürfen quer rollen:
   Tier-Sprung hat zehn davon, und zehn Zeilen übereinander nähmen der Liste
   den Platz weg, um die es hier geht. */
.highscore-levels {
  flex: 0 0 auto;
  max-width: 100%;
  display: flex;
  gap: 6px;
  padding: 2px;
  overflow-x: auto;
  scrollbar-width: none;
}
.highscore-levels::-webkit-scrollbar { display: none; }

.highscore-chip {
  flex: 0 0 auto;
  min-height: clamp(30px, 4.4vh, 40px);
  padding: 0 clamp(10px, 2vw, 16px);
  border: none;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.78);
  color: #4a5568;
  font: inherit;
  font-size: clamp(0.72rem, 1.7vh, 0.92rem);
  font-weight: 800;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.16s ease, color 0.16s ease;
  -webkit-tap-highlight-color: transparent;
}

.highscore-chip.active {
  background: #243047;
  color: #fff;
}
.highscore-chip:focus-visible { outline: 3px solid #fff; outline-offset: 2px; }

.highscore-list {
  flex: 1 1 auto;
  min-height: 0;
  width: min(100%, 560px);
  margin: 0;
  padding: 0 2px;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: clamp(4px, 0.9vh, 9px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.highscore-row {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: clamp(28px, 4.6vh, 40px) minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(8px, 2vw, 14px);
  padding: clamp(5px, 1.1vh, 10px) clamp(10px, 2vw, 16px);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.88);
  box-shadow: 0 3px 10px rgba(36, 48, 71, 0.14);
}

/* Der eigene Name steht hervorgehoben da: in einer Liste mit sechs Kindern
   sucht man zuerst sich selbst. */
.highscore-row.is-me {
  background: #fffaf0;
  box-shadow: 0 3px 10px rgba(36, 48, 71, 0.14), inset 0 0 0 2px #f0b429;
}

.highscore-row.is-empty { opacity: 0.62; }

.highscore-rank {
  display: grid;
  place-items: center;
  width: clamp(28px, 4.6vh, 40px);
  height: clamp(28px, 4.6vh, 40px);
  border-radius: 50%;
  background: rgba(36, 48, 71, 0.1);
  color: #4a5568;
  font-size: clamp(0.78rem, 1.9vh, 1rem);
  font-weight: 900;
  font-variant-numeric: tabular-nums;
}

/* Gold, Silber, Bronze – dieselben drei Farben, die ein Kind von jedem
   Treppchen kennt. Danach hört die Färbung auf: der Unterschied zwischen dem
   vierten und dem fünften Platz ist keiner, den man betonen müsste. */
.highscore-row[data-platz="1"] .highscore-rank { background: #f0b429; color: #3a2a05; }
.highscore-row[data-platz="2"] .highscore-rank { background: #c7cdd6; color: #2a3140; }
.highscore-row[data-platz="3"] .highscore-rank { background: #d9a273; color: #3a2409; }

.highscore-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: #243047;
  font-size: clamp(0.82rem, 2vh, 1.05rem);
  font-weight: 800;
}

.highscore-value {
  display: grid;
  justify-items: end;
  gap: 1px;
  text-align: right;
}

.highscore-value strong {
  color: #243047;
  font-size: clamp(0.82rem, 2vh, 1.05rem);
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.highscore-value small {
  color: #6b7a90;
  font-size: clamp(0.64rem, 1.5vh, 0.8rem);
  font-weight: 800;
  white-space: nowrap;
}

.highscore-hint {
  margin: 0;
  padding: 10px 18px;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.86);
  color: #4a5568;
  font-size: clamp(0.78rem, 1.9vh, 0.98rem);
  font-weight: 800;
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .highscore-chip { transition: none; }
}

/* ===========================================================================
   Szenenwahl
   ---------------------------------------------------------------------------
   Öffnet sich mit einem Tipp auf die Landschaft. Gesperrte Szenen sind blass
   und tragen ein Schloss – ein Zeichen, kein Text.
   =========================================================================== */

.scene-picker {
  position: absolute;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  background: rgba(23, 34, 42, 0.55);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  animation: scene-picker-in 0.22s ease both;
}

@keyframes scene-picker-in { from { opacity: 0; } to { opacity: 1; } }

.scene-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: clamp(10px, 2vw, 20px);
  max-width: min(100%, 900px);
  padding: 20px;
}

.scene-choice {
  position: relative;
  width: clamp(96px, 15vw, 150px);
  padding: 0;
  border: 4px solid rgba(255, 255, 255, 0.9);
  border-radius: 16px;
  background: #fff;
  overflow: hidden;
  cursor: pointer;
  line-height: 0;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.28);
  transition: transform 0.15s ease, border-color 0.15s ease;
}
.scene-choice:hover:not(:disabled) { transform: translateY(-3px); }
.scene-choice:active:not(:disabled) { transform: scale(0.96); }
.scene-choice.is-current { border-color: #ffd166; }
.scene-choice:focus-visible { outline: 3px solid #ffd166; outline-offset: 3px; }
.scene-choice.is-locked { cursor: not-allowed; filter: grayscale(0.75); opacity: 0.55; }

.scene-thumb-art { width: 100%; height: auto; display: block; }

.scene-lock {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 34%;
  height: auto;
  color: #ffffff;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5));
}

.scene-close {
  position: absolute;
  top: calc(var(--safe-top) + 14px);
  left: calc(var(--safe-left) + 16px);
  width: 52px;
  height: 52px;
  border-radius: 50%;
  border: 3px solid #fff;
  background: rgba(36, 48, 71, 0.85);
  color: #fff;
  display: grid;
  place-items: center;
  cursor: pointer;
  padding: 0;
}
.scene-close svg { width: 26px; height: 26px; }
.scene-close:focus-visible { outline: 3px solid #ffd166; outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .crate-fill, .scene-choice { transition: none; }
  .scene-picker { animation: none; }
  /* So genau wie die Regeln, die Räder und Landschaft in Gang setzen. Mit
     einem einfachen .train-wheel gewönnen jene und die Räder drehten weiter. */
  .train-stage[data-view] .train-wheel { animation: none; }
  .train-start { animation: none; transition: none; }
}

/* ===========================================================================
   Karten-Merker
   ---------------------------------------------------------------------------
   Ein Spiel auf Zeit, auf derselben Landschaft wie das Startbild. Deshalb hat
   es keine Kachelhülle: kein Kasten, kein Kartenrahmen, nichts, was zwischen
   dem Kind und dem einen Bild in der Mitte steht.

   Die Farbe ist die des Bereichs Geschwindigkeit, dem das Spiel gehört.
   =========================================================================== */

.cm-stage {
  --cm-accent: #F5A623;
  --cm-accent-dark: #b9741a;
  position: fixed;
  inset: 0;
  /* clip statt hidden: die Landschaft ist breiter als der Bildschirm, und ein
     hidden-Kasten lässt sich zwar nicht wischen, aber immer noch verschieben –
     ein Tastendruck auf einen Knopf am Rand schöbe sonst die ganze Bühne zur
     Seite und brächte sie nie mehr zurück. hidden bleibt als Rückfall stehen. */
  overflow: hidden;
  overflow: clip;
  touch-action: manipulation;
}

/* Die Landschaft steht still: es fährt kein Zug, und ein wandernder
   Hintergrund würde beim Vergleichen auf Zeit nur stören. */
.cm-stage .scene-strip,
.cm-stage .scene-bird { animation-play-state: paused; }

/* --- Leiste oben --- */

/* Links steht der Lautsprecher aus kids.js (bei 14 Pixeln vom Rand, gut 70
   breit), rechts die beiden festen Knöpfe für Ton und Konto (bei 14 und 76).
   Die Leiste hält zu beiden Seiten Abstand – darunter läge sie unter ihnen und
   wäre unsichtbar. */
.cm-bar {
  position: absolute;
  top: calc(var(--safe-top) + 12px);
  left: calc(var(--safe-left) + 98px);
  right: calc(var(--safe-right) + 138px);
  /* Über dem Start- und dem Ergebnisbild: wer aus Versehen im falschen Spiel
     gelandet ist, muss auch von dort wieder heraus. Der Platz für die Tafel
     darunter ist im Innenabstand des Überzugs schon freigehalten. */
  z-index: 8;
  display: flex;
  align-items: center;
  gap: 12px;
}

.cm-bar-left { display: flex; gap: 10px; }

/* Der Titel steht in der Mitte des Bildschirms, nicht in der Mitte der Leiste:
   die Leiste endet vor den Knöpfen rechts, ihre Mitte läge sichtbar daneben. */
.cm-title {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  white-space: nowrap;
  font-size: clamp(1.1rem, 2.6vw, 1.6rem);
  font-weight: 900;
  color: #243047;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
}

.cm-icon {
  width: 48px;
  height: 48px;
  padding: 0;
  border-radius: 50%;
  border: 3px solid #fff;
  background: var(--cm-accent);
  color: #fff;
  display: grid;
  place-items: center;
  cursor: pointer;
  box-shadow: 0 6px 16px rgba(36, 48, 71, 0.22);
  transition: transform 0.15s ease;
}
.cm-icon svg { width: 24px; height: 24px; display: block; }
.cm-icon:active { transform: scale(0.93); }
.cm-icon:focus-visible { outline: 3px solid #243047; outline-offset: 3px; }

/* Der Zähler bleibt dezent: eine Zahl mit Haken, kein Balken, kein Kasten in
   Signalfarbe. Er soll im Blick sein, nicht im Weg. */
.cm-count {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 68px;
  justify-content: flex-end;
  font-size: clamp(1.2rem, 3vw, 1.7rem);
  font-weight: 900;
  color: #2f6f4f;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8);
}
.cm-count-tick { width: 22px; height: 22px; color: #3fbf74; }
.cm-count-value { font-variant-numeric: tabular-nums; }

/* --- Zeitbalken --- */

.cm-time {
  position: absolute;
  top: calc(var(--safe-top) + 76px);
  left: calc(var(--safe-left) + 16px);
  right: calc(var(--safe-right) + 16px);
  z-index: 4;
  height: 6px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.55);
  overflow: hidden;
}
/* Uhr und Zähler gehören zum Lauf. Solange keiner läuft, wären sie nur zwei
   Zahlen ohne Bedeutung – und der Balken über die ganze Breite sähe aus wie ein
   Strich quer durchs Bild. */
.cm-stage:not([data-phase="play"]) .cm-time,
.cm-stage:not([data-phase="play"]) .cm-count { visibility: hidden; }

.cm-time-fill {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 4px;
  background: linear-gradient(90deg, var(--cm-accent), var(--cm-accent-dark));
  transform-origin: left center;
  transform: scaleX(1);
}

/* --- Das Spiel --- */

.cm-play {
  position: absolute;
  inset: calc(var(--safe-top) + 88px) 0 calc(var(--safe-bottom) + 12px);
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(10px, 2.4vh, 22px);
}

.cm-prompt {
  margin: 0;
  font-size: clamp(1rem, 2.4vw, 1.4rem);
  font-weight: 800;
  color: #243047;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.75);
}

.cm-card-host { display: grid; place-items: center; }

/* Ohne Rahmen und ohne Kasten: nur das Bild. Ein Rand wäre eine zweite Form
   neben der, auf die es ankommt. */
.cm-card {
  --card-color: #8338ec;
  display: grid;
  place-items: center;
  width: clamp(130px, 28vh, 230px);
  aspect-ratio: 1;
  border-radius: 28px;
  background: none;
  animation: cm-card-in 0.18s ease both;
}
.cm-card-symbol {
  font-size: clamp(4.5rem, 24vh, 10rem);
  line-height: 1;
  filter: drop-shadow(0 6px 14px rgba(36, 48, 71, 0.22));
}
.cm-card.mini { width: clamp(44px, 7vh, 64px); border-radius: 14px; animation: none; }
.cm-card.mini .cm-card-symbol { font-size: clamp(1.6rem, 4.4vh, 2.6rem); }
.cm-card.is-empty .cm-card-symbol { opacity: 0.4; }

@keyframes cm-card-in {
  from { transform: scale(0.82); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}

/* Rückmeldung: ein grüner Ring für richtig, ein rotes Zittern für falsch.
   Beides kurz – die Uhr läuft weiter. */
.cm-card.is-right { animation: cm-right 0.26s ease both; }
.cm-card.is-wrong { animation: cm-wrong 0.4s ease both; }

@keyframes cm-right {
  0% { transform: scale(1); }
  45% { transform: scale(1.12); }
  100% { transform: scale(1); }
}
@keyframes cm-wrong {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-10px) rotate(-3deg); }
  50% { transform: translateX(10px) rotate(3deg); }
  80% { transform: translateX(-5px); }
}

.cm-choices {
  display: flex;
  gap: clamp(12px, 3vw, 28px);
}

.cm-choice {
  min-width: clamp(120px, 24vw, 190px);
  min-height: clamp(58px, 11vh, 84px);
  padding: 0 18px;
  border-radius: 22px;
  border: 4px solid #fff;
  font-size: clamp(1.05rem, 2.6vw, 1.5rem);
  font-weight: 900;
  color: #fff;
  cursor: pointer;
  box-shadow: 0 8px 20px rgba(36, 48, 71, 0.24);
  transition: transform 0.12s ease;
}
.cm-choice.is-same { background: #3fbf74; }
.cm-choice.is-other { background: #e2694f; }
.cm-choice:active { transform: scale(0.95); }
.cm-choice:focus-visible { outline: 3px solid #243047; outline-offset: 3px; }

/* --- Start und Ergebnis --- */

.cm-overlay {
  position: absolute;
  inset: 0;
  z-index: 6;
  display: grid;
  place-items: center;
  padding: calc(var(--safe-top) + 84px) 16px calc(var(--safe-bottom) + 16px);
  background: rgba(23, 34, 42, 0.42);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  animation: cm-fade 0.2s ease both;
}
@keyframes cm-fade { from { opacity: 0; } to { opacity: 1; } }

.cm-panel {
  width: min(520px, 100%);
  max-height: 100%;
  overflow-y: auto;
  padding: clamp(16px, 3vh, 26px);
  border-radius: 26px;
  background: #fff;
  box-shadow: 0 18px 44px rgba(36, 48, 71, 0.3);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(8px, 1.6vh, 16px);
}

/* Der Startknopf pulst wie das Startsignal am Zug – dasselbe Zeichen für
   dasselbe: hier geht es los.

   Gepulst wird ein Ring um den Knopf, nicht der Knopf selbst. Ein Ziel, das
   grösser und wieder kleiner wird, trifft ein Kind schlechter; der Ring sagt
   dasselbe und lässt den Knopf stehen. */
.cm-start {
  position: relative;
  min-width: 200px;
  min-height: 66px;
  padding: 0 30px;
  border: none;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--cm-accent), var(--cm-accent-dark));
  color: #fff;
  font-size: clamp(1.2rem, 3vw, 1.6rem);
  font-weight: 900;
  cursor: pointer;
  box-shadow: 0 10px 26px rgba(185, 116, 26, 0.4);
}
.cm-start::after {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 999px;
  border: 4px solid var(--cm-accent);
  pointer-events: none;
  animation: cm-pulse 2.1s ease-in-out infinite;
}
.cm-start:active { transform: scale(0.96); }
.cm-start:focus-visible { outline: 3px solid #243047; outline-offset: 6px; }

@keyframes cm-pulse {
  0%, 100% { transform: scale(1); opacity: 0.55; }
  50% { transform: scale(1.09); opacity: 0.1; }
}

.cm-result-label { margin: 0; font-size: 0.95rem; font-weight: 800; color: #5c6a67; letter-spacing: 0.04em; text-transform: uppercase; }
.cm-result-score {
  margin: 0;
  font-size: clamp(2.8rem, 9vh, 4.4rem);
  font-weight: 900;
  line-height: 1;
  color: var(--cm-accent-dark);
  font-variant-numeric: tabular-nums;
}
.cm-result-detail { margin: 0; font-size: 1rem; font-weight: 700; color: #243047; }

/* Die Bestenliste. Der frische Lauf ist hervorgehoben – ohne die Markierung
   müsste ein Kind seine eigene Zahl suchen. */
.cm-scores {
  width: min(280px, 100%);
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.cm-score-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 7px 14px;
  border-radius: 12px;
  background: #f4f5f2;
  font-weight: 800;
  color: #243047;
}
.cm-score-item.is-new { background: var(--cm-accent); color: #fff; }
.cm-score-item.is-empty { opacity: 0.45; }
.cm-score-rank { font-size: 0.95rem; opacity: 0.75; }
.cm-score-value { font-size: 1.15rem; font-variant-numeric: tabular-nums; }

.cm-runs {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 700;
  color: #4a5b52;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
}
.cm-runs.is-done { color: #2f6f4f; }
/* Im Ergebnisfenster steht der Satz auf Weiss – dort braucht er keinen Schimmer
   hinter sich. */
.cm-panel .cm-runs { text-shadow: none; color: #5c6a67; }

.cm-actions { display: flex; gap: 16px; }
.cm-icon.big { width: 64px; height: 64px; }
.cm-icon.big svg { width: 32px; height: 32px; }

/* ===========================================================================
   Der Wagen wächst
   ---------------------------------------------------------------------------
   Nach einem geschafften Level, beim Zurückkommen auf die Bühne: der Wagen
   gross, sein Balken füllt sich, und die Bauart wächst Stufe um Stufe mit.
   Ein Tipp bricht ab – wer weiter will, soll nicht warten müssen.
   =========================================================================== */

.wagon-reward {
  position: absolute;
  inset: 0;
  z-index: 12;
  display: grid;
  place-items: center;
  padding: calc(var(--safe-top) + 12px) calc(var(--safe-right) + 12px) calc(var(--safe-bottom) + 12px) calc(var(--safe-left) + 12px);
  background: rgba(23, 34, 42, 0.5);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  animation: wagon-reward-in 0.24s ease both;
}
/* Zum Antippen wird die Feier erst, wenn sie durch ist. */
.wagon-reward.is-done { cursor: pointer; }

/* Die Tafel liegt quer, wie der Bildschirm: links der Wagen, rechts Name,
   Balken und Knopf. Der Wagen ist fast quadratisch – untereinander gestapelt
   ragte er auf einem Handy weit unter den Rand, und der Knopf mit ihm. */
.wagon-reward-card {
  width: min(680px, 100%);
  max-height: 100%;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas:
    "bild titel"
    "bild balken"
    "bild note"
    "bild weiter";
  align-items: center;
  justify-items: center;
  column-gap: clamp(12px, 3vw, 30px);
  row-gap: clamp(6px, 1.8vh, 14px);
  padding: clamp(12px, 3vh, 26px) clamp(14px, 3vw, 30px);
  border-radius: 28px;
  background: #fff;
  box-shadow: 0 22px 60px rgba(31, 42, 51, 0.4);
  animation: wagon-reward-pop 0.34s cubic-bezier(0.2, 1.3, 0.4, 1) both;
}

.wagon-reward-title {
  grid-area: titel;
  margin: 0;
  font-size: clamp(1.05rem, 2.8vw, 1.5rem);
  font-weight: 950;
  color: var(--reward-color, #6c5ce7);
  letter-spacing: 0.02em;
  text-align: center;
}

.wagon-reward-stage { grid-area: bild; display: grid; place-items: center; }
/* Die Höhe entscheidet: sie ist auf dem Handy das knappe Mass. */
.wagon-reward-svg { height: clamp(120px, 54vh, 320px); width: auto; max-width: 40vw; display: block; }

/* Das Teil, das gerade dazukommt, springt hervor: aus dem Nichts über die
   volle Grösse hinaus und zurück, kurz heller als alles andere. So ist ohne
   ein Wort zu sehen, was neu ist – auch wenn drei Teile nacheinander kommen.
   transform-box: fill-box, damit jedes Teil um seine eigene Mitte wächst und
   nicht um die Ecke des Wagens. */
.wagon-step.is-new {
  transform-box: fill-box;
  transform-origin: center;
  animation: wagon-step-in 0.7s cubic-bezier(0.2, 1.4, 0.4, 1) both;
}
@keyframes wagon-step-in {
  0% { transform: scale(0.2); opacity: 0; filter: brightness(2) drop-shadow(0 0 4px rgba(255, 255, 255, 0.9)); }
  45% { opacity: 1; filter: brightness(1.7) drop-shadow(0 0 6px rgba(255, 255, 255, 0.9)); }
  100% { transform: scale(1); opacity: 1; filter: brightness(1) drop-shadow(0 0 0 rgba(255, 255, 255, 0)); }
}

/* Der Hinweis "auf den Startbildschirm" liegt wie eine Feier über der Bühne,
   ist aber eine Tafel zum Lesen: einspaltig, Icon oben, Text darunter, zwei
   nummerierte Schritte mit den Symbolen, die iOS selbst zeigt, und Knöpfe mit
   Wörtern statt des grünen Hakens – hier lesen Eltern. */
.install-hint-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(8px, 1.8vh, 14px);
  width: min(520px, 100%);
  text-align: center;
  overflow: auto;
}
.install-hint-icon {
  width: clamp(56px, 12vh, 84px);
  height: auto;
  border-radius: 22%;
  box-shadow: 0 8px 20px rgba(36, 48, 71, 0.22);
}
.install-hint-title { color: #4c3fd6; }
.install-hint-text {
  margin: 0;
  font-size: clamp(0.95rem, 2.2vw, 1.08rem);
  line-height: 1.4;
  color: #2d3a45;
  max-width: 44ch;
}
.install-hint-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 8px;
  width: 100%;
  max-width: 44ch;
  text-align: left;
}
.install-hint-step {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 12px;
  border-radius: 14px;
  background: #f3f0ff;
  color: #2d3a45;
  font-size: clamp(0.92rem, 2.1vw, 1.02rem);
  line-height: 1.35;
}
.install-hint-step-number {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: #6c5ce7;
  color: #fff;
  font-weight: 900;
  font-size: 0.9rem;
}
.install-hint-symbol {
  width: 1.35em;
  height: 1.35em;
  vertical-align: -0.3em;
  color: #2b7de9;
  flex: 0 0 auto;
}
.install-hint-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 4px;
}
.install-hint-primary,
.install-hint-later {
  border: 0;
  border-radius: 999px;
  padding: 12px 22px;
  font-size: 1rem;
  font-weight: 800;
  cursor: pointer;
  min-height: 46px;
}
.install-hint-primary {
  background: #3fbf74;
  color: #fff;
  box-shadow: 0 8px 20px rgba(36, 48, 71, 0.22);
}
.install-hint-primary:disabled { opacity: 0.6; cursor: default; }
.install-hint-later {
  background: #eef1f5;
  color: #2d3a45;
}

/* Die zwölf Schritte als Punkte, in Dreiergruppen – ein Spiel je Gruppe. Die
   vollen sind in der Farbe des Bereichs, der neue leuchtet kurz auf. */
.wagon-reward-steps,
.wagon-steps {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: clamp(4px, 0.9vw, 7px);
}
.wagon-reward-steps { grid-area: balken; }
.wagon-steps { flex: 0 0 auto; pointer-events: none; }

.reward-dot {
  width: clamp(11px, 2.4vh, 17px);
  height: clamp(11px, 2.4vh, 17px);
  border-radius: 50%;
  background: #e3e8ef;
  box-shadow: inset 0 0 0 2px rgba(36, 48, 71, 0.08);
  transition: background 0.25s ease;
}
.reward-dot:nth-child(3n):not(:last-child) { margin-right: clamp(6px, 1.4vw, 12px); }
.reward-dot.is-done { background: var(--reward-color, #6c5ce7); box-shadow: none; }
.reward-dot.is-new {
  animation: reward-dot-pop 0.55s cubic-bezier(0.2, 1.4, 0.4, 1) both;
  box-shadow: 0 0 0 3px #fff, 0 0 12px var(--reward-color, #6c5ce7);
}
@keyframes reward-dot-pop {
  0% { transform: scale(0.4); }
  60% { transform: scale(1.5); }
  100% { transform: scale(1); }
}
.wagon-steps .reward-dot {
  width: clamp(9px, 1.8vh, 14px);
  height: clamp(9px, 1.8vh, 14px);
  background: rgba(255, 255, 255, 0.6);
  box-shadow: inset 0 0 0 2px rgba(36, 48, 71, 0.14);
}
.wagon-steps .reward-dot.is-done { background: var(--reward-color, #6c5ce7); box-shadow: 0 1px 3px rgba(36, 48, 71, 0.25); }

.wagon-reward-note {
  grid-area: note;
  margin: 0;
  font-size: clamp(0.95rem, 2.2vw, 1.2rem);
  font-weight: 800;
  color: #4a5b52;
  text-align: center;
}

/* Der Weiter-Knopf: rund und grün wie das Startsignal, mit einem Haken darin.
   Er kommt erst, wenn die Feier durch ist, und pulst dann wie der Startknopf –
   dasselbe Zeichen für dasselbe: hier geht es weiter. */
.wagon-reward-next {
  grid-area: weiter;
  position: relative;
  width: clamp(52px, 13vh, 68px);
  height: clamp(52px, 13vh, 68px);
  padding: 0;
  border: 3px solid #fff;
  border-radius: 50%;
  background: #3fbf74;
  color: #fff;
  display: grid;
  place-items: center;
  cursor: pointer;
  box-shadow: 0 8px 20px rgba(36, 48, 71, 0.26);
  opacity: 0;
  transform: scale(0.6);
  pointer-events: none;
  transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.2, 1.3, 0.4, 1);
}
.wagon-reward-next svg { width: 58%; height: 58%; display: block; }
.wagon-reward-next::after {
  content: "";
  position: absolute;
  inset: -7px;
  border-radius: 50%;
  border: 3px solid #3fbf74;
  opacity: 0;
  pointer-events: none;
}
.wagon-reward.is-done .wagon-reward-next { opacity: 1; transform: none; pointer-events: auto; }
.wagon-reward.is-done .wagon-reward-next::after { animation: cm-pulse 2.1s ease-in-out infinite; }
.wagon-reward-next:active { transform: scale(0.93); }
.wagon-reward-next:focus-visible { outline: 3px solid #243047; outline-offset: 4px; }

@keyframes wagon-reward-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes wagon-reward-pop {
  from { opacity: 0; transform: scale(0.86) translateY(14px); }
  to { opacity: 1; transform: none; }
}

/* Die neue Landschaft: dieselbe Tafel, statt des Wagens ihr Vorschaubild. */
.scene-reward-thumb {
  grid-area: bild;
  width: clamp(150px, 32vw, 340px);
  border-radius: 20px;
  overflow: hidden;
  box-shadow: 0 10px 24px rgba(31, 42, 51, 0.28);
  animation: scene-reward-in 0.5s ease both;
}

.scene-reward-thumb svg { display: block; width: 100%; height: auto; }

@keyframes scene-reward-in {
  from { opacity: 0; transform: scale(0.8) rotate(-2deg); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .wagon-reward, .wagon-reward-card, .wagon-step.is-new, .reward-dot.is-new { animation: none; }
  .reward-dot { transition: none; }
  .wagon-reward-next { transition: none; }
  .wagon-reward.is-done .wagon-reward-next::after { animation: none; opacity: 0.5; }
  .scene-reward-thumb { animation: none; }
}

/* ===========================================================================
   Die Rätselseiten auf der Landschaft
   ---------------------------------------------------------------------------
   Arukone, Battleships, Tiergehege, Buchstabenjagd, Wortdetektiv, Kakuro und
   Hidoku standen bisher auf einem eigenen Farbverlauf und sahen damit aus wie
   eine zweite App. Jetzt liegt dieselbe Landschaft dahinter wie überall sonst.

   Durchsichtig wird alles ausser dem Spielfeld: Gitter, Zahlen und Schiffe
   müssen auf einen Blick lesbar bleiben, und über einer Wiese mit Bäumen wären
   sie es nicht.
   =========================================================================== */

.puzzle-scene {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.puzzle-scene .scene { position: absolute; inset: 0; }

body.has-scene { background: #a8ddf0; }
body.has-scene .app-shell { position: relative; z-index: 1; }

/* Überschrift, Levelwahl-Vorspann und Levelbeschreibung fallen weg: sie standen
   zwischen dem Kind und dem Rätsel.

   Der Weg zurück stand früher mit in dieser Zeile und verschwand dadurch von
   der Levelwahl – dort blieb kein Knopf mehr, der zurückführt, und in der
   installierten App gibt es keine Zurück-Taste des Browsers. Ein Weg zurück
   wird nie ausgeblendet; er trägt jetzt die Klasse .controls und steht als
   runder Knopf an derselben Stelle wie im Rätsel. */
body.has-scene .hero,
body.has-scene .start-copy,
body.has-scene #puzzle-description { display: none; }

body.has-scene .start-panel,
body.has-scene .game-panel,
body.has-scene .status-row {
  background: none;
  border: none;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* Das Spielfeld behält seine helle Tafel. */
body.has-scene .game-card {
  background: rgba(255, 255, 255, 0.94);
  border: none;
  box-shadow: 0 14px 34px rgba(31, 42, 51, 0.24);
}

body.has-scene .small-label,
body.has-scene #puzzle-title,
body.has-scene .status {
  color: #243047;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
}

/* Die Knöpfe oben links – dieselben wie im Karten-Merker: rund, weiss, fest an
   derselben Stelle. Sie hängen nicht mehr im Fluss über dem Spielfeld, sondern
   stehen in der Leiste neben dem Lautsprecher. */
body.has-scene .controls {
  position: fixed;
  top: calc(var(--safe-top) + 12px);
  left: calc(var(--safe-left) + 98px);
  z-index: 8;
  width: auto;
  margin: 0;
  padding: 0;
  gap: 10px;
  background: none;
  border: none;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* Gefüllt in der Farbe des Bereichs, weisses Zeichen darauf – genau wie die
   Knöpfe der anderen Spiele. Weiss mit lila Zeichen war das Lila der alten
   App; im grünen Bereich sind die Knöpfe grün, im roten rot. */
body.has-scene .controls .icon-button {
  width: 48px;
  min-width: 48px;
  height: 48px;
  min-height: 48px;
  border-radius: 50%;
  border: 3px solid #fff;
  background: var(--area-accent, var(--primary));
  color: #fff;
  font-size: 1.4rem;
  box-shadow: 0 6px 16px rgba(36, 48, 71, 0.22);
}

body.has-scene .controls .icon-button:hover:not(:disabled),
body.has-scene .controls .icon-button:focus-visible {
  background: var(--area-accent-dark, var(--primary-dark));
  color: #fff;
  transform: none;
}

body.has-scene .controls .icon-button:active { transform: scale(0.94); }

/* Der Platz, den die Leiste oben braucht – sonst läge das Spielfeld darunter. */
body.has-scene.puzzle-active .app-shell { padding-top: calc(var(--safe-top) + 74px); }

/* Die Karte bekommt die ganze Höhe der Spielfläche. Die Knöpfe stehen fest in
   der Leiste und brauchen keine Rasterzeile mehr – vorher blieb die "auto"-
   Zeile für sie stehen, die Karte rutschte hinein, und ihre Höhe hing damit
   an ihrem Inhalt. Der Inhalt aber, das Brett, wird aus der Höhe der Karte
   gerechnet: die Karte schrumpfte aufs Brett, das Brett auf die Karte, und
   mit jeder Eingabe wurden beide ein paar Bildpunkte kleiner. */
body.has-scene.puzzle-active .game-panel {
  grid-template-rows: minmax(0, 1fr);
  gap: 0;
}

/* ===========================================================================
   Tier-Sprung auf der Spielhülle
   ---------------------------------------------------------------------------
   Die Levelwahl steht auf derselben Landschaft wie die anderen Spiele: zehn
   Tiere in zwei Reihen und ein Startknopf, sonst nichts. Der Name des Tiers,
   sein Ort und die Zahl der Leckerbissen sind weg – ein Kind sieht das Tier
   und die Sterne, und mehr ist vor der Wahl nicht zu entscheiden.

   Die Farbe ist die des Bereichs Geschwindigkeit.
   =========================================================================== */

.ts-stage { --cm-accent: #F5A623; --cm-accent-dark: #b9741a; }

/* Fünf nebeneinander, zwei Reihen: die Höhe ist knapp, weil darunter noch der
   Startknopf steht. */
.ts-stage .runner-map {
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: clamp(6px, 1.4vw, 14px);
  width: min(94vw, 760px);
  max-width: none;
  margin: 0;
}

.ts-stage .runner-level {
  gap: 0;
  padding: clamp(4px, 1vh, 10px) 4px;
  border-radius: 18px;
  border-width: 2px;
  box-shadow: 0 6px 14px rgba(50, 44, 109, 0.16);
}

.ts-stage .runner-level-art { width: clamp(48px, 8vw, 84px); height: auto; }
.ts-stage .runner-level-stars { font-size: 0.8rem; line-height: 1; }
.ts-stage .runner-level-badge { top: 4px; left: 6px; }

@media (max-height: 560px) {
  .ts-stage .runner-map { width: min(94vw, 640px); }
  .ts-stage .runner-level-art { width: clamp(38px, 6vw, 60px); }
}

/* ===========================================================================
   Memory
   ---------------------------------------------------------------------------
   Die Karten liegen auf einer weissen Tafel, wie die Rätsel. Verdeckt ist eine
   Karte ein schlichtes Rechteck in der Bereichsfarbe; aufgedeckt bleibt nur
   das Bild übrig, ohne Rahmen darum. Ein Kartenrand um ein offenes Bild wäre
   eine zweite Form neben der, die zu merken ist.
   =========================================================================== */

.me-stage { --cm-accent: #7C5CE6; --cm-accent-dark: #5a41b8; }

/* --- Die Wahl der Grösse --- */

.me-groessen { display: flex; flex-wrap: wrap; justify-content: center; gap: clamp(8px, 2vw, 18px); }

.me-groesse {
  position: relative;
  display: grid;
  place-items: center;
  min-width: clamp(58px, 12vw, 92px);
  padding: clamp(10px, 2vh, 16px) clamp(6px, 1.4vw, 14px);
  border: 3px solid #fff;
  border-radius: 22px;
  background: rgba(255, 255, 255, 0.9);
  cursor: pointer;
  box-shadow: 0 8px 18px rgba(31, 42, 51, 0.18);
  transition: transform 0.12s ease, background 0.16s ease;
  -webkit-tap-highlight-color: transparent;
}

.me-groesse:hover { background: #fff; }
.me-groesse:active { transform: scale(0.95); }
.me-groesse:focus-visible { outline: 3px solid #243047; outline-offset: 3px; }
.me-groesse.is-done { border-color: #2f9e44; }

.me-groesse-zahl {
  font-size: clamp(1.4rem, 4vw, 2.2rem);
  font-weight: 950;
  line-height: 1;
  color: var(--cm-accent-dark);
}

.me-groesse-haken {
  position: absolute;
  top: -10px;
  right: -8px;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: #2f9e44;
  color: #fff;
  font-size: 0.95rem;
  font-weight: 900;
}

/* --- Die Tafel --- */

/* Weiss und rund, wie die Karte der Rätsel. Auf der Landschaft mit ihren
   Bäumen und Hügeln wären zwanzig kleine Karten nur noch Gewimmel. */
.me-tafel {
  --me-rand: clamp(8px, 1.6vh, 16px);
  flex: 0 0 auto;
  padding: var(--me-rand);
  border-radius: clamp(16px, 3vh, 26px);
  background: rgba(255, 255, 255, 0.94);
  box-shadow: 0 14px 34px rgba(31, 42, 51, 0.24);
}

/* --- Das Spielfeld --- */

/* Die Breite ist von beiden Seiten begrenzt: vom Bildschirm und von der Höhe,
   die zwischen Leiste und unterem Rand übrig bleibt. Nur nach der Breite zu
   gehen hiesse: bei vierundzwanzig Karten fällt die unterste Reihe aus dem
   Bild. --me-platz ist die Höhe, die die Bühne dem Feld lässt: der Bildschirm
   ohne Leiste, Zeile, Rand und den Rand der Tafel. */
.me-feld {
  --me-platz: calc(100vh - var(--safe-top, 0px) - var(--safe-bottom, 0px) - 190px - 2 * var(--me-rand, 12px));
  --me-platz: calc(100dvh - var(--safe-top, 0px) - var(--safe-bottom, 0px) - 190px - 2 * var(--me-rand, 12px));
  display: grid;
  gap: clamp(5px, 1.2vw, 14px);
  grid-template-columns: repeat(var(--me-spalten, 4), minmax(0, 1fr));
  grid-auto-rows: 1fr;
  width: min(94vw - 2 * var(--me-rand, 12px), 940px, var(--me-platz) * var(--me-spalten, 4) / var(--me-zeilen, 2));
  aspect-ratio: var(--me-spalten, 4) / var(--me-zeilen, 2);
}

.me-feld[data-karten="8"] { --me-spalten: 4; --me-zeilen: 2; }
.me-feld[data-karten="12"] { --me-spalten: 4; --me-zeilen: 3; }
.me-feld[data-karten="16"] { --me-spalten: 4; --me-zeilen: 4; }
.me-feld[data-karten="20"] { --me-spalten: 5; --me-zeilen: 4; }
.me-feld[data-karten="24"] { --me-spalten: 6; --me-zeilen: 4; }
.me-feld[data-karten="30"] { --me-spalten: 6; --me-zeilen: 5; }

/* Auf dem Handy stehen nie mehr als vier nebeneinander: schmaler wäre eine
   Karte kaum noch zu treffen. Nur die dreissig Karten der Stufe "schwer"
   stehen zu fünft – acht Reihen zu vieren passten nicht mehr aufs Bild. */
@media (max-width: 560px) {
  .me-feld[data-karten="20"] { --me-spalten: 4; --me-zeilen: 5; }
  .me-feld[data-karten="24"] { --me-spalten: 4; --me-zeilen: 6; }
  .me-feld[data-karten="30"] { --me-spalten: 5; --me-zeilen: 6; }
}

.me-karte {
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
  padding: 0;
  border: none;
  border-radius: clamp(10px, 2vw, 18px);
  background: linear-gradient(140deg, var(--cm-accent), var(--cm-accent-dark));
  box-shadow: 0 5px 12px rgba(31, 42, 51, 0.24);
  cursor: pointer;
  transition: transform 0.14s ease, background 0.14s ease, box-shadow 0.14s ease;
  -webkit-tap-highlight-color: transparent;
}

.me-karte::after {
  content: "";
  width: 34%;
  height: 34%;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.28);
}

.me-karte:active:not(:disabled) { transform: scale(0.94); }
.me-karte:focus-visible { outline: 3px solid #243047; outline-offset: 3px; }

/* Eine aufgedeckte Karte ist ein abgeschalteter Knopf – sonst liesse sie sich
   ein zweites Mal antippen. Abgeschaltete Knöpfe blendet die App auf knapp die
   Hälfte aus (button:disabled, ganz oben); für eine Karte ist das falsch: sie
   ist nicht ausser Betrieb, sie liegt offen da, und ihr Bild war dadurch blass.
   Kacheln-Knobeln, Fischteich und Was fehlt? nehmen die Regel schon zurück –
   Memory hat sie als einziges Spiel bis jetzt mitgenommen. */
.me-karte:disabled { cursor: default; opacity: 1; }

/* Offen: die Vorderseite der Karte – heller Karton mit einem Ring in der Farbe
   des Bereichs, darauf das Bild. Vorher fiel der Kasten beim Umdrehen ganz weg
   und das Bild schwebte allein auf der weissen Tafel; neben den kräftigen
   Rückseiten sah eine aufgedeckte Karte damit aus, als fehlte sie. Jetzt ist
   jede umgedrehte Karte von Anfang an eine ganze Karte. */
.me-karte.is-offen {
  background: linear-gradient(150deg, #fffdf6 40%, #dbcdff);
  box-shadow: 0 5px 12px rgba(31, 42, 51, 0.24), inset 0 0 0 4px var(--cm-accent-dark);
  cursor: default;
  animation: me-auf 0.16s ease both;
}

.me-karte.is-offen::after { content: none; }

/* Ein Paar gefunden: der Ring wird grün, und beide Karten pulsieren einmal
   kurz. Das ist das Zeichen, dass es gestimmt hat – vorher lag es allein
   darin, dass die Karten überhaupt erst jetzt schön aussahen. Der Ring bleibt
   grün, bis sie weg sind; wer weniger Bewegung eingestellt hat, sieht ihn
   ohne das Pulsieren. */
.me-karte.is-paar {
  background: linear-gradient(150deg, #ecfced 40%, #b3e8c1);
  box-shadow: 0 5px 12px rgba(31, 42, 51, 0.24), inset 0 0 0 4px #2f9e44;
  animation: me-paar 0.42s ease both;
}

.me-karte-bild {
  font-size: clamp(1.5rem, 5.5vw, 3rem);
  line-height: 1;
  filter: drop-shadow(0 3px 6px rgba(31, 42, 51, 0.28));
}

/* Gefunden: das Paar hüpft einmal und ist weg – der Platz bleibt frei, damit
   die Karten daneben nicht wandern. Die Karte wird danach nie mehr angefasst;
   das Hüpfen läuft deshalb genau einmal. */
.me-karte.is-weg {
  background: none;
  box-shadow: none;
  cursor: default;
  visibility: hidden;
  pointer-events: none;
  animation: me-weg 0.4s ease both;
}

.me-karte.is-weg::after { content: none; }

@keyframes me-auf {
  from { transform: scaleX(0.55); }
  to { transform: scaleX(1); }
}

@keyframes me-paar {
  0% { transform: scale(1); }
  35% { transform: scale(1.12); }
  70% { transform: scale(0.97); }
  100% { transform: scale(1); }
}

@keyframes me-weg {
  0% { visibility: visible; transform: scale(1); opacity: 1; }
  40% { visibility: visible; transform: scale(1.15); opacity: 1; }
  100% { visibility: hidden; transform: scale(0.5); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .me-groesse, .me-karte { transition: none; }
  .me-karte.is-offen, .me-karte.is-paar, .me-karte.is-weg { animation: none; }
}

/* ===========================================================================
   Rucksack packen
   ---------------------------------------------------------------------------
   Zwei Schritte, die verschieden aussehen: beim Aussuchen stehen die
   Gegenstände auf goldenen Ringen, mit einem "Neu"-Schild über ihnen; beim
   Packen auf lila Ringen, mit einer Reihe nummerierter Plätze darüber. Der
   Ring gehört zum Schritt, nicht zur Sache – alle Gegenstände eines Schritts
   tragen denselben, und so wird er nicht mitgemerkt.

   Unten rechts steht der Rucksack; jeder Gegenstand fliegt sichtbar hinein.
   Die Farbe ist die des Bereichs Gedächtnis, der Rucksack selbst ist orange
   wie sein Haus auf dem Startbild.
   =========================================================================== */

.rs-stage {
  --cm-accent: #7C5CE6;
  --cm-accent-dark: #5a41b8;
  --rs-gold: #f2b134;
  --rs-gold-dark: #b97a12;
  --rs-rucksack: clamp(76px, 15vh, 118px);
}

/* --- Die Zeile über den Gegenständen --- */

/* Ein Schild vor dem Satz: gold mit "Neu" beim Aussuchen, lila mit der Nummer
   beim Packen. Wer nicht liest, sieht an der Farbe, welcher Schritt läuft. */
.rs-prompt {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  flex: 0 0 auto;
}

.rs-chip {
  display: inline-grid;
  place-items: center;
  min-width: 40px;
  height: 40px;
  padding: 0 12px;
  border-radius: 999px;
  border: 3px solid #fff;
  font-size: 1.05rem;
  font-weight: 950;
  line-height: 1;
  color: #fff;
  text-shadow: none;
  box-shadow: 0 4px 12px rgba(31, 42, 51, 0.2);
  white-space: nowrap;
}

.rs-chip.is-neu { background: var(--rs-gold); color: #4a2f00; }
.rs-chip.is-packen { background: var(--cm-accent); }

/* --- Die Plätze beim Packen --- */

/* Eine Reihe Kreise mit Nummern: die gepackten grün mit Haken, der Platz, der
   dran ist, lila und pulsend, der Rest blass. Bilder stehen keine darin –
   die sind ja gerade das, was zu merken ist. */
.rs-plaetze {
  display: flex;
  gap: clamp(6px, 1.2vw, 10px);
  flex: 0 0 auto;
}

.rs-platz {
  width: clamp(30px, 5.2vh, 42px);
  height: clamp(30px, 5.2vh, 42px);
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 3px solid #fff;
  background: rgba(255, 255, 255, 0.86);
  color: #7b8190;
  font-size: clamp(0.85rem, 2.4vh, 1.1rem);
  font-weight: 950;
  line-height: 1;
  box-shadow: 0 3px 10px rgba(31, 42, 51, 0.16);
}

.rs-platz.is-drin { background: #3fbf74; color: #fff; }
.rs-platz.is-dran { background: var(--cm-accent); color: #fff; animation: rs-dran 1s ease-in-out infinite; }

@keyframes rs-dran {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.16); }
}

/* --- Die Gegenstände --- */

/* Links und rechts bleibt Platz für den Rucksack: die Reihe darf nicht unter
   ihm liegen.

   Und sie soll eine Reihe bleiben. Wie gross ein Gegenstand sein darf, hängt
   deshalb nicht an der Fensterbreite allein, sondern daran, was ihm zusteht:
   die Breite des Felds, geteilt durch die Zahl der Sachen, die nebeneinander
   stehen (--rs-platz). Vorher wuchs das Bild mit 11vw – auf einem flachen
   Handy-Bild (700 × 320) waren vier Sachen dann zusammen zwei Pixel breiter
   als das Feld, die vierte rutschte in eine zweite Reihe, und die stand unter
   dem unteren Rand: ein Gegenstand, den kein Kind sehen oder antippen kann.
   Auf "schwer" mit sechs Sachen wäre es noch enger geworden. */
.rs-feld {
  --rs-abstand: clamp(10px, 2.6vw, 30px);
  --rs-breite: min(100vw - 2 * (var(--rs-rucksack) + 36px), 860px);
  /* Wie viele nebeneinander stehen. rucksack.js schreibt die Zahl als
     data-anzahl an das Feld; sie ist 1 (ein Gegenstand allein zum Merken), 3,
     4 oder 6 (die Stufen leicht, mittel, schwer). */
  --rs-n: 4;
  --rs-platz: calc((var(--rs-breite) - (var(--rs-n) - 1) * var(--rs-abstand)) / var(--rs-n));
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--rs-abstand);
  width: var(--rs-breite);
}

.rs-feld[data-anzahl="1"] { --rs-n: 1; }
.rs-feld[data-anzahl="2"] { --rs-n: 2; }
.rs-feld[data-anzahl="3"] { --rs-n: 3; }
.rs-feld[data-anzahl="5"] { --rs-n: 5; }
.rs-feld[data-anzahl="6"] { --rs-n: 6; }

.rs-item {
  --rs-polster: clamp(6px, 1.4vh, 12px);
  display: grid;
  place-items: center;
  /* Mehr als seinen Platz nimmt keiner ein – auch dann nicht, wenn ein
     Zeichen einmal breiter aufträgt als gerechnet. Dann ragt das Bild über
     seinen Ring hinaus; die Reihe bricht trotzdem nicht um. */
  max-width: var(--rs-platz);
  padding: var(--rs-polster);
  border: none;
  background: transparent;
  cursor: pointer;
  border-radius: 50%;
  transition: transform 0.12s ease;
  animation: rs-in 0.18s ease both;
  -webkit-tap-highlight-color: transparent;
}

.rs-item-bild {
  /* 1,3 ist etwas mehr, als ein Emoji in dieser Schrift breit aufträgt
     (gemessen: 1,25 mal die Schriftgrösse). Der Rest ist Luft. Nach oben
     begrenzen die Fensterhöhe und 5,4rem – so bleibt auf dem Tablet alles so
     gross wie bisher.

     Die 2rem nach unten sind für den Notfall: ein Fenster, das so schmal ist,
     dass rechnerisch nichts mehr übrig bleibt (ein hohes, schmales Fenster am
     Rechner etwa). Dann bricht die Reihe um wie früher, statt auf null zu
     schrumpfen. Auf keinem Handy und keinem Tablet greift diese Grenze. */
  font-size: min(max(calc((var(--rs-platz) - 2 * var(--rs-polster)) / 1.3), 2rem), 30vh, 5.4rem);
  line-height: 1;
  filter: drop-shadow(0 4px 8px rgba(31, 42, 51, 0.24));
}

/* Hochkant liegt der Platz unten, nicht an der Seite: Der Rucksack steht in
   der unteren Ecke, die Reihe schwebt weit darüber – sie muss ihm also nicht
   ausweichen. Ohne diese Ausnahme blieben auf einem 390 px breiten Handy
   82 px für das ganze Feld. Und statt sechs winziger Sachen nebeneinander
   stehen auf einem schmalen Bild zwei Reihen zu dritt: nach unten ist Luft. */
@media (orientation: portrait) {
  .rs-feld { --rs-breite: min(100vw - 24px, 860px); }
  /* Dafür steht der Rucksack hochkant unter der Reihe statt neben ihr: Unten
     bleibt sein Platz frei, alles darüber rückt nach. Sonst stiege die letzte
     Reihe ihm auf einem kurzen Hochkant-Bild (360 × 640) auf den Kopf. */
  .rs-stage .cm-play { padding-bottom: calc(var(--rs-rucksack) + 16px); }
}

@media (orientation: portrait) and (max-width: 520px) {
  .rs-feld[data-anzahl="4"] { --rs-n: 2; }
  .rs-feld[data-anzahl="5"] { --rs-n: 3; }
  .rs-feld[data-anzahl="6"] { --rs-n: 3; }
}

/* Der eine Gegenstand, den es sich zu merken gilt, steht gross und allein da.
   Auch er darf nicht unter den Rand geraten: auf einem flachen Bild
   entscheidet die Höhe, nicht die Breite. */
.rs-feld[data-anzahl="1"] .rs-item-bild { font-size: clamp(3rem, min(18vw, 34vh), 8rem); }
.rs-item:active { transform: scale(0.9); }
.rs-item:focus-visible { outline: 3px solid #243047; outline-offset: 6px; }

/* Der Ring hinter dem Bild: gold beim Aussuchen, lila beim Packen. */
.rs-stage[data-rs-phase="waehlen"] .rs-item,
.rs-stage[data-rs-phase="merken"] .rs-item {
  background: rgba(255, 244, 214, 0.92);
  box-shadow: 0 0 0 4px var(--rs-gold), 0 8px 18px rgba(31, 42, 51, 0.18);
}

.rs-stage[data-rs-phase="packen"] .rs-item {
  background: rgba(255, 255, 255, 0.92);
  box-shadow: 0 0 0 4px var(--cm-accent), 0 8px 18px rgba(31, 42, 51, 0.18);
}

@keyframes rs-in {
  from { opacity: 0; transform: scale(0.7); }
  to { opacity: 1; transform: scale(1); }
}

/* Richtig: der Gegenstand hüpft und ist weg – sein Bild fliegt derweil in den
   Rucksack. Falsch: ein kurzes Kopfschütteln, dann ist die Runde vorbei.
   Ausgesucht: dasselbe Hüpfen, das Bild kommt gleich allein gross wieder. */
.rs-item.is-right,
.rs-item.is-gewaehlt { animation: rs-pack 0.32s ease both; }
@keyframes rs-pack {
  0% { transform: scale(1); opacity: 1; }
  40% { transform: scale(1.2); opacity: 1; }
  100% { transform: scale(0.6); opacity: 0; }
}

.rs-item.is-weg { visibility: hidden; }

.rs-item.is-wrong { animation: rs-nope 0.4s ease both; }
@keyframes rs-nope {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-10px) rotate(-7deg); }
  60% { transform: translateX(10px) rotate(7deg); }
}

/* --- Der Flug in den Rucksack --- */

/* Eine Kopie des Bildes, die von ihrem Platz aus in einem Bogen zum Rucksack
   fliegt und dort kleiner wird. --dx und --dy setzt rucksack.js aus den
   gemessenen Stellen. */
.rs-flug {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 9;
  line-height: 1;
  pointer-events: none;
  filter: drop-shadow(0 4px 8px rgba(31, 42, 51, 0.24));
  animation: rs-flug var(--flug, 520ms) cubic-bezier(0.35, 0, 0.25, 1) both;
}

@keyframes rs-flug {
  0% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
  30% { transform: translate(calc(-50% + var(--dx) * 0.25), calc(-50% + var(--dy) * 0.15 - 40px)) scale(1.08); opacity: 1; }
  100% { transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) scale(0.22); opacity: 0.1; }
}

/* --- Der Rucksack --- */

.rs-rucksack {
  position: absolute;
  right: calc(var(--safe-right) + 18px);
  bottom: 6px;
  z-index: 4;
  width: var(--rs-rucksack);
  filter: drop-shadow(0 8px 14px rgba(31, 42, 51, 0.3));
  transform-origin: 50% 90%;
}

.rs-rucksack-art { display: block; width: 100%; height: auto; }
.rs-rucksack-bild { display: block; font-size: clamp(3rem, 12vh, 5rem); line-height: 1; text-align: center; }

/* Die Zahl auf dem Rucksack: wie viele Sachen gerade drin sind. */
.rs-rucksack-zahl {
  position: absolute;
  top: -6px;
  right: -10px;
  min-width: 36px;
  height: 36px;
  padding: 0 8px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  border: 3px solid var(--cm-accent);
  background: #fff;
  color: var(--cm-accent-dark);
  font-size: 1.1rem;
  font-weight: 950;
  line-height: 1;
  box-shadow: 0 4px 10px rgba(31, 42, 51, 0.2);
}

/* Etwas fällt hinein: der Rucksack hüpft. Ausgeleert: er kippt einmal. */
.rs-rucksack.is-bump { animation: rs-bump 0.42s ease both; }
@keyframes rs-bump {
  0% { transform: scale(1); }
  35% { transform: scale(1.14) translateY(-8px); }
  70% { transform: scale(0.96); }
  100% { transform: scale(1); }
}

.rs-rucksack.is-leer { animation: rs-leer 0.55s ease both; }
@keyframes rs-leer {
  0% { transform: rotate(0); }
  30% { transform: rotate(-16deg) translateY(-8px); }
  60% { transform: rotate(10deg); }
  100% { transform: rotate(0); }
}

@media (max-height: 540px) {
  .rs-stage { --rs-rucksack: 72px; }
  .rs-chip { min-width: 34px; height: 34px; font-size: 0.95rem; }
  .rs-rucksack-zahl { min-width: 30px; height: 30px; font-size: 0.95rem; }
}

@media (prefers-reduced-motion: reduce) {
  .rs-item { transition: none; }
  .rs-item, .rs-item.is-right, .rs-item.is-gewaehlt, .rs-item.is-wrong, .rs-flug,
  .rs-rucksack.is-bump, .rs-rucksack.is-leer, .rs-platz.is-dran { animation: none; }
}

/* ===========================================================================
   Weichen-Wirrwarr
   ---------------------------------------------------------------------------
   Anders als die drei Spiele daneben steht das Streckennetz auf einer weissen
   Tafel: Gleise, Weichen und Häuser sind feine Linien, und über einer Wiese mit
   Bäumen wäre nicht mehr zu erkennen, was Gleis ist und was Landschaft.

   Die Farbe ist die des Bereichs Konzentration.
   =========================================================================== */

.tr-stage { --cm-accent: #00A5B5; --cm-accent-dark: #00707c; }

.tr-board {
  flex: 1 1 auto;
  width: min(96vw, 1100px);
  min-height: 0;
  border-radius: 26px;
  background: #fdfdfd;
  box-shadow: 0 14px 34px rgba(31, 42, 51, 0.24);
  overflow: hidden;
  overflow: clip;
}

.tr-canvas { display: block; width: 100%; height: 100%; touch-action: manipulation; }

/* --- Die Levelwahl --- */

.tr-levels {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: clamp(8px, 1.6vw, 16px);
  width: min(94vw, 900px);
}

.tr-level {
  display: grid;
  justify-items: center;
  gap: 4px;
  padding: clamp(8px, 1.6vh, 14px) 6px;
  border: 3px solid #ffffff;
  border-radius: 20px;
  background: rgba(255, 255, 255, 0.9);
  cursor: pointer;
  box-shadow: 0 8px 18px rgba(31, 42, 51, 0.18);
  transition: transform 0.12s ease;
  -webkit-tap-highlight-color: transparent;
}

.tr-level:hover { background: #fff; }
.tr-level:active { transform: scale(0.95); }
.tr-level:focus-visible { outline: 3px solid #243047; outline-offset: 3px; }

.tr-level-nr {
  font-size: clamp(1.2rem, 3vw, 1.7rem);
  font-weight: 950;
  color: var(--cm-accent-dark);
  line-height: 1;
}

/* Die Farben des Levels als Punktreihe: wie voll es wird, ohne eine Zahl. */
.tr-level-colors { display: flex; flex-wrap: wrap; justify-content: center; gap: 3px; max-width: 68px; }
.tr-level-dot { width: 9px; height: 9px; border-radius: 50%; }

.tr-level-stars { display: flex; gap: 1px; font-size: 0.85rem; line-height: 1; color: #d5dae2; }
.tr-level-star.is-on { color: #f6b93b; }

@media (max-width: 620px) {
  .tr-levels { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 6px; }
  .tr-level { padding: 6px 2px; border-width: 2px; border-radius: 14px; }
  .tr-level-colors { max-width: 44px; gap: 2px; }
  .tr-level-dot { width: 7px; height: 7px; }
}

/* --- Sterne im Schlussbild --- */

.cm-result-stars { display: flex; justify-content: center; gap: 8px; margin: 2px 0 4px; }
.cm-result-star { display: grid; place-items: center; color: #d5dae2; }
.cm-result-star svg { width: clamp(38px, 9vw, 54px); height: auto; }
.cm-result-star.is-on { color: #f6b93b; animation: cm-star-pop 0.4s ease both; }
.cm-result-star.is-on:nth-child(2) { animation-delay: 0.12s; }
.cm-result-star.is-on:nth-child(3) { animation-delay: 0.24s; }
@keyframes cm-star-pop {
  0% { transform: scale(0.2) rotate(-30deg); opacity: 0; }
  70% { transform: scale(1.18) rotate(6deg); opacity: 1; }
  100% { transform: scale(1) rotate(0); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .tr-level { transition: none; }
  .cm-result-star.is-on { animation: none; }
}

/* ===========================================================================
   Schwarm-Fokus
   ---------------------------------------------------------------------------
   Dieselbe Bühne wie der Karten-Merker: die Landschaft der Startseite, oben
   die Leiste, unten die beiden Knöpfe. Die Farbe ist die des Bereichs
   Konzentration.

   Der Schwarm steht frei auf der Landschaft, ohne Kasten darum. Ein Rahmen
   wäre eine zweite Form neben den fünf Fischen, auf die es ankommt.
   =========================================================================== */

.sf-stage { --cm-accent: #00A5B5; --cm-accent-dark: #00707c; }

.sf-swarm {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(4px, 1.4vw, 14px);
  width: min(94vw, 900px);
}

.sf-slot { flex: 1 1 0; max-width: 150px; }

/* Der mittlere Fisch ist grösser: nur dann trägt die Aufgabe: "schau in die
   Mitte" auch für ein Kind, das die Reihe nur überfliegt. */
.sf-slot.is-target { transform: scale(1.28); z-index: 1; }

.sf-fish { width: 100%; height: auto; display: block; }
.sf-fish.is-target { filter: drop-shadow(0 4px 10px rgba(120, 70, 0, 0.3)); }

/* Kurze Rückmeldung am ganzen Schwarm statt an einem Knopf: der Blick liegt
   auf den Fischen, dort muss die Antwort ankommen. */
.sf-swarm.is-right { animation: sf-yes 0.3s ease both; }
.sf-swarm.is-wrong { animation: sf-no 0.4s ease both; }
@keyframes sf-yes {
  0% { transform: scale(1); }
  45% { transform: scale(1.06); }
  100% { transform: scale(1); }
}
@keyframes sf-no {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-12px); }
  60% { transform: translateX(12px); }
}

.sf-keys {
  display: flex;
  gap: clamp(12px, 3vw, 26px);
  width: min(420px, 90vw);
}

.sf-key {
  flex: 1 1 0;
  height: clamp(64px, 12vh, 92px);
  display: grid;
  place-items: center;
  border: none;
  border-radius: 24px;
  background: var(--cm-accent);
  color: #fff;
  cursor: pointer;
  box-shadow: 0 8px 18px rgba(31, 42, 51, 0.24);
  transition: transform 0.12s ease;
  -webkit-tap-highlight-color: transparent;
}

.sf-key svg { width: 40%; max-width: 44px; height: auto; }
.sf-key:active { transform: scale(0.95); }
.sf-key:focus-visible { outline: 3px solid #243047; outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .sf-swarm.is-right, .sf-swarm.is-wrong { animation: none; }
  .sf-key { transition: none; }
}

/* ===========================================================================
   Strand-Schätze
   ---------------------------------------------------------------------------
   Dieselbe Bühne wie der Karten-Merker, nur ohne Uhr: hier zählt nicht die
   Zeit, sondern wie viele Schätze man auseinanderhalten kann. Die Farbe ist
   die des Bereichs Gedächtnis.

   Der Strand nimmt allen Platz, den die Fläche hergibt – bei zwanzig Schätzen
   zählt jeder Pixel. Die Plätze rechnet strandschatz.js aus, hier steht nur,
   wie ein Schatz aussieht und was beim Antippen passiert.
   =========================================================================== */

.st-stage { --cm-accent: #7C5CE6; --cm-accent-dark: #5a41b8; }

.st-prompt { flex: 0 0 auto; }

.st-beach {
  position: relative;
  flex: 1 1 auto;
  width: min(96vw, 1100px);
  min-height: 0;
  border-radius: 30px;
  overflow: hidden;
  overflow: clip;
  box-shadow: 0 12px 30px rgba(31, 42, 51, 0.22), inset 0 0 0 3px rgba(255, 255, 255, 0.5);
}

.st-beach-art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* Ein Schatz liegt im Sand: kein Kasten, kein Rand, nur die Figur. Die Grösse
   und der Platz kommen aus dem Stil-Attribut, das strandschatz.js setzt. */
.st-item {
  position: absolute;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  border-radius: 50%;
  transition: transform 0.12s ease;
  animation: st-drop 0.22s ease both;
  -webkit-tap-highlight-color: transparent;
}

.st-item svg { width: 100%; height: 100%; display: block; }
.st-item:active { transform: scale(0.92); }
.st-item:focus-visible { outline: 3px solid #243047; outline-offset: 3px; }
.st-item:disabled { cursor: default; }

@keyframes st-drop {
  from { opacity: 0; transform: scale(0.6); }
  to { opacity: 1; transform: scale(1); }
}

/* Richtig: der Schatz hüpft einmal und wandert in die Kiste. */
.st-item.is-right { animation: st-take 0.44s ease both; }
@keyframes st-take {
  0% { transform: scale(1); }
  35% { transform: scale(1.25); }
  100% { transform: scale(0.2); opacity: 0; }
}

/* Schon gehabt: ein kurzes Kopfschütteln, dazu ein roter Ring – so ist zu
   sehen, welcher Schatz die Runde beendet hat. */
.st-item.is-wrong {
  animation: st-shake 0.42s ease both;
  box-shadow: 0 0 0 4px #e2694f, 0 0 0 9px rgba(226, 105, 79, 0.3);
}
@keyframes st-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-7px) rotate(-6deg); }
  50% { transform: translateX(7px) rotate(6deg); }
  80% { transform: translateX(-4px) rotate(-3deg); }
}

@media (prefers-reduced-motion: reduce) {
  .st-item, .st-item.is-right, .st-item.is-wrong { animation: none; }
  .st-item.is-right { opacity: 0; }
  .st-item { transition: none; }
}

/* ===========================================================================
   Kacheln-Knobeln
   ---------------------------------------------------------------------------
   Dieselbe Bühne wie die Strand-Schätze, nur mit einem Raster darauf. Das
   Raster nimmt den Platz, den es kriegen kann, bleibt aber quadratisch in den
   Kacheln: verzerrte Kacheln wären schwerer wiederzufinden, weil sich die Lage
   dann nicht mehr am Bild, sondern nur noch am Zählen merken liesse.
   =========================================================================== */

.kk-stage { --cm-accent: #7C5CE6; --cm-accent-dark: #5a41b8; }

.kk-prompt { flex: 0 0 auto; }

.kk-raster {
  display: grid;
  grid-template-columns: repeat(var(--kk-spalten, 4), 1fr);
  grid-template-rows: repeat(var(--kk-reihen, 3), 1fr);
  gap: clamp(5px, 1.1vh, 12px);
  /* Erst die Höhe füllen, dann die Breite darauf abstimmen: sonst rutscht das
     Raster auf einem flachen Bildschirm unten heraus. */
  height: min(100%, 74vh);
  aspect-ratio: var(--kk-spalten, 4) / var(--kk-reihen, 3);
  max-width: min(94vw, 900px);
  padding: clamp(8px, 1.6vh, 16px);
  border-radius: 26px;
  background: rgba(253, 251, 246, 0.9);
  box-shadow: 0 12px 30px rgba(31, 42, 51, 0.2), inset 0 0 0 3px rgba(255, 255, 255, 0.6);
}

.kk-kachel {
  min-width: 0;
  min-height: 0;
  padding: 0;
  border: none;
  border-radius: clamp(8px, 1.6vh, 16px);
  background: #d3dded;
  box-shadow: inset 0 -3px 0 rgba(31, 42, 51, 0.12);
  cursor: pointer;
  transition: transform 0.1s ease, background 0.16s ease;
  -webkit-tap-highlight-color: transparent;
}

.kk-kachel:active:not(:disabled) { transform: scale(0.94); }
.kk-kachel:focus-visible { outline: 3px solid #243047; outline-offset: 3px; }
/* Gesperrt heisst hier nicht "ausgegraut": während man sich das Muster merkt,
   sind alle Kacheln gesperrt – blass werden dürfen sie dabei nicht. */
.kk-kachel:disabled { cursor: default; opacity: 1; }

/* Leuchtend: so sieht die Kachel aus, während man sie sich merkt – und noch
   einmal am Schluss, wenn die Runde zeigt, welche es gewesen wären. */
.kk-kachel.is-muster {
  background: var(--cm-accent);
  box-shadow: inset 0 -3px 0 var(--cm-accent-dark), 0 0 0 3px rgba(255, 255, 255, 0.7);
}

/* Richtig nachgetippt: dieselbe Farbe, dazu ein kurzer Hüpfer. */
.kk-kachel.is-treffer {
  background: var(--cm-accent);
  box-shadow: inset 0 -3px 0 var(--cm-accent-dark);
  animation: kk-treffer 0.3s ease both;
}

@keyframes kk-treffer {
  0% { transform: scale(1); }
  45% { transform: scale(1.14); }
  100% { transform: scale(1); }
}

/* Daneben: die Kachel wackelt einmal und bleibt rot – so ist zu sehen, welche
   die Runde beendet hat. */
.kk-kachel.is-daneben {
  background: #e2694f;
  box-shadow: inset 0 -3px 0 #b34a33;
  animation: kk-daneben 0.4s ease both;
}

@keyframes kk-daneben {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-6px); }
  60% { transform: translateX(6px); }
}

@media (prefers-reduced-motion: reduce) {
  .kk-kachel, .kk-kachel.is-treffer, .kk-kachel.is-daneben { animation: none; transition: none; }
}

/* ===========================================================================
   Fischteich
   ---------------------------------------------------------------------------
   Dieselbe Bühne, nur mit einem Teich darauf. Die Fische stehen in Pixeln auf
   dem Teich – wo genau, rechnet fischteich.js Bild für Bild aus; hier steht
   nur, wie ein Fisch aussieht und was beim Antippen passiert.
   =========================================================================== */

.ft-stage { --cm-accent: #00A5B5; --cm-accent-dark: #00707c; }

.ft-prompt { flex: 0 0 auto; }

.ft-pond {
  position: relative;
  flex: 1 1 auto;
  width: min(96vw, 1100px);
  min-height: 0;
  border-radius: 30px;
  overflow: hidden;
  overflow: clip;
  box-shadow: 0 12px 30px rgba(31, 42, 51, 0.22), inset 0 0 0 3px rgba(255, 255, 255, 0.5);
}

.ft-pond-art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* Ein Fisch: kein Kasten, kein Rand, nur die Figur. Platz und Blickrichtung
   kommen aus dem Stil-Attribut, das fischteich.js jedes Bild neu setzt –
   deshalb hier auch keine Übergänge, die würden gegen die Bewegung arbeiten. */
.ft-fisch {
  position: absolute;
  top: 0;
  left: 0;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  will-change: transform;
  -webkit-tap-highlight-color: transparent;
}

/* Gespiegelt wird nur das Bild, nicht der Knopf: sonst stünde das Häkchen bei
   nach links schwimmenden Fischen seitenverkehrt da. */
.ft-fisch-art {
  width: 100%;
  height: 100%;
  display: block;
  transform: scaleX(var(--ft-blick, 1));
}
.ft-fisch:focus-visible { outline: 3px solid #243047; outline-offset: 2px; }
/* Nach dem letzten Tipp sind alle Fische gesperrt. Blass werden dürfen sie
   dabei nicht – man soll ja noch sehen, welcher die Runde beendet hat. */
.ft-fisch:disabled { cursor: default; opacity: 1; }

/* Nach dem Tippen füllt sich erst ein kleines Rad über dem Fisch – anderthalb
   Sekunden lang, in denen kein anderer Fisch zählt –, dann wird daraus das
   Häkchen, das kurz stehen bleibt und wieder weggeht. Beides sitzt im Fisch
   und schwimmt mit ihm mit. */
.ft-ring,
.ft-haken {
  position: absolute;
  top: -6%;
  right: 4%;
  width: 34%;
  height: auto;
  opacity: 0;
  pointer-events: none;
}

.ft-fisch.is-laden .ft-ring { opacity: 1; }
.ft-fisch.is-laden .ft-ring-fill { animation: ft-ring var(--ft-sperre, 1500ms) linear forwards; }

@keyframes ft-ring {
  from { stroke-dashoffset: 50.27; }
  to { stroke-dashoffset: 0; }
}

.ft-fisch.is-gefangen .ft-haken { animation: ft-haken 0.7s ease both; }

@keyframes ft-haken {
  0% { opacity: 0; transform: scale(0.4); }
  25% { opacity: 1; transform: scale(1.1); }
  70% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1); }
}

/* Schon gefangen: derselbe rote Ring wie bei einem Schatz, den man zweimal
   nimmt – damit zu sehen ist, welcher Fisch die Runde beendet hat. Wackeln darf
   er nicht: seine Lage steht im selben transform, das hier überschrieben würde. */
.ft-fisch.is-doppelt {
  border-radius: 50%;
  box-shadow: 0 0 0 4px #e2694f, 0 0 0 9px rgba(226, 105, 79, 0.3);
}

/* Ein Tipp ins Wasser: ein Ring, der aufgeht und verschwindet. Punkte gibt es
   dafür keine, aber stumm bleiben darf die App auch nicht. */
.ft-platsch {
  position: absolute;
  width: 12px;
  height: 12px;
  margin: -6px 0 0 -6px;
  border-radius: 50%;
  border: 3px solid rgba(255, 255, 255, 0.85);
  pointer-events: none;
  animation: ft-platsch 0.45s ease-out both;
}

@keyframes ft-platsch {
  from { transform: scale(0.4); opacity: 0.9; }
  to { transform: scale(4.5); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .ft-platsch { animation-duration: 0.2s; }
  .ft-fisch.is-gefangen .ft-haken { animation: none; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .cm-card, .cm-card.is-right, .cm-card.is-wrong, .cm-start::after, .cm-overlay { animation: none; }
  .cm-choice, .cm-icon { transition: none; }
}

/* ===========================================================================
   Blätter im Strom
   ---------------------------------------------------------------------------
   Dieselbe Bühne wie der Karten-Merker – die Farbe ist die des Bereichs
   Geschwindigkeit. Darauf eine Wasserfläche, über die Welle um Welle treibt.
   Gespielt wird nur mit dem Finger: ein Wisch auf dem Wasser ist die Antwort.

   Wo ein Blatt liegt und wie es gedreht ist, rechnet blaetter.js Bild für Bild
   aus und schreibt es ins Stil-Attribut. Deshalb steht hier kein Übergang auf
   transform: er würde gegen die Bewegung arbeiten.
   =========================================================================== */

.bs-stage { --cm-accent: #F5A623; --cm-accent-dark: #b9741a; }

/* Die Frage wechselt mit der Farbe der Welle und trägt deren Farbe mit: so ist
   die Regel auch dann noch abzulesen, wenn die Blätter gerade am Rand sind. */
.bs-frage { flex: 0 0 auto; min-height: 1.4em; transition: color 0.15s ease; }
.bs-frage.is-treibt { color: #b9741a; }
.bs-frage.is-zeigt { color: #2f7a43; }

.bs-wasser {
  position: relative;
  flex: 1 1 auto;
  width: min(96vw, 1100px);
  min-height: 0;
  border-radius: 30px;
  overflow: hidden;
  overflow: clip;
  box-shadow: 0 12px 30px rgba(31, 42, 51, 0.22), inset 0 0 0 3px rgba(255, 255, 255, 0.5);
}

.bs-wasser-art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* Ein Blatt: kein Kasten, kein Rand, nur die Figur. Es treibt auch über den
   Rand des Wassers hinaus – dort schneidet die Fläche es ab, und genau das
   sieht aus wie ein Blatt, das ins Bild und wieder hinaus schwimmt. */
.bs-blatt {
  position: absolute;
  top: 0;
  left: 0;
  will-change: transform;
  pointer-events: none;
}

.bs-blatt-art { width: 100%; height: 100%; display: block; }

/* Richtig: die Welle hüpft einmal kurz. Falsch oder verpasst: kein rotes X,
   kein Abzug – die Blätter werden nur blass und treiben weiter. */
.bs-welle.is-richtig .bs-blatt { animation: bs-ja 0.32s ease both; }
.bs-welle.is-vorbei { opacity: 0.45; transition: opacity 0.3s ease; }

@keyframes bs-ja {
  0% { filter: none; }
  45% { filter: drop-shadow(0 0 12px rgba(255, 255, 255, 0.95)); }
  100% { filter: none; }
}

/* --- Wischen --- */

/* Während der Runde gehört jede Berührung dem Spiel: ohne diese Regel nähme
   der Browser einen Wisch als Rollen oder Zoomen an sich, brächte das
   Zeigerereignis ab, und der Wisch käme nie an. */
.bs-stage[data-phase="play"],
.bs-stage[data-phase="play"] .cm-play,
.bs-stage[data-phase="play"] .bs-wasser {
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

/* Die Antwort auf einen Wisch: ein grüner Haken oder ein graues Kreuz, das
   kurz in der Mitte des Wassers aufblitzt, während schon die nächste Welle
   kommt. Es hält nichts auf – der Ton sagt dasselbe. */
.bs-flash {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 3;
  width: clamp(54px, 12vh, 84px);
  height: clamp(54px, 12vh, 84px);
  margin: calc(clamp(54px, 12vh, 84px) / -2) 0 0 calc(clamp(54px, 12vh, 84px) / -2);
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: #fff;
  font-size: clamp(1.8rem, 7vh, 2.8rem);
  font-weight: 950;
  line-height: 1;
  pointer-events: none;
  animation: bs-flash 0.42s ease-out both;
}

.bs-flash.is-richtig { background: rgba(63, 191, 116, 0.92); }
.bs-flash.is-falsch { background: rgba(120, 132, 148, 0.85); }

@keyframes bs-flash {
  0% { transform: scale(0.4); opacity: 0; }
  30% { transform: scale(1.1); opacity: 1; }
  100% { transform: scale(1.3); opacity: 0; }
}

/* --- Das Startbild --- */

/* Zwei Wasserstücke nebeneinander, ohne ein Wort dazu: links treibt ein oranges
   Blatt quer durch, obwohl seine Spitze nach oben zeigt, rechts liegt ein
   grünes still. Damit steht die ganze Regel als Bild da. */
.bs-demo { display: flex; gap: clamp(10px, 3vw, 20px); }

.bs-demo-chip {
  position: relative;
  width: clamp(130px, 20vw, 190px);
  height: clamp(92px, 13vw, 130px);
  border-radius: 20px;
  overflow: hidden;
  overflow: clip;
  background: linear-gradient(180deg, #7cd0ea, #41a6d0);
  box-shadow: 0 8px 18px rgba(31, 42, 51, 0.2), inset 0 0 0 3px rgba(255, 255, 255, 0.5);
}

.bs-demo-blatt {
  position: absolute;
  top: 19%;
  left: 30%;
  width: 40%;
  height: 62%;
  transform: rotate(var(--bs-dreh, 0deg));
}

.bs-demo-blatt svg { width: 100%; height: 100%; display: block; }

/* Das orange Blatt treibt, das grüne wippt nur: dieselbe Unterscheidung wie im
   Spiel, damit das Startbild nicht etwas anderes zeigt als die Runde danach. */
.bs-demo-chip.is-treibt .bs-demo-blatt { animation: bs-demo-treiben 2.6s linear infinite; }
.bs-demo-chip.is-zeigt .bs-demo-blatt { animation: bs-demo-wippen 2.6s ease-in-out infinite; }

@keyframes bs-demo-treiben {
  from { transform: translateX(-170%) rotate(var(--bs-dreh, 0deg)); }
  to { transform: translateX(170%) rotate(var(--bs-dreh, 0deg)); }
}

@keyframes bs-demo-wippen {
  0%, 100% { transform: translateY(-6%) rotate(var(--bs-dreh, 0deg)); }
  50% { transform: translateY(6%) rotate(var(--bs-dreh, 0deg)); }
}

@media (prefers-reduced-motion: reduce) {
  .bs-welle.is-richtig .bs-blatt { animation: none; }
  .bs-flash { animation: none; opacity: 0; }
  /* Das Treiben bleibt: es ist die Regel selbst, nicht Zierde. Nur langsamer,
     und das Wippen fällt ganz weg. */
  .bs-demo-chip.is-treibt .bs-demo-blatt { animation-duration: 5s; }
  .bs-demo-chip.is-zeigt .bs-demo-blatt { animation: none; }
}

/* ===========================================================================
   Freie Fahrt
   ---------------------------------------------------------------------------
   Dieselbe Bühne wie die anderen Spiele, in der Farbe des Bereichs
   Konzentration. Darauf ein Abstellbahnhof: ein Quadrat aus sechs mal sechs
   Feldern, rechts ein Tor.

   Alles auf dem Feld hängt an einer einzigen Zahl: --ff-zelle, der Kantenlänge
   eines Feldes in Pixeln. freiefahrt.js misst sie einmal und beim Drehen des
   Geräts neu; Breite, Höhe und Platz jedes Wagens rechnet der Browser daraus.
   Ein Wagen bewegt sich deshalb nur über transform – keine Positionsangabe
   ändert sich, und das Schieben läuft auf der Grafikkarte statt im Umbruch.
   =========================================================================== */

.ff-stage { --cm-accent: #00A5B5; --cm-accent-dark: #00707c; }

/* --- Die Levelwahl ------------------------------------------------------- */

/* Vier Spalten, eine je Stufe: so steht neben jedem Level, wie schwer es ist,
   ohne dass ein Kind eine Liste durchlesen müsste. */
.ff-stufen {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(6px, 1.4vw, 16px);
  width: min(94vw, 860px);
}

/* Fünf Spalten, wenn "Sehr leicht" dabei ist (Stufe "leicht"). */
.ff-stufen.has-fuenf {
  grid-template-columns: repeat(5, minmax(0, 1fr));
  width: min(96vw, 1000px);
}

.ff-stufe { display: grid; gap: clamp(4px, 1vh, 8px); align-content: start; }

.ff-stufe-name {
  margin: 0;
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--ff-stufe-farbe, #00A5B5);
  color: #fff;
  font-size: clamp(0.72rem, 1.7vw, 0.95rem);
  font-weight: 900;
  text-align: center;
  letter-spacing: 0.02em;
}

.ff-stufe-level { display: grid; gap: clamp(4px, 1vh, 8px); }

.ff-level {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 2px 6px;
  padding: clamp(4px, 1vh, 9px) clamp(5px, 1vw, 10px);
  border: 3px solid #ffffff;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.9);
  cursor: pointer;
  box-shadow: 0 8px 18px rgba(31, 42, 51, 0.18);
  transition: transform 0.12s ease;
  -webkit-tap-highlight-color: transparent;
}

.ff-level:hover { background: #fff; }
.ff-level:active { transform: scale(0.95); }
.ff-level:focus-visible { outline: 3px solid #243047; outline-offset: 3px; }

.ff-level-nr {
  grid-row: span 2;
  font-size: clamp(1.1rem, 2.8vw, 1.6rem);
  font-weight: 950;
  line-height: 1;
  color: var(--ff-stufe-farbe, #00707c);
}

/* Die Mindestzugzahl steht auf dem Knopf: sie ist das Mass des Levels, und die
   Ziffer allein sagt einem Kind, das noch nicht liest, schon "wenig" oder
   "viel". */
.ff-level-ziel { display: flex; align-items: baseline; gap: 3px; line-height: 1; }
.ff-level-ziel-zahl { font-size: clamp(0.85rem, 2vw, 1.05rem); font-weight: 900; color: #243047; font-variant-numeric: tabular-nums; }
.ff-level-ziel-wort { font-size: clamp(0.55rem, 1.3vw, 0.72rem); font-weight: 800; color: #6b7684; }

.ff-level-sterne { display: flex; gap: 1px; font-size: clamp(0.62rem, 1.5vw, 0.85rem); line-height: 1; color: #d5dae2; }
.ff-level-stern.is-on { color: #f6b93b; }

/* --- Der Bahnhof --------------------------------------------------------- */

/* Die Anzeige steht links neben dem Feld, nicht darüber: quer auf dem Handy ist
   die Höhe das knappe Mass, die Breite ist reichlich da. Und rechts bleibt frei
   für das Tor und für die Lok, die am Schluss hinausfährt. */
.ff-tisch {
  flex: 1 1 auto;
  min-height: 0;
  width: min(96vw, 1100px);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(8px, 2vw, 22px);
}

.ff-hud {
  flex: 0 0 auto;
  width: clamp(74px, 14vw, 128px);
  display: grid;
  gap: clamp(4px, 1.2vh, 10px);
}

.ff-hud-level {
  margin: 0;
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--ff-stufe-farbe, #00A5B5);
  color: #fff;
  font-size: clamp(0.7rem, 1.7vw, 0.92rem);
  font-weight: 900;
  text-align: center;
}

.ff-hud-wert {
  display: grid;
  justify-items: center;
  gap: 1px;
  padding: clamp(3px, 0.8vh, 7px) 4px;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.88);
  box-shadow: 0 6px 14px rgba(31, 42, 51, 0.14);
}

.ff-hud-name {
  font-size: clamp(0.55rem, 1.3vw, 0.7rem);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #6b7684;
}

.ff-hud-zahl {
  font-size: clamp(0.95rem, 2.6vh, 1.4rem);
  font-weight: 950;
  color: #243047;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

/* Über der Bestmarke: kein Rot, kein Ausrufezeichen – nur eine andere Farbe.
   Wer mehr Züge braucht, hat nichts falsch gemacht, es reicht nur nicht mehr
   für den dritten Stern. */
.ff-hud-zahl.is-drueber { color: #c9761b; }

.ff-platz {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  height: 100%;
  display: grid;
  place-items: center;
}

.ff-brett {
  position: relative;
  width: calc(var(--ff-zelle, 40px) * 6);
  height: calc(var(--ff-zelle, 40px) * 6);
  border-radius: 12px;
  background-color: #f1eee6;
  /* Das Raster als Hintergrund statt als sechsunddreissig Kästchen: ein Bild
     weniger im Umbruch, und beim Drehen des Geräts zeichnet es sich von selbst
     neu, weil es an derselben Variablen hängt wie die Wagen. */
  background-image:
    repeating-linear-gradient(90deg, rgba(60, 74, 92, 0.14) 0px, rgba(60, 74, 92, 0.14) 1px, transparent 1px, transparent var(--ff-zelle, 40px)),
    repeating-linear-gradient(180deg, rgba(60, 74, 92, 0.14) 0px, rgba(60, 74, 92, 0.14) 1px, transparent 1px, transparent var(--ff-zelle, 40px));
  /* Der Ring liegt aussen und nimmt deshalb keinen Platz vom Feld weg: die
     Wagen stehen exakt auf den Zellen, die die Variable vorgibt. */
  box-shadow: 0 0 0 3px #b7c1cf, 0 14px 32px rgba(31, 42, 51, 0.24);
  touch-action: none;
}

/* Das Tor: ein karierter Durchlass in der Mauer, rechts in der dritten Reihe,
   mit einem Pfeil nach draussen. Es steht immer da – es ist das Ziel und nicht
   ein Hinweis, der wieder verschwindet. */
.ff-tor {
  position: absolute;
  left: calc(var(--ff-zelle, 40px) * 6 - 3px);
  top: calc(var(--ff-zelle, 40px) * 2);
  width: calc(var(--ff-zelle, 40px) * 0.52);
  height: var(--ff-zelle, 40px);
  display: grid;
  place-items: center;
  border-radius: 0 8px 8px 0;
  color: #d9534f;
  background-color: #fdfdfd;
  background-image:
    linear-gradient(45deg, #3a4657 25%, transparent 25%, transparent 75%, #3a4657 75%),
    linear-gradient(45deg, #3a4657 25%, transparent 25%, transparent 75%, #3a4657 75%);
  background-size: calc(var(--ff-zelle, 40px) / 3.5) calc(var(--ff-zelle, 40px) / 3.5);
  background-position: 0 0, calc(var(--ff-zelle, 40px) / 7) calc(var(--ff-zelle, 40px) / 7);
  box-shadow: 0 0 0 3px #b7c1cf;
}

.ff-tor-pfeil {
  width: 74%;
  height: 74%;
  filter: drop-shadow(0 0 3px rgba(255, 255, 255, 0.95)) drop-shadow(0 0 2px rgba(255, 255, 255, 0.95));
  animation: ff-winken 1.9s ease-in-out infinite;
}

@keyframes ff-winken {
  0%, 100% { transform: translateX(-8%); opacity: 0.8; }
  50% { transform: translateX(14%); opacity: 1; }
}

/* Das aufleuchtende Gleis: so weit kann der angefasste Wagen. Es liegt unter
   den Wagen, damit ein Tipp auf einen Wagen ihn trifft und nicht das Gleis –
   und ein Tipp auf die freie Strecke fährt ihn dorthin. */
.ff-spur {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 1;
  width: calc(var(--ff-zelle, 40px) * var(--ff-spur-breite, 1));
  height: calc(var(--ff-zelle, 40px) * var(--ff-spur-hoehe, 1));
  transform: translate3d(
    calc(var(--ff-spur-spalte, 0) * var(--ff-zelle, 40px)),
    calc(var(--ff-spur-reihe, 0) * var(--ff-zelle, 40px)), 0);
  border-radius: calc(var(--ff-zelle, 40px) * 0.28);
  background: rgba(0, 165, 181, 0.2);
  box-shadow: inset 0 0 0 2px rgba(0, 165, 181, 0.55);
  opacity: 0;
  /* Nie anzutippen: die Tipps nimmt das Feld darunter entgegen. Sonst müsste
     jeder Tipp erst durch dieses Rechteck hindurch, und ein Tipp knapp neben
     dem Gleis käme anders an als einer knapp darauf. */
  pointer-events: none;
  transition: opacity 0.14s ease;
}

.ff-spur.is-da { opacity: 1; }

/* --- Die Wagen ----------------------------------------------------------- */

.ff-wagen {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 2;
  width: calc(var(--ff-zelle, 40px) * var(--ff-breite, 1));
  height: calc(var(--ff-zelle, 40px) * var(--ff-hoehe, 1));
  /* Zelle mal Spalte ist der Platz, --ff-schub der Finger. Beides in einem
     transform: während des Ziehens wird nur diese eine Zahl geschrieben. */
  transform: translate3d(
    calc(var(--ff-spalte, 0) * var(--ff-zelle, 40px) + var(--ff-schub, 0px)),
    calc(var(--ff-reihe, 0) * var(--ff-zelle, 40px) + var(--ff-hub, 0px)), 0);
  transition: transform 0.19s cubic-bezier(0.22, 0.7, 0.3, 1);
  will-change: transform;
  touch-action: none;
  cursor: grab;
  -webkit-tap-highlight-color: transparent;
}

.ff-wagen svg { display: block; width: 100%; height: 100%; }

/* Am Finger gibt es keinen Übergang: der Wagen muss dem Zeigen folgen, nicht
   ihm nachlaufen. Der Übergang kommt erst beim Loslassen zurück und lässt den
   Wagen ins Feld einrasten. */
.ff-wagen.is-zieht { transition: none; cursor: grabbing; z-index: 4; }

.ff-wagen.is-lok { z-index: 3; }

.ff-wagen.is-gewaehlt svg {
  filter: drop-shadow(0 0 4px rgba(0, 165, 181, 0.95)) drop-shadow(0 0 8px rgba(0, 165, 181, 0.6));
}

.ff-wagen:focus-visible { outline: 3px solid #243047; outline-offset: 2px; border-radius: 10px; }

/* Der kurze Ruck, wenn es nicht weitergeht – auf dem Bild im Wagen, nicht am
   Wagen selbst: dessen transform hält gerade den Platz auf dem Feld. */
.ff-wagen.is-anschlag.is-waagerecht svg { animation: ff-anschlag-quer 0.32s ease; }
.ff-wagen.is-anschlag.is-senkrecht svg { animation: ff-anschlag-hoch 0.32s ease; }

@keyframes ff-anschlag-quer {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(5%); }
  60% { transform: translateX(-3.5%); }
  85% { transform: translateX(1.5%); }
}

@keyframes ff-anschlag-hoch {
  0%, 100% { transform: translateY(0); }
  25% { transform: translateY(5%); }
  60% { transform: translateY(-3.5%); }
  85% { transform: translateY(1.5%); }
}

/* Die Ausfahrt: die Lok fährt durch das Tor aus dem Bild. Sie verblasst erst
   unterwegs, damit der Durchfahrt-Moment noch zu sehen ist. */
.ff-wagen.is-abfahrt {
  z-index: 5;
  opacity: 0;
  transition: transform 0.8s cubic-bezier(0.45, 0, 0.7, 0.45), opacity 0.42s ease 0.42s;
}

/* --- Ohne Bewegung -------------------------------------------------------- */

/* Geschoben wird weiter, und Zug- und Zeitanzeige bleiben, wie sie sind – nur
   das Beiwerk hört auf: kein wackelnder Pfeil, kein Ruck am Anschlag, kein
   Nachfedern beim Einrasten. */
@media (prefers-reduced-motion: reduce) {
  .ff-level { transition: none; }
  .ff-tor-pfeil { animation: none; }
  .ff-wagen { transition: none; }
  .ff-wagen.is-anschlag svg { animation: none; }
  .ff-wagen.is-abfahrt { transition: opacity 0.2s linear; }
}

/* --- Kleine Schirme ------------------------------------------------------- */

/* Quer auf einem alten Handy bleiben unter der Leiste keine 260 Bildpunkte.
   Dann rückt die Anzeige enger zusammen; das Feld selbst rechnet sich seine
   Zellengrösse ohnehin aus dem übrigen Platz aus. */
@media (max-height: 400px) {
  .ff-hud { width: clamp(64px, 12vw, 96px); gap: 4px; }
  .ff-hud-wert { padding: 2px 3px; border-radius: 11px; }
  .ff-level { border-width: 2px; border-radius: 12px; padding: 3px 5px; }
  .ff-stufe-name { padding: 2px 5px; }
}

/* ===========================================================================
   Turmbau
   ---------------------------------------------------------------------------
   Dieselbe Bühne wie die anderen Tempospiele – die Farbe ist die des Bereichs
   Geschwindigkeit. Darauf steht die Baustelle hochkant in der Mitte: ein Turm
   auf einer breiten, flachen Fläche wäre nach drei Blöcken schon oben, und die
   Höhe ist hier das Mass, nicht die Breite.

   Alles Bewegte darin – Himmel, Blöcke, Bruchstücke, Funken – zeichnet
   turmbau.js auf eine Leinwand. Hier steht nur, wie gross die Fläche ist und
   was um sie herum liegt: die Marke für die Reihe genauer Treffer und der
   Hinweis für den allerersten Tipp.
   =========================================================================== */

.tb-stage {
  --cm-accent: #F5A623;
  --cm-accent-dark: #b9741a;
  /* Getippt wird auf der ganzen Bühne. Ohne diese Zeile deutet der Browser den
     Tipp als Anlauf zum Rollen und schluckt ihn. */
  touch-action: none;
}

/* Der Satz steht nur vor dem ersten Block. Seinen Platz behält er trotzdem:
   verschwände er ganz, wüchse die Baustelle im selben Moment nach oben, und
   die Leinwand müsste mitten im Spiel neu vermessen werden. */
.tb-prompt {
  flex: 0 0 auto;
  min-height: 1.4em;
  transition: opacity 0.25s ease;
}
.tb-stage:not([data-phase="intro"]) .tb-prompt { opacity: 0; }

.tb-platz {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
}

/* Oben und unten am Platz festgemacht: damit steht die Höhe fest, und das
   Seitenverhältnis macht daraus die Breite. Weil die Höhe schon feststeht,
   wirkt es hier wie eine Höchstbreite: gut doppelt so breit wie hoch, mehr
   nicht. Genau das ist auf dem Handy quer der Fall – dort ist die Höhe knapp
   und die Breite reichlich, und die Baustelle nimmt sie sich. Auf einem hohen
   Bildschirm greift stattdessen max-width, und die Baustelle bleibt eine
   stehende Fläche; über den Rand geht sie nie.

   Wie viel Welt in dieser Fläche steckt, rechnet turmbau.js aus der Form aus:
   die Höhe bleibt dieselbe, die Breite wächst mit – und mit ihr jeder Block.
   Deshalb darf hier jede Form herauskommen, ohne dass das Spiel ein anderes
   wird. */
.tb-feld {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  aspect-ratio: 21 / 10;
  max-width: min(92vw, 1100px);
  border-radius: 30px;
  overflow: hidden;
  overflow: clip;
  box-shadow: 0 12px 30px rgba(31, 42, 51, 0.22), inset 0 0 0 3px rgba(255, 255, 255, 0.5);
}

.tb-canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* Die Reihe genauer Treffer. Sie steht nur, solange die Reihe hält, und poppt
   bei jedem weiteren Treffer einmal auf – das ist das Lob, das der Zähler oben
   rechts nicht geben kann. */
.tb-combo {
  position: absolute;
  top: 10px;
  left: 50%;
  padding: 3px 12px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.92);
  color: var(--cm-accent-dark);
  font-size: clamp(0.95rem, 2.6vw, 1.25rem);
  font-weight: 950;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 4px 12px rgba(31, 42, 51, 0.25);
  opacity: 0;
  transform: translateX(-50%) scale(0.7);
  transition: opacity 0.16s ease, transform 0.16s ease;
  pointer-events: none;
}
.tb-combo.is-an { opacity: 1; transform: translateX(-50%) scale(1); }
.tb-combo.is-pop { animation: tb-pop 0.26s cubic-bezier(0.2, 1.5, 0.4, 1) both; }

@keyframes tb-pop {
  0% { transform: translateX(-50%) scale(1); }
  45% { transform: translateX(-50%) scale(1.15); }
  100% { transform: translateX(-50%) scale(1); }
}

/* Der Hinweis für den allerersten Tipp: zwei Ringe, die aufgehen, und ein
   Punkt darin. Ohne ein Wort – wer noch nicht liest, sieht trotzdem, dass hier
   getippt wird. Mit dem ersten Block ist er weg. */
.tb-tipp {
  position: absolute;
  top: 44%;
  left: 50%;
  width: clamp(44px, 15%, 70px);
  transform: translate(-50%, -50%);
  opacity: 0;
  transition: opacity 0.25s ease;
  pointer-events: none;
  filter: drop-shadow(0 2px 6px rgba(31, 42, 51, 0.45));
}
.tb-stage[data-phase="intro"] .tb-tipp { opacity: 0.95; }
.tb-tipp svg { width: 100%; height: auto; display: block; }

.tb-tipp-ring { transform-origin: 32px 32px; animation: tb-tipp 1.8s ease-out infinite; }
.tb-tipp-ring.is-spaet { animation-delay: 0.9s; }

@keyframes tb-tipp {
  0% { transform: scale(0.55); opacity: 0.9; }
  70% { transform: scale(2.1); opacity: 0; }
  100% { transform: scale(2.1); opacity: 0; }
}

/* Neuer Rekord: derselbe Satz wie sonst, nur als Abzeichen. Eine eigene Zeile
   mehr hätte auf dem flachen Handy keinen Platz mehr. */
.tb-rekord {
  padding: 4px 16px;
  border-radius: 999px;
  background: linear-gradient(135deg, #ffd166, #f0a429);
  color: #6b4407;
  font-weight: 950;
  letter-spacing: 0.02em;
  box-shadow: 0 4px 12px rgba(240, 164, 41, 0.45);
  animation: tb-rekord 0.42s cubic-bezier(0.2, 1.4, 0.4, 1) both;
}

@keyframes tb-rekord {
  0% { transform: scale(0.6) rotate(-4deg); opacity: 0; }
  60% { transform: scale(1.08) rotate(1.5deg); opacity: 1; }
  100% { transform: scale(1) rotate(0); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  /* Der Hinweis bleibt stehen, statt zu pulsen: gezeigt werden muss er
     trotzdem, sonst weiss niemand, wo es losgeht. */
  .tb-tipp-ring { animation: none; opacity: 0.35; }
  .tb-combo.is-pop { animation: none; }
  .tb-rekord { animation: none; }
}

/* ===========================================================================
   Wo hält der Zug?
   ---------------------------------------------------------------------------
   Dieselbe Bühne wie die anderen Spiele, in der Farbe des Bereichs Zahl und
   Buchstabe. Oben das Schild mit der Zahl, darunter zehn Punkte für die zehn
   Zahlen der Runde, unten das Gleis: so breit wie die Bühne, mit der Lok des
   Kindes darauf.

   Alles auf dem Gleis steht an einem Anteil --zg-p zwischen 0 und 1; die
   Strecke beginnt --zg-rand vom linken Rand und endet --zg-rand vor dem
   rechten. zahlengleis.js rechnet nur mit diesem Anteil und misst die Strecke
   an den beiden Bahnhofsmarken nach – so stimmen Finger, Lok, Marken und
   Schild an derselben Stelle überein, egal wie breit das Gleis gerade ist.
   =========================================================================== */

.zg-stage {
  --cm-accent: #E8543F;
  --cm-accent-dark: #a8321f;
  /* Geschoben wird auf dem Gleis; ohne diese Zeile hielte der Browser das
     Schieben für einen Anlauf zum Rollen. */
  touch-action: none;
}

/* Das Schild mit der gesuchten Zahl: gross, weil es das Einzige ist, was ein
   Kind hier lesen muss – und die Zahl ist die ganze Aufgabe. */
.zg-schild {
  flex: 0 0 auto;
  min-width: 2.2em;
  padding: 0.05em 0.45em;
  border-radius: 18px;
  background: #fff8ea;
  color: #243047;
  font-size: clamp(2.4rem, 11vh, 4.4rem);
  font-weight: 900;
  line-height: 1.2;
  text-align: center;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 8px 20px rgba(31, 42, 51, 0.2), inset 0 0 0 4px var(--cm-accent);
}
.zg-schild.is-neu { animation: zg-schild 0.4s cubic-bezier(0.2, 1.4, 0.4, 1) both; }
@keyframes zg-schild {
  0% { transform: scale(0.7); opacity: 0; }
  100% { transform: scale(1); opacity: 1; }
}

/* Zehn Punkte, einer je Zahl; der, der dran ist, ist gross. */
.zg-fortschritt { display: flex; gap: 7px; flex: 0 0 auto; }
.zg-punkt {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: rgba(36, 48, 71, 0.22);
  transition: transform 0.2s ease, background 0.2s ease;
}
.zg-punkt.is-dran { background: var(--cm-accent); transform: scale(1.35); }

/* Das Gleis. Die Höhe ist die der Lok, das Messband darunter und Platz für das
   Schild darüber; die Breite nimmt, was da ist. */
.zg-gleis {
  --zg-rand: clamp(44px, 7vw, 80px);
  --zg-lok: clamp(72px, 14vh, 116px);
  /* Das Messband unter der Schiene. Dort stehen die Marken – über der Schiene
     verdeckte die Lok sie genau dann, wenn es darauf ankam: beim Zielen. Unten
     bleiben sie frei, und der Strich der Lok reicht bis zu ihnen hinunter, so
     dass beides nebeneinander zu sehen ist. */
  --zg-band: clamp(26px, 5.5vh, 38px);
  --zg-strich: clamp(10px, 2.2vh, 16px);
  position: relative;
  flex: 0 0 auto;
  width: min(96vw, 1100px);
  height: calc(var(--zg-lok) + 52px + var(--zg-band));
  cursor: pointer;
  outline: none;
  -webkit-tap-highlight-color: transparent;
}
.zg-gleis:focus-visible .zg-schiene { box-shadow: 0 0 0 3px #243047; }

/* Zwei Schienen auf Schwellen, über dem Messband. */
.zg-schiene {
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--zg-band);
  height: 16px;
  border-radius: 6px;
  background:
    linear-gradient(#8a5f1c, #8a5f1c) 0 2px / 100% 3px no-repeat,
    linear-gradient(#8a5f1c, #8a5f1c) 0 11px / 100% 3px no-repeat,
    repeating-linear-gradient(90deg, transparent 0 14px, #c98a3f 14px 22px);
}

/* Eine Marke ist nur eine Stelle auf dem Gleis: der Strich hängt unter der
   Schiene, seine Zahl darunter, und an den Enden steht der Bahnhof oben auf
   der Schiene. Die Marke selbst ist ohne Breite – ihre linke Kante ist die
   Stelle, und genau daran misst zahlengleis.js die Strecke nach. */
.zg-marken { position: absolute; inset: 0; pointer-events: none; }
.zg-marke {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--zg-rand) + var(--zg-p, 0) * (100% - 2 * var(--zg-rand)));
  width: 0;
}

/* Der Strich: hängt an der Unterkante der Schiene und zeigt nach unten. Er ist
   breiter als der Strich der Lok, damit er auch dann links und rechts
   hervorschaut, wenn dieser genau darauf liegt – daran sieht ein Kind, dass es
   wirklich mittig steht und nicht bloss irgendwo darüber. */
.zg-marke-strich {
  position: absolute;
  left: 50%;
  bottom: calc(var(--zg-band) - var(--zg-strich));
  width: 9px;
  height: var(--zg-strich);
  border-radius: 0 0 3px 3px;
  background: #243047;
  transform: translateX(-50%);
}
.zg-marke.is-bahnhof .zg-marke-strich { background: #8a3a2c; }

/* Die Zahl steht unter ihrem Strich, im Gras – nur auf dem ersten Gleis, wo
   gezählt statt geschätzt wird. */
.zg-marke-zahl {
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  color: #243047;
  font-weight: 900;
  font-size: clamp(0.8rem, 2.6vh, 1.15rem);
  line-height: 1;
  white-space: nowrap;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.85), 0 0 4px rgba(255, 255, 255, 0.9);
}

/* Der Bahnhof steht oben auf der Schiene, mit seiner Zahl im Giebel. */
.zg-bahnhof {
  position: absolute;
  left: 50%;
  bottom: calc(var(--zg-band) + 12px);
  transform: translateX(-50%);
  display: grid;
  place-items: center;
  width: clamp(38px, 8vh, 54px);
  color: #243047;
  font-weight: 900;
  font-size: clamp(0.9rem, 3vh, 1.25rem);
  line-height: 1;
  white-space: nowrap;
}
.zg-bahnhof svg { width: 100%; height: auto; display: block; }
.zg-bahnhof-zahl {
  position: absolute;
  top: 38%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: clamp(0.85rem, 2.8vh, 1.15rem);
  color: #8a3a2c;
}

/* Die Lok des Kindes. Sie steht mit ihrer Mitte an --zg-p; beim Schieben
   folgt sie dem Finger ohne Verzögerung, sonst fährt sie – etwa zurück zum
   Anfang, wenn die nächste Zahl kommt. */
.zg-lok {
  position: absolute;
  bottom: calc(var(--zg-band) + 12px);
  left: calc(var(--zg-rand) + var(--zg-p, 0) * (100% - 2 * var(--zg-rand)));
  width: var(--zg-lok);
  transform: translateX(-50%);
  transition: left 0.55s cubic-bezier(0.3, 0.7, 0.3, 1);
  pointer-events: none;
}
.zg-lok.is-drag, .zg-lok.is-sofort { transition: none; }
/* Der Strich unter der Lok: genau ihre Mitte, und genau die wird gewertet.
   Er reicht durch die Schiene hinunter bis zur Unterkante der Marken – dorthin,
   wo ihn nichts verdeckt. Rot statt dunkel, damit er auf der dunklen Marke zu
   sehen bleibt: liegt er mittig darin, steht die Lok genau auf der Zahl. Der
   helle Saum trennt ihn vom Holz der Schiene. */
.zg-lok::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: calc(-12px - var(--zg-strich));
  width: 3px;
  height: calc(18px + var(--zg-strich));
  border-radius: 1px;
  background: #c1341f;
  box-shadow: 0 0 0 1px #fff;
  transform: translateX(-50%);
}
.zg-lok-bild { display: block; width: 100%; height: auto; filter: drop-shadow(0 4px 6px rgba(31, 42, 51, 0.25)); }
.zg-lok.is-drag .zg-lok-bild { transform: scale(1.06); }
.zg-lok.is-genau .zg-lok-bild { filter: drop-shadow(0 0 10px #2f9e44); }
.zg-lok.is-fast .zg-lok-bild { filter: drop-shadow(0 0 10px #8fd14f); }
.zg-lok.is-knapp .zg-lok-bild { filter: drop-shadow(0 0 10px #f6b93b); }
.zg-lok.is-nah .zg-lok-bild { filter: drop-shadow(0 0 8px #ff9f1c); }
.zg-lok.is-weit .zg-lok-bild { filter: drop-shadow(0 0 8px #f0885a); }
.zg-lok.is-daneben .zg-lok-bild { filter: drop-shadow(0 0 8px #e2694f); }

/* Die Punkte für diese Zahl, über der Lok. */
.zg-plus {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 4px);
  transform: translateX(-50%);
  padding: 2px 10px;
  border-radius: 999px;
  background: #fff;
  color: #243047;
  font-weight: 900;
  font-size: clamp(0.95rem, 3vh, 1.3rem);
  box-shadow: 0 3px 8px rgba(31, 42, 51, 0.2);
  animation: zg-plus 0.4s cubic-bezier(0.2, 1.4, 0.4, 1) both;
}
.zg-plus.is-genau { background: #2f9e44; color: #fff; }
.zg-plus.is-fast { background: #b5e48c; }
.zg-plus.is-knapp { background: #f6b93b; }
.zg-plus.is-nah { background: #ffd8a8; }
.zg-plus.is-weit { background: #f7c7b4; }
.zg-plus.is-daneben { background: #f2c4bb; }
@keyframes zg-plus {
  0% { transform: translate(-50%, 8px) scale(0.6); opacity: 0; }
  100% { transform: translate(-50%, 0) scale(1); opacity: 1; }
}

/* Das Schild an der richtigen Stelle: ein Pfahl, obendrauf die Zahl in einem
   Kreis. Grün, wenn die Lok genau davorsteht, sonst gelb bis rot – dieselbe
   Farbe wie der Schein um die Lok. Es kommt von oben herunter, damit der
   Blick von der Lok dorthin springt. */
/* Der Pfahl reicht bis zur Unterkante der Marken hinunter, genau wie der Strich
   der Lok: so stehen beide auf derselben Linie und lassen sich vergleichen. */
.zg-ziel {
  --zg-ziel-farbe: #2f9e44;
  position: absolute;
  bottom: calc(var(--zg-band) - var(--zg-strich));
  left: calc(var(--zg-rand) + var(--zg-p, 0) * (100% - 2 * var(--zg-rand)));
  width: 6px;
  height: calc(var(--zg-lok) + 24px + var(--zg-strich));
  border-radius: 3px;
  background: #243047;
  transform: translateX(-50%);
  pointer-events: none;
  animation: zg-ziel 0.45s cubic-bezier(0.2, 1.2, 0.4, 1) both;
}
.zg-ziel.is-fast { --zg-ziel-farbe: #5aa832; }
.zg-ziel.is-knapp { --zg-ziel-farbe: #d9950f; }
.zg-ziel.is-nah { --zg-ziel-farbe: #e07b1a; }
.zg-ziel.is-weit { --zg-ziel-farbe: #d6613f; }
.zg-ziel.is-daneben { --zg-ziel-farbe: #c94a33; }
.zg-ziel-zahl {
  position: absolute;
  top: 0;
  left: 50%;
  display: grid;
  place-items: center;
  width: 2.1em;
  height: 2.1em;
  border-radius: 50%;
  background: var(--zg-ziel-farbe);
  color: #fff;
  font-weight: 900;
  font-size: clamp(1.1rem, 4vh, 1.7rem);
  line-height: 1;
  transform: translate(-50%, -50%);
  box-shadow: 0 4px 10px rgba(31, 42, 51, 0.25), inset 0 0 0 3px #fff;
}
@keyframes zg-ziel {
  0% { transform: translate(-50%, -30px); opacity: 0; }
  100% { transform: translate(-50%, 0); opacity: 1; }
}

/* Die Stufenwahl: vier Knöpfe mit der grössten Zahl der Stufe und einem
   kleinen Gleis darunter, das nach rechts immer weniger Striche trägt. */
.zg-stufen { display: flex; gap: clamp(10px, 2.4vw, 22px); flex: 0 0 auto; }
.zg-stufe {
  display: grid;
  place-items: center;
  gap: 4px;
  min-width: clamp(64px, 14vw, 110px);
  padding: clamp(10px, 2.2vh, 18px) clamp(8px, 1.6vw, 16px);
  border: 3px solid #fff;
  border-radius: 22px;
  background: rgba(255, 255, 255, 0.9);
  cursor: pointer;
  box-shadow: 0 8px 18px rgba(31, 42, 51, 0.18);
  transition: transform 0.12s ease, background 0.16s ease;
  -webkit-tap-highlight-color: transparent;
}
.zg-stufe:hover { background: #fff; }
.zg-stufe:active { transform: scale(0.95); }
.zg-stufe:focus-visible { outline: 3px solid #243047; outline-offset: 3px; }
.zg-stufe-zahl {
  font-size: clamp(1.6rem, 6.5vh, 2.6rem);
  font-weight: 900;
  line-height: 1.1;
  color: #243047;
  font-variant-numeric: tabular-nums;
}
.zg-stufe-gleis { display: block; width: clamp(56px, 11vw, 84px); height: auto; }

/* Dreistellige Bahnhofszahlen brauchen eine kleinere Schrift. */
.zg-bahnhof-zahl.is-lang { font-size: clamp(0.7rem, 2.3vh, 0.95rem); }

@media (prefers-reduced-motion: reduce) {
  .zg-lok { transition: none; }
  .zg-schild.is-neu, .zg-plus, .zg-ziel { animation: none; }
}

/* ===========================================================================
   Halt am Signal
   ---------------------------------------------------------------------------
   Dieselbe Bühne wie Schwarm-Fokus, in der Farbe des Bereichs Konzentration.
   In der Mitte die Strecke: links der Tunnel, rechts das Signal mit der
   Schranke, dazwischen das Gleis. Der Zug ist die Lok des Kindes; wo er
   gerade ist, sagt eine Klasse (is-tunnel, is-fahrt, is-halt, is-durch,
   is-ruck), und die Fahrt selbst ist ein Übergang auf left – signal.js setzt
   nur die Klasse und zählt die Zeit daneben mit.
   =========================================================================== */

.sg-stage {
  --cm-accent: #00A5B5;
  --cm-accent-dark: #00707c;
  --sg-fahrt: 0.9s;
  --sg-weiter: 0.48s;
  touch-action: manipulation;
}

/* Getippt wird auf der ganzen Spielfläche, und schnell hintereinander. Ohne
   das markiert ein zweiter Tipp den Text darüber, und der Browser legt seinen
   grauen Schatten über die Stelle – beides sieht aus, als wäre etwas
   schiefgegangen. */
.sg-stage .cm-play {
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

.sg-strecke {
  --sg-lok: clamp(84px, 18vh, 150px);
  position: relative;
  flex: 0 0 auto;
  width: min(96vw, 1000px);
  height: calc(var(--sg-lok) + 30px);
  overflow: hidden;
  overflow: clip;
  border-radius: 22px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.sg-strecke.is-fehler { animation: sg-fehler 0.45s ease both; }
@keyframes sg-fehler {
  0%, 100% { box-shadow: none; }
  30% { box-shadow: inset 0 0 0 6px rgba(226, 105, 79, 0.85); }
}

.sg-gleis {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 6px;
  height: 16px;
  border-radius: 6px;
  background:
    linear-gradient(#8a5f1c, #8a5f1c) 0 2px / 100% 3px no-repeat,
    linear-gradient(#8a5f1c, #8a5f1c) 0 11px / 100% 3px no-repeat,
    repeating-linear-gradient(90deg, transparent 0 14px, #c98a3f 14px 22px);
}

/* Der Tunnel: ein dunkler Bogen am linken Rand, vor dem Zug – so kommt der
   Zug aus ihm heraus. */
.sg-tunnel {
  position: absolute;
  left: -14px;
  bottom: 0;
  width: 64px;
  height: calc(var(--sg-lok) + 10px);
  border-radius: 40px 40px 0 0;
  background: linear-gradient(#5b6470, #3c4652 60%, #2b3440);
  box-shadow: inset -10px 0 14px rgba(0, 0, 0, 0.35);
  z-index: 3;
}

/* Der Zug. Sein linker Rand steht bei left; die Klassen setzen die Stationen:
   im Tunnel (links, unsichtbar), vor dem Signal, hinter dem Bild. */
.sg-zug {
  position: absolute;
  bottom: 2px;
  left: calc(-1 * var(--sg-lok) - 20px);
  width: var(--sg-lok);
  z-index: 2;
  filter: drop-shadow(0 4px 6px rgba(31, 42, 51, 0.25));
}
.sg-zug.is-fahrt { left: calc(100% - var(--sg-lok) - 118px); transition: left var(--sg-fahrt) cubic-bezier(0.3, 0.8, 0.5, 1); }
.sg-zug.is-halt { left: calc(100% - var(--sg-lok) - 118px); }
.sg-zug.is-durch { left: calc(100% + 30px); transition: left var(--sg-weiter) cubic-bezier(0.5, 0, 0.8, 0.6); }
.sg-zug.is-ruck { left: calc(100% - var(--sg-lok) - 110px); animation: sg-ruck 0.5s ease both; }
@keyframes sg-ruck {
  0% { transform: translateX(0); }
  30% { transform: translateX(10px) rotate(-2deg); }
  60% { transform: translateX(-6px); }
  100% { transform: translateX(0); }
}
.sg-lok-bild { display: block; width: 100%; height: auto; }

/* Das Signal rechts. Rot oben, Grün unten; das leuchtende Licht bekommt einen
   Schein, das andere bleibt dunkel. */
.sg-signal {
  position: absolute;
  right: 16px;
  bottom: 18px;
  width: clamp(30px, 6vh, 46px);
  z-index: 4;
}
.sg-signal-bild { display: block; width: 100%; height: auto; }
.sg-licht-rot { fill: #6b2b23; }
.sg-licht-gruen { fill: #1f5a2b; }
.sg-signal.is-rot .sg-licht-rot { fill: #ff5d5d; filter: drop-shadow(0 0 8px #ff5d5d); }
.sg-signal.is-gruen .sg-licht-gruen { fill: #4be27a; filter: drop-shadow(0 0 8px #4be27a); }

/* Die Schranke vor dem Signal: zu, bis der Zug durchdarf. */
.sg-schranke {
  position: absolute;
  right: calc(16px + clamp(30px, 6vh, 46px) - 6px);
  bottom: 20px;
  width: clamp(70px, 14vh, 112px);
  z-index: 4;
}
.sg-schranke-bild { display: block; width: 100%; height: auto; overflow: visible; }
.sg-balken { transform-box: fill-box; transform-origin: 8% 50%; transition: transform 0.28s cubic-bezier(0.3, 1.2, 0.5, 1); }
.sg-schranke.is-offen .sg-balken { transform: rotate(-78deg); }

/* Der grosse Knopf unter der Strecke: derselbe Tipp wie auf die Strecke, für
   Kinder, die lieber einen Knopf haben. */
.sg-knopf {
  flex: 0 0 auto;
  width: clamp(64px, 14vh, 96px);
  height: clamp(64px, 14vh, 96px);
  border: none;
  border-radius: 50%;
  background: #2f9e44;
  color: #fff;
  box-shadow: 0 6px 0 #1f6b2e, 0 10px 20px rgba(31, 42, 51, 0.25);
  cursor: pointer;
  display: grid;
  place-items: center;
  -webkit-tap-highlight-color: transparent;
}
.sg-knopf svg { width: 50%; height: auto; }
.sg-knopf:active { transform: translateY(3px); box-shadow: 0 3px 0 #1f6b2e, 0 6px 12px rgba(31, 42, 51, 0.25); }
.sg-knopf:focus-visible { outline: 3px solid #243047; outline-offset: 4px; }

@media (prefers-reduced-motion: reduce) {
  .sg-zug.is-fahrt, .sg-zug.is-durch { transition: opacity 0.2s linear; }
  .sg-zug.is-ruck { animation: none; }
  .sg-strecke.is-fehler { animation: none; box-shadow: inset 0 0 0 6px rgba(226, 105, 79, 0.85); }
}

/* ===========================================================================
   Was fehlt?
   ---------------------------------------------------------------------------
   Dieselbe Bühne wie Kacheln-Knobeln, in der Farbe des Bereichs Gedächtnis.
   In der Mitte ein offener Güterwagen, die Fracht in einer Reihe darauf, und
   eine Plane, die von oben herunterkommt (is-zu). Darunter die Auswahl.
   Die Fracht sind die Gegenstände aus strand-art.js; wie viele nebeneinander
   liegen, sagt --wf-stuecke, und danach richtet sich, wie gross jedes wird.
   =========================================================================== */

.wf-stage { --cm-accent: #7C5CE6; --cm-accent-dark: #5a41b8; }

.wf-prompt { flex: 0 0 auto; min-height: 1.4em; }

/* Der Wagen: Bordwand, Ladefläche, zwei Räder – aus Rahmen und
   Pseudo-Elementen, damit er so breit sein darf, wie die Fracht braucht. */
.wf-wagen {
  --wf-stueck: clamp(44px, 12vh, 84px);
  position: relative;
  flex: 0 0 auto;
  max-width: 96vw;
  padding: clamp(6px, 1.4vh, 12px) clamp(14px, 2.6vw, 26px) clamp(8px, 1.6vh, 14px);
  margin-bottom: clamp(12px, 3vh, 22px);
  border-radius: 8px 8px 14px 14px;
  background: linear-gradient(#a58ef0, #7C5CE6 30%, #6a4bd0);
  box-shadow: inset 0 -4px 0 #4d37a8, 0 10px 24px rgba(31, 42, 51, 0.22);
}
.wf-wagen::before,
.wf-wagen::after {
  content: "";
  position: absolute;
  bottom: calc(-1 * clamp(10px, 2.4vh, 18px));
  width: clamp(20px, 4.8vh, 36px);
  height: clamp(20px, 4.8vh, 36px);
  border-radius: 50%;
  background: radial-gradient(circle, #c9bcf5 0 24%, #3b2a86 26% 62%, #5a41b8 64%);
  box-shadow: 0 3px 6px rgba(31, 42, 51, 0.3);
}
.wf-wagen::before { left: 12%; }
.wf-wagen::after { right: 12%; }

/* Die Fracht in einer Reihe, jedes Stück gleich breit. */
.wf-ladung {
  display: flex;
  gap: clamp(4px, 1vw, 10px);
  align-items: flex-end;
  justify-content: center;
}
.wf-stueck {
  display: block;
  width: var(--wf-stueck);
  height: var(--wf-stueck);
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.35);
  transition: opacity 0.25s ease, transform 0.25s ease;
}
.wf-stueck .st-art { display: block; width: 100%; height: 100%; }

/* Die Lücke: das Stück ist unsichtbar, sein Platz bleibt als gestrichelter
   Rahmen stehen – wo etwas fehlt, sieht man; was, muss man wissen. */
.wf-stueck.is-weg .st-art { visibility: hidden; }
.wf-stueck.is-weg { background: none; box-shadow: inset 0 0 0 3px rgba(255, 255, 255, 0.75); border-style: dashed; }
.wf-stueck.is-zurueck { animation: wf-zurueck 0.5s cubic-bezier(0.2, 1.4, 0.4, 1) both; }
@keyframes wf-zurueck {
  0% { transform: translateY(-40px) scale(0.6); opacity: 0; }
  100% { transform: none; opacity: 1; }
}

/* Die Plane: liegt oberhalb des Wagens bereit und kommt herunter. */
.wf-plane {
  position: absolute;
  inset: -6px -4px -2px;
  border-radius: 12px;
  background:
    repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.08) 0 12px, transparent 12px 24px),
    linear-gradient(#e6d3ad, #c9b48a);
  box-shadow: 0 6px 14px rgba(31, 42, 51, 0.3);
  transform: translateY(-115%);
  opacity: 0;
  transition: transform 0.5s cubic-bezier(0.3, 0.8, 0.4, 1), opacity 0.2s linear;
  pointer-events: none;
}
.wf-wagen.is-zu .wf-plane { transform: translateY(0); opacity: 1; }

/* Die Auswahl: drei oder vier grosse Knöpfe mit je einem Stück. */
.wf-wahl {
  display: flex;
  gap: clamp(10px, 2.4vw, 24px);
  flex: 0 0 auto;
  min-height: clamp(56px, 14vh, 100px);
}
.wf-knopf {
  width: clamp(56px, 14vh, 100px);
  height: clamp(56px, 14vh, 100px);
  padding: 6px;
  border: none;
  border-radius: 18px;
  background: rgba(253, 251, 246, 0.94);
  box-shadow: 0 6px 0 rgba(31, 42, 51, 0.15), 0 10px 20px rgba(31, 42, 51, 0.18);
  cursor: pointer;
  animation: wf-knopf 0.35s cubic-bezier(0.2, 1.4, 0.4, 1) both;
  -webkit-tap-highlight-color: transparent;
}
.wf-knopf:nth-child(2) { animation-delay: 0.06s; }
.wf-knopf:nth-child(3) { animation-delay: 0.12s; }
.wf-knopf:nth-child(4) { animation-delay: 0.18s; }
@keyframes wf-knopf {
  0% { transform: translateY(16px) scale(0.8); opacity: 0; }
  100% { transform: none; opacity: 1; }
}
.wf-knopf .st-art { display: block; width: 100%; height: 100%; }
.wf-knopf:active:not(:disabled) { transform: scale(0.94); }
.wf-knopf:focus-visible { outline: 3px solid #243047; outline-offset: 3px; }
.wf-knopf:disabled { cursor: default; opacity: 1; }
.wf-knopf.is-richtig { background: #d3f1da; box-shadow: 0 0 0 4px #2f9e44, 0 10px 20px rgba(31, 42, 51, 0.18); }
.wf-knopf.is-daneben { background: #f7d6cf; box-shadow: 0 0 0 4px #e2694f, 0 10px 20px rgba(31, 42, 51, 0.18); animation: kk-daneben 0.4s ease both; }

@media (prefers-reduced-motion: reduce) {
  .wf-plane { transition: opacity 0.2s linear; }
  .wf-stueck.is-zurueck, .wf-knopf, .wf-knopf.is-daneben { animation: none; }
}

/* ===========================================================================
   Doppelt gleich
   ---------------------------------------------------------------------------
   Dieselbe Bühne wie Schwarm-Fokus, in der Farbe des Bereichs
   Geschwindigkeit. Zwei runde Karten nebeneinander, so gross, wie die Höhe
   hergibt; jedes Bild sitzt an einem Anteil --dg-x/--dg-y der Karte, gedreht
   um --dg-drehung und --dg-groesse Kartenbreiten gross – doppelt.js würfelt
   die Werte, hier steht nur, wie sie wirken.
   =========================================================================== */

.dg-stage { --cm-accent: #F5A623; --cm-accent-dark: #b9741a; }

.dg-karten {
  display: flex;
  gap: clamp(14px, 4vw, 48px);
  flex: 0 0 auto;
  justify-content: center;
}

.dg-karte {
  --dg-karte: clamp(150px, 56vh, 320px);
  position: relative;
  width: var(--dg-karte);
  height: var(--dg-karte);
  border-radius: 50%;
  background: #fff8ea;
  box-shadow: 0 12px 30px rgba(31, 42, 51, 0.22), inset 0 0 0 5px var(--cm-accent);
  animation: dg-karte 0.32s cubic-bezier(0.2, 1.3, 0.4, 1) both;
}
.dg-karte.is-b { animation-delay: 0.05s; }
/* Vor dem Start liegt unter den Karten noch der Startknopf: dann sind sie
   kleiner, sonst rutscht er auf dem Handy unter den Rand. */
.dg-stage[data-phase="intro"] .dg-karte { --dg-karte: clamp(110px, 38vh, 240px); }
@keyframes dg-karte {
  0% { transform: scale(0.7); opacity: 0; }
  100% { transform: scale(1); opacity: 1; }
}

/* Ein Bild: Mitte an --dg-x/--dg-y, in eigener Grösse. Gedreht wird das Bild
   im Knopf, nicht der Knopf: so bleibt die Tippfläche ein gerades Quadrat, und
   die Prüfung auf dem Handy kann nachmessen, dass sich keine zwei berühren. */
.dg-bild {
  position: absolute;
  left: calc(var(--dg-x, 0.5) * 100%);
  top: calc(var(--dg-y, 0.5) * 100%);
  width: calc(var(--dg-groesse, 0.3) * var(--dg-karte));
  height: calc(var(--dg-groesse, 0.3) * var(--dg-karte));
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  transform: translate(-50%, -50%);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.dg-bild .st-art { display: block; width: 100%; height: 100%; transform: rotate(var(--dg-drehung, 0deg)); }
.dg-bild:focus-visible { outline: 3px solid #243047; outline-offset: 2px; }
.dg-bild:active { transform: translate(-50%, -50%) scale(0.92); }

/* Gefunden: beide Zwillinge bekommen einen grünen Ring und hüpfen. */
.dg-bild.is-richtig {
  background: rgba(47, 158, 68, 0.18);
  box-shadow: 0 0 0 4px #2f9e44;
  animation: dg-richtig 0.3s ease both;
}
@keyframes dg-richtig {
  0% { transform: translate(-50%, -50%) scale(1); }
  50% { transform: translate(-50%, -50%) scale(1.22); }
  100% { transform: translate(-50%, -50%) scale(1); }
}

/* Daneben: ein roter Ring, und das Bild wackelt einmal. */
.dg-bild.is-daneben {
  box-shadow: 0 0 0 4px #e2694f;
  animation: dg-daneben 0.4s ease both;
}
@keyframes dg-daneben {
  0%, 100% { transform: translate(-50%, -50%); }
  25% { transform: translate(calc(-50% - 6px), -50%); }
  75% { transform: translate(calc(-50% + 6px), -50%); }
}

@media (prefers-reduced-motion: reduce) {
  .dg-karte, .dg-bild.is-richtig, .dg-bild.is-daneben { animation: none; }
}

/* ===========================================================================
   Fässer stapeln
   ---------------------------------------------------------------------------
   Dieselbe Bühne wie Weichen-Wirrwarr, in der Farbe des Bereichs
   Problemlösen. Erst die Levelwahl, dann der Platz: oben die Kranschiene mit
   der Laufkatze, die über einem der drei Gleise steht (--fs-x: 0, 1 oder 2),
   darunter die drei Gleise mit ihren Fässern. Ein Fass hängt entweder im
   Stapel eines Gleises oder am Haken der Laufkatze – faesser.js hängt das
   Element um, die Fahrt dazwischen ist ein Übergang auf left.
   =========================================================================== */

.fs-stage { --cm-accent: #3FA34D; --cm-accent-dark: #2b7336; }

/* --- Levelwahl ------------------------------------------------------------ */

.fs-levels {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: clamp(8px, 1.6vh, 14px);
  width: min(94vw, 760px);
}
.fs-level {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: clamp(6px, 1.4vh, 12px) 6px;
  border: 3px solid transparent;
  border-radius: 18px;
  background: rgba(253, 251, 246, 0.94);
  box-shadow: 0 6px 16px rgba(31, 42, 51, 0.16);
  cursor: pointer;
  transition: transform 0.12s ease, background 0.12s ease;
  -webkit-tap-highlight-color: transparent;
}
.fs-level:hover { background: #fff; }
.fs-level:active { transform: scale(0.95); }
.fs-level:focus-visible { outline: 3px solid #243047; outline-offset: 3px; }
.fs-level-nr {
  display: grid;
  place-items: center;
  width: clamp(28px, 5vh, 36px);
  height: clamp(28px, 5vh, 36px);
  border-radius: 50%;
  background: var(--cm-accent);
  color: #fff;
  font-weight: 900;
  font-size: clamp(0.9rem, 2.6vh, 1.1rem);
}
.fs-level-turm {
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
  gap: 2px;
  width: 100%;
  min-height: clamp(30px, 7vh, 50px);
  justify-content: flex-start;
}
.fs-level-fass { height: clamp(5px, 1.2vh, 8px); border-radius: 3px; box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.18); }
.fs-level-stars { display: flex; gap: 1px; font-size: 0.85rem; line-height: 1; color: #d5dae2; }
.fs-level-star.is-on { color: #f6b93b; }

/* --- Der Platz ------------------------------------------------------------ */

.fs-prompt { flex: 0 0 auto; }

.fs-platz {
  --fs-fass: clamp(18px, 5.2vh, 36px);
  position: relative;
  flex: 0 0 auto;
  display: flex;
  align-items: flex-end;
  width: min(96vw, 900px);
  height: calc(var(--fs-fass) * 5 + clamp(90px, 22vh, 150px));
  padding: 0 clamp(6px, 2vw, 20px);
  border-radius: 24px;
  background: rgba(253, 251, 246, 0.55);
  box-shadow: inset 0 0 0 3px rgba(255, 255, 255, 0.6);
}
.fs-platz.is-geschafft { box-shadow: inset 0 0 0 5px #2f9e44; }

/* Die Kranschiene oben, mit Trägern. */
.fs-schiene {
  position: absolute;
  top: clamp(8px, 2vh, 14px);
  left: 4%;
  right: 4%;
  height: 10px;
  border-radius: 5px;
  background: #5a6b5c;
  box-shadow: inset 0 -3px 0 #3a4a3c;
}

/* Die Laufkatze: steht über Gleis --fs-x. Die Gleise teilen sich die Breite
   zu dritt, ihre Mitten liegen bei 1/6, 3/6 und 5/6. */
.fs-laufkatze {
  position: absolute;
  top: clamp(4px, 1vh, 8px);
  left: calc((var(--fs-x, 0) * 2 + 1) / 6 * 100%);
  width: clamp(40px, 9vh, 64px);
  transform: translateX(-50%);
  transition: left 0.3s cubic-bezier(0.4, 0, 0.3, 1);
  z-index: 3;
  pointer-events: none;
}
.fs-laufkatze::before {
  content: "";
  display: block;
  height: clamp(14px, 3.4vh, 22px);
  border-radius: 6px;
  background: var(--cm-accent);
  box-shadow: inset 0 -4px 0 var(--cm-accent-dark);
}
.fs-seil {
  display: block;
  width: 3px;
  height: clamp(14px, 4vh, 28px);
  margin: 0 auto;
  background: #3a4a3c;
  transition: height 0.2s ease;
}
.fs-laufkatze.is-hebt .fs-seil { height: clamp(10px, 2.6vh, 18px); }
.fs-haken {
  position: relative;
  display: block;
  width: 100%;
  min-height: 12px;
}
.fs-haken::before {
  content: "";
  position: absolute;
  top: -4px;
  left: 50%;
  width: 10px;
  height: 10px;
  border: 3px solid #3a4a3c;
  border-top-color: transparent;
  border-radius: 50%;
  transform: translateX(-50%);
}
/* Das Fass am Haken: hängt mittig unter der Laufkatze, so breit wie im Stapel
   – die Laufkatze ist so breit wie ein Gleis ohne Rand. */
.fs-haken .fs-fass {
  position: absolute;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  width: calc(var(--fs-groesse, 1) * 26px + 14px);
}
.fs-laufkatze.is-verboten .fs-haken .fs-fass { animation: fs-verboten 0.4s ease both; }
@keyframes fs-verboten {
  0%, 100% { transform: translateX(-50%); }
  25% { transform: translateX(calc(-50% - 7px)); }
  75% { transform: translateX(calc(-50% + 7px)); }
}

/* Ein Gleis: ein Drittel der Breite, tippbar in ganzer Höhe. Der Pfosten
   steht in der Mitte, die Fässer stapeln sich von unten. */
.fs-gleis {
  position: relative;
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  height: calc(100% - clamp(40px, 9vh, 64px));
  margin: 0 clamp(4px, 1vw, 12px) clamp(8px, 2vh, 14px);
  border-radius: 16px;
  cursor: pointer;
  outline: none;
  -webkit-tap-highlight-color: transparent;
}
.fs-gleis:hover { background: rgba(255, 255, 255, 0.35); }
.fs-gleis:focus-visible { box-shadow: 0 0 0 3px #243047; }
.fs-gleis::after {
  content: "";
  position: absolute;
  bottom: calc(-1 * clamp(6px, 1.4vh, 10px));
  left: 4%;
  right: 4%;
  height: clamp(8px, 2vh, 14px);
  border-radius: 6px;
  background: #8a5f1c;
  box-shadow: inset 0 -3px 0 #5a3b10;
}
.fs-gleis.is-ziel::after { background: #2f9e44; box-shadow: inset 0 -3px 0 #1f6b2e; }
.fs-pfosten {
  position: absolute;
  bottom: 0;
  left: 50%;
  width: clamp(8px, 1.8vh, 12px);
  height: calc(var(--fs-fass) * 5 + 10px);
  border-radius: 5px 5px 0 0;
  background: #5a6b5c;
  transform: translateX(-50%);
}
.fs-fahne {
  position: absolute;
  top: calc(100% - var(--fs-fass) * 5 - 10px - clamp(26px, 6vh, 40px));
  left: calc(50% - 2px);
  width: clamp(20px, 4.6vh, 30px);
  height: auto;
}
.fs-stapel {
  position: relative;
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
  width: 100%;
  min-height: var(--fs-fass);
}

/* Ein Fass: Breite nach Grösse, zwei Reifen, ein Glanz. */
.fs-fass {
  display: block;
  width: calc(28% + var(--fs-groesse, 1) * 13%);
  height: var(--fs-fass);
  border-radius: calc(var(--fs-fass) * 0.42);
  background:
    linear-gradient(90deg, rgba(255, 255, 255, 0.35), transparent 40%),
    linear-gradient(transparent 26%, rgba(0, 0, 0, 0.28) 26% 36%, transparent 36% 64%, rgba(0, 0, 0, 0.28) 64% 74%, transparent 74%),
    var(--fs-farbe, #c98a3f);
  box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.18), 0 2px 4px rgba(31, 42, 51, 0.2);
}
.fs-fass.is-gelandet { animation: fs-gelandet 0.3s cubic-bezier(0.2, 1.3, 0.4, 1) both; }
@keyframes fs-gelandet {
  0% { transform: translateY(-14px); }
  100% { transform: none; }
}

/* Ein Tipp auf ein leeres Gleis ohne Fass am Haken, oder ein verbotenes
   Ablegen: das Gleis blinkt kurz. */
.fs-gleis.is-leer, .fs-gleis.is-nein { animation: fs-nein 0.4s ease both; }
@keyframes fs-nein {
  0%, 100% { background: transparent; }
  40% { background: rgba(226, 105, 79, 0.35); }
}

@media (prefers-reduced-motion: reduce) {
  .fs-laufkatze, .fs-seil { transition: none; }
  .fs-fass.is-gelandet, .fs-laufkatze.is-verboten .fs-haken .fs-fass, .fs-gleis.is-leer, .fs-gleis.is-nein { animation: none; }
}

/* ===========================================================================
   Handy quer: alles passt aufs Bild
   ---------------------------------------------------------------------------
   Die App läuft auf dem Handy im Querformat, und dort ist die Höhe das knappe
   Mass: 320 bis 412 Bildpunkte, mehr nicht. Nichts darf scrollen – kein
   Bildschirm, kein Kasten darin –, und kein Knopf darf unter dem Rand liegen.
   Was hier steht, hängt deshalb an der Bildschirmhöhe, nicht an der Breite:
   auf einem Tablet gilt weiter das Bild von oben.

   Geprüft wird das mit scripts/check-handy.mjs, das jede Seite in mehreren
   Handy-Grössen anfährt und misst.
   =========================================================================== */

/* --- Rätselseiten: Weltwahl und Levelwahl ohne Scrollen --- */

/* Die Auswahl steht auf dem ganzen Bildschirm, mittig, und wächst nicht
   darüber hinaus. Vorher war sie ein normaler Seitenfluss – vier Welten
   untereinander, drei Reihen Level – und scrollte auf jedem Handy. */
body.has-scene:not(.puzzle-active) {
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}

body.has-scene:not(.puzzle-active) .app-shell {
  width: 100%;
  max-width: none;
  height: 100vh;
  height: 100dvh;
  margin: 0;
  padding: calc(var(--safe-top) + 72px) calc(var(--safe-right) + 12px) calc(var(--safe-bottom) + 10px) calc(var(--safe-left) + 12px);
  display: grid;
  align-items: center;
  justify-items: center;
}

body.has-scene .start-panel {
  width: min(100%, 960px);
  max-width: none;
  padding: 0;
  gap: 0;
}

/* Die Knöpfe oben links stehen nebeneinander – nie untereinander, wo der
   zweite über die erste Kachel liefe. */
body.has-scene .selection-actions.controls {
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
}
body.has-scene .selection-actions.controls > * { width: 48px; }

/* Vier Welten nebeneinander, so hoch wie der Platz erlaubt. Auf schmalen
   Fenstern stapelte eine Regel weiter oben sie untereinander – auf einem
   Handy quer ist das Fenster schmal und flach zugleich, und untereinander
   ist dann der falsche Weg. */
body.has-scene .difficulty-grid {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(8px, 1.6vw, 18px);
  width: 100%;
}
body.has-scene .difficulty-grid.has-garten { grid-template-columns: repeat(5, minmax(0, 1fr)); }

body.has-scene .difficulty-card {
  min-height: 0;
  height: clamp(110px, calc(100vh - var(--safe-top) - var(--safe-bottom) - 100px), 230px);
  height: clamp(110px, calc(100dvh - var(--safe-top) - var(--safe-bottom) - 100px), 230px);
  display: grid;
  grid-template-columns: none;
  grid-template-rows: none;
  place-items: center;
  align-content: center;
  justify-items: center;
  gap: clamp(2px, 1vh, 8px);
  padding: clamp(6px, 1.4vh, 14px) clamp(6px, 1vw, 14px);
  text-align: center;
  border-width: 4px;
}

body.has-scene .difficulty-icon {
  grid-row: auto;
  grid-column: auto;
  align-self: auto;
  width: clamp(40px, 12vh, 76px);
  height: clamp(40px, 12vh, 76px);
  font-size: clamp(1.4rem, 6.5vh, 3rem);
}

body.has-scene .difficulty-name {
  grid-row: auto;
  grid-column: auto;
  align-self: auto;
  font-size: clamp(0.95rem, 3.8vh, 1.5rem);
}

body.has-scene .difficulty-card small {
  grid-row: auto;
  grid-column: auto;
  align-self: auto;
  font-size: clamp(0.62rem, 2.5vh, 0.9rem);
}

/* Zehn Level in zwei Reihen zu fünf. Die Kacheln teilen sich die Höhe, die
   unter der Leiste übrig bleibt. */
body.has-scene .level-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: clamp(6px, 1.4vw, 14px);
  width: min(100%, 760px);
  margin: 0 auto;
}

body.has-scene .level-tile {
  flex: none;
  min-width: 0;
  min-height: 0;
  height: clamp(58px, calc((100vh - var(--safe-top) - var(--safe-bottom) - 110px) / 2), 124px);
  height: clamp(58px, calc((100dvh - var(--safe-top) - var(--safe-bottom) - 110px) / 2), 124px);
  padding: 4px;
  gap: 2px;
  border-radius: clamp(12px, 3vh, 22px);
  border-width: 3px;
}

body.has-scene .level-tile span { font-size: clamp(1.1rem, 5.5vh, 1.75rem); }
body.has-scene .level-tile small { font-size: clamp(0.65rem, 2.5vh, 0.9rem); }
body.has-scene .level-tile small.lock-icon { font-size: clamp(0.9rem, 3.8vh, 1.15rem); }
body.has-scene .level-tile .star-row-small .star { font-size: clamp(0.7rem, 3vh, 1rem); }

/* --- Die Bühne der Spiele: Titel, Ergebnis, Tier-Sprung-Karte --- */

/* Der Titel steht in der Leiste zwischen den Knöpfen und dem Zähler, nicht
   mehr frei über der Bildmitte: dort lief er auf dem Handy über den dritten
   Knopf. Wird es eng, wird er abgeschnitten – lieber drei Punkte als ein
   Knopf, der nicht mehr zu treffen ist. */
@media (max-width: 1100px) {
  .cm-title {
    position: static;
    transform: none;
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: center;
    font-size: clamp(0.95rem, 2.2vw, 1.4rem);
  }
}

/* Der Zurück-Knopf sitzt oben links; unten braucht niemand mehr Platz für
   ihn. Der breite Rand liess die Reihen auf dem Handy umbrechen. */
.loco-row,
.wagon-shelf { padding-inline: clamp(12px, 3vw, 40px); }

/* Die Tiere der Levelwahl wachsen mit der Bildschirmhöhe, nicht mit einer
   festen Zahl: zwei Reihen zu fünf plus Startknopf müssen unter die Leiste
   passen. */
.runner-level-art { width: 96px; height: 84px; }
.ts-stage .runner-level-art-slot { height: auto; }
.ts-stage .runner-level-art { width: clamp(44px, 12vh, 84px); height: auto; aspect-ratio: 8 / 7; }
.ts-stage .runner-level-badge { width: 22px; height: 22px; font-size: 0.72rem; top: 4px; left: 5px; }
.ts-stage .cm-start { min-height: clamp(46px, 13vh, 66px); }

/* Auf einem flachen Bildschirm liegt die Ergebnistafel quer: links Punkte,
   Satz und Knöpfe, rechts die Bestenliste. Untereinander brauchte sie mehr
   Höhe, als da ist, und bekam einen Rollbalken – die Knöpfe lagen dann unten
   ausserhalb, und ein Kind musste erst wischen, um weiterzukommen. */
@media (max-height: 600px) {
  .cm-overlay {
    padding: calc(var(--safe-top) + 72px) calc(var(--safe-right) + 12px) calc(var(--safe-bottom) + 10px) calc(var(--safe-left) + 12px);
  }

  .cm-panel {
    width: min(660px, 100%);
    padding: 10px 18px 12px;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "label liste"
      "wert liste"
      "detail liste"
      "note liste"
      "knoepfe liste";
    column-gap: 20px;
    row-gap: clamp(2px, 1.2vh, 6px);
    align-items: center;
    align-content: center;
    justify-items: center;
    overflow: hidden;
  }

  .cm-result-label { grid-area: label; font-size: 0.85rem; }
  .cm-result-score { grid-area: wert; font-size: clamp(2.2rem, 13vh, 3.6rem); }
  .cm-result-stars { grid-area: wert; margin: 0; }
  .cm-result-star svg { width: clamp(34px, 9vh, 50px); }
  .cm-result-detail { grid-area: detail; font-size: 0.95rem; }
  .cm-panel .cm-runs { grid-area: note; font-size: 0.9rem; }
  .cm-actions { grid-area: knoepfe; margin-top: 2px; }
  .cm-scores { grid-area: liste; width: clamp(150px, 26vw, 220px); gap: 3px; }
  .cm-score-item { padding: 3px 10px; font-size: 0.9rem; }
  .cm-score-value { font-size: 1rem; }
  .cm-icon.big { width: 56px; height: 56px; }
  .cm-icon.big svg { width: 28px; height: 28px; }
}

/* --- Rätselseiten: der Erfolgsdialog --- */

/* Quer statt hoch: links Fino, rechts Titel, Sterne und Satz, darunter die
   beiden Knöpfe in einer Reihe. Hochkant war der Dialog auf dem Handy oben
   und unten abgeschnitten – samt der Knöpfe, mit denen es weitergeht. */
@media (max-height: 600px) {
  .success-overlay { padding: 10px; }

  .success-modal {
    width: min(600px, 100%);
    max-height: 100%;
    padding: 12px 18px 14px;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
      "bild titel"
      "bild sterne"
      "bild note"
      "knoepfe knoepfe";
    column-gap: 18px;
    row-gap: 4px;
    align-items: center;
    text-align: left;
  }

  #success-title { grid-area: titel; margin: 0; font-size: clamp(1.5rem, 8vh, 2.2rem); }
  .success-content { display: contents; }
  .success-content .success-mascot { grid-area: bild; width: clamp(64px, 24vh, 100px); margin: 0; }
  .success-content .success-stars { grid-area: sterne; margin: 0; }
  .success-content .success-stars .star-row { justify-content: flex-start; }
  .success-content .success-note { grid-area: note; margin: 0; font-size: 1rem; }
  .success-actions { grid-area: knoepfe; margin-top: 8px; gap: 12px; }
  .success-text-button { min-height: 50px; font-size: 1rem; }
}

/* --- Rätselseiten: Buchstabenjagd und Wortdetektiv --- */

/* Bild und Frage links, die Antwortkarten rechts. Untereinander lagen die
   Karten auf dem Handy unter dem Rand des Spielfelds – und das sind die
   Karten, auf die getippt werden muss. */
@media (max-height: 600px) {
  body.puzzle-active .letterPuzzle-board,
  body.puzzle-active .readingPuzzle-board {
    height: 100%;
    align-self: stretch;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
    grid-template-rows: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "punkte punkte"
      "frage antworten"
      "hinweis antworten";
    gap: 4px 16px;
    overflow: hidden;
  }

  body.puzzle-active .letterPuzzle-board .practice-progress-row,
  body.puzzle-active .readingPuzzle-board .practice-progress-row { grid-area: punkte; }

  body.puzzle-active .letterPuzzle-board .practice-task,
  body.puzzle-active .readingPuzzle-board .practice-task {
    grid-area: frage;
    min-height: 0;
    align-content: center;
    gap: clamp(2px, 1vh, 8px);
  }

  body.puzzle-active .letterPuzzle-board .practice-options,
  body.puzzle-active .readingPuzzle-board .practice-options {
    grid-area: antworten;
    align-self: center;
    gap: clamp(6px, 1.4vh, 10px);
  }
  body.puzzle-active .readingPuzzle-board .practice-options { grid-template-columns: 1fr; }
  body.puzzle-active .letterPuzzle-board .practice-options { grid-template-columns: repeat(var(--option-count, 3), minmax(0, 1fr)); }

  body.puzzle-active .letterPuzzle-board .practice-feedback,
  body.puzzle-active .readingPuzzle-board .practice-feedback {
    grid-area: hinweis;
    min-height: 0;
    gap: 4px;
  }
  body.puzzle-active .readingPuzzle-board .practice-feedback strong { font-size: 1.1rem; }
  body.puzzle-active .readingPuzzle-board .practice-feedback p { font-size: 0.95rem; }

  body.puzzle-active .practice-option {
    min-height: clamp(40px, 12vh, 62px);
    padding: 4px 10px;
    border-width: 3px;
    font-size: clamp(1.05rem, 5vh, 1.6rem);
  }
  body.puzzle-active .readingPuzzle-board .practice-option { font-size: clamp(0.95rem, 4.4vh, 1.35rem); }

  body.puzzle-active .letter-image { font-size: clamp(2.2rem, 16vh, 5rem); }
  body.puzzle-active .letter-word { font-size: clamp(1.1rem, 5.5vh, 1.6rem); }
  body.puzzle-active .letter-question { font-size: clamp(0.9rem, 4vh, 1.15rem); }

  body.puzzle-active .reading-image {
    width: clamp(56px, 22vh, 140px);
    border-radius: 18px;
    border-width: 3px;
    font-size: clamp(2rem, 13vh, 5rem);
  }
  body.puzzle-active .reading-prompt { font-size: clamp(0.95rem, 4.2vh, 1.3rem); }
  body.puzzle-active .letter-row span,
  body.puzzle-active .reading-display span {
    min-width: 30px;
    min-height: 36px;
    padding: 2px 6px;
    border-width: 2px;
    font-size: clamp(1rem, 5vh, 1.8rem);
  }
  body.puzzle-active .reading-display.sentence-gap span { font-size: clamp(0.9rem, 4.2vh, 1.4rem); }
  body.puzzle-active .correct-badge { width: 40px; height: 40px; font-size: 1.4rem; }
  body.puzzle-active .practice-dot { width: 11px; height: 11px; }
}

/* --- Rätselseiten: Schrift in den Feldern --- */

/* Die Schrift in einem Feld richtet sich nach dem Feld, nicht nach dem
   Fenster. Ein Buchstabe in Fenstergrösse in einem Feld von dreissig Bildpunkten
   macht die Reihe höher als breit – und das Brett damit höher als die Karte,
   die einen Rollbalken bekommt. --active-board-size setzt app.js, wenn es das
   Brett eingepasst hat; --size ist die Zahl der Spalten. */
body.puzzle-active .arukone-cell,
body.puzzle-active .hidoku-cell,
body.puzzle-active .kakuro-entry {
  line-height: 1;
  font-size: min(2.3rem, calc(var(--active-board-size, 400px) / var(--size, 5) * 0.58));
}

/* --- Tier-Sprung: die Dialoge im Spiel --- */

/* Kleiner und die Knöpfe in einer Reihe: hochkant ragte der Dialog auf dem
   Handy unter den Rand, und "Zur Karte" war nicht mehr zu erreichen. */
@media (max-height: 600px) {
  .runner-overlay { padding: 10px; }
  .runner-dialog { width: min(540px, 100%); max-height: 100%; padding: 12px 16px 14px; }
  .runner-dialog h2 { font-size: clamp(1.15rem, 6vh, 1.5rem); }
  .runner-mascot { width: clamp(44px, 16vh, 72px); margin-bottom: 0; }
  .runner-dialog-sub { margin-top: 4px; font-size: 0.92rem; }
  .runner-grow { margin-top: 4px; font-size: 0.95rem; }
  .runner-stars { margin: 4px 0 0; gap: 6px; }
  .runner-star { font-size: clamp(1.4rem, 7vh, 2rem); }
  .runner-dialog-actions { flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
  .runner-primary, .runner-secondary { flex: 1 1 auto; padding: 10px 12px; font-size: 0.95rem; }
}

/* --- Kakuro: das Zahlenfeld hat seinen festen Platz --- */

/* Brett links in der Mitte seiner Spalte, Zahlenfeld rechts daneben – vom
   ersten Bild an, nicht erst, wenn ein Feld gewählt ist. Vorher kam das
   Zahlenfeld unter das Brett, sobald es gebraucht wurde, und das Brett
   schrumpfte und rutschte ihm aus dem Weg; ohne gewähltes Feld sprang alles
   wieder zurück. Jetzt bleibt beides stehen, und ohne gewähltes Feld sind die
   Tasten nur blass. */
body.puzzle-active.pad-docked .game-card {
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: max-content minmax(0, 1fr);
  column-gap: clamp(10px, 2vw, 22px);
}

body.puzzle-active.pad-docked .status-row { grid-column: 1 / -1; }

body.puzzle-active.pad-docked .board {
  grid-column: 1;
  grid-row: 2;
  justify-self: center;
  align-self: center;
}

body.puzzle-active.pad-docked .number-pad {
  grid-column: 2;
  grid-row: 2;
  align-self: center;
  justify-self: end;
  grid-template-columns: repeat(var(--pad-cols, 3), minmax(44px, 60px));
  gap: 8px;
  padding: 10px;
  border-radius: 18px;
  transition: opacity 0.15s ease, border-color 0.15s ease;
}

body.puzzle-active.pad-docked .number-pad.is-idle {
  border-color: var(--line);
  box-shadow: none;
}

body.puzzle-active.pad-docked .number-pad.is-idle button {
  opacity: 0.3;
}

body.puzzle-active.pad-docked .number-pad button {
  min-height: 52px;
  font-size: 1.45rem;
}

@media (max-height: 540px) {
  body.puzzle-active.pad-docked .number-pad {
    grid-template-columns: repeat(var(--pad-cols, 3), minmax(36px, 46px));
    gap: 5px;
    padding: 6px;
    border-radius: 14px;
  }

  body.puzzle-active.pad-docked .number-pad button {
    min-height: 38px;
    padding: 0 6px;
    border-radius: 10px;
    font-size: 1.05rem;
  }

  body.puzzle-active.pad-docked .number-pad .clear-number-button { font-size: 0.8rem; }
}

/* ===========================================================================
   Die Reise: Streckenschild, Streckenkarte, Stempel
   ---------------------------------------------------------------------------
   Der sechste Weg vor den Toren. Die Karte zeichnet train-journey.js in die
   Bühnen-Ebene; hier steht, was sich bewegt: das Funkeln am Schild, der Nebel,
   der Stempel, das Signal, die Räder und der Dampf des kleinen Zugs, die
   Wahrzeichen bei der Ankunft.
   =========================================================================== */

/* --- Das Streckenschild vor den Toren --- */
.stage-layer .journey-sign { pointer-events: auto; cursor: pointer; }
.journey-sparkle {
  transform-box: fill-box;
  transform-origin: center;
  animation: journey-sparkle 1.8s ease-in-out infinite;
}
.journey-sparkle-2 { animation-delay: 0.6s; }
@keyframes journey-sparkle {
  0%, 100% { transform: scale(0.55); opacity: 0.35; }
  50% { transform: scale(1.15); opacity: 1; }
}

/* --- Die Ansicht --- */
/* Das grosse Zugband weg – der Zug steht klein auf der Karte. Die Ebene liegt
   über allen Landschaftsebenen, wie Werkstatt und Wagen-Ansicht. */
.train-stage[data-view="reise"] .train-band { opacity: 0; pointer-events: none; }
.train-stage[data-view="reise"] .stage-layer { z-index: 4; }
/* Der Lautsprecher spricht nur auf der Karte, neben dem Zurück-Knopf. */
body[data-page="train"] .train-stage[data-view="reise"] ~ .help-voice {
  display: flex;
  left: calc(var(--safe-left) + 86px);
}

.journey {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  /* Beschnitten am Rand der Bühne: Die Karte ist weit über ihren Ausschnitt
     hinaus gezeichnet, und beim Hereinschieben wäre sonst die ganze
     Zeichnung samt Rändern eine Ebene auf der Grafikkarte. */
  overflow: hidden;
  /* Der Himmel der Karte, wo die Zeichnung nicht hinreicht (hohes, schmales
     Bild): train-journey.js setzt die Farbe je Landschaft. */
  background: var(--journey-sky, transparent);
}
/* Unter der Karte bleibt die Landschaft des Startbilds unsichtbar. Sie war
   verdeckt, aber da: vier Bänder, doppelt so breit wie das Bild, jedes eine
   eigene Ebene, die der Browser Bild für Bild mitzeichnete. Was darin und
   am unsichtbaren Zugband noch animiert ist, steht dabei still: Eine
   Animation an etwas Unsichtbarem läuft nicht mehr auf der Grafikkarte,
   sondern im Hauptthread – und sobald eine einzige das tut, rechnet der
   Browser in jedem Bild alle Animationen der Seite nach und legt die ganze
   Karte neu aus. */
.train-stage[data-view="reise"] .scene { visibility: hidden; }
.train-stage[data-view="reise"] .scene-bird { animation-play-state: paused; }
.train-stage[data-view="reise"] .train-band * { animation: none; }
/* Von den Toren her schiebt sich die Karte von rechts herein, beim Wechsel
   auf die nächste Karte ebenso. Ohne die Klasse läuft nichts – auch nicht
   das Hereinschweben der Bühnen-Ebene, das die Karte sonst erbte. Und
   backwards statt both: Ist die Fahrt vorbei, hält nichts die Karte als
   eigene Ebene fest (siehe .stage-svg). */
.journey .journey-map { animation: none; }
.journey.is-entering .journey-map,
.journey.is-switching .journey-map { animation: journey-slide-in 0.7s cubic-bezier(0.2, 0.8, 0.2, 1) backwards; }
@keyframes journey-slide-in {
  from { opacity: 0; transform: translateX(26%); }
  to { opacity: 1; transform: none; }
}

/* Antippbar ist nur, was dran ist: die beiden offenen Stationen, gestempelte
   für den goldenen Stempel, die beiden Bilder einer Wahlstation, das
   Ausweichgleis. Der Nebel nimmt nichts entgegen. */
.stage-layer .journey-station,
.stage-layer .journey-choice,
.stage-layer .journey-alt { pointer-events: auto; cursor: pointer; }
.stage-layer .journey-station:not(.is-open):not(.is-done),
.stage-layer .journey-station.has-choice { pointer-events: none; cursor: default; }
.journey-station:focus-visible { outline: none; }
.journey-station:focus-visible > .journey-station-hit,
.journey-choice:focus-visible > rect,
.journey-alt:focus-visible > rect { stroke: #243047; stroke-width: 3; stroke-dasharray: 8 6; }
/* Beim Antippen wächst das Bild – das Bild selbst, nicht die Gruppe, die es
   aufstellt: ein transform-origin auf einer Gruppe mit transform-Attribut
   verschöbe die ganze Aufstellung. */
.journey-motif {
  transform-box: fill-box;
  transform-origin: center bottom;
  transition: transform 0.16s ease;
}
.journey-station.is-current:hover .journey-picture .journey-motif,
.journey-station.is-done:hover .journey-picture .journey-motif,
.journey-choice:hover .journey-motif, .journey-alt:hover .journey-motif { transform: scale(1.06); }
.journey-choice { transition: opacity 0.3s ease; }
.journey-choice.is-dropped { opacity: 0; }

/* Der Nebel: über allem, was noch nicht dran ist; hebt sich und treibt weg.
   Blass, aber ohne Filter: ein filter auf einem SVG-Teil ist eine eigene
   Zwischenfläche auf der Grafikkarte, je Station eine. */
.journey-station .journey-picture,
.journey-station .journey-choice { transition: opacity 0.6s ease; }
.journey-station.is-fog .journey-picture,
.journey-station.is-fog .journey-choice { opacity: 0.38; }
.journey-fog { transition: opacity 0.9s ease, transform 0.9s ease; pointer-events: none; }
.journey-station:not(.is-fog) .journey-fog { opacity: 0; transform: translate(60px, -12px); }

/* Die Nummer unter dem Gleis: das Ziel für den Finger. An der Station, die
   dran ist, pulst ein grüner Ring darum; die anderen tragen ihn unsichtbar
   mit, damit nichts springt, wenn der Zug weiterfährt. Im Nebel wird die
   Scheibe grau: da gibt es noch nichts zu tippen. */
.journey-ruler { position: absolute; width: 0; height: 0; overflow: hidden; pointer-events: none; }
.journey-number { pointer-events: none; }
.journey-number-disc { transition: fill 0.5s ease; }
/* Offen sind immer zwei Stationen: beide tragen den grünen Ring, die mit dem
   Zug pulst dazu. Gestempelt braucht keinen Ring mehr. */
.journey-number-ring { opacity: 0; transition: opacity 0.3s ease; }
.journey-station.is-open .journey-number-ring { opacity: 0.9; }
.journey-station.is-current .journey-number-ring { opacity: 1; }
.journey-station.is-done .journey-number-ring { opacity: 0; }
.journey-station.is-fog .journey-number-disc { fill: #9aa4b2; }
.journey-station.is-fog .journey-number { opacity: 0.7; }
/* Der Schatten unter der Scheibe ist gezeichnet, kein drop-shadow-Filter:
   der Filter hiesse eine eigene Zwischenfläche je offener Station. */
.journey-number-shadow { opacity: 0; transition: opacity 0.3s ease; }
.journey-station.is-open .journey-number-shadow { opacity: 0.2; }
/* Das Pulsieren an der Station, die dran ist, ist ein eigener Ring ganz
   zuoberst auf der Karte (train-journey.js, layers.marker) – nicht der Ring
   der Station selbst. Der Browser legt jedes animierte SVG-Teil auf eine
   eigene Ebene, und alles, was danach gezeichnet wird und es überlappt,
   gleich mit: ein pulsierender Ring mitten in der Karte riss so Nummer,
   Stempel, Zug und Berge in weitere Ebenen. Zuoberst überlappt ihn nichts. */
.journey-pulse {
  transform-box: fill-box;
  transform-origin: center;
  animation: journey-pulse 1.9s ease-in-out infinite;
  pointer-events: none;
}
.journey-pulse.is-hidden { opacity: 0; animation: none; }

/* Ist die Station geschafft, tritt der Stempel an die Stelle der Zahl. Zwei
   Zeichen nebeneinander waren eines zu viel. */
.journey-number-text, .journey-number-disc { transition: opacity 0.35s ease; }
.journey-station.is-done .journey-number-text,
.journey-station.is-done .journey-number-disc { opacity: 0; }
.journey-stamp-slot { pointer-events: none; }

/* Der Stempel fällt von oben auf die Station und prallt kurz zurück. */
.journey-stamp { transform-box: fill-box; transform-origin: center; }
.journey-stamp.is-new { animation: journey-stamp-in 0.55s cubic-bezier(0.2, 1.6, 0.4, 1) backwards; }
@keyframes journey-stamp-in {
  from { transform: scale(2.6) rotate(-30deg); opacity: 0; }
  60% { opacity: 1; }
  to { transform: scale(1) rotate(-14deg); opacity: 1; }
}

/* Das Signal an der aktuellen Station pulst wie das Startsignal. */
.journey-signal { transition: opacity 0.3s ease; }
.journey-signal.is-hidden { opacity: 0; }
.journey-signal-ring {
  transform-box: fill-box;
  transform-origin: center;
  animation: journey-pulse 2.2s ease-in-out infinite;
}
/* Unsichtbar pulst der Ring nicht weiter: während der Feier – Stempel, Legen,
   Fahrt – ist das Signal weg, und eine Animation, die niemand sieht, kostet
   auf einem Tablet trotzdem Bilder. Ganz weg, nicht angehalten: auch eine
   angehaltene Animation hält ihre Ebene auf der Grafikkarte. */
.journey-signal.is-hidden .journey-signal-ring { animation: none; }
@keyframes journey-pulse {
  0%, 100% { transform: scale(1); opacity: 0.55; }
  50% { transform: scale(1.4); opacity: 0.15; }
}

/* Das Gleis, das noch nicht gelegt ist, bleibt blass. */
.journey-rails-dim { opacity: 0.42; }

/* --- Ruhe auf der Karte ---
   Der Browser legt jedes SVG-Teil mit laufender transform- oder
   opacity-Animation auf eine eigene Ebene der Grafikkarte – und alles, was
   danach gezeichnet wird und es überlappt, auf weitere. Auf der Karte kamen
   so an die hundert Ebenen zusammen (die kleinen Bewegungen in jedem
   Stationsbild, der Dampf, das Wasser, die Mühle), zusammen ein Vielfaches
   des Bildschirms an Texturfläche. Ein Tablet mit hoher Auflösung
   (Xiaomi Pad 7) kam damit nicht mehr nach: Stationen, Stempel und Knöpfe
   fielen bildweise aus, die Karte flackerte. Deshalb steht die Karte im
   Stand still. Es bewegt sich nur, was zeigt, wo es weitergeht (der Ring
   und das Signal, beide zuoberst), und was gerade geschieht: die Fahrt, der
   Stempel, die Feier am Ziel. */
.journey-map .gi { animation: none; }

/* Der kleine Zug: Räder drehen und der Dampf stösst nur in der Fahrt. Beides
   bewegt train-journey.js mit der Fahrt selbst, über Attribute – keine
   CSS-Animation, keine Ebene. Im Stand steht der Dampf als kleine Wolke am
   Kamin, wie bei weniger Bewegung. */
.journey-map .train-wheel { animation: none; }
.journey-map .train-steam-puff { animation: none; opacity: 0.5; }

/* Die Karte lebt bei der Ankunft: das Wasser glitzert, die Mühle dreht sich
   schnell, das Licht geht an, der Leuchtturm blinkt, die Sterne funkeln.
   Im Stand steht das alles still (siehe oben). Der Rauch der Fähre steigt
   auch, solange sie fährt. */
.is-party .journey-water { animation: journey-wave 7s linear infinite; }
@keyframes journey-wave { from { transform: translateX(0); } to { transform: translateX(60px); } }
.journey-blades { transform-box: fill-box; transform-origin: center; }
.is-party .journey-blades { animation: journey-spin 2.2s linear infinite; }
@keyframes journey-spin { to { transform: rotate(1turn); } }
.is-party .journey-beam { animation: journey-beam 0.9s ease-in-out infinite alternate; }
@keyframes journey-beam { from { opacity: 0.15; } to { opacity: 0.55; } }
.is-party .journey-star { animation: journey-twinkle 2.6s ease-in-out infinite; }
.is-party .journey-star:nth-child(odd) { animation-delay: 1.1s; }
@keyframes journey-twinkle { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }
.journey-smoke { transform-box: fill-box; transform-origin: center; }
.is-party .journey-smoke, .is-sailing .journey-ferry .journey-smoke { animation: journey-smoke 3.4s ease-out infinite; }
.is-sailing .journey-ferry .journey-smoke { animation-duration: 2.6s; }
@keyframes journey-smoke {
  0% { transform: translate(0, 0) scale(0.6); opacity: 0; }
  20% { opacity: 0.9; }
  100% { transform: translate(14px, -34px) scale(1.6); opacity: 0; }
}
.is-party .journey-lantern, .is-party .journey-window { animation: journey-glow 0.9s ease-in-out infinite alternate; }

/* Das Schaufenster: das Schloss fällt ab, dahinter leuchtet es. */
.journey-lock { transition: opacity 0.5s ease, transform 0.5s ease; transform-box: fill-box; transform-origin: center; }
.journey-showcase:not(.is-locked) .journey-lock { opacity: 0; transform: translate(0, 30px) rotate(40deg); }
.journey-glow { transition: opacity 0.6s ease; }
.journey-showcase:not(.is-locked) .journey-glow { opacity: 0.8; }
.is-party .journey-showcase:not(.is-locked) .journey-glow { animation: journey-glow 1.4s ease-in-out infinite alternate; }
@keyframes journey-glow { from { opacity: 0.45; } to { opacity: 0.95; } }

/* Die zehn Punkte der Karte, oben in der Mitte – wie die Schrittpunkte des
   Wagens: voll, wenn gestempelt, golden, wenn die Runde gut war. */
/* Ein Knopf: ein Tipp darauf öffnet den Fahrplan mit allen sechs Karten. Das
   Zeichen der Reise davor sagt das ohne Wort. */
.journey-dots {
  position: absolute;
  top: calc(var(--safe-top) + 20px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px 7px 10px;
  border: 3px solid rgba(255, 255, 255, 0.9);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.72);
  box-shadow: 0 3px 10px rgba(36, 48, 71, 0.18);
  cursor: pointer;
  pointer-events: auto;
  font: inherit;
  color: #2b5fb3;
  transition: transform 0.15s ease;
}
.journey-dots:hover { transform: translateX(-50%) translateY(-2px); }
.journey-dots:active { transform: translateX(-50%) scale(0.96); }
.journey-dots:focus-visible { outline: 3px solid #243047; outline-offset: 3px; }
.journey-dots-glyph { width: 30px; height: 20px; margin-right: 4px; display: block; }
.journey-dot {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.85);
  border: 2px solid rgba(36, 48, 71, 0.28);
  transition: background 0.3s ease, border-color 0.3s ease;
}
.journey-dot.is-done { background: #2f9e44; border-color: #2f9e44; }
.journey-dot.is-gold { background: #f0b429; border-color: #f0b429; }
.journey-dot.is-pushed { background: #8c93a1; border-color: #8c93a1; }
@media (max-height: 600px) {
  .journey-dots { top: calc(var(--safe-top) + 12px); padding: 5px 10px 5px 8px; gap: 5px; }
  .journey-dot { width: 11px; height: 11px; }
  .journey-dots-glyph { width: 24px; height: 16px; }
}

/* --- Der Fahrplan: alle sechs Karten --- */
/* Wie die Landschaftswahl: ein dunkler Vorhang, darauf je Karte ein Bild mit
   dem Wahrzeichen, zehn Punkte, der Name und die Belohnung. Die eigene Karte
   trägt die Lok, fertige Karten sind antippbar, kommende liegen im Nebel. */
.journey-plan {
  position: absolute;
  inset: 0;
  z-index: 100;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Zentriert, solange es passt – und scrollbar von oben, wenn zwei Reihen
     nicht mehr passen (ein einfaches center schnitte die obere ab). */
  justify-content: safe center;
  gap: 10px;
  padding: calc(var(--safe-top) + 14px) calc(var(--safe-right) + 16px) calc(var(--safe-bottom) + 12px) calc(var(--safe-left) + 16px);
  overflow: auto;
  background: rgba(23, 34, 42, 0.55);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  animation: scene-picker-in 0.22s ease both;
}
.journey-plan-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: stretch;
  gap: clamp(8px, 1.6vw, 16px);
  max-width: min(100%, 1000px);
  padding: 22px 8px 6px;
}
.journey-plan-card {
  position: relative;
  width: clamp(104px, 14.5vw, 156px);
  padding: 6px 6px 8px;
  border: 4px solid rgba(255, 255, 255, 0.9);
  border-radius: 16px;
  background: #fff;
  color: #243047;
  cursor: pointer;
  display: grid;
  gap: 5px;
  justify-items: center;
  font: inherit;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.28);
  transition: transform 0.15s ease, border-color 0.15s ease;
}
.journey-plan-card:hover:not(:disabled) { transform: translateY(-3px); }
.journey-plan-card:active:not(:disabled) { transform: scale(0.96); }
.journey-plan-card:focus-visible { outline: 3px solid #ffd166; outline-offset: 3px; }
.journey-plan-card.is-current { border-color: #ffd166; }
.journey-plan-card.is-golden { border-color: #f0b429; box-shadow: 0 6px 18px rgba(240, 180, 41, 0.45); }
.journey-plan-card.is-locked { cursor: not-allowed; filter: grayscale(0.75); opacity: 0.55; }
.journey-plan-art { width: 100%; height: auto; display: block; border-radius: 10px; }
/* Die zehn Stempel als Zehnerfeld: zwei Reihen zu fünf. So passen sie auch
   auf eine schmale Karte, und ein Kind sieht "fünf und noch drei". */
.journey-plan-dots { display: grid; grid-template-columns: repeat(5, 10px); gap: 3px 4px; justify-content: center; }
.journey-plan-dots span {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #e6ebf2;
  border: 1.5px solid rgba(36, 48, 71, 0.25);
}
.journey-plan-dots .is-done { background: #2f9e44; border-color: #2f9e44; }
.journey-plan-dots .is-gold { background: #f0b429; border-color: #f0b429; }
.journey-plan-name { font-weight: 900; font-size: 0.86rem; letter-spacing: 0.01em; max-width: 100%; text-align: center; line-height: 1.15; }
.journey-plan-reward { position: relative; width: 46px; height: 40px; display: block; }
.journey-plan-reward svg, .journey-plan-bonus svg { width: 100%; height: 100%; display: block; }
.journey-plan-reward .journey-reward-scene { border-radius: 8px; }
.journey-plan-lock {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 52%;
  height: auto;
  color: #ffffff;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.55));
}
/* Die eigene Karte trägt die Lok oben auf dem Rand – über der eigenen
   Karte, nicht über der Nachbarin. */
.journey-plan-here {
  position: absolute;
  top: -22px;
  left: 50%;
  transform: translateX(-50%);
  width: 52px;
  height: 46px;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.35));
}
.journey-plan-here svg { width: 100%; height: 100%; display: block; }
.journey-plan-foot {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 7px 16px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.92);
  color: #243047;
  font-weight: 800;
  font-size: 0.9rem;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.22);
}
.journey-plan-bonus { position: relative; width: 40px; height: 34px; display: block; filter: grayscale(0.8); opacity: 0.7; }
.journey-plan-bonus.is-open { filter: none; opacity: 1; }
/* Reise 2: eine zweite Reihe mit goldenem Rahmen und einem Schild darüber.
   Zwei Reihen brauchen kleinere Karten, damit beide ins Bild passen. */
.journey-plan-row.is-lap2 { position: relative; padding-top: 28px; }
.journey-plan-row { flex-shrink: 0; }
.journey-plan.has-lap2 .journey-plan-row { gap: 8px; padding-top: 14px; padding-bottom: 2px; }
.journey-plan.has-lap2 .journey-plan-row.is-lap2 { padding-top: 26px; }
.journey-plan.has-lap2 .journey-plan-card { width: clamp(84px, 9.6vw, 112px); padding: 4px 4px 6px; gap: 3px; }
.journey-plan.has-lap2 .journey-plan-name { font-size: 0.7rem; }
.journey-plan.has-lap2 .journey-plan-reward { width: 34px; height: 30px; }
.journey-plan.has-lap2 .journey-plan-dots { grid-template-columns: repeat(5, 8px); gap: 2px 3px; }
.journey-plan.has-lap2 .journey-plan-dots span { width: 8px; height: 8px; }
.journey-plan.has-lap2 .journey-plan-here { width: 40px; height: 34px; top: -16px; }
.journey-plan.has-lap2 .journey-plan-foot { font-size: 0.8rem; padding: 5px 12px; }
.journey-plan-lap {
  position: absolute;
  top: 2px;
  left: 50%;
  transform: translateX(-50%);
  padding: 3px 14px;
  border-radius: 999px;
  background: #f0b429;
  color: #3b2a00;
  font-weight: 900;
  font-size: 0.8rem;
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.25);
}
.journey-plan-card.is-lap2 { border-color: #d9a520; }
.journey-plan-card.is-lap2.is-current { border-color: #ffd166; }
.journey-plan-card.is-lap2.is-golden { border-color: #f0b429; }
.journey-plan-dots .is-pushed { background: #8c93a1; border-color: #8c93a1; }
@media (max-height: 600px) {
  .journey-plan { gap: 6px; }
  .journey-plan-row { gap: 8px; padding-top: 18px; }
  .journey-plan-card { width: clamp(92px, 13vw, 128px); padding: 4px 4px 6px; gap: 3px; }
  .journey-plan-name { font-size: 0.72rem; }
  .journey-plan-reward { width: 36px; height: 32px; }
  .journey-plan-dots { grid-template-columns: repeat(5, 9px); gap: 2px 3px; }
  .journey-plan-dots span { width: 9px; height: 9px; }
  .journey-plan-here { width: 42px; height: 36px; top: -18px; }
  .journey-plan-foot { font-size: 0.8rem; padding: 5px 12px; }
}

/* --- Der Fahrgast --- */
/* Auf dem Bahnsteig, bis der erste Wagen vorbeikommt: dann springt er auf
   (die Figur hüpft weg, der Kopf erscheint im Wagen). Zu Hause winkt er, und
   im Wahrzeichen geht ein Licht an. Die äusseren Gruppen tragen die
   Aufstellung, animiert werden nur die inneren. */
.journey-passenger-stand { transform-box: fill-box; transform-origin: 50% 100%; transition: opacity 0.35s ease, transform 0.35s ease; }
.journey-passenger-stand.is-boarding { opacity: 0; transform: translateY(-26px) scale(0.8); }
.journey-passenger-stand.is-arriving { animation: journey-arrive 0.6s cubic-bezier(0.2, 1.4, 0.4, 1) backwards; }
@keyframes journey-arrive { from { opacity: 0; transform: translateY(-30px) scale(0.6); } to { opacity: 1; transform: none; } }
.journey-passenger-arm { transform-box: fill-box; transform-origin: 0% 100%; }
/* Gewinkt wird bei der Ankunft; danach steht er ruhig vor seinem Haus. */
.is-party .journey-passenger.is-waving .journey-passenger-arm { animation: journey-wave-arm 0.9s ease-in-out infinite alternate; }
@keyframes journey-wave-arm { from { transform: rotate(-18deg); } to { transform: rotate(22deg); } }
.journey-aboard { opacity: 0; transform: translateY(12px); transform-box: fill-box; transform-origin: 50% 100%; transition: opacity 0.3s ease, transform 0.4s cubic-bezier(0.2, 1.4, 0.4, 1); }
.journey-aboard.is-aboard { opacity: 1; transform: none; }
.journey-home-light { transition: fill 0.6s ease; }
.journey-landmark.is-lit .journey-home-light { fill: #ffe066; }
.is-party .journey-landmark.is-lit .journey-home-light { animation: journey-glow 1.6s ease-in-out infinite alternate; }

/* --- Die Strecke --- */
/* Der Scheinwerferkegel vor der Lok: nur in der Nacht und im All, nur in der
   Fahrt. Der Tunnelmund leuchtet dort auch. Glühwürmchen schweben und
   blinken. Auf der Zahnradstrecke drehen die Räder langsamer. */
.journey-headlight { opacity: 0; transition: opacity 0.5s ease; }
.journey[data-night] .journey-train.is-driving .journey-headlight { opacity: 0.55; }
.is-party .journey-portal-glow { animation: journey-glow 2.4s ease-in-out infinite alternate; }
/* Die Glühwürmchen schweben immer – sie liegen zuoberst auf der Karte
   (layers.top), wo sie nichts überlappen und darum nur sich selbst kosten. */
.journey-firefly { animation: journey-firefly 4s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
@keyframes journey-firefly {
  0%, 100% { opacity: 0.2; transform: translate(0, 0); }
  30% { opacity: 1; }
  50% { opacity: 0.4; transform: translate(10px, -14px); }
  80% { opacity: 0.9; transform: translate(-6px, -6px); }
}
/* Die Rakete hebt bei der Ankunft ab, mit Flamme. */
.journey-flame { opacity: 0; transform-box: fill-box; transform-origin: 50% 0; }
.journey-rocket { transform-box: fill-box; transform-origin: 50% 100%; }
.is-party .journey-rocket { animation: journey-liftoff 2.6s ease-in both; }
.is-party .journey-flame { opacity: 1; animation: journey-flame 0.18s ease-in-out infinite alternate; }
@keyframes journey-liftoff { 0% { transform: translateY(0); } 30% { transform: translateY(-4px); } 100% { transform: translateY(-150px); opacity: 0; } }
@keyframes journey-flame { from { transform: scaleY(0.8); } to { transform: scaleY(1.25); } }

/* Konfetti an einer Station: ein kleiner Kasten über ihr, in den kids.js
   seine Schnipsel streut. */
.journey-burst { position: absolute; pointer-events: none; z-index: 6; }

/* Die Feier am Ziel-Bahnhof: dieselbe Tafel wie beim Wagen, statt des Wagens
   die Belohnung – die eigene Lok mit dem neuen Teil, oder die Landschaft. */
.journey-reward .wagon-reward-card { --reward-color: #2b5fb3; }
.journey-reward-pic {
  grid-area: bild;
  width: clamp(150px, 30vw, 320px);
  display: grid;
  place-items: center;
}
.journey-reward-pic svg { display: block; width: 100%; height: auto; animation: scene-reward-in 0.5s ease backwards; }
.journey-reward-pic .journey-reward-scene { border-radius: 20px; box-shadow: 0 10px 24px rgba(31, 42, 51, 0.28); }

/* --- Die Werkstatt: neu freigeschaltete Teile --- */
/* Was die Reise gebracht hat, muss auffallen, sonst sucht ein Kind es nie:
   goldener Rahmen, goldener Stern in der Ecke, ein ruhiges Pulsieren. */
/* Der Stern sitzt aussen an der Ecke: dafür muss der Knopf ihn herauslassen. */
.loco-choice.is-new { position: relative; overflow: visible; }
/* In der Landschaftswahl bleibt das Bild beschnitten – dort sitzt er innen. */
.scene-choice .loco-new { top: 4px; right: 4px; }
.loco-choice.is-new,
.scene-choice.is-new {
  border-color: #f0b429;
  box-shadow: 0 0 0 4px rgba(240, 180, 41, 0.35), 0 6px 16px rgba(240, 180, 41, 0.4);
  animation: loco-new-pulse 1.8s ease-in-out infinite;
}
@keyframes loco-new-pulse {
  0%, 100% { box-shadow: 0 0 0 4px rgba(240, 180, 41, 0.32), 0 6px 16px rgba(240, 180, 41, 0.32); }
  50% { box-shadow: 0 0 0 7px rgba(240, 180, 41, 0.5), 0 8px 22px rgba(240, 180, 41, 0.5); }
}
.loco-new {
  position: absolute;
  top: -8px;
  right: -8px;
  width: 24px;
  height: 24px;
  color: #f0b429;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.45));
  pointer-events: none;
  animation: loco-new-star 2.4s ease-in-out infinite;
}
.loco-new svg { width: 100%; height: 100%; display: block; }
@keyframes loco-new-star {
  0%, 100% { transform: rotate(-8deg) scale(1); }
  50% { transform: rotate(8deg) scale(1.15); }
}
/* Der Punkt an der Lok, wo etwas Neues steckt. */
.loco-hotspot.is-new .loco-hotspot-halo { animation: journey-glow 1.4s ease-in-out infinite alternate; }
.loco-hotspot.is-new .loco-hotspot-dot { animation: loco-new-star 2.4s ease-in-out infinite; transform-box: fill-box; transform-origin: center; }
/* Und der Funke am Zug auf dem Startbild. */
.loco-sparkle { transform-box: fill-box; transform-origin: center; animation: loco-new-star 2.2s ease-in-out infinite; }
/* Der Landschafts-Knopf trägt seinen Stern aussen. */
.scene-button { position: relative; }
.scene-button .loco-new { top: -6px; right: -6px; width: 22px; height: 22px; }

/* --- Die Werkstatt: gesperrte Teile --- */
/* Was die Reise erst freischaltet, steht mit Schloss da – zu sehen, nicht zu
   wählen. Dieselbe Sprache wie bei den gesperrten Landschaften. */
.loco-choice.is-locked {
  position: relative;
  cursor: not-allowed;
  filter: grayscale(0.7);
  opacity: 0.62;
}
.loco-choice.is-locked:hover { transform: none; }
.loco-lock {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 38%;
  height: auto;
  color: #ffffff;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.55));
}

/* --- In den Spielen: der Auftrag --- */
/* Oben rechts steht bei einer Zielpunktzahl das Ziel dabei: "3 / 5", mit dem
   Fähnchen des Auftrags statt des Hakens. */
.cm-count.has-target .cm-count-tick { color: #f0b429; }
.cm-count-target { font-size: 0.72em; opacity: 0.8; margin-left: 2px; font-variant-numeric: tabular-nums; }
.cm-journey {
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-size: 0.98rem;
  font-weight: 800;
  color: #4a5b52;
}
.cm-journey.is-done { color: #2f6f4f; }
.cm-journey-icon { width: 22px; height: 22px; flex: 0 0 auto; color: currentColor; }
/* Der Weg zurück auf die Karte pulst, wenn der Stempel wartet. */
.cm-icon.is-primary { box-shadow: 0 0 0 4px rgba(63, 191, 116, 0.4); animation: cm-pulse 2.1s ease-in-out infinite; }
.success-journey, .runner-journey { font-weight: 800; color: #2f6f4f; }
.success-journey.is-missing { color: #b53a28; }

@media (prefers-reduced-motion: reduce) {
  .journey-sparkle, .journey-stamp.is-new, .journey-signal-ring, .journey-water, .journey-blades, .journey-beam,
  .journey-star, .journey-smoke, .is-party .journey-lantern, .is-party .journey-window,
  .journey-showcase:not(.is-locked) .journey-glow, .journey-reward-pic svg, .cm-icon.is-primary,
  .journey.is-entering .journey-map, .journey.is-switching .journey-map { animation: none; }
  .journey-train.is-driving .train-steam-puff { animation: none; }
  .journey-pulse, .journey-firefly, .is-party .journey-portal-glow, .is-sailing .journey-ferry .journey-smoke,
  .is-party .journey-passenger.is-waving .journey-passenger-arm, .is-party .journey-landmark.is-lit .journey-home-light { animation: none; }
  .journey-fog, .journey-station .journey-picture, .journey-station .journey-choice, .journey-signal, .journey-lock, .journey-glow, .journey-dot, .journey-motif, .journey-number-disc, .journey-number-ring { transition: none; }
  .journey-sparkle { opacity: 0.9; }
  .journey-showcase:not(.is-locked) .journey-glow { opacity: 0.7; }
  .journey-plan { animation: none; }
  .journey-plan-card, .journey-dots { transition: none; }
  .loco-choice.is-new, .scene-choice.is-new, .loco-new, .loco-sparkle,
  .loco-hotspot.is-new .loco-hotspot-halo, .loco-hotspot.is-new .loco-hotspot-dot,
  .journey-passenger.is-waving .journey-passenger-arm, .journey-firefly, .journey-portal-glow, .journey-landmark.is-lit .journey-home-light,
  .is-party .journey-rocket, .is-party .journey-flame, .journey-passenger-stand.is-arriving, .journey-pusher.is-driving .train-wheel { animation: none; }
  .journey-passenger-stand, .journey-aboard, .journey-home-light, .journey-headlight { transition: none; }
  .is-party .journey-flame { opacity: 1; }
}

/* ===========================================================================
   Kein Fokusrahmen nach Tipp oder Klick
   ---------------------------------------------------------------------------
   Die Gegenprobe zur Regel ganz oben, und der Grund, warum sie hier unten
   steht: sie muss die eigenen :focus-visible-Rahmen der App überstimmen, und
   das geht nur nach ihnen. Womit zuletzt bedient wurde, schreibt kids.js an
   das Wurzelelement – Tabulator und Pfeile setzen "tastatur", jeder Tipp und
   jeder Klick "zeiger". So hängt der Rahmen nicht daran, ob ein Browser bei
   einer SVG-Gruppe von sich aus zwischen Finger und Tastatur unterscheidet.

   :root zählt für die Gewichtung wie eine Klasse; zusammen mit dem
   Attribut und :focus wiegt jede Regel hier schwerer als die Rahmen, die sie
   abschaltet.
   =========================================================================== */
:root[data-eingabe="zeiger"] *:focus,
:root[data-eingabe="zeiger"] *:focus-visible { outline: none; }
/* Der gestrichelte Rahmen der Reisekarte ist kein outline, sondern ein Strich
   am Rechteck – er braucht seine eigene Zeile. */
:root[data-eingabe="zeiger"] .journey-station:focus-visible > .journey-station-hit,
:root[data-eingabe="zeiger"] .journey-choice:focus-visible > rect,
:root[data-eingabe="zeiger"] .journey-alt:focus-visible > rect { stroke: none; }
/* Das Tor: der Zug vor der Schranke. Für das Kind ein Bild und ein Satz, für
   die Eltern ein Knopf, dahinter das Rechenrätsel. Liegt wie eine Feier über
   der Bühne – oder, auf einer Spielseite, über dem ganzen Bild. */
.tor-overlay {
  position: fixed;
  inset: 0;
  z-index: 110;
  display: grid;
  place-items: center;
  padding: calc(var(--safe-top, 0px) + 12px) calc(var(--safe-right, 0px) + 12px) calc(var(--safe-bottom, 0px) + 12px) calc(var(--safe-left, 0px) + 12px);
  background: rgba(23, 34, 42, 0.5);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  animation: wagon-reward-in 0.24s ease both;
}
.train-stage .tor-overlay { position: absolute; }
.tor-card {
  width: min(560px, 100%);
  max-height: 100%;
  overflow: auto;
  display: grid;
  gap: clamp(8px, 1.8vh, 14px);
  justify-items: center;
  padding: clamp(12px, 3vh, 24px) clamp(14px, 3vw, 28px);
  border-radius: 28px;
  background: #fff;
  box-shadow: 0 22px 60px rgba(31, 42, 51, 0.4);
  animation: wagon-reward-pop 0.34s cubic-bezier(0.2, 1.3, 0.4, 1) both;
  text-align: center;
}
/* Das Kreuz oben rechts. Die Karte wird dafür position: relative – sonst
   klebte es am Bildschirmrand statt an der Karte. Es bleibt in jedem Zustand
   sichtbar, auch während des Rechenrätsels: Wer sich auf "Für Eltern"
   vertippt, muss ohne Neuladen wieder herauskommen. */
.tor-card { position: relative; }
.tor-schliessen {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 40px;
  height: 40px;
  padding: 0;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: #f1eefc;
  color: #5b6b7a;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.tor-schliessen svg { width: 22px; height: 22px; }
.tor-schliessen:hover,
.tor-schliessen:focus-visible { background: #e2dcf8; color: #2d3a45; }
/* Nicht unter das Kreuz laufen: Die Karte ist eng, und der Text steht mittig. */
.tor-card > .tor-bild { margin-top: 6px; }

.tor-bild { width: min(320px, 70vw); }
.tor-bild-svg { width: 100%; height: auto; display: block; }
.tor-text {
  margin: 0;
  font-size: clamp(1.05rem, 2.6vw, 1.35rem);
  font-weight: 900;
  color: #2d3a45;
  line-height: 1.35;
  max-width: 26ch;
}
.tor-actions { display: flex; gap: 12px; align-items: center; }
.tor-zurueck {
  width: clamp(52px, 12vh, 64px);
  height: clamp(52px, 12vh, 64px);
  padding: 0;
  border: 3px solid #fff;
  border-radius: 50%;
  background: #6c5ce7;
  color: #fff;
  display: grid;
  place-items: center;
  cursor: pointer;
  box-shadow: 0 8px 20px rgba(36, 48, 71, 0.26);
}
.tor-zurueck svg { width: 55%; height: 55%; }
.tor-eltern {
  border: 0;
  border-radius: 999px;
  padding: 12px 20px;
  min-height: 46px;
  background: #eef1f5;
  color: #2d3a45;
  font: inherit;
  font-weight: 800;
  cursor: pointer;
}
.tor-gate { display: grid; gap: 10px; justify-items: center; width: 100%; }
.tor-gate-text { margin: 0; font-weight: 800; color: #2d3a45; }
.tor-gate-row { display: flex; gap: 8px; }
.tor-gate-row input {
  width: 110px;
  min-height: 46px;
  border-radius: 12px;
  border: 2px solid #d9d4f5;
  padding: 0 12px;
  font: inherit;
  font-weight: 900;
  font-size: 1.2rem;
  text-align: center;
}
.tor-gate-row button {
  border: 0;
  border-radius: 999px;
  padding: 0 20px;
  min-height: 46px;
  background: #3fbf74;
  color: #fff;
  font: inherit;
  font-weight: 800;
  cursor: pointer;
}
.tor-gate-status { margin: 0; min-height: 1.3em; color: #b0002a; font-weight: 800; }
body.tor-offen { overflow: hidden; }

/* Ein Level, das zu ist, weil es gekauft werden muss: nicht ausgegraut wie
   eines, dem noch das Level davor fehlt, sondern mit goldenem Schloss – ein
   Tipp darauf zeigt das Tor. */
.difficulty-card.locked:not(:disabled),
.level-tile.locked:not(:disabled) {
  opacity: 0.85;
  cursor: pointer;
}


/* ===========================================================================
   Der Adminbereich als eigene Seite (admin.html)
   ===========================================================================
   Die Regeln oben zeichnen den Adminbereich, wie er im Profilfenster stand:
   in einem Panel, das höchstens 1120 px breit ist. Diese hier setzen ihn auf
   eine eigene Seite – mehr Platz, ein Kopf, Reiter über die ganze Breite.
   Alles, was dazwischen gleich aussieht (Konten-Akkordeon, Zug-Balken,
   Level-Karten), kommt aus denselben Regeln: Es ist dieselbe Ansicht, nur an
   einem anderen Ort.

   Diese Seite sieht kein Kind. Deshalb darf hier Text stehen, und deshalb ist
   sie auf Lesen ausgelegt statt auf grosse Finger. */
body[data-page="admin"] {
  margin: 0;
  min-height: 100vh;
  background: var(--bg, #f7f3ff);
  color: var(--ink, #1f2a33);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  overflow: auto;
}

/* Rechts oben schweben auf jeder Seite der App zwei Knöpfe: der Lautsprecher
   und das Profil. Der Lautsprecher liest Kindern vor – hier gibt es kein Kind
   und nichts vorzulesen, also weg damit. Das Profil bleibt: darüber meldet
   man sich ab. Der Kopf macht ihm Platz, statt sich von ihm überdecken zu
   lassen. */
body[data-page="admin"] .sound-toggle { display: none; }

.admin-kopf {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px 24px;
  width: min(1400px, 100% - 32px);
  margin: 0 auto;
  padding: 24px 0 8px;
}

.admin-kopf-nav { padding-right: 68px; }

.admin-kopf h1 {
  margin: 0;
  font-size: clamp(1.6rem, 3vw, 2.4rem);
  font-weight: 950;
  letter-spacing: -0.01em;
}

.admin-kopf-nav {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

/* Fünf Zahlen, nicht vier: Die feste Vierspaltigkeit des Streifens liesse die
   fünfte allein in einer zweiten Reihe stehen. */
body[data-page="admin"] .stat-strip {
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}

.admin-wer {
  font-weight: 800;
  color: var(--muted, #5b6b7a);
  font-size: 0.95rem;
}

.admin-kopf-nav a {
  display: inline-flex;
  align-items: center;
  min-height: 42px;
  padding: 0 18px;
  border-radius: 999px;
  border: 2px solid var(--line, #e4e0f7);
  background: #fff;
  color: var(--primary-dark, #4c3fd6);
  font-weight: 800;
  text-decoration: none;
}

.admin-seite {
  width: min(1400px, 100% - 32px);
  margin: 0 auto;
  padding: 8px 0 64px;
  display: grid;
  gap: 16px;
}

.admin-seite .admin-inhalt {
  display: grid;
  gap: 16px;
  min-width: 0;
}

/* Die Reiter: eine Pillenleiste, die oben stehen bleibt, mit dem
   Aktualisieren-Knopf am rechten Rand. */
.admin-reiter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 6px;
  border-radius: 999px;
  background: #fff;
  border: 2px solid var(--line, #e4e0f7);
  position: sticky;
  top: 8px;
  z-index: 3;
  box-shadow: 0 8px 24px rgba(50, 44, 109, 0.08);
}

.admin-reiter button {
  min-height: 40px;
  padding: 0 18px;
  border: 2px solid transparent;
  border-radius: 999px;
  background: transparent;
  color: var(--primary-dark, #4c3fd6);
  font: inherit;
  font-weight: 800;
  cursor: pointer;
}

.admin-reiter button.active,
.admin-reiter button:hover,
.admin-reiter button:focus-visible {
  background: var(--primary, #6c5ce7);
  color: #fff;
  outline: none;
}

.admin-reiter .admin-neu {
  margin-left: auto;
  background: #fbfaff;
  border-color: var(--line, #e4e0f7);
}

.admin-reiter .admin-neu:hover,
.admin-reiter .admin-neu:focus-visible {
  background: var(--primary, #6c5ce7);
  color: #fff;
}

.admin-hinweis {
  background: #fff;
  border: 2px solid var(--line, #e4e0f7);
  border-radius: 24px;
  padding: 28px;
  display: grid;
  gap: 12px;
  max-width: 640px;
}

.admin-hinweis h2 { margin: 0; }
.admin-hinweis p { margin: 0; }

.knopf {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 999px;
  min-height: 46px;
  padding: 0 24px;
  background: var(--primary, #6c5ce7);
  color: #fff;
  font: inherit;
  font-weight: 900;
  text-decoration: none;
  cursor: pointer;
  width: fit-content;
}

.knopf.zweit {
  background: #fff;
  color: var(--primary-dark, #4c3fd6);
  border: 2px solid var(--line, #e4e0f7);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --- Suchen, filtern, sortieren ---
   Eine Zeile über der Liste: Suchfeld links, die Filtergruppen daneben. Jede
   Gruppe ist eine Frage mit drei Antworten, und die gewählte ist gefüllt –
   so sieht man den Stand der Liste, ohne ihn zu lesen. */
.admin-werkzeug {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 14px;
  align-items: center;
}

.admin-suche { flex: 1 1 240px; min-width: 200px; }

.admin-suche input {
  width: 100%;
  min-height: 44px;
  padding: 0 16px;
  border-radius: 999px;
  border: 2px solid var(--line, #e4e0f7);
  background: #fff;
  font: inherit;
}

.admin-filtergruppe {
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  border-radius: 999px;
  background: #fff;
  border: 2px solid var(--line, #e4e0f7);
}

.admin-filtergruppe button {
  min-height: 34px;
  padding: 0 14px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--primary-dark, #4c3fd6);
  font: inherit;
  font-weight: 800;
  font-size: 0.9rem;
  cursor: pointer;
  white-space: nowrap;
}

.admin-filtergruppe button.active {
  background: var(--primary, #6c5ce7);
  color: #fff;
}

/* --- Marken in der Kontenzeile ---
   Zwei Wörter, die alles sagen, wonach hier gesucht wird: was für ein Konto
   das ist, und ob es bezahlt ist. Farbe statt Text, damit eine Liste von
   dreissig Konten auf einen Blick lesbar bleibt. */
.admin-marken {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: flex-start;
}

.admin-marke {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 999px;
  font-size: 0.74rem;
  font-weight: 900;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  white-space: nowrap;
  border: 2px solid transparent;
}

.admin-marke.rolle-admin { background: #322c6d; color: #fff; }
.admin-marke.rolle-eltern { background: #ece8ff; color: #4c3fd6; }
.admin-marke.rolle-kind { background: #e3f6ec; color: #1d7a45; }
.admin-marke.kauf-bezahlt { background: #3fbf74; color: #fff; }
.admin-marke.kauf-geschenk { background: #ffd166; color: #6b4e00; }
.admin-marke.kauf-gruender { background: #dff1fb; color: #0b5875; }
.admin-marke.kauf-zurueck { background: #ffe2e8; color: #b0002a; }
.admin-marke.kauf-gratis { background: #f1eff9; color: #5b6b7a; border-color: var(--line, #e4e0f7); }

/* ===========================================================================
   Der Reiter E-Mail
   ===========================================================================
   Oben die Weiterleitung als Karte, darunter dieselbe Liste zum Aufklappen
   wie bei Konten und Gästen – die Zeile ist nur anders gefüllt: Richtung,
   Betreff, Gegenüber, Art, Zeit. */
/* Wen eine Familienlöschung mit trifft: Die Namen stehen in der Rückfrage,
   damit niemand "Ja" tippt und danach sucht, wer weg ist. */
.admin-loesch-kinder {
  display: block;
  padding: 8px 12px;
  margin-top: 6px;
  border-radius: 12px;
  background: #fff1f4;
  border: 1px solid #ffb3c1;
  color: #7a0020;
}

.admin-mail {
  display: grid;
  gap: 14px;
}

.admin-mail-einstellungen {
  display: grid;
  gap: 12px;
  padding: 16px 18px;
  border-radius: 18px;
  background: #fff;
  border: 2px solid var(--line, #e4e0f7);
  box-shadow: 0 10px 22px rgba(50, 44, 109, 0.08);
}

.admin-mail-einstellungen header strong {
  display: block;
  font-size: 1.05rem;
  color: var(--ink, #243047);
}

.admin-mail-einstellungen header span {
  display: block;
  color: var(--muted, #5b6b7a);
  font-size: 0.86rem;
  font-weight: 700;
  line-height: 1.5;
  padding-top: 2px;
}

.admin-mail-schalter {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 800;
}

.admin-mail-schalter input[type="checkbox"] {
  width: 20px;
  height: 20px;
  accent-color: var(--primary, #6c5ce7);
}

/* Was fehlt, soll man sehen, ohne es zu suchen: Ohne Schlüssel bei Netlify
   geht nichts raus, ohne Geheimnis kommt nichts an. Beides steht hier – in
   Grün, wenn es da ist, sonst in Rot samt Namen der Variablen. */
.admin-mail-stand {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  padding-top: 4px;
  border-top: 2px solid var(--line, #e4e0f7);
  font-size: 0.82rem;
  font-weight: 800;
}

.admin-mail-stand span {
  display: grid;
  gap: 2px;
}

.admin-mail-stand b {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted, #5b6b7a);
}

.admin-mail-stand .is-ok { color: #0c8f6a; }
.admin-mail-stand .is-weg { color: #b0002a; }

.admin-mail-kopf {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  justify-content: space-between;
}

.admin-mail-richtung {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 999px;
  background: #ece8ff;
  color: #4c3fd6;
  font-size: 0.7rem;
  font-weight: 900;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  white-space: nowrap;
}

.admin-mail-richtung.ist-ein {
  background: #dff1fb;
  color: #0b5875;
}

.admin-mail-art {
  font-size: 0.78rem;
  font-weight: 800;
  color: var(--muted, #5b6b7a);
  white-space: nowrap;
}

/* Eine Mail, die nicht rausging, ist keine Zeile wie jede andere. */
.admin-mail-zeile.is-fehler {
  border-color: #ffb3c1;
  background: #fff7f9;
}

.admin-mail-zeile.is-fehler .admin-entry-zuletzt b { color: #b0002a; }

/* Der Inhalt so, wie er geschrieben wurde: Zeilenumbrüche bleiben, lange
   Adressen brechen um statt die Seite breit zu machen. */
.admin-mail-text {
  margin: 10px 0 0;
  padding: 12px 14px;
  border-radius: 14px;
  background: #f7f6fc;
  border: 1px solid var(--line, #e4e0f7);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.82rem;
  line-height: 1.55;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-height: 420px;
  overflow: auto;
}

.admin-entry-zuletzt {
  display: grid;
  gap: 2px;
  font-size: 0.85rem;
  font-weight: 800;
  white-space: nowrap;
}

.admin-entry-zuletzt b {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted, #5b6b7a);
}

/* Eine Gruppe, die der Admin von Hand gesetzt hat, sieht anders aus als eine,
   die von der Familie kommt: Sonst wäre nicht zu sehen, welche der Server
   beim nächsten Anmelden nachzieht und welche stehen bleibt. */
.admin-entry-group.ist-admin {
  background: #322c6d;
  color: #fff;
}

.admin-kauf strong { display: block; }

/* --- Welche Level probiert wurden ---
   Ein Punkt je Level, Spiel für Spiel. Die Frage, die eine Liste gelöster
   Level nicht beantwortet, ist die nach den ungelösten – und die beantwortet
   ein Raster in drei Farben auf einen Blick. */
.admin-abdeckung {
  display: grid;
  gap: 10px;
}

.admin-abdeckung h4 { margin: 0; }

.abdeckung-legende {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  margin: 0;
  font-weight: 800;
  font-size: 0.9rem;
}

.abdeckung-legende span { display: inline-flex; align-items: center; gap: 6px; }

.abdeckung-legende span::before {
  content: "";
  width: 12px;
  height: 12px;
  border-radius: 4px;
  background: currentColor;
}

.abdeckung-legende .ist-geloest { color: #2f9e5c; }
.abdeckung-legende .ist-probiert { color: #d98b00; }
.abdeckung-legende .ist-offen { color: #b7b2cc; }

.abdeckung-spiele {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 10px;
}

.abdeckung-spiele article {
  background: #fbfaff;
  border: 2px solid var(--line, #e4e0f7);
  border-radius: 16px;
  padding: 10px 12px;
  display: grid;
  gap: 8px;
}

.abdeckung-spiele header {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 0.9rem;
}

.abdeckung-spiele header span {
  font-weight: 900;
  color: var(--muted, #5b6b7a);
}

.abdeckung-punkte {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
}

.abdeckung-punkte i {
  width: 11px;
  height: 11px;
  border-radius: 3px;
  background: #e6e3f2;
}

.abdeckung-punkte i.ist-geloest { background: #2f9e5c; }
.abdeckung-punkte i.ist-probiert { background: #f0b429; }
.abdeckung-punkte i.ist-offen { background: #e6e3f2; }

.abdeckung-runden-titel { margin: 6px 0 0; }
.abdeckung-runden span.ist-offen { opacity: 0.55; }

/* --- Gruppen ---
   Wer sieht wessen Zug. Links die Gruppen, die es gibt; beim Ändern ein
   Editor mit Häkchen, nach Familien gebündelt – eine Gruppe ist eine Menge
   von Konten, und eine Menge hakt man an. */
.admin-gruppen {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 12px;
}

.admin-gruppe {
  background: #fff;
  border: 2px solid var(--line, #e4e0f7);
  border-radius: 20px;
  padding: 14px 16px;
  display: grid;
  gap: 10px;
  align-content: start;
}

.admin-gruppe.ist-admin { border-color: var(--primary, #6c5ce7); }
.admin-gruppe.ist-leer { background: #fbfaff; }

.admin-gruppe header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}

.admin-gruppe header span {
  font-size: 0.85rem;
  font-weight: 800;
  color: var(--muted, #5b6b7a);
}

.admin-gruppe ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 4px;
  font-weight: 700;
}

.admin-gruppe li small {
  color: var(--muted, #5b6b7a);
  font-weight: 800;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.admin-gruppen-kopf { justify-content: flex-start; }

.admin-gruppe-editor {
  background: #fff;
  border: 2px solid var(--line, #e4e0f7);
  border-radius: 24px;
  padding: 18px 20px;
  display: grid;
  gap: 14px;
}

.admin-gruppe-editor h3 { margin: 0; }

.admin-gruppe-name {
  display: grid;
  gap: 4px;
  max-width: 420px;
  font-weight: 800;
}

.admin-gruppe-name input {
  min-height: 44px;
  padding: 0 14px;
  border-radius: 14px;
  border: 2px solid var(--line, #e4e0f7);
  font: inherit;
}

.admin-familien {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 12px;
}

.admin-familie {
  border: 2px solid var(--line, #e4e0f7);
  border-radius: 16px;
  padding: 10px 14px 12px;
  margin: 0;
  display: grid;
  gap: 6px;
  align-content: start;
}

.admin-familie legend {
  font-weight: 900;
  padding: 0 6px;
}

.admin-familie legend label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
}

.admin-mitglied {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-weight: 700;
  cursor: pointer;
}

.admin-mitglied small {
  color: var(--muted, #5b6b7a);
  font-weight: 800;
  font-size: 0.72rem;
  text-transform: uppercase;
}

.admin-mitglied em {
  color: var(--primary-dark, #4c3fd6);
  font-size: 0.8rem;
  font-weight: 800;
}

.admin-familie input[type="checkbox"] {
  width: 20px;
  height: 20px;
  accent-color: var(--primary, #6c5ce7);
}

.admin-eigene-sets {
  display: grid;
  gap: 8px;
  padding-top: 8px;
  border-top: 2px solid var(--line, #e4e0f7);
}

.admin-eigene-sets h3 { margin: 0; }

/* ===========================================================================
   Der Elternbereich im Profilfenster
   ===========================================================================
   Was der Adminbereich für alle ist, ist das hier für die eigene Familie:
   die Kinder zum Aufklappen, ihr Zug, ihre Level, und die Knöpfe, mit denen
   Eltern sie verwalten. Es sind dieselben Bausteine wie auf admin.html –
   deshalb steht hier nur, was an der Kinderliste anders ist. */
.admin-link {
  text-decoration: none;
  color: inherit;
}

/* Der Knopf war grau auf Violett und damit fast nicht zu lesen – nicht, weil
   hier eine falsche Farbe stand, sondern weil eine Zeile weiter oben gewinnt:
   ".unlock-mode-card span" (zwei Teile, Klasse plus Element) schlägt
   ".admin-link-knopf" (ein Teil) und färbte den Knopf in das Grau, das für
   den Beschreibungstext daneben gedacht ist. Deshalb steht die Karte hier im
   Wähler mit davor: gleich viele Klassen, aber ein Element mehr – und damit
   gewinnt diese Regel. */
.admin-link .admin-link-knopf,
.unlock-mode-card span.admin-link-knopf {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 20px;
  border-radius: 999px;
  background: var(--primary, #6c5ce7);
  color: #fff;
  font-weight: 900;
  white-space: nowrap;
}

.kinder-liste .kind-zeile {
  display: grid;
  gap: 8px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line, #e4e0f7);
}

.kinder-liste .kind-zeile:last-child { border-bottom: 0; }

.kind-kopf {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 6px 4px;
  border: 0;
  border-radius: 14px;
  background: transparent;
  font: inherit;
  text-align: left;
  cursor: pointer;
  flex-wrap: wrap;
}

.kind-kopf:hover,
.kind-kopf:focus-visible {
  background: #fbfaff;
  outline: none;
}

.kind-zeile.is-open > .kind-kopf .admin-entry-caret { transform: rotate(90deg); }

.kind-kopf .kind-name {
  display: grid;
  gap: 2px;
  flex: 1 1 140px;
  min-width: 0;
}

.kind-kopf .kind-name span {
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--muted, #5b6b7a);
}

.kind-detail {
  display: grid;
  gap: 12px;
  padding: 10px 4px 14px;
}

.kind-aktionen { flex-wrap: wrap; justify-content: flex-start; }

.wagen-sets {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 10px;
}

@media (max-width: 720px) {
  .admin-kopf,
  .admin-seite { width: calc(100% - 24px); }
  .admin-reiter { position: static; }
  .admin-werkzeug { gap: 8px; }
  .admin-filtergruppe { width: 100%; justify-content: space-between; }
  .admin-marken { flex-direction: row; }
  .abdeckung-spiele,
  .admin-familien,
  .admin-gruppen { grid-template-columns: 1fr; }
}

/* Die Kontenzeile auf der eigenen Seite trägt zwei Dinge mehr als die im
   Fenster: die Marken (Rolle, Kaufstand) und das Datum der letzten Aktivität.
   Das Raster oben hat für jedes Kind eine feste Spalte – ein fünftes und
   sechstes Kind hätte dort keinen Platz. Auf dieser Seite wird die Zeile
   deshalb zu einer Flex-Reihe, die umbricht, wenn es eng wird: Die Reihenfolge
   im HTML ist dann auch die auf dem Bild. */
body[data-page="admin"] .admin-entry-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  padding: 14px 16px;
}

body[data-page="admin"] .admin-entry-head > * {
  grid-column: auto;
  grid-row: auto;
}

body[data-page="admin"] .admin-entry-name { flex: 1 1 200px; }
body[data-page="admin"] .admin-entry-head .admin-train { flex: 1 1 100%; }
body[data-page="admin"] .admin-entry-head .admin-user-summary { margin-left: auto; }
body[data-page="admin"] .admin-data-list { max-height: 520px; }

@media (max-width: 900px) {
  body[data-page="admin"] .admin-columns,
  body[data-page="admin"] .admin-top-levels > div,
  body[data-page="admin"] .admin-game-grid {
    grid-template-columns: 1fr;
  }

  body[data-page="admin"] .admin-data-list {
    max-height: none;
    padding-right: 0;
  }

  body[data-page="admin"] .admin-entry-head .admin-user-summary {
    margin-left: 0;
    flex: 1 1 100%;
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* --- Gäste: woher der Besuch kam ------------------------------------------
   Drei Klassen für den Reiter "Gäste", seit dort auch Geräte stehen, die nur
   besucht und nie gespielt haben.

   .admin-entry-herkunft  Gerät und Standort in der zugeklappten Zeile. Gleich
                          gebaut wie .admin-entry-zuletzt daneben, nur zwei
                          Angaben statt einer – und ohne nowrap, weil ein
                          Ortsname länger sein darf als ein Datum.
   .admin-gast-nurbesuch  Steht anstelle des Zug-Streifens, wenn es keinen
                          Fortschritt gibt. Ein leerer Zug sähe aus wie ein Zug
                          bei null; hier gibt es gar keinen.
   .admin-herkunft        Dasselbe ausgeschrieben im aufgeklappten Eintrag. */

.admin-entry-herkunft {
  display: grid;
  gap: 2px;
  font-size: 0.85rem;
  font-weight: 700;
  min-width: 0;
  overflow-wrap: anywhere;
}

.admin-entry-herkunft b {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 800;
  color: var(--muted, #5b6b7a);
  margin-right: 6px;
}

.admin-gast-nurbesuch {
  align-self: center;
  padding: 3px 10px;
  border-radius: 999px;
  background: #f1eefc;
  border: 1px solid var(--line, #e4e0f7);
  color: var(--muted, #5b6b7a);
  font-size: 0.72rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.admin-herkunft {
  margin: 0 0 18px;
  padding: 14px 16px;
  border: 1px solid var(--line, #e4e0f7);
  border-radius: 14px;
  background: #faf9ff;
}

.admin-herkunft h4 {
  margin: 0 0 10px;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted, #5b6b7a);
}

.admin-herkunft dl {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr));
  gap: 10px 18px;
  margin: 0 0 10px;
}

.admin-herkunft dt {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 800;
  color: var(--muted, #5b6b7a);
}

.admin-herkunft dd {
  margin: 2px 0 0;
  font-weight: 700;
  overflow-wrap: anywhere;
}

.admin-herkunft p { margin: 0; font-size: 0.78rem; }

body[data-page="admin"] .admin-entry-herkunft { flex: 1 1 220px; }

@media (max-width: 900px) {
  body[data-page="admin"] .admin-entry-herkunft { flex: 1 1 100%; }
}

/* --- Gespielte Spiele -------------------------------------------------------
   Eine Liste statt Punkteraster plus Zeile darunter. Das Raster hatte 40
   Kästchen je Spiel und stand auch für Spiele da, die nie jemand geöffnet hat –
   die Antwort auf "wer hat was gespielt?" ging darin unter. Jetzt steht hier
   nur, wozu es einen Eintrag gibt, und für beide Sorten Spiel dieselbe Form. */
.abdeckung-kopf { margin: 0 0 10px; }
.abdeckung-gespielt span { align-content: start; }
.abdeckung-gespielt b {
  display: block;
  margin-bottom: 2px;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted, #5b6b7a);
}

/* --- Gratis ohne Limite ------------------------------------------------------
   Der Haken im Reiter "Spiele": Er gibt ein einzelnes Spiel ohne Kauf frei,
   für eine Werbeaktion. Weil er als einziges Element auf der Karte etwas
   ändert statt etwas zu zeigen, hebt er sich ab – und eine freigegebene Karte
   trägt einen Rahmen, damit man von oben sieht, was gerade offen steht. */
.admin-gratis {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  padding: 8px 10px;
  border: 1px solid var(--line, #e4e0f7);
  border-radius: 12px;
  background: #faf9ff;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--muted, #5b6b7a);
  cursor: pointer;
}
.admin-gratis input { width: 18px; height: 18px; accent-color: #6c5ce7; cursor: pointer; }
.admin-gratis:hover { border-color: var(--line-strong, #cdc6ee); }
.admin-gratis.ist-an { background: #eaf7ef; border-color: #9fd8b5; color: #23713f; }
.admin-game-card.ist-gratis { border-color: #9fd8b5; box-shadow: 0 0 0 2px rgba(47, 158, 92, 0.18); }

/* ===========================================================================
   Mini-Games
   ---------------------------------------------------------------------------
   Die Spiele allein, unter kids.alae.app/mini-games – für jemanden, dem ein
   Link geschickt wurde. Kein Zug, keine Wagen, kein Konto: eine Seite, die in
   zehn Sekunden verstanden ist, sonst ist der Besucher wieder weg.

   Die Farben sind die der App, und die Wärme kommt von Turmbau: das Orange des
   Bereichs Geschwindigkeit (#F5A623) als Hausfarbe, der Himmel der Landschaft
   (#a8ddf0) darüber, das dunkle Blau der Schrift (#243047) darunter. Jede
   Spielkarte trägt zusätzlich die Farbe ihres Bereichs – dieselbe wie am Wagen
   und auf der Bühne des Spiels. Bunt, aber nicht laut: eine Farbe je Karte,
   der Rest ist Weiss und Abstand.
   =========================================================================== */

:root {
  --mini-ton: #F5A623;
  --mini-ton-dunkel: #b9741a;
  --mini-himmel: #a8ddf0;
}

/* Im Mini-Modus ist kein Konto im Spiel: Der Knopf oben rechts, der in der App
   das Profil öffnet, führte hier zu einer Anmeldung, die niemand braucht und
   niemand will. firebase.js hängt ihn an jede Seite – weg damit, aber nur
   hier. */
body[data-mini] .account-button,
body[data-mini] .account-modal { display: none !important; }

/* Ohne den Konto-Knopf wird oben rechts ein Platz frei. Der Ton-Schalter steht
   sonst auf dem zweiten (76 px) und der Konto-Knopf auf dem ersten (14 px) –
   hier rückt er auf den ersten nach, sonst bliebe in der Ecke ein Loch und die
   Leiste daneben verlöre Platz, den sie braucht. */
body[data-mini] .sound-toggle { right: calc(var(--safe-right) + 14px); }
body[data-mini] .cm-bar { right: calc(var(--safe-right) + 76px); }

/* Drei Wortknöpfe nebeneinander brauchen Platz, den ein hochkant gehaltenes
   Handy nicht hat. Statt umzubrechen – eine zweite Zeile läge über der
   Spielfläche – werden sie mit dem Bild kleiner: erst die Innenabstände, dann
   die Schrift. Ab 560 px stehen sie wieder in voller Grösse da. */
/* Passt es trotzdem nicht – ein schmales Fenster am Rechner, wo kein
   Dreh-Hinweis kommt –, rutschen die Knöpfe in eine zweite Zeile, statt
   einander und den Zähler zu überdecken. Auf dem Handy quer, wie diese Spiele
   gespielt werden, passiert das nie. */
body[data-mini] .cm-bar { flex-wrap: wrap; row-gap: 6px; }
body[data-mini] .cm-bar-left { gap: clamp(4px, 1vw, 10px); min-width: 0; }
body[data-mini] .cm-bar-left .mini-knopf {
  min-height: clamp(34px, 8vw, 44px);
  padding: 0 clamp(7px, 1.7vw, 18px);
  font-size: clamp(0.62rem, 1.75vw, 0.95rem);
  border-width: clamp(2px, 0.6vw, 3px);
}
body[data-mini] .cm-bar-left .cm-icon {
  width: clamp(32px, 7.5vw, 48px);
  height: clamp(32px, 7.5vw, 48px);
}
body[data-mini] .cm-bar-left .cm-icon svg { width: clamp(16px, 4vw, 24px); height: auto; }
/* Der Zähler behält seinen Platz: Er ist die Zahl, um die es im Spiel geht. */
body[data-mini] .cm-count { flex: 0 0 auto; }
/* Links bleibt der Platz für den Lautsprecher aus kids.js frei – er steht dort
   fest, und Knöpfe darunter wären unerreichbar. Weichen darf dafür der
   Spieltitel: Die Knöpfe sind das, was man braucht, der Name des Spiels steht
   ohnehin im Spiel. */
body[data-mini] .cm-bar .cm-title { display: none; }
@media (min-width: 860px) {
  body[data-mini] .cm-bar .cm-title {
    display: block;
    position: static;
    margin: 0 auto;
    transform: none;
  }
}

/* --- Der Knopf ------------------------------------------------------------- */
/* Eine Pille statt eines Kreises: Hier stehen Wörter, keine Zeichen. Ein
   Erwachsener, der über einen Link hereinkommt, rät nicht, was ein Haus
   bedeutet – er liest "Zur App". */
.mini-knopf {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 44px;
  padding: 0 18px;
  border: 3px solid #fff;
  border-radius: 999px;
  background: var(--mini-farbe, var(--mini-ton));
  color: #fff;
  font: inherit;
  font-size: 0.95rem;
  font-weight: 800;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: 0 6px 16px rgba(36, 48, 71, 0.22);
  transition: transform 0.15s ease, background 0.15s ease;
}
.mini-knopf:active { transform: scale(0.95); }
.mini-knopf:focus-visible { outline: 3px solid #243047; outline-offset: 3px; }
.mini-knopf-voll { background: var(--mini-farbe, var(--mini-ton)); }
.mini-knopf-hell { background: #fff; color: #243047; border-color: rgba(36, 48, 71, 0.12); }
.mini-knopf-still {
  background: #f1f3f8;
  color: #3a4657;
  border-color: transparent;
  box-shadow: none;
}
.mini-knopf-still.ist-an {
  background: var(--mini-farbe, var(--mini-ton));
  color: #fff;
}
.mini-knopf-klein {
  min-height: 0;
  padding: 3px 12px;
  font-size: 0.82rem;
  border-width: 2px;
  background: #f1f3f8;
  color: #3a4657;
  border-color: transparent;
  box-shadow: none;
}

/* --- Die Bestenliste ------------------------------------------------------- */
/* Platz, Name, Zahl, Runden – in dieser Reihenfolge, weil man in dieser
   Reihenfolge fragt: Wer ist oben? Wer ist das? Wie viel? Und wie oft hat er
   es probiert? Die eigene Zeile ist eingefärbt, sonst sucht man sich selbst. */
.mini-liste {
  width: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.mini-zeile {
  display: grid;
  grid-template-columns: 2.4rem 1fr auto;
  align-items: baseline;
  gap: 4px 10px;
  padding: 8px 12px;
  border-radius: 12px;
  background: #f4f5f8;
  font-weight: 800;
  color: #243047;
  text-align: left;
}
.mini-zeile.ist-erster { background: #fff5e2; box-shadow: inset 0 0 0 2px rgba(245, 166, 35, 0.45); }
.mini-zeile.ist-ich { background: var(--mini-farbe, var(--mini-ton)); color: #fff; }
.mini-zeile.ist-ich.ist-erster { box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.6); }
.mini-platz { font-size: 0.95rem; opacity: 0.7; font-variant-numeric: tabular-nums; }
.mini-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mini-wert { font-size: 1.1rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
.mini-einheit { font-size: 0.72rem; font-weight: 700; opacity: 0.75; }
.mini-versuche {
  grid-column: 2 / -1;
  font-size: 0.76rem;
  font-weight: 700;
  opacity: 0.6;
}
/* Die drei Punkte zwischen den Ersten und der eigenen Zeile weiter hinten. */
.mini-luecke {
  padding: 0 12px;
  color: #a6adbb;
  font-weight: 900;
  letter-spacing: 0.3em;
  line-height: 1;
  text-align: center;
}
.mini-liste-leer {
  padding: 12px;
  border-radius: 12px;
  border: 2px dashed #d7dbe6;
  color: #667085;
  font-weight: 700;
  text-align: center;
}

/* --- Unter dem Ergebnis ---------------------------------------------------- */
/* Steht in der Tafel von game-shell.js, unter der eigenen Zahl: erst der Satz,
   was gerade passiert ist, dann – wenn nötig – das Namensfeld, dann die
   Liste. */
.mini-ergebnis {
  width: min(320px, 100%);
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: stretch;
}
.mini-meldung {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 800;
  color: var(--cm-accent-dark, #b9741a);
  text-align: center;
}
.mini-namensfeld { display: flex; gap: 8px; }
.mini-namensfeld input {
  flex: 1;
  min-width: 0;
  min-height: 44px;
  padding: 0 14px;
  border: 2px solid #d7dbe6;
  border-radius: 999px;
  font: inherit;
  font-size: 1rem;
  font-weight: 700;
  color: #243047;
  background: #fff;
}
.mini-namensfeld input:focus-visible { outline: 3px solid var(--cm-accent, #F5A623); outline-offset: 2px; }
.mini-namensfeld .mini-knopf { background: var(--cm-accent, var(--mini-ton)); }
.mini-name-steht {
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 0.88rem;
  font-weight: 700;
  color: #5c6a67;
}
.mini-listen-wirt { display: flex; flex-direction: column; gap: 6px; }

/* --- Das Fenster "Mini Games" ---------------------------------------------- */
.mini-fenster {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: grid;
  place-items: center;
  padding: calc(var(--safe-top) + 16px) 14px calc(var(--safe-bottom) + 16px);
  background: rgba(23, 34, 42, 0.5);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}
.mini-tafel {
  width: min(560px, 100%);
  max-height: 100%;
  overflow-y: auto;
  padding: clamp(16px, 3vw, 24px);
  border-radius: 24px;
  background: #fff;
  box-shadow: 0 18px 44px rgba(36, 48, 71, 0.3);
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.mini-tafel-kopf {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
.mini-tafel-kopf h2 { margin: 2px 0 0; font-size: 1.5rem; font-weight: 900; color: #243047; }
.mini-marke {
  margin: 0;
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--mini-ton-dunkel);
}
.mini-wahl { display: flex; flex-wrap: wrap; gap: 8px; }
.mini-inhalt { display: flex; flex-direction: column; gap: 10px; }
.mini-spieltitel {
  margin: 0;
  font-size: 1.15rem;
  font-weight: 900;
  color: var(--mini-farbe-dunkel, var(--mini-ton-dunkel));
}
.mini-tafel-aktionen { display: flex; flex-wrap: wrap; gap: 10px; }
.mini-hinweis {
  margin: 0;
  padding: 14px;
  border-radius: 14px;
  background: #f4f5f8;
  color: #5c6a67;
  font-weight: 700;
  text-align: center;
}

/* --- Die Übersicht: /mini-games -------------------------------------------- */
.mini-seite {
  max-width: 1040px;
  margin: 0 auto;
  padding: clamp(18px, 4vw, 40px) clamp(14px, 4vw, 28px) 60px;
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 4vw, 34px);
}
.mini-kopf {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  /* Der Text nimmt den Platz, der Knopf nur seinen eigenen – sonst bricht der
     Vorspann mitten im Satz um, während rechts daneben nichts steht. */
  padding: clamp(14px, 3vw, 20px) clamp(18px, 4vw, 26px);
  border-radius: 22px;
  color: #fff;
  background:
    radial-gradient(circle at 88% -10%, rgba(255, 255, 255, 0.35), transparent 55%),
    linear-gradient(135deg, var(--mini-ton), var(--mini-ton-dunkel));
  box-shadow: 0 18px 40px rgba(185, 116, 26, 0.28);
}
.mini-kopf > div { flex: 1 1 auto; min-width: 0; }
.mini-kopf h1.mini-marke {
  margin: 0;
  font-size: clamp(0.9rem, 2.6vw, 1.15rem);
  letter-spacing: 0.1em;
}
.mini-kopf > .mini-knopf { flex: 0 0 auto; }
.mini-kopf .mini-marke { color: rgba(255, 255, 255, 0.85); }
.mini-kopf h1 {
  margin: 6px 0 10px;
  font-size: clamp(1.6rem, 5vw, 2.4rem);
  font-weight: 900;
  line-height: 1.1;
}
.mini-streifen {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}
.mini-streifen > div {
  padding: 14px 10px;
  border-radius: 18px;
  background: #fff;
  border: 2px solid rgba(168, 221, 240, 0.7);
  text-align: center;
}
.mini-streifen strong {
  display: block;
  font-size: clamp(1.5rem, 5vw, 2rem);
  font-weight: 900;
  color: var(--mini-ton-dunkel);
  font-variant-numeric: tabular-nums;
}
.mini-streifen span { font-size: 0.82rem; font-weight: 700; color: #667085; }

.mini-block { display: flex; flex-direction: column; gap: 14px; }
.mini-block > h2 {
  margin: 0;
  font-size: clamp(1.2rem, 3.4vw, 1.6rem);
  font-weight: 900;
  color: #243047;
}
.mini-karten {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 14px;
}
.mini-karte {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
  border-radius: 20px;
  background: #fff;
  border: 2px solid rgba(36, 48, 71, 0.08);
  border-top: 6px solid var(--mini-farbe, var(--mini-ton));
  box-shadow: 0 10px 26px rgba(36, 48, 71, 0.08);
}
.mini-karte-kopf {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
.mini-karte-kopf h3 {
  margin: 0;
  font-size: 1.1rem;
  font-weight: 900;
  color: var(--mini-farbe-dunkel, #243047);
}
.mini-karte-zahl { font-size: 0.78rem; font-weight: 800; color: #667085; white-space: nowrap; }
.mini-karte-aktionen { display: flex; gap: 8px; margin-top: auto; }
.mini-karte .mini-knopf-voll { flex: 1; }

.mini-tabelle {
  width: 100%;
  border-collapse: collapse;
  background: #fff;
  border-radius: 18px;
  overflow: hidden;
  box-shadow: 0 10px 26px rgba(36, 48, 71, 0.08);
  font-size: 0.92rem;
}
.mini-tabelle th,
.mini-tabelle td {
  padding: 10px 12px;
  text-align: left;
  border-bottom: 1px solid #eef0f5;
}
.mini-tabelle th {
  font-size: 0.74rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #667085;
  background: #f8f9fc;
  white-space: nowrap;
}
.mini-tabelle .zahl { text-align: right; font-variant-numeric: tabular-nums; }
.mini-tabelle tbody tr:last-child td { border-bottom: none; }
.mini-tabelle tbody tr.ist-erster { background: #fff8ec; }
.mini-tabelle-name { display: flex; align-items: center; gap: 10px; font-weight: 800; color: #243047; }
.mini-rangzahl {
  display: grid;
  place-items: center;
  min-width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--mini-ton);
  color: #fff;
  font-size: 0.78rem;
  font-weight: 900;
}
/* Auf dem Handy ist eine Tabelle mit sechs Spalten keine Tabelle mehr, sondern
   ein Rätsel. Sie darf dort seitwärts geschoben werden – der Name bleibt
   stehen, damit man weiss, wessen Zahlen man liest. */
/* Auf dem Handy soll die Auswertung ins Bild passen, nicht geschoben werden
   müssen: engere Zellen, kleinere Schrift, und der Name darf umbrechen.
   Reicht es trotzdem nicht – ein sehr langer Name, ein sehr schmales Gerät –,
   lässt sich der Block seitwärts schieben, statt die Seite mitzunehmen. */
@media (max-width: 560px) {
  .mini-block { overflow-x: auto; }
  .mini-tabelle { font-size: 0.8rem; }
  .mini-tabelle th,
  .mini-tabelle td { padding: 8px 5px; }
  .mini-tabelle th { font-size: 0.62rem; letter-spacing: 0; }
  .mini-tabelle-name { gap: 6px; align-items: center; }
  .mini-rangzahl { min-width: 22px; height: 22px; font-size: 0.7rem; }
}

@media (max-height: 600px) {
  .cm-panel .mini-ergebnis {
    grid-area: liste;
    width: clamp(200px, 36vw, 300px);
    max-height: 100%;
    overflow-y: auto;
    gap: 5px;
  }
  .cm-panel .mini-zeile { padding: 4px 10px; gap: 2px 8px; }
  .cm-panel .mini-wert { font-size: 1rem; }
  .cm-panel .mini-versuche { font-size: 0.7rem; }
  .cm-panel .mini-meldung { font-size: 0.85rem; }
  .cm-panel .mini-namensfeld input,
  .cm-panel .mini-namensfeld .mini-knopf { min-height: 38px; font-size: 0.9rem; }
  .cm-panel .mini-name-steht { font-size: 0.78rem; gap: 6px; }
}

/* Wer weniger Bewegung eingestellt hat, bekommt keine – hier gibt es ohnehin
   nur den kleinen Druck beim Antippen. */
@media (prefers-reduced-motion: reduce) {
  .mini-knopf { transition: none; }
  .mini-knopf:active { transform: none; }
}

/* Der zweite Haken auf der Spielkarte im Adminbereich: derselbe Kasten wie
   "Gratis ohne Limite", nur in der Hausfarbe der Mini-Games – zwei Haken
   nebeneinander, die dasselbe aussähen, verwechselt man. Darunter die Adresse
   zum Kopieren: Sie ist der eigentliche Zweck des Hakens. */
.admin-mini.ist-an { background: #fff5e6; border-color: #f0c583; color: #9a5f13; }
.admin-mini input { accent-color: #b9741a; }
.admin-game-card.ist-mini { border-color: #f0c583; box-shadow: 0 0 0 2px rgba(245, 166, 35, 0.18); }
.admin-mini-link {
  margin: 8px 0 0;
  font-size: 0.78rem;
  font-weight: 700;
  overflow-wrap: anywhere;
}
.admin-mini-link a { color: #9a5f13; }

/* Der Weg auf den Startbildschirm, ganz unten auf der Übersicht. Zurückhaltend:
   Wer nur eine Runde spielen will, soll nicht über eine Aufforderung stolpern –
   wer wiederkommt, findet sie. */
.mini-install {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 16px 18px;
  border-radius: 20px;
  background: #fff;
  border: 2px dashed rgba(245, 166, 35, 0.55);
}
.mini-install-text { flex: 1 1 16rem; min-width: 0; }
.mini-install-text strong { display: block; font-size: 1.05rem; font-weight: 900; color: #243047; }
.mini-install-text p { margin: 4px 0 0; font-size: 0.9rem; font-weight: 600; color: #667085; line-height: 1.45; }
.mini-install-schritte {
  flex: 1 1 100%;
  margin: 0;
  padding-left: 1.2rem;
  font-size: 0.88rem;
  font-weight: 600;
  color: #4a5b52;
  line-height: 1.5;
}
.mini-install-aktionen { display: flex; gap: 8px; flex: 0 0 auto; }

/* Der Nachsatz unter dem Hinweis auf die zweite Adresse: leiser als der Rest,
   aber er muss dastehen – ein Name gehört zu der Adresse, unter der er
   eingetragen wurde, und das merkt man sonst erst hinterher. */
.mini-install-nachsatz { font-size: 0.82rem; opacity: 0.85; }
