/* ═══ tokens.css ═══ */
/* ════════════════════════════════════════════════════════════════════════════
   tokens.css — Shared Design Tokens (Brand + Semantik + Typo + Spacing)
   ────────────────────────────────────────────────────────────────────────────
   Eine Datei, von ALLEN vier Surfaces geladen (Admin, Mieter-Portal,
   Eigentuemer-Portal, oeffentliche Homepage). Aenderungen hier propagieren
   ueberall.

   Layering:
     tokens.css           ← Foundation (DIESE Datei). Brand + Semantik + Typo.
     app.css              ← Bootstrap-Erweiterungen (Admin-Forms etc.)
     app.industrial.css   ← Admin-Theme-Override (Dark/Light, Neon-Akzent)
     portal.css           ← Portale: gemeinsame Komponenten ueber tokens
     (inline-Style)       ← Pro-Portal-Akzent (Mieter blau / Eigentuemer gruen)

   Brand-Entscheidung (Adrian):
     Aktuell sind --brand-* Platzhalter, die zu den bisher genutzten Werten
     passen. Sobald Adrian ein finales Logo + Hausfarbe hat, hier die fuenf
     ROOT-Variablen umstellen — alle vier Surfaces folgen automatisch.
   ════════════════════════════════════════════════════════════════════════════ */

:root {

  /* ── Brand ─────────────────────────────────────────────────────────────────
     Markenname entschieden 2026-05-08: "Rottensteiner Hausverwaltung"
     2026-05-16: Brand-Palette auf "Bodensee" geupdated (Audit-Befund: drei
     parallele Token-Namensraeume — siehe /docs/design-system/README.md).
     Neue Werte synchron mit public.css. Alle vier Surfaces sollen perspekti-
     visch auf --color-* migrieren. */
  --brand-name:                'Rottensteiner Hausverwaltung';
  --brand-primary:             #2D6B8E;     /* Bodensee-Mittelblau */
  --brand-primary-hover:       #1A4D6B;
  --brand-primary-on:          #FFFFFF;     /* Text auf Brand-Primary */

  /* ── Bodensee-Surface-Palette (NEUE Single-Source-of-Truth) ───────────────
     Semantische Namen statt Farbnamen ("ink" statt "dark" — bleibt stabil
     auch wenn die konkrete Farbe in Zukunft wechselt). */
  --color-ink:                 #15243C;     /* Haupttext + dunkle Surfaces */
  --color-ink-soft:            #6B5E4E;     /* Sekundaertext (warmer Erd-Ton) */
  --color-ink-faint:           #ACA193;     /* Captions, sehr leise */
  --color-brand:               #2D6B8E;     /* See-Blau, Haupt-Akzent */
  --color-brand-deep:          #1A4D6B;     /* See-Blau dunkel (Hover) */
  --color-brand-mist:          #B8D4E3;     /* Morgennebel-Hell, Highlight */
  --color-brand-haze:          #E5EFF5;     /* Sehr heller Brand-Hintergrund */
  --color-paper:               #F7F2EA;     /* Warmes Bodenseepapier */
  --color-paper-deep:          #EDE5D4;     /* Schilf-Beige */
  --color-line:                #DDD4C2;     /* Sanfter Trenner (warm) */
  --color-line-soft:           #ECE5D8;     /* Subtiler Trenner */
  --color-accent:              #C8553D;     /* Apfelblueten-Akzent (sparsam!) */
  --color-admin-accent:        #5BB0D9;     /* Helles See-Blau — Admin-Industrial Akzent (Dark) */
  --color-admin-accent-light:  #2D6B8E;     /* Bodensee-Blau — Admin-Industrial Akzent (Light) */

  --brand-accent-mieter:       #1E3A5F;     /* Akzent fuer Mieter-Portal */
  --brand-accent-eigentuemer:  #2D4A3E;     /* Akzent fuer Eigentuemer-Portal (gruen) */
  --brand-accent-industrial:   #5BB0D9;     /* Akzent fuer Admin-Industrial (helles See-Blau, Dark) */

  /* ── Semantische Statusfarben ──────────────────────────────────────────────
     Identisch ueber alle Surfaces. Werden ggf. pro Theme noch ge-tweaked. */
  --color-success:             #15803D;
  --color-success-bg:          #DCFCE7;
  --color-success-on:          #FFFFFF;
  --color-success-dark:        #00E08F;     /* Dark-Theme/Begehung-Erfolg (hoher Kontrast auf dunklem BG) */
  --color-warning:             #B45309;
  --color-warning-bg:          #FEF3C7;
  --color-warning-on:          #1A1A1A;
  --color-danger:              #B91C1C;
  --color-danger-bg:           #FEE2E2;
  --color-danger-on:           #FFFFFF;
  --color-info:                #1D4ED8;
  --color-info-bg:             #DBEAFE;
  --color-info-on:             #FFFFFF;
  --color-muted:               #525252;
  --color-muted-bg:            #E5E5E5;
  --color-muted-on:            #FFFFFF;
  --color-accent-gold:         #FFD166;     /* Portal-Gold-Akzent (Nav-aktiv, Schnellzugriff) */

  /* ── HITL-Ampel (Autonomie-Stufe) ──────────────────────────────────────────
     Bewusst nicht success/warning/danger nachgemalt — semantisch anders:
     gruen = autonom, gelb = HITL-Pflicht, rot = blockiert. Eine HITL-Pille
     "rot" ist KEIN Fehler, sondern eine bewusste Autonomie-Entscheidung. */
  --color-traffic-rot:         #7F1D1D;
  --color-traffic-rot-bg:      #FECACA;
  --color-traffic-rot-on:      #FFFFFF;
  --color-traffic-gelb:        #92400E;
  --color-traffic-gelb-bg:     #FDE68A;
  --color-traffic-gelb-on:     #1A1A1A;
  --color-traffic-gruen:       #14532D;
  --color-traffic-gruen-bg:    #BBF7D0;
  --color-traffic-gruen-on:    #FFFFFF;

  /* ── Typografie ────────────────────────────────────────────────────────────
     Sans-Stack: Geist (lokal) → System-UI. Mono-Stack: JetBrains Mono → ui-mono.
     Konvention (siehe app/CLAUDE.md): keine fonts.googleapis.com — privacy + offline. */
  --font-sans:                 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --font-mono:                 'JetBrains Mono', 'Geist Mono', ui-monospace, 'SF Mono', Consolas, monospace;
  --font-display:              var(--font-sans);

  --fs-xs:     0.75rem;
  --fs-sm:     0.85rem;
  --fs-base:   0.95rem;
  --fs-lg:     1.15rem;
  --fs-xl:     1.4rem;
  --fs-2xl:    1.85rem;

  --fw-regular: 400;
  --fw-medium:  500;
  --fw-bold:    600;

  /* ── Spacing-Skala (4px-Raster) ─────────────────────────────────────────── */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;

  /* ── Border-Radien ─────────────────────────────────────────────────────── */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-xl: 16px;
  --radius-pill: 9999px;

  /* ── Schatten (sehr dezent, Industrial-tauglich) ──────────────────────── */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04);
  --shadow-md: 0 2px 6px rgba(0, 0, 0, 0.06);
  --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.08);

  /* ── Fokus-Ring (gilt fuer alle Surfaces) ───────────────────────────────── */
  --focus-ring-width: 3px;
  --focus-ring-offset: 2px;
  --focus-ring-color: var(--brand-accent-industrial);   /* Hoher Kontrast auch auf dunklen Themes */

  /* ════════════════════════════════════════════════════════════════════════
     Public-Surface-Tokens (Bodensee-Homepage)
     ────────────────────────────────────────────────────────────────────────
     Status 2026-05-28 nach UI-Audit: das Repo hat **zwei stabile**
     Token-Familien, KEINE laufende Migration mehr:

       --color-*       Foundation/Bodensee-Surface (semantisch, theme-stabil)
       --hv-*          Admin-Industrial (theme-variant: Light/Dark)

     Beide sind Single-Source-of-Truth fuer ihre Surface. Die Aliases unten
     (--dark/--blue/--cream/--surface/--border/--text/--text-muted/--accent-spark)
     werden von public.css + ~25 Homepage-Views aktiv genutzt — keine
     "Backward-Compat", sondern produktive API. Tot war nur --dark-soft (raus).

     Wenn du neuen Public-Homepage-Code schreibst: --color-*-Tokens nutzen
     (semantisch klarer). Bestehende --dark/--blue/--cream-Stellen muessen
     NICHT migriert werden — sind via Aliases korrekt resolved.
     ════════════════════════════════════════════════════════════════════════ */

  /* Public-Homepage-Surface-Tokens (Bodensee-Palette) */
  --dark:                      var(--color-ink);
  --blue:                      var(--color-brand);
  --blue-dark:                 var(--color-brand-deep);
  --blue-light:                var(--color-brand-haze);
  --blue-mid:                  var(--color-brand-mist);
  --cream:                     var(--color-paper);
  --cream-dark:                var(--color-paper-deep);
  --surface:                   #FFFFFF;
  --border:                    var(--color-line);
  --text:                      var(--color-ink);
  --text-muted:                var(--color-ink-soft);
  --accent-spark:              var(--color-accent);

  /* Typografie-Aliases (Public benutzt eigene Display-Font Fraunces) */
  --font-display-public:       'Fraunces', Georgia, 'Times New Roman', serif;
  --font-sans-public:          'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
}

/* ════════════════════════════════════════════════════════════════════════════
   Globaler Fokus-Ring
   ────────────────────────────────────────────────────────────────────────────
   Konsistent ueber Admin und beide Portale. Nutzt :focus-visible (nur Tastatur),
   sodass Maus-Klicks keinen sichtbaren Ring triggern.
   ════════════════════════════════════════════════════════════════════════════ */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--radius-sm);
}

/* ════════════════════════════════════════════════════════════════════════════
   .hv-skip-link — Skip-to-content-Link (BFSG / WCAG 2.4.1)
   ────────────────────────────────────────────────────────────────────────────
   Unsichtbar bis Tab-Fokus, dann oben links. Konsistent ueber alle Surfaces.
   Public-Layout hat seine eigene .skip-link mit Bodensee-Farben, die hier
   ist die universelle Variante fuer Admin + beide Portale + Auth.
   ════════════════════════════════════════════════════════════════════════════ */
.hv-skip-link {
  position: absolute;
  top: -100px;
  left: 0;
  z-index: 10000;
  background: var(--color-ink, #15243C);
  color: #fff;
  padding: 0.75rem 1.25rem;
  border-radius: 0 0 var(--radius-md, 8px) 0;
  font-weight: 700;
  font-size: 0.9rem;
  text-decoration: none;
  transition: top 0.2s ease;
}
.hv-skip-link:focus,
.hv-skip-link:focus-visible {
  top: 0;
  outline: 3px solid var(--color-brand, #2D6B8E);
  outline-offset: 2px;
  color: #fff;
}

/* ════════════════════════════════════════════════════════════════════════════
   .hv-pill — Status-Pille (semantik + HITL-Ampel)
   ────────────────────────────────────────────────────────────────────────────
   Ersetzt Bootstrap-`badge bg-*` an allen Stellen, an denen ein Status oder
   eine Autonomie-Ampel angezeigt wird. Die Klasse setzt nur Layout (Padding,
   Radius, Schrift); die Variante (success / warning / danger / info / muted /
   traffic-*) bringt Hintergrund + Vordergrund.

   Nutzung:
     <span class="hv-pill hv-pill--success">Bezahlt</span>
     <span class="hv-pill hv-pill--traffic-gelb">HITL: gelb</span>

   Helper (Handlebars): {{statusPill domain wert}} — siehe server.js.
   Mapping siehe app/src/utils/statusPills.js.
   ════════════════════════════════════════════════════════════════════════════ */
.hv-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0.15rem 0.6rem;
  border-radius: var(--radius-pill);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  font-family: var(--font-sans);
  line-height: 1.4;
  white-space: nowrap;
  vertical-align: baseline;
  letter-spacing: 0.01em;
}
.hv-pill--success      { background: var(--color-success-bg); color: var(--color-success); }
.hv-pill--warning      { background: var(--color-warning-bg); color: var(--color-warning); }
.hv-pill--danger       { background: var(--color-danger-bg);  color: var(--color-danger);  }
.hv-pill--info         { background: var(--color-info-bg);    color: var(--color-info);    }
.hv-pill--muted        { background: var(--color-muted-bg);   color: var(--color-muted);   }
.hv-pill--traffic-rot   { background: var(--color-traffic-rot-bg);   color: var(--color-traffic-rot);   }
.hv-pill--traffic-gelb  { background: var(--color-traffic-gelb-bg);  color: var(--color-traffic-gelb);  }
.hv-pill--traffic-gruen { background: var(--color-traffic-gruen-bg); color: var(--color-traffic-gruen); }

/* Dark-Theme-Override (Industrial Admin): Hintergruende sind im Dark-Mode
   sehr hell → Pille wird zu grell. Auf gedaempfte Versionen mit Alpha. */
[data-theme="dark"] .hv-pill--success      { background: rgba(34, 197, 94, 0.18); color: #6EE7A7; }
[data-theme="dark"] .hv-pill--warning      { background: rgba(245, 158, 11, 0.18); color: #FCD34D; }
[data-theme="dark"] .hv-pill--danger       { background: rgba(239, 68, 68, 0.18);  color: #FCA5A5; }
[data-theme="dark"] .hv-pill--info         { background: rgba(59, 130, 246, 0.18); color: #93C5FD; }
[data-theme="dark"] .hv-pill--muted        { background: rgba(160, 160, 160, 0.18); color: #D4D4D4; }
[data-theme="dark"] .hv-pill--traffic-rot   { background: rgba(220, 38, 38, 0.22);  color: #FCA5A5; }
[data-theme="dark"] .hv-pill--traffic-gelb  { background: rgba(234, 179, 8, 0.22);  color: #FDE047; }
[data-theme="dark"] .hv-pill--traffic-gruen { background: rgba(34, 197, 94, 0.22);  color: #86EFAC; }

@media print {
  .hv-pill {
    background: transparent !important;
    color: #000 !important;
    border: 1px solid #000;
    padding: 0.05rem 0.4rem;
  }
}

/* ════════════════════════════════════════════════════════════════════════════
   Field-Level-Validation (CSS-only, kein JS)
   ────────────────────────────────────────────────────────────────────────────
   Pattern: <input required pattern="..." />
            <small class="hv-field-error">Bitte E-Mail im Format ... eingeben</small>

   Sichtbarkeitslogik nutzt :user-invalid (Browser-native, greift erst nach
   Blur oder Submit-Versuch — kein roter Initial-Hint bei leeren Feldern).
   Fallback fuer Browser ohne :user-invalid: :invalid:not(:placeholder-shown)
   :not(:focus). Damit der Fallback greift, sollte das Eingabefeld einen
   placeholder haben (z.B. " " als Spacer fuer Floating-Labels).

   Die Klasse .hv-form-error markiert serverseitige Validation-Fehler — die
   bleibt unabhaengig vom user-invalid-Zustand sichtbar.
   ════════════════════════════════════════════════════════════════════════════ */
.hv-field-error {
  display: none;
  font-size: var(--fs-xs);
  color: var(--color-danger);
  margin-top: 0.25rem;
  line-height: 1.35;
}

/* :user-invalid (modern) — greift nach User-Interaktion */
.form-control:user-invalid + .hv-field-error,
.form-select:user-invalid + .hv-field-error,
textarea:user-invalid + .hv-field-error {
  display: block;
}

/* Fallback :invalid + :not(:placeholder-shown) :not(:focus) */
.form-control:invalid:not(:placeholder-shown):not(:focus) + .hv-field-error,
.form-select:invalid:not(:focus) + .hv-field-error,
textarea:invalid:not(:placeholder-shown):not(:focus) + .hv-field-error {
  display: block;
}

/* Border-Akzent fuer invalid Eingaben (gleiche Sichtbarkeitslogik) */
.form-control:user-invalid,
.form-select:user-invalid,
textarea:user-invalid {
  border-color: var(--color-danger);
}
.form-control:invalid:not(:placeholder-shown):not(:focus),
textarea:invalid:not(:placeholder-shown):not(:focus) {
  border-color: var(--color-danger);
}

/* Server-seitige Inline-Fehler (immer sichtbar wenn die Klasse gesetzt ist) */
.hv-form-error {
  display: block;
  font-size: var(--fs-xs);
  color: var(--color-danger);
  margin-top: 0.25rem;
  line-height: 1.35;
}
.hv-form-error::before {
  content: '⚠ ';
  font-weight: var(--fw-bold);
}

/* Form-Field mit Server-Fehler: Container bekommt eine ID, die im
   form-errors-Partial als Anchor-Link verlinkt ist. Scroll-Margin-Top
   sorgt dafuer, dass der Anchor nicht hinter der Sidebar-Header verschwindet. */
.hv-field--error {
  scroll-margin-top: 80px;
}
.hv-field--error .form-control,
.hv-field--error .form-select,
.hv-field--error textarea {
  border-color: var(--color-danger);
}

/* ════════════════════════════════════════════════════════════════════════════
   Tabellen-Pattern (Phase 2.2)
   ────────────────────────────────────────────────────────────────────────────
   .hv-num         numerische Zelle (rechtsbuendig + Mono + tabular-nums)
   .hv-table-clickable  ganze Zeile als Click-Target (data-href)
   [data-density]  Density-Toggle (compact | cozy | comfy)
   ════════════════════════════════════════════════════════════════════════════ */
.hv-num {
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.hv-table-clickable tbody tr[data-href] {
  cursor: pointer;
  transition: background-color 120ms ease;
}
.hv-table-clickable tbody tr[data-href]:hover {
  background-color: rgba(0, 0, 0, 0.025);
}
[data-theme="dark"] .hv-table-clickable tbody tr[data-href]:hover {
  background-color: rgba(255, 255, 255, 0.04);
}

/* Density-Toggle: globale Einstellung in localStorage 'hv-density'.
   Default = cozy (Bootstrap-Default). Compact spart 30 % Vertikal-Raum,
   Comfy ist fuer Lese-Modus. */
[data-density="compact"] .table > :not(caption) > * > *,
[data-density="compact"] table > :not(caption) > * > * {
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
}
[data-density="compact"] .table,
[data-density="compact"] table.table-sm {
  font-size: var(--fs-xs);
}
[data-density="comfy"] .table > :not(caption) > * > *,
[data-density="comfy"] table > :not(caption) > * > * {
  padding-top: 0.85rem;
  padding-bottom: 0.85rem;
}

/* Density-Toggle-Button selbst (in Sidebar-Footer einsetzbar) */
.hv-density-toggle {
  display: inline-flex;
  gap: 1px;
  background: var(--color-muted-bg, #E5E5E5);
  border-radius: var(--radius-pill);
  padding: 2px;
}
.hv-density-toggle button {
  background: transparent;
  border: 0;
  padding: 0.15rem 0.55rem;
  font-size: var(--fs-xs);
  color: var(--color-muted);
  border-radius: var(--radius-pill);
  cursor: pointer;
}
.hv-density-toggle button[aria-pressed="true"] {
  background: var(--brand-primary, #1E3A5F);
  color: var(--brand-primary-on, #fff);
}
[data-theme="dark"] .hv-density-toggle {
  background: rgba(255, 255, 255, 0.06);
}
[data-theme="dark"] .hv-density-toggle button[aria-pressed="true"] {
  background: var(--brand-accent-industrial);
  color: #0A0A0B;
}

/* ════════════════════════════════════════════════════════════════════════════
   Mobile-Bottom-Nav (Phase 2.3)
   ────────────────────────────────────────────────────────────────────────────
   Fixe Navigation am unteren Bildschirmrand auf Mobile (<768px). Verdeckt
   die Sidebar nicht — Sidebar bleibt als Hamburger/Offcanvas verfuegbar.
   Touch-Targets mindestens 44x44 px (Apple/Google-Guideline).
   ════════════════════════════════════════════════════════════════════════════ */
.hv-bottom-nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 1030;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0;
  background: var(--brand-primary, #1E3A5F);
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  padding: 0.25rem 0 max(0.25rem, env(safe-area-inset-bottom));
  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.08);
}
[data-theme="dark"] .hv-bottom-nav {
  background: #0F1418;
  border-top-color: rgba(255, 255, 255, 0.08);
}
.hv-bottom-nav-item {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0.35rem 0.5rem;
  background: transparent;
  border: 0;
  text-decoration: none;
  color: rgba(255, 255, 255, 0.65);
  font-size: var(--fs-xs);
  line-height: 1.1;
  transition: color 100ms ease;
  cursor: pointer;
}
.hv-bottom-nav-item:hover,
.hv-bottom-nav-item:focus-visible {
  color: rgba(255, 255, 255, 0.92);
}
.hv-bottom-nav-item.active {
  color: var(--brand-accent-industrial, #5BB0D9);
}
.hv-bottom-nav-item i {
  font-size: 1.15rem;
  margin-bottom: 2px;
}
.hv-bottom-nav-item span {
  font-size: 0.65rem;
  letter-spacing: 0.02em;
}
/* Ungelesene-Mails-Badge: roter Kreis oben rechts am Icon */
.hv-bottom-nav-badge {
  position: absolute;
  top: 2px;
  left: 50%;
  margin-left: 0.4rem;
  min-width: 1.05rem;
  padding: 0.05rem 0.3rem;
  background: var(--hv-danger, #d92d20);
  color: #fff;
  font-size: 0.6rem;
  font-weight: 700;
  line-height: 1.3;
  text-align: center;
  border-radius: 10px;
  pointer-events: none;
}

/* Bottom-Nav verdeckt unteren Content — Padding fuer den Content auf Mobile */
@media (max-width: 767.98px) {
  body:has(.hv-bottom-nav) .hv-content,
  body:has(.hv-bottom-nav) main {
    padding-bottom: 4.5rem;
  }
}

/* ════════════════════════════════════════════════════════════════════════════
   Inline-Hilfe — Tooltip-Popover (Phase 2.5)
   ────────────────────────────────────────────────────────────────────────────
   Klick-getriggertes Popover fuer komplexe Felder (Kappungsgrenze,
   Verteilerschluessel, AfA-Restwert, Mietspiegel). Kein Hover, weil Mobile
   das nicht hat. Keine Wall-of-Text — max. 2-3 Saetze pro Tip.
   ════════════════════════════════════════════════════════════════════════════ */
.hv-tip {
  cursor: pointer;
  color: var(--color-muted, #525252);
  margin-left: 0.25rem;
  font-size: 0.95em;
  vertical-align: middle;
  transition: color 100ms ease;
}
.hv-tip:hover,
.hv-tip:focus-visible {
  color: var(--brand-primary, #1E3A5F);
}
[data-theme="dark"] .hv-tip:hover,
[data-theme="dark"] .hv-tip:focus-visible {
  color: var(--brand-accent-industrial, #5BB0D9);
}

/* Popover-Variante: schmaler + im Industrial-Look */
.popover.hv-tip-popover {
  max-width: 300px;
  font-size: var(--fs-sm);
  border: 1px solid var(--hv-border, #e5e7eb);
  box-shadow: var(--shadow-lg);
}
.popover.hv-tip-popover .popover-header {
  font-weight: var(--fw-bold);
  font-size: var(--fs-sm);
  background: var(--color-muted-bg, #f1f5f9);
  border-bottom: 0;
}
.popover.hv-tip-popover .popover-body {
  font-size: var(--fs-sm);
  line-height: 1.5;
}
[data-theme="dark"] .popover.hv-tip-popover {
  background: #1A1A1B;
  border-color: rgba(255, 255, 255, 0.12);
  color: #E5E5E5;
}
[data-theme="dark"] .popover.hv-tip-popover .popover-header {
  background: #0F1418;
  color: #fff;
}
[data-theme="dark"] .popover.hv-tip-popover .popover-arrow::after {
  border-top-color: #1A1A1B;
  border-bottom-color: #1A1A1B;
}

/* ════════════════════════════════════════════════════════════════════════════
   Auto-Save-Indikator (Phase 2.1)
   ────────────────────────────────────────────────────────────────────────────
   Wird in Wizard-Steps (Mieterhoehung, NK, Onboarding) eingesetzt.
   ════════════════════════════════════════════════════════════════════════════ */
.hv-autosave {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: var(--fs-xs);
  color: var(--color-success);
  opacity: 0;
  transition: opacity 200ms ease;
  pointer-events: none;
}
.hv-autosave.hv-autosave-visible {
  opacity: 1;
}
.hv-autosave::before {
  content: '✓';
  font-weight: var(--fw-bold);
}
[data-theme="dark"] .hv-autosave {
  color: #6EE7A7;
}

/* ════════════════════════════════════════════════════════════════════════════
   Step-Wizard (Phase 3.1)
   ────────────────────────────────────────────────────────────────────────────
   Fortschrittsanzeige fuer Multi-Step-Forms. Status:
   done = grün + Haken + klickbar; active = Brand + fett; todo = muted.
   ════════════════════════════════════════════════════════════════════════════ */
.hv-step-wizard-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  font-size: var(--fs-sm);
}
.hv-step-wizard-item {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.75rem;
  border-radius: var(--radius-pill);
  background: transparent;
}
.hv-step-wizard-item + .hv-step-wizard-item::before {
  content: '›';
  color: var(--color-muted);
  margin-right: 0.25rem;
  font-size: 1.1em;
}
.hv-step-wizard-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  background: var(--color-muted-bg);
  color: var(--color-muted);
}
.hv-step-wizard-item.done .hv-step-wizard-num {
  background: var(--color-success);
  color: var(--color-success-on);
}
.hv-step-wizard-item.active .hv-step-wizard-num {
  background: var(--brand-primary);
  color: var(--brand-primary-on);
}
.hv-step-wizard-titel {
  color: var(--color-muted);
  text-decoration: none;
}
.hv-step-wizard-item.done .hv-step-wizard-titel { color: var(--color-success); }
.hv-step-wizard-item.done a.hv-step-wizard-titel:hover { text-decoration: underline; }
.hv-step-wizard-item.active .hv-step-wizard-titel {
  color: var(--brand-primary);
  font-weight: var(--fw-bold);
}
[data-theme="dark"] .hv-step-wizard-num {
  background: rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 0.7);
}
[data-theme="dark"] .hv-step-wizard-item.done .hv-step-wizard-num {
  background: rgba(34, 197, 94, 0.3);
  color: #6EE7A7;
}
[data-theme="dark"] .hv-step-wizard-item.done .hv-step-wizard-titel { color: #6EE7A7; }
[data-theme="dark"] .hv-step-wizard-item.active .hv-step-wizard-num {
  background: var(--brand-accent-industrial);
  color: #0A0A0B;
}
[data-theme="dark"] .hv-step-wizard-item.active .hv-step-wizard-titel {
  color: var(--brand-accent-industrial);
}

/* ════════════════════════════════════════════════════════════════════════════
   Onboarding-Tour-Spotlight (Phase 3.6)
   ────────────────────────────────────────────────────────────────────────────
   Dunkler Backdrop mit "ausgeschnittenem" Spotlight um das Tour-Ziel-Element.
   Bubble (.hv-tour-bubble) zeigt Titel + Markdown-Body + Skip/Weiter.
   ════════════════════════════════════════════════════════════════════════════ */
.hv-tour-overlay {
  position: fixed;
  inset: 0;
  z-index: 1090;
  pointer-events: none;
}
.hv-tour-overlay::before {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(10, 10, 11, 0.55);
  backdrop-filter: blur(2px);
  pointer-events: auto;
}
.hv-tour-spotlight {
  position: absolute;
  border-radius: var(--radius-md);
  box-shadow: 0 0 0 9999px rgba(10, 10, 11, 0.55), 0 0 0 3px var(--brand-accent-industrial);
  pointer-events: none;
  transition: all 200ms ease;
}
.hv-tour-bubble {
  position: absolute;
  max-width: 360px;
  background: #fff;
  color: #1A1A1A;
  border-radius: var(--radius-md);
  padding: 1rem 1.15rem;
  box-shadow: var(--shadow-lg);
  pointer-events: auto;
  font-size: var(--fs-sm);
  line-height: 1.5;
}
.hv-tour-bubble h3 {
  margin: 0 0 0.5rem 0;
  font-size: var(--fs-base);
  font-weight: var(--fw-bold);
}
.hv-tour-bubble p { margin: 0 0 0.5rem 0; }
.hv-tour-bubble p:last-child { margin-bottom: 0; }
.hv-tour-bubble ul { margin: 0 0 0.75rem 0; padding-left: 1.25rem; }
.hv-tour-bubble strong { color: var(--brand-primary); }
.hv-tour-actions {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  margin-top: 0.85rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--hv-border, #e5e7eb);
}
.hv-tour-progress {
  font-size: var(--fs-xs);
  color: var(--color-muted);
  margin-right: auto;
}
[data-theme="dark"] .hv-tour-bubble {
  background: #1A1A1B;
  color: #E5E5E5;
}
[data-theme="dark"] .hv-tour-bubble strong { color: var(--brand-accent-industrial); }
[data-theme="dark"] .hv-tour-actions { border-top-color: rgba(255, 255, 255, 0.1); }

/* ════════════════════════════════════════════════════════════════════════════
   A11y: prefers-reduced-motion (Phase 3.5)
   ────────────────────────────────────────────────────────────────────────────
   Wenn der User reduced-motion eingestellt hat, schalten wir Animationen
   + Transitions global ab. Greift auf alle hv-* Komponenten, Bootstrap
   (.fade, .collapse) UND inline transition-Werte.
   ════════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ════════════════════════════════════════════════════════════════════════════
   S-NAV — Klick-Feedback bei klassischer Navigation (alle 4 Surfaces)
   ──────────────────────────────────────────────────────────────────────────── */

/* Top-Progress-Bar (gesteuert von public/js/nav-progress.js). */
.hv-nav-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 3px;
  width: 0;
  background: var(--brand-primary, #2D6B8E);
  box-shadow: 0 0 8px rgba(45, 107, 142, 0.55);
  opacity: 0;
  z-index: 2000;
  pointer-events: none;
  transition: width 0.2s ease, opacity 0.2s ease;
}
@media (prefers-reduced-motion: reduce) {
  /* Balken bleibt als reines „läuft"-Signal, aber ohne laufende Animation. */
  .hv-nav-progress { transition: none; }
}

/* View-Transitions: BEWUSST DEAKTIVIERT (Entscheidung 2026-06-10 nach 3
   Guard-Iterationen). @view-transition { navigation: auto } erzeugt in Chromium
   beim Browser-Teardown/Viewport-Resize einen in-page NICHT abfangbaren
   "Transition was aborted"-pageerror (weder pagereveal/pageswap-Catch noch
   früher unhandledrejection-Filter noch skipTransition halfen — die Seite ist
   beim Abort bereits in Zerstörung). Das verschmutzt jeden automatisierten
   Browser-Lauf und maskiert echte Fehler. Der Weiß-Blitz ist stattdessen über
   den dunklen Erst-Paint (Inline-Style in main.handlebars) gelöst, das
   Klick-Feedback über die Top-Progress-Bar (nav-progress.js). Die vt-guard-/
   skipTransition-Mechanik bleibt harmlos bestehen, falls VT später (besserer
   Browser-Support) reaktiviert wird. */

/* ════════════════════════════════════════════════════════════════════════════
   S-CHART/S-LOAD — Skeleton-Platzhalter (alle Surfaces; .hv-skeleton liegt sonst
   nur in app.css = Admin, fehlt aber im Portal/Public). Theme-neutral.
   ──────────────────────────────────────────────────────────────────────────── */
.hv-skeleton {
  position: relative;
  width: 100%;
  border-radius: 6px;
  background: linear-gradient(90deg,
    rgba(128, 128, 128, 0.06) 0%,
    rgba(128, 128, 128, 0.12) 50%,
    rgba(128, 128, 128, 0.06) 100%);
  background-size: 200% 100%;
  animation: hv-skeleton-pulse 1.5s ease-in-out infinite;
}
.hv-skeleton-msg {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 0.85rem;
  color: var(--color-ink-soft, #6c757d);
  white-space: nowrap;
}
@keyframes hv-skeleton-pulse {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .hv-skeleton { animation: none; }
}

/* Chart-Container: reserviert Platz (kein CLS/Leerblitz) + Skeleton liegt als
   Overlay über dem Canvas, bis nav die [data-chart-skeleton]-Elemente entfernt. */
.hv-chart-wrap { position: relative; }
.hv-chart-skeleton {
  position: absolute;
  inset: 0;
  height: auto !important;
  border-radius: 8px;
}

/* ════════════════════════════════════════════════════════════════════════════
   S-FILE — gestylter Datei-Upload (deutsch + Dateiname). Ersetzt das native,
   teils englische <input type=file>. Label umschließt das visuell versteckte
   Input; JS (app.js / portal-init.js) füllt [data-upload-name].
   ──────────────────────────────────────────────────────────────────────────── */
.hv-upload { position: relative; display: inline-flex; align-items: center; gap: 10px; flex-wrap: wrap; cursor: pointer; max-width: 100%; }
.hv-upload__input {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); border: 0;
}
.hv-upload__btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 0.45rem 0.9rem; border-radius: 8px;
  border: 1px solid var(--color-line, #ced4da);
  background: var(--color-paper-deep, #f1f3f5);
  color: var(--color-ink, #15243C);
  font-size: 0.9rem; font-weight: 500; white-space: nowrap;
}
.hv-upload:hover .hv-upload__btn,
.hv-upload:focus-within .hv-upload__btn {
  border-color: var(--brand-primary, #2D6B8E);
  background: var(--color-brand-haze, #E5EFF5);
}
.hv-upload__name { font-size: 0.85rem; color: var(--color-ink-soft, #6c757d); word-break: break-word; }


/* ═══ portal.css ═══ */
/* ════════════════════════════════════════════════════════════════════════════
   portal.css — Geteiltes Stylesheet fuer Mieter- und Eigentuemer-Portal
   ────────────────────────────────────────────────────────────────────────────
   Vorraussetzung: tokens.css ist VOR dieser Datei geladen.

   Pro-Portal-Akzent (Mieter blau / Eigentuemer gruen) wird ueber das CSS-
   Custom-Property --portal-accent gesetzt — entweder als Inline-Style auf
   <body data-portal="..."> oder per separatem Block. Default = Brand-Primary.
   ════════════════════════════════════════════════════════════════════════════ */

/* ── Marken-Fonts (Bodensee): Geist Sans + Fraunces Display — lokal gehostet.
   Lagen bisher nur in public.css; ohne sie fielen die Portale auf System-Fonts
   zurueck (Haupt-Grund fuer den "generischen Bootstrap-Look"). font-display:swap
   -> kein Layout-Blocking. (P5 Portal-Marke an Homepage angeglichen, 2026-06-10) */
@font-face {
  font-family: 'Fraunces';
  src: url('/fonts/Fraunces-Variable.woff2?v=sub1') format('woff2-variations'),
       url('/fonts/Fraunces-Variable.woff2?v=sub1') format('woff2');
  font-weight: 100 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Geist';
  src: url('/fonts/Geist-Variable.woff2?v=sub1') format('woff2-variations'),
       url('/fonts/Geist-Variable.woff2?v=sub1') format('woff2');
  font-weight: 100 900; font-style: normal; font-display: swap;
}

:root {
  --portal-accent: var(--brand-primary);
  --portal-accent-on: var(--brand-primary-on);
}

body[data-portal="mieter"] {
  --portal-accent: var(--brand-accent-mieter);
}
body[data-portal="eigentuemer"] {
  --portal-accent: var(--brand-accent-eigentuemer);
}

/* ── Body / Background ───────────────────────────────────────────────────── */
body.portal-body {
  font-family: var(--font-sans);
  --font-display: var(--font-display-public);   /* Fraunces fuer Ueberschriften (wie Homepage) */
  background: var(--color-paper);                /* warmes Bodenseepapier statt kuehlem Grau */
  color: var(--color-ink);
  min-height: 100vh;
}

/* ── Navbar (top, dunkel-akzent) ─────────────────────────────────────────── */
.portal-navbar {
  background: var(--portal-accent);
  color: var(--portal-accent-on);
}
.portal-navbar .navbar-brand,
.portal-navbar .nav-link {
  color: var(--portal-accent-on);
}
.portal-navbar .nav-link:hover,
.portal-navbar .nav-link.active {
  color: var(--portal-accent-on);
  font-weight: var(--fw-bold);
}
.portal-navbar .navbar-brand {
  font-family: var(--font-display);
  font-weight: var(--fw-bold);
}
.portal-navbar .btn-outline-light {
  border-color: rgba(255, 255, 255, 0.5);
}
.portal-navbar .btn-outline-light:hover {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.8);
}

/* ── Container ───────────────────────────────────────────────────────────── */
.portal-container {
  max-width: 980px;
  margin: var(--space-5) auto;
  padding: 0 var(--space-4);
}

/* ── Karte (= Card-Wrapper, eigene Klasse weil Bootstrap .card uebersteuert) */
.karte {
  background: #FFFFFF;
  border-radius: var(--radius-md);
  padding: var(--space-5);
  box-shadow: var(--shadow-sm);
  margin-bottom: var(--space-4);
  border: 1px solid var(--color-line);   /* warmer Trenner statt kuehlem Grau */
}
.karte h2 {
  font-family: var(--font-display);       /* Fraunces — Homepage-Anmutung */
  font-size: var(--fs-lg);
  margin: 0 0 var(--space-3);
  color: var(--portal-accent);
  font-weight: var(--fw-bold);
}

/* ── Status-Farben ───────────────────────────────────────────────────────── */
.saldo-positiv { color: var(--color-success); font-weight: var(--fw-bold); }
.saldo-negativ { color: var(--color-danger);  font-weight: var(--fw-bold); }

/* ── Footer ──────────────────────────────────────────────────────────────── */
.portal-footer {
  text-align: center;
  color: var(--color-ink-soft);
  font-size: var(--fs-xs);
  padding: var(--space-5) var(--space-4);
}

/* ════════════════════════════════════════════════════════════════════════════
   Portal-spezifische Overrides
   ────────────────────────────────────────────────────────────────────────────
   2026-05-16 Audit-Folge: Aus portal-mieter.handlebars + portal-eigentuemer.
   handlebars ausgelagert (waren die letzten Layouts mit Inline-<style>).
   ════════════════════════════════════════════════════════════════════════════ */

/* Mieter-Portal: warmer Akzent fuer aktiven Nav-Link (statt Default-Weiss) */
body[data-portal="mieter"] .portal-navbar .nav-link.active {
  color: var(--color-accent-gold);
}

/* Eigentuemer-Portal: breiterer Container, warmer Nav-Akzent + Anteil-Badge */
body[data-portal="eigentuemer"] .portal-container {
  max-width: 1100px;
}
body[data-portal="eigentuemer"] .portal-navbar .nav-link.active {
  color: var(--color-accent-gold);
}
body[data-portal="eigentuemer"] .anteil-badge {
  background: rgba(45, 74, 62, 0.10);
  color: var(--brand-accent-eigentuemer);
  font-weight: var(--fw-bold);
}

/* ── UX-2 PWA-Install-Banner (Mieter/Eigentuemer-Layout) ─────────────────── */
.hv-pwa-banner {
  background: var(--brand-bg-elevated, #FAFAFA);
  border-bottom: 1px solid var(--brand-border, #d0d0d0);
  color: var(--brand-fg, #0A0A0B);
}
.hv-pwa-banner.hv-pwa-banner-visible {
  animation: hv-pwa-slidedown 0.25s ease-out;
}
@keyframes hv-pwa-slidedown {
  from { transform: translateY(-100%); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

/* ── Card-Stacking für Portal-Tabellen auf Mobil (S-MOBILE Geld-Tabelle) ────
   <table class="table hv-table-stack"> + data-label je <td>: unter 768px wird
   jede Zeile zur Karte, vor jeder Zelle steht ihr data-label — so ist die
   Betrag-Spalte sichtbar OHNE Horizontal-Scroll. Portal-Pendant zur
   app.industrial.css-Variante (dort nur Admin). */
@media (max-width: 768px) {
  .hv-table-stack { border: 0; }
  .hv-table-stack thead {
    border: none; clip: rect(0 0 0 0);
    height: 1px; margin: -1px; overflow: hidden;
    padding: 0; position: absolute; width: 1px;
  }
  .hv-table-stack tbody tr {
    display: block;
    margin-bottom: 12px;
    border: 1px solid var(--color-line, #DDD4C2);
    border-radius: 10px;
    background: #fff;
    padding: 8px 12px;
  }
  .hv-table-stack tbody td {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 12px;
    padding: 6px 0;
    border: none;
    border-bottom: 1px solid var(--color-line-soft, #ECE5D8);
    text-align: right;
    word-break: break-word;
  }
  .hv-table-stack tbody td:last-child { border-bottom: none; }
  .hv-table-stack tbody td::before {
    content: attr(data-label);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--color-ink-soft, #6B5E4E);
    font-weight: 600;
    text-align: left;
    flex: 0 0 auto;
    min-width: 38%;
  }
  .hv-table-stack tbody td[data-label=""]::before { content: none; }
  .hv-table-stack tbody td[data-label=""] { justify-content: flex-end; }
  .hv-table-stack tbody td:not([data-label])::before { content: none; }
  .hv-table-stack tbody td:not([data-label]) { justify-content: flex-start; text-align: left; }
}

