:root {
  color-scheme: dark;
  /* Navy and teal separate the stage from the bright stones; gold and coral highlight rewards. Every pair below is checked for contrast by qa/syntax-surge.mjs. */
  --ink: #f4fbff; --muted: #b7ccd9; --cyan: #7ce9ff; --gold: #ffd77a; --pink: #ff8ae8; --green: #6ff0bd; --violet: #bb9dff; --red: #ff7183;
  --line: #ffffff26; --panel: #173747; --panel-2: #234857; --glass: #153645c0; --void: #000000;
  /* 🚨 DIE SICHEREN RÄNDER STEHEN EINMAL HIER — und quer sind es die SEITEN, nicht oben und unten.
     Gemeldet: „Landscape am Handy und Insets werden ignoriert." Beim Kippen wandert die Aussparung an den
     linken oder rechten Rand; `env(safe-area-inset-left/right)` war nirgends benutzt, also lag der Text
     eines bildschirmfüllenden Blatts unter der Kamera. Über Variablen, weil `env()` in jeder Regel neu
     ausgeschrieben unweigerlich irgendwo vergessen wird — und weil eine Prüfung sie so überschreiben und
     nachmessen kann. */
  --sa-oben: env(safe-area-inset-top,0px); --sa-rechts: env(safe-area-inset-right,0px);
  --sa-unten: env(safe-area-inset-bottom,0px); --sa-links: env(safe-area-inset-left,0px);
  font-family: ui-rounded, "Trebuchet MS", system-ui, sans-serif;
}
* { box-sizing: border-box; }
/* ONE edge colour for the whole app, exactly like KelRaid (#070b16), Kellerkind (#04070d) and Odyssee (#0d1420):
   the same value is the meta theme-color, both manifest colours and the html/body ground. Whatever iOS paints
   itself at the screen edges (status-bar band at cold start, launch backdrop) then equals the page and never
   shows as a seam. Set on html AND body so the canvas is painted even before body lays out. */
html, body { width: 100%; height: 100dvh; overflow: hidden; overscroll-behavior: none; background: var(--void); }
/* The topmost colour is flat and equals theme-color, so Safari's tinted status bar and the page meet without a seam;
   the glows start below the notch. In standalone the page itself paints under the status bar. */
body { user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; background:
    /* Front layer: the top strip stays exactly --void, so the page continues the iOS band without a step. */
    linear-gradient(180deg,var(--void) 0,var(--void) 68px,rgba(12,10,26,0) 200px),
    radial-gradient(70% 55% at 15% 48%,#216b7980,transparent 60%),
    radial-gradient(58% 48% at 88% 88%,#204b68,transparent 60%),
    radial-gradient(52% 38% at 80% 34%,#286b6855,transparent 65%),
    linear-gradient(#0d1424 0,#0f2f40 38%,#103444 56%,#081e2e 100%) no-repeat var(--void);
  color: var(--ink); }
.ambient { position: fixed; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 0; opacity: 0; transition: opacity .8s ease; }
/* 🚨 AUCH IM SPIEL. Der Sternenhimmel der Welt liegt hinter dem Brett (`.shell` ist durchsichtig,
   z-index 1 über dieser Leinwand). Dort steht er still — siehe `ambientSync()`. Damit ist die
   Leinwand auf allen vier Bildschirmen sichtbar und der Überblendung bleibt nichts zu tun; sie
   bleibt nur stehen, falls je ein Bildschirm ohne Himmel dazukommt. */
body[data-screen="menu"] .ambient, body[data-screen="progress"] .ambient, body[data-screen="leaderboard"] .ambient, body[data-screen="game"] .ambient { opacity: 1; }
button, input { font: inherit; }
input, textarea, .selectable { user-select: text; -webkit-user-select: text; }
button, a { -webkit-tap-highlight-color: transparent; touch-action: manipulation; }
button { cursor: pointer; color: inherit; border: 1px solid var(--line); transition: transform .15s ease, box-shadow .2s ease, filter .2s ease; }
button:active { transform: translateY(1px); }
button:focus-visible, a:focus-visible, input:focus-visible, summary:focus-visible { outline: 3px solid var(--cyan); outline-offset: -3px; }
/* 🚨 `summary` FEHLTE HIER — und Safari zeichnet dort seinen eigenen, dicken blauen Ring. `showModal()` setzt den
   Fokus auf das erste fokussierbare Element im Blatt; in den Spielregeln war das ein Aufklapp-Titel, der stand
   dann blau umrandet mitten im Text. Gemeldet: „Das sieht auch noch nicht gut aus." Jetzt trägt auch er den
   Ring des Spiels, und nur bei Tastaturbedienung. */
summary:focus:not(:focus-visible) { outline: none; }
/* The board is focused by script on every tap, and Safari paints :focus-visible for script focus. The ring is
   for keyboard players only, so the body flags keyboard use and a pointer clears it again. */
#board:focus { outline: 0; }
button:disabled { cursor: default; opacity: .45; }
svg { fill: none; flex: 0 0 auto; }
[hidden] { display: none !important; }
.shell {
  /* Pinned to all four screen edges like the Kellerkind/KelRaid shells: no vh/dvh math, the fixed box tracks
     Safari's collapsing toolbars by itself. Safe areas pad only the controls; the background is the page. */
  position: fixed; inset: 0; z-index: 1; width: 100%;
  padding: var(--sa-oben) var(--sa-rechts) var(--sa-unten) var(--sa-links);
  display: flex; flex-direction: column; overflow: hidden;
}
.top { flex: 0 0 52px; min-height: 0; display: flex; align-items: center; justify-content: space-between; padding: 0 16px; gap: 8px; }
/* 🚨 DIE KOPFLEISTE ZEIGT SICH NUR, WENN SIE ETWAS ZU SAGEN HAT. Gemeldet: „wieso gibt es oben links
   nochmal so ein icon?!?" — dort stand das App-Icon als Weg zurück ins Menü. Im Spiel ist das doppelt
   gemoppelt: Nova sitzt schon in der Kopfzeile des Bretts, und der Weg zurück läuft über die Pause. Auf
   den Listenseiten macht es der Zurück-Knopf. Das Icon ist deshalb weg — und damit bleibt in Menü und
   Spiel eine leere Leiste übrig, die nur Platz frisst. Also weg mit ihr: Im Spiel sind das 52 px mehr
   für das Brett. */
body[data-screen="menu"] .top, body[data-screen="game"] .top { display: none; }.top-spacer { flex: 1; }
/* 🚨 RUND HEISST RUND. `border-radius: 50%` auf einem Kasten, der nicht quadratisch ist, ergibt eine Ellipse —
   und genau das passierte zweimal: quer bekam JEDER runde Knopf 44×36 (die Regel war für die Kopfleiste
   gedacht und traf auch das Schließen-Kreuz in jedem Blatt), und in einer schmalen Kopfzeile drückte der
   Flex-Kasten ihn auf 36×44 zusammen. `flex: none` hält ihn, `aspect-ratio` fängt jede künftige Höhenangabe
   ab, ohne dass man daran denken muss. */
.round-button { flex: none; width: 44px; height: 44px; aspect-ratio: 1; border-radius: 50%; background: #ffffff10; display: grid; place-items: center; color: white; }
/* 🚨 HOVER NUR, WO ES HOVER GIBT. Gemeldet vom iPhone: Nach einem Tipp auf „Einstellungen" blieb eine
   Umrandung stehen. Safari auf iOS setzt `:hover` beim Tippen und LÄSST ES STEHEN, bis woanders hingetippt
   wird — jeder Hover-Effekt wird so zu einem Rest, der nach dem Tippen hängen bleibt (weiße Schrift, hellerer
   Grund, angehobene Karte). Darum steht jede `:hover`-Regel in `@media(hover:hover)`, und zwar AN IHRER
   STELLE: Eine Media-Query ändert die Spezifität nicht, nach unten verschoben würde sie aber gegen
   `.pressed` gewinnen, das bei gleicher Spezifität nur deshalb siegt, weil es später steht.
   `qa/polaris-haptik.mjs` besteht darauf, dass keine Hover-Regel mehr frei steht. */
@media(hover:hover) { .round-button:hover { background: #ffffff22; } }
.screen { display: none; flex: 1 1 0; min-height: 0; min-width: 0; }
body[data-screen="menu"] #menu { display: flex; }
body[data-screen="game"] #game { display: grid; }
body[data-screen="nova-egg"] #nova-egg { display: flex; }
body[data-screen="nova-egg"] .top { display: none; }
body[data-screen="progress"] #progress-screen, body[data-screen="leaderboard"] #leaderboard-screen { display: flex; }
.eyebrow { font-size: 10px; font-weight: 800; letter-spacing: .16em; color: var(--cyan); margin: 0; display: inline-flex; align-items: center; gap: 6px; }

/* ── Menu ─────────────────────────────────────────────────────────────── */
.menu { justify-content: center; align-items: center; padding: 8px 20px 16px; }
.hero { width: min(100%,520px); max-height: 100%; display: flex; flex-direction: column; align-items: center; text-align: center; gap: 12px; }
@keyframes rise { from { opacity: 0; transform: translateY(18px) scale(.97); } to { opacity: 1; transform: none; } }
@keyframes float { 50% { transform: translateY(-8px) rotate(-6deg); } }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes twinkle { 50% { transform: scale(1.5); opacity: .6; } }
@keyframes shimmer { to { background-position: 200% center; } }
@keyframes sheen { 0%, 60% { transform: translateX(-140%) skewX(-20deg); } 100% { transform: translateX(240%) skewX(-20deg); } }
@keyframes breathe { 50% { box-shadow: 0 6px 0 #995335, 0 14px 40px #ffbb7744; } }
.hero > * { animation: rise .55s cubic-bezier(.2,.8,.2,1) backwards; animation-delay: calc(var(--i,0) * 70ms + 40ms); }
/* Nova greets in the menu: a tappable rig (she hops, winks and answers), her line under the title. */
.hero-nova { --i: 0; background: none; border: 0; padding: 6px; margin: 0; border-radius: 50%; line-height: 0; transition: transform .15s ease; }
/* The press is a plain scale: the generic .pressed brightness filter made WebKit drop the rig (a filter on the
   button plus a drop-shadow filter on the SVG inside left only a dark square), so neither carries a filter. */
.hero-nova.pressed { transform: translateY(3px) !important; filter: none; }
.nova-hero { width: 132px; height: 132px; }
.hero-line { --i: 2; position: relative; margin: 0 0 6px; padding: 8px 14px; border-radius: 16px; background: #7ce9ff14; border: 1px solid #7ce9ff44; color: var(--ink); font-size: 14px; line-height: 1.4; max-width: 400px; }
.hero-line::before { content: ""; position: absolute; left: 50%; top: -7px; width: 12px; height: 12px; margin-left: -6px; transform: rotate(45deg); background: #17324a; border: 1px solid #7ce9ff44; border-right: 0; border-bottom: 0; border-radius: 3px 0 0 0; }
.hero-line.bump { animation: rise .3s ease; }.hero-line:empty { display: none; }
.hero h1 { --i: 1; font-size: clamp(36px,7vw,64px); line-height: 1; margin: 0; letter-spacing: -.045em; }
/* 🚨 DER TITEL IST EINE ZWEIZEILIGE MARKE, KEIN SATZ. „Polaris: Return Signal" ist fast doppelt so
   lang wie der alte Name; in einer Zeile bräche er auf dem Handy an einer beliebigen Stelle um.
   Deshalb steht der Beiname als eigene, kleinere Zeile darunter — und der Doppelpunkt entfällt,
   weil der Zeilenumbruch ihn schon spricht. */
.hero h1 em { display: block; font-size: .52em; letter-spacing: -.01em; margin-top: .06em; font-style: normal; background: linear-gradient(90deg,#7ce9ff,#c8f7ff 40%,#7ce9ff 60%,#b191ff); background-size: 200% auto; -webkit-background-clip: text; background-clip: text; color: transparent; animation: shimmer 4s linear infinite; }
.play-button { --i: 4; position: relative; overflow: hidden; width: 100%; min-height: 74px; display: flex; align-items: center; justify-content: space-between; padding: 10px 22px; border-radius: 24px; color: #14283a; background: linear-gradient(160deg,#ffe5a3,#ffae79); border: 2px solid #fff0c5; box-shadow: 0 6px 0 #995335,0 14px 32px #0004; animation: rise .55s cubic-bezier(.2,.8,.2,1) backwards, breathe 3s ease-in-out 1s infinite; }
.play-button::after { content: ""; position: absolute; top: -20%; bottom: -20%; width: 34%; background: linear-gradient(90deg,transparent,#ffffff77,transparent); animation: sheen 3.6s ease-in-out infinite; pointer-events: none; }
@media(hover:hover) { .play-button:hover { transform: translateY(-2px); filter: brightness(1.04); } }.play-button:active { transform: translateY(2px); box-shadow: 0 2px 0 #995335; }
.play-button strong { font-size: 27px; }.play-button small { display: block; font-size: 12px; margin-top: 3px; font-weight: 600; }
.play-icon, .play-arrow { display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%; background: #14283a18; }
.menu-grid { --i: 5; display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); grid-auto-rows: 1fr; gap: 10px; width: 100%; margin-top: 8px; }
.mode { --accent: var(--cyan); position: relative; min-height: 78px; border-radius: 19px; padding: 12px 12px 12px 14px; background: linear-gradient(150deg,#285061,#102e40); box-shadow: 0 4px 0 #061824, inset 0 1px 0 #ffffff18; text-align: left; display: grid; grid-template-columns: 34px 1fr; grid-template-rows: auto 1fr; gap: 2px 10px; align-items: center; animation: rise .55s cubic-bezier(.2,.8,.2,1) backwards; animation-delay: calc(var(--i,0) * 70ms + 40ms); overflow: hidden; }
.mode::before { content: ""; position: absolute; inset: 0; background: radial-gradient(circle at 15% 20%, color-mix(in srgb, var(--accent) 35%, transparent), transparent 55%); opacity: .7; transition: opacity .25s; }
@media(hover:hover) { .mode:hover { transform: translateY(-2px); box-shadow: 0 6px 0 #061824, 0 12px 30px #0006; } }@media(hover:hover) { .mode:hover::before { opacity: 1; } }
.mode-icon { grid-row: span 2; color: var(--accent); transition: transform .3s cubic-bezier(.3,1.5,.5,1); filter: drop-shadow(0 0 8px color-mix(in srgb, var(--accent) 70%, transparent)); }
.mode strong, .mode span { display: block; position: relative; min-width: 0; align-self: start; overflow-wrap: anywhere; }.mode strong { font-size: 15px; }.mode span { font-size: 11px; color: var(--muted); }
.mode-campaign { --accent: var(--gold); }.mode-daily { --accent: var(--cyan); }.mode-endless { --accent: var(--pink); }.mode-sprint { --accent: #ff9c57; }.mode-board { --accent: var(--green); }.mode-missions { --accent: #ffb0ff; }
.mode-wide { grid-column: span 2; min-height: 58px; grid-template-rows: auto; grid-template-columns: 34px auto 1fr; }.mode-wide strong { display: inline; }.mode-wide span { text-align: right; }
.mode.done span { color: var(--green); }
.menu-tools { --i: 6; display: flex; gap: 8px; align-items: center; justify-content: center; width: 100%; }
.menu-tools button, .menu-tools a { display: inline-flex; align-items: center; gap: 6px; background: transparent; border: 0; color: var(--muted); min-height: 44px; padding: 10px 6px; font-size: 12px; text-decoration: none; border-radius: 12px; transition: color .2s, background-color .2s; }
@media(hover:hover) { .menu-tools button:hover, .menu-tools a:hover { color: white; } }

/* ── Game ─────────────────────────────────────────────────────────────── */
.game { grid-template-columns: 250px minmax(0,1fr) 230px; grid-template-rows: minmax(0,1fr) auto; gap: 0 20px; padding: 0 24px 24px; }
/* 🚨 HIER STAND DIE WERKZEUGLEISTE. Hammer, Extra-Züge, Mischen und Hinweis sind keine Knöpfe mehr, sondern
   Novas Angebote — sie schlägt vor, ein Tipp auf sie führt aus (siehe `angebotZeigen()` in app.mjs). Damit sind
   `.game-dock`, `.dock-button`, `.booster*`, `.count` und der Ziel-Modus des Hammers (`.board-wrap.aim`)
   ersatzlos weg: 62 bis 66 px Höhe unten plus Abstand, die jetzt das Brett bekommt. */
.moves-stat.time { background: linear-gradient(140deg,#ff9c57,#c24d26); border-color: #ffd6b8; }
.hud-panel { align-self: center; min-width: 0; background: var(--glass); border: 1px solid var(--line); border-radius: 26px; padding: 18px; backdrop-filter: blur(10px); }
.level-heading { display: grid; grid-template-columns: 44px 1fr auto; gap: 0 10px; align-items: center; margin: 0 0 12px; }
.level-heading h2 { font-size: 22px; margin: 2px 0 0; }.heading-text { min-width: 0; grid-area: 1 / 2; }
.nova-hud { position: relative; width: 44px; height: 44px; filter: drop-shadow(0 0 8px #7ce9ff55); cursor: pointer; touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
/* Nova ist im Spiel ein echter Knopf (sie IST das Werkzeug), aber sie darf nicht wie einer aussehen: kein
   Rahmen, keine Fläche, kein Innenabstand — nur ihre Figur und ihre Marke. */
button.nova-hud { padding: 0; border: 0; background: none; display: block; overflow: visible; }
button.nova-hud:active { transform: none; }
button.nova-hud:focus-visible { outline: 3px solid var(--cyan); outline-offset: 3px; border-radius: 50%; }
.nova-hud.pressed { transform: none !important; filter: drop-shadow(0 0 14px #7ce9ffcc) brightness(1.08); }
/* Keeps Nova's grid column reserved while her single live rig flies to the result. */
/* Nova's charge ring: a conic sweep masked to a thin ring around her mount, golden and breathing when full. */
.nova-figure.look-hud::before { content: ""; position: absolute; inset: -3px; border-radius: 50%; background: conic-gradient(var(--gold) calc(var(--charge,0) * 360deg), #ffffff16 0); -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 3.5px), #000 calc(100% - 2.5px)); mask: radial-gradient(farthest-side, transparent calc(100% - 3.5px), #000 calc(100% - 2.5px)); pointer-events: none; }
.nova-figure.look-hud.ready { filter: drop-shadow(0 0 12px #ffd77aaa); }.nova-figure.look-hud.ready::before { background: conic-gradient(var(--gold), #fff1a6, var(--gold)); animation: nova-ready 1.1s ease-in-out infinite; }
@keyframes nova-ready { 50% { transform: scale(1.12); opacity: .75; } }
body.reduced .nova-figure.look-hud.ready::before { animation: none; }
/* Things Nova throws: positioned by app.mjs every frame along an arc from her mount to the target. */
.fly { position: fixed; left: 0; top: 0; z-index: 70; pointer-events: none; will-change: transform; }
.fly svg { width: 100%; height: 100%; display: block; overflow: visible; }
.fly-hammer { width: 56px; height: 56px; margin: -28px 0 0 -28px; filter: drop-shadow(0 8px 12px #000a) drop-shadow(0 0 10px #ff6d8266); }
.fly-orb { width: 44px; height: 44px; margin: -22px 0 0 -22px; filter: drop-shadow(0 0 16px #ffd77a); }.fly-orb .go-star { transform-origin: 32px 32px; animation: orb-star .9s linear infinite; }
@keyframes orb-star { to { transform: rotate(180deg); } }
.fly-mote { width: 18px; height: 18px; margin: -9px 0 0 -9px; border-radius: 50%; background: radial-gradient(circle, #ffffff, #7ce9ff 45%, #7ce9ff00 75%); box-shadow: 0 0 16px #7ce9ff, 0 0 4px #fff; }
.fly-mote.green { background: radial-gradient(circle, #ffffff, #6ff0bd 45%, #6ff0bd00 75%); box-shadow: 0 0 16px #6ff0bd, 0 0 4px #fff; }
.fly-mote.farbig { background: radial-gradient(circle, #ffffff, var(--c,#7ce9ff) 45%, transparent 75%); box-shadow: 0 0 12px var(--c,#7ce9ff), 0 0 3px #fff; }
.fly-mote.tiny { width: 11px; height: 11px; margin: -5.5px 0 0 -5.5px; box-shadow: 0 0 10px #7ce9ff; }.fly-mote.gold { background: radial-gradient(circle, #ffffff, #ffd77a 45%, #ffd77a00 75%); box-shadow: 0 0 10px #ffd77a; }
/* Trails: ghosts dropped along the flight that fade and shrink. */
.trail { position: fixed; left: 0; top: 0; z-index: 69; pointer-events: none; border-radius: 50%; animation: trail-fade .45s ease-out forwards; }
.trail-spark { width: 10px; height: 10px; margin: -5px 0 0 -5px; background: radial-gradient(circle, #fff, #ff8fa2 50%, #ff8fa200 75%); }
.trail-gold { width: 14px; height: 14px; margin: -7px 0 0 -7px; background: radial-gradient(circle, #fff8dc, #ffd77a 45%, #ffd77a00 75%); }
.trail-cyan { width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; background: radial-gradient(circle, #fff, #7ce9ff 50%, #7ce9ff00 75%); }
.trail-green { width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; background: radial-gradient(circle, #fff, #6ff0bd 50%, #6ff0bd00 75%); }
@keyframes trail-fade { to { opacity: 0; transform: translate(var(--x), var(--y)) scale(.2); } }
/* Impact: a bright flash and an expanding shock ring where something lands; the board takes a punch. */
.impact { position: fixed; left: 0; top: 0; z-index: 68; pointer-events: none; }
.impact-flash { width: 120px; height: 120px; margin: -60px 0 0 -60px; border-radius: 50%; background: radial-gradient(circle, #ffffffee, var(--c, #ffd77a) 35%, transparent 70%); animation: impact-flash .32s ease-out forwards; }
.impact-ring { width: 40px; height: 40px; margin: -20px 0 0 -20px; border-radius: 50%; border: 4px solid var(--c, #ffd77a); box-shadow: 0 0 18px var(--c, #ffd77a); animation: impact-ring .5s cubic-bezier(.2,.8,.2,1) forwards; }
@keyframes impact-flash { to { opacity: 0; transform: translate(var(--x), var(--y)) scale(1.6); } }
@keyframes impact-ring { to { opacity: 0; transform: translate(var(--x), var(--y)) scale(4); border-width: 1px; } }
.board-wrap.punch { animation: punch .3s ease-out; }
@keyframes punch { 30% { transform: scale(1.025); } }
body.reduced .trail, body.reduced .impact { display: none; }
.nova-chip { grid-area: 1 / 2; position: relative; margin: 0; padding: 5px 10px; border-radius: 12px; background: #7ce9ff14; border: 1px solid #7ce9ff44; color: var(--ink); font-size: 12.5px; line-height: 1.35; }
.nova-chip::before { content: ""; position: absolute; left: -7px; top: 14px; width: 12px; height: 12px; transform: rotate(45deg); background: #1d3a4e; border: 1px solid #7ce9ff44; border-right: 0; border-top: 0; border-radius: 0 0 0 3px; }
.nova-chip.bump { animation: rise .25s ease; }
.level-heading.talking .heading-text { display: none; }
.hud-stats { display: grid; grid-template-columns: 66px 1fr; gap: 12px; align-items: center; }
.hud-stats small { display: block; font-size: 9px; letter-spacing: .12em; color: var(--muted); }.hud-stats b { font-size: 23px; display: block; font-variant-numeric: tabular-nums; }
.moves-stat { border: 2px solid #ffd9fb; border-radius: 22px; text-align: center; background: linear-gradient(140deg,#b262c0,#6a3f9c); padding: 8px; box-shadow: 0 4px 0 #311e5a, 0 0 24px #b262c055; transition: box-shadow .3s; }
.moves-stat b { font-size: 30px; line-height: 1.1; }.moves-stat small { color: white; }.combo-stat { grid-column: span 2; }
@keyframes bump { 30% { transform: scale(1.18); } }
@keyframes alarm { 50% { box-shadow: 0 4px 0 #5a1e2e, 0 0 30px #ff5d73aa; border-color: #ffb3c0; } }
.moves-stat.bump b { animation: bump .35s ease; display: block; }
.moves-stat.low { background: linear-gradient(140deg,#d2496a,#8a2f52); animation: alarm 1s ease-in-out infinite; }
.combo-stat b { transition: color .2s; }.combo-stat.hot b { color: var(--gold); text-shadow: 0 0 14px var(--gold); }.combo-stat.bump b { animation: bump .4s ease; display: inline-block; }
.score-track { position: relative; margin-top: 6px; padding-top: 10px; }
.score-track progress { width: 100%; height: 6px; accent-color: var(--gold); display: block; border-radius: 3px; overflow: hidden; }
.star-marks { position: absolute; left: 0; right: 0; top: 0; height: 12px; pointer-events: none; }
.star-marks svg { position: absolute; top: -3px; width: 13px; height: 13px; margin-left: -6px; color: #ffffff3a; transition: color .3s, transform .3s; }
.star-marks svg.lit { color: var(--gold); filter: drop-shadow(0 0 6px var(--gold)); animation: bump .5s ease; }
.meter { position: relative; height: 14px; margin-top: 8px; border-radius: 7px; background: #ffffff14; overflow: hidden; }
.meter i { position: absolute; inset: 0; width: 0; border-radius: 7px; background: linear-gradient(90deg,#ff9c57,#ff79e5,#7ce9ff); transition: width .35s ease; box-shadow: 0 0 12px #ff79e5aa; }
.meter span { position: absolute; inset: 0; display: grid; place-items: center; font-size: 8px; letter-spacing: .2em; font-weight: 900; color: #fff; text-shadow: 0 1px 2px #000; }
body.fever .meter i { animation: fever-bar .5s linear infinite; background: linear-gradient(90deg,#ff9c57,#ffd35c,#ff79e5,#7ce9ff,#ff9c57); background-size: 200% 100%; }
@keyframes fever-bar { to { background-position: -200% 0; } }
body.fever .board-aura { background: radial-gradient(ellipse,#ff79e566,transparent 70%); animation: twinkle 1s ease-in-out infinite; }
body.fever #combo-stat b { color: var(--pink); text-shadow: 0 0 16px var(--pink); }
.stat-strip { display: grid; grid-template-columns: repeat(auto-fit,minmax(88px,1fr)); gap: 6px; margin: 0 0 12px; }
.stat-strip div { background: #ffffff0c; border-radius: 12px; padding: 8px 6px; text-align: center; }.stat-strip b { display: block; font-size: 17px; }.stat-strip small { font-size: 9px; letter-spacing: .1em; color: var(--muted); }
.reward { text-align: center; }.reward h2 { margin: 8px 0 14px; }
.streak { display: flex; justify-content: center; gap: 8px; margin-bottom: 16px; }
.streak i { width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; font-style: normal; font-size: 11px; font-weight: 800; background: #ffffff10; color: var(--muted); }
.streak i.done { background: var(--gold); color: #3a2600; box-shadow: 0 0 12px #ffd35c88; }.streak i.today { border: 2px solid var(--gold); color: white; animation: bump 1.2s ease-in-out infinite; }
.reward-box { display: flex; justify-content: center; gap: 10px; margin-bottom: 12px; }
.reward-box div { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 12px 16px; border-radius: 16px; background: #ffd35c14; border: 1px solid #ffd35c55; font-size: 12px; animation: starPop .6s cubic-bezier(.3,1.4,.5,1) both; }
.reward-box b { font-size: 22px; color: var(--gold); }.reward-box svg { color: var(--c); filter: drop-shadow(0 0 8px var(--c)); }
.share { background: #2f6a5a; border-color: #95f4bb66; }
.goals { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 14px; }
.goal { flex: 1 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 8px; background: #061d2b66; padding: 5px 10px 5px 5px; border: 1px solid #ffffff15; border-radius: 14px; font-size: 12px; transition: background .3s, border-color .3s; }
.goal b { display: flex; align-items: center; gap: 5px; }.goal canvas { width: 30px; height: 30px; display: block; }
.goal span { color: var(--gold); font-variant-numeric: tabular-nums; white-space: nowrap; }
.goal.bump { animation: bump .35s ease; }
.goal.done { background: #2b7c5c55; border-color: #95f4bb; }.goal.done span { color: #baffcf; display: inline-flex; align-items: center; gap: 4px; }
.board-wrap { position: relative; width: 100%; height: 100%; min-width: 0; min-height: 0; align-self: stretch; overflow: hidden; }
.board-aura { position: absolute; inset: 8% 0; background: radial-gradient(ellipse,#9b7bf042,transparent 70%); pointer-events: none; }
#board { position: absolute; inset: 0; display: block; width: 100%; height: 100%; touch-action: none; user-select: none; -webkit-user-select: none; }
@keyframes shake { 0%, 100% { transform: none; } 20% { transform: translate(-5px,3px); } 40% { transform: translate(5px,-3px); } 60% { transform: translate(-4px,-2px); } 80% { transform: translate(3px,2px); } }
.board-wrap.shake { animation: shake .4s ease; }
.banner { position: absolute; left: 0; right: 0; top: 30%; text-align: center; pointer-events: none; opacity: 0; font-weight: 900; font-size: clamp(28px,7vw,52px); letter-spacing: -.02em; color: white; -webkit-text-stroke: 1px #0006; text-shadow: 0 4px 18px #000a, 0 0 26px var(--banner-glow,#7ce9ff), 0 0 60px var(--banner-glow,#7ce9ff); transform-origin: center; }
.banner.callout { font-style: italic; font-size: clamp(34px,9vw,64px); background: linear-gradient(180deg,#fff,var(--banner-glow,#ffd35c)); -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-stroke: 0; filter: drop-shadow(0 4px 12px #000a) drop-shadow(0 0 22px var(--banner-glow,#ffd35c)); }
.banner b { display: block; font-size: .42em; letter-spacing: .3em; color: var(--banner-glow,#7ce9ff); }
/* 1.9 s with the hold from 10 % to 82 % leaves about 1.4 s fully legible. At 1.1 s the readable window was
   barely half a second, which is not enough for two lines. */
@keyframes bannerIn { 0% { opacity: 0; transform: scale(.55) rotate(-5deg); } 10% { opacity: 1; transform: scale(1.06) rotate(1deg); } 18% { transform: scale(1) rotate(0); } 82% { opacity: 1; transform: translateY(0) scale(1); } 100% { opacity: 0; transform: translateY(-26px) scale(1.04); } }
.banner.show { animation: bannerIn 1.9s cubic-bezier(.2,.8,.2,1) forwards; }
.level-intro { position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); width: min(88%,360px); background: #16123bf0; border: 1px solid #b2a2d855; border-radius: 24px; padding: 18px 20px; text-align: center; box-shadow: 0 24px 70px #000a; pointer-events: auto; }
.level-intro h3 { margin: 6px 0 4px; font-size: 26px; }.level-intro p { margin: 0; color: var(--muted); font-size: 13px; }
.intro-goals { display: flex; justify-content: center; gap: 10px; margin-top: 12px; flex-wrap: wrap; }
.intro-goals span { display: inline-flex; align-items: center; gap: 4px; font-weight: 800; color: var(--gold); font-size: 14px; }.intro-goals canvas { width: 34px; height: 34px; }
@keyframes introIn { from { opacity: 0; transform: translate(-50%,-50%) scale(.7); } to { opacity: 1; transform: translate(-50%,-50%) scale(1); } }
@keyframes introOut { to { opacity: 0; transform: translate(-50%,-60%) scale(.92); } }
.level-intro.show { animation: introIn .4s cubic-bezier(.3,1.4,.5,1) both; }.level-intro.leave { animation: introOut .35s ease forwards; }
.side-card { align-self: center; color: var(--muted); font-size: 14px; line-height: 1.7; }.side-card h2 { color: white; line-height: 1.3; }
.legend { display: grid; gap: 8px; color: var(--gold); }.legend span { display: flex; align-items: center; gap: 8px; }.legend i { width: 10px; height: 10px; border-radius: 50%; background: var(--c); box-shadow: 0 0 10px var(--c); }
/* Pause und Vollbild sind keine Spielaktionen, sondern Rahmen — sie stehen still in der Kopfzeile. */
.head-tools { grid-area: 1 / 3; display: flex; gap: 6px; align-self: center; }
.head-tools .round-button { width: 34px; height: 34px; background: #ffffff0c; }
.head-tools svg { opacity: .7; }@media(hover:hover) { .head-tools .round-button:hover svg { opacity: 1; } }
/* Desktop-only pieces: the round panel under the HUD, the keyboard help, the key badges on the controls and the
   numbers strip in the menu. Phones and tablets never see them (touch has no keys, and the columns are not there). */
.run-panel, .menu-stats { display: none; }
/* Novas Marke: das Zeichen dessen, was ein Tipp auf sie gerade tut. Sie sitzt auf ihrem Kreis, nimmt die Farbe
   der Aktion an und atmet — der einzige Knopf, den das Spiel im Lauf noch hat. */
.nova-badge { position: absolute; right: -3px; bottom: -3px; width: 23px; height: 23px; border-radius: 50%; display: grid; place-items: center; background: var(--c,#7ce9ff); color: #0d1524; box-shadow: 0 2px 8px #0009, 0 0 14px var(--c,#7ce9ff); animation: breathe-badge 1.5s ease-in-out infinite; }
.nova-badge svg { width: 14px; height: 14px; }
.reduced .nova-badge { animation: none; }
@keyframes breathe-badge { 50% { transform: scale(1.14); box-shadow: 0 2px 8px #0009, 0 0 22px var(--c,#7ce9ff); } }
/* Steht ein Angebot, trägt ihre Sprechblase die Farbe der Aktion — ohne dass die Zeile umspringt. */
.nova-chip.offer { background: color-mix(in srgb, var(--c,#7ce9ff) 12%, transparent); border-color: color-mix(in srgb, var(--c,#7ce9ff) 55%, transparent); }
.nova-chip .confirm { display: block; margin-top: 2px; font-size: 11px; font-weight: 700; color: var(--c,#7ce9ff); }

/* Desktop: status on the left, a centered board, and supporting information on the right.
   Side columns have equal widths; each panel can scroll without shrinking the board. */
@media(min-width:1001px) {
  .game { grid-template-columns: minmax(200px,20vw) minmax(0,1fr) minmax(200px,20vw); grid-template-rows: repeat(2,minmax(0,1fr)); gap: 16px 24px; padding: 6px 24px 22px; }
  .hud-panel { grid-area: 1 / 1 / 3 / 2; align-self: center; max-height: 100%; overflow: auto; padding: 22px; border-radius: 28px; }
  /* Equal side columns center the board. Round history and the legend share the right column,
     grouped around its midpoint without reserving space for the removed booster controls. */
  .run-panel { display: block; grid-area: 1 / 3; align-self: end; max-height: 100%; overflow: auto; padding: 16px 18px 14px; border-radius: 24px; background: var(--glass); border: 1px solid var(--line); }
  .run-stats { margin: 10px 0; grid-template-columns: repeat(3,1fr); }.run-stats b { font-size: 18px; }.run-stats b.beat { color: var(--gold); text-shadow: 0 0 12px #ffd77a88; }
  .run-feed { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; font-size: 12px; }
  .run-feed li { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; padding: 4px 8px 4px 9px; border-radius: 8px; background: #ffffff09; border-left: 3px solid var(--c,#7ce9ff); white-space: nowrap; overflow: hidden; animation: rise .3s ease backwards; }
  .run-feed b { flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; letter-spacing: .02em; }.run-feed span { flex: 1 1 auto; min-width: 0; text-align: right; color: var(--muted); font-size: 11px; overflow: hidden; text-overflow: ellipsis; }
  .run-feed:empty::before { content: "Deine Spezialsteine und Ketten landen hier."; display: block; padding: 6px 2px; color: var(--muted); font-size: 12px; }
  .level-heading { grid-template-columns: 56px minmax(0,1fr); grid-template-rows: 56px 88px; margin-bottom: 16px; row-gap: 8px; }
  .heading-text, .nova-chip { grid-area: 2 / 1 / 3 / 3; max-height: 88px; overflow: auto; }
  .head-tools { grid-area: 1 / 2; justify-self: end; }.nova-hud { width: 56px; height: 56px; }.level-heading h2 { font-size: 24px; }.nova-chip { font-size: 13.5px; }
  .hud-stats { grid-template-columns: 78px 1fr; gap: 14px; }.moves-stat b { font-size: 34px; }.score-stat b { font-size: 26px; }
  .goals { gap: 8px; margin-top: 16px; }.goal { font-size: 13px; padding: 6px 12px 6px 6px; }.goal canvas { width: 34px; height: 34px; }
  .board-wrap { grid-area: 1 / 2 / 3 / 3; padding: 0; }
  /* Both supporting cards scroll within their own half when the window is short. */
  .side-card { grid-area: 2 / 3; align-self: start; max-height: 100%; overflow: auto; overscroll-behavior: contain; padding: 18px 20px; border-radius: 22px; background: var(--glass); border: 1px solid var(--line); font-size: 13px; line-height: 1.6; }
  .side-card h2 { font-size: 18px; margin: 4px 0 8px; }.side-card .eyebrow { font-size: 9px; }.legend { gap: 6px; font-size: 12px; }
  .level-intro { width: min(88%,420px); padding: 22px 26px; }.level-intro h3 { font-size: 30px; }.level-intro p { font-size: 14px; }.intro-goals canvas { width: 40px; height: 40px; }.intro-goals span { font-size: 15px; }
  /* Menu: title and play on the left, the mode cards as a proper panel on the right. */
  .menu { padding: 10px 40px 24px; }
  .hero { width: min(100%,1080px); display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1.15fr); grid-template-rows: auto auto auto auto auto; column-gap: 56px; row-gap: 12px; align-items: center; text-align: left; justify-items: start; }
  .hero-nova { grid-column: 1; justify-self: start; margin-left: -6px; }.nova-hero { width: 150px; height: 150px; }
  .hero h1 { grid-column: 1; font-size: clamp(52px,5.6vw,84px); }.hero-line { grid-column: 1; font-size: 16px; margin-bottom: 4px; }.hero-line::before { left: 40px; }
  .play-button { grid-column: 1; min-height: 88px; padding: 12px 26px; border-radius: 28px; }.play-button strong { font-size: 32px; }.play-button small { font-size: 14px; }
  .menu-stats { grid-column: 1; display: grid; grid-template-columns: repeat(4,1fr); gap: 8px; width: 100%; }
  .menu-stats div { background: #ffffff0c; border: 1px solid #ffffff12; border-radius: 14px; padding: 8px 6px; text-align: center; }
  .menu-stats b { display: block; font-size: 20px; line-height: 1.15; font-variant-numeric: tabular-nums; }.menu-stats small { display: block; font-size: 9px; letter-spacing: .1em; color: var(--muted); white-space: nowrap; }
  .menu-grid { grid-column: 2; margin: 0; align-self: center; gap: 14px; }
  .mode { min-height: 118px; padding: 18px 18px 18px 20px; border-radius: 24px; grid-template-columns: 44px 1fr; }.mode-icon { width: 40px; height: 40px; }
  .mode strong { font-size: 20px; }.mode span { font-size: 13px; margin-top: 4px; }.mode-wide { min-height: 76px; }
  .menu-tools { grid-column: 1 / 3; justify-content: flex-start; gap: 18px; }.menu-tools button, .menu-tools a { font-size: 14px; }
  /* Campaign: the winding path on the left, achievements beside it instead of below. */

  #progress-screen .list-scroll { display: grid; grid-template-rows: minmax(0,1fr); column-gap: 40px; align-content: start; }
  /* Am Rechner gehören sie optisch zur Levelkarte, also auf deren Breite. */
  #progress-screen .head-books { max-width: 560px; }
  #progress-screen .level-map { grid-column: 1; width: 100%; margin: 0; }
  #progress-screen .list-subhead { margin-top: 4px; }#progress-screen .achievements { align-content: start; grid-template-columns: 1fr; }
  .node { --w: 112px; }.node-disc { width: 64px; height: 64px; }.node-disc strong { font-size: 24px; }.node-name { font-size: 11px; }.map-prop.mark { width: 104px; height: 104px; }.map-prop.far { width: 66px; height: 66px; }
  .leader-list .row { padding: 14px 20px; grid-template-columns: 48px 1fr auto; }.leader-list .rank { width: 40px; height: 40px; font-size: 15px; }.leader-list .pts { font-size: 18px; }
  .board-tabs button { flex: 0 1 auto; padding: 10px 22px; font-size: 14px; }

}
/* Small desktop windows keep full labels without borrowing space from neighboring cards. */
@media(min-width:1001px) and (max-width:1279px) and (min-height:601px) {
  .mode { grid-template-columns: 32px minmax(0,1fr); padding: 14px 12px; min-height: 110px; }
  .mode-icon { width: 30px; height: 30px; }.mode strong { font-size: 16px; }
}
/* Wide desktop screens: the side columns grow with the window instead of leaving the board alone in the middle. */
@media(min-width:1600px) {
  /* 🚨 DIE MITTELSPALTE DARF NICHT DEN GANZEN REST NEHMEN. Gemeldet: „das sieht am pc sehr gestreckt aus."
     Mit `minmax(0,1fr)` bekam sie auf einem 2000 px breiten Fenster 1120 px — das BRETT ist aber immer ein
     Quadrat und damit höchstens so breit wie hoch (dort 664 px). Der Überschuss war unsichtbare Leere IN
     der Mittelspalte, und weil die Randspalten am Rand der Spalte kleben, wurden die beiden Karten dadurch
     an die äußersten Ränder des Fensters geschoben: links Zahlen, rechts Legende, dazwischen ein halber
     Meter Nichts. Nicht das Brett war gestreckt, sondern alles darum herum.

     Die Mitte wird deshalb bei `94dvh` gedeckelt — das ist die Höhe, die dem Brett tatsächlich zur
     Verfügung steht, also genau seine größtmögliche Breite. Was dann übrig bleibt, verteilt
     `justify-content: center` nach AUSSEN, statt es in die Mitte zu stopfen: Die drei Spalten rücken
     zusammen und stehen als Block in der Mitte des Fensters. `minmax(0,…)` lässt die Mitte weiterhin
     schrumpfen, wenn das Fenster schmal ist — sonst würde sie auf einem flachen, breiten Schirm die
     Randspalten hinausdrücken. */
  .game { grid-template-columns: minmax(calc(300px * var(--ui,1)),calc(360px * var(--ui,1))) minmax(0,84dvh) minmax(calc(300px * var(--ui,1)),calc(360px * var(--ui,1)));
          justify-content: center; gap: calc(16px * var(--ui,1)) calc(32px * var(--ui,1)); padding: 8px 48px 28px; }
  /* 🚨 DIE KARTEN WACHSEN MIT, NICHT NUR IHRE SPALTE. Gemeldet: „das spielfeld ist rießig der rest ist klein".
     Eine breitere Spalte allein hätte nur mehr Luft um dieselbe 12-px-Schrift gelegt. `zoom` vergrößert
     alles darin — Zahlen, Ziele, Nova in der Kopfzeile, Abstände — im selben Verhältnis wie die Spalte, und
     anders als `transform` rechnet das Raster mit der neuen Größe. `--ui` setzt `uiScale()` in `app.mjs`:
     1 bis zu dem Maß, für das die Karten gebaut sind, darüber bis 1,8. Das Brett bekommt dafür höchstens
     84 % der Höhe statt 94 %: Es bleibt der größte Teil, aber nicht mehr der einzige. */
  .hud-panel, .run-panel, .side-card { zoom: var(--ui,1); }
  .run-feed { font-size: 13px; }.run-feed span { font-size: 12px; }
  .hero { width: min(100%,1180px); column-gap: 72px; }.level-intro { width: min(80%,460px); }
}
/* ── Lists ────────────────────────────────────────────────────────────── */
.screen-list { flex-direction: column; width: min(100%,940px); align-self: center; padding: 8px 18px 14px; }
.list-head { animation: rise .5s cubic-bezier(.2,.8,.2,1) backwards; }
.screen-list h1 { font-size: clamp(26px,4vw,44px); margin: 6px 0 4px; }.list-summary { margin: 0 0 12px; color: var(--muted); font-size: 13px; }
.list-subhead { font-size: 15px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); margin: 22px 0 10px; }
.list-scroll { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 4px 4px 12px; }
/* ── Die Levelkarte ──────────────────────────────────────────────────────
   Eine STRASSE durch Novas Welt, keine Liste: breiter Belag mit dunkler Kante und Mittellinie, runde Münzen
   darauf, Sterne direkt darunter, und dahinter je Kapitel ein getöntes Band mit seinem Wahrzeichen. */
.level-map { position: relative; width: min(100%,560px); margin: 0 auto; }
/* 🚨 HÖHE KOMMT VOM INHALT, NICHT VON `100%`. Am Rechner rollt `.level-map` SELBST (`overflow-y:auto`), dort
   sind 100 % nur das sichtbare Fenster — Straße und Landschaft klebten oben und waren beim Scrollen weg. Beide
   Lagen bekommen ihre Pixelhöhe deshalb aus `renderProgress()`, genau wie die Münzen. */
/* 🚨 KEIN KASTEN UM DEN PFAD. Gemeldet: „die deutliche Pfad-Abgrenzung sollte weggehen! Aktuell sieht man
   deutlich ein Viereck um den ganzen Pfad." Die Landschaft war auf die Breite der Karte begrenzt und endete
   an einer harten Kante — auf dem breiten Bildschirm stand dadurch eine getönte Säule im dunklen Raum. Jetzt
   greift sie über die Karte hinaus und blendet nach links und rechts aus; die Bänder tun das oben und unten
   schon. Damit gibt es keine Kante mehr, an der sie aufhört. */
.level-scenery { position: absolute; left: 0; top: 0; right: 0; pointer-events: none; overflow: hidden;
  /* Der Verlauf muss INNERHALB der Karte auf null sein: Am Rechner rollt `.level-map` selbst, und `overflow-y`
     macht `overflow-x` mit — die Landschaft wird also genau an der Kartenkante abgeschnitten. Ein Überstand
     hilft da nichts, ein früh auslaufender Verlauf schon. Die zweite Lage nimmt dem allerersten Band die
     Oberkante. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 38%, #000 62%, transparent), linear-gradient(180deg, transparent, #000 150px);
  mask-image: linear-gradient(90deg, transparent, #000 38%, #000 62%, transparent), linear-gradient(180deg, transparent, #000 150px);
  -webkit-mask-composite: source-in; mask-composite: intersect; }
.map-band { --c: var(--cyan); position: absolute; left: 0; right: 0;
  background: radial-gradient(120% 60% at 15% 12%, color-mix(in srgb, var(--c) 26%, transparent), transparent 70%),
              radial-gradient(110% 55% at 88% 78%, color-mix(in srgb, var(--c) 20%, transparent), transparent 72%),
              linear-gradient(180deg, color-mix(in srgb, var(--c) 13%, transparent), transparent 60%);
  /* Die Gegenden gehen ineinander über statt an einer harten Kante zu wechseln. */
  -webkit-mask-image: linear-gradient(180deg, transparent, #000 14%, #000 86%, transparent);
  mask-image: linear-gradient(180deg, transparent, #000 14%, #000 86%, transparent); }
/* Ein gesperrtes Kapitel behält seine Farbe: Man soll sehen, wohin die Reise geht, nur eben gedämpft. */
.map-band.locked { filter: saturate(.6); opacity: .72; }
.map-prop { --c: var(--cyan); position: absolute; color: var(--c); opacity: .38; }
.map-prop svg { width: 100%; height: 100%; display: block; filter: drop-shadow(0 0 14px color-mix(in srgb, var(--c) 45%, transparent)); }
.map-prop.mark { width: 86px; height: 86px; }
.map-prop.far { width: 54px; height: 54px; opacity: .24; }
.level-path { position: absolute; left: 0; top: 0; width: 100%; pointer-events: none; }
.level-path path { fill: none; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.level-path .edge { stroke: #0a0a1f; stroke-width: 30; }
.level-path .road { stroke: #3b3466; stroke-width: 24; }
.level-path .dashes { stroke: #ffffff1f; stroke-width: 3; stroke-dasharray: 5 13; }
.level-path .edge.done { stroke: #6a4a05; }
.level-path .road.done { stroke: url(#roadgold); stroke: #e3aa27; filter: drop-shadow(0 0 12px #ffd35c55); }
.level-path .dashes.done { stroke: #fff3c8aa; }
.level-nodes { position: relative; }
.node { --i: 0; --w: 96px; --c: var(--cyan); position: absolute; width: var(--w); margin-left: calc(var(--w) / -2); display: grid; justify-items: center; gap: 3px; padding: 0; border: 0; background: none; box-shadow: none; animation: rise .45s cubic-bezier(.2,.8,.2,1) backwards; animation-delay: calc(var(--i) * 40ms); }
/* Die Münze ist das einzige dicke Ding auf der Seite — hier soll es knallen, anders als in der Leiste im Spiel. */
.node-disc { display: grid; place-items: center; width: 58px; height: 58px; border-radius: 50%; background: radial-gradient(circle at 32% 24%,#8fefff,#2183b3 72%); box-shadow: 0 5px 0 #08283a, 0 0 0 4px #ffffff14, 0 0 22px #7ce9ff44, inset 0 -4px 0 #0000002e; }
.node-disc strong { font-size: 22px; color: #06222f; text-shadow: 0 1px 0 #ffffff5c; }
.node-disc .lock { color: #ffffff6b; }
.node-stars { display: flex; gap: 1px; padding: 2px 5px; border-radius: 9px; background: #0d0a26cc; box-shadow: 0 1px 0 #0006; margin-top: -8px; }
.node-stars svg { width: 13px; height: 13px; color: #ffffff2b; }.node-stars svg.lit { color: var(--gold); filter: drop-shadow(0 0 4px var(--gold)); }
.node-name { font-size: 10px; font-weight: 700; line-height: 1.15; color: #cfc9ef; text-align: center; text-shadow: 0 1px 3px #000, 0 0 9px #000c; }
.node.done .node-disc { background: radial-gradient(circle at 32% 24%,#ffe9a3,#dc9d16 72%); box-shadow: 0 5px 0 #6a4a05, 0 0 0 4px #ffffff1a, 0 0 22px #ffd35c55, inset 0 -4px 0 #0000002e; }
.node.done .node-disc strong { color: #4a3100; }
.node.current { z-index: 2; }
.node.current .node-disc { width: 68px; height: 68px; background: radial-gradient(circle at 32% 24%,#ffffff,#7ce9ff 40%,#1b9fd4 78%); box-shadow: 0 6px 0 #08283a, 0 0 0 5px var(--gold), 0 0 34px #ffd35c77, inset 0 -4px 0 #0000002e; animation: breathe-node 1.6s ease-in-out 1s infinite; }
.node.current .node-disc strong { font-size: 26px; }
.node.current .node-name { color: var(--gold); font-size: 11px; }
@keyframes breathe-node { 50% { box-shadow: 0 3px 0 #061824, 0 0 34px #ffd35caa; } }
/* Auch eine gesperrte Münze trägt die Farbe ihres Kapitels — die Straße vorn liest sich dann als Gegenden. */
.node.locked { opacity: .72; }.node.locked .node-disc { background: radial-gradient(circle at 32% 24%, color-mix(in srgb, var(--c) 34%, #453d6d), #262045 74%); box-shadow: 0 4px 0 #14102c, inset 0 -3px 0 #0000002e; }
@media(hover:hover) { .node:hover:not(:disabled) .node-disc { transform: translateY(-2px); } }.node .node-disc { transition: transform .12s ease; }
.level-card { --i: 0; min-height: 88px; border-radius: 18px; padding: 12px; text-align: left; background: linear-gradient(150deg,#3b3160,#221c46); display: grid; grid-template-columns: 36px 1fr; gap: 2px 8px; align-items: center; animation: rise .45s cubic-bezier(.2,.8,.2,1) backwards; animation-delay: calc(var(--i) * 40ms); box-shadow: 0 3px 0 #061824; }
@media(hover:hover) { .level-card:hover:not(:disabled) { transform: translateY(-2px); } }
.level-card strong { color: var(--cyan); font-size: 24px; grid-row: span 2; }.level-card span { font-size: 13px; font-weight: 700; }
.level-card em { font-style: normal; display: flex; gap: 2px; align-items: center; font-size: 11px; color: var(--muted); }
.level-card em svg { width: 15px; height: 15px; color: #ffffff30; }.level-card em svg.lit { color: var(--gold); filter: drop-shadow(0 0 4px var(--gold)); }
.level-card.current { border-color: var(--gold); box-shadow: 0 3px 0 #061824, 0 0 22px #ffd35c55; }
.level-card.locked strong { color: #ffffff40; }.level-card.locked svg.lock { color: #ffffff50; }
.achievements { display: grid; grid-template-columns: repeat(auto-fill,minmax(180px,1fr)); gap: 8px; }
.ach { --i: 0; display: grid; grid-template-columns: 38px 1fr; gap: 10px; align-items: center; padding: 10px; border: 1px solid var(--line); border-radius: 14px; color: var(--muted); font-size: 12px; animation: rise .45s cubic-bezier(.2,.8,.2,1) backwards; animation-delay: calc(var(--i) * 40ms + 200ms); }
.ach i { display: grid; place-items: center; width: 38px; height: 38px; border-radius: 50%; background: #ffffff0c; color: #ffffff50; }
.ach b { display: block; color: #ffffffb0; }.ach.earned { color: var(--ink); border-color: #ffd35c66; background: #ffd35c12; }.ach.earned i { background: #ffd35c22; color: var(--gold); box-shadow: 0 0 16px #ffd35c66; }.ach.earned b { color: var(--gold); }
/* Leaderboard tabs: four equal cells, a short word on top and the long name small underneath, nothing may overflow. */
.board-tabs { display: grid; grid-template-columns: repeat(4,minmax(0,1fr)); gap: 6px; margin-bottom: 8px; }
.board-tabs button { min-width: 0; display: grid; gap: 1px; padding: 7px 4px; min-height: 48px; border-radius: 14px; background: var(--panel); line-height: 1.15; }
.board-tabs b { font-size: 13px; font-weight: 800; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }.board-tabs small { font-size: 9.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; letter-spacing: .02em; }
.board-tabs .active { color: #161132; background: var(--cyan); box-shadow: 0 0 18px #7ce9ff66; }.board-tabs .active small { color: #161132aa; }
.board-scope { margin: 0 0 10px; color: var(--muted); font-size: 12.5px; }
/* Empty state: Nova and one clear call to action instead of a bare sentence. */
.board-empty { display: grid; justify-items: center; gap: 8px; margin: 12px auto 0; max-width: 360px; padding: 18px 16px; border-radius: 20px; background: var(--panel); border: 1px solid var(--line); text-align: center; animation: rise .4s ease backwards; }
.board-empty .nova-small { width: 64px; height: 64px; }.board-empty p { margin: 0; color: var(--ink); font-size: 14px; line-height: 1.45; }.board-empty .primary { min-height: 46px; padding: 8px 18px; margin-top: 4px; }
.board-empty.offline .primary { display: none; }
.leader-list .row { --i: 0; display: grid; grid-template-columns: 40px 1fr auto; gap: 10px; align-items: center; padding: 10px 14px; background: var(--panel); border-radius: 14px; margin-bottom: 8px; animation: rise .4s cubic-bezier(.2,.8,.2,1) backwards; animation-delay: calc(var(--i) * 30ms); }
.leader-list .row.me { background: linear-gradient(90deg,#2f6a5a,#221c46); border: 1px solid #95f4bb88; }
.leader-list .rank { display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%; font-weight: 900; background: #ffffff10; font-size: 13px; }
.rank.r1 { background: linear-gradient(#ffe8a3,#e0a41f); color: #4a3100; box-shadow: 0 0 14px #ffd35c88; }.rank.r2 { background: linear-gradient(#f4f4f4,#a9adb8); color: #2c2f38; }.rank.r3 { background: linear-gradient(#f5c29c,#b26a3a); color: #3e1f08; }
.leader-list .who { min-width: 0; }.leader-list .who b { display: block; overflow-wrap: anywhere; }.leader-list .who small { color: var(--muted); font-size: 11px; }
.leader-list .pts { color: var(--gold); font-weight: 900; font-variant-numeric: tabular-nums; text-align: right; }.leader-list .pts small { display: block; color: var(--muted); font-weight: 400; font-size: 10px; }
.leader-list p { color: var(--muted); }
.tag { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--cyan); font-size: 12px; letter-spacing: .08em; }
.board-me { flex: 0 0 auto; margin-top: 8px; padding: 10px 14px; border-radius: 16px; background: #102e40d0; border: 1px solid #95f4bb55; display: flex; justify-content: space-between; align-items: center; gap: 10px; font-size: 13px; }
.board-me b { color: var(--green); }.board-me.name { display: block; }.board-me label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 6px; }.board-me .code-row { flex: 1; margin: 0; }.board-me .code-row input { flex: 1; min-width: 0; }.board-me .hint { display: block; margin-top: 6px; color: var(--muted); font-size: 11px; }

/* ── Dialogs ──────────────────────────────────────────────────────────── */
/* Breite UND Höhe lassen die sicheren Ränder frei. Quer am Handy liegt die Aussparung links oder rechts,
   also begrenzt `max-width` das Blatt — sonst steht ein 92-vw-Blatt auf einem 844-px-Gerät mit 59 px
   Aussparung mit seinem Rand genau darunter. */
dialog { width: min(92vw,460px); max-width: calc(100vw - var(--sa-links) - var(--sa-rechts) - 24px); max-height: calc(var(--vvh,100dvh) - 40px - var(--sa-oben) - var(--sa-unten)); overflow-y: auto; border: 1px solid #a2cdd866; border-radius: 26px; padding: 24px; background: #221c46; color: var(--ink); box-shadow: 0 24px 90px #0008; }
/* 🚨 EIN BLATT MIT TASTATUR HÄNGT OBEN, NICHT IN DER MITTE. Gemeldet: „der Dialog wo man für Nova seinen Namen
   einträgt scrollt die UI unnötig nach oben mobil auf iOS". Ein `dialog` sitzt in der Mitte der LAYOUT-Fläche;
   die Tastatur verkleinert aber nur die SICHTBARE Fläche, also steht das Blatt zur Hälfte dahinter, und iOS hebt
   dann die ganze Seite an, um das Feld freizulegen. `--vvh` (aus `visualViewport.height`, siehe app.mjs) begrenzt
   die Höhe auf das, was übrig ist, und solange getippt wird, hängt das Blatt oben. */
body.typing dialog[open] { margin-top: max(10px, var(--sa-oben)); margin-bottom: auto; }
body.typing .settings { border-radius: 26px; }
@keyframes sheetIn { from { opacity: 0; transform: translateY(30px) scale(.94); } to { opacity: 1; transform: none; } }
@keyframes fadeIn { from { opacity: 0; } }
dialog[open] { animation: sheetIn .35s cubic-bezier(.2,.9,.3,1.1); }
/* The story sheet only fades: Nova flies into it as a fixed element, and a transform on the sheet during its
   entrance would become her containing block and bend the flight. */
.story[open] { animation: fadeIn .3s ease; }
/* No backdrop-filter here: iOS Safari sometimes keeps the blurred backdrop layer painted after a dialog closes, which
   left the whole screen behind it soft. A plain darkening never lingers. */
dialog::backdrop { background: #08051bd6; animation: fadeIn .3s ease; }
dialog h2 { font-size: 30px; margin: 10px 0 20px; }
.settings { padding: 0 0 16px; }.settings h2 { margin: 2px 0 0; font-size: 26px; }.sheet-head { position: sticky; top: 0; z-index: 2; display: flex; justify-content: space-between; align-items: center; padding: 18px 20px 12px; background: linear-gradient(#221c46 70%,#221c4600); }
/* 🚨 DER KOPF EINES BLATTS BLEIBT STEHEN — WEGEN NOVA. Gemeldet: „Die Header sollten sich nicht mit scrollen,
   denn das sorgt dafür, dass Nova sich sonst bewegt!" Ihr Dock sitzt in der Kopfzeile (`data-nova-dock`), und die
   Bühne misst dessen Lage in JEDEM Bild neu (`stage.mjs`, `track()`). Rollt der Kopf weg, zieht sie mit nach oben
   aus dem Bild — sie klebt ja am Dock. `.sheet-head` klebte schon, Novas Zeile darunter aber nicht; genau die
   trägt das Dock. Beide stehen jetzt zusammen in `.sheet-top`, und das klebt als Ganzes. */
.sheet-top { position: sticky; top: 0; z-index: 3; background: #221c46; }
.sheet-top .sheet-head { background: none; }
/* 🚨 KEIN SAUM UNTER DEM KOPF. Ein verlaufender Streifen darunter sah beim Rollen hübsch aus, lag aber auch im
   Ruhezustand über dem ersten Absatz und liess dessen erste Zeile verblassen. Gemeldet: „Hier ist der erste Satz
   aber nicht komplett sichtbar". Der Kopf deckt jetzt schlicht, und darunter beginnt der Inhalt.
   Ebenso kein negativer Rand am Kopf: Der zog den Fluss um 24 px hoch, sodass der erste Absatz UNTER dem
   stehenden Kopf begann und dort verschwand. Spielregeln und Pause tragen ihren Innenabstand deshalb im
   Kopf und im Rumpf (`.sheet-body`), nicht mehr im Blatt selbst — so wie die anderen Blätter es schon tun. */
#help-dialog, #pause-dialog { padding: 0 0 20px; }
#pause-dialog .sheet-top { padding: 24px 24px 12px; }
/* Die Spielregeln tragen jetzt dieselbe Kopfzeile wie die anderen Blätter, mitsamt Schliessen-Knopf. Der ist
   zugleich das erste fokussierbare Element, sodass der Fokus beim Öffnen dort landet statt im Text. */
#help-dialog .sheet-head { padding: 24px 24px 12px; }
/* Kleiner als die 30 px eines gewöhnlichen Blatt-Titels: Neben dem Schliessen-Knopf bliebe sonst zu wenig Breite,
   und „So wird gespielt" bräche auf zwei Zeilen. Dieselbe Größe wie in den Einstellungen. */
#help-dialog .sheet-head h2 { font-size: 26px; margin: 2px 0 0; }
#help-dialog .sheet-top .nova-line { margin: 0 24px 12px; }
#help-dialog .sheet-top h2, #pause-dialog .sheet-top h2 { margin-bottom: 12px; }
.sheet-body { padding: 0 24px; }
.sheet-body > :first-child { margin-top: 0; }
/* Sehr flache Fenster im Querformat: Dort frisst ein stehender Kopf zu viel Höhe. Er bleibt trotzdem stehen —
   gerade dort rollt am meisten (fast 900 px), Nova würde also am weitesten mitwandern. Statt ihn rollen zu lassen,
   wird er schlank: kleinerer Titel, engere Zeile, kleinere Figur. */
@media (orientation: landscape) and (max-height: 520px) {
  .sheet-top .sheet-head { padding: 10px 16px 6px; }
  .sheet-top h2 { font-size: 20px; }
  .sheet-top .nova-line { font-size: 12px; padding: 4px 10px 4px 4px; margin-bottom: 4px; }
  .sheet-top .nova-small { width: 34px; height: 34px; }
  #pause-dialog .sheet-top { padding: 14px 24px 8px; }
  #help-dialog .sheet-head { padding: 14px 24px 6px; }
}
.group { padding: 4px 20px 10px; }.group h3 { margin: 10px 0 6px; font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); display: flex; align-items: center; gap: 6px; }
.row { display: flex; align-items: center; gap: 12px; min-height: 60px; padding: 8px 10px; border-radius: 16px; background: #ffffff08; margin-bottom: 6px; cursor: pointer; }@media(hover:hover) { label.row:hover { background: #ffffff10; } }
.row-icon { display: grid; place-items: center; width: 36px; height: 36px; border-radius: 12px; background: color-mix(in srgb, var(--c) 18%, transparent); color: var(--c); flex: 0 0 auto; }
.row-body { flex: 1; min-width: 0; }.row-body span, .row-body label { display: block; font-weight: 700; font-size: 14px; }.row-body small { display: block; color: var(--muted); font-size: 11px; margin-top: 2px; }
.row-body .name-field { width: 100%; margin-top: 6px; }.settings .device-box { margin: 6px 20px 10px; padding: 12px 14px; }.settings .device-box p { font-size: 12px; margin: 4px 0 10px; }.settings .primary { width: calc(100% - 40px); margin: 6px auto 0; }.settings .danger { margin-top: 0; }
.inventory { display: grid; grid-template-columns: repeat(3,1fr); gap: 8px; }.inventory div { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 10px 6px; border-radius: 14px; background: #ffffff08; font-size: 11px; color: var(--muted); }.inventory b { font-size: 20px; color: white; }.inventory b i { font-style: normal; font-size: 12px; font-weight: 700; color: var(--muted); }.inventory div.full b i { color: var(--gold); }.inventory div.full { background: #ffffff12; box-shadow: inset 0 0 0 1px var(--c); }.inventory svg { color: var(--c); filter: drop-shadow(0 0 6px var(--c)); }
/* 🚨 DIESE REGEL MUSS HINTER `.inventory` STEHEN — und zwar genau deshalb, weil sie dieselbe Spezifität hat:
   eine Klasse. Bei Gleichstand gewinnt die SPÄTERE Regel. Erst stand sie oben im Spiel-Abschnitt, und damit
   war sie tot: `.inventory` setzte drei Spalten und Kachelform zurück, der Vorrat wurde quer 140 px hoch
   statt 34, und „Weiterspielen" stand 49 px unter dem Fensterrand. Im Quelltext sah die Überschreibung
   dabei völlig richtig aus — genau diesen Fehler hat diese Datei schon mehrfach gehabt.
   In der Pause zählt jeder Punkt Höhe, also: eine Zeile aus Symbol und Zahl, vier nebeneinander. */
.pause-stock { margin: 0 0 6px; grid-template-columns: repeat(4,1fr); gap: 6px; }
.pause-stock div { flex-direction: row; justify-content: center; gap: 5px; padding: 6px 4px; }
.pause-stock b { font-size: 16px; }.pause-stock b i { font-size: 11px; }
dialog p, dialog li { color: var(--muted); line-height: 1.6; }dialog li { margin-bottom: 10px; }dialog li b { color: var(--ink); }
.primary, .wide { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; min-height: 48px; padding: 12px; border-radius: 16px; margin-top: 10px; background: #285061; }
.primary { background: linear-gradient(#acffd0,#7bd7b6); color: #17263c; font-weight: 800; border-color: #d6ffe8; box-shadow: 0 4px 0 #995335; }
.primary:active { box-shadow: 0 1px 0 #995335; }.hotfix { background: linear-gradient(#ffe9a3,#ffb347); border-color: #fff1c8; box-shadow: 0 4px 0 #7a4a10; color: #3a2000; }@media(hover:hover) { .wide:hover { background: #55467a; } }.danger { color: #ffb3c0; background: transparent; border-color: #ff5d7344; font-size: 12px; min-height: 40px; }.app-version { margin: 8px 0 0; text-align: center; color: var(--muted); font-size: 11px; letter-spacing: .08em; }
.setting { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 54px; }
.name-field { display: flex; align-items: center; gap: 8px; width: 60%; }.name-field input { flex: 1; }
input[type=text], input:not([type]) { min-width: 0; width: 100%; padding: 10px; background: #0d2938; color: white; border: 1px solid var(--line); border-radius: 10px; font-size: 16px; }
.switch { appearance: none; -webkit-appearance: none; width: 48px; height: 28px; border-radius: 14px; background: #3a325d; position: relative; cursor: pointer; transition: background .25s; margin: 0; flex: 0 0 auto; }
.switch::before { content: ""; position: absolute; top: 3px; left: 3px; width: 22px; height: 22px; border-radius: 50%; background: white; transition: transform .25s cubic-bezier(.3,1.4,.5,1); box-shadow: 0 2px 6px #0006; }
.switch:checked { background: var(--cyan); }.switch:checked::before { transform: translateX(20px); }
.device-box { margin: 14px 0 6px; padding: 14px; border-radius: 16px; background: #0d293899; border: 1px solid var(--line); }
.device-box p { font-size: 12px; margin: 6px 0 10px; }
.code-row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.code-row code { flex: 1; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 15px; letter-spacing: .1em; padding: 10px; background: #091f2d; border-radius: 10px; color: var(--cyan); text-align: center; user-select: all; -webkit-user-select: all; }
.mini { min-height: 44px; min-width: 44px; padding: 8px 12px; border-radius: 12px; background: #285061; display: inline-flex; align-items: center; justify-content: center; gap: 6px; font-size: 13px; font-weight: 700; }
.import-status { font-size: 12px; min-height: 1em; margin: 0; }.import-status.ok { color: var(--green); }.import-status.error { color: #ffb3c0; }
.help-modules { display: grid; grid-template-columns: repeat(3,1fr); gap: 8px; margin: 6px 0 14px; }
.help-modules div { display: flex; flex-direction: column; align-items: center; gap: 2px; font-size: 11px; color: var(--muted); }.help-modules canvas { width: 40px; height: 40px; }

/* ── Result ───────────────────────────────────────────────────────────── */
.result { width: 100%; height: 100%; max-width: 100vw; max-height: 100vh; max-height: 100dvh; margin: 0; padding: 16px; border: 0; border-radius: 0; background: transparent; box-shadow: none; display: none; place-items: center; overflow: hidden; }
.result[open] { display: grid; animation: none; }
.result-fx { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2; }
@keyframes resultIn { from { opacity: 0; transform: translateY(40px) scale(.85); } to { opacity: 1; transform: none; } }
@keyframes stampIn { from { opacity: 0; transform: scale(2.4) rotate(-8deg); } 60% { opacity: 1; transform: scale(.94) rotate(2deg); } to { transform: scale(1) rotate(-2deg); } }
@keyframes starPop { 0% { transform: scale(0) rotate(-40deg); } 60% { transform: scale(1.35) rotate(8deg); } 100% { transform: scale(1) rotate(0); } }
@keyframes glow { 50% { filter: drop-shadow(0 0 16px var(--gold)) brightness(1.2); } }
@keyframes resultFade { from { opacity: 0; } to { opacity: 1; } }
.result.nova-arriving .result-card, .result.nova-arrived .result-card { animation: resultFade .28s ease both; }
.result-card { position: relative; width: min(94vw,440px); max-width: calc(100vw - var(--sa-links) - var(--sa-rechts) - 20px); max-height: calc(var(--vvh,100dvh) - 32px - var(--sa-oben) - var(--sa-unten)); overflow-y: auto; border: 1px solid #a2cdd866; border-radius: 28px; padding: 24px; background: linear-gradient(#204757,#102e40); color: var(--ink); box-shadow: 0 24px 90px #000a; text-align: center; animation: resultIn .5s cubic-bezier(.2,.9,.3,1.15) both; }
.stamp { display: inline-block; margin: 0; padding: 6px 14px; border: 3px solid var(--green); border-radius: 10px; color: var(--green); font-weight: 900; letter-spacing: .2em; font-size: 12px; transform: rotate(-2deg); animation: stampIn .55s cubic-bezier(.2,.9,.3,1) .25s both; }
.result.lose .stamp { color: var(--red); border-color: var(--red); }
/* Stars and score share one row: the eye reads both at once, and the card stays short. */
.result-score-row { display: flex; justify-content: center; align-items: center; gap: 18px; margin: 8px 0 0; }
.result-stars { display: flex; justify-content: center; align-items: center; gap: 2px; margin: 0; }.result-stars .star { width: 32px; height: 32px; }.result-stars .star:nth-child(2) { width: 42px; height: 42px; }
.result-points { text-align: left; }.result-stars[hidden] + .result-points { text-align: center; }
.result-stars .star { color: #ffffff22; transform: scale(.9); }
.result-stars .star.lit { color: var(--gold); animation: starPop .55s cubic-bezier(.3,1.4,.5,1) var(--d,0s) both, glow 2s ease-in-out calc(var(--d,0s) + .6s) infinite; }
.result-score-label { margin: 0; font-size: 9px; letter-spacing: .2em; color: var(--muted); }
.result-score { margin: 0; line-height: 1.05; font-size: 34px; font-weight: 900; font-variant-numeric: tabular-nums; letter-spacing: -.03em; background: linear-gradient(#fff,#ffd35c); -webkit-background-clip: text; background-clip: text; color: transparent; }
.result-stats { display: grid; grid-template-columns: repeat(4,1fr); gap: 4px; margin: 8px 0 0; }
.result-stats div { background: #ffffff0c; border-radius: 10px; padding: 5px 3px; }.result-stats b { display: block; font-size: 15px; line-height: 1.2; }.result-stats small { font-size: 9px; letter-spacing: .08em; color: var(--muted); line-height: 1.2; display: block; }
.result-rank { margin: 10px 0 0; padding: 10px; border-radius: 12px; background: #2f6a5a55; border: 1px solid #95f4bb66; color: var(--ink); font-size: 14px; animation: rise .4s ease backwards; }
.result-rank b { color: var(--green); font-size: 18px; }.result-rank small { display: block; color: var(--muted); font-size: 11px; }
.result-name { margin: 10px 0 0; text-align: left; }.result-name label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 6px; }
.result-actions { margin-top: 8px; display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }.result-actions .primary { grid-column: span 2; }.result-actions button { margin-top: 0; }.result-actions .wide { font-size: 14px; padding-left: 10px; padding-right: 10px; white-space: nowrap; }
.result.lose .result-card { animation: resultIn .5s cubic-bezier(.2,.9,.3,1.15) both, shake .5s ease .5s; }
.splash.leaving { opacity: 0; transition: opacity .28s ease; pointer-events: none; }
/* Im Blatt bleibt der Streifen am FENSTER stehen (`position: fixed` wie sonst auch) — er hängt nur im Blatt,
   damit er mit ihm im Top Layer liegt. 🚨 NICHT `position: absolute`: Ein Blatt rollt (`overflow-y: auto`),
   und dann meint „unten" das Ende des gerollten INHALTS, nicht die sichtbare Kante — gemessen landete der
   Streifen so 253 px über dem oberen Bildrand. */
.toasts { position: fixed; left: 50%; bottom: max(24px, var(--sa-unten)); transform: translateX(-50%); z-index: 60; display: flex; flex-direction: column-reverse; align-items: center; gap: 6px; width: max-content; max-width: 92vw; pointer-events: none; }
.toast { display: inline-flex; align-items: center; gap: 8px; background: #102e40f0; border: 1px solid var(--line); padding: 10px 16px; border-radius: 14px; font-size: 13px; animation: rise .3s ease backwards; pointer-events: none; }
.toast svg { color: var(--gold); flex: 0 0 auto; }.toast.quest svg { color: #ffb0ff; }.toast.rank svg { color: var(--cyan); }.toast.gift svg { color: var(--green); }.toast.streak svg { color: #ff9c57; }

/* ── Responsive ───────────────────────────────────────────────────────── */
@media(min-width:1001px) and (max-height:719px) { .menu-stats { display: none; } }
@media(max-width:1000px) {
  .game { grid-template-columns: minmax(0,1fr); grid-template-rows: auto minmax(0,1fr); padding: 0 0 8px; gap: 0; }
  /* The head of the phone screen is plain page background: no card, no gradient band, no blur. */
  .hud-panel { align-self: stretch; padding: 10px 14px 8px; border: 0; border-radius: 0; background: none; backdrop-filter: none; }
  body[data-screen="game"] .top { display: none; }
  .level-heading { grid-template-columns: 56px 1fr auto; gap: 0 12px; width: min(100%,500px); margin: 0 auto 8px; }.heading-text { display: block; }.level-heading h2 { font-size: 17px; line-height: 1.15; margin: 1px 0 0; font-weight: 800; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }.level-heading .eyebrow { font-size: 9px; }.nova-hud { width: 56px; height: 56px; }.nova-chip { font-size: 13px; padding: 6px 11px; }
  .hud-stats { grid-template-columns: 72px minmax(0,1fr) auto; gap: 14px; max-width: 500px; margin: auto; }
  .moves-stat { padding: 6px 4px; border-radius: 20px; }.moves-stat b { font-size: 30px; }.score-stat b { font-size: 24px; }.combo-stat { grid-column: auto; text-align: right; min-width: 56px; }.combo-stat b { font-size: 20px; }
  .goals { justify-content: center; margin: 10px auto 0; max-width: 580px; }.goal { flex: 0 1 auto; padding: 3px 10px 3px 3px; font-size: 11.5px; }.goal canvas { width: 28px; height: 28px; }
  .side-card { display: none; }.board-wrap { padding: 0; }
  /* 🚨 DAS BODENBLATT IST EINE HOCHKANT-FORM. Die Regel galt für jeden Bildschirm unter 1000 px, also auch
     für ein gekipptes Handy: Dort wurde daraus ein 844 px breites, 366 px hohes Band über die ganze Breite,
     dessen Text unter der Kamera lag und das nach unten aus dem Bild lief. Quer ist ein mittiges Blatt die
     richtige Form — genau wie bei den Spielregeln und den Missionen, die quer immer schon gut standen. */
  .settings { width: 100%; max-width: none; margin: auto 0 0; border-radius: 26px 26px 0 0; max-height: calc(var(--vvh,100dvh) - 24px - var(--sa-oben)); padding-bottom: max(16px, var(--sa-unten)); }
}
@media(max-width:1000px) and (orientation:portrait) {
  .level-heading { --nova-size: 56px; grid-template-rows: var(--nova-size); }
  .level-heading > .nova-hud { grid-area: 1 / 1; }
  .level-heading > .heading-text, .level-heading > .nova-chip { grid-area: 1 / 2; }
  .level-heading > .head-tools { grid-area: 1 / 3; }
  .nova-chip { max-height: 100%; min-height: 0; overflow: auto; overscroll-behavior: contain; }
  .nova-chip::before { display: none; }
}
@media(max-width:1000px) and (orientation:portrait) and (min-height:760px) {
  .level-heading { --nova-size: 64px; }
}
@media(max-width:1000px) and (orientation:portrait) and (max-height:650px) {
  .level-heading { --nova-size: 40px; }
}
@media(max-width:1000px) and (orientation:landscape) {
  .settings { width: min(92vw,520px); max-width: calc(100vw - var(--sa-links) - var(--sa-rechts) - 24px); margin: auto; border-radius: 26px; max-height: calc(var(--vvh,100dvh) - 24px - var(--sa-oben) - var(--sa-unten)); padding-bottom: 16px; }
}
/* A phone board is limited by width, so on a tall screen vertical slack is left over no matter what.
   Spend it on what is read: bigger numbers, bigger goal pills, a bigger Nova. NICHT auf die Leiste unten —
   die bekam hier einmal 88 px und 66 px hohe Knöpfe und sah danach aus, als wäre sie das eigentliche Spiel. */
@media(max-width:1000px) and (min-height:760px) {
  .game { grid-template-rows: auto minmax(0,1fr); }
  .moves-stat b { font-size: 34px; }.score-stat b { font-size: 28px; }.combo-stat b { font-size: 22px; }
  .level-heading { grid-template-columns: 64px 1fr auto; margin-bottom: 10px; }.nova-hud { width: 64px; height: 64px; }.level-heading h2 { font-size: 19px; }
  .goal { font-size: 12.5px; padding: 5px 12px 5px 5px; }.goal canvas { width: 32px; height: 32px; }
}
.intro-goals .assist { color: var(--green); font-size: 12.5px; font-weight: 700; }
@media(max-width:380px) {
  .top { padding: 0 10px; }.board-tabs small { display: none; }.board-tabs button { min-height: 42px; }
  .menu { padding: 4px 14px 8px; }.hero { gap: 9px; }.nova-hero { width: 96px; height: 96px; }
  .hero-line { font-size: 12.5px; }.mode { min-height: 66px; }.mode strong { font-size: 13px; }.play-button { min-height: 66px; }
  .result-card { padding: 18px; }.node { --w: 84px; }.node-disc { width: 52px; height: 52px; }
}
@media(max-height:650px) and (orientation:portrait) {
  /* Der neue Name steht auf ZWEI Zeilen; auf 568 Punkten Höhe fiel damit der Einstellungen-Knopf
     aus dem Bild. Eine Nummer kleiner holt die Zeile wieder herein. */
  .hero { gap: 5px; }.nova-hero { width: 72px; height: 72px; }.hero-line { display: none; }.hero h1 { font-size: 30px; }.rank-chip { min-height: 34px; padding: 4px 12px; }
  .mode { min-height: 56px; }.play-button { min-height: 60px; }.menu-grid { gap: 8px; }.game { grid-template-rows: auto minmax(0,1fr); }
  .hud-panel { padding-top: 4px; }.level-heading { grid-template-columns: 40px 1fr auto; gap: 0 8px; margin-bottom: 4px; }.nova-hud { width: 40px; height: 40px; }.level-heading h2 { font-size: 14px; }.level-heading .eyebrow { font-size: 8px; }.nova-chip { font-size: 11.5px; padding: 4px 9px; }
  /* Short portrait phones: the whole card, Nova included, has to fit without scrolling. */
  .result-card { padding: 12px 14px; }.result-hero { grid-template-columns: 52px minmax(0,1fr) auto; column-gap: 10px; }.result-nova-dock { width: 52px; height: 52px; }.result-hero h2 { font-size: 17px; }.result-bubble { font-size: 11.5px; padding: 5px 9px; margin-top: 6px; }
  .result-stars .star { width: 28px; height: 28px; }.result-stars .star:nth-child(2) { width: 36px; height: 36px; }.result-score-label { display: none; }.result-score { font-size: 28px; }.result-score-row { margin-top: 4px; }
  .result-stats { gap: 4px; margin: 4px 0; }.result-stats b { font-size: 14px; }.result-stats small { font-size: 9px; }.result-stats div { padding: 4px 3px; }.result-wish { font-size: 11px; padding: 4px 10px; margin-top: 4px; }.result-xp { padding: 5px 10px; font-size: 11px; }.result-progress { margin-top: 4px; }
  .result-name { margin-top: 6px; }.result-actions { gap: 4px; margin-top: 6px; }.result-actions button { min-height: 38px; padding-top: 6px; padding-bottom: 6px; }.stamp { font-size: 11px; padding: 4px 10px; }
}
@media(orientation:landscape) and (max-height:600px) {
  .top { flex-basis: 40px; }.top .round-button { width: 36px; height: 36px; }
  /* 🚨 IM SPIEL BLEIBT DIE MARKENLEISTE AUCH QUER WEG. Sie zeigt dort nur das App-Symbol — und das IST Nova.
     Neben ihrer Figur im Kopf stand sie damit zweimal auf dem Schirm; gemeldet mit Bild: „Einmal Nova und
     einmal Nova App Icon." Der Knopf darin (zurück) ist im Spiel ohnehin versteckt, und Pause steht seit dem
     Umbau im Kopf. Es geht also nichts verloren, und das Brett gewinnt 40 Punkte Höhe — genau in der Lage,
     die am wenigsten davon hat. (Hoch war die Leiste im Spiel längst weg, quer holte diese Zeile sie zurück.) */
  /* 🚨 DREI SPALTEN QUER: ZAHLEN LINKS, BRETT IN DER MITTE, ZIELE RECHTS. Gemeldet mit Bild: „Rechte Seite ist
     fast gar nichts. Da könnte man doch das platzieren wo man sieht was man noch braucht?" Vorher stapelten
     sich Kopf, Zahlen UND vier Ziele in einer 150-px-Spalte links, während rechts vom Brett nichts stand.
     Das Brett verliert dadurch NICHTS: Quer ist es höhenbegrenzt, die Breite daneben hatte es ohnehin übrig.
     Und zwei gleich breite Randspalten halten es in der Mitte, auch wenn es (endlos, Zeitspiel) gar keine
     Ziele gibt. */
  .game { grid-template-columns: clamp(148px,20vw,180px) minmax(0,1fr) clamp(148px,20vw,180px); grid-template-rows: minmax(0,1.15fr) minmax(0,.85fr); padding: 0 8px 8px; gap: 4px 8px; }
  /* `display: contents` löst nur die Karte auf, nicht ihren Inhalt: Kopf, Zahlen und Ziele bekommen damit
     eigene Gitterplätze in `.game`. Ein Kasten drumherum gibt es quer ohnehin nicht (`background: none`). */
  .hud-panel { display: contents; }
  .level-heading { grid-column: 1; grid-row: 1; align-self: end; }
  .hud-stats { grid-column: 1; grid-row: 2; align-self: start; }/* `margin: auto` aus der Handy-Regel schrumpft die Zahlen auf ihren Inhalt und stellt sie mittig — in einer
     148-px-Spalte bleiben davon 102 px, und die Sternleiste unter den Punkten wird zum Stummel. Quer soll
     die Spalte gefüllt werden. */
  .hud-stats { grid-template-columns: 60px minmax(0,1fr); gap: 8px; margin: 0; max-width: none; }
  .combo-stat { grid-column: span 2; text-align: center; }
  .level-heading { grid-template-columns: minmax(0,1fr) 40px; grid-template-rows: 88px 60px; gap: 6px; margin-bottom: 4px; }
  .level-heading > .nova-hud { grid-area: 1 / 1; width: 88px; height: 88px; justify-self: center; }
  .level-heading > .heading-text, .level-heading > .nova-chip { grid-area: 2 / 1 / 3 / 3; }
  .heading-text { text-align: center; }.heading-text .eyebrow { display: none; }
  .head-tools { grid-area: 1 / 2; flex-direction: column; justify-self: end; gap: 6px; margin: 0; }
  .head-tools .round-button { width: 40px; height: 40px; }
  .level-heading h2 { font-size: 16px; margin: 0; white-space: normal; }
  .nova-chip { font-size: 12px; padding: 5px 8px; max-height: 60px; overflow: auto; overscroll-behavior: contain; }
  .nova-chip::before { display: none; }
  .goals { grid-column: 3; grid-row: 1 / -1; align-self: center; flex-direction: column; flex-wrap: nowrap; gap: 5px; margin: 0; max-height: 100%; overflow: auto; scrollbar-width: none; }
  .goals::-webkit-scrollbar { display: none; }
  .goal { width: 100%; }
  .board-wrap { grid-column: 2; grid-row: 1 / -1; }
  .side-card { display: none; }.run-panel { display: none; }.menu-stats { display: none; }
  .menu { padding: 4px 24px 10px; }.hero { width: min(100%,960px); display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1.3fr); gap: 10px 24px; }
  /* Flat windows (phone in landscape): Nova stays, small and left of the title in the same row; her line hangs
     under her with the tail pointing up at her. Hiding her here left the menu the one screen without her. */
  .hero-nova { display: block; grid-column: 1; grid-row: 1; justify-self: start; align-self: center; margin: 0; padding: 0; position: relative; z-index: 1; }
  .hero h1 { grid-column: 1; grid-row: 1; align-self: center; justify-self: stretch; text-align: left; }
  .hero-line { grid-column: 1; text-align: left; }.hero-line::before { left: 28px; }
  .play-button { grid-column: 1; }.menu-grid { grid-column: 2; margin: 0; }.mode { grid-template-columns: 24px minmax(0,1fr); padding: 8px; gap: 2px 6px; }.mode-icon { width: 24px; height: 24px; }.mode strong { font-size: 14px; }.mode span { font-size: 11px; }.menu-tools { grid-column: span 2; }
  /* 🚨 QUER DARF DER KARTENRASTER NICHT ÜBERLAUFEN — UND ER WIRD VON LINKS GESCHOBEN.
     Gemeldet mit Bild von einem iPhone 16 Pro (874×402): „Landscape wurde nicht optimiert für Handy",
     die unterste Kartenreihe abgeschnitten, die Seite gerollt.

     Die Ursache steht NICHT bei den Karten. Der Raster spannt über alle Zeilen des `hero`-Gitters
     (`grid-row: 1/6`), seine Höhe ist also die Summe der LINKEN Spalte: Figur, Titel, Novas Zeile,
     Rangleiste, Spielknopf. Wird eine davon höher — Novas Zeile ist Fliesstext und wächst mit dem
     Spielstand („Bereit, Michael. Weiter geht es mit Level 13: Schmiede.") —, wächst der Raster mit,
     und `min-height` an der Karte ist dabei nur ein BODEN, keine Decke. Über die Bildschirmhöhe
     hinaus ist dann alles zu spät.

     Deshalb drei Riegel, keiner davon am Symptom:
       1  Die Fläche rollt quer nicht mehr. Was nicht passt, muss schrumpfen, nicht verschwinden.
       2  Die Kartenzeilen dürfen schrumpfen (`minmax(0,1fr)` statt eines Bodens von 70 px).
     Zusammen kann die Spalte nicht mehr höher werden als der Bildschirm.

     ⚠️ Novas Zeile wird NICHT auf zwei Zeilen gekürzt. Der erste Anlauf tat das (`line-clamp: 2`)
     und schnitt ihr mitten im Satz das Wort ab — sie sagt dort Dinge, die man lesen können muss.
     Nötig ist es auch nicht: Sobald die Karten schrumpfen dürfen, trägt die Spalte drei Zeilen. */
  .menu { overflow: hidden; }
  /* ZWEITER ANLAUF, UND DIESMAL AN DER URSACHE.
     Nach dem ersten rollte die Seite zwar nicht mehr, dafür war sie OBEN UND UNTEN abgeschnitten —
     die Handschrift von mittig gesetztem Inhalt, der höher ist als der Schirm. Im gemeldeten Bild
     stand zwischen Rangleiste und Spielknopf eine Lücke von dreissig Punkten, obwohl der
     Zeilenabstand zehn ist. Das war keine Lücke: Das waren MITGEWACHSENE ZEILEN.

     `auto`-Zeilen in einem Gitter nehmen freien Platz auf. Der Kartenraster spannt über alle fünf
     (`grid-row: 1/6`), und sobald IRGENDETWAS das Gitter höher macht als seine Zeilen, verteilt
     sich der Überschuss in genau diese Zeilen — die Karten werden höher, die Spalte wird höher,
     und beides schaukelt sich hoch, bis es hinten überläuft.

     `min-content`-Zeilen können das nicht: Sie sind so hoch wie ihr Inhalt und nehmen keinen
     freien Platz auf. Was übrig bleibt, legt `align-content: center` außerhalb der Zeilen ab.
     Damit ist die Schaukel weg — und die Deckel unten sorgen dafür, dass die Summe der fünf
     Zeilen den Schirm gar nicht erst füllen kann, egal wie lang Novas Satz gerade ist. */
  .hero { max-height: 100%; min-height: 0; align-content: center;
          grid-template-rows: repeat(5, min-content); row-gap: min(10px, 2.4dvh); }
  .nova-hero { width: min(72px, 16dvh); height: min(72px, 16dvh); }
  .hero h1 { font-size: min(38px, 8.4dvh); padding-left: min(84px, 19dvh); }
  .play-button { min-height: min(64px, 15.5dvh); }
  .menu-tools button, .menu-tools a { min-height: min(36px, 8.6dvh); }
  /* NOVAS ZEILE IST DIE EINZIGE STELLE MIT FREIEM TEXT — und sie wächst mit dem Spielstand
     (Beispiel: Bereit, Michael. Weiter geht es mit Level 13: Schmiede.). Drei Zeilen trägt die
     Spalte, die vierte drückt den Kartenraster über den Rand. Also ein Deckel bei drei; der erste
     Anlauf hatte zwei und schnitt ihr mitten im Satz das Wort ab. */
  .hero-line { margin-bottom: 0; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; font-size: min(12px, 3dvh); max-height: 15dvh; }
  .menu-grid { min-height: 0; align-self: stretch; grid-auto-rows: minmax(0,1fr); }
  .mode { min-height: 0; }
  .result-card { width: min(94vw,680px); padding: 14px 20px; display: grid; grid-template-columns: 1fr 1fr; gap: 0 18px; align-items: center; }.result-main { text-align: center; }
  .result-nova-dock { width: 80px; height: 80px; }.result-hero { grid-template-columns: 80px minmax(0,1fr) auto; }.result-hero h2 { font-size: 24px; }.result-score-row { justify-content: flex-start; margin-top: 12px; }.result-score { font-size: 34px; }.result-stats { margin: 0; }.result-stats b { font-size: 15px; }.result-rank { margin-top: 6px; padding: 6px 10px; font-size: 12px; }.result-actions { margin-top: 4px; }.result-actions .primary, .result-actions .wide { min-height: 40px; margin-top: 6px; padding: 6px; }
}
/* Compact landscape phones retain every entry while leaving room for the footer.
   Eng ist nicht nur schmal: Ein Gerät mit 740x360 ist breit genug für die große Fassung und
   trotzdem acht Punkte zu flach dafür. Deshalb greift dieser Block auch nach der HÖHE. */
@media(orientation:landscape) and ((max-width:700px) or (max-height:380px)) {
  /* Auf 320 Punkten Höhe (iPhone SE quer) standen zuletzt vier Punkte über dem Rand. Sie stecken
     im Rand der Fläche und im Zeilenabstand — beides ist hier ohnehin Luxus. */
  .menu { padding: 2px 12px 4px; }.hero { gap: min(8px, 2dvh) 16px; }
  .hero h1 { font-size: 32px; padding-left: 70px; }.nova-hero { width: 60px; height: 60px; }
  .hero-line { font-size: 11px; -webkit-line-clamp: 2; line-clamp: 2; }.mode strong { font-size: 12px; }.mode span { font-size: 10px; }
  .play-button { min-height: 56px; padding: 8px 14px; }.play-button strong { font-size: 24px; }.play-button small { font-size: 11px; }
  .play-icon, .play-arrow { width: 34px; height: 34px; }
  /* Auf 320 Punkten Höhe (iPhone SE quer) fehlten zuletzt vier Punkte. Sie stecken in der Fusszeile:
     44 Punkte Tipphöhe sind die Vorgabe für einen Daumen — hier reichen 36, denn quer liegt das Gerät
     ohnehin in zwei Händen. */
  .menu-tools button, .menu-tools a { min-height: 36px; padding: 6px; }
}
@media(prefers-reduced-motion:reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}
body.reduced *, body.reduced *::before, body.reduced *::after { animation: none !important; transition: none !important; }

/* ── iOS standalone cold-start viewport (same bug Kellerkind, KelRaid and Odyssee document) ──────────────
   On launch as an installed app iOS reports the viewport used by position:fixed / inset:0 / dvh one safe-area
   strip SHORT until a rotation forces a recompute. Odyssee and Kellerkind size html/body by 100lvh so the page
   canvas still covers the whole screen. The seam itself is handled by the single --void colour above, not by
   geometry, which is why nothing interactive is stretched to 100lvh here. */
@media (display-mode: standalone) {
  /* Only the page itself is sized by 100lvh. The shell must NOT be: it is a flex column whose last child is the
     control dock, and at cold start iOS's visible viewport is one status bar SHORTER than 100lvh — a taller shell
     pushes the dock off the screen. It keeps inset:0 so it always equals the visible area and the dock stays
     reachable; the strip iOS paints above it is already the same --void colour, so nothing shows as a seam. */
  html, body { height: 100lvh; }
}

/* ── Press feedback ──────────────────────────────────────────────────────────────────────────────────
   On a phone every control felt dead: iOS does not apply :active for touch, and the native tap highlight
   is switched off above. app.mjs therefore sets .pressed from pointer events. The transform needs
   !important because element:hover rules (higher specificity) and any animation fill both outrank a
   plain class declaration. */
/* transition-duration 0 only while pressed: the button sinks in on the same frame as the touch and eases
   back out once the class goes again. Buttons with a hard 3D shadow are pushed DOWN by exactly that
   shadow height and lose it, which reads as a physical press; flat buttons shrink a touch and tint.
   No brightness filter: it blew out the gradients (the green play button went almost white). */
/* Never scale a pressed element: iOS rasterises the layer at the smaller size and keeps that blurry raster after
   the press ends (the back button stayed soft). Presses move down instead. */
.pressed { transition-duration: 0s !important; transform: translateY(2px) !important; filter: brightness(.93); }
.play-button.pressed { transform: translateY(6px) !important; box-shadow: 0 0 0 #995335, 0 4px 14px #0004; filter: brightness(.97); }
.mode.pressed, .level-card.pressed { transform: translateY(4px) !important; box-shadow: 0 0 0 #061824, inset 0 1px 0 #ffffff10; filter: brightness(1.06); }
.node.pressed .node-disc { transform: translateY(4px); box-shadow: 0 1px 0 #08283a, 0 0 0 4px #ffffff14; filter: brightness(1.08); }
.primary.pressed { transform: translateY(4px) !important; box-shadow: 0 0 0 #995335; filter: brightness(.97); }
.hotfix.pressed { box-shadow: 0 0 0 #7a4a10; }
/* Die Fußzeile im Menü (Einstellungen, Spielregeln, Shop) hat keinen eigenen Grund. Gedrückt bekam sie hier
   bisher denselben harten `#ffffff2a`-Kasten wie ein runder Knopf — ohne Rundung sah das aus wie ein
   Auswahlrahmen, gemeldet als „so eine Umrandung". Jetzt: gerundet (siehe oben), leiser Schimmer, helle Schrift. */
.round-button.pressed, .mini.pressed { transform: translateY(2px) !important; background: #ffffff2a; filter: none; }
.menu-tools button.pressed { transform: translateY(2px) !important; background: #ffffff12; color: white; filter: none; }
.board-tabs button.pressed, .wide.pressed, .danger.pressed { transform: translateY(2px) !important; background: #315d70; filter: none; }
.board-tabs button.active.pressed { background: #5ed3ee; }

/* Objective progress stays inside the existing pills, preserving the mobile board area. */
.goal { position: relative; overflow: hidden; padding-bottom: 8px; }
.goal progress { appearance: none; -webkit-appearance: none; position: absolute; bottom: 0; left: 0; width: 100%; height: 3px; border: 0; background: #ffffff12; color: var(--goal-color); }
.goal progress::-webkit-progress-bar { background: #ffffff12; }
.goal progress::-webkit-progress-value { background: var(--goal-color); transition: width .3s ease; }
.goal progress::-moz-progress-bar { background: var(--goal-color); }
.goal.done { --goal-color: var(--green); }
.match-guide { display: grid; gap: 8px; margin: 18px 0; }
.match-guide > div { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 12px; background: #ffffff08; border: 1px solid #ffffff20; border-radius: 14px; padding: 12px; }
.match-guide b { color: var(--gold); font-size: 12px; }
.match-guide strong { text-align: right; color: var(--cyan); }
.match-guide span { grid-column: 1 / -1; font-size: 13px; color: var(--muted); }
#help-dialog details { padding: 12px 0; border-top: 1px solid var(--line); font-size: 14px; line-height: 1.6; }
#help-dialog summary { cursor: pointer; font-weight: 800; padding: 4px 0; }
#help-dialog p, .pause-objective { color: var(--muted); line-height: 1.6; }
@media (prefers-reduced-motion: reduce) { .goal progress::-webkit-progress-value { transition: none; } }
body.reduced .goal progress::-webkit-progress-value { transition: none; }

/* ── v18: rank, missions, chapters, achievements with progress, level preview ───────────────────── */
.rank-chip { --i: 3; display: grid; grid-template-columns: auto 1fr auto; grid-template-rows: auto auto; gap: 2px 8px; align-items: center; width: 100%; max-width: 360px; min-height: 40px; padding: 6px 14px; border-radius: 14px; background: #0b263699; border: 1px solid var(--line); color: var(--ink); text-align: left; font-size: 12px; font-weight: 700; }
.rank-chip svg { grid-row: span 2; color: var(--cyan); }.rank-chip small { color: var(--muted); font-weight: 600; font-size: 10px; grid-row: span 2; text-align: right; }
.rank-chip progress { grid-column: 2; width: 100%; height: 5px; accent-color: var(--cyan); display: block; border-radius: 3px; overflow: hidden; }
@media(hover:hover) { .rank-chip:hover { background: #0b2636cc; border-color: #7ce9ff66; } }.rank-chip.pressed { transform: translateY(2px) !important; background: #0b2636; filter: none; }
.mode.done .mode-icon { color: var(--green); }
/* 🚨 AUF DER FORTSCHRITTS-SEITE KOMMEN DIE LEVEL ZUERST. Vom Betreiber gemeldet, mit Foto: „diese seite ist
   mobile unbenutzbar" — Rang, Vertrauen, Kiste, zwei Bücher und sechs Zahlen standen ÜBER der Levelkarte und
   füllten auf einem iPhone die ganzen ersten 507 von 844 Pixeln. Wer die Seite öffnet, will ein Level wählen.
   Alles andere liegt seitdem in `#campaign-extra` UNTER der Karte (am Rechner in der rechten Spalte). */
.campaign-extra { margin-top: 18px; }
/* Die Karten richten sich nach dem Platz, den sie WIRKLICH haben: zwei nebeneinander auf einem Handy, eine
   untereinander in der schmalen rechten Spalte am Rechner. Feste zwei Spalten liessen die Titel dreizeilig brechen. */
.campaign-bars { display: grid; grid-template-columns: repeat(auto-fit,minmax(168px,1fr)); gap: 8px; margin: 0 0 10px; }
/* The diary: a bookmark-like button on the campaign screen, a sheet with every collected story page inside. */
/* 🚨 NOTIZBUCH UND TAGEBUCH SIND EINSTIEGE, KEINE STATISTIK. Gemeldet: „Notizbuch sieht man vielleicht im
   Browser, aber ich finde es nicht mobil — soll man aber!" Mit v67 waren sie unter die Levelkarte gerutscht,
   auf dem Handy also hinter 40 Level und ~5000 Pixel Weg. Sie stehen jetzt wieder in der Kopfzeile, aber zu
   zweit in EINER Reihe, damit die Karte trotzdem gleich beginnt. Die Zahlen daneben bleiben unten. */
.head-books { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 2px 0 12px; }
.head-books .diary-button { margin: 0; padding: 8px 12px; }
.head-books .diary-button span { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.head-books .diary-button small { margin-left: auto; white-space: nowrap; }
/* Auf schmalen Handys passt die Zahl nicht mehr daneben, dann rutscht sie unter den Namen. */
@media(max-width:480px) { .head-books .diary-button { flex-wrap: wrap; row-gap: 0; } .head-books .diary-button small { flex: 1 0 100%; margin-left: 26px; } }
.diary-button { display: flex; align-items: center; gap: 8px; width: 100%; margin: 0 0 10px; padding: 9px 14px; border-radius: 14px; background: linear-gradient(90deg,#7ce9ff1c,#bb9dff14); border: 1px solid #7ce9ff44; color: var(--ink); font-size: 13px; font-weight: 800; text-align: left; }
.diary-button svg { color: var(--cyan); flex: 0 0 auto; }.diary-button span { flex: 1; }.diary-button small { color: var(--muted); font-weight: 600; font-size: 11.5px; }
.diary { padding: 0 0 18px; }.diary-title { display: flex; align-items: center; gap: 12px; }.diary-title h2 { margin: 2px 0 0; font-size: 21px; white-space: nowrap; }.diary-title .nova-small { width: 48px; height: 48px; }
/* 🚨 HINTER DER GRUNDREGEL, nicht im Block für schmale Geräte weiter oben: Bei gleicher Spezifität gewinnt
   die spätere Regel, und die Grundregel steht später in der Datei. Genau daran ist der Deckel für große
   Bildschirme schon einmal wirkungslos gewesen (siehe der Block am Dateiende).
   320 px: Zwischen Novas Dock und dem Schließen-Kreuz bleiben für „Novas Tagebuch" rund 187 px — die Zeile
   braucht 192. Eine Nummer kleiner reicht also NICHT, der Titel stünde weiter als „Novas Tageb…" da. Er
   darf hier deshalb umbrechen; zwei kurze Zeilen lesen sich besser als ein abgeschnittener Name. */
@media(max-width:380px) {
  .diary-title { gap: 9px; }.diary-title .nova-small { width: 40px; height: 40px; }
  .diary-title h2 { font-size: 18px; white-space: normal; }
}
.diary-summary { margin: 0 20px 10px; color: var(--muted); font-size: 13px; }
.diary-body { display: grid; gap: 14px; padding: 0 20px; }
.diary-section { --c: var(--cyan); border-left: 3px solid var(--c); padding-left: 12px; }
.diary-section h3 { margin: 0 0 8px; font-size: 13px; letter-spacing: .08em; color: var(--c); text-transform: uppercase; }
.diary-entry { margin: 0 0 8px; padding: 10px 12px; border-radius: 14px; background: #ffffff0a; border: 1px solid color-mix(in srgb, var(--c) 35%, transparent); font-size: 13px; line-height: 1.45; }
.diary-entry small { display: block; color: var(--c); font-size: 10.5px; letter-spacing: .1em; margin-bottom: 4px; font-weight: 800; }
.diary-entry .after { margin-top: 6px; padding-top: 6px; border-top: 1px dashed color-mix(in srgb, var(--c) 35%, transparent); color: var(--muted); }
.diary-open { margin: 0; color: var(--muted); font-size: 12px; font-style: italic; }
.diary-empty { margin: 0 20px; padding: 16px; border-radius: 14px; background: #ffffff0a; border: 1px dashed var(--line); color: var(--muted); font-size: 13px; text-align: center; }
.result-wish.near { background: #ff9c5718; border-color: #ff9c5766; color: var(--ink); }.result-wish.near svg { color: #ffb47a; }
.bar-card { background: #ffffff0c; border: 1px solid var(--line); border-radius: 14px; padding: 8px 12px; font-size: 12px; }
.bar-card span { display: flex; align-items: center; gap: 6px; }.bar-card span svg { color: var(--gold); }.bar-card b { font-size: 12px; }
.bar-card progress { width: 100%; height: 6px; margin: 6px 0 3px; accent-color: var(--gold); display: block; border-radius: 3px; overflow: hidden; }
#rank-card progress { accent-color: var(--cyan); }#rank-card span svg { color: var(--cyan); }
.bar-card small { color: var(--muted); font-size: 10.5px; }
.chapter { --i: 0; --c: var(--cyan); position: absolute; left: 4%; right: 4%; display: grid; grid-template-columns: 1fr auto; gap: 0 10px; align-items: center; padding: 10px 14px; border-radius: 18px; background: linear-gradient(120deg, color-mix(in srgb, var(--c) 22%, #16123b), #16123bf0); border: 1px solid color-mix(in srgb, var(--c) 55%, transparent); box-shadow: 0 6px 24px #0006; animation: rise .45s cubic-bezier(.2,.8,.2,1) backwards; animation-delay: calc(var(--i) * 45ms); }
.chapter-no { grid-column: 1; font-size: 9px; letter-spacing: .18em; font-weight: 800; color: var(--c); }
.chapter strong { grid-column: 1; font-size: 16px; line-height: 1.1; }.chapter small { grid-column: 1; color: var(--muted); font-size: 11px; line-height: 1.3; margin-top: 2px; }
.chapter em { grid-column: 2; grid-row: 1 / 4; font-style: normal; font-size: 11px; font-weight: 700; color: var(--gold); text-align: right; display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
.chapter.locked { opacity: .6; }.chapter.locked em { color: var(--muted); }.chapter.done { border-color: var(--gold); }.chapter.done em { color: var(--green); }
.ach-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }.ach-count { color: var(--gold); font-size: 12px; font-weight: 700; }
.achievement-groups { display: grid; gap: 6px; }
.ach-group { --i: 0; animation: rise .45s cubic-bezier(.2,.8,.2,1) backwards; animation-delay: calc(var(--i) * 60ms + 120ms); }
.ach-group h3 { display: flex; justify-content: space-between; align-items: baseline; margin: 10px 0 6px; font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: var(--cyan); }.ach-group h3 span { color: var(--muted); letter-spacing: 0; text-transform: none; font-weight: 600; }
.ach { align-items: start; }.ach > div { min-width: 0; }
.ach progress { display: block; width: 100%; height: 4px; margin: 6px 0 2px; accent-color: var(--gold); border-radius: 2px; overflow: hidden; }
.ach small { display: block; font-size: 10px; color: var(--muted); font-variant-numeric: tabular-nums; }.ach.earned small { color: var(--green); }
.level-sheet { padding: 0 0 16px; }.level-sheet h2 { margin: 2px 0 0; font-size: 24px; }.level-body { padding: 4px 20px 6px; }
.level-intro-text { margin: 0 0 12px; font-size: 14px; line-height: 1.55; color: var(--ink); }
.level-goals { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.level-goals span { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px 4px 4px; border-radius: 14px; background: #061d2b66; border: 1px solid #ffffff15; font-weight: 800; color: var(--gold); font-size: 13px; }.level-goals canvas { width: 34px; height: 34px; }
.level-obstacles { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.chip { display: inline-flex; align-items: center; gap: 4px; padding: 5px 10px; border-radius: 12px; background: #ffffff0c; border: 1px solid var(--line); font-size: 12px; color: var(--muted); }.chip b { color: var(--ink); }
.level-best { font-size: 13px; color: var(--muted); margin-bottom: 8px; }.level-best b { color: var(--ink); }.level-best em { font-style: normal; color: var(--green); }
.level-star-list { display: flex; gap: 8px; }
.level-star-list span { flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 4px; padding: 6px; border-radius: 12px; background: #ffffff08; font-size: 12px; font-weight: 700; color: var(--muted); }
.level-star-list span svg { color: #ffffff30; }.level-star-list span.lit { color: var(--ink); background: #ffd35c14; }.level-star-list span.lit svg { color: var(--gold); filter: drop-shadow(0 0 4px var(--gold)); }
.level-sheet .primary { width: calc(100% - 40px); margin: 8px auto 0; }
.quest-sheet { padding: 0 0 16px; }.quest-sheet h2 { margin: 2px 0 0; font-size: 24px; }.quest-intro { margin: 4px 20px 10px; font-size: 13px; }
.quest-list { display: grid; gap: 8px; padding: 0 20px; }
.quest { --i: 0; display: grid; grid-template-columns: 38px 1fr auto; gap: 10px; align-items: center; padding: 10px 12px; border-radius: 16px; background: #ffffff08; border: 1px solid var(--line); animation: rise .4s cubic-bezier(.2,.8,.2,1) backwards; animation-delay: calc(var(--i) * 60ms); }
.nova-ask { text-align: center; }
.ask-head { display: grid; place-items: center; margin-bottom: 4px; }.nova-ask .nova-big { width: 110px; height: 110px; }
.nova-ask h2 { font-size: 21px; line-height: 1.35; margin: 6px 0 16px; }
.ask-options { display: grid; gap: 8px; }
.ask-options button { min-height: 52px; padding: 10px 16px; border-radius: 16px; background: linear-gradient(150deg,#285061,#102e40); box-shadow: 0 3px 0 #061824; font-size: 15px; font-weight: 700; color: var(--ink); }
@media(hover:hover) { .ask-options button:hover { filter: brightness(1.15); } }
.ask-reply { margin: 14px 0 0; padding: 10px 14px; border-radius: 16px; background: #7ce9ff14; border: 1px solid #7ce9ff44; font-size: 14px; line-height: 1.45; color: var(--ink); }
.ask-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 14px; }.ask-actions button { margin: 0; font-size: 12.5px; }
.ask-name { margin: 0 0 12px; text-align: left; }.ask-name label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 6px; }
.book-list { display: grid; gap: 8px; margin: 10px 0 16px; }
/* 🚨 NOVAS NOTIZBUCH STAND AM RAND. Es ist mit den Klassen der Bildschirm-Listen gebaut (`.list-summary`,
   `.book-list`); dort bringt der Bildschirm seinen Innenabstand mit. Als Blatt steht es auf `.diary`, und das
   hat `padding: 0` — Zusammenfassung, Karten und Knopf klebten mit 1 px am Rand, während das Tagebuch nebenan
   seine 20 px hat. Gemeldet: „Hier fehlt auch Abstand bei den Karten und Text oben." */
#nova-book-dialog .list-summary { margin: 4px 20px 12px; }
#nova-book-dialog .book-list { margin: 0 20px 16px; }
#nova-book-dialog .primary { width: calc(100% - 40px); margin: 0 auto; }
.book-entry { display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%; padding: 10px 14px; border-radius: 14px; background: #ffffff0c; border: 1px solid var(--line); text-align: left; font-size: 13px; color: var(--ink); }
.book-entry small { display: block; color: var(--muted); font-size: 11px; margin-top: 2px; }
.book-entry b { font-size: 13.5px; color: var(--cyan); white-space: nowrap; }
.book-entry.open b { color: var(--muted); }.book-entry.locked { opacity: .5; }
.weekly { display: block; margin: 0 0 14px; padding: 12px 14px; border-radius: 16px; background: linear-gradient(140deg,#2a4a6b55,#1b2f4a55); border: 1px solid #7ce9ff44; }
.weekly b { display: block; margin: 6px 0 8px; font-size: 15px; line-height: 1.3; }
.weekly progress { --bar: var(--cyan); width: 100%; height: 8px; display: block; border-radius: 4px; overflow: hidden; appearance: none; -webkit-appearance: none; border: 0; background: #ffffff18; color: var(--bar); accent-color: var(--cyan); }
.weekly progress::-webkit-progress-bar { background: #ffffff18; border-radius: 4px; }.weekly progress::-webkit-progress-value { background: var(--bar); border-radius: 4px; }.weekly progress::-moz-progress-bar { background: var(--bar); border-radius: 4px; }
.weekly.done progress { --bar: var(--green); }
.weekly small { display: block; margin-top: 6px; color: var(--muted); font-size: 12px; }
.weekly.done { background: linear-gradient(140deg,#2b7c5c55,#1b4a3a55); border-color: #95f4bb99; }.weekly.done small { color: var(--green); }
#trust-card progress { --bar: var(--pink); accent-color: var(--pink); }#trust-card span svg { color: var(--pink); }
.quest i { display: grid; place-items: center; width: 38px; height: 38px; border-radius: 50%; background: #ffb0ff22; color: #ffb0ff; }.quest > div { min-width: 0; }.quest b { display: block; font-size: 13px; }
.quest progress { display: block; width: 100%; height: 5px; margin: 6px 0 2px; accent-color: #ffb0ff; border-radius: 3px; overflow: hidden; }.quest small { color: var(--muted); font-size: 10.5px; }
.quest.done { border-color: #95f4bb88; background: #2b7c5c33; }.quest.done i { background: #6ff0bd33; color: var(--green); }.quest.done progress { accent-color: var(--green); }.quest.done small { color: var(--green); }
.quest-reward { display: inline-flex; align-items: center; gap: 3px; font-weight: 900; color: var(--c); font-size: 13px; }.quest-reward svg { filter: drop-shadow(0 0 6px var(--c)); }
.quest-bonus { margin: 10px 20px 0; font-size: 12px; }.quest-sheet .primary { width: calc(100% - 40px); margin: 10px auto 0; }
.result-progress { margin: 6px 0 0; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }.result-progress .result-xp { flex: 1 1 200px; }.result-xp span, .result-xp b { white-space: nowrap; }
.result-xp { display: grid; grid-template-columns: 1fr auto; gap: 2px 10px; align-items: center; padding: 6px 10px; border-radius: 12px; background: #ffffff0c; font-size: 12px; text-align: left; }
.result-xp span { grid-column: 1; grid-row: 1; color: var(--cyan); font-weight: 800; }.result-xp b { grid-column: 2; grid-row: 1 / 3; color: var(--gold); font-size: 15px; }
.result-xp progress { grid-column: 1; grid-row: 2; width: 100%; height: 5px; accent-color: var(--cyan); display: block; border-radius: 3px; overflow: hidden; }
.result-quests { display: grid; gap: 4px; font-size: 12px; color: var(--green); text-align: left; }.result-quests span { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 10px; background: #2b7c5c33; }
/* Die Ausbeute einer Runde, in der Karte statt als Streifen davor: Erfolge, Rang, Vertrauen, Booster,
   Siegesserie, Sternen-Kisten. 🚨 ALS MARKEN, DIE UMBRECHEN, NICHT ALS ZEILEN: Eine gute Runde wirft acht
   Belohnungen auf einmal ab, und acht volle Zeilen schieben auf einem 320-px-Handy die Knöpfe aus dem Bild.
   Nebeneinander passen zwei bis drei je Zeile, und das Symbol sagt, worum es geht. */
.result-news { display: flex; flex-wrap: wrap; justify-content: center; gap: 4px; margin-top: 6px; font-size: 11.5px; }
/* 🚨 EINE MARKE IST EINE ZEILE — IMMER. Gemeldet: unterschiedliche Höhen machen keinen Sinn.
   `white-space: nowrap` ist die Zusicherung: Ein Text, der nicht mehr in die Karte passt, bricht sonst um
   und macht aus einer 18-px-Marke eine mit 30 px. Zusammen mit `max-width` und `text-overflow` wird er
   stattdessen abgeschnitten — Missionstexte und Erfolgsnamen sind Daten, und die Liste kann wachsen.
   Die zweite Ursache lag woanders: Die Klasse hieß `ach` und traf damit die Erfolgsliste (`.ach` mit
   eigener Schriftgröße). Deshalb jetzt `news-ach`. */
.result-news .news { white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis; display: inline-flex; align-items: center; gap: 5px; padding: 4px 9px; border-radius: 99px; background: #ffffff10; border: 1px solid var(--line); color: var(--ink); font-size: inherit; font-weight: 700; line-height: 1.35; animation: rise .35s ease backwards; }
.result-news .news svg { flex: none; width: 13px; height: 13px; color: var(--gold); }
.result-news .news.news-rank svg { color: var(--cyan); }.result-news .news.news-quest svg { color: #ffb0ff; }
.result-news .news.news-gift svg { color: var(--green); }.result-news .news.news-streak svg { color: #ff9c57; }
/* 🚨 HINTER der Grundregel, nicht im 380-px-Block weiter oben: Bei gleicher Spezifität gewinnt die
   spätere Regel. Auf 320 px bleiben der Marke rund 284 px — eine Nummer kleiner, und zwei passen
   nebeneinander statt einer. */
@media(max-width:380px) {
  .result-news { gap: 3px; font-size: 10.5px; }
  .result-news .news { padding: 3px 7px; gap: 4px; }
  .result-news .news svg { width: 11px; height: 11px; }
}
@media(max-width:430px) { .mode { grid-template-columns: 30px 1fr; padding-left: 11px; padding-right: 8px; gap: 2px 8px; } .mode strong { font-size: 14px; } .mode span { font-size: 10.5px; } }
@media(max-width:380px) { .mode { grid-template-columns: 24px minmax(0,1fr); padding: 8px; gap: 2px 6px; }.mode-icon { width: 24px; height: 24px; } .campaign-bars { gap: 6px; } .chapter { left: 0; right: 0; padding: 8px 10px; } .chapter strong { font-size: 14px; } .chapter small { display: none; } .rank-chip { font-size: 11px; } }
@media(orientation:landscape) and (max-height:600px) { .rank-chip { grid-column: 1; min-height: min(34px, 8.4dvh); padding: 4px 12px; } .menu-grid { grid-row: 1 / 6; } .result-progress { margin-top: 4px; } .result-quests { display: none; } }
@media(min-width:1001px) { .rank-chip { grid-column: 1; max-width: none; min-height: 48px; font-size: 14px; } .rank-chip small { font-size: 12px; } .menu-grid { grid-row: 1 / 7; } #progress-screen .list-scroll { grid-template-columns: minmax(0,600px) minmax(320px,1fr); } .chapter strong { font-size: 18px; } .quest-list { padding: 0 24px; } }
/* Progress bars of the v18 screens: explicit track and fill, since accent-color renders differently per browser. */
.rank-chip progress, .bar-card progress, .ach progress, .quest progress, .result-xp progress { --bar: var(--gold); appearance: none; -webkit-appearance: none; border: 0; background: #ffffff18; color: var(--bar); }
.rank-chip progress, #rank-card progress, .result-xp progress { --bar: var(--cyan); }.quest progress { --bar: #ffb0ff; }.quest.done progress { --bar: var(--green); }.ach.earned progress { --bar: var(--green); }
.rank-chip progress::-webkit-progress-bar, .bar-card progress::-webkit-progress-bar, .ach progress::-webkit-progress-bar, .quest progress::-webkit-progress-bar, .result-xp progress::-webkit-progress-bar { background: #ffffff18; border-radius: 3px; }
.rank-chip progress::-webkit-progress-value, .bar-card progress::-webkit-progress-value, .ach progress::-webkit-progress-value, .quest progress::-webkit-progress-value, .result-xp progress::-webkit-progress-value { background: var(--bar); border-radius: 3px; box-shadow: 0 0 8px color-mix(in srgb, var(--bar) 60%, transparent); transition: width .4s ease; }
.rank-chip progress::-moz-progress-bar, .bar-card progress::-moz-progress-bar, .ach progress::-moz-progress-bar, .quest progress::-moz-progress-bar, .result-xp progress::-moz-progress-bar { background: var(--bar); border-radius: 3px; }
@media(max-width:340px) { .mode strong { font-size: 12px; } .mode span { font-size: 10px; } .menu-tools { gap: 4px; } .menu-tools button, .menu-tools a { padding: 10px 4px; font-size: 11px; white-space: nowrap; } }
.play-button strong { min-width: 0; }.play-button small { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
/* 🚨 ZWEITER BLOCK MIT DERSELBEN BEDINGUNG — UND ER STEHT SPÄTER, ALSO GEWINNT ER. Genau hier stand noch
   `.game { grid-template-columns: 176px minmax(0,1fr) }` und machte die drei Spalten oben still wieder zu
   zweien: Die Ziele landeten in einer IMPLIZITEN dritten Spalte, deren Breite der Inhalt bestimmte, und die
   linke Spalte war 176 statt 148 px breit. Im Quelltext sah oben alles richtig aus. Die Spaltenaufteilung
   steht deshalb jetzt nur noch an EINER Stelle; hier bleiben nur Feinheiten, die dort nichts überschreiben. */
@media(orientation:landscape) and (max-height:600px) { .goal { font-size: 11px; gap: 4px; padding: 3px 6px 6px 3px; flex-wrap: wrap; } .goal canvas { width: 22px; height: 22px; } }
/* Desktop campaign: map and achievements scroll independently, so jumping to the current level never empties the right column. */
/* 🚨 DIE RECHTE SPALTE ROLLT ALS GANZES. Vom Betreiber am Rechner fotografiert: „auf der rechten Seite nach unten
   scrollen … ich sehe nicht den kompletten Inhalt." Vorher rollten nur die Erfolge in der untersten Rasterzeile —
   Fortschrittskarten und Überschrift standen in festen Zeilen darüber, und sobald die höher wurden als das Fenster,
   blieb für die rollende Zeile nichts mehr übrig und der Rest verschwand hinter `overflow:hidden`. Jetzt ist die
   ganze Spalte EIN Rollbereich (`.list-side`), genau wie die Levelkarte daneben. */
@media(min-width:1001px) {
  #progress-screen .list-scroll { overflow: hidden; }
  #progress-screen .level-map { overflow-y: auto; max-height: 100%; overscroll-behavior: contain; padding: 4px 8px 12px 4px; }
  #progress-screen .list-side { grid-column: 2; min-height: 0; max-height: 100%; overflow-y: auto; overscroll-behavior: contain; padding: 0 4px 12px 0; }
  #progress-screen .campaign-extra { margin-top: 0; }
  #progress-screen .campaign-extra .list-subhead { margin-top: 0; }
}

/* ── v19: Nova, the companion ──────────────────────────────────────────────────────────────────── */
@keyframes nova-bob { 50% { transform: translateY(-4px); } }
@keyframes rise-in { from { opacity: 0; transform: translateY(8px); } }
.nova-small { width: 40px; height: 40px; filter: drop-shadow(0 0 8px #7ce9ff66); }
.nova-line { display: flex; align-items: center; gap: 10px; text-align: left; margin-top: 12px; padding: 8px 12px 8px 8px; border-radius: 16px; background: #7ce9ff14; border: 1px solid #7ce9ff44; font-size: 13px; line-height: 1.45; color: var(--ink); }
.nova-line span { min-width: 0; }.nova-line .bump { animation: rise .3s ease; }
@keyframes nudge { 50% { transform: scale(1.035); box-shadow: 0 0 0 8px #ffd77a33, 0 6px 0 #995335; } }.play-button.nudge { animation: nudge .7s ease 3; }
.list-nova { margin: 0 0 10px; padding: 6px 12px 6px 6px; max-width: 560px; }.sheet-nova { margin: 0 20px 8px; }.reward .nova-line { margin: 0 0 12px; }.pause-objective { margin-top: 12px; }
.level-intro .nova-line { margin-top: 12px; font-size: 12.5px; }
.intro-go { width: 100%; min-height: 48px; margin-top: 14px; }
/* The card must never outgrow the board area it sits in (the button was clipped on small phones): it scrolls inside. */
.level-intro { max-height: calc(100% - 12px); overflow-y: auto; overscroll-behavior: contain; }
@media(max-height:650px) and (orientation:portrait) { .level-intro { padding: 12px 14px; } .level-intro h3 { font-size: 20px; margin: 4px 0 2px; } .level-intro p { font-size: 12px; } .intro-goals { margin-top: 8px; gap: 8px; } .intro-goals canvas { width: 28px; height: 28px; } .intro-wish { margin-top: 8px !important; font-size: 11.5px !important; padding: 4px 10px; } .level-intro .nova-line { margin-top: 8px; } .intro-go { min-height: 40px; margin-top: 8px; } }
.level-sheet .nova-line { margin: 0 0 12px; }
.result-hero { display: grid; grid-template-columns: 64px minmax(0,1fr) auto; column-gap: 12px; row-gap: 0; align-items: center; justify-items: start; text-align: left; }
.result-hero .stamp { margin: 0; transform: none; font-size: 10px; padding: 3px 8px; border-width: 2px; }.result-hero h2 { margin: 3px 0 0; font-size: 21px; line-height: 1.1; }
.result-share { grid-column: 3; grid-row: 1 / 3; width: 44px; height: 44px; align-self: center; }
/* Docks: empty places on a card or sheet that borrow the one live Nova from the HUD. The dock keeps the size,
   the landed mount fills it, so nothing shifts while she is on her way. */
/* Novas Bühne (stage.mjs): EINE Figur für die ganze App, als manuelles Popover im Top-Layer, damit sie über jedem
   Blatt und dessen Backdrop liegt. Die Bühne bekommt je Frame Größe und Transform des aktiven Docks; unterwegs
   skaliert nur der Transform. Die UA-Regeln für [popover] (inset:0, margin:auto, Rahmen, Hintergrund) werden hier
   alle zurückgenommen, sonst stünde sie mittig in einem weißen Kasten. */
.nova-stage { position: fixed; inset: auto; left: 0; top: 0; width: 44px; height: 44px; margin: 0; padding: 0; border: 0; background: transparent; overflow: visible; color: inherit; z-index: 120; pointer-events: none; transform-origin: 0 0; will-change: transform; display: block; }
.nova-stage.nova-hidden { visibility: hidden; }
.nova-figure { display: block; position: relative; width: 100%; height: 100%; margin: 0; padding: 0; border: 0; border-radius: 50%; background: transparent; box-shadow: none; line-height: 0; pointer-events: auto; cursor: pointer; touch-action: manipulation; -webkit-tap-highlight-color: transparent; filter: drop-shadow(0 0 8px #7ce9ff66); }
/* 🚨 IHR RING IST RUND UND LIEGT AUSSEN. Die Grundregel für Knöpfe setzt `outline-offset: -3px` —
   drei Punkte NACH INNEN, quer über ihr Gesicht. Und die Bühne selbst bekommt nie einen Ring:
   Sie ist ein quadratischer Kasten um eine runde Figur, dort sähe jeder Ring wie ein Fehler aus. */
.nova-figure:focus-visible { outline: 3px solid var(--cyan); outline-offset: 4px; border-radius: 50%; }
.nova-stage:focus, .nova-stage:focus-visible, .nova-figure:focus:not(:focus-visible) { outline: none; }
.nova-figure svg { width: 100%; height: 100%; overflow: visible; display: block; }
/* Kein brightness-Filter auf dem Druck: zusammen mit dem drop-shadow malte WebKit nur noch ein dunkles Quadrat (v26). */
.nova-figure.pressed { filter: none; transform: translateY(2px) !important; }
.nova-figure.look-hero { filter: none; }.nova-figure.look-hud { filter: drop-shadow(0 0 8px #7ce9ff55); }
.nova-figure.look-big { filter: drop-shadow(0 0 26px color-mix(in srgb, var(--c) 70%, transparent)); }.nova-figure.look-glow { filter: drop-shadow(0 0 22px #7ce9ff66); }
.nova-dock { display: grid; place-items: center; flex: 0 0 auto; cursor: pointer; }
.nova-dock.nova-big, .nova-dock.nova-small { filter: none; }
.result-nova-dock { width: 64px; height: 64px; grid-row: 1 / 3; display: grid; place-items: center; border-radius: 50%; background: radial-gradient(circle,#7ce9ff1c 0 35%,transparent 68%); }
.result-nova-dock::after { content: ''; width: 42%; height: 42%; border: 1px solid #7ce9ff55; border-radius: 50%; box-shadow: 0 0 20px #7ce9ff66; animation: nova-dock 1.4s ease-in-out infinite; }
@keyframes nova-dock { 50% { transform: scale(1.45); opacity: .3; } }
.nova-travelling .nova-figure { pointer-events: none; }
/* Unterwegs aus eigenem Antrieb (`nova-geist.mjs`): Solange sie sich bewegt, fängt sie keine Klicks ab —
   sie fliegt über Knöpfe hinweg, und ein Klick darauf muss den Knopf treffen, nicht sie. Steht sie, ist
   sie wieder antippbar. Bei einem schnellen Satz bekommt sie denselben Schweif wie im Flug. */
.nova-eilt .nova-figure { pointer-events: none; }
.nova-schnell::after { content: ''; position: absolute; inset: -35%; border-radius: 50%; background: radial-gradient(circle,#fff7 0 8%,#7ce9ff55 26%,transparent 68%); filter: blur(2px); z-index: -1; animation: nova-flight 1s ease-out infinite; }
.nova-travelling::after { content: ''; position: absolute; inset: -35%; border-radius: 50%; background: radial-gradient(circle,#fff7 0 8%,#7ce9ff55 26%,transparent 68%); filter: blur(2px); z-index: -1; animation: nova-flight 1s ease-out infinite; }
@keyframes nova-flight { from { transform: scale(.55); opacity: .95; } to { transform: scale(1.8); opacity: 0; } }
.nova-stage.lose .nova-figure { filter: drop-shadow(0 0 22px #ff718366); }
.result.nova-arriving .result-score-row, .result.nova-arriving .result-side, .result.nova-arriving .result-bubble { opacity: 0; }
.result.nova-arrived .result-score-row, .result.nova-arrived .result-side, .result.nova-arrived .result-bubble:not([hidden]) { animation: rise .38s ease backwards; }
.result.nova-arrived .result-score-row { animation-delay: .02s; }.result.nova-arrived .result-side { animation-delay: .12s; }.result.nova-arrived .result-bubble:not([hidden]) { animation-delay: .2s; }
.result-bubble { position: relative; grid-column: 1 / -1; justify-self: stretch; margin: 8px 0 0; padding: 6px 10px; border-radius: 14px; background: #7ce9ff14; border: 1px solid #7ce9ff44; color: var(--ink); font-size: 12.5px; line-height: 1.4; text-align: left; max-width: none; animation: rise .4s ease .25s backwards; }
.result-bubble::before { content: ""; position: absolute; left: 26px; top: -7px; width: 12px; height: 12px; margin-left: -6px; transform: rotate(45deg); background: #1a3548; border: 1px solid #7ce9ff44; border-right: 0; border-bottom: 0; border-radius: 3px 0 0 0; }
.result-wish { display: flex; align-items: center; gap: 8px; margin: 6px 0 0; padding: 5px 10px; font-size: 12px; border-radius: 12px; background: #ffffff0c; border: 1px solid var(--line); font-size: 12.5px; text-align: left; color: var(--muted); animation: rise .4s ease .3s backwards; }
.result-wish svg { color: #ffffff3a; flex: 0 0 auto; }.result-wish span { flex: 1; min-width: 0; line-height: 1.3; }.result-wish b { color: var(--gold); white-space: nowrap; }
.result-wish.done { background: #ffd77a1c; border-color: #ffd77a66; color: var(--ink); }.result-wish.done svg { color: var(--gold); filter: drop-shadow(0 0 6px var(--gold)); }
.intro-wish { margin: 10px 0 0 !important; padding: 6px 12px; border-radius: 12px; background: #ffd77a14; border: 1px solid #ffd77a44; color: var(--gold) !important; font-size: 12.5px !important; font-weight: 700; }.intro-wish.done { color: var(--green) !important; background: #6ff0bd14; border-color: #6ff0bd44; }
.level-wish { margin: 0 0 10px; padding: 6px 12px; border-radius: 12px; background: #ffd77a14; border: 1px solid #ffd77a44; color: var(--gold); font-size: 12.5px; font-weight: 700; }.level-wish.done { color: var(--green); background: #6ff0bd14; border-color: #6ff0bd44; }
/* ── Die Story-Szene ──────────────────────────────────────────────────────
   🚨 SIE IST KEIN BLATT MEHR, SIE IST EINE SZENE. Gewünscht: „Wenn Nova Dialoge vom
   spielfortschritt erzählt möchte ich das die spielmenus verschwinden bis auf den Sternen
   Hintergrund, Nova soll deutlich größer werden und der Text Dialog soll richtig cool dargestellt
   werden."

   Also: Das Blatt füllt den Schirm, hat keinen eigenen Hintergrund und keinen Rahmen — was man
   sieht, ist der echte Sternenhimmel der Welt (`.ambient`), der dahinter WEITERLÄUFT. Die Menüs
   und das Brett darüber blendet `body.story-an .shell` aus; gelöscht wird nichts, sie stehen nach
   dem letzten „Weiter" unverändert wieder da.

   ⚠️ UND DER HIMMEL MUSS SICHTBAR BLEIBEN. `dialog::backdrop` verdunkelt sonst zu 84 %; hier ist
   er nur ein weicher Schleier am oberen und unteren Rand, damit Kicker und Text lesbar bleiben,
   ohne den Himmel zuzudecken. Dass er dabei weiterzieht, steht in `fx.mjs`: Die Bildsperre für
   offene Blätter nimmt die Story ausdrücklich aus. */
.story { --c: var(--cyan); text-align: center; overflow: hidden;
  width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh;
  padding: var(--sa-oben) var(--sa-rechts) var(--sa-unten) var(--sa-links);
  border: 0; border-radius: 0; background: transparent; box-shadow: none;
  grid-template-rows: minmax(0,1fr) auto; place-items: center; }
/* 🚨 `display` STEHT AM ZUSTAND, NICHT AN DER KLASSE. Ein `dialog` ist nur deshalb unsichtbar, solange er
   zu ist, weil der Browser selbst `dialog:not([open]) { display: none }` mitbringt. Wer `display: grid`
   an `.story` schreibt, schlaegt diese Regel — und die geschlossene Szene steht mitten im Menue, wo
   ihr „Weiter"-Knopf quer ueber der Fusszeile lag. Darum hier `[open]`, wie bei jedem anderen Blatt im
   Haus auch (`.result[open]`, `.nova-shop[open]`). Die Masse bleiben an `.story`, damit der
   Querformat-Block sie mit seiner niedrigeren Spezifitaet weiter abwandeln kann. */
.story[open] { display: grid; }
.story::backdrop { background: linear-gradient(180deg,#05030fcc 0,#05030f44 26%,#05030f44 58%,#05030fe8 100%); }
body.story-an .shell { opacity: 0; pointer-events: none; transition: opacity .4s ease; }

/* Die Bühne: Nova gross und mittig, ihr Licht dahinter. Der Kasten ist nur noch der Platz, den sie
   bekommt — Sterne malt der Himmel selbst, die vier gemalten Punkte von früher sind weg. */
.story-scene { position: relative; width: 100%; display: grid; place-items: center; align-self: center;
  background: none; border: 0; }
.story-scene::before { content: ""; position: absolute; width: min(86vw,520px); aspect-ratio: 1;
  border-radius: 50%; background: radial-gradient(circle, color-mix(in srgb, var(--c) 34%, transparent) 0,
  color-mix(in srgb, var(--c) 10%, transparent) 42%, transparent 68%); pointer-events: none;
  animation: story-atem 5s ease-in-out infinite; }
@keyframes story-atem { 50% { transform: scale(1.07); opacity: .82; } }
body.reduced .story-scene::before { animation: none; }
/* 🚨 SIE WÄCHST MIT DEM SCHIRM, NICHT AUF EINE FESTE ZAHL. 160 px waren im Blatt richtig; auf dem
   ganzen Schirm sind sie verloren. `min()` gegen BEIDE Achsen, sonst ragt sie quer aus dem Bild. */
.story .nova-big { width: min(54vh,74vw,380px); height: min(54vh,74vw,380px);
  filter: drop-shadow(0 0 40px color-mix(in srgb, var(--c) 75%, transparent)); }

/* Der Text: eine Bildunterschrift, kein Kasten. Eine Linie in der Kapitelfarbe trägt ihn, darunter
   steht er gross genug, um auf Armlänge gelesen zu werden. */
.story-fuss { width: min(100%,620px); padding: 0 20px 18px; }
.story .eyebrow { display: block; margin: 0 0 10px; color: var(--c); font-size: 11px; letter-spacing: .28em;
  text-shadow: 0 0 18px color-mix(in srgb, var(--c) 60%, transparent); }
.story-bubble { position: relative; margin: 0; padding: 16px 4px 4px; border: 0; border-radius: 0;
  background: none; min-height: 104px; text-align: center; cursor: pointer; }
.story-bubble::before { content: ""; position: absolute; top: 0; left: 50%; width: min(220px,70%); height: 2px;
  transform: translateX(-50%); border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--c), transparent);
  box-shadow: 0 0 14px color-mix(in srgb, var(--c) 70%, transparent); }
.story-bubble p { margin: 0; color: var(--ink); font-size: clamp(16px,4.4vw,21px); line-height: 1.55;
  font-weight: 600; text-shadow: 0 2px 18px #000a; }
.story-bubble p[data-done="0"]::after { content: "▍"; color: var(--c); animation: twinkle 1s steps(2) infinite; }
.story-dots { display: flex; justify-content: center; gap: 7px; margin: 16px 0 10px; }
.story-dots i { width: 8px; height: 8px; border-radius: 50%; background: #ffffff22; transition: background .2s; }
.story-dots i.on { background: var(--c); box-shadow: 0 0 10px var(--c); }
.story-dots i.done { background: color-mix(in srgb, var(--c) 45%, transparent); }
.story .primary { width: min(100%,340px); margin: 0 auto; }
@media(max-height:650px) and (orientation:portrait) { .level-intro .nova-line { font-size: 11.5px; padding: 6px 10px 6px 6px; } .level-intro .nova-small { width: 30px; height: 30px; } }
@media(orientation:landscape) and (max-height:600px) { .level-intro .nova-line { display: none; } .result-bubble { display: none; }
  /* Quer steht Nova NEBEN dem Text statt darüber — übereinander bliebe für beides zu wenig Höhe.
     Bildfüllend bleibt die Szene auch hier. */
  .story { grid-template-rows: minmax(0,1fr); grid-template-columns: minmax(0,1fr) minmax(0,1.1fr); align-items: center; }
  .story-scene::before { width: min(52vh,42vw); }
  .story .nova-big { width: min(64vh,34vw,300px); height: min(64vh,34vw,300px); }
  .story-fuss { width: min(100%,520px); padding: 0 24px 0 8px; text-align: left; }
  .story-bubble { text-align: left; min-height: 0; }
  .story-bubble::before { left: 0; transform: none; width: min(180px,60%); }
  .story-bubble p { font-size: clamp(14px,2.4vw,18px); }
  .story-dots { justify-content: flex-start; margin: 12px 0 8px; }
  .story .primary { margin: 0; }
}

/* Nova's targeting tools reuse the dock, preserving the full board on small screens. */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }

.nova-excursion .nova-figure { pointer-events: none; }
.nv-steam { opacity: 0; }
.nova-figure[data-reaction] .nv-steam { opacity: .9; animation: nova-steam .65s ease-in-out infinite alternate; }
.nova-figure[data-reaction="angry"] { filter: drop-shadow(0 0 12px #ff6555aa); }
@keyframes nova-steam { to { transform: translateY(-5px); opacity: .4; } }
body.reduced .nv-steam { animation: none; }

/* The shop scrolls only its catalogue. Header, explanation and the live preview always stay visible. */
.nova-shop[open] { overflow: hidden; display: grid; grid-template-rows: auto auto auto minmax(0,1fr); width: min(92vw,620px); padding: 18px; }
.nova-shop .sheet-head { position: static; padding: 0 0 8px; background: none; }
.nova-shop .sheet-head h2 { margin: 4px 0 6px; font-size: clamp(25px,7vw,34px); }
.shop-intro { color: var(--muted); font-size: 13px; line-height: 1.5; }
.nova-shop > .shop-intro { margin: 0 0 10px; }
.shop-preview { display: grid; grid-template-columns: 112px minmax(0,1fr); gap: 12px; align-items: center; min-height: 144px; padding: 12px; border-radius: 20px; background: #7ce9ff0a; }
.shop-preview .nova-dock { width: 112px; height: 112px; }
.shop-preview p { min-height: 3em; font-size: 13px; }
.shop-details { min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 0 4px 12px 0; }
.shop-items { display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); gap: 10px; }
.shop-item { display: flex; flex-direction: column; gap: 8px; padding: 12px; border: 1px solid var(--line); border-radius: 16px; }
.shop-item b { font-size: 14px; min-height: 2.5em; }
.shop-item button { width: 100%; min-height: 44px; padding: 8px; font-size: 12px; margin: 0; }
.coin-note { color: var(--gold); font-size: 13px; }
.nova-accessory { position: absolute; inset: 0; pointer-events: none; color: #ff8ac6; font-size: 24px; letter-spacing: .4em; opacity: 0; transform: translateY(12px) scale(.55); text-shadow: 0 0 12px currentColor; transition: opacity .24s ease,transform .42s cubic-bezier(.2,1.4,.4,1); }
.nova-accessory.show { opacity: 1; transform: none; animation: nova-accessory 1.4s .42s ease-in-out infinite alternate; }
.nova-accessory.leaving { opacity: 0; transform: translateY(-18px) scale(1.25); animation: none; }
[data-cosmetic=star] .nova-accessory { color: var(--gold); }
[data-cosmetic=orbit] .nova-accessory { color: var(--cyan); }
[data-cosmetic=confetti] .nova-accessory { color: #ff9c57; }
[data-cosmetic=aurora] .nova-accessory { color: #82ffd5; }
[data-cosmetic=frost] .nova-accessory { color: #d9fbff; }
[data-cosmetic=rocket] .nova-accessory { color: #ffb45f; }
[data-cosmetic=crown] .nova-accessory { color: #ffe275; }
[data-cosmetic=glitch] .nova-accessory { color: #b191ff; text-shadow: -4px 0 #55eaff88,4px 0 #ff5fa288; }
@keyframes nova-accessory { to { transform: translateY(-12px) rotate(12deg); } }
body.reduced .nova-accessory { animation: none; }
@media(max-width:480px) { .shop-items { grid-template-columns: 1fr; }.shop-item { display: grid; grid-template-columns: minmax(0,1fr) 78px 82px; align-items: center; padding: 10px; }.shop-item b { min-height: 0; font-size: 12px; } }

/* Hidden after the third full anger episode: a short target game made from the regular stones. */
.nova-egg { justify-content: center; align-items: center; padding: 10px 16px max(14px,var(--sa-unten)); background: radial-gradient(circle at 50% 20%,#e34e6750,transparent 38%),radial-gradient(circle at 50% 85%,#7ce9ff20,transparent 45%); }
.egg-card { width: min(100%,760px); height: min(100%,900px); display: grid; grid-template-rows: auto auto auto minmax(0,1fr) auto; gap: 8px; padding: 14px; border: 1px solid #ff7c8e66; border-radius: 28px; background: linear-gradient(155deg,#251947ee,#0c2a3bee); box-shadow: 0 24px 80px #0008,inset 0 1px #ffffff18; overflow: hidden; }
.egg-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }.egg-head > div { min-width: 0; }.egg-head h1 { margin: 2px 0 0; font-size: clamp(22px,6vw,40px); line-height: 1; overflow-wrap: anywhere; }.egg-head .eyebrow { color: #ff91a0; }
.egg-hero { display: grid; grid-template-columns: clamp(150px,35vw,250px) minmax(0,1fr); align-items: center; min-height: 180px; padding: 4px 14px; border-radius: 22px; background: radial-gradient(circle at 24% 50%,#ff637c2d,transparent 55%),#ffffff08; }
.egg-nova { width: clamp(150px,35vw,250px); height: clamp(150px,35vw,250px); }
.egg-hero > div { min-width: 0; }.egg-hero p { margin: 0 0 12px; color: white; font-size: clamp(14px,3vw,19px); font-weight: 800; line-height: 1.35; overflow-wrap: anywhere; }
.egg-stats { display: flex; flex-wrap: wrap; gap: 6px; }.egg-stats span { padding: 6px 9px; border-radius: 99px; background: #ffffff0e; color: var(--muted); font-size: 10px; letter-spacing: .08em; }.egg-stats b { color: var(--gold); font-size: 15px; }
.egg-rule { margin: 0; color: var(--muted); text-align: center; font-size: 12px; }
/* 🚨 DAS VERHÄLTNIS KOMMT VON DER KACHEL, NICHT VOM BRETT. Gemeldet: „mobil am handy sieht alles gut aus,
   im browser am pc sind die spielsteine aber verzerrt" — Käfer und Fenster standen in hochkant gezogenen
   Kästen. Derselbe Widerspruch wie damals im Hochformat, nur an einer Stelle, die der damalige Block nicht
   erreicht hat: `height: 100%` (die Zeile des Kartengitters) macht die Höhe fest, `max-width` deckelt die
   Breite — und `aspect-ratio` hat keine Achse mehr, über die es sich durchsetzen könnte. Es verliert
   still, und die Zeilen werden höher als die Spalten breit sind.

   Der Fix von damals stand in `@media(orientation:portrait)` und galt deshalb nur dort. Ein Browserfenster
   am PC ist quer und höher als 600 px, fällt also weder in diesen Block noch in den kurzen Querformat-Block
   darunter — und landete genau auf der widersprüchlichen Grundregel. Darum steht die Lösung jetzt HIER, in
   der Grundregel selbst: Die KACHEL ist quadratisch (`aspect-ratio: 1`), das Brett wird so breit, wie
   Breite UND Höhe es zulassen (`dvh` deckelt gegen die flache Seite), und die Zeilenhöhe ergibt sich
   daraus. Ein Verhältnis aus einer Regel statt zwei, die sich streiten. */
.egg-board { width: min(100%,430px,46dvh); height: auto; max-width: none; min-width: 0; min-height: 0;
  place-self: center; display: grid; grid-template-columns: repeat(5,minmax(0,1fr)); grid-auto-rows: auto;
  align-content: center; gap: clamp(4px,1.5vw,8px); }
.egg-tile { position: relative; aspect-ratio: 1; min-width: 0; min-height: 0; padding: 0; border: 1px solid #ffffff13; border-radius: 15px; background: linear-gradient(145deg,#194052,#0b2434); box-shadow: 0 4px 0 #061722; overflow: hidden; transition: transform .18s ease,filter .18s ease,background .18s ease; }
.egg-tile canvas { width: 100%; height: 100%; display: block; }.egg-tile.hot { background: #ff65792c; border-color: #ff8fa0; filter: drop-shadow(0 0 10px #ff637c); animation: egg-hot .55s ease-in-out infinite alternate; }.egg-tile.hit { animation: egg-hit .32s ease-out; }
/* 🚨 DER WECHSEL INS EASTER EGG IST EINE ÜBERNAHME, KEIN BILDSCHIRMWECHSEL. Gewünscht: „die
   Animation cooler aussehen wenn das Menu verschwindet und das easter egg Spiel beginnt."
   Vorher wurde `display` umgeschaltet — das Menü war in einem Bild weg, die Karte in einem Bild da.

   Jetzt geht die Energie von NOVA aus: Ihre Mitte steht als `--egg-x/--egg-y` bereit (gesetzt in
   `openNovaEgg()`), von dort fährt eine Druckwelle über den Schirm und deckt den harten Schnitt zu.
   Dahinter klappt ihre Karte auf, und die Steine fallen versetzt ein — ihre Steine, ihre Regeln.

   ⚠️ Die Welle liegt UNTER Novas Bühne (die ist im Top-Layer) und über der Hülle, damit sie das
   Menü verschluckt, aber nicht sie selbst. */
.egg-flash { display: none; position: fixed; inset: 0; z-index: 40; pointer-events: none; opacity: 0;
  transform-origin: var(--egg-x,50%) var(--egg-y,50%);
  background: radial-gradient(circle at var(--egg-x,50%) var(--egg-y,50%),
    #ffd9e0 0 6%, #ff6d86 14%, #ff6d8666 34%, #ff6d8600 64%); }
.egg-flash::after { content: ""; position: absolute; inset: 0;
  background: radial-gradient(circle at var(--egg-x,50%) var(--egg-y,50%),
    transparent 0 30%, #ffe3e9cc 33%, transparent 38%); opacity: 0; }
/* ⚠️ `display` statt nur `opacity`: Eine ganzflächige Ebene mit `opacity: 0` bleibt eine Ebene —
   der Browser hält sie die ganze Sitzung über bereit, für neunzehn Zwanzigstel Spielzeit, in der
   sie nie zu sehen ist. Eine Animation startet auch aus `display: none` heraus. */
body.egg-start .egg-flash { display: block; animation: egg-welle .9s cubic-bezier(.2,.8,.2,1) both; }
body.egg-start .egg-flash::after { animation: egg-ring .8s cubic-bezier(.15,.85,.25,1) both; }
@keyframes egg-welle { 0% { opacity: 0; transform: scale(.05); } 14% { opacity: 1; } 60% { opacity: .85; } 100% { opacity: 0; transform: scale(2.6); } }
@keyframes egg-ring { 0% { opacity: 0; transform: scale(.1); } 20% { opacity: 1; } 100% { opacity: 0; transform: scale(3.2); } }
body.egg-start .egg-card { animation: egg-karte .68s cubic-bezier(.2,.9,.25,1.05) .14s both; }
@keyframes egg-karte { from { opacity: 0; transform: translateY(20px) scale(.9) rotate(-1.4deg); filter: blur(7px); }
  to { opacity: 1; transform: none; filter: none; } }
body.egg-start .egg-tile { animation: egg-einschlag .42s cubic-bezier(.2,.9,.3,1.25) both;
  animation-delay: calc(var(--i,0) * 22ms + 280ms); }
@keyframes egg-einschlag { from { opacity: 0; transform: scale(.35) rotate(-9deg); } to { opacity: 1; transform: none; } }
.egg-actions { display: grid; grid-template-columns: minmax(0,1fr) auto; gap: 10px; align-items: center; }.egg-actions progress { width: 100%; height: 8px; accent-color: #ff758b; }.egg-actions .primary { margin: 0; min-height: 42px; padding: 8px 16px; }
@keyframes egg-hot { to { transform: translateY(-3px) scale(1.05); } }
@keyframes egg-hit { 50% { transform: scale(.72) rotate(12deg); opacity: .35; } }
@media(max-width:480px) { .egg-stats { justify-content: center; }.egg-head .round-button { width: 40px; height: 40px; }.egg-hero { grid-template-columns: 1fr; justify-items: center; min-height: 245px; padding: 0 10px 12px; text-align: center; }.egg-nova { width: 190px; height: 190px; margin-bottom: -20px; } }
/* 🚨 HOCHKANT WAREN DIE KACHELN KEINE QUADRATE. Gemeldet: „Fixe das easter Egg Spiel in Portrait
   wirkt es komisch!" — und genau das war es: Käfer und Fenster standen in hochkant gezogenen
   Kästen mit totem Raum darüber und darunter.

   Die Ursache ist ein Widerspruch in drei Zeilen: Das Brett bekam `height: 100%` (die Zeile des
   Kartengitters), dazu `aspect-ratio: 5/4` — und dann eine Klammer `max-width: 340px`. Die Höhe
   ist damit fest, die Breite gedeckelt, und das Seitenverhältnis hat keine Achse mehr, über die
   es sich durchsetzen könnte: 340 breit bei 411 hoch statt 340 bei 272.

   Quer stimmt es, dort gibt es die Klammer nicht — die Breite folgt der Höhe. Hochkant dreht
   dieser Block die Richtung um: Die KACHEL ist quadratisch (`aspect-ratio: 1`), das Brett wird so
   breit, wie Breite UND Höhe es zulassen (`dvh` deckelt gegen die flache Seite), und die Zeilen
   ergeben sich daraus. Damit ist das Verhältnis nicht mehr zwei Regeln, die sich widersprechen,
   sondern eine, die aus der Kachel folgt. */
@media(orientation:portrait) {
  /* 🚨 DIE KARTE IST SO HOCH WIE IHR INHALT, NICHT WIE DER SCHIRM. Mit `height: min(100%,900px)`
     stand hochkant ein leeres Band über und unter dem Brett: Das Brett kann nicht breiter werden
     als die Karte, seine Höhe folgt daraus — die übrige Höhe war Luft im Inneren. Jetzt schrumpft
     die Karte auf ihren Inhalt und steht mittig im Schirm; die Luft liegt aussen, wo sie hingehört. */
  .egg-card { height: auto; max-height: min(100%,900px); }
    /* Nova und ihr Kasten wachsen mit dem Schirm, damit dem Brett auf kurzen Geräten Platz bleibt. */
  .egg-hero { min-height: min(260px,30dvh); }
  .egg-nova { width: min(210px,24dvh); height: min(210px,24dvh); }
}
@media(orientation:portrait) and (max-width:480px) {
  .egg-board { width: min(100%,340px,44dvh); }
}

@media(max-height:700px) and (orientation:portrait) { .egg-hero { min-height: 150px; grid-template-columns: 120px minmax(0,1fr); justify-items: stretch; padding: 4px 10px; text-align: left; }.egg-nova { width: 120px; height: 120px; margin: 0; }.egg-card { padding: 10px; }.egg-board { max-width: min(100%,340px); width: min(100%,340px,38dvh); } }

@media(orientation:landscape) and (max-height:600px) {
  .nova-shop[open] { grid-template-columns: minmax(0,1fr) minmax(0,1.1fr); grid-template-rows: auto auto minmax(0,1fr); gap: 8px 16px; width: min(94vw,840px); height: calc(var(--vvh,100dvh) - 32px); padding: 16px; }
  .nova-shop .sheet-head { position: static; grid-column: 1 / -1; margin: 0; padding: 0; }
  .nova-shop h2 { font-size: 24px; }
  .nova-shop > .shop-intro { grid-column: 1 / -1; margin: 0; font-size: 11px; }
  .shop-details { min-height: 0; overflow: auto; overscroll-behavior: contain; }
  .shop-preview { align-self: start; grid-template-columns: 88px minmax(0,1fr); padding: 8px; min-height: 0; gap: 8px; }
  .shop-preview .nova-dock { width: 88px; height: 88px; }
  .shop-preview p { font-size: 11px; margin: 6px 0; }
  .shop-preview .mini { font-size: 11px; padding: 8px; }
  .shop-item { padding: 8px; }
  .shop-item b { font-size: 11px; }
  .shop-details > .coin-note { margin-top: 0; }
  .nova-egg { padding: 6px 12px; }.egg-card { width: min(100%,920px); height: 100%; grid-template-columns: minmax(0,.8fr) minmax(0,1.2fr); grid-template-rows: auto auto minmax(0,1fr) auto; gap: 6px 16px; padding: 10px 14px; }.egg-head { grid-column: 1 / -1; }.egg-head h1 { font-size: 25px; }.egg-hero { grid-column: 1; grid-row: 2 / 5; grid-template-columns: 1fr; align-content: center; justify-items: center; min-height: 0; text-align: center; }.egg-nova { width: min(31vh,180px); height: min(31vh,180px); }.egg-rule { grid-column: 2; grid-row: 2; }.egg-board { grid-column: 2; grid-row: 3; width: min(100%,58dvh); }.egg-actions { grid-column: 2; grid-row: 4; }
}

/* ── Große Bildschirme ─────────────────────────────────────────────────────
   Gemeldet: „das spiel sollte für browser, also pc optimiert werden. da nutzen wir den platz noch
   nicht gut! außerdem sollen die scrollbars kleiner dargestellt werden in chrome!"

   🚨 DIESER BLOCK MUSS AM ENDE DER DATEI STEHEN. Die Breiten-Deckel der Bildschirme und Blätter
   (`.screen-list`, `dialog`) stehen weiter oben OHNE Media-Query. Bei gleicher Spezifität gewinnt
   die spätere Regel — deshalb war der Deckel aus `@media(min-width:1001px)` (Zeile ~288)
   wirkungslos: „Novas Reise" und die Rangliste standen auch auf 2560 px als 940-px-Säule in der
   Mitte, links und rechts je 810 px leer. Wer diese Regeln nach oben schiebt, schaltet sie wieder ab.

   Das Spielbrett selbst bleibt unangetastet: Es füllt am Rechner schon die volle Höhe (auf 2560×1440
   sind das 1352 px im Quadrat), und breiter als hoch kann ein quadratisches Brett nicht werden.
   Handygrößen bleiben ebenfalls, wie sie sind — alles hier greift erst ab 1001 px. */

/* Schlanke Rollbalken statt der breiten Standardleiste von Chrome. Beide Schreibweisen stehen da:
   Chrome ab 121 und Firefox nehmen `scrollbar-width`/`scrollbar-color` und ignorieren dann die
   `::-webkit-`-Pseudoelemente; ältere Chrome- und alle Safari-Versionen kennen nur diese. Nur für
   Zeigergeräte — auf dem Handy überlagern sich die Balken ohnehin und würden sonst dauerhaft stehen. */
@media(hover:hover) and (pointer:fine) {
  html { scrollbar-width: thin; scrollbar-color: #ffffff30 transparent; }
  *, *::before, *::after { scrollbar-width: thin; scrollbar-color: #ffffff30 transparent; }
  ::-webkit-scrollbar { width: 9px; height: 9px; }
  ::-webkit-scrollbar-track, ::-webkit-scrollbar-corner { background: transparent; }
  ::-webkit-scrollbar-thumb { border-radius: 99px; border: 2px solid transparent; background: #ffffff30; background-clip: padding-box; }
  ::-webkit-scrollbar-thumb:hover { background: #ffffff55; background-clip: padding-box; }
}

@media(min-width:1001px) {
  /* Der Deckel von oben, hier endlich wirksam. */
  .screen-list { width: min(100%,1180px); padding: 12px 32px 20px; }
  /* Blätter dürfen breiter werden, sobald der Bildschirm es hergibt: 520 px auf einem 1920er sind eine
     Säule mit 700 px Leere daneben, und der Inhalt rollt trotzdem. */
  dialog { width: min(92vw,560px); }
  dialog.sheet { width: min(92vw,620px); }
  .result-card { width: min(94vw,520px); }
}

/* Ab hier stehen die Blätter zweispaltig: Einstellungen und Spielregeln passen dann ohne Rollen auf
   den Schirm. `columns` statt Raster, weil es die Spalten von selbst gleich hoch macht — ein Raster
   ließe unter „Profil" eine Lücke in der Höhe von „Audio & Haptik" stehen. Der stehende Kopf
   (`.sheet-top`, siehe oben) bleibt außerhalb: In einem Mehrspaltenkasten gibt es kein `sticky`. */
@media(min-width:1280px) {
  dialog.sheet { width: min(92vw,940px); }
  /* 🚨 KEINE SONDERBEHANDLUNG FÜR DAS ERSTE ELEMENT im Kasten. `:first-child` trifft nur das erste im
     HTML, also den Kopf der LINKEN Spalte; die rechte behielte ihren Abstand, und beide Spalten
     begännen auf verschiedener Höhe. Alle Kästen sehen gleich aus, dann fluchten die Spalten. */
  .sheet-cols { columns: 2; column-gap: 26px; margin-top: 10px; }
  .sheet-cols > * { break-inside: avoid; }
  /* Die Baustein-Übersicht darunter darf die volle Breite nutzen statt in drei weiten Spalten zu stehen. */
  .help-modules { grid-template-columns: repeat(auto-fit,minmax(88px,1fr)); }
  .settings .primary { width: calc(100% - 48px); }
}
@media(min-width:1600px) {
  dialog.sheet { width: min(88vw,1060px); }
  .sheet-cols { column-gap: 34px; }
}

/* Novas Reise: die Karte darf mitwachsen (ihre Straße ist in Prozent gezeichnet, siehe
   `renderProgress()`), und die Erfolge daneben stehen zu zweit statt untereinander. */
@media(min-width:1500px) {
  .screen-list { width: min(100%,1480px); }
  #progress-screen .list-scroll { grid-template-columns: minmax(0,680px) minmax(360px,1fr); column-gap: 48px; }
  #progress-screen .head-books { max-width: 680px; }
  #progress-screen .achievements { grid-template-columns: repeat(2,minmax(0,1fr)); }
  /* Drei Karten nebeneinander brauchen mehr als 168 px, sonst bricht „Novas Vertrauen · Neu hier" um
     und die Reihe steht auf drei verschiedenen Höhen. */
  #progress-screen .campaign-bars { grid-template-columns: repeat(auto-fit,minmax(230px,1fr)); }
  .node { --w: 128px; }
  /* Die Rangliste rollt in zwei Spalten, Platz 1 oben links. `columns` füllt die linke Spalte zuerst,
     ein Raster würde Platz 2 neben Platz 1 setzen und die Reihenfolge zerreißen. Die Klasse setzt
     `loadBoard()` erst ab genug Einträgen — bei drei Namen sähen zwei Spalten albern aus. */
  .leader-list.two-up { columns: 2; column-gap: 20px; }
  .leader-list.two-up .row { break-inside: avoid; }
}
@media(min-width:2000px) {
  .screen-list { width: min(100%,1680px); }
  #progress-screen .list-scroll { grid-template-columns: minmax(0,760px) minmax(420px,1fr); }
  #progress-screen .head-books { max-width: 760px; }
  .hero { width: min(100%,1380px); column-gap: 90px; }
  .nova-hero { width: 180px; height: 180px; }
}

/* ── Weltenauswahl ─────────────────────────────────────────────────────────
   Erscheint erst ab zwei Welten (`renderWorlds()` setzt `hidden`). Eine Zeile, die am Handy rollt statt
   umzubrechen — bei sechs Welten bräche sie sonst in drei Reihen und schöbe die Levelkarte aus dem Bild.
   Eine gesperrte Welt bleibt sichtbar und lesbar: Man soll sehen, dass es weitergeht. */
.world-picker { display: flex; gap: 8px; margin: 0 0 10px; overflow-x: auto; scroll-snap-type: x proximity; padding-bottom: 2px; }
.world-picker[hidden] { display: none; }
.world-chip { flex: 0 0 auto; scroll-snap-align: start; display: grid; gap: 1px; text-align: left; padding: 8px 14px; min-height: 48px; border-radius: 14px; background: var(--panel); border: 1px solid var(--line); color: var(--ink); }
.world-chip b { font-size: 14px; }
.world-chip small { font-size: 11px; color: var(--muted); }
.world-chip.active { background: #7ce9ff1f; border-color: #7ce9ff77; }
.world-chip.active b { color: var(--cyan); }
.world-chip.locked { opacity: .55; }
.world-chip.locked b { color: var(--muted); }
@media(min-width:1001px) { .world-chip { padding: 10px 18px; } .world-chip b { font-size: 15px; } }
