:root {
  --bg: #ffffff;
  --fg: #1a1a1a;
  --muted-fg: #555555;
  --card-bg: #ffffff;
  --border: #dddddd;
  --control-bg: #ffffff;
  --img-placeholder: #f0f0f0;
  --bar-color: #4a7c59;
}

body.dark {
  --bg: #121212;
  --fg: #eaeaea;
  --muted-fg: #aaaaaa;
  --card-bg: #1c1c1c;
  --border: #3a3a3a;
  --control-bg: #1a1a1a;
  --img-placeholder: #2a2a2a;
  --bar-color: #6fae82;
}

/* **Hinter dem Leuchtkasten steht die Seite still** (03.09.2026, auf
   Wunsch: *„while in lightbox the gallery in the background shall not be
   scrollable actively"*). Der Leuchtkasten ist `fixed` und deckt den
   Schirm, ist aber selbst nicht rollbar - also ging jedes Rad und jeder
   senkrechte Wisch an das Dokument darunter, und man kam zurueck und
   stand vierzig Kacheln weiter.

   `overflow: hidden` und kein `position: fixed`-Kniff: die Rollhoehe
   bleibt damit erhalten, und beim Schliessen findet der Leuchtkasten die
   Kachel wieder, aus der er geoeffnet wurde. */
body.leuchtkasten-offen { overflow: hidden; }

body {
  font-family: Arial, sans-serif;
  margin: 0 20px 20px 20px;
  padding-left: 64px;
  background: var(--bg);
  color: var(--fg);
}

button, select {
  background: var(--control-bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 4px 8px;
}

#appNav {
  position: fixed;
  top: 0;
  left: 0;
  height: 100vh;
  /* **`border-box`, or the rail is 12px taller than the screen** - the
     padding is added to `100vh` otherwise (measured: 812px in an 800px
     window).

     Als das hier stand, sass nichts in diesen 12px, und der Satz endete
     mit: *sie bleiben, weil ein Geländer, das `100vh` sagt und es nicht
     ist, eine Falle für das ist, was als Nächstes an seinen Fuss kommt.*
     Seit dem 04.09.2026 steht dort der Wechsler (`.nav-revier` weiter
     unten), und das Polster ist genau der Abstand, den er zum unteren
     Rand hält. Die Falle ist nie zugeschnappt, weil sie schon zugemacht
     war. */
  box-sizing: border-box;
  width: 64px;
  background: var(--control-bg);
  border-right: 1px solid var(--border);
  /* **Über der offenen Karte, unter dem Leuchtkasten** (04.09.2026, auf
     Wunsch: *„the count icon on the button shall be above the map ribbon
     / map. (But behind lightbox)"*).

     Die Leiste stand auf 750 und damit unter dem Band (900). Weil sie
     selbst ein Stapelzusammenhang ist, kann kein Kind von ihr - auch die
     Marke nicht - darüber hinaus; die Zahl musste also die Leiste
     mitnehmen. 910: über dem Band, unter den Knöpfen der offenen Karte
     (950, die in ihrer eigenen Ecke liegen), unter dem Wechsler-Aufklapp
     (950) und weit unter dem Leuchtkasten (9999). */
  z-index: 910;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 12px;
  /* Und unten dasselbe, seit der Wechsler am Fuss steht. */
  padding-bottom: 12px;
  gap: 4px;
}

.nav-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 10px 4px;
  color: var(--muted-fg);
  text-decoration: none;
  font-size: 10px;
  width: calc(100% - 12px);
  border-radius: 6px;
}

.nav-item.active {
  color: var(--bar-color);
  background: var(--bg);
}

.nav-item svg { stroke: currentColor; }

/* ---- The gallery's four filter buttons ----

   One per question you can ask of the archive, in the bottom right
   corner, each carrying the number of answers you have picked. They
   replaced a sticky bar holding a "Filter" button, a chips row naming
   every active filter, and a drop-down with all four groups stacked
   inside it - which was chrome about chrome, and the tallest thing on a
   page whose whole job is thumbnails.

   At the bottom rather than the top, where they started. Two reasons,
   and the second is the one that settled it: a thumb reaches the bottom
   of a phone and not the top of it, and up there the grid had to begin
   72px down the page to clear them. Now the thumbnails start at the top
   edge and every control floats over a corner.

   Fixed rather than sticky: they float over the grid like the map's own
   FABs, so scrolling never takes the filters away and the grid keeps its
   full height. Clear of the band's own height, and under the band's
   z-index (900), which covers the screen when the map is open. */
/* One row: the button that unasks everything, then the three questions.
   It was a column, with `clear all` on its own line above - which kept
   the row from moving when it appeared, but cost a whole line of screen
   for one button and put it furthest from the thumb that reaches this
   corner.

   **Leftmost, and outside the row.** Appearing still must not shove the
   three sideways, so it grows into the space to their *left* - away from
   the corner they are anchored to - and the row itself never moves. */
.gallery-fabs {
  position: fixed;
  right: 20px;
  bottom: calc(var(--revier-band-height, 0px) + 16px);
  z-index: 850;   /* over the page, under the open band (900) */
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 12px;
  /* **Der Kasten fängt nichts, nur seine Knöpfe** (04.09.2026, auf
     Wunsch: *„when gallery filter window are open in map the + fab is not
     clickable (desktop)"*).

     Mit offenem Blatt bekommt die Reihe ein `left: 76px` (siehe
     `.blatt-offen` weiter unten) - aus gutem Grund, denn die vier Knöpfe
     sollen nach links aufreihen können. Der Preis war ein unsichtbarer
     Streifen quer über die ganze untere Kante des Schirms, auf `z-index`
     950 über der offenen Karte (900): darunter lag der `+`-Knopf der
     Karte (`.map-actions`, im Band bei x 88-144), und ein Druck darauf
     landete auf dem Streifen und tat nichts. Zu sehen war nichts davon -
     der Kasten hat keine Farbe.

     Also nimmt der Kasten keine Zeiger mehr an und die Knöpfe darin
     schon. Dieselbe Antwort wie bei `.gallery-tools` oben rechts, aus
     demselben Grund: eine Ecke, die grösser ist als das, was in ihr
     steht, darf nicht so gross fangen, wie sie ist. */
  pointer-events: none;
}

.gallery-fabs > * { pointer-events: auto; }

.gallery-fab-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Everything off at once, and only on screen while there is something to
   turn off. Orange because it undoes rather than asks - the same colour
   the count bubbles, the ribbon's × and the empty grid's own way out
   wear, which are the other ways to drop a filter.

   Left of the three, not among them: it is not a fourth question. Hidden
   it is `display: none`, so the row's gap goes with it and the three sit
   exactly where they do with no filter set. */
.gallery-fab.clear-all {
  background: #d98324;
  border-color: #d98324;
  color: #fff;
}

.gallery-fab.clear-all:hover { background: #b3651b; border-color: #b3651b; }
.gallery-fab.clear-all[hidden] { display: none; }

.gallery-fab {
  position: relative;
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--control-bg);
  color: var(--fg);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,0.18);
}

/* Filtering by this group, whether or not its panel is down. The count
   bubble says how much; this says *that*, at a glance across four
   buttons, which is the job the chips row had. */
.gallery-fab.active {
  background: var(--bar-color);
  border-color: var(--bar-color);
  color: #fff;
}

/* Open: outlined rather than filled, so "this panel is showing" and
   "this filter is doing something" stay two different marks. */
.gallery-fab.open { box-shadow: 0 0 0 3px var(--bar-color); }

.gallery-fab-holder { position: relative; display: flex; }

/* ---------------------------------------------------------------
   **Anblicke auf der Karte ausblenden** (04.09.2026, auf Wunsch).

   Der Knopf gehoert der Karte und sonst niemandem: in der Galerie gibt
   es nichts auszublenden, und ein Knopf, der dort neben den vier Fragen
   staende, waere eine fuenfte, die er nicht ist. Also `display: none`
   als Regel und zwei benannte Ausnahmen.

   **Die erste**: waehrend die Reihe der vier ausgeklappt ist, steht er
   links davon - was die vier *nicht* fragt, steht neben ihnen und nicht
   in ihrer Reihe, dieselbe Stelle wie `clear-all` in der Galerie.

   **Die zweite ist die eigentliche** (*„when clicked, the filter window
   shall close and show the grey FAB"*): die Reihe gibt es nur, solange
   ein Filterfenster offen ist, und das macht genau dieser Knopf zu. Er
   bliebe also nach seinem eigenen Druck nicht stehen. Solange die
   Anblicke weg sind, steht er deshalb auch bei zugeklappter Reihe da -
   als das Zeichen, dass gerade etwas nicht gezeichnet wird, und als der
   Weg zurueck. Sind sie zu sehen, gibt es nichts anzuzeigen und er
   verschwindet mit der Reihe.
   --------------------------------------------------------------- */
.gallery-fab-holder.anblicke-aus { display: none; }

body.revier-open .gallery-fabs.blatt-offen .gallery-fab-holder.anblicke-aus {
  display: flex;
  /* **Ganz rechts, unter demselben Finger** (04.09.2026, auf Wunsch:
     *„when the anblick filter window is open, place the Anblicke
     Ausblenden FAB as the rightmost fab so that opening the window and
     clicking at the same location hides the Anblicke on the map"*).

     Er stand links neben den vier, mit dem Argument, dass er keine
     fuenfte Frage ist. Das stimmt weiter - nur wiegt etwas anderes
     schwerer: zugeklappt steht in dieser Ecke *ein* Knopf, und der
     oeffnet das Blatt. Wer ihn drueckt und dann noch einmal an dieselbe
     Stelle tippt, hat die Anblicke weg. Zwei Handgriffe an einem Ort,
     ohne den Daumen zu bewegen.

     `order` und nicht die Reihenfolge im DOM: dort steht er weiter
     vorn, weil er beim Zuklappen an die Stelle des Sammelknopfs tritt
     (siehe unten) - und diese Regel gilt nur fuer das offene Blatt. */
  order: 1;
  margin-left: 4px;
}

body.revier-open .gallery-fabs:not(.blatt-offen)
  .gallery-fab-holder.anblicke-aus.anblicke-verborgen { display: flex; }

/* **Und er tritt dabei an die Stelle des Sammelknopfes** (04.09.2026,
   auf Wunsch: *„when the map grey hide Anblick fab is active it shall
   take the place of the Anblick Filter fab on the bottom right"*).

   Nicht daneben: die Ecke traegt einen Kreis, und zwei nebeneinander
   waeren zwei Angebote, wo eines gilt. Solange die Anblicke weg sind,
   ist der graue der Knopf dieser Ecke - und ein Druck darauf holt beides
   zurueck, die Marker und die Reihe der vier (`toggleMapAnblicke`). */
body.revier-open .gallery-fabs:not(.blatt-offen):has(.anblicke-verborgen)
  .gallery-fab-holder.sammel { display: none; }

/* In der Ecke ist er so gross wie der Sammelknopf, den er vertritt (56) -
   zwei Groessen an derselben Stelle hiessen, die eine davon sei
   wichtiger. */
body.revier-open .gallery-fabs:not(.blatt-offen)
  .gallery-fab-holder.anblicke-aus .gallery-fab { width: 56px; height: 56px; }

/* Grau, solange die Anblicke weg sind - und ausdruecklich **nicht** die
   Balkenfarbe der aktiven Filterknoepfe: der hier ist keine Frage an das
   Archiv, sondern etwas, das gerade nicht gezeichnet wird. Zwei
   verschiedene Sachen duerfen nicht gleich aussehen. */
.gallery-fab-holder.anblicke-verborgen .gallery-fab {
  background: #6b6b6b;
  border-color: #6b6b6b;
  color: #fff;
}

.gallery-fab-holder.anblicke-verborgen .gallery-fab:hover {
  background: #565656;
  border-color: #565656;
}

/* ---------------------------------------------------------------
   **Die Ecke auf einem schmalen Schirm** (22.08.2026, auf Wunsch).

   Vier Knöpfe sind 218px auf einem 390px-Telefon - über die halbe
   Breite für Fragen, die man selten stellt, und ausgerechnet dort, wo
   der Daumen schon das `+` und das Elementregal sucht. Also steht dort
   nur noch **einer**: der zuletzt geöffnete, grün, mit der Summe aller
   vier auf der Schulter. Ein Druck macht das weisse Fenster auf, und
   die vier stehen darin - dieselben vier Knöpfe, an einem anderen Ort.

   **Kein fünfter Knopf**, sondern einer der vier: ein eigener
   Sammelknopf hätte dieselben vier Zeichen noch einmal gebraucht.

   Am Schreibtisch ändert sich nichts - dort ist Platz, und vier Knöpfe
   nebeneinander sind schneller als vier hinter einem.
   --------------------------------------------------------------- */
@media (max-width: 700px) {
  /* Zu: alle bis auf den einen weg. `display: none` und nicht
     `visibility`, sonst nähme die Reihe ihre Lücken mit. */
  .gallery-fabs:not(.blatt-offen) .gallery-fab-holder:not(.sammel) { display: none; }

  /* Die zweite Hälfte des Paares von weiter oben - hier drin, weil die
     Farbe an dieselbe Bedingung gehört wie das Wegblenden der drei
     anderen. */
  .gallery-fab.sammel {
    background: var(--bar-color);
    border-color: var(--bar-color);
    color: #fff;
  }

  /* **Offen: die vier unter dem Fenster**, als Reihe, die dazugehört.
     Die Ecke reicht dafür nach links auf, bis die vier plus der orange
     „alle aufheben" nebeneinander stehen.

     **Bis 76px und keinen Pixel weiter**: dort steht das `+` der
     Galerie (16px Rand, 48px Knopf). Mit `left: 20px` lag der erste der
     vier darunter - zwei Knöpfe an derselben Stelle, und der obere war
     der, den man nicht wollte. */
  .gallery-fabs.blatt-offen {
    left: 76px;
    justify-content: flex-end;
  }
}

/* Der eine trägt Grün - er steht für alle vier, auch wenn keiner davon
   etwas tut. Das ist der Unterschied zu `.active`: dort heisst Grün
   „dieser filtert gerade", hier „hier sind die Filter".

   **Grün trägt er nur, wo er allein steht**, und das sind genau zwei
   Stellen: der schmale Schirm und die offene Karte. Sie stehen hier als
   **Paar**, und das ist der Punkt - kurz stand die Regel ohne
   Media-Query da, und am Schreibtisch war der Anblick-Knopf damit ein
   grüner unter drei weissen. Wer die eine anfasst, fasst die andere an. */
body.revier-open .gallery-fab.sammel {
  background: var(--bar-color);
  border-color: var(--bar-color);
  color: #fff;
}

/* ---------------------------------------------------------------
   **Die Ecke der offenen Karte ist eine Spalte** (22.08.2026, auf
   Wunsch).

   Auf der Karte liegen unten rechts vier Dinge übereinander, und sie
   kamen aus vier verschiedenen Rechnungen: die Filterknöpfe der Galerie
   (44px, weiss, zu viert), der Regalknopf (56px, grün), Leaflets Regler
   und der orange „alles aufheben". Vier Grössen, vier Abstände, drei
   rechte Ränder.

   Jetzt ist es **eine Spalte aus 56ern**, von unten nach oben: der
   Anblick-Knopf, das Elementregal, Leaflets Regler. Alles rechtsbündig,
   alles im selben Abstand.

   - **Nur einer statt vier**, auch am Schreibtisch: auf der Karte ist
     die Ecke die Werkbank, und dort steht der Anblick-Knopf neben
     Knöpfen der Karte und nicht neben seinesgleichen. Vier weisse
     Kreise dazwischen lesen sich als Teil der Karte.
   - **56px**, weil der `+`-Knopf gegenüber 56 ist. Zwei Grössen in
     einer Reihe heissen: die eine davon ist wichtiger, und das stimmt
     hier nicht.
   - **Kein oranger Nachbar.** Die Blase auf dem Knopf hebt seit dem
     22.08. ohnehin *alle* Filter auf - der eigene Knopf daneben sagte
     dasselbe ein zweites Mal, und zwar in der Ecke, in der jeder Kreis
     etwas anderes tut.
   --------------------------------------------------------------- */
body.revier-open .gallery-fabs:not(.blatt-offen) .gallery-fab-holder:not(.sammel),
body.revier-open .gallery-fab.clear-all { display: none; }

body.revier-open .gallery-fab.sammel { width: 56px; height: 56px; }

/* Aufgeklappt stehen die vier auch hier als Reihe unter dem Blatt, und
   auch hier muss das `+` frei bleiben. */
body.revier-open .gallery-fabs.blatt-offen {
  left: 76px;
  justify-content: flex-end;
}

/* ---------------------------------------------------------------
   **Der Regalknopf rutscht auf die Höhe des Anblick-Fabs**
   (22.08.2026, auf Wunsch).

   Er stand auf 92px - „24 für den Rand, 56 für die Filterreihe, 12
   Luft" -, und das war die Rechnung für *Leaflets* Reihe darunter. Der
   Daumen kennt aber nur eine Ecke: in der Galerie greift er dort nach
   dem Anblick-Fab, auf der offenen Karte nach dem Regal. Zwei Höhen für
   denselben Griff sind eine zu viel.

   Dieselbe Formel wie `.gallery-fabs`, nicht dieselbe Zahl: das Band ist
   auf dem Telefon flach (44px) und am Schreibtisch ein Streifen (84px),
   und eine abgeschriebene Zahl wäre auf einem der beiden falsch.
   --------------------------------------------------------------- */
/* **Direkt über dem Anblick-Knopf**, und der steht auf 24 (`body.revier-open
   .gallery-fabs`). 24 + 56 + 12 Luft = 92. Auf dem Telefon kommt für
   beide dieselbe Verschiebung dazu - der Knopf der Galerie liegt fest im
   Sichtfenster, dieser im Band, und das endet über der Leiste -, also
   gilt die Zahl dort unverändert.

   Vorher stand hier eine Formel aus `--revier-band-height`. Sie war für
   die *Galerie* gerechnet, wo das Band zugeklappt ist; auf der offenen
   Karte gibt es keine Bandhöhe zu umgehen, sondern einen Knopf, über den
   dieser gehört. */
.map-sidebar-fab { bottom: 92px; }

/* The count, and the way to drop what it is counting.

   It is a button in its own right - a sibling of the FAB, not a child,
   because a button inside a button is not markup a browser will keep.
   Positioned against the holder so it still sits on the FAB's shoulder.

   Hovering turns the number into an ×. The number and the × are the same
   fact from two sides - "three things are picked" and "let the three
   go" - so they can share one 18px circle rather than needing a second
   control next to a button that is already small. */
.fab-count {
  position: absolute;
  top: -4px;
  right: -4px;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border: none;
  border-radius: 9px;
  background: #d98324;
  color: #fff;
  font-size: 11px;
  font-weight: bold;
  line-height: 1;
  cursor: pointer;
}

.fab-count[hidden] { display: none; }

.fab-count-x { font-size: 13px; }

/* With a pointer: the × replaces the number, and only while you are on
   it. Nothing is added to the resting state, which is the whole appeal -
   four counts with four ×s permanently beside them is the chips row
   again, in miniature. */
@media (hover: hover) {
  .fab-count-x { display: none; }
  .fab-count:hover { background: #b3651b; }
  .fab-count:hover .fab-count-n { display: none; }
  .fab-count:hover .fab-count-x { display: block; }
}

/* Without one there is no hover to reveal it with, so the × sits beside
   the number permanently and the bubble grows into a small pill. Bigger,
   too: a thing you are meant to hit with a thumb, sitting on the
   shoulder of another thing you are meant to hit with a thumb. */
@media (hover: none) {
  /* The pill reaches further past the button's edge than the circle
     does, so the row needs a little more air or it lands on its
     neighbour. */
  .gallery-fab-row { gap: 14px; }
  .fab-count {
    top: -6px;
    right: -8px;
    height: 22px;
    border-radius: 11px;
    padding: 0 7px;
    font-size: 12px;
  }
  .fab-count-x { font-size: 15px; opacity: 0.85; }
}

/* The panel above them. One at a time, and no taller than it has to be -
   it is over the grid, not part of the page.

   It opens upwards out of its button and shares the row's right edge, so
   the calendar and the clock grow away from the hand that opened them
   rather than under it.

   How far up is one number in two places (here and the phone rule near
   the foot of this file), so it is a variable: the row is 72px of
   clearance and there is only ever one row of it now.

   It has to clear the **other** corner too. On a phone the panel is very
   nearly the width of the screen, so the `+` in the bottom left is under
   it as surely as the filter row is - and this used to open straight
   over a column of four buttons. Hence the `max()`: whichever bottom
   corner is taller is the one the panel has to start above. The action
   column is one 48px FAB now (uploading moved into the window it opens,
   the mode and the sorting went to the top right), which is 64px plus
   air. */
.gallery-filter {
  position: fixed;
  right: 20px;
  --gallery-action-clearance: 76px;
  bottom: calc(
    var(--revier-band-height, 0px) +
    max(var(--gallery-fab-clearance, 72px), var(--gallery-action-clearance))
  );
  z-index: 850;
  width: min(340px, calc(100vw - 40px));
  max-height: min(60vh, 520px);
  overflow-y: auto;
  padding: 12px 14px 14px;
  background: var(--control-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 28px rgba(0,0,0,0.28);
}

.gallery-filter[hidden] { display: none; }

/* The panel opens out of the four buttons, so it has to clear whatever is
   stacked over them - which is the `clear all` line, and only while there
   is a filter to clear. `.has-clear` is put on the holder by
   applyFilterFabs() in the same breath as un-hiding the button. */
/* Nothing to add any more: `clear all` is *beside* the three rather than
   above them, so the corner is one row high whether or not it is
   showing. The class is still put on by applyFilterFabs() - it is what
   the full-width phone panel keys off. */

.gallery-filter-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}

.gallery-filter-title { flex: 1; font-weight: bold; }

.gallery-filter-clear {
  padding: 2px 8px;
  font-size: 12px;
}

.gallery-filter-clear[hidden] { display: none; }

.gallery-filter-close {
  width: 24px;
  height: 24px;
  padding: 0;
  font-size: 20px;
  line-height: 1;
  color: var(--muted-fg);
  background: transparent;
  border: none;
  cursor: pointer;
}

.gallery-filter-pane[hidden] { display: none; }

/* ---- The calendar ----

   A month at a time, Monday first. Days with something in them are
   shaded by how much; days with nothing are visibly nothing, which is
   the thing a list of days could never show. */
.day-calendar { font-size: 13px; }

.calendar-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}

.calendar-month { font-weight: bold; }

/* Der Pfeil und seine Blase stehen übereinander, also braucht es etwas,
   woran die Blase hängen kann - ein Geschwister des Knopfes, kein Kind
   davon (siehe `.fab-count`). */
.calendar-step-holder { position: relative; display: inline-flex; }

.calendar-step {
  width: 28px;
  height: 28px;
  padding: 0;
  font-size: 18px;
  line-height: 1;
}

.calendar-step:disabled { opacity: 0.3; cursor: default; }

/* **Was hinter dem Pfeil steht** (09.09.2026). Dieselbe orange Blase wie
   an den vier Filterknöpfen und dieselbe Klasse - Farbe, das × beim
   Darüberfahren und das dauerhafte × ohne Zeiger sind dort einmal
   geschrieben und gelten hier mit. Hier ist nur, was am 28px-Knopf
   anders sein muss als am 48px-Knopf: kleiner, und auf der Schulter, die
   in die Richtung zeigt, für die sie zählt.

   Die Blase steht höchstens 5px über den Knopf hinaus, das Fenster hat
   14px Polster - sie bleibt also innerhalb des Kastens, der scrollt. */
.calendar-step-count {
  top: -5px;
  min-width: 15px;
  height: 15px;
  border-radius: 8px;
  padding: 0 3px;
  font-size: 10px;
}

.calendar-step-holder.zurueck .calendar-step-count { right: auto; left: -5px; }
.calendar-step-holder.vor .calendar-step-count { right: -5px; }

.calendar-step-count .fab-count-x { font-size: 11px; }

/* Ohne Zeiger steht das × dauerhaft neben der Zahl, also wird aus dem
   Kreis eine kleine Pille - und weil die Regel für die grosse Blase in
   derselben Abfrage weiter oben steht, muss diese hier danach kommen. */
@media (hover: none) {
  .calendar-step-count {
    top: -6px;
    min-width: 18px;
    height: 18px;
    border-radius: 9px;
    padding: 0 5px;
    font-size: 11px;
  }
  .calendar-step-holder.zurueck .calendar-step-count { left: -8px; }
  .calendar-step-holder.vor .calendar-step-count { right: -8px; }
  .calendar-step-count .fab-count-x { font-size: 13px; }
}

.calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 3px;
}

.calendar-weekday {
  text-align: center;
  font-size: 11px;
  color: var(--muted-fg);
  padding-bottom: 2px;
}

.calendar-day {
  position: relative;
  aspect-ratio: 1;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: var(--card-bg);
  color: var(--fg);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  line-height: 1.1;
}

/* Four steps of the bar colour. Nothing at all is left as the plain card
   background and dimmed, so an empty day reads as empty rather than as
   the palest kind of busy. */
.calendar-day[data-heat="0"] { opacity: 0.45; cursor: default; }
/* Today. A ring, not a fill: the fill is what "picked" means on this
   grid, and today is a fact about the date rather than an answer to the
   question. Inside the border so it survives being picked as well. */
.calendar-day.today { box-shadow: inset 0 0 0 2px #d98324; }

.calendar-day[data-heat="1"] { background: color-mix(in srgb, var(--bar-color) 16%, var(--card-bg)); }
.calendar-day[data-heat="2"] { background: color-mix(in srgb, var(--bar-color) 34%, var(--card-bg)); }
.calendar-day[data-heat="3"] { background: color-mix(in srgb, var(--bar-color) 56%, var(--card-bg)); }
.calendar-day[data-heat="4"] { background: color-mix(in srgb, var(--bar-color) 78%, var(--card-bg)); color: #fff; }

/* Picked. An outline rather than another fill: the fill is already
   saying how busy the day was, and one box cannot carry two scales. */
.calendar-day.selected {
  border-color: #d98324;
  box-shadow: inset 0 0 0 2px #d98324;
  font-weight: bold;
}

/* **Die Tage der Nachbarmonate**, mit denen die sechs Wochen aufgefüllt
   sind (09.09.2026). Echte Tage, mit ihrer Zahl, ihrer Wärme und ihrem
   Druck - nur leiser, denn oben steht ein anderer Monat.

   Gedämpft am ganzen Feld statt an der Schrift: die Wärme ist das, was
   über die Monatsgrenze hinweg gelesen werden soll, und sie an der Kante
   fortzunehmen hiesse, den 31. und den 1. wieder auseinanderzureissen.
   Die zwei Ausnahmen danach sind die zwei Fälle, in denen die Dämpfung
   das Falsche täte: ein leerer Nachbartag darf ein Schatten sein, ein
   gewählter muss ganz da sein, sonst sieht man die Antwort nicht, die
   man abwählen will. */
.calendar-day.nachbarmonat { opacity: 0.5; }
.calendar-day.nachbarmonat[data-heat="0"] { opacity: 0.22; }
.calendar-day.nachbarmonat.selected { opacity: 1; }

.calendar-number { font-size: 12px; }
.calendar-count { font-size: 10px; opacity: 0.8; }

/* ---- The clock ----

   Twenty-four wedges from the middle, each as long as its hour is busy.
   "When do they come through" is a shape, and a column of twenty-four
   numbered rows made you assemble it in your head. */
.hour-clock { display: flex; flex-direction: column; align-items: center; }

.hour-clock-face { width: 100%; max-width: 260px; }

.hour-wedge-track {
  fill: var(--card-bg);
  stroke: var(--border);
  stroke-width: 0.5;
}

.hour-wedge.live .hour-wedge-track { cursor: pointer; }
.hour-wedge.live:hover .hour-wedge-track { fill: color-mix(in srgb, var(--bar-color) 18%, var(--card-bg)); }
.hour-wedge.empty .hour-wedge-track { opacity: 0.45; }

/* Deaf to the pointer: it lies on top of the wedge you are aiming at,
   and a three-pixel spike must not be the only part that answers. */
.hour-wedge-fill {
  fill: var(--bar-color);
  pointer-events: none;
}

.hour-wedge.selected .hour-wedge-track {
  stroke: #d98324;
  stroke-width: 2;
}

.hour-wedge.selected .hour-wedge-fill { fill: #d98324; }

.hour-tick {
  fill: var(--muted-fg);
  font-size: 11px;
}

.hour-clock-caption { margin-top: 6px; text-align: center; }

/* Armed: multi-select is on, the sort menu is down. Orange, the mark
   every armed thing in this app wears. */
.list-fab.active { background: #d98324; }

/* ---- The bottom-left corner ----

   One button now: the `+`, which writes. It was a column of three, and
   the other two left in opposite directions - uploading went *inside*
   the window this opens, and the mode and the sorting went to the top
   right, because neither of them changes the archive.

   The column rule stays for the other pages that use it, and because a
   second action here would stack the same way. */
.list-fabs.column { flex-direction: column; }

.list-fab.quiet {
  background: var(--control-bg);
  color: var(--fg);
  border: 1px solid var(--border);
}

.list-fab.quiet:hover { background: var(--bg); }
.list-fab.quiet.active { background: #d98324; border-color: #d98324; color: #fff; }

/* ---- Two glyphs, one button ----

   A pencil while the mode is off, a tick while it is on. Off the button's
   own `.active`, in CSS.

   **This was JS and it did nothing.** `svg.hidden = true` assigns a
   property that does not exist: `hidden` is an IDL attribute of
   HTMLElement, and an `<svg>` is an SVGElement, which is not one. The
   assignment quietly created an expando, both glyphs stayed exactly as
   the markup left them, and nothing anywhere threw - including the jsdom
   check, which read the expando straight back and reported success. See
   docs/testing.md. */
.fab-glyph.on { display: none; }
.list-fab.active .fab-glyph.off { display: none; }
.list-fab.active .fab-glyph.on { display: block; }

/* On for the mode, dead until there is something to do it to. Not
   `[hidden]`: what the mode can do is worth seeing before you have
   committed to using it, and a control that vanishes teaches nothing. */
.list-fab:disabled {
  opacity: 0.4;
  cursor: default;
  box-shadow: none;
}

.list-fab:disabled:hover { background: var(--bar-color); }
.list-fab.quiet:disabled:hover { background: var(--control-bg); }
.list-fab.danger:disabled:hover { background: #7a2020; }

/* Written out rather than drawn. Ansprechen is the one action in this
   corner that opens a dialog to drill a vocabulary, and it is the one
   reached for most; a stag's head alone asks you to remember that a stag
   means "name what this was". A pill, not a disc - the same shape the
   lightbox's own Ansprechen wears, which is where the habit comes from. */
.list-fab.wide {
  width: auto;
  min-width: 48px;
  gap: 8px;
  padding: 0 18px 0 14px;
  border-radius: 24px;
  font-size: 14px;
  white-space: nowrap;
}

/* ---- The top-right pair: the mode, and the order ----

   Sorting and multi-select are the two controls that change how you
   *read* the grid rather than what is in it, and both are set once and
   then forgotten about. That is not a thumb-reach control, and the two
   bottom corners were full: the filters own one, the `+` owns the other.

   Multi-select takes the corner - arming it changes what every tap on
   the page means - and sorting sits to its left, which is the order they
   are written in.

   Under the band's z-index and over the grid, like every other floating
   control on this page. Clear of the nav, which is a rail down the left
   on a desktop and a bar along the bottom on a phone: the top right is
   the one corner it is never in. */
.gallery-tools {
  position: fixed;
  top: 16px;
  right: 20px;
  z-index: 850;
  /* A column of rows: the two permanent buttons across the top, and
     under them whatever the mode has added - which is the delete, and
     only while something is selected. Right-aligned, so the tick keeps
     the corner and the button below hangs off the same edge. */
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
  /* The gap between the permanent pair and the delete below is grid, and
     in multi-select every tile of it is a target. */
  pointer-events: none;
}

.gallery-tools > * { pointer-events: auto; }

.gallery-tools-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

/* So the count of what is selected can sit on the tick's shoulder, the
   same way a filter's count sits on its button. */
.tool-fab-holder { position: relative; display: flex; }
/* Same trap as `.list-fab` further down: the `display` above outranks the
   UA's `[hidden]`, and an empty holder would still be taking the row's
   gap beside the `+`. */
.tool-fab-holder[hidden] { display: none; }

/* It is a label here, not a button - the way to drop the selection is the
   tick it is sitting on. `.fab-count` is a <button> in the filter row and
   a <span> here, so the shared rule cannot assume either. */
.tool-fab-holder .fab-count { pointer-events: none; }

.sort-fab-holder {
  position: relative;
  display: flex;
}

/* Downwards now, and right-aligned under its own button. It opened
   sideways for as long as it lived at the top of the bottom-left column,
   where there were two buttons directly beneath it - up here there is
   nothing under it but thumbnails, and sideways from the right edge of
   the screen would open off it. */
.sort-menu {
  position: absolute;
  top: 58px;
  right: 0;
  z-index: 1;
  padding: 8px;
  background: var(--control-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 28px rgba(0,0,0,0.28);
}

.sort-menu[hidden] { display: none; }

/* The map's element list is the only drawer left - the gallery's filters
   are four buttons over the grid, the Revier band took the bottom of the
   page, and the band's element list took the right edge. See
   docs/revier-band.md.

   A full-height bar down the pane's left edge closes it, and marks the
   edge to swipe from. */
.pane-close-bar {
  flex: 0 0 22px;
  background: var(--bar-color);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  font-size: 20px;
  line-height: 1;
}

.pane-body {
  flex: 1 1 auto;
  min-width: 0;
  overflow-y: auto;
}

@media (max-width: 700px) {
  body {
    padding-left: 0;
    padding-bottom: 56px;
  }
  /* The band sits on top of the bottom nav here, so the page has to clear
     both. Spelled out because `body.has-revier` outscores the plain
     `body` rule above whatever order they're written in, and would
     otherwise drop the nav's 56px on the floor. */
  body.has-revier { padding-bottom: calc(56px + var(--revier-band-height)); }
  /* One column: 230px minimums would otherwise wrap into a second column
     that doesn't fit. */
  .filter-panel { grid-template-columns: 1fr; }
  /* The nav is a bar along the bottom here and the band sits on top of
     it, so everything anchored to the bottom has to clear both. Same
     arithmetic as .list-fabs below. */
  .gallery-fabs { right: 16px; bottom: calc(56px + var(--revier-band-height, 0px) + 16px); }


  /* **A sheet, not a card.** At 360px a 340px panel floating 16px off
     three edges is a card with a hairline of page around it, which is
     the worst of both: it reads as an overlay you could dismiss by
     tapping past, and there is nowhere to tap past to.

     So it takes the full width and runs down to the Revier band - the
     one thing along the bottom it must not cover, because that is the
     other filter and you need to see what it says while narrowing by
     anything else. The band, not the nav: the nav is behind the band
     here and the band's own height already includes it.

     The FABs stay **over** it (they are z-index 850 to the panel's 840),
     so the three questions and the way to unask them are on top of the
     answer sheet rather than under it. That is the whole reason the
     panel is not simply the full screen. */
  .gallery-filter {
    right: 0;
    left: 0;
    bottom: calc(56px + var(--revier-band-height, 0px));
    z-index: 840;
    width: auto;
    max-height: min(70vh, calc(100vh - 140px));
    border-radius: 12px 12px 0 0;
    border-left: none;
    border-right: none;
    border-bottom: none;
    /* Room under the list for the row of FABs lying over it. */
    padding-bottom: calc(var(--gallery-fab-clearance, 72px) + 8px);
  }
  #appNav {
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    height: 56px;
    width: 100%;
    flex-direction: row;
    justify-content: space-around;
    border-right: none;
    border-top: 1px solid var(--border);
    padding-top: 0;
  }
  .nav-item { width: auto; flex: 1; }
}

/* ---- Ein Feld hat den Fokus: Leiste und Band gehen nach unten ----
   (19.08.2026, zweite Korrektur)

   Zwei Anläufe davor haben *gemessen*, wie hoch die Tastatur steht, und
   die Leiste um diese Zahl zurückgeschoben. Beide waren zu spät: eine
   Tastatur fährt animiert auf, die Zahlen kommen in Stufen, und bis eine
   davon über der Schwelle lag, waren zwei Zeichen getippt. So kam es auch
   zurück - „geht immer noch hoch, verschwindet erst nach dem zweiten
   Zeichen."

   Der Fokus ist sofort da und *ist* die Absicht: wer in ein Feld tippt,
   will das Feld sehen und nicht die Leiste. Also verlassen beide den
   Bildschirm **nach unten**, sobald ein Textfeld den Fokus hat - keine
   Messung, keine Schwelle, kein Zwischenzustand. Nach unten, weil das die
   Richtung ist, in die sie gehören; nach oben zu wandern war die Klage.

   **`pointer: coarse` gehört dazu.** Eine Maus öffnet keine Tastatur, und
   auf einem schmalen Fenster am Schreibtisch wäre eine Leiste, die beim
   Klick ins Suchfeld verschwindet, ein Fehler statt einer Hilfe.

   Das zugeklappte Band nur, solange es zugeklappt ist: eine offene Karte
   ist der ganze Bildschirm und hat keinen unteren Rand, an dem sie
   klebt. */
@media (max-width: 700px) and (pointer: coarse) {
  body.feld-fokus #appNav {
    /* Die eigene Höhe reicht: die Leiste klebt am unteren Rand. */
    transform: translateY(100%);
    transition: transform 0.18s ease;
  }

  /* **Das Band braucht mehr als seine eigene Höhe.** Es sitzt 56px über
     dem unteren Rand (auf der Leiste), also lässt `translateY(100%)` einen
     Streifen davon stehen - im Prüflauf waren es 40px, und ein
     grünes Band, das zu einem Drittel aus dem Bild ragt, ist genau die
     Sorte Halbheit, wegen der diese Regel überhaupt geschrieben wurde.
     Die eigene Höhe plus der Abstand, den es hält. */
  body.feld-fokus .revier-panel:not(.open) {
    transform: translateY(calc(100% + 60px));
    transition: transform 0.18s ease;
  }

  /* Und die Suchreihe? **Die zieht ganz um** - nach oben, siehe unten bei
     `.list-search-fab.open:focus-within`. Bis zum 21.08. stieg sie hier um
     die gemessene Tastaturhöhe; dass das auf einem Telefon die falsche
     Frage war, ist dort begründet.

     Übrig bleibt die Reihe selbst, in der dann nur noch das `+` steht. Sie
     rückt an den unteren Rand, weil die Leiste weg ist, und bleibt dort -
     hinter der Tastatur, was richtig ist: wer tippt, drückt kein `+`. Kein
     `--tastatur-hoehe` mehr, denn nichts an dieser Reihe muss noch
     sichtbar sein. */
  body.feld-fokus .list-fabs {
    bottom: 16px;
  }
}

/* `min-width: 0` von aussen nach innen: ein Flex-Kind ist von sich aus
   so breit wie sein Inhalt, und die Kette Baumzeile -> Etikett ->
   Name muss durchlaessig sein, damit die Auslassung am Namen (weiter
   unten) ueberhaupt greift. Fehlt sie an *einer* Stelle, schiebt ein
   langer Pfad den Balken aus dem Panel hinaus. */
.tree-row { display: flex; align-items: center; padding: 2px 0; min-width: 0; }

.tree-row .tree-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  color: var(--muted-fg);
  flex: 0 0 auto;
}

.tree-row .tree-arrow.leaf { visibility: hidden; }
.tree-row label.checkbox-row {
  flex: 1 1 auto;
  min-width: 0;
  cursor: pointer;
  padding: 0;
}
.tree-row.has-children > label .row-label { font-weight: bold; }

/* "Ohne Ansprache" - the ones nobody has answered. At the top of the tag
   panel and ruled off from it, because it is an answer to the same
   question and not a tag in the vocabulary. */
.tree-row.untagged-row {
  margin-bottom: 4px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border);
}

.tree-row.untagged-row .row-label { font-style: italic; }

.checkbox-list {
  display: flex;
  flex-direction: column;
  font-size: 13px;
  max-height: 40vh;
  overflow-y: auto;
  min-width: 0;
}

.checkbox-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 2px 0;
  cursor: pointer;
}

/* **Der Name gibt nach, nicht der Balken** (22.09.2026). `nowrap` allein
   heisst: die Zeile wird so breit wie der laengste Pfad, und der Balken
   rechts davon wurde aus dem Panel hinausgeschoben - gemessen 69px
   daneben bei `Raubhaarwild:Hundeartig:Fuchs`. `min-width: 0` ist das,
   was einem Flex-Kind ueberhaupt erst erlaubt, schmaler zu werden als
   sein Inhalt; die Auslassung sagt dann, dass etwas fehlt, und der
   `title` an der Zeile sagt es ganz. */
.checkbox-row .row-label {
  font-family: monospace;
  white-space: nowrap;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* **Der Balken steht rechts** (22.09.2026, auf Wunsch). Er war dehnbar
   (`flex: 1 1 auto`) und lag damit in jeder Zeile woanders; jetzt ist er
   fest breit, und der Zwischenraum davor dehnt sich - also stehen alle
   Balken an derselben Kante, was ein Balkendiagramm überhaupt erst
   lesbar macht. `margin-left: auto` ist der Zwischenraum: eine leere
   Spanne dafür in den Baum zu hängen wäre ein Element je Zeile für
   nichts. */
.checkbox-row .row-bar-track {
  flex: 0 0 auto;
  margin-left: auto;
  width: 72px;
  height: 6px;
  background: var(--border);
  border-radius: 3px;
  overflow: hidden;
}

/* **Und er trägt die Farbe seiner Ansprache**, wo eine gesetzt ist
   (`--tag-color`, aus `tagColour` - dieselbe Farbe wie im Rad und auf
   der Kachel). Wo keine gesetzt ist - die Zeilen der anderen Panels -
   bleibt es bei der Akzentfarbe, die hier immer stand. */
.checkbox-row .row-bar-fill {
  display: block;
  height: 100%;
  background: var(--tag-color, var(--bar-color));
}

.checkbox-row .count-label {
  color: var(--muted-fg);
  font-size: 11px;
  white-space: nowrap;
}

/* What is left of the line above the grid. It said "412 photos" whether
   or not that was news; now it only speaks when it has something to say -
   while more are still coming, or when nothing matched. The heading with
   the count that sat under it went with the grouping.

   It used to carry 72px of top padding, which was the room the filter
   buttons needed when they were pinned to the top of the page. They are
   in the bottom corners now, so the grid starts where the page does. */
.status {
  padding: 0 0 8px 0;
  color: var(--muted-fg);
  font-size: 13px;
}

.status:empty { padding: 0; }

/* ---- Nothing to show ----

   Centred on the window, because in this one state the message *is* the
   page: there is no grid behind it to be a caption for. It used to be a
   line of grey 13px text in the top left, above nothing, which read as a
   page that had failed to load rather than as an archive with nothing in
   it yet.

   Fixed rather than centred inside the container, whose height at that
   moment is zero - centring in it would put this at the top of the
   screen, which is where it already was. */
.gallery-empty {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 700;   /* under every control, over nothing */
  width: min(420px, calc(100vw - 48px));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 28px 24px;
  text-align: center;
  color: var(--muted-fg);
  pointer-events: none;   /* except the buttons, below */
}

.gallery-empty[hidden] { display: none; }

/* The same panel where the message belongs to a list rather than to the
   window: built by `buildEmptyPanel` in nav.js and dropped into the flow
   where the cards would have been. Not fixed, because there is a page
   around it that has not gone anywhere. */
.gallery-empty.inline {
  position: static;
  transform: none;
  margin: 24px auto;
  pointer-events: auto;
}

/* Waiting for the first page. The same panel in the same place as
   "there is nothing here", because it is the same shape of state and a
   message that jumps across the screen when the answer arrives is two
   messages. "Lädt…" used to be a word in the top left over a blank
   screen, which reads as a page that gave up.

   The mark becomes one of the four animals - the same ones that wait for
   a picture in the lightbox. A turning ring here beside a Reh over there
   was two answers to one question. */
.gallery-empty.waiting .gallery-empty-mark { opacity: 1; }
.gallery-empty.waiting .gallery-empty-text { display: none; }

/* Quiet. It is a mark saying which of the three empty states this is -
   a pin, a filter, a picture frame - not an illustration. */
.gallery-empty-mark {
  display: flex;
  opacity: 0.55;
}

.gallery-empty-text {
  margin: 0;
  font-size: 15px;
  line-height: 1.5;
  color: var(--fg);
  max-width: 34ch;
}

/* The way out of the state. A message that names the thing narrowing the
   archive to nothing and then leaves you to go and find the control is
   half an answer. */
.gallery-empty-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  pointer-events: auto;
}

/* The same orange as `clear all`, the count bubbles and the ribbon's ×.
   Every other way to drop a filter in this app wears it, and this is a
   way to drop a filter - it was the one that looked like an ordinary
   button and so read as a suggestion rather than as the undo. */
.gallery-empty-actions button {
  padding: 7px 16px;
  font-size: 13px;
  border-radius: 20px;
  background: #d98324;
  border: 1px solid #d98324;
  color: #fff;
  cursor: pointer;
}

.gallery-empty-actions button:hover { background: #b3651b; border-color: #b3651b; }

/* ---- Die zwei Abstandhalter ----

   (23.09.2026.) Über dem Geladenen so viele Zeilen, wie davor liegen,
   darunter so viele, wie danach kommen (`zeichneRand` in app.js). Über die
   ganze Breite des Rasters; Höhe und das Muster aus leeren Zellen setzt
   das Skript, weil beides an der Grösse einer Kachel hängt.

   Bis zum Mittag stand hier ein Platzhalter je Anblick - dasselbe Bild,
   aber Tausende Elemente, die das Raster bei jedem Umbruch mitlegte. */
.raster-rand {
  grid-column: 1 / -1;
  background-repeat: repeat;
  background-position: 0 0;
}
.raster-rand[hidden] { display: none; }

/* The browser's own scrollbar, gone - on the gallery only, and only
   because that page draws one of its own in the same strip of screen.
   Two controls in the same 20px is one too many to grab at, and the one
   underneath answers a question ("how far through") that nobody asks of
   ten thousand photographs in date order.

   Both spellings, because they are two separate features rather than a
   prefix pair: `scrollbar-width` is the standard property (Firefox, and
   Chrome since 121) and `::-webkit-scrollbar` is what older Blink and
   WebKit take. The page still scrolls by every other means - wheel,
   finger, space bar, keyboard - since neither of these touches overflow.

   Scoped by the class on <html> in index.html: every other page keeps
   its scrollbar, because none of them has anything to put in its place.

   The grid gets the ~15px the bar used to take on a desktop. Nothing has
   to be told: `updateGridColumns` runs off a ResizeObserver on the
   container, which is what the width change is. */
/* **Und seit dem 05.09.2026 auch im Buch**, aus demselben Grund: es zieht
   dort denselben Streifen. Zwei Klassen statt einer allgemeinen Regel,
   damit jede Seite, die eine eigene Leiste bekommt, es ausdruecklich
   sagen muss - eine Seite ohne Ersatz darf ihren Rollbalken nicht
   verlieren. */
html.gallery-page,
html.buch-page { scrollbar-width: none; }
html.gallery-page::-webkit-scrollbar,
html.buch-page::-webkit-scrollbar { display: none; }

/* ---- The timeline ----

   A strip down the right-hand edge carrying the date of the row at the
   top of the screen. Out while the grid is moving, gone a second after it
   stops - it lies over the pictures, and the pictures are the page.

   Right at the edge, over the browser's own scrollbar where there is one:
   it is doing that job, with the answer this grid actually has (see the
   block comment in app.js). Between the two corners of buttons, because
   both of those are further out from the edge than this is and a control
   that ran under them would be grabbable in the middle only. */
/* **Die Leiste selbst gehoert keiner Seite** (05.09.2026, auf Wunsch:
   *„Buch page shall get the same time-scrollbar as Anblick page"*). Was
   sie *ist* - ein 26px breiter Streifen am rechten Rand, unsichtbar bis
   sie gebraucht wird - steht hier; *wo sie anfaengt und aufhoert* weiss
   nur die Seite, denn dort stehen die Ecken mit den Knoepfen. Also zwei
   Klassen an einem Element: `.zeitleiste` und daneben die der Seite. */
.zeitleiste {
  position: fixed;
  right: 0;
  z-index: 840;   /* under the FABs (850), under the open band (900) */
  width: 26px;
  opacity: 0;
  /* Gone means gone: a strip you cannot see must not be a strip that eats
     a press meant for the picture behind it. */
  pointer-events: none;
  transition: opacity 260ms ease;
  /* The drag is ours - without this the same finger scrolls the page as
     well and the grid runs away at twice the speed. */
  touch-action: none;
}

.zeitleiste[hidden] { display: none; }

.zeitleiste.visible {
  opacity: 1;
  pointer-events: auto;
}

.gallery-timeline {
  top: 76px;
  bottom: calc(var(--revier-band-height, 0px) + var(--gallery-fab-clearance, 72px) + 16px);
}

/* In multi-select the top-right corner grows a second row of buttons, so
   the strip starts under that instead. */
.gallery-timeline.low { top: 140px; }

/* **Im Buch** liegt oben der Kopf mit Titel und Zeile, unten die Ecke mit
   den Knoepfen und darunter das Band. Weiter oben als in der Galerie,
   weil dort eine zweite Knopfreihe stehen kann und hier nicht. */
.buch-timeline {
  top: 64px;
  bottom: calc(var(--revier-band-height, 0px) + 96px);
}

/* The line the marks sit on. Faint: it is a scale, and a scale that is as
   dark as the thing sliding along it is two things sliding. */
.timeline-marks {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 9px;
  width: 2px;
  border-radius: 1px;
  background: rgba(128,128,128,0.28);
}

/* Where the day changes - or the month, when there are too many days to
   draw, or the hour under the other sort. */
.timeline-mark {
  position: absolute;
  right: -3px;
  width: 8px;
  height: 2px;
  border-radius: 1px;
  background: var(--muted-fg);
  opacity: 0.55;
}

/* The grip, with the reading on it. The bubble hangs to the *left* of the
   strip rather than sitting in it: 26px is a grip, not a date. */
.timeline-thumb {
  position: absolute;
  top: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  width: 100%;
  height: 34px;
  padding-right: 4px;
}

.timeline-thumb::after {
  content: "";
  width: 12px;
  height: 34px;
  border-radius: 6px;
  background: #d98324;
  box-shadow: 0 1px 6px rgba(0,0,0,0.35);
}

.timeline-label {
  position: absolute;
  right: 22px;
  padding: 5px 11px;
  border-radius: 14px;
  background: var(--control-bg);
  border: 1px solid var(--border);
  box-shadow: 0 4px 14px rgba(0,0,0,0.3);
  color: var(--fg);
  font-size: 13px;
  font-weight: bold;
  white-space: nowrap;
  /* It is a reading, not a target: the whole strip is the target. */
  pointer-events: none;
}

/* Being held. The grip fattens under the finger, which is the only
   feedback a control with no travel of its own can give. */
.zeitleiste.dragging .timeline-thumb::after {
  width: 16px;
  background: #d98324;
}

.zeitleiste.dragging .timeline-label {
  font-size: 15px;
  padding: 7px 14px;
}

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

/* On a phone the bottom corner carries the nav bar as well as the band
   and the FABs - the same arithmetic as .gallery-fabs above. */
@media (max-width: 700px) {
  .gallery-timeline {
    bottom: calc(56px + var(--revier-band-height, 0px) + var(--gallery-fab-clearance, 72px) + 16px);
  }
  /* Dieselbe Rechnung, nur ohne die Knopfecke der Galerie. */
  .buch-timeline {
    bottom: calc(56px + var(--revier-band-height, 0px) + 96px);
  }
}

/* Edge to edge. `body` keeps a 20px margin for everything that is read
   rather than looked at; a wall of thumbnails is not one of those, and
   two strips of background down the sides of it are just less picture.
   The bottom padding is the height of the tallest FAB corner - the
   column of three on the left - plus its gap. A floating control covers
   thumbnails while you scroll, which is what floating controls do; what
   it must not do is cover the *last* row, because there is then nothing
   left to scroll and no way to see underneath it. The space itself is
   never empty: it is exactly where the buttons are. */
#galleryContainer {
  margin: 0 -20px;
  padding-bottom: 180px;
  /* **Room to scroll the top row out from under the buttons** - and none
     of it until the page is scrolled at all.

     `padding-top` is written by applyGridTopRoom() in app.js, not here:
     it starts at 0, so the page opens with the grid at the top edge
     exactly as it always has, and appears on the first scroll with the
     scroll position corrected by the same amount in the same breath, so
     nothing appears to move. In multi-select it grows, because the
     left-hand column of buttons is taller than the row at rest.

     The alternative was pushing the grid down permanently, which spends
     the same space on every screen forever to solve a problem you have
     for one row. The one before that was moving the buttons, which is
     three obstacles instead of one. */
}

/* Grid, not flex, and the column *count* comes from JS
   (updateGridColumns): equal `1fr` columns divide the row exactly, so
   there's no leftover strip on the right. Getting the same result by
   computing a pixel width per card would leave a sub-pixel gap.
   --grid-columns is set on #galleryContainer and inherits. */
.gallery {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns, 4), 1fr);
  gap: 6px;
}

/* **`display` schlaegt `[hidden]`** - dieselbe Falle, die der
   Fortsetzen-Knopf schon einmal gestellt hat (siehe seinen Kommentar in
   index.html). Die Regel darueber gewinnt gegen das `display: none` des
   Browsers, also war ein zugeklappter Ordner im DOM zu und auf dem
   Schirm offen. Aufgefallen ist es nur im Bild: die Zusicherung
   `.gallery:not([hidden])` war die ganze Zeit gruen. */
.gallery[hidden] { display: none; }

/* ---- A tile: one Anblick ----

   The grid was one card per photograph, and before that a run of
   photographs under a heading per burst. The heading cost more vertical
   space than the pictures under it; the flat run lost every trace of what
   belonged with what. The tile is the third answer: the burst is one
   object on the page, up to four pictures wide.

   No map on it, deliberately - where it was is the Revier band's job and
   the lightbox's circle, and a hundred little maps would be a hundred
   Leaflet instances all saying what the pin says. */
.tile {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;   /* let a grid item shrink below its image's width */
  background: var(--card-bg);
  border-radius: 4px;
  overflow: hidden;
  /* Shift-clicking a range would otherwise drag a text selection across
     the grid. */
  user-select: none;
  -webkit-user-select: none;
}

/* Only a *pointer* in multi-select, where the whole tile is the target.
   Otherwise the pictures inside it are the things you press, and a hand
   cursor over the caption would promise something the caption doesn't do. */
.tile.selectable { cursor: pointer; }

.tile.selected { outline: 3px solid var(--bar-color); outline-offset: -3px; }

/* ---- Der Cursor (04.09.2026, auf Wunsch) ----

   Wo man im Raster *steht*. Die Pfeiltasten fahren ihn, Enter macht auf,
   und der Leuchtkasten zieht ihn beim Blättern mit - siehe den Kasten über
   `setzeCursor` in app.js.

   **Ein Ring aussen, kein Rahmen innen**, und das ist der Unterschied zur
   Auswahl: die trägt `outline` nach innen (ein Rahmen *um* das Bild), der
   Cursor liegt im Abstand zwischen den Kacheln. So können beide auf
   derselben Kachel liegen und bleiben zwei Aussagen - „diese ist
   angekreuzt" und „hier stehe ich". Ein zweites `outline` gäbe es nicht:
   ein Element hat genau eines.

   6px Abstand hat das Raster (`.gallery`), der Ring nimmt drei davon.
   `z-index`, damit er über den Nachbarn liegt und nicht halb unter
   ihnen. */
.tile.cursor {
  z-index: 1;
  box-shadow: 0 0 0 3px var(--bar-color);
}

/* **Und er blendet ein, wo der Leuchtkasten einen zurücklässt** (auf
   Wunsch: *„scroll / fade into it when the anblick is closed"*). Nach
   zehn Anblicken im Betrachter ist die Kachel, in die er zurückschrumpft,
   eine unter sechzig; der Ring, der von aussen hereinfährt, ist das, was
   sie danach noch einen Augenblick lang kenntlich hält. */
@keyframes kachel-cursor-herein {
  from {
    box-shadow: 0 0 0 11px color-mix(in srgb, var(--bar-color) 18%, transparent);
  }
  to {
    box-shadow: 0 0 0 3px var(--bar-color);
  }
}

.tile.cursor-frisch { animation: kachel-cursor-herein 380ms ease-out; }

/* Wer weniger Bewegung möchte, bekommt den Ring - der ist die Auskunft,
   die Einblendung ist nur, wie er ankommt. */
@media (prefers-reduced-motion: reduce) {
  .tile.cursor-frisch { animation: none; }
}

/* The four layouts. One picture fills the tile, two split it, four make a
   square - and three is one large with two stacked beside it, because
   three equal strips read as a mistake rather than as a decision.

   The block keeps the photos' own 220x180 whatever is inside it, so a row
   of tiles is a row and not a staircase. */
.tile-media {
  /* The tag strip hangs off the *pictures*, not off the tile: a tile
     with a note under its photos would otherwise put the Ansprache over
     the note instead of over the last frame. */
  position: relative;
  display: grid;
  gap: 2px;
  aspect-ratio: 220 / 180;
  line-height: 0;
  /* **The ratio wins over the content.** `aspect-ratio` sets a preferred
     height, and a box whose automatic minimum is its content will still
     grow past it - which is exactly what a note does and a photograph
     never did, so it only showed up once text moved inside this block.
     Without this, a card with a long note is a few pixels taller than
     the ones beside it and the row follows it. */
  min-height: 0;
}

.tile-media.faces-1 { grid-template-columns: 1fr; }
.tile-media.faces-2 { grid-template-columns: 1fr 1fr; }
.tile-media.faces-4 { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }

/* The first face spans both rows on the left; the other two stack. */
.tile-media.faces-3 {
  grid-template-columns: 1.6fr 1fr;
  grid-template-rows: 1fr 1fr;
}
.tile-media.faces-3 > :first-child { grid-row: span 2; }

/* ---- With a note on it ----

   **The note is a face, and it takes the top half.** Quarters below for
   what else there is: one picture fills that half, two split it. The box
   is the same box - a note may not make a card taller, or a row is as
   tall as the wordiest thing in it and the grid becomes a staircase.

   Written as the two-by-two the other layouts already use, so the seam
   between a card with a note and one without is 2px of gap in the same
   place. */
/* `minmax(0, 1fr)` and not `1fr`: a `1fr` track's automatic minimum is
   the min-content of what is in it, and prose has one - so a long note
   pushed its own row taller than half the box and the card grew by a
   line. Pictures never showed this up, having no minimum worth the name.
   The box is fixed and the text is what gives way, which is what
   `overflow: hidden` on the note is for. */
.tile-media.with-note {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
}

.tile-media.with-note > .tile-note { grid-column: span 2; }

/* Nothing under it: the note is the whole card. */
.tile-media.with-note.media-0 > .tile-note { grid-row: span 2; }

/* One picture under it, across the width - half a card split again would
   be a 55px sliver with a leg in it. */
.tile-media.with-note.media-1 > .tile-shot { grid-column: span 2; }

/* **Drei darunter** (04.09.2026). Seit die Flächen je *Art* stehen und
   nicht je Rahmen, sind es höchstens drei - Bild, Video, Ton -, und mit
   einer Notiz darüber teilen sich die drei die untere Hälfte. Enger als
   zwei, aber es sind drei verschiedene Dinge und nicht dreimal dasselbe:
   was hier verlorenginge, wäre die Auskunft „da liegt auch ein Video
   drin", und die ist der Grund, warum die Fläche existiert. */
.tile-media.with-note.media-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.tile-media.with-note.media-3 > .tile-note { grid-column: span 3; }

.tile-shot {
  position: relative;
  padding: 0;
  border: none;
  background: var(--img-placeholder);
  cursor: pointer;
  overflow: hidden;
  min-width: 0;
  min-height: 0;
}

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

/* In multi-select the tile takes the click, so a picture must not
   swallow it - the mode changes what a tap *means*, and it would be a
   poor bargain if four fifths of the tile disagreed. Gilt für jede Art
   Gesicht, nicht nur für das Bild: die Notiz und der Ton sind an dieser
   Stelle dasselbe. */
.tile.selectable .tile-shot,
.tile.selectable .tile-note,
.tile.selectable .tile-ton { pointer-events: none; }

/* A video has no player yet, so its chosen still says what it is rather
   than pretending to be a photograph. */
/* **A sound on a tile.** No thumbnail exists for one (ingest.py writes
   none), so the face is drawn: a wave over the card's own background, with
   the length under it. It takes a picture's place in the layout - a tile
   with a photograph and a recording in it is two faces, not one face and
   a hole. */
.tile-ton {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: color-mix(in srgb, var(--bar-color) 14%, var(--card-bg));
  color: var(--fg);
  border: none;
  cursor: pointer;
}

/* **Die Welle in der Mitte ist fort** (04.09.2026, auf Wunsch: *„sound
   shall show like text (without thumb) with the player icon top left in
   the tilepart as video has right now"*). Was bleibt, ist die Fläche in
   der Farbe der Notiz - kein Standbild, denn es gibt keines - und darauf
   die Länge; das Zeichen sitzt in der Ecke, in derselben Scheibe wie beim
   Video. Auf dunklem Grund wäre die Scheibe unsichtbar, hier liegt sie
   auf einer hellen Fläche - also bekommt sie dort einen Rand. */
.tile-ton .tile-video {
  background: rgba(0, 0, 0, 0.62);
  border: 1px solid rgba(255, 255, 255, 0.25);
}

.tile-ton-len {
  font-size: 11px;
  color: var(--muted-fg);
  font-variant-numeric: tabular-nums;
}

/* In the strip the same drawing, at the size of a thumbnail. */
.strip-shot.is-ton {
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
}

/* ---- Wie viele wovon, an der Fläche (04.09.2026) ----

   Dieselbe Auskunft in derselben Form wie am Chat (`.tile-note-mehr`):
   eine Zahl und das Zeichen ihrer Art, unten rechts in der Fläche. Was
   sich unterscheidet, ist der Grund darunter - eine Fotografie statt einer
   hellen Notizfläche -, also trägt sie hier ihre eigene dunkle Scheibe
   statt eines Verlaufs.

   `pointer-events: none`, damit sie den Druck auf die Fläche nicht
   abfängt: sie ist eine Auskunft, kein Knopf. */
.tile-mehr {
  position: absolute;
  right: 4px;
  bottom: 4px;
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 2px 6px;
  border-radius: 9px;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  pointer-events: none;
}

/* Auf der hellen Tonfläche steht sie in der Farbe des Textes und ohne die
   Scheibe - dort gibt es nichts, wogegen sie sich behaupten müsste. */
.tile-ton .tile-mehr {
  background: rgba(0, 0, 0, 0.12);
  color: var(--fg);
}

.tile-video {
  position: absolute;
  left: 4px;
  top: 4px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 10px;
  line-height: 20px;
  text-align: center;
}

/* ---- The note, as one of the faces ----

   It was two rules for one thing: `.tile-text` for a tile with no
   pictures (the photos' own ratio, so a mixed row stayed a row) and
   `.tile-text.under` for a note under some (a couple of clamped lines).
   The second made the card taller than its neighbours, which is what this
   replaced: the note is a face inside the same box now, and the box never
   changes size.

   **Clipped by the box, not by a line count.** `overflow: hidden` and
   nothing else - how much fits depends on how big the cards are, which is
   a setting, and a fixed clamp would leave white space on a big card and
   cut a small one twice. No ellipsis: the whole note is one press away,
   and "…" promises a fold-out this grid does not have. */
.tile-note {
  /* Der Zaehler unten rechts haengt daran (siehe `.tile-note-mehr`). */
  position: relative;
  /* **Dieselbe Hand wie über einem Bild** (2026-08-19). Eine Notiz auf
     einer Kachel öffnet den Lightbox genau wie ein Foto, ein Video oder
     eine Tonaufnahme - sie sah nur nicht danach aus, weil sie als
     einzige der vier keinen Zeiger trug. Was sich anklicken lässt, sagt
     das auch. */
  cursor: pointer;
  padding: 8px 10px;
  font-size: 13px;
  line-height: 1.4;
  color: var(--fg);
  background: color-mix(in srgb, var(--bar-color) 12%, var(--card-bg));
  border-left: 3px solid var(--bar-color);
  overflow: hidden;
  min-width: 0;
  min-height: 0;
  overflow-wrap: anywhere;
  text-align: left;
}

/* **The whole markdown vocabulary, at card size.** The note is rendered
   on the tile rather than flattened, so every shape it can take has to
   fit inside a face that may be 90px tall: headings step up only a
   little, margins are hairlines, and a tick box is 11px. What is being
   read here is the *shape* - a heading, three ticked things, a quote -
   and the words are a bonus at this size. */
.tile-note h1 { font-size: 1.15em; margin: 0 0 2px; font-weight: bold; }
.tile-note h2 { font-size: 1.08em; margin: 0 0 2px; font-weight: bold; }
.tile-note h3 { font-size: 1em; margin: 0 0 2px; font-weight: bold; }
.tile-note p { margin: 0 0 2px; }
.tile-note a { color: var(--bar-color); text-decoration: none; }
.tile-note code {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 0.9em;
  background: rgba(127, 127, 127, 0.18);
  border-radius: 2px;
  padding: 0 2px;
}
.tile-note blockquote {
  margin: 0 0 2px;
  padding-left: 6px;
  border-left: 2px solid var(--bar-color);
  opacity: 0.85;
}
.tile-note hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: 3px 0;
}
.tile-note .md-bullet,
.tile-note .md-task { gap: 5px; margin: 0 0 2px; }
.tile-note .md-gap { height: 0.35em; }
.tile-note .md-box {
  width: 11px;
  height: 11px;
  margin-top: 2px;
  border-width: 1.2px;
  font-size: 9px;
  line-height: 9px;
}

/* The whole card. The tag strip lies over its foot, so the text stops
   short of it - centred prose running under the answers would be prose
   you cannot read. */
.tile-note.alone {
  padding-bottom: 32px;
}

/* Nothing was written and nothing was seen: the one line that says so,
   quietly. Centred, because it is a label rather than prose. */
.tile-note.empty {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted-fg);
  font-size: 12px;
}

/* Not on the contact sheet, where a card is 55px and prose in it is a
   grey smear. The pictures are what that size is for; the tag strip is
   already dropped there for the same reason. A note-only Anblick keeps
   its text - dropping that would leave an empty square. */
#galleryContainer[data-size="mini"] .tile-media.with-note > .tile-note:not(.alone) {
  display: none;
}
#galleryContainer[data-size="mini"] .tile-media.with-note.media-1 > .tile-shot,
#galleryContainer[data-size="mini"] .tile-media.with-note.media-2 > .tile-shot {
  grid-row: span 2;
}

/* ---- What it was, along the bottom of the tile ----

   The foot used to be a strip *under* the pictures holding three things:
   what it was, when, and where. Two of those are gone from the tile
   altogether.

   **The time and the place are not what you scan a grid for.** You scan
   it for the animal. The date is on the filter you arrived through, in
   the lightbox, and in the Anblick's own window; the place is the Revier
   band's whole job, and it was repeating what the band already said on
   every one of sixty tiles. A caption line that is right twice and
   redundant once costs a row of thumbnails per screen.

   **Inside the tile, not under it.** Over the picture the strip costs no
   height at all, and a grid of tiles that are exactly as tall as their
   pictures is a grid you can read down. The old argument against this
   was that a caption burnt into a photograph is unreadable on a bright
   one - which is what the scrim is for, and a two-word Ansprache in
   white on a gradient is a different thing from a full meta line. */
.tile-tags {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 4px;
  padding: 18px 6px 5px;
  /* The gradient is the readability, not a bar: it fades into the
     picture rather than cutting a strip off the bottom of it. */
  background: linear-gradient(to top, rgba(0,0,0,0.72), rgba(0,0,0,0));
  /* A marker, not a target. The tile takes the click - in multi-select
     that is the whole point, and outside it the picture underneath is
     what you meant to press. Filtering by a tag is the lightbox's job,
     where there is one Anblick on screen and no ambiguity about it. */
  pointer-events: none;
  /* A burst of a dozen Ansprachen must not paper over the picture. */
  max-height: 60%;
  overflow: hidden;
}

.tile-tag {
  padding: 1px 6px;
  border-radius: 9px;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 11px;
  line-height: 16px;
  font-weight: bold;
  white-space: nowrap;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Nothing was made of it yet: no strip, no scrim, no words. It used to
   say "ohne Ansprache", and on a real archive that is the same three
   words down every tile in the grid - wallpaper rather than a way of
   finding them. The Anblick filter finds them. */
.tile-tags.empty { display: none; }

/* **Am kleinsten Schritt das Zeichen ohne das Wort** (06.09.2026, auf
   Wunsch: *„have ansprech icons also in the smallest view in the gallery
   cards"*).

   Hier stand `display: none`, mit dem Argument, ein Chip sei auf einer
   100px-Kachel ein Viertel davon. Das gilt fuer den Chip *mit Wort* - und
   das Wort ist auch das, was den Platz kostet. Das Zeichen allein ist
   gerade, wofuer man so weit herunterkneift: eine Wand aus
   Vorschaubildern nach einer Farbe absuchen geht ohne Schrift.

   Der Verlauf darunter wird flacher mit: er ist dazu da, ein 26px-Wort
   lesbar zu machen, und ueber einem 16px-Zeichen waere er ein Schatten,
   der mehr verdeckt als er traegt. */
#galleryContainer[data-size="mini"] .tile-tags {
  padding: 3px 3px 2px;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0));
}

#galleryContainer[data-size="mini"] .tile-tags.on-text { background: none; }

/* Das Zeichen auf 16px. **Breite und Hoehe direkt**, nicht ueber
   `--tag-size`: die Zahl schreibt `buildTagFab` als Inline-Stil, und
   gegen einen Inline-Stil kommt eine Regel aus dem Stylesheet nicht an.
   `width` und `height` stehen dagegen in der Klasse (`var(--tag-size)`),
   und dort gewinnt die spezifischere Regel - ohne `!important` und ohne
   dass das Bauen etwas davon wissen muss. */
#galleryContainer[data-size="mini"] .tile-tags .tag-fab {
  width: 16px;
  height: 16px;
  border-radius: 4px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

/* Und der Ueberlappungsversatz der gestapelten Zeichen mit ihnen: die
   -14px sind auf 34px gerechnet und wuerden ein 16px-Zeichen fast ganz
   verdecken. */
#galleryContainer[data-size="mini"] .tile-tags .tag-fab-row.stacked > * {
  margin-left: -6px;
}
#galleryContainer[data-size="mini"] .tile-tags .tag-fab-row.stacked > :first-child {
  margin-left: 0;
}

/* **Ausser in der Schriftfassung.** Wer die Ansprachen als *Wort* stellt
   (`storedTagStyle`), bekommt am kleinsten Schritt weiter nichts: ein
   Wort auf 16px ist keines mehr, und das Wort war der ganze Grund, warum
   der Streifen hier einmal wegfiel. Das Zeichen hat das Problem nicht -
   es ist eine Farbe und eine Form, und beides traegt auch klein. */
#galleryContainer[data-size="mini"] .tile-tags:has(.tag-fab.as-text) {
  display: none;
}

/* On a text tile there is no photograph to darken, and a black gradient
   over the tinted block reads as a shadow someone forgot to remove. The
   chips carry themselves there. */
.tile-tags.on-text {
  background: none;
  padding: 6px;
}

/* Top-left, out of the pictures' way. A marker, not a target - the whole
   tile takes the click in multi-select, so this must not swallow it. */
.tile-select {
  position: absolute;
  top: 6px;
  left: 6px;
  width: 20px;
  height: 20px;
  border-radius: 3px;
  border: 2px solid #fff;
  background: rgba(0, 0, 0, 0.45);
  color: #fff;
  font-size: 13px;
  line-height: 16px;
  text-align: center;
  pointer-events: none;
  box-shadow: 0 1px 3px rgba(0,0,0,0.6);
}

.tile.selected .tile-select { background: var(--bar-color); }

/* `.tile-place` - a pin in the top right saying "this one has
   coordinates" - is gone. It was on nearly every tile at once on an
   archive fed by cameras that stand somewhere known, and a mark that is
   almost always there says nothing when it is. The corner it held is the
   Gesellschaft label's now (`.tile-revier`). See buildTile. */

/* ---- What you can do to what is selected ----

   Three discs in the **top left**, which is the one corner this page
   otherwise leaves empty, plus the delete under the tick on the opposite
   side.

   This was a bar across the bottom of the window: six text buttons in a
   row, which on a phone wrapped to two lines of chrome over the last row
   of thumbnails - in the same strip of screen the Revier band and the `+`
   already had. The actions are unchanged. What changed is that they are
   discs in an empty corner, and that the two that cannot be taken back
   (Löschen, and Zusammenfassen) are as far apart as the window allows.

   Clear of the nav rail, which is 64px down the left on a desktop. On a
   phone the nav is a bar along the bottom and the corner is free. */
/* A group again. They were briefly spread down the whole left edge so
   that fewer tiles sat under them - which traded one obstacle for three,
   in three different places, and made a mode's worth of buttons read as
   unrelated. What actually buys the tiles their room is the headroom
   above the grid (see #galleryContainer's padding-top): you can scroll
   the top row out from under this corner instead of the corner being
   moved out from over it.

   `pointer-events: none` on the container stays: only the discs
   themselves should ever take a tap, never the gaps between them. */
.selection-fabs {
  position: fixed;
  top: 16px;
  left: 80px;
  z-index: 850;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  pointer-events: none;
}

.selection-fabs > * { pointer-events: auto; }

.selection-fabs[hidden] { display: none; }

/* The one destructive action, marked as such wherever it is. */
.list-fab.danger {
  background: #7a2020;
  border: 1px solid #a33;
  color: #fff;
}

.list-fab.danger:hover { background: #a33; }

@media (max-width: 700px) {
  .selection-fabs { left: 16px; }
}

/* ---- and the dialog that writes one ---- */

.sichtung-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 8px 0 4px 0;
}

.sichtung-tag {
  padding: 3px 8px;
  border-radius: 12px;
  border: none;
  background: var(--bar-color);
  color: #fff;
  font-size: 12px;
  cursor: pointer;
}

.sichtung-tag:hover { filter: brightness(1.1); }

.sichtung-note {
  width: 100%;
  margin-top: 10px;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--control-bg);
  color: var(--fg);
  font-family: inherit;
  font-size: 13px;
  resize: vertical;
}

/* ---- The drop field in the Anblick window ----

   Where the upload FAB went. A button on the page that opened a file
   picker and a button beside it that opened a window were two answers to
   one question - "put something in the archive" - and which you wanted
   depended only on whether a camera had been involved. Now the window
   takes words and files both.

   Dashed, because a dashed rectangle is what a drop target has looked
   like for twenty years and this is not the place to be original. */
.upload-field { margin-top: 12px; }

.upload-drop {
  padding: 12px;
  border: 2px dashed var(--border);
  border-radius: 8px;
  text-align: center;
}

/* Something is over it. The whole point of a drop target is that it says
   so before you let go. */
.upload-drop.over {
  border-color: var(--bar-color);
  background: color-mix(in srgb, var(--bar-color) 10%, transparent);
}

.upload-drop.busy { opacity: 0.7; }

.upload-drop-label {
  font-size: 12px;
  color: var(--muted-fg);
  line-height: 1.4;
}

.upload-drop-buttons {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-top: 8px;
}

.upload-drop-buttons button { font-size: 12px; }

/* What the clustering did, in the window it was asked for in rather than
   on the status line behind it. Empty until something has been dropped,
   so it takes no room in a window nobody is uploading through. */
.upload-status {
  margin-top: 8px;
  font-size: 12px;
  color: var(--fg);
  min-height: 1em;
}

/* The band's pick, which is both where these were seen and the scope the
   three-minute rule is applied within. Worth saying before the drop, not
   after: it is the difference between one Anblick and two. */
.upload-target {
  margin-bottom: 6px;
  font-size: 12px;
  color: var(--muted-fg);
}

/* Sort: two buttons sharing a border, each toggling its own direction.
   Floating, so they carry the same shadow as the other controls. */
.sort-toggle { display: flex; border-radius: 4px; }

.sort-toggle button {
  display: flex;
  align-items: center;
  gap: 5px;
  border-radius: 0;
  padding: 11px 14px;
}

.sort-toggle button + button { border-left: none; }
.sort-toggle button:first-child { border-radius: 4px 0 0 4px; }
.sort-toggle button:last-child { border-radius: 0 4px 4px 0; }

.sort-toggle button.active {
  background: var(--bar-color);
  color: #fff;
  border-color: var(--bar-color);
}

/* Dimmed on the inactive button: it's a hint at what you'd get, not a
   statement about the current order. */
.sort-arrow { font-size: 13px; line-height: 1; }
.sort-toggle button:not(.active) .sort-arrow { opacity: 0.45; }

/* **A band is kept clear at the foot of it.** The strip of the Anblick's
   other frames sits in the bottom of that band and the Ansprechen bar
   sits above the strip - both of them *outside* the photograph, which is
   the point: what you are looking at while deciding what an animal is, is
   the animal.

   Padding rather than a smaller `max-height` on the picture, because the
   picture is centred: shrinking it alone would have left the spare room
   split evenly above and below and put the picture's foot back where it
   was. The padding moves the whole centring up. It costs picture - about
   a tenth of the height on a laptop - and that is the price of the button
   not being on top of the photograph.

   Absolutely positioned children (the panel, the strip, the bar, the
   arrows) are laid against the *padding* box, so none of them move. */
.lightbox {
  display: none;
  position: fixed;
  z-index: 9999;
  inset: 0;
  /* **Zwei Baender, oben und unten** (28.08.2026). Beide werden von
     `placeAnsprechBar` gemessen und hier hineingeschrieben, weil beide
     wachsen koennen: oben mit einem Streifen, der da ist oder nicht,
     unten mit einer Antwortreihe, die umbricht. Die Ausweichwerte sind,
     was dabei mit einem Streifen und einer Zeile Antworten herauskommt,
     damit auch das erste Bild an der richtigen Stelle steht.

     **Das Polster ist beidseits das groessere der beiden** (28.08.2026,
     auf Wunsch: *„the lightbox media shall vertically always be centered
     like the left/right fabs"*).

     Ungleiches Polster heisst: das Bild sitzt mittig in dem, was uebrig
     bleibt, und das ist nicht die Mitte des Fensters. Die Pfeile stehen
     bei `top: 50%` und liegen - wie jedes absolut gesetzte Kind - am
     *Polster*-Kasten, also auf der echten Fenstermitte; bei 125px oben
     und 279px unten lagen sie damit 77px unter der Mitte des Bildes, an
     dem sie blaettern. Zwei Achsen fuer eine Sache, und die falsche war
     die des Bildes.

     Gleiches Polster ruecken beide auf dieselbe Achse, und dass es das
     *groessere* ist, ist die zweite Haelfte davon: das Bild bleibt so von
     beiden Baendern frei, ohne dass irgendwer nachrechnen muss.

     Es kostet Bildhoehe - so viel, wie die zwei Baender sich
     unterscheiden -, und das ist der Preis dafuer, dass Bild und Pfeile
     dasselbe meinen, wenn sie „Mitte" sagen. */
  --lightbox-band: max(var(--lightbox-ceiling, 116px), var(--lightbox-floor, 70px));
  padding-top: var(--lightbox-band);
  padding-bottom: var(--lightbox-band);
  background: rgba(0,0,0,0.9);
  justify-content: center;
  align-items: center;
  /* **Die zwei Katheten des Kartendreiecks** (24.09.2026, auf Wunsch:
     *„the map shall show as a triangle filling the bottom left corner (not
     too big)"*). Waagrecht am Fuss, senkrecht an der linken Kante; auf
     einem Telefon und auf einem flachen Fenster kleiner (Medienbloecke
     weiter unten). Bis dahin stand hier `--lightbox-map-height`, die Hoehe
     des Bandes ueber die ganze Breite. */
  --lightbox-dreieck-breite: min(30vw, 300px);
  --lightbox-dreieck-hoehe: min(28vh, 220px);
  /* Die runden Ecken der Karte, wenn sie der Grund ist (17.09.2026). So
     rund wie die Blasen des Chats darueber, damit auf einem Schirm nicht
     zwei Handschriften stehen. */
  --lightbox-karte-radius: 16px;
}

/* Ganz in das, was die beiden Baender uebrig lassen: das Polster oben und
   unten ist bereits abgezogen, also ist `100%` hier genau der freie
   Streifen und nicht mehr eine geratene Quote davon - und weil das
   Polster beidseits gleich ist, steht die Mitte dieses Streifens auf der
   Mitte des Fensters, wo auch die Pfeile stehen. */
.lightbox img {
  max-width: 95%;
  max-height: 100%;
  object-fit: contain;
  cursor: pointer;
  /* The pinch is ours, not the browser's - see enablePictureZoom in
     nav.js and the viewport comment in index.html. Without this the
     touch action is claimed before a pointermove is ever seen. */
  touch-action: none;
  transform-origin: center center;
  /* The browser's own picture-drag steals the pan on a desktop - see
     enablePictureZoom. `draggable = false` is the part that actually
     stops it; these two stop the selection highlight that came with it. */
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  /* Und die Blase, die iOS an jedem `<img>` von sich aus anbietet - der
     Absatz unter dieser Regel sagt, warum sie eine eigene Zeile braucht.
     Sie steht hier und nicht dort, weil die drei zusammengehoeren: wer
     eine davon anfasst, soll die anderen zwei sehen. */
  -webkit-touch-callout: none;
}

/* **Ein langer Druck auf das Bild ist keine Textauswahl** (05.09.2026,
   gemeldet: *„longpressing lightbox image shall not trigger
   textselection (at least on ios)"*).

   Auf iOS sind das zwei verschiedene Dinge, und `user-select: none`
   allein deckt nur das eine ab:

     - die **Auswahl**, die unter dem Finger aufblüht und die Lupe
       mitbringt - dagegen hilft `user-select`, aber unter Safari erst
       seit 16.4 ohne Präfix. Wer ein älteres iPhone in der Hand hat -
       und das ist im Revier die Regel und nicht die Ausnahme - braucht
       `-webkit-user-select`;
     - und die **Blase** über dem Bild (*Sichern*, *Kopieren*, *Teilen*),
       die iOS an jedem `<img>` von sich aus anbietet. Sie hört auf
       `user-select` überhaupt nicht, sondern nur auf
       `-webkit-touch-callout`.

   Beides nimmt dem Finger die Geste aus der Hand, ehe sie zu Ende ist -
   dasselbe Argument wie bei `[data-langdruck-verdrahtet]` weiter unten,
   nur hat das Bild gar keine eigene Geste: hier ist der lange Druck ein
   Halten, das nichts auslösen soll. Ein Rahmen, in dem beim Betrachten
   eines Fotos eine Blase aufgeht, ist ein Rahmen, den man nicht
   anfassen mag.

   **An den Medien, nicht am ganzen Leuchtkasten.** Der Chat darin, die
   Notiz, die Bildunterschrift sind Text und bleiben markierbar - wer
   eine Zeile aus einem Gespräch kopieren will, soll das können. */
.lightbox,
.picture-lightbox {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

/* **Am Rahmen und nicht nur am Bild**, und das ist der Punkt, an dem der
   erste Versuch danebenlag. Die Fotografie trug beides laengst: sie ist
   `[data-langdruck-verdrahtet]` (ein langer Druck darauf fragt
   *Ansprechen*), und die Regel dort unten schaltet Auswahl und Blase
   schon ab. Trotzdem blueht unter dem Daumen eine Auswahl auf - denn iOS
   sucht sich, wo der gedrueckte Knoten nichts hergibt, den naechsten
   Vorfahren, der etwas hergibt, und markiert *dessen* Text. Ein `none`
   am Kind ist damit keine Absage, sondern eine Weiterleitung.
   Abgeschaltet gehoert es an dem Kasten, in dem gesucht wird.

   **Und es kostet nichts.** Im Leuchtkasten ist heute genau ein Blatt
   markierbar - die Aufschrift des Knopfes *Ansprechen…*; alles andere
   steht ohnehin auf `none`, weil es eine Geste traegt. Was bleiben muss,
   ist die Zeile darunter. */
.lightbox input,
.lightbox textarea,
.lightbox [contenteditable="true"],
.picture-lightbox input,
.picture-lightbox textarea {
  user-select: text;
  -webkit-user-select: text;
}

/* Das Video und das Bild des zweiten Betrachters erben es jetzt vom
   Rahmen; die Blase bleibt hier stehen, weil sie an einem `<img>` und
   einem `<video>` haengt und nicht am Kasten darum. */
.lightbox-video,
.picture-lightbox-figure img {
  -webkit-touch-callout: none;
}

.lightbox img[hidden] { display: none; }

/* **Die Kreise der Ansprachen** (24.09.2026, auf Wunsch: *„in the display
   mode where all fabs are shown the ansprachen circles shall be drawn on
   the picture. when the associated tag is hovered the circle on the
   picture shall become bold"*; tags.md §19).

   Ein SVG genau über dem Kasten des Bildes, mit derselben Transformation
   (`enablePictureZoom`, `begleiter`) - so folgt es Zoom und Schieben ohne
   eine Rechnung je Bewegung. `--bild-massstab` teilt die Strichbreite,
   damit ein Kreis auf einem dreifach vergrösserten Bild nicht dreifach
   dick wird.

   Je Antwort drei Kreise übereinander: ein breiter dunkler Saum, ein
   heller Strich, und darauf der Strich in der Farbe der Option. Mit dem
   dunklen Saum allein verschwand ein dunkelgrünes Kitz auf einem
   Nachtbild (gesehen auf dem Prüfstand); der helle Strich trägt die Farbe
   auf jedem Grund. Ohne einen Filter, den nicht jeder Browser an einem
   SVG-Kind zeichnet.

   **Fängt nichts ab** (`pointer-events: none`): ein Druck auf die Stelle
   eines Kreises ist ein Druck aufs Bild. Weg auf *nur das Bild*, während
   das Rad offen ist (dann wandert das Bild unter die Nabe) und im
   Texteditor; über einem Film, einer Tonspur und lauter Worten zeichnet
   `zeichneAnsprechKreise` gar nichts. */
.lightbox-kreise {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
  overflow: visible;
  pointer-events: none;
  transform-origin: center center;
}
.lightbox-kreise[hidden],
.lightbox.chrome-hidden .lightbox-kreise,
.lightbox.waehlt .lightbox-kreise,
.lightbox.text-edit .lightbox-kreise,
.lightbox.ohne-bild .lightbox-kreise { display: none; }
.lightbox-kreise circle {
  fill: none;
  transition: stroke-width 120ms ease-out;
}
.lightbox-kreise g { opacity: 0.85; transition: opacity 120ms linear; }
.lightbox-kreise g.fett { opacity: 1; }
.lightbox-kreise .kreis-schatten {
  stroke: rgba(0, 0, 0, 0.5);
  stroke-width: calc(5px / var(--bild-massstab, 1));
}
.lightbox-kreise .kreis-licht {
  stroke: rgba(255, 255, 255, 0.8);
  stroke-width: calc(3.5px / var(--bild-massstab, 1));
}
.lightbox-kreise .kreis-farbe {
  stroke: var(--kreis-farbe, #fff);
  stroke-width: calc(2px / var(--bild-massstab, 1));
}
.lightbox-kreise .fett .kreis-schatten { stroke-width: calc(9px / var(--bild-massstab, 1)); }
.lightbox-kreise .fett .kreis-licht { stroke-width: calc(7px / var(--bild-massstab, 1)); }
.lightbox-kreise .fett .kreis-farbe { stroke-width: calc(5px / var(--bild-massstab, 1)); }
@media (prefers-reduced-motion: reduce) {
  .lightbox-kreise circle,
  .lightbox-kreise g { transition: none; }
}

/* **Das vorige Bild, während das nächste kommt** (28.08.2026, auf
   Wunsch). 70%, und daneben läuft das Tier in der Ecke: zusammen ist das
   ein Rahmen, der sichtbar noch nicht fertig ist. Ein *volles* voriges
   Bild wäre die alte Falle - alles daneben spricht schon von einem
   anderen Tier, und nichts sagte es.

   Der Übergang liegt auf `opacity` und nicht auf dem Umhängen: das Neue
   ist aus dem Cache in derselben Bildwiederholung da, und ohne die
   Blende wäre der Wechsel ein Sprung von blass auf voll. */
.lightbox img { transition: opacity 120ms linear; }
.lightbox img.verblasst { opacity: 0.7; }

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

/* Magnified: the picture is being panned, so it must not also be the
   thing that takes a tap for the chrome.

   The arrows and the × **stay**. They were hidden here on the theory that
   a magnified picture is a picture you are working on and the furniture
   is in the way - but the way out of a zoom you did not mean is the ×,
   and a viewer that removes its own exit at the moment you are most
   likely to want it is a trap. Tapping the picture still takes the whole
   lot away, which is the deliberate version of the same thing. */
.lightbox.zoomed img { cursor: grab; }

/* The Reviereinrichtungen viewer, same gesture and the same reasons. */
.picture-lightbox-figure { touch-action: none; }
.picture-lightbox-figure.zoomed img { cursor: grab; }

/* Two fingers on the grid step the thumbnail size, so the browser must
   not take the gesture for a scroll first. `pan-y` keeps one-finger
   scrolling, which is the only thing a single pointer means here. */
#galleryContainer { touch-action: pan-y; }

/* ---- Waiting for the picture ----

   The viewer used to leave the *previous* photograph up until the next
   one had decoded. On a fast connection you never see it; on a slow one
   you page forward, look at what is on the screen, and are looking at
   the wrong Anblick with nothing saying so. An empty frame that says it
   is loading is worse-looking and strictly more honest, and this is an
   archive - being wrong about which animal you are looking at is the one
   failure it cannot have.

   So: nothing, and a Reh. It runs a few bounds and verhofft - stops
   dead, head up, listening - then runs on, which is what they actually
   do and what makes it a Reh rather than a rotating arc. It stays on the
   spot and the ground moves under it, because a spinner that wandered
   out of the frame would be a different kind of wrong. */
.lightbox-loader { color: rgba(255, 255, 255, 0.75); }
.lightbox-loader[hidden] { display: none; }
.lightbox-loader .creature-loader-text { color: rgba(255, 255, 255, 0.75); }

/* ---- Und beim Blättern: klein, in der Ecke (28.08.2026) ----

   Gewünscht: *„Loading animation is off at the moment between lightbox
   swipes. It shall show small on the bottom right corner (Color
   inverted, since it is a dark background)"*.

   Beim Öffnen bleibt das Tier groß und in der Mitte — dort ist der Schirm
   leer und der Sprung vom Raster auf ein volles Bild ist ein Warten, das
   ein Zeichen verdient. Beim Blättern ist das Gegenteil richtig: man
   schaut schon hin, und was hier gebraucht wird, ist eine Auskunft am
   Rand statt eines Auftritts in der Mitte.

   **Unten rechts, über dem Streifen.** `--lightbox-floor` ist die Oberkante
   des Bandes am Fuß des Betrachters (Ansprech-Leiste plus Streifen), von
   `placeAnsprechBar` geschrieben — dieselbe Rechnung, die schon
   `.lightbox-frame-actions` benutzt, damit beides über dem Streifen
   bleibt, wie hoch die Antworten die Reihe auch machen.

   **Hell auf dunkel**: das Tier ist eine gefüllte Fläche in
   `currentColor`, und die Löcher darin — Auge, Nase, der Strich des
   Dachses — sind der Hintergrund, der durchscheint. Über dem Bild ist der
   schwarz, also steht hier das Umgekehrte dessen, was auf einer Seite
   steht: weiße Fläche, schwarze Löcher.

   Ohne Wort. „Wird geladen…" ist ein Satz für eine leere Mitte; in der
   Ecke ist er eine Zeile Text, die man nicht liest und die die Ecke
   doppelt so breit macht. */
.lightbox-loader.klein {
  position: absolute;
  right: 20px;
  bottom: calc(var(--lightbox-floor, 70px) + 10px);
  z-index: 2;
  color: rgba(255, 255, 255, 0.92);
  pointer-events: none;
}

.lightbox-loader.klein .creature { width: 64px; height: 51px; }
.lightbox-loader.klein .creature-loader-text { display: none; }

/* Der Boden unter dem Tier ist absichtlich breiter als der Rahmen (`x1`
   ist −40, `x2` ist 200 bei einer viewBox von 120), damit er sich
   darunter durchschieben kann, ohne dass ein Ende sichtbar wird. In der
   Mitte des Schirms fällt das nie auf; in der Ecke lief die gestrichelte
   Linie über den halben unteren Rand hinaus und sah aus wie ein
   Zeichenfehler. */
.lightbox-loader.klein { overflow: hidden; }

/* Der Trennen-Knopf stand einmal in derselben Ecke und schob dieses Tier
   eine Zeile höher. Er steht seit dem 28.08. unter dem Streifen, oben —
   die Ecke ist wieder frei. */

/* ---- Which animal ----

   Four are in the markup; one is shown. The class comes from the setting
   (see LOADER_CREATURES in nav.js), and `Zufall` picks one per opening,
   which is the default: an archive of trail-camera footage may as well
   have something living in the corner of it.

   Done in CSS rather than by building the shape in JS: the one frame you
   are already waiting on is the wrong place to do work, and four static
   SVGs cost nothing until the class that animates them arrives. */
.creature-loader .creature { display: none; }
.creature-loader.show-reh .reh,
.creature-loader.show-hase .hase,
.creature-loader.show-murmeltier .murmeltier,
.creature-loader.show-dachs .dachs,
.creature-loader.show-sau .sau { display: block; }

/* The cut-outs - eyes, a nose, the Dachs's stripe - are whatever is
   behind the animal showing through, so they are the backdrop's own
   colour rather than one of their own. A `fill` cannot be `transparent`
   here: these are holes in a filled shape, not gaps in it.
   Over the lightbox that backdrop is black; on a page it is the page. */
.creature-loader { --loader-eye: var(--bg); }
.lightbox-loader { --loader-eye: rgba(0, 0, 0, 0.9); }

.creature-loader {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.creature-loader-text {
  font-size: 13px;
  letter-spacing: 0.02em;
  color: var(--muted-fg);
}

/* A list page waiting for everything it draws. Reviereinrichtungen and
   Ausrüstung both fetch the lot up front and drew nothing at all until
   it landed, which on a slow link is a blank page with a nav on it. */
.page-loader {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 700;
  color: var(--muted-fg);
  pointer-events: none;
}

.page-loader[hidden] { display: none; }

/* ---- The four, on the settings page ----

   Running, at a quarter size, and each one a way to pick it. A picker
   whose options are four words asks you to remember which animal is
   which and then to go and open a picture to find out.

   The figures are `CREATURE_FIGURES` verbatim, so a preview cannot drift
   from the thing it previews; `transform: scale` rather than a smaller
   viewBox for the same reason - the drawing is not touched. */
.creature-previews {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 10px 0 4px;
}

.creature-preview {
  width: 78px;
  height: 62px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--control-bg);
  color: var(--muted-fg);
  cursor: pointer;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.creature-preview .creature {
  transform: scale(0.5);
  /* Or the scaled-down figure keeps the 150px box it was drawn in and
     four of them are mostly gap. */
  margin: -30px;
}

/* Which one you have picked - and with `Zufall`, all of them, which is
   the clearest way that setting can say what it does. */
.creature-preview.picked {
  border-color: var(--bar-color);
  color: var(--fg);
}

.creature-preview:not(.picked) { opacity: 0.45; }

/* The whole animal bobs while it runs and stands still while it doesn't.
   One timeline, 2.4s: bounds until 56%, then the halt. Every part below
   shares those numbers, which is what keeps the legs, the head and the
   ground agreeing about when it stopped. */
.creature { overflow: visible; }

.reh-run { animation: reh-bound 2.4s infinite; }

/* Legs swing about the hip, not about the middle of the SVG - hence a
   transform-origin per pair, in the viewBox's own coordinates. */
.reh-leg {
  fill: none;
  stroke: currentColor;
  stroke-width: 4.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  transform-box: view-box;
}

/* Each pair swings about its own joint - the Keule behind, the shoulder
   in front - which is where the paths in the markup start. */
.reh-hind-a, .reh-hind-b { transform-origin: 36px 52px; }
.reh-fore-a, .reh-fore-b { transform-origin: 66px 50px; }

/* The far leg of each pair trails its near one by a beat, which is the
   difference between a gallop and a pair of scissors. */
.reh-hind-a { animation: reh-hind 2.4s infinite; }
.reh-hind-b { animation: reh-hind 2.4s infinite; animation-delay: -0.14s; opacity: 0.55; }
.reh-fore-a { animation: reh-fore 2.4s infinite; }
.reh-fore-b { animation: reh-fore 2.4s infinite; animation-delay: -0.14s; opacity: 0.55; }

/* Verhoffen is the head coming *up*. Running it is held forward, level
   with the back; at the halt the Träger lifts and the Lauscher turn. */
.reh-head {
  transform-box: view-box;
  /* The bottom of the Träger, where the neck meets the chest - rotating
     about the head itself would swing it off the neck. */
  transform-origin: 67px 45px;
  animation: reh-head 2.4s infinite;
}

/* The ground each of them is covering. It only moves while the animal
   does, which is the whole tell: at the halt everything stops together.
   Shared, because the trick is the same for all of them - only the
   timeline differs, and that comes from the animal's own rule below. */
.reh .creature-ground,
.reh-ground { animation: reh-ground 2.4s linear infinite; }

@keyframes reh-bound {
  0%, 100% { transform: translateY(0); }
  7%   { transform: translateY(-5px); }
  14%  { transform: translateY(0); }
  21%  { transform: translateY(-5px); }
  28%  { transform: translateY(0); }
  35%  { transform: translateY(-5px); }
  42%  { transform: translateY(0); }
  49%  { transform: translateY(-5px); }
  56%  { transform: translateY(0); }
}

@keyframes reh-fore {
  0%   { transform: rotate(-30deg); }
  7%   { transform: rotate(24deg); }
  14%  { transform: rotate(-30deg); }
  21%  { transform: rotate(24deg); }
  28%  { transform: rotate(-30deg); }
  35%  { transform: rotate(24deg); }
  42%  { transform: rotate(-30deg); }
  49%  { transform: rotate(24deg); }
  56%, 100% { transform: rotate(0deg); }
}

@keyframes reh-hind {
  0%   { transform: rotate(26deg); }
  7%   { transform: rotate(-28deg); }
  14%  { transform: rotate(26deg); }
  21%  { transform: rotate(-28deg); }
  28%  { transform: rotate(26deg); }
  35%  { transform: rotate(-28deg); }
  42%  { transform: rotate(26deg); }
  49%  { transform: rotate(-28deg); }
  56%, 100% { transform: rotate(0deg); }
}

@keyframes reh-head {
  0%, 52%  { transform: rotate(0deg); }
  60%      { transform: rotate(-16deg); }
  92%      { transform: rotate(-16deg); }
  100%     { transform: rotate(0deg); }
}

/* 24px is the dash pattern (10 + 14), so the line arrives back where it
   started and the loop is seamless. */
@keyframes reh-ground {
  0%       { transform: translateX(0); }
  56%      { transform: translateX(-96px); }
  100%     { transform: translateX(-96px); }
}

/* ---- Der Hase ----

   Hoppelt: two long bounds off the hind legs, then he sits up. 2.6s, and
   like the Reh the halt is what makes it an animal - a hare that hopped
   without ever stopping to listen would be a toy.

   The bound is an arc, so the body rises *and* pitches nose-up on the
   way and nose-down on the landing. That is the difference between a hop
   and a lift. */
.hase-hop {
  transform-box: view-box;
  transform-origin: 54px 74px;
  animation: hase-hop 2.6s infinite;
}

.hase-leg {
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  transform-box: view-box;
}

/* The hind legs are the animal: heavy, and what he leaves the ground
   with. Drawn thicker than the front pair for the same reason. */
.hase-hind-a, .hase-hind-b { stroke-width: 6; transform-origin: 42px 64px; }
.hase-fore-a, .hase-fore-b { stroke-width: 4; transform-origin: 74px 62px; }

.hase-hind-a { animation: hase-hind 2.6s infinite; }
.hase-hind-b { animation: hase-hind 2.6s infinite; animation-delay: -0.1s; opacity: 0.55; }
.hase-fore-a { animation: hase-fore 2.6s infinite; }
.hase-fore-b { animation: hase-fore 2.6s infinite; animation-delay: -0.1s; opacity: 0.55; }

/* The Löffel lie back over the neck while he runs and come forward when
   he sits - which is the moment he is listening, and the only reason a
   hare stops at all. */
.hase-ear {
  transform-box: view-box;
  transform-origin: 84px 36px;
  animation: hase-ear 2.6s infinite;
}

.hase .creature-ground { animation: hase-ground 2.6s linear infinite; }

@keyframes hase-hop {
  0%           { transform: translate(0, 0) rotate(0deg); }
  12%          { transform: translate(0, -16px) rotate(-14deg); }
  24%          { transform: translate(0, 0) rotate(4deg); }
  30%          { transform: translate(0, 0) rotate(0deg); }
  42%          { transform: translate(0, -16px) rotate(-14deg); }
  54%          { transform: translate(0, 0) rotate(4deg); }
  60%, 100%    { transform: translate(0, 0) rotate(0deg); }
}

@keyframes hase-hind {
  0%           { transform: rotate(-18deg); }
  12%          { transform: rotate(34deg); }
  24%          { transform: rotate(-18deg); }
  30%          { transform: rotate(-18deg); }
  42%          { transform: rotate(34deg); }
  54%          { transform: rotate(-18deg); }
  60%, 100%    { transform: rotate(0deg); }
}

@keyframes hase-fore {
  0%           { transform: rotate(22deg); }
  12%          { transform: rotate(-24deg); }
  24%          { transform: rotate(22deg); }
  30%          { transform: rotate(22deg); }
  42%          { transform: rotate(-24deg); }
  54%          { transform: rotate(22deg); }
  60%, 100%    { transform: rotate(0deg); }
}

@keyframes hase-ear {
  0%, 54%      { transform: rotate(0deg); }
  64%          { transform: rotate(26deg); }
  92%          { transform: rotate(26deg); }
  100%         { transform: rotate(0deg); }
}

@keyframes hase-ground {
  0%           { transform: translateX(0); }
  60%          { transform: translateX(-120px); }
  100%         { transform: translateX(-120px); }
}

/* ---- Das Murmeltier ----

   Sits, and looks left, then right, then left again: the one thing
   everybody has seen one do, and the only one of the four that does not
   travel - so its ground does not move either. What it is standing on is
   its own burrow.

   The head turns about the *neck*, not about itself, or it would swing
   off the shoulders. A little translate with the rotate, because a real
   turn carries the muzzle sideways as well as round. */
.murmel-head {
  transform-box: view-box;
  transform-origin: 60px 52px;
  animation: murmel-look 3.2s ease-in-out infinite;
}

/* The body breathes. Small, and out of step with the head, so the two
   never look like one keyframe list. */
.murmel-sit {
  transform-box: view-box;
  transform-origin: 60px 88px;
  animation: murmel-breathe 3.2s ease-in-out infinite;
}

@keyframes murmel-look {
  0%, 8%       { transform: rotate(0deg) translateX(0); }
  22%, 38%     { transform: rotate(-9deg) translateX(-7px); }
  52%, 68%     { transform: rotate(9deg) translateX(7px); }
  82%, 100%    { transform: rotate(0deg) translateX(0); }
}

@keyframes murmel-breathe {
  0%, 100%     { transform: scaleY(1); }
  46%          { transform: scaleY(1.035); }
}

/* ---- Der Dachs ----

   Watschelt: low, heavy and rolling, which from the side is a rock
   rather than a sway - one shoulder down, then the other. He stops to
   put his nose to the ground, which is what the whole shape of him is
   for.

   3s, the slowest of the four. A badger in a hurry is not a badger. */
.dachs-waddle {
  transform-box: view-box;
  transform-origin: 52px 78px;
  animation: dachs-waddle 3s ease-in-out infinite;
}

.dachs-leg {
  fill: none;
  stroke: currentColor;
  stroke-width: 6;
  stroke-linecap: round;
  stroke-linejoin: round;
  transform-box: view-box;
}

.dachs-hind-a, .dachs-hind-b { transform-origin: 40px 68px; }
.dachs-fore-a, .dachs-fore-b { transform-origin: 70px 68px; }

.dachs-hind-a { animation: dachs-hind 3s ease-in-out infinite; }
.dachs-hind-b { animation: dachs-hind 3s ease-in-out infinite; animation-delay: -0.25s; opacity: 0.55; }
.dachs-fore-a { animation: dachs-fore 3s ease-in-out infinite; }
.dachs-fore-b { animation: dachs-fore 3s ease-in-out infinite; animation-delay: -0.25s; opacity: 0.55; }

/* The nose goes down at the halt. On the head alone, about the neck. */
.dachs-head {
  transform-box: view-box;
  transform-origin: 68px 58px;
  animation: dachs-nose 3s ease-in-out infinite;
}

.dachs .creature-ground { animation: dachs-ground 3s linear infinite; }

@keyframes dachs-waddle {
  0%           { transform: rotate(0deg) translateY(0); }
  10%          { transform: rotate(-2.6deg) translateY(-2px); }
  20%          { transform: rotate(2.6deg) translateY(0); }
  30%          { transform: rotate(-2.6deg) translateY(-2px); }
  40%          { transform: rotate(2.6deg) translateY(0); }
  50%          { transform: rotate(-2.6deg) translateY(-2px); }
  60%, 100%    { transform: rotate(0deg) translateY(0); }
}

@keyframes dachs-hind {
  0%           { transform: rotate(16deg); }
  10%          { transform: rotate(-16deg); }
  20%          { transform: rotate(16deg); }
  30%          { transform: rotate(-16deg); }
  40%          { transform: rotate(16deg); }
  50%          { transform: rotate(-16deg); }
  60%, 100%    { transform: rotate(0deg); }
}

@keyframes dachs-fore {
  0%           { transform: rotate(-16deg); }
  10%          { transform: rotate(16deg); }
  20%          { transform: rotate(-16deg); }
  30%          { transform: rotate(16deg); }
  40%          { transform: rotate(-16deg); }
  50%          { transform: rotate(16deg); }
  60%, 100%    { transform: rotate(0deg); }
}

@keyframes dachs-nose {
  0%, 56%      { transform: rotate(0deg); }
  66%          { transform: rotate(11deg); }
  92%          { transform: rotate(11deg); }
  100%         { transform: rotate(0deg); }
}

@keyframes dachs-ground {
  0%           { transform: translateX(0); }
  60%          { transform: translateX(-72px); }
  100%         { transform: translateX(-72px); }
}

/* ---- Das Wildschwein ----

   Reibt sich an einem Baum: shoves sideways into the stem, leans, shoves
   again, and stands. 2.8s.

   The **tree moves** - that is the whole picture. A Sau putting its
   weight into a stem bends the stem, and without that this is an animal
   twitching next to a post. It bends *away* from the shove and springs
   back slowly, because wood does.

   The rub is a translate rather than a rotate: the shoulder is what is
   doing the work and it travels almost horizontally. The small rock on
   top of it is the body settling into each shove. */
.sau-rub {
  transform-box: view-box;
  transform-origin: 40px 80px;
  animation: sau-rub 2.8s ease-in-out infinite;
}

/* Bends from the root, like a stem and not like a door. */
.sau-tree {
  transform-box: view-box;
  transform-origin: 104px 89px;
  animation: sau-tree 2.8s ease-in-out infinite;
}

.sau-leg {
  fill: none;
  stroke: currentColor;
  stroke-width: 6;
  stroke-linecap: round;
  stroke-linejoin: round;
  transform-box: view-box;
}

/* Braced rather than walking: the legs take the push and give a little,
   which is a much smaller movement than a gait. */
.sau-hind-a, .sau-hind-b { transform-origin: 24px 70px; }
.sau-fore-a, .sau-fore-b { transform-origin: 60px 70px; }
.sau-hind-b, .sau-fore-b { opacity: 0.55; }
.sau-leg { animation: sau-brace 2.8s ease-in-out infinite; }
.sau-hind-b, .sau-fore-b { animation-delay: -0.08s; }

@keyframes sau-rub {
  0%           { transform: translateX(0) rotate(0deg); }
  14%          { transform: translateX(7px) rotate(1.4deg); }
  26%          { transform: translateX(2px) rotate(0deg); }
  38%          { transform: translateX(7px) rotate(1.4deg); }
  50%          { transform: translateX(2px) rotate(0deg); }
  60%          { transform: translateX(6px) rotate(1.2deg); }
  70%, 100%    { transform: translateX(0) rotate(0deg); }
}

@keyframes sau-tree {
  0%           { transform: rotate(0deg); }
  14%          { transform: rotate(2.4deg); }
  26%          { transform: rotate(0.4deg); }
  38%          { transform: rotate(2.4deg); }
  50%          { transform: rotate(0.4deg); }
  60%          { transform: rotate(2deg); }
  /* The spring back is slower than any of the shoves - wood. */
  82%, 100%    { transform: rotate(0deg); }
}

@keyframes sau-brace {
  0%, 70%, 100% { transform: rotate(0deg); }
  14%, 38%, 60% { transform: rotate(-4deg); }
  26%, 50%      { transform: rotate(-1deg); }
}

/* Someone who has asked for less movement gets the animal standing
   still, which is a perfectly good "wait" - and for the Reh it is
   exactly what verhoffen looks like anyway. */
@media (prefers-reduced-motion: reduce) {
  .reh-run, .reh-leg, .reh-head, .reh-ground,
  .hase-hop, .hase-leg, .hase-ear,
  .murmel-head, .murmel-sit,
  .dachs-waddle, .dachs-leg, .dachs-head,
  .sau-rub, .sau-tree, .sau-leg,
  .creature-ground { animation: none; }
}

/* **Above the reading column.** The column is `z-index: 1` and these were
   on the default 0, which was invisible for as long as it was three short
   lines of text at the top of the screen. It is not any more - a button
   row, the tags, the time, a 120px map and the place under it - and on a
   phone that stack reaches the vertical middle, which is exactly where
   the `‹` sits. The arrow was still there and still worked; it was
   underneath a transparent column, and tapping it did nothing.

   The `×` goes up with them: same corner, same argument.

   **Und seit dem 04.09.2026 ueber den Chat** (auf Wunsch: *„the fabs
   (right, x, trash, share, edit) shall be on top of it"*). Der Chat liegt
   auf `z-index: 5`; die Pfeile lagen mit 3 darunter und waeren, seit er
   an die rechte Kante gerueckt ist, teils hinter ihm verschwunden. `6`
   ist zwischen ihm und der Werkzeugspalte (7), und damit stehen alle
   Knoepfe des Betrachters ueber ihm - was sie sind: das, was man tut,
   ueber dem, was gesagt wurde. */
.lightbox-prev, .lightbox-next {
  position: absolute;
  z-index: 6;
  color: white;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
}

/* **What can be done to what is on screen**, in one column at the top
   right: out, edit, and - with the pencil on - delete. They were spread
   between two corners, the × here and the other two on top of the reading
   column at the left, which made that column half a statement about the
   picture and half a set of controls. One kind of thing per corner, so no
   press lands on the wrong sort of control.

   Ordered by how often it is wanted, with the irreversible one furthest
   from the one you reach for by accident.

   **Ueber dem Chat** (04.09.2026, auf Wunsch: *„the fabs (right, x,
   trash, share, edit) shall be on top of it"*): der liegt auf 5 und
   reicht seit heute bis an dieselbe Kante. `7`, eine Stufe ueber den
   Pfeilen (6), damit die Reihenfolge in dieser Ecke dieselbe bleibt wie
   ueberall - was man tut, ueber dem, was gesagt wurde. */
.lightbox-tools {
  position: absolute;
  top: 16px;
  right: 20px;
  z-index: 7;
  display: flex;
  flex-direction: column;
  /* **Rechtsbuendig, damit der Stift unter dem `×` steht** (04.09.2026,
     auf Wunsch: *„lightbox edit button shall be in the same column as the
     x"*).

     Die Spalte war mittig ausgerichtet, und ihre oberste Zeile traegt
     seit dem 05.09. zwei Dinge: das Herz und das `×`. Damit war ihre
     Mitte nicht mehr die Mitte des `×`, und alles darunter - Stift,
     Kette, Korb - stand siebzehn Pixel links davon. Eine Spalte, die
     nicht fluchtet, liest sich als zwei.

     An der rechten Kante fluchten sie wieder: `×` und die Scheiben
     darunter sind gleich breit (44px), also fallen mit den Kanten auch
     die Mitten zusammen. Das Herz bleibt, wo es ist - links neben dem
     Ausgang, nicht in der Reihe der Werkzeuge. */
  align-items: flex-end;
  gap: 10px;
}

/* **Ein dünner schwarzer Rand** (26.08.2026, auf Wunsch). Drei weisse
   Glyphen ohne eigene Fläche liegen auf einer Photographie, und auf einer
   hellen sind sie fort - ein Himmel, eine Schneekanzel, ein überbelichteter
   Blitz bei Nacht. Die drei Scheiben darunter (Stift, Kette, Korb) haben
   ihre Fläche und ihren Rand schon; diese drei hatten nichts.

   **Schwarz und nicht weiss**, obwohl die Nachbarn weisse Ränder tragen:
   die stehen auf dunklem Grund und zeichnen sich davon ab. Diese hier
   stehen auf dem Bild selbst, und was dort fehlt, ist die *dunkle* Kante
   gegen ein helles Feld. Dazu ein Hauch Grund unter dem Glyph, sonst
   umschliesst der Rand nichts.

   Eine Regel für alle drei, weil es eine Entscheidung ist. */
.lightbox-close {
  border: 1px solid rgba(0, 0, 0, 0.65);
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.28);
  box-sizing: border-box;
}

.lightbox-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  color: white;
  font-size: 34px;
  line-height: 1;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
}

/* **Dünne weisse Pfeile, ohne Scheibe** (23.09.2026, auf Wunsch:
   *„lightbox: show the left right arrows as thin white arrows without
   drawing the black circle around them"*).

   Hier stand seit dem 26.08. eine 56px-Scheibe mit schwarzem Rand, und
   davor ein 60px-Glyph mit Polster. Beide waren schwer für das, was ein
   Pfeil sagt: *da geht es weiter*. Jetzt ein Winkel aus zwei dünnen
   Strichen, als SVG in der Auszeichnung (die Schriftzeichen ❰ ❱ sind
   fett gezeichnet und lassen sich nicht dünner machen).

   **Was der Rand tat, tut ein Schatten**: auf einer hellen Aufnahme -
   Schnee, Himmel, ein Blitz bei Nacht - wäre ein weisser Strich allein
   fort. `drop-shadow` folgt dem Strich und zeichnet keinen Kreis.

   Die Trefferfläche bleibt 56px hoch und breit - unsichtbar, aber der
   Daumen, der blind blättert, trifft dieselbe Stelle wie vorher. */
.lightbox-prev, .lightbox-next {
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.9)) drop-shadow(0 1px 4px rgba(0, 0, 0, 0.6));
  opacity: 0.9;
  transition: opacity 0.12s ease;
}
.lightbox-prev:hover, .lightbox-next:hover { opacity: 1; }
.lightbox-prev { left: 20px; }
.lightbox-next { right: 20px; }

/* **On a short window the `›` moves inward, off the tools column.**
   It is centred on the height and that column hangs from the top, so on
   anything tall enough the two never meet. Hold a phone sideways - which
   is how a landscape photograph is looked at, so it is not the odd case -
   and the vertical middle is level with the pencil and the bin.

   Inward rather than up or down, because the arrows are the one control
   here whose position *means* something - left is back, right is on - and
   a `›` in a corner is a different button.

   **Only the `›` now** (28.08.2026): the `‹` used to dodge the reading
   column, and there is no longer one to dodge - the left-hand side at
   mid-height is bare picture, with the strip above it and the map below. */
@media (max-height: 560px) {
  .lightbox-next { right: calc(20px + 44px + 12px); }
}
/* Spelled out rather than trusting the UA's [hidden] rule, since these
   carry an author `position` and are easy to give a `display` later. */
.lightbox-prev[hidden], .lightbox-next[hidden] { display: none; }

/* **Das obere Band** (28.08.2026): der Streifen der uebrigen Rahmen, und
   darunter links der Zeitstempel.

   Es haengt oben links und reicht nach rechts bis kurz vor die
   Werkzeugspalte - das ist es, was *„towards the X fab"* heisst. Die
   76px sind deren 44px plus ihre 20px Einzug plus ein Spalt, damit der
   letzte Daumennagel nicht unter dem `x` verschwindet.

   Mostly air, und Luft darf keine Griffe abfangen, die dem Bild darunter
   gelten - dieselbe Regel wie an den FAB-Ecken der Galerie. */
.lightbox-top {
  position: absolute;
  top: 12px;
  left: 12px;
  right: 76px;
  z-index: 2;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}

.lightbox-top > * { pointer-events: auto; }

/* Der Stempel steht in der Flucht der Daumennaegel, nicht an der Kante des
   Bandes: der Streifen traegt 12px Polster, also braucht die Zeile
   darueber dieselben, sonst steht sie einen Daumennagel weit links davon.
   (Er steht seit dem 28.08. ueber dem Streifen, nicht darunter - die
   Rechnung ist dieselbe.)

   **Und seit dem 20.09.2026 ist der Einzug fort** (auf Wunsch: *„move
   Datetime way to the top left"*). Er stand dafür, dass die Zeile in der
   Flucht der Daumennägel anfängt — erst mit dem Stempel, seit dem
   18.09. mit dem Anzeigeknopf davor. Der Knopf ist jetzt unten rechts
   (siehe dort), und was bliebe, wäre ein Stempel, der um zwölf Pixel
   eingerückt in einer sonst leeren Ecke steht. Also bündig: er fängt da
   an, wo der Betrachter anfängt, gegenüber dem `×`, das denselben
   Abstand zur anderen Kante hält. Der Streifen darunter behält sein
   eigenes Polster und damit seine eigene Flucht — das ist die Kante, die
   hier aufgegeben wird, und sie war die schwächere von beiden. */
.lightbox-kopfzeile {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* **Der Anzeigeknopf, unten rechts** (20.09.2026, auf Wunsch: *„move the
   display mode toggle button to the bottom right (on top of
   everything)"*).

   Eine stille Scheibe wie das Herz und die Kette oben rechts - er *tut*
   etwas und sagt zugleich, was gerade gilt, und in dieser Grösse ist er
   der kleinste Griff, den ein Daumen noch sicher trifft.

   **Über dem Chatknopf, nicht neben ihm.** Die Ecke ist besetzt: dort
   sitzt `#chatOeffnen`. Zwei Scheiben nebeneinander in einer Ecke lesen
   sich als ein Paar, das zusammengehört - diese zwei tun nichts
   miteinander. Übereinander ist dieselbe Anordnung, die die Spalte oben
   rechts schon hat, und sie sagt „noch ein Griff hier" statt „dasselbe
   noch einmal".

   **`z-index: 20`, und das ist eine Aussage über beide Richtungen.** Er
   liegt über allem, was der Betrachter unten hat - der Karte (1), der
   Ansprech-Reihe (6), dem Chat (5) -, denn das ist, worum gebeten wurde:
   er soll erreichbar bleiben, gleich was darunter aufgeht. Er liegt
   aber **nicht** über den Fenstern (9999 aufwärts): ein Löschfenster,
   das eine Frage stellt, muss den ganzen Schirm haben, und ein Knopf,
   der darüber stehenbliebe, wäre einer, den man aus Versehen trifft,
   während man etwas anderes liest. */
.lightbox-ansicht {
  position: absolute;
  /* **Ganz in die Ecke** (22.09.2026, auf Wunsch: *„lightbox displaymode
     button shall be even more in the bottom right corner"*). Er stand auf
     20/70 - eingerückt, und eine Scheibenhöhe über dem Boden, um dem
     Chatknopf auszuweichen. Der weicht ihm aber gar nicht aus: er hängt
     am Chat und der endet über der Karte, also war der Platz darunter die
     ganze Zeit frei. Jetzt 12/12, dieselben zwölf Pixel, die das `×`
     oben rechts und der Stempel oben links zur Kante halten - die vier
     Ecken des Betrachters sind damit gleich weit eingerückt. */
  right: 12px;
  bottom: 12px;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(20, 20, 20, 0.55);
  color: #fff;
  cursor: pointer;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.9));
}

/* **Und das `display: flex` schlägt das `[hidden]` des Browsers** -
   dieselbe Falle wie bei `.list-fab` und beim Suchband des Rades, hier
   zum dritten Mal. `renderAnsichtKnopf` setzt `hidden`, wo es nichts
   wegzuräumen gibt (ein Anblick ohne Bild zeigt ohnehin alles), und ohne
   diese Zeile stand der Knopf trotzdem da und zählte eine Stufe weiter,
   die niemand sieht. Oben links neben dem Stempel fiel das nicht auf;
   allein in einer Ecke ist er dann das einzige, was dort steht, und das
   einzige, was nichts tut. */
.lightbox-ansicht[hidden] { display: none; }

/* **Ein Grund unter ihm**, den er oben links nicht brauchte: dort lag er
   über dem schwarzen Band des Betrachters, hier über einer Landkarte in
   Tageslicht. Ein weisses Zeichen ohne Rückwand ist darauf nicht zu
   sehen - dieselbe Rechnung wie beim Schleier über der Karte. */
.lightbox-ansicht:hover { background: rgba(255, 255, 255, 0.28); }

/* **Eine Scheibe unter dem Stift** (23.09.2026): dieselbe Form wie er und
   wie das ✓ beim Anlegen, weil sie in derselben Spalte stehen. Bis heute
   ein Wort in einer Pille unten links. */
.lightbox-ansprechen {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  background: rgba(20,20,20,0.75);
  border: 1px solid rgba(255,255,255,0.35);
  border-radius: 50%;
  cursor: pointer;
}
.lightbox-ansprechen[hidden] { display: none; }
.lightbox-ansprechen:hover { background: var(--bar-color); border-color: var(--bar-color); }

/* The pencil, under the ×. Filled while armed, so it is obvious why the
   time, the map and the tags have changed behaviour. Bigger than it was:
   at 36px in the corner of a column it was the smallest target on screen
   and the one most often wanted on a phone. */
.lightbox-edit {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  background: rgba(20,20,20,0.75);
  border: 1px solid rgba(255,255,255,0.35);
  border-radius: 50%;
  cursor: pointer;
}
.lightbox.edit-mode .lightbox-edit {
  background: var(--bar-color);
  border-color: var(--bar-color);
}

/* **Das ✓ beim Anlegen** (04.09.2026, siehe index.html). Dieselbe Scheibe
   wie der Stift, an dessen Stelle es tritt, und dieselbe Farbe wie das ✓
   des Revierplans - grün heisst in dieser Anwendung „das ist es". */
.lightbox-anlegen-fertig {
  display: none;
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  color: #fff;
  background: var(--bar-color);
  border: 1px solid var(--bar-color);
  border-radius: 50%;
  cursor: pointer;
}

/* Die zwei tauschen die Stelle, statt nebeneinander zu stehen: eine Ecke,
   ein Ding, und „bearbeiten" ist beim Anlegen keine Frage. */
.lightbox.anlegen .lightbox-edit { display: none; }
/* Beim Anlegen gibt es noch nichts zu teilen: die Zeile ist ein Entwurf,
   und ein Link darauf zeigte auf etwas, das es gleich anders gibt. Das
   ist der Rest der Regel, die bis zum 05.09.2026 für das ganze
   Bearbeiten galt. */
.lightbox.anlegen .lightbox-share { display: none; }
.lightbox.anlegen .lightbox-anlegen-fertig { display: flex; }


/* **Der grosse Knopf beim Anlegen** (04.09.2026, siehe index.html).

   Mitten auf dem Schirm, weil dort das Bild hinkommt, das er holt - und
   weil ein frisch angelegter Anblick sonst nichts hat, was diese Fläche
   beanspruchen könnte.

   `z-index: 6`: über der Auflage des Chats (5), unter der Werkzeugspalte
   (7) - dieselbe Ordnung wie die Pfeile. Auf einem schmalen Schirm steht
   der Chat mittig, und ohne diese Stufe läge der Knopf hinter dessen
   (durchsichtiger, aber greifender) Fläche. */
.lightbox-anlegen-upload {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 6;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 26px 34px;
  border-radius: 18px;
  color: #fff;
  background: rgba(20, 20, 20, 0.55);
  border: 2px dashed rgba(255, 255, 255, 0.45);
  font-family: inherit;
  font-size: 15px;
  cursor: pointer;
}

.lightbox-anlegen-upload:hover {
  background: rgba(20, 20, 20, 0.72);
  border-color: rgba(255, 255, 255, 0.7);
}

/* `display: flex` oben schlägt das `[hidden]` des Browsers - dieselbe
   Falle wie bei `.list-fab`, und hier wäre sie ein Knopf, der auf jedem
   Bild mitten im Weg steht. */
.lightbox-anlegen-upload[hidden] { display: none; }

/* Same shape as the pencil, in the colour the app uses for destructive
   actions. Absent entirely until the pencil is on - it has no reading
   meaning, and it's the one control here that can't be undone. */
.lightbox-delete {
  display: none;
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  color: #fff;
  background: #7a2020;
  border: 1px solid #a33;
  border-radius: 50%;
  cursor: pointer;
}
.lightbox-delete:hover { background: #a33; }
.lightbox.edit-mode .lightbox-delete { display: flex; }

/* **Ausser beim Anlegen** (04.09.2026, auf Wunsch: *„it shall also not
   show the trashbin"*). Er löscht den Rahmen auf dem Schirm oder den
   ganzen Anblick - beim Anlegen gibt es keinen Rahmen, und der ganze
   Anblick ist genau das, was das `×` daneben verwirft. Ein zweites Wort
   für dasselbe, in der Ecke, in der jedes Zeichen etwas anderes tun soll.

   **Hinter** der Regel darüber und nicht vor ihr: beide wiegen gleich
   viel (zwei Klassen und ein Element), und beim Anlegen steht der Stift
   ohnehin an - vorher stehend hätte sie stumm verloren. */
.lightbox.anlegen .lightbox-delete { display: none; }

/* Full paths, not the thumbnails' leaf labels: there's room here, and a
   bare `Kitz` doesn't say whose. */
.lightbox-tags { display: flex; flex-wrap: wrap; gap: 6px; }

/* A tag always does something - filters the gallery to it, or with the
   pencil on, opens the Ansprech-Dialog - so it's always a pointer. */
.lightbox-tag {
  padding: 5px 10px;
  font-size: 13px;
  color: #fff;
  background: rgba(20,20,20,0.75);
  border: 1px solid transparent;
  border-radius: 20px;
  cursor: pointer;
}
.lightbox-tag:hover { background: var(--bar-color); }

/* When the photo was taken. Only live with the pencil on: there's nothing
   sensible for a click on it to mean otherwise. */
.lightbox-time {
  padding: 6px 12px;
  font-size: 13px;
  color: #fff;
  background: rgba(20,20,20,0.75);
  border: 1px solid transparent;
  border-radius: 20px;
  cursor: default;
}

/* **Ueber die ganze Breite, am Fuss** (28.08.2026, auf Wunsch: *„Map with
   fullwindow width at the bottom"*).

   Es war zuletzt ein 120px-Quadrat oben links, davor ein Kreis, der sich
   auf einen Druck aufblies. Alle diese Formen waren dasselbe Zugestaendnis:
   gross genug fuer *„ungefaehr dort"*, zu klein, um irgendetwas daran zu
   erkennen. Ueber die ganze Breite ist es zum ersten Mal eine Karte, an
   der man die Umgebung liest - der Wechsel, an dem die Kanzel steht, der
   Weg, ueber den das Stueck gekommen sein muss.

   Kein Rand ringsum und keine runden Ecken: es ist eine Kante des
   Fensters, kein Ding darin. Nur oben eine Linie, damit es sich vom Bild
   darueber loest.

   `position: relative` bleibt ausgeschrieben - es hindert Leaflet daran,
   es selbst inline hineinzuschreiben, und ein Inline-Stil schlaege alles
   hier. */
.lightbox-where {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  /* Ohne Karte ist das Band nur der Ortschip, und der Rest der Zeile ist
     Luft ueber der unteren Kante des Bildes. Luft darf keine Griffe
     abfangen - dieselbe Regel wie am oberen Band. */
  pointer-events: none;
}

.lightbox-where > * { pointer-events: auto; }

/* ---- Das Dreieck in der Ecke (24.09.2026) ----

   > the map shall show as a triangle filling the bottom left corner (not
   > too big). the chips shall be listed outside on the bottom of the page
   > right of that corner

   Ueber einem Medium ist die Karte kein Band mehr, sondern ein
   rechtwinkliges Dreieck: der rechte Winkel in der Ecke, die Katheten an
   der linken und der unteren Kante. **Es liegt ueber dem Bild** und haelt
   kein Polster frei (`ansprechFloor`), die Antwortreihe steht rechts
   daneben am Fuss (`placeAnsprechBar`, `ansprechDreieckKante`).

   **`clip-path` und nicht eine Maske oder ein gedrehtes Quadrat**: der
   Schnitt gilt auch fuer das Treffen. Ein Druck neben der Hypotenuse geht
   durch den Kasten hindurch an das Bild darunter (`.lightbox-where` faengt
   ohnehin nichts), ein Druck darin ist wie immer die Tuer zum Revierplan,
   und der lange Druck oder der mit dem Stift das Ortsfenster.

   Die Nadel steht im Schwerpunkt des Dreiecks und nicht in der Mitte des
   Kastens - die laege genau auf der Hypotenuse (`zeigeLightboxStelle`).

   Ein Schatten an der Hypotenuse, von `filter` am Behaelter: der schneidet
   erst und schattiert dann, und eine Linie laesst sich an einer
   geschnittenen Kante nicht ziehen. Er loest die Karte von einem hellen
   Bild. Die Linie oben (`border-top`) war die Kante des Bandes und faellt
   hier weg. */
.lightbox:not(.ohne-bild) .lightbox-where {
  right: auto;
  width: var(--lightbox-dreieck-breite);
  height: var(--lightbox-dreieck-hoehe);
  filter: drop-shadow(0 0 1px rgba(255, 255, 255, 0.45))
          drop-shadow(0 0 6px rgba(0, 0, 0, 0.6));
}
/* Mit dem Stift ist die Karte ein Griff (das Ortsfenster) und sagte das
   bisher mit der Akzentfarbe an ihrer oberen Linie; die gibt es am
   Dreieck nicht, also leuchtet die Hypotenuse. */
.lightbox.edit-mode:not(.ohne-bild) .lightbox-where {
  filter: drop-shadow(0 0 1.5px var(--bar-color))
          drop-shadow(0 0 6px rgba(0, 0, 0, 0.6));
}
.lightbox:not(.ohne-bild) .lightbox-map {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border-top: none;
  clip-path: polygon(0 0, 0 100%, 100% 100%);
}

/* **Kein Ort, klein in der Ecke** (13.09.2026, jetzt als Marke). Ein
   Dreieck, das sagt, dass es nichts zu zeigen hat, waere wieder die
   Flaeche Leere, die diese Anzeige einmal ganz verschwinden liess - also
   eine Pille unten links, so gross wie das Wort. Die Reihe beginnt dann
   neben ihr. Mit dem Stift (*Ort setzen*) bleibt das ganze Dreieck, denn
   dort ist es ein Ziel, und das Wort rueckt in seine breite untere Ecke. */
.lightbox:not(.ohne-bild) .lightbox-map.ohne-ort {
  inset: auto;
  left: 12px;
  bottom: 12px;
  width: auto;
  height: auto;
  padding: 4px 12px;
  clip-path: none;
  border-radius: 14px;
}
.lightbox:not(.ohne-bild) .lightbox-map.ohne-ort .lightbox-map-empty {
  font-size: 12px;
  white-space: nowrap;
}
.lightbox:not(.ohne-bild) .lightbox-map:not(.ohne-ort) .lightbox-map-empty {
  align-items: flex-end;
  justify-content: flex-start;
  padding: 0 0 14px 14px;
  box-sizing: border-box;
  text-align: left;
  font-size: 13px;
}

.lightbox-map {
  position: relative;
  z-index: 1;
  /* Damit das Band genau so hoch ist wie die Variable sagt: die Linie
     oben zaehlt sonst dazu, und alles, was sich an `--lightbox-floor`
     ausrichtet, liegt um diese eine Zeile daneben. */
  box-sizing: border-box;
  width: 100%;
  height: 100%;   /* der Kasten: das Dreieck, oder der Grund (24.09.2026) */
  overflow: hidden;
  border-top: 1px solid rgba(255,255,255,0.22);
  background: rgba(20,20,20,0.75);
  /* **Die Hand, denn sie tut immer etwas** (05.09.2026, auf Wunsch:
     *„lightbox map cursor shall show the hand"*).

     Hier stand `default` mit dem Vermerk, sie sei nur mit dem Stift ein
     Bedienelement. Das stimmt seit einer Weile nicht mehr:
     `onLightboxMapClick` schlaegt ohne Stift den Revierplan an dieser
     Stelle auf, mit Stift den Positionsdialog. Beides ist ein Druck, der
     etwas tut, und ein Zeiger, der das Gegenteil behauptet, ist eine
     Auskunft, die nicht stimmt. */
  cursor: pointer;
}

/* The map and the name of the place are one answer to one question, so
   they are one object: the chips lie across the foot of the map rather
   than under it. A caption sitting below a picture of the same thing is
   two things to look at, and three chips stacked under a 120px map were
   taller than the map.

   **Gestapelt** waren sie das - **nebeneinander** nicht (05.09.2026, auf
   Wunsch: *„all map element chips of wherein the anblick lies shall be
   listed in the map"*). Die Kette stand laengst da und wurde bis auf ihr
   letztes Glied weggeworfen; jetzt liegt sie quer und bricht um, wenn
   sie muss. Jeder Chip darf schrumpfen (`min-width: 0` und die Ellipse
   an `.place-chip`), damit das erste Glied nicht das letzte
   hinausdraengt - und *Kirsch* ist das, was man am Ende lesen will. */
.lightbox-place {
  position: absolute;
  left: 12px;
  bottom: 10px;
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  max-width: calc(100% - 24px);
}

.lightbox-place .place-chip { min-width: 0; }

.lightbox-place[hidden] { display: none; }

/* **In der Reihe am Fuss** (24.09.2026). Ueber einem Medium haengt
   `haengeOrtsketteEin` die Kette aus der Karte in die Antwortreihe, vor
   die Antworten: im Dreieck deckten vier Namen die Karte zu. Dort steht
   sie im Fluss, bricht mit der Reihe um und haelt einen kleinen Abstand
   zu den Antworten, damit *wo* und *was* nicht als eine Liste gelesen
   werden. */
.ansprech-row .lightbox-place,
.ansprech-row .lightbox-place.loose {
  position: static;
  margin: 0 6px 0 0;
  max-width: 100%;
}

/* No map to lie on. An Anblick with no coordinates has none outside edit
   mode, and it can still know its place - a `feature_id` says where it
   was seen without saying where that is. The chip then stands on its own
   at the foot of the viewer, in flow, and the band is as tall as it is -
   which is what `placeAnsprechBar` measures, so the Ansprechen row lands
   just above it either way. */
.lightbox-place.loose {
  position: static;
  margin: 0 12px 10px;
}

/* In the kind's own colour (paintKindColour writes the background and
   picks readable ink for it), and with only the name on it. */
.place-chip {
  max-width: 100%;
  padding: 3px 10px;
  border: none;
  border-radius: 14px;
  /* Until paintKindColour writes over both - a kind with no entry in
     KINDS must not come out as invisible text on nothing. */
  background: rgba(20,20,20,0.75);
  color: #fff;
  font-size: 12px;
  font-weight: bold;
  line-height: 1.35;
  text-align: left;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.place-chip:hover { filter: brightness(1.15); }

/* Armed by the pencil: the three things that can be edited in place say
   so by taking the accent colour. */
.lightbox.edit-mode .lightbox-tag,
.lightbox.edit-mode .lightbox-time,
.lightbox.edit-mode .lightbox-map {
  border-color: var(--bar-color);
  cursor: pointer;
}

/* **Und die Worte sagen mit dem Stift, dass ein Antippen sie öffnet**
   (29.08.2026). Der Stift macht seit heute keinen Editor mehr auf - er
   sagt nur, dass dieser Anblick zum Ändern offen ist -, also braucht der
   Text ein Zeichen dafür, dass er das nächste Ziel ist. Derselbe Rand in
   derselben Farbe wie an Zeitstempel und Karte daneben, aus demselben
   Grund. Nicht, während man schon schreibt: dann ist der Editor selbst
   die Antwort. */
/* **Am 02.09.2026 an die Blase gewandert**: sie ist das Ziel der Geste,
   und ein Rahmen um das ganze Fenster verspraeche, dass ein Druck
   irgendwo darin schreiben laesst. */
.lightbox.edit-mode:not(.text-edit) .lightbox-text .chat-blase {
  outline: 2px solid var(--bar-color);
  cursor: pointer;
}
.lightbox.edit-mode .lightbox-time:hover { background: var(--bar-color); }

/* **Die Kette ging einmal mit dem Stift** (28.08.2026, auf Wunsch: *„do
   not show the share fab"*) - und steht seit dem 05.09.2026 wieder da,
   auf ausdrücklichen Wunsch (*„show share button in edit mode after
   all"*). Das Argument von damals war, Teilen sei eine Handlung nach
   draussen und der Stift der Modus, in dem man an der Sache selbst
   arbeitet. Dagegen steht, dass der Knopf auch eine *Auskunft* ist:
   `.aktiv` heisst „geht gerade hinaus", und die will man gerade dann
   sehen, wenn man etwas ändert. Beim Anlegen bleibt er weg - dort gibt
   es noch nichts zu teilen (siehe `.lightbox.anlegen`). */
/* **Der Kettenknopf bleibt auch im Bearbeiten stehen** (05.09.2026, auf
   Wunsch: *„show share button in edit mode after all"*). Er stand hier
   auf `display: none`, weil das Bearbeiten die Knöpfe auf das
   Notwendige zusammenzieht - aber Teilen ist keine Zutat zum Bild,
   sondern eine Auskunft über den Anblick (`.aktiv` heisst „geht gerade
   hinaus"), und die gilt mit dem Stift wie ohne ihn. */

/* ---- Die eigene Reihe des Texteditors (28.08.2026) ----

   Sichern, wegwerfen, hinaus - quer, oben rechts, an der Stelle der
   Werkzeugspalte. Und **statt** ihr: zwei Saetze Knoepfe in derselben
   Ecke ist, was „ein Ding je Ecke" verbietet, und Stift und `x` daneben
   taeten dasselbe wie `✓` und `x` hier, nur ohne Warnung.

   Mit ihnen gehen die uebrigen Fabs: die Pfeile, weil in diesem Modus
   ohnehin nicht geblaettert wird (`imTextModus` haelt auch die Wische an),
   und die Ansprechen-Reihe, weil eine Ansprache eine Aussage ueber ein
   Bild ist und man hier gerade schreibt. Gewuenscht war es fuer kleine
   Schirme; es gilt ueberall, weil eine Ecke, die auf dem Telefon anders
   besetzt ist als auf dem Schreibtisch, zwei Oberflaechen sind. */
/* **Sie sitzt in der Leiste der schreibenden Blase**, rechts von
   Rueckgaengig und Wiederholen (02.09.2026). Vorher hing sie absolut oben
   rechts an der Stelle der Werkzeugspalte - quer ueber den Schirm von
   dem entfernt, woran sie arbeitet. Kein `position` mehr und kein
   `z-index`: sie liegt in einer Reihe und nicht ueber dem Bild.

   Sie steht auch dann nur da, wenn geschrieben wird - die Regel darunter
   ist dieselbe wie vorher, und `app.js` haengt sie ohnehin nur waehrend
   des Schreibens dort ein. */
.lightbox-text-tools {
  display: none;
  gap: 6px;
  /* Der Korb ist kleiner als seine Nachbarn (unten), also braucht die
     Reihe eine Achse, auf der die drei sitzen. */
  align-items: center;
  /* Etwas Luft zu den zwei Knoepfen links davon: sie sind ein anderer
     Satz Werkzeuge - der eine aendert den Text, der andere beendet ihn. */
  margin-left: 10px;
}

.lightbox.text-edit .lightbox-text-tools { display: flex; }

/* **Die Werkzeugspalte bleibt jetzt stehen** (02.09.2026). Sie ging,
   solange die drei Knoepfe des Editors in derselben Ecke standen wie sie
   - zwei Saetze Knoepfe an einem Fleck ist, was „ein Ding je Ecke"
   verbietet. Seit die drei in der Blase sitzen, ist der Grund fort, und
   was blieb, waere ein Betrachter ohne sichtbaren Ausgang: das `×` oben
   rechts weg, und das `×` in der Blase fuehrt nur aus dem Text.

   Die Pfeile bleiben fort: beim Schreiben wird nicht geblaettert
   (`imTextModus` haelt auch die Wische an), und das Trennen unten
   ebenso wenig. */
.lightbox.text-edit .lightbox-prev,
.lightbox.text-edit .lightbox-next,
.lightbox.text-edit .lightbox-frame-actions { display: none; }

/* **Nur der Anblick-Korb geht** (02.09.2026). Mit der zurueckgekehrten
   Werkzeugspalte stuenden zwei Koerbe auf einem Schirm: der grosse rote
   oben rechts nimmt das Bild oder den ganzen Anblick, der kleine in der
   Blase nimmt die Nachricht. Zwei verschieden grosse Zerstoerungen, die
   sich einen Blick teilen - genau das, was in diesem Repo sonst ueberall
   auseinandergehalten wird. Waehrend geschrieben wird, ist der kleine
   der gemeinte; der grosse ist einen Druck auf das `×` entfernt. */
.lightbox.text-edit .lightbox-delete { display: none; }

/* **Die Ansprech-Reihe bleibt, wo der Anblick nichts als Worte ist**
   (28.08.2026). Bei einer Notiz *zu* einem Anblick voller Bilder ist sie
   Beiwerk und tritt beiseite - die Ansprache ist eine Aussage ueber ein
   Bild, und man schreibt gerade. Bei einem Anblick ohne Bild ist sie die
   einzige Stelle, an der man sagen kann, was man gesehen hat; sie dort
   wegzunehmen hiesse, dass ein von Hand eingetragener Anblick nie
   angesprochen werden koennte. Dasselbe gilt fuer das obere Band und das
   Kartenband weiter unten: wann und wo es war, steht sonst nirgends. */
.lightbox.text-edit:not(.ohne-bild) .ansprech-bar { display: none; }

/* **Auf die Groesse der Leiste, in der sie jetzt stehen** (02.09.2026).
   Als Reihe ueber dem Bild waren es Scheiben von 44px wie die
   Werkzeugspalte daneben; in einer Leiste neben zwei 34px-Knoepfen waeren
   sie Fremdkoerper. Rund bleiben sie - es sind Knoepfe *am* Text und
   keine Werkzeuge *im* Text. */
.lightbox-text-tools button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  color: #fff;
  background: rgba(20, 20, 20, 0.4);
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: 50%;
  cursor: pointer;
}

.lightbox-text-tools button svg { width: 18px; height: 18px; }

.lightbox-text-tools button:hover { background: var(--bar-color); }

/* Der Korb traegt seine Farbe, wie ueberall in dieser Anwendung - er ist
   der eine der drei, der etwas wegnimmt.

   **Und er ist kleiner** (02.09.2026, auf Wunsch: *„mit einer kleinen
   mülltonne, einer checkmark und einem cancle x oben rechts im
   text(chat)-edit mode"*). Das war schon die Begruendung fuer seinen
   Platz in der Mitte: das Unwiderrufliche gehoert nicht dorthin, wo der
   Daumen von selbst hinrutscht. Eine kleinere Scheibe sagt dasselbe noch
   einmal - sie ist schwerer zufaellig zu treffen und liest sich als das,
   was sie ist: der Nebenweg zwischen zwei Wegen hinaus. */
.lightbox-text-tools button.danger {
  width: 27px;
  height: 27px;
  background: rgba(120, 30, 30, 0.85);
  border-color: rgba(255, 170, 170, 0.5);
}
.lightbox-text-tools button.danger:hover { background: #a33; }
.lightbox-text-tools button.danger svg { width: 15px; height: 15px; }

/* **Die halbe Sekunde ist um** (27.08.2026).

   Der lange Druck handelt erst beim Loslassen — sonst legte sich das
   Fenster unter den Finger, der noch aufliegt, und der Klick beim
   Loslassen schlösse es wieder (die Herleitung steht an
   `verdrahteLangdruck` in app.js). Das kostet Rückmeldung: zwischen der
   Schwelle und dem Loslassen passiert sonst nichts, und wer nicht weiss,
   ob es angekommen ist, lässt zu früh los. Also nimmt das Element in
   diesem Augenblick den Rand des Stifts an — genau den, den es gleich
   dauerhaft trägt, sobald der Stift an ist. Die Geste zeigt damit
   voraus, wohin sie führt.

   **Und wo ein langer Druck etwas bedeutet, bedeutet er nichts anderes**
   (28.08.2026, auf Wunsch: *„longpress shall not select (copy menu on
   mobile…) where an action is set for longpress"*).

   Auf einem Telefon ist ein langer Druck sonst „Text markieren" samt der
   Blase mit *Kopieren* darüber, oder das Kontextmenü — und beides nimmt
   dem Zeiger die Geste aus der Hand, ehe sie zu Ende ist. Man hält, sieht
   eine Auswahl aufblühen, lässt los und hat nichts von beidem bekommen.

   **Am Merkmal, nicht an einer Liste von Namen.**
   `data-langdruck-verdrahtet` setzt `verdrahteLangdruck` an jedes
   Element, dem es eine Geste gibt; die Regel gilt damit für das nächste
   von selbst, statt hier nachgetragen werden zu müssen. Vorher standen
   `.lightbox-time` und `.lightbox-map` namentlich da, und das war schon
   beim Schreiben eine Liste, die auseinanderläuft. */
[data-langdruck-verdrahtet] {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

/* **Ausser in Feldern darin** (28.08.2026). Seit der Notizrahmen selbst
   eine Geste trägt - ein langer Druck macht daraus einen Editor -, erbt
   der Editor, der dann darin steht, dieses `none`, und ein `textarea`,
   in dem sich nichts markieren lässt, ist ein Feld, in dem man nicht
   arbeiten kann. Was getippt wird, wird auch markiert. */
[data-langdruck-verdrahtet] input,
[data-langdruck-verdrahtet] textarea {
  -webkit-user-select: text;
  user-select: text;
}

/* **Nicht im Leuchtkasten** (23.09.2026, gemeldet: *„Longpress in
   lightbox draws a green rectangle which should not be there"*). Der Ring
   zeigt an, dass ein Halten gleich etwas auslöst - an einem Knopf oder
   einer Zeile eine brauchbare Auskunft. Im Leuchtkasten hängt die Geste
   aber an Flächen: an der Fotografie und an der Auflage des Chats, die
   über die ganze Höhe des Betrachters reicht. Dort war der Ring ein
   grüner Rahmen um das halbe Bild, mitten im Anschauen. Was der lange
   Druck dort tut, zeigt das Menü, das er öffnet. */
[data-langdruck-verdrahtet].langdruck-bereit:not(.lightbox *) {
  border-color: var(--bar-color);
  box-shadow: 0 0 0 2px var(--bar-color);
}
/* The holder *is* the Leaflet container once the map is built, so this
   beats leaflet.css's light grey backdrop while tiles load. */
.lightbox-map[hidden] { display: none; }

/* **Kein Ort: ein Streifen statt einer Fläche** (13.09.2026, auf Wunsch:
   *„even if no coords are set the »Kein Ort« banner shall be shown in
   non-edit mode"*).

   Dass ein Anblick keinen Ort hat, ist eine Auskunft und gehört gesagt -
   bis heute stand dort gar nichts, und ein Band, das zwischen zwei
   Anblicken erscheint und verschwindet, lässt einen raten, welcher der
   beiden Fälle vorliegt. Zweihundert Pixel Leere dafür wären aber genau
   der Einwand, der diese Anzeige einmal ganz hat verschwinden lassen:
   also so hoch wie die Zeile, die darin steht.

   Mit dem Stift bleibt die volle Höhe (keine Klasse): dort ist es *Ort
   setzen*, ein Ziel, das man trifft, und die Karte, die danach darin
   erscheint, ist genau so hoch. */
.lightbox-map.ohne-ort {
  height: auto;
  padding: 7px 0;
  cursor: default;
}
.lightbox-map.leaflet-container { background: #222; }

/* ---- Die Karte als Grund (17.09.2026) ----

   > when there is only txt and no media, the map shall be fullscreen
   > behind the interface buttons

   Ein Anblick aus lauter Worten hat nichts, wovor das Kartenband stuende:
   der Platz, den sonst die Fotografie einnimmt, war bis heute die
   abgedunkelte Galerie dahinter. Also nimmt ihn die Karte - derselbe
   Knoten, dieselben Gesten, nur ueber den ganzen Schirm.

   **Hinter den Knoepfen** heisst hier woertlich: `.lightbox-where` liegt
   auf `z-index: 1`, der Chat auf 5, die Ansprechen-Reihe auf 6 und die
   Knoepfe des Betrachters auf 7. Es ist nichts umzustapeln - das Band lag
   schon immer zuunterst, es war nur klein.

   **Und sie liegt *in* den Knoepfen, nicht unter ihnen** (17.09.2026, auf
   Wunsch: *„instead of the lightbox map being fullscreen when there is no
   other media it shall be within the FABs with rounded corners"*).

   Von Kante zu Kante lagen die Scheiben - `×`, Stift, die zwei Pfeile,
   *Ansprechen…* - auf der Karte. Jetzt steht die Karte in dem Ring, den
   sie bilden: ein Rechteck mit runden Ecken, um das herum sie auf dem
   Grund des Betrachters sitzen. Das ist auch das ehrlichere Bild - eine
   Fotografie fuellt den Rahmen, eine Karte ist eine Auskunft, und eine
   Auskunft hat einen Rand.

   Die drei Zahlen misst `setzeKartenrahmen` (ansprechfab.js) an den
   Knoepfen selbst; hier stehen nur die Rueckfallwerte, falls sie noch
   nicht gemessen sind - der erste Rahmen eines frisch geladenen
   Betrachters. */
.lightbox.ohne-bild .lightbox-where {
  top: var(--lightbox-karte-oben, 122px);
  left: var(--lightbox-karte-seite, 84px);
  right: var(--lightbox-karte-seite, 84px);
  bottom: var(--lightbox-karte-unten, 74px);
}

/* Kein Rand oben: eine Linie trennt ein Band vom Bild darueber, und ueber
   diesem hier ist nichts mehr. Dafuer ringsum eine, denn jetzt ist es ein
   Ding im Fenster und keine Kante davon - und `overflow: hidden` steht
   ohnehin schon am Behaelter, also schneiden die runden Ecken auch
   Leaflets Kacheln. */
.lightbox.ohne-bild .lightbox-map {
  height: 100%;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: var(--lightbox-karte-radius);
}

.lightbox.ohne-bild .lightbox-map-schleier {
  border-radius: var(--lightbox-karte-radius);
}

/* **Auf dem Telefon fast die ganze Breite, und die Pfeile liegen darauf**
   (18.09.2026, auf Wunsch: *„on phone make it near-full-width with the
   arrows over it"*).

   Der Ring aus Knoepfen ist auf einem Schreibtisch ein Rand; auf 390px
   ist er die halbe Karte: zweimal 84px fuer die Pfeile lassen 222px
   uebrig, und darin ist ein Revier nicht mehr zu lesen. Die Pfeile sind
   Scheiben mit halbdurchsichtigem Grund - sie liegen ueber einer
   Fotografie genauso, und das war nie das Problem.

   Nur die Seiten: oben bleibt der Rahmen unter der Werkzeugspalte, unten
   ueber der Ansprechen-Reihe, denn das sind Flaechen und keine Scheiben.
   Und es ist der Zwoelfer, der in diesem Betrachter ueberall der Rand ist
   (`ANSPRECH_SIDE`).

   Die zwei Zeilen koennen die gemessenen Werte einfach ueberschreiben:
   `setzeKartenrahmen` setzt eine *Variable* am Leuchtkasten, und was
   daraus wird, entscheidet das Stylesheet. */
@media (max-width: 700px) {
  .lightbox.ohne-bild .lightbox-where {
    left: 12px;
    right: 12px;
  }
}

/* **Der Schleier: keine Koordinaten, aber trotzdem eine Karte**
   (17.09.2026, auf Wunsch: *„if there is no coords selected, the map
   shall still show centered on the visible elements with the Kein Ort
   black (70% opacity) screen above it (still behind the other interface
   buttons)"*).

   Der Streifen `Kein Ort` (13.09.2026) bleibt, wo er hingehoert - unter
   einer Fotografie, wo der Platz der Fotografie gehoert. Wo die Karte der
   Grund ist, gibt es diesen Zwang nicht: der Plan steht da, wie er ist,
   und das Schwarz darueber sagt, dass dieser Anblick nicht darin
   verzeichnet ist. Beides zusammen ist mehr als der Streifen, denn die
   Umgebung ist auch ohne Nadel eine Auskunft.

   70%: die Karte bleibt lesbar, das Wort davor gewinnt. `pointer-events:
   none`, damit der lange Druck an die Karte darunter kommt - er ist der
   Weg, dem Anblick seinen ersten Ort zu geben. */
.lightbox-map-schleier {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.7);
  pointer-events: none;
}

.lightbox-map-schleier[hidden] { display: none; }

.lightbox-map-schleier .wort {
  padding: 8px 16px;
  border-radius: 16px;
  background: rgba(20, 20, 20, 0.55);
  color: rgba(255, 255, 255, 0.85);
  font-size: 15px;
  font-weight: bold;
  letter-spacing: 0.02em;
  text-align: center;
}

/* No position on the photo: the circle stays, saying so, because it's
   also the way to give it one. */
.lightbox-map-empty {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  text-align: center;
  color: rgba(255,255,255,0.7);
}

/* An Anblick with no picture in it: the words, centred, where the
   photograph would be. Not a grey rectangle with a caption - it has
   nothing to show, and pretending otherwise is a picture that never
   loads. */
/* ---- Der Chat, geoeffnet (02.09.2026) ----

   **Blasen auf dem schwarzen Grund** (nachgeschaerft am 02.09.2026, auf
   Wunsch: *„the chat bubbles shall be directly on the black lightbox
   background, without the white window"*).

   Es war zuerst ein *Blatt* (eine Spalte Prosa in Buchbreite, hell im
   schwarzen Betrachter), dann fuer eine Stunde ein weisses *Fenster* mit
   Rand und Schatten. Beides war eine Flaeche zu viel: der Leuchtkasten
   ist schon ein Raum, und ein Kasten darin, in dem die Blasen liegen,
   heisst, dass die Nachrichten zweimal eingerahmt sind - einmal von der
   Blase und einmal von dem Blatt, auf dem sie liegt. Ein Chat auf einem
   Telefon hat auch keinen Kasten; er hat einen Grund, und der ist hier
   der schwarze.

   Was bleibt, ist die **Geometrie**: derselbe Kasten, nur unsichtbar. Er
   haelt die Breite (eine Blase, die ueber den halben Schirm laeuft, ist
   keine mehr), er sammelt die Blasen am Fuss, und er rollt, wenn es mehr
   werden, als hineinpassen.

   **Beim Lesen so hoch wie das Gesagte, beim Schreiben so hoch wie der
   Platz.** Mit Rand war eine feste Hoehe richtig - ein Fenster hat eine.
   Ohne Rand waere sie ein leerer Bereich, den niemand sieht und in dem
   die eine Blase trotzdem weit unten haengt; also `height: auto` und eine
   Obergrenze. Beim Schreiben zurueck zur festen Hoehe: dort ist der
   Kasten eine Arbeitsflaeche, und ein Editor, der mit dem ersten Wort
   aufspringt, schiebt sich unter dem Finger weg. */
.lightbox-text {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  width: min(560px, 88vw);
  /* Die Luecke in der Reihe am Fuss. Daneben stand bis zum 09.09.2026
     `--chat-share-raum` - die Breite des Kettenknopfes plus diese
     Luecke -, um die sich der Lauf darueber polsterte, damit die Blasen
     ueber dem *Feld* endeten und nicht ueber dem Knopf. Der Knopf ist
     fort (er steht oben neben dem Herzen), also ist auch der Raum fort:
     das Feld sitzt jetzt selbst an der Kante, und die Blasen enden
     darueber, ohne dass etwas dafuer freigehalten werden muesste. */
  --chat-fuss-luft: 10px;
  padding: 0;
  color: #f2f2f0;
  font-size: 16px;
  line-height: 1.55;
  text-align: left;

  /* **Eine Auflage, kein Rahmen** (03.09.2026). Er war ein Kind im Fluss
     des Leuchtkastens und nahm damit die Stelle des Bildes ein; jetzt
     liegt er darueber, absolut gesetzt, und das Bild bleibt darunter
     stehen. Von oben bis an das Kartenband - `--lightbox-floor` misst
     `placeAnsprechBar` ohnehin, und das Feld am Fuss soll ueber der Karte
     enden und nicht auf ihr.

     `z-index` 5: ueber dem Bild und der Ansprech-Leiste, unter den
     Knoepfen des Betrachters (7) und unter dem Ansprech-Waehler, der die
     ganze Flaeche fuer sich nimmt. */
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  top: calc(var(--lightbox-ceiling, 116px) + 8px);
  /* **Und ueber der Bildschirmtastatur** (04.09.2026, gemeldet: *„adding
     a lightbox message on Android chrome is e.g. now behind the
     on-screen keyboard"*).

     Die Seite steht auf `interactive-widget=overlays-content`: der
     Browser legt die Tastatur ueber den Inhalt und ruehrt weder Layout
     noch sichtbares Fenster an. Ein Kasten mit `bottom` bleibt also
     genau dort, wo er war - und das Feld an seinem Fuss verschwindet
     darunter.

     Gemessen wird die Hoehe schon (`tastaturMass` in nav.js, seit dem
     19.08. fuer die Suchreihe): `navigator.virtualKeyboard.boundingRect`
     unter Chrome, `visualViewport` unter iOS, das Groessere von beiden.
     Hier wird sie nur noch gelesen. Der Kasten steigt nicht, er wird
     kuerzer - `top` bleibt stehen -, und der Lauf der Blasen scrollt
     ohnehin. */
  bottom: calc(var(--lightbox-floor, 70px) + var(--tastatur-hoehe, 0px) + 8px);
  z-index: 5;
}

/* **Der Fuss des Chats bleibt ueber dem Anzeigeknopf** (24.09.2026). Der
   Knopf sitzt seit dem 22.09. in der Ecke unten rechts, und der Platz war
   frei, weil der Chat ueber dem Kartenband endete. Seit die Karte ein
   Dreieck links ist und die Reihe am Fuss steht, endete er auf dem Knopf.
   Also nie tiefer als Knopf (12 + 34) plus Luft. Nicht ueber dem Grund:
   ohne Bild gibt es den Knopf nicht (`renderAnsichtKnopf`). */
.lightbox { --lightbox-ecke-unten: 56px; }
.lightbox:not(.ohne-bild) .lightbox-text {
  bottom: calc(max(var(--lightbox-floor, 70px) + 8px, var(--lightbox-ecke-unten))
               + var(--tastatur-hoehe, 0px));
}

/* **Rechts, wo ein Bild darunter liegt** (03.09.2026, auf Wunsch: *„the
   chat shall be rightbound on the page when there is media and as it is
   now if the anblick does not contain any media"*).

   Mittig war er, solange er der Rahmen war - dort *ist* er das, was man
   ansieht. Als Auflage ueber einer Fotografie ist er im Weg: er liegt
   genau auf dem, worueber er redet. An die rechte Kante geschoben bleibt
   die linke Haelfte des Bildes frei, und die Blasen haengen ohnehin an
   dieser Seite - der Chat rueckt also dorthin, wo er schon hinzeigt.

   **Ein Anblick ohne Bild bleibt mittig**: dort gibt es nichts, dem man
   ausweichen muesste, und der Chat ist das Bild.

   Der Abstand war 84px - 56px Scheibe des `›` plus 20px Rand plus Luft -,
   damit kein Pfeil auf den Blasen laege. **Seit dem 04.09.2026 sind es
   20px** (auf Wunsch: *„the chat overlay shall be even closer to the
   right. the fabs shall be on top of it"*), also dieselbe Kante, an der
   auch Pfeile und Werkzeugspalte haengen. Sie liegen jetzt **ueber** ihm
   (`z-index` 6 und 7 gegen seine 5) statt neben ihm: was man *tut*, steht
   ueber dem, was gesagt wurde. Der Preis ist der Zusammenstoss von
   damals - eine Blase, die bis unter das `›` reicht, gibt dort ihre
   Gesten an den Pfeil ab. Auf dem Telefon haelt der Lauf sich dessen Bahn
   weiter mit Polster frei (siehe den Medienblock weiter unten); auf dem
   Schreibtisch ist es die eine Scheibe in der Mitte der rechten Kante,
   und das war der Wunsch.

   Nur wo Platz dafuer ist: auf einem schmalen Schirm bliebe links nichts
   uebrig, also bleibt es dort bei der Mitte (siehe die Regel im
   Medienblock weiter unten). */
@media (min-width: 900px) {
  /* **Das Feld steht auf der Linie des Ansprechen-Knopfes** (04.09.2026,
     auf Wunsch: *„on screens with enough width the chat text field shall
     be on the same level as the ansprechen fab/tags"*).

     Der Chat endete ueber dem ganzen Ansprech-Band; jetzt endet er dort,
     wo dessen Reihe endet - und weil er rechtsbuendig steht und die
     Reihe links beginnt, stehen die zwei nebeneinander statt
     uebereinander. `--lightbox-ansprech-fuss` misst `placeAnsprechBar`,
     nachdem die Antworten umgebrochen sind; wo die Reihe gar nicht
     dasteht, fehlt die Zahl und es bleibt beim eigenen Polster.

     Nur hier drin, wo der Chat ohnehin rechts steht: mittig laege das
     Feld sonst auf dem Knopf. */
  .lightbox .lightbox-text {
    /* Dieselbe Zugabe wie oben - hier um die gemessene Kante der
       Ansprech-Reihe herum, statt um das Polster. */
    bottom: calc(var(--lightbox-ansprech-fuss,
                     calc(var(--lightbox-floor, 70px) + 8px))
                 + var(--tastatur-hoehe, 0px));
  }
  .lightbox:not(.ohne-bild) .lightbox-text {
    bottom: calc(max(var(--lightbox-ansprech-fuss,
                         calc(var(--lightbox-floor, 70px) + 8px)),
                     var(--lightbox-ecke-unten))
                 + var(--tastatur-hoehe, 0px));
  }

  /* **Auch ohne Bild** (04.09.2026, auf Wunsch: *„das chatoverlay soll
     auch bei den anblicken wo es keine anderen medien gibt rechtsbündig
     sein"*). Der Grund von gestern - mittig laege er auf dem, worueber er
     redet - traf nur die Haelfte: eine Auflage, die je nach Anblick ihre
     Seite wechselt, ist eine, die beim Blaettern springt. Der Chat steht
     jetzt immer rechts, und `.ohne-bild` entscheidet nur noch, was
     *sonst* auf dem Schirm ist. */
  .lightbox .lightbox-text {
    left: auto;
    /* **Bis an die Kante, damit der Rollbalken dort liegt** (04.09.2026,
       auf Wunsch: *„have its scroll bar moved directly next to the right
       side of the page"*).

       Hier standen 20px, und der Balken des Laufs lag damit 20px vom Rand
       - ein Streifen Schwarz zwischen ihm und der Kante, in dem nichts
       steht. Der Kasten reicht jetzt bis 0; was den Abstand haelt, ist das
       Polster *innen*: der Lauf polstert rechts (die Blasen bleiben, wo
       sie waren, der Balken sitzt aussen am Rand des Kastens), und die
       Reihe am Fuss polstert um dieselben 20px, damit der Kettenknopf an
       derselben Kante steht wie die Werkzeugspalte darueber.

       20px breiter, damit die linke Kante des Chats bleibt, wo sie war. */
    right: 0;
    width: min(580px, 88vw);
    transform: none;
    /* **Bis nach oben, wo Platz ist** (04.09.2026, auf Wunsch: *„where
       size allows it, the chat window shall go up to the top of the
       screen"*).

       Hier stand `max(Band + 8, 176px)`, und die 176 waren die Hoehe der
       Werkzeugspalte - aus der Sorge, die oberste Blase verschwaende
       hinter dem `×`. Nachgemessen stimmt das nicht: die Spalte steht bei
       x 1216-1260, der Chat endet bei 1196. Sie liegen nebeneinander und
       nicht uebereinander, und die 176px waren 176px Chat, die niemand
       gebraucht hat.

       Was oben wirklich liegt, ist das Band mit Stempel und Streifen -
       beide **linksbuendig**, der Chat rechts. Ein sehr langer Streifen
       reicht bis kurz vor das `×`; dann rollt die oberste Blase unter ihm
       durch. Das ist der Preis, und er ist klein: ein Chat fuellt sich
       von unten, und oben steht meistens nichts.

       Nur hier drin - auf einem schmalen Schirm steht der Chat unter dem
       Band, weil dort Stempel und Streifen quer ueber ihm laegen. */
    top: 8px;
  }

  /* Die zwei Polster, die aus `right: 0` wieder den alten Abstand machen -
     der Balken bleibt aussen an der Kante. Der Lauf hielt hier zusaetzlich
     den Raum des Kettenknopfes frei; seit der oben steht, ist es
     dieselbe Zahl wie unten. */
  .lightbox .lightbox-text .chat-fuss { padding-right: 20px; }
  .lightbox .lightbox-text .chat-lauf { padding-right: 22px; }
}

.lightbox-text[hidden] { display: none; }

/* **Der Lauf rollt, das Fenster steht.** Zwei Knoten fuer zwei Dinge:
   laege der Ueberlauf am Fenster, rollte sein Rand mit weg, sobald der
   Chat laenger wird als es - derselbe Grund, aus dem `md-editor-huelle`
   den Editor und nicht die Karte scrollen laesst.

   **Am Fuss und rechts.** `justify-content: flex-end` allein waere die
   bekannte Falle - laeuft der Inhalt ueber, ist oben nichts mehr zu
   erreichen -, also traegt das erste Kind ein `margin-top: auto`. Das
   schiebt genauso nach unten und laesst den Ueberlauf heil. */
.chat-lauf {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  /* Die senkrechte Achse gehoert dem Rollen, die waagrechte dem
     Betrachter und seinen Wischen (siehe `enableChatZug`). */
  touch-action: pan-y;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  padding: 2px;
}

.chat-lauf > :first-child { margin-top: auto; }

/* Waehrend gezogen wird, liegt die Hand darauf - und zwar ueberall im
   Leuchtkasten, denn seit dem 03.09. rollt der Chat von jeder Stelle aus
   (`enableChatZug`). Die Klasse steht darum am Leuchtkasten und nicht am
   Lauf. */
.lightbox.chat-zieht,
.lightbox.chat-zieht * { cursor: grabbing; }

/* **Die Blase.** Rechts, weil sie eine eigene Nachricht ist - heute sind
   alle eigen, und `.eigen` ist die Klasse, die fehlt, sobald es fremde
   gibt. Die eine eckige Ecke unten rechts ist der Zipfel: sie sagt, an
   welcher Seite die Blase haengt, ohne dass ein Dreieck gezeichnet werden
   muesste, das im Dunkelmodus zweimal eingefaerbt werden will. */
.chat-blase {
  box-sizing: border-box;
  max-width: 86%;
  padding: 9px 13px;
  border-radius: 16px;
  font-size: 15px;
  line-height: 1.5;
}

/* Das Gruen der Anwendung, deckend genug, dass die Schrift darauf steht
   und nicht darin schwimmt - was darunter liegt, ist ein schwarzer Grund
   und keine Fotografie, also braucht es keine zweite Sicherheit.

   **Eine Farbe, kein Dunkelmodus.** Der Grund ist immer schwarz, hell
   wie dunkel; eine Blase, die mit `body.dark` umschlaegt, waere im hellen
   Modus eine helle Flaeche auf Schwarz - genau das weisse Fenster, das
   hier gerade gegangen ist. */
.chat-blase.eigen {
  align-self: flex-end;
  border-bottom-right-radius: 5px;
  background: rgba(58, 99, 71, 0.92);
  border: 1px solid rgba(150, 200, 165, 0.35);
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.45);
  color: #f2f6f2;
}

/* Auf dem Gruen ist das Gruen der Anwendung als Linkfarbe unlesbar, und
   der Strich am Zitat ebenso. Beide hell. */
.chat-blase a { color: #d6ecdb; }
.chat-blase blockquote { border-left-color: rgba(255, 255, 255, 0.45); }

/* Der erste Absatz traegt keinen Abstand nach oben und der letzte keinen
   nach unten: sonst sitzt der Text nicht in der Blase, sondern haengt
   darin. */
.chat-wort > :first-child { margin-top: 0; }
.chat-wort > :last-child { margin-bottom: 0; }

/* **An der Ecke, auf die der Zipfel zeigt** (03.09.2026, auf Wunsch:
   *„notes have there own datetime … which is shown in each bubble at the
   pointing corner"*).

   Sie stand einen Tag lang unten *links*. Der Zipfel ist die eckige Ecke
   unten rechts; er sagt, wem die Blase gehoert, und die Zeit sagt, wann -
   an einer Ecke sind das zusammen eine Bildunterschrift, an zwei Ecken
   sind es zwei Angaben, die man einzeln suchen muss.

   Klein und blass: eine Fussnote an der Nachricht und nicht deren
   Anfang. */
.chat-zeit {
  margin-top: 5px;
  font-size: 11px;
  line-height: 1;
  opacity: 0.72;
  text-align: right;
}

/* ---- Das Feld am Fuss (03.09.2026) ----

   Es sitzt im Fluss der Auflage unter dem Lauf, also endet der Lauf
   darueber und das Feld liegt ueber dem Kartenband. Dieselbe Blasenfarbe
   wie eine eigene Nachricht: was man hineinschreibt, wird eine.

   `align-items: flex-end`, damit der Knopf am Fuss des wachsenden Feldes
   bleibt und nicht in dessen Mitte wandert. */
/* **Die Reihe am Fuss** (04.09.2026): das Feld und der Kettenknopf, der
   bis heute oben rechts in der Werkzeugspalte stand. Rechtsbuendig, weil
   das die Kante ist, an der auch die Blasen darueber haengen - und der
   Knopf ganz aussen, weil dort der Knopf zum Schreiben stand und die
   gelernte Stelle die Kante ist.

   `align-items: flex-end`, damit der Kettenknopf am Fuss des wachsenden
   Feldes bleibt und nicht in dessen Mitte wandert. */
.chat-fuss {
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  gap: var(--chat-fuss-luft, 10px);
  flex: 0 0 auto;
  align-self: stretch;
  margin-top: 8px;
}

.chat-komponist {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  flex: 0 0 auto;
  padding: 6px;
  background: rgba(58, 99, 71, 0.92);
  border: 1px solid rgba(150, 200, 165, 0.35);
  border-radius: 22px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.45);
}

.chat-komponist[hidden] { display: none; }

/* **Zugeklappt ist er ein Knopf** (04.09.2026). Die Huelle bleibt
   dieselbe - Farbe, Rand, Schatten -, sie schrumpft nur auf ihren
   Inhalt: so waechst der Knopf beim Druck zum Feld, statt dass eines
   das andere ersetzt.

   Aufgeklappt nimmt sie die Breite des Chats und ein Polster links,
   damit die Schrift nicht am Rand klebt. */
.chat-komponist:not(.offen) .chat-eingabe,
.chat-komponist:not(.offen) .chat-senden { display: none; }

.chat-komponist.offen {
  /* Aufgeklappt nimmt es, was der Kettenknopf uebriglaesst. */
  flex: 1 1 auto;
  min-width: 0;
  padding-left: 12px;
  border-radius: 18px;
}

.chat-komponist.offen .chat-oeffnen { display: none; }

.chat-oeffnen {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  padding: 0;
  color: #f2f6f2;
  background: transparent;
  border: none;
  border-radius: 50%;
  cursor: pointer;
}

.chat-oeffnen:hover { background: rgba(20, 20, 20, 0.35); }

.chat-eingabe {
  flex: 1 1 auto;
  min-width: 0;
  /* Waechst mit dem Text (siehe `passeChatFeldAn`), rollt danach. */
  height: 34px;
  max-height: 120px;
  padding: 8px 0;
  border: none;
  outline: none;
  resize: none;
  background: transparent;
  color: #f2f6f2;
  font-family: inherit;
  font-size: 15px;
  line-height: 1.35;
}

.chat-eingabe::placeholder { color: rgba(242, 246, 242, 0.55); }

.chat-senden {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  padding: 0;
  color: #f2f6f2;
  background: rgba(20, 20, 20, 0.3);
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: 50%;
  cursor: pointer;
}

.chat-senden:hover { background: rgba(20, 20, 20, 0.6); }

/* **Von rechts weg** (auf Wunsch: *„soll als chat bubble von rechts weg
   aufscheinen"*). Kurz, und nur wenn der Chat wirklich aufgeht: die
   Klasse `kommt` vergibt `baueChatBlase` nur beim ersten Zeichnen je
   Anblick, sonst floege die Blase bei jedem vergebenen Tag neu herein. */
@keyframes chat-herein {
  from { opacity: 0; transform: translateX(30px); }
  to { opacity: 1; transform: none; }
}

.chat-blase.kommt { animation: chat-herein 220ms ease-out; }

@media (prefers-reduced-motion: reduce) {
  .chat-blase.kommt { animation: none; }
}

/* **Die Worte sagen, dass man sie anfassen kann** (26.08.2026, seit dem
   02.09.2026 an der Blase). Nur der Zeiger, kein zweiter Rahmen: die
   Blase ist schon eine Blase. Wo nicht geschrieben werden darf, fehlt die
   Klasse und damit auch der Zeiger. */
.chat-blase.anfassbar { cursor: text; }

/* ---- Schreiben im Leuchtkasten ----

   **Das Blatt nahm auf dem Telefon den ganzen Schirm** (26.08.2026, auf
   Wunsch: *„fullscreen on small screens"*) - 72% Hoehe minus Tastatur
   minus Ansprech-Leiste liessen einen Streifen von zwei, drei Zeilen, in
   dem man nicht sah, was man schreibt. Die Regel dafuer stand hier: absolut
   an alle vier Kanten geheftet, `100dvh`, kein Radius.

   **Sie ist am 02.09.2026 gegangen** (auf Wunsch: *„wie ein chat-fenster
   zentriert in lightbox. nicht mehr vollbild"*). Der Platz, um den es
   ging, kommt jetzt anders zusammen: die beiden Baender treten beim
   Schreiben weiter beiseite (die Regeln darunter, unveraendert), und die
   Hoehe des Fensters rechnet in `dvh` statt in Prozent, sobald geschrieben
   wird - `dvh` rechnet die eingeblendete Tastatur heraus, wofuer der
   Prozentwert gegen den festen Leuchtkasten zu gross ist. Ein Fenster
   bleibt es dabei: mit Rand, mit Radius, in der Mitte.

   Der Ausschnitt ist `max-height: 560px` **und** schmal, denn es ist die
   Tastatur, die den Platz nimmt, und die kommt auf einem quer gehaltenen
   Telefon genauso. */
@media (max-width: 700px), (max-height: 560px) {

  /* **Und der Lauf haelt die Bahn der Pfeile frei** (02.09.2026,
     gesehen und nicht vermutet). Auf einem Telefon ist das Fenster
     beinahe so breit wie der Schirm, und `‹` und `›` liegen als Scheiben
     von 56px 20px vor dessen Kanten - also lagen sie ueber der Blase.
     Ueber einem *Bild* ist das gewollt (sie blaettern es), ueber der
     Blase nicht: sie ist das Ziel des langen Drucks, und ein Pfeil
     darauf faengt genau die Geste ab, mit der man schreiben wollte.

     Das Polster im Lauf statt eines schmaleren Fensters: ein
     Chatfenster, das auf dem Telefon 240px breit ist, waere kein
     Chatfenster mehr. So bleibt das Fenster, wo es war, und nur die
     Blasen ruecken aus der Bahn. */
  .chat-lauf {
    /* **Rechts, wo die Blasen sind.** 56px Scheibe plus 20px Abstand vom
       Schirmrand, minus die paar Pixel, die der Kasten selbst schmaler
       ist als der Schirm - gemessen und nicht gerechnet, mit Luft. Links
       nur ein wenig: dort haengt heute nichts, und dieselbe Zahl auf
       beiden Seiten nimmt einer langen Nachricht 50px Breite fuer nichts.
       **Wer eine fremde Blase links einhaengt, kommt hierher zurueck.** */
    padding-left: 8px;
    /* Die Bahn des `›`. Bis zum 09.09.2026 stand hier ein `max()` mit dem
       Raum des Kettenknopfes darin; der Knopf ist fort, und die 58 des
       Pfeils waren ohnehin die groessere der beiden Zahlen. */
    padding-right: 58px;
  }

  /* **Und die Lesespalte tritt beiseite.** Zeit und Karte hängen oben
     links und lagen quer über den ersten Zeilen - gesehen, nicht
     vermutet. Wer auf einem Telefon schreibt, schreibt; wo es war, steht
     einen Rahmen weiter am Bild, und dort ist der Stempel auch
     anzutippen. Nur in genau diesem Fall: schmaler Schirm, Stift an,
     Notizrahmen. */
  /* **Und beim Schreiben treten beide Baender beiseite** (28.08.2026).
     Der Satz von damals stimmt weiter, nur ist es jetzt mehr als eine
     Lesespalte: Streifen und Stempel oben, Karte und Ansprechen-Reihe
     unten nehmen zusammen ueber ein Drittel eines Telefons, und mit der
     Tastatur davor bleibt ein Schlitz. Wer auf einem Telefon schreibt,
     schreibt; was es war und wo es war, steht einen Rahmen weiter am
     Bild.

     Das Polster geht von selbst mit: `placeAnsprechBar` misst, was
     wirklich dasteht, und `display: none` misst 0. Es ueber die Variablen
     zu erzwingen ginge nicht - die stehen inline am Element und schlagen
     jede Regel hier. */
  .lightbox.text-edit:not(.ohne-bild) .lightbox-top,
  .lightbox.text-edit:not(.ohne-bild) .lightbox-where,
  .lightbox.text-edit:not(.ohne-bild) .ansprech-bar { display: none; }
}

/* Die Knopfleiste über dem Editor, und das Feld darunter, das den Rest
   nimmt. Ohne den Kasten stünde die Leiste in der Zeilenliste und wäre
   eine Zeile, die keine ist. */
.md-editor-huelle {
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: 100%;
}

/* **Der Editor scrollt, nicht das Blatt** (auf Wunsch: *„easy scroll"*).
   Vorher lag das Überlaufen beim `.lightbox-text`, also scrollte die
   Knopfleiste mit weg, sobald die Notiz länger war als der Schirm - und
   Rückgängig war genau dann nicht da, wenn man es braucht. */
.md-editor-huelle > .md-editor {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  /* Auf einem Telefon ruckelt eine Liste aus Textfeldern ohne das. */
  -webkit-overflow-scrolling: touch;
}

.md-werkzeuge {
  display: flex;
  justify-content: flex-end;
  gap: 6px;
  flex: 0 0 auto;
  margin-bottom: 6px;
  /* Sie bleibt stehen, während die Worte darunter laufen. */
  position: sticky;
  top: 0;
  z-index: 1;
}

.md-werkzeug {
  width: 34px;
  height: 34px;
  padding: 0;
  font-size: 17px;
  line-height: 1;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--control-bg);
  color: var(--fg);
  cursor: pointer;
}

.md-werkzeug:hover:not(:disabled) { background: var(--bar-color); color: #fff; }

/* Nichts zurückzunehmen ist ein echter Zustand und kein Fehler - der
   Knopf bleibt stehen und wird blass, statt zu verschwinden und die
   Leiste unter dem Finger zu verschieben. */
.md-werkzeug:disabled { opacity: 0.35; cursor: default; }

/* Writing. **Der Editor aus markdown.js in der Karte** - die Zeile unter
   dem Cursor zeigt ihre Zeichen, jede andere ist schon gerendert. Er
   bringt seinen eigenen Rahmen und seine eigene Fläche mit; hier wird
   beides abgeräumt, denn was auf dem Schirm steht, soll weiter die Notiz
   sein und nicht ein Formular in der Notiz. Die Schrift ist die der
   Karte, nicht die des Feldes, sonst schrumpft der Text beim Druck auf
   den Stift. */
/* In der Blase: sie nimmt den Lauf, der Editor nimmt die Blase. Die
   Blase *ist* das Eingabefeld - so, wie die Blase daneben die Nachricht
   ist -, also traegt sie die Hoehe und der Editor darin gar keine. */
.chat-blase.schreibt {
  align-self: stretch;
  /* **So gross wie eine Nachricht, nicht so gross wie der Lauf**
     (03.09.2026). Sie nahm die ganze Hoehe, solange sie die einzige
     Blase war; in einem Chat mit mehreren schiebt sie damit alle
     anderen aus dem Bild. Eine Untergrenze, damit man beim Tippen etwas
     sieht, und eine Obergrenze, damit eine lange Nachricht den Rest
     nicht doch verdraengt. */
  flex: 0 0 auto;
  min-height: 96px;
  max-height: 65%;
  /* **So breit wie der Kasten**, seit er unsichtbar ist (02.09.2026).
     Eine schreibende Blase auf 86% liess links einen Rand aus nichts -
     mit weissem Fenster war das der Rand des Fensters, ohne es ist es
     eine Fluchtlinie, die keiner sieht. Die gelesene Blase bleibt
     schmal: die haengt an einer Kante. Diese ist die Flaeche. */
  width: 100%;
  max-width: 100%;
  display: flex;
  flex-direction: column;
  padding: 8px 10px;
}

.chat-blase.schreibt > .md-editor-huelle { min-height: 0; }

/* Rueckgaengig und Wiederholen sitzen auf der Blase: durchsichtig, damit
   sie nicht als zweite Flaeche darin liegen. */
.chat-blase.schreibt .md-werkzeug {
  background: rgba(20, 20, 20, 0.25);
  border-color: rgba(255, 255, 255, 0.4);
  color: #f2f6f2;
}

.chat-blase.schreibt .md-werkzeug:hover:not(:disabled) {
  background: rgba(20, 20, 20, 0.55);
  color: #fff;
}

/* Die Leiste traegt zwei Saetze Knoepfe und muss sie auf eine Achse
   stellen; ihr Fuss ist die Kante, an der die Blase beginnt. */
.chat-blase.schreibt .md-werkzeuge { align-items: center; }

/* **Der Rahmen um das Feld war nie fort, obwohl hier stand, dass er es
   sei** (gefunden am 02.09.2026). Die Regel hiess
   `.lightbox-note-source.md-editor` - beide Klassen an *einem* Knoten -,
   und `lightbox-note-source` haengt seit der Werkzeugleiste (26.08.2026)
   an der Huelle, `md-editor` am Feld darin. Sie traf also nichts, und im
   Blatt stand ein Formular, dessen Rand niemand mehr begruenden konnte.
   In der Blase faellt es auf, weil die Blase selbst schon der Kasten ist:
   ein Kasten im Kasten, beide mit Radius. Als Nachfahren-Regel trifft sie
   das Feld und tut, was hier immer stehen sollte. */
.lightbox-note-source .md-editor {
  min-height: 0;
  border: none;
  border-radius: 0;
  padding: 0;
  background: transparent;
  color: inherit;
  font-size: 15px;
  line-height: 1.6;
}

.lightbox-note-source .md-editor:focus-within { border-color: transparent; }

.lightbox-note-source .md-line,
.lightbox-note-source .md-line-edit {
  font-size: 15px;
  line-height: 1.6;
}

/* Die gerenderten Zeilen erben die Farbe der Karte - im hellen Lightbox
   ist die Karte hell und `--fg` wäre im Dunkelmodus weiß auf weiß. */
.lightbox-note-source .md-line-edit { color: inherit; }

/* Der Rahmen, den das Fenster beim Schreiben trug, ist mit dem Fenster
   gegangen: ein Umriss um eine durchsichtige Flaeche ist ein Rechteck um
   nichts. Dass geschrieben wird, sagt die Blase, in der es geschieht. */

/* The note's thumbnail in the strip: **the note itself**, small. A
   thumbnail of a photograph is that photograph, and a drawing of a page
   would have said only "there is a note" - in a row whose job is telling
   frames apart, that is the one thing it must say more than.

   Paper-coloured like the card it opens into, so the two are plainly the
   same object at two sizes. Clipped at the top left rather than centred:
   a note is read from its first line, and centring would show its middle. */
/* **Die Kachel, mit der ein Bild dazukommt** (26.08.2026). Gestrichelt
   und leer: sie ist kein Rahmen des Anblicks, sondern der Platz, an dem
   einer entstehen kann - dieselbe Sprache, in der der Revierplan ein
   fremdes Element gestrichelt zeichnet. Damit steht sie neben dem
   Standbildwähler, ohne mit ihm verwechselt zu werden: der ist eine
   volle Fläche mit einer Blende darauf. */
/* **Noch keine Notiz, aber der Platz dafür** (26.08.2026). Dieselbe
   gestrichelte Sprache wie die Upload-Kachel am anderen Ende: nichts ist
   da, hier könnte etwas hin.

   **Hell gestrichelt seit dem 02.09.2026.** Der Unterschied zur vollen
   Kachel war der Rahmen und nicht die Farbe, solange beide *das Blatt*
   waren, auf dem geschrieben wird; das Blatt ist fort, die Kachel ist
   durchsichtig, und ein dunkler Strich auf dem dunklen Streifen ist der
   Rahmen, den man auf dem Telefon nicht findet - derselbe Fehler, den
   die Upload-Kachel schon einmal gemacht hat. Zwei Zeilen weniger: die
   Dunkelmodus-Fassung ist mit dem hellen Grund gegangen, den sie
   umkehren sollte. */
.strip-shot.is-note.strip-note-neu {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed rgba(255, 255, 255, 0.5);
  color: #d8d8d8;
}

.strip-shot.strip-upload {
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.08);
  border: 1px dashed rgba(255, 255, 255, 0.55);
  color: #fff;
  /* Ein dunkler Ring darum. Der Streifen liegt über dem Bild, und das ist
     mal dunkel und mal hell - eine weiss gestrichelte Kachel auf hellem
     Grund ist unsichtbar, und genau so war sie auf dem Telefon nicht zu
     finden, während sie auf dem Schreibtisch deutlich stand. */
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);
}

.strip-shot.strip-upload:hover {
  background: rgba(255, 255, 255, 0.18);
  border-style: solid;
}

/* **Die Kachel des Chats ist der Chat in klein** (02.09.2026, auf Wunsch:
   *„redesign the thumbnail image for the chat to show the bubbles on
   transparent background. still, the whole 'thumbnail' shall be
   clickable"*).

   Sie war ein weisses Blaettchen mit 5,5px Text darauf - die Kachel des
   *Blattes*, das es seit heute nicht mehr gibt. Ein Vorschaubild sagt in
   diesem Streifen, **welcher Rahmen** das ist, und der Rahmen ist jetzt
   ein Chat: also Blasen, und dahinter der Streifen selbst. Durchsichtig
   ist dabei keine Zierde, sondern dieselbe Aussage wie im Leuchtkasten -
   dort liegen die Blasen auf dem schwarzen Grund und nicht auf einem
   Blatt.

   **Anklickbar bleibt die ganze Kachel**: der Knopf behaelt seine 64x48,
   nur seine Farbe geht. Was darin liegt, nimmt keine Zeiger an
   (`pointer-events: none` unten), also trifft jeder Druck den Knopf -
   auch der neben der Blase. */
.strip-shot.is-note {
  background: transparent;
  color: #f2f6f2;
  overflow: hidden;
  padding: 0;
  /* Die Blasen sammeln sich unten rechts, wie im Chat selbst. */
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: flex-end;
}

.strip-note-mini {
  /* Eine Blase, kein Blatt: so breit wie ihr Text, hoechstens fast die
     Kachel, mit dem Zipfel unten rechts wie ihr grosses Vorbild. */
  max-width: 88%;
  max-height: 100%;
  padding: 3px 4px;
  box-sizing: border-box;
  font-size: 5.5px;
  line-height: 1.35;
  text-align: left;
  overflow: hidden;
  overflow-wrap: anywhere;
  pointer-events: none;
  background: rgba(58, 99, 71, 0.92);
  border: 0.5px solid rgba(150, 200, 165, 0.4);
  border-radius: 6px;
  border-bottom-right-radius: 2px;
  margin: 3px;
}

.strip-note-mini h1,
.strip-note-mini h2,
.strip-note-mini h3 { font-size: 1.2em; margin: 0 0 1px; font-weight: bold; }
.strip-note-mini p { margin: 0 0 1px; }
.strip-note-mini a { color: inherit; text-decoration: underline; }
.strip-note-mini code { background: rgba(127, 127, 127, 0.2); }
.strip-note-mini blockquote {
  margin: 0 0 1px;
  padding-left: 3px;
  border-left: 1px solid currentColor;
}
.strip-note-mini hr { border: none; border-top: 1px solid currentColor; margin: 2px 0; }
.strip-note-mini .md-bullet,
.strip-note-mini .md-task { gap: 2px; margin: 0 0 1px; }
.strip-note-mini .md-gap { height: 2px; }
.strip-note-mini .md-box {
  width: 5px;
  height: 5px;
  margin-top: 1px;
  border-width: 0.5px;
  font-size: 4px;
  line-height: 4px;
}

/* **Wie viele es sind** (04.09.2026, auf Wunsch: *„… and end with a count
   + message icon if there are more than one notes"*).

   Die Kachel zeigt die **erste** Nachricht; ohne ein Zeichen daneben saehe
   ein Chat aus vierzehn Saetzen aus wie eine Notiz. Zahl und Blase, klein
   und in der Farbe der Art.

   **In der Ecke und nicht im Fluss**: die Flaeche ist beschnitten (eine
   Notiz wird abgeschnitten, wo der Kasten endet - siehe `buildTileNote`),
   und was am Ende des Textes stuende, waere bei einem langen ersten Satz
   genau das, was fehlt. Unten rechts steht es immer. Der weiche Verlauf
   dahinter haelt es auch dann lesbar, wenn Worte darunter durchlaufen. */
.tile-note-mehr {
  position: absolute;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 6px 8px 4px 16px;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  color: var(--bar-color);
  background: linear-gradient(
    to right,
    transparent,
    color-mix(in srgb, var(--bar-color) 12%, var(--card-bg)) 45%);
  pointer-events: none;
}

/* ---- What the little markdown renderer produces ----

   Deliberately plain: this is prose in a card, not a document viewer.
   Headings step down in size and nothing else changes shape, because a
   note that renders like a web page is a note pretending to be one. */
.lightbox-text h1,
.lightbox-text h2,
.lightbox-text h3 { margin: 0.6em 0 0.3em; line-height: 1.25; }
.lightbox-text h1 { font-size: 1.35em; }
.lightbox-text h2 { font-size: 1.2em; }
.lightbox-text h3 { font-size: 1.05em; }
.lightbox-text p { margin: 0 0 0.45em; }
.lightbox-text a { color: var(--bar-color); }
.lightbox-text code {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 0.92em;
  padding: 1px 4px;
  border-radius: 3px;
  background: rgba(127, 127, 127, 0.18);
}
.lightbox-text blockquote {
  margin: 0.3em 0;
  padding-left: 10px;
  border-left: 3px solid var(--bar-color);
  color: inherit;
  opacity: 0.85;
}
.lightbox-text hr {
  border: none;
  border-top: 1px solid rgba(127, 127, 127, 0.35);
  margin: 0.8em 0;
}

.md-bullet, .md-task { display: flex; gap: 8px; margin: 0 0 0.35em; }
.md-dot { opacity: 0.6; }
.md-gap { height: 0.6em; }

/* A tick box that is drawn rather than offered: this view renders a note,
   and ticking things off is the notebook widget's job (notes.js). */
.md-box {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  margin-top: 3px;
  border: 1.5px solid currentColor;
  border-radius: 3px;
  opacity: 0.65;
  font-size: 11px;
  line-height: 13px;
  text-align: center;
}

.md-task.done { opacity: 0.6; }
.md-task.done .md-box { opacity: 0.9; }

/* ---- Ton ----

   A recording has nothing to look at, so the face is the wave, the file's
   name and the browser's own transport. Sized like the text face rather
   than like a picture: both are "this Anblick is not a photograph", and
   two different shapes for that would read as two different kinds of
   failure. */
.lightbox-audio {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  max-width: min(560px, 80%);
  padding: 32px 32px 26px;
  background: rgba(20, 20, 20, 0.75);
  border-radius: 10px;
  color: #fff;
}

.lightbox-audio[hidden] { display: none; }

.lightbox-audio-mark { opacity: 0.85; }

.lightbox-audio-name {
  font-size: 15px;
  text-align: center;
  overflow-wrap: anywhere;
}

/* The transport is the browser's, and it is the one control here that has
   to be wide enough to scrub with. */
.lightbox-audio audio { width: min(420px, 70vw); }

/* ---- The rest of this Anblick ----

   A strip of its other frames along the bottom, above the picture.
   Centred, scrolling sideways when a long burst outgrows the window, and
   `flex-shrink: 0` on the thumbnails so twenty frames stay legible rather
   than being squeezed into slivers. */
/* **Oben links, in der Flucht** (28.08.2026). Er lag am Fuss des
   Betrachters auf der Ansprechen-Leiste und war ueber die ganze Breite
   zentriert; jetzt steht er im oberen Band, linksbuendig, und sein
   `bottom` wird nirgends mehr von Hand geschrieben - er liegt im Fluss.

   `max-width: 100%` statt `right: 0`: er ist ein Kind des Bandes und
   soll dessen Breite nicht ueberschreiten, aber auch nicht immer
   ausfuellen - ein Anblick mit drei Bildern ist drei Daumennagel breit
   und nicht bildschirmbreit. */
.lightbox-strip {
  position: relative;
  /* `border-box`, sonst zaehlt das 12px-Polster links und rechts zu den
     100% dazu und die Reihe steht 24px weiter rechts als das Band, in dem
     sie liegt - auf einem Telefon genau die 24px, die sie unter das `x`
     schieben. Gemessen, nicht vermutet. */
  box-sizing: border-box;
  max-width: 100%;
  z-index: 2;
  display: flex;
  justify-content: flex-start;
  gap: 6px;
  padding: 8px 12px;
  overflow-x: auto;
  scrollbar-width: thin;
  /* It is dragged, so it says so. `grab` on a row that cannot scroll is a
     small lie and worth it: the alternative is measuring on every render
     to decide what the cursor should be. */
  cursor: grab;
  /* Sideways with a finger, and the page keeps the vertical - which on
     this overlay means the picture's own pinch keeps it. */
  touch-action: pan-x;
  /* Instant, never animated: the strip is rebuilt on every frame change
     and starts at the left edge, so a smooth scroll would be a visible
     slide from the beginning of the burst on every single press. */
  scroll-behavior: auto;
}

.lightbox-strip.panning { cursor: grabbing; }

/* Dragging the row must not drag the pictures in it - the browser's own
   image-drag starts a native gesture, the pointer events stop arriving,
   and the pan locks a few pixels in. Same trap as the photograph's own
   pan; see enablePictureZoom. */
/* `-webkit-*` aus demselben Grund wie an der Fotografie selbst: ein
   Daumen, der auf einer Kachel im Streifen liegen bleibt, soll keine
   Blase aufmachen. */
.lightbox-strip img {
  -webkit-user-drag: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

/* Die zwei Fuellstuecke, die ihn mittig hielten solange er passte, sind
   fort: er ist nicht mehr bildschirmbreit, sondern so breit wie seine
   Rahmen, und ein linksbuendiges Band braucht nichts, was es zentriert.
   (Der Grund, aus dem sie Fuellstuecke und kein `justify-content: center`
   waren, gilt weiter fuer jeden, der das zurueckdreht: zentriert und
   ueberlaufend schneidet die Reihe den ersten Rahmen ab, ohne einen Weg
   zurueck zu ihm.) */

.lightbox-strip[hidden] { display: none; }

/* One frame's place in the row. It held a corner mark as a second button
   beside the thumbnail once - a button inside a button is not markup a
   browser will keep - and it stays a wrapper because the `+` that picks a
   still out of a clip is built the same way. */
.strip-holder {
  position: relative;
  flex: 0 0 auto;
  display: flex;
}

.strip-shot {
  flex: 0 0 auto;
  width: 64px;
  height: 48px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 4px;
  background: rgba(20, 20, 20, 0.75);
  cursor: pointer;
  overflow: hidden;
  opacity: 0.65;
}

/* ---- Video ----

   The player fills the same space a photograph would and no more: the
   strip is underneath it and the reading column beside it, and a clip
   that covered either would be the one medium that changes the shape of
   the viewer.

   No `touch-action: none` here, unlike the picture. That rule exists so
   the pinch reaches enablePictureZoom; on a clip the gestures belong to
   the player's own controls - dragging the scrub bar is a pan the browser
   has to be allowed to have. */
.lightbox-video {
  max-width: 95%;
  max-height: 82%;
  background: #000;
  border-radius: 4px;
}

.lightbox-video[hidden] { display: none; }

/* Das Zeichen am Standbild - ein `▷`, das sagte „hier geht das Video
   weiter" - ist am 31.08.2026 mit den Standbildern selbst gegangen. */

/* Der `+` am Ende des Streifens eines Clips ist mit den Standbildern
   gegangen (31.08.2026). Die Upload-Kachel daneben ist eine andere und
   hat ihre eigene Klasse (`.strip-upload`). */

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

.strip-shot:hover { opacity: 1; }

/* Which one you are looking at. A border and full opacity, not a
   different size: a thumbnail that grew would shift every one after it
   along the strip each time you paged. */
.strip-shot.current {
  border-color: var(--bar-color);
  opacity: 1;
}
/* Editing the guess: which frame the tile shows, and where the Anblick
   should have been cut. Only with the pencil on - they are about the
   picture on screen, so they sit above the strip rather than in the
   reading column. */
/* **Where the cut would fall**, drawn between two thumbnails while the
   pencil is on. A line and nothing else: what it does is on the button
   standing over it, which cannot live in here because this row scrolls
   sideways and clips anything above it (see positionSplitButton). */
.strip-split {
  flex: 0 0 auto;
  align-self: stretch;
  width: 3px;
  margin: 0 3px;
  border-radius: 2px;
  background: var(--bar-color);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);
}

/* **Direkt unter den Streifen** (28.08.2026, auf Wunsch: *„anblick
   trennen shall move up below the strip"*).

   Es stand unten rechts über der Ansprechen-Reihe, also am anderen Ende
   des Schirms als die Sache, um die es geht: getrennt wird *vor dem
   Rahmen, auf dem man steht*, und wo der Schnitt fiele, zeichnet der
   Streifen als Strich zwischen zwei Daumennägeln. Der Knopf richtet sich
   ohnehin waagerecht nach diesem Strich aus (`positionSplitButton`) —
   er stand nur zweihundert Pixel darunter, mit dem ganzen Bild
   dazwischen. Jetzt steht er darunter, wo er hinzeigt.

   `--lightbox-ceiling` ist die Unterkante des oberen Bandes plus dessen
   Einzug; sechs Pixel darüber liegt seine eigene Oberkante. Geschrieben
   wird die Variable von `placeAnsprechBar`, also folgt der Knopf einem
   Streifen, der kommt und geht. */
.lightbox-frame-actions {
  display: none;
  position: absolute;
  right: 20px;
  top: calc(var(--lightbox-ceiling, 116px) - 6px);
  z-index: 2;
  gap: 8px;
}

.lightbox.edit-mode .lightbox-frame-actions { display: flex; }

.lightbox-frame-actions button {
  padding: 6px 12px;
  font-size: 13px;
  color: #fff;
  background: rgba(20, 20, 20, 0.8);
  border: 1px solid var(--bar-color);
  border-radius: 20px;
  cursor: pointer;
}

.lightbox-frame-actions button:hover { background: var(--bar-color); }
.lightbox-frame-actions button[hidden] { display: none; }

/* (Hier stand die erste Anzeigestufe, `.chrome-teil`: die Knöpfe gingen,
   das Gesagte blieb. Sie ist am 23.09.2026 gefallen - es gibt nur noch
   alles und „nur das Bild".) */
/* **Die Kette bleibt** (09.09.2026). Sie stand hier, solange sie ein
   Werkzeug unter Werkzeugen war; seit sie neben dem Herzen steht, ist
   sie auf der Seite dessen, was *sagt* — „geht gerade hinaus" ist eine
   Auskunft über den Anblick, und das Herz daneben bleibt aus genau
   diesem Grund schon immer stehen. Eine Reihe aus zwei Marken, von denen
   eine Stufe zwei überlebt und die andere nicht, wäre ausserdem eine
   Reihe, die auf halbem Weg umspringt. */

/* **Ausser: es wird gerade angesprochen** (30.08.2026, auf Wunsch:
   *„when in second state and ansprechen is started, the abbrechen fab
   shall show"*).

   Der Knopf ist waehrend des Waehlens nicht *Ansprechen…*, sondern
   **Zurueck** bzw. **Abbrechen** - der einzige Weg aus dem Waehler
   heraus, und der einzige, der nicht angeschrieben woanders steht. Ein
   langer Druck auf das Bild oeffnet den Waehler seit gestern auch auf
   dieser Stufe; ohne diese Zeile waere das ein Fenster ohne Klinke.

   **Und auf der dritten Stufe dasselbe, nur groesser**: dort ist die
   ganze Reihe fort, also auch die Buehne mit den Spalten - ein langer
   Druck haette gar nichts gezeigt. `.waehlt` steht am Leuchtkasten
   (ansprechfab.js) und nicht an der Reihe, damit eine Regel beide Faelle
   erreicht. Das Polster der Baender bleibt dabei weg: der Waehler wird
   ueber das Bild gezeichnet und nicht neben es. */
/* **Und seit dem 23.09.2026 steht er gar nicht mehr da** (auf Wunsch:
   *„Lightbox ansprech wheel: remove bottom left abbrechen fab"*). Der
   Absatz darüber stimmt nicht mehr: es gibt drei Wege hinaus, und alle
   drei stehen ohne ihn fest - ein Druck neben das Rad (auf das Bild,
   „keines davon"), Escape, und das `×` oben rechts. Die Antworten in der
   Reihe bleiben, samt ihrem `×`. */
.lightbox.waehlt .lightbox-ansprechen { display: none; }
.lightbox.chrome-hidden.waehlt .ansprech-bar { display: flex; }
/* Die Lupe stand hier einmal mit: sie war ein Griff *in* der Reihe. Seit
   dem 18.09.2026 ist die Suche ein eigener Streifen oben, der nur
   dasteht, während gewählt wird (`#ansprechSuche` trägt sonst `hidden`) -
   und das Rad selbst hängt an gar keiner Stufe. */

/* **Und auf dem Telefon geht die Karte weg, solange gewaehlt wird**
   (30.08.2026, auf Wunsch: *„Ansprechen on mobile shall still hide the
   map in lightbox and start from the bottom"*).

   *„Still"* ist woertlich zu nehmen: bis zum Umbau am 28.08. war die
   Karte ein 120px-Quadrat in der Lesespalte, und die Lesespalte trat
   beim Waehlen beiseite wie alle anderen Moebel (`ANSPRECH_CHROME`).
   Seither ist sie ein Band ueber die ganze Breite in `.lightbox-where`,
   und das stimmt dort nicht mit ab - also blieb sie stehen und die
   Spalten begannen 150px ueber dem Rand.

   `display: none` und nicht `visibility: hidden` wie beim Beiseitetreten:
   die zweite Haelfte des Wunsches ist *„start from the bottom"*, und
   dafuer muss das Band wirklich keine Hoehe mehr haben -
   `placeAnsprechBar` misst, was dasteht, und setzt die Reihe darauf. So
   faengt die unterste Reihe der Spalten am Fuss des Schirms an und es
   passt eine Reihe mehr hinein, was auf einem Telefon der Unterschied
   zwischen einer und zwei Spalten je Ebene ist.

   Nur auf dem Telefon: auf dem Schreibtisch ist das Band 200px hoch und
   der Schirm 900, die Spalten passen ohnehin - und dort ist die Karte
   waehrend des Waehlens eher eine Auskunft (*wo war das*) als etwas im
   Weg.

   **Die zweite Haelfte der Begruendung ist am 18.09.2026 verfallen**, die
   Regel nicht. Das Rad misst seinen Radius am ganzen Betrachter und nicht
   mehr am Raum ueber dem Fuss; ob die Karte dasteht, aendert an seiner
   Groesse also nichts, und *„start from the bottom"* beschreibt einen
   Waehler, den es nicht mehr gibt. Was bleibt, ist der erste Satz: auf
   einem Telefon liegt das Rad ueber der ganzen Flaeche, und eine Karte
   darunter ist dann nichts, was jemand liest. */
@media (max-width: 700px) {
  .lightbox.waehlt .lightbox-where { display: none; }

  /* **Und der Chat geht mit ihr** (03.09.2026, auf Wunsch: *„ansprechen
     overlay shall hide chat overlay on small screens"*).

     Dieselbe Begruendung wie beim Kartenband darueber, und seit heute
     dringender: der Chat ist eine Auflage ueber dem Bild und liegt damit
     genau dort, wo der Waehler seine Spalten aufbaut - auf einem Telefon
     ist das dieselbe Flaeche zweimal. Wer *„was war das"* fragt, liest
     gerade nicht mit, was jemand dazu gesagt hat.

     `display: none` und nicht `visibility: hidden`: sonst faengt die
     durchsichtige Auflage weiter die Griffe ab, die den Optionen gelten,
     und der Waehler waere auf halber Hoehe unbedienbar.

     Nur auf dem Telefon - auf dem Schreibtisch steht der Chat rechtsbuendig
     neben den Spalten und ist dort eine Auskunft und kein Hindernis. */
  .lightbox.waehlt .lightbox-text { display: none; }
}

/* Tap the photo to get an unobstructed view (see toggleLightboxChrome).
   The map is chrome too - it is in its own corner now rather than inside
   the panel, so it has to be named here or it would be the one thing
   left over the picture. So is the strip: it is the loudest thing on the
   screen after the picture itself, and "let me look at this" has to take
   it away too. */
.lightbox.chrome-hidden .lightbox-tools,
.lightbox.chrome-hidden .ansprech-bar,
.lightbox.chrome-hidden .lightbox-strip,
.lightbox.chrome-hidden .lightbox-time,
.lightbox.chrome-hidden .lightbox-where,
.lightbox.chrome-hidden .lightbox-frame-actions,
.lightbox.chrome-hidden .lightbox-prev,
.lightbox.chrome-hidden .lightbox-next {
  display: none;
}

/* **Das obere Band stand hier mit, bis zum 18.09.2026** - und mit ihm
   wäre jetzt der Anzeigeknopf fort, der die Stufen zählt. Der Druck auf
   das Bild tut das nicht mehr (er spricht an), also wäre die dritte Stufe
   ohne ihn eine Sackgasse, aus der nur noch Escape führt. Also gehen der
   Stempel und der Streifen einzeln (oben), und das Band bleibt als
   leerer Rahmen um den einen Knopf stehen. */

/* **Und die zwei Baender geben ihren Platz her** (28.08.2026). „Lass mich
   das ansehen" nimmt die Moebel weg; das Polster, das fuer sie freigehalten
   wurde, muss mit - sonst steht das Bild weiter in einem Ausschnitt, dessen
   Raender nichts mehr begruenden. */
.lightbox.chrome-hidden {
  padding-top: 0;
  padding-bottom: 0;
}

/* Narrow screens: the column would otherwise eat the picture. The
   expanded circle is capped against the viewport, not the panel, so it
   can't grow past the screen it's drawn on. */
@media (max-width: 700px) {
  .lightbox-top { right: 68px; gap: 6px; }
  /* Das Dreieck auf dem Telefon kleiner (24.09.2026): auf 390px bleiben
     rechts davon gut 200px fuer die Antworten, und es reicht nicht ueber
     die Pfeile hinauf. (Hier stand die Hoehe des alten Kartenbandes,
     150px.) */
  .lightbox {
    --lightbox-dreieck-breite: min(44vw, 180px);
    --lightbox-dreieck-hoehe: min(20vh, 150px);
  }
}

/* Quer gehaltenes Telefon: ein Dreieck von 28vh waere ein Keil ohne
   Inhalt - lieber etwas hoeher, als die Breite hergibt. */
@media (max-height: 560px) {
  .lightbox { --lightbox-dreieck-hoehe: 120px; }
}

.coord-row { display: flex; gap: 6px; align-items: center; }
.coord-row input { flex: 1 1 0; min-width: 0; }
.coord-row button { flex: 0 0 auto; font-size: 12px; }

/* ---- Woran der Anblick haengt (04.09.2026) ----

   Eine Zeile zwischen den Zahlen und der Karte: das Element des
   Revierplans, das auf der Karte darunter gedrueckt wurde, mit einem `×`,
   das es wieder abnimmt. Kein Klappfeld - gewaehlt wird auf der Karte, und
   diese Zeile ist die Quittung dafuer.

   `min-height`, damit die Zeile beim ersten Treffer nicht aufspringt und
   die Karte darunter mitnimmt. */
.coord-element {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 26px;
  margin-bottom: 8px;
  font-size: 13px;
}

.coord-element-label {
  flex: 0 0 auto;
  color: var(--muted-fg);
}

.coord-element-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: bold;
}

/* Leer sieht leer aus: ein fettes `—` liest sich wie ein Name. */
.coord-element-name.leer { font-weight: normal; color: var(--muted-fg); }

/* **Als Chip, sobald einer dasteht** (10.09.2026). Es ist derselbe
   `.map-element-chip` wie im Kopf des Elementfensters, nur dass er hier
   nicht die ganze Zeile nehmen darf: `flex: 1 1 auto` oben lässt ihn
   sonst über die Breite laufen, und ein Chip, der eine Zeile füllt, ist
   ein Balken. Also schrumpft er auf seinen Inhalt und schiebt das × ans
   Ende der Reihe.

   Die Schrift bleibt die der Zeile - `.map-element-chip` ist 11px, und
   neben *Element* und einem Knopf von 22px wäre das eine dritte
   Grösse. */
.coord-element-name.map-element-chip {
  flex: 0 1 auto;
  margin-right: auto;
  font-size: inherit;
  font-weight: bold;
  padding: 2px 9px;
  border-radius: 9px;
  cursor: default;
}

.coord-element-name.map-element-chip:hover,
.coord-element-name.map-element-chip:active { filter: none; }

.coord-element-weg {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  padding: 0;
  line-height: 1;
  font-size: 15px;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--control-bg);
  color: var(--fg);
  cursor: pointer;
}

.coord-element-weg:hover { background: #d98324; border-color: #d98324; color: #fff; }
.coord-element-weg[hidden] { display: none; }

/* The map inside the position dialog. Leaflet needs a real height to
   render at all; the dialog overrides this one inline. */
.coord-picker {
  height: 180px;
  margin-top: 6px;
  border: 1px solid #555;
  border-radius: 4px;
}

.coord-picker .leaflet-container { background: #2a2a2a; }

/* The Ansprechen *page* (`tagging.html`) is out for now, and its
   ninety lines of layout went with it - the queue, its frames, the
   crumbs and the choices. What is left under this name is the
   Ansprech-*dialog* (nav.js), which is a different thing in a
   different place: `.ansprechen-modal` and the rows inside it, further
   down this file. See the note in NAV_ITEMS. */

/* ---- Tagging game (retired, tagging.js) ---- */

.game-page { max-width: 760px; padding-top: 20px; }

.game-header { display: flex; flex-wrap: wrap; align-items: baseline; gap: 16px; }
.game-header h1 { margin: 0; }

.game-player { display: flex; align-items: center; gap: 8px; }

.game-player input {
  background: var(--control-bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 5px 8px;
  font-family: inherit;
  font-size: 13px;
}

.game-score { font-size: 13px; color: var(--muted-fg); }

.game-photo {
  margin: 16px 0;
  background: var(--img-placeholder);
  border-radius: 4px;
  /* Fixed height so the options don't jump around between photos. */
  height: min(60vh, 480px);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.game-photo img { max-width: 100%; max-height: 100%; object-fit: contain; }

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

.game-option {
  flex: 1 1 140px;
  padding: 12px 10px;
  font-size: 15px;
  cursor: pointer;
}

.game-option.correct { background: #2e7d32; color: #fff; border-color: #2e7d32; }
.game-option.wrong { background: #7a2020; color: #fff; border-color: #a33; }

.game-feedback { min-height: 24px; margin-top: 10px; font-size: 14px; }
.game-feedback.correct { color: #6fae82; }
.game-feedback.wrong { color: #e57373; }

.game-message {
  margin: 16px 0;
  font-size: 13px;
  color: var(--muted-fg);
  line-height: 1.5;
  max-width: 520px;
}

.game-leaderboard { margin-top: 28px; border-top: 1px solid var(--border); padding-top: 12px; }
.game-leaderboard h2 { font-size: 16px; margin: 0 0 8px 0; }

.leaderboard-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 0;
  font-size: 13px;
  border-bottom: 1px solid var(--border);
}

.leaderboard-row.you { font-weight: bold; }
.leaderboard-rank { width: 24px; color: var(--muted-fg); }
.leaderboard-name { flex: 1 1 auto; }
.leaderboard-score { min-width: 40px; text-align: right; }

/* ---- Settings (settings.html) ---- */

.settings-page { max-width: 720px; padding-top: 20px; }

/* ---- Das Inhaltsverzeichnis ----

   A strip along the top of every settings-style page, naming its
   sections. The Revier's page is the reason it exists: name, members,
   invitations, bin, export, links, Zeitstempel and vocabulary is a long
   column, and its only navigation was the scrollbar.

   **Sticky, not fixed**: it scrolls into place under the page's own
   heading and then stays, so no page has to reserve a strip of height at
   the top and nothing has to know how tall it is. It scrolls sideways
   when it does not fit rather than wrapping to two lines - a table of
   contents that pushes the page down by a second row on a phone costs
   more than it gives. */
.settings-toc {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  gap: 6px;
  margin: 0 0 10px;
  padding: 8px 0;
  overflow-x: auto;
  scrollbar-width: none;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}

.settings-toc::-webkit-scrollbar { display: none; }

.settings-toc-knopf {
  flex: 0 0 auto;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--control-bg);
  color: var(--muted-fg);
  font-size: 12px;
  white-space: nowrap;
  cursor: pointer;
}

.settings-toc-knopf:hover { color: var(--fg); border-color: var(--bar-color); }

/* Where you are. The topmost section still on screen, which is what
   somebody reading would call the one they are in. */
.settings-toc-knopf.hier {
  background: var(--bar-color);
  border-color: var(--bar-color);
  color: #fff;
}

/* The sections have to clear the sticky strip when one is jumped to, or
   the heading lands underneath it. */
.settings-page section.settings-section { scroll-margin-top: 56px; }

.settings-section {
  border-top: 1px solid var(--border);
  padding: 16px 0;
}

.settings-section h2 { font-size: 16px; margin: 0 0 10px 0; }

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

/* Only the checkbox rows are clickable as a whole (they're <label>s). */
label.settings-row { cursor: pointer; }

/* **Umbrechen statt hinausschieben** (2026-08-19). Sechs Ladetiere sind
   408px in einer Zeile, und auf einem 390px-Telefon war das der eine
   Kasten, der die ganze Seite waagerecht laufen ließ - was hier sonst
   nirgends passieren darf (conventions.md). Umbrochen bekommt jeder Knopf
   seine eigene Rundung und seinen eigenen Rahmen zurück, sonst sitzt die
   Naht der geteilten Kanten mitten in der Luft. */
.segmented { display: flex; flex-wrap: wrap; gap: 0; }

@media (max-width: 700px) {
  .segmented { gap: 4px; }
  .segmented button,
  .segmented button:first-child,
  .segmented button:last-child { border-radius: 4px; }
  .segmented button + button { border-left: 1px solid var(--border); }
}

.segmented button {
  border-radius: 0;
  font-size: 13px;
  padding: 4px 12px;
}

/* Shared borders, rounded only at the two ends. */
.segmented button + button { border-left: none; }
.segmented button:first-child { border-radius: 4px 0 0 4px; }
.segmented button:last-child { border-radius: 0 4px 4px 0; }

.segmented button.active {
  background: var(--bar-color);
  color: #fff;
  border-color: var(--bar-color);
}

/* The search box above a list's filters. Its own row rather than a chip
   in the bar: it is the widest control there and the one you go to
   first. */
.list-search { margin-bottom: 8px; }

.list-search input {
  width: 100%;
  max-width: 340px;
  background: var(--control-bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 5px 8px;
  font-family: inherit;
  font-size: 13px;
}

/* ---- A list's own filters: fort ----

   `.list-filters`, `.list-filter`, `.list-filter-clear` standen hier -
   die Artenknoepfe und die zwei Aufgabenzustaende, jeder mit der Zahl,
   die er uebrig liesse. Die Einrichtungsseite hat sie am 13.09.2026
   abgelegt, die Ausruestungsseite am 18.09.2026 (*„remove the filter
   chips and functionality on top of the ausruestungs page"*), und damit
   baut sie keine Seite mehr. Mitgenommen statt stehengelassen: eine
   Regel, die kein Markup mehr trifft, ist beim naechsten Mal die Regel,
   auf die jemand baut. Im Verlauf steht sie. */

/* ---- The Einrichtungen list ----

   Cards side by side rather than a single column: thirty installations
   read as a plan of the Revier that way, and each one is a notebook you
   open when you want it rather than a metre of open notebooks you scroll
   past. Shut by default for the same reason.

   `align-items: start` so opening one card doesn't stretch every other
   card in its row to match. */
.einrichtung-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  align-items: start;
  gap: 10px;
}

/* The Einrichtungen page is the cards, so it gets the whole window.
   `.settings-page` caps itself at 720px, which is right for a column of
   forms and wrong for a grid: on a wide screen it left two cards beside
   two thirds of empty page. The Bildordner section underneath keeps the
   reading width, because it *is* a column of forms. */
.einrichtung-page { max-width: none; }
/* **Two ids, because two pages wear this class** (21.08.2026): the
   Ausrüstung page became the same grid of tiles and would otherwise have
   its inventory capped at reading width - two tiles beside two thirds of
   empty page, which is exactly what this rule exists to prevent. */
.einrichtung-page .settings-section > *:not(#einrichtungList):not(#ausruestungList) {
  max-width: 720px;
}

/* No heading above the first section, so the rule across the top of it
   would be a line drawn under nothing. */
.settings-section.flush { border-top: none; padding-top: 0; }

/* Room for the two buttons in the corner: a card ending directly under
   them can't be scrolled past them. */
.einrichtung-page { padding-bottom: 88px; }

@media (max-width: 700px) {
  /* One card per row. `auto-fill` would already do this at 280px, but not
     at 320-700px, where it fits two cards that are then too narrow to
     read a name in - and the tiles under them too small to recognise
     anything in, which is the one job the tiles have. */
  .einrichtung-grid { grid-template-columns: 1fr; }
}

.einrichtung-card {
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;   /* keeps the coloured header inside the corners */
  background: var(--card-bg);
}

.einrichtung-card.highlight {
  border-color: var(--bar-color);
  box-shadow: 0 0 0 2px var(--bar-color);
}

/* **The card the panel came from.** A panel over the corner of a grid of
   thirty identical shapes has to say which of them it is about, or it is
   a card that appeared from nowhere - and on a wide screen the two are on
   screen together, which is the whole point of a sidebar. The ring is the
   kind's own colour (set on the card by the script), so it reads as the
   same object rather than as a second kind of highlight. */
.einrichtung-card.open {
  border-color: var(--kind-color, var(--bar-color));
  box-shadow: 0 0 0 2px var(--kind-color, var(--bar-color));
}

/* The whole header is the button - shut, it *is* the card, and a chevron
   you have to hit instead is a smaller target for no reason. Painted in
   the kind's colour, with the ink chosen per colour (readableTextOn, the
   same one the map's element window uses). */
.einrichtung-head {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 10px;
  border: none;
  border-radius: 0;
  font-family: inherit;
  font-size: 14px;
  text-align: left;
  cursor: pointer;
}

/* **Two lines, and taller for it.** The name and the kind were the two
   ends of one line, which squeezed the kind on a long name, left a gap of
   header between them on a short one, and read as two separate facts
   rather than as a thing and what kind of thing it is. Stacked, the
   header is also a target you can hit without aiming - which matters now
   that pressing it opens the panel.

   Only where the script asks for it: the Ausrüstung page wears the same
   card and its own one-line header, and this is not its change. */
.einrichtung-head.tall { padding: 10px; align-items: flex-start; }

.einrichtung-heading {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

/* The name, and beside it the camera hanging on this Kameraplatz. The
   name gives way by ellipsis (`min-width: 0` on the row, `flex: 1` on the
   name) rather than by pushing the badge off the card - which of my
   places is watching is the thing this line was added for, and a badge
   only long names lose is a badge you cannot trust. */
.einrichtung-title-row {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

/* Outlined in the header's own ink rather than filled: the header is
   already the kind's colour, and a second colour inside it would be
   claiming to be a kind of its own. `currentColor` is what
   `readableTextOn` chose, so it reads on the pale purple of a
   Kameraplatz and on anything else this ever sits on. */
.einrichtung-kamera {
  flex: 0 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 4px;
  max-width: 50%;
  padding: 1px 7px 1px 5px;
  border: 1px solid currentColor;
  border-radius: 9px;
  font-size: 11px;
  font-weight: normal;
  opacity: 0.9;
}

.einrichtung-kamera svg { flex: none; }

.einrichtung-kamera span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.einrichtung-name {
  flex: 1;
  min-width: 0;
  font-weight: bold;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* On the kind's own colour, so it's dimmed from the header's ink rather
   than set to a grey that would vanish into half the palette. */
.einrichtung-kind { flex: none; font-size: 12px; opacity: 0.8; }

/* **Kein Winkel mehr, auf keiner der beiden Seiten.** Er stand am Ende
   des Kopfes und drehte sich beim Aufklappen - und beide Karten klappen
   nicht mehr auf, sie oeffnen ein Fenster (Einrichtungen 06.09.2026,
   Ausruestung 18.09.2026). Ein Winkel verspricht, dass sich die Karte an
   Ort und Stelle entfaltet; `.tall` hat ihn deshalb schon einmal
   abgeschaltet, und jetzt gibt es nichts mehr, wofuer er stehen koennte. */

/* Tiles under the header, outside the collapsible body: what the thing
   looks like and where it is, both answerable without opening the card -
   which is what a shut card is for. Equal columns so three tiles, two
   tiles or one all fill the width. */
.einrichtung-tiles {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 2px;
  background: var(--border);   /* shows through as the hairlines between */
}

.einrichtung-tile {
  position: relative;
  display: block;
  height: 78px;
  padding: 0;
  border: none;
  border-radius: 0;
  overflow: hidden;
  background: var(--img-placeholder);
  cursor: pointer;
}

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

.einrichtung-tile:hover img,
.einrichtung-tile:hover .map-tile-plan { filter: brightness(1.06); }

/* The whole map tile: base squares, the Revierplan over them, and this
   element marked on top - one <svg>, built by mapTilePlan() in nav.js.

   The base map is *inside* it rather than an <img> underneath. Two
   pictures cropped by two different mechanisms (`object-fit` and
   `preserveAspectRatio`) have to agree about where the middle of the map
   went, and the version that got shipped didn't: the plan was drawn in
   the tile's own coordinates and then cropped to a band through the
   middle of the tile, which is 22% of it in a one-tile row - so the
   marker was usually cropped away entirely. In one viewBox they can't
   drift apart.

   `pointer-events: none` so the click lands on the button underneath. */
.map-tile-plan {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* "+2": the pictures that didn't fit, said out loud rather than hidden. */
.einrichtung-tile-more {
  position: absolute;
  right: 4px;
  bottom: 4px;
  padding: 0 6px;
  border-radius: 9px;
  background: rgba(0,0,0,0.62);
  color: #fff;
  font-size: 11px;
  font-weight: bold;
}

/* `.einrichtung-body`, `.einrichtung-danger` und `.collapsed` standen
   hier: der Rumpf, der sich auf- und zuklappte, und der Loeschknopf an
   seinem Ende. Beides ist ins Fenster gezogen - der Rumpf als
   `.einrichtung-detail-body`, das Loeschen als Papierkorb-FAB hinter dem
   Stift -, und seit dem 18.09.2026 baut auch die Ausruestungsseite
   keinen Rumpf mehr in eine Kachel. */



/* Kept in the header at zero rather than removed, so the title doesn't
   shift sideways as a task is ticked off. */
.einrichtung-head .task-chip { flex: none; }

/* ---- One installation, whole ----

   A card used to unroll where it stood: press the header and the
   notebook, the pictures and the delete button appeared between the cards
   on either side. Half of what an installation carries is prose you read
   and write - a worklist, a note about a Trittstufe - and reading it in a
   280px column of a grid, with everything below jumping down as it opens,
   is reading it through a letterbox.

   So the card is a handle and this is the thing it opens: a sidebar over
   the right of the grid where there is room for one, the whole screen
   where there isn't. Same panel, same order, whatever the screen.

   **Right, and stopping at the band.** The band owns the bottom edge of
   every list page and is the other filter; a panel over it would be
   covering the thing that decides which cards are behind it. Under the
   band's z-index for the same reason, and over the page's own corner
   buttons - it is the foreground while it is open. */
/* **Die zwei Ecken des Fensters** (06.09.2026, auf Wunsch): Stift unten
   links, Papierkorb unten rechts, und beide erst, seit sie nicht mehr im
   Kopf stehen. Dieselbe Aufteilung wie ueberall in dieser Anwendung -
   was schreibt, steht links, was wegnimmt, rechts -, damit nicht jedes
   Fenster ein eigenes Vokabular ist.

   `space-between` und nicht zwei Behaelter: mit nur einem Knopf darin
   liegt er links, wo er hingehoert, und der zweite kommt im Stift
   daneben, ohne dass sich der erste bewegt. */
.einrichtung-detail-fabs {
  position: absolute;
  left: 14px;
  right: 14px;
  bottom: 14px;
  z-index: 2;
  display: flex;
  justify-content: space-between;
  pointer-events: none;   /* nur die Knoepfe selbst nehmen den Druck */
}

.einrichtung-detail-fab {
  pointer-events: auto;
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 50%;
  background: var(--bar-color);
  color: #fff;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
}

/* Im Stift: derselbe Knopf, andere Aussage. Der Haken sagt „fertig", und
   die hellere Fuellung sagt, dass der Zustand an ist - dasselbe `.an` wie
   an der Knopfreihe des Elementfensters. */
.einrichtung-detail-fab.an { background: #d98324; }

.einrichtung-detail-fab.papierkorb { background: #b3453a; }

/* **Die zwei, die ändern, als Gruppe links** (10.09.2026). Die Reihe
   steht auf `space-between`; ein dritter Knopf als eigenes Kind landete
   in der Mitte, und das ist in dieser Anwendung keine Stelle. Der
   Setzen-Knopf gehört zum Stift - beide ändern etwas -, der Korb bleibt
   allein rechts. */
.einrichtung-detail-fab-gruppe {
  display: flex;
  gap: 10px;
  pointer-events: none;
}

.einrichtung-detail-fab:hover { filter: brightness(1.08); }

.einrichtung-detail {
  --detail-width: min(420px, 42vw);
  position: fixed;
  top: 0;
  right: 0;
  bottom: var(--revier-band-height, 0px);
  width: var(--detail-width);
  z-index: 860;   /* over the list FABs (850), under the open band (900) */
  display: flex;
  flex-direction: column;
  background: var(--card-bg);
  border-left: 1px solid var(--border);
  box-shadow: -6px 0 18px rgba(0, 0, 0, 0.18);
  overflow: hidden;
}

.einrichtung-detail[hidden] { display: none; }

/* Room for it, rather than cards hidden underneath: on a wide screen the
   grid and the panel are both meant to be readable at once - that is what
   a sidebar is for, and it is how the ring around the open card says
   anything. */
@media (min-width: 701px) {
  body.einrichtung-detail-open .einrichtung-page {
    padding-right: min(420px, 42vw);
  }
}

/* The banner: the kind's colour, the name over what it is, and the
   buttons at the right end. Close outermost, because it is the one
   pressed without looking; the pencil inside it; the trash inside that,
   furthest from the corner - it only exists in edit mode, and it takes
   the notebook and the pictures with it. */
.einrichtung-banner {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 12px;
}

.einrichtung-banner .einrichtung-name {
  font-size: 17px;
  white-space: normal;
  overflow: visible;
}

.einrichtung-banner-buttons {
  flex: none;
  display: flex;
  align-items: center;
  gap: 2px;
}

.einrichtung-banner-button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: inherit;   /* the ink readableTextOn picked for this colour */
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.85;
}

.einrichtung-banner-button:hover {
  opacity: 1;
  background: rgba(0, 0, 0, 0.15);
}

/* **Die Kette auf farbigem Grund** (19.09.2026): im Kopf einer
   Einrichtungskarte und im Kopf des Fensters daneben.

   `.map-share` traegt sonst `--muted-fg`, und das ist eine Tinte fuer den
   Seitengrund. Hier liegt sie auf der Farbe der Art - dunkelgruen,
   dunkelrot, ocker -, und dort ist die einzige Tinte, die immer lesbar
   ist, die, die `readableTextOn` fuer diesen Grund ausgerechnet hat. Also
   `currentColor` und eine Spur Deckkraft weniger, damit sie neben dem
   Namen nicht lauter ist als er.

   Der *geteilte* Zustand bleibt, wie er ueberall ist: weiss in einer
   gefuellten gruenen Scheibe. Die traegt ihren eigenen Grund mit und
   liest sich deshalb auf jedem. Das ist der Zustand, auf den es ankommt -
   er darf hier nicht anders aussehen als in der Liste. */
.einrichtung-head .map-share,
.einrichtung-banner-buttons .map-share {
  color: currentColor;
  opacity: 0.8;
}

.einrichtung-head .map-share:hover,
.einrichtung-banner-buttons .map-share:hover {
  color: currentColor;
  opacity: 1;
}

.einrichtung-head .map-share.geteilt,
.einrichtung-banner-buttons .map-share.geteilt { color: #fff; opacity: 1; }

/* Im Fenster steht sie neben dem × und ist so gross wie das: eine Reihe
   von Griffen, in der einer kleiner ist als der andere, liest sich als
   Versehen. Auf der Karte nicht - dort steht sie neben dem Aufgabenchip
   und nicht neben einem Knopf. */
.einrichtung-banner-buttons .map-share {
  /* `box-sizing` ist in diesem Stylesheet **nicht** global gesetzt, und
     `.map-share` hat 2px Polster: ohne die naechste Zeile sind aus 32
     Pixeln 36, und die Reihe steht schief. Zum zweiten Mal dieselbe
     Falle (siehe docs/conventions.md, 19.09.2026). */
  box-sizing: border-box;
  width: 32px;
  height: 32px;
  justify-content: center;
}

/* Der Kopf der Karte ist selbst ein Knopf, der sie aufschlaegt; die Kette
   darin ist ein eigener Griff. Ein sichtbarer Abstand sagt das, bevor
   jemand danebendrueckt. */
.einrichtung-head .einrichtung-kette { margin-left: 2px; }

/* **Der Kopf eines Stuecks Ausruestung traegt keine Farbe**, so wenig wie
   seine Kachel: die Farben gehoeren den Arten, die auf dem Revierplan
   gezeichnet werden (docs/ausruestung.md). Also dieselbe Flaeche, die die
   Kachel oben traegt, und ein Strich darunter statt einer Farbkante -
   sonst liefe der Kopf ohne Grenze in den Rumpf. */
.einrichtung-banner.kit {
  background: var(--control-bg);
  color: var(--fg);
  border-bottom: 1px solid var(--border);
}

/* Editing the name where it stands rather than in a dialog over the
   panel: it is one line, it is in front of you, and the check that ends
   edit mode is the same press that saves it. */
.einrichtung-banner-name {
  width: 100%;
  padding: 3px 6px;
  border: none;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.85);
  color: #222;
  font-family: inherit;
  font-size: 17px;
  font-weight: bold;
}

/* Auf dem farbigen Kopf ist das Feld ein weisser Streifen, und das ist
   dort richtig. Auf dem farblosen Kopf der Ausruestung waere es im
   dunklen Thema ein weisser Kasten mitten in der Seite - also dort die
   gewoehnliche Feldfarbe und ein Rahmen, damit es ueberhaupt als Feld zu
   erkennen ist. */
.einrichtung-banner.kit .einrichtung-banner-name {
  background: var(--card-bg);
  color: var(--fg);
  border: 1px solid var(--border);
}

.einrichtung-detail-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* Dasselbe Wort wie im Elementfenster der Karte, weil es dasselbe Mass
     ist: was ein Kind hier ausrechnen muss, um bis an die Kante zu
     reichen, ist genau dieses Polster. Das Kamera-Notizbuch steht in
     beiden Fenstern und rechnet nur einmal. */
  --inspector-polster: 12px;
  /* Unten Platz fuer die zwei Ecken (`.einrichtung-detail-fabs`, seit dem
     06.09.2026): eine Notiz, die unter einem FAB endet, ist eine Notiz,
     deren letzte Zeile man nicht lesen kann. */
  padding: 10px var(--inspector-polster) 76px;
}

/* The pictures and the map at the panel's width: big enough to recognise
   the thing by, which is what they are there for and what they cannot do
   at 78px on a card. */
.einrichtung-detail .einrichtung-tile { height: 130px; }

/* Where it stands, as chips: the Bereich, the Grund, the Revier. One row
   rather than the map's two, because an installation has an address and
   no contents - what lies *in* a Hochsitz is not a question. */
.einrichtung-where {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

.einrichtung-where-label {
  font-size: 12px;
  color: var(--muted-fg);
}

@media (max-width: 700px) {
  /* The whole screen, down to the band and the nav under it. A 420px
     sidebar on a 360px phone is the page with a sliver of grid down one
     side, which is neither the list nor the installation. */
  .einrichtung-detail {
    left: 0;
    width: auto;
    bottom: calc(56px + var(--revier-band-height, 0px));
    border-left: none;
  }

  /* Nothing to make room for: it is covering the grid on purpose. */
  body.einrichtung-detail-open .einrichtung-page { padding-right: 0; }
}

/* ---- The two buttons in the bottom-left corner ----

   `+` and search, in place of a button and a search box that sat in a
   column of controls above the list. Fixed, so they are reachable from
   anywhere down a page of thirty cards.

   Above the band, which owns the bottom edge of every list page, and
   clear of the nav rail on the left. `--revier-band-height` is set by
   `body.has-revier` and shrinks to 44px when the band is a flat bar, so
   this follows it without knowing which state it is in. */
.list-fabs {
  position: fixed;
  left: 80px;
  /* **Und über der Tastatur, wenn eine offen ist** (19.08.2026). Seit die
     Seiten `interactive-widget=overlays-content` tragen, staucht die
     Tastatur das Fenster nicht mehr - die Leiste unten und das Band
     bleiben also stehen, was gewollt ist. Diese Reihe hält aber das
     Suchfeld, und das muss sichtbar bleiben: sie ist das eine, was mit
     der Tastatur steigt. `--tastatur-hoehe` misst nav.js. */
  bottom: calc(var(--revier-band-height, 0px) + var(--tastatur-hoehe, 0px) + 16px);
  transition: bottom 0.15s ease;
  z-index: 850;   /* over the page, under the open band (900) */
  display: flex;
  align-items: center;
  gap: 10px;
}

.list-fab {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--bar-color);
  color: #fff;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,0.35);
}

.list-fab:hover { filter: brightness(1.08); }
.list-fab:active { filter: brightness(0.94); }

/* **`display: flex` above beats the UA's `[hidden]`**, so a button that
   sets `hidden` in JavaScript stays on screen unless this rule exists.
   It was written twice, narrowly - once inside `.selection-fabs` and once
   for `.danger` - and the third button that needed it was the one beside
   the `+` offering an unfinished import: it was there with nothing to
   offer, and pressing it did nothing. One rule for every list FAB, so the
   fourth one cannot repeat it. */
.list-fab[hidden] { display: none; }

/* The search button grows into the field rather than opening a box over
   the list: what you are filtering has to stay visible while you type, or
   you can't tell when to stop. The pill is the button plus the field, so
   the button stays put and the field appears beside it. */
.list-search-fab {
  display: flex;
  align-items: center;
  border-radius: 24px;
  background: var(--bar-color);
  box-shadow: 0 2px 8px rgba(0,0,0,0.35);
}

.list-search-fab .list-fab { box-shadow: none; }

.list-search-fab input {
  width: 0;
  padding: 0;
  border: none;
  background: transparent;
  color: #fff;
  font-family: inherit;
  font-size: 14px;
  /* Width rather than display, so it can be animated - and so the button
     doesn't jump sideways as the field arrives. */
  transition: width 0.2s ease, padding 0.2s ease;
}

.list-search-fab input::placeholder { color: rgba(255,255,255,0.7); }
.list-search-fab input:focus { outline: none; }

/* The × Safari and Chrome put in a search field is invisible on this
   background and Escape does the job anyway. */
.list-search-fab input::-webkit-search-cancel-button { display: none; }

.list-search-fab.open input {
  width: 220px;
  padding: 0 16px 0 4px;
}

@media (max-width: 700px) {
  /* No rail on a phone: the nav is a bar along the bottom, and the band
     sits on top of it. Both are already in `--revier-band-height`'s
     neighbours, so only the left edge changes. */
  /* Die Reihe mit dem Suchfeld ist das eine, was mit der Tastatur steigen
     *soll*. `--tastatur-hoehe` ist dabei nur der Teil, den die Tastatur
     vom sichtbaren Fenster nimmt, *ohne* das Layout zu stauchen - hat der
     Browser gestaucht, liegt die Reihe ohnehin schon darüber, und die Zahl
     ist dann null. Siehe nav.js. */
  .list-fabs {
    left: 16px;
    bottom: calc(56px + var(--revier-band-height, 0px) + var(--tastatur-hoehe, 0px) + 16px);
  }
  /* A 220px field plus two buttons doesn't fit beside the rail on a
     narrow screen; the field takes what is left instead. */
  .list-search-fab.open input { width: 40vw; }
}

/* ---- Und geht die Tastatur auf, geht das Feld nach oben ----
   (21.08.2026)

   Viermal stand hier dieselbe Aufgabe: das Suchfeld sitzt unten links,
   die Tastatur kommt von unten, also muss das Feld um deren Höhe steigen.
   Viermal wurde diese Höhe anders bestimmt, und viermal kam es von einem
   echten Telefon zurück - zuletzt, weil `interactive-widget=
   overlays-content` zusagt, weder Layout noch sichtbares Fenster
   anzurühren, dort also per Definition nichts zu messen ist (nav.js,
   `tastaturMass`).

   Die Lehre daraus ist nicht die fünfte Rechnung. **Eine Tastatur steht
   unten; was oben steht, muss nicht gemessen werden.** Sobald das Feld
   den Fokus hat, verlässt es deshalb die Reihe in der Ecke und heftet
   sich an den oberen Rand: keine Höhe, keine Schwelle, kein Browser, der
   die eine der beiden Fragen nicht beantwortet. Damit hängt der Fall, der
   auf iOS *und* auf Android schiefging, an gar keiner Messung mehr.

   **`:focus-within`, nicht `body.feld-fokus`**, obwohl beim Tippen beides
   gilt: umziehen soll genau das Feld, in dem getippt wird. `feld-fokus`
   steht auch, wenn das Umbenennen-Fenster ein Feld hat - eine Suchreihe,
   die dabei nach oben springt, wäre die nächste Meldung.

   **Ein Sprung, keine Fahrt.** `position` lässt sich nicht animieren, und
   ein Feld, das eine Fünftelsekunde quer über den Schirm zöge, während
   die Tastatur aufgeht, sähe nach Fehler aus. Es ist sofort dort, wo
   getippt wird.

   Kein `z-index` hier, und das ist Absicht statt Vergessen: `.list-fabs`
   trägt selbst einen (850) und ist positioniert, macht also einen
   Stapelzusammenhang auf, aus dem ein Kind nicht herauskommt. Eine Zahl
   an dieser Stelle wäre Theater. 850 reicht auch - oben liegt nur das
   Inhaltsverzeichnis (5), und über allem liegen die Fenster (9000).

   Breiter als ein Telefon bleibt es, wie es war: auf einem Tablett steht
   die Reihe unten und steigt um `--tastatur-hoehe`. Dort stimmt die
   Messung, dort ist der obere Rand weit weg, und von dort ist der Fehler
   nie gemeldet worden. */
@media (max-width: 700px) and (pointer: coarse) {
  .list-search-fab.open:focus-within {
    position: fixed;
    top: 12px;
    left: 16px;
  }
}

/* **Im Hochformat nimmt es den ganzen oberen Rand.** 40vw sind auf einem
   390px-Telefon keine 160px, also gut zwei Wörter - und wer sucht, will
   lesen, wonach. Oberhalb von 480px (Querformat, kleines Tablett) bleibt
   es die Pille oben links, weil eine Zeile quer über den Schirm dort zu
   drei Vierteln leer wäre. */
@media (max-width: 480px) and (pointer: coarse) {
  .list-search-fab.open:focus-within { right: 16px; }
  .list-search-fab.open:focus-within input {
    /* `flex` statt einer Breite: die Pille ist so breit wie der Rand es
       zulässt, das Feld nimmt, was der Knopf übriglässt. `min-width: 0`,
       weil ein Flex-Kind sonst nicht unter seine Inhaltsbreite geht. */
    width: auto;
    flex: 1;
    min-width: 0;
  }
}

/* ---- „In welches Revier?" ---- (21.08.2026)

   Die Frage, die in einer Gesellschaft vor dem Anlegen steht, wenn mehr
   als ein eigenes Revier darin liegt. Eine Liste von Knöpfen, jeder eine
   Zeile aus Wappen und Namen - **dieselbe Zeile wie im Wechsler**, weil
   es dieselbe Frage ist („welches Archiv") und zwei Darstellungen für
   eine Frage zwei sind, die man auseinanderhalten muss. */
.revier-wahl { width: min(420px, calc(100vw - 32px)); }

.revier-wahl-liste {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 14px 0 4px;
}

.revier-wahl-zeile {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--control-bg);
  color: var(--fg);
  font-family: inherit;
  font-size: 14px;
  text-align: left;
  cursor: pointer;
}

.revier-wahl-zeile:hover { border-color: var(--bar-color); }
.revier-wahl-zeile:focus-visible { outline: 2px solid var(--bar-color); outline-offset: 1px; }

/* ---- "Einrichtung anlegen": the map's two steps, off the map ----

   Which kind, then what it is called - the same order and the same
   coloured shape buttons the map's `+` opens (`.map-kinds`, reused
   verbatim so the two can't drift apart). It was a `<select>` and a text
   field in a plain modal, which hid nine of the ten answers behind a
   click and showed none of the colours the rest of the app names them
   with. */
/* `.modal-backdrop` already centres and dims; this only needs to say what
   goes in the middle of it. */
.kind-picker {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  width: min(560px, calc(100vw - 32px));
}

/* One step at a time. The kinds go when a kind has been picked - the
   answer is then on the chip above the name field, and leaving ten
   buttons up would make "which one did I press" a question. */
.kind-picker.naming .map-kinds { display: none; }
.kind-picker:not(.naming) .kind-picker-name { display: none; }

.kind-picker-name {
  width: 100%;
  max-width: 380px;
  padding: 16px;
  border-radius: 8px;
  background: var(--card-bg);
  box-shadow: 0 6px 24px rgba(0,0,0,0.4);
}

/* Which kind is being created, in its own colour - the same chip shape
   this app names an element with everywhere else. */
.kind-picker-chosen {
  display: inline-block;
  margin-bottom: 10px;
  padding: 3px 10px;
  border-radius: 12px;
  font-size: 13px;
  font-weight: bold;
}

.kind-picker-name input {
  width: 100%;
  box-sizing: border-box;
  padding: 8px 10px;
  font-family: inherit;
  font-size: 14px;
  color: var(--fg);
  background: var(--control-bg);
  border: 1px solid var(--border);
  border-radius: 4px;
}

/* Ausrüstung wears the same card and gets no colour, because there is no
   colour to wear: the kinds up there are the ones drawn on the
   Revierplan, and a Motorsäge is not drawn anywhere. A header painted in
   some seventh hue would be claiming a place in a vocabulary it isn't
   in. So: the page's own surface, and the name doing the work. */
.einrichtung-card.kit .einrichtung-head {
  background: var(--control-bg);
  color: var(--fg);
  border-bottom: 1px solid var(--border);
}

.einrichtung-card.kit .einrichtung-kind { opacity: 0.7; }

/* ---- Wo es liegt, auf der zugeklappten Kachel ---- (21.08.2026)

   Gefragt wurde nach vier Dingen auf einer Kachel - Gegenstand, Bereich,
   Ort, Log - und drei davon stehen im Kopf: Name, Art, und der Zähler der
   offenen Aufgaben. Der vierte ist diese Reihe, und sie steht **außerhalb
   von `.einrichtung-body`**, den `.collapsed` versteckt. Eine Kachel,
   deren ganzer Zweck „wo liegt meine Säge" ist und die man dafür aufmachen
   muss, ist wieder eine Liste von Namen.

   Dieselben Chips wie auf der Einrichtungsseite (`.einrichtung-where`),
   weil es dieselbe Frage ist und dieselbe Antwort: ein Element in seiner
   Farbe, das den Revierplan darauf öffnet. Nur der Rahmen ist anders -
   dort steht die Reihe im Körper unter den Kacheln, hier trägt sie die
   Kachel. */
/* **Auf der Kachel gerahmt, im Fenster nicht** (18.09.2026). Die Zeile
   stand zwischen Kopf und Rumpf und trug deshalb einen Strich nach unten;
   seit die Kachel ein Griff ist, ist sie das Letzte darauf, und ein
   Strich unter dem Letzten ist eine Linie unter nichts. Den Abstand
   braucht sie weiter - sie liegt direkt an der Kachelkante -, und im
   Fenster keins von beidem: dort ist sie eine Zeile unter vielen und
   folgt dem Abstand des Rumpfes. */
.einrichtung-card .kit-where {
  padding: 6px 10px;
}

/* Ein geschriebener Ort zeigt auf nichts, also sieht er nicht aus wie
   etwas zum Drücken: kein Chip, keine Farbe, kein Zeiger. Er ist eine
   Notiz, und ein Knopf, der die Karte *nicht* öffnet, wäre ein Versprechen
   das er nicht halten kann. */
.kit-ort-text {
  font-size: 13px;
  color: var(--fg);
  font-style: italic;
}

/* Die zwei Regler, die eine Antwort sind: die Liste der Revierelemente
   und das Feld für alles andere. Untereinander statt nebeneinander, damit
   auf einer 280px-Kachel keins von beiden auf zwei Zeichen schrumpft -
   und weil „oder" zwischen ihnen steht, nicht „und". */
.kit-ort-editor {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

/* **`border-box`, sonst laeuft das Feld ueber den Rand** (18.09.2026, im
   Browser gesehen). `box-sizing` ist in diesem Stylesheet nicht global,
   sondern je Regel gesetzt - `width: 100%` plus 8px Polster und 1px
   Rahmen ist also 18px breiter als der Platz, den es fuellen soll. Auf
   der 280px-Kachel schnitt `overflow: hidden` der Karte das ab; im
   Fenster gibt es nichts, was schneidet, und das Ortsfeld stand zur
   Haelfte ausserhalb.
   Und die Spalte selbst muss den Rest der Zeile nehmen *und* darin
   bleiben: ohne `min-width: 0` waere das breiteste `<option>` - ein Name
   samt Art in Klammern - der Boden, unter den sie nicht schrumpft. */
.kamera-service .kit-ort-editor { flex: 1 1 auto; }

.kit-ort-editor select,
.kit-ort-editor input {
  box-sizing: border-box;
  width: 100%;
}

.settings-note {
  font-size: 13px;
  color: var(--muted-fg);
  line-height: 1.5;
  margin: 0 0 12px 0;
}

.settings-note code { font-size: 12px; }

.settings-add { display: flex; gap: 8px; margin-bottom: 10px; }

/* **A class that sets `display` beats the UA's `[hidden]`**, so the
   `hidden` attribute on one of these did nothing at all: the field the
   Gesellschaften `+` toggles stood open on the page for ever, and the
   button appeared to do nothing because there was nothing left to open.
   Same companion nine other rules here already carry (.sort-menu,
   .list-fab, .gallery-filter …); this one was missed. */
.settings-add[hidden] { display: none; }

.settings-add input {
  flex: 1 1 auto;
  max-width: 340px;
  background: var(--control-bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 5px 8px;
  font-family: inherit;
  font-size: 13px;
}

.settings-status { font-size: 13px; color: var(--muted-fg); min-height: 20px; }
.settings-status.error { color: #e57373; }

/* ---- Shared modal (nav.js) ---- */

/* Above everything, including the map's own overlays and FABs - and the
   lightbox at 9999, which can open the Ansprechen dialog over itself. */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 10000;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.modal {
  width: 100%;
  max-width: 360px;
  background: var(--control-bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 18px 20px;
  box-shadow: 0 8px 30px rgba(0,0,0,0.5);
}

.modal h2 { margin: 0 0 14px 0; font-size: 17px; }

/* The Ansprech drill-down needs room for a row of options. */
.modal-wide { max-width: 560px; }

.ansprechen-extra { margin-top: 14px; }
.ansprechen-extra .settings-add { margin-top: 4px; }

/* One tap from the verdict the rest of the burst already has. */
.ansprechen-suggestions { margin-bottom: 12px; }

.ansprechen-suggestion-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 4px;
}

.ansprechen-suggestion {
  padding: 8px 12px;
  font-size: 14px;
  background: var(--bar-color);
  color: #fff;
  border-color: var(--bar-color);
}

.modal-field { margin-bottom: 10px; }

.modal-field label {
  display: block;
  font-size: 12px;
  color: var(--muted-fg);
  margin-bottom: 3px;
}

.modal-field input {
  width: 100%;
  box-sizing: border-box;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 7px 9px;
  font-family: inherit;
  font-size: 14px;
}

.modal-error { min-height: 18px; font-size: 12px; color: #e57373; }

/* The batch position dialog's lat/lng fields. */
.modal .coord-row { margin-bottom: 8px; }

.modal .coord-row input {
  flex: 1 1 0;
  min-width: 0;
  box-sizing: border-box;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 7px 9px;
  font-family: inherit;
  font-size: 14px;
}

.modal-actions { display: flex; gap: 8px; margin-top: 8px; }
.modal-actions button { padding: 7px 14px; }

.modal-actions .save-button {
  background: var(--bar-color);
  color: #fff;
  border-color: var(--bar-color);
}

/* Red, and filled. A destructive button that is the same grey as
   "Abbrechen" beside it is a destructive button you press by aiming
   roughly. */
.modal-actions .delete-button {
  background: #7a2020;
  border-color: #a33;
  color: #fff;
}

.modal-actions .delete-button:hover { background: #a33; }

/* ---- Which of the two deletions ----

   Three answers to one question, so the layout has to say which is which
   rather than lining them up as equals. Abbrechen is pushed to the left
   and left grey; the two red ones sit together on the right, and the
   *smaller* of them - this picture only - is the one that carries focus
   and answers Enter. See askDeleteScope.

   The default is the small one on purpose. Enter on a dialog you have not
   read should cost you one frame, not forty. */
.modal-actions.delete-scope { justify-content: flex-end; }
.modal-actions.delete-scope button:first-child { margin-right: auto; }

.modal-actions.delete-scope .delete-button.default {
  outline: 2px solid #fff;
  outline-offset: 2px;
}


/* ---- Reviereinrichtungen: installations, cameras, folders ---- */

.kamera-card {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 10px 12px;
  margin-bottom: 10px;
}

.kamera-header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 8px;
}

.kamera-folder { font-family: monospace; font-weight: bold; }

.kamera-service {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 0;
  font-size: 13px;
}

.kamera-service-label {
  flex: 0 0 110px;
  color: var(--muted-fg);
}

.kamera-service .count-label { flex: 1 1 auto; }
.kamera-service button { font-size: 12px; }

.kamera-service input {
  flex: 1 1 auto;
  min-width: 0;
  background: var(--control-bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 4px 8px;
  font-family: inherit;
  font-size: 13px;
}

.kamera-link { display: inline-block; margin-top: 8px; font-size: 12px; }

/* Arrived here from the map's "Kamera verwalten" link. */
.kamera-card.highlight { border-color: var(--bar-color); box-shadow: 0 0 0 2px var(--bar-color); }

/* ---- Das Revier-Band (revier.js) ---- */

/* The band is fixed at the top of every list page: a live but inert
   strip, and the full-screen map it grows into. One element at two
   heights - see docs/revier-band.md for why it isn't two maps.

   Its own stacking context on purpose: everything inside it (Leaflet's
   control corners at 1000, the drawer at 1100, the picker at 1200) is
   then measured against the band rather than against the page, which is
   what stops it competing with the gallery's own FABs and lightbox. */
.revier-panel {
  /* **Die Maße der Knopfreihe, einmal.** Der Hinweiskasten rechnet seinen
     Abstand daraus (`.map-hint`), und er ist ein *Geschwister* der Reihe,
     kein Kind - die zwei Zahlen müssen also am gemeinsamen Vorfahren
     stehen, sonst erbt er nichts und die Vorgabe im `calc` gilt still
     weiter. */
  --map-reihe-abstand: 24px;
  --map-reihe-hoehe: 56px;

  position: fixed;
  bottom: 0;
  left: 64px;   /* clears the nav rail */
  right: 0;
  height: 84px;
  z-index: 900;
  overflow: hidden;
  background: var(--img-placeholder);

  /* The ribbon's colour, and the ink that stays readable on it. Green
     until an element is picked, then that element's kind - set as an
     inline custom property by applyBandIdentity(), and *removed* again
     when nothing is picked, so this line is the only place the default
     lives. The whole strip follows it, border included: the band is what
     says which element the page is filtered by. */
  --revier-tab-color: var(--bar-color);
  --revier-tab-ink: #fff;

  border-top: 2px solid var(--revier-tab-color);
  /* The band is anchored to the bottom edge, so animating its height is
     what slides it: the top edge travels up to open and back down to
     close, and the map underneath is resized on every frame of it
     (sizeMapAcrossTransition) so it tracks the slide instead of popping
     into place at the end. Both ends have to be a length for that -
     `height: auto` would snap. */
  transition: height 0.28s ease;
}

.revier-panel.open {
  /* `dvh`, not `vh`. The band is anchored to the *bottom* edge, so its
     height is what decides where its top edge lands - and `100vh` counts
     the strip a mobile browser's URL bar is sitting on. That makes the
     panel taller than what you can see, pushing its top edge (and with
     it the tab bar, the only way out) off the screen. `dvh` is the
     viewport actually on show; the `vh` line above it is the fallback
     for browsers too old to know the unit. */
  height: 100vh;
  height: 100dvh;
  border-top: none;
}

/* The page ends above the collapsed band. Not changed for the open
   state: the band covers the page then anyway, and re-laying the gallery
   out underneath it is work nobody can see. */
body.has-revier {
  --revier-band-height: 84px;
  padding-bottom: var(--revier-band-height);
}

body.has-revier.revier-flat { --revier-band-height: 44px; }
body.has-revier.revier-open { overflow: hidden; }

.revier-panel #map {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: var(--img-placeholder);
}

/* No map in the strip at all: on a phone, and wherever Leaflet failed to
   load. The tab is then the whole band. */
.revier-panel.flat { height: 44px; }
.revier-panel.flat #map { display: none; }

.revier-panel #map.drawing { cursor: crosshair; }

/* Collapsed, the tab covers the whole strip and is the only thing you
   can click - the map underneath is scenery. Open, it shrinks to a bar
   at the top and becomes the way back out. */
.revier-tab {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 950;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  cursor: pointer;
  /* Translucent shut, so the peek shows through it; opaque open. Both
     take whatever colour the pick put on the panel. */
  background: color-mix(in srgb, var(--revier-tab-color) 62%, transparent);
  color: var(--revier-tab-ink);
  font-weight: bold;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-size: 13px;
  /* `color` too: picking an element can flip the ink from white to near
     black, and a bar that changes colour under a name that snaps is
     worse than one that doesn't change at all. */
  transition: background 0.2s ease, color 0.2s ease;
}

.revier-tab:hover { background: color-mix(in srgb, var(--revier-tab-color) 78%, transparent); }

/* Wearing a picked element's colour: nearly opaque, so the ink stays
   readable (it was chosen against the colour, not against the colour
   mixed with the map) and the colour reads as a statement about what the
   page is filtered by rather than as a tint on the peek. */
.revier-panel.picked .revier-tab {
  background: color-mix(in srgb, var(--revier-tab-color) 92%, transparent);
}

/* Same specificity as the rule above, so the plain :hover can't win. */
.revier-panel.picked .revier-tab:hover { background: var(--revier-tab-color); }

/* Open, it shrinks to a bar along the *top* edge. The ribbon is at the
   bottom, but a full-screen map's way out belongs at the top, where a
   title bar goes and where it can't be confused with the ribbon it just
   grew out of - and it leaves the bottom corners to the map's own
   buttons, which is where they were before the band existed. */
.revier-panel.open .revier-tab {
  /* Fixed, not absolute: this bar is the only way out of a full-screen
     map, so it is pinned to the top of the *viewport* rather than to the
     top of the panel. Then it stays reachable even if the panel's own
     top edge ends up above the screen - which is what the `vh` fallback
     above does on a browser that doesn't know `dvh`. Fixed positioning
     also escapes the panel's `overflow: hidden`, which costs nothing
     here: open, the panel fills the screen anyway. */
  position: fixed;
  top: 0;
  left: 64px;   /* the nav rail, same as the panel */
  right: 0;
  bottom: auto;
  height: 34px;
  background: var(--revier-tab-color);
  justify-content: space-between;
  padding: 0 16px;
}

/* "Revier · Kanzel am Bach" can outrun a phone. It truncates rather than
   pushing the hint and the chevron off the end of the bar. */
.revier-tab-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* **Das Wappen des Archivs, vor seinem Namen** (22.08.2026). Nur ohne
   Auswahl - mit einem Element trägt die Leiste dessen Farbe, und ein
   Wappen daneben behauptete, das Archiv sei das Thema.

   `:empty` statt `[hidden]`: applyBandIdentity leert den Behälter, statt
   ihn zu schalten, und dann darf er auch keine Lücke mehr machen - die
   Leiste ist zentriert, und eine unsichtbare Lücke davor schöbe den
   Namen aus der Mitte. */
.revier-tab-wappen {
  display: flex;
  align-items: center;
  flex: none;
}

.revier-tab-wappen:empty { display: none; }

/* Der Rand hebt es von der durchscheinenden Lasche ab - darunter liegt
   die Karte, und ein Wappen ohne Kante schwimmt darauf. Die Farbe ist
   die Tinte der Leiste, also dieselbe wie der Name daneben. */
.revier-tab-wappen .wappen {
  box-shadow: 0 0 0 1.5px var(--revier-tab-ink);
  color: var(--revier-tab-ink);
}

.revier-tab-hint {
  font-weight: normal;
  letter-spacing: normal;
  text-transform: none;
  opacity: 0.85;
}

/* The ribbon's right end, just inside the chevron, shut and only with
   something picked: letting go of the selection without opening the map
   to find the element window's ×. The ribbon is what tells you the page
   is filtered, so it is where you undo it.

   Beside the chevron rather than at the far end, because the two are the
   only things on the ribbon you can press *at* rather than through: one
   opens the map, one drops the pick, and a hand going for either of them
   goes to the same end of the strip. Its offset is the chevron's own
   (16px) plus that chevron's width and a gap.

   Absolute, like the chevron, so the name stays centred rather than
   shunted off to one side by a button that is usually not there. */
/* Orange on white, the same mark the gallery's count bubbles wear, and
   for the same reason: dropping a filter is one gesture with one look,
   whether the filter was picked in the gallery's filter row or out on the
   map. In the ribbon's own ink it read as part of the name it sits
   beside - which is the opposite of what it does. */
.revier-tab-clear {
  position: absolute;
  right: 44px;   /* the chevron's 16px, its 20px, and a gap */
  display: none;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  font-size: 18px;
  line-height: 1;
  color: #fff;
  background: #d98324;
  border: none;
  border-radius: 50%;
  cursor: pointer;
}

.revier-tab-clear:hover { background: #b3651b; }

/* Open, the map has its own × on the element window and the ribbon's one
   job is to be the way out of the map - two ×s a centimetre apart doing
   different things is the ambiguity worth avoiding. */
.revier-panel.picked:not(.open) .revier-tab-clear { display: flex; }

/* With the × out there the right end holds two things, so the name is
   kept off both rather than running its last letters underneath them.
   Symmetric, or the padding would shift a centred label off centre; and
   only in the state that has an × at all. The name truncates, which is
   what it already did. */
.revier-panel.picked:not(.open) .revier-tab { padding: 0 78px; }

/* The arrow at the ribbon's right end. Not a button - the whole ribbon
   is the target, for clicking and for dragging - just the mark that says
   so. It points the way the band is about to move: up to open, down to
   close. Turned on `.open`, i.e. once a drag's release has decided the
   outcome, rather than following the finger. */
.revier-tab-arrow {
  position: absolute;
  right: 16px;
  display: flex;
  align-items: center;
  opacity: 0.9;
}

.revier-tab-arrow svg { transition: transform 0.28s ease; }
.revier-panel.open .revier-tab-arrow svg { transform: rotate(180deg); }

/* Open, the hint sits at the right end of the bar and would run into the
   arrow; this is the room it needs. */
.revier-panel.open .revier-tab { padding-right: 48px; }

/* The element drawer is fixed, full height and at z-index 1100, so with
   it open it covered the right end of this bar - which is where both the
   arrow and the "Schließen" hint live, i.e. everything that says the bar
   is the way out. The bar stops short of the drawer instead of being
   raised over it: raised, it would only have painted across the drawer's
   own top edge. Its `right` is animated to the drawer's own 0.25s so the
   two move as one thing. */
.revier-panel.open .revier-tab { transition: background 0.2s ease, right 0.25s ease; }
.revier-panel.open.map-sidebar-open .revier-tab { right: 300px; }

/* Dragging drives the height by hand, frame by frame; the transition
   would be chasing the last one. */
.revier-panel.dragging { transition: none; }
.revier-panel.dragging .revier-tab { cursor: grabbing; }

/* `touch-action` or the browser's own scroll claims the gesture on a
   touch screen before pointermove is ever seen; `user-select` because
   with a mouse, dragging across a label is how you select text - and
   this label is the thing you drag. */
.revier-tab {
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

/* Ribbon mode is a picture of the Revier, not a control panel: none of
   the map's own chrome belongs on an 84px strip you can't draw on. The
   tab covers them for clicks (z-index 950 against their 800), but it is
   deliberately translucent so the map reads through it - which would
   leave the buttons showing through as well.

   Scoped `:not(.open)` rather than fought over with specificity: the
   rules that reveal these while shaping are scoped to `.open` below, so
   the two sets can never both match. */
.revier-panel:not(.open) .map-actions,
.revier-panel:not(.open) .map-fab,
.revier-panel:not(.open) .map-sidebar-fab,
.revier-panel:not(.open) .map-shape-picker,
.revier-panel:not(.open) .map-hint { display: none; }

/* Leaflet's own controls go with them. They are the same argument: zoom,
   the base-layer picker and the two view buttons are all things you press
   on a map you are working, and the shut ribbon is a picture. Worse than
   the FABs, in fact - they sit *under* the translucent tab, so they show
   through it while doing nothing when pressed.

   **Benannt statt nach Ecke** (2026-08-19): die Regler stehen jetzt unten
   links, und dort steht in der anderen Ecke die Herkunftszeile - die ist
   eine Lizenzbedingung auf Kacheln, die noch zu sehen sind, und bleibt
   deshalb stehen. Vorher konnte man dafür `.leaflet-top` sagen; jetzt
   werden die drei Bedienelemente genannt. */
.revier-panel:not(.open) .leaflet-top,
.revier-panel:not(.open) .leaflet-control-zoom,
.revier-panel:not(.open) .base-layer-picker,
.revier-panel:not(.open) .locate-control { display: none; }

/* Everything in the band hangs off the panel, not the viewport - the
   panel is the containing block, so `fixed` here would escape it and
   leave the FABs floating over the page while the band is shut. */
.revier-panel .map-sidebar,
.revier-panel .map-sidebar-fab,
.revier-panel .map-fab,
.revier-panel .map-actions { position: absolute; }
/* Alles in der Reihe unten links liegt in der Reihe, auch das × - seit es
   dort steht und nicht mehr in der oberen Ecke. */
.revier-panel .map-actions .map-fab { position: static; }

/* **Leaflets Regler stehen unten rechts, über dem Regalknopf**
   (19.08.2026, dritter und letzter Platz).

   Die Reihe der Umzüge ist die Herleitung: **oben links** wichen sie dem
   Abbrechen-Knopf aus, sobald gezeichnet wurde - die Karte schob ihre
   eigenen Bedienelemente mitten in einer fremden Geste. **Unten links**
   standen sie dann dem Hinweiskasten im Weg, und als der nach oben zog,
   verdeckte ihn dort das Elementfenster. Die linke untere Ecke gehört der
   Knopfreihe und dem Text darüber; also die rechte, wo sie mit dem
   Regalknopf (bottom 92) und den Filterknöpfen (bottom 24) **eine
   Spalte** bilden statt sich zu überlagern.

   **160px sind die 92 des Regalknopfs plus seine 56 Höhe plus 12 Luft**
   (22.08.2026, auf Wunsch: *„align the leaflet nav buttons directly above
   the element pane fab"*). Vorher standen hier 148 - gerechnet mit 48
   statt 56 -, und der rechte Rand war 24 statt 16: die Regler standen
   also weder auf dem Knopf noch neben ihm, sondern acht Pixel daneben und
   acht darunter. Am Ende ist keiner dieser Plätze willkürlich, sondern
   jeder die Antwort auf das, was am vorigen im Weg stand.

   `margin-right: 16px` **wie der Regalknopf**, damit die Spalte eine
   Kante hat. Leaflets Regler sind 34 breit und der Knopf 56, also stehen
   sie darin nicht mittig - bündig rechts ist die Kante, an der das Auge
   entlangläuft, und die zählt. */
.revier-panel.open .leaflet-bottom.leaflet-right {
  margin-bottom: 148px;
  margin-right: 16px;
}

/* **Und Leaflets eigene 10px müssen weg**, sonst stimmt keine der Zahlen
   darüber: der Behälter sitzt, wo er soll, und jeder Regler darin schiebt
   sich noch einmal 10 nach innen. Gemessen waren es zehn zu hoch und zehn
   zu weit links - genug, dass die Spalte schief aussieht, zu wenig, um es
   an einer Zahl zu suchen.

   `0 0 12px 0`: kein Rand zur Kante, 12 Luft zum nächsten. 148 + 12 = 160,
   und das ist die Oberkante des Regalknopfs. */
.revier-panel.open .leaflet-bottom.leaflet-right .leaflet-control {
  margin: 0 0 12px 0;
}

/* **Die Herkunftszeile geht diesen Weg nicht mit.** Sie ist eine
   Lizenzbedingung auf Kacheln, die zu sehen sind, und gehört an die
   Kante - nicht auf halbe Höhe zwischen zwei Knopfreihen.

   **Sie hängt dafür woanders**, und das ist der einzige DOM-Griff in
   dieser Ecke: revier.js hängt sie beim Aufbau der Karte an das Band
   selbst um (`map-herkunft`). Zwei Wege in CSS wurden versucht und beide
   gehen nicht - `position: absolute` bezieht sich auf `.leaflet-bottom`,
   nicht auf das Band, und setzte die Zeile mitten auf die Zoomknöpfe; ein
   negativer unterer Rand schrumpft den Behälter, statt das letzte Kind zu
   verschieben. */
.revier-panel .map-herkunft {
  position: absolute;
  right: 0;
  bottom: 0;
  z-index: 700;
  margin: 0;
}

/* Und sie weicht dem offenen Regal aus - das ist der Grund, aus dem sie
   vorher in Leaflets Ecke stand, und er gilt weiter: eine Lizenzzeile
   hinter einer Schublade ist keine. */
.revier-panel.map-sidebar-open .map-herkunft { right: 300px; }

/* **Zu ist sie ein Zeichen** (22.08.2026). Der `Leaflet`-Link ist ganz
   weg (`setPrefix("")` in revier.js) - er ist eine Höflichkeit, keine
   Bedingung. Der Kachelhinweis ist eine, solange Kacheln zu sehen sind,
   und das geschlossene Band zeigt welche: 84px echte Karte unter einer
   durchscheinenden Lasche. Also klein statt weg, und das ist genau der
   Ausweg, den die Attribution Guidelines der OSMF für knappen Platz
   nennen - der Hinweis darf hinter einem Zeichen liegen, das ihn zeigt.

   `font-size: 0` blendet den Text aus, **ohne ihn wegzunehmen**: er steht
   weiter im Baum und wird vorgelesen. `display: none` täte das nicht, und
   `text-indent` schöbe die Zeile nur aus dem Bild. Das ⓘ ist ein
   `::before` am Behälter selbst - Leaflet schreibt in dessen `innerHTML`
   und rührt ein Pseudoelement nie an. */
.revier-panel:not(.open) .map-herkunft {
  /* Über der Lasche (950), sonst kommt kein Druck an: sie deckt zu den
     ganzen Streifen und ist das Einzige, was man dort anklicken kann. */
  z-index: 960;
  right: 4px;
  bottom: 4px;
  min-width: 22px;
  padding: 0 6px;
  border-radius: 12px;
  line-height: 20px;
  text-align: center;
  white-space: nowrap;
  font-size: 0;
  cursor: pointer;
}

.revier-panel:not(.open) .map-herkunft::before {
  content: "ⓘ";
  font-size: 14px;
}

/* Zeiger, Tastaturfokus, Antippen - drei Wege, ein Zustand. Der Fokus
   ist dabei nicht der Nebenfall: ohne ihn wäre der Hinweis für den, der
   keine Maus führt, hinter einer Geste, die er nicht hat. */
.revier-panel:not(.open) .map-herkunft:hover,
.revier-panel:not(.open) .map-herkunft:focus-within,
.revier-panel:not(.open) .map-herkunft.offen {
  font-size: 11px;
  text-align: right;
}

.revier-panel:not(.open) .map-herkunft:hover::before,
.revier-panel:not(.open) .map-herkunft:focus-within::before,
.revier-panel:not(.open) .map-herkunft.offen::before { content: none; }

.revier-panel:not(.open) .map-herkunft:focus-visible {
  outline: 2px solid var(--revier-tab-color);
  outline-offset: 1px;
}

/* **Und am flachen Band gar nicht.** Dort ist keine Karte (`.flat #map {
   display: none }`), also keine Kachel, also nichts zu nennen - und die
   Zeile hängt seit dem Umhängen am Band statt an der Karte, verschwindet
   also nicht von selbst mit ihr. Sichtbar wird das nur, wenn ein Fenster
   von breit auf schmal gezogen wird, nachdem Leaflet geladen war. */
.revier-panel.flat .map-herkunft { display: none; }

/* Leaflet does NOT pass a click through to the map when it lands on an
   interactive layer - so without this, clicking inside an existing
   Revier while drawing a Bereich would open the Revier's popup instead
   of adding a point, making it impossible to draw one shape over
   another. Muting them for the duration of the draw lets the click
   reach the map. */
.revier-panel #map.drawing .leaflet-interactive { pointer-events: none; }

/* A Kameraplatz that currently has a camera on it. divIcon rather than a
   circleMarker, because an SVG glyph has to go inside the circle. */
.map-kameraplatz span {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0,0,0,0.5);
  box-sizing: border-box;
  transition: border-width 0.1s ease;
}

/* This one is a divIcon, so it can't be thickened by setStyle the way the
   vector layers are - same feedback, done in CSS. */
.map-kameraplatz:hover span,
.map-kameraplatz:active span,
.map-kameraplatz.emphasised span { border-width: 4px; }

/* Base maps as squares of the actual map, not a list of names. */
/* **Aufgeklappt nach links** (19.08.2026), nicht mehr nach unten.

   Untereinander war richtig, solange der Stapel oben links hing und nach
   unten Platz hatte. Unten hat er die Knopfreihe unter sich - vier
   Quadrate übereinander lägen darüber. Also zur Seite, und seit die
   Regler in der **rechten** Ecke stehen, zur linken: nach rechts liefe
   die Reihe aus dem Bild. `row-reverse` und nicht `flex-end`, damit das
   geschlossene Quadrat an der Kante bleibt und die anderen sich davor
   schieben statt es zu verrücken. */
.base-layer-picker {
  display: flex;
  flex-direction: row-reverse;
  align-items: flex-end;
  gap: 4px;
}

.base-layer-option {
  position: relative;
  width: 52px;
  height: 52px;
  padding: 0;
  border: 2px solid #fff;
  border-radius: 4px;
  overflow: hidden;
  cursor: pointer;
  background: var(--img-placeholder);
  box-shadow: 0 1px 4px rgba(0,0,0,0.4);
}

/* Closed, the picker is just the square you're on - it doubles as the
   button that opens the rest. */
.base-layer-picker.collapsed .base-layer-option:not(.active) { display: none; }

.base-layer-option img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The name still matters - the picture alone doesn't say "contour lines"
   until you know what you're looking at. */
.base-layer-option span {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 1px 2px;
  background: rgba(0,0,0,0.6);
  color: #fff;
  font-size: 9px;
  line-height: 1.3;
  text-align: center;
}

.base-layer-option.active { border-color: var(--bar-color); }

/* Nothing to preview - the square shows the blank ground you'd get. */
.base-layer-option.blank { background: var(--img-placeholder); }
.base-layer-option.blank img { display: none; }

/* **Die Regler der Revierkarte sind so groß wie das Kartenquadrat**
   (25.08.2026, auf Wunsch: *„can the map leaflet buttons (locate, center,
   +, -) be the same size as the layer selection square? if yes, they
   should be"*). Ja, sie können.

   Sie standen auf Leaflets Maß: 30×30 im Knopf, 34 in der Leiste — neben
   einem 52er-Quadrat in derselben Ecke, mit derselben rechten Kante. Vier
   Knöpfe, die halb so groß sind wie der fünfte daneben, lesen sich als
   zwei verschiedene Sorten Bedienung, und auf dem Telefon ist 30 unter
   allem, was man mit dem Daumen sicher trifft.

   **Die Leiste gibt ihren Rahmen ab, der Knopf nimmt ihn.** Leaflets
   `.leaflet-bar` hat 2px Rand rundum; ließe man ihn stehen, wäre ein
   52er-Knopf eine 56er-Leiste und die rechte Kante der Spalte um vier
   Pixel gebrochen. Also der Rand weg, die Knöpfe auf 52 mit
   `border-box`, und der Strich zwischen zweien wandert auf den oberen
   als `border-bottom` — dieselbe Trennung, die Leaflet selbst zeichnet.

   **Nur in der Revierkarte** (`.revier-panel`). Der kleine Kartenausschnitt
   im Leuchtkasten und der Koordinatenwähler haben eigene Leaflet-Karten
   in Kästen von 200 Pixeln; 52er-Knöpfe wären dort ein Viertel des
   Bildes.

   **Und `.leaflet-container` steht in jedem dieser Wähler mit drin**,
   obwohl es nichts auswählt, was `.revier-panel` nicht schon ausgewählt
   hätte. Es ist die Zählweise: `leaflet.css` wird zur Laufzeit
   nachgeladen (`loadLeaflet` in nav.js) und steht damit **hinter** dieser
   Datei — bei gleicher Spezifität gewinnt die spätere. Und
   `.leaflet-touch .leaflet-bar a` ist genau so schwer wie
   `.revier-panel .leaflet-bar a`. Ohne die dritte Klasse passiert hier
   sichtbar nichts, was beim ersten Versuch auch genau so ausging.
   Derselbe Fund steht ein Stück weiter unten schon einmal, an
   `.leaflet-bar.locate-control a`. */
.revier-panel .leaflet-container .leaflet-bar {
  border: none;
  box-shadow: 0 1px 4px rgba(0,0,0,0.4);
}

.revier-panel .leaflet-container .leaflet-bar a,
.revier-panel .leaflet-container .leaflet-bar a:hover {
  box-sizing: border-box;
  width: 52px;
  height: 52px;
  line-height: 52px;
  border-bottom: none;
}

/* Der Strich zwischen zwei Knöpfen derselben Leiste - der einzige Rand,
   der bleibt, weil er das eine ist, was er sagt: das sind zwei. */
.revier-panel .leaflet-container .leaflet-bar a:not(:last-child) {
  border-bottom: 1px solid #ccc;
}

/* Und das Zeichen wächst mit. `+` und `−` sind bei Leaflet Text und
   hängen an `font-size`; die zwei eigenen Knöpfe tragen ein SVG, dem die
   Breite hier gesagt wird statt im Attribut - so steht die Größe an einer
   Stelle für alle vier. */
.revier-panel .leaflet-container .leaflet-control-zoom a { font-size: 24px; }
.revier-panel .leaflet-container .leaflet-bar a svg { width: 24px; height: 24px; }

/* Leaflet's own bar styling gives the box and border; this centres the
   icon in it.

   **Two class names on purpose.** `leaflet.css` is appended to the head
   at *runtime* (loadLeaflet in nav.js loads it on demand), so it comes
   after this file in the cascade — and its `.leaflet-bar a { display:
   block }` has exactly the same specificity as a plain `.locate-control
   a`. Equal specificity, later sheet, and the flex below quietly lost:
   the glyph fell back onto the text baseline of a 30px line box and sat
   low and slightly left in both buttons. Anything here that has to beat
   a Leaflet rule needs the extra class. */
.leaflet-bar.locate-control a {
  display: flex;
  align-items: center;
  justify-content: center;
  color: #333;
}

/* And the SVG itself: inline elements sit on the baseline, which leaves a
   descender's worth of space under the glyph even inside a flex box. */
.leaflet-bar.locate-control a svg { display: block; }

/* The shape being edited is draggable as a whole - grab it anywhere and
   the outline follows, corners and all (enableShapeDrag in revier.js).
   The cursor is the only thing that says so, which is why it is here and
   not left to Leaflet's default. */
.map-edit-shape { cursor: move; }

/* **Griff und Punkt sind zwei Dinge** (05.09.2026) - siehe `vertexIcon`.
   Das Symbol selbst ist ein durchsichtiges 24px-Feld und faengt den
   Daumen; sichtbar ist nur das Quadrat darin. So bleibt die Form eines
   Grenzzugs lesbar, waehrend man ihn bearbeitet. */
.map-vertex {
  background: none;
  border: 0;
  cursor: move;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* **Runde Punkte, und groesser** (04.09.2026, gemeldet: *„the point(s) of
   an element in edit mode shall be bigger circles to easily grab with
   touch fingers. (right now they seem to have become small lines)"*).

   Sie waren 12px im Quadrat mit `border-radius: 2px` - also ein
   Kaestchen. Auf einer Karte, zwischen den Linien des Elements, liest
   sich ein dunkel umrandetes 12px-Kaestchen als Strich und nicht als
   Griff. Ein Kreis ist als *Punkt* zu erkennen, und darum geht es: er
   sagt „hier kannst du anfassen".

   16px mit der Maus, 26px unter dem Finger - siehe die Regel weiter
   unten. Der unsichtbare Griff darum herum ist noch einmal groesser
   (`vertexIcon` in revier.js). */
/* **Und er wird selbst gesetzt statt sich setzen zu lassen.**
   `.map-vertex` traegt oben `display: flex`, damit der Punkt in der Mitte
   des Griffs sitzt - nur setzt Leaflet an seinen Markern
   `display: block`, und das steht spaeter im Dokument. Der Punkt war
   damit ein **inline** Kasten, und ein inline Kasten hat keine Breite und
   keine Hoehe: uebrig blieben die zwei Pixel Rahmen, also ein dunkler
   Strich. Genau das war der Bericht (*„they seem to have become small
   lines"*), und es lag nicht an der Groesse, sondern daran, dass sie
   nicht galt.

   Absolut in der Mitte des Griffs, statt sich auf das `display` des
   Elternteils zu verlassen: Leaflet setzt den Marker ohnehin absolut, und
   damit haengt der Punkt an ihm, egal was sonst noch an ihm gilt. */
.map-vertex:not(.extend)::before {
  content: "";
  display: block;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 16px;
  height: 16px;
  background: #fff;
  border: 2px solid #333;
  border-radius: 50%;
  box-sizing: border-box;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}

/* Hollow: a midpoint that doesn't exist yet until you click it. */
.map-vertex.mid::before {
  background: rgba(255, 255, 255, 0.45);
  border-style: dashed;
  border-radius: 50%;
}
.map-vertex.mid { cursor: copy; }

/* **Auf einem groben Zeiger waechst der Punkt mit.** Der Griff ist
   ohnehin schon 24px; hier geht es darum, dass man ihn auch *sieht*,
   wenn ein Finger darueber steht. `pointer: coarse` fragt nach dem
   Zeigergeraet und nicht nach der Bildschirmbreite - ein Tablet mit
   Stift ist kein Telefon. */
@media (pointer: coarse) {
  .map-vertex:not(.extend)::before { width: 26px; height: 26px; border-width: 3px; }
}

/* Sits just past a line's end: arms that end for the next map click. */
.map-vertex.extend {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--bar-color);
  border-color: #fff;
  color: #fff;
  font-size: 15px;
  font-weight: bold;
  line-height: 1;
  cursor: crosshair;
}

.map-vertex.extend.active {
  background: #d98324;
  box-shadow: 0 0 0 3px rgba(217, 131, 36, 0.4);
}

/* The shape picker: the shapes and nothing else, laid over the whole map
   until one is chosen. No panel, no heading - the question ("what are you
   placing?") is obvious from the buttons. */
.map-shape-picker { display: none; }

.revier-panel.open .map-shape-picker.visible {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  /* Over the map and Leaflet's control corners (z-index 1000), but *not*
     over the element drawer - see below. */
  z-index: 1200;
  display: flex;
  flex-direction: column;   /* shapes, then the import option */
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: 24px;
  background: rgba(0, 0, 0, 0.45);
}

/* The drawer stays out from under it, and stays usable.

   Hiding what is already drawn is *part of* placing something new: you
   clear the ground you are about to work on, and a Kameraplatz goes on
   the same spot a Hochsitz is already sitting on. The picker used to
   cover the drawer for the whole flow, which meant the one moment the eye
   is most wanted was the one moment it could not be reached.

   So the drawer and the button that opens it sit above the picker, and
   the picker steps aside from the drawer rather than dimming it - the
   same 300px shift the tab bar and the attribution already make. Clicking
   the drawer therefore does not dismiss the picker either: the
   click-the-backdrop-to-back-out handler is on the picker, and the drawer
   is no longer inside it. */
.revier-panel.map-sidebar-open .map-shape-picker.visible { right: 300px; }

/* The drawer's own z-index is on its rule further down; this is the
   button that opens it, which has to come with it or a shut drawer stays
   shut for the length of the flow. */
.revier-panel .map-sidebar-fab { z-index: 1300; }

/* Set apart from the shapes: it puts elements on the map too, but from a
   file rather than by drawing. */
.map-picker-extra { text-align: center; }

.map-picker-extra button {
  padding: 10px 18px;
  font-size: 14px;
  border-style: dashed;
  border-width: 2px;
}

.map-kinds {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  max-width: 620px;
  max-height: 100%;
  overflow-y: auto;
}

/* Big enough to hit without aiming, on a phone in a wood. The border
   carries the colour the shape will be drawn in. */
.map-kinds button {
  min-width: 150px;
  padding: 18px 20px;
  font-size: 16px;
  border-width: 3px;
  border-radius: 8px;
  background: var(--control-bg);
  box-shadow: 0 2px 10px rgba(0,0,0,0.4);
  cursor: pointer;
}

.map-kinds button:hover { background: var(--bar-color); color: #fff; }

/* Reclassifying rather than drawing: the kind it already is, marked so
   the row says what you are changing *from*. Filled instead of outlined,
   because the border is already spoken for by the kind's colour. */
.map-kinds button.current {
  background: var(--bar-color);
  color: #fff;
  cursor: default;
}

/* Sits above the action buttons, outside the picker, because it carries
   the running measurement while drawing - when the picker is closed. */
/* **Über der Knopfreihe, unten links** (19.08.2026, endgültig).

   Auch er hat an einem Tag drei Plätze gehabt, und die Runde erklärt sich
   selbst: über den Knöpfen, dann neben Leaflets Reglern, dann oben links -
   und dort **verdeckte ihn das Elementfenster**, sobald eines offen war.
   Oben rechts steht das Fenster, oben links wäre frei, aber ein Kasten,
   der beim Zeichnen erscheint, gehört dorthin, wo gezeichnet wird.

   Jetzt hat er die Höhe für sich, die die Regler geräumt haben: unten
   links über der Reihe. Der Text, der zum Knopf gehört, steht über dem
   Knopf.

   `pointer-events: none`, weil er über der Karte liegt: ein Kasten, der
   nichts tut, darf keinen Klick schlucken, der der Karte gilt. */
.map-hint {
  position: absolute;
  /* **Der Abstand kommt aus der Reihe darunter, nicht aus einer Zahl**
     (20.08.2026). Er war 88px, die Reihe steht 24px über dem Rand und ist
     56px hoch - also blieben acht Pixel, und ein zweizeiliger Satz auf
     einem schmalen Schirm stieß an das `+`. Er wächst nach oben (der
     untere Rand steht fest), das war nie das Problem; der Abstand war es.

     Aus den zwei Werten gerechnet, damit die beiden nicht auseinander
     laufen können: wer die Reihe verschiebt, verschiebt den Kasten mit. */
  bottom: calc(var(--map-reihe-abstand, 24px) + var(--map-reihe-hoehe, 56px) + 14px);
  left: 24px;
  z-index: 700;
  pointer-events: none;
  max-width: min(320px, calc(100% - 120px));
  background: var(--control-bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 10px;
  font-size: 12px;
  color: var(--muted-fg);
}

.map-hint:empty { display: none; }

/* The picker covers the map, so the hint underneath would just be noise. */
.map-shape-picker.visible ~ .map-hint { display: none; }

/* Leaflet's popup is white; these live inside it, not on the dark page. */
/* Clicking a shape focuses its SVG path, and the browser then draws its
   focus ring around the path's *bounding box* - a black rectangle that
   has nothing to do with the shape and reads as a second, wrong
   selection. Which element is picked is already said by the thicker
   line, so the ring only misleads. */
.leaflet-container path.leaflet-interactive:focus,
.leaflet-container .leaflet-marker-icon:focus,
.leaflet-container .leaflet-interactive:focus { outline: none; }

/* Hover label naming the element under the pointer. Dark rather than
   Leaflet's white: it sits over aerial imagery as often as over a pale
   map. The arrow is a CSS border, so its colour has to be restated for
   every direction Leaflet might flip the tooltip to. */
.map-feature-tooltip {
  background: rgba(20, 24, 20, 0.88);
  color: #fff;
  border: none;
  box-shadow: none;
  font-size: 12px;
  font-weight: bold;
  padding: 3px 8px;
}
.map-feature-tooltip.leaflet-tooltip-top::before { border-top-color: rgba(20, 24, 20, 0.88); }
.map-feature-tooltip.leaflet-tooltip-bottom::before { border-bottom-color: rgba(20, 24, 20, 0.88); }
.map-feature-tooltip.leaflet-tooltip-left::before { border-left-color: rgba(20, 24, 20, 0.88); }
.map-feature-tooltip.leaflet-tooltip-right::before { border-right-color: rgba(20, 24, 20, 0.88); }

.map-popup { color: #222; min-width: 160px; }
.map-popup-title { font-weight: bold; margin-bottom: 2px; }
.map-popup-kind { font-size: 12px; color: #666; margin-bottom: 8px; }
.map-popup select {
  width: 100%;
  margin-bottom: 6px;
  color: #222;
  background: #fff;
  border: 1px solid #bbb;
  border-radius: 3px;
  padding: 3px 4px;
  font-size: 12px;
}
.map-popup-link { display: block; margin-bottom: 8px; font-size: 12px; }

.map-popup-note { margin-bottom: 8px; }

.map-popup-note textarea {
  width: 100%;
  box-sizing: border-box;
  padding: 4px 6px;
  font-size: 12px;
  font-family: inherit;
  color: #222;
  background: #fff;
  border: 1px solid #bbb;
  border-radius: 3px;
  resize: vertical;
}
.map-popup-actions { display: flex; gap: 6px; }
.map-popup-actions button { color: #222; background: #eee; border: 1px solid #bbb; }
.map-popup-actions .delete-button { background: #b33; color: #fff; border-color: #922; }

/* Element list: same right-hand drawer as the gallery's filter pane,
   but it overlays the map rather than pushing it - resizing the map
   container would mean an invalidateSize() dance on every toggle. */
.map-sidebar {
  position: fixed;
  top: 0;
  right: 0;
  height: 100vh;
  width: 300px;
  background: var(--control-bg);
  border-left: 1px solid var(--border);
  /* Over Leaflet's control corners. Those are `.leaflet-top`/`.leaflet-bottom`
     at z-index 1000 - the 800 on `.leaflet-control` itself sits inside that
     stacking context and doesn't decide anything here. Without this the OSM
     attribution paints over the open drawer.
     Over the shape picker (1200) as well - see the note there: the eye is
     wanted most while something is being placed. */
  z-index: 1300;
  transition: transform 0.25s ease;
  transform: translateX(0);
  display: flex;  /* close bar + scrolling body */
}

/* Same as the filter pane: the panel tracks the finger mid-swipe. */
.map-sidebar.dragging { transition: none; }

.map-sidebar.collapsed { transform: translateX(100%); }

/* The map tools moved left, but the OSM attribution is still bottom
   right, and the open drawer would swallow it. */
/* Seit dem 19.08. stehen dort auch die Regler, nicht nur die
   Herkunftszeile - beide sollen nicht unter dem Regal verschwinden. Der
   Wert addiert sich nicht zu den 24px oben, er ersetzt sie. */
.revier-panel.map-sidebar-open .leaflet-bottom.leaflet-right { margin-right: 324px; }

/* **Und sie rücken für nichts mehr beiseite.** Hier stand die Regel, die
   sie beim Zeichnen 64px nach unten schob, weil das × die obere linke
   Ecke belegte. Das × steht jetzt am Ende der Knopfreihe unten (siehe
   `.map-fab.cancel`), also gibt es nichts mehr auszuweichen - und das
   war ausdrücklich der Wunsch: was man im Bearbeiten braucht, soll dort
   sein, wo es vorher war. */

.map-sidebar-option {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
  cursor: pointer;
}

/* At the top of the drawer, above everything it searches. */
.map-sidebar-search {
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}

.map-sidebar-search input {
  width: 100%;
  box-sizing: border-box;
  background: var(--control-bg);
  color: var(--fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 5px 8px;
  font-family: inherit;
  font-size: 13px;
}

.map-popup-thumb {
  display: block;
  width: 180px;
  max-width: 100%;
  border-radius: 3px;
  margin-bottom: 6px;
}

.map-sidebar-group {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 12px 4px 12px;
  border-left: 4px solid transparent;
  font-size: 12px;
  font-weight: bold;
  color: var(--muted-fg);
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
}

/* Same caret as the gallery's filter categories, for the same gesture. */
.map-sidebar-group::before {
  content: "\25be";
  flex: 0 0 auto;
}

.map-sidebar-group.collapsed::before { content: "\25b8"; }

/* Pushes the eye to the right edge; the caret and label stay left. */
.map-sidebar-group > span:first-of-type { flex: 1 1 auto; }

/* The list's own header (#featureListHeader): the same row one level up,
   over every group rather than over one kind. Same caret and same eye in
   the same column, so the gesture is learnt once - what tells it apart is
   that it carries no kind colour on the left border (that stripe means "a
   kind", and this row is over all of them) and a rule under it, which is
   where the list proper starts. It sits above the list rather than at the
   end of it because with thirty elements drawn it is what you reach for
   first. */
.map-sidebar-all .map-sidebar-group.all {
  padding-top: 8px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Hidden elements stay listed - they're how you get them back. */
.map-sidebar-group.hidden > span:first-of-type,
.map-sidebar-row.hidden .map-sidebar-name,
.map-sidebar-row.hidden .map-sidebar-swatch { opacity: 0.4; }

.map-eye {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  padding: 2px;
  cursor: pointer;
  color: var(--muted-fg);
}

.map-eye:hover { color: var(--fg); }
.map-eye.off { opacity: 0.55; }

/* **Die Kette neben dem Auge** (09.09.2026, `0058`). Dieselbe Grösse und
   dasselbe Polster wie das Auge, weil es dieselbe Sorte Griff an
   derselben Kante ist - zwei Zeichen, die sich um zwei Pixel
   unterscheiden, liest man als Fehler.

   **Schwarz oder grün**, und das ist die ganze Auskunft: schwarz heisst
   „geht nirgends absichtlich hinaus", grün heisst „geht". Dieselbe Farbe
   wie überall in dieser Anwendung für *das ist an* - die Kette an der
   Kachel, der eingeschaltete Stift, das Kettenzeichen im Leuchtkasten.
   Im Ruhezustand nimmt sie die gedämpfte Schrift der Liste und nicht
   reines Schwarz: die Liste steht auf dunklem Grund, und „schwarz" heisst
   hier „so still wie das Auge daneben". */
.map-share {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  padding: 2px;
  cursor: pointer;
  color: var(--muted-fg);
}

.map-share:hover { color: var(--fg); }

/* **Geteilt: weiss in einer gefüllten grünen Scheibe** (09.09.2026, auf
   Wunsch). Es war der blosse Strich in `--bar-color`, und das war zu
   leise für die eine Zeile unter dreissig, auf die es ankommt - genau
   dieselbe Korrektur wie am Kettenzeichen im Leuchtkasten am selben Tag:
   dieses Grün ist überall sonst ein **Grund** mit Weiss darauf und nie
   eine Strichfarbe.

   Die Scheibe steht nur, wenn sie etwas sagt. Ungeteilt bleibt das
   Zeichen ein stiller Strich neben dem Auge, und die Zeile hat 30
   Pixel weniger Betrieb. */
.map-share.geteilt {
  color: #fff;
  background: var(--bar-color);
  border-radius: 50%;
  /* Dasselbe Kästchen wie ungeteilt (14px Zeichen plus 2px Polster), nur
     gefüllt - die Reihe darf beim Umschalten nicht springen. */
  padding: 3px;
  margin: -1px;
}

.map-share.geteilt:hover { filter: brightness(1.15); }

.map-sidebar-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px 5px 16px;
  font-size: 13px;
  cursor: pointer;
}

.map-sidebar-row:hover { background: rgba(255,255,255,0.06); }

/* The one picked from the list - matched by a thicker outline on the map,
   so the row and the shape agree about what you're looking at. */
.map-sidebar-row.active { background: rgba(255,255,255,0.09); }
.map-sidebar-row.active .map-sidebar-name { font-weight: bold; }

.map-sidebar-swatch {
  width: 10px;
  height: 10px;
  border-radius: 2px;
  flex: 0 0 auto;
}

.map-sidebar-name {
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* One row per thing found in the file, with what it should become. */
.gpx-list { max-height: 44vh; overflow-y: auto; margin: 10px 0; }

.gpx-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 0;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
}

.gpx-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gpx-row select { flex: 0 0 auto; max-width: 45%; }

.map-sidebar-empty {
  padding: 16px 12px;
  font-size: 13px;
  color: var(--muted-fg);
}

.map-sidebar-fab,
.map-fab {
  position: fixed;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--bar-color);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,0.35);
  z-index: 800;
}

/* **Bottom right, above the filters.** It went to the top corner for a
   day, on the argument that the drawer comes from the right edge; what
   that missed is that the drawer's button and the filter buttons are the
   same kind of thing - the controls of the map you are standing on - and
   splitting them across two corners means looking in two places. So they
   stack: the filters along the bottom, this one over them.

   It only exists while the drawer is shut, so it never collides with it,
   and the element window has the top corner back to itself.

   24 for the margin, 56 for the filter row, 12 of air. On a phone the
   band stops above the nav bar and the whole stack goes up with it - see
   the media query at the foot. */
/* `bottom` steht weiter oben, bei der Begründung dazu - hier nur noch,
   dass es den Knopf ohne offene Karte nicht gibt. */
.map-sidebar-fab { display: none; right: 24px; }
.revier-panel.open .map-sidebar-fab.visible { display: flex; }

/* Bottom-left cluster: the primary button, with undo/redo beside it.
   They're laid out by the container so the row can't drift apart. */
.map-actions {
  position: fixed;
  bottom: var(--map-reihe-abstand, 24px);
  left: 24px;
  display: flex;
  align-items: center;
  gap: 10px;
  z-index: 800;
}

.map-actions .map-fab { position: static; }

/* Smaller, and dimmer, than the button that commits the work. */
.map-fab.secondary {
  display: none;
  width: 40px;
  height: 40px;
  background: var(--control-bg);
  border: 1px solid var(--border);
}

.map-fab.secondary svg { stroke: var(--fg); }

.map-fab.disabled { opacity: 0.35; cursor: default; }

/* **Wartet auf eine fremde Antwort.** Der Knopf „auf Wege ziehen" holt die
   Wege bei OpenStreetMap; das dauert je nach Ausschnitt eine bis fünf
   Sekunden, und ein Knopf, der in dieser Zeit aussieht wie immer, wird
   ein zweites und ein drittes Mal gedrückt. Ein Pulsieren statt eines
   Kreisels: das ist der Knopf selbst, der arbeitet, und nicht die Karte. */
.map-fab.busy { animation: fab-puls 1s ease-in-out infinite; }

@keyframes fab-puls {
  0%, 100% { opacity: 0.35; }
  50% { opacity: 0.8; }
}

/* Armed: the + while the picker is open. */
.map-fab.active { background: #d98324; }

/* The + is always there now - it is what this corner is for. It used to
   appear only once a pencil beside it had been pressed, which cost two
   presses before the first click of every draw. Editing an element is
   that element's own window; see `.map-inspector-head` below. */
.map-fab.add { display: flex; }

/* **Weg nur, wo nichts anzulegen ist**: für einen Gast und in einer
   Gesellschaft (revier.js, `darfBearbeiten`).

   Er verschwand früher auch, während die Ecken eines bestehenden
   Elements gezogen wurden - das ✓ dafür stehe ja im Fenster des
   Elements. Das stimmte und war trotzdem falsch herum: der Daumen ist
   unten, rückgängig ist unten, das Fenster ist in der anderen Ecke. Jetzt
   steht in beiden Fällen dasselbe ✓ unten links, und das im Fenster tut
   weiter dasselbe. */
.revier-panel .map-fab.add.gone { display: none; }
/* Dieselbe Regel fuer die zwei Knoepfe, die 2026-09-04 aus dem
   Elementfenster in die Reihe gezogen sind. `.revier-panel` davor, damit
   sie dasselbe Gewicht hat wie die Zeile darueber und nicht von der
   Grundregel ueberstimmt wird. */
.revier-panel .map-fab.stift.gone,
.revier-panel .map-fab.papierkorb.gone { display: none; }

/* **`.gone` sticht die Anzeige-Regel**, und die Spezifität muss dafür
   reichen: `.revier-panel.open.map-shaping .map-fab.secondary` schaltet
   die Nebenknöpfe beim Zeichnen ein, und das sind drei Klassen gegen
   zwei. Der Knopf „auf Wege ziehen" wird bei einem Punkt-Element aber
   genau *während* des Zeichnens weggenommen - also muss die Ausnahme
   dieselbe Ebene erreichen. */
.revier-panel.open.map-shaping .map-fab.secondary.gone { display: none; }

/* **Am Ende der Reihe unten links** (2026-08-19), nicht mehr oben in der
   Ecke.

   Das alte Argument war gut: Verwerfen und Abschließen sollen sich keine
   Ecke teilen. Es hat nur übersehen, wo die Hand ist - auf einem Telefon
   ist die obere linke Ecke die eine, die ein Daumen nicht erreicht, und
   die Geste, für die man sie brauchte, ist ausgerechnet die, mit der man
   aufhört. Jetzt steht die ganze Zeichnung in einer Reihe: ✓, rückgängig,
   wiederherstellen, auf Wege ziehen, ×.

   **Und seit dem 04.09.2026 steht es vorn**, an der Stelle, an der beim
   Ansehen der `+` steht (auf Wunsch). Das kehrt den Gedanken von damals
   um - „abbrechen" liegt jetzt gerade *neben* „fertig" -, und der Grund
   dafuer ist derselbe Daumen: die Reihe faengt dort an, wo er sie zuletzt
   angefasst hat, und der Weg hinaus liegt unter demselben Finger wie der
   Weg hinein. Der Abstand von 18px ist damit hinfaellig; er sitzt jetzt
   am Papierkorb, der das Ende der Reihe geerbt hat. Rot bleibt rot. */
.map-fab.cancel {
  display: none;
  width: 44px;
  height: 44px;
  background: #7a2020;
  border: 1px solid #a33;
}

/* **Der Stift, rechts vom `+`** (04.09.2026). Wie die kleinen Knoepfe der
   Reihe gebaut, nur nicht abgeblendet: er ist der Weg *hinein* und kein
   Beiwerk zu einer laufenden Zeichnung. `display: flex` als Regel, `gone`
   als Ausnahme - anders als bei `.secondary`, das nur beim Zeichnen
   ueberhaupt da ist. */
.map-fab.stift {
  display: flex;
  width: 44px;
  height: 44px;
  background: var(--bar-color);
  border: 1px solid var(--bar-color);
}

/* **Der Papierkorb am Ende**, mit dem Abstand, den vorher das × trug:
   was nicht rueckgaengig zu machen ist, liegt am weitesten von dem
   entfernt, was man dauernd drueckt. */
.map-fab.papierkorb {
  display: flex;
  width: 40px;
  height: 40px;
  margin-left: 18px;
  background: #7a2020;
  border: 1px solid #a33;
}

/* **Setzen** (10.09.2026), in der Reihe zwischen Stift und Korb. Dieselbe
   Scheibe wie der Stift und in der Farbe des Bandes: es ist ein Griff wie
   jeder andere und ausdrücklich nichts, was sich nicht zurücknehmen lässt
   - der Korb trägt seine eigene Farbe, weil er das eine ist, was das
   nicht kann. */
.map-fab.setzen {
  display: flex;
  width: 40px;
  height: 40px;
}

.revier-panel .map-fab.setzen.gone { display: none; }

/* `.open` so this can never contradict the ribbon-mode rule that hides
   every FAB - a shape can only be in progress on an open map anyway. */
.revier-panel.open.map-shaping .map-fab.secondary,
.revier-panel.open.map-shaping .map-fab.cancel { display: flex; }

/* "Anblick (412)" and "Einrichtungen (7)", top right: where the pick can
   be taken, and what it is worth there. Pills rather than circles,
   because they are the buttons here with something to say - and their own
   buttons rather than a state of the + at the bottom left, so drawing an
   element never has to wait for a selection to be dealt with first. */
/* **Rechts oben, seit das Fenster nach links gezogen ist** (20.08.2026).

   Sie standen *links neben* dem Fenster, das die rechte Ecke hatte; jetzt
   hat das Fenster die linke, und die Pillen nehmen die freie. Sie hängen
   ohnehin nicht am Fenster - sie sagen, wohin eine Auswahl mitgenommen
   werden kann, und sind auch ohne offenes Fenster da. */
/* **Die zwei gruenen Pillen oben rechts sind fort** (05.09.2026,
   gemeldet: *„on the map there sometimes are the old Anblick And
   Einrichtung FABs (the green ones) stray on the top. they should be
   gone"*). `.map-show`, `.map-show-button` und die zwei Ausnahmeregeln
   fuer das offene Regal standen hier; was sie sagten, sagt seit dem
   04.09.2026 die Knopfreihe des Elementfensters
   (`.map-inspector-reihe`). Das *manchmal* im Bericht war der eine
   Zustand, fuer den sie noch erschienen: etwas gewaehlt, das Fenster zu. */

/* ---- The filters, on the open map ----

   The map draws the *answers* now (`0026`) and takes the gallery's own
   filters to decide which - so with the band open full screen, the four
   buttons that ask the questions have to be reachable. They are the same
   four buttons in the same corner they occupy on the grid, lifted over
   the open band; nothing about them changes except that they can be seen.

   The bottom right is free for them because the drawer's button left it
   for the top right, which is the other half of this change.

   Only where there is a gallery to filter: these selectors name elements
   that exist on index.html and nowhere else. */
body.revier-open .gallery-fabs {
  z-index: 950;   /* over the open band (900) */
  bottom: 24px;
  right: 24px;
}

/* **On the map, or on a grid - not over a list.** Every page with a
   Revier band loads filters.js now, and on the Reviereinrichtungen and
   Ausrüstung pages the four buttons narrow the *map's* Anblicke and
   nothing else: floating them over a list of Hochsitze they do not touch
   would be three buttons that lie about what they do. The gallery is the
   page whose whole content they narrow, and it is `html.gallery-page`. */
html:not(.gallery-page) body:not(.revier-open) .gallery-fabs,
html:not(.gallery-page) body:not(.revier-open) .gallery-filter { display: none; }

/* The panel opens upwards out of them and has to come with them, or the
   answer would open behind the map it is about. */
body.revier-open .gallery-filter {
  z-index: 940;
  bottom: 96px;   /* 24 + the 56px row + air */
}

/* **With the element list out, they go under the band.**

   This is the one place "the drawer covers them" can be made true, and it
   is not the drawer that does it. The drawer is inside `.revier-panel`,
   which has a z-index of its own (900) and is therefore a stacking
   context, so its 1300 is a number *within the band*; these two are
   appended to `document.body` by filters.js and are the band's siblings,
   so what they are really measured against is the band's 900 - and the
   950/940 above beat it. What that looked like was the filter row and its
   open sheet painting across the element list, which is the one thing the
   rule below promises will not happen. No number on the drawer can mend
   that; the row has to drop below the band instead. Back to the numbers
   it wears on the grid - 850 over 840, the row still over its own sheet -
   both under the band's 900.

   **The default, with the lift as the exception**, and that is the
   correction: this was a `max-width: 819px` block facing the `min-width:
   820px` one below, and every fractional width between the two matched
   *neither* - 1024px at 125% display scaling is 819.2 CSS pixels - so the
   row kept its 950 and stayed over the list on exactly the screens the
   pair was written for. Stated the other way round there is no width to
   fall through: covered unless the screen is provably wide enough to step
   aside.

   The cost, stated rather than hidden: below 820px the band is the whole
   screen, so under it means *not drawn*, not "drawn behind the list". An
   open sheet is not closed by this - it comes back the moment the list
   goes, exactly like the map's own `+` and the element window. The way
   to the four questions is the close stripe down the drawer's left edge,
   one tap away, which is the same way back to everything else a drawer
   covers on a phone. */
body.revier-open.map-list-open .gallery-fabs { z-index: 850; }
body.revier-open.map-list-open .gallery-filter { z-index: 840; }

/* **Und während die Formen zur Wahl stehen, liegen sie darunter**
   (2026-08-19).

   Die Auswahl der Elementart legt sich über die ganze Karte (z-index 1200
   *im Band*), und darüber standen die vier Filterknöpfe der Galerie -
   sichtbar, anklickbar, und mit einem Blatt, das über der Auswahl
   aufgegangen wäre. Keine Zahl an der Auswahl kann das beheben: sie liegt
   im Band, das Band ist bei 900 ein Stapelkontext, und die Knöpfe hängen
   mit 950 am `body`.

   Also dieselbe Antwort wie beim offenen Elementregal, an derselben
   Stelle argumentiert: **die Reihe fällt unter das Band**, statt dass die
   Auswahl über sie zu steigen versucht. Sie kommt wieder, sobald eine
   Form gewählt ist - und dann greift die Regel darunter, die sie fürs
   Zeichnen ganz wegnimmt. */
body.map-picker-open .gallery-fabs { z-index: 850; }
body.map-picker-open .gallery-filter { z-index: 840; }

/* **Und beim Zeichnen sind sie ganz weg** (2026-08-19).

   Die vier Filterknöpfe fragen die Galerie etwas; wer gerade eine Grenze
   zieht, fragt die Galerie nichts. Sie standen in derselben unteren
   rechten Ecke, in der beim Zeichnen die Karte selbst gebraucht wird,
   und die einzige Antwort auf einen Druck wäre gewesen: ein Filterblatt
   über der halb gezogenen Form.

   `body.map-shaping` und nicht die Klasse am Band: die Knöpfe hängen am
   `body` (filters.js), sind also Geschwister des Bandes und nicht seine
   Kinder - eine Regel innerhalb des Bandes erreicht sie nicht. Das
   offene Blatt geht mit; es kommt zurück, wenn die Form fertig ist,
   genauso wie beim offenen Elementregal darüber. */
body.map-shaping .gallery-fabs,
body.map-shaping .gallery-filter { display: none; }

/* **Auf dem Telefon reicht „darunter" nicht** (2026-08-19).

   Dort ist das Band der ganze Bildschirm, also ist „unter dem Band" schon
   „nicht gezeichnet" - für die Filterknöpfe. Der Knopf des Elementregals
   liegt aber *im* Band (bei 1300) und stünde damit über der Auswahl: ein
   Kreis in der rechten unteren Ecke, mitten in der Frage „was legst du
   an?", der ein Regal aufzieht statt zu antworten. Auf einem Schreibtisch
   ist daneben Platz für beides und er bleibt.

   Die Filterknöpfe stehen hier ausdrücklich mit `display: none` und nicht
   nur eine Ebene tiefer: was unter einem Blatt liegt, das den ganzen
   Bildschirm bedeckt, ist unsichtbar - aber ein `pointerdown` auf einem
   Telefon landet gern zwei Pixel neben dem, was man meint, und ein Knopf,
   der nicht da ist, kann nicht getroffen werden. */
@media (max-width: 700px) {
  body.map-picker-open .gallery-fabs,
  body.map-picker-open .gallery-filter { display: none; }
  /* Voll ausgeschrieben, weil `.revier-panel.open .map-sidebar-fab.visible`
     der Gegenspieler ist und vier Klassen trägt: eine Regel, die weniger
     wiegt, verliert hier unabhängig von der Reihenfolge. */
  body.map-picker-open .revier-panel.open .map-sidebar-fab.visible { display: none; }
}

/* **Left of the element drawer where there is room, under it where there
   is not.**

   The drawer slides in over this exact corner. Being covered by it is
   what everything else behind an open drawer does here
   (`.map-inspector`) - but that one is a *readout*, and these are the
   four questions the map is answering. A filter row you cannot reach
   while the list of elements is open means closing the list to ask, on
   the one screen where both are about the same map.

   **The drawer's 1300 does not cover them, and cannot** - the rule above
   is where that is argued and where it is answered. Here the row has
   stepped clear of the drawer, so nothing is overlapping and it can have
   its 950 back: over the band, where it belongs while it is reachable.

   This was tried once and taken out again, on the argument that the
   buttons moved out from under your thumb whenever the list opened. That
   argument is a phone's: there, stepping left would put them in the
   middle of what little map is left, and the row would have nowhere to go
   that is not also the drawer. So the step is only made where the screen
   has the room for both - 300px of drawer, the panel that opens out of
   the row, and map still visible beside them - and below that they stay
   in their corner and are covered by the band, per the rule above.

   `right`, not a transform: the panel opens out of these buttons and is
   anchored to the same edge, so both move with one number. */
@media (min-width: 820px) {
  body.revier-open.map-list-open .gallery-fabs,
  body.revier-open.map-list-open .gallery-filter { right: 324px; }
  /* `:not(.map-picker-open)` ist die eine Ausnahme von der Rückkehr auf
     950: steht die Elementauswahl offen, gehören die Knöpfe darunter, und
     diese Regel hier ist die spezifischere - ohne die Ausnahme gewönne sie
     ausgerechnet auf dem Schreibtisch, wo beide Platz haben und der
     Fehler deshalb am ehesten übersehen wird. */
  body.revier-open.map-list-open:not(.map-picker-open) .gallery-fabs { z-index: 950; }
  body.revier-open.map-list-open:not(.map-picker-open) .gallery-filter { z-index: 940; }
}

/* ---------------------------------------------------------------
   The element window - what a picked element says about itself.

   It replaced the Leaflet popup, which opened on the click: it covered
   the thing it was describing, moved with the map, and near an edge
   opened half off-screen. This is a fixed card instead, so it's always in
   the same place and never over the element.

   **Oben links, seit dem 20.08.2026** - vorher oben rechts. Zwei Gründe,
   und der zweite wiegt mehr:

   - rechts ist inzwischen die volle Spalte: das Elementregal, sein Knopf,
     die Filterknöpfe und seit gestern Leaflets Regler. Das Fenster war
     das fünfte Ding in derselben Ecke und musste als einziges
     beiseiterücken, sobald das Regal aufging (die Regel dafür ist unten
     und ist damit erledigt);
   - und links ist seit gestern frei - das × ist in die Knopfreihe
     gezogen, die Regler sind nach rechts. Ein Fenster, das immer am
     selben Platz steht, ist der ganze Grund, warum es kein Popup ist;
     dann soll dieser Platz auch keiner sein, um den es sich mit vier
     anderen streitet.

   Light, not dark like the rest of the map chrome: it holds the popup's
   own markup (`.map-popup*` above), all of it built for white.
   --------------------------------------------------------------- */
.map-inspector {
  /* absolute, like the FABs: the panel is the containing block, and
     `fixed` would escape it - see the note further up. */
  position: absolute;
  /* 48 clears the tab bar the open band wears. */
  top: 48px;
  left: 24px;
  width: 280px;
  max-width: calc(100% - 48px);
  /* Room for the tab bar above and the bottom row of controls below, so
     a Revier with a long note scrolls inside the card rather than off the
     map. */
  max-height: calc(100% - 190px);
  display: none;
  flex-direction: column;
  background: #fff;
  color: #222;
  border-radius: 8px;
  box-shadow: 0 6px 20px rgba(0,0,0,0.45);
  overflow: hidden;   /* keeps the coloured header inside the corners */
  /* Over the map and its controls, under the element drawer - the drawer
     is the thing that's meant to cover it. */
  z-index: 810;
}

.revier-panel.open .map-inspector.visible { display: flex; }

/* Painted in the kind's colour by openInspector(), text and all: the
   kinds run from a near-white yellow to a dark brown, so neither ink
   works for all of them and it's chosen per colour.

   Also the card's handle: a click folds the body away and leaves this bar
   on the map. Hence the pointer - the whole strip is pressable, and the
   controls inside it stop the click before it gets here. */
/* **Der Kopf ist jetzt zwei Zeilen** (06.09.2026): oben Name, Mass und
   das ×, darunter die Adresse als Chips. Er waechst mit ihr - das war der
   Wunsch -, und weil er beim Zuklappen stehenbleibt, behaelt man die
   Auskunft auch dann. */
.map-inspector-head {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 12px;
  cursor: pointer;
}

/* Die obere Zeile ist, was der Kopf vorher ganz war. */
.map-inspector-oben {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}

/* **Wo es liegt.** Umbrechend, denn drei Namen passen auf 280px nicht
   nebeneinander, und ein Streifen, der seitwaerts rollt, ist in einer
   Kartenecke eine Geste, die niemand findet. Die Chips tragen ihre eigene
   Farbe (`paintKindColour`) und stehen deshalb auch auf dem farbigen Kopf
   lesbar da. */
.map-inspector-wo {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.map-inspector-wo[hidden] { display: none; }

.map-inspector-heading { flex: 1; min-width: 0; }

.map-inspector-title {
  font-weight: bold;
  font-size: 15px;
  line-height: 1.25;
  /* An element can be named anything; the card doesn't grow to fit. */
  overflow-wrap: anywhere;
}

/* "Bereich · 12.4 ha". Dimmed rather than grey - the background here is
   the kind's colour, and a fixed grey would vanish into half of them. */
.map-inspector-measure { font-size: 12px; opacity: 0.85; margin-top: 2px; }

/* In edit mode the name and the kind are the controls that change them.
   Underlined on hover rather than drawn as buttons: a header full of
   chrome would bury the two words that say what you are looking at. */
.map-inspector.editing .map-inspector-title {
  cursor: text;
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 3px;
}

.map-inspector-kind {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  padding: 0;
}

/* Only the kind half is the button - the measurement beside it is a fact
   about the shape, not a choice. A Point has no kind to swap to, so it
   stays a plain span and this never applies to it. */
button.map-inspector-kind {
  cursor: pointer;
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 3px;
}

button.map-inspector-kind::after { content: " ▾"; }

/* Bin, pencil-or-✓, ×. In a row while the name is short enough to leave
   them the width, and stacked into the corner when it isn't - see
   layOutInspectorHead(). */
.map-inspector-buttons {
  flex: none;
  display: flex;
  align-items: flex-start;
  gap: 2px;
}

.map-inspector-head.stacked .map-inspector-buttons { flex-direction: column; }

.map-inspector-button {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  color: inherit;   /* the header's ink, whichever it turned out to be */
  background: transparent;
  border: none;
  cursor: pointer;
  opacity: 0.8;
}

.map-inspector-button:hover { opacity: 1; }
.map-inspector-button[hidden] { display: none; }

/* Folded: the coloured bar and nothing else. The point is to see the
   ground under the card without letting go of what is picked, so the
   buttons at its foot go too - they are about where the pick can be
   taken, and looking at the map is not that. */

/* **Die Knopfreihe bleibt stehen, wenn das Fenster zugeklappt ist**
   (05.09.2026, auf Wunsch). Zugeklappt sind Kopf und Reihe - also genau
   das, wofür man das Fenster meistens aufmacht: wie viele, und wohin
   damit. Der Rumpf darunter geht weg. */
/* Zugeklappt bleibt die Knopfreihe stehen - **und der Bilderstreifen**
   (04.09.2026). Er gehoert zum Kopf und nicht zur Auskunft: was ein
   Hochsitz ist, sieht man an seinem Bild, und das soll dastehen, bevor
   man irgendetwas aufklappt. Er steht trotzdem im Rumpf, weil er
   *zwischen* Kopf und Reihe gehoert und der Kopf seine eigene Zeile ist. */
.map-inspector.shut .map-inspector-body { display: none; }

/* The open-task count, in the header beside the name. Hidden when there
   is nothing open - a badge reading 0 is a badge that has stopped meaning
   anything. It keeps its amber against the kind's colour on purpose: it
   is the same mark as the badge on the map, and the map has no palette
   for "urgent" other than this one. */
/* Everything but the placement comes from `.task-chip`, which it also
   wears - one definition of what an open-task count looks like, on the
   map, in the lists and here. */
.map-inspector-tasks {
  display: none;
  align-self: center;
  box-shadow: 0 1px 3px rgba(0,0,0,0.3);
}

.map-inspector-tasks.visible { display: block; }

.map-inspector-close {
  flex: none;
  width: 24px;
  height: 24px;
  padding: 0;
  font-size: 20px;
  line-height: 1;
  color: inherit;   /* the header's ink, whichever it turned out to be */
  background: transparent;
  border: none;
  cursor: pointer;
  opacity: 0.8;
}

.map-inspector-close:hover { opacity: 1; }

/* **Die Kette links vom Ausgang** (09.09.2026). Dieselbe Scheibe wie das
   Schliesszeichen daneben - im Kopf, der die Farbe der Art trägt, sind
   die zwei Geschwister und keine zwei Sorten Knopf.

   `.geteilt` färbt sie **nicht** in `--bar-color`: der Kopf ist schon
   farbig, und ein Grün auf einem grünen Hochsitzkopf wäre unsichtbar.
   Stattdessen steht sie dann voll da, während sie sonst gedämpft ist -
   dieselbe Unterscheidung, die das Fenster ohnehin überall macht. */
.map-inspector-share {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  color: inherit;
  background: transparent;
  border: none;
  cursor: pointer;
  opacity: 0.65;
}

.map-inspector-share:hover { opacity: 1; }

/* Dieselbe gefüllte Scheibe wie in der Liste (09.09.2026, auf Wunsch) -
   und hier ist sie nötiger als dort: der Kopf trägt schon die Farbe der
   Art, und ein blosser Strich darin sagt nichts. Weiss auf Grün steht
   auch auf einem grünen Hochsitzkopf. */
.map-inspector-share.geteilt {
  opacity: 1;
  color: #fff;
  background: var(--bar-color);
  border-radius: 50%;
}

/* `display: flex` oben schlägt das `[hidden]` des Browsers - dieselbe
   Falle wie bei `.list-fab` und `.lightbox-anlegen-upload`, und hier
   wäre sie ein Knopf, der etwas anbietet, was der Server abweist. */
.map-inspector-share[hidden] { display: none; }

/* min-height: 0 or the flex item refuses to shrink and scrolls the card
   off the bottom of the map instead of itself. */
/* **Der Teil, der nicht rollt** (05.09.2026, auf Wunsch): Bilderband und
   Knopfreihe. Zwischen Kopf und Rumpf, und `flex: 0 0 auto`, damit er dem
   rollenden Rumpf daneben nichts von seiner Hoehe wegnimmt und selbst
   nicht schrumpft. */
.map-inspector-fest { flex: 0 0 auto; }

.map-inspector-body {
  /* Als Variable, weil der Bilderstreifen sie wieder herausrechnet, um
     von Kante zu Kante zu gehen (siehe `.map-inspector-bilder`). Eine
     abgeschriebene 12 waere die Zahl an zwei Stellen. */
  --inspector-polster: 12px;
  padding: 10px var(--inspector-polster);
  overflow-y: auto;
  min-height: 0;
}

/* The popup sized itself; in here the card decides the width. */
.map-inspector-body .map-popup { min-width: 0; }

/* **Das Kamerazeichen zwischen dem Platz und seiner Kamera**
   (05.09.2026, auf Wunsch: *„add more space between the kameraplatz log
   and the kamera selection drop down with a Kamera icon centered (big)
   inbetween"*).

   Die Luft ist der eigentliche Auftrag - das Zeichen sagt nur, wofuer
   sie da ist: ab hier redet die Ansicht von einem anderen Ding. Blass,
   weil es eine Ueberschrift ohne Wort ist und nicht lauter sein soll als
   das, was darunter steht. */
.kamera-trenner {
  display: flex;
  justify-content: center;
  color: var(--muted-fg);
  opacity: 0.55;
  margin: 18px 0 4px;
}

/* Im Fenster der Karte ist die Spalte 280px breit und der Platz knapp:
   dieselbe Naht, ein Drittel weniger Luft. Das Zeichen bleibt so gross
   wie es ist - es ist die Ueberschrift, und eine kleinere Ueberschrift
   auf der engeren Seite waere die falsche Ersparnis. */
.map-inspector .kamera-trenner { margin: 12px 0 2px; }

/* Das Auswahlfeld steht seit dem 05.09.2026 auch hier und nicht nur im
   Fenster der Karte; dort ist es 280px breit, hier so breit wie die
   Spalte. */
.einrichtung-detail-body .map-kamera-zeile { display: block; }
.einrichtung-detail-body .map-kamera-wahl { width: 100%; }

/* **Im Fenster nehmen die Klappfelder die Zeile** (18.09.2026). Auf der
   280px-Kachel sass die Wahl rechts neben ihrer Beschriftung und war so
   breit wie ihr laengster Eintrag; im Fenster steht daneben eine
   Handflaeche leerer Platz, und drei Felder verschiedener Breite
   untereinander lesen sich als drei verschiedene Sorten Bedienelement.
   Dieselbe Zeile, die die Kamerawahl drueben schon hat - nur fuer alle
   drei (Art, Ort, Kameraplatz) statt fuer eines. */
.einrichtung-detail-body .kamera-service > select { flex: 1 1 auto; min-width: 0; }

/* Hier stand `.map-inspector-show`, die zwei beschrifteten Pillen am Fuss
   des Fensters. Sie sind am 04.09.2026 der Knopfreihe unter dem Kopf
   gewichen (`.map-inspector-reihe`) und wurden seitdem von niemandem mehr
   gebaut; mit den zwei gruenen Pillen der Karte ist am 05.09.2026 auch
   ihr Rest gegangen. */
/* **Und es rückt für nichts mehr beiseite.** Hier stand die Regel, die es
   um die Breite des Regals nach links schob; seit es links steht, ist das
   Regal auf der anderen Seite des Schirms. Eine Bewegung weniger, die man
   im Kopf haben muss. */

/* While a *new* shape is being drawn the window would be describing an
   element that has nothing to do with the clicking going on, so it stands
   down. It no longer does that for an element being dragged: that element
   is the one the window is open on, its ✓ and its bin are in this
   header, and its measurement is rewritten from the live points on every
   corner moved (refreshInspectorMeasure).

   Specific enough to beat the `.visible` rule above, which is the whole
   job of this one. */
.revier-panel.open.map-drawing .map-inspector.visible { display: none; }

/* ---- The element window, reading ----

   Outside edit mode the card is what the element is: what is open there
   and what has been done. Read-only, and typographically quieter than the
   notebook it is a reading of - there is nothing here to click. */

.map-info-section { margin-top: 10px; }

.map-info-title {
  /* Eine Zeile, damit die Marke daneben steht und nicht darunter
     (04.09.2026): die Überschrift und ihre Zahl sind eine Aussage. */
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: bold;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #888;
  margin-bottom: 4px;
}

/* Die Zahl trägt ihre eigene Schrift: `text-transform` und die Sperrung
   der Überschrift gelten für Wörter, nicht für eine Ziffer in einer
   Marke - gesperrt sähe eine zweistellige Zahl aus wie zwei. */
.map-info-title .task-chip {
  letter-spacing: 0;
  font-size: 10px;
  line-height: 16px;
  min-width: 16px;
}

.map-info-item {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  padding: 2px 5px;
  border-radius: 3px;
  font-size: 13px;
}

/* The same striping as the notebook's own rows, and for the same reason -
   this card is a reading of that notebook. The section's heading is the
   first child here too. */
.map-info-section .map-info-item:nth-child(even) { background: rgba(0, 0, 0, 0.045); }

.map-info-date {
  flex: none;
  font-size: 11px;
  color: #888;
  font-variant-numeric: tabular-nums;
}

.map-info-text { flex: 1; min-width: 0; }

/* A chip naming another element, in that element's own colour, that
   picks it when pressed (elementChip in revier.js). Three places wear it:
   the ground an element stands on, in the row under the card's header;
   the owner of each line in an area's task and log lists - where it sits
   behind the words, because what is to be done is read first and where
   second; and the address in the Einrichtungen page's panel, where
   pressing one opens the Revierplan on it.

   The colour and its ink are set per chip in JS, the way the window's
   header is. */
.map-element-chip {
  flex: none;
  font-size: 11px;
  color: #666;
  background: #f0f0f0;
  border: none;
  border-radius: 8px;
  padding: 1px 7px;
  font-family: inherit;
  cursor: pointer;
}

.map-element-chip:hover { filter: brightness(1.08); }
.map-element-chip:active { filter: brightness(0.94); }

/* **Der Revier-Chip** (09.09.2026), als erster in der Reihe: weiter aussen
   als die Grenze steht, aus welchem Archiv das hier kommt. Er trägt die
   Farbe der Art nicht - er ist keine -, und er ist **kein Griff**: ein
   Chip, der aussieht wie seine Nachbarn und auf nichts führt, ist der
   eine, an dem man vergeblich drückt. Also ohne Zeigerwechsel und ohne
   das Aufhellen beim Darüberfahren, dafür in der Schrift des Kopfes und
   umrandet statt gefüllt. */
.map-revier-chip {
  cursor: default;
  color: inherit;
  background: transparent;
  border: 1px solid currentColor;
  opacity: 0.75;
  font-weight: 600;
}

.map-revier-chip:hover,
.map-revier-chip:active { filter: none; }

/* ---- Das Menü am Bild ---- (19.09.2026)

   Zwei Zeilen, dort wo der lange Druck war. Über dem Leuchtkasten
   (`z-index`), weil es seine Antwort ist, und nicht in der Mitte: es
   gehört an die Stelle, an der gedrückt wurde.

   Die Stellung setzt das Skript (gemessen, damit es nicht über den Rand
   ragt); hier steht nur, wie es aussieht. */
.bild-menue {
  position: fixed;
  /* **Über dem Leuchtkasten**, und der steht auf 9999. Darunter lag das
     Menü hinter der Auflage des Chats, die den halben Bildraum einnimmt -
     sichtbar war es trotzdem, weil der Chat durchsichtig ist, und genau
     das macht solche Fehler teuer: man sieht das Menü und trifft es
     nicht. */
  z-index: 10050;
  min-width: 180px;
  padding: 4px;
  border-radius: 8px;
  background: var(--card-bg);
  border: 1px solid var(--border);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.35);
  /* Gewählt wird mit einem Zug über die Zeilen (`oeffneMenueAn`), und
     ein Zug auf einem Telefon ist sonst ein Rollen: ohne diese Zeile
     nimmt der Browser den Finger an sich, ehe die zweite Zeile leuchtet.
     Und zu markieren gibt es hier nichts. */
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

.bild-menue-zeile {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--fg);
  font-family: inherit;
  font-size: 14px;
  text-align: left;
  cursor: pointer;
}

/* **`.aktiv` ist, was der Finger gerade trifft** (22.09.2026): das Menü
   wird mit einem Zug gewählt, nicht mit einem Treffer (`oeffneMenueAn`),
   und was leuchtet, sagt die Datei statt `:hover`. Der Rest der Regel
   ist derselbe - eine Zeile soll unter der Maus und unter dem Daumen
   gleich aussehen. */
/* Nur `.aktiv`, das `oeffneMenueAn` setzt - ein `:hover` daneben bliebe
   beim Ziehen an der Zeile hängen, über der der Zeiger zuletzt stand. */
.bild-menue-zeile.aktiv { background: var(--control-bg); }
.bild-menue-zeile svg { flex: none; }

/* ---- Die Knöpfe einer Art ---- (`0066`)

   Zwei Felder je Zeile, weil es zwei Dinge sind: was auf dem Knopf steht
   und was er ins Logbuch schreibt. Nebeneinander und gleich breit - sie
   gehören zusammen, und eines von beiden breiter zu machen hiesse, eines
   für wichtiger zu erklären. */
.art-knoepfe {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 6px 0;
}

.art-knopf-zeile {
  display: flex;
  gap: 6px;
}

.art-knopf-zeile input {
  flex: 1 1 0;
  min-width: 0;
}

/* ---- Die zwei Gruppen des Inventars ---- (`0064`)

   „Was im Revier liegt" und „meins, hier nicht hereingegeben". Die
   Überschrift geht über die ganze Breite des Rasters (`1 / -1`), weil
   zwei Raster untereinander zwei verschiedene Spaltenzahlen hätten,
   sobald die eine Gruppe schmaler ist - und dann stünden die Kacheln
   nicht mehr in einer Flucht.

   Die erste bekommt keinen Abstand nach oben: sie ist der Anfang der
   Seite, und eine Lücke über der ersten Zeile liest sich als fehlender
   Inhalt. */
.inventar-gruppe {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
  margin: 14px 0 0;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border);
}

.inventar-gruppe:first-child { margin-top: 0; }

.inventar-gruppe-titel {
  font-size: 13px;
  font-weight: bold;
  color: var(--fg);
}

/* Der Satz daneben und nicht darunter: er erklärt die Gruppe, er ist
   keine zweite Überschrift. Kleiner und blasser, damit die Reihe nicht
   wie zwei Zeilen liest. */
.inventar-gruppe-hinweis {
  font-size: 12px;
  color: var(--muted-fg);
}

/* **Ein Notizbuch, das nur zeigt** (`0064`): das Heft eines Stücks, das
   jemand anderem gehört. Die Eingabefelder und die Haken sind unbenutzbar
   - und zwar sichtbar, nicht heimlich: ein Feld, in das man tippen kann
   und in dem nichts stehen bleibt, ist schlimmer als eines, das zu ist.
   Der zweite Riegel steht in `buildNotesWidget` (`commit` kehrt sofort
   um); dies ist der, den man sieht. */
.notes.lesend input,
.notes.lesend textarea,
.notes.lesend button:not(.notes-mode) {
  pointer-events: none;
  opacity: 0.55;
}

.notes.lesend input[type="checkbox"] { cursor: default; }

/* **Der Chip eines Trägers** (`0063`): das Stück, in dem dieses liegt -
   die Werkzeugkiste vor dem Hochsitz.

   **Keine Farbe**, und das ist dieselbe Aussage, die die Kachel selbst
   macht: die Farben gehören den Arten, die auf dem Revierplan gezeichnet
   werden, und eine Werkzeugkiste steht dort nicht. Ein Chip in einer
   dieser Farben wäre die Behauptung, man könne ihn auf der Karte zeigen.

   **Aber ein Griff**, anders als der Revier-Chip darüber: er führt auf
   die eigene Kachel des Trägers. Also Zeiger und Aufhellen wie bei einem
   Element, nur umrandet statt gefüllt - so liest die Reihe sich als „ein
   Ding von meinen, dann ein Ort auf der Karte" und nicht als zwei Orte. */
.kit-traeger-chip {
  color: var(--fg);
  background: var(--card-bg);
  border: 1px solid var(--border);
  font-weight: 600;
}

.map-info-empty { font-size: 12px; color: #888; }

/* A note that isn't a notebook - a Grund's owner and telephone number. */
.map-info-note {
  font-size: 13px;
  white-space: pre-wrap;
  margin-bottom: 8px;
}

/* A row can be in the selection without being the one under discussion -
   the bar down its left edge is what a selection of several looks like in
   the list, and it is all that is left of one now that the element
   window's checkbox has gone. */
.map-sidebar-row.selected { box-shadow: inset 3px 0 0 var(--bar-color); }

@media (max-width: 700px) {
  /* The nav is a bottom bar here, so the band - ribbon and open alike -
     sits on top of it rather than under it: a full-screen map with no way
     off the page isn't one. */
  .revier-panel { left: 0; bottom: 56px; }
  /* No nav rail to clear here - it's the bar along the bottom. */
  .revier-panel.open .revier-tab { left: 0; }
  /* A length, not `height: auto` with `top: 0` - the panel already sits
     56px up, so this reaches the top of the screen, and unlike `auto` it
     can be animated. `dvh` for the same reason as above, and it matters
     most here: this is the viewport with the URL bar in it. */
  .revier-panel.open {
    height: calc(100vh - 56px);
    height: calc(100dvh - 56px);
  }
  .map-shape-picker.visible { padding: 16px; }
  /* **The shapes stay centred on the screen, and the drawer covers them.**

     On a desktop the picker steps aside from the open drawer by its 300px
     (the rule above), which keeps both usable side by side. Here the
     drawer is 88% of the phone, so the same shift squeezes the shapes into
     the 12% strip left over - and the question "what are you placing?"
     jumped across the screen the moment the drawer was opened, which is
     the one thing a set of big targets under a thumb must not do. So the
     picker keeps the whole viewport and stays centred in it whether the
     drawer is open or not.

     Behind the drawer, then, which needs no number: the picker is 1200 and
     the drawer 1300, decided once at those rules. Same answer as
     `.map-inspector` two rules down - on a phone, everything behind an
     open drawer is behind it, and the close stripe down the drawer's
     left edge is how you get back to it. */
  .revier-panel.map-sidebar-open .map-shape-picker.visible { right: 0; }
  /* Über der Knopfreihe wie am Schreibtisch, nur über die Breite: auf
     390px ist ein Satz in 320px eine Spalte aus drei Wörtern. Und rechts
     bleibt Platz für die Regler, die dort stehen. */
  /* Der Abstand nach unten kommt aus der Reihe (siehe `.map-hint`) und
     wird hier nicht wiederholt; nur die Breite ist eine andere. */
  .map-hint { left: 12px; right: 76px; max-width: none; }
  .map-kinds button { min-width: 130px; }
  /* Nearly the whole map: half-visible controls poking through the drawer
     read as broken. The stacking is handled once above. */
  .map-sidebar { width: 88%; max-width: 320px; box-shadow: -4px 0 12px rgba(0,0,0,0.3); }
  .revier-panel.map-sidebar-open .leaflet-bottom.leaflet-right { margin-right: 0; }
  /* Auf dem Telefon ist das Regal 88% breit; daneben ist kein Platz, und
     die Zeile bleibt, wo sie ist - verdeckt vom Regal wie alles andere. */
  .revier-panel.map-sidebar-open .map-herkunft { right: 0; }

  /* **Hier stand eine zweite Zahl für dieselbe Spalte** (212, dann 160) -
     weil der Regalknopf auf dem Telefon höher hing als am Schreibtisch.
     Seit er in beiden Breiten auf 92 steht (22.08.2026), gilt auch für
     die Regler in beiden Breiten dieselbe: die Regel ist ganz weg statt
     auf denselben Wert gesetzt. Zwei Stellen, die dasselbe sagen, sagen
     es beim nächsten Umzug nicht mehr. */

  /* The drawer is 88% wide here, so shifting Zeigen clear of it would put
     it on top of the cancel button instead. It stays put and the drawer
     covers it, like everything else behind an open drawer on a phone.
     The element window and the tab bar's right end go the same way: what
     is left of a bar squeezed into 12% of a phone would be worse than a
     bar you reach by shutting the drawer first - and the drawer's close
     stripe is right there on its left edge. */
  .revier-panel.open.map-sidebar-open .revier-tab { right: 0; }
  /* The panel already stops above the nav bar, so nothing inside it needs
     to allow for the nav a second time - only for the tab bar, which is
     the same 34px it is on a desktop. */
  .map-actions { left: 16px; }

  /* The gallery's filter buttons on the open map, in the corner the map's
     own tools use here. The drawer covers them, as it does everywhere -
     by `body.map-list-open` above, which drops the row and its sheet
     under the band while the list is open; the drawer's own 1300 is
     inside the band and never reaches them.

     **Above the nav bar**, which is a bottom bar on a phone and which the
     band itself already stops short of (`.revier-panel { bottom: 56px }`
     above). Sitting them 24px off the *viewport* put them across it - the
     one row of controls a full-screen map must not cover, being the way
     off the page. The drawer's button rides on top of them. */
  body.revier-open .gallery-fabs { right: 16px; bottom: 80px; }

  /* **With the map open, the sheet runs down to the nav bar** - past the
     filter row and past the map's `+`, which lie on top of it.

     It used to stop at 152px, clear of its own buttons, which is the
     right answer on a *grid*: the panel is a sheet over the thing it
     narrows and standing on its own controls. Over the open band it was
     the wrong one - the band is the whole phone here, so a panel that
     also clears the button row is a calendar or a clock squeezed into
     what is left, and the tag tree scrolled inside four visible rows.
     There is nothing underneath worth protecting: the band answers the
     *where* question and the sheet is asking one of the other three, so
     covering the map while it is open costs nothing and the ribbon along
     the bottom still names what is picked.

     56px, which is the nav: that is the one row a full-screen map must
     never cover, and the same line the band itself stops at. The FABs
     keep their z-index over it (950 to the sheet's 840), so the four
     questions and the `+` stay reachable on top of the answer - which is
     what the padding below is for. */
  body.revier-open .gallery-filter {
    bottom: 56px;
    max-height: calc(100dvh - 112px);
    padding-bottom: calc(var(--gallery-fab-clearance, 72px) + 8px);
  }

  /* **The map's own `+` is covered by the sheet, and cannot not be.** It
     lives inside `.revier-panel`, which is positioned with a z-index of
     its own - so it is stacked *within* the band, and no number on the
     button can lift it past a sheet that is the band's sibling. Raising
     the band instead would put the whole map over the sheet.

     Left covered deliberately, then, which is what everything behind an
     open drawer does on a phone (`.map-inspector`): with the
     filter open you are asking what-was-seen, and drawing a new element
     is a different job one press away. The four filter buttons *are*
     outside the band and do stay on top, which is the pair that matters -
     the questions and the way to unask them. */
  /* Nur der rechte Rand ist hier eng: 16 statt 24. Die Höhe steht weiter
     oben und gilt in beiden Breiten - siehe dort. */
  .revier-panel.open .map-sidebar-fab { right: 16px; }
}

.folder-header { display: flex; align-items: center; gap: 10px; }

.event-flag {
  cursor: pointer;
  font-size: 20px;
  color: var(--muted-fg);
  user-select: none;
  -webkit-user-select: none;
  line-height: 1;
}

.event-flag.flagged { color: #e0a800; }

/* ---- The Reviereinrichtung notebook (notes.js) ----

   One Markdown document, shown either as a worklist or as its own source.
   The list view is the default because ticking a task is the thing done
   most often; the mode is remembered across every notebook on the page,
   since which view you work in is a habit rather than a property of a
   particular Hochsitz. */

/* **Ohne Rahmen** (04.09.2026, auf Wunsch: *„anywhere where there are
   aufgaben/log the border of the aufgaben/log shall not be shown"*).

   Der Kasten stand in einem Kasten: das Notizbuch sitzt ueberall schon in
   etwas Umrandetem - einer Einrichtungskarte, dem Elementfenster, einer
   Ausruestungskachel -, und ein zweiter Strich darin trennt nichts, was
   nicht schon getrennt waere. Der eigene Grund bleibt: er sagt weiter,
   wo das Notizbuch anfaengt, ohne eine Linie dafuer zu brauchen. */
.notes {
  border: none;
  border-radius: 6px;
  margin-top: 10px;
  background: var(--control-bg);
}

.notes-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
}

.notes-count { font-size: 12px; color: var(--muted-fg); }

/* Open tasks are the one thing here worth a colour: the same claim the
   map badge makes, in the same amber. */
.notes-count.has-open { color: #d98324; font-weight: bold; }

.notes-mode {
  font-size: 12px;
  padding: 2px 10px;
  border-radius: 12px;
}

.notes-body { padding: 8px 10px; }

.notes-section + .notes-section { margin-top: 12px; }

/* **Und ohne Ueberschriften** (04.09.2026, auf Wunsch: *„the »Aufgabe«
   and »Logbuch« headlines shall not be shown as it is recognized by the
   textfield"*). Unter jedem der beiden Abschnitte steht sein eigenes
   Feld - *Aufgabe hinzufuegen*, *Eintrag hinzufuegen* -, und das sagt
   dasselbe wie die Ueberschrift darueber, nur an der Stelle, an der man
   es braucht.

   **`display: none` und nicht aus dem DOM genommen**: die Streifung der
   Zeilen zaehlt die Ueberschrift als erstes Kind mit
   (`.notes-section .notes-item:nth-child(even)` weiter unten). Ein
   entferntes Element haette sie umgedreht - die erste Zeile waere
   ungestreift und die zweite gestreift. Versteckt zaehlt sie weiter. */
.notes-section-title { display: none; }

.notes-empty { font-size: 12px; color: var(--muted-fg); padding: 2px 0; }

/* **Der Bilderstreifen im Elementfenster** (04.09.2026, auf Wunsch).
   Zwischen Kopf und Knopfreihe.

   **Ein Band und keine Kaechelchen** (04.09.2026, gemeldet: *„the tiles
   … look messed up. they shall have no margin except for a thin white
   separation between if there are multiple. they shall fit the current
   space height cropped"*).

   Es stand als Reihe fester 64px-Kaestchen mit Polster ringsum und
   `overflow-x` - vier davon liefen aus dem 280px breiten Fenster heraus
   und das vierte hing halb ueber der Kante. Jetzt teilen sie sich die
   Breite, die da ist: `flex: 1 1 0` gibt jedem denselben Anteil, wie
   viele es auch sind, und `min-width: 0` erlaubt ihnen zu schrumpfen -
   ohne das setzt Flexbox die Eigengroesse des Bildes als Untergrenze und
   es laeuft wieder ueber.

   Kein Polster, kein Radius, keine Farbe darunter: das Band geht von
   Kante zu Kante wie ein Bild, und das Einzige dazwischen ist die duenne
   weisse Fuge. Sie ist der `gap` und kein Rand - so gibt es sie nur
   *zwischen* zweien und nicht auch aussen. */
.map-inspector-bilder {
  display: flex;
  gap: 2px;
  background: #fff;   /* was durch die Fuge zu sehen ist */
  /* **Von Kante zu Kante** - und seit dem 05.09.2026 ohne Zutun: das Band
     steht ausserhalb des Rumpfes, und der feste Kasten polstert nicht.
     Hier standen negative Raender, die das Polster des Rumpfes wieder
     herausrechneten. */
}

/* **Die Knopfreihe zieht sich nicht mehr hoch, wenn ein Band davor
   steht.** Ihr `margin-top: -10px` gibt das obere Polster des Rumpfes
   zurueck, damit sie am Kopf klebt - solange sie das Erste im Rumpf ist.
   Mit dem Bilderband davor zog sie sich ueber dessen untere zehn Pixel,
   und die Kacheln standen abgeschnitten da. Der Nachbarschaftswaehler
   sagt genau den Fall: *wenn ein Band davorsteht*. */
/* (Hier stand bis zum 05.09.2026 eine Regel, die der Knopfreihe ihren
   negativen oberen Rand nahm, wenn ein Band davorstand. Beide Raender
   sind fort, seit die zwei ausserhalb des Rumpfes stehen.) */

/* **Das Bilderband steht in beiden Zustaenden** (04.09.2026, auf Wunsch:
   *„the pictures of the element shall still show between the buttons and
   the heading, even when the todo window is open"*).

   Hier stand einen Nachmittag lang das Gegenteil - aufgeklappt weg, um
   Hoehe zu sparen -, und das war die falsche Haelfte des Berichts: weg
   sollte das *Titelbild* in der Auskunft, das dieselbe Aufnahme ein
   zweites Mal zeigte (siehe `buildInspectorInfo`). Das Band gehoert zum
   Kopf, und ein Kopf, der sich beim Aufklappen aendert, ist einer, den
   man zweimal liest. */

.map-inspector-bild {
  /* Gleicher Anteil an der Breite, und schrumpfen duerfen sie. */
  flex: 1 1 0;
  min-width: 0;
  height: 72px;
  padding: 0;
  border: none;
  cursor: pointer;
  background: none;
  overflow: hidden;
}

/* **Die erste Kachel im Stift: ein Bild dazulegen** (06.09.2026). Sie
   steht in der Reihe der Bilder, in ihrer Groesse und an ihrem Platz -
   was sie erzeugt, erscheint gleich daneben. Gestrichelt, weil sie das
   einzige Feld der Reihe ist, das noch kein Bild *ist*: dasselbe Zeichen,
   mit dem die Anwendung sonst „hier koennte etwas stehen" sagt. */
.map-inspector-bild.neu {
  flex: 0 0 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed rgba(0, 0, 0, 0.28);
  border-radius: 4px;
  color: var(--muted-fg);
  background: rgba(0, 0, 0, 0.03);
}

.map-inspector-bild.neu:hover {
  border-color: var(--bar-color);
  color: var(--bar-color);
}

/* Solange die Datei laeuft: das Zeichen blass, und kein zweiter Druck. */
.map-inspector-bild.neu.laeuft { opacity: 0.5; pointer-events: none; }

/* **In der Hoehe beschnitten**, nicht gequetscht: `cover` fuellt die
   Kachel und schneidet weg, was nicht hineinpasst - bei einem Bild im
   Querformat in einer schmalen Kachel ist das oben und unten. */
.map-inspector-bild img { width: 100%; height: 100%; object-fit: cover; display: block; }

.notes-item {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 3px 6px;
  border-radius: 3px;
}

/* Alternating rows. A task and a log entry are each one line of text with
   no other structure to them, and a dozen in a column run together -
   which matters most exactly where these are read, on a phone at the
   stand. Striped from the section's own children, so the heading counts
   as the first: `nth-child(even)` therefore lands on the first row.

   Deliberately faint, and a tint of the card rather than a grey: it is a
   reading aid, not a table. */
.notes-section .notes-item:nth-child(even) { background: rgba(127, 127, 127, 0.07); }

/* Done, not gone: the list is a record as much as a worklist. */
.notes-item.done .notes-text { text-decoration: line-through; opacity: 0.6; }

/* A task that has been ticked off, standing in the log under the day it
   was done. Deliberately NOT struck through like a done task in a list:
   here it is not a cancelled line, it is something that happened. The
   box is only how it says it was a task - and the way back to being one. */
.notes-item.ticked input[type="checkbox"] { flex: none; opacity: 0.55; }
.notes-item.ticked:hover input[type="checkbox"] { opacity: 1; }

.notes-date { font-size: 11px; color: var(--muted-fg); flex: none; font-variant-numeric: tabular-nums; }

/* The words are the edit control - see notes.js on why there's no pencil. */
.notes-text { flex: 1; cursor: text; font-size: 13px; }
.notes-text:hover { text-decoration: underline dotted var(--border); }

.notes-edit { flex: 1; font-size: 13px; }

.notes-remove {
  flex: none;
  padding: 0 6px;
  font-size: 12px;
  color: var(--muted-fg);
  background: transparent;
  border: none;
  cursor: pointer;
  opacity: 0;
}

/* Out of the way until you mean it. Always there on touch, where there is
   no hover to reveal it with. */
.notes-item:hover .notes-remove { opacity: 1; }
.notes-remove:hover { color: #d33; }
@media (hover: none) { .notes-remove { opacity: 0.6; } }

.notes-add { display: flex; gap: 6px; margin-top: 6px; }
.notes-add input { flex: 1; font-size: 13px; }
.notes-add button { flex: none; padding: 0 12px; font-weight: bold; }

/* Everything the list has no buttons for. Shown so the list can't hide
   half the note; edited in the other view. */
.notes-prose {
  margin-top: 12px;
  padding-top: 8px;
  border-top: 1px dashed var(--border);
  font-size: 12px;
  color: var(--muted-fg);
  white-space: pre-wrap;
}

.notes-source {
  width: 100%;
  box-sizing: border-box;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  line-height: 1.5;
  resize: vertical;
}

/* In the map's element window, where the card is 280px wide and white. */
.notes.compact { background: transparent; }
.notes.compact .notes-body { padding: 8px 0 0; }
.notes.compact .notes-head { padding: 6px 0; }
.notes.compact .notes-source { color: #222; background: #fff; border: 1px solid #bbb; }
.notes.compact .notes-mode { color: #222; background: #eee; border: 1px solid #bbb; }
.notes.compact .notes-add input { color: #222; background: #fff; border: 1px solid #bbb; }
.notes.compact .notes-add button { color: #222; background: #eee; border: 1px solid #bbb; }
.notes.compact .notes-count,
.notes.compact .notes-section-title,
.notes.compact .notes-empty,
.notes.compact .notes-date,
.notes.compact .notes-prose { color: #666; }

/* **Ausser der Zahl der offenen Aufgaben** (05.09.2026, gemeldet: *„count
   offene aufgaben on map selected element window shall be same orange
   color as on einrichtung selected pane"*).

   Die Regel darueber malt alles grau, was im weissen Fenster der Karte
   sonst hell auf dunkel stuende - richtig fuer Datumsangaben,
   Ueberschriften und Fliesstext, falsch fuer diese eine Zeile. Sie ist
   kein Beiwerk, sondern das Signal des Notizbuchs: *hier steht noch
   etwas offen*. Auf der Einrichtungsseite ist sie orange, und dieselbe
   Auskunft in zwei Farben liest sich als zwei verschiedene Auskuenfte.

   `.has-open` steht in beiden Selektoren, also entscheidet die
   Spezifitaet - und die ist hier hoeher als bei der grauen Regel. */
.notes.compact .notes-count.has-open { color: #d98324; }
.notes.compact .notes-prose { border-top-color: #ddd; }

/* ---- Pictures of an element ----

   What the Hochsitz looks like: uploaded by hand, shown wherever the
   thing itself is described. A strip that wraps rather than scrolls -
   there are three of these, not three hundred, and a horizontal scroller
   inside a card is a gesture nobody finds. */

.picture-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 8px 0;
}

.picture { position: relative; }

.picture-thumb {
  display: block;
  border-radius: 4px;
  overflow: hidden;
  line-height: 0;   /* no descender gap under the image */
}

.picture-thumb img {
  width: 88px;
  height: 66px;
  object-fit: cover;   /* a Hochsitz is portrait as often as not */
  display: block;
}

/* The cover says which one it is by being marked, not by being first -
   in a strip that wraps, "first" is not a position you can see. */
.picture.cover .picture-thumb { box-shadow: 0 0 0 2px var(--bar-color); }

.picture-remove,
.picture-cover {
  position: absolute;
  border: none;
  border-radius: 3px;
  background: rgba(0, 0, 0, 0.62);
  color: #fff;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease;
}

.picture-remove { top: 2px; right: 2px; width: 20px; height: 20px; font-size: 12px; padding: 0; }
.picture-cover { bottom: 2px; left: 2px; font-size: 10px; padding: 1px 5px; }

.picture:hover .picture-remove,
.picture:hover .picture-cover { opacity: 1; }

/* Touch has no hover to reveal them with, so they simply stay. */
@media (hover: none) {
  .picture-remove, .picture-cover { opacity: 0.85; }
}

.picture-add {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 88px;
  height: 66px;
  border: 1px dashed var(--border);
  border-radius: 4px;
  color: var(--muted-fg);
  font-size: 12px;
  cursor: pointer;
}

.picture-add:hover { border-color: var(--bar-color); color: var(--fg); }

/* ---- The picture lightbox ----

   The small version of the gallery's: full-bleed image, an arrow either
   side, Escape or the backdrop to close. Not the gallery's own, which is
   built around paging a photo stream - see notes.js. */

.picture-lightbox {
  position: fixed;
  inset: 0;
  z-index: 1400;   /* over the band (900) and its drawers */
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 16px;
  background: rgba(0, 0, 0, 0.88);
}

.picture-lightbox-figure {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  max-height: 82vh;
}

.picture-lightbox-figure img {
  max-width: 100%;
  max-height: 82vh;
  object-fit: contain;
  border-radius: 4px;
}

.picture-lightbox-caption {
  flex: 1 0 100%;
  order: 3;
  text-align: center;
  color: #eee;
  font-size: 13px;
}

.picture-lightbox-bar {
  flex: 1 0 100%;
  order: 4;
  display: flex;
  justify-content: center;
  gap: 8px;
}

.picture-lightbox-bar button {
  background: rgba(255,255,255,0.12);
  color: #fff;
  border: 1px solid rgba(255,255,255,0.25);
  font-size: 13px;
}

.picture-lightbox-bar button:hover { background: rgba(255,255,255,0.22); }

/* Big targets: this is used one-handed, in gloves, on a phone. */
.picture-lightbox-arrow {
  flex: none;
  width: 44px;
  height: 64px;
  border: none;
  border-radius: 6px;
  background: rgba(255,255,255,0.12);
  color: #fff;
  font-size: 28px;
  line-height: 1;
  cursor: pointer;
}

.picture-lightbox-arrow:hover { background: rgba(255,255,255,0.22); }

/* In the element window, which is white whatever the page's theme is. */
.map-popup .picture-add { border-color: #bbb; color: #666; }
.map-popup > .picture-thumb img { width: 100%; height: 120px; }

/* `.einrichtung-cover` was here: a 34x26 thumbnail at the head of a
   card. It went with the tiles - the row a line below shows the same
   photograph at three times the size, and 34px is too small to recognise
   a Hochsitz in, which is the only job a picture of one has. */

/* ---- Open tasks, on the map and in the lists ----

   The count of unticked tasks, in one amber everywhere: a badge pinned
   above the element on the map, a chip beside its name in a list. Open
   only - a notebook full of finished work is not one that wants
   attention. */

.map-task-badge {
  display: flex !important;   /* Leaflet's divIcon default is block */
  /* Sie wählt seit dem 04.09.2026 das Element darunter, also sagt sie
     das auch: was man drücken kann, trägt die Hand. */
  cursor: pointer;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 9px;
  background: #d98324;
  color: #fff;
  font-size: 11px;
  font-weight: bold;
  line-height: 1;
  box-shadow: 0 1px 4px rgba(0,0,0,0.45);
  border: 1px solid rgba(255,255,255,0.85);
}

.task-chip {
  flex: none;
  min-width: 18px;
  padding: 0 5px;
  border-radius: 9px;
  background: #d98324;
  color: #fff;
  font-size: 11px;
  font-weight: bold;
  text-align: center;
}

/* ---------------------------------------------------------------
   Tags as buttons (`0026`).

   A tag used to be a word on a grey chip in four places, each with its
   own size and its own rounding. It is one object now - a square button
   in its group's colour - and this is the only place that says what it
   looks like, because a Fuchs that is dark red on a tile and grey in a
   dialog is two things rather than one.

   `--tag-color` and `--tag-size` are written by buildTagFab in nav.js.
   Everything else is here.
   --------------------------------------------------------------- */
.tag-fab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: var(--tag-size, 34px);
  height: var(--tag-size, 34px);
  padding: 0;
  border: none;
  /* Square with the corners taken off, not a circle: a circle is what
     every *action* in this app is (the FABs in the corners), and an
     answer about an animal is not an action. Same family, different
     shape, so the two are never confused at a glance. */
  border-radius: 6px;
  background: var(--tag-color, #444);
  color: #fff;
  font-size: calc(var(--tag-size, 34px) * 0.4);
  font-weight: bold;
  line-height: 1;
  overflow: hidden;
  box-shadow: 0 1px 4px rgba(0,0,0,0.45);
}

button.tag-fab { cursor: pointer; }
button.tag-fab:hover { filter: brightness(1.18); }

.tag-fab svg {
  width: 78%;
  height: 78%;
  display: block;
}

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

/* Two letters, when there is neither a picture nor a silhouette. Not a
   failure state - on a 26px square over a dark photograph, `Fu` on dark
   red says as much as a 16px animal would. */
.tag-fab.as-initials { font-size: calc(var(--tag-size, 34px) * 0.42); }

/* The other style, and the one this app had for its whole life so far:
   the word, in the group's colour. Wider than it is tall, so it stops
   being square - which is the honest shape for a label. */
.tag-fab.as-text {
  width: auto;
  min-width: 0;
  max-width: 100%;
  height: auto;
  padding: 2px 7px;
  border-radius: 9px;
  font-size: 11px;
  line-height: 16px;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* ---- The × ----

   Only built where something can be removed, which is the lightbox with
   the pencil on. Sitting on the button's shoulder rather than inside it:
   at 34px the button is a fingertip, and a × sharing that target is a
   coin toss between "filter by this" and "throw this away". */
.tag-fab-holder {
  position: relative;
  display: inline-flex;
  flex: 0 0 auto;
}

.tag-fab-remove {
  position: absolute;
  top: -6px;
  right: -6px;
  width: 18px;
  height: 18px;
  padding: 0;
  border: 1px solid rgba(255,255,255,0.55);
  border-radius: 50%;
  background: #7a2b2b;
  color: #fff;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  z-index: 2;
}

.tag-fab-remove:hover { background: #a33; }

/* ---- The row ----

   Several answers in one place - a tile's corner, a map marker - shingle
   into a stack, and one press fans them out. Closed, it costs one
   button's width and says "there are more" by the overlap; open, it is
   an ordinary row. The whole thing is CSS, so nothing has to be measured
   and a row can be built before it is in the document. */
.tag-fab-row {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  max-width: 100%;
}

.tag-fab-row.stacked > * { margin-left: -14px; }
.tag-fab-row.stacked > :first-child { margin-left: 0; }

/* Stacked, the ones behind are dimmed and outlined, so the shingle reads
   as depth rather than as a smear of colours. */
.tag-fab-row.stacked:not(.open) > * {
  box-shadow: 0 0 0 1.5px rgba(0,0,0,0.5);
}

.tag-fab-row.open > * {
  margin-left: 0;
  box-shadow: 0 1px 4px rgba(0,0,0,0.45);
}

.tag-fab-row.stacked:not(.open) { cursor: pointer; }

/* ---- On a tile ----

   The strip is still inert - the tile takes the click - *except* for the
   stack, which has to take the one press that opens it. Everything else
   about the strip is unchanged. */
.tile-tags .tag-fab-row { pointer-events: auto; }
.tile-tags .tag-fab-row.open { flex-wrap: wrap; }

/* ---- In the lightbox ----

   Spread out with their names beside them: this is the reading column and
   the answers are what it is for. */
.lightbox-tag-holder {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 8px 3px 3px;
  border-radius: 22px;
  background: rgba(20,20,20,0.75);
}

.lightbox-tag-name {
  color: #fff;
  font-size: 13px;
  font-weight: bold;
  white-space: nowrap;
}

/* ---- On the Revierplan ----

   Leaflet puts its own box around a divIcon; this takes it off and lets
   the button be the marker. */
.map-anblick-icon {
  background: none;
  border: none;
}

/* ---- Der Haufen: von weitem ein Kreis statt vieler Faehrten ----
   (18.09.2026, auf Wunsch: *„on higher zoom levels … they shall show as a
   single circle filled with a circular multicolor gradient of the
   ansprech colors"*).

   Die Fuellung schreibt `haufenMarker` als `conic-gradient` hinein - die
   Anteile der Ansprechfarben an dieser Stelle, von Mitte zu Mitte
   ineinander uebergehend (die Herleitung steht an `ansprechVerlauf`).
   Hier steht nur, was jeder Kreis mitbringt: dieselbe weisse Kante wie
   der Punkt eines Anblicks ohne Ansprache, damit beide als dasselbe
   Geschlecht von Zeichen lesbar bleiben. */
.map-anblick-haufen-icon {
  background: none;
  border: none;
}

.map-anblick-haufen {
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  /* **Ein gerundetes Quadrat und kein Kreis** (18.09.2026, auf Wunsch).
     In Prozent und nicht in Pixeln: die Kante waechst mit der Zahl der
     Anblicke darin (32-52px seit dem 19.09.2026), und eine feste Rundung waere am kleinen
     Quadrat fast ein Kreis und am grossen fast eine Ecke. 28% halten die
     Form ueber die ganze Spanne dieselbe.

     Das Zeichen unterscheidet sich damit auch von allem anderen auf
     dieser Karte: die Elemente des Revierplans sind Kreise und Linien,
     der einzelne Anblick ohne Ansprache ein Punkt. Ein Quadrat ist keins
     von beidem - es ist die Zusammenfassung, und die ist kein Ding im
     Revier. */
  border-radius: 28%;
  /* **Randlos, und nach aussen hin durchsichtig** (18.09.2026, auf
     Wunsch: *„make the square borderless and translucent towards the edge
     instead"*).

     Vorher: eine weisse Kante, ein Schatten darunter und das ganze
     Zeichen auf 75% - ueberall gleich blass, mit einer harten Kante
     drumherum. Das ist die Zeichnung eines *Dings*, und ein Haufen ist
     keines: er ist eine Aussage darueber, wie dicht es an einer Stelle
     zugeht, und die hat keinen Rand, an dem sie aufhoert.

     Also die Deckung dorthin, wo die Aussage sitzt: in der Mitte voll -
     dort sind die Ansprechfarben zu lesen -, nach aussen auslaufend, so
     dass der Plan darunter (Wechsel, Grenze, Weg) durch die Raender
     kommt. Die Maske ist ein Verlauf auf die *Ecken* zu
     (`farthest-corner`), nicht auf die Seiten: sonst waere von dem
     Quadrat ein Kreis uebrig, denn die Ecken lagen dann laengst
     jenseits des letzten Haltepunkts. So bleibt die Form, und weich wird
     der Rand.

     **Und es laeuft von der Mitte an aus** (18.09.2026, nachgeschaerft auf
     Wunsch: *„the border cuts of in an ugly way. it should fade out more
     and starting from the center"*). Der erste Wurf hielt die Deckung bis
     45% voll und liess draussen einen Rest von 0.12 stehen - beides
     zusammen ergab genau das, was weg sollte: eine Flaeche, die bis kurz
     vor den Rand satt ist und dort abgeschnitten wirkt. Jetzt faellt die
     Deckung vom ersten Pixel an, und aussen ist sie null: es gibt keine
     Kante mehr, an der etwas abreissen koennte.

     `border-radius` bleibt trotzdem stehen - wo eine Maske nicht
     unterstuetzt wird, ist es das, was die Form haelt.

     **Und schon die Mitte ist nicht voll deckend** (18.09.2026, auf
     Wunsch: *„start with less opacity in the center of the pi"*): 0.75,
     dieselbe Zahl, die vorher fuer das ganze Zeichen galt. Der Plan
     laeuft damit auch unter den Tortenstuecken durch und nicht erst an
     ihrem Rand - was zu einem Zeichen passt, das eine Auskunft ueber eine
     Gegend ist und keine Kachel, die sie zudeckt. Tiefer als 0.75 wird
     das kleinste Stueck unlesbar: bei 0.65 war der gruene Keil neben dem
     roten nicht mehr sicher zu unterscheiden (nebeneinander gerendert und
     angesehen). */
  -webkit-mask-image: radial-gradient(circle farthest-corner at 50% 50%,
                                      rgba(0, 0, 0, 0.75) 0%,
                                      rgba(0, 0, 0, 0.68) 30%,
                                      rgba(0, 0, 0, 0.42) 70%,
                                      rgba(0, 0, 0, 0) 100%);
  mask-image: radial-gradient(circle farthest-corner at 50% 50%,
                              rgba(0, 0, 0, 0.75) 0%,
                              rgba(0, 0, 0, 0.68) 30%,
                              rgba(0, 0, 0, 0.42) 70%,
                              rgba(0, 0, 0, 0) 100%);
  cursor: pointer;
  transition: transform 120ms ease;
}

/* **Beim Schweben: ein dünner weisser Rand, und der Verlauf ist fort**
   (19.09.2026, auf Wunsch: *„when it is hovered it shall show with a thin
   white border and without the fade toward the edge"*).

   Der ausgelaufene Rand ist richtig, solange das Zeichen eine Auskunft
   über eine Gegend ist - er hat keine Kante, weil die Aussage keine hat.
   Sobald der Zeiger darauf steht, ist es aber ein *Ziel*: man will
   wissen, was man trifft, und wie gross es ist. Dafür braucht es genau
   das Gegenteil - eine Kante.

   Die Maske wird deshalb voll deckend (`none` wäre falsch: dann trüge
   `border-radius` allein die Form, und der Verlauf spränge sichtbar auf
   eine andere Rundung). Ein Verlauf aus lauter gleichen Haltepunkten ist
   eine Fläche - dieselbe Rechnung, dieselbe Form, nur ohne das Auslaufen.

   `1px` und weiss: dünn genug, um die Tortenstücke nicht zu verdecken,
   und die eine Farbe, die auf jedem der acht Ansprechtöne steht. */
.map-anblick-haufen:hover {
  transform: scale(1.08);
  border: 1px solid #fff;
  -webkit-mask-image: radial-gradient(circle farthest-corner at 50% 50%,
                                      rgba(0, 0, 0, 1) 0%,
                                      rgba(0, 0, 0, 1) 100%);
  mask-image: radial-gradient(circle farthest-corner at 50% 50%,
                              rgba(0, 0, 0, 1) 0%,
                              rgba(0, 0, 0, 1) 100%);
}

/* **Die Zahl darin ist am 18.09.2026 wieder gegangen** (auf Wunsch: *„do
   not show counts in map anblick consolidation squares"*). Sie stand
   einen Tag lang als weisse Ziffern mit Schatten in der Mitte; seit die
   Kantenlaenge das Verhaeltnis traegt, sagt sie dasselbe noch einmal. Was
   von ihr bleibt, ist der `title` am Zeichen. */

/* ---- The picker ----

   One row per open level, top down, each button with its name under it.
   The open one stays lit while its children are shown, which is what
   makes "press it again to take it" a thing you can see rather than a
   thing you have to be told. */
.ansprechen-levels {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 6px 0 4px;
}

.ansprechen-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.ansprechen-option {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 64px;
}

.ansprechen-option-label {
  font-size: 11px;
  line-height: 1.2;
  text-align: center;
  color: var(--muted-fg);
  overflow-wrap: anywhere;
}

/* Lit, and ringed in the page's own foreground colour so it reads on
   both themes. */
.tag-fab.open {
  box-shadow: 0 0 0 3px var(--fg), 0 1px 6px rgba(0,0,0,0.5);
}

/* A branch says so: pressing it will show more rather than finish. */
.tag-fab.has-children::after {
  content: "";
  position: absolute;
  right: 3px;
  bottom: 3px;
  width: 5px;
  height: 5px;
  border-right: 2px solid rgba(255,255,255,0.85);
  border-bottom: 2px solid rgba(255,255,255,0.85);
  transform: rotate(45deg);
}

.tag-fab.has-children { position: relative; }

/* Neue Option: the same shape, hollow, because it is not an answer. */
.tag-fab-new {
  background: transparent;
  border: 2px dashed var(--border);
  color: var(--fg);
  box-shadow: none;
  font-size: 22px;
}

.ansprechen-option.typing { width: auto; flex-direction: row; }

.ansprechen-new-input {
  width: 190px;
  padding: 8px 10px;
  font-size: 14px;
}

/* ---- Der Wähler im Leuchtkasten: die Reihe am Fuss ----

   Der Knopf hart an der unteren linken Ecke, die schon gegebenen
   Antworten rechts daneben. `left`, `bottom` und `width` schreibt
   `placeAnsprechBar`, hier steht nur, was die Reihe *ist*.

   Die Optionen standen bis zum 18.09.2026 in Spalten über ihr (eine
   `.ansprech-stage` mit von Hand gesetzten Knöpfen). Sie sind jetzt das
   Rad in der Mitte des Betrachters, und das hängt nicht mehr an dieser
   Reihe - siehe `.ansprech-rad` weiter unten. */
.ansprech-bar {
  position: absolute;
  z-index: 6;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  /* Die Reihe geht über die ganze Breite und ist zum grössten Teil Luft.
     Luft darf keine Griffe abfangen, die der Fotografie darüber gelten. */
  pointer-events: none;
}

.ansprech-bar[hidden] { display: none; }

.ansprech-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 10px 12px;
  max-width: 100%;
}

/* Ohne Antworten ist die Reihe leer (der Knopf steht seit dem 23.09.2026
   oben rechts) - und eine leere Reihe hält kein Polster frei. */
.ansprech-row:has(> .lightbox-tags:empty):not(:has(> .lightbox-place:not([hidden]))) { padding: 0; }

.ansprech-row > * { pointer-events: auto; }
.ansprech-row .lightbox-tags { pointer-events: auto; }

/* ---- Das Rad (18.09.2026) ----

   Über dem ganzen Betrachter: ein Kreis um dessen Mitte, und die Fläche
   darum herum gehört ihm, solange die Frage offen ist - ein Druck neben
   das Rad heisst „keines davon" (siehe `startAnsprechBar`).

   `z-index: 7`, also über der Reihe am Fuss (6), den Werkzeugen und den
   Pfeilen (3) und dem Streifen (2). Was darunter geriete, tritt zwar
   ohnehin beiseite (`.crowded`), aber die Stapelreihenfolge ist das, was
   es *wahr* macht: ein Stück, das man sieht und nicht drücken kann, ist
   schlimmer als eines, das fehlt.

   **Und aus genau demselben Satz folgt `pointer-events: none` hier.** Der
   Rahmen liegt über dem ganzen Betrachter; finge er die Drücke ab, wären
   auf einem breiten Schirm das `×` und die Pfeile zwar sichtbar
   (das Messen lässt sie ja stehen), aber tot — sichtbar und nicht
   drückbar, also genau das, was zwei Zeilen höher als das Schlimmere
   benannt ist. Drücke nehmen darum nur die Stücke selbst und das Feld für
   eine neue Option; was daneben liegt, fällt auf die Fotografie durch,
   und ein Druck auf die heisst „keines davon" (`druckAufsBild`). */
/* ---- Der Raum des Rades ---- (22.09.2026)

   Das Rad lag im Leuchtkasten und konnte deshalb nur dort gefragt
   werden. Jetzt ein eigener, fester Raum über dem ganzen Fenster - über
   dem Leuchtkasten, wenn er offen ist, und über dem Raster, wenn nicht.

   **`pointer-events: none`, und die Stücke holen es sich einzeln
   zurück.** Dieselbe Regel, die `.lightbox-top` und `.gallery-fabs`
   schon haben: der Raum ist ein Fenster und keine Wand, also bleibt
   erreichbar, was darunter liegt - das `×` des Leuchtkastens, sein
   Stift, eine Kachel des Rasters.

   `z-index` über dem Leuchtkasten (9999) und unter den Menüs (10050):
   das Rad liegt über der Fotografie, und das Menü am langen Druck liegt
   über dem Rad. */
.ansprech-raum {
  position: fixed;
  inset: 0;
  z-index: 10010;
  pointer-events: none;
}

/* **Und was darin ein Möbelstück ist, nimmt den Druck wieder an**
   (22.09.2026).

   Der Raum lässt Drücke durch, weil der Platz *neben* dem Rad die
   Fotografie ist und ein Druck darauf „keines davon" heisst
   (`druckAufsBild`). Die Stücke des Rades holen sich den Druck einzeln
   zurück (`.ansprech-stueck`, `.ansprech-nabe`) - **die drei Kästen
   darüber und darunter taten das nicht**, weil sie bis zum 22.09.2026
   im Leuchtkasten hingen und gar nichts dafür brauchten. Seit sie im
   Raum liegen, waren das Suchfeld, die Trefferliste, das Feld für eine
   neue Option und der Abbruch des Zusammenlegens über einer Fotografie
   **nicht anzutippen**: der Druck fiel durch sie hindurch auf das Bild,
   und das machte den Wähler zu. Gefunden vom Browserlauf `zz_rad_nabe`,
   der einen Treffer anklicken wollte und dreissig Sekunden lang den
   Leuchtkasten im Weg hatte.

   Über dem Raster fiel es nicht auf: dort liegt die Rückwand darunter
   und nimmt den Druck ohnehin. */
.ansprech-suche,
.ansprech-neu,
.ansprech-hinweis { pointer-events: auto; }

.ansprech-raum[hidden] { display: none; }

/* **Die Rückwand, nur über dem Raster.** Im Leuchtkasten ist der Grund
   neben dem Rad die Fotografie, und ein Druck darauf heisst schon
   „keines davon" (`druckAufsBild`). Über dem Raster gibt es das nicht -
   dort wäre ein Druck daneben ein Druck auf eine Kachel, also ein
   zweiter Anblick mitten in einer Frage. Also eine eigene Fläche, die
   den Druck nimmt und die Frage beendet.

   Dunkel, aber nicht schwarz: was man gerade angekreuzt hat, soll
   dahinter noch zu sehen sein. */
.ansprech-raum-grund {
  position: absolute;
  inset: 0;
  background: rgba(10, 10, 10, 0.72);
  pointer-events: auto;
}

.ansprech-raum-grund[hidden] { display: none; }

.ansprech-rad {
  position: absolute;
  inset: 0;
  z-index: 7;
  pointer-events: none;
}

.ansprech-rad[hidden] { display: none; }

/* Das SVG trägt die Kuchenstücke; Lage und Grösse rechnet ansprechfab.js.
   `overflow: visible`, weil die Ringe an der Kante des Kastens enden und
   ein Schatten darüber hinausgeht. */
.ansprech-rad-svg {
  position: absolute;
  overflow: visible;
  filter: drop-shadow(0 6px 24px rgba(0, 0, 0, 0.55));
  /* Der Kasten des SVG ist ein Quadrat, das Rad darin ein Kreis: die vier
     Ecken sind leer und dürfen nichts abfangen. Die Stücke melden sich
     einzeln zurück (unten). */
  pointer-events: none;
}

/* Die Marken - Zeichen und Namen - liegen als gewöhnliches HTML über dem
   SVG, damit ein Trittsiegel hier dasselbe Ding ist wie auf einer Kachel
   (`buildTagFab`). Sie fangen **nichts** ab: jeder Druck gehört dem Stück
   darunter, sonst wäre die Mitte eines Stücks tot und sein Rand lebendig. */
.ansprech-rad-marken {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* Ein Stück. `--tag-color` setzt ansprechfab.js aus `tagColour`, also ist
   `Haarwild` hier dieselbe Farbe wie auf einer Kachel und auf der Karte.

   Halbdurchsichtig, weil unter dem Rad die Fotografie liegt, um die es
   geht - und mit einem hellen Riss dazwischen, damit zwei Nachbarn mit
   ähnlicher Farbe trotzdem zwei Stücke sind. */
.ansprech-stueck {
  fill: var(--tag-color, rgba(40, 40, 40, 0.9));
  fill-opacity: 0.82;
  stroke: rgba(255, 255, 255, 0.35);
  stroke-width: 1.5;
  cursor: pointer;
  pointer-events: auto;
  transition: fill-opacity 0.12s ease, filter 0.12s ease;
}

.ansprech-stueck:hover { fill-opacity: 0.95; }

/* **Und ein Stueck, das eine Fotografie ist, wird auch dunkler**
   (19.09.2026, auf Wunsch: *„hovering over an unselected option with a
   background image shall also make that segment darker"*).

   Die Zeile darueber allein konnte das nicht: ein Bildstueck steht
   ohnehin auf `fill-opacity: 1` (siehe gleich darunter), und eine Deckung
   von 0.82 auf 0.95 zu heben ist an etwas, das schon ganz deckt, gar
   keine Aenderung. Die Haelfte des Rades reagierte also auf den Zeiger
   und die andere nicht - und welche Haelfte, entschied sich daran, ob
   zufaellig schon jemand dieses Tier angesprochen hatte.

   **Zwei Fuellungen, zwei Mechanismen, eine Aussage.** Ein einfarbiges
   Stueck wird dichter und damit dunkler ueber der Aufnahme darunter; ein
   Bildstueck hat nichts zu verdichten, also wird das Bild selbst
   heruntergedreht. Beides liest sich als *„hier steht der Finger"*, und
   das ist das, was gleich sein muss - nicht der Weg dorthin.

   `filter` und nicht ein zweiter Pfad darueber: ein Schleierpfad haette
   den Druck abgefangen oder haette `pointer-events` ausschalten muessen,
   und die Mitte eines Stuecks tot zu machen ist genau der Fehler, den die
   Notiz an `.ansprech-rad-marken` schon einmal beschreibt. */
.ansprech-stueck.mit-bild:hover { filter: brightness(0.72); }

/* **Ein Stueck, das eine Fotografie ist** (19.09.2026). Die Deckung von
   0.82 liess ein farbiges Stueck den Revierplan darunter durchscheinen -
   das war der Sinn. Auf einer Fotografie ist sie ein Schleier zu viel:
   das Bild wuerde milchig und der Plan darunter kaeme als Gitter durch.
   Der Schleier steckt jetzt im Muster selbst (`ANSPRECH_SCHLEIER`), also
   deckt der Pfad ganz. */
.ansprech-stueck.mit-bild { fill-opacity: 1; }

/* Ein Ast sagt, dass er mehr zeigt statt fertig zu sein - dieselbe
   Aussage wie `has-children` am Knopf im Dialog, hier als heller Rand. */
.ansprech-stueck.ast { stroke: rgba(255, 255, 255, 0.55); }

/* Das aufgeschlagene Stück: es bleibt stehen, wo es gedrückt wurde, und
   trägt den Ring, aus dem der nächste kommt. Weiss und nicht `--fg` - auf
   einer hellen Aufnahme wäre die Vordergrundfarbe unsichtbar. */
.ansprech-stueck.offen {
  fill-opacity: 1;
  stroke: #fff;
  stroke-width: 3;
}

/* **Das, wohin ein Abkürzer oder ein Treffer aufgeschlagen hat.** Es ist
   eine *Marke* und keine Auswahl: ein Druck darauf ist weiterhin, was es
   nimmt, und alles daneben steht weiter zur Wahl. Darum die Akzentfarbe
   und nicht der weisse Ring des Offenen - zwei verschiedene Aussagen. */
.ansprech-stueck.gesucht {
  stroke: var(--bar-color);
  stroke-width: 3;
}

/* Der Zeiger der Tastatur. Gestrichelt, weil er an *keiner* der beiden
   anderen Aussagen teilhat: er sagt nur, wo der Finger gerade stünde. */
.ansprech-stueck.cursor {
  stroke: #fff;
  stroke-width: 2.5;
  stroke-dasharray: 6 4;
}

/* **Ein Vorschlag** (19.09.2026): ein Wort aus dem Standardwortschatz,
   das dieses Revier noch nicht gesagt hat. Es steht im selben Ring wie
   die eigenen - es beantwortet dieselbe Frage -, sagt aber, dass es noch
   keine Zeile ist: blasser, und gestrichelt umrandet.

   Gestrichelt und nicht bloss blass: auf einer dunklen Aufnahme ist eine
   Deckung von 0.55 gegen eine von 0.82 kein Unterschied, den jemand im
   Dämmerlicht liest. Der Strich ist die Aussage, die Deckung nur ihr
   Echo. Und es ist derselbe Strich, mit dem der Zeiger der Tastatur
   sagt, dass er noch nichts entschieden hat. */
.ansprech-stueck.vorschlag {
  fill-opacity: 0.55;
  stroke: rgba(255, 255, 255, 0.5);
  stroke-dasharray: 5 4;
}

.ansprech-stueck.vorschlag:hover { fill-opacity: 0.78; }

/* Das `+` jedes Rings: kein Tag, also keine Tag-Farbe, sondern der stille
   Grund, den auch die Nabe hat. */
.ansprech-stueck-neu {
  fill: rgba(20, 20, 20, 0.72);
  stroke: rgba(255, 255, 255, 0.45);
}

/* ---- Das offene Stück (24.09.2026, docs/tags.md §20) ----

   Der Bogen des zweiten Rings über dem `+` des ersten. Er trägt einen
   Griff und keine Vokabel: die Lupe (an der Wurzel über einer Fotografie)
   auf dem stillen Grund des `+`, oder das Zurück - in der Farbe dessen,
   wohin es führt, aber so stark abgedunkelt wie einst die untere Hälfte
   der Nabe (`.ansprech-nabe-ab`, 19.09.2026), damit es nicht als ein
   Stück mehr gelesen wird. Ein gestrichelter Rand sagt *Weg* statt
   *Wort*, dieselbe Sprache wie der Zeiger der Tastatur. */
.ansprech-stueck-suche {
  fill: rgba(20, 20, 20, 0.72);
  stroke: rgba(255, 255, 255, 0.45);
}
.ansprech-stueck-suche:hover { fill: var(--bar-color); fill-opacity: 0.9; }

.ansprech-zurueck {
  fill: color-mix(in srgb, var(--tag-color, #444) 38%, #0b0b0b);
  fill-opacity: 0.9;
  stroke: rgba(255, 255, 255, 0.6);
  stroke-dasharray: 5 4;
}
.ansprech-zurueck.zur-wurzel { fill: rgba(28, 28, 28, 0.85); }
.ansprech-zurueck:hover {
  fill: color-mix(in srgb, var(--tag-color, #555) 62%, #0b0b0b);
  fill-opacity: 1;
}

.ansprech-marke-griff { color: #fff; }
.ansprech-griff-kopf {
  display: flex;
  align-items: center;
  gap: 4px;
}
.ansprech-griff-zeichen { display: flex; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.9)); }

/* **Die leere Nabe** (24.09.2026, §20): an der Wurzel über einer
   Fotografie nur ein dünner Rand, damit das Rad als Ring zu lesen bleibt.
   Er fängt nichts ab - Schieben, Kneifen und Mausrad gehen an das Bild,
   und ein blosser Druck darin tut nichts (`ansprechInLeererNabe`). */
.ansprech-nabe-leer {
  fill: none;
  stroke: rgba(255, 255, 255, 0.55);
  stroke-width: 1.5;
  stroke-dasharray: 3 5;
  pointer-events: none;
}

/* Die Nabe. Dunkel und fast dicht, weil sie bis zum 22.09.2026 das eine
   war, was keine Antwort ist - ein Halbkreis Suchen, ein Halbkreis
   Zurück. Jetzt ist sie ein **ganzer** Kreis mit einer Sache darin:
   die Lupe (oder das `+`) an der Wurzel, und darunter die Gruppe, in
   der gerade gewählt wird. */
.ansprech-nabe {
  fill: rgba(16, 16, 16, 0.92);
  stroke: rgba(255, 255, 255, 0.4);
  stroke-width: 1.5;
  cursor: pointer;
  pointer-events: auto;
  /* **Durchsichtig, bis der Zeiger darauf steht** (23.09.2026, auf
     Wunsch: *„the inner circle (without hovering over it) shall be at 20%
     opacity"*). Unter der Nabe liegt jetzt die Stelle des Bildes, auf die
     gedrückt wurde (`ansprechBildFassen` in app.js) - sie ist der Kreis,
     um den es geht, und eine dichte Scheibe darüber deckte genau das zu.
     Wer auf die Nabe zielt, bekommt sie ganz. */
  /* **45% mit der Lupe, 75% sonst** (23.09.2026, zweiter Anlauf: *„let's
     try 45% opacity for the inner circle when it shows the search icon
     and 75% the rest of the time"*). Die 20% vom Vormittag liessen das
     Bild durch, aber auch die Nabe verschwinden - und eine gewählte
     Vokabel in der Mitte ist eine Antwort, die man lesen können muss.
     Die Lupe ist nur ein Weg zur Suche und darf dem Bild mehr lassen.
     Die Lupe trägt ihre eigene Klasse (`.ansprech-nabe-suche`).

     **Und das alles nur über einer Fotografie** (23.09.2026, auf Wunsch:
     *„only fade the inner circle … if there is a lightbox picture shown.
     For other lightbox media or on multi select gallery the ansprech wheel
     inner circle shall be at 100% opacity"*). Durchsicht ist dafür da,
     die Stelle im Bild zu zeigen; über einem Film, einem Anblick aus
     Worten oder dem Raster gibt es keine, und eine halbe Scheibe wäre dort
     nur blass. `.ueber-bild` setzt `renderAnsprechRad` aus
     `ansprechZiel.ueberBild()`. */
  opacity: 1;
  transition: opacity 0.35s ease;
}
.ansprech-rad.ueber-bild .ansprech-nabe { opacity: 0.75; }
.ansprech-rad.ueber-bild .ansprech-nabe-suche { opacity: 0.45; }
.ansprech-nabe-suche:hover { fill: var(--bar-color); }

.ansprech-rad.ueber-bild .ansprech-nabe:hover { opacity: 1; }

/* **Was darauf steht, bleibt ganz** (23.09.2026, auf Wunsch: *„The text /
   trittsiegel / icon in the inner circle shall always be 100%
   opacity"*). Durchsichtig wird nur die Scheibe: die Marken - Lupe, `+`,
   Siegel und Name - liegen als HTML in einer eigenen Schicht über dem SVG
   und erben ihre Deckung nicht. Bis zum Mittag gingen sie eigens mit und
   waren bei 20% auf einem dunklen Bild nicht mehr zu lesen.

   **Während das Bild bewegt oder ein Stück gewählt wird: 20%**
   (`ansprechNabeLeise`). Hinunter sofort (0.08 s), hinauf in einer Blende
   (0.35 s, von der Regel oben) - es gilt der Übergang des Zustands, *in
   den* gewechselt wird. Zuletzt und mit derselben Schärfe wie `:hover`,
   damit sie auch über einem Zeiger auf der Nabe gilt. */
.ansprech-rad.ueber-bild.nabe-leise .ansprech-nabe {
  opacity: 0.2;
  transition: opacity 0.08s ease;
}

/* **Die gewählte Gruppe in der Mitte** (22.09.2026). Sie trägt ihre
   eigene Farbe, und zwar heller als die Ringe um sie herum: sie ist
   nicht ein Stück mehr, sie ist das, worin alle anderen liegen. Ein
   Druck darauf trägt sie ein - deshalb sieht sie aus wie eine Antwort
   und nicht wie ein Knopf. */
.ansprech-nabe-gewaehlt {
  fill: color-mix(in srgb, var(--tag-color, #444) 72%, #000);
  stroke: rgba(255, 255, 255, 0.7);
  stroke-width: 2;
}
.ansprech-nabe-gewaehlt:hover {
  fill: color-mix(in srgb, var(--tag-color, #444) 92%, #000);
}

/* **Die obere Hälfte als `+`** (19.09.2026): sie steht dort, wo das Rad
   nicht an der Wurzel anfängt, und macht das ganze Verzeichnis samt
   Standardwortschatz auf. Dieselbe Akzentfarbe wie die Lupe, die sie
   vertritt — es ist dieselbe Hälfte mit derselben Aufgabe (*„zeig mir
   mehr, als hier steht"*), nur mit dem Werkzeug, das an dieser Stelle
   trägt. */
.ansprech-nabe-alles:hover { fill: var(--bar-color); }

/* `.ansprech-nabe-ab` ist mit dem Zurück gegangen (22.09.2026, auf
   Wunsch: *„zurück shall be removed"*). Sie trug die Farbe dessen,
   wohin sie zurueckfuehrte - stark abgedunkelt, damit sie nicht als
   ein Kuchenstueck mehr gelesen wurde. Genau dieses Argument steht
   jetzt umgekehrt bei `.ansprech-nabe-gewaehlt`: die Nabe ist heute
   eine Antwort, also darf sie leuchten. */

/* Eine Marke sitzt auf ihrem Mittelpunkt - `left`/`top` sind der Punkt,
   die Verschiebung macht daraus eine Mitte. */
.ansprech-marke {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  color: #fff;
}

/* **Der ganze Name oder keiner.** Geschnitten wäre `Schwarzwil…` neben
   `Schwarzwil…`, was die Frage unlesbar macht statt eng - also entscheidet
   ansprechfab.js am Bogen, ob überhaupt ein Name gezeichnet wird
   (`ansprechRadNamen`), und was gezeichnet wird, steht ganz da. Die Breite
   setzt es aus dem Bogen des Stücks; umbrechen darf er. */
.ansprech-marke-name {
  font-size: 11px;
  line-height: 1.15;
  font-weight: bold;
  text-align: center;
  overflow-wrap: anywhere;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.95);
}

.ansprech-marke.offen .ansprech-marke-name,
.ansprech-marke.gesucht .ansprech-marke-name { text-decoration: underline; }

.ansprech-marke .tag-fab { box-shadow: none; background: transparent; }
.ansprech-marke .tag-fab.as-initials { background: rgba(0, 0, 0, 0.35); }

.ansprech-marke-neu { font-size: 26px; line-height: 1; font-weight: bold; }

.ansprech-nabe-marke { gap: 2px; }

/* `.mit-ziel` und `.ansprech-nabe-ziel` sind mit dem Zurück gegangen
   (22.09.2026): sie setzten Pfeil und Name nebeneinander, weil die Nabe
   ein Halbkreis war. Sie ist jetzt ein ganzer Kreis, und was darin
   steht, steht wieder in der Spalte, die `.ansprech-marke` ohnehin
   stellt: Zeichen oben, Wort darunter. */

/* **Der Name der gewählten Gruppe, in der Nabe** (22.09.2026). Anders
   als in einem Kuchenstück wird er hier *nicht* am Bogen gemessen -
   die Nabe ist rund und in der Mitte am breitesten, und die Breite
   setzt ansprechfab.js aus ihrem Radius. Er darf umbrechen; was dann
   noch nicht passt, wird gekuerzt, und der `title` sagt ihn ganz. */
.ansprech-nabe-wort .ansprech-marke-name {
  font-size: 12px;
  max-height: 2.4em;
  overflow: hidden;
}

/* `.ansprech-nabe-wort` hiess bis zum 19.09.2026 schon einmal so und
   trug die Woerter *Suchen* und *Abbrechen*. Die sind damals gefallen
   (*„dont write »Suchen« »Abbrechen« just show symbols"*); was jetzt so
   heisst, ist eine Vokabel und kein Knopfwort - und eine Vokabel ohne
   ihren Namen waere im Rad das einzige Stueck, das nicht sagt, was es
   ist. */

/* `.ansprech-stummel-marke` ist mit dem Stummelring gegangen
   (22.09.2026) - die Regel stand hier noch, nachdem der Ring gefallen
   war, und ein `grep` nach „stummel" fand sie und sah lebendig aus. */

/* Das Feld für eine neue Option, unter dem Rad. Es steht dort und nicht
   in einem Stück: ein getipptes Wort passt in kein Kuchenstück. */
/* `top` schreibt ansprechfab.js: dicht unter den äussersten Ring, und das
   ist keine feste Zahl - das Rad ändert seine Grösse mit dem Fenster und
   mit der Tiefe. */
.ansprech-neu {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px 6px 12px;
  border-radius: 24px;
  background: rgba(20, 20, 20, 0.88);
  border: 1px solid rgba(255, 255, 255, 0.35);
  pointer-events: auto;
}

.ansprech-neu[hidden] { display: none; }

.ansprech-neu-feld {
  width: 200px;
  padding: 6px 0;
  border: none;
  outline: none;
  background: transparent;
  color: #fff;
  font-family: inherit;
  font-size: 14px;
}

.ansprech-neu-nehmen {
  width: 32px;
  height: 32px;
  border: none;
  border-radius: 50%;
  background: var(--bar-color);
  color: #fff;
  font-size: 16px;
  cursor: pointer;
}

/* ---- Die Suche, oben (18.09.2026) ----

   Sie war eine Lupe in der Reihe am Fuss, die zum Feld wuchs. Mit dem Rad
   in der Mitte stünde das Feld in dessen Nabe - und die Nabe ist jetzt
   der Griff, der die Suche aufmacht. Also oben, mittig, über allem: der
   eine Streifen, den weder das Rad noch die Antwort braucht.

   `z-index: 8`, eins über dem Rad: während gesucht wird, steht die Liste
   *statt* des Rades, aber das Feld selbst muss auch darüberstehen, wenn
   noch nichts getippt ist und das Rad noch dasteht. */
.ansprech-suche {
  position: absolute;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 8;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  width: min(420px, calc(100% - 24px));
}

/* Das `display: flex` oben schlägt das `[hidden]` des Browsers - dieselbe
   Falle wie bei `.list-fab`. */
.ansprech-suche[hidden] { display: none; }

/* ---- Das Band beim Zusammenlegen ---- (19.09.2026)

   Dort, wo sonst das Suchfeld steht, und in derselben Form: ein dunkles
   Pillenfeld über dem Rad. Es ist die Überschrift zu einer Frage, die
   das Rad sonst nie stellt, und es steht deshalb auch nur dann da.

   Warme Farbe statt der grauen des Suchfeldes: das hier ist ein
   Zustand, aus dem man wieder heraus muss, und kein Werkzeug, das offen
   stehenbleiben darf. */
.ansprech-hinweis {
  position: absolute;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 9;
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: min(460px, calc(100% - 24px));
  padding: 8px 6px 8px 14px;
  border-radius: 24px;
  background: rgba(122, 70, 20, 0.92);
  color: #fff;
  font-size: 14px;
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.4);
}

.ansprech-hinweis[hidden] { display: none; }

.ansprech-hinweis-ab {
  flex: none;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.25);
  color: #fff;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}

.ansprech-hinweis-ab:hover { background: rgba(0, 0, 0, 0.45); }

/* ---- Der Kopf eines Menüs an einer Stelle ---- (19.09.2026)

   `oeffneMenueAn` (nav.js) zeichnet ihn, wo der Aufrufer einen `kopf`
   mitgibt: über einem Rad mit zwanzig Kuchenstücken muss dabeistehen, an
   welchem das Menü hängt. Am Bild gibt es nur eine Fotografie, und dort
   bleibt er weg. (Hiess bis zum 22.09.2026 `.ansprech-option-menue-kopf`,
   als das Menü noch dem Rad allein gehörte.) */
.menue-an-kopf {
  padding: 6px 10px 8px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted, #999);
}

/* ---- Die Tafeln am Menü des Rades: Farbe und Trittsiegel ----

   (23.09.2026.) Dieselbe Schale wie das Menü (`.bild-menue`), mit einem
   Inhalt, der mehr ist als Zeilen - siehe `oeffneTafelAn` in nav.js. Was
   hier steht, stand bis heute als Farbfeld und Auswahlliste je Zeile auf
   der Einstellungsseite. */
.menue-tafel { max-width: min(320px, calc(100vw - 16px)); }

.ansprech-farbe { padding: 4px 6px 6px; }

.ansprech-farbe-flecken {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(30px, 1fr));
  gap: 6px;
}

.ansprech-farbe-fleck {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.25);
  padding: 0;
  cursor: pointer;
}
.ansprech-farbe-fleck:hover { border-color: rgba(255, 255, 255, 0.7); }
.ansprech-farbe-fleck.gewaehlt { border-color: var(--fg); box-shadow: 0 0 0 2px var(--bar-color); }

.ansprech-farbe-eigen,
.ansprech-farbe-unter {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  font-size: 14px;
  color: var(--fg);
  cursor: pointer;
}
.ansprech-farbe-eigen input[type="color"] {
  width: 34px;
  height: 26px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: transparent;
  cursor: pointer;
}

.ansprech-siegel {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(44px, 1fr));
  gap: 6px;
  padding: 4px 6px 6px;
  max-height: min(360px, 60vh);
  overflow-y: auto;
}

.ansprech-siegel-wahl {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 44px;
  border-radius: 8px;
  border: 2px solid transparent;
  background: var(--tag-color, #444);
  color: #fff;
  cursor: pointer;
  font-weight: 700;
}
.ansprech-siegel-wahl:hover { filter: brightness(1.2); }
.ansprech-siegel-wahl.gewaehlt { border-color: var(--fg); }

.ansprech-suche-zeile {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 6px 0 14px;
  border-radius: 24px;
  background: rgba(20, 20, 20, 0.88);
  border: 1px solid var(--bar-color);
  color: #fff;
}

.ansprech-suche-feld {
  flex: 1 1 auto;
  min-width: 0;
  padding: 10px 0;
  border: none;
  outline: none;
  background: transparent;
  color: #fff;
  font-family: inherit;
  font-size: 15px;
}

.ansprech-suche-feld::placeholder { color: rgba(255, 255, 255, 0.6); }
.ansprech-suche-feld::-webkit-search-cancel-button { display: none; }

.ansprech-suche-zu {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: none;
  color: #fff;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}

.ansprech-suche-zu:hover { background: rgba(255, 255, 255, 0.15); }

/* ---- Die Trefferliste ----

   Eine **Liste**, untereinander, in Leserichtung: der beste Treffer oben,
   direkt unter dem Wort, das ihn gefunden hat. (In der Spalte am Fuss
   stand er unten, am Daumen, und wuchs nach oben - das war die Richtung
   des Wählers von damals, und der wächst jetzt nach aussen.) */
.ansprech-treffer {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: min(60vh, 520px);
  overflow-y: auto;
  /* Der Zug am Bild blättert; hier wird gerollt. */
  touch-action: pan-y;
}

.ansprech-treffer[hidden] { display: none; }

.ansprech-treffer-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 5px 12px 5px 5px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 24px;
  background: rgba(20, 20, 20, 0.88);
  color: #fff;
  font-family: inherit;
  font-size: 14px;
  text-align: left;
  cursor: pointer;
}

.ansprech-treffer-chip:hover { border-color: var(--bar-color); }

/* **Der Zeiger der Tastatur in den Treffern** (24.09.2026): derselbe
   weisse, gestrichelte Strich wie am Stück im Rad (`.ansprech-stueck.cursor`)
   - er sagt nur, was Enter nehmen würde. */
.ansprech-treffer-chip.cursor {
  border: 2px dashed #fff;
  padding: 4px 11px 4px 4px;
}

/* **Ein Treffer, den dieses Revier noch nicht sagt** (22.09.2026):
   dieselbe Sprache wie ein Vorschlag im Rad - blasser und gestrichelt -,
   damit die zwei Sorten am Aussehen zu unterscheiden sind und nicht nur
   an ihrer Stelle in der Liste. */
.ansprech-treffer-chip.vorschlag {
  border-style: dashed;
  opacity: 0.72;
}

.ansprech-treffer-chip.vorschlag:hover { opacity: 1; }

.ansprech-treffer-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: bold;
}

/* **Die Fotografie eines Treffers ist getönt wie ein Stück im Rad**
   (24.09.2026, auf Wunsch: *„if a picture exists for a search result it
   shall be tinted in the options color"*). Ein Treffer ist derselbe
   Wegweiser wie ein Kuchenstück, nur in einer Liste - und im Rad gilt
   seit dem 19.09.2026: die Fotografie trägt die Form, die Tönung die
   Farbe (docs/tags.md §6). Also dieselben zwei Werte wie dort,
   `ANSPRECH_SAETTIGUNG` (0.15) und `ANSPRECH_SCHLEIER` (0.48) in
   ansprechfab.js; wer die ändert, ändert diese mit.
   `hat-bild` setzt buildTagFab nur, solange die Datei auch kommt. */
.ansprech-treffer-chip .tag-fab.hat-bild { position: relative; }

.ansprech-treffer-chip .tag-fab.hat-bild img { filter: saturate(0.15); }

.ansprech-treffer-chip .tag-fab.hat-bild::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--tag-color, #444);
  opacity: 0.48;
  pointer-events: none;
}

.ansprech-treffer-leer {
  padding: 8px 14px;
  border-radius: 20px;
  background: rgba(20, 20, 20, 0.88);
  color: rgba(255, 255, 255, 0.75);
  font-size: 13px;
}


/* **What the options land on steps aside - and only that.** Pressing
   Ansprechen used to clear the viewer outright: the panel, the tools, the
   arrows and the strip all went. That is right on a phone, where two
   columns of options cover the screen, and wrong on a desk, where four
   columns take the left third and the × is 900px from the nearest one.

   So `markAnsprechCrowding` measures the rectangle the options actually
   occupy against each of them and marks the ones in the way. On a large
   screen that is often none of them, and the viewer keeps its furniture
   while you answer.

   `visibility`, not `display`: an element with no box cannot be measured,
   so the next press would find nothing in the way and bring it back - on,
   off, on, off - and the strip is what the band at the foot is measured
   with, so taking its height out would resize that band mid-press. */
.lightbox .crowded {
  visibility: hidden;
}

.ansprechen-hint {
  font-size: 12px;
  color: var(--muted-fg);
}

/* ---- Bringing a card in ----

   An import is the gallery, over this page, holding only what just
   arrived. It is deliberately *not* an overlay in the z-index sense: the
   page's own furniture stays where it is and keeps working - the
   multi-select FAB, the selection FABs, the status line, the lightbox -
   and what changes is that the grid underneath is hidden and this is in
   its place. An overlay would have meant a second set of every one of
   those buttons, floating over the first.

   See import.js, and docs/ereignisse.md for what staging means. */

/* The veil while something is being dragged over the window. The whole
   window is the target: what is being dropped is a card, and a rectangle
   to hit is a game of aim on a trackpad. Under the modals (10000) and the
   lightbox, over everything else - nothing else can be dropped on. */
.import-veil {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(20, 20, 20, 0.62);
  /* The drop lands on the window's own listener; a veil that ate the
     event would be a veil you have to drop *on*, which is the rectangle
     again. */
  pointer-events: none;
}

.import-veil[hidden] { display: none; }

.import-veil-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 28px 34px;
  border: 2px dashed rgba(255, 255, 255, 0.75);
  border-radius: 12px;
  background: rgba(0, 0, 0, 0.35);
  color: #fff;
  text-align: center;
}

.import-veil-text { font-size: 17px; font-weight: bold; }
.import-veil-note { font-size: 13px; opacity: 0.85; max-width: 34ch; }

/* **What the page is while an import is open.** The grid, its timeline
   and its empty panel are about the archive; the filters and the sort
   would ask it questions this view cannot answer; the `+` writes into it.
   All of them go, and the two that are about *tiles* - multi-select and
   the selection FABs - stay, because the tiles are still tiles. */
body.import-open #galleryContainer,
body.import-open #galleryEmpty,
body.import-open #galleryTimeline,
body.import-open #galleryActionFabs,
body.import-open .sort-fab-holder,
body.import-open .gallery-fabs,
body.import-open .gallery-filter { display: none; }

.import-view { margin: 0 -20px; padding-bottom: 180px; }
.import-view[hidden] { display: none; }

/* The bar says what this is, what the rule made of it, and how to end it.
   Sticky rather than fixed: it belongs to the view and scrolls with the
   page until it reaches the top, which is where a summary of what you are
   looking at should stay. */
.import-bar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 10px 20px;
  background: var(--control-bg);
  border-bottom: 1px solid var(--border);
}

.import-heading {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
}

.import-title { font-size: 16px; font-weight: bold; }
.import-summary { font-size: 13px; color: var(--muted-fg); }

/* What is left to do, beside the summary rather than as a warning strip:
   it is a count, not a refusal - an Anblick with no place is a legal row,
   and there are cards whose place nobody knows. */
.import-missing {
  font-size: 12px;
  padding: 1px 8px;
  border-radius: 9px;
  background: #d98324;
  color: #fff;
}

.import-busy { font-size: 12px; color: var(--muted-fg); }

.import-actions { display: flex; gap: 8px; }

/* One folder, which is one camera's card and therefore one Kameraplatz -
   the assumption this whole view is built on. */
.import-group { margin-top: 14px; }

/* **Der Ordnerkopf bleibt oben stehen** (04.09.2026, auf Wunsch: *„when
   scrolling below an open folder header, that header shall stick frozen
   on the top while scrolling through its anblicke"*).

   Bei zweihundert Anblicken aus drei Karten ist die Frage, die man beim
   Rollen dauernd stellt, *„aus welchem Ordner ist das hier"* - und die
   Antwort stand einmal ganz oben und war dann fort. Jetzt bleibt sie
   stehen, solange ihre eigenen Anblicke laufen: `sticky` klebt innerhalb
   des Elternteils, und der Elternteil ist genau diese eine Gruppe. Der
   naechste Ordner schiebt den vorigen von selbst hinaus - ohne eine
   Zeile Javascript, weil das die Sache ist, fuer die es `sticky` gibt.

   **Unter der Leiste, nicht darunter verschwindend**: `--import-bar-hoehe`
   misst `messeImportLeiste` in import.js, weil die Leiste umbricht (auf
   einem Telefon zwei Zeilen, mit einer Stempel-Warnung drei) und eine
   abgeschriebene Zahl dort falsch waere. Der Rueckfallwert ist die
   einzeilige Hoehe am Schreibtisch.

   Ein eigener Grund ist noetig: ohne ihn scheinen die Kacheln durch, an
   denen der Kopf vorbeigeschoben wird. `z-index: 4` liegt unter der
   Leiste (5), damit er dort hineingleitet statt darueber. */
.import-group-head {
  position: sticky;
  top: var(--import-bar-hoehe, 56px);
  z-index: 4;
  background: var(--control-bg);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  padding: 6px 20px;
}

.import-folder { font-weight: bold; }
.import-count { flex: 1; min-width: 0; font-size: 12px; color: var(--muted-fg); }

/* The decision, in the folder's own bar. Orange while it is open, because
   it is the one thing standing between this card and the archive; quiet
   once it has an answer, because then it is a fact with a way to change
   it. */
.import-place {
  flex: none;
  padding: 5px 14px;
  border-radius: 20px;
  border: 1px solid #d98324;
  background: #d98324;
  color: #fff;
  font-family: inherit;
  font-size: 13px;
  cursor: pointer;
}

.import-place:hover { background: #b3651b; border-color: #b3651b; }

.import-place.set {
  background: transparent;
  border-color: var(--border);
  color: var(--fg);
}

.import-place.set:hover { background: var(--card-bg); }

/* The picker: every Kameraplatz there is, and a field for the one there
   isn't yet. A list of buttons rather than a `<select>` - it is a choice
   between places you know by name, and a dropdown hides all of them
   behind a click. */
.platz-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 40vh;
  overflow-y: auto;
  margin: 10px 0;
}

.platz-option {
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--card-bg);
  color: var(--fg);
  font-family: inherit;
  font-size: 14px;
  text-align: left;
  cursor: pointer;
}

.platz-option:hover { border-color: var(--bar-color); }

.platz-new { display: flex; gap: 6px; margin-top: 6px; }
.platz-new input { flex: 1; min-width: 0; }

@media (max-width: 700px) {
  .import-view { margin: 0 -20px; }
  .import-bar { padding: 10px 12px; }
  .import-group-head { padding: 6px 12px; }
  /* The buttons wrap under the summary rather than squeezing it: three
     text buttons and a count do not fit across 360px. */
  .import-actions { width: 100%; }
  .import-actions button { flex: 1; }
}

/* ---- Zeitstempel im Bild (settings.js) ----

   One row per camera, shut. What you come back to check is which methods
   exist and roughly what they look for; the twelve fields behind that are
   set once when a camera is bought. */
.stempel-row {
  border: 1px solid var(--border);
  border-radius: 6px;
  margin-bottom: 8px;
  background: var(--card-bg);
  overflow: hidden;
}

.stempel-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  width: 100%;
  padding: 10px 12px;
  border: none;
  background: transparent;
  color: var(--fg);
  font-family: inherit;
  font-size: 14px;
  text-align: left;
  cursor: pointer;
}

.stempel-name { font-weight: bold; }
.stempel-how { flex: 1; min-width: 0; font-size: 12px; color: var(--muted-fg); }

/* A method that cannot run *here* says so where it is read, rather than
   after an import has quietly used none of it. */
.stempel-missing {
  flex: none;
  padding: 1px 8px;
  border-radius: 9px;
  background: #d98324;
  color: #fff;
  font-size: 11px;
}

.stempel-editor {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  border-top: 1px solid var(--border);
}

.stempel-field {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
}

.stempel-label { flex: 0 0 190px; color: var(--muted-fg); }
.stempel-field input[type="text"],
.stempel-field input[type="number"],
.stempel-field select { flex: 1; min-width: 0; }
.stempel-field input[type="checkbox"] { flex: none; }

/* The four numbers of the crop, side by side, each with its own tiny
   label - "links %" beside a field means something, four bare boxes in a
   row do not. */
.stempel-crop { flex: 1; display: flex; flex-wrap: wrap; gap: 8px; }
.stempel-crop .stempel-field { flex: 1 1 120px; gap: 4px; }
.stempel-crop .stempel-label { flex: none; font-size: 11px; }
.stempel-crop input { width: 100%; }

.stempel-template-fields { display: flex; flex-direction: column; gap: 10px; }
.stempel-template-fields[hidden] { display: none; }

/* **The probe**, which is the point of the whole section: a crop box is
   four numbers and there is no way to guess them. The strip underneath is
   the reader's own input - masked and thresholded exactly as it sees it -
   so a wrong box, a wrong colour and a wrong layout each look different
   rather than all looking like "nothing found". */
.stempel-probe {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px;
  border: 1px dashed var(--border);
  border-radius: 6px;
}

.stempel-probe-result { font-size: 13px; }
.stempel-probe-result.error { color: var(--muted-fg); }

/* On the app's own dark, because the mask is white marks on black and a
   strip of that on a white page reads as a photographic negative of
   itself. */
.stempel-probe-crop {
  max-width: 100%;
  align-self: flex-start;
  background: #000;
  border: 1px solid var(--border);
  border-radius: 4px;
  image-rendering: pixelated;
}

.stempel-actions { display: flex; flex-wrap: wrap; gap: 8px; }

/* ---- What the import made of the times ----

   Beside the folder's own counts: how many times were read off the
   pictures, and how many are still the moment of the upload standing in
   for a time nobody knows. The second is the one that matters, and it is
   the same orange as everything else in this app that means "this is
   still open". */
.import-stempel { font-size: 12px; color: var(--muted-fg); }

.import-stempel.offen {
  padding: 1px 8px;
  border-radius: 9px;
  background: #d98324;
  color: #fff;
}

/* The whole width of the bar, under the summary: it is a sentence, not a
   count, and it is the one thing in this view that says something is
   wrong with the card rather than with what you have not done yet. */
.import-trouble {
  width: 100%;
  padding: 8px 10px;
  border-radius: 6px;
  background: rgba(217, 131, 36, 0.14);
  border: 1px solid #d98324;
  color: var(--fg);
  font-size: 13px;
}

.import-trouble[hidden] { display: none; }

/* ---- Drawing the crop on a sample picture ----

   A crop box is four numbers and there is nothing on a settings page
   that could tell you what they mean. So: a photograph off the card, a
   rectangle dragged round the stamp, and the two buttons under it. The
   rectangle is positioned in per cent of the picture's own box, which
   *is* the fraction the method stores - no scaling arithmetic anywhere,
   at any size. */
.stempel-picture {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  padding: 10px;
  border: 1px dashed var(--border);
  border-radius: 6px;
}

.stempel-stage {
  position: relative;
  max-width: 100%;
  line-height: 0;
  /* The drag is the gesture here, so the browser's own must not be. */
  touch-action: none;
  cursor: crosshair;
}

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

.stempel-sample {
  max-width: 100%;
  max-height: 46vh;
  border-radius: 4px;
  /* Dragging a picture is what a browser does with a picture, and it is
     the one thing that must not happen while dragging *on* it. */
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  /* Ein Bild ist es auch, und ein Daumen, der beim Aufziehen des Rahmens
     liegen bleibt, soll darauf keine Blase bekommen. */
  -webkit-touch-callout: none;
}

.stempel-rect {
  position: absolute;
  border: 2px solid #d98324;
  background: rgba(217, 131, 36, 0.16);
  pointer-events: none;   /* the drag belongs to the stage underneath */
}

.stempel-picture-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.stempel-picture-actions[hidden] { display: none; }

.stempel-set-row { flex: 1; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.stempel-set-row select, .stempel-set-row input { flex: 1; min-width: 0; }

/* ---- Making a digit set out of the camera's pictures ----

   The cells are cut where the reader would cut them, and typing what the
   stamp says labels them left to right. Seeing the punctuation cells
   greyed beside the digits is how you tell that the layout lines up with
   what the camera actually prints. */
.stempel-maker {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--card-bg);
}

.stempel-maker[hidden] { display: none; }
.stempel-maker-rows { display: flex; flex-direction: column; gap: 12px; }

.stempel-sample-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
}

.stempel-cells { display: flex; flex-wrap: wrap; gap: 3px; }

.stempel-cell {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: 3px;
  background: #000;   /* the cells are a white-on-black mask */
}

.stempel-cell img { height: 34px; image-rendering: pixelated; }

/* A cell that is punctuation by the layout: shown, because seeing it is
   how the layout is checked, and dimmed, because it is never read. */
.stempel-cell.static { opacity: 0.45; }
.stempel-cell.taken { border-color: #3f9142; }

.stempel-cell-label {
  position: absolute;
  right: 1px;
  bottom: 0;
  padding: 0 2px;
  background: rgba(0, 0, 0, 0.7);
  color: #fff;
  font-size: 10px;
  line-height: 1.3;
}

/* Ten boxes: which digits the samples have covered, and what each one
   will look like in the set. The set cannot be saved until all ten are
   there - one with a hole in it is a reader that answers some other
   digit for the missing one, confidently, on every picture. */
.stempel-coverage { display: flex; flex-wrap: wrap; gap: 4px; }

.stempel-digit {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-width: 30px;
  padding: 3px;
  border: 1px solid var(--border);
  border-radius: 4px;
  font-size: 12px;
  color: var(--muted-fg);
}

.stempel-digit.have { border-color: #3f9142; color: var(--fg); }
.stempel-digit img { height: 28px; background: #000; image-rendering: pixelated; }

/* The import screen before there is anything on it. The upload is a
   minute or more for a card, and the screen opens on the drop rather
   than on the answer - so this is what is on it until then: the same
   creature that waits everywhere else in this app, and a line saying
   what is being done. */
.import-waiting {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 60px 20px;
  text-align: center;
  color: var(--muted-fg);
}

/* ---------------------------------------------------------------
   The front door (login.html)

   The only page in the app with no nav rail, so it is centred in the
   viewport rather than laid out beside one - and the only page that is
   ever seen by somebody who is not logged in, which is why it says the
   name of the thing and nothing about what is in it.
   --------------------------------------------------------------- */

body.anmeldung-seite {
  margin: 0;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  color: var(--fg);
}

.anmeldung {
  width: min(380px, calc(100vw - 40px));
  padding: 28px 26px 26px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--card-bg);
  /* Lifted off the page rather than filling it: on a phone this is a
     card, on a desktop it is a card, and a form that spans a 27-inch
     screen is a form nobody knows where to start reading. */
  box-shadow: 0 6px 28px rgba(0, 0, 0, 0.07);
}

.anmeldung-titel {
  margin: 0 0 4px;
  font-size: 19px;
  letter-spacing: 0.01em;
}

.anmeldung-unterzeile {
  margin: 0 0 20px;
  color: var(--muted-fg);
  font-size: 13px;
  line-height: 1.45;
}

.anmeldung-form { display: flex; flex-direction: column; gap: 14px; }
.anmeldung-form[hidden] { display: none; }

.anmeldung-feld { display: flex; flex-direction: column; gap: 5px; font-size: 13px; }
.anmeldung-feld[hidden] { display: none; }
.anmeldung-feld > span { color: var(--muted-fg); }
.anmeldung-feld em { font-style: normal; opacity: 0.75; }

.anmeldung-feld input {
  padding: 9px 10px;
  font-size: 15px;
  color: var(--fg);
  background: var(--control-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
}

.anmeldung-feld input:focus {
  outline: 2px solid var(--bar-color);
  outline-offset: -1px;
}

.anmeldung-knopf {
  margin-top: 4px;
  padding: 11px 12px;
  font-size: 15px;
  color: #fff;
  background: var(--bar-color);
  border: none;
  border-radius: 8px;
  cursor: pointer;
}

.anmeldung-knopf:disabled { opacity: 0.6; cursor: default; }

.anmeldung-fehler {
  margin: 0;
  font-size: 13px;
  color: #b3261e;
}
.anmeldung-fehler[hidden] { display: none; }

body.dark .anmeldung-fehler { color: #f2b8b5; }

.anmeldung-hinweis {
  margin: 18px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  color: var(--muted-fg);
  font-size: 12px;
  line-height: 1.5;
}
.anmeldung-hinweis[hidden] { display: none; }

/* Dieselbe Farbe wie im Wechsel darunter. Ohne die Regel war „Als jemand
   anderes anmelden" auf der Einladungsseite das einzige knallblaue
   Browser-Blau in der ganzen Anwendung - aufgefallen ist es erst, als
   daneben eine Frage stand, die beantwortet werden will (21.08.2026). */
.anmeldung-hinweis a { color: var(--bar-color); }

/* ---- „Welche deiner Reviere sollen hineingeben?" ---- (21.08.2026)

   Die Frage bei einer Einladung in eine Gesellschaft. Ein Kasten mit
   Kästchen, zwischen den Feldern und dem Knopf - dort, wo man ihn vor dem
   Drücken liest, und nicht darunter, wo man ihn danach fände. */
.einladung-reviere {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--control-bg);
}

.einladung-reviere[hidden] { display: none; }

.einladung-reviere-frage {
  margin: 0;
  font-size: 13px;
  color: var(--fg);
}

/* Der Hinweis darunter trägt schon `.anmeldung-hinweis`; hier fällt nur
   der Abstand weg, den er als eigenständiger Absatz hätte. */
.einladung-reviere-note { margin: 2px 0 0; }

.einladung-revier {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  cursor: pointer;
}

.einladung-revier input { flex: none; }

/* The way from the login to the registration form and back. One line,
   under everything else and quieter than the button: both pages have one
   thing you came to do, and this is the note for the case where you came
   to the wrong one of the two. */
.anmeldung-wechsel {
  margin: 14px 0 0;
  color: var(--muted-fg);
  font-size: 13px;
  text-align: center;
}
.anmeldung-wechsel[hidden] { display: none; }
.anmeldung-wechsel a { color: var(--bar-color); }

/* ---------------------------------------------------------------
   Mein Konto, in the settings page

   The second of the four areas §17 lays out. The other two - "Dieses
   Revier" and "Freigaben" - arrive with the members list and the shares;
   this one is here now because a login you cannot get out of again is
   half a login.
   --------------------------------------------------------------- */

.konto-zeile {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: 14px;
  margin-bottom: 10px;
}

.konto-handle { font-weight: 600; }
.konto-revier { color: var(--muted-fg); font-size: 13px; }

.konto-knoepfe { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }

/* ---------------------------------------------------------------
   The four areas of the settings page (§17)

   `h2` is an area - this device, my account, this Revier, shares - and
   `h3` is a block inside one. The areas need to read as boundaries
   rather than as more headings, because the whole point of the division
   is that a members list and a dark-mode switch are not the same kind of
   thing.
   --------------------------------------------------------------- */

.settings-area {
  margin: 34px 0 2px;
  padding-bottom: 6px;
  border-bottom: 2px solid var(--bar-color);
  font-size: 17px;
  letter-spacing: 0.02em;
}

.settings-area:first-of-type { margin-top: 8px; }

/* Who is in the Revier, and which links are still out. Rows rather than
   a table: on a phone a table of three columns is a table with a
   horizontal scrollbar. */
.mitglieder, .einladungen { margin: 12px 0; }

.mitglied-zeile, .einladung-zeile {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}

/* **Das Notizbuch der Kamera am Kameraplatz** (22.08.2026). Es steht
   über dem des Platzes, und die zwei müssen auseinanderzuhalten sein -
   sonst liest sich das obere als Anfang des unteren. Also ein eigener
   Rahmen und ein Kopf mit dem Namen der Kamera darin: der Kasten sagt,
   wessen Buch das ist. */
/* **Nur waagerechte Striche, ueber die ganze Breite** (05.09.2026, auf
   Wunsch: *„if a camera is placed, its tasks/todo borders shall only be
   horizontal separators filling the whole width (no vertical border)"*).

   Es war ein Kasten mit Rand und Radius - und ein Kasten in einem Kasten,
   denn er steht ohnehin schon in der Karte einer Einrichtung oder im
   Fenster der Karte. Zwei Rahmen ineinander trennen nichts, was nicht
   schon getrennt waere; was hier wirklich getrennt gehoert, ist das Buch
   der Kamera von dem des Platzes darueber, und dafuer reicht ein Strich.

   Er geht von Kante zu Kante: ein Strich, der vor dem Rand aufhoert,
   liest sich als Rahmen, dem eine Seite fehlt. Das Polster gibt er dafuer
   wieder her - dieselbe Rechnung wie beim Bilderband im Elementfenster,
   nur waagerecht. */
.kamera-notizbuch {
  margin: 6px 0 14px;
  border: 0;
  border-top: 1px solid var(--border);
  border-radius: 0;
}

.kamera-notizbuch-kopf {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 6px 8px;
  background: var(--control-bg);
  border-bottom: 1px solid var(--border);
}

/* Der Strich soll die ganze Breite nehmen, auch wo der Kasten darum
   polstert: Kopf und Rand geben es wieder her. Die Zahl ist die des
   Fensters (`--inspector-polster`) - beide Fenster, die das Buch zeigen,
   nennen sie. Der Inhalt bekommt dasselbe Mass als Polster zurueck und
   steht damit auf der Flucht der uebrigen Zeilen: der Strich geht bis an
   die Kante, die Schrift darueber und darunter nicht. */
.kamera-notizbuch {
  margin-left: calc(-1 * var(--inspector-polster, 0px));
  margin-right: calc(-1 * var(--inspector-polster, 0px));
}

.kamera-notizbuch-kopf,
.kamera-notizbuch .notes {
  padding-left: var(--inspector-polster, 8px);
  padding-right: var(--inspector-polster, 8px);
}

/* Der Name nimmt den Platz, die zwei Knöpfe rücken ans andere Ende -
   sie sind das, wofür man hergekommen ist, und stehen deshalb dort, wo
   der Daumen ohnehin liegt. */
.kamera-notizbuch-name {
  font-weight: 600;
  font-size: 13px;
  margin-right: auto;
}

.kamera-notizbuch-kopf button {
  font-size: 12px;
  padding: 3px 8px;
}

/* Der Rahmen des Notizbuchs darin wäre der zweite - einer reicht. */
.kamera-notizbuch .notes { border: none; margin: 0; }

/* Die Zeile über dem Buch des Platzes, wenn das der Kamera darübersteht.
   In derselben stillen Schrift wie „Aufgaben" und „Logbuch" im Widget:
   sie beschriftet, sie sagt nichts. */
.notizbuch-titel {
  margin-top: 10px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted-fg);
}

/* Und der Abstand darunter gehört dann der Zeile, nicht dem Kasten. */
.notizbuch-titel + .notes { margin-top: 4px; }

.mitglied-handle { font-weight: 600; }

/* **Ein fremdes Revier und seine Leute** (22.08.2026), auf der Seite
   einer Gesellschaft. Ein Block je Revier statt einer flachen Liste mit
   dem Reviernamen in jeder Zeile: gefragt ist „wer steht dort", und dort
   ist die Gruppierung. Die Zeilen darin sind dieselben
   `.mitglied-zeile`n wie überall - eine zweite Zeilensorte für dieselbe
   Auskunft wäre eine, die irgendwann anders aussieht. */
.nachbar-revier { margin: 0 0 18px; }
.nachbar-revier:last-child { margin-bottom: 0; }

.nachbar-kopf {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-bottom: 6px;
  border-bottom: 2px solid var(--border);
}

/* Die Zeilen rücken unter dem Wappen ein, damit man auf einen Blick
   sieht, wo ein Revier aufhört und das nächste anfängt. */
.nachbar-revier .mitglied-zeile,
.nachbar-revier .settings-note { margin-left: 30px; }
.mitglied-seit { color: var(--muted-fg); font-size: 12px; }

/* **Der Rang** (`0044`): entweder ein Wort oder eine Auswahl, je nachdem,
   ob der Lesende ihn setzen darf. Beide bekommen dieselbe Breite, damit
   eine Liste, in der nur manche Zeilen bedienbar sind, nicht auf halber
   Höhe ausfranst - die Zeile ist ohnehin schon so ausgerichtet, wie der
   Handle lang ist, und ein zweiter Sprung wäre einer zu viel. Nicht so
   leise wie „dabei seit": das ist eine Angabe darüber, wer was darf, und
   keine Fußnote. */
.mitglied-rang, .rang-wahl { min-width: 92px; }
.mitglied-rang { color: var(--muted-fg); }
.rang-wahl { font-size: 13px; padding: 2px 4px; }

.mitglied-zeile .delete-button,
.einladung-zeile .delete-button { margin-left: auto; }

/* **The gear at the end of a row**, in the two lists whose rows name
   something that has a page of its own: a Revier, a Gesellschaft. Pushed
   to the far edge like the delete buttons above, because both are "the
   thing you do to this row" and a row should have one of those places,
   not two. Quiet until hovered - the row is a list entry first and a
   link second. */
.zeile-zahnrad {
  margin-left: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: none;
  color: var(--muted-fg);
  cursor: pointer;
}
.zeile-zahnrad:hover { background: var(--control-bg); color: var(--fg); }

/* **Zwei „ganz nach rechts" sind eines zu viel** (25.08.2026). Seit die
   Gesellschaftszeile neben dem Zahnrad auch ein *Hinausnehmen* trägt,
   stehen zwei `margin-left: auto` in derselben Reihe — und Flexbox teilt
   den freien Platz dann brüderlich zwischen beide auf, was den Knopf in
   die Mitte der Zeile setzt statt an ihr Ende. Der freie Platz gehört
   dem ersten der beiden; danach stehen sie nebeneinander, wie es
   aussehen soll. */
.mitglied-zeile .delete-button ~ .zeile-zahnrad,
.einladung-zeile .delete-button ~ .zeile-zahnrad { margin-left: 0; }

/* ---- Die Verwaltung ----

   The numbers at the top of the admin page: a wrapping row of tiles
   rather than a table, because there are nine of them, they have nothing
   to do with each other except being about this machine, and a table
   would promise columns that mean something. Each is one number and one
   word under it. */
/* ---------------------------------------------------------------
   Eine geliehene Sitzung sieht anders aus (`0048`)

   Ein Verwalter kann sich ein fremdes Konto ansehen; das darf nie
   aussehen wie eine gewöhnliche Anmeldung.

   **Es war erst ein rotes Band über der Seite** — auffällig, aber es nahm
   34 Pixel von jeder Seite, schob das Geländer nach unten und musste
   seine eigene Höhe messen, damit nichts um zwei Pixel verrutschte. Also
   umgekehrt: **die Leiste selbst wird rot.** Sie ist ohnehin auf jeder
   Seite, sie steht immer im Blick, sie kostet keinen Platz, und eine
   Anwendung, deren Hauptnavigation die Farbe wechselt, sagt „hier ist
   etwas anders" deutlicher als ein Streifen, den man nach zehn Minuten
   nicht mehr liest. Der Weg zurück steht im Wechsler darin, eine Zeile
   über der Kontozeile.

   `--bar-color` wird gleich mit umgestellt, damit alles, was sich in der
   Leiste danach richtet (der aktive Eintrag, das Wappen ohne Bild), nicht
   in Grün gegen das Rot steht. --------------------------------- */
body.uebernahme #appNav {
  --bar-color: #fff;
  background: #8a1c13;
  border-right-color: #6d160f;
}

body.uebernahme #appNav .nav-item { color: rgba(255, 255, 255, 0.82); }

body.uebernahme #appNav .nav-item.active {
  color: #fff;
  background: rgba(0, 0, 0, 0.22);
}

/* Der Wechslerknopf steht mitten darin: sein Trennstrich und die Platte
   unter einem Wappen ohne Bild müssen mit. */
body.uebernahme #appNav .nav-revier {
  color: #fff;
  border-bottom-color: rgba(255, 255, 255, 0.35);
}

body.uebernahme #appNav .nav-revier .wappen { background: rgba(0, 0, 0, 0.25); }
body.uebernahme #appNav .nav-revier .wappen.leer { background: rgba(0, 0, 0, 0.35); }
body.uebernahme #appNav .nav-revier-glyph { color: rgba(255, 255, 255, 0.85); }

@media (max-width: 700px) {
  body.uebernahme #appNav { border-right: none; border-top-color: #6d160f; }
  body.uebernahme #appNav .nav-revier { border-left-color: rgba(255, 255, 255, 0.35); }
}

/* **Der Weg zurück, im Wechsler.** Dieselbe Zeile wie die Verwaltung
   darüber — es sind beides Sachen, die nicht vom Baum handeln —, nur in
   der warnenden Farbe, weil sie etwas beendet. Sie steht unmittelbar über
   der Kontozeile: das ist die Stelle, an der man in diesem Popup nach dem
   sucht, was mit *einem selbst* zu tun hat. */
.nav-revier-zurueck {
  display: flex;
  align-items: center;
  gap: 8px;
  box-sizing: border-box;
  width: 100%;
  margin-top: 8px;
  padding: 8px 10px;
  border: 0;
  border-top: 1px solid var(--border);
  border-radius: 6px;
  background: none;
  color: #b3261e;
  font: inherit;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}

body.dark .nav-revier-zurueck { color: #f2b8b5; }

.nav-revier-zurueck:hover { background: var(--img-placeholder); }
.nav-revier-zurueck:disabled { opacity: 0.6; cursor: default; }

.nav-revier-zurueck .nav-revier-wort { font-weight: 600; }

/* Steht sie da, trägt sie den Strich und der Fuss darunter keinen — wie
   bei der Verwaltungszeile, und aus demselben Grund: zwei Linien in Folge
   lesen sich als zwei Gruppen, wo eine gemeint ist. */
.nav-revier-zurueck + .nav-revier-fuss {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}

/* Und wenn beide dastehen, trägt nur die obere einen. */
.nav-revier-verwaltung + .nav-revier-zurueck {
  margin-top: 0;
  border-top: none;
}

/* ---------------------------------------------------------------
   Das Netz und der Katalog (Verwaltung, 2026-08-16)

   Zwei Ansichten derselben Daten: unten eine Tabelle zum Suchen, oben
   ein Bild zum Verstehen. Die Tabelle ist die einzige *echte* Tabelle in
   dieser Anwendung - überall sonst sind Listen Zeilen aus <div>s, weil
   sie eine Sache je Zeile zeigen und nicht vier Spalten, die man
   vergleichen will. Hier will man vergleichen und sortieren, und dafür
   ist ein <table> das richtige Element, nicht das altmodische.
   --------------------------------------------------------------- */

/* **Unter dem Bild** (19.08.2026): die Knöpfe wirken auf das, was darin
   gewählt ist, und standen darüber - man drückte also nach oben, um etwas
   zu ändern, das unten steht. */
.netz-leiste {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}

/* Der Rahmen um das Bild, damit die Fläche als Fläche zu erkennen ist -
   ein Canvas ohne Rand ist ein Loch in der Seite. */
.netz-huelle {
  border: 1px solid var(--border);
  /* Oben ohne Rundung und ohne eigenen Strich: die Tabelle darüber bringt
     ihren mit, und zwei Linien zwischen zwei Hälften eines Werkzeugs sind
     eine zu viel. */
  border-top: none;
  border-radius: 0 0 8px 8px;
  background: var(--bg);
  overflow: hidden;
}

/* `display: block`, sonst setzt die Zeilenhöhe des Inline-Elements ein
   paar Pixel unter das Bild, und der Rahmen sitzt schief.

   Die Höhe steht hier und nicht im Attribut: `width`/`height` am Canvas
   sind seine *Pixel*, und die rechnet `zeichneNetz` aus der gemessenen
   Breite mal `devicePixelRatio` aus. Wer sie hier ändert, ändert die
   Fläche; das Bild darin passt sich an. */
.netz-canvas {
  display: block;
  width: 100%;
  height: 380px;
  touch-action: none;   /* sonst scrollt das Telefon, statt zu ziehen */
}

/* Der Weg zurück aus einer verstellten Ansicht (22.08.2026). Rechts,
   weil er selten gebraucht wird und die Zeile darüber die Auskunft
   trägt. */
.netz-ansicht {
  display: flex;
  justify-content: flex-end;
  margin-top: 4px;
}

/* **Nur zum Ansehen**: kein Zeiger-Wechsel auf „anklickbar", weil hier
   nichts auszuwählen ist - geschoben und gezogen wird trotzdem, und das
   sagt `grab` im Skript. */
.netz-nur-ansehen { cursor: default; }

.netz-auswahl {
  min-height: 18px;
  margin-top: 6px;
  color: var(--muted-fg);
  font-size: 12px;
}

/* Ein Schalter im Fenster: Kästchen links, Wort rechts daneben. Die
   anderen Felder tragen ihr Wort darüber - dort ist es eine Beschriftung
   über einer Eingabe, hier gehört es zum Kästchen wie bei jeder anderen
   Checkbox in dieser Anwendung. */
.modal-field.modal-schalter {
  display: flex;
  align-items: center;
  gap: 8px;
}

.modal-field.modal-schalter label { margin: 0; font-size: 13px; color: var(--fg); }
.modal-field.modal-schalter input { width: auto; }

.katalog-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}

.katalog-filter input[type="search"] {
  flex: 1 1 180px;
  min-width: 0;
  box-sizing: border-box;
  padding: 7px 9px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg);
  color: var(--fg);
  font: inherit;
  font-size: 14px;
}

.katalog-zahl {
  margin-left: auto;
  color: var(--muted-fg);
  font-size: 12px;
}

/* Die Tabelle darf schmaler werden als ihr Inhalt und rollt dann in
   ihrem eigenen Kasten - die Seite selbst nie waagerecht (conventions).

   **Und sie sitzt direkt auf der Zeichenfläche** (19.08.2026): die zwei
   sind ein Werkzeug in zwei Hälften, oben wird ausgewählt, unten steht,
   was daran hängt. Also teilen sie sich die Kante - unten keine Rundung,
   kein zweiter Strich - und darüber steht nichts, was sie trennt. Eine
   Höhe bekommt sie dafür: eine Tabelle mit vierzig Zeilen schöbe das Bild
   sonst aus dem Fenster, und dann wäre die Nachbarschaft wieder keine. */
.katalog-huelle {
  overflow: auto;
  max-height: 340px;
  border: 1px solid var(--border);
  border-radius: 8px 8px 0 0;
}

.katalog {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.katalog th,
.katalog td {
  padding: 7px 10px;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: middle;
}

/* Klebt beim Rollen oben - und seit der Kasten eine feste Höhe hat, ist
   das keine Zierde mehr, sondern das, was die Spaltennamen sichtbar hält,
   während man durch vierzig Zeilen fährt. */
.katalog thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--control-bg);
  color: var(--muted-fg);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* Sortieren ist ein Knopf, kein Wort mit Zufallsverhalten. */
.katalog-sort {
  padding: 0;
  border: none;
  background: none;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
}

.katalog-sort:hover { color: var(--fg); }

.katalog-haken { width: 32px; }
.katalog-name { font-weight: 600; }
.katalog-zustand { color: var(--muted-fg); white-space: nowrap; }

/* Verwaist ist der eine Zustand, der etwas *tut*: er läuft ab. Deshalb
   trägt er die warnende Farbe und die anderen nicht. */
.katalog-zustand.warnung { color: #b3261e; }
body.dark .katalog-zustand.warnung { color: #f2b8b5; }

.katalog-angaben { color: var(--muted-fg); }

.katalog-zeile { cursor: pointer; }
.katalog-zeile:hover { background: var(--img-placeholder); }
.katalog-zeile.gewaehlt { background: var(--img-placeholder); }
.katalog-zeile.gewaehlt .katalog-name { color: var(--bar-color); }

.katalog-leer {
  padding: 14px 10px;
  color: var(--muted-fg);
  text-align: center;
}

@media (max-width: 700px) {
  /* Auf dem Telefon ist das Netz flacher - es steht über einer Tabelle,
     die man erreichen können muss, ohne an ihm vorbeizuscrollen. */
  .netz-canvas { height: 280px; }
  .katalog-angaben { display: none; }
}

.verwaltung-zahlen {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 4px 0 12px;
}

.verwaltung-kachel {
  /* Never stretched: a row of four that ends in two very wide plates
     reads as two important numbers and two afterthoughts. They keep
     their size and the row simply wraps. */
  flex: 0 1 auto;
  min-width: 108px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--control-bg);
}

.verwaltung-zahl { font-size: 19px; font-weight: 600; }
.verwaltung-wort { color: var(--muted-fg); font-size: 12px; }

/* A heading with one button on it: `+` for a new Gesellschaft. The
   alternative is a name field standing under the list for ever, which is
   a form nobody is filling in for the eleven months between two of
   them. */
.settings-heading-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.heading-plus {
  width: 26px;
  height: 26px;
  padding: 0;
  border-radius: 50%;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}

/* The one place a status line has to be readable rather than glanced at:
   it carries the invitation link, which is the only time that string is
   ever shown. */
/* Both of them carry an invitation link, which is the only time that
   string is ever shown - see the note above. */
#revierStatus, #gesellschaftStatus { word-break: break-all; }

/* A status line that is reporting a failure. `.bad` is what every status
   helper on these pages toggles (settings.js, papierkorb.js) and it had
   no rule at all until now, so a refusal came out in the same grey as
   "gespeichert". `.error` is the older spelling of the same idea and
   keeps working. */
.settings-status.bad, .settings-status.error { color: #e57373; }

/* A link that stands in a row of buttons and has to look like one. Used
   for the way into the Papierkorb from the settings page: it is a
   navigation and belongs in an <a>, but a bare link between two buttons
   reads as a footnote. */
.button-link {
  display: inline-flex;
  align-items: center;
  padding: 6px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--control-bg);
  color: var(--fg);
  font-size: 14px;
  text-decoration: none;
}

.button-link:hover { border-color: var(--bar-color); }

/* ---------------------------------------------------------------
   Der Papierkorb (Etappe 5, usermanagement-design.md §15).

   A list of rows, not a grid of tiles, and that is the whole visual
   argument: the gallery is for looking at photographs and this is for
   finding one row you did not mean to throw away. So each row is wide
   and short, the pictures in it are small and in a line, and what
   carries the eye is the text beside them - when it went and how long
   is left.
   --------------------------------------------------------------- */

.papierkorb-leiste {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin: 14px 0 4px;
}

.papierkorb-zahl { font-size: 14px; color: var(--muted-fg); }

/* The two verbs to the right, away from the count they name - a button
   that empties everything should not sit against the number it would
   destroy as if it were a label for it. */
.papierkorb-knoepfe { margin-left: auto; display: flex; gap: 8px; }

.papierkorb-liste { margin: 10px 0 40px; }

/* The two sections' headings (`0046`). Small and quiet: they are labels
   telling a reader which kind of thing the rows under them are, not
   places to navigate to - the page is one bin and the buttons act on all
   of it. Each is hidden when its section is empty, so the ordinary state
   (an element deleted, no Anblicke) is one heading and one list rather
   than a page of headings over nothing. */
/* Die Zwischenüberschrift innerhalb eines Blocks, an zwei Stellen
   dieselbe: über den Elementen im Papierkorb, und über den zwei Rängen
   im Freigabeblock (`0045`). Eine Regel und nicht zwei, weil es dieselbe
   Sache ist - eine Liste in zwei Hälften teilen, ohne eine zweite
   Überschriftsebene aufzumachen. Grossbuchstaben plus Sperrung statt
   grösserer Schrift: der Block steht schon unter einer `h2`. */
.papierkorb-ueberschrift,
.freigabe-ueberschrift {
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted-fg);
  margin: 26px 0 0;
}

/* Im Freigabeblock steht direkt darunter eine Zeile mit einem
   Auswahlfeld statt einer Liste, also braucht sie unten auch Luft - und
   oben mehr als der Papierkorb, weil darüber die vier Kategorien stehen
   und die Trennung genau das ist, was diese Überschrift leisten soll. */
.freigabe-ueberschrift {
  margin: 32px 0 10px;
}

.papierkorb-ueberschrift[hidden] { display: none; }

.papierkorb-eintrag {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 8px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
}

.papierkorb-eintrag:hover { background: var(--img-placeholder); }

/* Ticked, and it has to be visible at a glance across a long list: the
   band on the left is what says so at the edge of vision, the tint is
   what says so under the cursor. */
.papierkorb-eintrag.gewaehlt {
  background: var(--img-placeholder);
  box-shadow: inset 3px 0 0 var(--bar-color);
}

.papierkorb-bilder { display: flex; gap: 3px; flex: none; }

.papierkorb-bilder img {
  width: 54px;
  height: 54px;
  object-fit: cover;
  border-radius: 3px;
  background: var(--img-placeholder);
}

/* Fewer of them as the window narrows. Four thumbnails, a title and two
   lines of small print do not fit on a phone, and the first picture is
   the one that says what this was. */
@media (max-width: 560px) {
  .papierkorb-bilder img:nth-child(n + 3) { display: none; }
}

.papierkorb-text { flex: 1 1 auto; min-width: 0; }

.papierkorb-titel { font-size: 14px; font-weight: 600; }

/* The three lines under the title are all small print, and all of them
   are one line: a text Anblick can be a paragraph, and a bin is not
   where it is read. */
.papierkorb-notiz,
.papierkorb-tags,
.papierkorb-wann {
  font-size: 12px;
  color: var(--muted-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.papierkorb-notiz { font-style: italic; }

/* How long is left, at the end of the row where a number belongs. It
   does not shrink and it does not wrap - it is the one thing on the row
   that is a deadline. */
.papierkorb-frist {
  flex: none;
  font-size: 12px;
  color: var(--muted-fg);
  white-space: nowrap;
}

/* ---------------------------------------------------------------
   Der Revierwechsler (Etappe 6, usermanagement-design.md §9).

   In the nav bar, because §17 puts it over every page and the Revier
   band is only on three of them.

   **Above the three items on the rail, at the right-hand end of the bar
   on a phone** (2026-08-16, auf Wunsch) - and that is two different
   places for one control, which is why it is done with `order` rather
   than with the DOM: `renderNav()` writes the three items and
   `loadRevierWechsler()` appends the button *after a fetch*, so the DOM
   order is "items, then button" whenever the fetch comes back, and
   `order: -1` up here turns that into "button, then items" without
   either function having to know about the other. Down in the phone
   query the `order` is put back to 0, where "last in the DOM" is again
   "right-hand end".

   **The older reasoning, overruled rather than deleted**: it stood
   *directly under* the last item, because the three before it are places
   in the archive and a run of places is read from the end you start at -
   this being not a fourth place but the thing that says which archive
   those three are about, plus (in its popup) everything about the
   account, both set-and-forget. That is still true of what it *is*; the
   user wants it first anyway, and on a desk the eye starts at the top of
   the rail, so the Wappen naming the archive is the first thing read.
   The phone is untouched: there the thumb argument stands and the button
   stays at the far right.

   It was briefly pushed to the *foot* of the rail with `margin-top: auto`,
   which is a different thing and was not what was asked for either: 500px
   of nothing between the third control and the fourth reads as two
   groups, not as the end of one. It is one run of four controls, and now
   the fourth is the first of them.

   **Und am 04.09.2026 doch an den Fuss** (auf Wunsch: *„the revier
   switcher on large screens shall go to the bottom of the screen after
   all"*). Das Argument von damals - die Lücke lese sich als zwei Gruppen
   - stimmt weiter und ist jetzt die Absicht: der Wechsler *ist* keine
   vierte Stelle im Archiv, sondern das, was sagt, um welches Archiv es
   bei den dreien geht, und was ihn von ihnen trennt, trennt zwei Dinge,
   die verschieden sind. Am Fuss ist er ausserdem dort, wo eine Anwendung
   ihr Konto hinlegt.

   `order: 1` statt `-1` und `margin-top: auto`: das eine hängt ihn hinter
   die drei, egal wann der Fetch zurückkommt, das andere schiebt ihn an
   den Rand. Der Strich wandert mit auf die Seite, auf der die Sachen
   stehen - jetzt oben. **Das Telefon bleibt unberührt**: dort ist die
   Leiste waagrecht, `order: 1` heisst rechtes Ende, und `margin-top: 0`
   in der Medienabfrage nimmt das `auto` wieder zurück.

   **And it is the Wappen alone.** No name under the mark: 64px will not
   hold one, so what stood there was `Harb…`, and in the empty state
   `Einst…` - a mark that has to be read is not a mark. The popup spells
   every name out, and nav.js puts the whole one in `title`/`aria-label`
   so the button still has something to be called.
   --------------------------------------------------------------- */

.nav-revier {
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(100% - 12px);
  padding: 10px 2px;
  /* Letztes im Geländer, egal wann der Knopf nachgereicht wird - und
     dann an dessen Fuss. */
  order: 1;
  margin-top: auto;
  border: none;
  /* The rule is on the side the items are on - above it down here,
     because it stands under them again. Die 4px liegen auf dem `gap` der
     Spalte und halten den Strich von der Zeile darüber frei. */
  border-top: 1px solid var(--border);
  padding-top: 14px;
  border-radius: 6px;
  background: none;
  color: var(--fg);
  cursor: pointer;
}

.nav-revier:hover { color: var(--bar-color); }

/* Der reservierte Platz, den `renderNav()` sofort setzt und
   `loadRevierWechsler()` gegen den fertigen Knopf tauscht: derselbe
   Kasten, nur unsichtbar - `visibility: hidden` und nicht `opacity`,
   sonst stünde für einen Moment ein Strich ohne etwas darüber. Er nimmt
   keine Klicks an und wird weggeräumt, wenn kein Knopf kommt. */
.nav-revier-platz {
  visibility: hidden;
  pointer-events: none;
}

/* An account in no Revier has no Wappen to show, so the button wears the
   gear its popup leads to (nav.js). The plate the Wappen sits on would be
   wrong here - it says "this archive chose no picture", and there is no
   archive. */
.nav-revier-glyph {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  color: var(--muted-fg);
}

/* ---- Das Wappen ----

   One square, wherever a Revier or a Gesellschaft is named: in the rail,
   in the switcher's list, and on their own pages beside the name. Always
   the same object at three sizes, which is what makes it a mark rather
   than an illustration.

   Rounded rather than a shield outline: at 22px a shield is a shape with
   a picture squeezed into it, and what has to read is the picture. */
.wappen {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--wappen-size, 28px);
  height: var(--wappen-size, 28px);
  border-radius: 22%;
  overflow: hidden;
  color: var(--fg);
  vertical-align: middle;
}

.wappen svg { width: 78%; height: 78%; }
.wappen img { width: 100%; height: 100%; object-fit: cover; }

/* Nothing chosen: the first letter of the name, on the app's own colour.
   Never a random glyph - a Wappen nobody picked must not look like one
   somebody did. */
.wappen.leer {
  background: var(--bar-color);
  color: #fff;
  font-size: calc(var(--wappen-size, 28px) * 0.5);
  font-weight: 600;
  line-height: 1;
}

/* In the rail the mark carries the whole recognition, so it is drawn on
   a quiet plate rather than floating in the column. */
.nav-revier .wappen { background: var(--img-placeholder); }
.nav-revier .wappen.leer { background: var(--bar-color); }

/* Im Wechsler sitzt es im Namensknopf, der eine Textzeile ist - die
   Zeilenform steht bei `.nav-revier-name` weiter unten, an einer Stelle
   statt an zweien. */

/* The picker on the two pages that own a Wappen: every drawn option as a
   tile, the current one ringed. A wrapping row rather than a select -
   there are eighteen of them and the whole question is what they look
   like. */
.wappen-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 6px 0 2px;
}

.wappen-option {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--control-bg);
  color: var(--fg);
  font-size: 20px;
  cursor: pointer;
}

.wappen-option:hover { border-color: var(--bar-color); }

/* Chosen: a ring in the accent colour, the same mark every other "this
   one is picked" in the app wears. */
.wappen-option.gewaehlt {
  border-color: var(--bar-color);
  box-shadow: 0 0 0 2px var(--bar-color);
}

/* The upload tile, which is a `+` until there is a picture in it. */
.wappen-option.eigenes { color: var(--muted-fg); }

/* One line, cut with an ellipsis. This is the *popup's* row now - the
   rail's button carries no name at all any more, so there is nothing here
   about fitting 64px. A name still has to be allowed to be too long: the
   popup is bounded by the window below, and 'Jagdgenossenschaft
   Oberammergau West' would otherwise decide how wide it is. */
.nav-revier-name {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  line-height: 1.2;
}

/* `fixed` and 950, both for the same reason: this list has to stand over
   the Revier band when the band is open (900), and it cannot do that
   from inside #appNav, which has a z-index and therefore a stacking
   context of its own. So nav.js hangs it on the body instead - which is
   safe because the rail the coordinates are measured from is itself fixed
   to an edge of the viewport. 950 is the number the gallery FABs already
   use to clear the open band, and it stays under the lightbox (9999) and
   the modals (10000).

   **Measured from the edge the button is on**, and das ist seit dem
   04.09.2026 der **Fuss** (auf Wunsch: *„the revierswitcher pane shall
   also move to the bottom on desktops"*). Der Satz, der hier stand, gilt
   unveraendert und nur andersherum: *ein Menue, das aus der einen Ecke
   waechst, waehrend sein Knopf in der anderen sitzt, ist ein Menue mit
   400px Nichts zwischen sich und dem, was es geoeffnet hat.* Der Knopf
   ist an den Fuss gegangen, also geht das Menue mit.

   Die Zahl bleibt dieselbe und stimmt gespiegelt genauso: das Gelaender
   traegt unten `padding-bottom: 12px`, also sitzt die Unterkante des
   Menues 4px unter der des Knopfes - es haengt an ihm, statt nur auf
   derselben Haelfte des Schirms zu liegen. `left: 68px` raeumt weiter das
   64px-Gelaender samt Strich, und die 8px sind auch die Rinne an der
   Gegenseite, die `max-height: calc(100vh - 16px)` mitzaehlt. Das Telefon
   hatte sein `bottom` schon immer, weil die Leiste dort wirklich am Fuss
   liegt - es ist jetzt dieselbe Antwort auf beiden Schirmen, aus
   demselben Grund.

   A max-height, because die Liste ist so lang wie meine Reviere plus die
   Gesellschaften darunter plus deren Reviere plus die Zeile am Fuß - seit
   dem Baum also länger als vorher. `border-box` with it, or the padding
   and the border are added on top of the max (fourteen Reviere in a 700px
   window is where that showed).

   **And a max-width, so it fits the window.** It is `fixed`, so it can
   never grow the *document's* scrollbars - what a name wider than the
   screen does instead is walk off the right edge, unreachable. 76px is
   the 68 it is offset by plus an 8px gutter on the other side; the names
   inside it ellipsise (above). */
.nav-revier-popup {
  position: fixed;
  z-index: 950;
  left: 68px;
  bottom: 8px;
  box-sizing: border-box;
  max-height: calc(100vh - 16px);
  max-width: calc(100vw - 76px);
  overflow-y: auto;
  /* 230 statt 190: drei Ebenen, und die tiefste fängt 32px eingerückt an
     - bei 190 blieb für den Namen darin eine Handbreit. */
  min-width: 230px;
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--control-bg);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* Der Baum, und der Fuß darunter. Zwei Kästen, weil der obere sich beim
   Auf- und Zuklappen neu zeichnet und der untere davon nichts merkt. */
.nav-revier-baum {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* **Eine Zeile ist drei Sachen**: der Pfeil, der auf- und zuklappt, der
   Name, der wechselt, und das Zahnrad, das die eigene Seite dieses Dings
   öffnet. Drei Verben an einem Ding, und der Wechsler ist die Stelle, an
   der alle drei gebraucht werden.

   **Die Tiefe steckt in der Einrückung** (`--tiefe`, von nav.js gesetzt):
   Ebene 0 sind meine Reviere, Ebene 1 die Gesellschaften darunter, Ebene 2
   die Reviere, die sonst noch in einer Gesellschaft liegen. 16px pro Stufe
   - genug, dass eine Kante entsteht, wenig genug, dass bei drei Ebenen und
   190px Mindestbreite noch ein Name danebenpasst.

   **`border-box`, und das war der waagerechte Rollbalken.** `width: 100%`
   plus seitliche Polsterung ergab in `content-box` die Breite des Popups
   **+20px**, und weil `overflow-y: auto` still auch `overflow-x` auf
   `auto` setzt, antwortete das Popup mit einem Balken am Fuß statt die
   Zeile zu kürzen. Gilt seither für jede Zeile hier, die eine Polsterung
   trägt. */
.nav-revier-option {
  display: flex;
  align-items: center;
  gap: 2px;
  width: 100%;
  box-sizing: border-box;
  border-radius: 6px;
  padding-left: calc(var(--tiefe, 0) * 16px);
}

/* **Der Klapppfeil, und die Lücke, wo keiner ist.** Beide gleich breit:
   ein Revier ohne Gesellschaft darunter darf mit seinem Namen nicht 20px
   weiter links anfangen als das Revier darüber, sonst liest sich die
   Spalte als Fehler. Gedreht statt zweimal gezeichnet. */
.nav-revier-pfeil {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: none;
  border-radius: 4px;
  background: none;
  color: var(--muted-fg);
  cursor: pointer;
  transition: transform 0.12s ease;
}

.nav-revier-pfeil.offen { transform: rotate(90deg); }
.nav-revier-pfeil.leer { cursor: default; }
button.nav-revier-pfeil:hover { color: var(--fg); background: var(--img-placeholder); }

.nav-revier-name {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border: none;
  border-radius: 6px;
  background: none;
  color: var(--fg);
  font-size: 14px;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

/* Der Name selbst kürzt, nicht die Zeile: rechts davon stehen der Rang
   und das Zahnrad, und die sollen nicht aus dem Popup geschoben werden,
   weil ein Revier „Harb/Apfelbeck-Süd" heißt. */
.nav-revier-wort {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* **Der Rang, am Namen** (`0043`). Klein und still: er sagt, was man in
   diesem Revier darf, und das ist eine Auskunft neben dem Namen und keine
   zweite Überschrift. */
.nav-revier-rang {
  flex: 0 0 auto;
  color: var(--muted-fg);
  font-size: 11px;
  white-space: nowrap;
}

/* **Wie viel in diesem Buch ungelesen ist** (04.09.2026, auf Wunsch).

   Dieselbe orange Marke wie an der Leiste (`.nav-ungelesen`) und an
   einer Kachel - wer sie dort gelernt hat, liest sie hier ohne
   Erklaerung. Nicht absolut positioniert wie jene: dort sitzt sie ueber
   einem Zeichen, hier steht sie in einer Zeile und darf sich ihren Platz
   nehmen.

   **Ganz rechts am Namen**, hinter Rang und Art: was die Zeile *ist*,
   steht zusammen, und was gerade an ihr neu ist, steht am Rand, wo das
   Auge eine Liste nach Marken absucht. */
.nav-revier-ungelesen {
  flex: 0 0 auto;
  margin-left: auto;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  border-radius: 9px;
  background: #d98324;
  color: #fff;
  font-size: 11px;
  font-weight: bold;
  line-height: 17px;
  text-align: center;
  box-sizing: border-box;
}

/* Die Regel oben setzt kein `display`, also versteckt `[hidden]` die
   Marke schon von selbst - diese Zeile ist Vorsorge, nicht Reparatur:
   wer der Marke spaeter ein `display: inline-flex` gibt, um etwas darin
   auszurichten, nimmt ihr sonst unbemerkt das Verstecken. Genau der
   Fehler, den tests/js/hidden.test.js in den .html-Seiten bewacht und
   hier nicht sehen kann, weil diese Marke aus nav.js kommt. */
.nav-revier-ungelesen[hidden] { display: none; }

/* **Was die Zeile ist, wenn der Name es nicht sagt**: „Gesellschaft" an
   der zweiten Ebene, „verwaist" an einem Revier, dem niemand mehr
   gehört, beides zusammen als „Gesellschaft, verwaist" (nav.js,
   `artWort`). Dieselbe stille Schrift wie der Rang daneben, denn es ist
   dieselbe Art Auskunft: eine Beschriftung des Namens und keine zweite
   Zeile.

   `flex: 0 1 auto` statt `0 0 auto` wie beim Rang: das Wort darf im
   Notfall schrumpfen. Es steht ganz rechts in der Zeile, und lieber
   kürzt sich „Gesellschaft" als dass es das Zahnrad hinausschöbe. */
.nav-revier-art {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  color: var(--muted-fg);
  font-size: 11px;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* **Die Verwaltung, über der Kontozeile.** Sie sieht aus wie diese - das
   ist Absicht: die zwei Zeilen unter dem Strich handeln beide nicht vom
   Baum darüber, sondern von dem, was ihn trägt. Nur ist diese hier ein
   Ort und keine Gruppe von Symbolen, also steht ihr Wort links neben dem
   Zeichen und nicht rechts. */
.nav-revier-verwaltung {
  display: flex;
  align-items: center;
  gap: 8px;
  box-sizing: border-box;
  width: 100%;
  margin-top: 8px;
  padding: 8px 10px;
  border-top: 1px solid var(--border);
  border-radius: 6px;
  color: var(--muted-fg);
  font-size: 13px;
  text-decoration: none;
}

.nav-revier-verwaltung:hover { background: var(--img-placeholder); color: var(--fg); }

.nav-revier-verwaltung[aria-current] {
  color: var(--bar-color);
  font-weight: 600;
}

/* Steht sie da, trägt sie den Strich, und der Fuss darunter braucht
   keinen zweiten - zwei Linien in Folge lesen sich als zwei Gruppen, wo
   eine gemeint ist. */
.nav-revier-verwaltung + .nav-revier-fuss {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}

/* The Revier one is standing in: the name is not a button any more, and
   a disabled button must not read as a greyed-out one. */
.nav-revier-name:disabled { color: inherit; cursor: default; opacity: 1; }

/* Quiet until the row is under the pointer - the popup is a list of
   places first, and a column of gears down the side of it would read as
   the point of the list. */
.nav-revier-zahnrad {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: none;
  color: var(--muted-fg);
  opacity: 0.55;
  cursor: pointer;
}
/* **Gone until the row is under the pointer** on a desk: a column of
   gears down the side of the list reads as the point of the list, and it
   is not - the list is places to go. `visibility` rather than `display`,
   so the row does not change width when one appears.

   On a touch device there is no hover, so they stay: a control you can
   only reveal with a pointer is a control a phone does not have (the
   same rule the filter count's × follows). */
@media (hover: hover) {
  .nav-revier-zahnrad { visibility: hidden; }
  .nav-revier-option:hover .nav-revier-zahnrad,
  .nav-revier-zahnrad:focus-visible { visibility: visible; opacity: 1; }
}
.nav-revier-zahnrad:hover { background: var(--img-placeholder); color: var(--fg); }

.nav-revier-leer {
  padding: 6px 10px 8px;
  color: var(--muted-fg);
  font-size: 12px;
}

button.nav-revier-name:hover { background: var(--img-placeholder); }

/* Where you are standing. Not a tick in a column of its own - at this
   width the mark and the name would each get half the room. */
.nav-revier-option.aktiv .nav-revier-name {
  color: var(--bar-color);
  font-weight: 600;
  cursor: default;
}

/* **Ein Revier unter einer Gesellschaft** - die dritte Ebene. Kein Ort,
   an den man von hier aus geht, sondern die Auskunft, wer sonst noch in
   dieser Gesellschaft liegt: also still, kleiner, ohne Zeiger und ohne
   Zahnrad. Das eigene darunter ist dasselbe eine Zeile weiter oben, und
   trägt deshalb nur eine etwas kräftigere Farbe. */
.nav-revier-option.glied .nav-revier-name {
  padding: 5px 10px;
  color: var(--muted-fg);
  font-size: 13px;
  cursor: default;
}

.nav-revier-option.glied.mein .nav-revier-name { color: var(--fg); }

/* **Der Fuß: eine Zeile, und sie handelt vom Konto.** Der Abstand darüber
   ist der Punkt - der Baum sagt, worin man steht, diese Zeile, wer man
   ist, und zwischen den beiden Aussagen gehört eine Lücke und ein Strich.
   „Neues Revier…" stand hier einmal als eigene Zeile und steht jetzt bei
   *Meine Reviere* in den Einstellungen: einmal im Leben eines Kontos
   gebraucht, zwanzigmal am Tag mitgelesen.

   **Alles vier hängt rechts** (2026-08-16, auf Wunsch). Vorher nahm die
   Kontozeile mit `flex: 1 1 auto` den freien Platz links ein und schob
   nur das Abmelden an die rechte Kante - Argument war, dass der Handle
   unter dem Baum anfängt, wo auch die Namen darüber anfangen. Der Nutzer
   wollte es andersherum; der freie Platz liegt jetzt *vor* dem Handle
   (`justify-content: flex-end`), also stehen Handle, Zahnrad, Strich und
   Abmelden als eine Gruppe am rechten Rand. Das alte Argument steht hier,
   weil es nicht falsch war - es ist überstimmt. */
.nav-revier-fuss {
  display: flex;
  align-items: center;
  /* Der freie Platz gehört an den Anfang der Zeile, damit die vier Sachen
     rechts zusammenstehen - und er liegt seit der Korrektur *im* Link
     (siehe `.nav-revier-konto`), sodass die leere Hälfte mitklickt. Die
     Regel hier bleibt trotzdem stehen: sie ist es, die den Strich und das
     Abmelden rechts hält, wenn der Link einmal nicht wachsen sollte. */
  justify-content: flex-end;
  gap: 6px;
  box-sizing: border-box;
  width: 100%;
  margin-top: 8px;
  padding-top: 6px;
  border-top: 1px solid var(--border);
}

.nav-revier-konto {
  /* **Die ganze Zeile ist der Link, der Text steht rechts darin.** Das
     war zwei Runden lang anders: erst füllte der Link die Zeile *und*
     schrieb links, dann schrumpfte er auf seinen Text und die linke
     Hälfte war tot. Beides war halb richtig - gewünscht ist rechts
     ausgerichtet, und ein Menüeintrag, dessen halbe Zeile nichts tut,
     ist ein Ziel, das man zweimal treffen muss.

     Also wieder `1 1 auto`, aber mit `justify-content: flex-end`
     darunter: der freie Platz liegt *im* Link, vor dem Handle. Damit
     deckt der Hover genau das ab, was klickbar ist - was die Regel ist,
     nach der dieses Popup überall sonst auch gebaut ist. Schrumpfen darf
     er weiterhin, sonst schöbe ein langer Handle den Strich und das
     Abmelden aus dem Popup; `min-width: 0` darunter lässt dann das
     `text-overflow` am Handle greifen. */
  flex: 1 1 auto;
  justify-content: flex-end;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  box-sizing: border-box;
  padding: 8px 10px;
  border-radius: 6px;
  color: var(--muted-fg);
  font-size: 13px;
  text-decoration: none;
}

.nav-revier-handle {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nav-revier-fuss-icon { flex: 0 0 auto; display: flex; align-items: center; }
.nav-revier-konto:hover { background: var(--img-placeholder); color: var(--fg); }

/* On the settings page itself. The rail can no longer say which page you
   are on for this one, so the row does. */
.nav-revier-konto[aria-current] {
  color: var(--bar-color);
  font-weight: 600;
}

.nav-revier-trenner {
  flex: 0 0 auto;
  width: 1px;
  height: 20px;
  background: var(--border);
}

/* **Abmelden, am Ende der Zeile.** Das einzige Symbol im Popup, das etwas
   beendet, deshalb zuletzt und in der warnenden Farbe - aber ohne Rahmen
   und ohne Grossbuchstaben: das Popup geht ständig auf, und was man jedes
   Mal sieht, warnt nach einer Woche nicht mehr.

   Dasselbe Rotpaar wie `.anmeldung-fehler` - das ist die einzige Stelle,
   an der dieses Stylesheet schon eine warnende Textfarbe für hell und
   dunkel festgelegt hat, und zwei Rots für eine Bedeutung wären zwei, die
   irgendwann auseinanderlaufen. */
.nav-revier-abmelden {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: #b3261e;
  cursor: pointer;
}

body.dark .nav-revier-abmelden { color: #f2b8b5; }

.nav-revier-abmelden:hover { background: var(--img-placeholder); }

@media (max-width: 700px) {
  /* The bar is horizontal down here and 56px tall, with three items
     across it. The switcher is the fourth and takes the **right-hand
     end** rather than the left, of the same shape as the items rather
     than a wide chip. (It carries no name in either layout now, so there
     is nothing left to hide at this width - the rule that did is gone.) */
  .nav-revier {
    width: auto;
    /* Hinter die drei statt vor sie: `1` und nicht `0`, obwohl der Knopf
       im DOM ohnehin der letzte ist (nav.js hängt ihn nach dem Fetch an).
       Bei `0` entschiede wieder die DOM-Reihenfolge, und dann hinge das
       rechte Ende der Leiste daran, dass `renderNav()` zuerst läuft; mit
       `1` gegen die `0` der `.nav-item` steht er rechts, egal wer wann
       fertig wird - dieselbe Zusicherung, die das `-1` oben gibt. */
    order: 1;
    margin-top: 0;
    margin-bottom: 0;
    padding: 10px 6px;
    border-top: none;
    /* The rule is on the side the items are on, which down here is the
       left of it. */
    border-left: 1px solid var(--border);
  }

  /* Still the full width of the screen, and measured up from the bar
     rather than down from the top: down here the bar really is along the
     foot, and a menu of names pinned to the right-hand corner of a phone
     is a column three words wide. `left` and `right` together already say
     how wide it is, so the desk's max-width - which is measured off a
     68px rail that does not exist at this width - has to go, or it would
     take another 70px out of a 390px screen. */
  .nav-revier-popup {
    left: 6px;
    right: 6px;
    top: auto;
    bottom: 62px;
    max-height: min(70vh, calc(100vh - 80px));
    max-width: none;
    min-width: 0;
  }
}


/* ---------------------------------------------------------------
   Eine Gesellschaft (Etappe 7, usermanagement-design.md §7 / §9).
   --------------------------------------------------------------- */

/* Which archive a tile came out of. Only ever drawn in a Gesellschaft - in
   one's own Revier the server sends null, because a label repeated on
   sixty tiles that are all the same answer is noise.

   Bottom left, which is the one corner of a tile nothing else wants: the
   place pin is bottom right, the tick top right, the tag strip across the
   foot of the picture. Small and quiet - it has to be readable before a
   thumb lands on the tile, not before the photograph is. */
/* **Das Zeichen „wird geteilt"** (25.08.2026, auf Wunsch). Oben rechts,
   in derselben Ecke wie der Revier-Name - und wenn beide da sind, rückt
   der Name nach links (siehe `.tile.geteilt .tile-revier`). Zwei Marken
   in einer Ecke sind zwei, die man zusammen liest; getrennte Ecken wären
   zwei Stellen, an denen man nachsehen muss.

   In der Farbe, die diese Anwendung für „das ist an" hat, damit es nicht
   für eine Verzierung gehalten wird. Klein: es ist eine Auskunft über die
   Zeile und nicht über das Bild, und der Platz gehört dem Bild. */
/* **Marke und Griff in einem** (09.09.2026, auf Wunsch). Sie war eine
   reine Auskunft und nicht anzufassen - eine Marke, die die Frage stellt,
   die sie nicht beantwortet: *wohin denn?* Jetzt macht ein Druck das
   Fenster auf, in dem es steht.

   `.aktiv` ist der alte Zustand: gefüllte Scheibe in der Farbe, die hier
   überall „das ist an" heisst. Ohne ihn ist sie ein stiller Umriss auf
   dunklem Grund - dasselbe Paar wie am Herzen und an der Kette im
   Leuchtkasten. */
.tile-geteilt {
  position: absolute;
  right: 4px;
  top: 4px;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: rgba(20, 20, 20, 0.55);
  color: #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,0.6);
  cursor: pointer;
  /* Ungeteilt ist sie fort und kommt beim Darüberfahren - siehe die
     Abfrage unten. Geteilt steht sie immer da. */
  opacity: 0;
  transition: opacity 0.12s ease;
}

.tile-geteilt.aktiv {
  background: var(--bar-color);
  opacity: 1;
}

.tile-geteilt:hover { filter: brightness(1.2); }

/* **Nur mit einem Zeiger erscheint sie beim Darüberfahren.** Sechzig
   Ketten, die dauerhaft auf sechzig Kacheln stehen, wären dieselbe Reihe
   aus gleich aussehenden Zeichen, gegen die die Ortsnadel schon einmal
   von der Kachel verschwunden ist.

   **Ohne Zeiger gibt es sie ungeteilt gar nicht** (unten): ein Griff, der
   nur beim Darüberfahren erscheint, ist auf einem Telefon keiner, und
   dort führt der Weg über den Leuchtkasten oder die Mehrfachauswahl. Die
   *Marke* bleibt davon unberührt - `.aktiv` steht auf beiden. */
@media (hover: hover) {
  .tile:hover .tile-geteilt,
  .tile-geteilt:focus-visible { opacity: 1; }
}

@media (hover: none) {
  .tile-geteilt:not(.aktiv) { display: none; }
}

/* ---------------------------------------------------------------
   **Noch nicht angesehen** (13.09.2026, auf Wunsch: *„Anblicke shall have
   read Status per user indicated as an orange dot in the top left corner
   of gallery card"*).

   Dieselbe Orange wie die Marke in der Leiste (`.nav-ungelesen`) und wie
   alles andere in dieser Anwendung, das *neu* oder *rückgängig* heisst -
   wer sie dort gelernt hat, liest sie hier ohne Erklärung.

   **Oben links, die eine freie Ecke.** Oben rechts stehen Herz, Kette und
   der Name des fremden Reviers, unten die Antworten. Ein Ring in der
   Farbe des Kartenhintergrunds, damit er auch auf einer hellen Stelle
   einer Fotografie ein Punkt bleibt und nicht ein Fleck.

   Er fängt keinen Zeiger: die Kachel darunter macht den Leuchtkasten auf,
   und genau das nimmt ihn weg.
   --------------------------------------------------------------- */
.tile-ungelesen {
  position: absolute;
  left: 6px;
  top: 6px;
  z-index: 3;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #d98324;
  box-shadow: 0 0 0 2px var(--card-bg), 0 1px 3px rgba(0, 0, 0, 0.6);
  pointer-events: none;
}

/* **Derselbe Punkt, in der Filterzeile** (19.09.2026) - eine Seite und
   ihr Zeichen sollen zusammengehören, und die Zeile *Ungelesen* sagt
   damit ohne ein zweites Wort, welche Kacheln sie meint.

   In der Zeile steht er im Fluss statt über einer Kachel, also fallen
   `position` und der Ring weg, der ihn dort vom Bild abhebt: hier steht
   er auf der Fläche des Panels und braucht nichts, wovon er sich abheben
   müsste. Dieselbe Stelle und dieselbe Bauart wie `.favorit-herz`. */
/* **Derselbe Punkt, als Legende** (19.09.2026). Die Filterzeile
   *Ungelesen* traegt ihn, damit man sieht, wovon sie spricht - es ist
   dasselbe Objekt wie an der Kachel, und das ist der Sinn.

   `im-filter` ist der Zusatz, an dem man die Legende von einer Kachel
   unterscheidet, und er ist nicht schmueckend: wer `.tile-ungelesen`
   zaehlt, um zu wissen, wieviele Kacheln ungelesen sind, zaehlt sonst
   die Legende mit. Genau das ist einem Prueflauf passiert
   (zz_gelesen.js), und die Antwort dort heisst `:not(.im-filter)`. */
.tile-ungelesen.im-filter {
  position: static;
  width: 9px;
  height: 9px;
  box-shadow: none;
  flex: none;
}

/* Im Auswahlmodus sitzt dort das Kästchen (20px ab 6px), also weicht der
   Punkt nach rechts aus. Er bleibt stehen und verschwindet nicht: ob eine
   Kachel neu ist, gilt auch, während man Kacheln anhakt. */
.tile.selectable .tile-ungelesen { left: 32px; }

/* Der Name des fremden Reviers weicht der Kette aus, statt darunter zu
   rutschen: die Ecke ist eine Zeile, und zwei Zeilen übereinander wären
   ein Block auf dem Bild. */
.tile.geteilt .tile-revier {
  right: 28px;
  max-width: calc(100% - 58px);
}

.tile-revier {
  position: absolute;
  /* **Top right.** It was bottom left, under the answers along the foot
     of the tile and beside the text on a written one; up here it is on
     the picture, in the corner the position pin used to hold, and clear
     of the tick box opposite it. */
  right: 4px;
  top: 4px;
  z-index: 1;
  max-width: calc(100% - 34px);
  padding: 1px 6px;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 10px;
  line-height: 1.5;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  pointer-events: none;
}

/* And the tile itself carries a thin edge in the band's colour, so that
   a screen of foreign tiles reads as foreign at a glance rather than
   sixty labels at a time. */
.tile.fremd { box-shadow: inset 0 0 0 2px var(--bar-color); }

/* Die Gesellschaften im Wechsler standen einmal unter einer eigenen
   Überschrift, weil sie eine andere Art Ding sind - ein Fenster in fremde
   Archive statt eines eigenen. Die Überschrift ist weg: sie hängen jetzt
   unter dem Revier, das in sie hineingibt (`.nav-revier-option` oben, und
   docs/reviere.md), und ein Baum sagt die Zugehörigkeit, ohne dass eine
   Zeile Grossbuchstaben sie behaupten muss. Was bleibt, ist die etwas
   stillere Schrift: eine Gesellschaft ist ein Ort, aber keiner, den man
   führt. */
.nav-revier-option.gesellschaft .nav-revier-name { font-size: 13px; }

/* One share, as a block: the Gesellschaft's name, four categories, the
   "nur angesprochene" switch and the two buttons. A block rather than a
   row, because unlike a member or an invitation this is a decision with
   several parts that are saved together - which is §8's whole point
   about a share being one sentence rather than fifty permissions. */
.freigabe-block {
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
}

.freigabe-block .settings-row {
  gap: 10px;
  padding: 4px 0;
  font-size: 14px;
}

.freigabe-block .settings-add { margin-top: 8px; }

/* **Aus welchem Revier** (09.09.2026), über den vier Kategorien. Ein
   Strich darunter, weil es keine der Einstellungen ist, sondern der Kopf,
   der sagt, worüber sie reden. */
.freigabe-revierwahl {
  gap: 8px;
  margin-bottom: 12px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
}

.freigabe-revierwahl .teilen-wahl {
  width: auto;
  max-width: 60%;
  margin: 0;
}

.freigabe-revier-name { font-weight: bold; }

/* **Die Arten unter ihrer Kategorie** (09.09.2026). Eingerückt auf die
   Höhe des Kategorietextes, damit sichtbar ist, wozu sie gehören, und in
   einer umbrechenden Reihe statt in eigenen Zeilen: es sind bis zu fünf
   kurze Wörter, und fünf volle Zeilen je Kategorie machten aus einem
   Block von vier Zeilen einen von vierzehn - genau die Liste aus gleich
   aussehenden Zeilen, gegen die dieser Block gebaut ist.

   Die 26px sind das Kästchen der Kategorie plus die Lücke dahinter
   (`.freigabe-block .settings-row` hat `gap: 10px`), also fluchtet die
   Reihe mit dem Wort darüber und nicht mit dem Häkchen. */
.freigabe-arten {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  margin: -2px 0 8px 26px;
}

.freigabe-art {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--muted-fg);
  cursor: pointer;
}

/* Ohne ihre Kategorie geht keine Art. Die Kästchen bleiben stehen und
   behalten, was eingestellt ist - sie sind nur nicht zu bedienen, und
   das muss man sehen können. */
.freigabe-arten.aus { opacity: 0.45; }
.freigabe-arten.aus .freigabe-art { cursor: default; }

/* ---------------------------------------------------------------
   Die geteilte Ansicht (Etappe 8, usermanagement-design.md §11).

   Its own page and its own few rules: no nav bar to clear, no band at
   the foot, nothing that can be pressed except a picture. It shares the
   stylesheet for the palette and the dark mode, and nothing else.
   --------------------------------------------------------------- */

body.share-page {
  padding: 0;
  background: var(--bg);
  color: var(--fg);
}

.share-kopf {
  padding: 18px 20px 10px;
  border-bottom: 1px solid var(--border);
}

.share-titel { font-size: 20px; font-weight: 600; }
.share-quelle { font-size: 13px; color: var(--muted-fg); margin-top: 2px; }

.share-status {
  padding: 16px 20px;
  color: var(--muted-fg);
  font-size: 14px;
}

/* A wall of photographs, and the only interactive thing on the page.
   `auto-fill` rather than a column count: this is read on a phone as
   often as on a desk, and the tiles should simply take what there is. */
.share-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 10px;
  padding: 14px 20px 40px;
}

.share-kachel {
  margin: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  background: var(--card-bg);
  cursor: pointer;
}

.share-kachel img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--img-placeholder);
}

/* A text Anblick has no picture and is the text - the same decision the
   gallery's tile makes, so the two look like the same archive. */
.share-kachel-text {
  padding: 14px;
  min-height: 90px;
  font-size: 14px;
  font-style: italic;
  color: var(--muted-fg);
}

.share-kachel figcaption {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 7px 9px 9px;
  font-size: 12px;
  color: var(--muted-fg);
}

.share-tags { color: var(--fg); }
.share-ort { font-size: 11px; }

/* The picture, big. There is nothing else on this page to be over. */
.share-viewer {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.94);
}

.share-viewer[hidden] { display: none; }

.share-viewer img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.share-zu,
.share-pfeil {
  position: absolute;
  border: none;
  background: rgba(0, 0, 0, 0.4);
  color: #fff;
  cursor: pointer;
  line-height: 1;
}

.share-zu {
  top: 12px;
  right: 14px;
  width: 40px;
  height: 40px;
  border-radius: 20px;
  font-size: 26px;
}

.share-pfeil {
  top: 50%;
  transform: translateY(-50%);
  width: 46px;
  height: 74px;
  border-radius: 8px;
  font-size: 34px;
}

.share-pfeil.links { left: 10px; }
.share-pfeil.rechts { right: 10px; }

.share-unterschrift {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 12px 16px;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.75));
  color: #eee;
  font-size: 13px;
  text-align: center;
  pointer-events: none;
}

.share-fuss {
  padding: 18px 20px 34px;
  border-top: 1px solid var(--border);
  color: var(--muted-fg);
  font-size: 12px;
}

/* The link field in the share dialogs: a URL is long, and the one time
   it is ever shown is the moment somebody has to get it out of here. */
.share-feld {
  width: 100%;
  margin: 10px 0;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--control-bg);
  color: var(--fg);
  font-size: 13px;
}

/* ---- The live markdown field ----

   The line under the caret is a textarea; every other line is what it
   will be. Both have to sit on the same left edge and the same line
   height, or the field jumps as the caret moves - which is the one thing
   that would make this unusable. */
.md-editor {
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--control-bg);
  color: var(--fg);
  padding: 8px 10px;
  font-size: 14px;
  line-height: 1.5;
  cursor: text;
}

.md-editor:focus-within { border-color: var(--bar-color); }

.md-line, .md-line-edit {
  font-size: 14px;
  line-height: 1.5;
  margin: 0;
  padding: 0;
}

.md-line-edit {
  display: block;
  width: 100%;
  box-sizing: border-box;
  border: none;
  outline: none;
  resize: none;
  overflow: hidden;
  background: transparent;
  color: var(--fg);
  font-family: inherit;
}

/* The rendered lines carry the same shapes the lightbox card does, at the
   size of the field rather than of a page. */
.md-editor h1 { font-size: 1.25em; margin: 0.2em 0; }
.md-editor h2 { font-size: 1.15em; margin: 0.2em 0; }
.md-editor h3 { font-size: 1.05em; margin: 0.2em 0; }
.md-editor p { margin: 0; }
.md-editor a { color: var(--bar-color); }
.md-editor code {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 0.92em;
  padding: 1px 4px;
  border-radius: 3px;
  background: rgba(127, 127, 127, 0.18);
}
.md-editor blockquote {
  margin: 0;
  padding-left: 8px;
  border-left: 3px solid var(--bar-color);
  opacity: 0.85;
}
.md-editor hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: 0.5em 0;
}

/* ---- An Anblick's marker, pressed ----

   The popup is the gallery's tile now (mapAnblickCard in app.js), so it
   is sized like one rather than like a caption: wide enough for the faces
   to be recognisable and no wider, because a popup is a thing standing on
   a map and the map is the reason you are here.

   `.gallery` inside it so the tile's own grid rules apply, with one
   column - the popup holds exactly one. */
.map-popup-tile { width: 220px; }

.map-popup-grid { --grid-columns: 1; }

.map-popup-tile .tile { cursor: pointer; }

/* While the row is on its way. The time is what the marker already knew. */
.map-popup-warten {
  padding: 26px 8px;
  text-align: center;
  color: var(--muted-fg);
  font-size: 12px;
}

/* **Was die drei Ränge dürfen**, auf der Revierseite unter „Mitglieder"
   (rollen-design.md §3). Eine Tabelle und keine Liste aus drei Blöcken,
   weil die Frage, die hier gestellt wird, ein Vergleich ist: *darf der
   Gast das auch?* Drei Blöcke beantworten sie mit Blättern.

   `table-layout: fixed` plus feste Breiten für die drei Rangspalten:
   sonst misst der Browser die längste Beschriftung aus und die Spalten
   wandern von Zeile zu Zeile. Die erste Spalte bekommt, was übrig ist,
   und bricht um. */
.rang-tabelle {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
  margin: 4px 0 18px;
  font-size: 14px;
}

.rang-tabelle th,
.rang-tabelle td {
  border-bottom: 1px solid var(--border);
  padding: 7px 4px;
  vertical-align: top;
}

/* Die Kopfzeile: die drei Rangnamen. Klein und gesperrt wie die anderen
   Zwischenüberschriften dieses Stylesheets, damit sie nicht schwerer
   wiegt als die Zeilen darunter. */
.rang-tabelle thead th {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted-fg);
  text-align: center;
  border-bottom-width: 2px;
}

.rang-tabelle thead th:first-child { text-align: left; }

/* Die drei schmalen Spalten. 62px trägt „Pächter" bei 14px Arial ohne
   Umbruch; darunter bricht es, und ein umbrochener Spaltenkopf über
   einem Häkchen sieht aus wie ein Fehler. */
.rang-tabelle col,
.rang-tabelle th:not(:first-child),
.rang-tabelle td { width: 62px; }

.rang-tabelle th:first-child,
.rang-tabelle tbody th[scope="row"] {
  width: auto;
  text-align: left;
  font-weight: normal;
  color: var(--fg);
}

.rang-tabelle td { text-align: center; }

/* Ja und Nein. Das Häkchen in der Leistenfarbe, der Strich im
   Gedämpften: der Unterschied soll sich beim Überfliegen einer Spalte
   zeigen, ohne dass eine Zeile rot wird - „darf nicht" ist hier kein
   Fehler, sondern die halbe Auskunft. */
.rang-tabelle td.ja { color: var(--bar-color); font-weight: 600; }
.rang-tabelle td.nein { color: var(--muted-fg); }

/* Die Zwischenüberschrift innerhalb der Tabelle - dieselbe Sprache wie
   `.papierkorb-ueberschrift` und `.freigabe-ueberschrift`, nur als
   Tabellenzeile, weil sie hier eine ist. */
.rang-tabelle .rang-gruppe th {
  padding: 16px 4px 4px;
  border-bottom: 0;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted-fg);
  text-align: left;
}

@media (max-width: 700px) {
  /* Bei 390 px bleiben der ersten Spalte rund zwei Drittel. Die
     Rangspalten dürfen dafür enger werden, und die Köpfe brechen dann
     doch - deshalb hier kleiner statt schmaler allein. */
  .rang-tabelle { font-size: 13px; }
  .rang-tabelle thead th { font-size: 11px; }
  .rang-tabelle th:not(:first-child),
  .rang-tabelle td { width: 52px; }
}


/* ---------------------------------------------------------------
   Das Teilen-Fenster (25.08.2026)

   Drei Teile, von oben nach unten: an wen, was dazugehört, und was schon
   läuft. Es braucht wenig Eigenes - die Zeilen darin sind dieselben
   `mitglied-zeile`n wie auf jeder Einstellungsseite, was Absicht ist:
   „wer sieht das" sieht überall gleich aus.
   --------------------------------------------------------------- */

/* **Der Kopf des Teilen-Fensters** (09.09.2026, auf Wunsch): Titel links,
   Ausgang rechts. Der Ausgang stand vorher unten als *Fertig* neben
   *Teilen* - zwei gleich aussehende Knöpfe, von denen einer etwas tut und
   einer nur zumacht. Oben rechts ist in dieser Anwendung die Stelle jedes
   Ausgangs (Leuchtkasten, Elementfenster), und dort steht er jetzt auch
   hier. */
.teilen-kopf {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}

.teilen-kopf h2 { flex: 1; margin: 0; }

.teilen-zu {
  flex: none;
  width: 28px;
  height: 28px;
  margin: -4px -4px 0 0;
  padding: 0;
  font-size: 22px;
  line-height: 1;
  color: var(--muted-fg);
  background: transparent;
  border: none;
  cursor: pointer;
}

.teilen-zu:hover { color: var(--fg); }

/* **Der Knopf gehört zu dem, was darüber steht** - nicht unter die Liste
   dessen, was schon läuft. Oben wird eingestellt, *was* hinausgehen soll;
   dieser Knopf tut es, und der Strich darunter trennt das Einstellen vom
   Bestand. Rechtsbündig, wo eine Handlung in dieser Anwendung steht. */
.teilen-tun {
  display: flex;
  justify-content: flex-end;
  margin-top: 10px;
}

.teilen-wahl {
  width: 100%;
  margin: 10px 0 4px;
}

/* Der Teil, der zur Wahl gehört, wird beim Umschalten neu gezeichnet.
   Ein Mindestmaß, damit das Fenster beim Wechsel zwischen Gesellschaft
   (ein Satz) und Link (Feld, Häkchen, zwei Sätze) nicht springt. */
.teilen-feld {
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* Ein Mindestmaß, damit das Fenster beim Wechsel zwischen Gesellschaft
     (ein Satz) und Link (Feld, Häkchen, zwei Sätze) nicht unter dem
     Zeiger springt — aber nur so viel, wie der kürzere Fall braucht. */
  min-height: 84px;
}

/* `.share-feld` steht mit `width: 100%` in einem Fenster, das ihm sein
   Maß über `padding` gibt — in einem Flex-Kasten schießt es damit rechts
   über den Rand hinaus. Ein Kasten, dessen Rahmen und Polster
   *innerhalb* seiner Breite liegen, ist die ganze Antwort darauf. */
.teilen-feld .share-feld {
  box-sizing: border-box;
  margin: 6px 0;
}

/* **Das Klappfeld „wer davon"** (09.09.2026), in beiden Teilen-Fenstern.
   Es steht in einer `.settings-row` wie das Häkchen darüber, also erbt es
   die Reihe; eigen ist ihm nur, dass es nicht die volle Breite nimmt -
   drei Wörter brauchen keine, und daneben steht der Satz, der den Preis
   nennt. */
.teilen-wem { flex: 0 0 auto; font-size: 13px; }

/* **Der Satz darunter, nicht daneben.** Die Reihe ist eine
   `.settings-row` wie die Häkchen darüber, und dort steht der Hinweis
   rechts vom Wort - hier passt er nicht: Klappfeld plus *Sehen dürfen es*
   plus ein ganzer Satz sind drei Dinge auf einer Zeile, und das mittlere
   wurde auf drei Zeilen gequetscht („Sehen / dürfen / es"). Gesehen, nicht
   vermutet.

   Also umbricht die Reihe, und der Hinweis nimmt die volle Breite unter
   den zwei Griffen - dieselbe Form, die die Einstellungsseite für ihre
   Rangzeilen hat, nur enger. */
.settings-row:has(> .teilen-wem) { flex-wrap: wrap; }
.settings-row > .teilen-wem + span { white-space: nowrap; }
.settings-row > .teilen-wem ~ .mitglied-seit {
  flex: 1 0 100%;
  white-space: normal;
  margin-top: 2px;
}

.teilen-laeuft:not(:empty) {
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px solid var(--border-color, rgba(128,128,128,0.35));
}

/* Ein Zurückziehen ist keine Zerstörung im Sinne des Papierkorbs - es ist
   die Rücknahme einer Entscheidung -, also der ruhigere Knopf: die Farbe
   trägt der Text, nicht die Fläche. */
.teilen-laeuft .delete-button {
  padding: 2px 8px;
  font-size: 12px;
}

/* **Das Zahnrad ans Ende der Zeile** (09.09.2026, auf Wunsch): von hier
   zu dem, was die Reviere dieser Runde als *ganzes* hergeben. Ein Link
   und kein Knopf - es führt woandershin, und das soll man sehen, bevor
   man drückt.

   Ganz aussen, hinter *Nicht mehr*: das Zurücknehmen gehört zu dieser
   Zeile, das Zahnrad führt von ihr weg. `margin-left: auto` sitzt
   deshalb hier und nicht mehr am Knopf davor - sonst schöbe der die
   beiden auseinander. */
.teilen-zahnrad {
  flex: none;
  display: inline-flex;
  align-items: center;
  margin-left: auto;
  padding: 2px;
  color: var(--muted-fg);
  opacity: 0.75;
}

.teilen-laeuft .delete-button + .teilen-zahnrad { margin-left: 6px; }

.teilen-zahnrad:hover { color: var(--fg); opacity: 1; }


/* ---------------------------------------------------------------
   Wohin es hinausgeht — der fünfte Abschnitt im Panel *Medien*
   (25.08.2026)

   Ein Strich trennt ihn von den vier Zeilen darüber: es sind zwei
   Fragen ("was ist das" / "was ist damit geschehen"), nicht fünf
   gleichrangige Zeilen. Derselbe Strich, mit dem im Anblick-Panel
   „Ohne Ansprache" abgesetzt ist.
   --------------------------------------------------------------- */

.geteilt-liste:not([hidden]) {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--border);
}

/* Die Kinder eingerückt, damit der Ast als Ast zu lesen ist — der
   Tag-Baum macht das mit `padding-left` je Tiefe, und hier gibt es genau
   eine. */
.geteilt-liste .tree-row.tief { padding-left: 16px; }

/* ---------------------------------------------------------------
   Favoriten (05.09.2026)

   Ein Herz an vier Stellen, und überall dieselbe Aussage: gefüllt und
   rot heisst gemerkt, ein weisser Umriss ist die Einladung. Die Form
   kommt aus `herzSymbol` in nav.js, die Farbe von hier - ein Herz auf
   einer Fotografie braucht einen Schatten, eines im Filterpanel nicht.
   --------------------------------------------------------------- */

:root { --herz: #e5484d; }

/* An der Kachel, oben rechts. Nur sichtbar, wenn gemerkt - im
   Auswahlmodus an jeder, damit man es dort setzen kann. */
.tile-herz {
  position: absolute;
  right: 4px;
  top: 4px;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  color: #fff;
  cursor: pointer;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.6));
}

.tile-herz.an { color: var(--herz); }
.tile-herz:hover { transform: scale(1.12); }

/* **Mit der Maus zeigt es sich beim Darüberfahren** (05.09.2026, auf
   Wunsch). An den gemerkten steht es immer - das ist die Auskunft, um
   die es geht -, und im Auswahlmodus an allen, weil man es dort setzt.
   An allen anderen kommt es unter den Zeiger und geht wieder. */
@media (hover: hover) and (pointer: fine) {
  .tile-herz { opacity: 0; transition: opacity 0.12s ease; }
  .tile:hover .tile-herz,
  .tile.gemerkt .tile-herz,
  .tile.selectable .tile-herz { opacity: 1; }
}

/* **Auf dem Telefon ist es eine Marke und kein Knopf** (05.09.2026, auf
   Wunsch: *„on mobile clicking the heart shall not unfavorite it from
   that screen/mode"*).

   Es gibt dort kein Darüberfahren, also stünde es entweder immer da oder
   nie; es steht an den gemerkten. Und ein Druck darauf gilt der Kachel,
   nicht ihm: das Herz sitzt in der Ecke einer Fotografie, die man
   antippt, um sie anzusehen, und ein Daumen, der 4px danebenliegt, hätte
   sonst eine Ansprache zurückgenommen, die man nicht wiederfindet.

   **Im Auswahlmodus schon**: dort ist die Kachel ohnehin ein Formular,
   das Herz trägt jede, und es zu drücken ist die einzige Art, es dort
   zu setzen. */
@media (hover: none) {
  .tile-herz { display: none; pointer-events: none; }
  .tile.gemerkt .tile-herz,
  .tile.selectable .tile-herz { display: flex; }
  .tile.selectable .tile-herz { pointer-events: auto; }
}

/* Die Kette rückt zur Seite, wenn ein Herz danebensteht. Beide sind
   Marken derselben Ecke, und die, nach der gerade gefragt wurde, nimmt
   den Platz. */
.tile.hat-herz .tile-geteilt { right: 30px; }

/* Im Leuchtkasten, links neben dem `×`. Dieselbe Grösse und dieselbe
   Ruhe wie die Knöpfe der Spalte darunter.

   **Und die Kette teilt sie sich mit ihm** (09.09.2026, seit sie links
   neben dem Herzen steht). Sie hatte eine 44px-Scheibe von der Zeit, in
   der sie zwischen Stift und Korb sass; in dieser Reihe wäre die eine
   Scheibe neben zwei blossen Zeichen. Eine Regel für beide, weil es
   dieselbe Sorte Marke ist: ein Zeichen, das eine Frage über den Anblick
   beantwortet und sie mit einem Druck auch umdreht. */
.lightbox-favorit,
.lightbox-share {
  background: none;
  border: 0;
  padding: 0;
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0.85;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.7));
}

.lightbox-favorit:hover,
.lightbox-share:hover { opacity: 1; }

/* **Was „an" heisst, sagt jede der zwei in ihrer eigenen Sprache** - und
   das ist keine Uneinheitlichkeit, sondern der Unterschied zwischen einem
   Umriss und einer Kette.

   Ein Herz lässt sich *füllen*, und gefüllt in `--herz` ist auf jedem
   Bild zu lesen. Eine Kette hat keine Fläche zum Füllen, also bliebe nur,
   ihre Linien einzufärben - und das war der erste Versuch am 09.09.2026:
   sie in `--bar-color` zu setzen, die Farbe, die in dieser Anwendung „das
   ist an" heisst. Auf dem Schirm wurde sie dadurch *leiser* als im
   Ruhezustand, denn dieses Grün ist überall sonst ein **Grund** mit Weiss
   darauf (das Zeichen an der Kachel, der eingeschaltete Stift) und nie
   eine Strichfarbe. Ein Zeichen, das beim Einschalten zurücktritt, sagt
   das Gegenteil dessen, was es soll.
   
   Also dieselbe Farbe in derselben Rolle: als Scheibe darunter, Weiss
   darauf - genau das Zeichen der Kachel, nur an dieser Stelle. Der Platz
   dafür steht immer, damit die Reihe beim Umschalten nicht springt; sie
   ist nur unsichtbar, solange nichts hinausgeht. */
.lightbox-favorit.an { color: var(--herz); opacity: 1; }

/* `display: flex` schlägt das `[hidden]` des Browsers - dieselbe Falle
   wie bei `.lightbox-anlegen-upload`. Seit dem 09.09.2026 werden beide
   Knöpfe wirklich versteckt (Kette: nur wo geteilt werden darf, Stift:
   nie in einer Gesellschaft), also muss die Zeile hier stehen. */
.lightbox-share[hidden],
.lightbox-edit[hidden] { display: none; }

.lightbox-share {
  width: 28px;
  height: 28px;
  border-radius: 50%;
}

.lightbox-share.aktiv {
  background: var(--bar-color);
  opacity: 1;
}

/* Die oberste Zeile der Werkzeugspalte: die Kette und das Herz links,
   der Ausgang rechts. Die Spalte selbst laeuft senkrecht weiter (Stift,
   Korb). Auf der dritten Anzeigestufe geht sie mit der ganzen Spalte
   (`.lightbox.chrome-hidden .lightbox-tools`). */
.lightbox-tools-oben {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Die Scheibe der Mehrfachauswahl. Gefüllt heisst: alle sind gemerkt. */
#selectionFavorit { color: #fff; }
#selectionFavorit.an { color: var(--herz); }

/* Die Zeile im Filterpanel: das Herz steht im Etikett, nicht davor. */
.favorit-zeile .favorit-herz {
  display: inline-flex;
  align-items: center;
  margin-left: 6px;
  color: var(--herz);
}

/* Ein Strich darüber - die vier Arten sagen, woraus ein Anblick
   besteht, das Herz sagt etwas über ihn. */
.favorit-zeile { border-top: 1px solid var(--line, rgba(0, 0, 0, 0.12)); margin-top: 4px; padding-top: 4px; }

/* **Die zwei Luecken-Zeilen** (04.09.2026), unter den Favoriten und
   wieder durch einen Strich abgesetzt: das Panel *Medien* traegt jetzt
   vier Gruppen - woraus ein Anblick besteht, ob jemand ihn gemerkt hat,
   wohin er hinausgeht, und was an ihm fehlt. Ein Strich je Wechsel,
   damit die Liste als vier Fragen zu lesen ist und nicht als eine lange.

   Nur die erste der beiden bekommt ihn: sie stehen zusammen. */
.fehlt-zeile:first-of-type {
  border-top: 1px solid var(--line, rgba(0, 0, 0, 0.12));
  margin-top: 4px;
  padding-top: 4px;
}

/* ---------------------------------------------------------------
   Die Einfuhr: Zeitpunkt und zugeklappte Ordner (05.09.2026)
   --------------------------------------------------------------- */

/* Oben links, vor dem Titel und leiser als er: die Auskunft ist wichtig
   beim Wiederaufmachen und sonst Beiwerk. */
.import-zeit {
  font-size: 12px;
  color: var(--muted-fg);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Eine Überschrift, die sich anfassen lässt, sagt das auch. Nur ab zwei
   Ordnern - `buildImportGroupHead` setzt die Klasse. */
.import-group-head.klappbar {
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  border-radius: 6px;
}

.import-group-head.klappbar:hover { background: rgba(0, 0, 0, 0.04); }

.import-klapp {
  display: inline-flex;
  width: 14px;
  justify-content: center;
  color: var(--muted-fg);
  font-size: 11px;
}

/* Zugeklappt bleibt die Zeile stehen, nur ruhiger: sie trägt weiter die
   Zahl und den Kameraplatz. */
.import-group-head.zu .import-folder { opacity: 0.8; }

/* **Die linke Ecke weicht der rechten** (05.09.2026) - siehe
   `messeAktionsEcke` in app.js. `visibility` und nicht `display`: der
   Kasten behält seinen Platz, sonst wäre er sofort wieder schmal genug
   und der Knopf blinkte im Takt der Messung. */
#galleryActionFabs.weicht-aus {
  visibility: hidden;
  pointer-events: none;
}

/* ---------------------------------------------------------------
   Die Knopfreihe des Elementfensters (05.09.2026)

   Vier gleich breite Knöpfe in der Farbe des Kopfes, durch weisse
   Striche getrennt - eine Leiste und keine Sammlung von Pillen. Die
   Farbe setzt `openInspector` am Kopf; die Reihe erbt sie, damit Kopf
   und Reihe ein Stück bleiben.
   --------------------------------------------------------------- */

.map-inspector-reihe {
  display: flex;
  /* Hintergrund und Schriftfarbe setzt `buildInspectorZeile` - sie
     gehören der Art des Elements, wie beim Kopf darüber.

     **Randlos, gegen das Polster des Rumpfes.** Die Reihe gehört optisch
     zum Kopf; ein weisser Rahmen ringsum machte aus einem Stück zwei.
     Die Werte sind die von `.map-inspector-body` - stehen sie einmal
     auseinander, sieht man es sofort.

     **Seit dem 05.09.2026 braucht es dafuer nichts mehr**: die Reihe
     steht ausserhalb des Rumpfes (`.map-inspector-fest`), und dort gibt
     es kein Polster, gegen das zu rechnen waere. Hier standen negative
     Raender in genau den Werten des Rumpfes. */
}

.map-inspector-reihe-knopf {
  flex: 1 1 0;
  min-width: 0;
  /* Mobilfreundlich: 48px ist das, was ein Daumen sicher trifft. Die
     alten Pillen waren 44px hoch und standen einzeln; hier sitzen vier
     nebeneinander, also zählt die Höhe doppelt. */
  min-height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: 0;
  /* Der weisse Strich zwischen ihnen, und keiner am Rand. */
  border-right: 1px solid rgba(255, 255, 255, 0.55);
  background: transparent;
  color: inherit;
  font-size: 14px;
  font-weight: bold;
  cursor: pointer;
  padding: 0 6px;
}

.map-inspector-reihe-knopf:last-child { border-right: 0; }
.map-inspector-reihe-knopf:hover { background: rgba(255, 255, 255, 0.16); }

/* Angeklappt: der Abschnitt darunter steht offen. Nur die zwei Knöpfe,
   die im Fenster bleiben, können das sein - die anderen führen fort. */
.map-inspector-reihe-knopf.an { background: rgba(255, 255, 255, 0.28); }

/* **Nichts zu zeigen heisst nicht: halb da** (05.09.2026, auf Wunsch:
   *„map selected element window buttons shall always be at full opacity
   even if count is 0"*).

   Hier stand `opacity: 0.55`, und das hiess *„hier ist nichts"*. Seit
   heute fuehrt der Knopf aber auch dann - auf die Seite, mit dieser
   Auswahl als Filter, und dort nichts zu finden ist eine Antwort. Ein
   Knopf, der etwas tut und dabei aussieht wie abgeschaltet, sagt das
   Gegenteil dessen, was er kann. Die Zahl daneben steht auf `0` und sagt
   es deutlich genug.

   Die Klasse bleibt, weil sie eine wahre Aussage ueber den Zustand ist -
   Prueflaeufe lesen sie, und der naechste, der hier etwas leise
   markieren will, findet den Haken vor. */
.map-inspector-reihe-knopf.leer { opacity: 1; }

.map-inspector-reihe-zahl {
  font-variant-numeric: tabular-nums;
  min-width: 1ch;
}

/* **Die orange Marke am Knopf** (19.09.2026, Etappe 6). Dieselbe Marke
   wie `.task-chip` an der Kachel und an der Ueberschrift im Elementregal
   - sie sagt ueberall dasselbe: *hier ist etwas offen*. Sie steht am
   Einrichtungsknopf, seit der an die Stelle des Aufgabenknopfes getreten
   ist: dessen Zahl waren die Aufgaben, jetzt sind es die Einrichtungen,
   und was an ihnen offen ist, sagt die Marke daneben.

   Nicht `.task-chip` selbst verwendet: die traegt ihren eigenen Rand und
   ihre eigene Groesse fuer eine Kachel: hier sitzt sie in einer 48px
   hohen Reihe neben einer Zahl, und 11px waeren dort zwei Schriftgroessen
   nebeneinander. */
.map-inspector-reihe-marke {
  flex: none;
  min-width: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: #d98324;
  color: #fff;
  font-size: 10px;
  font-weight: bold;
  line-height: 16px;
  text-align: center;
}

/* ---------------------------------------------------------------
   **Die zwei Ansichten des Fensters** (19.09.2026, Etappe 6).

   Beide sind Listen, in denen jede Zeile fortfuehrt - auf die
   Einrichtungsseite oder auf die Ausruestungsseite. Sie sehen deshalb
   gleich aus: es ist dieselbe Geste, und zwei Formen dafuer waeren zwei
   Dinge zu lernen.
   --------------------------------------------------------------- */
.map-weg-zeile,
.map-kit-zeile {
  width: 100%;
  box-sizing: border-box;
  border: 0;
  background: transparent;
  color: inherit;
  font-family: inherit;
  font-size: 13px;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.map-weg-zeile:hover,
.map-kit-zeile:hover { background: rgba(0, 0, 0, 0.08); }

/* Die Art hinter dem Namen: sie unterscheidet zwei Stuecke, die gleich
   heissen, und ist zugleich das, wonach die Liste sortiert. Leise, weil
   der Name die Antwort ist und die Art nur die Einordnung. */
.map-kit-art {
  flex: none;
  font-size: 11px;
  color: #888;
}

/* Der Kameraabschnitt und die Aufgabenansicht, unter der Reihe. */
.map-inspector-abschnitt {
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.map-kamera-wahl {
  width: 100%;
  min-height: 40px;
  font-size: 14px;
}

/* Der Kopf zeigt hin, also sagt der Zeiger das auch. */
.map-inspector-head { cursor: pointer; }
.map-inspector-head button,
.map-inspector-head select { cursor: pointer; }

/* ---------------------------------------------------------------
   Ein Bild auf ein Element werfen (05.09.2026)

   Zwei Ziele, ein Aussehen: das Elementfenster und die Zeile in der
   Schublade. Was aufleuchtet, sagt „hierhin" - und es leuchtet nur,
   wenn der Wurf wirklich Dateien traegt (`traegtDateien`).
   --------------------------------------------------------------- */

.bild-ziel {
  outline: 2px dashed var(--bar-color);
  outline-offset: -2px;
  background: rgba(93, 122, 74, 0.10);
}

/* Solange geschrieben wird: der Zeiger sagt, dass es laeuft, und ein
   zweiter Wurf soll nicht dazwischenfahren. */
.bild-laeuft {
  opacity: 0.7;
  pointer-events: none;
  cursor: progress;
}

/* Die Zahl des Ungelesenen am Revierbuch (05.09.2026) - dieselbe orange
   Marke wie an einer Kachel und auf der Karte. Am Zeichen und nicht
   neben dem Wort: auf dem Telefon steht das Wort unter dem Zeichen, und
   eine Marke daneben laege dann im Nichts. */
.nav-item { position: relative; }

/* **Die Marke sitzt auf dem Zeichen, nicht darüber** (04.09.2026,
   gemeldet: *„the button shall look the same as Anblicke/einrichtungen…
   it seems to be with more higher"*).

   Sie stand auf `top: 2px` und ragte damit über die Polsterkante des
   Eintrags hinaus (das Zeichen beginnt erst bei 10px). Der eine Eintrag
   mit Marke wurde dadurch optisch höher als seine vier Nachbarn, obwohl
   alle fünf dieselben 55px messen - gemessen war nie der Unterschied,
   gesehen schon. Auf der oberen rechten Ecke des Zeichens liegt sie
   innerhalb derselben Fläche wie jedes andere Zeichen der Leiste. */
.nav-ungelesen {
  position: absolute;
  top: 8px;
  right: 50%;
  /* **Neben dem Zeichen, nicht darauf.** Mit `right: 50%` endet die Marke
     an der Mitte des Eintrags; die Verschiebung setzt sie um genau ihre
     eigene Breite plus die halbe Zeichenbreite nach rechts, also frei
     daneben. Auf der Ecke sass sie vorher und schnitt die obere rechte
     Ecke des aufgeschlagenen Buches weg - das war es, was den Eintrag
     anders aussehen liess als seine vier Nachbarn.

     Mittenbezogen und nicht kantenbezogen, weil die Leiste auf dem
     Telefon eine Reihe am unteren Rand wird und die Eintraege dort viel
     breiter sind: an der Mitte haengt sie in beiden Anordnungen richtig. */
  transform: translateX(29px);
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  border-radius: 9px;
  background: #d98324;
  color: #fff;
  font-size: 11px;
  font-weight: bold;
  line-height: 17px;
  text-align: center;
  box-sizing: border-box;
}

/* ---------------------------------------------------------------
   Das Revierbuch (05.09.2026)

   Eine Reihe, neueste zuerst. Ein Vorgang und eine Nachricht stehen
   untereinander und sehen verschieden aus, ohne dass eines von beiden
   laut wird: der Vorgang ist die Chronik, die Nachricht ist jemand, der
   etwas sagt - die trägt darum den Grund und den Rand.
   --------------------------------------------------------------- */

.buch-seite {
  max-width: 780px;
  margin: 0 auto;
  padding: 16px 16px calc(96px + env(safe-area-inset-bottom, 0px));
}

.buch-kopf h1 { margin: 0 0 2px; font-size: 22px; }

.buch-kopf-zeile { font-size: 13px; color: var(--muted-fg); margin-bottom: 12px; }
.buch-kopf-zeile.neu { color: #d98324; font-weight: bold; }

/* Ein Datum je Tag, nicht je Zeile - siehe `zeichneBuch`. */
.buch-tag {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bg, #fff);
  padding: 10px 0 4px;
  font-size: 12px;
  font-weight: bold;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted-fg);
}

.buch-zeile {
  display: flex;
  gap: 10px;
  padding: 8px 8px 8px 6px;
  border-radius: 8px;
  align-items: flex-start;
}

/* Der Griff des langen Drucks: er darf nicht als Textauswahl enden. */
.buch-zeile { user-select: none; -webkit-user-select: none; }
.buch-zeile .buch-text { user-select: text; -webkit-user-select: text; }

.buch-zeile.nachricht {
  background: rgba(93, 122, 74, 0.07);
  border-left: 3px solid var(--bar-color);
}

/* ---------------------------------------------------------------
   **Der Kopf des Buches** (13.09.2026, auf Wunsch: *„Buch shall start
   with statistiks"*): vier Blöcke aus Chips über der ersten Zeile.

   Chips und keine Tabelle: es sind vier verschiedene Sorten Ding - ein
   Wort mit einer Zahl, eine Art mit ihrer Farbe, ein Mensch mit seinem
   Rang, eine Runde mit ihrem Namen -, und was sie gemeinsam haben, ist,
   dass man sie überfliegt. Eine Tabelle verspräche Spalten, die es nicht
   gibt.
   --------------------------------------------------------------- */
.buch-statistik {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0 0 18px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--card-bg);
}

.buch-statistik[hidden] { display: none; }

.buch-stat-titel {
  font-size: 11px;
  font-weight: bold;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted-fg);
  margin-bottom: 5px;
}

.buch-stat-reihe {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.buch-stat-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border-radius: 11px;
  border: 1px solid var(--border);
  font-size: 12px;
}

/* Die Farbe der Art, als Punkt davor - dieselbe, die das Element auf der
   Karte und im Regal trägt. Kein gefüllter Chip: neun gefärbte Flächen
   nebeneinander sind ein Farbkasten, kein Bestand. */
.buch-stat-punkt {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex: 0 0 auto;
}

.buch-stat-zahl { font-weight: bold; font-variant-numeric: tabular-nums; }

/* Der Rang hinter dem Namen: leiser als er, denn gesucht wird der Name. */
.buch-stat-rolle { color: var(--muted-fg); }

/* Was noch niemand angesprochen hat, ist das eine hier, das wartet -
   dieselbe Orange wie jede andere Marke in dieser Anwendung, die das
   sagt. */
.buch-stat-chip.offen { border-color: #d98324; color: #d98324; }

/* **Was zu tun ist, unter dem Bestand** (13.09.2026). Ein Kopf, den man
   drücken kann - die vier Blöcke darüber sind Chips, die man überfliegt,
   dies sind Sätze, die man liest, und darunter fängt das Buch an. Also
   zugeklappt, mit der Zahl als Auskunft. */
.buch-aufgaben { border-top: 1px solid var(--border); padding-top: 10px; }

.buch-aufgaben-kopf {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  margin: 0;
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  font-size: 11px;
  font-weight: bold;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted-fg);
  text-align: left;
  cursor: pointer;
}

/* Dasselbe Zeichen wie an den Gruppen des Elementregals und an den
   Kategorien des Filterblatts - eine Geste, ein Pfeil. */
.buch-aufgaben-kopf::before { content: "\25b8"; }
.buch-aufgaben-kopf[aria-expanded="true"]::before { content: "\25be"; }

.buch-aufgaben-liste { margin-top: 6px; }
.buch-aufgaben-liste[hidden] { display: none; }

/* Die Streifung, die diese Zeilen im Elementfenster haben und die dort an
   `.map-info-section` hängt, das es hier nicht gibt. */
.buch-aufgaben-liste .map-info-item:nth-child(even) {
  background: rgba(0, 0, 0, 0.045);
}

/* **Noch nicht gelesen** (13.09.2026, auf Wunsch: *„buch page rows which
   have not been read yet have an orange dot on ther left"*). Derselbe
   Punkt in derselben Orange wie an einer Kachel (`.tile-ungelesen`) und
   dieselbe Farbe wie die Marke in der Leiste - eine Aussage, eine Form.

   Ganz links, vor der Uhr, und mit derselben Höhe wie sie ausgerichtet:
   eine Spalte von Punkten am Rand ist etwas, das man überfliegt, und
   genau das soll man mit ihr tun. Sie nimmt keinen Platz, wo sie fehlt -
   die Zeile hat sie dann gar nicht -, deshalb rutschen gelesene Zeilen
   um ihre Breite nach links. Das ist gewollt: der Rand ist die Spalte
   des Neuen. */
.buch-punkt {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  margin-top: 6px;
  border-radius: 50%;
  background: #d98324;
}

.buch-uhr {
  flex: 0 0 auto;
  font-size: 12px;
  color: var(--muted-fg);
  font-variant-numeric: tabular-nums;
  padding-top: 2px;
}

.buch-koerper { flex: 1 1 auto; min-width: 0; }

.buch-zeile-kopf {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: baseline;
  font-size: 12px;
}

/* ---------------------------------------------------------------
   **Die Kachel unter einer Zeile, die auf einen Anblick zeigt**
   (13.09.2026, auf Wunsch).

   Klein und quer: ein Vorschaubild und daneben der Name. Sie steht in
   einem Buch und nicht im Raster - eine Kachel in Rastergrösse machte aus
   jeder zweiten Zeile einen halben Bildschirm.

   Ein gelöschter Anblick ist blass und **kein Knopf**: im Raster steht er
   nicht mehr, und ein Druck führte dorthin, wo er nicht ist. Der Weg
   daneben führt in den Papierkorb.
   --------------------------------------------------------------- */
.buch-vorschau {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
  flex-wrap: wrap;
}

.buch-vorschau-karte {
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: 100%;
  padding: 4px 8px 4px 4px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--card-bg);
  color: inherit;
  font-family: inherit;
  font-size: 12px;
  text-align: left;
}

button.buch-vorschau-karte { cursor: pointer; }
button.buch-vorschau-karte:hover { border-color: var(--bar-color); }

.buch-vorschau-bild {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  border-radius: 5px;
  object-fit: cover;
  display: block;
}

/* Statt eines Bildes das farbige Feld der Art - ein Element ohne
   Titelbild behält damit die Form der Kachel, und die Farbe ist genau
   die Auskunft, die es sonst auf der Karte gibt. */
.buch-vorschau-bild.farbe { border-radius: 5px; }

.buch-vorschau-wort { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.buch-vorschau-satz { color: var(--muted-fg); min-width: 0; }

.buch-vorschau.weg .buch-vorschau-karte { opacity: 0.6; }

/* Der Weg in den Papierkorb - ein Verweis und kein Knopf, denn er führt
   auf eine Seite. Er steht nur da, wo der Leser sie auch aufmachen darf
   (`buchDarfPapierkorb`). */
.buch-vorschau-korb {
  flex: 0 0 auto;
  font-size: 12px;
  padding: 3px 9px;
  border-radius: 8px;
  border: 1px solid var(--border);
  color: var(--fg);
  text-decoration: none;
}

.buch-vorschau-korb:hover { border-color: var(--bar-color); }

.buch-wer { font-weight: bold; }
.buch-art { color: var(--muted-fg); }

/* **Wie oft derselbe Vorgang** - an einer gefalteten Zeile (19.09.2026).
   Eine Marke und kein weiteres graues Wort: neben `wer` und `was fuer
   einer` stehen hier schon zwei, und die Zahl ist die eine Auskunft, die
   die Faltung sonst verschluckte. */
.buch-zahl {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: bold;
  line-height: 1;
  padding: 2px 6px;
  border-radius: 8px;
  background: var(--chip-bg, rgba(127, 127, 127, 0.22));
  color: var(--fg);
}

/* Der Rest, den die Zeile nicht mehr zeigt. Dieselbe Form wie eine
   Kachel, nur ohne Inhalt - eine Zahl, die sagt, dass hier etwas
   aufhoert, und nicht ein Knopf, der irgendwohin fuehrte. */
.buch-vorschau-mehr {
  flex: 0 0 auto;
  align-self: center;
  font-size: 12px;
  color: var(--muted-fg);
  padding: 4px 8px;
  border: 1px dashed var(--border);
  border-radius: 8px;
}
.buch-geaendert { color: var(--muted-fg); font-style: italic; }

/* **Aus welchem Revier eine geliehene Zeile kommt** - nur im
   Gesellschaftsbuch. Als Marke und nicht als weiteres graues Wort: an
   dieser Stelle stehen schon zwei davon (wer, und was für ein Vorgang),
   und die Herkunft ist die eine, nach der man sucht, wenn drei Reviere
   nebeneinander erzählen. Dieselbe Form wie die Revier-Marke auf einer
   fremden Kachel (§9). */
.buch-woher {
  font-size: 11px;
  padding: 1px 6px;
  border-radius: 999px;
  border: 1px solid var(--line, rgba(0, 0, 0, 0.14));
  color: var(--muted-fg);
  white-space: nowrap;
}

/* Eine geliehene Zeile ist Beiwerk der Runde und nicht ihr Gespräch: der
   Strich am Rand trennt sie von dem, was hier geschrieben wurde. */
.buch-zeile.geliehen .buch-koerper {
  border-left: 2px solid var(--line, rgba(0, 0, 0, 0.10));
  padding-left: 8px;
}

.buch-text { white-space: pre-wrap; overflow-wrap: anywhere; }

/* Ein Zitat: die Zeile, auf die geantwortet wird. Eingerückt und leiser
   als das, was darunter steht - es ist der Anlass, nicht die Sache. */
.buch-zitat {
  border-left: 2px solid var(--muted-fg);
  padding-left: 8px;
  margin: 2px 0 4px;
  font-size: 12px;
  color: var(--muted-fg);
}

.buch-zeile-knoepfe { display: flex; gap: 2px; opacity: 0; }
.buch-zeile:hover .buch-zeile-knoepfe { opacity: 1; }

/* Ohne Darüberfahren gibt es kein Aufdecken: auf dem Telefon stehen die
   Knöpfe einfach da. */
@media (hover: none) {
  .buch-zeile-knoepfe { opacity: 1; }
}

.buch-knopf {
  border: 0;
  background: none;
  color: var(--muted-fg);
  cursor: pointer;
  padding: 4px;
  border-radius: 6px;
}
.buch-knopf:hover { background: rgba(0, 0, 0, 0.08); color: inherit; }

.buch-leer { color: var(--muted-fg); padding: 24px 0; }
.buch-mehr { padding: 16px 0; text-align: center; }

/* ---- Die Suche, die springt ----

   Sie filtert nicht: alle Zeilen bleiben stehen, die Treffer bekommen
   einen Grund, und der, auf dem man gerade steht, einen kräftigeren.
   Wer in einem Buch sucht, sucht die Stelle - und will sehen, was
   daneben steht. */

.buch-suche {
  position: sticky;
  top: 0;
  z-index: 3;
  display: flex;
  gap: 6px;
  align-items: center;
  padding: 8px 0;
  background: var(--bg, #fff);
}

/* `display: flex` oben schlaegt die `display: none`, die der Browser an
   `[hidden]` haengt - eine Autorenregel gewinnt gegen die des Browsers.
   Ohne diese Zeile stuende das Suchfeld immer da, obwohl das Attribut
   gesetzt ist. Dieselbe Falle wie bei `.gallery[hidden]`. */
.buch-suche[hidden] { display: none; }

.buch-suche input { flex: 1 1 auto; min-width: 0; min-height: 38px; }
.buch-suche button {
  min-width: 38px;
  min-height: 38px;
  cursor: pointer;
}

.buch-treffer {
  font-size: 12px;
  color: var(--muted-fg);
  font-variant-numeric: tabular-nums;
  min-width: 4ch;
  text-align: right;
}

.buch-zeile.treffer { background: rgba(217, 131, 36, 0.12); }
.buch-zeile.treffer-hier {
  background: rgba(217, 131, 36, 0.28);
  outline: 2px solid #d98324;
  outline-offset: -2px;
}

/* ---- Der Kasten zum Schreiben ---- */

.buch-schreiber {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--bg, #fff);
  border: 1px solid var(--line, rgba(0, 0, 0, 0.12));
  border-radius: 10px;
  padding: 10px;
  margin-bottom: 12px;
}

.buch-schreiber textarea {
  width: 100%;
  box-sizing: border-box;
  resize: vertical;
  font: inherit;
}

.buch-schreiber-knoepfe {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 8px;
}

/* Die zwei Ecken dieser Seite. Links, was schreibt und sucht; rechts,
   was nur den eigenen Lesezeiger bewegt - dieselbe Aufteilung wie auf
   der Galerie. */
/* **Die linke Ecke rechnet nicht selbst** (04.09.2026, gemeldet: *„Buch
   page: the map ribbon covers the bottom fabs"*).

   Hier stand `bottom: 16px` und `z-index: 40`, geschrieben zu einer Zeit,
   als diese Seite kein Revierband hatte. Seit sie eines hat, liegt der
   Streifen am unteren Rand - und damit ueber den zwei Knoepfen.

   Die Reihe traegt `class="list-fabs"`, und jene Regel kann das alles
   laengst: `bottom` aus der gemessenen Bandhoehe *und* der Tastaturhoehe,
   `z-index: 850` (ueber der Seite, unter dem offenen Band), dazu die
   Fassungen fuer Telefon und offene Tastatur. Sie zu ueberschreiben hiess
   die Arithmetik ein zweites Mal zu schreiben - und zwar die alte. Also
   steht hier nur noch, was diese Seite wirklich anders macht. */
#buchFabs { gap: 12px; }

/* Eigene Klasse und nicht `.gallery-fabs` geborgt: jene traegt ein
   `display: none`, das nur die Galerie wieder aufhebt - eine Ecke, die
   auf einer anderen Seite unsichtbar ist, weil sie fremde Regeln
   mitbringt.

   Dieselbe Rechnung wie `.list-fabs` gegenueber, und aus demselben Grund
   von Hand: sie ist die *rechte* Ecke und kann jene Klasse nicht tragen,
   ohne auch deren `left` zu erben. */
.buch-ecke-rechts {
  position: fixed;
  right: 20px;
  bottom: calc(var(--revier-band-height, 0px) + var(--tastatur-hoehe, 0px) + 16px);
  transition: bottom 0.15s ease;
  z-index: 850;
  display: flex;
}

@media (max-width: 780px) {
  /* Auf dem Telefon liegt die Leiste unten, nicht links: die 56px sind
     ihre Hoehe. Dieselbe Zahl wie in `.list-fabs` eine Bildschirmbreite
     weiter oben. */
  .buch-ecke-rechts {
    bottom: calc(56px + var(--revier-band-height, 0px) + var(--tastatur-hoehe, 0px) + 16px);
  }
}

/* Und geht die Tastatur auf, geht auch diese Ecke nach unten - wer
   schreibt, drueckt kein „alles gelesen". Dieselbe Regel wie fuer
   `.list-fabs`. */
@media (max-width: 700px) and (pointer: coarse) {
  body.feld-fokus .buch-ecke-rechts { bottom: 16px; }
}


/* ---------------------------------------------------------------
   **Die Meldeblase** (05.09.2026, auf Wunsch): unten rechts, kurz, und
   ein Druck nimmt sie weg. Bisher nur fuers Loeschen - siehe `melde` in
   nav.js.

   **Ueber allem, auch ueber dem Leuchtkasten** (9999) und seinem
   Vorhang (10000): geloescht wird gerade *dort*, wo ein Bild aus dem
   Streifen verschwindet, und eine Meldung dahinter waere eine, die
   niemand liest.

   **Unten rechts, aber ueber dem Band**: dieselbe Rechnung wie bei den
   Ecken der Seiten, damit sie nicht hinter dem Revierstreifen sitzt.
   `pointer-events` steht nur an den Blasen selbst - der Kasten darum ist
   so breit wie die Ecke und wuerde sonst die Knoepfe darunter
   verschlucken, auch wenn er leer ist.
   --------------------------------------------------------------- */
.meldungen {
  position: fixed;
  right: 20px;
  bottom: calc(var(--revier-band-height, 0px) + 16px);
  z-index: 10001;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  pointer-events: none;
}

/* **Die Blase mit einem Rückgängig steht oben in der Mitte**
   (22.09.2026, auf Wunsch). Sie ist die einzige mit einer Frist - wer
   sie verpasst, verpasst die Gelegenheit -, und unten rechts ist die
   Ecke, in die man beim Arbeiten gerade nicht sieht. Ihre `z-index` ist
   dieselbe; nur die Kante, an der sie klebt, ist eine andere. */
.meldungen.meldungen-oben {
  right: auto;
  bottom: auto;
  top: 16px;
  left: 50%;
  transform: translateX(-50%);
  align-items: center;
}

.meldung {
  pointer-events: auto;
  max-width: min(360px, calc(100vw - 40px));
  padding: 10px 14px;
  border-radius: 10px;
  background: rgba(32, 32, 32, 0.94);
  color: #f2f2f0;
  font-size: 13px;
  line-height: 1.35;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
  cursor: pointer;
  animation: meldung-herein 160ms ease-out;
}

/* **Mit einem Zurueck** (06.09.2026): der Satz links, der Knopf rechts,
   und dazwischen so viel Luft, wie die Blase hergibt. Kein zweiter Kasten
   und keine zweite Zeile - die Blase liegt in einer Ecke, und was dort
   waechst, waechst ueber die Bilder. */
.meldung.mit-zurueck {
  display: flex;
  align-items: center;
  gap: 12px;
  padding-right: 8px;
}

.meldung-wort { flex: 1; min-width: 0; }

/* Ein Wort, kein Kasten: die Blase selbst ist schon der Rahmen, und ein
   Knopf darin, der noch einmal umrandet ist, ist eine Schachtel in einer
   Schachtel. Die Farbe ist die des Zeichens am offenen Aufgabenzaehler -
   in dieser Anwendung heisst orange *„hier ist noch etwas"*. */
.meldung-zurueck {
  flex: 0 0 auto;
  padding: 5px 10px;
  border: none;
  border-radius: 7px;
  background: rgba(255, 255, 255, 0.12);
  color: #f2b46a;
  font-size: 13px;
  font-weight: bold;
  cursor: pointer;
}

.meldung-zurueck:hover { background: rgba(255, 255, 255, 0.2); }
.meldung-zurueck:disabled { opacity: 0.6; cursor: default; }

@keyframes meldung-herein {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

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

/* Auf dem Telefon liegt die Leiste unten: dieselbe Zahl wie bei
   `.list-fabs`, damit die Blase nicht darauf sitzt. */
@media (max-width: 780px) {
  .meldungen { bottom: calc(56px + var(--revier-band-height, 0px) + 16px); }
  /* Die obere klebt oben und hat mit der Leiste am Fuss nichts zu tun -
     die Regel darüber setzt sonst auch ihr ein `bottom`, und dann steht
     sie in einem Kasten, der über den ganzen Schirm reicht. */
  .meldungen.meldungen-oben { bottom: auto; }
}


/* **Die Kachel „hier gesehen"** (05.09.2026, auf Wunsch): erste in der
   Reihe, in der Farbe der Art, Zeichen und Zahl untereinander. Sie steht
   nur da, wo wirklich etwas gesehen wurde - siehe `anblickKachel` in
   reviereinrichtungen.js.

   Die Groesse kommt von `.einrichtung-tile`; hier steht nur, was darin
   liegt. `flex-direction: column`, damit Zeichen und Zahl uebereinander
   stehen wie auf einem Knopf der Leiste, und nicht nebeneinander wie ein
   Etikett. */
.einrichtung-tile.anblicke {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
}

.einrichtung-tile-zeichen { display: flex; }
.einrichtung-tile-zeichen svg { width: 26px; height: 26px; }

.einrichtung-tile-zahl {
  font-size: 15px;
  font-weight: bold;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
