/* ============================================================================
   eris-responsive.css — zentrale Anpassungsebene für alle ERIS-Seiten
   ----------------------------------------------------------------------------
   Wird von backend/main.py in den <head> JEDER Seite injiziert (analog
   eris-theme.css) und liegt damit hinter den seiteneigenen Stylesheets.
   Damit die Regeln auch auf den Shells ohne literales </head>
   (admin/controlling/lehrdeputat/risiko/risiko-bericht/wissensdatenbank)
   greifen, tragen sie ein vorangestelltes `html` — Spezifitaet (0,1,1) schlaegt
   die Seitenregel (0,1,0) unabhaengig von der Ladereihenfolge.

   Leitgedanken
   ------------
   1) Breite statt Auflösung: Layouts richten sich nach der verfügbaren Breite,
      nicht nach Geräteklassen. Deshalb intrinsische Gitter (auto-fit +
      minmax(max(…))) statt reiner Breakpoint-Kaskaden — die wirken auch im
      Portal, wo der Inhalt schmaler ist als das Fenster.
   2) Hoch- und Querformat: keine vh-Vollhöhen (Browserleiste!), sondern
      dvh/svh; für flache Fenster (Querformat-Telefon, 150-%-Skalierung)
      eigene Regeln, die klebende Balken und Dialoge entlasten.
   3) Pixeldichte: alle Maße in CSS-Pixeln und fließenden Funktionen — keine
      Annahme über Gerätepixel. Text-Autoskalierung wird abgeschaltet.
   4) Nichts verschwindet: zu breite Inhalte bekommen einen Rollbereich
      (Tabellen), statt die Seite querzuschieben.
   ========================================================================== */

:root{
  /* Fließender Seitenrand: eng auf kleinen Fenstern, ruhig auf großen. */
  --eris-pad: clamp(12px, 2.4vw, 26px);
  --eris-gap: clamp(10px, 1.4vw, 16px);

  /* ── TYPOGRAFISCHE SKALA — EINE QUELLE ───────────────────────────────────
     Bis 06.09. gab es keine. Die Bestandsaufnahme zaehlte 35 verschiedene
     Pixelgroessen in 2.649 fest verdrahteten Vorkommen und KEINE einzige ueber
     ein Token — dazu 9 halbe Pixelstufen (581 Vorkommen), das sichere Zeichen
     fuer Einzelfall-Justierung statt Skala.
     Die zehn Stufen sind NICHT erfunden, sondern die meistbenutzten Werte
     des Bestandes. --fs-nano (10px) und --fs-h3 (20px) kamen am 06.09. dazu:
     20px war mit 35 Vorkommen haeufiger als das 26px, das von Anfang an drin
     stand — die erste Auswahl war an dieser Stelle willkuerlich. Wer eine neue Seite
     baut, greift hier zu, statt einen 36. Wert zu erfinden.
     ⚠ Halbe Pixel gehoeren NICHT in die Skala. Wenn 12px zu gross und 11px zu
       klein wirkt, ist meist der Abstand schuld, nicht die Schrift.
     ⚠ Wachhund: backend/tests/test_frontend_typo_skala.py — er verbietet halbe
       Pixel (3 geduldete Reste mit Deckel) und prueft, dass die Skala genau die
       Stufen traegt, auf die gerundet wurde. */
  --fs-nano:  10px;   /* Legenden, Tabellen-Fussnoten          (108 Vorkommen) */
  --fs-mikro: 11px;   /* Badges, Achsen, Fusszeilen            (483 Vorkommen) */
  --fs-klein: 12px;   /* Hilfstexte, Tabellen-Zusatz           (536)           */
  --fs-basis: 13px;   /* Fliesstext in Karten, Bedienelemente  (437)           */
  --fs-gross: 14px;   /* hervorgehobener Fliesstext            (147)           */
  --fs-lead:  16px;   /* Einleitung, groessere Bedienelemente  ( 51)           */
  --fs-titel: 18px;   /* Seitenname, Kartentitel               ( 49)           */
  --fs-h3:    20px;   /* Abschnittsueberschrift (Serife)       ( 35)           */
  --fs-h2:    22px;   /* Abschnittsueberschrift                ( 35)           */
  --fs-h1:    26px;   /* Seitenueberschrift                    ( 20)           */
}

/* ── 1 · Grundlagen ──────────────────────────────────────────────────────── */

html{
  /* iOS/Android skalieren Text im Querformat sonst eigenmaechtig hoch. */
  -webkit-text-size-adjust:100%;
  text-size-adjust:100%;
}

/* Kein Querschieben der ganzen Seite. `clip` (nicht `hidden`) erzeugt keinen
   Rollbereich → position:sticky in Topbars/Tabellen bleibt intakt. */
html, html body{ max-width:100%; overflow-x:clip; }

/* Gitterzellen und Kopfzeilen-Kinder duerfen unter ihre Inhaltsbreite
   schrumpfen. Ohne min-width:0 sprengt ein langes Wort oder ein <select> die
   Spur — das war die haeufigste Einzelursache fuer zu breite Layouts.
   BEWUSST NICHT global auf alle Flex-Kinder: dort ist der richtige Hebel
   flex-wrap am Container, sonst werden Textbloecke auf Restbreiten gequetscht. */
html .topbar > *, html .topbar-right > *,
html [class*="grid"] > *{ min-width:0; }

/* Auch die uebrigen Gitter-Huellen der App (Namenskonvention split/layout/cols)
   duerfen ihre Spuren schmaler werden lassen als den Inhalt — sonst waechst die
   Spur auf die min-content-Breite des laengsten Textes (gesehen auf
   position-trends: 1fr-Spur blieb 566px breit). */
html .split > *, html .layout > *, html [class*="cols"] > *{ min-width:0; }

/* Zeilen aus Text + Aktion (Querverweis-Karten) umbrechen statt ueberlaufen. */
html .crosslink{ flex-wrap:wrap; }

/* Bedienleisten der App folgen einer Namenskonvention (…seg, …tool(s|bar),
   …chips, …filter…, …bar). Sie sind durchweg waagerechte Flex-Leisten und
   duerfen umbrechen statt aus der Karte zu ragen. flex-wrap wirkt nur auf
   Flex-Container, max-width ist ueberall unschaedlich.
   Seitenspalten sind ausgenommen: dort ist Flex eine SPALTE, ein Umbruch
   wuerde sie in mehrere Spalten zerlegen. */
html [class*="seg"], html [class*="tool"], html [class*="chips"],
html [class*="filter"],
html [class*="bar"]:not([class*="sidebar"]):not([class*="side-"]){
  flex-wrap:wrap;
  max-width:100%;
}
/* AUSDRUECKLICHE AUSNAHME: die rechte Kopfzeilen-Gruppe des Portals heisst
   `.tb-right`. Alle uebrigen Seiten nennen sie `.topbar-right` — die enthaelt
   „bar" und wird von der Regel darueber schon erfasst; `.tb-right` passt auf
   kein einziges Muster und ist deshalb durchgerutscht. Sie trug
   `flex-shrink:0` und keinen Umbruch und blieb dadurch 368px breit in einem
   360px-Fenster (gemessen 2026-09-04 auf controlling.html; Ueberlauf auch bei
   `button.logout-btn` darin). `max-width` greift trotz `flex-shrink:0`, danach
   brechen die Kinder um. `flex-shrink` bleibt bewusst unangetastet, damit sich
   am Breitbild-Layout nichts aendert. */
html .tb-right{ flex-wrap:wrap; max-width:100%; }

/* In einer umbrechenden Leiste ist min-width:0 gefahrlos: die Zeilenaufteilung
   entscheidet sich VOR dem Schrumpfen — Elemente wandern also in die naechste
   Zeile, statt gequetscht zu werden. Ohne die Regel waechst ein Flex-Element
   auf die min-content-Breite seines Inhalts (gesehen auf haushalt: Label mit
   <select width:100%> blieb 373px in einer 336px-Leiste). */
html [class*="seg"] > *, html [class*="tool"] > *, html [class*="filter"] > *,
html .crosslink > *{ min-width:0; }

/* Auswahlfelder sind nie breiter als ihr Platz. Ein <select> waechst sonst auf
   die Breite seines laengsten Eintrags („Fakultaet fuer Forstwissenschaften und
   Waldoekologie") und schiebt die ganze Leiste aus der Karte. Mit min-width:0
   darf es als Flex-Element mitschrumpfen; das Gleiche gilt fuer die
   Label-Huelle, in der es oft steckt. */
html select, html input, html textarea{ max-width:100%; min-width:0; }
html label:has(> select), html label:has(> input), html label:has(> textarea){
  min-width:0;
  max-width:100%;
}

/* Eine Schaltflaeche ist nie breiter als ihr Platz; auf kleinen Fenstern darf
   die Beschriftung umbrechen (statt aus der Karte zu ragen). */
html .btn{ max-width:100%; }
@media (max-width: 700px){
  html .btn{ white-space:normal; }
}

/* Medien und eingebettete Fremdinhalte nie breiter als ihr Platz. */
html img, html video, html iframe, html embed, html object{ max-width:100%; }
html img, html video{ height:auto; }

/* Lange Bezeichner (Studiengaenge, Kostenstellen, URLs) umbrechen lassen. */
html .card-title, html .kpi-label, html .klabel, html .topbar-title,
html .page-title, html .meta{ overflow-wrap:anywhere; }

/* ── 2 · Kopfzeile (auf ~30 Report-Seiten identisch aufgebaut) ───────────── */
/* Vorher: display:flex + height:50px ohne Umbruch → die Kopfzeile wuchs bei
   schmalem Fenster ueber den rechten Rand und zog die ganze Seite mit. */

html .topbar{
  flex-wrap:wrap;
  height:auto;
  min-height:50px;
  row-gap:6px;
  padding-block:6px;
  padding-inline:var(--eris-pad);
}
html .topbar-title{ flex:1 1 auto; min-width:0; }
html .topbar-right{
  margin-left:auto;
  flex-wrap:wrap;
  row-gap:6px;
  justify-content:flex-end;
  min-width:0;
}
/* Auswahlfelder in der Kopfzeile duerfen schrumpfen, aber nicht verschwinden.
   Wie das Feld AUSSIEHT (Schrift, Rahmen, Flaeche), steht zentral in
   eris-theme.css unter „AUSWAHLFELD DER REPORT-KOPFZEILE" — hier steht nur,
   wie es sich in den Platz fuegt. */
html .topbar-right select,
html .topbar-right .scope-select{ max-width:100%; min-width:0; }

/* ── 2b · Kopfzeilen-MASSE — EINE QUELLE ─────────────────────────────────────
   Bis 06.09. stand jede dieser Groessen einzeln in bis zu 33 Seiten-Stylesheets
   und driftete mit jeder kopierten Seite weiter: der Titel in 5 Groessen, der
   Trenner in 5 Hoehen, der Avatar in 3 Durchmessern — 89 sichtbare Abweichungen,
   KEIN Stylesheet war abweichungsfrei (Bestandsaufnahme 06.09.).
   Die Werte sind der jeweilige Mehrheitswert des Bestandes. Ausnahme: Zurueck-Link
   (13px:13 / 14px:12) und Name (13px:12 / 14px:11) standen fast gleichauf — dort
   ist 13px eine Festlegung des Nutzers, keine Auszaehlung.
   ⚠ Die entsprechenden Seitenregeln sind ENTFERNT. Wer hier etwas aendert,
     aendert es auf allen Report-Seiten. Nicht in eine Seiten-CSS zurueckschreiben.
   ⚠ `height`/`padding` der Leiste stehen bewusst NICHT hier: sie werden schon vom
     Block darueber gesetzt (`padding-block`/`padding-inline`, `min-height`). Die
     abweichenden Seitenwerte dafuer sind wirkungslos, nicht wirksam.
   ⚠ `:not(.topbar--portal)` nimmt controlling.html aus — kompaktere Portal-Leiste
     ohne Seitennamen, so dokumentiert. Der Ausschluss haengt an einer eigenen
     Klasse im Markup, nicht an `data-rail`.
   ⚠ Die Mobil-Regel fuer den Titel weiter unten traegt DENSELBEN Praefix. Ohne das
     schlaegt dieser Block sie (0,3,1 gegen 0,1,1) und der Titel bliebe im schmalen
     Fenster auf 18px stehen. */
html .topbar:not(.topbar--portal) .topbar-title{ font-size:var(--fs-titel); }
html .topbar:not(.topbar--portal) .back-link{ font-size:var(--fs-basis); }
html .topbar:not(.topbar--portal) .user-name{ font-size:var(--fs-basis); }
html .topbar:not(.topbar--portal) .topbar-sep{ height:24px; }
html .topbar:not(.topbar--portal) .avatar{ width:30px; height:30px; font-size:var(--fs-basis); }
html .topbar:not(.topbar--portal) .topbar-right{ gap:12px; }

/* Kopfzeile zweizeilig: Titel oben, Bedienelemente in eigener Reihe. */
@media (max-width: 880px){
  html .topbar-right{ margin-left:0; width:100%; justify-content:flex-start; }
  html .topbar-right .scope-select{ flex:1 1 200px; }
}
@media (max-width: 700px){
  /* Umgebrochen wird die Kopfzeile drei- bis vierzeilig (~200px). Als klebende
     Leiste fraeße sie ein Viertel des Schirms — unter 700px scrollt sie deshalb
     mit. `relative` statt `static`, damit die z-index-Staffelung erhalten bleibt. */
  html .topbar{ position:relative; }
}
@media (max-width: 560px){
  html .topbar-right .user-name{ display:none; }   /* Avatar bleibt sichtbar */
  html .topbar-right .topbar-sep{ display:none; }
  /* Zwei Auswahlfelder passen nebeneinander; volle Breite nur, wenn nicht. */
  html .topbar-right .scope-select{ flex:1 1 150px; }
  html .topbar:not(.topbar--portal) .topbar-title{ font-size:clamp(13px, 3.6vw, 16px); }
}

/* ── 3 · Inhaltsspalte ───────────────────────────────────────────────────── */
/* Die Reports deckeln sich bereits auf min(94vw, --report-max). Unter ~700px
   sind 6vw Rand verschenkter Platz — dort auf fließenden Rand umstellen. */

@media (max-width: 700px){
  html .main, html .sheet, html .wrap{
    max-width:100%;
    padding-inline:var(--eris-pad);
  }
  /* Icon-Schiene links: auf kleinen Fenstern keinen festen Einzug halten. */
  html .page{ margin-left:0; padding-inline:var(--eris-pad); }
}
/* Portal-Inhaltsflaeche: rechts 64px Reserve nur, solange Platz dafuer ist. */
@media (max-width: 1100px){
  html .app > .main{ padding-right:var(--eris-pad); padding-left:var(--eris-pad); }
}

/* ── 4 · Tabellen: waagerecht rollen statt Seite sprengen ────────────────── */
/* Die Huelle setzt eris-responsive.js um jede Tabelle, die breiter ist als ihr
   Platz (Datentabellen werden im JS gerendert, deshalb kein Markup-Umbau). */

html .eris-scrollx{
  max-width:100%;
  overflow-x:auto;
  overscroll-behavior-x:contain;
  -webkit-overflow-scrolling:touch;
}
html .eris-scrollx > table{ min-width:100%; }
/* Tabellen mit klebender Kopfzeile brauchen im Rollbereich eine Hoehe, sonst
   haette position:sticky + top:0 keinen Bezug mehr. */
html .eris-scrollx[data-sticky="1"]{ max-height:min(78svh, 900px); overflow-y:auto; }

/* Zarter Hinweis, dass seitlich mehr steht. */
html .eris-scrollx[data-mehr="1"]{
  background-image:linear-gradient(to left, rgba(0,0,0,.10), rgba(0,0,0,0) 22px);
  background-repeat:no-repeat;
  background-position:right center;
  background-size:22px 100%;
}

/* ── 5 · Dialoge ─────────────────────────────────────────────────────────── */
/* Einheitliche Deckelung: nie ueber den Rand, nie hoeher als das Fenster —
   auch im Querformat, wo 90vh bislang mehr war als sichtbar. */

html .modal-bg{ padding:12px; }
html .modal, html .dlg{
  max-width:min(94vw, 100%);
  max-height:88svh;
  overflow-y:auto;
}
@media (max-width: 560px){
  html .modal, html .dlg{ width:100%; padding-inline:clamp(14px, 4vw, 24px); }
  html .modal .mrow, html .modal .frow, html .modal .frow2, html .modal .frow3,
  html .modal .form-grid, html .modal .field-row{ grid-template-columns:1fr; }
}

/* ── 6 · Vollhoehen-Huellen: dvh statt vh ───────────────────────────────── */
/* Auf Telefonen ist 100vh groesser als die sichtbare Flaeche (ein- und
   ausfahrende Browserleiste) — die Fußzeile lag „unter" dem Bildschirm. */

html .app{ height:100dvh; }
@supports (height: 100dvh){
  /* 50px war die Hoehe der Kopfzeile im Breitbild. Sie bricht aber um — dann
     rechnete das Layout mit einer Kopfzeile, die es nicht mehr gab. */
  html .layout{ min-height:calc(100dvh - var(--eris-kopf-unten,50px)); }
}

/* ── 7 · Querformat & flache Fenster ────────────────────────────────────── */
/* Querformat-Telefon, geteilter Bildschirm oder 150-%-Skalierung: die Hoehe
   ist knapp. Klebende Balken und Dialoge duerfen den Inhalt nicht auffressen. */

@media (max-height: 560px) and (orientation: landscape){
  html .topbar{ position:relative; min-height:42px; padding-block:3px; }
  /* Jede Zeile der Kopfzeile kostet hier spuerbar Inhalt: der Name entfaellt,
     der Avatar bleibt als Zugang zur eigenen Seite. */
  html .topbar-right .user-name{ display:none; }
  html .stationnav{ position:static; }
  html .modal, html .dlg{ max-height:96svh; }
  html .modal-bg{ padding:6px; align-items:flex-start; }
  html .main, html .sheet, html .wrap, html .page{ padding-block:10px; }
  html .eris-scrollx[data-sticky="1"]{ max-height:none; overflow-y:visible; }
}
/* Hochformat-Tablet: die feste Seitenspalte ist hier eine Spalte zu viel. */
@media (orientation: portrait) and (max-width: 820px){
  html .layout{ display:block; }
  html .bau-sidebar, html .master{ position:static; height:auto; }
}

/* ── 8 · Zeigergeraet & Pixeldichte ─────────────────────────────────────── */

@media (pointer: coarse){
  /* Fingerfreundliche Mindestgroeßen, ohne die Optik am Desktop zu aendern. */
  html .btn, html button, html select, html .tab, html .stab,
  html .sec-tab, html .mtab{ min-height:36px; }
  html .modal-x, html .modal-close{ min-width:36px; min-height:36px; }
}

/* Rasterlogos auf hoher Dichte nicht nachschaerfen lassen. */
@media (min-resolution: 1.5dppx){
  html .brand img, html .logo img{ image-rendering:auto; }
}

/* ── 9 · Druck ───────────────────────────────────────────────────────────── */
/* Im Druck gibt es keine Fenstergroeße — Rollbereiche wieder aufloesen. */
@media print{
  html .eris-scrollx{ overflow:visible !important; max-height:none !important; }
  html .eris-scrollx[data-mehr="1"]{ background-image:none; }
}


/* ── 10 · Wert und Einheit bleiben zusammen (Audit U-12) ────────────── */
/* Ein Umbruch zwischen Zahl und Einheit („4,3 Mio ⏎ €“) liest sich wie zwei
   Angaben statt einer. Gemessen am Server (17.09.2026, 958 px, angemeldet,
   echte Daten): 19 solcher Umbrüche bei rund 490 Euro-Vorkommen — und sie
   verteilen sich auf genau VIER WERT-ROLLEN, nicht auf Hunderte Textstellen.

   ⚠ WARUM CSS UND NICHT EIN GESCHÜTZTES LEERZEICHEN: die Einheit wird nicht an
     einer Stelle angehängt. Sie kommt überwiegend fertig formatiert aus dem
     Backend, im Frontend setzen sie 26 Dateien selbst zusammen. Ein NBSP wäre
     also kein „eine Stelle“, sondern Hunderte unsichtbarer Zeichen im Code —
     die Rolle dagegen gibt es viermal.
   ⚠ DIE LISTE IST GEMESSEN, NICHT GERATEN — und je Rolle GEKLEMMT: `.num` ist
     im Finanzbericht ein Nummernkreis und im Kalender ein Kachelwert, `.val`
     in der Balkenrangliste eine 11-px-Marke. Deshalb `td.num`/`th.num` und
     `.kcard .val`, nicht die nackte Klasse.
   ⚠ DER BREITENZUWACHS IST AUFGEFANGEN: wird eine Zahlenspalte dadurch breiter
     als ihr Platz, hüllt eris-responsive.js die Tabelle zur Laufzeit in
     `.eris-scrollx`. Am Server gegengemessen: Dokumentbreite 943 px und
     0 Überläufe — beides unverändert, Umbrüche 19 → 0.
   ⚠ BEWUSST NICHT DABEI: `.kval` (Portal-Leitkacheln) trägt gepflegten FREITEXT
     aus der kpis-Tabelle und darf als LETZTES an ihrem Leerzeichen umbrechen,
     nachdem die Sparkline-Spalte daneben schon ausgewichen ist (controlling.css,
     „Wer weicht zuerst“, 25.09.2026) — ein nowrap schöbe sie stattdessen über den
     Kachelrand. Gemessen bricht sie bei `--kachel-min` nirgends um.
   ⚠ EIN FLIESSTEXT BEKOMMT DIESE REGEL NIE: im Finanzbericht stehen zwei
     Euro-Beträge in Erklärsätzen (`.kern-x`). Ein nowrap auf einem Absatz schöbe
     ihn aus der Karte — dort ist der Umbruch der Preis für den Fließtext.      */
html .fvk-v .e,        /* FactSheet-Leitkachel: die Einheit neben dem Wert   */
html .k-v,             /* Kachelwert (Landesformel, Benchmark, Kapazität, …) */
html .kcard .val,      /* Kachelwert (Baumonitoring, Finanzbericht, …)       */
html table td.num,     /* Zahlenspalte, rechtsbündig, tabular-nums           */
html table th.num{ white-space:nowrap; }
