/* Schulchat — Eltern. Mobile first, 360–430 px.
   Der Raum lebt auf einem festen Design-Canvas von 400 x 376 px und wird
   als Ganzes skaliert (contain). Dadurch stimmen alle Abstände auf jedem
   Gerät und keine Illustration wird verzerrt. */

:root {
  --wall:        #ece3d4;
  --wall-shade:  #ded2bf;
  --wall-side:   #ddd1bc;
  --floor:       #c79a68;
  --floor-dark:  #b3865a;
  --wood:        #c08a4e;
  --wood-dark:   #8e5e2f;
  --wood-light:  #dcab6d;
  --board:       #2e4a3e;
  --board-frame: #9c6b3c;
  --chalk:       #f3f0e6;
  --ink:         #23303c;
  --brand:       #1d3a5f;
  --brand-2:     #2f6fb0;
  --accent:      #e2584d;
  --ok:          #3f8f5f;
  --paper:       #ffffff;
  --muted:       #6d7a86;
  --line:        #dfe4e9;

  --handy-h: 50vh;
  --night: 0;               /* 0 = Tag, 1 = Nacht */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0; padding: 0; height: 100%;
  overscroll-behavior: none;
  background: #0f1720;
  color: var(--ink);
  font-family: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;
  font-size: 15px;
  -webkit-font-smoothing: antialiased;
}
body { overflow: hidden; }

/* Das Zimmer liegt als Hintergrundbild über die ganze Fläche, das Handy
   darüber (Rückmeldung Board, 18.09.). Darum sind beide Kinder absolut
   gesetzt statt gestapelt: das Handy nimmt dem Zimmer keine Höhe weg, es
   deckt beim Hochziehen nur mehr davon zu. */
.app {
  position: fixed; inset: 0;
  max-width: 460px; margin: 0 auto;
  /* Keine Kopfzeile mehr — das Zimmer beginnt zuoberst. Der Streifen unter
     der Notch bekommt die Wandfarbe, damit die Wand einfach weitergeht. */
  padding-top: var(--safe-top);
  background: var(--wall);
  box-shadow: 0 0 60px rgba(0,0,0,.35);
}

/* ---------- Logo und Kind-Chip (in der Handy-Statusleiste) ---------- */

.logo { display: flex; align-items: center; gap: 5px; }
.logo-word {
  font-weight: 800; letter-spacing: .08em; font-size: 12.5px;
}
.logo-bubble {
  position: relative;
  background: #fff; color: var(--brand);
  font-weight: 800; letter-spacing: .08em; font-size: 11px;
  padding: 2px 7px 3px; border-radius: 9px 9px 9px 2px;
}
.logo-bubble::after {
  content: ""; position: absolute; left: 2px; bottom: -4px;
  border: 4px solid transparent;
  border-top-color: #fff; border-right-color: #fff;
  border-bottom-width: 0; border-left-width: 2px;
}

.kind-chip {
  display: flex; align-items: center; gap: 6px;
  background: rgba(255,255,255,.16); border: 0; color: #fff;
  padding: 3px 8px 3px 3px; border-radius: 999px;
  font: inherit; font-size: 11.5px; font-weight: 600; cursor: pointer;
}
.kind-avatar {
  width: 19px; height: 19px; border-radius: 50%;
  background: #f2c14e; color: #4a3a12;
  display: grid; place-items: center; font-weight: 800; font-size: 10.5px;
}
.kind-chev {
  width: 0; height: 0; margin-left: 1px;
  border: 4px solid transparent; border-top-color: rgba(255,255,255,.8); border-bottom: 0;
}

/* ---------- Bühne / Raum ---------- */

.stage {
  /* Die ganze Fläche, nicht der Rest über dem Handy. Das Zimmer behält
     dadurch immer dieselbe Grösse. */
  position: absolute; inset: 0;
  overflow: hidden;
  /* Unter der Komposition geht der Boden weiter — darum die Bodenfarbe. */
  background: var(--floor-dark);
}
/* Der Boden unter dem Canvas muss nachts mitdunkeln, sonst leuchtet unten
   ein heller Streifen. */
.stage::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  z-index: 5;
  background: linear-gradient(180deg, rgba(16,26,52,.44), rgba(10,18,38,.56));
  opacity: var(--night);
  transition: opacity 1.1s ease;
}
/* Unter der Komposition läuft der Boden bis zum Handy weiter. Vorher stand
   dort ein fremder Streifen (Rückmeldung Board, 18.09.: «die Ränder an der
   Seite sollen der Boden sein»). Die Dielenfugen kommen im selben Winkel
   und im selben, mitskalierten Abstand wie im Canvas — so läuft die Naht
   nicht auseinander. */
.floor-ext {
  position: absolute; left: 0; right: 0; z-index: 1;
  top: var(--raum-h, 376px); bottom: 0;
  background: linear-gradient(180deg, var(--floor-dark) 0%, #a67c51 100%);
}
/* Unter dem Canvas laeuft der Boden einfarbig aus. Dort noch einmal Fugen
   zu zeichnen hiesse, ein zweites Raster neben das perspektivische zu
   legen — und zwei Raster widersprechen sich immer. */
.room {
  position: absolute; inset: 0; z-index: 10;
  touch-action: none;
}
.room-canvas {
  position: absolute; left: 0; top: 0;
  width: 400px; height: 376px;
  /* Oben links verankert: der Canvas wird über die Breite eingepasst und
     füllt den Rahmen damit genau — links und rechts bleibt kein Streifen. */
  transform-origin: top left;
  transform: scale(var(--k, 1));
  perspective: 900px;
  /* Fest auf die Mitte der Komposition: sonst würde die Seitenwand ihre
     Flucht ändern, sobald sich die Höhe des Canvas ändert. */
  perspective-origin: 200px 188px;
  /* Die gedrehte Seitenwand ragt perspektivisch über die Unterkante hinaus —
     der Canvas ist die Komposition, also wird dort abgeschnitten. */
  overflow: hidden;
}
.layer { position: absolute; inset: 0; }

/* Wand und Fensterrahmen nehmen keine Klicks an. Sie sind reine Kulisse —
   und nur dadurch trifft ein Tippen durch das Fensterloch die Blaumeise,
   die dahinter sitzt. */
/* Eine Ebene ist ein Halter fuer Kulisse, keine Klickflaeche. Jede deckt den
   GANZEN Canvas — wer sie klickbar laesst, schluckt damit alles, was in einer
   Ebene darunter liegt. Genau daran war die Sonne nicht antippbar: die
   Lampenebene lag unsichtbar darueber.
   Darum: Ebenen nehmen nichts an, ihre Kinder schon. */
.layer { pointer-events: none; }
.layer > * { pointer-events: auto; }
/* Wand, Rahmen und Kabel sind reine Kulisse und muessen durchlassen —
   nur so trifft ein Tippen durchs Fensterloch die Blaumeise und die Sonne. */
#layerWall, #layerWindow, #layerWall *, #layerWindow *,
.kabel-svg, .lamp-rose, .cone-shape { pointer-events: none; }
.vogel, .gestirn-knopf, .lamp { pointer-events: auto; }

/* Der Vogel. Er braucht keine Maske: er liegt hinter der Wand, und deren
   Loch ist die Scheibe. Ausserhalb des Fensters ist er verdeckt.

   Seit dem Wechsel auf die Bilder des Boards steckt statt eines <svg> ein
   <img> darin — die Regeln gelten fuer beides, damit der Fluegelschlag
   nicht an der Bildart haengt. */
.vogel {
  position: absolute; padding: 0; border: 0; background: none;
  cursor: pointer; touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  will-change: transform, opacity;
}
.vogel > * { display: block; width: 100%; height: 100%; position: relative; z-index: 1; }
/* Seit die Voegel GEZEICHNET sind (Rueckmeldung Board, 18.09.: «Try to
   recreate the birds yourself»), braucht es keinen Farbangleich mehr. Der
   war dazu da, ein Foto in eine flaechige Landschaft zu zwingen — eine
   Zeichnung in derselben Bildsprache hat das Problem nicht. Saettigung
   herunterzudrehen wuerde sie jetzt nur blass machen. */
/* Der Kontaktschatten am Fuss. Er sitzt an --fussx, nicht mittig: die
   Fuesse stehen bei keinem der Voegel in der Bildmitte. */
.vogel::after {
  content: ''; position: absolute; z-index: 0;
  left: var(--fussx, 50%); bottom: -.5px; transform: translateX(-50%);
  width: 52%; height: 3px; border-radius: 50%;
  background: rgba(46,34,18,.46); filter: blur(1.3px);
}
.vogel:focus-visible { outline: 2px solid #ffd479; outline-offset: 2px; border-radius: 6px; }
/* Fluegelschlag: nur waehrend des Fluges. */
.vogel.fliegt > * { animation: schlag .16s linear infinite; }
@keyframes schlag {
  0%, 100% { transform: scaleY(1) }
  50%      { transform: scaleY(.84) }
}
/* Kleines Zucken im Sitzen — ohne das wirkt der Vogel aufgeklebt. */
.vogel.zuckt > * { animation: gucken .32s ease-in-out; }
@keyframes gucken {
  0%, 100% { transform: rotate(0) }
  40%      { transform: rotate(-5deg) translateY(-.6px) }
}
.layer-night {
  pointer-events: none; z-index: 60;
  /* Kein mix-blend-mode: die Seitenwand steht in einem 3D-Kontext, dort
     bleibt die Mischung wirkungslos. Normales Überblenden reicht. */
  background: linear-gradient(180deg, rgba(16,26,52,.44), rgba(10,18,38,.56));
  opacity: var(--night);
  transition: opacity 1.1s ease;
}
.layer-cone { pointer-events: none; z-index: 61; }

/* Das Wetter steht in der Handy-Leiste, nicht mehr im Zimmer (Rueckmeldung
   Board, 18.09.). Im Zimmer lag es als Schild auf dem Boden und deckte den
   Velo-Bereich zu; in der Leiste steht es dort, wo man auf einem Handy den
   Status sucht — und auf derselben Hoehe wie Logo und Kind.

   Die dunkle Pille von vorher waere auf dem anthrazitfarbenen Balken
   unsichtbar. Darum heller Aufdruck statt dunkler Pille. */
.weather-badge {
  pointer-events: none;
  justify-self: center; min-width: 0;
  background: rgba(255,255,255,.12); color: #eaf0f6;
  font-size: 10.5px; font-weight: 600; letter-spacing: .01em;
  padding: 2.5px 8px; border-radius: 999px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Bauteile auf dem Design-Canvas */
.el { position: absolute; }
.el svg { display: block; width: 100%; height: 100%; overflow: visible; }

/* Fenster */
.window-front { z-index: 5; }
.sky { position: absolute; inset: 0; overflow: hidden; }

/* Lampe

   Die Lampe hängt in einer Dreh-Ebene über dem ganzen Canvas und kippt um
   den Deckenpunkt (transform-origin setzt lampe.js). Die Ebene selber darf
   nichts abfangen, sonst läge ein unsichtbares Blatt über dem Fenster.
   Angefasst wird nur, was auch gezeichnet ist — dieselbe Lösung wie beim
   Velo.

   will-change öffnet einen Stapelkontext: ein z-index an der Lampe wirkt
   danach nur noch innerhalb der Ebene. Die Nummer gehört darum an die Ebene,
   sonst läge sie unter Fenster (5) und Wandtafel (10) — also unsichtbar. */
/* Die Lampe besteht aus drei Stuecken mit drei Aufgaben (Rueckmeldung Board,
   18.09.): die Rosette steht fest an der Decke, das Kabel verformt sich, die
   Schale haengt am Kabelende. Die z-Nummer gehoert an die EBENE, nicht an die
   Teile — siehe die will-change-Falle oben. */
#layerLamp { z-index: 12; }

.lamp-rose { position: absolute; pointer-events: none; z-index: 3; }
.lamp-rose svg { display: block; width: 100%; height: auto; overflow: visible; }

/* Das Kabel liegt als eine Linie ueber dem ganzen Canvas. Es wird pro Bild
   neu gezeichnet und darf darum keine Uebergaenge haben — eine Transition
   wuerde die Kurve gegen die Physik verschleppen. */
.kabel-svg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: 1; overflow: visible;
}

.lamp {
  padding: 0; border: 0; background: none;
  position: absolute; z-index: 2;
  pointer-events: none;
  cursor: grab; touch-action: none;
  will-change: transform;
  -webkit-tap-highlight-color: transparent;
}
.lamp svg * { pointer-events: visiblePainted; }
.lamp.zieht { cursor: grabbing; }
.lamp:focus-visible { outline: 2px solid #ffd479; outline-offset: 2px; border-radius: 10px; }
.bulb-glow { transition: opacity .9s ease; }

/* Velolichter. Gehaeuse und Glas sind immer da — nur Schein und Kegel gehen
   an, und zwar an derselben Variablen wie die Zimmerbeleuchtung. Eine zweite
   Zustandsvariable haette frueher oder spaeter nicht mehr zum Zimmer gepasst.
   Vorne weiss, hinten rot (Rueckmeldung Board, 18.09.). */
.velo-kegel, .velo-schein {
  opacity: var(--night);
  transition: opacity .9s ease;
}

/* Lichtkegel: EIN Strahl, glühende Schalenkante, Lichtteppich auf dem
   Boden. Die Geometrie rechnet lampe.js; hier steht nur, wie das Licht
   aussieht.

   Entscheidend fürs Echte: ein Strahl ist nicht flächig gefüllt. Quer
   verläuft er von der Achse nach aussen ins Nichts (Halbschatten), längs
   läuft er mit der Entfernung aus (Maske). Ohne das bekäme man ein
   gleichmässiges Trapez mit Papierkante — der übliche Fake-Kegel. */
.cone-shape {
  position: absolute;
  opacity: 0; transition: opacity 1.1s ease;
  mix-blend-mode: screen;
}
.layer-cone.an .cone-shape { opacity: 1; }
/* Am Tag ist der Raum hell — derselbe Kegel wäre grell und unglaubwürdig. */
.layer-cone.an.bei-tag .cone-shape { opacity: .4; }

/* Der eine Strahl (Rückmeldung Board, 18.09.). Der Querverlauf hat bewusst
   viele Stützstellen und keinen Sprung: er geht von der Achse in einem Zug
   auf null. Genau der Sprung war früher die zweite, sichtbare Kegelkante. */
.cone-strahl {
  background: linear-gradient(90deg,
    rgba(255,228,158,0)    0%,
    rgba(255,231,168,.06) 14%,
    rgba(255,236,182,.15) 28%,
    rgba(255,242,200,.24) 40%,
    rgba(255,246,214,.29) 50%,
    rgba(255,242,200,.24) 60%,
    rgba(255,236,182,.15) 72%,
    rgba(255,231,168,.06) 86%,
    rgba(255,228,158,0)  100%);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,.62) 44%, rgba(0,0,0,.24) 76%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,.62) 44%, rgba(0,0,0,.24) 76%, transparent 100%);
  filter: blur(6px);
}
/* Die Schalenkante glüht: dort tritt das Licht aus. */
.cone-rand {
  border-radius: 50%;
  background: radial-gradient(50% 50% at 50% 50%,
    rgba(255,248,220,.8), rgba(255,233,170,.34) 52%, rgba(255,226,150,0) 100%);
  filter: blur(3px);
}
/* Der Lichtteppich liegt flach auf dem Boden — er kippt nicht mit, er
   wandert und wird breiter. Hell an der Auftreffstelle, nach aussen weg. */
.cone-teppich {
  background: radial-gradient(56% 92% at 50% 10%,
    rgba(255,238,186,.30), rgba(255,226,152,.12) 52%, rgba(255,224,146,0) 80%);
  filter: blur(6px);
}
/* Streulicht: was die Birne unter der Schalenkante ringsum abgibt und was
   von Wand und Boden zurückkommt. Ohne das bleibt alles ausserhalb des
   Kegels — Tür, Ecke, Gestell — bretthart dunkel. Der Mittelpunkt hängt an
   der Birne und wandert beim Schaukeln mit. */
.layer-cone::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  mix-blend-mode: screen;
  background: radial-gradient(74% 56% at var(--wash-x, 50%) var(--wash-y, 24%),
    rgba(255,231,168,.19), rgba(255,222,145,.07) 54%, rgba(255,220,140,0) 84%);
  opacity: 0; transition: opacity 1.1s ease;
}
.layer-cone.an::before { opacity: 1; }
.layer-cone.an.bei-tag::before { opacity: .4; }

/* Wandtafel */
.wandtafel { z-index: 10; }
.wt-frame {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, #a87641, #8a5c31);
  border-radius: 5px;
  /* Die Tafel ist schmaler geworden, damit das Fenster wachsen konnte —
     der Rahmen gibt darum ein paar Pixel an die Schreibfläche zurück.

     UNTEN ist er tiefer als oben (Rueckmeldung Board, 19.09.: «the bottom
     part of the wandtafel does not look correct»). Eine Wandtafel hat dort
     die Kreideschale, und ein Rahmen, der unten duenner ist als seitlich,
     sieht aus, als waere das Bild abgeschnitten. Dieselbe Entscheidung wie
     auf schulpult.ch, wo der Rahmen unten 30 statt 12 Pixel misst. */
  padding: 4px 5px 3px;
  display: flex; flex-direction: column;
  box-shadow: 0 4px 10px rgba(0,0,0,.22), inset 0 1px 0 rgba(255,255,255,.28);
}
.wt-clip {
  position: relative; flex: 1 1 auto; min-height: 0; width: 100%;
  overflow: hidden; border-radius: 2px;
  background: radial-gradient(120% 120% at 30% 20%, #3a584a, #263e33 70%);
  box-shadow: inset 0 0 22px rgba(0,0,0,.45);
  touch-action: none; cursor: grab;
}
.wt-clip.grabbing { cursor: grabbing; }
.wt-canvas {
  position: absolute; left: 0; top: 0;
  width: 1200px; height: 900px;
  transform-origin: 0 0;
  will-change: transform;
}
.wt-canvas::before {
  content: ""; position: absolute; inset: 0;
  background-image:
    repeating-linear-gradient(0deg, rgba(255,255,255,.028) 0 1px, transparent 1px 34px);
  pointer-events: none;
}
.chalk {
  color: var(--chalk);
  font-family: "Bradley Hand", "Segoe Script", "Comic Sans MS", cursive;
  text-shadow: 0 0 1px rgba(255,255,255,.35);
}
.wt-block { position: absolute; }
.wt-h { font-size: 12.5px; font-weight: 700; letter-spacing: .02em; margin-bottom: 2px; }
.wt-h::after {
  content: ""; display: block; height: 1.5px; width: 100%;
  margin-top: 1.5px; border-radius: 2px;
  background: rgba(243,240,230,.5);
}
/* Enger gesetzt, seit die Tafel für das grössere Fenster niedriger ist:
   Überschrift und alle drei Prüfungen müssen ohne Schieben dastehen. */
.wt-line { font-size: 10.5px; line-height: 1.22; opacity: .92; white-space: nowrap; }
.wt-line .d { opacity: .62; }

/* Die Leiste sitzt am Rahmen, nicht auf der Tafelfläche — sie bleibt
   stehen, egal wohin die Tafel geschoben wird.

   Sie ist die KREIDESCHALE: eine flache Rinne im Holz, in der die Kreide
   liegt. Darum die helle Linie oben (das Licht faellt auf die Vorderkante)
   und der dunkle Streifen darunter (der Schatten in der Rinne). Ohne die
   zwei Linien ist es nur der Rahmen, und die Kreide schwebt darauf. */
.wt-bar {
  flex: 0 0 auto; height: 14px;
  display: flex; align-items: center; gap: 8px;
  padding: 0 4px; pointer-events: none;
  border-radius: 0 0 3px 3px;
  background: linear-gradient(180deg, rgba(0,0,0,.20) 0 1.5px, rgba(255,255,255,.14) 1.5px 3px, transparent 3px);
}

/* Ein Stueck weisse Kreide, links in der Schale (Rueckmeldung Board,
   19.09.: «add a white chalk in the bottom left just like in the schulpult
   site»).

   Gezeichnet und nicht gemalt: ein Rechteck mit runden Enden, der obere
   Rand heller als der untere, und darunter ein weicher Schatten. Eine
   Kreide ist nie ganz gerade in der Rinne — die halbe Grad Drehung ist der
   Unterschied zwischen «liegt da» und «ist dorthin gezeichnet». */
.wt-kreide {
  flex: 0 0 auto;
  width: 17px; height: 5px; border-radius: 2.5px;
  background: linear-gradient(180deg, #ffffff 0 45%, #e4e0d4 70%, #cfcabb);
  box-shadow: 0 1px 1.5px rgba(0,0,0,.35);
  transform: rotate(-.8deg);
}
.wt-hint { font-size: 9px; opacity: .55; white-space: nowrap; }
.wt-status {
  margin-left: auto; font-size: 10.5px; font-weight: 700;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.wt-status.done { color: #bdf0c9; }

/* Die Uhr an der Seitenwand. Nur der Sekundenzeiger steht hier: Stunde und
   Minute sind gezeichnet (svg.js), weil sie sich langsam genug bewegen, um
   sie beim Neuzeichnen mitzunehmen. Eine Sekunde ist dafuer zu schnell —
   und eine Uhr, deren roter Zeiger alle zwanzig Sekunden springt, ist
   kaputt. Sechzig Schritte, weil eine Schulzimmeruhr tickt. */
.uhr-sek {
  transform-origin: 30px 30px;
  animation: uhrSekunde 60s steps(60, end) infinite;
}
@keyframes uhrSekunde { to { transform: rotate(360deg); } }

/* Kalender */
.kalender {
  z-index: 11; padding: 0; border: 0; background: none; cursor: pointer;
  font: inherit;
}
.kal-card {
  position: absolute; inset: 0;
  background: #fffdf7; border-radius: 4px;
  box-shadow: 0 3px 7px rgba(0,0,0,.24);
  overflow: hidden; display: flex; flex-direction: column;
  border: 1px solid rgba(0,0,0,.08);
}
.kal-top {
  background: var(--accent); color: #fff;
  font-size: 9px; font-weight: 800; letter-spacing: .12em;
  text-align: center; padding: 2px 0 3px; text-transform: uppercase;
}
.kal-day {
  flex: 1; display: grid; place-items: center;
  font-size: 26px; font-weight: 800; color: #23303c; line-height: 1;
}
.kal-wd { font-size: 9px; font-weight: 700; color: var(--muted); text-align: center; padding-bottom: 3px; letter-spacing: .04em; }
.kal-dot {
  position: absolute; right: 4px; top: 16px;
  width: 6px; height: 6px; border-radius: 50%; background: var(--brand-2);
}

/* Möbel */
.furn { z-index: 8; }

/* Items */
.item {
  z-index: 20;
  touch-action: none;
  cursor: grab;
  transition: transform .18s ease, opacity .18s ease;
}
.item:active { cursor: grabbing; }
.item.packed { display: none; }
.item.returning { transition: left .26s cubic-bezier(.3,1.2,.5,1), top .26s cubic-bezier(.3,1.2,.5,1); }
.item.hidden { display: none; }
.item .hit {
  position: absolute; inset: -4px;
}
.velo { z-index: 30; pointer-events: none; }
.velo svg * { pointer-events: visibleStroke; }
.velo.checked { opacity: .42; filter: grayscale(.6); }

.name-chip {
  position: absolute; z-index: 90;
  transform: translate(-50%, -100%);
  background: rgba(24,36,48,.92); color: #fff;
  font-size: 11px; font-weight: 600;
  padding: 3px 8px; border-radius: 999px; white-space: nowrap;
  pointer-events: none;
}

/* Seitenwand. left und width setzt scene.js aus L.wand — Drehwinkel und
   Fluchtweite stehen dort als DREH und PERSP und müssen zusammenpassen. */
/* height setzt scene.js aus L.seitwandBis: die Wand endet am Boden, nicht am
   Canvasrand. Ihre Unterkante ist damit der Wandfuss und läuft im Bild von
   selbst in den Fluchtpunkt. */
.sidewall {
  position: absolute; left: 300px; top: 0; width: 120px; height: 324px;
  transform-origin: left 188px;
  transform: perspective(420px) rotateY(-40deg);
  z-index: 35;
}
.rucksack { cursor: pointer; z-index: 3; }
.rucksack.drop-hot .rs-body { filter: brightness(1.14) drop-shadow(0 0 6px rgba(255,220,120,.9)); }

/* Drag */
.drag-layer { position: fixed; inset: 0; pointer-events: none; z-index: 1000; }
.drag-ghost { position: absolute; will-change: transform; }
.drag-ghost svg { display: block; width: 100%; height: 100%; }

/* ---------- Handy ---------- */

.handy {
  /* Liegt über dem Zimmer (Rückmeldung Board, 18.09.). Wird es höher, deckt
     es mehr Zimmer zu — das Zimmer selber bleibt gleich gross. */
  position: absolute; left: 0; right: 0; bottom: 0;
  height: var(--handy-h);
  background: #11161c;
  border-radius: 20px 20px 0 0;
  box-shadow: 0 -10px 26px rgba(0,0,0,.35);
  z-index: 50;
  display: flex; flex-direction: column;
  transition: height .26s cubic-bezier(.22,.9,.3,1);
}
.handy.dragging { transition: none; }
.handy-grip {
  flex: 0 0 auto; height: 22px;
  display: grid; place-items: center;
  touch-action: none; cursor: grab;
}
.grip-bar { width: 42px; height: 4px; border-radius: 2px; background: #55606c; }
.handy-grip:active .grip-bar { background: #8a97a5; }
.handy-frame {
  flex: 1 1 auto; min-height: 0;
  margin: 0 8px 8px;
  padding: 3px;
  background: #000;
  border-radius: 16px 16px 6px 6px;
  box-shadow: inset 0 0 0 1px #2a3138;
}
.handy-display {
  position: relative; width: 100%; height: 100%;
  background: #f4f6f8; border-radius: 13px 13px 4px 4px;
  overflow: hidden;
  display: flex; flex-direction: column;
}
/* Statusleiste des Handys. Dunkles Anthrazit statt Markenblau: sonst
   stehen zwei gleich blaue Balken aufeinander und die Leiste sieht aus
   wie ein zweiter Chat-Kopf. */
.handy-taskbar {
  flex: 0 0 auto; height: 30px;
  padding: 0 9px 0 10px;
  /* 1fr auto 1fr statt space-between: nur so sitzt das Wetter auf der
     Mittelachse des Handys und nicht zwischen zwei ungleich breiten
     Nachbarn. */
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: 6px;
  background: #151c24; color: #fff;
  box-shadow: inset 0 -1px 0 rgba(255,255,255,.10);
  z-index: 2;
}
.handy-taskbar .logo-bubble { color: #151c24; }
.handy-taskbar .logo { min-width: 0; }
.handy-taskbar .kind-chip { justify-self: end; }
.handy-screen {
  position: relative; flex: 1 1 auto; min-height: 0; width: 100%;
  overflow: hidden;
  display: flex; flex-direction: column;
  container-type: size;
}
/* Ganz unten zugezogen bleibt nur eine Handbreit Chat — dann würde der
   Plus-Knopf auf dem Chat-Kopf liegen. */
@container (max-height: 150px) {
  .fab { display: none; }
}

.chat-head {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; background: var(--brand); color: #fff;
}
.chat-head .back {
  background: none; border: 0; color: #fff; font-size: 20px; line-height: 1;
  padding: 0 2px; cursor: pointer;
}
.chat-head .t { font-weight: 700; font-size: 14px; }
.chat-head .s { font-size: 11px; opacity: .75; }
.chat-head .grow { flex: 1; }
.head-btn {
  background: rgba(255,255,255,.15); border: 0; color: #fff;
  border-radius: 999px; font: inherit; font-size: 11px; font-weight: 600;
  padding: 4px 9px; cursor: pointer;
}
.icon-btn {
  width: 30px; height: 30px; border-radius: 50%;
  background: rgba(255,255,255,.15); border: 0; color: #fff;
  font-size: 18px; line-height: 1; cursor: pointer;
  display: grid; place-items: center;
}

.chat-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; }

.chat-row {
  display: flex; gap: 10px; align-items: center;
  padding: 9px 12px; cursor: pointer; background: #fff;
  border-bottom: 1px solid var(--line);
}
.chat-row:active { background: #eef2f6; }
.avatar {
  width: 38px; height: 38px; border-radius: 50%; flex: 0 0 auto;
  display: grid; place-items: center; font-weight: 700; color: #fff; font-size: 14px;
}
.chat-meta { flex: 1; min-width: 0; display: block; overflow: hidden; }
.chat-name { font-size: 14px; font-weight: 700; display: flex; align-items: center; gap: 5px; }
/* display:block ist nötig — an einem inline-Span greift text-overflow nicht. */
.chat-last { display: block; font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-side { text-align: right; flex: 0 0 auto; }
.chat-time { font-size: 10.5px; color: var(--muted); }
.badge {
  display: inline-block; min-width: 18px; margin-top: 4px;
  background: var(--accent); color: #fff; font-size: 10.5px; font-weight: 700;
  border-radius: 999px; padding: 1px 5px;
}
.tag {
  font-size: 9.5px; font-weight: 700; letter-spacing: .04em;
  background: #e8eef5; color: var(--brand); border-radius: 3px; padding: 1px 4px;
  text-transform: uppercase;
}

.msgs { padding: 10px 10px 4px; display: flex; flex-direction: column; gap: 6px; background: #eceff3; min-height: 100%; }
.daysep { align-self: center; font-size: 10.5px; color: #66727e; background: #dfe4ea; padding: 2px 9px; border-radius: 999px; margin: 4px 0; }
.msg { max-width: 82%; padding: 6px 9px 5px; border-radius: 11px; font-size: 13px; line-height: 1.38; position: relative; }
.msg .who { font-size: 11px; font-weight: 700; color: var(--brand-2); margin-bottom: 1px; }
.msg .tm { font-size: 9.5px; color: #8a949e; text-align: right; margin-top: 1px; }
.msg.them { align-self: flex-start; background: #fff; border-bottom-left-radius: 3px; }
.msg.me { align-self: flex-end; background: #d6ead6; border-bottom-right-radius: 3px; }
.msg.sys { align-self: center; background: #e3e8ee; color: #55606c; font-size: 11.5px; text-align: center; }
.msg-card {
  border-left: 3px solid var(--accent); background: #fff7f2;
  border-radius: 8px; padding: 7px 9px; font-size: 12.5px;
}
.msg-card b { display: block; font-size: 13px; margin-bottom: 2px; }
.msg-card ul { margin: 5px 0 0; padding-left: 16px; }
.msg-card li { line-height: 1.4; }

.composer {
  flex: 0 0 auto; display: flex; gap: 7px; align-items: center;
  padding: 7px 8px calc(7px + var(--safe-bottom));
  background: #fff; border-top: 1px solid var(--line);
}
.composer input {
  flex: 1; border: 1px solid var(--line); border-radius: 999px;
  padding: 8px 12px; font: inherit; font-size: 13px; background: #f4f6f8;
}
.composer input:focus { outline: 2px solid var(--brand-2); outline-offset: -1px; }
.send {
  width: 34px; height: 34px; border-radius: 50%; border: 0;
  background: var(--brand); color: #fff; font-size: 15px; cursor: pointer;
  display: grid; place-items: center;
}
.readonly-note {
  flex: 0 0 auto; padding: 9px 12px calc(9px + var(--safe-bottom));
  background: #fff; border-top: 1px solid var(--line);
  font-size: 11.5px; color: var(--muted); text-align: center;
}

.fab {
  position: absolute; right: 12px; bottom: 62px; z-index: 5;
  width: 46px; height: 46px; border-radius: 50%;
  background: var(--accent); color: #fff; border: 0;
  font-size: 26px; line-height: 1; cursor: pointer;
  box-shadow: 0 5px 14px rgba(226,88,77,.45);
}

.members li {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; background: #fff; border-bottom: 1px solid var(--line);
}
.members { list-style: none; margin: 0; padding: 0; }
.members .role { font-size: 11px; color: var(--muted); }
.members .mini-btn {
  margin-left: auto; background: #e8eef5; border: 0; color: var(--brand);
  font: inherit; font-size: 11.5px; font-weight: 700; padding: 5px 10px;
  border-radius: 999px; cursor: pointer;
}
.privacy-note {
  margin: 10px 12px; padding: 9px 11px; border-radius: 9px;
  background: #eef4ea; color: #3d5a3f; font-size: 11.5px; line-height: 1.45;
}
/* Der Weg zum Datenschutz-Blatt, seit der Kopfbalken weg ist. Als Textlink
   im Hinweis statt als Knopf in einer Leiste: er steht damit an der Stelle,
   an der man ohnehin ueber Datenschutz liest. */
.note-link {
  display: inline; margin: 0; padding: 0; border: 0; background: none;
  font: inherit; font-weight: 700; color: #2f6f43;
  text-decoration: underline; text-underline-offset: 2px; cursor: pointer;
}
.note-link:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* ---------- Overlays ---------- */

.overlays { position: fixed; inset: 0; z-index: 900; pointer-events: none; }

/* Der zweite Sheet-Host liegt IM Handy (Rueckmeldung Board, 18.09.:
   «Put the Packliste Popup inside the phone»). `absolute` statt `fixed`:
   dadurch begrenzt der Handy-Rahmen das Popup von selbst, und es waechst
   und schrumpft mit, wenn man am Ziehgriff zieht. */
.overlays-handy { position: absolute; inset: 0; z-index: 40; pointer-events: none; }
.overlays-handy .sheet-wrap { position: absolute; }
/* Im Handy braucht das Sheet keine eigene Maximalbreite — es hat schon eine:
   das Handy. */
.sheet-wrap.im-handy .sheet { max-width: none; border-radius: 16px 16px 0 0; }
/* Und es darf nie hoeher werden als der Bildschirm, in dem es steckt. */
.sheet-wrap.im-handy .sheet-body { max-height: min(46vh, 340px); }

/* Fuss der Packliste: Hauptknopf links, Drucken rechts. */
.pl-fuss { display: flex; align-items: center; gap: 10px; }
/* Die Knoepfe duerfen NICHT auf volle Breite wachsen: sonst schiebt
   «Drucken» den Fortschrittstext in einen zweizeiligen Umbruch. */
.pl-fuss .btn { flex: 0 0 auto; width: auto; padding: 10px 16px; }
.pl-fuss .pl-progress { flex: 1 1 auto; margin: 0; font-size: 13px; line-height: 1.35; }
.pl-fuss [data-tun="drucken"] { margin-left: auto; }
.sheet-wrap {
  position: absolute; inset: 0; pointer-events: auto;
  background: rgba(10,18,28,.44);
  display: flex; align-items: flex-end; justify-content: center;
  animation: fade .18s ease;
}
.sheet-wrap.center { align-items: center; padding: 16px; }
@keyframes fade { from { opacity: 0 } to { opacity: 1 } }
.sheet {
  width: 100%; max-width: 460px;
  background: #fff; border-radius: 18px 18px 0 0;
  max-height: 86%; display: flex; flex-direction: column;
  padding-bottom: var(--safe-bottom);
  animation: rise .22s cubic-bezier(.22,.9,.3,1);
  overflow: hidden;
}
.sheet-wrap.center .sheet { border-radius: 18px; max-width: 400px; }
@keyframes rise { from { transform: translateY(24px) } to { transform: none } }
.sheet-head {
  flex: 0 0 auto; padding: 14px 16px 10px;
  display: flex; align-items: flex-start; gap: 10px; border-bottom: 1px solid var(--line);
}
.sheet-head h2 { margin: 0; font-size: 16px; }
.sheet-head p { margin: 2px 0 0; font-size: 12px; color: var(--muted); }
.sheet-close {
  margin-left: auto; background: #eef1f4; border: 0; width: 30px; height: 30px;
  border-radius: 50%; font-size: 17px; line-height: 1; cursor: pointer; color: #55606c;
}
.sheet-body { flex: 1 1 auto; overflow-y: auto; }
.sheet-foot { flex: 0 0 auto; padding: 10px 16px 14px; border-top: 1px solid var(--line); }

.btn {
  display: block; width: 100%; border: 0; border-radius: 12px;
  padding: 12px 14px; font: inherit; font-size: 14px; font-weight: 700;
  background: var(--brand); color: #fff; cursor: pointer;
}
.btn.ghost { background: #eef1f4; color: var(--ink); }
/* Priming-Text vor einer Systemabfrage (Lagesensor, Mitteilungen, Standort). */
.prime-text { margin: 0 0 12px; font-size: 14.5px; line-height: 1.5; color: var(--ink-2, #55606b); }
.btn + .btn { margin-top: 8px; }

/* Packliste */
.pl { list-style: none; margin: 0; padding: 4px 0 8px; }
.pl li { display: flex; align-items: center; gap: 11px; padding: 9px 16px; }
/* Bundle-Inhalt: eine ruhige Zeile unter dem Bundle, ohne eigenes Kästchen. */
.pl li.sub {
  padding: 0 16px 8px 83px; margin-top: -6px;
  font-size: 12px; color: #7b8792; line-height: 1.4;
}
.pl .box {
  width: 22px; height: 22px; flex: 0 0 auto; border-radius: 6px;
  border: 2px solid #c3ccd5; background: #fff;
  display: grid; place-items: center; cursor: pointer;
}
.pl li.on .box { background: var(--ok); border-color: var(--ok); }
.pl .box svg { width: 13px; height: 13px; opacity: 0; }
.pl li.on .box svg { opacity: 1; }
/* Hochformat, nicht quadratisch: eine Jacke ist gut doppelt so hoch wie breit.
   In einem 26er Quadrat bliebe von ihr ein 11 px schmaler Streifen übrig. */
.pl .thumb { width: 30px; height: 38px; flex: 0 0 auto; }
.pl .thumb svg { width: 100%; height: 100%; }
.pl .lbl { font-size: 14px; }
.pl li.on .lbl { text-decoration: line-through; color: var(--muted); }
.pl li.sub .lbl { font-size: 13px; color: #4a5560; }
.pl .zone { margin-left: auto; font-size: 10.5px; color: #9aa5b0; }
.pl-progress { font-size: 12px; color: var(--muted); }
.pl-empty { padding: 24px 16px; text-align: center; color: var(--muted); font-size: 13.5px; }

/* Kalender-Popup */
.cal-head { display: flex; align-items: center; gap: 8px; padding: 10px 16px 6px; }
.cal-head .m { flex: 1; text-align: center; font-weight: 700; font-size: 14px; }
.cal-nav { background: #eef1f4; border: 0; width: 30px; height: 30px; border-radius: 50%; font-size: 15px; cursor: pointer; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; padding: 0 12px 10px; }
.cal-wd { text-align: center; font-size: 10.5px; color: var(--muted); font-weight: 700; padding: 4px 0; }
.cal-cell {
  position: relative; aspect-ratio: 1; border: 0; background: none; font: inherit;
  font-size: 13px; border-radius: 10px; cursor: pointer; color: var(--ink);
}
.cal-cell.out { color: #c3ccd5; }
.cal-cell.today { font-weight: 800; box-shadow: inset 0 0 0 1.5px var(--brand-2); }
.cal-cell.sel { background: var(--brand); color: #fff; font-weight: 800; }
.cal-cell .mk {
  position: absolute; left: 50%; bottom: 5px; transform: translateX(-50%);
  display: flex; gap: 2px;
}
.cal-cell .mk i { width: 4px; height: 4px; border-radius: 50%; background: var(--brand-2); }
.cal-cell .mk i.aus { background: var(--accent); }
.cal-cell.sel .mk i { background: #fff; }
.cal-legend { display: flex; gap: 14px; justify-content: center; padding: 2px 16px 10px; font-size: 11.5px; color: var(--muted); }
.cal-legend span { display: flex; align-items: center; gap: 5px; }
.cal-legend i { width: 6px; height: 6px; border-radius: 50%; background: var(--brand-2); }
.cal-legend i.aus { background: var(--accent); }

/* Kinder */
.kids { list-style: none; margin: 0; padding: 6px 0 10px; }
.kids li { display: flex; align-items: center; gap: 11px; padding: 11px 16px; cursor: pointer; }
.kids li.on { background: #eef4fb; }
.kids .k-name { font-size: 14.5px; font-weight: 700; }
.kids .k-sub { font-size: 12px; color: var(--muted); }
.kids .k-state { margin-left: auto; font-size: 11.5px; font-weight: 700; color: var(--accent); }
.kids .k-state.done { color: var(--ok); }

/* Onboarding */
.onb { text-align: center; padding: 26px 22px 8px; }
.onb .art { font-size: 44px; line-height: 1; margin-bottom: 12px; }
.onb h2 { margin: 0 0 8px; font-size: 19px; }
.onb p { margin: 0 0 6px; font-size: 13.5px; line-height: 1.5; color: #4a5560; }
.onb .dots { display: flex; gap: 6px; justify-content: center; padding: 14px 0 2px; }
.onb .dots i { width: 6px; height: 6px; border-radius: 50%; background: #d3dae1; }
.onb .dots i.on { background: var(--brand); }
.link-btn {
  display: block; width: 100%; background: none; border: 0; color: var(--muted);
  font: inherit; font-size: 12.5px; padding: 10px; cursor: pointer; text-decoration: underline;
}

/* Toast */
.toast {
  position: fixed; left: 50%; bottom: 84px; transform: translate(-50%, 12px);
  background: rgba(20,32,45,.94); color: #fff; font-size: 12.5px; font-weight: 600;
  padding: 8px 14px; border-radius: 999px; z-index: 1100;
  opacity: 0; pointer-events: none; transition: opacity .2s, transform .2s;
}
.toast.on { opacity: 1; transform: translate(-50%, 0); }

/* Wetter-Animationen */
.wx { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.drop { position: absolute; width: 1.6px; border-radius: 1px; background: rgba(210,232,245,.82); animation: fall linear infinite; }
@keyframes fall { from { transform: translateY(-14px) } to { transform: translateY(120px) } }
.flake { position: absolute; border-radius: 50%; background: rgba(255,255,255,.9); animation: snow linear infinite; }
@keyframes snow {
  from { transform: translate(0, -12px) }
  to   { transform: translate(7px, 120px) }
}
.hailstone { position: absolute; border-radius: 50%; background: #e9f4fb; box-shadow: 0 0 2px rgba(255,255,255,.7); animation: fall linear infinite; }
.fogbank {
  position: absolute; left: -30%; width: 160%; height: 22px; border-radius: 50%;
  background: rgba(238,242,245,.55); filter: blur(5px); animation: drift linear infinite;
}
@keyframes drift { from { transform: translateX(-16%) } to { transform: translateX(16%) } }
/* Eine Wolke faehrt einmal quer durch den Himmel und faengt links wieder an.
   `drift` (das Wackeln um die eigene Breite) gehoert weiterhin den
   Nebelbaenken: die sollen an Ort bleiben und nur atmen. */
.cloud { position: absolute; left: 0; animation: wolkenzug linear infinite; }
/* Von ganz links ausserhalb bis ganz rechts hinaus. Der Himmel ist 400 breit
   (scene.js, L.himmel) und die breiteste Wolke 42 — bei -70 steht sie also
   sicher draussen, bei 400 ebenso. */
@keyframes wolkenzug {
  from { transform: translateX(-70px) }
  to   { transform: translateX(400px) }
}
.flash {
  position: absolute; inset: 0; background: rgba(255,250,225,.92);
  opacity: 0; animation: flash 6.5s linear infinite;
}
@keyframes flash {
  0%, 92%, 100% { opacity: 0 }
  93% { opacity: .85 }
  94% { opacity: .1 }
  95% { opacity: .6 }
  96% { opacity: 0 }
}
.star { position: absolute; border-radius: 50%; background: #fff; animation: twinkle ease-in-out infinite alternate; }
@keyframes twinkle { from { opacity: .25 } to { opacity: 1 } }

/* Sternschnuppen (Rueckmeldung Board, 18.09.). Kopf und Schweif teilen sich
   `currentColor`: dadurch genuegt es, im Keyframe die FARBE zu animieren,
   damit die grosse Schnuppe unterwegs blau wird — Kopf, Leuchten und
   Schweif ziehen von selbst mit. */
.schnuppe {
  position: absolute; width: 2px; height: 2px; border-radius: 50%;
  color: #fff; background: currentColor; box-shadow: 0 0 5px 1px currentColor;
  opacity: 0; pointer-events: none; z-index: 1;
  animation: schnuppe linear infinite;
}
.schnuppe::after {
  content: ''; position: absolute; right: 1px; top: 50%;
  width: 26px; height: 1.4px; border-radius: 1px;
  transform: translateY(-50%) rotate(var(--w, 20deg));
  transform-origin: right center;
  background: linear-gradient(to left, currentColor, transparent);
}
.schnuppe.gross { width: 3px; height: 3px; animation-name: schnuppeGross; }
.schnuppe.gross::after { width: 44px; height: 2px; }

/* Sichtbar ist sie nur zwischen 98.4 und 99.6 Prozent des Umlaufs. Bei 100
   Sekunden Umlauf sind das gut 1.2 Sekunden — schnell, wie eine echte.

   Sie ist dabei von der ERSTEN Hundertstelsekunde an hell (Rueckmeldung
   Board, 19.09.). Vorher blendete sie zwischen 98.4 und 98.7 Prozent auf,
   also auf dem ersten Viertel ihres Weges — und weil sie oben ausserhalb des
   Fensters anfaengt, ist genau das das Stueck, auf dem sie ins Glas faellt.
   Sichtbar wurde sie damit erst mitten im Himmel.

   Verloeschen darf sie weiterhin weich: eine Schnuppe brennt aus, sie wird
   nicht ausgeschaltet. */
@keyframes schnuppe {
  0%, 98.4%  { opacity: 0; transform: translate(0, 0) }
  98.45%     { opacity: 1; transform: translate(0, 0) }
  99.3%      { opacity: 1 }
  99.6%      { opacity: 0; transform: translate(var(--dx), var(--dy)) }
  100%       { opacity: 0; transform: translate(var(--dx), var(--dy)) }
}
@keyframes schnuppeGross {
  0%, 98.4%  { opacity: 0; transform: translate(0, 0); color: #f2f7ff }
  98.45%     { opacity: 1; transform: translate(0, 0); color: #eaf2ff }
  99.3%      { opacity: 1; color: #7ab8ff }
  99.6%      { opacity: 0; transform: translate(var(--dx), var(--dy)); color: #4b96f5 }
  100%       { opacity: 0; transform: translate(var(--dx), var(--dy)); color: #4b96f5 }
}
/* Der Weg eines Blattes: nach RECHTS UNTEN, weg vom Baum am linken Rand
   (Rueckmeldung Board, 18.09.). Vorher fiel es fast senkrecht und startete
   irgendwo im Himmel — es kam aus dem Nichts.

   Ein- und Ausblenden gehoeren dazu: ein Blatt, das schlagartig erscheint,
   faellt als Erscheinen auf, nicht als Blatt. */
.leaf { position: absolute; animation: leaffall linear infinite; }
@keyframes leaffall {
  0%   { transform: translate(0,0) rotate(0deg); opacity: 0 }
  10%  { opacity: .95 }
  35%  { transform: translate(22px, 30px) rotate(110deg) }
  65%  { transform: translate(38px, 62px) rotate(210deg) }
  88%  { opacity: .85 }
  100% { transform: translate(64px, 98px) rotate(320deg); opacity: 0 }
}

.paused * { animation-play-state: paused !important; }

@media (prefers-reduced-motion: reduce) {
  .wx *, .leaf, .handy, .item, .sheet, .sheet-wrap { animation: none !important; transition: none !important; }
  .vogel > * { animation: none !important; }
  /* Sterne stehen still, Schnuppen entfallen: ein eingefrorener Streifen
     mitten im Himmel waere keine ruhige Fassung, sondern ein Kratzer. */
  .star { animation: none !important; opacity: .85 }
  .schnuppe { display: none !important; }
  /* Brille und Lachen bleiben — nur ohne Sprung. Ein Gag, den man nicht
     sieht, waere schlechter als einer ohne Schwung. */
  #layerSky.cool .sc-brille, #layerSky.cool .sc-mund { animation: none !important; }
  /* Blaetter fallen nicht mehr aus, statt stillzustehen: neun eingefrorene
     Blaetter sind neun Punkte auf einer Linie, keine ruhige Fassung
     (Rueckmeldung Board, 18.09.). Den Herbst traegt der Wald. */
  .leaf { display: none !important; }
  .cone-shape, .layer-night, .bulb-glow { transition: none !important; }
}

/* ---------- Raumhülle ---------- */

/* Die Wand ist ein SVG mit einem Loch, wo das Glas sitzt (scene.js,
   wandMitLoch). Durch das Loch sieht man die Landschaft, die hinter der
   ganzen Wand liegt — Rückmeldung Board, 18.09. */
.wall-back {
  position: absolute; left: 0; top: 0; width: 300px; height: 324px;
}
.wand-svg { display: block; width: 100%; height: 100%; }
.floor-front {
  position: absolute; left: 0; top: 324px; width: 400px; height: 52px;
  background: linear-gradient(180deg, var(--floor) 0%, var(--floor-dark) 100%);
  box-shadow: inset 0 2px 0 rgba(0,0,0,.14);
}
/* Der Boden ist ein SVG mit Fluchtpunkt (svg.js, bodenArt). Die alte
   Streifentextur ist ersatzlos weg — 92-Grad-Striche sind eine Wandtextur,
   die zufaellig unten liegt (Rueckmeldung Board, 18.09.). */
.floor-front { overflow: visible; background: none; box-shadow: none; }
.boden-svg {
  position: absolute; left: 0; top: -324px; width: 400px; height: 384px;
  pointer-events: none;
}

/* Fenster: nur noch Rahmen, kein Glasbild mehr. Der Rahmen ist als border
   gezeichnet und nicht als Fläche mit Innenabstand — sonst läge eine
   deckende Scheibe vor dem Loch in der Wand. Die Rahmenbreiten stehen
   doppelt (hier und in scene.js L.rahmen), weil die Wand ihr Loch daraus
   rechnet; sie müssen zusammen geändert werden. */
.win-frame {
  position: absolute; inset: 0;
  background: none; border: 5px solid #f4efe4; border-radius: 3px;
  box-shadow: 0 2px 6px rgba(0,0,0,.18), inset 0 0 0 1px rgba(0,0,0,.12);
}
.win-frame.gross { border-width: 6px; border-radius: 4px; }
.win-rand {
  position: absolute; inset: 0; pointer-events: none;
  border: 2px solid rgba(255,255,255,.96);
}
.win-frame.gross .win-rand { border-width: 3px; }
/* Ein Hauch Spiegelung auf dem Glas — darf die Sicht nicht zudecken. */
.win-glanz {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(112deg, rgba(255,255,255,.16) 0 18%, rgba(255,255,255,0) 34%,
                                       rgba(255,255,255,.10) 46%, rgba(255,255,255,0) 60%);
}

/* Die Aussenwelt. Ein Band über die ganze Canvasbreite, hinter der Wand —
   Grösse und Platz setzt scene.js aus L.himmel. */
.sky { position: absolute; left: 0; top: 0; overflow: hidden; }
.sky-bg {
  position: absolute; inset: 0; transition: background 1.2s ease;
  /* Ein Himmel, wie er über den Bergen steht: oben satt, gegen den Horizont
     hin dünner und wärmer. Der flache Verlauf von vorher sah nach Papier aus. */
  background: linear-gradient(180deg, #5f9fd4 0%, #8dc3e6 34%, #b9dcf0 62%, #ddeef7 84%, #e9f1ee 100%);
}
.sky.nacht .sky-bg { background: linear-gradient(180deg, #0d1730 0%, #1b2a4d 44%, #2c3f63 78%, #3a4a63 100%); }
.sky.wx-bewoelkt .sky-bg, .sky.wx-regen .sky-bg, .sky.wx-gewitter .sky-bg { filter: saturate(.72) brightness(.94); }
.sky.wx-nebel .sky-bg { filter: saturate(.35) brightness(1.02); }

.sky-gestirn { position: absolute; pointer-events: none; padding: 0; border: 0; background: none; }
/* Muss NACH .sky-gestirn stehen: gleiche Spezifitaet, und dort steht
   pointer-events: none fuer den Mond. Die spaetere Regel gewinnt. */
.gestirn-knopf { pointer-events: auto; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.gestirn-knopf:focus-visible { outline: 2px solid #ffd479; outline-offset: 3px; border-radius: 50%; }

/* Die Sonne setzt eine Brille auf (Rueckmeldung Board, 18.09.).
   Alles steckt in den Keyframes: auftauchen, Brille faellt herunter und
   federt, Lachen geht auf, halten, verschwinden. Die Klasse setzt scene.js,
   das Ende der Animation nimmt sie wieder weg — eine Dauer, eine Stelle. */
.sonne-cool { opacity: 0; }
.sc-brille, .sc-mund { transform-box: fill-box; }
.sc-brille { transform-origin: center bottom; }
.sc-mund   { transform-origin: center top; }

/* Vorschau fuer die Abnahme (?sonne=1): Brille und Lachen stehen still da.
   Ein Standbild kann eine Animation nicht zeigen, und klicken kann ein
   kopfloser Browser nicht. */
#layerSky.cool-fest .sonne-cool { opacity: 1; }

#layerSky.cool .sonne-cool { animation: sonneCool 2.6s ease-out both; }
#layerSky.cool .sc-brille  { animation: brilleFaellt 2.6s cubic-bezier(.3,1.4,.5,1) both; }
#layerSky.cool .sc-mund    { animation: mundLacht 2.6s ease-out both; }

@keyframes sonneCool {
  0%   { opacity: 0 }
  10%  { opacity: 1 }
  84%  { opacity: 1 }
  100% { opacity: 0 }
}
@keyframes brilleFaellt {
  0%   { transform: translateY(-15px) rotate(-14deg) }
  22%  { transform: translateY(1.4px) rotate(3deg) }
  34%  { transform: translateY(0) rotate(0) }
  100% { transform: translateY(0) rotate(0) }
}
@keyframes mundLacht {
  0%, 14% { transform: scale(.25, .25) }
  32%     { transform: scale(1.12, 1.25) }
  44%,100%{ transform: scale(1, 1) }
}
.sky-gestirn svg { width: 100%; height: 100%; overflow: visible; }

/* Das Alpenpanorama. Nachts wird der ganze Hang gedämpft statt jede Farbe
   doppelt gepflegt. Platz und Höhe im Band setzt scene.js aus L.land. */
.sky-land { position: absolute; left: 0; right: 0; pointer-events: none; }
.sky-land svg { display: block; width: 100%; height: 100%; }
.sky-land .land { transition: filter 1.2s ease; }
.sky.nacht .sky-land .land { filter: brightness(.40) saturate(.5) contrast(1.05); }

.cloud svg { width: 100%; height: auto; display: block; }

/* Seitenwand (Spalte 4). Sie steht schräg im Raum — damit die Ecke wirklich
   als Ecke liest, braucht es drei Dinge: die Drehung, einen dunkleren Ton
   als die Frontwand, und einen Boden, der um die Ecke mitgeht. */
/* Auch die Seitenwand ist ein SVG mit Loch — nur so schaut das
   Seitenfenster in dieselbe Welt wie das Frontfenster. Verlauf und
   Eckschatten stecken im SVG, weil sie am Loch aufhören müssen. */
.side-face { position: absolute; inset: 0; }
/* Der Boden um die Ecke IST derselbe Boden. Es gab hier einmal eine eigene
   Flaeche (`.side-floor`) mit eigenen Fugen; sie ist ersatzlos weg
   (Rueckmeldung Board, 19.09.: «Aktuell endet der Boden dort gerade und
   stoesst hart an die Wand»). Die Seitenwand hoert jetzt am Boden auf, und
   darunter laufen die Platten von bodenArt() weiter — gleiche Farbe,
   gleiche Fugen, gleicher Fluchtpunkt. Zwei Boeden nebeneinander koennen
   nicht zusammenpassen; einer kann gar nicht auseinanderlaufen. */
/* Der Eckschatten endet mit der Wand. Frueher lief er bis zum Canvasrand
   und legte einen senkrechten Streifen ueber den Boden — genau die Kante,
   die den Raum flach aussehen liess. */
.corner-shadow {
  position: absolute; left: 286px; top: 0; width: 14px; height: 324px;
  background: linear-gradient(90deg, rgba(0,0,0,0), rgba(0,0,0,.16));
  pointer-events: none; z-index: 34;
}
.side-window { z-index: 2; }
.door { z-index: 2; }

/* Rucksack an der Tür */
.rucksack { padding: 0; border: 0; background: none; }
.rucksack svg { transition: transform .32s cubic-bezier(.3,1.3,.5,1); }
.rucksack.voll svg { animation: rs-wink 2.4s ease-in-out infinite; }
@keyframes rs-wink {
  0%, 84%, 100% { transform: none }
  88% { transform: rotate(-3deg) translateY(-2px) }
  94% { transform: rotate(3deg) translateY(-1px) }
}
.rucksack.schliesst svg { transform: scale(.9) translateY(3px); }
.rs-hint {
  position: absolute; z-index: 65;
  transform: translate(-50%, -100%);
  font-family: "Bradley Hand", "Segoe Script", "Comic Sans MS", cursive;
  font-size: 12px; font-weight: 700; color: #fff7d8;
  background: #26331f; padding: 2px 7px; border-radius: 8px;
  box-shadow: 0 2px 5px rgba(0,0,0,.28);
  white-space: nowrap; pointer-events: none;
  animation: hint-auf .3s ease;
}
@keyframes hint-auf { from { opacity: 0; transform: translate(-50%, -80%) } to { opacity: 1 } }

/* Items: hintere Ablagereihe und Wetterkleider */
.item.hinten { filter: brightness(.94); z-index: 18; }
.item.wetter::after {
  content: ""; position: absolute; right: -3px; top: -3px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--brand-2); box-shadow: 0 0 0 1.5px rgba(255,255,255,.85);
}
.drag-ghost.rein { transition: transform .28s ease-in, opacity .28s ease-in; }
.drag-ghost.zurueck { transition: transform .26s cubic-bezier(.3,1.2,.5,1); }

/* Tafelstatus: drei Zustände statt zwei */
.wt-status.warten { color: #ffe9a8; }

/* Packliste: Gruppen und Gründe */
.pl-gruppe {
  padding: 12px 16px 2px; font-size: 11px; font-weight: 800;
  letter-spacing: .08em; text-transform: uppercase; color: #8f9aa5;
}
.pl-info {
  margin: 6px 16px 2px; padding: 8px 11px; border-radius: 9px;
  background: #f1f5f9; color: #46525e; font-size: 12.5px; line-height: 1.45;
}
.pl-info.wetter { background: #eaf2fa; color: #2f5273; }
.pl-info b { display: block; font-size: 13px; color: var(--ink); }
.pl li.grund {
  padding: 0 16px 8px 59px; margin-top: -6px;
  font-size: 11.5px; color: #8f9aa5;
}

@media (prefers-reduced-motion: reduce) {
  .rucksack.voll svg, .rs-hint { animation: none !important; }
}

/* Tablet / Desktop: dieselbe Komposition, nur zentriert */
@media (min-width: 560px) {
  body { background: #223041; }
  .app { max-width: 430px; border-radius: 0; }
}


/* ---------- Drucken ----------

   Rueckmeldung Board (18.09.): «add a button to print it out».

   Gedruckt wird ein EIGENES Blatt, nicht der Bildschirm. Ein abfotografiertes
   Handy-Popup auf A4 waere ein Rahmen mit Schatten und einer winzigen Liste
   darin.

   Das Aussehen des Blattes steht hier OHNE Media-Query — es kostet nichts,
   weil `.druckblatt` normalerweise auf `display: none` steht. Umgeschaltet
   wird nur die Sichtbarkeit, und zwar an zwei Stellen: im Druck und ueber
   `?druck=1`. Sonst liesse sich das Blatt nie anschauen, ohne wirklich zu
   drucken — und ein Druckbild, das niemand vor der Abnahme sieht, ist ein
   Druckbild, das niemand geprueft hat. */
.druckblatt {
  display: none;
  padding: 16mm 14mm;
  background: #fff;
  color: #16202a; font: 12pt/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;
}
.druckblatt h1 { margin: 0 0 2mm; font-size: 20pt; letter-spacing: -.01em; }
.druckblatt h2 {
  margin: 7mm 0 2mm; font-size: 11pt; text-transform: uppercase;
  letter-spacing: .08em; color: #5a6672; font-weight: 700;
}
.dr-kopf { margin: 0 0 5mm; color: #5a6672; font-size: 11pt; }
.dr-ausflug { margin: 0 0 5mm; padding: 2.5mm 3mm; border-left: 3px solid #2f6fb0; background: #eef4fa; }
.dr-liste { list-style: none; margin: 0; padding: 0; }
.dr-liste li {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 3mm;
  padding: 2.2mm 0; border-bottom: .3pt solid #d4dbe2;
  break-inside: avoid;
}
/* Kaestchen zum Abhaken mit dem Stift — der Packstand vom Bildschirm
   gehoert nicht aufs Papier. */
.dr-box {
  flex: 0 0 auto; width: 4.5mm; height: 4.5mm;
  border: .6pt solid #55606b; border-radius: .8mm;
}
.dr-name { flex: 1 1 auto; }
.dr-sub { display: block; font-size: 9.5pt; color: #6b7681; }
.dr-grund { flex: 0 0 auto; width: 100%; padding-left: 7.5mm; font-size: 9.5pt; color: #8a949d; }
.dr-fuss { margin-top: 9mm; font-size: 9pt; color: #98a2ab; }

/* Die zwei Umschalter: echter Druck und Vorschau am Bildschirm. */
@media print {
  html, body { background: #fff; margin: 0; }
  .app, .overlays, .drag-layer, .toast { display: none !important; }
  .druckblatt { display: block; }
  @page { size: A4 portrait; margin: 0; }
}
html.druckvorschau, html.druckvorschau body { background: #fff; margin: 0; }
html.druckvorschau .app,
html.druckvorschau .overlays,
html.druckvorschau .drag-layer,
html.druckvorschau .toast { display: none !important; }
html.druckvorschau .druckblatt { display: block; }

/* ---------------------------------------------------- Abend- und Morgenrot

   Rueckmeldung Board (19.09.): «At Sunset make the sky red like in real
   life.»

   Eine eigene Ebene und kein zweiter Verlauf im Himmelsgrund: die Roete
   kommt und geht mit dem Sonnenstand, und eine Deckkraft laesst sich weich
   ueberblenden, eine Hintergrundfarbe nicht.

   Wie draussen ist die Farbe UNTEN am kraeftigsten und oben fast weg — die
   Sonne steht am Horizont, und die Luft darueber ist noch blau. Die Toene
   sind die einer Schweizer Abenddaemmerung: unten Glut, darueber Orange,
   dann ein violetter Saum, der ins Blaue laeuft. */
.sky-glut {
  position: absolute; inset: 0;
  pointer-events: none;
  transition: opacity 1.4s ease;
  /* Die Farbstufen liegen HOCH, und das ist gerechnet und nicht Geschmack:
     sichtbar ist vom Himmelsband (126 hoch) nur der Streifen ueber dem Grat,
     und der endet bei y 50, also bei 40 Prozent. Ein Verlauf mit der Glut
     unten — wie ein Foto ihn zeigt — waere komplett hinter den Bergen
     gelandet. Die Glut sitzt darum auf 38 bis 48 Prozent, direkt ueber der
     Gratlinie, und laeuft nach oben ins Blaue aus. */
  background: linear-gradient(180deg,
    rgba(70, 84, 140, 0) 0%,
    rgba(118, 92, 152, .30) 14%,
    rgba(196, 106, 104, .58) 26%,
    rgba(232, 134, 78, .82) 34%,
    rgba(246, 168, 86, .93) 41%,
    rgba(252, 202, 132, .96) 48%,
    rgba(252, 214, 150, .96) 100%);
}
/* Nachts liegt derselbe Saum tiefer und kuehler: der Himmel ist schon
   dunkel, und was bleibt, ist der letzte Schein ueber dem Grat. */
.sky.nacht .sky-glut {
  background: linear-gradient(180deg,
    rgba(20, 28, 56, 0) 0%,
    rgba(66, 56, 100, .28) 16%,
    rgba(134, 74, 84, .52) 28%,
    rgba(180, 96, 72, .68) 36%,
    rgba(206, 122, 80, .76) 44%,
    rgba(214, 140, 92, .78) 100%);
}
