/**
 * Was die Website noch nie gebraucht hat.
 *
 * Die übernommenen 27 Seiten kommen mit `fileadmin/admin/css/style.css` aus —
 * jede Form darin ist einmal für einen echten Baustein geschrieben worden.
 * Drei der elf Typen im Studio hat es in TYPO3 nie gegeben (`steps`, `quote`)
 * oder sie treten in einer Form auf, für die nie eine Regel entstand
 * (`hero`-Vollbild, `text` im Kasten, Untertitel einer Karte). Sie fielen
 * deshalb auf das nackte Bootstrap-Reboot zurück: sichtbar, aber ohne
 * Zusammenhang mit dem Rest der Seite — genau der Eindruck, den die
 * Auftraggeberin mit „und wenn es klappt, sieht es nicht gut aus" meinte.
 *
 * Diese Datei füllt nur diese Lücken. Sie erfindet keine zweite Gestaltung:
 * jede Regel hier ist aus `style.css` abgeleitet und nennt die Stelle, von der
 * sie kommt. Die Farben sind die der Website — #DB1C1C rot (die Symbolkreise),
 * #FFE100 gelb, #293033 dunkel, #f2f2f2 hellgrau, #111111 Fließtext — und die
 * Schriften sind die geladenen (Roboto Light/Medium/Bold, OpenSans
 * Regular/Bold).
 *
 * EINGEBUNDEN WIRD SIE NUR, WENN DIE SEITE EINE DIESER KLASSEN AUSGIBT.
 * Siehe `apps/site/src/pages/[...slug].astro`: ein bedingungsloses `<link>`
 * hinge an allen 27 Seiten, und die Seiten, die diese Bausteine gar nicht
 * enthalten, sollen sich Byte für Byte nicht ändern.
 */


/* ====================================================================== *
 *  Schritte  (`steps`)
 * ====================================================================== */

/**
 * Die Ziffer im roten Kreis.
 *
 * Den Kreis liefert schon die Website: `.textblock i` in style.css:521 —
 * 80px, rund, #DB1C1C, weiße Schrift. `steps.mjs` gibt die Nummer deshalb als
 * `<i>` aus und trifft diese Regel (der Grund steht dort). Hier steht nur,
 * was der Bestandsselektor nicht wissen kann: er ist für Symbole geschrieben,
 * und ein `<i>` stellt der Browser kursiv — bei einem FontAwesome-Zeichen
 * fällt das nicht auf, bei einer Ziffer schon.
 *
 * NUR FÜR DIE ZIFFER (28.09.2026, im Browser): ohne die Einschränkung traf
 * die Regel auch `i.step-number.fa-light.fa-truck` — ein gewähltes Symbol in
 * den Schritten und jedes Symbol eines eigenen Typs. „Roboto Medium" (0,2,0)
 * schlug `.fa-light` (0,1,0), und auf der Website stand ein leeres Kästchen
 * im roten Kreis. Ein Symbol steht jetzt wie im Bestand
 * (`.textblock_icon > i.fal`), in Schrift, Gewicht und Grösse aus
 * `.textblock i` und seiner Symbolklasse. `:where()` hält die Spezifität bei
 * 0,2,0 — für die Ziffer ändert sich nichts. Misst `audit:bausteine`,
 * Prüfung (7).
 */
.ce-steps .step-number:where(:not([class*="fa-"])) {
  font-family: 'Roboto Medium', sans-serif;
  font-style: normal;
  font-size: 2rem;
  line-height: 1;
  letter-spacing: 0;
  /* `.textblock i` setzt font-weight:100. Die Roboto-Schnitte sind eigene
     Familien, das Gewicht wirkt dort nicht — hier stünde sonst die
     synthetische Magerung des Browsers. */
  font-weight: normal;
  flex: none;
}

/* Die Überschrift eines Schritts ist ein `h3` (versal, Roboto Medium). Ohne
   Deckel steht sie bei langen Titeln zu eng am Kreis. */
.ce-steps .textblock h3 {
  padding-top: 0;
  padding-bottom: 0.5rem;
}

/**
 * Im Bestand steht in diesem `d-flex` genau EIN Absatz — siehe
 * /leistungen/privatumzug.html, `<div class="d-flex justify-content-center">
 * <p class="text-center">…</p></div>`. Der Text eines Schritts kommt aber aus
 * dem Editor und kann zwei haben, und zwei Absätze in einer Flex-Zeile stehen
 * NEBENEINANDER. Nur die letzte Zeile umstellen: bei den beiden darüber
 * (Kreis, Überschrift) zentriert `justify-content-center` waagerecht, und in
 * einer Spalte täte es das nicht mehr.
 */
.ce-steps .textblock > .d-flex:last-child {
  flex-direction: column;
  align-items: center;
}

/**
 * „Mit Pfeilen".
 *
 * Die Variante ließ die Nummer ersatzlos weg und setzte nichts an ihre
 * Stelle — übrig blieben drei Textspalten nebeneinander, denen man den Ablauf
 * nicht ansah. Der Pfeil ist jetzt der VERBINDER zwischen zwei Schritten,
 * nicht ein Ersatz für ihre Nummer.
 *
 * Gezeichnet aus zwei Rändern, nicht als Zeichen aus FontAwesome: ein
 * fehlendes Schriftzeichen hinterlässt ein Kästchen, ein fehlender Rand
 * hinterlässt nichts.
 */
.ce-steps-arrows .col {
  position: relative;
}

.ce-steps-arrows .col:not(:last-child)::after {
  content: "";
  position: absolute;
  width: 18px;
  height: 18px;
  border-top: 3px solid #DB1C1C;
  border-right: 3px solid #DB1C1C;
  /* untereinander: der Pfeil zeigt nach unten, in den Innenabstand von
     `.textblock` hinein (style.css:520 — 3rem 1rem 2rem). */
  bottom: 0.75rem;
  left: 50%;
  transform: translateX(-50%) rotate(135deg);
}

/* Ab `lg` stehen alle Schritte in EINER Reihe (`row-cols-lg-N`, siehe
   steps.mjs) — erst dort darf der Pfeil nach rechts zeigen. `.col` trägt die
   halbe Rinne als Innenabstand, seine Kante IST also die Mitte zwischen zwei
   Kästen; -9px ist die halbe Pfeilbreite. Die Höhe ist die Kreismitte:
   3rem Innenabstand von `.textblock` plus der halbe 80px-Kreis. */
@media (min-width: 992px) {
  .ce-steps-arrows .col:not(:last-child)::after {
    top: calc(3rem + 40px);
    bottom: auto;
    left: auto;
    right: -9px;
    transform: translateY(-50%) rotate(45deg);
  }
}

/**
 * Mehr als vier Schritte passen nicht in eine Reihe.
 *
 * `steps.mjs` deckelt die Spaltenzahl bei vier, ein Ablauf darf aber mehr
 * Schritte haben. Der fünfte rutscht dann in eine zweite Reihe — und der
 * Pfeil des vierten zeigte aus der Reihe hinaus ins Leere, genau der Fehler,
 * den die Variante bei `row-cols-md-2` vermeidet. Gelesen wird die
 * Spaltenklasse der Reihe selbst, damit hier nichts doppelt gepflegt wird.
 */
@media (min-width: 992px) {
  .ce-steps-arrows .row-cols-lg-2 > .col:nth-child(2n)::after,
  .ce-steps-arrows .row-cols-lg-3 > .col:nth-child(3n)::after,
  .ce-steps-arrows .row-cols-lg-4 > .col:nth-child(4n)::after {
    content: none;
  }
}

/* Auf dunkler Fläche (#293033) verschwindet das Rot fast. Dort trägt der
   Pfeil die zweite Akzentfarbe der Website. Die Fläche sitzt auf dem
   Abschnitt, der den Baustein umschließt (`section()` in index.mjs), nie auf
   dem Rahmen des Bausteins selbst — deshalb nur als Vorfahre. */
.content .frame-layout-3 .ce-steps-arrows .col:not(:last-child)::after {
  border-color: #FFE100;
}


/* ====================================================================== *
 *  Zitat  (`quote`)
 * ====================================================================== */

/**
 * `figure` bekommt von Bootstraps Reboot `margin: 0 0 1rem` und sonst nichts;
 * `figcaption` bekommt gar nichts. Ein Zitat sah deshalb aus wie ein Absatz
 * mit einer Zeile darunter.
 *
 * Die Zitatform selbst ist nicht neu — style.css:159 kennt `blockquote`
 * bereits (roter Balken links, roter Text, grauer Grund). Sie gilt für Zitate
 * im Fließtext und bleibt dort unangetastet; die beiden Ausprägungen des
 * Bausteins bauen darauf auf.
 */
.ce-quote-figure {
  margin: 0;
}

.ce-quote blockquote {
  margin: 0;
  font-family: 'Roboto Light', sans-serif;
  font-size: 1.4rem;
  line-height: 2.2rem;
}

.ce-quote blockquote p {
  margin: 0 0 0.75rem;
}

.ce-quote blockquote p:last-child {
  margin-bottom: 0;
}

/**
 * Die Quelle.
 *
 * Versal mit Sperrung — dieselbe Form, in der die Website eine Funktion unter
 * einem Namen setzt (`.box.team .position`, style.css:549). Der Gedankenstrich
 * gehört zur Zitierweise, nicht zum Namen, und steht deshalb hier.
 */
.ce-quote-source {
  margin-top: 1.25rem;
  font-size: 0.875rem;
  line-height: 1.6rem;
  color: #111111;
  text-transform: uppercase;
  letter-spacing: 2px;
}

.ce-quote-source::before {
  content: "\2014\00a0";
  color: #DB1C1C;
}

.ce-quote-author {
  font-family: 'OpenSans Bold', sans-serif;
}

.ce-quote-role {
  font-family: 'OpenSans Regular', sans-serif;
}

/* „Schlicht": kein Kasten. Nur der rote Balken, in der Stärke, in der die
   Website eine Überschrift unterstreicht (`h1:after`, 5px #DB1C1C). */
.ce-quote-plain blockquote {
  background: transparent;
  border-left: 5px solid #DB1C1C;
  padding: 0 0 0 1.5rem;
}

.ce-quote-plain .ce-quote-source {
  padding-left: calc(1.5rem + 5px);
}

/* „Im Kasten": der graue Kasten der Website (`.box-inner.grau`,
   style.css:539 — #f2f2f2, 2rem). Der Balken bleibt, der zweite graue Grund
   im `blockquote` muss weg, sonst liegt Grau auf Grau. */
.ce-quote-boxed .ce-quote-figure {
  background: #f2f2f2;
  border: 1px solid #f2f2f2;
  padding: 2rem;
}

.ce-quote-boxed blockquote {
  background: transparent;
  border-left: 5px solid #DB1C1C;
  padding: 0 0 0 1.5rem;
}

.ce-quote-boxed .ce-quote-source {
  padding-left: calc(1.5rem + 5px);
}

/**
 * Ein Kasten auf einer Fläche.
 *
 * Die Hintergrundfläche eines Bausteins sitzt auf dem Abschnitt, der ihn
 * umschließt (`section()` in `packages/render/src/v2/index.mjs`), nicht auf
 * seinem eigenen Rahmen — deshalb steht `frame-layout-N` hier immer als
 * Vorfahre.
 *
 * Ohne diese drei Fälle verschwindet der Kasten: auf Hellgrau liegt sein
 * #f2f2f2 auf demselben #f2f2f2, auf Gelb wirkt es schmutzig, und auf Dunkel
 * stünde weißer Text (den `.content .frame-layout-3` setzt) auf hellem Grund.
 */
.content .frame-layout-1 .ce-quote-boxed .ce-quote-figure,
.content .frame-layout-2 .ce-quote-boxed .ce-quote-figure {
  background: #fff;
  border-color: #fff;
}

.content .frame-layout-3 .ce-quote-boxed .ce-quote-figure {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.08);
}

.content .frame-layout-3 .ce-quote blockquote,
.content .frame-layout-3 .ce-quote-source {
  color: #fff;
}

/* Das Rot des `blockquote` (style.css:159) steht auf #FFE100 fast ohne
   Kontrast. Der Balken behält es, der Text nicht. */
.content .frame-layout-2 .ce-quote blockquote {
  color: #111111;
}

/* Der Gedankenstrich ist Rot wie der Balken — und wird auf #293033 genauso
   unsichtbar wie er. */
.content .frame-layout-3 .ce-quote-source::before {
  color: #FFE100;
}


/* ====================================================================== *
 *  Titelbild über die volle Breite  (`hero`, Variante „full")
 * ====================================================================== */

/**
 * TYPO3 kannte diese Variante nicht — alle dreizehn übernommenen Titelbilder
 * stehen zweispaltig. Ohne Regel lag das Bild in der Bildergalerie-Hülle
 * (`ce-gallery` → `ce-outer` → `ce-inner` → `ce-row` → `ce-column`), die für
 * Spalten gebaut ist, und blieb bei seiner natürlichen Breite stehen.
 */
.hero-full .ce-gallery,
.hero-full .ce-outer,
.hero-full .ce-inner,
.hero-full .ce-row,
.hero-full .ce-column {
  width: 100%;
  float: none;
  right: auto;
}

.hero-full figure.image {
  margin: 0;
  display: block;
}

.hero-full .image-embed-item {
  width: 100%;
  height: auto;
  max-height: 460px;
  object-fit: cover;
  display: block;
}

.hero-caption {
  padding-top: 2rem;
}

/* `h1:after` (style.css) hängt den roten Balken 15px über die Unterkante der
   Überschrift. Der Innenabstand darunter muss also bleiben. */
.hero-caption h1 {
  padding-bottom: 2rem;
}

.hero-caption > p:last-child {
  margin-bottom: 0;
}


/* ====================================================================== *
 *  Text im Kasten  (`text`, Variante „boxed")
 * ====================================================================== */

/**
 * `.content .frame-layout-0 {background: transparent}` (style.css:186) hat
 * zwei Klassen und schlägt ein einfaches `.ce-boxed`. Deshalb hier ebenfalls
 * zweistufig — und weil diese Datei nach style.css geladen wird, gewinnt sie
 * bei gleicher Stärke.
 */
.content .ce-boxed {
  background: #f2f2f2;
  padding: 2rem;
}

.content .ce-boxed > :last-child {
  margin-bottom: 0;
}

@media (min-width: 768px) {
  .content .ce-boxed {
    padding: 3rem;
  }
}

/* Dieselben drei Flächen wie beim Zitat im Kasten: sonst liegt Grau auf Grau,
   oder weißer Text aus `.content .frame-layout-3` auf hellem Grund. */
.content .frame-layout-1 .ce-boxed,
.content .frame-layout-2 .ce-boxed {
  background: #fff;
}

.content .frame-layout-3 .ce-boxed {
  background: rgba(255, 255, 255, 0.08);
}


/* ====================================================================== *
 *  Untertitel einer Karte  (`grid`, Variante „cards")
 * ====================================================================== */

/* Dieselbe Form wie die Funktion unter einem Namen in den Personenkarten
   (`.box.team .position`), nur mittig wie alles in der Kachel. */
.ce-cardbox-position {
  margin: 0 0 1rem;
  text-align: center;
  font-size: 0.875rem;
  line-height: 1.4rem;
  text-transform: uppercase;
  letter-spacing: 2px;
}


/* ====================================================================== *
 *  Umzugsplaner  (`planer`)
 * ====================================================================== *
 *
 * Die Reiterleiste, die Mengenzeile und die Summe. Die Farben stehen HIER
 * und nicht in `zimmer.css`: jene Datei liegt nur auf `/umzugsplaner.html`,
 * und ein Planer auf einer anderen Seite sähe sonst kaputt aus, ohne dass
 * eine Prüfung es meldet.
 *
 * `.mandatory` ist in keiner übernommenen CSS-Datei definiert (nachgezählt:
 * null Treffer). Ohne die Regel unten wäre das Pflichtsternchen schwarz und
 * damit von der Beschriftung nicht zu unterscheiden.
 */

.planer-einleitung { margin-bottom: 1.5rem; }

.planer-ohne-skript {
  padding: 1rem 1.25rem;
  border-left: 4px solid #ec3013;
  background: #f8f4f4;
}

.mandatory { color: #ec3013; padding-left: 2px; }

/* -- Reiterleiste ------------------------------------------------------ */

/* Die Optik ist die des ORIGINALS, nicht eine eigene.
   Hier standen bis zum 02.09.2026 rote Textlinks mit Unterstrich — eine
   Neuerfindung, die die weiterhin geladenen cokuna-Stylesheets übersteuerte.
   Der Auftraggeber im Video: „Der Umzugsplaner funktioniert hier komplett
   anders als auf der Live-Webseite. Ich will eine vollständige visuelle
   Parität." Die Werte kommen aus `zimmer.css` des Spiegels:
   Reiter grau gefüllt (#eee), aktiv gelb (#FFE100), 1.5em, ohne Rahmen. */

.nav-umzug { margin-bottom: 1.5rem; flex-wrap: wrap; }

/*
 * VON DER ÜBERSCHRIFT BIS ZU DEN REITERN 64 PX, wie im Original.
 *
 * DER FEHLERFALL (Tims Videos vom 23.09.2026, Punkt 9): zwischen
 * „Umzugsplaner" und der Reiterleiste lagen 148 px — gemessen am Bau vom
 * Textende der h1: 32 Innenabstand + 32 Aussenabstand der h1 selbst (die hat
 * der Spiegel auch), dazu 64 unter dem Rahmen der Überschrift
 * (`frame-space-after-medium`) und 20 über der Leiste. Die letzten zwei hat
 * der Spiegel nicht. Genommen werden sie nur hier, über die Hülle — am
 * Inhalt ändert sich nichts, und beide Wähler treffen nur die Leiste eines
 * Planers und den Rahmen unmittelbar davor.
 */
.frame-type-ce_planer ul.nav-umzug:not(.u-unstyled) { margin-top: 0; }
.frame-space-after-medium:has(+ .frame-type-ce_container .frame-type-ce_planer) { padding-bottom: 0; }

.nav-umzug .nav-item { border: 0; background-color: transparent; margin-left: 0; }

.nav-umzug .nav-link {
  width: 100%;
  border: none;
  border-radius: 0;
  background-color: #eee;
  /* Bootstraps Vorgabe, die auch das Original nutzt. `0.75rem 0.5rem` machte
     jeden Reiter 8 Bildpunkte hoeher — bei vier Reitern 32. */
  padding: 0.5rem 1rem;
  color: #000;
  font-size: 1.5em;
  text-align: center;
}

.nav-umzug .nav-link i { display: inline-block; margin-right: 0.4rem; }

.nav-umzug .nav-link:hover,
.nav-umzug .nav-link.active { background-color: #FFE100; color: #000; }

/* Gesperrt, aber anfahrbar: `aria-disabled` statt `disabled`, damit der
   Reiter in der Tabulatorreihenfolge bleibt und die Begründung erreicht,
   wer sie braucht. Der Zeiger sagt es trotzdem. */
.nav-umzug .nav-link[aria-disabled="true"] { color: #767676; background-color: #f6f6f6; cursor: not-allowed; }

.nav-umzug .nav-link:focus-visible { outline: 2px solid #000; outline-offset: -2px; }

/*
 * VIER REITER IN EINER ZEILE, auch am Telefon (Tims Videos vom 23.09.2026:
 * Umzugsgut ist wieder ein Reiter).
 *
 * Die eingefrorene `style.css` stellt unter 768 px `ul.nav-umzug` auf
 * `flex-direction: column` (Spezifität 0,2,1) und jede `.cok_magicline`-
 * Aufschrift auf `1.2rem !important` — vier Reiter stünden dort als vier
 * volle Zeilen übereinander. Übersteuert wird NUR der Planer: seine Leiste
 * trägt `data-planer-steuer`, das Attribut hebt die Spezifität über die
 * eingefrorene Regel (0,3,1), und die Schriftgrösse geht gegen `!important`
 * nur mit `!important`. Symbol über der Aufschrift, jeder Reiter ein
 * Viertel; ein langer Name („Be-/Entladeort") bricht im Reiter, statt die
 * Leiste zu sprengen. Dieselbe Grenze wie `style.css` (`max-width: 767px`),
 * damit zwischen beiden keine Breite liegt, an der keine von beiden gilt.
 */
@media (max-width: 767px) {
  ul.nav-umzug[data-planer-steuer]:not(.u-unstyled) {
    flex-direction: row;
    flex-wrap: nowrap;
    gap: 0.25rem;
  }
  .nav-umzug[data-planer-steuer] .nav-item { flex: 1 1 0; min-width: 0; }
  .nav-umzug[data-planer-steuer] .nav-item.cok_magicline .nav-link {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.2rem;
    height: 100%;
    padding: 0.4rem 0.2rem;
    font-size: 0.8rem !important;
    line-height: 1.2;
    overflow-wrap: anywhere;
  }
  .nav-umzug[data-planer-steuer] .nav-link i { margin-right: 0; }
}

/*
 * Und zwischen 768 und 991 px: dort gilt die Regel oben nicht mehr, die
 * Leiste ist nur 696 px breit, und vier Reiter in 1.5em brauchen 898 —
 * „Services" rutschte in eine zweite Zeile und zog sich über die volle
 * Breite (gemessen am Bau, 23.09.2026). Mit drei Reitern passte es knapp
 * (659 px). Kleiner gesetzt und nicht umbrechend, wieder nur im Planer;
 * ab 992 px ist die Leiste 936 px breit und bleibt, wie sie war.
 */
@media (min-width: 768px) and (max-width: 991.98px) {
  ul.nav-umzug[data-planer-steuer] { flex-wrap: nowrap; }
  .nav-umzug[data-planer-steuer] .nav-item { flex: 1 1 auto; min-width: 0; }
  .nav-umzug[data-planer-steuer] .nav-link { padding: 0.5rem; font-size: 1.1rem; }
}

/* -- Schritte ---------------------------------------------------------- */

/* Der Schrittname steht im Reiter — ein zweites Mal darueber steht er im
   Original nirgends, und er kostete 56 Bildpunkte. Fuer Vorleseprogramme
   bleibt er: der Bereich wird ueber `aria-labelledby` ohnehin am Reiter
   benannt, die Ueberschrift gibt ihm zusaetzlich eine Gliederungsebene. */
.planer-schritttitel {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Der Bereich bekommt beim Wechsel den Fokus (`tabindex="-1"`) — ohne diese
   Regel zöge der Browser einen Rahmen um den ganzen Schritt. */
.tab-pane[data-planer-schritt-feld]:focus { outline: none; }

.planer-schrittfehler:empty { display: none; }

.planer-schrittfehler {
  margin: 0 0 1rem;
  padding: 0.75rem 1rem;
  border-left: 4px solid #ec3013;
  background: #fdf1ef;
  color: #a3200d;
}

.planer-gruppe { border: 0; padding: 0; margin: 0 0 2rem; }

.planer-gruppentitel {
  width: auto;
  float: none;
  margin-bottom: 0.75rem;
  font-size: 1rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
}

.planer-gruppenhinweis { margin: -0.25rem 0 1rem; color: #666; font-size: 0.875rem; }

/*
 * EINE HERVORGEHOBENE ÜBERSCHRIFT (Tims Videos vom 23.09.2026, Punkt 11):
 * „Besonderheiten" stand grau und klein als letzte Gruppe unter Services und
 * wurde übersprungen. Hervorgehoben steht sie auf der gelben Fläche des Hauses
 * (#FFE100, wie `.frame-layout-2` in style.css), ihr Hinweis in voller Grösse
 * und dunkel statt grau und klein.
 *
 * Die Legende FLIESST: eine Legende sitzt sonst auf der Rahmenlinie des
 * fieldset, und die Fläche finge erst auf halber Höhe der Überschrift an.
 * `float` + volle Breite holt sie in die Fläche; das nächste Element räumt.
 */
.planer-gruppe--betont {
  background: #ffe100;
  padding: 1rem 1.25rem 0.25rem;
}
.planer-gruppe--betont > .planer-gruppentitel { float: left; width: 100%; }
.planer-gruppe--betont > .planer-gruppentitel + * { clear: both; }
.planer-gruppe--betont > .planer-gruppenhinweis { margin: 0 0 1rem; color: #222; font-size: 1rem; }

/*
 * Die Erinnerung (`planer.js`, `erinnerungZeigen`): eine Frage, kein Fehler —
 * deshalb nicht im Fehlerrot des Planers (#ec3013), sondern weiss auf der
 * gelben Fläche mit dem Rot des Hauses als Kante. Kein `display` hier: das
 * `hidden` des Markups muss greifen.
 */
.planer-erinnerung {
  margin: 0 0 1rem;
  padding: 0.75rem 1rem;
  border-left: 4px solid #db1c1c;
  background: #fff;
  color: #222;
  font-weight: 700;
}

[data-planer-frage][aria-invalid="true"] { border-color: #ec3013; }



/*
 * Die Zimmerkachel ist ein `<button>` — und trug deshalb zwei Rahmen.
 *
 * Der Knopf steht da, weil die Kachel mit der Tastatur erreichbar sein muss
 * (`blocks/planer.mjs`, Absatz „172 Mengenfeldern": ohne ihn springt man an allen
 * vorbei auf „Daten absenden"). Er brachte aber seine Browservorgabe mit.
 * Im Browser nachgemessen am 04.09.2026:
 *
 *   Knopf:  border 2px outset rgb(0,0,0) · background rgb(239,239,239)
 *           · padding 1px 12px  (die 1 px oben/unten sind seine, die 12 px
 *                                links/rechts sind Bootstraps Rinne)
 *   .card:  border 1px solid rgba(0,0,0,.176)   (Bootstrap)
 *
 * Das Gelb sass damit 326 px breit in einer 354 px breiten grauen Fläche mit
 * schwarzem Rand. Im Original ist es ein randloses gelbes Quadrat.
 * `zimmer.css:9-19` setzt Gelb und `border-radius:0`, aber nie `border:0` —
 * die Datei ist eingefrorener Bestand und wird nicht angefasst.
 */
.planer-zimmer-block {
  margin-bottom: 2rem;
  /*
   * NUR oben und unten auf null — links und rechts gehören Bootstrap.
   *
   * Hier stand `padding: 0`, und der Kommentar oben führte die gemessenen
   * „1px 12px" als Vorgabe des Knopfes auf. Nachgemessen ist das falsch:
   * Chromes Knopfvorgabe ist `1px 6px`; die zwölf sind die halbe Rinne, die
   * `.row > *` aus `g-4` ablegt (`--bs-gutter-x: 1.5rem`). `padding: 0` nahm
   * beides — die gelben Kacheln stiessen danach mit 0 px aneinander, wo der
   * Abgriff 24 px führt.
   */
  padding-top: 0;
  padding-bottom: 0;
  border: 0;
  background: transparent;
  appearance: none;
  -webkit-appearance: none;
  font: inherit;
  color: inherit;
  text-align: inherit;
}

.planer-zimmer-block .card { border: 0; }

/* Der Knopf verliert oben seinen sichtbaren Zustand — der Tastaturweg braucht
   einen eigenen, sonst ist die Kachel erreichbar, aber unsichtbar gewählt. */
.planer-zimmer-block:focus-visible { outline: 3px solid #ec3013; outline-offset: 3px; }

.planer-position {
  align-items: center;
  padding: 0.4rem 0;
  border-bottom: 1px solid #eee;
}

.planer-position .inventarname { margin: 0; font-weight: 400; }

.planer-einheit { color: #888; font-size: 0.8125rem; }

.planer-position label { margin: 0; font-weight: 400; white-space: nowrap; }

/*
 * − und + um das Mengenfeld (Tims Videos vom 23.09.2026: „jedes Mal die
 * Tastatur").
 *
 * 44 px Tippfläche, wo Platz ist. Die Zeile darf dafür nicht länger werden:
 * Tim nannte die Liste schon so „eine Zumutung". Am Bau gemessen (erstes
 * Zimmer, 45 Posten, Summe der Zeilenhöhen): mit der Anzahl auf eigener Zeile
 * wuchs die Liste bei 390 px von 6531 auf 10221 px.
 *
 * Von 390 px bis `sm` stehen Anzahl, Montage und Entsorgung deshalb in EINER
 * Zeile, wie im Abgriff. `style.css:1067` hält jede `col-lg-1` im offenen
 * Dialog auf `min-width: 115px !important` — zwei Haken und die Anzahl
 * brauchten so 345 px, mehr als ein Telefon hat. Hier gilt dort `min-width:
 * auto`: die Haken so schmal wie ihr Titel, die Anzahl nimmt den Rest. Die
 * Haken geben 12 ihrer 24 px Rinne ab; die Knöpfe dürfen bis 34 px
 * schrumpfen, das Feld bis 2rem — für zwei Ziffern reicht das.
 *
 * Unter 390 px passte schon der Abgriff nicht in eine Zeile (Entsorgung brach
 * allein um). Dort steht die Anzahl auf eigener Zeile und beide Haken
 * darunter — gleich viele Zeilen wie vorher, aber keine verwaiste Zelle.
 *
 * Ab `lg` sitzt die Anzahl in der 115-px-Spalte des Abgriffs unter ihrem
 * Kopf. Dort werden die Knöpfe 30 px schmal, und die Zelle gibt 16 ihrer 24
 * px Rinne ab.
 *
 * `button:focus { outline: none }` (`style.css:232`) nimmt jedem Knopf den
 * Fokusrahmen; der Tastaturweg bekommt hier einen eigenen. Die Pfeile des
 * Zahlenfelds fallen weg — die Knöpfe tun dasselbe, und am Rechner standen
 * sonst zwei Bedienungen für eine Sache nebeneinander.
 */
.planer-mengenwahl {
  display: flex;
  justify-content: flex-start;
  align-items: stretch;
  gap: 4px;
}

.planer-mengenwahl button {
  flex: 0 1 44px;
  min-width: 34px;
  min-height: 44px;
  padding: 0;
  border: 1px solid #ced4da;
  border-radius: 0.25rem;
  background: #f5f5f5;
  color: #333;
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  touch-action: manipulation;
}

.planer-mengenwahl button:hover { background: #e9e9e9; }
.planer-mengenwahl button:active { background: #ddd; }
.planer-mengenwahl button:focus-visible { outline: 3px solid #ec3013; outline-offset: 2px; }

.planer-mengenwahl .planer-menge {
  flex: 0 1 4rem;
  width: 4rem;
  min-width: 2rem;
  height: auto;
  min-height: 44px;
  padding: 0 4px;
  text-align: center;
  -moz-appearance: textfield;
  appearance: textfield;
}

.planer-mengenwahl .planer-menge::-webkit-inner-spin-button,
.planer-mengenwahl .planer-menge::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }

@media (max-width: 575.98px) {
  .modal.show .modal-dialog .planer-position > .col-lg-1 { min-width: auto !important; }
}

/* 132 px = zwei Knöpfe zu 34, das Feld zu 2rem, zwei Lücken, die Rinne. */
@media (min-width: 390px) and (max-width: 575.98px) {
  .modal.show .modal-dialog .planer-position > .planer-anzahl { min-width: 8.25rem !important; }
  .invmodal .planer-position > .planer-haken { padding-left: 6px; padding-right: 6px; }
}

@media (max-width: 389.98px) {
  .invmodal .planer-position > .planer-anzahl { flex: 0 0 100%; }
}

@media (min-width: 992px) {
  .invmodal .planer-position > .planer-anzahl { padding-left: 4px; padding-right: 4px; }
  .planer-mengenwahl { justify-content: center; gap: 2px; }
  .planer-mengenwahl button { flex: 0 0 30px; min-width: 0; min-height: 32px; font-size: 1rem; }
  .planer-mengenwahl .planer-menge { min-height: 0; }
}

/* -- Der Haken der Einwilligung ---------------------------------------- */

/*
 * Angekreuzt sah aus wie nicht angekreuzt.
 *
 * `umzug.css` versteckt das Feld (`opacity: 0`, Zeile 1704) und zeichnet den
 * Kasten als `label::before`. Den Haken zeichnet dort
 * `.checkbox input[type=checkbox]:checked + label::after` — ein Wähler, der das
 * Feld als GESCHWISTER des Labels verlangt. Bis zum 04.09.2026 stand es bei
 * uns IM Label, also griff er nie: gemessen `content: none`. Seit main das
 * Feld vor das Label gestellt hat (`blocks/planer.mjs`,
 * Funktion `einwilligung`, Klasse `ce-einwilligung`), greift er — und zeichnet
 * das, was der Auftraggeber am 04.09.2026 als „ein bisschen schief" gemeldet
 * hat: die Schriftglyphe aus Zeile 1691, 5 px zu hoch. Die Regeln hier
 * ersetzen die Glyphe; die Kante des Kastens setzt der Block weiter unten.
 *
 * `umzug.css` ist eingefrorener Bestand und wird nicht angefasst. Der Haken
 * wird deshalb hier gezeichnet, mit Rändern statt einer Schriftglyphe: die
 * Glyphe des Bestands ist „Font Awesome 5 Pro", und ein fehlender Zeichensatz
 * ergäbe wieder ein Zeichen, das irgendwo hinragt.
 *
 * Der Kasten misst 20x20 bei left 0, top 10. Der Haken sitzt mittig darin und
 * bleibt auch gedreht innerhalb — `tools/planer-check.mjs` misst beides.
 *
 * Nur der Planer: `/kontakt.html` trägt dieselbe Bauform, lädt `umzug.css`
 * aber gar nicht und zeigt dort ein natives Ankreuzfeld wie der Spiegel.
 */
.ce-einwilligung input[type="checkbox"]:checked + label::after {
  content: "";
  left: 5px;
  top: 15px;
  width: 10px;
  height: 8px;
  padding: 0;
  border-left: 2px solid #1b1b1b;
  border-bottom: 2px solid #1b1b1b;
  transform: rotate(-45deg);
  font-size: 0;
}

/* Das Feld ist durchsichtig — ohne eigenen Ring wäre der Tastaturweg zu ihm
   unsichtbar. `umzug.css:1710` setzt dafür ausdrücklich `outline: none`, und
   auch das an einem Wähler, der unsere Bauform nicht trifft. */
.ce-einwilligung input[type="checkbox"]:focus-visible + label::before {
  outline: 3px solid #ec3013;
  outline-offset: 2px;
}

/* -- Wechsel und Absenden ---------------------------------------------- */

/*
 * Zwei Knöpfe nebeneinander, jeder über seine halbe Breite — wie im Original.
 *
 * DER FEHLERFALL (Video vom 04.09.2026): „Schau mal das Menü unten, das sieht
 * ganz anders aus." Hier standen zwei schmale Knöpfe links am Rand.
 *
 * Der Beleg ist `import/planer-abgriff/schritt-2.html:774-785`: `row
 * kundenFormActions` mit zwei `col-6`, in jeder ein `col-12 btn`. Der frühere
 * Verweis auf `schritt-3.html:805-820` war falsch — dort steht EIN Knopf in
 * einer `col-6` und daneben eine LEERE `col-6 cright`.
 *
 * UND GENAU DESHALB bleibt die rechte Hälfte auch bei uns leer, wenn nur ein
 * Knopf sichtbar ist. Hier stand einmal
 * `.planer-wechsel button:only-of-type { grid-column: 1 / -1 }`. Die Regel
 * konnte nie greifen — `planer.js` verbirgt die Knöpfe, es entfernt sie nicht,
 * es stehen also immer zwei im DOM — und hätte sie gegriffen, hätte sie vom
 * Original weggeführt: `schritt-1.html` stellt das einzelne „Weiter zum
 * Be-/Entladeort" in eine `col-6` und lässt den Rest der Zeile leer, genau wie
 * `schritt-3.html` das einzelne „Zurück".
 *
 * Das Grossschreiben kommt aus `umzug.css:382-384`
 * (`.kundendaten-container .kundenFormActions button`) — einer Klasse, die
 * unser Markup nicht trägt und nicht bekommen soll: dieselbe Familie setzt in
 * `umzug.css:370-377` `position: fixed`.
 *
 * KEINE Mindesthöhe. Hier stand `min-height: 45px` mit Verweis auf
 * `umzug.css:527-531`; der Block gilt aber nur für
 * `.row button.btn.btn-secondary`, und die Schrittknöpfe des Originals sind
 * `btn-primary`. Die Höhe war unsere Zutat mit fremdem Beleg.
 *
 * Als Raster und nicht mit Bootstrap-Spalten: die Knöpfe werden einzeln
 * verborgen, und eine leere `col-6` hinterliesse eine Lücke, in der einmal ein
 * Knopf war — eine leere Rasterzelle nimmt keinen Platz für sich.
 */
.planer-wechsel {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
  margin-top: 1.5rem;
}

.planer-wechsel button {
  width: 100%;
  text-transform: uppercase;
}

.planer-wechsel [hidden], .planer-absenden[hidden] { display: none; }

.planer-absenden { margin-top: 1.5rem; }

/* -- Die zwei Leisten kleben ------------------------------------------- */

/*
 * DIE REITER OBEN, „WEITER"/„ZURÜCK" UNTEN — in jedem Schritt, an jeder Breite.
 *
 * DER FEHLERFALL (Tims Videos vom 23.09.2026, Punkte 3 und 4): Umzugsgut und
 * Services sind am Telefon drei Fensterhöhen lang. Die Reiter standen nur an
 * ihrem Anfang, die Knöpfe nur an ihrem Ende; mittendrin kam man in keinen
 * anderen Schritt, ohne erst zurückzuwischen.
 *
 * `style.css:46` setzt `#main { overflow: hidden }`, und daran scheitert
 * jedes `position: sticky` darunter: der Rahmen wird selbst zum
 * Bildlaufbehälter, der nie läuft. `overflow: clip` schneidet genauso ab und
 * ist keiner; `flow-root` behält den eigenen Formatierungskontext, den
 * `hidden` nebenbei mitbrachte. Nur auf der Seite mit Planer.
 *
 * Die Oberkante ist der Website-Kopf, und der steht nicht still: ab 768 px
 * `fixed` (88 px hoch, ab 992 px 101 und nach dem ersten Bildlauf 78),
 * darunter `sticky` im `body` — der ist eine Fensterhöhe hoch, danach ist der
 * Kopf weg. Eine feste Zahl läge also entweder unter dem Kopf oder mit einer
 * Lücke darunter. `planer.js` misst ihn beim Bildlauf und schreibt
 * `--planer-kopf`.
 *
 * Weiss hinterlegt, sonst schiene der Schritt zwischen Reitern und Knöpfen
 * durch. Darüber liegen der Kopf (`z-index: 10`) und der Zimmerdialog
 * (Bootstraps 1050/1055) — `tools/mobil-check.mjs` misst beides.
 */
#main:has([data-planer-form]) { overflow: clip; display: flow-root; }

.nav-umzug[data-planer-steuer] {
  position: sticky;
  top: var(--planer-kopf, 0px);
  z-index: 9;
  background-color: #fff;
}

.planer-wechsel {
  position: sticky;
  bottom: 0;
  z-index: 9;
  padding: 0.5rem 0;
  background-color: #fff;
}

/* Der rote Pfeil nach oben (`style.css:727`, fest 6rem über dem Rand) läge am
   Telefon auf „Weiter" — die klebende Leiste ist dort über 100 px hoch. Er
   rückt über sie; ihre Höhe schreibt `planer.js` als `--planer-leiste`. */
body:has([data-planer-form]) .scrollToTop { bottom: max(6rem, calc(var(--planer-leiste, 0px) + 0.5rem)); }

/*
 * Ein mit der Tastatur angefahrenes Feld rückt der Browser nur bis an den
 * Fensterrand — mit klebenden Leisten also genau unter Kopf, Reiter oder
 * „Weiter" (gemessen: 11 bzw. 12 von 48 Tabulatorschritten durch Services,
 * bei 360, 768 und 1440 px). Der Abstand steht an den Feldern der Schritte,
 * NICHT als `scroll-padding` an `html`: dort lagen die Knöpfe der Leisten
 * selbst im Rand, und jeder Fokus auf „Weiter" oder einen Reiter riss die
 * Seite um 370 bis 440 px mit (gemessen am Bau, 23.09.2026). Nicht im
 * Zimmerdialog, der liegt fest über allem.
 */
[data-planer-schritt-feld] :is(input, select, textarea, button):not(.modal *) {
  scroll-margin-top: var(--planer-rand, 0px);
  scroll-margin-bottom: var(--planer-leiste, 0px);
}
/* ── Die Galerie ────────────────────────────────────────────────────────────
 *
 * Der Renderer schrieb seit jeher `data-ce-columns` an die Galerie, und
 * NIEMAND las es: eine Suche über alle CSS-, JS- und Astro-Dateien fand das
 * Attribut nur an seiner Schreibstelle. „Einzeln" und „Galerie" erzeugten
 * darum Markup, das sich in genau einer Ziffer unterschied, und sahen gleich
 * aus — der Auftraggeber: „wenn ich auf Galerie klicke, passiert nichts, ich
 * sehe keine Änderung."
 *
 * Verschärfend setzt das Bestands-Stylesheet für diesen Rahmen ausdrücklich
 * `float: none` (style.css:515), die Bilder standen also auch dann
 * untereinander, wenn TYPO3 sie nebeneinander gemeint hatte.
 *
 * `grid` statt `float`: die Höhen der Bilder sind verschieden, und mit
 * Umflüssen entstünden Treppen. Ab `--schmal` wird umgebrochen, sonst stehen
 * auf dem Telefon vier Bilder von je achtzig Pixeln nebeneinander.
 */
.ce-gallery[data-ce-columns="2"] .ce-row,
.ce-gallery[data-ce-columns="3"] .ce-row,
.ce-gallery[data-ce-columns="4"] .ce-row {
  display: grid;
  gap: 12px;
  align-items: start;
}
.ce-gallery[data-ce-columns="2"] .ce-row { grid-template-columns: repeat(2, 1fr); }
.ce-gallery[data-ce-columns="3"] .ce-row { grid-template-columns: repeat(3, 1fr); }
.ce-gallery[data-ce-columns="4"] .ce-row { grid-template-columns: repeat(4, 1fr); }

/* Der Bestand floatet die Spalten; im Raster wäre das ein zweiter,
   widersprechender Umbruch. */
.ce-gallery[data-ce-columns="2"] .ce-column,
.ce-gallery[data-ce-columns="3"] .ce-column,
.ce-gallery[data-ce-columns="4"] .ce-column {
  float: none;
  width: auto;
  margin: 0;
}

@media (max-width: 767px) {
  .ce-gallery[data-ce-columns="3"] .ce-row,
  .ce-gallery[data-ce-columns="4"] .ce-row { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 479px) {
  .ce-gallery[data-ce-columns="2"] .ce-row,
  .ce-gallery[data-ce-columns="3"] .ce-row,
  .ce-gallery[data-ce-columns="4"] .ce-row { grid-template-columns: 1fr; }
}

/**
 * Ein Team-Foto, das keine TYPO3-Ableitung ist.
 *
 * `style.css:549` gibt `.box.team img` nur `border-radius: 200px` und
 * Graustufe — keine Maße. Das reichte, solange jedes Foto als 200×200 aus
 * `_processed_` kam; TYPO3 hat sie für genau diese Kachel gerechnet. Wer im
 * Studio ein Bild aus der Mediathek wählt, bekommt das Original (bis 2000px),
 * und das sprengte die Reihe: „wenn ich hier ein Bild reinschiebe, dann kommt
 * es groß […] bei Personen ist es Mist."
 *
 * Die Maße sind von `.cardbox .ce-cardbox-inner-bild img` (style.css:451)
 * abgeleitet — die Variante „Karten mit Bild", die der Auftraggeber im selben
 * Atemzug als die funktionierende benannt hat. Dort steht `object-fit: cover`
 * aus demselben Grund: nicht stauchen, sondern beschneiden.
 *
 * 200px, nicht 100%: der Kreis aus `border-radius: 200px` ist nur dann einer,
 * wenn das Bild quadratisch ist. Die Klasse hängt ausschliesslich an Bildern,
 * die sie brauchen (`grid.mjs`, `brauchtMass`) — von den acht übernommenen
 * Team-Karten sind das zwei: 200×217 und 200×210, die im Spiegel als Oval
 * erschienen. `/ueber-uns/team.html` zieht darüber `studio.css` in den Kopf
 * und weicht damit vom Spiegel ab; das ist am 02.09.2026 entschieden worden
 * (`b86b481`) und steht in `tools/bewusst-anders.json`.
 */
.box.team img.ce-team-bild {
  width: 200px;
  height: 200px;
  object-fit: cover;
}

/**
 * Bilder in schlichten Spalten, nebeneinander gleich hoch.
 *
 * `plainCol` reicht das Bild an `gallery()` weiter, und die schreibt die Maße
 * der Datei. Zwei Bilder mit verschiedenem Seitenverhältnis stehen damit
 * verschieden hoch in einer Reihe: „Bei Schlicht sind die Bilder
 * unterschiedlich groß."
 *
 * Dieselbe Geometrie wie `.cardbox .ce-cardbox-inner-bild img` (style.css:451)
 * — die Variante „Karten mit Bild", die der Auftraggeber im selben Atemzug als
 * die funktionierende benannt hat. Beschneiden statt stauchen.
 *
 * Anders als `.ce-team-bild` hängt diese Regel unbedingt an jeder schlichten
 * Spalte mit Bild: kein übernommener Baustein benutzt diese Variante mit Bild,
 * es kann sich also keine Bestandsseite dadurch ändern.
 */
.ce-spalte-bild .ce-gallery img {
  width: 100%;
  height: 200px;
  object-fit: cover;
}

/**
 * Bilder, die jemand IN einen Fließtext gesetzt hat.
 *
 * DER FEHLERFALL (Video vom 01.09.2026): „Ich setze ein Bild rein — falsche
 * Formatierung. Bilder sollen in allen Bausteinen vorformatiert werden, immer
 * auf die richtige Größe. Das soll es nicht geben, dass ich dir ein Bild
 * reinschiebe und das ist so scheisse gross."
 *
 * Gemessen: das Einfügen schrieb ein nacktes `<img>` mit den Maßen der Datei
 * — Originale liegen bis 2000 px (`media.mjs` deckelt dort) — und im ganzen
 * Bestand gibt es KEINE Regel, die ein `<img>` im Fließtext begrenzt.
 * `style.css` setzt Maße nur innerhalb bestimmter Hüllen (`.cardbox …`,
 * `.frame-type-image img`, `#maintrailer img`), Bootstraps Reboot setzt
 * ebenfalls keine. Ein 2000 px breites Bild stand damit 2000 px breit da.
 *
 * Die Klasse setzt der Editor beim Einfügen und beim Säubern
 * (`apps/site/src/cms/bridge.js`), nicht der Renderer: übernommene Antworten
 * tragen ihre Bilder in einer eigenen TYPO3-Hülle (`ce-textpic`), die schon
 * sitzt. Deshalb ändert sich an keiner unveränderten Seite etwas — und
 * `studio.css` kommt nur in den Kopf, wenn die Klasse wirklich vorkommt.
 */
.ce-text-bild {
  max-width: 100%;
  height: auto;
}

/* ── Titelbild: die unsichtbare Folie darf den Klick nicht abfangen ────────
   DER FEHLERFALL (Jan, 02.09.2026, nach dem Umbau auf die echte Gestalt):
   „ich schaffe es nicht ein neues bild anzulegen oder es zu bearbeiten. es
   bleibt immer so wie es war in der vorschau."

   Der Wechsler blendet über (`fade: true` in der eingefrorenen slick-Anweisung).
   Dabei liegen ALLE Folien deckungsgleich übereinander; slick setzt den
   inaktiven `opacity: 0` und einen niedrigeren `z-index` — und ein Element mit
   `opacity: 0` nimmt Klicks weiterhin an. Wer auf das sichtbare Bild klickte,
   traf je nach Stapelreihenfolge eine ANDERE Folie, tauschte deren Bild, und
   sah oben unverändert dasselbe. Belegt durch Playwright: beim Ziel
   `bilder.2.src` meldete es „<img data-cms-el="bilder.0.src"> … intercepts
   pointer events".

   NUR IN DER PFLEGE: der Wähler hängt an `[data-cms-block]`, und das trägt der
   Trailer ausschliesslich im Bearbeitungsmodus (`titelbild.mjs`; die
   veröffentlichte Form trägt keine einzige `data-cms-`-Marke, festgehalten in
   `titelbild-baustein.test.mjs`). Auf der ausgelieferten Seite ändert diese
   Regel deshalb nichts — auch nicht auf den zwei Seiten, die `studio.css`
   wirklich laden. */
#maintrailer[data-cms-block] .slick-slide:not(.slick-current) {
  pointer-events: none;
}

/* ── Titelbild: KEINE eigene Gestalt in der Pflegeansicht ──────────────────
   Hier stand eine Regel `.cms-folienmarke` — ein schwarzer Balken mit dem
   Satz „Folie N von M — auf der Seite wechseln sie sich ab", der neben jeder
   gestapelten Folie stand.

   Jan am 02.09.2026: „es soll genau so aussehen, wie es auf der webseite
   aussehen wird. wenn die sich abwechseln, dann sollen die sich auch in der
   vorschau abwchseln. das irritiert."

   Die Vorschau zeigt jetzt denselben Wechsler wie die Seite; die Brücke
   startet die eingesetzten Skripte selbst (`skripteStarten` in
   `apps/site/src/cms/bridge.js`, gemessen in `tools/titelbild-check.mjs`).
   Damit gibt es nichts mehr zu erklären und nichts mehr zu gestalten. Dass
   die Regel WEG bleibt, hält `packages/render/test/titelbild-baustein.test.mjs`
   fest — käme sie zurück, wären die beiden Ansichten wieder auseinander. */

/* ── Zimmermodal: der Kopf darf die erste Zeile nicht verdecken ─────────────
   `umzug.css:2296` macht JEDEN `.modal-header` `position: fixed; width: 100%;
   left: 0` — er nimmt damit keinen Platz ein. Gemessen am gebauten Stand
   (1440 px): der Kopf reicht bis y=137, der Körper beginnt bei y=112, die
   erste Positionszeile liegt also 25 Bildpunkte darunter. Wer die erste
   Position bedienen will, klickt auf den Kopf — `audit:planer` bricht genau
   daran ab („header_inner … intercepts pointer events").

   `sticky` statt `fixed`: der Kopf bleibt beim Scrollen oben stehen (das ist
   der Sinn), nimmt aber seinen Platz ein. Nur für `.invmodal` — die
   Zimmermodals sind die einzigen Modale dieses Hauses mit einem mehrzeiligen
   Kopf, und jedes andere bleibt, wie es war. */
.invmodal .modal-header {
  position: sticky;
  top: 0;
  left: auto;
  width: auto;
}

/* ── Zimmermodal: was das Kreuz tut, steht unter dem Namen ─────────────────
   Tims Videos vom 23.09.2026: „ist Kreuz dasselbe Speichern wie Fertig oder
   bedeutet Kreuz Löschen?" Die Zeile sagt es. Dunkel auf dem Gelb des Kopfs,
   nicht grau und klein — grau-klein war genau das, was bei „Besonderheiten"
   übersehen wurde. Rechts hält sie den Platz des Kreuzes frei: `style.css`
   stellt es absolut an den rechten Rand des Kopfs (`right: .5rem`, 42 px
   breit); `tools/mobil-check.mjs` misst, dass keine Textzeile darunterläuft. */
.invmodal .planer-zimmer-hinweis {
  margin: 0.25rem 3rem 0 0;
  font-size: 0.9375rem;
  line-height: 1.3;
  color: #212529;
}

/**
 * DER EINWILLIGUNGSHAKEN — sichtbar, nicht gelb auf fast weiss.
 *
 * DER FEHLERFALL (Jan, 04.09.2026): „das häckchen funztz jetzt ist aber gelb
 * und unsichtbar." Gemessen am ausgelieferten Bau:
 *
 *   Häkchen  #fff600 auf Kästchen #f4f4f4   Kontrast 1,04 : 1
 *   Kästchen #f4f4f4 auf Seite    #ffffff   Kontrast 1,10 : 1, border-width 0
 *
 * WCAG verlangt für Bedienelemente 3 : 1. Beide Werte kommen aus
 * `typo3conf/ext/cok_umzug/…/umzug.css` — einer Datei, die BYTE-GLEICH aus dem
 * Spiegel übernommen ist (md5 467605b3…). Sie wird nicht angefasst; der
 * Aufschlag steht hier, weil `studio.css` nach ihr geladen wird und bei
 * gleicher Spezifität dann gewinnt.
 *
 * KEINE ABWEICHUNG VOM SPIEGEL, aber genauer als beim ersten Wurf gesagt:
 * `class="checkbox "` steht im Spiegel sehr wohl, nämlich in
 * `mirror/kontakt.html` (mit Leerzeichen; ein Muster mit unmittelbarem
 * Anführungszeichen verfehlt es — genau daran ging die erste Behauptung
 * „kommt auf KEINER Seite vor" vorbei). Jene Seite lädt `umzug.css` aber gar
 * nicht (gemessen: 0 Treffer), die Regeln griffen dort also nie. Auf
 * `/umzugsplaner.html`, wo `umzug.css` geladen wird, kennt der Spiegel die
 * Klasse nicht. Beide Aufschläge hier treffen deshalb nur unseren eigenen
 * Bau, und `tools/bewusst-anders.json` braucht keinen Eintrag.
 *
 * Die Farben sind die des Bestands: #1b1b1b Fliesstext, #898989 Linien. Der
 * hellere Linienton #c9c9c9 wäre zu schwach (siehe Regel unten). Das Gelb der
 * Seite bleibt, wo es hingehört — beim aktiven Reiter, und der ist im Spiegel
 * bildpunktgleich derselbe.
 */
.ce-einwilligung label::before {
  /* #898989, nicht #c9c9c9: gegen Weiss kommt der hellere Ton nur auf
     1,66 : 1, und WCAG 1.4.11 verlangt für die Kante eines Bedienelements
     3 : 1. Beide Töne stehen in der Palette des Bestands; gemessen ergibt
     #898989 hier 3,50 : 1. */
  border: 1px solid #898989;
}

.ce-einwilligung input[type="checkbox"]:checked + label::after {
  color: #1b1b1b;
}

/* -- Möbelliste und Übersicht ------------------------------------------ */

/*
 * Der Absendeknopf über die volle Breite, wie im Original. Hier standen bis
 * zum 23.09.2026 auch der Knopf „Zur Umzugsgutliste" und die zwei Rückknöpfe
 * „Zurück zur Eingabe" — seit Umzugsgut ein Reiter ist, führen die
 * Wechselknöpfe dorthin und zurück (Tims Videos vom 23.09.2026).
 */
.planer-absenden button {
  width: 100%;
  text-transform: uppercase;
}

.planer-moebel-einleitung { margin: 0 0 1rem; }

/*
 * Die Übersicht: je Schritt ein Kasten, darin „Label: Wert" Zeile für Zeile.
 *
 * DER FEHLERFALL (Tims Videos vom 23.09.2026): „Die Übersicht ist viel zu
 * lang und schwarz-weiß." Bis dahin stand jede Frage in einem Raster, Frage
 * links, Wert rechts, unbeantwortet als „Keine Angabe" — so zeigt es das
 * Original (Bildschirmaufnahme vom 04.09.2026). Jetzt steht nur
 * Beantwortetes da, je Schritt ein hellgrauer Kasten mit gelbem Rand und dem
 * Knopf „Ändern"; die Farben sind die der Website (#f2f2f2, #FFE100).
 */
.planer-uebersicht-block {
  margin-bottom: 1rem;
  padding: 0.75rem 1rem;
  border-left: 4px solid #FFE100;
  background: #f2f2f2;
}

.planer-uebersicht-kopf {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.planer-uebersicht-kopf h4 {
  margin: 0;
  font-size: 1.125rem;
  text-transform: uppercase;
}

/* Ein Textknopf, kein Bootstrap-`.btn`: er soll neben dem Titel stehen, nicht
   mit ihm um die Aufmerksamkeit ringen. 44 px hoch — die Mindestfläche für
   einen Finger. */
.planer-uebersicht-aendern {
  flex: none;
  min-height: 44px;
  padding: 0 0.5rem;
  border: 0;
  background: transparent;
  color: #111111;
  font-weight: 600;
  text-decoration: underline;
  cursor: pointer;
}

.planer-uebersicht-aendern:hover { color: #DB1C1C; }

.planer-uebersicht-aendern:focus-visible { outline: 2px solid #000; outline-offset: 2px; }

.planer-uebersicht-gruppe + .planer-uebersicht-gruppe { margin-top: 0.5rem; }

.planer-uebersicht-gruppe h5 {
  margin: 0.25rem 0;
  font-size: 1rem;
  font-weight: 600;
}

.planer-uebersicht-zeile { margin: 0; padding: 0.1rem 0; overflow-wrap: anywhere; }

.planer-uebersicht-label { color: #555; }

.planer-uebersicht-wert { color: #111111; }

/* Die Summenzeile — grau hinterlegt wie im Original, und ohne den Namen
   `planer-summe`: die Summenleiste, die am 01.09.2026 abbestellt wurde, bleibt
   weg, und `planer.test.mjs` prüft das über genau diese Zeichenkette. */
.planer-gesamt {
  margin: 1.5rem 0;
  padding: 0.75rem 1rem;
  background: #f4f4f4;
  font-weight: 600;
}

/*
 * Die Warnung ohne Umzugsgut, unmittelbar über „Daten absenden".
 *
 * DER FEHLERFALL (Tims Videos vom 23.09.2026): eine Anfrage ging ohne ein
 * einziges Möbelstück raus. „Ein rotes Feld muss da kommen." Rot ist hier das
 * Rot der Fehlermeldungen des Planers (#ec3013, wie `.planer-ohne-skript`);
 * die Schrift ein dunkleres Rot, das auf dem hellen Grund lesbar bleibt.
 * Keine `display`-Regel an der Klasse: sie schlüge das `hidden`-Attribut, und
 * die Warnung stünde von Anfang an da.
 */
.planer-ohne-umzugsgut {
  margin: 1.5rem 0 0;
  padding: 0.75rem 1rem;
  border-left: 4px solid #ec3013;
  background: #fdf1ef;
  color: #a3200d;
  font-weight: 600;
}

.planer-ohne-umzugsgut p { margin: 0 0 0.75rem; }

.planer-ohne-umzugsgut-knoepfe {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* -- Feste Ausgabegrösse für getauschte Bilder --------------------------- */

/*
 * DER FEHLERFALL (Video vom 04.09.2026): „Dass die Bilder nicht automatisch
 * eine andere Größe bekommen … In allen Blöcken müssen Bilder eine feste Größe
 * haben. Egal, in welche Größe man das Bild reinsetzt." Beim Tausch sprang das
 * Layout, weil die meisten Bausteine die Dateimaße unverändert ausgeben.
 *
 * Die Klassen hängen NUR an Bildern, die sie brauchen — die Bedingung und die
 * Zählung dahinter stehen in `packages/render/src/v2/mass.mjs`. Übernommene
 * Bilder tragen sie nicht, und deshalb bewegt sich keine Spiegelseite — mit
 * EINER gebuchten Ausnahme seit dem 05.09.2026: die zehn FAQ-Bilder auf
 * /leistungen/umzugslogistik.html (`.ce-faq-bild` weiter unten, unbedingt;
 * `tools/bewusst-anders.json`).
 *
 * `object-position` kommt vom Fokuspunkt und steht am Bild, nicht hier: nur
 * ein bewusst gesetzter Ausschnitt schreibt ihn, alles andere bleibt mittig.
 */
.ce-text-spalte-bild,
.ce-galerie-bild {
  width: 100%;
  height: auto;
  object-fit: cover;
}

/* 3:2 — der Mittelwert der vierundzwanzig übernommenen Textspaltenbilder. */
.ce-text-spalte-bild { aspect-ratio: 3 / 2; }

/* 4:3 — die acht übernommenen Galeriebilder liegen innerhalb von 0,002. */
.ce-galerie-bild { aspect-ratio: 4 / 3; }

/* 7:3 — die 26 übernommenen Trailerbilder liegen zwischen 2,331 und 2,430;
   ein 16:9-Upload liegt 0,56 daneben und bekommt diese Regel. Die Zahl ist
   `MASSE.titelbild` aus mass.mjs; ein Test hält beide zusammen. */
.ce-titel-bild {
  width: 100%;
  height: auto;
  aspect-ratio: 7 / 3;
  object-fit: cover;
}

/* 7:3 auch für das breite Titelbild-Layout (`hero/full`) — `MASSE.hero`. */
.ce-hero-bild {
  width: 100%;
  height: auto;
  aspect-ratio: 7 / 3;
  object-fit: cover;
}

/* -- Eigene Bausteintypen: das Raster ------------------------------------ */

/*
 * Ein Bild im Raster wählt sein Verhältnis in der Gestalt des Feldes
 * (`packages/schema/src/raster.mjs`, `VERHAELTNISSE`); der Erzeuger
 * (`packages/render/src/v2/blocks/raster.mjs`) hängt die passende Klasse
 * UNBEDINGT an — keine Spiegelseite benutzt einen eigenen Typ, es gibt nichts
 * zu schonen. Fünf Klassen, ein Wort in der Ladebedingung (`ce-raster-bild`);
 * `bilder-vorformatiert.test.mjs` hält jede einzeln dagegen.
 * „hochkant" ist 3:4.
 */
.ce-raster-bild-3-2,
.ce-raster-bild-4-3,
.ce-raster-bild-16-9,
.ce-raster-bild-1-1,
.ce-raster-bild-hochkant {
  width: 100%;
  height: auto;
  object-fit: cover;
}
.ce-raster-bild-3-2 { aspect-ratio: 3 / 2; }
.ce-raster-bild-4-3 { aspect-ratio: 4 / 3; }
.ce-raster-bild-16-9 { aspect-ratio: 16 / 9; }
.ce-raster-bild-1-1 { aspect-ratio: 1 / 1; }
.ce-raster-bild-hochkant { aspect-ratio: 3 / 4; }

/*
 * DER FEHLERFALL (gemessen am 05.09.2026 von `audit:bausteine`, nachdem das
 * Tor endlich auch den Dokumentüberlauf misst): eine Bildunterschrift in
 * einem Raster zog die Seite um 60 px nach rechts, und bei 768 px um 436 px.
 *
 * `style.css:623` setzt `.content .ce-gallery figcaption` auf
 * `position: absolute; width: 100%`. Absolut heisst: gegen den nächsten
 * GESETZTEN Vorfahren. `figure.image` ist `display: table` und ungesetzt —
 * also rechnete die Unterschrift ihre 100 % gegen die ganze Zeile
 * (1296 px) statt gegen ihr Bild (424 px) und ragte um den Unterschied
 * hinaus. Auf den Spiegelseiten fällt das nicht auf: dort steht eine
 * Unterschrift nur in einspaltigen Galerien, wo Zeile und Bild dasselbe Mass
 * haben. Im Raster ist die Zelle fast nie die ganze Zeile.
 *
 * Die Regel gilt NUR im Raster (`frame-type-raster`) — keine Spiegelseite
 * benutzt einen eigenen Typ, und ein `position: relative` auf jeder Figur
 * der Website wäre eine Änderung an der öffentlichen Ausgabe.
 */
.frame-type-raster .ce-gallery figure {
  position: relative;
}

/*
 * DER ZWEITE FEHLERFALL AUS DEMSELBEN LAUF: ein Knopf in einer sehr schmalen
 * Zelle zog die Seite um 60 px nach rechts.
 *
 * Gemessen: eine Kachelliste in einer Zelle von 1/12 — die Karte war 13 px
 * breit, der Knopf darin 128 px („Mehr erfahren", `inline-block`, also so
 * breit wie seine Beschriftung) und ragte um 115 px heraus. Im Bauer ist das
 * mit zwei Zügen erreicht, und der Entwurf sagt: „all wysiwig so a child
 * could use it" — was ein Kind zusammensetzen kann, darf die Seite nicht
 * seitlich aufreissen.
 *
 * `max-width` statt einer Regel, die die Liste verbietet: die Beschriftung
 * bricht dann um und die Karte bleibt in ihrer Spalte. Lesbar ist so eine
 * Zelle nicht — das ist eine Frage an den Benutzer, nicht an das Layout.
 */
.frame-type-raster .btn {
  max-width: 100%;
}

/*
 * DER FEHLERFALL (28.09.2026, im Browser): „Mehr erfahren" stand in jeder
 * Kachel eines eigenen Typs schwarz auf Rot — jeder andere Knopf der Website
 * ist weiss auf Rot.
 *
 * `style.css` setzt `.cardbox a { color: #000 }` (0,1,1), weil auf den
 * Spiegelseiten die GANZE Karte der Verweis ist; das schlägt
 * `.btn { color: #fff }` (0,1,0). Die Karte des Hauses mit Knopf
 * (`/umzug-starten.html`) hat dafür eine eigene Regel an
 * `.mainbutton .btn` — die Kachel des Rasters trägt ihren Knopf als
 * `p > a.btn`, und die traf keine. Das Hover bleibt, wie es war:
 * `.btn:hover` setzt ebenfalls Weiss. Misst `audit:bausteine`, Prüfung (6).
 */
.frame-type-raster .cardbox a.btn {
  color: #fff;
}

/* -- Vergrößern im festen Rahmen ----------------------------------------- */

/*
 * DER FEHLERFALL (Jan, 05.09.2026): „you cant resize the pictures in the
 * fixed size block. i mean that you cant zoom in the picture, while the
 * picture block stays the size."
 *
 * Die Vergrößerung ist `transform: scale(z)` am `<img>`, der Ursprung ist der
 * Fokuspunkt — beides schreibt der Renderer als EIN `style` ans Bild
 * (`html.mjs`, Funktion `bildStil`). Eine Transformation berührt das Layout
 * nicht, der Rahmen bleibt also, wie er ist. Aber ein transformiertes Bild
 * malt über seinen Kasten hinaus, und das Stylesheet des Bestands schneidet
 * erst an der Reihe (`typo3temp/…/af378321….css`, `.ce-row { overflow:
 * hidden }`): ein vergrößertes Galeriebild läge über der Nachbarspalte und
 * über der Bildunterschrift. Geschnitten wird deshalb an der Hülle, die der
 * Renderer ohnehin schreibt, erkannt am style-Wert selbst — so bleiben die
 * Titelbild-Vorlagen zeichengleich.
 *
 * `overflow: clip` statt `hidden`: kein neuer Formatierungskontext, nichts
 * rückt. `display: block`: `overflow` an `display: table` (dieselbe Datei,
 * `.ce-gallery figure`) ist nicht verlässlich. KEIN `transform-origin` hier:
 * er kommt vom Fokus, sonst folgte die Vergrößerung dem Ausschnitt nicht.
 *
 * DER RAHMEN: ein Bild, das die Maßklasse schon trägt, behält sein Maß. Ein
 * Bild, das noch in seinen eigenen Maßen steht, bekommt die Klasse mit dem
 * ersten Ausschnitt oder der ersten Vergrößerung (`html.mjs`, Funktion
 * `gallery`) — dann wechselt der Rahmen einmal auf das Verhältnis des
 * Bausteins, danach nicht mehr. Das misst `tools/bildausschnitt-check.mjs`.
 *
 * OHNE `:has()` gäbe es keinen Schnitt: das Bild wüchse über die Hülle.
 * Deshalb der Rückfall unten — wo der Wähler fehlt, unterbleibt die
 * Vergrößerung ganz, und es bleibt beim Ausschnitt (Firefox vor 121, Safari
 * vor 15.4; Durchsicht bild-zoom, Befund W).
 */
@supports not selector(figure:has(> img)) {
  img[style*="scale("] { transform: none !important; }
}
figure.image:has(> img[style*="scale("]),
figure.image:has(> a > img[style*="scale("]) {
  display: block;
  overflow: clip;
}

/* Die Teamkarte: Kreis und Abstand wandern vom Bild auf die Hülle, sonst
   wüchse der Kreis mit (`style.css` setzt `border-radius: 200px` am `<img>`).
   Die Graustufe bleibt am Bild. */
.box.team .bild:has(> img[style*="scale("]) {
  display: inline-block;
  width: 200px;
  height: 200px;
  margin-bottom: 1rem;
  border-radius: 200px;
  overflow: clip;
  vertical-align: middle;
}

.box.team .bild:has(> img[style*="scale("]) > img {
  display: block;
  margin-bottom: 0;
  border-radius: 0;
}

/* -- FAQ-Bild -------------------------------------------------------------- */

/*
 * DER FEHLERFALL (Jan, 05.09.2026): „pictures dont have a fixed size in the
 * faq baustein." Die zehn übernommenen Ableitungen auf
 * /leistungen/umzugslogistik.html sind 200 px breit und 133, 149 oder 150 px
 * hoch — drei Höhen in einem Baustein, und ein Tausch brachte eine vierte.
 *
 * 200 px wie die Ableitungen; 3:2 wie die Textspalte (`MASSE.textspalte`,
 * ein Test hält beide zusammen), weil dann keines der zehn hochskaliert wird:
 * die sechs mit 149 oder 150 px verlieren je 8 px oben und unten, die vier
 * mit 133 px nichts. 4:3 hätte vier davon um 12 % vergrössert. NICHT `width: 100%`: die Spalte ist ein Float aus dem
 * Bestand (`.ce-right .ce-gallery { float: right }`) und fiele zusammen.
 * UNBEDINGT, nicht bedingt — siehe `blocks/faq.mjs`, Funktion `textpic`.
 */
.ce-faq-bild {
  width: 200px;
  height: auto;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}

/* -- Zebrastreifen im Zimmerdialog --------------------------------------- */

/* Das Original setzt die Positionsliste als gestreifte Tabelle
   (Bildschirmaufnahme vom 04.09.2026, rechtes Fenster: helle und graue Zeilen
   im Wechsel). Unsere Zeilen hatten nur eine Trennlinie — bei 45 Positionen im
   Wohnzimmer verliert das Auge die Zeile. Nur Farbe, kein Markup: die
   Zellentitel (`d-block d-lg-none`) sind der Abgriff und bleiben. */
.planer-position:nth-of-type(even) { background: #f4f4f4; }

/* -- Bild im Fließtext (bildmass.mjs) ------------------------------------ */

/*
 * DER FEHLERFALL (21.09.2026, live): `bildmass.mjs` deckelt ein `<img>`, das
 * der MCP in ein Fließtextfeld setzt, auf `HOECHSTE_BREITE` — kein
 * Stylesheet der ersten Website band die dabei vergebene Klasse
 * `image-embed-item` je an ihre Spalte (das Lagerbox-Stylesheet tut es nur
 * INNERHALB von `.ce-image`, und auch dort nicht für ein Bild, das mitten im
 * Satz steht): `width="900"` blieb fest, gleich wie schmal die Spalte war.
 *
 * DIE REGEL zielt auf `ce-fliessbild`, nicht auf `image-embed-item`:
 * `bildmass.mjs` ist die EINZIGE Stelle, die diese Klasse vergibt — jedes
 * strukturierte Bildfeld (Titelbild, Bild-neben-Text, Kachelbild, Logo, das
 * FAQ-Bild) trägt zwar ebenfalls `image-embed-item`, aber nie
 * `ce-fliessbild`, und läuft über die Galerie
 * (`packages/render/src/v2/html.mjs`), nicht über diese Datei. Dieselbe
 * Klasse ist zugleich die Auslöserklasse, die `studio.css` überhaupt erst
 * lädt (`STUDIO_CSS` in `packages/render/src/v2/studio-css.mjs`) — eine
 * Regel, die an einer anderen Klasse hinge, könnte auf einer Seite landen,
 * die dieses Stylesheet gar nicht einbindet.
 */
.content img.ce-fliessbild {
  max-width: 100%;
  height: auto;
}
