/* ==========================================================================
   Colour system
   --------------------------------------------------------------------------
   The single place colours are defined (VIS-006). Application rules below use
   the tokens only - no colour literal appears outside this block (VIS-010),
   and CSS renders a token as --color-<token> (VIS-011).

   Two schemes, dark by default (VIS-009); public/theme.js sets
   data-scheme on <html> before first paint and the toggle in the top bar
   switches them in one click (VIS-005).

   Values: the dark scheme is the Style2020 table from
   team-standards/ui/visual-design.md, verbatim. The day scheme is NOT in the
   standard yet (its own TODO(team) says so - only foreground #152635 is
   settled), so the day values here are this project's local decision and are
   listed as a gap in docs/standards-setup-report.md, with an offer to
   upstream them.
   ========================================================================== */

/* Palette tier - raw hues. VIS-010: referenced only by the scheme blocks
   below, never by an application rule. */
:root {
  /* Platz, den die Kopfzeile rechts für den Farbschema-Knopf freihält, und
     seine Höhe - die erste Zeile der Leiste muss mindestens so hoch sein,
     sonst ragt der Knopf in die darunter umgebrochene Navigation. */
  --scheme-toggle-space: 68px;
  --scheme-toggle-height: 26px;

  --palette-hoppe-blue: #004876;
  --palette-hoppe-white: #ffffff;
  --palette-purple: #5055bc;
  --palette-light-blue: #3e8ecf;
  --palette-aqua-blue: #29e1d2;
  --palette-yellow: #ffdd4e;
  --palette-rose: #e2747d;
  --palette-dark-red: #591933;
  --palette-beige: #96756d;

  --radius: 10px;
}

/* Dark - the default scheme. Values from Style2020. */
:root,
:root[data-scheme='dark'] {
  color-scheme: dark;

  --color-background: #000f20;
  --color-background-2: #152634;
  --color-background-3: #22324c;
  --color-foreground: #ffffff;
  /* foreground-2 is TBD in the standard; this is a local value chosen for
     AA contrast on background and background-2. */
  --color-foreground-2: #9fb0c0;
  --color-inactive: var(--palette-hoppe-blue);

  /* Spacing scale. The standard's "Spacing & layout" section is still an
     empty heading, so these are local until it has one. They exist because
     spacing used to be decided in the page markup, 99 times over: 31 copies
     of style="margin-top:0" alone, and margins of 6, 10, 14 and 18px sitting
     next to each other with nothing to say which was meant. Four-pixel grid;
     the off-grid values were snapped to the nearest step on the way out of
     the HTML. styles.css still carries ad-hoc values of its own (3, 5, 7 and
     9px among them) - normalising those is a separate and much larger job. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --color-enabled: var(--palette-hoppe-blue);
  --color-btn-active: var(--palette-hoppe-blue);
  --color-offline: #5d606e;
  --color-disabled: #5d606e;
  --color-active: #2cd781;
  --color-alarm: #fc474a;
  --color-warning: #ff9e1b;
  --color-disturbed: #ec3b89;

  /* Role aliases. Not new colours - each is one of the tokens above, named
     for where it is used, so the application rules stay readable. */
  --surface-page: var(--color-background);
  --surface-card: var(--color-background-2);
  --surface-sunken: var(--color-background-3);
  --surface-brand: var(--palette-hoppe-blue);
  --border: var(--color-background-3);

  /* Text colour to place ON a filled status colour. The status hues are
     tuned for a dark ground, so on top of them the dark background is what
     stays legible. */
  --on-status: var(--color-background);
  --on-brand: var(--palette-hoppe-white);
  /* Link and hover colours, also aliases over the tokens above. */
  --accent: var(--palette-light-blue);
  /* Hover state of a filled button. Deliberately a lighter hoppe-blue rather
     than the light-blue accent: white button text on the accent is only
     3.5:1, which fails AA for 14px text - and a hovered button is exactly
     when it is being read. */
  --btn-hover: #005c96;
  /* Translucent white over the brand bar - a transparency, not a hue, so it
     needs no palette entry; it works over hoppe-blue in both schemes. */
  --overlay-brand-hover: rgba(255, 255, 255, 0.14);
}

/* Day. Local values - see the note above. */
:root[data-scheme='day'] {
  color-scheme: light;

  --color-background: #ffffff;
  --color-background-2: #f2f5f8;
  --color-background-3: #dbe3ec;
  --color-foreground: #152635;
  --color-foreground-2: #5a6875;
  --color-inactive: var(--palette-hoppe-blue);
  --color-enabled: var(--palette-hoppe-blue);
  --color-btn-active: var(--palette-hoppe-blue);
  --color-offline: #5d606e;
  --color-disabled: #7c8590;
  /* The Style2020 status hues are chosen against a dark ground and fail
     contrast on white, so the day scheme darkens them to the same hue at AA
     against the page. They stay unmistakably green / red / amber / pink. */
  --color-active: #0c6b3b;
  --color-alarm: #c1272d;
  --color-warning: #8a5300;
  --color-disturbed: #a81d5e;

  --surface-page: var(--color-background-2);
  --surface-card: var(--color-background);
  --surface-sunken: var(--color-background-2);
  --surface-brand: var(--palette-hoppe-blue);
  --border: var(--color-background-3);

  --on-status: var(--palette-hoppe-white);
  --on-brand: var(--palette-hoppe-white);
  --accent: var(--palette-hoppe-blue);
  --btn-hover: #003354; /* hoppe-blue, one step darker for the pressed state */
  --overlay-brand-hover: rgba(255, 255, 255, 0.14);
}

* { box-sizing: border-box; }

[hidden] { display: none !important; }

body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  background: var(--surface-page);
  color: var(--color-foreground);
}

header.topbar {
  background: var(--surface-brand);
  color: var(--on-brand);
  padding: 14px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  /* Für den Farbschema-Knopf unten, der in der Ecke steht statt
     mitzuwandern - und für den Platz, den er dort braucht. */
  position: relative;
  padding-right: calc(24px + var(--scheme-toggle-space));
}

header.topbar .brand {
  font-weight: 700;
  font-size: 16px;
  letter-spacing: 0.2px;
  /* Hält die erste Zeile so hoch wie der Knopf, der rechts darüber steht.
     Ohne das beginnt die umgebrochene Navigation weiter oben als der Knopf
     endet, und der erste Eintrag läuft darunter durch. */
  display: flex;
  align-items: center;
  min-height: var(--scheme-toggle-height);
}

header.topbar nav {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

header.topbar nav a {
  color: var(--on-brand);
  opacity: 0.82;
  text-decoration: none;
  padding: 8px 14px;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 600;
}

header.topbar nav a:hover { background: var(--overlay-brand-hover); opacity: 1; }
header.topbar nav a.active { background: var(--color-btn-active); color: var(--on-brand); opacity: 1; }

/* Die beiden Klappmenüs der Leiste (3.8.0). Neun Einträge nebeneinander wurden
   als Tapete gelesen; jetzt stehen die drei Seiten des Gesprächs allein, der
   Rest liegt unter „Reports" und „More". Verhalten in nav.js - per Klick, nicht
   per Hover: am Stand wird auf iPads gearbeitet, und ein Menü, das beim
   Darüberfahren aufgeht, bekommt ein Finger nicht wieder zu. */
header.topbar nav .nav-group { position: relative; }

header.topbar nav .nav-group-btn {
  color: var(--on-brand);
  opacity: 0.82;
  background: none;
  border: 0;
  padding: 8px 26px 8px 14px;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  width: auto;
}
/* Der Pfeil sagt „hier klappt etwas auf" - ohne ihn liest sich der Knopf wie
   ein Menüpunkt, der nirgendwohin führt. */
header.topbar nav .nav-group-btn::after {
  content: "";
  position: absolute;
  right: 11px;
  top: 50%;
  margin-top: -2px;
  border: 4px solid transparent;
  border-top-color: currentColor;
}
header.topbar nav .nav-group-btn:hover { background: var(--overlay-brand-hover); opacity: 1; }
header.topbar nav .nav-group.open > .nav-group-btn,
header.topbar nav .nav-group.current > .nav-group-btn {
  background: var(--color-btn-active);
  opacity: 1;
}

header.topbar nav .nav-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 30;
  min-width: 190px;
  padding: 6px;
  border-radius: 10px;
  background: var(--surface-card);
  border: 1px solid var(--border);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.28);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
/* Im Menü steht der Text auf der Seitenfläche, nicht auf dem Markenblau der
   Leiste - deshalb die Textfarben der Seite und nicht --on-brand. */
header.topbar nav .nav-menu a {
  color: var(--color-foreground);
  opacity: 1;
  padding: 8px 12px;
  white-space: nowrap;
}
header.topbar nav .nav-menu a:hover { background: var(--surface-sunken); }
header.topbar nav .nav-menu a.active { background: var(--color-btn-active); color: var(--on-brand); }

/* Mit welchem Konto dieses Gerät angemeldet ist: zwei Buchstaben auf dem
   „Sign out"-Eintrag. Vorher war es eine Zeile im Menü „More" - eine Angabe,
   die man nur sah, wenn man das Menü ohnehin aufklappte, also praktisch nie.
   Als Plakette steht sie immer da und kostet keine Zeile.
   Kein Farbcode pro Rolle: eine Statusfarbe müsste hier ein Token borgen, das
   etwas anderes heißt (VIS-010), und Farbe allein darf keine Bedeutung tragen.
   Die Rolle steht deshalb als Wort daneben - aber nur, wenn sie etwas erklärt:
   „Booth team" ist, was die meisten Sitzungen sind. Gefüllt von authguard.js. */
header.topbar nav #signout-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

/* Umgekehrt zur Leiste - heller Kreis, Markenfarbe als Schrift. Borgt kein
   Token, das anderswo etwas bedeutet, und hebt sich von den Einträgen ab,
   ohne wie einer auszusehen. */
header.topbar nav .nav-initials {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--on-brand);
  color: var(--surface-brand);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3px;
}
header.topbar nav .nav-initials:empty { display: none; }

header.topbar nav .nav-role {
  font-weight: 600;
  white-space: nowrap;
}
header.topbar nav .nav-role:empty { display: none; }

/* Unter 920px bleibt nur der Kreis stehen. Gemessen mit „Administrator", dem
   laengsten der drei Woerter: bei 920px passt die Leiste damit noch in zwei
   Zeilen, bei 910px bricht sie in eine dritte um - Hoehe, die der Seite
   darunter fehlt. Das Wort erklaert, warum die Leiste aussieht, wie sie
   aussieht; eine zusaetzliche Zeile zu kosten, um das zu erklaeren, ist der
   schlechtere Tausch. Ohne das Wort sagt die Leiste es ohnehin selbst, weil
   „Reports" und „Team" nur dort stehen, wo sie sich oeffnen lassen. */
@media (max-width: 920px) {
  header.topbar nav .nav-role { display: none; }
}

/* Am Telefon steht der Knopf dicht am rechten Rand; ein nach rechts geöffnetes
   Menü schiebt die Seite seitlich weg. Also andersherum aufklappen und die
   Breite auf den Bildschirm begrenzen - eine Seite, die seitlich scrollt, ist
   am Stand mit einer Hand nicht zu bedienen. */
@media (max-width: 700px) {
  header.topbar nav .nav-menu {
    left: auto;
    right: 0;
    max-width: calc(100vw - 24px);
  }
}

main {
  max-width: 980px;
  margin: 0 auto;
  padding: 24px 20px 60px;
}

/* The Overview is one wide table - and the Edit/Delete buttons live in its
   last column, so if the table has to scroll sideways on a normal laptop the
   page loses the thing people came to it for. Given a wide window it may use
   it; on a narrow one it behaves exactly like every other page. */
main.wide {
  max-width: 1360px;
}

h1 { font-size: 22px; margin: 0 0 4px; }
h2 { font-size: 16px; margin: 28px 0 12px; }
.subtitle { color: var(--color-foreground-2); margin: 0 0 20px; font-size: 14px; }

.card {
  background: var(--surface-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px;
  margin-bottom: 18px;
}

.grid { display: grid; gap: 14px; }
.grid-2 { grid-template-columns: 1fr 1fr; }
.grid-3 { grid-template-columns: 1fr 1fr 1fr; }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-5 { grid-template-columns: repeat(5, 1fr); }
/* Street / house no. / ZIP / City / Country in the proportions they are
   actually written in - a house number needs three characters, a street name
   twenty. Equal columns would make the form look like a form and read worse. */
.address-grid { grid-template-columns: 2.4fr 0.8fr 1fr 1.8fr 1.4fr; }
@media (max-width: 900px) {
  .grid-5 { grid-template-columns: repeat(3, 1fr); }
  .address-grid { grid-template-columns: 2.2fr 0.9fr 1fr; }
}
@media (max-width: 720px) {
  .grid-2, .grid-3, .grid-4, .grid-5 { grid-template-columns: 1fr; }
  /* Drei Spalten statt zwei (3.10.0). Mit „2.4fr 1fr" fielen Straße und
     Hausnummer richtig herum, PLZ und Ort aber falsch herum: die PLZ bekam
     die breite Spalte, der Ort die schmale, in der „Bremerhaven" abgeschnitten
     ankam. In drei gleichen Spalten lässt sich jedes Feld so breit setzen, wie
     es geschrieben wird - Straße 2, Nummer 1, PLZ 1, Ort 2, Land über alles. */
  .address-grid { grid-template-columns: repeat(3, 1fr); }
  .address-grid > :nth-child(1) { grid-column: span 2; }
  .address-grid > :nth-child(4) { grid-column: span 2; }
  .address-grid > :nth-child(5) { grid-column: 1 / -1; }
}

label {
  display: block;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--color-foreground-2);
  margin-bottom: 4px;
}

input[type="text"], input[type="email"], input[type="tel"], input[type="date"], input[type="password"], select, textarea {
  width: 100%;
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  font-family: inherit;
  background: var(--surface-card);
  color: var(--color-foreground);
}
textarea { resize: vertical; min-height: 56px; }

/* The open list of a <select> is painted by the browser, and giving the control
   its own background above opts it out of the colour-scheme-aware painting that
   would otherwise have made the list dark too. The options then inherit our
   near-white foreground onto the browser's white popup - white on white, which
   is why an opened picker looked like a blank rectangle.
   Painting them explicitly is the only reliable fix.

   This covers <select>. A <datalist> popup is drawn outside the page in Chromium
   and takes no author styling at all; there, `color-scheme` on :root is the only
   lever, and it is already set. */
option {
  background: var(--surface-card);
  color: var(--color-foreground);
}

/* Controls that open a list look like controls that open a list.

   On the Overview, "Visitor category" is a <select> (the categories are a
   closed set) while "Forwarded to" is an <input list> (it names a person, and
   people are always picked that way - see public/people.js). That difference is
   deliberate and stays. What was not deliberate is that they looked unrelated:
   the select carried the browser's own arrow and its own metrics, the input
   looked like a plain text box, and the two sat side by side in one filter row.

   So the native arrow goes (appearance: none) and both get the same chevron,
   drawn from the foreground token rather than a coloured image so it follows
   the colour scheme (VIS-010). input[type=date] is left alone: it has its own
   native picker button, and a second affordance next to it would be a lie. */
/* Scoped to the selects that actually get a chevron below. Unscoped, a select
   outside a .field - the "Merge into..." picker in a Team table row - would
   lose the browser's arrow and gain nothing, leaving a box with no sign that
   it opens. */
.field > select { appearance: none; }

.field { position: relative; }

/* Der Haken wird auf das Bedienelement selbst gezeichnet, nicht als ::after auf
   das umgebende .field: unter dem Feld koennen Hinweise stehen - „nicht im Team
   dieser Messe", „noch keine Messe angelegt" -, und ein vom Feldrand her
   positionierter Haken rutscht mit ihnen nach unten aus dem Kasten heraus.
   Zwei Verlaeufe statt eines gefaerbten Bildes, damit die Farbe aus dem Token
   kommt und dem Farbschema folgt (VIS-010). */
.field:has(> select) > select,
.field:has(> input[list]) > input[list] {
  /* room for the chevron, so a long name never runs under it */
  padding-right: 30px;
  background-image:
    linear-gradient(45deg, transparent calc(50% - 1px), var(--color-foreground-2) calc(50% - 1px),
      var(--color-foreground-2) calc(50% + 1px), transparent calc(50% + 1px)),
    linear-gradient(-45deg, transparent calc(50% - 1px), var(--color-foreground-2) calc(50% - 1px),
      var(--color-foreground-2) calc(50% + 1px), transparent calc(50% + 1px));
  background-size: 6px 6px;
  background-position: right 16px center, right 11px center;
  background-repeat: no-repeat;
}

/* On phones and tablets, iOS Safari zooms the page in whenever you tap a
   field whose text is smaller than 16px - and it does not zoom back out.
   With a form this long that's genuinely painful at the booth, so inputs go
   to 16px on smaller screens. Deliberately NOT solved by disabling pinch
   zoom in the viewport meta tag, which would hurt anyone who needs to zoom. */
@media (max-width: 1024px) {
  input[type="text"], input[type="email"], input[type="tel"], input[type="date"],
  input[type="password"], select, textarea {
    font-size: 16px;
  }
}

.field { margin-bottom: 4px; }

fieldset {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
  margin: 0;
}
legend {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--color-foreground-2);
  padding: 0 4px;
}

.checkbox-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px 12px;
}
@media (max-width: 600px) { .checkbox-grid { grid-template-columns: 1fr 1fr; } }

.checkbox-grid label {
  display: flex;
  align-items: center;
  gap: 7px;
  font-weight: 400;
  font-size: 13.5px;
  color: var(--color-foreground);
  margin-bottom: 0;
}
.checkbox-grid input { width: auto; }

.radio-row { display: flex; gap: 18px; flex-wrap: wrap; }
.radio-row label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-weight: 500;
  font-size: 14px;
  color: var(--color-foreground);
}
.radio-row input { width: auto; }

button, .btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--color-btn-active);
  color: var(--on-brand);
  border: none;
  padding: 11px 20px;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}
button:hover, .btn:hover { background: var(--btn-hover); }
button.secondary, .btn.secondary {
  background: transparent;
  color: var(--accent);
  border: 1px solid var(--border);
}
button.secondary:hover { background: var(--surface-sunken); }
button.danger { background: var(--color-alarm); color: var(--on-status); }
button.danger:hover { background: var(--color-disturbed); }
button:disabled { opacity: 0.55; cursor: not-allowed; }

/* The two buttons in a lead row. Small, but still a proper touch target: the
   booth works on tablets, and these sit at the right edge where thumbs are. */
.row-actions { white-space: nowrap; }

/* Die Knöpfe einer Messe stehen in einer eigenen Zeile unter ihren Daten.
   Nebeneinander brauchten sie 345px und bekamen 283: die Tabelle bekam einen
   Querbalken, und zwar auf jeder Fensterbreite, weil main ohnehin bei 980px
   endet. Die sechs Datenspalten zusammen sind 615px breit und passen damit
   auch auf ein hochkant gehaltenes iPad.
   Die Datenzeile gibt ihre Trennlinie ab, die Knopfzeile behält sie - so
   trennt die Linie Messen und nicht Zeilen. Kein Hover-Hintergrund mehr:
   eine von zwei Zeilen einzufärben zerreißt die Gruppe genau da, wo sie
   zusammengehört. */
/* Zwei Datumsfelder uebereinander, damit der Zeitraum die Tabelle nicht
   breiter macht als der Platz hergibt. */
.period-cell input[type="date"] {
  display: block;
  width: 100%;
  min-width: 118px;
  margin-bottom: 2px;
}
.period-cell input[type="date"]:last-child { margin-bottom: 0; }

#events-tbody tr:not(.row-actions-row) td { border-bottom: 0; }
#events-tbody tr:hover td { background: transparent; }
/* Die Knopfzeile einer Messe steht rechts: sie gehoert zu der Zeile darueber
   und soll nicht wie eine eigene Spalte gelesen werden. */
.row-actions-row td { padding-top: 0; padding-bottom: 12px; text-align: right; }
.row-actions .btn {
  padding: 6px 12px;
  font-size: 12px;
  min-height: 32px;
}
.row-actions .btn + .btn { margin-left: 6px; }

/* Die Meldungsleiste klebt oben am Bildschirmrand, sobald man an ihr
   vorbeigescrollt ist. Der Grund ist eine wiederholte Beobachtung am Stand:
   man füllt das lange Erfassungsformular, drückt Speichern, die Meldung
   erscheint am Seitenanfang - und niemand sieht sie, weil der Seitenanfang
   800px weiter oben liegt. Also klebrig statt fest positioniert: ganz oben
   steht sie im Fluss unter der Leiste und verdeckt nichts, weiter unten
   heftet sie sich an den oberen Rand und ist immer zu sehen.
   z-index zwischen der klebenden Knopfzeile (20) und den Klappmenüs (30):
   ein offenes Menü ist das, womit gerade gearbeitet wird, und gehört darüber.
   :empty, damit die leere Hülle keinen Platz einnimmt. */
#toast {
  position: sticky;
  /* Unter der Abmelde-Leiste, nicht darunter verborgen: die klebt ebenfalls
     oben, und ihre Höhe hängt davon ab, wie der Satz umbricht - authguard.js
     veröffentlicht sie deshalb als Variable. 0px, solange keine Warnung läuft. */
  top: calc(var(--session-warning-height, 0px) + var(--space-2));
  z-index: 25;
}
#toast:empty { display: none; }

.toast {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  border-radius: 8px;
  padding: 12px 16px;
  font-size: 14px;
  margin-bottom: 16px;
  border: 1px solid var(--border);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.28);
}
.toast-text { flex: 1; min-width: 0; }

/* Nur an den Meldungen, die stehen bleiben - eine Erfolgsmeldung räumt sich
   nach vier Sekunden selbst weg und braucht keinen Knopf. */
.toast-close {
  flex: none;
  width: 28px;
  height: 28px;
  margin: -4px -6px 0 0;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--color-foreground-2);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}
.toast-close:hover { background: var(--surface-card); color: var(--color-foreground); }
.toast.success { background: var(--surface-sunken); border-color: var(--color-active); color: var(--color-foreground); }
.toast.error { background: var(--surface-sunken); border-color: var(--color-alarm); color: var(--color-foreground); }
/* Zwischen beiden: es ist etwas passiert, aber nichts ist schiefgegangen -
   eine Messe ohne Team wird gespeichert und taugt trotzdem noch nicht. */
.toast.warn { background: var(--surface-sunken); border-color: var(--color-warning); color: var(--color-foreground); }

/* Die Abmelde-Warnung (authguard.js). Eine dünne Leiste über der Kopfzeile, die
   oben am Bildschirmrand klebt: sie steht die letzte Stunde über da, und das
   Einzige, was sie aushalten muss, ist das Wegscrollen - am Stand wird ein
   langes Formular abgearbeitet, und eine Warnung am Seitenanfang liest niemand.
   Oben im Fluss steht sie über der Kopfzeile und verdeckt nichts; weiter unten
   heftet sie sich an den Rand. z-index über allem anderen, auch über den
   Klappmenüs - sie ist die einzige Meldung, die sich nicht ersetzen lässt.
   --color-warning ist dafür das richtige Token (Alarmstufe 2, VIS-010): hier
   bedeutet Warnfarbe wirklich Warnung, nichts ist geborgt. */
.session-warning {
  position: sticky;
  top: 0;
  z-index: 50;
  padding: 6px 16px;
  font-size: 13px;
  line-height: 1.4;
  color: var(--color-foreground);
  background: var(--surface-page);
  border-bottom: 2px solid var(--color-warning);
  /* Klammert die Tönung unten auf die Leiste. */
  overflow: hidden;
}
/* Am Telefon bleiben Countdown und Ausweg, der erklärende Satz geht - die
   Leiste steht eine Stunde lang da, und drei Zeilen davon sind auf 390px keine
   dünne Leiste mehr. */
@media (max-width: 700px) {
  .session-warning-detail { display: none; }
}
.session-warning a { color: var(--color-foreground); }

/* Einmal pro Minute zwei Sekunden sanft auf- und wieder abblenden. Bewegung
   fängt den Blick; dauernde Bewegung wird übersehen und müsste nach WCAG 2.2.2
   ausserdem anzuhalten sein. Als Überlagerung statt als Farbwechsel am Text,
   damit die Zahl die ganze Zeit lesbar bleibt. */
.session-warning::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--color-warning);
  opacity: 0;
  pointer-events: none;
  animation: session-warning-pulse 60s ease-in-out infinite;
}
@keyframes session-warning-pulse {
  0% { opacity: 0; }
  1.7% { opacity: 0.22; }
  3.4% { opacity: 0; }
  100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .session-warning::after { animation: none; }
}

/* Confirmation panel after saving a lead. The number has to be copied onto the
   visitor's business card, so it is shown large and stays until dismissed
   rather than fading like a normal toast. */
.lead-number-panel {
  display: flex;
  align-items: center;
  gap: 16px;
  background: var(--surface-sunken);
  border: 2px solid var(--color-active);
  border-radius: var(--radius);
  padding: 14px 18px;
  margin-bottom: 16px;
}
.lead-number-badge {
  flex: 0 0 auto;
  min-width: 64px;
  padding: 6px 14px;
  background: var(--surface-brand);
  color: var(--on-brand);
  border-radius: 10px;
  font-size: 34px;
  font-weight: 800;
  line-height: 1.15;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.lead-number-text { flex: 1 1 auto; font-size: 15px; color: var(--color-foreground); }
.lead-number-text .muted { color: var(--color-foreground-2); }
.nowrap { white-space: nowrap; }
@media (max-width: 560px) {
  .lead-number-panel { flex-wrap: wrap; gap: 12px; }
  .lead-number-text { flex-basis: 100%; order: 3; }
}

/* Wide tables scroll inside their own box rather than making the whole page
   scroll sideways - on a phone the lead list is far wider than the screen. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
@media (max-width: 720px) {
  .table-scroll table { width: auto; min-width: 100%; }
  .table-scroll th, .table-scroll td { white-space: nowrap; }
}

table { width: 100%; border-collapse: collapse; font-size: 13px; }
/* VIS-003 / UX-009: align numbers with tabular figures rather than switching
   to a monospace font. */
td, th { font-feature-settings: "tnum"; font-variant-numeric: tabular-nums; }
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); }
th { color: var(--color-foreground-2); font-weight: 700; font-size: 12px; }
tr:hover td { background: var(--surface-sunken); }

/* Ein Pill ohne Zusatz ist neutral - und sieht auch so aus. Ohne eigene
   Grundfarbe waere es nur fetter Text in Pillenform ohne Pille, was beim
   „Pending" des Follow-ups sofort auffiel. */
.pill {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 700;
  background: var(--surface-sunken);
  color: var(--color-foreground-2);
}
.pill.high { background: var(--color-active); color: var(--on-status); }
.pill.middle { background: var(--color-warning); color: var(--on-status); }
.pill.low { background: var(--surface-sunken); color: var(--color-foreground-2); }

.stat-tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
@media (max-width: 720px) { .stat-tiles { grid-template-columns: 1fr 1fr; } }
.stat-tile {
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px 16px;
}
.stat-tile .value { font-size: 24px; font-weight: 700; }
.stat-tile .label { font-size: 12px; color: var(--color-foreground-2); margin-top: 2px; }

.chart-wrap { position: relative; margin-bottom: 8px; }
.chart-tooltip {
  position: absolute;
  pointer-events: none;
  background: var(--color-foreground);
  color: var(--surface-page);
  font-size: 12px;
  padding: 5px 9px;
  border-radius: 6px;
  transform: translate(-50%, -110%);
  white-space: nowrap;
  opacity: 0;
  transition: opacity 0.08s;
  z-index: 5;
}
/* Die Werteliste unter jedem Diagramm: fuer Bildschirmleser da, nicht fuers
   Auge. Ein Canvas hat fuer sie keinen Inhalt - ohne diese Liste waere das
   Diagramm fuer sie leer. Sichtbar war sie bis 1.2.0 und sagte dasselbe ein
   zweites Mal: das Diagramm beschriftet jeden Wert bereits am Balkenende.
   Deshalb ausgeblendet statt geloescht - `display:none` waere beides weg. */
.chart-data-list {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Non-blocking duplicate warning under the Company field. Amber, not red:
   a second contact at the same company is legitimate, so this informs, it
   does not accuse. */
.dup-hint {
  margin: 6px 0 0;
  padding: 8px 10px;
  border: 1px solid var(--color-warning);
  background: var(--surface-sunken);
  border-radius: 8px;
  font-size: 12.5px;
  color: var(--color-foreground);
}
.dup-hint ul { margin: 4px 0 4px; padding-left: 18px; }
.dup-hint .muted { color: var(--color-foreground-2); }

/* "Record a customer evaluation" switch on the capture form. */
.do-feedback-toggle {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  margin-bottom: 0;
  font-weight: 400;
  color: var(--color-foreground);
  cursor: pointer;
}
.do-feedback-toggle input { width: auto; margin-top: 2px; flex: 0 0 auto; }

/* NPS: the recommendation question. Bigger touch targets than the driver
   ratings, because this is the one answer that must always be captured, and
   colour-banded so the 0-6 / 7-8 / 9-10 groups are visible while asking. */
.nps-scale { display: flex; gap: 6px; flex-wrap: wrap; }
.nps-box {
  position: relative;
  flex: 1 1 54px;
  min-width: 46px;
  margin-bottom: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  text-align: center;
  cursor: pointer;
  background: var(--surface-card);
  padding: 0;
  overflow: hidden;
}
.nps-box input { position: absolute; opacity: 0; pointer-events: none; }
.nps-box span {
  display: block;
  padding: 12px 0;
  font-size: 16px;
  font-weight: 700;
  color: var(--color-foreground-2);
}
.nps-box.detractor { border-color: var(--color-alarm); }
.nps-box.passive { border-color: var(--color-warning); }
.nps-box.promoter { border-color: var(--color-active); }
.nps-box:hover span { background: var(--surface-sunken); }
.nps-box.detractor:has(input:checked) { background: var(--color-alarm); border-color: var(--color-alarm); }
.nps-box.passive:has(input:checked) { background: var(--color-warning); border-color: var(--color-warning); }
.nps-box.promoter:has(input:checked) { background: var(--color-active); border-color: var(--color-active); }
.nps-box:has(input:checked) span { color: var(--on-status); }
/* Fallback for browsers without :has() - the checked box still reads as chosen. */
.nps-box input:checked ~ span { text-decoration: underline; }

/* On narrow screens keep an even grid instead of letting the last box (10)
   stretch across the whole final row. */
@media (max-width: 620px) {
  .nps-box { flex: 0 0 calc(20% - 5px); }
}

.nps-legend {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 10px;
  font-size: 12px;
  color: var(--color-foreground-2);
}
.nps-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 5px; }
.nps-dot.detractor { background: var(--color-alarm); }
.nps-dot.passive { background: var(--color-warning); }
.nps-dot.promoter { background: var(--color-active); }
.nps-promoter { color: var(--color-active); }
.nps-passive { color: var(--color-warning); }
.nps-detractor { color: var(--color-alarm); }

/* NPS headline score in the report. */
.nps-score { font-size: 34px; font-weight: 800; line-height: 1.1; }
.nps-bar { display: flex; height: 14px; border-radius: 7px; overflow: hidden; margin: 10px 0 6px; }
.nps-bar span { display: block; }
.nps-bar .d { background: var(--color-alarm); }
.nps-bar .p { background: var(--color-warning); }
.nps-bar .r { background: var(--color-active); }

/* One row per feedback category: label, the 0-10 scale, and a Clear button. */
.rating-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}
.rating-row:last-child { border-bottom: none; }
.rating-label {
  flex: 0 0 170px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--color-foreground-2);
}
.rating-row .radio-row { flex: 1 1 auto; gap: 14px; }
.rating-clear { padding: 3px 10px; font-size: 11.5px; }
@media (max-width: 620px) {
  .rating-label { flex-basis: 100%; }
}

.muted { color: var(--color-foreground-2); }
.small { font-size: 12px; }
.right { text-align: right; }
/* Spacing utilities, so a one-off margin is a named step on the scale rather
   than a number typed into the markup. Vertical only: every element these are
   used on is a block whose horizontal margin is already zero, and a utility
   that silently reset left/right too would be a trap. */
.m-0 { margin: 0; }
.mt-0 { margin-top: 0; }
.mb-0 { margin-bottom: 0; }
.mt-1 { margin-top: var(--space-1); }
.mt-2 { margin-top: var(--space-2); }
.mt-3 { margin-top: var(--space-3); }
.mt-4 { margin-top: var(--space-4); }
.mt-5 { margin-top: var(--space-5); }
.mb-1 { margin-bottom: var(--space-1); }
.mb-2 { margin-bottom: var(--space-2); }
.mb-3 { margin-bottom: var(--space-3); }
.mb-4 { margin-bottom: var(--space-4); }
.ml-auto { margin-left: auto; }
.gap-2 { gap: var(--space-2); }
.gap-4 { gap: var(--space-4); }

/* The heading of the "Captured today" card pulls its own subtitle up under
   it; the subtitle is not a heading, so it does not inherit the weight. */
.nudge-up { margin-top: -6px; }
.weight-normal { font-weight: normal; }

/* Layout one-offs that were written inline. Named, because "max-width:420px"
   in the markup says the number but not the reason. */
.panel-centered { max-width: 460px; margin: 0 auto; }
.field-narrow { max-width: 420px; }
.field-fill { flex: 1; }
.items-end { align-items: end; }
.align-top { align-items: flex-start; }
.textarea-tall { min-height: 90px; }

/* One size for a button that sits in or beside a table row. This was written
   inline ten times in two different sizes - 4px/12px in the generated rows,
   6px/13px on the Excel link - which is the same control looking like two. */
.btn.btn-compact, button.btn-compact { padding: var(--space-1) var(--space-3); font-size: 12px; }

/* The compact picker in a table row ("Merge into..." on the Team page). It
   keeps the browser's own arrow - it sits outside a .field, so the chevron
   above does not reach it. */
.select-compact { padding: var(--space-1) var(--space-2); font-size: 12px; max-width: 150px; }

.flex { display: flex; align-items: center; gap: 10px; }
.flex-between { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
a.filelink { color: var(--accent); font-weight: 600; text-decoration: none; }
a.filelink:hover { text-decoration: underline; }
hr.sep { border: none; border-top: 1px solid var(--border); margin: 18px 0; }

/* Colour-scheme toggle in the top bar (VIS-005: one click, any screen).
   Injected by theme.js, so it exists on every page including the sign-in
   page. Styled as a quiet outline button so it does not compete with the
   navigation. */
/* Tag/Nacht steht immer in der oberen rechten Ecke der Leiste.
   Vorher hing der Knopf hinten an der Navigation: auf einer Zeile war das die
   Ecke, auf dem Handy aber das Ende der letzten umgebrochenen Zeile - also
   unten rechts, wo ihn niemand sucht. Absolut gesetzt steht er in beiden
   Fällen gleich: auf einer Zeile bündig mit ihr, bei mehreren oben rechts
   neben dem Namen. Den Platz dafür hält die Leiste mit padding-right frei,
   damit nichts darunter läuft, und min-width hält ihn gleich breit, damit er
   beim Wechsel von „Dark" zu „Day" nicht springt. */
.scheme-toggle {
  position: absolute;
  top: 19px;
  right: 24px;
  min-width: 56px;
  background: transparent;
  color: var(--on-brand);
  border: 1px solid var(--overlay-brand-hover);
  padding: 6px 12px;
  font-size: 13px;
  font-weight: 600;
  border-radius: 8px;
  opacity: 0.9;
}
.scheme-toggle:hover { background: var(--overlay-brand-hover); opacity: 1; }

/* One block per contact on the capture form. Bordered rather than plain, so
   at three contacts it stays obvious which fields belong to which person. */
.contact-block {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin-bottom: 12px;
  background: var(--surface-sunken);
}
.contact-block:last-child { margin-bottom: 0; }
.contact-block-head { margin-bottom: 10px; }
.contact-block-title { font-size: 13.5px; }
.contact-remove { padding: 5px 12px; font-size: 12.5px; }
/* Salutation is a two-word field next to two name fields. */
.contact-grid { grid-template-columns: 0.7fr 1.3fr 1.3fr; }
@media (max-width: 720px) {
  /* Salutation and first name share the first row; the surname gets the full
     width on the second, because on a phone a 0.8fr surname field showed
     about eight characters of "van der Berg". */
  .contact-grid { grid-template-columns: 0.8fr 1fr; }
  .contact-grid > :nth-child(3) { grid-column: 1 / -1; }
}
.contact-ticks {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px 20px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
@media (max-width: 720px) { .contact-ticks { grid-template-columns: 1fr; } }

/* Marks a mandatory field. Small and quiet - findable when someone looks for
   it, without reading as an error while nothing is wrong. */
.required-tag {
  display: inline-block;
  margin-left: 8px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--color-warning);
  color: var(--on-status);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3px;
  vertical-align: middle;
}
/* A field that was missing when Save was pressed. An outline rather than more
   text: the message is already in the toast at the top, and what is needed
   here is only "look at this one". */
.missing input, .missing select, .radio-row.missing {
  border: 2px solid var(--color-alarm) !important;
  border-radius: 8px;
}
.radio-row.missing { padding: 6px 10px; }

/* Ein Name, den die Teamliste nicht kennt. Kein Fehler, sondern eine Frage:
   entweder vertippt, oder der Kollege fehlt noch in der Liste - und dann steht
   der Knopf gleich daneben. */
.unknown-name {
  margin: 6px 0 0;
  font-size: 13px;
  color: var(--color-foreground-2);
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
/* Die Rollenauswahl auf der Anmeldeseite. Ankreuzen statt auswaehlen, weil die
   Rollen nicht aufeinander aufbauen: wer zwei braucht, kreuzt zwei an, und jede
   angekreuzte Rolle fragt nach ihrem eigenen Passwort. */
.role-set {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: 10px 14px 14px;
  margin: 0;
}
.role-set legend {
  padding: 0 6px;
  font-size: 13px;
  color: var(--color-foreground-2);
}
.role-row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px 10px;
  align-items: center;
  padding: 10px 0;
  border-top: 1px solid var(--color-border);
}
.role-row:first-child {
  border-top: none;
}
.role-row input[type="checkbox"] {
  width: 20px;
  height: 20px;
  margin: 0;
}
.role-row label {
  margin: 0;
  font-weight: 600;
  cursor: pointer;
}
.role-row .role-hint,
.role-row input[type="password"] {
  grid-column: 2;
}
.role-row .role-hint {
  margin: 0;
}
/* Das Passwortfeld erscheint erst mit dem Haken: ein Feld, in das man nicht
   tippen soll, ist eine Frage, die gerade niemand stellt. */
.role-row input[type="password"] {
  display: none;
  margin-top: 8px;
}
.role-row.is-on input[type="password"] {
  display: block;
}

/* Das Änderungsprotokoll eines Leads (3.5.0). Bewusst schmucklos: eine Liste
   von Zeitpunkten, darunter je Zeile ein Feld mit Vorher und Nachher. Wer hier
   liest, sucht eine Zeile - nicht ein Layout. */
.log-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.log-entry + .log-entry { border-top: 1px solid var(--border); padding-top: 12px; }
.log-head {
  font-size: 13px;
  font-weight: 600;
  color: var(--color-foreground-2);
}
.log-body { font-size: 14px; margin-top: 3px; }
.log-changes {
  list-style: none;
  margin: 4px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: 14px;
}
.log-changes > li { line-height: 1.45; }

/* ==========================================================================
   Telefon: Erfassung und Feedback (3.10.0)
   --------------------------------------------------------------------------
   Beide Formulare werden am Stand am Telefon ausgefüllt, im Stehen, mit einer
   Hand. Gemessen auf 390x844 waren das vorher 4161px bzw. 2917px Seitenlänge -
   fünf bzw. dreieinhalb Bildschirme, und „Speichern" lag am unteren Ende des
   fünften. Drei Dinge ändern sich deshalb: die Leiste wird flacher, die Karten
   rücken zusammen, und die Knopfzeile bleibt am unteren Rand stehen.

   Der Rest der App bleibt, wie er war: die Klasse dense-mobile steht nur auf
   index.html und feedback.html. Die Übersicht ist eine breite Tabelle und die
   Berichte sind zum Lesen da - dort hilft Verdichtung nicht.
   ========================================================================== */

/* Die Leiste gilt für alle Seiten: sie ist überall dieselbe, und eine Leiste,
   die auf zwei Seiten anders hoch ist als auf den übrigen, springt beim
   Wechseln. Vorher nahm sie am Telefon rund ein Drittel des Bildschirms. */
@media (max-width: 700px) {
  /* Kein Platzhalter rechts wie auf dem Desktop: hier steht die Navigation
     ohnehin unter dem Knopf, und ihr die Breite zu nehmen kostet eine
     zusätzliche Zeile. */
  header.topbar { padding: 9px 14px; gap: 6px; }
  /* Kleiner, damit die erste Zeile - die genauso hoch sein muss - die Leiste
     nicht in die Höhe treibt: am Telefon zählt jede Zeile gegen das Formular. */
  header.topbar .scheme-toggle {
    top: 9px;
    right: 14px;
    min-width: 46px;
    padding: 3px 9px;
    font-size: 12px;
  }
  header.topbar .brand { font-size: 14px; --scheme-toggle-height: 22px; }
  header.topbar nav { gap: 3px; }
  header.topbar nav a,
  header.topbar nav .nav-group-btn { padding: 7px 10px; font-size: 13px; }
  header.topbar nav .nav-group-btn { padding-right: 22px; }
  header.topbar nav .nav-group-btn::after { right: 8px; }
  .scheme-toggle { padding: 5px 10px; margin-left: 6px; font-size: 12px; }
}

@media (max-width: 720px) {
  body.dense-mobile main { padding: 14px 14px 24px; }
  body.dense-mobile h1 { font-size: 19px; }
  body.dense-mobile h2 { font-size: 15px; margin: 18px 0 10px; }
  /* Der Vorspann bleibt stehen - er nennt die Pflichtfelder, und die stehen
     sonst nirgends. Er wird nur kleiner gesetzt. */
  body.dense-mobile > main > .subtitle { font-size: 12.5px; line-height: 1.35; margin-bottom: 12px; }
  body.dense-mobile .card { padding: 14px; margin-bottom: 12px; }
  body.dense-mobile .grid { gap: 10px; }
  body.dense-mobile fieldset { padding: 10px 12px; }
  body.dense-mobile .contact-block { padding: 12px; }

  /* Die Knopfzeile bleibt am unteren Rand stehen, solange das Formular läuft.
     Sie klebt innerhalb des Formulars, nicht auf der Seite: sobald man unter
     dem Formular ankommt, scrollt sie mit weg und gibt die Tagesliste frei.
     Volle Breite über die negativen Ränder, damit die Trennlinie durchgeht. */
  body.dense-mobile .form-actions {
    position: sticky;
    bottom: 0;
    z-index: 20;
    margin: 0 -14px;
    padding: 10px 14px calc(10px + env(safe-area-inset-bottom, 0px));
    background: var(--surface-page);
    border-top: 1px solid var(--border);
    flex-wrap: wrap;
  }
  body.dense-mobile .form-actions button,
  body.dense-mobile .form-actions .btn { flex: 1 1 auto; justify-content: center; }
  /* Löschen rutscht in eine eigene Zeile, statt sich rechts an „Speichern" zu
     drängen: ein Fehlgriff daneben ist hier nicht rückgängig zu machen. */
  body.dense-mobile .form-actions .danger { margin-left: 0; flex-basis: 100%; }
}

/* Ankreuzfelder groß genug für einen Finger. 40px ist das Maß, unter dem am
   Stand danebengetippt wird; die Zahlenreihen der Detailbewertung werden
   dafür zu einem Raster wie die NPS-Reihe darüber. */
@media (max-width: 620px) {
  body.dense-mobile .radio-row > label { min-height: 40px; }
  body.dense-mobile .checkbox-grid label { min-height: 38px; }
  body.dense-mobile input[type="radio"],
  body.dense-mobile input[type="checkbox"] { width: 18px; height: 18px; }

  .rating-row { padding: 10px 0; }
  .rating-row .radio-row {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 6px;
  }
  .rating-row .radio-row > label {
    justify-content: center;
    gap: 5px;
    min-height: 42px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface-card);
    font-size: 13.5px;
  }
  /* Das Knopffeld selbst bleibt sichtbar - ohne :has() ist an dieser Stelle
     sonst nicht zu erkennen, was angekreuzt ist. */
  .rating-row .radio-row > label:has(input:checked) {
    border-color: var(--color-btn-active);
    background: var(--surface-sunken);
  }
  .rating-row .rating-clear { flex: 0 0 auto; }
}

/* Die zusammenklappbaren Abschnitte (3.10.0, Verhalten in collapse.js).
   Der Knopf steht im DOM jeder markierten Karte, wird aber nur am Telefon
   gezeigt: auf einem breiten Bildschirm ist das Formular eine Seite, über die
   man mit dem Rad läuft, und dort kostet jeder Klapp-Knopf einen Klick ohne
   Gegenwert. Deshalb entscheidet allein die Bildschirmbreite - und beim Drehen
   des Geräts ist nichts nachzuführen. */
/* Farben stehen hier und nicht erst in der Telefonregel: der Knopf erbt sonst
   die Knopffarben (weiss auf Markenblau) und stuende, sichtbar gemacht, weiss
   auf der hellen Seitenflaeche. tests/theme.js misst den Kontrast jedes
   Textelements - auch der ausgeblendeten, und genau das hat es gefunden. */
.card-toggle {
  display: none;
  background: none;
  color: var(--color-foreground);
}

@media (max-width: 720px) {
  body.dense-mobile [data-collapse] > h2 { display: none; }

  body.dense-mobile [data-collapse] > .card-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    width: calc(100% + 28px);
    margin: -14px -14px 0;
    padding: 14px;
    min-height: 48px;
    background: none;
    border: 0;
    border-radius: var(--radius);
    color: var(--color-foreground);
    font-family: inherit;
    font-size: 15px;
    font-weight: 700;
    text-align: left;
  }
  body.dense-mobile [data-collapse].is-closed > .card-toggle { margin-bottom: -14px; }
  body.dense-mobile [data-collapse].is-closed > *:not(.card-toggle) { display: none; }

  /* „3 entries" neben der Überschrift. Ohne diese Angabe sieht ein
     zugeklappter Abschnitt mit Inhalt aus wie ein leerer - und das ist der
     einzige Weg, auf dem Zusammenklappen Daten verlieren kann. */
  body.dense-mobile .card-toggle-state {
    position: relative;
    flex: 0 0 auto;
    padding-right: 20px;
    font-size: 12px;
    font-weight: 600;
    color: var(--color-foreground-2);
  }
  /* Zugeklappt zeigt der Pfeil nach unten („hier geht etwas auf"),
     aufgeklappt nach oben. */
  body.dense-mobile .card-toggle-state::after {
    content: "";
    position: absolute;
    right: 2px;
    top: 50%;
    margin-top: -7px;
    border: 5px solid transparent;
    border-bottom-color: currentColor;
  }
  body.dense-mobile [data-collapse].is-closed .card-toggle-state::after {
    margin-top: -2px;
    border-bottom-color: transparent;
    border-top-color: currentColor;
  }
}
